diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index e2553e7326..831d9a2674 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -70,8 +70,8 @@ build-system-debian: variables: IMAGE: debian CONFIGURE_ARGS: --with-coroutine=sigaltstack --enable-rust - TARGETS: arm-softmmu i386-softmmu riscv64-softmmu sh4eb-softmmu - sparc-softmmu xtensa-softmmu + TARGETS: arm-softmmu hexagon-softmmu i386-softmmu riscv64-softmmu + sh4eb-softmmu sparc-softmmu xtensa-softmmu MAKE_CHECK_ARGS: check-build check-system-debian: diff --git a/.gitlab-ci.d/container-template.yml b/.gitlab-ci.d/container-template.yml index 8c7311cca5..88317d650e 100644 --- a/.gitlab-ci.d/container-template.yml +++ b/.gitlab-ci.d/container-template.yml @@ -14,7 +14,7 @@ - echo "TAG:$TAG" - echo "COMMON_TAG:$COMMON_TAG" - docker build --tag "$TAG" --cache-from "$TAG" --cache-from "$COMMON_TAG" - --build-arg BUILDKIT_INLINE_CACHE=1 + --build-arg BUILDKIT_INLINE_CACHE=1 --provenance=false -f "tests/docker/dockerfiles/$NAME.docker" "." - docker push "$TAG" after_script: diff --git a/.gitlab-ci.d/macos-14.vars b/.gitlab-ci.d/macos-14.vars index def77cfdea..0c187624dd 100644 --- a/.gitlab-ci.d/macos-14.vars +++ b/.gitlab-ci.d/macos-14.vars @@ -12,5 +12,5 @@ NINJA='/opt/homebrew/bin/ninja' PACKAGING_COMMAND='brew' PIP3='/opt/homebrew/bin/pip3' PKGS='bash bc bindgen bison bzip2 capstone ccache cmocka coreutils ctags curl dbus diffutils dtc flex gcovr gettext git glib gnu-sed gnutls gtk+3 gtk-vnc jemalloc jpeg-turbo json-c libcbor libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb llvm lzo make meson mtools ncurses nettle ninja pixman pkg-config python-setuptools python3 rpm2cpio rust sdl2 sdl2_image snappy socat sparse spice-protocol swtpm tesseract usbredir vde vte3 vulkan-tools xorriso zlib zstd' -PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme tomli' +PYPI_PKGS='sphinx sphinx-rtd-theme tomli' PYTHON='/opt/homebrew/bin/python3' diff --git a/.gitlab-ci.d/macos.yml b/.gitlab-ci.d/macos.yml index 53b6e6c4d8..641a48b383 100644 --- a/.gitlab-ci.d/macos.yml +++ b/.gitlab-ci.d/macos.yml @@ -22,7 +22,7 @@ - export PKG_CONFIG_PATH="$PKG_CONFIG_PATH" - brew update - brew install $PKGS - - brew install gdb aarch64-elf-gcc i686-elf-gcc x86_64-elf-gcc + - brew install aarch64-elf-gcc i686-elf-gcc x86_64-elf-gcc - if test -n "$PYPI_PKGS" ; then PYLIB=$($PYTHON -c 'import sysconfig; print(sysconfig.get_path("stdlib"))'); rm -f $PYLIB/EXTERNALLY-MANAGED; $PIP3 install --break-system-packages $PYPI_PKGS ; fi script: - mkdir build diff --git a/.gitlab-ci.d/static_checks.yml b/.gitlab-ci.d/static_checks.yml index 61fe2fa39a..568e56e120 100644 --- a/.gitlab-ci.d/static_checks.yml +++ b/.gitlab-ci.d/static_checks.yml @@ -55,7 +55,7 @@ check-rust-tools-nightly: - source scripts/ci/gitlab-ci-section - section_start test "Running Rust code checks" - cd build - - pyvenv/bin/meson devenv -w ../rust ${CARGO-cargo} fmt --check + - pyvenv/bin/meson devenv -w ../ ${CARGO-cargo} fmt --check - make clippy - make rustdoc - section_end test @@ -69,7 +69,7 @@ check-rust-tools-nightly: when: on_success expire_in: 2 days paths: - - rust/target/doc + - target/doc check-build-units: extends: .base_job_template diff --git a/.gitlab-map-auto b/.gitlab-map-auto new file mode 100644 index 0000000000..28ce448f1b --- /dev/null +++ b/.gitlab-map-auto @@ -0,0 +1,93 @@ +# This file is auto-generated by scripts/gitlab-map-update +# +# This GitLab map associates GitLab account handles +# with real names, in order to allow mapping from +# MAINTAINERS entries. The format of entries is +# +# {gitlab-handle}{real name} +# +# Manual overrides must be placed in .gitlab-map-manual +TaoTang Tao Tang +a1xndr Alexander Bulekov +adi-g15-ibm Aditya Gupta +agraf Alexander Graf +alex.williamson Alex Williamson +aliang1 Aihua Liang +alistair23 Alistair Francis +anisinha Ani Sinha +anthony-linaro Anthony Roberts +anthonyper Anthony PERARD +berrange Daniel P. Berrangé +birkelund Klaus Jensen +bonzini Paolo Bonzini +brian-cain Brian Cain +bsdimp Warner Losh +cborntra Christian Borntraeger +chao23.liu Chao Liu (Zevorn) +cleber.gnu Cleber Rosa +clegoate Cédric Le Goater +cohuck Cornelia Huck +cota_ Emilio Cota +dagrh Dr. David Alan Gilbert +danielhb Daniel Henrique Barboza +davidhildenbrand David Hildenbrand +dgibson dgibson +dwmw2 David Woodhouse +eauger1 Eric Auger +ebblake Eric Blake +edgar.iglesias Edgar E. Iglesias +ehabkost Eduardo Habkost +eldondev Eldon +epilys Manos Pitsidianakis +famzheng Fam Zheng +farosas Fabiano Rosas +gautammenghani Gautam Menghani +gkurz Greg Kurz +gusbromero Gustavo Romero +harshpb Harsh Prateek Bora +hdeller Helge Deller +hreitz Hanna Czenczek +imammedo Igor Mammedov +jasowang Jason Wang +jmacarthur Jim MacArthur +jsnow John Snow +juan.quintela Juan Quintela +kbastian-qemu Bastian Koppelmann +kmwolf Kevin Wolf +kostyanf14 Kostiantyn Kostiuk +kraxel Gerd Hoffmann +lbmeng Bin Meng +legoater Cédric Le Goater +lvivier Laurent Vivier +lygstate Yonggang Luo +maciejsszmigiero Maciej S. Szmigiero +marcandre.lureau Marc-André Lureau +marcandre.lureau-rh Marc-André Lureau +mauromatteo.cascella Mauro Matteo Cascella +mcayland Mark Cave-Ayland +mdroth Michael Roth +mediouni-m M. Mediouni +mjt0k Michael Tokarev +mstredhat MST +npiggin npiggin +p-b-o Pierrick Bouvier +pauldzim Paul Zimmerman +peterx Peter Xu +philmd Philippe Mathieu-Daudé +pierrick.bouvier Pierrick Bouvier +pipo.sk Peter Krempa +pkrempa Peter Krempa (work) +pm215 Peter Maydell +qemu-janitor Qemu Janitor +rathc Chinmay Rath +rth7680 Richard Henderson +schoenebeck Christian Schoenebeck +sgarzarella Stefano Garzarella +sstabellini Stefano Stabellini +stefanberger Stefan Berger +stefanha Stefan Hajnoczi +stsquad Alex Bennée +stweil Stefan Weil +thuth Thomas Huth +vsementsov Vladimir Sementsov-Ogievskiy +xcancerberox Joaquin de Andres diff --git a/.gitlab-map-manual b/.gitlab-map-manual new file mode 100644 index 0000000000..f605a540e9 --- /dev/null +++ b/.gitlab-map-manual @@ -0,0 +1,18 @@ +# This GitLab map associates GitLab account handles +# with real names, in order to allow mapping from +# MAINTAINERS entries. The format of entries is +# +# {gitlab-handle}{real name} +# +# This file is manually written, to augment the +# auto-generated data in .gitlab-map-auto. This +# is needed where a GitLab account real name does +# not exactly match the MAINTAINERS file real +# name. +# +berrange Daniel P. Berrange +dgibson David Gibson +hreitz Hanna Reitz +mstredhat Michael S. Tsirkin +npiggin Nicholas Piggin +mediouni-m Mohamed Mediouni diff --git a/rust/Cargo.lock b/Cargo.lock similarity index 69% rename from rust/Cargo.lock rename to Cargo.lock index cbb3ca15f7..ad664a812d 100644 --- a/rust/Cargo.lock +++ b/Cargo.lock @@ -8,12 +8,6 @@ version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" -[[package]] -name = "arbitrary-int" -version = "1.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c84fc003e338a6f69fbd4f7fe9f92b535ff13e9af8997f3b14b6ddff8b1df46d" - [[package]] name = "attrs" version = "0.2.9" @@ -25,23 +19,11 @@ dependencies = [ ] [[package]] -name = "bilge" -version = "0.2.0" +name = "bitfield-struct" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc707ed8ebf81de5cd6c7f48f54b4c8621760926cdf35a57000747c512e67b57" +checksum = "3ca6739863c590881f038d033a146c51ddae239186a4327014839fd864f44ed5" dependencies = [ - "arbitrary-int", - "bilge-impl", -] - -[[package]] -name = "bilge-impl" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb11e002038ad243af39c2068c8a72bcf147acf05025dcdb916fcc000adb2d8" -dependencies = [ - "itertools", - "proc-macro-error", "proc-macro2", "quote", "syn", @@ -103,12 +85,6 @@ dependencies = [ "qemu_macros", ] -[[package]] -name = "either" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" - [[package]] name = "equivalent" version = "1.0.2" @@ -136,9 +112,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "heck" @@ -184,41 +160,25 @@ dependencies = [ "glib-sys", "migration-sys", "qom-sys", - "system-sys", "util-sys", ] [[package]] name = "indexmap" -version = "2.11.4" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", "hashbrown", ] -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - [[package]] name = "libc" version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" -[[package]] -name = "memchr" -version = "2.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" - [[package]] name = "migration" version = "0.1.0" @@ -250,8 +210,7 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" name = "pl011" version = "0.1.0" dependencies = [ - "bilge", - "bilge-impl", + "bitfield-struct", "bits", "bql", "chardev", @@ -271,29 +230,6 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "136558b6e1ebaecc92755d0ffaf9421f519531bed30cc2ad23b22cb00965cc5e" -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" version = "1.0.95" @@ -343,29 +279,20 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "serde" -version = "1.0.226" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd" -dependencies = [ - "serde_core", -] - [[package]] name = "serde_core" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -374,18 +301,18 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.9" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ - "serde", + "serde_core", ] [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "syn" @@ -414,9 +341,9 @@ dependencies = [ [[package]] name = "system-deps" -version = "7.0.5" +version = "7.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4be53aa0cba896d2dc615bd42bbc130acdcffa239e0a2d965ea5b3b2a86ffdb" +checksum = "396a35feb67335377e0251fcbc1092fc85c484bd4e3a7a54319399da127796e7" dependencies = [ "cfg-expr", "heck", @@ -431,6 +358,7 @@ version = "0.1.0" dependencies = [ "common", "glib-sys", + "hwcore-sys", "migration-sys", "qom-sys", "util-sys", @@ -458,38 +386,43 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.23" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +checksum = "53c96ecdfa941c8fc4fcaed14f99ada8ebed502eef533015095a07e3301d4c3c" dependencies = [ - "serde", + "indexmap", + "serde_core", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_parser", + "toml_writer", + "winnow", ] [[package]] name = "toml_datetime" -version = "0.6.11" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ - "serde", + "serde_core", ] [[package]] -name = "toml_edit" -version = "0.22.27" +name = "toml_parser" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", "winnow", ] +[[package]] +name = "toml_writer" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" + [[package]] name = "trace" version = "0.1.0" @@ -529,17 +462,8 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - [[package]] name = "winnow" -version = "0.7.13" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" -dependencies = [ - "memchr", -] +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" diff --git a/rust/Cargo.toml b/Cargo.toml similarity index 96% rename from rust/Cargo.toml rename to Cargo.toml index 0d24eb84e1..b35704faab 100644 --- a/rust/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [workspace] resolver = "2" members = [ - "hw/char/pl011", - "hw/timer/hpet", - "tests", + "rust/hw/char/pl011", + "rust/hw/timer/hpet", + "rust/tests", ] [workspace.package] @@ -101,5 +101,6 @@ used_underscore_binding = "deny" #wildcard_imports = "deny" # still have many bindings::* imports # these may have false positives +enum_variant_names = "allow" #option_if_let_else = "deny" cognitive_complexity = "deny" diff --git a/MAINTAINERS b/MAINTAINERS index ecb8cfdc41..2a0ee348c4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -250,6 +250,12 @@ M: Brian Cain R: Pierrick Bouvier S: Supported F: target/hexagon/ +F: hw/intc/hex-l2vic.c +F: include/hw/intc/hex-l2vic.h +F: tests/qtest/l2vic-test.c +F: hw/timer/qct-qtimer.c +F: include/hw/timer/qct-qtimer.h +F: tests/qtest/qct-qtimer-test.c X: target/hexagon/idef-parser/ X: target/hexagon/gen_idef_parser_funcs.py F: linux-user/hexagon/ @@ -262,6 +268,7 @@ F: gdbstub/gdb-xml/hexagon*.xml F: docs/system/target-hexagon.rst F: docs/system/hexagon/ F: docs/devel/hexagon-sys.rst +F: docs/devel/hexagon-l2vic.rst T: git https://github.com/qualcomm/qemu.git hex-next Hexagon idef-parser @@ -490,6 +497,7 @@ F: tests/functional/aarch64/test_kvm.py PPC KVM CPUs M: Nicholas Piggin R: Harsh Prateek Bora +R: Amit Machhiwal S: Odd Fixes F: target/ppc/kvm.c @@ -613,6 +621,7 @@ R: Wei Liu R: Doru Blânzeanu S: Supported F: target/i386/mshv/ +F: hw/i386/mshv/ X86 Instruction Emulator M: Roman Bolshakov @@ -1300,6 +1309,15 @@ M: Manos Pitsidianakis S: Maintained F: rust/hw/char/pl011/ +Axiado SoCs and EVKs +M: Kuan-Jui Chiu +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/ax3000*.c +F: hw/*/axiado*.c +F: include/hw/arm/ax3000*.h +F: include/hw/*/axiado*.h + AVR Machines ------------- @@ -1357,6 +1375,7 @@ F: hw/hexagon/ F: include/hw/hexagon/ F: configs/devices/hexagon-softmmu/default.mak F: docs/system/hexagon/ +F: tests/functional/hexagon/ F: docs/devel/hexagon-sys.rst LoongArch Machines @@ -1644,6 +1663,7 @@ F: tests/functional/ppc/test_40p.py sPAPR (pseries) M: Nicholas Piggin M: Harsh Prateek Bora +R: Amit Machhiwal L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/*/spapr* @@ -1745,6 +1765,22 @@ F: include/hw/ppc/vof* F: pc-bios/vof/* F: pc-bios/vof* +PowerPC RAS (Reliability, Availability and Serviceability) +M: Aditya Gupta +R: Sourabh Jain +R: Hari Bathini +R: Shivang Upadhyay +L: qemu-ppc@nongnu.org +S: Maintained +F: hw/ppc/spapr_events.c +F: hw/ppc/spapr_fadump.c +F: hw/ppc/spapr_pci_vfio.c +F: hw/ppc/spapr_rtas.c +F: hw/ppc/pnv_mpipl.c +F: include/hw/ppc/spapr_fadump.h +F: include/hw/ppc/pnv_mpipl.h +F: tests/functional/ppc64/test_fadump.py + RISC-V Machines --------------- OpenTitan @@ -1826,12 +1862,21 @@ M: Chao Liu L: qemu-riscv@nongnu.org S: Maintained F: docs/system/riscv/k230.rst +F: hw/misc/k230_ddr.c F: hw/riscv/k230.c F: hw/watchdog/k230_wdt.c +F: hw/dma/k230_gsdma.c +F: hw/misc/k230_decomp_gzip.c +F: include/hw/misc/k230_ddr.h F: include/hw/riscv/k230.h F: include/hw/watchdog/k230_wdt.h +F: include/hw/dma/k230_gsdma.h +F: include/hw/misc/k230_decomp_gzip.h F: tests/functional/riscv64/test_k230.py +F: tests/qtest/k230-ddr-test.c F: tests/qtest/k230-wdt-test.c +F: tests/qtest/k230-gsdma-test.c +F: tests/qtest/k230-decomp-gzip-test.c RX Machines ----------- @@ -2076,6 +2121,7 @@ Machine core M: Philippe Mathieu-Daudé R: Zhao Liu S: Maintained +F: hw/core/cpu-internal.h F: hw/core/cpu-common.c F: hw/core/cpu-system.c F: hw/core/machine-qmp-cmds.c @@ -2159,6 +2205,7 @@ F: docs/specs/edu.rst IDE M: John Snow +M: Denis V. Lunev L: qemu-block@nongnu.org S: Odd Fixes F: include/hw/ide/ @@ -2204,7 +2251,9 @@ M: Peter Maydell L: qemu-arm@nongnu.org S: Odd Fixes F: hw/*/omap* +F: hw/dma/soc_dma.c F: include/hw/arm/omap.h +F: include/hw/dma/soc_dma.h F: docs/system/arm/sx1.rst F: tests/functional/arm/test_sx1.py @@ -2350,6 +2399,7 @@ T: git https://github.com/bonzini/qemu.git scsi-next SSI M: Alistair Francis S: Maintained +F: docs/devel/ssi.rst F: hw/ssi/* F: hw/block/m25p80* F: include/hw/ssi/ssi.h @@ -2829,6 +2879,19 @@ F: include/hw/acpi/vmgenid.h F: docs/specs/vmgenid.rst F: tests/qtest/vmgenid-test.c +VM Launch Update +M: Ani Sinha +M: Gerd Hoffman +S: Maintained +F: hw/misc/vmlaunchupdate.c +F: include/hw/misc/vmlaunchupdate.h +F: include/standard-headers/misc/vmlaunchupdate.h +F: docs/specs/vmlaunchupdate.rst +F: tests/functional/aarch64/test_vm_launch_update_aarch.py +F: tests/functional/x86_64/test_vm_launch_update.py +F: tests/qtest/launchupdate-test.c +F: tests/data/igvm/* + LED M: Philippe Mathieu-Daudé S: Maintained @@ -2940,7 +3003,7 @@ F: include/hw/isa/vt82c686.h Firmware configuration (fw_cfg) R: Gerd Hoffmann -S: Orphaned +S: Orphan F: docs/specs/fw_cfg.rst F: hw/nvram/fw_cfg*.c F: stubs/fw_cfg.c @@ -3364,23 +3427,6 @@ F: scripts/coccinelle/remove_local_err.cocci F: scripts/coccinelle/use-error_fatal.cocci F: scripts/coccinelle/errp-guard.cocci -Firmware Assisted Dump (fadump) for sPAPR (pseries) -M: Aditya Gupta -R: Sourabh Jain -S: Maintained -F: include/hw/ppc/spapr_fadump.h -F: hw/ppc/spapr_fadump.c -F: tests/functional/ppc64/test_fadump.py - -Memory-Preserving Initial Program Load (MPIPL) for PowerNV -M: Aditya Gupta -R: Hari Bathini -R: Sourabh -S: Maintained -F: include/hw/ppc/pnv_mpipl.h -F: hw/ppc/pnv_mpipl.c -F: tests/functional/ppc64/test_fadump.py - GDB stub M: Alex Bennée R: Philippe Mathieu-Daudé @@ -3414,6 +3460,7 @@ F: system/ioport.c F: system/memory.c F: system/memory_mapping.c F: system/physmem.c +F: system/physmem-qmp-cmds.c F: system/memory_ldst* F: system/memory-internal.h F: system/ram-block-attributes.c @@ -3853,6 +3900,7 @@ Migration dirty limit and dirty page rate M: Hyman Huang S: Maintained F: system/dirtylimit.c +F: system/dirtylimit-hmp-cmds.c F: include/system/dirtylimit.h F: migration/dirtyrate.c F: migration/dirtyrate.h @@ -4094,7 +4142,7 @@ F: tests/uefi-test-tools/ IGVM Firmware M: Gerd Hoffmann M: Stefano Garzarella -R: Ani Sinha +M: Ani Sinha S: Maintained F: backends/igvm*.c F: docs/system/igvm.rst @@ -4389,7 +4437,7 @@ F: block/parallels.c F: block/parallels-ext.c F: docs/interop/parallels.rst F: docs/interop/prl-xml.rst -T: git https://src.openvz.org/scm/~den/qemu.git parallels +T: git https://gitlab.com/dlunev/qemu.git parallels qed M: Stefan Hajnoczi diff --git a/VERSION b/VERSION index a4efc3242f..cf942bd79b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -11.0.90 +11.1.50 diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c index 72721d0f0d..5921ce693e 100644 --- a/accel/mshv/mshv-all.c +++ b/accel/mshv/mshv-all.c @@ -39,6 +39,8 @@ #include "system/mshv.h" #include "system/mshv_int.h" #include "system/reset.h" +#include "migration/qemu-file-types.h" +#include "migration/register.h" #include "trace.h" #include #include @@ -58,53 +60,29 @@ static int init_mshv(int *mshv_fd) return 0; } -/* freeze 1 to pause, 0 to resume */ -static int set_time_freeze(int vm_fd, int freeze) +static int mshv_load_cleanup(void *opaque) { + CPUState *cpu; int ret; - struct hv_input_set_partition_property in = {0}; - in.property_code = HV_PARTITION_PROPERTY_TIME_FREEZE; - in.property_value = freeze; - struct mshv_root_hvcall args = {0}; - args.code = HVCALL_SET_PARTITION_PROPERTY; - args.in_sz = sizeof(in); - args.in_ptr = (uint64_t)∈ - - ret = mshv_hvcall(vm_fd, &args); + ret = mshv_arch_set_partition_msrs(first_cpu); if (ret < 0) { - error_report("Failed to set time freeze"); + error_report("Failed to set partition MSRs: %s", strerror(-ret)); return -1; } + CPU_FOREACH(cpu) { + ret = mshv_arch_set_mp_state(cpu); + if (ret < 0) { + error_report("Failed to set mp state for vCPU %d: %s", + cpu->cpu_index, strerror(-ret)); + return -1; + } + } + return 0; } -static int pause_vm(int vm_fd) -{ - int ret; - - ret = set_time_freeze(vm_fd, 1); - if (ret < 0) { - error_report("Failed to pause partition: %s", strerror(errno)); - return -1; - } - - return 0; -} - -static int resume_vm(int vm_fd) -{ - int ret; - - ret = set_time_freeze(vm_fd, 0); - if (ret < 0) { - error_report("Failed to resume partition: %s", strerror(errno)); - return -1; - } - - return 0; -} static int get_host_partition_property(int mshv_fd, uint32_t property_code, uint64_t *value) @@ -203,6 +181,14 @@ static int create_partition(int mshv_fd, int *vm_fd) /* enable all */ disabled_xsave_features.as_uint64 = 0; + /* + * AMX TILE XSAVE state (XTILE_DATA) is 8KB, which exceeds the + * current fixed 4KB XSAVE buffer size. + */ + disabled_xsave_features.amx_tile_support = 1; + disabled_xsave_features.amx_bf16_support = 1; + disabled_xsave_features.amx_int8_support = 1; + disabled_xsave_features.amx_fp16_support = 1; /* * query host for supported processor features and disable unsupported @@ -320,9 +306,6 @@ static int create_vm(int mshv_fd, int *vm_fd) return -1; } - /* Always create a frozen partition */ - pause_vm(*vm_fd); - return 0; } @@ -536,6 +519,10 @@ static int mshv_init_vcpu(CPUState *cpu) return 0; } +static SaveVMHandlers savevm_mshv = { + .load_cleanup = mshv_load_cleanup, +}; + static int mshv_init(AccelState *as, MachineState *ms) { MshvState *s; @@ -565,13 +552,6 @@ static int mshv_init(AccelState *as, MachineState *ms) return -1; } - ret = resume_vm(vm_fd); - if (ret < 0) { - close(mshv_fd); - close(vm_fd); - return -1; - } - s->vm = vm_fd; s->fd = mshv_fd; @@ -591,6 +571,10 @@ static int mshv_init(AccelState *as, MachineState *ms) 0, "mshv-memory"); memory_listener_register(&mshv_io_listener, &address_space_io); + register_savevm_live("mshv", 0, 1, &savevm_mshv, s); + + mshv_clock_init(); + return 0; } @@ -628,6 +612,13 @@ static int mshv_cpu_exec(CPUState *cpu) cpu->vcpu_dirty = false; } + /* Corresponding store-release is in cpu_exit. */ + if (qatomic_load_acquire(&cpu->exit_request)) { + trace_mshv_interrupt_exit_request(cpu->cpu_index); + ret = EXCP_INTERRUPT; + break; + } + ret = mshv_run_vcpu(mshv_state->vm, cpu, &mshv_msg, &exit_reason); if (ret < 0) { error_report("Failed to run on vcpu %d", cpu->cpu_index); @@ -655,17 +646,13 @@ static int mshv_cpu_exec(CPUState *cpu) } /* - * The signal handler is triggered when QEMU's main thread receives a SIG_IPI - * (SIGUSR1). This signal causes the current CPU thread to be kicked, forcing a - * VM exit on the CPU. The VM exit generates an exit reason that breaks the loop - * (see mshv_cpu_exec). If the exit is due to a Ctrl+A+x command, the system - * will shut down. For other cases, the system will continue running. + * We need a dummy handler to make SIG_IPI a deliverable signal. The kernel + * handler will be woken up by the caught signal and instruct the hypervisor + * to suspend execution (the concrete mechanism differs between schedulers) + * and return to userspace. */ -static void sa_ipi_handler(int sig) +static void dummy_handler(int sig) { - /* TODO: call IOCTL to set_immediate_exit, once implemented. */ - - qemu_cpu_kick_self(); } static void init_signal(CPUState *cpu) @@ -675,7 +662,7 @@ static void init_signal(CPUState *cpu) sigset_t set; memset(&sigact, 0, sizeof(sigact)); - sigact.sa_handler = sa_ipi_handler; + sigact.sa_handler = dummy_handler; sigaction(SIG_IPI, &sigact, NULL); pthread_sigmask(SIG_BLOCK, NULL, &set); @@ -851,6 +838,7 @@ static void mshv_accel_ops_class_init(ObjectClass *oc, const void *data) ops->synchronize_state = mshv_cpu_synchronize; ops->synchronize_pre_loadvm = mshv_cpu_synchronize_pre_loadvm; ops->cpus_are_resettable = mshv_cpus_are_resettable; + ops->cpu_thread_is_idle = mshv_vcpu_thread_is_idle; ops->handle_interrupt = generic_handle_interrupt; } diff --git a/accel/mshv/trace-events b/accel/mshv/trace-events index a4dffeb24a..859e8bfb0f 100644 --- a/accel/mshv/trace-events +++ b/accel/mshv/trace-events @@ -4,6 +4,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later mshv_start_vcpu_thread(const char* thread, uint32_t cpu) "thread=%s cpu_index=%d" +mshv_interrupt_exit_request(uint32_t cpu) "cpu_index=%d" mshv_set_memory(bool add, uint64_t gpa, uint64_t size, uint64_t user_addr, bool readonly, int ret) "add=%d gpa=0x%" PRIx64 " size=0x%" PRIx64 " user=0x%" PRIx64 " readonly=%d result=%d" mshv_mem_ioeventfd_add(uint64_t addr, uint32_t size, uint32_t data) "addr=0x%" PRIx64 " size=%d data=0x%x" diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index cd7d079fe0..57daded60f 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -387,14 +387,22 @@ static void record_save(DisasContextBase *db, vaddr pc, * Either the first or second page may be I/O. If it is the second, * then the first byte we need to record will be at a non-zero offset. * In either case, we should not need to record but a single insn. + * + * A read may re-read bytes that are already recorded: a target may + * fetch a whole aligned word to decode an insn (e.g. riscv Ziccif), + * then probe the following insn, which lies within that same word. + * Such a read extends the record only by the bytes past its end. */ if (db->record_len == 0) { db->record_start = offset; db->record_len = size; } else { - assert(offset == db->record_start + db->record_len); - assert(db->record_len + size <= sizeof(db->record)); - db->record_len += size; + int end = offset - db->record_start + size; + + assert(offset >= db->record_start); + assert(offset <= db->record_start + db->record_len); + assert(end <= sizeof(db->record)); + db->record_len = MAX(db->record_len, end); } memcpy(db->record + (offset - db->record_start), from, size); diff --git a/backends/igvm-cfg.c b/backends/igvm-cfg.c index e1f09855f6..935ba54f54 100644 --- a/backends/igvm-cfg.c +++ b/backends/igvm-cfg.c @@ -52,6 +52,8 @@ static void igvm_reset_hold(Object *obj, ResetType type) trace_igvm_reset_hold(type); + /* cleanup existing memory regions first */ + qigvm_cleanup_memory(igvm); qigvm_process_file(igvm, ms, false, &error_fatal); } @@ -65,6 +67,7 @@ static void igvm_complete(UserCreatable *uc, Error **errp) IgvmCfg *igvm = IGVM_CFG(uc); igvm->file = qigvm_file_init(igvm->filename, errp); + QTAILQ_INIT(&igvm->memory_regions); } OBJECT_DEFINE_TYPE_WITH_INTERFACES(IgvmCfg, igvm_cfg, IGVM_CFG, OBJECT, diff --git a/backends/igvm.c b/backends/igvm.c index 80e87fe602..7b7bdc72b7 100644 --- a/backends/igvm.c +++ b/backends/igvm.c @@ -14,6 +14,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/target-info-qapi.h" +#include "migration/vmstate.h" #include "system/igvm.h" #include "system/igvm-cfg.h" #include "system/igvm-internal.h" @@ -178,7 +179,8 @@ static int qigvm_handler(QIgvm *ctx, IgvmVariableHeaderType raw_type, if (handlers[handler].type != type) { continue; } - header_handle = igvm_get_header(ctx->file, handlers[handler].section, + header_handle = igvm_get_header(ctx->cfg->file, + handlers[handler].section, ctx->current_header_index); if (header_handle < 0) { error_setg( @@ -187,7 +189,7 @@ static int qigvm_handler(QIgvm *ctx, IgvmVariableHeaderType raw_type, (int)header_handle); return -1; } - header_data = igvm_get_buffer(ctx->file, header_handle); + header_data = igvm_get_buffer(ctx->cfg->file, header_handle); if (header_data != NULL) { header_data += sizeof(IGVM_VHS_VARIABLE_HEADER); result = handlers[handler].handler(ctx, header_data, errp); @@ -198,7 +200,7 @@ static int qigvm_handler(QIgvm *ctx, IgvmVariableHeaderType raw_type, header_handle, type); result = -1; } - igvm_free_buffer(ctx->file, header_handle); + igvm_free_buffer(ctx->cfg->file, header_handle); return result; } @@ -219,7 +221,7 @@ static void *qigvm_prepare_memory(QIgvm *ctx, uint64_t addr, uint64_t size, int region_identifier, Error **errp) { ERRP_GUARD(); - MemoryRegion *igvm_pages = NULL; + IgvmMemoryRegion *imr = NULL; Int128 gpa_region_size; MemoryRegionSection mrs = memory_region_find(get_system_memory(), addr, size); @@ -253,23 +255,27 @@ static void *qigvm_prepare_memory(QIgvm *ctx, uint64_t addr, uint64_t size, */ g_autofree char *region_name = g_strdup_printf("igvm.%X", region_identifier); - igvm_pages = g_new0(MemoryRegion, 1); + imr = g_new0(IgvmMemoryRegion, 1); + imr->mr = g_new0(MemoryRegion, 1); if (ctx->machine_state->cgs && ctx->machine_state->cgs->require_guest_memfd) { - if (!memory_region_init_ram_guest_memfd(igvm_pages, NULL, + if (!memory_region_init_ram_guest_memfd(imr->mr, NULL, region_name, size, errp)) { - g_free(igvm_pages); + g_free(imr->mr); + g_free(imr); return NULL; } } else { - if (!memory_region_init_ram(igvm_pages, NULL, region_name, size, + if (!memory_region_init_ram(imr->mr, NULL, region_name, size, errp)) { - g_free(igvm_pages); + g_free(imr->mr); + g_free(imr); return NULL; } } - memory_region_add_subregion(get_system_memory(), addr, igvm_pages); - return memory_region_get_ram_ptr(igvm_pages); + memory_region_add_subregion(get_system_memory(), addr, imr->mr); + QTAILQ_INSERT_TAIL(&ctx->cfg->memory_regions, imr, next); + return memory_region_get_ram_ptr(imr->mr); } } @@ -344,7 +350,8 @@ static int qigvm_process_mem_region(QIgvm *ctx, unsigned start_index, for (page_index = 0; page_index < page_count; page_index++) { data_handle = igvm_get_header_data( - ctx->file, IGVM_HEADER_SECTION_DIRECTIVE, page_index + start_index); + ctx->cfg->file, IGVM_HEADER_SECTION_DIRECTIVE, + page_index + start_index); if (data_handle == IGVMAPI_NO_DATA) { /* No data indicates a zero page */ memset(®ion[page_index * page_size], 0, page_size); @@ -357,7 +364,7 @@ static int qigvm_process_mem_region(QIgvm *ctx, unsigned start_index, return -1; } else { zero = false; - data_size = igvm_get_buffer_size(ctx->file, data_handle); + data_size = igvm_get_buffer_size(ctx->cfg->file, data_handle); if (data_size < page_size) { memset(®ion[page_index * page_size], 0, page_size); } else if (data_size > page_size) { @@ -367,14 +374,14 @@ static int qigvm_process_mem_region(QIgvm *ctx, unsigned start_index, page_index + start_index); return -1; } - data = igvm_get_buffer(ctx->file, data_handle); + data = igvm_get_buffer(ctx->cfg->file, data_handle); if (data == NULL) { error_setg(errp, "IGVM: No buffer for handle %d", data_handle); - igvm_free_buffer(ctx->file, data_handle); + igvm_free_buffer(ctx->cfg->file, data_handle); return -1; } memcpy(®ion[page_index * page_size], data, data_size); - igvm_free_buffer(ctx->file, data_handle); + igvm_free_buffer(ctx->cfg->file, data_handle); } } @@ -411,7 +418,8 @@ static int qigvm_process_mem_page(QIgvm *ctx, ctx->region_start = page_data->gpa; ctx->region_start_index = ctx->current_header_index; } else { - if (!qigvm_page_attrs_equal(ctx->file, ctx->current_header_index, + if (!qigvm_page_attrs_equal(ctx->cfg->file, + ctx->current_header_index, page_data, &ctx->region_prev_page_data) || ((ctx->region_prev_page_data.gpa + @@ -474,7 +482,8 @@ static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data, return 0; } - data_handle = igvm_get_header_data(ctx->file, IGVM_HEADER_SECTION_DIRECTIVE, + data_handle = igvm_get_header_data(ctx->cfg->file, + IGVM_HEADER_SECTION_DIRECTIVE, ctx->current_header_index); if (data_handle < 0) { error_setg(errp, "Invalid VP context in IGVM file. Error code: %X", @@ -482,7 +491,7 @@ static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data, return -1; } - data = (uint8_t *)igvm_get_buffer(ctx->file, data_handle); + data = (uint8_t *)igvm_get_buffer(ctx->cfg->file, data_handle); if (data == NULL) { error_setg(errp, "IGVM: No buffer for handle %d", data_handle); result = -1; @@ -491,7 +500,8 @@ static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data, if (ctx->machine_state->cgs) { result = ctx->cgsc->set_guest_state( - vp_context->gpa, data, igvm_get_buffer_size(ctx->file, data_handle), + vp_context->gpa, data, + igvm_get_buffer_size(ctx->cfg->file, data_handle), CGS_PAGE_TYPE_VMSA, vp_context->vp_index, errp); } else if (target_arch() == SYS_EMU_TARGET_X86_64) { result = qigvm_x86_set_vp_context(data, vp_context->vp_index, errp); @@ -504,7 +514,7 @@ static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data, } exit: - igvm_free_buffer(ctx->file, data_handle); + igvm_free_buffer(ctx->cfg->file, data_handle); if (result < 0) { return result; } @@ -863,7 +873,8 @@ static int qigvm_supported_platform_compat_mask(QIgvm *ctx, Error **errp) uint32_t compatibility_mask_sev_snp = 0; uint32_t compatibility_mask = 0; - header_count = igvm_header_count(ctx->file, IGVM_HEADER_SECTION_PLATFORM); + header_count = igvm_header_count(ctx->cfg->file, + IGVM_HEADER_SECTION_PLATFORM); if (header_count < 0) { error_setg(errp, "Invalid platform header count in IGVM file. Error code: %X", @@ -874,11 +885,11 @@ static int qigvm_supported_platform_compat_mask(QIgvm *ctx, Error **errp) for (header_index = 0; header_index < (unsigned)header_count; header_index++) { IgvmVariableHeaderType typ = igvm_get_header_type( - ctx->file, IGVM_HEADER_SECTION_PLATFORM, header_index); + ctx->cfg->file, IGVM_HEADER_SECTION_PLATFORM, header_index); typ = igvm_vht_type(typ); if (typ == IGVM_VHT_SUPPORTED_PLATFORM) { header_handle = igvm_get_header( - ctx->file, IGVM_HEADER_SECTION_PLATFORM, header_index); + ctx->cfg->file, IGVM_HEADER_SECTION_PLATFORM, header_index); if (header_handle < 0) { error_setg(errp, "Invalid platform header in IGVM file. " @@ -887,11 +898,11 @@ static int qigvm_supported_platform_compat_mask(QIgvm *ctx, Error **errp) return -1; } platform = - (IGVM_VHS_SUPPORTED_PLATFORM *)(igvm_get_buffer(ctx->file, + (IGVM_VHS_SUPPORTED_PLATFORM *)(igvm_get_buffer(ctx->cfg->file, header_handle)); if (platform == NULL) { error_setg(errp, "IGVM: No buffer for handle %d", header_handle); - igvm_free_buffer(ctx->file, header_handle); + igvm_free_buffer(ctx->cfg->file, header_handle); return -1; } @@ -922,7 +933,7 @@ static int qigvm_supported_platform_compat_mask(QIgvm *ctx, Error **errp) } else if (platform->platform_type == IGVM_PLATFORM_TYPE_NATIVE) { compatibility_mask = platform->compatibility_mask; } - igvm_free_buffer(ctx->file, header_handle); + igvm_free_buffer(ctx->cfg->file, header_handle); } } /* Choose the strongest supported isolation technology */ @@ -999,7 +1010,7 @@ int qigvm_process_file(IgvmCfg *cfg, MachineState *machine_state, error_setg(errp, "No IGVM file loaded."); return -1; } - ctx.file = cfg->file; + ctx.cfg = cfg; trace_igvm_process_file(cfg->file, onlyVpContext); ctx.machine_state = machine_state; @@ -1021,7 +1032,8 @@ int qigvm_process_file(IgvmCfg *cfg, MachineState *machine_state, goto cleanup; } - header_count = igvm_header_count(ctx.file, IGVM_HEADER_SECTION_DIRECTIVE); + header_count = igvm_header_count(ctx.cfg->file, + IGVM_HEADER_SECTION_DIRECTIVE); if (header_count <= 0) { error_setg( errp, "Invalid directive header count in IGVM file. Error code: %X", @@ -1035,7 +1047,8 @@ int qigvm_process_file(IgvmCfg *cfg, MachineState *machine_state, ctx.current_header_index < (unsigned)header_count; ctx.current_header_index++) { IgvmVariableHeaderType raw_type = igvm_get_header_type( - ctx.file, IGVM_HEADER_SECTION_DIRECTIVE, ctx.current_header_index); + ctx.cfg->file, IGVM_HEADER_SECTION_DIRECTIVE, + ctx.current_header_index); if (!onlyVpContext || igvm_vht_type(raw_type) == IGVM_VHT_VP_CONTEXT) { if (qigvm_handler(&ctx, raw_type, errp) < 0) { goto cleanup_parameters; @@ -1053,7 +1066,7 @@ int qigvm_process_file(IgvmCfg *cfg, MachineState *machine_state, } header_count = - igvm_header_count(ctx.file, IGVM_HEADER_SECTION_INITIALIZATION); + igvm_header_count(ctx.cfg->file, IGVM_HEADER_SECTION_INITIALIZATION); if (header_count < 0) { error_setg( errp, @@ -1066,7 +1079,8 @@ int qigvm_process_file(IgvmCfg *cfg, MachineState *machine_state, ctx.current_header_index < (unsigned)header_count; ctx.current_header_index++) { IgvmVariableHeaderType type = - igvm_get_header_type(ctx.file, IGVM_HEADER_SECTION_INITIALIZATION, + igvm_get_header_type(ctx.cfg->file, + IGVM_HEADER_SECTION_INITIALIZATION, ctx.current_header_index); if (qigvm_handler(&ctx, type, errp) < 0) { goto cleanup_parameters; @@ -1096,3 +1110,22 @@ cleanup_parameters: cleanup: return retval; } + +/* + * cleanup any memory regions created by qigvm_prepare_memory() + */ +void qigvm_cleanup_memory(IgvmCfg *cfg) +{ + IgvmMemoryRegion *imr, *tmp; + + QTAILQ_FOREACH_SAFE(imr, &cfg->memory_regions, next, tmp) + { + trace_qigvm_cleanup_memory(imr->mr->name); + memory_region_del_subregion(get_system_memory(), imr->mr); + vmstate_unregister_ram(imr->mr, NULL); + QTAILQ_REMOVE(&cfg->memory_regions, imr, next); + /* this triggers MemoryRegion cleanup */ + object_unparent(OBJECT(imr->mr)); + g_free(imr); + } +} diff --git a/backends/rng.c b/backends/rng.c index ab94dfea85..bc9c7a5ab3 100644 --- a/backends/rng.c +++ b/backends/rng.c @@ -11,11 +11,14 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "system/rng.h" #include "qapi/error.h" #include "qemu/module.h" #include "qom/object_interfaces.h" +#define RNG_MAX_REQUEST_SIZE (64 * KiB) + void rng_backend_request_entropy(RngBackend *s, size_t size, EntropyReceiveFunc *receive_entropy, void *opaque) @@ -27,7 +30,7 @@ void rng_backend_request_entropy(RngBackend *s, size_t size, req = g_malloc(sizeof(*req)); req->offset = 0; - req->size = size; + req->size = MIN(size, RNG_MAX_REQUEST_SIZE); req->receive_entropy = receive_entropy; req->opaque = opaque; req->data = g_malloc(req->size); @@ -68,6 +71,22 @@ static void rng_backend_free_request(RngRequest *req) g_free(req); } +void rng_backend_cancel_requests(RngBackend *s, + EntropyReceiveFunc *receive_entropy, + const void *opaque) +{ + RngRequest *req, *next; + + QSIMPLEQ_FOREACH_SAFE(req, &s->requests, next, next) { + if (req->receive_entropy != receive_entropy || + req->opaque != opaque) { + continue; + } + QSIMPLEQ_REMOVE(&s->requests, req, RngRequest, next); + rng_backend_free_request(req); + } +} + static void rng_backend_free_requests(RngBackend *s) { RngRequest *req, *next; diff --git a/backends/trace-events b/backends/trace-events index 009a25b0be..9b7b9b7d68 100644 --- a/backends/trace-events +++ b/backends/trace-events @@ -33,3 +33,4 @@ igvm_reset_hold(int type) "type=%u" igvm_reset_exit(int type) "type=%u" igvm_file_loaded(const char *fn, int32_t handle) "fn=%s, handle=0x%x" igvm_process_file(int32_t handle, bool context_only) "handle=0x%x context-only=%d" +qigvm_cleanup_memory(const char* name) "freeing mr %s" diff --git a/block/accounting.c b/block/accounting.c index f00fe99740..038af37017 100644 --- a/block/accounting.c +++ b/block/accounting.c @@ -194,6 +194,8 @@ int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type, return -EINVAL; } + qemu_mutex_lock(&stats->lock); + hist->nbins = new_nbins; g_free(hist->boundaries); hist->boundaries = g_new(uint64_t, hist->nbins - 1); @@ -206,6 +208,8 @@ int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type, g_free(hist->bins); hist->bins = g_new0(uint64_t, hist->nbins); + qemu_mutex_unlock(&stats->lock); + return 0; } @@ -213,12 +217,16 @@ void block_latency_histograms_clear(BlockAcctStats *stats) { int i; + qemu_mutex_lock(&stats->lock); + for (i = 0; i < BLOCK_MAX_IOTYPE; i++) { BlockLatencyHistogram *hist = &stats->latency_histogram[i]; g_free(hist->bins); g_free(hist->boundaries); memset(hist, 0, sizeof(*hist)); } + + qemu_mutex_unlock(&stats->lock); } static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, @@ -310,10 +318,9 @@ double block_acct_queue_depth(BlockAcctTimedStats *stats, uint64_t sum, elapsed; assert(type < BLOCK_MAX_IOTYPE); + assert(qemu_mutex_trylock(&stats->stats->lock) == -EBUSY); - qemu_mutex_lock(&stats->stats->lock); sum = timed_average_sum(&stats->latency[type], &elapsed); - qemu_mutex_unlock(&stats->stats->lock); return (double) sum / elapsed; } diff --git a/block/blkio.c b/block/blkio.c index fb8bec27d7..008320bf20 100644 --- a/block/blkio.c +++ b/block/blkio.c @@ -937,9 +937,8 @@ static int64_t coroutine_fn blkio_co_getlength(BlockDriverState *bs) uint64_t capacity; int ret; - WITH_QEMU_LOCK_GUARD(&s->blkio_lock) { - ret = blkio_get_uint64(s->blkio, "capacity", &capacity); - } + QEMU_LOCK_GUARD(&s->blkio_lock); + ret = blkio_get_uint64(s->blkio, "capacity", &capacity); if (ret < 0) { return -ret; } diff --git a/block/cloop.c b/block/cloop.c index 443af1444e..a16f08e6ef 100644 --- a/block/cloop.c +++ b/block/cloop.c @@ -202,7 +202,8 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, s->current_block = s->n_blocks; s->sectors_per_block = s->block_size/512; - bs->total_sectors = s->n_blocks * s->sectors_per_block; + /* Cast to uint64_t to prevent uint32_t overflow */ + bs->total_sectors = (uint64_t)s->n_blocks * s->sectors_per_block; qemu_co_mutex_init(&s->lock); return 0; diff --git a/block/commit.c b/block/commit.c index c5e3ef03a2..2d52c39594 100644 --- a/block/commit.c +++ b/block/commit.c @@ -128,8 +128,9 @@ static void commit_clean(Job *job) blk_unref(s->top); } -static int commit_iteration(CommitBlockJob *s, int64_t offset, - int64_t *requested_bytes, void *buf) +static int coroutine_fn +commit_iteration(CommitBlockJob *s, int64_t offset, + int64_t *requested_bytes, void *buf) { BlockErrorAction action; int64_t bytes = *requested_bytes; diff --git a/block/curl.c b/block/curl.c index 684c677ef7..1958eb80d1 100644 --- a/block/curl.c +++ b/block/curl.c @@ -482,6 +482,8 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state) } } if (curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, (long)s->timeout) || + curl_easy_setopt(state->curl, CURLOPT_USERAGENT, + "QEMU/" QEMU_VERSION) || curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_read_cb) || curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state) || diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index 13a1979755..9fda3a4b98 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -612,7 +612,7 @@ uint64_t bdrv_dirty_bitmap_serialization_coverage(int serialized_chunk_size, const BdrvDirtyBitmap *bitmap) { uint64_t granularity = bdrv_dirty_bitmap_granularity(bitmap); - uint64_t limit = granularity * (serialized_chunk_size << 3); + uint64_t limit = granularity * ((uint64_t)serialized_chunk_size << 3); assert(QEMU_IS_ALIGNED(limit, bdrv_dirty_bitmap_serialization_align(bitmap))); diff --git a/block/dmg.c b/block/dmg.c index 33dcb3a349..5d7d3b8901 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -312,6 +312,21 @@ static int dmg_read_mish_block(BDRVDMGState *s, DmgHeaderState *ds, goto fail; } + /* + * Uncompressed chunk length must match sector count. Compressed chunks + * are validated during dmg_read_chunk() since the uncompressed size is + * not known ahead of time. + */ + if (s->types[i] == UDRW) { + if (s->sectorcounts[i] != DIV_ROUND_UP(s->lengths[i], 512)) { + error_report("length %" PRIu64 " for chunk %" PRIu32 + " is inconsistent with sector count %" PRIu64, + s->lengths[i], i, s->sectorcounts[i]); + ret = -EINVAL; + goto fail; + } + } + update_max_chunk_size(s, i, &ds->max_compressed_size, &ds->max_sectors_per_chunk); offset += 40; @@ -559,6 +574,12 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } + /* There must be at least one chunk */ + if (s->n_chunks == 0) { + ret = -EINVAL; + goto fail; + } + /* initialize zlib engine */ s->compressed_chunk = qemu_try_blockalign(bs->file->bs, ds.max_compressed_size + 1); @@ -609,7 +630,10 @@ static inline int is_sector_in_chunk(BDRVDMGState *s, static inline uint32_t search_chunk(BDRVDMGState *s, uint64_t sector_num) { /* binary search */ - uint32_t chunk1 = 0, chunk2 = s->n_chunks, chunk3; + uint32_t chunk1 = 0, chunk2 = s->n_chunks - 1, chunk3; + if (s->n_chunks == 0) { + goto err; /* should never happen */ + } while (chunk1 <= chunk2) { chunk3 = (chunk1 + chunk2) / 2; if (s->sectors[chunk3] > sector_num) { @@ -713,6 +737,16 @@ dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) if (ret < 0) { return -1; } + + /* + * Zero the unread part of the last sector when chunk length is + * unaligned to avoid exposing uninitialized memory. Valid image + * files may never hit this case, but cover it to be safe. + */ + if (s->lengths[chunk] & 511) { + size_t trailing_bytes = 512 - (s->lengths[chunk] & 511); + memset(s->uncompressed_chunk + s->lengths[chunk], 0, trailing_bytes); + } break; case UDZE: /* zeros */ case UDIG: /* ignore */ diff --git a/block/export/fuse.c b/block/export/fuse.c index c0e8dfb643..9ae22a2e00 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -859,6 +859,18 @@ fuse_co_init(FuseExport *exp, struct fuse_init_out *out, uint32_t supported_flags = FUSE_ASYNC_READ | FUSE_ASYNC_DIO; uint32_t flags2 = 0; + if (!exp->growable) { + /* + * Back when libfuse was used, it would always set this flag and thus + * the kernel did not execute a truncate itself and passed along O_TRUNC + * to user space. Continue setting the flag for backwards compatibility + * when the export is not growable to avoid issues with O_TRUNC, i.e. + * blockdev-based exports running into ENOTSUP and file-based exports + * with growable=off to be truncated and then stuck with size 0. + */ + supported_flags = FUSE_ATOMIC_O_TRUNC; + } + if (in->major != 7) { error_report("FUSE major version mismatch: We have 7, but kernel has %" PRIu32, in->major); diff --git a/block/monitor/bitmap-qmp-cmds.c b/block/monitor/bitmap-qmp-cmds.c index a738e7bbf7..aabf2790b6 100644 --- a/block/monitor/bitmap-qmp-cmds.c +++ b/block/monitor/bitmap-qmp-cmds.c @@ -125,10 +125,17 @@ 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) { + if (!bdrv_is_writable(bs)) { + error_setg(errp, "Cannot add a persistent bitmap to " + "read-only or inactive node '%s'", + bdrv_get_node_name(bs)); + return; + } + + if (!bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp)) { + return; + } } bitmap = bdrv_create_dirty_bitmap(bs, granularity, name, errp); @@ -158,11 +165,11 @@ BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, return NULL; } - if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY | BDRV_BITMAP_RO, - errp)) { + if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY, errp)) { return NULL; } + /* Dropping a bitmap needs no write access unless it is actually stored. */ if (bdrv_dirty_bitmap_get_persistence(bitmap) && bdrv_remove_persistent_dirty_bitmap(bs, name, errp) < 0) { diff --git a/block/parallels-ext.c b/block/parallels-ext.c index 3410daa620..7f6ab6b0d2 100644 --- a/block/parallels-ext.c +++ b/block/parallels-ext.c @@ -70,20 +70,11 @@ parallels_load_bitmap_data(BlockDriverState *bs, const uint64_t *l1_table, uint64_t offset, limit; uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap); uint8_t *buf = NULL; - uint64_t i, tab_size = - DIV_ROUND_UP(bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size), - s->cluster_size); - - if (tab_size != l1_size) { - error_setg(errp, "Bitmap table size %" PRIu32 " does not correspond " - "to bitmap size and cluster size. Expected %" PRIu64, - l1_size, tab_size); - return -EINVAL; - } + uint64_t i; buf = qemu_blockalign(bs, s->cluster_size); limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap); - for (i = 0, offset = 0; i < tab_size; ++i, offset += limit) { + for (i = 0, offset = 0; i < l1_size; ++i, offset += limit) { uint64_t count = MIN(bm_size - offset, limit); uint64_t entry = l1_table[i]; @@ -124,12 +115,14 @@ static BdrvDirtyBitmap * GRAPH_RDLOCK parallels_load_bitmap(BlockDriverState *bs, uint8_t *data, size_t data_size, Error **errp) { + BDRVParallelsState *s = bs->opaque; int ret; ParallelsDirtyBitmapFeature bf; g_autofree uint64_t *l1_table = NULL; BdrvDirtyBitmap *bitmap; QemuUUID uuid; char uuidstr[UUID_STR_LEN]; + uint64_t bm_size, tab_size; int i; if (data_size < sizeof(bf)) { @@ -164,15 +157,34 @@ parallels_load_bitmap(BlockDriverState *bs, uint8_t *data, size_t data_size, return NULL; } - l1_table = g_new(uint64_t, bf.l1_size); - for (i = 0; i < bf.l1_size; i++, data += sizeof(uint64_t)) { - l1_table[i] = ldq_le_p(data); + bm_size = bdrv_dirty_bitmap_size(bitmap); + tab_size = DIV_ROUND_UP( + bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size), + s->cluster_size); + if (tab_size != bf.l1_size) { + error_setg(errp, "Bitmap table size %" PRIu32 " does not correspond " + "to bitmap size and cluster size. Expected %" PRIu64, + bf.l1_size, tab_size); + goto fail; } - ret = parallels_load_bitmap_data(bs, l1_table, bf.l1_size, bitmap, errp); - if (ret < 0) { - bdrv_release_dirty_bitmap(bitmap); - return NULL; + if (bf.l1_size != 0) { + l1_table = g_try_new(uint64_t, bf.l1_size); + if (!l1_table) { + error_setg(errp, "Failed to allocate the bitmap L1 table " + "(%" PRIu32 " entries)", bf.l1_size); + goto fail; + } + + for (i = 0; i < bf.l1_size; i++, data += sizeof(uint64_t)) { + l1_table[i] = ldq_le_p(data); + } + + ret = parallels_load_bitmap_data(bs, l1_table, bf.l1_size, bitmap, + errp); + if (ret < 0) { + goto fail; + } } /* We support format extension only for RO parallels images. */ @@ -180,6 +192,10 @@ parallels_load_bitmap(BlockDriverState *bs, uint8_t *data, size_t data_size, bdrv_dirty_bitmap_set_readonly(bitmap, true); return bitmap; + +fail: + bdrv_release_dirty_bitmap(bitmap); + return NULL; } static int GRAPH_RDLOCK diff --git a/block/parallels.c b/block/parallels.c index 7a90fb5220..93b5fa9dcd 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -52,6 +52,7 @@ #define HEADER_VERSION 2 #define HEADER_INUSE_MAGIC (0x746F6E59) #define MAX_PARALLELS_IMAGE_FACTOR (1ull << 32) +#define PARALLELS_HEADER_READ_CHUNK (64 * 1024 * 1024) static QEnumLookup prealloc_mode_lookup = { .array = (const char *const[]) { @@ -118,6 +119,7 @@ static uint32_t bat_entry_off(uint32_t idx) static int64_t seek_to_sector(BDRVParallelsState *s, int64_t sector_num) { uint32_t index, offset; + int64_t cluster_off; index = sector_num / s->tracks; offset = sector_num % s->tracks; @@ -126,7 +128,14 @@ static int64_t seek_to_sector(BDRVParallelsState *s, int64_t sector_num) if ((index >= s->bat_size) || (s->bat_bitmap[index] == 0)) { return -1; } - return bat2sect(s, index) + offset; + + cluster_off = bat2sect(s, index); + if (cluster_off < s->data_start || cluster_off + s->tracks > s->data_end) { + /* Cluster is outside of the image file or overlaps the header. */ + return -1; + } + + return cluster_off + offset; } static int cluster_remainder(BDRVParallelsState *s, int64_t sector_num, @@ -702,18 +711,22 @@ parallels_check_outside_image(BlockDriverState *bs, BdrvCheckResult *res, { BDRVParallelsState *s = bs->opaque; uint32_t i; - int64_t off, high_off, size; + int64_t off, high_off, size, data_start_off; size = bdrv_co_getlength(bs->file->bs); if (size < 0) { res->check_errors++; return size; } + data_start_off = s->data_start << BDRV_SECTOR_BITS; high_off = 0; for (i = 0; i < s->bat_size; i++) { off = bat2sect(s, i) << BDRV_SECTOR_BITS; - if (off + s->cluster_size > size) { + if (off == 0) { + continue; + } + if (off < data_start_off || off + s->cluster_size > size) { fprintf(stderr, "%s cluster %u is outside image\n", fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i); res->corruptions++; @@ -998,7 +1011,8 @@ parallels_co_create(BlockdevCreateOptions* opts, Error **errp) BlockdevCreateOptionsParallels *parallels_opts; BlockDriverState *bs; BlockBackend *blk; - int64_t total_size, cl_size; + int64_t total_size, cl_size, bat_count; + uint64_t cylinders; uint32_t bat_entries, bat_sectors; ParallelsHeader header; uint8_t tmp[BDRV_SECTOR_SIZE]; @@ -1016,16 +1030,22 @@ parallels_co_create(BlockdevCreateOptions* opts, Error **errp) cl_size = DEFAULT_CLUSTER_SIZE; } - /* XXX What is the real limit here? This is an insanely large maximum. */ + /* Bounds cl_size so the multiplication below can't overflow int64_t. */ if (cl_size >= INT64_MAX / MAX_PARALLELS_IMAGE_FACTOR) { error_setg(errp, "Cluster size is too large"); return -EINVAL; } - if (total_size >= MAX_PARALLELS_IMAGE_FACTOR * cl_size) { + if (cl_size <= 0 || total_size >= MAX_PARALLELS_IMAGE_FACTOR * cl_size) { error_setg(errp, "Image size is too large for this cluster size"); return -E2BIG; } + bat_count = DIV_ROUND_UP(total_size, cl_size); + if (bat_count > INT_MAX / (int64_t)sizeof(uint32_t)) { + error_setg(errp, "Catalog too large"); + return -EFBIG; + } + if (!QEMU_IS_ALIGNED(total_size, BDRV_SECTOR_SIZE)) { error_setg(errp, "Image size must be a multiple of 512 bytes"); return -EINVAL; @@ -1051,7 +1071,7 @@ parallels_co_create(BlockdevCreateOptions* opts, Error **errp) blk_set_allow_write_beyond_eof(blk, true); /* Create image format */ - bat_entries = DIV_ROUND_UP(total_size, cl_size); + bat_entries = bat_count; bat_sectors = DIV_ROUND_UP(bat_entry_off(bat_entries), cl_size); bat_sectors = (bat_sectors * cl_size) >> BDRV_SECTOR_BITS; @@ -1060,8 +1080,12 @@ parallels_co_create(BlockdevCreateOptions* opts, Error **errp) header.version = cpu_to_le32(HEADER_VERSION); /* don't care much about geometry, it is not used on image level */ header.heads = cpu_to_le32(HEADS_NUMBER); - header.cylinders = cpu_to_le32(total_size / BDRV_SECTOR_SIZE - / HEADS_NUMBER / SEC_IN_CYL); + cylinders = total_size / BDRV_SECTOR_SIZE / HEADS_NUMBER / SEC_IN_CYL; + /* Write only by spec, do not care */ + if (cylinders >= UINT32_MAX) { + cylinders = UINT32_MAX; + } + header.cylinders = cpu_to_le32(cylinders); header.tracks = cpu_to_le32(cl_size >> BDRV_SECTOR_BITS); header.bat_entries = cpu_to_le32(bat_entries); header.nb_sectors = cpu_to_le64(DIV_ROUND_UP(total_size, BDRV_SECTOR_SIZE)); @@ -1240,7 +1264,8 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, { BDRVParallelsState *s = bs->opaque; ParallelsHeader ph; - int ret, size, i; + int ret, i; + uint32_t size, header_off; int64_t file_nb_sectors, sector; uint32_t data_start; bool need_check = false; @@ -1303,6 +1328,12 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, return -EFBIG; } + if ((uint64_t)s->bat_size * s->tracks < bs->total_sectors) { + error_setg(errp, "Invalid image: Catalog size too small for " + "advertised disk size"); + return -EINVAL; + } + size = bat_entry_off(s->bat_size); s->header_size = ROUND_UP(size, bdrv_opt_mem_align(bs->file->bs)); s->header = qemu_try_blockalign(bs->file->bs, s->header_size); @@ -1310,9 +1341,17 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, return -ENOMEM; } - ret = bdrv_pread(bs->file, 0, s->header_size, s->header, 0); - if (ret < 0) { - goto fail; + /* A single request s->header_size large exceeds BDRV_REQUEST_MAX_BYTES. */ + for (header_off = 0; header_off < s->header_size; + header_off += PARALLELS_HEADER_READ_CHUNK) { + uint32_t chunk = MIN(s->header_size - header_off, + PARALLELS_HEADER_READ_CHUNK); + + ret = bdrv_pread(bs->file, header_off, chunk, + (uint8_t *)s->header + header_off, 0); + if (ret < 0) { + goto fail; + } } s->bat_bitmap = (uint32_t *)(s->header + 1); @@ -1377,11 +1416,18 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, for (i = 0; i < s->bat_size; i++) { sector = bat2sect(s, i); + if (sector == 0) { + continue; /* not allocated */ + } + if (sector < data_start || sector + s->tracks > file_nb_sectors) { + /* Cluster is outside of the image file or overlaps the header. */ + need_check = true; + continue; + } if (sector + s->tracks > s->data_end) { s->data_end = sector + s->tracks; } } - need_check = need_check || s->data_end > file_nb_sectors; if (!need_check) { ret = parallels_fill_used_bitmap(bs); diff --git a/block/qapi.c b/block/qapi.c index eabfbfc258..1dfac51091 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -535,6 +535,8 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk) BlockAcctTimedStats *ts = NULL; BlockLatencyHistogram *hgram; + qemu_mutex_lock(&stats->lock); + ds->rd_bytes = stats->nr_bytes[BLOCK_ACCT_READ]; ds->wr_bytes = stats->nr_bytes[BLOCK_ACCT_WRITE]; ds->zone_append_bytes = stats->nr_bytes[BLOCK_ACCT_ZONE_APPEND]; @@ -624,6 +626,7 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk) = bdrv_latency_histogram_stats(&hgram[BLOCK_ACCT_ZONE_APPEND]); ds->flush_latency_histogram = bdrv_latency_histogram_stats(&hgram[BLOCK_ACCT_FLUSH]); + qemu_mutex_unlock(&stats->lock); } static BlockStats * GRAPH_RDLOCK diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 256ec99878..ac5a724588 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -1487,6 +1487,15 @@ int coroutine_fn qcow2_co_remove_persistent_dirty_bitmap(BlockDriverState *bs, goto out; } + if (!can_write(bs)) { + error_setg(errp, "Cannot remove persistent bitmap '%s': " + "no write access to node '%s'", name, + bdrv_get_node_name(bs)); + ret = -EACCES; + bm = NULL; + goto out; + } + QSIMPLEQ_REMOVE(bm_list, bm, Qcow2Bitmap, entry); ret = update_ext_header_and_dir(bs, bm_list); diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index 1e8dc48be1..8453ad73d1 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -740,12 +740,6 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) ROUND_UP(sn->vm_state_size, s->cluster_size), QCOW2_DISCARD_NEVER, false); -#ifdef DEBUG_ALLOC - { - BdrvCheckResult result = {0}; - qcow2_check_refcounts(bs, &result, 0); - } -#endif return 0; fail: @@ -893,12 +887,6 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) goto fail; } -#ifdef DEBUG_ALLOC - { - BdrvCheckResult result = {0}; - qcow2_check_refcounts(bs, &result, 0); - } -#endif return 0; fail: @@ -975,12 +963,6 @@ int qcow2_snapshot_delete(BlockDriverState *bs, return ret; } -#ifdef DEBUG_ALLOC - { - BdrvCheckResult result = {0}; - qcow2_check_refcounts(bs, &result, 0); - } -#endif return 0; } diff --git a/block/qcow2.c b/block/qcow2.c index 19271b10a4..7292dd036c 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -2870,7 +2870,11 @@ static int GRAPH_RDLOCK qcow2_inactivate(BlockDriverState *bs) strerror(-ret)); } - if (result == 0) { + /* + * A read-only node cannot resolve an inherited dirty bit here; + * leave it dirty, same as plain read access already does. + */ + if (result == 0 && !bdrv_is_read_only(bs)) { qcow2_mark_clean(bs); } diff --git a/chardev/char-socket.c b/chardev/char-socket.c index b629575fcf..81bac42a15 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -1128,11 +1128,6 @@ static void qemu_chr_socket_connected(QIOTask *task, void *opaque) if (qio_task_propagate_error(task, &err)) { tcp_chr_change_state(s, TCP_CHARDEV_STATE_DISCONNECTED); - if (s->registered_yank) { - yank_unregister_function(CHARDEV_YANK_INSTANCE(chr->label), - char_socket_yank_iochannel, - QIO_CHANNEL(sioc)); - } check_report_connect_error(chr, err); goto cleanup; } diff --git a/chardev/char-win-stdio.c b/chardev/char-win-stdio.c index bb9c195a8b..0deba3bffb 100644 --- a/chardev/char-win-stdio.c +++ b/chardev/char-win-stdio.c @@ -128,7 +128,7 @@ static void win_stdio_thread_wait_func(void *opaque) SetEvent(stdio->hInputDoneEvent); } -static void win_stiod_chr_set_echo(Chardev *chr, bool echo) +static void win_stdio_chr_set_echo(Chardev *chr, bool echo) { WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr); DWORD dwMode = 0; @@ -205,7 +205,7 @@ static bool win_stdio_chr_open(Chardev *chr, SetConsoleMode(stdio->hStdIn, dwMode); - win_stiod_chr_set_echo(chr, false); + win_stdio_chr_set_echo(chr, false); qemu_chr_be_event(chr, CHR_EVENT_OPENED); return true; @@ -263,7 +263,7 @@ static void char_win_stdio_class_init(ObjectClass *oc, const void *data) cc->chr_open = win_stdio_chr_open; cc->chr_write = win_stdio_chr_write; - cc->chr_set_echo = win_stiod_chr_set_echo; + cc->chr_set_echo = win_stdio_chr_set_echo; } static const TypeInfo char_win_stdio_type_info = { diff --git a/configure b/configure index d8bc10060e..56158cd95f 100755 --- a/configure +++ b/configure @@ -172,7 +172,7 @@ fi # some defaults, based on the host environment # default parameters -container_engine="auto" +container_command="" cpu="" cross_compile="no" cross_prefix="" @@ -734,7 +734,7 @@ for opt do ;; --disable-containers) use_containers="no" ;; - --container-engine=*) container_engine="$optarg" + --container-command=*) container_command="$optarg" ;; --rust-target-triple=*) rust_target_triple="$optarg" ;; @@ -869,7 +869,7 @@ Advanced options (experts only): --enable-debug enable common debug build options --cpu=CPU Build for host CPU [$cpu] --disable-containers don't use containers for cross-building - --container-engine=TYPE which container engine to use [$container_engine] + --container-command=CMD which container command to use [autodetect] --gdb=GDB-path gdb to use for gdbstub tests [$gdb_bin] --wasm64-32bit-address-limit Restrict wasm64 address space to 32-bit (default is to use the whole 64-bit range). @@ -1166,12 +1166,12 @@ fi # detect rust triple meson_version=$($meson --version) -if test "$rust" != disabled && ! version_ge "$meson_version" 1.10.0; then +if test "$rust" != disabled && ! version_ge "$meson_version" 1.12.0; then if test "$rust" = enabled; then $mkvenv ensuregroup --dir "${source_path}/python/wheels" \ ${source_path}/pythondeps.toml meson-rust || exit 1 else - echo "Rust needs Meson 1.10.0, disabling" 2>&1 + echo "Rust needs Meson 1.12.0, disabling" 2>&1 rust=disabled fi fi @@ -1291,12 +1291,12 @@ fi ########################################## # functions to probe cross compilers -runc="no" -if test $use_containers = "yes" && (has "docker" || has "podman"); then - runc=$($python "$source_path"/tests/docker/docker.py --engine "$container_engine" probe) - if test "$runc" != "no"; then - docker_py="$python $source_path/tests/docker/docker.py --engine $container_engine" - fi +if test "$container_command" = ""; then + container_command=$($python "$source_path"/tests/docker/docker.py probe) + test "$container_command" = "no" && container_command="" +fi +if test $use_containers = "yes" && test "$container_command" != ""; then + docker_py="$python $source_path/tests/docker/docker.py --command $container_command" fi # cross compilers defaults, can be overridden with --cross-cc-ARCH @@ -1415,7 +1415,7 @@ probe_target_compiler() { esac for host in $container_hosts; do - test "$runc" != no || continue + test "$container_command" != "" || continue test "$host" = "$cpu" || continue case $target_arch in # debian-all-test-cross architectures @@ -1736,14 +1736,10 @@ echo all: >> $config_host_mak echo "SRC_PATH=$source_path" >> $config_host_mak echo "TARGET_DIRS=$target_list" >> $config_host_mak echo "GDB=$gdb_bin" >> $config_host_mak -if test "$runc" != no; then - echo "RUNC=$runc" >> $config_host_mak - echo "CONTAINER_ENGINE=$container_engine" >> $config_host_mak +if test "$container_command" != ""; then + echo "CONTAINER_COMMAND=$container_command" >> $config_host_mak fi echo "SUBDIRS=$subdirs" >> $config_host_mak -if test "$rust" != disabled; then - echo "RUST_TARGET_TRIPLE=$rust_target_triple" >> $config_host_mak -fi echo "PYTHON=$python" >> $config_host_mak echo "MKVENV_ENSUREGROUP=$mkvenv ensuregroup $mkvenv_online_flag" >> $config_host_mak echo "GENISOIMAGE=$genisoimage" >> $config_host_mak diff --git a/contrib/plugins/dlcall.c b/contrib/plugins/dlcall.c index 9d2230b2d1..b624735e62 100644 --- a/contrib/plugins/dlcall.c +++ b/contrib/plugins/dlcall.c @@ -10,7 +10,8 @@ * nothing about how a library is thunked. Any toolchain can implement the * userspace side. Lorelei is one end-to-end implementation (guest/host * runtimes plus a thunk compiler that generates thunks from a library's - * headers): + * headers), and how it handles argument marshalling, callbacks and variadic + * functions can serve as a reference: * https://github.com/rover2024/lorelei * * See docs/about/emulation.rst|Dynamic Linking Call for details and examples. @@ -20,12 +21,13 @@ * execution in the QEMU host process. It is NOT a sandbox and provides no * isolation; only load it for guests you fully trust. * - * WARNING: requires guest_base == 0, which is qemu-user's default. Pointer - * operands are dereferenced as host addresses directly, and the invoked host - * functions dereference guest pointers with no address translation, so guest - * and host must share a single address space. A non-zero guest_base (e.g. set - * via -B/-R) would make every pointer off by guest_base and hit unrelated - * host memory. + * WARNING: requires guest_base == 0, which is qemu-user's default, and a + * guest whose pointer width and endianness match the host's. Pointer operands + * are dereferenced as host addresses directly, and the invoked host functions + * dereference guest pointers with no address translation, so guest and host + * must share a single address space and agree on how a pointer is stored. A + * non-zero guest_base (e.g. set via -B/-R) would make every pointer off by + * guest_base and hit unrelated host memory. * * SPDX-License-Identifier: GPL-2.0-or-later */ @@ -46,8 +48,18 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; * * It defaults to DLCALL_SYSCALL_DEFAULT and can be overridden at load time * with the "syscall_num=N" argument. To avoid hijacking a real syscall the - * guest might issue, N must be at least DLCALL_SYSCALL_MIN: every Linux ABI - * keeps its syscall numbers well below this; numbers from here up are free. + * guest might issue, N must be at least DLCALL_SYSCALL_MIN, which most Linux + * ABIs keep their syscall numbers well below. + * + * N also has to reach the filter at all, which bounds it from above in a + * target specific way: arm32 answers anything past ARM_NR_BASE (0xf0000) with + * ENOSYS or SIGILL before do_syscall() runs, while aarch64 has no such bound. + * + * MIPS O32 bases its numbering at 4000, so the default is a real syscall there + * (getpriority). Raising N does not help either, because O32 rejects numbers + * its table does not define, again before the filter runs, which leaves no + * number that is both free and reachable on that ABI. Its N32 and N64 ABIs + * base at 6000 and 5000 and have no such gate, so they are unaffected. */ enum { DLCALL_SYSCALL_DEFAULT = 4096, @@ -168,6 +180,7 @@ static bool vcpu_syscall_filter(unsigned int vcpu_index, case DLCALL_ID_FREE_LIBRARY: { void *handle = (void *) a2; int *ret_ptr = (int *) a3; + assert(ret_ptr); *ret_ptr = dlclose(handle); *sysret = 0; break; @@ -176,6 +189,7 @@ static bool vcpu_syscall_filter(unsigned int vcpu_index, /* Get the last error message for a library event. */ case DLCALL_ID_GET_LIBRARY_ERROR: { const char **error_ptr = (const char **) a2; + assert(error_ptr); *error_ptr = dlerror(); *sysret = 0; break; diff --git a/contrib/plugins/execlog.c b/contrib/plugins/execlog.c index 74325495cc..cb0bd399d8 100644 --- a/contrib/plugins/execlog.c +++ b/contrib/plugins/execlog.c @@ -31,8 +31,12 @@ typedef struct CPU { QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; -static GArray *cpus; -static GRWLock expand_array_lock; +/* + * Per-vCPU state stored in a qemu_plugin_scoreboard. The scoreboard manages + * per-vCPU storage automatically, eliminating the need for manual array + * growth, locks, or pointer-stability workarounds. + */ +static struct qemu_plugin_scoreboard *cpus; static GPtrArray *imatches; static GArray *amatches; @@ -41,23 +45,13 @@ static bool disas_assist; static GMutex add_reg_name_lock; static GPtrArray *all_reg_names; -static CPU *get_cpu(int vcpu_index) -{ - CPU *c; - g_rw_lock_reader_lock(&expand_array_lock); - c = &g_array_index(cpus, CPU, vcpu_index); - g_rw_lock_reader_unlock(&expand_array_lock); - - return c; -} - /** * Add memory read or write information to current instruction log */ static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t info, uint64_t vaddr, void *udata) { - CPU *c = get_cpu(cpu_index); + CPU *c = qemu_plugin_scoreboard_find(cpus, cpu_index); GString *s = c->last_exec; /* Find vCPU in array */ @@ -117,7 +111,7 @@ static void insn_check_regs(CPU *cpu) /* Log last instruction while checking registers */ static void vcpu_insn_exec_with_regs(unsigned int cpu_index, void *udata) { - CPU *cpu = get_cpu(cpu_index); + CPU *cpu = qemu_plugin_scoreboard_find(cpus, cpu_index); /* Print previous instruction in cache */ if (cpu->last_exec->len) { @@ -125,8 +119,8 @@ static void vcpu_insn_exec_with_regs(unsigned int cpu_index, void *udata) insn_check_regs(cpu); } + g_string_append_c(cpu->last_exec, '\n'); qemu_plugin_outs(cpu->last_exec->str); - qemu_plugin_outs("\n"); } /* Store new instruction in cache */ @@ -138,7 +132,7 @@ static void vcpu_insn_exec_with_regs(unsigned int cpu_index, void *udata) /* Log last instruction while checking registers, ignore next */ static void vcpu_insn_exec_only_regs(unsigned int cpu_index, void *udata) { - CPU *cpu = get_cpu(cpu_index); + CPU *cpu = qemu_plugin_scoreboard_find(cpus, cpu_index); /* Print previous instruction in cache */ if (cpu->last_exec->len) { @@ -146,8 +140,8 @@ static void vcpu_insn_exec_only_regs(unsigned int cpu_index, void *udata) insn_check_regs(cpu); } + g_string_append_c(cpu->last_exec, '\n'); qemu_plugin_outs(cpu->last_exec->str); - qemu_plugin_outs("\n"); } /* reset */ @@ -157,12 +151,12 @@ static void vcpu_insn_exec_only_regs(unsigned int cpu_index, void *udata) /* Log last instruction without checking regs, setup next */ static void vcpu_insn_exec(unsigned int cpu_index, void *udata) { - CPU *cpu = get_cpu(cpu_index); + CPU *cpu = qemu_plugin_scoreboard_find(cpus, cpu_index); /* Print previous instruction in cache */ if (cpu->last_exec->len) { + g_string_append_c(cpu->last_exec, '\n'); qemu_plugin_outs(cpu->last_exec->str); - qemu_plugin_outs("\n"); } /* Store new instruction in cache */ @@ -378,40 +372,47 @@ static GPtrArray *registers_init(int vcpu_index) * - last_exec tracking data * - list of tracked registers * - initial value of registers - * - * As we could have multiple threads trying to do this we need to - * serialise the expansion under a lock. */ static void vcpu_init(unsigned int vcpu_index, void *userdata) { - CPU *c; - - g_rw_lock_writer_lock(&expand_array_lock); - if (vcpu_index >= cpus->len) { - g_array_set_size(cpus, vcpu_index + 1); - } - g_rw_lock_writer_unlock(&expand_array_lock); - - c = get_cpu(vcpu_index); + CPU *c = qemu_plugin_scoreboard_find(cpus, vcpu_index); c->last_exec = g_string_new(NULL); c->registers = registers_init(vcpu_index); } /** - * On plugin exit, print last instruction in cache + * On vCPU exit, flush the last cached instruction for this vCPU. + * + * The one-instruction-delay pattern stores each instruction in last_exec and + * only prints it when the *next* callback fires. When a thread exits via + * syscall (e.g. ecall/exit), no subsequent callback fires for that vCPU and + * the final instruction is silently dropped. Flushing here guarantees it is + * written before the vCPU is torn down. + */ +static void vcpu_exit(unsigned int vcpu_index, void *udata) +{ + CPU *c = qemu_plugin_scoreboard_find(cpus, vcpu_index); + if (c->last_exec && c->last_exec->len) { + g_string_append_c(c->last_exec, '\n'); + qemu_plugin_outs(c->last_exec->str); + g_string_truncate(c->last_exec, 0); + } +} + +/** + * On plugin exit, flush any remaining cached instructions and free state. */ static void plugin_exit(void *p) { - guint i; - g_rw_lock_reader_lock(&expand_array_lock); - for (i = 0; i < cpus->len; i++) { - CPU *c = get_cpu(i); - if (c->last_exec && c->last_exec->str) { + int n = qemu_plugin_num_vcpus(); + for (int i = 0; i < n; i++) { + CPU *c = qemu_plugin_scoreboard_find(cpus, i); + if (c->last_exec && c->last_exec->len) { + g_string_append_c(c->last_exec, '\n'); qemu_plugin_outs(c->last_exec->str); - qemu_plugin_outs("\n"); } } - g_rw_lock_reader_unlock(&expand_array_lock); + qemu_plugin_scoreboard_free(cpus); } /* Add a match to the array of matches */ @@ -452,12 +453,8 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) { - /* - * Initialize dynamic array to cache vCPU instruction. In user mode - * we don't know the size before emulation. - */ - cpus = g_array_sized_new(true, true, sizeof(CPU), - info->system_emulation ? info->system.max_vcpus : 1); + /* Initialize scoreboard to cache per-vCPU instruction state. */ + cpus = qemu_plugin_scoreboard_new(sizeof(CPU)); for (int i = 0; i < argc; i++) { char *opt = argv[i]; @@ -483,6 +480,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, /* Register init, translation block and exit callbacks */ qemu_plugin_register_vcpu_init_cb(id, vcpu_init, NULL); qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans, NULL); + qemu_plugin_register_vcpu_exit_cb(id, vcpu_exit, NULL); qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); return 0; diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c index bb41758e34..933bdbb671 100644 --- a/contrib/vhost-user-gpu/vhost-user-gpu.c +++ b/contrib/vhost-user-gpu/vhost-user-gpu.c @@ -388,7 +388,13 @@ vg_resource_create_2d(VuGpu *g, cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; return; } - vugbm_buffer_create(&res->buffer, &g->gdev, c2d.width, c2d.height); + if (!vugbm_buffer_create(&res->buffer, &g->gdev, c2d.width, c2d.height)) { + g_critical("%s: buffer creation failed %d %d %d", + __func__, c2d.resource_id, c2d.width, c2d.height); + g_free(res); + cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY; + return; + } res->image = pixman_image_create_bits(pformat, c2d.width, c2d.height, @@ -481,7 +487,7 @@ vg_create_mapping_iov(VuGpu *g, struct virtio_gpu_ctrl_command *cmd, struct iovec **iov) { - struct virtio_gpu_mem_entry *ents; + g_autofree struct virtio_gpu_mem_entry *ents = NULL; size_t esize, s; int i; @@ -492,17 +498,22 @@ vg_create_mapping_iov(VuGpu *g, } esize = sizeof(*ents) * ab->nr_entries; - ents = g_malloc(esize); + ents = g_try_malloc(esize); + if (!ents && esize) { + return -1; + } s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, sizeof(*ab), ents, esize); if (s != esize) { g_critical("%s: command data size incorrect %zu vs %zu", __func__, s, esize); - g_free(ents); return -1; } - *iov = g_new0(struct iovec, ab->nr_entries); + *iov = g_try_new0(struct iovec, ab->nr_entries); + if (!*iov && ab->nr_entries) { + return -1; + } for (i = 0; i < ab->nr_entries; i++) { uint64_t len = ents[i].length; (*iov)[i].iov_len = ents[i].length; @@ -511,12 +522,10 @@ vg_create_mapping_iov(VuGpu *g, g_critical("%s: resource %d element %d", __func__, ab->resource_id, i); g_free(*iov); - g_free(ents); *iov = NULL; return -1; } } - g_free(ents); return 0; } @@ -822,8 +831,14 @@ vg_resource_flush(VuGpu *g, PIXMAN_FORMAT_BPP(pixman_image_get_format(res->image)) / 8; size_t size = width * height * bpp; - void *p = g_malloc(VHOST_USER_GPU_HDR_SIZE + - sizeof(VhostUserGpuUpdate) + size); + void *p = g_try_malloc(VHOST_USER_GPU_HDR_SIZE + + sizeof(VhostUserGpuUpdate) + size); + if (!p) { + pixman_region_fini(®ion); + pixman_region_fini(&finalregion); + cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY; + break; + } VhostUserGpuMsg *msg = p; msg->request = VHOST_USER_GPU_UPDATE; msg->size = sizeof(VhostUserGpuUpdate) + size; @@ -924,16 +939,19 @@ vg_handle_ctrl(VuDev *dev, int qidx) if (len != sizeof(cmd->cmd_hdr)) { g_warning("%s: command size incorrect %zu vs %zu\n", __func__, len, sizeof(cmd->cmd_hdr)); - } - - virtio_gpu_ctrl_hdr_bswap(&cmd->cmd_hdr); - g_debug("%d %s\n", cmd->cmd_hdr.type, - vg_cmd_to_string(cmd->cmd_hdr.type)); - - if (vg->virgl) { - vg_virgl_process_cmd(vg, cmd); + memset(&cmd->cmd_hdr, 0, sizeof(cmd->cmd_hdr)); + vg_ctrl_response_nodata( + vg, cmd, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER); } else { - vg_process_cmd(vg, cmd); + virtio_gpu_ctrl_hdr_bswap(&cmd->cmd_hdr); + g_debug("%d %s\n", cmd->cmd_hdr.type, + vg_cmd_to_string(cmd->cmd_hdr.type)); + + if (vg->virgl) { + vg_virgl_process_cmd(vg, cmd); + } else { + vg_process_cmd(vg, cmd); + } } if (cmd->state != VG_CMD_STATE_FINISHED) { diff --git a/contrib/vhost-user-gpu/virgl.c b/contrib/vhost-user-gpu/virgl.c index 51da0e3667..20bae57d0f 100644 --- a/contrib/vhost-user-gpu/virgl.c +++ b/contrib/vhost-user-gpu/virgl.c @@ -202,7 +202,18 @@ virgl_cmd_submit_3d(VuGpu *g, VUGPU_FILL_CMD(cs); - buf = g_malloc(cs.size); + if (cs.size > VIRTIO_GPU_MAX_CMD_SUBMIT_SIZE) { + g_critical("%s: command buffer too large (%u)", + __func__, cs.size); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + + buf = g_try_malloc(cs.size); + if (!buf && cs.size) { + cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY; + return; + } s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, sizeof(cs), buf, cs.size); if (s != cs.size) { diff --git a/contrib/vhost-user-gpu/vugbm.c b/contrib/vhost-user-gpu/vugbm.c index 503d0a4566..e2d8385fd8 100644 --- a/contrib/vhost-user-gpu/vugbm.c +++ b/contrib/vhost-user-gpu/vugbm.c @@ -13,7 +13,10 @@ static bool mem_alloc_bo(struct vugbm_buffer *buf) { - buf->mmap = g_malloc(buf->width * buf->height * 4); + buf->mmap = g_try_malloc((uint64_t)buf->width * buf->height * 4); + if (!buf->mmap && buf->width && buf->height) { + return false; + } buf->stride = buf->width * 4; return true; } @@ -53,7 +56,8 @@ struct udmabuf_create { static size_t udmabuf_get_size(struct vugbm_buffer *buf) { - return ROUND_UP(buf->width * buf->height * 4, qemu_real_host_page_size()); + return ROUND_UP((uint64_t)buf->width * buf->height * 4, + qemu_real_host_page_size()); } static bool @@ -293,6 +297,12 @@ bool vugbm_buffer_create(struct vugbm_buffer *buffer, struct vugbm_device *dev, uint32_t width, uint32_t height) { + uint64_t size = (uint64_t)width * height * 4; + if (size > UINT32_MAX) { + g_warning("buffer dimensions too large: %ux%u", width, height); + return false; + } + buffer->dev = dev; buffer->width = width; buffer->height = height; diff --git a/contrib/vhost-user-gpu/vugpu.h b/contrib/vhost-user-gpu/vugpu.h index 654c392fbb..aaf2870cb2 100644 --- a/contrib/vhost-user-gpu/vugpu.h +++ b/contrib/vhost-user-gpu/vugpu.h @@ -22,6 +22,7 @@ #include "qemu/queue.h" #include "qemu/iov.h" #include "qemu/bswap.h" +#include "qemu/units.h" #include "vugbm.h" typedef enum VhostUserGpuRequest { @@ -163,6 +164,14 @@ struct virtio_gpu_ctrl_command { QTAILQ_ENTRY(virtio_gpu_ctrl_command) next; }; +/* + * With 4 KiB pages and QEMU's VIRTQUEUE_MAX_SIZE (1024) mapped-iov + * limit, the largest inline command is ~4 MiB. Cap submit_3d + * allocations to this value to prevent a malicious guest from + * triggering an OOM abort via an inflated cs.size field. + */ +#define VIRTIO_GPU_MAX_CMD_SUBMIT_SIZE (4 * MiB) + #define VUGPU_FILL_CMD(out) do { \ size_t vugpufillcmd_s_ = \ iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 0, \ @@ -170,6 +179,7 @@ struct virtio_gpu_ctrl_command { if (vugpufillcmd_s_ != sizeof(out)) { \ g_critical("%s: command size incorrect %zu vs %zu", \ __func__, vugpufillcmd_s_, sizeof(out)); \ + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; \ return; \ } \ } while (0) diff --git a/crypto/cipher-gcrypt.c.inc b/crypto/cipher-gcrypt.c.inc index 12eb9ddb5a..fce09a3c77 100644 --- a/crypto/cipher-gcrypt.c.inc +++ b/crypto/cipher-gcrypt.c.inc @@ -65,6 +65,8 @@ static int qcrypto_cipher_mode_to_gcry_mode(QCryptoCipherMode mode) return GCRY_CIPHER_MODE_CBC; case QCRYPTO_CIPHER_MODE_CTR: return GCRY_CIPHER_MODE_CTR; + case QCRYPTO_CIPHER_MODE_GCM: + return GCRY_CIPHER_MODE_GCM; default: return GCRY_CIPHER_MODE_NONE; } @@ -104,6 +106,10 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgo alg, case QCRYPTO_CIPHER_MODE_XTS: case QCRYPTO_CIPHER_MODE_CTR: return true; + case QCRYPTO_CIPHER_MODE_GCM: + /* GCM requires a 128-bit block cipher. */ + return gcry_cipher_get_algo_blklen( + qcrypto_cipher_alg_to_gcry_alg(alg)) == 16; default: return false; } @@ -228,6 +234,99 @@ static const struct QCryptoCipherDriver qcrypto_gcrypt_ctr_driver = { .cipher_free = qcrypto_gcrypt_ctx_free, }; +/* + * GCM is an AEAD stream mode: the IV/nonce need not match the block size, + * the message length need not be a multiple of the block size, associated + * data is fed with gcry_cipher_authenticate() and the authentication tag is + * read back with gcry_cipher_gettag(). + */ +static int qcrypto_gcrypt_gcm_setiv(QCryptoCipher *cipher, + const uint8_t *iv, size_t niv, + Error **errp) +{ + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base); + gcry_error_t err; + + gcry_cipher_reset(ctx->handle); + err = gcry_cipher_setiv(ctx->handle, iv, niv); + if (err != 0) { + error_setg(errp, "Cannot set IV: %s", gcry_strerror(err)); + return -1; + } + + return 0; +} + +static int qcrypto_gcrypt_gcm_setaad(QCryptoCipher *cipher, + const uint8_t *aad, size_t len, + Error **errp) +{ + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base); + gcry_error_t err; + + err = gcry_cipher_authenticate(ctx->handle, aad, len); + if (err != 0) { + error_setg(errp, "Cannot set AAD: %s", gcry_strerror(err)); + return -1; + } + + return 0; +} + +static int qcrypto_gcrypt_gcm_encrypt(QCryptoCipher *cipher, const void *in, + void *out, size_t len, Error **errp) +{ + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base); + gcry_error_t err; + + err = gcry_cipher_encrypt(ctx->handle, out, len, in, len); + if (err != 0) { + error_setg(errp, "Cannot encrypt data: %s", gcry_strerror(err)); + return -1; + } + + return 0; +} + +static int qcrypto_gcrypt_gcm_decrypt(QCryptoCipher *cipher, const void *in, + void *out, size_t len, Error **errp) +{ + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base); + gcry_error_t err; + + err = gcry_cipher_decrypt(ctx->handle, out, len, in, len); + if (err != 0) { + error_setg(errp, "Cannot decrypt data: %s", gcry_strerror(err)); + return -1; + } + + return 0; +} + +static int qcrypto_gcrypt_gcm_gettag(QCryptoCipher *cipher, + uint8_t *tag, size_t len, Error **errp) +{ + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base); + gcry_error_t err; + + err = gcry_cipher_gettag(ctx->handle, tag, len); + if (err != 0) { + error_setg(errp, "Cannot get tag: %s", gcry_strerror(err)); + return -1; + } + + return 0; +} + +static const struct QCryptoCipherDriver qcrypto_gcrypt_gcm_driver = { + .cipher_encrypt = qcrypto_gcrypt_gcm_encrypt, + .cipher_decrypt = qcrypto_gcrypt_gcm_decrypt, + .cipher_setiv = qcrypto_gcrypt_gcm_setiv, + .cipher_setaad = qcrypto_gcrypt_gcm_setaad, + .cipher_gettag = qcrypto_gcrypt_gcm_gettag, + .cipher_free = qcrypto_gcrypt_ctx_free, +}; + static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg, QCryptoCipherMode mode, const uint8_t *key, @@ -259,6 +358,8 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg, if (mode == QCRYPTO_CIPHER_MODE_CTR) { drv = &qcrypto_gcrypt_ctr_driver; + } else if (mode == QCRYPTO_CIPHER_MODE_GCM) { + drv = &qcrypto_gcrypt_gcm_driver; } else { drv = &qcrypto_gcrypt_driver; } diff --git a/crypto/cipher-gnutls.c.inc b/crypto/cipher-gnutls.c.inc index a8263fff6d..963b328fa1 100644 --- a/crypto/cipher-gnutls.c.inc +++ b/crypto/cipher-gnutls.c.inc @@ -48,6 +48,15 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgo alg, default: return false; } + case QCRYPTO_CIPHER_MODE_GCM: + switch (alg) { + case QCRYPTO_CIPHER_ALGO_AES_128: + case QCRYPTO_CIPHER_ALGO_AES_192: + case QCRYPTO_CIPHER_ALGO_AES_256: + return true; + default: + return false; + } default: return false; } @@ -223,6 +232,147 @@ static struct QCryptoCipherDriver gnutls_driver = { .cipher_free = qcrypto_gnutls_cipher_free, }; +/* + * GCM is an AEAD stream mode: the nonce need not match the block size, the + * message length need not be a multiple of the block size, associated data is + * fed with gnutls_cipher_add_auth() and the authentication tag is read back + * with gnutls_cipher_tag(). + */ +static int +qcrypto_gnutls_cipher_encrypt_gcm(QCryptoCipher *cipher, + const void *in, void *out, + size_t len, Error **errp) +{ + QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base); + int err; + + err = gnutls_cipher_encrypt2(ctx->handle, in, len, out, len); + if (err != 0) { + error_setg(errp, "Cannot encrypt data: %s", gnutls_strerror(err)); + return -1; + } + + return 0; +} + +static int +qcrypto_gnutls_cipher_decrypt_gcm(QCryptoCipher *cipher, + const void *in, void *out, + size_t len, Error **errp) +{ + QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base); + int err; + + err = gnutls_cipher_decrypt2(ctx->handle, in, len, out, len); + if (err != 0) { + error_setg(errp, "Cannot decrypt data: %s", gnutls_strerror(err)); + return -1; + } + + return 0; +} + +static int +qcrypto_gnutls_cipher_setiv_gcm(QCryptoCipher *cipher, + const uint8_t *iv, size_t niv, + Error **errp) +{ + QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base); + + gnutls_cipher_set_iv(ctx->handle, (void *)iv, niv); + + return 0; +} + +static int +qcrypto_gnutls_cipher_setaad_gcm(QCryptoCipher *cipher, + const uint8_t *aad, size_t len, + Error **errp) +{ + QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base); + int err; + + err = gnutls_cipher_add_auth(ctx->handle, aad, len); + if (err != 0) { + error_setg(errp, "Cannot add associated data: %s", + gnutls_strerror(err)); + return -1; + } + + return 0; +} + +static int +qcrypto_gnutls_cipher_gettag_gcm(QCryptoCipher *cipher, + uint8_t *tag, size_t len, + Error **errp) +{ + QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base); + int err; + + err = gnutls_cipher_tag(ctx->handle, tag, len); + if (err != 0) { + error_setg(errp, "Cannot get authentication tag: %s", + gnutls_strerror(err)); + return -1; + } + + return 0; +} + +static struct QCryptoCipherDriver gnutls_gcm_driver = { + .cipher_encrypt = qcrypto_gnutls_cipher_encrypt_gcm, + .cipher_decrypt = qcrypto_gnutls_cipher_decrypt_gcm, + .cipher_setiv = qcrypto_gnutls_cipher_setiv_gcm, + .cipher_setaad = qcrypto_gnutls_cipher_setaad_gcm, + .cipher_gettag = qcrypto_gnutls_cipher_gettag_gcm, + .cipher_free = qcrypto_gnutls_cipher_free, +}; + +static QCryptoCipher * +qcrypto_gnutls_aes_gcm_ctx_new(QCryptoCipherAlgo alg, const uint8_t *key, + size_t nkey, Error **errp) +{ + gnutls_datum_t gkey = { (unsigned char *)key, nkey }; + gnutls_cipher_algorithm_t galg = GNUTLS_CIPHER_UNKNOWN; + QCryptoCipherGnutls *ctx; + int err; + + switch (alg) { + case QCRYPTO_CIPHER_ALGO_AES_128: + galg = GNUTLS_CIPHER_AES_128_GCM; + break; + case QCRYPTO_CIPHER_ALGO_AES_192: + galg = GNUTLS_CIPHER_AES_192_GCM; + break; + case QCRYPTO_CIPHER_ALGO_AES_256: + galg = GNUTLS_CIPHER_AES_256_GCM; + break; + default: + error_setg(errp, "Unsupported cipher algorithm %s with GCM mode", + QCryptoCipherAlgo_str(alg)); + return NULL; + } + + if (!qcrypto_cipher_validate_key_length(alg, QCRYPTO_CIPHER_MODE_GCM, + nkey, errp)) { + return NULL; + } + + ctx = g_new0(QCryptoCipherGnutls, 1); + ctx->base.driver = &gnutls_gcm_driver; + ctx->blocksize = 16; + + err = gnutls_cipher_init(&ctx->handle, galg, &gkey, NULL); + if (err != 0) { + error_setg(errp, "Cannot initialize cipher: %s", gnutls_strerror(err)); + g_free(ctx); + return NULL; + } + + return &ctx->base; +} + static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg, QCryptoCipherMode mode, const uint8_t *key, @@ -234,6 +384,10 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg, gnutls_cipher_algorithm_t galg = GNUTLS_CIPHER_UNKNOWN; int err; + if (mode == QCRYPTO_CIPHER_MODE_GCM) { + return qcrypto_gnutls_aes_gcm_ctx_new(alg, key, nkey, errp); + } + switch (mode) { case QCRYPTO_CIPHER_MODE_XTS: switch (alg) { diff --git a/crypto/cipher-nettle.c.inc b/crypto/cipher-nettle.c.inc index 1afdc391b4..d4847f0efe 100644 --- a/crypto/cipher-nettle.c.inc +++ b/crypto/cipher-nettle.c.inc @@ -27,6 +27,7 @@ #include #include #include +#include #ifdef CONFIG_CRYPTO_SM4 #include #endif @@ -410,6 +411,125 @@ DEFINE_ECB(qcrypto_nettle_sm4, sm4_encrypt_native, sm4_decrypt_native) #endif +/* + * GCM is an AEAD mode built on AES (128-bit block only). Drive it through the + * generic gcm_* interface, using the block cipher's encrypt function for both + * directions; associated data is fed with gcm_update() and the authentication + * tag is produced by gcm_digest(). + */ +typedef struct QCryptoNettleAESGCM { + QCryptoCipher base; + struct gcm_key gcm_key; + struct gcm_ctx gcm_ctx; + union { + struct aes128_ctx aes128; + struct aes192_ctx aes192; + struct aes256_ctx aes256; + } cipher; + nettle_cipher_func *encrypt; +} QCryptoNettleAESGCM; + +static int qcrypto_nettle_aes_gcm_setiv(QCryptoCipher *cipher, + const uint8_t *iv, size_t niv, + Error **errp) +{ + QCryptoNettleAESGCM *ctx = container_of(cipher, QCryptoNettleAESGCM, base); + + gcm_set_iv(&ctx->gcm_ctx, &ctx->gcm_key, niv, iv); + return 0; +} + +static int qcrypto_nettle_aes_gcm_setaad(QCryptoCipher *cipher, + const uint8_t *aad, size_t len, + Error **errp) +{ + QCryptoNettleAESGCM *ctx = container_of(cipher, QCryptoNettleAESGCM, base); + + gcm_update(&ctx->gcm_ctx, &ctx->gcm_key, len, aad); + return 0; +} + +static int qcrypto_nettle_aes_gcm_encrypt(QCryptoCipher *cipher, + const void *in, void *out, + size_t len, Error **errp) +{ + QCryptoNettleAESGCM *ctx = container_of(cipher, QCryptoNettleAESGCM, base); + + gcm_encrypt(&ctx->gcm_ctx, &ctx->gcm_key, &ctx->cipher, ctx->encrypt, + len, out, in); + return 0; +} + +static int qcrypto_nettle_aes_gcm_decrypt(QCryptoCipher *cipher, + const void *in, void *out, + size_t len, Error **errp) +{ + QCryptoNettleAESGCM *ctx = container_of(cipher, QCryptoNettleAESGCM, base); + + gcm_decrypt(&ctx->gcm_ctx, &ctx->gcm_key, &ctx->cipher, ctx->encrypt, + len, out, in); + return 0; +} + +static int qcrypto_nettle_aes_gcm_gettag(QCryptoCipher *cipher, + uint8_t *tag, size_t len, + Error **errp) +{ + QCryptoNettleAESGCM *ctx = container_of(cipher, QCryptoNettleAESGCM, base); + + gcm_digest(&ctx->gcm_ctx, &ctx->gcm_key, &ctx->cipher, ctx->encrypt, + len, tag); + return 0; +} + +static const struct QCryptoCipherDriver qcrypto_nettle_aes_gcm_driver = { + .cipher_encrypt = qcrypto_nettle_aes_gcm_encrypt, + .cipher_decrypt = qcrypto_nettle_aes_gcm_decrypt, + .cipher_setiv = qcrypto_nettle_aes_gcm_setiv, + .cipher_setaad = qcrypto_nettle_aes_gcm_setaad, + .cipher_gettag = qcrypto_nettle_aes_gcm_gettag, + .cipher_free = qcrypto_cipher_ctx_free, +}; + +static QCryptoCipher *qcrypto_nettle_aes_gcm_ctx_new(QCryptoCipherAlgo alg, + const uint8_t *key, + size_t nkey, + Error **errp) +{ + QCryptoNettleAESGCM *ctx; + + if (!qcrypto_cipher_validate_key_length(alg, QCRYPTO_CIPHER_MODE_GCM, + nkey, errp)) { + return NULL; + } + + ctx = g_new0(QCryptoNettleAESGCM, 1); + ctx->base.driver = &qcrypto_nettle_aes_gcm_driver; + + switch (alg) { + case QCRYPTO_CIPHER_ALGO_AES_128: + aes128_set_encrypt_key(&ctx->cipher.aes128, key); + ctx->encrypt = aes128_encrypt_native; + break; + case QCRYPTO_CIPHER_ALGO_AES_192: + aes192_set_encrypt_key(&ctx->cipher.aes192, key); + ctx->encrypt = aes192_encrypt_native; + break; + case QCRYPTO_CIPHER_ALGO_AES_256: + aes256_set_encrypt_key(&ctx->cipher.aes256, key); + ctx->encrypt = aes256_encrypt_native; + break; + default: + error_setg(errp, "Unsupported cipher algorithm %s with GCM mode", + QCryptoCipherAlgo_str(alg)); + g_free(ctx); + return NULL; + } + + gcm_set_key(&ctx->gcm_key, &ctx->cipher, ctx->encrypt); + return &ctx->base; +} + bool qcrypto_cipher_supports(QCryptoCipherAlgo alg, QCryptoCipherMode mode) { @@ -440,6 +560,10 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgo alg, case QCRYPTO_CIPHER_MODE_XTS: case QCRYPTO_CIPHER_MODE_CTR: return true; + case QCRYPTO_CIPHER_MODE_GCM: + return alg == QCRYPTO_CIPHER_ALGO_AES_128 || + alg == QCRYPTO_CIPHER_ALGO_AES_192 || + alg == QCRYPTO_CIPHER_ALGO_AES_256; default: return false; } @@ -451,6 +575,10 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg, size_t nkey, Error **errp) { + if (mode == QCRYPTO_CIPHER_MODE_GCM) { + return qcrypto_nettle_aes_gcm_ctx_new(alg, key, nkey, errp); + } + switch (mode) { case QCRYPTO_CIPHER_MODE_ECB: case QCRYPTO_CIPHER_MODE_CBC: diff --git a/crypto/cipher.c b/crypto/cipher.c index 515165e0dc..1dc912b217 100644 --- a/crypto/cipher.c +++ b/crypto/cipher.c @@ -66,6 +66,7 @@ static const bool mode_need_iv[QCRYPTO_CIPHER_MODE__MAX] = { [QCRYPTO_CIPHER_MODE_CBC] = true, [QCRYPTO_CIPHER_MODE_XTS] = true, [QCRYPTO_CIPHER_MODE_CTR] = true, + [QCRYPTO_CIPHER_MODE_GCM] = true, }; @@ -142,7 +143,7 @@ qcrypto_cipher_validate_key_length(QCryptoCipherAlgo alg, #include "cipher-gcrypt.c.inc" #elif defined CONFIG_NETTLE #include "cipher-nettle.c.inc" -#elif defined CONFIG_GNUTLS +#elif defined CONFIG_GNUTLS_CRYPTO #include "cipher-gnutls.c.inc" #else #include "cipher-stub.c.inc" @@ -204,6 +205,37 @@ int qcrypto_cipher_setiv(QCryptoCipher *cipher, } +int qcrypto_cipher_setaad(QCryptoCipher *cipher, + const uint8_t *aad, size_t len, + Error **errp) +{ + const QCryptoCipherDriver *drv = cipher->driver; + + if (!drv->cipher_setaad) { + error_setg(errp, "The cipher mode does not support associated data"); + return -1; + } + + return drv->cipher_setaad(cipher, aad, len, errp); +} + + +int qcrypto_cipher_gettag(QCryptoCipher *cipher, + uint8_t *tag, size_t len, + Error **errp) +{ + const QCryptoCipherDriver *drv = cipher->driver; + + if (!drv->cipher_gettag) { + error_setg(errp, + "The cipher mode does not produce an authentication tag"); + return -1; + } + + return drv->cipher_gettag(cipher, tag, len, errp); +} + + void qcrypto_cipher_free(QCryptoCipher *cipher) { if (cipher) { diff --git a/crypto/cipherpriv.h b/crypto/cipherpriv.h index 64737ce961..c4f995f369 100644 --- a/crypto/cipherpriv.h +++ b/crypto/cipherpriv.h @@ -34,6 +34,14 @@ struct QCryptoCipherDriver { const uint8_t *iv, size_t niv, Error **errp); + int (*cipher_setaad)(QCryptoCipher *cipher, + const uint8_t *aad, size_t len, + Error **errp); + + int (*cipher_gettag)(QCryptoCipher *cipher, + uint8_t *tag, size_t len, + Error **errp); + void (*cipher_free)(QCryptoCipher *cipher); }; diff --git a/crypto/hash-nettle.c b/crypto/hash-nettle.c index 53f68301ef..2589bbf4c1 100644 --- a/crypto/hash-nettle.c +++ b/crypto/hash-nettle.c @@ -24,7 +24,8 @@ #include "crypto/hash.h" #include "hashpriv.h" #include -#include +#include +#include #include #ifdef CONFIG_CRYPTO_SM3 #include diff --git a/crypto/meson.build b/crypto/meson.build index b51597a879..6ac83857aa 100644 --- a/crypto/meson.build +++ b/crypto/meson.build @@ -38,7 +38,7 @@ if nettle.found() endif elif gcrypt.found() crypto_ss.add(gcrypt, files('hash-gcrypt.c', 'hmac-gcrypt.c', 'pbkdf-gcrypt.c')) -elif gnutls.found() +elif gnutls_crypto.found() crypto_ss.add(gnutls, files('hash-gnutls.c', 'hmac-gnutls.c', 'pbkdf-gnutls.c')) else crypto_ss.add(files('hash-glib.c', 'hmac-glib.c', 'pbkdf-stub.c')) diff --git a/disas/capstone.c b/disas/capstone.c index fe3efb0d3c..a5bd91890b 100644 --- a/disas/capstone.c +++ b/disas/capstone.c @@ -49,6 +49,20 @@ static const cs_opt_skipdata cap_skipdata_s390x = { .callback = cap_skipdata_s390x_cb }; +/* Similarly for RISCV */ +static size_t CAPSTONE_API +cap_skipdata_riscv_cb(const uint8_t *code, size_t code_size, + size_t offset, void *user_data) +{ + /* See insn_len() from target/riscv/internals.h */ + return (code[offset] & 3) == 3 ? 4 : 2; +} + +static const cs_opt_skipdata cap_skipdata_riscv = { + .mnemonic = ".byte", + .callback = cap_skipdata_riscv_cb +}; + /* * Initialize the Capstone library. * @@ -76,7 +90,12 @@ static cs_err cap_disas_start(disassemble_info *info, csh *handle) cs_option(*handle, CS_OPT_SKIPDATA, CS_OPT_ON); switch (info->cap_arch) { - case CS_ARCH_SYSZ: + case CS_ARCH_RISCV: + cs_option(*handle, CS_OPT_SKIPDATA_SETUP, + (uintptr_t)&cap_skipdata_riscv); + break; + + case CS_ARCH_SYSTEMZ: cs_option(*handle, CS_OPT_SKIPDATA_SETUP, (uintptr_t)&cap_skipdata_s390x); break; @@ -107,8 +126,9 @@ static void cap_dump_insn_units(disassemble_info *info, cs_insn *insn, { fprintf_function print = info->fprintf_func; FILE *stream = info->stream; + int unit = MIN(info->cap_insn_unit, n - i); - switch (info->cap_insn_unit) { + switch (unit) { case 4: if (info->endian == BFD_ENDIAN_BIG) { for (; i < n; i += 4) { @@ -140,6 +160,11 @@ static void cap_dump_insn_units(disassemble_info *info, cs_insn *insn, } break; } + + if (unit < info->cap_insn_unit) { + int width = (info->cap_insn_unit - unit) * 2; + print(stream, "%*s", width, ""); + } } static void cap_dump_insn(disassemble_info *info, cs_insn *insn) diff --git a/disas/hexagon.c b/disas/hexagon.c index 36b8321c26..e2d3804606 100644 --- a/disas/hexagon.c +++ b/disas/hexagon.c @@ -31,7 +31,6 @@ int print_insn_hexagon(bfd_vma memaddr, struct disassemble_info *info) { - const HexagonCPUDef *hex_def = (const HexagonCPUDef *)info->target_info; uint32_t words[PACKET_WORDS_MAX]; bool found_end = false; GString *buf; @@ -58,8 +57,9 @@ int print_insn_hexagon(bfd_vma memaddr, struct disassemble_info *info) return PACKET_WORDS_MAX * sizeof(uint32_t); } + const HexagonCPUConfig *cfg = info->target_info; buf = g_string_sized_new(PACKET_BUFFER_LEN); - len = disassemble_hexagon(words, i, memaddr, buf, hex_def); + len = disassemble_hexagon(words, i, memaddr, buf, cfg); (*info->fprintf_func)(info->stream, "%s", buf->str); g_string_free(buf, true); diff --git a/disas/riscv-op.c.inc b/disas/riscv-op.c.inc new file mode 100644 index 0000000000..1d334e4857 --- /dev/null +++ b/disas/riscv-op.c.inc @@ -0,0 +1,913 @@ +OP(add, "add", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(add_uw, "add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(addd, "addd", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(addi, "addi", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_addi) +OP(addid, "addid", rv_codec_i, rv_fmt_rd_rs1_imm) +OP(addiw, "addiw", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_addiw) +OP(addw, "addw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(aes32dsi, "aes32dsi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs) +OP(aes32dsmi, "aes32dsmi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs) +OP(aes32esi, "aes32esi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs) +OP(aes32esmi, "aes32esmi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs) +OP(aes64ds, "aes64ds", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(aes64dsm, "aes64dsm", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(aes64es, "aes64es", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(aes64esm, "aes64esm", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(aes64im, "aes64im", rv_codec_r, rv_fmt_rd_rs1) +OP(aes64ks1i, "aes64ks1i", rv_codec_k_rnum, rv_fmt_rd_rs1_rnum) +OP(aes64ks2, "aes64ks2", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(amoadd_b, "amoadd.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoadd_d, "amoadd.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoadd_h, "amoadd.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoadd_q, "amoadd.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoadd_w, "amoadd.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoand_b, "amoand.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoand_d, "amoand.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoand_h, "amoand.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoand_q, "amoand.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoand_w, "amoand.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amocas_b, "amocas.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amocas_d, "amocas.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amocas_h, "amocas.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amocas_q, "amocas.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amocas_w, "amocas.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomax_b, "amomax.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomax_d, "amomax.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomax_h, "amomax.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomax_q, "amomax.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomax_w, "amomax.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomaxu_b, "amomaxu.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomaxu_d, "amomaxu.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomaxu_h, "amomaxu.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomaxu_q, "amomaxu.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomaxu_w, "amomaxu.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomin_b, "amomin.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomin_d, "amomin.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomin_h, "amomin.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomin_q, "amomin.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomin_w, "amomin.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amominu_b, "amominu.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amominu_d, "amominu.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amominu_h, "amominu.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amominu_q, "amominu.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amominu_w, "amominu.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoor_b, "amoor.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoor_d, "amoor.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoor_h, "amoor.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoor_q, "amoor.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoor_w, "amoor.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoswap_b, "amoswap.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoswap_d, "amoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoswap_h, "amoswap.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoswap_q, "amoswap.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoswap_w, "amoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoxor_b, "amoxor.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoxor_d, "amoxor.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoxor_h, "amoxor.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoxor_q, "amoxor.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoxor_w, "amoxor.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(and, "and", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(andi, "andi", rv_codec_i, rv_fmt_rd_rs1_imm) +OP(andn, "andn", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(auipc, "auipc", rv_codec_u, rv_fmt_rd_uoffset) +OP(bclr, "bclr", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(bclri, "bclri", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(beq, "beq", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_beq) +OP(beqz, "beqz", rv_codec_illegal, rv_fmt_rs1_offset) +OP(bext, "bext", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(bexti, "bexti", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(bge, "bge", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bge) +OP(bgeu, "bgeu", rv_codec_sb, rv_fmt_rs1_rs2_offset) +OP(bgez, "bgez", rv_codec_illegal, rv_fmt_rs1_offset) +OP(bgtz, "bgtz", rv_codec_illegal, rv_fmt_rs2_offset) +OP(binv, "binv", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(binvi, "binvi", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(blez, "blez", rv_codec_illegal, rv_fmt_rs2_offset) +OP(blt, "blt", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_blt) +OP(bltu, "bltu", rv_codec_sb, rv_fmt_rs1_rs2_offset) +OP(bltz, "bltz", rv_codec_illegal, rv_fmt_rs1_offset) +OP(bne, "bne", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bne) +OP(bnez, "bnez", rv_codec_illegal, rv_fmt_rs1_offset) +OP(brev8, "brev8", rv_codec_r, rv_fmt_rd_rs1) +OP(bset, "bset", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(bseti, "bseti", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(c_add, "c.add", rv_codec_cr, NULL, DECOMP(op_add)) +OP(c_addi, "c.addi", rv_codec_ci, NULL, DECOMP(op_addi)) +OP(c_addi16sp, "c.addi16sp", rv_codec_ci_16sp, NULL, DECOMP(op_addi)) +OP(c_addi4spn, "c.addi4spn", rv_codec_ciw_4spn, NULL, DECOMP(op_addi)) +OP(c_addiw, "c.addiw", rv_codec_ci, NULL, DECOMP(op_addiw)) +OP(c_addw, "c.addw", rv_codec_cs, NULL, DECOMP(op_addw)) +OP(c_and, "c.and", rv_codec_cs, NULL, DECOMP(op_and)) +OP(c_andi, "c.andi", rv_codec_cb_imm, NULL, DECOMP(op_andi)) +OP(c_beqz, "c.beqz", rv_codec_cb, NULL, DECOMP(op_beqz)) +OP(c_bnez, "c.bnez", rv_codec_cb, NULL, DECOMP(op_bnez)) +OP(c_ebreak, "c.ebreak", rv_codec_ci_none, NULL, DECOMP(op_ebreak)) +OP(c_fld, "c.fld", rv_codec_cl_ld, NULL, DECOMP(op_fld)) +OP(c_fldsp, "c.fldsp", rv_codec_ci_ldsp, NULL, DECOMP(op_fld)) +OP(c_flw, "c.flw", rv_codec_cl_lw, NULL, DECOMP(op_flw)) +OP(c_flwsp, "c.flwsp", rv_codec_ci_lwsp, NULL, DECOMP(op_flw)) +OP(c_fsd, "c.fsd", rv_codec_cs_sd, NULL, DECOMP(op_fsd)) +OP(c_fsdsp, "c.fsdsp", rv_codec_css_sdsp, NULL, DECOMP(op_fsd)) +OP(c_fsw, "c.fsw", rv_codec_cs_sw, NULL, DECOMP(op_fsw)) +OP(c_fswsp, "c.fswsp", rv_codec_css_swsp, NULL, DECOMP(op_fsw)) +OP(c_j, "c.j", rv_codec_cj, NULL, DECOMP(op_j)) +OP(c_jal, "c.jal", rv_codec_cj_jal, NULL, DECOMP(op_jal)) +OP(c_jalr, "c.jalr", rv_codec_cr_jalr, NULL, DECOMP(op_jalr)) +OP(c_jr, "c.jr", rv_codec_cr_jr, NULL, DECOMP(op_jr)) +OP(c_lbu, "c.lbu", rv_codec_zcb_lb, rv_fmt_rs1_rs2_zce_ldst) +OP(c_ld, "c.ld", rv_codec_cl_ld, NULL, DECOMP(op_ld)) +OP(c_ldsp, "c.ldsp", rv_codec_ci_ldsp, NULL, DECOMP(op_ld)) +OP(c_lh, "c.lh", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst) +OP(c_lhu, "c.lhu", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst) +OP(c_li, "c.li", rv_codec_ci_li, NULL, DECOMP(op_addi)) +OP(c_lq, "c.lq", rv_codec_cl_lq, NULL, DECOMP(op_lq)) +OP(c_lqsp, "c.lqsp", rv_codec_ci_lqsp, NULL, DECOMP(op_lq)) +OP(c_lui, "c.lui", rv_codec_ci_lui, NULL, DECOMP(op_lui)) +OP(c_lw, "c.lw", rv_codec_cl_lw, NULL, DECOMP(op_lw)) +OP(c_lwsp, "c.lwsp", rv_codec_ci_lwsp, NULL, DECOMP(op_lw)) +OP(c_mop, "c.mop", rv_codec_cmop, rv_fmt_cmop) +OP(c_mul, "c.mul", rv_codec_zcb_mul, rv_fmt_rd_rs2) +OP(c_mv, "c.mv", rv_codec_cr_mv, NULL, DECOMP(op_mv)) +OP(c_not, "c.not", rv_codec_zcb_ext, rv_fmt_rd) +OP(c_or, "c.or", rv_codec_cs, NULL, DECOMP(op_or)) +OP(c_sb, "c.sb", rv_codec_zcb_lb, rv_fmt_rs1_rs2_zce_ldst) +OP(c_sd, "c.sd", rv_codec_cs_sd, NULL, DECOMP(op_sd)) +OP(c_sdsp, "c.sdsp", rv_codec_css_sdsp, NULL, DECOMP(op_sd)) +OP(c_sext_b, "c.sext.b", rv_codec_zcb_ext, rv_fmt_rd) +OP(c_sext_h, "c.sext.h", rv_codec_zcb_ext, rv_fmt_rd) +OP(c_sh, "c.sh", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst) +OP(c_slli, "c.slli", rv_codec_ci_sh6, NULL, DECOMP(op_slli)) +OP(c_sq, "c.sq", rv_codec_cs_sq, NULL, DECOMP(op_sq)) +OP(c_sqsp, "c.sqsp", rv_codec_css_sqsp, NULL, DECOMP(op_sq)) +OP(c_srai, "c.srai", rv_codec_cb_sh6, NULL, DECOMP(op_srai)) +OP(c_srli, "c.srli", rv_codec_cb_sh6, NULL, DECOMP(op_srli)) +OP(c_sspopchk, "c.sspopchk", rv_codec_cmop_ss, NULL, DECOMP(op_sspopchk)) +OP(c_sspush, "c.sspush", rv_codec_cmop_ss, NULL, DECOMP(op_sspush)) +OP(c_sub, "c.sub", rv_codec_cs, NULL, DECOMP(op_sub)) +OP(c_subw, "c.subw", rv_codec_cs, NULL, DECOMP(op_subw)) +OP(c_sw, "c.sw", rv_codec_cs_sw, NULL, DECOMP(op_sw)) +OP(c_swsp, "c.swsp", rv_codec_css_swsp, NULL, DECOMP(op_sw)) +OP(c_xor, "c.xor", rv_codec_cs, NULL, DECOMP(op_xor)) +OP(c_zext_b, "c.zext.b", rv_codec_zcb_ext, rv_fmt_rd) +OP(c_zext_h, "c.zext.h", rv_codec_zcb_ext, rv_fmt_rd) +OP(c_zext_w, "c.zext.w", rv_codec_zcb_ext, rv_fmt_rd) +OP(cbo_clean, "cbo.clean", rv_codec_r, rv_fmt_rs1) +OP(cbo_flush, "cbo.flush", rv_codec_r, rv_fmt_rs1) +OP(cbo_inval, "cbo.inval", rv_codec_r, rv_fmt_rs1) +OP(cbo_zero, "cbo.zero", rv_codec_r, rv_fmt_rs1) +OP(clmul, "clmul", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(clmulh, "clmulh", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(clmulr, "clmulr", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(clz, "clz", rv_codec_r, rv_fmt_rd_rs1) +OP(clzw, "clzw", rv_codec_r, rv_fmt_rd_rs1) +OP(cm_jalt, "cm.jalt", rv_codec_zcmt_jt, rv_fmt_zcmt_index) +OP(cm_jt, "cm.jt", rv_codec_zcmt_jt, rv_fmt_zcmt_index) +OP(cm_mva01s, "cm.mva01s", rv_codec_zcmp_cm_mv, rv_fmt_rd_rs2) +OP(cm_mvsa01, "cm.mvsa01", rv_codec_zcmp_cm_mv, rv_fmt_rd_rs2) +OP(cm_pop, "cm.pop", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist) +OP(cm_popret, "cm.popret", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist) +OP(cm_popretz, "cm.popretz", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist) +OP(cm_push, "cm.push", rv_codec_zcmp_cm_pushpop, rv_fmt_push_rlist) +OP(cpop, "cpop", rv_codec_r, rv_fmt_rd_rs1) +OP(cpopw, "cpopw", rv_codec_r, rv_fmt_rd_rs1) +OP(csrrc, "csrrc", rv_codec_i_csr, rv_fmt_rd_csr_rs1) +OP(csrrci, "csrrci", rv_codec_i_csr, rv_fmt_rd_csr_zimm) +OP(csrrs, "csrrs", rv_codec_i_csr, rv_fmt_rd_csr_rs1) +OP(csrrsi, "csrrsi", rv_codec_i_csr, rv_fmt_rd_csr_zimm) +OP(csrrw, "csrrw", rv_codec_i_csr, rv_fmt_rd_csr_rs1) +OP(csrrwi, "csrrwi", rv_codec_i_csr, rv_fmt_rd_csr_zimm) +OP(ctz, "ctz", rv_codec_r, rv_fmt_rd_rs1) +OP(ctzw, "ctzw", rv_codec_r, rv_fmt_rd_rs1) +OP(czero_eqz, "czero.eqz", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(czero_nez, "czero.nez", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(div, "div", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(divd, "divd", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(divu, "divu", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(divud, "divud", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(divuw, "divuw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(divw, "divw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(dret, "dret", rv_codec_none, rv_fmt_none) +OP(ebreak, "ebreak", rv_codec_none, rv_fmt_none) +OP(ecall, "ecall", rv_codec_none, rv_fmt_none) +OP(fabs_d, "fabs.d", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fabs_q, "fabs.q", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fabs_s, "fabs.s", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fadd_d, "fadd.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fadd_q, "fadd.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fadd_s, "fadd.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fclass_d, "fclass.d", rv_codec_r, rv_fmt_rd_frs1) +OP(fclass_q, "fclass.q", rv_codec_r, rv_fmt_rd_frs1) +OP(fclass_s, "fclass.s", rv_codec_r, rv_fmt_rd_frs1) +OP(fcvt_bf16_s, "fcvt.bf16.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fcvt_d_l, "fcvt.d.l", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_d_lu, "fcvt.d.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_d_q, "fcvt.d.q", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fcvt_d_s, "fcvt.d.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fcvt_d_w, "fcvt.d.w", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_d_wu, "fcvt.d.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_l_d, "fcvt.l.d", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_l_q, "fcvt.l.q", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_l_s, "fcvt.l.s", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_lu_d, "fcvt.lu.d", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_lu_q, "fcvt.lu.q", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_lu_s, "fcvt.lu.s", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_q_d, "fcvt.q.d", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fcvt_q_l, "fcvt.q.l", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_q_lu, "fcvt.q.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_q_s, "fcvt.q.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fcvt_q_w, "fcvt.q.w", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_q_wu, "fcvt.q.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_s_bf16, "fcvt.s.bf16", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fcvt_s_d, "fcvt.s.d", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fcvt_s_l, "fcvt.s.l", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_s_lu, "fcvt.s.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_s_q, "fcvt.s.q", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fcvt_s_w, "fcvt.s.w", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_s_wu, "fcvt.s.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_w_d, "fcvt.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_w_q, "fcvt.w.q", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_w_s, "fcvt.w.s", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_wu_d, "fcvt.wu.d", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_wu_q, "fcvt.wu.q", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_wu_s, "fcvt.wu.s", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvtmod_w_d, "fcvtmod.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fdiv_d, "fdiv.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fdiv_q, "fdiv.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fdiv_s, "fdiv.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fence, "fence", rv_codec_r_f, rv_fmt_pred_succ) +OP(fence_i, "fence.i", rv_codec_none, rv_fmt_none) +OP(feq_d, "feq.d", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(feq_q, "feq.q", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(feq_s, "feq.s", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fld, "fld", rv_codec_i, rv_fmt_frd_offset_rs1) +OP(fle_d, "fle.d", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fle_q, "fle.q", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fle_s, "fle.s", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fleq_d, "fleq.d", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fleq_h, "fleq.h", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fleq_q, "fleq.q", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fleq_s, "fleq.s", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(flh, "flh", rv_codec_i, rv_fmt_frd_offset_rs1) +OP(fli_d, "fli.d", rv_codec_fli, rv_fmt_fli) +OP(fli_h, "fli.h", rv_codec_fli, rv_fmt_fli) +OP(fli_q, "fli.q", rv_codec_fli, rv_fmt_fli) +OP(fli_s, "fli.s", rv_codec_fli, rv_fmt_fli) +OP(flq, "flq", rv_codec_i, rv_fmt_frd_offset_rs1) +OP(flt_d, "flt.d", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(flt_q, "flt.q", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(flt_s, "flt.s", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fltq_d, "fltq.d", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fltq_h, "fltq.h", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fltq_q, "fltq.q", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fltq_s, "fltq.s", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(flw, "flw", rv_codec_i, rv_fmt_frd_offset_rs1) +OP(fmadd_d, "fmadd.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fmadd_q, "fmadd.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fmadd_s, "fmadd.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fmax_d, "fmax.d", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmax_q, "fmax.q", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmax_s, "fmax.s", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmaxm_d, "fmaxm.d", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmaxm_h, "fmaxm.h", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmaxm_q, "fmaxm.q", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmaxm_s, "fmaxm.s", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmin_d, "fmin.d", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmin_q, "fmin.q", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmin_s, "fmin.s", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fminm_d, "fminm.d", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fminm_h, "fminm.h", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fminm_q, "fminm.q", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fminm_s, "fminm.s", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmsub_d, "fmsub.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fmsub_q, "fmsub.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fmsub_s, "fmsub.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fmul_d, "fmul.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fmul_q, "fmul.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fmul_s, "fmul.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fmv_d, "fmv.d", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fmv_d_x, "fmv.d.x", rv_codec_r, rv_fmt_frd_rs1) +OP(fmv_h_x, "fmv.h.x", rv_codec_r, rv_fmt_frd_rs1) +OP(fmv_q, "fmv.q", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fmv_q_x, "fmv.q.x", rv_codec_r, rv_fmt_frd_rs1) +OP(fmv_s, "fmv.s", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fmv_s_x, "fmv.s.x", rv_codec_r, rv_fmt_frd_rs1) +OP(fmv_x_d, "fmv.x.d", rv_codec_r, rv_fmt_rd_frs1) +OP(fmv_x_h, "fmv.x.h", rv_codec_r, rv_fmt_rd_frs1) +OP(fmv_x_q, "fmv.x.q", rv_codec_r, rv_fmt_rd_frs1) +OP(fmv_x_s, "fmv.x.s", rv_codec_r, rv_fmt_rd_frs1) +OP(fmvh_x_d, "fmvh.x.d", rv_codec_r, rv_fmt_rd_frs1) +OP(fmvh_x_q, "fmvh.x.q", rv_codec_r, rv_fmt_rd_frs1) +OP(fmvp_d_x, "fmvp.d.x", rv_codec_r, rv_fmt_frd_rs1_rs2) +OP(fmvp_q_x, "fmvp.q.x", rv_codec_r, rv_fmt_frd_rs1_rs2) +OP(fneg_d, "fneg.d", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fneg_q, "fneg.q", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fneg_s, "fneg.s", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fnmadd_d, "fnmadd.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fnmadd_q, "fnmadd.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fnmadd_s, "fnmadd.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fnmsub_d, "fnmsub.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fnmsub_q, "fnmsub.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fnmsub_s, "fnmsub.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(frcsr, "frcsr", rv_codec_i_csr, rv_fmt_rd) +OP(frflags, "frflags", rv_codec_i_csr, rv_fmt_rd) +OP(fround_d, "fround.d", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fround_h, "fround.h", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fround_q, "fround.q", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fround_s, "fround.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(froundnx_d, "froundnx.d", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(froundnx_h, "froundnx.h", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(froundnx_q, "froundnx.q", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(froundnx_s, "froundnx.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(frrm, "frrm", rv_codec_i_csr, rv_fmt_rd) +OP(fscsr, "fscsr", rv_codec_i_csr, rv_fmt_rd_rs1) +OP(fsd, "fsd", rv_codec_s, rv_fmt_frs2_offset_rs1) +OP(fsflags, "fsflags", rv_codec_i_csr, rv_fmt_rd_rs1) +OP(fsflagsi, "fsflagsi", rv_codec_i_csr, rv_fmt_rd_zimm) +OP(fsgnj_d, "fsgnj.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_d) +OP(fsgnj_q, "fsgnj.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_q) +OP(fsgnj_s, "fsgnj.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_s) +OP(fsgnjn_d, "fsgnjn.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_d) +OP(fsgnjn_q, "fsgnjn.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_q) +OP(fsgnjn_s, "fsgnjn.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_s) +OP(fsgnjx_d, "fsgnjx.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_d) +OP(fsgnjx_q, "fsgnjx.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_q) +OP(fsgnjx_s, "fsgnjx.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_s) +OP(fsh, "fsh", rv_codec_s, rv_fmt_frs2_offset_rs1) +OP(fsq, "fsq", rv_codec_s, rv_fmt_frs2_offset_rs1) +OP(fsqrt_d, "fsqrt.d", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fsqrt_q, "fsqrt.q", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fsqrt_s, "fsqrt.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fsrm, "fsrm", rv_codec_i_csr, rv_fmt_rd_rs1) +OP(fsrmi, "fsrmi", rv_codec_i_csr, rv_fmt_rd_zimm) +OP(fsub_d, "fsub.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fsub_q, "fsub.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fsub_s, "fsub.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fsw, "fsw", rv_codec_s, rv_fmt_frs2_offset_rs1) +OP(hret, "hret", rv_codec_none, rv_fmt_none) +OP(illegal, "illegal", rv_codec_none, rv_fmt_none) +OP(j, "j", rv_codec_illegal, rv_fmt_offset) +OP(jal, "jal", rv_codec_uj, rv_fmt_rd_offset, rvcp_jal) +OP(jal_ra, "jal", rv_codec_illegal, rv_fmt_offset) +OP(jalr, "jalr", rv_codec_i, rv_fmt_rd_rs1_offset, rvcp_jalr) +OP(jalr_ra, "jalr", rv_codec_illegal, rv_fmt_rs1) +OP(jr, "jr", rv_codec_illegal, rv_fmt_rs1, rvcp_jr) +OP(lb, "lb", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(lbu, "lbu", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(ld, "ld", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(ldu, "ldu", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(lh, "lh", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(lhu, "lhu", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(lpad, "lpad", rv_codec_lp, rv_fmt_imm) +OP(lq, "lq", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(lr_d, "lr.d", rv_codec_r_l, rv_fmt_aqrl_rd_rs1) +OP(lr_q, "lr.q", rv_codec_r_l, rv_fmt_aqrl_rd_rs1) +OP(lr_w, "lr.w", rv_codec_r_l, rv_fmt_aqrl_rd_rs1) +OP(lui, "lui", rv_codec_u, rv_fmt_rd_uimm) +OP(lw, "lw", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(lwu, "lwu", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(max, "max", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(maxu, "maxu", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(min, "min", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(minu, "minu", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(mnret, "mnret", rv_codec_none, rv_fmt_none) +OP(mop_r, "mop.r", rv_codec_mop_r, rv_fmt_mop_r) +OP(mop_rr, "mop.rr", rv_codec_mop_rr, rv_fmt_mop_rr) +OP(mret, "mret", rv_codec_none, rv_fmt_none) +OP(mul, "mul", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(muld, "muld", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(mulh, "mulh", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(mulhsu, "mulhsu", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(mulhu, "mulhu", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(mulw, "mulw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(mv, "mv", rv_codec_illegal, rv_fmt_rd_rs1, rvcp_mv) +OP(neg, "neg", rv_codec_illegal, rv_fmt_rd_rs2) +OP(negw, "negw", rv_codec_illegal, rv_fmt_rd_rs2) +OP(nop, "nop", rv_codec_illegal, rv_fmt_none) +OP(not, "not", rv_codec_illegal, rv_fmt_rd_rs1) +OP(or, "or", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(orc_b, "orc.b", rv_codec_r, rv_fmt_rd_rs1) +OP(ori, "ori", rv_codec_i, rv_fmt_rd_rs1_imm) +OP(orn, "orn", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(pack, "pack", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(packh, "packh", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(packw, "packw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(rdcycle, "rdcycle", rv_codec_i_csr, rv_fmt_rd) +OP(rdcycleh, "rdcycleh", rv_codec_i_csr, rv_fmt_rd) +OP(rdinstret, "rdinstret", rv_codec_i_csr, rv_fmt_rd) +OP(rdinstreth, "rdinstreth", rv_codec_i_csr, rv_fmt_rd) +OP(rdtime, "rdtime", rv_codec_i_csr, rv_fmt_rd) +OP(rdtimeh, "rdtimeh", rv_codec_i_csr, rv_fmt_rd) +OP(rem, "rem", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(remd, "remd", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(remu, "remu", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(remud, "remud", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(remuw, "remuw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(remw, "remw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(ret, "ret", rv_codec_illegal, rv_fmt_none) +OP(rev8, "rev8", rv_codec_r, rv_fmt_rd_rs1) +OP(rol, "rol", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(rolw, "rolw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(ror, "ror", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(rori, "rori", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(roriw, "roriw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm) +OP(rorw, "rorw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sb, "sb", rv_codec_s, rv_fmt_rs2_offset_rs1) +OP(sc_d, "sc.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(sc_q, "sc.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(sc_w, "sc.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(sd, "sd", rv_codec_s, rv_fmt_rs2_offset_rs1) +OP(seqz, "seqz", rv_codec_illegal, rv_fmt_rd_rs1) +OP(sext_b, "sext.b", rv_codec_r, rv_fmt_rd_rs1) +OP(sext_h, "sext.h", rv_codec_r, rv_fmt_rd_rs1) +OP(sext_w, "sext.w", rv_codec_illegal, rv_fmt_rd_rs1) +OP(sfence_vm, "sfence.vm", rv_codec_r, rv_fmt_rs1) +OP(sfence_vma, "sfence.vma", rv_codec_r, rv_fmt_rs1_rs2) +OP(sgtz, "sgtz", rv_codec_illegal, rv_fmt_rd_rs2) +OP(sh, "sh", rv_codec_s, rv_fmt_rs2_offset_rs1) +OP(sh1add, "sh1add", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sh1add_uw, "sh1add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sh2add, "sh2add", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sh2add_uw, "sh2add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sh3add, "sh3add", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sh3add_uw, "sh3add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha256sig0, "sha256sig0", rv_codec_r, rv_fmt_rd_rs1) +OP(sha256sig1, "sha256sig1", rv_codec_r, rv_fmt_rd_rs1) +OP(sha256sum0, "sha256sum0", rv_codec_r, rv_fmt_rd_rs1) +OP(sha256sum1, "sha256sum1", rv_codec_r, rv_fmt_rd_rs1) +OP(sha512sig0, "sha512sig0", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sig0h, "sha512sig0h", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sig0l, "sha512sig0l", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sig1, "sha512sig1", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sig1h, "sha512sig1h", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sig1l, "sha512sig1l", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sum0, "sha512sum0", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sum0r, "sha512sum0r", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sum1, "sha512sum1", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sum1r, "sha512sum1r", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sll, "sll", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(slld, "slld", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(slli, "slli", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(slli_uw, "slli.uw", rv_codec_i_sh6, rv_fmt_rd_rs1_imm) +OP(sllid, "sllid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm) +OP(slliw, "slliw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm) +OP(sllw, "sllw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(slt, "slt", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_slt) +OP(slti, "slti", rv_codec_i, rv_fmt_rd_rs1_imm) +OP(sltiu, "sltiu", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_sltiu) +OP(sltu, "sltu", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_sltu) +OP(sltz, "sltz", rv_codec_illegal, rv_fmt_rd_rs1) +OP(sm3p0, "sm3p0", rv_codec_r, rv_fmt_rd_rs1) +OP(sm3p1, "sm3p1", rv_codec_r, rv_fmt_rd_rs1) +OP(sm4ed, "sm4ed", rv_codec_k_bs, rv_fmt_rs1_rs2_bs) +OP(sm4ks, "sm4ks", rv_codec_k_bs, rv_fmt_rs1_rs2_bs) +OP(snez, "snez", rv_codec_illegal, rv_fmt_rd_rs2) +OP(sq, "sq", rv_codec_s, rv_fmt_rs2_offset_rs1) +OP(sra, "sra", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(srad, "srad", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(srai, "srai", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(sraid, "sraid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm) +OP(sraiw, "sraiw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm) +OP(sraw, "sraw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sret, "sret", rv_codec_none, rv_fmt_none) +OP(srl, "srl", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(srld, "srld", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(srli, "srli", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(srlid, "srlid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm) +OP(srliw, "srliw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm) +OP(srlw, "srlw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(ssamoswap_d, "ssamoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(ssamoswap_w, "ssamoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(sspopchk, "sspopchk", rv_codec_r, rv_fmt_rs1) +OP(sspush, "sspush", rv_codec_r, rv_fmt_rs2) +OP(ssrdp, "ssrdp", rv_codec_r, rv_fmt_rd) +OP(sub, "sub", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_sub) +OP(subd, "subd", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(subw, "subw", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_subw) +OP(sw, "sw", rv_codec_s, rv_fmt_rs2_offset_rs1) +OP(unzip, "unzip", rv_codec_r, rv_fmt_rd_rs1) +OP(uret, "uret", rv_codec_none, rv_fmt_none) +OP(vaadd_vv, "vaadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vaadd_vx, "vaadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vaaddu_vv, "vaaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vaaddu_vx, "vaaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vadc_vim, "vadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl) +OP(vadc_vvm, "vadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl) +OP(vadc_vxm, "vadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl) +OP(vadd_vi, "vadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vadd_vv, "vadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vadd_vx, "vadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vaesdf_vs, "vaesdf.vs", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaesdf_vv, "vaesdf.vv", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaesdm_vs, "vaesdm.vs", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaesdm_vv, "vaesdm.vv", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaesef_vs, "vaesef.vs", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaesef_vv, "vaesef.vv", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaesem_vs, "vaesem.vs", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaesem_vv, "vaesem.vv", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaeskf1_vi, "vaeskf1.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm) +OP(vaeskf2_vi, "vaeskf2.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm) +OP(vaesz_vs, "vaesz.vs", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vand_vi, "vand.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vand_vv, "vand.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vand_vx, "vand.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vandn_vv, "vandn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vandn_vx, "vandn.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vasub_vv, "vasub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vasub_vx, "vasub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vasubu_vv, "vasubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vasubu_vx, "vasubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vbrev8_v, "vbrev8.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vbrev_v, "vbrev.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vclmul_vv, "vclmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vclmul_vx, "vclmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vclmulh_vv, "vclmulh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vclmulh_vx, "vclmulh.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vclz_v, "vclz.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vcompress_vm, "vcompress.vm", rv_codec_v_r, rv_fmt_vd_vs2_vs1) +OP(vcpop_m, "vcpop.m", rv_codec_v_r, rv_fmt_rd_vs2_vm) +OP(vcpop_v, "vcpop.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vctz_v, "vctz.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vdiv_vv, "vdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vdiv_vx, "vdiv.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vdivu_vv, "vdivu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vdivu_vx, "vdivu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vfadd_vf, "vfadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfadd_vv, "vfadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfclass_v, "vfclass.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfcvt_f_x_v, "vfcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfcvt_f_xu_v, "vfcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfcvt_rtz_x_f_v, "vfcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfcvt_rtz_xu_f_v, "vfcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfcvt_x_f_v, "vfcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfcvt_xu_f_v, "vfcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfdiv_vf, "vfdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfdiv_vv, "vfdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfirst_m, "vfirst.m", rv_codec_v_r, rv_fmt_rd_vs2_vm) +OP(vfmacc_vf, "vfmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfmacc_vv, "vfmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfmadd_vf, "vfmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfmadd_vv, "vfmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfmax_vf, "vfmax.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfmax_vv, "vfmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfmerge_vfm, "vfmerge.vfm", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vl) +OP(vfmin_vf, "vfmin.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfmin_vv, "vfmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfmsac_vf, "vfmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfmsac_vv, "vfmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfmsub_vf, "vfmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfmsub_vv, "vfmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfmul_vf, "vfmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfmul_vv, "vfmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfmv_f_s, "vfmv.f.s", rv_codec_v_r, rv_fmt_fd_vs2) +OP(vfmv_s_f, "vfmv.s.f", rv_codec_v_r, rv_fmt_vd_fs1) +OP(vfmv_v_f, "vfmv.v.f", rv_codec_v_r, rv_fmt_vd_fs1) +OP(vfncvt_f_f_w, "vfncvt.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvt_f_x_w, "vfncvt.f.x.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvt_f_xu_w, "vfncvt.f.xu.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvt_rod_f_f_w, "vfncvt.rod.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvt_rtz_x_f_w, "vfncvt.rtz.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvt_rtz_xu_f_w, "vfncvt.rtz.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvt_x_f_w, "vfncvt.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvt_xu_f_w, "vfncvt.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvtbf16_f_f_w, "vfncvtbf16.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfnmacc_vf, "vfnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfnmacc_vv, "vfnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfnmadd_vf, "vfnmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfnmadd_vv, "vfnmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfnmsac_vf, "vfnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfnmsac_vv, "vfnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfnmsub_vf, "vfnmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfnmsub_vv, "vfnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfrdiv_vf, "vfrdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfrec7_v, "vfrec7.v", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vfredmax_vs, "vfredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfredmin_vs, "vfredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfredosum_vs, "vfredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfredusum_vs, "vfredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfrsqrt7_v, "vfrsqrt7.v", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vfrsub_vf, "vfrsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfsgnj_vf, "vfsgnj.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfsgnj_vv, "vfsgnj.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfsgnjn_vf, "vfsgnjn.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfsgnjn_vv, "vfsgnjn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfsgnjx_vf, "vfsgnjx.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfsgnjx_vv, "vfsgnjx.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfslide1down_vf, "vfslide1down.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfslide1up_vf, "vfslide1up.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfsqrt_v, "vfsqrt.v", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vfsub_vf, "vfsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfsub_vv, "vfsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfwadd_vf, "vfwadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfwadd_vv, "vfwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfwadd_wf, "vfwadd.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfwadd_wv, "vfwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfwcvt_f_f_v, "vfwcvt.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwcvt_f_x_v, "vfwcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwcvt_f_xu_v, "vfwcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwcvt_rtz_x_f_v, "vfwcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwcvt_rtz_xu_f_v, "vfwcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwcvt_x_f_v, "vfwcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwcvt_xu_f_v, "vfwcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwcvtbf16_f_f_v, "vfwcvtbf16.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwmacc_vf, "vfwmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfwmacc_vv, "vfwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfwmaccbf16_vf, "vfwmaccbf16.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfwmaccbf16_vv, "vfwmaccbf16.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfwmsac_vf, "vfwmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfwmsac_vv, "vfwmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfwmul_vf, "vfwmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfwmul_vv, "vfwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfwnmacc_vf, "vfwnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfwnmacc_vv, "vfwnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfwnmsac_vf, "vfwnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfwnmsac_vv, "vfwnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfwredosum_vs, "vfwredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfwredusum_vs, "vfwredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfwsub_vf, "vfwsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfwsub_vv, "vfwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfwsub_wf, "vfwsub.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfwsub_wv, "vfwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vghsh_vv, "vghsh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1) +OP(vgmul_vv, "vgmul.vv", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vid_v, "vid.v", rv_codec_v_r, rv_fmt_vd_vm) +OP(viota_m, "viota.m", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vl1re16_v, "vl1re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl1re32_v, "vl1re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl1re64_v, "vl1re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl1re8_v, "vl1re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl2re16_v, "vl2re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl2re32_v, "vl2re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl2re64_v, "vl2re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl2re8_v, "vl2re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl4re16_v, "vl4re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl4re32_v, "vl4re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl4re64_v, "vl4re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl4re8_v, "vl4re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl8re16_v, "vl8re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl8re32_v, "vl8re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl8re64_v, "vl8re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl8re8_v, "vl8re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vle16_v, "vle16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vle16ff_v, "vle16ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vle32_v, "vle32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vle32ff_v, "vle32ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vle64_v, "vle64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vle64ff_v, "vle64ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vle8_v, "vle8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vle8ff_v, "vle8ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vlm_v, "vlm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vloxei16_v, "vloxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vloxei32_v, "vloxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vloxei64_v, "vloxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vloxei8_v, "vloxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vlse16_v, "vlse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vlse32_v, "vlse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vlse64_v, "vlse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vlse8_v, "vlse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vluxei16_v, "vluxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vluxei32_v, "vluxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vluxei64_v, "vluxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vluxei8_v, "vluxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vmacc_vv, "vmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vmacc_vx, "vmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vmadc_vim, "vmadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl) +OP(vmadc_vvm, "vmadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl) +OP(vmadc_vxm, "vmadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl) +OP(vmadd_vv, "vmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vmadd_vx, "vmadd.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vmand_mm, "vmand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmandn_mm, "vmandn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmax_vv, "vmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmax_vx, "vmax.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmaxu_vv, "vmaxu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmaxu_vx, "vmaxu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmerge_vim, "vmerge.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl) +OP(vmerge_vvm, "vmerge.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl) +OP(vmerge_vxm, "vmerge.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl) +OP(vmfeq_vf, "vmfeq.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vmfeq_vv, "vmfeq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmfge_vf, "vmfge.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vmfgt_vf, "vmfgt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vmfle_vf, "vmfle.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vmfle_vv, "vmfle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmflt_vf, "vmflt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vmflt_vv, "vmflt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmfne_vf, "vmfne.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vmfne_vv, "vmfne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmin_vv, "vmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmin_vx, "vmin.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vminu_vv, "vminu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vminu_vx, "vminu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmnand_mm, "vmnand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmnor_mm, "vmnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmor_mm, "vmor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmorn_mm, "vmorn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmsbc_vvm, "vmsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl) +OP(vmsbc_vxm, "vmsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl) +OP(vmsbf_m, "vmsbf.m", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vmseq_vi, "vmseq.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vmseq_vv, "vmseq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmseq_vx, "vmseq.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmsgt_vi, "vmsgt.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vmsgt_vx, "vmsgt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmsgtu_vi, "vmsgtu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vmsgtu_vx, "vmsgtu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmsif_m, "vmsif.m", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vmsle_vi, "vmsle.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vmsle_vv, "vmsle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmsle_vx, "vmsle.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmsleu_vi, "vmsleu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vmsleu_vv, "vmsleu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmsleu_vx, "vmsleu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmslt_vv, "vmslt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmslt_vx, "vmslt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmsltu_vv, "vmsltu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmsltu_vx, "vmsltu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmsne_vi, "vmsne.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vmsne_vv, "vmsne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmsne_vx, "vmsne.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmsof_m, "vmsof.m", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vmul_vv, "vmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmul_vx, "vmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmulh_vv, "vmulh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmulh_vx, "vmulh.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmulhsu_vv, "vmulhsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmulhsu_vx, "vmulhsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmulhu_vv, "vmulhu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmulhu_vx, "vmulhu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmv1r_v, "vmv1r.v", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vmv2r_v, "vmv2r.v", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vmv4r_v, "vmv4r.v", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vmv8r_v, "vmv8r.v", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vmv_s_x, "vmv.s.x", rv_codec_v_r, rv_fmt_vd_rs1) +OP(vmv_v_i, "vmv.v.i", rv_codec_v_i, rv_fmt_vd_imm) +OP(vmv_v_v, "vmv.v.v", rv_codec_v_r, rv_fmt_vd_vs1) +OP(vmv_v_x, "vmv.v.x", rv_codec_v_r, rv_fmt_vd_rs1) +OP(vmv_x_s, "vmv.x.s", rv_codec_v_r, rv_fmt_rd_vs2) +OP(vmxnor_mm, "vmxnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmxor_mm, "vmxor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vnclip_wi, "vnclip.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vnclip_wv, "vnclip.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vnclip_wx, "vnclip.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vnclipu_wi, "vnclipu.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vnclipu_wv, "vnclipu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vnclipu_wx, "vnclipu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vnmsac_vv, "vnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vnmsac_vx, "vnmsac.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vnmsub_vv, "vnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vnmsub_vx, "vnmsub.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vnsra_wi, "vnsra.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vnsra_wv, "vnsra.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vnsra_wx, "vnsra.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vnsrl_wi, "vnsrl.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vnsrl_wv, "vnsrl.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vnsrl_wx, "vnsrl.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vor_vi, "vor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vor_vv, "vor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vor_vx, "vor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vredand_vs, "vredand.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vredmax_vs, "vredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vredmaxu_vs, "vredmaxu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vredmin_vs, "vredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vredminu_vs, "vredminu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vredor_vs, "vredor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vredsum_vs, "vredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vredxor_vs, "vredxor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vrem_vv, "vrem.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vrem_vx, "vrem.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vremu_vv, "vremu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vremu_vx, "vremu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vrev8_v, "vrev8.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vrgather_vi, "vrgather.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vrgather_vv, "vrgather.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vrgather_vx, "vrgather.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vrgatherei16_vv, "vrgatherei16.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vrol_vv, "vrol.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vrol_vx, "vrol.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vror_vi, "vror.vi", rv_codec_vror_vi, rv_fmt_vd_vs2_uimm_vm) +OP(vror_vv, "vror.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vror_vx, "vror.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vrsub_vi, "vrsub.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vrsub_vx, "vrsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vs1r_v, "vs1r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vs2r_v, "vs2r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vs4r_v, "vs4r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vs8r_v, "vs8r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vsadd_vi, "vsadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vsadd_vv, "vsadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vsadd_vx, "vsadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vsaddu_vi, "vsaddu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vsaddu_vv, "vsaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vsaddu_vx, "vsaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vsbc_vvm, "vsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl) +OP(vsbc_vxm, "vsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl) +OP(vse16_v, "vse16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vse32_v, "vse32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vse64_v, "vse64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vse8_v, "vse8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vsetivli, "vsetivli", rv_codec_vsetivli, rv_fmt_vsetivli) +OP(vsetvl, "vsetvl", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(vsetvli, "vsetvli", rv_codec_vsetvli, rv_fmt_vsetvli) +OP(vsext_vf2, "vsext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vsext_vf4, "vsext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vsext_vf8, "vsext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vsha2ch_vv, "vsha2ch.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1) +OP(vsha2cl_vv, "vsha2cl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1) +OP(vsha2ms_vv, "vsha2ms.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1) +OP(vslide1down_vx, "vslide1down.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vslide1up_vx, "vslide1up.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vslidedown_vi, "vslidedown.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vslidedown_vx, "vslidedown.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vslideup_vi, "vslideup.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vslideup_vx, "vslideup.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vsll_vi, "vsll.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vsll_vv, "vsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vsll_vx, "vsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vsm3c_vi, "vsm3c.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm) +OP(vsm3me_vv, "vsm3me.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1) +OP(vsm4k_vi, "vsm4k.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm) +OP(vsm4r_vs, "vsm4r.vs", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vsm4r_vv, "vsm4r.vv", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vsm_v, "vsm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vsmul_vv, "vsmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vsmul_vx, "vsmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vsoxei16_v, "vsoxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vsoxei32_v, "vsoxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vsoxei64_v, "vsoxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vsoxei8_v, "vsoxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vsra_vi, "vsra.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vsra_vv, "vsra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vsra_vx, "vsra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vsrl_vi, "vsrl.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vsrl_vv, "vsrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vsrl_vx, "vsrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vsse16_v, "vsse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vsse32_v, "vsse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vsse64_v, "vsse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vsse8_v, "vsse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vssra_vi, "vssra.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vssra_vv, "vssra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vssra_vx, "vssra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vssrl_vi, "vssrl.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vssrl_vv, "vssrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vssrl_vx, "vssrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vssub_vv, "vssub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vssub_vx, "vssub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vssubu_vv, "vssubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vssubu_vx, "vssubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vsub_vv, "vsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vsub_vx, "vsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vsuxei16_v, "vsuxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vsuxei32_v, "vsuxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vsuxei64_v, "vsuxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vsuxei8_v, "vsuxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vwadd_vv, "vwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwadd_vx, "vwadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwadd_wv, "vwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwadd_wx, "vwadd.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwaddu_vv, "vwaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwaddu_vx, "vwaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwaddu_wv, "vwaddu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwaddu_wx, "vwaddu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwmacc_vv, "vwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vwmacc_vx, "vwmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vwmaccsu_vv, "vwmaccsu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vwmaccsu_vx, "vwmaccsu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vwmaccu_vv, "vwmaccu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vwmaccu_vx, "vwmaccu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vwmaccus_vx, "vwmaccus.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vwmul_vv, "vwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwmul_vx, "vwmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwmulsu_vv, "vwmulsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwmulsu_vx, "vwmulsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwmulu_vv, "vwmulu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwmulu_vx, "vwmulu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwredsum_vs, "vwredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwredsumu_vs, "vwredsumu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwsll_vi, "vwsll.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vwsll_vv, "vwsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwsll_vx, "vwsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwsub_vv, "vwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwsub_vx, "vwsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwsub_wv, "vwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwsub_wx, "vwsub.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwsubu_vv, "vwsubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwsubu_vx, "vwsubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwsubu_wv, "vwsubu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwsubu_wx, "vwsubu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vxor_vi, "vxor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vxor_vv, "vxor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vxor_vx, "vxor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vzext_vf2, "vzext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vzext_vf4, "vzext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vzext_vf8, "vzext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(wfi, "wfi", rv_codec_none, rv_fmt_none) +OP(wrs_nto, "wrs.nto", rv_codec_none, rv_fmt_none) +OP(wrs_sto, "wrs.sto", rv_codec_none, rv_fmt_none) +OP(xnor, "xnor", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(xor, "xor", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(xori, "xori", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_xori) +OP(xperm4, "xperm4", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(xperm8, "xperm8", rv_codec_r, rv_fmt_rd_rs1) +OP(zext_h, "zext.h", rv_codec_r, rv_fmt_rd_rs1) +OP(zip, "zip", rv_codec_r, rv_fmt_rd_rs1) diff --git a/disas/riscv-xlrbr-op.c.inc b/disas/riscv-xlrbr-op.c.inc new file mode 100644 index 0000000000..57add37c24 --- /dev/null +++ b/disas/riscv-xlrbr-op.c.inc @@ -0,0 +1,8 @@ +OP(crc32_b, "crc32.b", rv_codec_r, rv_fmt_rd_rs1) +OP(crc32_h, "crc32.h", rv_codec_r, rv_fmt_rd_rs1) +OP(crc32_w, "crc32.w", rv_codec_r, rv_fmt_rd_rs1) +OP(crc32_d, "crc32.d", rv_codec_r, rv_fmt_rd_rs1) +OP(crc32c_b, "crc32c.b", rv_codec_r, rv_fmt_rd_rs1) +OP(crc32c_h, "crc32c.h", rv_codec_r, rv_fmt_rd_rs1) +OP(crc32c_w, "crc32c.w", rv_codec_r, rv_fmt_rd_rs1) +OP(crc32c_d, "crc32c.d", rv_codec_r, rv_fmt_rd_rs1) diff --git a/disas/riscv-xlrbr.c b/disas/riscv-xlrbr.c index 57cb434523..b4229e5c83 100644 --- a/disas/riscv-xlrbr.c +++ b/disas/riscv-xlrbr.c @@ -8,38 +8,16 @@ */ #include "qemu/osdep.h" - #include "disas/riscv.h" #include "disas/riscv-xlrbr.h" -typedef enum { - /* 0 is reserved for rv_op_illegal. */ - rv_op_crc32_b = 1, - rv_op_crc32_h = 2, - rv_op_crc32_w = 3, - rv_op_crc32_d = 4, - rv_op_crc32c_b = 5, - rv_op_crc32c_h = 6, - rv_op_crc32c_w = 7, - rv_op_crc32c_d = 8, -} rv_xlrbr_op; +#define OP(N, ...) static const rv_opcode_data op_##N = { __VA_ARGS__ }; +#include "riscv-xlrbr-op.c.inc" +#undef OP -const rv_opcode_data rv_xlrbr_opcode_data[] = { - { "illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 }, - { "crc32.b", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "crc32.h", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "crc32.w", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "crc32.d", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "crc32c.b", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "crc32c.h", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "crc32c.w", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "crc32c.d", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, -}; - -void decode_xlrbr(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xlrbr(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch ((inst >> 0) & 0b1111111) { case 0b0010011: @@ -47,33 +25,26 @@ void decode_xlrbr(rv_decode *dec, rv_isa isa) case 0b001: switch ((inst >> 20 & 0b111111111111)) { case 0b011000010000: - op = rv_op_crc32_b; - break; + return &op_crc32_b; case 0b011000010001: - op = rv_op_crc32_h; - break; + return &op_crc32_h; case 0b011000010010: - op = rv_op_crc32_w; - break; + return &op_crc32_w; case 0b011000010011: - op = rv_op_crc32_d; - break; + return &op_crc32_d; case 0b011000011000: - op = rv_op_crc32c_b; - break; + return &op_crc32c_b; case 0b011000011001: - op = rv_op_crc32c_h; - break; + return &op_crc32c_h; case 0b011000011010: - op = rv_op_crc32c_w; - break; + return &op_crc32c_w; case 0b011000011011: - op = rv_op_crc32c_d; - break; + return &op_crc32c_d; } break; } break; } - dec->op = op; + + return NULL; } diff --git a/disas/riscv-xlrbr.h b/disas/riscv-xlrbr.h index 939a69ea6d..da14f1b340 100644 --- a/disas/riscv-xlrbr.h +++ b/disas/riscv-xlrbr.h @@ -12,8 +12,6 @@ #include "disas/riscv.h" -extern const rv_opcode_data rv_xlrbr_opcode_data[]; - -void decode_xlrbr(rv_decode *, rv_isa); +const rv_opcode_data *decode_xlrbr(rv_decode *, rv_isa); #endif /* DISAS_RISCV_XLRBR_H */ diff --git a/disas/riscv-xthead-op.c.inc b/disas/riscv-xthead-op.c.inc new file mode 100644 index 0000000000..1e4e0557cd --- /dev/null +++ b/disas/riscv-xthead-op.c.inc @@ -0,0 +1,125 @@ +/* XTheadBa */ +OP(th_addsl, "th.addsl", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) + +/* XTheadBb */ +OP(th_srri, "th.srri", rv_codec_r2_imm6, rv_fmt_rd_rs1_imm) +OP(th_srriw, "th.srriw", rv_codec_r2_imm5, rv_fmt_rd_rs1_imm) +OP(th_ext, "th.ext", rv_codec_r2_immhl, rv_fmt_rd_rs1_immh_imml) +OP(th_extu, "th.extu", rv_codec_r2_immhl, rv_fmt_rd_rs1_immh_imml) +OP(th_ff0, "th.ff0", rv_codec_r2, rv_fmt_rd_rs1) +OP(th_ff1, "th.ff1", rv_codec_r2, rv_fmt_rd_rs1) +OP(th_rev, "th.rev", rv_codec_r2, rv_fmt_rd_rs1) +OP(th_revw, "th.revw", rv_codec_r2, rv_fmt_rd_rs1) +OP(th_tstnbz, "th.tstnbz", rv_codec_r2, rv_fmt_rd_rs1) + +/* XTheadBs */ +OP(th_tst, "th.tst", rv_codec_r2_imm6, rv_fmt_rd_rs1_imm) + +/* XTheadCmo */ +OP(th_dcache_call, "th.dcache.call", rv_codec_none, rv_fmt_none) +OP(th_dcache_ciall, "th.dcache.ciall", rv_codec_none, rv_fmt_none) +OP(th_dcache_iall, "th.dcache.iall", rv_codec_none, rv_fmt_none) +OP(th_dcache_cpa, "th.dcache.cpa", rv_codec_r, rv_fmt_rs1) +OP(th_dcache_cipa, "th.dcache.cipa", rv_codec_r, rv_fmt_rs1) +OP(th_dcache_ipa, "th.dcache.ipa", rv_codec_r, rv_fmt_rs1) +OP(th_dcache_cva, "th.dcache.cva", rv_codec_r, rv_fmt_rs1) +OP(th_dcache_civa, "th.dcache.civa", rv_codec_r, rv_fmt_rs1) +OP(th_dcache_iva, "th.dcache.iva", rv_codec_r, rv_fmt_rs1) +OP(th_dcache_csw, "th.dcache.csw", rv_codec_r, rv_fmt_rs1) +OP(th_dcache_cisw, "th.dcache.cisw", rv_codec_r, rv_fmt_rs1) +OP(th_dcache_isw, "th.dcache.isw", rv_codec_r, rv_fmt_rs1) +OP(th_dcache_cpal1, "th.dcache.cpal1", rv_codec_r, rv_fmt_rs1) +OP(th_dcache_cval1, "th.dcache.cval1", rv_codec_r, rv_fmt_rs1) +OP(th_icache_iall, "th.icache.iall", rv_codec_none, rv_fmt_none) +OP(th_icache_ialls, "th.icache.ialls", rv_codec_none, rv_fmt_none) +OP(th_icache_ipa, "th.icache.ipa", rv_codec_r, rv_fmt_rs1) +OP(th_icache_iva, "th.icache.iva", rv_codec_r, rv_fmt_rs1) +OP(th_l2cache_call, "th.l2cache.call", rv_codec_none, rv_fmt_none) +OP(th_l2cache_ciall, "th.l2cache.ciall", rv_codec_none, rv_fmt_none) +OP(th_l2cache_iall, "th.l2cache.iall", rv_codec_none, rv_fmt_none) + +/* XTheadCondMov */ +OP(th_mveqz, "th.mveqz", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(th_mvnez, "th.mvnez", rv_codec_r, rv_fmt_rd_rs1_rs2) + +/* XTheadFMemIdx */ +OP(th_flrd, "th.flrd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm) +OP(th_flrw, "th.flrw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm) +OP(th_flurd, "th.flurd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm) +OP(th_flurw, "th.flurw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm) +OP(th_fsrd, "th.fsrd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm) +OP(th_fsrw, "th.fsrw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm) +OP(th_fsurd, "th.fsurd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm) +OP(th_fsurw, "th.fsurw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm) + +/* XTheadFmv */ +OP(th_fmv_hw_x, "th.fmv.hw.x", rv_codec_r, rv_fmt_rd_frs1) +OP(th_fmv_x_hw, "th.fmv.x.hw", rv_codec_r, rv_fmt_rd_frs1) + +/* XTheadMac */ +OP(th_mula, "th.mula", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(th_mulaw, "th.mulaw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(th_mulah, "th.mulah", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(th_muls, "th.muls", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(th_mulsw, "th.mulsw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(th_mulsh, "th.mulsh", rv_codec_r, rv_fmt_rd_rs1_rs2) + +/* XTheadMemIdx */ +OP(th_lbia, "th.lbia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lbib, "th.lbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lbuia, "th.lbuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lbuib, "th.lbuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lhia, "th.lhia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lhib, "th.lhib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lhuia, "th.lhuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lhuib, "th.lhuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lwia, "th.lwia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lwib, "th.lwib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lwuia, "th.lwuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lwuib, "th.lwuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_ldia, "th.ldia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_ldib, "th.ldib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_sbia, "th.sbia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_sbib, "th.sbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_shia, "th.shia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_shib, "th.shib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_swia, "th.swia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_swib, "th.swib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_sdia, "th.sdia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_sdib, "th.sdib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lrb, "th.lrb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lrbu, "th.lrbu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lrh, "th.lrh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lrhu, "th.lrhu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lrw, "th.lrw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lrwu, "th.lrwu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lrd, "th.lrd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_srb, "th.srb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_srh, "th.srh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_srw, "th.srw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_srd, "th.srd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lurb, "th.lurb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lurbu, "th.lurbu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lurh, "th.lurh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lurhu, "th.lurhu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lurw, "th.lurw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lurwu, "th.lurwu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lurd, "th.lurd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_surb, "th.surb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_surh, "th.surh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_surw, "th.surw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_surd, "th.surd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) + +/* XTheadMemPair */ +OP(th_ldd, "th.ldd", rv_codec_r_imm2, rv_fmt_rd2_imm) +OP(th_lwd, "th.lwd", rv_codec_r_imm2, rv_fmt_rd2_imm) +OP(th_lwud, "th.lwud", rv_codec_r_imm2, rv_fmt_rd2_imm) +OP(th_sdd, "th.sdd", rv_codec_r_imm2, rv_fmt_rd2_imm) +OP(th_swd, "th.swd", rv_codec_r_imm2, rv_fmt_rd2_imm) + +/* XTheadSync */ +OP(th_sfence_vmas, "th.sfence.vmas", rv_codec_r, rv_fmt_rs1_rs2) +OP(th_sync, "th.sync", rv_codec_none, rv_fmt_none) +OP(th_sync_i, "th.sync.i", rv_codec_none, rv_fmt_none) +OP(th_sync_is, "th.sync.is", rv_codec_none, rv_fmt_none) +OP(th_sync_s, "th.sync.s", rv_codec_none, rv_fmt_none) diff --git a/disas/riscv-xthead.c b/disas/riscv-xthead.c index fcca326d1c..387c22c869 100644 --- a/disas/riscv-xthead.c +++ b/disas/riscv-xthead.c @@ -8,248 +8,13 @@ #include "disas/riscv.h" #include "disas/riscv-xthead.h" -typedef enum { - /* 0 is reserved for rv_op_illegal. */ - /* XTheadBa */ - rv_op_th_addsl = 1, - /* XTheadBb */ - rv_op_th_srri, - rv_op_th_srriw, - rv_op_th_ext, - rv_op_th_extu, - rv_op_th_ff0, - rv_op_th_ff1, - rv_op_th_rev, - rv_op_th_revw, - rv_op_th_tstnbz, - /* XTheadBs */ - rv_op_th_tst, - /* XTheadCmo */ - rv_op_th_dcache_call, - rv_op_th_dcache_ciall, - rv_op_th_dcache_iall, - rv_op_th_dcache_cpa, - rv_op_th_dcache_cipa, - rv_op_th_dcache_ipa, - rv_op_th_dcache_cva, - rv_op_th_dcache_civa, - rv_op_th_dcache_iva, - rv_op_th_dcache_csw, - rv_op_th_dcache_cisw, - rv_op_th_dcache_isw, - rv_op_th_dcache_cpal1, - rv_op_th_dcache_cval1, - rv_op_th_icache_iall, - rv_op_th_icache_ialls, - rv_op_th_icache_ipa, - rv_op_th_icache_iva, - rv_op_th_l2cache_call, - rv_op_th_l2cache_ciall, - rv_op_th_l2cache_iall, - /* XTheadCondMov */ - rv_op_th_mveqz, - rv_op_th_mvnez, - /* XTheadFMemIdx */ - rv_op_th_flrd, - rv_op_th_flrw, - rv_op_th_flurd, - rv_op_th_flurw, - rv_op_th_fsrd, - rv_op_th_fsrw, - rv_op_th_fsurd, - rv_op_th_fsurw, - /* XTheadFmv */ - rv_op_th_fmv_hw_x, - rv_op_th_fmv_x_hw, - /* XTheadMac */ - rv_op_th_mula, - rv_op_th_mulah, - rv_op_th_mulaw, - rv_op_th_muls, - rv_op_th_mulsw, - rv_op_th_mulsh, - /* XTheadMemIdx */ - rv_op_th_lbia, - rv_op_th_lbib, - rv_op_th_lbuia, - rv_op_th_lbuib, - rv_op_th_lhia, - rv_op_th_lhib, - rv_op_th_lhuia, - rv_op_th_lhuib, - rv_op_th_lwia, - rv_op_th_lwib, - rv_op_th_lwuia, - rv_op_th_lwuib, - rv_op_th_ldia, - rv_op_th_ldib, - rv_op_th_sbia, - rv_op_th_sbib, - rv_op_th_shia, - rv_op_th_shib, - rv_op_th_swia, - rv_op_th_swib, - rv_op_th_sdia, - rv_op_th_sdib, - rv_op_th_lrb, - rv_op_th_lrbu, - rv_op_th_lrh, - rv_op_th_lrhu, - rv_op_th_lrw, - rv_op_th_lrwu, - rv_op_th_lrd, - rv_op_th_srb, - rv_op_th_srh, - rv_op_th_srw, - rv_op_th_srd, - rv_op_th_lurb, - rv_op_th_lurbu, - rv_op_th_lurh, - rv_op_th_lurhu, - rv_op_th_lurw, - rv_op_th_lurwu, - rv_op_th_lurd, - rv_op_th_surb, - rv_op_th_surh, - rv_op_th_surw, - rv_op_th_surd, - /* XTheadMemPair */ - rv_op_th_ldd, - rv_op_th_lwd, - rv_op_th_lwud, - rv_op_th_sdd, - rv_op_th_swd, - /* XTheadSync */ - rv_op_th_sfence_vmas, - rv_op_th_sync, - rv_op_th_sync_i, - rv_op_th_sync_is, - rv_op_th_sync_s, -} rv_xthead_op; +#define OP(N, ...) static const rv_opcode_data op_##N = { __VA_ARGS__ }; +#include "riscv-xthead-op.c.inc" +#undef OP -const rv_opcode_data xthead_opcode_data[] = { - { "th.illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 }, - /* XTheadBa */ - { "th.addsl", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - /* XTheadBb */ - { "th.srri", rv_codec_r2_imm6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "th.srriw", rv_codec_r2_imm5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "th.ext", rv_codec_r2_immhl, rv_fmt_rd_rs1_immh_imml, NULL, 0, 0, 0 }, - { "th.extu", rv_codec_r2_immhl, rv_fmt_rd_rs1_immh_imml, NULL, 0, 0, 0 }, - { "th.ff0", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "th.ff1", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "th.rev", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "th.revw", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "th.tstnbz", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - /* XTheadBs */ - { "th.tst", rv_codec_r2_imm6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - /* XTheadCmo */ - { "th.dcache.call", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "th.dcache.ciall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "th.dcache.iall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "th.dcache.cpa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.dcache.cipa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.dcache.ipa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.dcache.cva", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.dcache.civa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.dcache.iva", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.dcache.csw", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.dcache.cisw", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.dcache.isw", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.dcache.cpal1", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.dcache.cval1", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.icache.iall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "th.icache.ialls", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "th.icache.ipa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.icache.iva", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.l2cache.call", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "th.l2cache.ciall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "th.l2cache.iall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - /* XTheadCondMov */ - { "th.mveqz", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "th.mvnez", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - /* XTheadFMemIdx */ - { "th.flrd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.flrw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.flurd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.flurw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.fsrd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.fsrw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.fsurd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.fsurw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, - /* XTheadFmv */ - { "th.fmv.hw.x", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, - { "th.fmv.x.hw", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, - /* XTheadMac */ - { "th.mula", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "th.mulaw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "th.mulah", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "th.muls", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "th.mulsw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "th.mulsh", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - /* XTheadMemIdx */ - { "th.lbia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.lbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml, NULL, 0, 0, 0 }, - { "th.lbuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.lbuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.lhia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.lhib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.lhuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.lhuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.lwia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.lwib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.lwuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.lwuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.ldia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.ldib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.sbia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.sbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.shia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.shib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.swia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.swib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.sdia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.sdib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.lrb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lrbu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lrh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lrhu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lrw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lrwu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lrd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.srb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.srh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.srw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.srd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lurb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lurbu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lurh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lurhu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lurw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lurwu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lurd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.surb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.surh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.surw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.surd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - /* XTheadMemPair */ - { "th.ldd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 }, - { "th.lwd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 }, - { "th.lwud", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 }, - { "th.sdd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 }, - { "th.swd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 }, - /* XTheadSync */ - { "th.sfence.vmas", rv_codec_r, rv_fmt_rs1_rs2, NULL, 0, 0, 0 }, - { "th.sync", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "th.sync.i", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "th.sync.is", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "th.sync.s", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, -}; - -void decode_xtheadba(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xtheadba(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: @@ -262,7 +27,7 @@ void decode_xtheadba(rv_decode *dec, rv_isa isa) case 0b0000000: case 0b0000001: case 0b0000010: - case 0b0000011: op = rv_op_th_addsl; break; + case 0b0000011: return &op_th_addsl; } break; } @@ -272,13 +37,12 @@ void decode_xtheadba(rv_decode *dec, rv_isa isa) break; } - dec->op = op; + return NULL; } -void decode_xtheadbb(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xtheadbb(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: @@ -288,39 +52,41 @@ void decode_xtheadbb(rv_decode *dec, rv_isa isa) switch ((inst >> 12) & 0b111) { case 1: switch ((inst >> 25) & 0b1111111) { - case 0b0001010: op = rv_op_th_srriw; break; + case 0b0001010: return &op_th_srriw; case 0b1000000: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_tstnbz; + return &op_th_tstnbz; } break; case 0b1000001: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_rev; + return &op_th_rev; } break; case 0b1000010: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_ff0; + return &op_th_ff0; } break; case 0b1000011: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_ff1; + return &op_th_ff1; } break; case 0b1000100: case 0b1001000: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_revw; + return &op_th_revw; } break; - case 0b0000100: - case 0b0000101: op = rv_op_th_srri; break; + case 0b0001000: + case 0b0001001: + return &op_th_srri; + break; } break; - case 2: op = rv_op_th_ext; break; - case 3: op = rv_op_th_extu; break; + case 2: return &op_th_ext; + case 3: return &op_th_extu; } break; /* custom-0 */ @@ -328,13 +94,12 @@ void decode_xtheadbb(rv_decode *dec, rv_isa isa) break; } - dec->op = op; + return NULL; } -void decode_xtheadbs(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xtheadbs(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: @@ -344,7 +109,7 @@ void decode_xtheadbs(rv_decode *dec, rv_isa isa) switch ((inst >> 12) & 0b111) { case 1: switch ((inst >> 26) & 0b111111) { - case 0b100010: op = rv_op_th_tst; break; + case 0b100010: return &op_th_tst; } break; } @@ -354,13 +119,12 @@ void decode_xtheadbs(rv_decode *dec, rv_isa isa) break; } - dec->op = op; + return NULL; } -void decode_xtheadcmo(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xtheadcmo(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: @@ -372,55 +136,55 @@ void decode_xtheadcmo(rv_decode *dec, rv_isa isa) switch ((inst >> 20 & 0b111111111111)) { case 0b000000000001: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_dcache_call; + return &op_th_dcache_call; } break; case 0b000000000011: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_dcache_ciall; + return &op_th_dcache_ciall; } break; case 0b000000000010: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_dcache_iall; + return &op_th_dcache_iall; } break; - case 0b000000101001: op = rv_op_th_dcache_cpa; break; - case 0b000000101011: op = rv_op_th_dcache_cipa; break; - case 0b000000101010: op = rv_op_th_dcache_ipa; break; - case 0b000000100101: op = rv_op_th_dcache_cva; break; - case 0b000000100111: op = rv_op_th_dcache_civa; break; - case 0b000000100110: op = rv_op_th_dcache_iva; break; - case 0b000000100001: op = rv_op_th_dcache_csw; break; - case 0b000000100011: op = rv_op_th_dcache_cisw; break; - case 0b000000100010: op = rv_op_th_dcache_isw; break; - case 0b000000101000: op = rv_op_th_dcache_cpal1; break; - case 0b000000100100: op = rv_op_th_dcache_cval1; break; + case 0b000000101001: return &op_th_dcache_cpa; + case 0b000000101011: return &op_th_dcache_cipa; + case 0b000000101010: return &op_th_dcache_ipa; + case 0b000000100101: return &op_th_dcache_cva; + case 0b000000100111: return &op_th_dcache_civa; + case 0b000000100110: return &op_th_dcache_iva; + case 0b000000100001: return &op_th_dcache_csw; + case 0b000000100011: return &op_th_dcache_cisw; + case 0b000000100010: return &op_th_dcache_isw; + case 0b000000101000: return &op_th_dcache_cpal1; + case 0b000000100100: return &op_th_dcache_cval1; case 0b000000010000: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_icache_iall; + return &op_th_icache_iall; } break; case 0b000000010001: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_icache_ialls; + return &op_th_icache_ialls; } break; - case 0b000000111000: op = rv_op_th_icache_ipa; break; - case 0b000000110000: op = rv_op_th_icache_iva; break; + case 0b000000111000: return &op_th_icache_ipa; + case 0b000000110000: return &op_th_icache_iva; case 0b000000010101: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_l2cache_call; + return &op_th_l2cache_call; } break; case 0b000000010111: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_l2cache_ciall; + return &op_th_l2cache_ciall; } break; case 0b000000010110: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_l2cache_iall; + return &op_th_l2cache_iall; } break; } @@ -432,13 +196,12 @@ void decode_xtheadcmo(rv_decode *dec, rv_isa isa) break; } - dec->op = op; + return NULL; } -void decode_xtheadcondmov(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xtheadcondmov(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: @@ -448,8 +211,8 @@ void decode_xtheadcondmov(rv_decode *dec, rv_isa isa) switch ((inst >> 12) & 0b111) { case 1: switch ((inst >> 25) & 0b1111111) { - case 0b0100000: op = rv_op_th_mveqz; break; - case 0b0100001: op = rv_op_th_mvnez; break; + case 0b0100000: return &op_th_mveqz; + case 0b0100001: return &op_th_mvnez; } break; } @@ -459,13 +222,12 @@ void decode_xtheadcondmov(rv_decode *dec, rv_isa isa) break; } - dec->op = op; + return NULL; } -void decode_xtheadfmemidx(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xtheadfmemidx(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: @@ -475,18 +237,18 @@ void decode_xtheadfmemidx(rv_decode *dec, rv_isa isa) switch ((inst >> 12) & 0b111) { case 6: switch ((inst >> 27) & 0b11111) { - case 8: op = rv_op_th_flrw; break; - case 10: op = rv_op_th_flurw; break; - case 12: op = rv_op_th_flrd; break; - case 14: op = rv_op_th_flurd; break; + case 8: return &op_th_flrw; + case 10: return &op_th_flurw; + case 12: return &op_th_flrd; + case 14: return &op_th_flurd; } break; case 7: switch ((inst >> 27) & 0b11111) { - case 8: op = rv_op_th_fsrw; break; - case 10: op = rv_op_th_fsurw; break; - case 12: op = rv_op_th_fsrd; break; - case 14: op = rv_op_th_fsurd; break; + case 8: return &op_th_fsrw; + case 10: return &op_th_fsurw; + case 12: return &op_th_fsrd; + case 14: return &op_th_fsurd; } break; } @@ -496,13 +258,12 @@ void decode_xtheadfmemidx(rv_decode *dec, rv_isa isa) break; } - dec->op = op; + return NULL; } -void decode_xtheadfmv(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xtheadfmv(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: @@ -514,12 +275,12 @@ void decode_xtheadfmv(rv_decode *dec, rv_isa isa) switch ((inst >> 25) & 0b1111111) { case 0b1010000: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_fmv_hw_x; + return &op_th_fmv_hw_x; } break; case 0b1100000: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_fmv_x_hw; + return &op_th_fmv_x_hw; } break; } @@ -531,13 +292,12 @@ void decode_xtheadfmv(rv_decode *dec, rv_isa isa) break; } - dec->op = op; + return NULL; } -void decode_xtheadmac(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xtheadmac(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: @@ -547,12 +307,12 @@ void decode_xtheadmac(rv_decode *dec, rv_isa isa) switch ((inst >> 12) & 0b111) { case 1: switch ((inst >> 25) & 0b1111111) { - case 0b0010000: op = rv_op_th_mula; break; - case 0b0010001: op = rv_op_th_muls; break; - case 0b0010010: op = rv_op_th_mulaw; break; - case 0b0010011: op = rv_op_th_mulsw; break; - case 0b0010100: op = rv_op_th_mulah; break; - case 0b0010101: op = rv_op_th_mulsh; break; + case 0b0010000: return &op_th_mula; + case 0b0010001: return &op_th_muls; + case 0b0010010: return &op_th_mulaw; + case 0b0010011: return &op_th_mulsw; + case 0b0010100: return &op_th_mulah; + case 0b0010101: return &op_th_mulsh; } break; } @@ -562,13 +322,12 @@ void decode_xtheadmac(rv_decode *dec, rv_isa isa) break; } - dec->op = op; + return NULL; } -void decode_xtheadmemidx(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xtheadmemidx(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: @@ -578,54 +337,54 @@ void decode_xtheadmemidx(rv_decode *dec, rv_isa isa) switch ((inst >> 12) & 0b111) { case 4: switch ((inst >> 27) & 0b11111) { - case 0: op = rv_op_th_lrb; break; - case 1: op = rv_op_th_lbib; break; - case 2: op = rv_op_th_lurb; break; - case 3: op = rv_op_th_lbia; break; - case 4: op = rv_op_th_lrh; break; - case 5: op = rv_op_th_lhib; break; - case 6: op = rv_op_th_lurh; break; - case 7: op = rv_op_th_lhia; break; - case 8: op = rv_op_th_lrw; break; - case 9: op = rv_op_th_lwib; break; - case 10: op = rv_op_th_lurw; break; - case 11: op = rv_op_th_lwia; break; - case 12: op = rv_op_th_lrd; break; - case 13: op = rv_op_th_ldib; break; - case 14: op = rv_op_th_lurd; break; - case 15: op = rv_op_th_ldia; break; - case 16: op = rv_op_th_lrbu; break; - case 17: op = rv_op_th_lbuib; break; - case 18: op = rv_op_th_lurbu; break; - case 19: op = rv_op_th_lbuia; break; - case 20: op = rv_op_th_lrhu; break; - case 21: op = rv_op_th_lhuib; break; - case 22: op = rv_op_th_lurhu; break; - case 23: op = rv_op_th_lhuia; break; - case 24: op = rv_op_th_lrwu; break; - case 25: op = rv_op_th_lwuib; break; - case 26: op = rv_op_th_lurwu; break; - case 27: op = rv_op_th_lwuia; break; + case 0: return &op_th_lrb; + case 1: return &op_th_lbib; + case 2: return &op_th_lurb; + case 3: return &op_th_lbia; + case 4: return &op_th_lrh; + case 5: return &op_th_lhib; + case 6: return &op_th_lurh; + case 7: return &op_th_lhia; + case 8: return &op_th_lrw; + case 9: return &op_th_lwib; + case 10: return &op_th_lurw; + case 11: return &op_th_lwia; + case 12: return &op_th_lrd; + case 13: return &op_th_ldib; + case 14: return &op_th_lurd; + case 15: return &op_th_ldia; + case 16: return &op_th_lrbu; + case 17: return &op_th_lbuib; + case 18: return &op_th_lurbu; + case 19: return &op_th_lbuia; + case 20: return &op_th_lrhu; + case 21: return &op_th_lhuib; + case 22: return &op_th_lurhu; + case 23: return &op_th_lhuia; + case 24: return &op_th_lrwu; + case 25: return &op_th_lwuib; + case 26: return &op_th_lurwu; + case 27: return &op_th_lwuia; } break; case 5: switch ((inst >> 27) & 0b11111) { - case 0: op = rv_op_th_srb; break; - case 1: op = rv_op_th_sbib; break; - case 2: op = rv_op_th_surb; break; - case 3: op = rv_op_th_sbia; break; - case 4: op = rv_op_th_srh; break; - case 5: op = rv_op_th_shib; break; - case 6: op = rv_op_th_surh; break; - case 7: op = rv_op_th_shia; break; - case 8: op = rv_op_th_srw; break; - case 9: op = rv_op_th_swib; break; - case 10: op = rv_op_th_surw; break; - case 11: op = rv_op_th_swia; break; - case 12: op = rv_op_th_srd; break; - case 13: op = rv_op_th_sdib; break; - case 14: op = rv_op_th_surd; break; - case 15: op = rv_op_th_sdia; break; + case 0: return &op_th_srb; + case 1: return &op_th_sbib; + case 2: return &op_th_surb; + case 3: return &op_th_sbia; + case 4: return &op_th_srh; + case 5: return &op_th_shib; + case 6: return &op_th_surh; + case 7: return &op_th_shia; + case 8: return &op_th_srw; + case 9: return &op_th_swib; + case 10: return &op_th_surw; + case 11: return &op_th_swia; + case 12: return &op_th_srd; + case 13: return &op_th_sdib; + case 14: return &op_th_surd; + case 15: return &op_th_sdia; } break; break; @@ -636,13 +395,12 @@ void decode_xtheadmemidx(rv_decode *dec, rv_isa isa) break; } - dec->op = op; + return NULL; } -void decode_xtheadmempair(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xtheadmempair(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: @@ -652,15 +410,15 @@ void decode_xtheadmempair(rv_decode *dec, rv_isa isa) switch ((inst >> 12) & 0b111) { case 4: switch ((inst >> 27) & 0b11111) { - case 28: op = rv_op_th_lwd; break; - case 30: op = rv_op_th_lwud; break; - case 31: op = rv_op_th_ldd; break; + case 28: return &op_th_lwd; + case 30: return &op_th_lwud; + case 31: return &op_th_ldd; } break; case 5: switch ((inst >> 27) & 0b11111) { - case 28: op = rv_op_th_swd; break; - case 31: op = rv_op_th_sdd; break; + case 28: return &op_th_swd; + case 31: return &op_th_sdd; } break; } @@ -670,13 +428,12 @@ void decode_xtheadmempair(rv_decode *dec, rv_isa isa) break; } - dec->op = op; + return NULL; } -void decode_xtheadsync(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xtheadsync(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: @@ -686,13 +443,13 @@ void decode_xtheadsync(rv_decode *dec, rv_isa isa) switch ((inst >> 12) & 0b111) { case 0: switch ((inst >> 25) & 0b1111111) { - case 0b0000010: op = rv_op_th_sfence_vmas; break; + case 0b0000010: return &op_th_sfence_vmas; case 0b0000000: switch ((inst >> 20) & 0b11111) { - case 0b11000: op = rv_op_th_sync; break; - case 0b11010: op = rv_op_th_sync_i; break; - case 0b11011: op = rv_op_th_sync_is; break; - case 0b11001: op = rv_op_th_sync_s; break; + case 0b11000: return &op_th_sync; + case 0b11010: return &op_th_sync_i; + case 0b11011: return &op_th_sync_is; + case 0b11001: return &op_th_sync_s; } break; } @@ -704,5 +461,5 @@ void decode_xtheadsync(rv_decode *dec, rv_isa isa) break; } - dec->op = op; + return NULL; } diff --git a/disas/riscv-xthead.h b/disas/riscv-xthead.h index fcd42746e7..f2160be8a4 100644 --- a/disas/riscv-xthead.h +++ b/disas/riscv-xthead.h @@ -11,18 +11,16 @@ #include "disas/riscv.h" -extern const rv_opcode_data xthead_opcode_data[]; - -void decode_xtheadba(rv_decode *, rv_isa); -void decode_xtheadbb(rv_decode *, rv_isa); -void decode_xtheadbs(rv_decode *, rv_isa); -void decode_xtheadcmo(rv_decode *, rv_isa); -void decode_xtheadcondmov(rv_decode *, rv_isa); -void decode_xtheadfmemidx(rv_decode *, rv_isa); -void decode_xtheadfmv(rv_decode *, rv_isa); -void decode_xtheadmac(rv_decode *, rv_isa); -void decode_xtheadmemidx(rv_decode *, rv_isa); -void decode_xtheadmempair(rv_decode *, rv_isa); -void decode_xtheadsync(rv_decode *, rv_isa); +const rv_opcode_data *decode_xtheadba(rv_decode *, rv_isa); +const rv_opcode_data *decode_xtheadbb(rv_decode *, rv_isa); +const rv_opcode_data *decode_xtheadbs(rv_decode *, rv_isa); +const rv_opcode_data *decode_xtheadcmo(rv_decode *, rv_isa); +const rv_opcode_data *decode_xtheadcondmov(rv_decode *, rv_isa); +const rv_opcode_data *decode_xtheadfmemidx(rv_decode *, rv_isa); +const rv_opcode_data *decode_xtheadfmv(rv_decode *, rv_isa); +const rv_opcode_data *decode_xtheadmac(rv_decode *, rv_isa); +const rv_opcode_data *decode_xtheadmemidx(rv_decode *, rv_isa); +const rv_opcode_data *decode_xtheadmempair(rv_decode *, rv_isa); +const rv_opcode_data *decode_xtheadsync(rv_decode *, rv_isa); #endif /* DISAS_RISCV_XTHEAD_H */ diff --git a/disas/riscv-xventana-op.c.inc b/disas/riscv-xventana-op.c.inc new file mode 100644 index 0000000000..0fe4666aee --- /dev/null +++ b/disas/riscv-xventana-op.c.inc @@ -0,0 +1,2 @@ +OP(vt_maskc, "vt.maskc", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(vt_maskcn, "vt.maskcn", rv_codec_r, rv_fmt_rd_rs1_rs2) diff --git a/disas/riscv-xventana.c b/disas/riscv-xventana.c index cd694f15f3..5d2e07865f 100644 --- a/disas/riscv-xventana.c +++ b/disas/riscv-xventana.c @@ -8,35 +8,26 @@ #include "disas/riscv.h" #include "disas/riscv-xventana.h" -typedef enum { - /* 0 is reserved for rv_op_illegal. */ - ventana_op_vt_maskc = 1, - ventana_op_vt_maskcn = 2, -} rv_ventana_op; +#define OP(N, ...) static const rv_opcode_data op_##N = { __VA_ARGS__ }; +#include "riscv-xventana-op.c.inc" +#undef OP -const rv_opcode_data ventana_opcode_data[] = { - { "vt.illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 }, - { "vt.maskc", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "vt.maskcn", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, -}; - -void decode_xventanacondops(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xventanacondops(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: switch (((inst >> 2) & 0b11111)) { case 30: switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) { - case 6: op = ventana_op_vt_maskc; break; - case 7: op = ventana_op_vt_maskcn; break; + case 6: return &op_vt_maskc; + case 7: return &op_vt_maskcn; } break; } break; } - dec->op = op; + return NULL; } diff --git a/disas/riscv-xventana.h b/disas/riscv-xventana.h index 72be9ffa16..d28e84c7e4 100644 --- a/disas/riscv-xventana.h +++ b/disas/riscv-xventana.h @@ -11,8 +11,6 @@ #include "disas/riscv.h" -extern const rv_opcode_data ventana_opcode_data[]; - -void decode_xventanacondops(rv_decode*, rv_isa); +const rv_opcode_data *decode_xventanacondops(rv_decode*, rv_isa); #endif /* DISAS_RISCV_XVENTANA_H */ diff --git a/disas/riscv.c b/disas/riscv.c index 7f1b262773..b8099cbdf8 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -28,970 +28,6 @@ #include "disas/riscv-xventana.h" #include "disas/riscv-xlrbr.h" -typedef enum { - /* 0 is reserved for rv_op_illegal. */ - rv_op_lui = 1, - rv_op_auipc = 2, - rv_op_jal = 3, - rv_op_jalr = 4, - rv_op_beq = 5, - rv_op_bne = 6, - rv_op_blt = 7, - rv_op_bge = 8, - rv_op_bltu = 9, - rv_op_bgeu = 10, - rv_op_lb = 11, - rv_op_lh = 12, - rv_op_lw = 13, - rv_op_lbu = 14, - rv_op_lhu = 15, - rv_op_sb = 16, - rv_op_sh = 17, - rv_op_sw = 18, - rv_op_addi = 19, - rv_op_slti = 20, - rv_op_sltiu = 21, - rv_op_xori = 22, - rv_op_ori = 23, - rv_op_andi = 24, - rv_op_slli = 25, - rv_op_srli = 26, - rv_op_srai = 27, - rv_op_add = 28, - rv_op_sub = 29, - rv_op_sll = 30, - rv_op_slt = 31, - rv_op_sltu = 32, - rv_op_xor = 33, - rv_op_srl = 34, - rv_op_sra = 35, - rv_op_or = 36, - rv_op_and = 37, - rv_op_fence = 38, - rv_op_fence_i = 39, - rv_op_lwu = 40, - rv_op_ld = 41, - rv_op_sd = 42, - rv_op_addiw = 43, - rv_op_slliw = 44, - rv_op_srliw = 45, - rv_op_sraiw = 46, - rv_op_addw = 47, - rv_op_subw = 48, - rv_op_sllw = 49, - rv_op_srlw = 50, - rv_op_sraw = 51, - rv_op_ldu = 52, - rv_op_lq = 53, - rv_op_sq = 54, - rv_op_addid = 55, - rv_op_sllid = 56, - rv_op_srlid = 57, - rv_op_sraid = 58, - rv_op_addd = 59, - rv_op_subd = 60, - rv_op_slld = 61, - rv_op_srld = 62, - rv_op_srad = 63, - rv_op_mul = 64, - rv_op_mulh = 65, - rv_op_mulhsu = 66, - rv_op_mulhu = 67, - rv_op_div = 68, - rv_op_divu = 69, - rv_op_rem = 70, - rv_op_remu = 71, - rv_op_mulw = 72, - rv_op_divw = 73, - rv_op_divuw = 74, - rv_op_remw = 75, - rv_op_remuw = 76, - rv_op_muld = 77, - rv_op_divd = 78, - rv_op_divud = 79, - rv_op_remd = 80, - rv_op_remud = 81, - rv_op_lr_w = 82, - rv_op_sc_w = 83, - rv_op_amoswap_w = 84, - rv_op_amoadd_w = 85, - rv_op_amoxor_w = 86, - rv_op_amoor_w = 87, - rv_op_amoand_w = 88, - rv_op_amomin_w = 89, - rv_op_amomax_w = 90, - rv_op_amominu_w = 91, - rv_op_amomaxu_w = 92, - rv_op_lr_d = 93, - rv_op_sc_d = 94, - rv_op_amoswap_d = 95, - rv_op_amoadd_d = 96, - rv_op_amoxor_d = 97, - rv_op_amoor_d = 98, - rv_op_amoand_d = 99, - rv_op_amomin_d = 100, - rv_op_amomax_d = 101, - rv_op_amominu_d = 102, - rv_op_amomaxu_d = 103, - rv_op_lr_q = 104, - rv_op_sc_q = 105, - rv_op_amoswap_q = 106, - rv_op_amoadd_q = 107, - rv_op_amoxor_q = 108, - rv_op_amoor_q = 109, - rv_op_amoand_q = 110, - rv_op_amomin_q = 111, - rv_op_amomax_q = 112, - rv_op_amominu_q = 113, - rv_op_amomaxu_q = 114, - rv_op_ecall = 115, - rv_op_ebreak = 116, - rv_op_uret = 117, - rv_op_sret = 118, - rv_op_hret = 119, - rv_op_mret = 120, - rv_op_dret = 121, - rv_op_sfence_vm = 122, - rv_op_sfence_vma = 123, - rv_op_wfi = 124, - rv_op_csrrw = 125, - rv_op_csrrs = 126, - rv_op_csrrc = 127, - rv_op_csrrwi = 128, - rv_op_csrrsi = 129, - rv_op_csrrci = 130, - rv_op_flw = 131, - rv_op_fsw = 132, - rv_op_fmadd_s = 133, - rv_op_fmsub_s = 134, - rv_op_fnmsub_s = 135, - rv_op_fnmadd_s = 136, - rv_op_fadd_s = 137, - rv_op_fsub_s = 138, - rv_op_fmul_s = 139, - rv_op_fdiv_s = 140, - rv_op_fsgnj_s = 141, - rv_op_fsgnjn_s = 142, - rv_op_fsgnjx_s = 143, - rv_op_fmin_s = 144, - rv_op_fmax_s = 145, - rv_op_fsqrt_s = 146, - rv_op_fle_s = 147, - rv_op_flt_s = 148, - rv_op_feq_s = 149, - rv_op_fcvt_w_s = 150, - rv_op_fcvt_wu_s = 151, - rv_op_fcvt_s_w = 152, - rv_op_fcvt_s_wu = 153, - rv_op_fmv_x_s = 154, - rv_op_fclass_s = 155, - rv_op_fmv_s_x = 156, - rv_op_fcvt_l_s = 157, - rv_op_fcvt_lu_s = 158, - rv_op_fcvt_s_l = 159, - rv_op_fcvt_s_lu = 160, - rv_op_fld = 161, - rv_op_fsd = 162, - rv_op_fmadd_d = 163, - rv_op_fmsub_d = 164, - rv_op_fnmsub_d = 165, - rv_op_fnmadd_d = 166, - rv_op_fadd_d = 167, - rv_op_fsub_d = 168, - rv_op_fmul_d = 169, - rv_op_fdiv_d = 170, - rv_op_fsgnj_d = 171, - rv_op_fsgnjn_d = 172, - rv_op_fsgnjx_d = 173, - rv_op_fmin_d = 174, - rv_op_fmax_d = 175, - rv_op_fcvt_s_d = 176, - rv_op_fcvt_d_s = 177, - rv_op_fsqrt_d = 178, - rv_op_fle_d = 179, - rv_op_flt_d = 180, - rv_op_feq_d = 181, - rv_op_fcvt_w_d = 182, - rv_op_fcvt_wu_d = 183, - rv_op_fcvt_d_w = 184, - rv_op_fcvt_d_wu = 185, - rv_op_fclass_d = 186, - rv_op_fcvt_l_d = 187, - rv_op_fcvt_lu_d = 188, - rv_op_fmv_x_d = 189, - rv_op_fcvt_d_l = 190, - rv_op_fcvt_d_lu = 191, - rv_op_fmv_d_x = 192, - rv_op_flq = 193, - rv_op_fsq = 194, - rv_op_fmadd_q = 195, - rv_op_fmsub_q = 196, - rv_op_fnmsub_q = 197, - rv_op_fnmadd_q = 198, - rv_op_fadd_q = 199, - rv_op_fsub_q = 200, - rv_op_fmul_q = 201, - rv_op_fdiv_q = 202, - rv_op_fsgnj_q = 203, - rv_op_fsgnjn_q = 204, - rv_op_fsgnjx_q = 205, - rv_op_fmin_q = 206, - rv_op_fmax_q = 207, - rv_op_fcvt_s_q = 208, - rv_op_fcvt_q_s = 209, - rv_op_fcvt_d_q = 210, - rv_op_fcvt_q_d = 211, - rv_op_fsqrt_q = 212, - rv_op_fle_q = 213, - rv_op_flt_q = 214, - rv_op_feq_q = 215, - rv_op_fcvt_w_q = 216, - rv_op_fcvt_wu_q = 217, - rv_op_fcvt_q_w = 218, - rv_op_fcvt_q_wu = 219, - rv_op_fclass_q = 220, - rv_op_fcvt_l_q = 221, - rv_op_fcvt_lu_q = 222, - rv_op_fcvt_q_l = 223, - rv_op_fcvt_q_lu = 224, - rv_op_fmv_x_q = 225, - rv_op_fmv_q_x = 226, - rv_op_c_addi4spn = 227, - rv_op_c_fld = 228, - rv_op_c_lw = 229, - rv_op_c_flw = 230, - rv_op_c_fsd = 231, - rv_op_c_sw = 232, - rv_op_c_fsw = 233, - rv_op_c_nop = 234, - rv_op_c_addi = 235, - rv_op_c_jal = 236, - rv_op_c_li = 237, - rv_op_c_addi16sp = 238, - rv_op_c_lui = 239, - rv_op_c_srli = 240, - rv_op_c_srai = 241, - rv_op_c_andi = 242, - rv_op_c_sub = 243, - rv_op_c_xor = 244, - rv_op_c_or = 245, - rv_op_c_and = 246, - rv_op_c_subw = 247, - rv_op_c_addw = 248, - rv_op_c_j = 249, - rv_op_c_beqz = 250, - rv_op_c_bnez = 251, - rv_op_c_slli = 252, - rv_op_c_fldsp = 253, - rv_op_c_lwsp = 254, - rv_op_c_flwsp = 255, - rv_op_c_jr = 256, - rv_op_c_mv = 257, - rv_op_c_ebreak = 258, - rv_op_c_jalr = 259, - rv_op_c_add = 260, - rv_op_c_fsdsp = 261, - rv_op_c_swsp = 262, - rv_op_c_fswsp = 263, - rv_op_c_ld = 264, - rv_op_c_sd = 265, - rv_op_c_addiw = 266, - rv_op_c_ldsp = 267, - rv_op_c_sdsp = 268, - rv_op_c_lq = 269, - rv_op_c_sq = 270, - rv_op_c_lqsp = 271, - rv_op_c_sqsp = 272, - rv_op_nop = 273, - rv_op_mv = 274, - rv_op_not = 275, - rv_op_neg = 276, - rv_op_negw = 277, - rv_op_sext_w = 278, - rv_op_seqz = 279, - rv_op_snez = 280, - rv_op_sltz = 281, - rv_op_sgtz = 282, - rv_op_fmv_s = 283, - rv_op_fabs_s = 284, - rv_op_fneg_s = 285, - rv_op_fmv_d = 286, - rv_op_fabs_d = 287, - rv_op_fneg_d = 288, - rv_op_fmv_q = 289, - rv_op_fabs_q = 290, - rv_op_fneg_q = 291, - rv_op_beqz = 292, - rv_op_bnez = 293, - rv_op_blez = 294, - rv_op_bgez = 295, - rv_op_bltz = 296, - rv_op_bgtz = 297, - rv_op_ble = 298, - rv_op_bleu = 299, - rv_op_bgt = 300, - rv_op_bgtu = 301, - rv_op_j = 302, - rv_op_ret = 303, - rv_op_jr = 304, - rv_op_rdcycle = 305, - rv_op_rdtime = 306, - rv_op_rdinstret = 307, - rv_op_rdcycleh = 308, - rv_op_rdtimeh = 309, - rv_op_rdinstreth = 310, - rv_op_frcsr = 311, - rv_op_frrm = 312, - rv_op_frflags = 313, - rv_op_fscsr = 314, - rv_op_fsrm = 315, - rv_op_fsflags = 316, - rv_op_fsrmi = 317, - rv_op_fsflagsi = 318, - rv_op_bseti = 319, - rv_op_bclri = 320, - rv_op_binvi = 321, - rv_op_bexti = 322, - rv_op_rori = 323, - rv_op_clz = 324, - rv_op_ctz = 325, - rv_op_cpop = 326, - rv_op_sext_h = 327, - rv_op_sext_b = 328, - rv_op_xnor = 329, - rv_op_orn = 330, - rv_op_andn = 331, - rv_op_rol = 332, - rv_op_ror = 333, - rv_op_sh1add = 334, - rv_op_sh2add = 335, - rv_op_sh3add = 336, - rv_op_sh1add_uw = 337, - rv_op_sh2add_uw = 338, - rv_op_sh3add_uw = 339, - rv_op_clmul = 340, - rv_op_clmulr = 341, - rv_op_clmulh = 342, - rv_op_min = 343, - rv_op_minu = 344, - rv_op_max = 345, - rv_op_maxu = 346, - rv_op_clzw = 347, - rv_op_ctzw = 348, - rv_op_cpopw = 349, - rv_op_slli_uw = 350, - rv_op_add_uw = 351, - rv_op_rolw = 352, - rv_op_rorw = 353, - rv_op_rev8 = 354, - rv_op_zext_h = 355, - rv_op_roriw = 356, - rv_op_orc_b = 357, - rv_op_bset = 358, - rv_op_bclr = 359, - rv_op_binv = 360, - rv_op_bext = 361, - rv_op_aes32esmi = 362, - rv_op_aes32esi = 363, - rv_op_aes32dsmi = 364, - rv_op_aes32dsi = 365, - rv_op_aes64ks1i = 366, - rv_op_aes64ks2 = 367, - rv_op_aes64im = 368, - rv_op_aes64esm = 369, - rv_op_aes64es = 370, - rv_op_aes64dsm = 371, - rv_op_aes64ds = 372, - rv_op_sha256sig0 = 373, - rv_op_sha256sig1 = 374, - rv_op_sha256sum0 = 375, - rv_op_sha256sum1 = 376, - rv_op_sha512sig0 = 377, - rv_op_sha512sig1 = 378, - rv_op_sha512sum0 = 379, - rv_op_sha512sum1 = 380, - rv_op_sha512sum0r = 381, - rv_op_sha512sum1r = 382, - rv_op_sha512sig0l = 383, - rv_op_sha512sig0h = 384, - rv_op_sha512sig1l = 385, - rv_op_sha512sig1h = 386, - rv_op_sm3p0 = 387, - rv_op_sm3p1 = 388, - rv_op_sm4ed = 389, - rv_op_sm4ks = 390, - rv_op_brev8 = 391, - rv_op_pack = 392, - rv_op_packh = 393, - rv_op_packw = 394, - rv_op_unzip = 395, - rv_op_zip = 396, - rv_op_xperm4 = 397, - rv_op_xperm8 = 398, - rv_op_vle8_v = 399, - rv_op_vle16_v = 400, - rv_op_vle32_v = 401, - rv_op_vle64_v = 402, - rv_op_vse8_v = 403, - rv_op_vse16_v = 404, - rv_op_vse32_v = 405, - rv_op_vse64_v = 406, - rv_op_vlm_v = 407, - rv_op_vsm_v = 408, - rv_op_vlse8_v = 409, - rv_op_vlse16_v = 410, - rv_op_vlse32_v = 411, - rv_op_vlse64_v = 412, - rv_op_vsse8_v = 413, - rv_op_vsse16_v = 414, - rv_op_vsse32_v = 415, - rv_op_vsse64_v = 416, - rv_op_vluxei8_v = 417, - rv_op_vluxei16_v = 418, - rv_op_vluxei32_v = 419, - rv_op_vluxei64_v = 420, - rv_op_vloxei8_v = 421, - rv_op_vloxei16_v = 422, - rv_op_vloxei32_v = 423, - rv_op_vloxei64_v = 424, - rv_op_vsuxei8_v = 425, - rv_op_vsuxei16_v = 426, - rv_op_vsuxei32_v = 427, - rv_op_vsuxei64_v = 428, - rv_op_vsoxei8_v = 429, - rv_op_vsoxei16_v = 430, - rv_op_vsoxei32_v = 431, - rv_op_vsoxei64_v = 432, - rv_op_vle8ff_v = 433, - rv_op_vle16ff_v = 434, - rv_op_vle32ff_v = 435, - rv_op_vle64ff_v = 436, - rv_op_vl1re8_v = 437, - rv_op_vl1re16_v = 438, - rv_op_vl1re32_v = 439, - rv_op_vl1re64_v = 440, - rv_op_vl2re8_v = 441, - rv_op_vl2re16_v = 442, - rv_op_vl2re32_v = 443, - rv_op_vl2re64_v = 444, - rv_op_vl4re8_v = 445, - rv_op_vl4re16_v = 446, - rv_op_vl4re32_v = 447, - rv_op_vl4re64_v = 448, - rv_op_vl8re8_v = 449, - rv_op_vl8re16_v = 450, - rv_op_vl8re32_v = 451, - rv_op_vl8re64_v = 452, - rv_op_vs1r_v = 453, - rv_op_vs2r_v = 454, - rv_op_vs4r_v = 455, - rv_op_vs8r_v = 456, - rv_op_vadd_vv = 457, - rv_op_vadd_vx = 458, - rv_op_vadd_vi = 459, - rv_op_vsub_vv = 460, - rv_op_vsub_vx = 461, - rv_op_vrsub_vx = 462, - rv_op_vrsub_vi = 463, - rv_op_vwaddu_vv = 464, - rv_op_vwaddu_vx = 465, - rv_op_vwadd_vv = 466, - rv_op_vwadd_vx = 467, - rv_op_vwsubu_vv = 468, - rv_op_vwsubu_vx = 469, - rv_op_vwsub_vv = 470, - rv_op_vwsub_vx = 471, - rv_op_vwaddu_wv = 472, - rv_op_vwaddu_wx = 473, - rv_op_vwadd_wv = 474, - rv_op_vwadd_wx = 475, - rv_op_vwsubu_wv = 476, - rv_op_vwsubu_wx = 477, - rv_op_vwsub_wv = 478, - rv_op_vwsub_wx = 479, - rv_op_vadc_vvm = 480, - rv_op_vadc_vxm = 481, - rv_op_vadc_vim = 482, - rv_op_vmadc_vvm = 483, - rv_op_vmadc_vxm = 484, - rv_op_vmadc_vim = 485, - rv_op_vsbc_vvm = 486, - rv_op_vsbc_vxm = 487, - rv_op_vmsbc_vvm = 488, - rv_op_vmsbc_vxm = 489, - rv_op_vand_vv = 490, - rv_op_vand_vx = 491, - rv_op_vand_vi = 492, - rv_op_vor_vv = 493, - rv_op_vor_vx = 494, - rv_op_vor_vi = 495, - rv_op_vxor_vv = 496, - rv_op_vxor_vx = 497, - rv_op_vxor_vi = 498, - rv_op_vsll_vv = 499, - rv_op_vsll_vx = 500, - rv_op_vsll_vi = 501, - rv_op_vsrl_vv = 502, - rv_op_vsrl_vx = 503, - rv_op_vsrl_vi = 504, - rv_op_vsra_vv = 505, - rv_op_vsra_vx = 506, - rv_op_vsra_vi = 507, - rv_op_vnsrl_wv = 508, - rv_op_vnsrl_wx = 509, - rv_op_vnsrl_wi = 510, - rv_op_vnsra_wv = 511, - rv_op_vnsra_wx = 512, - rv_op_vnsra_wi = 513, - rv_op_vmseq_vv = 514, - rv_op_vmseq_vx = 515, - rv_op_vmseq_vi = 516, - rv_op_vmsne_vv = 517, - rv_op_vmsne_vx = 518, - rv_op_vmsne_vi = 519, - rv_op_vmsltu_vv = 520, - rv_op_vmsltu_vx = 521, - rv_op_vmslt_vv = 522, - rv_op_vmslt_vx = 523, - rv_op_vmsleu_vv = 524, - rv_op_vmsleu_vx = 525, - rv_op_vmsleu_vi = 526, - rv_op_vmsle_vv = 527, - rv_op_vmsle_vx = 528, - rv_op_vmsle_vi = 529, - rv_op_vmsgtu_vx = 530, - rv_op_vmsgtu_vi = 531, - rv_op_vmsgt_vx = 532, - rv_op_vmsgt_vi = 533, - rv_op_vminu_vv = 534, - rv_op_vminu_vx = 535, - rv_op_vmin_vv = 536, - rv_op_vmin_vx = 537, - rv_op_vmaxu_vv = 538, - rv_op_vmaxu_vx = 539, - rv_op_vmax_vv = 540, - rv_op_vmax_vx = 541, - rv_op_vmul_vv = 542, - rv_op_vmul_vx = 543, - rv_op_vmulh_vv = 544, - rv_op_vmulh_vx = 545, - rv_op_vmulhu_vv = 546, - rv_op_vmulhu_vx = 547, - rv_op_vmulhsu_vv = 548, - rv_op_vmulhsu_vx = 549, - rv_op_vdivu_vv = 550, - rv_op_vdivu_vx = 551, - rv_op_vdiv_vv = 552, - rv_op_vdiv_vx = 553, - rv_op_vremu_vv = 554, - rv_op_vremu_vx = 555, - rv_op_vrem_vv = 556, - rv_op_vrem_vx = 557, - rv_op_vwmulu_vv = 558, - rv_op_vwmulu_vx = 559, - rv_op_vwmulsu_vv = 560, - rv_op_vwmulsu_vx = 561, - rv_op_vwmul_vv = 562, - rv_op_vwmul_vx = 563, - rv_op_vmacc_vv = 564, - rv_op_vmacc_vx = 565, - rv_op_vnmsac_vv = 566, - rv_op_vnmsac_vx = 567, - rv_op_vmadd_vv = 568, - rv_op_vmadd_vx = 569, - rv_op_vnmsub_vv = 570, - rv_op_vnmsub_vx = 571, - rv_op_vwmaccu_vv = 572, - rv_op_vwmaccu_vx = 573, - rv_op_vwmacc_vv = 574, - rv_op_vwmacc_vx = 575, - rv_op_vwmaccsu_vv = 576, - rv_op_vwmaccsu_vx = 577, - rv_op_vwmaccus_vx = 578, - rv_op_vmv_v_v = 579, - rv_op_vmv_v_x = 580, - rv_op_vmv_v_i = 581, - rv_op_vmerge_vvm = 582, - rv_op_vmerge_vxm = 583, - rv_op_vmerge_vim = 584, - rv_op_vsaddu_vv = 585, - rv_op_vsaddu_vx = 586, - rv_op_vsaddu_vi = 587, - rv_op_vsadd_vv = 588, - rv_op_vsadd_vx = 589, - rv_op_vsadd_vi = 590, - rv_op_vssubu_vv = 591, - rv_op_vssubu_vx = 592, - rv_op_vssub_vv = 593, - rv_op_vssub_vx = 594, - rv_op_vaadd_vv = 595, - rv_op_vaadd_vx = 596, - rv_op_vaaddu_vv = 597, - rv_op_vaaddu_vx = 598, - rv_op_vasub_vv = 599, - rv_op_vasub_vx = 600, - rv_op_vasubu_vv = 601, - rv_op_vasubu_vx = 602, - rv_op_vsmul_vv = 603, - rv_op_vsmul_vx = 604, - rv_op_vssrl_vv = 605, - rv_op_vssrl_vx = 606, - rv_op_vssrl_vi = 607, - rv_op_vssra_vv = 608, - rv_op_vssra_vx = 609, - rv_op_vssra_vi = 610, - rv_op_vnclipu_wv = 611, - rv_op_vnclipu_wx = 612, - rv_op_vnclipu_wi = 613, - rv_op_vnclip_wv = 614, - rv_op_vnclip_wx = 615, - rv_op_vnclip_wi = 616, - rv_op_vfadd_vv = 617, - rv_op_vfadd_vf = 618, - rv_op_vfsub_vv = 619, - rv_op_vfsub_vf = 620, - rv_op_vfrsub_vf = 621, - rv_op_vfwadd_vv = 622, - rv_op_vfwadd_vf = 623, - rv_op_vfwadd_wv = 624, - rv_op_vfwadd_wf = 625, - rv_op_vfwsub_vv = 626, - rv_op_vfwsub_vf = 627, - rv_op_vfwsub_wv = 628, - rv_op_vfwsub_wf = 629, - rv_op_vfmul_vv = 630, - rv_op_vfmul_vf = 631, - rv_op_vfdiv_vv = 632, - rv_op_vfdiv_vf = 633, - rv_op_vfrdiv_vf = 634, - rv_op_vfwmul_vv = 635, - rv_op_vfwmul_vf = 636, - rv_op_vfmacc_vv = 637, - rv_op_vfmacc_vf = 638, - rv_op_vfnmacc_vv = 639, - rv_op_vfnmacc_vf = 640, - rv_op_vfmsac_vv = 641, - rv_op_vfmsac_vf = 642, - rv_op_vfnmsac_vv = 643, - rv_op_vfnmsac_vf = 644, - rv_op_vfmadd_vv = 645, - rv_op_vfmadd_vf = 646, - rv_op_vfnmadd_vv = 647, - rv_op_vfnmadd_vf = 648, - rv_op_vfmsub_vv = 649, - rv_op_vfmsub_vf = 650, - rv_op_vfnmsub_vv = 651, - rv_op_vfnmsub_vf = 652, - rv_op_vfwmacc_vv = 653, - rv_op_vfwmacc_vf = 654, - rv_op_vfwnmacc_vv = 655, - rv_op_vfwnmacc_vf = 656, - rv_op_vfwmsac_vv = 657, - rv_op_vfwmsac_vf = 658, - rv_op_vfwnmsac_vv = 659, - rv_op_vfwnmsac_vf = 660, - rv_op_vfsqrt_v = 661, - rv_op_vfrsqrt7_v = 662, - rv_op_vfrec7_v = 663, - rv_op_vfmin_vv = 664, - rv_op_vfmin_vf = 665, - rv_op_vfmax_vv = 666, - rv_op_vfmax_vf = 667, - rv_op_vfsgnj_vv = 668, - rv_op_vfsgnj_vf = 669, - rv_op_vfsgnjn_vv = 670, - rv_op_vfsgnjn_vf = 671, - rv_op_vfsgnjx_vv = 672, - rv_op_vfsgnjx_vf = 673, - rv_op_vfslide1up_vf = 674, - rv_op_vfslide1down_vf = 675, - rv_op_vmfeq_vv = 676, - rv_op_vmfeq_vf = 677, - rv_op_vmfne_vv = 678, - rv_op_vmfne_vf = 679, - rv_op_vmflt_vv = 680, - rv_op_vmflt_vf = 681, - rv_op_vmfle_vv = 682, - rv_op_vmfle_vf = 683, - rv_op_vmfgt_vf = 684, - rv_op_vmfge_vf = 685, - rv_op_vfclass_v = 686, - rv_op_vfmerge_vfm = 687, - rv_op_vfmv_v_f = 688, - rv_op_vfcvt_xu_f_v = 689, - rv_op_vfcvt_x_f_v = 690, - rv_op_vfcvt_f_xu_v = 691, - rv_op_vfcvt_f_x_v = 692, - rv_op_vfcvt_rtz_xu_f_v = 693, - rv_op_vfcvt_rtz_x_f_v = 694, - rv_op_vfwcvt_xu_f_v = 695, - rv_op_vfwcvt_x_f_v = 696, - rv_op_vfwcvt_f_xu_v = 697, - rv_op_vfwcvt_f_x_v = 698, - rv_op_vfwcvt_f_f_v = 699, - rv_op_vfwcvt_rtz_xu_f_v = 700, - rv_op_vfwcvt_rtz_x_f_v = 701, - rv_op_vfncvt_xu_f_w = 702, - rv_op_vfncvt_x_f_w = 703, - rv_op_vfncvt_f_xu_w = 704, - rv_op_vfncvt_f_x_w = 705, - rv_op_vfncvt_f_f_w = 706, - rv_op_vfncvt_rod_f_f_w = 707, - rv_op_vfncvt_rtz_xu_f_w = 708, - rv_op_vfncvt_rtz_x_f_w = 709, - rv_op_vredsum_vs = 710, - rv_op_vredand_vs = 711, - rv_op_vredor_vs = 712, - rv_op_vredxor_vs = 713, - rv_op_vredminu_vs = 714, - rv_op_vredmin_vs = 715, - rv_op_vredmaxu_vs = 716, - rv_op_vredmax_vs = 717, - rv_op_vwredsumu_vs = 718, - rv_op_vwredsum_vs = 719, - rv_op_vfredusum_vs = 720, - rv_op_vfredosum_vs = 721, - rv_op_vfredmin_vs = 722, - rv_op_vfredmax_vs = 723, - rv_op_vfwredusum_vs = 724, - rv_op_vfwredosum_vs = 725, - rv_op_vmand_mm = 726, - rv_op_vmnand_mm = 727, - rv_op_vmandn_mm = 728, - rv_op_vmxor_mm = 729, - rv_op_vmor_mm = 730, - rv_op_vmnor_mm = 731, - rv_op_vmorn_mm = 732, - rv_op_vmxnor_mm = 733, - rv_op_vcpop_m = 734, - rv_op_vfirst_m = 735, - rv_op_vmsbf_m = 736, - rv_op_vmsif_m = 737, - rv_op_vmsof_m = 738, - rv_op_viota_m = 739, - rv_op_vid_v = 740, - rv_op_vmv_x_s = 741, - rv_op_vmv_s_x = 742, - rv_op_vfmv_f_s = 743, - rv_op_vfmv_s_f = 744, - rv_op_vslideup_vx = 745, - rv_op_vslideup_vi = 746, - rv_op_vslide1up_vx = 747, - rv_op_vslidedown_vx = 748, - rv_op_vslidedown_vi = 749, - rv_op_vslide1down_vx = 750, - rv_op_vrgather_vv = 751, - rv_op_vrgatherei16_vv = 752, - rv_op_vrgather_vx = 753, - rv_op_vrgather_vi = 754, - rv_op_vcompress_vm = 755, - rv_op_vmv1r_v = 756, - rv_op_vmv2r_v = 757, - rv_op_vmv4r_v = 758, - rv_op_vmv8r_v = 759, - rv_op_vzext_vf2 = 760, - rv_op_vzext_vf4 = 761, - rv_op_vzext_vf8 = 762, - rv_op_vsext_vf2 = 763, - rv_op_vsext_vf4 = 764, - rv_op_vsext_vf8 = 765, - rv_op_vsetvli = 766, - rv_op_vsetivli = 767, - rv_op_vsetvl = 768, - rv_op_c_zext_b = 769, - rv_op_c_sext_b = 770, - rv_op_c_zext_h = 771, - rv_op_c_sext_h = 772, - rv_op_c_zext_w = 773, - rv_op_c_not = 774, - rv_op_c_mul = 775, - rv_op_c_lbu = 776, - rv_op_c_lhu = 777, - rv_op_c_lh = 778, - rv_op_c_sb = 779, - rv_op_c_sh = 780, - rv_op_cm_push = 781, - rv_op_cm_pop = 782, - rv_op_cm_popret = 783, - rv_op_cm_popretz = 784, - rv_op_cm_mva01s = 785, - rv_op_cm_mvsa01 = 786, - rv_op_cm_jt = 787, - rv_op_cm_jalt = 788, - rv_op_czero_eqz = 789, - rv_op_czero_nez = 790, - rv_op_fcvt_bf16_s = 791, - rv_op_fcvt_s_bf16 = 792, - rv_op_vfncvtbf16_f_f_w = 793, - rv_op_vfwcvtbf16_f_f_v = 794, - rv_op_vfwmaccbf16_vv = 795, - rv_op_vfwmaccbf16_vf = 796, - rv_op_flh = 797, - rv_op_fsh = 798, - rv_op_fmv_h_x = 799, - rv_op_fmv_x_h = 800, - rv_op_fli_s = 801, - rv_op_fli_d = 802, - rv_op_fli_q = 803, - rv_op_fli_h = 804, - rv_op_fminm_s = 805, - rv_op_fmaxm_s = 806, - rv_op_fminm_d = 807, - rv_op_fmaxm_d = 808, - rv_op_fminm_q = 809, - rv_op_fmaxm_q = 810, - rv_op_fminm_h = 811, - rv_op_fmaxm_h = 812, - rv_op_fround_s = 813, - rv_op_froundnx_s = 814, - rv_op_fround_d = 815, - rv_op_froundnx_d = 816, - rv_op_fround_q = 817, - rv_op_froundnx_q = 818, - rv_op_fround_h = 819, - rv_op_froundnx_h = 820, - rv_op_fcvtmod_w_d = 821, - rv_op_fmvh_x_d = 822, - rv_op_fmvp_d_x = 823, - rv_op_fmvh_x_q = 824, - rv_op_fmvp_q_x = 825, - rv_op_fleq_s = 826, - rv_op_fltq_s = 827, - rv_op_fleq_d = 828, - rv_op_fltq_d = 829, - rv_op_fleq_q = 830, - rv_op_fltq_q = 831, - rv_op_fleq_h = 832, - rv_op_fltq_h = 833, - rv_op_vaesdf_vv = 834, - rv_op_vaesdf_vs = 835, - rv_op_vaesdm_vv = 836, - rv_op_vaesdm_vs = 837, - rv_op_vaesef_vv = 838, - rv_op_vaesef_vs = 839, - rv_op_vaesem_vv = 840, - rv_op_vaesem_vs = 841, - rv_op_vaeskf1_vi = 842, - rv_op_vaeskf2_vi = 843, - rv_op_vaesz_vs = 844, - rv_op_vandn_vv = 845, - rv_op_vandn_vx = 846, - rv_op_vbrev_v = 847, - rv_op_vbrev8_v = 848, - rv_op_vclmul_vv = 849, - rv_op_vclmul_vx = 850, - rv_op_vclmulh_vv = 851, - rv_op_vclmulh_vx = 852, - rv_op_vclz_v = 853, - rv_op_vcpop_v = 854, - rv_op_vctz_v = 855, - rv_op_vghsh_vv = 856, - rv_op_vgmul_vv = 857, - rv_op_vrev8_v = 858, - rv_op_vrol_vv = 859, - rv_op_vrol_vx = 860, - rv_op_vror_vv = 861, - rv_op_vror_vx = 862, - rv_op_vror_vi = 863, - rv_op_vsha2ch_vv = 864, - rv_op_vsha2cl_vv = 865, - rv_op_vsha2ms_vv = 866, - rv_op_vsm3c_vi = 867, - rv_op_vsm3me_vv = 868, - rv_op_vsm4k_vi = 869, - rv_op_vsm4r_vv = 870, - rv_op_vsm4r_vs = 871, - rv_op_vwsll_vv = 872, - rv_op_vwsll_vx = 873, - rv_op_vwsll_vi = 874, - rv_op_amocas_w = 875, - rv_op_amocas_d = 876, - rv_op_amocas_q = 877, - rv_mop_r_0 = 878, - rv_mop_r_1 = 879, - rv_mop_r_2 = 880, - rv_mop_r_3 = 881, - rv_mop_r_4 = 882, - rv_mop_r_5 = 883, - rv_mop_r_6 = 884, - rv_mop_r_7 = 885, - rv_mop_r_8 = 886, - rv_mop_r_9 = 887, - rv_mop_r_10 = 888, - rv_mop_r_11 = 889, - rv_mop_r_12 = 890, - rv_mop_r_13 = 891, - rv_mop_r_14 = 892, - rv_mop_r_15 = 893, - rv_mop_r_16 = 894, - rv_mop_r_17 = 895, - rv_mop_r_18 = 896, - rv_mop_r_19 = 897, - rv_mop_r_20 = 898, - rv_mop_r_21 = 899, - rv_mop_r_22 = 900, - rv_mop_r_23 = 901, - rv_mop_r_24 = 902, - rv_mop_r_25 = 903, - rv_mop_r_26 = 904, - rv_mop_r_27 = 905, - rv_mop_r_28 = 906, - rv_mop_r_29 = 907, - rv_mop_r_30 = 908, - rv_mop_r_31 = 909, - rv_mop_rr_0 = 910, - rv_mop_rr_1 = 911, - rv_mop_rr_2 = 912, - rv_mop_rr_3 = 913, - rv_mop_rr_4 = 914, - rv_mop_rr_5 = 915, - rv_mop_rr_6 = 916, - rv_mop_rr_7 = 917, - rv_c_mop_1 = 918, - rv_c_mop_3 = 919, - rv_c_mop_5 = 920, - rv_c_mop_7 = 921, - rv_c_mop_9 = 922, - rv_c_mop_11 = 923, - rv_c_mop_13 = 924, - rv_c_mop_15 = 925, - rv_op_amoswap_b = 926, - rv_op_amoadd_b = 927, - rv_op_amoxor_b = 928, - rv_op_amoor_b = 929, - rv_op_amoand_b = 930, - rv_op_amomin_b = 931, - rv_op_amomax_b = 932, - rv_op_amominu_b = 933, - rv_op_amomaxu_b = 934, - rv_op_amoswap_h = 935, - rv_op_amoadd_h = 936, - rv_op_amoxor_h = 937, - rv_op_amoor_h = 938, - rv_op_amoand_h = 939, - rv_op_amomin_h = 940, - rv_op_amomax_h = 941, - rv_op_amominu_h = 942, - rv_op_amomaxu_h = 943, - rv_op_amocas_b = 944, - rv_op_amocas_h = 945, - rv_op_wrs_sto = 946, - rv_op_wrs_nto = 947, - rv_op_lpad = 948, - rv_op_sspush = 949, - rv_op_sspopchk = 950, - rv_op_ssrdp = 951, - rv_op_ssamoswap_w = 952, - rv_op_ssamoswap_d = 953, - rv_op_c_sspush = 954, - rv_op_c_sspopchk = 955, - rv_op_cbo_inval = 956, - rv_op_cbo_clean = 957, - rv_op_cbo_flush = 958, - rv_op_cbo_zero = 959, - rv_op_mnret = 960, -} rv_op; - /* register names */ static const char rv_ireg_name_sym[32][5] = { @@ -1034,11 +70,11 @@ static const char rv_fli_name_const[32][9] = /* pseudo-instruction constraints */ -static const rvc_constraint rvcc_jal[] = { rvc_rd_eq_ra, rvc_end }; -static const rvc_constraint rvcc_jalr[] = { rvc_rd_eq_ra, rvc_imm_eq_zero, - rvc_end }; +static const rvc_constraint rvcc_jal_ra[] = { rvc_rd_eq_ra, rvc_end }; +static const rvc_constraint rvcc_jalr_ra[] = { rvc_rd_eq_ra, rvc_imm_eq_zero, + rvc_end }; static const rvc_constraint rvcc_nop[] = { rvc_rd_eq_x0, rvc_rs1_eq_x0, - rvc_imm_eq_zero, rvc_end }; + rvc_end }; static const rvc_constraint rvcc_mv[] = { rvc_imm_eq_zero, rvc_end }; static const rvc_constraint rvcc_not[] = { rvc_imm_eq_n1, rvc_end }; static const rvc_constraint rvcc_neg[] = { rvc_rs1_eq_x0, rvc_end }; @@ -1063,1209 +99,569 @@ static const rvc_constraint rvcc_blez[] = { rvc_rs1_eq_x0, rvc_end }; static const rvc_constraint rvcc_bgez[] = { rvc_rs2_eq_x0, rvc_end }; static const rvc_constraint rvcc_bltz[] = { rvc_rs2_eq_x0, rvc_end }; static const rvc_constraint rvcc_bgtz[] = { rvc_rs1_eq_x0, rvc_end }; -static const rvc_constraint rvcc_ble[] = { rvc_end }; -static const rvc_constraint rvcc_bleu[] = { rvc_end }; -static const rvc_constraint rvcc_bgt[] = { rvc_end }; -static const rvc_constraint rvcc_bgtu[] = { rvc_end }; static const rvc_constraint rvcc_j[] = { rvc_rd_eq_x0, rvc_end }; -static const rvc_constraint rvcc_ret[] = { rvc_rd_eq_x0, rvc_rs1_eq_ra, - rvc_end }; +static const rvc_constraint rvcc_ret[] = { rvc_rs1_eq_ra, rvc_end }; static const rvc_constraint rvcc_jr[] = { rvc_rd_eq_x0, rvc_imm_eq_zero, rvc_end }; -static const rvc_constraint rvcc_rdcycle[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc00, - rvc_end }; -static const rvc_constraint rvcc_rdtime[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc01, - rvc_end }; -static const rvc_constraint rvcc_rdinstret[] = { rvc_rs1_eq_x0, - rvc_csr_eq_0xc02, rvc_end }; -static const rvc_constraint rvcc_rdcycleh[] = { rvc_rs1_eq_x0, - rvc_csr_eq_0xc80, rvc_end }; -static const rvc_constraint rvcc_rdtimeh[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc81, - rvc_end }; -static const rvc_constraint rvcc_rdinstreth[] = { rvc_rs1_eq_x0, - rvc_csr_eq_0xc82, rvc_end }; -static const rvc_constraint rvcc_frcsr[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x003, - rvc_end }; -static const rvc_constraint rvcc_frrm[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x002, - rvc_end }; -static const rvc_constraint rvcc_frflags[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x001, - rvc_end }; -static const rvc_constraint rvcc_fscsr[] = { rvc_csr_eq_0x003, rvc_end }; -static const rvc_constraint rvcc_fsrm[] = { rvc_csr_eq_0x002, rvc_end }; -static const rvc_constraint rvcc_fsflags[] = { rvc_csr_eq_0x001, rvc_end }; -static const rvc_constraint rvcc_fsrmi[] = { rvc_csr_eq_0x002, rvc_end }; -static const rvc_constraint rvcc_fsflagsi[] = { rvc_csr_eq_0x001, rvc_end }; +static const rvc_constraint rvcc_true[] = { rvc_end }; /* pseudo-instruction metadata */ +/* Forward declare the all the opcodes so that we may link them. */ +#define OP(N, ...) static const rv_opcode_data op_##N; +#include "riscv-op.c.inc" +#undef OP + static const rv_comp_data rvcp_jal[] = { - { rv_op_j, rvcc_j }, - { rv_op_jal, rvcc_jal }, - { rv_op_illegal, NULL } + { &op_j, rvcc_j }, + { &op_jal_ra, rvcc_jal_ra }, + { }, }; static const rv_comp_data rvcp_jalr[] = { - { rv_op_ret, rvcc_ret }, - { rv_op_jr, rvcc_jr }, - { rv_op_jalr, rvcc_jalr }, - { rv_op_illegal, NULL } + { &op_jr, rvcc_jr }, + { &op_jalr_ra, rvcc_jalr_ra }, + { }, +}; + +static const rv_comp_data rvcp_jr[] = { + { &op_ret, rvcc_ret }, + { }, }; static const rv_comp_data rvcp_beq[] = { - { rv_op_beqz, rvcc_beqz }, - { rv_op_illegal, NULL } + { &op_beqz, rvcc_beqz }, + { }, }; static const rv_comp_data rvcp_bne[] = { - { rv_op_bnez, rvcc_bnez }, - { rv_op_illegal, NULL } + { &op_bnez, rvcc_bnez }, + { }, }; static const rv_comp_data rvcp_blt[] = { - { rv_op_bltz, rvcc_bltz }, - { rv_op_bgtz, rvcc_bgtz }, - { rv_op_bgt, rvcc_bgt }, - { rv_op_illegal, NULL } + { &op_bltz, rvcc_bltz }, + { &op_bgtz, rvcc_bgtz }, + { }, }; static const rv_comp_data rvcp_bge[] = { - { rv_op_blez, rvcc_blez }, - { rv_op_bgez, rvcc_bgez }, - { rv_op_ble, rvcc_ble }, - { rv_op_illegal, NULL } -}; - -static const rv_comp_data rvcp_bltu[] = { - { rv_op_bgtu, rvcc_bgtu }, - { rv_op_illegal, NULL } -}; - -static const rv_comp_data rvcp_bgeu[] = { - { rv_op_bleu, rvcc_bleu }, - { rv_op_illegal, NULL } + { &op_blez, rvcc_blez }, + { &op_bgez, rvcc_bgez }, + { }, }; static const rv_comp_data rvcp_addi[] = { - { rv_op_nop, rvcc_nop }, - { rv_op_mv, rvcc_mv }, - { rv_op_illegal, NULL } + { &op_mv, rvcc_mv }, + { }, +}; + +static const rv_comp_data rvcp_mv[] = { + { &op_nop, rvcc_nop }, + { }, }; static const rv_comp_data rvcp_sltiu[] = { - { rv_op_seqz, rvcc_seqz }, - { rv_op_illegal, NULL } + { &op_seqz, rvcc_seqz }, + { }, }; static const rv_comp_data rvcp_xori[] = { - { rv_op_not, rvcc_not }, - { rv_op_illegal, NULL } + { &op_not, rvcc_not }, + { }, }; static const rv_comp_data rvcp_sub[] = { - { rv_op_neg, rvcc_neg }, - { rv_op_illegal, NULL } + { &op_neg, rvcc_neg }, + { }, }; static const rv_comp_data rvcp_slt[] = { - { rv_op_sltz, rvcc_sltz }, - { rv_op_sgtz, rvcc_sgtz }, - { rv_op_illegal, NULL } + { &op_sltz, rvcc_sltz }, + { &op_sgtz, rvcc_sgtz }, + { }, }; static const rv_comp_data rvcp_sltu[] = { - { rv_op_snez, rvcc_snez }, - { rv_op_illegal, NULL } + { &op_snez, rvcc_snez }, + { }, }; static const rv_comp_data rvcp_addiw[] = { - { rv_op_sext_w, rvcc_sext_w }, - { rv_op_illegal, NULL } + { &op_sext_w, rvcc_sext_w }, + { }, }; static const rv_comp_data rvcp_subw[] = { - { rv_op_negw, rvcc_negw }, - { rv_op_illegal, NULL } -}; - -static const rv_comp_data rvcp_csrrw[] = { - { rv_op_fscsr, rvcc_fscsr }, - { rv_op_fsrm, rvcc_fsrm }, - { rv_op_fsflags, rvcc_fsflags }, - { rv_op_illegal, NULL } -}; - - -static const rv_comp_data rvcp_csrrs[] = { - { rv_op_rdcycle, rvcc_rdcycle }, - { rv_op_rdtime, rvcc_rdtime }, - { rv_op_rdinstret, rvcc_rdinstret }, - { rv_op_rdcycleh, rvcc_rdcycleh }, - { rv_op_rdtimeh, rvcc_rdtimeh }, - { rv_op_rdinstreth, rvcc_rdinstreth }, - { rv_op_frcsr, rvcc_frcsr }, - { rv_op_frrm, rvcc_frrm }, - { rv_op_frflags, rvcc_frflags }, - { rv_op_illegal, NULL } -}; - -static const rv_comp_data rvcp_csrrwi[] = { - { rv_op_fsrmi, rvcc_fsrmi }, - { rv_op_fsflagsi, rvcc_fsflagsi }, - { rv_op_illegal, NULL } + { &op_negw, rvcc_negw }, + { }, }; static const rv_comp_data rvcp_fsgnj_s[] = { - { rv_op_fmv_s, rvcc_fmv_s }, - { rv_op_illegal, NULL } + { &op_fmv_s, rvcc_fmv_s }, + { }, }; static const rv_comp_data rvcp_fsgnjn_s[] = { - { rv_op_fneg_s, rvcc_fneg_s }, - { rv_op_illegal, NULL } + { &op_fneg_s, rvcc_fneg_s }, + { }, }; static const rv_comp_data rvcp_fsgnjx_s[] = { - { rv_op_fabs_s, rvcc_fabs_s }, - { rv_op_illegal, NULL } + { &op_fabs_s, rvcc_fabs_s }, + { }, }; static const rv_comp_data rvcp_fsgnj_d[] = { - { rv_op_fmv_d, rvcc_fmv_d }, - { rv_op_illegal, NULL } + { &op_fmv_d, rvcc_fmv_d }, + { }, }; static const rv_comp_data rvcp_fsgnjn_d[] = { - { rv_op_fneg_d, rvcc_fneg_d }, - { rv_op_illegal, NULL } + { &op_fneg_d, rvcc_fneg_d }, + { }, }; static const rv_comp_data rvcp_fsgnjx_d[] = { - { rv_op_fabs_d, rvcc_fabs_d }, - { rv_op_illegal, NULL } + { &op_fabs_d, rvcc_fabs_d }, + { }, }; static const rv_comp_data rvcp_fsgnj_q[] = { - { rv_op_fmv_q, rvcc_fmv_q }, - { rv_op_illegal, NULL } + { &op_fmv_q, rvcc_fmv_q }, + { }, }; static const rv_comp_data rvcp_fsgnjn_q[] = { - { rv_op_fneg_q, rvcc_fneg_q }, - { rv_op_illegal, NULL } + { &op_fneg_q, rvcc_fneg_q }, + { }, }; static const rv_comp_data rvcp_fsgnjx_q[] = { - { rv_op_fabs_q, rvcc_fabs_q }, - { rv_op_illegal, NULL } + { &op_fabs_q, rvcc_fabs_q }, + { }, }; +/* Convert compressed insns into normal insns via pseudo expansion. */ +#define DECOMP(X) &(const rv_comp_data){ &X, rvcc_true } + +/* operand extractors */ + +static uint32_t operand_rd(rv_inst inst) +{ + return extract32(inst, 7, 5); +} + +static uint32_t operand_rs1(rv_inst inst) +{ + return extract32(inst, 15, 5); +} + +static uint32_t operand_rs2(rv_inst inst) +{ + return extract32(inst, 20, 5); +} + +static uint32_t operand_rs3(rv_inst inst) +{ + return extract32(inst, 27, 5); +} + +static uint32_t operand_aq(rv_inst inst) +{ + return extract32(inst, 26, 1); +} + +static uint32_t operand_rl(rv_inst inst) +{ + return extract32(inst, 25, 1); +} + +static uint32_t operand_pred(rv_inst inst) +{ + return extract32(inst, 24, 4); +} + +static uint32_t operand_succ(rv_inst inst) +{ + return extract32(inst, 20, 4); +} + +static uint32_t operand_rm(rv_inst inst) +{ + return extract32(inst, 12, 3); +} + +static uint32_t operand_shamt5(rv_inst inst) +{ + return extract32(inst, 20, 5); +} + +static uint32_t operand_shamt6(rv_inst inst) +{ + return extract32(inst, 20, 6); +} + +static uint32_t operand_shamt7(rv_inst inst) +{ + return extract32(inst, 20, 7); +} + +static uint32_t operand_crdq(rv_inst inst) +{ + return extract32(inst, 2, 3); +} + +static uint32_t operand_crs1q(rv_inst inst) +{ + return extract32(inst, 7, 3); +} + +static uint32_t operand_crs1rdq(rv_inst inst) +{ + return extract32(inst, 7, 3); +} + +static uint32_t operand_crs2q(rv_inst inst) +{ + return extract32(inst, 2, 3); +} + +static uint32_t calculate_xreg(uint32_t sreg) +{ + return sreg < 2 ? sreg + 8 : sreg + 16; +} + +static uint32_t operand_sreg1(rv_inst inst) +{ + return calculate_xreg(extract32(inst, 7, 3)); +} + +static uint32_t operand_sreg2(rv_inst inst) +{ + return calculate_xreg(extract32(inst, 2, 3)); +} + +static uint32_t operand_crd(rv_inst inst) +{ + return extract32(inst, 7, 5); +} + +static uint32_t operand_crs1(rv_inst inst) +{ + return extract32(inst, 7, 5); +} + +static uint32_t operand_crs1rd(rv_inst inst) +{ + return extract32(inst, 7, 5); +} + +static uint32_t operand_crs2(rv_inst inst) +{ + return extract32(inst, 2, 5); +} + +static uint32_t operand_cimmsh5(rv_inst inst) +{ + return extract32(inst, 2, 5); +} + +static uint32_t operand_csr12(rv_inst inst) +{ + return extract32(inst, 20, 12); +} + +static int32_t operand_imm12(rv_inst inst) +{ + return sextract32(inst, 20, 12); +} + +static int32_t operand_imm20(rv_inst inst) +{ + return sextract32(inst, 12, 20) << 12; +} + +static int32_t operand_jimm20(rv_inst inst) +{ + return sextract32(inst, 31, 1) << 20 | + extract32(inst, 21, 10) << 1 | + extract32(inst, 20, 1) << 11 | + extract32(inst, 12, 8) << 12; +} + +static int32_t operand_simm12(rv_inst inst) +{ + return sextract32(inst, 25, 7) << 5 | + extract32(inst, 7, 5); +} + +static int32_t operand_sbimm12(rv_inst inst) +{ + return sextract32(inst, 31, 1) << 12 | + extract32(inst, 25, 6) << 5 | + extract32(inst, 8, 4) << 1 | + extract32(inst, 7, 1) << 11; +} + +static uint32_t operand_cimmshl6(rv_inst inst, rv_isa isa) +{ + int imm = extract32(inst, 12, 1) << 5 | + extract32(inst, 2, 5); + if (isa == rv128) { + imm = imm ? imm : 64; + } + return imm; +} + +static uint32_t operand_cimmshr6(rv_inst inst, rv_isa isa) +{ + int imm = extract32(inst, 12, 1) << 5 | + extract32(inst, 2, 5); + if (isa == rv128) { + imm = imm | (imm & 32) << 1; + imm = imm ? imm : 64; + } + return imm; +} + +static int32_t operand_cimmi(rv_inst inst) +{ + return sextract32(inst, 12, 1) << 5 | + extract32(inst, 2, 5); +} + +static int32_t operand_cimmui(rv_inst inst) +{ + return sextract32(inst, 12, 1) << 17 | + extract32(inst, 2, 5) << 12; +} + +static uint32_t operand_cimmlwsp(rv_inst inst) +{ + return extract32(inst, 12, 1) << 5 | + extract32(inst, 4, 3) << 2 | + extract32(inst, 2, 2) << 6; +} + +static uint32_t operand_cimmldsp(rv_inst inst) +{ + return extract32(inst, 12, 1) << 5 | + extract32(inst, 5, 2) << 3 | + extract32(inst, 2, 3) << 6; +} + +static uint32_t operand_cimmlqsp(rv_inst inst) +{ + return extract32(inst, 12, 1) << 5 | + extract32(inst, 6, 1) << 4 | + extract32(inst, 2, 4) << 6; +} + +static int32_t operand_cimm16sp(rv_inst inst) +{ + return sextract32(inst, 12, 1) << 9 | + extract32(inst, 6, 1) << 4 | + extract32(inst, 5, 1) << 6 | + extract32(inst, 3, 2) << 7 | + extract32(inst, 2, 1) << 5; +} + +static int32_t operand_cimmj(rv_inst inst) +{ + return sextract32(inst, 12, 1) << 11 | + extract32(inst, 11, 1) << 4 | + extract32(inst, 9, 2) << 8 | + extract32(inst, 8, 1) << 10 | + extract32(inst, 7, 1) << 6 | + extract32(inst, 6, 1) << 7 | + extract32(inst, 3, 3) << 1 | + extract32(inst, 2, 1) << 5; +} + +static int32_t operand_cimmb(rv_inst inst) +{ + return sextract32(inst, 12, 1) << 8 | + extract32(inst, 10, 2) << 3 | + extract32(inst, 5, 2) << 6 | + extract32(inst, 3, 2) << 1 | + extract32(inst, 2, 1) << 5; +} + +static uint32_t operand_cimmswsp(rv_inst inst) +{ + return extract32(inst, 9, 4) << 2 | + extract32(inst, 7, 2) << 6; +} + +static uint32_t operand_cimmsdsp(rv_inst inst) +{ + return extract32(inst, 10, 3) << 3 | + extract32(inst, 7, 3) << 6; +} + +static uint32_t operand_cimmsqsp(rv_inst inst) +{ + return extract32(inst, 11, 2) << 4 | + extract32(inst, 7, 4) << 6; +} + +static uint32_t operand_cimm4spn(rv_inst inst) +{ + return extract32(inst, 11, 2) << 4 | + extract32(inst, 7, 4) << 6 | + extract32(inst, 6, 1) << 2 | + extract32(inst, 5, 1) << 3; +} + +static uint32_t operand_cimmw(rv_inst inst) +{ + return extract32(inst, 10, 3) << 3 | + extract32(inst, 6, 1) << 2 | + extract32(inst, 5, 1) << 6; +} + +static uint32_t operand_cimmd(rv_inst inst) +{ + return extract32(inst, 10, 3) << 3 | + extract32(inst, 5, 2) << 6; +} + +static uint32_t operand_cimmq(rv_inst inst) +{ + return extract32(inst, 11, 2) << 4 | + extract32(inst, 10, 1) << 8 | + extract32(inst, 5, 2) << 6; +} + +static int32_t operand_vimm(rv_inst inst) +{ + return sextract32(inst, 15, 5); +} + +static uint32_t operand_vuimm(rv_inst inst) +{ + return extract32(inst, 15, 5); +} + +static uint32_t operand_vzimm11(rv_inst inst) +{ + return extract32(inst, 20, 11); +} + +static uint32_t operand_vzimm10(rv_inst inst) +{ + return extract32(inst, 20, 10); +} + +static uint32_t operand_vzimm6(rv_inst inst) +{ + return extract32(inst, 26, 1) << 5 | + extract32(inst, 15, 5); +} + +static uint32_t operand_bs(rv_inst inst) +{ + return extract32(inst, 30, 2); +} + +static uint32_t operand_rnum(rv_inst inst) +{ + return extract32(inst, 20, 4); +} + +static uint32_t operand_vm(rv_inst inst) +{ + return extract32(inst, 25, 1); +} + +static uint32_t operand_uimm_c_lb(rv_inst inst) +{ + return extract32(inst, 5, 1) << 1 | + extract32(inst, 6, 1); +} + +static uint32_t operand_uimm_c_lh(rv_inst inst) +{ + return extract32(inst, 5, 1) << 1; +} + +static uint32_t operand_zcmp_spimm(rv_inst inst) +{ + return extract32(inst, 2, 2) << 4; +} + +static uint32_t operand_zcmp_rlist(rv_inst inst) +{ + return extract32(inst, 4, 4); +} + +static uint32_t operand_imm6(rv_inst inst) +{ + return extract32(inst, 20, 6); +} + +static uint32_t operand_imm2(rv_inst inst) +{ + return extract32(inst, 25, 2); +} + +static uint32_t operand_immh(rv_inst inst) +{ + return extract32(inst, 26, 6); +} + +static uint32_t operand_imml(rv_inst inst) +{ + return extract32(inst, 20, 6); +} + +static uint32_t calculate_stack_adj(rv_isa isa, uint32_t rlist, uint32_t spimm) +{ + int xlen_bytes_log2 = isa == rv64 ? 3 : 2; + int regs = rlist == 15 ? 13 : rlist - 3; + uint32_t stack_adj_base = ROUND_UP(regs << xlen_bytes_log2, 16); + return stack_adj_base + spimm; +} + +static uint32_t operand_zcmp_stack_adj(rv_inst inst, rv_isa isa) +{ + return calculate_stack_adj(isa, operand_zcmp_rlist(inst), + operand_zcmp_spimm(inst)); +} + +static uint32_t operand_tbl_index(rv_inst inst) +{ + return extract32(inst, 2, 8); +} + +static uint32_t operand_lpl(rv_inst inst) +{ + return extract32(inst, 12, 20); +} + +static uint32_t operand_cmop_imm(rv_inst inst) +{ + return extract32(inst, 8, 3) * 2 + 1; +} + +static uint32_t operand_mop_r_imm(rv_inst inst) +{ + return (extract32(inst, 30, 1) << 4) | + (extract32(inst, 26, 2) << 2) | + extract32(inst, 20, 2); +} + +static uint32_t operand_mop_rr_imm(rv_inst inst) +{ + return (extract32(inst, 30, 1) << 2) | extract32(inst, 26, 2); +} + /* instruction metadata */ -const rv_opcode_data rvi_opcode_data[] = { - { "illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 }, - { "lui", rv_codec_u, rv_fmt_rd_uimm, NULL, 0, 0, 0 }, - { "auipc", rv_codec_u, rv_fmt_rd_uoffset, NULL, 0, 0, 0 }, - { "jal", rv_codec_uj, rv_fmt_rd_offset, rvcp_jal, 0, 0, 0 }, - { "jalr", rv_codec_i, rv_fmt_rd_rs1_offset, rvcp_jalr, 0, 0, 0 }, - { "beq", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_beq, 0, 0, 0 }, - { "bne", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bne, 0, 0, 0 }, - { "blt", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_blt, 0, 0, 0 }, - { "bge", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bge, 0, 0, 0 }, - { "bltu", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bltu, 0, 0, 0 }, - { "bgeu", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bgeu, 0, 0, 0 }, - { "lb", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, - { "lh", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, - { "lw", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, - { "lbu", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, - { "lhu", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, - { "sb", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 }, - { "sh", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 }, - { "sw", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 }, - { "addi", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_addi, 0, 0, 0 }, - { "slti", rv_codec_i, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "sltiu", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_sltiu, 0, 0, 0 }, - { "xori", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_xori, 0, 0, 0 }, - { "ori", rv_codec_i, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "andi", rv_codec_i, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "slli", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "srli", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "srai", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "add", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sub", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_sub, 0, 0, 0 }, - { "sll", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "slt", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_slt, 0, 0, 0 }, - { "sltu", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_sltu, 0, 0, 0 }, - { "xor", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "srl", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sra", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "or", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "and", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "fence", rv_codec_r_f, rv_fmt_pred_succ, NULL, 0, 0, 0 }, - { "fence.i", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "lwu", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, - { "ld", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, - { "sd", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 }, - { "addiw", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_addiw, 0, 0, 0 }, - { "slliw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "srliw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "sraiw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "addw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "subw", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_subw, 0, 0, 0 }, - { "sllw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "srlw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sraw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "ldu", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, - { "lq", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, - { "sq", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 }, - { "addid", rv_codec_i, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "sllid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "srlid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "sraid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "addd", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "subd", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "slld", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "srld", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "srad", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mul", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mulh", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mulhsu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mulhu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "div", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "divu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "rem", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "remu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mulw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "divw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "divuw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "remw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "remuw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "muld", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "divd", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "divud", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "remd", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "remud", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "lr.w", rv_codec_r_l, rv_fmt_aqrl_rd_rs1, NULL, 0, 0, 0 }, - { "sc.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoadd.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoxor.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoor.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoand.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomin.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomax.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amominu.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomaxu.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "lr.d", rv_codec_r_l, rv_fmt_aqrl_rd_rs1, NULL, 0, 0, 0 }, - { "sc.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoadd.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoxor.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoor.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoand.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomin.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomax.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amominu.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomaxu.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "lr.q", rv_codec_r_l, rv_fmt_aqrl_rd_rs1, NULL, 0, 0, 0 }, - { "sc.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoswap.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoadd.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoxor.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoor.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoand.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomin.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomax.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amominu.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomaxu.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "ecall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "ebreak", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "uret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "sret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "hret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "mret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "dret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "sfence.vm", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "sfence.vma", rv_codec_r, rv_fmt_rs1_rs2, NULL, 0, 0, 0 }, - { "wfi", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "csrrw", rv_codec_i_csr, rv_fmt_rd_csr_rs1, rvcp_csrrw, 0, 0, 0 }, - { "csrrs", rv_codec_i_csr, rv_fmt_rd_csr_rs1, rvcp_csrrs, 0, 0, 0 }, - { "csrrc", rv_codec_i_csr, rv_fmt_rd_csr_rs1, NULL, 0, 0, 0 }, - { "csrrwi", rv_codec_i_csr, rv_fmt_rd_csr_zimm, rvcp_csrrwi, 0, 0, 0 }, - { "csrrsi", rv_codec_i_csr, rv_fmt_rd_csr_zimm, NULL, 0, 0, 0 }, - { "csrrci", rv_codec_i_csr, rv_fmt_rd_csr_zimm, NULL, 0, 0, 0 }, - { "flw", rv_codec_i, rv_fmt_frd_offset_rs1, NULL, 0, 0, 0 }, - { "fsw", rv_codec_s, rv_fmt_frs2_offset_rs1, NULL, 0, 0, 0 }, - { "fmadd.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fmsub.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fnmsub.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fnmadd.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fadd.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fsub.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fmul.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fdiv.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fsgnj.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_s, 0, 0, 0 }, - { "fsgnjn.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_s, 0, 0, 0 }, - { "fsgnjx.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_s, 0, 0, 0 }, - { "fmin.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fmax.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fsqrt.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fle.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "flt.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "feq.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "fcvt.w.s", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.wu.s", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.s.w", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fcvt.s.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fmv.x.s", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, - { "fclass.s", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, - { "fmv.s.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 }, - { "fcvt.l.s", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.lu.s", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.s.l", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fcvt.s.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fld", rv_codec_i, rv_fmt_frd_offset_rs1, NULL, 0, 0, 0 }, - { "fsd", rv_codec_s, rv_fmt_frs2_offset_rs1, NULL, 0, 0, 0 }, - { "fmadd.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fmsub.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fnmsub.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fnmadd.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fadd.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fsub.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fmul.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fdiv.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fsgnj.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_d, 0, 0, 0 }, - { "fsgnjn.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_d, 0, 0, 0 }, - { "fsgnjx.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_d, 0, 0, 0 }, - { "fmin.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fmax.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fcvt.s.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fcvt.d.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fsqrt.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fle.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "flt.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "feq.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "fcvt.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.wu.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.d.w", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fcvt.d.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fclass.d", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.l.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.lu.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fmv.x.d", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.d.l", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fcvt.d.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fmv.d.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 }, - { "flq", rv_codec_i, rv_fmt_frd_offset_rs1, NULL, 0, 0, 0 }, - { "fsq", rv_codec_s, rv_fmt_frs2_offset_rs1, NULL, 0, 0, 0 }, - { "fmadd.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fmsub.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fnmsub.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fnmadd.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fadd.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fsub.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fmul.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fdiv.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fsgnj.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_q, 0, 0, 0 }, - { "fsgnjn.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_q, 0, 0, 0 }, - { "fsgnjx.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_q, 0, 0, 0 }, - { "fmin.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fmax.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fcvt.s.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fcvt.q.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fcvt.d.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fcvt.q.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fsqrt.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fle.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "flt.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "feq.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "fcvt.w.q", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.wu.q", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.q.w", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fcvt.q.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fclass.q", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.l.q", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.lu.q", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.q.l", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fcvt.q.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fmv.x.q", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, - { "fmv.q.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 }, - { "c.addi4spn", rv_codec_ciw_4spn, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, - rv_op_addi, rv_op_addi, rvcd_imm_nz }, - { "c.fld", rv_codec_cl_ld, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, - rv_op_fld, 0 }, - { "c.lw", rv_codec_cl_lw, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, rv_op_lw, - rv_op_lw }, - { "c.flw", rv_codec_cl_lw, rv_fmt_frd_offset_rs1, NULL, rv_op_flw, 0, 0 }, - { "c.fsd", rv_codec_cs_sd, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd, - rv_op_fsd, 0 }, - { "c.sw", rv_codec_cs_sw, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, rv_op_sw, - rv_op_sw }, - { "c.fsw", rv_codec_cs_sw, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw, 0, 0 }, - { "c.nop", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_addi, rv_op_addi, - rv_op_addi }, - { "c.addi", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, - rv_op_addi, rvcd_imm_nz }, - { "c.jal", rv_codec_cj_jal, rv_fmt_rd_offset, NULL, rv_op_jal, 0, 0 }, - { "c.li", rv_codec_ci_li, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, - rv_op_addi }, - { "c.addi16sp", rv_codec_ci_16sp, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, - rv_op_addi, rv_op_addi, rvcd_imm_nz }, - { "c.lui", rv_codec_ci_lui, rv_fmt_rd_uimm, NULL, rv_op_lui, rv_op_lui, - rv_op_lui, rvcd_imm_nz }, - { "c.srli", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srli, - rv_op_srli, rv_op_srli, rvcd_imm_nz }, - { "c.srai", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srai, - rv_op_srai, rv_op_srai, rvcd_imm_nz }, - { "c.andi", rv_codec_cb_imm, rv_fmt_rd_rs1_imm, NULL, rv_op_andi, - rv_op_andi, rv_op_andi }, - { "c.sub", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_sub, rv_op_sub, - rv_op_sub }, - { "c.xor", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_xor, rv_op_xor, - rv_op_xor }, - { "c.or", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_or, rv_op_or, - rv_op_or }, - { "c.and", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_and, rv_op_and, - rv_op_and }, - { "c.subw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_subw, rv_op_subw, - rv_op_subw }, - { "c.addw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_addw, rv_op_addw, - rv_op_addw }, - { "c.j", rv_codec_cj, rv_fmt_rd_offset, NULL, rv_op_jal, rv_op_jal, - rv_op_jal }, - { "c.beqz", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_beq, rv_op_beq, - rv_op_beq }, - { "c.bnez", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_bne, rv_op_bne, - rv_op_bne }, - { "c.slli", rv_codec_ci_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_slli, - rv_op_slli, rv_op_slli, rvcd_imm_nz }, - { "c.fldsp", rv_codec_ci_ldsp, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, - rv_op_fld, rv_op_fld }, - { "c.lwsp", rv_codec_ci_lwsp, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, - rv_op_lw, rv_op_lw }, - { "c.flwsp", rv_codec_ci_lwsp, rv_fmt_frd_offset_rs1, NULL, rv_op_flw, 0, - 0 }, - { "c.jr", rv_codec_cr_jr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr, - rv_op_jalr, rv_op_jalr }, - { "c.mv", rv_codec_cr_mv, rv_fmt_rd_rs1_rs2, NULL, rv_op_addi, rv_op_addi, - rv_op_addi }, - { "c.ebreak", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_ebreak, - rv_op_ebreak, rv_op_ebreak }, - { "c.jalr", rv_codec_cr_jalr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr, - rv_op_jalr, rv_op_jalr }, - { "c.add", rv_codec_cr, rv_fmt_rd_rs1_rs2, NULL, rv_op_add, rv_op_add, - rv_op_add }, - { "c.fsdsp", rv_codec_css_sdsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd, - rv_op_fsd, rv_op_fsd }, - { "c.swsp", rv_codec_css_swsp, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, - rv_op_sw, rv_op_sw }, - { "c.fswsp", rv_codec_css_swsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw, 0, - 0 }, - { "c.ld", rv_codec_cl_ld, rv_fmt_rd_offset_rs1, NULL, 0, rv_op_ld, - rv_op_ld }, - { "c.sd", rv_codec_cs_sd, rv_fmt_rs2_offset_rs1, NULL, 0, rv_op_sd, - rv_op_sd }, - { "c.addiw", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, 0, rv_op_addiw, - rv_op_addiw }, - { "c.ldsp", rv_codec_ci_ldsp, rv_fmt_rd_offset_rs1, NULL, 0, rv_op_ld, - rv_op_ld }, - { "c.sdsp", rv_codec_css_sdsp, rv_fmt_rs2_offset_rs1, NULL, 0, rv_op_sd, - rv_op_sd }, - { "c.lq", rv_codec_cl_lq, rv_fmt_rd_offset_rs1, NULL, 0, 0, rv_op_lq }, - { "c.sq", rv_codec_cs_sq, rv_fmt_rs2_offset_rs1, NULL, 0, 0, rv_op_sq }, - { "c.lqsp", rv_codec_ci_lqsp, rv_fmt_rd_offset_rs1, NULL, 0, 0, rv_op_lq }, - { "c.sqsp", rv_codec_css_sqsp, rv_fmt_rs2_offset_rs1, NULL, 0, 0, - rv_op_sq }, - { "nop", rv_codec_i, rv_fmt_none, NULL, 0, 0, 0 }, - { "mv", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "not", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "neg", rv_codec_r, rv_fmt_rd_rs2, NULL, 0, 0, 0 }, - { "negw", rv_codec_r, rv_fmt_rd_rs2, NULL, 0, 0, 0 }, - { "sext.w", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "seqz", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "snez", rv_codec_r, rv_fmt_rd_rs2, NULL, 0, 0, 0 }, - { "sltz", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "sgtz", rv_codec_r, rv_fmt_rd_rs2, NULL, 0, 0, 0 }, - { "fmv.s", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, - { "fabs.s", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, - { "fneg.s", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, - { "fmv.d", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, - { "fabs.d", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, - { "fneg.d", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, - { "fmv.q", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, - { "fabs.q", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, - { "fneg.q", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, - { "beqz", rv_codec_sb, rv_fmt_rs1_offset, NULL, 0, 0, 0 }, - { "bnez", rv_codec_sb, rv_fmt_rs1_offset, NULL, 0, 0, 0 }, - { "blez", rv_codec_sb, rv_fmt_rs2_offset, NULL, 0, 0, 0 }, - { "bgez", rv_codec_sb, rv_fmt_rs1_offset, NULL, 0, 0, 0 }, - { "bltz", rv_codec_sb, rv_fmt_rs1_offset, NULL, 0, 0, 0 }, - { "bgtz", rv_codec_sb, rv_fmt_rs2_offset, NULL, 0, 0, 0 }, - { "ble", rv_codec_sb, rv_fmt_rs2_rs1_offset, NULL, 0, 0, 0 }, - { "bleu", rv_codec_sb, rv_fmt_rs2_rs1_offset, NULL, 0, 0, 0 }, - { "bgt", rv_codec_sb, rv_fmt_rs2_rs1_offset, NULL, 0, 0, 0 }, - { "bgtu", rv_codec_sb, rv_fmt_rs2_rs1_offset, NULL, 0, 0, 0 }, - { "j", rv_codec_uj, rv_fmt_offset, NULL, 0, 0, 0 }, - { "ret", rv_codec_i, rv_fmt_none, NULL, 0, 0, 0 }, - { "jr", rv_codec_i, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "rdcycle", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, - { "rdtime", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, - { "rdinstret", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, - { "rdcycleh", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, - { "rdtimeh", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, - { "rdinstreth", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, - { "frcsr", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, - { "frrm", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, - { "frflags", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, - { "fscsr", rv_codec_i_csr, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "fsrm", rv_codec_i_csr, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "fsflags", rv_codec_i_csr, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "fsrmi", rv_codec_i_csr, rv_fmt_rd_zimm, NULL, 0, 0, 0 }, - { "fsflagsi", rv_codec_i_csr, rv_fmt_rd_zimm, NULL, 0, 0, 0 }, - { "bseti", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "bclri", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "binvi", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "bexti", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "rori", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "clz", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "ctz", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "cpop", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "sext.h", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "sext.b", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "xnor", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "orn", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "andn", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "rol", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "ror", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sh1add", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sh2add", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sh3add", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sh1add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sh2add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sh3add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "clmul", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "clmulr", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "clmulh", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "min", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "minu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "max", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "maxu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "clzw", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "ctzw", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "cpopw", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "slli.uw", rv_codec_i_sh6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "rolw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "rorw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "rev8", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "zext.h", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "roriw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "orc.b", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "bset", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "bclr", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "binv", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "bext", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "aes32esmi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, - { "aes32esi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, - { "aes32dsmi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, - { "aes32dsi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, - { "aes64ks1i", rv_codec_k_rnum, rv_fmt_rd_rs1_rnum, NULL, 0, 0, 0 }, - { "aes64ks2", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "aes64im", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "aes64esm", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "aes64es", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "aes64dsm", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "aes64ds", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sha256sig0", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "sha256sig1", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "sha256sum0", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "sha256sum1", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "sha512sig0", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sha512sig1", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sha512sum0", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sha512sum1", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sha512sum0r", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sha512sum1r", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sha512sig0l", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sha512sig0h", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sha512sig1l", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sha512sig1h", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sm3p0", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "sm3p1", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "sm4ed", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, - { "sm4ks", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, - { "brev8", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "pack", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "packh", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "packw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "unzip", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "zip", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "xperm4", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "xperm8", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "vle8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vle16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vle32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vle64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vse8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vse16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vse32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vse64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vlm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vsm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vlse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, - { "vlse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, - { "vlse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, - { "vlse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, - { "vsse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, - { "vsse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, - { "vsse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, - { "vsse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, - { "vluxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vluxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vluxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vluxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vloxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vloxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vloxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vloxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vsuxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vsuxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vsuxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vsuxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vsoxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vsoxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vsoxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vsoxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vle8ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vle16ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vle32ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vle64ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl1re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl1re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl1re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl1re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl2re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl2re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl2re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl2re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl4re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl4re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl4re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl4re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl8re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl8re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl8re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl8re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vs1r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vs2r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vs4r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vs8r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vrsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vrsub.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vwaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwsubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwsubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwaddu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwaddu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwadd.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwsubu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwsubu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwsub.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, 0, 0, 0 }, - { "vadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, 0, 0, 0 }, - { "vadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl, NULL, 0, 0, 0 }, - { "vmadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, 0, 0, 0 }, - { "vmadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, 0, 0, 0 }, - { "vmadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl, NULL, 0, 0, 0 }, - { "vsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, 0, 0, 0 }, - { "vsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, 0, 0, 0 }, - { "vmsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, 0, 0, 0 }, - { "vmsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, 0, 0, 0 }, - { "vand.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vand.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vand.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vxor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vxor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vxor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vsll.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vsrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vsrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vsrl.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vsra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vsra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vsra.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vnsrl.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vnsrl.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vnsrl.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vnsra.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vnsra.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vnsra.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vmseq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmseq.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmseq.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vmsne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmsne.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmsne.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vmsltu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmsltu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmslt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmslt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmsleu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmsleu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmsleu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vmsle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmsle.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmsle.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vmsgtu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmsgtu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vmsgt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmsgt.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vminu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vminu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmin.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmaxu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmaxu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmax.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmulh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmulh.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmulhu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmulhu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmulhsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmulhsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vdivu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vdivu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vdiv.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vremu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vremu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vrem.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vrem.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwmulu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwmulu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwmulsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwmulsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vnmsac.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vmadd.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vnmsub.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vwmaccu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vwmaccu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vwmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vwmaccsu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vwmaccsu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vwmaccus.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vmv.v.v", rv_codec_v_r, rv_fmt_vd_vs1, NULL, 0, 0, 0 }, - { "vmv.v.x", rv_codec_v_r, rv_fmt_vd_rs1, NULL, 0, 0, 0 }, - { "vmv.v.i", rv_codec_v_i, rv_fmt_vd_imm, NULL, 0, 0, 0 }, - { "vmerge.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, 0, 0, 0 }, - { "vmerge.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, 0, 0, 0 }, - { "vmerge.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl, NULL, 0, 0, 0 }, - { "vsaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vsaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vsaddu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vsadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vsadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vsadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vssubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vssubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vssub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vssub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vaadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vaadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vaaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vaaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vasub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vasub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vasubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vasubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vsmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vsmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vssrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vssrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vssrl.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vssra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vssra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vssra.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vnclipu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vnclipu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vnclipu.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vnclip.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vnclip.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vnclip.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vfadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfrsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfwadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfwadd.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfwsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfwsub.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfrdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfwmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfnmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfnmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfnmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfwmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfwnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfwnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfwmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfwmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfwnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfwnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfsqrt.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vfrsqrt7.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vfrec7.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vfmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfmin.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfmax.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfsgnj.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfsgnj.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfsgnjn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfsgnjn.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfsgnjx.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfsgnjx.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfslide1up.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfslide1down.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vmfeq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmfeq.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vmfne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmfne.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vmflt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmflt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vmfle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmfle.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vmfgt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vmfge.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfclass.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfmerge.vfm", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vl, NULL, 0, 0, 0 }, - { "vfmv.v.f", rv_codec_v_r, rv_fmt_vd_fs1, NULL, 0, 0, 0 }, - { "vfcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfwcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfwcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfwcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfwcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfwcvt.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfwcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfwcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfncvt.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfncvt.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfncvt.f.xu.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfncvt.f.x.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfncvt.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfncvt.rod.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfncvt.rtz.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfncvt.rtz.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vredand.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vredor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vredxor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vredminu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vredmaxu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwredsumu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfwredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfwredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmnand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmandn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmxor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmorn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmxnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vcpop.m", rv_codec_v_r, rv_fmt_rd_vs2_vm, NULL, 0, 0, 0 }, - { "vfirst.m", rv_codec_v_r, rv_fmt_rd_vs2_vm, NULL, 0, 0, 0 }, - { "vmsbf.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vmsif.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vmsof.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "viota.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vid.v", rv_codec_v_r, rv_fmt_vd_vm, NULL, 0, 0, 0 }, - { "vmv.x.s", rv_codec_v_r, rv_fmt_rd_vs2, NULL, 0, 0, 0 }, - { "vmv.s.x", rv_codec_v_r, rv_fmt_vd_rs1, NULL, 0, 0, 0 }, - { "vfmv.f.s", rv_codec_v_r, rv_fmt_fd_vs2, NULL, 0, 0, 0 }, - { "vfmv.s.f", rv_codec_v_r, rv_fmt_vd_fs1, NULL, 0, 0, 0 }, - { "vslideup.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vslideup.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vslide1up.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vslidedown.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vslidedown.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vslide1down.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vrgather.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vrgatherei16.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vrgather.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vrgather.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vcompress.vm", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, - { "vmv1r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vmv2r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vmv4r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vmv8r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vzext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vzext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vzext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vsext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vsext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vsext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vsetvli", rv_codec_vsetvli, rv_fmt_vsetvli, NULL, 0, 0, 0 }, - { "vsetivli", rv_codec_vsetivli, rv_fmt_vsetivli, NULL, 0, 0, 0 }, - { "vsetvl", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "c.zext.b", rv_codec_zcb_ext, rv_fmt_rd, NULL, 0 }, - { "c.sext.b", rv_codec_zcb_ext, rv_fmt_rd, NULL, 0 }, - { "c.zext.h", rv_codec_zcb_ext, rv_fmt_rd, NULL, 0 }, - { "c.sext.h", rv_codec_zcb_ext, rv_fmt_rd, NULL, 0 }, - { "c.zext.w", rv_codec_zcb_ext, rv_fmt_rd, NULL, 0 }, - { "c.not", rv_codec_zcb_ext, rv_fmt_rd, NULL, 0 }, - { "c.mul", rv_codec_zcb_mul, rv_fmt_rd_rs2, NULL, 0, 0 }, - { "c.lbu", rv_codec_zcb_lb, rv_fmt_rs1_rs2_zce_ldst, NULL, 0, 0, 0 }, - { "c.lhu", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst, NULL, 0, 0, 0 }, - { "c.lh", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst, NULL, 0, 0, 0 }, - { "c.sb", rv_codec_zcb_lb, rv_fmt_rs1_rs2_zce_ldst, NULL, 0, 0, 0 }, - { "c.sh", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst, NULL, 0, 0, 0 }, - { "cm.push", rv_codec_zcmp_cm_pushpop, rv_fmt_push_rlist, NULL, 0, 0 }, - { "cm.pop", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist, NULL, 0, 0 }, - { "cm.popret", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist, NULL, 0, 0, 0 }, - { "cm.popretz", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist, NULL, 0, 0 }, - { "cm.mva01s", rv_codec_zcmp_cm_mv, rv_fmt_rd_rs2, NULL, 0, 0, 0 }, - { "cm.mvsa01", rv_codec_zcmp_cm_mv, rv_fmt_rd_rs2, NULL, 0, 0, 0 }, - { "cm.jt", rv_codec_zcmt_jt, rv_fmt_zcmt_index, NULL, 0 }, - { "cm.jalt", rv_codec_zcmt_jt, rv_fmt_zcmt_index, NULL, 0 }, - { "czero.eqz", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "czero.nez", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "fcvt.bf16.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fcvt.s.bf16", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "vfncvtbf16.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfwcvtbf16.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfwmaccbf16.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfwmaccbf16.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "flh", rv_codec_i, rv_fmt_frd_offset_rs1, NULL, 0, 0, 0 }, - { "fsh", rv_codec_s, rv_fmt_frs2_offset_rs1, NULL, 0, 0, 0 }, - { "fmv.h.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 }, - { "fmv.x.h", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, - { "fli.s", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 }, - { "fli.d", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 }, - { "fli.q", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 }, - { "fli.h", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 }, - { "fminm.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fmaxm.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fminm.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fmaxm.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fminm.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fmaxm.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fminm.h", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fmaxm.h", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fround.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "froundnx.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fround.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "froundnx.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fround.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "froundnx.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fround.h", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "froundnx.h", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fcvtmod.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fmvh.x.d", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, - { "fmvp.d.x", rv_codec_r, rv_fmt_frd_rs1_rs2, NULL, 0, 0, 0 }, - { "fmvh.x.q", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, - { "fmvp.q.x", rv_codec_r, rv_fmt_frd_rs1_rs2, NULL, 0, 0, 0 }, - { "fleq.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "fltq.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "fleq.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "fltq.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "fleq.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "fltq.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "fleq.h", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "fltq.h", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "vaesdf.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vaesdf.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vaesdm.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vaesdm.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vaesef.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vaesef.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vaesem.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vaesem.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vaeskf1.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, - { "vaeskf2.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, - { "vaesz.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vandn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vandn.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vbrev.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vbrev8.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vclmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vclmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vclmulh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vclmulh.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vclz.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vcpop.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vctz.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vghsh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, - { "vgmul.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vrev8.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vrol.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vrol.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vror.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vror.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vror.vi", rv_codec_vror_vi, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vsha2ch.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, - { "vsha2cl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, - { "vsha2ms.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, - { "vsm3c.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, - { "vsm3me.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, - { "vsm4k.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, - { "vsm4r.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vsm4r.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vwsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwsll.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "amocas.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amocas.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amocas.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "mop.r.0", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.1", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.2", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.3", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.4", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.5", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.6", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.7", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.8", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.9", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.10", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.11", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.12", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.13", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.14", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.15", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.16", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.17", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.18", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.19", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.20", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.21", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.22", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.23", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.24", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.25", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.26", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.27", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.28", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.29", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.30", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.31", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.rr.0", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mop.rr.1", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mop.rr.2", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mop.rr.3", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mop.rr.4", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mop.rr.5", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mop.rr.6", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mop.rr.7", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "c.mop.1", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "c.mop.3", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "c.mop.5", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "c.mop.7", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "c.mop.9", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "c.mop.11", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "c.mop.13", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "c.mop.15", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "amoswap.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoadd.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoxor.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoor.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoand.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomin.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomax.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amominu.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomaxu.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoswap.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoadd.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoxor.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoor.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoand.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomin.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomax.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amominu.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomaxu.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amocas.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amocas.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "wrs.sto", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "wrs.nto", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "lpad", rv_codec_lp, rv_fmt_imm, NULL, 0, 0, 0 }, - { "sspush", rv_codec_r, rv_fmt_rs2, NULL, 0, 0, 0 }, - { "sspopchk", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "ssrdp", rv_codec_r, rv_fmt_rd, NULL, 0, 0, 0 }, - { "ssamoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "ssamoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "c.sspush", rv_codec_cmop_ss, rv_fmt_rs2, NULL, rv_op_sspush, - rv_op_sspush, 0 }, - { "c.sspopchk", rv_codec_cmop_ss, rv_fmt_rs1, NULL, rv_op_sspopchk, - rv_op_sspopchk, 0 }, - { "cbo.inval", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "cbo.clean", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "cbo.flush", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "cbo.zero", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "mnret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, -}; +#define OP(N, ...) static const rv_opcode_data op_##N = { __VA_ARGS__ }; +#include "riscv-op.c.inc" +#undef OP /* CSR names */ @@ -2549,343 +945,339 @@ static const char *csr_name(int csrno) /* decode opcode */ -static void decode_inst_opcode(rv_decode *dec, rv_isa isa) +static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; + switch ((inst >> 0) & 0b11) { case 0: switch ((inst >> 13) & 0b111) { - case 0: op = rv_op_c_addi4spn; break; + case 0: + if ((inst >> 5) & 0xff) { + return &op_c_addi4spn; + } + break; case 1: if (isa == rv128) { - op = rv_op_c_lq; - } else { - op = rv_op_c_fld; + return &op_c_lq; } - break; - case 2: op = rv_op_c_lw; break; + return &op_c_fld; + case 2: return &op_c_lw; case 3: if (isa == rv32) { - op = rv_op_c_flw; - } else { - op = rv_op_c_ld; + return &op_c_flw; } - break; + return &op_c_ld; case 4: switch ((inst >> 10) & 0b111) { - case 0: op = rv_op_c_lbu; break; + case 0: return &op_c_lbu; case 1: if (((inst >> 6) & 1) == 0) { - op = rv_op_c_lhu; - } else { - op = rv_op_c_lh; + return &op_c_lhu; } - break; - case 2: op = rv_op_c_sb; break; + return &op_c_lh; + case 2: return &op_c_sb; case 3: if (((inst >> 6) & 1) == 0) { - op = rv_op_c_sh; + return &op_c_sh; } break; } break; case 5: if (isa == rv128) { - op = rv_op_c_sq; - } else { - op = rv_op_c_fsd; + return &op_c_sq; } - break; - case 6: op = rv_op_c_sw; break; + return &op_c_fsd; + case 6: return &op_c_sw; case 7: if (isa == rv32) { - op = rv_op_c_fsw; - } else { - op = rv_op_c_sd; + return &op_c_fsw; } - break; + return &op_c_sd; } break; case 1: switch ((inst >> 13) & 0b111) { case 0: - switch ((inst >> 2) & 0b11111111111) { - case 0: op = rv_op_c_nop; break; - default: op = rv_op_c_addi; break; - } - break; + return &op_c_addi; /* or unspecified HINT */ case 1: if (isa == rv32) { - op = rv_op_c_jal; - } else { - op = rv_op_c_addiw; + return &op_c_jal; } - break; - case 2: op = rv_op_c_li; break; + return &op_c_addiw; + case 2: return &op_c_li; case 3: - if (dec->cfg && dec->cfg->ext_zcmop) { - if ((((inst >> 2) & 0b111111) == 0b100000) && - (((inst >> 11) & 0b11) == 0b0)) { - unsigned int cmop_code = 0; - cmop_code = ((inst >> 8) & 0b111); - op = rv_c_mop_1 + cmop_code; - if (dec->cfg->ext_zicfiss) { - op = (cmop_code == 0) ? rv_op_c_sspush : op; - op = (cmop_code == 2) ? rv_op_c_sspopchk : op; + if (dec->cfg + && dec->cfg->ext_zcmop + && ((inst >> 2) & 0b111111) == 0b100000 + && ((inst >> 11) & 0b11) == 0) { + if (dec->cfg->ext_zicfiss) { + switch (operand_cmop_imm(inst)) { + case 1: return &op_c_sspush; + case 5: return &op_c_sspopchk; } - break; } + return &op_c_mop; } - switch ((inst >> 7) & 0b11111) { - case 2: op = rv_op_c_addi16sp; break; - default: op = rv_op_c_lui; break; + if (inst & ((1 << 12) | (0x1f << 2))) { + switch ((inst >> 7) & 0b11111) { + case 2: return &op_c_addi16sp; + default: return &op_c_lui; + } } break; case 4: switch ((inst >> 10) & 0b11) { case 0: - op = rv_op_c_srli; + /* For rv32, shamt[5]=1 is designated for custom extensions. */ + if (isa != rv32 || (inst & 0x1000) == 0) { + return &op_c_srli; /* or unspecified HINT */ + } break; case 1: - op = rv_op_c_srai; + /* For rv32, shamt[5]=1 is designated for custom extensions. */ + if (isa != rv32 || (inst & 0x1000) == 0) { + return &op_c_srai; /* or unspecified HINT */ + } break; - case 2: op = rv_op_c_andi; break; + case 2: return &op_c_andi; case 3: switch (((inst >> 10) & 0b100) | ((inst >> 5) & 0b011)) { - case 0: op = rv_op_c_sub; break; - case 1: op = rv_op_c_xor; break; - case 2: op = rv_op_c_or; break; - case 3: op = rv_op_c_and; break; - case 4: op = rv_op_c_subw; break; - case 5: op = rv_op_c_addw; break; - case 6: op = rv_op_c_mul; break; + case 0: return &op_c_sub; + case 1: return &op_c_xor; + case 2: return &op_c_or; + case 3: return &op_c_and; + case 4: + if (isa != rv32) { + return &op_c_subw; + } + break; + case 5: + if (isa != rv32) { + return &op_c_addw; + } + break; + case 6: return &op_c_mul; case 7: switch ((inst >> 2) & 0b111) { - case 0: op = rv_op_c_zext_b; break; - case 1: op = rv_op_c_sext_b; break; - case 2: op = rv_op_c_zext_h; break; - case 3: op = rv_op_c_sext_h; break; - case 4: op = rv_op_c_zext_w; break; - case 5: op = rv_op_c_not; break; + case 0: return &op_c_zext_b; + case 1: return &op_c_sext_b; + case 2: return &op_c_zext_h; + case 3: return &op_c_sext_h; + case 4: return &op_c_zext_w; + case 5: return &op_c_not; } break; } break; } break; - case 5: op = rv_op_c_j; break; - case 6: op = rv_op_c_beqz; break; - case 7: op = rv_op_c_bnez; break; + case 5: return &op_c_j; + case 6: return &op_c_beqz; + case 7: return &op_c_bnez; } break; case 2: switch ((inst >> 13) & 0b111) { case 0: - op = rv_op_c_slli; + if (isa != rv32 || (inst & 0x1000) == 0) { + return &op_c_slli; /* or unspecified HINT */ + } break; case 1: if (isa == rv128) { - op = rv_op_c_lqsp; - } else { - op = rv_op_c_fldsp; + return &op_c_lqsp; } - break; - case 2: op = rv_op_c_lwsp; break; + return &op_c_fldsp; + case 2: return &op_c_lwsp; case 3: if (isa == rv32) { - op = rv_op_c_flwsp; - } else { - op = rv_op_c_ldsp; + return &op_c_flwsp; } - break; + return &op_c_ldsp; case 4: switch ((inst >> 12) & 0b1) { case 0: switch ((inst >> 2) & 0b11111) { - case 0: op = rv_op_c_jr; break; - default: op = rv_op_c_mv; break; + case 0: return &op_c_jr; + default: return &op_c_mv; } break; case 1: switch ((inst >> 2) & 0b11111) { case 0: switch ((inst >> 7) & 0b11111) { - case 0: op = rv_op_c_ebreak; break; - default: op = rv_op_c_jalr; break; + case 0: return &op_c_ebreak; + default: return &op_c_jalr; } break; - default: op = rv_op_c_add; break; + default: return &op_c_add; } break; } break; case 5: if (isa == rv128) { - op = rv_op_c_sqsp; - } else { - op = rv_op_c_fsdsp; - if (dec->cfg && dec->cfg->ext_zcmp && ((inst >> 12) & 0b01)) { + return &op_c_sqsp; + } + if (dec->cfg) { + if (dec->cfg->ext_zcmp && ((inst >> 12) & 0b01)) { switch ((inst >> 8) & 0b01111) { case 8: if (((inst >> 4) & 0b01111) >= 4) { - op = rv_op_cm_push; + return &op_cm_push; } break; case 10: if (((inst >> 4) & 0b01111) >= 4) { - op = rv_op_cm_pop; + return &op_cm_pop; } break; case 12: if (((inst >> 4) & 0b01111) >= 4) { - op = rv_op_cm_popretz; + return &op_cm_popretz; } break; case 14: if (((inst >> 4) & 0b01111) >= 4) { - op = rv_op_cm_popret; - } - break; - } - } else { - switch ((inst >> 10) & 0b011) { - case 0: - if (dec->cfg && !dec->cfg->ext_zcmt) { - break; - } - if (((inst >> 2) & 0xFF) >= 32) { - op = rv_op_cm_jalt; - } else { - op = rv_op_cm_jt; - } - break; - case 3: - if (dec->cfg && !dec->cfg->ext_zcmp) { - break; - } - switch ((inst >> 5) & 0b011) { - case 1: op = rv_op_cm_mvsa01; break; - case 3: op = rv_op_cm_mva01s; break; + return &op_cm_popret; } break; } } + switch ((inst >> 10) & 0b011) { + case 0: + if (!dec->cfg->ext_zcmt) { + break; + } + if (((inst >> 2) & 0xFF) >= 32) { + return &op_cm_jalt; + } + return &op_cm_jt; + case 3: + if (!dec->cfg->ext_zcmp) { + break; + } + switch ((inst >> 5) & 0b011) { + case 1: return &op_cm_mvsa01; + case 3: return &op_cm_mva01s; + } + break; + } } - break; - case 6: op = rv_op_c_swsp; break; + return &op_c_fsdsp; + case 6: return &op_c_swsp; case 7: if (isa == rv32) { - op = rv_op_c_fswsp; - } else { - op = rv_op_c_sdsp; + return &op_c_fswsp; } - break; + return &op_c_sdsp; } break; case 3: switch ((inst >> 2) & 0b11111) { case 0: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_lb; break; - case 1: op = rv_op_lh; break; - case 2: op = rv_op_lw; break; - case 3: op = rv_op_ld; break; - case 4: op = rv_op_lbu; break; - case 5: op = rv_op_lhu; break; - case 6: op = rv_op_lwu; break; - case 7: op = rv_op_ldu; break; + case 0: return &op_lb; + case 1: return &op_lh; + case 2: return &op_lw; + case 3: return &op_ld; + case 4: return &op_lbu; + case 5: return &op_lhu; + case 6: return &op_lwu; + case 7: return &op_ldu; } break; case 1: switch ((inst >> 12) & 0b111) { case 0: switch ((inst >> 20) & 0b111111111111) { - case 40: op = rv_op_vl1re8_v; break; - case 552: op = rv_op_vl2re8_v; break; - case 1576: op = rv_op_vl4re8_v; break; - case 3624: op = rv_op_vl8re8_v; break; + case 40: return &op_vl1re8_v; + case 552: return &op_vl2re8_v; + case 1576: return &op_vl4re8_v; + case 3624: return &op_vl8re8_v; } switch ((inst >> 26) & 0b111) { case 0: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_vle8_v; break; - case 11: op = rv_op_vlm_v; break; - case 16: op = rv_op_vle8ff_v; break; + case 0: return &op_vle8_v; + case 11: return &op_vlm_v; + case 16: return &op_vle8ff_v; } break; - case 1: op = rv_op_vluxei8_v; break; - case 2: op = rv_op_vlse8_v; break; - case 3: op = rv_op_vloxei8_v; break; + case 1: return &op_vluxei8_v; + case 2: return &op_vlse8_v; + case 3: return &op_vloxei8_v; } break; - case 1: op = rv_op_flh; break; - case 2: op = rv_op_flw; break; - case 3: op = rv_op_fld; break; - case 4: op = rv_op_flq; break; + case 1: return &op_flh; + case 2: return &op_flw; + case 3: return &op_fld; + case 4: return &op_flq; case 5: switch ((inst >> 20) & 0b111111111111) { - case 40: op = rv_op_vl1re16_v; break; - case 552: op = rv_op_vl2re16_v; break; - case 1576: op = rv_op_vl4re16_v; break; - case 3624: op = rv_op_vl8re16_v; break; + case 40: return &op_vl1re16_v; + case 552: return &op_vl2re16_v; + case 1576: return &op_vl4re16_v; + case 3624: return &op_vl8re16_v; } switch ((inst >> 26) & 0b111) { case 0: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_vle16_v; break; - case 16: op = rv_op_vle16ff_v; break; + case 0: return &op_vle16_v; + case 16: return &op_vle16ff_v; } break; - case 1: op = rv_op_vluxei16_v; break; - case 2: op = rv_op_vlse16_v; break; - case 3: op = rv_op_vloxei16_v; break; + case 1: return &op_vluxei16_v; + case 2: return &op_vlse16_v; + case 3: return &op_vloxei16_v; } break; case 6: switch ((inst >> 20) & 0b111111111111) { - case 40: op = rv_op_vl1re32_v; break; - case 552: op = rv_op_vl2re32_v; break; - case 1576: op = rv_op_vl4re32_v; break; - case 3624: op = rv_op_vl8re32_v; break; + case 40: return &op_vl1re32_v; + case 552: return &op_vl2re32_v; + case 1576: return &op_vl4re32_v; + case 3624: return &op_vl8re32_v; } switch ((inst >> 26) & 0b111) { case 0: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_vle32_v; break; - case 16: op = rv_op_vle32ff_v; break; + case 0: return &op_vle32_v; + case 16: return &op_vle32ff_v; } break; - case 1: op = rv_op_vluxei32_v; break; - case 2: op = rv_op_vlse32_v; break; - case 3: op = rv_op_vloxei32_v; break; + case 1: return &op_vluxei32_v; + case 2: return &op_vlse32_v; + case 3: return &op_vloxei32_v; } break; case 7: switch ((inst >> 20) & 0b111111111111) { - case 40: op = rv_op_vl1re64_v; break; - case 552: op = rv_op_vl2re64_v; break; - case 1576: op = rv_op_vl4re64_v; break; - case 3624: op = rv_op_vl8re64_v; break; + case 40: return &op_vl1re64_v; + case 552: return &op_vl2re64_v; + case 1576: return &op_vl4re64_v; + case 3624: return &op_vl8re64_v; } switch ((inst >> 26) & 0b111) { case 0: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_vle64_v; break; - case 16: op = rv_op_vle64ff_v; break; + case 0: return &op_vle64_v; + case 16: return &op_vle64ff_v; } break; - case 1: op = rv_op_vluxei64_v; break; - case 2: op = rv_op_vlse64_v; break; - case 3: op = rv_op_vloxei64_v; break; + case 1: return &op_vluxei64_v; + case 2: return &op_vlse64_v; + case 3: return &op_vloxei64_v; } break; } break; case 3: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_fence; break; - case 1: op = rv_op_fence_i; break; + case 0: return &op_fence; + case 1: return &op_fence_i; case 2: /* * 'lq' shares the "(...) 010 ..... 0001111" opcode space @@ -2900,192 +1292,203 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) * Anything that doesn't match these will default to 'lq'. */ switch ((inst >> 17) & 0b11111) { - case 0: op = rv_op_cbo_inval; break; - case 1: op = rv_op_cbo_clean; break; - case 2: op = rv_op_cbo_flush; break; - case 4: op = rv_op_cbo_zero; break; - default: op = rv_op_lq; break; + case 0: return &op_cbo_inval; + case 1: return &op_cbo_clean; + case 2: return &op_cbo_flush; + case 4: return &op_cbo_zero; + default: return &op_lq; } } break; case 4: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_addi; break; + case 0: return &op_addi; case 1: switch ((inst >> 27) & 0b11111) { - case 0b00000: op = rv_op_slli; break; + case 0b00000: return &op_slli; case 0b00001: switch ((inst >> 20) & 0b1111111) { - case 0b0001111: op = rv_op_zip; break; + case 0b0001111: return &op_zip; } break; case 0b00010: switch ((inst >> 20) & 0b1111111) { - case 0b0000000: op = rv_op_sha256sum0; break; - case 0b0000001: op = rv_op_sha256sum1; break; - case 0b0000010: op = rv_op_sha256sig0; break; - case 0b0000011: op = rv_op_sha256sig1; break; - case 0b0000100: op = rv_op_sha512sum0; break; - case 0b0000101: op = rv_op_sha512sum1; break; - case 0b0000110: op = rv_op_sha512sig0; break; - case 0b0000111: op = rv_op_sha512sig1; break; - case 0b0001000: op = rv_op_sm3p0; break; - case 0b0001001: op = rv_op_sm3p1; break; + case 0b0000000: return &op_sha256sum0; + case 0b0000001: return &op_sha256sum1; + case 0b0000010: return &op_sha256sig0; + case 0b0000011: return &op_sha256sig1; + case 0b0000100: return &op_sha512sum0; + case 0b0000101: return &op_sha512sum1; + case 0b0000110: return &op_sha512sig0; + case 0b0000111: return &op_sha512sig1; + case 0b0001000: return &op_sm3p0; + case 0b0001001: return &op_sm3p1; } break; - case 0b00101: op = rv_op_bseti; break; + case 0b00101: return &op_bseti; case 0b00110: switch ((inst >> 20) & 0b1111111) { - case 0b0000000: op = rv_op_aes64im; break; + case 0b0000000: return &op_aes64im; default: if (((inst >> 24) & 0b0111) == 0b001) { - op = rv_op_aes64ks1i; + return &op_aes64ks1i; } break; } break; - case 0b01001: op = rv_op_bclri; break; - case 0b01101: op = rv_op_binvi; break; + case 0b01001: return &op_bclri; + case 0b01101: return &op_binvi; case 0b01100: switch ((inst >> 20) & 0b1111111) { - case 0b0000000: op = rv_op_clz; break; - case 0b0000001: op = rv_op_ctz; break; - case 0b0000010: op = rv_op_cpop; break; + case 0b0000000: return &op_clz; + case 0b0000001: return &op_ctz; + case 0b0000010: return &op_cpop; /* 0b0000011 */ - case 0b0000100: op = rv_op_sext_b; break; - case 0b0000101: op = rv_op_sext_h; break; + case 0b0000100: return &op_sext_b; + case 0b0000101: return &op_sext_h; } break; } break; - case 2: op = rv_op_slti; break; - case 3: op = rv_op_sltiu; break; - case 4: op = rv_op_xori; break; + case 2: return &op_slti; + case 3: return &op_sltiu; + case 4: return &op_xori; case 5: switch ((inst >> 27) & 0b11111) { - case 0b00000: op = rv_op_srli; break; + case 0b00000: return &op_srli; case 0b00001: switch ((inst >> 20) & 0b1111111) { - case 0b0001111: op = rv_op_unzip; break; + case 0b0001111: return &op_unzip; } break; - case 0b00101: op = rv_op_orc_b; break; - case 0b01000: op = rv_op_srai; break; - case 0b01001: op = rv_op_bexti; break; - case 0b01100: op = rv_op_rori; break; + case 0b00101: return &op_orc_b; + case 0b01000: return &op_srai; + case 0b01001: return &op_bexti; + case 0b01100: return &op_rori; case 0b01101: switch ((inst >> 20) & 0b1111111) { - case 0b0011000: op = rv_op_rev8; break; - case 0b0111000: op = rv_op_rev8; break; - case 0b0000111: op = rv_op_brev8; break; + case 0b0011000: + if (isa == rv32) { + return &op_rev8; + } + break; + case 0b0111000: + if (isa == rv64) { + return &op_rev8; + } + break; + case 0b0000111: return &op_brev8; } break; } break; - case 6: op = rv_op_ori; break; - case 7: op = rv_op_andi; break; + case 6: return &op_ori; + case 7: return &op_andi; } break; case 5: - op = rv_op_auipc; if (dec->cfg && dec->cfg->ext_zicfilp && (((inst >> 7) & 0b11111) == 0b00000)) { - op = rv_op_lpad; + return &op_lpad; } - break; + return &op_auipc; case 6: + /* OP-IMM-32 */ + if (isa == rv32) { + break; + } switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_addiw; break; + case 0: return &op_addiw; case 1: switch ((inst >> 26) & 0b111111) { - case 0: op = rv_op_slliw; break; - case 2: op = rv_op_slli_uw; break; + case 0: return &op_slliw; + case 2: return &op_slli_uw; case 24: switch ((inst >> 20) & 0b11111) { - case 0b00000: op = rv_op_clzw; break; - case 0b00001: op = rv_op_ctzw; break; - case 0b00010: op = rv_op_cpopw; break; + case 0b00000: return &op_clzw; + case 0b00001: return &op_ctzw; + case 0b00010: return &op_cpopw; } break; } break; case 5: switch ((inst >> 25) & 0b1111111) { - case 0: op = rv_op_srliw; break; - case 32: op = rv_op_sraiw; break; - case 48: op = rv_op_roriw; break; + case 0: return &op_srliw; + case 32: return &op_sraiw; + case 48: return &op_roriw; } break; } break; case 8: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_sb; break; - case 1: op = rv_op_sh; break; - case 2: op = rv_op_sw; break; - case 3: op = rv_op_sd; break; - case 4: op = rv_op_sq; break; + case 0: return &op_sb; + case 1: return &op_sh; + case 2: return &op_sw; + case 3: return &op_sd; + case 4: return &op_sq; } break; case 9: switch ((inst >> 12) & 0b111) { case 0: switch ((inst >> 20) & 0b111111111111) { - case 40: op = rv_op_vs1r_v; break; - case 552: op = rv_op_vs2r_v; break; - case 1576: op = rv_op_vs4r_v; break; - case 3624: op = rv_op_vs8r_v; break; + case 40: return &op_vs1r_v; + case 552: return &op_vs2r_v; + case 1576: return &op_vs4r_v; + case 3624: return &op_vs8r_v; } switch ((inst >> 26) & 0b111) { case 0: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_vse8_v; break; - case 11: op = rv_op_vsm_v; break; + case 0: return &op_vse8_v; + case 11: return &op_vsm_v; } break; - case 1: op = rv_op_vsuxei8_v; break; - case 2: op = rv_op_vsse8_v; break; - case 3: op = rv_op_vsoxei8_v; break; + case 1: return &op_vsuxei8_v; + case 2: return &op_vsse8_v; + case 3: return &op_vsoxei8_v; } break; - case 1: op = rv_op_fsh; break; - case 2: op = rv_op_fsw; break; - case 3: op = rv_op_fsd; break; - case 4: op = rv_op_fsq; break; + case 1: return &op_fsh; + case 2: return &op_fsw; + case 3: return &op_fsd; + case 4: return &op_fsq; case 5: switch ((inst >> 26) & 0b111) { case 0: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_vse16_v; break; + case 0: return &op_vse16_v; } break; - case 1: op = rv_op_vsuxei16_v; break; - case 2: op = rv_op_vsse16_v; break; - case 3: op = rv_op_vsoxei16_v; break; + case 1: return &op_vsuxei16_v; + case 2: return &op_vsse16_v; + case 3: return &op_vsoxei16_v; } break; case 6: switch ((inst >> 26) & 0b111) { case 0: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_vse32_v; break; + case 0: return &op_vse32_v; } break; - case 1: op = rv_op_vsuxei32_v; break; - case 2: op = rv_op_vsse32_v; break; - case 3: op = rv_op_vsoxei32_v; break; + case 1: return &op_vsuxei32_v; + case 2: return &op_vsse32_v; + case 3: return &op_vsoxei32_v; } break; case 7: switch ((inst >> 26) & 0b111) { case 0: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_vse64_v; break; + case 0: return &op_vse64_v; } break; - case 1: op = rv_op_vsuxei64_v; break; - case 2: op = rv_op_vsse64_v; break; - case 3: op = rv_op_vsoxei64_v; break; + case 1: return &op_vsuxei64_v; + case 2: return &op_vsse64_v; + case 3: return &op_vsoxei64_v; } break; } @@ -3093,466 +1496,468 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 11: switch (((inst >> 24) & 0b11111000) | ((inst >> 12) & 0b00000111)) { - case 0: op = rv_op_amoadd_b; break; - case 1: op = rv_op_amoadd_h; break; - case 2: op = rv_op_amoadd_w; break; - case 3: op = rv_op_amoadd_d; break; - case 4: op = rv_op_amoadd_q; break; - case 8: op = rv_op_amoswap_b; break; - case 9: op = rv_op_amoswap_h; break; - case 10: op = rv_op_amoswap_w; break; - case 11: op = rv_op_amoswap_d; break; - case 12: op = rv_op_amoswap_q; break; + case 0: return &op_amoadd_b; + case 1: return &op_amoadd_h; + case 2: return &op_amoadd_w; + case 3: return &op_amoadd_d; + case 4: return &op_amoadd_q; + case 8: return &op_amoswap_b; + case 9: return &op_amoswap_h; + case 10: return &op_amoswap_w; + case 11: return &op_amoswap_d; + case 12: return &op_amoswap_q; case 18: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_lr_w; break; + case 0: return &op_lr_w; } break; case 19: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_lr_d; break; + case 0: return &op_lr_d; } break; case 20: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_lr_q; break; + case 0: return &op_lr_q; } break; - case 26: op = rv_op_sc_w; break; - case 27: op = rv_op_sc_d; break; - case 28: op = rv_op_sc_q; break; - case 32: op = rv_op_amoxor_b; break; - case 33: op = rv_op_amoxor_h; break; - case 34: op = rv_op_amoxor_w; break; - case 35: op = rv_op_amoxor_d; break; - case 36: op = rv_op_amoxor_q; break; - case 40: op = rv_op_amocas_b; break; - case 41: op = rv_op_amocas_h; break; - case 42: op = rv_op_amocas_w; break; - case 43: op = rv_op_amocas_d; break; - case 44: op = rv_op_amocas_q; break; - case 64: op = rv_op_amoor_b; break; - case 65: op = rv_op_amoor_h; break; - case 66: op = rv_op_amoor_w; break; - case 67: op = rv_op_amoor_d; break; - case 68: op = rv_op_amoor_q; break; - case 74: op = rv_op_ssamoswap_w; break; - case 75: op = rv_op_ssamoswap_d; break; - case 96: op = rv_op_amoand_b; break; - case 97: op = rv_op_amoand_h; break; - case 98: op = rv_op_amoand_w; break; - case 99: op = rv_op_amoand_d; break; - case 100: op = rv_op_amoand_q; break; - case 128: op = rv_op_amomin_b; break; - case 129: op = rv_op_amomin_h; break; - case 130: op = rv_op_amomin_w; break; - case 131: op = rv_op_amomin_d; break; - case 132: op = rv_op_amomin_q; break; - case 160: op = rv_op_amomax_b; break; - case 161: op = rv_op_amomax_h; break; - case 162: op = rv_op_amomax_w; break; - case 163: op = rv_op_amomax_d; break; - case 164: op = rv_op_amomax_q; break; - case 192: op = rv_op_amominu_b; break; - case 193: op = rv_op_amominu_h; break; - case 194: op = rv_op_amominu_w; break; - case 195: op = rv_op_amominu_d; break; - case 196: op = rv_op_amominu_q; break; - case 224: op = rv_op_amomaxu_b; break; - case 225: op = rv_op_amomaxu_h; break; - case 226: op = rv_op_amomaxu_w; break; - case 227: op = rv_op_amomaxu_d; break; - case 228: op = rv_op_amomaxu_q; break; + case 26: return &op_sc_w; + case 27: return &op_sc_d; + case 28: return &op_sc_q; + case 32: return &op_amoxor_b; + case 33: return &op_amoxor_h; + case 34: return &op_amoxor_w; + case 35: return &op_amoxor_d; + case 36: return &op_amoxor_q; + case 40: return &op_amocas_b; + case 41: return &op_amocas_h; + case 42: return &op_amocas_w; + case 43: return &op_amocas_d; + case 44: return &op_amocas_q; + case 64: return &op_amoor_b; + case 65: return &op_amoor_h; + case 66: return &op_amoor_w; + case 67: return &op_amoor_d; + case 68: return &op_amoor_q; + case 74: return &op_ssamoswap_w; + case 75: return &op_ssamoswap_d; + case 96: return &op_amoand_b; + case 97: return &op_amoand_h; + case 98: return &op_amoand_w; + case 99: return &op_amoand_d; + case 100: return &op_amoand_q; + case 128: return &op_amomin_b; + case 129: return &op_amomin_h; + case 130: return &op_amomin_w; + case 131: return &op_amomin_d; + case 132: return &op_amomin_q; + case 160: return &op_amomax_b; + case 161: return &op_amomax_h; + case 162: return &op_amomax_w; + case 163: return &op_amomax_d; + case 164: return &op_amomax_q; + case 192: return &op_amominu_b; + case 193: return &op_amominu_h; + case 194: return &op_amominu_w; + case 195: return &op_amominu_d; + case 196: return &op_amominu_q; + case 224: return &op_amomaxu_b; + case 225: return &op_amomaxu_h; + case 226: return &op_amomaxu_w; + case 227: return &op_amomaxu_d; + case 228: return &op_amomaxu_q; } break; case 12: switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) { - case 0: op = rv_op_add; break; - case 1: op = rv_op_sll; break; - case 2: op = rv_op_slt; break; - case 3: op = rv_op_sltu; break; - case 4: op = rv_op_xor; break; - case 5: op = rv_op_srl; break; - case 6: op = rv_op_or; break; - case 7: op = rv_op_and; break; - case 8: op = rv_op_mul; break; - case 9: op = rv_op_mulh; break; - case 10: op = rv_op_mulhsu; break; - case 11: op = rv_op_mulhu; break; - case 12: op = rv_op_div; break; - case 13: op = rv_op_divu; break; - case 14: op = rv_op_rem; break; - case 15: op = rv_op_remu; break; + case 0: return &op_add; + case 1: return &op_sll; + case 2: return &op_slt; + case 3: return &op_sltu; + case 4: return &op_xor; + case 5: return &op_srl; + case 6: return &op_or; + case 7: return &op_and; + case 8: return &op_mul; + case 9: return &op_mulh; + case 10: return &op_mulhsu; + case 11: return &op_mulhu; + case 12: return &op_div; + case 13: return &op_divu; + case 14: return &op_rem; + case 15: return &op_remu; case 36: - switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_zext_h; break; - default: op = rv_op_pack; break; + if (isa == rv32 && !((inst >> 20) & 0b11111)) { + return &op_zext_h; } - break; - case 39: op = rv_op_packh; break; - - case 41: op = rv_op_clmul; break; - case 42: op = rv_op_clmulr; break; - case 43: op = rv_op_clmulh; break; - case 44: op = rv_op_min; break; - case 45: op = rv_op_minu; break; - case 46: op = rv_op_max; break; - case 47: op = rv_op_maxu; break; - case 075: op = rv_op_czero_eqz; break; - case 077: op = rv_op_czero_nez; break; - case 130: op = rv_op_sh1add; break; - case 132: op = rv_op_sh2add; break; - case 134: op = rv_op_sh3add; break; - case 161: op = rv_op_bset; break; - case 162: op = rv_op_xperm4; break; - case 164: op = rv_op_xperm8; break; - case 200: op = rv_op_aes64es; break; - case 216: op = rv_op_aes64esm; break; - case 232: op = rv_op_aes64ds; break; - case 248: op = rv_op_aes64dsm; break; - case 256: op = rv_op_sub; break; - case 260: op = rv_op_xnor; break; - case 261: op = rv_op_sra; break; - case 262: op = rv_op_orn; break; - case 263: op = rv_op_andn; break; - case 289: op = rv_op_bclr; break; - case 293: op = rv_op_bext; break; - case 320: op = rv_op_sha512sum0r; break; - case 328: op = rv_op_sha512sum1r; break; - case 336: op = rv_op_sha512sig0l; break; - case 344: op = rv_op_sha512sig1l; break; - case 368: op = rv_op_sha512sig0h; break; - case 376: op = rv_op_sha512sig1h; break; - case 385: op = rv_op_rol; break; - case 389: op = rv_op_ror; break; - case 417: op = rv_op_binv; break; - case 504: op = rv_op_aes64ks2; break; + return &op_pack; + case 39: return &op_packh; + case 41: return &op_clmul; + case 42: return &op_clmulr; + case 43: return &op_clmulh; + case 44: return &op_min; + case 45: return &op_minu; + case 46: return &op_max; + case 47: return &op_maxu; + case 075: return &op_czero_eqz; + case 077: return &op_czero_nez; + case 130: return &op_sh1add; + case 132: return &op_sh2add; + case 134: return &op_sh3add; + case 161: return &op_bset; + case 162: return &op_xperm4; + case 164: return &op_xperm8; + case 200: return &op_aes64es; + case 216: return &op_aes64esm; + case 232: return &op_aes64ds; + case 248: return &op_aes64dsm; + case 256: return &op_sub; + case 260: return &op_xnor; + case 261: return &op_sra; + case 262: return &op_orn; + case 263: return &op_andn; + case 289: return &op_bclr; + case 293: return &op_bext; + case 320: return &op_sha512sum0r; + case 328: return &op_sha512sum1r; + case 336: return &op_sha512sig0l; + case 344: return &op_sha512sig1l; + case 368: return &op_sha512sig0h; + case 376: return &op_sha512sig1h; + case 385: return &op_rol; + case 389: return &op_ror; + case 417: return &op_binv; + case 504: return &op_aes64ks2; } switch ((inst >> 25) & 0b0011111) { - case 17: op = rv_op_aes32esi; break; - case 19: op = rv_op_aes32esmi; break; - case 21: op = rv_op_aes32dsi; break; - case 23: op = rv_op_aes32dsmi; break; - case 24: op = rv_op_sm4ed; break; - case 26: op = rv_op_sm4ks; break; + case 17: return &op_aes32esi; + case 19: return &op_aes32esmi; + case 21: return &op_aes32dsi; + case 23: return &op_aes32dsmi; + case 24: return &op_sm4ed; + case 26: return &op_sm4ks; } break; - case 13: op = rv_op_lui; break; + case 13: return &op_lui; case 14: + /* OP-32 */ + if (isa == rv32) { + break; + } switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) { - case 0: op = rv_op_addw; break; - case 1: op = rv_op_sllw; break; - case 5: op = rv_op_srlw; break; - case 8: op = rv_op_mulw; break; - case 12: op = rv_op_divw; break; - case 13: op = rv_op_divuw; break; - case 14: op = rv_op_remw; break; - case 15: op = rv_op_remuw; break; - case 32: op = rv_op_add_uw; break; + case 0: return &op_addw; + case 1: return &op_sllw; + case 5: return &op_srlw; + case 8: return &op_mulw; + case 12: return &op_divw; + case 13: return &op_divuw; + case 14: return &op_remw; + case 15: return &op_remuw; + case 32: return &op_add_uw; case 36: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_zext_h; break; - default: op = rv_op_packw; break; + case 0: return &op_zext_h; + default: return &op_packw; } break; - case 130: op = rv_op_sh1add_uw; break; - case 132: op = rv_op_sh2add_uw; break; - case 134: op = rv_op_sh3add_uw; break; - case 256: op = rv_op_subw; break; - case 261: op = rv_op_sraw; break; - case 385: op = rv_op_rolw; break; - case 389: op = rv_op_rorw; break; + case 130: return &op_sh1add_uw; + case 132: return &op_sh2add_uw; + case 134: return &op_sh3add_uw; + case 256: return &op_subw; + case 261: return &op_sraw; + case 385: return &op_rolw; + case 389: return &op_rorw; } break; case 16: switch ((inst >> 25) & 0b11) { - case 0: op = rv_op_fmadd_s; break; - case 1: op = rv_op_fmadd_d; break; - case 3: op = rv_op_fmadd_q; break; + case 0: return &op_fmadd_s; + case 1: return &op_fmadd_d; + case 3: return &op_fmadd_q; } break; case 17: switch ((inst >> 25) & 0b11) { - case 0: op = rv_op_fmsub_s; break; - case 1: op = rv_op_fmsub_d; break; - case 3: op = rv_op_fmsub_q; break; + case 0: return &op_fmsub_s; + case 1: return &op_fmsub_d; + case 3: return &op_fmsub_q; } break; case 18: switch ((inst >> 25) & 0b11) { - case 0: op = rv_op_fnmsub_s; break; - case 1: op = rv_op_fnmsub_d; break; - case 3: op = rv_op_fnmsub_q; break; + case 0: return &op_fnmsub_s; + case 1: return &op_fnmsub_d; + case 3: return &op_fnmsub_q; } break; case 19: switch ((inst >> 25) & 0b11) { - case 0: op = rv_op_fnmadd_s; break; - case 1: op = rv_op_fnmadd_d; break; - case 3: op = rv_op_fnmadd_q; break; + case 0: return &op_fnmadd_s; + case 1: return &op_fnmadd_d; + case 3: return &op_fnmadd_q; } break; case 20: switch ((inst >> 25) & 0b1111111) { - case 0: op = rv_op_fadd_s; break; - case 1: op = rv_op_fadd_d; break; - case 3: op = rv_op_fadd_q; break; - case 4: op = rv_op_fsub_s; break; - case 5: op = rv_op_fsub_d; break; - case 7: op = rv_op_fsub_q; break; - case 8: op = rv_op_fmul_s; break; - case 9: op = rv_op_fmul_d; break; - case 11: op = rv_op_fmul_q; break; - case 12: op = rv_op_fdiv_s; break; - case 13: op = rv_op_fdiv_d; break; - case 15: op = rv_op_fdiv_q; break; + case 0: return &op_fadd_s; + case 1: return &op_fadd_d; + case 3: return &op_fadd_q; + case 4: return &op_fsub_s; + case 5: return &op_fsub_d; + case 7: return &op_fsub_q; + case 8: return &op_fmul_s; + case 9: return &op_fmul_d; + case 11: return &op_fmul_q; + case 12: return &op_fdiv_s; + case 13: return &op_fdiv_d; + case 15: return &op_fdiv_q; case 16: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_fsgnj_s; break; - case 1: op = rv_op_fsgnjn_s; break; - case 2: op = rv_op_fsgnjx_s; break; + case 0: return &op_fsgnj_s; + case 1: return &op_fsgnjn_s; + case 2: return &op_fsgnjx_s; } break; case 17: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_fsgnj_d; break; - case 1: op = rv_op_fsgnjn_d; break; - case 2: op = rv_op_fsgnjx_d; break; + case 0: return &op_fsgnj_d; + case 1: return &op_fsgnjn_d; + case 2: return &op_fsgnjx_d; } break; case 19: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_fsgnj_q; break; - case 1: op = rv_op_fsgnjn_q; break; - case 2: op = rv_op_fsgnjx_q; break; + case 0: return &op_fsgnj_q; + case 1: return &op_fsgnjn_q; + case 2: return &op_fsgnjx_q; } break; case 20: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_fmin_s; break; - case 1: op = rv_op_fmax_s; break; - case 2: op = rv_op_fminm_s; break; - case 3: op = rv_op_fmaxm_s; break; + case 0: return &op_fmin_s; + case 1: return &op_fmax_s; + case 2: return &op_fminm_s; + case 3: return &op_fmaxm_s; } break; case 21: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_fmin_d; break; - case 1: op = rv_op_fmax_d; break; - case 2: op = rv_op_fminm_d; break; - case 3: op = rv_op_fmaxm_d; break; + case 0: return &op_fmin_d; + case 1: return &op_fmax_d; + case 2: return &op_fminm_d; + case 3: return &op_fmaxm_d; } break; case 22: switch (((inst >> 12) & 0b111)) { - case 2: op = rv_op_fminm_h; break; - case 3: op = rv_op_fmaxm_h; break; + case 2: return &op_fminm_h; + case 3: return &op_fmaxm_h; } break; case 23: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_fmin_q; break; - case 1: op = rv_op_fmax_q; break; - case 2: op = rv_op_fminm_q; break; - case 3: op = rv_op_fmaxm_q; break; + case 0: return &op_fmin_q; + case 1: return &op_fmax_q; + case 2: return &op_fminm_q; + case 3: return &op_fmaxm_q; } break; case 32: switch ((inst >> 20) & 0b11111) { - case 1: op = rv_op_fcvt_s_d; break; - case 3: op = rv_op_fcvt_s_q; break; - case 4: op = rv_op_fround_s; break; - case 5: op = rv_op_froundnx_s; break; - case 6: op = rv_op_fcvt_s_bf16; break; + case 1: return &op_fcvt_s_d; + case 3: return &op_fcvt_s_q; + case 4: return &op_fround_s; + case 5: return &op_froundnx_s; + case 6: return &op_fcvt_s_bf16; } break; case 33: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_fcvt_d_s; break; - case 3: op = rv_op_fcvt_d_q; break; - case 4: op = rv_op_fround_d; break; - case 5: op = rv_op_froundnx_d; break; + case 0: return &op_fcvt_d_s; + case 3: return &op_fcvt_d_q; + case 4: return &op_fround_d; + case 5: return &op_froundnx_d; } break; case 34: switch (((inst >> 20) & 0b11111)) { - case 4: op = rv_op_fround_h; break; - case 5: op = rv_op_froundnx_h; break; - case 8: op = rv_op_fcvt_bf16_s; break; + case 4: return &op_fround_h; + case 5: return &op_froundnx_h; + case 8: return &op_fcvt_bf16_s; } break; case 35: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_fcvt_q_s; break; - case 1: op = rv_op_fcvt_q_d; break; - case 4: op = rv_op_fround_q; break; - case 5: op = rv_op_froundnx_q; break; + case 0: return &op_fcvt_q_s; + case 1: return &op_fcvt_q_d; + case 4: return &op_fround_q; + case 5: return &op_froundnx_q; } break; case 44: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_fsqrt_s; break; + case 0: return &op_fsqrt_s; } break; case 45: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_fsqrt_d; break; + case 0: return &op_fsqrt_d; } break; case 47: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_fsqrt_q; break; + case 0: return &op_fsqrt_q; } break; case 80: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_fle_s; break; - case 1: op = rv_op_flt_s; break; - case 2: op = rv_op_feq_s; break; - case 4: op = rv_op_fleq_s; break; - case 5: op = rv_op_fltq_s; break; + case 0: return &op_fle_s; + case 1: return &op_flt_s; + case 2: return &op_feq_s; + case 4: return &op_fleq_s; + case 5: return &op_fltq_s; } break; case 81: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_fle_d; break; - case 1: op = rv_op_flt_d; break; - case 2: op = rv_op_feq_d; break; - case 4: op = rv_op_fleq_d; break; - case 5: op = rv_op_fltq_d; break; + case 0: return &op_fle_d; + case 1: return &op_flt_d; + case 2: return &op_feq_d; + case 4: return &op_fleq_d; + case 5: return &op_fltq_d; } break; case 82: switch (((inst >> 12) & 0b111)) { - case 4: op = rv_op_fleq_h; break; - case 5: op = rv_op_fltq_h; break; + case 4: return &op_fleq_h; + case 5: return &op_fltq_h; } break; case 83: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_fle_q; break; - case 1: op = rv_op_flt_q; break; - case 2: op = rv_op_feq_q; break; - case 4: op = rv_op_fleq_q; break; - case 5: op = rv_op_fltq_q; break; + case 0: return &op_fle_q; + case 1: return &op_flt_q; + case 2: return &op_feq_q; + case 4: return &op_fleq_q; + case 5: return &op_fltq_q; } break; case 89: switch (((inst >> 12) & 0b111)) { - case 0: op = rv_op_fmvp_d_x; break; + case 0: return &op_fmvp_d_x; } break; case 91: switch (((inst >> 12) & 0b111)) { - case 0: op = rv_op_fmvp_q_x; break; + case 0: return &op_fmvp_q_x; } break; case 96: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_fcvt_w_s; break; - case 1: op = rv_op_fcvt_wu_s; break; - case 2: op = rv_op_fcvt_l_s; break; - case 3: op = rv_op_fcvt_lu_s; break; + case 0: return &op_fcvt_w_s; + case 1: return &op_fcvt_wu_s; + case 2: return &op_fcvt_l_s; + case 3: return &op_fcvt_lu_s; } break; case 97: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_fcvt_w_d; break; - case 1: op = rv_op_fcvt_wu_d; break; - case 2: op = rv_op_fcvt_l_d; break; - case 3: op = rv_op_fcvt_lu_d; break; - case 8: op = rv_op_fcvtmod_w_d; break; + case 0: return &op_fcvt_w_d; + case 1: return &op_fcvt_wu_d; + case 2: return &op_fcvt_l_d; + case 3: return &op_fcvt_lu_d; + case 8: return &op_fcvtmod_w_d; } break; case 99: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_fcvt_w_q; break; - case 1: op = rv_op_fcvt_wu_q; break; - case 2: op = rv_op_fcvt_l_q; break; - case 3: op = rv_op_fcvt_lu_q; break; + case 0: return &op_fcvt_w_q; + case 1: return &op_fcvt_wu_q; + case 2: return &op_fcvt_l_q; + case 3: return &op_fcvt_lu_q; } break; case 104: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_fcvt_s_w; break; - case 1: op = rv_op_fcvt_s_wu; break; - case 2: op = rv_op_fcvt_s_l; break; - case 3: op = rv_op_fcvt_s_lu; break; + case 0: return &op_fcvt_s_w; + case 1: return &op_fcvt_s_wu; + case 2: return &op_fcvt_s_l; + case 3: return &op_fcvt_s_lu; } break; case 105: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_fcvt_d_w; break; - case 1: op = rv_op_fcvt_d_wu; break; - case 2: op = rv_op_fcvt_d_l; break; - case 3: op = rv_op_fcvt_d_lu; break; + case 0: return &op_fcvt_d_w; + case 1: return &op_fcvt_d_wu; + case 2: return &op_fcvt_d_l; + case 3: return &op_fcvt_d_lu; } break; case 107: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_fcvt_q_w; break; - case 1: op = rv_op_fcvt_q_wu; break; - case 2: op = rv_op_fcvt_q_l; break; - case 3: op = rv_op_fcvt_q_lu; break; + case 0: return &op_fcvt_q_w; + case 1: return &op_fcvt_q_wu; + case 2: return &op_fcvt_q_l; + case 3: return &op_fcvt_q_lu; } break; case 112: switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { - case 0: op = rv_op_fmv_x_s; break; - case 1: op = rv_op_fclass_s; break; + case 0: return &op_fmv_x_s; + case 1: return &op_fclass_s; } break; case 113: switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { - case 0: op = rv_op_fmv_x_d; break; - case 1: op = rv_op_fclass_d; break; - case 8: op = rv_op_fmvh_x_d; break; + case 0: return &op_fmv_x_d; + case 1: return &op_fclass_d; + case 8: return &op_fmvh_x_d; } break; case 114: switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { - case 0: op = rv_op_fmv_x_h; break; + case 0: return &op_fmv_x_h; } break; case 115: switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { - case 0: op = rv_op_fmv_x_q; break; - case 1: op = rv_op_fclass_q; break; - case 8: op = rv_op_fmvh_x_q; break; + case 0: return &op_fmv_x_q; + case 1: return &op_fclass_q; + case 8: return &op_fmvh_x_q; } break; case 120: switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { - case 0: op = rv_op_fmv_s_x; break; - case 8: op = rv_op_fli_s; break; + case 0: return &op_fmv_s_x; + case 8: return &op_fli_s; } break; case 121: switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { - case 0: op = rv_op_fmv_d_x; break; - case 8: op = rv_op_fli_d; break; + case 0: return &op_fmv_d_x; + case 8: return &op_fli_d; } break; case 122: switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { - case 0: op = rv_op_fmv_h_x; break; - case 8: op = rv_op_fli_h; break; + case 0: return &op_fmv_h_x; + case 8: return &op_fli_h; } break; case 123: switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { - case 0: op = rv_op_fmv_q_x; break; - case 8: op = rv_op_fli_q; break; + case 0: return &op_fmv_q_x; + case 8: return &op_fli_q; } break; } @@ -3561,484 +1966,537 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) switch ((inst >> 12) & 0b111) { case 0: switch ((inst >> 26) & 0b111111) { - case 0: op = rv_op_vadd_vv; break; - case 1: op = rv_op_vandn_vv; break; - case 2: op = rv_op_vsub_vv; break; - case 4: op = rv_op_vminu_vv; break; - case 5: op = rv_op_vmin_vv; break; - case 6: op = rv_op_vmaxu_vv; break; - case 7: op = rv_op_vmax_vv; break; - case 9: op = rv_op_vand_vv; break; - case 10: op = rv_op_vor_vv; break; - case 11: op = rv_op_vxor_vv; break; - case 12: op = rv_op_vrgather_vv; break; - case 14: op = rv_op_vrgatherei16_vv; break; + case 0: return &op_vadd_vv; + case 1: return &op_vandn_vv; + case 2: return &op_vsub_vv; + case 4: return &op_vminu_vv; + case 5: return &op_vmin_vv; + case 6: return &op_vmaxu_vv; + case 7: return &op_vmax_vv; + case 9: return &op_vand_vv; + case 10: return &op_vor_vv; + case 11: return &op_vxor_vv; + case 12: return &op_vrgather_vv; + case 14: return &op_vrgatherei16_vv; case 16: if (((inst >> 25) & 1) == 0) { - op = rv_op_vadc_vvm; + return &op_vadc_vvm; } break; - case 17: op = rv_op_vmadc_vvm; break; + case 17: return &op_vmadc_vvm; case 18: if (((inst >> 25) & 1) == 0) { - op = rv_op_vsbc_vvm; + return &op_vsbc_vvm; } break; - case 19: op = rv_op_vmsbc_vvm; break; - case 20: op = rv_op_vror_vv; break; - case 21: op = rv_op_vrol_vv; break; + case 19: return &op_vmsbc_vvm; + case 20: return &op_vror_vv; + case 21: return &op_vrol_vv; case 23: - if (((inst >> 20) & 0b111111) == 32) - op = rv_op_vmv_v_v; - else if (((inst >> 25) & 1) == 0) - op = rv_op_vmerge_vvm; + if (((inst >> 20) & 0b111111) == 32) { + return &op_vmv_v_v; + } else if (((inst >> 25) & 1) == 0) { + return &op_vmerge_vvm; + } break; - case 24: op = rv_op_vmseq_vv; break; - case 25: op = rv_op_vmsne_vv; break; - case 26: op = rv_op_vmsltu_vv; break; - case 27: op = rv_op_vmslt_vv; break; - case 28: op = rv_op_vmsleu_vv; break; - case 29: op = rv_op_vmsle_vv; break; - case 32: op = rv_op_vsaddu_vv; break; - case 33: op = rv_op_vsadd_vv; break; - case 34: op = rv_op_vssubu_vv; break; - case 35: op = rv_op_vssub_vv; break; - case 37: op = rv_op_vsll_vv; break; - case 39: op = rv_op_vsmul_vv; break; - case 40: op = rv_op_vsrl_vv; break; - case 41: op = rv_op_vsra_vv; break; - case 42: op = rv_op_vssrl_vv; break; - case 43: op = rv_op_vssra_vv; break; - case 44: op = rv_op_vnsrl_wv; break; - case 45: op = rv_op_vnsra_wv; break; - case 46: op = rv_op_vnclipu_wv; break; - case 47: op = rv_op_vnclip_wv; break; - case 48: op = rv_op_vwredsumu_vs; break; - case 49: op = rv_op_vwredsum_vs; break; - case 53: op = rv_op_vwsll_vv; break; + case 24: return &op_vmseq_vv; + case 25: return &op_vmsne_vv; + case 26: return &op_vmsltu_vv; + case 27: return &op_vmslt_vv; + case 28: return &op_vmsleu_vv; + case 29: return &op_vmsle_vv; + case 32: return &op_vsaddu_vv; + case 33: return &op_vsadd_vv; + case 34: return &op_vssubu_vv; + case 35: return &op_vssub_vv; + case 37: return &op_vsll_vv; + case 39: return &op_vsmul_vv; + case 40: return &op_vsrl_vv; + case 41: return &op_vsra_vv; + case 42: return &op_vssrl_vv; + case 43: return &op_vssra_vv; + case 44: return &op_vnsrl_wv; + case 45: return &op_vnsra_wv; + case 46: return &op_vnclipu_wv; + case 47: return &op_vnclip_wv; + case 48: return &op_vwredsumu_vs; + case 49: return &op_vwredsum_vs; + case 53: return &op_vwsll_vv; } break; case 1: switch ((inst >> 26) & 0b111111) { - case 0: op = rv_op_vfadd_vv; break; - case 1: op = rv_op_vfredusum_vs; break; - case 2: op = rv_op_vfsub_vv; break; - case 3: op = rv_op_vfredosum_vs; break; - case 4: op = rv_op_vfmin_vv; break; - case 5: op = rv_op_vfredmin_vs; break; - case 6: op = rv_op_vfmax_vv; break; - case 7: op = rv_op_vfredmax_vs; break; - case 8: op = rv_op_vfsgnj_vv; break; - case 9: op = rv_op_vfsgnjn_vv; break; - case 10: op = rv_op_vfsgnjx_vv; break; + case 0: return &op_vfadd_vv; + case 1: return &op_vfredusum_vs; + case 2: return &op_vfsub_vv; + case 3: return &op_vfredosum_vs; + case 4: return &op_vfmin_vv; + case 5: return &op_vfredmin_vs; + case 6: return &op_vfmax_vv; + case 7: return &op_vfredmax_vs; + case 8: return &op_vfsgnj_vv; + case 9: return &op_vfsgnjn_vv; + case 10: return &op_vfsgnjx_vv; case 16: switch ((inst >> 15) & 0b11111) { - case 0: if ((inst >> 25) & 1) op = rv_op_vfmv_f_s; break; + case 0: + if ((inst >> 25) & 1) { + return &op_vfmv_f_s; + } } break; case 18: switch ((inst >> 15) & 0b11111) { - case 0: op = rv_op_vfcvt_xu_f_v; break; - case 1: op = rv_op_vfcvt_x_f_v; break; - case 2: op = rv_op_vfcvt_f_xu_v; break; - case 3: op = rv_op_vfcvt_f_x_v; break; - case 6: op = rv_op_vfcvt_rtz_xu_f_v; break; - case 7: op = rv_op_vfcvt_rtz_x_f_v; break; - case 8: op = rv_op_vfwcvt_xu_f_v; break; - case 9: op = rv_op_vfwcvt_x_f_v; break; - case 10: op = rv_op_vfwcvt_f_xu_v; break; - case 11: op = rv_op_vfwcvt_f_x_v; break; - case 12: op = rv_op_vfwcvt_f_f_v; break; - case 13: op = rv_op_vfwcvtbf16_f_f_v; break; - case 14: op = rv_op_vfwcvt_rtz_xu_f_v; break; - case 15: op = rv_op_vfwcvt_rtz_x_f_v; break; - case 16: op = rv_op_vfncvt_xu_f_w; break; - case 17: op = rv_op_vfncvt_x_f_w; break; - case 18: op = rv_op_vfncvt_f_xu_w; break; - case 19: op = rv_op_vfncvt_f_x_w; break; - case 20: op = rv_op_vfncvt_f_f_w; break; - case 21: op = rv_op_vfncvt_rod_f_f_w; break; - case 22: op = rv_op_vfncvt_rtz_xu_f_w; break; - case 23: op = rv_op_vfncvt_rtz_x_f_w; break; - case 29: op = rv_op_vfncvtbf16_f_f_w; break; + case 0: return &op_vfcvt_xu_f_v; + case 1: return &op_vfcvt_x_f_v; + case 2: return &op_vfcvt_f_xu_v; + case 3: return &op_vfcvt_f_x_v; + case 6: return &op_vfcvt_rtz_xu_f_v; + case 7: return &op_vfcvt_rtz_x_f_v; + case 8: return &op_vfwcvt_xu_f_v; + case 9: return &op_vfwcvt_x_f_v; + case 10: return &op_vfwcvt_f_xu_v; + case 11: return &op_vfwcvt_f_x_v; + case 12: return &op_vfwcvt_f_f_v; + case 13: return &op_vfwcvtbf16_f_f_v; + case 14: return &op_vfwcvt_rtz_xu_f_v; + case 15: return &op_vfwcvt_rtz_x_f_v; + case 16: return &op_vfncvt_xu_f_w; + case 17: return &op_vfncvt_x_f_w; + case 18: return &op_vfncvt_f_xu_w; + case 19: return &op_vfncvt_f_x_w; + case 20: return &op_vfncvt_f_f_w; + case 21: return &op_vfncvt_rod_f_f_w; + case 22: return &op_vfncvt_rtz_xu_f_w; + case 23: return &op_vfncvt_rtz_x_f_w; + case 29: return &op_vfncvtbf16_f_f_w; } break; case 19: switch ((inst >> 15) & 0b11111) { - case 0: op = rv_op_vfsqrt_v; break; - case 4: op = rv_op_vfrsqrt7_v; break; - case 5: op = rv_op_vfrec7_v; break; - case 16: op = rv_op_vfclass_v; break; + case 0: return &op_vfsqrt_v; + case 4: return &op_vfrsqrt7_v; + case 5: return &op_vfrec7_v; + case 16: return &op_vfclass_v; } break; - case 24: op = rv_op_vmfeq_vv; break; - case 25: op = rv_op_vmfle_vv; break; - case 27: op = rv_op_vmflt_vv; break; - case 28: op = rv_op_vmfne_vv; break; - case 32: op = rv_op_vfdiv_vv; break; - case 36: op = rv_op_vfmul_vv; break; - case 40: op = rv_op_vfmadd_vv; break; - case 41: op = rv_op_vfnmadd_vv; break; - case 42: op = rv_op_vfmsub_vv; break; - case 43: op = rv_op_vfnmsub_vv; break; - case 44: op = rv_op_vfmacc_vv; break; - case 45: op = rv_op_vfnmacc_vv; break; - case 46: op = rv_op_vfmsac_vv; break; - case 47: op = rv_op_vfnmsac_vv; break; - case 48: op = rv_op_vfwadd_vv; break; - case 49: op = rv_op_vfwredusum_vs; break; - case 50: op = rv_op_vfwsub_vv; break; - case 51: op = rv_op_vfwredosum_vs; break; - case 52: op = rv_op_vfwadd_wv; break; - case 54: op = rv_op_vfwsub_wv; break; - case 56: op = rv_op_vfwmul_vv; break; - case 59: op = rv_op_vfwmaccbf16_vv; break; - case 60: op = rv_op_vfwmacc_vv; break; - case 61: op = rv_op_vfwnmacc_vv; break; - case 62: op = rv_op_vfwmsac_vv; break; - case 63: op = rv_op_vfwnmsac_vv; break; + case 24: return &op_vmfeq_vv; + case 25: return &op_vmfle_vv; + case 27: return &op_vmflt_vv; + case 28: return &op_vmfne_vv; + case 32: return &op_vfdiv_vv; + case 36: return &op_vfmul_vv; + case 40: return &op_vfmadd_vv; + case 41: return &op_vfnmadd_vv; + case 42: return &op_vfmsub_vv; + case 43: return &op_vfnmsub_vv; + case 44: return &op_vfmacc_vv; + case 45: return &op_vfnmacc_vv; + case 46: return &op_vfmsac_vv; + case 47: return &op_vfnmsac_vv; + case 48: return &op_vfwadd_vv; + case 49: return &op_vfwredusum_vs; + case 50: return &op_vfwsub_vv; + case 51: return &op_vfwredosum_vs; + case 52: return &op_vfwadd_wv; + case 54: return &op_vfwsub_wv; + case 56: return &op_vfwmul_vv; + case 59: return &op_vfwmaccbf16_vv; + case 60: return &op_vfwmacc_vv; + case 61: return &op_vfwnmacc_vv; + case 62: return &op_vfwmsac_vv; + case 63: return &op_vfwnmsac_vv; } break; case 2: switch ((inst >> 26) & 0b111111) { - case 0: op = rv_op_vredsum_vs; break; - case 1: op = rv_op_vredand_vs; break; - case 2: op = rv_op_vredor_vs; break; - case 3: op = rv_op_vredxor_vs; break; - case 4: op = rv_op_vredminu_vs; break; - case 5: op = rv_op_vredmin_vs; break; - case 6: op = rv_op_vredmaxu_vs; break; - case 7: op = rv_op_vredmax_vs; break; - case 8: op = rv_op_vaaddu_vv; break; - case 9: op = rv_op_vaadd_vv; break; - case 10: op = rv_op_vasubu_vv; break; - case 11: op = rv_op_vasub_vv; break; - case 12: op = rv_op_vclmul_vv; break; - case 13: op = rv_op_vclmulh_vv; break; + case 0: return &op_vredsum_vs; + case 1: return &op_vredand_vs; + case 2: return &op_vredor_vs; + case 3: return &op_vredxor_vs; + case 4: return &op_vredminu_vs; + case 5: return &op_vredmin_vs; + case 6: return &op_vredmaxu_vs; + case 7: return &op_vredmax_vs; + case 8: return &op_vaaddu_vv; + case 9: return &op_vaadd_vv; + case 10: return &op_vasubu_vv; + case 11: return &op_vasub_vv; + case 12: return &op_vclmul_vv; + case 13: return &op_vclmulh_vv; case 16: switch ((inst >> 15) & 0b11111) { - case 0: if ((inst >> 25) & 1) op = rv_op_vmv_x_s; break; - case 16: op = rv_op_vcpop_m; break; - case 17: op = rv_op_vfirst_m; break; + case 0: + if ((inst >> 25) & 1) { + return &op_vmv_x_s; + } + break; + case 16: return &op_vcpop_m; + case 17: return &op_vfirst_m; } break; case 18: switch ((inst >> 15) & 0b11111) { - case 2: op = rv_op_vzext_vf8; break; - case 3: op = rv_op_vsext_vf8; break; - case 4: op = rv_op_vzext_vf4; break; - case 5: op = rv_op_vsext_vf4; break; - case 6: op = rv_op_vzext_vf2; break; - case 7: op = rv_op_vsext_vf2; break; - case 8: op = rv_op_vbrev8_v; break; - case 9: op = rv_op_vrev8_v; break; - case 10: op = rv_op_vbrev_v; break; - case 12: op = rv_op_vclz_v; break; - case 13: op = rv_op_vctz_v; break; - case 14: op = rv_op_vcpop_v; break; + case 2: return &op_vzext_vf8; + case 3: return &op_vsext_vf8; + case 4: return &op_vzext_vf4; + case 5: return &op_vsext_vf4; + case 6: return &op_vzext_vf2; + case 7: return &op_vsext_vf2; + case 8: return &op_vbrev8_v; + case 9: return &op_vrev8_v; + case 10: return &op_vbrev_v; + case 12: return &op_vclz_v; + case 13: return &op_vctz_v; + case 14: return &op_vcpop_v; } break; case 20: switch ((inst >> 15) & 0b11111) { - case 1: op = rv_op_vmsbf_m; break; - case 2: op = rv_op_vmsof_m; break; - case 3: op = rv_op_vmsif_m; break; - case 16: op = rv_op_viota_m; break; + case 1: return &op_vmsbf_m; break; + case 2: return &op_vmsof_m; + case 3: return &op_vmsif_m; + case 16: return &op_viota_m; case 17: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_vid_v; + return &op_vid_v; } break; } break; - case 23: if ((inst >> 25) & 1) op = rv_op_vcompress_vm; break; - case 24: if ((inst >> 25) & 1) op = rv_op_vmandn_mm; break; - case 25: if ((inst >> 25) & 1) op = rv_op_vmand_mm; break; - case 26: if ((inst >> 25) & 1) op = rv_op_vmor_mm; break; - case 27: if ((inst >> 25) & 1) op = rv_op_vmxor_mm; break; - case 28: if ((inst >> 25) & 1) op = rv_op_vmorn_mm; break; - case 29: if ((inst >> 25) & 1) op = rv_op_vmnand_mm; break; - case 30: if ((inst >> 25) & 1) op = rv_op_vmnor_mm; break; - case 31: if ((inst >> 25) & 1) op = rv_op_vmxnor_mm; break; - case 32: op = rv_op_vdivu_vv; break; - case 33: op = rv_op_vdiv_vv; break; - case 34: op = rv_op_vremu_vv; break; - case 35: op = rv_op_vrem_vv; break; - case 36: op = rv_op_vmulhu_vv; break; - case 37: op = rv_op_vmul_vv; break; - case 38: op = rv_op_vmulhsu_vv; break; - case 39: op = rv_op_vmulh_vv; break; - case 41: op = rv_op_vmadd_vv; break; - case 43: op = rv_op_vnmsub_vv; break; - case 45: op = rv_op_vmacc_vv; break; - case 47: op = rv_op_vnmsac_vv; break; - case 48: op = rv_op_vwaddu_vv; break; - case 49: op = rv_op_vwadd_vv; break; - case 50: op = rv_op_vwsubu_vv; break; - case 51: op = rv_op_vwsub_vv; break; - case 52: op = rv_op_vwaddu_wv; break; - case 53: op = rv_op_vwadd_wv; break; - case 54: op = rv_op_vwsubu_wv; break; - case 55: op = rv_op_vwsub_wv; break; - case 56: op = rv_op_vwmulu_vv; break; - case 58: op = rv_op_vwmulsu_vv; break; - case 59: op = rv_op_vwmul_vv; break; - case 60: op = rv_op_vwmaccu_vv; break; - case 61: op = rv_op_vwmacc_vv; break; - case 63: op = rv_op_vwmaccsu_vv; break; + case 23: + if ((inst >> 25) & 1) { + return &op_vcompress_vm; + } + break; + case 24: + if ((inst >> 25) & 1) { + return &op_vmandn_mm; + } + break; + case 25: + if ((inst >> 25) & 1) { + return &op_vmand_mm; + } + break; + case 26: + if ((inst >> 25) & 1) { + return &op_vmor_mm; + } + break; + case 27: + if ((inst >> 25) & 1) { + return &op_vmxor_mm; + } + break; + case 28: + if ((inst >> 25) & 1) { + return &op_vmorn_mm; + } + break; + case 29: + if ((inst >> 25) & 1) { + return &op_vmnand_mm; + } + break; + case 30: + if ((inst >> 25) & 1) { + return &op_vmnor_mm; + } + break; + case 31: + if ((inst >> 25) & 1) { + return &op_vmxnor_mm; + } + break; + case 32: return &op_vdivu_vv; + case 33: return &op_vdiv_vv; + case 34: return &op_vremu_vv; + case 35: return &op_vrem_vv; + case 36: return &op_vmulhu_vv; + case 37: return &op_vmul_vv; + case 38: return &op_vmulhsu_vv; + case 39: return &op_vmulh_vv; + case 41: return &op_vmadd_vv; + case 43: return &op_vnmsub_vv; + case 45: return &op_vmacc_vv; + case 47: return &op_vnmsac_vv; + case 48: return &op_vwaddu_vv; + case 49: return &op_vwadd_vv; + case 50: return &op_vwsubu_vv; + case 51: return &op_vwsub_vv; + case 52: return &op_vwaddu_wv; + case 53: return &op_vwadd_wv; + case 54: return &op_vwsubu_wv; + case 55: return &op_vwsub_wv; + case 56: return &op_vwmulu_vv; + case 58: return &op_vwmulsu_vv; + case 59: return &op_vwmul_vv; + case 60: return &op_vwmaccu_vv; + case 61: return &op_vwmacc_vv; + case 63: return &op_vwmaccsu_vv; } break; case 3: switch ((inst >> 26) & 0b111111) { - case 0: op = rv_op_vadd_vi; break; - case 3: op = rv_op_vrsub_vi; break; - case 9: op = rv_op_vand_vi; break; - case 10: op = rv_op_vor_vi; break; - case 11: op = rv_op_vxor_vi; break; - case 12: op = rv_op_vrgather_vi; break; - case 14: op = rv_op_vslideup_vi; break; - case 15: op = rv_op_vslidedown_vi; break; + case 0: return &op_vadd_vi; + case 3: return &op_vrsub_vi; + case 9: return &op_vand_vi; + case 10: return &op_vor_vi; + case 11: return &op_vxor_vi; + case 12: return &op_vrgather_vi; + case 14: return &op_vslideup_vi; + case 15: return &op_vslidedown_vi; case 16: if (((inst >> 25) & 1) == 0) { - op = rv_op_vadc_vim; + return &op_vadc_vim; } break; - case 17: op = rv_op_vmadc_vim; break; - case 20: case 21: op = rv_op_vror_vi; break; + case 17: return &op_vmadc_vim; + case 20: case 21: return &op_vror_vi; case 23: - if (((inst >> 20) & 0b111111) == 32) - op = rv_op_vmv_v_i; - else if (((inst >> 25) & 1) == 0) - op = rv_op_vmerge_vim; + if (((inst >> 20) & 0b111111) == 32) { + return &op_vmv_v_i; + } else if (((inst >> 25) & 1) == 0) { + return &op_vmerge_vim; + } break; - case 24: op = rv_op_vmseq_vi; break; - case 25: op = rv_op_vmsne_vi; break; - case 28: op = rv_op_vmsleu_vi; break; - case 29: op = rv_op_vmsle_vi; break; - case 30: op = rv_op_vmsgtu_vi; break; - case 31: op = rv_op_vmsgt_vi; break; - case 32: op = rv_op_vsaddu_vi; break; - case 33: op = rv_op_vsadd_vi; break; - case 37: op = rv_op_vsll_vi; break; + case 24: return &op_vmseq_vi; + case 25: return &op_vmsne_vi; + case 28: return &op_vmsleu_vi; + case 29: return &op_vmsle_vi; + case 30: return &op_vmsgtu_vi; + case 31: return &op_vmsgt_vi; + case 32: return &op_vsaddu_vi; + case 33: return &op_vsadd_vi; + case 37: return &op_vsll_vi; case 39: switch ((inst >> 15) & 0b11111) { - case 0: op = rv_op_vmv1r_v; break; - case 1: op = rv_op_vmv2r_v; break; - case 3: op = rv_op_vmv4r_v; break; - case 7: op = rv_op_vmv8r_v; break; + case 0: return &op_vmv1r_v; + case 1: return &op_vmv2r_v; + case 3: return &op_vmv4r_v; + case 7: return &op_vmv8r_v; } break; - case 40: op = rv_op_vsrl_vi; break; - case 41: op = rv_op_vsra_vi; break; - case 42: op = rv_op_vssrl_vi; break; - case 43: op = rv_op_vssra_vi; break; - case 44: op = rv_op_vnsrl_wi; break; - case 45: op = rv_op_vnsra_wi; break; - case 46: op = rv_op_vnclipu_wi; break; - case 47: op = rv_op_vnclip_wi; break; - case 53: op = rv_op_vwsll_vi; break; + case 40: return &op_vsrl_vi; + case 41: return &op_vsra_vi; + case 42: return &op_vssrl_vi; + case 43: return &op_vssra_vi; + case 44: return &op_vnsrl_wi; + case 45: return &op_vnsra_wi; + case 46: return &op_vnclipu_wi; + case 47: return &op_vnclip_wi; + case 53: return &op_vwsll_vi; } break; case 4: switch ((inst >> 26) & 0b111111) { - case 0: op = rv_op_vadd_vx; break; - case 1: op = rv_op_vandn_vx; break; - case 2: op = rv_op_vsub_vx; break; - case 3: op = rv_op_vrsub_vx; break; - case 4: op = rv_op_vminu_vx; break; - case 5: op = rv_op_vmin_vx; break; - case 6: op = rv_op_vmaxu_vx; break; - case 7: op = rv_op_vmax_vx; break; - case 9: op = rv_op_vand_vx; break; - case 10: op = rv_op_vor_vx; break; - case 11: op = rv_op_vxor_vx; break; - case 12: op = rv_op_vrgather_vx; break; - case 14: op = rv_op_vslideup_vx; break; - case 15: op = rv_op_vslidedown_vx; break; + case 0: return &op_vadd_vx; + case 1: return &op_vandn_vx; + case 2: return &op_vsub_vx; + case 3: return &op_vrsub_vx; + case 4: return &op_vminu_vx; + case 5: return &op_vmin_vx; + case 6: return &op_vmaxu_vx; + case 7: return &op_vmax_vx; + case 9: return &op_vand_vx; + case 10: return &op_vor_vx; + case 11: return &op_vxor_vx; + case 12: return &op_vrgather_vx; + case 14: return &op_vslideup_vx; + case 15: return &op_vslidedown_vx; case 16: if (((inst >> 25) & 1) == 0) { - op = rv_op_vadc_vxm; + return &op_vadc_vxm; } break; - case 17: op = rv_op_vmadc_vxm; break; + case 17: return &op_vmadc_vxm; case 18: if (((inst >> 25) & 1) == 0) { - op = rv_op_vsbc_vxm; + return &op_vsbc_vxm; } break; - case 19: op = rv_op_vmsbc_vxm; break; - case 20: op = rv_op_vror_vx; break; - case 21: op = rv_op_vrol_vx; break; + case 19: return &op_vmsbc_vxm; + case 20: return &op_vror_vx; + case 21: return &op_vrol_vx; case 23: - if (((inst >> 20) & 0b111111) == 32) - op = rv_op_vmv_v_x; - else if (((inst >> 25) & 1) == 0) - op = rv_op_vmerge_vxm; + if (((inst >> 20) & 0b111111) == 32) { + return &op_vmv_v_x; + } else if (((inst >> 25) & 1) == 0) { + return &op_vmerge_vxm; + } break; - case 24: op = rv_op_vmseq_vx; break; - case 25: op = rv_op_vmsne_vx; break; - case 26: op = rv_op_vmsltu_vx; break; - case 27: op = rv_op_vmslt_vx; break; - case 28: op = rv_op_vmsleu_vx; break; - case 29: op = rv_op_vmsle_vx; break; - case 30: op = rv_op_vmsgtu_vx; break; - case 31: op = rv_op_vmsgt_vx; break; - case 32: op = rv_op_vsaddu_vx; break; - case 33: op = rv_op_vsadd_vx; break; - case 34: op = rv_op_vssubu_vx; break; - case 35: op = rv_op_vssub_vx; break; - case 37: op = rv_op_vsll_vx; break; - case 39: op = rv_op_vsmul_vx; break; - case 40: op = rv_op_vsrl_vx; break; - case 41: op = rv_op_vsra_vx; break; - case 42: op = rv_op_vssrl_vx; break; - case 43: op = rv_op_vssra_vx; break; - case 44: op = rv_op_vnsrl_wx; break; - case 45: op = rv_op_vnsra_wx; break; - case 46: op = rv_op_vnclipu_wx; break; - case 47: op = rv_op_vnclip_wx; break; - case 53: op = rv_op_vwsll_vx; break; + case 24: return &op_vmseq_vx; + case 25: return &op_vmsne_vx; + case 26: return &op_vmsltu_vx; + case 27: return &op_vmslt_vx; + case 28: return &op_vmsleu_vx; + case 29: return &op_vmsle_vx; + case 30: return &op_vmsgtu_vx; + case 31: return &op_vmsgt_vx; + case 32: return &op_vsaddu_vx; + case 33: return &op_vsadd_vx; + case 34: return &op_vssubu_vx; + case 35: return &op_vssub_vx; + case 37: return &op_vsll_vx; + case 39: return &op_vsmul_vx; + case 40: return &op_vsrl_vx; + case 41: return &op_vsra_vx; + case 42: return &op_vssrl_vx; + case 43: return &op_vssra_vx; + case 44: return &op_vnsrl_wx; + case 45: return &op_vnsra_wx; + case 46: return &op_vnclipu_wx; + case 47: return &op_vnclip_wx; + case 53: return &op_vwsll_vx; } break; case 5: switch ((inst >> 26) & 0b111111) { - case 0: op = rv_op_vfadd_vf; break; - case 2: op = rv_op_vfsub_vf; break; - case 4: op = rv_op_vfmin_vf; break; - case 6: op = rv_op_vfmax_vf; break; - case 8: op = rv_op_vfsgnj_vf; break; - case 9: op = rv_op_vfsgnjn_vf; break; - case 10: op = rv_op_vfsgnjx_vf; break; - case 14: op = rv_op_vfslide1up_vf; break; - case 15: op = rv_op_vfslide1down_vf; break; + case 0: return &op_vfadd_vf; + case 2: return &op_vfsub_vf; + case 4: return &op_vfmin_vf; + case 6: return &op_vfmax_vf; + case 8: return &op_vfsgnj_vf; + case 9: return &op_vfsgnjn_vf; + case 10: return &op_vfsgnjx_vf; + case 14: return &op_vfslide1up_vf; + case 15: return &op_vfslide1down_vf; case 16: switch ((inst >> 20) & 0b11111) { - case 0: if ((inst >> 25) & 1) op = rv_op_vfmv_s_f; break; + case 0: + if ((inst >> 25) & 1) { + return &op_vfmv_s_f; + } } break; case 23: - if (((inst >> 25) & 1) == 0) - op = rv_op_vfmerge_vfm; - else if (((inst >> 20) & 0b111111) == 32) - op = rv_op_vfmv_v_f; + if (((inst >> 25) & 1) == 0) { + return &op_vfmerge_vfm; + } else if (((inst >> 20) & 0b111111) == 32) { + return &op_vfmv_v_f; + } break; - case 24: op = rv_op_vmfeq_vf; break; - case 25: op = rv_op_vmfle_vf; break; - case 27: op = rv_op_vmflt_vf; break; - case 28: op = rv_op_vmfne_vf; break; - case 29: op = rv_op_vmfgt_vf; break; - case 31: op = rv_op_vmfge_vf; break; - case 32: op = rv_op_vfdiv_vf; break; - case 33: op = rv_op_vfrdiv_vf; break; - case 36: op = rv_op_vfmul_vf; break; - case 39: op = rv_op_vfrsub_vf; break; - case 40: op = rv_op_vfmadd_vf; break; - case 41: op = rv_op_vfnmadd_vf; break; - case 42: op = rv_op_vfmsub_vf; break; - case 43: op = rv_op_vfnmsub_vf; break; - case 44: op = rv_op_vfmacc_vf; break; - case 45: op = rv_op_vfnmacc_vf; break; - case 46: op = rv_op_vfmsac_vf; break; - case 47: op = rv_op_vfnmsac_vf; break; - case 48: op = rv_op_vfwadd_vf; break; - case 50: op = rv_op_vfwsub_vf; break; - case 52: op = rv_op_vfwadd_wf; break; - case 54: op = rv_op_vfwsub_wf; break; - case 56: op = rv_op_vfwmul_vf; break; - case 59: op = rv_op_vfwmaccbf16_vf; break; - case 60: op = rv_op_vfwmacc_vf; break; - case 61: op = rv_op_vfwnmacc_vf; break; - case 62: op = rv_op_vfwmsac_vf; break; - case 63: op = rv_op_vfwnmsac_vf; break; + case 24: return &op_vmfeq_vf; + case 25: return &op_vmfle_vf; + case 27: return &op_vmflt_vf; + case 28: return &op_vmfne_vf; + case 29: return &op_vmfgt_vf; + case 31: return &op_vmfge_vf; + case 32: return &op_vfdiv_vf; + case 33: return &op_vfrdiv_vf; + case 36: return &op_vfmul_vf; + case 39: return &op_vfrsub_vf; + case 40: return &op_vfmadd_vf; + case 41: return &op_vfnmadd_vf; + case 42: return &op_vfmsub_vf; + case 43: return &op_vfnmsub_vf; + case 44: return &op_vfmacc_vf; + case 45: return &op_vfnmacc_vf; + case 46: return &op_vfmsac_vf; + case 47: return &op_vfnmsac_vf; + case 48: return &op_vfwadd_vf; + case 50: return &op_vfwsub_vf; + case 52: return &op_vfwadd_wf; + case 54: return &op_vfwsub_wf; + case 56: return &op_vfwmul_vf; + case 59: return &op_vfwmaccbf16_vf; + case 60: return &op_vfwmacc_vf; + case 61: return &op_vfwnmacc_vf; + case 62: return &op_vfwmsac_vf; + case 63: return &op_vfwnmsac_vf; } break; case 6: switch ((inst >> 26) & 0b111111) { - case 8: op = rv_op_vaaddu_vx; break; - case 9: op = rv_op_vaadd_vx; break; - case 10: op = rv_op_vasubu_vx; break; - case 11: op = rv_op_vasub_vx; break; - case 12: op = rv_op_vclmul_vx; break; - case 13: op = rv_op_vclmulh_vx; break; - case 14: op = rv_op_vslide1up_vx; break; - case 15: op = rv_op_vslide1down_vx; break; + case 8: return &op_vaaddu_vx; + case 9: return &op_vaadd_vx; + case 10: return &op_vasubu_vx; + case 11: return &op_vasub_vx; + case 12: return &op_vclmul_vx; + case 13: return &op_vclmulh_vx; + case 14: return &op_vslide1up_vx; + case 15: return &op_vslide1down_vx; case 16: switch ((inst >> 20) & 0b11111) { - case 0: if ((inst >> 25) & 1) op = rv_op_vmv_s_x; break; + case 0: + if ((inst >> 25) & 1) { + return &op_vmv_s_x; + } } break; - case 32: op = rv_op_vdivu_vx; break; - case 33: op = rv_op_vdiv_vx; break; - case 34: op = rv_op_vremu_vx; break; - case 35: op = rv_op_vrem_vx; break; - case 36: op = rv_op_vmulhu_vx; break; - case 37: op = rv_op_vmul_vx; break; - case 38: op = rv_op_vmulhsu_vx; break; - case 39: op = rv_op_vmulh_vx; break; - case 41: op = rv_op_vmadd_vx; break; - case 43: op = rv_op_vnmsub_vx; break; - case 45: op = rv_op_vmacc_vx; break; - case 47: op = rv_op_vnmsac_vx; break; - case 48: op = rv_op_vwaddu_vx; break; - case 49: op = rv_op_vwadd_vx; break; - case 50: op = rv_op_vwsubu_vx; break; - case 51: op = rv_op_vwsub_vx; break; - case 52: op = rv_op_vwaddu_wx; break; - case 53: op = rv_op_vwadd_wx; break; - case 54: op = rv_op_vwsubu_wx; break; - case 55: op = rv_op_vwsub_wx; break; - case 56: op = rv_op_vwmulu_vx; break; - case 58: op = rv_op_vwmulsu_vx; break; - case 59: op = rv_op_vwmul_vx; break; - case 60: op = rv_op_vwmaccu_vx; break; - case 61: op = rv_op_vwmacc_vx; break; - case 62: op = rv_op_vwmaccus_vx; break; - case 63: op = rv_op_vwmaccsu_vx; break; + case 32: return &op_vdivu_vx; + case 33: return &op_vdiv_vx; + case 34: return &op_vremu_vx; + case 35: return &op_vrem_vx; + case 36: return &op_vmulhu_vx; + case 37: return &op_vmul_vx; + case 38: return &op_vmulhsu_vx; + case 39: return &op_vmulh_vx; + case 41: return &op_vmadd_vx; + case 43: return &op_vnmsub_vx; + case 45: return &op_vmacc_vx; + case 47: return &op_vnmsac_vx; + case 48: return &op_vwaddu_vx; + case 49: return &op_vwadd_vx; + case 50: return &op_vwsubu_vx; + case 51: return &op_vwsub_vx; + case 52: return &op_vwaddu_wx; + case 53: return &op_vwadd_wx; + case 54: return &op_vwsubu_wx; + case 55: return &op_vwsub_wx; + case 56: return &op_vwmulu_vx; + case 58: return &op_vwmulsu_vx; + case 59: return &op_vwmul_vx; + case 60: return &op_vwmaccu_vx; + case 61: return &op_vwmacc_vx; + case 62: return &op_vwmaccus_vx; + case 63: return &op_vwmaccsu_vx; } break; case 7: if (((inst >> 31) & 1) == 0) { - op = rv_op_vsetvli; + return &op_vsetvli; } else if ((inst >> 30) & 1) { - op = rv_op_vsetivli; + return &op_vsetivli; } else if (((inst >> 25) & 0b11111) == 0) { - op = rv_op_vsetvl; + return &op_vsetvl; } break; } break; case 22: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_addid; break; + case 0: return &op_addid; case 1: switch ((inst >> 26) & 0b111111) { - case 0: op = rv_op_sllid; break; + case 0: return &op_sllid; } break; case 5: switch ((inst >> 26) & 0b111111) { - case 0: op = rv_op_srlid; break; - case 16: op = rv_op_sraid; break; + case 0: return &op_srlid; + case 16: return &op_sraid; } break; } break; case 24: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_beq; break; - case 1: op = rv_op_bne; break; - case 4: op = rv_op_blt; break; - case 5: op = rv_op_bge; break; - case 6: op = rv_op_bltu; break; - case 7: op = rv_op_bgeu; break; + case 0: return &op_beq; + case 1: return &op_bne; + case 4: return &op_blt; + case 5: return &op_bge; + case 6: return &op_bltu; + case 7: return &op_bgeu; } break; case 25: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_jalr; break; + case 0: return &op_jalr; } break; - case 27: op = rv_op_jal; break; + case 27: return &op_jal; case 28: switch ((inst >> 12) & 0b111) { case 0: @@ -4046,556 +2504,184 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) ((inst >> 7) & 0b000000011111)) { case 0: switch ((inst >> 15) & 0b1111111111) { - case 0: op = rv_op_ecall; break; - case 32: op = rv_op_ebreak; break; - case 64: op = rv_op_uret; break; - case 416: op = rv_op_wrs_nto; break; - case 928: op = rv_op_wrs_sto; break; + case 0: return &op_ecall; + case 32: return &op_ebreak; + case 64: return &op_uret; + case 416: return &op_wrs_nto; + case 928: return &op_wrs_sto; } break; case 256: switch ((inst >> 20) & 0b11111) { case 2: switch ((inst >> 15) & 0b11111) { - case 0: op = rv_op_sret; break; + case 0: return &op_sret; } break; - case 4: op = rv_op_sfence_vm; break; + case 4: return &op_sfence_vm; case 5: switch ((inst >> 15) & 0b11111) { - case 0: op = rv_op_wfi; break; + case 0: return &op_wfi; } break; } break; - case 288: op = rv_op_sfence_vma; break; + case 288: return &op_sfence_vma; case 512: switch ((inst >> 15) & 0b1111111111) { - case 64: op = rv_op_hret; break; + case 64: return &op_hret; } break; case 768: switch ((inst >> 15) & 0b1111111111) { - case 64: op = rv_op_mret; break; + case 64: return &op_mret; } break; case 1792: switch ((inst >> 15) & 0b1111111111) { - case 64: op = rv_op_mnret; break; + case 64: return &op_mnret; } break; case 1952: switch ((inst >> 15) & 0b1111111111) { - case 576: op = rv_op_dret; break; + case 576: return &op_dret; } break; } break; - case 1: op = rv_op_csrrw; break; - case 2: op = rv_op_csrrs; break; - case 3: op = rv_op_csrrc; break; - case 4: - if (dec->cfg && dec->cfg->ext_zimop) { - int imm_mop5, imm_mop3, reg_num; - if ((extract32(inst, 22, 10) & 0b1011001111) - == 0b1000000111) { - imm_mop5 = deposit32(deposit32(extract32(inst, 20, 2), - 2, 2, - extract32(inst, 26, 2)), - 4, 1, extract32(inst, 30, 1)); - op = rv_mop_r_0 + imm_mop5; - /* if zicfiss enabled and mop5 is shadow stack */ - if (dec->cfg->ext_zicfiss && - ((imm_mop5 & 0b11100) == 0b11100)) { - /* rs1=0 means ssrdp */ - if ((inst & (0b011111 << 15)) == 0) { - op = rv_op_ssrdp; - } - /* rd=0 means sspopchk */ - reg_num = (inst >> 15) & 0b011111; - if (((inst & (0b011111 << 7)) == 0) && - ((reg_num == 1) || (reg_num == 5))) { - op = rv_op_sspopchk; - } - } - } else if ((extract32(inst, 25, 7) & 0b1011001) - == 0b1000001) { - imm_mop3 = deposit32(extract32(inst, 26, 2), - 2, 1, extract32(inst, 30, 1)); - op = rv_mop_rr_0 + imm_mop3; - /* if zicfiss enabled and mop3 is shadow stack */ - if (dec->cfg->ext_zicfiss && - ((imm_mop3 & 0b111) == 0b111)) { - /* rs1=0 and rd=0 means sspush */ - reg_num = (inst >> 20) & 0b011111; - if (((inst & (0b011111 << 15)) == 0) && - ((inst & (0b011111 << 7)) == 0) && - ((reg_num == 1) || (reg_num == 5))) { - op = rv_op_sspush; - } - } + case 1: + switch (operand_csr12(inst)) { + case 1: return &op_fsflags; + case 2: return &op_fsrm; + case 3: return &op_fscsr; + default: return &op_csrrw; + } + break; + case 2: + return &op_csrrs; + if (operand_rs1(inst) == 0) { + switch (operand_csr12(inst)) { + case 0x001: return &op_frflags; + case 0x002: return &op_frrm; + case 0x003: return &op_frcsr; + case 0xc00: return &op_rdcycle; + case 0xc01: return &op_rdtime; + case 0xc02: return &op_rdinstret; + case 0xc80: return &op_rdcycleh; + case 0xc81: return &op_rdtimeh; + case 0xc82: return &op_rdinstreth; } } break; - case 5: op = rv_op_csrrwi; break; - case 6: op = rv_op_csrrsi; break; - case 7: op = rv_op_csrrci; break; + case 3: return &op_csrrc; + case 4: + if (dec->cfg && dec->cfg->ext_zimop) { + if (((inst >> 22) & 0b1011001111) == 0b1000000111) { + if (dec->cfg->ext_zicfiss + && operand_mop_r_imm(inst) == 28) { + switch (operand_rs1(inst)) { + case 0: + return &op_ssrdp; + case 1: + case 5: + if (operand_rd(inst) == 0) { + return &op_sspopchk; + } + } + } + return &op_mop_r; + } + if (((inst >> 25) & 0b1011001) == 0b1000001) { + if (dec->cfg->ext_zicfiss + && operand_mop_rr_imm(inst) == 7 + && operand_rd(inst) == 0 + && operand_rs1(inst) == 0) { + switch (operand_rs2(inst)) { + case 1: + case 5: + return &op_sspush; + } + } + return &op_mop_rr; + } + } + break; + case 5: + switch (operand_csr12(inst)) { + case 1: return &op_fsflagsi; + case 2: return &op_fsrmi; + default: return &op_csrrwi; + } + break; + case 6: return &op_csrrsi; + case 7: return &op_csrrci; } break; case 29: if (((inst >> 25) & 1) == 1 && ((inst >> 12) & 0b111) == 2) { switch ((inst >> 26) & 0b111111) { - case 32: op = rv_op_vsm3me_vv; break; - case 33: op = rv_op_vsm4k_vi; break; - case 34: op = rv_op_vaeskf1_vi; break; + case 32: return &op_vsm3me_vv; + case 33: return &op_vsm4k_vi; + case 34: return &op_vaeskf1_vi; case 40: switch ((inst >> 15) & 0b11111) { - case 0: op = rv_op_vaesdm_vv; break; - case 1: op = rv_op_vaesdf_vv; break; - case 2: op = rv_op_vaesem_vv; break; - case 3: op = rv_op_vaesef_vv; break; - case 16: op = rv_op_vsm4r_vv; break; - case 17: op = rv_op_vgmul_vv; break; + case 0: return &op_vaesdm_vv; + case 1: return &op_vaesdf_vv; + case 2: return &op_vaesem_vv; + case 3: return &op_vaesef_vv; + case 16: return &op_vsm4r_vv; + case 17: return &op_vgmul_vv; } break; case 41: switch ((inst >> 15) & 0b11111) { - case 0: op = rv_op_vaesdm_vs; break; - case 1: op = rv_op_vaesdf_vs; break; - case 2: op = rv_op_vaesem_vs; break; - case 3: op = rv_op_vaesef_vs; break; - case 7: op = rv_op_vaesz_vs; break; - case 16: op = rv_op_vsm4r_vs; break; + case 0: return &op_vaesdm_vs; + case 1: return &op_vaesdf_vs; + case 2: return &op_vaesem_vs; + case 3: return &op_vaesef_vs; + case 7: return &op_vaesz_vs; + case 16: return &op_vsm4r_vs; } break; - case 42: op = rv_op_vaeskf2_vi; break; - case 43: op = rv_op_vsm3c_vi; break; - case 44: op = rv_op_vghsh_vv; break; - case 45: op = rv_op_vsha2ms_vv; break; - case 46: op = rv_op_vsha2ch_vv; break; - case 47: op = rv_op_vsha2cl_vv; break; + case 42: return &op_vaeskf2_vi; + case 43: return &op_vsm3c_vi; + case 44: return &op_vghsh_vv; + case 45: return &op_vsha2ms_vv; + case 46: return &op_vsha2ch_vv; + case 47: return &op_vsha2cl_vv; } } break; case 30: switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) { - case 0: op = rv_op_addd; break; - case 1: op = rv_op_slld; break; - case 5: op = rv_op_srld; break; - case 8: op = rv_op_muld; break; - case 12: op = rv_op_divd; break; - case 13: op = rv_op_divud; break; - case 14: op = rv_op_remd; break; - case 15: op = rv_op_remud; break; - case 256: op = rv_op_subd; break; - case 261: op = rv_op_srad; break; + case 0: return &op_addd; + case 1: return &op_slld; + case 5: return &op_srld; + case 8: return &op_muld; + case 12: return &op_divd; + case 13: return &op_divud; + case 14: return &op_remd; + case 15: return &op_remud; + case 256: return &op_subd; + case 261: return &op_srad; } break; } break; } - dec->op = op; -} -/* operand extractors */ - -static uint32_t operand_rd(rv_inst inst) -{ - return (inst << 52) >> 59; -} - -static uint32_t operand_rs1(rv_inst inst) -{ - return (inst << 44) >> 59; -} - -static uint32_t operand_rs2(rv_inst inst) -{ - return (inst << 39) >> 59; -} - -static uint32_t operand_rs3(rv_inst inst) -{ - return (inst << 32) >> 59; -} - -static uint32_t operand_aq(rv_inst inst) -{ - return (inst << 37) >> 63; -} - -static uint32_t operand_rl(rv_inst inst) -{ - return (inst << 38) >> 63; -} - -static uint32_t operand_pred(rv_inst inst) -{ - return (inst << 36) >> 60; -} - -static uint32_t operand_succ(rv_inst inst) -{ - return (inst << 40) >> 60; -} - -static uint32_t operand_rm(rv_inst inst) -{ - return (inst << 49) >> 61; -} - -static uint32_t operand_shamt5(rv_inst inst) -{ - return (inst << 39) >> 59; -} - -static uint32_t operand_shamt6(rv_inst inst) -{ - return (inst << 38) >> 58; -} - -static uint32_t operand_shamt7(rv_inst inst) -{ - return (inst << 37) >> 57; -} - -static uint32_t operand_crdq(rv_inst inst) -{ - return (inst << 59) >> 61; -} - -static uint32_t operand_crs1q(rv_inst inst) -{ - return (inst << 54) >> 61; -} - -static uint32_t operand_crs1rdq(rv_inst inst) -{ - return (inst << 54) >> 61; -} - -static uint32_t operand_crs2q(rv_inst inst) -{ - return (inst << 59) >> 61; -} - -static uint32_t calculate_xreg(uint32_t sreg) -{ - return sreg < 2 ? sreg + 8 : sreg + 16; -} - -static uint32_t operand_sreg1(rv_inst inst) -{ - return calculate_xreg((inst << 54) >> 61); -} - -static uint32_t operand_sreg2(rv_inst inst) -{ - return calculate_xreg((inst << 59) >> 61); -} - -static uint32_t operand_crd(rv_inst inst) -{ - return (inst << 52) >> 59; -} - -static uint32_t operand_crs1(rv_inst inst) -{ - return (inst << 52) >> 59; -} - -static uint32_t operand_crs1rd(rv_inst inst) -{ - return (inst << 52) >> 59; -} - -static uint32_t operand_crs2(rv_inst inst) -{ - return (inst << 57) >> 59; -} - -static uint32_t operand_cimmsh5(rv_inst inst) -{ - return (inst << 57) >> 59; -} - -static uint32_t operand_csr12(rv_inst inst) -{ - return (inst << 32) >> 52; -} - -static int32_t operand_imm12(rv_inst inst) -{ - return ((int64_t)inst << 32) >> 52; -} - -static int32_t operand_imm20(rv_inst inst) -{ - return (((int64_t)inst << 32) >> 44) << 12; -} - -static int32_t operand_jimm20(rv_inst inst) -{ - return (((int64_t)inst << 32) >> 63) << 20 | - ((inst << 33) >> 54) << 1 | - ((inst << 43) >> 63) << 11 | - ((inst << 44) >> 56) << 12; -} - -static int32_t operand_simm12(rv_inst inst) -{ - return (((int64_t)inst << 32) >> 57) << 5 | - (inst << 52) >> 59; -} - -static int32_t operand_sbimm12(rv_inst inst) -{ - return (((int64_t)inst << 32) >> 63) << 12 | - ((inst << 33) >> 58) << 5 | - ((inst << 52) >> 60) << 1 | - ((inst << 56) >> 63) << 11; -} - -static uint32_t operand_cimmshl6(rv_inst inst, rv_isa isa) -{ - int imm = ((inst << 51) >> 63) << 5 | - (inst << 57) >> 59; - if (isa == rv128) { - imm = imm ? imm : 64; - } - return imm; -} - -static uint32_t operand_cimmshr6(rv_inst inst, rv_isa isa) -{ - int imm = ((inst << 51) >> 63) << 5 | - (inst << 57) >> 59; - if (isa == rv128) { - imm = imm | (imm & 32) << 1; - imm = imm ? imm : 64; - } - return imm; -} - -static int32_t operand_cimmi(rv_inst inst) -{ - return (((int64_t)inst << 51) >> 63) << 5 | - (inst << 57) >> 59; -} - -static int32_t operand_cimmui(rv_inst inst) -{ - return (((int64_t)inst << 51) >> 63) << 17 | - ((inst << 57) >> 59) << 12; -} - -static uint32_t operand_cimmlwsp(rv_inst inst) -{ - return ((inst << 51) >> 63) << 5 | - ((inst << 57) >> 61) << 2 | - ((inst << 60) >> 62) << 6; -} - -static uint32_t operand_cimmldsp(rv_inst inst) -{ - return ((inst << 51) >> 63) << 5 | - ((inst << 57) >> 62) << 3 | - ((inst << 59) >> 61) << 6; -} - -static uint32_t operand_cimmlqsp(rv_inst inst) -{ - return ((inst << 51) >> 63) << 5 | - ((inst << 57) >> 63) << 4 | - ((inst << 58) >> 60) << 6; -} - -static int32_t operand_cimm16sp(rv_inst inst) -{ - return (((int64_t)inst << 51) >> 63) << 9 | - ((inst << 57) >> 63) << 4 | - ((inst << 58) >> 63) << 6 | - ((inst << 59) >> 62) << 7 | - ((inst << 61) >> 63) << 5; -} - -static int32_t operand_cimmj(rv_inst inst) -{ - return (((int64_t)inst << 51) >> 63) << 11 | - ((inst << 52) >> 63) << 4 | - ((inst << 53) >> 62) << 8 | - ((inst << 55) >> 63) << 10 | - ((inst << 56) >> 63) << 6 | - ((inst << 57) >> 63) << 7 | - ((inst << 58) >> 61) << 1 | - ((inst << 61) >> 63) << 5; -} - -static int32_t operand_cimmb(rv_inst inst) -{ - return (((int64_t)inst << 51) >> 63) << 8 | - ((inst << 52) >> 62) << 3 | - ((inst << 57) >> 62) << 6 | - ((inst << 59) >> 62) << 1 | - ((inst << 61) >> 63) << 5; -} - -static uint32_t operand_cimmswsp(rv_inst inst) -{ - return ((inst << 51) >> 60) << 2 | - ((inst << 55) >> 62) << 6; -} - -static uint32_t operand_cimmsdsp(rv_inst inst) -{ - return ((inst << 51) >> 61) << 3 | - ((inst << 54) >> 61) << 6; -} - -static uint32_t operand_cimmsqsp(rv_inst inst) -{ - return ((inst << 51) >> 62) << 4 | - ((inst << 53) >> 60) << 6; -} - -static uint32_t operand_cimm4spn(rv_inst inst) -{ - return ((inst << 51) >> 62) << 4 | - ((inst << 53) >> 60) << 6 | - ((inst << 57) >> 63) << 2 | - ((inst << 58) >> 63) << 3; -} - -static uint32_t operand_cimmw(rv_inst inst) -{ - return ((inst << 51) >> 61) << 3 | - ((inst << 57) >> 63) << 2 | - ((inst << 58) >> 63) << 6; -} - -static uint32_t operand_cimmd(rv_inst inst) -{ - return ((inst << 51) >> 61) << 3 | - ((inst << 57) >> 62) << 6; -} - -static uint32_t operand_cimmq(rv_inst inst) -{ - return ((inst << 51) >> 62) << 4 | - ((inst << 53) >> 63) << 8 | - ((inst << 57) >> 62) << 6; -} - -static uint32_t operand_vimm(rv_inst inst) -{ - return (int64_t)(inst << 44) >> 59; -} - -static uint32_t operand_vzimm11(rv_inst inst) -{ - return (inst << 33) >> 53; -} - -static uint32_t operand_vzimm10(rv_inst inst) -{ - return (inst << 34) >> 54; -} - -static uint32_t operand_vzimm6(rv_inst inst) -{ - return ((inst << 37) >> 63) << 5 | - ((inst << 44) >> 59); -} - -static uint32_t operand_bs(rv_inst inst) -{ - return (inst << 32) >> 62; -} - -static uint32_t operand_rnum(rv_inst inst) -{ - return (inst << 40) >> 60; -} - -static uint32_t operand_vm(rv_inst inst) -{ - return (inst << 38) >> 63; -} - -static uint32_t operand_uimm_c_lb(rv_inst inst) -{ - return (((inst << 58) >> 63) << 1) | - ((inst << 57) >> 63); -} - -static uint32_t operand_uimm_c_lh(rv_inst inst) -{ - return (((inst << 58) >> 63) << 1); -} - -static uint32_t operand_zcmp_spimm(rv_inst inst) -{ - return ((inst << 60) >> 62) << 4; -} - -static uint32_t operand_zcmp_rlist(rv_inst inst) -{ - return ((inst << 56) >> 60); -} - -static uint32_t operand_imm6(rv_inst inst) -{ - return (inst << 38) >> 60; -} - -static uint32_t operand_imm2(rv_inst inst) -{ - return (inst << 37) >> 62; -} - -static uint32_t operand_immh(rv_inst inst) -{ - return (inst << 32) >> 58; -} - -static uint32_t operand_imml(rv_inst inst) -{ - return (inst << 38) >> 58; -} - -static uint32_t calculate_stack_adj(rv_isa isa, uint32_t rlist, uint32_t spimm) -{ - int xlen_bytes_log2 = isa == rv64 ? 3 : 2; - int regs = rlist == 15 ? 13 : rlist - 3; - uint32_t stack_adj_base = ROUND_UP(regs << xlen_bytes_log2, 16); - return stack_adj_base + spimm; -} - -static uint32_t operand_zcmp_stack_adj(rv_inst inst, rv_isa isa) -{ - return calculate_stack_adj(isa, operand_zcmp_rlist(inst), - operand_zcmp_spimm(inst)); -} - -static uint32_t operand_tbl_index(rv_inst inst) -{ - return ((inst << 54) >> 56); -} - -static uint32_t operand_lpl(rv_inst inst) -{ - return inst >> 12; + return NULL; } /* decode operands */ -static void decode_inst_operands(rv_decode *dec, rv_isa isa) +static void decode_inst_operands(rv_decode *dec, rv_isa isa, + const rv_opcode_data *op) { - const rv_opcode_data *opcode_data = dec->opcode_data; rv_inst inst = dec->inst; - dec->codec = opcode_data[dec->op].codec; - switch (dec->codec) { + + switch (op->codec) { case rv_codec_none: dec->rd = dec->rs1 = dec->rs2 = rv_ireg_zero; dec->imm = 0; @@ -4895,6 +2981,12 @@ static void decode_inst_operands(rv_decode *dec, rv_isa isa) dec->imm = operand_vimm(inst); dec->vm = operand_vm(inst); break; + case rv_codec_v_i_u: + dec->rd = operand_rd(inst); + dec->rs2 = operand_rs2(inst); + dec->imm = operand_vuimm(inst); + dec->vm = operand_vm(inst); + break; case rv_codec_vror_vi: dec->rd = operand_rd(inst); dec->rs2 = operand_rs2(inst); @@ -4978,12 +3070,28 @@ static void decode_inst_operands(rv_decode *dec, rv_isa isa) case rv_codec_lp: dec->imm = operand_lpl(inst); break; + case rv_codec_cmop: + dec->imm = operand_cmop_imm(inst); + break; case rv_codec_cmop_ss: dec->rd = rv_ireg_zero; dec->rs1 = dec->rs2 = operand_crs1(inst); dec->imm = 0; break; - }; + case rv_codec_mop_r: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->imm = operand_mop_r_imm(inst); + break; + case rv_codec_mop_rr: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->rs2 = operand_rs2(inst); + dec->imm = operand_mop_rr_imm(inst); + break; + default: + g_assert_not_reached(); + } } /* check constraint */ @@ -5039,51 +3147,6 @@ static bool check_constraints(rv_decode *dec, const rvc_constraint *c) return false; } break; - case rvc_csr_eq_0x001: - if (!(imm == 0x001)) { - return false; - } - break; - case rvc_csr_eq_0x002: - if (!(imm == 0x002)) { - return false; - } - break; - case rvc_csr_eq_0x003: - if (!(imm == 0x003)) { - return false; - } - break; - case rvc_csr_eq_0xc00: - if (!(imm == 0xc00)) { - return false; - } - break; - case rvc_csr_eq_0xc01: - if (!(imm == 0xc01)) { - return false; - } - break; - case rvc_csr_eq_0xc02: - if (!(imm == 0xc02)) { - return false; - } - break; - case rvc_csr_eq_0xc80: - if (!(imm == 0xc80)) { - return false; - } - break; - case rvc_csr_eq_0xc81: - if (!(imm == 0xc81)) { - return false; - } - break; - case rvc_csr_eq_0xc82: - if (!(imm == 0xc82)) { - return false; - } - break; default: break; } c++; @@ -5099,19 +3162,19 @@ static size_t inst_length(rv_inst inst) /* format instruction */ -static GString *format_inst(size_t tab, rv_decode *dec) +static GString *format_inst(size_t tab, rv_decode *dec, + const rv_opcode_data *op) { - const rv_opcode_data *opcode_data = dec->opcode_data; GString *buf = g_string_sized_new(64); - const char *fmt; + const char *fmt = op->format; - fmt = opcode_data[dec->op].format; while (*fmt) { switch (*fmt) { case 'O': - g_string_append(buf, opcode_data[dec->op].name); + g_string_append(buf, op->name); break; case '(': + case '.': case ',': case ')': case '-': @@ -5346,83 +3409,20 @@ static GString *format_inst(size_t tab, rv_decode *dec) /* lift instruction to pseudo-instruction */ -static void decode_inst_lift_pseudo(rv_decode *dec) +static const rv_opcode_data *decode_inst_lift_pseudo(rv_decode *dec, + const rv_opcode_data *op) { - const rv_opcode_data *opcode_data = dec->opcode_data; - const rv_comp_data *comp_data = opcode_data[dec->op].pseudo; - if (!comp_data) { - return; - } - while (comp_data->constraints) { - if (check_constraints(dec, comp_data->constraints)) { - dec->op = comp_data->op; - dec->codec = opcode_data[dec->op].codec; - return; - } - comp_data++; - } -} - -/* decompress instruction */ - -static void decode_inst_decompress_rv32(rv_decode *dec) -{ - const rv_opcode_data *opcode_data = dec->opcode_data; - int decomp_op = opcode_data[dec->op].decomp_rv32; - if (decomp_op != rv_op_illegal) { - if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz) - && dec->imm == 0) { - dec->op = rv_op_illegal; - } else { - dec->op = decomp_op; - dec->codec = opcode_data[decomp_op].codec; + const rv_comp_data *comp_data = op->pseudo; + if (comp_data) { + while (comp_data->constraints) { + if (check_constraints(dec, comp_data->constraints)) { + assert(op != comp_data->op); + return decode_inst_lift_pseudo(dec, comp_data->op); + } + comp_data++; } } -} - -static void decode_inst_decompress_rv64(rv_decode *dec) -{ - const rv_opcode_data *opcode_data = dec->opcode_data; - int decomp_op = opcode_data[dec->op].decomp_rv64; - if (decomp_op != rv_op_illegal) { - if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz) - && dec->imm == 0) { - dec->op = rv_op_illegal; - } else { - dec->op = decomp_op; - dec->codec = opcode_data[decomp_op].codec; - } - } -} - -static void decode_inst_decompress_rv128(rv_decode *dec) -{ - const rv_opcode_data *opcode_data = dec->opcode_data; - int decomp_op = opcode_data[dec->op].decomp_rv128; - if (decomp_op != rv_op_illegal) { - if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz) - && dec->imm == 0) { - dec->op = rv_op_illegal; - } else { - dec->op = decomp_op; - dec->codec = opcode_data[decomp_op].codec; - } - } -} - -static void decode_inst_decompress(rv_decode *dec, rv_isa isa) -{ - switch (isa) { - case rv32: - decode_inst_decompress_rv32(dec); - break; - case rv64: - decode_inst_decompress_rv64(dec); - break; - case rv128: - decode_inst_decompress_rv128(dec); - break; - } + return op; } /* disassemble instruction */ @@ -5430,60 +3430,55 @@ static void decode_inst_decompress(rv_decode *dec, rv_isa isa) static GString *disasm_inst(rv_isa isa, uint64_t pc, rv_inst inst, const RISCVCPUConfig *cfg) { - rv_decode dec = { 0 }; - dec.pc = pc; - dec.inst = inst; - dec.cfg = cfg; - - static const struct { - bool (*guard_func)(const RISCVCPUConfig *); - const rv_opcode_data *opcode_data; - void (*decode_func)(rv_decode *, rv_isa); - } decoders[] = { - { always_true_p, rvi_opcode_data, decode_inst_opcode }, - { has_xtheadba_p, xthead_opcode_data, decode_xtheadba }, - { has_xtheadbb_p, xthead_opcode_data, decode_xtheadbb }, - { has_xtheadbs_p, xthead_opcode_data, decode_xtheadbs }, - { has_xtheadcmo_p, xthead_opcode_data, decode_xtheadcmo }, - { has_xtheadcondmov_p, xthead_opcode_data, decode_xtheadcondmov }, - { has_xtheadfmemidx_p, xthead_opcode_data, decode_xtheadfmemidx }, - { has_xtheadfmv_p, xthead_opcode_data, decode_xtheadfmv }, - { has_xtheadmac_p, xthead_opcode_data, decode_xtheadmac }, - { has_xtheadmemidx_p, xthead_opcode_data, decode_xtheadmemidx }, - { has_xtheadmempair_p, xthead_opcode_data, decode_xtheadmempair }, - { has_xtheadsync_p, xthead_opcode_data, decode_xtheadsync }, - { has_XVentanaCondOps_p, ventana_opcode_data, decode_xventanacondops }, - { has_xlrbr_p, rv_xlrbr_opcode_data, decode_xlrbr }, + rv_decode dec = { + .pc = pc, + .inst = inst, + .cfg = cfg, }; + const rv_opcode_data *op = decode_inst_opcode(&dec, isa); - for (size_t i = 0; i < ARRAY_SIZE(decoders); i++) { - bool (*guard_func)(const RISCVCPUConfig *) = decoders[i].guard_func; - const rv_opcode_data *opcode_data = decoders[i].opcode_data; - void (*decode_func)(rv_decode *, rv_isa) = decoders[i].decode_func; + if (!op && cfg) { + static const struct { + bool (*guard_func)(const RISCVCPUConfig *); + const rv_opcode_data *(*decode_func)(rv_decode *, rv_isa); + } decoders[] = { + { has_xtheadba_p, decode_xtheadba }, + { has_xtheadbb_p, decode_xtheadbb }, + { has_xtheadbs_p, decode_xtheadbs }, + { has_xtheadcmo_p, decode_xtheadcmo }, + { has_xtheadcondmov_p, decode_xtheadcondmov }, + { has_xtheadfmemidx_p, decode_xtheadfmemidx }, + { has_xtheadfmv_p, decode_xtheadfmv }, + { has_xtheadmac_p, decode_xtheadmac }, + { has_xtheadmemidx_p, decode_xtheadmemidx }, + { has_xtheadmempair_p, decode_xtheadmempair }, + { has_xtheadsync_p, decode_xtheadsync }, + { has_XVentanaCondOps_p, decode_xventanacondops }, + { has_xlrbr_p, decode_xlrbr }, + }; - /* always_true_p don't dereference cfg */ - if (((i == 0) || cfg) && guard_func(cfg)) { - dec.opcode_data = opcode_data; - decode_func(&dec, isa); - if (dec.op != rv_op_illegal) - break; + for (size_t i = 0; i < ARRAY_SIZE(decoders); i++) { + if (decoders[i].guard_func(cfg)) { + op = decoders[i].decode_func(&dec, isa); + if (op) { + break; + } + } } } - if (dec.op == rv_op_illegal) { - dec.opcode_data = rvi_opcode_data; + if (op) { + decode_inst_operands(&dec, isa, op); + op = decode_inst_lift_pseudo(&dec, op); + } else { + op = &op_illegal; } - decode_inst_operands(&dec, isa); - decode_inst_decompress(&dec, isa); - decode_inst_lift_pseudo(&dec); - return format_inst(24, &dec); + return format_inst(24, &dec, op); } -#define INST_FMT_2 "%04" PRIx64 " " -#define INST_FMT_4 "%08" PRIx64 " " -#define INST_FMT_6 "%012" PRIx64 " " -#define INST_FMT_8 "%016" PRIx64 " " +#define INST_FMT_2 "%04x " +#define INST_FMT_4 "%08x " static int print_insn_riscv(bfd_vma memaddr, struct disassemble_info *info, rv_isa isa) @@ -5519,12 +3514,8 @@ print_insn_riscv(bfd_vma memaddr, struct disassemble_info *info, rv_isa isa) case 4: (*info->fprintf_func)(info->stream, INST_FMT_4, inst); break; - case 6: - (*info->fprintf_func)(info->stream, INST_FMT_6, inst); - break; default: - (*info->fprintf_func)(info->stream, INST_FMT_8, inst); - break; + g_assert_not_reached(); } } diff --git a/disas/riscv.h b/disas/riscv.h index 379e642ec8..b0269bb709 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -11,7 +11,7 @@ /* types */ -typedef uint64_t rv_inst; +typedef uint32_t rv_inst; typedef uint16_t rv_opcode; /* enums */ @@ -84,15 +84,6 @@ typedef enum { rvc_imm_eq_zero, rvc_imm_eq_n1, rvc_imm_eq_p1, - rvc_csr_eq_0x001, - rvc_csr_eq_0x002, - rvc_csr_eq_0x003, - rvc_csr_eq_0xc00, - rvc_csr_eq_0xc01, - rvc_csr_eq_0xc02, - rvc_csr_eq_0xc80, - rvc_csr_eq_0xc81, - rvc_csr_eq_0xc82, } rvc_constraint; typedef enum { @@ -149,6 +140,7 @@ typedef enum { rv_codec_v_r, rv_codec_v_ldst, rv_codec_v_i, + rv_codec_v_i_u, rv_codec_vsetvli, rv_codec_vsetivli, rv_codec_vror_vi, @@ -167,36 +159,34 @@ typedef enum { rv_codec_r2_imm2_imm5, rv_codec_fli, rv_codec_lp, + rv_codec_cmop, rv_codec_cmop_ss, + rv_codec_mop_r, + rv_codec_mop_rr, } rv_codec; /* structures */ +typedef struct rv_opcode_data rv_opcode_data; + typedef struct { - const int op; + const rv_opcode_data *op; const rvc_constraint *constraints; } rv_comp_data; -typedef struct { - const char * const name; - const rv_codec codec; - const char * const format; +struct rv_opcode_data { + const char *name; + rv_codec codec; + const char *format; const rv_comp_data *pseudo; - const short decomp_rv32; - const short decomp_rv64; - const short decomp_rv128; - const short decomp_data; -} rv_opcode_data; +}; typedef struct { const RISCVCPUConfig *cfg; uint64_t pc; uint64_t inst; - const rv_opcode_data *opcode_data; int32_t imm; int32_t imm1; - uint16_t op; - uint8_t codec; uint8_t rd; uint8_t rs1; uint8_t rs2; @@ -213,14 +203,6 @@ typedef struct { uint8_t rlist; } rv_decode; -enum { - rv_op_illegal = 0 -}; - -enum { - rvcd_imm_nz = 0x1 -}; - /* instruction formats */ #define rv_fmt_none "O\t" @@ -305,5 +287,8 @@ enum { #define rv_fmt_rd_rs1_immh_imml_addr "O\t0,(1),i,j" #define rv_fmt_rd2_imm "O\t0,2,(1),i" #define rv_fmt_fli "O\t3,h" +#define rv_fmt_cmop "O.i" +#define rv_fmt_mop_r "O.i\t0,1" +#define rv_fmt_mop_rr "O.i\t0,1,2" #endif /* DISAS_RISCV_H */ diff --git a/docs/about/emulation.rst b/docs/about/emulation.rst index b861501e85..c58149086c 100644 --- a/docs/about/emulation.rst +++ b/docs/about/emulation.rst @@ -1072,14 +1072,13 @@ syscall, so the real kernel never sees it. Trusted guests only. The guest can load arbitrary host libraries and run arbitrary code in the QEMU host process. The plugin is not a sandbox and provides no isolation. It also requires ``guest_base == 0`` (qemu-user's - default), as guest pointers are dereferenced as host addresses with no - translation. + default) and a guest whose pointer width and endianness match the host's, as + guest pointers are dereferenced as host addresses with no translation. The plugin intentionally keeps the QEMU side lightweight and knows nothing -about any particular library or its calling convention. Turning a real library -into working thunks, including argument marshalling, callbacks and variadic -functions, is done entirely in userspace, and any toolchain can implement the -interface. +about any particular library or its calling convention. Producing the thunks +for a real library is done entirely in userspace, and any toolchain can +implement the interface. Loading the plugin is all that is required from QEMU's side: @@ -1087,11 +1086,21 @@ Loading the plugin is all that is required from QEMU's side: qemu-x86_64 -plugin contrib/plugins/libdlcall.so ... +If the default number does not suit the guest ABI, pick another one, and build +the userspace side to issue the same one: + +.. code-block:: shell + + qemu-x86_64 -plugin contrib/plugins/libdlcall.so,syscall_num=8192 \ + ... + `Lorelei `_ is one end-to-end userspace implementation of this: it provides the guest and host runtimes and an automated toolchain that generates the thunks from a library's headers, so guest -library calls run on the host's native libraries. It supports an x86_64 guest -running on an x86_64, aarch64 or riscv64 host. +library calls run on the host's native libraries. How it handles the parts the +plugin leaves out, including argument marshalling, callbacks and variadic +functions, can serve as a reference. It supports an x86_64 guest running on an +x86_64, aarch64 or riscv64 host. A minimal end-to-end example uses a one-function library, ``libhello.so``, built two ways: the guest build tags its output ``(from the guest)`` and the host @@ -1201,8 +1210,10 @@ which prints:: * - Option - Description * - syscall_num=N - - The magic syscall number the guest issues (default 4096). Must be high - enough not to clash with a real syscall. + - The magic syscall number the guest issues (default 4096). It must be a + number the guest ABI does not use for a real syscall, and does not + reject before the plugin sees it, which bounds the choice from both + sides. Other emulation features ------------------------ diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index f7a2aa326d..0735b4ea1d 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -1241,7 +1241,7 @@ RISC-V default machine (removed in 11.1) '''''''''''''''''''''''''''''''''''''''' RISC-V used to define ``spike`` as the default machine if no machine option -was given via the command line. This happend because ``spike`` was the first +was given via the command line. This happened because ``spike`` was the first RISC-V machine implemented in QEMU and setting it as default was convenient at that time. Now we have 7 riscv64 and 6 riscv32 machines and having ``spike`` as a default is no longer justified. diff --git a/docs/devel/hexagon-l2vic.rst b/docs/devel/hexagon-l2vic.rst new file mode 100644 index 0000000000..9cb2a86871 --- /dev/null +++ b/docs/devel/hexagon-l2vic.rst @@ -0,0 +1,55 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Hexagon L2 Vectored Interrupt Controller +======================================== + + +.. code-block:: none + + +-------------+ +----------------------+ + | l2vic | | hexagon core | + | | | | + IRQ in ---->| | | | + IRQ in ---->| VID0 -|----------------->| irq2 | + ... ---->| | | | | + IRQ in ---->| | | v | + | ... | | | + | | | / | | \ | + IRQ in ---->| | | t0 t1 t2 t3 ...| + IRQ in ---->| VIDN -| | | + ... ---->| | | | + IRQ in ---->| | | Global SREG File | + | | | | + | State | | | + | [ ] <--|==================|==> [ VID ] | + | [ ] <--|==================|==> [ VID1 ] | + | | | | + +-------------+ +----------------------+ + +L2VIC/Core Integration +---------------------- + +* hexagon core supports 8 external interrupt sources +* l2vic supports 1024 input interrupts mapped among 4 output interrupts +* l2vic has four output signals: { VID0, VID1, VID2, VID3 } +* l2vic device has a bank of registers per-VID that can be used to query + the status or assert new interrupts. +* Interrupts are 'steered' to threads based on { thread priority, 'EX' state, + thread interrupt mask, thread interrupt enable, global interrupt enable, + etc. }. +* Any hardware thread could conceivably handle any input interrupt, dependent + on state. +* The system register transfer instruction can read the VID0-VID3 values from + the l2vic when reading from hexagon core system registers "VID" and "VID1". +* When l2vic VID0 has multiple active interrupts, it pulses the VID0 output + IRQ and stores the IRQ number for the VID0 register field. Only after this + interrupt is cleared can the l2vic pulse the VID0 output IRQ again and provide + the next interrupt number on the VID0 register. +* The ``ciad`` instruction clears the l2vic input interrupt and un-disables the + core interrupt. If some/an l2vic VID0 interrupt is pending when this occurs, + the next interrupt should fire and any subsequent reads of the VID register + should reflect the newly raised interrupt. +* In QEMU, on an external interrupt or an unmasked-pending interrupt, + all vCPUs are triggered (has_work==true) and each will grab the IO lock + while considering the steering logic to determine whether they're the thread + that must handle the interrupt. diff --git a/docs/devel/index-internals.rst b/docs/devel/index-internals.rst index b89bab9b30..763cda1e76 100644 --- a/docs/devel/index-internals.rst +++ b/docs/devel/index-internals.rst @@ -15,11 +15,13 @@ Details about QEMU's various subsystems including how to add features to them. clocks ebpf_rss hexagon-sys + hexagon-l2vic migration/index multi-process reset s390-cpu-topology s390-dasd-ipl + ssi tracing uefi-vars vfio-iommufd diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 67ea84539a..94c550b39b 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -21,19 +21,15 @@ invokes rustc directly, building static libraries that are then linked together with the C code. This is completely automatic when you run ``make`` or ``ninja``. -However, QEMU's build system also tries to be easy to use for people who -are accustomed to the more "normal" Cargo-based development workflow. -In particular: - -* the set of warnings and lints that are used to build QEMU always - comes from the ``rust/Cargo.toml`` workspace file - -* it is also possible to use ``cargo`` for common Rust-specific coding - tasks, in particular to invoke ``clippy``, ``rustfmt`` and ``rustdoc``. +However, Meson is able to consume ``Cargo.toml`` files and tries +to be easy to use for people who are accustomed to the more "normal" +Cargo-based development workflow. In the case of QEMU, in addition, +it is possible to use ``cargo`` for common Rust-specific coding +tasks, in particular to invoke ``clippy``, ``rustfmt`` and ``rustdoc``. To this end, QEMU includes a ``build.rs`` build script that picks up generated sources from QEMU's build directory and puts it in Cargo's -output directory (typically ``rust/target/``). A vanilla invocation +output directory (typically ``target/``). A vanilla invocation of Cargo will complain that it cannot find the generated sources, which can be fixed in different ways: @@ -466,63 +462,38 @@ Adding dependencies Generally, the set of dependent crates is kept small. Think twice before adding a new external crate, especially if it comes with a large set of dependencies itself. Sometimes QEMU only needs a small subset of the -functionality; see for example QEMU's ``assertions`` module. +functionality; see for example QEMU's ``assertions`` module. Also, +choose a version of the crate that works with QEMU's minimum supported +Rust version (|msrv|). On top of this recommendation, adding external crates to QEMU is a slightly complicated process, mostly due to the need to teach Meson how -to build them. While Meson has initial support for parsing ``Cargo.lock`` -files, it is still highly experimental and is therefore not used. +to download them. While QEMU uses Meson's support for parsing ``Cargo.toml`` +files, it ships ``.wrap`` files instead of using ``Cargo.lock``; this way, +distros can adjust the set of dependencies to the exact versions they use. +The versions specified in QEMU's ``Cargo.lock`` must be the same as the +one in the wrap file. -Therefore, external crates must be added as subprojects for Meson to -learn how to build them, as well as to the relevant ``Cargo.toml`` files. -The versions specified in ``rust/Cargo.lock`` must be the same as the -subprojects; note that the ``rust/`` directory forms a Cargo `workspace`__, -and therefore there is a single lock file for the whole build. - -__ https://doc.rust-lang.org/cargo/reference/workspaces.html#virtual-workspace - -Choose a version of the crate that works with QEMU's minimum supported -Rust version (|msrv|). - -Second, a new ``wrap`` file must be added to teach Meson how to download the -crate. The wrap file must be named ``NAME-SEMVER-rs.wrap``, where ``NAME`` +The wrap file must be named ``NAME-SEMVER-rs.wrap``, where ``NAME`` is the name of the crate and ``SEMVER`` is the version up to and including the first non-zero number. For example, a crate with version ``0.2.3`` will use ``0.2`` for its ``SEMVER``, while a crate with version ``1.0.84`` will use ``1``. -Third, the Meson rules to build the crate must be added at -``subprojects/NAME-SEMVER-rs/meson.build``. Generally this includes: +Usually, Meson is able to figure out how to build the crate, and also handles +cross compilation correctly. For crates that have a ``build.rs`` file, +equivalent rules must be added to +``subprojects/packagefiles/NAME-SEMVER-rs/meson/meson.build``. +The file can modify the ``extra_args`` and ``extra_deps`` variables, +which contain respectively the compiler arguments and external dependencies +for the crate. -* ``subproject`` and ``dependency`` lines for all dependent crates +After every change to the ``meson/meson.build`` file you have to update the +patched version with ``meson subprojects update --reset ``NAME-SEMVER-rs``. +This might be automated in the future. -* a ``static_library`` or ``rust.proc_macro`` line to perform the actual build - -* ``declare_dependency`` and a ``meson.override_dependency`` lines to expose - the result to QEMU and to other subprojects - -Remember to add ``native: true`` to ``dependency``, ``static_library`` and -``meson.override_dependency`` for dependencies of procedural macros. -If a crate is needed in both procedural macros and QEMU binaries, everything -apart from ``subproject`` must be duplicated to build both native and -non-native versions of the crate. - -It's important to specify the right compiler options. These include: - -* the language edition (which can be found in the ``Cargo.toml`` file) - -* the ``--cfg`` (which have to be "reverse engineered" from the ``build.rs`` - file of the crate). - -* usually, a ``--cap-lints allow`` argument to hide warnings from rustc - or clippy. - -After every change to the ``meson.build`` file you have to update the patched -version with ``meson subprojects update --reset ``NAME-SEMVER-rs``. This might -be automated in the future. - -Also, after every change to the ``meson.build`` file it is strongly suggested to -do a dummy change to the ``.wrap`` file (for example adding a comment like -``# version 2``), which will help Meson notice that the subproject is out of date. +Also, after every change to the file it is strongly suggested to do a dummy +change to the ``.wrap`` file (for example adding a comment like ``# version 2``), +which will help Meson notice that the subproject is out of date. As a last step, add the new subproject to ``scripts/archive-source.sh``, ``scripts/make-release`` and ``subprojects/.gitignore``. diff --git a/docs/devel/ssi.rst b/docs/devel/ssi.rst new file mode 100644 index 0000000000..864b5d9320 --- /dev/null +++ b/docs/devel/ssi.rst @@ -0,0 +1,132 @@ +================================ +SSI devices and SPI flash models +================================ + +QEMU's Synchronous Serial Interface (SSI) bus models the full-duplex transfer +of words between a controller and one selected peripheral. Most SPI flash +models, including ``m25p80``, are attached to controllers through this bus. + +This page documents the expected boundary between a controller model and a +flash model for SPI fast-read dummy cycles. The boundary is important because +many real controllers expose dummy-cycle configuration in registers, while the +flash model observes only the byte stream delivered through ``ssi_transfer()``. + +SSI transfer granularity +------------------------ + +``ssi_transfer()`` transfers one SSI word. Flash models that implement common +SPI NOR command streams usually consume one 8-bit word at a time: + +* command opcode; +* address bytes; +* optional mode or continuous-read bytes; +* dummy bytes; +* data bytes. + +The SSI core does not model individual clock edges or the number of active SPI +data lines. If a real transaction has a dummy phase expressed in clock cycles, +the device model that generates transfers on the SSI bus must represent that +phase as a number of dummy byte transfers. + +Flash model responsibilities +---------------------------- + +A SPI flash model owns the command semantics for the flash device: + +* which opcodes are recognized; +* how many address bytes are required; +* whether a command has mode bytes; +* how many dummy bytes must be consumed before data can be returned; +* manufacturer-specific differences in fast-read command behavior. + +For the ``m25p80`` model, ``needed_bytes`` is a byte count. It must not store +raw dummy cycles. When a flash datasheet describes the dummy phase in cycles, +the flash model converts the cycles to bytes using the bus width used for the +dummy phase:: + + dummy_bytes = DIV_ROUND_UP(dummy_cycles * dummy_bus_width, 8) + +For SPI NOR fast-read commands modeled by ``m25p80``, the dummy phase follows +the address phase width. For example, output-only dual and quad read commands +such as DOR and QOR use one line for command, address, and dummy phases, then +use two or four lines only for the data phase. Dual I/O and Quad I/O commands +such as DIOR and QIOR use the wider bus for both address and dummy phases. + +If the exact dummy phase cannot be represented as a whole number of SSI byte +transfers, the model should round up and log the limitation instead of silently +treating cycles as bytes. + +Controller model responsibilities +--------------------------------- + +A controller model owns the behavior of the controller hardware: + +* how guest-visible registers select command, address width, bus width, and + dummy-cycle count; +* whether the guest supplies dummy bytes in a transmit FIFO; +* whether the controller itself generates the dummy phase for a memory-mapped, + direct-read, or other automatic transfer mode; +* how chip-select state changes around controller-generated transfers. + +When guest software writes dummy bytes into a transmit FIFO or manual transfer +path, the controller should pass those bytes to ``ssi_transfer()`` like any +other guest-provided byte. It should not add more dummy transfers on behalf of +the flash. + +When hardware registers instruct the controller to generate a dummy phase, the +controller must emit dummy byte transfers before data transfers reach the flash +model. The controller should convert the configured cycle count using the bus +width that the controller uses during the dummy phase. For example: + +* 8 dummy cycles on a single data line become 1 dummy byte; +* 8 dummy cycles on two data lines become 2 dummy bytes; +* 8 dummy cycles on four data lines become 4 dummy bytes. + +The controller should not duplicate flash-specific opcode tables merely to +guess which commands need dummy cycles. In automatic modes the controller +already has enough hardware configuration to know whether it must generate a +dummy phase. In manual modes the guest-provided byte stream is authoritative. + +Avoiding double counting +------------------------ + +Exactly one side should generate each dummy byte transfer seen by the flash: + +* If the guest sends dummy bytes through the controller, the controller forwards + them and the flash consumes them. +* If the guest programs a controller dummy-cycle register, the controller + converts those cycles to dummy byte transfers and the flash consumes them. +* The flash may know that a command requires dummy bytes, but it does not create + transfers on the SSI bus. + +Do not implement controller-side snooping that watches manual-mode opcode +streams and injects extra dummy transfers based on flash opcodes. That mixes +flash command semantics into the controller and is fragile when flash models +gain correct dummy-byte accounting. + +Examples in the tree +-------------------- + +The following models illustrate the boundary: + +* ``hw/block/m25p80.c`` keeps fast-read dummy requirements as byte counts in + ``needed_bytes``. Manufacturer-specific helpers convert datasheet dummy + cycles to the byte stream expected by the model. +* ``hw/ssi/aspeed_smc.c`` generates dummy byte transfers for direct fast-read + mode from controller registers, but manual user-mode writes are forwarded as + guest-provided bytes. +* ``hw/ssi/npcm7xx_fiu.c`` converts the direct-read dummy configuration to the + number of dummy byte transfers sent before reading data. + +Review checklist +---------------- + +When adding or changing a SPI flash controller or flash model, check: + +* Are dummy counts stored in byte units when they drive flash state machines? +* If a hardware register stores cycles, is the conversion to bytes based on the + bus width of the dummy phase? +* Are manual guest-provided dummy bytes forwarded without extra injection? +* Are automatic controller-generated dummy phases modeled by the controller? +* Is flash-specific opcode knowledge kept in the flash model rather than copied + into controller snooping paths? diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 92ef127c80..88d48a1f99 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -317,6 +317,18 @@ Arithmetic pass 0 to *nh* to make a simple zero-extension of *nl*, so overflow should never occur. + * - smax *t0*, *t1*, *t2* + + umax *t0*, *t1*, *t2* + + - | *t0* = MAX(*t1*, *t2*), for signed and unsigned integers. + + * - smin *t0*, *t1*, *t2* + + umin *t0*, *t1*, *t2* + + - | *t0* = MIN(*t1*, *t2*), for signed and unsigned integers. + Logical ------- @@ -495,6 +507,22 @@ Misc into 32-bit output *t0*. Depending on the host, this may be a simple shift, or may require additional canonicalization. + * - revbit8 *dest*, *t1* + + - | Reverse the 8 bits within each byte of input *t1* with + | output in *dest*; the byte order is unchanged. + + * - revbit32 *dest*, *t1*, *flags* + + - | Reverse the 32 bits of the lower 32 bits of input *t1* + | with output in *dest*. On TCG_TYPE_I64, *flags* control + | any required sign or zero extension of the result in + | the same way as for bswap32. + | On TCG_TYPE_I32, *flags* should be zero. + + * - revbit64 *dest*, *t1* + + - | Reverse the 64 bits of input *t1* with output in *dest*. Conditional moves ----------------- diff --git a/docs/meson.build b/docs/meson.build index a8d893681e..4d8cc545fc 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -4,7 +4,7 @@ sphinx_build = find_program(fs.parent(python.full_path()) / 'sphinx-build', # Check if tools are available to build documentation. build_docs = false if sphinx_build.found() - SPHINX_ARGS = ['env', 'CONFDIR=' + qemu_confdir, sphinx_build, '-q', '-j', 'auto'] + SPHINX_ARGS = ['env', 'CONFDIR=' + qemu_confdir, sphinx_build, '-q'] # If we're making warnings fatal, apply this to Sphinx runs as well if get_option('werror') SPHINX_ARGS += [ '-W', '-Dkerneldoc_werror=1' ] diff --git a/docs/specs/index.rst b/docs/specs/index.rst index b7909a108a..3cdf242661 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -34,6 +34,7 @@ guest hardware that is specific to QEMU. virt-ctlr vmcoreinfo vmgenid + vmlaunchupdate rapl-msr rocker riscv-iommu diff --git a/docs/specs/vmlaunchupdate.rst b/docs/specs/vmlaunchupdate.rst new file mode 100644 index 0000000000..2f1e95afbf --- /dev/null +++ b/docs/specs/vmlaunchupdate.rst @@ -0,0 +1,198 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +VMLAUNCHUPDATE Interface Specification +###################################### + +Introduction +************ + +``VmLaunchUpdate`` is an extension to ``fw-cfg`` that allows guests to replace +boot state in their virtual machine using IGVM file container. Through a combination +of this ``fw-cfg`` hypervisor interface, an IGVM file containing specific directives +and with hypervisor stack knowledge, guests can deterministically replace the launch +state for guests. This is useful for environments like SEV-SNP where the +launch payload becomes the launch digest. Guests can use vm-launch-update device to +provide a measured, full guest payload (BIOS image, kernel, initramfs, kernel +command line) to the virtual machine which enables them to easily reason about +integrity of the resulting system. +It is also to be noted that this mechanism currently works only when the guest was +already started with an IGVM file defining its initial launch state. Subsequent +guest resets will use the launch state as defined in the guest provided IGVM file, +not the file with which the guest was initially started. If the guest was not started +with IGVM, writing a new bundle through the ``fw-cfg`` interface has no effect. + +For more information, please see the `KVM Forum 2024 presentation `__ +about this work. + + +.. _KVMFORUM: https://www.youtube.com/watch?v=VCMBxU6tAto + +Base Requirements +***************** + +#. **fw-cfg**: + The target system must provide a ``fw-cfg`` interface. For x86 based + environments, this ``fw-cfg`` interface must be accessible through PIO ports + 0x510 and 0x511. The ``fw-cfg`` interface does not need to be announced as part + of system device tables such as DSDT. The ``fw-cfg`` interface must support the + DMA interface. It may only support the DMA interface for write operations. + +#. **IGVM support**: + The hypervisor must provide support for parsing and executing the IGVM file bundle. + +#. **Confidential guests**: + For confidential guests, the hypervisor must support guest reset. Otherwise, the new + boot state provided through IGVM will not be applied. + +The Fw-cfg File +*************** + +Guests drive vmlaunchupdate through special ``fw-cfg`` files that control its flow +followed by a standard system reset operation. When the ``vm-launch-update`` device +is available, it provides the following ``fw-cfg`` file: + +* ``etc/vmlaunchupdate`` - It exposes a structure of the following type, all in + little-endian format: + +.. code-block:: c + :linenos: + + typedef struct { + uint16_t version; + uint16_t status; + + uint32_t _padding; + + uint64_t capabilities; + uint64_t control; + + uint64_t fw_image_addr; + uint64_t fw_image_size; + + uint64_t opaque_addr; + uint64_t opaque_size; + + } VMLaunchUpdate; + + +Currently, the ``version`` number (line 2 above) is initialized to the value ``1``. +Only IGVM files are supported at present. The ``capabilities`` (line 7) and ``control`` (line 8) both support +the following single value: + +* ``VM_LAUNCHUPDATE_FORMAT_IGVM`` + + This value is used by the hypervisor to indicate that only IGVM container files are supported. + This is set as a part of ``capabilities`` parameter (line 7) in the above structure. This same value + is passed by the guest to the hypervisor in the ``control`` parameter (line 8) in the above structure + to indicate that the guest passed IGVM file in memory to the hypervisor. The starting guest physical + address of the IGVM file in memory is specified in ``fw_image_addr`` and it's length is specified in + ``fw_image_size`` by the guest. If any other value is passed by the guest in the ``control`` parameter, + the write is ignored by the hypervisor. + +Following ``control`` parameters are supported: + +* ``VM_LAUNCHUPDATE_CTL_DISABLE`` + + This value is set in the ``control`` parameter by the guest in order to disable this ``fw-cfg`` + hypervisor interface from further updating the guest launch state with a new IGVM file. + +* ``VM_LAUNCHUPDATE_CTL_HOST_IGVM`` + + This value is set in the ``control`` parameter by the guest in order to send request to the + hypervisor to initialize the guest using the original host provided IGVM file. + It is useful if the guest wanted to update the UKIs present in the ESP and upon + reset, use one of the updated UKIs present there. If the guest passed addresses in memory + where its own IGVM file is loaded (see below) while also setting this control value, the next + reset will load the guest provided IGVM file and a subsequent second reset will restore the original + host IGVM. If the guest did not provide any addresses of its own IGVM (the address values are + cleared) while setting this control parameter, the immediate next guest reset will load the + original host provided IGVM file. + + The combination of the above two ctl interfaces work as + follows: + + A) ``CTL_HOST_IGVM`` = off ``CTL_DISABLE`` = off + + Supplied IGVM file replaces the firmware permanently. Updating the + firmware again is possible. + + B) ``CTL_HOST_IGVM`` = off ``CTL_DISABLE`` = on + + Supplied IGVM file replaces the firmware permanently. Updating the + firmware again is not possible. + + C) ``CTL_HOST_IGVM`` = on ``CTL_DISABLE`` = off + + Supplied IGVM file replaces the firmware for one reset. Resetting + again will switch back to the original firmware. Updating the + firmware again is possible. + + D) ``CTL_HOST_IGVM`` = on ``CTL_DISABLE`` = on + + Supplied IGVM file replaces the firmware for one reset. Resetting + again will switch back to the original firmware. Updating the + firmware again is NOT possible. + +``fw_image_addr`` (line 10) is the base guest physical address of the guest memory where the IGVM file of size +``fw_image_size`` (line 11) is loaded. ``opaque_addr`` (line 13) and ``opaque_size`` (line 14) are used by +the guest for passing data across resets. The contents of this guest memory are preserved across the +reset. For confidential guests, this memory region must come from guest shared unencrypted memory. + +``status`` (line 3) is written by the hypervisor and it indicates the result of the IGVM loading operation. +A success indicates status code 0. Otherwise a non-zero status code indicates failure. The nature of the +failure is indicated by the value of the code. + +Triggering the Launch State Update using IGVM +********************************************* + +To initiate the launch update process, the guest issues a standard system reset +operation through any of the means implemented by the machine model. + +On a write to the ``etc/vmlaunchupdate`` interface, the hypervisor evaluates whether this +hypervisor interface is disabled. If it is, it ignores any writes to this ``fw-cfg`` file +by the guest. No updates to initial launch state is performed. + +If the hypervisor interface is enabled, upon write to the ``etc/vmlaunchupdate`` interface, +the hypervisor parses the IGVM file bundle passed to it in memory, with starting guest physical +address at ``fw_image_addr`` and length ``fw_image_size``. If parsing is successful, it creates +a context handle to the IGVM file. If parsing and context loading is successful and there are no +errors, ``fw_image_addr`` and ``fw_image_size`` are cleared. The guest can check this in order +to determine if the IGVM was successfully parsed and the new context was loaded. If not, the +guest can throw error and abort rebooting to new IGVM boot state. Alternatively, the guest can +also check the ``status`` code from the ``fw-cfg`` file. A status code of 0 indicates success +of the operation. Non-zero status code indicates failure. Exact nature of the failure is +indicated by the value of the code. Currently, only two error values are supported: + +* ``VM_LAUNCHUPDATE_LOAD_FAIL`` - defined as value 1 and is set when loading of the IGVM file failed. +* ``VM_LAUNCHUPDATE_NOT_IGVM_INIT`` - defined as value 2 and is set when the guest was not started with + IGVM file. + +Upon guest reset, the hypervisor executes the IGVM bundle using +the context handle, setting the initial launch state of the guest accordingly. +If an invalid IGVM file is passed, parsing the file fails and the hypervisor ignores it +when ``fw-cfg`` files are written. In this case, the initial launch state +is not modified. If invalid addresses are passed, the hypervisor ignores them as well and no +new launch state is set. + +The launch state update mechanism works both for confidential and non-confidential +guests. In confidential guests, as a part of the reset operation, all existing +guest shared memory (shared with the hypervisor) as well as the guest memory region +starting with ``opaque_addr`` and length ``opaque_size`` are preserved. +The reset causes recreation of the VM context which triggers a fresh +measurement of the replaced BIOS region and reset CPU state. + +For non-confidential guests, there is no concept of guest private memory and all the existing +guest memory is preserved (this is the default behaviour today - QEMU does not reset/clear +guest memory upon reset). + +In both confidential and non-confidential cases, CPU and device state are reset to +the reset states specified in IGVM. In confidential environments, the guest +always resumes operation in the highest privileged mode available to it (VMPL0 in SEV-SNP). + +Closing Remarks +*************** +The exact content of the memory region specified by starting address ``opaque_addr`` +and length ``opaque_size`` is guest specific and is hypervisor agnostic. The hypervisor does +not care about the contents of this memory region. Therefore, it is not included in this +specification. As of writing this document, TDX guests on QEMU does not support IGVM. +Therefore, this mechanism cannot be used to change launch state of TDX guests. diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 9930974a50..cc42db9e0b 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -188,6 +188,7 @@ the following architecture extensions: - FEAT_SME_I16I64 (16-bit to 64-bit integer widening outer product instructions) - FEAT_SME_LUTv2 (Lookup table instructions with 4-bit indices and 8-bit elements) - FEAT_SME_MOP4 (Quarter-tile outer product instructions) +- FEAT_SME_TMOP (Structured sparsity outer product instructions) - FEAT_SSVE_AES (Streaming SVE Mode Advanced Encryption Standard and 128-bit polynomial multiply long instructions) - FEAT_SSVE_FEXPA (Streaming FEXPA instruction) - FEAT_SSVE_FP8DOT2 (SVE2 FP8 2-way dot product to half-precision instructions in Streaming SVE mode) diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst index e4827fb43a..51d36b2bc0 100644 --- a/docs/system/arm/nuvoton.rst +++ b/docs/system/arm/nuvoton.rst @@ -94,16 +94,60 @@ Boot options ------------ The Nuvoton machines can boot from an OpenBMC firmware image, or directly into -a kernel using the ``-kernel`` option. OpenBMC images for ``quanta-gsj`` and -possibly others can be downloaded from the OpenBMC jenkins : +a kernel using the ``-kernel`` option. OpenBMC machine names do not always +match QEMU machine names. Check the OpenBMC supported-machine list and Jenkins +for currently available source and pre-built images. + +Known OpenBMC (v2.18.0) target names for QEMU Nuvoton machines per: + + https://github.com/openbmc/openbmc/blob/2.18.0/meta-phosphor/docs/supported-machines.md + +.. list-table:: + :header-rows: 1 + + * - QEMU machine + - OpenBMC machine + * - ``npcm750-evb`` + - ``evb-npcm750`` + * - ``npcm845-evb`` + - ``evb-npcm845`` + * - ``quanta-gbs-bmc`` + - ``gbs`` + * - ``kudo-bmc`` + - ``kudo`` + * - ``mori-bmc`` + - ``mori`` + +As of June 2026, the latest OpenBMC release, ``2.18.0``, no longer lists a +``gsj`` machine. To build an image for QEMU's ``quanta-gsj`` machine, use an +older OpenBMC release that still contains ``meta-quanta/meta-gsj``. The +``2.14.0`` release contains the ``gsj`` machine: + + https://github.com/openbmc/openbmc/tree/2.14.0/meta-quanta/meta-gsj + +Some pre-built OpenBMC images for QEMU Nuvoton machines may be available on +Jenkins: https://jenkins.openbmc.org/ -The firmware image should be attached as an MTD drive. Example : +To find a pre-built MTD image on Jenkins, start from the Jenkins home page and +open the ``latest-master`` job. Select the matrix configuration whose +``target`` matches the OpenBMC machine name, for example +``label=docker-builder,target=gbs``, then open its latest successful build's +artifacts. The MTD image is usually published under +``openbmc/build/tmp/deploy/images//`` as +``obmc-phosphor-image--.static.mtd``. If Jenkins does not +list a matching target, or the build artifacts do not include an MTD image, +there is no current pre-built MTD image for that machine. + +The firmware image should be attached as an MTD drive. Example: .. code-block:: bash - $ qemu-system-arm -machine quanta-gsj -nographic \ - -drive file=image-bmc,if=mtd,bus=0,unit=0,format=raw + $ qemu-system-arm -machine quanta-gbs-bmc -nographic \ + -drive file=obmc-phosphor-image-gbs-xxxxxx.static.mtd,if=mtd,bus=0,unit=0,format=raw The default root password for test images is usually ``0penBmc``. + +For other machines that don't have pre-built images on Jenkins, build an image +from source by following the OpenBMC build documentation. diff --git a/docs/system/i386/hyperv.rst b/docs/system/i386/hyperv.rst index 1c1de77feb..e1babffb0f 100644 --- a/docs/system/i386/hyperv.rst +++ b/docs/system/i386/hyperv.rst @@ -71,8 +71,11 @@ Existing enlightenments by the guest when it crashes, HV_X64_MSR_CRASH_P0..HV_X64_MSR_CRASH_P5 MSRs contain additional crash information. This information is outputted in QEMU log and through QAPI. - Note: unlike under genuine Hyper-V, write to HV_X64_MSR_CRASH_CTL causes guest - to shutdown. This effectively blocks crash dump generation by Windows. + Note: unlike under genuine Hyper-V, write to HV_X64_MSR_CRASH_CTL triggers + ``qemu_system_guest_panicked()`` via ``KVM_SYSTEM_EVENT_CRASH`` and the + resulting action depends on the ``-action panic=...`` policy (default: + ``shutdown``). With the default action, this effectively blocks crash dump + generation by Windows. ``hv-time`` Enables two Hyper-V-specific clocksources available to the guest: MSR-based @@ -304,8 +307,9 @@ currently implemented Hyper-V enlightenments with the following exceptions: ``hv-version-id-snumber`` can be left unchanged, guests are not supposed to behave differently when different Hyper-V version is presented to them. - ``hv-crash`` must only be enabled if the crash information is consumed via - QAPI by higher levels of the virtualization stack. Enabling this feature - effectively prevents Windows from creating dumps upon crashes. + QAPI by higher levels of the virtualization stack. With the default + ``-action panic=shutdown`` policy, enabling this feature effectively + prevents Windows from creating dumps upon crashes. - ``hv-reenlightenment`` can only be used on hardware which supports TSC scaling or when guest migration is not needed. - ``hv-spinlocks`` should be set to e.g. 0xfff when host CPUs are overcommited diff --git a/docs/system/riscv/k230.rst b/docs/system/riscv/k230.rst index cea8202e55..237611eddf 100644 --- a/docs/system/riscv/k230.rst +++ b/docs/system/riscv/k230.rst @@ -20,12 +20,18 @@ The ``k230`` machine supports the following devices: * Platform-Level Interrupt Controller (PLIC) * 2 K230 Watchdog Timer * 5 UART +* K230 DDRC CFG and DDR PHY +* System Direct Memory Access (SDMA) +* GZIP Decompress Engine (Decomp_gzip) Boot options ------------ The ``k230`` machine supports K230 SDK boot through M-mode U-Boot, which then starts OpenSBI/Linux with ``bootm``. It also supports direct Linux boot. +The DDRC CFG and DDR PHY models allow the K230 SDK U-Boot SPL to complete DDR +initialization before loading the next boot stage. + K230 SDK Linux kernels use T-HEAD C9xx private MAEE page table attributes. QEMU does not implement MAEE in the generic RISC-V MMU, so such kernels need to be built with standard RISC-V PTE bits before they can boot under QEMU. diff --git a/docs/system/riscv/virt.rst b/docs/system/riscv/virt.rst index 60850970ce..3c87246891 100644 --- a/docs/system/riscv/virt.rst +++ b/docs/system/riscv/virt.rst @@ -16,7 +16,7 @@ The ``virt`` machine supports the following devices: * Core Local Interruptor (CLINT) * Platform-Level Interrupt Controller (PLIC) * CFI parallel NOR flash memory -* 1 NS16550 compatible UART +* Either 1 or 2 NS16550 compatible UARTs * 1 Google Goldfish RTC * 1 SiFive Test device * 8 virtio-mmio transport devices @@ -27,6 +27,9 @@ The hypervisor extension has been enabled for the default CPU, so virtual machines with hypervisor extension can simply be used without explicitly declaring. +The second UART only exists if a backend is configured explicitly (e.g. +with a second ``-serial`` command line option). + Hardware configuration information ---------------------------------- diff --git a/docs/system/security.rst b/docs/system/security.rst index 52bbf0cc7a..af626a4230 100644 --- a/docs/system/security.rst +++ b/docs/system/security.rst @@ -133,6 +133,16 @@ an issue as a normal bug. that affect the level 0 QEMU process. While these bugs should be fixed, they will not be triaged as security flaws at this time. +* **migration/snapshots**. Migration failures and snapshot load + failures are considered part of normal operation as long as the + source virtual machine and savevm file, respectively, are still + functional. Aborting the QEMU process at the migration/snapshot + destination is similarly not considered a security issue. The + migration stream is assumed to be secure as long as the design + principles described in the Architecture section are held, in + which case plain manipulation of the stream is not considered as + an attack vector. + * **low severity impact**. As a catch all rule, issues which are judged to have a "low" severity impact on the system will usually not justify handling as security bugs, nor assignment @@ -159,10 +169,11 @@ could allow malicious guests to gain code execution in QEMU. At this point the guest has escaped the virtual machine and is able to act in the context of the QEMU process on the host. -Guests often interact with other guests and share resources with them. A -malicious guest must not gain control of other guests or access their data. -Disk image files and network traffic must be protected from other guests unless -explicitly shared between them by the user. +Guests often interact with other guests and share resources with them. +A malicious guest must not gain control of other guests or access +their data. Disk image files and network traffic must be protected +from other guests, users and processes unless explicitly shared with +them by the user. Principle of Least Privilege '''''''''''''''''''''''''''' @@ -223,6 +234,9 @@ Some Linux distros already ship with UNIX groups for these devices by default. system calls that are not needed by QEMU, thereby reducing the host kernel attack surface. +- Transport Layer Security (TLS) protocol can be used to ensure authenticity and + encryption of the live migration connection where the network is untrusted. + Sensitive configurations ------------------------ diff --git a/hmp-commands.hx b/hmp-commands.hx index 7ae2468a3d..43ff220b5f 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -865,12 +865,13 @@ ERST .name = "nmi", .args_type = "", .params = "", - .help = "inject an NMI", + .help = "Inject an NMI, in a machine-specific way", .cmd = hmp_nmi, }, SRST -``nmi`` *cpu* - Inject an NMI on the default CPU (x86/s390) or all CPUs (ppc64). +``nmi`` + Inject an NMI, in a machine-specific way. + Not all machines implement NMI handling. ERST { @@ -928,16 +929,17 @@ ERST { .name = "migrate", - .args_type = "detach:-d,resume:-r,uri:s", - .params = "[-d] [-r] uri", + .args_type = "detach:-d,resume:-r,uri-cpr:-cs,uri:s", + .params = "[-d] [-r] [-c uri-cpr] uri", .help = "migrate to URI (using -d to not wait for completion)" - "\n\t\t\t -r to resume a paused postcopy migration", + "\n\t\t\t -r to resume a paused postcopy migration" + "\n\t\t\t -c to specify a CPR URI for cpr-transfer mode", .cmd = hmp_migrate, }, SRST -``migrate [-d] [-r]`` *uri* +``migrate [-d] [-r] [-c uri-cpr]`` *uri* Migrate the VM to *uri*. ``-d`` @@ -945,6 +947,9 @@ SRST query an ongoing migration process, use "info migrate". ``-r`` Resume a paused postcopy migration. + ``-c`` *uri-cpr* + Specify the CPR URI for cpr-transfer mode. It must be a UNIX domain + socket. ERST { diff --git a/host/include/aarch64/host/cpuinfo.h b/host/include/aarch64/host/cpuinfo.h index fe671534e4..c32c04dade 100644 --- a/host/include/aarch64/host/cpuinfo.h +++ b/host/include/aarch64/host/cpuinfo.h @@ -12,6 +12,7 @@ #define CPUINFO_AES (1u << 3) #define CPUINFO_PMULL (1u << 4) #define CPUINFO_BTI (1u << 5) +#define CPUINFO_CSSC (1u << 6) /* Initialized with a constructor. */ extern unsigned cpuinfo; diff --git a/host/include/riscv64/host/cpuinfo.h b/host/include/riscv64/host/cpuinfo.h index b2b53dbf62..1f047051a3 100644 --- a/host/include/riscv64/host/cpuinfo.h +++ b/host/include/riscv64/host/cpuinfo.h @@ -12,6 +12,7 @@ #define CPUINFO_ZBS (1u << 3) #define CPUINFO_ZICOND (1u << 4) #define CPUINFO_ZVE64X (1u << 5) +#define CPUINFO_ZBKB (1u << 6) /* Initialized with a constructor. */ extern unsigned cpuinfo; diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 3119f01117..4764d9db13 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -55,7 +55,8 @@ enum { P9ARRAY_DEFINE_TYPE(V9fsPath, v9fs_path_free); -static ssize_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...) +static ssize_t coroutine_fn +pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...) { ssize_t ret; va_list ap; @@ -67,7 +68,8 @@ static ssize_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...) return ret; } -static ssize_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...) +static ssize_t coroutine_fn +pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...) { ssize_t ret; va_list ap; @@ -1841,7 +1843,8 @@ out_nofid: pdu_complete(pdu, err); } -static int v9fs_walk_marshal(V9fsPDU *pdu, uint16_t nwnames, V9fsQID *qids) +static int coroutine_fn +v9fs_walk_marshal(V9fsPDU *pdu, uint16_t nwnames, V9fsQID *qids) { int i; ssize_t err; @@ -2181,8 +2184,8 @@ static void coroutine_fn v9fs_open(void *opaque) flags = omode_to_uflags(mode); } if (is_ro_export(&s->ctx)) { - if (mode & O_WRONLY || mode & O_RDWR || - mode & O_APPEND || mode & O_TRUNC) { + if (flags & O_WRONLY || flags & O_RDWR || + flags & O_APPEND || flags & O_TRUNC) { err = -EROFS; goto out; } @@ -2363,9 +2366,10 @@ out_nofid: * The resulting QEMUIOVector has heap-allocated iovecs and must be cleaned up * with qemu_iovec_destroy(). */ -static void v9fs_init_qiov_from_pdu(QEMUIOVector *qiov, V9fsPDU *pdu, - size_t skip, size_t size, - bool is_write) +static void coroutine_fn +v9fs_init_qiov_from_pdu(QEMUIOVector *qiov, V9fsPDU *pdu, + size_t skip, size_t size, + bool is_write) { QEMUIOVector elem; struct iovec *iov; @@ -2382,8 +2386,9 @@ static void v9fs_init_qiov_from_pdu(QEMUIOVector *qiov, V9fsPDU *pdu, qemu_iovec_concat(qiov, &elem, skip, size); } -static int v9fs_xattr_read(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp, - uint64_t off, uint32_t max_count) +static int coroutine_fn +v9fs_xattr_read(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp, + uint64_t off, uint32_t max_count) { ssize_t err; size_t offset = 7; @@ -2793,9 +2798,10 @@ out_nofid: pdu_complete(pdu, retval); } -static int v9fs_xattr_write(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp, - uint64_t off, uint32_t count, - struct iovec *sg, int cnt) +static int coroutine_fn +v9fs_xattr_write(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp, + uint64_t off, uint32_t count, + struct iovec *sg, int cnt) { int i, to_copy; ssize_t err = 0; @@ -3729,7 +3735,8 @@ out_nofid: pdu_complete(pdu, err); } -static int v9fs_fill_statfs(V9fsState *s, V9fsPDU *pdu, struct statfs *stbuf) +static int coroutine_fn +v9fs_fill_statfs(V9fsState *s, V9fsPDU *pdu, struct statfs *stbuf) { uint32_t f_type; uint32_t f_bsize; @@ -4530,6 +4537,7 @@ void v9fs_device_unrealize_common(V9fsState *s) qp_table_destroy(&s->qpp_table); qp_table_destroy(&s->qpf_table); g_free(s->ctx.fs_root); + s->transport = NULL; } typedef struct VirtfsCoResetData { diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index 1a309664f6..0e52ffbdf3 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h @@ -472,17 +472,21 @@ void pdu_submit(V9fsPDU *pdu, P9MsgHeader *hdr); void v9fs_reset(V9fsState *s); struct V9fsTransport { - ssize_t (*pdu_vmarshal)(V9fsPDU *pdu, size_t offset, const char *fmt, - va_list ap); - ssize_t (*pdu_vunmarshal)(V9fsPDU *pdu, size_t offset, const char *fmt, - va_list ap); - void (*init_in_iov_from_pdu)(V9fsPDU *pdu, struct iovec **piov, - unsigned int *pniov, size_t size); - void (*init_out_iov_from_pdu)(V9fsPDU *pdu, struct iovec **piov, - unsigned int *pniov, size_t size); - void (*push_and_notify)(V9fsPDU *pdu); - size_t (*msize_limit)(V9fsState *s); - size_t (*response_buffer_size)(V9fsPDU *pdu); + ssize_t coroutine_fn (*pdu_vmarshal)(V9fsPDU *pdu, size_t offset, + const char *fmt, va_list ap); + ssize_t coroutine_fn (*pdu_vunmarshal)(V9fsPDU *pdu, size_t offset, + const char *fmt, va_list ap); + void coroutine_fn (*init_in_iov_from_pdu)(V9fsPDU *pdu, + struct iovec **piov, + unsigned int *pniov, + size_t size); + void coroutine_fn (*init_out_iov_from_pdu)(V9fsPDU *pdu, + struct iovec **piov, + unsigned int *pniov, + size_t size); + void coroutine_fn (*push_and_notify)(V9fsPDU *pdu); + size_t coroutine_fn (*msize_limit)(V9fsState *s); + size_t coroutine_fn (*response_buffer_size)(V9fsPDU *pdu); }; #endif diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index 50dc93091d..2774fc2290 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -28,7 +28,7 @@ #include "qemu/module.h" #include "system/qtest.h" -static void virtio_9p_push_and_notify(V9fsPDU *pdu) +static void coroutine_fn virtio_9p_push_and_notify(V9fsPDU *pdu) { V9fsState *s = pdu->s; V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); @@ -117,8 +117,8 @@ static void virtio_9p_reset(VirtIODevice *vdev) v9fs_reset(&v->state); } -static ssize_t virtio_pdu_vmarshal(V9fsPDU *pdu, size_t offset, - const char *fmt, va_list ap) +static ssize_t coroutine_fn +virtio_pdu_vmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, va_list ap) { V9fsState *s = pdu->s; V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); @@ -135,8 +135,8 @@ static ssize_t virtio_pdu_vmarshal(V9fsPDU *pdu, size_t offset, return ret; } -static ssize_t virtio_pdu_vunmarshal(V9fsPDU *pdu, size_t offset, - const char *fmt, va_list ap) +static ssize_t coroutine_fn +virtio_pdu_vunmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, va_list ap) { V9fsState *s = pdu->s; V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); @@ -152,8 +152,9 @@ static ssize_t virtio_pdu_vunmarshal(V9fsPDU *pdu, size_t offset, return ret; } -static void virtio_init_in_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, - unsigned int *pniov, size_t size) +static void coroutine_fn +virtio_init_in_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, + unsigned int *pniov, size_t size) { V9fsState *s = pdu->s; V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); @@ -172,8 +173,9 @@ static void virtio_init_in_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, *pniov = elem->in_num; } -static void virtio_init_out_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, - unsigned int *pniov, size_t size) +static void coroutine_fn +virtio_init_out_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, + unsigned int *pniov, size_t size) { V9fsState *s = pdu->s; V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); @@ -192,13 +194,13 @@ static void virtio_init_out_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, *pniov = elem->out_num; } -static size_t virtio_9p_msize_limit(V9fsState *s) +static size_t coroutine_fn virtio_9p_msize_limit(V9fsState *s) { const size_t guestPageSize = 4096; return (VIRTQUEUE_MAX_SIZE - 2) * guestPageSize; } -static size_t virtio_9p_response_buffer_size(V9fsPDU *pdu) +static size_t coroutine_fn virtio_9p_response_buffer_size(V9fsPDU *pdu) { V9fsState *s = pdu->s; V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); @@ -243,6 +245,7 @@ static void virtio_9p_device_unrealize(DeviceState *dev) V9fsVirtioState *v = VIRTIO_9P(dev); V9fsState *s = &v->state; + v9fs_reset(s); /* cancel all in-flight PDUs to prevent UAF */ virtio_delete_queue(v->vq); virtio_cleanup(vdev); v9fs_device_unrealize_common(s); diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c index 24c90d97ec..06709d5408 100644 --- a/hw/9pfs/xen-9p-backend.c +++ b/hw/9pfs/xen-9p-backend.c @@ -68,6 +68,11 @@ typedef struct Xen9pfsDev { static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev); +static void xen_9pfs_disconnect_bh(void *opaque) +{ + xen_9pfs_disconnect(opaque); +} + static void xen_9pfs_in_sg(Xen9pfsRing *ring, struct iovec *in_sg, int *num, @@ -131,10 +136,10 @@ static void xen_9pfs_out_sg(Xen9pfsRing *ring, } } -static ssize_t xen_9pfs_pdu_vmarshal(V9fsPDU *pdu, - size_t offset, - const char *fmt, - va_list ap) +static ssize_t coroutine_fn xen_9pfs_pdu_vmarshal(V9fsPDU *pdu, + size_t offset, + const char *fmt, + va_list ap) { Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state); struct iovec in_sg[2]; @@ -150,15 +155,16 @@ static ssize_t xen_9pfs_pdu_vmarshal(V9fsPDU *pdu, "Failed to encode VirtFS reply type %d\n", pdu->id + 1); xen_be_set_state(&xen_9pfs->xendev, XenbusStateClosing); - xen_9pfs_disconnect(&xen_9pfs->xendev); + aio_bh_schedule_oneshot(qemu_get_aio_context(), + xen_9pfs_disconnect_bh, &xen_9pfs->xendev); } return ret; } -static ssize_t xen_9pfs_pdu_vunmarshal(V9fsPDU *pdu, - size_t offset, - const char *fmt, - va_list ap) +static ssize_t coroutine_fn xen_9pfs_pdu_vunmarshal(V9fsPDU *pdu, + size_t offset, + const char *fmt, + va_list ap) { Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state); struct iovec out_sg[2]; @@ -173,15 +179,16 @@ static ssize_t xen_9pfs_pdu_vunmarshal(V9fsPDU *pdu, xen_pv_printf(&xen_9pfs->xendev, 0, "Failed to decode VirtFS request type %d\n", pdu->id); xen_be_set_state(&xen_9pfs->xendev, XenbusStateClosing); - xen_9pfs_disconnect(&xen_9pfs->xendev); + aio_bh_schedule_oneshot(qemu_get_aio_context(), + xen_9pfs_disconnect_bh, &xen_9pfs->xendev); } return ret; } -static void xen_9pfs_init_out_iov_from_pdu(V9fsPDU *pdu, - struct iovec **piov, - unsigned int *pniov, - size_t size) +static void coroutine_fn xen_9pfs_init_out_iov_from_pdu(V9fsPDU *pdu, + struct iovec **piov, + unsigned int *pniov, + size_t size) { Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state); Xen9pfsRing *ring = &xen_9pfs->rings[pdu->tag % xen_9pfs->num_rings]; @@ -195,10 +202,10 @@ static void xen_9pfs_init_out_iov_from_pdu(V9fsPDU *pdu, *pniov = num; } -static void xen_9pfs_init_in_iov_from_pdu(V9fsPDU *pdu, - struct iovec **piov, - unsigned int *pniov, - size_t size) +static void coroutine_fn xen_9pfs_init_in_iov_from_pdu(V9fsPDU *pdu, + struct iovec **piov, + unsigned int *pniov, + size_t size) { Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state); Xen9pfsRing *ring = &xen_9pfs->rings[pdu->tag % xen_9pfs->num_rings]; @@ -227,7 +234,7 @@ again: *pniov = num; } -static void xen_9pfs_push_and_notify(V9fsPDU *pdu) +static void coroutine_fn xen_9pfs_push_and_notify(V9fsPDU *pdu) { RING_IDX prod; Xen9pfsDev *priv = container_of(pdu->s, Xen9pfsDev, state); @@ -368,10 +375,16 @@ static void xen_9pfs_evtchn_event(void *opaque) static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev) { Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev); + V9fsState *s = &xen_9pdev->state; int i; trace_xen_9pfs_disconnect(xendev->name); + if (s->transport) { + v9fs_reset(s); /* cancel all in-flight PDUs to prevent UAF */ + v9fs_device_unrealize_common(s); + } + for (i = 0; i < xen_9pdev->num_rings; i++) { if (xen_9pdev->rings[i].evtchndev != NULL) { qemu_set_fd_handler(qemu_xen_evtchn_fd(xen_9pdev->rings[i].evtchndev), diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 82e0bc2e70..260d2f0751 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -533,7 +533,9 @@ config ASPEED_SOC bool default y depends on TCG && ARM + imply GENERIC_LOADER imply PCI_DEVICES + imply E1000E_PCI_EXPRESS select DS1338 select FTGMAC100 select I2C @@ -551,14 +553,17 @@ config ASPEED_SOC select TMP105 select TMP421 select EMC141X + select OR_IRQ select UNIMP select LED select PMBUS select MAX31785 + select ADC128D818 select FSI_APB2OPB_ASPEED select AT24C - select PCI_EXPRESS select PCI_EXPRESS_ASPEED + select USB_EHCI_SYSBUS + select SDHCI config MPS2 bool @@ -725,3 +730,18 @@ config ARMSSE select UNIMP select SSE_COUNTER select SSE_TIMER + +config AXIADO_SOC + bool + select ARM_GIC + select CADENCE # UART + select AXIADO_CLK + select CADENCE_GPIO + select AXIADO_SDHCI + select UNIMP + +config AXIADO_EVK + bool + default y + depends on TCG && ARM + select AXIADO_SOC diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index ddb210c895..55fbc2c290 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -409,7 +409,7 @@ static const ARMSSEDeviceInfo sse300_devices[] = { .name = "s32kwatchdog", .type = TYPE_CMSDK_APB_WATCHDOG, .index = 0, - .addr = 0x4802e000, + .addr = 0x5802e000, .ppc = NO_PPC, .irq = NMI_0, .slowclk = true, @@ -452,7 +452,7 @@ static const ARMSSEDeviceInfo sse300_devices[] = { .name = "CPU0CORE_PPU", .type = TYPE_UNIMPLEMENTED_DEVICE, .index = 2, - .addr = 0x50023000, + .addr = 0x58023000, .size = 0x1000, .ppc = NO_PPC, .irq = NO_IRQ, @@ -461,7 +461,7 @@ static const ARMSSEDeviceInfo sse300_devices[] = { .name = "MGMT_PPU", .type = TYPE_UNIMPLEMENTED_DEVICE, .index = 3, - .addr = 0x50028000, + .addr = 0x58028000, .size = 0x1000, .ppc = NO_PPC, .irq = NO_IRQ, @@ -470,7 +470,7 @@ static const ARMSSEDeviceInfo sse300_devices[] = { .name = "DEBUG_PPU", .type = TYPE_UNIMPLEMENTED_DEVICE, .index = 4, - .addr = 0x50029000, + .addr = 0x58029000, .size = 0x1000, .ppc = NO_PPC, .irq = NO_IRQ, diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index a48c442058..8fa16b3831 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -24,10 +24,6 @@ #include "hw/core/qdev-clock.h" #include "system/system.h" -static struct arm_boot_info aspeed_board_binfo = { - .board_id = -1, /* device-tree-only board */ -}; - #define AST_SMP_MAILBOX_BASE 0x1e6e2180 #define AST_SMP_MBOX_FIELD_ENTRY (AST_SMP_MAILBOX_BASE + 0x0) #define AST_SMP_MBOX_FIELD_GOSIGN (AST_SMP_MAILBOX_BASE + 0x4) @@ -206,13 +202,14 @@ static void aspeed_machine_init(MachineState *machine) memory_region_add_subregion(get_system_memory(), AST_SMP_MAILBOX_BASE, smpboot); - aspeed_board_binfo.write_secondary_boot = aspeed_write_smpboot; - aspeed_board_binfo.secondary_cpu_reset_hook = aspeed_reset_secondary; - aspeed_board_binfo.smp_loader_start = AST_SMP_MBOX_CODE; + bmc->bootinfo.write_secondary_boot = aspeed_write_smpboot; + bmc->bootinfo.secondary_cpu_reset_hook = aspeed_reset_secondary; + bmc->bootinfo.smp_loader_start = AST_SMP_MBOX_CODE; } - aspeed_board_binfo.ram_size = machine->ram_size; - aspeed_board_binfo.loader_start = sc->memmap[ASPEED_DEV_SDRAM]; + bmc->bootinfo.board_id = -1; /* device-tree-only board */ + bmc->bootinfo.ram_size = machine->ram_size; + bmc->bootinfo.loader_start = sc->memmap[ASPEED_DEV_SDRAM]; if (amc->i2c_init) { amc->i2c_init(bmc); @@ -248,7 +245,7 @@ static void aspeed_machine_init(MachineState *machine) aspeed_load_vbootrom(bmc->soc, bios_name, &error_abort); } - arm_load_kernel(ARM_CPU(first_cpu), machine, &aspeed_board_binfo); + arm_load_kernel(ARM_CPU(first_cpu), machine, &bmc->bootinfo); } void aspeed_create_pca9552(AspeedSoCState *soc, int bus_id, int addr) @@ -327,7 +324,7 @@ static void aspeed_set_bmc_console(Object *obj, const char *value, Error **errp) int uart_first = aspeed_uart_first(sc->uarts_base); int uart_last = aspeed_uart_last(sc->uarts_base, sc->uarts_num); - if (sscanf(value, "uart%u", &val) != 1) { + if (sscanf(value, "uart%d", &val) != 1 || val < 0) { error_setg(errp, "Bad value for \"uart\" property"); return; } diff --git a/hw/arm/aspeed_ast2600_anacapa.c b/hw/arm/aspeed_ast2600_anacapa.c index a1c8111a93..65d6b0faa2 100644 --- a/hw/arm/aspeed_ast2600_anacapa.c +++ b/hw/arm/aspeed_ast2600_anacapa.c @@ -1,13 +1,14 @@ /* * Facebook Anacapa * - * Copyright (c) Meta Platforms, Inc. and affiliates. + * Copyright (c) 2026 Meta Platforms, Inc. and affiliates. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" #include "qapi/error.h" +#include "hw/sensor/adc128d818.h" #include "hw/arm/machines-qom.h" #include "hw/arm/aspeed.h" #include "hw/arm/aspeed_soc.h" @@ -15,7 +16,6 @@ #include "hw/gpio/pca9552.h" #include "hw/nvram/eeprom_at24c.h" -/* Anacapa hardware value */ #define ANACAPA_BMC_HW_STRAP1 0x00002002 #define ANACAPA_BMC_HW_STRAP2 0x00000000 #define ANACAPA_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB) @@ -221,6 +221,17 @@ static const uint8_t hpm_brd_id_eeprom[] = { }; static const size_t hpm_brd_id_eeprom_len = sizeof(hpm_brd_id_eeprom); +static void anacapa_add_adc128d818(I2CBus *bus, uint8_t addr, + const char *description) +{ + DeviceState *dev = DEVICE(i2c_slave_new(TYPE_ADC128D818, addr)); + g_autofree char *childname = g_strdup_printf("0x%02x", addr); + + qdev_prop_set_string(dev, "description", description); + object_property_add_child(OBJECT(bus), childname, OBJECT(dev)); + i2c_slave_realize_and_unref(I2C_SLAVE(dev), bus, &error_fatal); +} + static void anacapa_bmc_i2c_init(AspeedMachineState *bmc) { /* Reference: aspeed-bmc-facebook-anacapa.dts */ @@ -242,7 +253,7 @@ static void anacapa_bmc_i2c_init(AspeedMachineState *bmc) /* &i2c1 */ /* eeprom@50 */ at24c_eeprom_init(i2c[1], 0x50, 256 * KiB); - /* i2c-mux@70 (PCA9546) — 4 channels, empty */ + /* i2c-mux@70 (PCA9546) - 4 channels, empty */ i2c_slave_create_simple(i2c[1], TYPE_PCA9546, 0x70); /* &i2c4 */ @@ -259,7 +270,8 @@ static void anacapa_bmc_i2c_init(AspeedMachineState *bmc) i2c_mux = i2c_slave_create_simple(i2c[8], TYPE_PCA9546, 0x72); /* i2c8mux ch0 */ - /* adc128d818@1f — no model */ + /* adc128d818@1f - R-PDB ADC (mode 1: 8 voltage channels) */ + anacapa_add_adc128d818(pca954x_i2c_get_bus(i2c_mux, 0), 0x1f, "i2c8:0:1f"); /* pca9555@22 */ i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 0), TYPE_PCA9552, 0x22); @@ -305,7 +317,7 @@ static void anacapa_bmc_i2c_init(AspeedMachineState *bmc) /* i2c-mux@71 (PCA9548) */ i2c_mux = i2c_slave_create_simple(i2c[11], TYPE_PCA9548, 0x71); - /* i2c11mux ch0-ch4 — empty */ + /* i2c11mux ch0-ch4 - empty */ /* i2c11mux ch5 */ /* pca9555@22 */ @@ -320,7 +332,8 @@ static void anacapa_bmc_i2c_init(AspeedMachineState *bmc) i2c_mux = i2c_slave_create_simple(i2c[13], TYPE_PCA9548, 0x70); /* i2c13mux ch3 */ - /* adc128d818@1f - no model */ + /* adc128d818@1f - MB ADC (mode 1: 8 voltage channels) */ + anacapa_add_adc128d818(pca954x_i2c_get_bus(i2c_mux, 3), 0x1f, "i2c13:3:1f"); /* i2c13mux ch4 */ /* eeprom@51 */ @@ -328,7 +341,7 @@ static void anacapa_bmc_i2c_init(AspeedMachineState *bmc) hpm_brd_id_eeprom, hpm_brd_id_eeprom_len); /* i2c13mux ch7 */ - /* nfc@28 — no model */ + /* nfc@28 - no model */ } static void aspeed_machine_anacapa_class_init(ObjectClass *oc, diff --git a/hw/arm/aspeed_ast2600_catalina.c b/hw/arm/aspeed_ast2600_catalina.c index 65495a524e..f714c9d1b3 100644 --- a/hw/arm/aspeed_ast2600_catalina.c +++ b/hw/arm/aspeed_ast2600_catalina.c @@ -472,7 +472,16 @@ static void catalina_bmc_i2c_init(AspeedMachineState *bmc) /* &i2c0 */ /* i2c-mux@71 (PCA9546) on i2c0 */ - i2c_slave_create_simple(i2c[0], TYPE_PCA9546, 0x71); + i2c_mux = i2c_slave_create_simple(i2c[0], TYPE_PCA9546, 0x71); + + /* i2c0mux0ch0 */ + /* IOB0 NIC0 temperature-sensor@1f - tmp421 */ + i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 0), + TYPE_TMP421, 0x1f); + /* i2c0mux0ch2 */ + /* IOB0 NIC1 temperature-sensor@1f - tmp421 */ + i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 2), + TYPE_TMP421, 0x1f); /* i2c-mux@72 (PCA9546) on i2c0 */ i2c_mux = i2c_slave_create_simple(i2c[0], TYPE_PCA9546, 0x72); @@ -489,7 +498,16 @@ static void catalina_bmc_i2c_init(AspeedMachineState *bmc) i2c_slave_create_simple(i2c[0], TYPE_PCA9546, 0x73); /* i2c-mux@75 (PCA9546) on i2c0 */ - i2c_slave_create_simple(i2c[0], TYPE_PCA9546, 0x75); + i2c_mux = i2c_slave_create_simple(i2c[0], TYPE_PCA9546, 0x75); + + /* i2c0mux3ch0 */ + /* IOB1 NIC0 temperature-sensor@1f - tmp421 */ + i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 0), + TYPE_TMP421, 0x1f); + /* i2c0mux3ch2 */ + /* IOB1 NIC1 temperature-sensor@1f - tmp421 */ + i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 2), + TYPE_TMP421, 0x1f); /* i2c-mux@76 (PCA9546) on i2c0 */ i2c_mux = i2c_slave_create_simple(i2c[0], TYPE_PCA9546, 0x76); @@ -533,7 +551,7 @@ static void catalina_bmc_i2c_init(AspeedMachineState *bmc) TYPE_PCA9554, 0x27); /* io_expander6 - pca9555@25 */ i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 6), - TYPE_PCA9552, 0x25); + TYPE_PCA9555, 0x25); /* eeprom@51 */ at24c_eeprom_init_rom(pca954x_i2c_get_bus(i2c_mux, 6), 0x51, 8 * KiB, osfp_eeprom, osfp_eeprom_len); @@ -544,14 +562,16 @@ static void catalina_bmc_i2c_init(AspeedMachineState *bmc) fio_eeprom, fio_eeprom_len); /* temperature-sensor@4b - tmp75 */ i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 7), TYPE_TMP75, 0x4b); + /* temperature-sensor@4f - tmp75 (FIO remote) */ + i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 7), TYPE_TMP75, 0x4f); /* &i2c2 */ /* io_expander0 - pca9555@20 */ - i2c_slave_create_simple(i2c[2], TYPE_PCA9552, 0x20); + i2c_slave_create_simple(i2c[2], TYPE_PCA9555, 0x20); /* io_expander0 - pca9555@21 */ - i2c_slave_create_simple(i2c[2], TYPE_PCA9552, 0x21); + i2c_slave_create_simple(i2c[2], TYPE_PCA9555, 0x21); /* io_expander0 - pca9555@27 */ - i2c_slave_create_simple(i2c[2], TYPE_PCA9552, 0x27); + i2c_slave_create_simple(i2c[2], TYPE_PCA9555, 0x27); /* eeprom@50 */ at24c_eeprom_init(i2c[2], 0x50, 8 * KiB); /* eeprom@51 */ @@ -564,21 +584,17 @@ static void catalina_bmc_i2c_init(AspeedMachineState *bmc) /* eeprom@52 */ at24c_eeprom_init_rom(pca954x_i2c_get_bus(i2c_mux, 6), 0x52, 8 * KiB, hdd_eeprom, hdd_eeprom_len); - /* i2c5mux0ch7 */ - /* ina230@40 - no model */ - /* ina230@41 - no model */ - /* ina230@44 - no model */ - /* ina230@45 - no model */ + /* i2c5mux0ch7 - empty */ /* &i2c6 */ /* io_expander3 - pca9555@21 */ - i2c_slave_create_simple(i2c[6], TYPE_PCA9552, 0x21); + i2c_slave_create_simple(i2c[6], TYPE_PCA9555, 0x21); /* rtc@6f - nct3018y */ i2c_slave_create_simple(i2c[6], TYPE_DS1338, 0x6f); /* &i2c9 */ /* io_expander4 - pca9555@4f */ - i2c_slave_create_simple(i2c[9], TYPE_PCA9552, 0x4f); + i2c_slave_create_simple(i2c[9], TYPE_PCA9555, 0x4f); /* temperature-sensor@4b - tpm75 */ i2c_slave_create_simple(i2c[9], TYPE_TMP75, 0x4b); /* eeprom@50 */ @@ -615,17 +631,17 @@ static void catalina_bmc_i2c_init(AspeedMachineState *bmc) /* &i2c14 */ /* io_expander9 - pca9555@10 */ - i2c_slave_create_simple(i2c[14], TYPE_PCA9552, 0x10); + i2c_slave_create_simple(i2c[14], TYPE_PCA9555, 0x10); /* io_expander10 - pca9555@11 */ - i2c_slave_create_simple(i2c[14], TYPE_PCA9552, 0x11); + i2c_slave_create_simple(i2c[14], TYPE_PCA9555, 0x11); /* io_expander11 - pca9555@12 */ - i2c_slave_create_simple(i2c[14], TYPE_PCA9552, 0x12); + i2c_slave_create_simple(i2c[14], TYPE_PCA9555, 0x12); /* io_expander12 - pca9555@13 */ - i2c_slave_create_simple(i2c[14], TYPE_PCA9552, 0x13); + i2c_slave_create_simple(i2c[14], TYPE_PCA9555, 0x13); /* io_expander13 - pca9555@14 */ - i2c_slave_create_simple(i2c[14], TYPE_PCA9552, 0x14); + i2c_slave_create_simple(i2c[14], TYPE_PCA9555, 0x14); /* io_expander14 - pca9555@15 */ - i2c_slave_create_simple(i2c[14], TYPE_PCA9552, 0x15); + i2c_slave_create_simple(i2c[14], TYPE_PCA9555, 0x15); /* &i2c15 */ /* temperature-sensor@1f - tmp421 */ diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index 7d9fade68d..ceeae336df 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -27,10 +27,6 @@ #define TYPE_AST2700FC MACHINE_TYPE_NAME("ast2700fc") OBJECT_DECLARE_SIMPLE_TYPE(Ast2700FCState, AST2700FC); -static struct arm_boot_info ast2700fc_board_info = { - .board_id = -1, /* device-tree-only board */ -}; - struct Ast2700FCState { MachineState parent_obj; @@ -46,6 +42,8 @@ struct Ast2700FCState { Aspeed27x0SoCState ca35; Aspeed27x0CoprocessorState ssp; Aspeed27x0CoprocessorState tsp; + + struct arm_boot_info bootinfo; }; #define AST2700FC_BMC_RAM_SIZE (2 * GiB) @@ -114,8 +112,9 @@ static bool ast2700fc_ca35_init(MachineState *machine, Error **errp) aspeed_board_init_flashes(&soc->fmc, AST2700FC_FMC_MODEL, 2, 0); aspeed_board_init_flashes(&soc->spi[0], AST2700FC_SPI_MODEL, 1, 2); - ast2700fc_board_info.ram_size = machine->ram_size; - ast2700fc_board_info.loader_start = sc->memmap[ASPEED_DEV_SDRAM]; + s->bootinfo.ram_size = machine->ram_size; + s->bootinfo.loader_start = sc->memmap[ASPEED_DEV_SDRAM]; + s->bootinfo.board_id = -1; /* device-tree-only board */ dev = ssi_get_cs(soc->fmc.spi, 0); fmc0 = dev ? m25p80_get_blk(dev) : NULL; @@ -129,16 +128,14 @@ static bool ast2700fc_ca35_init(MachineState *machine, Error **errp) bios_name = machine->firmware ?: VBOOTROM_FILE_NAME; aspeed_load_vbootrom(soc, bios_name, errp); - arm_load_kernel(ARM_CPU(first_cpu), machine, &ast2700fc_board_info); + arm_load_kernel(ARM_CPU(first_cpu), machine, &s->bootinfo); return true; } -static bool ast2700fc_ssp_init(MachineState *machine, Error **errp) +static bool ast2700fc_ssp_init(Ast2700FCState *s, AspeedSoCState *psp, + Error **errp) { - Ast2700FCState *s = AST2700FC(machine); - AspeedSoCState *psp = ASPEED_SOC(&s->ca35); - s->ssp_sysclk = clock_new(OBJECT(s), "SSP_SYSCLK"); clock_set_hz(s->ssp_sysclk, 200000000ULL); @@ -158,7 +155,11 @@ static bool ast2700fc_ssp_init(MachineState *machine, Error **errp) object_property_set_link(OBJECT(&s->ssp), "sram", OBJECT(&psp->sram), &error_abort); object_property_set_link(OBJECT(&s->ssp), "scu", - OBJECT(&psp->scu), &error_abort); + OBJECT(&s->ca35.scu), &error_abort); + object_property_set_link(OBJECT(&s->ssp), "scuio", + OBJECT(&psp->scuio), &error_abort); + object_property_set_link(OBJECT(&s->ssp), "fmc", + OBJECT(&psp->fmc), &error_abort); if (!qdev_realize(DEVICE(&s->ssp), NULL, errp)) { return false; } @@ -166,11 +167,9 @@ static bool ast2700fc_ssp_init(MachineState *machine, Error **errp) return true; } -static bool ast2700fc_tsp_init(MachineState *machine, Error **errp) +static bool ast2700fc_tsp_init(Ast2700FCState *s, AspeedSoCState *psp, + Error **errp) { - Ast2700FCState *s = AST2700FC(machine); - AspeedSoCState *psp = ASPEED_SOC(&s->ca35); - s->tsp_sysclk = clock_new(OBJECT(s), "TSP_SYSCLK"); clock_set_hz(s->tsp_sysclk, 200000000ULL); @@ -190,7 +189,11 @@ static bool ast2700fc_tsp_init(MachineState *machine, Error **errp) object_property_set_link(OBJECT(&s->tsp), "sram", OBJECT(&psp->sram), &error_abort); object_property_set_link(OBJECT(&s->tsp), "scu", - OBJECT(&psp->scu), &error_abort); + OBJECT(&s->ca35.scu), &error_abort); + object_property_set_link(OBJECT(&s->tsp), "scuio", + OBJECT(&psp->scuio), &error_abort); + object_property_set_link(OBJECT(&s->tsp), "fmc", + OBJECT(&psp->fmc), &error_abort); if (!qdev_realize(DEVICE(&s->tsp), NULL, errp)) { return false; } @@ -200,9 +203,19 @@ static bool ast2700fc_tsp_init(MachineState *machine, Error **errp) static void ast2700fc_init(MachineState *machine) { + Ast2700FCState *s = AST2700FC(machine); + AspeedSoCState *psp; + ast2700fc_ca35_init(machine, &error_abort); - ast2700fc_ssp_init(machine, &error_abort); - ast2700fc_tsp_init(machine, &error_abort); + + /* + * SSP and TSP use resources owned by the PSP SoC, such as UART, + * SRAM, SCU and SCUIO. Therefore the PSP SoC must be realized + * before the coprocessors are initialized. + */ + psp = ASPEED_SOC(&s->ca35); + ast2700fc_ssp_init(s, psp, &error_abort); + ast2700fc_tsp_init(s, psp, &error_abort); } static void ast2700fc_class_init(ObjectClass *oc, const void *data) diff --git a/hw/arm/aspeed_ast27x0-ssp.c b/hw/arm/aspeed_ast27x0-ssp.c index 68a8ab26f7..e03653086c 100644 --- a/hw/arm/aspeed_ast27x0-ssp.c +++ b/hw/arm/aspeed_ast27x0-ssp.c @@ -27,6 +27,7 @@ static const hwaddr aspeed_soc_ast27x0ssp_memmap[] = { [ASPEED_DEV_TIMER1] = 0x72C10000, [ASPEED_DEV_UART4] = 0x72C1A000, [ASPEED_DEV_IPC0] = 0x72C1C000, + [ASPEED_DEV_FMC] = 0x74000000, [ASPEED_DEV_PRIC1] = 0x74100000, [ASPEED_DEV_SCUIO] = 0x74C02000, [ASPEED_DEV_OTP] = 0x74C07000, @@ -142,8 +143,6 @@ static void aspeed_soc_ast27x0ssp_init(Object *obj) TYPE_UNIMPLEMENTED_DEVICE); object_initialize_child(obj, "ipc1", &a->ipc[1], TYPE_UNIMPLEMENTED_DEVICE); - object_initialize_child(obj, "scuio", &a->scuio, - TYPE_UNIMPLEMENTED_DEVICE); object_initialize_child(obj, "pric0", &a->pric[0], TYPE_UNIMPLEMENTED_DEVICE); object_initialize_child(obj, "pric1", &a->pric[1], @@ -167,6 +166,24 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) return; } + if (!a->scu) { + error_setg(errp, TYPE_ASPEED27X0SSP_COPROCESSOR + ": 'scu' link is not set"); + return; + } + + if (!a->scuio) { + error_setg(errp, TYPE_ASPEED27X0SSP_COPROCESSOR + ": 'scuio' link is not set"); + return; + } + + if (!a->fmc) { + error_setg(errp, TYPE_ASPEED27X0SSP_COPROCESSOR + ": 'fmc' link is not set"); + return; + } + /* AST27X0 SSP Core */ armv7m = DEVICE(&a->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 256); @@ -195,11 +212,18 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) &s->sram_alias); /* SCU */ - memory_region_init_alias(&s->scu_alias, OBJECT(s), "scu.alias", - &s->scu->iomem, 0, - memory_region_size(&s->scu->iomem)); + memory_region_init_alias(&a->scu_alias, OBJECT(a), "scu.alias", + &a->scu->parent_obj.iomem, 0, + memory_region_size(&a->scu->parent_obj.iomem)); memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SCU], - &s->scu_alias); + &a->scu_alias); + + /* SCUIO */ + memory_region_init_alias(&a->scuio_alias, OBJECT(a), "scuio.alias", + &a->scuio->iomem, 0, + memory_region_size(&a->scuio->iomem)); + memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SCUIO], + &a->scuio_alias); /* INTC */ if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc[0]), errp)) { @@ -252,6 +276,13 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(s->uart), 0, aspeed_soc_ast27x0ssp_get_irq(s, s->uart_dev)); + /* FMC */ + memory_region_init_alias(&a->fmc_alias, OBJECT(a), "fmc.alias", + &a->fmc->mmio, 0, + memory_region_size(&a->fmc->mmio)); + memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_FMC], + &a->fmc_alias); + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->timerctrl), "aspeed.timerctrl", sc->memmap[ASPEED_DEV_TIMER1], 0x200); @@ -261,9 +292,6 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&a->ipc[1]), "aspeed.ipc1", sc->memmap[ASPEED_DEV_IPC1], 0x1000); - aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&a->scuio), - "aspeed.scuio", - sc->memmap[ASPEED_DEV_SCUIO], 0x1000); aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&a->pric[0]), "aspeed.pric0", sc->memmap[ASPEED_DEV_PRIC0], 0x1000); @@ -275,6 +303,15 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) sc->memmap[ASPEED_DEV_OTP], 0x800); } +static const Property aspeed_27x0_coprocessor_properties[] = { + DEFINE_PROP_LINK("scu", Aspeed27x0CoprocessorState, scu, + TYPE_ASPEED_2700_SCU, Aspeed2700SCUState *), + DEFINE_PROP_LINK("scuio", Aspeed27x0CoprocessorState, scuio, + TYPE_ASPEED_SCU, AspeedSCUState *), + DEFINE_PROP_LINK("fmc", Aspeed27x0CoprocessorState, fmc, TYPE_ASPEED_SMC, + AspeedSMCState *), +}; + static void aspeed_soc_ast27x0ssp_class_init(ObjectClass *klass, const void *data) { @@ -288,6 +325,7 @@ static void aspeed_soc_ast27x0ssp_class_init(ObjectClass *klass, /* Reason: The Aspeed Coprocessor can only be instantiated from a board */ dc->user_creatable = false; dc->realize = aspeed_soc_ast27x0ssp_realize; + device_class_set_props(dc, aspeed_27x0_coprocessor_properties); sc->valid_cpu_types = valid_cpu_types; sc->irqmap = aspeed_soc_ast27x0ssp_irqmap; diff --git a/hw/arm/aspeed_ast27x0-tsp.c b/hw/arm/aspeed_ast27x0-tsp.c index b8a4f7c91d..39ba062a20 100644 --- a/hw/arm/aspeed_ast27x0-tsp.c +++ b/hw/arm/aspeed_ast27x0-tsp.c @@ -27,6 +27,7 @@ static const hwaddr aspeed_soc_ast27x0tsp_memmap[] = { [ASPEED_DEV_TIMER1] = 0x72C10000, [ASPEED_DEV_UART4] = 0x72C1A000, [ASPEED_DEV_IPC0] = 0x72C1C000, + [ASPEED_DEV_FMC] = 0x74000000, [ASPEED_DEV_PRIC1] = 0x74100000, [ASPEED_DEV_SCUIO] = 0x74C02000, [ASPEED_DEV_OTP] = 0x74C07000, @@ -142,8 +143,6 @@ static void aspeed_soc_ast27x0tsp_init(Object *obj) TYPE_UNIMPLEMENTED_DEVICE); object_initialize_child(obj, "ipc1", &a->ipc[1], TYPE_UNIMPLEMENTED_DEVICE); - object_initialize_child(obj, "scuio", &a->scuio, - TYPE_UNIMPLEMENTED_DEVICE); object_initialize_child(obj, "pric0", &a->pric[0], TYPE_UNIMPLEMENTED_DEVICE); object_initialize_child(obj, "pric1", &a->pric[1], @@ -167,6 +166,24 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) return; } + if (!a->scu) { + error_setg(errp, TYPE_ASPEED27X0TSP_COPROCESSOR + ": 'scu' link is not set"); + return; + } + + if (!a->scuio) { + error_setg(errp, TYPE_ASPEED27X0TSP_COPROCESSOR + ": 'scuio' link is not set"); + return; + } + + if (!a->fmc) { + error_setg(errp, TYPE_ASPEED27X0TSP_COPROCESSOR + ": 'fmc' link is not set"); + return; + } + /* AST27X0 TSP Core */ armv7m = DEVICE(&a->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 256); @@ -195,11 +212,18 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) &s->sram_alias); /* SCU */ - memory_region_init_alias(&s->scu_alias, OBJECT(s), "scu.alias", - &s->scu->iomem, 0, - memory_region_size(&s->scu->iomem)); + memory_region_init_alias(&a->scu_alias, OBJECT(a), "scu.alias", + &a->scu->parent_obj.iomem, 0, + memory_region_size(&a->scu->parent_obj.iomem)); memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SCU], - &s->scu_alias); + &a->scu_alias); + + /* SCUIO */ + memory_region_init_alias(&a->scuio_alias, OBJECT(a), "scuio.alias", + &a->scuio->iomem, 0, + memory_region_size(&a->scuio->iomem)); + memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SCUIO], + &a->scuio_alias); /* INTC */ if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc[0]), errp)) { @@ -252,6 +276,13 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(s->uart), 0, aspeed_soc_ast27x0tsp_get_irq(s, s->uart_dev)); + /* FMC */ + memory_region_init_alias(&a->fmc_alias, OBJECT(a), "fmc.alias", + &a->fmc->mmio, 0, + memory_region_size(&a->fmc->mmio)); + memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_FMC], + &a->fmc_alias); + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->timerctrl), "aspeed.timerctrl", sc->memmap[ASPEED_DEV_TIMER1], 0x200); @@ -261,9 +292,6 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&a->ipc[1]), "aspeed.ipc1", sc->memmap[ASPEED_DEV_IPC1], 0x1000); - aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&a->scuio), - "aspeed.scuio", - sc->memmap[ASPEED_DEV_SCUIO], 0x1000); aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&a->pric[0]), "aspeed.pric0", sc->memmap[ASPEED_DEV_PRIC0], 0x1000); @@ -275,6 +303,15 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) sc->memmap[ASPEED_DEV_OTP], 0x800); } +static const Property aspeed_27x0_coprocessor_properties[] = { + DEFINE_PROP_LINK("scu", Aspeed27x0CoprocessorState, scu, + TYPE_ASPEED_2700_SCU, Aspeed2700SCUState *), + DEFINE_PROP_LINK("scuio", Aspeed27x0CoprocessorState, scuio, + TYPE_ASPEED_SCU, AspeedSCUState *), + DEFINE_PROP_LINK("fmc", Aspeed27x0CoprocessorState, fmc, TYPE_ASPEED_SMC, + AspeedSMCState *), +}; + static void aspeed_soc_ast27x0tsp_class_init(ObjectClass *klass, const void *data) { @@ -288,6 +325,7 @@ static void aspeed_soc_ast27x0tsp_class_init(ObjectClass *klass, /* Reason: The Aspeed Coprocessor can only be instantiated from a board */ dc->user_creatable = false; dc->realize = aspeed_soc_ast27x0tsp_realize; + device_class_set_props(dc, aspeed_27x0_coprocessor_properties); sc->valid_cpu_types = valid_cpu_types; sc->irqmap = aspeed_soc_ast27x0tsp_irqmap; diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index dddd7d2106..6365dbd638 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -435,12 +435,12 @@ static void aspeed_soc_ast2700_init(Object *obj) object_initialize_child(obj, "gic", &a->gic, gicv3_class_name()); - object_initialize_child(obj, "scu", &s->scu, TYPE_ASPEED_2700_SCU); - qdev_prop_set_uint32(DEVICE(&s->scu), "silicon-rev", + object_initialize_child(obj, "scu", &a->scu, TYPE_ASPEED_2700_SCU); + qdev_prop_set_uint32(DEVICE(&a->scu), "silicon-rev", sc->silicon_rev); - object_property_add_alias(obj, "hw-strap1", OBJECT(&s->scu), + object_property_add_alias(obj, "hw-strap1", OBJECT(&a->scu), "hw-strap1"); - object_property_add_alias(obj, "hw-prot-key", OBJECT(&s->scu), + object_property_add_alias(obj, "hw-prot-key", OBJECT(&a->scu), "hw-prot-key"); object_initialize_child(obj, "scuio", &s->scuio, TYPE_ASPEED_2700_SCUIO); @@ -808,10 +808,10 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) sc->memmap[ASPEED_DEV_VBOOTROM], &s->vbootrom); /* SCU */ - if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { + if (!sysbus_realize(SYS_BUS_DEVICE(&a->scu), errp)) { return; } - aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->scu), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&a->scu), 0, sc->memmap[ASPEED_DEV_SCU]); /* SCU1 */ @@ -870,6 +870,11 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) /* EHCI */ for (i = 0; i < sc->ehcis_num; i++) { + object_property_set_int(OBJECT(&s->ehci[i]), "ctrldssegment-default", + sc->memmap[ASPEED_DEV_SDRAM] >> 32, + &error_abort); + object_property_set_bool(OBJECT(&s->ehci[i]), "caps-64bit-addr", true, + &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->ehci[i]), errp)) { return; } @@ -929,7 +934,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(&s->wdt[i]); hwaddr wdt_offset = sc->memmap[ASPEED_DEV_WDT] + i * awc->iosize; - object_property_set_link(OBJECT(&s->wdt[i]), "scu", OBJECT(&s->scu), + object_property_set_link(OBJECT(&s->wdt[i]), "scu", OBJECT(&a->scu), &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt[i]), errp)) { return; @@ -1032,7 +1037,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) aspeed_soc_ast2700_get_irq(s, ASPEED_DEV_EMMC)); /* Timer */ - object_property_set_link(OBJECT(&s->timerctrl), "scu", OBJECT(&s->scu), + object_property_set_link(OBJECT(&s->timerctrl), "scu", OBJECT(&a->scu), &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->timerctrl), errp)) { return; diff --git a/hw/arm/aspeed_coprocessor_common.c b/hw/arm/aspeed_coprocessor_common.c index a0a4c73d08..43026d2a55 100644 --- a/hw/arm/aspeed_coprocessor_common.c +++ b/hw/arm/aspeed_coprocessor_common.c @@ -27,8 +27,6 @@ static const Property aspeed_coprocessor_properties[] = { TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_LINK("sram", AspeedCoprocessorState, sram, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_LINK("scu", AspeedCoprocessorState, scu, TYPE_ASPEED_SCU, - AspeedSCUState *), DEFINE_PROP_LINK("uart", AspeedCoprocessorState, uart, TYPE_SERIAL_MM, SerialMM *), DEFINE_PROP_INT32("uart-dev", AspeedCoprocessorState, uart_dev, 0), diff --git a/hw/arm/ax3000-boards.c b/hw/arm/ax3000-boards.c new file mode 100644 index 0000000000..4b8deec15c --- /dev/null +++ b/hw/arm/ax3000-boards.c @@ -0,0 +1,56 @@ +/* + * Axiado Boards + * + * Author: Kuan-Jui Chiu + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/arm/ax3000-boards.h" +#include "hw/arm/boot.h" +#include "hw/arm/machines-qom.h" +#include "qemu/error-report.h" +#include "qom/object.h" + +static struct arm_boot_info ax3000_binfo = { + .loader_start = AX3000_DRAM0_BASE, + .board_id = -1, +}; + +static void ax3000_machine_init(MachineState *machine) +{ + Ax3000MachineState *ams = AX3000_MACHINE(machine); + + ams->soc = AX3000_SOC(object_new(TYPE_AX3000_SOC)); + object_property_add_child(OBJECT(machine), "soc", OBJECT(ams->soc)); + sysbus_realize_and_unref(SYS_BUS_DEVICE(ams->soc), &error_fatal); + + ax3000_binfo.ram_size = machine->ram_size; + arm_load_kernel(&ams->soc->cpu[0], machine, &ax3000_binfo); +} + +static void ax3000_machine_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->init = ax3000_machine_init; + mc->default_cpus = AX3000_NUM_CPUS; + mc->min_cpus = AX3000_NUM_CPUS; + mc->max_cpus = AX3000_NUM_CPUS; + mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a53"); +} + +static const TypeInfo ax3000_machine_types[] = { + { + .name = TYPE_AX3000_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(Ax3000MachineState), + .class_size = sizeof(Ax3000MachineClass), + .class_init = ax3000_machine_class_init, + .interfaces = aarch64_machine_interfaces, + .abstract = true, + } +}; + +DEFINE_TYPES(ax3000_machine_types) diff --git a/hw/arm/ax3000-evk.c b/hw/arm/ax3000-evk.c new file mode 100644 index 0000000000..a170848871 --- /dev/null +++ b/hw/arm/ax3000-evk.c @@ -0,0 +1,27 @@ +/* + * Axiado Evaluation Kit Emulation + * + * Author: Kuan-Jui Chiu + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/arm/ax3000-boards.h" + +static void axiado_scm3003_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Axiado SCM3003 EVK Board"; +} + +static const TypeInfo ax3000_evk_types[] = { + { + .name = MACHINE_TYPE_NAME("axiado-scm3003"), + .parent = TYPE_AX3000_MACHINE, + .class_init = axiado_scm3003_class_init, + } +}; + +DEFINE_TYPES(ax3000_evk_types) diff --git a/hw/arm/ax3000-soc.c b/hw/arm/ax3000-soc.c new file mode 100644 index 0000000000..ebe174fb97 --- /dev/null +++ b/hw/arm/ax3000-soc.c @@ -0,0 +1,243 @@ +/* + * Axiado SoC AX3000 + * + * Author: Kuan-Jui Chiu + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "system/address-spaces.h" +#include "hw/arm/bsa.h" +#include "hw/arm/ax3000-soc.h" +#include "hw/misc/unimp.h" +#include "system/system.h" +#include "qobject/qlist.h" +#include "qom/object.h" +#include "hw/core/boards.h" + +static void ax3000_init(Object *obj) +{ + Ax3000SoCState *s = AX3000_SOC(obj); + Ax3000SoCClass *sc = AX3000_SOC_GET_CLASS(s); + + for (int i = 0; i < sc->num_cpus; i++) { + g_autofree char *name = g_strdup_printf("cpu%d", i); + object_initialize_child(obj, name, &s->cpu[i], + ARM_CPU_TYPE_NAME("cortex-a53")); + } + + object_initialize_child(obj, "gic", &s->gic, gicv3_class_name()); + + for (int i = 0; i < AX3000_NUM_UARTS; i++) { + g_autofree char *name = g_strdup_printf("uart%d", i); + object_initialize_child(obj, name, &s->uart[i], TYPE_CADENCE_UART); + } + + object_initialize_child(obj, "clk", &s->ax3000_clk, TYPE_AX3000_CLK); + object_initialize_child(obj, "sdhci0", &s->sdhci0, TYPE_AXIADO_SDHCI); + + for (int i = 0; i < AX3000_NUM_GPIOS; i++) { + g_autofree char *name = g_strdup_printf("gpio%d", i); + object_initialize_child(obj, name, &s->gpio[i], TYPE_CADENCE_GPIO); + } +} + +static void ax3000_realize(DeviceState *dev, Error **errp) +{ + Ax3000SoCState *s = AX3000_SOC(dev); + Ax3000SoCClass *sc = AX3000_SOC_GET_CLASS(s); + SysBusDevice *gic_sbd = SYS_BUS_DEVICE(&s->gic); + DeviceState *gic_dev = DEVICE(&s->gic); + QList *redist_region_count; + SysBusDevice *sdhci0_sbd; + DeviceState *card; + DriveInfo *dinfo; + + /* CPUs */ + for (int i = 0; i < sc->num_cpus; i++) { + object_property_set_int(OBJECT(&s->cpu[i]), "cntfrq", 8000000, + &error_abort); + + if (object_property_find(OBJECT(&s->cpu[i]), "has_el3")) { + object_property_set_bool(OBJECT(&s->cpu[i]), "has_el3", + false, &error_abort); + } + + if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) { + return; + } + } + + /* GIC */ + qdev_prop_set_uint32(gic_dev, "num-cpu", sc->num_cpus); + qdev_prop_set_uint32(gic_dev, "num-irq", + AX3000_NUM_IRQS + GIC_INTERNAL); + + redist_region_count = qlist_new(); + qlist_append_int(redist_region_count, sc->num_cpus); + qdev_prop_set_array(gic_dev, "redist-region-count", redist_region_count); + + if (!sysbus_realize(gic_sbd, errp)) { + return; + } + + sysbus_mmio_map(gic_sbd, 0, AX3000_GIC_DIST_BASE); + sysbus_mmio_map(gic_sbd, 1, AX3000_GIC_REDIST_BASE); + + /* + * Mapping from the output timer irq lines from the CPU to the + * GIC PPI inputs. + */ + const int timer_irqs[] = { + [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ, + [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ, + [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ, + [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ + }; + + /* + * Wire the outputs from each CPU's generic timer and the GICv3 + * maintenance interrupt signal to the appropriate GIC PPI inputs, and + * the GIC's IRQ/FIQ interrupt outputs to the CPU's inputs. + */ + for (int i = 0; i < sc->num_cpus; i++) { + DeviceState *cpu_dev = DEVICE(&s->cpu[i]); + int intidbase = AX3000_NUM_IRQS + i * GIC_INTERNAL; + qemu_irq irq; + + for (int j = 0; j < ARRAY_SIZE(timer_irqs); j++) { + irq = qdev_get_gpio_in(gic_dev, intidbase + timer_irqs[j]); + qdev_connect_gpio_out(cpu_dev, j, irq); + } + + irq = qdev_get_gpio_in(gic_dev, intidbase + ARCH_GIC_MAINT_IRQ); + qdev_connect_gpio_out_named(cpu_dev, "gicv3-maintenance-interrupt", + 0, irq); + + sysbus_connect_irq(gic_sbd, i, + qdev_get_gpio_in(cpu_dev, ARM_CPU_IRQ)); + sysbus_connect_irq(gic_sbd, i + sc->num_cpus, + qdev_get_gpio_in(cpu_dev, ARM_CPU_FIQ)); + sysbus_connect_irq(gic_sbd, i + 2 * sc->num_cpus, + qdev_get_gpio_in(cpu_dev, ARM_CPU_VIRQ)); + sysbus_connect_irq(gic_sbd, i + 3 * sc->num_cpus, + qdev_get_gpio_in(cpu_dev, ARM_CPU_VFIQ)); + } + + /* DRAM */ + const struct { + hwaddr addr; + size_t size; + const char *name; + } dram_table[] = { + { AX3000_DRAM0_BASE, AX3000_DRAM0_SIZE, "dram0" }, + { AX3000_DRAM1_BASE, AX3000_DRAM1_SIZE, "dram1" } + }; + + for (int i = 0; i < AX3000_NUM_BANKS; i++) { + memory_region_init_ram(&s->dram[i], OBJECT(s), dram_table[i].name, + dram_table[i].size, &error_fatal); + memory_region_add_subregion(get_system_memory(), dram_table[i].addr, + &s->dram[i]); + } + + /* UARTs */ + const struct { + hwaddr addr; + unsigned int irq; + } serial_table[] = { + { AX3000_UART0_BASE, AX3000_UART0_IRQ }, + { AX3000_UART1_BASE, AX3000_UART1_IRQ }, + { AX3000_UART2_BASE, AX3000_UART2_IRQ }, + { AX3000_UART3_BASE, AX3000_UART3_IRQ } + }; + + for (int i = 0; i < AX3000_NUM_UARTS; i++) { + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, serial_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, + qdev_get_gpio_in(gic_dev, serial_table[i].irq)); + } + + /* Timer control */ + create_unimplemented_device("ax3000.timerctrl", AX3000_TIMER_CTRL, 32); + + /* Clock control */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->ax3000_clk), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ax3000_clk), 0, AX3000_PLL_BASE); + + /* SDHCI */ + sdhci0_sbd = SYS_BUS_DEVICE(&s->sdhci0); + if (!sysbus_realize(sdhci0_sbd, errp)) { + return; + } + + sysbus_mmio_map(sdhci0_sbd, 0, AX3000_SDHCI0_BASE); + sysbus_mmio_map(sdhci0_sbd, 1, AX3000_EMMC_PHY_BASE); + sysbus_connect_irq(sdhci0_sbd, 0, + qdev_get_gpio_in(gic_dev, AX3000_SDHCI0_IRQ)); + + dinfo = drive_get(IF_SD, 0, 0); + if (dinfo) { + card = qdev_new(TYPE_SD_CARD); + qdev_prop_set_drive_err(card, "drive", + blk_by_legacy_dinfo(dinfo), + &error_fatal); + qdev_realize_and_unref(card, s->sdhci0.sd_bus, &error_fatal); + } + + /* GPIOs */ + const struct { + hwaddr addr; + unsigned int irq; + } gpio_table[] = { + { AX3000_GPIO0_BASE, AX3000_GPIO0_IRQ }, + { AX3000_GPIO1_BASE, AX3000_GPIO1_IRQ }, + { AX3000_GPIO2_BASE, AX3000_GPIO2_IRQ }, + { AX3000_GPIO3_BASE, AX3000_GPIO3_IRQ }, + { AX3000_GPIO4_BASE, AX3000_GPIO4_IRQ }, + { AX3000_GPIO5_BASE, AX3000_GPIO5_IRQ }, + { AX3000_GPIO6_BASE, AX3000_GPIO6_IRQ }, + { AX3000_GPIO7_BASE, AX3000_GPIO7_IRQ } + }; + + for (int i = 0; i < AX3000_NUM_GPIOS; i++) { + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, gpio_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, + qdev_get_gpio_in(gic_dev, gpio_table[i].irq)); + } +} + +static void ax3000_class_init(ObjectClass *oc, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + Ax3000SoCClass *sc = AX3000_SOC_CLASS(oc); + + dc->desc = "Axiado SoC AX3000"; + dc->realize = ax3000_realize; + sc->num_cpus = AX3000_NUM_CPUS; +} + +static const TypeInfo axiado_soc_types[] = { + { + .name = TYPE_AX3000_SOC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Ax3000SoCState), + .instance_init = ax3000_init, + .class_init = ax3000_class_init, + .class_size = sizeof(Ax3000SoCClass), + } +}; + +DEFINE_TYPES(axiado_soc_types) diff --git a/hw/arm/bananapi_m2u.c b/hw/arm/bananapi_m2u.c index 9b468cd8ac..0079aa1dd2 100644 --- a/hw/arm/bananapi_m2u.c +++ b/hw/arm/bananapi_m2u.c @@ -29,7 +29,14 @@ #include "hw/arm/boot.h" #include "hw/arm/machines-qom.h" -static struct arm_boot_info bpim2u_binfo; +#define TYPE_BPIM2U_MACHINE MACHINE_TYPE_NAME("bpim2u") +OBJECT_DECLARE_SIMPLE_TYPE(Bpim2uMachineState, BPIM2U_MACHINE) + +struct Bpim2uMachineState { + MachineState parent; + + struct arm_boot_info bootinfo; +}; /* * R40 can boot from mmc0 and mmc2, and bpim2u has two mmc interface, one is @@ -62,6 +69,7 @@ static void mmc_attach_drive(AwR40State *s, AwSdHostState *mmc, int unit, static void bpim2u_init(MachineState *machine) { + Bpim2uMachineState *bpms = BPIM2U_MACHINE(machine); bool bootroom_loaded = false; AwR40State *r40; I2CBus *i2c; @@ -120,10 +128,10 @@ static void bpim2u_init(MachineState *machine) memory_region_add_subregion(get_system_memory(), r40->memmap[AW_R40_DEV_SDRAM], machine->ram); - bpim2u_binfo.loader_start = r40->memmap[AW_R40_DEV_SDRAM]; - bpim2u_binfo.ram_size = machine->ram_size; - bpim2u_binfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; - arm_load_kernel(&r40->cpus[0], machine, &bpim2u_binfo); + bpms->bootinfo.loader_start = r40->memmap[AW_R40_DEV_SDRAM]; + bpms->bootinfo.ram_size = machine->ram_size; + bpms->bootinfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; + arm_load_kernel(&r40->cpus[0], machine, &bpms->bootinfo); } static void bpim2u_machine_init(MachineClass *mc) @@ -145,4 +153,5 @@ static void bpim2u_machine_init(MachineClass *mc) mc->auto_create_sdcard = true; } -DEFINE_MACHINE_ARM("bpim2u", bpim2u_machine_init) +DEFINE_MACHINE_EXTENDED("bpim2u", MACHINE, Bpim2uMachineState, + bpim2u_machine_init, false, arm_machine_interfaces) diff --git a/hw/arm/collie.c b/hw/arm/collie.c index 91f0a94b6f..01d74ad38c 100644 --- a/hw/arm/collie.c +++ b/hw/arm/collie.c @@ -30,16 +30,12 @@ struct CollieMachineState { MachineState parent; StrongARMState *sa1110; + struct arm_boot_info bootinfo; }; #define TYPE_COLLIE_MACHINE MACHINE_TYPE_NAME("collie") OBJECT_DECLARE_SIMPLE_TYPE(CollieMachineState, COLLIE_MACHINE) -static struct arm_boot_info collie_binfo = { - .loader_start = SA_SDCS0, - .ram_size = RAM_SIZE, -}; - static void collie_init(MachineState *machine) { MachineClass *mc = MACHINE_GET_CLASS(machine); @@ -66,8 +62,10 @@ static void collie_init(MachineState *machine) sysbus_create_simple("scoop", 0x40800000, NULL); - collie_binfo.board_id = 0x208; - arm_load_kernel(cms->sa1110->cpu, machine, &collie_binfo); + cms->bootinfo.loader_start = SA_SDCS0; + cms->bootinfo.ram_size = RAM_SIZE; + cms->bootinfo.board_id = 0x208; + arm_load_kernel(cms->sa1110->cpu, machine, &cms->bootinfo); } static void collie_machine_class_init(ObjectClass *oc, const void *data) diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c index a643ae4e27..d59c1ebba7 100644 --- a/hw/arm/cubieboard.c +++ b/hw/arm/cubieboard.c @@ -25,13 +25,18 @@ #include "hw/arm/machines-qom.h" #include "hw/i2c/i2c.h" -static struct arm_boot_info cubieboard_binfo = { - .loader_start = AW_A10_SDRAM_BASE, - .board_id = 0x1008, +#define TYPE_CUBIEBOARD_MACHINE MACHINE_TYPE_NAME("cubieboard") +OBJECT_DECLARE_SIMPLE_TYPE(CubieboardMachineState, CUBIEBOARD_MACHINE) + +struct CubieboardMachineState { + MachineState parent; + + struct arm_boot_info bootinfo; }; static void cubieboard_init(MachineState *machine) { + CubieboardMachineState *cbs = CUBIEBOARD_MACHINE(machine); AwA10State *a10; Error *err = NULL; DriveInfo *di; @@ -103,8 +108,10 @@ static void cubieboard_init(MachineState *machine) } /* TODO create and connect IDE devices for ide_drive_get() */ - cubieboard_binfo.ram_size = machine->ram_size; - arm_load_kernel(&a10->cpu, machine, &cubieboard_binfo); + cbs->bootinfo.loader_start = AW_A10_SDRAM_BASE; + cbs->bootinfo.board_id = 0x1008; + cbs->bootinfo.ram_size = machine->ram_size; + arm_load_kernel(&a10->cpu, machine, &cbs->bootinfo); } static void cubieboard_machine_init(MachineClass *mc) @@ -126,4 +133,6 @@ static void cubieboard_machine_init(MachineClass *mc) mc->auto_create_sdcard = true; } -DEFINE_MACHINE_ARM("cubieboard", cubieboard_machine_init) +DEFINE_MACHINE_EXTENDED("cubieboard", MACHINE, CubieboardMachineState, + cubieboard_machine_init, false, + arm_machine_interfaces) diff --git a/hw/arm/exynos4_boards.c b/hw/arm/exynos4_boards.c index 1b8c9b618f..b7decdb2c7 100644 --- a/hw/arm/exynos4_boards.c +++ b/hw/arm/exynos4_boards.c @@ -49,6 +49,7 @@ typedef struct Exynos4BoardState { Exynos4210State soc; MemoryRegion dram0_mem; MemoryRegion dram1_mem; + struct arm_boot_info bootinfo; } Exynos4BoardState; static int exynos4_board_id[EXYNOS4_NUM_OF_BOARDS] = { @@ -66,12 +67,6 @@ static unsigned long exynos4_board_ram_size[EXYNOS4_NUM_OF_BOARDS] = { [EXYNOS4_BOARD_SMDKC210] = 1 * GiB, }; -static struct arm_boot_info exynos4_board_binfo = { - .loader_start = EXYNOS4210_BASE_BOOT_ADDR, - .smp_loader_start = EXYNOS4210_SMP_BOOT_ADDR, - .write_secondary_boot = exynos4210_write_secondary, -}; - static void lan9215_init(uint32_t base, qemu_irq irq) { DeviceState *dev; @@ -113,14 +108,15 @@ static Exynos4BoardState * exynos4_boards_init_common(MachineState *machine, Exynos4BoardType board_type) { - Exynos4BoardState *s = g_new(Exynos4BoardState, 1); + Exynos4BoardState *s = g_new0(Exynos4BoardState, 1); - exynos4_board_binfo.ram_size = exynos4_board_ram_size[board_type]; - exynos4_board_binfo.board_id = exynos4_board_id[board_type]; - exynos4_board_binfo.smp_bootreg_addr = - exynos4_board_smp_bootreg_addr[board_type]; - exynos4_board_binfo.gic_cpu_if_addr = - EXYNOS4210_SMP_PRIVATE_BASE_ADDR + 0x100; + s->bootinfo.loader_start = EXYNOS4210_BASE_BOOT_ADDR; + s->bootinfo.smp_loader_start = EXYNOS4210_SMP_BOOT_ADDR; + s->bootinfo.write_secondary_boot = exynos4210_write_secondary; + s->bootinfo.ram_size = exynos4_board_ram_size[board_type]; + s->bootinfo.board_id = exynos4_board_id[board_type]; + s->bootinfo.smp_bootreg_addr = exynos4_board_smp_bootreg_addr[board_type]; + s->bootinfo.gic_cpu_if_addr = EXYNOS4210_SMP_PRIVATE_BASE_ADDR + 0x100; exynos4_boards_init_ram(s, get_system_memory(), exynos4_board_ram_size[board_type]); @@ -137,7 +133,7 @@ static void nuri_init(MachineState *machine) Exynos4BoardState *s = exynos4_boards_init_common(machine, EXYNOS4_BOARD_NURI); - arm_load_kernel(s->soc.cpu[0], machine, &exynos4_board_binfo); + arm_load_kernel(s->soc.cpu[0], machine, &s->bootinfo); } static void smdkc210_init(MachineState *machine) @@ -147,7 +143,7 @@ static void smdkc210_init(MachineState *machine) lan9215_init(SMDK_LAN9118_BASE_ADDR, qemu_irq_invert(s->soc.irq_table[exynos4210_get_irq(37, 1)])); - arm_load_kernel(s->soc.cpu[0], machine, &exynos4_board_binfo); + arm_load_kernel(s->soc.cpu[0], machine, &s->bootinfo); } static const char * const valid_cpu_types[] = { diff --git a/hw/arm/imx25_pdk.c b/hw/arm/imx25_pdk.c index 7ebd6c8eb9..f7a51481af 100644 --- a/hw/arm/imx25_pdk.c +++ b/hw/arm/imx25_pdk.c @@ -61,10 +61,9 @@ typedef struct IMX25PDK { FslIMX25State soc; MemoryRegion ram_alias; + struct arm_boot_info bootinfo; } IMX25PDK; -static struct arm_boot_info imx25_pdk_binfo; - static void imx25_pdk_init(MachineState *machine) { IMX25PDK *s = g_new0(IMX25PDK, 1); @@ -114,9 +113,9 @@ static void imx25_pdk_init(MachineState *machine) alias_offset += ram[i].size; } - imx25_pdk_binfo.ram_size = machine->ram_size; - imx25_pdk_binfo.loader_start = FSL_IMX25_SDRAM0_ADDR; - imx25_pdk_binfo.board_id = 1771; + s->bootinfo.ram_size = machine->ram_size; + s->bootinfo.loader_start = FSL_IMX25_SDRAM0_ADDR; + s->bootinfo.board_id = 1771; for (i = 0; i < FSL_IMX25_NUM_ESDHCS; i++) { BusState *bus; @@ -138,7 +137,7 @@ static void imx25_pdk_init(MachineState *machine) * fail. */ if (!qtest_enabled()) { - arm_load_kernel(&s->soc.cpu, machine, &imx25_pdk_binfo); + arm_load_kernel(&s->soc.cpu, machine, &s->bootinfo); } } diff --git a/hw/arm/imx8mm-evk.c b/hw/arm/imx8mm-evk.c index 6b7774d3e2..019c609c54 100644 --- a/hw/arm/imx8mm-evk.c +++ b/hw/arm/imx8mm-evk.c @@ -20,6 +20,15 @@ #include "qapi/error.h" #include +#define TYPE_IMX8MM_EVK_MACHINE MACHINE_TYPE_NAME("imx8mm-evk") +OBJECT_DECLARE_SIMPLE_TYPE(Imx8mmEvkMachineState, IMX8MM_EVK_MACHINE) + +struct Imx8mmEvkMachineState { + MachineState parent; + + struct arm_boot_info bootinfo; +}; + static void imx8mm_evk_modify_dtb(const struct arm_boot_info *info, void *fdt) { int i, offset; @@ -60,7 +69,7 @@ static void imx8mm_evk_modify_dtb(const struct arm_boot_info *info, void *fdt) static void imx8mm_evk_init(MachineState *machine) { - static struct arm_boot_info boot_info; + Imx8mmEvkMachineState *ims = IMX8MM_EVK_MACHINE(machine); FslImx8mmState *s; if (machine->ram_size > FSL_IMX8MM_RAM_SIZE_MAX) { @@ -69,13 +78,11 @@ static void imx8mm_evk_init(MachineState *machine) exit(1); } - boot_info = (struct arm_boot_info) { - .loader_start = FSL_IMX8MM_RAM_START, - .board_id = -1, - .ram_size = machine->ram_size, - .psci_conduit = QEMU_PSCI_CONDUIT_SMC, - .modify_dtb = imx8mm_evk_modify_dtb, - }; + ims->bootinfo.loader_start = FSL_IMX8MM_RAM_START; + ims->bootinfo.board_id = -1; + ims->bootinfo.ram_size = machine->ram_size; + ims->bootinfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; + ims->bootinfo.modify_dtb = imx8mm_evk_modify_dtb; s = FSL_IMX8MM(object_new_with_props(TYPE_FSL_IMX8MM, OBJECT(machine), "soc", &error_fatal, NULL)); @@ -103,7 +110,7 @@ static void imx8mm_evk_init(MachineState *machine) } if (!qtest_enabled()) { - arm_load_kernel(&s->cpu[0], machine, &boot_info); + arm_load_kernel(&s->cpu[0], machine, &ims->bootinfo); } } @@ -127,4 +134,6 @@ static void imx8mm_evk_machine_init(MachineClass *mc) mc->get_default_cpu_type = imx8mm_evk_get_default_cpu_type; } -DEFINE_MACHINE_AARCH64("imx8mm-evk", imx8mm_evk_machine_init) +DEFINE_MACHINE_EXTENDED("imx8mm-evk", MACHINE, Imx8mmEvkMachineState, + imx8mm_evk_machine_init, false, + aarch64_machine_interfaces) diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index c25bbf3c82..f8bcf42c61 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -581,13 +581,19 @@ static void icp_control_init(Object *obj) /* Board init. */ -static struct arm_boot_info integrator_binfo = { - .loader_start = 0x0, - .board_id = 0x113, +#define TYPE_INTEGRATORCP_MACHINE MACHINE_TYPE_NAME("integratorcp") +OBJECT_DECLARE_SIMPLE_TYPE(IntegratorcpMachineState, + INTEGRATORCP_MACHINE) + +struct IntegratorcpMachineState { + MachineState parent; + + struct arm_boot_info bootinfo; }; static void integratorcp_init(MachineState *machine) { + IntegratorcpMachineState *icms = INTEGRATORCP_MACHINE(machine); ram_addr_t ram_size = machine->ram_size; Object *cpuobj; ARMCPU *cpu; @@ -680,8 +686,10 @@ static void integratorcp_init(MachineState *machine) sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xc0000000); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[22]); - integrator_binfo.ram_size = ram_size; - arm_load_kernel(cpu, machine, &integrator_binfo); + icms->bootinfo.loader_start = 0x0; + icms->bootinfo.board_id = 0x113; + icms->bootinfo.ram_size = ram_size; + arm_load_kernel(cpu, machine, &icms->bootinfo); } static void integratorcp_machine_init(MachineClass *mc) @@ -696,7 +704,9 @@ static void integratorcp_machine_init(MachineClass *mc) machine_add_audiodev_property(mc); } -DEFINE_MACHINE_ARM("integratorcp", integratorcp_machine_init) +DEFINE_MACHINE_EXTENDED("integratorcp", MACHINE, IntegratorcpMachineState, + integratorcp_machine_init, false, + arm_machine_interfaces) static const Property core_properties[] = { DEFINE_PROP_UINT32("memsz", IntegratorCMState, memsz, 0), diff --git a/hw/arm/kzm.c b/hw/arm/kzm.c index 168ecf35d0..1aa5b49de9 100644 --- a/hw/arm/kzm.c +++ b/hw/arm/kzm.c @@ -54,17 +54,13 @@ typedef struct IMX31KZM { FslIMX31State soc; MemoryRegion ram_alias; + struct arm_boot_info bootinfo; } IMX31KZM; #define KZM_RAM_ADDR (FSL_IMX31_SDRAM0_ADDR) #define KZM_FPGA_ADDR (FSL_IMX31_CS4_ADDR + 0x1040) #define KZM_LAN9118_ADDR (FSL_IMX31_CS5_ADDR) -static struct arm_boot_info kzm_binfo = { - .loader_start = KZM_RAM_ADDR, - .board_id = 1722, -}; - static void kzm_init(MachineState *machine) { IMX31KZM *s = g_new0(IMX31KZM, 1); @@ -125,10 +121,12 @@ static void kzm_init(MachineState *machine) 14745600, serial_hd(2), DEVICE_NATIVE_ENDIAN); } - kzm_binfo.ram_size = machine->ram_size; + s->bootinfo.loader_start = KZM_RAM_ADDR; + s->bootinfo.board_id = 1722; + s->bootinfo.ram_size = machine->ram_size; if (!qtest_enabled()) { - arm_load_kernel(&s->soc.cpu, machine, &kzm_binfo); + arm_load_kernel(&s->soc.cpu, machine, &s->bootinfo); } } diff --git a/hw/arm/mcimx7d-sabre.c b/hw/arm/mcimx7d-sabre.c index a0ac647c3d..22fe1fa932 100644 --- a/hw/arm/mcimx7d-sabre.c +++ b/hw/arm/mcimx7d-sabre.c @@ -22,9 +22,19 @@ #include "qemu/error-report.h" #include "system/qtest.h" +#define TYPE_MCIMX7D_SABRE_MACHINE MACHINE_TYPE_NAME("mcimx7d-sabre") +OBJECT_DECLARE_SIMPLE_TYPE(Mcimx7dSabreMachineState, + MCIMX7D_SABRE_MACHINE) + +struct Mcimx7dSabreMachineState { + MachineState parent; + + struct arm_boot_info bootinfo; +}; + static void mcimx7d_sabre_init(MachineState *machine) { - static struct arm_boot_info boot_info; + Mcimx7dSabreMachineState *msms = MCIMX7D_SABRE_MACHINE(machine); FslIMX7State *s; int i; @@ -34,12 +44,10 @@ static void mcimx7d_sabre_init(MachineState *machine) exit(1); } - boot_info = (struct arm_boot_info) { - .loader_start = FSL_IMX7_MMDC_ADDR, - .board_id = -1, - .ram_size = machine->ram_size, - .psci_conduit = QEMU_PSCI_CONDUIT_SMC, - }; + msms->bootinfo.loader_start = FSL_IMX7_MMDC_ADDR; + msms->bootinfo.board_id = -1; + msms->bootinfo.ram_size = machine->ram_size; + msms->bootinfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; s = FSL_IMX7(object_new(TYPE_FSL_IMX7)); object_property_add_child(OBJECT(machine), "soc", OBJECT(s)); @@ -65,7 +73,7 @@ static void mcimx7d_sabre_init(MachineState *machine) } if (!qtest_enabled()) { - arm_load_kernel(&s->cpu[0], machine, &boot_info); + arm_load_kernel(&s->cpu[0], machine, &msms->bootinfo); } } @@ -78,4 +86,6 @@ static void mcimx7d_sabre_machine_init(MachineClass *mc) mc->auto_create_sdcard = true; } -DEFINE_MACHINE_ARM("mcimx7d-sabre", mcimx7d_sabre_machine_init) +DEFINE_MACHINE_EXTENDED("mcimx7d-sabre", MACHINE, Mcimx7dSabreMachineState, + mcimx7d_sabre_machine_init, false, + arm_machine_interfaces) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 4233a800be..8ee5307a91 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -110,6 +110,12 @@ arm_common_ss.add(when: 'CONFIG_SX1', if_true: files('omap_sx1.c')) arm_common_ss.add(when: 'CONFIG_VERSATILE', if_true: files('versatilepb.c')) arm_common_ss.add(when: 'CONFIG_VEXPRESS', if_true: files('vexpress.c')) +arm_common_ss.add(when: ['CONFIG_AXIADO_SOC', 'TARGET_AARCH64'], if_true: files( + 'ax3000-soc.c')) +arm_common_ss.add(when: ['CONFIG_AXIADO_EVK', 'TARGET_AARCH64'], if_true: files( + 'ax3000-boards.c', + 'ax3000-evk.c')) + arm_common_ss.add(files('boot.c')) hw_common_arch += {'arm': arm_common_ss} diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index f101c1b7c3..d6d178642b 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -1048,6 +1048,19 @@ static void mps2tz_common_init(MachineState *machine) const PPCInfo an547_ppcs[] = { { .name = "apb_ppcexp0", .ports = { + { /* port 0 USER MEM APB0 */ }, + { /* port 1 USER MEM APB0 */ }, + { /* port 2 reserved */ }, + { /* port 3 reserved */ }, + { /* port 4 NPU APB0 */ }, + { /* port 5 NPU APB1 */ }, + { /* port 6 reserved */ }, + { /* port 7 reserved */ }, + { /* port 8 reserved */ }, + { /* port 9 reserved */ }, + { /* port 10 reserved */ }, + { /* port 11 reserved */ }, + { /* port 12 reserved */ }, { "ssram-mpc", make_mpc, &mms->mpc[0], 0x57000000, 0x1000 }, { "qspi-mpc", make_mpc, &mms->mpc[1], 0x57001000, 0x1000 }, { "ddr-mpc", make_mpc, &mms->mpc[2], 0x57002000, 0x1000 }, @@ -1100,6 +1113,14 @@ static void mps2tz_common_init(MachineState *machine) { /* port 7 USER AHB interface 3 */ }, { "eth-usb", make_eth_usb, NULL, 0x41400000, 0x200000, { 49 } }, }, + }, { + .name = "ahb_ppcexp1", + .ports = { + { /* port 0 reserved */ }, + { "dma1", make_dma, &mms->dma[1], 0x41201000, 0x1000, { 62, 60, 61 } }, + { "dma2", make_dma, &mms->dma[2], 0x41202000, 0x1000, { 65, 63, 64 } }, + { "dma3", make_dma, &mms->dma[3], 0x41203000, 0x1000, { 68, 66, 67 } }, + }, }, }; diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index 69e83bdd97..8a70ed7377 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -1200,13 +1200,18 @@ static const TypeInfo musicpal_key_info = { #define FLASH_SECTOR_SIZE (64 * KiB) -static struct arm_boot_info musicpal_binfo = { - .loader_start = 0x0, - .board_id = 0x20e, +#define TYPE_MUSICPAL_MACHINE MACHINE_TYPE_NAME("musicpal") +OBJECT_DECLARE_SIMPLE_TYPE(MusicPalMachineState, MUSICPAL_MACHINE) + +struct MusicPalMachineState { + MachineState parent; + + struct arm_boot_info bootinfo; }; static void musicpal_init(MachineState *machine) { + MusicPalMachineState *mpms = MUSICPAL_MACHINE(machine); ARMCPU *cpu; DeviceState *dev; DeviceState *pic; @@ -1341,8 +1346,10 @@ static void musicpal_init(MachineState *machine) sysbus_mmio_map(s, 0, MP_AUDIO_BASE); sysbus_connect_irq(s, 0, qdev_get_gpio_in(pic, MP_AUDIO_IRQ)); - musicpal_binfo.ram_size = MP_RAM_DEFAULT_SIZE; - arm_load_kernel(cpu, machine, &musicpal_binfo); + mpms->bootinfo.loader_start = 0x0; + mpms->bootinfo.board_id = 0x20e; + mpms->bootinfo.ram_size = MP_RAM_DEFAULT_SIZE; + arm_load_kernel(cpu, machine, &mpms->bootinfo); } static void musicpal_machine_init(MachineClass *mc) @@ -1357,7 +1364,9 @@ static void musicpal_machine_init(MachineClass *mc) machine_add_audiodev_property(mc); } -DEFINE_MACHINE_ARM("musicpal", musicpal_machine_init) +DEFINE_MACHINE_EXTENDED("musicpal", MACHINE, MusicPalMachineState, + musicpal_machine_init, false, + arm_machine_interfaces) static void mv88w8618_wlan_class_init(ObjectClass *klass, const void *data) { diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c index c27f149c04..695f30a0dc 100644 --- a/hw/arm/npcm7xx.c +++ b/hw/arm/npcm7xx.c @@ -364,22 +364,20 @@ static void npcm7xx_write_secondary_boot(ARMCPU *cpu, NPCM7XX_SMP_LOADER_START); } -static struct arm_boot_info npcm7xx_binfo = { - .loader_start = NPCM7XX_LOADER_START, - .smp_loader_start = NPCM7XX_SMP_LOADER_START, - .smp_bootreg_addr = NPCM7XX_SMP_BOOTREG_ADDR, - .gic_cpu_if_addr = NPCM7XX_GIC_CPU_IF_ADDR, - .write_secondary_boot = npcm7xx_write_secondary_boot, - .board_id = -1, - .board_setup_addr = NPCM7XX_BOARD_SETUP_ADDR, - .write_board_setup = npcm7xx_write_board_setup, -}; - -void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc) +void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc, + struct arm_boot_info *binfo) { - npcm7xx_binfo.ram_size = machine->ram_size; + binfo->loader_start = NPCM7XX_LOADER_START; + binfo->smp_loader_start = NPCM7XX_SMP_LOADER_START; + binfo->smp_bootreg_addr = NPCM7XX_SMP_BOOTREG_ADDR; + binfo->gic_cpu_if_addr = NPCM7XX_GIC_CPU_IF_ADDR; + binfo->write_secondary_boot = npcm7xx_write_secondary_boot; + binfo->board_id = -1; + binfo->board_setup_addr = NPCM7XX_BOARD_SETUP_ADDR; + binfo->write_board_setup = npcm7xx_write_board_setup; + binfo->ram_size = machine->ram_size; - arm_load_kernel(&soc->cpu[0], machine, &npcm7xx_binfo); + arm_load_kernel(&soc->cpu[0], machine, binfo); } static void npcm7xx_init_fuses(NPCM7xxState *s) diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c index 57a8d3186e..4f14d25746 100644 --- a/hw/arm/npcm7xx_boards.c +++ b/hw/arm/npcm7xx_boards.c @@ -374,7 +374,7 @@ static void npcm750_evb_init(MachineState *machine) npcm7xx_connect_flash(&soc->fiu[0], 0, "w25q256", drive_get(IF_MTD, 0, 0)); npcm750_evb_i2c_init(soc); npcm750_evb_fan_init(NPCM7XX_MACHINE(machine), soc); - npcm7xx_load_kernel(machine, soc); + npcm7xx_load_kernel(machine, soc, &NPCM7XX_MACHINE(machine)->bootinfo); } static void quanta_gsj_init(MachineState *machine) @@ -390,7 +390,7 @@ static void quanta_gsj_init(MachineState *machine) drive_get(IF_MTD, 0, 0)); quanta_gsj_i2c_init(soc); quanta_gsj_fan_init(NPCM7XX_MACHINE(machine), soc); - npcm7xx_load_kernel(machine, soc); + npcm7xx_load_kernel(machine, soc, &NPCM7XX_MACHINE(machine)->bootinfo); } static void quanta_gbs_init(MachineState *machine) @@ -408,7 +408,7 @@ static void quanta_gbs_init(MachineState *machine) quanta_gbs_i2c_init(soc); sdhci_attach_drive(&soc->mmc.sdhci, 0); - npcm7xx_load_kernel(machine, soc); + npcm7xx_load_kernel(machine, soc, &NPCM7XX_MACHINE(machine)->bootinfo); } static void kudo_bmc_init(MachineState *machine) @@ -427,7 +427,7 @@ static void kudo_bmc_init(MachineState *machine) kudo_bmc_i2c_init(soc); sdhci_attach_drive(&soc->mmc.sdhci, 0); - npcm7xx_load_kernel(machine, soc); + npcm7xx_load_kernel(machine, soc, &NPCM7XX_MACHINE(machine)->bootinfo); } static void mori_bmc_init(MachineState *machine) @@ -442,7 +442,7 @@ static void mori_bmc_init(MachineState *machine) npcm7xx_connect_flash(&soc->fiu[1], 0, "mx66u51235f", drive_get(IF_MTD, 3, 0)); - npcm7xx_load_kernel(machine, soc); + npcm7xx_load_kernel(machine, soc, &NPCM7XX_MACHINE(machine)->bootinfo); } static void npcm7xx_set_soc_type(NPCM7xxMachineClass *nmc, const char *type) diff --git a/hw/arm/npcm8xx.c b/hw/arm/npcm8xx.c index 9ce6ea52d9..7d707a09b4 100644 --- a/hw/arm/npcm8xx.c +++ b/hw/arm/npcm8xx.c @@ -357,22 +357,20 @@ static const struct { }, }; -static struct arm_boot_info npcm8xx_binfo = { - .loader_start = NPCM8XX_LOADER_START, - .smp_loader_start = NPCM8XX_SMP_LOADER_START, - .smp_bootreg_addr = NPCM8XX_SMP_BOOTREG_ADDR, - .gic_cpu_if_addr = NPCM8XX_GICC_BA, - .secure_boot = false, - .board_id = -1, - .board_setup_addr = NPCM8XX_BOARD_SETUP_ADDR, - .psci_conduit = QEMU_PSCI_CONDUIT_SMC, -}; - -void npcm8xx_load_kernel(MachineState *machine, NPCM8xxState *soc) +void npcm8xx_load_kernel(MachineState *machine, NPCM8xxState *soc, + struct arm_boot_info *binfo) { - npcm8xx_binfo.ram_size = machine->ram_size; + binfo->loader_start = NPCM8XX_LOADER_START; + binfo->smp_loader_start = NPCM8XX_SMP_LOADER_START; + binfo->smp_bootreg_addr = NPCM8XX_SMP_BOOTREG_ADDR; + binfo->gic_cpu_if_addr = NPCM8XX_GICC_BA; + binfo->secure_boot = false; + binfo->board_id = -1; + binfo->board_setup_addr = NPCM8XX_BOARD_SETUP_ADDR; + binfo->psci_conduit = QEMU_PSCI_CONDUIT_SMC; + binfo->ram_size = machine->ram_size; - arm_load_kernel(&soc->cpu[0], machine, &npcm8xx_binfo); + arm_load_kernel(&soc->cpu[0], machine, binfo); } static void npcm8xx_init_fuses(NPCM8xxState *s) diff --git a/hw/arm/npcm8xx_boards.c b/hw/arm/npcm8xx_boards.c index 042a928857..f462369bea 100644 --- a/hw/arm/npcm8xx_boards.c +++ b/hw/arm/npcm8xx_boards.c @@ -198,7 +198,7 @@ static void npcm845_evb_init(MachineState *machine) npcm8xx_connect_flash(&soc->fiu[0], 0, "w25q256", drive_get(IF_MTD, 0, 0)); npcm845_evb_i2c_init(soc); npcm845_evb_fan_init(NPCM8XX_MACHINE(machine), soc); - npcm8xx_load_kernel(machine, soc); + npcm8xx_load_kernel(machine, soc, &NPCM8XX_MACHINE(machine)->bootinfo); } static void npcm8xx_set_soc_type(NPCM8xxMachineClass *nmc, const char *type) diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index 44f9dd67c3..0f78cbd410 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -32,7 +32,7 @@ #include "hw/sd/sd.h" #include "system/blockdev.h" #include "system/system.h" -#include "hw/arm/soc_dma.h" +#include "hw/dma/soc_dma.h" #include "system/qtest.h" #include "system/reset.h" #include "system/runstate.h" @@ -3799,10 +3799,8 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *dram, s->port[tipb_mpui].addr_valid = omap_validate_tipb_mpui_addr; /* Register SDRAM and SRAM DMA ports for fast transfers. */ - soc_dma_port_add_mem(s->dma, memory_region_get_ram_ptr(dram), - OMAP_EMIFF_BASE, s->sdram_size); - soc_dma_port_add_mem(s->dma, memory_region_get_ram_ptr(&s->imif_ram), - OMAP_IMIF_BASE, s->sram_size); + soc_dma_port_add_mem(s->dma, OMAP_EMIFF_BASE, s->sdram_size); + soc_dma_port_add_mem(s->dma, OMAP_IMIF_BASE, s->sram_size); s->timer[0] = omap_mpu_timer_init(system_memory, 0xfffec500, qdev_get_gpio_in(s->ih[0], OMAP_INT_TIMER1), diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c index bcb7105323..3b628d13cb 100644 --- a/hw/arm/omap_sx1.c +++ b/hw/arm/omap_sx1.c @@ -91,14 +91,16 @@ static const MemoryRegionOps static_ops = { #define FLASH1_SIZE (8 * MiB) #define FLASH2_SIZE (32 * MiB) -static struct arm_boot_info sx1_binfo = { - .loader_start = OMAP_EMIFF_BASE, - .ram_size = SDRAM_SIZE, - .board_id = 0x265, -}; +typedef struct Sx1MachineState { + MachineState parent; + + struct arm_boot_info bootinfo; +} Sx1MachineState; static void sx1_init(MachineState *machine, const int version) { + /* Both sx1 and sx1-v1 embed the same state as their first member */ + Sx1MachineState *sms = (Sx1MachineState *)machine; struct omap_mpu_state_s *mpu; MachineClass *mc = MACHINE_GET_CLASS(machine); MemoryRegion *address_space = get_system_memory(); @@ -187,7 +189,10 @@ static void sx1_init(MachineState *machine, const int version) } /* Load the kernel. */ - arm_load_kernel(mpu->cpu, machine, &sx1_binfo); + sms->bootinfo.loader_start = OMAP_EMIFF_BASE; + sms->bootinfo.ram_size = SDRAM_SIZE; + sms->bootinfo.board_id = 0x265; + arm_load_kernel(mpu->cpu, machine, &sms->bootinfo); /* TODO: fix next line */ //~ qemu_console_resize(ds, 640, 480); @@ -220,6 +225,7 @@ static const TypeInfo sx1_machine_v2_type = { .name = MACHINE_TYPE_NAME("sx1"), .parent = TYPE_MACHINE, .class_init = sx1_machine_v2_class_init, + .instance_size = sizeof(Sx1MachineState), .interfaces = arm_machine_interfaces, }; @@ -240,6 +246,7 @@ static const TypeInfo sx1_machine_v1_type = { .name = MACHINE_TYPE_NAME("sx1-v1"), .parent = TYPE_MACHINE, .class_init = sx1_machine_v1_class_init, + .instance_size = sizeof(Sx1MachineState), .interfaces = arm_machine_interfaces, }; diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c index d84443bb7b..0e149ed6c9 100644 --- a/hw/arm/orangepi.c +++ b/hw/arm/orangepi.c @@ -28,10 +28,18 @@ #include "hw/arm/boot.h" #include "hw/arm/machines-qom.h" -static struct arm_boot_info orangepi_binfo; +#define TYPE_ORANGEPI_MACHINE MACHINE_TYPE_NAME("orangepi-pc") +OBJECT_DECLARE_SIMPLE_TYPE(OrangePiMachineState, ORANGEPI_MACHINE) + +struct OrangePiMachineState { + MachineState parent; + + struct arm_boot_info bootinfo; +}; static void orangepi_init(MachineState *machine) { + OrangePiMachineState *opms = ORANGEPI_MACHINE(machine); AwH3State *h3; DriveInfo *di; BlockBackend *blk; @@ -98,10 +106,10 @@ static void orangepi_init(MachineState *machine) /* Use Boot ROM to copy data from SD card to SRAM */ allwinner_h3_bootrom_setup(h3, blk); } - orangepi_binfo.loader_start = h3->memmap[AW_H3_DEV_SDRAM]; - orangepi_binfo.ram_size = machine->ram_size; - orangepi_binfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; - arm_load_kernel(&h3->cpus[0], machine, &orangepi_binfo); + opms->bootinfo.loader_start = h3->memmap[AW_H3_DEV_SDRAM]; + opms->bootinfo.ram_size = machine->ram_size; + opms->bootinfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; + arm_load_kernel(&h3->cpus[0], machine, &opms->bootinfo); } static void orangepi_machine_init(MachineClass *mc) @@ -125,4 +133,6 @@ static void orangepi_machine_init(MachineClass *mc) mc->auto_create_sdcard = true; } -DEFINE_MACHINE_ARM("orangepi-pc", orangepi_machine_init) +DEFINE_MACHINE_EXTENDED("orangepi-pc", MACHINE, OrangePiMachineState, + orangepi_machine_init, false, + arm_machine_interfaces) diff --git a/hw/arm/raspi4b.c b/hw/arm/raspi4b.c index 06aeb8db01..e1595a875f 100644 --- a/hw/arm/raspi4b.c +++ b/hw/arm/raspi4b.c @@ -85,7 +85,7 @@ static void raspi4_modify_dtb(const struct arm_boot_info *info, void *fdt) ram_size = board_ram_size(info->board_id); - if (info->ram_size > UPPER_RAM_BASE) { + if (ram_size > UPPER_RAM_BASE) { raspi_add_memory_node(fdt, UPPER_RAM_BASE, ram_size - UPPER_RAM_BASE); } } diff --git a/hw/arm/realview.c b/hw/arm/realview.c index 2b9f3271d6..0c2daf5c01 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -39,10 +39,11 @@ /* Board init. */ -static struct arm_boot_info realview_binfo = { - .smp_loader_start = SMP_BOOT_ADDR, - .smp_bootreg_addr = SMP_BOOTREG_ADDR, -}; +typedef struct RealViewMachineState { + MachineState parent; + + struct arm_boot_info bootinfo; +} RealViewMachineState; /* The following two lists must be consistent. */ enum realview_board_type { @@ -76,6 +77,8 @@ static void split_irq_from_named(DeviceState *src, const char* outname, static void realview_init(MachineState *machine, enum realview_board_type board_type) { + /* All realview-* machines embed the same state as their first member */ + RealViewMachineState *rvms = (RealViewMachineState *)machine; ARMCPU *cpu = NULL; CPUARMState *env; MemoryRegion *sysmem = get_system_memory(); @@ -202,7 +205,7 @@ static void realview_init(MachineState *machine, } sysbus_create_varargs("l2x0", periphbase + 0x2000, NULL); /* Both A9 and 11MPCore put the GIC CPU i/f at base + 0x100 */ - realview_binfo.gic_cpu_if_addr = periphbase + 0x100; + rvms->bootinfo.gic_cpu_if_addr = periphbase + 0x100; } else { uint32_t gic_addr = is_pb ? 0x1e000000 : 0x10040000; /* For now just create the nIRQ GIC, and ignore the others. */ @@ -387,10 +390,12 @@ static void realview_init(MachineState *machine, &error_fatal); memory_region_add_subregion(sysmem, SMP_BOOT_ADDR, ram_hack); - realview_binfo.ram_size = ram_size; - realview_binfo.board_id = realview_board_id[board_type]; - realview_binfo.loader_start = (board_type == BOARD_PB_A8 ? 0x70000000 : 0); - arm_load_kernel(cpu, machine, &realview_binfo); + rvms->bootinfo.smp_loader_start = SMP_BOOT_ADDR; + rvms->bootinfo.smp_bootreg_addr = SMP_BOOTREG_ADDR; + rvms->bootinfo.ram_size = ram_size; + rvms->bootinfo.board_id = realview_board_id[board_type]; + rvms->bootinfo.loader_start = board_type == BOARD_PB_A8 ? 0x70000000 : 0; + arm_load_kernel(cpu, machine, &rvms->bootinfo); } static void realview_eb_init(MachineState *machine) @@ -431,6 +436,7 @@ static const TypeInfo realview_eb_type = { .name = MACHINE_TYPE_NAME("realview-eb"), .parent = TYPE_MACHINE, .class_init = realview_eb_class_init, + .instance_size = sizeof(RealViewMachineState), .interfaces = arm_machine_interfaces, }; @@ -453,6 +459,7 @@ static const TypeInfo realview_eb_mpcore_type = { .name = MACHINE_TYPE_NAME("realview-eb-mpcore"), .parent = TYPE_MACHINE, .class_init = realview_eb_mpcore_class_init, + .instance_size = sizeof(RealViewMachineState), .interfaces = arm_machine_interfaces, }; @@ -473,6 +480,7 @@ static const TypeInfo realview_pb_a8_type = { .name = MACHINE_TYPE_NAME("realview-pb-a8"), .parent = TYPE_MACHINE, .class_init = realview_pb_a8_class_init, + .instance_size = sizeof(RealViewMachineState), .interfaces = arm_machine_interfaces, }; @@ -494,6 +502,7 @@ static const TypeInfo realview_pbx_a9_type = { .name = MACHINE_TYPE_NAME("realview-pbx-a9"), .parent = TYPE_MACHINE, .class_init = realview_pbx_a9_class_init, + .instance_size = sizeof(RealViewMachineState), .interfaces = arm_machine_interfaces, }; diff --git a/hw/arm/sabrelite.c b/hw/arm/sabrelite.c index 84bbc2f979..140b2ecb9e 100644 --- a/hw/arm/sabrelite.c +++ b/hw/arm/sabrelite.c @@ -25,18 +25,12 @@ struct SabreliteMachineState { FslIMX6State soc; CanBusState *canbus[FSL_IMX6_NUM_CANS]; + struct arm_boot_info bootinfo; }; #define TYPE_SABRELITE_MACHINE MACHINE_TYPE_NAME("sabrelite") OBJECT_DECLARE_SIMPLE_TYPE(SabreliteMachineState, SABRELITE_MACHINE) -static struct arm_boot_info sabrelite_binfo = { - /* DDR memory start */ - .loader_start = FSL_IMX6_MMDC_ADDR, - /* No board ID, we boot from DT tree */ - .board_id = -1, -}; - /* No need to do any particular setup for secondary boot */ static void sabrelite_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info) @@ -110,13 +104,17 @@ static void sabrelite_init(MachineState *machine) } } - sabrelite_binfo.ram_size = machine->ram_size; - sabrelite_binfo.secure_boot = true; - sabrelite_binfo.write_secondary_boot = sabrelite_write_secondary; - sabrelite_binfo.secondary_cpu_reset_hook = sabrelite_reset_secondary; + /* DDR memory start */ + s->bootinfo.loader_start = FSL_IMX6_MMDC_ADDR; + /* No board ID, we boot from DT tree */ + s->bootinfo.board_id = -1; + s->bootinfo.ram_size = machine->ram_size; + s->bootinfo.secure_boot = true; + s->bootinfo.write_secondary_boot = sabrelite_write_secondary; + s->bootinfo.secondary_cpu_reset_hook = sabrelite_reset_secondary; if (!qtest_enabled()) { - arm_load_kernel(&s->soc.cpu[0], machine, &sabrelite_binfo); + arm_load_kernel(&s->soc.cpu[0], machine, &s->bootinfo); } } diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c index c6991a52e6..520af79c80 100644 --- a/hw/arm/versatilepb.c +++ b/hw/arm/versatilepb.c @@ -182,10 +182,16 @@ static void vpb_sic_init(Object *obj) peripherals and expansion busses. For now we emulate a subset of the PB peripherals and just change the board ID. */ -static struct arm_boot_info versatile_binfo; +typedef struct VersatileMachineState { + MachineState parent; + + struct arm_boot_info bootinfo; +} VersatileMachineState; static void versatile_init(MachineState *machine, int board_id) { + /* versatilepb and versatileab embed the same state as first member */ + VersatileMachineState *vms = (VersatileMachineState *)machine; Object *cpuobj; ARMCPU *cpu; MemoryRegion *sysmem = get_system_memory(); @@ -397,9 +403,9 @@ static void versatile_init(MachineState *machine, int board_id) VERSATILE_FLASH_SECT_SIZE, 4, 0x0089, 0x0018, 0x0000, 0x0, 0); - versatile_binfo.ram_size = machine->ram_size; - versatile_binfo.board_id = board_id; - arm_load_kernel(cpu, machine, &versatile_binfo); + vms->bootinfo.ram_size = machine->ram_size; + vms->bootinfo.board_id = board_id; + arm_load_kernel(cpu, machine, &vms->bootinfo); } static void vpb_init(MachineState *machine) @@ -431,6 +437,7 @@ static const TypeInfo versatilepb_type = { .name = MACHINE_TYPE_NAME("versatilepb"), .parent = TYPE_MACHINE, .class_init = versatilepb_class_init, + .instance_size = sizeof(VersatileMachineState), .interfaces = arm_machine_interfaces, }; @@ -453,6 +460,7 @@ static const TypeInfo versatileab_type = { .name = MACHINE_TYPE_NAME("versatileab"), .parent = TYPE_MACHINE, .class_init = versatileab_class_init, + .instance_size = sizeof(VersatileMachineState), .interfaces = arm_machine_interfaces, }; diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 219597cabd..e7a56e37f7 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -4441,10 +4441,17 @@ static void machvirt_machine_init(void) } type_init(machvirt_machine_init); -static void virt_machine_11_1_options(MachineClass *mc) +static void virt_machine_11_2_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE_AS_LATEST(11, 1) +DEFINE_VIRT_MACHINE_AS_LATEST(11, 2) + +static void virt_machine_11_1_options(MachineClass *mc) +{ + virt_machine_11_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_11_1, hw_compat_11_1_len); +} +DEFINE_VIRT_MACHINE(11, 1) static void virt_machine_11_0_options(MachineClass *mc) { diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 6c83439017..f314897540 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -103,8 +103,6 @@ static void zynq_write_board_setup(ARMCPU *cpu, sizeof(board_setup_blob), BOARD_SETUP_ADDR); } -static struct arm_boot_info zynq_binfo = {}; - static void gem_init(uint32_t base, qemu_irq irq) { DeviceState *dev; @@ -268,7 +266,7 @@ static void zynq_init(MachineState *machine) busdev = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE); - zynq_binfo.gic_cpu_if_addr = MPCORE_PERIPHBASE + 0x100; + zynq_machine->bootinfo.gic_cpu_if_addr = MPCORE_PERIPHBASE + 0x100; sysbus_create_varargs("l2x0", MPCORE_PERIPHBASE + 0x2000, NULL); for (n = 0; n < smp_cpus; n++) { /* See "hw/intc/arm_gic.h" for the IRQ line association */ @@ -444,13 +442,14 @@ static void zynq_init(MachineState *machine) create_unimplemented_device("zynq.qos301_dmac", 0xF8947000, 0x130); create_unimplemented_device("zynq.qos301_iou", 0xF8948000, 0x130); - zynq_binfo.ram_size = machine->ram_size; - zynq_binfo.board_id = 0xd32; - zynq_binfo.loader_start = 0; - zynq_binfo.board_setup_addr = BOARD_SETUP_ADDR; - zynq_binfo.write_board_setup = zynq_write_board_setup; + zynq_machine->bootinfo.ram_size = machine->ram_size; + zynq_machine->bootinfo.board_id = 0xd32; + zynq_machine->bootinfo.loader_start = 0; + zynq_machine->bootinfo.board_setup_addr = BOARD_SETUP_ADDR; + zynq_machine->bootinfo.write_board_setup = zynq_write_board_setup; - arm_load_kernel(zynq_machine->cpu[0], machine, &zynq_binfo); + arm_load_kernel(zynq_machine->cpu[0], machine, + &zynq_machine->bootinfo); } static void zynq_machine_class_init(ObjectClass *oc, const void *data) diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index d7c2c3c2fd..3d361a4976 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -305,6 +305,7 @@ static int intel_hda_send_command(IntelHDAState *d, uint32_t verb) static void intel_hda_corb_run(IntelHDAState *d) { + const MemTxAttrs attrs = { .memory = true }; hwaddr addr; uint32_t rp, verb; @@ -330,7 +331,7 @@ static void intel_hda_corb_run(IntelHDAState *d) rp = (d->corb_rp + 1) & 0xff; addr = intel_hda_addr(d->corb_lbase, d->corb_ubase); - ldl_le_pci_dma(&d->pci, addr + 4 * rp, &verb, MEMTXATTRS_UNSPECIFIED); + ldl_le_pci_dma(&d->pci, addr + 4 * rp, &verb, attrs); d->corb_rp = rp; dprint(d, 2, "%s: [rp 0x%x] verb 0x%08x\n", __func__, rp, verb); @@ -395,7 +396,7 @@ static void intel_hda_response(HDACodecDevice *dev, bool solicited, uint32_t res static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, uint8_t *buf, uint32_t len) { - const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; + const MemTxAttrs attrs = { .memory = true }; HDACodecBus *bus = HDA_BUS(dev->qdev.parent_bus); IntelHDAState *d = container_of(bus, IntelHDAState, codecs); hwaddr addr; @@ -466,6 +467,7 @@ static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, static void intel_hda_parse_bdl(IntelHDAState *d, IntelHDAStream *st) { + const MemTxAttrs attrs = { .memory = true }; hwaddr addr; uint8_t buf[16]; uint32_t i; @@ -475,7 +477,8 @@ static void intel_hda_parse_bdl(IntelHDAState *d, IntelHDAStream *st) g_free(st->bpl); st->bpl = g_new(bpl, st->bentries); for (i = 0; i < st->bentries; i++, addr += 16) { - pci_dma_read(&d->pci, addr, buf, 16); + pci_dma_rw(&d->pci, addr, buf, 16, + DMA_DIRECTION_TO_DEVICE, attrs); st->bpl[i].addr = le64_to_cpu(*(uint64_t *)buf); st->bpl[i].len = le32_to_cpu(*(uint32_t *)(buf + 8)); st->bpl[i].flags = le32_to_cpu(*(uint32_t *)(buf + 12)); diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index fb5cff3866..694bcebb60 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -850,7 +850,7 @@ static void virtio_snd_handle_tx_xfer(VirtIODevice *vdev, VirtQueue *vq) VirtIOSound *vsnd = VIRTIO_SND(vdev); VirtIOSoundPCMBuffer *buffer; VirtQueueElement *elem; - size_t msg_sz, size; + size_t msg_sz, size, tmp; virtio_snd_pcm_xfer hdr; uint32_t stream_id; /* @@ -880,6 +880,8 @@ static void virtio_snd_handle_tx_xfer(VirtIODevice *vdev, VirtQueue *vq) if (msg_sz != sizeof(virtio_snd_pcm_xfer)) { goto tx_err; } + assert(iov_size(elem->out_sg, elem->out_num) >= msg_sz); + size = iov_size(elem->out_sg, elem->out_num) - msg_sz; stream_id = le32_to_cpu(hdr.stream_id); if (stream_id >= vsnd->snd_conf.streams @@ -892,9 +894,11 @@ static void virtio_snd_handle_tx_xfer(VirtIODevice *vdev, VirtQueue *vq) goto tx_err; } + /* Check for g_malloc0 overflow. */ + if (!g_size_checked_add(&tmp, sizeof(VirtIOSoundPCMBuffer), size)) { + goto tx_err; + } WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { - size = iov_size(elem->out_sg, elem->out_num) - msg_sz; - buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size); buffer->elem = elem; buffer->populated = false; @@ -932,7 +936,7 @@ static void virtio_snd_handle_rx_xfer(VirtIODevice *vdev, VirtQueue *vq) VirtIOSound *vsnd = VIRTIO_SND(vdev); VirtIOSoundPCMBuffer *buffer; VirtQueueElement *elem; - size_t msg_sz, size; + size_t msg_sz, size, tmp; virtio_snd_pcm_xfer hdr; uint32_t stream_id; /* @@ -970,12 +974,18 @@ static void virtio_snd_handle_rx_xfer(VirtIODevice *vdev, VirtQueue *vq) } stream = vsnd->pcm.streams[stream_id]; - if (stream == NULL || stream->info.direction != VIRTIO_SND_D_INPUT) { + size = iov_size(elem->in_sg, elem->in_num); + if (stream == NULL + || stream->info.direction != VIRTIO_SND_D_INPUT + || size < sizeof(virtio_snd_pcm_status)) { + goto rx_err; + } + size -= sizeof(virtio_snd_pcm_status); + /* Check for g_malloc0 overflow. */ + if (!g_size_checked_add(&tmp, sizeof(VirtIOSoundPCMBuffer), size)) { goto rx_err; } WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { - size = iov_size(elem->in_sg, elem->in_num) - - sizeof(virtio_snd_pcm_status); buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size); buffer->elem = elem; buffer->vq = vq; diff --git a/hw/block/fdc.c b/hw/block/fdc.c index 2c1681b7d0..1178b959a6 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -196,6 +196,12 @@ static void fd_init(FDrive *drv) #define NUM_SIDES(drv) ((drv)->flags & FDISK_DBL_SIDES ? 2 : 1) +/* Is a diskette present in the drive? */ +static bool fd_media_present(FDrive *drv) +{ + return drv->blk != NULL && blk_is_inserted(drv->blk); +} + static int fd_sector_calc(uint8_t head, uint8_t track, uint8_t sect, uint8_t last_sect, uint8_t num_sides) { @@ -222,6 +228,7 @@ static int fd_offset(FDrive *drv) * returns 2 if track is invalid * returns 3 if sector is invalid * returns 4 if seek is disabled + * returns 5 if no floppy is inserted */ static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect, int enable_seek) @@ -258,7 +265,7 @@ static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect, #endif drv->head = head; if (drv->track != track) { - if (drv->blk != NULL && blk_is_inserted(drv->blk)) { + if (fd_media_present(drv)) { drv->media_changed = 0; } ret = 1; @@ -267,8 +274,8 @@ static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect, drv->sect = sect; } - if (drv->blk == NULL || !blk_is_inserted(drv->blk)) { - ret = 2; + if (!fd_media_present(drv)) { + ret = 5; } return ret; @@ -1476,14 +1483,24 @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction) NUM_SIDES(cur_drv))); switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) { case 2: - /* sect too big */ + /* track/head out of range */ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); fdctrl->fifo[3] = kt; fdctrl->fifo[4] = kh; fdctrl->fifo[5] = ks; return; + case 5: + /* + * No medium: there is no address mark to be found. Guests that tell + * an absent diskette from an unreadable one rely on ST1.MA. + */ + fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00); + fdctrl->fifo[3] = kt; + fdctrl->fifo[4] = kh; + fdctrl->fifo[5] = ks; + return; case 3: - /* track too big */ + /* sector too big */ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00); fdctrl->fifo[3] = kt; fdctrl->fifo[4] = kh; @@ -1791,14 +1808,21 @@ static void fdctrl_format_sector(FDCtrl *fdctrl) NUM_SIDES(cur_drv))); switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) { case 2: - /* sect too big */ + /* track/head out of range */ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); fdctrl->fifo[3] = kt; fdctrl->fifo[4] = kh; fdctrl->fifo[5] = ks; return; + case 5: + /* no medium */ + fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00); + fdctrl->fifo[3] = kt; + fdctrl->fifo[4] = kh; + fdctrl->fifo[5] = ks; + return; case 3: - /* track too big */ + /* sector too big */ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00); fdctrl->fifo[3] = kt; fdctrl->fifo[4] = kh; @@ -1936,7 +1960,10 @@ static void fdctrl_handle_save(FDCtrl *fdctrl, int direction) static void fdctrl_handle_readid(FDCtrl *fdctrl, int direction) { - FDrive *cur_drv = get_cur_drv(fdctrl); + FDrive *cur_drv; + + SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); + cur_drv = get_cur_drv(fdctrl); cur_drv->head = (fdctrl->fifo[1] >> 2) & 1; timer_mod(fdctrl->result_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + @@ -2303,6 +2330,18 @@ static void fdctrl_result_timer(void *opaque) FDCtrl *fdctrl = opaque; FDrive *cur_drv = get_cur_drv(fdctrl); + /* + * An empty drive has no address marks to read. Completing READ ID + * successfully, with the made-up sector ID left over from the "spinning" + * emulation below, tells the guest that a diskette is still present after + * it has been ejected. The only error path left was a data rate mismatch, + * and media_rate is never reset when the medium is removed. + */ + if (!fd_media_present(cur_drv)) { + FLOPPY_DPRINTF("read id on empty drive\n"); + fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00); + return; + } /* Pretend we are spinning. * This is needed for Coherent, which uses READ ID to check for * sector interleaving. diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 5b9ddb20b1..a13b91967e 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -1030,6 +1030,16 @@ static int pflash_post_load(void *opaque, int version_id) { PFlashCFI01 *pfl = opaque; + /* + * ROMD mode is not in the VMState; derive it from the migrated + * cmd and wcycle. Only (wcycle == 0, cmd == 0x00) is read-array. + */ + if (pfl->wcycle == 0 && pfl->cmd == 0x00) { + memory_region_rom_device_set_romd(&pfl->mem, true); + } else { + memory_region_rom_device_set_romd(&pfl->mem, false); + } + if (!pfl->ro) { pfl->vmstate = qemu_add_vm_change_state_handler(postload_update_cb, pfl); diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index 080b7f6331..fb41ee2ac5 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -43,14 +43,15 @@ static const VMStateDescription vmstate_imx_serial = { .name = TYPE_IMX_SERIAL, - .version_id = 3, - .minimum_version_id = 3, + .version_id = 4, + .minimum_version_id = 4, .fields = (const VMStateField[]) { VMSTATE_FIFO32(rx_fifo, IMXSerialState), VMSTATE_TIMER(ageing_timer, IMXSerialState), VMSTATE_UINT32(usr1, IMXSerialState), VMSTATE_UINT32(usr2, IMXSerialState), VMSTATE_UINT32(ucr1, IMXSerialState), + VMSTATE_UINT32(ucr2, IMXSerialState), VMSTATE_UINT32(uts1, IMXSerialState), VMSTATE_UINT32(onems, IMXSerialState), VMSTATE_UINT32(ufcr, IMXSerialState), diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c index 9a16896d22..f6ed282f1b 100644 --- a/hw/char/sclpconsole-lm.c +++ b/hw/char/sclpconsole-lm.c @@ -243,7 +243,8 @@ static int write_event_data(SCLPEvent *event, EventBufferHeader *ebh) SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); len = be16_to_cpu(data->mdb.header.length); - if (len < sizeof(data->mdb.header)) { + if (len < sizeof(data->mdb.header) || + len > be16_to_cpu(data->header.length) - sizeof(EventBufferHeader)) { return SCLP_RC_INCONSISTENT_LENGTHS; } len -= sizeof(data->mdb.header); diff --git a/hw/char/serial.c b/hw/char/serial.c index 0729cd2ce9..4339562ab0 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -936,6 +936,7 @@ static void serial_unrealize(DeviceState *dev) { SerialState *s = SERIAL(dev); + g_clear_handle_id(&s->watch_tag, g_source_remove); qemu_chr_fe_deinit(&s->chr, false); timer_free(s->modem_status_poll); diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index e314f916f8..4f4c87f33a 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -23,6 +23,7 @@ #include "qapi/error.h" #include "hw/core/cpu.h" #include "system/hw_accel.h" +#include "qemu/accel.h" #include "qemu/log.h" #include "qemu/main-loop.h" #include "qemu/lockcnt.h" @@ -32,12 +33,12 @@ #include "exec/log.h" #include "exec/gdbstub.h" #include "system/tcg.h" -#include "hw/core/boards.h" #include "hw/core/qdev-properties.h" #include "trace.h" #ifdef CONFIG_PLUGIN #include "qemu/plugin.h" #endif +#include "cpu-internal.h" CPUState *cpu_by_arch_id(int64_t id) { @@ -228,7 +229,7 @@ const char *parse_cpu_option(const char *cpu_option) return cpu_type; } -bool cpu_exec_realizefn(CPUState *cpu, Error **errp) +bool cpu_common_realize(CPUState *cpu, Error **errp) { if (!accel_cpu_common_realize(cpu, errp)) { return false; @@ -246,26 +247,7 @@ bool cpu_exec_realizefn(CPUState *cpu, Error **errp) static void cpu_common_realizefn(DeviceState *dev, Error **errp) { - CPUState *cpu = CPU(dev); - Object *machine = qdev_get_machine(); - - /* qdev_get_machine() can return something that's not TYPE_MACHINE - * if this is one of the user-only emulators; in that case there's - * no need to check the ignore_memory_transaction_failures board flag. - */ - if (object_dynamic_cast(machine, TYPE_MACHINE)) { - MachineClass *mc = MACHINE_GET_CLASS(machine); - - if (mc) { - cpu->ignore_memory_transaction_failures = - mc->ignore_memory_transaction_failures; - } - } - - if (dev->hotplugged) { - cpu_synchronize_post_init(cpu); - cpu_resume(cpu); - } + cpu_exec_realize(CPU(dev), errp); /* NOTE: latest generic point where the cpu is fully realized */ } @@ -282,10 +264,10 @@ static void cpu_common_unrealizefn(DeviceState *dev) #endif /* NOTE: latest generic point before the cpu is fully unrealized */ - cpu_exec_unrealizefn(cpu); + cpu_common_unrealize(cpu); } -void cpu_exec_unrealizefn(CPUState *cpu) +void cpu_common_unrealize(CPUState *cpu) { cpu_vmstate_unregister(cpu); @@ -325,7 +307,7 @@ static void cpu_common_initfn(Object *obj) QTAILQ_INIT(&cpu->breakpoints); QTAILQ_INIT(&cpu->watchpoints); - cpu_exec_initfn(cpu); + cpu_exec_init(cpu); /* * Plugin initialization must wait until the cpu start executing diff --git a/hw/core/cpu-internal.h b/hw/core/cpu-internal.h new file mode 100644 index 0000000000..44715b4432 --- /dev/null +++ b/hw/core/cpu-internal.h @@ -0,0 +1,23 @@ +/* + * QEMU private CPU interface between user / system modes) + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef HW_CORE_CPU_INTERNAL_H +#define HW_CORE_CPU_INTERNAL_H + +#include "hw/core/qdev.h" +#include "hw/core/cpu.h" + +void cpu_class_init_props(DeviceClass *dc); +void cpu_exec_class_post_init(CPUClass *cc); + +void cpu_exec_init(CPUState *cpu); +void cpu_exec_realize(CPUState *cpu, Error **errp); + +void cpu_vmstate_register(CPUState *cpu); +void cpu_vmstate_unregister(CPUState *cpu); + +#endif diff --git a/hw/core/cpu-system.c b/hw/core/cpu-system.c index 14eb4ed87f..a64e8c2b68 100644 --- a/hw/core/cpu-system.c +++ b/hw/core/cpu-system.c @@ -25,11 +25,14 @@ #include "exec/target_page.h" #include "system/memory.h" #include "qemu/target-info.h" +#include "hw/core/boards.h" #include "hw/core/qdev.h" #include "hw/core/qdev-properties.h" #include "hw/core/sysemu-cpu-ops.h" #include "migration/vmstate.h" +#include "system/hw_accel.h" #include "system/tcg.h" +#include "cpu-internal.h" bool cpu_has_work(CPUState *cpu) { @@ -188,7 +191,7 @@ void cpu_exec_class_post_init(CPUClass *cc) g_assert(cc->sysemu_ops->has_work); } -void cpu_exec_initfn(CPUState *cpu) +void cpu_exec_init(CPUState *cpu) { cpu->memory = get_system_memory(); object_ref(OBJECT(cpu->memory)); @@ -220,6 +223,31 @@ static int cpu_common_pre_load(void *opaque) return 0; } +void cpu_exec_realize(CPUState *cpu, Error **errp) +{ + Object *machine = qdev_get_machine(); + + /* + * qdev_get_machine() can return something that's not TYPE_MACHINE + * if this is one of the user-only emulators; in that case there's + * no need to check the ignore_memory_transaction_failures board flag. + */ + if (object_dynamic_cast(machine, TYPE_MACHINE)) { + MachineClass *mc = MACHINE_GET_CLASS(machine); + + if (mc) { + cpu->ignore_memory_transaction_failures = + mc->ignore_memory_transaction_failures; + } + } + + if (DEVICE(cpu)->hotplugged) { + cpu_synchronize_post_init(cpu); + cpu_resume(cpu); + } + +} + static bool cpu_common_exception_index_needed(void *opaque) { CPUState *cpu = opaque; diff --git a/hw/core/cpu-user.c b/hw/core/cpu-user.c index 25aa25ad24..1e38c88f9b 100644 --- a/hw/core/cpu-user.c +++ b/hw/core/cpu-user.c @@ -11,6 +11,7 @@ #include "hw/core/qdev-properties.h" #include "hw/core/cpu.h" #include "migration/vmstate.h" +#include "cpu-internal.h" static const Property cpu_user_props[] = { /* @@ -32,7 +33,12 @@ void cpu_exec_class_post_init(CPUClass *cc) /* nothing to do */ } -void cpu_exec_initfn(CPUState *cpu) +void cpu_exec_init(CPUState *cpu) +{ + /* nothing to do */ +} + +void cpu_exec_realize(CPUState *cpu, Error **errp) { /* nothing to do */ } diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index e62cb4ec88..1aa5f26229 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "hw/acpi/vmgenid.h" #include "hw/core/boards.h" +#include "hw/core/nmi.h" #include "hw/intc/intc.h" #include "hw/mem/memory-device.h" #include "qapi/error.h" @@ -448,3 +449,8 @@ void qmp_dump_skeys(const char *filename, Error **errp) } DUMP_SKEYS_INTERFACE_CLASS(oc)->qmp_dump_skeys(filename, errp); } + +void qmp_inject_nmi(Error **errp) +{ + nmi_inject(errp); +} diff --git a/hw/core/machine.c b/hw/core/machine.c index 805148678d..8939ae1666 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -40,7 +40,14 @@ #include "qemu/audio.h" #include "hw/arm/smmuv3.h" +GlobalProperty hw_compat_11_1[] = { + { "sysbus-ehci-usb", "x-migrate-fetch-addr-64bit", "off" }, + { "pci-ehci-usb", "x-migrate-fetch-addr-64bit", "off" }, +}; +const size_t hw_compat_11_1_len = G_N_ELEMENTS(hw_compat_11_1); + GlobalProperty hw_compat_11_0[] = { + { "virtio-mmio", VIRTIO_QUEUE_SIZE_OVERRIDE, "1024" }, { "chardev-vc", "encoding", "cp437" }, { "tpm-crb", "cap-chunk", "off" }, { "tpm-crb", "x-allow-chunk-migration", "off" }, @@ -1307,6 +1314,7 @@ static void machine_finalize(Object *obj) g_free(ms->nvdimms_state); g_free(ms->numa_state); g_free(ms->audiodev); + g_free(ms->fdt); } bool machine_usb(MachineState *machine) diff --git a/hw/core/nmi.c b/hw/core/nmi.c index 4b447e126b..84f2166598 100644 --- a/hw/core/nmi.c +++ b/hw/core/nmi.c @@ -22,55 +22,35 @@ #include "qemu/osdep.h" #include "hw/core/nmi.h" #include "qapi/error.h" -#include "qemu/module.h" -#include "monitor/monitor.h" - -struct do_nmi_s { - int cpu_index; - Error *err; - bool handled; -}; - -static void nmi_children(Object *o, struct do_nmi_s *ns); static int do_nmi(Object *o, void *opaque) { - struct do_nmi_s *ns = opaque; + bool *handled = opaque; NMIState *n = (NMIState *) object_dynamic_cast(o, TYPE_NMI); if (n) { - NMIClass *nc = NMI_GET_CLASS(n); - - ns->handled = true; - nc->nmi_monitor_handler(n, ns->cpu_index, &ns->err); - if (ns->err) { - return -1; - } + *handled = true; + NMI_GET_CLASS(n)->raise_nmi(n); + /* + * We expect only one object to implement TYPE_NMI, so once + * we've asked it to deliver the NMI we can stop looking. + */ + return 1; } - nmi_children(o, ns); return 0; } -static void nmi_children(Object *o, struct do_nmi_s *ns) +bool nmi_inject(Error **errp) { - object_child_foreach(o, do_nmi, ns); -} + bool handled = false; -void nmi_monitor_handle(int cpu_index, Error **errp) -{ - struct do_nmi_s ns = { - .cpu_index = cpu_index, - .err = NULL, - .handled = false - }; - - nmi_children(object_get_root(), &ns); - if (ns.handled) { - error_propagate(errp, ns.err); - } else { + object_child_foreach_recursive(object_get_root(), do_nmi, &handled); + if (!handled) { error_setg(errp, "machine does not provide NMIs"); + return false; } + return true; } static const TypeInfo nmi_info = { diff --git a/hw/core/qdev.c b/hw/core/qdev.c index e2aab3d1fc..0b0f2f47fa 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -264,17 +264,43 @@ static void device_reset_child_foreach(Object *obj, ResettableChildCallback cb, bool qdev_realize(DeviceState *dev, BusState *bus, Error **errp) { - assert(!dev->realized && !dev->parent_bus); + static int unattached_count; + bool unattached_parent = false; + + assert(!dev->parent_bus); + + if (!OBJECT(dev)->parent) { + gchar *name = g_strdup_printf("device[%d]", unattached_count++); + + object_property_add_child(machine_get_container("unattached"), + name, OBJECT(dev)); + unattached_parent = true; + g_free(name); + } if (bus) { if (!qdev_set_parent_bus(dev, bus, errp)) { - return false; + goto fail; } } else { assert(!DEVICE_GET_CLASS(dev)->bus_type); } - return object_property_set_bool(OBJECT(dev), "realized", true, errp); + if (object_property_set_bool(OBJECT(dev), "realized", true, errp)) { + return true; + } + +fail: + if (unattached_parent) { + /* + * Beware, this doesn't just revert + * object_property_add_child(), it also runs bus_remove()! + */ + object_unparent(OBJECT(dev)); + unattached_count--; + } + + return false; } bool qdev_realize_and_unref(DeviceState *dev, BusState *bus, Error **errp) @@ -479,8 +505,6 @@ static void device_set_realized(Object *obj, bool value, Error **errp) BusState *bus; NamedClockList *ncl; Error *local_err = NULL; - bool unattached_parent = false; - static int unattached_count; if (dev->hotplugged && !dc->hotpluggable) { error_setg(errp, "Device '%s' does not support hotplugging", @@ -493,15 +517,6 @@ static void device_set_realized(Object *obj, bool value, Error **errp) goto fail; } - if (!obj->parent) { - gchar *name = g_strdup_printf("device[%d]", unattached_count++); - - object_property_add_child(machine_get_container("unattached"), - name, obj); - unattached_parent = true; - g_free(name); - } - hotplug_ctrl = qdev_get_hotplug_handler(dev); if (hotplug_ctrl) { hotplug_handler_pre_plug(hotplug_ctrl, dev, &local_err); @@ -627,14 +642,6 @@ post_realize_fail: fail: error_propagate(errp, local_err); - if (unattached_parent) { - /* - * Beware, this doesn't just revert - * object_property_add_child(), it also runs bus_remove()! - */ - object_unparent(OBJECT(dev)); - unattached_count--; - } } static bool device_get_hotpluggable(Object *obj, Error **errp) diff --git a/hw/cxl/cxl-host.c b/hw/cxl/cxl-host.c index 7e744312f1..eba13c9e7c 100644 --- a/hw/cxl/cxl-host.c +++ b/hw/cxl/cxl-host.c @@ -279,7 +279,7 @@ static void cxl_fmws_direct_passthrough_setup(CXLDirectPTState *state, MemoryRegion *mr = NULL; uint64_t vmr_size = 0, pmr_size = 0, offset = 0; MemoryRegion *direct_mr; - g_autofree char *direct_mr_name; + g_autofree char *direct_mr_name = NULL; unsigned int idx = state->hdm_decoder_idx; if (ct3d->hostvmem) { diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 20e0b7e476..ec18338b42 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -1813,6 +1813,10 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd, return CXL_MBOX_UNSUPPORTED; } + if ((uint32_t)hdr->offset + bytes_to_copy > + sizeof(ct3d->soft_ppr_wr_attrs)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } memcpy((uint8_t *)&ct3d->soft_ppr_wr_attrs + hdr->offset, sppr_write_attrs, bytes_to_copy); set_feat_info->data_size += bytes_to_copy; @@ -1832,6 +1836,10 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd, return CXL_MBOX_UNSUPPORTED; } + if ((uint32_t)hdr->offset + bytes_to_copy > + sizeof(ct3d->hard_ppr_wr_attrs)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } memcpy((uint8_t *)&ct3d->hard_ppr_wr_attrs + hdr->offset, hppr_write_attrs, bytes_to_copy); set_feat_info->data_size += bytes_to_copy; @@ -1851,6 +1859,10 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd, return CXL_MBOX_UNSUPPORTED; } + if ((uint32_t)hdr->offset + bytes_to_copy > + sizeof(ct3d->cacheline_sparing_wr_attrs)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } memcpy((uint8_t *)&ct3d->cacheline_sparing_wr_attrs + hdr->offset, mem_sparing_write_attrs, bytes_to_copy); set_feat_info->data_size += bytes_to_copy; @@ -1869,6 +1881,10 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd, return CXL_MBOX_UNSUPPORTED; } + if ((uint32_t)hdr->offset + bytes_to_copy > + sizeof(ct3d->row_sparing_wr_attrs)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } memcpy((uint8_t *)&ct3d->row_sparing_wr_attrs + hdr->offset, mem_sparing_write_attrs, bytes_to_copy); set_feat_info->data_size += bytes_to_copy; @@ -1887,6 +1903,10 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd, return CXL_MBOX_UNSUPPORTED; } + if ((uint32_t)hdr->offset + bytes_to_copy > + sizeof(ct3d->bank_sparing_wr_attrs)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } memcpy((uint8_t *)&ct3d->bank_sparing_wr_attrs + hdr->offset, mem_sparing_write_attrs, bytes_to_copy); set_feat_info->data_size += bytes_to_copy; @@ -1905,6 +1925,10 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd, return CXL_MBOX_UNSUPPORTED; } + if ((uint32_t)hdr->offset + bytes_to_copy > + sizeof(ct3d->rank_sparing_wr_attrs)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } memcpy((uint8_t *)&ct3d->rank_sparing_wr_attrs + hdr->offset, mem_sparing_write_attrs, bytes_to_copy); set_feat_info->data_size += bytes_to_copy; diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c index 7507e4fd3c..6c062a28c0 100644 --- a/hw/display/exynos4210_fimd.c +++ b/hw/display/exynos4210_fimd.c @@ -282,7 +282,7 @@ struct Exynos4210fimdWindow { pixel_to_rgb_func *pixel_to_rgb; void (*draw_line)(Exynos4210fimdWindow *w, uint8_t *src, uint8_t *dst, - bool blend); + uint32_t width, bool blend); uint32_t (*get_alpha)(Exynos4210fimdWindow *w, uint32_t pix_a); uint16_t lefttop_x, lefttop_y; /* VIDOSD0 register */ uint16_t rightbot_x, rightbot_y; /* VIDOSD1 register */ @@ -784,9 +784,9 @@ exynos4210_fimd_blend_pixel(Exynos4210fimdWindow *w, rgba p_bg, rgba *ret) /* Draw line with index in palette table in RAM frame buffer data */ #define DEF_DRAW_LINE_PALETTE(N) \ static void glue(draw_line_palette_, N)(Exynos4210fimdWindow *w, uint8_t *src, \ - uint8_t *dst, bool blend) \ + uint8_t *dst, uint32_t width, \ + bool blend) \ { \ - int width = w->rightbot_x - w->lefttop_x + 1; \ uint8_t *ifb = dst; \ uint8_t swap = (w->wincon & FIMD_WINCON_SWAP) >> FIMD_WINCON_SWAP_SHIFT; \ uint64_t data; \ @@ -813,9 +813,8 @@ static void glue(draw_line_palette_, N)(Exynos4210fimdWindow *w, uint8_t *src, \ /* Draw line with direct color value in RAM frame buffer data */ #define DEF_DRAW_LINE_NOPALETTE(N) \ static void glue(draw_line_, N)(Exynos4210fimdWindow *w, uint8_t *src, \ - uint8_t *dst, bool blend) \ + uint8_t *dst, uint32_t width, bool blend) \ { \ - int width = w->rightbot_x - w->lefttop_x + 1; \ uint8_t *ifb = dst; \ uint8_t swap = (w->wincon & FIMD_WINCON_SWAP) >> FIMD_WINCON_SWAP_SHIFT; \ uint64_t data; \ @@ -848,11 +847,10 @@ DEF_DRAW_LINE_NOPALETTE(32) /* Special draw line routine for window color map case */ static void draw_line_mapcolor(Exynos4210fimdWindow *w, uint8_t *src, - uint8_t *dst, bool blend) + uint8_t *dst, uint32_t width, bool blend) { rgba p, p_old; uint8_t *ifb = dst; - int width = w->rightbot_x - w->lefttop_x + 1; uint32_t map_color = w->winmap & FIMD_WINMAP_COLOR_MASK; do { @@ -1195,15 +1193,25 @@ static void exynos4210_fimd_update_irq(Exynos4210fimdState *s) } } +static uint32_t exynos4210_fimd_global_width(Exynos4210fimdState *s) +{ + return ((s->vidtcon[2] >> FIMD_VIDTCON2_HOR_SHIFT) & + FIMD_VIDTCON2_SIZE_MASK) + 1; +} + +static uint32_t exynos4210_fimd_global_height(Exynos4210fimdState *s) +{ + return ((s->vidtcon[2] >> FIMD_VIDTCON2_VER_SHIFT) & + FIMD_VIDTCON2_SIZE_MASK) + 1; +} + static void exynos4210_update_resolution(Exynos4210fimdState *s) { DisplaySurface *surface = qemu_console_surface(s->console); /* LCD resolution is stored in VIDEO TIME CONTROL REGISTER 2 */ - uint32_t width = ((s->vidtcon[2] >> FIMD_VIDTCON2_HOR_SHIFT) & - FIMD_VIDTCON2_SIZE_MASK) + 1; - uint32_t height = ((s->vidtcon[2] >> FIMD_VIDTCON2_VER_SHIFT) & - FIMD_VIDTCON2_SIZE_MASK) + 1; + uint32_t width = exynos4210_fimd_global_width(s); + uint32_t height = exynos4210_fimd_global_height(s); if (s->ifb == NULL || surface_width(surface) != width || surface_height(surface) != height) { @@ -1229,22 +1237,37 @@ static bool exynos4210_fimd_update(void *opaque) bool blend = false; uint8_t *host_fb_addr; bool is_dirty = false; - int global_width; + uint32_t global_width, global_height; + uint32_t window_width; if (!s || !s->console || !s->enabled || surface_bits_per_pixel(qemu_console_surface(s->console)) == 0) { return true; } - global_width = (s->vidtcon[2] & FIMD_VIDTCON2_SIZE_MASK) + 1; + global_width = exynos4210_fimd_global_width(s); + global_height = exynos4210_fimd_global_height(s); exynos4210_update_resolution(s); surface = qemu_console_surface(s->console); for (i = 0; i < NUM_OF_WINDOWS; i++) { w = &s->window[i]; if ((w->wincon & FIMD_WINCON_ENWIN) && w->host_fb_addr) { - scrn_height = w->rightbot_y - w->lefttop_y + 1; + uint32_t rightbot_x, rightbot_y; + + if (w->lefttop_x >= global_width || + w->lefttop_y >= global_height) { + /* Guest has put the window entirely offscreen: ignore */ + continue; + } + + /* Clamp right corner coords to be within the screen */ + rightbot_x = MIN(w->rightbot_x, global_width - 1); + rightbot_y = MIN(w->rightbot_y, global_height - 1); + scrn_height = rightbot_y - w->lefttop_y + 1; scrn_width = w->virtpage_width; + /* Number of bytes to actually draw */ + window_width = rightbot_x - w->lefttop_x + 1; /* Total width of virtual screen page in bytes */ inc_size = scrn_width + w->virtpage_offsize; host_fb_addr = w->host_fb_addr; @@ -1263,7 +1286,8 @@ static bool exynos4210_fimd_update(void *opaque) last_line = line; w->draw_line(w, host_fb_addr, s->ifb + w->lefttop_x * RGBA_SIZE + (w->lefttop_y + line) * - global_width * RGBA_SIZE, blend); + global_width * RGBA_SIZE, + window_width, blend); } host_fb_addr += inc_size; fb_line_addr += inc_size; diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index 3bf634ee05..348ddba768 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -27,6 +27,7 @@ static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect) { DisplaySurface *surface = qemu_console_surface(qxl->vga.con); + int dst_stride = surface_stride(surface); uint8_t *dst = surface_data(surface); uint8_t *src; int len, i; @@ -45,14 +46,14 @@ static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect) } else { src += rect->top * qxl->guest_primary.abs_stride; } - dst += rect->top * qxl->guest_primary.abs_stride; + dst += rect->top * dst_stride; src += rect->left * qxl->guest_primary.bytes_pp; dst += rect->left * qxl->guest_primary.bytes_pp; len = (rect->right - rect->left) * qxl->guest_primary.bytes_pp; for (i = rect->top; i < rect->bottom; i++) { memcpy(dst, src, len); - dst += qxl->guest_primary.abs_stride; + dst += dst_stride; src += qxl->guest_primary.qxl_stride; } } @@ -61,30 +62,13 @@ void qxl_render_resize(PCIQXLDevice *qxl) { QXLSurfaceCreate *sc = &qxl->guest_primary.surface; - qxl->guest_primary.qxl_stride = sc->stride; - qxl->guest_primary.abs_stride = abs(sc->stride); + qxl->guest_primary.qxl_stride = le32_to_cpu(sc->stride); + qxl->guest_primary.abs_stride = abs(qxl->guest_primary.qxl_stride); qxl->guest_primary.resized++; - switch (sc->format) { - case SPICE_SURFACE_FMT_16_555: - qxl->guest_primary.bytes_pp = 2; - qxl->guest_primary.bits_pp = 15; - break; - case SPICE_SURFACE_FMT_16_565: - qxl->guest_primary.bytes_pp = 2; - qxl->guest_primary.bits_pp = 16; - break; - case SPICE_SURFACE_FMT_32_xRGB: - case SPICE_SURFACE_FMT_32_ARGB: - qxl->guest_primary.bytes_pp = 4; - qxl->guest_primary.bits_pp = 32; - break; - default: - fprintf(stderr, "%s: unhandled format: %x\n", __func__, - qxl->guest_primary.surface.format); - qxl->guest_primary.bytes_pp = 4; - qxl->guest_primary.bits_pp = 32; - break; - } + /* fallback to default bpp if format is unknown */ + qxl_format_bpp(qxl, le32_to_cpu(sc->format), + &qxl->guest_primary.bytes_pp, + &qxl->guest_primary.bits_pp); } static void qxl_set_rect_to_surface(PCIQXLDevice *qxl, QXLRect *area) @@ -101,15 +85,45 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) DisplaySurface *surface; int width = qxl->guest_head0_width ?: qxl->guest_primary.surface.width; int height = qxl->guest_head0_height ?: qxl->guest_primary.surface.height; + uint64_t map_height; int i; + if (width <= 0 || height <= 0) { + goto end; + } + + if (qxl->guest_primary.bytes_pp > 0) { + int max_width = qxl->guest_primary.abs_stride + / qxl->guest_primary.bytes_pp; + width = MIN(width, max_width); + } + + if (qxl->guest_primary.qxl_stride < 0) { + /* qxl_blit() uses the primary height to find the first scanline. */ + height = MIN(height, (int)qxl->guest_primary.surface.height); + } + + if (qxl->guest_primary.abs_stride > 0) { + int max_height = qxl->vgamem_size / qxl->guest_primary.abs_stride; + height = MIN(height, max_height); + } + + /* + * height limits the visible update, while map_height is the guest memory + * span validated by qxl_phys2virt(). With a negative stride qxl_blit() + * addresses scanlines from the declared primary height, so a shorter + * monitor still requires validating the full primary surface. + */ + map_height = qxl->guest_primary.qxl_stride < 0 ? + qxl->guest_primary.surface.height : height; + if (qxl->guest_primary.resized) { qxl->guest_primary.resized = 0; qxl->guest_primary.data = qxl_phys2virt(qxl, qxl->guest_primary.surface.mem, MEMSLOT_GROUP_GUEST, qxl->guest_primary.abs_stride - * height); + * map_height); if (!qxl->guest_primary.data) { goto end; } @@ -217,7 +231,8 @@ void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie) } static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl, - QXLDataChunk *chunk, uint32_t group_id) + QXLDataChunk *chunk, uint32_t group_id, + uint32_t chunk_data_size) { uint32_t max_chunks = 32; size_t offset = 0; @@ -225,22 +240,21 @@ static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl, QXLPHYSICAL next_chunk_phys = 0; for (;;) { - bytes = MIN(size - offset, chunk->data_size); + bytes = MIN(size - offset, chunk_data_size); memcpy(dest + offset, chunk->data, bytes); offset += bytes; if (offset == size) { return; } next_chunk_phys = chunk->next_chunk; - /* fist time, only get the next chunk's data size */ chunk = qxl_phys2virt(qxl, next_chunk_phys, group_id, sizeof(QXLDataChunk)); if (!chunk) { return; } - /* second time, check data size and get data */ + chunk_data_size = chunk->data_size; chunk = qxl_phys2virt(qxl, next_chunk_phys, group_id, - sizeof(QXLDataChunk) + chunk->data_size); + sizeof(QXLDataChunk) + chunk_data_size); if (!chunk) { return; } @@ -252,7 +266,7 @@ static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl, } static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor, - uint32_t group_id) + uint32_t group_id, uint32_t chunk_data_size) { QEMUCursor *c; uint8_t *and_mask, *xor_mask; @@ -272,11 +286,11 @@ static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor, 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 || cursor->chunk.data_size < size) { + if (size != cursor->data_size || chunk_data_size < size) { qxl_set_guest_bug(qxl, "%s: bad monochrome cursor %ux%u" " data_size %u chunk_size %u", __func__, c->width, c->height, - cursor->data_size, cursor->chunk.data_size); + cursor->data_size, chunk_data_size); goto fail; } and_mask = cursor->chunk.data; @@ -288,7 +302,8 @@ static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor, break; case SPICE_CURSOR_TYPE_ALPHA: size = sizeof(uint32_t) * c->width * c->height; - qxl_unpack_chunks(c->data, size, qxl, &cursor->chunk, group_id); + qxl_unpack_chunks(c->data, size, qxl, &cursor->chunk, group_id, + chunk_data_size); if (qxl->debug > 2) { cursor_print_ascii_art(c, "qxl/alpha"); } @@ -325,19 +340,23 @@ int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext) } switch (cmd->type) { case QXL_CURSOR_SET: + { + uint32_t chunk_data_size; + /* First read the QXLCursor to get QXLDataChunk::data_size ... */ cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id, sizeof(QXLCursor)); if (!cursor) { return 1; } + chunk_data_size = cursor->chunk.data_size; /* Then read including the chunked data following QXLCursor. */ cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id, - sizeof(QXLCursor) + cursor->chunk.data_size); + sizeof(QXLCursor) + chunk_data_size); if (!cursor) { return 1; } - c = qxl_cursor(qxl, cursor, ext->group_id); + c = qxl_cursor(qxl, cursor, ext->group_id, chunk_data_size); if (c == NULL) { c = cursor_builtin_left_ptr(); } @@ -351,6 +370,7 @@ int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext) qemu_mutex_unlock(&qxl->ssd.lock); qemu_bh_schedule(qxl->ssd.cursor_bh); break; + } case QXL_CURSOR_MOVE: qemu_mutex_lock(&qxl->ssd.lock); qxl->ssd.mouse_x = cmd->u.position.x; diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 74258afa58..384b8767b8 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -270,7 +270,7 @@ static void qxl_spice_monitors_config_async(PCIQXLDevice *qxl, int replay) } cfg = qxl_phys2virt(qxl, qxl->guest_monitors_config, MEMSLOT_GROUP_GUEST, - sizeof(QXLMonitorsConfig)); + sizeof(QXLMonitorsConfig) + sizeof(QXLHead)); if (cfg != NULL && cfg->count == 1) { qxl->guest_primary.resized = 1; qxl->guest_head0_width = cfg->heads[0].width; @@ -1489,6 +1489,47 @@ static void qxl_create_guest_primary_complete(PCIQXLDevice *qxl) qxl_render_resize(qxl); } +/* + * Convert a SpiceSurfaceFormat to bytes per pixel and bits per pixel. + * + * Only valid for surface suitable for rendering. + */ +bool qxl_format_bpp(PCIQXLDevice *qxl, SpiceSurfaceFmt format, + uint32_t *bytes_pp, uint32_t *bits_pp) +{ + uint32_t bypp = 4; + uint32_t bipp = 32; + bool ret = true; + + switch (format) { + case SPICE_SURFACE_FMT_16_555: + bypp = 2; + bipp = 15; + break; + case SPICE_SURFACE_FMT_16_565: + bypp = 2; + bipp = 16; + break; + case SPICE_SURFACE_FMT_32_xRGB: + case SPICE_SURFACE_FMT_32_ARGB: + bypp = 4; + bipp = 32; + break; + default: + ret = false; + qxl_set_guest_bug(qxl, "%s: unhandled format: %x", __func__, format); + } + + if (bytes_pp != NULL) { + *bytes_pp = bypp; + } + if (bits_pp != NULL) { + *bits_pp = bipp; + } + + return ret; +} + static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm, qxl_async_io async) { @@ -1496,6 +1537,7 @@ static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm, QXLSurfaceCreate *sc = &qxl->guest_primary.surface; uint32_t requested_height = le32_to_cpu(sc->height); int requested_stride = le32_to_cpu(sc->stride); + uint32_t bytes_pp; if (requested_stride == INT32_MIN || abs(requested_stride) * (uint64_t)requested_height @@ -1532,6 +1574,23 @@ static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm, return; } + if (!qxl_format_bpp(qxl, surface.format, &bytes_pp, NULL)) { + return; + } + + if (surface.width == 0 || surface.height == 0) { + qxl_set_guest_bug(qxl, "%s: zero dimension %ux%u", + __func__, surface.width, surface.height); + return; + } + + if ((uint64_t)surface.width * bytes_pp > abs(surface.stride)) { + qxl_set_guest_bug(qxl, "%s: stride too small for width:" + " stride %d width %u bpp %u", + __func__, surface.stride, surface.width, bytes_pp); + return; + } + surface.mouse_mode = true; surface.group_id = MEMSLOT_GROUP_GUEST; if (loadvm) { @@ -2203,7 +2262,8 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp) error_report_err(err); } - qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl); + qxl->vmstate_handler = + qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl); qxl->update_irq = qemu_bh_new_guarded(qxl_update_irq_bh, qxl, &DEVICE(qxl)->mem_reentrancy_guard); @@ -2475,6 +2535,18 @@ static const Property qxl_properties[] = { DEFINE_PROP_UINT32("yres", PCIQXLDevice, yres, 0), }; +static void qxl_exit(PCIDevice *dev) +{ + PCIQXLDevice *qxl = PCI_QXL(dev); + + /* TODO: complete cleanup, error paths etc */ + g_clear_pointer(&qxl->vmstate_handler, qemu_del_vm_change_state_handler); + g_clear_pointer(&qxl->update_irq, qemu_bh_delete); + g_clear_pointer(&qxl->update_area_bh, qemu_bh_delete); + g_clear_pointer(&qxl->ssd.cursor_bh, qemu_bh_delete); + g_clear_pointer(&qxl->guest_surfaces.cmds, g_free); +} + static void qxl_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -2482,6 +2554,7 @@ static void qxl_pci_class_init(ObjectClass *klass, const void *data) k->vendor_id = REDHAT_PCI_VENDOR_ID; k->device_id = QXL_DEVICE_ID_STABLE; + k->exit = qxl_exit; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); device_class_set_legacy_reset(dc, qxl_reset_handler); dc->vmsd = &qxl_vmstate; diff --git a/hw/display/qxl.h b/hw/display/qxl.h index ad8a912878..6f5b96fe86 100644 --- a/hw/display/qxl.h +++ b/hw/display/qxl.h @@ -83,6 +83,7 @@ struct PCIQXLDevice { /* thread signaling */ QEMUBH *update_irq; + VMChangeStateEntry *vmstate_handler; /* ram pci bar */ QXLRam *ram; @@ -180,6 +181,8 @@ void qxl_spice_oom(PCIQXLDevice *qxl); void qxl_spice_reset_memslots(PCIQXLDevice *qxl); void qxl_spice_reset_image_cache(PCIQXLDevice *qxl); void qxl_spice_reset_cursor(PCIQXLDevice *qxl); +bool qxl_format_bpp(PCIQXLDevice *qxl, SpiceSurfaceFmt format, + uint32_t *bytes_pp, uint32_t *bits_pp); /* qxl-logger.c */ int qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id); diff --git a/hw/display/vga.c b/hw/display/vga.c index abe3f8e077..cb0e28b79b 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -1241,7 +1241,10 @@ static void vga_draw_text(VGACommonState *s, int full_update) return; } - if (width != s->last_width || height != s->last_height || + if (surface == NULL || + surface_width(surface) != width * cw || + surface_height(surface) != height * cheight || + width != s->last_text_width || height != s->last_text_height || cw != s->last_cw || cheight != s->last_ch || s->last_depth) { s->last_scr_width = width * cw; s->last_scr_height = height * cheight; @@ -1249,8 +1252,8 @@ static void vga_draw_text(VGACommonState *s, int full_update) surface = qemu_console_surface(s->con); qemu_console_text_resize(s->con, width, height); s->last_depth = 0; - s->last_width = width; - s->last_height = height; + s->last_text_width = width; + s->last_text_height = height; s->last_ch = cheight; s->last_cw = cw; full_update = 1; @@ -1647,11 +1650,12 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) s->last_line_offset = s->params.line_offset; s->last_depth = depth; s->last_byteswap = byteswap; - /* 16 extra pixels are needed for double-width planar modes. */ - s->panning_buf = g_realloc(s->panning_buf, - (disp_width + 16) * sizeof(uint32_t)); full_update = 1; } + + /* 16 extra pixels are needed for double-width planar modes. */ + s->panning_buf = g_realloc(s->panning_buf, + (disp_width + 16) * sizeof(uint32_t)); if (surface_data(surface) != s->vram_ptr + (s->params.start_addr * 4) && !surface_is_allocated(surface)) { /* base address changed (page flip) -> shared display surfaces @@ -1844,6 +1848,8 @@ static void vga_invalidate_display(void *opaque) s->last_width = -1; s->last_height = -1; + s->last_text_width = -1; + s->last_text_height = -1; } void vga_common_reset(VGACommonState *s) @@ -1886,6 +1892,8 @@ void vga_common_reset(VGACommonState *s) s->last_ch = 0; s->last_width = 0; s->last_height = 0; + s->last_text_width = 0; + s->last_text_height = 0; s->last_scr_width = 0; s->last_scr_height = 0; s->cursor_start = 0; @@ -1937,8 +1945,8 @@ static void vga_update_text(void *opaque, uint32_t *chardata) s->graphic_mode = graphic_mode; full_update = 1; } - if (s->last_width == -1) { - s->last_width = 0; + if (s->last_text_width == -1) { + s->last_text_width = 0; full_update = 1; } @@ -1977,15 +1985,15 @@ static void vga_update_text(void *opaque, uint32_t *chardata) break; } - if (width != s->last_width || height != s->last_height || + if (width != s->last_text_width || height != s->last_text_height || cw != s->last_cw || cheight != s->last_ch) { s->last_scr_width = width * cw; s->last_scr_height = height * cheight; qemu_console_resize(s->con, s->last_scr_width, s->last_scr_height); qemu_console_text_resize(s->con, width, height); s->last_depth = 0; - s->last_width = width; - s->last_height = height; + s->last_text_width = width; + s->last_text_height = height; s->last_ch = cheight; s->last_cw = cw; full_update = 1; @@ -2070,22 +2078,22 @@ static void vga_update_text(void *opaque, uint32_t *chardata) } /* Display a message */ - s->last_width = 60; - s->last_height = height = 3; + s->last_text_width = 60; + s->last_text_height = height = 3; qemu_console_text_set_cursor(s->con, -1, -1); - qemu_console_text_resize(s->con, s->last_width, height); + qemu_console_text_resize(s->con, s->last_text_width, height); - for (dst = chardata, i = 0; i < s->last_width * height; i ++) + for (dst = chardata, i = 0; i < s->last_text_width * height; i ++) *dst++ = ' '; size = strlen(msg_buffer); - width = (s->last_width - size) / 2; - dst = chardata + s->last_width + width; + width = (s->last_text_width - size) / 2; + dst = chardata + s->last_text_width + width; for (i = 0; i < size; i ++) *dst++ = ATTR2CHTYPE(msg_buffer[i], QEMU_COLOR_BLUE, QEMU_COLOR_BLACK, 1); - qemu_console_text_update(s->con, 0, 0, s->last_width, height); + qemu_console_text_update(s->con, 0, 0, s->last_text_width, height); } static uint64_t vga_mem_read(void *opaque, hwaddr addr, diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h index 5664317ecd..ca69ae9815 100644 --- a/hw/display/vga_int.h +++ b/hw/display/vga_int.h @@ -122,7 +122,8 @@ typedef struct VGACommonState { uint32_t plane_updated; uint32_t last_line_offset; uint8_t last_cw, last_ch; - uint32_t last_width, last_height; /* in chars or pixels */ + uint32_t last_width, last_height; /* in pixels (graphics renderer) */ + uint32_t last_text_width, last_text_height; /* in chars (text renderer) */ uint32_t last_scr_width, last_scr_height; /* in pixels */ uint32_t last_depth; /* in bits */ bool last_byteswap; diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index 57360898ca..cd684d6363 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -119,6 +119,31 @@ static VhostUserGpuMsg m __attribute__ ((unused)); static void vhost_user_gpu_update_blocked(VhostUserGPU *g, bool blocked); +static size_t +vhost_user_gpu_min_payload_size(VhostUserGpuRequest request) +{ + switch (request) { + case VHOST_USER_GPU_CURSOR_POS: + case VHOST_USER_GPU_CURSOR_POS_HIDE: + return sizeof(VhostUserGpuCursorPos); + case VHOST_USER_GPU_CURSOR_UPDATE: + return sizeof(VhostUserGpuCursorUpdate); + case VHOST_USER_GPU_GET_EDID: + return sizeof(VhostUserGpuEdidRequest); + case VHOST_USER_GPU_SCANOUT: + return sizeof(VhostUserGpuScanout); + case VHOST_USER_GPU_DMABUF_SCANOUT: + return sizeof(VhostUserGpuDMABUFScanout); + case VHOST_USER_GPU_DMABUF_SCANOUT2: + return sizeof(VhostUserGpuDMABUFScanout2); + case VHOST_USER_GPU_DMABUF_UPDATE: + case VHOST_USER_GPU_UPDATE: + return sizeof(VhostUserGpuUpdate); + default: + return 0; + } +} + static void vhost_user_gpu_handle_cursor(VhostUserGPU *g, VhostUserGpuMsg *msg) { @@ -322,6 +347,14 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) if (m->scanout_id >= g->parent_obj.conf.max_outputs) { break; } + + if ((uint64_t)m->width * m->height > + (msg->size - sizeof(VhostUserGpuUpdate)) / sizeof(uint32_t)) { + error_report("vhost-user-gpu: update payload too small" + " for %ux%u", m->width, m->height); + break; + } + s = &g->parent_obj.scanout[m->scanout_id]; con = s->con; pixman_image_t *image = @@ -396,6 +429,11 @@ vhost_user_gpu_chr_read(void *opaque) msg->flags = flags; msg->size = size; + if (size < vhost_user_gpu_min_payload_size(request)) { + error_report("vhost-user-gpu: message %d payload too small", request); + goto end; + } + if (request == VHOST_USER_GPU_CURSOR_UPDATE || request == VHOST_USER_GPU_CURSOR_POS || request == VHOST_USER_GPU_CURSOR_POS_HIDE) { diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c index a68b184829..270fbaae10 100644 --- a/hw/display/virtio-gpu-base.c +++ b/hw/display/virtio-gpu-base.c @@ -69,7 +69,7 @@ virtio_gpu_base_generate_edid(VirtIOGPUBase *g, int scanout, for (output_idx = 0, node = g->conf.outputs; output_idx <= scanout && node; output_idx++, node = node->next) { - if (output_idx == scanout && node->value && node->value->name) { + if (output_idx == scanout && node->value->name) { info.name = node->value->name; break; } @@ -195,18 +195,34 @@ virtio_gpu_base_device_realize(DeviceState *qdev, return false; } + g->enabled_output_bitmask = 1; + + g->req_state[0].width = g->conf.xres; + g->req_state[0].height = g->conf.yres; + for (output_idx = 0, node = g->conf.outputs; node; output_idx++, node = node->next) { if (output_idx == g->conf.max_outputs) { error_setg(errp, "invalid outputs > %d", g->conf.max_outputs); return false; } - if (node->value && node->value->name && + if (node->value->name && strlen(node->value->name) > EDID_NAME_MAX_LENGTH) { error_setg(errp, "invalid output name '%s' > %d", node->value->name, EDID_NAME_MAX_LENGTH); return false; } + if (node->value->has_xres != node->value->has_yres) { + error_setg(errp, + "must set both outputs[%zd].xres and outputs[%zd].yres", + output_idx, output_idx); + return false; + } + if (node->value->has_xres && node->value->has_yres) { + g->enabled_output_bitmask |= (1 << output_idx); + g->req_state[output_idx].width = node->value->xres; + g->req_state[output_idx].height = node->value->yres; + } } if (virtio_gpu_virgl_enabled(g->conf)) { @@ -229,27 +245,6 @@ virtio_gpu_base_device_realize(DeviceState *qdev, virtio_add_queue(vdev, 16, cursor_cb); } - g->enabled_output_bitmask = 1; - - g->req_state[0].width = g->conf.xres; - g->req_state[0].height = g->conf.yres; - - for (output_idx = 0, node = g->conf.outputs; - node && output_idx < g->conf.max_outputs; - output_idx++, node = node->next) { - if (node->value->has_xres != node->value->has_yres) { - error_setg(errp, - "must set both outputs[%zd].xres and outputs[%zd].yres", - output_idx, output_idx); - return false; - } - if (node->value->has_xres && node->value->has_yres) { - g->enabled_output_bitmask |= (1 << output_idx); - g->req_state[output_idx].width = node->value->xres; - g->req_state[output_idx].height = node->value->yres; - } - } - g->hw_ops = &virtio_gpu_ops; for (i = 0; i < g->conf.max_outputs; i++) { g->scanout[i].con = diff --git a/hw/display/virtio-gpu-rutabaga.c b/hw/display/virtio-gpu-rutabaga.c index 6ff1263901..041216a10d 100644 --- a/hw/display/virtio-gpu-rutabaga.c +++ b/hw/display/virtio-gpu-rutabaga.c @@ -9,6 +9,7 @@ #include "hw/virtio/virtio-gpu.h" #include "hw/virtio/virtio-gpu-pixman.h" #include "hw/virtio/virtio-iommu.h" +#include "migration/blocker.h" #include #include @@ -302,7 +303,7 @@ rutabaga_cmd_set_scanout(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id, ss.r.width, ss.r.height, ss.r.x, ss.r.y); - CHECK(ss.scanout_id < VIRTIO_GPU_MAX_SCANOUTS, cmd); + CHECK(ss.scanout_id < vb->conf.max_outputs, cmd); scanout = &vb->scanout[ss.scanout_id]; if (ss.resource_id == 0) { @@ -314,6 +315,12 @@ rutabaga_cmd_set_scanout(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) res = virtio_gpu_find_resource(g, ss.resource_id); CHECK(res, cmd); + if (!virtio_gpu_check_scanout_bounds(ss.scanout_id, ss.resource_id, + res->width, res->height, &ss.r, + &cmd->error)) { + return; + } + if (!res->image) { pixman_format_code_t pformat; pformat = virtio_gpu_get_pixman_format(res->format); @@ -351,10 +358,28 @@ rutabaga_cmd_submit_3d(VirtIOGPU *g, VIRTIO_GPU_FILL_CMD(cs); trace_virtio_gpu_cmd_ctx_submit(cs.hdr.ctx_id, cs.size); - buf = g_new0(uint8_t, cs.size); + if (cs.size > VIRTIO_GPU_MAX_CMD_SUBMIT_SIZE) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: command buffer too large (%u)\n", + __func__, cs.size); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + + buf = g_try_new0(uint8_t, cs.size); + if (!buf && cs.size) { + cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY; + return; + } s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, sizeof(cs), buf, cs.size); - CHECK(s == cs.size, cmd); + if (s != cs.size) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: size mismatch (%zu/%u)\n", + __func__, s, cs.size); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } rutabaga_cmd.ctx_id = cs.hdr.ctx_id; rutabaga_cmd.cmd = buf; @@ -547,6 +572,8 @@ rutabaga_cmd_get_capset_info(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) VIRTIO_GPU_FILL_CMD(info); + memset(&resp, 0, sizeof(resp)); + result = rutabaga_get_capset_info(vr->rutabaga, info.capset_index, &resp.capset_id, &resp.capset_max_version, &resp.capset_max_size); @@ -1070,6 +1097,7 @@ static void virtio_gpu_rutabaga_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) static void virtio_gpu_rutabaga_realize(DeviceState *qdev, Error **errp) { + ERRP_GUARD(); uint32_t num_capsets; VirtIOGPUBase *bdev = VIRTIO_GPU_BASE(qdev); VirtIOGPU *gpudev = VIRTIO_GPU(qdev); @@ -1079,12 +1107,17 @@ static void virtio_gpu_rutabaga_realize(DeviceState *qdev, Error **errp) return; #endif - if (!virtio_gpu_rutabaga_init(gpudev, errp)) { + error_setg(&bdev->migration_blocker, "rutabaga is not yet migratable"); + if (migrate_add_blocker(&bdev->migration_blocker, errp) < 0) { return; } + if (!virtio_gpu_rutabaga_init(gpudev, errp)) { + goto fail; + } + if (!virtio_gpu_rutabaga_get_num_capsets(gpudev, &num_capsets, errp)) { - return; + goto fail; } bdev->conf.flags |= (1 << VIRTIO_GPU_FLAG_RUTABAGA_ENABLED); @@ -1093,6 +1126,12 @@ static void virtio_gpu_rutabaga_realize(DeviceState *qdev, Error **errp) bdev->virtio_config.num_capsets = num_capsets; virtio_gpu_device_realize(qdev, errp); + if (!*errp) { + return; + } + +fail: + migrate_del_blocker(&bdev->migration_blocker); } static const Property virtio_gpu_rutabaga_properties[] = { diff --git a/hw/display/virtio-gpu-udmabuf-stubs.c b/hw/display/virtio-gpu-udmabuf-stubs.c index 85d03935a3..0883bf05fa 100644 --- a/hw/display/virtio-gpu-udmabuf-stubs.c +++ b/hw/display/virtio-gpu-udmabuf-stubs.c @@ -7,9 +7,10 @@ bool virtio_gpu_have_udmabuf(void) return false; } -void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res) +bool virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res) { /* nothing (stub) */ + return false; } void virtio_gpu_fini_udmabuf(VirtIOGPU *g, struct virtio_gpu_simple_resource *res) diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c index d5ac1cfca0..c230509852 100644 --- a/hw/display/virtio-gpu-udmabuf.c +++ b/hw/display/virtio-gpu-udmabuf.c @@ -39,8 +39,11 @@ static void virtio_gpu_create_udmabuf(struct virtio_gpu_simple_resource *res) return; } - list = g_malloc0(sizeof(struct udmabuf_create_list) + - sizeof(struct udmabuf_create_item) * res->iov_cnt); + list = g_try_malloc0(sizeof(struct udmabuf_create_list) + + sizeof(struct udmabuf_create_item) * res->iov_cnt); + if (!list) { + return; + } for (i = 0; i < res->iov_cnt; i++) { rcu_read_lock(); @@ -128,7 +131,7 @@ bool virtio_gpu_have_udmabuf(void) return memfd_backend; } -void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res) +bool virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res) { void *pdata = NULL; @@ -136,19 +139,22 @@ void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res) if (res->iov_cnt == 1 && res->iov[0].iov_len < 4096) { pdata = res->iov[0].iov_base; - } else { + } else if (res->blob_size) { virtio_gpu_create_udmabuf(res); if (res->dmabuf_fd < 0) { - return; + return false; } virtio_gpu_remap_udmabuf(res); if (!res->remapped) { - return; + virtio_gpu_destroy_udmabuf(res); + return false; } pdata = res->remapped; } res->blob = pdata; + + return true; } static void virtio_gpu_free_dmabuf(VirtIOGPU *g, VGPUDMABuf *dmabuf) diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c index 60c78af06a..9bda572426 100644 --- a/hw/display/virtio-gpu-virgl.c +++ b/hw/display/virtio-gpu-virgl.c @@ -560,7 +560,7 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, } g->parent_obj.enable = 1; - if (ss.resource_id && ss.r.width && ss.r.height) { + if (ss.resource_id) { struct virgl_renderer_resource_info info; void *d3d_tex2d = NULL; @@ -581,6 +581,11 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; return; } + if (!virtio_gpu_check_scanout_bounds(ss.scanout_id, ss.resource_id, + info.width, info.height, &ss.r, + &cmd->error)) { + return; + } qemu_console_resize(g->parent_obj.scanout[ss.scanout_id].con, ss.r.width, ss.r.height); virgl_renderer_force_ctx_0(); @@ -607,7 +612,19 @@ static void virgl_cmd_submit_3d(VirtIOGPU *g, VIRTIO_GPU_FILL_CMD(cs); trace_virtio_gpu_cmd_ctx_submit(cs.hdr.ctx_id, cs.size); - buf = g_malloc(cs.size); + if (cs.size > VIRTIO_GPU_MAX_CMD_SUBMIT_SIZE) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: command buffer too large (%u)\n", + __func__, cs.size); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + + buf = g_try_malloc(cs.size); + if (!buf && cs.size) { + cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY; + return; + } s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, sizeof(cs), buf, cs.size); if (s != cs.size) { @@ -979,16 +996,9 @@ static void virgl_cmd_set_scanout_blob(VirtIOGPU *g, return; } - if (ss.width < 16 || - ss.height < 16 || - ss.r.x + ss.r.width > ss.width || - ss.r.y + ss.r.height > ss.height) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for" - " resource %d, rect (%d,%d)+%d,%d, fb %d %d\n", - __func__, ss.scanout_id, ss.resource_id, - ss.r.x, ss.r.y, ss.r.width, ss.r.height, - ss.width, ss.height); - cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + if (!virtio_gpu_check_scanout_bounds(ss.scanout_id, ss.resource_id, + ss.width, ss.height, &ss.r, + &cmd->error)) { return; } diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 88526051a9..55a1c7f80f 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -37,7 +37,6 @@ static struct virtio_gpu_simple_resource * virtio_gpu_find_check_resource(VirtIOGPU *g, uint32_t resource_id, - bool require_backing, const char *caller, uint32_t *error); static void virtio_gpu_reset_bh(void *opaque); @@ -50,24 +49,23 @@ void virtio_gpu_update_cursor_data(VirtIOGPU *g, uint32_t pixels; void *data; - res = virtio_gpu_find_check_resource(g, resource_id, false, - __func__, NULL); + res = virtio_gpu_find_check_resource(g, resource_id, __func__, NULL); if (!res) { return; } - if (res->blob_size) { - if (res->blob_size < (s->current_cursor->width * - s->current_cursor->height * 4)) { - return; - } - data = res->blob; - } else { + if (res->image) { if (pixman_image_get_width(res->image) != s->current_cursor->width || pixman_image_get_height(res->image) != s->current_cursor->height) { return; } data = pixman_image_get_data(res->image); + } else { + if (!res->iov || res->blob_size < (s->current_cursor->width * + s->current_cursor->height * 4)) { + return; + } + data = res->blob; } pixels = s->current_cursor->width * s->current_cursor->height; @@ -128,7 +126,6 @@ virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id) static struct virtio_gpu_simple_resource * virtio_gpu_find_check_resource(VirtIOGPU *g, uint32_t resource_id, - bool require_backing, const char *caller, uint32_t *error) { struct virtio_gpu_simple_resource *res; @@ -143,17 +140,6 @@ virtio_gpu_find_check_resource(VirtIOGPU *g, uint32_t resource_id, return NULL; } - if (require_backing) { - if (!res->iov || (!res->image && !res->blob)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: no backing storage %d\n", - caller, resource_id); - if (error) { - *error = VIRTIO_GPU_RESP_ERR_UNSPEC; - } - return NULL; - } - } - return res; } @@ -363,16 +349,34 @@ static void virtio_gpu_resource_create_blob(VirtIOGPU *g, res->resource_id = cblob.resource_id; res->blob_size = cblob.size; - ret = virtio_gpu_create_mapping_iov(g, cblob.nr_entries, sizeof(cblob), - cmd, &res->addrs, &res->iov, - &res->iov_cnt); - if (ret < 0) { - cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; - g_free(res); - return; + if (cblob.nr_entries) { + ret = virtio_gpu_create_mapping_iov(g, cblob.nr_entries, sizeof(cblob), + cmd, &res->addrs, &res->iov, + &res->iov_cnt); + if (ret < 0) { + cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; + g_free(res); + return; + } + + if (iov_size(res->iov, res->iov_cnt) < res->blob_size) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: backing storage smaller than blob size\n", + __func__); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + virtio_gpu_cleanup_mapping(g, res); + g_free(res); + return; + } + + if (!virtio_gpu_init_udmabuf(res)) { + cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; + virtio_gpu_cleanup_mapping(g, res); + g_free(res); + return; + } } - virtio_gpu_init_udmabuf(res); QTAILQ_INSERT_HEAD(&g->reslist, res, next); } @@ -456,9 +460,24 @@ static void virtio_gpu_transfer_to_host_2d(VirtIOGPU *g, virtio_gpu_t2d_bswap(&t2d); trace_virtio_gpu_cmd_res_xfer_toh_2d(t2d.resource_id); - res = virtio_gpu_find_check_resource(g, t2d.resource_id, true, + res = virtio_gpu_find_check_resource(g, t2d.resource_id, __func__, &cmd->error); - if (!res || res->blob) { + if (!res) { + return; + } + + if (!res->image) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: resource %d is a blob\n", + __func__, t2d.resource_id); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + if (!res->iov) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: resource %d has no backing storage\n", + __func__, t2d.resource_id); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; return; } @@ -515,7 +534,7 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g, trace_virtio_gpu_cmd_res_flush(rf.resource_id, rf.r.width, rf.r.height, rf.r.x, rf.r.y); - res = virtio_gpu_find_check_resource(g, rf.resource_id, false, + res = virtio_gpu_find_check_resource(g, rf.resource_id, __func__, &cmd->error); if (!res) { return; @@ -617,6 +636,31 @@ void virtio_gpu_update_scanout(VirtIOGPU *g, scanout->fb = *fb; } +static uint32_t virtio_gpu_format_bytes_pp(pixman_format_code_t format) +{ + return DIV_ROUND_UP(PIXMAN_FORMAT_BPP(format), 8); +} + +bool virtio_gpu_check_scanout_bounds(uint32_t scanout_id, uint32_t resource_id, + uint32_t width, uint32_t height, + const struct virtio_gpu_rect *r, + uint32_t *error) +{ + if (r->width < 16 || + r->height < 16 || + (uint64_t)r->x + r->width > width || + (uint64_t)r->y + r->height > height) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for" + " resource %d, fb %d %d, rect (%d,%d)+%d,%d\n", + __func__, scanout_id, resource_id, width, height, + r->x, r->y, r->width, r->height); + *error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return false; + } + + return true; +} + static bool virtio_gpu_do_set_scanout(VirtIOGPU *g, uint32_t scanout_id, struct virtio_gpu_framebuffer *fb, @@ -625,23 +669,28 @@ static bool virtio_gpu_do_set_scanout(VirtIOGPU *g, uint32_t *error) { struct virtio_gpu_scanout *scanout; + uint32_t bytes_pp = virtio_gpu_format_bytes_pp(fb->format); uint8_t *data; scanout = &g->parent_obj.scanout[scanout_id]; - if (r->x > fb->width || - r->y > fb->height || - r->width < 16 || - r->height < 16 || - r->width > fb->width || - r->height > fb->height || - r->x + r->width > fb->width || - r->y + r->height > fb->height) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for" - " resource %d, rect (%d,%d)+%d,%d, fb %d %d\n", - __func__, scanout_id, res->resource_id, - r->x, r->y, r->width, r->height, - fb->width, fb->height); + if (!virtio_gpu_check_scanout_bounds(scanout_id, res->resource_id, + fb->width, fb->height, r, error)) { + return false; + } + + if (fb->stride < (uint64_t)fb->width * bytes_pp) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: stride %u too small for width %u at %u bpp\n", + __func__, fb->stride, fb->width, bytes_pp); + *error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return false; + } + + if (fb->stride > INT_MAX) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: stride is %" PRIu32 + ", larger than the supported maximum (%d)\n", + __func__, fb->stride, INT_MAX); *error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; return false; } @@ -674,6 +723,10 @@ static bool virtio_gpu_do_set_scanout(VirtIOGPU *g, void *ptr = data + fb->offset; rect = pixman_image_create_bits(fb->format, r->width, r->height, ptr, fb->stride); + if (!rect) { + *error = VIRTIO_GPU_RESP_ERR_UNSPEC; + return false; + } if (res->image) { pixman_image_ref(res->image); @@ -700,6 +753,7 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, struct virtio_gpu_simple_resource *res; struct virtio_gpu_framebuffer fb = { 0 }; struct virtio_gpu_set_scanout ss; + uint32_t bytes_pp; VIRTIO_GPU_FILL_CMD(ss); virtio_gpu_bswap_32(&ss, sizeof(ss)); @@ -718,18 +772,25 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, return; } - res = virtio_gpu_find_check_resource(g, ss.resource_id, true, + res = virtio_gpu_find_check_resource(g, ss.resource_id, __func__, &cmd->error); if (!res) { return; } + if (!res->image) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: resource %d is a blob\n", + __func__, ss.resource_id); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + fb.format = pixman_image_get_format(res->image); - fb.bytes_pp = DIV_ROUND_UP(PIXMAN_FORMAT_BPP(fb.format), 8); + bytes_pp = virtio_gpu_format_bytes_pp(fb.format); fb.width = pixman_image_get_width(res->image); fb.height = pixman_image_get_height(res->image); fb.stride = pixman_image_get_stride(res->image); - fb.offset = ss.r.x * fb.bytes_pp + ss.r.y * fb.stride; + fb.offset = ss.r.x * bytes_pp + ss.r.y * fb.stride; virtio_gpu_do_set_scanout(g, ss.scanout_id, &fb, res, &ss.r, &cmd->error); @@ -739,7 +800,8 @@ bool virtio_gpu_scanout_blob_to_fb(struct virtio_gpu_framebuffer *fb, struct virtio_gpu_set_scanout_blob *ss, uint64_t blob_size) { - uint64_t fbend; + uint64_t fbend, offset; + uint32_t bytes_pp; fb->format = virtio_gpu_get_pixman_format(ss->format); if (!fb->format) { @@ -749,22 +811,39 @@ bool virtio_gpu_scanout_blob_to_fb(struct virtio_gpu_framebuffer *fb, return false; } - fb->bytes_pp = DIV_ROUND_UP(PIXMAN_FORMAT_BPP(fb->format), 8); + bytes_pp = virtio_gpu_format_bytes_pp(fb->format); fb->width = ss->width; fb->height = ss->height; fb->stride = ss->strides[0]; - fb->offset = ss->offsets[0] + ss->r.x * fb->bytes_pp + ss->r.y * fb->stride; - fbend = fb->offset; - fbend += (uint64_t) fb->stride * ss->r.height; - - if (fbend > blob_size) { + if (fb->stride < (uint64_t)fb->width * bytes_pp) { qemu_log_mask(LOG_GUEST_ERROR, - "%s: fb end out of range\n", + "%s: stride %u too small for width %u at %u bpp\n", + __func__, fb->stride, fb->width, bytes_pp); + return false; + } + + if (fb->stride > INT_MAX) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: stride is %" PRIu32 + ", larger than the supported maximum (%d)\n", + __func__, fb->stride, INT_MAX); + return false; + } + + offset = (uint64_t)ss->offsets[0] + (uint64_t)ss->r.x * bytes_pp + + (uint64_t)ss->r.y * fb->stride; + + fbend = offset + (uint64_t)fb->stride * ss->r.height; + + if (offset > UINT32_MAX || fbend > blob_size) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid fb bounds\n", __func__); return false; } + fb->offset = offset; + return true; } @@ -795,12 +874,28 @@ static void virtio_gpu_set_scanout_blob(VirtIOGPU *g, return; } - res = virtio_gpu_find_check_resource(g, ss.resource_id, true, + res = virtio_gpu_find_check_resource(g, ss.resource_id, __func__, &cmd->error); if (!res) { return; } + if (res->image) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: resource %d is not a blob\n", + __func__, ss.resource_id); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + if (!res->iov) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: resource %d has no backing storage\n", + __func__, ss.resource_id); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + if (!virtio_gpu_scanout_blob_to_fb(&fb, &ss, res->blob_size)) { cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; return; @@ -828,7 +923,10 @@ int virtio_gpu_create_mapping_iov(VirtIOGPU *g, } esize = sizeof(*ents) * nr_entries; - ents = g_malloc(esize); + ents = g_try_malloc(esize); + if (!ents && esize) { + return -1; + } s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, offset, ents, esize); if (s != esize) { @@ -849,6 +947,7 @@ int virtio_gpu_create_mapping_iov(VirtIOGPU *g, hwaddr len; void *map; + /* TODO: a common DMA map SG helper */ do { len = l; map = dma_memory_map(VIRTIO_DEVICE(g)->dma_as, a, &len, @@ -857,20 +956,27 @@ int virtio_gpu_create_mapping_iov(VirtIOGPU *g, if (!map) { qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to map MMIO memory for" " element %d\n", __func__, e); - virtio_gpu_cleanup_mapping_iov(g, *iov, v); - g_free(ents); - *iov = NULL; - if (addr) { - g_free(*addr); - *addr = NULL; - } - return -1; + goto err; } if (!(v % 16)) { - *iov = g_renew(struct iovec, *iov, v + 16); + struct iovec *new_iov; + new_iov = g_try_renew(struct iovec, *iov, v + 16); + if (!new_iov) { + dma_memory_unmap(VIRTIO_DEVICE(g)->dma_as, map, len, + DMA_DIRECTION_TO_DEVICE, len); + goto err; + } + *iov = new_iov; if (addr) { - *addr = g_renew(uint64_t, *addr, v + 16); + uint64_t *new_addr; + new_addr = g_try_renew(uint64_t, *addr, v + 16); + if (!new_addr) { + dma_memory_unmap(VIRTIO_DEVICE(g)->dma_as, map, len, + DMA_DIRECTION_TO_DEVICE, len); + goto err; + } + *addr = new_addr; } } (*iov)[v].iov_base = map; @@ -888,6 +994,15 @@ int virtio_gpu_create_mapping_iov(VirtIOGPU *g, g_free(ents); return 0; + +err: + virtio_gpu_cleanup_mapping_iov(g, *iov, v); + *iov = NULL; + if (addr) { + g_clear_pointer(addr, g_free); + } + g_free(ents); + return -1; } void virtio_gpu_cleanup_mapping_iov(VirtIOGPU *g, @@ -949,6 +1064,20 @@ virtio_gpu_resource_attach_backing(VirtIOGPU *g, cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; return; } + + if (iov_size(res->iov, res->iov_cnt) < res->blob_size) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: backing storage smaller than blob size\n", + __func__); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + virtio_gpu_cleanup_mapping(g, res); + return; + } + + if (!res->image && !virtio_gpu_init_udmabuf(res)) { + cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; + virtio_gpu_cleanup_mapping(g, res); + } } static void @@ -962,7 +1091,7 @@ virtio_gpu_resource_detach_backing(VirtIOGPU *g, virtio_gpu_bswap_32(&detach, sizeof(detach)); trace_virtio_gpu_cmd_res_back_detach(detach.resource_id); - res = virtio_gpu_find_check_resource(g, detach.resource_id, true, + res = virtio_gpu_find_check_resource(g, detach.resource_id, __func__, &cmd->error); if (!res) { return; @@ -1203,8 +1332,7 @@ static const VMStateDescription vmstate_virtio_gpu_scanout = { VMSTATE_UINT32(cursor.pos.y, struct virtio_gpu_scanout), VMSTATE_UINT32_TEST(fb.format, struct virtio_gpu_scanout, scanout_vmstate_after_v2), - VMSTATE_UINT32_TEST(fb.bytes_pp, struct virtio_gpu_scanout, - scanout_vmstate_after_v2), + VMSTATE_UNUSED_TEST(scanout_vmstate_after_v2, 4), VMSTATE_UINT32_TEST(fb.width, struct virtio_gpu_scanout, scanout_vmstate_after_v2), VMSTATE_UINT32_TEST(fb.height, struct virtio_gpu_scanout, @@ -1244,7 +1372,7 @@ static int virtio_gpu_save(QEMUFile *f, void *opaque, size_t size, assert(QTAILQ_EMPTY(&g->cmdq)); QTAILQ_FOREACH(res, &g->reslist, next) { - if (res->blob_size) { + if (!res->image) { continue; } qemu_put_be32(f, res->resource_id); @@ -1293,8 +1421,6 @@ static bool virtio_gpu_load_restore_mapping(VirtIOGPU *g, } } - QTAILQ_INSERT_HEAD(&g->reslist, res, next); - g->hostmem += res->hostmem; return true; } @@ -1349,8 +1475,15 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, return -EINVAL; } - res->addrs = g_new(uint64_t, res->iov_cnt); - res->iov = g_new(struct iovec, res->iov_cnt); + res->addrs = g_try_new(uint64_t, res->iov_cnt); + res->iov = g_try_new(struct iovec, res->iov_cnt); + if (res->iov_cnt && (!res->addrs || !res->iov)) { + pixman_image_unref(res->image); + g_free(res->addrs); + g_free(res->iov); + g_free(res); + return -EINVAL; + } /* read data */ for (i = 0; i < res->iov_cnt; i++) { @@ -1366,6 +1499,8 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, return -EINVAL; } + QTAILQ_INSERT_HEAD(&g->reslist, res, next); + g->hostmem += hostmem; resource_id = qemu_get_be32(f); } @@ -1388,7 +1523,7 @@ static int virtio_gpu_blob_save(QEMUFile *f, void *opaque, size_t size, assert(QTAILQ_EMPTY(&g->cmdq)); QTAILQ_FOREACH(res, &g->reslist, next) { - if (!res->blob_size) { + if (res->image) { continue; } assert(!res->image); @@ -1424,22 +1559,43 @@ static int virtio_gpu_blob_load(QEMUFile *f, void *opaque, size_t size, res->resource_id = resource_id; res->blob_size = qemu_get_be32(f); res->iov_cnt = qemu_get_be32(f); - res->addrs = g_new(uint64_t, res->iov_cnt); - res->iov = g_new(struct iovec, res->iov_cnt); - /* read data */ - for (i = 0; i < res->iov_cnt; i++) { - res->addrs[i] = qemu_get_be64(f); - res->iov[i].iov_len = qemu_get_be32(f); + if (res->iov_cnt) { + res->addrs = g_try_new(uint64_t, res->iov_cnt); + res->iov = g_try_new(struct iovec, res->iov_cnt); + if (!res->addrs || !res->iov) { + g_free(res->addrs); + g_free(res->iov); + g_free(res); + return -EINVAL; + } + + /* read data */ + for (i = 0; i < res->iov_cnt; i++) { + res->addrs[i] = qemu_get_be64(f); + res->iov[i].iov_len = qemu_get_be32(f); + } + + if (iov_size(res->iov, res->iov_cnt) < res->blob_size) { + g_free(res->addrs); + g_free(res->iov); + g_free(res); + return -EINVAL; + } + + if (!virtio_gpu_load_restore_mapping(g, res)) { + g_free(res); + return -EINVAL; + } + + if (!virtio_gpu_init_udmabuf(res)) { + virtio_gpu_cleanup_mapping(g, res); + g_free(res); + return -EINVAL; + } } - if (!virtio_gpu_load_restore_mapping(g, res)) { - g_free(res); - return -EINVAL; - } - - virtio_gpu_init_udmabuf(res); - + QTAILQ_INSERT_HEAD(&g->reslist, res, next); resource_id = qemu_get_be32(f); } @@ -1627,12 +1783,14 @@ void virtio_gpu_reset(VirtIODevice *vdev) while (!QTAILQ_EMPTY(&g->cmdq)) { cmd = QTAILQ_FIRST(&g->cmdq); QTAILQ_REMOVE(&g->cmdq, cmd, next); + virtqueue_detach_element(cmd->vq, &cmd->elem, 0); g_free(cmd); } while (!QTAILQ_EMPTY(&g->fenceq)) { cmd = QTAILQ_FIRST(&g->fenceq); QTAILQ_REMOVE(&g->fenceq, cmd, next); + virtqueue_detach_element(cmd->vq, &cmd->elem, 0); g->inflight--; g_free(cmd); } diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index f6f9edfd1d..567806f0e4 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -737,6 +737,10 @@ static void vmsvga_fifo_run(struct vmsvga_state_s *s) vmsvga_fifo_read(s); x = vmsvga_fifo_read(s); y = vmsvga_fifo_read(s); + if (x < 0 || x >= SVGA_MAX_WIDTH || + y < 0 || y >= SVGA_MAX_HEIGHT) { + goto rewind; + } args = x * y; goto badcmd; case SVGA_CMD_RECT_ROP_FILL: @@ -776,7 +780,7 @@ static void vmsvga_fifo_run(struct vmsvga_state_s *s) if (len < 0) { goto rewind; } - while (args--) { + while (args-- > 0) { vmsvga_fifo_read(s); } printf("%s: Unknown command 0x%02x in SVGA command FIFO\n", diff --git a/hw/dma/Kconfig b/hw/dma/Kconfig index 98fbb1bb04..0a23e7d7b8 100644 --- a/hw/dma/Kconfig +++ b/hw/dma/Kconfig @@ -30,3 +30,6 @@ config SIFIVE_PDMA config XLNX_CSU_DMA bool select REGISTER + +config K230_GSDMA + bool \ No newline at end of file diff --git a/hw/dma/k230_gsdma.c b/hw/dma/k230_gsdma.c new file mode 100644 index 0000000000..7635cd19e8 --- /dev/null +++ b/hw/dma/k230_gsdma.c @@ -0,0 +1,551 @@ +/* + * Kendryte K230 GSDMA + * + * Copyright (c) 2026 Tao Ding + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * GSDMA includes SDMA (System Direct Memory Access, K230 TRM section 10.2) and + * GDMA (Graphic Direct Memory Access, K230 TRM section 2.5.2). + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "migration/vmstate.h" +#include "system/address-spaces.h" +#include "hw/dma/k230_gsdma.h" +#include "hw/core/irq.h" +#include "trace.h" + +static void k230_gsdma_update_irq(K230GSDMAState *s) +{ + qemu_set_irq(s->irq, !!(s->dma_int_stat & ~s->dma_int_mask)); +} + +static bool k230_gsdma_decomp_enabled(K230GSDMAState *s) +{ + /* Only sdma channel 0 has decomp enable flag */ + return !!(s->channels[0].cfg & K230_GSDMA_CH0_CFG_DECOMP_CTRL_EN); +} + +static bool k230_gsdma_read_sdma_llt(hwaddr addr, K230GSDMALLT *llt) +{ + bool ok = address_space_read(&address_space_memory, addr, + MEMTXATTRS_UNSPECIFIED, llt, + sizeof(*llt)) == MEMTX_OK; + + if (ok) { + trace_k230_gsdma_read_sdma_llt(addr, le32_to_cpu(llt->cfg), + le32_to_cpu(llt->src_addr), + le32_to_cpu(llt->line_size), + le32_to_cpu(llt->line_cfg), + le32_to_cpu(llt->dst_addr), + le32_to_cpu(llt->next_llt_addr)); + } else { + trace_k230_gsdma_read_sdma_llt_failed(addr); + } + + return ok; +} + +static unsigned int k230_gsdma_usr_data_size(uint32_t cfg) +{ + /* decode usr_dat_size in CH_CFG register */ + switch (extract32(cfg, K230_GSDMA_CH_CFG_USR_DATA_SIZE_SHIFT, 2)) { + case 0: + return 1; + case 1: + return 2; + default: + return 4; + } +} + +static void k230_gsdma_convert_endian(uint8_t *buf, uint32_t len, uint32_t cfg) +{ + unsigned int mode = extract32(cfg, + K230_GSDMA_CH_CFG_DAT_ENDIAN_SHIFT, 2); + unsigned int unit_size; + unsigned int offset; + + if (mode == 0) { + return; + } + + unit_size = 1U << mode; + for (offset = 0; offset + unit_size <= len; offset += unit_size) { + unsigned int i; + + for (i = 0; i < unit_size / 2; i++) { + uint8_t tmp = buf[offset + i]; + + buf[offset + i] = buf[offset + unit_size - i - 1]; + buf[offset + unit_size - i - 1] = tmp; + } + } +} + +static bool k230_gsdma_transfer_llt(K230GSDMAChannel *c, const K230GSDMALLT *llt) +{ + uint32_t llt_cfg = le32_to_cpu(llt->cfg); + hwaddr src = le32_to_cpu(llt->src_addr); + hwaddr dst = le32_to_cpu(llt->dst_addr); + uint32_t line_size = le32_to_cpu(llt->line_size); + uint32_t line_cfg = le32_to_cpu(llt->line_cfg); + /* Number of lines required for 2d mode */ + uint32_t line_num = + (llt_cfg & K230_GSDMA_LLT_2D_MODE) ? extract32(line_cfg, 0, 16) : 1; + /* Stride line size required for 2d mode */ + uint32_t line_space = extract32(line_cfg, 16, 16); + + bool dat_mode = c->cfg & K230_GSDMA_CH_CFG_DAT_MODE; + bool src_fixed = c->cfg & K230_GSDMA_CH_CFG_SRC_FIXED; + bool dst_fixed = c->cfg & K230_GSDMA_CH_CFG_DST_FIXED; + + uint8_t buf[8]; /* K230 sdma axi data width is 64-bit */ + uint8_t fill[4]; /* Usr data is 32-bit*/ + unsigned int fill_len = k230_gsdma_usr_data_size(c->cfg); + uint32_t i; + + memcpy(fill, &c->usr_data, sizeof(fill)); + + for (i = 0; i < line_num; i++) { + uint32_t remaining = line_size; + hwaddr line_src = src; + hwaddr line_dst = dst; + + while (remaining) { + uint32_t chunk = MIN(remaining, (uint32_t)sizeof(buf)); + + if (dat_mode) { + uint32_t j; + + for (j = 0; j < chunk; j++) { + buf[j] = fill[j % fill_len]; + } + } else if (address_space_read(&address_space_memory, line_src, + MEMTXATTRS_UNSPECIFIED, buf, + chunk) != MEMTX_OK) { + return false; + } + + k230_gsdma_convert_endian(buf, chunk, c->cfg); + + if (address_space_write(&address_space_memory, line_dst, + MEMTXATTRS_UNSPECIFIED, buf, + chunk) != MEMTX_OK) { + return false; + } + + if (!dat_mode && !src_fixed) { + line_src += chunk; + } + if (!dst_fixed) { + line_dst += chunk; + } + remaining -= chunk; + } + + if (!dat_mode && !src_fixed) { + if (llt_cfg & K230_GSDMA_LLT_2D_MODE) { /* 2d mode */ + src += line_size + line_space; + } else { + src += line_size; + } + } + + if (!dst_fixed) { + dst += line_size; + } + } + + return true; +} + +static void k230_gsdma_sdma_set_idle(K230GSDMAChannel *c) +{ + c->status &= ~(K230_GSDMA_SDMA_STATUS_BUSY | K230_GSDMA_SDMA_STATUS_PAUSE); + c->next_llt = 0; +} + +static void k230_gsdma_sdma_set_paused(K230GSDMAChannel *c, hwaddr next_llt) +{ + c->status &= ~K230_GSDMA_SDMA_STATUS_BUSY; + c->status |= K230_GSDMA_SDMA_STATUS_PAUSE; + c->next_llt = next_llt; +} + +static void k230_gsdma_run_sdma(K230GSDMAState *s, unsigned int ch, hwaddr addr) +{ + K230GSDMAChannel *c = &s->channels[ch]; + + c->status &= ~K230_GSDMA_SDMA_STATUS_PAUSE; + c->status |= K230_GSDMA_SDMA_STATUS_BUSY; + c->next_llt = 0; + c->current_llt = addr; + + while (addr) { + K230GSDMALLT llt; + uint32_t cfg; + hwaddr next; + + if (!k230_gsdma_read_sdma_llt(addr, &llt)) { + k230_gsdma_sdma_set_idle(c); + return; + } + + cfg = le32_to_cpu(llt.cfg); + next = le32_to_cpu(llt.next_llt_addr); + c->current_llt = addr; + + if (!k230_gsdma_transfer_llt(c, &llt)) { + k230_gsdma_sdma_set_idle(c); + return; + } + + if (cfg & K230_GSDMA_LLT_NODE_INTR) { + s->dma_int_stat |= K230_GSDMA_SDMA_ITEM_INT(ch); + } + + if (cfg & K230_GSDMA_LLT_PAUSE) { + s->dma_int_stat |= K230_GSDMA_SDMA_PAUSE_INT(ch); + k230_gsdma_sdma_set_paused(c, next); + k230_gsdma_update_irq(s); + return; + } + + addr = next; + } + + s->dma_int_stat |= K230_GSDMA_SDMA_DONE_INT(ch); + s->dma_ch_en &= ~BIT(ch); + k230_gsdma_sdma_set_idle(c); + k230_gsdma_update_irq(s); +} + +/* Decomp gzip request step sdma */ +static void k230_gsdma_step_sdma(K230GSDMAState *s, unsigned int ch, hwaddr addr) +{ + K230GSDMAChannel *c = &s->channels[ch]; + K230GSDMALLT llt; + uint32_t cfg; + hwaddr next; + + if (!addr || !k230_gsdma_read_sdma_llt(addr, &llt)) { + k230_gsdma_sdma_set_idle(c); + return; + } + + cfg = le32_to_cpu(llt.cfg); + next = le32_to_cpu(llt.next_llt_addr); + + c->status |= K230_GSDMA_SDMA_STATUS_BUSY; + c->status &= ~K230_GSDMA_SDMA_STATUS_PAUSE; + c->current_llt = addr; + + if (!k230_gsdma_transfer_llt(c, &llt)) { + k230_gsdma_sdma_set_idle(c); + return; + } + + if (cfg & K230_GSDMA_LLT_NODE_INTR) { + s->dma_int_stat |= K230_GSDMA_SDMA_ITEM_INT(ch); + } + + c->status &= ~K230_GSDMA_SDMA_STATUS_BUSY; + c->next_llt = next; + if (next == 0) { + s->dma_int_stat |= K230_GSDMA_SDMA_DONE_INT(ch); + s->dma_ch_en &= ~BIT(ch); + } + + k230_gsdma_update_irq(s); +} + +static const VMStateDescription vmstate_k230_gsdma_channel = { + .name = "k230.gsdma.channel", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(ctl, K230GSDMAChannel), + VMSTATE_UINT32(status, K230GSDMAChannel), + VMSTATE_UINT32(cfg, K230GSDMAChannel), + VMSTATE_UINT32(usr_data, K230GSDMAChannel), + VMSTATE_UINT32(llt_saddr, K230GSDMAChannel), + VMSTATE_UINT32(current_llt, K230GSDMAChannel), + VMSTATE_UINT32(next_llt, K230GSDMAChannel), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_k230_gsdma = { + .name = "k230.gsdma", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(dma_ch_en, K230GSDMAState), + VMSTATE_UINT32(dma_int_mask, K230GSDMAState), + VMSTATE_UINT32(dma_int_stat, K230GSDMAState), + VMSTATE_UINT32(dma_cfg, K230GSDMAState), + VMSTATE_UINT32(dma_weight, K230GSDMAState), + VMSTATE_STRUCT_ARRAY(channels, K230GSDMAState, + K230_GSDMA_NUM_SDMA_CHANNELS, 2, + vmstate_k230_gsdma_channel, K230GSDMAChannel), + VMSTATE_END_OF_LIST() + } +}; + +static uint64_t k230_gsdma_read(void *opaque, hwaddr addr, unsigned int size) +{ + K230GSDMAState *s = K230_GSDMA(opaque); + unsigned int ch; + hwaddr ch_off; + uint64_t ret = 0; + + switch (addr) { + case K230_GSDMA_DMA_CH_EN: + ret = s->dma_ch_en; + break; + case K230_GSDMA_DMA_INT_MASK: + ret = s->dma_int_mask; + break; + case K230_GSDMA_DMA_INT_STAT: + ret = s->dma_int_stat; + break; + case K230_GSDMA_DMA_CFG: + ret = s->dma_cfg; + break; + case K230_GSDMA_DMA_WEIGHT: + ret = s->dma_weight; + break; + default: + if (addr < K230_GSDMA_CH_BASE) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: not implement gdma at offset 0x%" HWADDR_PRIx + "\n", __func__, addr); + } + break; + } + + ch = (addr - K230_GSDMA_CH_BASE) / K230_GSDMA_CH_STRIDE; + ch_off = (addr - K230_GSDMA_CH_BASE) % K230_GSDMA_CH_STRIDE; + if (ch < K230_GSDMA_NUM_SDMA_CHANNELS) { + switch (ch_off) { + case K230_GSDMA_CH_CTL: + break; + case K230_GSDMA_CH_STATUS: + ret = s->channels[ch].status; + break; + case K230_GSDMA_CH_CFG: + ret = s->channels[ch].cfg; + break; + case K230_GSDMA_CH_USR_DATA: + ret = s->channels[ch].usr_data; + break; + case K230_GSDMA_CH_LLT_SADDR: + ret = s->channels[ch].llt_saddr; + break; + case K230_GSDMA_CH_CURRENT_LLT: + ret = s->channels[ch].current_llt; + break; + default: + break; + } + } + + trace_k230_gsdma_read(addr, size, ret); + return ret; +} + +static void k230_gsdma_write_channel(K230GSDMAState *s, unsigned int ch, + hwaddr ch_off, uint32_t value) +{ + K230GSDMAChannel *c = &s->channels[ch]; + bool handshake_mode = ch < 2 && k230_gsdma_decomp_enabled(s); + + switch (ch_off) { + case K230_GSDMA_CH_CTL: + c->ctl = value; + if ((value & K230_GSDMA_CTL_STOP) != 0) { + c->started = false; + k230_gsdma_sdma_set_idle(c); + break; + } + if ((value & K230_GSDMA_CTL_RESUME) != 0 && + (c->status & K230_GSDMA_SDMA_STATUS_PAUSE) && c->next_llt != 0) { + k230_gsdma_run_sdma(s, ch, c->next_llt); + break; + } + if (handshake_mode && (value & K230_GSDMA_CTL_START) != 0) { + c->started = true; + c->status &= ~(K230_GSDMA_SDMA_STATUS_BUSY | + K230_GSDMA_SDMA_STATUS_PAUSE); + c->current_llt = 0; + c->next_llt = 0; + break; + } + if ((value & K230_GSDMA_CTL_START) != 0 && + (s->dma_ch_en & BIT(ch)) && c->llt_saddr != 0) { + c->started = true; + k230_gsdma_run_sdma(s, ch, c->llt_saddr); + } + break; + case K230_GSDMA_CH_CFG: + c->cfg = value; + break; + case K230_GSDMA_CH_USR_DATA: + c->usr_data = value; + break; + case K230_GSDMA_CH_LLT_SADDR: + c->llt_saddr = value; + break; + case K230_GSDMA_CH_CURRENT_LLT: + break; + default: + break; + } +} + +static void k230_gsdma_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ + K230GSDMAState *s = K230_GSDMA(opaque); + unsigned int ch; + hwaddr ch_off; + uint32_t v = value; + + trace_k230_gsdma_write(addr, size, value); + + switch (addr) { + case K230_GSDMA_DMA_CH_EN: + s->dma_ch_en = v & K230_GSDMA_DMA_CH_EN_MASK; + return; + case K230_GSDMA_DMA_INT_MASK: + s->dma_int_mask = v; + k230_gsdma_update_irq(s); + return; + case K230_GSDMA_DMA_INT_STAT: + s->dma_int_stat &= ~v; + k230_gsdma_update_irq(s); + return; + case K230_GSDMA_DMA_CFG: + s->dma_cfg = v; + return; + case K230_GSDMA_DMA_WEIGHT: + s->dma_weight = v & 0x00ffffff; + return; + default: + break; + } + + if (addr < K230_GSDMA_CH_BASE) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: not implement gdma at offset 0x%" HWADDR_PRIx + "\n", __func__, addr); + return; + } + + ch = (addr - K230_GSDMA_CH_BASE) / K230_GSDMA_CH_STRIDE; + ch_off = (addr - K230_GSDMA_CH_BASE) % K230_GSDMA_CH_STRIDE; + if (ch >= K230_GSDMA_NUM_SDMA_CHANNELS) { + return; + } + + k230_gsdma_write_channel(s, ch, ch_off, v); +} + +static void k230_gsdma_handle_signal(void *opaque, int n, int level) +{ + K230GSDMAState *s = opaque; + K230GSDMAChannel *c; + hwaddr addr; + unsigned int ch; + + if (!level || n == K230_GSDMA_GPIO_DECOMP_CTRL_EN) { + return; + } + + ch = n - 1; + + c = &s->channels[ch]; + if (!k230_gsdma_decomp_enabled(s) || !(s->dma_ch_en & BIT(ch)) || !c->started) { + return; + } + + if (c->current_llt == 0) { + addr = c->llt_saddr; + } else { + addr = c->next_llt; + } + if (!addr) { + return; + } + + k230_gsdma_step_sdma(s, ch, addr); + qemu_set_irq(s->handshake_out[n - 1], 1); + qemu_set_irq(s->handshake_out[n - 1], 0); +} + +static const MemoryRegionOps k230_gsdma_ops = { + .read = k230_gsdma_read, + .write = k230_gsdma_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static void k230_gsdma_reset_hold(Object *obj, ResetType type) +{ + K230GSDMAState *s = K230_GSDMA(obj); + + memset(s->channels, 0, sizeof(s->channels)); + s->dma_ch_en = 0; + s->dma_int_mask = 0; + s->dma_int_stat = 0; + s->dma_cfg = K230_GSDMA_DMA_CFG_RESET; + s->dma_weight = 0; +} + +static void k230_gsdma_realize(DeviceState *dev, Error **errp) +{ + K230GSDMAState *s = K230_GSDMA(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, OBJECT(dev), &k230_gsdma_ops, s, + TYPE_K230_GSDMA, K230_GSDMA_MMIO_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); + qdev_init_gpio_in(DEVICE(dev), k230_gsdma_handle_signal, + K230_GSDMA_NUM_GPIOS_IN); + qdev_init_gpio_out(DEVICE(dev), s->handshake_out, + K230_GSDMA_NUM_GPIOS_OUT); +} + +static void k230_gsdma_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->realize = k230_gsdma_realize; + rc->phases.hold = k230_gsdma_reset_hold; + dc->vmsd = &vmstate_k230_gsdma; + dc->desc = "Kendryte K230 GSDMA"; +} + +static const TypeInfo k230_gsdma_info = { + .name = TYPE_K230_GSDMA, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(K230GSDMAState), + .class_init = k230_gsdma_class_init, +}; + +static void k230_gsdma_register_types(void) +{ + type_register_static(&k230_gsdma_info); +} + +type_init(k230_gsdma_register_types) diff --git a/hw/dma/meson.build b/hw/dma/meson.build index cc7810beb8..45db5e7357 100644 --- a/hw/dma/meson.build +++ b/hw/dma/meson.build @@ -12,3 +12,4 @@ system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_dma.c', 'soc_dma.c')) system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_dma.c')) system_ss.add(when: 'CONFIG_SIFIVE_PDMA', if_true: files('sifive_pdma.c')) system_ss.add(when: 'CONFIG_XLNX_CSU_DMA', if_true: files('xlnx_csu_dma.c')) +system_ss.add(when: 'CONFIG_K230_GSDMA', if_true: files('k230_gsdma.c')) diff --git a/hw/dma/omap_dma.c b/hw/dma/omap_dma.c index 77f1441498..16575e73b7 100644 --- a/hw/dma/omap_dma.c +++ b/hw/dma/omap_dma.c @@ -22,7 +22,7 @@ #include "qemu/timer.h" #include "hw/arm/omap.h" #include "hw/core/irq.h" -#include "hw/arm/soc_dma.h" +#include "hw/dma/soc_dma.h" #include "system/physmem.h" struct omap_dma_channel_s { @@ -391,7 +391,7 @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) struct omap_dma_reg_set_s *a; struct omap_dma_channel_s *ch = dma->opaque; struct omap_dma_s *s = dma->dma->opaque; - int frames, min_elems, elements[__omap_dma_intr_last]; + uint32_t frames, min_elems, elements[__omap_dma_intr_last]; a = &ch->active_set; @@ -403,7 +403,14 @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) __func__, dma->num); } - min_elems = INT_MAX; + /* + * The maximum frame count and maximum element count are both 0xffff, + * so our worst case possible number of elements to transfer is + * 0xffff * 0xffff == 0xfffe0001. We can therefore keep element + * counts in a uint32_t and use UINT_MAX as a sentinel value for + * "not set" / "condition does not occur". + */ + min_elems = UINT_MAX; /* Check all the conditions that terminate the transfer starting * with those that can occur the soonest. */ @@ -413,7 +420,7 @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) if (elements[id] < min_elems) \ min_elems = elements[id]; \ } else \ - elements[id] = INT_MAX; + elements[id] = UINT_MAX; /* Elements */ INTR_CHECK( @@ -465,7 +472,7 @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) (a->frames - a->frame - 1) * a->elements + (a->elements - a->element)) - dma->bytes = min_elems * ch->data_type; + dma->bytes = (uint64_t)min_elems * ch->data_type; /* Set appropriate interrupts and/or deactivate channels */ @@ -528,8 +535,9 @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) /* Update packet number */ if (ch->fs && ch->bs) { - a->pck_element += min_elems; - a->pck_element %= a->pck_elements; + /* Can't overflow: worst case min_elems 0xFFFE0001 + element 0xFFFF */ + uint32_t new_pck_element = a->pck_element + min_elems; + a->pck_element = new_pck_element % a->pck_elements; } /* @@ -537,23 +545,20 @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) * can skip part of this. */ if (dma->update) { + /* Can't overflow: worst case min_elems 0xFFFE0001 + element 0xFFFF */ + uint32_t new_element = a->element + min_elems; a->element += min_elems; - frames = a->element / a->elements; - a->element = a->element % a->elements; + frames = new_element / a->elements; + a->element = new_element % a->elements; a->frame += frames; - a->src += min_elems * a->elem_delta[0] + frames * a->frame_delta[0]; - a->dest += min_elems * a->elem_delta[1] + frames * a->frame_delta[1]; + a->src += (uint64_t)min_elems * a->elem_delta[0] + frames * a->frame_delta[0]; + a->dest += (uint64_t)min_elems * a->elem_delta[1] + frames * a->frame_delta[1]; /* If the channel is async, update cpc */ if (!ch->sync && frames) { ch->cpc = a->dest & 0xffff; } - - /* - * TODO: if the destination port is IMIF or EMIFF, set the dirty - * bits on it. - */ } omap_dma_interrupts_update(s); diff --git a/hw/dma/soc_dma.c b/hw/dma/soc_dma.c index d5c52b804f..4feb22e6ea 100644 --- a/hw/dma/soc_dma.c +++ b/hw/dma/soc_dma.c @@ -20,40 +20,58 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/timer.h" -#include "hw/arm/soc_dma.h" +#include "qemu/log.h" +#include "system/physmem.h" +#include "hw/dma/soc_dma.h" static void transfer_mem2mem(struct soc_dma_ch_s *ch) { - memcpy(ch->paddr[0], ch->paddr[1], ch->bytes); - ch->paddr[0] += ch->bytes; - ch->paddr[1] += ch->bytes; -} + /* + * Memory-to-memory transfer: do the whole thing in one go. The + * hardware spec says that it is invalid to program the OMAP DMA + * controller with addresses that don't match the port (i.e. to + * ask for a transfer to/from a memory port with a physaddr that + * isn't within that port range) and that if you do then the + * transfer continues and memory can be corrupted. So we can map + * both source and destination, and treat short mappings and + * failed mappings as a guest error. + */ + hwaddr srclen = ch->bytes; + hwaddr dstlen = ch->bytes; + hwaddr srcaddr = ch->vaddr[0]; + hwaddr dstaddr = ch->vaddr[1]; + void *srcmem, *dstmem; + hwaddr xferlen = 0; -static void transfer_mem2fifo(struct soc_dma_ch_s *ch) -{ - ch->io_fn[1](ch->io_opaque[1], ch->paddr[0], ch->bytes); - ch->paddr[0] += ch->bytes; -} + srcmem = physical_memory_map(srcaddr, &srclen, false); + if (!srcmem) { + qemu_log_mask(LOG_GUEST_ERROR, + "soc_dma mem2mem transfer: could not map source; " + "guest error programming source port/address\n"); + return; + } -static void transfer_fifo2mem(struct soc_dma_ch_s *ch) -{ - ch->io_fn[0](ch->io_opaque[0], ch->paddr[1], ch->bytes); - ch->paddr[1] += ch->bytes; -} + dstmem = physical_memory_map(dstaddr, &dstlen, true); + if (!dstmem) { + qemu_log_mask(LOG_GUEST_ERROR, + "soc_dma mem2mem transfer: could not map destination; " + "guest error programming destination port/address\n"); + goto unmap_src; + } -/* This is further optimisable but isn't very important because often - * DMA peripherals forbid this kind of transfers and even when they don't, - * oprating systems may not need to use them. */ -static void *fifo_buf; -static int fifo_size; -static void transfer_fifo2fifo(struct soc_dma_ch_s *ch) -{ - if (ch->bytes > fifo_size) - fifo_buf = g_realloc(fifo_buf, fifo_size = ch->bytes); + xferlen = MIN(srclen, dstlen); + if (xferlen < ch->bytes) { + qemu_log_mask(LOG_GUEST_ERROR, + "soc_dma mem2mem transfer: could not transfer all data; " + "guest error programming src or destination addresses\n"); + /* Continue to transfer whatever did fit in the port window */ + } - /* Implement as transfer_fifo2linear + transfer_linear2fifo. */ - ch->io_fn[0](ch->io_opaque[0], fifo_buf, ch->bytes); - ch->io_fn[1](ch->io_opaque[1], fifo_buf, ch->bytes); + memmove(dstmem, srcmem, xferlen); + + physical_memory_unmap(dstmem, dstlen, true, xferlen); +unmap_src: + physical_memory_unmap(srcmem, srclen, false, xferlen); } struct dma_s { @@ -66,28 +84,24 @@ struct dma_s { struct memmap_entry_s { enum soc_dma_port_type type; hwaddr addr; - union { - struct { - void *opaque; - soc_dma_io_t fn; - int out; - } fifo; - struct { - void *base; - size_t size; - } mem; - } u; + struct { + size_t size; + } mem; } *memmap; int memmap_size; struct soc_dma_ch_s ch[]; }; -static void soc_dma_ch_schedule(struct soc_dma_ch_s *ch, int delay_bytes) +static void soc_dma_ch_schedule(struct soc_dma_ch_s *ch, uint64_t delay_bytes) { int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); struct dma_s *dma = (struct dma_s *) ch->dma; + /* + * Worst case delay bytes is only slightly larger than fits into + * a 32-bit integer, so this won't overflow. + */ timer_mod(ch->timer, now + delay_bytes / dma->channel_freq); } @@ -129,22 +143,9 @@ static inline enum soc_dma_port_type soc_dma_ch_update_type( struct dma_s *dma = (struct dma_s *) ch->dma; struct memmap_entry_s *entry = soc_dma_lookup(dma, ch->vaddr[port]); - if (entry->type == soc_dma_port_fifo) { - while (entry < dma->memmap + dma->memmap_size && - entry->u.fifo.out != port) - entry ++; - if (entry->addr != ch->vaddr[port] || entry->u.fifo.out != port) - return soc_dma_port_other; - - if (ch->type[port] != soc_dma_access_const) - return soc_dma_port_other; - - ch->io_fn[port] = entry->u.fifo.fn; - ch->io_opaque[port] = entry->u.fifo.opaque; - return soc_dma_port_fifo; - } else if (entry->type == soc_dma_port_mem) { + if (entry->type == soc_dma_port_mem) { if (entry->addr > ch->vaddr[port] || - entry->addr + entry->u.mem.size <= ch->vaddr[port]) + entry->addr + entry->mem.size <= ch->vaddr[port]) return soc_dma_port_other; /* TODO: support constant memory address for source port as used for @@ -152,10 +153,6 @@ static inline enum soc_dma_port_type soc_dma_ch_update_type( if (ch->type[port] != soc_dma_access_const) return soc_dma_port_other; - ch->paddr[port] = (uint8_t *) entry->u.mem.base + - (ch->vaddr[port] - entry->addr); - /* TODO: save bytes left to the end of the mapping somewhere so we - * can check we're not reading beyond it. */ return soc_dma_port_mem; } else return soc_dma_port_other; @@ -166,26 +163,14 @@ void soc_dma_ch_update(struct soc_dma_ch_s *ch) enum soc_dma_port_type src, dst; src = soc_dma_ch_update_type(ch, 0); - if (src == soc_dma_port_other) { + dst = soc_dma_ch_update_type(ch, 1); + if (src == soc_dma_port_other || dst == soc_dma_port_other) { ch->update = 0; ch->transfer_fn = ch->dma->transfer_fn; - return; - } - dst = soc_dma_ch_update_type(ch, 1); - - /* TODO: use src and dst as array indices. */ - if (src == soc_dma_port_mem && dst == soc_dma_port_mem) + } else { + ch->update = 1; ch->transfer_fn = transfer_mem2mem; - else if (src == soc_dma_port_mem && dst == soc_dma_port_fifo) - ch->transfer_fn = transfer_mem2fifo; - else if (src == soc_dma_port_fifo && dst == soc_dma_port_mem) - ch->transfer_fn = transfer_fifo2mem; - else if (src == soc_dma_port_fifo && dst == soc_dma_port_fifo) - ch->transfer_fn = transfer_fifo2fifo; - else - ch->transfer_fn = ch->dma->transfer_fn; - - ch->update = (dst != soc_dma_port_other); + } } static void soc_dma_ch_freq_update(struct dma_s *s) @@ -251,63 +236,11 @@ struct soc_dma_s *soc_dma_init(int n) } soc_dma_reset(&s->soc); - fifo_size = 0; return &s->soc; } -void soc_dma_port_add_fifo(struct soc_dma_s *soc, hwaddr virt_base, - soc_dma_io_t fn, void *opaque, int out) -{ - struct memmap_entry_s *entry; - struct dma_s *dma = (struct dma_s *) soc; - - dma->memmap = g_realloc(dma->memmap, sizeof(*entry) * - (dma->memmap_size + 1)); - entry = soc_dma_lookup(dma, virt_base); - - if (dma->memmap_size) { - if (entry->type == soc_dma_port_mem) { - if (entry->addr <= virt_base && - entry->addr + entry->u.mem.size > virt_base) { - error_report("%s: FIFO at %"PRIx64 - " collides with RAM region at %"PRIx64 - "-%"PRIx64, __func__, - virt_base, entry->addr, - (entry->addr + entry->u.mem.size)); - exit(-1); - } - - if (entry->addr <= virt_base) - entry ++; - } else - while (entry < dma->memmap + dma->memmap_size && - entry->addr <= virt_base) { - if (entry->addr == virt_base && entry->u.fifo.out == out) { - error_report("%s: FIFO at %"PRIx64 - " collides FIFO at %"PRIx64, - __func__, virt_base, entry->addr); - exit(-1); - } - - entry ++; - } - - memmove(entry + 1, entry, - (uint8_t *) (dma->memmap + dma->memmap_size ++) - - (uint8_t *) entry); - } else - dma->memmap_size ++; - - entry->addr = virt_base; - entry->type = soc_dma_port_fifo; - entry->u.fifo.fn = fn; - entry->u.fifo.opaque = opaque; - entry->u.fifo.out = out; -} - -void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base, - hwaddr virt_base, size_t size) +void soc_dma_port_add_mem(struct soc_dma_s *soc, hwaddr virt_base, size_t size) { struct memmap_entry_s *entry; struct dma_s *dma = (struct dma_s *) soc; @@ -320,12 +253,12 @@ void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base, if (entry->type == soc_dma_port_mem) { if ((entry->addr >= virt_base && entry->addr < virt_base + size) || (entry->addr <= virt_base && - entry->addr + entry->u.mem.size > virt_base)) { + entry->addr + entry->mem.size > virt_base)) { error_report("%s: RAM at %"PRIx64 "-%"PRIx64 " collides with RAM region at %"PRIx64 "-%"PRIx64, __func__, virt_base, virt_base + size, - entry->addr, entry->addr + entry->u.mem.size); + entry->addr, entry->addr + entry->mem.size); exit(-1); } @@ -354,8 +287,7 @@ void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base, entry->addr = virt_base; entry->type = soc_dma_port_mem; - entry->u.mem.base = phys_base; - entry->u.mem.size = size; + entry->mem.size = size; } /* TODO: port removal for ports like PCMCIA memory */ diff --git a/hw/dma/trace-events b/hw/dma/trace-events index 4c09790f9a..a494755b5a 100644 --- a/hw/dma/trace-events +++ b/hw/dma/trace-events @@ -47,3 +47,9 @@ pl330_iomem_read(uint32_t addr, uint32_t data) "addr: 0x%08"PRIx32" data: 0x%08" # xilinx_axidma.c xilinx_axidma_loading_desc_fail(uint32_t res) "error:%u" + +# k230_gsdma.c +k230_gsdma_read(uint64_t addr, unsigned int size, uint64_t data) "K230 GSDMA read: [0x%"PRIx64"] size %u -> 0x%"PRIx64 +k230_gsdma_write(uint64_t addr, unsigned int size, uint64_t data) "K230 GSDMA write: [0x%"PRIx64"] size %u <- 0x%"PRIx64 +k230_gsdma_read_sdma_llt(uint64_t addr, uint32_t cfg, uint32_t src_addr, uint32_t line_size, uint32_t line_cfg, uint32_t dst_addr, uint32_t next_llt_addr) "K230 GSDMA read SDMA LLT: addr=0x%"PRIx64" cfg=0x%"PRIx32" src=0x%"PRIx32" line_size=0x%"PRIx32" line_cfg=0x%"PRIx32" dst=0x%"PRIx32" next=0x%"PRIx32 +k230_gsdma_read_sdma_llt_failed(uint64_t addr) "K230 GSDMA read SDMA LLT failed: addr=0x%"PRIx64 diff --git a/hw/gpio/Kconfig b/hw/gpio/Kconfig index a209294c20..fcc7c70bd5 100644 --- a/hw/gpio/Kconfig +++ b/hw/gpio/Kconfig @@ -30,3 +30,6 @@ config PCF8574 config ZAURUS_SCOOP bool + +config CADENCE_GPIO + bool diff --git a/hw/gpio/cadence_gpio.c b/hw/gpio/cadence_gpio.c new file mode 100644 index 0000000000..ce8256b381 --- /dev/null +++ b/hw/gpio/cadence_gpio.c @@ -0,0 +1,292 @@ +/* + * Cadence GPIO emulation. + * + * Author: Kuan-Jui Chiu + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/gpio/cadence_gpio.h" +#include "hw/core/irq.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "trace.h" + +static void cdns_gpio_update_irq(CadenceGPIOState *s) +{ + qemu_set_irq(s->irq, s->isr ? 1 : 0); +} + +static void cdns_gpio_update_isr_with_inpvr(CadenceGPIOState *s, uint32_t new) +{ + uint32_t new_isr = 0; + uint32_t any_edges, rising_edges, falling_edges, deassert_mask; + + /* + * If ITR is set, this is level triggered: + * set corresponding ISR bits when IVR matches new inpvr value. + */ + new_isr |= s->itr & ~(s->ivr ^ new); + + /* + * If ITR is not set, this is edge-triggered: + * If IOAR bit is set, trigger on any edge; + * otherwise trigger on rising edge if IVR is set, + * trigger on falling edge if IVR bit is 0. + */ + any_edges = s->ioar & (s->inpvr ^ new); + rising_edges = s->ivr & ~s->inpvr & new; + falling_edges = ~s->ivr & s->inpvr & ~new; + new_isr |= ~s->itr & (any_edges | rising_edges | falling_edges); + + /* + * In bypass mode or if this isn't an input pin, the corresponding ISR + * bit is forced to zero. + */ + deassert_mask = s->bmr | ~s->dmr | s->imr; + + new_isr &= ~deassert_mask; + s->isr = new_isr; + + cdns_gpio_update_irq(s); +} + +static void cdns_gpio_update_isr(CadenceGPIOState *s) +{ + cdns_gpio_update_isr_with_inpvr(s, s->inpvr); +} + +static void cdns_gpio_set(void *opaque, int line, int level) +{ + CadenceGPIOState *s = CADENCE_GPIO(opaque); + uint32_t new_inpvr = deposit32(s->inpvr, line, 1, level ? 1 : 0); + + trace_cdns_gpio_set(DEVICE(s)->canonical_path, line, level); + + cdns_gpio_update_isr_with_inpvr(s, new_inpvr); + + /* Sync INPVR with new value */ + s->inpvr = new_inpvr; +} + +static inline void cdns_gpio_update_output_irq(CadenceGPIOState *s) +{ + uint32_t is_output = ~s->bmr & ~s->dmr & s->oer; + + for (int i = 0; i < CDNS_GPIO_NUM; i++) { + if (extract32(is_output, i, 1)) { + /* Forward the output value to corresponding irq */ + qemu_set_irq(s->output[i], extract32(s->ovr, i, 1)); + } + } +} + +static uint64_t cdns_gpio_read(void *opaque, hwaddr offset, unsigned size) +{ + CadenceGPIOState *s = CADENCE_GPIO(opaque); + uint32_t reg_value = 0x0; + + switch (offset) { + case CDNS_GPIO_BYPASS_MODE: + reg_value = s->bmr; + break; + + case CDNS_GPIO_DIRECTION_MODE: + reg_value = s->dmr; + break; + + case CDNS_GPIO_OUTPUT_EN: + reg_value = s->oer; + break; + + case CDNS_GPIO_OUTPUT_VALUE: + reg_value = s->ovr; + break; + + case CDNS_GPIO_INPUT_VALUE: + reg_value = s->inpvr; + break; + + case CDNS_GPIO_IRQ_MASK: + reg_value = s->imr; + break; + + case CDNS_GPIO_IRQ_STATUS: + reg_value = s->isr; + break; + + case CDNS_GPIO_IRQ_TYPE: + reg_value = s->itr; + break; + + case CDNS_GPIO_IRQ_VALUE: + reg_value = s->ivr; + break; + + case CDNS_GPIO_IRQ_ANY_EDGE: + reg_value = s->ioar; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_CADENCE_GPIO, __func__, offset); + break; + } + + trace_cdns_gpio_read(DEVICE(s)->canonical_path, offset, reg_value); + + return reg_value; +} + +static void cdns_gpio_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + CadenceGPIOState *s = CADENCE_GPIO(opaque); + + trace_cdns_gpio_write(DEVICE(s)->canonical_path, offset, value); + + switch (offset) { + case CDNS_GPIO_BYPASS_MODE: + s->bmr = value; + cdns_gpio_update_output_irq(s); + cdns_gpio_update_isr(s); + break; + + case CDNS_GPIO_DIRECTION_MODE: + s->dmr = value; + cdns_gpio_update_output_irq(s); + cdns_gpio_update_isr(s); + break; + + case CDNS_GPIO_OUTPUT_EN: + s->oer = value; + cdns_gpio_update_output_irq(s); + break; + + case CDNS_GPIO_OUTPUT_VALUE: + s->ovr = value; + cdns_gpio_update_output_irq(s); + break; + + case CDNS_GPIO_IRQ_EN: + s->imr &= ~value; + cdns_gpio_update_isr(s); + break; + + case CDNS_GPIO_IRQ_DIS: + s->imr |= value; + cdns_gpio_update_isr(s); + break; + + case CDNS_GPIO_IRQ_TYPE: + s->itr = value; + break; + + case CDNS_GPIO_IRQ_VALUE: + s->ivr = value; + break; + + case CDNS_GPIO_IRQ_ANY_EDGE: + s->ioar = value; + break; + + case CDNS_GPIO_INPUT_VALUE: + case CDNS_GPIO_IRQ_MASK: + case CDNS_GPIO_IRQ_STATUS: + /* Read-Only */ + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_CADENCE_GPIO, __func__, offset); + break; + } +} + +static const MemoryRegionOps cdns_gpio_ops = { + .read = cdns_gpio_read, + .write = cdns_gpio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static const VMStateDescription vmstate_cdns_gpio = { + .name = TYPE_CADENCE_GPIO, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(bmr, CadenceGPIOState), + VMSTATE_UINT32(dmr, CadenceGPIOState), + VMSTATE_UINT32(oer, CadenceGPIOState), + VMSTATE_UINT32(ovr, CadenceGPIOState), + VMSTATE_UINT32(inpvr, CadenceGPIOState), + VMSTATE_UINT32(imr, CadenceGPIOState), + VMSTATE_UINT32(isr, CadenceGPIOState), + VMSTATE_UINT32(itr, CadenceGPIOState), + VMSTATE_UINT32(ivr, CadenceGPIOState), + VMSTATE_UINT32(ioar, CadenceGPIOState), + VMSTATE_END_OF_LIST() + } +}; + +static void cdns_gpio_reset(DeviceState *dev) +{ + CadenceGPIOState *s = CADENCE_GPIO(dev); + + s->bmr = 0; + s->dmr = 0; + s->oer = 0; + s->ovr = 0; + s->inpvr = 0; + s->imr = 0xffffffff; + s->isr = 0; + s->itr = 0; + s->ivr = 0; + s->ioar = 0; +} + +static void cdns_gpio_init(Object *obj) +{ + CadenceGPIOState *s = CADENCE_GPIO(obj); + + memory_region_init_io(&s->iomem, obj, &cdns_gpio_ops, s, + TYPE_CADENCE_GPIO, CDNS_GPIO_REG_SIZE); + + qdev_init_gpio_in(DEVICE(s), cdns_gpio_set, CDNS_GPIO_NUM); + qdev_init_gpio_out(DEVICE(s), s->output, CDNS_GPIO_NUM); + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); +} + +static void cdns_gpio_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_legacy_reset(dc, cdns_gpio_reset); + dc->vmsd = &vmstate_cdns_gpio; + dc->desc = "Cadence GPIO controller"; +} + +static const TypeInfo cdns_gpio_info = { + .name = TYPE_CADENCE_GPIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(CadenceGPIOState), + .instance_init = cdns_gpio_init, + .class_init = cdns_gpio_class_init, +}; + +static void cdns_gpio_register_types(void) +{ + type_register_static(&cdns_gpio_info); +} + +type_init(cdns_gpio_register_types) diff --git a/hw/gpio/meson.build b/hw/gpio/meson.build index 6a67ee958f..0555f44b6a 100644 --- a/hw/gpio/meson.build +++ b/hw/gpio/meson.build @@ -19,3 +19,4 @@ system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_gpio.c')) system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_sgpio.c')) system_ss.add(when: 'CONFIG_SIFIVE_GPIO', if_true: files('sifive_gpio.c')) system_ss.add(when: 'CONFIG_PCF8574', if_true: files('pcf8574.c')) +system_ss.add(when: 'CONFIG_CADENCE_GPIO', if_true: files('cadence_gpio.c')) diff --git a/hw/gpio/pca9552.c b/hw/gpio/pca9552.c index b13ac9fd9c..719149b717 100644 --- a/hw/gpio/pca9552.c +++ b/hw/gpio/pca9552.c @@ -1,7 +1,10 @@ /* - * PCA9552 I2C LED blinker + * PCA955X I2C LED blinker and I/O expanders * * https://www.nxp.com/docs/en/application-note/AN264.pdf + * https://www.nxp.com/docs/en/data-sheet/PCA9552.pdf + * https://www.nxp.com/docs/en/data-sheet/PCA9555.pdf + * https://www.nxp.com/docs/en/data-sheet/PCA9535_PCA9535C.pdf * * Copyright (c) 2017-2018, IBM Corporation. * Copyright (c) 2020 Philippe Mathieu-Daudé @@ -12,9 +15,9 @@ #include "qemu/osdep.h" #include "qemu/log.h" -#include "qemu/module.h" #include "qemu/bitops.h" #include "hw/core/qdev-properties.h" +#include "hw/i2c/i2c.h" #include "hw/gpio/pca9552.h" #include "hw/gpio/pca9552_regs.h" #include "hw/core/irq.h" @@ -24,6 +27,25 @@ #include "trace.h" #include "qom/object.h" +#define PCA955X_NR_REGS 10 +#define PCA955X_PIN_COUNT_MAX 16 + +OBJECT_DECLARE_TYPE(PCA955xState, PCA955xClass, PCA955X) + +struct PCA955xState { + /*< private >*/ + I2CSlave parent_obj; + /*< public >*/ + + uint8_t len; + uint8_t pointer; + + uint8_t regs[PCA955X_NR_REGS]; + qemu_irq gpio_out[PCA955X_PIN_COUNT_MAX]; + uint8_t ext_state[PCA955X_PIN_COUNT_MAX]; + char *description; /* For debugging purpose only */ +}; + struct PCA955xClass { /*< private >*/ I2CSlaveClass parent_class; @@ -33,10 +55,7 @@ struct PCA955xClass { uint8_t max_reg; bool has_led_support; }; -typedef struct PCA955xClass PCA955xClass; -DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X, - TYPE_PCA955X) /* * Note: The LED_ON and LED_OFF configuration values for the PCA955X * chips are the reverse of the PCA953X family of chips. @@ -49,6 +68,7 @@ DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X, #define PCA9552_PIN_HIZ 0x1 static const char *led_state[] = {"on", "off", "pwm0", "pwm1"}; +static const char *pin_state[] = {"low", "high"}; static uint8_t pca955x_pin_get_config(PCA955xState *s, int pin) { @@ -148,9 +168,12 @@ static void pca955x_update_pin_input(PCA955xState *s) /* PCA9535: Simple GPIO behavior */ uint8_t config_reg = PCA9535_CONFIG0 + (i / 8); uint8_t output_reg = PCA9535_OUTPUT0 + (i / 8); - uint8_t polarity_reg = PCA9535_POLARITY0 + (i / 8); - /* Check if pin is configured as input */ + /* + * The input register holds the raw pin logic level; the + * polarity inversion register is only applied when the input + * port is read (see pca955x_read()). + */ if (s->regs[config_reg] & bit_mask) { /* Input mode - reflect external state */ if (s->ext_state[i] == PCA9552_PIN_LOW) { @@ -160,12 +183,8 @@ static void pca955x_update_pin_input(PCA955xState *s) } } else { /* Output mode - reflect output register value */ - uint8_t output_bit = s->regs[output_reg] & bit_mask; - uint8_t polarity_bit = s->regs[polarity_reg] & bit_mask; - - /* Apply polarity inversion if set */ s->regs[input_reg] = (s->regs[input_reg] & ~bit_mask) | - ((output_bit ^ polarity_bit) & bit_mask); + (s->regs[output_reg] & bit_mask); } } @@ -187,6 +206,18 @@ static uint8_t pca955x_read(PCA955xState *s, uint8_t reg) return 0xFF; } + /* + * On the GPIO variants, reading an input port returns the raw pin + * levels XORed with the polarity inversion register, as specified by + * the datasheet. + */ + if (!k->has_led_support && + (reg == PCA9535_INPUT0 || reg == PCA9535_INPUT1)) { + uint8_t polarity_reg = PCA9535_POLARITY0 + (reg - PCA9535_INPUT0); + + return s->regs[reg] ^ s->regs[polarity_reg]; + } + return s->regs[reg]; } @@ -229,14 +260,26 @@ static void pca955x_write(PCA955xState *s, uint8_t reg, uint8_t data) } /* - * When Auto-Increment is on, the register address is incremented - * after each byte is sent to or received by the device. The index - * rollovers to 0 when the maximum register address is reached. + * Advance the command pointer after each byte sent to or received from the + * device. + * + * The LED variant auto-increments only when the AI bit (bit 4) is set in the + * command byte, rolling over to 0 once the maximum register address is + * reached. + * + * The GPIO variants auto-increment on every access, toggling bit 0 so the + * pointer stays within the addressed register pair + * (input/output/polarity/config), as specified by their datasheet. */ static void pca955x_autoinc(PCA955xState *s) { PCA955xClass *k = PCA955X_GET_CLASS(s); + if (!k->has_led_support) { + s->pointer ^= 0x1; + return; + } + if (s->pointer != 0xFF && s->pointer & PCA9552_AUTOINC) { uint8_t reg = s->pointer & 0xf; @@ -245,12 +288,25 @@ static void pca955x_autoinc(PCA955xState *s) } } +/* + * The LED variant addresses its registers with a 4-bit command field, while + * the GPIO variants only decode 3 bits (the command wraps into the 8-register + * window). + */ +static inline uint8_t pca955x_cmd_reg(PCA955xState *s) +{ + PCA955xClass *k = PCA955X_GET_CLASS(s); + + return s->pointer & (k->has_led_support ? 0xf : 0x7); +} + static uint8_t pca955x_recv(I2CSlave *i2c) { PCA955xState *s = PCA955X(i2c); + PCA955xClass *k = PCA955X_GET_CLASS(s); uint8_t ret; - ret = pca955x_read(s, s->pointer & 0xf); + ret = pca955x_read(s, pca955x_cmd_reg(s)); /* * From the Specs: @@ -262,7 +318,7 @@ static uint8_t pca955x_recv(I2CSlave *i2c) * I don't know what should be done in this case, so throw an * error. */ - if (s->pointer == PCA9552_AUTOINC) { + if (k->has_led_support && s->pointer == PCA9552_AUTOINC) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Autoincrement read starting with register 0\n", __func__); @@ -282,7 +338,7 @@ static int pca955x_send(I2CSlave *i2c, uint8_t data) s->pointer = data; s->len++; } else { - pca955x_write(s, s->pointer & 0xf, data); + pca955x_write(s, pca955x_cmd_reg(s), data); pca955x_autoinc(s); } @@ -373,6 +429,79 @@ static void pca955x_set_led(Object *obj, Visitor *v, const char *name, pca955x_write(s, reg, val); } +static void pca955x_set_ext_state(PCA955xState *s, int pin, int level); + +static void pca955x_get_pin(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + PCA955xClass *k = PCA955X_GET_CLASS(obj); + PCA955xState *s = PCA955X(obj); + int pin, rc; + uint8_t input_reg, state; + + rc = sscanf(name, "pin%2d", &pin); + if (rc != 1) { + error_setg(errp, "%s: error reading %s", __func__, name); + return; + } + if (pin < 0 || pin >= k->pin_count) { + error_setg(errp, "%s invalid pin %s", __func__, name); + return; + } + + /* + * Report the raw pin logic level; polarity inversion is a read-time + * transform applied to the INPUT register, not to the pin state itself. + */ + input_reg = PCA9535_INPUT0 + (pin / 8); + state = (s->regs[input_reg] >> (pin % 8)) & 0x1; + visit_type_str(v, name, (char **)&pin_state[state], errp); +} + +static void pca955x_set_pin(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + PCA955xClass *k = PCA955X_GET_CLASS(obj); + PCA955xState *s = PCA955X(obj); + int pin, rc; + uint8_t state, config_reg; + g_autofree char *state_str = NULL; + + if (!visit_type_str(v, name, &state_str, errp)) { + return; + } + rc = sscanf(name, "pin%2d", &pin); + if (rc != 1) { + error_setg(errp, "%s: error reading %s", __func__, name); + return; + } + if (pin < 0 || pin >= k->pin_count) { + error_setg(errp, "%s invalid pin %s", __func__, name); + return; + } + + for (state = 0; state < ARRAY_SIZE(pin_state); state++) { + if (!strcmp(state_str, pin_state[state])) { + break; + } + } + if (state >= ARRAY_SIZE(pin_state)) { + error_setg(errp, "%s invalid pin state %s", __func__, state_str); + return; + } + + /* Only input-configured pins can be driven by an external device. */ + config_reg = PCA9535_CONFIG0 + (pin / 8); + if (!((s->regs[config_reg] >> (pin % 8)) & 0x1)) { + qemu_log_mask(LOG_UNIMP, + "%s: pin %d is configured as output, ignoring set\n", + s->description, pin); + return; + } + + pca955x_set_ext_state(s, pin, state != PCA9552_PIN_LOW); +} + static const VMStateDescription pca9552_vmstate = { .name = "PCA9552", .version_id = 0, @@ -382,14 +511,14 @@ static const VMStateDescription pca9552_vmstate = { VMSTATE_UINT8(pointer, PCA955xState), VMSTATE_UINT8_ARRAY(regs, PCA955xState, PCA955X_NR_REGS), VMSTATE_UINT8_ARRAY(ext_state, PCA955xState, PCA955X_PIN_COUNT_MAX), - VMSTATE_I2C_SLAVE(i2c, PCA955xState), + VMSTATE_I2C_SLAVE(parent_obj, PCA955xState), VMSTATE_END_OF_LIST() } }; -static void pca9552_reset(DeviceState *dev) +static void pca9552_reset_hold(Object *obj, ResetType type) { - PCA955xState *s = PCA955X(dev); + PCA955xState *s = PCA955X(obj); s->regs[PCA9552_PSC0] = 0xFF; s->regs[PCA9552_PWM0] = 0x80; @@ -407,9 +536,9 @@ static void pca9552_reset(DeviceState *dev) s->len = 0; } -static void pca9535_reset(DeviceState *dev) +static void pca9535_reset_hold(Object *obj, ResetType type) { - PCA955xState *s = PCA955X(dev); + PCA955xState *s = PCA955X(obj); s->regs[PCA9535_INPUT0] = 0xFF; /* All inputs high (pull-ups) */ s->regs[PCA9535_INPUT1] = 0xFF; /* All inputs high (pull-ups) */ @@ -430,15 +559,22 @@ static void pca9535_reset(DeviceState *dev) static void pca955x_initfn(Object *obj) { PCA955xClass *k = PCA955X_GET_CLASS(obj); - int led; assert(k->pin_count <= PCA955X_PIN_COUNT_MAX); - for (led = 0; led < k->pin_count; led++) { + for (int ix = 0; ix < k->pin_count; ix++) { char *name; - name = g_strdup_printf("led%d", led); - object_property_add(obj, name, "bool", pca955x_get_led, pca955x_set_led, - NULL, NULL); + if (k->has_led_support) { + /* LED variant: expose the LED selector state as led%d. */ + name = g_strdup_printf("led%d", ix); + object_property_add(obj, name, "bool", + pca955x_get_led, pca955x_set_led, NULL, NULL); + } else { + /* GPIO variant: expose the pin logic level as pin%d. */ + name = g_strdup_printf("pin%d", ix); + object_property_add(obj, name, "str", + pca955x_get_pin, pca955x_set_pin, NULL, NULL); + } g_free(name); } } @@ -469,7 +605,7 @@ static void pca955x_realize(DeviceState *dev, Error **errp) PCA955xState *s = PCA955X(dev); if (!s->description) { - s->description = g_strdup("pca-unspecified"); + s->description = g_strdup(object_get_typename(OBJECT(dev))); } qdev_init_gpio_out(dev, s->gpio_out, k->pin_count); @@ -492,57 +628,57 @@ static void pca955x_class_init(ObjectClass *klass, const void *data) device_class_set_props(dc, pca955x_properties); } -static const TypeInfo pca955x_info = { - .name = TYPE_PCA955X, - .parent = TYPE_I2C_SLAVE, - .instance_init = pca955x_initfn, - .instance_size = sizeof(PCA955xState), - .class_init = pca955x_class_init, - .class_size = sizeof(PCA955xClass), - .abstract = true, -}; - static void pca9552_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); PCA955xClass *pc = PCA955X_CLASS(oc); - device_class_set_legacy_reset(dc, pca9552_reset); + rc->phases.hold = pca9552_reset_hold; dc->vmsd = &pca9552_vmstate; pc->max_reg = PCA9552_LS3; pc->pin_count = 16; pc->has_led_support = true; } -static void pca9535_class_init(ObjectClass *oc, const void *data) +static void pca95x5_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); PCA955xClass *pc = PCA955X_CLASS(oc); - device_class_set_legacy_reset(dc, pca9535_reset); + rc->phases.hold = pca9535_reset_hold; dc->vmsd = &pca9552_vmstate; pc->max_reg = PCA9535_CONFIG1; pc->pin_count = 16; pc->has_led_support = false; } -static const TypeInfo pca9552_info = { - .name = TYPE_PCA9552, - .parent = TYPE_PCA955X, - .class_init = pca9552_class_init, +static const TypeInfo pca955x_types[] = { + { + .name = TYPE_PCA955X, + .parent = TYPE_I2C_SLAVE, + .instance_init = pca955x_initfn, + .instance_size = sizeof(PCA955xState), + .class_init = pca955x_class_init, + .class_size = sizeof(PCA955xClass), + .abstract = true, + }, + { + .name = TYPE_PCA9552, + .parent = TYPE_PCA955X, + .class_init = pca9552_class_init, + }, + { + .name = TYPE_PCA9535, + .parent = TYPE_PCA955X, + .class_init = pca95x5_class_init, + }, + { + .name = TYPE_PCA9555, + .parent = TYPE_PCA955X, + .class_init = pca95x5_class_init, + } }; -static const TypeInfo pca9535_info = { - .name = TYPE_PCA9535, - .parent = TYPE_PCA955X, - .class_init = pca9535_class_init, -}; - -static void pca955x_register_types(void) -{ - type_register_static(&pca955x_info); - type_register_static(&pca9552_info); - type_register_static(&pca9535_info); -} - -type_init(pca955x_register_types) +DEFINE_TYPES(pca955x_types) diff --git a/hw/gpio/pca9554.c b/hw/gpio/pca9554.c index 8427e01e9b..904698cdce 100644 --- a/hw/gpio/pca9554.c +++ b/hw/gpio/pca9554.c @@ -24,6 +24,8 @@ struct PCA9554Class { /*< private >*/ I2CSlaveClass parent_class; /*< public >*/ + + uint8_t pin_count; }; typedef struct PCA9554Class PCA9554Class; @@ -37,46 +39,36 @@ static const char *pin_state[] = {"low", "high"}; static void pca9554_update_pin_input(PCA9554State *s) { + PCA9554Class *pc = PCA9554_GET_CLASS(s); int i; uint8_t config = s->regs[PCA9554_CONFIG]; uint8_t output = s->regs[PCA9554_OUTPUT]; - uint8_t internal_state = config | output; - for (i = 0; i < PCA9554_PIN_COUNT; i++) { + for (i = 0; i < pc->pin_count; i++) { uint8_t bit_mask = 1 << i; - uint8_t internal_pin_state = (internal_state >> i) & 0x1; uint8_t old_value = s->regs[PCA9554_INPUT] & bit_mask; uint8_t new_value; - switch (internal_pin_state) { - case PCA9554_PIN_LOW: - s->regs[PCA9554_INPUT] &= ~bit_mask; - break; - case PCA9554_PIN_HIZ: + if (config & bit_mask) { /* - * pullup sets it to a logical 1 unless - * external device drives it low. + * Input: the pin is Hi-Z with a pull-up, so it reads high + * unless an external device drives it low. */ if (s->ext_state[i] == PCA9554_PIN_LOW) { s->regs[PCA9554_INPUT] &= ~bit_mask; } else { - s->regs[PCA9554_INPUT] |= bit_mask; + s->regs[PCA9554_INPUT] |= bit_mask; } - break; - default: - break; + } else { + /* Output: the push-pull stage drives the output register level. */ + s->regs[PCA9554_INPUT] = (s->regs[PCA9554_INPUT] & ~bit_mask) | + (output & bit_mask); } - /* update irq state only if pin state changed */ + /* drive the per-pin GPIO output only if the pin level changed */ new_value = s->regs[PCA9554_INPUT] & bit_mask; if (new_value != old_value) { - if (new_value) { - /* changed from 0 to 1 */ - qemu_set_irq(s->gpio_out[i], 1); - } else { - /* changed from 1 to 0 */ - qemu_set_irq(s->gpio_out[i], 0); - } + qemu_set_irq(s->gpio_out[i], !!new_value); } } } @@ -99,6 +91,12 @@ static uint8_t pca9554_read(PCA9554State *s, uint8_t reg) static void pca9554_write(PCA9554State *s, uint8_t reg, uint8_t data) { + PCA9554Class *pc = PCA9554_GET_CLASS(s); + uint8_t pin_mask = (1 << pc->pin_count) - 1; + + /* Variants narrower than 8 bits ignore the unimplemented upper pins. */ + data &= pin_mask; + switch (reg) { case PCA9554_OUTPUT: case PCA9554_CONFIG: @@ -145,6 +143,14 @@ static int pca9554_event(I2CSlave *i2c, enum i2c_event event) return 0; } +static void pca9554_set_ext_state(PCA9554State *s, int pin, int level) +{ + if (s->ext_state[pin] != level) { + s->ext_state[pin] = level; + pca9554_update_pin_input(s); + } +} + static void pca9554_get_pin(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -157,14 +163,18 @@ static void pca9554_get_pin(Object *obj, Visitor *v, const char *name, error_setg(errp, "%s: error reading %s", __func__, name); return; } - if (pin < 0 || pin >= PCA9554_PIN_COUNT) { + if (pin < 0 || pin >= PCA9554_GET_CLASS(s)->pin_count) { error_setg(errp, "%s invalid pin %s", __func__, name); return; } - state = pca9554_read(s, PCA9554_CONFIG); - state |= pca9554_read(s, PCA9554_OUTPUT); - state = (state >> pin) & 0x1; + /* + * Report the physical pin level. The input register is kept in sync by + * pca9554_update_pin_input(): output pins mirror the OUTPUT register and + * input pins reflect the externally driven (or pulled-up) level, so it + * holds the wire level regardless of the configured direction. + */ + state = (s->regs[PCA9554_INPUT] >> pin) & 0x1; visit_type_str(v, name, (char **)&pin_state[state], errp); } @@ -184,7 +194,7 @@ static void pca9554_set_pin(Object *obj, Visitor *v, const char *name, error_setg(errp, "%s: error reading %s", __func__, name); return; } - if (pin < 0 || pin >= PCA9554_PIN_COUNT) { + if (pin < 0 || pin >= PCA9554_GET_CLASS(s)->pin_count) { error_setg(errp, "%s invalid pin %s", __func__, name); return; } @@ -199,20 +209,34 @@ static void pca9554_set_pin(Object *obj, Visitor *v, const char *name, return; } - /* First, modify the output register bit */ - val = pca9554_read(s, PCA9554_OUTPUT); - mask = 0x1 << pin; - if (state == PCA9554_PIN_LOW) { - val &= ~(mask); + if (s->hw_dir) { + /* Warn and ignore if the guest has configured this pin as output */ + if (!((s->regs[PCA9554_CONFIG] >> pin) & 0x1)) { + qemu_log_mask(LOG_UNIMP, + "%s: pin %d is configured as output, " + "ignoring external set\n", + s->description, pin); + return; + } + /* Drive the external input level */ + pca9554_set_ext_state(s, pin, state != PCA9554_PIN_LOW); } else { - val |= mask; - } - pca9554_write(s, PCA9554_OUTPUT, val); + /* Legacy behavior: force output mode and drive */ + /* First, modify the output register bit */ + val = pca9554_read(s, PCA9554_OUTPUT); + mask = 0x1 << pin; + if (state == PCA9554_PIN_LOW) { + val &= ~(mask); + } else { + val |= mask; + } + pca9554_write(s, PCA9554_OUTPUT, val); - /* Then, clear the config register bit for output mode */ - val = pca9554_read(s, PCA9554_CONFIG); - val &= ~mask; - pca9554_write(s, PCA9554_CONFIG, val); + /* Then, clear the config register bit for output mode */ + val = pca9554_read(s, PCA9554_CONFIG); + val &= ~mask; + pca9554_write(s, PCA9554_CONFIG, val); + } } static const VMStateDescription pca9554_vmstate = { @@ -232,13 +256,15 @@ static const VMStateDescription pca9554_vmstate = { static void pca9554_reset(DeviceState *dev) { PCA9554State *s = PCA9554(dev); + PCA9554Class *pc = PCA9554_GET_CLASS(s); + uint8_t pin_mask = (1 << pc->pin_count) - 1; - s->regs[PCA9554_INPUT] = 0xFF; - s->regs[PCA9554_OUTPUT] = 0xFF; + s->regs[PCA9554_INPUT] = pin_mask; + s->regs[PCA9554_OUTPUT] = pin_mask; s->regs[PCA9554_POLARITY] = 0x0; /* No pins are inverted */ - s->regs[PCA9554_CONFIG] = 0xFF; /* All pins are inputs */ + s->regs[PCA9554_CONFIG] = pin_mask; /* All pins are inputs */ - memset(s->ext_state, PCA9554_PIN_HIZ, PCA9554_PIN_COUNT); + memset(s->ext_state, PCA9554_PIN_HIZ, pc->pin_count); pca9554_update_pin_input(s); s->pointer = 0x0; @@ -247,55 +273,51 @@ static void pca9554_reset(DeviceState *dev) static void pca9554_initfn(Object *obj) { + PCA9554Class *pc = PCA9554_GET_CLASS(obj); int pin; - for (pin = 0; pin < PCA9554_PIN_COUNT; pin++) { + for (pin = 0; pin < pc->pin_count; pin++) { char *name; name = g_strdup_printf("pin%d", pin); - object_property_add(obj, name, "bool", pca9554_get_pin, pca9554_set_pin, + object_property_add(obj, name, "str", pca9554_get_pin, pca9554_set_pin, NULL, NULL); g_free(name); } } -static void pca9554_set_ext_state(PCA9554State *s, int pin, int level) -{ - if (s->ext_state[pin] != level) { - s->ext_state[pin] = level; - pca9554_update_pin_input(s); - } -} - static void pca9554_gpio_in_handler(void *opaque, int pin, int level) { - PCA9554State *s = PCA9554(opaque); + PCA9554Class *pc = PCA9554_GET_CLASS(s); - assert((pin >= 0) && (pin < PCA9554_PIN_COUNT)); + assert((pin >= 0) && (pin < pc->pin_count)); pca9554_set_ext_state(s, pin, level); } static void pca9554_realize(DeviceState *dev, Error **errp) { PCA9554State *s = PCA9554(dev); + PCA9554Class *pc = PCA9554_GET_CLASS(s); if (!s->description) { - s->description = g_strdup("pca9554"); + s->description = g_strdup(object_get_typename(OBJECT(dev))); } - qdev_init_gpio_out(dev, s->gpio_out, PCA9554_PIN_COUNT); - qdev_init_gpio_in(dev, pca9554_gpio_in_handler, PCA9554_PIN_COUNT); + qdev_init_gpio_out(dev, s->gpio_out, pc->pin_count); + qdev_init_gpio_in(dev, pca9554_gpio_in_handler, pc->pin_count); } static const Property pca9554_properties[] = { DEFINE_PROP_STRING("description", PCA9554State, description), + DEFINE_PROP_BOOL("hw-dir", PCA9554State, hw_dir, false), }; static void pca9554_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); + PCA9554Class *pc = PCA9554_CLASS(klass); k->event = pca9554_event; k->recv = pca9554_recv; @@ -304,21 +326,32 @@ static void pca9554_class_init(ObjectClass *klass, const void *data) device_class_set_legacy_reset(dc, pca9554_reset); dc->vmsd = &pca9554_vmstate; device_class_set_props(dc, pca9554_properties); + + pc->pin_count = PCA9554_PIN_COUNT; } -static const TypeInfo pca9554_info = { - .name = TYPE_PCA9554, - .parent = TYPE_I2C_SLAVE, - .instance_init = pca9554_initfn, - .instance_size = sizeof(PCA9554State), - .class_init = pca9554_class_init, - .class_size = sizeof(PCA9554Class), - .abstract = false, +static void pca9536_class_init(ObjectClass *klass, const void *data) +{ + PCA9554Class *pc = PCA9554_CLASS(klass); + + pc->pin_count = PCA9536_PIN_COUNT; +} + +static const TypeInfo pca9554_types[] = { + { + .name = TYPE_PCA9554, + .parent = TYPE_I2C_SLAVE, + .instance_init = pca9554_initfn, + .instance_size = sizeof(PCA9554State), + .class_init = pca9554_class_init, + .class_size = sizeof(PCA9554Class), + .abstract = false, + }, + { + .name = TYPE_PCA9536, + .parent = TYPE_PCA9554, + .class_init = pca9536_class_init, + } }; -static void pca9554_register_types(void) -{ - type_register_static(&pca9554_info); -} - -type_init(pca9554_register_types) +DEFINE_TYPES(pca9554_types); diff --git a/hw/gpio/trace-events b/hw/gpio/trace-events index cea896b28f..80ca783a03 100644 --- a/hw/gpio/trace-events +++ b/hw/gpio/trace-events @@ -46,3 +46,8 @@ stm32l4x5_gpio_read(char *gpio, uint64_t addr) "GPIO%s addr: 0x%" PRIx64 " " stm32l4x5_gpio_write(char *gpio, uint64_t addr, uint64_t data) "GPIO%s addr: 0x%" PRIx64 " val: 0x%" PRIx64 "" stm32l4x5_gpio_update_idr(char *gpio, uint32_t old_idr, uint32_t new_idr) "GPIO%s from: 0x%x to: 0x%x" stm32l4x5_gpio_pins(char *gpio, uint16_t disconnected, uint16_t high) "GPIO%s disconnected pins: 0x%x levels: 0x%x" + +# cadence_gpio.c +cdns_gpio_read(const char *path, uint64_t offset, uint32_t value) "%s:reg[0x%04" PRIx64 "] -> 0x%" PRIx32 +cdns_gpio_write(const char *path, uint64_t offset, uint64_t value) "%s:reg[0x%04" PRIx64 "] <- 0x%04" PRIx64 +cdns_gpio_set(const char *path, int line, int level) "%s:[%d] <- %d" diff --git a/hw/hexagon/Kconfig b/hw/hexagon/Kconfig index 52065ab3b2..83b2763d1e 100644 --- a/hw/hexagon/Kconfig +++ b/hw/hexagon/Kconfig @@ -2,6 +2,9 @@ config HEX_DSP bool default y depends on HEXAGON + select CPU_CLUSTER + select HEX_L2VIC + select HEX_QTIMER config HEX_VIRT bool diff --git a/hw/hexagon/hex-subsys.c b/hw/hexagon/hex-subsys.c new file mode 100644 index 0000000000..4e3a418340 --- /dev/null +++ b/hw/hexagon/hex-subsys.c @@ -0,0 +1,175 @@ +/* + * Hexagon subsystem helpers shared between the machine models. + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/hexagon/hex-subsys.h" +#include "hw/hexagon/hexagon_globalreg.h" +#include "hw/hexagon/hexagon_tlb.h" +#include "hw/intc/hex-l2vic.h" +#include "hw/timer/qct-qtimer.h" +#include "hw/cpu/cluster.h" +#include "hw/core/loader.h" +#include "hw/core/qdev-properties.h" +#include "hw/core/qdev.h" +#include "hw/core/sysbus.h" +#include "system/address-spaces.h" + +#define HEX_L2VIC_CPU_IRQS 8 + +/* Number of QTimer frames instantiated for every Hexagon machine. */ +#define HEX_QTIMER_NR_FRAMES 3 + +#define HEX_QTIMER_L2VIC_IRQ_BASE 2 + +static DeviceState *l2vic_create(HexagonCommonMachineState *hms, + const struct hexagon_machine_config *m_cfg) +{ + DeviceState *l2vic = qdev_new(TYPE_HEX_L2VIC); + + object_property_add_child(OBJECT(hms), "l2vic", OBJECT(l2vic)); + sysbus_realize_and_unref(SYS_BUS_DEVICE(l2vic), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(l2vic), 0, m_cfg->l2vic_base); + sysbus_mmio_map(SYS_BUS_DEVICE(l2vic), 1, + m_cfg->cfgtable.fastl2vic_base << 16); + + return l2vic; +} + +static void l2vic_connect_cpu(DeviceState *l2vic, DeviceState *cpu) +{ + int i; + + for (i = 0; i < HEX_L2VIC_CPU_IRQS; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(l2vic), i, qdev_get_gpio_in(cpu, i)); + } +} + +static DeviceState *qtimer_create(HexagonCommonMachineState *hms, + const struct hexagon_machine_config *m_cfg) +{ + DeviceState *qtimer = qdev_new(TYPE_QCT_QTIMER); + + object_property_add_child(OBJECT(hms), "qtimer", OBJECT(qtimer)); + qdev_prop_set_uint32(qtimer, "nr_frames", HEX_QTIMER_NR_FRAMES); + sysbus_realize_and_unref(SYS_BUS_DEVICE(qtimer), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(qtimer), 0, m_cfg->csr_base); + sysbus_mmio_map(SYS_BUS_DEVICE(qtimer), 1, m_cfg->qtmr_region); + for (unsigned int i = 0; i < HEX_QTIMER_NR_FRAMES; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(qtimer), i, + qdev_get_gpio_in(hms->l2vic, + HEX_QTIMER_L2VIC_IRQ_BASE + i)); + } + + return qtimer; +} + +static DeviceState *globalreg_create(HexagonCommonMachineState *hms, + const struct hexagon_machine_config *m_cfg, + Rev_t rev) +{ + DeviceState *glob_regs = qdev_new(TYPE_HEXAGON_GLOBALREG); + + object_property_add_child(OBJECT(hms), "global-regs", OBJECT(glob_regs)); + qdev_prop_set_uint64(glob_regs, "config-table-addr", m_cfg->cfgbase); + qdev_prop_set_uint32(glob_regs, "dsp-rev", rev); + object_property_set_link(OBJECT(glob_regs), "l2vic", OBJECT(hms->l2vic), + &error_fatal); + object_property_set_link(OBJECT(glob_regs), "qtimer", OBJECT(hms->qtimer), + &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(glob_regs), &error_fatal); + + return glob_regs; +} + +static DeviceState *tlb_create(HexagonCommonMachineState *hms, + const struct hexagon_machine_config *m_cfg) +{ + DeviceState *tlb = qdev_new(TYPE_HEXAGON_TLB); + + object_property_add_child(OBJECT(hms), "tlb", OBJECT(tlb)); + qdev_prop_set_uint32(tlb, "num-entries", m_cfg->cfgtable.jtlb_size_entries); + sysbus_realize_and_unref(SYS_BUS_DEVICE(tlb), &error_fatal); + + return tlb; +} + +static DeviceState *cluster_create(HexagonCommonMachineState *hms) +{ + DeviceState *cluster = qdev_new(TYPE_CPU_CLUSTER); + + object_property_add_child(OBJECT(hms), "cluster", OBJECT(cluster)); + qdev_prop_set_uint32(cluster, "cluster-id", 0); + + return cluster; +} + +void hex_subsys_create(HexagonCommonMachineState *hms, + const struct hexagon_machine_config *m_cfg, Rev_t rev) +{ + MachineState *machine = MACHINE(hms); + MemoryRegion *sysmem = get_system_memory(); + + /* Main DDR at the reset vector. */ + memory_region_init_ram(&hms->ram, NULL, "ddr.ram", machine->ram_size, + &error_fatal); + memory_region_add_subregion(sysmem, 0x0, &hms->ram); + + /* Config-table ROM and the blob that backs it. */ + memory_region_init_rom(&hms->cfgtable_rom, NULL, "config_table.rom", + sizeof(m_cfg->cfgtable), &error_fatal); + memory_region_add_subregion(sysmem, m_cfg->cfgbase, &hms->cfgtable_rom); + rom_add_blob_fixed_as("config_table.rom", &m_cfg->cfgtable, + sizeof(m_cfg->cfgtable), m_cfg->cfgbase, + &address_space_memory); + + if (m_cfg->cfgtable.vtcm_size_kb > 0) { + memory_region_init_ram(&hms->vtcm, NULL, "vtcm.ram", + m_cfg->cfgtable.vtcm_size_kb * 1024, + &error_fatal); + memory_region_add_subregion(sysmem, m_cfg->cfgtable.vtcm_base << 16, + &hms->vtcm); + } + + hms->cluster = cluster_create(hms); + hms->l2vic = l2vic_create(hms, m_cfg); + hms->qtimer = qtimer_create(hms, m_cfg); + hms->glob_regs = globalreg_create(hms, m_cfg, rev); + hms->tlb = tlb_create(hms, m_cfg); +} + +void hex_subsys_add_cpu(HexagonCommonMachineState *hms, DeviceState *cpu) +{ + object_property_add_child(OBJECT(hms->cluster), "cpu[*]", OBJECT(cpu)); + object_property_set_link(OBJECT(cpu), "global-regs", + OBJECT(hms->glob_regs), &error_fatal); + object_property_set_link(OBJECT(cpu), "tlb", OBJECT(hms->tlb), + &error_fatal); + object_property_set_link(OBJECT(cpu), "l2vic", OBJECT(hms->l2vic), + &error_fatal); +} + +void hex_subsys_realize_cluster(HexagonCommonMachineState *hms) +{ + /* + * The cluster must be realized after its CPUs have been parented into it + * (see hex_subsys_add_cpu()) but before any CPU is itself realized, since + * qdev_realize_and_unref() on a CPU latches cluster_index into the TCG + * cflags at that point. + */ + qdev_realize_and_unref(hms->cluster, NULL, &error_fatal); +} + +void hex_subsys_realize_cpu(HexagonCommonMachineState *hms, DeviceState *cpu, + bool boot_cpu) +{ + qdev_realize_and_unref(cpu, NULL, &error_fatal); + + if (boot_cpu) { + l2vic_connect_cpu(hms->l2vic, cpu); + } +} diff --git a/hw/hexagon/hexagon_dsp.c b/hw/hexagon/hexagon_dsp.c index aa49399322..20306c28e7 100644 --- a/hw/hexagon/hexagon_dsp.c +++ b/hw/hexagon/hexagon_dsp.c @@ -14,8 +14,7 @@ #include "hw/core/boards.h" #include "hw/core/qdev-properties.h" #include "hw/hexagon/hexagon.h" -#include "hw/hexagon/hexagon_globalreg.h" -#include "hw/hexagon/hexagon_tlb.h" +#include "hw/hexagon/hex-subsys.h" #include "hw/core/loader.h" #include "qapi/error.h" #include "qemu/error-report.h" @@ -108,9 +107,6 @@ static void hexagon_common_init(MachineState *machine, Rev_t rev, { HexagonCommonMachineState *hms = HEXAGON_COMMON_MACHINE(machine); HexagonDspMachineState *dms = HEXAGON_DSP_MACHINE(machine); - MemoryRegion *address_space; - DeviceState *glob_regs_dev; - DeviceState *tlb_dev; memset(&hexagon_binfo, 0, sizeof(hexagon_binfo)); if (machine->kernel_filename) { @@ -120,29 +116,9 @@ static void hexagon_common_init(MachineState *machine, Rev_t rev, machine->enable_graphics = 0; - address_space = get_system_memory(); + hex_subsys_create(hms, m_cfg, rev); - memory_region_init_rom(&hms->cfgtable_rom, NULL, "config_table.rom", - sizeof(m_cfg->cfgtable), &error_fatal); - memory_region_add_subregion(address_space, m_cfg->cfgbase, - &hms->cfgtable_rom); - - memory_region_init_ram(&hms->ram, NULL, "ddr.ram", - machine->ram_size, &error_fatal); - memory_region_add_subregion(address_space, 0x0, &hms->ram); - - glob_regs_dev = qdev_new(TYPE_HEXAGON_GLOBALREG); - object_property_add_child(OBJECT(machine), "global-regs", - OBJECT(glob_regs_dev)); - qdev_prop_set_uint64(glob_regs_dev, "config-table-addr", m_cfg->cfgbase); - qdev_prop_set_uint32(glob_regs_dev, "dsp-rev", rev); - sysbus_realize_and_unref(SYS_BUS_DEVICE(glob_regs_dev), &error_fatal); - - tlb_dev = qdev_new(TYPE_HEXAGON_TLB); - object_property_add_child(OBJECT(machine), "tlb", OBJECT(tlb_dev)); - qdev_prop_set_uint32(tlb_dev, "num-entries", - m_cfg->cfgtable.jtlb_size_entries); - sysbus_realize_and_unref(SYS_BUS_DEVICE(tlb_dev), &error_fatal); + g_autofree HexagonCPU **cpus = g_new(HexagonCPU *, machine->smp.cpus); for (int i = 0; i < machine->smp.cpus; i++) { HexagonCPU *cpu = HEXAGON_CPU(object_new(machine->cpu_type)); @@ -156,16 +132,15 @@ static void hexagon_common_init(MachineState *machine, Rev_t rev, if (i == 0) { hexagon_init_bootstrap(dms, cpu); } - object_property_set_link(OBJECT(cpu), "global-regs", - OBJECT(glob_regs_dev), &error_fatal); - object_property_set_link(OBJECT(cpu), "tlb", - OBJECT(tlb_dev), &error_fatal); - qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal); + hex_subsys_add_cpu(hms, DEVICE(cpu)); + cpus[i] = cpu; } - rom_add_blob_fixed_as("config_table.rom", &m_cfg->cfgtable, - sizeof(m_cfg->cfgtable), m_cfg->cfgbase, - &address_space_memory); + hex_subsys_realize_cluster(hms); + + for (int i = 0; i < machine->smp.cpus; i++) { + hex_subsys_realize_cpu(hms, DEVICE(cpus[i]), (i == 0)); + } } static void init_mc(MachineClass *mc) diff --git a/hw/hexagon/hexagon_globalreg.c b/hw/hexagon/hexagon_globalreg.c index b5e5913507..61621cf2b8 100644 --- a/hw/hexagon/hexagon_globalreg.c +++ b/hw/hexagon/hexagon_globalreg.c @@ -11,6 +11,8 @@ #include "hw/core/qdev-properties.h" #include "hw/core/sysbus.h" #include "hw/core/resettable.h" +#include "hw/intc/hex-l2vic.h" +#include "hw/timer/qct-qtimer.h" #include "migration/vmstate.h" #include "qom/object.h" #include "target/hexagon/cpu.h" @@ -135,6 +137,38 @@ static inline uint32_t apply_write_mask(uint32_t new_val, uint32_t cur_val, return new_val; } +static inline bool is_vid_reg(uint32_t reg) +{ + return reg == HEX_SREG_VID || reg == HEX_SREG_VID1; +} + +static inline bool is_timer_reg(uint32_t reg) +{ + return reg == HEX_SREG_TIMERLO || reg == HEX_SREG_TIMERHI; +} + +static uint32_t get_reg_value(HexagonGlobalRegState *s, uint32_t reg) +{ + if (is_vid_reg(reg)) { + return l2vic_read_vid(s->l2vic, reg == HEX_SREG_VID ? 0 : 1); + } + if (is_timer_reg(reg)) { + return reg == HEX_SREG_TIMERLO ? + qct_qtimer_get_timer_lo(s->qtimer) : + qct_qtimer_get_timer_hi(s->qtimer); + } + return s->regs[reg]; +} + +static void set_reg_value(HexagonGlobalRegState *s, uint32_t reg, + uint32_t value) +{ + s->regs[reg] = value; + if (is_vid_reg(reg)) { + l2vic_update_vid(s->l2vic, reg == HEX_SREG_VID ? 0 : 1, value); + } +} + uint32_t hexagon_globalreg_read(HexagonGlobalRegState *s, uint32_t reg, uint32_t htid) { @@ -146,7 +180,7 @@ uint32_t hexagon_globalreg_read(HexagonGlobalRegState *s, uint32_t reg, g_assert(reg < NUM_SREGS); g_assert(reg >= HEX_SREG_GLB_START); - value = s->regs[reg]; + value = get_reg_value(s, reg); trace_hexagon_globalreg_read(htid, get_sreg_name(reg), value); return value; @@ -160,7 +194,7 @@ void hexagon_globalreg_write(HexagonGlobalRegState *s, uint32_t reg, } g_assert(reg < NUM_SREGS); g_assert(reg >= HEX_SREG_GLB_START); - s->regs[reg] = value; + set_reg_value(s, reg, value); trace_hexagon_globalreg_write(htid, get_sreg_name(reg), value); } @@ -168,6 +202,7 @@ uint32_t hexagon_globalreg_masked_value(HexagonGlobalRegState *s, uint32_t reg, uint32_t value) { uint32_t reg_mask; + uint32_t cur_val; if (!s) { return value; @@ -175,9 +210,10 @@ uint32_t hexagon_globalreg_masked_value(HexagonGlobalRegState *s, uint32_t reg, g_assert(reg < NUM_SREGS); g_assert(reg >= HEX_SREG_GLB_START); reg_mask = global_sreg_immut_masks[reg]; + cur_val = get_reg_value(s, reg); return reg_mask == IMMUTABLE ? - s->regs[reg] : - apply_write_mask(value, s->regs[reg], reg_mask); + cur_val : + apply_write_mask(value, cur_val, reg_mask); } void hexagon_globalreg_write_masked(HexagonGlobalRegState *s, uint32_t reg, @@ -186,7 +222,7 @@ void hexagon_globalreg_write_masked(HexagonGlobalRegState *s, uint32_t reg, if (!s) { return; } - s->regs[reg] = hexagon_globalreg_masked_value(s, reg, value); + set_reg_value(s, reg, hexagon_globalreg_masked_value(s, reg, value)); } uint64_t hexagon_globalreg_get_pcycle_base(HexagonGlobalRegState *s) @@ -256,6 +292,20 @@ static void hexagon_globalreg_reset_hold(Object *obj, ResetType type) do_hexagon_globalreg_reset(s); } +static void hexagon_globalreg_realize(DeviceState *dev, Error **errp) +{ + HexagonGlobalRegState *s = HEXAGON_GLOBALREG(dev); + + if (!s->l2vic) { + error_setg(errp, "hexagon_globalreg: 'l2vic' link property not set"); + return; + } + if (!s->qtimer) { + error_setg(errp, "hexagon_globalreg: 'qtimer' link property not set"); + return; + } +} + static const VMStateDescription vmstate_hexagon_globalreg = { .name = "hexagon_globalreg", .version_id = 1, @@ -275,6 +325,10 @@ static const VMStateDescription vmstate_hexagon_globalreg = { }; static const Property hexagon_globalreg_properties[] = { + DEFINE_PROP_LINK("l2vic", HexagonGlobalRegState, l2vic, + TYPE_HEX_L2VIC_INTERFACE, HexL2VicInterface *), + DEFINE_PROP_LINK("qtimer", HexagonGlobalRegState, qtimer, + TYPE_QCT_QTIMER_INTERFACE, QctQtimerInterface *), DEFINE_PROP_UINT32("boot-evb", HexagonGlobalRegState, boot_evb, 0x0), DEFINE_PROP_UINT64("config-table-addr", HexagonGlobalRegState, config_table_addr, 0xffffffffULL), @@ -295,6 +349,7 @@ static void hexagon_globalreg_class_init(ObjectClass *klass, const void *data) ResettableClass *rc = RESETTABLE_CLASS(klass); rc->phases.hold = hexagon_globalreg_reset_hold; + dc->realize = hexagon_globalreg_realize; dc->vmsd = &vmstate_hexagon_globalreg; dc->user_creatable = false; device_class_set_props(dc, hexagon_globalreg_properties); diff --git a/hw/hexagon/meson.build b/hw/hexagon/meson.build index bade3a3292..720a5d54dc 100644 --- a/hw/hexagon/meson.build +++ b/hw/hexagon/meson.build @@ -1,6 +1,7 @@ hexagon_ss = ss.source_set() hexagon_ss.add(files('hexagon_tlb.c')) hexagon_ss.add(files('hexagon_globalreg.c')) +hexagon_ss.add(when: 'CONFIG_HEX_DSP', if_true: files('hex-subsys.c')) hexagon_ss.add(when: 'CONFIG_HEX_DSP', if_true: files('hexagon_dsp.c')) hexagon_ss.add(when: 'CONFIG_HEX_VIRT', if_true: files('virt.c')) diff --git a/hw/hexagon/virt.c b/hw/hexagon/virt.c index a3638998b8..64d8366d27 100644 --- a/hw/hexagon/virt.c +++ b/hw/hexagon/virt.c @@ -13,8 +13,7 @@ #include "hw/core/clock.h" #include "hw/core/sysbus-fdt.h" #include "hw/hexagon/hexagon.h" -#include "hw/hexagon/hexagon_globalreg.h" -#include "hw/hexagon/hexagon_tlb.h" +#include "hw/hexagon/hex-subsys.h" #include "hw/core/loader.h" #include "hw/core/qdev-properties.h" #include "hw/core/qdev-clock.h" @@ -31,11 +30,20 @@ enum { VIRT_UART0, + VIRT_MMIO, VIRT_FDT, }; +/* + * Virtio IRQs run from VIRTIO_IRQ_BASE to + * VIRTIO_IRQ_BASE + VIRTIO_DEV_COUNT - 1 + */ +static const int VIRTIO_IRQ_BASE = 16; +static const int VIRT_UART0_IRQ = 15; + static const MemMapEntry base_memmap[] = { [VIRT_UART0] = { 0x10000000, 0x00000200 }, + [VIRT_MMIO] = { 0x11000000, 0x00001000 }, [VIRT_FDT] = { 0x99800000, 0x00400000 }, }; @@ -68,17 +76,36 @@ static void create_fdt(HexagonVirtMachineState *vms) qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); } +static int32_t fdt_add_l2vic(HexagonVirtMachineState *vms, + const struct hexagon_machine_config *m_cfg) +{ + MachineState *ms = MACHINE(vms); + int32_t l2vic_phandle = qemu_fdt_alloc_phandle(ms->fdt); + char *nodename = g_strdup_printf("/soc/interrupt-controller@%x", + m_cfg->l2vic_base); + const char compat[] = "qcom,h2-pic\0hvm-pic"; + + qemu_fdt_setprop_cell(ms->fdt, "/soc", "interrupt-parent", l2vic_phandle); + + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#address-cells", 0x0); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 0x1); + qemu_fdt_setprop(ms->fdt, nodename, "compatible", compat, sizeof(compat)); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0, + m_cfg->l2vic_base, m_cfg->l2vic_size); + qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", l2vic_phandle); + + g_free(nodename); + return l2vic_phandle; +} + static void fdt_add_hvx(HexagonVirtMachineState *vms, const struct hexagon_machine_config *m_cfg) { const MachineState *ms = MACHINE(vms); uint32_t vtcm_size_bytes = m_cfg->cfgtable.vtcm_size_kb * 1024; if (vtcm_size_bytes > 0) { - memory_region_init_ram(&vms->vtcm, NULL, "vtcm.ram", vtcm_size_bytes, - &error_fatal); - memory_region_add_subregion(vms->sys, m_cfg->cfgtable.vtcm_base << 16, - &vms->vtcm); - qemu_fdt_add_subnode(ms->fdt, "/soc/vtcm"); qemu_fdt_setprop_string(ms->fdt, "/soc/vtcm", "compatible", "qcom,hexagon_vtcm"); @@ -117,7 +144,7 @@ static int32_t fdt_add_clocks(const HexagonVirtMachineState *vms) } static void fdt_add_uart(const HexagonVirtMachineState *vms, int uart, - int32_t clk_phandle) + int32_t clk_phandle, int32_t l2vic_phandle) { char *nodename; hwaddr base = base_memmap[uart].base; @@ -135,6 +162,8 @@ static void fdt_add_uart(const HexagonVirtMachineState *vms, int uart, qdev_connect_clock_in(dev, "clk", vms->apb_clk); sysbus_realize_and_unref(s, &error_fatal); sysbus_mmio_map(s, 0, base); + sysbus_connect_irq(s, 0, + qdev_get_gpio_in(vms->parent_obj.l2vic, VIRT_UART0_IRQ)); nodename = g_strdup_printf("/pl011@%" PRIx64, base); qemu_fdt_add_subnode(ms->fdt, nodename); @@ -142,6 +171,9 @@ static void fdt_add_uart(const HexagonVirtMachineState *vms, int uart, /* Note that we can't use setprop_string because of the embedded NUL */ qemu_fdt_setprop(ms->fdt, nodename, "compatible", compat, sizeof(compat)); qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0, base, size); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupts", VIRT_UART0_IRQ); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", + l2vic_phandle); qemu_fdt_setprop_cells(ms->fdt, nodename, "clocks", clk_phandle, clk_phandle); qemu_fdt_setprop(ms->fdt, nodename, "clock-names", clocknames, @@ -173,13 +205,37 @@ static void fdt_add_cpu_nodes(const HexagonVirtMachineState *vms) } } - - -static void virt_instance_init(Object *obj) +static void create_virtio_devices(HexagonVirtMachineState *vms, + int32_t l2vic_phandle) { - HexagonVirtMachineState *vms = HEXAGON_VIRT_MACHINE(obj); + MachineState *ms = MACHINE(vms); + hwaddr size = base_memmap[VIRT_MMIO].size; - create_fdt(vms); + for (int i = 0; i < VIRTIO_DEV_COUNT; i++) { + int irq = VIRTIO_IRQ_BASE + i; + hwaddr base = base_memmap[VIRT_MMIO].base + i * size; + char *nodename = g_strdup_printf("/soc/virtio_mmio@%" PRIx64, base); + DeviceState *dev = qdev_new("virtio-mmio"); + SysBusDevice *s = SYS_BUS_DEVICE(dev); + + object_property_add_child(OBJECT(MACHINE(vms)), "virtio-mmio[*]", + OBJECT(dev)); + sysbus_realize_and_unref(s, &error_fatal); + sysbus_mmio_map(s, 0, base); + sysbus_connect_irq(s, 0, + qdev_get_gpio_in(vms->parent_obj.l2vic, irq)); + vms->virtio_mmio[i] = dev; + + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", + "virtio,mmio"); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0, base, size); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupts", irq); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", + l2vic_phandle); + + g_free(nodename); + } } void hexagon_load_fdt(const HexagonVirtMachineState *vms) @@ -237,11 +293,10 @@ static void virt_init(MachineState *ms) { HexagonVirtMachineState *vms = HEXAGON_VIRT_MACHINE(ms); const struct hexagon_machine_config *m_cfg = &v68n_1024; - DeviceState *gsregs_dev; - DeviceState *tlb_dev; - DeviceState *cpu0; int32_t clk_phandle; + int32_t l2vic_phandle; + create_fdt(vms); qemu_fdt_setprop_string(ms->fdt, "/chosen", "bootargs", ms->kernel_cmdline); vms->sys = get_system_memory(); @@ -250,9 +305,7 @@ static void virt_init(MachineState *ms) vms->apb_clk = clock_new(OBJECT(ms), "apb-pclk"); clock_set_hz(vms->apb_clk, 24000000); - memory_region_init_ram(&vms->parent_obj.ram, NULL, "ddr.ram", - ms->ram_size, &error_fatal); - memory_region_add_subregion(vms->sys, 0x0, &vms->parent_obj.ram); + hex_subsys_create(&vms->parent_obj, m_cfg, v68_rev); if (m_cfg->l2tcm_size) { memory_region_init_ram(&vms->tcm, NULL, "tcm.ram", m_cfg->l2tcm_size, @@ -261,56 +314,41 @@ static void virt_init(MachineState *ms) &vms->tcm); } - memory_region_init_rom(&vms->parent_obj.cfgtable_rom, NULL, - "config_table.rom", sizeof(m_cfg->cfgtable), - &error_fatal); - memory_region_add_subregion(vms->sys, m_cfg->cfgbase, - &vms->parent_obj.cfgtable_rom); fdt_add_hvx(vms, m_cfg); - gsregs_dev = qdev_new(TYPE_HEXAGON_GLOBALREG); - object_property_add_child(OBJECT(ms), "global-regs", OBJECT(gsregs_dev)); - qdev_prop_set_uint64(gsregs_dev, "config-table-addr", m_cfg->cfgbase); - qdev_prop_set_uint32(gsregs_dev, "dsp-rev", v68_rev); - sysbus_realize_and_unref(SYS_BUS_DEVICE(gsregs_dev), &error_fatal); + l2vic_phandle = fdt_add_l2vic(vms, m_cfg); + create_virtio_devices(vms, l2vic_phandle); - tlb_dev = qdev_new(TYPE_HEXAGON_TLB); - object_property_add_child(OBJECT(ms), "tlb", OBJECT(tlb_dev)); - qdev_prop_set_uint32(tlb_dev, "num-entries", - m_cfg->cfgtable.jtlb_size_entries); - sysbus_realize_and_unref(SYS_BUS_DEVICE(tlb_dev), &error_fatal); + g_autofree HexagonCPU **cpus = g_new(HexagonCPU *, ms->smp.cpus); - cpu0 = NULL; for (int i = 0; i < ms->smp.cpus; i++) { HexagonCPU *cpu = HEXAGON_CPU(object_new(ms->cpu_type)); qemu_register_reset(do_cpu_reset, cpu); if (i == 0) { - cpu0 = DEVICE(cpu); if (ms->kernel_filename) { uint64_t entry = load_kernel(vms); - qdev_prop_set_uint32(cpu0, "exec-start-addr", entry); + qdev_prop_set_uint32(DEVICE(cpu), "exec-start-addr", entry); } else if (ms->firmware) { uint64_t entry = load_bios(vms); - qdev_prop_set_uint32(cpu0, "exec-start-addr", entry); + qdev_prop_set_uint32(DEVICE(cpu), "exec-start-addr", entry); } } qdev_prop_set_uint32(DEVICE(cpu), "htid", i); qdev_prop_set_bit(DEVICE(cpu), "start-powered-off", (i != 0)); - object_property_set_link(OBJECT(cpu), "global-regs", - OBJECT(gsregs_dev), &error_fatal); - object_property_set_link(OBJECT(cpu), "tlb", - OBJECT(tlb_dev), &error_fatal); - - qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal); + hex_subsys_add_cpu(&vms->parent_obj, DEVICE(cpu)); + cpus[i] = cpu; } + + hex_subsys_realize_cluster(&vms->parent_obj); + + for (int i = 0; i < ms->smp.cpus; i++) { + hex_subsys_realize_cpu(&vms->parent_obj, DEVICE(cpus[i]), (i == 0)); + } + fdt_add_cpu_nodes(vms); clk_phandle = fdt_add_clocks(vms); - fdt_add_uart(vms, VIRT_UART0, clk_phandle); - - rom_add_blob_fixed_as("config_table.rom", &m_cfg->cfgtable, - sizeof(m_cfg->cfgtable), m_cfg->cfgbase, - &address_space_memory); + fdt_add_uart(vms, VIRT_UART0, clk_phandle, l2vic_phandle); hexagon_load_fdt(vms); } @@ -341,7 +379,6 @@ static const TypeInfo virt_machine_types[] = { { .parent = TYPE_HEXAGON_COMMON_MACHINE, .instance_size = sizeof(HexagonVirtMachineState), .class_init = virt_class_init, - .instance_init = virt_instance_init, } }; DEFINE_TYPES(virt_machine_types) diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index d762163ddf..98931481b2 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -829,7 +829,7 @@ static void hppa_machine_reset(MachineState *ms, ResetType type) cpu[0]->env.cmdline_or_bootorder = 'c'; } -static void hppa_nmi(NMIState *n, int cpu_index, Error **errp) +static void hppa_nmi(NMIState *ns) { CPUState *cs; @@ -851,7 +851,7 @@ static void hppa_machine_common_class_init(ObjectClass *oc, const void *data) mc->default_ram_id = "hppa.ram"; mc->default_nic = "tulip"; - nc->nmi_monitor_handler = hppa_nmi; + nc->raise_nmi = hppa_nmi; } static void HP_B160L_machine_init_class_init(ObjectClass *oc, const void *data) diff --git a/hw/hyperv/hv-balloon.c b/hw/hyperv/hv-balloon.c index 9dd759f11e..b8664a246b 100644 --- a/hw/hyperv/hv-balloon.c +++ b/hw/hyperv/hv-balloon.c @@ -197,7 +197,7 @@ static int build_dimm_list(Object *obj, void *opaque) if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { DeviceState *dev = DEVICE(obj); - if (dev->realized) { /* only realized DIMMs matter */ + if (qdev_is_realized(dev)) { /* only realized DIMMs matter */ *list = g_slist_prepend(*list, dev); } } diff --git a/hw/hyperv/vmbus.c b/hw/hyperv/vmbus.c index c5259d1a76..ab99f082d1 100644 --- a/hw/hyperv/vmbus.c +++ b/hw/hyperv/vmbus.c @@ -1525,14 +1525,12 @@ static VMBusChannel *find_channel(VMBus *vmbus, uint32_t id) static int enqueue_incoming_message(VMBus *vmbus, const struct hyperv_post_message_input *msg) { - int ret = 0; uint8_t idx, prev_size; - qemu_mutex_lock(&vmbus->rx_queue_lock); + QEMU_LOCK_GUARD(&vmbus->rx_queue_lock); if (vmbus->rx_queue_size == HV_MSG_QUEUE_LEN) { - ret = -ENOBUFS; - goto out; + return -ENOBUFS; } prev_size = vmbus->rx_queue_size; @@ -1544,9 +1542,7 @@ static int enqueue_incoming_message(VMBus *vmbus, if (!prev_size) { vmbus_resched(vmbus); } -out: - qemu_mutex_unlock(&vmbus->rx_queue_lock); - return ret; + return 0; } static uint16_t vmbus_recv_message(const struct hyperv_post_message_input *msg, @@ -2097,10 +2093,10 @@ static void process_message(VMBus *vmbus) void *msgdata; uint32_t msglen; - qemu_mutex_lock(&vmbus->rx_queue_lock); + QEMU_LOCK_GUARD(&vmbus->rx_queue_lock); if (!vmbus->rx_queue_size) { - goto unlock; + return; } hv_msg = &vmbus->rx_queue[vmbus->rx_queue_head]; @@ -2149,8 +2145,6 @@ out: vmbus->rx_queue_head %= HV_MSG_QUEUE_LEN; vmbus_resched(vmbus); -unlock: - qemu_mutex_unlock(&vmbus->rx_queue_lock); } static const struct { diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index 27afcaecee..68bdcd0e25 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -159,6 +159,7 @@ static uint64_t aspeed_i2c_bus_new_read(AspeedI2CBus *bus, hwaddr offset, case A_I2CS_INTR_CTRL: case A_I2CS_DMA_LEN_STS: case A_I2CS_INTR_STS: + case A_I2CC_VERSION_CTRL: value = bus->regs[offset / sizeof(*bus->regs)]; break; case A_I2CC_DMA_ADDR: @@ -295,6 +296,65 @@ static int aspeed_i2c_dma_read(AspeedI2CBus *bus, uint8_t *data) return 0; } +/* + * In AST2700 buffer mode the master DMA command bits (TX/RX_DMA_EN) and the + * DMA length registers are reused, but data is moved through the controller + * internal SRAM pool at the offset programmed in I2CM_DMA_TX/RX_ADDR instead + * of DRAM. FUNC_CFG_DMA_EN selects between the two (set = DRAM). + */ +static bool aspeed_i2c_bus_dma_to_pool(AspeedI2CBus *bus) +{ + return aspeed_i2c_is_new_mode(bus->controller) && + !ARRAY_FIELD_EX32(bus->regs, I2CC_VERSION_CTRL, FUNC_CFG_DMA_EN); +} + +static int aspeed_i2c_bus_send_dma_pool(AspeedI2CBus *bus) +{ + AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); + uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); + uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); + uint32_t offset = bus->regs[R_I2CM_DMA_TX_ADDR]; + uint8_t *pool_base = aic->bus_pool_base(bus); + int ret = -1; + int i; + + ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, TX_LEN, 0); + for (i = 0; bus->regs[reg_dma_len] && + offset + i < ASPEED_I2C_BUS_POOL_SIZE; i++) { + trace_aspeed_i2c_bus_send("BUFF", i + 1, bus->regs[reg_dma_len], + pool_base[offset + i]); + ret = i2c_send(bus->bus, pool_base[offset + i]); + bus->regs[reg_dma_len]--; + ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, TX_LEN, i + 1); + if (ret) { + break; + } + } + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, TX_DMA_EN, 0); + return ret; +} + +static void aspeed_i2c_bus_recv_dma_pool(AspeedI2CBus *bus) +{ + AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); + uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); + uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); + uint32_t offset = bus->regs[R_I2CM_DMA_RX_ADDR]; + uint8_t *pool_base = aic->bus_pool_base(bus); + int i; + + ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, RX_LEN, 0); + for (i = 0; bus->regs[reg_dma_len] && + offset + i < ASPEED_I2C_BUS_POOL_SIZE; i++) { + pool_base[offset + i] = i2c_recv(bus->bus); + trace_aspeed_i2c_bus_recv("BUFF", i + 1, bus->regs[reg_dma_len], + pool_base[offset + i]); + bus->regs[reg_dma_len]--; + ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, RX_LEN, i + 1); + } + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, RX_DMA_EN, 0); +} + static int aspeed_i2c_bus_send(AspeedI2CBus *bus) { AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); @@ -320,6 +380,10 @@ static int aspeed_i2c_bus_send(AspeedI2CBus *bus) } SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, TX_BUFF_EN, 0); } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN)) { + /* In buffer mode the DMA moves data through the pool, not DRAM */ + if (aspeed_i2c_bus_dma_to_pool(bus)) { + return aspeed_i2c_bus_send_dma_pool(bus); + } /* In new mode, clear how many bytes we TXed */ if (aspeed_i2c_is_new_mode(bus->controller)) { ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, TX_LEN, 0); @@ -385,6 +449,11 @@ static void aspeed_i2c_bus_recv(AspeedI2CBus *bus) SHARED_ARRAY_FIELD_DP32(bus->regs, reg_pool_ctrl, RX_COUNT, i & 0xff); SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, RX_BUFF_EN, 0); } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_DMA_EN)) { + /* In buffer mode the DMA moves data through the pool, not DRAM */ + if (aspeed_i2c_bus_dma_to_pool(bus)) { + aspeed_i2c_bus_recv_dma_pool(bus); + return; + } /* In new mode, clear how many bytes we RXed */ if (aspeed_i2c_is_new_mode(bus->controller)) { ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, RX_LEN, 0); @@ -854,6 +923,9 @@ static void aspeed_i2c_bus_new_write(AspeedI2CBus *bus, hwaddr offset, I2CS_DMA_RX_ADDR_HI, ADDR_HI); break; + case A_I2CC_VERSION_CTRL: + bus->regs[R_I2CC_VERSION_CTRL] = value; + break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); @@ -1497,6 +1569,13 @@ static void aspeed_i2c_bus_reset_hold(Object *obj, ResetType type) memset(s->regs, 0, sizeof(s->regs)); s->pending_intr_sts = 0; i2c_end_transfer(s->bus); + /* + * I2CC_VERSION_CTRL resets to all-ones. FUNC_CFG_DMA_EN is therefore set, + * so master DMA targets DRAM unless the guest clears it to select buffer + * mode. Guests unaware of buffer mode never touch this register and keep + * doing DRAM DMA. + */ + s->regs[R_I2CC_VERSION_CTRL] = 0xffffffff; } static void aspeed_i2c_bus_realize(DeviceState *dev, Error **errp) diff --git a/hw/i2c/bcm2835_i2c.c b/hw/i2c/bcm2835_i2c.c index 34de1f36e5..5f6093087d 100644 --- a/hw/i2c/bcm2835_i2c.c +++ b/hw/i2c/bcm2835_i2c.c @@ -222,7 +222,7 @@ static void bcm2835_i2c_realize(DeviceState *dev, Error **errp) s->bus = i2c_init_bus(dev, NULL); memory_region_init_io(&s->iomem, OBJECT(dev), &bcm2835_i2c_ops, s, - TYPE_BCM2835_I2C, 0x24); + TYPE_BCM2835_I2C, 0x20); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); } diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 90252c52af..578c27ccbe 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -323,7 +323,7 @@ static void amdvi_setevent_bits(uint64_t *buffer, uint64_t value, int start, int length) { int index = start / 64, bitpos = start % 64; - uint64_t mask = MAKE_64BIT_MASK(start, length); + uint64_t mask = MAKE_64BIT_MASK(bitpos, length); buffer[index] &= ~mask; buffer[index] |= (value << bitpos) & mask; } diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index d422302c1c..5903994a7c 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -13,6 +13,8 @@ */ #include "qemu/osdep.h" +#include "system/mshv.h" +#include "system/whpx.h" #include "system/numa.h" #include "hw/acpi/acpi.h" #include "hw/acpi/aml-build.h" @@ -182,8 +184,15 @@ void fw_cfg_build_feature_control(MachineState *ms, FWCfgState *fw_cfg) uint64_t *val; cpu_x86_cpuid(env, 1, 0, &unused, &unused, &ecx, &edx); - if (ecx & CPUID_EXT_VMX) { - feature_control_bits |= FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX; + + /* + * Hyper-V in 26100 disallows this bit to be set. + * Otherwise a #GP gets raised. + */ + if (!(whpx_enabled())) { + if (ecx & CPUID_EXT_VMX) { + feature_control_bits |= FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX; + } } if ((edx & (CPUID_EXT2_MCE | CPUID_EXT2_MCA)) == diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index d1af7a3135..82c3c3b2c3 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -3021,6 +3021,8 @@ static void vtd_piotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id, { VTDIOTLBPageInvInfo info; + assert(am <= VTD_MAMV); + info.domain_id = domain_id; info.pasid = pasid; info.addr = addr; @@ -3060,6 +3062,13 @@ static bool vtd_process_piotlb_desc(IntelIOMMUState *s, case VTD_INV_DESC_PIOTLB_PSI_IN_PASID: am = VTD_INV_DESC_PIOTLB_AM(inv_desc->val[1]); + if (am > VTD_MAMV) { + error_report_once("%s: invalid piotlb inv desc: hi=0x%"PRIx64 + ", lo=0x%"PRIx64" (am=%u > VTD_MAMV=%llu)", + __func__, inv_desc->val[1], inv_desc->val[0], + am, VTD_MAMV); + return false; + } addr = (hwaddr) VTD_INV_DESC_PIOTLB_ADDR(inv_desc->val[1]); vtd_piotlb_page_invalidate(s, domain_id, pasid, addr, am, VTD_INV_DESC_PIOTLB_IH(inv_desc)); diff --git a/hw/i386/meson.build b/hw/i386/meson.build index b611fbb5a7..39ac8c9edc 100644 --- a/hw/i386/meson.build +++ b/hw/i386/meson.build @@ -38,6 +38,7 @@ i386_ss.add(when: 'CONFIG_X86_FW_OVMF', if_true: files('pc_sysfw_ovmf.c'), i386_ss.add(when: 'CONFIG_TDX', if_true: files('tdvf.c', 'tdvf-hob.c')) subdir('kvm') +subdir('mshv') subdir('xen') i386_ss.add_all(xenpv_ss) diff --git a/hw/i386/mshv/apic.c b/hw/i386/mshv/apic.c new file mode 100644 index 0000000000..ad326030bf --- /dev/null +++ b/hw/i386/mshv/apic.c @@ -0,0 +1,405 @@ +/* + * MSHV in-kernel APIC support + * + * Copyright Microsoft, Corp. 2026 + * + * Authors: Magnus Kulke + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "qemu/memalign.h" +#include "qemu/error-report.h" +#include "hw/i386/apic_internal.h" +#include "hw/i386/apic-msidef.h" +#include "hw/pci/msi.h" +#include "migration/vmstate.h" +#include "qemu/typedefs.h" +#include "system/hw_accel.h" +#include "system/mshv.h" +#include "system/mshv_int.h" + +typedef struct hv_local_interrupt_controller_state + hv_local_interrupt_controller_state; + +#define TYPE_MSHV_APIC "mshv-apic" +OBJECT_DECLARE_SIMPLE_TYPE(MshvAPICState, MSHV_APIC) + +struct MshvAPICState { + APICCommonState parent_obj; + + uint32_t apic_version; + uint32_t apic_lvt_cmci; + uint32_t apic_error_status; + uint32_t apic_counter_value; + uint32_t apic_remote_read; +}; + +static int get_lapic(int cpu_fd, + struct hv_local_interrupt_controller_state *state) +{ + int ret; + size_t size = 4096; + /* buffer aligned to 4k, as *state requires that */ + void *buffer = qemu_memalign(size, size); + struct mshv_get_set_vp_state mshv_state = { 0 }; + + mshv_state.buf_ptr = (uint64_t) buffer; + mshv_state.buf_sz = size; + mshv_state.type = MSHV_VP_STATE_LAPIC; + + ret = mshv_get_vp_state(cpu_fd, &mshv_state); + if (ret == 0) { + memcpy(state, buffer, sizeof(*state)); + } + qemu_vfree(buffer); + if (ret < 0) { + error_report("failed to get lapic"); + return -1; + } + + return 0; +} + +static int set_lapic(int cpu_fd, + const struct hv_local_interrupt_controller_state *state) +{ + int ret; + size_t size = 4096; + /* buffer aligned to 4k, as *state requires that */ + void *buffer = qemu_memalign(size, size); + struct mshv_get_set_vp_state mshv_state = { 0 }; + + if (!state) { + error_report("lapic state is NULL"); + return -1; + } + memcpy(buffer, state, sizeof(*state)); + + mshv_state.buf_ptr = (uint64_t) buffer; + mshv_state.buf_sz = size; + mshv_state.type = MSHV_VP_STATE_LAPIC; + + ret = mshv_set_vp_state(cpu_fd, &mshv_state); + qemu_vfree(buffer); + if (ret < 0) { + error_report("failed to set lapic: %s", strerror(errno)); + return -1; + } + + return 0; +} + +static void populate_apic_state(CPUState *cpu, + const hv_local_interrupt_controller_state *hv) +{ + X86CPU *x86cpu = X86_CPU(cpu); + MshvAPICState *ms = MSHV_APIC(x86cpu->apic_state); + APICCommonState *s = &ms->parent_obj; + size_t i; + + /* + * x2APIC: + * - APIC ID is the full 32-bit initial_apic_id + * - LDR is read-only, architecturally derived from the ID + * - DFR does not exist in x2APIC mode + */ + if (is_x2apic_mode(s)) { + s->initial_apic_id = hv->apic_id; + } else { + s->id = hv->apic_id >> 24; + s->log_dest = hv->apic_ldr >> 24; + s->dest_mode = hv->apic_dfr >> 28; + } + ms->apic_version = hv->apic_version; + s->spurious_vec = hv->apic_spurious; + for (i = 0; i < 8; i++) { + s->isr[i] = hv->apic_isr[i]; + s->tmr[i] = hv->apic_tmr[i]; + s->irr[i] = hv->apic_irr[i]; + } + s->esr = hv->apic_esr; + s->icr[1] = hv->apic_icr_high; + s->icr[0] = hv->apic_icr_low; + + s->lvt[APIC_LVT_TIMER] = hv->apic_lvt_timer; + s->lvt[APIC_LVT_THERMAL] = hv->apic_lvt_thermal; + s->lvt[APIC_LVT_PERFORM] = hv->apic_lvt_perfmon; + s->lvt[APIC_LVT_LINT0] = hv->apic_lvt_lint0; + s->lvt[APIC_LVT_LINT1] = hv->apic_lvt_lint1; + s->lvt[APIC_LVT_ERROR] = hv->apic_lvt_error; + ms->apic_lvt_cmci = hv->apic_lvt_cmci; + + ms->apic_error_status = hv->apic_error_status; + s->initial_count = hv->apic_initial_count; + ms->apic_counter_value = hv->apic_counter_value; + s->divide_conf = hv->apic_divide_configuration; + ms->apic_remote_read = hv->apic_remote_read; +} + +static uint32_t set_apic_delivery_mode(uint32_t reg, uint32_t mode) +{ + return ((reg) & ~0x700) | ((mode) << 8); +} + +int mshv_init_lint(CPUState *cpu) +{ + uint32_t *lvt_lint0, *lvt_lint1; + int cpu_fd = mshv_vcpufd(cpu); + int ret; + struct hv_local_interrupt_controller_state lapic_state = { 0 }; + + ret = get_lapic(cpu_fd, &lapic_state); + if (ret < 0) { + return ret; + } + + lvt_lint0 = &lapic_state.apic_lvt_lint0; + *lvt_lint0 = set_apic_delivery_mode(*lvt_lint0, APIC_DM_EXTINT); + + lvt_lint1 = &lapic_state.apic_lvt_lint1; + *lvt_lint1 = set_apic_delivery_mode(*lvt_lint1, APIC_DM_NMI); + + /* TODO: should we skip setting lapic if the values are the same? */ + + ret = set_lapic(cpu_fd, &lapic_state); + if (ret < 0) { + return -1; + } + + populate_apic_state(cpu, &lapic_state); + + return 0; +} + +static void populate_hv_lapic_state(hv_local_interrupt_controller_state *hv, + const CPUState *cpu) +{ + uint32_t x2apic_id; + X86CPU *x86cpu = X86_CPU(cpu); + MshvAPICState *ms = MSHV_APIC(x86cpu->apic_state); + APICCommonState *s = &ms->parent_obj; + size_t i; + + /* + * x2APIC: + * - APIC ID is the full 32-bit initial_apic_id + * - LDR is read-only, architecturally derived from the ID + * - DFR does not exist in x2APIC mode + */ + if (is_x2apic_mode(s)) { + x2apic_id = s->initial_apic_id; + + hv->apic_id = x2apic_id; + hv->apic_ldr = ((x2apic_id >> 4) << 16) | (1 << (x2apic_id & 0xf)); + hv->apic_dfr = 0; + } else { + hv->apic_id = s->id << 24; + hv->apic_ldr = s->log_dest << 24; + hv->apic_dfr = s->dest_mode << 28 | 0x0fffffff; + } + hv->apic_version = ms->apic_version; + hv->apic_spurious = s->spurious_vec; + for (i = 0; i < 8; i++) { + hv->apic_isr[i] = s->isr[i]; + hv->apic_tmr[i] = s->tmr[i]; + hv->apic_irr[i] = s->irr[i]; + } + hv->apic_esr = s->esr; + hv->apic_icr_high = s->icr[1]; + hv->apic_icr_low = s->icr[0]; + + hv->apic_lvt_timer = s->lvt[APIC_LVT_TIMER]; + hv->apic_lvt_thermal = s->lvt[APIC_LVT_THERMAL]; + hv->apic_lvt_perfmon = s->lvt[APIC_LVT_PERFORM]; + hv->apic_lvt_lint0 = s->lvt[APIC_LVT_LINT0]; + hv->apic_lvt_lint1 = s->lvt[APIC_LVT_LINT1]; + hv->apic_lvt_error = s->lvt[APIC_LVT_ERROR]; + hv->apic_lvt_cmci = ms->apic_lvt_cmci; + + hv->apic_error_status = ms->apic_error_status; + hv->apic_initial_count = s->initial_count; + hv->apic_counter_value = ms->apic_counter_value; + hv->apic_divide_configuration = s->divide_conf; + hv->apic_remote_read = ms->apic_remote_read; +} + +int mshv_set_lapic(const CPUState *cpu) +{ + int cpu_fd = mshv_vcpufd(cpu); + struct hv_local_interrupt_controller_state lapic_state = { 0 }; + + populate_hv_lapic_state(&lapic_state, cpu); + + return set_lapic(cpu_fd, &lapic_state); +} + +int mshv_get_lapic(CPUState *cpu) +{ + int cpu_fd = mshv_vcpufd(cpu); + int ret; + struct hv_local_interrupt_controller_state lapic_state = { 0 }; + + ret = get_lapic(cpu_fd, &lapic_state); + if (ret < 0) { + return -1; + } + + populate_apic_state(cpu, &lapic_state); + + return 0; +} + +static int mshv_apic_set_base(APICCommonState *s, uint64_t val) +{ + s->apicbase = val; + + return 0; +} + +static void mshv_apic_set_tpr(APICCommonState *s, uint8_t val) +{ + s->tpr = (val & APIC_PR_SUB_CLASS) << APIC_PR_CLASS_SHIFT; +} + +static uint8_t mshv_apic_get_tpr(APICCommonState *s) +{ + return s->tpr >> APIC_PR_CLASS_SHIFT; +} + +static void mshv_apic_external_nmi(APICCommonState *s) +{ +} + +static void mshv_apic_vapic_base_update(APICCommonState *s) +{ +} + +static void mshv_send_msi(MSIMessage *msi) +{ + uint64_t addr; + uint32_t data, dest; + uint8_t vector, dest_mode, trigger_mode, delivery; + + addr = msi->address; + data = msi->data; + dest = (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT | + (addr >> 32); + vector = (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT; + dest_mode = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1; + trigger_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1; + delivery = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) & + MSI_DATA_DELIVERY_MODE_MASK; + + /* + * Vector 0 is not a valid interrupt vector (0-15 are reserved for CPU + * exceptions). This can trigger during machine reset, if hpet_reset() + * forces the PIT to pulse GSI 2 before IOAPIC's own reset has masked its + * redirection entries. + */ + if (vector == 0) { + return; + } + + mshv_request_interrupt(mshv_state, delivery, vector, dest, dest_mode, + trigger_mode); +} + +static uint64_t mshv_apic_mem_read(void *opaque, hwaddr addr, + unsigned size) +{ + return UINT64_MAX; +} + +static void mshv_apic_mem_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + MSIMessage msg = { .address = addr, .data = data }; + + mshv_send_msi(&msg); +} + +static const MemoryRegionOps mshv_apic_io_ops = { + .read = mshv_apic_mem_read, + .write = mshv_apic_mem_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void mshv_apic_reset(APICCommonState *s) +{ + s->wait_for_sipi = 0; +} + +static const VMStateDescription vmstate_mshv_apic = { + .name = "mshv-apic", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(apic_version, MshvAPICState), + VMSTATE_UINT32(apic_lvt_cmci, MshvAPICState), + VMSTATE_UINT32(apic_error_status, MshvAPICState), + VMSTATE_UINT32(apic_counter_value, MshvAPICState), + VMSTATE_UINT32(apic_remote_read, MshvAPICState), + VMSTATE_END_OF_LIST() + } +}; + +static void mshv_apic_realize(DeviceState *dev, Error **errp) +{ + APICCommonState *s = APIC_COMMON(dev); + MshvAPICState *ms = MSHV_APIC(dev); + + memory_region_init_io(&s->io_memory, OBJECT(s), &mshv_apic_io_ops, s, + "mshv-apic-msi", APIC_SPACE_SIZE); + + msi_nonbroken = true; + + /* + * We register this state explicity, rather than going via dc->vmsd. + * The auto-wiring would register the state with + * instance_id == VMSTATE_INSTANCE_ID_ANY, which for the APIC doesn't + * work, b/c the ID carries semantic meaning for restoring the state + * on the destination (which vcpu it belongs to). + */ + vmstate_register_with_alias_id(NULL, + s->initial_apic_id, &vmstate_mshv_apic, ms, + -1, 0, NULL); +} + +static void mshv_apic_unrealize(DeviceState *dev) +{ + MshvAPICState *ms = MSHV_APIC(dev); + + vmstate_unregister(NULL, &vmstate_mshv_apic, ms); +} + +static void mshv_apic_class_init(ObjectClass *klass, const void *data) +{ + APICCommonClass *k = APIC_COMMON_CLASS(klass); + + k->realize = mshv_apic_realize; + k->unrealize = mshv_apic_unrealize; + k->reset = mshv_apic_reset; + k->set_base = mshv_apic_set_base; + k->set_tpr = mshv_apic_set_tpr; + k->get_tpr = mshv_apic_get_tpr; + k->external_nmi = mshv_apic_external_nmi; + k->vapic_base_update = mshv_apic_vapic_base_update; + k->send_msi = mshv_send_msi; +} + +static const TypeInfo mshv_apic_info = { + .name = TYPE_MSHV_APIC, + .parent = TYPE_APIC_COMMON, + .instance_size = sizeof(MshvAPICState), + .class_init = mshv_apic_class_init, +}; + +static void mshv_apic_register_types(void) +{ + type_register_static(&mshv_apic_info); +} + +type_init(mshv_apic_register_types) diff --git a/hw/i386/mshv/clock.c b/hw/i386/mshv/clock.c new file mode 100644 index 0000000000..02c586503a --- /dev/null +++ b/hw/i386/mshv/clock.c @@ -0,0 +1,189 @@ +/* + * MSHV partition reference clock + * + * Copyright Microsoft, Corp. 2026 + * + * Authors: Magnus Kulke + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "migration/vmstate.h" +#include "system/runstate.h" +#include "hw/hyperv/hvhdk.h" +#include "hw/hyperv/hvhdk_mini.h" +#include "hw/hyperv/hvgdk.h" +#include "hw/hyperv/hvgdk_mini.h" +#include "linux/mshv.h" +#include "system/mshv.h" +#include "system/mshv_int.h" + +/* + * Partition reference clock (HV_PARTITION_PROPERTY_REFERENCE_TIME), captured + * when the VM is stopped and re-applied when it resumes. + * + * Mirrors hw/i386/kvm/clock.c. + */ +typedef struct MshvClockState { + uint64_t ref_time; + bool ref_time_pending; +} MshvClockState; + +static MshvClockState mshv_clock; + +static int mshv_get_reference_time(int vm_fd, uint64_t *ref_time) +{ + struct hv_input_get_partition_property in = { 0 }; + struct hv_output_get_partition_property out = { 0 }; + struct mshv_root_hvcall args = { 0 }; + int ret; + + in.property_code = HV_PARTITION_PROPERTY_REFERENCE_TIME; + + args.code = HVCALL_GET_PARTITION_PROPERTY; + args.in_sz = sizeof(in); + args.in_ptr = (uint64_t)∈ + args.out_sz = sizeof(out); + args.out_ptr = (uint64_t)&out; + + ret = mshv_hvcall(vm_fd, &args); + if (ret < 0) { + error_report("Failed to get reference time"); + return -1; + } + + *ref_time = out.property_value; + return 0; +} + +static int mshv_set_reference_time(int vm_fd, uint64_t ref_time) +{ + struct hv_input_set_partition_property in = { 0 }; + struct mshv_root_hvcall args = { 0 }; + int ret; + + in.property_code = HV_PARTITION_PROPERTY_REFERENCE_TIME; + in.property_value = ref_time; + + args.code = HVCALL_SET_PARTITION_PROPERTY; + args.in_sz = sizeof(in); + args.in_ptr = (uint64_t)∈ + + ret = mshv_hvcall(vm_fd, &args); + if (ret < 0) { + error_report("Failed to set reference time"); + return -1; + } + + return 0; +} + +/* + * Freeze (freeze=1) or unfreeze (freeze=0) time for a partition. This will not + * pause/eject vCPU execution. It is assumed that the caller already has stopped + * the partition's vCPUs. + * + * NB: a partition's reference clock can only be written while the time is + * frozen. + */ +static int mshv_set_time_freeze(int vm_fd, int freeze) +{ + struct hv_input_set_partition_property in = { 0 }; + struct mshv_root_hvcall args = { 0 }; + int ret; + + in.property_code = HV_PARTITION_PROPERTY_TIME_FREEZE; + in.property_value = freeze; + + args.code = HVCALL_SET_PARTITION_PROPERTY; + args.in_sz = sizeof(in); + args.in_ptr = (uint64_t)∈ + + ret = mshv_hvcall(vm_fd, &args); + if (ret < 0) { + error_report("Failed to set time freeze"); + return -1; + } + + return 0; +} + +static void mshv_clock_vm_state_change(void *opaque, bool running, + RunState state) +{ + MshvClockState *s = opaque; + int vm_fd = mshv_state->vm; + int ret; + + if (running) { + /* Skip if we have nothing to restore, e.g. on initial boot. */ + if (!s->ref_time_pending) { + return; + } + + ret = mshv_set_time_freeze(vm_fd, 1); + if (ret < 0) { + error_report("Failed to freeze partition time on resume"); + abort(); + } + + /* INVARIANT: reference time can only be written if time is frozen. */ + ret = mshv_set_reference_time(vm_fd, s->ref_time); + if (ret < 0) { + error_report("Failed to restore reference time on resume"); + abort(); + } + + if (mshv_set_time_freeze(vm_fd, 0) < 0) { + error_report("Failed to unfreeze partition time on resume"); + abort(); + } + + s->ref_time_pending = false; + } else { + /* Skip if we already have a to-be-set ref time */ + if (s->ref_time_pending) { + return; + } + + ret = mshv_get_reference_time(vm_fd, &s->ref_time); + if (ret < 0) { + error_report("Failed to capture reference time on stop"); + abort(); + } + + s->ref_time_pending = true; + } +} + +/* + * The incoming reference time should be applied on the next resume before + * vCPUs start executing. + */ +static int mshv_clock_post_load(void *opaque, int version_id) +{ + MshvClockState *s = opaque; + + s->ref_time_pending = true; + + return 0; +} + +static const VMStateDescription vmstate_mshv_clock = { + .name = "mshv-clock", + .version_id = 1, + .minimum_version_id = 1, + .post_load = mshv_clock_post_load, + .fields = (const VMStateField[]) { + VMSTATE_UINT64(ref_time, MshvClockState), + VMSTATE_END_OF_LIST() + }, +}; + +void mshv_clock_init(void) +{ + vmstate_register(NULL, 0, &vmstate_mshv_clock, &mshv_clock); + qemu_add_vm_change_state_handler(mshv_clock_vm_state_change, &mshv_clock); +} diff --git a/hw/i386/mshv/meson.build b/hw/i386/mshv/meson.build new file mode 100644 index 0000000000..c631ee1302 --- /dev/null +++ b/hw/i386/mshv/meson.build @@ -0,0 +1,5 @@ +i386_mshv_ss = ss.source_set() +i386_mshv_ss.add(files('clock.c')) +i386_mshv_ss.add(when: 'CONFIG_APIC', if_true: files('apic.c')) + +i386_ss.add_all(when: 'CONFIG_MSHV', if_true: i386_mshv_ss) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index f064aa2b3e..e9e4fc262b 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -74,6 +74,9 @@ #include "hw/xen/xen-bus.h" #endif +GlobalProperty pc_compat_11_1[] = {}; +const size_t pc_compat_11_1_len = G_N_ELEMENTS(pc_compat_11_1); + GlobalProperty pc_compat_11_0[] = {}; const size_t pc_compat_11_0_len = G_N_ELEMENTS(pc_compat_11_0); diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 82457bdb16..a929cc4dec 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -428,12 +428,21 @@ static void pc_i440fx_machine_options(MachineClass *m) pc_piix_compat_defaults, pc_piix_compat_defaults_len); } -static void pc_i440fx_machine_11_1_options(MachineClass *m) +static void pc_i440fx_machine_11_2_options(MachineClass *m) { pc_i440fx_machine_options(m); } -DEFINE_I440FX_MACHINE_AS_LATEST(11, 1); +DEFINE_I440FX_MACHINE_AS_LATEST(11, 2); + +static void pc_i440fx_machine_11_1_options(MachineClass *m) +{ + pc_i440fx_machine_11_2_options(m); + compat_props_add(m->compat_props, hw_compat_11_1, hw_compat_11_1_len); + compat_props_add(m->compat_props, pc_compat_11_1, pc_compat_11_1_len); +} + +DEFINE_I440FX_MACHINE(11, 1); static void pc_i440fx_machine_11_0_options(MachineClass *m) { @@ -658,6 +667,7 @@ static void xenfv_machine_4_2_options(MachineClass *m) { pc_i440fx_machine_4_2_options(m); m->desc = "Xen Fully-virtualized PC"; + m->alias = "xenfv"; m->max_cpus = HVM_MAX_VCPUS; m->default_machine_opts = "accel=xen,suppress-vmdesc=on"; } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 6c1e4eff5f..94cd71124b 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -383,12 +383,21 @@ static void pc_q35_machine_options(MachineClass *m) pc_q35_compat_defaults, pc_q35_compat_defaults_len); } -static void pc_q35_machine_11_1_options(MachineClass *m) +static void pc_q35_machine_11_2_options(MachineClass *m) { pc_q35_machine_options(m); } -DEFINE_Q35_MACHINE_AS_LATEST(11, 1); +DEFINE_Q35_MACHINE_AS_LATEST(11, 2); + +static void pc_q35_machine_11_1_options(MachineClass *m) +{ + pc_q35_machine_11_2_options(m); + compat_props_add(m->compat_props, hw_compat_11_1, hw_compat_11_1_len); + compat_props_add(m->compat_props, pc_compat_11_1, pc_compat_11_1_len); +} + +DEFINE_Q35_MACHINE(11, 1); static void pc_q35_machine_11_0_options(MachineClass *m) { diff --git a/hw/i386/x86-cpu.c b/hw/i386/x86-cpu.c index 95e08e3c2a..fba313376c 100644 --- a/hw/i386/x86-cpu.c +++ b/hw/i386/x86-cpu.c @@ -22,6 +22,7 @@ */ #include "qemu/osdep.h" #include "system/whpx.h" +#include "system/mshv.h" #include "system/cpu-timers.h" #include "trace.h" @@ -44,6 +45,11 @@ static void pic_irq_request(void *opaque, int irq, int level) X86CPU *cpu = X86_CPU(cs); trace_x86_pic_interrupt(irq, level); + + if (mshv_irqchip_in_kernel()) { + return; + } + if (cpu_is_apic_enabled(cpu->apic_state) && !kvm_irqchip_in_kernel() && !whpx_irqchip_in_kernel()) { CPU_FOREACH(cs) { diff --git a/hw/i386/x86.c b/hw/i386/x86.c index dc7f0d56b0..f8ba3244e2 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -147,7 +147,7 @@ static const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) return ms->possible_cpus; } -static void x86_nmi(NMIState *n, int cpu_index, Error **errp) +static void x86_nmi(NMIState *ns) { /* cpu index isn't used */ CPUState *cs; @@ -389,7 +389,7 @@ static void x86_machine_class_init(ObjectClass *oc, const void *data) mc->get_default_cpu_node_id = x86_get_default_cpu_node_id; mc->possible_cpu_arch_ids = x86_possible_cpu_arch_ids; mc->kvm_type = x86_kvm_type; - nc->nmi_monitor_handler = x86_nmi; + nc->raise_nmi = x86_nmi; object_class_property_add(oc, X86_MACHINE_SMM, "OnOffAuto", x86_machine_get_smm, x86_machine_set_smm, diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 749f0efa1d..6b04762c4a 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -37,7 +37,7 @@ static void check_cmd(AHCIState *s, int port); static void handle_cmd(AHCIState *s, int port, uint8_t slot); -static void ahci_reset_port(AHCIState *s, int port); +static void ahci_reset_port(AHCIState *s, int port, IDEResetKind kind); static bool ahci_write_fis_d2h(AHCIDevice *ad, bool d2h_fis_i); static void ahci_clear_cmd_issue(AHCIDevice *ad, uint8_t slot); static void ahci_init_d2h(AHCIDevice *ad); @@ -334,7 +334,7 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) case AHCI_PORT_REG_SCR_CTL: if (((pr->scr_ctl & AHCI_SCR_SCTL_DET) == 1) && ((val & AHCI_SCR_SCTL_DET) == 0)) { - ahci_reset_port(s, port); + ahci_reset_port(s, port, IDE_RESET_HARDWARE); } pr->scr_ctl = val; break; @@ -619,35 +619,12 @@ static void ahci_set_signature(AHCIDevice *ad, uint32_t sig) s->lcyl, s->hcyl, sig); } -static void ahci_reset_port(AHCIState *s, int port) +static void ahci_cancel_ncq_requests(AHCIDevice *ad) { - AHCIDevice *d = &s->dev[port]; - AHCIPortRegs *pr = &d->port_regs; - IDEState *ide_state = &d->port.ifs[0]; int i; - trace_ahci_reset_port(s, port); - - ide_bus_reset(&d->port); - ide_state->ncq_queues = AHCI_MAX_CMDS; - - pr->scr_stat = 0; - pr->scr_err = 0; - pr->scr_act = 0; - pr->tfdata = 0x7F; - pr->sig = 0xFFFFFFFF; - pr->cmd_issue = 0; - d->busy_slot = -1; - d->init_d2h_sent = false; - - ide_state = &s->dev[port].port.ifs[0]; - if (!ide_state->blk) { - return; - } - - /* reset ncq queue */ for (i = 0; i < AHCI_MAX_CMDS; i++) { - NCQTransferState *ncq_tfs = &s->dev[port].ncq_tfs[i]; + NCQTransferState *ncq_tfs = &ad->ncq_tfs[i]; ncq_tfs->halt = false; if (!ncq_tfs->used) { continue; @@ -666,6 +643,34 @@ static void ahci_reset_port(AHCIState *s, int port) qemu_sglist_destroy(&ncq_tfs->sglist); ncq_tfs->used = 0; } +} + +static void ahci_reset_port(AHCIState *s, int port, IDEResetKind kind) +{ + AHCIDevice *d = &s->dev[port]; + AHCIPortRegs *pr = &d->port_regs; + IDEState *ide_state = &d->port.ifs[0]; + + trace_ahci_reset_port(s, port); + + ide_bus_reset(&d->port, kind); + ide_state->ncq_queues = AHCI_MAX_CMDS; + + pr->scr_stat = 0; + pr->scr_err = 0; + pr->scr_act = 0; + pr->tfdata = 0x7F; + pr->sig = 0xFFFFFFFF; + pr->cmd_issue = 0; + d->busy_slot = -1; + d->init_d2h_sent = false; + + ide_state = &s->dev[port].port.ifs[0]; + if (!ide_state->blk) { + return; + } + + ahci_cancel_ncq_requests(d); s->dev[port].port_state = STATE_RUN; if (ide_state->drive_kind == IDE_CD) { @@ -743,6 +748,12 @@ static void ahci_unmap_clb_address(AHCIDevice *ad) /* Cancel in-flight reads that would complete against a cleared cur_cmd. */ ide_cancel_dma_sync(ide_bus_active_if(&ad->port)); + /* + * Whatever survives the cancel must not be left pointing into the + * mapping this function is about to drop. + */ + ad->cur_cmd = NULL; + if (ad->lst == NULL) { trace_ahci_unmap_clb_address_null(ad->hba, ad->port_no); return; @@ -906,12 +917,12 @@ static int prdt_tbl_entry_size(const AHCI_SG *tbl) static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, AHCICmdHdr *cmd, int64_t limit, uint64_t offset) { - uint16_t opts = le16_to_cpu(cmd->opts); - uint16_t prdtl = le16_to_cpu(cmd->prdtl); - uint64_t cfis_addr = le64_to_cpu(cmd->tbl_addr); - uint64_t prdt_addr = cfis_addr + 0x80; - dma_addr_t prdt_len = (prdtl * sizeof(AHCI_SG)); - dma_addr_t real_prdt_len = prdt_len; + uint16_t opts; + uint16_t prdtl; + uint64_t cfis_addr; + uint64_t prdt_addr; + dma_addr_t prdt_len; + dma_addr_t real_prdt_len; uint8_t *prdt; int i; int r = 0; @@ -923,6 +934,18 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, trace_ahci_populate_sglist(ad->hba, ad->port_no); + if (!cmd) { + trace_ahci_populate_sglist_no_cmd(ad->hba, ad->port_no); + return -1; + } + + opts = le16_to_cpu(cmd->opts); + prdtl = le16_to_cpu(cmd->prdtl); + cfis_addr = le64_to_cpu(cmd->tbl_addr); + prdt_addr = cfis_addr + 0x80; + prdt_len = (prdtl * sizeof(AHCI_SG)); + real_prdt_len = prdt_len; + if (!prdtl) { trace_ahci_populate_sglist_no_prdtl(ad->hba, ad->port_no, opts); return -1; @@ -1244,7 +1267,7 @@ static void handle_reg_h2d_fis(AHCIState *s, int port, * COMRESET or by setting and clearing the SRST bit. Therefore, * the logic for this is found in ahci_init_d2h() and not here. */ - ahci_reset_port(s, port); + ahci_reset_port(s, port, IDE_RESET_SOFTWARE); } break; } @@ -1316,6 +1339,7 @@ static void handle_cmd(AHCIState *s, int port, uint8_t slot) AHCICmdHdr *cmd; uint8_t *cmd_fis; dma_addr_t cmd_len; + uint8_t cfl; if (s->dev[port].port.ifs[0].status & (BUSY_STAT|DRQ_STAT)) { /* Engine currently busy, try again later */ @@ -1328,6 +1352,14 @@ static void handle_cmd(AHCIState *s, int port, uint8_t slot) return; } cmd = get_cmd_header(s, port, slot); + + /* AHCI 1.3.1: a CFL below 2 dwords or above 16 is illegal */ + cfl = le16_to_cpu(cmd->opts) & AHCI_CMD_HDR_CMD_FIS_LEN; + if (cfl < 2 || cfl > 16) { + trace_handle_cmd_badcfl(s, port, le16_to_cpu(cmd->opts)); + return; + } + /* remember current slot handle for later */ s->dev[port].cur_cmd = cmd; @@ -1371,18 +1403,27 @@ out: } /* Transfer PIO data between RAM and device */ -static void ahci_pio_transfer(const IDEDMA *dma) +static bool ahci_pio_transfer(const IDEDMA *dma) { AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); IDEState *s = &ad->port.ifs[0]; uint32_t size = (uint32_t)(s->data_end - s->data_ptr); /* write == ram -> device */ - uint16_t opts = le16_to_cpu(ad->cur_cmd->opts); - int is_write = opts & AHCI_CMD_WRITE; - int is_atapi = opts & AHCI_CMD_ATAPI; + uint16_t opts; + int is_write; + int is_atapi; int has_sglist = 0; bool pio_fis_i; + if (ad->cur_cmd == NULL) { + trace_ahci_pio_transfer_no_cmd(ad->hba, ad->port_no); + return false; + } + + opts = le16_to_cpu(ad->cur_cmd->opts); + is_write = opts & AHCI_CMD_WRITE; + is_atapi = opts & AHCI_CMD_ATAPI; + /* The PIO Setup FIS is received prior to transfer, but the interrupt * is only triggered after data is received. * @@ -1401,7 +1442,7 @@ static void ahci_pio_transfer(const IDEDMA *dma) goto out; } - if (ahci_dma_prepare_buf(dma, size)) { + if (ahci_dma_prepare_buf(dma, size) > 0) { has_sglist = 1; } @@ -1430,6 +1471,8 @@ out: if (pio_fis_i) { ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_PSS); } + + return true; } static void ahci_start_dma(const IDEDMA *dma, IDEState *s, @@ -1492,6 +1535,10 @@ static void ahci_commit_buf(const IDEDMA *dma, uint32_t tx_bytes) { AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); + if (ad->cur_cmd == NULL) { + return; + } + tx_bytes += le32_to_cpu(ad->cur_cmd->status); ad->cur_cmd->status = cpu_to_le32(tx_bytes); } @@ -1617,8 +1664,29 @@ void ahci_uninit(AHCIState *s) for (i = 0; i < s->ports; i++) { AHCIDevice *ad = &s->dev[i]; + /* + * Unplug does not go through a reset, so this is the only chance to + * detach the requests and the bottom half that would otherwise walk + * s->dev after it is freed below. + */ + ahci_cancel_ncq_requests(ad); + if (ad->check_bh) { + qemu_bh_delete(ad->check_bh); + ad->check_bh = NULL; + } + for (j = 0; j < 2; j++) { - ide_exit(&ad->port.ifs[j]); + IDEState *ide_state = &ad->port.ifs[j]; + + /* + * Everything the port still owns points into the allocation this + * function frees, io_buffer included, so nothing may be left in + * flight once ide_exit() has run. + */ + if (ide_state->blk) { + blk_drain(ide_state->blk); + } + ide_exit(ide_state); } object_unparent(OBJECT(&ad->port)); } @@ -1650,7 +1718,7 @@ void ahci_reset(AHCIState *s) pr->irq_mask = 0; pr->scr_ctl = 0; pr->cmd = PORT_CMD_SPIN_UP | PORT_CMD_POWER_ON; - ahci_reset_port(s, i); + ahci_reset_port(s, i, IDE_RESET_HARDWARE); } } diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index a42b748521..0ea149ad8c 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -88,46 +88,14 @@ static void cd_data_to_raw(uint8_t *buf, int lba) memset(buf, 0, 288); } -static int -cd_read_sector_sync(IDEState *s) -{ - int ret; - block_acct_start(blk_get_stats(s->blk), &s->acct, - ATAPI_SECTOR_SIZE, BLOCK_ACCT_READ); - - trace_cd_read_sector_sync(s->lba); - - switch (s->cd_sector_size) { - case 2048: - ret = blk_pread(s->blk, (int64_t)s->lba << ATAPI_SECTOR_BITS, - ATAPI_SECTOR_SIZE, s->io_buffer, 0); - break; - case 2352: - ret = blk_pread(s->blk, (int64_t)s->lba << ATAPI_SECTOR_BITS, - ATAPI_SECTOR_SIZE, s->io_buffer + 16, 0); - if (ret >= 0) { - cd_data_to_raw(s->io_buffer, s->lba); - } - break; - default: - block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ); - return -EIO; - } - - if (ret < 0) { - block_acct_failed(blk_get_stats(s->blk), &s->acct); - } else { - block_acct_done(blk_get_stats(s->blk), &s->acct); - s->lba++; - s->io_buffer_index = 0; - } - - return ret; -} - static void cd_read_sector_cb(void *opaque, int ret) { IDEState *s = opaque; + int et = s->elementary_transfer_size; + int skip = s->io_buffer_index; + int nsec = DIV_ROUND_UP(skip + et, s->cd_sector_size); + uint8_t *buf; + int i; trace_cd_read_sector_cb(s->lba, ret); @@ -140,34 +108,64 @@ static void cd_read_sector_cb(void *opaque, int ret) block_acct_done(blk_get_stats(s->blk), &s->acct); if (s->cd_sector_size == 2352) { - cd_data_to_raw(s->io_buffer, s->lba); + /* unpack back-to-front so a sector never clobbers an unmoved one */ + for (i = nsec - 1; i >= 0; i--) { + memmove(s->io_buffer + i * 2352 + 16, s->io_buffer + i * 2048, + ATAPI_SECTOR_SIZE); + cd_data_to_raw(s->io_buffer + i * 2352, s->lba + i); + } } - s->lba++; - s->io_buffer_index = 0; s->status &= ~BUSY_STAT; - ide_atapi_cmd_reply_end(s); + s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO; + s->lcyl = et & 0xff; + s->hcyl = (et >> 8) & 0xff; + ide_bus_set_irq(s->bus); + + /* a boundary sector shared with the next burst is re-read there */ + buf = s->io_buffer + skip; + s->packet_transfer_size -= et; + s->lba += (skip + et) / s->cd_sector_size; + s->io_buffer_index = (skip + et) % s->cd_sector_size; + s->elementary_transfer_size = 0; + + if (ide_transfer_start_norecurse(s, buf, et, ide_atapi_cmd_reply_end)) { + ide_atapi_cmd_reply_end(s); + } } +/* + * Read the whole elementary transfer (one DRQ burst) in a single async + * request. No read is issued mid-burst, so unlike the old synchronous + * rebuffer it cannot deadlock against a concurrent drain. + */ static int cd_read_sector(IDEState *s) { - void *buf; + int et = s->elementary_transfer_size; + int skip = s->io_buffer_index; + int nsec = DIV_ROUND_UP(skip + et, s->cd_sector_size); if (s->cd_sector_size != 2048 && s->cd_sector_size != 2352) { block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ); return -EINVAL; } - buf = (s->cd_sector_size == 2352) ? s->io_buffer + 16 : s->io_buffer; - qemu_iovec_init_buf(&s->qiov, buf, ATAPI_SECTOR_SIZE); + /* a burst is bounded by the byte count limit, so it fits io_buffer */ + assert(nsec * s->cd_sector_size <= s->io_buffer_total_len); + + /* + * Read the payload packed at the front of io_buffer; the 2352 raw case is + * unpacked into place on completion. + */ + qemu_iovec_init_buf(&s->qiov, s->io_buffer, nsec * ATAPI_SECTOR_SIZE); trace_cd_read_sector(s->lba); block_acct_start(blk_get_stats(s->blk), &s->acct, - ATAPI_SECTOR_SIZE, BLOCK_ACCT_READ); + nsec * ATAPI_SECTOR_SIZE, BLOCK_ACCT_READ); - ide_buffered_readv(s, (int64_t)s->lba << 2, &s->qiov, 4, + ide_buffered_readv(s, (int64_t)s->lba << 2, &s->qiov, nsec * 4, cd_read_sector_cb, s); s->status |= BUSY_STAT; @@ -222,59 +220,49 @@ static uint16_t atapi_byte_count_limit(IDEState *s) void ide_atapi_cmd_reply_end(IDEState *s) { int byte_count_limit, size, ret; - while (s->packet_transfer_size > 0) { - trace_ide_atapi_cmd_reply_end(s, s->packet_transfer_size, - s->elementary_transfer_size, - s->io_buffer_index); - /* see if a new sector must be read */ - if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) { - if (!s->elementary_transfer_size) { - ret = cd_read_sector(s); - if (ret < 0) { - ide_atapi_io_error(s, ret); - } - return; - } else { - /* rebuffering within an elementary transfer is - * only possible with a sync request because we - * end up with a race condition otherwise */ - ret = cd_read_sector_sync(s); - if (ret < 0) { - ide_atapi_io_error(s, ret); - return; - } + trace_ide_atapi_cmd_reply_end(s, s->packet_transfer_size, + s->elementary_transfer_size, + s->io_buffer_index); + + if (s->lba != -1 && s->packet_transfer_size > 0) { + byte_count_limit = atapi_byte_count_limit(s); + trace_ide_atapi_cmd_reply_end_bcl(s, byte_count_limit); + size = s->packet_transfer_size; + if (size > byte_count_limit) { + /* byte count limit must be even if this case */ + if (byte_count_limit & 1) { + byte_count_limit--; } + size = byte_count_limit; } - if (s->elementary_transfer_size > 0) { - /* there are some data left to transmit in this elementary - transfer */ - size = s->cd_sector_size - s->io_buffer_index; - if (size > s->elementary_transfer_size) - size = s->elementary_transfer_size; - } else { - /* a new transfer is needed */ - s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO; - ide_bus_set_irq(s->bus); - byte_count_limit = atapi_byte_count_limit(s); - trace_ide_atapi_cmd_reply_end_bcl(s, byte_count_limit); - size = s->packet_transfer_size; - if (size > byte_count_limit) { - /* byte count limit must be even if this case */ - if (byte_count_limit & 1) - byte_count_limit--; - size = byte_count_limit; - } - s->lcyl = size & 0xff; - s->hcyl = size >> 8; - s->elementary_transfer_size = size; - /* we cannot transmit more than one sector at a time */ - if (s->lba != -1) { - if (size > (s->cd_sector_size - s->io_buffer_index)) - size = (s->cd_sector_size - s->io_buffer_index); - } - trace_ide_atapi_cmd_reply_end_new(s, s->status); + s->elementary_transfer_size = size; + ret = cd_read_sector(s); + if (ret < 0) { + ide_atapi_io_error(s, ret); } + return; + } + + while (s->packet_transfer_size > 0) { + /* a new transfer is needed */ + s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO; + ide_bus_set_irq(s->bus); + byte_count_limit = atapi_byte_count_limit(s); + trace_ide_atapi_cmd_reply_end_bcl(s, byte_count_limit); + size = s->packet_transfer_size; + if (size > byte_count_limit) { + /* byte count limit must be even if this case */ + if (byte_count_limit & 1) { + byte_count_limit--; + } + size = byte_count_limit; + } + s->lcyl = size & 0xff; + s->hcyl = size >> 8; + s->elementary_transfer_size = size; + trace_ide_atapi_cmd_reply_end_new(s, s->status); + s->packet_transfer_size -= size; s->elementary_transfer_size -= size; s->io_buffer_index += size; @@ -329,7 +317,7 @@ static void ide_atapi_cmd_read_pio(IDEState *s, int lba, int nb_sectors, s->lba = lba; s->packet_transfer_size = nb_sectors * sector_size; s->elementary_transfer_size = 0; - s->io_buffer_index = sector_size; + s->io_buffer_index = 0; s->cd_sector_size = sector_size; ide_atapi_cmd_reply_end(s); diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index d44a90a162..79ab65d64a 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -214,7 +214,7 @@ static void cmd646_reset(DeviceState *dev) unsigned int i; for (i = 0; i < 2; i++) { - ide_bus_reset(&d->bus[i]); + ide_bus_reset(&d->bus[i], IDE_RESET_HARDWARE); } } diff --git a/hw/ide/core.c b/hw/ide/core.c index f78b00220b..ef573798d9 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -80,6 +80,7 @@ static const char *IDE_DMA_CMD_str(enum ide_dma_cmd enval) } static void ide_dummy_transfer_stop(IDEState *s); +static void ide_transfer_halt(IDEState *s); const MemoryRegionPortio ide_portio_list[] = { { 0, 8, 1, .read = ide_ioport_read, .write = ide_ioport_write }, @@ -110,6 +111,18 @@ static void put_le16(uint16_t *p, unsigned int v) *p = cpu_to_le16(v); } +static void ide_identify_chs(IDEState *s) +{ + uint16_t *p = (uint16_t *)s->identify_data; + unsigned int cur_sec = s->cylinders * s->heads * s->sectors; + + put_le16(p + 54, s->cylinders); + put_le16(p + 55, s->heads); + put_le16(p + 56, s->sectors); + put_le16(p + 57, cur_sec); + put_le16(p + 58, cur_sec >> 16); +} + static void ide_identify_size(IDEState *s) { uint16_t *p = (uint16_t *)s->identify_data; @@ -128,7 +141,6 @@ static void ide_identify_size(IDEState *s) static void ide_identify(IDEState *s) { uint16_t *p; - unsigned int oldsize; IDEDevice *dev = s->unit ? s->bus->slave : s->bus->master; p = (uint16_t *)s->identify_data; @@ -138,14 +150,15 @@ static void ide_identify(IDEState *s) memset(p, 0, sizeof(s->identify_data)); put_le16(p + 0, 0x0040); + /* Words 1, 3 and 6 describe the default translation (ATA-5 8.16.8) */ put_le16(p + 1, s->cylinders); - put_le16(p + 3, s->heads); - put_le16(p + 4, 512 * s->sectors); /* XXX: retired, remove ? */ - put_le16(p + 5, 512); /* XXX: retired, remove ? */ - put_le16(p + 6, s->sectors); + put_le16(p + 3, s->drive_heads); + put_le16(p + 4, 512 * s->drive_sectors); /* ATA-1 unformatted bytes/trk */ + put_le16(p + 5, 512); /* ATA-1 unformatted bytes per sector */ + put_le16(p + 6, s->drive_sectors); padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */ - put_le16(p + 20, 3); /* XXX: retired, remove ? */ - put_le16(p + 21, 512); /* cache size in sectors */ + put_le16(p + 20, 3); /* ATA-1 buffer type: dual ported, read caching */ + put_le16(p + 21, 512); /* ATA-1 buffer size in 512 byte increments */ put_le16(p + 22, 4); /* ecc bytes */ padstr((char *)(p + 23), s->version, 8); /* firmware version */ padstr((char *)(p + 27), s->drive_model_str, 40); /* model */ @@ -157,12 +170,7 @@ static void ide_identify(IDEState *s) put_le16(p + 51, 0x200); /* PIO transfer cycle */ put_le16(p + 52, 0x200); /* DMA transfer cycle */ put_le16(p + 53, 1 | (1 << 1) | (1 << 2)); /* words 54-58,64-70,88 are valid */ - put_le16(p + 54, s->cylinders); - put_le16(p + 55, s->heads); - put_le16(p + 56, s->sectors); - oldsize = s->cylinders * s->heads * s->sectors; - put_le16(p + 57, oldsize); - put_le16(p + 58, oldsize >> 16); + ide_identify_chs(s); if (s->mult_sectors) put_le16(p + 59, 0x100 | s->mult_sectors); /* *(p + 60) := nb_sectors -- see ide_identify_size */ @@ -284,7 +292,7 @@ static void ide_atapi_identify(IDEState *s) put_le16(p + 76, (1 << 8)); } - put_le16(p + 80, 0x1e); /* support up to ATA/ATAPI-4 */ + put_le16(p + 80, 0x70); /* support up to ATA/ATAPI-6 */ if (s->wwn) { put_le16(p + 84, (1 << 8)); /* supports WWN for words 108-111 */ put_le16(p + 87, (1 << 8)); /* WWN enabled */ @@ -292,6 +300,10 @@ static void ide_atapi_identify(IDEState *s) #ifdef USE_DMA_CDROM put_le16(p + 88, 0x3f | (1 << 13)); /* udma5 set and supported */ + if (!s->ncq_queues) { + /* word 93 is parallel ATA only, a SATA device reports zero */ + put_le16(p + 93, 0x600f); + } #endif if (s->wwn) { @@ -320,7 +332,6 @@ static void ide_cfata_identify_size(IDEState *s) static void ide_cfata_identify(IDEState *s) { uint16_t *p; - uint32_t cur_sec; p = (uint16_t *)s->identify_data; if (s->identify_set) { @@ -328,12 +339,10 @@ static void ide_cfata_identify(IDEState *s) } memset(p, 0, sizeof(s->identify_data)); - cur_sec = s->cylinders * s->heads * s->sectors; - put_le16(p + 0, 0x848a); /* CF Storage Card signature */ put_le16(p + 1, s->cylinders); /* Default cylinders */ - put_le16(p + 3, s->heads); /* Default heads */ - put_le16(p + 6, s->sectors); /* Default sectors per track */ + put_le16(p + 3, s->drive_heads); /* Default heads */ + put_le16(p + 6, s->drive_sectors); /* Default sectors per track */ /* *(p + 7) := nb_sectors >> 16 -- see ide_cfata_identify_size */ /* *(p + 8) := nb_sectors -- see ide_cfata_identify_size */ padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */ @@ -349,11 +358,7 @@ static void ide_cfata_identify(IDEState *s) put_le16(p + 51, 0x0002); /* PIO cycle timing mode */ put_le16(p + 52, 0x0001); /* DMA cycle timing mode */ put_le16(p + 53, 0x0003); /* Translation params valid */ - put_le16(p + 54, s->cylinders); /* Current cylinders */ - put_le16(p + 55, s->heads); /* Current heads */ - put_le16(p + 56, s->sectors); /* Current sectors */ - put_le16(p + 57, cur_sec); /* Current capacity */ - put_le16(p + 58, cur_sec >> 16); /* Current capacity */ + ide_identify_chs(s); /* Current C/H/S and capacity */ if (s->mult_sectors) /* Multiple sector setting */ put_le16(p + 59, 0x100 | s->mult_sectors); /* *(p + 60) := nb_sectors -- see ide_cfata_identify_size */ @@ -568,7 +573,15 @@ bool ide_transfer_start_norecurse(IDEState *s, uint8_t *buf, int size, s->end_transfer_func = end_transfer_func; return false; } - s->bus->dma->ops->pio_transfer(s->bus->dma); + if (!s->bus->dma->ops->pio_transfer(s->bus->dma)) { + /* + * No data reached the buffer, so the caller must not act on it. A + * write would otherwise commit whatever the previous phase left + * there to the next sector. + */ + ide_transfer_halt(s); + return false; + } return true; } @@ -741,10 +754,17 @@ void ide_cancel_dma_sync(IDEState *s) * In the future we'll be able to safely cancel the I/O if the * whole DMA operation will be submitted to disk with a single * aio operation with preadv/pwritev. + * + * Note: s->bus->dma->aiocb might belong to the adjacent IDEState, + * so we have to drain both drives to get it cleared. */ if (s->bus->dma->aiocb) { trace_ide_cancel_dma_sync_remaining(); - blk_drain(s->blk); + for (int i = 0; i < 2; i++) { + if (s->bus->ifs[i].blk) { + blk_drain(s->bus->ifs[i].blk); + } + } assert(s->bus->dma->aiocb == NULL); } } @@ -921,8 +941,12 @@ static void ide_dma_cb(void *opaque, int ret) s->io_buffer_index = 0; s->io_buffer_size = n * 512; prep_size = s->bus->dma->ops->prepare_buf(s->bus->dma, s->io_buffer_size); - /* prepare_buf() must succeed and respect the limit */ - assert(prep_size >= 0 && prep_size <= n * 512); + if (prep_size < 0) { + ide_dma_error(s); + return; + } + /* If prepare_buf() succeeds, it must respect the limit. */ + assert(prep_size <= n * 512); /* * Now prep_size stores the number of bytes in the sglist, and @@ -1336,7 +1360,7 @@ void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val) } } -static void ide_reset(IDEState *s) +static void ide_reset(IDEState *s, IDEResetKind kind) { trace_ide_reset(s); @@ -1345,10 +1369,14 @@ static void ide_reset(IDEState *s) s->pio_aiocb = NULL; } - if (s->reset_reverts) { + if (kind == IDE_RESET_HARDWARE || s->reset_reverts) { s->reset_reverts = false; s->heads = s->drive_heads; s->sectors = s->drive_sectors; + /* An ATAPI device takes SET FEATURES 0xCC but has no translation */ + if (s->identify_set && s->drive_kind != IDE_CD) { + ide_identify_chs(s); + } } if (s->drive_kind == IDE_CFATA) s->mult_sectors = 0; @@ -1409,7 +1437,7 @@ static bool cmd_device_reset(IDEState *s, uint8_t cmd) ide_cancel_dma_sync(s); /* Reset any PIO commands, reset signature, etc */ - ide_reset(s); + ide_reset(s, IDE_RESET_SOFTWARE); /* RESET: ATA8-ACS3 7.10.4 "Normal Outputs"; * ATA8-ACS3 Table 184 "Device Signatures for Normal Output" */ @@ -1644,12 +1672,21 @@ static bool cmd_check_power_mode(IDEState *s, uint8_t cmd) /* INITIALIZE DEVICE PARAMETERS */ static bool cmd_specify(IDEState *s, uint8_t cmd) { - if (s->blk && s->drive_kind != IDE_CD) { - s->heads = (s->select & (ATA_DEV_HS)) + 1; - s->sectors = s->nsector; - ide_bus_set_irq(s->bus); - } else { + if (!s->blk || s->drive_kind == IDE_CD) { ide_abort_command(s); + return true; + } + + /* ATA-2 D.2.8 limits IDENTIFY DEVICE word 56, and the count, to 1..255 */ + if (s->nsector == 0 || s->nsector > 255) { + ide_abort_command(s); + return true; + } + + s->heads = (s->select & (ATA_DEV_HS)) + 1; + s->sectors = s->nsector; + if (s->identify_set) { + ide_identify_chs(s); } return true; @@ -2323,7 +2360,7 @@ static void ide_perform_srst(IDEState *s) ide_cancel_dma_sync(s); /* Cancel PIO callback, reset registers/signature, etc */ - ide_reset(s); + ide_reset(s, IDE_RESET_SOFTWARE); /* perform diagnostic */ cmd_exec_dev_diagnostic(s, WIN_DIAGNOSE); @@ -2528,7 +2565,7 @@ static void ide_dummy_transfer_stop(IDEState *s) s->io_buffer[3] = 0xff; } -void ide_bus_reset(IDEBus *bus) +void ide_bus_reset(IDEBus *bus, IDEResetKind kind) { /* pending async DMA - needs the IDEState before it is reset */ if (bus->dma->aiocb) { @@ -2539,8 +2576,8 @@ void ide_bus_reset(IDEBus *bus) bus->unit = 0; bus->cmd = 0; - ide_reset(&bus->ifs[0]); - ide_reset(&bus->ifs[1]); + ide_reset(&bus->ifs[0], kind); + ide_reset(&bus->ifs[1], kind); ide_clear_hob(bus); /* reset dma provider too */ @@ -2654,7 +2691,7 @@ int ide_init_drive(IDEState *s, IDEDevice *dev, IDEDriveKind kind, Error **errp) pstrcpy(s->version, sizeof(s->version), QEMU_HW_VERSION); } - ide_reset(s); + ide_reset(s, IDE_RESET_HARDWARE); blk_iostatus_enable(s->blk); return 0; } @@ -2791,7 +2828,7 @@ void ide_bus_init_output_irq(IDEBus *bus, qemu_irq irq_out) for(i = 0; i < 2; i++) { ide_init1(bus, i); - ide_reset(&bus->ifs[i]); + ide_reset(&bus->ifs[i], IDE_RESET_HARDWARE); } bus->irq = irq_out; bus->dma = &ide_dma_nop; @@ -2838,10 +2875,29 @@ static int transfer_end_table_idx(EndTransferFunc *fn) return -1; } +static int ide_drive_pre_load(void *opaque) +{ + IDEState *s = opaque; + + /* The subsections below are sent only where the guest replaced these */ + s->heads = s->drive_heads; + s->sectors = s->drive_sectors; + s->reset_reverts = false; + + return 0; +} + static int ide_drive_post_load(void *opaque, int version_id) { IDEState *s = opaque; + /* Only a disk has a translation; an empty slot and ATAPI keep these zero */ + if (s->blk && s->drive_kind != IDE_CD && + (s->heads < 1 || s->heads > 16 || + s->sectors < 1 || s->sectors > 255)) { + return -EINVAL; + } + if (s->blk && s->identify_set) { blk_set_enable_write_cache(s->blk, !!(s->identify_data[85] & (1 << 5))); } @@ -2855,6 +2911,12 @@ static int ide_drive_pio_post_load(void *opaque, int version_id) if (s->end_transfer_fn_idx >= ARRAY_SIZE(transfer_end_table)) { return -EINVAL; } + if (s->cur_io_buffer_offset < 0 || s->cur_io_buffer_len < 0 || + s->cur_io_buffer_offset > s->io_buffer_total_len || + s->cur_io_buffer_len > + s->io_buffer_total_len - s->cur_io_buffer_offset) { + return -EINVAL; + } s->end_transfer_func = transfer_end_table[s->end_transfer_fn_idx]; s->data_ptr = s->io_buffer + s->cur_io_buffer_offset; s->data_end = s->data_ptr + s->cur_io_buffer_len; @@ -2925,6 +2987,44 @@ static const VMStateDescription vmstate_ide_atapi_gesn_state = { } }; +static bool ide_chs_translation_needed(void *opaque) +{ + IDEState *s = opaque; + + return s->heads != s->drive_heads || s->sectors != s->drive_sectors; +} + +static const VMStateDescription vmstate_ide_drive_chs_translation = { + .name = "ide_drive/chs_translation", + .version_id = 1, + .minimum_version_id = 1, + .needed = ide_chs_translation_needed, + .fields = (const VMStateField[]) { + VMSTATE_INT32(heads, IDEState), + VMSTATE_INT32(sectors, IDEState), + VMSTATE_END_OF_LIST() + } +}; + +static bool ide_reset_reverts_needed(void *opaque) +{ + IDEState *s = opaque; + + return s->reset_reverts && ide_chs_translation_needed(opaque); +} + +/* The flag decides nothing on the default geometry, so it travels with one */ +static const VMStateDescription vmstate_ide_drive_reset_reverts = { + .name = "ide_drive/reset_reverts", + .version_id = 1, + .minimum_version_id = 1, + .needed = ide_reset_reverts_needed, + .fields = (const VMStateField[]) { + VMSTATE_BOOL(reset_reverts, IDEState), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_ide_tray_state = { .name = "ide_drive/tray_state", .version_id = 1, @@ -2961,6 +3061,7 @@ const VMStateDescription vmstate_ide_drive = { .name = "ide_drive", .version_id = 3, .minimum_version_id = 0, + .pre_load = ide_drive_pre_load, .post_load = ide_drive_post_load, .fields = (const VMStateField[]) { VMSTATE_INT32(mult_sectors, IDEState), @@ -2987,6 +3088,8 @@ const VMStateDescription vmstate_ide_drive = { }, .subsections = (const VMStateDescription * const []) { &vmstate_ide_drive_pio_state, + &vmstate_ide_drive_chs_translation, + &vmstate_ide_drive_reset_reverts, &vmstate_ide_tray_state, &vmstate_ide_atapi_gesn_state, NULL diff --git a/hw/ide/ide-internal.h b/hw/ide/ide-internal.h index 281d07c9d5..094772209d 100644 --- a/hw/ide/ide-internal.h +++ b/hw/ide/ide-internal.h @@ -393,7 +393,12 @@ extern const VMStateDescription vmstate_ide_drive; #define VMSTATE_IDE_DRIVE(_field, _state) \ VMSTATE_STRUCT(_field, _state, 1, vmstate_ide_drive, IDEState) -void ide_bus_reset(IDEBus *bus); +typedef enum { + IDE_RESET_HARDWARE, /* power on, hardware reset or COMRESET, ATA-5 9.1 */ + IDE_RESET_SOFTWARE, /* SRST or DEVICE RESET, ATA-5 9.2 */ +} IDEResetKind; + +void ide_bus_reset(IDEBus *bus, IDEResetKind kind); int64_t ide_get_sector(IDEState *s); void ide_set_sector(IDEState *s, int64_t sector_num); diff --git a/hw/ide/isa.c b/hw/ide/isa.c index c97b7a1ff4..30f02867b1 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -51,7 +51,7 @@ static void isa_ide_reset(DeviceState *d) { ISAIDEState *s = ISA_IDE(d); - ide_bus_reset(&s->bus); + ide_bus_reset(&s->bus, IDE_RESET_HARDWARE); } static const VMStateDescription vmstate_ide_isa = { diff --git a/hw/ide/macio.c b/hw/ide/macio.c index a7ed41fa26..40fb4f3b4f 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -368,7 +368,7 @@ static void macio_ide_reset(DeviceState *dev) { MACIOIDEState *d = MACIO_IDE(dev); - ide_bus_reset(&d->bus); + ide_bus_reset(&d->bus, IDE_RESET_HARDWARE); } static int ide_nop_int(const IDEDMA *dma, bool is_write) diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c index 0de904ac56..00819d559d 100644 --- a/hw/ide/mmio.c +++ b/hw/ide/mmio.c @@ -55,7 +55,7 @@ static void mmio_ide_reset(DeviceState *dev) { MMIOIDEState *s = MMIO_IDE(dev); - ide_bus_reset(&s->bus); + ide_bus_reset(&s->bus, IDE_RESET_HARDWARE); } static uint64_t mmio_ide_read(void *opaque, hwaddr addr, diff --git a/hw/ide/piix.c b/hw/ide/piix.c index a0f2709c69..a8472f3e58 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -111,7 +111,7 @@ static void piix_ide_reset(DeviceState *dev) int i; for (i = 0; i < 2; i++) { - ide_bus_reset(&d->bus[i]); + ide_bus_reset(&d->bus[i], IDE_RESET_HARDWARE); } /* PCI command register default value (0000h) per [1, p.48]. */ diff --git a/hw/ide/sii3112.c b/hw/ide/sii3112.c index 9b28c691fd..03313eb271 100644 --- a/hw/ide/sii3112.c +++ b/hw/ide/sii3112.c @@ -185,7 +185,7 @@ static void sii3112_reg_write(void *opaque, hwaddr addr, case 0x100: d->regs[0].scontrol = val & 0xfff; if (val & 1) { - ide_bus_reset(&d->i.bus[0]); + ide_bus_reset(&d->i.bus[0], IDE_RESET_HARDWARE); } break; case 0x148: @@ -194,7 +194,7 @@ static void sii3112_reg_write(void *opaque, hwaddr addr, case 0x180: d->regs[1].scontrol = val & 0xfff; if (val & 1) { - ide_bus_reset(&d->i.bus[1]); + ide_bus_reset(&d->i.bus[1], IDE_RESET_HARDWARE); } break; case 0x1c8: @@ -243,7 +243,7 @@ static void sii3112_reset(DeviceState *dev) for (i = 0; i < 2; i++) { s->regs[i].confstat = 0x6515 << 16; - ide_bus_reset(&s->i.bus[i]); + ide_bus_reset(&s->i.bus[i], IDE_RESET_HARDWARE); } } diff --git a/hw/ide/trace-events b/hw/ide/trace-events index 57042cafdd..3ab5e7bd1d 100644 --- a/hw/ide/trace-events +++ b/hw/ide/trace-events @@ -85,6 +85,7 @@ ahci_reset_port(void *s, int port) "ahci(%p)[%d]: reset port" ahci_unmap_fis_address_null(void *s, int port) "ahci(%p)[%d]: Attempt to unmap NULL FIS address" ahci_unmap_clb_address_null(void *s, int port) "ahci(%p)[%d]: Attempt to unmap NULL CLB address" ahci_populate_sglist(void *s, int port) "ahci(%p)[%d]" +ahci_populate_sglist_no_cmd(void *s, int port) "ahci(%p)[%d]: no command header" ahci_populate_sglist_no_prdtl(void *s, int port, uint16_t opts) "ahci(%p)[%d]: no sg list given by guest: 0x%04x" ahci_populate_sglist_no_map(void *s, int port) "ahci(%p)[%d]: DMA mapping failed" ahci_populate_sglist_short_map(void *s, int port) "ahci(%p)[%d]: mapped less than expected" @@ -105,10 +106,12 @@ handle_reg_h2d_fis_res(void *s, int port, char b0, char b1, char b2) "ahci(%p)[% handle_cmd_busy(void *s, int port) "ahci(%p)[%d]: engine busy" handle_cmd_nolist(void *s, int port) "ahci(%p)[%d]: handle_cmd called without s->dev[port].lst" handle_cmd_badport(void *s, int port) "ahci(%p)[%d]: guest accessed unused port" +handle_cmd_badcfl(void *s, int port, uint16_t opts) "ahci(%p)[%d]: guest provided an invalid cmd FIS length: 0x%04x" handle_cmd_badfis(void *s, int port) "ahci(%p)[%d]: guest provided an invalid cmd FIS" handle_cmd_badmap(void *s, int port, uint64_t len) "ahci(%p)[%d]: dma_memory_map failed, 0x%02"PRIx64" != 0x80" handle_cmd_unhandled_fis(void *s, int port, uint8_t b0, uint8_t b1, uint8_t b2) "ahci(%p)[%d]: unhandled FIS type. cmd_fis: 0x%02x-%02x-%02x" ahci_pio_transfer(void *s, int port, const char *rw, uint32_t size, const char *tgt, const char *sgl) "ahci(%p)[%d]: %sing %d bytes on %s w/%s sglist" +ahci_pio_transfer_no_cmd(void *s, int port) "ahci(%p)[%d]: PIO transfer without a command header" ahci_start_dma(void *s, int port) "ahci(%p)[%d]: start dma" ahci_dma_prepare_buf(void *s, int port, int32_t io_buffer_size, int32_t limit) "ahci(%p)[%d]: prepare buf limit=%"PRId32" prepared=%"PRId32 ahci_dma_prepare_buf_fail(void *s, int port) "ahci(%p)[%d]: sglist population failed" diff --git a/hw/ide/via.c b/hw/ide/via.c index 3a77d744ca..6c0610ff41 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -127,7 +127,7 @@ static void via_ide_reset(DeviceState *dev) int i; for (i = 0; i < ARRAY_SIZE(d->bus); i++) { - ide_bus_reset(&d->bus[i]); + ide_bus_reset(&d->bus[i], IDE_RESET_HARDWARE); } pci_config_set_prog_interface(pci_conf, 0x8a); /* legacy mode */ diff --git a/hw/input/ps2.c b/hw/input/ps2.c index 5516eb262d..01af4350b3 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -73,6 +73,7 @@ #define AUX_SET_DEFAULT 0xF6 #define AUX_RESET 0xFF /* Reset aux device */ #define AUX_ACK 0xFA /* Command byte ACK. */ +#define AUX_RESEND 0xFE /* Command NACK, send the cmd again */ #define MOUSE_STATUS_REMOTE 0x40 #define MOUSE_STATUS_ENABLED 0x20 @@ -646,6 +647,10 @@ void ps2_write_keyboard(PS2KbdState *s, int val) ps2_cqueue_1(ps2, KBD_REPLY_ACK); break; default: + /* + * A PS/2 device answers every command it is given; an unknown + * one draws a resend. + */ ps2_cqueue_1(ps2, KBD_REPLY_RESEND); break; } @@ -955,6 +960,11 @@ void ps2_write_mouse(PS2MouseState *s, int val) s->mouse_type); break; default: + /* + * A PS/2 device answers every command it is given; an unknown + * one draws a resend. + */ + ps2_queue(ps2, AUX_RESEND); break; } break; diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index 636d00b7e8..05e697967d 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -8,6 +8,9 @@ config I8259 config PL190 bool +config HEX_L2VIC + bool + config IOAPIC bool select I8259 @@ -23,7 +26,7 @@ config APIC config ARM_GIC bool - select ARM_GICV3 if TCG + select ARM_GICV3 if TCG || HVF || WHPX select ARM_GIC_KVM if KVM select MSI_NONBROKEN diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c index 49c03a5bce..0f0f37d457 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -424,7 +424,7 @@ static void apic_common_set_id(Object *obj, Visitor *v, const char *name, DeviceState *dev = DEVICE(obj); uint32_t value; - if (dev->realized) { + if (qdev_is_realized(dev)) { qdev_prop_set_after_realize(dev, name, errp); return; } diff --git a/hw/intc/hex-l2vic.c b/hw/intc/hex-l2vic.c new file mode 100644 index 0000000000..f07ec850d4 --- /dev/null +++ b/hw/intc/hex-l2vic.c @@ -0,0 +1,556 @@ +/* + * QEMU L2VIC Interrupt Controller + * + * Arm PrimeCell PL190 Vector Interrupt Controller was used as a reference. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/core/irq.h" +#include "hw/core/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/bitmap.h" +#include "qemu/bitops.h" +#include "hw/intc/hex-l2vic.h" +#include "trace.h" + +#define L2VIC_VID_GRP_0 0x0 /* Read */ +#define L2VIC_VID_GRP_1 0x4 /* Read */ +#define L2VIC_VID_GRP_2 0x8 /* Read */ +#define L2VIC_VID_GRP_3 0xC /* Read */ +#define L2VIC_INT_ENABLEn 0x100 /* Read/Write */ +#define L2VIC_INT_ENABLE_CLEARn 0x180 /* Write */ +#define L2VIC_INT_ENABLE_SETn 0x200 /* Write */ +#define L2VIC_INT_TYPEn 0x280 /* Read/Write */ +#define L2VIC_INT_STATUSn 0x380 /* Read */ +#define L2VIC_INT_CLEARn 0x400 /* Write */ +#define L2VIC_SOFT_INTn 0x480 /* Write */ +#define L2VIC_INT_PENDINGn 0x500 /* Read */ +#define L2VIC_INT_GRPn_0 0x600 /* Read/Write */ +#define L2VIC_INT_GRPn_1 0x680 /* Read/Write */ +#define L2VIC_INT_GRPn_2 0x700 /* Read/Write */ +#define L2VIC_INT_GRPn_3 0x780 /* Read/Write */ + +#define L2VIC_INTERRUPT_MAX 1024 +/* + * Note about l2vic groups: + * Each interrupt to L2VIC can be configured to associate with one of + * four groups. + * Group 0 interrupts go to IRQ2 via VID 0 (SSR: 0xC2, the default) + * Group 1 interrupts go to IRQ3 via VID 1 (SSR: 0xC3) + * Group 2 interrupts go to IRQ4 via VID 2 (SSR: 0xC4) + * Group 3 interrupts go to IRQ5 via VID 3 (SSR: 0xC5) + */ + +static void bitmap32_write_word(uint32_t *bitmap, int word_offset, uint32_t val) +{ + bitmap[word_offset] = val; +} + +static void bitmap32_clear_word(uint32_t *bitmap, int word_offset, + uint32_t mask) +{ + bitmap[word_offset] &= ~mask; +} + +static void bitmap32_set_word(uint32_t *bitmap, int word_offset, uint32_t mask) +{ + bitmap[word_offset] |= mask; +} + +static uint32_t bitmap32_read_word(uint32_t *bitmap, int word_offset) +{ + return bitmap[word_offset]; +} + +OBJECT_DECLARE_SIMPLE_TYPE(HexL2VICState, HEX_L2VIC) + +#define SLICE_MAX (L2VIC_INTERRUPT_MAX / 32) +#define L2VIC_REG_RANGE_SIZE 0x80 + +typedef struct HexL2VICState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + MemoryRegion fast_iomem; + /* + * vid_group[i] is readable at L2VIC_VID_GRP_i (offset i*4): the irq + * last delivered through VID group i, 0-1023 so only 10 bits are used. + */ + uint32_t vid_group[4]; + /* + * Last irq delivered on any VID group; not specific to group 0. + * Used by the ciad path to clear the most-recently-delivered + * interrupt from int_status. + */ + uint32_t vid; + DECLARE_BITMAP32(int_enable, L2VIC_INTERRUPT_MAX); + /* Asserted interrupts awaiting delivery once no VID is active */ + DECLARE_BITMAP32(int_pending, L2VIC_INTERRUPT_MAX); + /* Which enabled interrupt is active */ + DECLARE_BITMAP32(int_status, L2VIC_INTERRUPT_MAX); + /* Edge or Level interrupt */ + DECLARE_BITMAP32(int_type, L2VIC_INTERRUPT_MAX); + DECLARE_BITMAP32(int_group_n[4], L2VIC_INTERRUPT_MAX); + qemu_irq irq[8]; +} HexL2VICState; + +typedef enum { + L2VIC_OP_WRITE, + L2VIC_OP_CLEAR, + L2VIC_OP_SET, + L2VIC_OP_NONE, +} L2VicWriteOp; + +typedef struct { + hwaddr base; + size_t state_offset; + L2VicWriteOp write_op; + bool write_only; +} L2VicRegRange; + +static const L2VicRegRange l2vic_reg_ranges[] = { + { L2VIC_INT_ENABLEn, offsetof(HexL2VICState, int_enable), + L2VIC_OP_WRITE, false }, + { L2VIC_INT_ENABLE_CLEARn, offsetof(HexL2VICState, int_enable), + L2VIC_OP_CLEAR, true }, + { L2VIC_INT_ENABLE_SETn, offsetof(HexL2VICState, int_enable), + L2VIC_OP_SET, true }, + { L2VIC_INT_TYPEn, offsetof(HexL2VICState, int_type), + L2VIC_OP_WRITE, false }, + { L2VIC_INT_STATUSn, offsetof(HexL2VICState, int_status), + L2VIC_OP_NONE, false }, + { L2VIC_INT_CLEARn, offsetof(HexL2VICState, int_status), + L2VIC_OP_CLEAR, true }, + { L2VIC_SOFT_INTn, offsetof(HexL2VICState, int_pending), + L2VIC_OP_NONE, true }, + { L2VIC_INT_PENDINGn, offsetof(HexL2VICState, int_pending), + L2VIC_OP_WRITE, false }, + { L2VIC_INT_GRPn_0, offsetof(HexL2VICState, int_group_n[0]), + L2VIC_OP_WRITE, false }, + { L2VIC_INT_GRPn_1, offsetof(HexL2VICState, int_group_n[1]), + L2VIC_OP_WRITE, false }, + { L2VIC_INT_GRPn_2, offsetof(HexL2VICState, int_group_n[2]), + L2VIC_OP_WRITE, false }, + { L2VIC_INT_GRPn_3, offsetof(HexL2VICState, int_group_n[3]), + L2VIC_OP_WRITE, false }, +}; + +static uint32_t *l2vic_state_bitmap(HexL2VICState *s, size_t state_offset) +{ + return (uint32_t *)((char *)s + state_offset); +} + +static bool l2vic_reg_read_range(HexL2VICState *s, hwaddr offset, + uint64_t *value) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(l2vic_reg_ranges); i++) { + const L2VicRegRange *r = &l2vic_reg_ranges[i]; + + if (offset >= r->base && + offset < r->base + L2VIC_REG_RANGE_SIZE) { + if (r->write_only) { + *value = 0; + } else { + uint32_t *bitmap = l2vic_state_bitmap(s, r->state_offset); + *value = bitmap32_read_word(bitmap, + (offset - r->base) >> 2); + } + return true; + } + } + return false; +} + +static bool l2vic_reg_write_range(HexL2VICState *s, hwaddr offset, + uint32_t val) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(l2vic_reg_ranges); i++) { + const L2VicRegRange *r = &l2vic_reg_ranges[i]; + + if (offset >= r->base && + offset < r->base + L2VIC_REG_RANGE_SIZE) { + uint32_t *bitmap = l2vic_state_bitmap(s, r->state_offset); + int word = (offset - r->base) >> 2; + + switch (r->write_op) { + case L2VIC_OP_WRITE: + bitmap32_write_word(bitmap, word, val); + break; + case L2VIC_OP_CLEAR: + bitmap32_clear_word(bitmap, word, val); + break; + case L2VIC_OP_SET: + bitmap32_set_word(bitmap, word, val); + break; + case L2VIC_OP_NONE: + /* Read-only or handled elsewhere; ignore the write. */ + break; + default: + g_assert_not_reached(); + } + return true; + } + } + return false; +} + +/* + * The four INT_GRPn_* register arrays are interleaved across irqs in + * blocks of 8: irq 0-7 live in group_n[0], irq 8-15 in group_n[1], irq + * 16-23 in group_n[2], irq 24-31 in group_n[3], irq 32-39 back in + * group_n[0], and so on. + */ +static uint32_t *get_int_group(HexL2VICState *s, int irq) +{ + return s->int_group_n[extract32(irq, 3, 2)]; +} + +static int find_slice(int irq) +{ + return irq / 32; +} + +static int get_vid(HexL2VICState *s, int irq) +{ + uint32_t *group = get_int_group(s, irq); + uint32_t slice = group[find_slice(irq)]; + uint32_t vid; + /* + * Each irq occupies a 4-bit field: bit 3 is the group-enable bit, + * bits 0-2 select the VID group. Shift down to this irq's field. + */ + uint32_t val = slice >> ((irq & 0x7) * 4); + + if (!(val & 0x8)) { + return 0; + } + vid = val & 0x7; + if (vid >= ARRAY_SIZE(s->vid_group)) { + qemu_log_mask(LOG_GUEST_ERROR, + "L2VIC: irq %d requests invalid vid group %u\n", + irq, vid); + return 0; + } + return vid; +} + +static inline bool vid_active(HexL2VICState *s) +{ + const uint32_t size = L2VIC_INTERRUPT_MAX; + const uint32_t active_irq = find_first_bit32(s->int_status, size); + return active_irq != size; +} + +static bool l2vic_update(HexL2VICState *s, int irq) +{ + bool pending; + bool enable; + + if (vid_active(s)) { + return true; + } + + pending = test_bit32(irq, s->int_pending); + enable = test_bit32(irq, s->int_enable); + if (pending && enable) { + int vid = get_vid(s, irq); + set_bit32(irq, s->int_status); + clear_bit32(irq, s->int_pending); + /* + * Only auto-disable for edge-triggered interrupts (type=1). + * Level-triggered interrupts (type=0, the default) keep their + * enable bit set across deliveries -- the firmware enables once + * and expects the interrupt to remain enabled. + */ + if (test_bit32(irq, s->int_type)) { + clear_bit32(irq, s->int_enable); + } + s->vid = irq; + s->vid_group[vid] = irq; + + qemu_irq_pulse(s->irq[vid + 2]); + trace_hex_l2vic_delivered(irq, vid); + return true; + } + return false; +} + +static void l2vic_update_all(HexL2VICState *s) +{ + for (int i = 0; i < L2VIC_INTERRUPT_MAX; i++) { + if (l2vic_update(s, i)) { + /* once vid is active, no-one else can set it until ciad */ + return; + } + } +} + +static void l2vic_set_irq(void *opaque, int irq, int level) +{ + HexL2VICState *s = (HexL2VICState *)opaque; + + if (level) { + set_bit32(irq, s->int_pending); + } + l2vic_update(s, irq); +} + +static void l2vic_write(void *opaque, hwaddr offset, uint64_t val, + unsigned size) +{ + HexL2VICState *s = (HexL2VICState *)opaque; + + trace_hex_l2vic_reg_write((unsigned)offset, (uint32_t)val); + + if (!l2vic_reg_write_range(s, offset, val)) { + qemu_log_mask(LOG_UNIMP, + "%s: offset 0x%" HWADDR_PRIx " unimplemented\n", + __func__, offset); + } + + /* SOFT_INT also sets pending for edge-triggered interrupts */ + if (offset >= L2VIC_SOFT_INTn && + offset < L2VIC_SOFT_INTn + L2VIC_REG_RANGE_SIZE && val) { + int base_irq = ((offset - L2VIC_SOFT_INTn) >> 2) * 32; + uint32_t bits = val; + int bit; + + while ((bit = ctz32(bits)) < 32) { + int irq = base_irq + bit; + + if (test_bit32(irq, s->int_type)) { + set_bit32(irq, s->int_pending); + } + bits &= ~(1u << bit); + } + } + + l2vic_update_all(s); +} + +static uint64_t l2vic_read(void *opaque, hwaddr offset, unsigned size) +{ + uint64_t value; + HexL2VICState *s = (HexL2VICState *)opaque; + + if (offset <= L2VIC_VID_GRP_3) { + value = s->vid_group[offset >> 2]; + } else if (!l2vic_reg_read_range(s, offset, &value)) { + value = 0; + qemu_log_mask(LOG_GUEST_ERROR, + "L2VIC: %s: offset 0x%" HWADDR_PRIx "\n", __func__, + offset); + } + + trace_hex_l2vic_reg_read((unsigned)offset, (uint32_t)value); + return value; +} + +static const MemoryRegionOps l2vic_ops = { + .read = l2vic_read, + .write = l2vic_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .valid.unaligned = false, +}; + +#define FASTL2VIC_ENABLE 0x0 +#define FASTL2VIC_DISABLE 0x1 +#define FASTL2VIC_INT 0x2 + +static void fastl2vic_write(void *opaque, hwaddr offset, uint64_t val, + unsigned size) +{ + if (offset == 0) { + uint32_t cmd = (val >> 16) & 0x3; + uint32_t irq = val & 0x3ff; + uint32_t slice = (irq / 32) * 4; + val = 1 << (irq % 32); + + if (cmd == FASTL2VIC_ENABLE) { + l2vic_write(opaque, L2VIC_INT_ENABLE_SETn + slice, val, size); + } else if (cmd == FASTL2VIC_DISABLE) { + l2vic_write(opaque, L2VIC_INT_ENABLE_CLEARn + slice, val, size); + } else if (cmd == FASTL2VIC_INT) { + l2vic_write(opaque, L2VIC_SOFT_INTn + slice, val, size); + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid write cmd %" PRId32 "\n", + __func__, cmd); + } + return; + } + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid write offset 0x%08" HWADDR_PRIx + "\n", __func__, offset); +} + +static uint64_t fastl2vic_read(void *opaque, hwaddr offset, unsigned size) +{ + return 0; +} + +static const MemoryRegionOps fastl2vic_ops = { + .read = fastl2vic_read, + .write = fastl2vic_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .valid.unaligned = false, +}; + +static uint32_t l2vic_interface_read_vid_impl(HexL2VicInterface *iface, + uint32_t group) +{ + HexL2VICState *s = HEX_L2VIC(iface); + uint32_t result = 0; + + if (group == 0) { + /* VID register combines vid_group[0] (VID0) and vid_group[1] (VID1) */ + result = deposit32(result, 0, 16, s->vid_group[0]); + result = deposit32(result, 16, 16, s->vid_group[1]); + } else if (group == 1) { + /* VID1 register combines vid_group[2] (VID2) and vid_group[3] (VID3) */ + result = deposit32(result, 0, 16, s->vid_group[2]); + result = deposit32(result, 16, 16, s->vid_group[3]); + } + return result; +} + +static void l2vic_interface_update_vid_impl(HexL2VicInterface *iface, + uint32_t group, uint32_t value) +{ + HexL2VICState *s = HEX_L2VIC(iface); + + if (group == 0) { + s->vid_group[0] = extract32(value, 0, 16); + s->vid_group[1] = extract32(value, 16, 16); + } else if (group == 1) { + s->vid_group[2] = extract32(value, 0, 16); + s->vid_group[3] = extract32(value, 16, 16); + } + + l2vic_update_all(s); +} + +static void l2vic_interface_clear_interrupt_impl(HexL2VicInterface *iface) +{ + HexL2VICState *s = HEX_L2VIC(iface); + + if (s->vid < L2VIC_INTERRUPT_MAX) { + clear_bit32(s->vid, s->int_status); + } + l2vic_update_all(s); +} + +static void l2vic_reset_hold(Object *obj, ResetType type G_GNUC_UNUSED) +{ + HexL2VICState *s = HEX_L2VIC(obj); + + memset(s->int_enable, 0, sizeof(s->int_enable)); + memset(s->int_pending, 0, sizeof(s->int_pending)); + memset(s->int_status, 0, sizeof(s->int_status)); + memset(s->int_type, 0, sizeof(s->int_type)); + memset(s->int_group_n, 0, sizeof(s->int_group_n)); + memset(s->vid_group, 0, sizeof(s->vid_group)); + s->vid = 0; + + l2vic_update_all(s); +} + +static void reset_irq_handler(void *opaque, int irq, int level) +{ + Object *obj = OBJECT(opaque); + + if (level) { + l2vic_reset_hold(obj, RESET_TYPE_COLD); + } +} + +static void l2vic_init(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + HexL2VICState *s = HEX_L2VIC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + int i; + + memory_region_init_io(&s->iomem, obj, &l2vic_ops, s, "l2vic", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + memory_region_init_io(&s->fast_iomem, obj, &fastl2vic_ops, s, "fast", + 0x10000); + sysbus_init_mmio(sbd, &s->fast_iomem); + + qdev_init_gpio_in(dev, l2vic_set_irq, L2VIC_INTERRUPT_MAX); + qdev_init_gpio_in_named(dev, reset_irq_handler, "reset", 1); + for (i = 0; i < 8; i++) { + sysbus_init_irq(sbd, &s->irq[i]); + } +} + +static const VMStateDescription vmstate_l2vic = { + .name = "l2vic", + .version_id = 1, + .minimum_version_id = 1, + .fields = + (VMStateField[]){ + VMSTATE_UINT32_ARRAY(vid_group, HexL2VICState, 4), + VMSTATE_UINT32(vid, HexL2VICState), + VMSTATE_UINT32_ARRAY(int_enable, HexL2VICState, SLICE_MAX), + VMSTATE_UINT32_ARRAY(int_type, HexL2VICState, SLICE_MAX), + VMSTATE_UINT32_ARRAY(int_status, HexL2VICState, SLICE_MAX), + VMSTATE_UINT32_ARRAY(int_pending, HexL2VICState, SLICE_MAX), + VMSTATE_UINT32_2DARRAY(int_group_n, HexL2VICState, 4, SLICE_MAX), + VMSTATE_END_OF_LIST() } +}; + +static void l2vic_interface_class_init(ObjectClass *klass, const void *data) +{ + HexL2VicInterfaceClass *k = HEX_L2VIC_INTERFACE_CLASS(klass); + + k->read_vid = l2vic_interface_read_vid_impl; + k->update_vid = l2vic_interface_update_vid_impl; + k->clear_interrupt = l2vic_interface_clear_interrupt_impl; +} + +static void l2vic_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->vmsd = &vmstate_l2vic; + rc->phases.hold = l2vic_reset_hold; +} + +static const TypeInfo l2vic_interface_info = { + .name = TYPE_HEX_L2VIC_INTERFACE, + .parent = TYPE_INTERFACE, + .class_size = sizeof(HexL2VicInterfaceClass), + .class_init = l2vic_interface_class_init, +}; + +static const TypeInfo l2vic_info = { + .name = TYPE_HEX_L2VIC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HexL2VICState), + .instance_init = l2vic_init, + .class_init = l2vic_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HEX_L2VIC_INTERFACE }, + { } + }, +}; + +static const TypeInfo l2vic_types[] = { + l2vic_interface_info, + l2vic_info, +}; + +DEFINE_TYPES(l2vic_types) diff --git a/hw/intc/loongarch_dintc.c b/hw/intc/loongarch_dintc.c index e40292887c..94d418261c 100644 --- a/hw/intc/loongarch_dintc.c +++ b/hw/intc/loongarch_dintc.c @@ -68,6 +68,15 @@ static void loongarch_dintc_mem_write(void *opaque, hwaddr addr, } irq_num = FIELD_EX64(msg_addr, MSG_ADDR, IRQ_NUM); + if (kvm_irqchip_in_kernel()) { + MSIMessage msg; + + msg.address = msg_addr; + msg.data = val; + kvm_irqchip_send_msi(kvm_state, msg); + return; + } + async_run_on_cpu(cs, do_set_vcpu_dintc_irq, RUN_ON_CPU_HOST_INT(irq_num)); qemu_set_irq(s->cpu[cpu_num].parent_irq, 1); @@ -110,6 +119,10 @@ static void loongarch_dintc_realize(DeviceState *dev, Error **errp) qdev_init_gpio_out(dev, &s->cpu[i].parent_irq, 1); } + if (kvm_irqchip_in_kernel()) { + kvm_dintc_realize(dev, errp); + } + return; } diff --git a/hw/intc/loongarch_dintc_kvm.c b/hw/intc/loongarch_dintc_kvm.c new file mode 100644 index 0000000000..1137bcd9a6 --- /dev/null +++ b/hw/intc/loongarch_dintc_kvm.c @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch DINTC interrupt kvm support + * + * Copyright (C) 2025 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "hw/intc/loongarch_dintc.h" +#include "linux/kvm.h" +#include "qapi/error.h" +#include "system/kvm.h" + +void kvm_dintc_realize(DeviceState *dev, Error **errp) +{ + LoongArchDINTCState *lds = LOONGARCH_DINTC(dev); + int ret; + + ret = kvm_create_device(kvm_state, KVM_DEV_TYPE_LOONGARCH_DMSINTC, false); + if (ret < 0) { + fprintf(stderr, "create KVM_DEV_TYPE_LOONGARCH_AVEC failed: %s\n", + strerror(-ret)); + abort(); + } + lds->dev_fd = ret; + + /* init dintc config */ + lds->msg_addr_base = VIRT_DINTC_BASE; + lds->msg_addr_size = VIRT_DINTC_SIZE; + + ret = kvm_device_access(lds->dev_fd, KVM_DEV_LOONGARCH_DMSINTC_GRP_CTRL, + KVM_DEV_LOONGARCH_DMSINTC_MSG_ADDR_BASE, + &lds->msg_addr_base, true, NULL); + if (ret < 0) { + fprintf(stderr, "KVM_DEV_LOONGARCH_DINTC_MSG_ADDR_BASE failed: %s\n", + strerror(ret)); + abort(); + } + + ret = kvm_device_access(lds->dev_fd, KVM_DEV_LOONGARCH_DMSINTC_GRP_CTRL, + KVM_DEV_LOONGARCH_DMSINTC_MSG_ADDR_SIZE, + &lds->msg_addr_size, true, NULL); + if (ret < 0) { + fprintf(stderr, "KVM_DEV_LOONGARCH_DINTC_MSG_ADDR_SIZE failed: %s\n", + strerror(ret)); + abort(); + } +} diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index 82e16be391..e87c7497c1 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -19,13 +19,21 @@ static void pch_pic_update_irq(LoongArchPICCommonState *s, uint64_t mask, { uint64_t val; int irq; + uint8_t vector; if (level) { val = mask & s->intirr & ~s->int_mask; if (val) { irq = ctz64(val); + vector = s->htmsi_vector[irq]; + if (vector >= s->irq_num) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: htmsi_vector[%d]=%u out of range\n", + __func__, irq, vector); + return; + } s->intisr |= MAKE_64BIT_MASK(irq, 1); - qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 1); + qemu_set_irq(s->parent_irq[vector], 1); } } else { /* @@ -35,8 +43,15 @@ static void pch_pic_update_irq(LoongArchPICCommonState *s, uint64_t mask, val = mask & s->intisr & ~s->intirr; if (val) { irq = ctz64(val); + vector = s->htmsi_vector[irq]; + if (vector >= s->irq_num) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: htmsi_vector[%d]=%u out of range\n", + __func__, irq, vector); + return; + } s->intisr &= ~MAKE_64BIT_MASK(irq, 1); - qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0); + qemu_set_irq(s->parent_irq[vector], 0); } } } diff --git a/hw/intc/m68k_irqc.c b/hw/intc/m68k_irqc.c index 68ddb5351b..47f626c0fa 100644 --- a/hw/intc/m68k_irqc.c +++ b/hw/intc/m68k_irqc.c @@ -70,7 +70,7 @@ static void m68k_irqc_instance_init(Object *obj) qdev_init_gpio_in(DEVICE(obj), m68k_set_irq, M68K_IRQC_LEVEL_NUM); } -static void m68k_nmi(NMIState *n, int cpu_index, Error **errp) +static void m68k_nmi(NMIState *n) { m68k_set_irq(n, M68K_IRQC_LEVEL_7, 1); } @@ -97,7 +97,7 @@ static void m68k_irqc_class_init(ObjectClass *oc, const void *data) InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(oc); device_class_set_props(dc, m68k_irqc_properties); - nc->nmi_monitor_handler = m68k_nmi; + nc->raise_nmi = m68k_nmi; device_class_set_legacy_reset(dc, m68k_irqc_reset); dc->vmsd = &vmstate_m68k_irqc; ic->get_statistics = m68k_irqc_get_statistics; diff --git a/hw/intc/meson.build b/hw/intc/meson.build index fac2d228f9..0620b0792a 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -45,10 +45,10 @@ specific_ss.add(when: 'CONFIG_APIC', if_true: files('apic.c', 'apic_common.c')) arm_common_ss.add(when: 'CONFIG_ARM_GIC', if_true: files('arm_gicv3_cpuif_common.c')) arm_common_ss.add(when: 'CONFIG_ARM_GICV3', if_true: files('arm_gicv3_cpuif.c')) specific_ss.add(when: 'CONFIG_ARM_GIC_KVM', if_true: files('arm_gic_kvm.c')) -specific_ss.add(when: ['CONFIG_WHPX', 'TARGET_AARCH64'], if_true: files('arm_gicv3_whpx.c')) +specific_ss.add(when: ['CONFIG_WHPX', 'CONFIG_ARM_GICV3'], if_true: files('arm_gicv3_whpx.c')) specific_ss.add(when: ['CONFIG_HVF', 'CONFIG_ARM_GICV3'], if_true: files('arm_gicv3_hvf.c')) stub_ss.add(files('arm_gicv3_hvf_stub.c')) -specific_ss.add(when: ['CONFIG_ARM_GIC_KVM', 'TARGET_AARCH64'], if_true: files('arm_gicv3_kvm.c', 'arm_gicv3_its_kvm.c')) +specific_ss.add(when: 'CONFIG_ARM_GIC_KVM', if_true: files('arm_gicv3_kvm.c', 'arm_gicv3_its_kvm.c')) arm_common_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_nvic.c')) specific_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_irqmp.c')) specific_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic.c')) @@ -74,6 +74,8 @@ specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('xics_spapr.c', 'spapr_xi specific_ss.add(when: 'CONFIG_XIVE', if_true: files('xive.c')) specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'], if_true: files('spapr_xive_kvm.c')) + +specific_ss.add(when: 'CONFIG_HEX_L2VIC', if_true: files('hex-l2vic.c')) specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c')) specific_ss.add(when: 'CONFIG_LOONGSON_IPI_COMMON', if_true: files('loongson_ipi_common.c')) specific_ss.add(when: 'CONFIG_LOONGSON_IPI', if_true: files('loongson_ipi.c')) @@ -88,3 +90,5 @@ specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extio specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_LOONGARCH_EXTIOI'], if_true: files('loongarch_extioi_kvm.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_DINTC', if_true: files('loongarch_dintc.c')) +specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_LOONGARCH_DINTC'], + if_true: files('loongarch_dintc_kvm.c')) diff --git a/hw/intc/trace-events b/hw/intc/trace-events index e7d6c30448..7512c6e600 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -337,6 +337,10 @@ sh_intc_register(const char *s, int id, unsigned short v, int c, int m) "%s %u - sh_intc_read(unsigned size, uint64_t offset, unsigned long val) "size %u 0x%" PRIx64 " -> 0x%lx" sh_intc_write(unsigned size, uint64_t offset, unsigned long val) "size %u 0x%" PRIx64 " <- 0x%lx" sh_intc_set(int id, int enable) "setting interrupt group %d to %d" +# hex-l2vic.c +hex_l2vic_reg_write(unsigned int addr, uint32_t value) "addr: 0x%03x value: 0x%08"PRIx32 +hex_l2vic_reg_read(unsigned int addr, uint32_t value) "addr: 0x%03x value: 0x%08"PRIx32 +hex_l2vic_delivered(int irq, int vid) "l2vic: delivered %d (vid %d)" # loongson_ipi.c loongson_ipi_read(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%"PRIx64 diff --git a/hw/ipmi/ipmi.c b/hw/ipmi/ipmi.c index 74818ff3ce..dedf23cb99 100644 --- a/hw/ipmi/ipmi.c +++ b/hw/ipmi/ipmi.c @@ -59,8 +59,7 @@ static int ipmi_do_hw_op(IPMIInterface *s, enum ipmi_op op, int checkonly) if (checkonly) { return 0; } - /* We don't care what CPU we use. */ - nmi_monitor_handle(0, NULL); + nmi_inject(NULL); return 0; case IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP: diff --git a/hw/loongarch/virt-acpi-build.c b/hw/loongarch/virt-acpi-build.c index a0b445f297..600ef60fc5 100644 --- a/hw/loongarch/virt-acpi-build.c +++ b/hw/loongarch/virt-acpi-build.c @@ -128,7 +128,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, MachineClass *mc = MACHINE_GET_CLASS(ms); const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms); int i, arch_id, flags; - AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lvms->oem_id, + AcpiTable table = { .sig = "APIC", .rev = 6, .oem_id = lvms->oem_id, .oem_table_id = lvms->oem_table_id }; acpi_table_begin(&table, table_data); diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 6693dea647..9cc79929a8 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -1465,7 +1465,6 @@ static void virt_class_init(ObjectClass *oc, const void *data) mc->default_ram_id = "loongarch.ram"; mc->desc = "QEMU LoongArch Virtual Machine"; mc->max_cpus = LOONGARCH_MAX_CPUS; - mc->is_default = 1; mc->default_kernel_irqchip_split = false; mc->block_default_type = IF_VIRTIO; mc->default_boot_order = "c"; @@ -1546,6 +1545,7 @@ static void virt_class_init(ObjectClass *oc, const void *data) MACHINE_VER_DEPRECATION(__VA_ARGS__); \ if (latest) { \ mc->alias = "virt"; \ + mc->is_default = true; \ } \ } \ static const TypeInfo MACHINE_VER_SYM(info, virt, __VA_ARGS__) = \ @@ -1600,7 +1600,14 @@ static void machvirt_machine_init(void) type_init(machvirt_machine_init); -static void virt_machine_11_1_options(MachineClass *mc) +static void virt_machine_11_2_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE_AS_LATEST(11, 1) +DEFINE_VIRT_MACHINE_AS_LATEST(11, 2) + +static void virt_machine_11_1_options(MachineClass *mc) +{ + virt_machine_11_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_11_1, hw_compat_11_1_len); +} +DEFINE_VIRT_MACHINE(11, 1) diff --git a/hw/m68k/q800-glue.c b/hw/m68k/q800-glue.c index ac9062c648..223e7d9019 100644 --- a/hw/m68k/q800-glue.c +++ b/hw/m68k/q800-glue.c @@ -159,7 +159,7 @@ static void glue_auxmode_set_irq(void *opaque, int irq, int level) s->auxmode = level; } -static void glue_nmi(NMIState *n, int cpu_index, Error **errp) +static void glue_nmi(NMIState *n) { GLUEState *s = GLUE(n); @@ -237,7 +237,7 @@ static void glue_class_init(ObjectClass *klass, const void *data) dc->vmsd = &vmstate_glue; device_class_set_props(dc, glue_properties); rc->phases.hold = glue_reset_hold; - nc->nmi_monitor_handler = glue_nmi; + nc->raise_nmi = glue_nmi; } static const TypeInfo glue_info_types[] = { diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index 51158ce1c0..274970978f 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -367,10 +367,17 @@ type_init(virt_machine_register_types) #define DEFINE_VIRT_MACHINE(major, minor) \ DEFINE_VIRT_MACHINE_IMPL(false, major, minor) -static void virt_machine_11_1_options(MachineClass *mc) +static void virt_machine_11_2_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE_AS_LATEST(11, 1) +DEFINE_VIRT_MACHINE_AS_LATEST(11, 2) + +static void virt_machine_11_1_options(MachineClass *mc) +{ + virt_machine_11_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_11_1, hw_compat_11_1_len); +} +DEFINE_VIRT_MACHINE(11, 1) static void virt_machine_11_0_options(MachineClass *mc) { diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index cba05ec57d..28f41fa623 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -278,6 +278,9 @@ static bool cxl_doe_cdat_rsp(DOECap *doe_cap) } ent = req->entry_handle; + if (ent >= cdat->entry_len) { + return false; + } base = cdat->entry[ent].base; len = cdat->entry[ent].length; diff --git a/hw/mem/memory-device.c b/hw/mem/memory-device.c index 9cf82b84cf..24bebfb6b3 100644 --- a/hw/mem/memory-device.c +++ b/hw/mem/memory-device.c @@ -59,7 +59,7 @@ static int memory_device_build_list(Object *obj, void *opaque) if (object_dynamic_cast(obj, TYPE_MEMORY_DEVICE)) { DeviceState *dev = DEVICE(obj); - if (dev->realized) { /* only realized memory devices matter */ + if (qdev_is_realized(dev)) { *list = g_slist_insert_sorted(*list, dev, memory_device_addr_sort); } } @@ -326,7 +326,7 @@ static int memory_device_plugged_size(Object *obj, void *opaque) const MemoryDeviceState *md = MEMORY_DEVICE(obj); const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(obj); - if (dev->realized && !memory_device_is_empty(md)) { + if (qdev_is_realized(dev) && !memory_device_is_empty(md)) { *size += mdc->get_plugged_size(md, &error_abort); } } diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c index b703252527..cf8a4d8c5f 100644 --- a/hw/mem/nvdimm.c +++ b/hw/mem/nvdimm.c @@ -99,7 +99,7 @@ static void nvdimm_set_uuid(Object *obj, Visitor *v, const char *name, static void nvdimm_init(Object *obj) { - object_property_add(obj, NVDIMM_LABEL_SIZE_PROP, "int", + object_property_add(obj, NVDIMM_LABEL_SIZE_PROP, "size", nvdimm_get_label_size, nvdimm_set_label_size, NULL, NULL); diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 3efe47f499..68862926ee 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -103,7 +103,7 @@ static int pc_dimm_slot2bitmap(Object *obj, void *opaque) if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { DeviceState *dev = DEVICE(obj); - if (dev->realized) { /* count only realized DIMMs */ + if (qdev_is_realized(dev)) { /* count only realized DIMMs */ PCDIMMDevice *d = PC_DIMM(obj); set_bit(d->slot, bitmap); } diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index 1543ee6653..46e3c03cc8 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -257,4 +257,10 @@ config XLNX_VERSAL_TRNG config XLNX_ZYNQ_DDRC bool +config AXIADO_CLK + bool + +config K230_DECOMP_GZIP + bool + source macio/Kconfig diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c index fd96f5f245..d004e8b443 100644 --- a/hw/misc/applesmc.c +++ b/hw/misc/applesmc.c @@ -333,9 +333,9 @@ static void applesmc_isa_realize(DeviceState *dev, Error **errp) applesmc_add_key(s, "REV ", 6, "\x01\x13\x0f\x00\x00\x03"); applesmc_add_key(s, "OSK0", 32, s->osk); applesmc_add_key(s, "OSK1", 32, s->osk + 32); - applesmc_add_key(s, "NATJ", 1, "\0"); - applesmc_add_key(s, "MSSP", 1, "\0"); - applesmc_add_key(s, "MSSD", 1, "\0x3"); + applesmc_add_key(s, "NATJ", 1, "\x00"); + applesmc_add_key(s, "MSSP", 1, "\x00"); + applesmc_add_key(s, "MSSD", 1, "\x03"); } static void applesmc_unrealize(DeviceState *dev) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index c61efe50c4..8de05a9d98 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -18,11 +18,58 @@ #include "qapi/error.h" #include "migration/vmstate.h" #include "crypto/hash.h" +#include "crypto/cipher.h" #include "hw/core/qdev-properties.h" #include "hw/core/irq.h" #include "trace.h" -#define R_CRYPT_CMD (0x10 / 4) +/* Crypto engine registers */ +#define R_CRYPT_SRC (0x00 / 4) +#define R_CRYPT_DEST (0x04 / 4) +#define R_CRYPT_CONTEXT (0x08 / 4) +#define R_CRYPT_DATA_LEN (0x0c / 4) +/* HACE0C[27:0] holds the crypto data length */ +#define CRYPT_DATA_LEN_MASK 0x0FFFFFFF +#define R_CRYPT_CMD (0x10 / 4) +/* AES-GCM associated data length (HACE14) and tag write buffer (HACE18) */ +#define R_CRYPT_GCM_ADD_LEN (0x14 / 4) +#define R_CRYPT_GCM_TAG (0x18 / 4) +/* Crypto engine command register (HACE10) bits */ +#define CRYPT_CMD_ENCRYPT BIT(7) +#define CRYPT_CMD_ISR_EN BIT(12) +#define CRYPT_CMD_DES_SELECT BIT(16) +#define CRYPT_CMD_TRIPLE_DES BIT(17) +#define CRYPT_CMD_SRC_SG_CTRL BIT(18) +/* Operation mode HACE10[6:4] */ +#define CRYPT_CMD_OP_MODE_MASK (0x7 << 4) +#define CRYPT_CMD_ECB (0x0 << 4) +#define CRYPT_CMD_CBC (0x1 << 4) +#define CRYPT_CMD_CTR (0x4 << 4) +#define CRYPT_CMD_GCM (0x5 << 4) +/* AES key length HACE10[3:2] */ +#define CRYPT_CMD_AES_KEY_LEN_MASK (0x3 << 2) +#define CRYPT_CMD_AES256 (0x2 << 2) +#define CRYPT_CMD_AES192 (0x1 << 2) +#define CRYPT_CMD_AES128 (0x0 << 2) + +/* + * Crypto context buffer layout (HACE08). The IV is at the start of the buffer + * (DES places its 8 byte IV at offset 8) and the cipher key at offset 0x10. + */ +#define CRYPT_CTX_IV_OFFSET 0x00 +#define CRYPT_CTX_DES_IV_OFFSET 0x08 +#define CRYPT_CTX_KEY_OFFSET 0x10 +#define CRYPT_CTX_SIZE 0x30 + +/* AES-GCM uses a 96-bit IV and a 128-bit authentication tag */ +#define CRYPT_GCM_IV_LEN 12 +#define CRYPT_GCM_TAG_LEN 16 + +/* AST2700 64-bit DMA high address registers for the crypto command */ +#define R_CRYPT_SRC_HI (0x80 / 4) +#define R_CRYPT_DEST_HI (0x84 / 4) +#define R_CRYPT_CONTEXT_HI (0x88 / 4) +#define R_CRYPT_GCM_TAG_HI (0x8c / 4) #define R_STATUS (0x1c / 4) #define HASH_IRQ BIT(9) @@ -65,7 +112,6 @@ /* Other cmd bits */ #define HASH_IRQ_EN BIT(9) #define HASH_SG_EN BIT(18) -#define CRYPT_IRQ_EN BIT(12) /* Scatter-gather data list */ #define SG_LIST_LEN_SIZE 4 #define SG_LIST_LEN_MASK 0x0FFFFFFF @@ -501,6 +547,368 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, } } +static bool crypt_aes_alg(uint32_t cmd, QCryptoCipherAlgo *alg, size_t *keylen) +{ + switch (cmd & CRYPT_CMD_AES_KEY_LEN_MASK) { + case CRYPT_CMD_AES128: + *alg = QCRYPTO_CIPHER_ALGO_AES_128; + *keylen = 16; + break; + case CRYPT_CMD_AES192: + *alg = QCRYPTO_CIPHER_ALGO_AES_192; + *keylen = 24; + break; + case CRYPT_CMD_AES256: + *alg = QCRYPTO_CIPHER_ALGO_AES_256; + *keylen = 32; + break; + default: + return false; + } + + return true; +} + +/* + * Decode the crypto command register into a libqcrypto algorithm/mode pair + * and the block/IV geometry. Returns false for unsupported selections. + */ +static bool crypt_decode_cmd(uint32_t cmd, QCryptoCipherAlgo *alg, + QCryptoCipherMode *mode, size_t *keylen, + size_t *blocklen, size_t *iv_offset) +{ + if (cmd & CRYPT_CMD_DES_SELECT) { + *blocklen = 8; + *iv_offset = CRYPT_CTX_DES_IV_OFFSET; + if (cmd & CRYPT_CMD_TRIPLE_DES) { + *alg = QCRYPTO_CIPHER_ALGO_3DES; + *keylen = 24; + } else { + *alg = QCRYPTO_CIPHER_ALGO_DES; + *keylen = 8; + } + } else { + *blocklen = 16; + *iv_offset = CRYPT_CTX_IV_OFFSET; + if (!crypt_aes_alg(cmd, alg, keylen)) { + return false; + } + } + + switch (cmd & CRYPT_CMD_OP_MODE_MASK) { + case CRYPT_CMD_ECB: + *mode = QCRYPTO_CIPHER_MODE_ECB; + break; + case CRYPT_CMD_CBC: + *mode = QCRYPTO_CIPHER_MODE_CBC; + break; + case CRYPT_CMD_CTR: + *mode = QCRYPTO_CIPHER_MODE_CTR; + break; + case CRYPT_CMD_GCM: + *mode = QCRYPTO_CIPHER_MODE_GCM; + break; + default: + return false; + } + + return true; +} + +/* + * Direct access mode: the source/destination register (HACE00/HACE04) points + * at a single contiguous buffer in DRAM. Copy @len bytes between it and the + * bounce buffer @buf; when @to_dram is true @buf is written out, otherwise it + * is read in. Returns true on success. + */ +static bool crypt_prepare_direct(AspeedHACEState *s, uint64_t addr, + uint8_t *buf, uint32_t len, bool to_dram) +{ + return !address_space_rw(&s->dram_as, addr, MEMTXATTRS_UNSPECIFIED, + buf, len, to_dram); +} + +/* + * Scatter-gather mode: the source/destination register points at an SG list + * whose entries are a length word (SG_LIST_LEN_LAST flags the final entry) + * followed by a DRAM address, matching the hash engine layout. Gather @len + * bytes into @buf, or scatter @buf back out when @to_dram is true. + * Returns true on success. + */ +static bool crypt_prepare_sg(AspeedHACEState *s, uint64_t addr, + uint8_t *buf, uint32_t len, bool to_dram) +{ + uint32_t copied = 0; + uint32_t sg_addr; + uint32_t sg_len; + uint32_t entry; + int i; + + for (i = 0; i < ASPEED_HACE_MAX_SG && copied < len; i++) { + entry = address_space_ldl_le(&s->dram_as, addr, + MEMTXATTRS_UNSPECIFIED, NULL); + sg_addr = address_space_ldl_le(&s->dram_as, addr + SG_LIST_LEN_SIZE, + MEMTXATTRS_UNSPECIFIED, NULL); + sg_len = entry & SG_LIST_LEN_MASK; + + sg_addr &= SG_LIST_ADDR_MASK; + addr += SG_LIST_ENTRY_SIZE; + + if (sg_len > len - copied) { + sg_len = len - copied; + } + if (address_space_rw(&s->dram_as, sg_addr, MEMTXATTRS_UNSPECIFIED, + buf + copied, sg_len, to_dram)) { + return false; + } + copied += sg_len; + + if (entry & SG_LIST_LEN_LAST) { + break; + } + } + + return copied == len; +} + +/* + * Add @add to the big-endian counter block @ctr (@len bytes) in place, so the + * CTR mode counter can be advanced by the number of blocks just consumed. + */ +static void crypt_be_add(uint8_t *ctr, size_t len, uint64_t add) +{ + size_t i = len; + + while (i > 0 && add) { + i--; + add += ctr[i]; + ctr[i] = add & 0xff; + add >>= 8; + } +} + +static uint64_t crypt_get_addr(AspeedHACEState *s, int reg, int reg_hi) +{ + AspeedHACEClass *ahc = ASPEED_HACE_GET_CLASS(s); + uint64_t addr; + + addr = deposit64(0, 0, 32, s->regs[reg]); + if (ahc->has_dma64) { + addr = deposit64(addr, 32, 32, s->regs[reg_hi]); + } + + return addr; +} + +/* + * Perform an AES/DES/3DES ECB/CBC/CTR or AES-GCM operation. The source and + * destination are either single contiguous buffers (direct access mode) or + * scatter-gather lists (HACE10[18]/[19]), addressed by HACE00/HACE04; the + * IV/key come from the context buffer (HACE08). For CBC and CTR the resulting + * chaining state is written back to the context buffer so the driver can + * continue; for GCM the authentication tag is written to the tag buffer. + */ +static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd) +{ + bool sg_mode = cmd & CRYPT_CMD_SRC_SG_CTRL; + uint32_t len = s->regs[R_CRYPT_DATA_LEN]; + bool encrypt = cmd & CRYPT_CMD_ENCRYPT; + g_autoptr(QCryptoCipher) cipher = NULL; + g_autofree uint8_t *src_buf = NULL; + g_autofree uint8_t *dst_buf = NULL; + uint8_t tag[CRYPT_GCM_TAG_LEN]; + uint8_t ctx[CRYPT_CTX_SIZE]; + Error *local_err = NULL; + QCryptoCipherMode mode; + QCryptoCipherAlgo alg; + const uint8_t *next_iv; + uint64_t ctx_addr; + uint64_t src_addr; + uint64_t dst_addr; + uint64_t tag_addr; + uint32_t aad_len; + size_t iv_offset; + size_t blocklen; + size_t buf_len; + size_t keylen; + size_t ivlen; + bool status; + + if (len == 0) { + return; + } + + if (!crypt_decode_cmd(cmd, &alg, &mode, &keylen, &blocklen, &iv_offset)) { + qemu_log_mask(LOG_UNIMP, + "%s: Unsupported crypt command 0x%x\n", __func__, cmd); + return; + } + + if (!qcrypto_cipher_supports(alg, mode)) { + qemu_log_mask(LOG_UNIMP, + "%s: cipher mode not supported by the crypto backend\n", + __func__); + return; + } + + /* GCM uses a 96-bit IV; the block modes use a full-block IV. */ + ivlen = (mode == QCRYPTO_CIPHER_MODE_GCM) ? CRYPT_GCM_IV_LEN : blocklen; + + /* + * The hardware GCM path is only exercised without associated data (the + * driver falls back to software when there is any), so AAD is not modelled. + */ + aad_len = s->regs[R_CRYPT_GCM_ADD_LEN]; + if (mode == QCRYPTO_CIPHER_MODE_GCM && aad_len != 0) { + qemu_log_mask(LOG_UNIMP, + "%s: GCM associated data is not implemented\n", __func__); + return; + } + + /* Fetch the IV and key from the context buffer in DRAM. */ + ctx_addr = crypt_get_addr(s, R_CRYPT_CONTEXT, R_CRYPT_CONTEXT_HI); + if (address_space_read(&s->dram_as, ctx_addr, MEMTXATTRS_UNSPECIFIED, + ctx, sizeof(ctx))) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to read context, addr=0x%" HWADDR_PRIx "\n", + __func__, ctx_addr); + return; + } + + if (trace_event_get_state_backends(TRACE_ASPEED_HACE_HEXDUMP)) { + hace_hexdump("context", (char *)ctx, sizeof(ctx)); + } + + cipher = qcrypto_cipher_new(alg, mode, ctx + CRYPT_CTX_KEY_OFFSET, keylen, + &local_err); + if (cipher == NULL) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto cipher new failed: %s\n", + __func__, error_get_pretty(local_err)); + error_free(local_err); + return; + } + + if (mode != QCRYPTO_CIPHER_MODE_ECB && + qcrypto_cipher_setiv(cipher, ctx + iv_offset, ivlen, + &local_err) < 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto cipher setiv failed: %s\n", + __func__, error_get_pretty(local_err)); + error_free(local_err); + return; + } + + /* + * Round the working buffers up to a whole block. Block modes are already + * block-aligned; the stream-like CTR mode may leave a partial final block + * that the engine still processes a full block at a time. GCM handles a + * partial final block itself, so it operates on the exact length. + */ + buf_len = (mode == QCRYPTO_CIPHER_MODE_GCM) ? + len : QEMU_ALIGN_UP(len, blocklen); + src_buf = g_malloc0(buf_len); + dst_buf = g_malloc0(buf_len); + + /* Gather the source into the bounce buffer, per the selected mode. */ + src_addr = crypt_get_addr(s, R_CRYPT_SRC, R_CRYPT_SRC_HI); + if (sg_mode) { + status = crypt_prepare_sg(s, src_addr, src_buf, len, false); + } else { + status = crypt_prepare_direct(s, src_addr, src_buf, len, false); + } + if (!status) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to read src, addr=0x%" HWADDR_PRIx "\n", + __func__, src_addr); + return; + } + + if (trace_event_get_state_backends(TRACE_ASPEED_HACE_HEXDUMP)) { + hace_hexdump("src", (char *)src_buf, len); + } + + if (encrypt) { + if (qcrypto_cipher_encrypt(cipher, src_buf, dst_buf, buf_len, + &local_err) < 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: encrypt failed: %s\n", + __func__, error_get_pretty(local_err)); + error_free(local_err); + return; + } + } else { + if (qcrypto_cipher_decrypt(cipher, src_buf, dst_buf, buf_len, + &local_err) < 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: decrypt failed: %s\n", + __func__, error_get_pretty(local_err)); + error_free(local_err); + return; + } + } + + /* Scatter the result back out, per the selected mode. */ + dst_addr = crypt_get_addr(s, R_CRYPT_DEST, R_CRYPT_DEST_HI); + if (sg_mode) { + status = crypt_prepare_sg(s, dst_addr, dst_buf, len, true); + } else { + status = crypt_prepare_direct(s, dst_addr, dst_buf, len, true); + } + if (!status) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to write dst, addr=0x%" HWADDR_PRIx "\n", + __func__, dst_addr); + return; + } + + if (trace_event_get_state_backends(TRACE_ASPEED_HACE_HEXDUMP)) { + hace_hexdump("dst", (char *)dst_buf, len); + } + + if (mode == QCRYPTO_CIPHER_MODE_CBC) { + /* + * CBC chains on the last ciphertext block: the final block of the + * output when encrypting, or of the input when decrypting. Write it + * back as the IV for the next request. + */ + next_iv = (encrypt ? dst_buf : src_buf) + buf_len - blocklen; + if (address_space_write(&s->dram_as, ctx_addr + iv_offset, + MEMTXATTRS_UNSPECIFIED, next_iv, blocklen)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to write IV, addr=0x%" HWADDR_PRIx "\n", + __func__, ctx_addr + iv_offset); + } + } else if (mode == QCRYPTO_CIPHER_MODE_CTR) { + /* + * CTR chains on the counter, which advances by one per block. Add the + * number of blocks processed (buf_len / blocklen) and write it back. + */ + crypt_be_add(ctx + iv_offset, blocklen, buf_len / blocklen); + if (address_space_write(&s->dram_as, ctx_addr + iv_offset, + MEMTXATTRS_UNSPECIFIED, ctx + iv_offset, + blocklen)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to write IV, addr=0x%" HWADDR_PRIx "\n", + __func__, ctx_addr + iv_offset); + } + } else if (mode == QCRYPTO_CIPHER_MODE_GCM) { + /* + * GCM authenticates the message and writes the resulting tag to the + * dedicated tag buffer (HACE18/HACE8C). + */ + if (qcrypto_cipher_gettag(cipher, tag, sizeof(tag), &local_err) < 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto cipher gettag failed: " + "%s\n", __func__, error_get_pretty(local_err)); + error_free(local_err); + return; + } + tag_addr = crypt_get_addr(s, R_CRYPT_GCM_TAG, R_CRYPT_GCM_TAG_HI); + if (address_space_write(&s->dram_as, tag_addr, MEMTXATTRS_UNSPECIFIED, + tag, sizeof(tag))) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to write tag, addr=0x%" HWADDR_PRIx "\n", + __func__, tag_addr); + } + } +} + static uint64_t aspeed_hace_read(void *opaque, hwaddr addr, unsigned int size) { AspeedHACEState *s = ASPEED_HACE(opaque); @@ -531,16 +939,24 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, qemu_irq_lower(s->irq); } } - if (ahc->raise_crypt_interrupt_workaround) { - if (data & CRYPT_IRQ) { - data &= ~CRYPT_IRQ; + if (data & CRYPT_IRQ) { + data &= ~CRYPT_IRQ; - if (s->regs[addr] & CRYPT_IRQ) { - qemu_irq_lower(s->irq); - } + if (s->regs[addr] & CRYPT_IRQ) { + qemu_irq_lower(s->irq); } } break; + case R_CRYPT_SRC: + case R_CRYPT_DEST: + case R_CRYPT_CONTEXT: + case R_CRYPT_GCM_TAG: + data &= ahc->src_mask; + break; + case R_CRYPT_DATA_LEN: + case R_CRYPT_GCM_ADD_LEN: + data &= CRYPT_DATA_LEN_MASK; + break; case R_HASH_SRC: data &= ahc->src_mask; break; @@ -589,13 +1005,12 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, break; } case R_CRYPT_CMD: - qemu_log_mask(LOG_UNIMP, "%s: Crypt commands not implemented\n", - __func__); - if (ahc->raise_crypt_interrupt_workaround) { - s->regs[R_STATUS] |= CRYPT_IRQ; - if (data & CRYPT_IRQ_EN) { - qemu_irq_raise(s->irq); - } + do_crypt_operation(s, data); + + /* Hardware raises the crypt interrupt once the command finishes. */ + s->regs[R_STATUS] |= CRYPT_IRQ; + if (data & CRYPT_CMD_ISR_EN) { + qemu_irq_raise(s->irq); } break; case R_HASH_SRC_HI: @@ -607,6 +1022,16 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, case R_HASH_KEY_BUFF_HI: data &= ahc->key_hi_mask; break; + case R_CRYPT_SRC_HI: + data &= ahc->src_hi_mask; + break; + case R_CRYPT_DEST_HI: + case R_CRYPT_GCM_TAG_HI: + data &= ahc->dest_hi_mask; + break; + case R_CRYPT_CONTEXT_HI: + data &= ahc->key_hi_mask; + break; default: break; } @@ -782,12 +1207,6 @@ static void aspeed_ast2700_hace_class_init(ObjectClass *klass, const void *data) ahc->dest_hi_mask = 0x00000003; ahc->key_hi_mask = 0x00000003; - /* - * Currently, it does not support the CRYPT command. Instead, it only - * sends an interrupt to notify the firmware that the crypt command - * has completed. It is a temporary workaround. - */ - ahc->raise_crypt_interrupt_workaround = true; ahc->has_dma64 = true; } diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index 5dbf81c0ce..ca93c3699d 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -930,6 +930,11 @@ static void aspeed_ast2700_scu_reset_hold(Object *obj, ResetType type) s->regs[AST2700_HW_STRAP1] = s->hw_strap1; } +static void aspeed_2700_scu_realize(DeviceState *dev, Error **errp) +{ + aspeed_scu_realize(dev, errp); +} + static void aspeed_2700_scu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -937,6 +942,7 @@ static void aspeed_2700_scu_class_init(ObjectClass *klass, const void *data) AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass); dc->desc = "ASPEED 2700 System Control Unit"; + dc->realize = aspeed_2700_scu_realize; rc->phases.hold = aspeed_ast2700_scu_reset_hold; asc->resets = ast2700_a0_resets; asc->calc_hpll = aspeed_2600_scu_calc_hpll; @@ -1063,6 +1069,16 @@ static const uint32_t ast2700_a0_resets_io[ASPEED_AST2700_SCU_NR_REGS] = { [AST2700_SCUIO_FREQ_CNT_CTL] = 0x00000080, }; +static void aspeed_ast2700_scuio_reset_hold(Object *obj, ResetType type) +{ + AspeedSCUState *s = ASPEED_SCU(obj); + AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(obj); + + memcpy(s->regs, asc->resets, asc->nr_regs * 4); + s->regs[AST2700_SILICON_REV] = s->silicon_rev; + s->regs[AST2700_HW_STRAP1] = s->hw_strap1; +} + static void aspeed_2700_scuio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1070,7 +1086,7 @@ static void aspeed_2700_scuio_class_init(ObjectClass *klass, const void *data) AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass); dc->desc = "ASPEED 2700 System Control Unit I/O"; - rc->phases.hold = aspeed_ast2700_scu_reset_hold; + rc->phases.hold = aspeed_ast2700_scuio_reset_hold; asc->resets = ast2700_a0_resets_io; asc->calc_hpll = aspeed_2600_scu_calc_hpll; asc->get_apb = aspeed_2700_scuio_get_apb_freq; @@ -1161,7 +1177,7 @@ static const TypeInfo aspeed_scu_types[] = { { .name = TYPE_ASPEED_2700_SCU, .parent = TYPE_ASPEED_SCU, - .instance_size = sizeof(AspeedSCUState), + .instance_size = sizeof(Aspeed2700SCUState), .class_init = aspeed_2700_scu_class_init, }, { diff --git a/hw/misc/axiado_clk.c b/hw/misc/axiado_clk.c new file mode 100644 index 0000000000..090beb38b5 --- /dev/null +++ b/hw/misc/axiado_clk.c @@ -0,0 +1,79 @@ +/* + * Axiado Clock Control + * + * Author: Kuan-Jui Chiu + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/misc/axiado_clk.h" +#include "qemu/log.h" + +#define CLKRST_CPU_PLL_POSTDIV_OFFSET 0x0C +#define CLKRST_CPU_PLL_STS_OFFSET 0x14 + +static uint64_t pll_read(void *opaque, hwaddr offset, unsigned size) +{ + switch (offset) { + case CLKRST_CPU_PLL_POSTDIV_OFFSET: + return 0x20891b; + case CLKRST_CPU_PLL_STS_OFFSET: + return 0x01; + default: + qemu_log_mask(LOG_UNIMP, + "Register 0x%" HWADDR_PRIx " not implemented\n", offset); + break; + } + return 0x00; +} + +static void pll_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) +{ + qemu_log_mask(LOG_UNIMP, + "Register 0x%" HWADDR_PRIx " not implemented\n", offset); +} + +static const MemoryRegionOps pll_ops = { + .read = pll_read, + .write = pll_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static void ax3000_clk_init(Object *obj) +{ + Ax3000ClkState *s = AX3000_CLK(obj); + + memory_region_init_io(&s->pll_ctrl, obj, &pll_ops, s, + TYPE_AX3000_CLK, AX3000_CLK_PLL_CTRL_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->pll_ctrl); +} + +static void ax3000_clk_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "Axiado AX3000 Clock Control"; +} + +static const TypeInfo ax3000_clk_info = { + .parent = TYPE_SYS_BUS_DEVICE, + .name = TYPE_AX3000_CLK, + .instance_size = sizeof(Ax3000ClkState), + .instance_init = ax3000_clk_init, + .class_init = ax3000_clk_class_init, +}; + +static void axiado_clk_register_type(void) +{ + type_register_static(&ax3000_clk_info); +} +type_init(axiado_clk_register_type); diff --git a/hw/misc/bcm2835_powermgt.c b/hw/misc/bcm2835_powermgt.c index 3ec7abad0e..7b01be48bb 100644 --- a/hw/misc/bcm2835_powermgt.c +++ b/hw/misc/bcm2835_powermgt.c @@ -19,10 +19,40 @@ #define PASSWORD_MASK 0xff000000 #define R_RSTC 0x1c -#define V_RSTC_RESET 0x20 +#define V_RSTC_WRCFG_MASK 0x30 +#define V_RSTC_FULL_RESET 0x20 #define R_RSTS 0x20 #define V_RSTS_POWEROFF 0x555 /* Linux uses partition 63 to indicate halt. */ #define R_WDOG 0x24 +#define V_WDOG_TIME_MASK 0xfffff +#define WDOG_TICKS_PER_SECOND 65536 + +static void bcm2835_powermgt_expire(void *opaque) +{ + BCM2835PowerMgtState *s = opaque; + + if ((s->rsts & 0xfff) == V_RSTS_POWEROFF) { + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + } else { + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + } +} + +static void bcm2835_powermgt_update_wdog(BCM2835PowerMgtState *s) +{ + uint64_t timeout_ns; + + if ((s->rstc & V_RSTC_WRCFG_MASK) != V_RSTC_FULL_RESET || + s->wdog == 0) { + timer_del(s->wdog_timer); + return; + } + + timeout_ns = muldiv64(s->wdog, NANOSECONDS_PER_SECOND, + WDOG_TICKS_PER_SECOND); + timer_mod(s->wdog_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + timeout_ns); +} static uint64_t bcm2835_powermgt_read(void *opaque, hwaddr offset, unsigned size) @@ -70,13 +100,7 @@ static void bcm2835_powermgt_write(void *opaque, hwaddr offset, switch (offset) { case R_RSTC: s->rstc = value; - if (value & V_RSTC_RESET) { - if ((s->rsts & 0xfff) == V_RSTS_POWEROFF) { - qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); - } else { - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); - } - } + bcm2835_powermgt_update_wdog(s); break; case R_RSTS: qemu_log_mask(LOG_UNIMP, @@ -84,9 +108,8 @@ static void bcm2835_powermgt_write(void *opaque, hwaddr offset, s->rsts = value; break; case R_WDOG: - qemu_log_mask(LOG_UNIMP, - "bcm2835_powermgt_write: WDOG\n"); - s->wdog = value; + s->wdog = value & V_WDOG_TIME_MASK; + bcm2835_powermgt_update_wdog(s); break; default: @@ -107,12 +130,13 @@ static const MemoryRegionOps bcm2835_powermgt_ops = { static const VMStateDescription vmstate_bcm2835_powermgt = { .name = TYPE_BCM2835_POWERMGT, - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (const VMStateField[]) { VMSTATE_UINT32(rstc, BCM2835PowerMgtState), VMSTATE_UINT32(rsts, BCM2835PowerMgtState), VMSTATE_UINT32(wdog, BCM2835PowerMgtState), + VMSTATE_TIMER_PTR(wdog_timer, BCM2835PowerMgtState), VMSTATE_END_OF_LIST() } }; @@ -124,6 +148,8 @@ static void bcm2835_powermgt_init(Object *obj) memory_region_init_io(&s->iomem, obj, &bcm2835_powermgt_ops, s, TYPE_BCM2835_POWERMGT, 0x200); sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); + s->wdog_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + bcm2835_powermgt_expire, s); } static void bcm2835_powermgt_reset(DeviceState *dev) @@ -134,6 +160,7 @@ static void bcm2835_powermgt_reset(DeviceState *dev) s->rstc = 0x00000102; s->rsts = 0x00001000; s->wdog = 0x00000000; + timer_del(s->wdog_timer); } static void bcm2835_powermgt_class_init(ObjectClass *klass, const void *data) diff --git a/hw/misc/iotkit-secctl.c b/hw/misc/iotkit-secctl.c index 54bfe1ba59..93753c2ced 100644 --- a/hw/misc/iotkit-secctl.c +++ b/hw/misc/iotkit-secctl.c @@ -195,7 +195,7 @@ static MemTxResult iotkit_secctl_s_read(void *opaque, hwaddr addr, case A_AHBSPPPCEXP1: case A_AHBSPPPCEXP2: case A_AHBSPPPCEXP3: - r = s->apbexp[offset_to_ppc_idx(offset)].sp; + r = s->ahbexp[offset_to_ppc_idx(offset)].sp; break; case A_APBSPPPC0: case A_APBSPPPC1: diff --git a/hw/misc/ivshmem-pci.c b/hw/misc/ivshmem-pci.c index 536475e9de..372e9dc58d 100644 --- a/hw/misc/ivshmem-pci.c +++ b/hw/misc/ivshmem-pci.c @@ -938,6 +938,9 @@ static void ivshmem_exit(PCIDevice *dev) IVShmemState *s = IVSHMEM_COMMON(dev); int i; + qemu_chr_fe_set_handlers(&s->server_chr, + NULL, NULL, NULL, NULL, NULL, NULL, true); + migrate_del_blocker(&s->migration_blocker); if (memory_region_is_mapped(s->ivshmem_bar2)) { @@ -966,6 +969,8 @@ static void ivshmem_exit(PCIDevice *dev) close_peer_eventfds(s, i); } g_free(s->peers); + s->peers = NULL; + s->nb_peers = 0; } if (ivshmem_has_feature(s, IVSHMEM_MSI)) { @@ -973,6 +978,7 @@ static void ivshmem_exit(PCIDevice *dev) } g_free(s->msi_vectors); + s->msi_vectors = NULL; } static int ivshmem_pre_load(void *opaque) diff --git a/hw/misc/k230_ddr.c b/hw/misc/k230_ddr.c new file mode 100644 index 0000000000..dac700b201 --- /dev/null +++ b/hw/misc/k230_ddr.c @@ -0,0 +1,556 @@ +/* + * Kendryte K230 DDR controller and PHY models + * + * Models the K230 DDRC CFG registers and K230 DDR PHY registers exercised + * by the K230 SDK U-Boot SPL. + * + * Copyright (c) 2026 Junze Cao + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "migration/vmstate.h" +#include "hw/core/registerfields.h" +#include "hw/core/resettable.h" +#include "hw/misc/k230_ddr.h" + +/* DDRC CFG registers */ + +enum K230DDRCOperatingMode { + K230_DDRC_MODE_INIT = 0, + K230_DDRC_MODE_NORMAL = 1, +}; + +REG32(K230_DDRC_MSTR, 0x000) + +REG32(K230_DDRC_STAT, 0x004) + FIELD(K230_DDRC_STAT, OPERATING_MODE, 0, 3) + +REG32(K230_DDRC_PWRCTL, 0x030) + +REG32(K230_DDRC_RFSHCTL0, 0x050) + +REG32(K230_DDRC_RFSHTMG, 0x064) + +REG32(K230_DDRC_RFSHTMG1, 0x068) + +REG32(K230_DDRC_INIT0, 0x0d0) + +REG32(K230_DDRC_INIT2, 0x0d8) + +REG32(K230_DDRC_INIT3, 0x0dc) + +REG32(K230_DDRC_INIT5, 0x0e4) + +REG32(K230_DDRC_DRAMTMG0, 0x100) +#define R_K230_DDRC_DRAMTMG(index) \ + (R_K230_DDRC_DRAMTMG0 + (index)) + +REG32(K230_DDRC_ZQCTL0, 0x180) + +REG32(K230_DDRC_ZQCTL1, 0x184) + +REG32(K230_DDRC_ZQCTL2, 0x188) + +REG32(K230_DDRC_ZQSTAT, 0x18c) + +REG32(K230_DDRC_DFITMG0, 0x190) + +REG32(K230_DDRC_DFITMG1, 0x194) + +REG32(K230_DDRC_DFIUPD0, 0x1a0) + +REG32(K230_DDRC_DFIUPD1, 0x1a4) + +REG32(K230_DDRC_DFIUPD2, 0x1a8) + +REG32(K230_DDRC_DFIMISC, 0x1b0) + FIELD(K230_DDRC_DFIMISC, DFI_INIT_COMPLETE_EN, 0, 1) + FIELD(K230_DDRC_DFIMISC, DFI_INIT_START, 5, 1) + +REG32(K230_DDRC_DFITMG2, 0x1b4) + +REG32(K230_DDRC_DFISTAT, 0x1bc) + FIELD(K230_DDRC_DFISTAT, DFI_INIT_COMPLETE, 0, 1) + +REG32(K230_DDRC_ODTCFG, 0x240) + +REG32(K230_DDRC_SWCTL, 0x320) + FIELD(K230_DDRC_SWCTL, SW_DONE, 0, 1) + +REG32(K230_DDRC_SWSTAT, 0x324) + FIELD(K230_DDRC_SWSTAT, SW_DONE_ACK, 0, 1) + +static const uint32_t k230_ddrc_reset_values[K230_DDRC_REG_COUNT] = { + [R_K230_DDRC_MSTR] = 0x01040000, + [R_K230_DDRC_RFSHCTL0] = 0x00210000, + [R_K230_DDRC_RFSHTMG] = 0x0062008c, + [R_K230_DDRC_RFSHTMG1] = 0x0000008c, + [R_K230_DDRC_INIT0] = 0x0002004e, + [R_K230_DDRC_INIT3] = 0x00000510, + [R_K230_DDRC_INIT5] = 0x00100000, + [R_K230_DDRC_DRAMTMG(0)] = 0x0f101b0f, + [R_K230_DDRC_DRAMTMG(1)] = 0x00080414, + [R_K230_DDRC_DRAMTMG(2)] = 0x0305060d, + [R_K230_DDRC_DRAMTMG(3)] = 0x00004000, + [R_K230_DDRC_DRAMTMG(4)] = 0x05040405, + [R_K230_DDRC_DRAMTMG(5)] = 0x05050403, + [R_K230_DDRC_DRAMTMG(6)] = 0x02020005, + [R_K230_DDRC_DRAMTMG(7)] = 0x00000202, + [R_K230_DDRC_DRAMTMG(8)] = 0x03034405, + [R_K230_DDRC_DRAMTMG(9)] = 0x0004040d, + [R_K230_DDRC_DRAMTMG(10)] = 0x001c180a, + [R_K230_DDRC_DRAMTMG(11)] = 0x440c021c, + [R_K230_DDRC_DRAMTMG(12)] = 0x00020610, + [R_K230_DDRC_DRAMTMG(13)] = 0x1c200004, + [R_K230_DDRC_DRAMTMG(14)] = 0x000000a0, + [R_K230_DDRC_DRAMTMG(16)] = 0x05100404, + [R_K230_DDRC_ZQCTL0] = 0x02000040, + [R_K230_DDRC_ZQCTL1] = 0x02000100, + [R_K230_DDRC_DFITMG0] = 0x07020002, + [R_K230_DDRC_DFITMG1] = 0x00000404, + [R_K230_DDRC_DFIUPD0] = 0x00400003, + [R_K230_DDRC_DFIUPD1] = 0x00010001, + [R_K230_DDRC_DFIUPD2] = 0x80000000, + [R_K230_DDRC_DFIMISC] = 0x00000001, + [R_K230_DDRC_DFITMG2] = 0x00000202, + [R_K230_DDRC_ODTCFG] = 0x04000400, + [R_K230_DDRC_SWCTL] = 0x00000001, + [R_K230_DDRC_SWSTAT] = 0x00000001, +}; + +/* K230 DDR PHY CSRs use byte offset = CSR index * 4. */ +#define K230_DDR_PHY_CSR_OFFSET(index) ((index) * 4) +#define K230_DDR_PHY_MASTER_CSR(reg) \ + K230_DDR_PHY_CSR_OFFSET(0x20000 + (reg)) +#define K230_DDR_PHY_APBONLY_CSR(reg) \ + K230_DDR_PHY_CSR_OFFSET(0xd0000 + (reg)) + +#define K230_DDR_PHY_ATX_IMPEDANCE_RESET UINT32_C(0x03ff) + +REG32(K230_DDR_PHY_MICRO_CONT_MUX_SEL, + K230_DDR_PHY_APBONLY_CSR(0x000)) + FIELD(K230_DDR_PHY_MICRO_CONT_MUX_SEL, MICRO_CONT_MUX_SEL, 0, 1) + +REG32(K230_DDR_PHY_TRAINING_STATUS, + K230_DDR_PHY_APBONLY_CSR(0x004)) + FIELD(K230_DDR_PHY_TRAINING_STATUS, MAILBOX_EMPTY, 0, 1) + +REG32(K230_DDR_PHY_TRAINING_ACK, + K230_DDR_PHY_APBONLY_CSR(0x031)) + +REG32(K230_DDR_PHY_TRAINING_MESSAGE, + K230_DDR_PHY_APBONLY_CSR(0x032)) + +REG32(K230_DDR_PHY_TRAINING_TRIGGER, + K230_DDR_PHY_APBONLY_CSR(0x099)) + +#define K230_DDR_PHY_TX_IMPEDANCE_CTRL1_RESET UINT32_C(0x0fff) + +REG32(K230_DDR_PHY_DFI_INIT_COMPLETE, + K230_DDR_PHY_MASTER_CSR(0x0f9)) + FIELD(K230_DDR_PHY_DFI_INIT_COMPLETE, DFI_INIT_COMPLETE, 0, 1) + +REG32(K230_DDR_PHY_VREF_IN_GLOBAL, + K230_DDR_PHY_MASTER_CSR(0x0b2)) +#define K230_DDR_PHY_VREF_IN_GLOBAL_RESET UINT32_C(0x0200) + +static void k230_ddr_cfg_update_state(K230DDRCfgState *s) +{ + bool dfi_complete = false; + unsigned int operating_mode = K230_DDRC_MODE_INIT; + + if (s->phy) { + dfi_complete = FIELD_EX32(s->phy->dfi_init_complete, + K230_DDR_PHY_DFI_INIT_COMPLETE, + DFI_INIT_COMPLETE); + } + + ARRAY_FIELD_DP32(s->regs, K230_DDRC_DFISTAT, + DFI_INIT_COMPLETE, dfi_complete); + ARRAY_FIELD_DP32(s->regs, K230_DDRC_SWSTAT, SW_DONE_ACK, + ARRAY_FIELD_EX32(s->regs, K230_DDRC_SWCTL, SW_DONE)); + + if (dfi_complete && + ARRAY_FIELD_EX32(s->regs, K230_DDRC_DFIMISC, + DFI_INIT_COMPLETE_EN)) { + s->initialized = true; + } + + if (s->initialized) { + operating_mode = K230_DDRC_MODE_NORMAL; + } + + ARRAY_FIELD_DP32(s->regs, K230_DDRC_STAT, + OPERATING_MODE, operating_mode); +} + +static uint64_t k230_ddr_cfg_read(void *opaque, hwaddr addr, unsigned int size) +{ + K230DDRCfgState *s = opaque; + uint64_t value = 0; + + if (addr < K230_DDRC_REG_SIZE) { + k230_ddr_cfg_update_state(s); + value = s->regs[addr / sizeof(uint32_t)]; + } + + return value; +} + +static void k230_ddr_cfg_write(void *opaque, hwaddr addr, uint64_t value, + unsigned int size) +{ + K230DDRCfgState *s = opaque; + uint32_t val = value; + + if (addr >= K230_DDRC_REG_SIZE) { + return; + } + + switch (addr) { + case A_K230_DDRC_STAT: + case A_K230_DDRC_DFISTAT: + case A_K230_DDRC_ZQSTAT: + case A_K230_DDRC_SWSTAT: + return; + case A_K230_DDRC_PWRCTL: + s->regs[R_K230_DDRC_PWRCTL] = val & MAKE_64BIT_MASK(0, 9); + break; + case A_K230_DDRC_ZQCTL2: + /* ZQ reset completes immediately and the W1S bit self-clears. */ + s->regs[R_K230_DDRC_ZQCTL2] = 0; + s->regs[R_K230_DDRC_ZQSTAT] = 0; + break; + case A_K230_DDRC_DFIMISC: + s->regs[R_K230_DDRC_DFIMISC] = + val & (MAKE_64BIT_MASK(0, 6) | MAKE_64BIT_MASK(8, 5)); + if (s->phy && s->phy->training_complete && + FIELD_EX32(val, K230_DDRC_DFIMISC, DFI_INIT_START)) { + s->phy->dfi_init_complete = FIELD_DP32( + s->phy->dfi_init_complete, + K230_DDR_PHY_DFI_INIT_COMPLETE, DFI_INIT_COMPLETE, 1); + } + break; + case A_K230_DDRC_SWCTL: + s->regs[R_K230_DDRC_SWCTL] = + val & R_K230_DDRC_SWCTL_SW_DONE_MASK; + break; + default: + s->regs[addr / sizeof(uint32_t)] = val; + break; + } + + k230_ddr_cfg_update_state(s); +} + +static bool k230_ddr_phy_decode_anib(hwaddr addr, unsigned int *anib) +{ + uint32_t csr = addr / sizeof(uint32_t); + + if ((csr & 0xfff) != 0x043) { + return false; + } + + *anib = csr >> 12; + return *anib < K230_DDR_PHY_ANIB_COUNT; +} + +static bool k230_ddr_phy_decode_dbyte(hwaddr addr, uint32_t reg, + unsigned int *dbyte, + unsigned int *nibble) +{ + uint32_t csr = addr / sizeof(uint32_t); + uint32_t block_offset; + + if (csr < 0x10000) { + return false; + } + + csr -= 0x10000; + *dbyte = csr >> 12; + if (*dbyte >= K230_DDR_PHY_DBYTE_COUNT) { + return false; + } + + block_offset = csr & 0xfff; + *nibble = block_offset >> 8; + return *nibble < K230_DDR_PHY_NIBBLES_PER_DBYTE && + (block_offset & 0xff) == reg; +} + +static uint64_t k230_ddr_phy_read(void *opaque, hwaddr addr, + unsigned int size) +{ + K230DDRPhyState *s = opaque; + unsigned int dbyte; + unsigned int nibble; + unsigned int anib; + uint64_t value = 0; + + switch (addr) { + case A_K230_DDR_PHY_MICRO_CONT_MUX_SEL: + value = s->micro_cont_mux_sel; + break; + case A_K230_DDR_PHY_TRAINING_STATUS: + value = s->mailbox_message_pending ? 0 : + R_K230_DDR_PHY_TRAINING_STATUS_MAILBOX_EMPTY_MASK; + break; + case A_K230_DDR_PHY_TRAINING_MESSAGE: + value = s->training_complete ? 0x07 : 0; + break; + case A_K230_DDR_PHY_TRAINING_ACK: + case A_K230_DDR_PHY_TRAINING_TRIGGER: + break; + case A_K230_DDR_PHY_DFI_INIT_COMPLETE: + value = s->dfi_init_complete; + break; + case A_K230_DDR_PHY_VREF_IN_GLOBAL: + value = s->vref_in_global; + break; + default: + if (k230_ddr_phy_decode_anib(addr, &anib)) { + value = s->atx_impedance[anib]; + } else if (k230_ddr_phy_decode_dbyte(addr, 0x049, + &dbyte, &nibble)) { + value = s->tx_impedance_ctrl1[dbyte][nibble]; + } else if (k230_ddr_phy_decode_dbyte(addr, 0x04d, + &dbyte, &nibble)) { + value = s->tx_odt_drv_stren[dbyte][nibble]; + } else { + qemu_log_mask(LOG_UNIMP, + "%s: unimplemented DDR PHY read at offset 0x%" + HWADDR_PRIx ", returning 0\n", + __func__, addr); + } + break; + } + + return value; +} + +static void k230_ddr_phy_write(void *opaque, hwaddr addr, uint64_t value, + unsigned int size) +{ + K230DDRPhyState *s = opaque; + uint16_t val = value; + unsigned int dbyte; + unsigned int nibble; + unsigned int anib; + + switch (addr) { + case A_K230_DDR_PHY_MICRO_CONT_MUX_SEL: + s->micro_cont_mux_sel = + val & R_K230_DDR_PHY_MICRO_CONT_MUX_SEL_MICRO_CONT_MUX_SEL_MASK; + return; + case A_K230_DDR_PHY_TRAINING_STATUS: + case A_K230_DDR_PHY_TRAINING_MESSAGE: + return; + case A_K230_DDR_PHY_TRAINING_ACK: + if (val == 0) { + s->mailbox_message_pending = false; + } + return; + case A_K230_DDR_PHY_TRAINING_TRIGGER: + if (val != 0) { + s->training_trigger_seen = true; + } else if (s->training_trigger_seen) { + /* Abstract the loaded firmware run to its final mailbox message. */ + s->training_trigger_seen = false; + s->training_complete = true; + s->mailbox_message_pending = true; + } + return; + default: + break; + } + + if (FIELD_EX32(s->micro_cont_mux_sel, + K230_DDR_PHY_MICRO_CONT_MUX_SEL, + MICRO_CONT_MUX_SEL)) { + return; + } + + switch (addr) { + case A_K230_DDR_PHY_DFI_INIT_COMPLETE: + s->dfi_init_complete = + val & R_K230_DDR_PHY_DFI_INIT_COMPLETE_DFI_INIT_COMPLETE_MASK; + return; + case A_K230_DDR_PHY_VREF_IN_GLOBAL: + s->vref_in_global = val & UINT16_C(0x7fff); + return; + default: + break; + } + + if (k230_ddr_phy_decode_anib(addr, &anib)) { + s->atx_impedance[anib] = val & UINT16_C(0x03ff); + return; + } + + if (k230_ddr_phy_decode_dbyte(addr, 0x049, &dbyte, &nibble)) { + s->tx_impedance_ctrl1[dbyte][nibble] = val & UINT16_C(0x0fff); + return; + } + + if (k230_ddr_phy_decode_dbyte(addr, 0x04d, &dbyte, &nibble)) { + s->tx_odt_drv_stren[dbyte][nibble] = val & UINT16_C(0x0fff); + return; + } + + qemu_log_mask(LOG_UNIMP, + "%s: unimplemented DDR PHY write at offset 0x%" + HWADDR_PRIx ", value 0x%" PRIx64 "\n", + __func__, addr, value); +} + +static const MemoryRegionOps k230_ddr_cfg_ops = { + .read = k230_ddr_cfg_read, + .write = k230_ddr_cfg_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static const MemoryRegionOps k230_ddr_phy_ops = { + .read = k230_ddr_phy_read, + .write = k230_ddr_phy_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void k230_ddr_cfg_init(Object *obj) +{ + K230DDRCfgState *s = K230_DDR_CFG(obj); + + memory_region_init_io(&s->mmio, obj, &k230_ddr_cfg_ops, s, + "k230-ddr-cfg", K230_DDRC_MMIO_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static void k230_ddr_phy_init(Object *obj) +{ + K230DDRPhyState *s = K230_DDR_PHY(obj); + + memory_region_init_io(&s->mmio, obj, &k230_ddr_phy_ops, s, + "k230-ddr-phy", K230_DDR_PHY_MMIO_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static void k230_ddr_cfg_reset_enter(Object *obj, ResetType type) +{ + K230DDRCfgState *s = K230_DDR_CFG(obj); + + s->initialized = false; + memcpy(s->regs, k230_ddrc_reset_values, sizeof(s->regs)); +} + +static void k230_ddr_phy_reset_enter(Object *obj, ResetType type) +{ + K230DDRPhyState *s = K230_DDR_PHY(obj); + unsigned int dbyte; + unsigned int nibble; + unsigned int anib; + + memset(s->tx_odt_drv_stren, 0, sizeof(s->tx_odt_drv_stren)); + + for (anib = 0; anib < ARRAY_SIZE(s->atx_impedance); anib++) { + s->atx_impedance[anib] = K230_DDR_PHY_ATX_IMPEDANCE_RESET; + } + + for (dbyte = 0; dbyte < ARRAY_SIZE(s->tx_impedance_ctrl1); dbyte++) { + for (nibble = 0; + nibble < ARRAY_SIZE(s->tx_impedance_ctrl1[dbyte]); nibble++) { + s->tx_impedance_ctrl1[dbyte][nibble] = + K230_DDR_PHY_TX_IMPEDANCE_CTRL1_RESET; + } + } + + s->dfi_init_complete = 0; + s->vref_in_global = K230_DDR_PHY_VREF_IN_GLOBAL_RESET; + s->micro_cont_mux_sel = 0; + s->training_trigger_seen = false; + s->training_complete = false; + s->mailbox_message_pending = false; +} + +static const VMStateDescription vmstate_k230_ddr_cfg = { + .name = "k230-ddr-cfg", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, K230DDRCfgState, K230_DDRC_REG_COUNT), + VMSTATE_BOOL(initialized, K230DDRCfgState), + VMSTATE_END_OF_LIST() + }, +}; + +static const VMStateDescription vmstate_k230_ddr_phy = { + .name = "k230-ddr-phy", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT16_ARRAY(atx_impedance, K230DDRPhyState, + K230_DDR_PHY_ANIB_COUNT), + VMSTATE_UINT16_2DARRAY(tx_impedance_ctrl1, K230DDRPhyState, + K230_DDR_PHY_DBYTE_COUNT, + K230_DDR_PHY_NIBBLES_PER_DBYTE), + VMSTATE_UINT16_2DARRAY(tx_odt_drv_stren, K230DDRPhyState, + K230_DDR_PHY_DBYTE_COUNT, + K230_DDR_PHY_NIBBLES_PER_DBYTE), + VMSTATE_UINT16(dfi_init_complete, K230DDRPhyState), + VMSTATE_UINT16(vref_in_global, K230DDRPhyState), + VMSTATE_UINT16(micro_cont_mux_sel, K230DDRPhyState), + VMSTATE_BOOL(training_trigger_seen, K230DDRPhyState), + VMSTATE_BOOL(training_complete, K230DDRPhyState), + VMSTATE_BOOL(mailbox_message_pending, K230DDRPhyState), + VMSTATE_END_OF_LIST() + }, +}; + +static void k230_ddr_cfg_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.enter = k230_ddr_cfg_reset_enter; + dc->vmsd = &vmstate_k230_ddr_cfg; +} + +static void k230_ddr_phy_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.enter = k230_ddr_phy_reset_enter; + dc->vmsd = &vmstate_k230_ddr_phy; +} + +static const TypeInfo k230_ddr_types[] = { + { + .name = TYPE_K230_DDR_CFG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(K230DDRCfgState), + .instance_init = k230_ddr_cfg_init, + .class_init = k230_ddr_cfg_class_init, + }, + { + .name = TYPE_K230_DDR_PHY, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(K230DDRPhyState), + .instance_init = k230_ddr_phy_init, + .class_init = k230_ddr_phy_class_init, + }, +}; + +DEFINE_TYPES(k230_ddr_types) diff --git a/hw/misc/k230_decomp_gzip.c b/hw/misc/k230_decomp_gzip.c new file mode 100644 index 0000000000..0d8a3b2d2e --- /dev/null +++ b/hw/misc/k230_decomp_gzip.c @@ -0,0 +1,511 @@ +/* + * Kendryte K230 GZIP decompression engine + * + * Copyright (c) 2026 Tao Ding + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Decompression accelerator is mainly used to implement hardware + * GZIP decompression function. (K230 TRM section 15) + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "hw/core/qdev-properties.h" +#include "migration/vmstate.h" +#include "system/address-spaces.h" +#include "hw/core/irq.h" +#include "hw/misc/k230_decomp_gzip.h" +#include "trace.h" + +#define GZIP_METHOD_DEFLATE 8 +#define GZIP_METHOD_VENDOR_9 9 + +#define DEFLATE_BTYPE_DYNAMIC 2 + +#define K230_DECOMP_GZIP_OUTPUT_SLOTS 4 +#define K230_DECOMP_GZIP_INPUT_SLOTS 2 + +static void k230_decomp_gzip_set_output(K230DecompGzipState *s, int line, + int level) +{ + qemu_set_irq(s->signal_out[line], level); +} + +static bool k230_decomp_gzip_read_mem(K230DecompGzipState *s, hwaddr addr, + void *buf, size_t len) +{ + MemTxResult ret = address_space_read(&address_space_memory, + s->sram_base + addr, + MEMTXATTRS_UNSPECIFIED, buf, len); + + return ret == MEMTX_OK; +} + +static bool k230_decomp_gzip_write_mem(K230DecompGzipState *s, hwaddr addr, + const void *buf, size_t len) +{ + return address_space_write(&address_space_memory, s->sram_base + addr, + MEMTXATTRS_UNSPECIFIED, buf, len) == MEMTX_OK; +} + +static hwaddr k230_decomp_gzip_output_addr(K230DecompGzipState *s) +{ + return K230_DECOMP_GZIP_SRAM_OUT_BASE + + s->output.slot * K230_DECOMP_GZIP_BLOCK_SIZE + + s->output.current_offset; +} + +static hwaddr k230_decomp_gzip_input_addr(K230DecompGzipState *s) +{ + return s->input.slot * K230_DECOMP_GZIP_BLOCK_SIZE + + K230_DECOMP_GZIP_SRAM_IN_BASE; +} + +static uint32_t k230_decomp_gzip_current_input_size(K230DecompGzipState *s) +{ + if (s->total_requested == 0) { + return 0; + } + + return ((s->total_requested - 1) % K230_DECOMP_GZIP_BLOCK_SIZE) + 1; +} + +static void k230_decomp_gzip_update_ctrl_en(K230DecompGzipState *s) +{ + int level = s->active && !!(s->gzip_src_size & K230_DECOMP_GZIP_CTRL_EN); + + k230_decomp_gzip_set_output(s, K230_DECOMP_GZIP_GPIO_DECOMP_CTRL_EN, + level); +} + +static void k230_decomp_gzip_reset_stream(K230DecompGzipState *s) +{ + if (s->zstream_inited) { + inflateEnd(&s->zs); + s->zstream_inited = false; + } + memset(&s->zs, 0, sizeof(s->zs)); +} + +static void k230_decomp_gzip_finish(K230DecompGzipState *s, bool crc_ok) +{ + s->active = false; + k230_decomp_gzip_reset_stream(s); + k230_decomp_gzip_update_ctrl_en(s); + if (crc_ok) { + s->decomp_stat |= K230_DECOMP_GZIP_STAT_CRC_OK; + } else { + s->decomp_stat &= ~K230_DECOMP_GZIP_STAT_CRC_OK; + } +} + +static bool k230_decomp_gzip_load_input(K230DecompGzipState *s, + uint32_t addr, uint32_t size) +{ + s->input.current_offset = 0; + + return k230_decomp_gzip_read_mem(s, addr, s->input_buf, size); +} + +static bool k230_decomp_gzip_request_input(K230DecompGzipState *s) +{ + uint32_t total_requested = s->total_requested; + + k230_decomp_gzip_set_output(s, K230_DECOMP_GZIP_GPIO_DMA_WRITE_REQ, 1); + if (s->total_requested == total_requested) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: dma write request not acknowledged\n", + TYPE_K230_DECOMP_GZIP); + return false; + } + return true; +} + +static bool k230_decomp_gzip_request_output(K230DecompGzipState *s) +{ + uint32_t slot = s->output.slot; + uint32_t current_offset = s->output.current_offset; + + k230_decomp_gzip_set_output(s, K230_DECOMP_GZIP_GPIO_DMA_READ_REQ, 1); + if (s->output.slot == slot && + s->output.current_offset == current_offset) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: dma read request not acknowledged\n", + TYPE_K230_DECOMP_GZIP); + return false; + } + return true; +} + +static bool k230_decomp_gzip_init_stream(K230DecompGzipState *s) +{ + uint8_t btype = (s->input_buf[10] >> 1) & 0x3; + if (btype != DEFLATE_BTYPE_DYNAMIC) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: unsupported DEFLATE BTYPE %u\n", + TYPE_K230_DECOMP_GZIP, btype); + return false; + } + + /* The compression script for K230 will modify the third byte to 0x9 */ + if (s->input_buf[2] == GZIP_METHOD_VENDOR_9) { + s->input_buf[2] = GZIP_METHOD_DEFLATE; + } + + if (inflateInit2(&s->zs, 15 + 16) != Z_OK) { + return false; + } + + s->zstream_inited = true; + return true; +} + +static bool k230_decomp_gzip_run_inflate(K230DecompGzipState *s) +{ + uint32_t output_size = s->gzip_out_size; + uint32_t current_input_size = k230_decomp_gzip_current_input_size(s); + uint32_t avail_in; + uint32_t avail_out; + uint32_t consumed; + uint32_t produced; + int ret; + + avail_in = current_input_size - s->input.current_offset; + avail_out = K230_DECOMP_GZIP_BLOCK_SIZE - s->output.current_offset; + + s->zs.next_in = s->input_buf + s->input.current_offset; + s->zs.avail_in = avail_in; + s->zs.next_out = s->output_buf; + s->zs.avail_out = avail_out; + + ret = inflate(&s->zs, Z_NO_FLUSH); + if (ret != Z_OK && ret != Z_STREAM_END && ret != Z_BUF_ERROR) { + return false; + } + + consumed = avail_in - s->zs.avail_in; + produced = avail_out - s->zs.avail_out; + + if (produced) { + if (s->total_produced + produced > output_size || + !k230_decomp_gzip_write_mem(s, k230_decomp_gzip_output_addr(s), + s->output_buf, produced)) { + return false; + } + s->output.current_offset += produced; + s->total_produced += produced; + } + + if (consumed) { + s->input.current_offset += consumed; + } + + if (ret == Z_STREAM_END) { + s->stream_end = true; + if (s->total_produced != output_size) { + return false; + } + } + + return true; +} + +static void k230_decomp_gzip_kick(K230DecompGzipState *s) +{ + if (!s->active || s->in_kick) { + return; + } + + uint32_t input_size = s->gzip_src_size & K230_DECOMP_GZIP_DMA_IN_MASK; + s->in_kick = true; + while (s->active) { + uint32_t current_input_size = k230_decomp_gzip_current_input_size(s); + /* Output is full or last block */ + if (s->output.current_offset == K230_DECOMP_GZIP_BLOCK_SIZE || + (s->stream_end && s->output.current_offset != 0)) { + if (!k230_decomp_gzip_request_output(s)) { + k230_decomp_gzip_finish(s, false); + break; + } + continue; + } + + if (!s->zstream_inited) { + if (!k230_decomp_gzip_request_input(s) || + !k230_decomp_gzip_init_stream(s)) { + k230_decomp_gzip_finish(s, false); + break; + } + continue; + } + + /* Transfer finish */ + if (s->stream_end) { + k230_decomp_gzip_finish(s, + s->total_produced == s->gzip_out_size); + break; + } + + /* Input buf has has been fully decompreed, need request more */ + if (s->input.current_offset == current_input_size) { + if (s->total_requested < input_size) { + if (!k230_decomp_gzip_request_input(s)) { + k230_decomp_gzip_finish(s, false); + break; + } + continue; + } + k230_decomp_gzip_finish(s, false); + break; + } + + /* Decompress the data, input buf or output buf are consumed in one block */ + if (!k230_decomp_gzip_run_inflate(s)) { + k230_decomp_gzip_finish(s, false); + break; + } + } + s->in_kick = false; +} + +static void k230_decomp_gzip_handle_ack(void *opaque, int n, int level) +{ + K230DecompGzipState *s = opaque; + + if (!level || !s->active) { + return; + } + + switch (n) { + case K230_DECOMP_GZIP_GPIO_DMA_WRITE_ACK: + { + uint32_t input_size = s->gzip_src_size & K230_DECOMP_GZIP_DMA_IN_MASK; + uint32_t chunk = MIN(K230_DECOMP_GZIP_BLOCK_SIZE, + input_size - s->total_requested); + + if (!k230_decomp_gzip_load_input(s, k230_decomp_gzip_input_addr(s), + chunk)) { + k230_decomp_gzip_finish(s, false); + return; + } + s->total_requested += chunk; + s->input.slot = (s->input.slot + 1) % K230_DECOMP_GZIP_INPUT_SLOTS; + break; + } + case K230_DECOMP_GZIP_GPIO_DMA_READ_ACK: + if (s->output.current_offset == 0) { + k230_decomp_gzip_finish(s, false); + return; + } + s->output.slot = (s->output.slot + 1) % K230_DECOMP_GZIP_OUTPUT_SLOTS; + s->output.current_offset = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid DMA acknowledgment signal %d\n", + TYPE_K230_DECOMP_GZIP, n); + return; + } + + k230_decomp_gzip_kick(s); +} + +static void k230_decomp_gzip_start(K230DecompGzipState *s) +{ + uint32_t input_size = s->gzip_src_size & K230_DECOMP_GZIP_DMA_IN_MASK; + + k230_decomp_gzip_reset_stream(s); + s->decomp_stat &= ~K230_DECOMP_GZIP_STAT_CRC_OK; + s->total_requested = 0; + s->total_produced = 0; + s->stream_end = false; + memset(&s->input, 0, sizeof(s->input)); + memset(&s->output, 0, sizeof(s->output)); + + if (!(s->gzip_src_size & K230_DECOMP_GZIP_CTRL_EN) || + input_size == 0 || s->gzip_out_size == 0) { + k230_decomp_gzip_finish(s, false); + return; + } + + s->active = true; + k230_decomp_gzip_update_ctrl_en(s); + k230_decomp_gzip_kick(s); +} + +static uint64_t k230_decomp_gzip_read(void *opaque, hwaddr offset, + unsigned size) +{ + K230DecompGzipState *s = opaque; + uint64_t value = 0; + + switch (offset) { + case K230_DECOMP_GZIP_DECOMP_START: + value = s->decomp_start & ~K230_DECOMP_GZIP_START; /* start bit is write only */ + break; + case K230_DECOMP_GZIP_GZIP_SRC_SIZE: + value = s->gzip_src_size; + break; + case K230_DECOMP_GZIP_GZIP_OUT_SIZE: + value = s->gzip_out_size; + break; + case K230_DECOMP_GZIP_DECOMP_STAT: + value = s->decomp_stat; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad read offset 0x%" HWADDR_PRIx "\n", + TYPE_K230_DECOMP_GZIP, offset); + break; + } + + trace_k230_decomp_gzip_read(offset, size, value); + return value; +} + +static void k230_decomp_gzip_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + K230DecompGzipState *s = opaque; + + trace_k230_decomp_gzip_write(offset, size, value); + + switch (offset) { + case K230_DECOMP_GZIP_DECOMP_START: + s->decomp_start = value; + if (value & K230_DECOMP_GZIP_START) { + k230_decomp_gzip_start(s); + } + break; + case K230_DECOMP_GZIP_GZIP_SRC_SIZE: + s->gzip_src_size = value; + k230_decomp_gzip_update_ctrl_en(s); + break; + case K230_DECOMP_GZIP_GZIP_OUT_SIZE: + s->gzip_out_size = value; + break; + case K230_DECOMP_GZIP_DECOMP_STAT: + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad write offset 0x%" HWADDR_PRIx "\n", + TYPE_K230_DECOMP_GZIP, offset); + break; + } +} + +static const MemoryRegionOps k230_decomp_gzip_ops = { + .read = k230_decomp_gzip_read, + .write = k230_decomp_gzip_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .impl.min_access_size = 4, + .impl.max_access_size = 4, +}; + +static const VMStateDescription vmstate_k230_decomp_gzip_slot = { + .name = "k230.decomp-gzip.slot", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(slot, K230DecompGzipSlotState), + VMSTATE_UINT32(current_offset, K230DecompGzipSlotState), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_k230_decomp_gzip = { + .name = "k230.decomp-gzip", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(decomp_start, K230DecompGzipState), + VMSTATE_UINT32(gzip_src_size, K230DecompGzipState), + VMSTATE_UINT32(gzip_out_size, K230DecompGzipState), + VMSTATE_UINT32(decomp_stat, K230DecompGzipState), + VMSTATE_STRUCT(input, K230DecompGzipState, 1, + vmstate_k230_decomp_gzip_slot, + K230DecompGzipSlotState), + VMSTATE_STRUCT(output, K230DecompGzipState, 1, + vmstate_k230_decomp_gzip_slot, + K230DecompGzipSlotState), + VMSTATE_UINT32(total_requested, K230DecompGzipState), + VMSTATE_UINT32(total_produced, K230DecompGzipState), + VMSTATE_BOOL(active, K230DecompGzipState), + VMSTATE_BOOL(in_kick, K230DecompGzipState), + VMSTATE_BOOL(zstream_inited, K230DecompGzipState), + VMSTATE_BOOL(stream_end, K230DecompGzipState), + VMSTATE_UINT8_ARRAY(input_buf, K230DecompGzipState, + K230_DECOMP_GZIP_BLOCK_SIZE), + VMSTATE_UINT8_ARRAY(output_buf, K230DecompGzipState, + K230_DECOMP_GZIP_BLOCK_SIZE), + VMSTATE_END_OF_LIST() + } +}; + +static void k230_decomp_gzip_reset_hold(Object *obj, ResetType type) +{ + K230DecompGzipState *s = K230_DECOMP_GZIP(obj); + + k230_decomp_gzip_reset_stream(s); + memset(&s->zs, 0, sizeof(s->zs)); + s->decomp_start = 0; + s->gzip_src_size = 0; + s->gzip_out_size = 0; + s->decomp_stat = 0; + memset(&s->input, 0, sizeof(s->input)); + memset(&s->output, 0, sizeof(s->output)); + s->total_requested = 0; + s->total_produced = 0; + s->active = false; + s->in_kick = false; + s->stream_end = false; +} + +static void k230_decomp_gzip_realize(DeviceState *dev, Error **errp) +{ + K230DecompGzipState *s = K230_DECOMP_GZIP(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, OBJECT(dev), &k230_decomp_gzip_ops, s, + TYPE_K230_DECOMP_GZIP, + K230_DECOMP_GZIP_MMIO_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + qdev_init_gpio_in(dev, k230_decomp_gzip_handle_ack, + K230_DECOMP_GZIP_NUM_GPIOS_IN); + qdev_init_gpio_out(dev, s->signal_out, + K230_DECOMP_GZIP_NUM_GPIOS_OUT); +} + +static const Property k230_decomp_gzip_properties[] = { + DEFINE_PROP_UINT64("sram-base", K230DecompGzipState, sram_base, 0), +}; + +static void k230_decomp_gzip_class_init(ObjectClass *oc, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); + + dc->realize = k230_decomp_gzip_realize; + rc->phases.hold = k230_decomp_gzip_reset_hold; + device_class_set_props(dc, k230_decomp_gzip_properties); + dc->vmsd = &vmstate_k230_decomp_gzip; + dc->desc = "Kendryte K230 GZIP decompression engine"; +} + +static const TypeInfo k230_decomp_gzip_info = { + .name = TYPE_K230_DECOMP_GZIP, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(K230DecompGzipState), + .class_init = k230_decomp_gzip_class_init, +}; + +static void k230_decomp_gzip_register_types(void) +{ + type_register_static(&k230_decomp_gzip_info); +} + +type_init(k230_decomp_gzip_register_types) diff --git a/hw/misc/macio/gpio.c b/hw/misc/macio/gpio.c index 1a7c534d65..ba001f7b20 100644 --- a/hw/misc/macio/gpio.c +++ b/hw/misc/macio/gpio.c @@ -188,7 +188,7 @@ static void macio_gpio_reset(DeviceState *dev) macio_set_gpio(s, 1, true); } -static void macio_gpio_nmi(NMIState *n, int cpu_index, Error **errp) +static void macio_gpio_nmi(NMIState *n) { macio_set_gpio(MACIO_GPIO(n), 9, true); macio_set_gpio(MACIO_GPIO(n), 9, false); @@ -201,7 +201,7 @@ static void macio_gpio_class_init(ObjectClass *oc, const void *data) device_class_set_legacy_reset(dc, macio_gpio_reset); dc->vmsd = &vmstate_macio_gpio; - nc->nmi_monitor_handler = macio_gpio_nmi; + nc->raise_nmi = macio_gpio_nmi; } static const TypeInfo macio_gpio_init_info = { diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 23265f6035..54e07aacda 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -1,4 +1,5 @@ system_ss.add(when: 'CONFIG_APPLESMC', if_true: files('applesmc.c')) + system_ss.add(when: 'CONFIG_EDU', if_true: files('edu.c')) system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('vmcoreinfo.c')) system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c')) @@ -28,6 +29,7 @@ system_ss.add(when: 'CONFIG_IOSB', if_true: files('iosb.c')) system_ss.add(when: 'CONFIG_VIRT_CTRL', if_true: files('virt_ctrl.c')) # RISC-V devices +system_ss.add(when: 'CONFIG_K230', if_true: files('k230_ddr.c')) system_ss.add(when: 'CONFIG_MCHP_PFSOC_DMC', if_true: files('mchp_pfsoc_dmc.c')) system_ss.add(when: 'CONFIG_MCHP_PFSOC_IOSCB', if_true: files('mchp_pfsoc_ioscb.c')) system_ss.add(when: 'CONFIG_MCHP_PFSOC_SYSREG', if_true: files('mchp_pfsoc_sysreg.c')) @@ -36,6 +38,7 @@ system_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true: files('sifive_e_prci.c')) system_ss.add(when: 'CONFIG_SIFIVE_E_AON', if_true: files('sifive_e_aon.c')) system_ss.add(when: 'CONFIG_SIFIVE_U_OTP', if_true: files('sifive_u_otp.c')) system_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true: files('sifive_u_prci.c')) +system_ss.add(when: 'CONFIG_K230_DECOMP_GZIP', if_true: files('k230_decomp_gzip.c')) subdir('macio') @@ -163,8 +166,13 @@ specific_ss.add(when: 'CONFIG_MIPS_ITU', if_true: files('mips_itu.c')) specific_ss.add(when: 'CONFIG_RISCV_MIPS_CMGCR', if_true: files('riscv_cmgcr.c')) specific_ss.add(when: 'CONFIG_RISCV_MIPS_CPC', if_true: files('riscv_cpc.c')) +if igvm.found() + specific_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('vmlaunchupdate.c')) +endif system_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa_ec.c')) # HPPA devices system_ss.add(when: 'CONFIG_LASI', if_true: files('lasi.c')) + +system_ss.add(when: 'CONFIG_AXIADO_CLK', if_true: files('axiado_clk.c')) diff --git a/hw/misc/mps2-scc.c b/hw/misc/mps2-scc.c index 7877b31479..554b504c72 100644 --- a/hw/misc/mps2-scc.c +++ b/hw/misc/mps2-scc.c @@ -57,7 +57,7 @@ REG32(ID, 0xFFC) static int scc_partno(MPS2SCC *s) { /* Return the partno field of the SCC_ID (0x524, 0x511, etc) */ - return extract32(s->id, 4, 8); + return extract32(s->id, 4, 12); } /* Is CFG_REG2 present? */ @@ -299,7 +299,7 @@ static void mps2_scc_write(void *opaque, hwaddr offset, uint64_t value, goto bad_offset; } /* AN536: Core 1 vector table base address */ - s->cfg6 = value; + s->cfg7 = value; break; case A_CFGDATA_OUT: s->cfgdata_out = value; diff --git a/hw/misc/trace-events b/hw/misc/trace-events index c9a868b3ef..0b8be3d0f2 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -442,3 +442,13 @@ iommu_testdev_dma_read(uint64_t gva, uint32_t len) "gva=0x%" PRIx64 " len=%u" iommu_testdev_dma_verify(uint32_t expected, uint32_t actual) "expected=0x%x actual=0x%x" iommu_testdev_dma_result(uint32_t result) "DMA completed result=0x%x" iommu_testdev_dma_armed(bool armed) "armed=%d" + +# k230_decomp_gzip.c +k230_decomp_gzip_read(uint64_t offset, unsigned int size, uint64_t value) "K230 DECOMP GZIP read: [0x%"PRIx64"] size %u -> 0x%"PRIx64 +k230_decomp_gzip_write(uint64_t offset, unsigned int size, uint64_t value) "K230 DECOMP GZIP write: [0x%"PRIx64"] size %u <- 0x%"PRIx64 + +# vmlaunchupdate.c +launch_update_write(void) "" +vmlaunch_reset_enter(void) "" +vm_launchupdate_finalize(void) "" +restore_host_x86_igvm(void) "" diff --git a/hw/misc/vmlaunchupdate.c b/hw/misc/vmlaunchupdate.c new file mode 100644 index 0000000000..afa2d278ae --- /dev/null +++ b/hw/misc/vmlaunchupdate.c @@ -0,0 +1,333 @@ +/* + * Guest driven VM launch component update (using IGVM) device + * For details and specification, please look at docs/specs/vmlaunchupdate.rst. + * + * Copyright (C) 2026 Red Hat, Inc. + * + * Authors: Ani Sinha + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "system/physmem.h" +#include "system/reset.h" +#include "qemu/target-info-qapi.h" +#include "hw/nvram/fw_cfg.h" +#include "hw/core/qdev-properties.h" +#include "hw/i386/pc.h" +#include "exec/cpu-common.h" +#include "hw/misc/vmlaunchupdate.h" +#include "system/igvm.h" +#include "system/igvm-internal.h" +#include "qemu/error-report.h" +#include "trace.h" + +/* returns NULL unless there is exactly one device */ +static VMLaunchUpdateState *vm_launchupdate_find(void) +{ + Object *o = object_resolve_path_type("", TYPE_VMLAUNCHUPDATE, NULL); + + return o ? VMLAUNCHUPDATE(o) : NULL; +} + +static bool vmlaunchupdate_supported(void) +{ + return target_arch() == SYS_EMU_TARGET_X86_64; +} + +static void init_vm_launch_update(VMLaunchUpdateState *s) +{ + s->launch_update.capabilities = VM_LAUNCHUPDATE_FORMAT_IGVM; + s->launch_update.control = 0; + + if (s->disabled) { + s->launch_update.control |= VM_LAUNCHUPDATE_CTL_DISABLE; + } + + s->launch_update.version = VM_LAUNCHUPDATE_VERSION; + return; +} + +static void clear_init_vm_launch_update(VMLaunchUpdateState *s) +{ + memset(&s->launch_update, 0, sizeof(s->launch_update)); + init_vm_launch_update(s); +} + +static bool no_igvmcfg(X86MachineState *x86m) +{ + IgvmCfg *igvmc; + + if (!x86m) { + return true; + } + + igvmc = x86m->igvm; + + if (!igvmc) { + /* The VM was not started with an IGVM, bail */ + info_report("guest was not initially started with IGVM, " + "not changing launch state."); + return true; + } + return false; +} + +static int process_x86_igvm(VMLaunchUpdateState *s, + uint64_t fw_image_addr, uint64_t fw_image_size) +{ + X86MachineState *x86machine = X86_MACHINE(qdev_get_machine()); + IgvmCfg *igvmc = x86machine->igvm; + IgvmHandle igvm; + void *image_addr_ptr; + hwaddr len; + + if (no_igvmcfg(x86machine)) { + return -2; + } + + if (!fw_image_addr || !fw_image_size) { + return -1; + } + + len = (hwaddr) fw_image_size; + image_addr_ptr = physical_memory_map((hwaddr) fw_image_addr, + (hwaddr *) &len, 0); + + if (!image_addr_ptr || (len < fw_image_size)) { + warn_report("vmlaunchupdate: Invalid guest addresses."); + goto err; + } + + igvm = igvm_new_from_binary(image_addr_ptr, fw_image_size); + if (igvm < 0) { + warn_report("vmlaunchupdate: Unable to parse IGVM file %" + PRIx64 ": %" PRIx64, fw_image_addr, fw_image_size); + goto err; + } + + /* free previous file context */ + if (igvmc->file >= 0) { + igvm_free(igvmc->file); + } + /* set new context */ + igvmc->file = igvm; + + physical_memory_unmap(image_addr_ptr, len, 0, 0); + info_report("vmlaunchupdate: new IGVM context set."); + + return 0; + err: + if (image_addr_ptr) { + physical_memory_unmap(image_addr_ptr, len, 0, 0); + } + return -1; +} + +static void restore_host_x86_igvm(void) +{ + X86MachineState *x86machine = X86_MACHINE(qdev_get_machine()); + IgvmCfg *igvmc = x86machine->igvm; + Error *errp = NULL; + + if (no_igvmcfg(x86machine)) { + return; + } + + /* free previous file context */ + if (igvmc->file >= 0) { + igvm_free(igvmc->file); + } + + info_report("restoring original host IGVM: %s", igvmc->filename); + igvmc->file = qigvm_file_init(igvmc->filename, &errp); + assert(!errp); + + info_report("vmlaunchupdate: host IGVM context set."); + + trace_restore_host_x86_igvm(); + + return; +} + +static bool fw_address_cleared(VMLaunchUpdateState *s) +{ + return !s->launch_update.fw_image_addr && + !s->launch_update.fw_image_size; +} + +static void launch_update_write(void *dev, off_t offset, size_t len) +{ + VMLaunchUpdateState *s = VMLAUNCHUPDATE(dev); + uint64_t addr; + uint64_t size; + int rc; + + s->launch_update.status = VM_LAUNCHUPDATE_SUCCESS; + + if (s->disabled) { + goto end; + } + + if (s->launch_update.control & VM_LAUNCHUPDATE_CTL_DISABLE) { + s->disabled = true; + goto end; + } + + if (fw_address_cleared(s) && + (s->launch_update.control & VM_LAUNCHUPDATE_CTL_HOST_IGVM)) { + /* restore host IGVM on immediate next reset */ + s->host_igvm_on_reset = true; + goto end; + } + + if (!(s->launch_update.control & VM_LAUNCHUPDATE_FORMAT_IGVM) && + !fw_address_cleared(s)) { + /* at least one address provided but the format is not IGVM */ + s->launch_update.status = VM_LAUNCHUPDATE_LOAD_FAIL; + goto end; + } + + /* process guest provided IGVM image */ + if (s->launch_update.control & VM_LAUNCHUPDATE_FORMAT_IGVM) { + if (target_arch() == SYS_EMU_TARGET_X86_64) { + addr = le64_to_cpu(s->launch_update.fw_image_addr); + size = le64_to_cpu(s->launch_update.fw_image_size); + rc = process_x86_igvm(s, addr, size); + if (rc < 0) { + switch (rc) { + case -2: + s->launch_update.status = VM_LAUNCHUPDATE_NOT_IGVM_INIT; + break; + default: + s->launch_update.status = VM_LAUNCHUPDATE_LOAD_FAIL; + } + goto end; + } + } + /* process other machines here when support is added */ + } + + /* clear the addresses */ + s->launch_update.fw_image_addr = 0x0; + s->launch_update.fw_image_size = 0x0; + + end: + trace_launch_update_write(); + return; +} + +static void launch_update_select(void *dev) +{ + VMLaunchUpdateState *s = VMLAUNCHUPDATE(dev); + init_vm_launch_update(s); +} + +static void vmlaunch_reset_enter(Object *obj, ResetType type) +{ + VMLaunchUpdateState *s = VMLAUNCHUPDATE(obj); + + if (target_arch() != SYS_EMU_TARGET_X86_64) { + return; + } + + if (s->host_igvm_on_reset) { + restore_host_x86_igvm(); + s->host_igvm_on_reset = false; + /* restoring host igvm enables the interface again */ + s->disabled = false; + /* clear the host IGVM ctrl bit */ + s->launch_update.control &= ~VM_LAUNCHUPDATE_CTL_HOST_IGVM; + } + + if ((s->launch_update.control & VM_LAUNCHUPDATE_CTL_HOST_IGVM) && + (s->launch_update.status == VM_LAUNCHUPDATE_SUCCESS)) { + info_report("vmlaunchupdate: next reset will use host igvm"); + s->host_igvm_on_reset = true; + } + + trace_vmlaunch_reset_enter(); +} + +static ResettableState *vmlaunch_reset_state(Object *obj) +{ + VMLaunchUpdateState *s = VMLAUNCHUPDATE(obj); + + return &s->reset_state; +} + +static void vm_launchupdate_realize(DeviceState *dev, Error **errp) +{ + VMLaunchUpdateState *s = VMLAUNCHUPDATE(dev); + FWCfgState *fw_cfg = fw_cfg_find(); + + /* multiple devices are not supported */ + if (!vm_launchupdate_find()) { + error_setg(errp, "at most one %s device is permitted", + TYPE_VMLAUNCHUPDATE); + return; + } + + /* if current machine is not supported, do not initialize */ + if (!vmlaunchupdate_supported()) { + error_setg(errp, + "This machine does not support vm-launch-update device"); + return; + } + + /* fw_cfg with DMA support is necessary to support this device */ + if (!fw_cfg || !fw_cfg_dma_enabled(fw_cfg)) { + error_setg(errp, "%s device requires fw_cfg", + TYPE_VMLAUNCHUPDATE); + return; + } + + fw_cfg_add_file_callback(fw_cfg, FILE_VMLAUNCHUPDATE, + launch_update_select, launch_update_write, s, + &s->launch_update, + sizeof(s->launch_update), + false); + + clear_init_vm_launch_update(s); + /* + * This device requires to register a global reset because it is + * not plugged to a bus (which, as its QOM parent, would reset it). + */ + qemu_register_resettable(OBJECT(s)); +} + +static void vm_launchupdate_finalize(Object *obj) +{ + qemu_unregister_resettable(obj); + trace_vm_launchupdate_finalize(); +} + +static void vmlaunchupdate_device_class_init(ObjectClass *klass, + const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + /* we are not interested in migration - so no need to populate dc->vmsd */ + dc->desc = "VM launch state update device"; + dc->realize = vm_launchupdate_realize; + dc->hotpluggable = false; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + rc->phases.enter = vmlaunch_reset_enter; + rc->get_state = vmlaunch_reset_state; +} + +static const TypeInfo vmlaunchupdate_device_types[] = { + { + .name = TYPE_VMLAUNCHUPDATE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(VMLaunchUpdateState), + .class_init = vmlaunchupdate_device_class_init, + .instance_finalize = vm_launchupdate_finalize, + }, +}; + +DEFINE_TYPES(vmlaunchupdate_device_types) diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index b568fa3392..39e3620ef5 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -1469,6 +1469,8 @@ static void gem_reset(DeviceState *d) /* Set post reset register values */ memset(&s->regs[0], 0, sizeof(s->regs)); + memset(&s->rx_desc_addr[0], 0, sizeof(s->rx_desc_addr)); + memset(&s->tx_desc_addr[0], 0, sizeof(s->tx_desc_addr)); s->regs[R_NWCFG] = 0x00080000; s->regs[R_NWSTATUS] = 0x00000006; s->regs[R_DMACFG] = 0x00020784; @@ -1593,9 +1595,22 @@ static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size) offset >>= 2; retval = s->regs[offset]; - DB_PRINT("offset: 0x%04x read: 0x%08x\n", (unsigned)offset*4, retval); + DB_PRINT("offset: 0x%04x read: 0x%08x\n", (unsigned)offset * 4, + retval); switch (offset) { + case R_RXQBASE: + retval = s->rx_desc_addr[0]; + break; + case R_TXQBASE: + retval = s->tx_desc_addr[0]; + break; + case R_TRANSMIT_Q1_PTR ... R_TRANSMIT_Q7_PTR: + retval = s->tx_desc_addr[offset - R_TRANSMIT_Q1_PTR + 1]; + break; + case R_RECEIVE_Q1_PTR ... R_RECEIVE_Q7_PTR: + retval = s->rx_desc_addr[offset - R_RECEIVE_Q1_PTR + 1]; + break; case R_ISR: DB_PRINT("lowering irqs on ISR read\n"); /* The interrupts get updated at the end of the function. */ diff --git a/hw/net/can/flexcan.c b/hw/net/can/flexcan.c index da36d10bd3..a8bad83cb8 100644 --- a/hw/net/can/flexcan.c +++ b/hw/net/can/flexcan.c @@ -106,7 +106,11 @@ static const FlexcanRegs flexcan_regs_write_mask = { .gfwr_mx6 = 0xFFFFFFFF, ._reserved6 = {0}, ._reserved8 = {0}, - .rx_smb0_raw = {0, 0, 0, 0}, + .rx_smb0 = { + .can_ctrl = 0, + .can_id = 0, + .data = { 0, 0 }, + }, .rx_smb1 = {0, 0, 0, 0}, }; static const FlexcanRegs flexcan_regs_reset_mask = { @@ -134,14 +138,22 @@ static const FlexcanRegs flexcan_regs_reset_mask = { ._reserved2 = 0, .dbg1 = 0, .dbg2 = 0, - .mb = {0xFFFFFFFF}, + .mbs = { [0 ... FLEXCAN_MAILBOX_COUNT - 1] = { + .can_ctrl = 0xFFFFFFFF, + .can_id = 0xFFFFFFFF, + .data = { 0xFFFFFFFF, 0xFFFFFFFF }, + } }, ._reserved4 = {0}, - .rximr = {0xFFFFFFFF}, + .rximr = { [0 ... 63] = 0xFFFFFFFF }, ._reserved5 = {0}, .gfwr_mx6 = 0, ._reserved6 = {0}, ._reserved8 = {0}, - .rx_smb0_raw = {0, 0, 0, 0}, + .rx_smb0 = { + .can_ctrl = 0, + .can_id = 0, + .data = { 0, 0 }, + }, .rx_smb1 = {0, 0, 0, 0}, }; @@ -879,22 +891,22 @@ static bool flexcan_can_receive(CanBusClientState *client) */ static void flexcan_fifo_pop(FlexcanState *s) { - if (s->regs.fifo.mb_back.can_ctrl != 0) { + if (s->regs.mbs[0].can_ctrl != 0) { /* move queue elements forward */ - memmove(&s->regs.fifo.mb_back, &s->regs.fifo.mbs_queue[0], - sizeof(s->regs.fifo.mbs_queue)); + memmove(&s->regs.mbs[0], &s->regs.mbs[1], + sizeof(s->regs.mbs[0]) * (FLEXCAN_FIFO_DEPTH - 1)); /* clear the first-in slot */ memset(&s->regs.mbs[FLEXCAN_FIFO_DEPTH - 1], 0, sizeof(FlexcanRegsMessageBuffer)); trace_flexcan_fifo_pop(DEVICE(s)->canonical_path, 1, - s->regs.fifo.mb_back.can_ctrl != 0); + s->regs.mbs[0].can_ctrl != 0); } else { trace_flexcan_fifo_pop(DEVICE(s)->canonical_path, 0, 0); } - if (s->regs.fifo.mb_back.can_ctrl != 0) { + if (s->regs.mbs[0].can_ctrl != 0) { flexcan_irq_iflag_set(s, I_FIFO_AVAILABLE); } else { flexcan_irq_iflag_clear(s, I_FIFO_AVAILABLE); @@ -1075,7 +1087,7 @@ static enum FlexcanRx flexcan_mb_rx(FlexcanState *s, const qemu_can_frame *buf) } } - if (last_not_free_to_receive_mbid >= -1) { + if (last_not_free_to_receive_mbid >= 0) { if (last_not_free_to_receive_locked) { /* * copy to temporary mailbox (SMB) @@ -1151,6 +1163,8 @@ static void flexcan_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { FlexcanState *s = opaque; + const int mbid = (addr - offsetof(FlexcanRegs, mbs)) / + sizeof(s->regs.mbs[0]); uint32_t write_mask = ((const uint32_t *) &flexcan_regs_write_mask)[addr / 4]; uint32_t old_value = s->regs_raw[addr / 4]; @@ -1208,11 +1222,8 @@ static void flexcan_mem_write(void *opaque, hwaddr addr, uint64_t val, default: s->regs_raw[addr / 4] = (val & write_mask) | (old_value & ~write_mask); - if (addr >= offsetof(FlexcanRegs, mb) && - addr < offsetof(FlexcanRegs, _reserved4)) { + if (0 <= mbid && mbid < ARRAY_SIZE(s->regs.mbs)) { /* access to mailbox */ - int mbid = (addr - offsetof(FlexcanRegs, mb)) / - sizeof(FlexcanRegsMessageBuffer); if (s->locked_mbidx == mbid) { flexcan_mb_unlock(s); @@ -1240,14 +1251,12 @@ static void flexcan_mem_write(void *opaque, hwaddr addr, uint64_t val, static uint64_t flexcan_mem_read(void *opqaue, hwaddr addr, unsigned size) { FlexcanState *s = opqaue; + const int mbid = (addr - offsetof(FlexcanRegs, mbs)) / + sizeof(s->regs.mbs[0]); uint32_t rv = s->regs_raw[addr >> 2]; - if (addr >= offsetof(FlexcanRegs, mb) && - addr < offsetof(FlexcanRegs, _reserved4)) { + if (0 <= mbid && mbid < ARRAY_SIZE(s->regs.mbs)) { /* reading from mailbox */ - hwaddr offset = addr - offsetof(FlexcanRegs, mb); - int mbid = offset / sizeof(FlexcanRegsMessageBuffer); - if (addr % 16 == 0 && s->locked_mbidx != mbid) { /* reading control word locks the mailbox */ flexcan_mb_unlock(s); diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c index 46e156a5dd..b87a9f167a 100644 --- a/hw/net/e1000e_core.c +++ b/hw/net/e1000e_core.c @@ -1948,6 +1948,16 @@ e1000e_calc_rxdesclen(E1000ECore *core) trace_e1000e_rx_desc_len(core->rx_desc_len); } +static void +e1000e_calc_rxconf(E1000ECore *core) +{ + e1000e_parse_rxbufsize(core); + e1000e_calc_rxdesclen(core); + core->rxbuf_min_shift = + ((core->mac[RCTL] / E1000_RCTL_RDMTS_QUAT) & 3) + 1 + + E1000_RING_DESC_LEN_SHIFT; +} + static void e1000e_set_rx_control(E1000ECore *core, int index, uint32_t val) { @@ -1955,11 +1965,7 @@ e1000e_set_rx_control(E1000ECore *core, int index, uint32_t val) trace_e1000e_rx_set_rctl(core->mac[RCTL]); if (val & E1000_RCTL_EN) { - e1000e_parse_rxbufsize(core); - e1000e_calc_rxdesclen(core); - core->rxbuf_min_shift = ((val / E1000_RCTL_RDMTS_QUAT) & 3) + 1 + - E1000_RING_DESC_LEN_SHIFT; - + e1000e_calc_rxconf(core); e1000e_start_recv(core); } } @@ -3557,5 +3563,7 @@ e1000e_core_post_load(E1000ECore *core) e1000e_intrmgr_resume(core); e1000e_autoneg_resume(core); + e1000e_calc_rxconf(core); + return 0; } diff --git a/hw/net/igb_core.c b/hw/net/igb_core.c index 45d8fd795b..2a48839073 100644 --- a/hw/net/igb_core.c +++ b/hw/net/igb_core.c @@ -4548,5 +4548,7 @@ igb_core_post_load(IGBCore *core) igb_intrmgr_resume(core); igb_autoneg_resume(core); + igb_calc_rxdesclen(core); + return 0; } diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index 424af73a18..16479284ee 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -778,7 +778,6 @@ static void rtl8139_write_buffer(RTL8139State *s, const void *buf, int size) s->RxBufAddr += size; } -#define MIN_BUF_SIZE 60 static inline dma_addr_t rtl8139_addr64(uint32_t low, uint32_t high) { return low | ((uint64_t)high << 32); @@ -1007,10 +1006,6 @@ static ssize_t rtl8139_receive(NetClientState *nc, lduw_be_p(&buf[ETH_ALEN * 2]) == ETH_P_VLAN) { dot1q_buf = &buf[ETH_ALEN * 2]; size -= VLAN_HLEN; - /* if too small buffer, use the tailroom added duing expansion */ - if (size < MIN_BUF_SIZE) { - size = MIN_BUF_SIZE; - } rxdw1 &= ~CP_RX_VLAN_TAG_MASK; /* BE + ~le_to_cpu()~ + cpu_to_le() = BE */ @@ -1770,6 +1765,7 @@ static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size, buf2 = g_malloc(buf2_size); iov_to_buf(iov, 3, 0, buf2, buf2_size); buf = buf2; + size = buf2_size; } DPRINTF("+++ transmit loopback mode\n"); diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index f0e3beb290..814b99a43d 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -1744,10 +1744,21 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size) if (n->promisc) return 1; + if (size < n->host_hdr_len + 14) { + /* Truncated ethernet packet */ + return 0; + } + ptr += n->host_hdr_len; if (!memcmp(&ptr[12], vlan, sizeof(vlan))) { - int vid = lduw_be_p(ptr + 14) & 0xfff; + int vid; + + /* Truncated vlan packet */ + if (size < n->host_hdr_len + 16) { + return 0; + } + vid = lduw_be_p(ptr + 14) & 0xfff; if (!(n->vlans[vid >> 5] & (1U << (vid & 0x1f)))) return 0; } @@ -2674,6 +2685,13 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, { VirtIONet *n = qemu_get_nic_opaque(nc); if ((n->rsc4_enabled || n->rsc6_enabled)) { + /* this never happens with existing backends, but just in case. */ + if (n->host_hdr_len != n->guest_hdr_len) { + warn_report_once("virtio-net: host_hdr_len %zu != guest_hdr_len %zu, " + "skipping RSC", + n->host_hdr_len, n->guest_hdr_len); + return virtio_net_do_receive(nc, buf, size); + } return virtio_net_rsc_receive(nc, buf, size); } else { return virtio_net_do_receive(nc, buf, size); @@ -2991,8 +3009,9 @@ static void virtio_net_add_queue(VirtIONet *n, int index) n->vqs[index].tx_vq = virtio_add_queue(vdev, n->net_conf.tx_queue_size, virtio_net_handle_tx_bh); - n->vqs[index].tx_bh = qemu_bh_new_guarded(virtio_net_tx_bh, &n->vqs[index], - &DEVICE(vdev)->mem_reentrancy_guard); + n->vqs[index].tx_bh = virtio_bh_new_guarded(DEVICE(vdev), + virtio_net_tx_bh, + &n->vqs[index]); } n->vqs[index].tx_waiting = 0; diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 8569484b2f..24c551a054 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -1336,32 +1336,46 @@ static bool vmxnet3_verify_intx(VMXNET3State *s, int intx) || intx == pci_get_byte(s->parent_obj.config + PCI_INTERRUPT_PIN) - 1; } -static void vmxnet3_validate_interrupt_idx(bool is_msix, int idx) +static bool vmxnet3_validate_irq_idx(const char *type, bool is_msix, int idx) { int max_ints = is_msix ? VMXNET3_MAX_INTRS : VMXNET3_MAX_NMSIX_INTRS; + if (idx >= max_ints) { - hw_error("Bad interrupt index: %d\n", idx); + qemu_log_mask(LOG_GUEST_ERROR, + "vmxnet3: Bad %s queue interrupt index: %d\n", + type, idx); + return false; } + + return true; } -static void vmxnet3_validate_interrupts(VMXNET3State *s) +static bool vmxnet3_validate_interrupts(VMXNET3State *s) { int i; VMW_CFPRN("Verifying event interrupt index (%d)", s->event_int_idx); - vmxnet3_validate_interrupt_idx(s->msix_used, s->event_int_idx); + if (!vmxnet3_validate_irq_idx("event", s->msix_used, s->event_int_idx)) { + return false; + } for (i = 0; i < s->txq_num; i++) { int idx = s->txq_descr[i].intr_idx; VMW_CFPRN("Verifying TX queue %d interrupt index (%d)", i, idx); - vmxnet3_validate_interrupt_idx(s->msix_used, idx); + if (!vmxnet3_validate_irq_idx("TX", s->msix_used, idx)) { + return false; + } } for (i = 0; i < s->rxq_num; i++) { int idx = s->rxq_descr[i].intr_idx; VMW_CFPRN("Verifying RX queue %d interrupt index (%d)", i, idx); - vmxnet3_validate_interrupt_idx(s->msix_used, idx); + if (!vmxnet3_validate_irq_idx("RX", s->msix_used, idx)) { + return false; + } } + + return true; } static bool vmxnet3_validate_queues(VMXNET3State *s) @@ -1554,7 +1568,9 @@ static void vmxnet3_activate_device(VMXNET3State *s) sizeof(s->rxq_descr[i].rxq_stats)); } - vmxnet3_validate_interrupts(s); + if (!vmxnet3_validate_interrupts(s)) { + return; + } /* Make sure everything is in place before device activation */ smp_wmb(); @@ -2392,7 +2408,9 @@ static int vmxnet3_post_load(void *opaque, int version_id) if (!vmxnet3_validate_queues(s)) { return -1; } - vmxnet3_validate_interrupts(s); + if (!vmxnet3_validate_interrupts(s)) { + return -1; + } return 0; } diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c index 970732b162..d35f4a847d 100644 --- a/hw/net/xilinx_axienet.c +++ b/hw/net/xilinx_axienet.c @@ -103,6 +103,9 @@ static unsigned int tdk_read(struct PHY *phy, unsigned int req) case 17: /* Marvell PHY on many xilinx boards. */ r = 0x8000; /* 1000Mb */ + if (phy->link) { + r |= 0x0400; /* Link is up */ + } break; case 18: { @@ -919,20 +922,27 @@ xilinx_axienet_data_stream_push(StreamSink *obj, uint8_t *buf, size_t size, if (s->hdr[0] & 1) { unsigned int start_off = s->hdr[1] >> 16; unsigned int write_off = s->hdr[1] & 0xffff; - uint32_t tmp_csum; - uint16_t csum; - tmp_csum = net_checksum_add(s->txpos - start_off, - buf + start_off); - /* Accumulate the seed. */ - tmp_csum += s->hdr[2] & 0xffff; + if (start_off > s->txpos || write_off + 2 > s->txpos) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: offsets outside packet, skipping checksum\n", + TYPE_XILINX_AXI_ENET); + } else { + uint32_t tmp_csum; + uint16_t csum; - /* Fold the 32bit partial checksum. */ - csum = net_checksum_finish(tmp_csum); + tmp_csum = net_checksum_add(s->txpos - start_off, + buf + start_off); + /* Accumulate the seed. */ + tmp_csum += s->hdr[2] & 0xffff; - /* Writeback. */ - buf[write_off] = csum >> 8; - buf[write_off + 1] = csum & 0xff; + /* Fold the 32bit partial checksum. */ + csum = net_checksum_finish(tmp_csum); + + /* Writeback. */ + buf[write_off] = csum >> 8; + buf[write_off + 1] = csum & 0xff; + } } qemu_send_packet(qemu_get_queue(s->nic), buf, s->txpos); diff --git a/hw/nvme/Kconfig b/hw/nvme/Kconfig index cfa2ab0f9d..cfeeed64cd 100644 --- a/hw/nvme/Kconfig +++ b/hw/nvme/Kconfig @@ -2,3 +2,4 @@ config NVME_PCI bool default y if PCI_DEVICES || PCIE_DEVICES depends on PCI + select SPDM_SOCKET diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index a67e159889..4893cf7e74 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -196,6 +196,7 @@ */ #include "qemu/osdep.h" +#include "qemu/bitops.h" #include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/log.h" @@ -2810,6 +2811,7 @@ static void nvme_copy_done(NvmeCopyAIOCB *iocb) qemu_iovec_destroy(&iocb->iov); g_free(iocb->bounce); + g_free(iocb->ranges); if (iocb->ret < 0) { block_acct_failed(stats, &iocb->acct.read); @@ -3210,7 +3212,7 @@ static void nvme_do_copy(NvmeCopyAIOCB *iocb) uint16_t prinfow = ((copy->control[2] >> 2) & 0xf); uint64_t slba; uint32_t nlb; - size_t len; + size_t len, blen; uint16_t status; uint32_t dnsid = le32_to_cpu(req->cmd.nsid); uint32_t snsid = dnsid; @@ -3331,10 +3333,13 @@ static void nvme_do_copy(NvmeCopyAIOCB *iocb) } g_free(iocb->bounce); - iocb->bounce = g_malloc_n(le16_to_cpu(sns->id_ns.mssrl), - sns->lbasz + sns->lbaf.ms); + assert(g_size_checked_mul(&blen, le16_to_cpu(sns->id_ns.mssrl), + sns->lbasz + MAX(sns->lbaf.ms, dns->lbaf.ms))); + + iocb->bounce = g_malloc(blen); qemu_iovec_reset(&iocb->iov); + assert(len <= blen); qemu_iovec_add(&iocb->iov, iocb->bounce, len); block_acct_start(blk_get_stats(sns->blkconf.blk), &iocb->acct.read, 0, @@ -4822,6 +4827,26 @@ static int nvme_init_sq_ioeventfd(NvmeSQueue *sq) return 0; } +/* + * A pending Async Event Request has no aiocb (nvme_aer() parks it without + * issuing any block I/O), so there is nothing to cancel; just drop it. + */ +static void nvme_sq_cancel_inflight(NvmeSQueue *sq, uint16_t status) +{ + NvmeRequest *r; + + while (!QTAILQ_EMPTY(&sq->out_req_list)) { + r = QTAILQ_FIRST(&sq->out_req_list); + r->status = status; + + if (r->aiocb) { + blk_aio_cancel(r->aiocb); + } else { + QTAILQ_REMOVE(&sq->out_req_list, r, entry); + } + } +} + static void nvme_free_sq(NvmeSQueue *sq, NvmeCtrl *n) { uint16_t offset = sq->sqid << 3; @@ -4856,14 +4881,7 @@ static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeRequest *req) trace_pci_nvme_del_sq(qid); sq = n->sq[qid]; - while (!QTAILQ_EMPTY(&sq->out_req_list)) { - r = QTAILQ_FIRST(&sq->out_req_list); - assert(r->aiocb); - r->status = NVME_CMD_ABORT_SQ_DEL; - blk_aio_cancel(r->aiocb); - } - - assert(QTAILQ_EMPTY(&sq->out_req_list)); + nvme_sq_cancel_inflight(sq, NVME_CMD_ABORT_SQ_DEL); if (!nvme_check_cqid(n, sq->cqid)) { cq = n->cq[sq->cqid]; @@ -6622,7 +6640,8 @@ static uint16_t nvme_set_feature_fdp_events(NvmeCtrl *n, NvmeNamespace *ns, if (!shift && event_type) { continue; } - event_mask |= (1 << nvme_fdp_evf_shifts[events[i]]); + event_mask = + deposit64(event_mask, nvme_fdp_evf_shifts[events[i]], 1, 1); } if (enable) { @@ -8022,6 +8041,18 @@ static void nvme_ctrl_reset(NvmeCtrl *n, NvmeResetType rst) nvme_ns_drain(ns); } + /* + * Cancel and wait out every inflight command on every queue first. A + * reset is not required to be preceded by the guest's graceful + * Delete I/O SQ/CQ sequence, so sq/cq must not be freed below while a + * blk_aio_* completion for them could still be in flight. + */ + for (i = 0; i < n->num_queues; i++) { + if (n->sq[i] != NULL) { + nvme_sq_cancel_inflight(n->sq[i], NVME_CMD_ABORT_SQ_DEL); + } + } + for (i = 0; i < n->num_queues; i++) { if (n->sq[i] != NULL) { nvme_free_sq(n->sq[i], n); @@ -9352,22 +9383,11 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) } } -#define BLOCKER_FEATURES_MAX_LEN 256 - -static inline void nvme_add_blocker_feature(char *blocker_features, - const char *feature) -{ - if (strlen(blocker_features) > 0) { - g_strlcat(blocker_features, ", ", BLOCKER_FEATURES_MAX_LEN); - } - g_strlcat(blocker_features, feature, BLOCKER_FEATURES_MAX_LEN); -} - static bool nvme_set_migration_blockers(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) { uint64_t unsupported_cap, cap = ldq_le_p(&n->bar.cap); - char blocker_features[BLOCKER_FEATURES_MAX_LEN] = ""; + g_autoptr(GPtrArray) blocker_features = g_ptr_array_new(); bool adm_cmd_security_checked = false; bool cmd_io_mgmt_checked = false; bool cmd_zone_checked = false; @@ -9416,15 +9436,15 @@ static bool nvme_set_migration_blockers(NvmeCtrl *n, PCIDevice *pci_dev, } if (namespaces_num > 1) { - nvme_add_blocker_feature(blocker_features, - "Namespace Attachment"); + g_ptr_array_add(blocker_features, + (void *) "Namespace Attachment"); } break; } case NVME_ADM_CMD_VIRT_MNGMT: if (n->params.sriov_max_vfs) { - nvme_add_blocker_feature(blocker_features, "SR-IOV"); + g_ptr_array_add(blocker_features, (void *) "SR-IOV"); } break; @@ -9435,7 +9455,7 @@ static bool nvme_set_migration_blockers(NvmeCtrl *n, PCIDevice *pci_dev, } if (pci_dev->spdm_port) { - nvme_add_blocker_feature(blocker_features, "SPDM"); + g_ptr_array_add(blocker_features, (void *) "SPDM"); } adm_cmd_security_checked = true; @@ -9469,7 +9489,7 @@ static bool nvme_set_migration_blockers(NvmeCtrl *n, PCIDevice *pci_dev, /* check for NVME_IOMS_MO_RUH_UPDATE */ if (n->subsys->params.fdp.enabled) { - nvme_add_blocker_feature(blocker_features, "FDP"); + g_ptr_array_add(blocker_features, (void *) "FDP"); } cmd_io_mgmt_checked = true; @@ -9504,8 +9524,8 @@ static bool nvme_set_migration_blockers(NvmeCtrl *n, PCIDevice *pci_dev, } if (ns->params.zoned) { - nvme_add_blocker_feature(blocker_features, - "Zoned Namespace"); + g_ptr_array_add(blocker_features, + (void *) "Zoned Namespace"); break; } } @@ -9525,24 +9545,28 @@ static bool nvme_set_migration_blockers(NvmeCtrl *n, PCIDevice *pci_dev, * covered by unsupported_cap check. */ if (NVME_CAP_CMBS(cap)) { - nvme_add_blocker_feature(blocker_features, "CMB"); + g_ptr_array_add(blocker_features, (void *) "CMB"); cap &= ~((uint64_t)CAP_CMBS_MASK << CAP_CMBS_SHIFT); } if (NVME_CAP_PMRS(cap)) { - nvme_add_blocker_feature(blocker_features, "PMR"); + g_ptr_array_add(blocker_features, (void *) "PMR"); cap &= ~((uint64_t)CAP_PMRS_MASK << CAP_PMRS_SHIFT); } unsupported_cap = cap & ~NVME_MIGRATION_SUPPORTED_CAP_BITS; if (unsupported_cap) { - nvme_add_blocker_feature(blocker_features, "unknown capability"); + g_ptr_array_add(blocker_features, (void *) "unknown capability"); } assert(n->migration_blocker == NULL); - if (strlen(blocker_features) > 0) { + if (blocker_features->len > 0) { + g_autofree char *blocker_list = NULL; + + g_ptr_array_add(blocker_features, NULL); + blocker_list = g_strjoinv(", ", (void *)blocker_features->pdata); error_setg(&n->migration_blocker, - "Migration is not supported for %s", blocker_features); + "Migration is not supported for %s", blocker_list); if (migrate_add_blocker(&n->migration_blocker, errp) < 0) { return false; } @@ -9710,10 +9734,9 @@ static void nvme_exit(PCIDevice *pci_dev) msix_uninit_exclusive_bar(pci_dev); } else { msix_uninit(pci_dev, &n->bar0, &n->bar0); + memory_region_del_subregion(&n->bar0, &n->iomem); } - memory_region_del_subregion(&n->bar0, &n->iomem); - migrate_del_blocker(&n->migration_blocker); } diff --git a/hw/nvram/xlnx-bbram.c b/hw/nvram/xlnx-bbram.c index edfb592a5e..e336874bde 100644 --- a/hw/nvram/xlnx-bbram.c +++ b/hw/nvram/xlnx-bbram.c @@ -468,7 +468,7 @@ static void bbram_prop_set_drive(Object *obj, Visitor *v, const char *name, qdev_prop_drive.set(obj, v, name, opaque, errp); /* Fill initial data if backend is attached after realized */ - if (dev->realized) { + if (qdev_is_realized(dev)) { bbram_bdrv_read(XLNX_BBRAM(obj), errp); } } diff --git a/hw/nvram/xlnx-efuse.c b/hw/nvram/xlnx-efuse.c index 1a9650ba12..03c9fbc0d0 100644 --- a/hw/nvram/xlnx-efuse.c +++ b/hw/nvram/xlnx-efuse.c @@ -233,7 +233,7 @@ static void efuse_prop_set_drive(Object *obj, Visitor *v, const char *name, qdev_prop_drive.set(obj, v, name, opaque, errp); /* Fill initial data if backend is attached after realized */ - if (dev->realized) { + if (qdev_is_realized(dev)) { efuse_bdrv_read(XLNX_EFUSE(obj), errp); } } diff --git a/hw/pci-bridge/cxl_upstream.c b/hw/pci-bridge/cxl_upstream.c index b6281cbd4c..69773ff163 100644 --- a/hw/pci-bridge/cxl_upstream.c +++ b/hw/pci-bridge/cxl_upstream.c @@ -158,6 +158,9 @@ static bool cxl_doe_cdat_rsp(DOECap *doe_cap) } ent = req->entry_handle; + if (ent >= cdat->entry_len) { + return false; + } base = cdat->entry[ent].base; len = cdat->entry[ent].length; diff --git a/hw/pci-host/Kconfig b/hw/pci-host/Kconfig index 8cbb8304a3..bda6e161f1 100644 --- a/hw/pci-host/Kconfig +++ b/hw/pci-host/Kconfig @@ -49,6 +49,7 @@ config PCI_I440FX config PCI_EXPRESS_ASPEED bool select PCI_EXPRESS + select PCIE_PORT config PCI_EXPRESS_Q35 bool diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index 4784b8f59b..f4556ad03a 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -485,22 +485,20 @@ static void mch_write_config(PCIDevice *d, mch_update_pciexbar(mch); } - if (!mch->has_smm_ranges) { - return; - } + if (mch->has_smm_ranges) { + if (ranges_overlap(address, len, MCH_HOST_BRIDGE_SMRAM, + MCH_HOST_BRIDGE_SMRAM_SIZE)) { + mch_update_smram(mch); + } - if (ranges_overlap(address, len, MCH_HOST_BRIDGE_SMRAM, - MCH_HOST_BRIDGE_SMRAM_SIZE)) { - mch_update_smram(mch); - } + if (ranges_overlap(address, len, MCH_HOST_BRIDGE_EXT_TSEG_MBYTES, + MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_SIZE)) { + mch_update_ext_tseg_mbytes(mch); + } - if (ranges_overlap(address, len, MCH_HOST_BRIDGE_EXT_TSEG_MBYTES, - MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_SIZE)) { - mch_update_ext_tseg_mbytes(mch); - } - - if (ranges_overlap(address, len, MCH_HOST_BRIDGE_F_SMBASE, 1)) { - mch_update_smbase_smram(mch); + if (ranges_overlap(address, len, MCH_HOST_BRIDGE_F_SMBASE, 1)) { + mch_update_smbase_smram(mch); + } } } @@ -572,42 +570,9 @@ static void mch_reset(DeviceState *qdev) mch_update(mch); } -static void mch_realize(PCIDevice *d, Error **errp) +static void mch_init_smram_regions(MCHPCIState *mch) { - int i; - MCHPCIState *mch = MCH_PCI_DEVICE(d); - - if (mch->ext_tseg_mbytes > MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_MAX) { - error_setg(errp, "invalid extended-tseg-mbytes value: %" PRIu16, - mch->ext_tseg_mbytes); - return; - } - - /* setup pci memory mapping */ - pc_pci_as_mapping_init(mch->system_memory, mch->pci_address_space); - - /* PAM */ - init_pam(&mch->pam_regions[0], OBJECT(mch), mch->ram_memory, - mch->system_memory, mch->pci_address_space, - PAM_BIOS_BASE, PAM_BIOS_SIZE); - for (i = 0; i < ARRAY_SIZE(mch->pam_regions) - 1; ++i) { - init_pam(&mch->pam_regions[i + 1], OBJECT(mch), mch->ram_memory, - mch->system_memory, mch->pci_address_space, - PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, PAM_EXPAN_SIZE); - } - - if (!mch->has_smm_ranges) { - return; - } - - /* if *disabled* show SMRAM to all CPUs */ - memory_region_init_alias(&mch->smram_region, OBJECT(mch), "smram-region", - mch->pci_address_space, MCH_HOST_BRIDGE_SMRAM_C_BASE, - MCH_HOST_BRIDGE_SMRAM_C_SIZE); - memory_region_add_subregion_overlap(mch->system_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, - &mch->smram_region, 1); - memory_region_set_enabled(&mch->smram_region, true); - + /* Initialize all the SMRAM specific MemoryRegions */ memory_region_init_alias(&mch->open_high_smram, OBJECT(mch), "smram-open-high", mch->ram_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, MCH_HOST_BRIDGE_SMRAM_C_SIZE); @@ -663,9 +628,52 @@ static void mch_realize(PCIDevice *d, Error **errp) memory_region_set_enabled(&mch->smbase_window, false); memory_region_add_subregion(&mch->smram, MCH_HOST_BRIDGE_SMBASE_ADDR, &mch->smbase_window); +} - object_property_add_const_link(qdev_get_machine(), "smram", - OBJECT(&mch->smram)); +static void mch_realize(PCIDevice *d, Error **errp) +{ + int i; + MCHPCIState *mch = MCH_PCI_DEVICE(d); + + if (mch->ext_tseg_mbytes > MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_MAX) { + error_setg(errp, "invalid extended-tseg-mbytes value: %" PRIu16, + mch->ext_tseg_mbytes); + return; + } + + /* setup pci memory mapping */ + pc_pci_as_mapping_init(mch->system_memory, mch->pci_address_space); + + /* PAM */ + init_pam(&mch->pam_regions[0], OBJECT(mch), mch->ram_memory, + mch->system_memory, mch->pci_address_space, + PAM_BIOS_BASE, PAM_BIOS_SIZE); + for (i = 0; i < ARRAY_SIZE(mch->pam_regions) - 1; ++i) { + init_pam(&mch->pam_regions[i + 1], OBJECT(mch), mch->ram_memory, + mch->system_memory, mch->pci_address_space, + PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, PAM_EXPAN_SIZE); + } + + /* + * This memory region looks like it's SMM specific, but it is not. + * It's an alias that makes the pci_address_space appear in system + * memory at the SMRAM_C_BASE address. The alias is enabled when the + * CPU should not see SMRAM, and *disabled* when the low SMRAM should be + * visible. So for non-SMM configs we need to create the alias, and + * leave it permanently enabled. + */ + memory_region_init_alias(&mch->smram_region, OBJECT(mch), "smram-region", + mch->pci_address_space, MCH_HOST_BRIDGE_SMRAM_C_BASE, + MCH_HOST_BRIDGE_SMRAM_C_SIZE); + memory_region_add_subregion_overlap(mch->system_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, + &mch->smram_region, 1); + memory_region_set_enabled(&mch->smram_region, true); + + if (mch->has_smm_ranges) { + mch_init_smram_regions(mch); + object_property_add_const_link(qdev_get_machine(), "smram", + OBJECT(&mch->smram)); + } } static const Property mch_props[] = { diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index c0cb45dbfb..f0413639f9 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -3552,7 +3552,7 @@ static void pnv_cpu_do_nmi(PnvChip *chip, PowerPCCPU *cpu, void *opaque) async_run_on_cpu(CPU(cpu), pnv_cpu_do_nmi_on_cpu, RUN_ON_CPU_HOST_INT(0)); } -static void pnv_nmi(NMIState *n, int cpu_index, Error **errp) +static void pnv_nmi(NMIState *ns) { PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); int i; @@ -3583,7 +3583,7 @@ static void pnv_machine_class_init(ObjectClass *oc, const void *data) mc->default_ram_size = 1 * GiB; mc->default_ram_id = "pnv.ram"; ispc->print_info = pnv_pic_print_info; - nc->nmi_monitor_handler = pnv_nmi; + nc->raise_nmi = pnv_nmi; object_class_property_add_bool(oc, "hb-mode", pnv_machine_get_hb, pnv_machine_set_hb); diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c index e8701c6100..39ec448f3c 100644 --- a/hw/ppc/pnv_psi.c +++ b/hw/ppc/pnv_psi.c @@ -688,6 +688,8 @@ static uint64_t pnv_psi_p9_mmio_read(void *opaque, hwaddr addr, unsigned size) case PSIHB9_ESB_CI_BASE: case PSIHB9_ESB_NOTIF_ADDR: case PSIHB9_IVT_OFFSET: + case PSIHB9_IRQ_LEVEL: + case PSIHB9_IRQ_STAT: val = psi->regs[reg]; break; default: @@ -818,17 +820,20 @@ static void pnv_psi_power9_set_irq(void *opaque, int irq, int state) { PnvPsi *psi = opaque; uint64_t irq_method = psi->regs[PSIHB_REG(PSIHB9_INTERRUPT_CONTROL)]; + uint64_t irq_bit = PPC_BIT(irq); if (irq_method & PSIHB9_IRQ_METHOD) { qemu_log_mask(LOG_GUEST_ERROR, "PSI: LSI IRQ method no supported\n"); return; } - /* Update LSI levels */ + /* Update LSI levels and pending status */ if (state) { - psi->regs[PSIHB_REG(PSIHB9_IRQ_LEVEL)] |= PPC_BIT(irq); + psi->regs[PSIHB_REG(PSIHB9_IRQ_LEVEL)] |= irq_bit; + psi->regs[PSIHB_REG(PSIHB9_IRQ_STAT)] |= irq_bit; } else { - psi->regs[PSIHB_REG(PSIHB9_IRQ_LEVEL)] &= ~PPC_BIT(irq); + psi->regs[PSIHB_REG(PSIHB9_IRQ_LEVEL)] &= ~irq_bit; + psi->regs[PSIHB_REG(PSIHB9_IRQ_STAT)] &= ~irq_bit; } qemu_set_irq(psi->qirqs[irq], state); diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c index dc1ffc6c01..7af722584e 100644 --- a/hw/ppc/pnv_xscom.c +++ b/hw/ppc/pnv_xscom.c @@ -253,7 +253,7 @@ static int xscom_dt_child(Object *child, void *opaque) /* * Only "realized" devices should be configured in the DT */ - if (xc->dt_xscom && DEVICE(child)->realized) { + if (xc->dt_xscom && qdev_is_realized(DEVICE(child))) { _FDT((xc->dt_xscom(xd, args->fdt, args->xscom_offset))); } } diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index b79828b4e9..20e024907b 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3550,7 +3550,7 @@ void spapr_do_system_reset_on_cpu(CPUState *cs, run_on_cpu_data arg) } } -static void spapr_nmi(NMIState *n, int cpu_index, Error **errp) +static void spapr_nmi(NMIState *ns) { CPUState *cs; @@ -4652,7 +4652,7 @@ static void spapr_machine_class_init(ObjectClass *oc, const void *data) mc->nvdimm_supported = true; smc->resize_hpt_default = SPAPR_RESIZE_HPT_ENABLED; fwc->get_dev_path = spapr_get_fw_dev_path; - nc->nmi_monitor_handler = spapr_nmi; + nc->raise_nmi = spapr_nmi; vhc->cpu_in_nested = spapr_cpu_in_nested; vhc->deliver_hv_excp = spapr_exit_nested; vhc->hypercall = emulate_spapr_hypercall; @@ -4764,14 +4764,25 @@ static void spapr_machine_latest_class_options(MachineClass *mc) DEFINE_SPAPR_MACHINE_IMPL(false, major, minor) /* - * pseries-11.1 + * pseries-11.2 */ -static void spapr_machine_11_1_class_options(MachineClass *mc) +static void spapr_machine_11_2_class_options(MachineClass *mc) { /* Defaults for the latest behaviour inherited from the base class */ } -DEFINE_SPAPR_MACHINE_AS_LATEST(11, 1); +DEFINE_SPAPR_MACHINE_AS_LATEST(11, 2); + +/* + * pseries-11.1 + */ +static void spapr_machine_11_1_class_options(MachineClass *mc) +{ + spapr_machine_11_2_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_11_1, hw_compat_11_1_len); +} + +DEFINE_SPAPR_MACHINE(11, 1); /* * pseries-11.0 diff --git a/hw/remote/vfio-user-obj.c b/hw/remote/vfio-user-obj.c index 87fa7b6572..a0498d218f 100644 --- a/hw/remote/vfio-user-obj.c +++ b/hw/remote/vfio-user-obj.c @@ -375,9 +375,9 @@ static int vfu_object_mr_rw(MemoryRegion *mr, uint8_t *buf, hwaddr offset, ram_ptr = memory_region_get_ram_ptr(mr); if (is_write) { - memcpy((ram_ptr + offset), buf, size); + qemu_ram_move((ram_ptr + offset), buf, size); } else { - memcpy(buf, (ram_ptr + offset), size); + qemu_ram_move(buf, (ram_ptr + offset), size); } return 0; diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index de37c08cae..d06ac26648 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -162,3 +162,5 @@ config K230 select SERIAL_MM select UNIMP select K230_WDT + select K230_GSDMA + select K230_DECOMP_GZIP diff --git a/hw/riscv/k230.c b/hw/riscv/k230.c index 656f28190c..558f30b97e 100644 --- a/hw/riscv/k230.c +++ b/hw/riscv/k230.c @@ -97,6 +97,7 @@ static const MemMapEntry memmap[] = { [K230_DEV_SPI] = { 0x91584000, 0x00001000 }, [K230_DEV_HI_SYS_CFG] = { 0x91585000, 0x00000400 }, [K230_DEV_DDRC_CFG] = { 0x98000000, 0x02000000 }, + [K230_DEV_DDR_PHY] = { 0x9A000000, 0x00400000 }, [K230_DEV_FLASH] = { 0xC0000000, 0x08000000 }, [K230_DEV_PLIC] = { 0xF00000000, 0x00400000 }, [K230_DEV_CLINT] = { 0xF04000000, 0x00400000 }, @@ -108,8 +109,16 @@ static void k230_soc_init(Object *obj) RISCVHartArrayState *cpu0 = &s->c908_cpu; object_initialize_child(obj, "c908-cpu", cpu0, TYPE_RISCV_HART_ARRAY); + object_initialize_child(obj, "ddr-cfg", &s->ddr_cfg, + TYPE_K230_DDR_CFG); + object_initialize_child(obj, "ddr-phy", &s->ddr_phy, + TYPE_K230_DDR_PHY); + s->ddr_cfg.phy = &s->ddr_phy; object_initialize_child(obj, "k230-wdt0", &s->wdt[0], TYPE_K230_WDT); object_initialize_child(obj, "k230-wdt1", &s->wdt[1], TYPE_K230_WDT); + object_initialize_child(obj, "k230-gsdma", &s->gsdma, TYPE_K230_GSDMA); + object_initialize_child(obj, "k230-decomp-gzip", &s->decomp_gzip, + TYPE_K230_DECOMP_GZIP); qdev_prop_set_uint32(DEVICE(cpu0), "hartid-base", 0); qdev_prop_set_string(DEVICE(cpu0), "cpu-type", TYPE_RISCV_CPU_THEAD_C908); @@ -158,6 +167,16 @@ static void k230_soc_realize(DeviceState *dev, Error **errp) int c908_cpus; sysbus_realize(SYS_BUS_DEVICE(&s->c908_cpu), &error_fatal); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->ddr_cfg), errp)) { + return; + } + if (!sysbus_realize(SYS_BUS_DEVICE(&s->ddr_phy), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ddr_cfg), 0, + memmap[K230_DEV_DDRC_CFG].base); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ddr_phy), 0, + memmap[K230_DEV_DDR_PHY].base); c908_cpus = s->c908_cpu.num_harts; @@ -206,6 +225,34 @@ static void k230_soc_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[1]), 0, qdev_get_gpio_in(DEVICE(s->c908_plic), K230_WDT1_IRQ)); + /* Gsdma */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gsdma), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gsdma), 0, memmap[K230_DEV_GSDMA].base); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gsdma), 0, + qdev_get_gpio_in(DEVICE(s->c908_plic), K230_GSDMA_IRQ)); + + /* Decomp gzip */ + qdev_prop_set_uint64(DEVICE(&s->decomp_gzip), "sram-base", + memmap[K230_DEV_SRAM].base); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->decomp_gzip), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->decomp_gzip), 0, + memmap[K230_DEV_DECOMP_GZIP].base); + for (int i = 0; i < K230_DECOMP_GZIP_NUM_GPIOS_OUT; i++) { + qdev_connect_gpio_out(DEVICE(&s->decomp_gzip), i, + qdev_get_gpio_in(DEVICE(&s->gsdma), i)); + } + for (int i = 0; i < K230_DECOMP_GZIP_NUM_GPIOS_IN; i++) { + qdev_connect_gpio_out(DEVICE(&s->gsdma), i, + qdev_get_gpio_in(DEVICE(&s->decomp_gzip), i)); + } + + /* Noc stub, the k230 decomp_gzip driver in uboot will access this region */ + create_unimplemented_device("noc-stub", 0x91302000, 0x1000); + /* unimplemented devices */ create_unimplemented_device("kpu.l2-cache", memmap[K230_DEV_KPU_L2_CACHE].base, @@ -221,16 +268,9 @@ static void k230_soc_realize(DeviceState *dev, Error **errp) memmap[K230_DEV_AI_2D_ENGINE].base, memmap[K230_DEV_AI_2D_ENGINE].size); - create_unimplemented_device("gsdma", memmap[K230_DEV_GSDMA].base, - memmap[K230_DEV_GSDMA].size); - create_unimplemented_device("dma", memmap[K230_DEV_DMA].base, memmap[K230_DEV_DMA].size); - create_unimplemented_device("decomp-gzip", - memmap[K230_DEV_DECOMP_GZIP].base, - memmap[K230_DEV_DECOMP_GZIP].size); - create_unimplemented_device("2d-engine.non-ai", memmap[K230_DEV_NON_AI_2D].base, memmap[K230_DEV_NON_AI_2D].size); @@ -361,9 +401,6 @@ static void k230_soc_realize(DeviceState *dev, Error **errp) create_unimplemented_device("hi_sys_cfg", memmap[K230_DEV_HI_SYS_CFG].base, memmap[K230_DEV_HI_SYS_CFG].size); - create_unimplemented_device("ddrc_cfg", memmap[K230_DEV_DDRC_CFG].base, - memmap[K230_DEV_DDRC_CFG].size); - create_unimplemented_device("flash", memmap[K230_DEV_FLASH].base, memmap[K230_DEV_FLASH].size); } diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index f6865d1e16..323a041b4a 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -281,7 +281,7 @@ static hwaddr riscv_iommu_napot_page_mask(hwaddr ppn, hwaddr addr, hwaddr *out) static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, IOMMUTLBEntry *iotlb) { - IOMMUAccessFlags pte_perm; + IOMMUAccessFlags trans_perm = IOMMU_NONE; dma_addr_t addr, base; uint64_t satp, gatp, pte; bool en_s, en_g; @@ -298,6 +298,14 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, } pass; MemTxResult ret; bool pv = !!ctx->process_id; + /* + * Keep the request permission separate from iotlb->perm. G-stage + * walks translate S-stage PTE addresses before the real leaf is + * reached, but permission checks and fault types must still use the + * original request. A successful walk leaves iotlb->perm with the + * effective leaf permission for the translation cache. + */ + const IOMMUAccessFlags req_perm = iotlb->perm; satp = get_field(ctx->satp, RISCV_IOMMU_ATP_MODE_FIELD); gatp = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD); @@ -316,7 +324,7 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, * means we can't do an early MSI check unless we have * strictly !en_s. */ - if (!en_s && (iotlb->perm & IOMMU_WO) && + if (!en_s && (req_perm & IOMMU_WO) && riscv_iommu_msi_check(s, ctx, iotlb->iova)) { iotlb->target_as = &s->trap_as; iotlb->translated_addr = iotlb->iova; @@ -434,13 +442,13 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, masked_msbs = (addr >> (va_len - 1)) & mask; if (masked_msbs != 0 && masked_msbs != mask) { - return (iotlb->perm & IOMMU_WO) ? + return (req_perm & IOMMU_WO) ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S : RISCV_IOMMU_FQ_CAUSE_RD_FAULT_S; } } else { if ((addr & va_mask) != addr) { - return (iotlb->perm & IOMMU_WO) ? + return (req_perm & IOMMU_WO) ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS : RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS; } @@ -465,8 +473,8 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, MEMTXATTRS_UNSPECIFIED); } if (ret != MEMTX_OK) { - return (iotlb->perm & IOMMU_WO) ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT - : RISCV_IOMMU_FQ_CAUSE_RD_FAULT; + return (req_perm & IOMMU_WO) ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT + : RISCV_IOMMU_FQ_CAUSE_RD_FAULT; } sc[pass].step++; @@ -476,13 +484,6 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, break; /* Invalid PTE */ } else if (pte & PTE_RESERVED(false)) { break; /* Reserved PTE bits set */ - } else if (!(pte & PTE_U) && !pv) { - /* - * All accesses are assumed to be User mode unless - * process_id is valid (pv). In case we have a - * non-user mode PTE and !pv we need to fault. - */ - break; } else if (!(pte & (PTE_R | PTE_W | PTE_X))) { base = PPN_PHYS(ppn); /* Inner PTE, continue walking */ } else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) { @@ -491,13 +492,20 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, break; /* Reserved leaf PTE flags: PTE_W + PTE_X */ } else if (ppn & ((1ULL << (va_skip - TARGET_PAGE_BITS)) - 1)) { break; /* Misaligned PPN */ - } else if ((iotlb->perm & IOMMU_RO) && !(pte & PTE_R)) { + } else if (!(pte & PTE_U) && !pv) { + /* + * All accesses are assumed to be User mode unless + * process_id is valid (pv). In case we have a + * non-user mode leaf PTE and !pv we need to fault. + */ + break; + } else if ((req_perm & IOMMU_RO) && !(pte & PTE_R)) { break; /* Read access check failed */ - } else if ((iotlb->perm & IOMMU_WO) && !(pte & PTE_W)) { + } else if ((req_perm & IOMMU_WO) && !(pte & PTE_W)) { break; /* Write access check failed */ } else if (!ade && !(pte & PTE_A)) { break; /* Access bit not set */ - } else if ((iotlb->perm & IOMMU_WO) && !ade && !(pte & PTE_D)) { + } else if ((req_perm & IOMMU_WO) && !ade && !(pte & PTE_D)) { break; /* Dirty bit not set */ } else if (pass == G_STAGE && !(pte & PTE_U)) { /* @@ -532,21 +540,20 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, addr = iotlb->iova; continue; } + + /* Cache the effective permission, not this request's subset. */ + IOMMUAccessFlags leaf_perm = (pte & PTE_W) ? + ((pte & PTE_R) ? IOMMU_RW : IOMMU_WO) : + IOMMU_RO; + + trans_perm = trans_perm == IOMMU_NONE ? + leaf_perm : trans_perm & leaf_perm; + /* Translation phase completed (GPA or SPA) */ iotlb->translated_addr = base; - /* - * Do a bit_and between the PTE bits and the original - * request flags to determine the exact permission we - * need, i.e. if the original request is RO and the - * PTE has RW flags the actual perm is RO. - */ - pte_perm = (pte & PTE_W) ? ((pte & PTE_R) ? IOMMU_RW : IOMMU_WO) - : IOMMU_RO; - iotlb->perm &= pte_perm; - /* Check MSI GPA address match */ - if (pass == S_STAGE && (iotlb->perm & IOMMU_WO) && + if (pass == S_STAGE && (req_perm & IOMMU_WO) && riscv_iommu_msi_check(s, ctx, base)) { /* Trap MSI writes and return GPA address. */ iotlb->target_as = &s->trap_as; @@ -563,6 +570,7 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, continue; } + iotlb->perm = trans_perm; return 0; } @@ -587,7 +595,7 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, */ iotlb->translated_addr = addr; - return (iotlb->perm & IOMMU_WO) ? + return (req_perm & IOMMU_WO) ? (pass ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS : RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S) : (pass ? RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS : diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 99423ef472..57a57c96e1 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -564,19 +564,20 @@ static void sifive_u_machine_init(MachineState *machine) 0, 0, 0x00028067, /* jr t0 */ + 0x00000000, /* padding for alignment */ start_addr, /* start: .dword */ start_addr_hi32, fdt_load_addr, /* fdt_laddr: .dword */ fdt_load_addr_hi32, - 0x00000000, /* fw_dyn: */ }; + if (riscv_is_32bit(&s->soc.u_cpus)) { - reset_vec[4] = 0x0202a583; /* lw a1, 32(t0) */ - reset_vec[5] = 0x0182a283; /* lw t0, 24(t0) */ + reset_vec[4] = 0x0242a583; /* lw a1, 36(t0) */ + reset_vec[5] = 0x01c2a283; /* lw t0, 28(t0) */ } else { - reset_vec[4] = 0x0202b583; /* ld a1, 32(t0) */ - reset_vec[5] = 0x0182b283; /* ld t0, 24(t0) */ + reset_vec[4] = 0x0242b583; /* ld a1, 36(t0) */ + reset_vec[5] = 0x01c2b283; /* ld t0, 28(t0) */ } diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index 9fde0faf39..630b65f569 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -278,8 +278,9 @@ static void spike_machine_class_init(ObjectClass *oc, const void *data) object_class_property_add_str(oc, "signature", NULL, spike_set_signature); object_class_property_set_description(oc, "signature", "File to write ACT test signature"); - object_class_property_add_uint8_ptr(oc, "signature-granularity", - &line_size, OBJ_PROP_FLAG_WRITE); + object_class_static_property_add_uint8_ptr(oc, "signature-granularity", + &line_size, + OBJ_PROP_FLAG_WRITE); object_class_property_set_description(oc, "signature-granularity", "Size of each line in ACT signature " "file"); diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c index 59c454f4f9..8e516ec114 100644 --- a/hw/riscv/virt-acpi-build.c +++ b/hw/riscv/virt-acpi-build.c @@ -177,11 +177,11 @@ static void acpi_dsdt_add_plic_aplic(Aml *scope, uint8_t socket_count, static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap, - uint32_t uart_irq) + uint32_t uart_irq, int uartidx) { - Aml *dev = aml_device("COM0"); + Aml *dev = aml_device("COM%d", uartidx); aml_append(dev, aml_name_decl("_HID", aml_string("RSCV0003"))); - aml_append(dev, aml_name_decl("_UID", aml_int(0))); + aml_append(dev, aml_name_decl("_UID", aml_int(uartidx))); Aml *crs = aml_resource_template(); aml_append(crs, aml_memory32_fixed(uart_memmap->base, @@ -490,7 +490,11 @@ static void build_dsdt(GArray *table_data, memmap[VIRT_APLIC_S].size, "RSCV0002"); } - acpi_dsdt_add_uart(scope, &memmap[VIRT_UART0], UART0_IRQ); + acpi_dsdt_add_uart(scope, &memmap[VIRT_UART0], UART0_IRQ, 0); + if (s->uart1_present) { + acpi_dsdt_add_uart(scope, &memmap[VIRT_UART1], UART1_IRQ, 1); + } + if (virt_is_iommu_sys_enabled(s)) { acpi_dsdt_add_iommu_sys(scope, &memmap[VIRT_IOMMU_SYS], IOMMU_SYS_IRQ); } diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 51bac47a91..d58656f70d 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -97,6 +97,8 @@ static const MemMapEntry virt_memmap[] = { [VIRT_APLIC_S] = { 0xd000000, APLIC_SIZE(VIRT_CPUS_MAX) }, [VIRT_UART0] = { 0x10000000, 0x100 }, [VIRT_VIRTIO] = { 0x10001000, 0x1000 }, + /* UART1 supports page isolation from UART0 */ + [VIRT_UART1] = { 0x1000a000, 0x100 }, [VIRT_FW_CFG] = { 0x10100000, 0x18 }, [VIRT_FLASH] = { 0x20000000, 0x4000000 }, [VIRT_IMSIC_M] = { 0x24000000, VIRT_IMSIC_MAX_SIZE }, @@ -187,7 +189,8 @@ static void create_pcie_irq_map(RISCVVirtState *s, void *fdt, char *nodename, FDT_MAX_INT_MAP_WIDTH] = {}; uint32_t *irq_map = full_irq_map; - /* This code creates a standard swizzle of interrupts such that + /* + * 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()) * @@ -805,28 +808,38 @@ static void create_fdt_reset(RISCVVirtState *s, uint32_t *phandle) } static void create_fdt_uart(RISCVVirtState *s, - uint32_t irq_mmio_phandle) + uint32_t irq_mmio_phandle, int memId, int irqNo) { g_autofree char *name = NULL; MachineState *ms = MACHINE(s); name = g_strdup_printf("/soc/serial@%"HWADDR_PRIx, - s->memmap[VIRT_UART0].base); + s->memmap[memId].base); qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "ns16550a"); qemu_fdt_setprop_sized_cells(ms->fdt, name, "reg", - 2, s->memmap[VIRT_UART0].base, - 2, s->memmap[VIRT_UART0].size); + 2, s->memmap[memId].base, + 2, s->memmap[memId].size); qemu_fdt_setprop_cell(ms->fdt, name, "clock-frequency", 3686400); qemu_fdt_setprop_cell(ms->fdt, name, "interrupt-parent", irq_mmio_phandle); if (s->aia_type == VIRT_AIA_TYPE_NONE) { - qemu_fdt_setprop_cell(ms->fdt, name, "interrupts", UART0_IRQ); + qemu_fdt_setprop_cell(ms->fdt, name, "interrupts", irqNo); } else { - qemu_fdt_setprop_cells(ms->fdt, name, "interrupts", UART0_IRQ, 0x4); + qemu_fdt_setprop_cells(ms->fdt, name, "interrupts", irqNo, 0x4); } - qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", name); - qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial0", name); + if (VIRT_UART0 == memId) { + qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", name); + qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial0", name); + } +} + +static void create_fdt_uarts(RISCVVirtState *s, uint32_t irq_mmio_phandle) +{ + if (s->uart1_present) { + create_fdt_uart(s, irq_mmio_phandle, VIRT_UART1, UART1_IRQ); + } + create_fdt_uart(s, irq_mmio_phandle, VIRT_UART0, UART0_IRQ); } static void create_fdt_rtc(RISCVVirtState *s, @@ -996,7 +1009,7 @@ static void finalize_fdt(RISCVVirtState *s) create_fdt_reset(s, &phandle); - create_fdt_uart(s, irq_mmio_phandle); + create_fdt_uarts(s, irq_mmio_phandle); create_fdt_rtc(s, irq_mmio_phandle); } @@ -1486,6 +1499,13 @@ static void virt_machine_init(MachineState *machine) 0, qdev_get_gpio_in(mmio_irqchip, UART0_IRQ), 399193, serial_hd(0), DEVICE_LITTLE_ENDIAN); + if (serial_hd(1)) { + serial_mm_init(system_memory, s->memmap[VIRT_UART1].base, + 0, qdev_get_gpio_in(mmio_irqchip, UART1_IRQ), 399193, + serial_hd(1), DEVICE_LITTLE_ENDIAN); + s->uart1_present = true; + } + sysbus_create_simple("goldfish_rtc", s->memmap[VIRT_RTC].base, qdev_get_gpio_in(mmio_irqchip, RTC_IRQ)); diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index fac30763df..ef9c063d90 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -124,6 +124,12 @@ static inline bool ipl_valid_pv_components(IplParameterBlock *iplb) return false; } + if (offsetof(IplParameterBlock, pv.components) + + ipib_pv->num_comp * sizeof(IPLBlockPVComp) > + be32_to_cpu(iplb->len)) { + return false; + } + for (i = 0; i < ipib_pv->num_comp; i++) { /* Addr must be 4k aligned */ if (ipib_pv->components[i].addr & ~TARGET_PAGE_MASK) { diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 25a9fa4955..06e5def909 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -725,11 +725,9 @@ static HotplugHandler *s390_get_hotplug_handler(MachineState *machine, return NULL; } -static void s390_nmi(NMIState *n, int cpu_index, Error **errp) +static void s390_nmi(NMIState *ns) { - CPUState *cs = qemu_get_cpu(cpu_index); - - s390_cpu_restart(S390_CPU(cs)); + s390_cpu_restart(S390_CPU(first_cpu)); } static inline bool machine_get_aes_key_wrap(Object *obj, Error **errp) @@ -832,7 +830,7 @@ static void ccw_machine_class_init(ObjectClass *oc, const void *data) hc->plug = s390_machine_device_plug; hc->unplug_request = s390_machine_device_unplug_request; hc->unplug = s390_machine_device_unplug; - nc->nmi_monitor_handler = s390_nmi; + nc->raise_nmi = s390_nmi; mc->default_ram_id = "s390.ram"; mc->default_nic = "virtio-net-ccw"; dsi->qmp_dump_skeys = s390_qmp_dump_skeys; @@ -925,14 +923,26 @@ static const TypeInfo ccw_machine_info = { DEFINE_CCW_MACHINE_IMPL(false, major, minor) +static void ccw_machine_11_2_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_11_2_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE_AS_LATEST(11, 2); + static void ccw_machine_11_1_instance_options(MachineState *machine) { + ccw_machine_11_2_instance_options(machine); } static void ccw_machine_11_1_class_options(MachineClass *mc) { + ccw_machine_11_2_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_11_1, hw_compat_11_1_len); } -DEFINE_CCW_MACHINE_AS_LATEST(11, 1); +DEFINE_CCW_MACHINE(11, 1); static void ccw_machine_11_0_instance_options(MachineState *machine) { diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index dccb2f25b2..deb43d5560 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -1513,6 +1513,13 @@ void scsi_req_unref(SCSIRequest *req) } } +void scsi_req_unref_detach_hba(SCSIRequest *req) +{ + /* Unref when the HBA frees hba_private separately (e.g. virtio_scsi_free_req) */ + req->hba_private = NULL; + scsi_req_unref(req); +} + /* Tell the device that we finished processing this chunk of I/O. It will start the next chunk or complete the command. */ void scsi_req_continue(SCSIRequest *req) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 5ba5b46c4f..1b0cce128c 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -1321,7 +1321,7 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, return -1; } - assert(length < 256); + assert(length + 2 <= SCSI_MAX_MODE_LEN); (*p_outbuf)[0] = page; (*p_outbuf)[1] = length; *p_outbuf += length + 2; @@ -1524,7 +1524,7 @@ static void scsi_disk_emulate_read_data(SCSIRequest *req) static int scsi_disk_check_mode_select(SCSIDiskState *s, int page, uint8_t *inbuf, int inlen) { - uint8_t mode_current[SCSI_MAX_MODE_LEN]; + uint8_t mode_current[SCSI_MAX_MODE_LEN] = { 0 }; uint8_t mode_changeable[SCSI_MAX_MODE_LEN]; uint8_t *p; int len, expected_len, changeable_len, i; @@ -1543,21 +1543,21 @@ static int scsi_disk_check_mode_select(SCSIDiskState *s, int page, } p = mode_current; - memset(mode_current, 0, inlen + 2); len = mode_sense_page(s, page, &p, 0); - if (len < 0 || len != expected_len) { + /* The guest may send a truncated page, but not a longer one. */ + if (len < 0 || expected_len > len) { return -1; } p = mode_changeable; - memset(mode_changeable, 0, inlen + 2); + memset(mode_changeable, 0, len); changeable_len = mode_sense_page(s, page, &p, 1); assert(changeable_len == len); /* Check that unchangeable bits are the same as what MODE SENSE * would return. */ - for (i = 2; i < len; i++) { + for (i = 2; i < expected_len; i++) { if (((mode_current[i] ^ inbuf[i - 2]) & ~mode_changeable[i]) != 0) { return -1; } @@ -1565,11 +1565,15 @@ static int scsi_disk_check_mode_select(SCSIDiskState *s, int page, return 0; } -static void scsi_disk_apply_mode_select(SCSIDiskState *s, int page, uint8_t *p) +/* Note p may be truncated, so check any bytes you access against len. */ +static void scsi_disk_apply_mode_select(SCSIDiskState *s, int page, + uint8_t *p, int len) { switch (page) { case MODE_PAGE_CACHING: - blk_set_enable_write_cache(s->qdev.conf.blk, (p[0] & 4) != 0); + if (len > 0) { + blk_set_enable_write_cache(s->qdev.conf.blk, (p[0] & 4) != 0); + } break; default: @@ -1612,6 +1616,7 @@ static int mode_select_pages(SCSIDiskReq *r, uint8_t *p, int len, bool change) goto invalid_param_len; } trace_scsi_disk_mode_select_page_truncated(page, page_len, len); + page_len = len; } if (!change) { @@ -1619,7 +1624,7 @@ static int mode_select_pages(SCSIDiskReq *r, uint8_t *p, int len, bool change) goto invalid_param; } } else { - scsi_disk_apply_mode_select(s, page, p); + scsi_disk_apply_mode_select(s, page, p, page_len); } p += page_len; diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 6c73768011..bf64d1231a 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -931,7 +931,9 @@ static void virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq) req = QTAILQ_FIRST(&reqs); QTAILQ_REMOVE(&reqs, req, next); defer_call_end(); + /* Drop both the ref from _prepare and the initial ref */ scsi_req_unref(req->sreq); + scsi_req_unref_detach_hba(req->sreq); virtqueue_detach_element(req->vq, &req->elem, 0); virtio_scsi_free_req(req); } diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index 4398aa5499..05f93171cd 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -392,9 +392,18 @@ static void pvscsi_cmp_ring_put(PVSCSIState *s, struct PVSCSIRingCmpDesc *cmp_desc) { hwaddr cmp_descr_pa; + PVSCSIRingCmpDesc cmp_desc_conv; cmp_descr_pa = pvscsi_ring_pop_cmp_descr(&s->rings); trace_pvscsi_cmp_ring_put(cmp_descr_pa); + cmp_desc_conv = (struct PVSCSIRingCmpDesc) { + .context = cpu_to_le64(cmp_desc->context), + .dataLen = cpu_to_le64(cmp_desc->dataLen), + .senseLen = cpu_to_le32(cmp_desc->senseLen), + .hostStatus = cpu_to_le16(cmp_desc->hostStatus), + .scsiStatus = cpu_to_le16(cmp_desc->scsiStatus), + }; + cmp_desc = &cmp_desc_conv; physical_memory_write(cmp_descr_pa, cmp_desc, sizeof(*cmp_desc)); } @@ -402,9 +411,18 @@ static void pvscsi_msg_ring_put(PVSCSIState *s, struct PVSCSIRingMsgDesc *msg_desc) { hwaddr msg_descr_pa; + PVSCSIRingMsgDesc msg_desc_conv; + int i; msg_descr_pa = pvscsi_ring_pop_msg_descr(&s->rings); trace_pvscsi_msg_ring_put(msg_descr_pa); + msg_desc_conv = (PVSCSIRingMsgDesc) { + .type = cpu_to_le32(msg_desc->type), + }; + for (i = 0; i < ARRAY_SIZE(msg_desc->args); i++) { + msg_desc_conv.args[i] = cpu_to_le32(msg_desc->args[i]); + } + msg_desc = &msg_desc_conv; physical_memory_write(msg_descr_pa, msg_desc, sizeof(*msg_desc)); } @@ -481,6 +499,9 @@ pvscsi_get_next_sg_elem(PVSCSISGState *sg) struct PVSCSISGElement elem; physical_memory_read(sg->elemAddr, &elem, sizeof(elem)); + elem.addr = le64_to_cpu(elem.addr); + elem.length = le32_to_cpu(elem.length); + elem.flags = le32_to_cpu(elem.flags); if ((elem.flags & ~PVSCSI_KNOWN_FLAGS) != 0) { /* * There is PVSCSI_SGE_FLAG_CHAIN_ELEMENT flag described in @@ -759,6 +780,12 @@ pvscsi_process_io(PVSCSIState *s) trace_pvscsi_process_io(next_descr_pa); physical_memory_read(next_descr_pa, &descr, sizeof(descr)); + descr.context = le64_to_cpu(descr.context); + descr.dataAddr = le64_to_cpu(descr.dataAddr); + descr.dataLen = le64_to_cpu(descr.dataLen); + descr.senseAddr = le64_to_cpu(descr.senseAddr); + descr.senseLen = le32_to_cpu(descr.senseLen); + descr.flags = le32_to_cpu(descr.flags); pvscsi_process_request_descriptor(s, &descr); } @@ -808,6 +835,17 @@ pvscsi_on_cmd_setup_rings(PVSCSIState *s) { PVSCSICmdDescSetupRings *rc = (PVSCSICmdDescSetupRings *) s->curr_cmd_data; + PVSCSICmdDescSetupRings translated; + int i; + + translated.reqRingNumPages = le32_to_cpu(rc->reqRingNumPages); + translated.cmpRingNumPages = le32_to_cpu(rc->cmpRingNumPages); + translated.ringsStatePPN = le64_to_cpu(rc->ringsStatePPN); + for (i = 0; i < PVSCSI_SETUP_RINGS_MAX_NUM_PAGES; i++) { + translated.reqRingPPNs[i] = le64_to_cpu(rc->reqRingPPNs[i]); + translated.cmpRingPPNs[i] = le64_to_cpu(rc->cmpRingPPNs[i]); + } + rc = &translated; trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_SETUP_RINGS"); @@ -831,6 +869,11 @@ pvscsi_on_cmd_abort(PVSCSIState *s) PVSCSICmdDescAbortCmd *cmd = (PVSCSICmdDescAbortCmd *) s->curr_cmd_data; PVSCSIRequest *r, *next; + PVSCSICmdDescAbortCmd translated = *cmd; + translated.context = le32_to_cpu(cmd->context); + translated.target = le32_to_cpu(cmd->target); + cmd = &translated; + trace_pvscsi_on_cmd_abort(cmd->context, cmd->target); QTAILQ_FOREACH_SAFE(r, &s->pending_queue, next, next) { @@ -862,6 +905,10 @@ pvscsi_on_cmd_reset_device(PVSCSIState *s) (struct PVSCSICmdDescResetDevice *) s->curr_cmd_data; SCSIDevice *sdev; + PVSCSICmdDescResetDevice translated = *cmd; + translated.target = le32_to_cpu(cmd->target); + cmd = &translated; + sdev = pvscsi_device_find(s, 0, cmd->target, cmd->lun, &target_lun); trace_pvscsi_on_cmd_reset_dev(cmd->target, (int) target_lun, sdev); @@ -892,6 +939,14 @@ pvscsi_on_cmd_setup_msg_ring(PVSCSIState *s) { PVSCSICmdDescSetupMsgRing *rc = (PVSCSICmdDescSetupMsgRing *) s->curr_cmd_data; + PVSCSICmdDescSetupMsgRing translated = *rc; + int i; + + translated.numPages = le32_to_cpu(rc->numPages); + for (i = 0; i < PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES; i++) { + translated.ringPPNs[i] = le64_to_cpu(rc->ringPPNs[i]); + } + rc = &translated; trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_SETUP_MSG_RING"); @@ -994,7 +1049,7 @@ pvscsi_on_command_data(PVSCSIState *s, uint32_t value) size_t bytes_arrived = s->curr_cmd_data_cntr * sizeof(uint32_t); assert(bytes_arrived < sizeof(s->curr_cmd_data)); - s->curr_cmd_data[s->curr_cmd_data_cntr++] = value; + s->curr_cmd_data[s->curr_cmd_data_cntr++] = cpu_to_le32(value); pvscsi_do_command_processing(s); } diff --git a/hw/scsi/vmw_pvscsi.h b/hw/scsi/vmw_pvscsi.h index a3ae517e19..defff9e179 100644 --- a/hw/scsi/vmw_pvscsi.h +++ b/hw/scsi/vmw_pvscsi.h @@ -108,6 +108,20 @@ enum PVSCSICommands { #define PVSCSI_COMMAND_PROCESSING_FAILED (-1) #define PVSCSI_COMMAND_NOT_ENOUGH_DATA (-2) +/* + * About endianess for the below structs: + * + * These structs are used to describe the data that is exchanged between the + * guest and the PVSCSI device. The endianess of the fields in these structs + * is not defined by any standard. The current implemented drivers are designed + * to only work on x86 architecture, so there is no endianess awareness in the + * drivers and thus we have no idea whether the fields should be in little- + * endian or target native endian format. + * + * Considering the above, we assume that PVSCSI is implicitly little-endian and + * expect the fields in these structs to be in little-endian format. + */ + /* * Command descriptor for PVSCSI_CMD_RESET_DEVICE -- */ diff --git a/hw/sd/Kconfig b/hw/sd/Kconfig index 633b9afec9..c69bf24f8d 100644 --- a/hw/sd/Kconfig +++ b/hw/sd/Kconfig @@ -23,3 +23,7 @@ config SDHCI_PCI config CADENCE_SDHCI bool select SDHCI + +config AXIADO_SDHCI + bool + select SDHCI diff --git a/hw/sd/axiado_sdhci.c b/hw/sd/axiado_sdhci.c new file mode 100644 index 0000000000..2e88bffdd1 --- /dev/null +++ b/hw/sd/axiado_sdhci.c @@ -0,0 +1,119 @@ +/* + * Axiado SD Host Controller with embedded PHY + * + * Author: Kuan-Jui Chiu + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/sd/axiado_sdhci.h" +#include "sdhci-internal.h" +#include "qapi/error.h" +#include "hw/core/qdev-properties.h" +#include "qemu/log.h" + +#define EMMC_PHY_ID 0x00 +#define EMMC_PHY_STATUS 0x50 + +#define DLL_RDY (1u << 0) +#define CAL_DONE (1u << 6) + +static uint64_t emmc_phy_read(void *opaque, hwaddr offset, unsigned size) +{ + uint32_t val = 0x00; + + switch (offset) { + case EMMC_PHY_ID: + val = 0x3dff6870; + break; + case EMMC_PHY_STATUS: + val = DLL_RDY | CAL_DONE; + break; + default: + qemu_log_mask(LOG_UNIMP, + "Register 0x%" HWADDR_PRIx " not implemented\n", offset); + break; + } + + return val; +} + +static void emmc_phy_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + qemu_log_mask(LOG_UNIMP, + "Register 0x%" HWADDR_PRIx " not implemented\n", offset); +} + +static const MemoryRegionOps emmc_phy_ops = { + .read = emmc_phy_read, + .write = emmc_phy_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static void axiado_sdhci_realize(DeviceState *dev, Error **errp) +{ + AxiadoSDHCIState *s = AXIADO_SDHCI(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + SysBusDevice *sdhci_sbd; + + qdev_prop_set_uint64(DEVICE(&s->sdhci), "capareg", 0x216737eed0b0); + qdev_prop_set_uint64(DEVICE(&s->sdhci), "sd-spec-version", 3); + + sdhci_sbd = SYS_BUS_DEVICE(&s->sdhci); + if (!sysbus_realize(sdhci_sbd, errp)) { + return; + } + + sysbus_init_mmio(sbd, sysbus_mmio_get_region(sdhci_sbd, 0)); + + /* Propagate IRQ from SDHCI and SD bus */ + sysbus_pass_irq(sbd, sdhci_sbd); + s->sd_bus = qdev_get_child_bus(DEVICE(sdhci_sbd), "sd-bus"); + + /* Initialize eMMC PHY MMIO */ + memory_region_init_io(&s->emmc_phy, OBJECT(s), &emmc_phy_ops, s, + "axiado.emmc-phy", 0x1000); + + sysbus_init_mmio(sbd, &s->emmc_phy); +} + +static void axiado_sdhci_instance_init(Object *obj) +{ + AxiadoSDHCIState *s = AXIADO_SDHCI(obj); + + object_initialize_child(OBJECT(s), "sdhci", &s->sdhci, + TYPE_SYSBUS_SDHCI); +} + +static void axiado_sdhci_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = axiado_sdhci_realize; + dc->desc = "Axiado SD Host Controller with eMMC PHY"; +} + +static const TypeInfo axiado_sdhci_info = { + .name = TYPE_AXIADO_SDHCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AxiadoSDHCIState), + .instance_init = axiado_sdhci_instance_init, + .class_init = axiado_sdhci_class_init, +}; + +static void axiado_sdhci_register_types(void) +{ + type_register_static(&axiado_sdhci_info); +} + +type_init(axiado_sdhci_register_types); diff --git a/hw/sd/meson.build b/hw/sd/meson.build index b43d45bc56..ebf09e30a4 100644 --- a/hw/sd/meson.build +++ b/hw/sd/meson.build @@ -10,3 +10,4 @@ system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_sdhci.c')) system_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sdhost.c')) system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_sdhci.c')) system_ss.add(when: 'CONFIG_CADENCE_SDHCI', if_true: files('cadence_sdhci.c')) +system_ss.add(when: 'CONFIG_AXIADO_SDHCI', if_true: files('axiado_sdhci.c')) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 3360757004..a30c541df0 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2860,7 +2860,8 @@ static size_t sd_read_data(SDState *sd, void *buf, size_t length) if (sd->data_offset == 0) { if (!address_in_range(sd, "READ_MULTIPLE_BLOCK", sd->data_start, io_len)) { - return dummy_byte; + *value = dummy_byte; + return length; } partition_access = sd->ext_csd[EXT_CSD_PART_CONFIG] & EXT_CSD_PART_CONFIG_ACC_MASK; diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index c86dfa281f..e58a610397 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -1795,34 +1795,6 @@ esdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) sdhci_write(opaque, offset, value, size); break; - case ESDHC_MIX_CTRL: - /* - * So, when SD/MMC stack in Linux tries to write to "Transfer - * Mode Register", ESDHC i.MX quirk code will translate it - * into a write to ESDHC_MIX_CTRL, so we do the opposite in - * order to get where we started - * - * Note that Auto CMD23 Enable bit is located in a wrong place - * on i.MX, but since it is not used by QEMU we do not care. - * - * We don't want to call sdhci_write(.., SDHC_TRNMOD, ...) - * here because it will result in a call to - * sdhci_send_command(s) which we don't want. - * - */ - s->trnmod = value & UINT16_MAX; - break; - case SDHC_TRNMOD: - /* - * Similar to above, but this time a write to "Command - * Register" will be translated into a 4-byte write to - * "Transfer Mode register" where lower 16-bit of value would - * be set to zero. So what we do is fill those bits with - * cached value from s->trnmod and let the SDHCI - * infrastructure handle the rest - */ - sdhci_write(opaque, offset, val | s->trnmod, size); - break; case SDHC_BLKSIZE: /* * ESDHCI does not implement "Host SDMA Buffer Boundary", and @@ -1891,9 +1863,52 @@ static void fsl_esdhc_le_init(Object *obj) qdev_prop_set_uint8(dev, "sd-spec-version", 2); } +static void +usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) +{ + SDHCIState *s = SYSBUS_SDHCI(opaque); + uint32_t value = (uint32_t)val; + + switch (offset) { + case ESDHC_MIX_CTRL: + /* + * So, when SD/MMC stack in Linux tries to write to "Transfer + * Mode Register", uSDHC i.MX quirk code will translate it + * into a write to ESDHC_MIX_CTRL, so we do the opposite in + * order to get where we started. + * + * Note that Auto CMD23 Enable bit is located in a wrong place + * on i.MX, but since it is not used by QEMU we do not care. + * + * We don't want to call sdhci_write(.., SDHC_TRNMOD, ...) + * here because it will result in a call to + * sdhci_send_command(s) which we don't want. + * + */ + s->trnmod = value & UINT16_MAX; + break; + + case SDHC_TRNMOD: + /* + * Similar to above, but this time a write to "Command + * Register" will be translated into a 4-byte write to + * "Transfer Mode register" where lower 16-bit of value would + * be set to zero. So what we do is fill those bits with + * cached value from s->trnmod and let the SDHCI + * infrastructure handle the rest + */ + sdhci_write(opaque, offset, val | s->trnmod, size); + break; + + default: + esdhc_write(opaque, offset, val, size); + break; + } +} + static const MemoryRegionOps usdhc_mmio_ops = { .read = esdhc_read, - .write = esdhc_write, + .write = usdhc_write, .valid = { .min_access_size = 1, .max_access_size = 4, diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig index bc6331b4ab..b459ac2240 100644 --- a/hw/sensor/Kconfig +++ b/hw/sensor/Kconfig @@ -1,3 +1,7 @@ +config ADC128D818 + bool + depends on I2C + config TMP105 bool depends on I2C diff --git a/hw/sensor/adc128d818.c b/hw/sensor/adc128d818.c new file mode 100644 index 0000000000..c65508cba1 --- /dev/null +++ b/hw/sensor/adc128d818.c @@ -0,0 +1,696 @@ +/* + * Texas Instruments ADC128D818 12-bit 8-channel ADC with I2C interface + * + * Copyright (c) 2026 Meta Platforms, Inc. and affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "qom/object.h" +#include "hw/sensor/adc128d818.h" +#include "hw/core/irq.h" +#include "hw/core/qdev-properties.h" +#include "hw/i2c/i2c.h" +#include "migration/vmstate.h" +#include "trace.h" + + +/* Register addresses */ +#define REG_CONFIG 0x00 +#define REG_INT_STATUS 0x01 +#define REG_INT_MASK 0x03 +#define REG_CONV_RATE 0x07 +#define REG_CH_DISABLE 0x08 +#define REG_ONE_SHOT 0x09 +#define REG_DEEP_SHUTDOWN 0x0a +#define REG_ADV_CONFIG 0x0b +#define REG_BUSY_STATUS 0x0c + +/* Channel Reading Registers (16-bit, read-only) */ +#define REG_CH_READING_BASE 0x20 +#define REG_CH_READING_LAST 0x27 + +/* Limit Registers (8-bit, read/write) */ +#define REG_LIMIT_BASE 0x2a +#define REG_LIMIT_LAST 0x39 + +/* ID Registers (read-only) */ +#define REG_MANUFACTURER_ID 0x3e +#define REG_REVISION_ID 0x3f + +/* Configuration Register (0x00) bitfields */ +#define CONFIG_START BIT(0) +#define CONFIG_INT_ENABLE BIT(1) +#define CONFIG_INT_CLEAR BIT(3) +#define CONFIG_INITIALIZATION BIT(7) +#define CONFIG_WR_MASK \ + (CONFIG_START | CONFIG_INT_ENABLE | CONFIG_INT_CLEAR) + +/* Advanced Configuration Register (0x0B) bitfields */ +#define ADV_CONFIG_EXT_REF_EN BIT(0) +#define ADV_CONFIG_MODE_SHIFT 1 +#define ADV_CONFIG_MODE_MASK (0x3 << ADV_CONFIG_MODE_SHIFT) +#define ADV_CONFIG_WR_MASK \ + (ADV_CONFIG_EXT_REF_EN | ADV_CONFIG_MODE_MASK) + +/* Busy Status Register (0x0C) bitfields */ +#define BUSY_STATUS_NOT_READY BIT(1) + +/* Conversion Rate Register (0x07) bitfields */ +#define CONV_RATE_MASK 0x01 + +/* Deep Shutdown Register (0x0A) bitfields */ +#define DEEP_SHUTDOWN_EN 0x01 + +/* Device constants */ +#define ADC128D818_NUM_CHANNELS 8 +#define ADC128D818_NUM_REGS 0x40 + +#define ADC128D818_INTERNAL_VREF_MV 2560 +#define ADC128D818_MAX_VDD_MV 5500 +#define ADC128D818_MANUFACTURER_ID_VAL 0x01 +#define ADC128D818_REVISION_ID_VAL 0x09 + +/* ADC resolution */ +#define ADC128D818_ADC_RESOLUTION 4096 +#define ADC128D818_ADC_MAX 4095 + +/* Temperature: 0.5 deg C per LSb = 500 milli-degrees per LSb */ +#define ADC128D818_TEMP_LSB_MC 500 +#define ADC128D818_TEMP_RAW_MIN (-256) +#define ADC128D818_TEMP_RAW_MAX 255 + + +OBJECT_DECLARE_SIMPLE_TYPE(ADC128D818State, ADC128D818) + +struct ADC128D818State { + I2CSlave parent_obj; + + qemu_irq irq; + + uint8_t len; + uint8_t pointer; + uint8_t rx_byte; + + uint8_t regs[ADC128D818_NUM_REGS]; + uint16_t channel[ADC128D818_NUM_CHANNELS]; + + int16_t ain[ADC128D818_NUM_CHANNELS]; /* mV */ + int32_t temperature; /* milli-degrees Celsius */ + uint16_t ext_vref; /* mV, 0 means not connected */ + bool temp_alarm; /* temperature high-limit alarm latched */ + + char *description; +}; + +static uint16_t adc128d818_get_vref(const ADC128D818State *s) +{ + if (s->regs[REG_ADV_CONFIG] & ADV_CONFIG_EXT_REF_EN) { + if (s->ext_vref > 0u) { + return s->ext_vref; + } + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: external VREF selected but not" + " connected, falling back to internal\n", + __func__, s->description); + } + + return ADC128D818_INTERNAL_VREF_MV; +} + +static uint8_t adc128d818_get_mode(const ADC128D818State *s) +{ + return (s->regs[REG_ADV_CONFIG] & ADV_CONFIG_MODE_MASK) >> + ADV_CONFIG_MODE_SHIFT; +} + +static bool adc128d818_is_temp_channel(const ADC128D818State *s, unsigned ch) +{ + if (ch != 7u) { + return false; + } + + return adc128d818_get_mode(s) != 1u; +} + +static bool adc128d818_is_reserved_channel(const ADC128D818State *s, + unsigned ch) +{ + switch (adc128d818_get_mode(s)) { + case 2u: + return ch >= 4u && ch <= 6u; + case 3u: + return ch == 6u; + default: + return false; + } +} + +static int16_t adc128d818_channel_voltage(const ADC128D818State *s, unsigned ch) +{ + switch (adc128d818_get_mode(s)) { + case 2u: + switch (ch) { + case 0u: + return (int16_t)(s->ain[0] - s->ain[1]); + case 1u: + return (int16_t)(s->ain[3] - s->ain[2]); + case 2u: + return (int16_t)(s->ain[4] - s->ain[5]); + case 3u: + return (int16_t)(s->ain[7] - s->ain[6]); + default: + return 0; + } + case 3u: + switch (ch) { + case 4u: + return (int16_t)(s->ain[4] - s->ain[5]); + case 5u: + return (int16_t)(s->ain[7] - s->ain[6]); + default: + return s->ain[ch]; + } + default: + return s->ain[ch]; + } +} + +static void adc128d818_update_irq(ADC128D818State *s) +{ + uint8_t cfg = s->regs[REG_CONFIG]; + uint8_t active; + bool level; + + active = s->regs[REG_INT_STATUS] & ~s->regs[REG_INT_MASK]; + + /* INT pin is active-low */ + level = !((cfg & CONFIG_INT_ENABLE) && !(cfg & CONFIG_INT_CLEAR) && + (active != 0u)); + + trace_adc128d818_irq(s->description, level); + qemu_set_irq(s->irq, level); +} + +static bool adc128d818_monitoring_active(const ADC128D818State *s) +{ + if (s->regs[REG_DEEP_SHUTDOWN] & DEEP_SHUTDOWN_EN) { + return false; + } + if (!(s->regs[REG_CONFIG] & CONFIG_START)) { + return false; + } + if (s->regs[REG_CONFIG] & CONFIG_INT_CLEAR) { + return false; + } + + return true; +} + +static void adc128d818_check_limits(ADC128D818State *s) +{ + uint8_t disabled = s->regs[REG_CH_DISABLE]; + uint8_t int_status = 0u; + + for (unsigned ch = 0u; ch < ADC128D818_NUM_CHANNELS; ch++) { + if ((disabled & (1u << ch)) || + adc128d818_is_reserved_channel(s, ch)) { + continue; + } + + if (adc128d818_is_temp_channel(s, ch)) { + int raw = s->temperature / ADC128D818_TEMP_LSB_MC; + int thot; + int thyst; + + raw = MAX(ADC128D818_TEMP_RAW_MIN, + MIN(ADC128D818_TEMP_RAW_MAX, raw)); + thot = (int)(int8_t)s->regs[REG_LIMIT_BASE + ch * 2u] * 2; + thyst = (int)(int8_t)s->regs[REG_LIMIT_BASE + ch * 2u + 1u] * 2; + + if (raw > thot) { + s->temp_alarm = true; + } else if (raw <= thyst) { + s->temp_alarm = false; + } + if (s->temp_alarm) { + int_status |= (1u << ch); + } + } else { + uint8_t msb = (uint8_t)(s->channel[ch] >> 8u); + uint8_t high_lim = s->regs[REG_LIMIT_BASE + ch * 2u]; + uint8_t low_lim = s->regs[REG_LIMIT_BASE + ch * 2u + 1u]; + + if (msb > high_lim || msb <= low_lim) { + int_status |= (1u << ch); + } + } + } + + s->regs[REG_INT_STATUS] = int_status; + adc128d818_update_irq(s); +} + +static void adc128d818_convert(ADC128D818State *s) +{ + uint8_t disabled; + uint16_t vref; + + disabled = s->regs[REG_CH_DISABLE]; + vref = adc128d818_get_vref(s); + + for (unsigned ch = 0u; ch < ADC128D818_NUM_CHANNELS; ch++) { + if ((disabled & (1u << ch)) || + adc128d818_is_reserved_channel(s, ch)) { + continue; + } + + if (adc128d818_is_temp_channel(s, ch)) { + int32_t raw = s->temperature / ADC128D818_TEMP_LSB_MC; + + raw = + MAX(ADC128D818_TEMP_RAW_MIN, MIN(ADC128D818_TEMP_RAW_MAX, raw)); + s->channel[ch] = (uint16_t)(((unsigned)raw & 0x1FFu) << 7u); + } else { + int16_t vin = adc128d818_channel_voltage(s, ch); + int32_t dout; + + dout = vin * (int32_t)ADC128D818_ADC_RESOLUTION / vref; + dout = MAX(0, MIN((int32_t)ADC128D818_ADC_MAX, dout)); + s->channel[ch] = (uint16_t)(dout << 4u); + } + + trace_adc128d818_convert(s->description, ch, s->channel[ch]); + } + + s->regs[REG_BUSY_STATUS] &= ~BUSY_STATUS_NOT_READY; + + adc128d818_check_limits(s); +} + +static uint8_t adc128d818_read_channel(ADC128D818State *s, unsigned ch) +{ + uint8_t val; + + if (s->rx_byte == 0u) { + val = (uint8_t)(s->channel[ch] >> 8u); + trace_adc128d818_read_channel(s->description, ch, s->channel[ch]); + } else { + val = (uint8_t)(s->channel[ch] & 0xFFu); + } + s->rx_byte ^= 1u; + + return val; +} + +static uint8_t adc128d818_read_reg(ADC128D818State *s, uint8_t reg) +{ + uint8_t val; + + switch (reg) { + case REG_INT_STATUS: + val = s->regs[REG_INT_STATUS]; + s->regs[REG_INT_STATUS] = 0x00u; + if (adc128d818_monitoring_active(s)) { + adc128d818_check_limits(s); + } else { + adc128d818_update_irq(s); + } + trace_adc128d818_read(s->description, reg, val); + return val; + case REG_CONFIG: + case REG_INT_MASK: + case REG_CONV_RATE: + case REG_CH_DISABLE: + case REG_ONE_SHOT: + case REG_DEEP_SHUTDOWN: + case REG_ADV_CONFIG: + case REG_BUSY_STATUS: + case REG_LIMIT_BASE ... REG_LIMIT_LAST: + case REG_MANUFACTURER_ID: + case REG_REVISION_ID: + trace_adc128d818_read(s->description, reg, s->regs[reg]); + return s->regs[reg]; + case REG_CH_READING_BASE ... REG_CH_READING_LAST: + return adc128d818_read_channel(s, reg - REG_CH_READING_BASE); + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: read from undefined register 0x%02x\n", + __func__, s->description, reg); + return 0x00u; + } +} + +static void adc128d818_write_reg(ADC128D818State *s, uint8_t reg, uint8_t val); + +static void adc128d818_reset_regs(ADC128D818State *s) +{ + memset(s->regs, 0, sizeof(s->regs)); + memset(s->channel, 0, sizeof(s->channel)); + s->temp_alarm = false; + + s->regs[REG_CONFIG] = 0x08u; + s->regs[REG_BUSY_STATUS] = 0x02u; + s->regs[REG_MANUFACTURER_ID] = ADC128D818_MANUFACTURER_ID_VAL; + s->regs[REG_REVISION_ID] = ADC128D818_REVISION_ID_VAL; + + for (unsigned ch = 0u; ch < ADC128D818_NUM_CHANNELS; ch++) { + s->regs[REG_LIMIT_BASE + ch * 2u] = 0xFFu; + } + + s->pointer = 0x00u; + s->len = 0u; + s->rx_byte = 0u; + + adc128d818_update_irq(s); +} + +static void adc128d818_write_reg(ADC128D818State *s, uint8_t reg, uint8_t val) +{ + trace_adc128d818_write(s->description, reg, val); + + switch (reg) { + case REG_CONFIG: + if (val & CONFIG_INITIALIZATION) { + trace_adc128d818_reset(s->description, "reg"); + adc128d818_reset_regs(s); + break; + } + s->regs[REG_CONFIG] = val & CONFIG_WR_MASK; + if ((val & CONFIG_START) && !(val & CONFIG_INT_CLEAR) && + !(s->regs[REG_DEEP_SHUTDOWN] & DEEP_SHUTDOWN_EN)) { + adc128d818_convert(s); + } + adc128d818_update_irq(s); + break; + case REG_INT_MASK: + s->regs[REG_INT_MASK] = val; + adc128d818_update_irq(s); + break; + case REG_CONV_RATE: + if (s->regs[REG_CONFIG] & CONFIG_START) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: CONV_RATE written while running\n", + __func__, s->description); + break; + } + s->regs[REG_CONV_RATE] = val & CONV_RATE_MASK; + break; + case REG_CH_DISABLE: + if (s->regs[REG_CONFIG] & CONFIG_START) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: CH_DISABLE written while running\n", + __func__, s->description); + break; + } + s->regs[REG_CH_DISABLE] = val; + memset(s->channel, 0, sizeof(s->channel)); + s->regs[REG_INT_STATUS] = 0x00u; + s->temp_alarm = false; + adc128d818_update_irq(s); + break; + case REG_ONE_SHOT: + if (!(s->regs[REG_CONFIG] & CONFIG_START)) { + adc128d818_convert(s); + } + break; + case REG_DEEP_SHUTDOWN: + if ((val & DEEP_SHUTDOWN_EN) && (s->regs[REG_CONFIG] & CONFIG_START)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: DEEP_SHUTDOWN set while running\n", + __func__, s->description); + break; + } + s->regs[REG_DEEP_SHUTDOWN] = val & DEEP_SHUTDOWN_EN; + break; + case REG_ADV_CONFIG: + if (s->regs[REG_CONFIG] & CONFIG_START) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: ADV_CONFIG written while running\n", + __func__, s->description); + break; + } + s->regs[REG_ADV_CONFIG] = val & ADV_CONFIG_WR_MASK; + memset(s->channel, 0, sizeof(s->channel)); + s->regs[REG_INT_STATUS] = 0x00u; + s->temp_alarm = false; + adc128d818_update_irq(s); + break; + case REG_LIMIT_BASE ... REG_LIMIT_LAST: + s->regs[reg] = val; + break; + case REG_INT_STATUS: + case REG_BUSY_STATUS: + case REG_MANUFACTURER_ID: + case REG_REVISION_ID: + case REG_CH_READING_BASE ... REG_CH_READING_LAST: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: write to read-only register 0x%02x\n", + __func__, s->description, reg); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: write to undefined register 0x%02x\n", + __func__, s->description, reg); + break; + } +} + +static uint8_t adc128d818_recv(I2CSlave *i2c) +{ + ADC128D818State *s = ADC128D818(i2c); + + return adc128d818_read_reg(s, s->pointer); +} + +static int adc128d818_send(I2CSlave *i2c, uint8_t data) +{ + ADC128D818State *s = ADC128D818(i2c); + + if (s->len == 0u) { + s->pointer = data; + s->len++; + } else { + adc128d818_write_reg(s, s->pointer, data); + } + + return 0; +} + +static int adc128d818_event(I2CSlave *i2c, enum i2c_event event) +{ + ADC128D818State *s = ADC128D818(i2c); + + s->len = 0u; + s->rx_byte = 0u; + + return 0; +} + +static void adc128d818_get_ain(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + ADC128D818State *s = ADC128D818(obj); + int64_t value; + int ch_num; + int rc; + + rc = sscanf(name, "ain%d", &ch_num); + if (rc != 1 || ch_num < 0 || ch_num >= (int)ADC128D818_NUM_CHANNELS) { + error_setg(errp, "%s: %s: invalid channel '%s'", __func__, + s->description, name); + return; + } + + value = s->ain[ch_num]; + visit_type_int(v, name, &value, errp); +} + +static void adc128d818_set_ain(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + ADC128D818State *s = ADC128D818(obj); + int64_t value; + int ch_num; + int rc; + + if (!visit_type_int(v, name, &value, errp)) { + return; + } + + rc = sscanf(name, "ain%d", &ch_num); + if (rc != 1 || ch_num < 0 || ch_num >= (int)ADC128D818_NUM_CHANNELS) { + error_setg(errp, "%s: %s: invalid channel '%s'", __func__, + s->description, name); + return; + } + + if (value < INT16_MIN || value > INT16_MAX) { + error_setg(errp, "%s: %s: value %" PRId64 " out of range for '%s'", + __func__, s->description, value, name); + return; + } + + s->ain[ch_num] = (int16_t)value; + + if (adc128d818_monitoring_active(s)) { + adc128d818_convert(s); + } +} + +static void adc128d818_get_temperature( + Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) +{ + ADC128D818State *s = ADC128D818(obj); + int64_t value = s->temperature; + + visit_type_int(v, name, &value, errp); +} + +static void adc128d818_set_temperature( + Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) +{ + ADC128D818State *s = ADC128D818(obj); + int64_t value; + + if (!visit_type_int(v, name, &value, errp)) { + return; + } + + if (value < INT32_MIN || value > INT32_MAX) { + error_setg(errp, "%s: %s: value %" PRId64 " out of range", __func__, + s->description, value); + return; + } + + s->temperature = (int32_t)value; + + if (adc128d818_monitoring_active(s)) { + adc128d818_convert(s); + } +} + +static const VMStateDescription adc128d818_vmstate = { + .name = "ADC128D818", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT8(len, ADC128D818State), + VMSTATE_UINT8(pointer, ADC128D818State), + VMSTATE_UINT8(rx_byte, ADC128D818State), + VMSTATE_UINT8_ARRAY(regs, ADC128D818State, + ADC128D818_NUM_REGS), + VMSTATE_UINT16_ARRAY(channel, ADC128D818State, + ADC128D818_NUM_CHANNELS), + VMSTATE_INT16_ARRAY(ain, ADC128D818State, + ADC128D818_NUM_CHANNELS), + VMSTATE_INT32(temperature, ADC128D818State), + VMSTATE_UINT16(ext_vref, ADC128D818State), + VMSTATE_BOOL(temp_alarm, ADC128D818State), + VMSTATE_I2C_SLAVE(parent_obj, ADC128D818State), + VMSTATE_END_OF_LIST() + } +}; + +static void adc128d818_reset_hold(Object *obj, ResetType type) +{ + ADC128D818State *s = ADC128D818(obj); + + trace_adc128d818_reset(s->description, "hw"); + adc128d818_reset_regs(s); +} + +static void adc128d818_get_ext_vref( + Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) +{ + ADC128D818State *s = ADC128D818(obj); + int64_t value = (int64_t)s->ext_vref; + + visit_type_int(v, name, &value, errp); +} + +static void adc128d818_set_ext_vref( + Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) +{ + ADC128D818State *s = ADC128D818(obj); + int64_t value; + + if (!visit_type_int(v, name, &value, errp)) { + return; + } + + if (value < 0 || value > ADC128D818_MAX_VDD_MV) { + error_setg(errp, + "%s: %s: ext-vref-mv %" PRId64 " out of range (0..%u mV)", + __func__, s->description, value, ADC128D818_MAX_VDD_MV); + return; + } + + s->ext_vref = (uint16_t)value; + + if (adc128d818_monitoring_active(s)) { + adc128d818_convert(s); + } +} + +static void adc128d818_initfn(Object *obj) +{ + for (unsigned ch = 0u; ch < ADC128D818_NUM_CHANNELS; ch++) { + char *name = g_strdup_printf("ain%u", ch); + + object_property_add(obj, name, "int", adc128d818_get_ain, + adc128d818_set_ain, NULL, NULL); + g_free(name); + } + + object_property_add(obj, "temperature", "int", adc128d818_get_temperature, + adc128d818_set_temperature, NULL, NULL); + object_property_add(obj, "ext-vref-mv", "int", adc128d818_get_ext_vref, + adc128d818_set_ext_vref, NULL, NULL); +} + +static void adc128d818_realize(DeviceState *dev, Error **errp) +{ + ADC128D818State *s = ADC128D818(dev); + + if (!s->description) { + s->description = g_strdup(object_get_typename(OBJECT(dev))); + } + + qdev_init_gpio_out(dev, &s->irq, 1u); +} + +static const Property adc128d818_properties[] = { + DEFINE_PROP_STRING("description", ADC128D818State, description), +}; + +static void adc128d818_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *ic = I2C_SLAVE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + ic->event = adc128d818_event; + ic->recv = adc128d818_recv; + ic->send = adc128d818_send; + dc->realize = adc128d818_realize; + rc->phases.hold = adc128d818_reset_hold; + dc->vmsd = &adc128d818_vmstate; + device_class_set_props(dc, adc128d818_properties); +} + +static const TypeInfo adc128d818_types[] = { + { + .name = TYPE_ADC128D818, + .parent = TYPE_I2C_SLAVE, + .instance_init = adc128d818_initfn, + .instance_size = sizeof(ADC128D818State), + .class_init = adc128d818_class_init, + }, +}; + +DEFINE_TYPES(adc128d818_types) diff --git a/hw/sensor/meson.build b/hw/sensor/meson.build index 420fdc3359..fe36c9ef91 100644 --- a/hw/sensor/meson.build +++ b/hw/sensor/meson.build @@ -1,3 +1,4 @@ +system_ss.add(when: 'CONFIG_ADC128D818', if_true: files('adc128d818.c')) system_ss.add(when: 'CONFIG_TMP105', if_true: files('tmp105.c')) system_ss.add(when: 'CONFIG_TMP421', if_true: files('tmp421.c')) system_ss.add(when: 'CONFIG_DPS310', if_true: files('dps310.c')) diff --git a/hw/sensor/trace-events b/hw/sensor/trace-events index a3fe54fa6d..5a3630f7bb 100644 --- a/hw/sensor/trace-events +++ b/hw/sensor/trace-events @@ -1,5 +1,13 @@ # See docs/devel/tracing.rst for syntax documentation. +# adc128d818.c +adc128d818_read(const char *id, uint8_t reg, uint8_t value) "%s reg 0x%02x val 0x%02x" +adc128d818_read_channel(const char *id, uint8_t channel, uint16_t value) "%s ch %u val 0x%04x" +adc128d818_write(const char *id, uint8_t reg, uint8_t value) "%s reg 0x%02x val 0x%02x" +adc128d818_convert(const char *id, uint8_t channel, uint16_t value) "%s ch %u val 0x%04x" +adc128d818_irq(const char *id, bool level) "%s level %u" +adc128d818_reset(const char *id, const char *source) "%s %s" + # tmp105.c tmp105_read(uint8_t dev, uint8_t addr) "device: 0x%02x, addr: 0x%02x" tmp105_write(uint8_t dev, uint8_t addr) "device: 0x%02x, addr 0x%02x" diff --git a/hw/sparc64/niagara.c b/hw/sparc64/niagara.c index 1211ecb82d..ad5e5945a8 100644 --- a/hw/sparc64/niagara.c +++ b/hw/sparc64/niagara.c @@ -137,7 +137,7 @@ static void niagara_init(MachineState *machine) outside of the partition RAM */ if (dinfo) { BlockBackend *blk = blk_by_legacy_dinfo(dinfo); - int size = blk_getlength(blk); + int64_t size = blk_getlength(blk); if (size > 0) { memory_region_init_ram(&s->vdisk_ram, NULL, "sun4v_vdisk.ram", size, &error_fatal); diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index d69ed9a81a..a348096aee 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -719,6 +719,13 @@ static void sun4uv_init(MemoryRegion *address_space_mem, fw_cfg_add_i16(fw_cfg, FW_CFG_SPARC64_DEPTH, graphic_depth); qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); + + /* + * Mark internal PCI busses as full so that the plugging of additional + * PCI devices happens on the right bus that still has free slots: + */ + qbus_mark_full(&pci_bus->qbus); + qbus_mark_full(&pci_busA->qbus); } enum { diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index c8cc6cfa56..bf596f7b2d 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -163,6 +163,9 @@ /* Read Timing Compensation Register */ #define R_TIMINGS (0x94 / 4) +/* Data fifo */ +#define R_DATA_FIFO (0x200 / 4) + /* SPI controller registers and bits (AST2400) */ #define R_SPI_CONF (0x00 / 4) #define SPI_CONF_ENABLE_W0 0 @@ -209,6 +212,7 @@ static const AspeedSegments aspeed_2500_spi2_segments[]; #define ASPEED_SMC_FEATURE_DMA_GRANT 0x2 #define ASPEED_SMC_FEATURE_WDT_CONTROL 0x4 #define ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH 0x08 +#define ASPEED_SMC_FEATURE_DATA_FIFO 0x10 static inline bool aspeed_smc_has_dma(const AspeedSMCClass *asc) { @@ -225,6 +229,11 @@ static inline bool aspeed_smc_has_dma64(const AspeedSMCClass *asc) return !!(asc->features & ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH); } +static inline bool aspeed_smc_has_data_fifo(const AspeedSMCClass *asc) +{ + return !!(asc->features & ASPEED_SMC_FEATURE_DATA_FIFO); +} + #define aspeed_smc_error(fmt, ...) \ qemu_log_mask(LOG_GUEST_ERROR, "%s: " fmt "\n", __func__, ## __VA_ARGS__) @@ -664,6 +673,7 @@ static MemTxResult aspeed_smc_read(void *opaque, hwaddr addr, uint64_t *data, { AspeedSMCState *s = ASPEED_SMC(opaque); AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(opaque); + int cs; addr >>= 2; @@ -689,6 +699,18 @@ static MemTxResult aspeed_smc_read(void *opaque, hwaddr addr, uint64_t *data, trace_aspeed_smc_read(addr << 2, size, s->regs[addr]); *data = s->regs[addr]; + } else if (aspeed_smc_has_data_fifo(asc) && addr >= R_DATA_FIFO) { + cs = asc->data_fifo_offset_to_cs(s, addr << 2); + if (cs >= 0) { + /* + * Data fifo mode only supports SPI user mode. + * The flash address is provided by the SPI command/address cycles, + * the MMIO addr parameter is ignored. + */ + return aspeed_smc_flash_read(&s->flashes[cs], 0, data, size, attrs); + } + aspeed_smc_error("Invalid data fifo offset %" HWADDR_PRIx, addr << 2); + return MEMTX_ERROR; } else { qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n", __func__, addr); @@ -1063,6 +1085,19 @@ static MemTxResult aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data, } else if (aspeed_smc_has_dma(asc) && aspeed_smc_has_dma64(asc) && addr == R_DMA_DRAM_ADDR_HIGH) { s->regs[addr] = DMA_DRAM_ADDR_HIGH(value); + } else if (aspeed_smc_has_data_fifo(asc) && addr >= R_DATA_FIFO) { + int cs = asc->data_fifo_offset_to_cs(s, addr << 2); + if (cs >= 0) { + /* + * Data fifo mode only supports SPI user mode. + * The flash address is provided by the SPI command/address cycles, + * the MMIO addr parameter is ignored. + */ + return aspeed_smc_flash_write(&s->flashes[cs], 0, data, size, + attrs); + } + aspeed_smc_error("Invalid data fifo offset %" HWADDR_PRIx, addr << 2); + return MEMTX_ERROR; } else { qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n", __func__, addr); @@ -1183,8 +1218,8 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) static const VMStateDescription vmstate_aspeed_smc = { .name = "aspeed.smc", - .version_id = 3, - .minimum_version_id = 1, + .version_id = 4, + .minimum_version_id = 2, .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AspeedSMCState, ASPEED_SMC_R_MAX), VMSTATE_UNUSED_V(2, 2), /* was snoop_index/snoop_dummies */ @@ -1808,6 +1843,39 @@ static void aspeed_2700_smc_reg_to_segment(const AspeedSMCState *s, } } +/* + * Convert a data fifo offset to a chip select (CS). + * + * Data fifo access starts at 0x200. The data fifo offset index is + * calculated by subtracting the data fifo base offset from the MMIO address. + * + * The data fifo offset index increments by 1 for every 16MB of flash address + * space. Each offset step therefore represents a 16MB address decode range. + * + * The CS is determined by matching the data fifo offset index against the + * segment start address of each CS. + * + * Returns the CS index on success, or -1 if the offset is invalid. + */ +static int aspeed_2700_smc_data_fifo_offset_to_cs(const AspeedSMCState *s, + uint32_t offset) +{ + AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); + uint32_t start_offset; + uint32_t fifo_offset; + int i; + + for (i = 0; i < asc->cs_num_max; i++) { + start_offset = (s->regs[R_SEG_ADDR0 + i] & 0x0000ffff) << 16; + fifo_offset = start_offset / 0x1000000; + if (fifo_offset == offset - (R_DATA_FIFO << 2)) { + return i; + } + } + + return -1; +} + static const uint32_t aspeed_2700_fmc_resets[ASPEED_SMC_R_MAX] = { [R_CONF] = (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0 | CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE1), @@ -1842,6 +1910,27 @@ static const AspeedSegments aspeed_2700_fmc_segments[] = { { 0x0, 0 }, /* disabled */ }; +/* + * AST2700 supports data fifo mode with a base data fifo start offset of 0x200. + * + * The data fifo start offset increments by 1 for every 16MB of flash address + * space. Each offset step therefore represents a 16MB address decode range. + * + * Assuming each chip select (CS) can use the maximum flash size of 256MB: + * 256MB / 16MB = 0x10 offset steps per CS. + * + * Data fifo start offset for CSn: + * 0x200 + (n * 0x10) + * + * Examples: + * CS0: 0x200 + * CS1: 0x210 + * CS2: 0x220 + * CS3: 0x230 + * + * asc->nregs should be set to: 0x200 + (asc->cs_num_max * 0x10) + * to cover all possible data fifo regions. + */ static void aspeed_2700_fmc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1861,14 +1950,16 @@ static void aspeed_2700_fmc_class_init(ObjectClass *klass, const void *data) asc->flash_window_base = 0x100000000; asc->flash_window_size = 1 * GiB; asc->features = ASPEED_SMC_FEATURE_DMA | - ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH; + ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH | + ASPEED_SMC_FEATURE_DATA_FIFO; asc->dma_flash_mask = 0x2FFFFFFC; asc->dma_dram_mask = 0xFFFFFFFC; asc->dma_start_length = 1; - asc->nregs = ASPEED_SMC_R_MAX; + asc->nregs = (0x200 + (asc->cs_num_max * 0x10)) >> 2; asc->segment_to_reg = aspeed_2700_smc_segment_to_reg; asc->reg_to_segment = aspeed_2700_smc_reg_to_segment; asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->data_fifo_offset_to_cs = aspeed_2700_smc_data_fifo_offset_to_cs; asc->reg_ops = &aspeed_2700_smc_flash_ops; } @@ -1896,14 +1987,16 @@ static void aspeed_2700_spi0_class_init(ObjectClass *klass, const void *data) asc->flash_window_base = 0x180000000; asc->flash_window_size = 1 * GiB; asc->features = ASPEED_SMC_FEATURE_DMA | - ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH; + ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH | + ASPEED_SMC_FEATURE_DATA_FIFO; asc->dma_flash_mask = 0x2FFFFFFC; asc->dma_dram_mask = 0xFFFFFFFC; asc->dma_start_length = 1; - asc->nregs = ASPEED_SMC_R_MAX; + asc->nregs = (0x200 + (asc->cs_num_max * 0x10)) >> 2; asc->segment_to_reg = aspeed_2700_smc_segment_to_reg; asc->reg_to_segment = aspeed_2700_smc_reg_to_segment; asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->data_fifo_offset_to_cs = aspeed_2700_smc_data_fifo_offset_to_cs; asc->reg_ops = &aspeed_2700_smc_flash_ops; } @@ -1930,14 +2023,16 @@ static void aspeed_2700_spi1_class_init(ObjectClass *klass, const void *data) asc->flash_window_base = 0x200000000; asc->flash_window_size = 1 * GiB; asc->features = ASPEED_SMC_FEATURE_DMA | - ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH; + ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH | + ASPEED_SMC_FEATURE_DATA_FIFO; asc->dma_flash_mask = 0x2FFFFFFC; asc->dma_dram_mask = 0xFFFFFFFC; asc->dma_start_length = 1; - asc->nregs = ASPEED_SMC_R_MAX; + asc->nregs = (0x200 + (asc->cs_num_max * 0x10)) >> 2; asc->segment_to_reg = aspeed_2700_smc_segment_to_reg; asc->reg_to_segment = aspeed_2700_smc_reg_to_segment; asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->data_fifo_offset_to_cs = aspeed_2700_smc_data_fifo_offset_to_cs; asc->reg_ops = &aspeed_2700_smc_flash_ops; } @@ -1964,14 +2059,16 @@ static void aspeed_2700_spi2_class_init(ObjectClass *klass, const void *data) asc->flash_window_base = 0x280000000; asc->flash_window_size = 1 * GiB; asc->features = ASPEED_SMC_FEATURE_DMA | - ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH; + ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH | + ASPEED_SMC_FEATURE_DATA_FIFO; asc->dma_flash_mask = 0x0FFFFFFC; asc->dma_dram_mask = 0xFFFFFFFC; asc->dma_start_length = 1; - asc->nregs = ASPEED_SMC_R_MAX; + asc->nregs = (0x200 + (asc->cs_num_max * 0x10)) >> 2; asc->segment_to_reg = aspeed_2700_smc_segment_to_reg; asc->reg_to_segment = aspeed_2700_smc_reg_to_segment; asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->data_fifo_offset_to_cs = aspeed_2700_smc_data_fifo_offset_to_cs; asc->reg_ops = &aspeed_2700_smc_flash_ops; } diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig index b3d823ce2c..e1b751a54a 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -65,3 +65,6 @@ config STELLARIS_GPTM config AVR_TIMER16 bool + +config HEX_QTIMER + bool diff --git a/hw/timer/meson.build b/hw/timer/meson.build index 201b5d8316..8323efaf46 100644 --- a/hw/timer/meson.build +++ b/hw/timer/meson.build @@ -34,3 +34,5 @@ specific_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_timer.c')) system_ss.add(when: 'CONFIG_SIFIVE_PWM', if_true: files('sifive_pwm.c')) system_ss.add(when: 'CONFIG_AVR_TIMER16', if_true: files('avr_timer16.c')) + +system_ss.add(when: 'CONFIG_HEX_QTIMER', if_true: files('qct-qtimer.c')) diff --git a/hw/timer/qct-qtimer.c b/hw/timer/qct-qtimer.c new file mode 100644 index 0000000000..f628e8d263 --- /dev/null +++ b/hw/timer/qct-qtimer.c @@ -0,0 +1,667 @@ +/* + * Qualcomm QCT QTimer + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/core/irq.h" +#include "hw/core/qdev-properties.h" +#include "hw/core/sysbus.h" +#include "hw/timer/qct-qtimer.h" +#include "migration/vmstate.h" +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/timer.h" +#include "qapi/error.h" +#include "trace.h" + +#define QTIMER_MEM_SIZE_BYTES 0x1000 +#define QTIMER_DEFAULT_FREQ_HZ 19200000ULL + +#define QCT_QTIMER_TIMER_FRAME_ELTS (16) +#define QCT_QTIMER_TIMER_VIEW_ELTS (2) + +#define QCT_QTIMER_AC_CNTFRQ (0x000) +#define QCT_QTIMER_AC_CNTSR (0x004) +#define QCT_QTIMER_AC_CNTTID_0 (0x08) +#define QCT_QTIMER_AC_CNTACR_START (0x40) +#define QCT_QTIMER_AC_CNTACR_END (0x5c) +#define QCT_QTIMER_AC_CNTTID_1 (0x108) +#define QCT_QTIMER_AC_CNTACR_RWPT (1 << 5) /* R/W of CNTP_* regs */ +#define QCT_QTIMER_AC_CNTACR_RWVT (1 << 4) /* R/W of CNTV_* regs */ +#define QCT_QTIMER_AC_CNTACR_RVOFF (1 << 3) /* R/W of CNTVOFF register */ +#define QCT_QTIMER_AC_CNTACR_RFRQ (1 << 2) /* R/W of CNTFRQ register */ +#define QCT_QTIMER_AC_CNTACR_RPVCT (1 << 1) /* R/W of CNTVCT register */ +#define QCT_QTIMER_AC_CNTACR_RPCT (1 << 0) /* R/W of CNTPCT register */ +#define QCT_QTIMER_VERSION (0x0fd0) + +#define QCT_QTIMER_CNTPCT_LO (0x000) +#define QCT_QTIMER_CNTPCT_HI (0x004) +#define QCT_QTIMER_CNT_FREQ (0x010) +#define QCT_QTIMER_CNTPL0ACR (0x014) +#define QCT_QTIMER_CNTPL0ACR_PL0CTEN (1 << 9) +#define QCT_QTIMER_CNTPL0ACR_PL0TVEN (1 << 8) +#define QCT_QTIMER_CNTPL0ACR_PL0VCTEN (1 << 1) +#define QCT_QTIMER_CNTPL0ACR_PL0PCTEN (1 << 0) +#define QCT_QTIMER_CNTP_CVAL_LO (0x020) +#define QCT_QTIMER_CNTP_CVAL_HI (0x024) +#define QCT_QTIMER_CNT_MASK 0x00ffffffffffffffULL +#define QCT_QTIMER_CNT_HI_BITS 24 +#define QCT_QTIMER_CNTP_TVAL (0x028) +#define QCT_QTIMER_CNTP_CTL (0x02c) +#define QCT_QTIMER_CNTP_CTL_ENABLE (1 << 0) +#define QCT_QTIMER_CNTP_CTL_INTEN (1 << 1) +#define QCT_QTIMER_CNTP_CTL_ISTAT (1 << 2) + +OBJECT_DECLARE_SIMPLE_TYPE(QCTQtimerState, QCT_QTIMER) + +typedef struct QCTHextimerState { + QCTQtimerState *qtimer; + QEMUTimer *timer; /* one-shot deadline timer */ + int64_t offset_ns; /* QEMU_CLOCK_VIRTUAL ns at which cntpct == 0 */ + uint64_t cntval; /* 64-bit physical timer compare value */ + uint32_t control; + uint32_t cnt_ctrl; + uint32_t cntpl0acr; + uint32_t int_level; + qemu_irq irq; +} QCTHextimerState; + +struct QCTQtimerState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + MemoryRegion view_iomem; + uint32_t secure; + QCTHextimerState timer[QCT_QTIMER_TIMER_FRAME_ELTS]; + uint32_t freq_hz; + uint32_t nr_frames; + uint32_t nr_views; + uint32_t frame_stride; + uint32_t freq_scale; +}; + +/* + * QTimer version register: + * + * 3 2 1 + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Major | Minor | Step | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +#define QCT_QTIMER_VERSION_VALUE 0x20020000 + +static uint32_t qct_qtimer_cnttid(QCTQtimerState *s, unsigned int half) +{ + uint32_t nibble = 0x1 | (s->nr_views > 1 ? 0x4 : 0x0); + uint32_t base = half * 8; + uint32_t cnttid = 0; + unsigned int i; + + for (i = 0; i < 8; i++) { + if (base + i < s->nr_frames) { + cnttid |= nibble << (i * 4); + } + } + return cnttid; +} + +/* Counter value derived on-demand from QEMU_CLOCK_VIRTUAL. */ +static uint64_t hex_timer_now(QCTHextimerState *s) +{ + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + uint32_t scale; + uint64_t scaled_elapsed; + + if (now <= s->offset_ns) { + return 0; + } + scale = MAX(s->qtimer->freq_scale, 1u); + scaled_elapsed = (uint64_t)(now - s->offset_ns) / scale; + return muldiv64(scaled_elapsed, s->qtimer->freq_hz, + NANOSECONDS_PER_SECOND) & + QCT_QTIMER_CNT_MASK; +} + +/* Arm (or disarm) the one-shot deadline timer. */ +static void hex_timer_rearm(QCTHextimerState *s) +{ + uint32_t scale; + uint64_t base_ns; + int64_t deadline_ns; + + if (!(s->control & QCT_QTIMER_CNTP_CTL_ENABLE)) { + timer_del(s->timer); + return; + } + + scale = MAX(s->qtimer->freq_scale, 1u); + /* + * Round the ticks-to-ns conversion up so that hex_timer_now(), which + * truncates when it divides elapsed ns by scale, is guaranteed to + * report >= cntval once this deadline fires. A truncating conversion + * here could re-arm at the same deadline forever when scale > 1. + */ + base_ns = muldiv64_round_up(s->cntval, NANOSECONDS_PER_SECOND, + s->qtimer->freq_hz); + if (base_ns > + ((uint64_t)INT64_MAX - (uint64_t)s->offset_ns) / scale) { + timer_del(s->timer); + return; + } + deadline_ns = s->offset_ns + (int64_t)(base_ns * scale); + timer_mod(s->timer, deadline_ns); +} + +static void hex_timer_update(QCTHextimerState *s) +{ + int level = s->int_level && + (s->control & QCT_QTIMER_CNTP_CTL_ENABLE) && + !(s->control & QCT_QTIMER_CNTP_CTL_INTEN); + + trace_qtimer_interrupt(); + qemu_set_irq(s->irq, level); +} + +/* + * Access-control (AC) region: offsets below 0x1000, gates CNTFRQ/CNTSR/ + * CNTTID/CNTACR per frame plus the shared VERSION register. + */ +static uint64_t qct_qtimer_ac_read(void *opaque, hwaddr offset, unsigned size) +{ + QCTQtimerState *s = opaque; + uint32_t frame; + + switch (offset) { + case QCT_QTIMER_AC_CNTFRQ: + return s->freq_hz; + case QCT_QTIMER_AC_CNTSR: + return s->secure; + case QCT_QTIMER_AC_CNTTID_0: + return qct_qtimer_cnttid(s, 0); + case QCT_QTIMER_AC_CNTTID_1: + return qct_qtimer_cnttid(s, 1); + case QCT_QTIMER_AC_CNTACR_START ... QCT_QTIMER_AC_CNTACR_END: + frame = (offset - QCT_QTIMER_AC_CNTACR_START) / 4; + if (frame >= s->nr_frames) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad CNTACR offset 0x%x\n", + __func__, (int)offset); + return 0; + } + return s->timer[frame].cnt_ctrl; + case QCT_QTIMER_VERSION: + return QCT_QTIMER_VERSION_VALUE; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad offset 0x%x\n", __func__, + (int)offset); + return 0; + } +} + +static void qct_qtimer_ac_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + QCTQtimerState *s = opaque; + uint32_t frame; + + switch (offset) { + case QCT_QTIMER_AC_CNTFRQ: + if (value == 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad CNTFRQ value 0\n", + __func__); + return; + } + s->freq_hz = value; + return; + case QCT_QTIMER_AC_CNTSR: + if (value > 0xff) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad CNTSR value 0x%x\n", + __func__, (int)value); + return; + } + s->secure = value; + return; + case QCT_QTIMER_AC_CNTACR_START ... QCT_QTIMER_AC_CNTACR_END: + frame = (offset - QCT_QTIMER_AC_CNTACR_START) / 4; + if (frame >= s->nr_frames) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad CNTACR offset 0x%x\n", + __func__, (int)offset); + return; + } + s->timer[frame].cnt_ctrl = value; + return; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad offset 0x%x\n", __func__, + (int)offset); + return; + } +} + +static const MemoryRegionOps qct_qtimer_ac_ops = { + .read = qct_qtimer_ac_read, + .write = qct_qtimer_ac_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +/* + * View region: a flat array of (frame, view) slots, each frame_stride + * bytes wide, holding the per-frame CNTPCT/CNTP_CVAL/CNTP_TVAL/CNTP_CTL + * register set. + */ +static QCTHextimerState *qct_qtimer_demux(QCTQtimerState *s, hwaddr offset, + uint32_t *reg_offset, + uint32_t *view) +{ + uint32_t stride = s->frame_stride; + uint32_t stride_shift = ctz32(stride); + uint32_t slot_nr = offset >> stride_shift; + uint32_t frame = slot_nr / s->nr_views; + + *reg_offset = offset & (stride - 1); + *view = slot_nr % s->nr_views; + if (frame >= s->nr_frames) { + return NULL; + } + return &s->timer[frame]; +} + +/* Frames 8+ are described by CNTTID_1; each frame's 2nd view is a gated bit. */ +static bool qct_qtimer_view_visible(QCTQtimerState *s, uint32_t frame, + uint32_t view) +{ + uint32_t cnttid = qct_qtimer_cnttid(s, frame < 8 ? 0 : 1); + uint32_t frame_idx = frame < 8 ? frame : frame - 8; + + return !view || (cnttid & (0x4 << (frame_idx * 4))); +} + +static bool access_ok(QCTHextimerState *s, uint32_t reg_offset, uint32_t view) +{ + uint32_t acr; + uint32_t pl0acr; + + switch (reg_offset) { + case QCT_QTIMER_CNT_FREQ: + acr = QCT_QTIMER_AC_CNTACR_RFRQ; + pl0acr = QCT_QTIMER_CNTPL0ACR_PL0PCTEN | + QCT_QTIMER_CNTPL0ACR_PL0VCTEN; + break; + case QCT_QTIMER_CNTPCT_LO: + case QCT_QTIMER_CNTPCT_HI: + acr = QCT_QTIMER_AC_CNTACR_RPCT; + pl0acr = QCT_QTIMER_CNTPL0ACR_PL0PCTEN; + break; + case QCT_QTIMER_CNTP_CVAL_LO: + case QCT_QTIMER_CNTP_CVAL_HI: + case QCT_QTIMER_CNTP_TVAL: + case QCT_QTIMER_CNTP_CTL: + acr = QCT_QTIMER_AC_CNTACR_RWPT; + pl0acr = QCT_QTIMER_CNTPL0ACR_PL0CTEN; + break; + default: + /* CNTPL0ACR and VERSION are ungated. */ + return true; + } + + if (!(s->cnt_ctrl & acr)) { + return false; + } + return !view || (s->cntpl0acr & pl0acr); +} + +static MemTxResult hex_timer_read(void *opaque, hwaddr offset, uint64_t *data, + unsigned size, MemTxAttrs attrs) +{ + QCTQtimerState *qs = opaque; + uint32_t reg_offset; + uint32_t view; + QCTHextimerState *s = qct_qtimer_demux(qs, offset, ®_offset, &view); + uint32_t frame; + + if (!s) { + *data = 0; + return MEMTX_ACCESS_ERROR; + } + frame = s - qs->timer; + + trace_qtimer_read(offset); + + if (!qct_qtimer_view_visible(qs, frame, view)) { + *data = 0; + return MEMTX_OK; + } + + if (!access_ok(s, reg_offset, view)) { + return MEMTX_ACCESS_ERROR; + } + + switch (reg_offset) { + case QCT_QTIMER_CNT_FREQ: + *data = s->qtimer->freq_hz; + return MEMTX_OK; + case QCT_QTIMER_CNTP_CVAL_LO: + *data = extract64(s->cntval, 0, 32); + return MEMTX_OK; + case QCT_QTIMER_CNTP_CVAL_HI: + /* HI half is 24-bit per TRM; bits [31:24] are reserved. */ + *data = extract64(s->cntval, 32, QCT_QTIMER_CNT_HI_BITS); + return MEMTX_OK; + case QCT_QTIMER_CNTPCT_LO: + *data = extract64(hex_timer_now(s), 0, 32); + return MEMTX_OK; + case QCT_QTIMER_CNTPCT_HI: + *data = extract64(hex_timer_now(s), 32, QCT_QTIMER_CNT_HI_BITS); + return MEMTX_OK; + case QCT_QTIMER_CNTP_TVAL: + *data = (uint32_t)(int32_t)(int64_t)(s->cntval - hex_timer_now(s)); + return MEMTX_OK; + case QCT_QTIMER_CNTP_CTL: + /* + * CNTP_CTL: bit 0 EN, bit 1 IMASK, bit 2 ISTAT (interrupt + * pending). ISTAT tracks int_level and is read-only. + */ + *data = s->control | ((s->int_level & 0x1) << 2); + return MEMTX_OK; + case QCT_QTIMER_CNTPL0ACR: + *data = view ? 0 : s->cntpl0acr; + return MEMTX_OK; + case QCT_QTIMER_VERSION: + *data = QCT_QTIMER_VERSION_VALUE; + return MEMTX_OK; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad offset 0x%x\n", __func__, + (int)offset); + *data = 0; + return MEMTX_ACCESS_ERROR; + } +} + +static MemTxResult hex_timer_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size, + MemTxAttrs attrs) +{ + QCTQtimerState *qs = opaque; + uint32_t reg_offset; + uint32_t view; + QCTHextimerState *s = qct_qtimer_demux(qs, offset, ®_offset, &view); + uint32_t frame; + + if (!s) { + return MEMTX_ACCESS_ERROR; + } + frame = s - qs->timer; + + trace_qtimer_write(offset, value); + + if (!qct_qtimer_view_visible(qs, frame, view)) { + return MEMTX_OK; + } + + if (!access_ok(s, reg_offset, view)) { + return MEMTX_ACCESS_ERROR; + } + + switch (reg_offset) { + case QCT_QTIMER_CNTP_CVAL_LO: + s->int_level = 0; + s->cntval = deposit64(s->cntval, 0, 32, value); + hex_timer_rearm(s); + break; + case QCT_QTIMER_CNTP_CVAL_HI: + s->int_level = 0; + /* HI half is 24-bit per TRM; bits [31:24] are reserved. */ + s->cntval = deposit64(s->cntval, 32, QCT_QTIMER_CNT_HI_BITS, value) & + QCT_QTIMER_CNT_MASK; + hex_timer_rearm(s); + break; + case QCT_QTIMER_CNTP_CTL: + /* ISTAT (bit 2) is read-only; keep SW writes from polluting it. */ + s->control = value & ~QCT_QTIMER_CNTP_CTL_ISTAT; + hex_timer_rearm(s); + break; + case QCT_QTIMER_CNTP_TVAL: + /* TVAL write: CVAL = CNTPCT + TVAL (TVAL is signed 32-bit). */ + s->int_level = 0; + s->cntval = (hex_timer_now(s) + (int64_t)(int32_t)value) & + QCT_QTIMER_CNT_MASK; + hex_timer_rearm(s); + break; + case QCT_QTIMER_CNTPL0ACR: + if (!view) { + s->cntpl0acr = value; + } + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad offset 0x%x\n", __func__, + (int)offset); + return MEMTX_ACCESS_ERROR; + } + hex_timer_update(s); + return MEMTX_OK; +} + +static void hex_timer_tick(void *opaque) +{ + QCTHextimerState *s = opaque; + uint64_t now = hex_timer_now(s); + uint64_t diff56 = (now - s->cntval) & QCT_QTIMER_CNT_MASK; + int64_t signed_diff = (int64_t)(diff56 << 8) >> 8; + + if (signed_diff >= 0) { + s->int_level = 1; + hex_timer_update(s); + } else { + hex_timer_rearm(s); + } +} + +static const MemoryRegionOps hex_timer_ops = { + .read_with_attrs = hex_timer_read, + .write_with_attrs = hex_timer_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + .unaligned = false, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static const VMStateDescription vmstate_qct_hextimer = { + .name = "qct-hextimer", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(control, QCTHextimerState), + VMSTATE_UINT32(cnt_ctrl, QCTHextimerState), + VMSTATE_INT64(offset_ns, QCTHextimerState), + VMSTATE_UINT64(cntval, QCTHextimerState), + VMSTATE_UINT32(cntpl0acr, QCTHextimerState), + VMSTATE_UINT32(int_level, QCTHextimerState), + VMSTATE_TIMER_PTR(timer, QCTHextimerState), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_qct_qtimer = { + .name = "qct-qtimer", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(freq_hz, QCTQtimerState), + VMSTATE_UINT32(secure, QCTQtimerState), + VMSTATE_STRUCT_VARRAY_UINT32(timer, QCTQtimerState, nr_frames, + 1, vmstate_qct_hextimer, QCTHextimerState), + VMSTATE_END_OF_LIST() + } +}; + +static void qct_qtimer_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + QCTQtimerState *s = QCT_QTIMER(dev); + unsigned int i; + + if (s->nr_frames > QCT_QTIMER_TIMER_FRAME_ELTS) { + error_setg(errp, "nr_frames too high"); + return; + } + if (s->nr_views > QCT_QTIMER_TIMER_VIEW_ELTS) { + error_setg(errp, "nr_views too high"); + return; + } + if (s->freq_hz == 0) { + error_setg(errp, "freq-hz must be nonzero"); + return; + } + if (s->frame_stride == 0 || !is_power_of_2(s->frame_stride)) { + error_setg(errp, "frame_stride must be a nonzero power of two"); + return; + } + + memory_region_init_io(&s->iomem, OBJECT(s), &qct_qtimer_ac_ops, s, + "qct-qtimer-ac", QTIMER_MEM_SIZE_BYTES); + sysbus_init_mmio(sbd, &s->iomem); + + memory_region_init_io(&s->view_iomem, OBJECT(s), &hex_timer_ops, s, + "qct-qtimer-view", + (uint64_t)s->frame_stride * s->nr_frames * + s->nr_views); + sysbus_init_mmio(sbd, &s->view_iomem); + + for (i = 0; i < s->nr_frames; i++) { + QCTHextimerState *t = &s->timer[i]; + + t->qtimer = s; + s->secure |= (1 << i); + + sysbus_init_irq(sbd, &t->irq); + t->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hex_timer_tick, t); + } +} + +static void qct_qtimer_unrealize(DeviceState *dev) +{ + QCTQtimerState *s = QCT_QTIMER(dev); + unsigned int i; + + for (i = 0; i < s->nr_frames; i++) { + QCTHextimerState *t = &s->timer[i]; + + if (t->timer) { + timer_free(t->timer); + t->timer = NULL; + } + } +} + +static void qct_qtimer_reset_hold(Object *obj, ResetType type) +{ + QCTQtimerState *s = QCT_QTIMER(obj); + unsigned int i; + + for (i = 0; i < s->nr_frames; i++) { + QCTHextimerState *t = &s->timer[i]; + + /* + * Per TRM: CTL = 0 (EN=0, IMASK=0, ISTAT=0), CVAL = 0 so that + * TVAL (= CVAL - CNTPCT) also reads 0 at reset. The QEMUTimer is + * only armed when SW sets CTL.EN=1, so cntval=0 does not cause a + * spurious fire before SW programs the compare value. + */ + t->control = 0; + t->cnt_ctrl = QCT_QTIMER_AC_CNTACR_RWPT | QCT_QTIMER_AC_CNTACR_RWVT | + QCT_QTIMER_AC_CNTACR_RVOFF | QCT_QTIMER_AC_CNTACR_RFRQ | + QCT_QTIMER_AC_CNTACR_RPVCT | QCT_QTIMER_AC_CNTACR_RPCT; + t->cntval = 0; + t->cntpl0acr = 0; + t->int_level = 0; + t->offset_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + timer_del(t->timer); + qemu_set_irq(t->irq, 0); + } +} + +static const Property qct_qtimer_properties[] = { + DEFINE_PROP_UINT32("freq-hz", QCTQtimerState, freq_hz, + QTIMER_DEFAULT_FREQ_HZ), + DEFINE_PROP_UINT32("freq-scale", QCTQtimerState, freq_scale, 1), + DEFINE_PROP_UINT32("nr_frames", QCTQtimerState, nr_frames, 2), + DEFINE_PROP_UINT32("nr_views", QCTQtimerState, nr_views, 1), + DEFINE_PROP_UINT32("frame_stride", QCTQtimerState, frame_stride, 0x1000), +}; + +static void qct_qtimer_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + device_class_set_props(dc, qct_qtimer_properties); + dc->realize = qct_qtimer_realize; + dc->unrealize = qct_qtimer_unrealize; + dc->vmsd = &vmstate_qct_qtimer; + rc->phases.hold = qct_qtimer_reset_hold; +} + +/* QTimer interface implementation, backing HEX_SREG_TIMERLO/TIMERHI */ +static uint32_t qct_qtimer_get_timer_lo_impl(const QctQtimerInterface *obj) +{ + QCTQtimerState *s = QCT_QTIMER((QctQtimerInterface *)obj); + + return s->nr_frames > 0 ? extract64(hex_timer_now(&s->timer[0]), 0, 32) + : 0; +} + +static uint32_t qct_qtimer_get_timer_hi_impl(const QctQtimerInterface *obj) +{ + QCTQtimerState *s = QCT_QTIMER((QctQtimerInterface *)obj); + + return s->nr_frames > 0 ? extract64(hex_timer_now(&s->timer[0]), 32, 32) + : 0; +} + +static void qct_qtimer_interface_class_init(ObjectClass *klass, + const void *data) +{ + QctQtimerInterfaceClass *k = QCT_QTIMER_INTERFACE_CLASS(klass); + + k->get_timer_lo = qct_qtimer_get_timer_lo_impl; + k->get_timer_hi = qct_qtimer_get_timer_hi_impl; +} + +static const TypeInfo qct_qtimer_types[] = { + { + .name = TYPE_QCT_QTIMER_INTERFACE, + .parent = TYPE_INTERFACE, + .class_size = sizeof(QctQtimerInterfaceClass), + .class_init = qct_qtimer_interface_class_init, + }, + { + .name = TYPE_QCT_QTIMER, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(QCTQtimerState), + .class_init = qct_qtimer_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_QCT_QTIMER_INTERFACE }, + { } + }, + }, +}; + +DEFINE_TYPES(qct_qtimer_types) diff --git a/hw/timer/trace-events b/hw/timer/trace-events index 634ba1da27..636310f8ca 100644 --- a/hw/timer/trace-events +++ b/hw/timer/trace-events @@ -128,3 +128,8 @@ imx_epit_get_freq(uint32_t freq) "ptimer frequency is %u" imx_epit_read(const char *name, uint32_t value) "(%s) = 0x%08x" imx_epit_write(const char *name, uint64_t value) "(%s, value = 0x%08" PRIx64 ")" imx_epit_cmp(uint32_t sr) "sr was %d" + +# qct-qtimer.c +qtimer_interrupt(void) "qtimer interrupt line updated" +qtimer_read(uint64_t offset) "offset 0x%" PRIx64 +qtimer_write(uint64_t offset, uint64_t value) "offset 0x%" PRIx64 " value 0x%" PRIx64 diff --git a/hw/uefi/var-service-auth.c b/hw/uefi/var-service-auth.c index f3dc9c6ca6..899444af12 100644 --- a/hw/uefi/var-service-auth.c +++ b/hw/uefi/var-service-auth.c @@ -298,6 +298,13 @@ efi_status uefi_vars_check_secure_boot(uefi_vars_state *uv, uefi_variable *var) return EFI_WRITE_PROTECTED; } + /* reject SetupMode updates */ + if (qemu_uuid_is_equal(&var->guid, &EfiGlobalVariable) && + uefi_str_equal(var->name, var->name_size, + name_setup_mode, sizeof(name_setup_mode))) { + return EFI_WRITE_PROTECTED; + } + return EFI_SUCCESS; } diff --git a/hw/uefi/var-service-core.c b/hw/uefi/var-service-core.c index 828d760073..65da71029a 100644 --- a/hw/uefi/var-service-core.c +++ b/hw/uefi/var-service-core.c @@ -5,6 +5,7 @@ */ #include "qemu/osdep.h" #include "qemu/crc32c.h" +#include "qemu/error-report.h" #include "system/dma.h" #include "migration/vmstate.h" @@ -28,9 +29,20 @@ static int uefi_vars_post_load(void *opaque, int version_id) { uefi_vars_state *uv = opaque; - uefi_vars_update_storage(uv); - uefi_vars_json_save(uv); + if (uv->buf_size > MAX_BUFFER_SIZE) { + error_report("invalid buffer size"); + return -1; + } uv->buffer = g_malloc(uv->buf_size); + + uefi_vars_update_storage(uv); + if (uv->used_storage > uv->max_storage) { + error_report("out of variable memory (%" PRId64 " > %" PRId64 ")", + uv->used_storage, uv->max_storage); + return -1; + } + + uefi_vars_json_save(uv); return 0; } diff --git a/hw/uefi/var-service-json.c b/hw/uefi/var-service-json.c index 8621b86c5c..88757d58b6 100644 --- a/hw/uefi/var-service-json.c +++ b/hw/uefi/var-service-json.c @@ -18,6 +18,7 @@ #include "qobject/qobject.h" #include "qobject/qjson.h" +#include "qapi/error.h" #include "qapi/dealloc-visitor.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" @@ -249,6 +250,10 @@ void uefi_vars_json_load(uefi_vars_state *uv, Error **errp) if (!(*errp)) { uefi_vars_from_qapi(uv, vs); uefi_vars_update_storage(uv); + if (uv->used_storage > uv->max_storage) { + error_setg(errp, "out of variable memory (%" PRId64 " > %" PRId64 ")", + uv->used_storage, uv->max_storage); + } } qapi_free_UefiVarStore(vs); diff --git a/hw/uefi/var-service-policy.c b/hw/uefi/var-service-policy.c index 58da4adbeb..d43cc9b250 100644 --- a/hw/uefi/var-service-policy.c +++ b/hw/uefi/var-service-policy.c @@ -7,6 +7,7 @@ * https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/VariablePolicyLib/ReadMe.md */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "system/dma.h" #include "migration/vmstate.h" @@ -16,14 +17,18 @@ #include "trace.h" -static void calc_policy(uefi_var_policy *pol); +static int check_calc_policy(uefi_var_policy *pol); static int uefi_var_policy_post_load(void *opaque, int version_id) { uefi_var_policy *pol = opaque; + int rc; - calc_policy(pol); - return 0; + rc = check_calc_policy(pol); + if (rc != 0) { + error_report("invalid uefi variable policy"); + } + return rc; } const VMStateDescription vmstate_uefi_var_policy = { @@ -37,37 +42,6 @@ const VMStateDescription vmstate_uefi_var_policy = { }, }; -static void print_policy_entry(variable_policy_entry *pe) -{ - uint16_t *name = (void *)pe + pe->offset_to_name; - - fprintf(stderr, "%s:\n", __func__); - - fprintf(stderr, " name ´"); - while (*name) { - fprintf(stderr, "%c", *name); - name++; - } - fprintf(stderr, "', version=%d.%d, size=%d\n", - pe->version >> 16, pe->version & 0xffff, pe->size); - - if (pe->min_size) { - fprintf(stderr, " size min=%d\n", pe->min_size); - } - if (pe->max_size != UINT32_MAX) { - fprintf(stderr, " size max=%u\n", pe->max_size); - } - if (pe->attributes_must_have) { - fprintf(stderr, " attr must=0x%x\n", pe->attributes_must_have); - } - if (pe->attributes_cant_have) { - fprintf(stderr, " attr cant=0x%x\n", pe->attributes_cant_have); - } - if (pe->lock_policy_type) { - fprintf(stderr, " lock policy type %d\n", pe->lock_policy_type); - } -} - static gboolean wildcard_str_equal(uefi_var_policy *pol, uefi_variable *var) { @@ -111,32 +85,45 @@ static uefi_var_policy *wildcard_find_policy(uefi_vars_state *uv, return NULL; } -static void calc_policy(uefi_var_policy *pol) +static int check_calc_policy(uefi_var_policy *pol) { variable_policy_entry *pe = pol->entry; unsigned int i; + if (pol->entry_size != pe->size || + pe->offset_to_name >= pe->size) { + return -1; + } + pol->name = (void *)pol->entry + pe->offset_to_name; pol->name_size = pe->size - pe->offset_to_name; + if (!uefi_str_is_valid(pol->name, pol->name_size, false)) { + return -1; + } + for (i = 0; i < pol->name_size / 2; i++) { if (pol->name[i] == '#') { pol->hashmarks++; } } + + return 0; } uefi_var_policy *uefi_vars_add_policy(uefi_vars_state *uv, variable_policy_entry *pe) { uefi_var_policy *pol, *p; + int rc; pol = g_new0(uefi_var_policy, 1); pol->entry = g_malloc(pe->size); memcpy(pol->entry, pe, pe->size); pol->entry_size = pe->size; - calc_policy(pol); + rc = check_calc_policy(pol); + g_assert(rc == 0); /* keep list sorted by priority, add to tail of priority group */ QTAILQ_FOREACH(p, &uv->var_policies, next) { @@ -173,7 +160,6 @@ efi_status uefi_vars_policy_check(uefi_vars_state *uv, pe = pol->entry; uefi_trace_variable(__func__, var->guid, var->name, var->name_size); - print_policy_entry(pe); if ((var->attributes & pe->attributes_must_have) != pe->attributes_must_have) { trace_uefi_vars_policy_deny("must-have-attr"); @@ -276,6 +262,9 @@ static uint32_t uefi_vars_mm_check_policy_register(uefi_vars_state *uv, uefi_var_policy *pol; uint64_t length; + if (mhdr->length < sizeof(*mchk) + sizeof(*pe)) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); + } if (uadd64_overflow(sizeof(*mchk), pe->size, &length)) { return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); } @@ -312,7 +301,12 @@ static uint32_t uefi_vars_mm_check_policy_register(uefi_vars_state *uv, return uefi_vars_mm_policy_error(mhdr, mchk, EFI_ALREADY_STARTED); } + if (uv->used_storage + pe->size > uv->max_storage) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_OUT_OF_RESOURCES); + } + uefi_vars_add_policy(uv, pe); + uv->used_storage += pe->size; mchk->result = EFI_SUCCESS; return sizeof(*mchk); diff --git a/hw/uefi/var-service-siglist.c b/hw/uefi/var-service-siglist.c index 8948f1b784..9e03a4dc0c 100644 --- a/hw/uefi/var-service-siglist.c +++ b/hw/uefi/var-service-siglist.c @@ -94,6 +94,9 @@ void uefi_vars_siglist_parse(uefi_vars_siglist *siglist, break; } efilist = data; + if (efilist->siglist_size < sizeof(*efilist)) { + break; + } if (size < efilist->siglist_size) { break; } diff --git a/hw/uefi/var-service-vars.c b/hw/uefi/var-service-vars.c index 2c83130ebf..e0ea3d532e 100644 --- a/hw/uefi/var-service-vars.c +++ b/hw/uefi/var-service-vars.c @@ -47,6 +47,20 @@ static int uefi_vars_pre_load(void *opaque) return 0; } +static int uefi_vars_post_load(void *opaque, int version_id) +{ + uefi_variable *var = opaque; + + if (!uefi_str_is_valid(var->name, var->name_size, true) || + var->attributes & ~EFI_VARIABLE_ATTRIBUTE_SUPPORTED || + (var->digest_size != 0 && + var->digest_size != 32 /* AUTHVAR_DIGEST_SIZE */)) { + error_report("invalid uefi variable"); + return -1; + } + return 0; +} + static bool uefi_vars_digest_is_needed(void *opaque) { uefi_variable *var = opaque; @@ -72,6 +86,7 @@ const VMStateDescription vmstate_uefi_variable_digest = { const VMStateDescription vmstate_uefi_variable = { .name = "uefi-variable", .pre_load = uefi_vars_pre_load, + .post_load = uefi_vars_post_load, .fields = (VMStateField[]) { VMSTATE_UINT8_ARRAY_V(guid.data, uefi_variable, sizeof(QemuUUID), 0), VMSTATE_UINT32(name_size, uefi_variable), @@ -201,11 +216,15 @@ void uefi_vars_clear_all(uefi_vars_state *uv) void uefi_vars_update_storage(uefi_vars_state *uv) { uefi_variable *var; + uefi_var_policy *pol; uv->used_storage = 0; QTAILQ_FOREACH(var, &uv->variables, next) { uv->used_storage += variable_size(var); } + QTAILQ_FOREACH(pol, &uv->var_policies, next) { + uv->used_storage += pol->entry->size; + } } static gboolean check_access(uefi_vars_state *uv, uefi_variable *var) diff --git a/hw/ufs/Kconfig b/hw/ufs/Kconfig index b7b3392e85..0e40f08a41 100644 --- a/hw/ufs/Kconfig +++ b/hw/ufs/Kconfig @@ -1,4 +1,13 @@ +config UFS + bool + select SCSI + config UFS_PCI bool default y if PCI_DEVICES depends on PCI + select UFS + +config UFS_SYSBUS + bool + select UFS diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c index 13f4a90145..eeca865eb5 100644 --- a/hw/ufs/lu.c +++ b/hw/ufs/lu.c @@ -497,7 +497,7 @@ static void ufs_lu_realize(DeviceState *dev, Error **errp) { UfsLu *lu = DO_UPCAST(UfsLu, qdev, dev); BusState *s = qdev_get_parent_bus(dev); - UfsHc *u = UFS(s->parent); + UfsHc *u = UFS_BUS(s)->hc; BlockBackend *blk = lu->conf.blk; if (!ufs_lu_check_constraints(lu, errp)) { diff --git a/hw/ufs/meson.build b/hw/ufs/meson.build index 6e68328b93..a84627b96a 100644 --- a/hw/ufs/meson.build +++ b/hw/ufs/meson.build @@ -1 +1,3 @@ -system_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs.c', 'lu.c')) +system_ss.add(when: 'CONFIG_UFS', if_true: files('ufs.c', 'lu.c')) +system_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs-pci.c')) +system_ss.add(when: 'CONFIG_UFS_SYSBUS', if_true: files('ufs-sysbus.c')) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events index 662d9afee3..0cd3ba9b02 100644 --- a/hw/ufs/trace-events +++ b/hw/ufs/trace-events @@ -1,6 +1,6 @@ # ufs.c -ufs_irq_raise(void) "INTx" -ufs_irq_lower(void) "INTx" +ufs_irq_raise(void) "IRQ" +ufs_irq_lower(void) "IRQ" ufs_mmio_read(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" ufs_mmio_write(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" ufs_process_db(uint32_t slot) "UTRLDBR slot %"PRIu32"" diff --git a/hw/ufs/ufs-pci.c b/hw/ufs/ufs-pci.c new file mode 100644 index 0000000000..8abd7d98e3 --- /dev/null +++ b/hw/ufs/ufs-pci.c @@ -0,0 +1,112 @@ +/* + * QEMU Universal Flash Storage (UFS) PCI Controller + * + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. + * + * Written by Jeuk Kim + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/** + * Usage + * ----- + * + * Add options: + * -drive file=,if=none,id= + * -device ufs,serial=,id=, \ + * nutrs=,nutmrs= + * -device ufs-lu,drive=,bus= + */ + +#include "qemu/osdep.h" +#include "hw/core/irq.h" +#include "hw/core/qdev-properties.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" +#include "migration/vmstate.h" +#include "ufs.h" + +#define TYPE_UFS_PCI "ufs" +OBJECT_DECLARE_SIMPLE_TYPE(UfsPciState, UFS_PCI) + +struct UfsPciState { + PCIDevice parent_obj; + UfsHc ufs; +}; + +static void ufs_pci_realize(PCIDevice *pci_dev, Error **errp) +{ + UfsPciState *s = UFS_PCI(pci_dev); + UfsHc *u = &s->ufs; + uint8_t *pci_conf = pci_dev->config; + + pci_conf[PCI_INTERRUPT_PIN] = 1; + pci_config_set_prog_interface(pci_conf, 0x1); + u->irq = pci_allocate_irq(pci_dev); + if (!ufs_realize(u, DEVICE(pci_dev), pci_get_address_space(pci_dev), + errp)) { + qemu_free_irq(u->irq); + u->irq = NULL; + return; + } + + pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &u->iomem); +} + +static void ufs_pci_exit(PCIDevice *pci_dev) +{ + UfsPciState *s = UFS_PCI(pci_dev); + + ufs_unrealize(&s->ufs); + qemu_free_irq(s->ufs.irq); +} + +static const Property ufs_pci_props[] = { + DEFINE_PROP_STRING("serial", UfsPciState, ufs.params.serial), + DEFINE_PROP_UINT8("nutrs", UfsPciState, ufs.params.nutrs, 32), + DEFINE_PROP_UINT8("nutmrs", UfsPciState, ufs.params.nutmrs, 8), + DEFINE_PROP_BOOL("mcq", UfsPciState, ufs.params.mcq, false), + DEFINE_PROP_UINT8("mcq-maxq", UfsPciState, ufs.params.mcq_maxq, 2), + DEFINE_PROP_UINT32("wb-max-size", UfsPciState, ufs.params.wb_max_size, + 0x400), + DEFINE_PROP_UINT32("wb-min-size", UfsPciState, ufs.params.wb_min_size, + 0x100), +}; + +static const VMStateDescription ufs_pci_vmstate = { + .name = "ufs", + .unmigratable = 1, +}; + +static void ufs_pci_class_init(ObjectClass *oc, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); + + pc->realize = ufs_pci_realize; + pc->exit = ufs_pci_exit; + pc->vendor_id = PCI_VENDOR_ID_REDHAT; + pc->device_id = PCI_DEVICE_ID_REDHAT_UFS; + pc->class_id = PCI_CLASS_STORAGE_UFS; + + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + dc->desc = "Universal Flash Storage"; + device_class_set_props(dc, ufs_pci_props); + dc->vmsd = &ufs_pci_vmstate; +} + +static const TypeInfo ufs_pci_info = { + .name = TYPE_UFS_PCI, + .parent = TYPE_PCI_DEVICE, + .class_init = ufs_pci_class_init, + .instance_size = sizeof(UfsPciState), + .interfaces = (const InterfaceInfo[]){ { INTERFACE_PCIE_DEVICE }, {} }, +}; + +static void ufs_pci_register_types(void) +{ + type_register_static(&ufs_pci_info); +} + +type_init(ufs_pci_register_types) diff --git a/hw/ufs/ufs-sysbus.c b/hw/ufs/ufs-sysbus.c new file mode 100644 index 0000000000..84de2e95ac --- /dev/null +++ b/hw/ufs/ufs-sysbus.c @@ -0,0 +1,80 @@ +/* + * QEMU Universal Flash Storage (UFS) sysbus controller + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/core/qdev-properties.h" +#include "ufs-sysbus.h" +#include "migration/vmstate.h" +#include "system/address-spaces.h" + +static void ufs_sysbus_realize(DeviceState *dev, Error **errp) +{ + SysbusUfsState *s = SYSBUS_UFS(dev); + + if (!ufs_realize(&s->ufs, dev, &address_space_memory, errp)) { + return; + } + + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->ufs.iomem); +} + +static void ufs_sysbus_unrealize(DeviceState *dev) +{ + SysbusUfsState *s = SYSBUS_UFS(dev); + + ufs_unrealize(&s->ufs); +} + +static void ufs_sysbus_init(Object *obj) +{ + SysbusUfsState *s = SYSBUS_UFS(obj); + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->ufs.irq); +} + +static const Property ufs_sysbus_props[] = { + DEFINE_PROP_STRING("serial", SysbusUfsState, ufs.params.serial), + DEFINE_PROP_UINT8("nutrs", SysbusUfsState, ufs.params.nutrs, 32), + DEFINE_PROP_UINT8("nutmrs", SysbusUfsState, ufs.params.nutmrs, 8), + DEFINE_PROP_BOOL("mcq", SysbusUfsState, ufs.params.mcq, false), + DEFINE_PROP_UINT8("mcq-maxq", SysbusUfsState, ufs.params.mcq_maxq, 2), + DEFINE_PROP_UINT32("wb-max-size", SysbusUfsState, ufs.params.wb_max_size, + 0x400), + DEFINE_PROP_UINT32("wb-min-size", SysbusUfsState, ufs.params.wb_min_size, + 0x100), +}; + +static const VMStateDescription ufs_sysbus_vmstate = { + .name = TYPE_SYSBUS_UFS, + .unmigratable = 1, +}; + +static void ufs_sysbus_class_init(ObjectClass *oc, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = ufs_sysbus_realize; + dc->unrealize = ufs_sysbus_unrealize; + dc->vmsd = &ufs_sysbus_vmstate; + dc->desc = "Universal Flash Storage"; + device_class_set_props(dc, ufs_sysbus_props); + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); +} + +static const TypeInfo ufs_sysbus_info = { + .name = TYPE_SYSBUS_UFS, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SysbusUfsState), + .instance_init = ufs_sysbus_init, + .class_init = ufs_sysbus_class_init, +}; + +static void ufs_sysbus_register_types(void) +{ + type_register_static(&ufs_sysbus_info); +} + +type_init(ufs_sysbus_register_types) diff --git a/hw/ufs/ufs-sysbus.h b/hw/ufs/ufs-sysbus.h new file mode 100644 index 0000000000..71b6787d3a --- /dev/null +++ b/hw/ufs/ufs-sysbus.h @@ -0,0 +1,22 @@ +/* + * QEMU Universal Flash Storage (UFS) sysbus controller + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_UFS_UFS_SYSBUS_H +#define HW_UFS_UFS_SYSBUS_H + +#include "hw/core/sysbus.h" +#include "ufs.h" + +#define TYPE_SYSBUS_UFS "sysbus-ufs" +OBJECT_DECLARE_SIMPLE_TYPE(SysbusUfsState, SYSBUS_UFS) + +struct SysbusUfsState { + SysBusDevice parent_obj; + + UfsHc ufs; +}; + +#endif /* HW_UFS_UFS_SYSBUS_H */ diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 464fd465b3..160a4ac4f3 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -11,19 +11,10 @@ /** * Reference Specs: https://www.jedec.org/, 4.1 * - * Usage - * ----- - * - * Add options: - * -drive file=,if=none,id= - * -device ufs,serial=,id=, \ - * nutrs=,nutmrs= - * -device ufs-lu,drive=,bus= */ #include "qemu/osdep.h" #include "qapi/error.h" -#include "migration/vmstate.h" #include "scsi/constants.h" #include "hw/core/irq.h" #include "trace.h" @@ -102,7 +93,7 @@ static MemTxResult ufs_addr_read(UfsHc *u, hwaddr addr, void *buf, int size) return MEMTX_DECODE_ERROR; } - return pci_dma_read(PCI_DEVICE(u), addr, buf, size); + return dma_memory_read(u->dma_as, addr, buf, size, MEMTXATTRS_UNSPECIFIED); } static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf, @@ -117,7 +108,7 @@ static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf, return MEMTX_DECODE_ERROR; } - return pci_dma_write(PCI_DEVICE(u), addr, buf, size); + return dma_memory_write(u->dma_as, addr, buf, size, MEMTXATTRS_UNSPECIFIED); } static inline hwaddr ufs_get_utrd_addr(UfsHc *u, uint32_t slot) @@ -222,7 +213,7 @@ static MemTxResult ufs_dma_read_prdt(UfsRequest *req) } req->sg = g_malloc0(sizeof(QEMUSGList)); - pci_dma_sglist_init(req->sg, PCI_DEVICE(u), prdt_len); + qemu_sglist_init(req->sg, u->dev, prdt_len, u->dma_as); req->data_len = 0; for (uint16_t i = 0; i < prdt_len; ++i) { @@ -317,14 +308,12 @@ static MemTxResult ufs_dma_write_upiu(UfsRequest *req) static void ufs_irq_check(UfsHc *u) { - PCIDevice *pci = PCI_DEVICE(u); - if ((u->reg.is & UFS_INTR_MASK) & u->reg.ie) { trace_ufs_irq_raise(); - pci_irq_assert(pci); + qemu_irq_raise(u->irq); } else { trace_ufs_irq_lower(); - pci_irq_deassert(pci); + qemu_irq_lower(u->irq); } } @@ -596,7 +585,7 @@ static bool ufs_mcq_create_sq(UfsHc *u, uint8_t qid, uint32_t attr) sq->size = qsize; sq->bh = qemu_bh_new_guarded(ufs_mcq_process_sq, sq, - &DEVICE(u)->mem_reentrancy_guard); + &u->dev->mem_reentrancy_guard); sq->req = g_new0(UfsRequest, sq->size); QTAILQ_INIT(&sq->req_list); for (int i = 0; i < sq->size; i++) { @@ -690,7 +679,7 @@ static bool ufs_mcq_create_cq(UfsHc *u, uint8_t qid, uint32_t attr) cq->size = qsize; cq->bh = qemu_bh_new_guarded(ufs_mcq_process_cq, cq, - &DEVICE(u)->mem_reentrancy_guard); + &u->dev->mem_reentrancy_guard); QTAILQ_INIT(&cq->req_list); u->cq[qid] = cq; @@ -2488,19 +2477,6 @@ static bool ufs_check_constraints(UfsHc *u, Error **errp) return true; } -static void ufs_init_pci(UfsHc *u, PCIDevice *pci_dev) -{ - uint8_t *pci_conf = pci_dev->config; - - pci_conf[PCI_INTERRUPT_PIN] = 1; - pci_config_set_prog_interface(pci_conf, 0x1); - - memory_region_init_io(&u->iomem, OBJECT(u), &ufs_mmio_ops, u, "ufs", - u->reg_size); - pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &u->iomem); - u->irq = pci_allocate_irq(pci_dev); -} - static void ufs_init_state(UfsHc *u) { u->req_list = g_new0(UfsRequest, u->params.nutrs); @@ -2512,10 +2488,10 @@ static void ufs_init_state(UfsHc *u) u->req_list[i].state = UFS_REQUEST_IDLE; } - u->doorbell_bh = qemu_bh_new_guarded(ufs_process_req, u, - &DEVICE(u)->mem_reentrancy_guard); - u->complete_bh = qemu_bh_new_guarded(ufs_sendback_req, u, - &DEVICE(u)->mem_reentrancy_guard); + u->doorbell_bh = + qemu_bh_new_guarded(ufs_process_req, u, &u->dev->mem_reentrancy_guard); + u->complete_bh = + qemu_bh_new_guarded(ufs_sendback_req, u, &u->dev->mem_reentrancy_guard); if (u->params.mcq) { memset(u->sq, 0, sizeof(u->sq)); @@ -2689,35 +2665,35 @@ static void ufs_init_hc(UfsHc *u) timer_mod(&u->idle_timer, now + UFS_IDLE_TIMER_TICK); } -static void ufs_realize(PCIDevice *pci_dev, Error **errp) +bool ufs_realize(UfsHc *u, DeviceState *dev, AddressSpace *dma_as, Error **errp) { - UfsHc *u = UFS(pci_dev); + u->dev = dev; + u->dma_as = dma_as; if (!ufs_check_constraints(u, errp)) { - return; + return false; } - qbus_init(&u->bus, sizeof(UfsBus), TYPE_UFS_BUS, &pci_dev->qdev, - u->parent_obj.qdev.id); + qbus_init(&u->bus, sizeof(UfsBus), TYPE_UFS_BUS, dev, dev->id); + u->bus.hc = u; ufs_init_state(u); ufs_init_hc(u); - ufs_init_pci(u, pci_dev); + memory_region_init_io(&u->iomem, OBJECT(dev), &ufs_mmio_ops, u, "ufs", + u->reg_size); ufs_init_wlu(&u->report_wlu, UFS_UPIU_REPORT_LUNS_WLUN); ufs_init_wlu(&u->dev_wlu, UFS_UPIU_UFS_DEVICE_WLUN); ufs_init_wlu(&u->boot_wlu, UFS_UPIU_BOOT_WLUN); ufs_init_wlu(&u->rpmb_wlu, UFS_UPIU_RPMB_WLUN); + + return true; } -static void ufs_exit(PCIDevice *pci_dev) +void ufs_unrealize(UfsHc *u) { - UfsHc *u = UFS(pci_dev); - timer_del(&u->idle_timer); - qemu_free_irq(u->irq); - qemu_bh_delete(u->doorbell_bh); qemu_bh_delete(u->complete_bh); @@ -2740,38 +2716,6 @@ static void ufs_exit(PCIDevice *pci_dev) } } -static const Property ufs_props[] = { - DEFINE_PROP_STRING("serial", UfsHc, params.serial), - DEFINE_PROP_UINT8("nutrs", UfsHc, params.nutrs, 32), - DEFINE_PROP_UINT8("nutmrs", UfsHc, params.nutmrs, 8), - DEFINE_PROP_BOOL("mcq", UfsHc, params.mcq, false), - DEFINE_PROP_UINT8("mcq-maxq", UfsHc, params.mcq_maxq, 2), - DEFINE_PROP_UINT32("wb-max-size", UfsHc, params.wb_max_size, 0x400), - DEFINE_PROP_UINT32("wb-min-size", UfsHc, params.wb_min_size, 0x100), -}; - -static const VMStateDescription ufs_vmstate = { - .name = "ufs", - .unmigratable = 1, -}; - -static void ufs_class_init(ObjectClass *oc, const void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); - - pc->realize = ufs_realize; - pc->exit = ufs_exit; - pc->vendor_id = PCI_VENDOR_ID_REDHAT; - pc->device_id = PCI_DEVICE_ID_REDHAT_UFS; - pc->class_id = PCI_CLASS_STORAGE_UFS; - - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->desc = "Universal Flash Storage"; - device_class_set_props(dc, ufs_props); - dc->vmsd = &ufs_vmstate; -} - static bool ufs_bus_check_address(BusState *qbus, DeviceState *qdev, Error **errp) { @@ -2798,14 +2742,6 @@ static void ufs_bus_class_init(ObjectClass *class, const void *data) bc->check_address = ufs_bus_check_address; } -static const TypeInfo ufs_info = { - .name = TYPE_UFS, - .parent = TYPE_PCI_DEVICE, - .class_init = ufs_class_init, - .instance_size = sizeof(UfsHc), - .interfaces = (const InterfaceInfo[]){ { INTERFACE_PCIE_DEVICE }, {} }, -}; - static const TypeInfo ufs_bus_info = { .name = TYPE_UFS_BUS, .parent = TYPE_BUS, @@ -2816,7 +2752,6 @@ static const TypeInfo ufs_bus_info = { static void ufs_register_types(void) { - type_register_static(&ufs_info); type_register_static(&ufs_bus_info); } diff --git a/hw/ufs/ufs.h b/hw/ufs/ufs.h index feb47f460d..aa8361d93d 100644 --- a/hw/ufs/ufs.h +++ b/hw/ufs/ufs.h @@ -11,9 +11,11 @@ #ifndef HW_UFS_UFS_H #define HW_UFS_UFS_H -#include "hw/pci/pci_device.h" +#include "hw/core/qdev.h" #include "hw/scsi/scsi.h" #include "block/ufs.h" +#include "scsi/constants.h" +#include "system/dma.h" #define UFS_MAX_LUS 32 #define UFS_MAX_MCQ_QNUM 32 @@ -27,6 +29,7 @@ typedef struct UfsBusClass { typedef struct UfsBus { BusState parent_bus; + struct UfsHc *hc; } UfsBus; #define TYPE_UFS_BUS "ufs-bus" @@ -141,7 +144,8 @@ typedef struct UfsWb { } UfsWb; typedef struct UfsHc { - PCIDevice parent_obj; + DeviceState *dev; + AddressSpace *dma_as; UfsBus bus; MemoryRegion iomem; UfsReg reg; @@ -268,9 +272,6 @@ static inline bool ufs_is_write_req(UfsRequest *req) return (cmd == WRITE_6) || (cmd == WRITE_10) || (cmd == WRITE_16); } -#define TYPE_UFS "ufs" -#define UFS(obj) OBJECT_CHECK(UfsHc, (obj), TYPE_UFS) - #define TYPE_UFS_LU "ufs-lu" #define UFSLU(obj) OBJECT_CHECK(UfsLu, (obj), TYPE_UFS_LU) @@ -302,4 +303,7 @@ void ufs_build_query_response(UfsRequest *req); void ufs_complete_req(UfsRequest *req, UfsReqResult req_result); void ufs_wb_update_avail_buffer(UfsHc *u); void ufs_init_wlu(UfsLu *wlu, uint8_t wlun); +bool ufs_realize(UfsHc *u, DeviceState *dev, AddressSpace *dma_as, + Error **errp); +void ufs_unrealize(UfsHc *u); #endif /* HW_UFS_UFS_H */ diff --git a/hw/usb/core.c b/hw/usb/core.c index 9572a870cc..43653c26ca 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -26,6 +26,7 @@ #include "qemu/osdep.h" #include "hw/usb/usb.h" #include "qemu/iov.h" +#include "qemu/log.h" #include "trace.h" void usb_pick_speed(USBPort *port) @@ -288,6 +289,15 @@ static void do_parameter(USBDevice *s, USBPacket *p) p->status = USB_RET_STALL; return; } + if ((p->pid == USB_TOKEN_OUT || p->pid == USB_TOKEN_IN) && + setup_len > p->iov.size) { + qemu_log_mask(LOG_GUEST_ERROR, + "xhci: setup state param length %u > iov size %zu\n", + setup_len, p->iov.size); + p->status = USB_RET_STALL; + return; + } + s->setup_len = setup_len; if (p->pid == USB_TOKEN_OUT) { @@ -345,6 +355,7 @@ void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p) p->actual_length = 0; usb_packet_copy(p, s->data_buf, s->setup_len); } + usb_pcap_ctrl(p, false); break; default: diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index 8576dfec96..be8c3667e8 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -359,9 +359,11 @@ static void usb_uas_send_status_bh(void *opaque) UASDevice *uas = opaque; UASStatus *st; USBPacket *p; + uint32_t length; while ((st = QTAILQ_FIRST(&uas->results)) != NULL) { if (uas_using_streams(uas)) { + assert(st->stream <= UAS_MAX_STREAMS); p = uas->status3[st->stream]; uas->status3[st->stream] = NULL; } else { @@ -372,7 +374,14 @@ static void usb_uas_send_status_bh(void *opaque) break; } - usb_packet_copy(p, &st->status, st->length); + length = st->length; + if (length > p->iov.size) { + qemu_log_mask(LOG_GUEST_ERROR, + "usb uas: packet (%zd) too small for status (%d)\n", + p->iov.size, length); + length = p->iov.size; + } + usb_packet_copy(p, &st->status, length); QTAILQ_REMOVE(&uas->results, st, next); g_free(st); @@ -383,8 +392,14 @@ static void usb_uas_send_status_bh(void *opaque) static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length) { - USBPacket *p = uas_using_streams(uas) ? - uas->status3[st->stream] : uas->status2; + USBPacket *p; + + if (uas_using_streams(uas)) { + assert(st->stream <= UAS_MAX_STREAMS); + p = uas->status3[st->stream]; + } else { + p = uas->status2; + } st->length += length; QTAILQ_INSERT_TAIL(&uas->results, st, next); @@ -700,14 +715,22 @@ static void usb_uas_command(UASDevice *uas, uas_iu *iu) uint16_t tag = be16_to_cpu(iu->hdr.tag); size_t cdb_len = sizeof(iu->command.cdb) + iu->command.add_cdb_length; + if (uas_using_streams(uas) && tag > UAS_MAX_STREAMS) { + /* + * Our status delivery only works with valid tags, so in case the + * stream ID is out of bounds, we have to return immediately here + * without sending a fake sense_code_INVALID_TAG to the guest. + */ + qemu_log_mask(LOG_GUEST_ERROR, + "invalid tag 0x%x for USB UAS command\n", tag); + return; + } + if (iu->command.add_cdb_length > 0) { qemu_log_mask(LOG_UNIMP, "additional adb length not yet supported\n"); goto unsupported_len; } - if (uas_using_streams(uas) && tag > UAS_MAX_STREAMS) { - goto invalid_tag; - } req = usb_uas_find_request(uas, tag); if (req) { goto overlapped_tag; @@ -744,10 +767,6 @@ unsupported_len: usb_uas_queue_fake_sense(uas, tag, sense_code_INVALID_PARAM_VALUE); return; -invalid_tag: - usb_uas_queue_fake_sense(uas, tag, sense_code_INVALID_TAG); - return; - overlapped_tag: usb_uas_queue_fake_sense(uas, tag, sense_code_OVERLAPPED_COMMANDS); return; @@ -864,7 +883,14 @@ static void usb_uas_handle_data(USBDevice *dev, USBPacket *p) break; } } - usb_packet_copy(p, &st->status, st->length); + length = st->length; + if (length > p->iov.size) { + qemu_log_mask(LOG_GUEST_ERROR, + "usb uas: packet (%zd) too small for status (%d)\n", + p->iov.size, length); + length = p->iov.size; + } + usb_packet_copy(p, &st->status, length); QTAILQ_REMOVE(&uas->results, st, next); g_free(st); break; diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 28a60e4c1a..451a918e9f 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -72,7 +72,7 @@ typedef enum { } EHCI_STATES; /* macros for accessing fields within next link pointer entry */ -#define NLPTR_GET(x) ((x) & 0xffffffe0) +#define NLPTR_GET(x) ((x) & ~0x1fULL) #define NLPTR_TYPE_GET(x) (((x) >> 1) & 3) #define NLPTR_TBIT(x) ((x) & 1) /* 1=invalid, 0=valid */ @@ -96,6 +96,17 @@ typedef enum { *data = val; \ } while (0) +/* + * EHCIqh / EHCIqtd / EHCIitd are sized to always include the extended + * high buffer pointer fields from EHCI 1.0 Appendix B. When 64-bit + * addressing capability is not advertised to the guest, the descriptors + * in guest memory only have the classic 32-bit layout, so DMA transfers + * must not read or write past that boundary. + */ +#define EHCI_QH_DWORDS_32 (offsetof(EHCIqh, bufptr_hi) / sizeof(uint32_t)) +#define EHCI_QTD_DWORDS_32 (offsetof(EHCIqtd, bufptr_hi) / sizeof(uint32_t)) +#define EHCI_ITD_DWORDS_32 (offsetof(EHCIitd, bufptr_hi) / sizeof(uint32_t)) + static const char *ehci_state_names[] = { [EST_INACTIVE] = "INACTIVE", [EST_ACTIVE] = "ACTIVE", @@ -147,6 +158,38 @@ static const char *addr2str(hwaddr addr) return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names), addr); } +static uint64_t ehci_get_buf_addr(const EHCIState *s, uint32_t hi, + uint32_t lo, uint32_t lo_mask) +{ + uint64_t addr = lo & lo_mask; + + if (s->caps_64bit_addr) { + addr = deposit64(addr, 32, 32, hi); + } + + return addr; +} + +static uint64_t ehci_get_desc_addr(const EHCIState *s, uint32_t lo) +{ + return ehci_get_buf_addr(s, s->ctrldssegment, lo, UINT32_MAX); +} + +static uint32_t ehci_qh_dwords(const EHCIState *s) +{ + return s->caps_64bit_addr ? (sizeof(EHCIqh) >> 2) : EHCI_QH_DWORDS_32; +} + +static uint32_t ehci_qtd_dwords(const EHCIState *s) +{ + return s->caps_64bit_addr ? (sizeof(EHCIqtd) >> 2) : EHCI_QTD_DWORDS_32; +} + +static uint32_t ehci_itd_dwords(const EHCIState *s) +{ + return s->caps_64bit_addr ? (sizeof(EHCIitd) >> 2) : EHCI_ITD_DWORDS_32; +} + static void ehci_trace_usbsts(uint32_t mask, int state) { /* interrupts */ @@ -287,7 +330,7 @@ static int ehci_get_state(EHCIState *s, int async) return async ? s->astate : s->pstate; } -static void ehci_set_fetch_addr(EHCIState *s, int async, uint32_t addr) +static void ehci_set_fetch_addr(EHCIState *s, int async, uint64_t addr) { if (async) { s->a_fetch_addr = addr; @@ -296,7 +339,7 @@ static void ehci_set_fetch_addr(EHCIState *s, int async, uint32_t addr) } } -static int ehci_get_fetch_addr(EHCIState *s, int async) +static uint64_t ehci_get_fetch_addr(EHCIState *s, int async) { return async ? s->a_fetch_addr : s->p_fetch_addr; } @@ -373,7 +416,7 @@ static inline bool ehci_periodic_enabled(EHCIState *s) } /* Get an array of dwords from main memory */ -static inline int get_dwords(EHCIState *ehci, uint32_t addr, +static inline int get_dwords(EHCIState *ehci, uint64_t addr, uint32_t *buf, int num) { int i; @@ -395,7 +438,7 @@ static inline int get_dwords(EHCIState *ehci, uint32_t addr, } /* Put an array of dwords in to main memory */ -static inline int put_dwords(EHCIState *ehci, uint32_t addr, +static inline int put_dwords(EHCIState *ehci, uint64_t addr, uint32_t *buf, int num) { int i; @@ -440,7 +483,7 @@ static bool ehci_verify_qh(EHCIQueue *q, EHCIqh *qh) (qh->current_qtd != q->qh.current_qtd) || (q->async && qh->next_qtd != q->qh.next_qtd) || (memcmp(&qh->altnext_qtd, &q->qh.altnext_qtd, - 7 * sizeof(uint32_t)) != 0) || + EHCI_QH_OVERLAY_COUNT * sizeof(uint32_t)) != 0) || (q->dev != NULL && q->dev->addr != devaddr)) { return false; } else { @@ -455,7 +498,8 @@ static bool ehci_verify_qtd(EHCIPacket *p, EHCIqtd *qtd) (p->qtd.next != qtd->next)) || (!NLPTR_TBIT(p->qtd.altnext) && (p->qtd.altnext != qtd->altnext)) || p->qtd.token != qtd->token || - p->qtd.bufptr[0] != qtd->bufptr[0]) { + p->qtd.bufptr[0] != qtd->bufptr[0] || + p->qtd.bufptr_hi[0] != qtd->bufptr_hi[0]) { return false; } else { return true; @@ -487,10 +531,12 @@ static void ehci_writeback_async_complete_packet(EHCIPacket *p) int state; /* Verify the qh + qtd, like we do when going through fetchqh & fetchqtd */ + memset(&qh, 0, sizeof(qh)); + memset(&qtd, 0, sizeof(qtd)); get_dwords(q->ehci, NLPTR_GET(q->qhaddr), - (uint32_t *) &qh, sizeof(EHCIqh) >> 2); + (uint32_t *) &qh, ehci_qh_dwords(q->ehci)); get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), - (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2); + (uint32_t *) &qtd, ehci_qtd_dwords(q->ehci)); if (!ehci_verify_qh(q, &qh) || !ehci_verify_qtd(p, &qtd)) { p->async = EHCI_ASYNC_INITIALIZED; ehci_free_packet(p); @@ -549,7 +595,7 @@ static void ehci_free_packet(EHCIPacket *p) /* queue management */ -static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, uint32_t addr, int async) +static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, uint64_t addr, int async) { EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; EHCIQueue *q; @@ -622,7 +668,7 @@ static void ehci_free_queue(EHCIQueue *q, const char *warn) g_free(q); } -static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr, +static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint64_t addr, int async) { EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; @@ -1109,6 +1155,16 @@ static void ehci_opreg_write(void *ptr, hwaddr addr, } break; + case CTRLDSSEGMENT: + if (!s->caps_64bit_addr) { + qemu_log_mask(LOG_GUEST_ERROR, + "ehci: write to CTRLDSSEGMENT while " + "64-bit addressing capability is disabled\n"); + return; + } + val |= s->ctrldssegment_default; + break; + case ASYNCLISTADDR: if (ehci_async_enabled(s)) { qemu_log_mask(LOG_GUEST_ERROR, @@ -1134,8 +1190,8 @@ static void ehci_opreg_write(void *ptr, hwaddr addr, static void ehci_flush_qh(EHCIQueue *q) { uint32_t *qh = (uint32_t *) &q->qh; - uint32_t dwords = sizeof(EHCIqh) >> 2; - uint32_t addr = NLPTR_GET(q->qhaddr); + uint32_t dwords = ehci_qh_dwords(q->ehci); + uint64_t addr = NLPTR_GET(q->qhaddr); put_dwords(q->ehci, addr + 3 * sizeof(uint32_t), qh + 3, dwords - 3); } @@ -1174,6 +1230,7 @@ static int ehci_qh_do_overlay(EHCIQueue *q) for (i = 0; i < 5; i++) { q->qh.bufptr[i] = p->qtd.bufptr[i]; + q->qh.bufptr_hi[i] = p->qtd.bufptr_hi[i]; } if (!(q->qh.epchar & QH_EPCHAR_DTC)) { @@ -1207,7 +1264,8 @@ static int ehci_init_transfer(EHCIPacket *p) return -1; } - page = p->qtd.bufptr[cpage] & QTD_BUFPTR_MASK; + page = ehci_get_buf_addr(p->queue->ehci, p->qtd.bufptr_hi[cpage], + p->qtd.bufptr[cpage], QTD_BUFPTR_MASK); page += offset; plen = bytes; if (plen > 4096 - offset) { @@ -1406,12 +1464,13 @@ static int ehci_execute(EHCIPacket *p, const char *action) /* 4.7.2 */ static int ehci_process_itd(EHCIState *ehci, EHCIitd *itd, - uint32_t addr) + uint64_t addr) { USBDevice *dev; USBEndpoint *ep; uint32_t i, len, pid, dir, devaddr, endp; - uint32_t pg, off, ptr1, ptr2, max, mult; + uint32_t pg, off, max, mult; + uint64_t ptr1, ptr2; ehci->periodic_sched_active = PERIODIC_ACTIVE; @@ -1434,7 +1493,8 @@ static int ehci_process_itd(EHCIState *ehci, return -1; } - ptr1 = (itd->bufptr[pg] & ITD_BUFPTR_MASK); + ptr1 = ehci_get_buf_addr(ehci, itd->bufptr_hi[pg], + itd->bufptr[pg], ITD_BUFPTR_MASK); qemu_sglist_init(&ehci->isgl, ehci->device, 2, ehci->as); if (off + len > 4096) { /* transfer crosses page border */ @@ -1442,7 +1502,9 @@ static int ehci_process_itd(EHCIState *ehci, qemu_sglist_destroy(&ehci->isgl); return -1; /* avoid page pg + 1 */ } - ptr2 = (itd->bufptr[pg + 1] & ITD_BUFPTR_MASK); + ptr2 = ehci_get_buf_addr(ehci, itd->bufptr_hi[pg + 1], + itd->bufptr[pg + 1], + ITD_BUFPTR_MASK); uint32_t len2 = off + len - 4096; uint32_t len1 = len - len2; qemu_sglist_add(&ehci->isgl, ptr1 + off, len1); @@ -1528,7 +1590,9 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async) EHCIqh qh; int i = 0; int again = 0; - uint32_t entry = ehci->asynclistaddr; + uint64_t entry = 0; + + entry = ehci_get_desc_addr(ehci, ehci->asynclistaddr); /* set reclamation flag at start event (4.8.6) */ if (async) { @@ -1538,9 +1602,10 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async) ehci_queues_rip_unused(ehci, async); /* Find the head of the list (4.9.1.1) */ + memset(&qh, 0, sizeof(qh)); for (i = 0; i < MAX_QH; i++) { if (get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &qh, - sizeof(EHCIqh) >> 2) < 0) { + ehci_qh_dwords(ehci)) < 0) { return 0; } ehci_trace_qh(NULL, NLPTR_GET(entry), &qh); @@ -1556,8 +1621,8 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async) goto out; } - entry = qh.next; - if (entry == ehci->asynclistaddr) { + entry = ehci_get_desc_addr(ehci, qh.next); + if (entry == ehci_get_desc_addr(ehci, ehci->asynclistaddr)) { break; } } @@ -1578,7 +1643,7 @@ out: static int ehci_state_fetchentry(EHCIState *ehci, int async) { int again = 0; - uint32_t entry = ehci_get_fetch_addr(ehci, async); + uint64_t entry = ehci_get_fetch_addr(ehci, async); if (NLPTR_TBIT(entry)) { ehci_set_state(ehci, async, EST_ACTIVE); @@ -1611,8 +1676,8 @@ static int ehci_state_fetchentry(EHCIState *ehci, int async) default: /* TODO: handle FSTN type */ qemu_log_mask(LOG_GUEST_ERROR, - "FETCHENTRY: entry at 0x%x is of type %u " - "which is not supported yet\n", + "FETCHENTRY: entry at %" PRIx64 " is of type %" PRIu64 + " which is not supported yet\n", entry, NLPTR_TYPE_GET(entry)); return -1; } @@ -1623,7 +1688,7 @@ out: static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) { - uint32_t entry; + uint64_t entry; EHCIQueue *q; EHCIqh qh; @@ -1641,8 +1706,9 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) goto out; } + memset(&qh, 0, sizeof(qh)); if (get_dwords(ehci, NLPTR_GET(q->qhaddr), - (uint32_t *) &qh, sizeof(EHCIqh) >> 2) < 0) { + (uint32_t *) &qh, ehci_qh_dwords(ehci)) < 0) { q = NULL; goto out; } @@ -1683,7 +1749,7 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) } if (trace_event_get_state_backends(TRACE_USB_EHCI_FETCHQH_DBG)) { - if (q->qhaddr != q->qh.next) { + if (q->qhaddr != ehci_get_desc_addr(ehci, q->qh.next)) { trace_usb_ehci_fetchqh_dbg(q->qhaddr, q->qh.epchar & QH_EPCHAR_H, q->qh.token & QTD_TOKEN_HALT, @@ -1698,7 +1764,7 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) } else if ((q->qh.token & QTD_TOKEN_ACTIVE) && (NLPTR_TBIT(q->qh.current_qtd) == 0) && (q->qh.current_qtd != 0)) { - q->qtdaddr = q->qh.current_qtd; + q->qtdaddr = ehci_get_desc_addr(ehci, q->qh.current_qtd); ehci_set_state(ehci, async, EST_FETCHQTD); } else { @@ -1712,14 +1778,15 @@ out: static int ehci_state_fetchitd(EHCIState *ehci, int async) { - uint32_t entry; + uint64_t entry; EHCIitd itd; assert(!async); entry = ehci_get_fetch_addr(ehci, async); + memset(&itd, 0, sizeof(itd)); if (get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd, - sizeof(EHCIitd) >> 2) < 0) { + ehci_itd_dwords(ehci)) < 0) { return -1; } ehci_trace_itd(ehci, entry, &itd); @@ -1729,8 +1796,8 @@ static int ehci_state_fetchitd(EHCIState *ehci, int async) } put_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd, - sizeof(EHCIitd) >> 2); - ehci_set_fetch_addr(ehci, async, itd.next); + ehci_itd_dwords(ehci)); + ehci_set_fetch_addr(ehci, async, ehci_get_desc_addr(ehci, itd.next)); ehci_set_state(ehci, async, EST_FETCHENTRY); return 1; @@ -1738,7 +1805,7 @@ static int ehci_state_fetchitd(EHCIState *ehci, int async) static int ehci_state_fetchsitd(EHCIState *ehci, int async) { - uint32_t entry; + uint64_t entry; EHCIsitd sitd; assert(!async); @@ -1757,7 +1824,7 @@ static int ehci_state_fetchsitd(EHCIState *ehci, int async) warn_report("Skipping active siTD"); } - ehci_set_fetch_addr(ehci, async, sitd.next); + ehci_set_fetch_addr(ehci, async, ehci_get_desc_addr(ehci, sitd.next)); ehci_set_state(ehci, async, EST_FETCHENTRY); return 1; } @@ -1776,14 +1843,14 @@ static int ehci_state_advqueue(EHCIQueue *q) */ if (((q->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) && (NLPTR_TBIT(q->qh.altnext_qtd) == 0)) { - q->qtdaddr = q->qh.altnext_qtd; + q->qtdaddr = ehci_get_desc_addr(q->ehci, q->qh.altnext_qtd); ehci_set_state(q->ehci, q->async, EST_FETCHQTD); /* * next qTD is valid */ } else if (NLPTR_TBIT(q->qh.next_qtd) == 0) { - q->qtdaddr = q->qh.next_qtd; + q->qtdaddr = ehci_get_desc_addr(q->ehci, q->qh.next_qtd); ehci_set_state(q->ehci, q->async, EST_FETCHQTD); /* @@ -1802,17 +1869,21 @@ static int ehci_state_fetchqtd(EHCIQueue *q) EHCIqtd qtd; EHCIPacket *p; int again = 1; - uint32_t addr; + uint64_t addr; addr = NLPTR_GET(q->qtdaddr); if (get_dwords(q->ehci, addr + 8, &qtd.token, 1) < 0) { return 0; } barrier(); + memset(qtd.bufptr_hi, 0, sizeof(qtd.bufptr_hi)); 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) { + ARRAY_SIZE(qtd.bufptr)) < 0 || + (q->ehci->caps_64bit_addr && + get_dwords(q->ehci, addr + offsetof(EHCIqtd, bufptr_hi), + qtd.bufptr_hi, ARRAY_SIZE(qtd.bufptr_hi)) < 0)) { return 0; } ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd); @@ -1866,10 +1937,12 @@ static int ehci_state_fetchqtd(EHCIQueue *q) static int ehci_state_horizqh(EHCIQueue *q) { + uint64_t addr; int again = 0; - if (ehci_get_fetch_addr(q->ehci, q->async) != q->qh.next) { - ehci_set_fetch_addr(q->ehci, q->async, q->qh.next); + addr = ehci_get_desc_addr(q->ehci, q->qh.next); + if (ehci_get_fetch_addr(q->ehci, q->async) != addr) { + ehci_set_fetch_addr(q->ehci, q->async, addr); ehci_set_state(q->ehci, q->async, EST_FETCHENTRY); again = 1; } else { @@ -1885,13 +1958,13 @@ static int ehci_fill_queue(EHCIPacket *p) USBEndpoint *ep = p->packet.ep; EHCIQueue *q = p->queue; EHCIqtd qtd = p->qtd; - uint32_t qtdaddr; + uint64_t qtdaddr; for (;;) { if (NLPTR_TBIT(qtd.next) != 0) { break; } - qtdaddr = qtd.next; + qtdaddr = ehci_get_desc_addr(q->ehci, qtd.next); /* * Detect circular td lists, Windows creates these, counting on the * active bit going low after execution to make the queue stop. @@ -1901,8 +1974,9 @@ static int ehci_fill_queue(EHCIPacket *p) goto leave; } } + memset(qtd.bufptr_hi, 0, sizeof(qtd.bufptr_hi)); if (get_dwords(q->ehci, NLPTR_GET(qtdaddr), - (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2) < 0) { + (uint32_t *) &qtd, ehci_qtd_dwords(q->ehci)) < 0) { return -1; } ehci_trace_qtd(q, NLPTR_GET(qtdaddr), &qtd); @@ -2008,7 +2082,8 @@ static int ehci_state_executing(EHCIQueue *q) static int ehci_state_writeback(EHCIQueue *q) { EHCIPacket *p = QTAILQ_FIRST(&q->packets); - uint32_t *qtd, addr; + uint32_t *qtd; + uint64_t addr; int again = 0; /* Write back the QTD from the QH area */ @@ -2194,6 +2269,8 @@ static void ehci_advance_periodic_state(EHCIState *ehci) uint32_t entry; uint32_t list; const int async = 0; + uint64_t entry64; + uint64_t list64; /* 4.6 */ @@ -2218,12 +2295,14 @@ static void ehci_advance_periodic_state(EHCIState *ehci) break; } list |= ((ehci->frindex & 0x1ff8) >> 1); - - if (get_dwords(ehci, list, &entry, 1) < 0) { + list64 = ehci_get_desc_addr(ehci, list); + if (get_dwords(ehci, list64, &entry, 1) < 0) { break; } - trace_usb_ehci_periodic_state_advance(ehci->frindex / 8, list, entry); - ehci_set_fetch_addr(ehci, async, entry); + entry64 = ehci_get_desc_addr(ehci, entry); + trace_usb_ehci_periodic_state_advance(ehci->frindex / 8, + list64, entry64); + ehci_set_fetch_addr(ehci, async, entry64); ehci_set_state(ehci, async, EST_FETCHENTRY); ehci_advance_state(ehci, async); ehci_queues_rip_unused(ehci, async); @@ -2414,6 +2493,18 @@ static USBBusOps ehci_bus_ops_standalone = { .wakeup_endpoint = ehci_wakeup_endpoint, }; +static bool ehci_fetch_addr_64_needed(void *opaque, int version_id) +{ + EHCIState *s = opaque; + + return s->migrate_fetch_addr_64bit; +} + +static bool ehci_fetch_addr_32_needed(void *opaque, int version_id) +{ + return !ehci_fetch_addr_64_needed(opaque, version_id); +} + static int usb_ehci_pre_save(void *opaque) { EHCIState *ehci = opaque; @@ -2424,6 +2515,11 @@ static int usb_ehci_pre_save(void *opaque) ehci->last_run_ns -= (ehci->frindex - new_frindex) * UFRAME_TIMER_NS; ehci->frindex = new_frindex; + if (!ehci->migrate_fetch_addr_64bit) { + ehci->migrate_a_fetch_addr = ehci->a_fetch_addr; + ehci->migrate_p_fetch_addr = ehci->p_fetch_addr; + } + return 0; } @@ -2444,6 +2540,11 @@ static int usb_ehci_post_load(void *opaque, int version_id) } } + if (!s->migrate_fetch_addr_64bit) { + s->a_fetch_addr = s->migrate_a_fetch_addr; + s->p_fetch_addr = s->migrate_p_fetch_addr; + } + return 0; } @@ -2504,8 +2605,14 @@ const VMStateDescription vmstate_ehci = { /* schedule state */ VMSTATE_UINT32(astate, EHCIState), VMSTATE_UINT32(pstate, EHCIState), - VMSTATE_UINT32(a_fetch_addr, EHCIState), - VMSTATE_UINT32(p_fetch_addr, EHCIState), + VMSTATE_UINT32_TEST(migrate_a_fetch_addr, EHCIState, + ehci_fetch_addr_32_needed), + VMSTATE_UINT32_TEST(migrate_p_fetch_addr, EHCIState, + ehci_fetch_addr_32_needed), + VMSTATE_UINT64_TEST(a_fetch_addr, EHCIState, + ehci_fetch_addr_64_needed), + VMSTATE_UINT64_TEST(p_fetch_addr, EHCIState, + ehci_fetch_addr_64_needed), VMSTATE_END_OF_LIST() } }; @@ -2524,6 +2631,9 @@ void usb_ehci_realize(EHCIState *s, DeviceState *dev, Error **errp) s->maxframes); return; } + if (s->caps_64bit_addr) { + s->caps[0x08] |= BIT(0); + } memory_region_add_subregion(&s->mem, s->capsbase, &s->mem_caps); memory_region_add_subregion(&s->mem, s->opregbase, &s->mem_opreg); @@ -2583,7 +2693,7 @@ void usb_ehci_init(EHCIState *s, DeviceState *dev) s->caps[0x05] = 0x00; /* No companion ports at present */ s->caps[0x06] = 0x00; s->caps[0x07] = 0x00; - s->caps[0x08] = 0x80; /* We can cache whole frame, no 64-bit */ + s->caps[0x08] = 0x80; /* We can cache whole frame */ s->caps[0x0a] = 0x00; s->caps[0x0b] = 0x00; diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index d038ee1e31..b5ac9c8670 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -63,6 +63,7 @@ typedef struct EHCIitd { #define ITD_BUFPTR_MAXPKT_SH 0 #define ITD_BUFPTR_MULT_MASK 0x00000003 #define ITD_BUFPTR_MULT_SH 0 + uint32_t bufptr_hi[7]; } EHCIitd; /* @@ -139,8 +140,12 @@ typedef struct EHCIqtd { uint32_t bufptr[5]; /* Standard buffer pointer */ #define QTD_BUFPTR_MASK 0xfffff000 #define QTD_BUFPTR_SH 12 + uint32_t bufptr_hi[5]; } EHCIqtd; +/* QH overlay: altnext_qtd, token, bufptr[5], bufptr_hi[5] */ +#define EHCI_QH_OVERLAY_COUNT 12 + /* * EHCI spec version 1.0 Section 3.6 */ @@ -194,6 +199,7 @@ typedef struct EHCIqh { #define BUFPTR_FRAMETAG_MASK 0x0000001f #define BUFPTR_SBYTES_MASK 0x00000fe0 #define BUFPTR_SBYTES_SH 5 + uint32_t bufptr_hi[5]; } EHCIqh; enum async_state { @@ -208,7 +214,7 @@ struct EHCIPacket { QTAILQ_ENTRY(EHCIPacket) next; EHCIqtd qtd; /* copy of current QTD (being worked on) */ - uint32_t qtdaddr; /* address QTD read from */ + uint64_t qtdaddr; /* address QTD read from */ USBPacket packet; QEMUSGList sgl; @@ -229,8 +235,8 @@ struct EHCIQueue { * when guest removes an entry (doorbell, handshake sequence) */ EHCIqh qh; /* copy of current QH (being worked on) */ - uint32_t qhaddr; /* address QH read from */ - uint32_t qtdaddr; /* address QTD read from */ + uint64_t qhaddr; /* address QH read from */ + uint64_t qtdaddr; /* address QTD read from */ int last_pid; /* pid of last packet executed */ USBDevice *dev; QTAILQ_HEAD(, EHCIPacket) packets; @@ -256,6 +262,13 @@ struct EHCIState { /* properties */ uint32_t maxframes; + /* + * Controls migration stream compatibility for old machine types. + * Old machine types only transfer 32-bit fetch addresses. + */ + bool migrate_fetch_addr_64bit; + bool caps_64bit_addr; + uint32_t ctrldssegment_default; /* * EHCI spec version 1.0 Section 2.3 @@ -293,9 +306,18 @@ struct EHCIState { EHCIQueueHead aqueues; EHCIQueueHead pqueues; - /* which address to look at next */ - uint32_t a_fetch_addr; - uint32_t p_fetch_addr; + /* + * which address to look at next + * + * Migration compatibility fields for old machine types that only + * support 32-bit fetch addresses in the migration stream. + * + * New machine types migrate the full 64-bit runtime fetch address. + */ + uint32_t migrate_a_fetch_addr; + uint32_t migrate_p_fetch_addr; + uint64_t a_fetch_addr; + uint64_t p_fetch_addr; USBPacket ipacket; QEMUSGList isgl; @@ -308,7 +330,13 @@ struct EHCIState { }; #define DEFINE_EHCI_COMMON_PROPERTIES(_state) \ - DEFINE_PROP_UINT32("maxframes", _state, ehci.maxframes, 128) + DEFINE_PROP_UINT32("maxframes", _state, ehci.maxframes, 128), \ + DEFINE_PROP_BOOL("x-migrate-fetch-addr-64bit", _state, \ + ehci.migrate_fetch_addr_64bit, true), \ + DEFINE_PROP_BOOL("caps-64bit-addr", _state, \ + ehci.caps_64bit_addr, false), \ + DEFINE_PROP_UINT32("ctrldssegment-default", _state, \ + ehci.ctrldssegment_default, 0) extern const VMStateDescription vmstate_ehci; diff --git a/hw/usb/hcd-ohci-pci.c b/hw/usb/hcd-ohci-pci.c index 18b58f5fcb..70c9e9ac4f 100644 --- a/hw/usb/hcd-ohci-pci.c +++ b/hw/usb/hcd-ohci-pci.c @@ -25,7 +25,6 @@ #include "migration/vmstate.h" #include "hw/pci/pci_device.h" #include "hw/core/sysbus.h" -#include "hw/core/qdev-dma.h" #include "hw/core/qdev-properties.h" #include "trace.h" #include "hcd-ohci.h" diff --git a/hw/usb/hcd-ohci-sysbus.c b/hw/usb/hcd-ohci-sysbus.c index 1a2cf29bed..4f51eebccb 100644 --- a/hw/usb/hcd-ohci-sysbus.c +++ b/hw/usb/hcd-ohci-sysbus.c @@ -26,7 +26,6 @@ #include "hw/usb/usb.h" #include "migration/vmstate.h" #include "hw/core/sysbus.h" -#include "hw/core/qdev-dma.h" #include "hw/core/qdev-properties.h" #include "trace.h" #include "hcd-ohci.h" @@ -61,7 +60,7 @@ static const Property ohci_sysbus_properties[] = { DEFINE_PROP_STRING("masterbus", OHCISysBusState, masterbus), DEFINE_PROP_UINT32("num-ports", OHCISysBusState, num_ports, 3), DEFINE_PROP_UINT32("firstport", OHCISysBusState, firstport, 0), - DEFINE_PROP_DMAADDR("dma-offset", OHCISysBusState, dma_offset, 0), + DEFINE_PROP_UINT64("dma-offset", OHCISysBusState, dma_offset, 0), }; static void ohci_sysbus_class_init(ObjectClass *klass, const void *data) diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 40ebafb4dd..23990115ab 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -28,12 +28,12 @@ #include "qemu/osdep.h" #include "hw/core/irq.h" #include "qapi/error.h" +#include "qemu/log.h" #include "qemu/module.h" #include "qemu/timer.h" #include "hw/usb/usb.h" #include "migration/vmstate.h" #include "hw/core/sysbus.h" -#include "hw/core/qdev-dma.h" #include "hw/core/qdev-properties.h" #include "trace.h" #include "hcd-ohci.h" @@ -1129,6 +1129,8 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head) return 0; } for (cur = head; cur && link_cnt++ < ED_LINK_LIMIT; cur = next_ed) { + unsigned int ed_cnt = 0; + if (ohci_read_ed(ohci, cur, &ed)) { trace_usb_ohci_ed_read_error(cur); ohci_die(ohci); @@ -1172,6 +1174,13 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head) break; } } + + if (ed_cnt++ > ED_LINK_LIMIT) { + qemu_log_mask(LOG_GUEST_ERROR, + "ohci: Too many endpoint descriptors in loop\n"); + ohci_die(ohci); + return 0; + } } if (ohci_put_ed(ohci, cur, &ed)) { diff --git a/hw/usb/hcd-xhci-pci.c b/hw/usb/hcd-xhci-pci.c index c5446a4a5e..b124251ae3 100644 --- a/hw/usb/hcd-xhci-pci.c +++ b/hw/usb/hcd-xhci-pci.c @@ -196,6 +196,18 @@ static void usb_xhci_pci_exit(PCIDevice *dev) && dev->msix_entry_used) { msix_uninit(dev, &s->xhci.mem, &s->xhci.mem); } + /* + * The embedded xhci-core child holds a strong "host" link back to this + * PCI device (set in usb_xhci_pci_realize()), forming a refcount cycle: + * the PCI device owns the child, and the child's strong link pins the PCI + * device. On unplug, object_unparent() only drops the parent/bus refs, so + * the link ref keeps this device at refcount 1 forever and + * device_finalize() never runs. Unrealize the child first (so the + * realized-check in set_link passes), then clear the link to break the + * cycle. + */ + qdev_unrealize(DEVICE(&s->xhci)); + object_property_set_link(OBJECT(&s->xhci), "host", NULL, &error_abort); } static const VMStateDescription vmstate_xhci_pci = { diff --git a/hw/usb/hcd-xhci-sysbus.c b/hw/usb/hcd-xhci-sysbus.c index 19664c5985..bbdd5fd64a 100644 --- a/hw/usb/hcd-xhci-sysbus.c +++ b/hw/usb/hcd-xhci-sysbus.c @@ -20,6 +20,7 @@ static bool xhci_sysbus_intr_raise(XHCIState *xhci, int n, bool level) { XHCISysbusState *s = container_of(xhci, XHCISysbusState, xhci); + assert(n < xhci->numintrs); qemu_set_irq(s->irq[n], level); return false; diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 2cdab3ba0e..d342aa2739 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -39,8 +39,6 @@ #else #define DPRINTF(...) do {} while (0) #endif -#define FIXME(_msg) do { fprintf(stderr, "FIXME %s:%d %s\n", \ - __func__, __LINE__, _msg); abort(); } while (0) #define TRB_LINK_LIMIT 32 #define COMMAND_LIMIT 256 @@ -965,11 +963,13 @@ static TRBCCode xhci_alloc_device_streams(XHCIState *xhci, unsigned int slotid, * together and make an usb_device_alloc_streams call per group. */ if (epctxs[i]->nr_pstreams != req_nr_streams) { - FIXME("guest streams config not identical for all eps"); + qemu_log_mask(LOG_UNIMP, + "guest streams config not identical for all eps\n"); return CC_RESOURCE_ERROR; } if (eps[i]->max_streams != dev_max_streams) { - FIXME("device streams config not identical for all eps"); + qemu_log_mask(LOG_UNIMP, + "device streams config not identical for all eps\n"); return CC_RESOURCE_ERROR; } } @@ -1009,7 +1009,12 @@ static XHCIStreamContext *xhci_find_stream(XHCIEPContext *epctx, dma_addr_t base; uint32_t ctx[2], sct; - assert(streamid != 0); + if (!streamid) { + qemu_log_mask(LOG_GUEST_ERROR, "xhci: stream ID is zero\n"); + *cc_error = CC_INVALID_STREAM_ID_ERROR; + return NULL; + } + if (epctx->lsa) { if (streamid >= epctx->nr_pstreams) { *cc_error = CC_INVALID_STREAM_ID_ERROR; @@ -1017,7 +1022,8 @@ static XHCIStreamContext *xhci_find_stream(XHCIEPContext *epctx, } sctx = epctx->pstreams + streamid; } else { - fprintf(stderr, "xhci: FIXME: secondary streams not implemented yet"); + qemu_log_mask(LOG_UNIMP, + "xhci: secondary streams not implemented yet\n"); *cc_error = CC_INVALID_STREAM_TYPE_ERROR; return NULL; } @@ -1120,7 +1126,7 @@ static void xhci_init_epctx(XHCIEPContext *epctx, epctx->ring.ccs = ctx[2] & 1; } - epctx->interval = 1 << ((ctx[0] >> 16) & 0xff); + epctx->interval = 1u << MIN((ctx[0] >> 16) & 0xffu, 18u); } static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, @@ -1458,7 +1464,8 @@ static int xhci_xfer_create_sgl(XHCITransfer *xfer, int in_xfer) switch (TRB_TYPE(*trb)) { case TR_DATA: if ((!(trb->control & TRB_TR_DIR)) != (!in_xfer)) { - DPRINTF("xhci: data direction mismatch for TR_DATA\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "xhci: data direction mismatch for TR_DATA\n"); goto err; } /* fallthrough */ @@ -1468,7 +1475,8 @@ static int xhci_xfer_create_sgl(XHCITransfer *xfer, int in_xfer) chunk = trb->status & 0x1ffff; if (trb->control & TRB_TR_IDT) { if (chunk > 8 || in_xfer) { - DPRINTF("xhci: invalid immediate data TRB\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "xhci: invalid immediate data TRB\n"); goto err; } qemu_sglist_add(&xfer->sgl, trb->addr, chunk); @@ -1611,7 +1619,9 @@ static int xhci_setup_packet(XHCITransfer *xfer) } } - xhci_xfer_create_sgl(xfer, dir == USB_TOKEN_IN); /* Also sets int_req */ + if (xhci_xfer_create_sgl(xfer, dir == USB_TOKEN_IN) < 0) { /* Also sets int_req */ + return -1; + } usb_packet_setup(&xfer->packet, dir, ep, xfer->streamid, xfer->trbs[0].addr, false, xfer->int_req); if (usb_packet_map(&xfer->packet, &xfer->sgl)) { @@ -1671,9 +1681,7 @@ static int xhci_try_complete_packet(XHCITransfer *xfer) xhci_stall_ep(xfer); break; default: - DPRINTF("%s: FIXME: status = %d\n", __func__, - xfer->packet.status); - FIXME("unhandled USB_RET_*"); + g_assert_not_reached(); } return 0; } @@ -3040,6 +3048,12 @@ static uint64_t xhci_runtime_read(void *ptr, hwaddr reg, } } else { int v = (reg - 0x20) / 0x20; + + if (v >= xhci->numintrs) { + qemu_log_mask(LOG_GUEST_ERROR, + "xhci: read from nonexistent interrupter %i\n", v); + goto out_trace; + } XHCIInterrupter *intr = &xhci->intr[v]; switch (reg & 0x1f) { case 0x00: /* IMAN */ @@ -3066,6 +3080,7 @@ static uint64_t xhci_runtime_read(void *ptr, hwaddr reg, } } +out_trace: trace_usb_xhci_runtime_read(reg, ret); return ret; } @@ -3083,7 +3098,13 @@ static void xhci_runtime_write(void *ptr, hwaddr reg, trace_usb_xhci_unimplemented("runtime write", reg); return; } + v = (reg - 0x20) / 0x20; + if (v >= xhci->numintrs) { + qemu_log_mask(LOG_GUEST_ERROR, + "xhci: write to nonexistent interrupter %i\n", v); + return; + } intr = &xhci->intr[v]; switch (reg & 0x1f) { diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index bde821e214..dfd9e8bb50 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -690,6 +690,7 @@ static void usbredir_buffered_bulk_in_complete_ftdi(USBRedirDevice *dev, struct buf_packet *bulkp; int count; + assert(maxp != 0); while ((bulkp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq)) && p->actual_length < p->iov.size && p->status == USB_RET_SUCCESS) { if (bulkp->len < 2) { @@ -739,6 +740,7 @@ static void usbredir_handle_buffered_bulk_in_data(USBRedirDevice *dev, .stream_id = 0, .no_transfers = 5, }; + assert(dev->endpoint[EP2I(ep)].max_packet_size != 0); /* Round bytes_per_transfer up to a multiple of max_packet_size */ bpt = 512 + dev->endpoint[EP2I(ep)].max_packet_size - 1; bpt /= dev->endpoint[EP2I(ep)].max_packet_size; @@ -793,6 +795,7 @@ static void usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, } if (dev->endpoint[EP2I(ep)].bulk_receiving_enabled) { + assert(maxp != 0); if (size != 0 && (size % maxp) == 0) { usbredir_handle_buffered_bulk_in_data(dev, p, ep); return; @@ -1796,6 +1799,17 @@ static void usbredir_ep_info(void *priv, if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_ep_info_max_packet_size)) { dev->endpoint[i].max_packet_size = ep_info->max_packet_size[i]; + if (ep_info->max_packet_size[i] == 0 && + dev->endpoint[i].bulk_receiving_enabled) { + USBPacket *p = dev->endpoint[i].pending_async_packet; + usbredir_stop_bulk_receiving(dev, I2EP(i)); + dev->endpoint[i].bulk_receiving_enabled = 0; + if (p != NULL) { + dev->endpoint[i].pending_async_packet = NULL; + p->status = USB_RET_IOERROR; + usb_packet_complete(&dev->dev, p); + } + } } #if USBREDIR_VERSION >= 0x000700 if (usbredirparser_peer_has_cap(dev->parser, @@ -2138,7 +2152,7 @@ static void usbredir_buffered_bulk_packet(void *priv, uint64_t id, USBRedirDevice *dev = priv; uint8_t status, ep = buffered_bulk_packet->endpoint; void *free_on_destroy; - int i, len; + int i, len, queued = 0; DPRINTF("buffered-bulk-in status %d ep %02X len %d id %"PRIu64"\n", buffered_bulk_packet->status, ep, data_len, id); @@ -2156,6 +2170,7 @@ static void usbredir_buffered_bulk_packet(void *priv, uint64_t id, } /* Data must be in maxp chunks for buffered_bulk_add_*_data_to_packet */ + assert(dev->endpoint[EP2I(ep)].max_packet_size != 0); len = dev->endpoint[EP2I(ep)].max_packet_size; status = usb_redir_success; free_on_destroy = NULL; @@ -2169,8 +2184,24 @@ static void usbredir_buffered_bulk_packet(void *priv, uint64_t id, /* bufp_alloc also adds the packet to the ep queue */ r = bufp_alloc(dev, data + i, len, status, ep, free_on_destroy); if (r) { + /* + * Earlier fragments from this packet are in the queue + * with interior pointers into data. If the dropped + * fragment was the final one, bufp_alloc already freed + * data so those pointers are dangling. Remove them. + */ + while (queued > 0) { + struct buf_packet *bufp; + bufp = QTAILQ_LAST(&dev->endpoint[EP2I(ep)].bufpq); + bufp_free(dev, bufp, ep); + queued--; + } + if (!free_on_destroy) { + free(data); + } break; } + queued++; } if (dev->endpoint[EP2I(ep)].pending_async_packet) { @@ -2223,6 +2254,15 @@ static int usbredir_post_load(void *priv, int version_id) usbredir_setup_usb_eps(dev); usbredir_check_bulk_receiving(dev); + for (int i = 0; i < MAX_ENDPOINTS; i++) { + if (dev->endpoint[i].bulk_receiving_started && + dev->endpoint[i].max_packet_size == 0) { + error_report("usbredir: endpoint %d has bulk receiving started " + "with zero max_packet_size", i); + return -EINVAL; + } + } + return 0; } diff --git a/hw/usb/trace-events b/hw/usb/trace-events index 0d4318dcf1..67249d69c2 100644 --- a/hw/usb/trace-events +++ b/hw/usb/trace-events @@ -86,15 +86,15 @@ usb_ehci_portsc_write(uint32_t addr, uint32_t port, uint32_t val) "wr mmio 0x%04 usb_ehci_portsc_change(uint32_t addr, uint32_t port, uint32_t new, uint32_t old) "ch mmio 0x%04x [port %d] = 0x%x (old: 0x%x)" usb_ehci_usbsts(const char *sts, int state) "usbsts %s %d" usb_ehci_state(const char *schedule, const char *state) "%s schedule %s" -usb_ehci_qh_ptrs(void *q, uint32_t addr, uint32_t nxt, uint32_t c_qtd, uint32_t n_qtd, uint32_t a_qtd) "q %p - QH @ 0x%08x: next 0x%08x qtds 0x%08x,0x%08x,0x%08x" -usb_ehci_qh_fields(uint32_t addr, int rl, int mplen, int eps, int ep, int devaddr) "QH @ 0x%08x - rl %d, mplen %d, eps %d, ep %d, dev %d" -usb_ehci_qh_bits(uint32_t addr, int c, int h, int dtc, int i) "QH @ 0x%08x - c %d, h %d, dtc %d, i %d" +usb_ehci_qh_ptrs(void *q, uint64_t addr, uint32_t nxt, uint32_t c_qtd, uint32_t n_qtd, uint32_t a_qtd) "q %p - QH @ 0x%" PRIx64 ": next 0x%08x qtds 0x%08x,0x%08x,0x%08x" +usb_ehci_qh_fields(uint64_t addr, int rl, int mplen, int eps, int ep, int devaddr) "QH @ 0x%" PRIx64 " - rl %d, mplen %d, eps %d, ep %d, dev %d" +usb_ehci_qh_bits(uint64_t addr, int c, int h, int dtc, int i) "QH @ 0x%" PRIx64 " - c %d, h %d, dtc %d, i %d" usb_ehci_qh_tbytes(uint32_t tbytes) "updating tbytes to %d" -usb_ehci_qtd_ptrs(void *q, uint32_t addr, uint32_t nxt, uint32_t altnext) "q %p - QTD @ 0x%08x: next 0x%08x altnext 0x%08x" -usb_ehci_qtd_fields(uint32_t addr, int tbytes, int cpage, int cerr, int pid) "QTD @ 0x%08x - tbytes %d, cpage %d, cerr %d, pid %d" -usb_ehci_qtd_bits(uint32_t addr, int ioc, int active, int halt, int babble, int xacterr) "QTD @ 0x%08x - ioc %d, active %d, halt %d, babble %d, xacterr %d" -usb_ehci_itd(uint32_t addr, uint32_t nxt, uint32_t mplen, uint32_t mult, uint32_t ep, uint32_t devaddr) "ITD @ 0x%08x: next 0x%08x - mplen %d, mult %d, ep %d, dev %d" -usb_ehci_sitd(uint32_t addr, uint32_t nxt, uint32_t active) "ITD @ 0x%08x: next 0x%08x - active %d" +usb_ehci_qtd_ptrs(void *q, uint64_t addr, uint32_t nxt, uint32_t altnext) "q %p - QTD @ 0x%" PRIx64 ": next 0x%08x altnext 0x%08x" +usb_ehci_qtd_fields(uint64_t addr, int tbytes, int cpage, int cerr, int pid) "QTD @ 0x%" PRIx64 " - tbytes %d, cpage %d, cerr %d, pid %d" +usb_ehci_qtd_bits(uint64_t addr, int ioc, int active, int halt, int babble, int xacterr) "QTD @ 0x%" PRIx64 " - ioc %d, active %d, halt %d, babble %d, xacterr %d" +usb_ehci_itd(uint64_t addr, uint32_t nxt, uint32_t mplen, uint32_t mult, uint32_t ep, uint32_t devaddr) "ITD @ 0x%" PRIx64 ": next 0x%08x - mplen %d, mult %d, ep %d, dev %d" +usb_ehci_sitd(uint64_t addr, uint32_t nxt, uint32_t active) "SITD @ 0x%" PRIx64 ": next 0x%08x - active %d" usb_ehci_port_attach(uint32_t port, const char *owner, const char *device) "attach port #%d, owner %s, device %s" usb_ehci_port_detach(uint32_t port, const char *owner) "detach port #%d, owner %s" usb_ehci_port_reset(uint32_t port, int enable) "reset port #%d - %d" @@ -104,16 +104,16 @@ usb_ehci_port_resume(uint32_t port) "port #%d" usb_ehci_port_disable(uint32_t port) "port #%d" usb_ehci_queue_action(void *q, const char *action) "q %p: %s" usb_ehci_packet_action(void *q, void *p, const char *action) "q %p p %p: %s" -usb_ehci_packet_submit(uint32_t qhaddr, uint32_t next, uint32_t qtdaddr, int pid, size_t len, int endp, int status, int actual_length) "qh=0x%x, next=0x%x, qtd=0x%x, pid=0x%x, len=%zd, endp=0x%x, status=%d, actual_length=%d" +usb_ehci_packet_submit(uint64_t qhaddr, uint32_t next, uint64_t qtdaddr, int pid, size_t len, int endp, int status, int actual_length) "qh=0x%" PRIx64 ", next=0x%x, qtd=0x%" PRIx64 ", pid=0x%x, len=%zd, endp=0x%x, status=%d, actual_length=%d" usb_ehci_irq(uint32_t level, uint32_t frindex, uint32_t sts, uint32_t mask) "level %d, frindex 0x%04x, sts 0x%x, mask 0x%x" usb_ehci_guest_bug(const char *reason) "%s" usb_ehci_doorbell_ring(void) "" usb_ehci_doorbell_ack(void) "" usb_ehci_dma_error(void) "" -usb_ehci_execute_complete(uint32_t qhaddr, uint32_t next, uint32_t qtdaddr, int status, int actual_length) "qhaddr=0x%x, next=0x%x, qtdaddr=0x%x, status=%d, actual_length=%d" -usb_ehci_fetchqh_reclaim_done(uint32_t qhaddr) "QH 0x%08x H-bit set, reclamation status reset - done processing" -usb_ehci_fetchqh_dbg(uint32_t qhaddr, uint32_t h, uint32_t halt, uint32_t active, uint32_t next) "QH 0x%08x (h 0x%x halt 0x%x active 0x%x) next 0x%08x" -usb_ehci_periodic_state_advance(uint32_t frame, uint32_t list, uint32_t entry) "frame=%d, list=0x%x, entry=0x%x" +usb_ehci_execute_complete(uint64_t qhaddr, uint32_t next, uint64_t qtdaddr, int status, int actual_length) "qhaddr=0x%" PRIx64 ", next=0x%x, qtdaddr=0x%" PRIx64 ", status=%d, actual_length=%d" +usb_ehci_fetchqh_reclaim_done(uint64_t qhaddr) "QH 0x%" PRIx64 " H-bit set, reclamation status reset - done processing" +usb_ehci_fetchqh_dbg(uint64_t qhaddr, uint32_t h, uint32_t halt, uint32_t active, uint32_t next) "QH 0x%" PRIx64 " (h 0x%x halt 0x%x active 0x%x) next 0x%08x" +usb_ehci_periodic_state_advance(uint32_t frame, uint64_t list, uint64_t entry) "frame=%d, list=0x%" PRIx64 ", entry=0x%" PRIx64 usb_ehci_skipped_uframes(uint64_t skipped_uframes) "skipped %" PRIu64 " uframes" usb_ehci_log(const char *msg) "%s" diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c index b8d2b7c1a8..1a01e748cd 100644 --- a/hw/vfio-user/device.c +++ b/hw/vfio-user/device.c @@ -79,9 +79,18 @@ vfio_user_device_io_device_feature(VFIODevice *vbasedev, struct vfio_device_feature *feature) { g_autofree VFIOUserDeviceFeature *msgp = NULL; - int size = sizeof(VFIOUserHdr) + feature->argsz; VFIOUserProxy *proxy = vbasedev->proxy; Error *local_err = NULL; + int size; + + if (__builtin_add_overflow(feature->argsz, sizeof(VFIOUserHdr), &size)) { + error_printf("vfio_user_device_io_device_feature argsz too large\n"); + return -E2BIG; + } + if (size > proxy->max_xfer_size) { + error_printf("vfio_user_device_io_device_feature argsz too large\n"); + return -E2BIG; + } msgp = g_malloc0(size); @@ -128,12 +137,25 @@ static int vfio_user_get_region_info(VFIOUserProxy *proxy, error_printf("vfio_user_get_region_info argsz too small\n"); return -E2BIG; } + + /* + * Ensure that size doesn't overflow, otherwise we'll allocate a much + * smaller buffer than we need. + */ + if (__builtin_add_overflow(info->argsz, sizeof(VFIOUserHdr), &size)) { + error_printf("vfio_user_get_region_info argsz too large\n"); + return -E2BIG; + } + if (size > proxy->max_xfer_size) { + error_printf("vfio_user_get_region_info argsz too large\n"); + return -E2BIG; + } + if (fds != NULL && fds->send_fds != 0) { error_printf("vfio_user_get_region_info can't send FDs\n"); return -EINVAL; } - size = info->argsz + sizeof(VFIOUserHdr); msgp = g_malloc0(size); vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_GET_REGION_INFO, @@ -152,6 +174,21 @@ static int vfio_user_get_region_info(VFIOUserProxy *proxy, } trace_vfio_user_get_region_info(msgp->index, msgp->flags, msgp->size); + if (msgp->argsz < sizeof(*info)) { + error_printf("vfio_user_get_region_info reply argsz too small\n"); + return -EINVAL; + } + + /* + * The server can respond with a larger argsz in the reply to request a + * larger buffer on the next iteration via vfio_device_get_region_info(). + * Reject values that would trigger an oversized realloc. + */ + if (msgp->argsz > proxy->max_xfer_size) { + error_printf("vfio_user_get_region_info reply argsz too large\n"); + return -E2BIG; + } + memcpy(info, &msgp->argsz, info->argsz); /* @@ -186,7 +223,8 @@ static int vfio_user_device_io_get_region_info(VFIODevice *vbasedev, /* cap_offset in valid area */ if ((info->flags & VFIO_REGION_INFO_FLAG_CAPS) && - (info->cap_offset < sizeof(*info) || info->cap_offset > info->argsz)) { + (info->cap_offset < sizeof(*info) + || info->cap_offset + sizeof(struct vfio_info_cap_header) > info->argsz)) { return -EINVAL; } @@ -255,7 +293,15 @@ static int vfio_user_device_io_set_irqs(VFIODevice *vbasedev, * Handle simple case */ if ((irq->flags & VFIO_IRQ_SET_DATA_EVENTFD) == 0) { - size = sizeof(VFIOUserHdr) + irq->argsz; + if (__builtin_add_overflow(irq->argsz, sizeof(VFIOUserHdr), &size)) { + error_printf("vfio_user_set_irqs argsz too large\n"); + return -E2BIG; + } + if (size > proxy->max_xfer_size) { + error_printf("vfio_user_device_io_set_irqs argsz too large\n"); + return -E2BIG; + } + msgp = g_malloc0(size); vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_SET_IRQS, size, 0); diff --git a/hw/vfio/container-legacy.c b/hw/vfio/container-legacy.c index d301b27aa6..7ac7b37214 100644 --- a/hw/vfio/container-legacy.c +++ b/hw/vfio/container-legacy.c @@ -1075,7 +1075,7 @@ static int vfio_legacy_pci_hot_reset(VFIODevice *vbasedev, bool single) /* Prep dependent devices for reset and clear our marker. */ QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { - if (!vbasedev_iter->dev->realized || + if (!qdev_is_realized(vbasedev_iter->dev) || !vfio_pci_from_vfio_device(vbasedev_iter)) { continue; } @@ -1160,7 +1160,7 @@ out: } QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { - if (!vbasedev_iter->dev->realized || + if (!qdev_is_realized(vbasedev_iter->dev) || !vfio_pci_from_vfio_device(vbasedev_iter)) { continue; } diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 1a7f8088aa..4f11959633 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -59,13 +59,13 @@ void vfio_device_reset_handler(void *opaque) trace_vfio_device_reset_handler(); QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { - if (vbasedev->dev->realized) { + if (qdev_is_realized(vbasedev->dev)) { vbasedev->ops->vfio_compute_needs_reset(vbasedev); } } QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { - if (vbasedev->dev->realized && vbasedev->needs_reset) { + if (qdev_is_realized(vbasedev->dev) && vbasedev->needs_reset) { vbasedev->ops->vfio_hot_reset_multi(vbasedev); } } diff --git a/hw/vfio/igd-stubs.c b/hw/vfio/igd-stubs.c index f7687d9091..5f60b24c8b 100644 --- a/hw/vfio/igd-stubs.c +++ b/hw/vfio/igd-stubs.c @@ -18,3 +18,8 @@ bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) { return true; } + +void vfio_igd_legacy_rom_quirk(VFIOPCIDevice *vdev) +{ + return; +} diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index e091f21b6a..413a49aae9 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -724,3 +724,118 @@ bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) return vfio_pci_igd_config_quirk(vdev, errp); } + +/* + * IGD ROM BAR read from kernel is actually the host VBIOS shadow RAM region, + * which contains host modifications. In Gen 6-9 VBIOS, the routine below is + * used to get BDSM value when programming the initial GTT. + * xx xx xx xx v: .long ? # saved value + * 66 53 push %ebx + * 66 2e 83 3e xx xx 00 cmpl $0x0,%cs:v # is saved value empty? + * 74 07 je 1f # if zero, go compute + * 66 2e a1 xx xx mov %cs:v,%eax # else return saved value + * eb 0f jmp 2f + * b8 5e 10 1: mov $0x105e,%ax # dev 00:02.0, offset 5E + * e8 xx xx call pci_read_cfg_word + * 66 c1 e0 10 shl $0x10,%eax # left shift 16 bits + * 66 2e a3 xx xx mov %eax,%cs:v # save the result + * 66 5b 2: pop %ebx + * c3 ret + * When running the VBIOS in guest, saved value still reflects the host stolen + * memory base address, which is not correct in guest. So we need to patch the + * VBIOS to clear the saved value. + * + * The unique 19-byte starts at `cmpl $0,%cs:v` and ends at `mov $0x105e,%ax` + * anchors the match to the routine. Both `cs:` displacements must reference + * the same offset. + */ +static int igd_vbios_find_saved_bdsm(const uint8_t *rom, size_t rom_size, + uint16_t *bdsm_offset) +{ + static const uint8_t start[] = { 0x66, 0x2e, 0x83, 0x3e }; + static const uint8_t middle[] = { 0x00, 0x74, 0x07, 0x66, 0x2e, 0xa1 }; + static const uint8_t end[] = { 0xeb, 0x0f, 0xb8, 0x5e, 0x10 }; + uint16_t val; + size_t i; + bool found = false; + + if (rom_size < 19) { + return -ENOENT; + } + + for (i = 0; i + 19 <= rom_size; i++) { + if (memcmp(rom + i, start, sizeof(start)) != 0 || + memcmp(rom + i + 6, middle, sizeof(middle)) != 0 || + memcmp(rom + i + 14, end, sizeof(end)) != 0) { + continue; + } + + /* same saved value address? */ + if (rom[i + 4] != rom[i + 12] || rom[i + 5] != rom[i + 13]) { + continue; + } + + if (found) { + return -EEXIST; + } + + val = rom[i + 4] | ((uint16_t)rom[i + 5] << 8); + if (val + sizeof(uint32_t) <= rom_size) { + *bdsm_offset = val; + found = true; + } + } + + if (!found) { + return -ENOENT; + } + + return 0; +} + +void vfio_igd_legacy_rom_quirk(VFIOPCIDevice *vdev) +{ + uint8_t *rom = vdev->rom; + int gen; + uint16_t pcir_offset; + uint16_t bdsm_offset = 0; + uint8_t checksum = 0; + uint32_t i; + + if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || + !vfio_is_vga(vdev) || !vdev->vga) { + return; + } + + /* Only Gen 6~9 devices have legacy VBIOS as Option ROM */ + gen = igd_gen(vdev); + if (gen < 6 || gen > 9) { + return; + } + + if (pci_get_word(rom) != 0xaa55) { + return; + } + + /* Must be a legacy ROM */ + pcir_offset = pci_get_word(rom + 0x18); + if (pcir_offset + 0x14 >= vdev->rom_size || + memcmp(rom + pcir_offset, "PCIR", 4) || + pci_get_byte(rom + pcir_offset + 0x14) != 0x00) { + return; + } + + /* Search and clear the saved BDSM value */ + if (igd_vbios_find_saved_bdsm(rom, vdev->rom_size, &bdsm_offset)) { + return; + } + memset(rom + bdsm_offset, 0, sizeof(uint32_t)); + + /* Recalculate checksum and patch it. */ + for (i = 0; i < vdev->rom_size; i++) { + checksum += rom[i]; + } + rom[6] -= checksum; + + trace_vfio_pci_igd_vbios_patched(vdev->vbasedev.name); +} diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 6ff668d259..242644aa00 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -829,7 +829,7 @@ iommufd_cdev_dep_get_realized_vpdev(struct vfio_pci_dependent_device *dep_dev, vbasedev_tmp = iommufd_cdev_pci_find_by_devid(dep_dev->devid); if (!vfio_pci_from_vfio_device(vbasedev_tmp) || - !vbasedev_tmp->dev->realized) { + !qdev_is_realized(vbasedev_tmp->dev)) { return NULL; } diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index c19600e980..008f488a3e 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include -#include #include #include "exec/target_page.h" diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index bccf31751f..c5b4f9091d 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -1592,3 +1592,8 @@ bool vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp) return true; } + +void vfio_rom_quirk_setup(VFIOPCIDevice *vdev) +{ + vfio_igd_legacy_rom_quirk(vdev); +} diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index c204706e63..428ab2f069 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -323,15 +323,26 @@ static void vfio_irqchip_change(Notifier *notify, void *data) static bool vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) { PCIDevice *pdev = PCI_DEVICE(vdev); - uint8_t pin = vfio_pci_read_config(pdev, PCI_INTERRUPT_PIN, 1); + uint32_t val = vfio_pci_read_config(pdev, PCI_INTERRUPT_PIN, 1); + uint8_t pin; Error *err = NULL; int32_t fd; + if (val == (uint32_t)-1) { + error_setg(errp, "failed to read PCI_INTERRUPT_PIN"); + return false; + } + pin = val; if (!pin) { return true; } + if (pin > PCI_NUM_PINS) { + error_setg(errp, "invalid PCI interrupt pin %d", pin); + return false; + } + /* * Do not alter interrupt state during vfio_realize and cpr load. * The incoming state is cleared thereafter. @@ -578,7 +589,7 @@ static void vfio_connect_kvm_msi_virq(VFIOMSIVector *vector, int nr) { const char *name = "kvm_interrupt"; - if (vector->virq < 0) { + if (!vector->use || vector->virq < 0) { return; } @@ -688,7 +699,7 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, if (msg) { if (vdev->defer_kvm_irq_routing) { vfio_pci_add_kvm_msi_virq(vdev, vector, nr, true); - } else { + } else if (accel_msi_via_irqfd_enabled()) { vfio_route_change = accel_irqchip_begin_route_changes(); vfio_pci_add_kvm_msi_virq(vdev, vector, nr, true); accel_irqchip_commit_route_changes(&vfio_route_change); @@ -790,7 +801,9 @@ void vfio_pci_prepare_kvm_msi_virq_batch(VFIOPCIDevice *vdev) { assert(!vdev->defer_kvm_irq_routing); vdev->defer_kvm_irq_routing = true; - vfio_route_change = accel_irqchip_begin_route_changes(); + if (accel_msi_via_irqfd_enabled()) { + vfio_route_change = accel_irqchip_begin_route_changes(); + } } void vfio_pci_commit_kvm_msi_virq_batch(VFIOPCIDevice *vdev) @@ -1119,6 +1132,8 @@ static bool vfio_pci_load_rom(VFIOPCIDevice *vdev, Error **errp) } } + vfio_rom_quirk_setup(vdev); + return true; } @@ -2764,6 +2779,7 @@ bool vfio_pci_add_capabilities(VFIOPCIDevice *vdev, Error **errp) void vfio_pci_pre_reset(VFIOPCIDevice *vdev) { PCIDevice *pdev = PCI_DEVICE(vdev); + uint32_t val; uint16_t cmd; vfio_disable_interrupts(vdev); @@ -2772,23 +2788,34 @@ void vfio_pci_pre_reset(VFIOPCIDevice *vdev) * Stop any ongoing DMA by disconnecting I/O, MMIO, and bus master. * Also put INTx Disable in known state. */ - cmd = vfio_pci_read_config(pdev, PCI_COMMAND, 2); - cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | - PCI_COMMAND_INTX_DISABLE); - vfio_pci_write_config(pdev, PCI_COMMAND, cmd, 2); + val = vfio_pci_read_config(pdev, PCI_COMMAND, 2); + if (val != (uint32_t)-1) { + cmd = val; + cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | + PCI_COMMAND_INTX_DISABLE); + vfio_pci_write_config(pdev, PCI_COMMAND, cmd, 2); + } /* Make sure the device is in D0 */ if (pdev->pm_cap) { uint16_t pmcsr; uint8_t state; - pmcsr = vfio_pci_read_config(pdev, pdev->pm_cap + PCI_PM_CTRL, 2); + val = vfio_pci_read_config(pdev, pdev->pm_cap + PCI_PM_CTRL, 2); + if (val == (uint32_t)-1) { + return; + } + pmcsr = val; state = pmcsr & PCI_PM_CTRL_STATE_MASK; if (state) { pmcsr &= ~PCI_PM_CTRL_STATE_MASK; vfio_pci_write_config(pdev, pdev->pm_cap + PCI_PM_CTRL, pmcsr, 2); /* vfio handles the necessary delay here */ - pmcsr = vfio_pci_read_config(pdev, pdev->pm_cap + PCI_PM_CTRL, 2); + val = vfio_pci_read_config(pdev, pdev->pm_cap + PCI_PM_CTRL, 2); + if (val == (uint32_t)-1) { + return; + } + pmcsr = val; state = pmcsr & PCI_PM_CTRL_STATE_MASK; if (state) { error_report("vfio: Unable to power on device, stuck in D%d", diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index fe52e9df6e..c9ab949870 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -252,10 +252,13 @@ void vfio_bar_quirk_exit(VFIOPCIDevice *vdev, int nr); void vfio_bar_quirk_finalize(VFIOPCIDevice *vdev, int nr); void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev); bool vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp); +void vfio_rom_quirk_setup(VFIOPCIDevice *vdev); void vfio_quirk_reset(VFIOPCIDevice *vdev); VFIOQuirk *vfio_quirk_alloc(int nr_mem); + void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr); bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp); +void vfio_igd_legacy_rom_quirk(VFIOPCIDevice *vdev); extern const PropertyInfo qdev_prop_nv_gpudirect_clique; diff --git a/hw/vfio/region.c b/hw/vfio/region.c index dbde339180..54ad11a6c8 100644 --- a/hw/vfio/region.c +++ b/hw/vfio/region.c @@ -321,13 +321,12 @@ static bool vfio_region_create_dma_buf(VFIORegion *region, Error **errp) ret = vfio_device_get_feature(vbasedev, feature); if (ret < 0) { if (ret == -ENOTTY) { - warn_report_once("VFIO dma-buf not supported in kernel: " - "PCI BAR IOMMU mappings may fail"); + warn_report_once("VFIO dma-buf not supported in kernel, " + "using mmap fallback, P2P DMA will not work"); return true; } - /* P2P DMA or exposing device memory use cases are not supported. */ - error_setg_errno(errp, -ret, "%s: failed to create dma-buf: " - "PCI BAR IOMMU mappings may fail", + error_setg_errno(errp, -ret, "%s: dma-buf unavailable, " + "using mmap fallback, P2P DMA will not work", memory_region_name(region->mem)); return false; } @@ -448,7 +447,7 @@ int vfio_region_mmap(VFIORegion *region) } if (!vfio_region_create_dma_buf(region, &local_err)) { - error_report_err(local_err); + warn_report_err_once(local_err); } return 0; diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index f71d0bbc0a..bfdaf229e4 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -90,6 +90,7 @@ vfio_pci_igd_bar4_write(const char *name, uint32_t index, uint32_t data, uint32_ vfio_pci_igd_bdsm_enabled(const char *name, int size) "%s %dMB" vfio_pci_igd_host_bridge_enabled(const char *name) "%s" vfio_pci_igd_lpc_bridge_enabled(const char *name) "%s" +vfio_pci_igd_vbios_patched(const char *name) "%s" # listener.c vfio_iommu_map_notify(const char *op, uint64_t iova_start, uint64_t iova_end) "iommu %s @ 0x%"PRIx64" - 0x%"PRIx64 diff --git a/hw/virtio/vdpa-dev.c b/hw/virtio/vdpa-dev.c index 089a77f4d0..6dc684ab09 100644 --- a/hw/virtio/vdpa-dev.c +++ b/hw/virtio/vdpa-dev.c @@ -212,7 +212,7 @@ vhost_vdpa_device_set_config(VirtIODevice *vdev, const uint8_t *config) VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); int ret; - ret = vhost_dev_set_config(&s->dev, s->config, 0, s->config_size, + ret = vhost_dev_set_config(&s->dev, config, 0, s->config_size, VHOST_SET_CONFIG_TYPE_FRONTEND); if (ret) { error_report("set device config space failed"); diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 517cc4ca71..2881cec72d 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -946,6 +946,9 @@ static int vhost_user_add_remove_regions(struct vhost_dev *dev, msg->hdr.size = sizeof(msg->payload.mem_reg); + /* Ensure nregions fits the fixed-size arrays used below. */ + assert(dev->mem->nregions <= VHOST_USER_MAX_RAM_SLOTS); + /* Find the regions which need to be removed or added. */ scrub_shadow_regions(dev, add_reg, &nr_add_reg, rem_reg, &nr_rem_reg, shadow_pcb, track_ramblocks); diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index af41841b52..371dca17dd 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -755,8 +755,9 @@ static void vhost_commit(MemoryListener *listener) (void *)(uintptr_t)dev->mem->regions[i].userspace_addr, dev->mem->regions[i].guest_phys_addr, dev->mem->regions[i].memory_size)) { - error_report("Verify ring failure on region %d", i); - abort(); + virtio_error(dev->vdev, + "Verify ring failure on region %d", i); + goto out; } } diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 08f7e8b783..533bd5073f 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -993,6 +993,18 @@ static int virtio_iommu_handle_probe(VirtIOIOMMU *s, return ret ? ret : virtio_iommu_probe(s, &req, buf); } +static void virtio_iommu_handle_command(VirtIODevice *vdev, VirtQueue *vq); + +static void virtio_iommu_handle_command_timer(void *opaque) +{ + VirtIOIOMMU *s = opaque; + VirtIODevice *vdev = VIRTIO_DEVICE(s); + + if (virtio_device_started(vdev, vdev->status) && !vdev->broken) { + virtio_iommu_handle_command(vdev, s->req_vq); + } +} + static void virtio_iommu_handle_command(VirtIODevice *vdev, VirtQueue *vq) { VirtIOIOMMU *s = VIRTIO_IOMMU(vdev); @@ -1003,10 +1015,17 @@ static void virtio_iommu_handle_command(VirtIODevice *vdev, VirtQueue *vq) struct iovec *iov; void *buf = NULL; size_t sz; + unsigned int batch = 0; for (;;) { size_t output_size = sizeof(tail); + if (++batch > virtio_queue_get_num(vdev, virtio_get_queue_index(vq))) { + timer_mod(s->cmd_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) + 1); + break; + } + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); if (!elem) { return; @@ -1416,6 +1435,8 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) s->req_vq = virtio_add_queue(vdev, VIOMMU_DEFAULT_QUEUE_SIZE, virtio_iommu_handle_command); s->event_vq = virtio_add_queue(vdev, VIOMMU_DEFAULT_QUEUE_SIZE, NULL); + s->cmd_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT, + virtio_iommu_handle_command_timer, s); /* * config.bypass is needed to get initial address space early, such as @@ -1498,6 +1519,7 @@ static void virtio_iommu_device_unrealize(DeviceState *dev) qemu_rec_mutex_destroy(&s->mutex); + timer_free(s->cmd_timer); virtio_delete_queue(s->req_vq); virtio_delete_queue(s->event_vq); virtio_cleanup(vdev); @@ -1509,6 +1531,8 @@ static void virtio_iommu_device_reset_exit(Object *obj, ResetType type) trace_virtio_iommu_device_reset_exit(); + timer_del(s->cmd_timer); + if (s->domains) { g_tree_destroy(s->domains); } @@ -1628,6 +1652,11 @@ static int iommu_post_load(void *opaque, int version_id) * still correct. */ virtio_iommu_switch_address_space_all(s); + + if (virtio_device_started(VIRTIO_DEVICE(s), VIRTIO_DEVICE(s)->status)) { + timer_mod(s->cmd_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) + 1); + } return 0; } diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index 35e03ed759..7130ed852d 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -1453,7 +1453,7 @@ static void virtio_mem_set_requested_size(Object *obj, Visitor *v, * The block size and memory backend are not fixed until the device was * realized. realize() will verify these properties then. */ - if (DEVICE(obj)->realized) { + if (qdev_is_realized(DEVICE(obj))) { if (!QEMU_IS_ALIGNED(value, vmem->block_size)) { error_setg(errp, "'%s' has to be multiples of '%s' (0x%" PRIx64 ")", name, VIRTIO_MEM_BLOCK_SIZE_PROP, @@ -1507,7 +1507,7 @@ static void virtio_mem_set_block_size(Object *obj, Visitor *v, const char *name, VirtIOMEM *vmem = VIRTIO_MEM(obj); uint64_t value; - if (DEVICE(obj)->realized) { + if (qdev_is_realized(DEVICE(obj))) { error_setg(errp, "'%s' cannot be changed", name); return; } diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 58c6d46aab..55ceaeef5f 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -172,10 +172,7 @@ static uint64_t virtio_mmio_read(void *opaque, hwaddr offset, unsigned size) >> (32 * proxy->host_features_sel); } case VIRTIO_MMIO_QUEUE_NUM_MAX: - if (!virtio_queue_get_num(vdev, vdev->queue_sel)) { - return 0; - } - return VIRTQUEUE_MAX_SIZE; + return virtio_queue_get_max_num(vdev, vdev->queue_sel); case VIRTIO_MMIO_QUEUE_PFN: if (!proxy->legacy) { qemu_log_mask(LOG_GUEST_ERROR, @@ -738,6 +735,8 @@ static const Property virtio_mmio_properties[] = { DEFINE_PROP_BOOL("force-legacy", VirtIOMMIOProxy, legacy, true), DEFINE_PROP_BIT("ioeventfd", VirtIOMMIOProxy, flags, VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT16(VIRTIO_QUEUE_SIZE_OVERRIDE, VirtIOMMIOProxy, + override_queue_size, 0), }; static void virtio_mmio_realizefn(DeviceState *d, Error **errp) diff --git a/hw/virtio/virtio-pmem.c b/hw/virtio/virtio-pmem.c index c3b3299c9c..6f7271c140 100644 --- a/hw/virtio/virtio-pmem.c +++ b/hw/virtio/virtio-pmem.c @@ -23,6 +23,7 @@ #include "standard-headers/linux/virtio_pmem.h" #include "system/hostmem.h" #include "block/thread-pool.h" +#include "qemu/aio-wait.h" #include "trace.h" typedef struct VirtIODeviceRequest { @@ -54,14 +55,20 @@ static int worker_cb(void *opaque) static void done_cb(void *opaque, int ret) { VirtIODeviceRequest *req_data = opaque; + VirtIOPMEM *pmem = req_data->pmem; int len = iov_from_buf(req_data->elem.in_sg, req_data->elem.in_num, 0, &req_data->resp, sizeof(struct virtio_pmem_resp)); /* Callbacks are serialized, so no need to use atomic ops. */ - virtqueue_push(req_data->pmem->rq_vq, &req_data->elem, len); - virtio_notify((VirtIODevice *)req_data->pmem, req_data->pmem->rq_vq); + virtqueue_push(pmem->rq_vq, &req_data->elem, len); + virtio_notify((VirtIODevice *)pmem, pmem->rq_vq); trace_virtio_pmem_response(); g_free(req_data); + + pmem->inflight--; + if (!pmem->inflight) { + aio_wait_kick(); + } } static void virtio_pmem_flush(VirtIODevice *vdev, VirtQueue *vq) @@ -85,6 +92,7 @@ static void virtio_pmem_flush(VirtIODevice *vdev, VirtQueue *vq) req_data->fd = memory_region_get_fd(&backend->mr); req_data->pmem = pmem; req_data->vdev = vdev; + pmem->inflight++; thread_pool_submit_aio(worker_cb, req_data, done_cb, req_data); } @@ -122,6 +130,7 @@ static void virtio_pmem_realize(DeviceState *dev, Error **errp) host_memory_backend_set_mapped(pmem->memdev, true); virtio_init(vdev, VIRTIO_ID_PMEM, sizeof(struct virtio_pmem_config)); pmem->rq_vq = virtio_add_queue(vdev, 128, virtio_pmem_flush); + pmem->inflight = 1; } static void virtio_pmem_unrealize(DeviceState *dev) @@ -129,6 +138,10 @@ static void virtio_pmem_unrealize(DeviceState *dev) VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOPMEM *pmem = VIRTIO_PMEM(dev); + /* Release the device's own reference and wait for in-flight flushes */ + pmem->inflight--; + AIO_WAIT_WHILE(NULL, pmem->inflight > 0); + host_memory_backend_set_mapped(pmem->memdev, false); virtio_delete_queue(pmem->rq_vq); virtio_cleanup(vdev); diff --git a/hw/virtio/virtio-qmp.c b/hw/virtio/virtio-qmp.c index 57cc03828c..5aaeebdd96 100644 --- a/hw/virtio/virtio-qmp.c +++ b/hw/virtio/virtio-qmp.c @@ -659,7 +659,7 @@ static int query_dev_child(Object *child, void *opaque) { VirtioInfoList **vdevs = opaque; Object *dev = object_dynamic_cast(child, TYPE_VIRTIO_DEVICE); - if (dev != NULL && DEVICE(dev)->realized) { + if (dev != NULL && qdev_is_realized(DEVICE(dev))) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtioInfo *info = g_new(VirtioInfo, 1); @@ -688,7 +688,7 @@ VirtIODevice *qmp_find_virtio_device(const char *path) /* Verify the canonical path is a realized virtio device */ Object *dev = object_dynamic_cast(object_resolve_path(path, NULL), TYPE_VIRTIO_DEVICE); - if (!dev || !DEVICE(dev)->realized) { + if (!dev || !qdev_is_realized(DEVICE(dev))) { return NULL; } return VIRTIO_DEVICE(dev); diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index 66690a34dc..d68d901195 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -234,6 +234,8 @@ static void virtio_rng_device_unrealize(DeviceState *dev) VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIORNG *vrng = VIRTIO_RNG(dev); + rng_backend_cancel_requests(vrng->rng, chr_read, vrng); + qemu_del_vm_change_state_handler(vrng->vmstate); timer_free(vrng->rate_limit_timer); virtio_del_queue(vdev, 0); diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index f4d86a3655..daa5607338 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -763,6 +763,10 @@ static int virtio_queue_packed_empty_rcu(VirtQueue *vq) struct VRingPackedDesc desc; VRingMemoryRegionCaches *cache; + if (virtio_device_disabled(vq->vdev)) { + return 1; + } + if (unlikely(!vq->vring.desc)) { return 1; } @@ -1475,7 +1479,7 @@ static void virtqueue_packed_get_avail_bytes(VirtQueue *vq, } if (desc.flags & VRING_DESC_F_INDIRECT) { - if (desc.len % sizeof(VRingPackedDesc)) { + if (!desc.len || (desc.len % sizeof(VRingPackedDesc))) { virtio_error(vdev, "Invalid size for indirect buffer table"); goto err; } @@ -1927,7 +1931,7 @@ static void *virtqueue_packed_pop(VirtQueue *vq, size_t sz) vring_packed_desc_read(vdev, &desc, desc_cache, i, true); id = desc.id; if (desc.flags & VRING_DESC_F_INDIRECT) { - if (desc.len % sizeof(VRingPackedDesc)) { + if (!desc.len || (desc.len % sizeof(VRingPackedDesc))) { virtio_error(vdev, "Invalid size for indirect buffer table"); goto done; } @@ -2418,6 +2422,11 @@ void virtio_queue_set_num(VirtIODevice *vdev, int n, int num) num < 0) { return; } + if (num > vdev->vq[n].vring.num_default) { + virtio_error(vdev, "virtio: queue %d size %d exceeds max size %u", + n, num, vdev->vq[n].vring.num_default); + return; + } vdev->vq[n].vring.num = num; } @@ -2568,6 +2577,17 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, if (i == VIRTIO_QUEUE_MAX || queue_size > VIRTQUEUE_MAX_SIZE) abort(); + BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); + if (qbus && qbus->parent && + object_property_find(OBJECT(qbus->parent), VIRTIO_QUEUE_SIZE_OVERRIDE)) { + int override = object_property_get_int(OBJECT(qbus->parent), + VIRTIO_QUEUE_SIZE_OVERRIDE, + &error_abort); + if (override) { + queue_size = override; + } + } + vdev->vq[i].vring.num = queue_size; vdev->vq[i].vring.num_default = queue_size; vdev->vq[i].vring.align = VIRTIO_PCI_VRING_ALIGN; @@ -2789,14 +2809,6 @@ static bool virtio_packed_virtqueue_needed(void *opaque) static bool virtio_ringsize_needed(void *opaque) { - VirtIODevice *vdev = opaque; - int i; - - for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { - if (vdev->vq[i].vring.num != vdev->vq[i].vring.num_default) { - return true; - } - } return false; } @@ -2885,7 +2897,7 @@ static const VMStateDescription vmstate_ringsize = { .version_id = 1, .minimum_version_id = 1, .fields = (const VMStateField[]) { - VMSTATE_UINT32(vring.num_default, struct VirtQueue), + VMSTATE_UNUSED(sizeof(uint32_t)), VMSTATE_END_OF_LIST() } }; @@ -3320,7 +3332,7 @@ static int virtio_set_features_nocheck(VirtIODevice *vdev, const uint64_t *val) virtio_features_and(tmp, val, vdev->host_features_ex); if (k->set_features_ex) { - k->set_features_ex(vdev, val); + k->set_features_ex(vdev, tmp); } else if (k->set_features) { bad = bad || virtio_features_use_ex(tmp); k->set_features(vdev, tmp[0]); @@ -3497,7 +3509,7 @@ int coroutine_mixed_fn virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) { int i, ret; - int32_t config_len; + uint32_t config_len; uint32_t num; uint32_t features; BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); @@ -3545,6 +3557,9 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) qemu_get_buffer(f, vdev->config, MIN(config_len, vdev->config_len)); while (config_len > vdev->config_len) { + if (qemu_file_get_error(f)) { + return -1; + } qemu_get_byte(f); config_len--; } @@ -3565,6 +3580,12 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) for (i = 0; i < num; i++) { vdev->vq[i].vring.num = qemu_get_be32(f); + if (vdev->vq[i].vring.num > vdev->vq[i].vring.num_default) { + error_report("VQ %d vring.num %u exceeds allocated max %u", + i, vdev->vq[i].vring.num, + vdev->vq[i].vring.num_default); + return -1; + } if (k->has_variable_vring_alignment) { vdev->vq[i].vring.align = qemu_get_be32(f); } diff --git a/hw/watchdog/spapr_watchdog.c b/hw/watchdog/spapr_watchdog.c index 5b3f50de3a..5a72896066 100644 --- a/hw/watchdog/spapr_watchdog.c +++ b/hw/watchdog/spapr_watchdog.c @@ -127,6 +127,12 @@ static void watchdog_expired(void *pw) } } +static inline bool watchdog_number_valid(target_ulong watchdogNumber, + SpaprMachineState *spapr) +{ + return watchdogNumber >= 1 && watchdogNumber <= ARRAY_SIZE(spapr->wds); +} + static target_ulong h_watchdog(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) @@ -145,7 +151,7 @@ static target_ulong h_watchdog(PowerPCCPU *cpu, switch (operation) { case PSERIES_WDTF_OP_START: - if (watchdogNumber > ARRAY_SIZE(spapr->wds)) { + if (!watchdog_number_valid(watchdogNumber, spapr)) { return H_P2; } if (timeoutInMs <= WDT_MIN_TIMEOUT) { @@ -170,11 +176,11 @@ static target_ulong h_watchdog(PowerPCCPU *cpu, case PSERIES_WDTF_OP_STOP: if (watchdogNumber == PSERIES_WDT_STOP_ALL) { ret = watchdog_stop_all(spapr); - } else if (watchdogNumber <= ARRAY_SIZE(spapr->wds)) { + } else if (!watchdog_number_valid(watchdogNumber, spapr)) { + return H_P2; + } else { ret = watchdog_stop(watchdogNumber, &spapr->wds[watchdogNumber - 1]); - } else { - return H_P2; } break; case PSERIES_WDTF_OP_QUERY: @@ -184,7 +190,7 @@ static target_ulong h_watchdog(PowerPCCPU *cpu, trace_spapr_watchdog_query(args[0]); break; case PSERIES_WDTF_OP_QUERY_LPM: - if (watchdogNumber > ARRAY_SIZE(spapr->wds)) { + if (!watchdog_number_valid(watchdogNumber, spapr)) { return H_P2; } args[0] = PSERIES_WDTQL_QUERY_NOT_STOPPED; diff --git a/hw/watchdog/watchdog.c b/hw/watchdog/watchdog.c index 0842fe373a..5f764a0c1b 100644 --- a/hw/watchdog/watchdog.c +++ b/hw/watchdog/watchdog.c @@ -81,7 +81,7 @@ void watchdog_perform_action(void) case WATCHDOG_ACTION_INJECT_NMI: qapi_event_send_watchdog(WATCHDOG_ACTION_INJECT_NMI); - nmi_monitor_handle(0, NULL); + nmi_inject(NULL); break; default: diff --git a/include/block/accounting.h b/include/block/accounting.h index b1cf417b57..12d3246092 100644 --- a/include/block/accounting.h +++ b/include/block/accounting.h @@ -116,6 +116,7 @@ void block_acct_invalid(BlockAcctStats *stats, enum BlockAcctType type); void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type, int num_requests); int64_t block_acct_idle_time_ns(BlockAcctStats *stats); +/* Caller must hold stats->stats->lock. */ double block_acct_queue_depth(BlockAcctTimedStats *stats, enum BlockAcctType type); int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type, diff --git a/include/crypto/cipher.h b/include/crypto/cipher.h index 92939310ef..2e361411b9 100644 --- a/include/crypto/cipher.h +++ b/include/crypto/cipher.h @@ -235,4 +235,40 @@ int qcrypto_cipher_setiv(QCryptoCipher *cipher, const uint8_t *iv, size_t niv, Error **errp); +/** + * qcrypto_cipher_setaad: + * @cipher: the cipher object + * @aad: the associated data to authenticate + * @len: the length of @aad + * @errp: pointer to a NULL-initialized error object + * + * For AEAD modes such as GCM, feed the associated data (AAD) that is + * authenticated but not encrypted. It must be called after + * qcrypto_cipher_setiv() and before the first encrypt/decrypt call. It is + * an error to call this on a mode that is not an AEAD mode. + * + * Returns: 0 on success, -1 on error + */ +int qcrypto_cipher_setaad(QCryptoCipher *cipher, + const uint8_t *aad, size_t len, + Error **errp); + +/** + * qcrypto_cipher_gettag: + * @cipher: the cipher object + * @tag: buffer to receive the authentication tag + * @len: the length of @tag + * @errp: pointer to a NULL-initialized error object + * + * For AEAD modes such as GCM, read back the authentication tag computed + * over the associated data and the message. It must be called after the + * encrypt/decrypt operation. It is an error to call this on a mode that is + * not an AEAD mode. + * + * Returns: 0 on success, -1 on error + */ +int qcrypto_cipher_gettag(QCryptoCipher *cipher, + uint8_t *tag, size_t len, + Error **errp); + #endif /* QCRYPTO_CIPHER_H */ diff --git a/include/disas/capstone.h b/include/disas/capstone.h index c43033f7f6..e72faf8179 100644 --- a/include/disas/capstone.h +++ b/include/disas/capstone.h @@ -4,20 +4,21 @@ #ifdef CONFIG_CAPSTONE #define CAPSTONE_AARCH64_COMPAT_HEADER -#define CAPSTONE_SYSTEMZ_COMPAT_HEADER #include #else /* Just enough to allow backends to init without ifdefs. */ +#define CS_API_MAJOR 0 + #define CS_ARCH_ARM -1 #define CS_ARCH_ARM64 -1 +#define CS_ARCH_M68K -1 #define CS_ARCH_MIPS -1 #define CS_ARCH_X86 -1 #define CS_ARCH_PPC -1 #define CS_ARCH_SPARC -1 -#define CS_ARCH_SYSZ -1 #define CS_MODE_LITTLE_ENDIAN 0 #define CS_MODE_BIG_ENDIAN 0 @@ -28,13 +29,115 @@ #define CS_MODE_THUMB 0 #define CS_MODE_MCLASS 0 #define CS_MODE_V8 0 -#define CS_MODE_MICRO 0 -#define CS_MODE_MIPS3 0 -#define CS_MODE_MIPS32R6 0 -#define CS_MODE_MIPSGP64 0 #define CS_MODE_V9 0 +#define CS_MODE_M68K_000 0 +#define CS_MODE_M68K_010 0 +#define CS_MODE_M68K_020 0 +#define CS_MODE_M68K_030 0 +#define CS_MODE_M68K_040 0 +#define CS_MODE_M68K_060 0 +#define CS_MODE_MICRO 0 +#define CS_MODE_MIPS2 0 +#define CS_MODE_MIPS3 0 #define CS_MODE_MIPS32 0 +#define CS_MODE_MIPS32R6 0 #define CS_MODE_MIPS64 0 #endif /* CONFIG_CAPSTONE */ + +#if CS_API_MAJOR < 6 +#define CS_ARCH_LOONGARCH -1 +#define CS_MODE_LOONGARCH32 0 +#define CS_MODE_LOONGARCH64 0 +#endif + +#if CS_API_MAJOR < 6 +#define CS_MODE_M68K_CF_ISA_A 0 +#define CS_MODE_M68K_CF_ISA_A_PLUS 0 +#define CS_MODE_M68K_CF_ISA_B 0 +#define CS_MODE_M68K_CF_ISA_C 0 +#define CS_MODE_M68K_CF_USP 0 +#define CS_MODE_M68K_CF_DIV 0 +#define CS_MODE_M68K_CF_MAC 0 +#define CS_MODE_M68K_CF_EMAC 0 +#define CS_MODE_M68K_CF_EMAC_B 0 +#define CS_MODE_M68K_CF_FPU 0 +#endif + +#if CS_API_MAJOR < 6 +#define CS_MODE_MIPS16 0 +#define CS_MODE_MIPS1 0 +#define CS_MODE_MIPS32R2 0 +#define CS_MODE_MIPS32R3 0 +#define CS_MODE_MIPS32R5 0 +#define CS_MODE_MIPS4 0 +#define CS_MODE_MIPS5 0 +#define CS_MODE_MIPS64R2 0 +#define CS_MODE_MIPS64R3 0 +#define CS_MODE_MIPS64R5 0 +#define CS_MODE_MIPS64R6 0 +#define CS_MODE_OCTEON 0 +#define CS_MODE_OCTEONP 0 +#define CS_MODE_NANOMIPS 0 +#define CS_MODE_MIPS_PTR64 0 +#endif + +#if CS_API_MAJOR < 5 +#define CS_ARCH_RISCV -1 +#define CS_MODE_RISCV32 0 +#define CS_MODE_RISCV64 0 +#define CS_MODE_RISCV_C 0 +#elif CS_API_MAJOR == 5 +/* The C symbol name changed between v5 and v6 */ +#define CS_MODE_RISCV_C CS_MODE_RISCVC +#endif +#if CS_API_MAJOR < 6 +#define CS_MODE_RISCV_FD 0 +#define CS_MODE_RISCV_V 0 +#define CS_MODE_RISCV_ZFINX 0 +#define CS_MODE_RISCV_ZCMP_ZCMT_ZCE 0 +#define CS_MODE_RISCV_ZICFISS 0 +#define CS_MODE_RISCV_E 0 +#define CS_MODE_RISCV_A 0 +#define CS_MODE_RISCV_COREV 0 +#define CS_MODE_RISCV_THEAD 0 +#define CS_MODE_RISCV_SIFIVE 0 +#define CS_MODE_RISCV_BITMANIP 0 +#define CS_MODE_RISCV_ZBA 0 +#define CS_MODE_RISCV_ZBB 0 +#define CS_MODE_RISCV_ZBC 0 +#define CS_MODE_RISCV_ZBKB 0 +#define CS_MODE_RISCV_ZBKC 0 +#define CS_MODE_RISCV_ZBKX 0 +#define CS_MODE_RISCV_ZBS 0 +#define CS_MODE_RISCV_VENTANA 0 +#endif + +#if CS_API_MAJOR < 5 +#define CS_ARCH_SH -1 +#define CS_MODE_SHFPU 0 +#define CS_MODE_SH4 0 +#define CS_MODE_SH4A 0 +#endif + +#if CS_API_MAJOR == 0 +#define CS_ARCH_SYSTEMZ -1 +#elif CS_API_MAJOR < 6 +#define CS_ARCH_SYSTEMZ CS_ARCH_SYSZ +#endif +#if CS_API_MAJOR < 6 +#define CS_MODE_SYSTEMZ_ARCH14 0 +#endif + +#if CS_API_MAJOR < 5 +#define CS_ARCH_TRICORE -1 +#define CS_MODE_TRICORE_110 0 +#define CS_MODE_TRICORE_120 0 +#define CS_MODE_TRICORE_130 0 +#define CS_MODE_TRICORE_131 0 +#define CS_MODE_TRICORE_160 0 +#define CS_MODE_TRICORE_161 0 +#define CS_MODE_TRICORE_162 0 +#endif + #endif /* QEMU_CAPSTONE_H */ diff --git a/include/hw/arm/aspeed.h b/include/hw/arm/aspeed.h index a00238ed74..54f0d7643d 100644 --- a/include/hw/arm/aspeed.h +++ b/include/hw/arm/aspeed.h @@ -12,6 +12,7 @@ #include "hw/core/boards.h" #include "qom/object.h" #include "hw/arm/aspeed_soc.h" +#include "hw/arm/boot.h" typedef struct AspeedMachineState AspeedMachineState; @@ -42,6 +43,7 @@ struct AspeedMachineState { char *fmc_model; char *spi_model; uint32_t hw_strap1; + struct arm_boot_info bootinfo; }; struct AspeedMachineClass { diff --git a/include/hw/arm/aspeed_ast1700.h b/include/hw/arm/aspeed_ast1700.h index f7bd4e8650..39c5977cf1 100644 --- a/include/hw/arm/aspeed_ast1700.h +++ b/include/hw/arm/aspeed_ast1700.h @@ -41,7 +41,7 @@ struct AspeedAST1700SoCState { MemoryRegion sram; AspeedSMCState spi; AspeedADCState adc; - AspeedSCUState scu; + Aspeed2700SCUState scu; AspeedGPIOState gpio; AspeedSGPIOState sgpiom[AST1700_SGPIO_NUM]; AspeedI2CState i2c; diff --git a/include/hw/arm/aspeed_coprocessor.h b/include/hw/arm/aspeed_coprocessor.h index ac58a5f424..acb8b06457 100644 --- a/include/hw/arm/aspeed_coprocessor.h +++ b/include/hw/arm/aspeed_coprocessor.h @@ -20,11 +20,8 @@ struct AspeedCoprocessorState { MemoryRegion *sram; MemoryRegion sram_alias; MemoryRegion uart_alias; - MemoryRegion scu_alias; Clock *sysclk; - AspeedSCUState *scu; - AspeedSCUState scuio; AspeedTimerCtrlState timerctrl; SerialMM *uart; int uart_dev; @@ -47,11 +44,22 @@ struct Aspeed27x0CoprocessorState { AspeedCoprocessorState parent; AspeedINTCState intc[2]; UnimplementedDeviceState ipc[2]; - UnimplementedDeviceState scuio; UnimplementedDeviceState pric[2]; UnimplementedDeviceState otp; ARMv7MState armv7m; + + /* + * SCU, SCUIO and FMC are not owned by this coprocessor: they are + * shared with the main PSP SoC, and only aliased into this + * coprocessor's own address space here. + */ + MemoryRegion scu_alias; + MemoryRegion scuio_alias; + MemoryRegion fmc_alias; + Aspeed2700SCUState *scu; + AspeedSCUState *scuio; + AspeedSMCState *fmc; }; #define TYPE_ASPEED27X0SSP_COPROCESSOR "aspeed27x0ssp-coprocessor" diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 41dc04e293..cd68c7f1ca 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -151,6 +151,7 @@ struct Aspeed27x0SoCState { AspeedINTCState intcioexp[ASPEED_IOEXP_NUM]; GICv3State gic; MemoryRegion dram_empty; + Aspeed2700SCUState scu; }; #define TYPE_ASPEED27X0_SOC "aspeed27x0-soc" diff --git a/include/hw/arm/ax3000-boards.h b/include/hw/arm/ax3000-boards.h new file mode 100644 index 0000000000..4a632661e1 --- /dev/null +++ b/include/hw/arm/ax3000-boards.h @@ -0,0 +1,28 @@ +/* + * Axiado Boards + * + * Author: Kuan-Jui Chiu + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef AXIADO_BOARD_H +#define AXIADO_BOARD_H + +#include "hw/core/boards.h" +#include "hw/arm/ax3000-soc.h" + +#define TYPE_AX3000_MACHINE MACHINE_TYPE_NAME("ax3000") +OBJECT_DECLARE_TYPE(Ax3000MachineState, Ax3000MachineClass, AX3000_MACHINE) + +typedef struct Ax3000MachineState { + MachineState parent; + + Ax3000SoCState *soc; +} Ax3000MachineState; + +typedef struct Ax3000MachineClass { + MachineClass parent; + +} Ax3000MachineClass; +#endif diff --git a/include/hw/arm/ax3000-soc.h b/include/hw/arm/ax3000-soc.h new file mode 100644 index 0000000000..344fcb6f3c --- /dev/null +++ b/include/hw/arm/ax3000-soc.h @@ -0,0 +1,98 @@ +/* + * Axiado SoC AX3000 + * + * Author: Kuan-Jui Chiu + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef AXIADO_AX3000_H +#define AXIADO_AX3000_H + +#include "cpu.h" +#include "hw/intc/arm_gicv3_common.h" +#include "hw/char/cadence_uart.h" +#include "hw/misc/axiado_clk.h" +#include "hw/gpio/cadence_gpio.h" +#include "hw/sd/axiado_sdhci.h" +#include "hw/core/sysbus.h" +#include "qemu/units.h" + +#define TYPE_AX3000_SOC "ax3000" +OBJECT_DECLARE_TYPE(Ax3000SoCState, Ax3000SoCClass, AX3000_SOC) + +#define AX3000_DRAM0_BASE 0x3C000000 +#define AX3000_DRAM0_SIZE (1088 * MiB) +#define AX3000_DRAM1_BASE 0x400000000 +#define AX3000_DRAM1_SIZE (2 * GiB) + +#define AX3000_GIC_DIST_BASE 0x80300000 +#define AX3000_GIC_DIST_SIZE (64 * KiB) +#define AX3000_GIC_REDIST_BASE 0x80380000 +#define AX3000_GIC_REDIST_SIZE (512 * KiB) + +#define AX3000_UART0_BASE 0x80520000 +#define AX3000_UART1_BASE 0x805a0000 +#define AX3000_UART2_BASE 0x80620000 +#define AX3000_UART3_BASE 0x80520800 + +#define AX3000_SDHCI0_BASE 0x86000000 +#define AX3000_EMMC_PHY_BASE 0x80801C00 + +#define AX3000_GPIO0_BASE 0x80500000 +#define AX3000_GPIO1_BASE 0x80580000 +#define AX3000_GPIO2_BASE 0x80600000 +#define AX3000_GPIO3_BASE 0x80680000 +#define AX3000_GPIO4_BASE 0x80700000 +#define AX3000_GPIO5_BASE 0x80780000 +#define AX3000_GPIO6_BASE 0x80800000 +#define AX3000_GPIO7_BASE 0x80880000 + +#define AX3000_TIMER_CTRL 0x8A020000 +#define AX3000_PLL_BASE 0x80000000 + +enum Ax3000Configuration { + AX3000_NUM_CPUS = 4, + AX3000_NUM_IRQS = 224, + AX3000_NUM_BANKS = 2, + AX3000_NUM_UARTS = 4, + AX3000_NUM_GPIOS = 8, +}; + +typedef struct Ax3000SoCState { + SysBusDevice parent; + + ARMCPU cpu[AX3000_NUM_CPUS]; + GICv3State gic; + MemoryRegion dram[AX3000_NUM_BANKS]; + Ax3000ClkState ax3000_clk; + CadenceUARTState uart[AX3000_NUM_UARTS]; + CadenceGPIOState gpio[AX3000_NUM_GPIOS]; + AxiadoSDHCIState sdhci0; +} Ax3000SoCState; + +typedef struct Ax3000SoCClass { + SysBusDeviceClass parent; + + uint32_t num_cpus; +} Ax3000SoCClass; + +enum Ax3000Irqs { + AX3000_UART0_IRQ = 112, + AX3000_UART1_IRQ = 113, + AX3000_UART2_IRQ = 114, + AX3000_UART3_IRQ = 170, + + AX3000_SDHCI0_IRQ = 123, + + AX3000_GPIO0_IRQ = 183, + AX3000_GPIO1_IRQ = 184, + AX3000_GPIO2_IRQ = 185, + AX3000_GPIO3_IRQ = 186, + AX3000_GPIO4_IRQ = 187, + AX3000_GPIO5_IRQ = 188, + AX3000_GPIO6_IRQ = 189, + AX3000_GPIO7_IRQ = 190, +}; + +#endif /* AXIADO_AX3000_H */ diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h index eef7cc5332..9f3dd489ec 100644 --- a/include/hw/arm/npcm7xx.h +++ b/include/hw/arm/npcm7xx.h @@ -19,6 +19,7 @@ #include "hw/core/boards.h" #include "hw/adc/npcm7xx_adc.h" #include "hw/core/split-irq.h" +#include "hw/arm/boot.h" #include "hw/cpu/a9mpcore.h" #include "hw/gpio/npcm7xx_gpio.h" #include "hw/i2c/npcm7xx_smbus.h" @@ -62,6 +63,7 @@ struct NPCM7xxMachine { */ SplitIRQ fan_splitter[NPCM7XX_NR_PWM_MODULES * NPCM7XX_PWM_PER_MODULE]; + struct arm_boot_info bootinfo; }; #define TYPE_NPCM7XX_MACHINE MACHINE_TYPE_NAME("npcm7xx") @@ -129,11 +131,15 @@ typedef struct NPCM7xxClass { * npcm7xx_load_kernel - Loads memory with everything needed to boot * @machine - The machine containing the SoC to be booted. * @soc - The SoC containing the CPU to be booted. + * @binfo - Caller owned boot info structure to be filled in. * * This will set up the ARM boot info structure for the specific NPCM7xx * derivative and call arm_load_kernel() to set up loading of the kernel, etc. - * into memory, if requested by the user. + * into memory, if requested by the user. The boot info is owned by the + * caller because arm_load_kernel() keeps a pointer to it for the lifetime + * of the CPUs. */ -void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc); +void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc, + struct arm_boot_info *binfo); #endif /* NPCM7XX_H */ diff --git a/include/hw/arm/npcm8xx.h b/include/hw/arm/npcm8xx.h index a8377db490..676c82150d 100644 --- a/include/hw/arm/npcm8xx.h +++ b/include/hw/arm/npcm8xx.h @@ -23,6 +23,7 @@ #include "hw/i2c/npcm7xx_smbus.h" #include "hw/intc/arm_gic_common.h" #include "hw/mem/npcm7xx_mc.h" +#include "hw/arm/boot.h" #include "hw/misc/npcm_clk.h" #include "hw/misc/npcm_gcr.h" #include "hw/misc/npcm7xx_mft.h" @@ -62,6 +63,7 @@ struct NPCM8xxMachine { */ SplitIRQ fan_splitter[NPCM8XX_NR_PWM_MODULES * NPCM7XX_PWM_PER_MODULE]; + struct arm_boot_info bootinfo; }; @@ -122,11 +124,15 @@ OBJECT_DECLARE_TYPE(NPCM8xxState, NPCM8xxClass, NPCM8XX) * npcm8xx_load_kernel - Loads memory with everything needed to boot * @machine - The machine containing the SoC to be booted. * @soc - The SoC containing the CPU to be booted. + * @binfo - Caller owned boot info structure to be filled in. * * This will set up the ARM boot info structure for the specific NPCM8xx * derivative and call arm_load_kernel() to set up loading of the kernel, etc. - * into memory, if requested by the user. + * into memory, if requested by the user. The boot info is owned by the + * caller because arm_load_kernel() keeps a pointer to it for the lifetime + * of the CPUs. */ -void npcm8xx_load_kernel(MachineState *machine, NPCM8xxState *soc); +void npcm8xx_load_kernel(MachineState *machine, NPCM8xxState *soc, + struct arm_boot_info *binfo); #endif /* NPCM8XX_H */ diff --git a/include/hw/arm/xilinx_zynq.h b/include/hw/arm/xilinx_zynq.h index cefb7789ff..669c516ad1 100644 --- a/include/hw/arm/xilinx_zynq.h +++ b/include/hw/arm/xilinx_zynq.h @@ -14,6 +14,7 @@ #include "target/arm/cpu-qom.h" #include "hw/core/qdev-clock.h" +#include "hw/arm/boot.h" #define TYPE_ZYNQ_MACHINE MACHINE_TYPE_NAME("xilinx-zynq-a9") OBJECT_DECLARE_SIMPLE_TYPE(ZynqMachineState, ZYNQ_MACHINE) @@ -25,6 +26,7 @@ struct ZynqMachineState { Clock *ps_clk; ARMCPU *cpu[ZYNQ_MAX_CPUS]; uint8_t boot_mode; + struct arm_boot_info bootinfo; }; #endif /* QEMU_ARM_ZYNQ_H */ diff --git a/include/hw/core/boards.h b/include/hw/core/boards.h index 29c68931d8..a436d48c8e 100644 --- a/include/hw/core/boards.h +++ b/include/hw/core/boards.h @@ -815,6 +815,9 @@ compat_props_add(GPtrArray *arr, } } +extern GlobalProperty hw_compat_11_1[]; +extern const size_t hw_compat_11_1_len; + extern GlobalProperty hw_compat_11_0[]; extern const size_t hw_compat_11_0_len; diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index b54035fb13..81af7b9ee1 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -60,7 +60,7 @@ typedef int (*WriteCoreDumpFunction)(const void *buf, size_t size, * expensive given the eventual call to * object_class_dynamic_cast_assert(). Because of this the CPUState * has a cached value for the class in cs->cc which is set up in - * cpu_exec_realizefn() for use in hot code paths. + * cpu_common_initfn() for use in hot code paths. */ typedef struct CPUClass CPUClass; DECLARE_CLASS_CHECKERS(CPUClass, CPU, @@ -1178,14 +1178,11 @@ G_NORETURN void cpu_abort(CPUState *cpu, const char *fmt, ...) */ void qemu_process_cpu_events(CPUState *cpu); -/* $(top_srcdir)/cpu.c */ -void cpu_class_init_props(DeviceClass *dc); -void cpu_exec_class_post_init(CPUClass *cc); -void cpu_exec_initfn(CPUState *cpu); -void cpu_vmstate_register(CPUState *cpu); -void cpu_vmstate_unregister(CPUState *cpu); -bool cpu_exec_realizefn(CPUState *cpu, Error **errp); -void cpu_exec_unrealizefn(CPUState *cpu); +/** cpu_common_realize: CPU DeviceRealize common handler */ +bool cpu_common_realize(CPUState *cpu, Error **errp); +/** cpu_common_realize: CPU DeviceUnrealize common handler */ +void cpu_common_unrealize(CPUState *cpu); + void cpu_exec_reset_hold(CPUState *cpu); extern const VMStateDescription vmstate_cpu_common; diff --git a/include/hw/core/nmi.h b/include/hw/core/nmi.h index fff41bebc6..d71d92dfbb 100644 --- a/include/hw/core/nmi.h +++ b/include/hw/core/nmi.h @@ -37,9 +37,38 @@ typedef struct NMIState NMIState; struct NMIClass { InterfaceClass parent_class; - void (*nmi_monitor_handler)(NMIState *n, int cpu_index, Error **errp); + /** + * raise_nmi: Callback to handle NMI notifications. + * @ns: Class #NMIState state + * + * Called by nmi_inject() to perform the machine-specific + * action when a NMI is requested. + */ + void (*raise_nmi)(NMIState *ns); }; -void nmi_monitor_handle(int cpu_index, Error **errp); +/** + * nmi_inject: Inject an NMI, in a machine-specific way + * @errp: pointer to error object + * + * This function injects an NMI, in a machine-specific way. The + * intention is that this should typically trigger a guest kernel + * dump or reboot, and might happen as a result of user request + * from the monitor, watchdog timeouts, and similar events. + * (For example on the x86 PC it triggers an NMI on all CPUs, + * and on s390 it triggers the RESTART interrupt on the first CPU.) + * + * The NMI is injected by looking for a QOM object which implements + * the TYPE_NMI interface, and calling its raise_nmi method. Usually + * it is the machine model class that implements this interface. + * + * Not all machines implement NMI handling; this function + * will return an error if used on a machine which does not + * implement NMIs. + * + * On success, return %true. + * On failure, store an error through @errp and return %false. + */ +bool nmi_inject(Error **errp); #endif /* NMI_H */ diff --git a/include/hw/core/qdev-dma.h b/include/hw/core/qdev-dma.h deleted file mode 100644 index b00391aa0c..0000000000 --- a/include/hw/core/qdev-dma.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Support for dma_addr_t typed properties - * - * Copyright (C) 2012 David Gibson, IBM 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. - */ - -#ifndef HW_QDEV_DMA_H -#define HW_QDEV_DMA_H - -#define DEFINE_PROP_DMAADDR(_n, _s, _f, _d) \ - DEFINE_PROP_UINT64(_n, _s, _f, _d) - -#endif diff --git a/include/hw/core/qdev.h b/include/hw/core/qdev.h index e147622341..37f7d33551 100644 --- a/include/hw/core/qdev.h +++ b/include/hw/core/qdev.h @@ -437,7 +437,7 @@ DeviceState *qdev_try_new(const char *name); * Context: May be called outside big qemu lock. * Return: true if the device has been fully constructed, false otherwise. */ -static inline bool qdev_is_realized(DeviceState *dev) +static inline bool qdev_is_realized(const DeviceState *dev) { return qatomic_load_acquire(&dev->realized); } diff --git a/include/hw/dma/k230_gsdma.h b/include/hw/dma/k230_gsdma.h new file mode 100644 index 0000000000..629cce29f8 --- /dev/null +++ b/include/hw/dma/k230_gsdma.h @@ -0,0 +1,130 @@ +/* + * K230 GSDMA + * + * Copyright (c) 2026 Tao Ding + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_DMA_K230_GSDMA_H +#define HW_DMA_K230_GSDMA_H + +#include "hw/core/sysbus.h" + +#define TYPE_K230_GSDMA "riscv.k230.gsdma" +OBJECT_DECLARE_SIMPLE_TYPE(K230GSDMAState, K230_GSDMA) + +#define K230_GSDMA_MMIO_SIZE 0x4000 +#define K230_GSDMA_NUM_SDMA_CHANNELS 4 + +#define K230_GSDMA_CH_STRIDE 0x30 +#define K230_GSDMA_CH_BASE 0x50 + +/* K230 SDMA request input */ +enum { + K230_GSDMA_GPIO_DECOMP_CTRL_EN, + K230_GSDMA_GPIO_DMA_WRITE_REQ, + K230_GSDMA_GPIO_DMA_READ_REQ, + K230_GSDMA_NUM_GPIOS_IN, +}; + +/* K230 SDMA ACK output */ +enum { + K230_GSDMA_GPIO_DMA_WRITE_ACK, + K230_GSDMA_GPIO_DMA_READ_ACK, + K230_GSDMA_NUM_GPIOS_OUT, +}; + +/* K230 GSDMA Registers Map */ +#define K230_GSDMA_DMA_CH_EN 0x00 +#define K230_GSDMA_DMA_INT_MASK 0x04 +#define K230_GSDMA_DMA_INT_STAT 0x08 +#define K230_GSDMA_DMA_CFG 0x0c +#define K230_GSDMA_GDMA_CTRL 0x10 +#define K230_GSDMA_GDMA_LLI_BASE 0x14 +#define K230_GSDMA_GDMA_CH_CNT0 0x18 +#define K230_GSDMA_GDMA_CH_CNT_LAST 0x34 +#define K230_GSDMA_GDMA_CURRENT_LLT 0x38 +#define K230_GSDMA_DMA_WEIGHT 0x48 + +/* K230 GSDMA SDMA Channel Register Map */ +#define K230_GSDMA_CH_CTL 0x00 +#define K230_GSDMA_CH_STATUS 0x04 +#define K230_GSDMA_CH_CFG 0x08 +#define K230_GSDMA_CH_USR_DATA 0x0c +#define K230_GSDMA_CH_LLT_SADDR 0x10 +#define K230_GSDMA_CH_CURRENT_LLT 0x14 + +/* K230 SDMA Control bit */ +#define K230_GSDMA_CTL_START BIT(0) +#define K230_GSDMA_CTL_STOP BIT(1) +#define K230_GSDMA_CTL_RESUME BIT(2) + +/* K230 SDMA States bit */ +#define K230_GSDMA_SDMA_STATUS_BUSY BIT(0) +#define K230_GSDMA_SDMA_STATUS_PAUSE BIT(1) + +/* K230 SDMA Interrupt bit */ +#define K230_GSDMA_SDMA_DONE_INT(ch) BIT(ch) +#define K230_GSDMA_SDMA_ITEM_INT(ch) BIT((ch) + 4) +#define K230_GSDMA_SDMA_PAUSE_INT(ch) BIT((ch) + 8) + + +#define K230_GSDMA_DMA_CH_EN_MASK MAKE_64BIT_MASK(0, 5) +#define K230_GSDMA_DMA_CFG_RESET 0x000007ff + +/* K230 SDMA Linked List bit */ +#define K230_GSDMA_LLT_2D_MODE BIT(28) +#define K230_GSDMA_LLT_PAUSE BIT(29) +#define K230_GSDMA_LLT_NODE_INTR BIT(30) +#define K230_GSDMA_GDMA_LLT_GCH_SHIFT 16 +#define K230_GSDMA_GDMA_LLT_GCH_MASK 0x7 +#define K230_GSDMA_CH_CFG_DAT_MODE BIT(0) +#define K230_GSDMA_CH_CFG_USR_DATA_SIZE_SHIFT 1 +#define K230_GSDMA_CH_CFG_USR_DATA_SIZE_MASK 0x3 +#define K230_GSDMA_CH_CFG_DAT_ENDIAN_SHIFT 4 +#define K230_GSDMA_CH_CFG_DAT_ENDIAN_MASK 0x3 +#define K230_GSDMA_CH_CFG_SRC_FIXED BIT(8) +#define K230_GSDMA_CH_CFG_DST_FIXED BIT(9) +#define K230_GSDMA_CH0_CFG_DECOMP_CTRL_EN BIT(10) + +/* K230 SDMA channel state */ +typedef struct K230GSDMAChannel { + uint32_t ctl; + uint32_t status; + uint32_t cfg; + uint32_t usr_data; + uint32_t llt_saddr; + uint32_t current_llt; + uint32_t next_llt; + bool started; +} K230GSDMAChannel; + +/* K230 SDMA Linked List */ +typedef struct K230GSDMALLT { + uint32_t cfg; + uint32_t src_addr; + uint32_t line_size; + uint32_t line_cfg; + uint32_t dst_addr; + uint32_t next_llt_addr; +} K230GSDMALLT; + +/* K230 GSDMA state */ +struct K230GSDMAState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + qemu_irq irq; + qemu_irq handshake_out[K230_GSDMA_NUM_GPIOS_OUT]; + + uint32_t dma_ch_en; + uint32_t dma_int_mask; + uint32_t dma_int_stat; + uint32_t dma_cfg; + uint32_t dma_weight; + + K230GSDMAChannel channels[K230_GSDMA_NUM_SDMA_CHANNELS]; +}; + +#endif diff --git a/include/hw/arm/soc_dma.h b/include/hw/dma/soc_dma.h similarity index 70% rename from include/hw/arm/soc_dma.h rename to include/hw/dma/soc_dma.h index bcdb91425a..fdae7a29c2 100644 --- a/include/hw/arm/soc_dma.h +++ b/include/hw/dma/soc_dma.h @@ -25,12 +25,10 @@ struct soc_dma_s; struct soc_dma_ch_s; -typedef void (*soc_dma_io_t)(void *opaque, uint8_t *buf, int len); typedef void (*soc_dma_transfer_t)(struct soc_dma_ch_s *ch); enum soc_dma_port_type { soc_dma_port_mem, - soc_dma_port_fifo, soc_dma_port_other, }; @@ -51,14 +49,10 @@ struct soc_dma_ch_s { int update; /* This should be set by dma->setup_fn(). */ - int bytes; + uint64_t bytes; /* Initialised by the DMA module, call soc_dma_ch_update after writing. */ enum soc_dma_access_type type[2]; hwaddr vaddr[2]; /* Updated by .transfer_fn(). */ - /* Private */ - void *paddr[2]; - soc_dma_io_t io_fn[2]; - void *io_opaque[2]; int running; soc_dma_transfer_t transfer_fn; @@ -82,33 +76,20 @@ struct soc_dma_s { /* Call to activate or stop a DMA channel. */ void soc_dma_set_request(struct soc_dma_ch_s *ch, int level); -/* Call after every write to one of the following fields and before +/* + * Call after every write to one of the following fields and before * calling soc_dma_set_request(ch, 1): * ch->type[0...1], * ch->vaddr[0...1], - * ch->paddr[0...1], - * or after a soc_dma_port_add_fifo() or soc_dma_port_add_mem(). */ + * or after a soc_dma_port_add_mem(). + */ void soc_dma_ch_update(struct soc_dma_ch_s *ch); /* The SoC should call this when the DMA module is being reset. */ void soc_dma_reset(struct soc_dma_s *s); struct soc_dma_s *soc_dma_init(int n); -void soc_dma_port_add_fifo(struct soc_dma_s *dma, hwaddr virt_base, - soc_dma_io_t fn, void *opaque, int out); -void soc_dma_port_add_mem(struct soc_dma_s *dma, uint8_t *phys_base, - hwaddr virt_base, size_t size); - -static inline void soc_dma_port_add_fifo_in(struct soc_dma_s *dma, - hwaddr virt_base, soc_dma_io_t fn, void *opaque) -{ - return soc_dma_port_add_fifo(dma, virt_base, fn, opaque, 0); -} - -static inline void soc_dma_port_add_fifo_out(struct soc_dma_s *dma, - hwaddr virt_base, soc_dma_io_t fn, void *opaque) -{ - return soc_dma_port_add_fifo(dma, virt_base, fn, opaque, 1); -} +void soc_dma_port_add_mem(struct soc_dma_s *dma, + hwaddr virt_base, size_t size); #endif diff --git a/include/hw/elf_ops.h.inc b/include/hw/elf_ops.h.inc index 9c35d1b9da..044e72de2a 100644 --- a/include/hw/elf_ops.h.inc +++ b/include/hw/elf_ops.h.inc @@ -427,6 +427,11 @@ static ssize_t glue(load_elf, SZ)(const char *name, int fd, file_size = ph->p_filesz; /* Size of the allocated data */ data_offset = ph->p_offset; /* Offset where the data is located */ + if (file_size > mem_size) { + ret = ELF_LOAD_TOO_BIG; + goto fail; + } + if (file_size > 0) { if (g_mapped_file_get_length(mapped_file) < file_size + data_offset) { diff --git a/include/hw/gpio/cadence_gpio.h b/include/hw/gpio/cadence_gpio.h new file mode 100644 index 0000000000..69646ba639 --- /dev/null +++ b/include/hw/gpio/cadence_gpio.h @@ -0,0 +1,53 @@ +/* + * Cadence GPIO registers definition. + * + * Author: Kuan-Jui Chiu + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef CADENCE_GPIO_H +#define CADENCE_GPIO_H + +#include "hw/core/sysbus.h" +#include "qom/object.h" + +#define TYPE_CADENCE_GPIO "cadence_gpio" +OBJECT_DECLARE_SIMPLE_TYPE(CadenceGPIOState, CADENCE_GPIO) + +#define CDNS_GPIO_REG_SIZE 0x400 +#define CDNS_GPIO_NUM 32 + +#define CDNS_GPIO_BYPASS_MODE 0x00 +#define CDNS_GPIO_DIRECTION_MODE 0x04 +#define CDNS_GPIO_OUTPUT_EN 0x08 +#define CDNS_GPIO_OUTPUT_VALUE 0x0c +#define CDNS_GPIO_INPUT_VALUE 0x10 +#define CDNS_GPIO_IRQ_MASK 0x14 +#define CDNS_GPIO_IRQ_EN 0x18 +#define CDNS_GPIO_IRQ_DIS 0x1c +#define CDNS_GPIO_IRQ_STATUS 0x20 +#define CDNS_GPIO_IRQ_TYPE 0x24 +#define CDNS_GPIO_IRQ_VALUE 0x28 +#define CDNS_GPIO_IRQ_ANY_EDGE 0x2c + +struct CadenceGPIOState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + + uint32_t bmr; + uint32_t dmr; + uint32_t oer; + uint32_t ovr; + uint32_t inpvr; + uint32_t imr; + uint32_t isr; + uint32_t itr; + uint32_t ivr; + uint32_t ioar; + qemu_irq irq; + qemu_irq output[CDNS_GPIO_NUM]; +}; + +#endif /* CADENCE_GPIO_H */ diff --git a/include/hw/gpio/pca9552.h b/include/hw/gpio/pca9552.h index 43b175235d..5299c13829 100644 --- a/include/hw/gpio/pca9552.h +++ b/include/hw/gpio/pca9552.h @@ -1,39 +1,18 @@ /* - * PCA9552 I2C LED blinker + * PCA955X I2C LED blinker and I/O expanders * * Copyright (c) 2017-2018, IBM 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. */ -#ifndef PCA9552_H -#define PCA9552_H -#include "hw/i2c/i2c.h" -#include "qom/object.h" +#ifndef HW_GPIO_PCA9552_H +#define HW_GPIO_PCA9552_H -#define TYPE_PCA9552 "pca9552" #define TYPE_PCA955X "pca955x" +#define TYPE_PCA9552 "pca9552" #define TYPE_PCA9535 "pca9535" -typedef struct PCA955xState PCA955xState; -DECLARE_INSTANCE_CHECKER(PCA955xState, PCA955X, - TYPE_PCA955X) - -#define PCA955X_NR_REGS 10 -#define PCA955X_PIN_COUNT_MAX 16 - -struct PCA955xState { - /*< private >*/ - I2CSlave i2c; - /*< public >*/ - - uint8_t len; - uint8_t pointer; - - uint8_t regs[PCA955X_NR_REGS]; - qemu_irq gpio_out[PCA955X_PIN_COUNT_MAX]; - uint8_t ext_state[PCA955X_PIN_COUNT_MAX]; - char *description; /* For debugging purpose only */ -}; +#define TYPE_PCA9555 "pca9555" #endif diff --git a/include/hw/gpio/pca9554.h b/include/hw/gpio/pca9554.h index 54bfc4c4c7..ac835371aa 100644 --- a/include/hw/gpio/pca9554.h +++ b/include/hw/gpio/pca9554.h @@ -12,12 +12,14 @@ #include "qom/object.h" #define TYPE_PCA9554 "pca9554" +#define TYPE_PCA9536 "pca9536" typedef struct PCA9554State PCA9554State; DECLARE_INSTANCE_CHECKER(PCA9554State, PCA9554, TYPE_PCA9554) #define PCA9554_NR_REGS 4 #define PCA9554_PIN_COUNT 8 +#define PCA9536_PIN_COUNT 4 struct PCA9554State { /*< private >*/ @@ -31,6 +33,7 @@ struct PCA9554State { qemu_irq gpio_out[PCA9554_PIN_COUNT]; uint8_t ext_state[PCA9554_PIN_COUNT]; char *description; /* For debugging purpose only */ + bool hw_dir; /* Honor pin direction */ }; #endif diff --git a/include/hw/hexagon/hex-subsys.h b/include/hw/hexagon/hex-subsys.h new file mode 100644 index 0000000000..5792f7b5af --- /dev/null +++ b/include/hw/hexagon/hex-subsys.h @@ -0,0 +1,32 @@ +/* + * Hexagon subsystem helpers shared between the machine models. + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_HEXAGON_HEX_SUBSYS_H +#define HW_HEXAGON_HEX_SUBSYS_H + +#include "hw/hexagon/hexagon.h" +#include "hw/core/qdev.h" + +/* Create the subsystem shared by every Hexagon machine. */ +void hex_subsys_create(HexagonCommonMachineState *hms, + const struct hexagon_machine_config *m_cfg, Rev_t rev); + +/* + * Parent a CPU into the subsystem's cluster and wire its links. Call for + * every CPU before hex_subsys_realize_cluster(), then realize each CPU with + * hex_subsys_realize_cpu(). CPU[0] receives the L2VIC outputs. + */ +void hex_subsys_add_cpu(HexagonCommonMachineState *hms, DeviceState *cpu); + +/* Realize the CPU cluster, once all CPUs have been parented into it. */ +void hex_subsys_realize_cluster(HexagonCommonMachineState *hms); + +/* Realize a CPU previously parented via hex_subsys_add_cpu(). */ +void hex_subsys_realize_cpu(HexagonCommonMachineState *hms, DeviceState *cpu, + bool boot_cpu); + +#endif /* HW_HEXAGON_HEX_SUBSYS_H */ diff --git a/include/hw/hexagon/hexagon.h b/include/hw/hexagon/hexagon.h index 1034b09c2a..3d7b3cb12d 100644 --- a/include/hw/hexagon/hexagon.h +++ b/include/hw/hexagon/hexagon.h @@ -156,6 +156,12 @@ struct HexagonCommonMachineState { MemoryRegion ram; MemoryRegion cfgtable_rom; + MemoryRegion vtcm; + DeviceState *cluster; + DeviceState *l2vic; + DeviceState *qtimer; + DeviceState *glob_regs; + DeviceState *tlb; }; #endif diff --git a/include/hw/hexagon/hexagon_globalreg.h b/include/hw/hexagon/hexagon_globalreg.h index 950099808f..07437dfabb 100644 --- a/include/hw/hexagon/hexagon_globalreg.h +++ b/include/hw/hexagon/hexagon_globalreg.h @@ -10,6 +10,8 @@ #include "hw/core/qdev.h" #include "hw/core/sysbus.h" +#include "hw/intc/hex-l2vic.h" +#include "hw/timer/qct-qtimer.h" #include "qom/object.h" #include "target/hexagon/cpu.h" @@ -22,6 +24,12 @@ struct HexagonGlobalRegState { /* Array of system registers */ uint32_t regs[NUM_SREGS]; + /* L2VIC interface used to back the VID/VID1 registers */ + HexL2VicInterface *l2vic; + + /* QTimer interface used to back the TIMERLO/TIMERHI registers */ + QctQtimerInterface *qtimer; + /* Global performance cycle counter base */ uint64_t g_pcycle_base; diff --git a/include/hw/hexagon/hexagon_tlb.h b/include/hw/hexagon/hexagon_tlb.h index 90d9ed8404..760dc1ea81 100644 --- a/include/hw/hexagon/hexagon_tlb.h +++ b/include/hw/hexagon/hexagon_tlb.h @@ -12,7 +12,7 @@ #include "qom/object.h" #include "exec/hwaddr.h" #include "exec/mmu-access-type.h" -#include "monitor/monitor.h" + #define TYPE_HEXAGON_TLB "hexagon-tlb" OBJECT_DECLARE_SIMPLE_TYPE(HexagonTLBState, HEXAGON_TLB) diff --git a/include/hw/hexagon/virt.h b/include/hw/hexagon/virt.h index fcb4776219..4ca2f1ef85 100644 --- a/include/hw/hexagon/virt.h +++ b/include/hw/hexagon/virt.h @@ -11,15 +11,17 @@ #include "hw/hexagon/hexagon.h" #include "target/hexagon/cpu.h" +#define VIRTIO_DEV_COUNT 8 + struct HexagonVirtMachineState { HexagonCommonMachineState parent_obj; int fdt_size; MemoryRegion *sys; MemoryRegion tcm; - MemoryRegion vtcm; MemoryRegion bios; Clock *apb_clk; + DeviceState *virtio_mmio[VIRTIO_DEV_COUNT]; }; void hexagon_load_fdt(const struct HexagonVirtMachineState *vms); diff --git a/include/hw/hyperv/hvgdk_mini.h b/include/hw/hyperv/hvgdk_mini.h index f8838a31bb..a5527d49cf 100644 --- a/include/hw/hyperv/hvgdk_mini.h +++ b/include/hw/hyperv/hvgdk_mini.h @@ -23,6 +23,9 @@ #define HV_X64_MSR_APIC_FREQUENCY 0x40000023 typedef enum hv_register_name { + /* VP Management Registers */ + HV_REGISTER_INTERNAL_ACTIVITY_STATE = 0x00000004, + /* Pending Interruption Register */ HV_REGISTER_PENDING_INTERRUPTION = 0x00010002, HV_REGISTER_INTERRUPT_STATE = 0x00010003, @@ -168,6 +171,7 @@ typedef enum hv_register_name { /* Available */ HV_X64_REGISTER_SPEC_CTRL = 0x00080084, + HV_X64_REGISTER_TSC_DEADLINE = 0x00080095, HV_X64_REGISTER_TSC_ADJUST = 0x00080096, /* CET / Shadow Stack */ @@ -930,6 +934,7 @@ struct hv_cpuid { #define IA32_MSR_DEBUG_CTL 0x1D9 #define IA32_MSR_SPEC_CTRL 0x00000048 #define IA32_MSR_TSC_ADJUST 0x0000003b +#define IA32_MSR_TSC_DEADLINE 0x000006e0 #define IA32_MSR_MISC_ENABLE 0x000001a0 diff --git a/include/hw/i2c/aspeed_i2c.h b/include/hw/i2c/aspeed_i2c.h index 156998e7c1..05937a7a0b 100644 --- a/include/hw/i2c/aspeed_i2c.h +++ b/include/hw/i2c/aspeed_i2c.h @@ -231,6 +231,8 @@ REG32(I2CS_DMA_TX_ADDR_HI, 0x68) FIELD(I2CS_DMA_TX_ADDR_HI, ADDR_HI, 0, 7) REG32(I2CS_DMA_RX_ADDR_HI, 0x6c) FIELD(I2CS_DMA_RX_ADDR_HI, ADDR_HI, 0, 7) +REG32(I2CC_VERSION_CTRL, 0x94) + FIELD(I2CC_VERSION_CTRL, FUNC_CFG_DMA_EN, 2, 1) struct AspeedI2CState; diff --git a/include/hw/i2c/bcm2835_i2c.h b/include/hw/i2c/bcm2835_i2c.h index 45f876df22..519c65f765 100644 --- a/include/hw/i2c/bcm2835_i2c.h +++ b/include/hw/i2c/bcm2835_i2c.h @@ -38,7 +38,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(BCM2835I2CState, BCM2835_I2C) #define BCM2835_I2C_FIFO 0x10 /* FIFO */ #define BCM2835_I2C_DIV 0x14 /* Clock Divider */ #define BCM2835_I2C_DEL 0x18 /* Data Delay */ -#define BCM2835_I2C_CLKT 0x20 /* Clock Stretch Timeout */ +#define BCM2835_I2C_CLKT 0x1c /* Clock Stretch Timeout */ #define BCM2835_I2C_C_I2CEN BIT(15) /* I2C enable */ #define BCM2835_I2C_C_INTR BIT(10) /* Interrupt on RXR */ diff --git a/include/hw/i386/apic-msidef.h b/include/hw/i386/apic-msidef.h index 420b41167d..6b860b5807 100644 --- a/include/hw/i386/apic-msidef.h +++ b/include/hw/i386/apic-msidef.h @@ -13,6 +13,7 @@ #define MSI_DATA_VECTOR_MASK 0x000000ff #define MSI_DATA_DELIVERY_MODE_SHIFT 8 +#define MSI_DATA_DELIVERY_MODE_MASK 7 #define MSI_DATA_LEVEL_SHIFT 14 #define MSI_DATA_TRIGGER_SHIFT 15 diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index d4b6d3ed57..ac03da97b6 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -209,6 +209,9 @@ void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size); /* sgx.c */ void pc_machine_init_sgx_epc(PCMachineState *pcms); +extern GlobalProperty pc_compat_11_1[]; +extern const size_t pc_compat_11_1_len; + extern GlobalProperty pc_compat_11_0[]; extern const size_t pc_compat_11_0_len; diff --git a/include/hw/ide/ide-dma.h b/include/hw/ide/ide-dma.h index 296010a4e0..34154b7cbc 100644 --- a/include/hw/ide/ide-dma.h +++ b/include/hw/ide/ide-dma.h @@ -10,6 +10,7 @@ typedef struct IDEDMA IDEDMA; typedef void DMAStartFunc(const IDEDMA *, IDEState *, BlockCompletionFunc *); typedef void DMAVoidFunc(const IDEDMA *); +typedef bool DMABoolFunc(const IDEDMA *); typedef int DMAIntFunc(const IDEDMA *, bool); typedef int32_t DMAInt32Func(const IDEDMA *, int32_t len); typedef void DMAu32Func(const IDEDMA *, uint32_t); @@ -17,7 +18,7 @@ typedef void DMAStopFunc(const IDEDMA *, bool); struct IDEDMAOps { DMAStartFunc *start_dma; - DMAVoidFunc *pio_transfer; + DMABoolFunc *pio_transfer; DMAInt32Func *prepare_buf; DMAu32Func *commit_buf; DMAIntFunc *rw_buf; diff --git a/include/hw/intc/hex-l2vic.h b/include/hw/intc/hex-l2vic.h new file mode 100644 index 0000000000..edc278266c --- /dev/null +++ b/include/hw/intc/hex-l2vic.h @@ -0,0 +1,61 @@ +/* + * QEMU L2VIC Interrupt Controller + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_INTC_HEX_L2VIC_H +#define HW_INTC_HEX_L2VIC_H + +#include "qom/object.h" + +#define TYPE_HEX_L2VIC "hex-l2vic" +/* + * L2VIC Interface for CPU/GlobalReg interaction + */ +#define TYPE_HEX_L2VIC_INTERFACE "hex-l2vic-if" + +typedef struct HexL2VicInterface HexL2VicInterface; + +typedef struct HexL2VicInterfaceClass { + InterfaceClass parent_class; + + uint32_t (*read_vid)(HexL2VicInterface *l2vic, uint32_t group); + + /* + * Write the VID: unpack the fields into per-group VIDs. This does + * not deliver or clear any interrupt; a pending interrupt stays + * gated until ciad. + */ + void (*update_vid)(HexL2VicInterface *l2vic, uint32_t group, + uint32_t value); + + /* Clear interrupt using CIAD instruction */ + void (*clear_interrupt)(HexL2VicInterface *l2vic); +} HexL2VicInterfaceClass; + +DECLARE_OBJ_CHECKERS(HexL2VicInterface, HexL2VicInterfaceClass, + HEX_L2VIC_INTERFACE, TYPE_HEX_L2VIC_INTERFACE); + +static inline uint32_t l2vic_read_vid(HexL2VicInterface *l2vic, + uint32_t group) +{ + HexL2VicInterfaceClass *k = HEX_L2VIC_INTERFACE_GET_CLASS(l2vic); + return k->read_vid(l2vic, group); +} + +static inline void l2vic_update_vid(HexL2VicInterface *l2vic, uint32_t group, + uint32_t value) +{ + HexL2VicInterfaceClass *k = HEX_L2VIC_INTERFACE_GET_CLASS(l2vic); + k->update_vid(l2vic, group, value); +} + +static inline void l2vic_clear_interrupt(HexL2VicInterface *l2vic) +{ + HexL2VicInterfaceClass *k = HEX_L2VIC_INTERFACE_GET_CLASS(l2vic); + k->clear_interrupt(l2vic); +} + +#endif /* HW_INTC_HEX_L2VIC_H */ diff --git a/include/hw/intc/loongarch_dintc.h b/include/hw/intc/loongarch_dintc.h index 1f4f65705a..95a7879e26 100644 --- a/include/hw/intc/loongarch_dintc.h +++ b/include/hw/intc/loongarch_dintc.h @@ -9,8 +9,13 @@ #include "hw/core/sysbus.h" #include "hw/loongarch/virt.h" #include "system/memory.h" +#include "hw/pci-host/ls7a.h" #define NR_VECTORS 256 +#define IRQ_BIT_BASE 5 +#define IRQ_BIT_LEN 8 +#define CPU_BIT_BASE 13 +#define CPU_BIT_LEN 8 #define TYPE_LOONGARCH_DINTC "loongarch_dintc" OBJECT_DECLARE_TYPE(LoongArchDINTCState, LoongArchDINTCClass, LOONGARCH_DINTC) @@ -25,7 +30,10 @@ struct LoongArchDINTCState { SysBusDevice parent_obj; MemoryRegion dintc_mmio; DINTCCore *cpu; + int dev_fd; uint32_t num_cpu; + uint64_t msg_addr_base; + uint64_t msg_addr_size; }; struct LoongArchDINTCClass { @@ -34,3 +42,5 @@ struct LoongArchDINTCClass { DeviceRealize parent_realize; DeviceUnrealize parent_unrealize; }; + +void kvm_dintc_realize(DeviceState *dev, Error **errp); diff --git a/include/hw/misc/aspeed_hace.h b/include/hw/misc/aspeed_hace.h index b5416b0cb5..9b0e7683fa 100644 --- a/include/hw/misc/aspeed_hace.h +++ b/include/hw/misc/aspeed_hace.h @@ -49,7 +49,6 @@ struct AspeedHACEClass { uint32_t key_mask; uint32_t hash_mask; uint64_t nr_regs; - bool raise_crypt_interrupt_workaround; uint32_t src_hi_mask; uint32_t dest_hi_mask; uint32_t key_hi_mask; diff --git a/include/hw/misc/aspeed_scu.h b/include/hw/misc/aspeed_scu.h index c30940ab76..904549465f 100644 --- a/include/hw/misc/aspeed_scu.h +++ b/include/hw/misc/aspeed_scu.h @@ -20,6 +20,7 @@ OBJECT_DECLARE_TYPE(AspeedSCUState, AspeedSCUClass, ASPEED_SCU) #define TYPE_ASPEED_2500_SCU TYPE_ASPEED_SCU "-ast2500" #define TYPE_ASPEED_2600_SCU TYPE_ASPEED_SCU "-ast2600" #define TYPE_ASPEED_2700_SCU TYPE_ASPEED_SCU "-ast2700" +OBJECT_DECLARE_SIMPLE_TYPE(Aspeed2700SCUState, ASPEED_2700_SCU) #define TYPE_ASPEED_2700_SCUIO TYPE_ASPEED_SCU "io" "-ast2700" #define TYPE_ASPEED_1030_SCU TYPE_ASPEED_SCU "-ast1030" @@ -41,6 +42,10 @@ struct AspeedSCUState { uint32_t hw_prot_key; }; +struct Aspeed2700SCUState { + AspeedSCUState parent_obj; +}; + #define AST2400_A1_SILICON_REV 0x02010303U #define AST2500_A1_SILICON_REV 0x04010303U #define AST2600_A3_SILICON_REV 0x05030303U diff --git a/include/hw/misc/axiado_clk.h b/include/hw/misc/axiado_clk.h new file mode 100644 index 0000000000..6e12a509d7 --- /dev/null +++ b/include/hw/misc/axiado_clk.h @@ -0,0 +1,26 @@ +/* + * Axiado AX3000 Clock Control + * + * Author: Kuan-Jui Chiu + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef AXIADO_AX3000_CLK_H +#define AXIADO_AX3000_CLK_H + +#include "hw/core/sysbus.h" +#include "qom/object.h" + +#define TYPE_AX3000_CLK "ax3000-clk" +OBJECT_DECLARE_SIMPLE_TYPE(Ax3000ClkState, AX3000_CLK) + +#define AX3000_CLK_PLL_CTRL_SIZE 0x1000 + +typedef struct Ax3000ClkState { + SysBusDevice parent; + + MemoryRegion pll_ctrl; +} Ax3000ClkState; + +#endif /* AXIADO_AX3000_CLK_H */ diff --git a/include/hw/misc/bcm2835_powermgt.h b/include/hw/misc/bcm2835_powermgt.h index fb0740c01e..d1903a9cce 100644 --- a/include/hw/misc/bcm2835_powermgt.h +++ b/include/hw/misc/bcm2835_powermgt.h @@ -12,6 +12,7 @@ #define BCM2835_POWERMGT_H #include "hw/core/sysbus.h" +#include "qemu/timer.h" #include "qom/object.h" #define TYPE_BCM2835_POWERMGT "bcm2835-powermgt" @@ -24,6 +25,7 @@ struct BCM2835PowerMgtState { uint32_t rstc; uint32_t rsts; uint32_t wdog; + QEMUTimer *wdog_timer; }; #endif diff --git a/include/hw/misc/k230_ddr.h b/include/hw/misc/k230_ddr.h new file mode 100644 index 0000000000..d912e8e1b0 --- /dev/null +++ b/include/hw/misc/k230_ddr.h @@ -0,0 +1,59 @@ +/* + * Kendryte K230 DDR controller and PHY models + * + * Device state and type declarations for the K230 DDRC CFG and K230 DDR PHY + * registers. + * + * Copyright (c) 2026 Junze Cao + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_MISC_K230_DDR_H +#define HW_MISC_K230_DDR_H + +#include "hw/core/sysbus.h" + +#define K230_DDRC_MMIO_SIZE 0x02000000 +#define K230_DDR_PHY_MMIO_SIZE 0x00400000 + +#define K230_DDRC_REG_SIZE 0x328 +#define K230_DDRC_REG_COUNT (K230_DDRC_REG_SIZE / sizeof(uint32_t)) + +#define K230_DDR_PHY_ANIB_COUNT 10 +#define K230_DDR_PHY_DBYTE_COUNT 4 +#define K230_DDR_PHY_NIBBLES_PER_DBYTE 2 + +#define TYPE_K230_DDR_CFG "riscv.k230.ddr-cfg" +OBJECT_DECLARE_SIMPLE_TYPE(K230DDRCfgState, K230_DDR_CFG) + +#define TYPE_K230_DDR_PHY "riscv.k230.ddr-phy" +OBJECT_DECLARE_SIMPLE_TYPE(K230DDRPhyState, K230_DDR_PHY) + +struct K230DDRCfgState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + K230DDRPhyState *phy; + uint32_t regs[K230_DDRC_REG_COUNT]; + bool initialized; +}; + +struct K230DDRPhyState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + uint16_t atx_impedance[K230_DDR_PHY_ANIB_COUNT]; + uint16_t tx_impedance_ctrl1[K230_DDR_PHY_DBYTE_COUNT] + [K230_DDR_PHY_NIBBLES_PER_DBYTE]; + uint16_t tx_odt_drv_stren[K230_DDR_PHY_DBYTE_COUNT] + [K230_DDR_PHY_NIBBLES_PER_DBYTE]; + uint16_t dfi_init_complete; + uint16_t vref_in_global; + uint16_t micro_cont_mux_sel; + bool training_trigger_seen; + bool training_complete; + bool mailbox_message_pending; +}; + +#endif diff --git a/include/hw/misc/k230_decomp_gzip.h b/include/hw/misc/k230_decomp_gzip.h new file mode 100644 index 0000000000..9911af0095 --- /dev/null +++ b/include/hw/misc/k230_decomp_gzip.h @@ -0,0 +1,92 @@ +/* + * K230 Decompress Engine + * + * Copyright (c) 2026 Tao Ding + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_MISC_K230_DECOMP_GZIP_H +#define HW_MISC_K230_DECOMP_GZIP_H + +#include +#include "hw/core/sysbus.h" + +#define TYPE_K230_DECOMP_GZIP "riscv.k230.decomp-gzip" +OBJECT_DECLARE_SIMPLE_TYPE(K230DecompGzipState, K230_DECOMP_GZIP) + +/* K230 DECOMP GZIP ACK input */ +enum { + K230_DECOMP_GZIP_GPIO_DMA_WRITE_ACK, + K230_DECOMP_GZIP_GPIO_DMA_READ_ACK, + K230_DECOMP_GZIP_NUM_GPIOS_IN, +}; + +/* K230 DECOMP GZIP REQ output */ +enum { + K230_DECOMP_GZIP_GPIO_DECOMP_CTRL_EN, + K230_DECOMP_GZIP_GPIO_DMA_WRITE_REQ, + K230_DECOMP_GZIP_GPIO_DMA_READ_REQ, + K230_DECOMP_GZIP_NUM_GPIOS_OUT, +}; + +#define K230_DECOMP_GZIP_MMIO_SIZE 0x4000 + +/* K230 DECOMP GZIP Registers map */ +/* Start decompression controller */ +#define K230_DECOMP_GZIP_DECOMP_START 0x00 +/* Source data length register */ +#define K230_DECOMP_GZIP_GZIP_SRC_SIZE 0x04 +/* Output data length register */ +#define K230_DECOMP_GZIP_GZIP_OUT_SIZE 0x08 +/* Decompress status register */ +#define K230_DECOMP_GZIP_DECOMP_STAT 0x0c + +/* Start decompression controller */ +#define K230_DECOMP_GZIP_START (1U << 0) + +#define K230_DECOMP_GZIP_CTRL_EN (1U << 31) +#define K230_DECOMP_GZIP_DMA_IN_MASK 0x7fffffffU + +#define K230_DECOMP_GZIP_STAT_CRC_OK (1U << 10) +#define K230_DECOMP_GZIP_STAT_STATE_MASK 0xfU + +#define K230_DECOMP_GZIP_BLOCK_SIZE 0x00020000 +#define K230_DECOMP_GZIP_SRAM_IN_BASE 0x80000 +#define K230_DECOMP_GZIP_SRAM_OUT_BASE 0 + +typedef struct K230DecompGzipSlotState { + uint32_t slot; /* Slot index */ + /* + * Data offset in current slot. + * First valid data in input buffer. Last valid data in output buffer. + */ + uint32_t current_offset; +} K230DecompGzipSlotState; + +struct K230DecompGzipState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + hwaddr sram_base; + qemu_irq signal_out[K230_DECOMP_GZIP_NUM_GPIOS_OUT]; + uint32_t decomp_start; + uint32_t gzip_src_size; + uint32_t gzip_out_size; + uint32_t decomp_stat; + K230DecompGzipSlotState input; /* decomp gzip ring input */ + K230DecompGzipSlotState output; /* decomp gzip ring output */ + uint32_t total_requested; + uint32_t total_produced; + bool active; + bool in_kick; + bool zstream_inited; + bool stream_end; + /* Read from ring input. */ + uint8_t input_buf[K230_DECOMP_GZIP_BLOCK_SIZE]; + /* Write to ring output. */ + uint8_t output_buf[K230_DECOMP_GZIP_BLOCK_SIZE]; + z_stream zs; +}; + +#endif diff --git a/include/hw/misc/vmlaunchupdate.h b/include/hw/misc/vmlaunchupdate.h new file mode 100644 index 0000000000..02f96cebdc --- /dev/null +++ b/include/hw/misc/vmlaunchupdate.h @@ -0,0 +1,38 @@ +/* + * Guest driven VM launch state update device via IGVM. + * For details and specification, please look at docs/specs/vmlaunchupdate.rst. + * + * Copyright (C) 2026 Red Hat, Inc. + * + * Authors: Ani Sinha + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ +#ifndef VMLAUNCHUPDATE_H +#define VMLAUNCHUPDATE_H + +#include "hw/core/qdev.h" +#include "qom/object.h" +#include "qemu/units.h" +#include "system/igvm-cfg.h" +#include "standard-headers/misc/vmlaunchupdate.h" + +#define TYPE_VMLAUNCHUPDATE "vm-launch-update" + +typedef struct VMLaunchUpdateState { + DeviceState parent_obj; + VMLaunchUpdate launch_update; + bool disabled; + bool host_igvm_on_reset; + ResettableState reset_state; +} VMLaunchUpdateState; + + +typedef struct VMLaunchUpdateStateClass { + ObjectClass parent_class; +} VMLaunchUpdateStateClass; + +OBJECT_DECLARE_SIMPLE_TYPE(VMLaunchUpdateState, VMLAUNCHUPDATE); + +#endif diff --git a/include/hw/net/flexcan.h b/include/hw/net/flexcan.h index 32de7b904a..eee70bad42 100644 --- a/include/hw/net/flexcan.h +++ b/include/hw/net/flexcan.h @@ -35,16 +35,6 @@ typedef struct FlexcanRegsMessageBuffer { uint32_t data[2]; } FlexcanRegsMessageBuffer; -/* RX FIFO view of message buffer registers */ -typedef struct FlexcanRegsRXFifo { - /* 6 message buffer deep queue, queue back first */ - FlexcanRegsMessageBuffer mb_back; - FlexcanRegsMessageBuffer mbs_queue[FLEXCAN_FIFO_DEPTH - 1]; - - /* number of filter elements active depends on ctrl2 | FLEXCAN_CTRL2_RFFN */ - uint32_t filter_table_els[128]; -} FlexcanRegsRXFifo; - /* FlexCAN register in hw layout */ typedef struct FlexcanRegs { uint32_t mcr; /* 0x00 */ @@ -75,11 +65,8 @@ typedef struct FlexcanRegs { uint32_t dbg1; /* 0x58, unused */ uint32_t dbg2; /* 0x5C, unused */ uint32_t _reserved3[8]; /* 0x60 */ - union { /* 0x80 - not affected by soft reset */ - uint32_t mb[sizeof(FlexcanRegsMessageBuffer) * FLEXCAN_MAILBOX_COUNT]; - FlexcanRegsMessageBuffer mbs[FLEXCAN_MAILBOX_COUNT]; - FlexcanRegsRXFifo fifo; - }; + /* 0x80 - not affected by soft reset */ + FlexcanRegsMessageBuffer mbs[FLEXCAN_MAILBOX_COUNT]; uint32_t _reserved4[256]; /* 0x480 */ uint32_t rximr[64]; /* 0x880 - not affected by soft reset */ uint32_t _reserved5[24]; /* 0x980 */ @@ -94,10 +81,7 @@ typedef struct FlexcanRegs { uint32_t _rx14mask; /* 0xAA8 */ uint32_t _rx15mask; /* 0xAAC */ uint32_t tx_smb[4]; /* 0xAB0 */ - union { /* 0xAC0, used for SMB emulation */ - uint32_t rx_smb0_raw[4]; - FlexcanRegsMessageBuffer rx_smb0; - }; + FlexcanRegsMessageBuffer rx_smb0; /* 0xAC0, used for SMB emulation */ uint32_t rx_smb1[4]; /* 0xAD0 */ uint32_t mecr; /* 0xAE0 */ uint32_t erriar; /* 0xAE4 */ diff --git a/include/hw/riscv/k230.h b/include/hw/riscv/k230.h index 592e1c26bf..8679d09891 100644 --- a/include/hw/riscv/k230.h +++ b/include/hw/riscv/k230.h @@ -16,8 +16,11 @@ #define HW_K230_H #include "hw/core/boards.h" +#include "hw/misc/k230_ddr.h" #include "hw/riscv/riscv_hart.h" #include "hw/watchdog/k230_wdt.h" +#include "hw/dma/k230_gsdma.h" +#include "hw/misc/k230_decomp_gzip.h" #define C908_CPU_HARTID (0) @@ -32,7 +35,11 @@ typedef struct K230SoCState { /*< public >*/ RISCVHartArrayState c908_cpu; /* Small core */ + K230DDRCfgState ddr_cfg; + K230DDRPhyState ddr_phy; K230WdtState wdt[2]; + K230GSDMAState gsdma; + K230DecompGzipState decomp_gzip; MemoryRegion sram; MemoryRegion bootrom; @@ -112,6 +119,7 @@ enum { K230_DEV_SPI, K230_DEV_HI_SYS_CFG, K230_DEV_DDRC_CFG, + K230_DEV_DDR_PHY, K230_DEV_FLASH, K230_DEV_PLIC, K230_DEV_CLINT, @@ -129,6 +137,7 @@ enum { K230_UART4_IRQ = 20, K230_WDT0_IRQ = 107, K230_WDT1_IRQ = 108, + K230_GSDMA_IRQ = 140, }; #define K230_UART_COUNT 5 diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index 36a2def410..7c862b0da2 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -59,6 +59,7 @@ struct RISCVVirtState { int aia_guests; char *oem_id; char *oem_table_id; + bool uart1_present; OnOffAuto acpi; const MemMapEntry *memmap; struct GPEXHost *gpex_host; @@ -79,6 +80,7 @@ enum { VIRT_APLIC_S, VIRT_UART0, VIRT_VIRTIO, + VIRT_UART1, VIRT_FW_CFG, VIRT_IMSIC_M, VIRT_IMSIC_S, @@ -94,6 +96,7 @@ enum { enum { UART0_IRQ = 10, RTC_IRQ = 11, + UART1_IRQ = 12, VIRTIO_IRQ = 1, /* 1 to 8 */ VIRTIO_COUNT = 8, PCIE_IRQ = 0x20, /* 32 to 35 */ diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h index 8d3c83a80b..b390f2f112 100644 --- a/include/hw/s390x/ipl/qipl.h +++ b/include/hw/s390x/ipl/qipl.h @@ -20,6 +20,8 @@ #define LOADPARM_LEN 8 #define NO_LOADPARM "\0\0\0\0\0\0\0\0" +#define MAX_BOOT_ENTRIES 32 + enum S390IplType { S390_IPL_TYPE_FCP = 0x00, S390_IPL_TYPE_CCW = 0x02, diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h index 5f83e58d1d..c60c6e8810 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -221,6 +221,7 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, int32_t scsi_req_enqueue(SCSIRequest *req); SCSIRequest *scsi_req_ref(SCSIRequest *req); void scsi_req_unref(SCSIRequest *req); +void scsi_req_unref_detach_hba(SCSIRequest *req); int scsi_bus_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf, size_t buf_len, void *hba_private); diff --git a/include/hw/sd/axiado_sdhci.h b/include/hw/sd/axiado_sdhci.h new file mode 100644 index 0000000000..85afebad93 --- /dev/null +++ b/include/hw/sd/axiado_sdhci.h @@ -0,0 +1,21 @@ +/* + * Axiado SD Host Controller + * + * Author: Kuan-Jui Chiu + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "hw/sd/sdhci.h" +#include "qom/object.h" + +#define TYPE_AXIADO_SDHCI "axiado-sdhci" +OBJECT_DECLARE_SIMPLE_TYPE(AxiadoSDHCIState, AXIADO_SDHCI) + +typedef struct AxiadoSDHCIState { + SysBusDevice parent; + + SDHCIState sdhci; + MemoryRegion emmc_phy; + BusState *sd_bus; +} AxiadoSDHCIState; diff --git a/include/hw/sensor/adc128d818.h b/include/hw/sensor/adc128d818.h new file mode 100644 index 0000000000..10c34b9646 --- /dev/null +++ b/include/hw/sensor/adc128d818.h @@ -0,0 +1,14 @@ +/* + * Texas Instruments ADC128D818 12-bit 8-channel ADC with I2C interface + * + * Copyright (c) 2026 Meta Platforms, Inc. and affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_SENSOR_ADC128D818_H +#define HW_SENSOR_ADC128D818_H + +#define TYPE_ADC128D818 "adc128d818" + +#endif diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index a273365689..5f391fc24e 100644 --- a/include/hw/ssi/aspeed_smc.h +++ b/include/hw/ssi/aspeed_smc.h @@ -47,7 +47,7 @@ struct AspeedSMCFlash { #define TYPE_ASPEED_SMC "aspeed.smc" OBJECT_DECLARE_TYPE(AspeedSMCState, AspeedSMCClass, ASPEED_SMC) -#define ASPEED_SMC_R_MAX (0x100 / 4) +#define ASPEED_SMC_R_MAX (0x300 / 4) #define ASPEED_SMC_CS_MAX 5 struct AspeedSMCState { @@ -114,6 +114,7 @@ struct AspeedSMCClass { AspeedSegments *seg); void (*dma_ctrl)(AspeedSMCState *s, uint32_t value); int (*addr_width)(const AspeedSMCState *s); + int (*data_fifo_offset_to_cs)(const AspeedSMCState *s, uint32_t offset); const MemoryRegionOps *reg_ops; }; diff --git a/include/hw/timer/qct-qtimer.h b/include/hw/timer/qct-qtimer.h new file mode 100644 index 0000000000..53d8291472 --- /dev/null +++ b/include/hw/timer/qct-qtimer.h @@ -0,0 +1,43 @@ +/* + * Qualcomm QCT QTimer + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_TIMER_QCT_QTIMER_H +#define HW_TIMER_QCT_QTIMER_H + +#include "qom/object.h" + +#define TYPE_QCT_QTIMER "qct-qtimer" + +/* QTimer interface for external access from hexagon_globalreg */ +#define TYPE_QCT_QTIMER_INTERFACE "qct-qtimer-if" + +typedef struct QctQtimerInterface QctQtimerInterface; + +typedef struct QctQtimerInterfaceClass { + InterfaceClass parent_class; + + /* Read the live physical counter, backing HEX_SREG_TIMERLO/TIMERHI */ + uint32_t (*get_timer_lo)(const QctQtimerInterface *qtimer); + uint32_t (*get_timer_hi)(const QctQtimerInterface *qtimer); +} QctQtimerInterfaceClass; + +DECLARE_OBJ_CHECKERS(QctQtimerInterface, QctQtimerInterfaceClass, + QCT_QTIMER_INTERFACE, TYPE_QCT_QTIMER_INTERFACE); + +static inline uint32_t qct_qtimer_get_timer_lo(const QctQtimerInterface *qtimer) +{ + QctQtimerInterfaceClass *k = QCT_QTIMER_INTERFACE_GET_CLASS(qtimer); + return k->get_timer_lo(qtimer); +} + +static inline uint32_t qct_qtimer_get_timer_hi(const QctQtimerInterface *qtimer) +{ + QctQtimerInterfaceClass *k = QCT_QTIMER_INTERFACE_GET_CLASS(qtimer); + return k->get_timer_hi(qtimer); +} + +#endif /* HW_TIMER_QCT_QTIMER_H */ diff --git a/include/hw/virtio/virtio-bus.h b/include/hw/virtio/virtio-bus.h index 1a2d396156..f80fd71424 100644 --- a/include/hw/virtio/virtio-bus.h +++ b/include/hw/virtio/virtio-bus.h @@ -30,6 +30,7 @@ #include "qom/object.h" #define TYPE_VIRTIO_BUS "virtio-bus" +#define VIRTIO_QUEUE_SIZE_OVERRIDE "x-override-queue-size" typedef struct VirtioBusClass VirtioBusClass; typedef struct VirtioBusState VirtioBusState; DECLARE_OBJ_CHECKERS(VirtioBusState, VirtioBusClass, diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index f69fc19462..69b5ee2e38 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -15,6 +15,7 @@ #define HW_VIRTIO_GPU_H #include "qemu/queue.h" +#include "qemu/units.h" #include "ui/qemu-pixman.h" #include "ui/console.h" #include "hw/virtio/virtio.h" @@ -65,7 +66,6 @@ struct virtio_gpu_simple_resource { struct virtio_gpu_framebuffer { pixman_format_code_t format; - uint32_t bytes_pp; uint32_t width, height; uint32_t stride; uint32_t offset; @@ -299,6 +299,14 @@ struct VirtIOGPURutabaga { struct rutabaga *rutabaga; }; +/* + * With 4 KiB pages and QEMU's VIRTQUEUE_MAX_SIZE (1024) mapped-iov + * limit, the largest inline command is ~4 MiB. Cap submit_3d + * allocations to this value to prevent a malicious guest from + * triggering an OOM abort via an inflated cs.size field. + */ +#define VIRTIO_GPU_MAX_CMD_SUBMIT_SIZE (4 * MiB) + #define VIRTIO_GPU_FILL_CMD(out) do { \ size_t virtiogpufillcmd_s_ = \ iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 0, \ @@ -307,6 +315,9 @@ struct VirtIOGPURutabaga { qemu_log_mask(LOG_GUEST_ERROR, \ "%s: command size incorrect %zu vs %zu\n", \ __func__, virtiogpufillcmd_s_, sizeof(out)); \ + memset(&out, 0, sizeof(out)); \ + virtio_gpu_ctrl_response_nodata( \ + g, cmd, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER); \ return; \ } \ } while (0) @@ -355,6 +366,11 @@ void virtio_gpu_update_cursor_data(VirtIOGPU *g, struct virtio_gpu_scanout *s, uint32_t resource_id); +bool virtio_gpu_check_scanout_bounds(uint32_t scanout_id, uint32_t resource_id, + uint32_t width, uint32_t height, + const struct virtio_gpu_rect *r, + uint32_t *error); + /** * virtio_gpu_scanout_blob_to_fb() - fill out fb based on scanout data * fb: the frame-buffer descriptor to fill out @@ -372,7 +388,7 @@ bool virtio_gpu_scanout_blob_to_fb(struct virtio_gpu_framebuffer *fb, /* virtio-gpu-udmabuf.c */ bool virtio_gpu_have_udmabuf(void); -void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res); +bool virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res); void virtio_gpu_fini_udmabuf(VirtIOGPU *g, struct virtio_gpu_simple_resource *res); int virtio_gpu_update_dmabuf(VirtIOGPU *g, diff --git a/include/hw/virtio/virtio-iommu.h b/include/hw/virtio/virtio-iommu.h index 3b86050f2c..1f265540ad 100644 --- a/include/hw/virtio/virtio-iommu.h +++ b/include/hw/virtio/virtio-iommu.h @@ -65,6 +65,7 @@ struct VirtIOIOMMU { GTree *domains; QemuRecMutex mutex; GTree *endpoints; + QEMUTimer *cmd_timer; bool boot_bypass; Notifier machine_done; bool granule_frozen; diff --git a/include/hw/virtio/virtio-mmio.h b/include/hw/virtio/virtio-mmio.h index 1644d09810..0a9069868c 100644 --- a/include/hw/virtio/virtio-mmio.h +++ b/include/hw/virtio/virtio-mmio.h @@ -69,6 +69,7 @@ struct VirtIOMMIOProxy { /* Fields only used for non-legacy (v2) devices */ uint32_t guest_features[2]; VirtIOMMIOQueue vqs[VIRTIO_QUEUE_MAX]; + uint16_t override_queue_size; }; #endif diff --git a/include/hw/virtio/virtio-pmem.h b/include/hw/virtio/virtio-pmem.h index 9cce600d0b..bb959d10cf 100644 --- a/include/hw/virtio/virtio-pmem.h +++ b/include/hw/virtio/virtio-pmem.h @@ -32,6 +32,7 @@ struct VirtIOPMEM { VirtQueue *rq_vq; uint64_t start; HostMemoryBackend *memdev; + unsigned int inflight; }; struct VirtIOPMEMClass { diff --git a/include/io/channel.h b/include/io/channel.h index 98485c9280..c90ace20d5 100644 --- a/include/io/channel.h +++ b/include/io/channel.h @@ -1016,8 +1016,8 @@ void qio_channel_wait(QIOChannel *ioc, * This will work with/without a coroutine context, by automatically select * the proper API to wait. */ -void qio_channel_wait_cond(QIOChannel *ioc, - GIOCondition condition); +void coroutine_mixed_fn qio_channel_wait_cond(QIOChannel *ioc, + GIOCondition condition); /** * qio_channel_set_aio_fd_handler: diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index 890497b0d2..54134f2aa4 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -1,9 +1,8 @@ #ifndef MONITOR_H #define MONITOR_H -#include "block/block.h" #include "qapi/qapi-types-misc.h" -#include "qapi/qapi-emit-events.h" +#include "qemu/coroutine-core.h" #include "qemu/readline.h" #include "exec/hwaddr.h" #include "qom/object.h" @@ -56,8 +55,8 @@ void monitor_flush_locked(Monitor *mon); void *gpa2hva(MemoryRegion **p_mr, hwaddr addr, uint64_t size, Error **errp); -void monitor_read_command(MonitorHMP *mon, int show_prompt); -int monitor_read_password(MonitorHMP *mon, ReadLineFunc *readline_func, +void monitor_read_command(MonitorHMP *hmp, int show_prompt); +int monitor_read_password(MonitorHMP *hmp, ReadLineFunc *readline_func, void *opaque); AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id, diff --git a/include/net/net.h b/include/net/net.h index 45bc86fc86..9edfacf827 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -349,9 +349,32 @@ uint32_t net_crc32_le(const uint8_t *p, int len); .offset = vmstate_offset_macaddr(_state, _field), \ } +/** + * net_peer_needs_padding: Should we pad as we send out packets? + * @nc: NetClientState + * + * Return true if the peer of this NetClientState (i.e. the + * destination that qemu_send_packet() etc send to) requires us to pad + * out packets that are shorter than the minimum ethernet frame + * length. + */ static inline bool net_peer_needs_padding(NetClientState *nc) { return nc->peer && !nc->peer->do_not_pad; } +/** + * net_client_needs_padding: Should we pad as we queue packets to ourselves? + * @nc: NetClientState + * + * Return true if this NetClientState requires us to pad out packets + * that are shorter than the minimum ethernet frame length. This is + * the check to make in qemu_receive_packet() when we are queuing a + * packet back into ourselves (i.e. loopback). + */ +static inline bool net_client_needs_padding(NetClientState *nc) +{ + return !nc->do_not_pad; +} + #endif diff --git a/include/qapi/qmp-registry.h b/include/qapi/qmp-registry.h index e0ee1ad3ac..6146306a3a 100644 --- a/include/qapi/qmp-registry.h +++ b/include/qapi/qmp-registry.h @@ -14,7 +14,6 @@ #ifndef QAPI_QMP_DISPATCH_H #define QAPI_QMP_DISPATCH_H -#include "monitor/monitor.h" #include "qemu/queue.h" typedef void (QmpCommandFunc)(QDict *, QObject **, Error **); diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h index c7b838a628..f7363a5479 100644 --- a/include/qemu/bitops.h +++ b/include/qemu/bitops.h @@ -43,10 +43,9 @@ * be some guest-visible register view of the bit array. * * We do not currently implement uint32_t versions of find_last_bit(), - * find_next_bit(), find_next_zero_bit(), find_first_bit() or - * find_first_zero_bit(), because we haven't yet needed them. If you - * need them you should implement them similarly to the 'unsigned long' - * versions. + * find_next_bit(), find_next_zero_bit() or find_first_zero_bit(), + * because we haven't yet needed them. If you need them you should + * implement them similarly to the 'unsigned long' versions. * * You can declare a bitmap to be used with these functions via the * DECLARE_BITMAP and DECLARE_BITMAP32 macros in bitmap.h. @@ -382,6 +381,29 @@ static inline int test_bit32(long nr, const uint32_t *addr) return 1U & (addr[BIT32_WORD(nr)] >> (nr & 31)); } +/** + * find_first_bit32 - find the first set bit in a memory region + * @addr: The address to start the search at + * @size: The maximum size to search + * + * Returns the bit number of the first set bit, + * or @size if there is no set bit in the bitmap. + */ +static inline uint32_t find_first_bit32(const uint32_t *addr, uint32_t size) +{ + uint32_t result; + + for (result = 0; result < size; result += 32) { + uint32_t tmp = *addr++; + if (tmp) { + result += ctz32(tmp); + return result < size ? result : size; + } + } + /* Not found */ + return size; +} + /** * DOC: Miscellaneous bit operations on single values * diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h index e545bbf620..1c31de60f9 100644 --- a/include/qemu/coroutine.h +++ b/include/qemu/coroutine.h @@ -260,10 +260,19 @@ int coroutine_fn qemu_co_timeout(CoroutineEntry *entry, void *opaque, uint64_t timeout_ns, CleanupFunc clean); /** - * Wake a coroutine if it is sleeping in qemu_co_sleep_ns. The timer will be - * deleted. @sleep_state must be the variable whose address was given to - * qemu_co_sleep_ns() and should be checked to be non-NULL before calling - * qemu_co_sleep_wake(). + * Wake a coroutine sleeping in qemu_co_sleep() or qemu_co_sleep_ns_wakeable(). + * The timer set up by the latter is deleted on wakeup. + * + * The wake is sticky: if no sleeper is parked on @w at the time of the call, + * the wake is recorded on @w and consumed by the next qemu_co_sleep() on the + * same @w, which then returns without yielding. This closes the lost-wakeup + * window between two sleeps and is the documented behavior callers should + * rely on -- e.g. a cancellation signal raised between iterations of a + * sleep/work loop will shorten the next sleep instead of being dropped. + * + * The state persists until consumed: if no further qemu_co_sleep() is ever + * called on @w, the pending wake is harmlessly discarded when @w goes away. + * Multiple wakes coalesce -- the next sleep consumes at most one. */ void qemu_co_sleep_wake(QemuCoSleep *w); diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index 2e8da7fb3d..1db9dbb138 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -607,10 +607,10 @@ static inline bool umul64_overflow(uint64_t x, uint64_t y, uint64_t *ret) } /** - * sadd32_saturate - addition with saturation + * sadd32_saturate - 32-bit signed addition with saturation * @x, @y: addends * - * Computes @x + @y, and saturates rathern than truncating the result. + * Computes @x + @y, and saturates rather than truncating the result. */ static inline int32_t sadd32_saturate(int32_t x, int32_t y) { @@ -622,10 +622,10 @@ static inline int32_t sadd32_saturate(int32_t x, int32_t y) } /** - * sadd64_saturate - addition with saturation + * sadd64_saturate - 64-bit signed addition with saturation * @x, @y: addends * - * Computes @x + @y, and saturates rathern than truncating the result. + * Computes @x + @y, and saturates rather than truncating the result. */ static inline int64_t sadd64_saturate(int64_t x, int64_t y) { @@ -637,31 +637,31 @@ static inline int64_t sadd64_saturate(int64_t x, int64_t y) } /** - * ssub32_saturate - subtraction with saturation + * ssub32_saturate - 32-bit signed subtraction with saturation * @x, @y: addends * - * Computes @x + @y, and saturates rathern than truncating the result. + * Computes @x - @y, and saturates rather than truncating the result. */ -static inline bool ssub32_saturate(int32_t x, int32_t y) +static inline int32_t ssub32_saturate(int32_t x, int32_t y) { int32_t ret; if (ssub32_overflow(x, y, &ret)) { - ret = x < 0 ? INT32_MAX : INT32_MIN; + ret = x < 0 ? INT32_MIN : INT32_MAX; } return ret; } /** - * ssub64_saturate - subtraction with saturation + * ssub64_saturate - 64-bit signed subtraction with saturation * @x, @y: addends * - * Computes @x + @y, and saturates rathern than truncating the result. + * Computes @x - @y, and saturates rather than truncating the result. */ -static inline bool ssub64_saturate(int64_t x, int64_t y) +static inline int64_t ssub64_saturate(int64_t x, int64_t y) { int64_t ret; if (ssub64_overflow(x, y, &ret)) { - ret = x < 0 ? INT64_MAX : INT64_MIN; + ret = x < 0 ? INT64_MIN : INT64_MAX; } return ret; } diff --git a/include/qom/object.h b/include/qom/object.h index 11f55613fc..11b1c9d2dc 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -1802,8 +1802,8 @@ void object_property_allow_set_link(const Object *obj, const char *name, * property is read-only and cannot be set. Care must be taken to handle NULL * values for @val. * - * Ownership of the pointer that @child points to is transferred to the - * link property. The reference count for *@child is + * Ownership of the pointer that @targetp points to is transferred to the + * link property. The reference count for *@targetp is * managed by the property from after the function returns till the * property is deleted with object_property_del(). If the * @flags %OBJ_PROP_LINK_STRONG bit is set, @@ -1818,6 +1818,33 @@ ObjectProperty *object_property_add_link(Object *obj, const char *name, Object *val, Error **errp), ObjectPropertyLinkFlags flags); +/** + * object_class_property_add_link: + * @oc: the object class to add a property to + * @name: the name of the property + * @type: the qobj type of the link + * @offset: the offset from the object instance where the link object reference + * is stored + * @check: callback to veto setting or NULL if the property is read-only + * @flags: additional options for the link + * + * Links establish relationships between objects. Links are unidirectional + * although two links can be combined to form a bidirectional relationship + * between objects. + * + * Links form the graph in the object model. + * + * The @check() callback is invoked when object_property_set_link() is called + * and can raise an error to prevent the link being set. If @check is NULL, the + * property is read-only and cannot be set. Care must be taken to handle NULL + * values for @val. + * + * If the @flags %OBJ_PROP_LINK_STRONG bit is set, the reference count of the + * linked object is incremented when the property is set, and decremented again + * when the property is modified. + * + * Returns: The newly added property on success, or %NULL on failure. + */ ObjectProperty *object_class_property_add_link(ObjectClass *oc, const char *name, const char *type, ptrdiff_t offset, @@ -1859,6 +1886,19 @@ ObjectProperty *object_property_add_str(Object *obj, const char *name, char *(*get)(Object *, Error **), void (*set)(Object *, const char *, Error **)); +/** + * object_class_property_add_str: + * @klass: the object class to add a property to + * @name: the name of the property + * @get: the getter or NULL if the property is write-only. This function must + * return a string to be freed by g_free(). + * @set: the setter or NULL if the property is read-only + * + * Add a string property using getters/setters. This function will add a + * property of type 'string'. + * + * Returns: The newly added property on success, or %NULL on failure. + */ ObjectProperty *object_class_property_add_str(ObjectClass *klass, const char *name, char *(*get)(Object *, Error **), @@ -1881,6 +1921,18 @@ ObjectProperty *object_property_add_bool(Object *obj, const char *name, bool (*get)(Object *, Error **), void (*set)(Object *, bool, Error **)); +/** + * object_class_property_add_bool: + * @klass: the object class to add a property to + * @name: the name of the property + * @get: the getter or NULL if the property is write-only. + * @set: the setter or NULL if the property is read-only + * + * Add a bool property using getters/setters. This function will add a + * property of type 'bool'. + * + * Returns: The newly added property on success, or %NULL on failure. + */ ObjectProperty *object_class_property_add_bool(ObjectClass *klass, const char *name, bool (*get)(Object *, Error **), @@ -1906,6 +1958,20 @@ ObjectProperty *object_property_add_enum(Object *obj, const char *name, int (*get)(Object *, Error **), void (*set)(Object *, int, Error **)); +/** + * object_class_property_add_enum: + * @klass: the object class to add a property to + * @name: the name of the property + * @typename: the name of the enum data type + * @lookup: enum value namelookup table + * @get: the getter or %NULL if the property is write-only. + * @set: the setter or %NULL if the property is read-only + * + * Add an enum property using getters/setters. This function will add a + * property of type '@typename'. + * + * Returns: The newly added property on success, or %NULL on failure. + */ ObjectProperty *object_class_property_add_enum(ObjectClass *klass, const char *name, const char *typename, @@ -1927,6 +1993,17 @@ ObjectProperty *object_class_property_add_enum(ObjectClass *klass, ObjectProperty *object_property_add_tm(Object *obj, const char *name, void (*get)(Object *, struct tm *, Error **)); +/** + * object_class_property_add_tm: + * @klass: the object class to add a property to + * @name: the name of the property + * @get: the getter or NULL if the property is write-only. + * + * Add a read-only struct tm valued property using a getter function. + * This function will add a property of type 'struct tm'. + * + * Returns: The newly added property on success, or %NULL on failure. + */ ObjectProperty *object_class_property_add_tm(ObjectClass *klass, const char *name, void (*get)(Object *, struct tm *, Error **)); @@ -1956,7 +2033,25 @@ ObjectProperty *object_property_add_uint8_ptr(Object *obj, const char *name, const uint8_t *v, ObjectPropertyFlags flags); -ObjectProperty *object_class_property_add_uint8_ptr(ObjectClass *klass, +/** + * object_class_static_property_add_uint8_ptr: + * @klass: the object class to add a static property to + * @name: the name of the property + * @v: pointer to value + * @flags: bitwise-or'd ObjectPropertyFlags + * + * Add a static integer property in memory. This function will add a + * property of type 'uint8'. + * + * A static property is one which is stored outside of the object instance, + * typically in global variables. It is only appropriate to use static + * properties when the class is designed as a singleton. If there is a + * possibility of multiple instances, then properties must be stored + * per-instance. + * + * Returns: The newly added property on success, or %NULL on failure. + */ +ObjectProperty *object_class_static_property_add_uint8_ptr(ObjectClass *klass, const char *name, const uint8_t *v, ObjectPropertyFlags flags); @@ -1977,7 +2072,25 @@ ObjectProperty *object_property_add_uint16_ptr(Object *obj, const char *name, const uint16_t *v, ObjectPropertyFlags flags); -ObjectProperty *object_class_property_add_uint16_ptr(ObjectClass *klass, +/** + * object_class_static_property_add_uint16_ptr: + * @klass: the object class to add a static property to + * @name: the name of the property + * @v: pointer to value + * @flags: bitwise-or'd ObjectPropertyFlags + * + * Add a static integer property in memory. This function will add a + * property of type 'uint16'. + * + * A static property is one which is stored outside of the object instance, + * typically in global variables. It is only appropriate to use static + * properties when the class is designed as a singleton. If there is a + * possibility of multiple instances, then properties must be stored + * per-instance. + * + * Returns: The newly added property on success, or %NULL on failure. + */ +ObjectProperty *object_class_static_property_add_uint16_ptr(ObjectClass *klass, const char *name, const uint16_t *v, ObjectPropertyFlags flags); @@ -1998,7 +2111,25 @@ ObjectProperty *object_property_add_uint32_ptr(Object *obj, const char *name, const uint32_t *v, ObjectPropertyFlags flags); -ObjectProperty *object_class_property_add_uint32_ptr(ObjectClass *klass, +/** + * object_class_static_property_add_uint32_ptr: + * @klass: the object class to add a static property to + * @name: the name of the property + * @v: pointer to value + * @flags: bitwise-or'd ObjectPropertyFlags + * + * Add a static integer property in memory. This function will add a + * property of type 'uint32'. + * + * A static property is one which is stored outside of the object instance, + * typically in global variables. It is only appropriate to use static + * properties when the class is designed as a singleton. If there is a + * possibility of multiple instances, then properties must be stored + * per-instance. + * + * Returns: The newly added property on success, or %NULL on failure. + */ +ObjectProperty *object_class_static_property_add_uint32_ptr(ObjectClass *klass, const char *name, const uint32_t *v, ObjectPropertyFlags flags); @@ -2019,7 +2150,25 @@ ObjectProperty *object_property_add_uint64_ptr(Object *obj, const char *name, const uint64_t *v, ObjectPropertyFlags flags); -ObjectProperty *object_class_property_add_uint64_ptr(ObjectClass *klass, +/** + * object_class_static_property_add_uint64_ptr: + * @klass: the object class to add a static property to + * @name: the name of the property + * @v: pointer to value + * @flags: bitwise-or'd ObjectPropertyFlags + * + * Add a static integer property in memory. This function will add a + * property of type 'uint64'. + * + * A static property is one which is stored outside of the object instance, + * typically in global variables. It is only appropriate to use static + * properties when the class is designed as a singleton. If there is a + * possibility of multiple instances, then properties must be stored + * per-instance. + * + * Returns: The newly added property on success, or %NULL on failure. + */ +ObjectProperty *object_class_static_property_add_uint64_ptr(ObjectClass *klass, const char *name, const uint64_t *v, ObjectPropertyFlags flags); @@ -2075,6 +2224,17 @@ ObjectProperty *object_property_add_const_link(Object *obj, const char *name, */ void object_property_set_description(Object *obj, const char *name, const char *description); + +/** + * object_class_property_set_description: + * @klass: the object class owning the property + * @name: the name of the property + * @description: the description of the property on the object + * + * Set an object property's description. + * + * Returns: %true on success, %false on failure. + */ void object_class_property_set_description(ObjectClass *klass, const char *name, const char *description); diff --git a/include/standard-headers/misc/vmlaunchupdate.h b/include/standard-headers/misc/vmlaunchupdate.h new file mode 100644 index 0000000000..7f8382a765 --- /dev/null +++ b/include/standard-headers/misc/vmlaunchupdate.h @@ -0,0 +1,102 @@ +/* + * Guest driven VM launch state update device via IGVM. + * The definitions in this header defines the API for the hypervisor interface. + * For details and specification, please look at docs/specs/vmlaunchupdate.rst. + * + * Copyright (C) 2026 Red Hat, Inc. + * + * Authors: Ani Sinha + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ +#ifndef VMLAUNCHUPDATE_API_H +#define VMLAUNCHUPDATE_API_H + +/* fw-cfg file definition */ +#define FILE_VMLAUNCHUPDATE "etc/vmlaunchupdate" + +/* version */ +#define VM_LAUNCHUPDATE_VERSION 0x01 + +/* format bits, used by both 'capabilities' and 'control' */ + +/* igvm */ +#define VM_LAUNCHUPDATE_FORMAT_IGVM (1ULL << 32) + +/* 'control' field bits */ + +/* disable vmlaunchupdate interface */ +#define VM_LAUNCHUPDATE_CTL_DISABLE (1 << 0) +/* revert to the original host provided igvm */ +#define VM_LAUNCHUPDATE_CTL_HOST_IGVM (1 << 1) + +/* The combination of the above two ctl interfaces work as + * follows: + * + * A) CTL_HOST_IGVM=off CTL_DISABLE=off + * + * Supplied IGVM file replaces the firmware permanently. Updating the + * firmware again is possible. + * + * B) CTL_HOST_IGVM=off CTL_DISABLE=on + * + * Supplied IGVM file replaces the firmware permanently. Updating the + * firmware again is not possible. + * + * C) CTL_HOST_IGVM=on CTL_DISABLE=off + * + * Supplied IGVM file replaces the firmware for one reset. Resetting + * again will switch back to the original firmware. Updating the + * firmware again is possible. + * + * D) CTL_HOST_IGVM=on CTL_DISABLE=on + * + * Supplied IGVM file replaces the firmware for one reset. Resetting + * again will switch back to the original firmware. Updating the + * firmware again is NOT possible. + * + */ + +/* status code */ +enum VMLaunchUpdateStatus { + VM_LAUNCHUPDATE_SUCCESS = 0, + VM_LAUNCHUPDATE_LOAD_FAIL = 1, + VM_LAUNCHUPDATE_NOT_IGVM_INIT = 2, +}; + +typedef struct QEMU_PACKED { + /* api version */ + uint16_t version; + + /* + * The guest can read this in order to determine if loading new IGVM + * succeeded. + */ + uint16_t status; + + uint32_t _padding; + + /* VMM capabilities, read-only. */ + uint64_t capabilities; + /* control bits, see VMFWUPDATE_CTL_* */ + uint64_t control; + + /* + * address and size of the IGVM image. Will be cleared when + * the write completes successfully and IGVM file is correctly parsed. + */ + uint64_t fw_image_addr; + uint64_t fw_image_size; + + /* + * address + size of opaque blob. The guest can use this to pass on + * information, for example which memory region the linux kernel has been + * loaded to. writable, will be kept intact on firmware update. + */ + uint64_t opaque_addr; + uint64_t opaque_size; + +} VMLaunchUpdate; + +#endif diff --git a/include/system/igvm-internal.h b/include/system/igvm-internal.h index 7eb3792ed8..9e9fa1d9af 100644 --- a/include/system/igvm-internal.h +++ b/include/system/igvm-internal.h @@ -18,6 +18,11 @@ #include "system/confidential-guest-support.h" #include +typedef struct IgvmMemoryRegion { + QTAILQ_ENTRY(IgvmMemoryRegion) next; + MemoryRegion *mr; +} IgvmMemoryRegion; + struct IgvmCfg { Object parent_obj; @@ -29,6 +34,7 @@ struct IgvmCfg { char *filename; IgvmHandle file; ResettableState reset_state; + QTAILQ_HEAD(, IgvmMemoryRegion) memory_regions; }; typedef struct QIgvmParameterData { @@ -43,7 +49,7 @@ typedef struct QIgvmParameterData { * file. */ struct QIgvm { - IgvmHandle file; + IgvmCfg *cfg; MachineState *machine_state; ConfidentialGuestSupportClass *cgsc; uint32_t compatibility_mask; diff --git a/include/system/igvm.h b/include/system/igvm.h index 64d3542311..e219f1a4ed 100644 --- a/include/system/igvm.h +++ b/include/system/igvm.h @@ -20,6 +20,7 @@ typedef struct QIgvm QIgvm; int qigvm_process_file(IgvmCfg *igvm, MachineState *machine_state, bool onlyVpContext, Error **errp); +void qigvm_cleanup_memory(IgvmCfg *igvm); /* x86 native */ int qigvm_x86_get_mem_map_entry(int index, diff --git a/include/system/memory.h b/include/system/memory.h index 47a0e06fbf..1dc761058f 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -1085,6 +1085,8 @@ void memory_region_ref(MemoryRegion *mr); */ void memory_region_unref(MemoryRegion *mr); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(MemoryRegion, memory_region_unref) + /** * memory_region_init_io: Initialize an I/O memory region. * @@ -2666,6 +2668,39 @@ void address_space_register_map_client(AddressSpace *as, QEMUBH *bh); void address_space_unregister_map_client(AddressSpace *as, QEMUBH *bh); /* Internal functions, part of the implementation of address_space_read. */ + +/** + * qemu_ram_move: move data from or to ramblock + * + * @dst: destination where the data is moved to + * @src: source where the data is moved from + * @n: length of data to be moved + * + * Move @n bytes from @src to @dst, the memory areas may overlap. This + * provides the same semantics as memmove(), plus an additional stronger + * guarantee: if @n is 1, 2 or 4 or 8 bytes, and @src and @dst are both + * naturally aligned for that access size, then both the load and the store + * will be done as a single atomic access (with the semantics of + * qatomic_read() and qatomic_set()). + * + * This is the underlying function that we use to implement accesses by + * a guest vCPU or a device DMA operation to a ram block. The atomic + * guarantee is needed for two major cases: (A) When the ram block is + * backed by a PCI BAR passed through from a host device (and so it might + * be hardware registers that must be accessed exactly once at the right + * width); (B) When an emulated device updates a data structure shared in + * guest memory with guest software (e.g. a network device's set of tx and + * rx descriptor blocks), if a write to memory is accidentally performed + * multiple times then it can break the guest code when it busy polls the + * guest memory. + * + * We don't attempt to perform the exact access when it would be unaligned + * because this can't be done on all host architectures. Although this is + * strictly speaking not doing what would happen on real hardware, we don't + * think there are going to be situations where that matters in practice. + */ +void qemu_ram_move(void *dst, const void *src, size_t n); + MemTxResult address_space_read_full(const AddressSpace *as, hwaddr addr, MemTxAttrs attrs, void *buf, hwaddr len); MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, @@ -2683,15 +2718,8 @@ static inline bool memory_region_supports_direct_access(const MemoryRegion *mr) if (memory_region_is_romd(mr)) { return true; } - if (!memory_region_is_ram(mr)) { - return false; - } - /* - * RAM DEVICE regions can be accessed directly using memcpy, but it might - * be MMIO and access using mempy can be wrong (e.g., using instructions not - * intended for MMIO access). So we treat this as IO. - */ - return !memory_region_is_ram_device(mr); + + return memory_region_is_ram(mr); } static inline bool memory_access_is_direct(const MemoryRegion *mr, @@ -2739,7 +2767,7 @@ MemTxResult address_space_read(const AddressSpace *as, hwaddr addr, mr = flatview_translate(fv, addr, &addr1, &l, false, attrs); if (len == l && memory_access_is_direct(mr, false, attrs)) { ptr = qemu_map_ram_ptr(mr->ram_block, addr1); - memcpy(buf, ptr, len); + qemu_ram_move(buf, ptr, len); } else { result = flatview_read_continue(fv, addr, attrs, buf, len, addr1, l, mr); diff --git a/include/system/mshv.h b/include/system/mshv.h index 46fe3fcebc..f98ddff9b5 100644 --- a/include/system/mshv.h +++ b/include/system/mshv.h @@ -41,9 +41,11 @@ extern bool mshv_allowed; #define mshv_enabled() (mshv_allowed) #define mshv_msi_via_irqfd_enabled() mshv_enabled() +#define mshv_irqchip_in_kernel() mshv_enabled() #else /* CONFIG_MSHV_IS_POSSIBLE */ #define mshv_enabled() false #define mshv_msi_via_irqfd_enabled() mshv_enabled() +#define mshv_irqchip_in_kernel() mshv_enabled() #endif #define TYPE_MSHV_ACCEL ACCEL_CLASS_NAME("mshv") @@ -55,6 +57,9 @@ DECLARE_INSTANCE_CHECKER(MshvState, MSHV_STATE, extern MshvState *mshv_state; +/* clock (partition reference time) */ +void mshv_clock_init(void); + /* interrupt */ int mshv_request_interrupt(MshvState *mshv_state, uint32_t interrupt_type, uint32_t vector, uint32_t vp_index, bool logical_destination_mode, diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h index b91c4d661a..3dffe3c5fb 100644 --- a/include/system/mshv_int.h +++ b/include/system/mshv_int.h @@ -98,6 +98,8 @@ int mshv_get_generic_regs(CPUState *cpu, hv_register_assoc *assocs, size_t n_regs); int mshv_arch_store_vcpu_state(const CPUState *cpu); int mshv_arch_load_vcpu_state(CPUState *cpu); +int mshv_arch_set_partition_msrs(const CPUState *cpu); +int mshv_arch_set_mp_state(const CPUState *cpu); void mshv_arch_init_vcpu(CPUState *cpu); void mshv_arch_destroy_vcpu(CPUState *cpu); void mshv_arch_amend_proc_features( @@ -105,10 +107,16 @@ void mshv_arch_amend_proc_features( void mshv_arch_disable_partition_proc_features( union hv_partition_processor_features *disabled_features); int mshv_arch_post_init_vm(int vm_fd); - +int mshv_get_vp_state(int cpu_fd, struct mshv_get_set_vp_state *state); +int mshv_set_vp_state(int cpu_fd, const struct mshv_get_set_vp_state *state); typedef struct mshv_root_hvcall mshv_root_hvcall; int mshv_hvcall(int fd, const mshv_root_hvcall *args); +/* apic */ +int mshv_init_lint(CPUState *cpu); +int mshv_set_lapic(const CPUState *cpu); +int mshv_get_lapic(CPUState *cpu); + /* memory */ typedef struct MshvMemoryRegion { uint64_t guest_phys_addr; @@ -132,4 +140,13 @@ int mshv_init_msrs(const CPUState *cpu); int mshv_get_msrs(CPUState *cpu); int mshv_set_msrs(const CPUState *cpu); +/* synic */ +int mshv_get_simp(int cpu_fd, uint8_t *page); +int mshv_set_simp(int cpu_fd, const uint8_t *page); +int mshv_get_siefp(int cpu_fd, uint8_t *page); +int mshv_set_siefp(int cpu_fd, const uint8_t *page); +bool mshv_synic_enabled(const CPUState *cpu); +int mshv_get_synthetic_timers(int cpu_fd, uint8_t *state); +int mshv_set_synthetic_timers(int cpu_fd, const uint8_t *state); + #endif diff --git a/include/system/rng.h b/include/system/rng.h index e383f87d20..a2667d75ef 100644 --- a/include/system/rng.h +++ b/include/system/rng.h @@ -86,4 +86,18 @@ void rng_backend_request_entropy(RngBackend *s, size_t size, * deleted. */ void rng_backend_finalize_request(RngBackend *s, RngRequest *req); + +/** + * rng_backend_cancel_requests: + * @s: the backend that created the request + * @receive_entropy: the function invoked when entropy is available + * @opaque: data passed to @receive_entropy + * + * This function is used by the front-end to cancel all requests to a + * given backend. Requests to cancel are identified by the receive_entropy + * function and the data passed to the function. + */ +void rng_backend_cancel_requests(RngBackend *s, + EntropyReceiveFunc *receive_entropy, + const void *opaque); #endif diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h index 1fe342db0d..9b321f959c 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -163,7 +163,10 @@ void tcg_gen_smin_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); void tcg_gen_smax_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); void tcg_gen_umin_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); void tcg_gen_umax_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_ussub_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); void tcg_gen_abs_i32(TCGv_i32, TCGv_i32); +void tcg_gen_revbit8_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_revbit32_i32(TCGv_i32 ret, TCGv_i32 arg); /* Replicate a value of size @vece from @in to all the lanes in @out */ void tcg_gen_dup_i32(unsigned vece, TCGv_i32 out, TCGv_i32 in); @@ -274,7 +277,11 @@ void tcg_gen_smin_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); void tcg_gen_smax_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); void tcg_gen_umin_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); void tcg_gen_umax_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_ussub_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); void tcg_gen_abs_i64(TCGv_i64, TCGv_i64); +void tcg_gen_revbit8_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_revbit32_i64(TCGv_i64 ret, TCGv_i64 arg, int flags); +void tcg_gen_revbit64_i64(TCGv_i64 ret, TCGv_i64 arg); /* Replicate a value of size @vece from @in to all the lanes in @out */ void tcg_gen_dup_i64(unsigned vece, TCGv_i64 out, TCGv_i64 in); diff --git a/include/tcg/tcg-op.h b/include/tcg/tcg-op.h index 96a5af1a29..3721164236 100644 --- a/include/tcg/tcg-op.h +++ b/include/tcg/tcg-op.h @@ -115,6 +115,10 @@ typedef TCGv_i64 TCGv; #define tcg_gen_bswap_tl tcg_gen_bswap64_i64 #define tcg_gen_hswap_tl tcg_gen_hswap_i64 #define tcg_gen_wswap_tl tcg_gen_wswap_i64 +#define tcg_gen_revbit8_tl tcg_gen_revbit8_i64 +#define tcg_gen_revbit32_tl tcg_gen_revbit32_i64 +#define tcg_gen_revbit64_tl tcg_gen_revbit64_i64 +#define tcg_gen_revbit_tl tcg_gen_revbit64_i64 #define tcg_gen_concat_tl_i64 tcg_gen_concat32_i64 #define tcg_gen_extr_i64_tl tcg_gen_extr32_i64 #define tcg_gen_andc_tl tcg_gen_andc_i64 @@ -149,6 +153,7 @@ typedef TCGv_i64 TCGv; #define tcg_gen_umin_tl tcg_gen_umin_i64 #define tcg_gen_smax_tl tcg_gen_smax_i64 #define tcg_gen_umax_tl tcg_gen_umax_i64 +#define tcg_gen_ussub_tl tcg_gen_ussub_i64 #define tcg_gen_atomic_cmpxchg_tl tcg_gen_atomic_cmpxchg_i64 #define tcg_gen_atomic_xchg_tl tcg_gen_atomic_xchg_i64 #define tcg_gen_atomic_fetch_add_tl tcg_gen_atomic_fetch_add_i64 @@ -234,6 +239,9 @@ typedef TCGv_i64 TCGv; #define tcg_gen_bswap32_tl(D, S, F) tcg_gen_bswap32_i32(D, S) #define tcg_gen_bswap_tl tcg_gen_bswap32_i32 #define tcg_gen_hswap_tl tcg_gen_hswap_i32 +#define tcg_gen_revbit8_tl tcg_gen_revbit8_i32 +#define tcg_gen_revbit32_tl(D, S, F) tcg_gen_revbit32_i32(D, S) +#define tcg_gen_revbit_tl tcg_gen_revbit32_i32 #define tcg_gen_concat_tl_i64 tcg_gen_concat_i32_i64 #define tcg_gen_extr_i64_tl tcg_gen_extr_i64_i32 #define tcg_gen_andc_tl tcg_gen_andc_i32 @@ -268,6 +276,7 @@ typedef TCGv_i64 TCGv; #define tcg_gen_umin_tl tcg_gen_umin_i32 #define tcg_gen_smax_tl tcg_gen_smax_i32 #define tcg_gen_umax_tl tcg_gen_umax_i32 +#define tcg_gen_ussub_tl tcg_gen_ussub_i32 #define tcg_gen_atomic_cmpxchg_tl tcg_gen_atomic_cmpxchg_i32 #define tcg_gen_atomic_xchg_tl tcg_gen_atomic_xchg_i32 #define tcg_gen_atomic_fetch_add_tl tcg_gen_atomic_fetch_add_i32 diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 61f1c28858..f3a81d5d7f 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -79,6 +79,9 @@ DEF(or, 1, 2, 0, TCG_OPF_INT) DEF(orc, 1, 2, 0, TCG_OPF_INT) DEF(rems, 1, 2, 0, TCG_OPF_INT) DEF(remu, 1, 2, 0, TCG_OPF_INT) +DEF(revbit8, 1, 1, 0, TCG_OPF_INT) +DEF(revbit32, 1, 1, 1, TCG_OPF_INT) +DEF(revbit64, 1, 1, 0, TCG_OPF_INT) DEF(rotl, 1, 2, 0, TCG_OPF_INT) DEF(rotr, 1, 2, 0, TCG_OPF_INT) DEF(sar, 1, 2, 0, TCG_OPF_INT) @@ -86,11 +89,15 @@ DEF(setcond, 1, 2, 1, TCG_OPF_INT) DEF(sextract, 1, 1, 2, TCG_OPF_INT) DEF(shl, 1, 2, 0, TCG_OPF_INT) DEF(shr, 1, 2, 0, TCG_OPF_INT) +DEF(smax, 1, 2, 0, TCG_OPF_INT) +DEF(smin, 1, 2, 0, TCG_OPF_INT) DEF(st8, 0, 2, 1, TCG_OPF_INT) DEF(st16, 0, 2, 1, TCG_OPF_INT) DEF(st32, 0, 2, 1, TCG_OPF_INT) DEF(st, 0, 2, 1, TCG_OPF_INT) DEF(sub, 1, 2, 0, TCG_OPF_INT) +DEF(umax, 1, 2, 0, TCG_OPF_INT) +DEF(umin, 1, 2, 0, TCG_OPF_INT) DEF(xor, 1, 2, 0, TCG_OPF_INT) DEF(addco, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_OUT) diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h index 405ddd9125..679c79eacd 100644 --- a/include/ui/egl-helpers.h +++ b/include/ui/egl-helpers.h @@ -9,7 +9,7 @@ #include "ui/console.h" #include "ui/shader.h" -extern EGLDisplay *qemu_egl_display; +extern EGLDisplay qemu_egl_display; extern EGLConfig qemu_egl_config; extern DisplayGLMode qemu_egl_mode; extern bool qemu_egl_angle_d3d; diff --git a/include/user/guest-host.h b/include/user/guest-host.h index 506efc097e..f136f81f5d 100644 --- a/include/user/guest-host.h +++ b/include/user/guest-host.h @@ -80,6 +80,7 @@ static inline bool guest_range_valid_untagged_vaddr(vaddr start, vaddr len) }) #ifdef COMPILING_PER_TARGET +#include "exec/abi_ptr.h" /* * These functions take the guest virtual address as an abi_ptr. This diff --git a/io/channel.c b/io/channel.c index 2853dadb68..620256f310 100644 --- a/io/channel.c +++ b/io/channel.c @@ -905,8 +905,9 @@ void qio_channel_wait(QIOChannel *ioc, g_main_context_unref(ctxt); } -void qio_channel_wait_cond(QIOChannel *ioc, - GIOCondition condition) +void coroutine_mixed_fn +qio_channel_wait_cond(QIOChannel *ioc, + GIOCondition condition) { if (qemu_in_coroutine()) { qio_channel_yield(ioc, condition); diff --git a/job.c b/job.c index e747908472..d7220aaf30 100644 --- a/job.c +++ b/job.c @@ -629,7 +629,14 @@ static void coroutine_fn job_pause_point_locked(Job *job) ? JOB_STATUS_STANDBY : JOB_STATUS_PAUSED); job->paused = true; - job_do_yield_locked(job, -1); + /* + * Stay paused across back-to-back pause requests: a transient + * paused == false while pause_count > 0 would be observed as + * "not paused" by job_set_aio_context() and other drain consumers. + */ + do { + job_do_yield_locked(job, -1); + } while (job_should_pause_locked(job) && !job_is_cancelled_locked(job)); job->paused = false; job_state_transition_locked(job, status); } diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c index 64e25a04e3..48be49580a 100644 --- a/linux-user/aarch64/elfload.c +++ b/linux-user/aarch64/elfload.c @@ -177,6 +177,7 @@ abi_ulong get_elf_hwcap(CPUState *cs) GET_FEATURE_ID(aa64_ssve_fexpa, ARM_HWCAP_A64_SME_SFEXPA); GET_FEATURE_ID(aa64_fprcvt, ARM_HWCAP_A64_FPRCVT); GET_FEATURE_ID(aa64_sme_mop4, ARM_HWCAP_A64_SME_SMOP4); + GET_FEATURE_ID(aa64_sme_tmop, ARM_HWCAP_A64_SME_STMOP); return hwcaps; } diff --git a/linux-user/alpha/elfload.c b/linux-user/alpha/elfload.c index 7be9e466b6..c2517516ad 100644 --- a/linux-user/alpha/elfload.c +++ b/linux-user/alpha/elfload.c @@ -32,3 +32,13 @@ const char *get_elf_cpu_model(uint32_t eflags) { return "ev67"; } + +void elf_core_copy_fpregs(target_elf_fpregset_t *r, const CPUAlphaState *env) +{ + int i; + + for (i = 0; i < 31; i++) { + r->fpr[i] = tswap64(env->fir[i]); + } + r->fpcr = tswap64(cpu_alpha_load_fpcr((CPUAlphaState *)env)); +} diff --git a/linux-user/alpha/target_elf.h b/linux-user/alpha/target_elf.h index dd90c6f783..5efe7f3174 100644 --- a/linux-user/alpha/target_elf.h +++ b/linux-user/alpha/target_elf.h @@ -19,6 +19,17 @@ * r0-r30 at indices 0-30, pc at 31, ps at 32. * r31 (hardwired zero) is not stored; pc occupies index 31. */ +/* + * The floating-point note holds $f0 through $f30 and then the control + * register in the slot $f31 would occupy; $f31 reads as zero. + */ +#define HAVE_ELF_CORE_FPREGS 1 + +typedef struct target_elf_fpregset_t { + uint64_t fpr[31]; /* $f0-$f30 */ + uint64_t fpcr; /* the slot for $f31 */ +} target_elf_fpregset_t; + typedef struct target_elf_gregset_t { abi_ulong regs[31]; /* integer registers r0-r30 [0..30] */ abi_ulong pc; /* program counter [31] */ diff --git a/linux-user/elfload.c b/linux-user/elfload.c index e7c56af8ed..88508deac5 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1887,6 +1887,17 @@ static void fill_elf_note_phdr(struct elf_phdr *phdr, size_t sz, off_t offset) bswap_phdr(phdr, 1); } +#ifdef HAVE_ELF_CORE_FPREGS +static void fill_fpregset_note(void *data, CPUState *cpu) +{ + /* Fill locally and copy: note memory is only aligned to 4. */ + target_elf_fpregset_t fpregs = {}; + + elf_core_copy_fpregs(&fpregs, cpu_env(cpu)); + memcpy(data, &fpregs, sizeof(fpregs)); +} +#endif + static void fill_prstatus_note(void *data, CPUState *cpu, int signr) { /* @@ -2166,6 +2177,9 @@ static int elf_core_dump(int signr, const CPUArchState *env) offset += size_note("CORE", ts->info->auxv_len); offset += size_note("CORE", sizeof(struct target_elf_prpsinfo)); offset += size_note("CORE", sizeof(struct target_elf_prstatus)) * cpus; +#ifdef HAVE_ELF_CORE_FPREGS + offset += size_note("CORE", sizeof(target_elf_fpregset_t)) * cpus; +#endif note_size = offset - note_offset; data_offset = TARGET_PAGE_ALIGN(offset); @@ -2222,6 +2236,11 @@ static int elf_core_dump(int signr, const CPUArchState *env) dptr = fill_note(&hptr, NT_PRSTATUS, "CORE", sizeof(struct target_elf_prstatus)); fill_prstatus_note(dptr, cpu_iter, cpu_iter == cpu ? signr : 0); +#ifdef HAVE_ELF_CORE_FPREGS + dptr = fill_note(&hptr, NT_FPREGSET, "CORE", + sizeof(target_elf_fpregset_t)); + fill_fpregset_note(dptr, cpu_iter); +#endif } if (dump_write(fd, header, data_offset) < 0) { diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c index 0958c51fbb..d7f73439db 100644 --- a/linux-user/hexagon/cpu_loop.c +++ b/linux-user/hexagon/cpu_loop.c @@ -76,6 +76,11 @@ void cpu_loop(CPUHexagonState *env) force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->gpr[HEX_REG_PC]); break; + case HEX_CAUSE_MISALIGNED_LOAD: + case HEX_CAUSE_MISALIGNED_STORE: + force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN, + env->gpr[HEX_REG_PC]); + break; default: EXCP_DUMP(env, "\nqemu: unhandled CPU precise exception " "cause code 0x%x - aborting\n", diff --git a/linux-user/hppa/elfload.c b/linux-user/hppa/elfload.c index ff4301b2ed..dd5b0b380e 100644 --- a/linux-user/hppa/elfload.c +++ b/linux-user/hppa/elfload.c @@ -17,6 +17,13 @@ const char *get_elf_platform(CPUState *cs) return "PARISC"; } +void elf_core_copy_fpregs(target_elf_fpregset_t *r, const CPUArchState *env) +{ + for (int i = 0; i < 32; i++) { + r->fpr[i] = tswap64(env->fr[i]); + } +} + void elf_core_copy_regs(target_elf_gregset_t *r, const CPUArchState *env) { int i; diff --git a/linux-user/hppa/target_elf.h b/linux-user/hppa/target_elf.h index 22547b1437..4357873aff 100644 --- a/linux-user/hppa/target_elf.h +++ b/linux-user/hppa/target_elf.h @@ -39,4 +39,13 @@ typedef struct target_elf_gregset_t { #define STACK_ALIGNMENT 64 #define VDSO_HEADER "vdso.c.inc" +#define HAVE_ELF_CORE_FPREGS 1 + +/* + * Matches the kernel's elf_fpregset_t (ELF_NFPREG = 32): fr0-fr31. + */ +typedef struct target_elf_fpregset_t { + uint64_t fpr[32]; +} target_elf_fpregset_t; + #endif diff --git a/linux-user/loader.h b/linux-user/loader.h index da9ad28db5..5ddd140276 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -109,6 +109,9 @@ bool init_guest_commpage(void); struct target_elf_gregset_t; void elf_core_copy_regs(struct target_elf_gregset_t *, const CPUArchState *); +/* Only defined by a target whose target_elf.h sets HAVE_ELF_CORE_FPREGS. */ +struct target_elf_fpregset_t; +void elf_core_copy_fpregs(struct target_elf_fpregset_t *, const CPUArchState *); typedef struct { const uint8_t *image; diff --git a/linux-user/mips/elfload.c b/linux-user/mips/elfload.c index ce2c4514f3..1d62ae0a4d 100644 --- a/linux-user/mips/elfload.c +++ b/linux-user/mips/elfload.c @@ -130,6 +130,14 @@ const char *get_elf_base_platform(CPUState *cs) #undef MATCH_PLATFORM_INSN +void elf_core_copy_fpregs(target_elf_fpregset_t *r, const CPUMIPSState *env) +{ + for (int i = 0; i < 32; i++) { + r->fpr[i] = tswap64(env->active_fpu.fpr[i].d); + } + r->fcsr = tswap32(env->active_fpu.fcr31); +} + /* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ #ifndef TARGET_MIPS64 void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMIPSState *env) diff --git a/linux-user/mips/target_elf.h b/linux-user/mips/target_elf.h index 157306f7a0..426f905d3a 100644 --- a/linux-user/mips/target_elf.h +++ b/linux-user/mips/target_elf.h @@ -26,4 +26,17 @@ typedef struct target_elf_gregset_t { }; } target_elf_gregset_t; +#define HAVE_ELF_CORE_FPREGS 1 + +/* + * Matches the kernel's elf_fpregset_t (ELF_NFPREG = 33): + * fpr[0..31] hold f0-f31; fcsr occupies the low 32 bits of slot 32. + * pad rounds the struct to 33 × 8 bytes = 264 bytes. + */ +typedef struct target_elf_fpregset_t { + uint64_t fpr[32]; + uint32_t fcsr; + uint32_t pad; +} target_elf_fpregset_t; + #endif diff --git a/linux-user/mips64/target_elf.h b/linux-user/mips64/target_elf.h index 061471a0f1..efb5d97563 100644 --- a/linux-user/mips64/target_elf.h +++ b/linux-user/mips64/target_elf.h @@ -32,4 +32,17 @@ typedef struct target_elf_gregset_t { }; } target_elf_gregset_t; +#define HAVE_ELF_CORE_FPREGS 1 + +/* + * Matches the kernel's elf_fpregset_t (ELF_NFPREG = 33): + * fpr[0..31] hold f0-f31; fcsr occupies the low 32 bits of slot 32. + * pad rounds the struct to 33 × 8 bytes = 264 bytes. + */ +typedef struct target_elf_fpregset_t { + uint64_t fpr[32]; + uint32_t fcsr; + uint32_t pad; +} target_elf_fpregset_t; + #endif diff --git a/linux-user/riscv/elfload.c b/linux-user/riscv/elfload.c index afe103a631..1bc8beacb1 100644 --- a/linux-user/riscv/elfload.c +++ b/linux-user/riscv/elfload.c @@ -11,6 +11,15 @@ const char *get_elf_cpu_model(uint32_t eflags) return "max"; } +void elf_core_copy_fpregs(target_elf_fpregset_t *r, const CPURISCVState *env) +{ + for (int i = 0; i < 32; i++) { + r->fpr[i] = tswap64(env->fpr[i]); + } + r->fcsr = tswap32(((uint32_t)env->frm << FSR_RD_SHIFT) | + (riscv_cpu_get_fflags((CPURISCVState *)env) << FSR_AEXC_SHIFT)); +} + void elf_core_copy_regs(target_elf_gregset_t *r, const CPURISCVState *env) { r->pc = tswapal(env->pc); diff --git a/linux-user/riscv/target_elf.h b/linux-user/riscv/target_elf.h index 859f726578..185b81a2db 100644 --- a/linux-user/riscv/target_elf.h +++ b/linux-user/riscv/target_elf.h @@ -27,4 +27,15 @@ typedef struct target_elf_gregset_t { abi_ulong regs[31]; } target_elf_gregset_t; +#define HAVE_ELF_CORE_FPREGS 1 + +/* + * Matches struct __riscv_d_ext_state from uapi/asm/ptrace.h: + * f0-f31 as 64-bit values followed by fcsr. + */ +typedef struct target_elf_fpregset_t { + uint64_t fpr[32]; + uint32_t fcsr; +} target_elf_fpregset_t; + #endif diff --git a/linux-user/sh4/cpu_loop.c b/linux-user/sh4/cpu_loop.c index ee2958d0d9..0815b9c9bf 100644 --- a/linux-user/sh4/cpu_loop.c +++ b/linux-user/sh4/cpu_loop.c @@ -64,6 +64,13 @@ void cpu_loop(CPUSH4State *env) cpu_exec_step_atomic(cs); arch_interrupt = false; break; + case 0x180: + /* Illegal instruction */ + /* fallthrough */ + case 0x1a0: + /* Illegal instruction in delay slot */ + force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->pc); + break; default: fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, 0); diff --git a/linux-user/sh4/elfload.c b/linux-user/sh4/elfload.c index f03ce49e7d..41601626ca 100644 --- a/linux-user/sh4/elfload.c +++ b/linux-user/sh4/elfload.c @@ -52,6 +52,16 @@ abi_ulong get_elf_hwcap(CPUState *cs) return hwcap; } +void elf_core_copy_fpregs(target_elf_fpregset_t *r, const CPUSH4State *env) +{ + for (int i = 0; i < 16; i++) { + r->fpregs[i] = tswap32(env->fregs[i]); + r->xfpregs[i] = tswap32(env->fregs[16 + i]); + } + r->fpscr = tswap32(env->fpscr); + r->fpul = tswap32(env->fpul); +} + void elf_core_copy_regs(target_elf_gregset_t *r, const CPUSH4State *env) { for (int i = 0; i < 16; i++) { diff --git a/linux-user/sh4/signal.c b/linux-user/sh4/signal.c index 00290d6e40..047174ac8f 100644 --- a/linux-user/sh4/signal.c +++ b/linux-user/sh4/signal.c @@ -109,7 +109,7 @@ static void unwind_gusa(CPUSH4State *regs) the SP, otherwise we would be pushing the signal context to invalid memory. */ regs->gregs[15] = regs->gregs[1]; - } else if (regs->flags & TB_FLAG_DELAY_SLOT) { + } else if (regs->flags & (TB_FLAG_DELAY_SLOT | TB_FLAG_DELAY_SLOT_COND)) { /* If we are in a delay slot, push the previous instruction. */ regs->pc -= 2; } @@ -206,6 +206,8 @@ void setup_frame(int sig, struct target_sigaction *ka, __put_user(set->sig[i + 1], &frame->extramask[i]); } + regs->fpscr = FPSCR_PR; + /* Set up to return from userspace. If provided, use a stub already in userspace. */ if (ka->sa_flags & TARGET_SA_RESTORER) { @@ -258,6 +260,8 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); } + regs->fpscr = FPSCR_PR; + /* Set up to return from userspace. If provided, use a stub already in userspace. */ if (ka->sa_flags & TARGET_SA_RESTORER) { diff --git a/linux-user/sh4/target_elf.h b/linux-user/sh4/target_elf.h index 3fcb63d409..5923022036 100644 --- a/linux-user/sh4/target_elf.h +++ b/linux-user/sh4/target_elf.h @@ -25,4 +25,16 @@ typedef struct target_elf_gregset_t { struct target_pt_regs pt; } target_elf_gregset_t; +#define HAVE_ELF_CORE_FPREGS 1 + +/* + * Matches struct user_fpu_struct from arch/sh/include/asm/user.h. + */ +typedef struct target_elf_fpregset_t { + uint32_t fpregs[16]; + uint32_t xfpregs[16]; + uint32_t fpscr; + uint32_t fpul; +} target_elf_fpregset_t; + #endif diff --git a/linux-user/sh4/target_mman.h b/linux-user/sh4/target_mman.h index dd9016081e..2d05837c2f 100644 --- a/linux-user/sh4/target_mman.h +++ b/linux-user/sh4/target_mman.h @@ -1,6 +1,6 @@ /* arch/sh/include/asm/processor_32.h */ #define TASK_UNMAPPED_BASE \ - TARGET_PAGE_ALIGN((1u << TARGET_VIRT_ADDR_SPACE_BITS) / 3) + TARGET_PAGE_ALIGN((1ull << TARGET_VIRT_ADDR_SPACE_BITS) / 3) /* arch/sh/include/asm/elf.h */ #define ELF_ET_DYN_BASE (TASK_UNMAPPED_BASE * 2) diff --git a/linux-user/sparc/cpu_loop.c b/linux-user/sparc/cpu_loop.c index 0aacda9448..eaf388c167 100644 --- a/linux-user/sparc/cpu_loop.c +++ b/linux-user/sparc/cpu_loop.c @@ -282,6 +282,16 @@ void cpu_loop (CPUSPARCState *env) break; case TT_TRAP + 0x6f: flush_windows(env); + /* + * If we have a pending signal, sparc64_set_context() may + * return early without changing register state (like a + * syscall that returns -QEMU_ERESTARTSYS). We will then + * take the pending signal via process_pending_signals() + * below and eventually re-execute the trap. We don't need + * the function to return a different value for the + * "restart" case because this main loop code does the + * same thing in both cases. + */ sparc64_set_context(env); break; #endif diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c index ba692c3123..4baf983ed8 100644 --- a/linux-user/sparc/signal.c +++ b/linux-user/sparc/signal.c @@ -594,6 +594,27 @@ void sparc64_set_context(CPUSPARCState *env) unsigned int i; unsigned char fenab; + if (env->regwptr[WREG_O1]) { + /* + * We're going to set the signal mask; we need to call + * block_signals() first, so that process_pending_signals() is + * guaranteed to run after the mask change. Without this, a + * guest signal that is pending-and-blocked at setcontext time + * is left undelivered even after its mask bit is cleared, + * because signal_pending stays 0 and the post-trap + * process_pending_signals() loop never enters. + * + * If block_signals() returns true, this means we have a + * pending signal that we could take now; we return early so + * the cpu_loop takes that signal. Eventually the guest will + * re-execute the trap insn and we'll come back here to have + * another go at set_context. This is the same way that + * do_sigprocmask() handles setting the signal mask. + */ + if (block_signals()) { + return; + } + } ucp_addr = env->regwptr[WREG_O0]; if (!lock_user_struct(VERIFY_READ, ucp, ucp_addr, 1)) { goto do_sigsegv; @@ -619,15 +640,6 @@ void sparc64_set_context(CPUSPARCState *env) } } target_to_host_sigset_internal(&set, &target_set); - /* - * set_sigmask() requires the caller to have first called - * block_signals() so that process_pending_signals() is guaranteed - * to run after the mask change. Without this, a guest signal that - * is pending-and-blocked at setcontext time is left undelivered - * even after its mask bit is cleared, because signal_pending stays - * 0 and the post-trap process_pending_signals() loop never enters. - */ - block_signals(); set_sigmask(&set); } env->pc = pc; diff --git a/linux-user/strace.c b/linux-user/strace.c index 3a81cc95f4..bc43a95a77 100644 --- a/linux-user/strace.c +++ b/linux-user/strace.c @@ -4344,7 +4344,7 @@ print_statx(CPUArchState *cpu_env, const struct syscallname *name, } #endif -#if defined(TARGET_NR_fsconfig) && defined(NR_fsconfig) +#if defined(TARGET_NR_fsconfig) && defined(__NR_fsconfig) && defined(FSCONFIG_SET_FLAG) static void print_fsconfig_cmd_name(int cmd) { diff --git a/linux-user/strace.list b/linux-user/strace.list index e363892e0a..e952f15d20 100644 --- a/linux-user/strace.list +++ b/linux-user/strace.list @@ -1725,7 +1725,7 @@ #ifdef TARGET_NR_fsopen { TARGET_NR_fsopen, "fsopen", "%s(%s,%d)", NULL, NULL }, #endif -#if defined(TARGET_NR_fsconfig) && defined(NR_fsconfig) +#if defined(TARGET_NR_fsconfig) && defined(__NR_fsconfig) && defined(FSCONFIG_SET_FLAG) { TARGET_NR_fsconfig, "fsconfig", NULL, print_fsconfig, NULL }, #endif #ifdef TARGET_NR_fsmount @@ -1737,3 +1737,6 @@ #ifdef TARGET_NR_fspick { TARGET_NR_fspick, "fspick", "%s(%d,%s,%d)", NULL, NULL }, #endif +#ifdef TARGET_NR_mount_setattr +{ TARGET_NR_mount_setattr, "mount_setattr", "%s(%d,%s,%d,%p,%d)", NULL, NULL }, +#endif diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 3da5530d42..cfa68dfbdb 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -4216,26 +4216,20 @@ static inline abi_long do_semtimedop(int semid, } #endif +#define target_time64_t abi_ullong +#define target_swap_time64(x) tswap64(x) + struct target_msqid_ds { struct target_ipc_perm msg_perm; - abi_ulong msg_stime; -#if TARGET_ABI_BITS == 32 - abi_ulong __unused1; -#endif - abi_ulong msg_rtime; -#if TARGET_ABI_BITS == 32 - abi_ulong __unused2; -#endif - abi_ulong msg_ctime; -#if TARGET_ABI_BITS == 32 - abi_ulong __unused3; -#endif + target_time64_t msg_stime; + target_time64_t msg_rtime; + target_time64_t msg_ctime; abi_ulong __msg_cbytes; abi_ulong msg_qnum; abi_ulong msg_qbytes; - abi_ulong msg_lspid; - abi_ulong msg_lrpid; + abi_int msg_lspid; + abi_int msg_lrpid; abi_ulong __unused4; abi_ulong __unused5; }; @@ -4249,14 +4243,14 @@ static inline abi_long target_to_host_msqid_ds(struct msqid_ds *host_md, return -TARGET_EFAULT; if (target_to_host_ipc_perm(&(host_md->msg_perm),target_addr)) return -TARGET_EFAULT; - host_md->msg_stime = tswapal(target_md->msg_stime); - host_md->msg_rtime = tswapal(target_md->msg_rtime); - host_md->msg_ctime = tswapal(target_md->msg_ctime); + host_md->msg_stime = target_swap_time64(target_md->msg_stime); + host_md->msg_rtime = target_swap_time64(target_md->msg_rtime); + host_md->msg_ctime = target_swap_time64(target_md->msg_ctime); host_md->__msg_cbytes = tswapal(target_md->__msg_cbytes); host_md->msg_qnum = tswapal(target_md->msg_qnum); host_md->msg_qbytes = tswapal(target_md->msg_qbytes); - host_md->msg_lspid = tswapal(target_md->msg_lspid); - host_md->msg_lrpid = tswapal(target_md->msg_lrpid); + host_md->msg_lspid = tswap32(target_md->msg_lspid); + host_md->msg_lrpid = tswap32(target_md->msg_lrpid); unlock_user_struct(target_md, target_addr, 0); return 0; } @@ -4270,14 +4264,14 @@ static inline abi_long host_to_target_msqid_ds(abi_ulong target_addr, return -TARGET_EFAULT; if (host_to_target_ipc_perm(target_addr,&(host_md->msg_perm))) return -TARGET_EFAULT; - target_md->msg_stime = tswapal(host_md->msg_stime); - target_md->msg_rtime = tswapal(host_md->msg_rtime); - target_md->msg_ctime = tswapal(host_md->msg_ctime); + target_md->msg_stime = target_swap_time64(host_md->msg_stime); + target_md->msg_rtime = target_swap_time64(host_md->msg_rtime); + target_md->msg_ctime = target_swap_time64(host_md->msg_ctime); target_md->__msg_cbytes = tswapal(host_md->__msg_cbytes); target_md->msg_qnum = tswapal(host_md->msg_qnum); target_md->msg_qbytes = tswapal(host_md->msg_qbytes); - target_md->msg_lspid = tswapal(host_md->msg_lspid); - target_md->msg_lrpid = tswapal(host_md->msg_lrpid); + target_md->msg_lspid = tswap32(host_md->msg_lspid); + target_md->msg_lrpid = tswap32(host_md->msg_lrpid); unlock_user_struct(target_md, target_addr, 1); return 0; } @@ -6906,7 +6900,8 @@ static abi_long do_map_shadow_stack(CPUArchState *env, abi_ulong addr, /* Leave an extra empty frame at top-of-stack. */ cap_ptr -= 8; } - cap_val = (cap_ptr & TARGET_PAGE_MASK) | 1; + /* Note the 12 bit field is unaffected by current page size. */ + cap_val = deposit64(cap_ptr, 0, 12, 1); if (put_user_u64(cap_val, cap_ptr)) { /* Allocation succeeded above. */ g_assert_not_reached(); @@ -9740,7 +9735,14 @@ _syscall5(int, sys_move_mount, int, __from_dfd, const char *, __from_pathname, int, __to_dfd, const char *, __to_pathname, unsigned int, flag) #endif -#if defined(TARGET_NR_fsopen) && defined(NR_fsopen) +#if defined(TARGET_NR_mount_setattr) && defined(__NR_mount_setattr) +#define __NR_sys_mount_setattr __NR_mount_setattr +_syscall5(int, sys_mount_setattr, int, dfd, const char *, path, + unsigned int, flags, struct mount_attr_ver0 *, uattr, + size_t, usize) +#endif + +#if defined(TARGET_NR_fsopen) && defined(__NR_fsopen) #define __NR_sys_fsopen __NR_fsopen _syscall2(int, sys_fsopen, const char *, fs_name, unsigned int, flags); #define __NR_sys_fsconfig __NR_fsconfig @@ -14485,7 +14487,44 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, return do_map_shadow_stack(cpu_env, arg1, arg2, arg3); #endif -#if defined(TARGET_NR_fsopen) && defined(NR_fsopen) +#if defined(TARGET_NR_mount_setattr) && defined(__NR_mount_setattr) + case TARGET_NR_mount_setattr: + { + struct mount_attr_ver0 attr = {}; + abi_ulong usize = arg5; + + if (usize < sizeof(struct target_mount_attr_ver0)) { + return -TARGET_EINVAL; + } + ret = copy_struct_from_user(&attr, sizeof(attr), arg4, usize); + if (ret) { + if (ret == -TARGET_E2BIG) { + qemu_log_mask(LOG_UNIMP, + "Unimplemented mount_setattr mount_attr " + "size: " TARGET_ABI_FMT_lu "\n", usize); + } + return ret; + } + /* + * MOUNT_ATTR_* and the MS_* propagation flags have the same + * values on all targets, so only byte order needs fixing up. + */ + attr.attr_set = tswap64(attr.attr_set); + attr.attr_clr = tswap64(attr.attr_clr); + attr.propagation = tswap64(attr.propagation); + attr.userns_fd = tswap64(attr.userns_fd); + + p = lock_user_string(arg2); + if (!p) { + return -TARGET_EFAULT; + } + ret = get_errno(sys_mount_setattr(arg1, p, arg3, &attr, + sizeof(attr))); + unlock_user(p, arg2, 0); + } + return ret; +#endif +#if defined(TARGET_NR_fsopen) && defined(__NR_fsopen) case TARGET_NR_fsopen: { p = lock_user_string(arg1); diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 5799769f83..e28853c93b 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2593,7 +2593,9 @@ struct target_drm_i915_getparam { #define FUTEX_PRIVATE_FLAG 128 #define FUTEX_CLOCK_REALTIME 256 +#ifndef FUTEX_CMD_MASK #define FUTEX_CMD_MASK ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME) +#endif #if defined(TARGET_X86_64) #define TARGET_EPOLL_PACKED QEMU_PACKED @@ -2768,6 +2770,19 @@ struct target_open_how_ver0 { abi_ullong mode; abi_ullong resolve; }; +/* from kernel's include/uapi/linux/mount.h */ +struct mount_attr_ver0 { + uint64_t attr_set; + uint64_t attr_clr; + uint64_t propagation; + uint64_t userns_fd; +}; +struct target_mount_attr_ver0 { + abi_ullong attr_set; + abi_ullong attr_clr; + abi_ullong propagation; + abi_ullong userns_fd; +}; #ifndef RESOLVE_NO_MAGICLINKS #define RESOLVE_NO_MAGICLINKS 0x02 #endif diff --git a/meson.build b/meson.build index 164328ded8..6dcfdceb50 100644 --- a/meson.build +++ b/meson.build @@ -4,7 +4,8 @@ project('qemu', ['c'], meson_version: '>=1.5.0', # build.rust_std breaks with older meson, but Rust does not # support old meson anyway - (meson.version().version_compare('>= 1.9') ? ['rust_std=2021', 'build.rust_std=2021'] : []), + (meson.version().version_compare('>= 1.9') ? ['rust_std=2021', 'build.rust_std=2021', + 'rust_nightly=disabled'] : []), version: files('VERSION')) @@ -129,16 +130,11 @@ endif if have_rust rustc_args = [find_program('scripts/rust/rustc_args.py'), - '--rustc-version', rustc.version(), - '--workspace', meson.project_source_root() / 'rust'] + '--workspace', meson.project_source_root()] + rustfmt = find_program('rustfmt', required: false) - rustc_lint_args = run_command(rustc_args, '--lints', - capture: true, check: true).stdout().strip().splitlines() - if get_option('strict_rust_lints') - rustc_lint_args += ['-Dwarnings', '-Funknown_lints'] - endif - + rustc_lint_args = get_option('strict_rust_lints') ? ['-Dwarnings', '-Funknown_lints'] : [] add_project_arguments(rustc_lint_args + ['--cfg', 'MESON'], native: false, language: 'rust') add_project_arguments(rustc_lint_args + ['--cfg', 'MESON'], @@ -602,6 +598,11 @@ if host_os == 'windows' qemu_ldflags += cc.get_supported_link_arguments('-Wl,--no-seh', '-Wl,--nxcompat') qemu_ldflags += cc.get_supported_link_arguments('-Wl,--dynamicbase', '-Wl,--high-entropy-va') endif +if host_os == 'linux' and get_option('werror') and get_option('prefer_static') + # On glibc systems, glib causes warnings about getpwuid, getpwuid_r + # and getpwnam_r not being supported with static linking + qemu_ldflags += cc.get_supported_link_arguments('-Wl,--no-fatal-warnings') +endif if get_option('fuzzing') # Specify a filter to only instrument code that is directly related to @@ -767,6 +768,9 @@ endif add_project_arguments(cc.get_supported_arguments(qemu_common_flags + qemu_cflags + warn_flags), native: false, language: 'c') add_global_link_arguments(qemu_ldflags, native: false, language: all_languages) +if have_rust + add_global_link_arguments(qemu_ldflags, native: false, language: 'rust') +endif if 'cpp' in all_languages add_project_arguments(cxx.get_supported_arguments(qemu_common_flags + qemu_cxxflags), @@ -1783,11 +1787,13 @@ if not get_option('libcbor').auto() or have_system endif gnutls = not_found +gnutls_crypto = not_found gnutls_bug1717_workaround = false if get_option('gnutls').enabled() or (get_option('gnutls').auto() and have_system) gnutls = dependency('gnutls', version: '>=3.7.5', method: 'pkg-config', required: get_option('gnutls')) + gnutls_crypto = gnutls #if gnutls.found() and not get_option('gnutls-bug1717-workaround').disabled() # XXX: when bug 1717 is resolved, add logic to probe for @@ -1811,7 +1817,12 @@ if get_option('nettle').enabled() and get_option('gcrypt').enabled() error('Only one of gcrypt & nettle can be enabled') endif -if not gnutls.found() +# Explicit nettle/gcrypt request, so ignore gnutls for crypto +if get_option('nettle').enabled() or get_option('gcrypt').enabled() + gnutls_crypto = not_found +endif + +if not gnutls_crypto.found() if (not get_option('gcrypt').auto() or have_system) and not get_option('nettle').enabled() gcrypt = dependency('libgcrypt', version: '>=1.9.4', required: get_option('gcrypt')) @@ -2513,6 +2524,7 @@ config_host_data.set('CONFIG_XKBCOMMON', xkbcommon.found()) config_host_data.set('CONFIG_KEYUTILS', keyutils.found()) config_host_data.set('CONFIG_GETTID', has_gettid) config_host_data.set('CONFIG_GNUTLS', gnutls.found()) +config_host_data.set('CONFIG_GNUTLS_CRYPTO', gnutls_crypto.found()) config_host_data.set('CONFIG_GNUTLS_BUG1717_WORKAROUND', gnutls_bug1717_workaround) config_host_data.set('CONFIG_TASN1', tasn1.found()) config_host_data.set('CONFIG_GCRYPT', gcrypt.found()) @@ -4729,7 +4741,7 @@ else endif summary_info += {'Rust support': have_rust} if have_rust - summary_info += {'Rust target': config_host['RUST_TARGET_TRIPLE']} + summary_info += {'Rust target': rust.compiler_target(native: false)} summary_info += {'rustc': ' '.join(rustc.cmd_array())} summary_info += {'rustc version': rustc.version()} summary_info += {'rustdoc': rustdoc} @@ -4865,6 +4877,7 @@ summary_info = {} summary_info += {'TLS priority': get_option('tls_priority')} summary_info += {'GNUTLS support': gnutls} if gnutls.found() + summary_info += {' GNUTLS crypto': gnutls_crypto.found()} summary_info += {' GNUTLS bug 1717 workaround': gnutls_bug1717_workaround } endif summary_info += {'libgcrypt': gcrypt} diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c index cba54e25cd..1b8f39c12b 100644 --- a/migration/block-dirty-bitmap.c +++ b/migration/block-dirty-bitmap.c @@ -812,13 +812,6 @@ static int dirty_bitmap_load_start(QEMUFile *f, DBMLoadState *s) error_report("Bitmap with the same name ('%s') already exists on " "destination", bdrv_dirty_bitmap_name(s->bitmap)); return -EINVAL; - } else { - s->bitmap = bdrv_create_dirty_bitmap(s->bs, granularity, - s->bitmap_name, &local_err); - if (!s->bitmap) { - error_report_err(local_err); - return -EINVAL; - } } if (flags & DIRTY_BITMAP_MIG_START_FLAG_RESERVED_MASK) { @@ -835,6 +828,21 @@ static int dirty_bitmap_load_start(QEMUFile *f, DBMLoadState *s) persistent = flags & DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT; } + /* Not bdrv_is_writable(): nodes stay inactive until migration ends. */ + if (persistent && bdrv_is_read_only(s->bs)) { + error_report("Cannot make migrated bitmap '%s' persistent " + "on read-only node '%s'", s->bitmap_name, + bdrv_get_node_name(s->bs)); + return -EINVAL; + } + + s->bitmap = bdrv_create_dirty_bitmap(s->bs, granularity, + s->bitmap_name, &local_err); + if (!s->bitmap) { + error_report_err(local_err); + return -EINVAL; + } + if (persistent) { bdrv_dirty_bitmap_set_persistence(s->bitmap, true); } diff --git a/migration/channel.c b/migration/channel.c index 1e2935f926..266ae8f776 100644 --- a/migration/channel.c +++ b/migration/channel.c @@ -296,9 +296,16 @@ int migration_channel_read_peek(QIOChannel *ioc, if (len == buflen) { break; + } else if (len == QIO_CHANNEL_ERR_BLOCK) { + qio_channel_wait_cond(ioc, G_IO_IN); + } else { + /* + * When partially ready, we can't use qio_channel_wait_cond() + * because it will return immediately. Apply a manual wait. + */ + assert(!qemu_in_coroutine()); + g_usleep(1000); } - - qio_channel_wait_cond(ioc, G_IO_IN); } return 0; diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index 502ca704da..ad68fa23aa 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -15,6 +15,7 @@ #include "qemu/osdep.h" #include "block/qapi.h" +#include "block/block-global-state.h" #include "migration/snapshot.h" #include "monitor/hmp.h" #include "monitor/hmp-completion.h" @@ -96,7 +97,7 @@ static void migration_dump_blocktime(Monitor *mon, MigrationInfo *info) } if (info->has_postcopy_non_vcpu_latency) { - monitor_printf(mon, "Postcopy non-vCPU Latencies (ns): %" PRIu64 "\n", + monitor_printf(mon, "Postcopy non-vCPU Latency (ns): %" PRIu64 "\n", info->postcopy_non_vcpu_latency); } @@ -837,9 +838,11 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) bool detach = qdict_get_try_bool(qdict, "detach", false); bool resume = qdict_get_try_bool(qdict, "resume", false); const char *uri = qdict_get_str(qdict, "uri"); + const char *uri_cpr = qdict_get_try_str(qdict, "uri-cpr"); Error *err = NULL; g_autoptr(MigrationChannelList) caps = NULL; g_autoptr(MigrationChannel) channel = NULL; + g_autoptr(MigrationChannel) channel_cpr = NULL; if (!migrate_uri_parse(uri, &channel, &err)) { hmp_handle_error(mon, err); @@ -847,6 +850,22 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) } QAPI_LIST_PREPEND(caps, g_steal_pointer(&channel)); + if (uri_cpr) { + if (migrate_mode() != MIG_MODE_CPR_TRANSFER) { + error_setg(&err, "-c can only be used in cpr-transfer mode"); + hmp_handle_error(mon, err); + return; + } + + if (!migrate_uri_parse(uri_cpr, &channel_cpr, &err)) { + hmp_handle_error(mon, err); + return; + } + + channel_cpr->channel_type = MIGRATION_CHANNEL_TYPE_CPR; + QAPI_LIST_PREPEND(caps, g_steal_pointer(&channel_cpr)); + } + qmp_migrate(NULL, true, caps, true, resume, &err); if (hmp_handle_error(mon, err)) { return; diff --git a/migration/migration.c b/migration/migration.c index 9736c14458..9df6da131c 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2806,7 +2806,7 @@ static bool migration_switchover_prepare(MigrationState *s) bql_lock(); /* * After BQL released and retaken, the state can be CANCELLING if it - * happend during sem_wait().. Only change the state if it's still + * happened during sem_wait().. Only change the state if it's still * pre-switchover. */ migrate_set_state(&s->state, MIGRATION_STATUS_PRE_SWITCHOVER, diff --git a/migration/multifd-qatzip.c b/migration/multifd-qatzip.c index 7419e5dc0d..0262e81eac 100644 --- a/migration/multifd-qatzip.c +++ b/migration/multifd-qatzip.c @@ -348,7 +348,10 @@ static int qatzip_recv(MultiFDRecvParams *p, Error **errp) multifd_recv_zero_page_process(p); if (!p->normal_num) { - assert(in_size == 0); + if (in_size != 0) { + error_setg(errp, "multifd %u: expected empty packet", p->id); + return -1; + } return 0; } diff --git a/migration/multifd-qpl.c b/migration/multifd-qpl.c index 52902eb00c..3826e7f340 100644 --- a/migration/multifd-qpl.c +++ b/migration/multifd-qpl.c @@ -664,26 +664,42 @@ static int multifd_qpl_recv(MultiFDRecvParams *p, Error **errp) } multifd_recv_zero_page_process(p); if (!p->normal_num) { - assert(in_size == 0); + if (in_size != 0) { + error_setg(errp, "multifd %u: expected empty packet", p->id); + return -1; + } return 0; } /* read compressed page lengths */ len = p->normal_num * sizeof(uint32_t); - assert(len < in_size); + if (len >= in_size) { + error_setg(errp, "multifd %u: header len %"PRIu32 + " >= packet size %"PRIu32, p->id, len, in_size); + return -1; + } ret = qio_channel_read_all(p->c, (void *) qpl->zlen, len, errp); if (ret != 0) { return ret; } for (int i = 0; i < p->normal_num; i++) { qpl->zlen[i] = be32_to_cpu(qpl->zlen[i]); - assert(qpl->zlen[i] <= multifd_ram_page_size()); + if (qpl->zlen[i] > multifd_ram_page_size()) { + error_setg(errp, "multifd %u: page %d compressed len %" + PRIu32" too large", p->id, i, qpl->zlen[i]); + return -1; + } zbuf_len += qpl->zlen[i]; ramblock_recv_bitmap_set_offset(p->block, p->normal[i]); } /* read compressed pages */ - assert(in_size == len + zbuf_len); + if (in_size != len + zbuf_len) { + error_setg(errp, "multifd %u: packet size %"PRIu32 + " != header %"PRIu32" + data %"PRIu32, + p->id, in_size, len, zbuf_len); + return -1; + } ret = qio_channel_read_all(p->c, (void *) qpl->zbuf, zbuf_len, errp); if (ret != 0) { return ret; diff --git a/migration/multifd-uadk.c b/migration/multifd-uadk.c index fd7cd9b5e8..d373615ba8 100644 --- a/migration/multifd-uadk.c +++ b/migration/multifd-uadk.c @@ -245,12 +245,19 @@ static int multifd_uadk_recv(MultiFDRecvParams *p, Error **errp) multifd_recv_zero_page_process(p); if (!p->normal_num) { - assert(in_size == 0); + if (in_size != 0) { + error_setg(errp, "multifd %u: expected empty packet", p->id); + return -1; + } return 0; } /* read compressed data lengths */ - assert(hdr_len < in_size); + if (hdr_len >= in_size) { + error_setg(errp, "multifd %u: header len %"PRIu32 + " >= packet size %"PRIu32, p->id, hdr_len, in_size); + return -1; + } ret = qio_channel_read_all(p->c, (void *) uadk_data->buf_hdr, hdr_len, errp); if (ret != 0) { @@ -259,12 +266,21 @@ static int multifd_uadk_recv(MultiFDRecvParams *p, Error **errp) for (int i = 0; i < p->normal_num; i++) { uadk_data->buf_hdr[i] = be32_to_cpu(uadk_data->buf_hdr[i]); + if (uadk_data->buf_hdr[i] > page_size) { + error_setg(errp, "multifd %u: page %d compressed len %"PRIu32 + " too large", p->id, i, uadk_data->buf_hdr[i]); + return -1; + } data_len += uadk_data->buf_hdr[i]; - assert(uadk_data->buf_hdr[i] <= page_size); } /* read compressed data */ - assert(in_size == hdr_len + data_len); + if (in_size != hdr_len + data_len) { + error_setg(errp, "multifd %u: packet size %"PRIu32 + " != header %"PRIu32" + data %"PRIu32, + p->id, in_size, hdr_len, data_len); + return -1; + } ret = qio_channel_read_all(p->c, (void *)buf, data_len, errp); if (ret != 0) { return ret; diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c index 8820b2a787..400146566e 100644 --- a/migration/multifd-zlib.c +++ b/migration/multifd-zlib.c @@ -216,10 +216,19 @@ static int multifd_zlib_recv(MultiFDRecvParams *p, Error **errp) return -1; } + if (in_size > z->zbuff_len) { + error_setg(errp, "multifd %u: next_packet_size %"PRIu32 + " exceeds allocated %"PRIu32, p->id, in_size, z->zbuff_len); + return -1; + } + multifd_recv_zero_page_process(p); if (!p->normal_num) { - assert(in_size == 0); + if (in_size != 0) { + error_setg(errp, "multifd %u: expected empty packet", p->id); + return -1; + } return 0; } diff --git a/migration/multifd-zstd.c b/migration/multifd-zstd.c index 3c2dcf76b0..69ef1a5f38 100644 --- a/migration/multifd-zstd.c +++ b/migration/multifd-zstd.c @@ -210,10 +210,19 @@ static int multifd_zstd_recv(MultiFDRecvParams *p, Error **errp) return -1; } + if (in_size > z->zbuff_len) { + error_setg(errp, "multifd %u: next_packet_size %"PRIu32 + " exceeds allocated %"PRIu32, p->id, in_size, z->zbuff_len); + return -1; + } + multifd_recv_zero_page_process(p); if (!p->normal_num) { - assert(in_size == 0); + if (in_size != 0) { + error_setg(errp, "multifd %u: expected empty packet", p->id); + return -1; + } return 0; } diff --git a/migration/multifd.c b/migration/multifd.c index dbad525d2a..503014f76b 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -1056,6 +1056,7 @@ static void multifd_recv_terminate_threads(Error *err) trace_multifd_recv_terminate_threads(err != NULL); if (qatomic_xchg(&multifd_recv_state->exiting, 1)) { + error_free(err); return; } diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index f5ef93f193..a3314d3180 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -297,9 +297,6 @@ static struct PostcopyBlocktimeContext *blocktime_context_new(void) unsigned int smp_cpus = ms->smp.cpus; PostcopyBlocktimeContext *ctx = g_new0(PostcopyBlocktimeContext, 1); - /* Initialize all counters to be zeros */ - memset(ctx->latency_buckets, 0, sizeof(ctx->latency_buckets)); - ctx->vcpu_blocktime_total = g_new0(uint64_t, smp_cpus); ctx->vcpu_faults_count = g_new0(uint64_t, smp_cpus); ctx->vcpu_faults_current = g_new0(uint8_t, smp_cpus); @@ -1093,7 +1090,11 @@ void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, /* * Account how many concurrent faults on this vCPU we trapped. See * comments above vcpu_faults_current[] on why it can be more than one. + * + * vcpu_faults_current[] is uint8_t, so assert before incrementing to + * catch overflow before it wraps. */ + assert(dc->vcpu_faults_current[cpu] < 255); if (dc->vcpu_faults_current[cpu]++ == 0) { dc->smp_cpus_down++; /* @@ -1103,9 +1104,6 @@ void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, */ dc->last_begin = current; } - - /* Making sure it won't overflow - it really should never! */ - assert(dc->vcpu_faults_current[cpu] <= 255); } else { /* * For non-vCPU thread faults, we don't care about tid or cpu index @@ -1368,7 +1366,7 @@ static void *postcopy_ram_fault_thread(void *opaque) } } if (msg.event != UFFD_EVENT_PAGEFAULT) { - error_report("%s: Read unexpected event %ud from userfaultfd", + error_report("%s: Read unexpected event %u from userfaultfd", __func__, msg.event); continue; /* It's not a page fault, shouldn't happen */ } @@ -1443,7 +1441,7 @@ retry: } } if (msg.event != UFFD_EVENT_PAGEFAULT) { - error_report("%s: Read unexpected event %ud " + error_report("%s: Read unexpected event %u " "from userfaultfd (shared)", __func__, msg.event); continue; /* It's not a page fault, shouldn't happen */ diff --git a/migration/qemu-file.c b/migration/qemu-file.c index d5a48115bd..f30e13c66d 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -830,7 +830,7 @@ uint64_t qemu_get_be64(QEMUFile *f) * else 0 * (Note a 0 length string will return 0 either way) */ -size_t coroutine_fn qemu_get_counted_string(QEMUFile *f, char buf[256]) +size_t coroutine_mixed_fn qemu_get_counted_string(QEMUFile *f, char buf[256]) { size_t len = qemu_get_byte(f); size_t res = qemu_get_buffer(f, (uint8_t *)buf, len); diff --git a/migration/ram.c b/migration/ram.c index 8918b2f03b..b6eb842746 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -4263,15 +4263,15 @@ static int parse_ramblock(QEMUFile *f, RAMBlock *block, ram_addr_t length) return ret; } -static int parse_ramblocks(QEMUFile *f, ram_addr_t total_ram_bytes) +static int parse_ramblocks(QEMUFile *f, uint64_t total_ram_bytes) { int ret = 0; /* Synchronize RAM block list */ - while (!ret && total_ram_bytes) { + while (total_ram_bytes) { RAMBlock *block; char id[256]; - ram_addr_t length; + uint64_t length; int len = qemu_get_byte(f); qemu_get_buffer(f, (uint8_t *)id, len); @@ -4285,8 +4285,15 @@ static int parse_ramblocks(QEMUFile *f, ram_addr_t total_ram_bytes) error_report("Unknown ramblock \"%s\", cannot accept " "migration", id); ret = -EINVAL; + break; + } + + if (usub64_overflow(total_ram_bytes, length, &total_ram_bytes)) { + error_report("%s: RAMBlock '%s' size underflow total RAM size", + __func__, block->idstr); + ret = -EFAULT; + break; } - total_ram_bytes -= length; } return ret; diff --git a/migration/rdma.c b/migration/rdma.c index 3e37a1d440..438419d189 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -1348,17 +1348,13 @@ static int qemu_rdma_poll(RDMAContext *rdma, struct ibv_cq *cq, /* Wait for activity on the completion channel. * Returns 0 on success, none-0 on error. */ -static int qemu_rdma_wait_comp_channel(RDMAContext *rdma, - struct ibv_comp_channel *comp_channel) +static int coroutine_mixed_fn +qemu_rdma_wait_comp_channel(RDMAContext *rdma, + struct ibv_comp_channel *comp_channel) { struct rdma_cm_event *cm_event; - /* - * 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 && - migration_incoming_get_current()->state == MIGRATION_STATUS_ACTIVE) { + if (qemu_in_coroutine()) { yield_until_fd_readable(comp_channel->fd); } else { /* This is the source side, we're in a separate thread diff --git a/migration/trace-events b/migration/trace-events index f5339f4193..af0e784535 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -20,7 +20,7 @@ loadvm_postcopy_handle_run(void) "" loadvm_postcopy_handle_resume(void) "" loadvm_postcopy_ram_handle_discard(void) "" loadvm_postcopy_ram_handle_discard_end(void) "" -loadvm_postcopy_ram_handle_discard_header(const char *ramid, uint16_t len) "%s: %ud" +loadvm_postcopy_ram_handle_discard_header(const char *ramid, uint16_t len) "%s: %u" loadvm_process_command(const char *s, uint16_t len) "com=%s len=%d" loadvm_process_command_ping(uint32_t val) "0x%x" loadvm_approve_switchover_legacy(const char *approver, unsigned int switchover_ack_pending_num_legacy) "Approver %s, switchover_ack_pending_num_legacy %u" @@ -28,7 +28,7 @@ loadvm_approve_switchover(const char *approver) "Approver %s" postcopy_ram_listen_thread_exit(void) "" postcopy_ram_listen_thread_start(void) "" qemu_savevm_send_postcopy_advise(void) "" -qemu_savevm_send_postcopy_ram_discard(const char *id, uint16_t len) "%s: %ud" +qemu_savevm_send_postcopy_ram_discard(const char *id, uint16_t len) "%s: %u" savevm_command_send(uint16_t command, uint16_t len) "com=0x%x len=%d" savevm_section_start(const char *id, unsigned int section_id) "%s, section_id %u" savevm_section_end(const char *id, unsigned int section_id, int ret) "%s, section_id %u -> %d" @@ -119,7 +119,7 @@ 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_save_iterate_big_wait(uint64_t milliseconds, int iterations) "big wait: %" PRIu64 " milliseconds, %d iterations" ram_load_start(void) "" ram_load_complete(int ret, uint64_t seq_iter) "exit_code %d seq iteration %" PRIu64 ram_write_tracking_ramblock_start(const char *block_id, size_t page_size, void *addr, size_t length) "%s: page_size: %zu addr: %p length: %zu" diff --git a/monitor/fds.c b/monitor/fds.c index cc35d2ec33..abe5f13487 100644 --- a/monitor/fds.c +++ b/monitor/fds.c @@ -29,6 +29,7 @@ #include "qapi/qmp/qerror.h" #include "qemu/ctype.h" #include "qemu/cutils.h" +#include "qemu/lockable.h" #include "system/runstate.h" /* file descriptors passed via SCM_RIGHTS */ diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index e9fb8d827a..4e8d996dbb 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -290,16 +290,16 @@ void hmp_info_sync_profile(Monitor *mon, const QDict *qdict) void hmp_info_history(Monitor *mon, const QDict *qdict) { - MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, parent_obj); + MonitorHMP *hmp = MONITOR_HMP(mon); int i; const char *str; - if (!hmp_mon->rs) { + if (!hmp->rs) { return; } i = 0; for(;;) { - str = readline_get_history(hmp_mon->rs, i); + str = readline_get_history(hmp->rs, i); if (!str) { break; } diff --git a/monitor/hmp.c b/monitor/hmp.c index 71a1888249..22b1972d34 100644 --- a/monitor/hmp.c +++ b/monitor/hmp.c @@ -49,24 +49,24 @@ OBJECT_DEFINE_TYPE(MonitorHMP, monitor_hmp, MONITOR_HMP, MONITOR); static void monitor_hmp_finalize(Object *obj) { - MonitorHMP *mon = MONITOR_HMP(obj); - if (mon->rs) { - readline_free(mon->rs); + MonitorHMP *hmp = MONITOR_HMP(obj); + if (hmp->rs) { + readline_free(hmp->rs); } } static bool monitor_hmp_get_readline(Object *obj, Error **errp) { - MonitorHMP *mon = MONITOR_HMP(obj); + MonitorHMP *hmp = MONITOR_HMP(obj); - return mon->use_readline; + return hmp->use_readline; } static void monitor_hmp_set_readline(Object *obj, bool val, Error **errp) { - MonitorHMP *mon = MONITOR_HMP(obj); + MonitorHMP *hmp = MONITOR_HMP(obj); - mon->use_readline = val; + hmp->use_readline = val; } int monitor_hmp_vprintf(Monitor *mon, const char *fmt, va_list ap) @@ -128,34 +128,34 @@ static void monitor_hmp_accept_input(Monitor *mon) static void monitor_command_cb(void *opaque, const char *cmdline, void *readline_opaque) { - MonitorHMP *mon = opaque; + MonitorHMP *hmp = opaque; - monitor_suspend(&mon->parent_obj); - handle_hmp_command(mon, cmdline); - monitor_resume(&mon->parent_obj); + monitor_suspend(&hmp->parent_obj); + handle_hmp_command(hmp, cmdline); + monitor_resume(&hmp->parent_obj); } -void monitor_read_command(MonitorHMP *mon, int show_prompt) +void monitor_read_command(MonitorHMP *hmp, int show_prompt) { - if (!mon->rs) { + if (!hmp->rs) { return; } - readline_start(mon->rs, "(qemu) ", 0, monitor_command_cb, NULL); + readline_start(hmp->rs, "(qemu) ", 0, monitor_command_cb, NULL); if (show_prompt) { - readline_show_prompt(mon->rs); + readline_show_prompt(hmp->rs); } } -int monitor_read_password(MonitorHMP *mon, ReadLineFunc *readline_func, +int monitor_read_password(MonitorHMP *hmp, ReadLineFunc *readline_func, void *opaque) { - if (mon->rs) { - readline_start(mon->rs, "Password: ", 1, readline_func, opaque); + if (hmp->rs) { + readline_start(hmp->rs, "Password: ", 1, readline_func, opaque); /* prompt is printed on return from the command handler */ return 0; } else { - monitor_printf(&mon->parent_obj, + monitor_printf(&hmp->parent_obj, "terminal does not support password prompting\n"); return -ENOTTY; } @@ -772,12 +772,12 @@ static const HMPCommand *search_dispatch_table(const HMPCommand *disp_table, * Do not assume the return value points into @table! It doesn't when * the command is found in a sub-command table. */ -static const HMPCommand *monitor_parse_command(MonitorHMP *hmp_mon, +static const HMPCommand *monitor_parse_command(MonitorHMP *hmp, const char *cmdp_start, const char **cmdp, HMPCommand *table) { - Monitor *mon = &hmp_mon->parent_obj; + Monitor *mon = &hmp->parent_obj; const char *p; const HMPCommand *cmd; char cmdname[256]; @@ -809,7 +809,7 @@ static const HMPCommand *monitor_parse_command(MonitorHMP *hmp_mon, *cmdp = p; /* search sub command */ if (cmd->sub_table != NULL && *p != '\0') { - return monitor_parse_command(hmp_mon, cmdp_start, cmdp, cmd->sub_table); + return monitor_parse_command(hmp, cmdp_start, cmdp, cmd->sub_table); } return cmd; @@ -1254,15 +1254,15 @@ static void handle_hmp_command_co(void *opaque) data->done = true; } -void handle_hmp_command(MonitorHMP *mon, const char *cmdline) +void handle_hmp_command(MonitorHMP *hmp, const char *cmdline) { QDict *qdict; const HMPCommand *cmd; const char *cmd_start = cmdline; - trace_handle_hmp_command(mon, cmdline); + trace_handle_hmp_command(hmp, cmdline); - cmd = monitor_parse_command(mon, cmdline, &cmdline, + cmd = monitor_parse_command(hmp, cmdline, &cmdline, hmp_cmds_for_target(false)); if (!cmd) { return; @@ -1270,17 +1270,17 @@ void handle_hmp_command(MonitorHMP *mon, const char *cmdline) if (!cmd->cmd && !cmd->cmd_info_hrt) { /* FIXME: is it useful to try autoload modules here ??? */ - monitor_printf(&mon->parent_obj, "Command \"%.*s\" is not available.\n", + monitor_printf(&hmp->parent_obj, "Command \"%.*s\" is not available.\n", (int)(cmdline - cmd_start), cmd_start); return; } - qdict = monitor_parse_arguments(&mon->parent_obj, &cmdline, cmd); + qdict = monitor_parse_arguments(&hmp->parent_obj, &cmdline, cmd); if (!qdict) { while (cmdline > cmd_start && qemu_isspace(cmdline[-1])) { cmdline--; } - monitor_printf(&mon->parent_obj, + monitor_printf(&hmp->parent_obj, "Try \"help %.*s\" for more information\n", (int)(cmdline - cmd_start), cmd_start); return; @@ -1289,18 +1289,18 @@ void handle_hmp_command(MonitorHMP *mon, const char *cmdline) if (!cmd->coroutine) { /* old_mon is non-NULL when called from qmp_human_monitor_command() */ Monitor *old_mon = monitor_set_cur(qemu_coroutine_self(), - &mon->parent_obj); - handle_hmp_command_exec(&mon->parent_obj, cmd, qdict); + &hmp->parent_obj); + handle_hmp_command_exec(&hmp->parent_obj, cmd, qdict); monitor_set_cur(qemu_coroutine_self(), old_mon); } else { HandleHmpCommandCo data = { - .mon = &mon->parent_obj, + .mon = &hmp->parent_obj, .cmd = cmd, .qdict = qdict, .done = false, }; Coroutine *co = qemu_coroutine_create(handle_hmp_command_co, &data); - monitor_set_cur(co, &mon->parent_obj); + monitor_set_cur(co, &hmp->parent_obj); aio_co_enter(qemu_get_aio_context(), co); AIO_WAIT_WHILE_UNLOCKED(NULL, !data.done); } @@ -1308,7 +1308,8 @@ void handle_hmp_command(MonitorHMP *mon, const char *cmdline) qobject_unref(qdict); } -static void cmd_completion(MonitorHMP *mon, const char *name, const char *list) +static void cmd_completion(MonitorHMP *hmp, + const char *name, const char *list) { const char *p, *pstart; char cmd[128]; @@ -1324,7 +1325,7 @@ static void cmd_completion(MonitorHMP *mon, const char *name, const char *list) } memcpy(cmd, pstart, len); cmd[len] = '\0'; - readline_add_completion_of(mon->rs, name, cmd); + readline_add_completion_of(hmp->rs, name, cmd); if (*p == '\0') { break; } @@ -1332,7 +1333,7 @@ static void cmd_completion(MonitorHMP *mon, const char *name, const char *list) } } -static void file_completion(MonitorHMP *mon, const char *input) +static void file_completion(MonitorHMP *hmp, const char *input) { DIR *ffs; struct dirent *d; @@ -1384,7 +1385,7 @@ static void file_completion(MonitorHMP *mon, const char *input) if (stat(file, &sb) == 0 && S_ISDIR(sb.st_mode)) { pstrcat(file, sizeof(file), "/"); } - readline_add_completion(mon->rs, file); + readline_add_completion(hmp->rs, file); } } closedir(ffs); @@ -1396,7 +1397,7 @@ static const char *next_arg_type(const char *typestr) return (p != NULL ? ++p : typestr); } -static void monitor_find_completion_by_table(MonitorHMP *mon, +static void monitor_find_completion_by_table(MonitorHMP *hmp, const HMPCommand *cmd_table, char **args, int nb_args) @@ -1414,10 +1415,10 @@ static void monitor_find_completion_by_table(MonitorHMP *mon, } else { cmdname = args[0]; } - readline_set_completion_index(mon->rs, strlen(cmdname)); + readline_set_completion_index(hmp->rs, strlen(cmdname)); for (cmd = cmd_table; cmd->name != NULL; cmd++) { if (cmd_available(cmd)) { - cmd_completion(mon, cmdname, cmd->name); + cmd_completion(hmp, cmdname, cmd->name); } } } else { @@ -1434,12 +1435,12 @@ static void monitor_find_completion_by_table(MonitorHMP *mon, if (cmd->sub_table) { /* do the job again */ - monitor_find_completion_by_table(mon, cmd->sub_table, + monitor_find_completion_by_table(hmp, cmd->sub_table, &args[1], nb_args - 1); return; } if (cmd->command_completion) { - cmd->command_completion(mon->rs, nb_args, args[nb_args - 1]); + cmd->command_completion(hmp->rs, nb_args, args[nb_args - 1]); return; } @@ -1461,20 +1462,20 @@ static void monitor_find_completion_by_table(MonitorHMP *mon, switch (*ptype) { case 'F': /* file completion */ - readline_set_completion_index(mon->rs, strlen(str)); - file_completion(mon, str); + readline_set_completion_index(hmp->rs, strlen(str)); + file_completion(hmp, str); break; case 'B': /* block device name completion */ - readline_set_completion_index(mon->rs, strlen(str)); + readline_set_completion_index(hmp->rs, strlen(str)); while ((blk = blk_next(blk)) != NULL) { - readline_add_completion_of(mon->rs, str, blk_name(blk)); + readline_add_completion_of(hmp->rs, str, blk_name(blk)); } break; case 's': case 'S': if (!strcmp(cmd->name, "help|?")) { - monitor_find_completion_by_table(mon, cmd_table, + monitor_find_completion_by_table(hmp, cmd_table, &args[1], nb_args - 1); } break; @@ -1487,7 +1488,7 @@ static void monitor_find_completion_by_table(MonitorHMP *mon, static void monitor_find_completion(void *opaque, const char *cmdline) { - MonitorHMP *mon = opaque; + MonitorHMP *hmp = opaque; char *args[MAX_ARGS]; int nb_args, len; @@ -1509,7 +1510,7 @@ static void monitor_find_completion(void *opaque, } /* 2. auto complete according to args */ - monitor_find_completion_by_table(mon, hmp_cmds_for_target(false), + monitor_find_completion_by_table(hmp, hmp_cmds_for_target(false), args, nb_args); cleanup: @@ -1518,18 +1519,19 @@ cleanup: static void monitor_read(void *opaque, const uint8_t *buf, int size) { - MonitorHMP *mon = container_of(opaque, MonitorHMP, parent_obj); + Monitor *mon = opaque; + MonitorHMP *hmp = MONITOR_HMP(mon); int i; - if (mon->rs) { + if (hmp->rs) { for (i = 0; i < size; i++) { - readline_handle_byte(mon->rs, buf[i]); + readline_handle_byte(hmp->rs, buf[i]); } } else { if (size == 0 || buf[size - 1] != 0) { - monitor_printf(&mon->parent_obj, "corrupted command\n"); + monitor_printf(&hmp->parent_obj, "corrupted command\n"); } else { - handle_hmp_command(mon, (char *)buf); + handle_hmp_command(hmp, (char *)buf); } } } @@ -1598,17 +1600,17 @@ static void monitor_event(void *opaque, QEMUChrEvent event) static void G_GNUC_PRINTF(2, 3) monitor_readline_printf(void *opaque, const char *fmt, ...) { - MonitorHMP *mon = opaque; + MonitorHMP *hmp = opaque; va_list ap; va_start(ap, fmt); - monitor_vprintf(&mon->parent_obj, fmt, ap); + monitor_vprintf(&hmp->parent_obj, fmt, ap); va_end(ap); } static void monitor_readline_flush(void *opaque) { - MonitorHMP *mon = opaque; - monitor_flush(&mon->parent_obj); + MonitorHMP *hmp = opaque; + monitor_flush(&hmp->parent_obj); } void monitor_new_hmp(const char *id, const char *chardev_id, @@ -1626,11 +1628,11 @@ void monitor_new_hmp(const char *id, const char *chardev_id, static void monitor_hmp_complete(UserCreatable *uc, Error **errp) { - MonitorHMP *mon = MONITOR_HMP(uc); + MonitorHMP *hmp = MONITOR_HMP(uc); UserCreatableClass *ucc_parent = USER_CREATABLE_CLASS( object_class_get_parent( - OBJECT_CLASS(MONITOR_HMP_GET_CLASS(mon)))); + OBJECT_CLASS(MONITOR_HMP_GET_CLASS(hmp)))); ERRP_GUARD(); ucc_parent->complete(uc, errp); @@ -1638,21 +1640,21 @@ static void monitor_hmp_complete(UserCreatable *uc, Error **errp) return; } - if (mon->parent_obj.chardev_id) { - if (mon->use_readline) { - mon->rs = readline_init(monitor_readline_printf, + if (hmp->parent_obj.chardev_id) { + if (hmp->use_readline) { + hmp->rs = readline_init(monitor_readline_printf, monitor_readline_flush, - mon, + hmp, monitor_find_completion); - monitor_read_command(mon, 0); + monitor_read_command(hmp, 0); } - qemu_chr_fe_set_handlers(&mon->parent_obj.chr, + qemu_chr_fe_set_handlers(&hmp->parent_obj.chr, monitor_can_read, monitor_read, monitor_event, NULL, - &mon->parent_obj, NULL, true); - monitor_list_append(&mon->parent_obj); + &hmp->parent_obj, NULL, true); + monitor_list_append(&hmp->parent_obj); } } diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h index 23829f32f9..e0a124a317 100644 --- a/monitor/monitor-internal.h +++ b/monitor/monitor-internal.h @@ -27,6 +27,7 @@ #include "chardev/char-fe.h" #include "monitor/monitor.h" +#include "qapi/qapi-emit-events.h" #include "qapi/qapi-types-control.h" #include "qapi/qapi-types-qom.h" #include "qapi/qmp-registry.h" @@ -220,7 +221,7 @@ void monitor_data_destroy_qmp(MonitorQMP *mon); void coroutine_fn monitor_qmp_dispatcher_co(void *data); void qmp_dispatcher_co_wake(void); -void handle_hmp_command(MonitorHMP *mon, const char *cmdline); +void handle_hmp_command(MonitorHMP *hmp, const char *cmdline); int hmp_compare_cmd(const char *name, const char *list); /* diff --git a/monitor/monitor.c b/monitor/monitor.c index ed195fd97b..6af09f2f6e 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -30,7 +30,9 @@ #include "qapi/qapi-visit-control.h" #include "qobject/qdict.h" #include "qom/object_interfaces.h" +#include "qemu/aio-wait.h" #include "qemu/error-report.h" +#include "qemu/lockable.h" #include "qemu/option.h" #include "system/qtest.h" #include "trace.h" diff --git a/monitor/qmp.c b/monitor/qmp.c index 223e0643c2..aec0315775 100644 --- a/monitor/qmp.c +++ b/monitor/qmp.c @@ -23,7 +23,8 @@ */ #include "qemu/osdep.h" - +#include "qemu/aio-wait.h" +#include "qemu/lockable.h" #include "chardev/char-io.h" #include "monitor-internal.h" #include "qapi/error.h" @@ -342,7 +343,8 @@ static QMPRequest *monitor_qmp_requests_pop_any_with_lock(void) return req_obj; } -static QMPRequest *monitor_qmp_dispatcher_pop_any(void) +static QMPRequest * coroutine_fn +monitor_qmp_dispatcher_pop_any(void) { while (true) { /* diff --git a/net/filter-rewriter.c b/net/filter-rewriter.c index cdf85aa5ee..c85d180eb9 100644 --- a/net/filter-rewriter.c +++ b/net/filter-rewriter.c @@ -373,7 +373,7 @@ static void colo_rewriter_cleanup(NetFilterState *nf) g_free(s->incoming_queue); } - g_hash_table_destroy(s->connection_track_table); + g_clear_pointer(&s->connection_track_table, g_hash_table_destroy); } static void colo_rewriter_setup(NetFilterState *nf, Error **errp) diff --git a/net/net.c b/net/net.c index 2892f1730d..0a30579ca4 100644 --- a/net/net.c +++ b/net/net.c @@ -783,7 +783,7 @@ ssize_t qemu_receive_packet(NetClientState *nc, const uint8_t *buf, int size) return 0; } - if (net_peer_needs_padding(nc)) { + if (net_client_needs_padding(nc)) { if (eth_pad_short_frame(min_pkt, &min_pktsz, buf, size)) { buf = min_pkt; size = min_pktsz; @@ -1385,7 +1385,9 @@ void show_netdevs(void) "dgram", "hubport", "tap", +#ifdef CONFIG_PASST "passt", +#endif #ifdef CONFIG_SLIRP "user", #endif diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index c526c2b2dc..1052361a4a 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -17,6 +17,7 @@ #include "hw/virtio/vhost-vdpa.h" #include "qemu/config-file.h" #include "qemu/error-report.h" +#include "qemu/iov.h" #include "qemu/log.h" #include "qemu/memalign.h" #include "qemu/option.h" diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img index e24a93e14a..291cd77eaf 100644 Binary files a/pc-bios/s390-ccw.img and b/pc-bios/s390-ccw.img differ diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index 420ee32eff..81512265ce 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -61,6 +61,7 @@ static uint8_t _s2[MAX_SECTOR_SIZE * 3] __attribute__((__aligned__(PAGE_SIZE))); static void *s2_prev_blk = _s2; static void *s2_cur_blk = _s2 + MAX_SECTOR_SIZE; static void *s2_next_blk = _s2 + MAX_SECTOR_SIZE * 2; +static void *s2_end = _s2 + sizeof(_s2); static inline int verify_boot_info(BootInfo *bip) { @@ -308,7 +309,8 @@ static int eckd_get_boot_menu_index(block_number_t s1b_block_nr) } } - return menu_get_zipl_boot_index(s2_cur_blk + banner_offset); + return menu_get_zipl_boot_index(s2_cur_blk + banner_offset, + s2_end); } prev_block_nr = cur_block_nr; @@ -902,7 +904,7 @@ static inline long iso_get_file_size(uint32_t load_rba) if (dir_rem[level] == 0) { /* Nothing remaining */ level--; - if (virtio_read(sec_loc[level], temp)) { + if (level >= 0 && virtio_read(sec_loc[level], temp)) { puts("Failed to read ISO directory"); return -EIO; } diff --git a/pc-bios/s390-ccw/helper.h b/pc-bios/s390-ccw/helper.h index 8e3dfcb6d6..d9b7da444a 100644 --- a/pc-bios/s390-ccw/helper.h +++ b/pc-bios/s390-ccw/helper.h @@ -45,4 +45,14 @@ static inline void sleep(unsigned int seconds) } } +static inline size_t strnlen(const char *s, size_t maxlen) +{ + size_t len = 0; + + while (len < maxlen && s[len]) { + len++; + } + return len; +} + #endif diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c index eeaff78f87..9b81154b0e 100644 --- a/pc-bios/s390-ccw/menu.c +++ b/pc-bios/s390-ccw/menu.c @@ -16,6 +16,7 @@ #include "s390-ccw.h" #include "sclp.h" #include "s390-time.h" +#include "helper.h" #define KEYCODE_NO_INP '\0' #define KEYCODE_ESCAPE '\033' @@ -26,6 +27,9 @@ #define ZIPL_TIMEOUT_OFFSET 138 #define ZIPL_FLAG_OFFSET 140 +/* Max printable chars for a zipl boot menu entry */ +#define ZIPL_ENTRY_MAX 80 + #define TOD_CLOCK_MILLISECOND 0x3e8000 #define LOW_CORE_EXTERNAL_INT_ADDR 0x86 @@ -176,21 +180,31 @@ int menu_get_boot_index(bool *valid_entries) return boot_index; } -/* Returns the entry number that was printed */ +/* Returns the entry number that was printed, or -1 on invalid entry */ static int zipl_print_entry(const char *data, size_t len) { - char buf[len + 2]; + char buf[ZIPL_ENTRY_MAX + 2]; + const char *p; + + if (len > ZIPL_ENTRY_MAX) { + len = ZIPL_ENTRY_MAX; + } ebcdic_to_ascii(data, buf, len); buf[len] = '\n'; buf[len + 1] = '\0'; + p = (buf[0] == ' ') ? buf + 1 : buf; + if (!isdigit((unsigned char)*p)) { + return -1; + } + printf("%s", buf); - return buf[0] == ' ' ? atoi(buf + 1) : atoi(buf); + return atoi(p); } -int menu_get_zipl_boot_index(const char *menu_data) +int menu_get_zipl_boot_index(const char *menu_data, const char *menu_data_end) { size_t len; int entry; @@ -206,16 +220,28 @@ int menu_get_zipl_boot_index(const char *menu_data) timeout = zipl_timeout * 1000; } - /* Print banner */ + if (menu_data >= menu_data_end) { + return 0; /* Boot default */ + } + + /* Skip banner */ + len = strnlen(menu_data, menu_data_end - menu_data); + menu_data += len + 1; + if (menu_data >= menu_data_end || !(*menu_data)) { + return 0; /* No entries, boot default */ + } + puts("s390-ccw zIPL Boot Menu\n"); - menu_data += strlen(menu_data) + 1; /* Print entries */ - while (*menu_data) { - len = strlen(menu_data); + while (menu_data < menu_data_end && *menu_data) { + len = strnlen(menu_data, menu_data_end - menu_data); entry = zipl_print_entry(menu_data, len); menu_data += len + 1; + if (entry < 0 || entry >= MAX_BOOT_ENTRIES) { + continue; + } valid_entries[entry] = true; if (entry == 0) { diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c index 651cedf6ef..791854fce0 100644 --- a/pc-bios/s390-ccw/netmain.c +++ b/pc-bios/s390-ccw/netmain.c @@ -40,6 +40,9 @@ #define DEFAULT_BOOT_RETRIES 10 #define DEFAULT_TFTP_RETRIES 20 +/* Index 0 is reserved for default alias, start PXE cfg indices at 1 */ +#define PXECFG_MAX (MAX_BOOT_ENTRIES - 1) + extern char _start[]; #define KERNEL_ADDR ((void *)0L) @@ -381,13 +384,13 @@ static int net_select_and_load_kernel(filename_ip_t *fn_ip, static int net_try_pxelinux_cfg(filename_ip_t *fn_ip) { - struct pl_cfg_entry entries[MAX_BOOT_ENTRIES]; + struct pl_cfg_entry entries[PXECFG_MAX]; int num_ent, def_ent = 0; num_ent = pxelinux_load_parse_cfg(fn_ip, mac, get_uuid(), DEFAULT_TFTP_RETRIES, cfgbuf, sizeof(cfgbuf), - entries, MAX_BOOT_ENTRIES, &def_ent); + entries, PXECFG_MAX, &def_ent); return net_select_and_load_kernel(fn_ip, num_ent, def_ent, entries); } @@ -470,11 +473,11 @@ static int net_try_direct_tftp_load(filename_ip_t *fn_ip) * a magic comment string. */ if (!strncasecmp("# pxelinux", cfgbuf, 10)) { - struct pl_cfg_entry entries[MAX_BOOT_ENTRIES]; + struct pl_cfg_entry entries[PXECFG_MAX]; int num_ent, def_ent = 0; num_ent = pxelinux_parse_cfg(cfgbuf, sizeof(cfgbuf), entries, - MAX_BOOT_ENTRIES, &def_ent); + PXECFG_MAX, &def_ent); return net_select_and_load_kernel(fn_ip, num_ent, def_ent, entries); } diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index 1e1f71775e..25aac91d45 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -76,14 +76,12 @@ void jump_to_low_kernel(void); /* menu.c */ void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout); -int menu_get_zipl_boot_index(const char *menu_data); +int menu_get_zipl_boot_index(const char *menu_data, const char *menu_data_end); bool menu_is_enabled_zipl(void); int menu_get_enum_boot_index(bool *valid_entries); bool menu_is_enabled_enum(void); int menu_get_boot_index(bool *valid_entries); -#define MAX_BOOT_ENTRIES 31 - __attribute__ ((__noreturn__)) static inline void panic(const char *string) { diff --git a/python/scripts/vendor.py b/python/scripts/vendor.py index 1bb59d863a..9b4c763f9e 100755 --- a/python/scripts/vendor.py +++ b/python/scripts/vendor.py @@ -41,8 +41,8 @@ def main() -> int: parser.parse_args() packages = { - "meson==1.11.1": - "9b3a023657e393dbc5335b95c561337d49b7a458f5541e47ec44f2cc566e0d80", + "meson==1.12.0": + "71f133147fa0fcfe8f4df49fa1045771064947834538409e5d97b3613aac8b4e", "qemu.qmp==0.0.6": "5d7c5af0e9de427696e3bf72e333965c3a697929f77f6b7ddc30c989fc7b539b", "pycotap==1.3.1": diff --git a/python/wheels/meson-1.11.1-py3-none-any.whl b/python/wheels/meson-1.11.1-py3-none-any.whl deleted file mode 100644 index 8dc3c9f2f6..0000000000 Binary files a/python/wheels/meson-1.11.1-py3-none-any.whl and /dev/null differ diff --git a/python/wheels/meson-1.12.0-py3-none-any.whl b/python/wheels/meson-1.12.0-py3-none-any.whl new file mode 100644 index 0000000000..0b764d238f Binary files /dev/null and b/python/wheels/meson-1.12.0-py3-none-any.whl differ diff --git a/pythondeps.toml b/pythondeps.toml index bef88d49a6..061f0cff0c 100644 --- a/pythondeps.toml +++ b/pythondeps.toml @@ -19,12 +19,12 @@ [meson] # The install key should match the version in python/wheels/ -meson = { accepted = ">=1.5.0", installed = "1.11.1", canary = "meson" } +meson = { accepted = ">=1.5.0", installed = "1.12.0", canary = "meson" } pycotap = { accepted = ">=1.1.0", installed = "1.3.1" } [meson-rust] # The install key should match the version in python/wheels/ -meson = { accepted = ">=1.11.0", installed = "1.11.1", canary = "meson" } +meson = { accepted = ">=1.12.0", installed = "1.12.0", canary = "meson" } [docs] # Please keep the installed versions in sync with docs/requirements.txt diff --git a/qapi/block-core.json b/qapi/block-core.json index 1f87b07850..199efc1e00 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2353,7 +2353,9 @@ # @persistent: the bitmap is persistent, i.e. it will be saved to the # corresponding block device image file on its close. For now # only Qcow2 disks support persistent bitmaps. Default is false -# for `block-dirty-bitmap-add`. (Since: 2.10) +# for `block-dirty-bitmap-add`. This fails if the node is +# read-only or inactive, since such a bitmap could never be +# stored. (Since: 2.10) # # @disabled: the bitmap is created in the disabled state, which means # that it will not track drive changes. The bitmap may be enabled diff --git a/qapi/crypto.json b/qapi/crypto.json index 2b55befef9..6e3a98ff68 100644 --- a/qapi/crypto.json +++ b/qapi/crypto.json @@ -121,10 +121,12 @@ # # @ctr: Counter (Since 2.8) # +# @gcm: Galois/Counter Mode (Since 11.2) +# # Since: 2.6 ## { 'enum': 'QCryptoCipherMode', - 'data': ['ecb', 'cbc', 'xts', 'ctr']} + 'data': ['ecb', 'cbc', 'xts', 'ctr', 'gcm']} ## # @QCryptoIVGenAlgo: diff --git a/qapi/dump.json b/qapi/dump.json index 726b520870..690f3963fe 100644 --- a/qapi/dump.json +++ b/qapi/dump.json @@ -38,6 +38,13 @@ # @win-dmp: Windows full crashdump format, can be used instead of ELF # converting (since 2.13) # +# Features: +# +# @allowed-by-guest: If present, @win-dmp is listed by +# `query-dump-guest-memory-capability`, and accepted by +# `dump-guest-memory`, only when the guest has published a Windows +# dump header through the vmcoreinfo device (since 11.1) +# # Since: 2.0 ## { 'enum': 'DumpGuestMemoryFormat', @@ -45,7 +52,7 @@ 'elf', 'kdump-zlib', 'kdump-lzo', 'kdump-snappy', 'kdump-raw-zlib', 'kdump-raw-lzo', 'kdump-raw-snappy', - 'win-dmp' ] } + { 'name': 'win-dmp', 'features': ['allowed-by-guest'] } ] } ## # @dump-guest-memory: diff --git a/qapi/machine-common.json b/qapi/machine-common.json index 92e84dfb14..aca98e994a 100644 --- a/qapi/machine-common.json +++ b/qapi/machine-common.json @@ -108,7 +108,7 @@ # # @caches: the list of `SmpCacheProperties`. # -# Since 9.2 +# Since: 9.2 ## { 'struct': 'SmpCachePropertiesWrapper', 'data': { 'caches': ['SmpCacheProperties'] } } diff --git a/qapi/machine.json b/qapi/machine.json index 9b2248038f..2d63c1bac3 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -432,9 +432,10 @@ ## # @inject-nmi: # -# Injects a Non-Maskable Interrupt into the default CPU (x86/s390) or -# all CPUs (ppc64). The command fails when the guest doesn't support -# injecting. +# Injects a Non-Maskable Interrupt (machine specific: for example on +# s390x CCW only the first vCPU receives the NMI, but on x86 machines +# all vCPUs receive it). The command fails when the guest doesn't +# support injecting. # # Since: 0.14 # diff --git a/qapi/migration.json b/qapi/migration.json index 66bd900465..b1eaf7b054 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -270,8 +270,8 @@ # (Since 10.1) # # @postcopy-non-vcpu-latency: average remote page fault latency for -# all faults happend in non-vCPU threads (in ns). It has the same -# definition of @postcopy-latency but this only provides +# all faults happened in non-vCPU threads (in ns). It has the +# same definition of @postcopy-latency but this only provides # statistics to non-vCPU faults. This is only present when the # postcopy-blocktime migration capability is enabled. # (Since 10.1) diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index e3897d5197..965cad6499 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -14,6 +14,7 @@ #include "qemu/osdep.h" #include "qemu/aio.h" +#include "qemu/aio-wait.h" #include "qapi/compat-policy.h" #include "qapi/error.h" #include "qapi/qmp-registry.h" @@ -24,6 +25,7 @@ #include "qobject/qbool.h" #include "qemu/coroutine.h" #include "qemu/main-loop.h" +#include "monitor/monitor.h" Visitor *qobject_input_visitor_new_qmp(QObject *obj) { diff --git a/qapi/qom.json b/qapi/qom.json index d96cc5aa21..4a9b7f9088 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -58,7 +58,7 @@ # @value: the value of the property. Absent when the property cannot # be read. # -# Since 10.1 +# Since: 10.1 ## { 'struct': 'ObjectPropertyValue', 'data': { 'name': 'str', @@ -70,7 +70,7 @@ # # @properties: a list of properties. # -# Since 10.1 +# Since: 10.1 ## { 'struct': 'ObjectPropertiesValues', 'data': { 'properties': [ 'ObjectPropertyValue' ] }} @@ -167,7 +167,7 @@ # Returns: A list where each element is the result for the # corresponding element of @paths. # -# Since 10.1 +# Since: 10.1 ## { 'command': 'qom-list-get', 'data': { 'paths': [ 'str' ] }, @@ -1017,6 +1017,10 @@ # designated guest firmware page for measured boot with -kernel # (default: false) (since 6.2) # +# @debug-swap: enable virtualization of debug registers, +# only supported on SEV-ES and SEV-SNP guests +# (default: false) (since 11.1) +# # Features: # # @confidential-guest-reset: If present, the hypervisor supports @@ -1028,7 +1032,8 @@ 'data': { '*sev-device': 'str', '*cbitpos': 'uint32', 'reduced-phys-bits': 'uint32', - '*kernel-hashes': 'bool' }, + '*kernel-hashes': 'bool', + '*debug-swap': 'bool' }, 'features': ['confidential-guest-reset']} ## @@ -1108,6 +1113,12 @@ # firmware. Set this to true to disable the use of VCEK. # (default: false) (since: 9.1) # +# @secure-tsc: enable Secure TSC +# (default: false) (since 11.1) +# +# @tsc-frequency: set secure TSC frequency. Only valid if Secure TSC +# is enabled (default: zero) (since 11.1) +# # Since: 9.1 ## { 'struct': 'SevSnpGuestProperties', @@ -1119,7 +1130,9 @@ '*id-auth': 'str', '*author-key-enabled': 'bool', '*host-data': 'str', - '*vcek-disabled': 'bool' } } + '*vcek-disabled': 'bool', + '*secure-tsc': 'bool', + '*tsc-frequency': 'uint32' } } ## # @TdxGuestProperties: diff --git a/qapi/run-state.json b/qapi/run-state.json index a5771ad468..e4fdead1e0 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -317,8 +317,10 @@ # # @none: nothing is done # -# @inject-nmi: a non-maskable interrupt is injected into the first -# VCPU (all VCPUS on x86) (since 2.4) +# @inject-nmi: a non-maskable interrupt is injected (machine +# specific: for example on s390x CCW only the first vCPU +# receives the NMI, but on x86 machines all vCPUs receive +# it). (since 2.4) # # Since: 2.1 ## diff --git a/qobject/json-parser.c b/qobject/json-parser.c index 5935730c0c..6b1b4762bd 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -29,9 +29,10 @@ * object, an error, or NULL (if the object is incomplete and no error * happened) after every token. Therefore it has an explicit * representation of its parser stack; each stack entry consists of a - * parser state and a QObject: - a QList, for an array that is being - * added to - a QDict, for a dictionary that is being added to - a - * QString, for the key of the next pair that will be added to a QDict + * parser state and a QObject: + * - a QList, for an array that is being added to + * - a QDict, for a dictionary that is being added to + * - a QString, for the key of the next pair that will be added to a QDict * * The stack represents an arbitrary nesting of arrays and dictionaries * (whose next key has been parsed); it can also have a dictionary whose diff --git a/qom/object.c b/qom/object.c index f79b2cf361..47977b1f44 100644 --- a/qom/object.c +++ b/qom/object.c @@ -2661,85 +2661,47 @@ static char *object_get_type(Object *obj, Error **errp) return g_strdup(object_get_typename(obj)); } -static void property_get_uint8_ptr(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint8_t value = *(uint8_t *)opaque; - visit_type_uint8(v, name, &value, errp); -} -static void property_set_uint8_ptr(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint8_t *field = opaque; - uint8_t value; - - if (!visit_type_uint8(v, name, &value, errp)) { - return; +#define OBJECT_PROPERTY_SCALAR_GETTER(type) \ + static void property_get_##type##_ptr(Object *obj, Visitor *v, \ + const char *name, \ + void *opaque, Error **errp) \ + { \ + type##_t value = *(type##_t *)opaque; \ + visit_type_##type(v, name, &value, errp); \ } - *field = value; -} -static void property_get_uint16_ptr(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint16_t value = *(uint16_t *)opaque; - visit_type_uint16(v, name, &value, errp); -} - -static void property_set_uint16_ptr(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint16_t *field = opaque; - uint16_t value; - - if (!visit_type_uint16(v, name, &value, errp)) { - return; +#define OBJECT_PROPERTY_SCALAR_SETTER(type) \ + static void property_set_##type##_ptr(Object *obj, Visitor *v, \ + const char *name, \ + void *opaque, Error **errp) \ + { \ + type##_t *field = opaque; \ + type##_t value; \ + \ + if (!visit_type_##type(v, name, &value, errp)) { \ + return; \ + } \ + \ + *field = value; \ } - *field = value; -} -static void property_get_uint32_ptr(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint32_t value = *(uint32_t *)opaque; - visit_type_uint32(v, name, &value, errp); -} +#define DEFINE_OBJECT_PROPERTY_SCALAR_METHODS(type) \ + OBJECT_PROPERTY_SCALAR_GETTER(type) \ + OBJECT_PROPERTY_SCALAR_SETTER(type) -static void property_set_uint32_ptr(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint32_t *field = opaque; - uint32_t value; - if (!visit_type_uint32(v, name, &value, errp)) { - return; - } +DEFINE_OBJECT_PROPERTY_SCALAR_METHODS(uint8) +DEFINE_OBJECT_PROPERTY_SCALAR_METHODS(uint16) +DEFINE_OBJECT_PROPERTY_SCALAR_METHODS(uint32) +DEFINE_OBJECT_PROPERTY_SCALAR_METHODS(uint64) - *field = value; -} +#undef OBJECT_PROPERTY_SCALAR_GETTER +#undef OBJECT_PROPERTY_SCALAR_SETTER +#undef DEFINE_OBJECT_PROPERTY_SCALAR_METHODS -static void property_get_uint64_ptr(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint64_t value = *(uint64_t *)opaque; - visit_type_uint64(v, name, &value, errp); -} - -static void property_set_uint64_ptr(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint64_t *field = opaque; - uint64_t value; - - if (!visit_type_uint64(v, name, &value, errp)) { - return; - } - - *field = value; -} ObjectProperty * object_property_add_uint8_ptr(Object *obj, const char *name, @@ -2762,9 +2724,10 @@ object_property_add_uint8_ptr(Object *obj, const char *name, } ObjectProperty * -object_class_property_add_uint8_ptr(ObjectClass *klass, const char *name, - const uint8_t *v, - ObjectPropertyFlags flags) +object_class_static_property_add_uint8_ptr(ObjectClass *klass, + const char *name, + const uint8_t *v, + ObjectPropertyFlags flags) { ObjectPropertyAccessor *getter = NULL; ObjectPropertyAccessor *setter = NULL; @@ -2802,9 +2765,10 @@ object_property_add_uint16_ptr(Object *obj, const char *name, } ObjectProperty * -object_class_property_add_uint16_ptr(ObjectClass *klass, const char *name, - const uint16_t *v, - ObjectPropertyFlags flags) +object_class_static_property_add_uint16_ptr(ObjectClass *klass, + const char *name, + const uint16_t *v, + ObjectPropertyFlags flags) { ObjectPropertyAccessor *getter = NULL; ObjectPropertyAccessor *setter = NULL; @@ -2842,9 +2806,10 @@ object_property_add_uint32_ptr(Object *obj, const char *name, } ObjectProperty * -object_class_property_add_uint32_ptr(ObjectClass *klass, const char *name, - const uint32_t *v, - ObjectPropertyFlags flags) +object_class_static_property_add_uint32_ptr(ObjectClass *klass, + const char *name, + const uint32_t *v, + ObjectPropertyFlags flags) { ObjectPropertyAccessor *getter = NULL; ObjectPropertyAccessor *setter = NULL; @@ -2882,9 +2847,10 @@ object_property_add_uint64_ptr(Object *obj, const char *name, } ObjectProperty * -object_class_property_add_uint64_ptr(ObjectClass *klass, const char *name, - const uint64_t *v, - ObjectPropertyFlags flags) +object_class_static_property_add_uint64_ptr(ObjectClass *klass, + const char *name, + const uint64_t *v, + ObjectPropertyFlags flags) { ObjectPropertyAccessor *getter = NULL; ObjectPropertyAccessor *setter = NULL; diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c index 48b38d2b7f..330895361d 100644 --- a/qom/qom-qmp-cmds.c +++ b/qom/qom-qmp-cmds.c @@ -218,13 +218,6 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename, continue; } - /* Skip legacy properties since they are just string versions of - * properties that we already list. - */ - if (strstart(prop->name, "legacy-", NULL)) { - continue; - } - info = g_new0(ObjectPropertyInfo, 1); info->name = g_strdup(prop->name); info->type = g_strdup(prop->type); diff --git a/replay/replay-debugging.c b/replay/replay-debugging.c index 1105364002..7b904a1416 100644 --- a/replay/replay-debugging.c +++ b/replay/replay-debugging.c @@ -139,9 +139,8 @@ static char *replay_find_nearest_snapshot(int64_t icount, int64_t *snapshot_icount) { BlockDriverState *bs; - QEMUSnapshotInfo *sn_tab; + g_autofree QEMUSnapshotInfo *sn_tab = NULL; QEMUSnapshotInfo *nearest = NULL; - char *ret = NULL; int rv; int nb_sns, i; @@ -149,15 +148,19 @@ static char *replay_find_nearest_snapshot(int64_t icount, bs = bdrv_all_find_vmstate_bs(NULL, false, NULL, NULL); if (!bs) { - goto fail; + return NULL; } nb_sns = bdrv_snapshot_list(bs, &sn_tab); + if (nb_sns < 0) { + return NULL; + } for (i = 0; i < nb_sns; i++) { rv = bdrv_all_has_snapshot(sn_tab[i].name, false, NULL, NULL); - if (rv < 0) - goto fail; + if (rv < 0) { + return NULL; + } if (rv == 1) { if (sn_tab[i].icount != -1ULL && sn_tab[i].icount <= icount @@ -166,14 +169,12 @@ static char *replay_find_nearest_snapshot(int64_t icount, } } } - if (nearest) { - ret = g_strdup(nearest->name); - *snapshot_icount = nearest->icount; + if (!nearest) { + return NULL; } - g_free(sn_tab); -fail: - return ret; + *snapshot_icount = nearest->icount; + return g_strdup(nearest->name); } static void replay_seek(int64_t icount, QEMUTimerCB callback, Error **errp) diff --git a/rust/bindings/chardev-sys/meson.build b/rust/bindings/chardev-sys/meson.build index 458075b806..7af5d74dd5 100644 --- a/rust/bindings/chardev-sys/meson.build +++ b/rust/bindings/chardev-sys/meson.build @@ -1,12 +1,9 @@ _bindgen_chardev_rs = rust.bindgen( args: bindgen_args_common + bindgen_args_data['chardev-sys'].split(), kwargs: bindgen_kwargs) -_chardev_sys_rs = static_library( - 'chardev_sys', - structured_sources(['lib.rs', _bindgen_chardev_rs]), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - dependencies: [glib_sys_rs, common_rs, qom_sys_rs, util_sys_rs], -) + +_chardev_sys_rs = cargo_ws.package('chardev-sys').library( + structured_sources(['lib.rs', _bindgen_chardev_rs])) +cargo_ws.package('chardev-sys').override_dependency(declare_dependency(link_with: _chardev_sys_rs)) chardev_sys_rs = declare_dependency(link_with: [_chardev_sys_rs]) diff --git a/rust/bindings/hwcore-sys/meson.build b/rust/bindings/hwcore-sys/meson.build index 3d51947b4a..6b8d218f69 100644 --- a/rust/bindings/hwcore-sys/meson.build +++ b/rust/bindings/hwcore-sys/meson.build @@ -1,12 +1,9 @@ _bindgen_hwcore_rs = rust.bindgen( args: bindgen_args_common + bindgen_args_data['hwcore-sys'].split(), kwargs: bindgen_kwargs) -_hwcore_sys_rs = static_library( - 'hwcore_sys', - structured_sources(['lib.rs', _bindgen_hwcore_rs]), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - dependencies: [common_rs, glib_sys_rs, qom_sys_rs, util_sys_rs, migration_sys_rs, chardev_sys_rs], -) + +_hwcore_sys_rs = cargo_ws.package('hwcore-sys').library( + structured_sources(['lib.rs', _bindgen_hwcore_rs])) +cargo_ws.package('hwcore-sys').override_dependency(declare_dependency(link_with: _hwcore_sys_rs)) hwcore_sys_rs = declare_dependency(link_with: [_hwcore_sys_rs]) diff --git a/rust/bindings/migration-sys/meson.build b/rust/bindings/migration-sys/meson.build index 9243acba30..91f2967c9b 100644 --- a/rust/bindings/migration-sys/meson.build +++ b/rust/bindings/migration-sys/meson.build @@ -1,12 +1,9 @@ _bindgen_migration_rs = rust.bindgen( args: bindgen_args_common + bindgen_args_data['migration-sys'].split(), kwargs: bindgen_kwargs) -_migration_sys_rs = static_library( - 'migration_sys', - structured_sources(['lib.rs', _bindgen_migration_rs]), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - dependencies: [glib_sys_rs, common_rs, util_sys_rs], -) + +_migration_sys_rs = cargo_ws.package('migration-sys').library( + structured_sources(['lib.rs', _bindgen_migration_rs])) +cargo_ws.package('migration-sys').override_dependency(declare_dependency(link_with: _migration_sys_rs)) migration_sys_rs = declare_dependency(link_with: [_migration_sys_rs]) diff --git a/rust/bindings/qom-sys/meson.build b/rust/bindings/qom-sys/meson.build index 8f8ae7d1bc..540e55eea8 100644 --- a/rust/bindings/qom-sys/meson.build +++ b/rust/bindings/qom-sys/meson.build @@ -1,12 +1,9 @@ _bindgen_qom_rs = rust.bindgen( args: bindgen_args_common + bindgen_args_data['qom-sys'].split(), kwargs: bindgen_kwargs) -_qom_sys_rs = static_library( - 'qom_sys', - structured_sources(['lib.rs', _bindgen_qom_rs]), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - dependencies: [glib_sys_rs, util_sys_rs], -) + +_qom_sys_rs = cargo_ws.package('qom-sys').library( + structured_sources(['lib.rs', _bindgen_qom_rs])) +cargo_ws.package('qom-sys').override_dependency(declare_dependency(link_with: _qom_sys_rs)) qom_sys_rs = declare_dependency(link_with: [_qom_sys_rs]) diff --git a/rust/bindings/system-sys/meson.build b/rust/bindings/system-sys/meson.build index aa5e880114..b1f4770499 100644 --- a/rust/bindings/system-sys/meson.build +++ b/rust/bindings/system-sys/meson.build @@ -1,12 +1,9 @@ _bindgen_system_rs = rust.bindgen( args: bindgen_args_common + bindgen_args_data['system-sys'].split(), kwargs: bindgen_kwargs) -_system_sys_rs = static_library( - 'system_sys', - structured_sources(['lib.rs', _bindgen_system_rs]), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - dependencies: [common_rs, glib_sys_rs, hwcore_sys_rs, migration_sys_rs, qom_sys_rs, util_sys_rs], -) + +_system_sys_rs = cargo_ws.package('system-sys').library( + structured_sources(['lib.rs', _bindgen_system_rs])) +cargo_ws.package('system-sys').override_dependency(declare_dependency(link_with: _system_sys_rs)) system_sys_rs = declare_dependency(link_with: [_system_sys_rs]) diff --git a/rust/bindings/util-sys/meson.build b/rust/bindings/util-sys/meson.build index c37f50a94b..5a9db6152e 100644 --- a/rust/bindings/util-sys/meson.build +++ b/rust/bindings/util-sys/meson.build @@ -1,12 +1,9 @@ _bindgen_util_rs = rust.bindgen( args: bindgen_args_common + bindgen_args_data['util-sys'].split(), kwargs: bindgen_kwargs) -_util_sys_rs = static_library( - 'util_sys', - structured_sources(['lib.rs', _bindgen_util_rs]), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - dependencies: [glib_sys_rs], -) + +_util_sys_rs = cargo_ws.package('util-sys').library( + structured_sources(['lib.rs', _bindgen_util_rs])) +cargo_ws.package('util-sys').override_dependency(declare_dependency(link_with: _util_sys_rs)) util_sys_rs = declare_dependency(link_with: [_util_sys_rs]) diff --git a/rust/bits/meson.build b/rust/bits/meson.build index c0094ffcf3..c64a17ba15 100644 --- a/rust/bits/meson.build +++ b/rust/bits/meson.build @@ -1,8 +1,5 @@ -_bits_rs = static_library( - 'bits', - 'src/lib.rs', - dependencies: [qemu_macros], -) +_bits_rs = cargo_ws.package('bits').library() +cargo_ws.package('bits').override_dependency(declare_dependency(link_with: _bits_rs)) bits_rs = declare_dependency(link_with: _bits_rs) diff --git a/rust/bits/src/lib.rs b/rust/bits/src/lib.rs index d1141f7c88..5769bc761a 100644 --- a/rust/bits/src/lib.rs +++ b/rust/bits/src/lib.rs @@ -215,7 +215,9 @@ macro_rules! bits { impl ::std::fmt::Binary for $struct_name { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { // If no width, use the highest valid bit - let width = f.width().unwrap_or((Self::VALID__.ilog2() + 1) as usize); + let width = f + .width() + .unwrap_or(Self::VALID__.checked_ilog2().map_or(1, |bit| (bit + 1) as usize)); write!(f, "{:0>width$.precision$b}", self.0, width = width, precision = f.precision().unwrap_or(width)) @@ -320,7 +322,7 @@ macro_rules! bits { impl ::std::ops::SubAssign<$struct_name> for $struct_name { fn sub_assign(&mut self, rhs: $struct_name) { - self.0 = self.0 - rhs.0 + self.0 &= !rhs.0 } } @@ -412,6 +414,12 @@ mod test { } } + bits! { + pub struct EmptyMask(u32) { + NONE = 0, + } + } + #[test] pub fn test_not() { assert_eq!( @@ -443,4 +451,16 @@ mod test { InterruptMask::OE | InterruptMask::PE | InterruptMask::FE ); } + + #[test] + pub fn test_sub_assign() { + let mut op1 = InterruptMask::E; + op1 -= InterruptMask::RI; + assert_eq!(op1, InterruptMask::E - InterruptMask::RI); + } + + #[test] + pub fn test_bit_display_empty() { + assert_eq!(format!("{:b}", EmptyMask::NONE), "0"); + } } diff --git a/rust/bql/meson.build b/rust/bql/meson.build index de295d2983..8bdc9b53a2 100644 --- a/rust/bql/meson.build +++ b/rust/bql/meson.build @@ -1,17 +1,10 @@ -_bql_cfg = run_command(rustc_args, - '--config-headers', config_host_h, '--features', files('Cargo.toml'), - capture: true, check: true).stdout().strip().splitlines() - +_bql_cfg = [] if get_option('debug_mutex') _bql_cfg += ['--cfg', 'feature="debug_cell"'] endif -_bql_rs = static_library( - 'bql', - 'src/lib.rs', - rust_args: _bql_cfg, - dependencies: [glib_sys_rs, util_sys_rs], -) +_bql_rs = cargo_ws.package('bql').library(rust_args: _bql_cfg) +cargo_ws.package('bql').override_dependency(declare_dependency(link_with: _bql_rs)) bql_rs = declare_dependency(link_with: [_bql_rs], dependencies: [qemuutil]) diff --git a/rust/chardev/meson.build b/rust/chardev/meson.build index 7b267fd23a..f3462e9209 100644 --- a/rust/chardev/meson.build +++ b/rust/chardev/meson.build @@ -1,8 +1,4 @@ -_chardev_rs = static_library( - 'chardev', - 'src/lib.rs', - link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs], - dependencies: [glib_sys_rs, common_rs, qemu_macros, chardev_sys_rs], -) +_chardev_rs = cargo_ws.package('chardev').library() +cargo_ws.package('chardev').override_dependency(declare_dependency(link_with: _chardev_rs)) chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [chardev, qemuutil]) diff --git a/rust/common/meson.build b/rust/common/meson.build index 57091b18fc..43afe417dc 100644 --- a/rust/common/meson.build +++ b/rust/common/meson.build @@ -1,13 +1,9 @@ _common_cfg = run_command(rustc_args, - '--config-headers', config_host_h, '--features', files('Cargo.toml'), + '--config-headers', config_host_h, files('Cargo.toml'), capture: true, check: true).stdout().strip().splitlines() -_common_rs = static_library( - 'common', - 'src/lib.rs', - rust_args: _common_cfg, - dependencies: [libc_rs, qemu_macros], -) +_common_rs = cargo_ws.package('common').library(rust_args: _common_cfg) +cargo_ws.package('common').override_dependency(declare_dependency(link_with: _common_rs)) common_rs = declare_dependency(link_with: [_common_rs]) diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index 5b319455ee..d303c241e4 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -14,8 +14,7 @@ rust-version.workspace = true [dependencies] glib-sys.workspace = true -bilge = { version = "0.2.0" } -bilge-impl = { version = "0.2.0" } +bitfield-struct = { version = "0.13" } bits = { path = "../../../bits" } common = { path = "../../../common" } util = { path = "../../../util" } diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index 1a1a09e508..92b7d375f7 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -8,8 +8,7 @@ _libpl011_bindings_inc_rs = rust.bindgen( kwargs: bindgen_kwargs, ) -_libpl011_rs = static_library( - 'pl011', +_libpl011_rs = cargo_ws.package('pl011').library( structured_sources( [ 'src/lib.rs', @@ -19,25 +18,6 @@ _libpl011_rs = static_library( ], {'.' : _libpl011_bindings_inc_rs}, ), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - link_with: [ - _util_rs, - _migration_rs, - _bql_rs, - _qom_rs, - _chardev_rs, - _system_rs, - _hwcore_rs, - _trace_rs - ], - dependencies: [ - bilge_rs, - bilge_impl_rs, - bits_rs, - common_rs, - glib_sys_rs, - ], ) rust_devices_ss.add(when: 'CONFIG_X_PL011_RUST', if_true: [declare_dependency( diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs index fa572811b2..a8ab8ad93d 100644 --- a/rust/hw/char/pl011/src/registers.rs +++ b/rust/hw/char/pl011/src/registers.rs @@ -5,12 +5,16 @@ //! Device registers exposed as typed structs which are backed by arbitrary //! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc. +// rustc prefers "constant-like" enums to use upper case names, but that +// is inconsistent in its own way. +#![allow(non_upper_case_globals)] + // For more detail see the PL011 Technical Reference Manual DDI0183: // https://developer.arm.com/documentation/ddi0183/latest/ -use bilge::prelude::*; +use bitfield_struct::bitfield; use bits::bits; -use migration::{impl_vmstate_bitsized, impl_vmstate_forward}; +use migration::impl_vmstate_forward; /// Offset of each register from the base memory address of the device. #[doc(alias = "offset")] @@ -78,14 +82,18 @@ pub enum RegisterOffset { /// The `UARTRSR` register is updated only when a read occurs /// from the `UARTDR` register with the same status information /// that can also be obtained by reading the `UARTDR` register -#[bitsize(8)] -#[derive(Clone, Copy, Default, DebugBits, FromBits)] +#[bitfield(u8)] pub struct Errors { pub framing_error: bool, pub parity_error: bool, pub break_error: bool, pub overrun_error: bool, - _reserved_unpredictable: u4, + #[bits(4)] + _reserved_unpredictable: u8, +} + +impl Errors { + pub const BREAK: Self = Errors::new().with_break_error(true); } /// Data Register, `UARTDR` @@ -93,19 +101,18 @@ pub struct Errors { /// The `UARTDR` register is the data register; write for TX and /// read for RX. It is a 12-bit register, where bits 7..0 are the /// character and bits 11..8 are error bits. -#[bitsize(32)] -#[derive(Clone, Copy, Default, DebugBits, FromBits)] +#[bitfield(u32)] #[doc(alias = "UARTDR")] pub struct Data { pub data: u8, + #[bits(8)] pub errors: Errors, _reserved: u16, } -impl_vmstate_bitsized!(Data); +impl_vmstate_forward!(Data); impl Data { - // bilge is not very const-friendly, unfortunately - pub const BREAK: Self = Self { value: 1 << 10 }; + pub const BREAK: Self = Self::new().with_errors(Errors::BREAK); } /// Receive Status Register / Error Clear Register, `UARTRSR/UARTECR` @@ -119,13 +126,14 @@ impl Data { /// and UARTECR for writes, but really it's a single error status /// register where writing anything to the register clears the error /// bits. -#[bitsize(32)] -#[derive(Clone, Copy, DebugBits, FromBits)] +#[bitfield(u32)] pub struct ReceiveStatusErrorClear { + #[bits(8)] pub errors: Errors, - _reserved_unpredictable: u24, + #[bits(24)] + _reserved_unpredictable: u32, } -impl_vmstate_bitsized!(ReceiveStatusErrorClear); +impl_vmstate_forward!(ReceiveStatusErrorClear); impl ReceiveStatusErrorClear { pub fn set_from_data(&mut self, data: Data) { @@ -138,14 +146,7 @@ impl ReceiveStatusErrorClear { } } -impl Default for ReceiveStatusErrorClear { - fn default() -> Self { - 0.into() - } -} - -#[bitsize(32)] -#[derive(Clone, Copy, DebugBits, FromBits)] +#[bitfield(u32, default = false)] /// Flag Register, `UARTFR` /// /// This has the usual inbound RS232 modem-control signals, plus flags @@ -171,9 +172,10 @@ pub struct Flags { pub transmit_fifo_empty: bool, /// RI: Ring indicator pub ring_indicator: bool, - _reserved_zero_no_modify: u23, + #[bits(23)] + _reserved_zero_no_modify: u32, } -impl_vmstate_bitsized!(Flags); +impl_vmstate_forward!(Flags); impl Flags { pub fn reset(&mut self) { @@ -183,16 +185,14 @@ impl Flags { impl Default for Flags { fn default() -> Self { - let mut ret: Self = 0.into(); // After reset TXFF, RXFF, and BUSY are 0, and TXFE and RXFE are 1 - ret.set_receive_fifo_empty(true); - ret.set_transmit_fifo_empty(true); - ret + Self::from(0) + .with_receive_fifo_empty(true) + .with_transmit_fifo_empty(true) } } -#[bitsize(32)] -#[derive(Clone, Copy, DebugBits, FromBits)] +#[bitfield(u32)] /// Line Control Register, `UARTLCR_H` #[doc(alias = "UARTLCR_H")] pub struct LineControl { @@ -201,48 +201,46 @@ pub struct LineControl { /// PEN: Parity enable pub parity_enabled: bool, /// EPS: Even parity select + #[bits(1)] pub parity: Parity, /// STP2: Two stop bits select pub two_stops_bits: bool, /// FEN: Enable FIFOs + #[bits(1)] pub fifos_enabled: Mode, /// WLEN: Word length in bits /// b11 = 8 bits /// b10 = 7 bits /// b01 = 6 bits /// b00 = 5 bits. + #[bits(2)] pub word_length: WordLength, /// SPS Stick parity select pub sticky_parity: bool, /// 31:8 - Reserved, do not modify, read as zero. - _reserved_zero_no_modify: u24, + #[bits(24)] + _reserved_zero_no_modify: u32, } -impl_vmstate_bitsized!(LineControl); +impl_vmstate_forward!(LineControl); impl LineControl { pub fn reset(&mut self) { // All the bits are cleared to 0 when reset. - *self = 0.into(); + *self = Self::default(); } } -impl Default for LineControl { - fn default() -> Self { - 0.into() - } -} - -#[bitsize(1)] -#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] /// `EPS` "Even parity select", field of [Line Control /// register](LineControl). +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, common::TryInto)] pub enum Parity { Odd = 0, Even = 1, } -#[bitsize(1)] -#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, common::TryInto)] /// `FEN` "Enable FIFOs" or Device mode, field of [Line Control /// register](LineControl). pub enum Mode { @@ -253,8 +251,8 @@ pub enum Mode { FIFO = 1, } -#[bitsize(2)] -#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, common::TryInto)] #[allow(clippy::enum_variant_names)] /// `WLEN` Word length, field of [Line Control register](LineControl). /// @@ -276,9 +274,8 @@ pub enum WordLength { /// The `UARTCR` register is the control register. It contains various /// enable bits, and the bits to write to set the usual outbound RS232 /// modem control signals. All bits reset to 0 except TXE and RXE. -#[bitsize(32)] +#[bitfield(u32, default = false)] #[doc(alias = "UARTCR")] -#[derive(Clone, Copy, DebugBits, FromBits)] pub struct Control { /// `UARTEN` UART enable: 0 = UART is disabled. pub enable_uart: bool, @@ -286,9 +283,10 @@ pub struct Control { /// QEMU does not model this. pub enable_sir: bool, /// `SIRLP` SIR low-power IrDA mode. QEMU does not model this. - pub sir_lowpower_irda_mode: u1, + pub sir_lowpower_irda_mode: bool, /// Reserved, do not modify, read as zero. - _reserved_zero_no_modify: u4, + #[bits(4)] + _reserved_zero_no_modify: u8, /// `LBE` Loopback enable: feed UART output back to the input pub enable_loopback: bool, /// `TXE` Transmit enable @@ -310,21 +308,19 @@ pub struct Control { /// 31:16 - Reserved, do not modify, read as zero. _reserved_zero_no_modify2: u16, } -impl_vmstate_bitsized!(Control); +impl_vmstate_forward!(Control); impl Control { pub fn reset(&mut self) { - *self = 0.into(); - self.set_enable_receive(true); - self.set_enable_transmit(true); + *self = Self::default(); } } impl Default for Control { fn default() -> Self { - let mut ret: Self = 0.into(); - ret.reset(); - ret + Self::from(0) + .with_enable_receive(true) + .with_enable_transmit(true) } } diff --git a/rust/hw/core/meson.build b/rust/hw/core/meson.build index 6d1bfd4d20..da0f13d314 100644 --- a/rust/hw/core/meson.build +++ b/rust/hw/core/meson.build @@ -1,11 +1,5 @@ -_hwcore_rs = static_library( - 'hwcore', - 'src/lib.rs', - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - link_with: [_bql_rs, _chardev_rs, _migration_rs, _qom_rs, _util_rs], - dependencies: [glib_sys_rs, qemu_macros, common_rs, hwcore_sys_rs], -) +_hwcore_rs = cargo_ws.package('hwcore').library() +cargo_ws.package('hwcore').override_dependency(declare_dependency(link_with: _hwcore_rs)) hwcore_rs = declare_dependency(link_with: [_hwcore_rs], dependencies: [qom_rs, hwcore]) diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build index 3bb7ce2a6c..419e8f40b7 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -1,19 +1,4 @@ -_libhpet_rs = static_library( - 'hpet', - files('src/lib.rs'), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - link_with: [ - _util_rs, - _migration_rs, - _bql_rs, - _qom_rs, - _system_rs, - _hwcore_rs, - _trace_rs, - ], - dependencies: [common_rs], -) +_libhpet_rs = cargo_ws.package('hpet').library() rust_devices_ss.add(when: 'CONFIG_X_HPET_RUST', if_true: [declare_dependency( link_whole: [_libhpet_rs], diff --git a/rust/meson.build b/rust/meson.build index b6711fe77d..c0c4e52c1b 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -4,31 +4,7 @@ else message('Rust enabled but it is only used by system emulators.') endif -subproject('anyhow-1-rs', required: true) -subproject('bilge-0.2-rs', required: true) -subproject('bilge-impl-0.2-rs', required: true) -subproject('foreign-0.3-rs', required: true) -subproject('glib-sys-0.21-rs', required: true) -subproject('libc-0.2-rs', required: true) -subproject('probe-0.5-rs', required: true) - -anyhow_rs = dependency('anyhow-1-rs') -bilge_rs = dependency('bilge-0.2-rs') -bilge_impl_rs = dependency('bilge-impl-0.2-rs') -foreign_rs = dependency('foreign-0.3-rs') -glib_sys_rs = dependency('glib-sys-0.21-rs') -libc_rs = dependency('libc-0.2-rs') -probe_rs = dependency('probe-0.5-rs') - -subproject('proc-macro2-1-rs', required: true) -subproject('quote-1-rs', required: true) -subproject('syn-2-rs', required: true) -subproject('attrs-0.2-rs', required: true) - -quote_rs_native = dependency('quote-1-rs', native: true) -syn_rs_native = dependency('syn-2-rs', native: true) -proc_macro2_rs_native = dependency('proc-macro2-1-rs', native: true) -attrs_rs_native = dependency('attrs-0.2-rs', native: true) +cargo_ws = import('rust').workspace() genrs = [] diff --git a/rust/migration/meson.build b/rust/migration/meson.build index 9626689777..b9ba82989e 100644 --- a/rust/migration/meson.build +++ b/rust/migration/meson.build @@ -1,9 +1,5 @@ -_migration_rs = static_library( - 'migration', - 'src/lib.rs', - link_with: [_util_rs, _bql_rs], - dependencies: [common_rs, glib_sys_rs, qemu_macros, migration_sys_rs], -) +_migration_rs = cargo_ws.package('migration').library() +cargo_ws.package('migration').override_dependency(declare_dependency(link_with: _migration_rs)) migration_rs = declare_dependency(link_with: [_migration_rs], dependencies: [bql_rs, migration, qemuutil]) diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs index 63d78b4f27..7409b592a2 100644 --- a/rust/migration/src/vmstate.rs +++ b/rust/migration/src/vmstate.rs @@ -11,8 +11,7 @@ //! migration format for a struct. This is based on the [`VMState`] trait, //! which is defined by all migratable types. //! -//! * [`impl_vmstate_forward`](crate::impl_vmstate_forward), -//! [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), and +//! * [`impl_vmstate_forward`](crate::impl_vmstate_forward) and //! [`impl_vmstate_struct`](crate::impl_vmstate_struct), which help with the //! definition of the [`VMState`] trait (respectively for transparent structs, //! nested structs and `bilge`-defined types) @@ -90,7 +89,7 @@ macro_rules! call_func_with_field { /// The contents of this trait go straight into structs that are parsed by C /// code and used to introspect into other structs. Generally, you don't need /// to implement it except via macros that do it for you, such as -/// `impl_vmstate_bitsized!`. +/// `impl_vmstate_forward!`. pub unsafe trait VMState { /// The base contents of a `VMStateField` (minus the name and offset) for /// the type that is implementing the trait. @@ -133,9 +132,8 @@ pub const fn vmstate_varray_flag(_: PhantomData) -> VMStateFlags /// * an array of any of the above /// /// In order to support other types, the trait `VMState` must be implemented -/// for them. The macros [`impl_vmstate_forward`](crate::impl_vmstate_forward), -/// [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), and -/// [`impl_vmstate_struct`](crate::impl_vmstate_struct) help with this. +/// for them. The macros [`impl_vmstate_forward`](crate::impl_vmstate_forward) +/// and [`impl_vmstate_struct`](crate::impl_vmstate_struct) help with this. /// /// [`BqlCell`]: ../../bql/cell/struct.BqlCell.html /// [`BqlRefCell`]: ../../bql/cell/struct.BqlRefCell.html @@ -211,41 +209,6 @@ impl_vmstate_transparent!(std::pin::Pin where T: VMState); impl_vmstate_transparent!(common::Opaque where T: VMState); impl_vmstate_transparent!(std::mem::ManuallyDrop where T: VMState); -#[macro_export] -macro_rules! impl_vmstate_bitsized { - ($type:ty) => { - unsafe impl $crate::vmstate::VMState for $type { - const BASE: $crate::bindings::VMStateField = - <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt - as ::bilge::prelude::Number>::UnderlyingType - as $crate::vmstate::VMState>::BASE; - const VARRAY_FLAG: $crate::bindings::VMStateFlags = - <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt - as ::bilge::prelude::Number>::UnderlyingType - as $crate::vmstate::VMState>::VARRAY_FLAG; - } - - impl $crate::migratable::ToMigrationState for $type { - type Migrated = <<$type as ::bilge::prelude::Bitsized>::ArbitraryInt - as ::bilge::prelude::Number>::UnderlyingType; - - fn snapshot_migration_state(&self, target: &mut Self::Migrated) -> Result<(), $crate::InvalidError> { - *target = Self::Migrated::from(*self); - Ok(()) - } - - fn restore_migrated_state_mut( - &mut self, - source: Self::Migrated, - version_id: u8, - ) -> Result<(), $crate::InvalidError> { - *self = Self::from(source); - Ok(()) - } - } - }; -} - // Scalar types using predefined VMStateInfos macro_rules! impl_vmstate_scalar { diff --git a/rust/qemu-macros/meson.build b/rust/qemu-macros/meson.build index cdea5bf439..7b25ccefe3 100644 --- a/rust/qemu-macros/meson.build +++ b/rust/qemu-macros/meson.build @@ -1,13 +1,5 @@ -_qemu_macros_rs = rust.proc_macro( - 'qemu_macros', - files('src/lib.rs'), - dependencies: [ - attrs_rs_native, - proc_macro2_rs_native, - quote_rs_native, - syn_rs_native, - ], -) +_qemu_macros_rs = cargo_ws.package('qemu_macros').proc_macro() +cargo_ws.package('qemu_macros').override_dependency(declare_dependency(link_with: _qemu_macros_rs)) qemu_macros = declare_dependency( link_with: _qemu_macros_rs, diff --git a/rust/qom/meson.build b/rust/qom/meson.build index 9865da280c..6736cfb24c 100644 --- a/rust/qom/meson.build +++ b/rust/qom/meson.build @@ -1,9 +1,5 @@ -_qom_rs = static_library( - 'qom', - 'src/lib.rs', - link_with: [_bql_rs, _migration_rs], - dependencies: [common_rs, glib_sys_rs, qemu_macros, qom_sys_rs], -) +_qom_rs = cargo_ws.package('qom').library() +cargo_ws.package('qom').override_dependency(declare_dependency(link_with: _qom_rs)) qom_rs = declare_dependency(link_with: [_qom_rs], dependencies: [qemu_macros, qom, qemuutil]) diff --git a/rust/system/meson.build b/rust/system/meson.build index 89c1f2b84d..b1edcd6bbe 100644 --- a/rust/system/meson.build +++ b/rust/system/meson.build @@ -1,9 +1,5 @@ -_system_rs = static_library( - 'system', - 'src/lib.rs', - link_with: [_bql_rs, _hwcore_rs, _migration_rs, _qom_rs, _util_rs], - dependencies: [glib_sys_rs, common_rs, qemu_macros, system_sys_rs], -) +_system_rs = cargo_ws.package('system').library() +cargo_ws.package('system').override_dependency(declare_dependency(link_with: _system_rs)) system_rs = declare_dependency(link_with: [_system_rs], dependencies: [hwcore]) diff --git a/rust/trace/meson.build b/rust/trace/meson.build index 0071a49cf7..b049f73b54 100644 --- a/rust/trace/meson.build +++ b/rust/trace/meson.build @@ -7,11 +7,8 @@ lib_rs = configure_file( 'MESON_BUILD_ROOT': meson.project_build_root(), }) -_trace_rs = static_library( - 'trace', # Library name, - lib_rs, - trace_rs_targets, # List of generated `.rs` custom targets - dependencies: [libc_rs, probe_rs], -) +_trace_rs = cargo_ws.package('trace').library( + structured_sources([lib_rs, trace_rs_targets])) +cargo_ws.package('trace').override_dependency(declare_dependency(link_with: _trace_rs)) trace_rs = declare_dependency(link_with: _trace_rs) diff --git a/rust/util/meson.build b/rust/util/meson.build index 6d175ae0b0..704501b573 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -1,8 +1,5 @@ -_util_rs = static_library( - 'util', - 'src/lib.rs', - dependencies: [anyhow_rs, libc_rs, foreign_rs, glib_sys_rs, common_rs, util_sys_rs], -) +_util_rs = cargo_ws.package('util').library() +cargo_ws.package('util').override_dependency(declare_dependency(link_with: _util_rs)) util_rs = declare_dependency(link_with: [_util_rs], dependencies: [qemuutil, qom]) diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh index a37acab524..399b2d99e8 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -32,18 +32,13 @@ subprojects=( attrs-0.2-rs berkeley-softfloat-3 berkeley-testfloat-3 - bilge-0.2-rs - bilge-impl-0.2-rs - either-1-rs + bitfield-0.9-rs foreign-0.3-rs glib-sys-0.21-rs - itertools-0.11-rs keycodemapdb libc-0.2-rs libvfio-user probe-0.5-rs - proc-macro-error-1-rs - proc-macro-error-attr-1-rs proc-macro2-1-rs quote-1-rs syn-2-rs diff --git a/scripts/ci/setup/debian/debian-13-ppc64le.yaml b/scripts/ci/setup/debian/debian-13-ppc64le.yaml index e64321357d..9acd99a743 100644 --- a/scripts/ci/setup/debian/debian-13-ppc64le.yaml +++ b/scripts/ci/setup/debian/debian-13-ppc64le.yaml @@ -105,9 +105,6 @@ packages: - openssh-client - pkgconf - python3 - - python3-numpy - - python3-opencv - - python3-pillow - python3-pip - python3-setuptools - python3-sphinx @@ -115,7 +112,6 @@ packages: - python3-tomli - python3-venv - python3-wheel - - python3-yaml - rpm2cpio - rustc - sed diff --git a/scripts/ci/setup/ubuntu/ubuntu-2404-aarch64.yaml b/scripts/ci/setup/ubuntu/ubuntu-2404-aarch64.yaml index 12b0e38a22..5c948109df 100644 --- a/scripts/ci/setup/ubuntu/ubuntu-2404-aarch64.yaml +++ b/scripts/ci/setup/ubuntu/ubuntu-2404-aarch64.yaml @@ -106,9 +106,6 @@ packages: - openssh-client - pkgconf - python3 - - python3-numpy - - python3-opencv - - python3-pillow - python3-pip - python3-setuptools - python3-sphinx @@ -116,7 +113,6 @@ packages: - python3-tomli - python3-venv - python3-wheel - - python3-yaml - rpm2cpio - rustc-1.83 - sed diff --git a/scripts/ci/setup/ubuntu/ubuntu-2404-s390x.yaml b/scripts/ci/setup/ubuntu/ubuntu-2404-s390x.yaml index 56299f4cc2..010b7f86aa 100644 --- a/scripts/ci/setup/ubuntu/ubuntu-2404-s390x.yaml +++ b/scripts/ci/setup/ubuntu/ubuntu-2404-s390x.yaml @@ -104,9 +104,6 @@ packages: - openssh-client - pkgconf - python3 - - python3-numpy - - python3-opencv - - python3-pillow - python3-pip - python3-setuptools - python3-sphinx @@ -114,7 +111,6 @@ packages: - python3-tomli - python3-venv - python3-wheel - - python3-yaml - rpm2cpio - rustc-1.83 - sed diff --git a/scripts/get-wraps-from-cargo-registry.py b/scripts/get-wraps-from-cargo-registry.py index 31eed5c2dd..78b6f1f7ae 100755 --- a/scripts/get-wraps-from-cargo-registry.py +++ b/scripts/get-wraps-from-cargo-registry.py @@ -116,12 +116,6 @@ class UpdateSubprojects: patch_dir = config["wrap-file"]["patch_directory"] patch_dir = os.path.join("packagefiles", patch_dir) _, ver = registry_namever.rsplit("-", 1) - subprocess.run( - ["meson", "rewrite", "kwargs", "set", "project", "/", "version", ver], - cwd=patch_dir, - env=env, - check=True, - ) subprocess.run( ["meson", "subprojects", "download", wrap_name], diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl index 76be402e11..473d133126 100755 --- a/scripts/get_maintainer.pl +++ b/scripts/get_maintainer.pl @@ -365,6 +365,31 @@ sub read_mailmap { close($mailmap_file); } +my %gitlabmap; + +read_gitlabmap(".gitlab-map-auto"); +read_gitlabmap(".gitlab-map-manual"); + +sub read_gitlabmap { + my $mapfile = shift; + open MAP, "<$mapfile" + or die "cannot open $mapfile: $!"; + + while () { + next if /^#/; + next if /^\s*$/; + + if (/^(\S+)\t(.*)$/) { + my $handle = $1; + my $realname = $2; + + $gitlabmap{$realname} = $handle; + } + } + + close MAP; +} + ## use the filenames on the command line or find the filenames in the patchfiles my @files = (); @@ -1068,6 +1093,18 @@ sub email_inuse { return 0; } +sub gitlab_handle { + my $name = shift; + + $name =~ s/"//g; + + my $gitlab_handle; + if (exists $gitlabmap{$name}) { + return $gitlabmap{$name}; + } + return undef; +} + sub push_email_address { my ($line, $role) = @_; @@ -1077,10 +1114,14 @@ sub push_email_address { return 0; } + my $gitlab_handle = gitlab_handle($name); + if (!$email_remove_duplicates) { - push(@email_to, [format_email($name, $address, $email_usename), $role]); + push(@email_to, [format_email($name, $address, $email_usename), + $role, $gitlab_handle]); } elsif (!email_inuse($name, $address)) { - push(@email_to, [format_email($name, $address, $email_usename), $role]); + push(@email_to, [format_email($name, $address, $email_usename), + $role, $gitlab_handle]); $email_hash_name{lc($name)}++ if ($name ne ""); $email_hash_address{lc($address)}++; } @@ -2026,10 +2067,14 @@ sub merge_email { my %saw; for (@_) { - my ($address, $role) = @$_; + my ($address, $role, $gitlab_handle) = @$_; if (!$saw{$address}) { if ($output_roles) { - push(@lines, "$address ($role)"); + if (defined $gitlab_handle) { + push(@lines, "$address ($role, gitlab:\@$gitlab_handle)"); + } else { + push(@lines, "$address ($role)"); + } } else { push(@lines, $address); } diff --git a/scripts/gitlab-map-check b/scripts/gitlab-map-check new file mode 100755 index 0000000000..f72a06756d --- /dev/null +++ b/scripts/gitlab-map-check @@ -0,0 +1,51 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later + +missing=0 +present=0 + +grep -E '^(R|M):' MAINTAINERS | \ +sed -E -e 's/^(M|R): //' -e 's/ <.*//' | \ +sort | uniq > names.txt +while IFS= read -r NAME +do + grep "$NAME" .gitlab-map-{auto,manual} > /dev/null + if test $? != 0 + then + echo "Missing GitLab handle for maintainer '$NAME'" + missing=$(($missing + 1)) + else + present=$(($present + 1)) + fi +done < names.txt +rm -f names.txt + +echo "GitLab handles missing: $missing / present: $present" + +# .gitlab-map-manual is to provide alternate real names +# for cases where name in MAINTAINERS does not match +# the GitLab account real name. As such... + +# ...all gitlab handles in .gitlab-map-manual must also exist +# in .gitlab-map-auto and .... +for HANDLE in $(grep -v '^#' .gitlab-map-manual | awk '{print $1}') +do + grep -E "^$HANDLE\s" .gitlab-map-auto >/dev/null + if test $? != 0 + then + echo "Unexpected GitLab handle '$HANDLE' is not a member" + fi +done + +# ...and all realnames in .gitlab-map-manual must also +# exist in MAINTAINERS +grep -v '^#' .gitlab-map-manual | \ +awk '{ $1 = ""; print substr($0, 2) }' | \ +while IFS= read -r NAME +do + grep -E "\s$NAME\s" MAINTAINERS >/dev/null + if test $? != 0 + then + echo "Unexpected real name '$NAME' is not a maintainer" + fi +done diff --git a/scripts/gitlab-map-update b/scripts/gitlab-map-update new file mode 100755 index 0000000000..c134c226aa --- /dev/null +++ b/scripts/gitlab-map-update @@ -0,0 +1,45 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later + +echo "Checking authentication token status" +glab auth status + +if test $? != 0 +then + echo "ERROR: not authenticated with gitlab.com" + exit 1 +fi + +if ! test -f .gitlab-map-auto +then + echo "ERROR: cannot find existing '.gitlab-map-auto file'" + echo "ERROR: this must be executed from the root of the" + echo "ERROR: git repository checkout" + exit 1 +fi + +fail=0 +echo "Updating member list from qemu-project" +glab api --paginate /groups/qemu-project/members | \ + jq -r -c '.[] | [.username, .name] | @tsv' | \ + grep -v '^group_3038080_bot' > \ + .gitlab-map-auto.tmp +test $? != 0 && fail=1 + +echo "Updating member list from qemu-project/qemu" +glab api --paginate /projects/qemu-project%2fqemu/members | \ + jq -r -c '.[] | [.username, .name] | @tsv' | \ + grep -v 'Duo Developer' >> \ + .gitlab-map-auto.tmp +test $? != 0 && fail=1 + +if test $fail == 0 +then + grep '^#' .gitlab-map-auto > .gitlab-map-auto.new + LC_ALL=C sort .gitlab-map-auto.tmp | uniq >> .gitlab-map-auto.new + mv .gitlab-map-auto.new .gitlab-map-auto + echo "OK: GitLab member list updated" +else + echo "ERROR: Updating member list failed" +fi +rm -f .gitlab-map-auto.tmp diff --git a/scripts/make-release b/scripts/make-release index 5f54b0e793..b5944d3928 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -41,9 +41,7 @@ fi # Only include wraps that are invoked with subproject() SUBPROJECTS="libvfio-user keycodemapdb berkeley-softfloat-3 berkeley-testfloat-3 anyhow-1-rs arbitrary-int-1-rs attrs-0.2-rs bilge-0.2-rs - bilge-impl-0.2-rs either-1-rs foreign-0.3-rs itertools-0.11-rs - libc-0.2-rs probe-0.5-rs proc-macro2-1-rs - proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs + bitfield-0.9-rs foreign-0.3-rs libc-0.2-rs probe-0.5-rs proc-macro2-1-rs quote-1-rs syn-2-rs unicode-ident-1-rs" src="$1" diff --git a/scripts/rust/rustc_args.py b/scripts/rust/rustc_args.py index 8098053720..107b944c16 100644 --- a/scripts/rust/rustc_args.py +++ b/scripts/rust/rustc_args.py @@ -25,10 +25,9 @@ along with this program. If not, see . """ import argparse -from dataclasses import dataclass import logging from pathlib import Path -from typing import Any, Iterable, List, Mapping, Optional, Set +from typing import Any, Iterable, Mapping, Optional, Set try: import tomllib @@ -74,40 +73,6 @@ class CargoTOML: return table -@dataclass -class LintFlag: - flags: List[str] - priority: int - - -def generate_lint_flags(cargo_toml: CargoTOML) -> Iterable[str]: - """Converts Cargo.toml lints to rustc -A/-D/-F/-W flags.""" - - toml_lints = cargo_toml.lints - - lint_list = [] - for k, v in toml_lints.items(): - prefix = "" if k == "rust" else k + "::" - for lint, data in v.items(): - level = data if isinstance(data, str) else data["level"] - priority = 0 if isinstance(data, str) else data.get("priority", 0) - if level == "deny": - flag = "-D" - elif level == "allow": - flag = "-A" - elif level == "warn": - flag = "-W" - elif level == "forbid": - flag = "-F" - else: - raise Exception(f"invalid level {level} for {prefix}{lint}") - lint_list.append(LintFlag(flags=[flag, prefix + lint], priority=priority)) - - lint_list.sort(key=lambda x: x.priority) - for lint in lint_list: - yield from lint.flags - - def generate_cfg_flags(header: str, cargo_toml: CargoTOML) -> Iterable[str]: """Converts defines from config[..].h headers to rustc --cfg flags.""" @@ -154,60 +119,17 @@ def main() -> None: required=False, default=None, ) - parser.add_argument( - "--features", - action="store_true", - dest="features", - help="generate --check-cfg arguments for features", - required=False, - default=None, - ) - parser.add_argument( - "--lints", - action="store_true", - dest="lints", - help="generate arguments from [lints] table", - required=False, - default=None, - ) - parser.add_argument( - "--rustc-version", - metavar="VERSION", - dest="rustc_version", - action="store", - help="version of rustc", - required=False, - default="1.0.0", - ) args = parser.parse_args() if args.verbose: logging.basicConfig(level=logging.DEBUG) logging.debug("args: %s", args) - rustc_version = tuple((int(x) for x in args.rustc_version.split('.')[0:2])) if args.workspace: workspace_cargo_toml = Path(args.workspace, "Cargo.toml").resolve() cargo_toml = CargoTOML(args.cargo_toml, str(workspace_cargo_toml)) else: cargo_toml = CargoTOML(args.cargo_toml, None) - if args.lints: - for tok in generate_lint_flags(cargo_toml): - print(tok) - - if rustc_version >= (1, 80): - if args.lints: - print("--check-cfg") - print("cfg(test)") - for cfg in sorted(cargo_toml.check_cfg): - print("--check-cfg") - print(cfg) - if args.features: - for feature in cargo_toml.get_table("features"): - if feature != "default": - print("--check-cfg") - print(f'cfg(feature,values("{feature}"))') - for header in args.config_headers: for tok in generate_cfg_flags(header, cargo_toml): print(tok) diff --git a/subprojects/.gitignore b/subprojects/.gitignore index 011ce4dc3b..3559b2c758 100644 --- a/subprojects/.gitignore +++ b/subprojects/.gitignore @@ -7,18 +7,12 @@ /libvfio-user /slirp /anyhow-* -/arbitrary-int-* /attrs-* -/bilge-* -/bilge-impl-* -/either-* +/bitfield-struct-* /foreign-* /glib-sys-* -/itertools-* /libc-* /probe-* -/proc-macro-error-* -/proc-macro-error-attr-* /proc-macro* /quote-* /syn-* diff --git a/subprojects/anyhow-1-rs.wrap b/subprojects/anyhow-1-rs.wrap index a69a3645b4..dbb28f4133 100644 --- a/subprojects/anyhow-1-rs.wrap +++ b/subprojects/anyhow-1-rs.wrap @@ -3,5 +3,8 @@ directory = anyhow-1.0.98 source_url = https://crates.io/api/v1/crates/anyhow/1.0.98/download source_filename = anyhow-1.0.98.tar.gz source_hash = e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487 -#method = cargo +method = cargo patch_directory = anyhow-1-rs + +# bump this version number on every change to meson.build or the patches: +# v1 diff --git a/subprojects/arbitrary-int-1-rs.wrap b/subprojects/arbitrary-int-1-rs.wrap deleted file mode 100644 index a1838b20b0..0000000000 --- a/subprojects/arbitrary-int-1-rs.wrap +++ /dev/null @@ -1,10 +0,0 @@ -[wrap-file] -directory = arbitrary-int-1.2.7 -source_url = https://crates.io/api/v1/crates/arbitrary-int/1.2.7/download -source_filename = arbitrary-int-1.2.7.tar.gz -source_hash = c84fc003e338a6f69fbd4f7fe9f92b535ff13e9af8997f3b14b6ddff8b1df46d -#method = cargo -patch_directory = arbitrary-int-1-rs - -# bump this version number on every change to meson.build or the patches: -# v2 diff --git a/subprojects/attrs-0.2-rs.wrap b/subprojects/attrs-0.2-rs.wrap index cd43c91d63..fef89ea936 100644 --- a/subprojects/attrs-0.2-rs.wrap +++ b/subprojects/attrs-0.2-rs.wrap @@ -3,5 +3,4 @@ directory = attrs-0.2.9 source_url = https://crates.io/api/v1/crates/attrs/0.2.9/download source_filename = attrs-0.2.9.tar.gz source_hash = 2a207d40f43de65285f3de0509bb6cb16bc46098864fce957122bbacce327e5f -#method = cargo -patch_directory = attrs-0.2-rs +method = cargo diff --git a/subprojects/bilge-0.2-rs.wrap b/subprojects/bilge-0.2-rs.wrap deleted file mode 100644 index 900bb1497b..0000000000 --- a/subprojects/bilge-0.2-rs.wrap +++ /dev/null @@ -1,10 +0,0 @@ -[wrap-file] -directory = bilge-0.2.0 -source_url = https://crates.io/api/v1/crates/bilge/0.2.0/download -source_filename = bilge-0.2.0.tar.gz -source_hash = dc707ed8ebf81de5cd6c7f48f54b4c8621760926cdf35a57000747c512e67b57 -#method = cargo -patch_directory = bilge-0.2-rs - -# bump this version number on every change to meson.build or the patches: -# v2 diff --git a/subprojects/bilge-impl-0.2-rs.wrap b/subprojects/bilge-impl-0.2-rs.wrap deleted file mode 100644 index 4f84eca1cc..0000000000 --- a/subprojects/bilge-impl-0.2-rs.wrap +++ /dev/null @@ -1,10 +0,0 @@ -[wrap-file] -directory = bilge-impl-0.2.0 -source_url = https://crates.io/api/v1/crates/bilge-impl/0.2.0/download -source_filename = bilge-impl-0.2.0.tar.gz -source_hash = feb11e002038ad243af39c2068c8a72bcf147acf05025dcdb916fcc000adb2d8 -#method = cargo -patch_directory = bilge-impl-0.2-rs - -# bump this version number on every change to meson.build or the patches: -# v2 diff --git a/subprojects/bitfield-struct-0.13-rs.wrap b/subprojects/bitfield-struct-0.13-rs.wrap new file mode 100644 index 0000000000..dcffe0abd8 --- /dev/null +++ b/subprojects/bitfield-struct-0.13-rs.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = bitfield-struct-0.13.0 +source_url = https://crates.io/api/v1/crates/bitfield-struct/0.13.0/download +source_filename = bitfield-struct-0.13.0.tar.gz +source_hash = 3ca6739863c590881f038d033a146c51ddae239186a4327014839fd864f44ed5 +method = cargo diff --git a/subprojects/either-1-rs.wrap b/subprojects/either-1-rs.wrap deleted file mode 100644 index 352e11cfee..0000000000 --- a/subprojects/either-1-rs.wrap +++ /dev/null @@ -1,10 +0,0 @@ -[wrap-file] -directory = either-1.12.0 -source_url = https://crates.io/api/v1/crates/either/1.12.0/download -source_filename = either-1.12.0.tar.gz -source_hash = 3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b -#method = cargo -patch_directory = either-1-rs - -# bump this version number on every change to meson.build or the patches: -# v2 diff --git a/subprojects/foreign-0.3-rs.wrap b/subprojects/foreign-0.3-rs.wrap index 0d218ec2c2..95d450397e 100644 --- a/subprojects/foreign-0.3-rs.wrap +++ b/subprojects/foreign-0.3-rs.wrap @@ -3,5 +3,4 @@ directory = foreign-0.3.1 source_url = https://crates.io/api/v1/crates/foreign/0.3.1/download source_filename = foreign-0.3.1.tar.gz source_hash = 17ca1b5be8c9d320daf386f1809c7acc0cb09accbae795c2001953fa50585846 -#method = cargo -patch_directory = foreign-0.3-rs +method = cargo diff --git a/subprojects/glib-sys-0.21-rs.wrap b/subprojects/glib-sys-0.21-rs.wrap index 313ced731a..ea4d5f66a5 100644 --- a/subprojects/glib-sys-0.21-rs.wrap +++ b/subprojects/glib-sys-0.21-rs.wrap @@ -3,5 +3,4 @@ directory = glib-sys-0.21.2 source_url = https://crates.io/api/v1/crates/glib-sys/0.21.2/download source_filename = glib-sys-0.21.2.tar.gz source_hash = d09d3d0fddf7239521674e57b0465dfbd844632fec54f059f7f56112e3f927e1 -#method = cargo -patch_directory = glib-sys-0.21-rs +method = cargo diff --git a/subprojects/itertools-0.11-rs.wrap b/subprojects/itertools-0.11-rs.wrap deleted file mode 100644 index ee12d0053b..0000000000 --- a/subprojects/itertools-0.11-rs.wrap +++ /dev/null @@ -1,10 +0,0 @@ -[wrap-file] -directory = itertools-0.11.0 -source_url = https://crates.io/api/v1/crates/itertools/0.11.0/download -source_filename = itertools-0.11.0.tar.gz -source_hash = b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57 -#method = cargo -patch_directory = itertools-0.11-rs - -# bump this version number on every change to meson.build or the patches: -# v2 diff --git a/subprojects/libc-0.2-rs.wrap b/subprojects/libc-0.2-rs.wrap index bbe08f8788..6e22557979 100644 --- a/subprojects/libc-0.2-rs.wrap +++ b/subprojects/libc-0.2-rs.wrap @@ -3,5 +3,8 @@ directory = libc-0.2.162 source_url = https://crates.io/api/v1/crates/libc/0.2.162/download source_filename = libc-0.2.162.tar.gz source_hash = 18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398 -#method = cargo +method = cargo patch_directory = libc-0.2-rs + +# bump this version number on every change to meson.build or the patches: +# v1 diff --git a/subprojects/libvduse/libvduse.c b/subprojects/libvduse/libvduse.c index df9ca5e56f..712b97b4c3 100644 --- a/subprojects/libvduse/libvduse.c +++ b/subprojects/libvduse/libvduse.c @@ -902,6 +902,11 @@ static void vduse_queue_enable(VduseVirtq *vq) return; } + if (vq_info.num > VIRTQUEUE_MAX_SIZE) { + fprintf(stderr, "vq[%d] vring num %u exceeds max %u\n", + vq->index, vq_info.num, VIRTQUEUE_MAX_SIZE); + return; + } vq->vring.num = vq_info.num; vq->vring.desc_addr = vq_info.desc_addr; vq->vring.avail_addr = vq_info.driver_addr; diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c index 2c35bddd6f..a74d814bb4 100644 --- a/subprojects/libvhost-user/libvhost-user.c +++ b/subprojects/libvhost-user/libvhost-user.c @@ -1200,6 +1200,12 @@ vu_set_vring_num_exec(VuDev *dev, VhostUserMsg *vmsg) DPRINT("State.index: %u\n", index); DPRINT("State.num: %u\n", num); + + if (index >= dev->max_queues) { + vu_panic(dev, "Invalid vring_num index: %u", index); + return false; + } + dev->vq[index].vring.num = num; return false; @@ -1210,7 +1216,7 @@ vu_set_vring_addr_exec(VuDev *dev, VhostUserMsg *vmsg) { struct vhost_vring_addr addr = vmsg->payload.addr, *vra = &addr; unsigned int index = vra->index; - VuVirtq *vq = &dev->vq[index]; + VuVirtq *vq; DPRINT("vhost_vring_addr:\n"); DPRINT(" index: %d\n", vra->index); @@ -1220,6 +1226,12 @@ vu_set_vring_addr_exec(VuDev *dev, VhostUserMsg *vmsg) DPRINT(" avail_user_addr: 0x%016" PRIx64 "\n", (uint64_t)vra->avail_user_addr); DPRINT(" log_guest_addr: 0x%016" PRIx64 "\n", (uint64_t)vra->log_guest_addr); + if (index >= dev->max_queues) { + vu_panic(dev, "Invalid vring_addr index: %u", index); + return false; + } + + vq = &dev->vq[index]; vq->vra = *vra; vq->vring.flags = vra->flags; vq->vring.log_guest_addr = vra->log_guest_addr; @@ -1256,6 +1268,12 @@ vu_set_vring_base_exec(VuDev *dev, VhostUserMsg *vmsg) DPRINT("State.index: %u\n", index); DPRINT("State.num: %u\n", num); + + if (index >= dev->max_queues) { + vu_panic(dev, "Invalid vring_base index: %u", index); + return false; + } + dev->vq[index].shadow_avail_idx = dev->vq[index].last_avail_idx = num; return false; @@ -1267,6 +1285,14 @@ vu_get_vring_base_exec(VuDev *dev, VhostUserMsg *vmsg) unsigned int index = vmsg->payload.state.index; DPRINT("State.index: %u\n", index); + + if (index >= dev->max_queues) { + vu_panic(dev, "Invalid vring_base index: %u", index); + vmsg->payload.state.num = 0; + vmsg->size = sizeof(vmsg->payload.state); + return true; + } + vmsg->payload.state.num = dev->vq[index].last_avail_idx; vmsg->size = sizeof(vmsg->payload.state); @@ -1353,6 +1379,12 @@ vu_check_queue_inflights(VuDev *dev, VuVirtq *vq) vq->counter = 0; if (unlikely(vq->inflight->used_idx != vq->used_idx)) { + if (vq->inflight->last_batch_head >= vq->inflight->desc_num) { + vu_panic(dev, "vu_check_queue_inflights: last_batch_head %u " + "out of range (desc_num %u)", + vq->inflight->last_batch_head, vq->inflight->desc_num); + return -1; + } vq->inflight->desc[vq->inflight->last_batch_head].inflight = 0; barrier(); @@ -1376,6 +1408,13 @@ vu_check_queue_inflights(VuDev *dev, VuVirtq *vq) for (i = 0; i < vq->inflight->desc_num; i++) { if (vq->inflight->desc[i].inflight) { + /* + * We earlier counted exactly vq->inuse in flight - + * what is going on? + */ + if (vq->resubmit_num >= vq->inuse) { + return -1; + } vq->resubmit_list[vq->resubmit_num].index = i; vq->resubmit_list[vq->resubmit_num].counter = vq->inflight->desc[i].counter; @@ -1998,6 +2037,8 @@ vu_get_inflight_fd(VuDev *dev, VhostUserMsg *vmsg) if (vmsg->size != sizeof(vmsg->payload.inflight)) { vu_panic(dev, "Invalid get_inflight_fd message:%d", vmsg->size); + vmsg_close_fds(vmsg); + vmsg->fd_num = 0; vmsg->payload.inflight.mmap_size = 0; return true; } @@ -2005,6 +2046,15 @@ vu_get_inflight_fd(VuDev *dev, VhostUserMsg *vmsg) num_queues = vmsg->payload.inflight.num_queues; queue_size = vmsg->payload.inflight.queue_size; + if (num_queues > dev->max_queues) { + vu_panic(dev, "Invalid get_inflight_fd num_queues: %"PRId16, + num_queues); + vmsg_close_fds(vmsg); + vmsg->fd_num = 0; + vmsg->payload.inflight.mmap_size = 0; + return true; + } + DPRINT("set_inflight_fd num_queues: %"PRId16"\n", num_queues); DPRINT("set_inflight_fd queue_size: %"PRId16"\n", queue_size); @@ -2052,6 +2102,7 @@ vu_set_inflight_fd(VuDev *dev, VhostUserMsg *vmsg) vmsg->size != sizeof(vmsg->payload.inflight)) { vu_panic(dev, "Invalid set_inflight_fd message size:%d fds:%d", vmsg->size, vmsg->fd_num); + vmsg_close_fds(vmsg); return false; } @@ -2061,6 +2112,13 @@ vu_set_inflight_fd(VuDev *dev, VhostUserMsg *vmsg) num_queues = vmsg->payload.inflight.num_queues; queue_size = vmsg->payload.inflight.queue_size; + if (num_queues > dev->max_queues) { + vu_panic(dev, "Invalid set_inflight_fd num_queues: %"PRId16, + num_queues); + close(fd); + return false; + } + 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); @@ -2071,6 +2129,7 @@ vu_set_inflight_fd(VuDev *dev, VhostUserMsg *vmsg) if (rc == MAP_FAILED) { vu_panic(dev, "set_inflight_fd mmap error: %s", strerror(errno)); + close(fd); return false; } diff --git a/subprojects/packagefiles/anyhow-1-rs/meson.build b/subprojects/packagefiles/anyhow-1-rs/meson.build deleted file mode 100644 index 348bab98b9..0000000000 --- a/subprojects/packagefiles/anyhow-1-rs/meson.build +++ /dev/null @@ -1,33 +0,0 @@ -project('anyhow-1-rs', 'rust', - meson_version: '>=1.5.0', - version: '1.0.98', - license: 'MIT OR Apache-2.0', - default_options: []) - -rustc = meson.get_compiler('rust') - -rust_args = ['--cap-lints', 'allow'] -rust_args += ['--cfg', 'feature="std"'] -if rustc.version().version_compare('<1.65.0') - error('rustc version ' + rustc.version() + ' is unsupported. Please upgrade to at least 1.65.0') -endif -rust_args += [ '--cfg', 'std_backtrace' ] # >= 1.65.0 -if rustc.version().version_compare('<1.81.0') - rust_args += [ '--cfg', 'anyhow_no_core_error' ] -endif - -_anyhow_rs = static_library( - 'anyhow', - files('src/lib.rs'), - gnu_symbol_visibility: 'hidden', - override_options: ['rust_std=2018', 'build.rust_std=2018'], - rust_abi: 'rust', - rust_args: rust_args, - dependencies: [], -) - -anyhow_dep = declare_dependency( - link_with: _anyhow_rs, -) - -meson.override_dependency('anyhow-1-rs', anyhow_dep) diff --git a/subprojects/packagefiles/anyhow-1-rs/meson/meson.build b/subprojects/packagefiles/anyhow-1-rs/meson/meson.build new file mode 100644 index 0000000000..91c46539c3 --- /dev/null +++ b/subprojects/packagefiles/anyhow-1-rs/meson/meson.build @@ -0,0 +1,9 @@ +rustc = meson.get_compiler('rust') + +if rustc.version().version_compare('<1.65.0') + error('rustc version ' + rustc.version() + ' is unsupported. Please upgrade to at least 1.65.0') +endif +extra_args += [ '--cfg', 'std_backtrace' ] # >= 1.65.0 +if rustc.version().version_compare('<1.81.0') + extra_args += [ '--cfg', 'anyhow_no_core_error' ] +endif diff --git a/subprojects/packagefiles/arbitrary-int-1-rs/meson.build b/subprojects/packagefiles/arbitrary-int-1-rs/meson.build deleted file mode 100644 index 00733d1faa..0000000000 --- a/subprojects/packagefiles/arbitrary-int-1-rs/meson.build +++ /dev/null @@ -1,21 +0,0 @@ -project('arbitrary-int-1-rs', 'rust', - meson_version: '>=1.5.0', - version: '1.2.7', - license: 'MIT', - default_options: []) - -_arbitrary_int_rs = static_library( - 'arbitrary_int', - files('src/lib.rs'), - gnu_symbol_visibility: 'hidden', - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_args: ['--cap-lints', 'allow'], - rust_abi: 'rust', - dependencies: [], -) - -arbitrary_int_dep = declare_dependency( - link_with: _arbitrary_int_rs, -) - -meson.override_dependency('arbitrary-int-1-rs', arbitrary_int_dep) diff --git a/subprojects/packagefiles/attrs-0.2-rs/meson.build b/subprojects/packagefiles/attrs-0.2-rs/meson.build deleted file mode 100644 index ee575476cb..0000000000 --- a/subprojects/packagefiles/attrs-0.2-rs/meson.build +++ /dev/null @@ -1,33 +0,0 @@ -project('attrs-0.2-rs', 'rust', - meson_version: '>=1.5.0', - version: '0.2.9', - license: 'MIT OR Apache-2.0', - default_options: []) - -subproject('proc-macro2-1-rs', required: true) -subproject('syn-2-rs', required: true) - -proc_macro2_dep = dependency('proc-macro2-1-rs', native: true) -syn_dep = dependency('syn-2-rs', native: true) - -_attrs_rs = static_library( - 'attrs', - files('src/lib.rs'), - gnu_symbol_visibility: 'hidden', - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - rust_args: [ - '--cap-lints', 'allow', - ], - dependencies: [ - proc_macro2_dep, - syn_dep, - ], - native: true, -) - -attrs_dep = declare_dependency( - link_with: _attrs_rs, -) - -meson.override_dependency('attrs-0.2-rs', attrs_dep, native: true) diff --git a/subprojects/packagefiles/bilge-0.2-rs/meson.build b/subprojects/packagefiles/bilge-0.2-rs/meson.build deleted file mode 100644 index ce13d0fe80..0000000000 --- a/subprojects/packagefiles/bilge-0.2-rs/meson.build +++ /dev/null @@ -1,31 +0,0 @@ -project( - 'bilge-0.2-rs', - 'rust', - meson_version: '>=1.5.0', - version : '0.2.0', - license : 'MIT or Apache-2.0', -) - -subproject('arbitrary-int-1-rs', required: true) -subproject('bilge-impl-0.2-rs', required: true) - -arbitrary_int_dep = dependency('arbitrary-int-1-rs') -bilge_impl_dep = dependency('bilge-impl-0.2-rs') - -lib = static_library( - 'bilge', - 'src/lib.rs', - override_options : ['rust_std=2021', 'build.rust_std=2021'], - rust_abi : 'rust', - rust_args: ['--cap-lints', 'allow'], - dependencies: [ - arbitrary_int_dep, - bilge_impl_dep, - ], -) - -bilge_dep = declare_dependency( - link_with : [lib], -) - -meson.override_dependency('bilge-0.2-rs', bilge_dep) diff --git a/subprojects/packagefiles/bilge-impl-0.2-rs/meson.build b/subprojects/packagefiles/bilge-impl-0.2-rs/meson.build deleted file mode 100644 index 04617b875c..0000000000 --- a/subprojects/packagefiles/bilge-impl-0.2-rs/meson.build +++ /dev/null @@ -1,44 +0,0 @@ -project('bilge-impl-0.2-rs', 'rust', - meson_version: '>=1.5.0', - version: '0.2.0', - license: 'MIT OR Apache-2.0', - default_options: []) - -subproject('itertools-0.11-rs', required: true) -subproject('proc-macro-error-attr-1-rs', required: true) -subproject('proc-macro-error-1-rs', required: true) -subproject('quote-1-rs', required: true) -subproject('syn-2-rs', required: true) -subproject('proc-macro2-1-rs', required: true) - -itertools_dep = dependency('itertools-0.11-rs', native: true) -proc_macro_error_attr_dep = dependency('proc-macro-error-attr-1-rs', native: true) -proc_macro_error_dep = dependency('proc-macro-error-1-rs', native: true) -quote_dep = dependency('quote-1-rs', native: true) -syn_dep = dependency('syn-2-rs', native: true) -proc_macro2_dep = dependency('proc-macro2-1-rs', native: true) - -rust = import('rust') - -_bilge_impl_rs = rust.proc_macro( - 'bilge_impl', - files('src/lib.rs'), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_args: [ - '--cap-lints', 'allow', - ], - dependencies: [ - itertools_dep, - proc_macro_error_attr_dep, - proc_macro_error_dep, - quote_dep, - syn_dep, - proc_macro2_dep, - ], -) - -bilge_impl_dep = declare_dependency( - link_with: _bilge_impl_rs, -) - -meson.override_dependency('bilge-impl-0.2-rs', bilge_impl_dep) diff --git a/subprojects/packagefiles/either-1-rs/meson.build b/subprojects/packagefiles/either-1-rs/meson.build deleted file mode 100644 index 04c96cc5fb..0000000000 --- a/subprojects/packagefiles/either-1-rs/meson.build +++ /dev/null @@ -1,26 +0,0 @@ -project('either-1-rs', 'rust', - meson_version: '>=1.5.0', - version: '1.12.0', - license: 'MIT OR Apache-2.0', - default_options: []) - -_either_rs = static_library( - 'either', - files('src/lib.rs'), - gnu_symbol_visibility: 'hidden', - override_options: ['rust_std=2018', 'build.rust_std=2018'], - rust_abi: 'rust', - rust_args: [ - '--cap-lints', 'allow', - '--cfg', 'feature="use_std"', - '--cfg', 'feature="use_alloc"', - ], - dependencies: [], - native: true, -) - -either_dep = declare_dependency( - link_with: _either_rs, -) - -meson.override_dependency('either-1-rs', either_dep, native: true) diff --git a/subprojects/packagefiles/foreign-0.3-rs/meson.build b/subprojects/packagefiles/foreign-0.3-rs/meson.build deleted file mode 100644 index 0901c02c52..0000000000 --- a/subprojects/packagefiles/foreign-0.3-rs/meson.build +++ /dev/null @@ -1,26 +0,0 @@ -project('foreign-0.3-rs', 'rust', - meson_version: '>=1.5.0', - version: '0.2.0', - license: 'MIT OR Apache-2.0', - default_options: []) - -subproject('libc-0.2-rs', required: true) -libc_rs = dependency('libc-0.2-rs') - -_foreign_rs = static_library( - 'foreign', - files('src/lib.rs'), - gnu_symbol_visibility: 'hidden', - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - rust_args: [ - '--cap-lints', 'allow', - ], - dependencies: [libc_rs], -) - -foreign_dep = declare_dependency( - link_with: _foreign_rs, -) - -meson.override_dependency('foreign-0.3-rs', foreign_dep) diff --git a/subprojects/packagefiles/glib-sys-0.21-rs/meson.build b/subprojects/packagefiles/glib-sys-0.21-rs/meson.build deleted file mode 100644 index 8c5483311e..0000000000 --- a/subprojects/packagefiles/glib-sys-0.21-rs/meson.build +++ /dev/null @@ -1,33 +0,0 @@ -project('glib-sys-0.21-rs', 'rust', - meson_version: '>=1.5.0', - version: '0.21.2', - license: 'MIT', - default_options: []) - -subproject('libc-0.2-rs', required: true) -libc_rs = dependency('libc-0.2-rs') - -_glib_sys_rs = static_library( - 'glib_sys', - files('src/lib.rs'), - gnu_symbol_visibility: 'hidden', - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - rust_args: [ - '--cap-lints', 'allow', - '--cfg', 'feature="v2_66"', - '--cfg', 'feature="v2_64"', - '--cfg', 'feature="v2_62"', - '--cfg', 'feature="v2_60"', - '--cfg', 'feature="v2_58"', - ], - # should also link with glib; don't bother doing it here since all - # QEMU targets have it - dependencies: [libc_rs], -) - -glib_sys_dep = declare_dependency( - link_with: _glib_sys_rs, -) - -meson.override_dependency('glib-sys-0.21-rs', glib_sys_dep) diff --git a/subprojects/packagefiles/itertools-0.11-rs/meson.build b/subprojects/packagefiles/itertools-0.11-rs/meson.build deleted file mode 100644 index 2a3fbe9ee5..0000000000 --- a/subprojects/packagefiles/itertools-0.11-rs/meson.build +++ /dev/null @@ -1,32 +0,0 @@ -project('itertools-0.11-rs', 'rust', - meson_version: '>=1.5.0', - version: '0.11.0', - license: 'MIT OR Apache-2.0', - default_options: []) - -subproject('either-1-rs', required: true) - -either_dep = dependency('either-1-rs', native: true) - -_itertools_rs = static_library( - 'itertools', - files('src/lib.rs'), - gnu_symbol_visibility: 'hidden', - override_options: ['rust_std=2018', 'build.rust_std=2018'], - rust_abi: 'rust', - rust_args: [ - '--cap-lints', 'allow', - '--cfg', 'feature="use_std"', - '--cfg', 'feature="use_alloc"', - ], - dependencies: [ - either_dep, - ], - native: true, -) - -itertools_dep = declare_dependency( - link_with: _itertools_rs, -) - -meson.override_dependency('itertools-0.11-rs', itertools_dep, native: true) diff --git a/subprojects/packagefiles/libc-0.2-rs/meson.build b/subprojects/packagefiles/libc-0.2-rs/meson.build deleted file mode 100644 index ac4f80dba9..0000000000 --- a/subprojects/packagefiles/libc-0.2-rs/meson.build +++ /dev/null @@ -1,37 +0,0 @@ -project('libc-0.2-rs', 'rust', - meson_version: '>=1.5.0', - version: '0.2.162', - license: 'MIT OR Apache-2.0', - default_options: []) - -_libc_rs = static_library( - 'libc', - files('src/lib.rs'), - gnu_symbol_visibility: 'hidden', - override_options: ['rust_std=2015', 'build.rust_std=2015'], - rust_abi: 'rust', - rust_args: [ - '--cap-lints', 'allow', - '--cfg', 'freebsd11', - '--cfg', 'libc_priv_mod_use', - '--cfg', 'libc_union', - '--cfg', 'libc_const_size_of', - '--cfg', 'libc_align', - '--cfg', 'libc_int128', - '--cfg', 'libc_core_cvoid', - '--cfg', 'libc_packedN', - '--cfg', 'libc_cfg_target_vendor', - '--cfg', 'libc_non_exhaustive', - '--cfg', 'libc_long_array', - '--cfg', 'libc_ptr_addr_of', - '--cfg', 'libc_underscore_const_names', - '--cfg', 'libc_const_extern_fn', - ], - dependencies: [], -) - -libc_dep = declare_dependency( - link_with: _libc_rs, -) - -meson.override_dependency('libc-0.2-rs', libc_dep) diff --git a/subprojects/packagefiles/libc-0.2-rs/meson/meson.build b/subprojects/packagefiles/libc-0.2-rs/meson/meson.build new file mode 100644 index 0000000000..db9480ada1 --- /dev/null +++ b/subprojects/packagefiles/libc-0.2-rs/meson/meson.build @@ -0,0 +1,16 @@ +extra_args += [ + '--cfg', 'freebsd11', + '--cfg', 'libc_priv_mod_use', + '--cfg', 'libc_union', + '--cfg', 'libc_const_size_of', + '--cfg', 'libc_align', + '--cfg', 'libc_int128', + '--cfg', 'libc_core_cvoid', + '--cfg', 'libc_packedN', + '--cfg', 'libc_cfg_target_vendor', + '--cfg', 'libc_non_exhaustive', + '--cfg', 'libc_long_array', + '--cfg', 'libc_ptr_addr_of', + '--cfg', 'libc_underscore_const_names', + '--cfg', 'libc_const_extern_fn', +] diff --git a/subprojects/packagefiles/probe-0.5-rs/meson.build b/subprojects/packagefiles/probe-0.5-rs/meson.build deleted file mode 100644 index e6ea69533b..0000000000 --- a/subprojects/packagefiles/probe-0.5-rs/meson.build +++ /dev/null @@ -1,22 +0,0 @@ -project('probe-0.5-rs', 'rust', - meson_version: '>=1.5.0', - version: '0.5.2', - license: 'Apache-2.0 OR MIT', - default_options: []) - -_probe_rs = static_library( - 'probe', - files('src/lib.rs'), - gnu_symbol_visibility: 'hidden', - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - rust_args: [ - '--cap-lints', 'allow', - ], -) - -probe_deps = declare_dependency( - link_with: _probe_rs, -) - -meson.override_dependency('probe-0.5-rs', probe_deps) diff --git a/subprojects/packagefiles/proc-macro-error-1-rs/meson.build b/subprojects/packagefiles/proc-macro-error-1-rs/meson.build deleted file mode 100644 index 8ba558e133..0000000000 --- a/subprojects/packagefiles/proc-macro-error-1-rs/meson.build +++ /dev/null @@ -1,41 +0,0 @@ -project('proc-macro-error-1-rs', 'rust', - meson_version: '>=1.5.0', - version: '1.0.4', - license: 'MIT OR Apache-2.0', - default_options: []) - -subproject('proc-macro-error-attr-1-rs', required: true) -subproject('quote-1-rs', required: true) -subproject('syn-2-rs', required: true) -subproject('proc-macro2-1-rs', required: true) - -proc_macro_error_attr_dep = dependency('proc-macro-error-attr-1-rs', native: true) -proc_macro2_dep = dependency('proc-macro2-1-rs', native: true) -quote_dep = dependency('quote-1-rs', native: true) -syn_dep = dependency('syn-2-rs', native: true) - -_proc_macro_error_rs = static_library( - 'proc_macro_error', - files('src/lib.rs'), - override_options: ['rust_std=2018', 'build.rust_std=2018'], - rust_abi: 'rust', - rust_args: [ - '--cap-lints', 'allow', - '--cfg', 'use_fallback', - '--cfg', 'feature="syn-error"', - '-A', 'non_fmt_panics' - ], - dependencies: [ - proc_macro_error_attr_dep, - proc_macro2_dep, - quote_dep, - syn_dep, - ], - native: true, -) - -proc_macro_error_dep = declare_dependency( - link_with: _proc_macro_error_rs, -) - -meson.override_dependency('proc-macro-error-1-rs', proc_macro_error_dep, native: true) diff --git a/subprojects/packagefiles/proc-macro-error-attr-1-rs/meson.build b/subprojects/packagefiles/proc-macro-error-attr-1-rs/meson.build deleted file mode 100644 index a85d7c0714..0000000000 --- a/subprojects/packagefiles/proc-macro-error-attr-1-rs/meson.build +++ /dev/null @@ -1,31 +0,0 @@ -project('proc-macro-error-attr-1-rs', 'rust', - meson_version: '>=1.5.0', - version: '1.12.0', - license: 'MIT OR Apache-2.0', - default_options: []) - -subproject('proc-macro2-1-rs', required: true) -subproject('quote-1-rs', required: true) - -proc_macro2_dep = dependency('proc-macro2-1-rs', native: true) -quote_dep = dependency('quote-1-rs', native: true) - -rust = import('rust') -_proc_macro_error_attr_rs = rust.proc_macro( - 'proc_macro_error_attr', - files('src/lib.rs'), - override_options: ['rust_std=2018', 'build.rust_std=2018'], - rust_args: [ - '--cap-lints', 'allow', - ], - dependencies: [ - proc_macro2_dep, - quote_dep, - ], -) - -proc_macro_error_attr_dep = declare_dependency( - link_with: _proc_macro_error_attr_rs, -) - -meson.override_dependency('proc-macro-error-attr-1-rs', proc_macro_error_attr_dep, native: true) diff --git a/subprojects/packagefiles/proc-macro2-1-rs/meson.build b/subprojects/packagefiles/proc-macro2-1-rs/meson.build deleted file mode 100644 index ba7de07029..0000000000 --- a/subprojects/packagefiles/proc-macro2-1-rs/meson.build +++ /dev/null @@ -1,35 +0,0 @@ -project('proc-macro2-1-rs', 'rust', - meson_version: '>=1.5.0', - version: '1.0.95', - license: 'MIT OR Apache-2.0', - default_options: []) - -subproject('unicode-ident-1-rs', required: true) - -unicode_ident_dep = dependency('unicode-ident-1-rs', native: true) - -_proc_macro2_rs = static_library( - 'proc_macro2', - files('src/lib.rs'), - gnu_symbol_visibility: 'hidden', - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - rust_args: [ - '--cap-lints', 'allow', - '--cfg', 'feature="proc-macro"', - '--cfg', 'no_literal_byte_character', - '--cfg', 'no_literal_c_string', - '--cfg', 'no_source_text', - '--cfg', 'wrap_proc_macro', - ], - dependencies: [ - unicode_ident_dep, - ], - native: true, -) - -proc_macro2_dep = declare_dependency( - link_with: _proc_macro2_rs, -) - -meson.override_dependency('proc-macro2-1-rs', proc_macro2_dep, native: true) diff --git a/subprojects/packagefiles/proc-macro2-1-rs/meson/meson.build b/subprojects/packagefiles/proc-macro2-1-rs/meson/meson.build new file mode 100644 index 0000000000..09712bb0b0 --- /dev/null +++ b/subprojects/packagefiles/proc-macro2-1-rs/meson/meson.build @@ -0,0 +1,6 @@ +extra_args = [ + '--cfg', 'no_literal_byte_character', + '--cfg', 'no_literal_c_string', + '--cfg', 'no_source_text', + '--cfg', 'wrap_proc_macro', +] diff --git a/subprojects/packagefiles/quote-1-rs/meson.build b/subprojects/packagefiles/quote-1-rs/meson.build deleted file mode 100644 index bf41fad99b..0000000000 --- a/subprojects/packagefiles/quote-1-rs/meson.build +++ /dev/null @@ -1,31 +0,0 @@ -project('quote-1-rs', 'rust', - meson_version: '>=1.5.0', - version: '1.12.0', - license: 'MIT OR Apache-2.0', - default_options: []) - -subproject('proc-macro2-1-rs', required: true) - -proc_macro2_dep = dependency('proc-macro2-1-rs', native: true) - -_quote_rs = static_library( - 'quote', - files('src/lib.rs'), - gnu_symbol_visibility: 'hidden', - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - rust_args: [ - '--cap-lints', 'allow', - '--cfg', 'feature="proc-macro"', - ], - dependencies: [ - proc_macro2_dep, - ], - native: true, -) - -quote_dep = declare_dependency( - link_with: _quote_rs, -) - -meson.override_dependency('quote-1-rs', quote_dep, native: true) diff --git a/subprojects/packagefiles/syn-2-rs/meson.build b/subprojects/packagefiles/syn-2-rs/meson.build deleted file mode 100644 index 3e6dc318a9..0000000000 --- a/subprojects/packagefiles/syn-2-rs/meson.build +++ /dev/null @@ -1,43 +0,0 @@ -project('syn-2-rs', 'rust', - meson_version: '>=1.5.0', - version: '2.0.104', - license: 'MIT OR Apache-2.0', - default_options: []) - -subproject('proc-macro2-1-rs', required: true) -subproject('quote-1-rs', required: true) -subproject('unicode-ident-1-rs', required: true) - -proc_macro2_dep = dependency('proc-macro2-1-rs', native: true) -quote_dep = dependency('quote-1-rs', native: true) -unicode_ident_dep = dependency('unicode-ident-1-rs', native: true) - -_syn_rs = static_library( - 'syn', - files('src/lib.rs'), - gnu_symbol_visibility: 'hidden', - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - rust_args: [ - '--cap-lints', 'allow', - '--cfg', 'feature="full"', - '--cfg', 'feature="derive"', - '--cfg', 'feature="parsing"', - '--cfg', 'feature="printing"', - '--cfg', 'feature="clone-impls"', - '--cfg', 'feature="proc-macro"', - '--cfg', 'feature="extra-traits"', - ], - dependencies: [ - quote_dep, - proc_macro2_dep, - unicode_ident_dep, - ], - native: true, -) - -syn_dep = declare_dependency( - link_with: _syn_rs, -) - -meson.override_dependency('syn-2-rs', syn_dep, native: true) diff --git a/subprojects/packagefiles/unicode-ident-1-rs/meson.build b/subprojects/packagefiles/unicode-ident-1-rs/meson.build deleted file mode 100644 index 11a5dab97d..0000000000 --- a/subprojects/packagefiles/unicode-ident-1-rs/meson.build +++ /dev/null @@ -1,22 +0,0 @@ -project('unicode-ident-1-rs', 'rust', - meson_version: '>=1.5.0', - version: '1.0.12', - license: '(MIT OR Apache-2.0) AND Unicode-DFS-2016', - default_options: []) - -_unicode_ident_rs = static_library( - 'unicode_ident', - files('src/lib.rs'), - gnu_symbol_visibility: 'hidden', - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - rust_args: ['--cap-lints', 'allow'], - dependencies: [], - native: true, -) - -unicode_ident_dep = declare_dependency( - link_with: _unicode_ident_rs, -) - -meson.override_dependency('unicode-ident-1-rs', unicode_ident_dep, native: true) diff --git a/subprojects/probe-0.5-rs.wrap b/subprojects/probe-0.5-rs.wrap index 73229ee1c2..72e771ec78 100644 --- a/subprojects/probe-0.5-rs.wrap +++ b/subprojects/probe-0.5-rs.wrap @@ -3,5 +3,4 @@ directory = probe-0.5.2 source_url = https://crates.io/api/v1/crates/probe/0.5.2/download source_filename = probe-0.5.2.tar.gz source_hash = 136558b6e1ebaecc92755d0ffaf9421f519531bed30cc2ad23b22cb00965cc5e -#method = cargo -patch_directory = probe-0.5-rs +method = cargo diff --git a/subprojects/proc-macro-error-1-rs.wrap b/subprojects/proc-macro-error-1-rs.wrap deleted file mode 100644 index 59f892f782..0000000000 --- a/subprojects/proc-macro-error-1-rs.wrap +++ /dev/null @@ -1,10 +0,0 @@ -[wrap-file] -directory = proc-macro-error-1.0.4 -source_url = https://crates.io/api/v1/crates/proc-macro-error/1.0.4/download -source_filename = proc-macro-error-1.0.4.tar.gz -source_hash = da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c -#method = cargo -patch_directory = proc-macro-error-1-rs - -# bump this version number on every change to meson.build or the patches: -# v2 diff --git a/subprojects/proc-macro-error-attr-1-rs.wrap b/subprojects/proc-macro-error-attr-1-rs.wrap deleted file mode 100644 index 5aeb224a10..0000000000 --- a/subprojects/proc-macro-error-attr-1-rs.wrap +++ /dev/null @@ -1,10 +0,0 @@ -[wrap-file] -directory = proc-macro-error-attr-1.0.4 -source_url = https://crates.io/api/v1/crates/proc-macro-error-attr/1.0.4/download -source_filename = proc-macro-error-attr-1.0.4.tar.gz -source_hash = a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869 -#method = cargo -patch_directory = proc-macro-error-attr-1-rs - -# bump this version number on every change to meson.build or the patches: -# v2 diff --git a/subprojects/proc-macro2-1-rs.wrap b/subprojects/proc-macro2-1-rs.wrap index 0f06cd8e11..676d1a3a8c 100644 --- a/subprojects/proc-macro2-1-rs.wrap +++ b/subprojects/proc-macro2-1-rs.wrap @@ -3,8 +3,8 @@ directory = proc-macro2-1.0.95 source_url = https://crates.io/api/v1/crates/proc-macro2/1.0.95/download source_filename = proc-macro2-1.0.95.0.tar.gz source_hash = 02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778 -#method = cargo +method = cargo patch_directory = proc-macro2-1-rs # bump this version number on every change to meson.build or the patches: -# v2 +# v3 diff --git a/subprojects/quote-1-rs.wrap b/subprojects/quote-1-rs.wrap index 8b721dfa00..5f38f56499 100644 --- a/subprojects/quote-1-rs.wrap +++ b/subprojects/quote-1-rs.wrap @@ -3,8 +3,7 @@ directory = quote-1.0.36 source_url = https://crates.io/api/v1/crates/quote/1.0.36/download source_filename = quote-1.0.36.0.tar.gz source_hash = 0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7 -#method = cargo -patch_directory = quote-1-rs +method = cargo # bump this version number on every change to meson.build or the patches: # v2 diff --git a/subprojects/syn-2-rs.wrap b/subprojects/syn-2-rs.wrap index 1e5e9d9fb6..4b97bed258 100644 --- a/subprojects/syn-2-rs.wrap +++ b/subprojects/syn-2-rs.wrap @@ -3,8 +3,7 @@ directory = syn-2.0.104 source_url = https://crates.io/api/v1/crates/syn/2.0.104/download source_filename = syn-2.0.104.0.tar.gz source_hash = 17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40 -#method = cargo -patch_directory = syn-2-rs +method = cargo # bump this version number on every change to meson.build or the patches: # v2 diff --git a/subprojects/unicode-ident-1-rs.wrap b/subprojects/unicode-ident-1-rs.wrap index 50988f612e..90aae02ba0 100644 --- a/subprojects/unicode-ident-1-rs.wrap +++ b/subprojects/unicode-ident-1-rs.wrap @@ -3,8 +3,7 @@ directory = unicode-ident-1.0.12 source_url = https://crates.io/api/v1/crates/unicode-ident/1.0.12/download source_filename = unicode-ident-1.0.12.tar.gz source_hash = 3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b -#method = cargo -patch_directory = unicode-ident-1-rs +method = cargo # bump this version number on every change to meson.build or the patches: # v2 diff --git a/system/cpus.c b/system/cpus.c index 97e5a5edee..f37e215c58 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -23,13 +23,8 @@ */ #include "qemu/osdep.h" -#include "monitor/monitor.h" #include "qemu/coroutine-tls.h" #include "qapi/error.h" -#include "qapi/qapi-commands-machine.h" -#include "qapi/qapi-commands-misc.h" -#include "qapi/qapi-events-run-state.h" -#include "qapi/qmp/qerror.h" #include "exec/gdbstub.h" #include "accel/accel-cpu-ops.h" #include "system/hw_accel.h" @@ -39,11 +34,9 @@ #include "qemu/plugin.h" #include "system/cpus.h" #include "qemu/guest-random.h" -#include "hw/core/nmi.h" #include "system/physmem.h" #include "system/replay.h" #include "system/runstate.h" -#include "migration/misc.h" #include "system/cpu-timers.h" #include "system/whpx.h" #include "hw/core/boards.h" @@ -278,58 +271,6 @@ void cpu_interrupt(CPUState *cpu, int mask) cpus_accel->handle_interrupt(cpu, mask); } -/* - * True if the vm was previously suspended, and has not been woken or reset. - */ -static int vm_was_suspended; - -void vm_set_suspended(bool suspended) -{ - vm_was_suspended = suspended; -} - -bool vm_get_suspended(void) -{ - return vm_was_suspended; -} - -static int do_vm_stop(RunState state, bool send_stop) -{ - int ret = 0; - RunState oldstate = runstate_get(); - - if (runstate_is_live(oldstate)) { - vm_was_suspended = (oldstate == RUN_STATE_SUSPENDED); - runstate_set(state); - cpu_disable_ticks(); - if (oldstate == RUN_STATE_RUNNING) { - pause_all_vcpus(); - } - ret = vm_state_notify(0, state); - if (send_stop) { - qapi_event_send_stop(); - } - } - - bdrv_drain_all(); - /* - * Even if vm_state_notify() return failure, - * it would be better to flush as before. - */ - ret |= bdrv_flush_all(); - trace_vm_stop_flush_all(ret); - - return ret; -} - -/* Special vm_stop() variant for terminating the process. Historically clients - * did not expect a QMP STOP event and so we need to retain compatibility. - */ -int vm_shutdown(void) -{ - return do_vm_stop(RUN_STATE_SHUTDOWN, false); -} - bool cpu_can_run(CPUState *cpu) { if (cpu->stop) { @@ -740,192 +681,3 @@ void cpu_stop_current(void) cpu_exit(current_cpu); } } - -int vm_stop(RunState state) -{ - if (qemu_in_vcpu_thread()) { - qemu_system_vmstop_request_prepare(); - qemu_system_vmstop_request(state); - /* - * FIXME: should not return to device code in case - * vm_stop() has been requested. - */ - cpu_stop_current(); - return 0; - } - - return do_vm_stop(state, true); -} - -/** - * Prepare for (re)starting the VM. - * Returns 0 if the vCPUs should be restarted, -1 on an error condition, - * and 1 otherwise. - */ -int vm_prepare_start(bool step_pending) -{ - int ret = vm_was_suspended ? 1 : 0; - RunState state = vm_was_suspended ? RUN_STATE_SUSPENDED : RUN_STATE_RUNNING; - RunState requested; - - qemu_vmstop_requested(&requested); - if (runstate_is_running() && requested == RUN_STATE__MAX) { - return -1; - } - - /* Ensure that a STOP/RESUME pair of events is emitted if a - * vmstop request was pending. The BLOCK_IO_ERROR event, for - * example, according to documentation is always followed by - * the STOP event. - */ - if (runstate_is_running()) { - qapi_event_send_stop(); - qapi_event_send_resume(); - return -1; - } - - /* - * WHPX accelerator needs to know whether we are going to step - * any CPUs, before starting the first one. - */ - accel_pre_resume(MACHINE(qdev_get_machine()), step_pending); - - /* We are sending this now, but the CPUs will be resumed shortly later */ - qapi_event_send_resume(); - - cpu_enable_ticks(); - runstate_set(state); - vm_state_notify(1, state); - vm_was_suspended = false; - return ret; -} - -void vm_start(void) -{ - if (!vm_prepare_start(false)) { - resume_all_vcpus(); - } -} - -void vm_resume(RunState state) -{ - if (runstate_is_live(state)) { - vm_start(); - } else { - runstate_set(state); - } -} - -/* does a state transition even if the VM is already stopped, - current state is forgotten forever */ -int vm_stop_force_state(RunState state) -{ - if (runstate_is_live(runstate_get())) { - return vm_stop(state); - } else { - int ret; - runstate_set(state); - - bdrv_drain_all(); - /* Make sure to return an error if the flush in a previous vm_stop() - * failed. */ - ret = bdrv_flush_all(); - trace_vm_stop_flush_all(ret); - return ret; - } -} - -void qmp_memsave(uint64_t addr, uint64_t size, const char *filename, - bool has_cpu, int64_t cpu_index, Error **errp) -{ - FILE *f; - uint64_t l; - CPUState *cpu; - uint8_t buf[1024]; - uint64_t orig_addr = addr, orig_size = size; - - if (migration_guest_ram_loading()) { - error_setg(errp, "Guest memory access not allowed during migration"); - return; - } - - if (!has_cpu) { - cpu_index = 0; - } - - cpu = qemu_get_cpu(cpu_index); - if (cpu == NULL) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index", - "a CPU number"); - return; - } - - f = fopen(filename, "wb"); - if (!f) { - error_setg_file_open(errp, errno, filename); - return; - } - - while (size != 0) { - l = sizeof(buf); - if (l > size) - l = size; - if (cpu_memory_rw_debug(cpu, addr, buf, l, 0) != 0) { - error_setg(errp, "Invalid addr 0x%016" PRIx64 "/size %" PRIu64 - " specified", orig_addr, orig_size); - goto exit; - } - if (fwrite(buf, 1, l, f) != l) { - error_setg(errp, "writing memory to '%s' failed", - filename); - goto exit; - } - addr += l; - size -= l; - } - -exit: - fclose(f); -} - -void qmp_pmemsave(uint64_t addr, uint64_t size, const char *filename, - Error **errp) -{ - FILE *f; - uint64_t l; - uint8_t buf[1024]; - - if (migration_guest_ram_loading()) { - error_setg(errp, "Guest memory access not allowed during migration"); - return; - } - - f = fopen(filename, "wb"); - if (!f) { - error_setg_file_open(errp, errno, filename); - return; - } - - while (size != 0) { - l = sizeof(buf); - if (l > size) - l = size; - physical_memory_read(addr, buf, l); - if (fwrite(buf, 1, l, f) != l) { - error_setg(errp, "writing memory to '%s' failed", - filename); - goto exit; - } - addr += l; - size -= l; - } - -exit: - fclose(f); -} - -void qmp_inject_nmi(Error **errp) -{ - nmi_monitor_handle(monitor_get_cpu_index(monitor_cur()), errp); -} - diff --git a/system/device_tree.c b/system/device_tree.c index 1ea1962984..48365435ea 100644 --- a/system/device_tree.c +++ b/system/device_tree.c @@ -29,7 +29,6 @@ #include "qemu/config-file.h" #include "qapi/qapi-commands-machine.h" #include "qobject/qdict.h" -#include "monitor/hmp.h" #include diff --git a/system/dirtylimit-hmp-cmds.c b/system/dirtylimit-hmp-cmds.c new file mode 100644 index 0000000000..4928d57cc8 --- /dev/null +++ b/system/dirtylimit-hmp-cmds.c @@ -0,0 +1,74 @@ +/* + * HMP commands related to migration dirty page rate limit + * + * Copyright (c) 2022 CHINA TELECOM CO.,LTD. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-migration.h" +#include "qobject/qdict.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "system/dirtylimit.h" + +void hmp_cancel_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) +{ + int64_t cpu_index = qdict_get_try_int(qdict, "cpu_index", -1); + Error *err = NULL; + + qmp_cancel_vcpu_dirty_limit(!!(cpu_index != -1), cpu_index, &err); + if (err) { + hmp_handle_error(mon, err); + return; + } + + monitor_printf(mon, "[Please use 'info vcpu_dirty_limit' to query " + "dirty limit for virtual CPU]\n"); +} + +void hmp_set_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) +{ + int64_t dirty_rate = qdict_get_int(qdict, "dirty_rate"); + int64_t cpu_index = qdict_get_try_int(qdict, "cpu_index", -1); + Error *err = NULL; + + if (dirty_rate < 0) { + error_setg(&err, "invalid dirty page limit %" PRId64, dirty_rate); + goto out; + } + + qmp_set_vcpu_dirty_limit(!!(cpu_index != -1), cpu_index, dirty_rate, &err); + +out: + hmp_handle_error(mon, err); +} + +void hmp_info_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) +{ + DirtyLimitInfoList *info; + g_autoptr(DirtyLimitInfoList) head = NULL; + Error *err = NULL; + + if (!dirtylimit_in_service()) { + monitor_printf(mon, "Dirty page limit not enabled!\n"); + return; + } + + head = qmp_query_vcpu_dirty_limit(&err); + if (err) { + hmp_handle_error(mon, err); + return; + } + + for (info = head; info != NULL; info = info->next) { + monitor_printf(mon, "vcpu[%"PRIi64"], limit rate %"PRIi64 " (MB/s)," + " current rate %"PRIi64 " (MB/s)\n", + info->value->cpu_index, + info->value->limit_rate, + info->value->current_rate); + } +} diff --git a/system/dirtylimit.c b/system/dirtylimit.c index 50fa67f3d6..70bb7bac2d 100644 --- a/system/dirtylimit.c +++ b/system/dirtylimit.c @@ -17,8 +17,6 @@ #include "qapi/error.h" #include "system/dirtyrate.h" #include "system/dirtylimit.h" -#include "monitor/hmp.h" -#include "monitor/monitor.h" #include "system/memory.h" #include "exec/target_page.h" #include "hw/core/boards.h" @@ -491,21 +489,6 @@ void qmp_cancel_vcpu_dirty_limit(bool has_cpu_index, dirtylimit_state_unlock(); } -void hmp_cancel_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) -{ - int64_t cpu_index = qdict_get_try_int(qdict, "cpu_index", -1); - Error *err = NULL; - - qmp_cancel_vcpu_dirty_limit(!!(cpu_index != -1), cpu_index, &err); - if (err) { - hmp_handle_error(mon, err); - return; - } - - monitor_printf(mon, "[Please use 'info vcpu_dirty_limit' to query " - "dirty limit for virtual CPU]\n"); -} - void qmp_set_vcpu_dirty_limit(bool has_cpu_index, int64_t cpu_index, uint64_t dirty_rate, @@ -548,23 +531,6 @@ void qmp_set_vcpu_dirty_limit(bool has_cpu_index, dirtylimit_state_unlock(); } -void hmp_set_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) -{ - int64_t dirty_rate = qdict_get_int(qdict, "dirty_rate"); - int64_t cpu_index = qdict_get_try_int(qdict, "cpu_index", -1); - Error *err = NULL; - - if (dirty_rate < 0) { - error_setg(&err, "invalid dirty page limit %" PRId64, dirty_rate); - goto out; - } - - qmp_set_vcpu_dirty_limit(!!(cpu_index != -1), cpu_index, dirty_rate, &err); - -out: - hmp_handle_error(mon, err); -} - /* Return the max throttle time of each virtual CPU */ uint64_t dirtylimit_throttle_time_per_round(void) { @@ -646,29 +612,3 @@ struct DirtyLimitInfoList *qmp_query_vcpu_dirty_limit(Error **errp) { return dirtylimit_query_all(); } - -void hmp_info_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) -{ - DirtyLimitInfoList *info; - g_autoptr(DirtyLimitInfoList) head = NULL; - Error *err = NULL; - - if (!dirtylimit_in_service()) { - monitor_printf(mon, "Dirty page limit not enabled!\n"); - return; - } - - head = qmp_query_vcpu_dirty_limit(&err); - if (err) { - hmp_handle_error(mon, err); - return; - } - - for (info = head; info != NULL; info = info->next) { - monitor_printf(mon, "vcpu[%"PRIi64"], limit rate %"PRIi64 " (MB/s)," - " current rate %"PRIi64 " (MB/s)\n", - info->value->cpu_index, - info->value->limit_rate, - info->value->current_rate); - } -} diff --git a/system/memory.c b/system/memory.c index 5fc36708ec..9760721e45 100644 --- a/system/memory.c +++ b/system/memory.c @@ -1364,43 +1364,6 @@ const MemoryRegionOps unassigned_mem_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static uint64_t memory_region_ram_device_read(void *opaque, - hwaddr addr, unsigned size) -{ - MemoryRegion *mr = opaque; - uint64_t data = ldn_he_p(mr->ram_block->host + addr, size); - - trace_memory_region_ram_device_read(get_cpu_index(), mr, addr, data, size); - - return data; -} - -static void memory_region_ram_device_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - MemoryRegion *mr = opaque; - - trace_memory_region_ram_device_write(get_cpu_index(), mr, addr, data, size); - - stn_he_p(mr->ram_block->host + addr, size, data); -} - -static const MemoryRegionOps ram_device_mem_ops = { - .read = memory_region_ram_device_read, - .write = memory_region_ram_device_write, - .endianness = HOST_BIG_ENDIAN ? DEVICE_BIG_ENDIAN : DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 8, - .unaligned = true, - }, - .impl = { - .min_access_size = 1, - .max_access_size = 8, - .unaligned = true, - }, -}; - bool memory_region_access_valid(MemoryRegion *mr, hwaddr addr, unsigned size, @@ -1692,10 +1655,8 @@ void memory_region_init_ram_device_ptr(MemoryRegion *mr, Object *owner, const char *name, uint64_t size, void *ptr) { - memory_region_init_io(mr, owner, &ram_device_mem_ops, mr, name, size); - mr->ram = true; + memory_region_init_ram_ptr(mr, owner, name, size, ptr); mr->ram_device = true; - memory_region_set_ram_ptr(mr, size, ptr); } void memory_region_init_alias(MemoryRegion *mr, Object *owner, @@ -1730,6 +1691,7 @@ static void memory_region_finalize(Object *obj) { MemoryRegion *mr = MEMORY_REGION(obj); + trace_memory_region_finalize(mr->name); /* * Each memory region (that can be freed) must have an owner, and it * always has the same lifecycle of its owner. It means when reaching diff --git a/system/meson.build b/system/meson.build index cd3193d170..64e06e7abc 100644 --- a/system/meson.build +++ b/system/meson.build @@ -9,6 +9,7 @@ system_ss.add(files( 'cpus.c', 'cpu-timers.c', 'dirtylimit.c', + 'dirtylimit-hmp-cmds.c', 'dma-helpers.c', 'exit-with-parent.c', 'globals.c', @@ -18,6 +19,7 @@ system_ss.add(files( 'memory_mapping.c', 'memory.c', 'physmem.c', + 'physmem-qmp-cmds.c', 'qdev-monitor.c', 'qtest.c', 'rtc.c', diff --git a/system/physmem-qmp-cmds.c b/system/physmem-qmp-cmds.c new file mode 100644 index 0000000000..d85430357d --- /dev/null +++ b/system/physmem-qmp-cmds.c @@ -0,0 +1,107 @@ +/* + * QMP commands to dump physical memory + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" +#include "qapi/qmp/qerror.h" +#include "hw/core/cpu.h" +#include "system/physmem.h" +#include "migration/misc.h" + +void qmp_memsave(uint64_t addr, uint64_t size, const char *filename, + bool has_cpu, int64_t cpu_index, Error **errp) +{ + FILE *f; + uint64_t l; + CPUState *cpu; + uint8_t buf[1024]; + uint64_t orig_addr = addr, orig_size = size; + + if (migration_guest_ram_loading()) { + error_setg(errp, "Guest memory access not allowed during migration"); + return; + } + + if (!has_cpu) { + cpu_index = 0; + } + + cpu = qemu_get_cpu(cpu_index); + if (cpu == NULL) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index", + "a CPU number"); + return; + } + + f = fopen(filename, "wb"); + if (!f) { + error_setg_file_open(errp, errno, filename); + return; + } + + while (size != 0) { + l = sizeof(buf); + if (l > size) { + l = size; + } + if (cpu_memory_rw_debug(cpu, addr, buf, l, 0) != 0) { + error_setg(errp, "Invalid addr 0x%016" PRIx64 "/size %" PRIu64 + " specified", orig_addr, orig_size); + goto exit; + } + if (fwrite(buf, 1, l, f) != l) { + error_setg(errp, "writing memory to '%s' failed", + filename); + goto exit; + } + addr += l; + size -= l; + } + +exit: + fclose(f); +} + +void qmp_pmemsave(uint64_t addr, uint64_t size, const char *filename, + Error **errp) +{ + FILE *f; + uint64_t l; + uint8_t buf[1024]; + + if (migration_guest_ram_loading()) { + error_setg(errp, "Guest memory access not allowed during migration"); + return; + } + + f = fopen(filename, "wb"); + if (!f) { + error_setg_file_open(errp, errno, filename); + return; + } + + while (size != 0) { + l = sizeof(buf); + if (l > size) { + l = size; + } + physical_memory_read(addr, buf, l); + if (fwrite(buf, 1, l, f) != l) { + error_setg(errp, "writing memory to '%s' failed", + filename); + goto exit; + } + addr += l; + size -= l; + } + +exit: + fclose(f); +} diff --git a/system/physmem.c b/system/physmem.c index c21ea92915..362a00f76c 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -84,8 +84,6 @@ #include "qemu/mmap-alloc.h" #endif -#include "monitor/monitor.h" - #ifdef CONFIG_LIBDAXCTL #include #endif @@ -3158,6 +3156,50 @@ void memory_region_flush_rom_device(MemoryRegion *mr, hwaddr addr, hwaddr size) invalidate_and_set_dirty(mr, addr, size); } +void qemu_ram_move(void *dst, const void *src, size_t n) +{ + uintptr_t test, len; + + if (n == 0) { + return; + } + + /* + * Calculate "the lowest set bit" over @src, @dst and @n, result put + * into @len (which guarantees a power-of-two). With that and the + * later check (len!=n), it makes sure that we will only do the atomic + * ops when: + * + * (1) @n is a power-of-two + * (2) @src and @dst addresses are both aligned to @n + */ + test = (uintptr_t)src | (uintptr_t)dst | n; + len = test & -test; + + /* Overlapping buffers, unaligned or oversized access */ + if (n > 8 || len != n) { + memmove(dst, src, n); + return; + } + + switch (len) { + case 1: + qatomic_set((uint8_t *)dst, qatomic_read((uint8_t *)src)); + break; + case 2: + qatomic_set((uint16_t *)dst, qatomic_read((uint16_t *)src)); + break; + case 4: + qatomic_set((uint32_t *)dst, qatomic_read((uint32_t *)src)); + break; + case 8: + qatomic_set((uint64_t *)dst, qatomic_read((uint64_t *)src)); + break; + default: + g_assert_not_reached(); + } +} + int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr) { unsigned access_size_max = mr->ops->valid.max_access_size; @@ -3270,7 +3312,7 @@ static MemTxResult flatview_write_continue_step(MemTxAttrs attrs, uint8_t *ram_ptr = qemu_ram_ptr_length(mr->ram_block, mr_addr, l, false, true); - memmove(ram_ptr, buf, *l); + qemu_ram_move(ram_ptr, buf, *l); invalidate_and_set_dirty(mr, mr_addr, *l); return MEMTX_OK; @@ -3363,7 +3405,7 @@ static MemTxResult flatview_read_continue_step(MemTxAttrs attrs, uint8_t *buf, uint8_t *ram_ptr = qemu_ram_ptr_length(mr->ram_block, mr_addr, l, false, false); - memcpy(buf, ram_ptr, *l); + qemu_ram_move(buf, ram_ptr, *l); return MEMTX_OK; } diff --git a/system/runstate.c b/system/runstate.c index 08acf801b0..d3e64d2b62 100644 --- a/system/runstate.c +++ b/system/runstate.c @@ -52,6 +52,7 @@ #include "qemu/thread.h" #include "qom/object.h" #include "qom/object_interfaces.h" +#include "system/cpu-timers.h" #include "system/cpus.h" #include "system/qtest.h" #include "system/replay.h" @@ -408,6 +409,159 @@ int vm_state_notify(bool running, RunState state) return ret; } +/* + * True if the vm was previously suspended, and has not been woken or reset. + */ +static int vm_was_suspended; + +void vm_set_suspended(bool suspended) +{ + vm_was_suspended = suspended; +} + +bool vm_get_suspended(void) +{ + return vm_was_suspended; +} + +static int do_vm_stop(RunState state, bool send_stop) +{ + int ret = 0; + RunState oldstate = runstate_get(); + + if (runstate_is_live(oldstate)) { + vm_was_suspended = (oldstate == RUN_STATE_SUSPENDED); + runstate_set(state); + cpu_disable_ticks(); + if (oldstate == RUN_STATE_RUNNING) { + pause_all_vcpus(); + } + ret = vm_state_notify(0, state); + if (send_stop) { + qapi_event_send_stop(); + } + } + + bdrv_drain_all(); + /* + * Even if vm_state_notify() return failure, + * it would be better to flush as before. + */ + ret |= bdrv_flush_all(); + trace_vm_stop_flush_all(ret); + + return ret; +} + +/* + * Special vm_stop() variant for terminating the process. Historically clients + * did not expect a QMP STOP event and so we need to retain compatibility. + */ +int vm_shutdown(void) +{ + return do_vm_stop(RUN_STATE_SHUTDOWN, false); +} + + +int vm_stop(RunState state) +{ + if (qemu_in_vcpu_thread()) { + qemu_system_vmstop_request_prepare(); + qemu_system_vmstop_request(state); + /* + * FIXME: should not return to device code in case + * vm_stop() has been requested. + */ + cpu_stop_current(); + return 0; + } + + return do_vm_stop(state, true); +} + +/** + * Prepare for (re)starting the VM. + * Returns 0 if the vCPUs should be restarted, -1 on an error condition, + * and 1 otherwise. + */ +int vm_prepare_start(bool step_pending) +{ + int ret = vm_was_suspended ? 1 : 0; + RunState state = vm_was_suspended ? RUN_STATE_SUSPENDED : RUN_STATE_RUNNING; + RunState requested; + + qemu_vmstop_requested(&requested); + if (runstate_is_running() && requested == RUN_STATE__MAX) { + return -1; + } + + /* + * Ensure that a STOP/RESUME pair of events is emitted if a + * vmstop request was pending. The BLOCK_IO_ERROR event, for + * example, according to documentation is always followed by + * the STOP event. + */ + if (runstate_is_running()) { + qapi_event_send_stop(); + qapi_event_send_resume(); + return -1; + } + + /* + * WHPX accelerator needs to know whether we are going to step + * any CPUs, before starting the first one. + */ + accel_pre_resume(MACHINE(qdev_get_machine()), step_pending); + + /* We are sending this now, but the CPUs will be resumed shortly later */ + qapi_event_send_resume(); + + cpu_enable_ticks(); + runstate_set(state); + vm_state_notify(1, state); + vm_was_suspended = false; + return ret; +} + +void vm_start(void) +{ + if (!vm_prepare_start(false)) { + resume_all_vcpus(); + } +} + +void vm_resume(RunState state) +{ + if (runstate_is_live(state)) { + vm_start(); + } else { + runstate_set(state); + } +} + +/* + * does a state transition even if the VM is already stopped, + * current state is forgotten forever + */ +int vm_stop_force_state(RunState state) +{ + if (runstate_is_live(runstate_get())) { + return vm_stop(state); + } else { + int ret; + runstate_set(state); + + bdrv_drain_all(); + /* + * Make sure to return an error if the flush in a previous vm_stop() + * failed. + */ + ret = bdrv_flush_all(); + trace_vm_stop_flush_all(ret); + return ret; + } +} + static ShutdownCause reset_requested; static ShutdownCause shutdown_requested; static int shutdown_exit_code = EXIT_SUCCESS; diff --git a/system/trace-events b/system/trace-events index 51b4a4679a..38ad0be8a9 100644 --- a/system/trace-events +++ b/system/trace-events @@ -20,13 +20,12 @@ memory_region_ops_read(int cpu_index, void *mr, uint64_t addr, uint64_t value, u memory_region_ops_write(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size, const char *name) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u name '%s'" memory_region_subpage_read(int cpu_index, void *mr, uint64_t offset, uint64_t value, unsigned size) "cpu %d mr %p offset 0x%"PRIx64" value 0x%"PRIx64" size %u" memory_region_subpage_write(int cpu_index, void *mr, uint64_t offset, uint64_t value, unsigned size) "cpu %d mr %p offset 0x%"PRIx64" value 0x%"PRIx64" size %u" -memory_region_ram_device_read(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u" -memory_region_ram_device_write(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u" memory_region_sync_dirty(const char *mr, const char *listener, int global) "mr '%s' listener '%s' synced (global=%d)" flatview_new(void *view, void *root) "%p (root %p)" flatview_destroy(void *view, void *root) "%p (root %p)" flatview_destroy_rcu(void *view, void *root) "%p (root %p)" global_dirty_changed(unsigned int bitmask) "bitmask 0x%"PRIx32 +memory_region_finalize(const char* name) "mr %s" # physmem.c address_space_map(void *as, uint64_t addr, uint64_t len, bool is_write, uint32_t attrs) "as:%p addr 0x%"PRIx64":%"PRIx64" write:%d attrs:0x%x" diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 0c35067b20..12e8602166 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -117,7 +117,7 @@ static void alpha_cpu_realizefn(DeviceState *dev, Error **errp) cs->tcg_cflags |= CF_PCREL; #endif - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index fb5ed25ad0..280ead6f9c 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -1590,6 +1590,11 @@ static inline bool isar_feature_aa64_sme_mop4(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64SMFR0, SMOP4); } +static inline bool isar_feature_aa64_sme_tmop(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64SMFR0, STMOP); +} + static inline bool isar_feature_aa64_ssve_aes(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64SMFR0, AES); @@ -1837,6 +1842,26 @@ static inline bool isar_feature_aa64_sme_mop4_i16i64(const ARMISARegisters *id) return isar_feature_aa64_sme_mop4(id) && isar_feature_aa64_sme_i16i64(id); } +static inline bool isar_feature_aa64_sme_tmop_b16b16(const ARMISARegisters *id) +{ + return isar_feature_aa64_sme_tmop(id) && isar_feature_aa64_sme_b16b16(id); +} + +static inline bool isar_feature_aa64_sme_tmop_f16f16(const ARMISARegisters *id) +{ + return isar_feature_aa64_sme_tmop(id) && isar_feature_aa64_sme_f16f16(id); +} + +static inline bool isar_feature_aa64_sme_tmop_f8f16(const ARMISARegisters *id) +{ + return isar_feature_aa64_sme_tmop(id) && isar_feature_aa64_sme_f8f16(id); +} + +static inline bool isar_feature_aa64_sme_tmop_f8f32(const ARMISARegisters *id) +{ + return isar_feature_aa64_sme_tmop(id) && isar_feature_aa64_sme_f8f32(id); +} + /* * Feature tests for "does this exist in either 32-bit or 64-bit?" */ diff --git a/target/arm/cpu-max.c b/target/arm/cpu-max.c index 88b7c04633..4df52bcb08 100644 --- a/target/arm/cpu-max.c +++ b/target/arm/cpu-max.c @@ -16,15 +16,14 @@ #include "target/arm/internals.h" #include "target/arm/cpregs.h" -void aarch64_aa32_a57_init(Object *obj, bool aa32_only) +void aarch64_aa32_a57_init(ARMCPU *cpu, bool aarch64_enabled) { - ARMCPU *cpu = ARM_CPU(obj); ARMISARegisters *isar = &cpu->isar; - const bool aarch64_enabled = !aa32_only; cpu->dtb_compatible = "arm,cortex-a57"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); if (aarch64_enabled) { @@ -87,102 +86,6 @@ void aarch64_aa32_a57_init(Object *obj, bool aa32_only) define_cortex_a72_a57_a53_cp_reginfo(cpu); } -/* Share AArch32 -cpu max features with AArch64. */ -void aa32_max_features(ARMCPU *cpu) -{ - uint32_t t; - ARMISARegisters *isar = &cpu->isar; - - /* Add additional features supported by QEMU */ - t = GET_IDREG(isar, ID_ISAR5); - t = FIELD_DP32(t, ID_ISAR5, AES, 2); /* FEAT_PMULL */ - t = FIELD_DP32(t, ID_ISAR5, SHA1, 1); /* FEAT_SHA1 */ - t = FIELD_DP32(t, ID_ISAR5, SHA2, 1); /* FEAT_SHA256 */ - t = FIELD_DP32(t, ID_ISAR5, CRC32, 1); - t = FIELD_DP32(t, ID_ISAR5, RDM, 1); /* FEAT_RDM */ - t = FIELD_DP32(t, ID_ISAR5, VCMA, 1); /* FEAT_FCMA */ - SET_IDREG(isar, ID_ISAR5, t); - - t = GET_IDREG(isar, ID_ISAR6); - t = FIELD_DP32(t, ID_ISAR6, JSCVT, 1); /* FEAT_JSCVT */ - t = FIELD_DP32(t, ID_ISAR6, DP, 1); /* Feat_DotProd */ - t = FIELD_DP32(t, ID_ISAR6, FHM, 1); /* FEAT_FHM */ - t = FIELD_DP32(t, ID_ISAR6, SB, 1); /* FEAT_SB */ - t = FIELD_DP32(t, ID_ISAR6, SPECRES, 1); /* FEAT_SPECRES */ - t = FIELD_DP32(t, ID_ISAR6, BF16, 1); /* FEAT_AA32BF16 */ - t = FIELD_DP32(t, ID_ISAR6, I8MM, 1); /* FEAT_AA32I8MM */ - SET_IDREG(isar, ID_ISAR6, t); - - t = cpu->isar.mvfr1; - t = FIELD_DP32(t, MVFR1, FPHP, 3); /* FEAT_FP16 */ - t = FIELD_DP32(t, MVFR1, SIMDHP, 2); /* FEAT_FP16 */ - cpu->isar.mvfr1 = 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; - - FIELD_DP32_IDREG(isar, ID_MMFR3, PAN, 2); /* FEAT_PAN2 */ - - t = GET_IDREG(isar, ID_MMFR4); - t = FIELD_DP32(t, ID_MMFR4, HPDS, 2); /* FEAT_HPDS2 */ - t = FIELD_DP32(t, ID_MMFR4, AC2, 1); /* ACTLR2, HACTLR2 */ - t = FIELD_DP32(t, ID_MMFR4, CNP, 1); /* FEAT_TTCNP */ - t = FIELD_DP32(t, ID_MMFR4, XNX, 1); /* FEAT_XNX */ - t = FIELD_DP32(t, ID_MMFR4, EVT, 2); /* FEAT_EVT2 */ - SET_IDREG(isar, ID_MMFR4, t); - - FIELD_DP32_IDREG(isar, ID_MMFR5, ETS, 2); /* FEAT_ETS2 */ - - t = GET_IDREG(isar, ID_PFR0); - t = FIELD_DP32(t, ID_PFR0, CSV2, 2); /* FEAT_CSV2 */ - t = FIELD_DP32(t, ID_PFR0, DIT, 1); /* FEAT_DIT */ - t = FIELD_DP32(t, ID_PFR0, RAS, 1); /* FEAT_RAS */ - SET_IDREG(isar, ID_PFR0, t); - - t = GET_IDREG(isar, ID_PFR2); - t = FIELD_DP32(t, ID_PFR2, CSV3, 1); /* FEAT_CSV3 */ - t = FIELD_DP32(t, ID_PFR2, SSBS, 1); /* FEAT_SSBS */ - SET_IDREG(isar, ID_PFR2, t); - - t = GET_IDREG(isar, ID_DFR0); - t = FIELD_DP32(t, ID_DFR0, COPDBG, 10); /* FEAT_Debugv8p8 */ - t = FIELD_DP32(t, ID_DFR0, COPSDBG, 10); /* FEAT_Debugv8p8 */ - t = FIELD_DP32(t, ID_DFR0, PERFMON, 6); /* FEAT_PMUv3p5 */ - SET_IDREG(isar, ID_DFR0, t); - - /* Debug ID registers. */ - - /* Bit[15] is RES1, Bit[13] and Bits[11:0] are RES0. */ - t = 0x00008000; - t = FIELD_DP32(t, DBGDIDR, SE_IMP, 1); - t = FIELD_DP32(t, DBGDIDR, NSUHD_IMP, 1); - t = FIELD_DP32(t, DBGDIDR, VERSION, 10); /* FEAT_Debugv8p8 */ - t = FIELD_DP32(t, DBGDIDR, CTX_CMPS, 1); - t = FIELD_DP32(t, DBGDIDR, BRPS, 5); - t = FIELD_DP32(t, DBGDIDR, WRPS, 3); - cpu->isar.dbgdidr = t; - - t = 0; - t = FIELD_DP32(t, DBGDEVID, PCSAMPLE, 3); - t = FIELD_DP32(t, DBGDEVID, WPADDRMASK, 1); - t = FIELD_DP32(t, DBGDEVID, BPADDRMASK, 15); - t = FIELD_DP32(t, DBGDEVID, VECTORCATCH, 0); - t = FIELD_DP32(t, DBGDEVID, VIRTEXTNS, 1); - t = FIELD_DP32(t, DBGDEVID, DOUBLELOCK, 1); - t = FIELD_DP32(t, DBGDEVID, AUXREGS, 0); - t = FIELD_DP32(t, DBGDEVID, CIDMASK, 0); - cpu->isar.dbgdevid = t; - - /* Bits[31:4] are RES0. */ - t = 0; - t = FIELD_DP32(t, DBGDEVID1, PCSROFFSET, 2); - cpu->isar.dbgdevid1 = t; - - FIELD_DP32_IDREG(isar, ID_DFR1, HPMN0, 1); /* FEAT_HPMN0 */ -} - /* * -cpu max: a CPU with as many features enabled as our emulation supports. * The version of '-cpu max' for qemu-system-aarch64 is defined in cpu64.c; @@ -203,29 +106,18 @@ static void cpu_max_initfn(Object *obj) return; } - if (tcg_enabled() || qtest_enabled()) { - aarch64_aa32_a57_init(obj, !aarch64_enabled); + if (tcg_enabled()) { + if (!aarch64_enabled) { + aarch32_max_tcg_init(cpu); + } else { + aarch64_max_tcg_initfn(obj); + } + return; } - if (!aarch64_enabled) { - aa32_max_features(cpu); -#ifdef CONFIG_USER_ONLY - /* - * Break with true ARMv8 and add back old-style VFP short-vector - * support. Only do this for user-mode, where -cpu max is the default, - * so that older v6 and v7 programs are more likely to work without - * adjustment. - */ - cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1); -#endif - } else if (tcg_enabled()) { - assert(aarch64_enabled); - /* - * '-cpu max' for TCG: we currently do this as - * "A57 with extra things" - */ - aarch64_max_tcg_initfn(obj); - } + /* Ultimate fallback: -cpu max as cortex-a57. */ + assert(qtest_enabled()); + aarch64_aa32_a57_init(cpu, aarch64_enabled); } static const ARMCPUInfo arm_max_cpu = { diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 787e4dc7ab..77aa78f00e 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1891,7 +1891,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) } #endif - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 03a30afcbe..e8dfc3179f 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -766,7 +766,6 @@ typedef struct CPUArchState { /* * The event register is shared by all ARM profiles (A/R/M), * so it is stored in the top-level CPU state. - * WFE/SEV handling is currently implemented only for M-profile. */ bool event_register; @@ -2178,6 +2177,15 @@ enum arm_features { * CPU types added in future. */ ARM_FEATURE_BACKCOMPAT_CNTFRQ, /* 62.5MHz timer default */ + /* + * ARM_FEATURE_NEON_TRAPS should be set if the CPU implements the + * CPACR.ASEDIS and HCPTR.TASE bits for trapping A32 Neon. This + * is architecturally IMPDEF, but seems to be implemented by all + * ARM_FEATURE_NEON CPUs except the Cortex-A8. + */ + ARM_FEATURE_NEON_TRAPS, + /* Does the CPU implement CPACR.D32DIS ? */ + ARM_FEATURE_D32DIS, }; static inline int arm_feature(const CPUARMState *env, int feature) @@ -2500,6 +2508,14 @@ FIELD(TBFLAG_A32, NS, 10, 1) * This requires an SME trap from AArch32 mode when using NEON. */ FIELD(TBFLAG_A32, SME_TRAP_NONSTREAMING, 11, 1) +/* + * Target EL for a Neon-disabled exception via CPACR.ASEDIS, HCPTR.TASE. + * If FPEXC_EL indicates a trap to a lower EL than this, that will + * take precedence. + */ +FIELD(TBFLAG_A32, NEONEXC_EL, 12, 2) +/* Should VFP insns touching D16..D31 UNDEF? (CPACR.D32DIS) */ +FIELD(TBFLAG_A32, D32DIS, 14, 1) /* * Bit usage when in AArch32 state, for M-profile only. diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 2816735577..4e8c472530 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -688,7 +688,7 @@ void aarch64_cpu_lpa2_finalize(ARMCPU *cpu, Error **errp) static void aarch64_a57_initfn(Object *obj) { - aarch64_aa32_a57_init(obj, false); + aarch64_aa32_a57_init(ARM_CPU(obj), true); } static void aarch64_a53_initfn(Object *obj) @@ -699,6 +699,7 @@ static void aarch64_a53_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a53"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); diff --git a/target/arm/gicv5-stubs.c b/target/arm/gicv5-stubs.c new file mode 100644 index 0000000000..845626d7c6 --- /dev/null +++ b/target/arm/gicv5-stubs.c @@ -0,0 +1,17 @@ +/* + * QEMU ARM stubs for GICv5 TCG helper functions + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "internals.h" + +void define_gicv5_cpuif_regs(ARMCPU *cpu) +{ + g_assert_not_reached(); +} + +void gicv5_update_ppi_state(CPUARMState *env, int ppi, bool level) +{ +} diff --git a/target/arm/helper.c b/target/arm/helper.c index af45234ad2..4f30a94ecd 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -554,7 +554,17 @@ static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, { uint32_t mask = 0; - /* In ARMv8 most bits of CPACR_EL1 are RES0. */ + /* + * The AArch64 view of CPACR_EL1 has a different layout to the old + * AArch32 one. We also need to permit the old AArch32 bits to be + * read and written so that an AArch64 EL2 hypervisor can set up + * the register for an AArch32 EL1 guest. So we choose not to + * enforce any RAZ/WI or RAO/WI bits for v8 based on feature + * presence/absence. + * + * For v7 the situation is a bit simpler and there we do choose to + * enforce RAZ/WI and RAO/WI. + */ if (!arm_feature(env, ARM_FEATURE_V8)) { /* * ARMv7 defines bits for unimplemented coprocessors as RAZ/WI. @@ -563,14 +573,19 @@ static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, */ if (cpu_isar_feature(aa32_vfp_simd, env_archcpu(env))) { /* VFP coprocessor: cp10 & cp11 [23:20] */ - mask |= R_CPACR_ASEDIS_MASK | - R_CPACR_D32DIS_MASK | - R_CPACR_CP11_MASK | + mask |= R_CPACR_CP11_MASK | R_CPACR_CP10_MASK; if (!arm_feature(env, ARM_FEATURE_NEON)) { /* ASEDIS [31] bit is RAO/WI */ value |= R_CPACR_ASEDIS_MASK; + mask |= R_CPACR_ASEDIS_MASK; + } else if (arm_feature(env, ARM_FEATURE_NEON_TRAPS)) { + /* + * bit is present unless CPU doesn't implement ASEDIS + * (in which case it is RAZ/WI; this is the Cortex-A8) + */ + mask |= R_CPACR_ASEDIS_MASK; } /* @@ -580,6 +595,13 @@ static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, if (!cpu_isar_feature(aa32_simd_r32, env_archcpu(env))) { /* D32DIS [30] is RAO/WI if D16-31 are not implemented. */ value |= R_CPACR_D32DIS_MASK; + mask |= R_CPACR_D32DIS_MASK; + } else if (arm_feature(env, ARM_FEATURE_D32DIS)) { + /* + * Bit is present unless CPU doesn't implement D32DIS, + * in which case it is RAZ/WI. + */ + mask |= R_CPACR_D32DIS_MASK; } } value &= mask; @@ -588,11 +610,23 @@ static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, /* * For A-profile AArch32 EL3 (but not M-profile secure mode), if NSACR.CP10 * is 0 then CPACR.{CP11,CP10} ignore writes and read as 0b00. + * Similarly, if NSACR.NSASEDIS is 1 then CPACR.ASEDIS ignores writes + * and reads as 1, and NSACR.NSD32DIS makes CPACR.D32DIS behave as RAO/WI. */ if (arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3) && - !arm_is_secure(env) && !FIELD_EX32(env->cp15.nsacr, NSACR, CP10)) { - mask = R_CPACR_CP11_MASK | R_CPACR_CP10_MASK; - value = (value & ~mask) | (env->cp15.cpacr_el1 & mask); + !arm_is_secure(env)) { + if (!FIELD_EX32(env->cp15.nsacr, NSACR, CP10)) { + mask = R_CPACR_CP11_MASK | R_CPACR_CP10_MASK; + value = (value & ~mask) | (env->cp15.cpacr_el1 & mask); + } + if (FIELD_EX32(env->cp15.nsacr, NSACR, NSASEDIS)) { + mask = R_CPACR_ASEDIS_MASK; + value = (value & ~mask) | (env->cp15.cpacr_el1 & mask); + } + if (FIELD_EX32(env->cp15.nsacr, NSACR, NSD32DIS)) { + mask = R_CPACR_D32DIS_MASK; + value = (value & ~mask) | (env->cp15.cpacr_el1 & mask); + } } env->cp15.cpacr_el1 = value; @@ -603,12 +637,21 @@ static uint64_t cpacr_read(CPUARMState *env, const ARMCPRegInfo *ri) /* * For A-profile AArch32 EL3 (but not M-profile secure mode), if NSACR.CP10 * is 0 then CPACR.{CP11,CP10} ignore writes and read as 0b00. + * Similarly NSACR.NSASEDIS makes CPACR.ASEDIS read as 1. */ uint64_t value = env->cp15.cpacr_el1; if (arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3) && - !arm_is_secure(env) && !FIELD_EX32(env->cp15.nsacr, NSACR, CP10)) { - value = ~(R_CPACR_CP11_MASK | R_CPACR_CP10_MASK); + !arm_is_secure(env)) { + if (!FIELD_EX32(env->cp15.nsacr, NSACR, CP10)) { + value = ~(R_CPACR_CP11_MASK | R_CPACR_CP10_MASK); + } + if (FIELD_EX32(env->cp15.nsacr, NSACR, NSASEDIS)) { + value |= R_CPACR_ASEDIS_MASK; + } + if (FIELD_EX32(env->cp15.nsacr, NSACR, NSD32DIS)) { + value |= R_CPACR_D32DIS_MASK; + } } return value; } @@ -4100,14 +4143,28 @@ uint64_t arm_hcrx_el2_eff(CPUARMState *env) static void cptr_el2_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { + if (!arm_feature(env, ARM_FEATURE_NEON_TRAPS)) { + /* + * If CPU doesn't implement HCPTR.TASE it's RAZ/WI. Note that + * NSACR.NSASEDIS being 1 overrides this. + */ + value &= ~R_HCPTR_TASE_MASK; + } /* * For A-profile AArch32 EL3, if NSACR.CP10 * is 0 then HCPTR.{TCP11,TCP10} ignore writes and read as 1. + * Similarly, if NSACR.NSASEDIS is 1 then HCPTR.TASE behaves as RAO/WI. */ if (arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3) && - !arm_is_secure(env) && !FIELD_EX32(env->cp15.nsacr, NSACR, CP10)) { - uint64_t mask = R_HCPTR_TCP11_MASK | R_HCPTR_TCP10_MASK; - value = (value & ~mask) | (env->cp15.cptr_el[2] & mask); + !arm_is_secure(env)) { + if (!FIELD_EX32(env->cp15.nsacr, NSACR, CP10)) { + uint64_t mask = R_HCPTR_TCP11_MASK | R_HCPTR_TCP10_MASK; + value = (value & ~mask) | (env->cp15.cptr_el[2] & mask); + } + if (FIELD_EX32(env->cp15.nsacr, NSACR, NSASEDIS)) { + uint64_t mask = R_HCPTR_TASE_MASK; + value = (value & ~mask) | (env->cp15.cptr_el[2] & mask); + } } env->cp15.cptr_el[2] = value; } @@ -4117,12 +4174,18 @@ static uint64_t cptr_el2_read(CPUARMState *env, const ARMCPRegInfo *ri) /* * For A-profile AArch32 EL3, if NSACR.CP10 * is 0 then HCPTR.{TCP11,TCP10} ignore writes and read as 1. + * Similarly, if NSACR.NSASEDIS is 1 then HCPTR.TASE behaves as RAO/WI. */ uint64_t value = env->cp15.cptr_el[2]; if (arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3) && - !arm_is_secure(env) && !FIELD_EX32(env->cp15.nsacr, NSACR, CP10)) { - value |= R_HCPTR_TCP11_MASK | R_HCPTR_TCP10_MASK; + !arm_is_secure(env)) { + if (!FIELD_EX32(env->cp15.nsacr, NSACR, CP10)) { + value |= R_HCPTR_TCP11_MASK | R_HCPTR_TCP10_MASK; + } + if (!FIELD_EX32(env->cp15.nsacr, NSACR, NSASEDIS)) { + value |= R_HCPTR_TASE_MASK; + } } return value; } @@ -8915,6 +8978,7 @@ static void take_aarch32_exception(CPUARMState *env, int new_mode, } } +#ifdef CONFIG_TCG void arm_do_plugin_vcpu_discon_cb(CPUState *cs, uint64_t from) { switch (cs->exception_index) { @@ -8932,6 +8996,7 @@ void arm_do_plugin_vcpu_discon_cb(CPUState *cs, uint64_t from) qemu_plugin_vcpu_exception_cb(cs, from); } } +#endif static void arm_cpu_do_interrupt_aarch32_hyp(CPUState *cs) { @@ -9677,9 +9742,9 @@ void arm_cpu_do_interrupt(CPUState *cs) if (tcg_enabled()) { cpu_set_interrupt(cs, CPU_INTERRUPT_EXITTB); - } - arm_do_plugin_vcpu_discon_cb(cs, last_pc); + arm_do_plugin_vcpu_discon_cb(cs, last_pc); + } } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/arm/internals.h b/target/arm/internals.h index 4026f67579..f6b4e173a5 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1813,7 +1813,7 @@ void aarch64_max_tcg_initfn(Object *obj); void aarch64_add_pauth_properties(Object *obj); void aarch64_add_sve_properties(Object *obj); void aarch64_add_sme_properties(Object *obj); -void aarch64_aa32_a57_init(Object *obj, bool aa32_only); +void aarch64_aa32_a57_init(ARMCPU *cpu, bool aa64_enabled); void aarch64_host_initfn(Object *obj); /* Return true if the gdbstub is presenting an AArch64 CPU */ @@ -1838,6 +1838,7 @@ uint32_t *arm_v7m_get_sp_ptr(CPUARMState *env, bool secure, bool el_is_in_host(CPUARMState *env, int el); void aa32_max_features(ARMCPU *cpu); +void aarch32_max_tcg_init(ARMCPU *cpu); int exception_target_el(CPUARMState *env); bool arm_singlestep_active(CPUARMState *env); bool arm_generate_debug_exceptions(CPUARMState *env); diff --git a/target/arm/meson.build b/target/arm/meson.build index 4412fde065..0369f96b4c 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -37,7 +37,9 @@ arm_system_ss.add(when: 'CONFIG_HVF', if_true: files('hyp_gdbstub.c')) arm_user_ss.add(files('cpu.c')) arm_stubs_ss.add(files( 'cpu32-stubs.c', - 'kvm-stub.c' + 'gicv5-stubs.c', + 'kvm-stub.c', + 'tcg-stubs.c', )) arm_user_ss.add(files( 'el2-stubs.c', @@ -64,8 +66,6 @@ subdir('whpx') if 'CONFIG_TCG' in config_all_accel subdir('tcg') -else - arm_common_system_ss.add(files('tcg-stubs.c')) endif arm_user_ss.add_all(arm_common_user_system_ss) diff --git a/target/arm/tcg-stubs.c b/target/arm/tcg-stubs.c index 8d97449cfc..aeeede8066 100644 --- a/target/arm/tcg-stubs.c +++ b/target/arm/tcg-stubs.c @@ -43,7 +43,3 @@ void vfp_clear_float_status_exc_flags(CPUARMState *env) void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) { } - -void gicv5_update_ppi_state(CPUARMState *env, int ppi, bool level) -{ -} diff --git a/target/arm/tcg/cpu32-system.c b/target/arm/tcg/cpu32.c similarity index 85% rename from target/arm/tcg/cpu32-system.c rename to target/arm/tcg/cpu32.c index 6e98390089..bc07e33877 100644 --- a/target/arm/tcg/cpu32-system.c +++ b/target/arm/tcg/cpu32.c @@ -1,5 +1,5 @@ /* - * QEMU ARM TCG-only CPUs (not needed for the AArch64 linux-user build) + * QEMU ARM TCG-only CPUs. * * Copyright (c) 2012 SUSE LINUX Products GmbH * @@ -13,9 +13,15 @@ #include "cpu.h" #include "accel/tcg/cpu-ops.h" #include "internals.h" +#if !defined(CONFIG_USER_ONLY) #include "hw/core/boards.h" +#endif #include "cpregs.h" + +/* CPU models. These are not needed for the AArch64 linux-user build. */ +#if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64) + static void arm926_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -245,6 +251,10 @@ static void cortex_a8_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a8"; set_feature(&cpu->env, ARM_FEATURE_V7); set_feature(&cpu->env, ARM_FEATURE_NEON); + /* + * The Cortex-A8 doesn't have CPACR.ASEDIS and HCPTR.TASE, + * so don't set ARM_FEATURE_NEON_TRAPS. + */ set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_EL3); @@ -315,6 +325,7 @@ 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_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_EL3); set_feature(&cpu->env, ARM_FEATURE_PMU); @@ -325,6 +336,7 @@ static void cortex_a9_initfn(Object *obj) */ set_feature(&cpu->env, ARM_FEATURE_V7MP); set_feature(&cpu->env, ARM_FEATURE_CBAR); + set_feature(&cpu->env, ARM_FEATURE_D32DIS); cpu->midr = 0x410fc090; cpu->reset_fpsid = 0x41033090; cpu->isar.mvfr0 = 0x11110222; @@ -383,6 +395,7 @@ static void cortex_a7_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a7"; set_feature(&cpu->env, ARM_FEATURE_V7VE); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); @@ -391,6 +404,7 @@ static void cortex_a7_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_EL2); set_feature(&cpu->env, ARM_FEATURE_EL3); set_feature(&cpu->env, ARM_FEATURE_PMU); + set_feature(&cpu->env, ARM_FEATURE_D32DIS); cpu->midr = 0x410fc075; cpu->reset_fpsid = 0x41023075; cpu->isar.mvfr0 = 0x10110222; @@ -433,6 +447,7 @@ static void cortex_a15_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a15"; set_feature(&cpu->env, ARM_FEATURE_V7VE); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); @@ -628,6 +643,7 @@ static void cortex_r52_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_EL2); set_feature(&cpu->env, ARM_FEATURE_PMSA); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); @@ -740,3 +756,116 @@ static void arm_tcg_cpu_register_types(void) } type_init(arm_tcg_cpu_register_types) + +#endif /* !CONFIG_USER_ONLY || !TARGET_AARCH64 */ + +/* Share AArch32 -cpu max features with AArch64. */ +void aa32_max_features(ARMCPU *cpu) +{ + uint32_t t; + ARMISARegisters *isar = &cpu->isar; + + /* Add additional features supported by QEMU */ + t = GET_IDREG(isar, ID_ISAR5); + t = FIELD_DP32(t, ID_ISAR5, AES, 2); /* FEAT_PMULL */ + t = FIELD_DP32(t, ID_ISAR5, SHA1, 1); /* FEAT_SHA1 */ + t = FIELD_DP32(t, ID_ISAR5, SHA2, 1); /* FEAT_SHA256 */ + t = FIELD_DP32(t, ID_ISAR5, CRC32, 1); + t = FIELD_DP32(t, ID_ISAR5, RDM, 1); /* FEAT_RDM */ + t = FIELD_DP32(t, ID_ISAR5, VCMA, 1); /* FEAT_FCMA */ + SET_IDREG(isar, ID_ISAR5, t); + + t = GET_IDREG(isar, ID_ISAR6); + t = FIELD_DP32(t, ID_ISAR6, JSCVT, 1); /* FEAT_JSCVT */ + t = FIELD_DP32(t, ID_ISAR6, DP, 1); /* Feat_DotProd */ + t = FIELD_DP32(t, ID_ISAR6, FHM, 1); /* FEAT_FHM */ + t = FIELD_DP32(t, ID_ISAR6, SB, 1); /* FEAT_SB */ + t = FIELD_DP32(t, ID_ISAR6, SPECRES, 1); /* FEAT_SPECRES */ + t = FIELD_DP32(t, ID_ISAR6, BF16, 1); /* FEAT_AA32BF16 */ + t = FIELD_DP32(t, ID_ISAR6, I8MM, 1); /* FEAT_AA32I8MM */ + SET_IDREG(isar, ID_ISAR6, t); + + t = cpu->isar.mvfr1; + t = FIELD_DP32(t, MVFR1, FPHP, 3); /* FEAT_FP16 */ + t = FIELD_DP32(t, MVFR1, SIMDHP, 2); /* FEAT_FP16 */ + cpu->isar.mvfr1 = 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; + + FIELD_DP32_IDREG(isar, ID_MMFR3, PAN, 2); /* FEAT_PAN2 */ + + t = GET_IDREG(isar, ID_MMFR4); + t = FIELD_DP32(t, ID_MMFR4, HPDS, 2); /* FEAT_HPDS2 */ + t = FIELD_DP32(t, ID_MMFR4, AC2, 1); /* ACTLR2, HACTLR2 */ + t = FIELD_DP32(t, ID_MMFR4, CNP, 1); /* FEAT_TTCNP */ + t = FIELD_DP32(t, ID_MMFR4, XNX, 1); /* FEAT_XNX */ + t = FIELD_DP32(t, ID_MMFR4, EVT, 2); /* FEAT_EVT2 */ + SET_IDREG(isar, ID_MMFR4, t); + + FIELD_DP32_IDREG(isar, ID_MMFR5, ETS, 2); /* FEAT_ETS2 */ + + t = GET_IDREG(isar, ID_PFR0); + t = FIELD_DP32(t, ID_PFR0, CSV2, 2); /* FEAT_CSV2 */ + t = FIELD_DP32(t, ID_PFR0, DIT, 1); /* FEAT_DIT */ + t = FIELD_DP32(t, ID_PFR0, RAS, 1); /* FEAT_RAS */ + SET_IDREG(isar, ID_PFR0, t); + + t = GET_IDREG(isar, ID_PFR2); + t = FIELD_DP32(t, ID_PFR2, CSV3, 1); /* FEAT_CSV3 */ + t = FIELD_DP32(t, ID_PFR2, SSBS, 1); /* FEAT_SSBS */ + SET_IDREG(isar, ID_PFR2, t); + + t = GET_IDREG(isar, ID_DFR0); + t = FIELD_DP32(t, ID_DFR0, COPDBG, 10); /* FEAT_Debugv8p8 */ + t = FIELD_DP32(t, ID_DFR0, COPSDBG, 10); /* FEAT_Debugv8p8 */ + t = FIELD_DP32(t, ID_DFR0, PERFMON, 6); /* FEAT_PMUv3p5 */ + SET_IDREG(isar, ID_DFR0, t); + + /* Debug ID registers. */ + + /* Bit[15] is RES1, Bit[13] and Bits[11:0] are RES0. */ + t = 0x00008000; + t = FIELD_DP32(t, DBGDIDR, SE_IMP, 1); + t = FIELD_DP32(t, DBGDIDR, NSUHD_IMP, 1); + t = FIELD_DP32(t, DBGDIDR, VERSION, 10); /* FEAT_Debugv8p8 */ + t = FIELD_DP32(t, DBGDIDR, CTX_CMPS, 1); + t = FIELD_DP32(t, DBGDIDR, BRPS, 5); + t = FIELD_DP32(t, DBGDIDR, WRPS, 3); + cpu->isar.dbgdidr = t; + + t = 0; + t = FIELD_DP32(t, DBGDEVID, PCSAMPLE, 3); + t = FIELD_DP32(t, DBGDEVID, WPADDRMASK, 1); + t = FIELD_DP32(t, DBGDEVID, BPADDRMASK, 15); + t = FIELD_DP32(t, DBGDEVID, VECTORCATCH, 0); + t = FIELD_DP32(t, DBGDEVID, VIRTEXTNS, 1); + t = FIELD_DP32(t, DBGDEVID, DOUBLELOCK, 1); + t = FIELD_DP32(t, DBGDEVID, AUXREGS, 0); + t = FIELD_DP32(t, DBGDEVID, CIDMASK, 0); + cpu->isar.dbgdevid = t; + + /* Bits[31:4] are RES0. */ + t = 0; + t = FIELD_DP32(t, DBGDEVID1, PCSROFFSET, 2); + cpu->isar.dbgdevid1 = t; + + FIELD_DP32_IDREG(isar, ID_DFR1, HPMN0, 1); /* FEAT_HPMN0 */ +} + +void aarch32_max_tcg_init(ARMCPU *cpu) +{ + aarch64_aa32_a57_init(cpu, false); + aa32_max_features(cpu); +#ifdef CONFIG_USER_ONLY + /* + * Break with true ARMv8 and add back old-style VFP short-vector + * support. Only do this for user-mode, where -cpu max is the default, + * so that older v6 and v7 programs are more likely to work without + * adjustment. + */ + cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1); +#endif +} diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 8a3ebf6321..b38c61ba62 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -37,6 +37,7 @@ static void aarch64_a35_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a35"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); @@ -208,6 +209,7 @@ static void aarch64_a55_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a55"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); @@ -281,6 +283,7 @@ static void aarch64_a72_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a72"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); @@ -341,6 +344,7 @@ static void aarch64_a76_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a76"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); @@ -415,6 +419,7 @@ static void aarch64_a78ae_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a78ae"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_AARCH64); set_feature(&cpu->env, ARM_FEATURE_EL2); @@ -488,6 +493,7 @@ static void aarch64_a64fx_initfn(Object *obj) cpu->dtb_compatible = "arm,a64fx"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); @@ -662,6 +668,7 @@ static void aarch64_neoverse_n1_initfn(Object *obj) cpu->dtb_compatible = "arm,neoverse-n1"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); @@ -738,6 +745,7 @@ static void aarch64_neoverse_v1_initfn(Object *obj) cpu->dtb_compatible = "arm,neoverse-v1"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); @@ -965,6 +973,7 @@ static void aarch64_a710_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a710"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); @@ -1067,6 +1076,7 @@ static void aarch64_neoverse_n2_initfn(Object *obj) cpu->dtb_compatible = "arm,neoverse-n2"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); @@ -1167,6 +1177,8 @@ void aarch64_max_tcg_initfn(Object *obj) uint64_t t; uint32_t u; + aarch64_aa32_a57_init(cpu, true); + SET_IDREG(isar, CLIDR, 0x8200123); /* 64KB L1 dcache */ cpu->ccsidr[0] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 4, 64, 64 * KiB, 7); @@ -1224,198 +1236,202 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, CTR_EL0, DIC, 1); cpu->ctr = t; + /* + * Below, note the revision from which the feature is OPTIONAL. + */ t = GET_IDREG(isar, ID_AA64ISAR0); - t = FIELD_DP64(t, ID_AA64ISAR0, AES, 2); /* FEAT_PMULL */ - t = FIELD_DP64(t, ID_AA64ISAR0, SHA1, 1); /* FEAT_SHA1 */ - t = FIELD_DP64(t, ID_AA64ISAR0, SHA2, 2); /* FEAT_SHA512 */ - t = FIELD_DP64(t, ID_AA64ISAR0, CRC32, 1); /* FEAT_CRC32 */ - t = FIELD_DP64(t, ID_AA64ISAR0, ATOMIC, 3); /* FEAT_LSE, FEAT_LSE128 */ - t = FIELD_DP64(t, ID_AA64ISAR0, RDM, 1); /* FEAT_RDM */ - t = FIELD_DP64(t, ID_AA64ISAR0, SHA3, 1); /* FEAT_SHA3 */ - t = FIELD_DP64(t, ID_AA64ISAR0, SM3, 1); /* FEAT_SM3 */ - t = FIELD_DP64(t, ID_AA64ISAR0, SM4, 1); /* FEAT_SM4 */ - t = FIELD_DP64(t, ID_AA64ISAR0, DP, 1); /* FEAT_DotProd */ - t = FIELD_DP64(t, ID_AA64ISAR0, FHM, 1); /* FEAT_FHM */ - t = FIELD_DP64(t, ID_AA64ISAR0, TS, 2); /* FEAT_FlagM2 */ - t = FIELD_DP64(t, ID_AA64ISAR0, TLB, 2); /* FEAT_TLBIRANGE */ - t = FIELD_DP64(t, ID_AA64ISAR0, RNDR, 1); /* FEAT_RNG */ + t = FIELD_DP64(t, ID_AA64ISAR0, AES, 2); /* v8.0: FEAT_PMULL */ + t = FIELD_DP64(t, ID_AA64ISAR0, SHA1, 1); /* v8.0: FEAT_SHA1 */ + t = FIELD_DP64(t, ID_AA64ISAR0, SHA2, 2); /* v8.1: FEAT_SHA512 */ + t = FIELD_DP64(t, ID_AA64ISAR0, CRC32, 1); /* v8.0: FEAT_CRC32 */ + t = FIELD_DP64(t, ID_AA64ISAR0, ATOMIC, 3); /* v9.3: FEAT_LSE128 */ + t = FIELD_DP64(t, ID_AA64ISAR0, RDM, 1); /* v8.0: FEAT_RDM */ + t = FIELD_DP64(t, ID_AA64ISAR0, SHA3, 1); /* v8.1: FEAT_SHA3 */ + t = FIELD_DP64(t, ID_AA64ISAR0, SM3, 1); /* v8.1: FEAT_SM3 */ + t = FIELD_DP64(t, ID_AA64ISAR0, SM4, 1); /* v8.1: FEAT_SM4 */ + t = FIELD_DP64(t, ID_AA64ISAR0, DP, 1); /* v8.1: FEAT_DotProd */ + t = FIELD_DP64(t, ID_AA64ISAR0, FHM, 1); /* v8.1: FEAT_FHM */ + t = FIELD_DP64(t, ID_AA64ISAR0, TS, 2); /* v8.4: FEAT_FlagM2 */ + t = FIELD_DP64(t, ID_AA64ISAR0, TLB, 2); /* v8.3: FEAT_TLBIRANGE */ + t = FIELD_DP64(t, ID_AA64ISAR0, RNDR, 1); /* v8.4: FEAT_RNG */ SET_IDREG(isar, ID_AA64ISAR0, t); t = GET_IDREG(isar, ID_AA64ISAR1); - t = FIELD_DP64(t, ID_AA64ISAR1, DPB, 2); /* FEAT_DPB2 */ - t = FIELD_DP64(t, ID_AA64ISAR1, APA, PauthFeat_FPACCOMBINED); + t = FIELD_DP64(t, ID_AA64ISAR1, DPB, 2); /* v8.1: FEAT_DPB2 */ + t = FIELD_DP64(t, ID_AA64ISAR1, APA, PauthFeat_FPACCOMBINED); /* v8.2 */ t = FIELD_DP64(t, ID_AA64ISAR1, API, 1); - t = FIELD_DP64(t, ID_AA64ISAR1, JSCVT, 1); /* FEAT_JSCVT */ - t = FIELD_DP64(t, ID_AA64ISAR1, FCMA, 1); /* FEAT_FCMA */ - t = FIELD_DP64(t, ID_AA64ISAR1, LRCPC, 2); /* FEAT_LRCPC2 */ - t = FIELD_DP64(t, ID_AA64ISAR1, FRINTTS, 1); /* FEAT_FRINTTS */ - t = FIELD_DP64(t, ID_AA64ISAR1, SB, 1); /* FEAT_SB */ - t = FIELD_DP64(t, ID_AA64ISAR1, SPECRES, 1); /* FEAT_SPECRES */ - t = FIELD_DP64(t, ID_AA64ISAR1, BF16, 2); /* FEAT_BF16, FEAT_EBF16 */ - t = FIELD_DP64(t, ID_AA64ISAR1, DGH, 1); /* FEAT_DGH */ - t = FIELD_DP64(t, ID_AA64ISAR1, I8MM, 1); /* FEAT_I8MM */ - t = FIELD_DP64(t, ID_AA64ISAR1, XS, 1); /* FEAT_XS */ + t = FIELD_DP64(t, ID_AA64ISAR1, JSCVT, 1); /* v8.2: FEAT_JSCVT */ + t = FIELD_DP64(t, ID_AA64ISAR1, FCMA, 1); /* v8.2: FEAT_FCMA */ + t = FIELD_DP64(t, ID_AA64ISAR1, LRCPC, 2); /* v8.2: FEAT_LRCPC2 */ + t = FIELD_DP64(t, ID_AA64ISAR1, FRINTTS, 1); /* v8.4: FEAT_FRINTTS */ + t = FIELD_DP64(t, ID_AA64ISAR1, SB, 1); /* v8.0: FEAT_SB */ + t = FIELD_DP64(t, ID_AA64ISAR1, SPECRES, 1); /* v8.0: FEAT_SPECRES */ + t = FIELD_DP64(t, ID_AA64ISAR1, BF16, 2); /* v8.2: FEAT_EBF16 */ + t = FIELD_DP64(t, ID_AA64ISAR1, DGH, 1); /* v8.0: FEAT_DGH */ + t = FIELD_DP64(t, ID_AA64ISAR1, I8MM, 1); /* v8.1: FEAT_I8MM */ + t = FIELD_DP64(t, ID_AA64ISAR1, XS, 1); /* v8.6: FEAT_XS */ SET_IDREG(isar, ID_AA64ISAR1, t); t = GET_IDREG(isar, ID_AA64ISAR2); - t = FIELD_DP64(t, ID_AA64ISAR2, RPRES, 1); /* FEAT_RPRES */ - t = FIELD_DP64(t, ID_AA64ISAR2, MOPS, 1); /* FEAT_MOPS */ - t = FIELD_DP64(t, ID_AA64ISAR2, BC, 1); /* FEAT_HBC */ - t = FIELD_DP64(t, ID_AA64ISAR2, WFXT, 2); /* FEAT_WFxT */ - t = FIELD_DP64(t, ID_AA64ISAR2, CSSC, 2); /* FEAT_CSSC, FEAT_CMPBR */ - t = FIELD_DP64(t, ID_AA64ISAR2, LUT, 1); /* FEAT_LUT */ - t = FIELD_DP64(t, ID_AA64ISAR2, ATS1A, 1); /* FEAT_ATS1A */ + t = FIELD_DP64(t, ID_AA64ISAR2, RPRES, 1); /* v8.6: FEAT_RPRES */ + t = FIELD_DP64(t, ID_AA64ISAR2, MOPS, 1); /* v8.7: FEAT_MOPS */ + t = FIELD_DP64(t, ID_AA64ISAR2, BC, 1); /* v8.7: FEAT_HBC */ + t = FIELD_DP64(t, ID_AA64ISAR2, WFXT, 2); /* v8.6: FEAT_WFxT */ + t = FIELD_DP64(t, ID_AA64ISAR2, CSSC, 2); /* v9.3: FEAT_CMPBR */ + t = FIELD_DP64(t, ID_AA64ISAR2, LUT, 1); /* v9.2: FEAT_LUT */ + t = FIELD_DP64(t, ID_AA64ISAR2, ATS1A, 1); /* v8.8: FEAT_ATS1A */ SET_IDREG(isar, ID_AA64ISAR2, t); t = GET_IDREG(isar, ID_AA64ISAR3); - t = FIELD_DP64(t, ID_AA64ISAR3, FAMINMAX, 1); /* FEAT_FAMINMAX */ - t = FIELD_DP64(t, ID_AA64ISAR3, FPRCVT, 1); /* FEAT_FPRCVT */ + t = FIELD_DP64(t, ID_AA64ISAR3, FAMINMAX, 1); /* v9.2: FEAT_FAMINMAX */ + t = FIELD_DP64(t, ID_AA64ISAR3, FPRCVT, 1); /* v9.5: FEAT_FPRCVT */ SET_IDREG(isar, ID_AA64ISAR3, t); t = GET_IDREG(isar, ID_AA64PFR0); - t = FIELD_DP64(t, ID_AA64PFR0, FP, 1); /* FEAT_FP16 */ - t = FIELD_DP64(t, ID_AA64PFR0, ADVSIMD, 1); /* FEAT_FP16 */ - t = FIELD_DP64(t, ID_AA64PFR0, RAS, 2); /* FEAT_RASv1p1 + FEAT_DoubleFault */ - t = FIELD_DP64(t, ID_AA64PFR0, SVE, 1); - t = FIELD_DP64(t, ID_AA64PFR0, SEL2, 1); /* FEAT_SEL2 */ - t = FIELD_DP64(t, ID_AA64PFR0, DIT, 1); /* FEAT_DIT */ - t = FIELD_DP64(t, ID_AA64PFR0, CSV2, 3); /* FEAT_CSV2_3 */ - t = FIELD_DP64(t, ID_AA64PFR0, CSV3, 1); /* FEAT_CSV3 */ + t = FIELD_DP64(t, ID_AA64PFR0, FP, 1); /* v8.2: FEAT_FP16 */ + t = FIELD_DP64(t, ID_AA64PFR0, ADVSIMD, 1); /* v8.2: FEAT_FP16 */ + t = FIELD_DP64(t, ID_AA64PFR0, RAS, 2); /* v8.3: FEAT_DoubleFault */ + t = FIELD_DP64(t, ID_AA64PFR0, SVE, 1); /* v8.2: FEAT_SVE */ + t = FIELD_DP64(t, ID_AA64PFR0, SEL2, 1); /* v8.3: FEAT_SEL2 */ + t = FIELD_DP64(t, ID_AA64PFR0, DIT, 1); /* v8.3: FEAT_DIT */ + t = FIELD_DP64(t, ID_AA64PFR0, CSV2, 3); /* v8.0: FEAT_CSV2_3 */ + t = FIELD_DP64(t, ID_AA64PFR0, CSV3, 1); /* v8.0: FEAT_CSV3 */ SET_IDREG(isar, ID_AA64PFR0, t); t = GET_IDREG(isar, ID_AA64PFR1); - t = FIELD_DP64(t, ID_AA64PFR1, BT, 1); /* FEAT_BTI */ - t = FIELD_DP64(t, ID_AA64PFR1, SSBS, 2); /* FEAT_SSBS2 */ + t = FIELD_DP64(t, ID_AA64PFR1, BT, 1); /* v8.4: FEAT_BTI */ + t = FIELD_DP64(t, ID_AA64PFR1, SSBS, 2); /* v8.0: FEAT_SSBS2 */ /* * Begin with full support for MTE. This will be downgraded to MTE=0 * during realize if the board provides no tag memory, much like * we do for EL2 with the virtualization=on property. */ - t = FIELD_DP64(t, ID_AA64PFR1, MTE, 3); /* FEAT_MTE3 */ - t = FIELD_DP64(t, ID_AA64PFR1, RAS_FRAC, 0); /* FEAT_RASv1p1 + FEAT_DoubleFault */ - t = FIELD_DP64(t, ID_AA64PFR1, SME, 2); /* FEAT_SME2 */ - t = FIELD_DP64(t, ID_AA64PFR1, RNDR_TRAP, 1); /* FEAT_RNG_TRAP */ - t = FIELD_DP64(t, ID_AA64PFR1, CSV2_FRAC, 0); /* FEAT_CSV2_3 */ - t = FIELD_DP64(t, ID_AA64PFR1, NMI, 1); /* FEAT_NMI */ - t = FIELD_DP64(t, ID_AA64PFR1, GCS, 1); /* FEAT_GCS */ - /* FEAT_MTE_NO_ADDRESS_TAGS + FEAT_MTE_CANONICAL_TAGS */ + t = FIELD_DP64(t, ID_AA64PFR1, MTE, 3); /* v8.5: FEAT_MTE3 */ + t = FIELD_DP64(t, ID_AA64PFR1, RAS_FRAC, 0); /* v8.3: FEAT_DoubleFault */ + t = FIELD_DP64(t, ID_AA64PFR1, SME, 2); /* v9.2: FEAT_SME2 */ + t = FIELD_DP64(t, ID_AA64PFR1, RNDR_TRAP, 1); /* v8.4: FEAT_RNG_TRAP */ + t = FIELD_DP64(t, ID_AA64PFR1, CSV2_FRAC, 0); /* v8.0: FEAT_CSV2_3 */ + t = FIELD_DP64(t, ID_AA64PFR1, NMI, 1); /* v8.7: FEAT_NMI */ + t = FIELD_DP64(t, ID_AA64PFR1, GCS, 1); /* v9.3: FEAT_GCS */ + /* v8.7: FEAT_MTE_NO_ADDRESS_TAGS + FEAT_MTE_CANONICAL_TAGS */ t = FIELD_DP64(t, ID_AA64PFR1, MTEX, 1); SET_IDREG(isar, ID_AA64PFR1, t); t = GET_IDREG(isar, ID_AA64PFR2); - t = FIELD_DP64(t, ID_AA64PFR2, FPMR, 1); /* FEAT_FPMR */ - t = FIELD_DP64(t, ID_AA64PFR2, MTEFAR, 1); /* FEAT_MTE_TAGGED_FAR */ - t = FIELD_DP64(t, ID_AA64PFR2, MTESTOREONLY, 1); /* FEAT_MTE_STORE_ONLY */ - t = FIELD_DP64(t, ID_AA64PFR2, MTEPERM, 1); /* FEAT_MTE_PERM */ + t = FIELD_DP64(t, ID_AA64PFR2, FPMR, 1); /* v9.2: FEAT_FPMR */ + t = FIELD_DP64(t, ID_AA64PFR2, MTEFAR, 1); /* v8.7: FEAT_MTE_TAGGED_FAR */ + t = FIELD_DP64(t, ID_AA64PFR2, MTESTOREONLY, 1); /* v8.7: FEAT_MTE_STORE_ONLY */ + t = FIELD_DP64(t, ID_AA64PFR2, MTEPERM, 1); /* v8.7: FEAT_MTE_PERM */ SET_IDREG(isar, ID_AA64PFR2, t); t = GET_IDREG(isar, ID_AA64MMFR0); - t = FIELD_DP64(t, ID_AA64MMFR0, PARANGE, 6); /* FEAT_LPA: 52 bits */ - t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN16, 1); /* 16k pages supported */ - t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN16_2, 2); /* 16k stage2 supported */ - t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN64_2, 2); /* 64k stage2 supported */ - t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN4_2, 2); /* 4k stage2 supported */ - t = FIELD_DP64(t, ID_AA64MMFR0, FGT, 1); /* FEAT_FGT */ - t = FIELD_DP64(t, ID_AA64MMFR0, ECV, 2); /* FEAT_ECV */ + t = FIELD_DP64(t, ID_AA64MMFR0, PARANGE, 6); /* v8.1: FEAT_LPA: 52 bits */ + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN16, 1); /* v8.0: FEAT_TGran16K */ + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN16_2, 2); /* v8.0: FEAT_S2TGran16K */ + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN64_2, 2); /* v8.0: FEAT_S2TGran64K */ + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN4_2, 2); /* v8.0: FEAT_S2TGran4K */ + t = FIELD_DP64(t, ID_AA64MMFR0, FGT, 1); /* v8.5: FEAT_FGT */ + t = FIELD_DP64(t, ID_AA64MMFR0, ECV, 2); /* v8.5: FEAT_ECV_POFF */ SET_IDREG(isar, ID_AA64MMFR0, t); t = GET_IDREG(isar, ID_AA64MMFR1); - t = FIELD_DP64(t, ID_AA64MMFR1, HAFDBS, 2); /* FEAT_HAFDBS */ - t = FIELD_DP64(t, ID_AA64MMFR1, VMIDBITS, 2); /* FEAT_VMID16 */ - t = FIELD_DP64(t, ID_AA64MMFR1, VH, 1); /* FEAT_VHE */ - t = FIELD_DP64(t, ID_AA64MMFR1, HPDS, 2); /* FEAT_HPDS2 */ - t = FIELD_DP64(t, ID_AA64MMFR1, LO, 1); /* FEAT_LOR */ - t = FIELD_DP64(t, ID_AA64MMFR1, PAN, 3); /* FEAT_PAN3 */ - t = FIELD_DP64(t, ID_AA64MMFR1, XNX, 1); /* FEAT_XNX */ - t = FIELD_DP64(t, ID_AA64MMFR1, ETS, 2); /* FEAT_ETS2 */ - t = FIELD_DP64(t, ID_AA64MMFR1, HCX, 1); /* FEAT_HCX */ - t = FIELD_DP64(t, ID_AA64MMFR1, AFP, 1); /* FEAT_AFP */ - t = FIELD_DP64(t, ID_AA64MMFR1, TIDCP1, 1); /* FEAT_TIDCP1 */ - t = FIELD_DP64(t, ID_AA64MMFR1, CMOW, 1); /* FEAT_CMOW */ + t = FIELD_DP64(t, ID_AA64MMFR1, HAFDBS, 2); /* v8.0: FEAT_HAFDBS */ + t = FIELD_DP64(t, ID_AA64MMFR1, VMIDBITS, 2); /* v8.0: FEAT_VMID16 */ + t = FIELD_DP64(t, ID_AA64MMFR1, VH, 1); /* v8.0: FEAT_VHE */ + t = FIELD_DP64(t, ID_AA64MMFR1, HPDS, 2); /* v8.1: FEAT_HPDS2 */ + t = FIELD_DP64(t, ID_AA64MMFR1, LO, 1); /* v8.0: FEAT_LOR */ + t = FIELD_DP64(t, ID_AA64MMFR1, PAN, 3); /* v8.1: FEAT_PAN3 */ + t = FIELD_DP64(t, ID_AA64MMFR1, XNX, 1); /* v8.1: FEAT_XNX */ + t = FIELD_DP64(t, ID_AA64MMFR1, ETS, 2); /* v8.0: FEAT_ETS2 */ + t = FIELD_DP64(t, ID_AA64MMFR1, HCX, 1); /* v8.6: FEAT_HCX */ + t = FIELD_DP64(t, ID_AA64MMFR1, AFP, 1); /* v8.6: FEAT_AFP */ + t = FIELD_DP64(t, ID_AA64MMFR1, TIDCP1, 1); /* v8.7: FEAT_TIDCP1 */ + t = FIELD_DP64(t, ID_AA64MMFR1, CMOW, 1); /* v8.7: FEAT_CMOW */ SET_IDREG(isar, ID_AA64MMFR1, t); t = GET_IDREG(isar, ID_AA64MMFR2); - t = FIELD_DP64(t, ID_AA64MMFR2, CNP, 1); /* FEAT_TTCNP */ - t = FIELD_DP64(t, ID_AA64MMFR2, UAO, 1); /* FEAT_UAO */ - t = FIELD_DP64(t, ID_AA64MMFR2, IESB, 1); /* FEAT_IESB */ - t = FIELD_DP64(t, ID_AA64MMFR2, VARANGE, 1); /* FEAT_LVA */ - t = FIELD_DP64(t, ID_AA64MMFR2, NV, 2); /* FEAT_NV2 */ - t = FIELD_DP64(t, ID_AA64MMFR2, ST, 1); /* FEAT_TTST */ - t = FIELD_DP64(t, ID_AA64MMFR2, AT, 1); /* FEAT_LSE2 */ - t = FIELD_DP64(t, ID_AA64MMFR2, IDS, 1); /* FEAT_IDST */ - t = FIELD_DP64(t, ID_AA64MMFR2, FWB, 1); /* FEAT_S2FWB */ - t = FIELD_DP64(t, ID_AA64MMFR2, TTL, 1); /* FEAT_TTL */ - t = FIELD_DP64(t, ID_AA64MMFR2, BBM, 2); /* FEAT_BBM at level 2 */ - t = FIELD_DP64(t, ID_AA64MMFR2, EVT, 2); /* FEAT_EVT */ - t = FIELD_DP64(t, ID_AA64MMFR2, E0PD, 1); /* FEAT_E0PD */ + t = FIELD_DP64(t, ID_AA64MMFR2, CNP, 1); /* v8.1: FEAT_TTCNP */ + t = FIELD_DP64(t, ID_AA64MMFR2, UAO, 1); /* v8.1: FEAT_UAO */ + t = FIELD_DP64(t, ID_AA64MMFR2, IESB, 1); /* v8.1: FEAT_IESB */ + t = FIELD_DP64(t, ID_AA64MMFR2, VARANGE, 1); /* v8.1: FEAT_LVA */ + t = FIELD_DP64(t, ID_AA64MMFR2, NV, 2); /* v8.3: FEAT_NV2 */ + t = FIELD_DP64(t, ID_AA64MMFR2, ST, 1); /* v8.3: FEAT_TTST */ + t = FIELD_DP64(t, ID_AA64MMFR2, AT, 1); /* v8.2: FEAT_LSE2 */ + t = FIELD_DP64(t, ID_AA64MMFR2, IDS, 1); /* v8.3: FEAT_IDST */ + t = FIELD_DP64(t, ID_AA64MMFR2, FWB, 1); /* v8.3: FEAT_S2FWB */ + t = FIELD_DP64(t, ID_AA64MMFR2, TTL, 1); /* v8.3: FEAT_TTL */ + t = FIELD_DP64(t, ID_AA64MMFR2, BBM, 2); /* v8.3: FEAT_BBML2 */ + t = FIELD_DP64(t, ID_AA64MMFR2, EVT, 2); /* v8.2: FEAT_EVT2 */ + t = FIELD_DP64(t, ID_AA64MMFR2, E0PD, 1); /* v8.4: FEAT_E0PD */ SET_IDREG(isar, ID_AA64MMFR2, t); t = GET_IDREG(isar, ID_AA64MMFR3); - t = FIELD_DP64(t, ID_AA64MMFR3, TCRX, 1); /* FEAT_TCR2 */ - t = FIELD_DP64(t, ID_AA64MMFR3, SCTLRX, 1); /* FEAT_SCTLR2 */ - t = FIELD_DP64(t, ID_AA64MMFR3, MEC, 1); /* FEAT_MEC */ - t = FIELD_DP64(t, ID_AA64MMFR3, SPEC_FPACC, 1); /* FEAT_FPACC_SPEC */ - t = FIELD_DP64(t, ID_AA64MMFR3, S1PIE, 1); /* FEAT_S1PIE */ - t = FIELD_DP64(t, ID_AA64MMFR3, S2PIE, 1); /* FEAT_S2PIE */ - t = FIELD_DP64(t, ID_AA64MMFR3, AIE, 1); /* FEAT_AIE */ + t = FIELD_DP64(t, ID_AA64MMFR3, TCRX, 1); /* v8.0: FEAT_TCR2 */ + t = FIELD_DP64(t, ID_AA64MMFR3, SCTLRX, 1); /* v8.0: FEAT_SCTLR2 */ + t = FIELD_DP64(t, ID_AA64MMFR3, MEC, 1); /* v9.2: FEAT_MEC */ + t = FIELD_DP64(t, ID_AA64MMFR3, SPEC_FPACC, 1); /* v8.2: FEAT_FPACC_SPEC */ + t = FIELD_DP64(t, ID_AA64MMFR3, S1PIE, 1); /* v8.8: FEAT_S1PIE */ + t = FIELD_DP64(t, ID_AA64MMFR3, S2PIE, 1); /* v8.8: FEAT_S2PIE */ + t = FIELD_DP64(t, ID_AA64MMFR3, AIE, 1); /* v8.8: FEAT_AIE */ SET_IDREG(isar, ID_AA64MMFR3, t); t = GET_IDREG(isar, ID_AA64MMFR4); - t = FIELD_DP64(t, ID_AA64MMFR4, ASID2, 1); /* FEAT_ASID2 */ + t = FIELD_DP64(t, ID_AA64MMFR4, ASID2, 1); /* v9.4: FEAT_ASID2 */ SET_IDREG(isar, ID_AA64MMFR4, t); t = GET_IDREG(isar, ID_AA64ZFR0); - t = FIELD_DP64(t, ID_AA64ZFR0, SVEVER, 2); /* FEAT_SVE2p1 */ - t = FIELD_DP64(t, ID_AA64ZFR0, AES, 2); /* FEAT_SVE_PMULL128 */ - t = FIELD_DP64(t, ID_AA64ZFR0, BITPERM, 1); /* FEAT_SVE_BitPerm */ - t = FIELD_DP64(t, ID_AA64ZFR0, BFLOAT16, 2); /* FEAT_BF16, FEAT_EBF16 */ - t = FIELD_DP64(t, ID_AA64ZFR0, B16B16, 1); /* FEAT_SVE_B16B16 */ - t = FIELD_DP64(t, ID_AA64ZFR0, SHA3, 1); /* FEAT_SVE_SHA3 */ - t = FIELD_DP64(t, ID_AA64ZFR0, SM4, 1); /* FEAT_SVE_SM4 */ - t = FIELD_DP64(t, ID_AA64ZFR0, I8MM, 1); /* FEAT_I8MM */ - t = FIELD_DP64(t, ID_AA64ZFR0, F32MM, 1); /* FEAT_F32MM */ - t = FIELD_DP64(t, ID_AA64ZFR0, F64MM, 1); /* FEAT_F64MM */ + t = FIELD_DP64(t, ID_AA64ZFR0, SVEVER, 2); /* v9.2: FEAT_SVE2p1 */ + t = FIELD_DP64(t, ID_AA64ZFR0, AES, 2); /* v9.0: FEAT_SVE_PMULL128 */ + t = FIELD_DP64(t, ID_AA64ZFR0, BITPERM, 1); /* v9.0: FEAT_SVE_BitPerm */ + t = FIELD_DP64(t, ID_AA64ZFR0, BFLOAT16, 2); /* v8.2: FEAT_EBF16 */ + t = FIELD_DP64(t, ID_AA64ZFR0, B16B16, 1); /* v9.2: FEAT_SVE_B16B16 */ + t = FIELD_DP64(t, ID_AA64ZFR0, SHA3, 1); /* v9.0: FEAT_SVE_SHA3 */ + t = FIELD_DP64(t, ID_AA64ZFR0, SM4, 1); /* v9.0: FEAT_SVE_SM4 */ + t = FIELD_DP64(t, ID_AA64ZFR0, I8MM, 1); /* v8.1: FEAT_I8MM */ + t = FIELD_DP64(t, ID_AA64ZFR0, F32MM, 1); /* v8.2: FEAT_F32MM */ + t = FIELD_DP64(t, ID_AA64ZFR0, F64MM, 1); /* v8.2: FEAT_F64MM */ SET_IDREG(isar, ID_AA64ZFR0, t); t = GET_IDREG(isar, ID_AA64DFR0); - t = FIELD_DP64(t, ID_AA64DFR0, DEBUGVER, 10); /* FEAT_Debugv8p8 */ - t = FIELD_DP64(t, ID_AA64DFR0, PMUVER, 6); /* FEAT_PMUv3p5 */ - t = FIELD_DP64(t, ID_AA64DFR0, HPMN0, 1); /* FEAT_HPMN0 */ + t = FIELD_DP64(t, ID_AA64DFR0, DEBUGVER, 10); /* v8.7: FEAT_Debugv8p8 */ + t = FIELD_DP64(t, ID_AA64DFR0, PMUVER, 6); /* v8.4: FEAT_PMUv3p5 */ + t = FIELD_DP64(t, ID_AA64DFR0, HPMN0, 1); /* v8.5: FEAT_HPMN0 */ SET_IDREG(isar, ID_AA64DFR0, t); t = GET_IDREG(isar, ID_AA64SMFR0); - t = FIELD_DP64(t, ID_AA64SMFR0, SFEXPA, 1); /* FEAT_SSVE_FEXPA */ - t = FIELD_DP64(t, ID_AA64SMFR0, SMOP4, 1); /* FEAT_SME_MOP4 */ - t = FIELD_DP64(t, ID_AA64SMFR0, AES, 1); /* FEAT_SSVE_AES */ - t = FIELD_DP64(t, ID_AA64SMFR0, SF8DP2, 1); /* FEAT_SSVE_FP8DOT2 */ - t = FIELD_DP64(t, ID_AA64SMFR0, SF8DP4, 1); /* FEAT_SSVE_FP8DOT4 */ - t = FIELD_DP64(t, ID_AA64SMFR0, SF8FMA, 1); /* FEAT_SSVE_FP8FMA */ - t = FIELD_DP64(t, ID_AA64SMFR0, F32F32, 1); /* FEAT_SME */ - t = FIELD_DP64(t, ID_AA64SMFR0, BI32I32, 1); /* FEAT_SME2 */ - t = FIELD_DP64(t, ID_AA64SMFR0, B16F32, 1); /* FEAT_SME */ - t = FIELD_DP64(t, ID_AA64SMFR0, F16F32, 1); /* FEAT_SME */ - t = FIELD_DP64(t, ID_AA64SMFR0, I8I32, 0xf); /* FEAT_SME */ - t = FIELD_DP64(t, ID_AA64SMFR0, F8F32, 1); /* FEAT_SME_F8F32 */ - t = FIELD_DP64(t, ID_AA64SMFR0, F8F16, 1); /* FEAT_SME_F8F16 */ - t = FIELD_DP64(t, ID_AA64SMFR0, F16F16, 1); /* FEAT_SME_F16F16 */ - t = FIELD_DP64(t, ID_AA64SMFR0, B16B16, 1); /* FEAT_SME_B16B16 */ - t = FIELD_DP64(t, ID_AA64SMFR0, I16I32, 5); /* FEAT_SME2 */ - t = FIELD_DP64(t, ID_AA64SMFR0, F64F64, 1); /* FEAT_SME_F64F64 */ - t = FIELD_DP64(t, ID_AA64SMFR0, I16I64, 0xf); /* FEAT_SME_I16I64 */ - t = FIELD_DP64(t, ID_AA64SMFR0, SMEVER, 2); /* FEAT_SME2p1 */ - t = FIELD_DP64(t, ID_AA64SMFR0, LUTv2, 1); /* FEAT_SME_LUTv2 */ - t = FIELD_DP64(t, ID_AA64SMFR0, FA64, 1); /* FEAT_SME_FA64 */ + t = FIELD_DP64(t, ID_AA64SMFR0, SMOP4, 1); /* v9.4: FEAT_SME_MOP4 */ + t = FIELD_DP64(t, ID_AA64SMFR0, STMOP, 1); /* v9.4: FEAT_SME_TMOP */ + t = FIELD_DP64(t, ID_AA64SMFR0, SFEXPA, 1); /* v9.4: FEAT_SSVE_FEXPA */ + t = FIELD_DP64(t, ID_AA64SMFR0, AES, 1); /* v9.5: FEAT_SSVE_AES */ + t = FIELD_DP64(t, ID_AA64SMFR0, SF8DP2, 1); /* v9.2: FEAT_SSVE_FP8DOT2 */ + t = FIELD_DP64(t, ID_AA64SMFR0, SF8DP4, 1); /* v9.2: FEAT_SSVE_FP8DOT4 */ + t = FIELD_DP64(t, ID_AA64SMFR0, SF8FMA, 1); /* v9.2: FEAT_SSVE_FP8FMA */ + t = FIELD_DP64(t, ID_AA64SMFR0, F32F32, 1); /* v9.2: FEAT_SME */ + t = FIELD_DP64(t, ID_AA64SMFR0, BI32I32, 1); /* v9.2: FEAT_SME2 */ + t = FIELD_DP64(t, ID_AA64SMFR0, B16F32, 1); /* v9.2: FEAT_SME */ + t = FIELD_DP64(t, ID_AA64SMFR0, F16F32, 1); /* v9.2: FEAT_SME */ + t = FIELD_DP64(t, ID_AA64SMFR0, I8I32, 0xf); /* v9.2: FEAT_SME */ + t = FIELD_DP64(t, ID_AA64SMFR0, F8F32, 1); /* v9.2: FEAT_SME_F8F32 */ + t = FIELD_DP64(t, ID_AA64SMFR0, F8F16, 1); /* v9.2: FEAT_SME_F8F16 */ + t = FIELD_DP64(t, ID_AA64SMFR0, F16F16, 1); /* v9.2: FEAT_SME_F16F16 */ + t = FIELD_DP64(t, ID_AA64SMFR0, B16B16, 1); /* v9.2: FEAT_SME_B16B16 */ + t = FIELD_DP64(t, ID_AA64SMFR0, I16I32, 5); /* v9.2: FEAT_SME2 */ + t = FIELD_DP64(t, ID_AA64SMFR0, F64F64, 1); /* v9.2: FEAT_SME_F64F64 */ + t = FIELD_DP64(t, ID_AA64SMFR0, I16I64, 0xf); /* v9.2: FEAT_SME_I16I64 */ + t = FIELD_DP64(t, ID_AA64SMFR0, SMEVER, 2); /* v9.2: FEAT_SME2p1 */ + t = FIELD_DP64(t, ID_AA64SMFR0, LUTv2, 1); /* v9.2: FEAT_SME_LUTv2 */ + t = FIELD_DP64(t, ID_AA64SMFR0, FA64, 1); /* v9.2: FEAT_SME_FA64 */ SET_IDREG(isar, ID_AA64SMFR0, t); t = GET_IDREG(isar, ID_AA64FPFR0); - t = FIELD_DP64(t, ID_AA64FPFR0, F8E5M2, 1); /* FEAT_FP8 */ - t = FIELD_DP64(t, ID_AA64FPFR0, F8E4M3, 1); /* FEAT_FP8 */ - t = FIELD_DP64(t, ID_AA64FPFR0, F8MM4, 1); /* FEAT_F8F16MM */ - t = FIELD_DP64(t, ID_AA64FPFR0, F8MM8, 1); /* FEAT_F8F32MM */ - t = FIELD_DP64(t, ID_AA64FPFR0, F8DP2, 1); /* FEAT_FP8DOT2 */ - t = FIELD_DP64(t, ID_AA64FPFR0, F8DP4, 1); /* FEAT_FP8DOT4 */ - t = FIELD_DP64(t, ID_AA64FPFR0, F8FMA, 1); /* FEAT_FP8FMA */ - t = FIELD_DP64(t, ID_AA64FPFR0, F8CVT, 1); /* FEAT_FP8 */ + t = FIELD_DP64(t, ID_AA64FPFR0, F8E5M2, 1); /* v9.2: FEAT_FP8 */ + t = FIELD_DP64(t, ID_AA64FPFR0, F8E4M3, 1); /* v9.2: FEAT_FP8 */ + t = FIELD_DP64(t, ID_AA64FPFR0, F8MM4, 1); /* v9.2: FEAT_F8F16MM */ + t = FIELD_DP64(t, ID_AA64FPFR0, F8MM8, 1); /* v9.2: FEAT_F8F32MM */ + t = FIELD_DP64(t, ID_AA64FPFR0, F8DP2, 1); /* v9.2: FEAT_FP8DOT2 */ + t = FIELD_DP64(t, ID_AA64FPFR0, F8DP4, 1); /* v9.2: FEAT_FP8DOT4 */ + t = FIELD_DP64(t, ID_AA64FPFR0, F8FMA, 1); /* v9.2: FEAT_FP8FMA */ + t = FIELD_DP64(t, ID_AA64FPFR0, F8CVT, 1); /* v9.2: FEAT_FP8 */ SET_IDREG(isar, ID_AA64FPFR0, t); /* Replicate the same data to the 32-bit id registers. */ @@ -1429,19 +1445,31 @@ void aarch64_max_tcg_initfn(Object *obj) cpu->ctr = 0x80038003; /* 32 byte I and D cacheline size, VIPT icache */ set_dczid_bs(cpu, 7); /* 512 bytes */ #endif + + /* v8.4: FEAT_MTE2 */ cpu->gm_blocksize = 6; /* 256 bytes */ + /* v8.2: FEAT_SVE */ cpu->sve_vq.supported = MAKE_64BIT_MASK(0, ARM_MAX_VQ); - cpu->sme_vq.supported = SVE_VQ_POW2_MAP; - - aarch64_add_pauth_properties(obj); aarch64_add_sve_properties(obj); - aarch64_add_sme_properties(obj); object_property_add(obj, "sve-max-vq", "uint32", cpu_max_get_sve_max_vq, cpu_max_set_sve_max_vq, NULL, NULL); + + /* v9.2: FEAT_SME */ + cpu->sme_vq.supported = SVE_VQ_POW2_MAP; + aarch64_add_sme_properties(obj); + + /* v8.2: FEAT_PAuth2 */ + aarch64_add_pauth_properties(obj); + + /* v9.1: FEAT_RME */ object_property_add_bool(obj, "x-rme", cpu_arm_get_rme, cpu_arm_set_rme); + + /* v9.4: FEAT_RME_GPC2 */ object_property_add(obj, "x-l0gptsz", "uint32", cpu_max_get_l0gptsz, cpu_max_set_l0gptsz, NULL, NULL); + + /* v8.6: FEAT_LPA2 */ qdev_property_add_static(DEVICE(obj), &arm_cpu_lpa2_property); } diff --git a/target/arm/tcg/fp8_helper.c b/target/arm/tcg/fp8_helper.c index 4b046f5f26..0be811e71b 100644 --- a/target/arm/tcg/fp8_helper.c +++ b/target/arm/tcg/fp8_helper.c @@ -1005,3 +1005,53 @@ void HELPER(sme_fmop4a_hb)(void *vza, void *vzn, void *vzm, FP8MulContext ctx = fp8_mul_start(env, 0xf); sme_mop4(vza, vzn, vzm, &ctx, desc, sizeof(float16), inner_fmop4a_hb); } + +void HELPER(sme_ftmopa_hb)(void *vza, void *vzn, void *vzm, void *vzk, + CPUArchState *env, uint32_t desc) +{ + FP8MulContext ctx = fp8_mul_start(env, 0xf); + intptr_t oprsz = simd_maxsz(desc); + intptr_t dim = oprsz >> MO_16; + intptr_t index = simd_data(desc); + intptr_t ctrl_base = (index * oprsz) >> 1; + uint8_t *zn0 = vzn, *zn1 = vzn + sizeof(ARMVectorReg); + uint16_t *za = vza, *zm = vzm; + uint64_t *zk = vzk; + + for (intptr_t row = 0; row < dim; row++) { + uint16_t *za_row = za + tile_vslice_offset(row); + + for (intptr_t col = 0; col < dim; col++) { + uint16_t e2 = zm[H2(col)]; + uint16_t *e3 = za_row + H2(col); + uint16_t e1 = 0; + + /* + * Four control bits select two elements. The two elements + * may be non-contiguous, so assemble them locally into e1. + * Pseudo-code has a double loop running forward, with a + * test for (i < 2) to limit construction to 2 elements. + * Easier to run a single loop backward, shifting extra + * elements off the top of our uint16_t. + */ + uint64_t this_ctrl = extractn(zk, ctrl_base + col * 4, 4); + for (int i = 3; i >= 0; i--) { + if (this_ctrl & (1 << i)) { + bool e = i & 1; + bool r = i & 2; + uint8_t *p = (r ? zn1 : zn0) + H1(2 * row + e); + e1 = (e1 << 8) | *p; + } + } + + *e3 = f8dotadd_h(e1, e2, 2, *e3, &ctx); + } + } +} + +void HELPER(sme_ftmopa_sb)(void *vza, void *vzn, void *vzm, void *vzk, + CPUArchState *env, uint32_t desc) +{ + FP8MulContext ctx = fp8_mul_start(env, 0xf); + sme_tmop_4way_sb(vza, vzn, vzm, vzk, &ctx, desc, inner_fmop4a_sb); +} diff --git a/target/arm/tcg/gicv5-cpuif.c b/target/arm/tcg/gicv5-cpuif.c index 1cdd4103d0..a6e6c44084 100644 --- a/target/arm/tcg/gicv5-cpuif.c +++ b/target/arm/tcg/gicv5-cpuif.c @@ -495,6 +495,7 @@ static void gic_icc_apr_el1_write(CPUARMState *env, const ARMCPRegInfo *ri, GICv5Domain domain = gicv5_logical_domain(env); value &= 0xffffffff; env->gicv5_cpuif.icc_apr[domain] = value; + gicv5_update_irq_fiq(env); } static uint64_t gic_icc_apr_el1_read(CPUARMState *env, const ARMCPRegInfo *ri) @@ -633,7 +634,8 @@ static uint64_t gicr_cdia_read(CPUARMState *env, const ARMCPRegInfo *ri) switch (type) { case GICV5_PPI: { - uint32_t ppireg, ppibit; + uint32_t ppireg; + uint64_t ppibit; assert(id < GICV5_NUM_PPIS); ppireg = id / 64; diff --git a/target/arm/tcg/helper-a64-defs.h b/target/arm/tcg/helper-a64-defs.h index 0e56e00f45..12f58c6a44 100644 --- a/target/arm/tcg/helper-a64-defs.h +++ b/target/arm/tcg/helper-a64-defs.h @@ -18,7 +18,6 @@ */ 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) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 05ab9ab6d3..9d805231a0 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -69,11 +69,6 @@ int64_t HELPER(sdiv64)(int64_t num, int64_t den) return num / den; } -uint64_t HELPER(rbit64)(uint64_t x) -{ - return revbit64(x); -} - void HELPER(msr_i_spsel)(CPUARMState *env, uint32_t imm) { update_spsel(env, imm); diff --git a/target/arm/tcg/helper-defs.h b/target/arm/tcg/helper-defs.h index 0077aeb4e2..42376af2c6 100644 --- a/target/arm/tcg/helper-defs.h +++ b/target/arm/tcg/helper-defs.h @@ -10,7 +10,6 @@ DEF_HELPER_3(add_usaturate, i32, env, i32, i32) DEF_HELPER_3(sub_usaturate, i32, env, i32, i32) DEF_HELPER_FLAGS_3(sdiv, TCG_CALL_NO_RWG, s32, env, s32, s32) DEF_HELPER_FLAGS_3(udiv, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_1(rbit, TCG_CALL_NO_RWG_SE, i32, i32) #define PAS_OP(pfx) \ DEF_HELPER_3(pfx ## add8, i32, i32, i32, ptr) \ diff --git a/target/arm/tcg/helper-fp8-defs.h b/target/arm/tcg/helper-fp8-defs.h index dedbd8541c..e2cf2ba8c7 100644 --- a/target/arm/tcg/helper-fp8-defs.h +++ b/target/arm/tcg/helper-fp8-defs.h @@ -47,3 +47,6 @@ DEF_HELPER_FLAGS_5(sme_fvdot_idx_hb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, DEF_HELPER_FLAGS_5(sme_fmop4a_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) DEF_HELPER_FLAGS_5(sme_fmop4a_hb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_6(sme_ftmopa_hb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(sme_ftmopa_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) diff --git a/target/arm/tcg/helper-sme-defs.h b/target/arm/tcg/helper-sme-defs.h index 7fc7129e54..b8cb643367 100644 --- a/target/arm/tcg/helper-sme-defs.h +++ b/target/arm/tcg/helper-sme-defs.h @@ -404,3 +404,18 @@ DEF_HELPER_FLAGS_4(sme_usmop4a_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sme_usmop4s_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sme_usmop4a_dh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sme_usmop4s_dh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sme_bftmopa_hh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sme_ftmopa_hh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sme_ftmopa_ss, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_6(sme_bftmopa_sh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(sme_ftmopa_sh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_5(sme_stmopa_sh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme_utmopa_sh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sme_stmopa_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme_sutmopa_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme_utmopa_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme_ustmopa_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index 0716ca98fd..296ec8101a 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -164,6 +164,104 @@ static bool sme_fa64(CPUARMState *env, int el) return true; } +static int neon_exception_el(CPUARMState *env, int cur_el) +{ + /* + * Return the EL to trap to for A32 Neon specific traps + * (CPACR.ASEDIS and HCPTR.TASE). In the pseudocode these are + * checked in the same function as the more general trap bits that + * we handle in fp_exception_el(). Fortunately it is always the + * case that if the trap/enable bits specify taking an exception + * to different ELs for the Neon-specific insns and the general fp + * insns then the trap to the lower of the two ELs has priority, + * so we can calculate the two target ELs separately and pick the + * right destination later. Compare AArch32_CheckAdvSIMDOrFPEnabled(). + * + * CPACR doesn't exist before v6, but neither does Neon, so we can + * assume that if we're here testing this then the register exists. + * HCPTR always exists if EL2 is present. + */ + uint64_t hcr_el2 = arm_hcr_el2_eff(env); + bool cpacr_asedis = FIELD_EX64(env->cp15.cpacr_el1, CPACR, ASEDIS); + bool hcptr_tase = FIELD_EX64(env->cp15.cptr_el[2], HCPTR, TASE); + bool have_aarch32_el3 = + arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3); + + if (!arm_feature(env, ARM_FEATURE_NEON_TRAPS)) { + /* This CPU doesn't implement the trap bits (Cortex-A8) */ + return 0; + } + + if (arm_feature(env, ARM_FEATURE_EL2) && arm_el_is_aa64(env, 2)) { + /* + * The AArch64 CPTR_EL2 has no equivalent to HCPTR.TASE; only + * an AArch32 EL2 can trap Neon specifically. + */ + hcptr_tase = false; + } + + /* + * We know we're in AArch32, but if this is EL0 and EL1 is AArch64 + * then CPACR_EL1 applies rather than CPACR, and it doesn't have + * ASEDIS (instead using the same bit for TCPAC). + */ + if (cur_el == 0 && arm_el_is_aa64(env, 1)) { + cpacr_asedis = false; + } + + /* CPACR is ignored if E2H+TGE are both set */ + if ((hcr_el2 & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { + cpacr_asedis = false; + } + + /* + * NSACR.NSASEDIS makes the effective values of HCPTR.TASE and + * CPACR.ASEDIS be 1 in NonSecure state. NSACR has no + * effect unless EL3 exists and is AArch32. + */ + if (have_aarch32_el3 && cur_el <= 2 && !arm_is_secure_below_el3(env)) { + if (FIELD_EX32(env->cp15.nsacr, NSACR, NSASEDIS)) { + cpacr_asedis = true; + hcptr_tase = true; + } + } + + if (cpacr_asedis) { + if (have_aarch32_el3 && (cur_el == 3 || arm_is_secure_below_el3(env))) { + /* Trap from Secure PL0 or PL1 to Secure PL1 */ + return 3; + } + if (cur_el <= 1) { + /* trap from EL0 or EL1 to EL1 */ + return 1; + } + } + + /* HCPTR.TASE traps to EL2, including for execution at EL2 */ + if (hcptr_tase && cur_el <= 2) { + return 2; + } + return 0; +} + +static bool arm_d32dis(CPUARMState *env, int cur_el) +{ + bool cpacr_d32dis = FIELD_EX64(env->cp15.cpacr_el1, CPACR, D32DIS); + + if (!arm_feature(env, ARM_FEATURE_D32DIS)) { + return false; + } + + /* If NSACR.NSD32DIS is set, CPACR.D32DIS acts as 1 in NonSecure */ + if ((arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3) && + cur_el <= 2 && !arm_is_secure_below_el3(env))) { + if (FIELD_EX32(env->cp15.nsacr, NSACR, NSD32DIS)) { + cpacr_d32dis = true; + } + } + return cpacr_d32dis; +} + static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el, ARMMMUIdx mmu_idx) { @@ -209,6 +307,10 @@ static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el, DP_TBFLAG_A32(flags, SME_TRAP_NONSTREAMING, 1); } + DP_TBFLAG_A32(flags, NEONEXC_EL, neon_exception_el(env, el)); + + DP_TBFLAG_A32(flags, D32DIS, arm_d32dis(env, el)); + return rebuild_hflags_common_32(env, fp_el, mmu_idx, flags); } diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index 573e8b5af4..cbfac0036e 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -30,6 +30,10 @@ translate32_d = [ arm_ss.add(when: 'TARGET_AARCH64', if_true: gen_a64) arm_stubs_ss.add(files('stubs32.c')) +arm_ss.add(files( + 'cpu32.c', +)) + arm_ss.add(when: 'TARGET_AARCH64', if_true: files( 'gengvec64.c', 'translate-a64.c', @@ -82,12 +86,14 @@ arm_common_user_system_ss.add(when: 'TARGET_AARCH64', if_true: files( arm_common_system_ss.add(files( 'cpregs-at.c', - 'cpu32-system.c', 'gicv5-cpuif.c', 'psci.c', 'tlb_helper.c', 'tlb-insns.c', )) +arm_common_system_ss.add(when: 'CONFIG_ARM_GICV5', if_true: files( + 'gicv5-cpuif.c', +)) arm_user_ss.add(files( 'tlb_helper.c', )) diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index dca4ef5a94..ad4ad5b4cc 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -317,6 +317,12 @@ int load_tag1(uint64_t ptr, uint8_t *mem) return extract32(*mem, ofs, 4); } +/* Like mtx_check, but simple mtx bit pair instead of MTEDESC. */ +static bool raw_mtx_check(unsigned mtx, unsigned bit55) +{ + return (mtx >> bit55) & 1; +} + uint64_t HELPER(ldg)(CPUARMState *env, uint64_t ptr, uint64_t xt, uint32_t mtx) { int mmu_idx = arm_env_mmu_index(env); @@ -330,9 +336,11 @@ uint64_t HELPER(ldg)(CPUARMState *env, uint64_t ptr, uint64_t xt, uint32_t mtx) /* Load if page supports tags. */ if (mem) { rtag = load_tag1(ptr, mem); - } else if (mtx) { - uint64_t bit55 = extract64(ptr, 55, 1); - rtag = 0xF * bit55; + } else { + bool bit55 = extract64(ptr, 55, 1); + if (raw_mtx_check(mtx, bit55)) { + rtag = 0xF * bit55; + } } return address_with_allocation_tag(xt, rtag); @@ -387,7 +395,7 @@ static inline void do_stg(CPUARMState *env, uint64_t ptr, uint64_t xt, /* Store if page supports tags. */ if (mem) { store1(ptr, mem, allocation_tag_from_addr(xt)); - } else if (mtx) { + } else if (raw_mtx_check(mtx, extract64(ptr, 55, 1))) { canonical_tag_write_fail(env, ptr, ra); } } @@ -420,6 +428,7 @@ static inline void do_st2g(CPUARMState *env, uint64_t ptr, uint64_t xt, uint8_t *mem1, *mem2; check_tag_aligned(env, ptr, ra); + mtx = raw_mtx_check(mtx, extract64(ptr, 55, 1)); /* * Trap if accessing an invalid page(s). @@ -504,8 +513,8 @@ uint64_t HELPER(ldgm)(CPUARMState *env, uint64_t ptr, uint32_t mtx) /* The tag is squashed to zero if the page does not support tags. */ if (!tag_mem) { /* Load canonical value if mtx is set (untagged memory region) */ - if (mtx) { - bool bit55 = extract64(ptr, 55, 1); + bool bit55 = extract64(ptr, 55, 1); + if (raw_mtx_check(mtx, bit55)) { ret = extract64(-bit55, 0, 1 << gm_bs); shift = extract64(ptr, LOG2_TAG_GRANULE, 4) * 4; return ret << shift; @@ -573,7 +582,7 @@ void HELPER(stgm)(CPUARMState *env, uint64_t ptr, uint64_t val, uint32_t mtx) */ if (!tag_mem) { /* Storing tags to canonically tagged region: fault. */ - if (mtx) { + if (raw_mtx_check(mtx, extract64(ptr, 55, 1))) { canonical_tag_write_fail(env, ptr, ra); } return; @@ -630,7 +639,7 @@ void HELPER(stzgm_tags)(CPUARMState *env, uint64_t ptr, uint64_t val, if (mem) { int tag_pair = (val & 0xf) * 0x11; memset(mem, tag_pair, tag_bytes); - } else if (mtx) { + } else if (raw_mtx_check(mtx, extract64(ptr, 55, 1))) { canonical_tag_write_fail(env, ptr, ra); } } diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index c4433be2ed..857e897a48 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -172,11 +172,6 @@ uint32_t HELPER(udiv)(CPUARMState *env, uint32_t num, uint32_t den) return num / den; } -uint32_t HELPER(rbit)(uint32_t x) -{ - return revbit32(x); -} - uint32_t HELPER(add_setq)(CPUARMState *env, uint32_t a, uint32_t b) { uint32_t res = a + b; diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 53e7e67feb..e8176fffdb 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -1131,3 +1131,31 @@ UMOP4_dh 1010 0001 111. ...0 0000 00.. ..0. 1... @mop4_o3 USMOP4_sb 1000 0001 000. ...0 1000 00.. ..0. 00.. @mop4_o2 USMOP4_dh 1010 0001 110. ...0 0000 00.. ..0. 1... @mop4_o3 + +# SME TMOP Sparse outer products + +&tmop zad zn zm zk idx +%tmop_zk 10:3 !function=expand_tmop_zk + +@tmop_o1 .... .... ... zm:5 ... ... .... idx:2 ... zad:1 \ + &tmop zn=%zn_ax2 zk=%tmop_zk +@tmop_o2 .... .... ... zm:5 ... ... .... idx:2 .. zad:2 \ + &tmop zn=%zn_ax2 zk=%tmop_zk + +BFTMOPA_hh 1000 0001 011 ..... 000 ... .... .. 100 . @tmop_o1 +FTMOPA_hh 1000 0001 010 ..... 000 ... .... .. 100 . @tmop_o1 +FTMOPA_ss 1000 0000 010 ..... 000 ... .... .. 00 .. @tmop_o2 + +BFTMOPA_sh 1000 0001 010 ..... 000 ... .... .. 00 .. @tmop_o2 +FTMOPA_sh 1000 0001 011 ..... 000 ... .... .. 00 .. @tmop_o2 + +FTMOPA_hb 1000 0000 011 ..... 000 ... .... .. 100 . @tmop_o1 +FTMOPA_sb 1000 0000 011 ..... 000 ... .... .. 00 .. @tmop_o2 + +STMOPA_sh 1000 0000 010 ..... 100 ... .... .. 10 .. @tmop_o2 +UTMOPA_sh 1000 0001 010 ..... 100 ... .... .. 10 .. @tmop_o2 + +STMOPA_sb 1000 0000 010 ..... 100 ... .... .. 00 .. @tmop_o2 +SUTMOPA_sb 1000 0000 011 ..... 100 ... .... .. 00 .. @tmop_o2 +USTMOPA_sb 1000 0001 010 ..... 100 ... .... .. 00 .. @tmop_o2 +UTMOPA_sb 1000 0001 011 ..... 100 ... .... .. 00 .. @tmop_o2 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 23bb816b6a..9de95428d2 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -2636,6 +2636,137 @@ void sme_mop4(void *vza, void *vzn, void *vzm, void *fn_opaque, } } +/* + * Sparse outer product, non-widening. ESZ in {16, 32}. + */ +static void sme_tmop(void *vza, void *vzn, void *vzm, uint64_t *zk, + void *fn_opaque, uint32_t desc, MemOp esz, + void (*fn)(void *, void *, void *, void *)) +{ + intptr_t oprsz = simd_maxsz(desc); + intptr_t index = simd_data(desc); + intptr_t esize = 1 << esz; + intptr_t host_adj = HOST_BIG_ENDIAN ? 8 - esize : 0; + /* Base in bits for op3[index*:csize], csize = (VL * 2) / esize. */ + intptr_t ctrl_base = index * oprsz * 2; + /* Create a zero for use with the largest esz. */ + uint32_t zero = 0; + + for (intptr_t row = 0; row < oprsz; row += esize) { + void *vza_row = vza + tile_vslice_offset(row); + + for (intptr_t col = 0; col < oprsz; col += esize) { + void *e2 = vzm + (col ^ host_adj); + void *e3 = vza_row + (col ^ host_adj); + + /* + * Two control bits select one element: + * Zn[row], if [0] is set, + * Zn+1[row], if [1] is set, + * 0, otherwise. + * Compute the address of that element. + */ + void *e1 = &zero; + uint64_t this_ctrl = extractn(zk, (ctrl_base + 2 * col) >> esz, 2); + if (this_ctrl) { + e1 = vzn + (row ^ host_adj); + if (!(this_ctrl & 1)) { + e1 += sizeof(ARMVectorReg); + } + } + fn(e3, e1, e2, fn_opaque); + } + } +} + +/* + * Sparse outer product, widening 2-way, 16 to 32-bit. + */ +static void sme_tmop_2way_sh(uint32_t *za, uint16_t *zn0, uint32_t *zm, + uint64_t *zk, void *fn_opaque, uint32_t desc, + void (*fn)(void *, void *, void *, void *)) +{ + intptr_t oprsz = simd_maxsz(desc); + intptr_t dim = oprsz >> MO_32; + intptr_t index = simd_data(desc); + intptr_t ctrl_base = (index * oprsz) >> 1; + uint16_t *zn1 = zn0 + sizeof(ARMVectorReg) / 2; + + for (intptr_t row = 0; row < dim; row++) { + uint32_t *za_row = za + tile_vslice_offset(row); + + for (intptr_t col = 0; col < dim; col++) { + uint32_t *e2 = zm + H4(col); + uint32_t *e3 = za_row + H4(col); + uint32_t e1 = 0; + + /* + * Four control bits select two elements. The two elements + * may be non-contiguous, so assemble them locally into e1. + * Pseudo-code has a double loop running forward, with a + * test for (i < 2) to limit construction to 2 elements. + * Easier to run a single loop backward, shifting extra + * elements off the top of our uint32_t. + */ + uint64_t this_ctrl = extractn(zk, ctrl_base + col * 4, 4); + for (int i = 3; i >= 0; i--) { + if (this_ctrl & (1 << i)) { + bool e = i & 1; + bool r = i & 2; + uint16_t *p = (r ? zn1 : zn0) + H2(2 * row + e); + e1 = (e1 << 16) | *p; + } + } + + fn(e3, &e1, e2, fn_opaque); + } + } +} + +void sme_tmop_4way_sb(uint32_t *za, uint8_t *zn0, uint32_t *zm, + uint64_t *zk, void *fn_opaque, uint32_t desc, + void (*fn)(void *, void *, void *, void *)) +{ + intptr_t oprsz = simd_maxsz(desc); + intptr_t dim = oprsz >> MO_32; + intptr_t index = simd_data(desc); + intptr_t ctrl_base = (index * oprsz) >> 1; + uint8_t *zn1 = zn0 + sizeof(ARMVectorReg); + + for (intptr_t row = 0; row < dim; row++) { + uint32_t *za_row = za + tile_vslice_offset(row); + + for (intptr_t col = 0; col < dim; col++) { + uint32_t *e2 = zm + H4(col); + uint32_t *e3 = za_row + H4(col); + uint16_t e1l = 0, e1h = 0; + uint32_t e1; + + /* + * Eight control bits select two elements from each row. + * The elements may be non-contiguous, so assemble them + * locally into e1. + * Pseudo-code has a triple loop running forward, with a + * test for (i < 2) to limit construction to 2 elements. + * Easier to run a single loop backward, shifting extra + * elements off the top. + */ + uint64_t this_ctrl = extractn(zk, ctrl_base + col * 8, 8); + for (int e = 3; e >= 0; e--) { + if (this_ctrl & (0x01 << e)) { + e1l = (e1l << 8) | zn0[H1(4 * row + e)]; + } + if (this_ctrl & (0x10 << e)) { + e1h = (e1h << 8) | zn1[H1(4 * row + e)]; + } + } + e1 = (e1h << 16) | e1l; + + fn(e3, &e1, e2, fn_opaque); + } + } +} + static void inner_fmop4a_hh(void *vd, void *vn, void *vm, void *vinfo) { float16 *d = vd, *n = vn, *m = vm; @@ -2650,6 +2781,12 @@ void HELPER(sme_fmop4a_hh)(void *vza, void *vzn, void *vzm, sme_mop4(vza, vzn, vzm, fpst, desc, sizeof(float16), inner_fmop4a_hh); } +void HELPER(sme_ftmopa_hh)(void *vza, void *vzn, void *vzm, void *vzk, + float_status *fpst, uint32_t desc) +{ + sme_tmop(vza, vzn, vzm, vzk, fpst, desc, MO_16, inner_fmop4a_hh); +} + static void inner_fmop4s_hh(void *vd, void *vn, void *vm, void *vinfo) { float16 *d = vd, *n = vn, *m = vm; @@ -2692,6 +2829,12 @@ void HELPER(sme_fmop4a_ss)(void *vza, void *vzn, void *vzm, sme_mop4(vza, vzn, vzm, fpst, desc, sizeof(float32), inner_fmop4a_ss); } +void HELPER(sme_ftmopa_ss)(void *vza, void *vzn, void *vzm, void *vzk, + float_status *fpst, uint32_t desc) +{ + sme_tmop(vza, vzn, vzm, vzk, fpst, desc, MO_32, inner_fmop4a_ss); +} + static void inner_fmop4s_ss(void *vd, void *vn, void *vm, void *vinfo) { float32 *d = vd, *n = vn, *m = vm; @@ -2776,6 +2919,12 @@ void HELPER(sme_bfmop4a_hh)(void *vza, void *vzn, void *vzm, sme_mop4(vza, vzn, vzm, fpst, desc, sizeof(bfloat16), inner_bfmop4a_hh); } +void HELPER(sme_bftmopa_hh)(void *vza, void *vzn, void *vzm, void *vzk, + float_status *fpst, uint32_t desc) +{ + sme_tmop(vza, vzn, vzm, vzk, fpst, desc, MO_16, inner_bfmop4a_hh); +} + static void inner_bfmop4s_hh(void *vd, void *vn, void *vm, void *vinfo) { bfloat16 *d = vd, *n = vn, *m = vm; @@ -2832,6 +2981,16 @@ void HELPER(sme_bfmop4a_sh)(void *vza, void *vzn, void *vzm, : inner_bfmop4a_sh); } +void HELPER(sme_bftmopa_sh)(void *vza, void *vzn, void *vzm, void *vzk, + CPUArchState *env, uint32_t desc) +{ + float_status fpst; + + sme_tmop_2way_sh(vza, vzn, vzm, vzk, &fpst, desc, + is_ebf(env, &fpst) ? inner_ebf_bfmop4a_sh + : inner_bfmop4a_sh); +} + static void inner_bfmop4s_sh(void *vd, void *vn, void *vm, void *vinfo) { float32 *d = vd; @@ -2905,6 +3064,12 @@ void HELPER(sme_fmop4a_sh)(void *vza, void *vzn, void *vzm, sme_mop4(vza, vzn, vzm, env, desc, sizeof(float32), inner_fmop4a_sh); } +void HELPER(sme_ftmopa_sh)(void *vza, void *vzn, void *vzm, void *vzk, + CPUArchState *env, uint32_t desc) +{ + sme_tmop_2way_sh(vza, vzn, vzm, vzk, env, desc, inner_fmop4a_sh); +} + static void inner_fmop4s_sh(void *vd, void *vn, void *vm, void *vinfo) { float32 *d = vd; @@ -2958,6 +3123,18 @@ IMOP4_2WAY(umop4s_sh, -, int32_t, uint16_t, uint16_t) #undef IMOP4_2WAY +#define ITMOP_2WAY(TNAME, MNAME) \ +void HELPER(sme_##TNAME)(void *vza, void *vzn, void *vzm, \ + void *vzk, uint32_t desc) \ +{ \ + sme_tmop_2way_sh(vza, vzn, vzm, vzk, NULL, desc, inner_##MNAME); \ +} + +ITMOP_2WAY(stmopa_sh, smop4a_sh) +ITMOP_2WAY(utmopa_sh, umop4a_sh) + +#undef ITMOP_2WAY + #define IMOP4_4WAY(NAME, OP, TYPED, TYPEN, TYPEM) \ static void inner_##NAME(void *vd, void *vn, void *vm, void *vinfo) \ { \ @@ -2991,3 +3168,17 @@ IMOP4_4WAY(usmop4a_dh, +, int64_t, uint16_t, int16_t) IMOP4_4WAY(usmop4s_dh, -, int64_t, uint16_t, int16_t) #undef IMOP4_4WAY + +#define ITMOP_4WAY(TNAME, MNAME) \ +void HELPER(sme_##TNAME)(void *vza, void *vzn, void *vzm, \ + void *vzk, uint32_t desc) \ +{ \ + sme_tmop_4way_sb(vza, vzn, vzm, vzk, NULL, desc, inner_##MNAME); \ +} + +ITMOP_4WAY(stmopa_sb, smop4a_sb) +ITMOP_4WAY(utmopa_sb, umop4a_sb) +ITMOP_4WAY(sutmopa_sb, sumop4a_sb) +ITMOP_4WAY(ustmopa_sb, usmop4a_sb) + +#undef ITMOP_4WAY diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 1780490065..4f9a93950b 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8963,7 +8963,7 @@ static void gen_wrap2_i32(TCGv_i64 d, TCGv_i64 n, NeonGenOneOpFn fn) static void gen_rbit32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) { - gen_wrap2_i32(tcg_rd, tcg_rn, gen_helper_rbit); + tcg_gen_revbit32_i64(tcg_rd, tcg_rn, TCG_BSWAP_OZ); } static void gen_rev16_xx(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn, TCGv_i64 mask) @@ -8998,7 +8998,7 @@ static void gen_rev32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) tcg_gen_rotri_i64(tcg_rd, tcg_rd, 32); } -TRANS(RBIT, gen_rr, a->rd, a->rn, a->sf ? gen_helper_rbit64 : gen_rbit32) +TRANS(RBIT, gen_rr, a->rd, a->rn, a->sf ? tcg_gen_revbit64_i64 : gen_rbit32) TRANS(REV16, gen_rr, a->rd, a->rn, a->sf ? gen_rev16_64 : gen_rev16_32) TRANS(REV32, gen_rr, a->rd, a->rn, a->sf ? gen_rev32 : gen_rev_32) TRANS(REV64, gen_rr, a->rd, a->rn, tcg_gen_bswap64_i64) diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index 50a44511d9..f811940f64 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -118,11 +118,24 @@ static void neon_store_element64(int reg, int ele, MemOp size, TCGv_i64 var) } } +/* + * Return true if a Neon insn is OK to access the registers indicated + * by regmask, false if it should UNDEF. This checks whether the + * D16-D31 regs are implemented by the CPU. + * Note that Neon insns accessing D16..D31 do not need to check D32DIS. + * + * @regmask should be the logical OR of the Dregs being accessed. + */ +static bool neon_dregs_ok(DisasContext *s, int dregmask) +{ + return !(dregmask & s->invalid_neon_dreg_mask); +} + static bool do_neon_ddda(DisasContext *s, int q, int vd, int vn, int vm, int data, gen_helper_gvec_4 *fn_gvec) { /* UNDEF accesses to D16-D31 if they don't exist. */ - if (((vd | vn | vm) & 0x10) && !dc_isar_feature(aa32_simd_r32, s)) { + if (!neon_dregs_ok(s, vd | vn | vm)) { return false; } @@ -152,7 +165,7 @@ static bool do_neon_ddda_env(DisasContext *s, int q, int vd, int vn, int vm, int data, gen_helper_gvec_4_ptr *fn_gvec) { /* UNDEF accesses to D16-D31 if they don't exist. */ - if (((vd | vn | vm) & 0x10) && !dc_isar_feature(aa32_simd_r32, s)) { + if (!neon_dregs_ok(s, vd | vn | vm)) { return false; } @@ -184,7 +197,7 @@ static bool do_neon_ddda_fpst(DisasContext *s, int q, int vd, int vn, int vm, gen_helper_gvec_4_ptr *fn_gvec_ptr) { /* UNDEF accesses to D16-D31 if they don't exist. */ - if (((vd | vn | vm) & 0x10) && !dc_isar_feature(aa32_simd_r32, s)) { + if (!neon_dregs_ok(s, vd | vn | vm)) { return false; } @@ -240,8 +253,7 @@ static bool trans_VCADD(DisasContext *s, arg_VCADD *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -310,8 +322,7 @@ static bool trans_VFML(DisasContext *s, arg_VFML *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - (a->vd & 0x10)) { + if (!neon_dregs_ok(s, a->vd)) { return false; } @@ -398,14 +409,14 @@ static bool trans_VDOT_b16_scal(DisasContext *s, arg_VDOT_b16_scal *a) static bool trans_VFML_scalar(DisasContext *s, arg_VFML_scalar *a) { int opr_sz; + int dregmask = a->vd | (a->q ? a->vn : 0); if (!dc_isar_feature(aa32_fhm, s)) { return false; } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd & 0x10) || (a->q && (a->vn & 0x10)))) { + if (!neon_dregs_ok(s, dregmask)) { return false; } @@ -478,7 +489,7 @@ static bool trans_VLDST_multiple(DisasContext *s, arg_VLDST_multiple *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) { + if (!neon_dregs_ok(s, a->vd)) { return false; } if (a->itype > 10) { @@ -580,7 +591,7 @@ static bool trans_VLD_all_lanes(DisasContext *s, arg_VLD_all_lanes *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) { + if (!neon_dregs_ok(s, a->vd)) { return false; } @@ -672,7 +683,7 @@ static bool trans_VLDST_single(DisasContext *s, arg_VLDST_single *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) { + if (!neon_dregs_ok(s, a->vd)) { return false; } @@ -789,8 +800,7 @@ static bool do_3same(DisasContext *s, arg_3same *a, GVecGen3Fn fn) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -1067,8 +1077,7 @@ static bool do_vector_2sh(DisasContext *s, arg_2reg_shift *a, GVecGen2iFn *fn) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -1117,8 +1126,7 @@ static bool do_2shift_narrow_64(DisasContext *s, arg_2reg_shift *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -1168,8 +1176,7 @@ static bool do_2shift_narrow_32(DisasContext *s, arg_2reg_shift *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -1293,8 +1300,7 @@ static bool do_vshll_2sh(DisasContext *s, arg_2reg_shift *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -1383,8 +1389,7 @@ static bool do_fp_2sh(DisasContext *s, arg_2reg_shift *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -1428,7 +1433,7 @@ static bool do_1reg_imm(DisasContext *s, arg_1reg_imm *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) { + if (!neon_dregs_ok(s, a->vd)) { return false; } @@ -1485,8 +1490,7 @@ static bool do_prewiden_3d(DisasContext *s, arg_3diff *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -1592,8 +1596,7 @@ static bool do_narrow_3d(DisasContext *s, arg_3diff *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -1682,8 +1685,7 @@ static bool do_long_3d(DisasContext *s, arg_3diff *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -1944,8 +1946,7 @@ static bool trans_VMULL_P_3d(DisasContext *s, arg_3diff *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -2027,8 +2028,7 @@ static bool do_2scalar(DisasContext *s, arg_2scalar *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -2125,8 +2125,7 @@ static bool do_2scalar_fp_vec(DisasContext *s, arg_2scalar *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -2222,8 +2221,7 @@ static bool do_vqrdmlah_2sc(DisasContext *s, arg_2scalar *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -2293,8 +2291,7 @@ static bool do_2scalar_long(DisasContext *s, arg_2scalar *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -2438,8 +2435,7 @@ static bool trans_VEXT(DisasContext *s, arg_VEXT *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -2507,8 +2503,7 @@ static bool trans_VTBL(DisasContext *s, arg_VTBL *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -2546,8 +2541,7 @@ static bool trans_VDUP_scalar(DisasContext *s, arg_VDUP_scalar *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -2577,8 +2571,7 @@ static bool do_zip_uzp(DisasContext *s, arg_2misc *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -2647,8 +2640,7 @@ static bool do_vmovn(DisasContext *s, arg_2misc *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -2711,8 +2703,7 @@ static bool trans_VSHLL(DisasContext *s, arg_2misc *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -2755,8 +2746,7 @@ static bool trans_VCVT_B16_F32(DisasContext *s, arg_2misc *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -2795,8 +2785,7 @@ static bool trans_VCVT_F16_F32(DisasContext *s, arg_2misc *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -2841,8 +2830,7 @@ static bool trans_VCVT_F32_F16(DisasContext *s, arg_2misc *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -2887,8 +2875,7 @@ static bool do_2misc_vec(DisasContext *s, arg_2misc *a, GVecGen2Fn *fn) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -3015,8 +3002,7 @@ static bool do_2misc(DisasContext *s, arg_2misc *a, NeonGenOneOpFn *fn) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -3219,8 +3205,7 @@ static bool trans_VSWP(DisasContext *s, arg_2misc *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -3292,8 +3277,7 @@ static bool trans_VTRN(DisasContext *s, arg_2misc *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index dcc4690fba..768b76f01e 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -31,6 +31,12 @@ * Include the generated decoder. */ +static int expand_tmop_zk(DisasContext *s, int x) +{ + /* Pseudocode for 1:K:1:zk. */ + return 0b10100 | ((x & 4) << 1) | (x & 3); +} + #include "decode-sme.c.inc" static bool sme2_zt0_enabled_check(DisasContext *s) @@ -756,7 +762,7 @@ static bool do_z2z_n1_fpst(DisasContext *s, arg_z2z_en *a, return false; } /* These insns use MO_8 to encode BFloat16. */ - if (esz == MO_8 && !dc_isar_feature(aa64_sme_b16b16, s)) { + if (esz == MO_8 && !dc_isar_feature(aa64_sve_b16b16, s)) { return false; } if (!sme_sm_enabled_check(s)) { @@ -793,7 +799,7 @@ static bool do_z2z_nn_fpst(DisasContext *s, arg_z2z_en *a, if (fn == NULL) { return false; } - if (esz == MO_8 && !dc_isar_feature(aa64_sme_b16b16, s)) { + if (esz == MO_8 && !dc_isar_feature(aa64_sve_b16b16, s)) { return false; } if (!sme_sm_enabled_check(s)) { @@ -1850,7 +1856,7 @@ static bool trans_FCLAMP(DisasContext *s, arg_zzz_en *a) return false; } /* This insn uses MO_8 to encode BFloat16. */ - if (a->esz == MO_8 && !dc_isar_feature(aa64_sme_b16b16, s)) { + if (a->esz == MO_8 && !dc_isar_feature(aa64_sve_b16b16, s)) { return false; } if (!sme_sm_enabled_check(s)) { @@ -2132,3 +2138,76 @@ TRANS_FEAT(USMOP4_sb, aa64_sme_mop4, do_mop4_int, a, MO_32, a->s ? gen_helper_sme_usmop4s_sb : gen_helper_sme_usmop4a_sb) TRANS_FEAT(USMOP4_dh, aa64_sme_mop4_i16i64, do_mop4_int, a, MO_64, a->s ? gen_helper_sme_usmop4s_dh : gen_helper_sme_usmop4a_dh) + +static bool do_tmop_fp(DisasContext *s, arg_tmop *a, MemOp esz, + int e_fpst, gen_helper_gvec_4_ptr *fn) +{ + if (sme_smza_enabled_check(s)) { + int svl = streaming_vec_reg_size(s); + uint32_t desc = simd_desc(svl, svl, a->idx); + TCGv_ptr za = get_tile(s, esz, a->zad); + TCGv_ptr zn = vec_full_reg_ptr(s, a->zn); + TCGv_ptr zm = vec_full_reg_ptr(s, a->zm); + TCGv_ptr zk = vec_full_reg_ptr(s, a->zm); + TCGv_ptr fpst = (e_fpst >= 0 ? fpstatus_ptr(e_fpst) : tcg_env); + + fn(za, zn, zm, zk, fpst, tcg_constant_i32(desc)); + } + return true; +} + +TRANS_FEAT(BFTMOPA_hh, aa64_sme_tmop_b16b16, do_tmop_fp, + a, MO_16, FPST_ZA, gen_helper_sme_bftmopa_hh) +TRANS_FEAT(FTMOPA_hh, aa64_sme_tmop_f16f16, do_tmop_fp, + a, MO_16, FPST_ZA_F16, gen_helper_sme_ftmopa_hh) +TRANS_FEAT(FTMOPA_ss, aa64_sme_tmop, do_tmop_fp, + a, MO_32, FPST_ZA, gen_helper_sme_ftmopa_ss) + +TRANS_FEAT(BFTMOPA_sh, aa64_sme_tmop, do_tmop_fp, + a, MO_32, FPST_ENV, gen_helper_sme_bftmopa_sh) +TRANS_FEAT(FTMOPA_sh, aa64_sme_tmop, do_tmop_fp, + a, MO_32, FPST_ENV, gen_helper_sme_ftmopa_sh) + +static bool do_tmop_fp8(DisasContext *s, arg_tmop *a, MemOp esz, + gen_helper_gvec_4_ptr *fn) +{ + if (!fpmr_access_check(s)) { + return true; + } + return do_tmop_fp(s, a, esz, FPST_ENV, fn); +} + +TRANS_FEAT(FTMOPA_hb, aa64_sme_tmop_f8f16, do_tmop_fp8, + a, MO_16, gen_helper_sme_ftmopa_hb) +TRANS_FEAT(FTMOPA_sb, aa64_sme_tmop_f8f32, do_tmop_fp8, + a, MO_32, gen_helper_sme_ftmopa_sb) + +static bool do_tmop_int(DisasContext *s, arg_tmop *a, MemOp esz, + gen_helper_gvec_4 *fn) +{ + if (sme_smza_enabled_check(s)) { + int svl = streaming_vec_reg_size(s); + uint32_t desc = simd_desc(svl, svl, a->idx); + TCGv_ptr za = get_tile(s, esz, a->zad); + TCGv_ptr zn = vec_full_reg_ptr(s, a->zn); + TCGv_ptr zm = vec_full_reg_ptr(s, a->zm); + TCGv_ptr zk = vec_full_reg_ptr(s, a->zm); + + fn(za, zn, zm, zk, tcg_constant_i32(desc)); + } + return true; +} + +TRANS_FEAT(STMOPA_sh, aa64_sme_tmop, do_tmop_int, + a, MO_32, gen_helper_sme_stmopa_sh) +TRANS_FEAT(UTMOPA_sh, aa64_sme_tmop, do_tmop_int, + a, MO_32, gen_helper_sme_utmopa_sh) + +TRANS_FEAT(STMOPA_sb, aa64_sme_tmop, do_tmop_int, + a, MO_32, gen_helper_sme_stmopa_sb) +TRANS_FEAT(SUTMOPA_sb, aa64_sme_tmop, do_tmop_int, + a, MO_32, gen_helper_sme_sutmopa_sb) +TRANS_FEAT(USTMOPA_sb, aa64_sme_tmop, do_tmop_int, + a, MO_32, gen_helper_sme_ustmopa_sb) +TRANS_FEAT(UTMOPA_sb, aa64_sme_tmop, do_tmop_int, + a, MO_32, gen_helper_sme_utmopa_sb) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index fc4cc8c479..0becef16a9 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3697,7 +3697,7 @@ TRANS_FEAT(WHILE_gt_cnt4, aa64_sme2_or_sve2p1, do_WHILE, static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a) { - TCGv_i64 op0, op1, diff, t1, tmax; + TCGv_i64 op0, op1, diff, t1; TCGv_i32 t2; TCGv_ptr ptr; unsigned vsz = vec_full_reg_size(s); @@ -3713,7 +3713,6 @@ static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a) op0 = read_cpu_reg(s, a->rn, 1); op1 = read_cpu_reg(s, a->rm, 1); - tmax = tcg_constant_i64(vsz >> a->esz); diff = tcg_temp_new_i64(); if (a->rw) { @@ -3723,25 +3722,36 @@ static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a) tcg_gen_sub_i64(diff, op0, op1); tcg_gen_sub_i64(t1, op1, op0); tcg_gen_movcond_i64(TCG_COND_GEU, diff, op0, op1, diff, t1); - /* Divide, rounding down, by ESIZE. */ - tcg_gen_shri_i64(diff, diff, a->esz); - /* If op1 == op0, diff == 0, and the condition is always true. */ - tcg_gen_movcond_i64(TCG_COND_EQ, diff, op0, op1, tmax, diff); } else { /* WHILEWR */ - tcg_gen_sub_i64(diff, op1, op0); - /* Divide, rounding down, by ESIZE. */ - tcg_gen_shri_i64(diff, diff, a->esz); - /* If op0 >= op1, diff <= 0, the condition is always true. */ - tcg_gen_movcond_i64(TCG_COND_GEU, diff, op0, op1, tmax, diff); + /* Saturating subtraction maps diff <= 0 to diff == 0. */ + tcg_gen_ussub_i64(diff, op1, op0); } - /* Bound to the maximum. */ - tcg_gen_umin_i64(diff, diff, tmax); + /* Divide, rounding down, by ESIZE. */ + tcg_gen_shri_i64(diff, diff, a->esz); - /* Since we're bounded, pass as a 32-bit type. */ + /* + * If diff == 0, the condition is always true. Also, bound to max. + * Simplify + * diff = diff ? diff : max; + * diff = umin(diff, max); + * via + * diff -= 1; + * diff = umin(diff, max - 1); + * diff += 1; + * via 0 - 1 == UINT64_MAX. + */ + tcg_gen_addi_i64(diff, diff, -1); + tcg_gen_umin_i64(diff, diff, tcg_constant_i64((vsz >> a->esz) - 1)); + + /* + * Since we're bounded, pass as a 32-bit type. + * Sink the diff += 1 from above into the 32-bit type. + */ t2 = tcg_temp_new_i32(); tcg_gen_extrl_i64_i32(t2, diff); + tcg_gen_addi_i32(t2, t2, 1); desc = FIELD_DP32(desc, PREDDESC, OPRSZ, vsz / 8); desc = FIELD_DP32(desc, PREDDESC, ESZ, a->esz); diff --git a/target/arm/tcg/translate-vfp.c b/target/arm/tcg/translate-vfp.c index 6e944a0322..0a87a0165b 100644 --- a/target/arm/tcg/translate-vfp.c +++ b/target/arm/tcg/translate-vfp.c @@ -207,6 +207,20 @@ static void gen_update_fp_context(DisasContext *s) } } +/* + * Return true if a VFP insn is OK to access the registers indicated + * by regmask, false if it should UNDEF. This checks whether the + * D16-D31 regs are implemented by the CPU and not disabled by CPACR.D32DIS. + * Note that Neon insns accessing D16..D31 do not need to check D32DIS, + * so this function is for VFP insns only. + * + * @regmask should be the logical OR of the VFP Dregs being accessed. + */ +static bool vfp_dregs_ok(DisasContext *s, int dregmask) +{ + return !(dregmask & s->invalid_vfp_dreg_mask); +} + /* * Check that VFP access is enabled, A-profile specific version. * @@ -306,15 +320,25 @@ bool vfp_access_check(DisasContext *s) /* * Access check for Neon; this is for instructions which can be - * trapped by CPACR.ASEDIS and HCPTR.TASE. Support for those traps - * is optional and we currently do not implement them, so this - * is identical to a VFP access check for now. + * trapped by CPACR.ASEDIS and HCPTR.TASE. */ bool neon_access_check(DisasContext *s) { if (arm_dc_feature(s, ARM_FEATURE_M)) { return vfp_access_check_m(s, false); } else { + /* + * If the Neon-specific trap bits request a trap to a lower EL + * than the general FP trap bits, the trap to the lower EL + * has priority. + */ + if (s->neon_excp_el && + (!s->fp_excp_el || s->neon_excp_el < s->fp_excp_el)) { + uint32_t syn = syn_a32_fp_access_trap(1, 0xe, 1, 0); + + gen_exception_insn_el(s, 0, EXCP_UDEF, syn, s->neon_excp_el); + return false; + } return vfp_access_check_a(s, false, true); } } @@ -337,8 +361,7 @@ static bool trans_VSEL(DisasContext *s, arg_VSEL *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (sz == 3 && !dc_isar_feature(aa32_simd_r32, s) && - ((a->vm | a->vn | a->vd) & 0x10)) { + if (sz == 3 && !vfp_dregs_ok(s, a->vm | a->vn | a->vd)) { return false; } @@ -463,8 +486,7 @@ static bool trans_VRINT(DisasContext *s, arg_VRINT *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (sz == 3 && !dc_isar_feature(aa32_simd_r32, s) && - ((a->vm | a->vd) & 0x10)) { + if (sz == 3 && !vfp_dregs_ok(s, a->vm | a->vd)) { return false; } @@ -531,7 +553,7 @@ static bool trans_VCVT(DisasContext *s, arg_VCVT *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (sz == 3 && !dc_isar_feature(aa32_simd_r32, s) && (a->vm & 0x10)) { + if (sz == 3 && !vfp_dregs_ok(s, a->vm)) { return false; } @@ -652,7 +674,7 @@ static bool trans_VMOV_to_gp(DisasContext *s, arg_VMOV_to_gp *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vn & 0x10)) { + if (!vfp_dregs_ok(s, a->vn & 0x10)) { return false; } @@ -699,7 +721,7 @@ static bool trans_VMOV_from_gp(DisasContext *s, arg_VMOV_from_gp *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vn & 0x10)) { + if (!vfp_dregs_ok(s, a->vn & 0x10)) { return false; } @@ -735,7 +757,7 @@ static bool trans_VDUP(DisasContext *s, arg_VDUP *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vn & 0x10)) { + if (!vfp_dregs_ok(s, a->vn)) { return false; } @@ -1020,7 +1042,7 @@ static bool trans_VMOV_64_dp(DisasContext *s, arg_VMOV_64_dp *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vm & 0x10)) { + if (!vfp_dregs_ok(s, a->vm)) { return false; } @@ -1122,7 +1144,7 @@ static bool trans_VLDR_VSTR_dp(DisasContext *s, arg_VLDR_VSTR_dp *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) { + if (!vfp_dregs_ok(s, a->vd)) { return false; } @@ -1251,7 +1273,7 @@ static bool trans_VLDM_VSTM_dp(DisasContext *s, arg_VLDM_VSTM_dp *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd + n) > 16) { + if (!vfp_dregs_ok(s, a->vd + n - 1)) { return false; } @@ -1503,7 +1525,7 @@ static bool do_vfp_3op_dp(DisasContext *s, VFPGen3OpDPFn *fn, } /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_simd_r32, s) && ((vd | vn | vm) & 0x10)) { + if (!vfp_dregs_ok(s, vd | vn | vm)) { return false; } @@ -1675,7 +1697,7 @@ static bool do_vfp_2op_dp(DisasContext *s, VFPGen2OpDPFn *fn, int vd, int vm) /* Note that the caller must check the aa32_fpdp_v2 feature. */ /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_simd_r32, s) && ((vd | vm) & 0x10)) { + if (!vfp_dregs_ok(s, vd | vm)) { return false; } @@ -2242,8 +2264,7 @@ static bool do_vfm_dp(DisasContext *s, arg_VFMA_dp *a, bool neg_n, bool neg_d) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!vfp_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -2370,7 +2391,7 @@ static bool trans_VMOV_imm_dp(DisasContext *s, arg_VMOV_imm_dp *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && (vd & 0x10)) { + if (!vfp_dregs_ok(s, vd)) { return false; } @@ -2545,7 +2566,7 @@ static bool trans_VCMP_dp(DisasContext *s, arg_VCMP_dp *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && ((a->vd | a->vm) & 0x10)) { + if (!vfp_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -2611,7 +2632,7 @@ static bool trans_VCVT_f64_f16(DisasContext *s, arg_VCVT_f64_f16 *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) { + if (!vfp_dregs_ok(s, a->vd)) { return false; } @@ -2692,7 +2713,7 @@ static bool trans_VCVT_f16_f64(DisasContext *s, arg_VCVT_f16_f64 *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vm & 0x10)) { + if (!vfp_dregs_ok(s, a->vm)) { return false; } @@ -2767,7 +2788,7 @@ static bool trans_VRINTR_dp(DisasContext *s, arg_VRINTR_dp *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && ((a->vd | a->vm) & 0x10)) { + if (!vfp_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -2846,7 +2867,7 @@ static bool trans_VRINTZ_dp(DisasContext *s, arg_VRINTZ_dp *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && ((a->vd | a->vm) & 0x10)) { + if (!vfp_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -2920,7 +2941,7 @@ static bool trans_VRINTX_dp(DisasContext *s, arg_VRINTX_dp *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && ((a->vd | a->vm) & 0x10)) { + if (!vfp_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -2946,7 +2967,7 @@ static bool trans_VCVT_sp(DisasContext *s, arg_VCVT_sp *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) { + if (!vfp_dregs_ok(s, a->vd)) { return false; } @@ -2972,7 +2993,7 @@ static bool trans_VCVT_dp(DisasContext *s, arg_VCVT_dp *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vm & 0x10)) { + if (!vfp_dregs_ok(s, a->vm)) { return false; } @@ -3053,7 +3074,7 @@ static bool trans_VCVT_int_dp(DisasContext *s, arg_VCVT_int_dp *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) { + if (!vfp_dregs_ok(s, a->vd)) { return false; } @@ -3090,7 +3111,7 @@ static bool trans_VJCVT(DisasContext *s, arg_VJCVT *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vm & 0x10)) { + if (!vfp_dregs_ok(s, a->vm)) { return false; } @@ -3230,7 +3251,7 @@ static bool trans_VCVT_fix_dp(DisasContext *s, arg_VCVT_fix_dp *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) { + if (!vfp_dregs_ok(s, a->vd)) { return false; } @@ -3359,7 +3380,7 @@ static bool trans_VCVT_dp_int(DisasContext *s, arg_VCVT_dp_int *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vm & 0x10)) { + if (!vfp_dregs_ok(s, a->vm)) { return false; } diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index a1fc050618..c866148383 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -3235,10 +3235,14 @@ static bool trans_YIELD(DisasContext *s, arg_YIELD *a) * the next round-robin scheduled vCPU gets a crack. When running in * MTTCG we don't generate jumps to the helper as it won't affect the * scheduling of other vCPUs. + * This is a NOP hint on older architectures. */ - if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { - gen_update_pc(s, curr_insn_len(s)); - s->base.is_jmp = DISAS_YIELD; + if (arm_dc_feature(s, ARM_FEATURE_M) || + arm_dc_feature(s, ARM_FEATURE_V6K)) { + if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { + gen_update_pc(s, curr_insn_len(s)); + s->base.is_jmp = DISAS_YIELD; + } } return true; } @@ -3246,12 +3250,19 @@ static bool trans_YIELD(DisasContext *s, arg_YIELD *a) static bool trans_SEV(DisasContext *s, arg_SEV *a) { /* - * SEV is a NOP for user-mode emulation. For v6T2 and earlier - * non-M-profile cores this encoding is a NOP hint. + * SEV is a NOP for user-mode emulation. The instruction is + * also a NOP hint on cores that pre-date the architectural + * feature that adds it: + * - M-profile always has SEV + * - for A/R profile, it exists from v6K onward + * The v7A Arm ARM is not entirely clear about whether v6K has the + * Thumb SEV or not; we make the condition the same, to be + * conservative. (If guests try to execute the Thumb SEV insn it + * will be because they want SEV, not because they want a NOP.) */ #ifndef CONFIG_USER_ONLY if (arm_dc_feature(s, ARM_FEATURE_M) || - arm_dc_feature(s, ARM_FEATURE_V7)) { + arm_dc_feature(s, ARM_FEATURE_V6K)) { gen_helper_sev(tcg_env); } #endif @@ -3273,17 +3284,30 @@ static bool trans_SEVL(DisasContext *s, arg_SEV *a) static bool trans_WFE(DisasContext *s, arg_WFE *a) { - /* For WFE, halt the vCPU until an event. */ - gen_update_pc(s, curr_insn_len(s)); - s->base.is_jmp = DISAS_WFE; + /* + * For WFE, halt the vCPU until an event. This is a NOP + * hint on older architectures, with the same conditions + * as SEV. + */ + if (arm_dc_feature(s, ARM_FEATURE_M) || + arm_dc_feature(s, ARM_FEATURE_V6K)) { + gen_update_pc(s, curr_insn_len(s)); + s->base.is_jmp = DISAS_WFE; + } return true; } static bool trans_WFI(DisasContext *s, arg_WFI *a) { - /* For WFI, halt the vCPU until an IRQ. */ - gen_update_pc(s, curr_insn_len(s)); - s->base.is_jmp = DISAS_WFI; + /* + * For WFI, halt the vCPU until an IRQ. This is a NOP + * hint on older architectures. + */ + if (arm_dc_feature(s, ARM_FEATURE_M) || + arm_dc_feature(s, ARM_FEATURE_V6K)) { + gen_update_pc(s, curr_insn_len(s)); + s->base.is_jmp = DISAS_WFI; + } return true; } @@ -4811,7 +4835,7 @@ static bool trans_RBIT(DisasContext *s, arg_rr *a) if (!ENABLE_ARCH_6T2) { return false; } - return op_rr(s, a, gen_helper_rbit); + return op_rr(s, a, tcg_gen_revbit32_i32); } /* @@ -6318,6 +6342,7 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ARMCPU *cpu = env_archcpu(env); CPUARMTBFlags tb_flags = arm_tbflags_from_tb(dc->base.tb); uint32_t condexec, core_mmu_idx; + bool d32dis = false; dc->isar = &cpu->isar; dc->condjmp = 0; @@ -6379,7 +6404,14 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->vec_stride = EX_TBFLAG_A32(tb_flags, VECSTRIDE); dc->sme_trap_nonstreaming = EX_TBFLAG_A32(tb_flags, SME_TRAP_NONSTREAMING); + dc->neon_excp_el = EX_TBFLAG_A32(tb_flags, NEONEXC_EL); + d32dis = EX_TBFLAG_A32(tb_flags, D32DIS); } + + dc->invalid_vfp_dreg_mask = + (d32dis || !dc_isar_feature(aa32_simd_r32, dc)) ? 0x10 : 0; + dc->invalid_neon_dreg_mask = !dc_isar_feature(aa32_simd_r32, dc) ? 0x10 : 0; + dc->lse2 = false; /* applies only to aarch64 */ dc->cp_regs = cpu->cp_regs; dc->features = env->features; diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index a3d03159ad..31f52a34a6 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -88,11 +88,14 @@ typedef struct DisasContext { int sve_excp_el; /* SVE exception EL or 0 if enabled */ int sme_excp_el; /* SME exception EL or 0 if enabled */ int zt0_excp_el; /* ZT0 exception EL or 0 if enabled */ + int neon_excp_el; /* A32 Neon exception EL or 0 if enabled */ int vl; /* current vector length in bytes */ int svl; /* current streaming vector length in bytes */ int max_svl; /* maximum implemented streaming vector length */ int max_any_vl; /* maximum implemented vector length */ bool vfp_enabled; /* FP enabled via FPSCR.EN */ + int invalid_vfp_dreg_mask; /* mask for whether VFP D16..D31 should UNDEF */ + int invalid_neon_dreg_mask; /* ditto, for Neon */ int vec_len; int vec_stride; bool v7m_handler_mode; diff --git a/target/arm/tcg/vec_internal.h b/target/arm/tcg/vec_internal.h index 038a2a3439..8f6d8a984b 100644 --- a/target/arm/tcg/vec_internal.h +++ b/target/arm/tcg/vec_internal.h @@ -555,4 +555,11 @@ void sme_mop4(void *vza, void *vzn, void *vzm, void *fn_opaque, uint32_t desc, size_t esize, void (*fn)(void *, void *, void *, void *)); +/* + * Perform SME sparse outer product, 4-way, 8 to 32-bit. + */ +void sme_tmop_4way_sb(uint32_t *za, uint8_t *zn0, uint32_t *zm, + uint64_t *zk, void *fn_opaque, uint32_t desc, + void (*fn)(void *, void *, void *, void *)); + #endif /* TARGET_ARM_VEC_INTERNAL_H */ diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 3591219212..f8409f32ab 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -131,7 +131,7 @@ static void avr_cpu_realizefn(DeviceState *dev, Error **errp) AVRCPUClass *mcc = AVR_CPU_GET_CLASS(dev); Error *local_err = NULL; - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/hexagon/arch.c b/target/hexagon/arch.c index 0a400bf7a7..3c417358e0 100644 --- a/target/hexagon/arch.c +++ b/target/hexagon/arch.c @@ -199,6 +199,10 @@ void arch_fpop_start(CPUHexagonState *env) set_float_rounding_mode( softfloat_roundingmodes[fREAD_REG_FIELD(USR, USR_FPRND)], &env->fp_status); + /* + * No need to check env->hvx_fp_status, these instructions don't + * raise exceptions nor interact with usr fields. + */ } #ifdef CONFIG_USER_ONLY @@ -237,6 +241,10 @@ void arch_fpop_end(CPUHexagonState *env, bool pkt_need_commit) SOFTFLOAT_TEST_FLAG(float_flag_overflow, FPOVFF, FPOVFE); SOFTFLOAT_TEST_FLAG(float_flag_underflow, FPUNFF, FPUNFE); } + /* + * No need to check env->hvx_fp_status, these instructions don't + * raise exceptions nor interact with usr fields. + */ } int arch_sf_recip_common(float32 *Rs, float32 *Rt, float32 *Rd, int *adjust, diff --git a/target/hexagon/attribs_def.h.inc b/target/hexagon/attribs_def.h.inc index 6c55063a30..d12f6ac4b1 100644 --- a/target/hexagon/attribs_def.h.inc +++ b/target/hexagon/attribs_def.h.inc @@ -84,6 +84,7 @@ DEF_ATTRIB(CVI_SCATTER, "CVI Scatter operation", "", "") DEF_ATTRIB(CVI_SCATTER_RELEASE, "CVI Store Release for scatter", "", "") DEF_ATTRIB(CVI_TMP_DST, "CVI instruction that doesn't write a register", "", "") DEF_ATTRIB(CVI_SLOT23, "Can execute in slot 2 or slot 3 (HVX)", "", "") +DEF_ATTRIB(CVI_VA_2SRC, "Execs on multimedia vector engine; requires two srcs", "", "") DEF_ATTRIB(VTCM_ALLBANK_ACCESS, "Allocates in all VTCM schedulers.", "", "") @@ -196,5 +197,13 @@ DEF_ATTRIB(NOTE_SHIFT_RESOURCE, "Uses the HVX shift resource.", "", "") DEF_ATTRIB(RESTRICT_NOSLOT1_STORE, "Packet must not have slot 1 store", "", "") DEF_ATTRIB(RESTRICT_LATEPRED, "Predicate can not be used as a .new.", "", "") +/* HVX IEEE FP extension attributes */ +DEF_ATTRIB(HVX_IEEE_FP, "HVX IEEE FP extension instruction", "", "") +DEF_ATTRIB(HVX_IEEE_FP_ACC, "HVX IEEE FP accumulate instruction", "", "") +DEF_ATTRIB(HVX_IEEE_FP_OUT_16, "HVX IEEE FP 16-bit output", "", "") +DEF_ATTRIB(HVX_IEEE_FP_OUT_32, "HVX IEEE FP 32-bit output", "", "") +DEF_ATTRIB(CVI_VX_NO_TMP_LD, "HVX multiply without tmp load", "", "") +DEF_ATTRIB(HVX_FLT, "This a floating point HVX instruction.", "", "") + /* Keep this as the last attribute: */ DEF_ATTRIB(ZZ_LASTATTRIB, "Last attribute in the file", "", "") diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 42d93e5da4..7067e5b70f 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -65,14 +65,17 @@ static const Property hexagon_cpu_properties[] = { DEFINE_PROP_LINK("tlb", HexagonCPU, tlb, TYPE_HEXAGON_TLB, HexagonTLBState *), DEFINE_PROP_UINT32("exec-start-addr", HexagonCPU, boot_addr, 0xffffffff), + DEFINE_PROP_LINK("l2vic", HexagonCPU, l2vic, + TYPE_HEX_L2VIC_INTERFACE, HexL2VicInterface *), DEFINE_PROP_LINK("global-regs", HexagonCPU, globalregs, TYPE_HEXAGON_GLOBALREG, HexagonGlobalRegState *), DEFINE_PROP_UINT32("htid", HexagonCPU, htid, 0), #endif - DEFINE_PROP_BOOL("lldb-compat", HexagonCPU, lldb_compat, false), - DEFINE_PROP_UNSIGNED("lldb-stack-adjust", HexagonCPU, lldb_stack_adjust, 0, - qdev_prop_uint32, target_ulong), - DEFINE_PROP_BOOL("short-circuit", HexagonCPU, short_circuit, true), + DEFINE_PROP_BOOL("lldb-compat", HexagonCPU, cfg.lldb_compat, false), + DEFINE_PROP_UNSIGNED("lldb-stack-adjust", HexagonCPU, cfg.lldb_stack_adjust, + 0, qdev_prop_uint32, target_ulong), + DEFINE_PROP_BOOL("short-circuit", HexagonCPU, cfg.short_circuit, true), + DEFINE_PROP_BOOL("ieee-fp", HexagonCPU, cfg.ieee_fp_extension, true), }; const char * const hexagon_regnames[TOTAL_PER_THREAD_REGS] = { @@ -124,7 +127,7 @@ const char * const hexagon_gregnames[] = { static target_ulong adjust_stack_ptrs(CPUHexagonState *env, target_ulong addr) { HexagonCPU *cpu = env_archcpu(env); - target_ulong stack_adjust = cpu->lldb_stack_adjust; + target_ulong stack_adjust = cpu->cfg.lldb_stack_adjust; target_ulong stack_start = env->stack_start; target_ulong stack_size = 0x10000; @@ -236,7 +239,7 @@ static void hexagon_dump(CPUHexagonState *env, FILE *f, int flags) { HexagonCPU *cpu = env_archcpu(env); - if (cpu->lldb_compat) { + if (cpu->cfg.lldb_compat) { /* * When comparing with LLDB, it doesn't step through single-cycle * hardware loops the same way. So, we just skip them here @@ -417,6 +420,9 @@ static void hexagon_cpu_reset_hold(Object *obj, ResetType type) set_float_detect_tininess(float_tininess_before_rounding, &env->fp_status); /* Default NaN value: sign bit set, all frac bits set */ set_float_default_nan_pattern(0b11111111, &env->fp_status); + + set_default_nan_mode(1, &env->hvx_fp_status); + set_float_default_nan_pattern(0b01111111, &env->hvx_fp_status); #ifndef CONFIG_USER_ONLY memset(env->t_sreg, 0, sizeof(uint32_t) * NUM_SREGS); memset(env->greg, 0, sizeof(uint32_t) * NUM_GREGS); @@ -441,21 +447,24 @@ static void hexagon_cpu_disas_set_info(const CPUState *cs, const HexagonCPU *cpu = HEXAGON_CPU(cs); info->print_insn = print_insn_hexagon; info->endian = BFD_ENDIAN_LITTLE; - info->target_info = HEXAGON_CPU_GET_CLASS(cpu)->hex_def; + info->target_info = &cpu->cfg; } static void hexagon_cpu_realize(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); + HexagonCPU *cpu = HEXAGON_CPU(dev); HexagonCPUClass *mcc = HEXAGON_CPU_GET_CLASS(dev); Error *local_err = NULL; - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; } + cpu->cfg.hex_def = mcc->hex_def; + gdb_register_coprocessor(cs, hexagon_hvx_gdb_read_register, hexagon_hvx_gdb_write_register, gdb_find_static_feature("hexagon-hvx.xml")); @@ -569,7 +578,9 @@ static hwaddr hexagon_cpu_get_phys_addr_debug(CPUState *cs, vaddr addr) if (get_physical_address(env, &phys_addr, &prot, &page_size, &excp, addr, 0, mmu_idx)) { + vaddr page_offset = addr & (TARGET_PAGE_SIZE - 1); find_qemu_subpage(&addr, &phys_addr, page_size); + phys_addr += hexagon_cpu_mmu_enabled(env) ? page_offset : 0; return phys_addr; } @@ -637,6 +648,18 @@ static void raise_perm_exception(CPUState *cs, uint32_t VA, int slot, cs->exception_index = excp; } +static void raise_misaligned_exception(CPUState *cs, uint32_t VA, int slot, + MMUAccessType access_type) +{ + CPUHexagonState *env = cpu_env(cs); + int32_t excp = (access_type == MMU_DATA_STORE) ? + HEX_CAUSE_MISALIGNED_STORE : HEX_CAUSE_MISALIGNED_LOAD; + + set_badva_regs(env, VA, slot, access_type); + cs->exception_index = HEX_EVENT_PRECISE; + env->cause_code = excp; +} + static const char *access_type_names[] = { "MMU_DATA_LOAD ", "MMU_DATA_STORE", "MMU_INST_FETCH" }; @@ -715,6 +738,18 @@ static vaddr hexagon_pointer_wrap(CPUState *cs, int mmu_idx, return result; } +static G_NORETURN +void hexagon_cpu_do_unaligned_access(CPUState *cs, vaddr addr, + MMUAccessType access_type, int mmu_idx, + uintptr_t retaddr) +{ + CPUHexagonState *env = cpu_env(cs); + + raise_misaligned_exception(cs, addr, 0, access_type); + do_raise_exception(env, cs->exception_index, env->gpr[HEX_REG_PC], + retaddr); +} + #endif static const TCGCPUOps hexagon_tcg_ops = { @@ -732,6 +767,7 @@ static const TCGCPUOps hexagon_tcg_ops = { .pointer_wrap = hexagon_pointer_wrap, .cpu_exec_reset = cpu_reset, .tlb_fill = hexagon_tlb_fill, + .do_unaligned_access = hexagon_cpu_do_unaligned_access, .cpu_exec_halt = hexagon_cpu_has_work, .do_interrupt = hexagon_cpu_do_interrupt, #endif /* !CONFIG_USER_ONLY */ diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index 7694fd91fa..c50fbb3f72 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -39,6 +39,7 @@ typedef struct HexagonGlobalRegState HexagonGlobalRegState; #include "qemu/bitmap.h" #include "target/hexagon/reg_fields.h" +#include "hw/intc/hex-l2vic.h" #define NUM_PREGS 4 #define TOTAL_PER_THREAD_REGS 64 @@ -151,6 +152,7 @@ typedef struct CPUArchState { MemLog mem_log_stores[STORES_MAX]; float_status fp_status; + float_status hvx_fp_status; target_ulong llsc_addr; target_ulong llsc_val; @@ -185,24 +187,22 @@ typedef struct HexagonCPUClass { const HexagonCPUDef *hex_def; } HexagonCPUClass; +#include "cpu_bits.h" + struct ArchCPU { CPUState parent_obj; CPUHexagonState env; - - bool lldb_compat; - target_ulong lldb_stack_adjust; - bool short_circuit; + HexagonCPUConfig cfg; #ifndef CONFIG_USER_ONLY HexagonTLBState *tlb; uint32_t boot_addr; HexagonGlobalRegState *globalregs; uint32_t htid; + HexL2VicInterface *l2vic; #endif }; -#include "cpu_bits.h" - FIELD(TB_FLAGS, IS_TIGHT_LOOP, 0, 1) FIELD(TB_FLAGS, MMU_INDEX, 1, 3) FIELD(TB_FLAGS, PCYCLE_ENABLED, 4, 1) diff --git a/target/hexagon/cpu_bits.h b/target/hexagon/cpu_bits.h index 164e74c782..a8fba4aa18 100644 --- a/target/hexagon/cpu_bits.h +++ b/target/hexagon/cpu_bits.h @@ -21,6 +21,14 @@ #include "qemu/bitops.h" #include "cpu-qom.h" +typedef struct HexagonCPUConfig { + bool lldb_compat; + uint32_t lldb_stack_adjust; + bool short_circuit; + bool ieee_fp_extension; + const HexagonCPUDef *hex_def; +} HexagonCPUConfig; + #define PCALIGN 4 #define PCALIGN_MASK (PCALIGN - 1) @@ -123,7 +131,7 @@ static inline bool is_packet_end(uint32_t endocing) return ((bits == 0x3) || (bits == 0x0)); } -int disassemble_hexagon(uint32_t *words, int nwords, bfd_vma pc, GString *buf, - const HexagonCPUDef *hex_def); +int disassemble_hexagon(uint32_t *words, int nwords, bfd_vma pc, + GString *buf, const HexagonCPUConfig *cfg); #endif diff --git a/target/hexagon/decode.c b/target/hexagon/decode.c index 6eddcca26e..b12e91fe6a 100644 --- a/target/hexagon/decode.c +++ b/target/hexagon/decode.c @@ -549,21 +549,35 @@ static bool decode_parsebits_is_loopend(uint32_t encoding32) return bits == 0x2; } +/* + * Check that the packet's instructions can be grouped into slots: walk them + * in encoding order handing out slots in strictly decreasing order, and fail + * if an instruction has no valid slot at or below the running slot. Two + * instructions may legally share a slot, so this does not require unique + * slots, only that every instruction fits. + */ static bool has_valid_slot_assignment(Packet *pkt) { - int used_slots = 0; - for (int i = 0; i < pkt->num_insns; i++) { - int slot_mask; - Insn *insn = &pkt->insn[i]; - if (decode_opcode_ends_loop(insn->opcode)) { + int i; + int slot = 3; + + for (i = 0; i < pkt->num_insns; i++) { + SlotMask valid_slots; + if (decode_opcode_ends_loop(pkt->insn[i].opcode)) { /* We overload slot 0 for endloop. */ continue; } - slot_mask = 1 << insn->slot; - if (used_slots & slot_mask) { + if (slot < 0) { return false; } - used_slots |= slot_mask; + valid_slots = get_valid_slots(pkt, i); + while (!(valid_slots & (1 << slot))) { + if (slot <= 0) { + return false; + } + slot--; + } + slot--; } return true; } @@ -842,7 +856,7 @@ int decode_packet(DisasContext *ctx, int max_words, const uint32_t *words, /* Used for "-d in_asm" logging */ int disassemble_hexagon(uint32_t *words, int nwords, bfd_vma pc, - GString *buf, const HexagonCPUDef *hex_def) + GString *buf, const HexagonCPUConfig *cfg) { HexagonCPUDef any_def = { .hex_version = HEX_VER_ANY, /* Allow decode to accept anything */ @@ -853,7 +867,7 @@ int disassemble_hexagon(uint32_t *words, int nwords, bfd_vma pc, ctx.hex_def = &any_def; if (decode_packet(&ctx, nwords, words, &ctx.pkt, true) > 0) { - snprint_a_pkt_disas(buf, &ctx.pkt, words, pc, hex_def); + snprint_a_pkt_disas(buf, &ctx.pkt, words, pc, cfg); return ctx.pkt.encod_pkt_size_in_bytes; } else { for (int i = 0; i < nwords; i++) { diff --git a/target/hexagon/gen_printinsn.py b/target/hexagon/gen_printinsn.py index d5f969960a..cf1a12adbc 100755 --- a/target/hexagon/gen_printinsn.py +++ b/target/hexagon/gen_printinsn.py @@ -28,13 +28,14 @@ import argparse ## Generate data for printing each instruction (format string + operands) ## def regprinter(m): - str = m.group(1) - str += ":".join(["%d"] * len(m.group(2))) - str += m.group(3) if ("S" in m.group(1)) and (len(m.group(2)) == 1): - str += "/%s" + str = "%s" elif ("C" in m.group(1)) and (len(m.group(2)) == 1): - str += "/%s" + str = "%s" + else: + str = m.group(1) + str += ":".join(["%d"] * len(m.group(2))) + str += m.group(3) return str @@ -142,11 +143,12 @@ def main(): else: regno = ri if len(b) == 1: - f.write(f", insn->regno[{regno}]") if "S" in a: f.write(f", sreg2str(insn->regno[{regno}])") elif "C" in a: f.write(f", creg2str(insn->regno[{regno}])") + else: + f.write(f", insn->regno[{regno}]") elif len(b) == 2: f.write(f", insn->regno[{regno}] + 1" f", insn->regno[{regno}]") else: diff --git a/target/hexagon/gen_tcg.h b/target/hexagon/gen_tcg.h index a28af0c245..40e03781d3 100644 --- a/target/hexagon/gen_tcg.h +++ b/target/hexagon/gen_tcg.h @@ -414,7 +414,7 @@ #define fGEN_TCG_STORE(SHORTCODE) \ do { \ - TCGv HALF G_GNUC_UNUSED = tcg_temp_new(); \ + TCGv tmp_half G_GNUC_UNUSED = tcg_temp_new(); \ TCGv BYTE G_GNUC_UNUSED = tcg_temp_new(); \ SHORTCODE; \ } while (0) @@ -422,7 +422,7 @@ #define fGEN_TCG_STORE_pcr(SHIFT, STORE) \ do { \ TCGv ireg = tcg_temp_new(); \ - TCGv HALF G_GNUC_UNUSED = tcg_temp_new(); \ + TCGv tmp_half G_GNUC_UNUSED = tcg_temp_new(); \ TCGv BYTE G_GNUC_UNUSED = tcg_temp_new(); \ tcg_gen_mov_tl(EA, RxV); \ gen_read_ireg(ireg, MuV, SHIFT); \ diff --git a/target/hexagon/gen_tcg_funcs.py b/target/hexagon/gen_tcg_funcs.py index 6d5d99cee3..2592acf21e 100755 --- a/target/hexagon/gen_tcg_funcs.py +++ b/target/hexagon/gen_tcg_funcs.py @@ -23,6 +23,15 @@ import string import hex_common from textwrap import dedent +def gen_disabled_ieee_insn(f, tag, regs): + f.write(" if (!ctx->ieee_fp_extension) {\n") + for regtype, regid in regs: + reg = hex_common.get_register(tag, regtype, regid) + if reg.is_hvx_reg() and reg.is_written(): + reg.gen_zero(f) + f.write(" return;\n") + f.write(" }\n") + ## ## Generate the TCG code to call the helper ## For A2_add: Rd32=add(Rs32,Rt32), { RdV=RsV+RtV;} @@ -74,7 +83,25 @@ def gen_tcg_func(f, tag, regs, imms): i = 1 if immlett.isupper() else 0 f.write(f" int {hex_common.imm_name(immlett)} = insn->immed[{i}];\n") + if "A_HVX_IEEE_FP" in hex_common.attribdict[tag]: + gen_disabled_ieee_insn(f, tag, regs) + if hex_common.is_idef_parser_enabled(tag): + gpr_operands = [ + hex_common.get_register(tag, regtype, regid) + for regtype, regid in regs + if hex_common.get_register(tag, regtype, regid).may_alias_gpr() + ] + dests = [reg for reg in gpr_operands if reg.is_written()] + for reg in gpr_operands: + if reg.is_written() or not reg.is_read(): + continue + src = reg.reg_tcg() + for dest in dests: + f.write(hex_common.code_fmt(f"""\ + {src} = gen_unalias_gpr_src({src}, {dest.reg_tcg()}); + """)) + declared = [] ## Handle registers for regtype, regid in regs: diff --git a/target/hexagon/gen_tcg_hvx.h b/target/hexagon/gen_tcg_hvx.h index 0da64d467e..2a342cdee6 100644 --- a/target/hexagon/gen_tcg_hvx.h +++ b/target/hexagon/gen_tcg_hvx.h @@ -234,6 +234,176 @@ static inline void assert_vhist_tmp(DisasContext *ctx) tcg_gen_gvec_sub(MO_32, VddV_off, VuuV_off, VvvV_off, \ sizeof(MMVector) * 2, sizeof(MMVector) * 2) +#define fGEN_TCG_V6_vaddbsat(SHORTCODE) \ + tcg_gen_gvec_ssadd(MO_8, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vaddhsat(SHORTCODE) \ + tcg_gen_gvec_ssadd(MO_16, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vaddwsat(SHORTCODE) \ + tcg_gen_gvec_ssadd(MO_32, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vaddubsat(SHORTCODE) \ + tcg_gen_gvec_usadd(MO_8, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vadduhsat(SHORTCODE) \ + tcg_gen_gvec_usadd(MO_16, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vadduwsat(SHORTCODE) \ + tcg_gen_gvec_usadd(MO_32, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_PAIR_ADDSUB(OP, VECE, DST, SRC_A, SRC_B) \ + tcg_gen_gvec_##OP(VECE, DST, SRC_A, SRC_B, \ + 2 * VECTOR_SIZE_BYTE, 2 * VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vaddbsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(ssadd, MO_8, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vaddhsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(ssadd, MO_16, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vaddwsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(ssadd, MO_32, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vaddubsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(usadd, MO_8, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vadduhsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(usadd, MO_16, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vadduwsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(usadd, MO_32, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vsubbsat(SHORTCODE) \ + tcg_gen_gvec_sssub(MO_8, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vsubhsat(SHORTCODE) \ + tcg_gen_gvec_sssub(MO_16, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vsubwsat(SHORTCODE) \ + tcg_gen_gvec_sssub(MO_32, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vsububsat(SHORTCODE) \ + tcg_gen_gvec_ussub(MO_8, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vsubuhsat(SHORTCODE) \ + tcg_gen_gvec_ussub(MO_16, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vsubuwsat(SHORTCODE) \ + tcg_gen_gvec_ussub(MO_32, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vsubbsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(sssub, MO_8, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vsubhsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(sssub, MO_16, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vsubwsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(sssub, MO_32, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vsububsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(ussub, MO_8, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vsubuhsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(ussub, MO_16, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vsubuwsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(ussub, MO_32, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vmpyih(SHORTCODE) \ + tcg_gen_gvec_mul(MO_16, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vabsdiffub(SHORTCODE) \ + gen_gvec_uabsdiff(MO_8, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vabsdiffuh(SHORTCODE) \ + gen_gvec_uabsdiff(MO_16, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vabsdiffh(SHORTCODE) \ + gen_gvec_sabsdiff(MO_16, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vabsdiffw(SHORTCODE) \ + gen_gvec_sabsdiff(MO_32, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_VEC_AVG(VECE, SHIFT_FN) \ + do { \ + intptr_t tmpoff = offsetof(CPUHexagonState, vtmp); \ + tcg_gen_gvec_and(MO_64, tmpoff, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE); \ + tcg_gen_gvec_xor(MO_64, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE); \ + SHIFT_FN(VECE, VdV_off, VdV_off, 1, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE); \ + tcg_gen_gvec_add(VECE, VdV_off, tmpoff, VdV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE); \ + } while (0) + +#define fGEN_TCG_V6_vavgub(SHORTCODE) \ + fGEN_TCG_VEC_AVG(MO_8, tcg_gen_gvec_shri) + +#define fGEN_TCG_V6_vavguh(SHORTCODE) \ + fGEN_TCG_VEC_AVG(MO_16, tcg_gen_gvec_shri) + +#define fGEN_TCG_V6_vavguw(SHORTCODE) \ + fGEN_TCG_VEC_AVG(MO_32, tcg_gen_gvec_shri) + +#define fGEN_TCG_V6_vavgb(SHORTCODE) \ + fGEN_TCG_VEC_AVG(MO_8, tcg_gen_gvec_sari) + +#define fGEN_TCG_V6_vavgh(SHORTCODE) \ + fGEN_TCG_VEC_AVG(MO_16, tcg_gen_gvec_sari) + +#define fGEN_TCG_V6_vavgw(SHORTCODE) \ + fGEN_TCG_VEC_AVG(MO_32, tcg_gen_gvec_sari) + +#define fGEN_TCG_VEC_AVGRND(VECE, SHIFT_FN) \ + do { \ + intptr_t tmpoff = offsetof(CPUHexagonState, vtmp); \ + tcg_gen_gvec_or(MO_64, tmpoff, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE); \ + tcg_gen_gvec_xor(MO_64, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE); \ + SHIFT_FN(VECE, VdV_off, VdV_off, 1, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE); \ + tcg_gen_gvec_sub(VECE, VdV_off, tmpoff, VdV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE); \ + } while (0) + +#define fGEN_TCG_V6_vavgubrnd(SHORTCODE) \ + fGEN_TCG_VEC_AVGRND(MO_8, tcg_gen_gvec_shri) + +#define fGEN_TCG_V6_vavguhrnd(SHORTCODE) \ + fGEN_TCG_VEC_AVGRND(MO_16, tcg_gen_gvec_shri) + +#define fGEN_TCG_V6_vavguwrnd(SHORTCODE) \ + fGEN_TCG_VEC_AVGRND(MO_32, tcg_gen_gvec_shri) + +#define fGEN_TCG_V6_vavgbrnd(SHORTCODE) \ + fGEN_TCG_VEC_AVGRND(MO_8, tcg_gen_gvec_sari) + +#define fGEN_TCG_V6_vavghrnd(SHORTCODE) \ + fGEN_TCG_VEC_AVGRND(MO_16, tcg_gen_gvec_sari) + +#define fGEN_TCG_V6_vavgwrnd(SHORTCODE) \ + fGEN_TCG_VEC_AVGRND(MO_32, tcg_gen_gvec_sari) + /* Vector shift right - various forms */ #define fGEN_TCG_V6_vasrh(SHORTCODE) \ do { \ diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index 1ebc747140..2a98b13b71 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c @@ -37,6 +37,76 @@ #include "genptr.h" +static void gen_sabsdiff_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + tcg_gen_sub_i32(t, a, b); + tcg_gen_sub_i32(d, b, a); + tcg_gen_movcond_i32(TCG_COND_LT, d, a, b, d, t); +} + +static void gen_sabsdiff_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + tcg_gen_smin_vec(vece, t, a, b); + tcg_gen_smax_vec(vece, d, a, b); + tcg_gen_sub_vec(vece, d, d, t); +} + +void gen_gvec_sabsdiff(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sub_vec, INDEX_op_smin_vec, INDEX_op_smax_vec, 0 + }; + static const GVecGen3 ops[4] = { + [MO_16] = { .fniv = gen_sabsdiff_vec, + .fno = gen_helper_gvec_sabsdiff_h, + .opt_opc = vecop_list, + .vece = MO_16 }, + [MO_32] = { .fni4 = gen_sabsdiff_i32, + .fniv = gen_sabsdiff_vec, + .fno = gen_helper_gvec_sabsdiff_w, + .opt_opc = vecop_list, + .vece = MO_32 }, + }; + + tcg_debug_assert(vece == MO_16 || vece == MO_32); + tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &ops[vece]); +} + +static void gen_uabsdiff_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + tcg_gen_umin_vec(vece, t, a, b); + tcg_gen_umax_vec(vece, d, a, b); + tcg_gen_sub_vec(vece, d, d, t); +} + +void gen_gvec_uabsdiff(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sub_vec, INDEX_op_umin_vec, INDEX_op_umax_vec, 0 + }; + static const GVecGen3 ops[4] = { + [MO_8] = { .fniv = gen_uabsdiff_vec, + .fno = gen_helper_gvec_uabsdiff_b, + .opt_opc = vecop_list, + .vece = MO_8 }, + [MO_16] = { .fniv = gen_uabsdiff_vec, + .fno = gen_helper_gvec_uabsdiff_h, + .opt_opc = vecop_list, + .vece = MO_16 }, + }; + + tcg_debug_assert(vece == MO_8 || vece == MO_16); + tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &ops[vece]); +} + TCGv gen_read_reg(TCGv result, int num) { tcg_gen_mov_tl(result, hex_gpr[num]); @@ -91,6 +161,17 @@ TCGv get_result_gpr(DisasContext *ctx, int rnum) } } +TCGv gen_unalias_gpr_src(TCGv src, TCGv dst) +{ + if (src != dst) { + return src; + } + + TCGv tmp = tcg_temp_new(); + tcg_gen_mov_tl(tmp, src); + return tmp; +} + static TCGv_i64 get_result_gpr_pair(DisasContext *ctx, int rnum) { TCGv_i64 result = tcg_temp_new_i64(); @@ -470,14 +551,14 @@ void gen_set_byte_i64(int N, TCGv_i64 result, TCGv src) static inline void gen_load_locked4u(TCGv dest, TCGv vaddr, int mem_index) { - tcg_gen_qemu_ld_tl(dest, vaddr, mem_index, MO_LE | MO_UL); + tcg_gen_qemu_ld_tl(dest, vaddr, mem_index, MO_LE | MO_UL | MO_ALIGN); tcg_gen_mov_tl(hex_llsc_addr, vaddr); tcg_gen_mov_tl(hex_llsc_val, dest); } static inline void gen_load_locked8u(TCGv_i64 dest, TCGv vaddr, int mem_index) { - tcg_gen_qemu_ld_i64(dest, vaddr, mem_index, MO_LE | MO_UQ); + tcg_gen_qemu_ld_i64(dest, vaddr, mem_index, MO_LE | MO_UQ | MO_ALIGN); tcg_gen_mov_tl(hex_llsc_addr, vaddr); tcg_gen_mov_i64(hex_llsc_val_i64, dest); } @@ -495,7 +576,7 @@ static inline void gen_store_conditional4(DisasContext *ctx, zero = tcg_constant_tl(0); tmp = tcg_temp_new(); tcg_gen_atomic_cmpxchg_tl(tmp, hex_llsc_addr, hex_llsc_val, src, - ctx->mem_idx, MO_32); + ctx->mem_idx, MO_32 | MO_ALIGN); tcg_gen_movcond_tl(TCG_COND_EQ, pred, tmp, hex_llsc_val, one, zero); tcg_gen_br(done); @@ -520,7 +601,7 @@ static inline void gen_store_conditional8(DisasContext *ctx, zero = tcg_constant_i64(0); tmp = tcg_temp_new_i64(); tcg_gen_atomic_cmpxchg_i64(tmp, hex_llsc_addr, hex_llsc_val_i64, src, - ctx->mem_idx, MO_64); + ctx->mem_idx, MO_64 | MO_ALIGN); tcg_gen_movcond_i64(TCG_COND_EQ, tmp, tmp, hex_llsc_val_i64, one, zero); tcg_gen_extrl_i64_i32(pred, tmp); @@ -613,14 +694,22 @@ static void gen_write_new_pc_addr(DisasContext *ctx, TCGv addr, tcg_gen_brcondi_tl(cond, pred, 1, pred_false); } + /* + * If gen_end_tb() will unconditionally overwrite PC with hex_next_PC + * (because this packet has a predicated COF that may not execute), + * write the branch target there instead of directly into the PC + * global, or the overwrite in gen_end_tb() would clobber it. + */ + TCGv pc_wr = ctx->need_next_pc ? hex_next_PC : hex_gpr[HEX_REG_PC]; + if (ctx->pkt.pkt_has_multi_cof) { /* If there are multiple branches in a packet, ignore the second one */ - tcg_gen_movcond_tl(TCG_COND_NE, hex_gpr[HEX_REG_PC], + tcg_gen_movcond_tl(TCG_COND_NE, pc_wr, ctx->branch_taken, tcg_constant_tl(0), - hex_gpr[HEX_REG_PC], addr); + pc_wr, addr); tcg_gen_movi_tl(ctx->branch_taken, 1); } else { - tcg_gen_mov_tl(hex_gpr[HEX_REG_PC], addr); + tcg_gen_mov_tl(pc_wr, addr); } if (cond != TCG_COND_ALWAYS) { @@ -890,7 +979,7 @@ static void gen_load_frame(DisasContext *ctx, TCGv_i64 frame, TCGv EA) { Insn *insn = ctx->insn; /* Needed for CHECK_NOSHUF */ CHECK_NOSHUF(EA, 8); - tcg_gen_qemu_ld_i64(frame, EA, ctx->mem_idx, MO_LE | MO_UQ); + tcg_gen_qemu_ld_i64(frame, EA, ctx->mem_idx, MO_LE | MO_UQ | MO_ALIGN); } /* Stack overflow check */ diff --git a/target/hexagon/genptr.h b/target/hexagon/genptr.h index 45ee038ca9..f498398367 100644 --- a/target/hexagon/genptr.h +++ b/target/hexagon/genptr.h @@ -36,6 +36,7 @@ void gen_store8i(TCGv_env cpu_env, TCGv vaddr, int64_t src, uint32_t slot); TCGv gen_read_reg(TCGv result, int num); TCGv gen_read_preg(TCGv pred, uint8_t num); TCGv get_result_gpr(DisasContext *ctx, int rnum); +TCGv gen_unalias_gpr_src(TCGv src, TCGv dst); TCGv get_result_pred(DisasContext *ctx, int pnum); void gen_pred_write(DisasContext *ctx, int pnum, TCGv val); void gen_set_usr_field(DisasContext *ctx, int field, TCGv val); @@ -58,6 +59,10 @@ TCGv gen_get_half(TCGv result, int N, TCGv src, bool sign); void gen_set_half(int N, TCGv result, TCGv src); void gen_set_half_i64(int N, TCGv_i64 result, TCGv src); void probe_noshuf_load(TCGv va, int s, int mi); +void gen_gvec_sabsdiff(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void gen_gvec_uabsdiff(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); extern const target_ulong reg_immut_masks[TOTAL_PER_THREAD_REGS]; diff --git a/target/hexagon/helper.h b/target/hexagon/helper.h index 033e5619d6..78dc28ca9e 100644 --- a/target/hexagon/helper.h +++ b/target/hexagon/helper.h @@ -108,6 +108,11 @@ DEF_HELPER_2(probe_pkt_scalar_store_s0, void, env, int) DEF_HELPER_2(probe_hvx_stores, void, env, int) DEF_HELPER_2(probe_pkt_scalar_hvx_stores, void, env, int) +DEF_HELPER_FLAGS_4(gvec_sabsdiff_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sabsdiff_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_uabsdiff_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_uabsdiff_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + #if !defined(CONFIG_USER_ONLY) DEF_HELPER_3(raise_stack_overflow, void, env, i32, i32) DEF_HELPER_2(swi, void, env, i32) diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py index d91a653c3d..e33d43e3ce 100755 --- a/target/hexagon/hex_common.py +++ b/target/hexagon/hex_common.py @@ -253,6 +253,8 @@ def need_env(tag): "A_LOAD" in attribdict[tag] or "A_CVI_GATHER" in attribdict[tag] or "A_CVI_SCATTER" in attribdict[tag] or + "A_HVX_IEEE_FP" in attribdict[tag] or + "A_HVX_FLT" in attribdict[tag] or "A_IMPLICIT_WRITES_USR" in attribdict[tag] or "A_PRIV" in attribdict[tag] or "J2_trap" in tag) @@ -388,6 +390,8 @@ class Register: """)) def idef_arg(self, declared): declared.append(self.reg_tcg()) + def may_alias_gpr(self): + return False def helper_arg(self): return HelperArg( self.helper_proto_type(), @@ -495,6 +499,8 @@ class ReadWrite: return False class GprDest(Register, Single, Dest): + def may_alias_gpr(self): + return True def decl_tcg(self, f, tag, regno): self.decl_reg_num(f, regno) f.write(code_fmt(f"""\ @@ -510,6 +516,8 @@ class GprDest(Register, Single, Dest): """)) class GprSource(Register, Single, OldSource): + def may_alias_gpr(self): + return True def decl_tcg(self, f, tag, regno): self.decl_reg_num(f, regno) f.write(code_fmt(f"""\ @@ -531,6 +539,8 @@ class GprNewSource(Register, Single, NewSource): """)) class GprReadWrite(Register, Single, ReadWrite): + def may_alias_gpr(self): + return True def decl_tcg(self, f, tag, regno): self.decl_reg_num(f, regno) f.write(code_fmt(f"""\ @@ -557,6 +567,8 @@ class GprReadWrite(Register, Single, ReadWrite): """)) class ControlDest(Register, Single, Dest): + def may_alias_gpr(self): + return True def decl_reg_num(self, f, regno): f.write(code_fmt(f"""\ const int {self.reg_num} = insn->regno[{regno}] + HEX_REG_SA0; @@ -593,6 +605,8 @@ class ControlSource(Register, Single, OldSource): """)) class ModifierSource(Register, Single, OldSource): + def may_alias_gpr(self): + return True def decl_reg_num(self, f, regno): f.write(code_fmt(f"""\ const int {self.reg_num} = insn->regno[{regno}] + HEX_REG_M0; @@ -772,6 +786,11 @@ class VRegDest(Register, Hvx, Dest): TCGv_ptr {self.reg_tcg()} = tcg_temp_new_ptr(); tcg_gen_addi_ptr({self.reg_tcg()}, tcg_env, {self.hvx_off()}); """)) + def gen_zero(self, f): + f.write(code_fmt(f"""\ + tcg_gen_gvec_dup_imm(MO_64, {self.hvx_off()}, + sizeof(MMVector), sizeof(MMVector), 0); + """)) def gen_write(self, f, tag): pass def helper_hvx_desc(self, f): @@ -838,6 +857,11 @@ class VRegReadWrite(Register, Hvx, ReadWrite): TCGv_ptr {self.reg_tcg()} = tcg_temp_new_ptr(); tcg_gen_addi_ptr({self.reg_tcg()}, tcg_env, {self.hvx_off()}); """)) + def gen_zero(self, f): + f.write(code_fmt(f"""\ + tcg_gen_gvec_dup_imm(MO_64, {self.hvx_off()}, + sizeof(MMVector), sizeof(MMVector), 0); + """)) def gen_write(self, f, tag): pass def helper_hvx_desc(self, f): @@ -870,6 +894,11 @@ class VRegTmp(Register, Hvx, ReadWrite): vreg_src_off(ctx, {self.reg_num}), sizeof(MMVector), sizeof(MMVector)); """)) + def gen_zero(self, f): + f.write(code_fmt(f"""\ + tcg_gen_gvec_dup_imm(MO_64, {self.hvx_off()}, + sizeof(MMVector), sizeof(MMVector), 0); + """)) def gen_write(self, f, tag): f.write(code_fmt(f"""\ gen_vreg_write(ctx, {self.hvx_off()}, {self.reg_num}, @@ -903,6 +932,11 @@ class VRegPairDest(Register, Hvx, Dest): TCGv_ptr {self.reg_tcg()} = tcg_temp_new_ptr(); tcg_gen_addi_ptr({self.reg_tcg()}, tcg_env, {self.hvx_off()}); """)) + def gen_zero(self, f): + f.write(code_fmt(f"""\ + tcg_gen_gvec_dup_imm(MO_64, {self.hvx_off()}, + sizeof(MMVectorPair), sizeof(MMVectorPair), 0); + """)) def gen_write(self, f, tag): pass def helper_hvx_desc(self, f): @@ -962,6 +996,11 @@ class VRegPairReadWrite(Register, Hvx, ReadWrite): TCGv_ptr {self.reg_tcg()} = tcg_temp_new_ptr(); tcg_gen_addi_ptr({self.reg_tcg()}, tcg_env, {self.hvx_off()}); """)) + def gen_zero(self, f): + f.write(code_fmt(f"""\ + tcg_gen_gvec_dup_imm(MO_64, {self.hvx_off()}, + sizeof(MMVectorPair), sizeof(MMVectorPair), 0); + """)) def gen_write(self, f, tag): f.write(code_fmt(f"""\ gen_vreg_write_pair(ctx, {self.hvx_off()}, {self.reg_num}, diff --git a/target/hexagon/hex_mmu.h b/target/hexagon/hex_mmu.h index 4f556c715a..6aa450b941 100644 --- a/target/hexagon/hex_mmu.h +++ b/target/hexagon/hex_mmu.h @@ -7,8 +7,9 @@ #ifndef HEXAGON_MMU_H #define HEXAGON_MMU_H +#include "exec/hwaddr.h" +#include "exec/mmu-access-type.h" #include "cpu.h" -#include "monitor/monitor.h" extern void hex_tlbw(CPUHexagonState *env, uint32_t index, uint64_t value); extern uint32_t hex_tlb_lookup(CPUHexagonState *env, uint32_t ssr, uint32_t VA); diff --git a/target/hexagon/idef-parser/parser-helpers.c b/target/hexagon/idef-parser/parser-helpers.c index b942d9ea16..49cc11fddd 100644 --- a/target/hexagon/idef-parser/parser-helpers.c +++ b/target/hexagon/idef-parser/parser-helpers.c @@ -1770,7 +1770,7 @@ void gen_load(Context *c, YYLTYPE *locp, HexValue *width, if (signedness == SIGNED) { OUT(c, locp, " | MO_SIGN"); } - OUT(c, locp, " | MO_LE);\n"); + OUT(c, locp, " | MO_LE | MO_ALIGN);\n"); } void gen_store(Context *c, YYLTYPE *locp, HexValue *width, HexValue *ea, diff --git a/target/hexagon/imported/mmvec/encode_ext.def b/target/hexagon/imported/mmvec/encode_ext.def index 402438f566..16f043b77d 100644 --- a/target/hexagon/imported/mmvec/encode_ext.def +++ b/target/hexagon/imported/mmvec/encode_ext.def @@ -647,36 +647,36 @@ DEF_ENC(V6_vsubububb_sat, ICLASS_CJ" 1 110 101 vvvvv PP 0 uuuuu 101 ddddd") DEF_ENC(V6_vmpyewuh_64, ICLASS_CJ" 1 110 101 vvvvv PP 0 uuuuu 110 ddddd") DEF_FIELDROW_DESC32( ICLASS_CJ" 1 110 --0 ----- PP 1 ----- ----- ---","Vx32=Vu32") -DEF_ENC(V6_vunpackob, ICLASS_CJ" 1 110 --0 ---00 PP 1 uuuuu 000 xxxxx") // -DEF_ENC(V6_vunpackoh, ICLASS_CJ" 1 110 --0 ---00 PP 1 uuuuu 001 xxxxx") // +DEF_ENC(V6_vunpackob, ICLASS_CJ" 1 110 --0 --000 PP 1 uuuuu 000 xxxxx") // +DEF_ENC(V6_vunpackoh, ICLASS_CJ" 1 110 --0 --000 PP 1 uuuuu 001 xxxxx") // //DEF_ENC(V6_vunpackow, ICLASS_CJ" 1 110 --0 ---00 PP 1 uuuuu 010 xxxxx") // -DEF_ENC(V6_vhist, ICLASS_CJ" 1 110 --0 ---00 PP 1 -000- 100 -----") -DEF_ENC(V6_vwhist256, ICLASS_CJ" 1 110 --0 ---00 PP 1 -0010 100 -----") -DEF_ENC(V6_vwhist256_sat, ICLASS_CJ" 1 110 --0 ---00 PP 1 -0011 100 -----") -DEF_ENC(V6_vwhist128, ICLASS_CJ" 1 110 --0 ---00 PP 1 -010- 100 -----") -DEF_ENC(V6_vwhist128m, ICLASS_CJ" 1 110 --0 ---00 PP 1 -011i 100 -----") +DEF_ENC(V6_vhist, ICLASS_CJ" 1 110 --0 --000 PP 1 -000- 100 -----") +DEF_ENC(V6_vwhist256, ICLASS_CJ" 1 110 --0 --000 PP 1 -0010 100 -----") +DEF_ENC(V6_vwhist256_sat, ICLASS_CJ" 1 110 --0 --000 PP 1 -0011 100 -----") +DEF_ENC(V6_vwhist128, ICLASS_CJ" 1 110 --0 --000 PP 1 -010- 100 -----") +DEF_ENC(V6_vwhist128m, ICLASS_CJ" 1 110 --0 --000 PP 1 -011i 100 -----") DEF_FIELDROW_DESC32( ICLASS_CJ" 1 110 --0 ----- PP 1 ----- ----- ---","if (Qv4) Vx32=Vu32") -DEF_ENC(V6_vaddbq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 000 xxxxx") // -DEF_ENC(V6_vaddhq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 001 xxxxx") // -DEF_ENC(V6_vaddwq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 010 xxxxx") // -DEF_ENC(V6_vaddbnq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 011 xxxxx") // -DEF_ENC(V6_vaddhnq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 100 xxxxx") // -DEF_ENC(V6_vaddwnq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 101 xxxxx") // -DEF_ENC(V6_vsubbq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 110 xxxxx") // -DEF_ENC(V6_vsubhq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 111 xxxxx") // +DEF_ENC(V6_vaddbq, ICLASS_CJ" 1 110 vv0 --001 PP 1 uuuuu 000 xxxxx") // +DEF_ENC(V6_vaddhq, ICLASS_CJ" 1 110 vv0 --001 PP 1 uuuuu 001 xxxxx") // +DEF_ENC(V6_vaddwq, ICLASS_CJ" 1 110 vv0 --001 PP 1 uuuuu 010 xxxxx") // +DEF_ENC(V6_vaddbnq, ICLASS_CJ" 1 110 vv0 --001 PP 1 uuuuu 011 xxxxx") // +DEF_ENC(V6_vaddhnq, ICLASS_CJ" 1 110 vv0 --001 PP 1 uuuuu 100 xxxxx") // +DEF_ENC(V6_vaddwnq, ICLASS_CJ" 1 110 vv0 --001 PP 1 uuuuu 101 xxxxx") // +DEF_ENC(V6_vsubbq, ICLASS_CJ" 1 110 vv0 --001 PP 1 uuuuu 110 xxxxx") // +DEF_ENC(V6_vsubhq, ICLASS_CJ" 1 110 vv0 --001 PP 1 uuuuu 111 xxxxx") // -DEF_ENC(V6_vsubwq, ICLASS_CJ" 1 110 vv0 ---10 PP 1 uuuuu 000 xxxxx") // -DEF_ENC(V6_vsubbnq, ICLASS_CJ" 1 110 vv0 ---10 PP 1 uuuuu 001 xxxxx") // -DEF_ENC(V6_vsubhnq, ICLASS_CJ" 1 110 vv0 ---10 PP 1 uuuuu 010 xxxxx") // -DEF_ENC(V6_vsubwnq, ICLASS_CJ" 1 110 vv0 ---10 PP 1 uuuuu 011 xxxxx") // +DEF_ENC(V6_vsubwq, ICLASS_CJ" 1 110 vv0 --010 PP 1 uuuuu 000 xxxxx") // +DEF_ENC(V6_vsubbnq, ICLASS_CJ" 1 110 vv0 --010 PP 1 uuuuu 001 xxxxx") // +DEF_ENC(V6_vsubhnq, ICLASS_CJ" 1 110 vv0 --010 PP 1 uuuuu 010 xxxxx") // +DEF_ENC(V6_vsubwnq, ICLASS_CJ" 1 110 vv0 --010 PP 1 uuuuu 011 xxxxx") // -DEF_ENC(V6_vhistq, ICLASS_CJ" 1 110 vv0 ---10 PP 1 --00- 100 -----") -DEF_ENC(V6_vwhist256q, ICLASS_CJ" 1 110 vv0 ---10 PP 1 --010 100 -----") -DEF_ENC(V6_vwhist256q_sat, ICLASS_CJ" 1 110 vv0 ---10 PP 1 --011 100 -----") -DEF_ENC(V6_vwhist128q, ICLASS_CJ" 1 110 vv0 ---10 PP 1 --10- 100 -----") -DEF_ENC(V6_vwhist128qm, ICLASS_CJ" 1 110 vv0 ---10 PP 1 --11i 100 -----") +DEF_ENC(V6_vhistq, ICLASS_CJ" 1 110 vv0 --010 PP 1 --00- 100 -----") +DEF_ENC(V6_vwhist256q, ICLASS_CJ" 1 110 vv0 --010 PP 1 --010 100 -----") +DEF_ENC(V6_vwhist256q_sat, ICLASS_CJ" 1 110 vv0 --010 PP 1 --011 100 -----") +DEF_ENC(V6_vwhist128q, ICLASS_CJ" 1 110 vv0 --010 PP 1 --10- 100 -----") +DEF_ENC(V6_vwhist128qm, ICLASS_CJ" 1 110 vv0 --010 PP 1 --11i 100 -----") DEF_ENC(V6_vandvqv, ICLASS_CJ" 1 110 vv0 ---11 PP 1 uuuuu 000 ddddd") @@ -804,5 +804,83 @@ DEF_ENC(V6_vmpyewuh, ICLASS_CJ" 1 111 111 vvvvv PP 0 uuuuu 101 ddddd") DEF_ENC(V6_vmpyowh, ICLASS_CJ" 1 111 111 vvvvv PP 0 uuuuu 111 ddddd") DEF_ENC(V6_vmpyuhvs,"00011111110vvvvvPP1uuuuu111ddddd") +/* IEEE FP multiply instructions */ +DEF_ENC(V6_vmpy_sf_sf,"00011111100vvvvvPP1uuuuu001ddddd") +DEF_ENC(V6_vmpy_sf_hf,"00011111100vvvvvPP1uuuuu010ddddd") +DEF_ENC(V6_vmpy_hf_hf,"00011111100vvvvvPP1uuuuu011ddddd") +DEF_ENC(V6_vdmpy_sf_hf,"00011111101vvvvvPP1uuuuu110ddddd") + +/* IEEE FP multiply-accumulate instructions */ +DEF_ENC(V6_vmpy_sf_hf_acc,"00011100010vvvvvPP1uuuuu001xxxxx") +DEF_ENC(V6_vmpy_hf_hf_acc,"00011100010vvvvvPP1uuuuu010xxxxx") +DEF_ENC(V6_vdmpy_sf_hf_acc,"00011100010vvvvvPP1uuuuu011xxxxx") + +/* IEEE FP add/sub instructions */ +DEF_ENC(V6_vadd_sf_sf,"00011111100vvvvvPP1uuuuu110ddddd") +DEF_ENC(V6_vsub_sf_sf,"00011111100vvvvvPP1uuuuu111ddddd") +DEF_ENC(V6_vadd_sf_hf,"00011111100vvvvvPP1uuuuu100ddddd") +DEF_ENC(V6_vsub_sf_hf,"00011111100vvvvvPP1uuuuu101ddddd") +DEF_ENC(V6_vadd_hf_hf,"00011111101vvvvvPP1uuuuu111ddddd") +DEF_ENC(V6_vsub_hf_hf,"00011111011vvvvvPP1uuuuu000ddddd") + +/* IEEE FP min/max instructions */ +DEF_ENC(V6_vfmin_hf,"00011100011vvvvvPP1uuuuu000ddddd") +DEF_ENC(V6_vfmin_sf,"00011100011vvvvvPP1uuuuu001ddddd") +DEF_ENC(V6_vfmax_hf,"00011100011vvvvvPP1uuuuu010ddddd") +DEF_ENC(V6_vfmax_sf,"00011100011vvvvvPP1uuuuu011ddddd") +DEF_ENC(V6_vmax_sf,"00011111110vvvvvPP1uuuuu001ddddd") +DEF_ENC(V6_vmin_sf,"00011111110vvvvvPP1uuuuu010ddddd") +DEF_ENC(V6_vmax_hf,"00011111110vvvvvPP1uuuuu011ddddd") +DEF_ENC(V6_vmin_hf,"00011111110vvvvvPP1uuuuu100ddddd") + +/* IEEE FP move, negate, abs instructions */ +DEF_ENC(V6_vassign_fp,"00011110--0-0110PP1uuuuu001ddddd") +DEF_ENC(V6_vfneg_hf,"00011110--0-0110PP1uuuuu010ddddd") +DEF_ENC(V6_vfneg_sf,"00011110--0-0110PP1uuuuu011ddddd") +DEF_ENC(V6_vabs_hf,"00011110--0-0110PP1uuuuu100ddddd") +DEF_ENC(V6_vabs_sf,"00011110--0-0110PP1uuuuu101ddddd") + +/* IEEE FP vcvt instructions */ +DEF_ENC(V6_vcvt_sf_hf,"00011110--0-0100PP1uuuuu100ddddd") +DEF_ENC(V6_vcvt_hf_sf,"00011111011vvvvvPP1uuuuu001ddddd") +DEF_ENC(V6_vcvt_hf_ub,"00011110--0-0100PP1uuuuu001ddddd") +DEF_ENC(V6_vcvt_hf_b,"00011110--0-0100PP1uuuuu010ddddd") +DEF_ENC(V6_vcvt_hf_uh,"00011110--0-0100PP1uuuuu101ddddd") +DEF_ENC(V6_vcvt_hf_h,"00011110--0-0100PP1uuuuu111ddddd") +DEF_ENC(V6_vcvt_uh_hf,"00011110--0--101PP1uuuuu000ddddd") +DEF_ENC(V6_vcvt_h_hf,"00011110--0-0110PP1uuuuu000ddddd") +DEF_ENC(V6_vcvt_ub_hf,"00011111110vvvvvPP1uuuuu101ddddd") +DEF_ENC(V6_vcvt_b_hf,"00011111110vvvvvPP1uuuuu110ddddd") + +/* IEEE FP vconv instructions */ +DEF_ENC(V6_vconv_sf_w,"00011110--0--101PP1uuuuu011ddddd") +DEF_ENC(V6_vconv_w_sf,"00011110--0--101PP1uuuuu001ddddd") +DEF_ENC(V6_vconv_hf_h,"00011110--0--101PP1uuuuu100ddddd") +DEF_ENC(V6_vconv_h_hf,"00011110--0--101PP1uuuuu010ddddd") + +/* IEEE FP compare instructions */ +DEF_ENC(V6_vgtsf,"00011100100vvvvvPP1uuuuu011100dd") +DEF_ENC(V6_vgthf,"00011100100vvvvvPP1uuuuu011101dd") +DEF_ENC(V6_vgtsf_and,"00011100100vvvvvPP1uuuuu110010xx") +DEF_ENC(V6_vgthf_and,"00011100100vvvvvPP1uuuuu110011xx") +DEF_ENC(V6_vgtsf_or,"00011100100vvvvvPP1uuuuu001100xx") +DEF_ENC(V6_vgthf_or,"00011100100vvvvvPP1uuuuu001101xx") +DEF_ENC(V6_vgtsf_xor,"00011100100vvvvvPP1uuuuu111010xx") +DEF_ENC(V6_vgthf_xor,"00011100100vvvvvPP1uuuuu111011xx") + +/* BFLOAT instructions */ +DEF_ENC(V6_vmpy_sf_bf,"00011101010vvvvvPP1uuuuu100ddddd") +DEF_ENC(V6_vmpy_sf_bf_acc,"00011101000vvvvvPP1uuuuu000xxxxx") +DEF_ENC(V6_vadd_sf_bf,"00011101010vvvvvPP1uuuuu110ddddd") +DEF_ENC(V6_vsub_sf_bf,"00011101010vvvvvPP1uuuuu101ddddd") +DEF_ENC(V6_vmax_bf,"00011101010vvvvvPP1uuuuu111ddddd") +DEF_ENC(V6_vmin_bf,"00011101010vvvvvPP1uuuuu000ddddd") +DEF_ENC(V6_vcvt_bf_sf,"00011101010vvvvvPP1uuuuu011ddddd") + +/* BFLOAT compare instructions */ +DEF_ENC(V6_vgtbf,"00011100100vvvvvPP1uuuuu011110dd") +DEF_ENC(V6_vgtbf_and,"00011100100vvvvvPP1uuuuu110100xx") +DEF_ENC(V6_vgtbf_or,"00011100100vvvvvPP1uuuuu001110xx") +DEF_ENC(V6_vgtbf_xor,"00011100100vvvvvPP1uuuuu111100xx") #endif /* NO MMVEC */ diff --git a/target/hexagon/imported/mmvec/ext.idef b/target/hexagon/imported/mmvec/ext.idef index 03d31f6181..857aa6133f 100644 --- a/target/hexagon/imported/mmvec/ext.idef +++ b/target/hexagon/imported/mmvec/ext.idef @@ -43,7 +43,9 @@ EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA), \ DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) - +#define ITERATOR_INSN_ANY_SLOT_2SRC(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) #define ITERATOR_INSN2_ANY_SLOT(WIDTH,TAG,SYNTAX,SYNTAX2,DESCR,CODE) \ ITERATOR_INSN_ANY_SLOT(WIDTH,TAG,SYNTAX2,DESCR,CODE) @@ -61,6 +63,9 @@ ITERATOR_INSN_ANY_SLOT_DOUBLE_VEC(WIDTH,TAG,SYNTAX2,DESCR,CODE) EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VS), \ DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) +#define ITERATOR_INSN_SHIFT_SLOT_FLT(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VS,A_HVX_FLT), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) #define ITERATOR_INSN_SHIFT3_SLOT(WIDTH,TAG,SYNTAX,DESCR,CODE) \ EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VS,A_CVI_VS_3SRC,A_NOTE_SHIFT_RESOURCE,A_NOTE_NOVP,A_NOTE_VA_UNARY), \ @@ -2895,9 +2900,371 @@ EXTINSN(V6_vprefixqw,"Vd32.w=prefixsum(Qv4)", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_ } } ) +/* KVX - IEEE FP Instructions */ +/* Single pipe, 32-bit output */ +#define ITERATOR_INSN_IEEE_FP_32(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ +ATTRIBS(A_EXTENSION,A_HVX_IEEE_FP,A_CVI,A_CVI_VX,A_HVX_IEEE_FP_OUT_32), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) +/* Single pipe, 16-bit output */ +#define ITERATOR_INSN_IEEE_FP_16(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ +ATTRIBS(A_EXTENSION,A_HVX_IEEE_FP,A_CVI,A_CVI_VX,A_HVX_IEEE_FP_OUT_16), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) +/* Two pipes: P2 & P3, single output: P2, 32-bit output */ +#define ITERATOR_INSN_IEEE_FP_DOUBLE_SINGLE_32(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ +ATTRIBS(A_EXTENSION,A_HVX_IEEE_FP,A_CVI,A_CVI_VX_DV,A_HVX_IEEE_FP_OUT_32), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +/* Two pipes: P2 & P3, two outputs, 32-bit output */ +#define ITERATOR_INSN_IEEE_FP_DOUBLE_32(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ +ATTRIBS(A_EXTENSION,A_HVX_IEEE_FP,A_CVI,A_CVI_VX_DV,A_HVX_IEEE_FP_OUT_32), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +/* + * single pipe, accumulate instruction, produces 16-bit output, requires 16-bit + * accumulate input + */ +#define ITERATOR_INSN_IEEE_FP_ACC_16(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ +ATTRIBS(A_EXTENSION,A_HVX_IEEE_FP,A_CVI,A_CVI_VX,A_HVX_IEEE_FP_ACC,A_HVX_IEEE_FP_OUT_16,A_CVI_VX_NO_TMP_LD), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +/* + * single pipe, accumulate instruction, produces 32-bit output, requires 32-bit + * accumulate input + */ +#define ITERATOR_INSN_IEEE_FP_ACC_32(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ +ATTRIBS(A_EXTENSION,A_HVX_IEEE_FP,A_CVI,A_CVI_VX,A_HVX_IEEE_FP_ACC,A_HVX_IEEE_FP_OUT_32,A_CVI_VX_NO_TMP_LD), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +/* IEEE FP multiply instructions */ +ITERATOR_INSN_IEEE_FP_DOUBLE_SINGLE_32(32, vmpy_sf_sf, + "Vd32.sf=vmpy(Vu32.sf,Vv32.sf)", "Vector IEEE mul: sf", + VdV.sf[i] = float32_mul(VuV.sf[i], VvV.sf[i], &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_DOUBLE_32(32, vmpy_sf_hf, + "Vdd32.sf=vmpy(Vu32.hf,Vv32.hf)", "Vector IEEE mul: hf widen to sf", + VddV.v[0].sf[i] = fp_mult_sf_hf(VuV.hf[2*i], VvV.hf[2*i], &env->hvx_fp_status); + VddV.v[1].sf[i] = fp_mult_sf_hf(VuV.hf[2*i+1], VvV.hf[2*i+1], &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_16(16, vmpy_hf_hf, "Vd32.hf=vmpy(Vu32.hf,Vv32.hf)", + "Vector IEEE mul: hf", + VdV.hf[i] = float16_mul(VuV.hf[i], VvV.hf[i], &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_32(32, vdmpy_sf_hf, "Vd32.sf=vdmpy(Vu32.hf,Vv32.hf)", + "Vector IEEE mul reduction: hf widen to sf", + VdV.sf[i] = fp_vdmpy(VuV.hf[2*i+1], VuV.hf[2*i], VvV.hf[2*i+1], + VvV.hf[2*i], &env->hvx_fp_status)) + +/* IEEE FP multiply-accumulate instructions */ +ITERATOR_INSN_IEEE_FP_DOUBLE_32(32, vmpy_sf_hf_acc, + "Vxx32.sf+=vmpy(Vu32.hf,Vv32.hf)", "Vector IEEE fma: hf widen to sf", + VxxV.v[0].sf[i] = float32_muladd(f16_to_f32(VuV.hf[2*i]), + f16_to_f32(VvV.hf[2*i]), + VxxV.v[0].sf[i], 0, &env->hvx_fp_status); + VxxV.v[1].sf[i] = float32_muladd(f16_to_f32(VuV.hf[2*i+1]), + f16_to_f32(VvV.hf[2*i+1]), + VxxV.v[1].sf[i], 0, &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_ACC_16(16, vmpy_hf_hf_acc, + "Vx32.hf+=vmpy(Vu32.hf,Vv32.hf)", "Vector IEEE fma: hf", + VxV.hf[i] = float16_muladd(VuV.hf[i], VvV.hf[i], VxV.hf[i], 0, &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_ACC_32(32, vdmpy_sf_hf_acc, + "Vx32.sf+=vdmpy(Vu32.hf,Vv32.hf)", "Vector IEEE fma reduce: hf widen to sf", + VxV.sf[i] = float32_add(fp_vdmpy(VuV.hf[2*i+1], VuV.hf[2*i], + VvV.hf[2*i+1], VvV.hf[2*i], + &env->hvx_fp_status), + VxV.sf[i], &env->hvx_fp_status)) + +/* IEEE FP add/sub instructions */ +ITERATOR_INSN_IEEE_FP_32(32, vadd_sf_sf, "Vd32.sf=vadd(Vu32.sf,Vv32.sf)", + "Vector IEEE add: sf", + VdV.sf[i] = float32_add(VuV.sf[i], VvV.sf[i], &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_32(32, vsub_sf_sf, "Vd32.sf=vsub(Vu32.sf,Vv32.sf)", + "Vector IEEE sub: sf", + VdV.sf[i] = float32_sub(VuV.sf[i], VvV.sf[i], &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_16(16, vadd_hf_hf, "Vd32.hf=vadd(Vu32.hf,Vv32.hf)", + "Vector IEEE add: hf", + VdV.hf[i] = float16_add(VuV.hf[i], VvV.hf[i], &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_16(16, vsub_hf_hf, "Vd32.hf=vsub(Vu32.hf,Vv32.hf)", + "Vector IEEE sub: hf", + VdV.hf[i] = float16_sub(VuV.hf[i], VvV.hf[i], &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_DOUBLE_32(32, vadd_sf_hf, + "Vdd32.sf=vadd(Vu32.hf,Vv32.hf)", "Vector IEEE add: hf widen to sf", + VddV.v[0].sf[i] = float32_add(f16_to_f32(VuV.hf[2*i]), + f16_to_f32(VvV.hf[2*i]), &env->hvx_fp_status); + VddV.v[1].sf[i] = float32_add(f16_to_f32(VuV.hf[2*i+1]), + f16_to_f32(VvV.hf[2*i+1]), &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_DOUBLE_32(32, vsub_sf_hf, + "Vdd32.sf=vsub(Vu32.hf,Vv32.hf)", "Vector IEEE sub: hf widen to sf", + VddV.v[0].sf[i] = float32_sub(f16_to_f32(VuV.hf[2*i]), + f16_to_f32(VvV.hf[2*i]), &env->hvx_fp_status); + VddV.v[1].sf[i] = float32_sub(f16_to_f32(VuV.hf[2*i+1]), + f16_to_f32(VvV.hf[2*i+1]), &env->hvx_fp_status)) + +#define ITERATOR_INSN_IEEE_FP_16_32_LATE(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ + ATTRIBS(A_EXTENSION,A_HVX_IEEE_FP,A_CVI,A_CVI_VX,A_HVX_IEEE_FP_OUT_16,A_HVX_IEEE_FP_OUT_32), \ + DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +/* IEEE FP min/max instructions */ +ITERATOR_INSN_IEEE_FP_16_32_LATE(16, vfmin_hf, "Vd32.hf=vfmin(Vu32.hf,Vv32.hf)", \ + "Vector IEEE min: hf", VdV.hf[i] = float16_min(VuV.hf[i], VvV.hf[i], \ + &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_16_32_LATE(32, vfmin_sf, "Vd32.sf=vfmin(Vu32.sf,Vv32.sf)", \ + "Vector IEEE min: sf", VdV.sf[i] = float32_min(VuV.sf[i], VvV.sf[i], \ + &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_16_32_LATE(16, vfmax_hf, "Vd32.hf=vfmax(Vu32.hf,Vv32.hf)", \ + "Vector IEEE max: hf", VdV.hf[i] = float16_max(VuV.hf[i], VvV.hf[i], \ + &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_16_32_LATE(32, vfmax_sf, "Vd32.sf=vfmax(Vu32.sf,Vv32.sf)", \ + "Vector IEEE max: sf", VdV.sf[i] = float32_max(VuV.sf[i], VvV.sf[i], \ + &env->hvx_fp_status)) + +ITERATOR_INSN_ANY_SLOT_2SRC(32,vmax_sf,"Vd32.sf=vmax(Vu32.sf,Vv32.sf)", \ + "Vector max of sf input", VdV.sf[i] = qf_max_sf(VuV.sf[i], VvV.sf[i], \ + &env->hvx_fp_status)) +ITERATOR_INSN_ANY_SLOT_2SRC(32,vmin_sf,"Vd32.sf=vmin(Vu32.sf,Vv32.sf)", \ + "Vector min of sf input", VdV.sf[i] = qf_min_sf(VuV.sf[i], VvV.sf[i], \ + &env->hvx_fp_status)) +ITERATOR_INSN_ANY_SLOT_2SRC(16,vmax_hf,"Vd32.hf=vmax(Vu32.hf,Vv32.hf)", \ + "Vector max of hf input", VdV.hf[i] = qf_max_hf(VuV.hf[i], VvV.hf[i], \ + &env->hvx_fp_status)) +ITERATOR_INSN_ANY_SLOT_2SRC(16,vmin_hf,"Vd32.hf=vmin(Vu32.hf,Vv32.hf)", \ + "Vector min of hf input", VdV.hf[i] = qf_min_hf(VuV.hf[i], VvV.hf[i], \ + &env->hvx_fp_status)) + +/* IEEE FP move, negate, abs instructions */ +ITERATOR_INSN_IEEE_FP_16_32_LATE(32, vassign_fp, "Vd32.w=vfmv(Vu32.w)", \ + "Vector IEEE move", VdV.w[i] = VuV.w[i]) +ITERATOR_INSN_IEEE_FP_16_32_LATE(16, vfneg_hf, "Vd32.hf=vfneg(Vu32.hf)", \ + "Vector IEEE neg: hf", VdV.hf[i] = float16_chs(VuV.hf[i])) +ITERATOR_INSN_IEEE_FP_16_32_LATE(32, vfneg_sf, "Vd32.sf=vfneg(Vu32.sf)", \ + "Vector IEEE neg: sf", VdV.sf[i] = float32_chs(VuV.sf[i])) +ITERATOR_INSN_IEEE_FP_16_32_LATE(16, vabs_hf, "Vd32.hf=vabs(Vu32.hf)", \ + "Vector IEEE abs: hf", VdV.hf[i] = float16_abs(VuV.hf[i])) +ITERATOR_INSN_IEEE_FP_16_32_LATE(32, vabs_sf, "Vd32.sf=vabs(Vu32.sf)", \ + "Vector IEEE abs: sf", VdV.sf[i] = float32_abs(VuV.sf[i])) + +/* Two pipes: P2 & P3, two outputs, 16-bit */ +#define ITERATOR_INSN_IEEE_FP_DOUBLE_16(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ +ATTRIBS(A_EXTENSION,A_HVX_IEEE_FP,A_CVI,A_CVI_VX_DV,A_HVX_IEEE_FP_OUT_16), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +/* Two pipes: P2 & P3, two outputs, 32-bit output */ +#define ITERATOR_INSN_IEEE_FP_DOUBLE_32(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ + ATTRIBS(A_EXTENSION,A_HVX_IEEE_FP,A_CVI,A_CVI_VX_DV,A_HVX_IEEE_FP_OUT_32), \ + DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +/* Single pipe, 16-bit output */ +#define ITERATOR_INSN_IEEE_FP_16(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ + ATTRIBS(A_EXTENSION,A_HVX_IEEE_FP,A_CVI,A_CVI_VX,A_HVX_IEEE_FP_OUT_16), \ + DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +/* single pipe, output can feed 16- or 32-bit accumulate */ +#define ITERATOR_INSN_IEEE_FP_16_32(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ + ATTRIBS(A_EXTENSION,A_HVX_IEEE_FP,A_CVI,A_CVI_VX,A_HVX_IEEE_FP_OUT_16,A_HVX_IEEE_FP_OUT_32), \ + DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +/****************************************************************************** + * IEEE FP convert instructions + ******************************************************************************/ + +ITERATOR_INSN_IEEE_FP_DOUBLE_16(32, vcvt_hf_ub, "Vdd32.hf=vcvt(Vu32.ub)", + "Vector IEEE cvt from int: ub widen to hf", + VddV.v[0].hf[2*i] = uint64_to_float16_scalbn(VuV.ub[4*i], float_round_nearest_even, 0); + VddV.v[0].hf[2*i+1] = uint64_to_float16_scalbn(VuV.ub[4*i+1], float_round_nearest_even, 0); + VddV.v[1].hf[2*i] = uint64_to_float16_scalbn(VuV.ub[4*i+2], float_round_nearest_even, 0); + VddV.v[1].hf[2*i+1] = uint64_to_float16_scalbn(VuV.ub[4*i+3], float_round_nearest_even, 0)) + +ITERATOR_INSN_IEEE_FP_DOUBLE_16(32, vcvt_hf_b, "Vdd32.hf=vcvt(Vu32.b)", + "Vector IEEE cvt from int: b widen to hf", + VddV.v[0].hf[2*i] = int64_to_float16_scalbn(VuV.b[4*i], float_round_nearest_even, 0); + VddV.v[0].hf[2*i+1] = int64_to_float16_scalbn(VuV.b[4*i+1], float_round_nearest_even, 0); + VddV.v[1].hf[2*i] = int64_to_float16_scalbn(VuV.b[4*i+2], float_round_nearest_even, 0); + VddV.v[1].hf[2*i+1] = int64_to_float16_scalbn(VuV.b[4*i+3], float_round_nearest_even, 0)) + +ITERATOR_INSN_IEEE_FP_DOUBLE_32(32, vcvt_sf_hf, "Vdd32.sf=vcvt(Vu32.hf)", + "Vector IEEE cvt: hf widen to sf", + VddV.v[0].sf[i] = f16_to_f32(VuV.hf[2*i]); + VddV.v[1].sf[i] = f16_to_f32(VuV.hf[2*i+1])) + +ITERATOR_INSN_IEEE_FP_16(16, vcvt_hf_uh, "Vd32.hf=vcvt(Vu32.uh)", + "Vector IEEE cvt from int: uh to hf", + VdV.hf[i] = uint64_to_float16_scalbn(VuV.uh[i], float_round_nearest_even, 0)) +ITERATOR_INSN_IEEE_FP_16(16, vcvt_hf_h, "Vd32.hf=vcvt(Vu32.h)", + "Vector IEEE cvt from int: h to hf", + VdV.hf[i] = int64_to_float16_scalbn(VuV.h[i], float_round_nearest_even, 0)) +ITERATOR_INSN_IEEE_FP_16_32(16, vcvt_uh_hf, "Vd32.uh=vcvt(Vu32.hf)", + "Vector IEEE cvt to int: hf to uh", + VdV.uh[i] = float16_to_uint16_scalbn(VuV.hf[i], float_round_nearest_even, 0, &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_16_32(16, vcvt_h_hf, "Vd32.h=vcvt(Vu32.hf)", + "Vector IEEE cvt to int: hf to h", + VdV.h[i] = float16_to_int16_scalbn(VuV.hf[i], float_round_nearest_even, 0, &env->hvx_fp_status)) + +ITERATOR_INSN_IEEE_FP_16(32, vcvt_hf_sf, "Vd32.hf=vcvt(Vu32.sf,Vv32.sf)", + "Vector IEEE cvt: sf to hf", + VdV.hf[2*i] = f32_to_f16(VuV.sf[i]); + VdV.hf[2*i+1] = f32_to_f16(VvV.sf[i])) + +ITERATOR_INSN_IEEE_FP_16_32(32, vcvt_ub_hf, "Vd32.ub=vcvt(Vu32.hf,Vv32.hf)", "Vector cvt to int: hf narrow to ub", + VdV.ub[4*i] = float16_to_uint8_scalbn(VuV.hf[2*i], float_round_nearest_even, 0, &env->hvx_fp_status); + VdV.ub[4*i+1] = float16_to_uint8_scalbn(VuV.hf[2*i+1], float_round_nearest_even, 0, &env->hvx_fp_status); + VdV.ub[4*i+2] = float16_to_uint8_scalbn(VvV.hf[2*i], float_round_nearest_even, 0, &env->hvx_fp_status); + VdV.ub[4*i+3] = float16_to_uint8_scalbn(VvV.hf[2*i+1], float_round_nearest_even, 0, &env->hvx_fp_status)) + +ITERATOR_INSN_IEEE_FP_16_32(32, vcvt_b_hf, "Vd32.b=vcvt(Vu32.hf,Vv32.hf)", + "Vector cvt to int: hf narrow to b", + VdV.b[4*i] = float16_to_int8_scalbn(VuV.hf[2*i], float_round_nearest_even, 0, &env->hvx_fp_status); + VdV.b[4*i+1] = float16_to_int8_scalbn(VuV.hf[2*i+1], float_round_nearest_even, 0, &env->hvx_fp_status); + VdV.b[4*i+2] = float16_to_int8_scalbn(VvV.hf[2*i], float_round_nearest_even, 0, &env->hvx_fp_status); + VdV.b[4*i+3] = float16_to_int8_scalbn(VvV.hf[2*i+1], float_round_nearest_even, 0, &env->hvx_fp_status)) + +ITERATOR_INSN_SHIFT_SLOT_FLT(32, vconv_w_sf,"Vd32.w=Vu32.sf", + "Vector conversion of sf32 format to int w", + VdV.w[i] = conv_w_sf(VuV.sf[i], &env->hvx_fp_status)) + +ITERATOR_INSN_SHIFT_SLOT_FLT(16, vconv_h_hf,"Vd32.h=Vu32.hf", + "Vector conversion of hf16 format to int hw", + VdV.h[i] = conv_h_hf(VuV.hf[i], &env->hvx_fp_status)) + +ITERATOR_INSN_SHIFT_SLOT_FLT(32, vconv_sf_w,"Vd32.sf=Vu32.w", + "Vector conversion of int w format to sf32", + VdV.sf[i] = int32_to_float32(VuV.w[i], &env->hvx_fp_status)) + +ITERATOR_INSN_SHIFT_SLOT_FLT(16, vconv_hf_h,"Vd32.hf=Vu32.h", + "Vector conversion of int hw format to hf16", + VdV.hf[i] = float16_val(int16_to_float16(VuV.h[i], &env->hvx_fp_status))) + +/****************************************************************************** + * IEEE FP compare instructions + ******************************************************************************/ + +#define VCMPGT_SF(DEST, ASRC, ASRCOP, CMP, N, SRC, MASK, WIDTH) \ +{ \ + for (fHIDE(int) i = 0; i < fVBYTES(); i += WIDTH) { \ + fHIDE(int) VAL = fCMPGT_SF(VuV.SRC[i/WIDTH],VvV.SRC[i/WIDTH]) ? MASK : 0; \ + fSETQBITS(DEST,WIDTH,MASK,i,ASRC ASRCOP VAL); \ + } \ +} + +#define VCMPGT_HF(DEST, ASRC, ASRCOP, CMP, N, SRC, MASK, WIDTH) \ +{ \ + for (fHIDE(int) i = 0; i < fVBYTES(); i += WIDTH) { \ + fHIDE(int) VAL = fCMPGT_HF(VuV.SRC[i/WIDTH],VvV.SRC[i/WIDTH]) ? MASK : 0; \ + fSETQBITS(DEST,WIDTH,MASK,i,ASRC ASRCOP VAL); \ + } \ +} + +#define VCMPGT_BF(DEST, ASRC, ASRCOP, CMP, N, SRC, MASK, WIDTH) \ +{ \ + fBFLOAT(); \ + for (fHIDE(int) i = 0; i < fVBYTES(); i += WIDTH) { \ + fHIDE(int) VAL = fCMPGT_BF(VuV.SRC[i/WIDTH],VvV.SRC[i/WIDTH]) ? MASK : 0; \ + fSETQBITS(DEST,WIDTH,MASK,i,ASRC ASRCOP VAL); \ + } \ +} + +/* Vector SF compare */ +#define MMVEC_CMPGT_SF(TYPE,TYPE2,DESCR,N,MASK,WIDTH,SRC) \ + EXTINSN(V6_vgt##TYPE##_and, "Qx4&=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", \ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than with predicate-and", \ + VCMPGT_SF(QxV, fGETQBITS(QxV,WIDTH,MASK,i), &, ">", N, SRC, MASK, WIDTH)) \ + EXTINSN(V6_vgt##TYPE##_xor, "Qx4^=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", \ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than with predicate-xor", \ + VCMPGT_SF(QxV, fGETQBITS(QxV,WIDTH,MASK,i), ^, ">", N, SRC, MASK, WIDTH)) \ + EXTINSN(V6_vgt##TYPE##_or, "Qx4|=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", \ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than with predicate-or", \ + VCMPGT_SF(QxV, fGETQBITS(QxV,WIDTH,MASK,i), |, ">", N, SRC, MASK, WIDTH)) \ + EXTINSN(V6_vgt##TYPE, "Qd4=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", \ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than", \ + VCMPGT_SF(QdV, , , ">", N, SRC, MASK, WIDTH)) + +/* Vector HF compare */ +#define MMVEC_CMPGT_HF(TYPE,TYPE2,DESCR,N,MASK,WIDTH,SRC) \ + EXTINSN(V6_vgt##TYPE##_and, "Qx4&=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", \ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than with predicate-and", \ + VCMPGT_HF(QxV, fGETQBITS(QxV,WIDTH,MASK,i), &, ">", N, SRC, MASK, WIDTH)) \ + EXTINSN(V6_vgt##TYPE##_xor, "Qx4^=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", \ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than with predicate-xor", \ + VCMPGT_HF(QxV, fGETQBITS(QxV,WIDTH,MASK,i), ^, ">", N, SRC, MASK, WIDTH)) \ + EXTINSN(V6_vgt##TYPE##_or, "Qx4|=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", \ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than with predicate-or", \ + VCMPGT_HF(QxV, fGETQBITS(QxV,WIDTH,MASK,i), |, ">", N, SRC, MASK, WIDTH)) \ + EXTINSN(V6_vgt##TYPE, "Qd4=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", \ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than", \ + VCMPGT_HF(QdV, , , ">", N, SRC, MASK, WIDTH)) + +/* Vector BF compare */ +#define MMVEC_CMPGT_BF(TYPE,TYPE2,DESCR,N,MASK,WIDTH,SRC) \ + EXTINSN(V6_vgt##TYPE##_and, "Qx4&=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")",\ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than with predicate-and", \ + VCMPGT_BF(QxV, fGETQBITS(QxV,WIDTH,MASK,i), &, ">", N, SRC, MASK, WIDTH)) \ + EXTINSN(V6_vgt##TYPE##_xor, "Qx4^=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", \ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than with predicate-xor", \ + VCMPGT_BF(QxV, fGETQBITS(QxV,WIDTH,MASK,i), ^, ">", N, SRC, MASK, WIDTH)) \ + EXTINSN(V6_vgt##TYPE##_or, "Qx4|=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", \ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than with predicate-or", \ + VCMPGT_BF(QxV, fGETQBITS(QxV,WIDTH,MASK,i), |, ">", N, SRC, MASK, WIDTH)) \ + EXTINSN(V6_vgt##TYPE, "Qd4=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", \ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than", \ + VCMPGT_BF(QdV, , , ">", N, SRC, MASK, WIDTH)) + +MMVEC_CMPGT_SF(sf,"sf","Vector sf Compare ", fVELEM(32), 0xF, 4, sf) +MMVEC_CMPGT_HF(hf,"hf","Vector hf Compare ", fVELEM(16), 0x3, 2, hf) +MMVEC_CMPGT_BF(bf,"bf","Vector bf Compare ", fVELEM(16), 0x3, 2, bf) + +/****************************************************************************** + BFloat arithmetic and max/min instructions + ******************************************************************************/ + +ITERATOR_INSN_IEEE_FP_DOUBLE_32(32, vadd_sf_bf, + "Vdd32.sf=vadd(Vu32.bf,Vv32.bf)", "Vector IEEE add: bf widen to sf", + VddV.v[0].sf[i] = fp_add_sf_bf(VuV.bf[2*i], VvV.bf[2*i]); + VddV.v[1].sf[i] = fp_add_sf_bf(VuV.bf[2*i+1], VvV.bf[2*i+1]); fBFLOAT()) +ITERATOR_INSN_IEEE_FP_DOUBLE_32(32, vsub_sf_bf, + "Vdd32.sf=vsub(Vu32.bf,Vv32.bf)", "Vector IEEE sub: bf widen to sf", + VddV.v[0].sf[i] = fp_sub_sf_bf(VuV.bf[2*i], VvV.bf[2*i]); + VddV.v[1].sf[i] = fp_sub_sf_bf(VuV.bf[2*i+1], VvV.bf[2*i+1]); fBFLOAT()) +ITERATOR_INSN_IEEE_FP_DOUBLE_32(32, vmpy_sf_bf, + "Vdd32.sf=vmpy(Vu32.bf,Vv32.bf)", "Vector IEEE mul: hf widen to sf", + VddV.v[0].sf[i] = fp_mult_sf_bf(VuV.bf[2*i], VvV.bf[2*i]); + VddV.v[1].sf[i] = fp_mult_sf_bf(VuV.bf[2*i+1], VvV.bf[2*i+1]); fBFLOAT()) +ITERATOR_INSN_IEEE_FP_DOUBLE_32(32, vmpy_sf_bf_acc, + "Vxx32.sf+=vmpy(Vu32.bf,Vv32.bf)", "Vector IEEE fma: hf widen to sf", + VxxV.v[0].sf[i] = fp_mult_sf_bf_acc(VuV.bf[2*i], VvV.bf[2*i], VxxV.v[0].sf[i]); + VxxV.v[1].sf[i] = fp_mult_sf_bf_acc(VuV.bf[2*i+1], VvV.bf[2*i+1], VxxV.v[1].sf[i]); + fCVI_VX_NO_TMP_LD(); fBFLOAT()) +ITERATOR_INSN_IEEE_FP_16(32, vcvt_bf_sf, + "Vd32.bf=vcvt(Vu32.sf,Vv32.sf)", "Vector IEEE cvt: sf to bf", + VdV.bf[2*i] = f32_to_bf16(VuV.sf[i], &env->hvx_fp_status); + VdV.bf[2*i+1] = f32_to_bf16(VvV.sf[i], &env->hvx_fp_status); fBFLOAT()) + +ITERATOR_INSN_IEEE_FP_16_32_LATE(16, vmax_bf, "Vd32.bf=vmax(Vu32.bf,Vv32.bf)", + "Vector IEEE max: bf", VdV.bf[i] = fp_max_bf(VuV.bf[i], VvV.bf[i]); + fBFLOAT()) +ITERATOR_INSN_IEEE_FP_16_32_LATE(16, vmin_bf, "Vd32.bf=vmin(Vu32.bf,Vv32.bf)", + "Vector IEEE min: bf", VdV.bf[i] = fp_min_bf(VuV.bf[i], VvV.bf[i]); + fBFLOAT()) /****************************************************************************** DEBUG Vector/Register Printing diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index 21ab8ae5bb..a5b9c24c6b 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -106,37 +106,37 @@ #define MEM_LOAD1s(DST, VA) \ do { \ CHECK_NOSHUF(VA, 1); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_SB); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_SB | MO_ALIGN); \ } while (0) #define MEM_LOAD1u(DST, VA) \ do { \ CHECK_NOSHUF(VA, 1); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_UB); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_UB | MO_ALIGN); \ } while (0) #define MEM_LOAD2s(DST, VA) \ do { \ CHECK_NOSHUF(VA, 2); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_SW); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_SW | MO_ALIGN); \ } while (0) #define MEM_LOAD2u(DST, VA) \ do { \ CHECK_NOSHUF(VA, 2); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_UW); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_UW | MO_ALIGN); \ } while (0) #define MEM_LOAD4s(DST, VA) \ do { \ CHECK_NOSHUF(VA, 4); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_SL); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_SL | MO_ALIGN); \ } while (0) #define MEM_LOAD4u(DST, VA) \ do { \ CHECK_NOSHUF(VA, 4); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_UL); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_UL | MO_ALIGN); \ } while (0) #define MEM_LOAD8u(DST, VA) \ do { \ CHECK_NOSHUF(VA, 8); \ - tcg_gen_qemu_ld_i64(DST, VA, ctx->mem_idx, MO_LE | MO_UQ); \ + tcg_gen_qemu_ld_i64(DST, VA, ctx->mem_idx, MO_LE | MO_UQ | MO_ALIGN); \ } while (0) #define MEM_STORE1_FUNC(X) \ @@ -519,9 +519,15 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) #define fLOAD(NUM, SIZE, SIGN, EA, DST) MEM_LOAD##SIZE##SIGN(DST, EA) #else #define MEM_LOAD1 cpu_ldub_data_ra -#define MEM_LOAD2 cpu_lduw_le_data_ra -#define MEM_LOAD4 cpu_ldl_le_data_ra -#define MEM_LOAD8 cpu_ldq_le_data_ra +#define MEM_LOAD2(ENV, EA, RA) \ + cpu_ldw_mmu(ENV, EA, make_memop_idx(MO_LEUW | MO_ALIGN, \ + cpu_mmu_index(env_cpu(ENV), false)), RA) +#define MEM_LOAD4(ENV, EA, RA) \ + cpu_ldl_mmu(ENV, EA, make_memop_idx(MO_LEUL | MO_ALIGN, \ + cpu_mmu_index(env_cpu(ENV), false)), RA) +#define MEM_LOAD8(ENV, EA, RA) \ + cpu_ldq_mmu(ENV, EA, make_memop_idx(MO_LEUQ | MO_ALIGN, \ + cpu_mmu_index(env_cpu(ENV), false)), RA) #define fLOAD(NUM, SIZE, SIGN, EA, DST) \ do { \ @@ -576,8 +582,8 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) } while (0) #ifdef QEMU_GENERATE -#define fGETHALF(N, SRC) gen_get_half(HALF, N, SRC, true) -#define fGETUHALF(N, SRC) gen_get_half(HALF, N, SRC, false) +#define fGETHALF(N, SRC) gen_get_half(tmp_half, N, SRC, true) +#define fGETUHALF(N, SRC) gen_get_half(tmp_half, N, SRC, false) #else #define fGETHALF(N, SRC) ((int16_t)((SRC >> ((N) * 16)) & 0xffff)) #define fGETUHALF(N, SRC) ((uint16_t)((SRC >> ((N) * 16)) & 0xffff)) diff --git a/target/hexagon/meson.build b/target/hexagon/meson.build index 59cb09c107..4c921eee73 100644 --- a/target/hexagon/meson.build +++ b/target/hexagon/meson.build @@ -252,6 +252,7 @@ hexagon_ss.add(files( 'fma_emu.c', 'mmvec/decode_ext_mmvec.c', 'mmvec/system_ext_mmvec.c', + 'mmvec/hvx_ieee_fp.c', )) hexagon_softmmu_ss.add(files( diff --git a/target/hexagon/mmvec/hvx_ieee_fp.c b/target/hexagon/mmvec/hvx_ieee_fp.c new file mode 100644 index 0000000000..d7751adbe2 --- /dev/null +++ b/target/hexagon/mmvec/hvx_ieee_fp.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hvx_ieee_fp.h" + +float32 fp_mult_sf_hf(float16 a1, float16 a2, float_status *fp_status) +{ + return float32_mul(float16_to_float32(a1, true, fp_status), + float16_to_float32(a2, true, fp_status), fp_status); +} + +float32 fp_vdmpy(float16 a1, float16 a2, float16 a3, float16 a4, + float_status *fp_status) +{ + return float32_add(fp_mult_sf_hf(a1, a3, fp_status), + fp_mult_sf_hf(a2, a4, fp_status), fp_status); +} + +#define float32_is_pos_nan(X) (float32_is_any_nan(X) && !float32_is_neg(X)) +#define float32_is_neg_nan(X) (float32_is_any_nan(X) && float32_is_neg(X)) +#define float16_is_pos_nan(X) (float16_is_any_nan(X) && !float16_is_neg(X)) +#define float16_is_neg_nan(X) (float16_is_any_nan(X) && float16_is_neg(X)) + +/* Qfloat min/max treat +NaN as greater than +INF and -NaN as smaller than -INF */ +float32 qf_max_sf(float32 a1, float32 a2, float_status *fp_status) +{ + if (float32_is_pos_nan(a1) || float32_is_neg_nan(a2)) { + return a1; + } + if (float32_is_pos_nan(a2) || float32_is_neg_nan(a1)) { + return a2; + } + return float32_max(a1, a2, fp_status); +} + +float32 qf_min_sf(float32 a1, float32 a2, float_status *fp_status) +{ + if (float32_is_pos_nan(a1) || float32_is_neg_nan(a2)) { + return a2; + } + if (float32_is_pos_nan(a2) || float32_is_neg_nan(a1)) { + return a1; + } + return float32_min(a1, a2, fp_status); +} + +float16 qf_max_hf(float16 a1, float16 a2, float_status *fp_status) +{ + if (float16_is_pos_nan(a1) || float16_is_neg_nan(a2)) { + return a1; + } + if (float16_is_pos_nan(a2) || float16_is_neg_nan(a1)) { + return a2; + } + return float16_max(a1, a2, fp_status); +} + +float16 qf_min_hf(float16 a1, float16 a2, float_status *fp_status) +{ + if (float16_is_pos_nan(a1) || float16_is_neg_nan(a2)) { + return a2; + } + if (float16_is_pos_nan(a2) || float16_is_neg_nan(a1)) { + return a1; + } + return float16_min(a1, a2, fp_status); +} + +int32_t conv_w_sf(float32 a, float_status *fp_status) +{ + /* float32_to_int32 converts any NaN to MAX, hexagon looks at the sign. */ + if (float32_is_any_nan(a)) { + return float32_is_neg(a) ? INT32_MIN : INT32_MAX; + } + return float32_to_int32_round_to_zero(a, fp_status); +} + +int16_t conv_h_hf(float16 a, float_status *fp_status) +{ + /* float16_to_int16 converts any NaN to MAX, hexagon looks at the sign. */ + if (float16_is_any_nan(a)) { + return float16_is_neg(a) ? INT16_MIN : INT16_MAX; + } + return float16_to_int16_round_to_zero(a, fp_status); +} + +/* + * Returns true if f1 > f2, where at least one of the elements is guaranteed + * to be NaN. + * Up to v73, Hexagon HVX IEEE FP follows this order: + * QNaN > SNaN > +Inf > numbers > -Inf > SNaN_neg > QNaN_neg + */ +static bool float32_nan_compare(float32 f1, float32 f2, float_status *fp_status) +{ + /* opposite signs case */ + if (float32_is_neg(f1) != float32_is_neg(f2)) { + return !float32_is_neg(f1); + } + + /* same sign case */ + bool result = (float32_is_any_nan(f1) && !float32_is_any_nan(f2)) || + (float32_is_quiet_nan(f1, fp_status) && !float32_is_quiet_nan(f2, fp_status)); + return float32_is_neg(f1) ? !result : result; +} + +static bool float16_nan_compare(float16 f1, float16 f2, float_status *fp_status) +{ + /* opposite signs case */ + if (float16_is_neg(f1) != float16_is_neg(f2)) { + return !float16_is_neg(f1); + } + + /* same sign case */ + bool result = (float16_is_any_nan(f1) && !float16_is_any_nan(f2)) || + (float16_is_quiet_nan(f1, fp_status) && !float16_is_quiet_nan(f2, fp_status)); + return float16_is_neg(f1) ? !result : result; +} + +uint32_t cmpgt_sf(float32 a1, float32 a2, float_status *fp_status) +{ + if (float32_is_any_nan(a1) || float32_is_any_nan(a2)) { + return float32_nan_compare(a1, a2, fp_status); + } + return float32_compare(a1, a2, fp_status) == float_relation_greater; +} + +uint16_t cmpgt_hf(float16 a1, float16 a2, float_status *fp_status) +{ + if (float16_is_any_nan(a1) || float16_is_any_nan(a2)) { + return float16_nan_compare(a1, a2, fp_status); + } + return float16_compare(a1, a2, fp_status) == float_relation_greater; +} diff --git a/target/hexagon/mmvec/hvx_ieee_fp.h b/target/hexagon/mmvec/hvx_ieee_fp.h new file mode 100644 index 0000000000..b7e379b089 --- /dev/null +++ b/target/hexagon/mmvec/hvx_ieee_fp.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HEXAGON_HVX_IEEE_H +#define HEXAGON_HVX_IEEE_H + +#include "fpu/softfloat.h" + +#define FP32_DEF_NAN 0x7FFFFFFF + +#define f16_to_f32(A) float16_to_float32((A), true, &env->hvx_fp_status) +#define f32_to_f16(A) float32_to_float16((A), true, &env->hvx_fp_status) +#define bf16_to_f32(A) bfloat16_to_float32(A, &env->hvx_fp_status) + +float32 fp_mult_sf_hf(float16 a1, float16 a2, float_status *fp_status); +float32 fp_vdmpy(float16 a1, float16 a2, float16 a3, float16 a4, + float_status *fp_status); + +/* Qfloat min/max treat +NaN as greater than +INF and -NaN as smaller than -INF */ +float32 qf_max_sf(float32 a1, float32 a2, float_status *fp_status); +float32 qf_min_sf(float32 a1, float32 a2, float_status *fp_status); +float16 qf_max_hf(float16 a1, float16 a2, float_status *fp_status); +float16 qf_min_hf(float16 a1, float16 a2, float_status *fp_status); + +int32_t conv_w_sf(float32 a, float_status *fp_status); +int16_t conv_h_hf(float16 a, float_status *fp_status); + +/* IEEE - FP compare instructions */ +uint32_t cmpgt_sf(float32 a1, float32 a2, float_status *fp_status); +uint16_t cmpgt_hf(float16 a1, float16 a2, float_status *fp_status); + +/* IEEE BFloat instructions */ + +#define fp_mult_sf_bf(A, B) \ + float32_mul(bf16_to_f32(A), bf16_to_f32(B), &env->hvx_fp_status) + +#define fp_add_sf_bf(A, B) \ + float32_add(bf16_to_f32(A), bf16_to_f32(B), &env->hvx_fp_status) + +#define fp_sub_sf_bf(A, B) \ + float32_sub(bf16_to_f32(A), bf16_to_f32(B), &env->hvx_fp_status) + +#define fp_mult_sf_bf_acc(f1, f2, f3) \ + float32_muladd(bf16_to_f32(f1), bf16_to_f32(f2), f3, 0, &env->hvx_fp_status) + +static inline bfloat16 f32_to_bf16(float32 A, float_status *fp_status) +{ + uint32_t rslt = A; + if ((rslt & 0x1FFFF) == 0x08000) { + /* do not round up if exactly .5 and even already */ + } else if ((rslt & 0x8000) == 0x8000) { + rslt += 0x8000; /* rounding to nearest number */ + } + rslt = float32_is_any_nan(A) ? FP32_DEF_NAN : rslt; + return float32_to_bfloat16(rslt, fp_status); +} + +#define fp_min_bf(A, B) \ + f32_to_bf16(float32_min(bf16_to_f32(A), bf16_to_f32(B), &env->hvx_fp_status), \ + &env->hvx_fp_status); + +#define fp_max_bf(A, B) \ + f32_to_bf16(float32_max(bf16_to_f32(A), bf16_to_f32(B), &env->hvx_fp_status), \ + &env->hvx_fp_status); + +#endif diff --git a/target/hexagon/mmvec/macros.h b/target/hexagon/mmvec/macros.h index c7840fbf2e..b36f833b1d 100644 --- a/target/hexagon/mmvec/macros.h +++ b/target/hexagon/mmvec/macros.h @@ -23,6 +23,10 @@ #include "mmvec/system_ext_mmvec.h" #include "accel/tcg/getpc.h" #include "accel/tcg/probe.h" +#include "mmvec/hvx_ieee_fp.h" + +#define fBFLOAT() +#define fCVI_VX_NO_TMP_LD() #ifndef QEMU_GENERATE #define VdV (*(MMVector *restrict)(VdV_void)) @@ -353,6 +357,11 @@ do { \ COE = (sextract32(VAL, 24 + 2 * POS, 2) << 8) | \ extract32(VAL, POS * 8, 8); \ - } while (0); + } while (0) \ + ; + +#define fCMPGT_SF(A, B) cmpgt_sf(A, B, &env->hvx_fp_status) +#define fCMPGT_HF(A, B) cmpgt_hf(A, B, &env->hvx_fp_status) +#define fCMPGT_BF(A, B) fCMPGT_SF((uint32_t)(A) << 16, (uint32_t)(B) << 16) #endif diff --git a/target/hexagon/mmvec/mmvec.h b/target/hexagon/mmvec/mmvec.h index 4a4f6cc980..8e72f2f6ae 100644 --- a/target/hexagon/mmvec/mmvec.h +++ b/target/hexagon/mmvec/mmvec.h @@ -41,6 +41,9 @@ typedef union { int16_t h[MAX_VEC_SIZE_BYTES / 2]; uint8_t ub[MAX_VEC_SIZE_BYTES / 1]; int8_t b[MAX_VEC_SIZE_BYTES / 1]; + float32 sf[MAX_VEC_SIZE_BYTES / 4]; + float16 hf[MAX_VEC_SIZE_BYTES / 2]; + bfloat16 bf[MAX_VEC_SIZE_BYTES / 2]; } MMVector; typedef union { diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index 125952aee5..23894ff3d2 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -35,11 +35,13 @@ #include "mmvec/macros.h" #include "op_helper.h" #include "cpu_helper.h" +#include "tcg/tcg-gvec-desc.h" #include "translate.h" #ifndef CONFIG_USER_ONLY #include "hw/hexagon/hexagon_globalreg.h" #include "hex_mmu.h" #include "hw/hexagon/hexagon_tlb.h" +#include "hw/intc/hex-l2vic.h" #include "hex_interrupts.h" #include "hexswi.h" #endif @@ -95,19 +97,26 @@ static void commit_store(CPUHexagonState *env, int slot_num, uintptr_t ra) { uint32_t width = env->mem_log_stores[slot_num].width; target_ulong va = env->mem_log_stores[slot_num].va; + MemOpIdx oi; switch (width) { case 1: cpu_stb_data_ra(env, va, env->mem_log_stores[slot_num].data32, ra); break; case 2: - cpu_stw_le_data_ra(env, va, env->mem_log_stores[slot_num].data32, ra); + oi = make_memop_idx(MO_LEUW | MO_ALIGN, + cpu_mmu_index(env_cpu(env), false)); + cpu_stw_mmu(env, va, env->mem_log_stores[slot_num].data32, oi, ra); break; case 4: - cpu_stl_le_data_ra(env, va, env->mem_log_stores[slot_num].data32, ra); + oi = make_memop_idx(MO_LEUL | MO_ALIGN, + cpu_mmu_index(env_cpu(env), false)); + cpu_stl_mmu(env, va, env->mem_log_stores[slot_num].data32, oi, ra); break; case 8: - cpu_stq_le_data_ra(env, va, env->mem_log_stores[slot_num].data64, ra); + oi = make_memop_idx(MO_LEUQ | MO_ALIGN, + cpu_mmu_index(env_cpu(env), false)); + cpu_stq_mmu(env, va, env->mem_log_stores[slot_num].data64, oi, ra); break; default: g_assert_not_reached(); @@ -1557,7 +1566,20 @@ void HELPER(raise_stack_overflow)(CPUHexagonState *env, uint32_t slot, void HELPER(ciad)(CPUHexagonState *env, uint32_t mask) { - g_assert_not_reached(); + uint32_t ipendad; + uint32_t iad; + HexagonCPU *cpu; + + BQL_LOCK_GUARD(); + cpu = env_archcpu(env); + ipendad = hexagon_globalreg_read(cpu->globalregs, HEX_SREG_IPENDAD, + env->threadId); + iad = fGET_FIELD(ipendad, IPENDAD_IAD); + fSET_FIELD(ipendad, IPENDAD_IAD, iad & ~(mask)); + hexagon_globalreg_write(cpu->globalregs, HEX_SREG_IPENDAD, + ipendad, env->threadId); + l2vic_clear_interrupt(cpu->l2vic); + hex_interrupt_update(env); } void HELPER(siad)(CPUHexagonState *env, uint32_t mask) @@ -1940,3 +1962,23 @@ void HELPER(pending_interrupt)(CPUHexagonState *env) printf("ERROR: bogus helper: " #tag "\n") #include "helper_funcs_generated.c.inc" + +#define DO_ABSDIFF(NAME, TYPE, UTYPE) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + UTYPE *d = vd; \ + TYPE *n = vn, *m = vm; \ + \ + for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ + d[i] = n[i] < m[i] ? (UTYPE)m[i] - (UTYPE)n[i] \ + : (UTYPE)n[i] - (UTYPE)m[i]; \ + } \ +} + +DO_ABSDIFF(gvec_sabsdiff_h, int16_t, uint16_t) +DO_ABSDIFF(gvec_sabsdiff_w, int32_t, uint32_t) +DO_ABSDIFF(gvec_uabsdiff_b, uint8_t, uint8_t) +DO_ABSDIFF(gvec_uabsdiff_h, uint16_t, uint16_t) + +#undef DO_ABSDIFF diff --git a/target/hexagon/printinsn.c b/target/hexagon/printinsn.c index a7e46f4bcd..023ea12dba 100644 --- a/target/hexagon/printinsn.c +++ b/target/hexagon/printinsn.c @@ -23,18 +23,105 @@ #include "internal.h" #include "decode.h" -static const char *sreg2str(unsigned int reg) +/* + * Used when there is some sort of error and we can't figure out the real + * system register name + */ +static const char *const generic_sreg_names[256] = { + "S000", "S001", "S002", "S003", "S004", "S005", "S006", "S007", + "S008", "S009", "S010", "S011", "S012", "S013", "S014", "S015", + "S016", "S017", "S018", "S019", "S020", "S021", "S022", "S023", + "S024", "S025", "S026", "S027", "S028", "S029", "S030", "S031", + "S032", "S033", "S034", "S035", "S036", "S037", "S038", "S039", + "S040", "S041", "S042", "S043", "S044", "S045", "S046", "S047", + "S048", "S049", "S050", "S051", "S052", "S053", "S054", "S055", + "S056", "S057", "S058", "S059", "S060", "S061", "S062", "S063", + "S064", "S065", "S066", "S067", "S068", "S069", "S070", "S071", + "S072", "S073", "S074", "S075", "S076", "S077", "S078", "S079", + "S080", "S081", "S082", "S083", "S084", "S085", "S086", "S087", + "S088", "S089", "S090", "S091", "S092", "S093", "S094", "S095", + "S096", "S097", "S098", "S099", "S100", "S101", "S102", "S103", + "S104", "S105", "S106", "S107", "S108", "S109", "S110", "S111", + "S112", "S113", "S114", "S115", "S116", "S117", "S118", "S119", + "S120", "S121", "S122", "S123", "S124", "S125", "S126", "S127", + "S128", "S129", "S130", "S131", "S132", "S133", "S134", "S135", + "S136", "S137", "S138", "S139", "S140", "S141", "S142", "S143", + "S144", "S145", "S146", "S147", "S148", "S149", "S150", "S151", + "S152", "S153", "S154", "S155", "S156", "S157", "S158", "S159", + "S160", "S161", "S162", "S163", "S164", "S165", "S166", "S167", + "S168", "S169", "S170", "S171", "S172", "S173", "S174", "S175", + "S176", "S177", "S178", "S179", "S180", "S181", "S182", "S183", + "S184", "S185", "S186", "S187", "S188", "S189", "S190", "S191", + "S192", "S193", "S194", "S195", "S196", "S197", "S198", "S199", + "S200", "S201", "S202", "S203", "S204", "S205", "S206", "S207", + "S208", "S209", "S210", "S211", "S212", "S213", "S214", "S215", + "S216", "S217", "S218", "S219", "S220", "S221", "S222", "S223", + "S224", "S225", "S226", "S227", "S228", "S229", "S230", "S231", + "S232", "S233", "S234", "S235", "S236", "S237", "S238", "S239", + "S240", "S241", "S242", "S243", "S244", "S245", "S246", "S247", + "S248", "S249", "S250", "S251", "S252", "S253", "S254", "S255", +}; + +static const char *sreg2str(uint8_t reg) { - if (reg < TOTAL_PER_THREAD_REGS) { - return hexagon_regnames[reg]; +#ifndef CONFIG_USER_ONLY + if (reg < NUM_SREGS) { + return hexagon_sregnames[reg]; } else { - return "???"; + return generic_sreg_names[reg]; } +#else + return generic_sreg_names[reg]; +#endif } -static const char *creg2str(unsigned int reg) +/* + * Used when there is some sort of error and we can't figure out the real + * control register name + */ +static const char *const generic_creg_names[256] = { + "C000", "C001", "C002", "C003", "C004", "C005", "C006", "C007", + "C008", "C009", "C010", "C011", "C012", "C013", "C014", "C015", + "C016", "C017", "C018", "C019", "C020", "C021", "C022", "C023", + "C024", "C025", "C026", "C027", "C028", "C029", "C030", "C031", + "C032", "C033", "C034", "C035", "C036", "C037", "C038", "C039", + "C040", "C041", "C042", "C043", "C044", "C045", "C046", "C047", + "C048", "C049", "C050", "C051", "C052", "C053", "C054", "C055", + "C056", "C057", "C058", "C059", "C060", "C061", "C062", "C063", + "C064", "C065", "C066", "C067", "C068", "C069", "C070", "C071", + "C072", "C073", "C074", "C075", "C076", "C077", "C078", "C079", + "C080", "C081", "C082", "C083", "C084", "C085", "C086", "C087", + "C088", "C089", "C090", "C091", "C092", "C093", "C094", "C095", + "C096", "C097", "C098", "C099", "C100", "C101", "C102", "C103", + "C104", "C105", "C106", "C107", "C108", "C109", "C110", "C111", + "C112", "C113", "C114", "C115", "C116", "C117", "C118", "C119", + "C120", "C121", "C122", "C123", "C124", "C125", "C126", "C127", + "C128", "C129", "C130", "C131", "C132", "C133", "C134", "C135", + "C136", "C137", "C138", "C139", "C140", "C141", "C142", "C143", + "C144", "C145", "C146", "C147", "C148", "C149", "C150", "C151", + "C152", "C153", "C154", "C155", "C156", "C157", "C158", "C159", + "C160", "C161", "C162", "C163", "C164", "C165", "C166", "C167", + "C168", "C169", "C170", "C171", "C172", "C173", "C174", "C175", + "C176", "C177", "C178", "C179", "C180", "C181", "C182", "C183", + "C184", "C185", "C186", "C187", "C188", "C189", "C190", "C191", + "C192", "C193", "C194", "C195", "C196", "C197", "C198", "C199", + "C200", "C201", "C202", "C203", "C204", "C205", "C206", "C207", + "C208", "C209", "C210", "C211", "C212", "C213", "C214", "C215", + "C216", "C217", "C218", "C219", "C220", "C221", "C222", "C223", + "C224", "C225", "C226", "C227", "C228", "C229", "C230", "C231", + "C232", "C233", "C234", "C235", "C236", "C237", "C238", "C239", + "C240", "C241", "C242", "C243", "C244", "C245", "C246", "C247", + "C248", "C249", "C250", "C251", "C252", "C253", "C254", "C255", +}; + +static const char *creg2str(uint8_t reg) { - return sreg2str(reg + HEX_REG_SA0); + uint8_t gpr = reg + HEX_REG_SA0; + if (gpr < TOTAL_PER_THREAD_REGS) { + return hexagon_regnames[gpr]; + } else { + return generic_creg_names[reg]; + } } static void snprintinsn(GString *buf, Insn *insn) @@ -52,7 +139,7 @@ static void snprintinsn(GString *buf, Insn *insn) } void snprint_a_pkt_disas(GString *buf, Packet *pkt, uint32_t *words, - target_ulong pc, const HexagonCPUDef *hex_def) + target_ulong pc, const HexagonCPUConfig *cfg) { bool has_endloop0 = false; bool has_endloop1 = false; @@ -84,12 +171,17 @@ void snprint_a_pkt_disas(GString *buf, Packet *pkt, uint32_t *words, } g_string_append(buf, "\t"); - if (opcode_supported(pkt->insn[i].opcode, hex_def)) { + if (opcode_supported(pkt->insn[i].opcode, cfg->hex_def)) { snprintinsn(buf, &(pkt->insn[i])); } else { g_string_append(buf, ""); } + if (!cfg->ieee_fp_extension && + GET_ATTRIB(pkt->insn[i].opcode, A_HVX_IEEE_FP)) { + g_string_append(buf, " (disabled: no ieee_fp)"); + } + if (i < pkt->num_insns - 1) { /* * Subinstructions are two instructions encoded diff --git a/target/hexagon/printinsn.h b/target/hexagon/printinsn.h index de962b5f2e..c8389408d3 100644 --- a/target/hexagon/printinsn.h +++ b/target/hexagon/printinsn.h @@ -22,6 +22,6 @@ #include "insn.h" void snprint_a_pkt_disas(GString *buf, Packet *pkt, uint32_t *words, - target_ulong pc, const HexagonCPUDef *hex_def); + target_ulong pc, const HexagonCPUConfig *cfg); #endif diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 77235916f4..06a8159d28 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -54,6 +54,7 @@ static const AnalyzeInsn opcode_analyze[XX_LAST_OPCODE] = { TCGv hex_gpr[TOTAL_PER_THREAD_REGS]; TCGv hex_pred[NUM_PREGS]; TCGv hex_slot_cancelled; +TCGv hex_next_PC; TCGv hex_new_value_usr; TCGv hex_store_addr[STORES_MAX]; TCGv_i32 hex_store_width[STORES_MAX]; @@ -184,10 +185,16 @@ static void gen_goto_tb(DisasContext *ctx, unsigned tb_slot_idx, } } +static bool need_next_PC(DisasContext *ctx); + static void gen_end_tb(DisasContext *ctx) { gen_exec_counters(ctx); + if (ctx->need_next_pc) { + tcg_gen_mov_tl(hex_gpr[HEX_REG_PC], hex_next_PC); + } + if (ctx->branch_cond != TCG_COND_NEVER) { if (ctx->branch_cond != TCG_COND_ALWAYS) { TCGLabel *skip = gen_new_label(); @@ -391,17 +398,25 @@ static bool pkt_ends_tb(Packet *pkt) static bool need_next_PC(DisasContext *ctx) { - /* Check for conditional control flow or HW loop end */ - for (int i = 0; i < ctx->pkt.num_insns; i++) { - uint16_t opcode = ctx->pkt.insn[i].opcode; - if (GET_ATTRIB(opcode, A_CONDEXEC) && GET_ATTRIB(opcode, A_COF)) { - return true; - } - if (GET_ATTRIB(opcode, A_HWLOOP0_END) || - GET_ATTRIB(opcode, A_HWLOOP1_END)) { - return true; + Packet *pkt = &ctx->pkt; + if (pkt->pkt_has_cof || ctx->pkt_ends_tb) { + for (int i = 0; i < pkt->num_insns; i++) { + uint16_t opcode = pkt->insn[i].opcode; + if ((GET_ATTRIB(opcode, A_CONDEXEC) && GET_ATTRIB(opcode, A_COF)) || + GET_ATTRIB(opcode, A_HWLOOP0_END) || + GET_ATTRIB(opcode, A_HWLOOP1_END)) { + return true; + } } } + /* + * We end the TB on some instructions that do not change the flow (for + * other reasons). In these cases, we must set pc too, as the insn won't + * do it themselves. + */ + if (ctx->pkt_ends_tb && !check_for_attrib(pkt, A_COF)) { + return true; + } return false; } @@ -615,9 +630,10 @@ static void gen_start_packet(DisasContext *ctx) } for (i = 0; i < ctx->sreg_log_idx; i++) { int reg_num = ctx->sreg_log[i]; - if (reg_num < HEX_SREG_GLB_START && - (ctx->need_commit || reg_num == HEX_SREG_SSR)) { + if (reg_num < HEX_SREG_GLB_START) { ctx->t_sreg_new_value[reg_num] = tcg_temp_new(); + tcg_gen_mov_tl(ctx->t_sreg_new_value[reg_num], + hex_t_sreg[reg_num]); } } for (i = 0; i < NUM_GREGS; i++) { @@ -636,12 +652,14 @@ static void gen_start_packet(DisasContext *ctx) ctx->branch_taken = NULL; if (ctx->pkt.pkt_has_cof) { ctx->branch_taken = tcg_temp_new(); - if (ctx->pkt.pkt_has_multi_cof) { - tcg_gen_movi_tl(ctx->branch_taken, 0); - } - if (need_next_PC(ctx)) { - tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], next_PC); - } + } + if (ctx->pkt.pkt_has_multi_cof) { + tcg_gen_movi_tl(ctx->branch_taken, 0); + } + ctx->pkt_ends_tb = pkt_ends_tb(&ctx->pkt); + ctx->need_next_pc = need_next_PC(ctx); + if (ctx->need_next_pc) { + tcg_gen_movi_tl(hex_next_PC, next_PC); } /* Preload the predicated registers into get_result_gpr(ctx, i) */ @@ -896,17 +914,17 @@ void process_store(DisasContext *ctx, int slot_num) case 2: tcg_gen_qemu_st_tl(hex_store_val32[slot_num], hex_store_addr[slot_num], - ctx->mem_idx, MO_LE | MO_UW); + ctx->mem_idx, MO_LE | MO_UW | MO_ALIGN); break; case 4: tcg_gen_qemu_st_tl(hex_store_val32[slot_num], hex_store_addr[slot_num], - ctx->mem_idx, MO_LE | MO_UL); + ctx->mem_idx, MO_LE | MO_UL | MO_ALIGN); break; case 8: tcg_gen_qemu_st_i64(hex_store_val64[slot_num], hex_store_addr[slot_num], - ctx->mem_idx, MO_LE | MO_UQ); + ctx->mem_idx, MO_LE | MO_UQ | MO_ALIGN); break; default: { @@ -1141,7 +1159,7 @@ static void gen_commit_packet(DisasContext *ctx) ctx->pkt.vhist_insn->generate(ctx); } - if (pkt_ends_tb(&ctx->pkt) || ctx->base.is_jmp == DISAS_NORETURN) { + if (ctx->pkt_ends_tb || ctx->base.is_jmp == DISAS_NORETURN) { gen_end_tb(ctx); } } @@ -1191,8 +1209,9 @@ static void hexagon_tr_init_disas_context(DisasContextBase *dcbase, ctx->num_hvx_insns = 0; ctx->branch_cond = TCG_COND_NEVER; ctx->is_tight_loop = FIELD_EX32(hex_flags, TB_FLAGS, IS_TIGHT_LOOP); - ctx->short_circuit = hex_cpu->short_circuit; + ctx->short_circuit = hex_cpu->cfg.short_circuit; ctx->hex_def = HEXAGON_CPU_GET_CLASS(hex_cpu)->hex_def; + ctx->ieee_fp_extension = hex_cpu->cfg.ieee_fp_extension; #ifndef CONFIG_USER_ONLY ctx->num_cycles = 0; ctx->pcycle_enabled = FIELD_EX32(hex_flags, TB_FLAGS, PCYCLE_ENABLED); @@ -1249,7 +1268,7 @@ static void hexagon_tr_translate_packet(DisasContextBase *dcbase, CPUState *cpu) * so end the TLB after every packet. */ HexagonCPU *hex_cpu = env_archcpu(env); - if (hex_cpu->lldb_compat && qemu_loglevel_mask(CPU_LOG_TB_CPU)) { + if (hex_cpu->cfg.lldb_compat && qemu_loglevel_mask(CPU_LOG_TB_CPU)) { ctx->base.is_jmp = DISAS_TOO_MANY; } } @@ -1326,6 +1345,8 @@ void hexagon_translate_init(void) } hex_new_value_usr = tcg_global_mem_new(tcg_env, offsetof(CPUHexagonState, new_value_usr), "new_value_usr"); + hex_next_PC = tcg_global_mem_new(tcg_env, + offsetof(CPUHexagonState, next_PC), "next_PC"); for (i = 0; i < NUM_PREGS; i++) { hex_pred[i] = tcg_global_mem_new(tcg_env, diff --git a/target/hexagon/translate.h b/target/hexagon/translate.h index 0a7f37d584..3c5773e2c7 100644 --- a/target/hexagon/translate.h +++ b/target/hexagon/translate.h @@ -40,6 +40,7 @@ typedef struct DisasContext { int reg_log_idx; DECLARE_BITMAP(regs_written, TOTAL_PER_THREAD_REGS); DECLARE_BITMAP(predicated_regs, TOTAL_PER_THREAD_REGS); + bool pkt_ends_tb; bool implicit_usr_write; #ifndef CONFIG_USER_ONLY int greg_log[GREG_WRITES_MAX]; @@ -75,10 +76,12 @@ typedef struct DisasContext { DECLARE_BITMAP(insn_qregs_read, NUM_QREGS); bool pre_commit; bool need_commit; + bool need_next_pc; TCGCond branch_cond; target_ulong branch_dest; bool is_tight_loop; bool short_circuit; + bool ieee_fp_extension; bool read_after_write; bool has_hvx_overlap; TCGv new_value[TOTAL_PER_THREAD_REGS]; @@ -310,6 +313,7 @@ extern TCGv hex_gpr[TOTAL_PER_THREAD_REGS]; extern TCGv hex_pred[NUM_PREGS]; extern TCGv hex_slot_cancelled; extern TCGv hex_new_value_usr; +extern TCGv hex_next_PC; extern TCGv hex_store_addr[STORES_MAX]; extern TCGv_i32 hex_store_width[STORES_MAX]; extern TCGv hex_store_val32[STORES_MAX]; diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index a68152f968..07b49e5132 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -181,7 +181,7 @@ static void hppa_cpu_realizefn(DeviceState *dev, Error **errp) HPPACPUClass *acc = HPPA_CPU_GET_CLASS(dev); Error *local_err = NULL; - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/i386/cpu-apic.c b/target/i386/cpu-apic.c index 04b7257ad1..b4cf048a7c 100644 --- a/target/i386/cpu-apic.c +++ b/target/i386/cpu-apic.c @@ -14,6 +14,7 @@ #include "system/hw_accel.h" #include "system/kvm.h" #include "system/xen.h" +#include "system/mshv.h" #include "system/address-spaces.h" #include "hw/core/qdev-properties.h" #include "hw/i386/apic_internal.h" @@ -34,6 +35,8 @@ APICCommonClass *apic_get_class(Error **errp) apic_type = "xen-apic"; } else if (whpx_irqchip_in_kernel()) { apic_type = "whpx-apic"; + } else if (mshv_enabled()) { + apic_type = "mshv-apic"; } return APIC_COMMON_CLASS(object_class_by_name(apic_type)); diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 5805d33ab9..e5ffb10d15 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -10164,7 +10164,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) * These may be set by the accel-specific code, * and the results are subsequently checked / assumed in this function. */ - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; @@ -10179,7 +10179,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) if (cpu->guest_phys_bits == -1) { /* * If it was not set by the user, or by the accelerator via - * cpu_exec_realizefn, clear. + * cpu_common_realize, clear. */ cpu->guest_phys_bits = 0; } @@ -10188,7 +10188,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) /* * The default is the same as KVM's. Note that this check * needs to happen after the evenual setting of ucode_rev in - * accel-specific code in cpu_exec_realizefn. + * accel-specific code in cpu_common_realize. */ if (IS_AMD_CPU(env)) { cpu->ucode_rev = 0x01000065; @@ -10201,7 +10201,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) * mwait extended info: needed for Core compatibility * We always wake on interrupt even if host does not have the capability. * - * requires the accel-specific code in cpu_exec_realizefn to + * requires the accel-specific code in cpu_common_realize to * have already acquired the CPUID data into cpu->mwait. */ cpu->mwait.ecx |= CPUID_MWAIT_EMX | CPUID_MWAIT_IBE; @@ -10230,7 +10230,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) * Note that this code assumes features expansion has already been done * (as it checks for CPUID_EXT2_LM), and also assumes that potential * phys_bits adjustments to match the host have been already done in - * accel-specific code in cpu_exec_realizefn. + * accel-specific code in cpu_common_realize. */ if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) { if (cpu->phys_bits && cpu->phys_bits < 32) { @@ -10387,7 +10387,7 @@ static void x86_cpu_set_bit_prop(Object *obj, Visitor *v, const char *name, BitProperty *fp = opaque; bool value; - if (dev->realized) { + if (qdev_is_realized(dev)) { qdev_prop_set_after_realize(dev, name, errp); return; } diff --git a/target/i386/cpu.h b/target/i386/cpu.h index e6a197602d..641f3ee5c2 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -33,6 +33,7 @@ #include "qemu/cpu-float.h" #include "qemu/timer.h" #include "standard-headers/asm-x86/kvm_para.h" +#include "hw/hyperv/hvgdk_mini.h" #define XEN_NR_VIRQS 24 @@ -44,6 +45,10 @@ #define ELF_MACHINE_UNAME "i686" #endif +#ifdef CONFIG_MSHV +#define MSHV_STIMERS_STATE_SIZE 200 +#endif + enum { R_EAX = 0, R_ECX = 1, @@ -655,9 +660,11 @@ typedef enum X86Seg { #define XSTATE_DYNAMIC_MASK (XSTATE_XTILE_DATA_MASK) +#define ESA_FEATURE_XSS_BIT 0 #define ESA_FEATURE_ALIGN64_BIT 1 #define ESA_FEATURE_XFD_BIT 2 +#define ESA_FEATURE_XSS_MASK (1U << ESA_FEATURE_XSS_BIT) #define ESA_FEATURE_ALIGN64_MASK (1U << ESA_FEATURE_ALIGN64_BIT) #define ESA_FEATURE_XFD_MASK (1U << ESA_FEATURE_XFD_BIT) @@ -2299,6 +2306,11 @@ typedef struct CPUArchState { #if defined(CONFIG_HVF) || defined(CONFIG_MSHV) || defined(CONFIG_WHPX) void *emu_mmio_buf; #endif +#if defined(CONFIG_MSHV) + uint8_t hv_simp_page[HV_HYP_PAGE_SIZE]; + uint8_t hv_siefp_page[HV_HYP_PAGE_SIZE]; + uint8_t hv_synthetic_timers_state[MSHV_STIMERS_STATE_SIZE]; +#endif uint64_t mcg_cap; uint64_t mcg_ctl; diff --git a/target/i386/emulate/x86_decode.c b/target/i386/emulate/x86_decode.c index bae1dd4d6f..34dcac155a 100644 --- a/target/i386/emulate/x86_decode.c +++ b/target/i386/emulate/x86_decode.c @@ -1851,6 +1851,12 @@ static void decode_prefix(CPUX86State *env, struct x86_decode *decode) case PREFIX_SS_SEG_OVERRIDE: case PREFIX_DS_SEG_OVERRIDE: case PREFIX_ES_SEG_OVERRIDE: + if (x86_is_long_mode(env_cpu(env))) { + /* ES/CS/SS/DS segment overrides are ignored in long mode */ + decode->rex.rex = 0; + break; + } + /* fall through when not in long mode */ case PREFIX_FS_SEG_OVERRIDE: case PREFIX_GS_SEG_OVERRIDE: decode->segment_override = byte; diff --git a/target/i386/emulate/x86_flags.c b/target/i386/emulate/x86_flags.c index 3c4270a14c..c49d8bb836 100644 --- a/target/i386/emulate/x86_flags.c +++ b/target/i386/emulate/x86_flags.c @@ -30,28 +30,24 @@ /* - * The algorithms here are similar to those in Bochs. After an ALU - * operation, CC_DST can be used to compute ZF, SF and PF, whereas - * CC_SRC is used to compute AF, CF and OF. In reality, SF and PF are the - * XOR of the value computed from CC_DST and the value found in bits 7 and 2 - * of CC_SRC; this way the same logic can be used to compute the flags - * both before and after an ALU operation. + * The emulator always encodes flags in the same way as CC_OP_CCMPB + MO_TL. + * While for arithmetic operations ZF/SF/PF are computed from the same value, + * ZF=1 may be inconsistent with PF/SF for arbitrary RFLAGS values so CC_SRC2 + * is used for SF and PF. CC_SRC holds a carry-out vector that is used to + * compute AF, CF and OF. * * Compared to the TCG CC_OP codes, this avoids conditionals when converting * to and from the RFLAGS representation. + * + * The underlying ideas ultimately descend from Bochs, but with significant + * simplifications obtained by storing flags in three words rather than two. */ #define LF_SIGN_BIT (TARGET_LONG_BITS - 1) -#define LF_BIT_PD (2) /* lazy Parity Delta, same bit as PF */ -#define LF_BIT_AF (3) /* lazy Adjust flag */ -#define LF_BIT_SD (7) /* lazy Sign Flag Delta, same bit as SF */ #define LF_BIT_CF (TARGET_LONG_BITS - 1) /* lazy Carry Flag */ #define LF_BIT_PO (TARGET_LONG_BITS - 2) /* lazy Partial Overflow = CF ^ OF */ -#define LF_MASK_PD ((target_ulong)0x01 << LF_BIT_PD) -#define LF_MASK_AF ((target_ulong)0x01 << LF_BIT_AF) -#define LF_MASK_SD ((target_ulong)0x01 << LF_BIT_SD) #define LF_MASK_CF ((target_ulong)0x01 << LF_BIT_CF) #define LF_MASK_PO ((target_ulong)0x01 << LF_BIT_PO) @@ -59,19 +55,15 @@ /* OSZAPC */ /* ******************* */ -/* use carries to fill in AF, PO and CF, while ensuring PD and SD are clear. - * for full-word operations just clear PD and SD; for smaller operand - * sizes only keep AF in the low byte and shift the carries left to - * place PO and CF in the top two bits. +/* + * For arithmetic operations ZF/SF/PF are consistent so DST == SRC2. + * For operations that are not full-word, keep AF in the low byte and shift + * the carries left to place PO and CF in the top two bits. */ #define SET_FLAGS_OSZAPC_SIZE(size, lf_carries, lf_result) { \ - env->cc_dst = (target_ulong)(int##size##_t)(lf_result); \ - target_ulong temp = (lf_carries); \ - if ((size) == TARGET_LONG_BITS) { \ - temp = temp & ~(LF_MASK_PD | LF_MASK_SD); \ - } else { \ - temp = (temp & LF_MASK_AF) | (temp << (TARGET_LONG_BITS - (size))); \ - } \ + env->cc_dst = env->cc_src2 = (target_ulong)(int##size##_t)(lf_result); \ + target_ulong temp = (lf_carries) & MAKE_64BIT_MASK(0, size); \ + temp |= temp << (TARGET_LONG_BITS - (size)); \ env->cc_src = temp; \ } @@ -93,13 +85,9 @@ /* same as setting OSZAPC, but preserve CF and flip PO if the old value of CF * did not match the high bit of lf_carries. */ #define SET_FLAGS_OSZAP_SIZE(size, lf_carries, lf_result) { \ - env->cc_dst = (target_ulong)(int##size##_t)(lf_result); \ - target_ulong temp = (lf_carries); \ - if ((size) == TARGET_LONG_BITS) { \ - temp = (temp & ~(LF_MASK_PD | LF_MASK_SD)); \ - } else { \ - temp = (temp & LF_MASK_AF) | (temp << (TARGET_LONG_BITS - (size))); \ - } \ + env->cc_dst = env->cc_src2 = (target_ulong)(int##size##_t)(lf_result); \ + target_ulong temp = (lf_carries) & MAKE_64BIT_MASK(0, size); \ + temp |= temp << (TARGET_LONG_BITS - (size)); \ target_ulong cf_changed = ((target_long)(env->cc_src ^ temp)) < 0; \ env->cc_src = temp ^ (cf_changed * (LF_MASK_PO | LF_MASK_CF)); \ } @@ -255,7 +243,7 @@ void SET_FLAGS_OSZAPC_LOGIC8(CPUX86State *env, uint8_t v1, uint8_t v2, static inline uint32_t get_PF(CPUX86State *env) { - return ((parity8(env->cc_dst) - 1) ^ env->cc_src) & CC_P; + return (parity8(env->cc_src2) - 1) & CC_P; } static inline uint32_t get_OF(CPUX86State *env) @@ -283,8 +271,7 @@ static inline uint32_t get_ZF(CPUX86State *env) static inline uint32_t get_SF(CPUX86State *env) { - return ((env->cc_dst >> (LF_SIGN_BIT - LF_BIT_SD)) ^ - env->cc_src) & CC_S; + return (target_long)env->cc_src2 < 0 ? CC_S : 0; } void lflags_to_rflags(CPUX86State *env) @@ -304,16 +291,14 @@ void rflags_to_lflags(CPUX86State *env) { target_ulong cf_af, cf_xor_of; - /* Leave the low byte zero so that parity is always even... */ - env->cc_dst = !(env->eflags & CC_Z) << 8; - - /* ... and therefore cc_src always uses opposite polarity. */ - env->cc_src = CC_P; - env->cc_src ^= env->eflags & (CC_S | CC_P); + /* compute DST and SRC2 that reconstruct ZF/SF/PF. */ + env->cc_dst = ~env->eflags & CC_Z; /* DST = 0 if ZF=1 */ + env->cc_src2 = ~env->eflags & CC_P; /* odd parity if PF=0 */ + env->cc_src2 ^= -!!(env->eflags & CC_S); /* rotate right by one to move CF and AF into the carry-out positions */ cf_af = env->eflags & (CC_C | CC_A); - env->cc_src |= ((cf_af >> 1) | (cf_af << (TARGET_LONG_BITS - 1))); + env->cc_src = ((cf_af >> 1) | (cf_af << (TARGET_LONG_BITS - 1))); cf_xor_of = ((env->eflags & (CC_C | CC_O)) + (CC_O - CC_C)) & CC_O; env->cc_src |= -cf_xor_of & LF_MASK_PO; diff --git a/target/i386/kvm/kvm-cpu.c b/target/i386/kvm/kvm-cpu.c index c34d9f15c7..da8d77ade0 100644 --- a/target/i386/kvm/kvm-cpu.c +++ b/target/i386/kvm/kvm-cpu.c @@ -57,13 +57,13 @@ static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) * * x86_cpu_realizefn(): * x86_cpu_expand_features() - * cpu_exec_realizefn(): + * cpu_common_realize(): * accel_cpu_common_realize() * kvm_cpu_realizefn() * host_cpu_realizefn() * kvm_set_guest_phys_bits() * check/update ucode_rev, phys_bits, guest_phys_bits, mwait - * cpu_common_realizefn() (via xcc->parent_realize) + * cpu_exec_realize() (via xcc->parent_realize) */ if (xcc->max_features) { if (enable_cpu_pm) { diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 4272b6770c..644c45fb0a 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -5022,7 +5022,7 @@ static int kvm_get_msrs(X86CPU *cpu) kvm_msr_entry_add(cpu, MSR_IA32_U_CET, 0); kvm_msr_entry_add(cpu, MSR_IA32_S_CET, 0); - if (env->features[FEAT_7_0_EDX] & CPUID_7_0_ECX_CET_SHSTK) { + if (env->features[FEAT_7_0_ECX] & CPUID_7_0_ECX_CET_SHSTK) { kvm_msr_entry_add(cpu, MSR_IA32_PL0_SSP, 0); kvm_msr_entry_add(cpu, MSR_IA32_PL1_SSP, 0); kvm_msr_entry_add(cpu, MSR_IA32_PL2_SSP, 0); diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index c9c3b05d5f..8294dbde3a 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -887,7 +887,7 @@ static int tdx_check_features(X86ConfidentialGuest *cg, CPUState *cs) FeatureWordInfo *wi; FeatureWord w; bool mismatch = false; - int r; + int r = -1; fetch_cpuid = tdx_fetch_cpuid(cs, &r); if (!fetch_cpuid) { diff --git a/target/i386/machine.c b/target/i386/machine.c index df0e0c178e..8d69d7e25e 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -10,6 +10,7 @@ #include "exec/watchpoint.h" #include "system/kvm.h" #include "system/kvm_xen.h" +#include "system/mshv.h" #include "system/tcg.h" #include "qemu/error-report.h" @@ -951,6 +952,47 @@ static const VMStateDescription vmstate_msr_hyperv_reenlightenment = { } }; +#ifdef CONFIG_MSHV + +static bool mshv_synthetic_timers_needed(void *opaque) +{ + /* Always migrate synthetic timers */ + return mshv_enabled(); +} + +static const VMStateDescription vmstate_mshv_synthetic_timers = { + .name = "cpu/mshv_synthetic_timers", + .version_id = 1, + .minimum_version_id = 1, + .needed = mshv_synthetic_timers_needed, + .fields = (const VMStateField[]) { + VMSTATE_BUFFER(env.hv_synthetic_timers_state, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + +static bool mshv_synic_vp_state_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + /* Only migrate SIMP/SIEFP if SynIC is enabled */ + return env->msr_hv_synic_control & 1; +} + +static const VMStateDescription vmstate_mshv_synic_vp_state = { + .name = "cpu/mshv_synic_vp_state", + .version_id = 1, + .minimum_version_id = 1, + .needed = mshv_synic_vp_state_needed, + .fields = (const VMStateField[]) { + VMSTATE_BUFFER(env.hv_simp_page, X86CPU), + VMSTATE_BUFFER(env.hv_siefp_page, X86CPU), + VMSTATE_END_OF_LIST() + } +}; +#endif + static bool avx512_needed(void *opaque) { X86CPU *cpu = opaque; @@ -1915,6 +1957,10 @@ const VMStateDescription vmstate_x86_cpu = { &vmstate_cet, #ifdef TARGET_X86_64 &vmstate_apx, +#endif +#ifdef CONFIG_MSHV + &vmstate_mshv_synic_vp_state, + &vmstate_mshv_synthetic_timers, #endif NULL } diff --git a/target/i386/mshv/meson.build b/target/i386/mshv/meson.build index 6091c21887..31ff4cc995 100644 --- a/target/i386/mshv/meson.build +++ b/target/i386/mshv/meson.build @@ -3,6 +3,7 @@ i386_mshv_ss = ss.source_set() i386_mshv_ss.add(files( 'mshv-cpu.c', 'msr.c', + 'synic.c', )) i386_system_ss.add_all(when: 'CONFIG_MSHV', if_true: i386_mshv_ss) diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index 1c433c408c..f528dd2b9a 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -21,7 +21,6 @@ #include "hw/hyperv/hvgdk.h" #include "hw/hyperv/hvgdk_mini.h" #include "hw/hyperv/hvhdk_mini.h" -#include "hw/i386/apic_internal.h" #include "cpu.h" #include "host-cpu.h" @@ -36,6 +35,11 @@ #include +#define MSHV_MP_STATE_RUNNABLE 0 +#define MSHV_MP_STATE_UNINITIALIZED 1 +#define MSHV_MP_STATE_INIT_RECEIVED 2 +#define MSHV_MP_STATE_HALTED 3 + #define MAX_REGISTER_COUNT (MAX_CONST(ARRAY_SIZE(STANDARD_REGISTER_NAMES), \ MAX_CONST(ARRAY_SIZE(SPECIAL_REGISTER_NAMES), \ ARRAY_SIZE(FPU_REGISTER_NAMES)))) @@ -112,6 +116,39 @@ static enum hv_register_name FPU_REGISTER_NAMES[26] = { static int set_special_regs(const CPUState *cpu); +static int get_synic_state(CPUState *cpu) +{ + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + int cpu_fd = mshv_vcpufd(cpu); + int ret; + + ret = mshv_get_synthetic_timers(cpu_fd, env->hv_synthetic_timers_state); + if (ret < 0) { + error_report("failed to get synthetic timers"); + return -1; + } + + /* SIMP/SIEFP can only be read when SynIC is enabled */ + if (!mshv_synic_enabled(cpu)) { + return 0; + } + + ret = mshv_get_simp(cpu_fd, env->hv_simp_page); + if (ret < 0) { + error_report("failed to get simp state"); + return -1; + } + + ret = mshv_get_siefp(cpu_fd, env->hv_siefp_page); + if (ret < 0) { + error_report("failed to get siefp state"); + return -1; + } + + return 0; +} + static int get_xsave_state(CPUState *cpu) { X86CPU *x86cpu = X86_CPU(cpu); @@ -918,6 +955,76 @@ static int set_vcpu_events(const CPUState *cpu) return 0; } +static int get_mp_state(CPUState *cpu) +{ + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + struct hv_register_assoc assoc = { + .name = HV_REGISTER_INTERNAL_ACTIVITY_STATE, + }; + union hv_internal_activity_register activity; + int ret; + + ret = mshv_get_generic_regs(cpu, &assoc, 1); + if (ret < 0) { + error_report("failed to get internal activity state"); + return -1; + } + + activity.as_uint64 = assoc.value.reg64; + + /* + * map MSHV activity state to KVM mp_state values, which are used as the + * shared representation in env->mp_state and serialized by vmstate_x86_cpu. + */ + + if (activity.startup_suspend) { + env->mp_state = MSHV_MP_STATE_UNINITIALIZED; + } else if (activity.halt_suspend) { + env->mp_state = MSHV_MP_STATE_HALTED; + } else { + env->mp_state = MSHV_MP_STATE_RUNNABLE; + } + + cpu->halted = (env->mp_state == MSHV_MP_STATE_HALTED); + + return 0; +} + +int mshv_arch_set_mp_state(const CPUState *cpu) +{ + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + union hv_internal_activity_register activity = { 0 }; + struct hv_register_assoc assoc = { + .name = HV_REGISTER_INTERNAL_ACTIVITY_STATE, + }; + int ret; + + switch (env->mp_state) { + case MSHV_MP_STATE_HALTED: + activity.halt_suspend = 1; + break; + case MSHV_MP_STATE_UNINITIALIZED: + case MSHV_MP_STATE_INIT_RECEIVED: + activity.startup_suspend = 1; + break; + case MSHV_MP_STATE_RUNNABLE: + default: + break; + } + + assoc.value.reg64 = activity.as_uint64; + + ret = mshv_set_generic_regs(cpu, &assoc, 1); + if (ret < 0) { + error_report("failed to set internal activity state"); + return -1; + } + + return 0; +} + static int update_hflags(CPUState *cpu) { X86CPU *x86cpu = X86_CPU(cpu); @@ -950,7 +1057,12 @@ int mshv_arch_load_vcpu_state(CPUState *cpu) return ret; } - ret = get_fpu(cpu); + ret = get_xsave_state(cpu); + if (ret < 0) { + return ret; + } + + ret = mshv_get_lapic(cpu); if (ret < 0) { return ret; } @@ -960,7 +1072,12 @@ int mshv_arch_load_vcpu_state(CPUState *cpu) return ret; } - ret = get_xsave_state(cpu); + ret = get_fpu(cpu); + if (ret < 0) { + return ret; + } + + ret = get_synic_state(cpu); if (ret < 0) { return ret; } @@ -970,6 +1087,11 @@ int mshv_arch_load_vcpu_state(CPUState *cpu) return ret; } + ret = get_mp_state(cpu); + if (ret < 0) { + return ret; + } + return 0; } @@ -1377,116 +1499,39 @@ static int set_xc_reg(const CPUState *cpu) return 0; } -static int get_vp_state(int cpu_fd, struct mshv_get_set_vp_state *state) +static int set_synic_state(const CPUState *cpu) { - int ret; - - ret = ioctl(cpu_fd, MSHV_GET_VP_STATE, state); - if (ret < 0) { - error_report("failed to get partition state: %s", strerror(errno)); - return -1; - } - - return 0; -} - -static int get_lapic(const CPUState *cpu, - struct hv_local_interrupt_controller_state *state) -{ - int ret; - size_t size = 4096; - /* buffer aligned to 4k, as *state requires that */ - void *buffer = qemu_memalign(size, size); - struct mshv_get_set_vp_state mshv_state = { 0 }; + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; int cpu_fd = mshv_vcpufd(cpu); + int ret; - mshv_state.buf_ptr = (uint64_t) buffer; - mshv_state.buf_sz = size; - mshv_state.type = MSHV_VP_STATE_LAPIC; - - ret = get_vp_state(cpu_fd, &mshv_state); - if (ret == 0) { - memcpy(state, buffer, sizeof(*state)); - } - qemu_vfree(buffer); + ret = mshv_set_synthetic_timers(cpu_fd, env->hv_synthetic_timers_state); if (ret < 0) { - error_report("failed to get lapic"); + error_report("failed to set synthetic timers state"); + return -1; + } + + /* SIMP/SIEFP can only be written when SynIC is enabled */ + if (!mshv_synic_enabled(cpu)) { + return 0; + } + + ret = mshv_set_simp(cpu_fd, env->hv_simp_page); + if (ret < 0) { + error_report("failed to set simp state"); + return -1; + } + + ret = mshv_set_siefp(cpu_fd, env->hv_siefp_page); + if (ret < 0) { + error_report("failed to set siefp state"); return -1; } return 0; } -static uint32_t set_apic_delivery_mode(uint32_t reg, uint32_t mode) -{ - return ((reg) & ~0x700) | ((mode) << 8); -} - -static int set_vp_state(int cpu_fd, const struct mshv_get_set_vp_state *state) -{ - int ret; - - ret = ioctl(cpu_fd, MSHV_SET_VP_STATE, state); - if (ret < 0) { - error_report("failed to set partition state: %s", strerror(errno)); - return -1; - } - - return 0; -} - -static int set_lapic(const CPUState *cpu, - const struct hv_local_interrupt_controller_state *state) -{ - int ret; - size_t size = 4096; - /* buffer aligned to 4k, as *state requires that */ - void *buffer = qemu_memalign(size, size); - struct mshv_get_set_vp_state mshv_state = { 0 }; - int cpu_fd = mshv_vcpufd(cpu); - - if (!state) { - error_report("lapic state is NULL"); - return -1; - } - memcpy(buffer, state, sizeof(*state)); - - mshv_state.buf_ptr = (uint64_t) buffer; - mshv_state.buf_sz = size; - mshv_state.type = MSHV_VP_STATE_LAPIC; - - ret = set_vp_state(cpu_fd, &mshv_state); - qemu_vfree(buffer); - if (ret < 0) { - error_report("failed to set lapic: %s", strerror(errno)); - return -1; - } - - return 0; -} - -static int init_lint(const CPUState *cpu) -{ - int ret; - uint32_t *lvt_lint0, *lvt_lint1; - - struct hv_local_interrupt_controller_state lapic_state = { 0 }; - ret = get_lapic(cpu, &lapic_state); - if (ret < 0) { - return ret; - } - - lvt_lint0 = &lapic_state.apic_lvt_lint0; - *lvt_lint0 = set_apic_delivery_mode(*lvt_lint0, APIC_DM_EXTINT); - - lvt_lint1 = &lapic_state.apic_lvt_lint1; - *lvt_lint1 = set_apic_delivery_mode(*lvt_lint1, APIC_DM_NMI); - - /* TODO: should we skip setting lapic if the values are the same? */ - - return set_lapic(cpu, &lapic_state); -} - int mshv_arch_store_vcpu_state(const CPUState *cpu) { int ret; @@ -1506,7 +1551,13 @@ int mshv_arch_store_vcpu_state(const CPUState *cpu) return ret; } - ret = set_fpu(cpu); + ret = set_xsave_state(cpu); + if (ret < 0) { + return ret; + } + + /* INVARIANT: special regs (APIC_BASE) must be restored before LAPIC */ + ret = mshv_set_lapic(cpu); if (ret < 0) { return ret; } @@ -1516,7 +1567,13 @@ int mshv_arch_store_vcpu_state(const CPUState *cpu) return ret; } - ret = set_xsave_state(cpu); + /* INVARIANT: legacy FPU state must be restored after XSAVE */ + ret = set_fpu(cpu); + if (ret < 0) { + return ret; + } + + ret = set_synic_state(cpu); if (ret < 0) { return ret; } @@ -1529,6 +1586,21 @@ int mshv_arch_store_vcpu_state(const CPUState *cpu) return 0; } +int mshv_arch_set_partition_msrs(const CPUState *cpu) +{ + CPUX86State *env = &X86_CPU(cpu)->env; + struct hv_register_assoc assocs[] = { + { .name = HV_REGISTER_GUEST_OS_ID, + .value.reg64 = env->msr_hv_guest_os_id }, + { .name = HV_REGISTER_REFERENCE_TSC, + .value.reg64 = env->msr_hv_tsc }, + { .name = HV_X64_REGISTER_HYPERCALL, + .value.reg64 = env->msr_hv_hypercall }, + }; + + return mshv_set_generic_regs(cpu, assocs, ARRAY_SIZE(assocs)); +} + void mshv_arch_amend_proc_features( union hv_partition_synthetic_processor_features *features) { @@ -2099,7 +2171,7 @@ void mshv_arch_init_vcpu(CPUState *cpu) ret = mshv_init_msrs(cpu); assert(ret == 0); - ret = init_lint(cpu); + ret = mshv_init_lint(cpu); assert(ret == 0); } @@ -2156,6 +2228,22 @@ uint32_t mshv_get_supported_cpuid(uint32_t func, uint32_t idx, int reg) */ ret &= ~CPUID_7_0_ECX_LA57; } + if (func == 0x07 && idx == 0 && reg == R_EDX) { + /* + * AMX TILE XSAVE state (XTILE_DATA) is 8KB, which exceeds the + * current fixed 4KB XSAVE buffer size. Filter until buffer + * sizing is computed dynamically from CPUID. + */ + ret &= ~CPUID_7_0_EDX_AMX_TILE; + ret &= ~CPUID_7_0_EDX_AMX_BF16; + ret &= ~CPUID_7_0_EDX_AMX_INT8; + } + if (func == 0x07 && idx == 1 && reg == R_EAX) { + ret &= ~CPUID_7_1_EAX_AMX_FP16; + } + if (func == 0x07 && idx == 1 && reg == R_EDX) { + ret &= ~CPUID_7_1_EDX_AMX_COMPLEX; + } return ret; } @@ -2230,6 +2318,33 @@ static void mshv_cpu_xsave_init(void) } } +int mshv_set_vp_state(int cpu_fd, const struct mshv_get_set_vp_state *state) +{ + int ret; + + ret = ioctl(cpu_fd, MSHV_SET_VP_STATE, state); + if (ret < 0) { + error_report("failed to set partition state: %s", strerror(errno)); + return -1; + } + + return 0; +} + + +int mshv_get_vp_state(int cpu_fd, struct mshv_get_set_vp_state *state) +{ + int ret; + + ret = ioctl(cpu_fd, MSHV_GET_VP_STATE, state); + if (ret < 0) { + error_report("failed to get partition state: %s", strerror(errno)); + return -1; + } + + return 0; +} + static void mshv_cpu_instance_init(CPUState *cs) { X86CPU *cpu = X86_CPU(cs); diff --git a/target/i386/mshv/msr.c b/target/i386/mshv/msr.c index 8c220a9942..d3788d7715 100644 --- a/target/i386/mshv/msr.c +++ b/target/i386/mshv/msr.c @@ -60,6 +60,8 @@ static const MshvMsrEnvMap msr_env_map[] = { offsetof(CPUX86State, tsc_aux) }, { IA32_MSR_TSC_ADJUST, HV_X64_REGISTER_TSC_ADJUST, offsetof(CPUX86State, tsc_adjust) }, + { IA32_MSR_TSC_DEADLINE, HV_X64_REGISTER_TSC_DEADLINE, + offsetof(CPUX86State, tsc_deadline) }, /* Hyper-V per-partition MSRs */ { HV_X64_MSR_HYPERCALL, HV_X64_REGISTER_HYPERCALL, @@ -329,6 +331,7 @@ int mshv_get_msrs(CPUState *cpu) struct hv_register_assoc assocs[MSHV_MSR_TOTAL_COUNT]; size_t i, j; uint32_t name; + X86CPU *x86cpu = X86_CPU(cpu); set_hv_name_in_assocs(assocs, n_assocs); @@ -355,6 +358,26 @@ int mshv_get_msrs(CPUState *cpu) store_in_env(cpu, assocs, n_assocs); + /* Read SINT MSRs only if SynIC is enabled */ + if (mshv_synic_enabled(cpu)) { + QEMU_BUILD_BUG_ON(MSHV_MSR_TOTAL_COUNT < HV_SINT_COUNT); + + for (i = 0; i < HV_SINT_COUNT; i++) { + assocs[i].name = HV_REGISTER_SINT0 + i; + } + + ret = mshv_get_generic_regs(cpu, assocs, HV_SINT_COUNT); + if (ret < 0) { + error_report("Failed to get SynIC SINT MSRs"); + return -errno; + } + + for (i = 0; i < HV_SINT_COUNT; i++) { + uint64_t hv_sint_value = assocs[i].value.reg64; + x86cpu->env.msr_hv_synic_sint[i] = hv_sint_value; + } + } + return 0; } @@ -389,6 +412,7 @@ int mshv_set_msrs(const CPUState *cpu) struct hv_register_assoc assocs[MSHV_MSR_TOTAL_COUNT]; int ret; size_t i, j; + X86CPU *x86cpu = X86_CPU(cpu); load_from_env(cpu, assocs, n_assocs); @@ -421,5 +445,21 @@ int mshv_set_msrs(const CPUState *cpu) return -errno; } + /* SINT MSRs can only be written if SCONTROL has been set, so we split */ + if (mshv_synic_enabled(cpu)) { + QEMU_BUILD_BUG_ON(MSHV_MSR_TOTAL_COUNT < HV_SINT_COUNT); + + for (i = 0; i < HV_SINT_COUNT; i++) { + assocs[i].name = HV_REGISTER_SINT0 + i; + assocs[i].value.reg64 = x86cpu->env.msr_hv_synic_sint[i]; + } + + ret = mshv_set_generic_regs(cpu, assocs, HV_SINT_COUNT); + if (ret < 0) { + error_report("Failed to set SynIC SINT MSRs"); + return -errno; + } + } + return 0; } diff --git a/target/i386/mshv/synic.c b/target/i386/mshv/synic.c new file mode 100644 index 0000000000..4c629adc3a --- /dev/null +++ b/target/i386/mshv/synic.c @@ -0,0 +1,206 @@ +/* + * QEMU MSHV SynIC support + * + * Copyright Microsoft, Corp. 2026 + * + * Authors: Magnus Kulke + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/memalign.h" +#include "qemu/error-report.h" + +#include "system/mshv.h" +#include "system/mshv_int.h" + +#include "linux/mshv.h" +#include "hw/hyperv/hvgdk_mini.h" +#include "cpu.h" + +#include + +bool mshv_synic_enabled(const CPUState *cpu) +{ + X86CPU *x86cpu = X86_CPU(cpu); + + return x86cpu->env.msr_hv_synic_control & 1; +} + +static int get_vp_state(int cpu_fd, struct mshv_get_set_vp_state *state) +{ + int ret; + + ret = ioctl(cpu_fd, MSHV_GET_VP_STATE, state); + if (ret < 0) { + error_report("failed to get vp state: %s", strerror(errno)); + return -1; + } + + return 0; +} + +static int set_vp_state(int cpu_fd, const struct mshv_get_set_vp_state *state) +{ + int ret; + + ret = ioctl(cpu_fd, MSHV_SET_VP_STATE, state); + if (ret < 0) { + error_report("failed to set vp state: %s", strerror(errno)); + return -1; + } + + return 0; +} + +int mshv_get_synthetic_timers(int cpu_fd, uint8_t *state) +{ + int ret; + void *buffer; + struct mshv_get_set_vp_state args = {0}; + + buffer = qemu_memalign(HV_HYP_PAGE_SIZE, HV_HYP_PAGE_SIZE); + args.buf_ptr = (uint64_t)buffer; + args.buf_sz = HV_HYP_PAGE_SIZE; + args.type = MSHV_VP_STATE_SYNTHETIC_TIMERS; + + ret = get_vp_state(cpu_fd, &args); + + if (ret < 0) { + qemu_vfree(buffer); + error_report("failed to get synthetic timers"); + return -1; + } + + memcpy(state, buffer, MSHV_STIMERS_STATE_SIZE); + qemu_vfree(buffer); + + return 0; +} + +int mshv_set_synthetic_timers(int cpu_fd, const uint8_t *state) +{ + int ret; + void *buffer; + struct mshv_get_set_vp_state args = {0}; + + buffer = qemu_memalign(HV_HYP_PAGE_SIZE, HV_HYP_PAGE_SIZE); + memset(buffer, 0, HV_HYP_PAGE_SIZE); + args.buf_ptr = (uint64_t)buffer; + args.buf_sz = HV_HYP_PAGE_SIZE; + args.type = MSHV_VP_STATE_SYNTHETIC_TIMERS; + + assert(state); + memcpy(buffer, state, MSHV_STIMERS_STATE_SIZE); + + ret = set_vp_state(cpu_fd, &args); + qemu_vfree(buffer); + + if (ret < 0) { + error_report("failed to set synthetic timers"); + return -1; + } + + return 0; +} + +int mshv_get_simp(int cpu_fd, uint8_t *page) +{ + int ret; + void *buffer; + struct mshv_get_set_vp_state args = {0}; + + buffer = qemu_memalign(HV_HYP_PAGE_SIZE, HV_HYP_PAGE_SIZE); + args.buf_ptr = (uint64_t)buffer; + args.buf_sz = HV_HYP_PAGE_SIZE; + args.type = MSHV_VP_STATE_SIMP; + + ret = get_vp_state(cpu_fd, &args); + + if (ret < 0) { + qemu_vfree(buffer); + error_report("failed to get simp"); + return -1; + } + + memcpy(page, buffer, HV_HYP_PAGE_SIZE); + qemu_vfree(buffer); + + return 0; +} + +int mshv_set_simp(int cpu_fd, const uint8_t *page) +{ + int ret; + void *buffer; + struct mshv_get_set_vp_state args = {0}; + + buffer = qemu_memalign(HV_HYP_PAGE_SIZE, HV_HYP_PAGE_SIZE); + args.buf_ptr = (uint64_t)buffer; + args.buf_sz = HV_HYP_PAGE_SIZE; + args.type = MSHV_VP_STATE_SIMP; + + assert(page); + memcpy(buffer, page, HV_HYP_PAGE_SIZE); + + ret = set_vp_state(cpu_fd, &args); + qemu_vfree(buffer); + + if (ret < 0) { + error_report("failed to set simp"); + return -1; + } + + return 0; +} + +int mshv_get_siefp(int cpu_fd, uint8_t *page) +{ + int ret; + void *buffer; + struct mshv_get_set_vp_state args = {0}; + + buffer = qemu_memalign(HV_HYP_PAGE_SIZE, HV_HYP_PAGE_SIZE); + args.buf_ptr = (uint64_t)buffer; + args.buf_sz = HV_HYP_PAGE_SIZE; + args.type = MSHV_VP_STATE_SIEFP, + + ret = get_vp_state(cpu_fd, &args); + + if (ret < 0) { + qemu_vfree(buffer); + error_report("failed to get siefp"); + return -1; + } + + memcpy(page, buffer, HV_HYP_PAGE_SIZE); + qemu_vfree(buffer); + + return 0; +} + +int mshv_set_siefp(int cpu_fd, const uint8_t *page) +{ + int ret; + void *buffer; + struct mshv_get_set_vp_state args = {0}; + + buffer = qemu_memalign(HV_HYP_PAGE_SIZE, HV_HYP_PAGE_SIZE); + args.buf_ptr = (uint64_t)buffer; + args.buf_sz = HV_HYP_PAGE_SIZE; + args.type = MSHV_VP_STATE_SIEFP, + + assert(page); + memcpy(buffer, page, HV_HYP_PAGE_SIZE); + + ret = set_vp_state(cpu_fd, &args); + qemu_vfree(buffer); + + if (ret < 0) { + error_report("failed to set simp"); + return -1; + } + + return 0; +} diff --git a/target/i386/sev.c b/target/i386/sev.c index debe6344ab..4d875d10ff 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -183,6 +183,7 @@ struct SevSnpGuestState { char *id_auth_base64; uint8_t *id_auth; char *host_data; + uint32_t tsc_khz; struct kvm_sev_snp_launch_start kvm_start_conf; struct kvm_sev_snp_launch_finish kvm_finish_conf; @@ -324,6 +325,20 @@ sev_set_guest_state(SevCommonState *sev_common, SevState new_state) sev_common->state = new_state; } +static bool is_sev_feature_set(SevCommonState *sev_common, uint64_t feature) +{ + return !!(sev_common->sev_features & feature); +} + +static void sev_set_feature(SevCommonState *sev_common, uint64_t feature, bool set) +{ + if (set) { + sev_common->sev_features |= feature; + } else { + sev_common->sev_features &= ~feature; + } +} + static void sev_ram_block_added(RAMBlockNotifier *n, void *host, size_t size, size_t max_size) @@ -348,8 +363,8 @@ sev_ram_block_added(RAMBlockNotifier *n, void *host, size_t size, trace_kvm_memcrypt_register_region(host, max_size); r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_REG_REGION, &range); if (r) { - error_report("%s: failed to register region (%p+%#zx) error '%s'", - __func__, host, max_size, strerror(errno)); + error_report("SEV: Failed to register region (%p+%#zx) error '%s'", + host, max_size, strerror(errno)); exit(1); } } @@ -378,8 +393,8 @@ sev_ram_block_removed(RAMBlockNotifier *n, void *host, size_t size, trace_kvm_memcrypt_unregister_region(host, max_size); r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_UNREG_REGION, &range); if (r) { - error_report("%s: failed to unregister region (%p+%#zx)", - __func__, host, max_size); + error_report("SEV: Failed to unregister region (%p+%#zx)", + host, max_size); } } @@ -492,35 +507,54 @@ static void sev_apply_cpu_context(CPUState *cpu) } } +/* + * Ensure SEV_FEATURES is configured for correct SEV hardware and that + * the requested features are supported. In addition, ensure feature + * dependencies are satisfied (allow tsc-frequency only if secure-tsc + * is also enabled, as an example). + */ static int check_sev_features(SevCommonState *sev_common, uint64_t sev_features, Error **errp) { - /* - * Ensure SEV_FEATURES is configured for correct SEV hardware and that - * the requested features are supported. If SEV-SNP is enabled then - * that feature must be enabled, otherwise it must be cleared. - */ - if (sev_snp_enabled() && !(sev_features & SVM_SEV_FEAT_SNP_ACTIVE)) { - error_setg( - errp, - "%s: SEV_SNP is enabled but is not enabled in VMSA sev_features", - __func__); - return -1; - } else if (!sev_snp_enabled() && - (sev_features & SVM_SEV_FEAT_SNP_ACTIVE)) { - error_setg( - errp, - "%s: SEV_SNP is not enabled but is enabled in VMSA sev_features", - __func__); + if (sev_features && !sev_es_enabled()) { + error_setg(errp, + "SEV: SEV features require either SEV-ES or SEV-SNP to be enabled"); return -1; } + if (sev_features & ~sev_common->supported_sev_features) { error_setg(errp, - "%s: VMSA contains unsupported sev_features: %lX, " + "SEV: VMSA contains unsupported sev_features: %lX, " "supported features: %lX", - __func__, sev_features, sev_common->supported_sev_features); + sev_features, sev_common->supported_sev_features); return -1; } + + if (sev_snp_enabled()) { + if (!(sev_features & SVM_SEV_FEAT_SNP_ACTIVE)) { + error_setg(errp, + "SEV: SEV_SNP is enabled but is not enabled in VMSA sev_features"); + return -1; + } + if (SEV_SNP_GUEST(sev_common)->tsc_khz && + !(sev_features & SVM_SEV_FEAT_SECURE_TSC)) { + error_setg(errp, + "SEV: TSC frequency can only be set if Secure TSC is enabled"); + return -1; + } + } else { + if (sev_features && sev_es_enabled()) { + error_setg(errp, + "SEV: SEV features are not supported with SEV-ES at this time"); + return -1; + } + if (sev_features & SVM_SEV_FEAT_SNP_ACTIVE) { + error_setg(errp, + "SEV: SEV_SNP is not enabled but is enabled in VMSA sev_features"); + return -1; + } + } + return 0; } @@ -539,8 +573,8 @@ static int check_vmsa_supported(SevCommonState *sev_common, hwaddr gpa, */ if (gpa != KVM_VMSA_GPA) { error_setg(errp, - "%s: The VMSA GPA must be %lX but is specified as %lX", - __func__, KVM_VMSA_GPA, gpa); + "SEV: The VMSA GPA must be %lX but is specified as %lX", + KVM_VMSA_GPA, gpa); return -1; } @@ -591,18 +625,14 @@ static int check_vmsa_supported(SevCommonState *sev_common, hwaddr gpa, vmsa_check.x87_fcw = 0; vmsa_check.mxcsr = 0; - if (check_sev_features(sev_common, vmsa_check.sev_features, errp) < 0) { - return -1; - } vmsa_check.sev_features = 0; if (!buffer_is_zero(&vmsa_check, sizeof(vmsa_check))) { error_setg(errp, - "%s: The VMSA contains fields that are not " + "SEV: The VMSA contains fields that are not " "synchronized with KVM. Continuing would result in " "either unpredictable guest behavior, or a " - "mismatched launch measurement.", - __func__); + "mismatched launch measurement."); return -1; } return 0; @@ -1072,11 +1102,24 @@ sev_snp_launch_start(SevCommonState *sev_common) return 1; } + if (is_sev_feature_set(sev_common, SVM_SEV_FEAT_SECURE_TSC) && + sev_snp_guest->tsc_khz) { + rc = -EINVAL; + if (kvm_check_extension(kvm_state, KVM_CAP_VM_TSC_CONTROL)) { + rc = kvm_vm_ioctl(kvm_state, KVM_SET_TSC_KHZ, sev_snp_guest->tsc_khz); + } + if (rc < 0) { + error_report("SEV: Unable to set Secure TSC frequency to %u kHz ret=%d", + sev_snp_guest->tsc_khz, rc); + return 1; + } + } + rc = sev_ioctl(sev_common->sev_fd, KVM_SEV_SNP_LAUNCH_START, start, &fw_error); if (rc < 0) { - error_report("%s: SNP_LAUNCH_START ret=%d fw_error=%d '%s'", - __func__, rc, fw_error, fw_error_to_str(fw_error)); + error_report("SEV: SNP_LAUNCH_START ret=%d fw_error=%d '%s'", + rc, fw_error, fw_error_to_str(fw_error)); return 1; } @@ -1118,8 +1161,8 @@ sev_launch_start(SevCommonState *sev_common) trace_kvm_sev_launch_start(start.policy, session, dh_cert); rc = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_START, &start, &fw_error); if (rc < 0) { - error_report("%s: LAUNCH_START ret=%d fw_error=%d '%s'", - __func__, ret, fw_error, fw_error_to_str(fw_error)); + error_report("SEV: LAUNCH_START ret=%d fw_error=%d '%s'", + ret, fw_error, fw_error_to_str(fw_error)); goto out; } @@ -1292,7 +1335,7 @@ static int sev_launch_update_data(SevCommonState *sev_common, hwaddr gpa, ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA, &update, &fw_error); if (ret) { - error_setg(errp, "%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'", __func__, + error_setg(errp, "SEV: LAUNCH_UPDATE ret=%d fw_error=%d '%s'", ret, fw_error, fw_error_to_str(fw_error)); } @@ -1317,8 +1360,8 @@ sev_launch_update_vmsa(SevGuestState *sev_guest) ret = sev_ioctl(SEV_COMMON(sev_guest)->sev_fd, KVM_SEV_LAUNCH_UPDATE_VMSA, NULL, &fw_error); if (ret) { - error_report("%s: LAUNCH_UPDATE_VMSA ret=%d fw_error=%d '%s'", - __func__, ret, fw_error, fw_error_to_str(fw_error)); + error_report("SEV: LAUNCH_UPDATE_VMSA ret=%d fw_error=%d '%s'", + ret, fw_error, fw_error_to_str(fw_error)); } return ret; @@ -1350,8 +1393,8 @@ sev_launch_get_measure(Notifier *notifier, void *unused) ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_MEASURE, &measurement, &error); if (!measurement.len) { - error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", - __func__, ret, error, fw_error_to_str(errno)); + error_report("SEV: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", + ret, error, fw_error_to_str(errno)); return; } @@ -1362,8 +1405,8 @@ sev_launch_get_measure(Notifier *notifier, void *unused) ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_MEASURE, &measurement, &error); if (ret) { - error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", - __func__, ret, error, fw_error_to_str(errno)); + error_report("SEV: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", + ret, error, fw_error_to_str(errno)); return; } @@ -1418,8 +1461,8 @@ sev_launch_finish(SevCommonState *sev_common) ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_FINISH, 0, &error); if (ret) { - error_report("%s: LAUNCH_FINISH ret=%d fw_error=%d '%s'", - __func__, ret, error, fw_error_to_str(error)); + error_report("SEV: LAUNCH_FINISH ret=%d fw_error=%d '%s'", + ret, error, fw_error_to_str(error)); exit(1); } @@ -1569,7 +1612,7 @@ snp_populate_metadata_pages(SevSnpGuestState *sev_snp, OvmfSevMetadataDesc *desc; int type, ret, i; void *hva; - MemoryRegion *mr = NULL; + g_autoptr(MemoryRegion) mr = NULL; for (i = 0; i < metadata->num_desc; i++) { desc = &metadata->descs[i]; @@ -1578,8 +1621,8 @@ snp_populate_metadata_pages(SevSnpGuestState *sev_snp, hva = gpa2hva(&mr, desc->base, desc->len, NULL); if (!hva) { - error_report("%s: Failed to get HVA for GPA 0x%x sz 0x%x", - __func__, desc->base, desc->len); + error_report("SEV: Failed to get HVA for GPA 0x%x sz 0x%x", + desc->base, desc->len); exit(1); } @@ -1595,8 +1638,8 @@ snp_populate_metadata_pages(SevSnpGuestState *sev_snp, } if (ret) { - error_report("%s: Failed to add metadata page gpa 0x%x+%x type %d", - __func__, desc->base, desc->len, desc->type); + error_report("SEV: Failed to add metadata page gpa 0x%x+%x type %d", + desc->base, desc->len, desc->type); exit(1); } } @@ -1625,7 +1668,7 @@ sev_snp_launch_finish(SevCommonState *sev_common) */ metadata = pc_system_get_ovmf_sev_metadata_ptr(); if (metadata == NULL) { - error_report("%s: Failed to locate SEV metadata header", __func__); + error_report("SEV: SNP_LAUNCH_FINISH failed to locate SEV metadata header"); exit(1); } @@ -1685,8 +1728,7 @@ sev_vm_state_change(void *opaque, bool running, RunState state) */ static bool sev_init2_required(SevGuestState *sev_guest) { - /* Currently no KVM_SEV_INIT2-specific options are exposed via QEMU */ - return false; + return !!SEV_COMMON(sev_guest)->sev_features; } static int sev_kvm_type(X86ConfidentialGuest *cg) @@ -1764,8 +1806,7 @@ static int sev_init_supported_features(ConfidentialGuestSupport *cgs, .addr = (unsigned long)&sev_common->supported_sev_features, }; if (kvm_ioctl(kvm_state, KVM_GET_DEVICE_ATTR, &attr) < 0) { - error_setg(errp, "%s: failed to query supported sev_features", - __func__); + error_setg(errp, "SEV: failed to query supported sev_features"); return -1; } if (sev_snp_enabled()) { @@ -1803,8 +1844,8 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) * comparison against the host value accomplishes that. */ if (host_cbitpos != sev_common->cbitpos) { - error_setg(errp, "%s: cbitpos check failed, host '%d' requested '%d'", - __func__, host_cbitpos, sev_common->cbitpos); + error_setg(errp, "SEV: cbitpos check failed, host '%d' requested '%d'", + host_cbitpos, sev_common->cbitpos); return -1; } @@ -1815,9 +1856,9 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) */ if (sev_common->reduced_phys_bits < 1 || sev_common->reduced_phys_bits > 63) { - error_setg(errp, "%s: reduced_phys_bits check failed," + error_setg(errp, "SEV: reduced_phys_bits check failed," " it should be in the range of 1 to 63, requested '%d'", - __func__, sev_common->reduced_phys_bits); + sev_common->reduced_phys_bits); return -1; } @@ -1833,8 +1874,8 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) ret = sev_platform_ioctl(sev_common->sev_fd, SEV_PLATFORM_STATUS, &status, &fw_error); if (ret) { - error_setg(errp, "%s: failed to get platform status ret=%d " - "fw_error='%d: %s'", __func__, ret, fw_error, + error_setg(errp, "SEV: failed to get platform status ret=%d " + "fw_error='%d: %s'", ret, fw_error, fw_error_to_str(fw_error)); return -1; } @@ -1844,17 +1885,15 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) if (sev_es_enabled()) { if (!kvm_kernel_irqchip_allowed()) { - error_setg(errp, "%s: SEV-ES guests require in-kernel irqchip" - "support", __func__); + error_setg(errp, "SEV: SEV-ES guests require in-kernel irqchip support"); return -1; } } if (sev_es_enabled() && !sev_snp_enabled()) { if (!(status.flags & SEV_STATUS_FLAGS_CONFIG_ES)) { - error_setg(errp, "%s: guest policy requires SEV-ES, but " - "host SEV-ES support unavailable", - __func__); + error_setg(errp, "SEV: guest policy requires SEV-ES, but " + "host SEV-ES support unavailable"); return -1; } } @@ -1895,38 +1934,50 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) * as SEV_STATE_UNINIT. */ if (x86machine->igvm) { + /* + * Test only the user-set SEV features by masking out + * SVM_SEV_FEAT_SNP_ACTIVE which is set by default. + */ + if (sev_common->sev_features & ~SVM_SEV_FEAT_SNP_ACTIVE) { + error_setg(errp, + "SEV: SEV features can't be specified when using IGVM files"); + return -1; + } if (IGVM_CFG_GET_CLASS(x86machine->igvm) ->process(x86machine->igvm, machine, true, errp) == -1) { return -1; } - /* - * KVM maintains a bitmask of allowed sev_features. This does not - * include SVM_SEV_FEAT_SNP_ACTIVE which is set accordingly by KVM - * itself. Therefore we need to clear this flag. - */ - args.vmsa_features = sev_common->sev_features & - ~SVM_SEV_FEAT_SNP_ACTIVE; } + if (check_sev_features(sev_common, sev_common->sev_features, errp) < 0) { + return -1; + } + + /* + * KVM maintains a bitmask of allowed sev_features. This does not + * include SVM_SEV_FEAT_SNP_ACTIVE which is set accordingly by KVM + * itself. Therefore we need to clear this flag. + */ + args.vmsa_features = sev_common->sev_features & ~SVM_SEV_FEAT_SNP_ACTIVE; + ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_INIT2, &args, &fw_error); break; } default: - error_setg(errp, "%s: host kernel does not support the requested SEV configuration.", - __func__); + error_setg(errp, "SEV: host kernel does not support the requested SEV configuration."); return -1; } if (ret) { - error_setg(errp, "%s: failed to initialize ret=%d fw_error=%d '%s'", - __func__, ret, fw_error, fw_error_to_str(fw_error)); + error_setg(errp, "SEV: failed to initialize ret=%d fw_error=%d '%s'", + ret, fw_error, fw_error_to_str(fw_error)); return -1; } ret = klass->launch_start(sev_common); if (ret) { - error_setg(errp, "%s: failed to create encryption context", __func__); + error_setg(errp, "SEV: failed to create encryption context"); return -1; } @@ -1954,7 +2005,7 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) */ ret = ram_block_discard_disable(true); if (ret) { - error_setg(errp, "%s: cannot disable RAM discard", __func__); + error_setg(errp, "SEV: cannot disable RAM discard"); return -1; } @@ -2063,7 +2114,7 @@ int sev_inject_launch_secret(const char *packet_hdr, const char *secret, int error, ret = 1; void *hva; gsize hdr_sz = 0, data_sz = 0; - MemoryRegion *mr = NULL; + g_autoptr(MemoryRegion) mr = NULL; SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); if (!sev_common) { @@ -2558,11 +2609,7 @@ static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, const struct sev_es_save_area *sa = (const struct sev_es_save_area *)ptr; if (len < sizeof(*sa)) { - error_setg(errp, "%s: invalid VMSA length encountered", - __func__); - return -1; - } - if (check_sev_features(sev_common, sa->sev_features, errp) < 0) { + error_setg(errp, "SEV: invalid VMSA length encountered"); return -1; } sev_common->sev_features = sa->sev_features; @@ -2571,8 +2618,8 @@ static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, } if (!sev_enabled()) { - error_setg(errp, "%s: attempt to configure guest memory, but SEV " - "is not enabled", __func__); + error_setg(errp, "SEV: attempt to configure guest memory, but SEV " + "is not enabled"); return -1; } @@ -2584,9 +2631,8 @@ static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, case CGS_PAGE_TYPE_VMSA: if (!sev_es_enabled()) { error_setg(errp, - "%s: attempt to configure initial VMSA, but SEV-ES " - "is not supported", - __func__); + "SEV: attempt to configure initial VMSA, but SEV-ES " + "is not supported"); return -1; } if (check_vmsa_supported(sev_common, gpa, @@ -2607,9 +2653,8 @@ static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, case CGS_PAGE_TYPE_SECRETS: if (!sev_snp_enabled()) { error_setg(errp, - "%s: attempt to configure secrets page, but SEV-SNP " - "is not supported", - __func__); + "SEV: attempt to configure secrets page, but SEV-SNP " + "is not supported"); return -1; } return snp_launch_update_data(gpa, ptr, len, @@ -2619,8 +2664,8 @@ static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, if (kvm_convert_memory(gpa, len, true) < 0) { error_setg( errp, - "%s: failed to configure required memory. gpa: %lX, type: %d", - __func__, gpa, memory_type); + "SEV: failed to configure required memory. gpa: %lX, type: %d", + gpa, memory_type); return -1; } return 0; @@ -2628,14 +2673,13 @@ static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, case CGS_PAGE_TYPE_CPUID: if (!sev_snp_enabled()) { error_setg(errp, - "%s: attempt to configure CPUID page, but SEV-SNP " - "is not supported", - __func__); + "SEV: attempt to configure CPUID page, but SEV-SNP " + "is not supported"); return -1; } return snp_launch_update_cpuid(gpa, ptr, len, errp); } - error_setg(errp, "%s: failed to update guest. gpa: %lX, type: %d", __func__, + error_setg(errp, "SEV: failed to update guest. gpa: %lX, type: %d", gpa, memory_type); return -1; } @@ -2691,8 +2735,8 @@ static int cgs_set_guest_policy(ConfidentialGuestPolicyType policy_type, } if (policy_type != GUEST_POLICY_SEV) { - error_setg(errp, "%s: Invalid guest policy type provided for SEV: %d", - __func__, policy_type); + error_setg(errp, "SEV: Invalid guest policy type provided for SEV: %d", + policy_type); return -1; } /* @@ -2725,14 +2769,12 @@ static int cgs_set_guest_policy(ConfidentialGuestPolicyType policy_type, (struct sev_snp_id_authentication *)policy_data2; if (policy_data1_size != KVM_SEV_SNP_ID_BLOCK_SIZE) { - error_setg(errp, "%s: Invalid SEV-SNP ID block: incorrect size", - __func__); + error_setg(errp, "SEV: Invalid SEV-SNP ID block: incorrect size"); return -1; } if (policy_data2_size != KVM_SEV_SNP_ID_AUTH_SIZE) { error_setg(errp, - "%s: Invalid SEV-SNP ID auth block: incorrect size", - __func__); + "SEV: Invalid SEV-SNP ID auth block: incorrect size"); return -1; } assert(policy_data1 != NULL); @@ -2762,8 +2804,8 @@ static int cgs_set_guest_policy(ConfidentialGuestPolicyType policy_type, SevGuestState *sev_guest = SEV_GUEST(MACHINE(qdev_get_machine())->cgs); /* Only the policy flags are supported for SEV and SEV-ES */ if ((policy_data1_size > 0) || (policy_data2_size > 0) || !sev_guest) { - error_setg(errp, "%s: An ID block/ID auth block has been provided " - "but SEV-SNP is not enabled", __func__); + error_setg(errp, "SEV: An ID block/ID auth block has been provided " + "but SEV-SNP is not enabled"); return -1; } @@ -2775,6 +2817,16 @@ static int cgs_set_guest_policy(ConfidentialGuestPolicyType policy_type, return 0; } +static bool sev_common_get_debug_swap(Object *obj, Error **errp) +{ + return is_sev_feature_set(SEV_COMMON(obj), SVM_SEV_FEAT_DEBUG_SWAP); +} + +static void sev_common_set_debug_swap(Object *obj, bool value, Error **errp) +{ + sev_set_feature(SEV_COMMON(obj), SVM_SEV_FEAT_DEBUG_SWAP, value); +} + static void sev_common_class_init(ObjectClass *oc, const void *data) { @@ -2800,6 +2852,11 @@ sev_common_class_init(ObjectClass *oc, const void *data) sev_common_set_kernel_hashes); object_class_property_set_description(oc, "kernel-hashes", "add kernel hashes to guest firmware for measured Linux boot"); + object_class_property_add_bool(oc, "debug-swap", + sev_common_get_debug_swap, + sev_common_set_debug_swap); + object_class_property_set_description(oc, "debug-swap", + "enable virtualization of debug registers"); } static void @@ -3164,6 +3221,38 @@ sev_snp_guest_set_host_data(Object *obj, const char *value, Error **errp) memcpy(finish->host_data, blob, len); } +static bool sev_snp_guest_get_secure_tsc(Object *obj, Error **errp) +{ + return is_sev_feature_set(SEV_COMMON(obj), SVM_SEV_FEAT_SECURE_TSC); +} + +static void sev_snp_guest_set_secure_tsc(Object *obj, bool value, Error **errp) +{ + sev_set_feature(SEV_COMMON(obj), SVM_SEV_FEAT_SECURE_TSC, value); +} + +static void +sev_snp_guest_get_tsc_frequency(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint32_t value = SEV_SNP_GUEST(obj)->tsc_khz * 1000; + + visit_type_uint32(v, name, &value, errp); +} + +static void +sev_snp_guest_set_tsc_frequency(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint32_t value; + + if (!visit_type_uint32(v, name, &value, errp)) { + return; + } + + SEV_SNP_GUEST(obj)->tsc_khz = value / 1000; +} + static void sev_snp_guest_class_init(ObjectClass *oc, const void *data) { @@ -3199,6 +3288,12 @@ sev_snp_guest_class_init(ObjectClass *oc, const void *data) object_class_property_add_str(oc, "host-data", sev_snp_guest_get_host_data, sev_snp_guest_set_host_data); + object_class_property_add_bool(oc, "secure-tsc", + sev_snp_guest_get_secure_tsc, + sev_snp_guest_set_secure_tsc); + object_class_property_add(oc, "tsc-frequency", "uint32", + sev_snp_guest_get_tsc_frequency, + sev_snp_guest_set_tsc_frequency, NULL, NULL); } static void @@ -3211,6 +3306,7 @@ sev_snp_guest_instance_init(Object *obj) /* default init/start/finish params for kvm */ sev_snp_guest->kvm_start_conf.policy = DEFAULT_SEV_SNP_POLICY; + sev_set_feature(SEV_COMMON(sev_snp_guest), SVM_SEV_FEAT_SNP_ACTIVE, true); } static void diff --git a/target/i386/sev.h b/target/i386/sev.h index 4358df40e4..7725f92e19 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -46,7 +46,9 @@ bool sev_snp_enabled(void); #define SEV_SNP_POLICY_SMT 0x10000 #define SEV_SNP_POLICY_DBG 0x80000 -#define SVM_SEV_FEAT_SNP_ACTIVE 1 +#define SVM_SEV_FEAT_SNP_ACTIVE BIT(0) +#define SVM_SEV_FEAT_DEBUG_SWAP BIT(5) +#define SVM_SEV_FEAT_SECURE_TSC BIT(9) typedef struct SevKernelLoaderContext { char *setup_data; diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index ac181308ca..9c429e113d 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -603,23 +603,33 @@ static void decode_0F77(DisasContext *s, CPUX86State *env, X86OpEntry *entry, ui static void decode_0F78(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) { - static const X86OpEntry opcodes_0F78[4] = { - {}, - X86_OP_ENTRY3(EXTRQ_i, V,x, None,None, I,w, cpuid(SSE4A)), /* AMD extension */ - {}, - X86_OP_ENTRY3(INSERTQ_i, V,x, U,x, I,w, cpuid(SSE4A)), /* AMD extension */ - }; - *entry = *decode_by_prefix(s, opcodes_0F78); + static const X86OpEntry opcodes_0F78_f2 = + X86_OP_ENTRY3(INSERTQ_i, V,x, U,x, I,w, cpuid(SSE4A)); /* AMD extension */ + static const X86OpEntry opcodes_0F78_66 = + X86_OP_ENTRY3(EXTRQ_i, U,x, None,None, I,w, cpuid(SSE4A)); /* AMD extension */ + + entry->gen = NULL; + if (s->prefix & PREFIX_REPNZ) { + *entry = opcodes_0F78_f2; + } else if (s->prefix & PREFIX_REPZ) { + /* undefined */ + } else if (s->prefix & PREFIX_DATA) { + int op = (get_modrm(s, env) >> 3) & 7; + if (op == 0) { + *entry = opcodes_0F78_66; + } + } } static void decode_0F79(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) { + entry->gen = NULL; if (s->prefix & PREFIX_REPNZ) { entry->gen = gen_INSERTQ_r; /* AMD extension */ + } else if (s->prefix & PREFIX_REPZ) { + /* undefined */ } else if (s->prefix & PREFIX_DATA) { entry->gen = gen_EXTRQ_r; /* AMD extension */ - } else { - entry->gen = NULL; }; } @@ -1614,6 +1624,7 @@ static void decode_group3(DisasContext *s, CPUX86State *env, X86OpEntry *entry, static const X86OpEntry opcodes_grp3[16] = { /* 0xf6 */ [0x00] = X86_OP_ENTRYrr(AND, E,b, I,b), + [0x01] = X86_OP_ENTRYrr(AND, E,b, I,b), [0x02] = X86_OP_ENTRY1(NOT, E,b, lock), [0x03] = X86_OP_ENTRY1(NEG, E,b, lock), [0x04] = X86_OP_ENTRYrr(MUL, E,b, 0,b, zextT0), @@ -1623,6 +1634,7 @@ static void decode_group3(DisasContext *s, CPUX86State *env, X86OpEntry *entry, /* 0xf7 */ [0x08] = X86_OP_ENTRYrr(AND, E,v, I,z), + [0x09] = X86_OP_ENTRYrr(AND, E,v, I,z), [0x0a] = X86_OP_ENTRY1(NOT, E,v, lock), [0x0b] = X86_OP_ENTRY1(NEG, E,v, lock), [0x0c] = X86_OP_ENTRYrr(MUL, E,v, 0,v, zextT0), @@ -2806,16 +2818,24 @@ static void disas_insn(DisasContext *s, CPUState *cpu) s->prefix |= PREFIX_LOCK; goto next_byte; case 0x2e: - s->override = R_CS; + if (!CODE64(s)) { + s->override = R_CS; + } goto next_byte; case 0x36: - s->override = R_SS; + if (!CODE64(s)) { + s->override = R_SS; + } goto next_byte; case 0x3e: - s->override = R_DS; + if (!CODE64(s)) { + s->override = R_DS; + } goto next_byte; case 0x26: - s->override = R_ES; + if (!CODE64(s)) { + s->override = R_ES; + } goto next_byte; case 0x64: s->override = R_FS; diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 83c889cc3b..473f415766 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -3424,7 +3424,9 @@ static void gen_rot_overflow(X86DecodedInsn *decode, TCGv result, TCGv old, /* * RCx operations are invariant modulo 8*operand_size+1. For 8 and 16-bit operands, * this is less than 0x1f (the mask applied by gen_shift_count) so reduce further. - * FIXME: are flags updated if the count is nonzero, but a multiple of (8 << op) + 1? + * A count that is a nonzero multiple of (8 << op) + 1 reduces to 0 and leaves + * CF and OF unmodified (confirmed against hardware that rcl $9,%al behaves + * exactly like rcl $0,%al). */ static MemOp gen_rotc_count(DisasContext *s, X86DecodedInsn *decode, bool *can_be_zero, TCGv *count, int unit) @@ -3778,10 +3780,13 @@ static void gen_SAHF(DisasContext *s, X86DecodedInsn *decode) return gen_illegal_opcode(s); } tcg_gen_shri_tl(s->T0, cpu_regs[R_EAX], 8); - gen_neg_setcc(s, JCC_O << 1, cpu_cc_src); - tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, CC_O); + gen_neg_setcc(s, JCC_O << 1, s->T1); + tcg_gen_andi_tl(s->T1, s->T1, CC_O); tcg_gen_andi_tl(s->T0, s->T0, CC_S | CC_Z | CC_A | CC_P | CC_C); - tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, s->T0); + tcg_gen_or_tl(s->T0, s->T0, s->T1); + + decode->cc_src = s->T0; + decode->cc_op = CC_OP_EFLAGS; } static void gen_SALC(DisasContext *s, X86DecodedInsn *decode) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 978eb1411b..b812125efa 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -550,12 +550,11 @@ static const int fcomi_ccval[4] = {CC_C, CC_Z, 0, CC_Z | CC_P | CC_C}; void helper_fcomi_ST0_FT0(CPUX86State *env) { int old_flags = save_exception_flags(env); - int eflags; FloatRelation ret; ret = floatx80_compare(ST0, FT0, &env->fp_status); - eflags = cpu_cc_compute_all(env) & ~(CC_Z | CC_P | CC_C); - CC_SRC = eflags | fcomi_ccval[ret + 1]; + /* OF, SF, and AF are unconditionally cleared to 0 */ + CC_SRC = fcomi_ccval[ret + 1]; CC_OP = CC_OP_EFLAGS; merge_exception_flags(env, old_flags); } @@ -563,12 +562,11 @@ void helper_fcomi_ST0_FT0(CPUX86State *env) void helper_fucomi_ST0_FT0(CPUX86State *env) { int old_flags = save_exception_flags(env); - int eflags; FloatRelation ret; ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status); - eflags = cpu_cc_compute_all(env) & ~(CC_Z | CC_P | CC_C); - CC_SRC = eflags | fcomi_ccval[ret + 1]; + /* OF, SF, and AF are unconditionally cleared to 0 */ + CC_SRC = fcomi_ccval[ret + 1]; CC_OP = CC_OP_EFLAGS; merge_exception_flags(env, old_flags); } diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index 58aac72011..6d424349a5 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -1096,17 +1096,22 @@ void helper_sysret(CPUX86State *env, int dflag) selector = (env->star >> 48) & 0xffff; #ifdef TARGET_X86_64 if (env->hflags & HF_LMA_MASK) { - cpu_load_eflags(env, (uint32_t)(env->regs[11]), TF_MASK | AC_MASK - | ID_MASK | IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | - NT_MASK); if (dflag == 2) { + uint64_t new_rip = env->regs[R_ECX]; + if (IS_INTEL_CPU(env)) { + int shift = (get_pg_mode(env) & PG_MODE_LA57) ? 56 : 47; + int64_t sext = (int64_t)new_rip >> shift; + if (sext != 0 && sext != -1) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); + } + } cpu_x86_load_seg_cache(env, R_CS, (selector + 16) | 3, 0, 0xffffffff, DESC_G_MASK | DESC_P_MASK | DESC_S_MASK | (3 << DESC_DPL_SHIFT) | DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK); - env->eip = env->regs[R_ECX]; + env->eip = new_rip; } else { cpu_x86_load_seg_cache(env, R_CS, selector | 3, 0, 0xffffffff, @@ -1120,6 +1125,10 @@ void helper_sysret(CPUX86State *env, int dflag) DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | (3 << DESC_DPL_SHIFT) | DESC_W_MASK | DESC_A_MASK); + + cpu_load_eflags(env, (uint32_t)(env->regs[11]), TF_MASK | AC_MASK + | ID_MASK | IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | + NT_MASK); } else #endif { @@ -2068,7 +2077,8 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, new_cs = popl(&sa) & 0xffff; if (is_iret) { new_eflags = popl(&sa); - if (new_eflags & VM_MASK) { + bool allow_vm86 = (cpl == 0) && !(env->hflags & HF_LMA_MASK); + if ((new_eflags & VM_MASK) && allow_vm86) { goto return_to_vm86; } } diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index 634d542821..113f79afb9 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -115,7 +115,7 @@ static const WHV_REGISTER_NAME whpx_register_names[] = { #ifdef TARGET_X86_64 WHvX64RegisterKernelGsBase, #endif - /* WHvX64RegisterPat, */ + WHvX64RegisterPat, WHvX64RegisterSysenterCs, WHvX64RegisterSysenterEip, WHvX64RegisterSysenterEsp, @@ -614,9 +614,9 @@ void whpx_set_registers(CPUState *cpu, WHPXStateLevel level) if (whpx_is_xsave_enabled(cpu)) { whpx_set_xsave_state(cpu); - } else { - whpx_set_legacy_fp_registers(cpu, level); } + whpx_set_legacy_fp_registers(cpu, level); + /* MSRs */ assert(whpx_register_names[idx] == WHvX64RegisterEfer); vcxt.values[idx++].Reg64 = env->efer; @@ -624,9 +624,8 @@ void whpx_set_registers(CPUState *cpu, WHPXStateLevel level) assert(whpx_register_names[idx] == WHvX64RegisterKernelGsBase); vcxt.values[idx++].Reg64 = env->kernelgsbase; #endif - - /* WHvX64RegisterPat - Skipped */ - + assert(whpx_register_names[idx] == WHvX64RegisterPat); + vcxt.values[idx++].Reg64 = env->pat; assert(whpx_register_names[idx] == WHvX64RegisterSysenterCs); vcxt.values[idx++].Reg64 = env->sysenter_cs; assert(whpx_register_names[idx] == WHvX64RegisterSysenterEip); @@ -951,9 +950,8 @@ void whpx_get_registers(CPUState *cpu, WHPXStateLevel level) if (whpx_is_xsave_enabled(cpu)) { whpx_get_xsave_state(cpu); - } else { - whpx_get_legacy_fp_registers(cpu, level); } + whpx_get_legacy_fp_registers(cpu, level); /* MSRs */ assert(whpx_register_names[idx] == WHvX64RegisterEfer); @@ -962,9 +960,8 @@ void whpx_get_registers(CPUState *cpu, WHPXStateLevel level) assert(whpx_register_names[idx] == WHvX64RegisterKernelGsBase); env->kernelgsbase = vcxt.values[idx++].Reg64; #endif - - /* WHvX64RegisterPat - Skipped */ - + assert(whpx_register_names[idx] == WHvX64RegisterPat); + env->pat = vcxt.values[idx++].Reg64; assert(whpx_register_names[idx] == WHvX64RegisterSysenterCs); env->sysenter_cs = vcxt.values[idx++].Reg64; assert(whpx_register_names[idx] == WHvX64RegisterSysenterEip); @@ -1082,6 +1079,31 @@ static void whpx_inject_back_gpf(CPUState *cpu) whpx_set_reg(cpu, WHvRegisterPendingEvent, reg); } +static void whpx_inject_back_db(CPUState *cpu) +{ + WHV_VP_EXCEPTION_CONTEXT *ctx = &cpu->accel->exit_ctx.VpException; + WHV_REGISTER_VALUE reg = {}; + + if (ctx->ExceptionInfo.SoftwareException) { + /* TODO */ + warn_report("Was asked to inject software exception."); + return; + } + + if (ctx->ExceptionType != EXCP01_DB) { + warn_report("Was asked to inject exception other than debug."); + return; + } + + reg.ExceptionEvent.EventPending = 1; + reg.ExceptionEvent.EventType = WHvX64PendingEventException; + reg.ExceptionEvent.DeliverErrorCode = ctx->ExceptionInfo.ErrorCodeValid; + reg.ExceptionEvent.Vector = ctx->ExceptionType; + reg.ExceptionEvent.ErrorCode = ctx->ErrorCode; + reg.ExceptionEvent.ExceptionParameter = ctx->ExceptionParameter; + whpx_set_reg(cpu, WHvRegisterPendingEvent, reg); +} + static void handle_io(CPUState *env, uint16_t port, void *buffer, int direction, int size, int count) { @@ -2649,11 +2671,7 @@ int whpx_vcpu_run(CPUState *cpu) } else if ((vcpu->exit_ctx.VpException.ExceptionType == WHvX64ExceptionTypeDebugTrapOrFault) && !cpu_single_stepping(cpu)) { - /* - * Just finished stepping over a breakpoint, but the - * gdb does not expect us to do single-stepping. - * Don't do anything special. - */ + whpx_inject_back_db(cpu); cpu->exception_index = EXCP_INTERRUPT; } else { /* Another exception or debug event. Report it to GDB. */ @@ -3247,6 +3265,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms) synthetic_features.Bank0.HypervisorPresent = 1; synthetic_features.Bank0.Hv1 = 1; + synthetic_features.Bank0.FastHypercallOutput = 1; synthetic_features.Bank0.AccessVpRunTimeReg = 1; synthetic_features.Bank0.AccessPartitionReferenceCounter = 1; synthetic_features.Bank0.AccessPartitionReferenceTsc = 1; diff --git a/target/i386/whpx/whpx-cpu-legacy.c b/target/i386/whpx/whpx-cpu-legacy.c index d341e6f4fd..3fb5ed79e3 100644 --- a/target/i386/whpx/whpx-cpu-legacy.c +++ b/target/i386/whpx/whpx-cpu-legacy.c @@ -113,7 +113,7 @@ uint32_t whpx_get_supported_cpuid_legacy(uint32_t func, uint32_t idx, if (idx == 0) { eax = supported_xcr0; } else if (idx == 1) { - eax &= CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XGETBV1; + eax &= CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XGETBV1 | CPUID_XSAVE_XSAVES; if (!whpx_has_xsaves()) { eax &= ~CPUID_XSAVE_XSAVES; } diff --git a/target/i386/xsave_helper.c b/target/i386/xsave_helper.c index 625bae103a..666a281002 100644 --- a/target/i386/xsave_helper.c +++ b/target/i386/xsave_helper.c @@ -332,7 +332,7 @@ int decompact_xsave_area(const void *buf, size_t buflen, CPUX86State *env) size_t i; uint32_t eax, ebx, ecx, edx; uint32_t size, dst_off; - bool align64; + bool align64, supervisor; uint64_t guest_xcr0, *xstate_bv; compacted_xstate_bv = *(uint64_t *)(buf + XSAVE_XSTATE_BV_OFFSET); @@ -382,7 +382,8 @@ int decompact_xsave_area(const void *buf, size_t buflen, CPUX86State *env) size = eax; dst_off = ebx; - align64 = (ecx & (1u << 1)) != 0; + align64 = (ecx & ESA_FEATURE_ALIGN64_MASK) != 0; + supervisor = (ecx & ESA_FEATURE_XSS_MASK) != 0; /* Component is in the layout but unknown to the guest CPUID model */ if (size == 0) { @@ -393,7 +394,7 @@ int decompact_xsave_area(const void *buf, size_t buflen, CPUX86State *env) */ host_cpuid(0xD, i, &eax, &ebx, &ecx, &edx); size = eax; - align64 = (ecx & (1u << 1)) != 0; + align64 = (ecx & ESA_FEATURE_ALIGN64_MASK) != 0; if (size == 0) { error_report("xsave component %zu: size unknown to both " "guest and host CPUID", i); @@ -433,8 +434,14 @@ int decompact_xsave_area(const void *buf, size_t buflen, CPUX86State *env) return -E2BIG; } - /* Copy components marked present in XSTATE_BV to guest model */ - if (((compacted_xstate_bv >> i) & 1) != 0) { + /* + * Copy components marked present in XSTATE_BV to guest model. + * + * NB: Supervisor state is skipped b/c there is no slot in the + * standard format XSAVE buffer (CET state is migrated via MSRs, + * others supervisor state isn't migrated). + */ + if (((compacted_xstate_bv >> i) & 1) != 0 && !supervisor) { memcpy(env->xsave_buf + dst_off, buf + xsave_offset, size); } @@ -523,7 +530,7 @@ int compact_xsave_area(CPUX86State *env, void *buf, size_t buflen) host_cpuid(0xD, i, &eax, &ebx, &ecx, &edx); size = eax; src_off = ebx; - align64 = (ecx >> 1) & 1; + align64 = (ecx & ESA_FEATURE_ALIGN64_MASK) != 0; if (size == 0) { /* Component in host xcr0 but unknown - shouldn't happen */ diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index fb03424ffa..bd819247c7 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -29,6 +29,7 @@ #include #endif #include "tcg/tcg_loongarch.h" +#include "disas/capstone.h" const char * const regnames[32] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", @@ -449,10 +450,10 @@ static void loongarch_max_initfn(Object *obj) loongarch_la464_initfn(obj); cpu->ptw = ON_OFF_AUTO_AUTO; + if (kvm_enabled()){ + cpu->msgint=ON_OFF_AUTO_OFF; + } if (tcg_enabled()) { - cpu->env.cpucfg[1] = FIELD_DP32(cpu->env.cpucfg[1], CPUCFG1, MSG_INT, 1); - cpu->msgint = ON_OFF_AUTO_AUTO; - uint32_t data = cpu->env.cpucfg[2]; data = FIELD_DP32(data, CPUCFG2, HPTW, 1); /* Enable LA v1.1 instructions */ @@ -466,6 +467,8 @@ static void loongarch_max_initfn(Object *obj) data = cpu->env.cpucfg[3]; data = FIELD_DP32(data, CPUCFG3, DBAR_HINTS, 1); cpu->env.cpucfg[3] = data; + cpu->env.cpucfg[1] = FIELD_DP32(cpu->env.cpucfg[1], CPUCFG1, MSG_INT, 1); + cpu->msgint = ON_OFF_AUTO_AUTO; } } @@ -694,8 +697,15 @@ static void loongarch_cpu_reset_hold(Object *obj, ResetType type) static void loongarch_cpu_disas_set_info(const CPUState *cs, disassemble_info *info) { + CPULoongArchState *env = cpu_env((CPUState *)cs); + info->endian = BFD_ENDIAN_LITTLE; info->print_insn = print_insn_loongarch; + + info->cap_arch = CS_ARCH_LOONGARCH; + info->cap_insn_unit = 4; + info->cap_insn_split = 4; + info->cap_mode = is_la64(env) ? CS_MODE_LOONGARCH64 : CS_MODE_LOONGARCH32; } static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) @@ -704,7 +714,7 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(dev); Error *local_err = NULL; - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index ad30c73167..97db3b453f 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -301,6 +301,7 @@ enum loongarch_features { LOONGARCH_FEATURE_PV_IPI, LOONGARCH_FEATURE_STEALTIME, LOONGARCH_FEATURE_PTW, + LOONGARCH_FEATURE_MSGINT, }; typedef struct LoongArchBT { diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index 4d0cad5732..15082a01f8 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -36,6 +36,13 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_LAST_INFO }; +static bool kvm_cpu_has_msgint(CPUState *cs) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + + return FIELD_EX64(cpu->env.cpucfg[1], CPUCFG1, MSG_INT); +} + static int kvm_get_stealtime(CPUState *cs) { CPULoongArchState *env = cpu_env(cs); @@ -362,6 +369,25 @@ static int kvm_loongarch_get_csr(CPUState *cs) ret |= kvm_loongarch_get_pmu(cs); + /* + * CSR register MSGIS getting must be put after CSR register CSR_ESTAT, + * Since register CSR_ESTAT will sync software pending MSGINT status to + * hardware register and modify HW CSR MSGIS registers. + */ + if (kvm_cpu_has_msgint(cs)) { + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_MSGIS(0)), + &sys->CSR_MSGIS[0]); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_MSGIS(1)), + &sys->CSR_MSGIS[1]); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_MSGIS(2)), + &sys->CSR_MSGIS[2]); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_MSGIS(3)), + &sys->CSR_MSGIS[3]); + } + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TVAL), &sys->CSR_TVAL); @@ -538,6 +564,20 @@ static int kvm_loongarch_put_csr(CPUState *cs, KvmPutState level) ret |= kvm_loongarch_put_pmu(cs); + if (kvm_cpu_has_msgint(cs)) { + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_MSGIS(0)), + &sys->CSR_MSGIS[0]); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_MSGIS(1)), + &sys->CSR_MSGIS[1]); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_MSGIS(2)), + &sys->CSR_MSGIS[2]); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_MSGIS(3)), + &sys->CSR_MSGIS[3]); + } + /* * timer cfg must be put at last since it is used to enable * guest timer @@ -993,6 +1033,12 @@ static bool kvm_feature_supported(CPUState *cs, enum loongarch_features feature) ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); return (ret == 0); + case LOONGARCH_FEATURE_MSGINT: + attr.group = KVM_LOONGARCH_VM_FEAT_CTRL; + attr.attr = KVM_LOONGARCH_VM_FEAT_MSGINT; + ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); + return (ret == 0); + default: return false; } @@ -1166,6 +1212,28 @@ static int kvm_cpu_check_pv_features(CPUState *cs, Error **errp) return 0; } +static int kvm_cpu_check_msgint(CPUState *cs, Error **errp) +{ + CPULoongArchState *env = cpu_env(cs); + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + bool kvm_supported; + + kvm_supported = kvm_feature_supported(cs, LOONGARCH_FEATURE_MSGINT); + env->cpucfg[1] = FIELD_DP32(env->cpucfg[1], CPUCFG1, MSG_INT, 0); + if (cpu->msgint == ON_OFF_AUTO_ON) { + if (kvm_supported) { + env->cpucfg[1] = FIELD_DP32(env->cpucfg[1], CPUCFG1, MSG_INT, 1); + } else { + error_setg(errp, "'msgint' feature not supported by KVM on this host"); + return -ENOTSUP; + } + } else if ((cpu->msgint == ON_OFF_AUTO_AUTO) && kvm_supported) { + env->cpucfg[1] = FIELD_DP32(env->cpucfg[1], CPUCFG1, MSG_INT, 1); + } + + return 0; +} + int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp) { return 0; @@ -1221,6 +1289,12 @@ int kvm_arch_init_vcpu(CPUState *cs) return ret; } + ret = kvm_cpu_check_msgint(cs, &local_err); + if (ret < 0) { + error_report_err(local_err); + return ret; + } + return 0; } diff --git a/target/loongarch/tcg/constant_timer.c b/target/loongarch/tcg/constant_timer.c index f56e76d482..b91ad1de69 100644 --- a/target/loongarch/tcg/constant_timer.c +++ b/target/loongarch/tcg/constant_timer.c @@ -22,12 +22,18 @@ uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu) uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu) { + CPULoongArchState *env = &cpu->env; + CPUSysState *sys = env_sys(env); uint64_t now, expire; - now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - expire = timer_expire_time_ns(&cpu->timer); + if ((sys->CSR_TCFG & CONSTANT_TIMER_ENABLE) && + (sys->CSR_TVAL < sys->CSR_TCFG)) { + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + expire = timer_expire_time_ns(&cpu->timer); + sys->CSR_TVAL = (expire - now) / TIMER_PERIOD; + } - return (expire - now) / TIMER_PERIOD; + return sys->CSR_TVAL; } void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu, @@ -42,8 +48,10 @@ void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); next = now + (value & CONSTANT_TIMER_TICK_MASK) * TIMER_PERIOD; timer_mod(&cpu->timer, next); + sys->CSR_TVAL = sys->CSR_TCFG & CONSTANT_TIMER_TICK_MASK; } else { timer_del(&cpu->timer); + sys->CSR_TVAL = 0; } } @@ -58,8 +66,9 @@ void loongarch_constant_timer_cb(void *opaque) now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); next = now + (sys->CSR_TCFG & CONSTANT_TIMER_TICK_MASK) * TIMER_PERIOD; timer_mod(&cpu->timer, next); + sys->CSR_TVAL = sys->CSR_TCFG & CONSTANT_TIMER_TICK_MASK; } else { - sys->CSR_TCFG = FIELD_DP64(sys->CSR_TCFG, CSR_TCFG, EN, 0); + sys->CSR_TVAL = CONSTANT_TIMER_TICK_MASK; } loongarch_cpu_set_irq(opaque, IRQ_TIMER, 1); diff --git a/target/loongarch/tcg/helper.h b/target/loongarch/tcg/helper.h index 8a6c62f116..e76c73c775 100644 --- a/target/loongarch/tcg/helper.h +++ b/target/loongarch/tcg/helper.h @@ -5,10 +5,6 @@ DEF_HELPER_2(raise_exception, noreturn, env, i32) -DEF_HELPER_FLAGS_1(bitrev_w, TCG_CALL_NO_RWG_SE, tl, tl) -DEF_HELPER_FLAGS_1(bitrev_d, TCG_CALL_NO_RWG_SE, tl, tl) -DEF_HELPER_FLAGS_1(bitswap, TCG_CALL_NO_RWG_SE, tl, tl) - DEF_HELPER_FLAGS_3(asrtle_d, TCG_CALL_NO_WG, void, env, tl, tl) DEF_HELPER_FLAGS_3(asrtgt_d, TCG_CALL_NO_WG, void, env, tl, tl) diff --git a/target/loongarch/tcg/insn_trans/trans_bit.c.inc b/target/loongarch/tcg/insn_trans/trans_bit.c.inc index ee5fa003ce..b91bb7eaa6 100644 --- a/target/loongarch/tcg/insn_trans/trans_bit.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_bit.c.inc @@ -178,6 +178,11 @@ static void gen_masknez(TCGv dest, TCGv src1, TCGv src2) tcg_gen_movcond_tl(TCG_COND_NE, dest, src2, zero, zero, src1); } +static void gen_bitrev_w(TCGv dest, TCGv src) +{ + tcg_gen_revbit32_tl(dest, src, TCG_BSWAP_OS); +} + TRANS(ext_w_h, ALL, gen_rr, EXT_NONE, EXT_NONE, tcg_gen_ext16s_tl) TRANS(ext_w_b, ALL, gen_rr, EXT_NONE, EXT_NONE, tcg_gen_ext8s_tl) TRANS(clo_w, ALL, gen_rr, EXT_NONE, EXT_NONE, gen_clo_w) @@ -194,10 +199,10 @@ TRANS(revb_2w, 64, gen_rr, EXT_NONE, EXT_NONE, gen_revb_2w) TRANS(revb_d, 64, gen_rr, EXT_NONE, EXT_NONE, tcg_gen_bswap64_i64) TRANS(revh_2w, 64, gen_rr, EXT_NONE, EXT_NONE, gen_revh_2w) TRANS(revh_d, 64, gen_rr, EXT_NONE, EXT_NONE, gen_revh_d) -TRANS(bitrev_4b, ALL, gen_rr, EXT_ZERO, EXT_SIGN, gen_helper_bitswap) -TRANS(bitrev_8b, 64, gen_rr, EXT_NONE, EXT_NONE, gen_helper_bitswap) -TRANS(bitrev_w, ALL, gen_rr, EXT_NONE, EXT_SIGN, gen_helper_bitrev_w) -TRANS(bitrev_d, 64, gen_rr, EXT_NONE, EXT_NONE, gen_helper_bitrev_d) +TRANS(bitrev_4b, ALL, gen_rr, EXT_NONE, EXT_SIGN, tcg_gen_revbit8_i64) +TRANS(bitrev_8b, 64, gen_rr, EXT_NONE, EXT_NONE, tcg_gen_revbit8_i64) +TRANS(bitrev_w, ALL, gen_rr, EXT_NONE, EXT_NONE, gen_bitrev_w) +TRANS(bitrev_d, 64, gen_rr, EXT_NONE, EXT_NONE, tcg_gen_revbit64_i64) TRANS(maskeqz, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_maskeqz) TRANS(masknez, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_masknez) TRANS(bytepick_w, ALL, gen_rrr_sa, EXT_NONE, EXT_NONE, gen_bytepick_w) diff --git a/target/loongarch/tcg/insn_trans/trans_branch.c.inc b/target/loongarch/tcg/insn_trans/trans_branch.c.inc index f94c1f37ab..da07778658 100644 --- a/target/loongarch/tcg/insn_trans/trans_branch.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_branch.c.inc @@ -66,6 +66,8 @@ static bool gen_cz_bc(DisasContext *ctx, arg_c_offs *a, TCGCond cond) TCGv src1 = tcg_temp_new(); TCGv src2 = tcg_constant_tl(0); + CHECK_FPE; + tcg_gen_ld8u_tl(src1, tcg_env, offsetof(CPULoongArchState, cf[a->cj])); gen_bc(ctx, src1, src2, a->offs, cond); diff --git a/target/loongarch/tcg/op_helper.c b/target/loongarch/tcg/op_helper.c index e63ac66daa..f41f0cb1e6 100644 --- a/target/loongarch/tcg/op_helper.c +++ b/target/loongarch/tcg/op_helper.c @@ -22,27 +22,6 @@ void helper_raise_exception(CPULoongArchState *env, uint32_t exception) do_raise_exception(env, exception, GETPC()); } -target_ulong helper_bitrev_w(target_ulong rj) -{ - return (int32_t)revbit32(rj); -} - -target_ulong helper_bitrev_d(target_ulong rj) -{ - return revbit64(rj); -} - -target_ulong helper_bitswap(target_ulong v) -{ - v = ((v >> 1) & (target_ulong)0x5555555555555555ULL) | - ((v & (target_ulong)0x5555555555555555ULL) << 1); - v = ((v >> 2) & (target_ulong)0x3333333333333333ULL) | - ((v & (target_ulong)0x3333333333333333ULL) << 2); - v = ((v >> 4) & (target_ulong)0x0F0F0F0F0F0F0F0FULL) | - ((v & (target_ulong)0x0F0F0F0F0F0F0F0FULL) << 4); - return v; -} - /* loongarch assert op */ void helper_asrtle_d(CPULoongArchState *env, target_ulong rj, target_ulong rk) { diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 9ad3057d91..6012dc3186 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -22,7 +22,7 @@ #include "accel/tcg/cpu-ops.h" #include "fpu/softfloat.h" #include "qapi/error.h" - +#include "disas/capstone.h" #ifndef CONFIG_USER_ONLY #include "migration/vmstate.h" #include "monitor/hmp.h" @@ -182,9 +182,72 @@ static void m68k_cpu_reset_hold(Object *obj, ResetType type) static void m68k_cpu_disas_set_info(const CPUState *cs, disassemble_info *info) { + CPUM68KState *env = cpu_env((CPUState *)cs); + int cap_mode = 0; + info->print_insn = print_insn_m68k; info->endian = BFD_ENDIAN_BIG; info->mach = 0; + + /* m68k support in capstone prior to v6 is unusably bad */ + if (CS_API_MAJOR < 6) { + return; + } + + info->cap_arch = CS_ARCH_M68K; + info->cap_insn_unit = 2; + + if (m68k_feature(env, M68K_FEATURE_M68K)) { + /* + * m68k insns may have up to 11 words, and 5 words are common. + * Choosing 12 bytes splits after 6 words and disassembly + * uses no more than 2 lines. + */ + info->cap_insn_split = 12; + if (m68k_feature(env, M68K_FEATURE_M68060)) { + cap_mode = CS_MODE_M68K_060; + } else if (m68k_feature(env, M68K_FEATURE_M68040)) { + cap_mode = CS_MODE_M68K_040; + } else if (m68k_feature(env, M68K_FEATURE_M68030)) { + cap_mode = CS_MODE_M68K_030; + } else if (m68k_feature(env, M68K_FEATURE_M68020)) { + cap_mode = CS_MODE_M68K_020; + } else if (m68k_feature(env, M68K_FEATURE_M68010)) { + cap_mode = CS_MODE_M68K_010; + } else { + cap_mode = CS_MODE_M68K_000; + } + } else { + /* ColdFire insns may have up to 3 words. */ + info->cap_insn_split = 6; + if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) { + cap_mode |= CS_MODE_M68K_CF_ISA_A; + cap_mode |= CS_MODE_M68K_CF_DIV; + } + if (m68k_feature(env, M68K_FEATURE_CF_ISA_B)) { + cap_mode |= CS_MODE_M68K_CF_ISA_B; + } + if (m68k_feature(env, M68K_FEATURE_CF_ISA_APLUSC)) { + cap_mode |= CS_MODE_M68K_CF_ISA_A_PLUS; + cap_mode |= CS_MODE_M68K_CF_ISA_C; + } + if (m68k_feature(env, M68K_FEATURE_USP)) { + cap_mode |= CS_MODE_M68K_CF_USP; + } + if (m68k_feature(env, M68K_FEATURE_CF_FPU)) { + cap_mode |= CS_MODE_M68K_CF_FPU; + } + if (m68k_feature(env, M68K_FEATURE_CF_MAC)) { + cap_mode |= CS_MODE_M68K_CF_MAC; + } + if (m68k_feature(env, M68K_FEATURE_CF_EMAC)) { + cap_mode |= CS_MODE_M68K_CF_EMAC; + } + if (m68k_feature(env, M68K_FEATURE_CF_EMAC_B)) { + cap_mode |= CS_MODE_M68K_CF_EMAC_B; + } + } + info->cap_mode = cap_mode; } /* CPU models */ @@ -389,7 +452,7 @@ static void m68k_cpu_realizefn(DeviceState *dev, Error **errp) register_m68k_insns(&cpu->env); - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index a97c92a7b6..389a5124b1 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -251,7 +251,7 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) int i = 0; Error *local_err = NULL; - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/mips/cpu.c b/target/mips/cpu.c index d72044aef6..0fead20d65 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -30,6 +30,7 @@ #include "system/qtest.h" #include "hw/core/qdev-properties.h" #include "hw/core/qdev-clock.h" +#include "disas/capstone.h" #include "fpu_helper.h" #ifndef CONFIG_USER_ONLY #include "semihosting/semihost.h" @@ -488,15 +489,66 @@ static void mips_cpu_disas_set_info(const CPUState *cs, disassemble_info *info) { const MIPSCPU *cpu = MIPS_CPU(cs); const CPUMIPSState *env = &cpu->env; + bool is64 = env->hflags & MIPS_HFLAG_64; + int cap_mode = 0; - if (!(env->insn_flags & ISA_NANOMIPS32)) { + if (env->insn_flags & ISA_NANOMIPS32) { + info->print_insn = print_insn_nanomips; + info->endian = BFD_ENDIAN_LITTLE; + /* nanomips has 16, 32 and 48-bit insns */ + info->cap_insn_unit = 2; + info->cap_insn_split = 6; + cap_mode = CS_MODE_NANOMIPS; + } else { info->endian = TARGET_BIG_ENDIAN ? BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE; info->print_insn = TARGET_BIG_ENDIAN ? print_insn_big_mips : print_insn_little_mips; - } else { - info->print_insn = print_insn_nanomips; - info->endian = BFD_ENDIAN_LITTLE; + + if (env->hflags & MIPS_HFLAG_M16) { + cap_mode = (env->insn_flags & ASE_MICROMIPS + ? CS_MODE_MICRO : CS_MODE_MIPS16); + /* Beware unsupported capstone mode */ + if (cap_mode == 0) { + return; + } + info->cap_insn_unit = 2; + } else { + info->cap_insn_unit = 4; + } + info->cap_insn_split = 4; + + cap_mode |= is64 ? CS_MODE_MIPS64 : CS_MODE_MIPS32; + if (env->insn_flags & ISA_MIPS_R6) { + cap_mode |= is64 ? CS_MODE_MIPS64R6 : CS_MODE_MIPS32R6; + } else if (env->insn_flags & ISA_MIPS_R5) { + cap_mode |= is64 ? CS_MODE_MIPS64R5 : CS_MODE_MIPS32R5; + } else if (env->insn_flags & ISA_MIPS_R3) { + cap_mode |= is64 ? CS_MODE_MIPS64R3 : CS_MODE_MIPS32R3; + } else if (env->insn_flags & ISA_MIPS_R2) { + cap_mode |= is64 ? CS_MODE_MIPS64R2 : CS_MODE_MIPS32R2; + } else if (env->insn_flags & ISA_MIPS5) { + cap_mode |= CS_MODE_MIPS5; + } else if (env->insn_flags & ISA_MIPS4) { + cap_mode |= CS_MODE_MIPS4; + } else if (env->insn_flags & ISA_MIPS3) { + cap_mode |= CS_MODE_MIPS3; + } else if (env->insn_flags & ISA_MIPS2) { + cap_mode |= CS_MODE_MIPS2; + } else if (env->insn_flags & ISA_MIPS1) { + cap_mode |= CS_MODE_MIPS1; + } + if (env->insn_flags & INSN_OCTEON) { + cap_mode |= CS_MODE_OCTEON | CS_MODE_OCTEONP; + } + if (is64 && !(env->hflags & MIPS_HFLAG_AWRAP)) { + cap_mode |= CS_MODE_MIPS_PTR64; + } + } + /* Beware unsupported capstone mode */ + if (cap_mode) { + info->cap_arch = CS_ARCH_MIPS; + info->cap_mode = cap_mode; } } @@ -544,7 +596,7 @@ static void mips_cpu_realizefn(DeviceState *dev, Error **errp) } mips_cp0_period_set(cpu); - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/mips/helper.h b/target/mips/helper.h index 786117813a..779b87101d 100644 --- a/target/mips/helper.h +++ b/target/mips/helper.h @@ -27,10 +27,6 @@ DEF_HELPER_FLAGS_4(rotx, TCG_CALL_NO_RWG_SE, tl, tl, i32, i32, i32) /* Octeon COP2 selector operation helpers. */ DEF_HELPER_1(octeon_cp2_mf_crc_iv_reflect, i64, env) -DEF_HELPER_1(octeon_cp2_mf_gfm_mul_reflect0, i64, env) -DEF_HELPER_1(octeon_cp2_mf_gfm_mul_reflect1, i64, env) -DEF_HELPER_1(octeon_cp2_mf_gfm_resinp_reflect0, i64, env) -DEF_HELPER_1(octeon_cp2_mf_gfm_resinp_reflect1, i64, env) DEF_HELPER_2(octeon_cp2_mt_crc_write_iv_reflect, void, env, i64) DEF_HELPER_2(octeon_cp2_mt_crc_write_polynomial_reflect, void, env, i64) DEF_HELPER_2(octeon_cp2_mt_crc_write_byte, void, env, i64) @@ -43,9 +39,6 @@ DEF_HELPER_2(octeon_cp2_mt_crc_write_dword, void, env, i64) DEF_HELPER_2(octeon_cp2_mt_crc_write_var, void, env, i64) DEF_HELPER_2(octeon_cp2_mt_crc_write_dword_reflect, void, env, i64) DEF_HELPER_2(octeon_cp2_mt_crc_write_var_reflect, void, env, i64) -DEF_HELPER_2(octeon_cp2_mt_gfm_mul_reflect0, void, env, i64) -DEF_HELPER_2(octeon_cp2_mt_gfm_mul_reflect1, void, env, i64) -DEF_HELPER_2(octeon_cp2_mt_gfm_xor0_reflect, void, env, i64) DEF_HELPER_2(octeon_cp2_mt_gfm_xormul1_reflect, void, env, i64) DEF_HELPER_2(octeon_cp2_mt_gfm_xormul1, void, env, i64) DEF_HELPER_1(octeon_cp2_mt_sha3_startop, void, env) diff --git a/target/mips/tcg/octeon_crypto.c b/target/mips/tcg/octeon_crypto.c index fbf80be2a5..118397e632 100644 --- a/target/mips/tcg/octeon_crypto.c +++ b/target/mips/tcg/octeon_crypto.c @@ -2127,41 +2127,6 @@ uint64_t helper_octeon_cp2_mf_crc_iv_reflect(CPUMIPSState *env) return octeon_crc_reflect32_by_byte(env->octeon_crypto.crc_iv); } -uint64_t helper_octeon_cp2_mf_gfm_mul_reflect0(CPUMIPSState *env) -{ - return revbit64(env->octeon_crypto.gfm_mul[0]); -} - -uint64_t helper_octeon_cp2_mf_gfm_mul_reflect1(CPUMIPSState *env) -{ - return revbit64(env->octeon_crypto.gfm_mul[1]); -} - -uint64_t helper_octeon_cp2_mf_gfm_resinp_reflect0(CPUMIPSState *env) -{ - return revbit64(env->octeon_crypto.gfm_resinp[0]); -} - -uint64_t helper_octeon_cp2_mf_gfm_resinp_reflect1(CPUMIPSState *env) -{ - return revbit64(env->octeon_crypto.gfm_resinp[1]); -} - -void helper_octeon_cp2_mt_gfm_mul_reflect0(CPUMIPSState *env, uint64_t value) -{ - env->octeon_crypto.gfm_mul[0] = revbit64(value); -} - -void helper_octeon_cp2_mt_gfm_mul_reflect1(CPUMIPSState *env, uint64_t value) -{ - env->octeon_crypto.gfm_mul[1] = revbit64(value); -} - -void helper_octeon_cp2_mt_gfm_xor0_reflect(CPUMIPSState *env, uint64_t value) -{ - env->octeon_crypto.gfm_resinp[0] ^= revbit64(value); -} - static void octeon_gfm_xormul1_common(MIPSOcteonCryptoState *crypto, uint64_t value) { diff --git a/target/mips/tcg/octeon_translate.c b/target/mips/tcg/octeon_translate.c index a0db6630c7..b689adb46b 100644 --- a/target/mips/tcg/octeon_translate.c +++ b/target/mips/tcg/octeon_translate.c @@ -28,6 +28,8 @@ TRANS(NAME, trans_octeon_cp2_mf_hsh_pair, \ OCTEON_CRYPTO_OFFSET(FIELD[2 * (INDEX)]), \ OCTEON_CRYPTO_OFFSET(FIELD[2 * (INDEX) + 1])) +#define CP2_MF_REFLECT(NAME, FIELD) \ + TRANS(NAME, trans_octeon_cp2_mf_reflect, OCTEON_CRYPTO_OFFSET(FIELD)) #define CP2_MF_HELPER(NAME, SUFFIX) \ TRANS(NAME, trans_octeon_cp2_mf_helper, \ gen_helper_octeon_cp2_mf_ ## SUFFIX) @@ -44,6 +46,8 @@ TRANS(NAME, trans_octeon_cp2_mt_hsh_pair, \ OCTEON_CRYPTO_OFFSET(FIELD[2 * (INDEX)]), \ OCTEON_CRYPTO_OFFSET(FIELD[2 * (INDEX) + 1])) +#define CP2_MT_REFLECT(NAME, FIELD) \ + TRANS(NAME, trans_octeon_cp2_mt_reflect, OCTEON_CRYPTO_OFFSET(FIELD)) #define CP2_MT_HELPER(NAME, SUFFIX) \ TRANS(NAME, trans_octeon_cp2_mt_helper, \ gen_helper_octeon_cp2_mt_ ## SUFFIX) @@ -110,6 +114,17 @@ static bool trans_octeon_cp2_mf_hsh_pair(DisasContext *ctx, arg_cp2 *a, return true; } +static bool trans_octeon_cp2_mf_reflect(DisasContext *ctx, arg_cp2 *a, + int offset) +{ + TCGv_i64 value = tcg_temp_new_i64(); + + tcg_gen_ld_i64(value, tcg_env, offset); + tcg_gen_revbit64_i64(value, value); + gen_store_gpr(value, a->rt); + return true; +} + static bool trans_octeon_cp2_mf_helper(DisasContext *ctx, arg_cp2 *a, void (*gen_helper)(TCGv_i64, TCGv_env)) { @@ -183,6 +198,17 @@ static bool trans_octeon_cp2_mt_xor_i64(DisasContext *ctx, arg_cp2 *a, return true; } +static bool trans_octeon_cp2_mt_reflect(DisasContext *ctx, arg_cp2 *a, + int offset) +{ + TCGv_i64 value = tcg_temp_new_i64(); + + gen_load_gpr(value, a->rt); + tcg_gen_revbit64_i64(value, value); + tcg_gen_st_i64(value, tcg_env, offset); + return true; +} + static bool trans_octeon_cp2_mt_helper(DisasContext *ctx, arg_cp2 *a, void (*gen_helper)(TCGv_env, TCGv_i64)) { @@ -200,6 +226,17 @@ static bool trans_octeon_cp2_mt_helper_env(DisasContext *ctx, arg_cp2 *a, return true; } +static void gen_helper_octeon_cp2_mt_gfm_xor0_reflect(TCGv_env t_env, + TCGv_i64 value) +{ + TCGv_i64 resinp = tcg_temp_new_i64(); + + tcg_gen_revbit64_i64(value, value); + tcg_gen_ld_i64(resinp, t_env, OCTEON_CRYPTO_OFFSET(gfm_resinp[0])); + tcg_gen_xor_i64(resinp, resinp, value); + tcg_gen_st_i64(resinp, t_env, OCTEON_CRYPTO_OFFSET(gfm_resinp[0])); +} + CP2_MF_HSH_PAIR(CVM_MF_HSH_DAT0, hsh_dat, 0); CP2_MF_HSH_PAIR(CVM_MF_HSH_DAT1, hsh_dat, 1); CP2_MF_HSH_PAIR(CVM_MF_HSH_DAT2, hsh_dat, 2); @@ -241,10 +278,10 @@ CP2_MF_I64(CVM_MF_LLM_DATA1, llm_data[1]); CP2_MF_HELPER(CVM_MF_CRC_IV_REFLECT, crc_iv_reflect); CP2_MF_I64(CVM_MF_SHA3_DAT24, sha3_dat24); -CP2_MF_HELPER(CVM_MF_GFM_MUL_REFLECT0, gfm_mul_reflect0); -CP2_MF_HELPER(CVM_MF_GFM_MUL_REFLECT1, gfm_mul_reflect1); -CP2_MF_HELPER(CVM_MF_GFM_RESINP_REFLECT0, gfm_resinp_reflect0); -CP2_MF_HELPER(CVM_MF_GFM_RESINP_REFLECT1, gfm_resinp_reflect1); +CP2_MF_REFLECT(CVM_MF_GFM_MUL_REFLECT0, gfm_mul[0]) +CP2_MF_REFLECT(CVM_MF_GFM_MUL_REFLECT1, gfm_mul[1]) +CP2_MF_REFLECT(CVM_MF_GFM_RESINP_REFLECT0, gfm_resinp[0]) +CP2_MF_REFLECT(CVM_MF_GFM_RESINP_REFLECT1, gfm_resinp[1]) CP2_MF_I64(CVM_MF_HSH_DATW0, hsh_dat[0]); CP2_MF_I64(CVM_MF_HSH_DATW1, hsh_dat[1]); CP2_MF_I64(CVM_MF_HSH_DATW2, hsh_dat[2]); @@ -281,8 +318,8 @@ CP2_MT_HSH_PAIR(CVM_MT_HSH_IV0, hsh_iv, 0); CP2_MT_HSH_PAIR(CVM_MT_HSH_IV1, hsh_iv, 1); CP2_MT_HSH_PAIR(CVM_MT_HSH_IV2, hsh_iv, 2); CP2_MT_HSH_PAIR(CVM_MT_HSH_IV3, hsh_iv, 3); -CP2_MT_HELPER(CVM_MT_GFM_MUL_REFLECT0, gfm_mul_reflect0); -CP2_MT_HELPER(CVM_MT_GFM_MUL_REFLECT1, gfm_mul_reflect1); +CP2_MT_REFLECT(CVM_MT_GFM_MUL_REFLECT0, gfm_mul[0]); +CP2_MT_REFLECT(CVM_MT_GFM_MUL_REFLECT1, gfm_mul[1]); CP2_MT_HELPER(CVM_MT_GFM_XOR0_REFLECT, gfm_xor0_reflect); CP2_MT_I64(CVM_MT_3DES_KEY0, des3_key[0]); CP2_MT_I64(CVM_MT_3DES_KEY1, des3_key[1]); diff --git a/target/or1k/cpu.c b/target/or1k/cpu.c index ea29b2e01f..66c00c0930 100644 --- a/target/or1k/cpu.c +++ b/target/or1k/cpu.c @@ -170,7 +170,7 @@ static void openrisc_cpu_realizefn(DeviceState *dev, Error **errp) OpenRISCCPUClass *occ = OPENRISC_CPU_GET_CLASS(dev); Error *local_err = NULL; - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index f404c7e549..e2533481d8 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -6921,7 +6921,7 @@ static void ppc_cpu_realize(DeviceState *dev, Error **errp) PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); Error *local_err = NULL; - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; @@ -6956,7 +6956,7 @@ static void ppc_cpu_realize(DeviceState *dev, Error **errp) return; unrealize: - cpu_exec_unrealizefn(cs); + cpu_common_unrealize(cs); } static void ppc_cpu_unrealize(DeviceState *dev) @@ -7130,7 +7130,7 @@ static gint ppc_cpu_list_compare(gconstpointer a, gconstpointer b, gpointer d) } else if (pcc_a->pvr > pcc_b->pvr) { return 1; } else { - return 0; + return strcmp(name_a, name_b); } } } diff --git a/target/ppc/helper.h b/target/ppc/helper.h index e99c8c824b..6b2d19a3ad 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -54,7 +54,6 @@ DEF_HELPER_FLAGS_2(dcbzl, TCG_CALL_NO_WG, void, env, tl) #endif 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) DEF_HELPER_4(DIVDEU, i64, env, i64, i64, i32) diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index 119dc1df23..787ba2cdec 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -367,34 +367,6 @@ void helper_icbiep(CPUPPCState *env, target_ulong addr) #endif } -/* XXX: to be tested */ -target_ulong helper_lscbx(CPUPPCState *env, target_ulong addr, uint32_t reg, - uint32_t ra, uint32_t rb) -{ - int i, c, d; - - d = 24; - for (i = 0; i < xer_bc; i++) { - c = cpu_ldub_data_ra(env, addr, GETPC()); - addr = addr_add(env, addr, 1); - /* ra (if not 0) and rb are never modified */ - if (likely(reg != rb && (ra == 0 || reg != ra))) { - env->gpr[reg] = (env->gpr[reg] & ~(0xFF << d)) | (c << d); - } - if (unlikely(c == xer_cmp)) { - break; - } - if (likely(d != 0)) { - d -= 8; - } else { - d = 24; - reg++; - reg = reg & 0x1F; - } - } - return i; -} - /*****************************************************************************/ /* Altivec extension helpers */ #if HOST_BIG_ENDIAN diff --git a/target/ppc/mmu-book3s-v3.c b/target/ppc/mmu-book3s-v3.c index 3865556310..f329a7a0f2 100644 --- a/target/ppc/mmu-book3s-v3.c +++ b/target/ppc/mmu-book3s-v3.c @@ -23,24 +23,37 @@ #include "mmu-hash64.h" #include "mmu-book3s-v3.h" +#define PPC64_V3_PATE_SIZE 16 /* two 64-bit words */ + 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; + uint64_t table_size; + uint64_t entries; + + /* + * The POWER9 Processor User's Manual, section 4.9.4, specifies that + * POWER9 ignores PTCR[PATS] and only supports a 64 KiB partition table. + */ + if (cpu->env.excp_model == POWERPC_EXCP_POWER9) { + pats = 4; + } + table_size = 1ULL << (pats + 12); /* Check if partition table is properly aligned */ - if (patb & MAKE_64BIT_MASK(0, pats + 12)) { + if (patb & (table_size - 1)) { return false; } /* Calculate number of entries */ - pats = 1ull << (pats + 12 - 4); - if (pats <= lpid) { + entries = table_size / PPC64_V3_PATE_SIZE; + if (entries <= lpid) { return false; } /* Grab entry */ - patb += 16 * lpid; + patb += PPC64_V3_PATE_SIZE * 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_common.c b/target/ppc/mmu_common.c index 2499e619f8..2a36817181 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -42,24 +42,39 @@ void ppc_store_sdr1(CPUPPCState *env, target_ulong value) PowerPCCPU *cpu = env_archcpu(env); qemu_log_mask(CPU_LOG_MMU, "%s: " TARGET_FMT_lx "\n", __func__, value); assert(!cpu->env.has_hv_mode || !cpu->vhyp); -#if defined(TARGET_PPC64) if (mmu_is_64bit(env->mmu_model)) { +#if defined(TARGET_PPC64) target_ulong sdr_mask = SDR_64_HTABORG | SDR_64_HTABSIZE; target_ulong htabsize = value & SDR_64_HTABSIZE; if (value & ~sdr_mask) { qemu_log_mask(LOG_GUEST_ERROR, "Invalid bits 0x"TARGET_FMT_lx - " set in SDR1", value & ~sdr_mask); + " set in SDR1\n", value & ~sdr_mask); value &= sdr_mask; } if (htabsize > 28) { qemu_log_mask(LOG_GUEST_ERROR, "Invalid HTABSIZE 0x" TARGET_FMT_lx - " stored in SDR1", htabsize); + " stored in SDR1\n", htabsize); + return; + } +#endif /* defined(TARGET_PPC64) */ + } else { + target_ulong sdr_mask = SDR_32_HTABORG | SDR_32_HTABMASK; + target_ulong htabmask = value & SDR_32_HTABMASK; + + if (value & ~sdr_mask) { + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid bits 0x" TARGET_FMT_lx + " set in SDR1\n", value & ~sdr_mask); + value &= sdr_mask; + } + if ((htabmask & (htabmask + 1)) != 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid HTABMASK 0x" TARGET_FMT_lx + " in SDR1 (must be of form 2^n-1)\n", htabmask); return; } } -#endif /* defined(TARGET_PPC64) */ - /* FIXME: Should check for valid HTABMASK values in 32-bit case */ env->spr[SPR_SDR1] = value; } diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 5a82e6563b..a13177b7b7 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -39,6 +39,7 @@ #include "system/tcg.h" #include "kvm/kvm_riscv.h" #include "tcg/tcg-cpu.h" +#include "disas/capstone.h" #if !defined(CONFIG_USER_ONLY) #include "target/riscv/tcg/debug.h" #endif @@ -164,7 +165,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zicboz, PRIV_VERSION_1_12_0, ext_zicboz), ISA_INTERNAL_EXT_DATA_ENTRY(ziccamoa, PRIV_VERSION_1_11_0, has_priv_1_11), ISA_INTERNAL_EXT_DATA_ENTRY(ziccif, PRIV_VERSION_1_11_0, has_priv_1_11), - ISA_INTERNAL_EXT_DATA_ENTRY(zicclsm, PRIV_VERSION_1_11_0, has_priv_1_11), + ISA_EXT_DATA_ENTRY(zicclsm, PRIV_VERSION_1_11_0, ext_zicclsm), ISA_EXT_DATA_ENTRY(ziccrse, PRIV_VERSION_1_11_0, ext_ziccrse), ISA_EXT_DATA_ENTRY(zicfilp, PRIV_VERSION_1_12_0, ext_zicfilp), ISA_EXT_DATA_ENTRY(zicfiss, PRIV_VERSION_1_13_0, ext_zicfiss), @@ -650,6 +651,9 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; + bool rv32 = riscv_cpu_is_32bit(cpu); + int width = rv32 ? 8 : 16; + uint64_t mask = rv32 ? UINT32_MAX : UINT64_MAX; int i, j; uint8_t *p; @@ -664,7 +668,7 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) qemu_fprintf(f, " %-13s %d\n", "elp", env->elp); } #endif - qemu_fprintf(f, " %-13s %" PRIx64 "\n", "pc", env->pc); + qemu_fprintf(f, " %-13s %0*" PRIx64 "\n", "pc", width, env->pc & mask); #if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) for (i = 0; i < ARRAY_SIZE(csr_ops); i++) { int csrno = i; @@ -691,8 +695,8 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) #endif for (i = 0; i < 32; i++) { - qemu_fprintf(f, " %-8s %" PRIx64, - riscv_int_regnames[i], env->gpr[i]); + qemu_fprintf(f, " %-8s %0*" PRIx64, + riscv_int_regnames[i], width, env->gpr[i] & mask); if ((i & 3) == 3) { qemu_fprintf(f, "\n"); } @@ -1061,6 +1065,14 @@ static void riscv_cpu_reset_hold(Object *obj, ResetType type) #else env->priv = PRV_U; env->senvcfg = 0; + /* + * Match the user-mode view of a typical firmware/kernel setup where + * cbo.zero is enabled for user mode; the CBCFE/CBIE bits stay zero, + * so the cache-management operations remain illegal in user mode. + */ + if (riscv_cpu_cfg(env)->ext_zicboz) { + env->senvcfg |= SENVCFG_CBZE; + } env->menvcfg = 0; #endif /* !CONFIG_USER_ONLY */ @@ -1099,6 +1111,7 @@ static void riscv_cpu_disas_set_info(const CPUState *s, disassemble_info *info) { const RISCVCPU *cpu = RISCV_CPU(s); const CPURISCVState *env = &cpu->env; + int cap_mode; info->target_info = &cpu->cfg; @@ -1111,16 +1124,93 @@ static void riscv_cpu_disas_set_info(const CPUState *s, disassemble_info *info) switch (env->xl) { case MXL_RV32: info->print_insn = print_insn_riscv32; + cap_mode = CS_MODE_RISCV32; break; case MXL_RV64: info->print_insn = print_insn_riscv64; + cap_mode = CS_MODE_RISCV64; break; case MXL_RV128: info->print_insn = print_insn_riscv128; - break; + /* capstone v6 doesn't support RV128 */ + return; default: g_assert_not_reached(); } + + info->cap_arch = CS_ARCH_RISCV; + info->cap_insn_unit = 4; + info->cap_insn_split = 4; + + /* + * Capstone compresses some features together. See RISCV_getFeatureBits, + * which maps LLVM feature bits to capstone bits. + */ + if (riscv_has_ext(env, RVC) || cpu->cfg.ext_zca) { + cap_mode |= CS_MODE_RISCV_C; + } + if (riscv_has_ext(env, RVF)) { + cap_mode |= CS_MODE_RISCV_FD; + } + if (riscv_has_ext(env, RVV)) { + cap_mode |= CS_MODE_RISCV_V; + } + if (cpu->cfg.ext_zfinx || cpu->cfg.ext_zdinx || cpu->cfg.ext_zhinx) { + cap_mode |= CS_MODE_RISCV_ZFINX; + } + if (cpu->cfg.ext_zcmp || cpu->cfg.ext_zcmt || cpu->cfg.ext_zce) { + cap_mode |= CS_MODE_RISCV_ZCMP_ZCMT_ZCE; + } + if (cpu->cfg.ext_zicfiss) { + cap_mode |= CS_MODE_RISCV_ZICFISS; + } + if (riscv_has_ext(env, RVE)) { + cap_mode |= CS_MODE_RISCV_E; + } + if (riscv_has_ext(env, RVA)) { + cap_mode |= CS_MODE_RISCV_A; + } + if (cpu->cfg.ext_xlrbr) { + cap_mode |= CS_MODE_RISCV_BITMANIP; + } + if (cpu->cfg.ext_zba) { + cap_mode |= CS_MODE_RISCV_ZBA; + } + if (cpu->cfg.ext_zbb) { + cap_mode |= CS_MODE_RISCV_ZBB; + } + if (cpu->cfg.ext_zbc) { + cap_mode |= CS_MODE_RISCV_ZBC; + } + if (cpu->cfg.ext_zbkb) { + cap_mode |= CS_MODE_RISCV_ZBKB; + } + if (cpu->cfg.ext_zbkc) { + cap_mode |= CS_MODE_RISCV_ZBKC; + } + if (cpu->cfg.ext_zbkx) { + cap_mode |= CS_MODE_RISCV_ZBKX; + } + if (cpu->cfg.ext_zbs) { + cap_mode |= CS_MODE_RISCV_ZBS; + } + if (cpu->cfg.ext_xtheadba || + cpu->cfg.ext_xtheadbb || + cpu->cfg.ext_xtheadbs || + cpu->cfg.ext_xtheadcmo || + cpu->cfg.ext_xtheadcondmov || + cpu->cfg.ext_xtheadfmemidx || + cpu->cfg.ext_xtheadfmv || + cpu->cfg.ext_xtheadmac || + cpu->cfg.ext_xtheadmemidx || + cpu->cfg.ext_xtheadmempair || + cpu->cfg.ext_xtheadsync) { + cap_mode |= CS_MODE_RISCV_THEAD; + } + if (cpu->cfg.ext_XVentanaCondOps) { + cap_mode |= CS_MODE_RISCV_VENTANA; + } + info->cap_mode = cap_mode; } #ifndef CONFIG_USER_ONLY @@ -1226,7 +1316,7 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev); Error *local_err = NULL; - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; @@ -2237,6 +2327,7 @@ static RISCVCPUProfile RVA22U64 = { CPU_CFG_OFFSET(ext_zkt), CPU_CFG_OFFSET(ext_zicntr), CPU_CFG_OFFSET(ext_zihpm), CPU_CFG_OFFSET(ext_zicbom), CPU_CFG_OFFSET(ext_zicbop), CPU_CFG_OFFSET(ext_zicboz), + CPU_CFG_OFFSET(ext_zicclsm), /* mandatory named features for this profile */ CPU_CFG_OFFSET(ext_zic64b), @@ -3268,6 +3359,7 @@ static const TypeInfo riscv_cpu_type_infos[] = { .cfg.ext_zicbom = true, .cfg.ext_zicbop = true, .cfg.ext_zicboz = true, + .cfg.ext_zicclsm = true, .cfg.ext_zicntr = true, .cfg.ext_zicsr = true, .cfg.ext_zifencei = true, @@ -3340,6 +3432,7 @@ static const TypeInfo riscv_cpu_type_infos[] = { .cfg.ext_zicbom = true, .cfg.ext_zicbop = true, .cfg.ext_zicboz = true, + .cfg.ext_zicclsm = true, .cfg.ext_zicntr = true, .cfg.ext_zicsr = true, .cfg.ext_zifencei = true, @@ -3426,6 +3519,7 @@ static const TypeInfo riscv_cpu_type_infos[] = { .cfg.ext_zbs = true, .cfg.ext_zkt = true, .cfg.ext_zbkc = true, + .cfg.ext_zicclsm = true, .cfg.ext_zicsr = true, .cfg.ext_zifencei = true, .cfg.ext_zihintpause = true, @@ -3469,6 +3563,7 @@ static const TypeInfo riscv_cpu_type_infos[] = { .cfg.ext_zicbom = true, .cfg.ext_zicbop = true, .cfg.ext_zicboz = true, + .cfg.ext_zicclsm = true, .cfg.ext_zicntr = true, .cfg.ext_zicond = true, .cfg.ext_zicsr = true, @@ -3518,6 +3613,7 @@ static const TypeInfo riscv_cpu_type_infos[] = { /* ISA extensions */ .cfg.mmu = true, + .cfg.ext_zicclsm = true, .cfg.ext_zifencei = true, .cfg.ext_zicsr = true, .cfg.pmp = true, @@ -3577,6 +3673,7 @@ static const TypeInfo riscv_cpu_type_infos[] = { * The RISC-V Instruction Set Manual: Volume I * Unprivileged Architecture */ + .cfg.ext_zicclsm = true, .cfg.ext_zicntr = true, .cfg.ext_zihpm = true, .cfg.ext_zihintntl = true, @@ -3633,6 +3730,7 @@ static const TypeInfo riscv_cpu_type_infos[] = { .misa_ext = RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU, .priv_spec = PRIV_VERSION_1_12_0, .cfg.max_satp_mode = VM_1_10_SV48, + .cfg.ext_zicclsm = true, .cfg.ext_zifencei = true, .cfg.ext_zicsr = true, .cfg.mmu = true, diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index c9dfa7daff..718b66487a 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -538,6 +538,10 @@ struct CPUArchState { uint64_t kvm_timer_compare; uint64_t kvm_timer_state; uint64_t kvm_timer_frequency; + + /* KVM multiprocessor state */ + uint32_t kvm_mp_state; + bool kvm_mp_state_loaded; #endif /* CONFIG_KVM */ }; @@ -615,7 +619,7 @@ struct RISCVCPUClass { RISCVCPUDef *def; }; -static inline int riscv_has_ext(CPURISCVState *env, uint32_t ext) +static inline bool riscv_has_ext(const CPURISCVState *env, uint32_t ext) { return (env->misa_ext & ext) != 0; } diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 3f146a43fe..c01050ce2b 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -1086,11 +1086,11 @@ typedef enum CTRType { (HVICTL_VTI | HVICTL_IID | HVICTL_IPRIOM | HVICTL_IPRIO) /* seed CSR bits */ -#define SEED_OPST (0b11 << 30) -#define SEED_OPST_BIST (0b00 << 30) -#define SEED_OPST_WAIT (0b01 << 30) -#define SEED_OPST_ES16 (0b10 << 30) -#define SEED_OPST_DEAD (0b11 << 30) +#define SEED_OPST (0b11U << 30) +#define SEED_OPST_BIST (0b00U << 30) +#define SEED_OPST_WAIT (0b01U << 30) +#define SEED_OPST_ES16 (0b10U << 30) +#define SEED_OPST_DEAD (0b11U << 30) /* PMU related bits */ #define MIE_LCOFIE (1 << IRQ_PMU_OVF) diff --git a/target/riscv/cpu_cfg_fields.h.inc b/target/riscv/cpu_cfg_fields.h.inc index 9eb47af0a7..f8c27a574f 100644 --- a/target/riscv/cpu_cfg_fields.h.inc +++ b/target/riscv/cpu_cfg_fields.h.inc @@ -139,6 +139,8 @@ BOOL_FIELD(has_priv_1_11) /* Always enabled for TCG if has_priv_1_11 */ BOOL_FIELD(ext_ziccrse) +BOOL_FIELD(ext_zicclsm) + /* Vendor-specific custom extensions */ BOOL_FIELD(ext_xtheadba) BOOL_FIELD(ext_xtheadbb) diff --git a/target/riscv/helper.h b/target/riscv/helper.h index 542b7c264f..4fc2d3a155 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -79,7 +79,6 @@ DEF_HELPER_FLAGS_2(froundnx_d, TCG_CALL_NO_RWG_SE, i64, env, i64) /* Bitmanip */ DEF_HELPER_FLAGS_2(clmul, TCG_CALL_NO_RWG_SE, tl, tl, tl) DEF_HELPER_FLAGS_2(clmulr, TCG_CALL_NO_RWG_SE, tl, tl, tl) -DEF_HELPER_FLAGS_1(brev8, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_1(unzip, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_1(zip, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_2(xperm4, TCG_CALL_NO_RWG_SE, tl, tl, tl) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 495cb42dc8..68e1501b21 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -603,6 +603,12 @@ static int kvm_riscv_get_regs_core(CPUState *cs) } env->pc = reg; + ret = kvm_get_one_reg(cs, RISCV_CORE_REG(mode), ®); + if (ret) { + return ret; + } + env->priv = reg; + for (i = 1; i < 32; i++) { uint64_t id = KVM_RISCV_REG_ID_ULONG(KVM_REG_RISCV_CORE, i); ret = kvm_get_one_reg(cs, id, ®); @@ -628,6 +634,12 @@ static int kvm_riscv_put_regs_core(CPUState *cs) return ret; } + reg = env->priv; + ret = kvm_set_one_reg(cs, RISCV_CORE_REG(mode), ®); + if (ret) { + return ret; + } + for (i = 1; i < 32; i++) { uint64_t id = KVM_RISCV_REG_ID_ULONG(KVM_REG_RISCV_CORE, i); reg = env->gpr[i]; @@ -1362,25 +1374,35 @@ int kvm_arch_get_registers(CPUState *cs, Error **errp) return ret; } + if (cap_has_mp_state) { + struct kvm_mp_state mp_state; + + ret = kvm_vcpu_ioctl(cs, KVM_GET_MP_STATE, &mp_state); + if (ret) { + return ret; + } + RISCV_CPU(cs)->env.kvm_mp_state = mp_state.mp_state; + } + return ret; } -int kvm_riscv_sync_mpstate_to_kvm(RISCVCPU *cpu, int state) +bool kvm_riscv_has_mp_state(void) { - if (cap_has_mp_state) { - struct kvm_mp_state mp_state = { - .mp_state = state - }; + return cap_has_mp_state; +} - int ret = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MP_STATE, &mp_state); - if (ret) { - fprintf(stderr, "%s: failed to sync MP_STATE %d/%s\n", - __func__, ret, strerror(-ret)); - return -1; - } +static int kvm_riscv_put_mp_state(CPUState *cs) +{ + struct kvm_mp_state mp_state = { + .mp_state = RISCV_CPU(cs)->env.kvm_mp_state, + }; + + if (!cap_has_mp_state) { + return 0; } - return 0; + return kvm_vcpu_ioctl(cs, KVM_SET_MP_STATE, &mp_state); } int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp) @@ -1419,10 +1441,18 @@ int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp) } if (KVM_PUT_RESET_STATE == level) { - RISCVCPU *cpu = RISCV_CPU(cs); - int state = cs->cpu_index == 0 ? KVM_MP_STATE_RUNNABLE - : KVM_MP_STATE_STOPPED; - ret = kvm_riscv_sync_mpstate_to_kvm(cpu, state); + CPURISCVState *env = &RISCV_CPU(cs)->env; + + env->kvm_mp_state = cs->cpu_index == 0 ? KVM_MP_STATE_RUNNABLE + : KVM_MP_STATE_STOPPED; + env->kvm_mp_state_loaded = false; + ret = kvm_riscv_put_mp_state(cs); + if (ret) { + return ret; + } + } else if (KVM_PUT_FULL_STATE == level && + RISCV_CPU(cs)->env.kvm_mp_state_loaded) { + ret = kvm_riscv_put_mp_state(cs); if (ret) { return ret; } @@ -2006,7 +2036,7 @@ static void kvm_cpu_instance_init(CPUState *cs) * We'll get here via the following path: * * riscv_cpu_realize() - * -> cpu_exec_realizefn() + * -> cpu_common_realize() * -> kvm_cpu_realize() (via accel_cpu_common_realize()) */ static bool kvm_cpu_realize(CPUState *cs, Error **errp) diff --git a/target/riscv/kvm/kvm_riscv.h b/target/riscv/kvm/kvm_riscv.h index b2bcd1041f..61eaa12443 100644 --- a/target/riscv/kvm/kvm_riscv.h +++ b/target/riscv/kvm/kvm_riscv.h @@ -28,7 +28,7 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift, uint64_t aplic_base, uint64_t imsic_base, uint64_t guest_num); void riscv_kvm_aplic_request(void *opaque, int irq, int level); -int kvm_riscv_sync_mpstate_to_kvm(RISCVCPU *cpu, int state); +bool kvm_riscv_has_mp_state(void); void riscv_kvm_cpu_finalize_features(RISCVCPU *cpu, Error **errp); uint64_t kvm_riscv_get_timebase_frequency(RISCVCPU *cpu); diff --git a/target/riscv/machine.c b/target/riscv/machine.c index 0ab613a298..31c49ca3e6 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -25,6 +25,9 @@ #include "exec/icount.h" #include "target/riscv/tcg/debug.h" #include "hw/riscv/machines-qom.h" +#ifdef CONFIG_KVM +#include "kvm/kvm_riscv.h" +#endif static bool pmp_needed(void *opaque) { @@ -222,6 +225,44 @@ static const VMStateDescription vmstate_kvmtimer = { VMSTATE_END_OF_LIST() } }; + +static int riscv_cpu_kvm_pre_load(void *opaque) +{ + RISCVCPU *cpu = opaque; + + cpu->env.kvm_mp_state_loaded = false; + return 0; +} + +static bool kvm_mp_state_needed(void *opaque) +{ + return kvm_enabled() && kvm_riscv_has_mp_state(); +} + +static int kvm_mp_state_post_load(void *opaque, int version_id) +{ + RISCVCPU *cpu = opaque; + CPURISCVState *env = &cpu->env; + + if (!kvm_enabled() || !kvm_riscv_has_mp_state()) { + return -ENOTSUP; + } + + env->kvm_mp_state_loaded = true; + return 0; +} + +static const VMStateDescription vmstate_kvm_mp_state = { + .name = "cpu/kvm-mp-state", + .version_id = 1, + .minimum_version_id = 1, + .needed = kvm_mp_state_needed, + .post_load = kvm_mp_state_post_load, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(env.kvm_mp_state, RISCVCPU), + VMSTATE_END_OF_LIST() + } +}; #endif static bool debug_needed(void *opaque) @@ -457,8 +498,11 @@ static const VMStateDescription vmstate_mseccfg = { const VMStateDescription vmstate_riscv_cpu = { .name = "cpu", - .version_id = 11, - .minimum_version_id = 11, + .version_id = 12, + .minimum_version_id = 12, +#ifdef CONFIG_KVM + .pre_load = riscv_cpu_kvm_pre_load, +#endif .post_load = riscv_cpu_post_load, .fields = (const VMStateField[]) { VMSTATE_UINT64_ARRAY(env.gpr, RISCVCPU, 32), @@ -522,6 +566,7 @@ const VMStateDescription vmstate_riscv_cpu = { &vmstate_rv128, #ifdef CONFIG_KVM &vmstate_kvmtimer, + &vmstate_kvm_mp_state, #endif &vmstate_envcfg, &vmstate_debug, diff --git a/target/riscv/tcg/bitmanip_helper.c b/target/riscv/tcg/bitmanip_helper.c index 1156a87dd3..d8d94bca7b 100644 --- a/target/riscv/tcg/bitmanip_helper.c +++ b/target/riscv/tcg/bitmanip_helper.c @@ -52,21 +52,6 @@ target_ulong HELPER(clmulr)(target_ulong rs1, target_ulong rs2) return result; } -static inline target_ulong do_swap(target_ulong x, uint64_t mask, int shift) -{ - return ((x & mask) << shift) | ((x & ~mask) >> shift); -} - -target_ulong HELPER(brev8)(target_ulong rs1) -{ - target_ulong x = rs1; - - x = do_swap(x, 0x5555555555555555ull, 1); - x = do_swap(x, 0x3333333333333333ull, 2); - x = do_swap(x, 0x0f0f0f0f0f0f0f0full, 4); - return x; -} - static const uint64_t shuf_masks[] = { dup_const(MO_8, 0x44), dup_const(MO_8, 0x30), diff --git a/target/riscv/tcg/csr.c b/target/riscv/tcg/csr.c index 36f2004bc5..002f7e69c1 100644 --- a/target/riscv/tcg/csr.c +++ b/target/riscv/tcg/csr.c @@ -2014,8 +2014,8 @@ static uint64_t riscv_write_uxl(CPURISCVState *env, uint64_t val, RISCVMXL xl = riscv_cpu_mxl(env); uint64_t uxl = get_field(val, field); - if (uxl == MXL_RV128) { - uxl = xl == MXL_RV128 ? MXL_RV64 : xl; + if (xl != MXL_RV128 && uxl == MXL_RV128) { + uxl = xl; val = set_field(val, field, uxl); } @@ -2067,6 +2067,11 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, } if (xl != MXL_RV32 || env->debugger) { + if ((val & MSTATUS64_SXL) != 0) { + mask |= MSTATUS64_SXL; + val = riscv_write_uxl(env, val, MSTATUS64_SXL); + } + if ((val & MSTATUS64_UXL) != 0) { mask |= MSTATUS64_UXL; val = riscv_write_uxl(env, val, MSTATUS64_UXL); @@ -2182,9 +2187,13 @@ static RISCVException write_misa(CPURISCVState *env, int csrno, /* Mask extensions that are not supported by this hart */ val &= env->misa_ext_mask; - /* Suppress 'C' if next instruction is not aligned. */ - if ((val & RVC) && (get_next_pc(env, ra) & 3) != 0) { - val &= ~RVC; + /* drop write if RVC is cleared and next instruction is not aligned */ + if ((env->misa_ext & RVC) && !(val & RVC) && + (get_next_pc(env, ra) & 3) != 0) { + qemu_log_mask(LOG_GUEST_ERROR, "Unable to write MISA ext value " + "0x%x, MISA.C disable failed\n", env->misa_ext); + + return RISCV_EXCP_NONE; } /* Disable RVG if any of its dependencies are disabled */ @@ -3216,6 +3225,19 @@ static RISCVException write_menvcfg(CPURISCVState *env, int csrno, MENVCFG_CBZE; bool stce_changed = false; + /* + * menvcfg.LPE (Zicfilp) and menvcfg.SSE (Zicfiss) reside in the low + * 32 bits and are defined for both RV32 and RV64, so they must be + * writable regardless of MXLEN. + */ + if (cfg->ext_zicfilp) { + mask |= MENVCFG_LPE; + } + + if (cfg->ext_zicfiss) { + mask |= MENVCFG_SSE; + } + if (riscv_cpu_mxl(env) == MXL_RV64) { mask |= (cfg->ext_svpbmt ? MENVCFG_PBMTE : 0) | (cfg->ext_sstc ? MENVCFG_STCE : 0) | @@ -3223,14 +3245,6 @@ static RISCVException write_menvcfg(CPURISCVState *env, int csrno, (cfg->ext_svadu ? MENVCFG_ADUE : 0) | (cfg->ext_ssdbltrp ? MENVCFG_DTE : 0); - if (env_archcpu(env)->cfg.ext_zicfilp) { - mask |= MENVCFG_LPE; - } - - if (env_archcpu(env)->cfg.ext_zicfiss) { - mask |= MENVCFG_SSE; - } - /* Update PMM field only if the value is valid according to Zjpm v1.0 */ if (env_archcpu(env)->cfg.ext_smnpm && get_field(val, MENVCFG_PMM) != PMM_FIELD_RESERVED) { @@ -3378,20 +3392,24 @@ static RISCVException write_henvcfg(CPURISCVState *env, int csrno, return ret; } + /* + * henvcfg.LPE (Zicfilp) and henvcfg.SSE (Zicfiss) reside in the low + * 32 bits and are defined for both RV32 and RV64, so they must be + * writable regardless of MXLEN. + */ + if (cfg->ext_zicfilp) { + mask |= HENVCFG_LPE; + } + + /* H can light up SSE for VS only if HS had it from menvcfg */ + if (cfg->ext_zicfiss && get_field(env->menvcfg, MENVCFG_SSE)) { + mask |= HENVCFG_SSE; + } + if (riscv_cpu_mxl(env) == MXL_RV64) { mask |= env->menvcfg & (HENVCFG_PBMTE | HENVCFG_STCE | HENVCFG_ADUE | HENVCFG_DTE); - if (env_archcpu(env)->cfg.ext_zicfilp) { - mask |= HENVCFG_LPE; - } - - /* H can light up SSE for VS only if HS had it from menvcfg */ - if (env_archcpu(env)->cfg.ext_zicfiss && - get_field(env->menvcfg, MENVCFG_SSE)) { - mask |= HENVCFG_SSE; - } - /* Update PMM field only if the value is valid according to Zjpm v1.0 */ if (env_archcpu(env)->cfg.ext_ssnpm && get_field(val, HENVCFG_PMM) != PMM_FIELD_RESERVED) { @@ -3984,7 +4002,7 @@ static RISCVException read_sstatus_i128(CPURISCVState *env, int csrno, Int128 *val) { uint64_t mask = sstatus_v1_10_mask; - uint64_t sstatus = env->mstatus & mask; + uint64_t sstatus; if (env->xl != MXL_RV32 || env->debugger) { mask |= SSTATUS64_UXL; } @@ -3995,7 +4013,7 @@ static RISCVException read_sstatus_i128(CPURISCVState *env, int csrno, if (env_archcpu(env)->cfg.ext_zicfilp) { mask |= SSTATUS_SPELP; } - + sstatus = env->mstatus & mask; *val = int128_make128(sstatus, add_status_sd(MXL_RV128, sstatus)); return RISCV_EXCP_NONE; } @@ -4014,8 +4032,8 @@ static RISCVException read_sstatus(CPURISCVState *env, int csrno, if (riscv_cpu_cfg(env)->ext_ssdbltrp) { mask |= SSTATUS_SDT; } - /* TODO: Use SXL not MXL. */ - *val = add_status_sd(riscv_cpu_mxl(env), env->mstatus & mask); + + *val = add_status_sd(riscv_cpu_sxl(env), env->mstatus & mask); return RISCV_EXCP_NONE; } diff --git a/target/riscv/tcg/debug.c b/target/riscv/tcg/debug.c index 3c0fe70101..5ab29f6945 100644 --- a/target/riscv/tcg/debug.c +++ b/target/riscv/tcg/debug.c @@ -1080,7 +1080,7 @@ void riscv_trigger_unrealize(CPURISCVState *env) g_free(env->cpu_watchpoint); for (int i = 0; i < env->num_triggers; i++) { - timer_del(env->itrigger_timer[i]); + timer_free(env->itrigger_timer[i]); } g_free(env->itrigger_timer); } diff --git a/target/riscv/tcg/insn_trans/trans_rvb.c.inc b/target/riscv/tcg/insn_trans/trans_rvb.c.inc index e4dcc7c991..3e74be223b 100644 --- a/target/riscv/tcg/insn_trans/trans_rvb.c.inc +++ b/target/riscv/tcg/insn_trans/trans_rvb.c.inc @@ -522,7 +522,7 @@ static void gen_packw(TCGv ret, TCGv src1, TCGv src2) static bool trans_brev8(DisasContext *ctx, arg_brev8 *a) { REQUIRE_ZBKB(ctx); - return gen_unary(ctx, a, EXT_NONE, gen_helper_brev8); + return gen_unary(ctx, a, EXT_NONE, tcg_gen_revbit8_tl); } static bool trans_pack(DisasContext *ctx, arg_pack *a) diff --git a/target/riscv/tcg/insn_trans/trans_rvd.c.inc b/target/riscv/tcg/insn_trans/trans_rvd.c.inc index 3b9a745520..1be2bee946 100644 --- a/target/riscv/tcg/insn_trans/trans_rvd.c.inc +++ b/target/riscv/tcg/insn_trans/trans_rvd.c.inc @@ -61,6 +61,9 @@ static bool trans_fld(DisasContext *ctx, arg_fld *a) memop |= MO_ATOM_IFALIGN; } memop |= ctx->mo_endianness; + if (!ctx->cfg_ptr->ext_zicclsm) { + memop |= MO_ALIGN; + } decode_save_opc(ctx, 0); addr = get_address(ctx, a->rs1, a->imm); @@ -86,6 +89,9 @@ static bool trans_fsd(DisasContext *ctx, arg_fsd *a) memop |= MO_ATOM_IFALIGN; } memop |= ctx->mo_endianness; + if (!ctx->cfg_ptr->ext_zicclsm) { + memop |= MO_ALIGN; + } decode_save_opc(ctx, 0); addr = get_address(ctx, a->rs1, a->imm); diff --git a/target/riscv/tcg/insn_trans/trans_rvf.c.inc b/target/riscv/tcg/insn_trans/trans_rvf.c.inc index e935523c93..636dec1664 100644 --- a/target/riscv/tcg/insn_trans/trans_rvf.c.inc +++ b/target/riscv/tcg/insn_trans/trans_rvf.c.inc @@ -52,6 +52,9 @@ static bool trans_flw(DisasContext *ctx, arg_flw *a) if (ctx->cfg_ptr->ext_zama16b) { memop |= MO_ATOM_WITHIN16; } + if (!ctx->cfg_ptr->ext_zicclsm) { + memop |= MO_ALIGN; + } decode_save_opc(ctx, 0); addr = get_address(ctx, a->rs1, a->imm); @@ -75,6 +78,9 @@ static bool trans_fsw(DisasContext *ctx, arg_fsw *a) if (ctx->cfg_ptr->ext_zama16b) { memop |= MO_ATOM_WITHIN16; } + if (!ctx->cfg_ptr->ext_zicclsm) { + memop |= MO_ALIGN; + } decode_save_opc(ctx, 0); addr = get_address(ctx, a->rs1, a->imm); @@ -428,7 +434,8 @@ 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_ZFINX_OR_F(ctx); + /* Zfinx explicitly excludes the FMV transfer instructions. */ + REQUIRE_EXT(ctx, RVF); TCGv dest = dest_gpr(ctx, a->rd); TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); @@ -531,7 +538,8 @@ 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_ZFINX_OR_F(ctx); + /* Zfinx explicitly excludes the FMV transfer instructions. */ + REQUIRE_EXT(ctx, RVF); TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO); diff --git a/target/riscv/tcg/insn_trans/trans_rvi.c.inc b/target/riscv/tcg/insn_trans/trans_rvi.c.inc index e47b16a7fa..cc1b5dbbad 100644 --- a/target/riscv/tcg/insn_trans/trans_rvi.c.inc +++ b/target/riscv/tcg/insn_trans/trans_rvi.c.inc @@ -413,6 +413,9 @@ static bool gen_load(DisasContext *ctx, arg_lb *a, MemOp memop) if (ctx->cfg_ptr->ext_zama16b) { memop |= MO_ATOM_WITHIN16; } + if (!ctx->cfg_ptr->ext_zicclsm) { + memop |= MO_ALIGN; + } decode_save_opc(ctx, 0); if (get_xl(ctx) == MXL_RV128) { out = gen_load_i128(ctx, a, memop); @@ -524,6 +527,9 @@ static bool gen_store(DisasContext *ctx, arg_sb *a, MemOp memop) if (ctx->cfg_ptr->ext_zama16b) { memop |= MO_ATOM_WITHIN16; } + if (!ctx->cfg_ptr->ext_zicclsm) { + memop |= MO_ALIGN; + } decode_save_opc(ctx, 0); if (get_xl(ctx) == MXL_RV128) { return gen_store_i128(ctx, a, memop); diff --git a/target/riscv/tcg/insn_trans/trans_rvv.c.inc b/target/riscv/tcg/insn_trans/trans_rvv.c.inc index 23262b1d03..a22e2cae6c 100644 --- a/target/riscv/tcg/insn_trans/trans_rvv.c.inc +++ b/target/riscv/tcg/insn_trans/trans_rvv.c.inc @@ -1191,26 +1191,38 @@ static bool ldst_whole_trans(uint32_t vd, uint32_t rs1, uint32_t nf, * Use the helper function if either: * - vstart is not 0. */ - bool use_helper_fn = !s->vstart_eq_zero; if (!use_helper_fn) { uint32_t size = s->cfg_ptr->vlenb * nf; TCGv_i64 t8 = tcg_temp_new_i64(); MemOp atomicity = MO_ATOM_NONE; + MemOp alignment = MO_UNALN; + + /* + * If Zicclsm is disabled, require alignment based on element size. + * Use MO_ALIGN_* based on log2_esz (0 = MO_UNALN, 1 = MO_ALIGN_2, etc). + */ + if (!s->cfg_ptr->ext_zicclsm) { + alignment = log2_esz << MO_ASHIFT; + } + if (log2_esz == 0) { atomicity = MO_ATOM_NONE; } else { atomicity = MO_ATOM_IFALIGN_PAIR; } + for (int i = 0; i < size; i += 8) { TCGv addr = get_address(s, rs1, i); if (is_load) { - tcg_gen_qemu_ld_i64(t8, addr, s->mem_idx, MO_LEUQ | atomicity); + tcg_gen_qemu_ld_i64(t8, addr, s->mem_idx, + MO_LEUQ | atomicity | alignment); tcg_gen_st_i64(t8, tcg_env, vreg_ofs(s, vd) + i); } else { tcg_gen_ld_i64(t8, tcg_env, vreg_ofs(s, vd) + i); - tcg_gen_qemu_st_i64(t8, addr, s->mem_idx, MO_LEUQ | atomicity); + tcg_gen_qemu_st_i64(t8, addr, s->mem_idx, + MO_LEUQ | atomicity | alignment); } if (i == size - 8) { tcg_gen_movi_i32(cpu_vstart, 0); diff --git a/target/riscv/tcg/insn_trans/trans_rvzfh.c.inc b/target/riscv/tcg/insn_trans/trans_rvzfh.c.inc index f36b46c211..b17ef9541c 100644 --- a/target/riscv/tcg/insn_trans/trans_rvzfh.c.inc +++ b/target/riscv/tcg/insn_trans/trans_rvzfh.c.inc @@ -50,6 +50,9 @@ static bool trans_flh(DisasContext *ctx, arg_flh *a) REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx); memop |= ctx->mo_endianness; + if (!ctx->cfg_ptr->ext_zicclsm) { + memop |= MO_ALIGN; + } decode_save_opc(ctx, 0); t0 = get_gpr(ctx, a->rs1, EXT_NONE); if (a->imm) { @@ -75,6 +78,9 @@ static bool trans_fsh(DisasContext *ctx, arg_fsh *a) REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx); memop |= ctx->mo_endianness; + if (!ctx->cfg_ptr->ext_zicclsm) { + memop |= MO_ALIGN; + } decode_save_opc(ctx, 0); t0 = get_gpr(ctx, a->rs1, EXT_NONE); if (a->imm) { @@ -413,6 +419,7 @@ static bool trans_fcvt_d_h(DisasContext *ctx, arg_fcvt_d_h *a) REQUIRE_FPU; REQUIRE_ZFHMIN_OR_ZHINXMIN(ctx); REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rd); TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); @@ -447,6 +454,7 @@ static bool trans_fcvt_h_d(DisasContext *ctx, arg_fcvt_h_d *a) REQUIRE_FPU; REQUIRE_ZFHMIN_OR_ZHINXMIN(ctx); REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rs1); TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); diff --git a/target/riscv/tcg/insn_trans/trans_rvzicfiss.c.inc b/target/riscv/tcg/insn_trans/trans_rvzicfiss.c.inc index a813232887..d47a9f9c7d 100644 --- a/target/riscv/tcg/insn_trans/trans_rvzicfiss.c.inc +++ b/target/riscv/tcg/insn_trans/trans_rvzicfiss.c.inc @@ -32,6 +32,7 @@ static bool trans_sspopchk(DisasContext *ctx, arg_sspopchk *a) TCGLabel *skip = gen_new_label(); uint32_t tmp = (get_xl(ctx) == MXL_RV64) ? 8 : 4; TCGv data = tcg_temp_new(); + gen_update_pc(ctx, 0); TCGv_i64 wide_addr = tcg_temp_new_i64(); tcg_gen_ld_i64(wide_addr, tcg_env, offsetof(CPURISCVState, ssp)); tcg_gen_trunc_i64_tl(addr, wide_addr); @@ -42,7 +43,6 @@ static bool trans_sspopchk(DisasContext *ctx, arg_sspopchk *a) tcg_gen_brcond_tl(TCG_COND_EQ, data, rs1, skip); tcg_gen_st8_i32(tcg_constant_i32(RISCV_EXCP_SW_CHECK_BCFI_TVAL), tcg_env, offsetof(CPURISCVState, sw_check_code)); - gen_update_pc(ctx, 0); gen_helper_raise_exception(tcg_env, tcg_constant_i32(RISCV_EXCP_SW_CHECK)); gen_set_label(skip); diff --git a/target/riscv/tcg/op_helper.c b/target/riscv/tcg/op_helper.c index ba3c7da375..3e94005d2b 100644 --- a/target/riscv/tcg/op_helper.c +++ b/target/riscv/tcg/op_helper.c @@ -21,6 +21,9 @@ #include "qemu/osdep.h" #include "cpu.h" #include "target/riscv/tcg/csr.h" +#ifndef CONFIG_USER_ONLY +#include "pmu.h" +#endif #include "internals.h" #include "exec/cputlb.h" #include "accel/tcg/cpu-ldst.h" @@ -47,6 +50,9 @@ G_NORETURN void riscv_raise_exception(CPURISCVState *env, void helper_raise_exception(CPURISCVState *env, uint32_t exception) { +#ifndef CONFIG_USER_ONLY + riscv_pmu_decr_instret(env); +#endif riscv_raise_exception(env, exception, 0); } @@ -146,7 +152,16 @@ target_ulong helper_csrrw_i128(CPURISCVState *env, int csr, static void check_zicbo_envcfg(CPURISCVState *env, target_ulong envbits, uintptr_t ra) { -#ifndef CONFIG_USER_ONLY +#if defined(CONFIG_USER_ONLY) + /* + * linux-user: the machine-level envcfg fields are not part of the + * user-mode environment; only the user-mode view of the enabling + * bits (senvcfg, as initialized for the guest) applies. + */ + if (!get_field(env->senvcfg, envbits)) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, ra); + } +#else if ((env->priv < PRV_M) && !get_field(env->menvcfg, envbits)) { riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, ra); } @@ -293,6 +308,11 @@ target_ulong helper_sret(CPURISCVState *env) const privilege_mode_t src_priv = env->priv; const bool src_virt = env->virt_enabled; + if ((env->virt_enabled && env->priv < PRV_S) || + (env->virt_enabled && get_field(env->hstatus, HSTATUS_VTSR))) { + riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); + } + if (!(env->priv >= PRV_S)) { riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } @@ -308,10 +328,6 @@ target_ulong helper_sret(CPURISCVState *env) riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } - if (env->virt_enabled && get_field(env->hstatus, HSTATUS_VTSR)) { - riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); - } - mstatus = env->mstatus; prev_priv = get_field(mstatus, MSTATUS_SPP); mstatus = set_field(mstatus, MSTATUS_SIE, diff --git a/target/riscv/tcg/pmu.c b/target/riscv/tcg/pmu.c index 38ad2737e1..1a4658319b 100644 --- a/target/riscv/tcg/pmu.c +++ b/target/riscv/tcg/pmu.c @@ -49,6 +49,21 @@ static bool riscv_pmu_counter_enabled(RISCVCPU *cpu, uint32_t ctr_idx) } } +static bool riscv_pmu_counter_filtered(CPURISCVState *env, uint64_t cfg) +{ + bool virt_on = env->virt_enabled; + + return (env->priv == PRV_M && (cfg & MHPMEVENT_BIT_MINH)) || + (env->priv == PRV_S && virt_on && + (cfg & MHPMEVENT_BIT_VSINH)) || + (env->priv == PRV_U && virt_on && + (cfg & MHPMEVENT_BIT_VUINH)) || + (env->priv == PRV_S && !virt_on && + (cfg & MHPMEVENT_BIT_SINH)) || + (env->priv == PRV_U && !virt_on && + (cfg & MHPMEVENT_BIT_UINH)); +} + /* * Information needed to update counters: * new_priv, new_virt: To correctly save starting snapshot for the newly @@ -147,12 +162,27 @@ void riscv_pmu_update_fixed_ctrs(CPURISCVState *env, riscv_pmu_icount_update_priv(env, newpriv, new_virt); } +void riscv_pmu_decr_instret(CPURISCVState *env) +{ + if (!icount_enabled() || + (env->mcountinhibit & COUNTEREN_IR) || + riscv_pmu_counter_filtered(env, env->minstretcfg)) { + return; + } + + /* + * minstret is derived from icount, which includes the current + * instruction. Move the baseline forward to exclude an instruction + * that raises an exception and therefore does not retire. + */ + env->pmu_ctrs[2].mhpmcounter_prev++; +} + int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx) { uint32_t ctr_idx; CPURISCVState *env = &cpu->env; uint64_t max_val = UINT64_MAX; - bool virt_on = env->virt_enabled; PMUCTRState *counter; gpointer value; @@ -170,17 +200,7 @@ int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx) return -1; } - /* Privilege mode filtering */ - if ((env->priv == PRV_M && - (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_MINH)) || - (env->priv == PRV_S && virt_on && - (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_VSINH)) || - (env->priv == PRV_U && virt_on && - (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_VUINH)) || - (env->priv == PRV_S && !virt_on && - (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_SINH)) || - (env->priv == PRV_U && !virt_on && - (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_UINH))) { + if (riscv_pmu_counter_filtered(env, env->mhpmevent_val[ctr_idx])) { return 0; } diff --git a/target/riscv/tcg/pmu.h b/target/riscv/tcg/pmu.h index b4f1e469a2..2429c01b77 100644 --- a/target/riscv/tcg/pmu.h +++ b/target/riscv/tcg/pmu.h @@ -36,6 +36,7 @@ int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, uint32_t ctr_idx); void riscv_pmu_update_fixed_ctrs(CPURISCVState *env, privilege_mode_t newpriv, bool new_virt); +void riscv_pmu_decr_instret(CPURISCVState *env); RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val, bool upper_half, uint32_t ctr_idx); diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 4af5cd9c73..9e3cc87f8a 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -1304,7 +1304,7 @@ static void riscv_cpu_set_profile(RISCVCPU *cpu, * We'll get here via the following path: * * riscv_cpu_realize() - * -> cpu_exec_realizefn() + * -> cpu_common_realize() * -> tcg_cpu_realize() (via accel_cpu_common_realize()) */ static bool riscv_tcg_cpu_realize(CPUState *cs, Error **errp) diff --git a/target/riscv/tcg/vector_helper.c b/target/riscv/tcg/vector_helper.c index e321ca2616..e28d8a3d9f 100644 --- a/target/riscv/tcg/vector_helper.c +++ b/target/riscv/tcg/vector_helper.c @@ -199,20 +199,34 @@ static inline void vext_set_elem_mask(void *v0, int index, ((uint64_t *)v0)[idx] = deposit64(old, pos, 1, value); } +static inline MemOpIdx vext_make_memop_idx(CPURISCVState *env, size_t size) +{ + int mmu_idx = riscv_env_mmu_index(env, false); + MemOp memop = size_memop(size) | mo_endian_env(env); + + if (!riscv_cpu_cfg(env)->ext_zicclsm) { + memop |= MO_ALIGN; + } + + return make_memop_idx(memop, mmu_idx); +} + /* elements operations for load and store */ typedef void vext_ldst_elem_fn_tlb(CPURISCVState *env, abi_ptr addr, uint32_t idx, void *vd, uintptr_t retaddr); typedef void vext_ldst_elem_fn_host(void *vd, uint32_t idx, void *host); -#define GEN_VEXT_LD_ELEM(NAME, ETYPE, H, LDSUF) \ +#define GEN_VEXT_TLB_LD_ELEM(NAME, ETYPE, H, LDSUF) \ static inline QEMU_ALWAYS_INLINE \ void NAME##_tlb(CPURISCVState *env, abi_ptr addr, \ uint32_t idx, void *vd, uintptr_t retaddr) \ { \ ETYPE *cur = ((ETYPE *)vd + H(idx)); \ - *cur = cpu_##LDSUF##_data_ra(env, addr, retaddr); \ + MemOpIdx oi = vext_make_memop_idx(env, sizeof(ETYPE)); \ + *cur = cpu_##LDSUF##_mmu(env, addr, oi, retaddr); \ } \ - \ + +#define GEN_VEXT_HOST_LD_ELEM(NAME, ETYPE, H, LDSUF) \ static inline QEMU_ALWAYS_INLINE \ void NAME##_host(void *vd, uint32_t idx, void *host) \ { \ @@ -220,20 +234,27 @@ void NAME##_host(void *vd, uint32_t idx, void *host) \ *cur = (ETYPE)LDSUF##_p(host); \ } -GEN_VEXT_LD_ELEM(lde_b, uint8_t, H1, ldub) -GEN_VEXT_LD_ELEM(lde_h, uint16_t, H2, lduw_le) -GEN_VEXT_LD_ELEM(lde_w, uint32_t, H4, ldl_le) -GEN_VEXT_LD_ELEM(lde_d, uint64_t, H8, ldq_le) +GEN_VEXT_TLB_LD_ELEM(lde_b, uint8_t, H1, ldb) +GEN_VEXT_TLB_LD_ELEM(lde_h, uint16_t, H2, ldw) +GEN_VEXT_TLB_LD_ELEM(lde_w, uint32_t, H4, ldl) +GEN_VEXT_TLB_LD_ELEM(lde_d, uint64_t, H8, ldq) -#define GEN_VEXT_ST_ELEM(NAME, ETYPE, H, STSUF) \ +GEN_VEXT_HOST_LD_ELEM(lde_b, uint8_t, H1, ldub) +GEN_VEXT_HOST_LD_ELEM(lde_h, uint16_t, H2, lduw_le) +GEN_VEXT_HOST_LD_ELEM(lde_w, uint32_t, H4, ldl_le) +GEN_VEXT_HOST_LD_ELEM(lde_d, uint64_t, H8, ldq_le) + +#define GEN_VEXT_TLB_ST_ELEM(NAME, ETYPE, H, STSUF) \ static inline QEMU_ALWAYS_INLINE \ void NAME##_tlb(CPURISCVState *env, abi_ptr addr, \ uint32_t idx, void *vd, uintptr_t retaddr) \ { \ ETYPE data = *((ETYPE *)vd + H(idx)); \ - cpu_##STSUF##_data_ra(env, addr, data, retaddr); \ + MemOpIdx oi = vext_make_memop_idx(env, sizeof(ETYPE)); \ + cpu_##STSUF##_mmu(env, addr, data, oi, retaddr); \ } \ - \ + +#define GEN_VEXT_HOST_ST_ELEM(NAME, ETYPE, H, STSUF) \ static inline QEMU_ALWAYS_INLINE \ void NAME##_host(void *vd, uint32_t idx, void *host) \ { \ @@ -241,10 +262,15 @@ void NAME##_host(void *vd, uint32_t idx, void *host) \ STSUF##_p(host, data); \ } -GEN_VEXT_ST_ELEM(ste_b, uint8_t, H1, stb) -GEN_VEXT_ST_ELEM(ste_h, uint16_t, H2, stw_le) -GEN_VEXT_ST_ELEM(ste_w, uint32_t, H4, stl_le) -GEN_VEXT_ST_ELEM(ste_d, uint64_t, H8, stq_le) +GEN_VEXT_TLB_ST_ELEM(ste_b, uint8_t, H1, stb) +GEN_VEXT_TLB_ST_ELEM(ste_h, uint16_t, H2, stw) +GEN_VEXT_TLB_ST_ELEM(ste_w, uint32_t, H4, stl) +GEN_VEXT_TLB_ST_ELEM(ste_d, uint64_t, H8, stq) + +GEN_VEXT_HOST_ST_ELEM(ste_b, uint8_t, H1, stb) +GEN_VEXT_HOST_ST_ELEM(ste_h, uint16_t, H2, stw_le) +GEN_VEXT_HOST_ST_ELEM(ste_w, uint32_t, H4, stl_le) +GEN_VEXT_HOST_ST_ELEM(ste_d, uint64_t, H8, stq_le) static inline QEMU_ALWAYS_INLINE void vext_continuous_ldst_tlb(CPURISCVState *env, vext_ldst_elem_fn_tlb *ldst_tlb, @@ -398,7 +424,16 @@ vext_page_ldst_us(CPURISCVState *env, void *vd, target_ulong addr, probe_pages(env, addr, size, ra, access_type, mmu_index, &host, &flags, true); - if (flags == 0) { + bool misaligned = addr & (esz - 1); + + /* + * Allow the host fast-pash when: + * 1. Page permission/pmp/watchpoint are checked and we have a contigous + * host mapping. + * 2. Zicclsm is enabled or load/store is not a misaligned access. + * Otherwise, we will fall back to the slow TLB-path. + */ + if (flags == 0 && (riscv_cpu_cfg(env)->ext_zicclsm || !misaligned)) { if (nf == 1) { vext_continuous_ldst_host(env, ldst_host, vd, evl, env->vstart, host, esz, is_load); diff --git a/target/riscv/tcg/zce_helper.c b/target/riscv/tcg/zce_helper.c index 15bf0a99c8..c6ba4c1a47 100644 --- a/target/riscv/tcg/zce_helper.c +++ b/target/riscv/tcg/zce_helper.c @@ -18,12 +18,14 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "internals.h" #include "exec/helper-proto.h" #include "accel/tcg/cpu-ldst.h" target_ulong HELPER(cm_jalt)(CPURISCVState *env, uint32_t index) { unsigned mmu_index = cpu_mmu_index(env_cpu(env), true); + MemOp endian = mo_endian_env(env); MemOpIdx oi; #if !defined(CONFIG_USER_ONLY) @@ -45,11 +47,11 @@ target_ulong HELPER(cm_jalt)(CPURISCVState *env, uint32_t index) } if (xlen == 32) { - oi = make_memop_idx(MO_LEUL, mmu_index); + oi = make_memop_idx(MO_UL | endian, mmu_index); t0 = base + (index << 2); target = cpu_ldl_code_mmu(env, t0, oi, 0); } else { - oi = make_memop_idx(MO_LEUQ, mmu_index); + oi = make_memop_idx(MO_UQ | endian, mmu_index); t0 = base + (index << 3); target = cpu_ldq_code_mmu(env, t0, oi, 0); } diff --git a/target/rx/cpu.c b/target/rx/cpu.c index 20b188c24c..9b8473d71c 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -147,7 +147,7 @@ static void rx_cpu_realize(DeviceState *dev, Error **errp) RXCPUClass *rcc = RX_CPU_GET_CLASS(dev); Error *local_err = NULL; - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index c074e12ba2..7c725b8a4a 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -225,10 +225,12 @@ static void s390_cpu_reset_hold(Object *obj, ResetType type) static void s390_cpu_disas_set_info(const CPUState *cpu, disassemble_info *info) { info->mach = bfd_mach_s390_64; - info->cap_arch = CS_ARCH_SYSZ; + info->cap_arch = CS_ARCH_SYSTEMZ; info->endian = BFD_ENDIAN_BIG; info->cap_insn_unit = 2; info->cap_insn_split = 6; + /* Disassemble everything, even if the cpu would trap on the insn. */ + info->cap_mode = CS_MODE_SYSTEMZ_ARCH14; } static void s390_cpu_realizefn(DeviceState *dev, Error **errp) @@ -249,7 +251,7 @@ static void s390_cpu_realizefn(DeviceState *dev, Error **errp) } #endif - cpu_exec_realizefn(cs, &err); + cpu_common_realize(cs, &err); if (err != NULL) { goto out; } diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 2dd9aac807..f292af7ad0 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -714,7 +714,7 @@ static void set_feature(Object *obj, Visitor *v, const char *name, S390CPU *cpu = S390_CPU(obj); bool value; - if (dev->realized) { + if (qdev_is_realized(dev)) { error_setg(errp, "Attempt to set property '%s' on '%s' after " "it was realized", name, object_get_typename(obj)); return; @@ -770,7 +770,7 @@ static void set_feature_group(Object *obj, Visitor *v, const char *name, S390CPU *cpu = S390_CPU(obj); bool value; - if (dev->realized) { + if (qdev_is_realized(dev)) { error_setg(errp, "Attempt to set property '%s' on '%s' after " "it was realized", name, object_get_typename(obj)); return; diff --git a/target/s390x/diag.c b/target/s390x/diag.c index 80f0958478..46d191b87d 100644 --- a/target/s390x/diag.c +++ b/target/s390x/diag.c @@ -185,7 +185,7 @@ out: return false; } - if (kvm_enabled() && kvm_s390_get_hpage_1m()) { + if (kvm_enabled() && kvm_s390_get_hpage()) { error_report("Protected VMs can currently not be backed with " "huge pages"); env->regs[r1 + 1] = DIAG_308_RC_INVAL_FOR_PV; diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index 72031a5714..803b878b4f 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -145,7 +145,7 @@ static int cap_mem_op; static int cap_mem_op_extension; static int cap_s390_irq; static int cap_ri; -static int cap_hpage_1m; +static int cap_hpage; static int cap_vcpu_resets; static int cap_protected; static int cap_zpci_op; @@ -231,7 +231,7 @@ static void kvm_s390_enable_cmma(void) .attr = KVM_S390_VM_MEM_ENABLE_CMMA, }; - if (cap_hpage_1m) { + if (cap_hpage) { warn_report("CMM will not be enabled because it is not " "compatible with huge memory backings."); return; @@ -292,30 +292,28 @@ void kvm_s390_crypto_reset(void) } } -void kvm_s390_set_max_pagesize(uint64_t pagesize, Error **errp) +static bool kvm_s390_pgsize_cap(uint32_t capa, const char *s, Error **errp) { - if (pagesize == 4 * KiB) { - return; + if (kvm_vm_enable_cap(kvm_state, capa, 0)) { + error_setg(errp, "Memory backing with %s pages was specified, " + "but KVM does not support this memory backing", s); + return false; } - - if (pagesize != 1 * MiB) { - error_setg(errp, "Memory backing with 2G pages was specified, " - "but KVM does not support this memory backing"); - return; - } - - if (kvm_vm_enable_cap(kvm_state, KVM_CAP_S390_HPAGE_1M, 0)) { - error_setg(errp, "Memory backing with 1M pages was specified, " - "but KVM does not support this memory backing"); - return; - } - - cap_hpage_1m = 1; + return true; } -int kvm_s390_get_hpage_1m(void) +void kvm_s390_set_max_pagesize(uint64_t pagesize, Error **errp) { - return cap_hpage_1m; + if (pagesize == MiB) { + cap_hpage = kvm_s390_pgsize_cap(KVM_CAP_S390_HPAGE_1M, "1M", errp); + } else if (pagesize != 4 * KiB) { + cap_hpage = 2 * kvm_s390_pgsize_cap(KVM_CAP_S390_HPAGE_2G, "2G", errp); + } +} + +int kvm_s390_get_hpage(void) +{ + return cap_hpage; } static void ccw_machine_class_foreach(ObjectClass *oc, void *opaque) diff --git a/target/s390x/kvm/kvm_s390x.h b/target/s390x/kvm/kvm_s390x.h index 7b1cce3e60..3c4fa0489c 100644 --- a/target/s390x/kvm/kvm_s390x.h +++ b/target/s390x/kvm/kvm_s390x.h @@ -25,7 +25,7 @@ void kvm_s390_program_interrupt(S390CPU *cpu, uint16_t code); int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state); void kvm_s390_vcpu_interrupt_pre_save(S390CPU *cpu); int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu); -int kvm_s390_get_hpage_1m(void); +int kvm_s390_get_hpage(void); int kvm_s390_get_protected_dump(void); int kvm_s390_get_ri(void); int kvm_s390_get_zpci_op(void); diff --git a/target/s390x/kvm/stubs.c b/target/s390x/kvm/stubs.c index 196127baa5..ebf3c83994 100644 --- a/target/s390x/kvm/stubs.c +++ b/target/s390x/kvm/stubs.c @@ -143,7 +143,7 @@ int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu) g_assert_not_reached(); } -int kvm_s390_get_hpage_1m(void) +int kvm_s390_get_hpage(void) { g_assert_not_reached(); } diff --git a/target/s390x/tcg/crypto_helper.c b/target/s390x/tcg/crypto_helper.c index 8fe0a22219..6a5dbe1caf 100644 --- a/target/s390x/tcg/crypto_helper.c +++ b/target/s390x/tcg/crypto_helper.c @@ -16,6 +16,7 @@ #include "qemu/guest-random.h" #include "s390x-internal.h" #include "tcg_s390x.h" +#include "exec/cpu-common.h" #include "exec/helper-proto.h" #include "accel/tcg/cpu-ldst-common.h" #include "accel/tcg/cpu-mmu-index.h" @@ -242,8 +243,8 @@ static int cpacf_sha512(CPUS390XState *env, const int mmu_idx, uintptr_t ra, return !len ? 0 : 3; } -static void fill_buf_random(CPUS390XState *env, const int mmu_idx, uintptr_t ra, - uint64_t *buf_reg, uint64_t *len_reg) +static int fill_buf_random(CPUS390XState *env, const int mmu_idx, uintptr_t ra, + uint64_t *buf_reg, uint64_t *len_reg) { const MemOpIdx oi = make_memop_idx(MO_8, mmu_idx); uint8_t tmp[256]; @@ -265,7 +266,13 @@ static void fill_buf_random(CPUS390XState *env, const int mmu_idx, uintptr_t ra, --*len_reg; } len -= block; + + if (cpu_loop_exit_requested(env_cpu(env))) { + break; + } } + + return len == 0 ? 0 : 3; } uint32_t HELPER(msa)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t r3, @@ -278,6 +285,7 @@ uint32_t HELPER(msa)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t r3, uint8_t subfunc[16] = { 0 }; uint64_t param_addr; MemOpIdx oi; + int cc; switch (type) { case S390_FEAT_TYPE_KMAC: @@ -308,9 +316,13 @@ uint32_t HELPER(msa)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t r3, return cpacf_sha512(env, mmu_idx, ra, env->regs[1], &env->regs[r2], &env->regs[r2 + 1], type); case 114: /* CPACF_PRNO_TRNG */ - fill_buf_random(env, mmu_idx, ra, &env->regs[r1], &env->regs[r1 + 1]); - fill_buf_random(env, mmu_idx, ra, &env->regs[r2], &env->regs[r2 + 1]); - break; + cc = fill_buf_random(env, mmu_idx, ra, + &env->regs[r1], &env->regs[r1 + 1]); + if (cc == 0) { + cc = fill_buf_random(env, mmu_idx, ra, + &env->regs[r2], &env->regs[r2 + 1]); + } + return cc; default: /* we don't implement any other subfunction yet */ g_assert_not_reached(); diff --git a/target/s390x/tcg/insn-data.h.inc b/target/s390x/tcg/insn-data.h.inc index 0d5392eac5..1ea72248a6 100644 --- a/target/s390x/tcg/insn-data.h.inc +++ b/target/s390x/tcg/insn-data.h.inc @@ -887,8 +887,8 @@ C(0xe32f, STRVG, RXY_a, Z, la2, r1_o, new, m1_64, rev64, 0) /* STORE CLOCK */ - F(0xb205, STCK, S, Z, la2, 0, new, m1_64, stck, 0, IF_IO) - F(0xb27c, STCKF, S, SCF, la2, 0, new, m1_64, stck, 0, IF_IO) + F(0xb205, STCK, S, Z, la2, 0, new, 0, stck, 0, IF_IO) + F(0xb27c, STCKF, S, SCF, la2, 0, new, 0, stck, 0, IF_IO) /* STORE CLOCK EXTENDED */ F(0xb278, STCKE, S, Z, 0, a2, 0, 0, stcke, 0, IF_IO) diff --git a/target/s390x/tcg/int_helper.c b/target/s390x/tcg/int_helper.c index fbda396f5b..5aedd1405b 100644 --- a/target/s390x/tcg/int_helper.c +++ b/target/s390x/tcg/int_helper.c @@ -39,7 +39,8 @@ uint64_t HELPER(divs32)(CPUS390XState *env, int64_t a, int64_t b64) int32_t b = b64; int64_t q, r; - if (b == 0) { + /* Catch divide by zero, and non-representable quotient (MIN / -1). */ + if (b == 0 || (b == -1 && a == (1ll << 63))) { tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); } diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index 82165ac1ec..1b6023168b 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -4108,7 +4108,9 @@ static DisasJumpType op_stap(DisasContext *s, DisasOps *o) static DisasJumpType op_stck(DisasContext *s, DisasOps *o) { gen_helper_stck(o->out, tcg_env); + tcg_gen_qemu_st_i64(o->out, o->addr1, get_mem_index(s), MO_BEUQ); /* ??? We don't implement clock states. */ + /* Set the CC after the store; a suppressed store must preserve it. */ gen_op_movi_cc(s, 0); return DISAS_NEXT; } diff --git a/target/sh4/cpu-param.h b/target/sh4/cpu-param.h index c3b8114e53..9d72e35e31 100644 --- a/target/sh4/cpu-param.h +++ b/target/sh4/cpu-param.h @@ -9,10 +9,8 @@ #define SH4_CPU_PARAM_H #define TARGET_PAGE_BITS 12 /* 4k */ -#ifdef CONFIG_USER_ONLY -# define TARGET_VIRT_ADDR_SPACE_BITS 31 -#else -# define TARGET_VIRT_ADDR_SPACE_BITS 32 -#endif + +/* qemu-user does not emulate the MMU, so no need to limit to 31 bits. */ +#define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 763424695c..3bbdee301d 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -28,6 +28,7 @@ #include "fpu/softfloat-helpers.h" #include "accel/tcg/cpu-ops.h" #include "tcg/tcg.h" +#include "disas/capstone.h" static void superh_cpu_set_pc(CPUState *cs, vaddr value) { @@ -167,10 +168,23 @@ static void superh_cpu_reset_hold(Object *obj, ResetType type) static void superh_cpu_disas_set_info(const CPUState *cpu, disassemble_info *info) { + const CPUSH4State *env = cpu_env((CPUState *)cpu); + info->endian = TARGET_BIG_ENDIAN ? BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE; info->mach = bfd_mach_sh4; info->print_insn = print_insn_sh; + + info->cap_arch = CS_ARCH_SH; + info->cap_insn_unit = 2; + info->cap_insn_split = 2; + /* + * Possible capstone bug: the isa levels are not additive: + * least significant bit wins, so SH4 overrides SH4A. + * Work around by setting one or the other but not both. + */ + info->cap_mode = CS_MODE_SHFPU + | (env->features & SH_FEATURE_SH4A ? CS_MODE_SH4A : CS_MODE_SH4); } static ObjectClass *superh_cpu_class_by_name(const char *cpu_model) @@ -250,7 +264,7 @@ static void superh_cpu_realizefn(DeviceState *dev, Error **errp) SuperHCPUClass *scc = SUPERH_CPU_GET_CLASS(dev); Error *local_err = NULL; - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 13ebb122a3..1bc14b586b 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -892,7 +892,7 @@ static void sparc_cpu_realizefn(DeviceState *dev, Error **errp) /* Default NaN value: sign bit clear, all frac bits set */ set_float_default_nan_pattern(0b01111111, &env->fp_status); - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/sparc/machine.c b/target/sparc/machine.c index 5f402e098c..0d5d79b5e7 100644 --- a/target/sparc/machine.c +++ b/target/sparc/machine.c @@ -151,6 +151,37 @@ static const VMStateInfo vmstate_xcc = { .get = get_xcc, .put = put_xcc, }; + +static int get_cwp(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field) +{ + SPARCCPU *cpu = opaque; + CPUSPARCState *env = &cpu->env; + uint32_t val = qemu_get_be32(f); + + /* needed to ensure that the wrapping registers are correctly updated */ + env->cwp = 0; + cpu_set_cwp(env, val); + + return 0; +} + +static int put_cwp(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + SPARCCPU *cpu = opaque; + CPUSPARCState *env = &cpu->env; + uint32_t val = env->cwp; + + qemu_put_be32(f, val); + return 0; +} + +static const VMStateInfo vmstate_cwp = { + .name = "uint32", + .get = get_cwp, + .put = put_cwp, +}; #else static bool fq_needed(void *opaque) { @@ -286,7 +317,14 @@ const VMStateDescription vmstate_sparc_cpu = { VMSTATE_CPU_TIMER(env.hstick, SPARCCPU), /* On SPARC32 env.psrpil and env.cwp are migrated as part of the PSR */ VMSTATE_UINT32(env.psrpil, SPARCCPU), - VMSTATE_UINT32(env.cwp, SPARCCPU), + { + .name = "env.cwp", + .version_id = 0, + .size = sizeof(uint32_t), + .info = &vmstate_cwp, + .flags = VMS_SINGLE, + .offset = 0, + }, #endif VMSTATE_END_OF_LIST() }, diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index 472c24ae32..dcd5c5065b 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -24,6 +24,7 @@ #include "qemu/error-report.h" #include "tcg/debug-assert.h" #include "accel/tcg/cpu-ops.h" +#include "disas/capstone.h" static inline void set_feature(CPUTriCoreState *env, int feature) { @@ -35,6 +36,35 @@ static const gchar *tricore_gdb_arch_name(CPUState *cs) return "tricore"; } +static void tricore_disas_set_info(const CPUState *cpu, disassemble_info *info) +{ + CPUTriCoreState *env = cpu_env((CPUState *)cpu); + + info->endian = BFD_ENDIAN_LITTLE; + info->cap_arch = CS_ARCH_TRICORE; + info->cap_insn_unit = 4; + info->cap_insn_split = 4; + + /* + * Possible capstone bug: the isa levels are not additive. + * Work around by settiing only one. + * Note that TriCore 1.3.0 is the earliest we support. + */ + if (tricore_has_feature(env, TRICORE_FEATURE_162)) { + info->cap_mode = CS_MODE_TRICORE_162; + } else if (tricore_has_feature(env, TRICORE_FEATURE_161)) { + info->cap_mode = CS_MODE_TRICORE_161; + } else if (tricore_has_feature(env, TRICORE_FEATURE_16)) { + info->cap_mode = CS_MODE_TRICORE_160; + } else if (tricore_has_feature(env, TRICORE_FEATURE_131)) { + info->cap_mode = CS_MODE_TRICORE_131; + } else if (tricore_has_feature(env, TRICORE_FEATURE_13)) { + info->cap_mode = CS_MODE_TRICORE_130; + } else { + g_assert_not_reached(); + } +} + static void tricore_cpu_set_pc(CPUState *cs, vaddr value) { cpu_env(cs)->PC = value & ~1; @@ -99,7 +129,7 @@ static void tricore_cpu_realizefn(DeviceState *dev, Error **errp) CPUTriCoreState *env = &cpu->env; Error *local_err = NULL; - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; @@ -214,6 +244,7 @@ static void tricore_cpu_class_init(ObjectClass *c, const void *data) cc->gdb_write_register = tricore_cpu_gdb_write_register; cc->gdb_num_core_regs = 44; cc->gdb_arch_name = tricore_gdb_arch_name; + cc->disas_set_info = tricore_disas_set_info; cc->dump_state = tricore_cpu_dump_state; cc->set_pc = tricore_cpu_set_pc; diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 2015a4e3d0..7c25b9ab70 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -257,7 +257,7 @@ static void xtensa_cpu_realizefn(DeviceState *dev, Error **errp) xtensa_irq_init(&XTENSA_CPU(dev)->env); #endif - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/tcg/aarch64/tcg-target-con-set.h b/tcg/aarch64/tcg-target-con-set.h index d0622e65fb..dd84a9af58 100644 --- a/tcg/aarch64/tcg-target-con-set.h +++ b/tcg/aarch64/tcg-target-con-set.h @@ -24,6 +24,8 @@ C_O1_I2(r, r, rAL) C_O1_I2(r, r, rC) C_O1_I2(r, r, ri) C_O1_I2(r, r, rL) +C_O1_I2(r, r, rS) +C_O1_I2(r, r, rU) C_O1_I2(r, rZ, rA) C_O1_I2(r, rz, rMZ) C_O1_I2(r, rz, rz) diff --git a/tcg/aarch64/tcg-target-con-str.h b/tcg/aarch64/tcg-target-con-str.h index 48e1722c68..fef75e8a76 100644 --- a/tcg/aarch64/tcg-target-con-str.h +++ b/tcg/aarch64/tcg-target-con-str.h @@ -21,4 +21,6 @@ CONST('L', TCG_CT_CONST_LIMM) CONST('M', TCG_CT_CONST_MONE) CONST('O', TCG_CT_CONST_ORRI) CONST('N', TCG_CT_CONST_ANDI) +CONST('S', TCG_CT_CONST_S8) +CONST('U', TCG_CT_CONST_U8) CONST('Z', TCG_CT_CONST_ZERO) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index cc9c2a5158..1e64c5994e 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -152,6 +152,8 @@ static bool patch_reloc(tcg_insn_unit *code_ptr, int type, #define TCG_CT_CONST_ORRI 0x1000 #define TCG_CT_CONST_ANDI 0x2000 #define TCG_CT_CONST_CMP 0x4000 +#define TCG_CT_CONST_S8 0x8000 +#define TCG_CT_CONST_U8 0x10000 #define ALL_GENERAL_REGS 0xffffffffu #define ALL_VECTOR_REGS 0xffffffff00000000ull @@ -320,6 +322,12 @@ static bool tcg_target_const_match(int64_t val, int ct, if ((ct & TCG_CT_CONST_LIMM) && is_limm(val)) { return 1; } + if ((ct & TCG_CT_CONST_S8) && val == (int8_t)val) { + return 1; + } + if ((ct & TCG_CT_CONST_U8) && val == (uint8_t)val) { + return 1; + } if ((ct & TCG_CT_CONST_ZERO) && val == 0) { return 1; } @@ -472,6 +480,12 @@ typedef enum { Iaddsub_imm_SUBI = 0x51000000, Iaddsub_imm_SUBSI = 0x71000000, + /* Min/max immediate instructions. */ + Iminmax_imm_SMAXI = 0x11c00000, + Iminmax_imm_UMAXI = 0x11c40000, + Iminmax_imm_SMINI = 0x11c80000, + Iminmax_imm_UMINI = 0x11cc0000, + /* Bitfield instructions. */ Ibitfield_BFM = 0x33000000, Ibitfield_SBFM = 0x13000000, @@ -521,6 +535,8 @@ typedef enum { /* Data-processing (1 source) instructions. */ Irr_sf_CLZ = 0x5ac01000, + Irr_sf_CTZ = 0x5ac01800, + Irr_sf_CNT = 0x5ac01c00, Irr_sf_RBIT = 0x5ac00000, Irr_sf_REV = 0x5ac00000, /* + size << 10 */ @@ -533,6 +549,10 @@ typedef enum { Irrr_UMULH = 0x9bc07c00, Irrr_UDIV = 0x1ac00800, Irrr_SDIV = 0x1ac00c00, + Irrr_SMAX = 0x1ac00600, + Irrr_UMAX = 0x1ac00640, + Irrr_SMIN = 0x1ac00680, + Irrr_UMIN = 0x1ac006c0, /* Data-processing (3 source) instructions. */ Irrrr_MADD = 0x1b000000, @@ -737,6 +757,13 @@ static void tcg_out_insn_addsub_imm(TCGContext *s, AArch64Insn insn, tcg_out32(s, insn | ext << 31 | aimm << 10 | rn << 5 | rd); } +static void tcg_out_insn_minmax_imm(TCGContext *s, AArch64Insn insn, + TCGType ext, TCGReg rd, TCGReg rn, + uint8_t imm) +{ + tcg_out32(s, insn | ext << 31 | imm << 10 | rn << 5 | rd); +} + /* This function can be used for both 3.4.2 (Bitfield) and 3.4.4 (Logical immediate). Both insn groups have N, IMMR and IMMS fields that feed the DecodeBitMasks pseudo function. */ @@ -2187,24 +2214,30 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; -static void tgen_clz(TCGContext *s, TCGType type, - TCGReg a0, TCGReg a1, TCGReg a2) +static void tgen_clzctz(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + TCGReg a2, AArch64Insn insn) { tcg_out_cmp(s, type, TCG_COND_NE, a1, 0, true); - tcg_out_insn(s, rr_sf, CLZ, type, TCG_REG_TMP0, a1); + tcg_out_insn_rr_sf(s, insn, type, TCG_REG_TMP0, a1); tcg_out_insn(s, csel, CSEL, type, a0, TCG_REG_TMP0, a2, TCG_COND_NE); } -static void tgen_clzi(TCGContext *s, TCGType type, - TCGReg a0, TCGReg a1, tcg_target_long a2) +static void tgen_clz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_clzctz(s, type, a0, a1, a2, Irr_sf_CLZ); +} + +static void tgen_clzctzi(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + tcg_target_long a2, AArch64Insn insn) { if (a2 == (type == TCG_TYPE_I32 ? 32 : 64)) { - tcg_out_insn(s, rr_sf, CLZ, type, a0, a1); + tcg_out_insn_rr_sf(s, insn, type, a0, a1); return; } tcg_out_cmp(s, type, TCG_COND_NE, a1, 0, true); - tcg_out_insn(s, rr_sf, CLZ, type, a0, a1); + tcg_out_insn_rr_sf(s, insn, type, a0, a1); switch (a2) { case -1: @@ -2220,28 +2253,54 @@ static void tgen_clzi(TCGContext *s, TCGType type, } } +static void tgen_clzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_clzctzi(s, type, a0, a1, a2, Irr_sf_CLZ); +} + static const TCGOutOpBinary outop_clz = { .base.static_constraint = C_O1_I2(r, r, rAL), .out_rrr = tgen_clz, .out_rri = tgen_clzi, }; +static TCGConstraintSetIndex cset_ctpop(TCGType type, unsigned flags) +{ + return cpuinfo & CPUINFO_CSSC ? C_O1_I1(r, r) : C_NotImplemented; +} + +static void tgen_ctpop(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_insn(s, rr_sf, CNT, type, a0, a1); +} + static const TCGOutOpUnary outop_ctpop = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_ctpop, + .out_rr = tgen_ctpop, }; static void tgen_ctz(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { - tcg_out_insn(s, rr_sf, RBIT, type, TCG_REG_TMP0, a1); - tgen_clz(s, type, a0, TCG_REG_TMP0, a2); + if (cpuinfo & CPUINFO_CSSC) { + tgen_clzctz(s, type, a0, a1, a2, Irr_sf_CTZ); + } else { + tcg_out_insn(s, rr_sf, RBIT, type, TCG_REG_TMP0, a1); + tgen_clzctz(s, type, a0, TCG_REG_TMP0, a2, Irr_sf_CLZ); + } } static void tgen_ctzi(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, tcg_target_long a2) { - tcg_out_insn(s, rr_sf, RBIT, type, TCG_REG_TMP0, a1); - tgen_clzi(s, type, a0, TCG_REG_TMP0, a2); + if (cpuinfo & CPUINFO_CSSC) { + tgen_clzctzi(s, type, a0, a1, a2, Irr_sf_CTZ); + } else { + tcg_out_insn(s, rr_sf, RBIT, type, TCG_REG_TMP0, a1); + tgen_clzctzi(s, type, a0, TCG_REG_TMP0, a2, Irr_sf_CLZ); + } } static const TCGOutOpBinary outop_ctz = { @@ -2592,6 +2651,92 @@ static void tcg_out_set_borrow(TCGContext *s) TCG_REG_XZR, TCG_REG_XZR, TCG_REG_XZR); } +static TCGConstraintSetIndex cset_sminmax(TCGType type, unsigned flags) +{ + return cpuinfo & CPUINFO_CSSC ? C_O1_I2(r, r, rS) : C_NotImplemented; +} + +static void tgen_smax(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, rrr, SMAX, type, a0, a1, a2); +} + +static void tgen_smaxi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_insn(s, minmax_imm, SMAXI, type, a0, a1, a2); +} + +static const TCGOutOpBinary outop_smax = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_sminmax, + .out_rrr = tgen_smax, + .out_rri = tgen_smaxi, +}; + +static void tgen_smin(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, rrr, SMIN, type, a0, a1, a2); +} + +static void tgen_smini(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_insn(s, minmax_imm, SMINI, type, a0, a1, a2); +} + +static const TCGOutOpBinary outop_smin = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_sminmax, + .out_rrr = tgen_smin, + .out_rri = tgen_smini, +}; + +static TCGConstraintSetIndex cset_uminmax(TCGType type, unsigned flags) +{ + return cpuinfo & CPUINFO_CSSC ? C_O1_I2(r, r, rU) : C_NotImplemented; +} + +static void tgen_umax(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, rrr, UMAX, type, a0, a1, a2); +} + +static void tgen_umaxi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_insn(s, minmax_imm, UMAXI, type, a0, a1, a2); +} + +static const TCGOutOpBinary outop_umax = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_uminmax, + .out_rrr = tgen_umax, + .out_rri = tgen_umaxi, +}; + +static void tgen_umin(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, rrr, UMIN, type, a0, a1, a2); +} + +static void tgen_umini(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_insn(s, minmax_imm, UMINI, type, a0, a1, a2); +} + +static const TCGOutOpBinary outop_umin = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_uminmax, + .out_rrr = tgen_umin, + .out_rri = tgen_umini, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2652,6 +2797,34 @@ static const TCGOutOpUnary outop_bswap64 = { .out_rr = tgen_bswap64, }; +static const TCGOutOpUnary outop_revbit8 = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_revbit32(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_insn(s, rr_sf, RBIT, TCG_TYPE_I32, a0, a1); + if (flags & TCG_BSWAP_OS) { + tcg_out_ext32s(s, a0, a0); + } +} + +static const TCGOutOpBswap outop_revbit32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_revbit32, +}; + +static void tgen_revbit64(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_insn(s, rr_sf, RBIT, TCG_TYPE_I64, a0, a1); +} + +static const TCGOutOpUnary outop_revbit64 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_revbit64, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_sub(s, type, a0, TCG_REG_XZR, a1); diff --git a/tcg/loongarch64/tcg-insn-defs.c.inc b/tcg/loongarch64/tcg-insn-defs.c.inc index 6bb8656fd8..e907fc755f 100644 --- a/tcg/loongarch64/tcg-insn-defs.c.inc +++ b/tcg/loongarch64/tcg-insn-defs.c.inc @@ -4,7 +4,7 @@ * * This file is auto-generated by genqemutcgdefs from * https://github.com/loongson-community/loongarch-opcodes, - * from commit 7f353fb69bd99ce6edfad7ad63948c4bb526f0bf. + * from commit 1b95bb8c443ad71e266120fd97bd227df2d8ecfc. * DO NOT EDIT. */ @@ -18,6 +18,10 @@ typedef enum { OPC_REVB_2H = 0x00003000, OPC_REVB_2W = 0x00003800, OPC_REVB_D = 0x00003c00, + OPC_REVBIT_4B = 0x00004800, + OPC_REVBIT_8B = 0x00004c00, + OPC_REVBIT_W = 0x00005000, + OPC_REVBIT_D = 0x00005400, OPC_SEXT_H = 0x00005800, OPC_SEXT_B = 0x00005c00, OPC_ADD_W = 0x00100000, @@ -545,14 +549,14 @@ typedef enum { OPC_XVLDI = 0x77e00000, } LoongArchInsn; -static int32_t __attribute__((unused)) -encode_d_slot(LoongArchInsn opc, uint32_t d) +static int32_t __attribute__((unused)) encode_d_slot(LoongArchInsn opc, + uint32_t d) { return opc | d; } -static int32_t __attribute__((unused)) -encode_dj_slots(LoongArchInsn opc, uint32_t d, uint32_t j) +static int32_t __attribute__((unused)) encode_dj_slots(LoongArchInsn opc, + uint32_t d, uint32_t j) { return opc | d | j << 5; } @@ -563,43 +567,43 @@ encode_djk_slots(LoongArchInsn opc, uint32_t d, uint32_t j, uint32_t k) return opc | d | j << 5 | k << 10; } -static int32_t __attribute__((unused)) -encode_djka_slots(LoongArchInsn opc, uint32_t d, uint32_t j, uint32_t k, - uint32_t a) +static int32_t __attribute__((unused)) encode_djka_slots(LoongArchInsn opc, + uint32_t d, uint32_t j, + uint32_t k, uint32_t a) { return opc | d | j << 5 | k << 10 | a << 15; } -static int32_t __attribute__((unused)) -encode_djkm_slots(LoongArchInsn opc, uint32_t d, uint32_t j, uint32_t k, - uint32_t m) +static int32_t __attribute__((unused)) encode_djkm_slots(LoongArchInsn opc, + uint32_t d, uint32_t j, + uint32_t k, uint32_t m) { return opc | d | j << 5 | k << 10 | m << 16; } -static int32_t __attribute__((unused)) -encode_djkn_slots(LoongArchInsn opc, uint32_t d, uint32_t j, uint32_t k, - uint32_t n) +static int32_t __attribute__((unused)) encode_djkn_slots(LoongArchInsn opc, + uint32_t d, uint32_t j, + uint32_t k, uint32_t n) { return opc | d | j << 5 | k << 10 | n << 18; } -static int32_t __attribute__((unused)) -encode_dk_slots(LoongArchInsn opc, uint32_t d, uint32_t k) +static int32_t __attribute__((unused)) encode_dk_slots(LoongArchInsn opc, + uint32_t d, uint32_t k) { return opc | d | k << 10; } -static int32_t __attribute__((unused)) -encode_dfj_insn(LoongArchInsn opc, TCGReg d, TCGReg fj) +static int32_t __attribute__((unused)) encode_dfj_insn(LoongArchInsn opc, + TCGReg d, TCGReg fj) { tcg_debug_assert(d >= 0 && d <= 0x1f); tcg_debug_assert(fj >= 0x20 && fj <= 0x3f); return encode_dj_slots(opc, d, fj & 0x1f); } -static int32_t __attribute__((unused)) -encode_dj_insn(LoongArchInsn opc, TCGReg d, TCGReg j) +static int32_t __attribute__((unused)) encode_dj_insn(LoongArchInsn opc, + TCGReg d, TCGReg j) { tcg_debug_assert(d >= 0 && d <= 0x1f); tcg_debug_assert(j >= 0 && j <= 0x1f); @@ -669,9 +673,10 @@ encode_djuk5_insn(LoongArchInsn opc, TCGReg d, TCGReg j, uint32_t uk5) return encode_djk_slots(opc, d, j, uk5); } -static int32_t __attribute__((unused)) -encode_djuk5um5_insn(LoongArchInsn opc, TCGReg d, TCGReg j, uint32_t uk5, - uint32_t um5) +static int32_t __attribute__((unused)) encode_djuk5um5_insn(LoongArchInsn opc, + TCGReg d, TCGReg j, + uint32_t uk5, + uint32_t um5) { tcg_debug_assert(d >= 0 && d <= 0x1f); tcg_debug_assert(j >= 0 && j <= 0x1f); @@ -689,9 +694,10 @@ encode_djuk6_insn(LoongArchInsn opc, TCGReg d, TCGReg j, uint32_t uk6) return encode_djk_slots(opc, d, j, uk6); } -static int32_t __attribute__((unused)) -encode_djuk6um6_insn(LoongArchInsn opc, TCGReg d, TCGReg j, uint32_t uk6, - uint32_t um6) +static int32_t __attribute__((unused)) encode_djuk6um6_insn(LoongArchInsn opc, + TCGReg d, TCGReg j, + uint32_t uk6, + uint32_t um6) { tcg_debug_assert(d >= 0 && d <= 0x1f); tcg_debug_assert(j >= 0 && j <= 0x1f); @@ -700,16 +706,16 @@ encode_djuk6um6_insn(LoongArchInsn opc, TCGReg d, TCGReg j, uint32_t uk6, return encode_djkm_slots(opc, d, j, uk6, um6); } -static int32_t __attribute__((unused)) -encode_dsj20_insn(LoongArchInsn opc, TCGReg d, int32_t sj20) +static int32_t __attribute__((unused)) encode_dsj20_insn(LoongArchInsn opc, + TCGReg d, int32_t sj20) { tcg_debug_assert(d >= 0 && d <= 0x1f); tcg_debug_assert(sj20 >= -0x80000 && sj20 <= 0x7ffff); return encode_dj_slots(opc, d, sj20 & 0xfffff); } -static int32_t __attribute__((unused)) -encode_dtj_insn(LoongArchInsn opc, TCGReg d, TCGReg tj) +static int32_t __attribute__((unused)) encode_dtj_insn(LoongArchInsn opc, + TCGReg d, TCGReg tj) { tcg_debug_assert(d >= 0 && d <= 0x1f); tcg_debug_assert(tj >= 0 && tj <= 0x3); @@ -770,16 +776,16 @@ encode_dxjuk3_insn(LoongArchInsn opc, TCGReg d, TCGReg xj, uint32_t uk3) return encode_djk_slots(opc, d, xj & 0x1f, uk3); } -static int32_t __attribute__((unused)) -encode_fdfj_insn(LoongArchInsn opc, TCGReg fd, TCGReg fj) +static int32_t __attribute__((unused)) encode_fdfj_insn(LoongArchInsn opc, + TCGReg fd, TCGReg fj) { tcg_debug_assert(fd >= 0x20 && fd <= 0x3f); tcg_debug_assert(fj >= 0x20 && fj <= 0x3f); return encode_dj_slots(opc, fd & 0x1f, fj & 0x1f); } -static int32_t __attribute__((unused)) -encode_fdj_insn(LoongArchInsn opc, TCGReg fd, TCGReg j) +static int32_t __attribute__((unused)) encode_fdj_insn(LoongArchInsn opc, + TCGReg fd, TCGReg j) { tcg_debug_assert(fd >= 0x20 && fd <= 0x3f); tcg_debug_assert(j >= 0 && j <= 0x1f); @@ -804,37 +810,37 @@ encode_fdjsk12_insn(LoongArchInsn opc, TCGReg fd, TCGReg j, int32_t sk12) return encode_djk_slots(opc, fd & 0x1f, j, sk12 & 0xfff); } -static int32_t __attribute__((unused)) -encode_sd10k16_insn(LoongArchInsn opc, int32_t sd10k16) +static int32_t __attribute__((unused)) encode_sd10k16_insn(LoongArchInsn opc, + int32_t sd10k16) { tcg_debug_assert(sd10k16 >= -0x2000000 && sd10k16 <= 0x1ffffff); return encode_dk_slots(opc, (sd10k16 >> 16) & 0x3ff, sd10k16 & 0xffff); } -static int32_t __attribute__((unused)) -encode_sd5k16_insn(LoongArchInsn opc, int32_t sd5k16) +static int32_t __attribute__((unused)) encode_sd5k16_insn(LoongArchInsn opc, + int32_t sd5k16) { tcg_debug_assert(sd5k16 >= -0x100000 && sd5k16 <= 0xfffff); return encode_dk_slots(opc, (sd5k16 >> 16) & 0x1f, sd5k16 & 0xffff); } -static int32_t __attribute__((unused)) -encode_tdj_insn(LoongArchInsn opc, TCGReg td, TCGReg j) +static int32_t __attribute__((unused)) encode_tdj_insn(LoongArchInsn opc, + TCGReg td, TCGReg j) { tcg_debug_assert(td >= 0 && td <= 0x3); tcg_debug_assert(j >= 0 && j <= 0x1f); return encode_dj_slots(opc, td, j); } -static int32_t __attribute__((unused)) -encode_ud15_insn(LoongArchInsn opc, uint32_t ud15) +static int32_t __attribute__((unused)) encode_ud15_insn(LoongArchInsn opc, + uint32_t ud15) { tcg_debug_assert(ud15 <= 0x7fff); return encode_d_slot(opc, ud15); } -static int32_t __attribute__((unused)) -encode_vdj_insn(LoongArchInsn opc, TCGReg vd, TCGReg j) +static int32_t __attribute__((unused)) encode_vdj_insn(LoongArchInsn opc, + TCGReg vd, TCGReg j) { tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); tcg_debug_assert(j >= 0 && j <= 0x1f); @@ -974,8 +980,8 @@ encode_vdsj13_insn(LoongArchInsn opc, TCGReg vd, int32_t sj13) return encode_dj_slots(opc, vd & 0x1f, sj13 & 0x1fff); } -static int32_t __attribute__((unused)) -encode_vdvj_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj) +static int32_t __attribute__((unused)) encode_vdvj_insn(LoongArchInsn opc, + TCGReg vd, TCGReg vj) { tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); tcg_debug_assert(vj >= 0x20 && vj <= 0x3f); @@ -1083,8 +1089,8 @@ encode_vdvjvkva_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj, TCGReg vk, return encode_djka_slots(opc, vd & 0x1f, vj & 0x1f, vk & 0x1f, va & 0x1f); } -static int32_t __attribute__((unused)) -encode_xdj_insn(LoongArchInsn opc, TCGReg xd, TCGReg j) +static int32_t __attribute__((unused)) encode_xdj_insn(LoongArchInsn opc, + TCGReg xd, TCGReg j) { tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); tcg_debug_assert(j >= 0 && j <= 0x1f); @@ -1206,8 +1212,8 @@ encode_xdsj13_insn(LoongArchInsn opc, TCGReg xd, int32_t sj13) return encode_dj_slots(opc, xd & 0x1f, sj13 & 0x1fff); } -static int32_t __attribute__((unused)) -encode_xdxj_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj) +static int32_t __attribute__((unused)) encode_xdxj_insn(LoongArchInsn opc, + TCGReg xd, TCGReg xj) { tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); @@ -1316,523 +1322,555 @@ encode_xdxjxkxa_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, TCGReg xk, } /* Emits the `movgr2scr td, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_movgr2scr(TCGContext *s, TCGReg td, TCGReg j) +static void __attribute__((unused)) tcg_out_opc_movgr2scr(TCGContext *s, + TCGReg td, TCGReg j) { tcg_out32(s, encode_tdj_insn(OPC_MOVGR2SCR, td, j)); } /* Emits the `movscr2gr d, tj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_movscr2gr(TCGContext *s, TCGReg d, TCGReg tj) +static void __attribute__((unused)) tcg_out_opc_movscr2gr(TCGContext *s, + TCGReg d, TCGReg tj) { tcg_out32(s, encode_dtj_insn(OPC_MOVSCR2GR, d, tj)); } /* Emits the `clz.w d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_clz_w(TCGContext *s, TCGReg d, TCGReg j) +static void __attribute__((unused)) tcg_out_opc_clz_w(TCGContext *s, TCGReg d, + TCGReg j) { tcg_out32(s, encode_dj_insn(OPC_CLZ_W, d, j)); } /* Emits the `ctz.w d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ctz_w(TCGContext *s, TCGReg d, TCGReg j) +static void __attribute__((unused)) tcg_out_opc_ctz_w(TCGContext *s, TCGReg d, + TCGReg j) { tcg_out32(s, encode_dj_insn(OPC_CTZ_W, d, j)); } /* Emits the `clz.d d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_clz_d(TCGContext *s, TCGReg d, TCGReg j) +static void __attribute__((unused)) tcg_out_opc_clz_d(TCGContext *s, TCGReg d, + TCGReg j) { tcg_out32(s, encode_dj_insn(OPC_CLZ_D, d, j)); } /* Emits the `ctz.d d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ctz_d(TCGContext *s, TCGReg d, TCGReg j) +static void __attribute__((unused)) tcg_out_opc_ctz_d(TCGContext *s, TCGReg d, + TCGReg j) { tcg_out32(s, encode_dj_insn(OPC_CTZ_D, d, j)); } /* Emits the `revb.2h d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_revb_2h(TCGContext *s, TCGReg d, TCGReg j) +static void __attribute__((unused)) tcg_out_opc_revb_2h(TCGContext *s, TCGReg d, + TCGReg j) { tcg_out32(s, encode_dj_insn(OPC_REVB_2H, d, j)); } /* Emits the `revb.2w d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_revb_2w(TCGContext *s, TCGReg d, TCGReg j) +static void __attribute__((unused)) tcg_out_opc_revb_2w(TCGContext *s, TCGReg d, + TCGReg j) { tcg_out32(s, encode_dj_insn(OPC_REVB_2W, d, j)); } /* Emits the `revb.d d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_revb_d(TCGContext *s, TCGReg d, TCGReg j) +static void __attribute__((unused)) tcg_out_opc_revb_d(TCGContext *s, TCGReg d, + TCGReg j) { tcg_out32(s, encode_dj_insn(OPC_REVB_D, d, j)); } +/* Emits the `revbit.4b d, j` instruction. */ +static void __attribute__((unused)) tcg_out_opc_revbit_4b(TCGContext *s, + TCGReg d, TCGReg j) +{ + tcg_out32(s, encode_dj_insn(OPC_REVBIT_4B, d, j)); +} + +/* Emits the `revbit.8b d, j` instruction. */ +static void __attribute__((unused)) tcg_out_opc_revbit_8b(TCGContext *s, + TCGReg d, TCGReg j) +{ + tcg_out32(s, encode_dj_insn(OPC_REVBIT_8B, d, j)); +} + +/* Emits the `revbit.w d, j` instruction. */ +static void __attribute__((unused)) tcg_out_opc_revbit_w(TCGContext *s, + TCGReg d, TCGReg j) +{ + tcg_out32(s, encode_dj_insn(OPC_REVBIT_W, d, j)); +} + +/* Emits the `revbit.d d, j` instruction. */ +static void __attribute__((unused)) tcg_out_opc_revbit_d(TCGContext *s, + TCGReg d, TCGReg j) +{ + tcg_out32(s, encode_dj_insn(OPC_REVBIT_D, d, j)); +} + /* Emits the `sext.h d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sext_h(TCGContext *s, TCGReg d, TCGReg j) +static void __attribute__((unused)) tcg_out_opc_sext_h(TCGContext *s, TCGReg d, + TCGReg j) { tcg_out32(s, encode_dj_insn(OPC_SEXT_H, d, j)); } /* Emits the `sext.b d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sext_b(TCGContext *s, TCGReg d, TCGReg j) +static void __attribute__((unused)) tcg_out_opc_sext_b(TCGContext *s, TCGReg d, + TCGReg j) { tcg_out32(s, encode_dj_insn(OPC_SEXT_B, d, j)); } /* Emits the `add.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_add_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_add_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_ADD_W, d, j, k)); } /* Emits the `add.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_add_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_add_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_ADD_D, d, j, k)); } /* Emits the `sub.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sub_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_sub_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_SUB_W, d, j, k)); } /* Emits the `sub.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sub_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_sub_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_SUB_D, d, j, k)); } /* Emits the `slt d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_slt(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_slt(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_SLT, d, j, k)); } /* Emits the `sltu d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sltu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_sltu(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_SLTU, d, j, k)); } /* Emits the `maskeqz d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_maskeqz(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_maskeqz(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MASKEQZ, d, j, k)); } /* Emits the `masknez d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_masknez(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_masknez(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MASKNEZ, d, j, k)); } /* Emits the `nor d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_nor(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_nor(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_NOR, d, j, k)); } /* Emits the `and d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_and(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_and(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_AND, d, j, k)); } /* Emits the `or d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_or(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_or(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_OR, d, j, k)); } /* Emits the `xor d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xor(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_xor(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_XOR, d, j, k)); } /* Emits the `orn d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_orn(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_orn(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_ORN, d, j, k)); } /* Emits the `andn d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_andn(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_andn(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_ANDN, d, j, k)); } /* Emits the `sll.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sll_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_sll_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_SLL_W, d, j, k)); } /* Emits the `srl.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_srl_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_srl_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_SRL_W, d, j, k)); } /* Emits the `sra.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sra_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_sra_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_SRA_W, d, j, k)); } /* Emits the `sll.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sll_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_sll_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_SLL_D, d, j, k)); } /* Emits the `srl.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_srl_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_srl_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_SRL_D, d, j, k)); } /* Emits the `sra.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sra_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_sra_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_SRA_D, d, j, k)); } /* Emits the `rotr.b d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_rotr_b(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_rotr_b(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_ROTR_B, d, j, k)); } /* Emits the `rotr.h d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_rotr_h(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_rotr_h(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_ROTR_H, d, j, k)); } /* Emits the `rotr.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_rotr_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_rotr_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_ROTR_W, d, j, k)); } /* Emits the `rotr.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_rotr_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_rotr_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_ROTR_D, d, j, k)); } /* Emits the `mul.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_mul_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_mul_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MUL_W, d, j, k)); } /* Emits the `mulh.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_mulh_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_mulh_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MULH_W, d, j, k)); } /* Emits the `mulh.wu d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_mulh_wu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_mulh_wu(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MULH_WU, d, j, k)); } /* Emits the `mul.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_mul_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_mul_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MUL_D, d, j, k)); } /* Emits the `mulh.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_mulh_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_mulh_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MULH_D, d, j, k)); } /* Emits the `mulh.du d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_mulh_du(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_mulh_du(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MULH_DU, d, j, k)); } /* Emits the `div.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_div_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_div_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_DIV_W, d, j, k)); } /* Emits the `mod.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_mod_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_mod_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MOD_W, d, j, k)); } /* Emits the `div.wu d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_div_wu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_div_wu(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_DIV_WU, d, j, k)); } /* Emits the `mod.wu d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_mod_wu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_mod_wu(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MOD_WU, d, j, k)); } /* Emits the `div.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_div_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_div_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_DIV_D, d, j, k)); } /* Emits the `mod.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_mod_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_mod_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MOD_D, d, j, k)); } /* Emits the `div.du d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_div_du(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_div_du(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_DIV_DU, d, j, k)); } /* Emits the `mod.du d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_mod_du(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_mod_du(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MOD_DU, d, j, k)); } /* Emits the `slli.w d, j, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_slli_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5) +static void __attribute__((unused)) tcg_out_opc_slli_w(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk5) { tcg_out32(s, encode_djuk5_insn(OPC_SLLI_W, d, j, uk5)); } /* Emits the `slli.d d, j, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_slli_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6) +static void __attribute__((unused)) tcg_out_opc_slli_d(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk6) { tcg_out32(s, encode_djuk6_insn(OPC_SLLI_D, d, j, uk6)); } /* Emits the `srli.w d, j, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_srli_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5) +static void __attribute__((unused)) tcg_out_opc_srli_w(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk5) { tcg_out32(s, encode_djuk5_insn(OPC_SRLI_W, d, j, uk5)); } /* Emits the `srli.d d, j, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_srli_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6) +static void __attribute__((unused)) tcg_out_opc_srli_d(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk6) { tcg_out32(s, encode_djuk6_insn(OPC_SRLI_D, d, j, uk6)); } /* Emits the `srai.w d, j, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_srai_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5) +static void __attribute__((unused)) tcg_out_opc_srai_w(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk5) { tcg_out32(s, encode_djuk5_insn(OPC_SRAI_W, d, j, uk5)); } /* Emits the `srai.d d, j, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_srai_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6) +static void __attribute__((unused)) tcg_out_opc_srai_d(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk6) { tcg_out32(s, encode_djuk6_insn(OPC_SRAI_D, d, j, uk6)); } /* Emits the `rotri.b d, j, uk3` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_rotri_b(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk3) +static void __attribute__((unused)) tcg_out_opc_rotri_b(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk3) { tcg_out32(s, encode_djuk3_insn(OPC_ROTRI_B, d, j, uk3)); } /* Emits the `rotri.h d, j, uk4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_rotri_h(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk4) +static void __attribute__((unused)) tcg_out_opc_rotri_h(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk4) { tcg_out32(s, encode_djuk4_insn(OPC_ROTRI_H, d, j, uk4)); } /* Emits the `rotri.w d, j, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_rotri_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5) +static void __attribute__((unused)) tcg_out_opc_rotri_w(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk5) { tcg_out32(s, encode_djuk5_insn(OPC_ROTRI_W, d, j, uk5)); } /* Emits the `rotri.d d, j, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_rotri_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6) +static void __attribute__((unused)) tcg_out_opc_rotri_d(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk6) { tcg_out32(s, encode_djuk6_insn(OPC_ROTRI_D, d, j, uk6)); } /* Emits the `bstrins.w d, j, uk5, um5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_bstrins_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5, - uint32_t um5) +static void __attribute__((unused)) tcg_out_opc_bstrins_w(TCGContext *s, + TCGReg d, TCGReg j, + uint32_t uk5, + uint32_t um5) { tcg_out32(s, encode_djuk5um5_insn(OPC_BSTRINS_W, d, j, uk5, um5)); } /* Emits the `bstrpick.w d, j, uk5, um5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_bstrpick_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5, - uint32_t um5) +static void __attribute__((unused)) tcg_out_opc_bstrpick_w(TCGContext *s, + TCGReg d, TCGReg j, + uint32_t uk5, + uint32_t um5) { tcg_out32(s, encode_djuk5um5_insn(OPC_BSTRPICK_W, d, j, uk5, um5)); } /* Emits the `bstrins.d d, j, uk6, um6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_bstrins_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6, - uint32_t um6) +static void __attribute__((unused)) tcg_out_opc_bstrins_d(TCGContext *s, + TCGReg d, TCGReg j, + uint32_t uk6, + uint32_t um6) { tcg_out32(s, encode_djuk6um6_insn(OPC_BSTRINS_D, d, j, uk6, um6)); } /* Emits the `bstrpick.d d, j, uk6, um6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_bstrpick_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6, - uint32_t um6) +static void __attribute__((unused)) tcg_out_opc_bstrpick_d(TCGContext *s, + TCGReg d, TCGReg j, + uint32_t uk6, + uint32_t um6) { tcg_out32(s, encode_djuk6um6_insn(OPC_BSTRPICK_D, d, j, uk6, um6)); } /* Emits the `fmov.d fd, fj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_fmov_d(TCGContext *s, TCGReg fd, TCGReg fj) +static void __attribute__((unused)) tcg_out_opc_fmov_d(TCGContext *s, TCGReg fd, + TCGReg fj) { tcg_out32(s, encode_fdfj_insn(OPC_FMOV_D, fd, fj)); } /* Emits the `movgr2fr.d fd, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_movgr2fr_d(TCGContext *s, TCGReg fd, TCGReg j) +static void __attribute__((unused)) tcg_out_opc_movgr2fr_d(TCGContext *s, + TCGReg fd, TCGReg j) { tcg_out32(s, encode_fdj_insn(OPC_MOVGR2FR_D, fd, j)); } /* Emits the `movfr2gr.d d, fj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_movfr2gr_d(TCGContext *s, TCGReg d, TCGReg fj) +static void __attribute__((unused)) tcg_out_opc_movfr2gr_d(TCGContext *s, + TCGReg d, TCGReg fj) { tcg_out32(s, encode_dfj_insn(OPC_MOVFR2GR_D, d, fj)); } /* Emits the `slti d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_slti(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_slti(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_SLTI, d, j, sk12)); } /* Emits the `sltui d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sltui(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_sltui(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_SLTUI, d, j, sk12)); } /* Emits the `addi.w d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_addi_w(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_addi_w(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_ADDI_W, d, j, sk12)); } /* Emits the `addi.d d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_addi_d(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_addi_d(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_ADDI_D, d, j, sk12)); } /* Emits the `cu52i.d d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_cu52i_d(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_cu52i_d(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_CU52I_D, d, j, sk12)); } /* Emits the `andi d, j, uk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_andi(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk12) +static void __attribute__((unused)) tcg_out_opc_andi(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk12) { tcg_out32(s, encode_djuk12_insn(OPC_ANDI, d, j, uk12)); } /* Emits the `ori d, j, uk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ori(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk12) +static void __attribute__((unused)) tcg_out_opc_ori(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk12) { tcg_out32(s, encode_djuk12_insn(OPC_ORI, d, j, uk12)); } /* Emits the `xori d, j, uk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xori(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk12) +static void __attribute__((unused)) tcg_out_opc_xori(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk12) { tcg_out32(s, encode_djuk12_insn(OPC_XORI, d, j, uk12)); } @@ -1845,9 +1883,9 @@ tcg_out_opc_vbitsel_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) } /* Emits the `xvbitsel.v xd, xj, xk, xa` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvbitsel_v(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk, - TCGReg xa) +static void __attribute__((unused)) tcg_out_opc_xvbitsel_v(TCGContext *s, + TCGReg xd, TCGReg xj, + TCGReg xk, TCGReg xa) { tcg_out32(s, encode_xdxjxkxa_insn(OPC_XVBITSEL_V, xd, xj, xk, xa)); } @@ -1874,22 +1912,22 @@ tcg_out_opc_addu16i_d(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) } /* Emits the `lu12i.w d, sj20` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_lu12i_w(TCGContext *s, TCGReg d, int32_t sj20) +static void __attribute__((unused)) tcg_out_opc_lu12i_w(TCGContext *s, TCGReg d, + int32_t sj20) { tcg_out32(s, encode_dsj20_insn(OPC_LU12I_W, d, sj20)); } /* Emits the `cu32i.d d, sj20` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_cu32i_d(TCGContext *s, TCGReg d, int32_t sj20) +static void __attribute__((unused)) tcg_out_opc_cu32i_d(TCGContext *s, TCGReg d, + int32_t sj20) { tcg_out32(s, encode_dsj20_insn(OPC_CU32I_D, d, sj20)); } /* Emits the `pcaddu2i d, sj20` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_pcaddu2i(TCGContext *s, TCGReg d, int32_t sj20) +static void __attribute__((unused)) tcg_out_opc_pcaddu2i(TCGContext *s, + TCGReg d, int32_t sj20) { tcg_out32(s, encode_dsj20_insn(OPC_PCADDU2I, d, sj20)); } @@ -1916,134 +1954,134 @@ tcg_out_opc_pcaddu18i(TCGContext *s, TCGReg d, int32_t sj20) } /* Emits the `ld.b d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ld_b(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_ld_b(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_LD_B, d, j, sk12)); } /* Emits the `ld.h d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ld_h(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_ld_h(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_LD_H, d, j, sk12)); } /* Emits the `ld.w d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ld_w(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_ld_w(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_LD_W, d, j, sk12)); } /* Emits the `ld.d d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ld_d(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_ld_d(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_LD_D, d, j, sk12)); } /* Emits the `st.b d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_st_b(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_st_b(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_ST_B, d, j, sk12)); } /* Emits the `st.h d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_st_h(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_st_h(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_ST_H, d, j, sk12)); } /* Emits the `st.w d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_st_w(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_st_w(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_ST_W, d, j, sk12)); } /* Emits the `st.d d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_st_d(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_st_d(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_ST_D, d, j, sk12)); } /* Emits the `ld.bu d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ld_bu(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_ld_bu(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_LD_BU, d, j, sk12)); } /* Emits the `ld.hu d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ld_hu(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_ld_hu(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_LD_HU, d, j, sk12)); } /* Emits the `ld.wu d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ld_wu(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_ld_wu(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_LD_WU, d, j, sk12)); } /* Emits the `fld.s fd, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_fld_s(TCGContext *s, TCGReg fd, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_fld_s(TCGContext *s, TCGReg fd, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_fdjsk12_insn(OPC_FLD_S, fd, j, sk12)); } /* Emits the `fst.s fd, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_fst_s(TCGContext *s, TCGReg fd, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_fst_s(TCGContext *s, TCGReg fd, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_fdjsk12_insn(OPC_FST_S, fd, j, sk12)); } /* Emits the `fld.d fd, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_fld_d(TCGContext *s, TCGReg fd, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_fld_d(TCGContext *s, TCGReg fd, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_fdjsk12_insn(OPC_FLD_D, fd, j, sk12)); } /* Emits the `fst.d fd, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_fst_d(TCGContext *s, TCGReg fd, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_fst_d(TCGContext *s, TCGReg fd, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_fdjsk12_insn(OPC_FST_D, fd, j, sk12)); } /* Emits the `vld vd, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vld(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_vld(TCGContext *s, TCGReg vd, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_vdjsk12_insn(OPC_VLD, vd, j, sk12)); } /* Emits the `vst vd, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vst(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_vst(TCGContext *s, TCGReg vd, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_vdjsk12_insn(OPC_VST, vd, j, sk12)); } /* Emits the `xvld xd, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvld(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_xvld(TCGContext *s, TCGReg xd, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_xdjsk12_insn(OPC_XVLD, xd, j, sk12)); } /* Emits the `xvst xd, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvst(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_xvst(TCGContext *s, TCGReg xd, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_xdjsk12_insn(OPC_XVST, xd, j, sk12)); } @@ -2077,33 +2115,37 @@ tcg_out_opc_vldrepl_b(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk12) } /* Emits the `vstelm.d vd, j, sk8, un1` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vstelm_d(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk8, - uint32_t un1) +static void __attribute__((unused)) tcg_out_opc_vstelm_d(TCGContext *s, + TCGReg vd, TCGReg j, + int32_t sk8, + uint32_t un1) { tcg_out32(s, encode_vdjsk8un1_insn(OPC_VSTELM_D, vd, j, sk8, un1)); } /* Emits the `vstelm.w vd, j, sk8, un2` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vstelm_w(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk8, - uint32_t un2) +static void __attribute__((unused)) tcg_out_opc_vstelm_w(TCGContext *s, + TCGReg vd, TCGReg j, + int32_t sk8, + uint32_t un2) { tcg_out32(s, encode_vdjsk8un2_insn(OPC_VSTELM_W, vd, j, sk8, un2)); } /* Emits the `vstelm.h vd, j, sk8, un3` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vstelm_h(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk8, - uint32_t un3) +static void __attribute__((unused)) tcg_out_opc_vstelm_h(TCGContext *s, + TCGReg vd, TCGReg j, + int32_t sk8, + uint32_t un3) { tcg_out32(s, encode_vdjsk8un3_insn(OPC_VSTELM_H, vd, j, sk8, un3)); } /* Emits the `vstelm.b vd, j, sk8, un4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vstelm_b(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk8, - uint32_t un4) +static void __attribute__((unused)) tcg_out_opc_vstelm_b(TCGContext *s, + TCGReg vd, TCGReg j, + int32_t sk8, + uint32_t un4) { tcg_out32(s, encode_vdjsk8un4_insn(OPC_VSTELM_B, vd, j, sk8, un4)); } @@ -2137,306 +2179,310 @@ tcg_out_opc_xvldrepl_b(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk12) } /* Emits the `xvstelm.d xd, j, sk8, un2` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvstelm_d(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk8, - uint32_t un2) +static void __attribute__((unused)) tcg_out_opc_xvstelm_d(TCGContext *s, + TCGReg xd, TCGReg j, + int32_t sk8, + uint32_t un2) { tcg_out32(s, encode_xdjsk8un2_insn(OPC_XVSTELM_D, xd, j, sk8, un2)); } /* Emits the `xvstelm.w xd, j, sk8, un3` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvstelm_w(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk8, - uint32_t un3) +static void __attribute__((unused)) tcg_out_opc_xvstelm_w(TCGContext *s, + TCGReg xd, TCGReg j, + int32_t sk8, + uint32_t un3) { tcg_out32(s, encode_xdjsk8un3_insn(OPC_XVSTELM_W, xd, j, sk8, un3)); } /* Emits the `xvstelm.h xd, j, sk8, un4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvstelm_h(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk8, - uint32_t un4) +static void __attribute__((unused)) tcg_out_opc_xvstelm_h(TCGContext *s, + TCGReg xd, TCGReg j, + int32_t sk8, + uint32_t un4) { tcg_out32(s, encode_xdjsk8un4_insn(OPC_XVSTELM_H, xd, j, sk8, un4)); } /* Emits the `xvstelm.b xd, j, sk8, un5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvstelm_b(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk8, - uint32_t un5) +static void __attribute__((unused)) tcg_out_opc_xvstelm_b(TCGContext *s, + TCGReg xd, TCGReg j, + int32_t sk8, + uint32_t un5) { tcg_out32(s, encode_xdjsk8un5_insn(OPC_XVSTELM_B, xd, j, sk8, un5)); } /* Emits the `ldx.b d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ldx_b(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_ldx_b(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_LDX_B, d, j, k)); } /* Emits the `ldx.h d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ldx_h(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_ldx_h(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_LDX_H, d, j, k)); } /* Emits the `ldx.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ldx_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_ldx_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_LDX_W, d, j, k)); } /* Emits the `ldx.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ldx_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_ldx_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_LDX_D, d, j, k)); } /* Emits the `stx.b d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_stx_b(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_stx_b(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_STX_B, d, j, k)); } /* Emits the `stx.h d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_stx_h(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_stx_h(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_STX_H, d, j, k)); } /* Emits the `stx.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_stx_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_stx_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_STX_W, d, j, k)); } /* Emits the `stx.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_stx_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_stx_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_STX_D, d, j, k)); } /* Emits the `ldx.bu d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ldx_bu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_ldx_bu(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_LDX_BU, d, j, k)); } /* Emits the `ldx.hu d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ldx_hu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_ldx_hu(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_LDX_HU, d, j, k)); } /* Emits the `ldx.wu d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ldx_wu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_ldx_wu(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_LDX_WU, d, j, k)); } /* Emits the `fldx.s fd, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_fldx_s(TCGContext *s, TCGReg fd, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_fldx_s(TCGContext *s, TCGReg fd, + TCGReg j, TCGReg k) { tcg_out32(s, encode_fdjk_insn(OPC_FLDX_S, fd, j, k)); } /* Emits the `fldx.d fd, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_fldx_d(TCGContext *s, TCGReg fd, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_fldx_d(TCGContext *s, TCGReg fd, + TCGReg j, TCGReg k) { tcg_out32(s, encode_fdjk_insn(OPC_FLDX_D, fd, j, k)); } /* Emits the `fstx.s fd, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_fstx_s(TCGContext *s, TCGReg fd, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_fstx_s(TCGContext *s, TCGReg fd, + TCGReg j, TCGReg k) { tcg_out32(s, encode_fdjk_insn(OPC_FSTX_S, fd, j, k)); } /* Emits the `fstx.d fd, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_fstx_d(TCGContext *s, TCGReg fd, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_fstx_d(TCGContext *s, TCGReg fd, + TCGReg j, TCGReg k) { tcg_out32(s, encode_fdjk_insn(OPC_FSTX_D, fd, j, k)); } /* Emits the `vldx vd, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vldx(TCGContext *s, TCGReg vd, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_vldx(TCGContext *s, TCGReg vd, + TCGReg j, TCGReg k) { tcg_out32(s, encode_vdjk_insn(OPC_VLDX, vd, j, k)); } /* Emits the `vstx vd, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vstx(TCGContext *s, TCGReg vd, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_vstx(TCGContext *s, TCGReg vd, + TCGReg j, TCGReg k) { tcg_out32(s, encode_vdjk_insn(OPC_VSTX, vd, j, k)); } /* Emits the `xvldx xd, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvldx(TCGContext *s, TCGReg xd, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_xvldx(TCGContext *s, TCGReg xd, + TCGReg j, TCGReg k) { tcg_out32(s, encode_xdjk_insn(OPC_XVLDX, xd, j, k)); } /* Emits the `xvstx xd, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvstx(TCGContext *s, TCGReg xd, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_xvstx(TCGContext *s, TCGReg xd, + TCGReg j, TCGReg k) { tcg_out32(s, encode_xdjk_insn(OPC_XVSTX, xd, j, k)); } /* Emits the `dbar ud15` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_dbar(TCGContext *s, uint32_t ud15) +static void __attribute__((unused)) tcg_out_opc_dbar(TCGContext *s, + uint32_t ud15) { tcg_out32(s, encode_ud15_insn(OPC_DBAR, ud15)); } /* Emits the `jiscr0 sd5k16` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_jiscr0(TCGContext *s, int32_t sd5k16) +static void __attribute__((unused)) tcg_out_opc_jiscr0(TCGContext *s, + int32_t sd5k16) { tcg_out32(s, encode_sd5k16_insn(OPC_JISCR0, sd5k16)); } /* Emits the `jiscr1 sd5k16` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_jiscr1(TCGContext *s, int32_t sd5k16) +static void __attribute__((unused)) tcg_out_opc_jiscr1(TCGContext *s, + int32_t sd5k16) { tcg_out32(s, encode_sd5k16_insn(OPC_JISCR1, sd5k16)); } /* Emits the `jirl d, j, sk16` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_jirl(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +static void __attribute__((unused)) tcg_out_opc_jirl(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk16) { tcg_out32(s, encode_djsk16_insn(OPC_JIRL, d, j, sk16)); } /* Emits the `b sd10k16` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_b(TCGContext *s, int32_t sd10k16) +static void __attribute__((unused)) tcg_out_opc_b(TCGContext *s, + int32_t sd10k16) { tcg_out32(s, encode_sd10k16_insn(OPC_B, sd10k16)); } /* Emits the `bl sd10k16` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_bl(TCGContext *s, int32_t sd10k16) +static void __attribute__((unused)) tcg_out_opc_bl(TCGContext *s, + int32_t sd10k16) { tcg_out32(s, encode_sd10k16_insn(OPC_BL, sd10k16)); } /* Emits the `beq d, j, sk16` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_beq(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +static void __attribute__((unused)) tcg_out_opc_beq(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk16) { tcg_out32(s, encode_djsk16_insn(OPC_BEQ, d, j, sk16)); } /* Emits the `bne d, j, sk16` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_bne(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +static void __attribute__((unused)) tcg_out_opc_bne(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk16) { tcg_out32(s, encode_djsk16_insn(OPC_BNE, d, j, sk16)); } /* Emits the `bgt d, j, sk16` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_bgt(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +static void __attribute__((unused)) tcg_out_opc_bgt(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk16) { tcg_out32(s, encode_djsk16_insn(OPC_BGT, d, j, sk16)); } /* Emits the `ble d, j, sk16` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ble(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +static void __attribute__((unused)) tcg_out_opc_ble(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk16) { tcg_out32(s, encode_djsk16_insn(OPC_BLE, d, j, sk16)); } /* Emits the `bgtu d, j, sk16` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_bgtu(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +static void __attribute__((unused)) tcg_out_opc_bgtu(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk16) { tcg_out32(s, encode_djsk16_insn(OPC_BGTU, d, j, sk16)); } /* Emits the `bleu d, j, sk16` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_bleu(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +static void __attribute__((unused)) tcg_out_opc_bleu(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk16) { tcg_out32(s, encode_djsk16_insn(OPC_BLEU, d, j, sk16)); } /* Emits the `vseq.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vseq_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vseq_b(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSEQ_B, vd, vj, vk)); } /* Emits the `vseq.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vseq_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vseq_h(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSEQ_H, vd, vj, vk)); } /* Emits the `vseq.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vseq_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vseq_w(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSEQ_W, vd, vj, vk)); } /* Emits the `vseq.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vseq_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vseq_d(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSEQ_D, vd, vj, vk)); } /* Emits the `vsle.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsle_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsle_b(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLE_B, vd, vj, vk)); } /* Emits the `vsle.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsle_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsle_h(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLE_H, vd, vj, vk)); } /* Emits the `vsle.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsle_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsle_w(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLE_W, vd, vj, vk)); } /* Emits the `vsle.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsle_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsle_d(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLE_D, vd, vj, vk)); } @@ -2470,29 +2516,29 @@ tcg_out_opc_vsle_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) } /* Emits the `vslt.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vslt_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vslt_b(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLT_B, vd, vj, vk)); } /* Emits the `vslt.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vslt_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vslt_h(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLT_H, vd, vj, vk)); } /* Emits the `vslt.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vslt_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vslt_w(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLT_W, vd, vj, vk)); } /* Emits the `vslt.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vslt_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vslt_d(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLT_D, vd, vj, vk)); } @@ -2526,57 +2572,57 @@ tcg_out_opc_vslt_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) } /* Emits the `vadd.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vadd_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vadd_b(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VADD_B, vd, vj, vk)); } /* Emits the `vadd.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vadd_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vadd_h(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VADD_H, vd, vj, vk)); } /* Emits the `vadd.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vadd_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vadd_w(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VADD_W, vd, vj, vk)); } /* Emits the `vadd.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vadd_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vadd_d(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VADD_D, vd, vj, vk)); } /* Emits the `vsub.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsub_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsub_b(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSUB_B, vd, vj, vk)); } /* Emits the `vsub.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsub_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsub_h(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSUB_H, vd, vj, vk)); } /* Emits the `vsub.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsub_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsub_w(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSUB_W, vd, vj, vk)); } /* Emits the `vsub.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsub_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsub_d(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSUB_D, vd, vj, vk)); } @@ -2694,57 +2740,57 @@ tcg_out_opc_vssub_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) } /* Emits the `vmax.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmax_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmax_b(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMAX_B, vd, vj, vk)); } /* Emits the `vmax.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmax_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmax_h(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMAX_H, vd, vj, vk)); } /* Emits the `vmax.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmax_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmax_w(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMAX_W, vd, vj, vk)); } /* Emits the `vmax.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmax_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmax_d(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMAX_D, vd, vj, vk)); } /* Emits the `vmin.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmin_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmin_b(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMIN_B, vd, vj, vk)); } /* Emits the `vmin.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmin_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmin_h(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMIN_H, vd, vj, vk)); } /* Emits the `vmin.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmin_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmin_w(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMIN_W, vd, vj, vk)); } /* Emits the `vmin.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmin_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmin_d(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMIN_D, vd, vj, vk)); } @@ -2806,113 +2852,113 @@ tcg_out_opc_vmin_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) } /* Emits the `vmul.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmul_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmul_b(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMUL_B, vd, vj, vk)); } /* Emits the `vmul.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmul_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmul_h(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMUL_H, vd, vj, vk)); } /* Emits the `vmul.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmul_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmul_w(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMUL_W, vd, vj, vk)); } /* Emits the `vmul.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmul_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmul_d(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMUL_D, vd, vj, vk)); } /* Emits the `vsll.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsll_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsll_b(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLL_B, vd, vj, vk)); } /* Emits the `vsll.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsll_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsll_h(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLL_H, vd, vj, vk)); } /* Emits the `vsll.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsll_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsll_w(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLL_W, vd, vj, vk)); } /* Emits the `vsll.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsll_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsll_d(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLL_D, vd, vj, vk)); } /* Emits the `vsrl.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrl_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsrl_b(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSRL_B, vd, vj, vk)); } /* Emits the `vsrl.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrl_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsrl_h(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSRL_H, vd, vj, vk)); } /* Emits the `vsrl.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrl_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsrl_w(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSRL_W, vd, vj, vk)); } /* Emits the `vsrl.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrl_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsrl_d(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSRL_D, vd, vj, vk)); } /* Emits the `vsra.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsra_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsra_b(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSRA_B, vd, vj, vk)); } /* Emits the `vsra.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsra_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsra_h(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSRA_H, vd, vj, vk)); } /* Emits the `vsra.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsra_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsra_w(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSRA_W, vd, vj, vk)); } /* Emits the `vsra.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsra_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsra_d(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSRA_D, vd, vj, vk)); } @@ -2974,29 +3020,29 @@ tcg_out_opc_vreplve_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg k) } /* Emits the `vand.v vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vand_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vand_v(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VAND_V, vd, vj, vk)); } /* Emits the `vor.v vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vor_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vor_v(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VOR_V, vd, vj, vk)); } /* Emits the `vxor.v vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vxor_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vxor_v(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VXOR_V, vd, vj, vk)); } /* Emits the `vnor.v vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vnor_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vnor_v(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VNOR_V, vd, vj, vk)); } @@ -3009,8 +3055,8 @@ tcg_out_opc_vandn_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) } /* Emits the `vorn.v vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vorn_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vorn_v(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VORN_V, vd, vj, vk)); } @@ -3324,29 +3370,29 @@ tcg_out_opc_vmini_du(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) } /* Emits the `vneg.b vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vneg_b(TCGContext *s, TCGReg vd, TCGReg vj) +static void __attribute__((unused)) tcg_out_opc_vneg_b(TCGContext *s, TCGReg vd, + TCGReg vj) { tcg_out32(s, encode_vdvj_insn(OPC_VNEG_B, vd, vj)); } /* Emits the `vneg.h vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vneg_h(TCGContext *s, TCGReg vd, TCGReg vj) +static void __attribute__((unused)) tcg_out_opc_vneg_h(TCGContext *s, TCGReg vd, + TCGReg vj) { tcg_out32(s, encode_vdvj_insn(OPC_VNEG_H, vd, vj)); } /* Emits the `vneg.w vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vneg_w(TCGContext *s, TCGReg vd, TCGReg vj) +static void __attribute__((unused)) tcg_out_opc_vneg_w(TCGContext *s, TCGReg vd, + TCGReg vj) { tcg_out32(s, encode_vdvj_insn(OPC_VNEG_W, vd, vj)); } /* Emits the `vneg.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vneg_d(TCGContext *s, TCGReg vd, TCGReg vj) +static void __attribute__((unused)) tcg_out_opc_vneg_d(TCGContext *s, TCGReg vd, + TCGReg vj) { tcg_out32(s, encode_vdvj_insn(OPC_VNEG_D, vd, vj)); } @@ -3702,8 +3748,8 @@ tcg_out_opc_vandi_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) } /* Emits the `vori.b vd, vj, uk8` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vori_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) +static void __attribute__((unused)) tcg_out_opc_vori_b(TCGContext *s, TCGReg vd, + TCGReg vj, uint32_t uk8) { tcg_out32(s, encode_vdvjuk8_insn(OPC_VORI_B, vd, vj, uk8)); } @@ -3723,8 +3769,8 @@ tcg_out_opc_vnori_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) } /* Emits the `vldi vd, sj13` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vldi(TCGContext *s, TCGReg vd, int32_t sj13) +static void __attribute__((unused)) tcg_out_opc_vldi(TCGContext *s, TCGReg vd, + int32_t sj13) { tcg_out32(s, encode_vdsj13_insn(OPC_VLDI, vd, sj13)); } @@ -4325,8 +4371,8 @@ tcg_out_opc_xvand_v(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) } /* Emits the `xvor.v xd, xj, xk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvor_v(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +static void __attribute__((unused)) tcg_out_opc_xvor_v(TCGContext *s, TCGReg xd, + TCGReg xj, TCGReg xk) { tcg_out32(s, encode_xdxjxk_insn(OPC_XVOR_V, xd, xj, xk)); } @@ -4668,29 +4714,29 @@ tcg_out_opc_xvmini_du(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) } /* Emits the `xvneg.b xd, xj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvneg_b(TCGContext *s, TCGReg xd, TCGReg xj) +static void __attribute__((unused)) tcg_out_opc_xvneg_b(TCGContext *s, + TCGReg xd, TCGReg xj) { tcg_out32(s, encode_xdxj_insn(OPC_XVNEG_B, xd, xj)); } /* Emits the `xvneg.h xd, xj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvneg_h(TCGContext *s, TCGReg xd, TCGReg xj) +static void __attribute__((unused)) tcg_out_opc_xvneg_h(TCGContext *s, + TCGReg xd, TCGReg xj) { tcg_out32(s, encode_xdxj_insn(OPC_XVNEG_H, xd, xj)); } /* Emits the `xvneg.w xd, xj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvneg_w(TCGContext *s, TCGReg xd, TCGReg xj) +static void __attribute__((unused)) tcg_out_opc_xvneg_w(TCGContext *s, + TCGReg xd, TCGReg xj) { tcg_out32(s, encode_xdxj_insn(OPC_XVNEG_W, xd, xj)); } /* Emits the `xvneg.d xd, xj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvneg_d(TCGContext *s, TCGReg xd, TCGReg xj) +static void __attribute__((unused)) tcg_out_opc_xvneg_d(TCGContext *s, + TCGReg xd, TCGReg xj) { tcg_out32(s, encode_xdxj_insn(OPC_XVNEG_D, xd, xj)); } @@ -5060,8 +5106,8 @@ tcg_out_opc_xvnori_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk8) } /* Emits the `xvldi xd, sj13` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvldi(TCGContext *s, TCGReg xd, int32_t sj13) +static void __attribute__((unused)) tcg_out_opc_xvldi(TCGContext *s, TCGReg xd, + int32_t sj13) { tcg_out32(s, encode_xdsj13_insn(OPC_XVLDI, xd, sj13)); } diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 182dcfd5eb..f65496a040 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1804,6 +1804,22 @@ static void tcg_out_set_borrow(TCGContext *s) g_assert_not_reached(); } +static const TCGOutOpBinary outop_smax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_smin = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umin = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1866,6 +1882,46 @@ static const TCGOutOpUnary outop_bswap64 = { .out_rr = tgen_bswap64, }; +static void tgen_revbit8(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_revbit_4b(s, a0, a1); + } else { + tcg_out_opc_revbit_8b(s, a0, a1); + } +} + +static const TCGOutOpUnary outop_revbit8 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_revbit8; +}; + +static void tgen_revbit32(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_opc_revbit_w(s, a0, a1); + + /* All 32-bit values are computed sign-extended in the register. */ + if (type == TCG_TYPE_I64 && (flags & TCG_BSWAP_OZ)) { + tcg_out_ext32u(s, a0, a0); + } +} + +static const TCGOutOpBswap outop_revbit32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_revbit32, +}; + +static void tgen_revbit64(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_opc_revbit_d(s, a0, a1); +} + +static const TCGOutOpUnary outop_revbit64 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_revbit64, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_sub(s, type, a0, TCG_REG_ZERO, a1); diff --git a/tcg/optimize.c b/tcg/optimize.c index fcdef25bee..d291c844ca 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -534,6 +534,20 @@ static uint64_t do_constant_folding_2(TCGOpcode op, TCGType type, case INDEX_op_bswap64: return bswap64(x); + case INDEX_op_revbit8: + /* Note the host-utils.h revbit8 operates on uint8_t. */ + if (type == TCG_TYPE_I32) { + return bswap32(revbit32(x)); + } + return bswap64(revbit64(x)); + + case INDEX_op_revbit32: + x = revbit32(x); + return y & TCG_BSWAP_OS ? (int32_t)x : x; + + case INDEX_op_revbit64: + return revbit64(x); + case INDEX_op_ext_i32_i64: return (int32_t)x; @@ -583,6 +597,30 @@ static uint64_t do_constant_folding_2(TCGOpcode op, TCGType type, } return (uint64_t)x % ((uint64_t)y ? : 1); + case INDEX_op_smax: + if (type == TCG_TYPE_I32) { + return MAX((int32_t)x, (int32_t)y); + } + return MAX((int64_t)x, (int64_t)y); + + case INDEX_op_smin: + if (type == TCG_TYPE_I32) { + return MIN((int32_t)x, (int32_t)y); + } + return MIN((int64_t)x, (int64_t)y); + + case INDEX_op_umax: + if (type == TCG_TYPE_I32) { + return MAX((uint32_t)x, (uint32_t)y); + } + return MAX((uint64_t)x, (uint64_t)y); + + case INDEX_op_umin: + if (type == TCG_TYPE_I32) { + return MIN((uint32_t)x, (uint32_t)y); + } + return MIN((uint64_t)x, (uint64_t)y); + default: g_assert_not_reached(); } @@ -1483,7 +1521,26 @@ static bool fold_bswap(OptContext *ctx, TCGOp *op) { uint64_t z_mask, o_mask, s_mask; TempOptInfo *t1 = arg_info(op->args[1]); - int flags = op->args[2]; + int flags = 0; + + switch (op->opc) { + case INDEX_op_bswap16: + flags = op->args[2]; + s_mask = INT16_MIN; + break; + case INDEX_op_bswap32: + case INDEX_op_revbit32: + flags = op->args[2]; + s_mask = INT32_MIN; + break; + case INDEX_op_bswap64: + case INDEX_op_revbit8: + case INDEX_op_revbit64: + s_mask = 0; + break; + default: + g_assert_not_reached(); + } if (ti_is_const(t1)) { return tcg_opt_gen_movi(ctx, op, op->args[0], @@ -1491,39 +1548,16 @@ static bool fold_bswap(OptContext *ctx, TCGOp *op) ti_const_val(t1), flags)); } - z_mask = t1->z_mask; - o_mask = t1->o_mask; - s_mask = 0; + z_mask = do_constant_folding(op->opc, ctx->type, t1->z_mask, flags); + o_mask = do_constant_folding(op->opc, ctx->type, t1->o_mask, flags); - switch (op->opc) { - case INDEX_op_bswap16: - z_mask = bswap16(z_mask); - o_mask = bswap16(o_mask); - if (flags & TCG_BSWAP_OS) { - z_mask = (int16_t)z_mask; - o_mask = (int16_t)o_mask; - s_mask = INT16_MIN; - } else if (!(flags & TCG_BSWAP_OZ)) { - z_mask |= MAKE_64BIT_MASK(16, 48); + if (flags & TCG_BSWAP_OS) { + /* s_mask set */ + } else { + if (!(flags & TCG_BSWAP_OZ)) { + z_mask |= s_mask << 1; } - break; - case INDEX_op_bswap32: - z_mask = bswap32(z_mask); - o_mask = bswap32(o_mask); - if (flags & TCG_BSWAP_OS) { - z_mask = (int32_t)z_mask; - o_mask = (int32_t)o_mask; - s_mask = INT32_MIN; - } else if (!(flags & TCG_BSWAP_OZ)) { - z_mask |= MAKE_64BIT_MASK(32, 32); - } - break; - case INDEX_op_bswap64: - z_mask = bswap64(z_mask); - o_mask = bswap64(o_mask); - break; - default: - g_assert_not_reached(); + s_mask = 0; } return fold_masks_zos(ctx, op, z_mask, o_mask, s_mask); @@ -2091,6 +2125,16 @@ static bool fold_mb(OptContext *ctx, TCGOp *op) return true; } +static bool fold_minmax(OptContext *ctx, TCGOp *op, uint64_t bound) +{ + if (fold_const2_commutative(ctx, op) || + fold_xi_to_i(ctx, op, bound) || + fold_xx_to_x(ctx, op)) { + return true; + } + return finish_folding(ctx, op); +} + static bool fold_mov(OptContext *ctx, TCGOp *op) { return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[1]); @@ -2152,7 +2196,7 @@ static bool fold_movcond(OptContext *ctx, TCGOp *op) static bool fold_mul(OptContext *ctx, TCGOp *op) { - if (fold_const2(ctx, op) || + if (fold_const2_commutative(ctx, op) || fold_xi_to_i(ctx, op, 0) || fold_xi_to_x(ctx, op, 1)) { return true; @@ -2656,8 +2700,17 @@ static bool fold_shift(OptContext *ctx, TCGOp *op) z_mask = do_constant_folding(op->opc, ctx->type, z_mask, sh); o_mask = do_constant_folding(op->opc, ctx->type, o_mask, sh); - s_mask = do_constant_folding(op->opc, ctx->type, s_mask, sh); + if (op->opc == INDEX_op_shr) { + /* + * Logical right shift will force the sign bit zero. + * Don't bother computing s_mask and let fold_masks + * recompute from z_mask. + */ + return fold_masks_zo(ctx, op, z_mask, o_mask); + } + + s_mask = do_constant_folding(op->opc, ctx->type, s_mask, sh); return fold_masks_zos(ctx, op, z_mask, o_mask, s_mask); } @@ -3095,6 +3148,9 @@ void tcg_optimize(TCGContext *s) case INDEX_op_bswap16: case INDEX_op_bswap32: case INDEX_op_bswap64: + case INDEX_op_revbit8: + case INDEX_op_revbit32: + case INDEX_op_revbit64: done = fold_bswap(&ctx, op); break; case INDEX_op_clz: @@ -3236,6 +3292,14 @@ void tcg_optimize(TCGContext *s) case INDEX_op_sextract: done = fold_sextract(&ctx, op); break; + case INDEX_op_smax: + done = fold_minmax(&ctx, op, (ctx.type == TCG_TYPE_I32 + ? INT32_MAX : INT64_MAX)); + break; + case INDEX_op_smin: + done = fold_minmax(&ctx, op, (ctx.type == TCG_TYPE_I32 + ? INT32_MIN : INT64_MIN)); + break; case INDEX_op_sub: done = fold_sub(&ctx, op); break; @@ -3251,6 +3315,16 @@ void tcg_optimize(TCGContext *s) case INDEX_op_sub_vec: done = fold_sub_vec(&ctx, op); break; + case INDEX_op_umax: + /* + * Note that 32-bit constants are stored sign extended, + * so (int32_t)UINT32_MAX == -1. + */ + done = fold_minmax(&ctx, op, -1); + break; + case INDEX_op_umin: + done = fold_minmax(&ctx, op, 0); + break; case INDEX_op_xor: case INDEX_op_xor_vec: done = fold_xor(&ctx, op); diff --git a/tcg/ppc64/tcg-target.c.inc b/tcg/ppc64/tcg-target.c.inc index b54afa0b6d..cd1c234367 100644 --- a/tcg/ppc64/tcg-target.c.inc +++ b/tcg/ppc64/tcg-target.c.inc @@ -3281,6 +3281,22 @@ static void tcg_out_set_borrow(TCGContext *s) tcg_out32(s, ADDIC | TAI(TCG_REG_R0, TCG_REG_R0, 0)); } +static const TCGOutOpBinary outop_smax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_smin = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umin = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -3421,6 +3437,18 @@ static const TCGOutOpUnary outop_bswap64 = { .out_rr = tgen_bswap64, }; +static const TCGOutOpUnary outop_revbit8 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBswap outop_revbit32 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpUnary outop_revbit64 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tcg_out32(s, NEG | RT(a0) | RA(a1)); diff --git a/tcg/region.c b/tcg/region.c index 5d4be1453b..1145f35085 100644 --- a/tcg/region.c +++ b/tcg/region.c @@ -360,30 +360,30 @@ static void tcg_region_assign(TCGContext *s, size_t curr_region) static bool tcg_region_alloc__locked(TCGContext *s) { if (region.current == region.n) { - return true; + return false; } tcg_region_assign(s, region.current); region.current++; - return false; + return true; } /* * Request a new region once the one in use has filled up. - * Returns true on error. + * Returns true on success. */ bool tcg_region_alloc(TCGContext *s) { - bool err; + bool ok; /* read the region size now; alloc__locked will overwrite it on success */ size_t size_full = s->code_gen_buffer_size; qemu_mutex_lock(®ion.lock); - err = tcg_region_alloc__locked(s); - if (!err) { + ok = tcg_region_alloc__locked(s); + if (ok) { region.agg_size_full += size_full - TCG_HIGHWATER; } qemu_mutex_unlock(®ion.lock); - return err; + return ok; } /* @@ -392,15 +392,35 @@ bool tcg_region_alloc(TCGContext *s) */ static void tcg_region_initial_alloc__locked(TCGContext *s) { - bool err = tcg_region_alloc__locked(s); - g_assert(!err); + bool ok = tcg_region_alloc__locked(s); + g_assert(ok); } -void tcg_region_initial_alloc(TCGContext *s) +void tcg_region_thread_initial_alloc(TCGContext *s) { + bool ok; + qemu_mutex_lock(®ion.lock); - tcg_region_initial_alloc__locked(s); + ok = tcg_region_alloc__locked(s); qemu_mutex_unlock(®ion.lock); + + /* + * A vCPU hotplug may happen at any time. When the new thread is + * started, the region pool may be exhausted. At this point in + * the new thread call stack, we are not in a position to fix this. + * Leave code_gen_ptr NULL, so that this thread's first call to + * tcg_tb_alloc() returns NULL, so that the translator performs + * a tb_flush() and retry. + * + * During the tb_flush(), tcg_region_reset_all() will assign a + * new region to all contexts, including this one. + */ + if (!ok) { + s->code_gen_buffer = NULL; + s->code_gen_ptr = NULL; + s->code_gen_buffer_size = 0; + s->code_gen_highwater = NULL; + } } /* Call from a safe-work context */ diff --git a/tcg/riscv64/tcg-target.c.inc b/tcg/riscv64/tcg-target.c.inc index 76dd4fca97..2ce9d47a63 100644 --- a/tcg/riscv64/tcg-target.c.inc +++ b/tcg/riscv64/tcg-target.c.inc @@ -233,6 +233,10 @@ typedef enum { OPC_CPOPW = 0x6020101b, OPC_CTZ = 0x60101013, OPC_CTZW = 0x6010101b, + OPC_MAX = 0x0a006033, + OPC_MAXU = 0x0a007033, + OPC_MIN = 0x0a004033, + OPC_MINU = 0x0a005033, OPC_ORN = 0x40006033, OPC_REV8 = 0x6b805013, OPC_ROL = 0x60001033, @@ -246,6 +250,9 @@ typedef enum { OPC_XNOR = 0x40004033, OPC_ZEXT_H = 0x0800403b, + /* Zbkb: Bit Manipulation for Cryptography */ + OPC_BREV8 = 0x68705013, + /* Zicond: integer conditional operations */ OPC_CZERO_EQZ = 0x0e005033, OPC_CZERO_NEZ = 0x0e007033, @@ -2401,6 +2408,54 @@ static void tcg_out_set_borrow(TCGContext *s) g_assert_not_reached(); } +static void tgen_smax(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_MAX, a0, a1, a2); +} + +static const TCGOutOpBinary outop_smax = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_zbb_rrr, + .out_rrr = tgen_smax, +}; + +static void tgen_smin(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_MIN, a0, a1, a2); +} + +static const TCGOutOpBinary outop_smin = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_zbb_rrr, + .out_rrr = tgen_smin, +}; + +static void tgen_umax(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_MAXU, a0, a1, a2); +} + +static const TCGOutOpBinary outop_umax = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_zbb_rrr, + .out_rrr = tgen_umax, +}; + +static void tgen_umin(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_MINU, a0, a1, a2); +} + +static const TCGOutOpBinary outop_umin = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_zbb_rrr, + .out_rrr = tgen_umin, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2469,6 +2524,30 @@ static const TCGOutOpUnary outop_bswap64 = { .out_rr = tgen_bswap64, }; +static TCGConstraintSetIndex cset_revbit8(TCGType type, unsigned flags) +{ + return cpuinfo & CPUINFO_ZBKB ? C_O1_I1(r, r) : C_NotImplemented; +} + +static void tgen_revbit8(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_opc_imm(s, OPC_BREV8, a0, a1, 0); +} + +static const TCGOutOpUnary outop_revbit8 = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_revbit8, + .out_rr = tgen_revbit8, +}; + +static const TCGOutOpBswap outop_revbit32 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpUnary outop_revbit64 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_sub(s, type, a0, TCG_REG_ZERO, a1); diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 84a9e73a46..4d1a779c47 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2950,6 +2950,22 @@ static void tcg_out_set_borrow(TCGContext *s) tcg_out_insn(s, RR, CLR, TCG_REG_R0, TCG_REG_R0); /* cc = 0 */ } +static const TCGOutOpBinary outop_smax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_smin = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umin = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -3020,6 +3036,18 @@ static const TCGOutOpUnary outop_bswap64 = { .out_rr = tgen_bswap64, }; +static const TCGOutOpUnary outop_revbit8 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBswap outop_revbit32 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpUnary outop_revbit64 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { if (type == TCG_TYPE_I32) { diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 5e5c3f1cda..35cd14a5b6 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1917,6 +1917,22 @@ static void tcg_out_set_borrow(TCGContext *s) tcg_out_set_carry(s); /* borrow == carry */ } +static const TCGOutOpBinary outop_smax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_smin = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umin = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1947,6 +1963,18 @@ static const TCGOutOpUnary outop_bswap64 = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpUnary outop_revbit8 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBswap outop_revbit32 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpUnary outop_revbit64 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_sub(s, type, a0, TCG_REG_G0, a1); diff --git a/tcg/tcg-internal.h b/tcg/tcg-internal.h index c0997ab224..e35440dc8c 100644 --- a/tcg/tcg-internal.h +++ b/tcg/tcg-internal.h @@ -42,7 +42,7 @@ extern unsigned int tcg_max_ctxs; void tcg_region_init(size_t tb_size, int splitwx, unsigned max_threads); bool tcg_region_alloc(TCGContext *s); -void tcg_region_initial_alloc(TCGContext *s); +void tcg_region_thread_initial_alloc(TCGContext *s); void tcg_region_prologue_set(TCGContext *s); static inline void *tcg_call_func(TCGOp *op) diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index bc323e2500..27f3807b00 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -2399,20 +2399,6 @@ void tcg_gen_gvec_usadd(unsigned vece, uint32_t dofs, uint32_t aofs, tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g[vece]); } -static void tcg_gen_ussub_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - TCGv_i32 min = tcg_constant_i32(0); - tcg_gen_sub_i32(d, a, b); - tcg_gen_movcond_i32(TCG_COND_LTU, d, a, b, min, d); -} - -static void tcg_gen_ussub_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) -{ - TCGv_i64 min = tcg_constant_i64(0); - tcg_gen_sub_i64(d, a, b); - tcg_gen_movcond_i64(TCG_COND_LTU, d, a, b, min, d); -} - void tcg_gen_gvec_ussub(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t oprsz, uint32_t maxsz) { diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index bbcb510c76..28d3b2a847 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1160,6 +1160,45 @@ void tcg_gen_ext16u_i32(TCGv_i32 ret, TCGv_i32 arg) tcg_gen_extract_i32(ret, arg, 0, 16); } +/* + * Internal helper for bit and byte reversal. + * Given a repeating matched block of 1's and 0's, swap the bits within + * those two blocks. E.g. mask=00ff00ff, shift the input bits left and + * right 8 bits. + */ +static void gen_bitswap_i32(TCGv_i32 ret, TCGv_i32 arg, uint32_t mask) +{ + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); + int sh = cto32(mask); + + tcg_gen_andi_i32(t0, arg, mask); + tcg_gen_shri_i32(t1, arg, sh); + tcg_gen_shli_i32(t0, t0, sh); + tcg_gen_andi_i32(t1, t1, mask); + tcg_gen_or_i32(ret, t0, t1); + + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); +} + +/* Similarly for 64-bit operands. */ +static void gen_bitswap_i64(TCGv_i64 ret, TCGv_i64 arg, uint64_t mask) +{ + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); + int sh = cto64(mask); + + tcg_gen_andi_i64(t0, arg, mask); + tcg_gen_shri_i64(t1, arg, sh); + tcg_gen_shli_i64(t0, t0, sh); + tcg_gen_andi_i64(t1, t1, mask); + tcg_gen_or_i64(ret, t0, t1); + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); +} + /* * bswap16_i32: 16-bit byte swap on the low bits of a 32-bit value. * @@ -1213,23 +1252,8 @@ void tcg_gen_bswap32_i32(TCGv_i32 ret, TCGv_i32 arg) if (tcg_op_supported(INDEX_op_bswap32, TCG_TYPE_I32, 0)) { tcg_gen_op3i_i32(INDEX_op_bswap32, ret, arg, 0); } else { - TCGv_i32 t0 = tcg_temp_ebb_new_i32(); - TCGv_i32 t1 = tcg_temp_ebb_new_i32(); - TCGv_i32 t2 = tcg_constant_i32(0x00ff00ff); - - /* 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_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_temp_free_i32(t0); - tcg_temp_free_i32(t1); + gen_bitswap_i32(ret, arg, 0x00ff00ff); + tcg_gen_hswap_i32(ret, ret); } } @@ -1244,24 +1268,74 @@ void tcg_gen_hswap_i32(TCGv_i32 ret, TCGv_i32 arg) tcg_gen_rotli_i32(ret, arg, 16); } +void tcg_gen_revbit8_i32(TCGv_i32 ret, TCGv_i32 arg) +{ + if (tcg_op_supported(INDEX_op_revbit8, TCG_TYPE_I32, 0)) { + tcg_gen_op2_i32(INDEX_op_revbit8, ret, arg); + } else if (tcg_op_supported(INDEX_op_revbit32, TCG_TYPE_I32, 0)) { + tcg_gen_op2_i32(INDEX_op_revbit32, ret, arg); + tcg_gen_bswap32_i32(ret, ret); + } else { + gen_bitswap_i32(ret, arg, 0x55555555u); + gen_bitswap_i32(ret, ret, 0x33333333u); + gen_bitswap_i32(ret, ret, 0x0f0f0f0fu); + } +} + +void tcg_gen_revbit32_i32(TCGv_i32 ret, TCGv_i32 arg) +{ + if (tcg_op_supported(INDEX_op_revbit32, TCG_TYPE_I32, 0)) { + tcg_gen_op3i_i32(INDEX_op_revbit32, ret, arg, 0); + } else { + tcg_gen_revbit8_i32(ret, arg); + tcg_gen_bswap32_i32(ret, ret); + } +} + void tcg_gen_smin_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) { - tcg_gen_movcond_i32(TCG_COND_LT, ret, a, b, a, b); + if (tcg_op_supported(INDEX_op_smin, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_smin, ret, a, b); + } else { + tcg_gen_movcond_i32(TCG_COND_LT, ret, a, b, a, b); + } } void tcg_gen_umin_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) { - tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, a, b); + if (tcg_op_supported(INDEX_op_umin, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_umin, ret, a, b); + } else { + tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, a, b); + } } void tcg_gen_smax_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) { - tcg_gen_movcond_i32(TCG_COND_LT, ret, a, b, b, a); + if (tcg_op_supported(INDEX_op_smax, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_smax, ret, a, b); + } else { + tcg_gen_movcond_i32(TCG_COND_LT, ret, a, b, b, a); + } } void tcg_gen_umax_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) { - tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, b, a); + if (tcg_op_supported(INDEX_op_umax, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_umax, ret, a, b); + } else { + tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, b, a); + } +} + +void tcg_gen_ussub_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t = tcg_temp_ebb_new_i32(); + TCGv_i32 z = tcg_constant_i32(0); + + tcg_gen_sub_i32(t, a, b); + tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, z, t); + tcg_temp_free_i32(t); } void tcg_gen_abs_i32(TCGv_i32 ret, TCGv_i32 a) @@ -1771,14 +1845,9 @@ void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg, int flags) } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); TCGv_i64 t1 = tcg_temp_ebb_new_i64(); - TCGv_i64 t2 = tcg_constant_i64(0x00ff00ff); - /* arg = xxxxabcd */ - tcg_gen_shri_i64(t0, arg, 8); /* t0 = .xxxxabc */ - 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 */ + /* arg = xxxxabcd */ + gen_bitswap_i64(ret, arg, 0x00ff00ff); /* ret = ....badc */ tcg_gen_shli_i64(t1, ret, 48); /* t1 = dc...... */ tcg_gen_shri_i64(t0, ret, 16); /* t0 = ......ba */ @@ -1805,32 +1874,8 @@ void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg) if (tcg_op_supported(INDEX_op_bswap64, TCG_TYPE_I64, 0)) { tcg_gen_op3i_i64(INDEX_op_bswap64, ret, arg, 0); } else { - TCGv_i64 t0 = tcg_temp_ebb_new_i64(); - TCGv_i64 t1 = tcg_temp_ebb_new_i64(); - TCGv_i64 t2 = tcg_temp_ebb_new_i64(); - - /* 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_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_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_temp_free_i64(t0); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); + gen_bitswap_i64(ret, arg, 0x00ff00ff00ff00ffull); + tcg_gen_hswap_i64(ret, ret); } } @@ -1842,20 +1887,8 @@ void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg) */ void tcg_gen_hswap_i64(TCGv_i64 ret, TCGv_i64 arg) { - uint64_t m = 0x0000ffff0000ffffull; - TCGv_i64 t0 = tcg_temp_ebb_new_i64(); - TCGv_i64 t1 = tcg_temp_ebb_new_i64(); - - /* arg = abcdefgh */ - tcg_gen_rotli_i64(t1, arg, 32); /* t1 = efghabcd */ - tcg_gen_andi_i64(t0, t1, m); /* t0 = ..gh..cd */ - tcg_gen_shli_i64(t0, t0, 16); /* t0 = gh..cd.. */ - tcg_gen_shri_i64(t1, t1, 16); /* t1 = ..efghab */ - tcg_gen_andi_i64(t1, t1, m); /* t1 = ..ef..ab */ - tcg_gen_or_i64(ret, t0, t1); /* ret = ghefcdab */ - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); + gen_bitswap_i64(ret, ret, 0x0000ffff0000ffffull); + tcg_gen_wswap_i64(ret, ret); } /* @@ -1869,6 +1902,57 @@ void tcg_gen_wswap_i64(TCGv_i64 ret, TCGv_i64 arg) tcg_gen_rotli_i64(ret, arg, 32); } +void tcg_gen_revbit32_i64(TCGv_i64 ret, TCGv_i64 arg, int flags) +{ + /* Only one extension flag may be present. */ + tcg_debug_assert(!(flags & TCG_BSWAP_OS) || !(flags & TCG_BSWAP_OZ)); + + if (tcg_op_supported(INDEX_op_revbit32, TCG_TYPE_I64, 0)) { + tcg_gen_op3i_i64(INDEX_op_revbit32, ret, arg, flags); + } else if (tcg_op_supported(INDEX_op_revbit64, TCG_TYPE_I64, 0)) { + tcg_gen_op2_i64(INDEX_op_revbit64, ret, arg); + if (flags & TCG_BSWAP_OS) { + tcg_gen_sari_i64(ret, ret, 32); + } else { + tcg_gen_shri_i64(ret, ret, 32); + } + } else { + if (tcg_op_supported(INDEX_op_revbit8, TCG_TYPE_I64, 0)) { + tcg_gen_op2_i64(INDEX_op_revbit8, ret, arg); + } else { + gen_bitswap_i64(ret, arg, 0x55555555ull); + gen_bitswap_i64(ret, ret, 0x33333333ull); + gen_bitswap_i64(ret, ret, 0x0f0f0f0full); + flags |= TCG_BSWAP_IZ; + } + tcg_gen_bswap32_i64(ret, ret, flags); + } +} + +void tcg_gen_revbit8_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + if (tcg_op_supported(INDEX_op_revbit8, TCG_TYPE_I64, 0)) { + tcg_gen_op2_i64(INDEX_op_revbit8, ret, arg); + } else if (tcg_op_supported(INDEX_op_revbit64, TCG_TYPE_I64, 0)) { + tcg_gen_op2_i64(INDEX_op_revbit64, ret, arg); + tcg_gen_bswap64_i64(ret, ret); + } else { + gen_bitswap_i64(ret, arg, 0x5555555555555555ull); + gen_bitswap_i64(ret, ret, 0x3333333333333333ull); + gen_bitswap_i64(ret, ret, 0x0f0f0f0f0f0f0f0full); + } +} + +void tcg_gen_revbit64_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + if (tcg_op_supported(INDEX_op_revbit64, TCG_TYPE_I64, 0)) { + tcg_gen_op2_i64(INDEX_op_revbit64, ret, arg); + } else { + tcg_gen_revbit8_i64(ret, arg); + tcg_gen_bswap64_i64(ret, ret); + } +} + void tcg_gen_not_i64(TCGv_i64 ret, TCGv_i64 arg) { if (tcg_op_supported(INDEX_op_not, TCG_TYPE_I64, 0)) { @@ -2415,22 +2499,48 @@ void tcg_gen_mulsu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) void tcg_gen_smin_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) { - tcg_gen_movcond_i64(TCG_COND_LT, ret, a, b, a, b); + if (tcg_op_supported(INDEX_op_smin, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_smin, ret, a, b); + } else { + tcg_gen_movcond_i64(TCG_COND_LT, ret, a, b, a, b); + } } void tcg_gen_umin_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) { - tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, a, b); + if (tcg_op_supported(INDEX_op_umin, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_umin, ret, a, b); + } else { + tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, a, b); + } } void tcg_gen_smax_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) { - tcg_gen_movcond_i64(TCG_COND_LT, ret, a, b, b, a); + if (tcg_op_supported(INDEX_op_smax, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_smax, ret, a, b); + } else { + tcg_gen_movcond_i64(TCG_COND_LT, ret, a, b, b, a); + } } void tcg_gen_umax_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) { - tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, b, a); + if (tcg_op_supported(INDEX_op_umax, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_umax, ret, a, b); + } else { + tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, b, a); + } +} + +void tcg_gen_ussub_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_ebb_new_i64(); + TCGv_i64 z = tcg_constant_i64(0); + + tcg_gen_sub_i64(t, a, b); + tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, z, t); + tcg_temp_free_i64(t); } void tcg_gen_abs_i64(TCGv_i64 ret, TCGv_i64 a) diff --git a/tcg/tcg.c b/tcg/tcg.c index 1e77f2365a..489df0e738 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1203,6 +1203,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_qemu_st2, TCGOutOpQemuLdSt2, outop_qemu_st2), OUTOP(INDEX_op_rems, TCGOutOpBinary, outop_rems), OUTOP(INDEX_op_remu, TCGOutOpBinary, outop_remu), + OUTOP(INDEX_op_revbit32, TCGOutOpBswap, outop_revbit32), OUTOP(INDEX_op_rotl, TCGOutOpBinary, outop_rotl), OUTOP(INDEX_op_rotr, TCGOutOpBinary, outop_rotr), OUTOP(INDEX_op_sar, TCGOutOpBinary, outop_sar), @@ -1210,6 +1211,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_sextract, TCGOutOpExtract, outop_sextract), OUTOP(INDEX_op_shl, TCGOutOpBinary, outop_shl), OUTOP(INDEX_op_shr, TCGOutOpBinary, outop_shr), + OUTOP(INDEX_op_smax, TCGOutOpBinary, outop_smax), + OUTOP(INDEX_op_smin, TCGOutOpBinary, outop_smin), OUTOP(INDEX_op_st, TCGOutOpStore, outop_st), OUTOP(INDEX_op_st8, TCGOutOpStore, outop_st8), OUTOP(INDEX_op_st16, TCGOutOpStore, outop_st16), @@ -1219,6 +1222,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_subbo, TCGOutOpAddSubCarry, outop_subbo), /* subb1o is implemented with set_borrow + subbio */ OUTOP(INDEX_op_subb1o, TCGOutOpAddSubCarry, outop_subbio), + OUTOP(INDEX_op_umax, TCGOutOpBinary, outop_umax), + OUTOP(INDEX_op_umin, TCGOutOpBinary, outop_umin), OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor), [INDEX_op_goto_ptr] = &outop_goto_ptr, @@ -1230,6 +1235,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_extrh_i64_i32, TCGOutOpUnary, outop_extrh_i64_i32), OUTOP(INDEX_op_ld32u, TCGOutOpLoad, outop_ld32u), OUTOP(INDEX_op_ld32s, TCGOutOpLoad, outop_ld32s), + OUTOP(INDEX_op_revbit8, TCGOutOpUnary, outop_revbit8), + OUTOP(INDEX_op_revbit64, TCGOutOpUnary, outop_revbit64), OUTOP(INDEX_op_st32, TCGOutOpStore, outop_st), }; @@ -1279,7 +1286,7 @@ void tcg_register_thread(void) qatomic_set(&tcg_ctxs[n], s); if (n > 0) { - tcg_region_initial_alloc(s); + tcg_region_thread_initial_alloc(s); } tcg_ctx = s; @@ -1830,18 +1837,24 @@ TranslationBlock *tcg_tb_alloc(TCGContext *s) TranslationBlock *tb; void *next; - retry: - tb = (void *)ROUND_UP((uintptr_t)s->code_gen_ptr, align); - next = (void *)ROUND_UP((uintptr_t)(tb + 1), align); + while (1) { + tb = (void *)ROUND_UP((uintptr_t)s->code_gen_ptr, align); - if (unlikely(next > s->code_gen_highwater)) { - if (tcg_region_alloc(s)) { + /* + * Note that code_gen_ptr can be NULL after vCPU hotplug. + * See tcg_region_thread_initial_alloc. + */ + if (tb) { + next = (void *)ROUND_UP((uintptr_t)(tb + 1), align); + if (next <= s->code_gen_highwater) { + qatomic_set(&s->code_gen_ptr, next); + return tb; + } + } + if (!tcg_region_alloc(s)) { return NULL; } - goto retry; } - qatomic_set(&s->code_gen_ptr, next); - return tb; } void tcg_prologue_init(void) @@ -2944,6 +2957,7 @@ void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) case INDEX_op_bswap16: case INDEX_op_bswap32: case INDEX_op_bswap64: + case INDEX_op_revbit32: { TCGArg flags = op->args[k]; const char *name = NULL; @@ -2951,7 +2965,7 @@ void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) if (flags < ARRAY_SIZE(bswap_flag_name)) { name = bswap_flag_name[flags]; } - if (name) { + if (name && name[0]) { col += ne_fprintf(f, ",%s", name); } else { col += ne_fprintf(f, ",$0x%" TCG_PRIlx, flags); @@ -5508,6 +5522,10 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_sar: case INDEX_op_shl: case INDEX_op_shr: + case INDEX_op_smax: + case INDEX_op_smin: + case INDEX_op_umax: + case INDEX_op_umin: case INDEX_op_xor: { const TCGOutOpBinary *out = @@ -5575,6 +5593,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_ctpop: case INDEX_op_neg: case INDEX_op_not: + case INDEX_op_revbit8: + case INDEX_op_revbit64: { const TCGOutOpUnary *out = container_of(all_outop[op->opc], TCGOutOpUnary, base); @@ -5587,6 +5607,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_bswap16: case INDEX_op_bswap32: + case INDEX_op_revbit32: { const TCGOutOpBswap *out = container_of(all_outop[op->opc], TCGOutOpBswap, base); diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 1b22c70616..4cd1c1431c 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -894,6 +894,22 @@ static void tcg_out_set_borrow(TCGContext *s) tcg_out_op_v(s, INDEX_op_tci_setcarry); /* borrow == carry */ } +static const TCGOutOpBinary outop_smax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_smin = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umin = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -959,6 +975,18 @@ static const TCGOutOpUnary outop_bswap64 = { .out_rr = tgen_bswap64, }; +static const TCGOutOpUnary outop_revbit8 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBswap outop_revbit32 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpUnary outop_revbit64 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tcg_out_op_rr(s, INDEX_op_neg, a0, a1); diff --git a/tcg/x86_64/tcg-target.c.inc b/tcg/x86_64/tcg-target.c.inc index 1fc45e4ec6..2c8f1f3e58 100644 --- a/tcg/x86_64/tcg-target.c.inc +++ b/tcg/x86_64/tcg-target.c.inc @@ -1290,6 +1290,18 @@ static inline void tcg_out_bswap64(TCGContext *s, int reg) tcg_out_opc(s, OPC_BSWAP + P_REXW + LOWREGMASK(reg), 0, reg, 0); } +static const TCGOutOpUnary outop_revbit8 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBswap outop_revbit32 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpUnary outop_revbit64 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_arithi(TCGContext *s, int c, int r0, tcg_target_long val, int cf) { @@ -2965,6 +2977,22 @@ static void tcg_out_set_borrow(TCGContext *s) tcg_out8(s, OPC_STC); } +static const TCGOutOpBinary outop_smax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_smin = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umin = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tests/data/acpi/loongarch64/virt/APIC b/tests/data/acpi/loongarch64/virt/APIC index 3477789f42..e90c267e1f 100644 Binary files a/tests/data/acpi/loongarch64/virt/APIC and b/tests/data/acpi/loongarch64/virt/APIC differ diff --git a/tests/data/acpi/loongarch64/virt/APIC.topology b/tests/data/acpi/loongarch64/virt/APIC.topology index da0089d57f..d00b7241c7 100644 Binary files a/tests/data/acpi/loongarch64/virt/APIC.topology and b/tests/data/acpi/loongarch64/virt/APIC.topology differ diff --git a/tests/data/igvm/README b/tests/data/igvm/README new file mode 100644 index 0000000000..d4d26478ee --- /dev/null +++ b/tests/data/igvm/README @@ -0,0 +1,45 @@ + +=== This is the readme file for all IGVM file bundles provided === +=== All IGVM files are covered by GPL v2 or later === +SPDX-License-Identifier: GPL-2.0-or-later + +Small IGVM file bundles required for tesing vm-launch-update device +=================================================================== + +This directory contains IGVM files required for launchupdate-test.c. +These IGVM bundles can be built from the following repository: + +https://gitlab.com/anisinha/virt-firmware-rs + +Just type "make" in the top of the repository. The built IGVM files +can be found in the target/x86_64-unknown-none/debug directory. + +$ ls -l ./target/x86_64-unknown-none/debug/*.igvm +-rw-r--r--. 1 anisinha anisinha 153624 Jun 8 10:05 ./target/x86_64-unknown-none/debug/hello.igvm +-rw-r--r--. 1 anisinha anisinha 157848 Jun 8 10:05 ./target/x86_64-unknown-none/debug/igvmtest.igvm +-rw-r--r--. 1 anisinha anisinha 987384 Jun 8 10:05 ./target/x86_64-unknown-none/debug/mefisto.igvm +-rw-r--r--. 1 anisinha anisinha 368312 Jun 8 10:05 ./target/x86_64-unknown-none/debug/qemuinit.igvm +-rw-r--r--. 1 anisinha anisinha 223944 Jun 8 10:05 ./target/x86_64-unknown-none/debug/snptest.igvm + +The IGVM bundles for sev-snp testing are also kept in a seperate directory /snp_igvm_bundles in the +above repository for use without requiring to build from source: + +$ virtfirmware-rs/snp_igvm_bundles$ ls -l +total 388 +-rw-r--r-- 1 anisinha anisinha 199176 Aug 7 00:58 snptest.igvm +-rw-r--r-- 1 anisinha anisinha 195048 Aug 6 06:10 snptest-nohello.igvm + +Following files are used by the tests/qtest/launchupdate-test.c for non-coco case: + +hello.igvm +qemuinit.igvm + +For confidential case (that is when COCO=1 is passed in the environment), the following +IGVM bundles are used: + +snptest-nohello.igvm +snptest.igvm + +The IGVM bundles used by the confidential case are not checked into the QEMU +repository. Users who wish to test the confidential case can use the IGVM +bundles from https://gitlab.com/anisinha/virt-firmware-rs as mentioned above. diff --git a/tests/data/igvm/hello.igvm b/tests/data/igvm/hello.igvm new file mode 100644 index 0000000000..2d3ffda5b1 Binary files /dev/null and b/tests/data/igvm/hello.igvm differ diff --git a/tests/data/igvm/qemuinit.igvm b/tests/data/igvm/qemuinit.igvm new file mode 100644 index 0000000000..08e605ae0d Binary files /dev/null and b/tests/data/igvm/qemuinit.igvm differ diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 4725c39807..0adddb6a5c 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -16,9 +16,8 @@ DOCKER_DEFAULT_REGISTRY := registry.gitlab.com/qemu-project/qemu endif DOCKER_REGISTRY := $(if $(REGISTRY),$(REGISTRY),$(DOCKER_DEFAULT_REGISTRY)) -CONTAINER_ENGINE = auto -DOCKER_SCRIPT=$(SRC_PATH)/tests/docker/docker.py --engine $(CONTAINER_ENGINE) -RUNC ?= $(shell $(DOCKER_SCRIPT) probe) +CONTAINER_COMMAND ?= $(shell $(SRC_PATH)/tests/docker/docker.py probe) +DOCKER_SCRIPT=$(SRC_PATH)/tests/docker/docker.py --command "$(CONTAINER_COMMAND)" CUR_TIME := $(shell date +%Y-%m-%d-%H.%M.%S.$$$$) DOCKER_SRC_COPY := $(BUILD_DIR)/docker-src.$(CUR_TIME) @@ -41,7 +40,7 @@ docker-qemu-src: $(DOCKER_SRC_COPY) # General rule for building docker images. docker-image-%: $(DOCKER_FILES_DIR)/%.docker $(call quiet-command, \ - DOCKER_BUILDKIT=1 $(RUNC) build \ + DOCKER_BUILDKIT=1 $(CONTAINER_COMMAND) build \ $(if $(DOCKER_V),,--quiet) \ $(if $(NOCACHE),--no-cache, \ $(if $(DOCKER_REGISTRY),--cache-from $(DOCKER_REGISTRY)/qemu/$*)) \ @@ -152,7 +151,7 @@ $(foreach i,$(filter-out $(DOCKER_PARTIAL_IMAGES),$(DOCKER_IMAGES)), \ ) docker: - @echo 'Build QEMU and run tests inside $(RUNC) containers' + @echo 'Build QEMU and run tests inside $(CONTAINER_COMMAND) containers' @echo @echo 'Available targets:' @echo @@ -219,10 +218,10 @@ docker-run: docker-qemu-src $(IMAGE) --executable $(EXECUTABLE), \ " COPYING $(EXECUTABLE) to $(IMAGE)")) $(call quiet-command, \ - $(RUNC) run \ + $(CONTAINER_COMMAND) run \ --rm \ $(if $(NOUSER),, \ - $(if $(filter docker,$(RUNC)), \ + $(if $(filter docker,$(CONTAINER_COMMAND)), \ -u $(UID), \ --userns keep-id \ ) \ diff --git a/tests/docker/docker.py b/tests/docker/docker.py index 9e18b984f4..d2f39b5645 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -35,27 +35,6 @@ FILTERED_ENV_NAMES = ['ftp_proxy', 'http_proxy', 'https_proxy'] DEVNULL = open(os.devnull, 'wb') -class EngineEnum(enum.IntEnum): - AUTO = 1 - DOCKER = 2 - PODMAN = 3 - - def __str__(self): - return self.name.lower() - - def __repr__(self): - return str(self) - - @staticmethod - def argparse(s): - try: - return EngineEnum[s.upper()] - except KeyError: - return s - - -USE_ENGINE = EngineEnum.AUTO - def _bytes_checksum(bytes): """Calculate a digest string unique to the text content""" return hashlib.sha1(bytes).hexdigest() @@ -73,12 +52,11 @@ def _file_checksum(filename): def _guess_engine_command(): """ Guess a working engine command or raise exception if not found""" - commands = [] - - if USE_ENGINE in [EngineEnum.AUTO, EngineEnum.PODMAN]: - commands += [["podman"], ["podman-remote"], ["podman", "--remote"]] - if USE_ENGINE in [EngineEnum.AUTO, EngineEnum.DOCKER]: - commands += [["docker"], ["sudo", "-n", "docker"]] + commands = [["podman"], + ["podman-remote"], + ["podman", "--remote"], + ["docker"], + ["sudo", "-n", "docker"]] for cmd in commands: try: # 'version' is not sufficient to prove a working binary @@ -222,8 +200,11 @@ def _dockerfile_verify_flat(df): class Docker(object): """ Running Docker commands """ - def __init__(self): - self._command = _guess_engine_command() + def __init__(self, commandstr=None): + if commandstr is None: + self._command = _guess_engine_command() + else: + self._command = commandstr.split(" ") if ("docker" in self._command and "TRAVIS" not in os.environ and @@ -411,8 +392,8 @@ class RunCommand(SubCommand): help="Run container using the current user's uid") def run(self, args, argv): - return Docker().run(argv, args.keep, quiet=args.quiet, - as_user=args.run_as_current_user) + return Docker(args.command).run(argv, args.keep, quiet=args.quiet, + as_user=args.run_as_current_user) class BuildCommand(SubCommand): @@ -445,7 +426,7 @@ class BuildCommand(SubCommand): dockerfile = _read_dockerfile(args.dockerfile) tag = args.tag - dkr = Docker() + dkr = Docker(args.command) if "--no-cache" not in argv and \ dkr.image_matches_dockerfile(tag, dockerfile): if not args.quiet: @@ -512,7 +493,7 @@ class FetchCommand(SubCommand): help="Docker registry") def run(self, args, argv): - dkr = Docker() + dkr = Docker(args.command) dkr.command(cmd="pull", quiet=args.quiet, argv=["%s/%s" % (args.registry, args.tag)]) dkr.command(cmd="tag", quiet=args.quiet, @@ -590,7 +571,7 @@ class UpdateCommand(SubCommand): tmp.seek(0) # Run the build with our tarball context - dkr = Docker() + dkr = Docker(args.command) dkr.update_image(args.tag, tmp, quiet=args.quiet) return 0 @@ -601,7 +582,7 @@ class CleanCommand(SubCommand): name = "clean" def run(self, args, argv): - Docker().clean() + Docker(args.command).clean() return 0 @@ -610,7 +591,7 @@ class ImagesCommand(SubCommand): name = "images" def run(self, args, argv): - return Docker().command("images", argv, args.quiet) + return Docker(args.command).command("images", argv, args.quiet) class ProbeCommand(SubCommand): @@ -619,7 +600,7 @@ class ProbeCommand(SubCommand): def run(self, args, argv): try: - docker = Docker() + docker = Docker(args.command) print(" ".join(docker._command)) except Exception: print("no") @@ -651,18 +632,16 @@ class CcCommand(SubCommand): cmd += ["-v", "%s:%s:ro,z" % (p, p)] cmd += [args.image, args.cc] cmd += argv - return Docker().run(cmd, False, quiet=args.quiet, - as_user=True) + return Docker(args.command).run(cmd, False, quiet=args.quiet, + as_user=True) def main(): - global USE_ENGINE - parser = argparse.ArgumentParser(description="A Docker helper", usage="%s ..." % os.path.basename(sys.argv[0])) - parser.add_argument("--engine", type=EngineEnum.argparse, choices=list(EngineEnum), - help="specify which container engine to use") + parser.add_argument("--command", + help="specify which container engine command to use") subparsers = parser.add_subparsers(title="subcommands", help=None) for cls in SubCommand.__subclasses__(): cmd = cls() @@ -671,8 +650,6 @@ def main(): cmd.args(subp) subp.set_defaults(cmdobj=cmd) args, argv = parser.parse_known_args() - if args.engine: - USE_ENGINE = args.engine return args.cmdobj.run(args, argv) diff --git a/tests/docker/dockerfiles/alpine.docker b/tests/docker/dockerfiles/alpine.docker index 118c6e8eea..8a5a355e89 100644 --- a/tests/docker/dockerfiles/alpine.docker +++ b/tests/docker/dockerfiles/alpine.docker @@ -83,14 +83,11 @@ RUN apk update && \ pixman-dev \ pkgconf \ pulseaudio-dev \ - py3-numpy \ - py3-pillow \ py3-pip \ py3-setuptools \ py3-sphinx \ py3-sphinx_rtd_theme \ py3-wheel \ - py3-yaml \ python3 \ rpm2cpio \ rust \ diff --git a/tests/docker/dockerfiles/centos9.docker b/tests/docker/dockerfiles/centos9.docker index 0d5b5fa7f5..8101370a2d 100644 --- a/tests/docker/dockerfiles/centos9.docker +++ b/tests/docker/dockerfiles/centos9.docker @@ -95,16 +95,11 @@ RUN dnf --quiet distro-sync -y && \ pixman-devel \ pkgconfig \ pulseaudio-libs-devel \ - python3 \ - python3-PyYAML \ - python3-numpy \ - python3-pillow \ - python3-pip \ - python3-setuptools \ - python3-sphinx \ - python3-sphinx_rtd_theme \ python3-tomli \ - python3-wheel \ + python3.11 \ + python3.11-pip \ + python3.11-setuptools \ + python3.11-wheel \ rdma-core-devel \ rust \ rust-std-static \ @@ -134,11 +129,15 @@ RUN dnf --quiet distro-sync -y && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc +RUN /usr/bin/pip3.11 install \ + sphinx \ + sphinx-rtd-theme + ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" ENV MAKE="/usr/bin/make" ENV NINJA="/usr/bin/ninja" -ENV PYTHON="/usr/bin/python3" +ENV PYTHON="/usr/bin/python3.11" # As a final step configure the user (if env is defined) ARG USER ARG UID diff --git a/tests/docker/dockerfiles/debian-all-test-cross.docker b/tests/docker/dockerfiles/debian-all-test-cross.docker index 2dc580552e..833185f607 100644 --- a/tests/docker/dockerfiles/debian-all-test-cross.docker +++ b/tests/docker/dockerfiles/debian-all-test-cross.docker @@ -47,7 +47,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc -RUN /usr/bin/pip3 install meson==1.8.1 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/debian-amd64-cross.docker b/tests/docker/dockerfiles/debian-amd64-cross.docker index 061cd8e9b8..9e30457ca7 100644 --- a/tests/docker/dockerfiles/debian-amd64-cross.docker +++ b/tests/docker/dockerfiles/debian-amd64-cross.docker @@ -41,9 +41,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ openssh-client \ pkgconf \ python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ @@ -51,7 +48,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-tomli \ python3-venv \ python3-wheel \ - python3-yaml \ rpm2cpio \ rustc \ sed \ @@ -70,7 +66,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED -RUN /usr/bin/pip3 install meson==1.8.1 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/debian-arm64-cross.docker b/tests/docker/dockerfiles/debian-arm64-cross.docker index 3dfa60fdfd..b9b94e5e6f 100644 --- a/tests/docker/dockerfiles/debian-arm64-cross.docker +++ b/tests/docker/dockerfiles/debian-arm64-cross.docker @@ -41,9 +41,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ openssh-client \ pkgconf \ python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ @@ -51,7 +48,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-tomli \ python3-venv \ python3-wheel \ - python3-yaml \ rpm2cpio \ rustc \ sed \ @@ -70,7 +66,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED -RUN /usr/bin/pip3 install meson==1.8.1 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/debian-armhf-cross.docker b/tests/docker/dockerfiles/debian-armhf-cross.docker index 401c64269c..aa412ff66f 100644 --- a/tests/docker/dockerfiles/debian-armhf-cross.docker +++ b/tests/docker/dockerfiles/debian-armhf-cross.docker @@ -41,9 +41,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ openssh-client \ pkgconf \ python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ @@ -51,7 +48,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-tomli \ python3-venv \ python3-wheel \ - python3-yaml \ rpm2cpio \ rustc \ sed \ @@ -70,7 +66,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED -RUN /usr/bin/pip3 install meson==1.8.1 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/debian-hexagon-cross.docker b/tests/docker/dockerfiles/debian-hexagon-cross.docker index 23e8bb2fb2..e07a2be37e 100644 --- a/tests/docker/dockerfiles/debian-hexagon-cross.docker +++ b/tests/docker/dockerfiles/debian-hexagon-cross.docker @@ -17,13 +17,13 @@ RUN apt-get update && \ # Install common build utilities apt-get install -y --no-install-recommends \ curl \ + wget \ + gnupg \ + lsb-release \ ccache \ xz-utils \ zstd \ ca-certificates \ - libc++1 \ - libc++abi1 \ - libunwind-19 \ bison \ flex \ git \ @@ -43,15 +43,22 @@ RUN apt-get update && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt -ENV TOOLCHAIN_INSTALL=/opt -ENV TOOLCHAIN_RELEASE=22.1.0 -ENV TOOLCHAIN_BASENAME=clang+llvm-${TOOLCHAIN_RELEASE}-cross-hexagon-unknown-linux-musl -ENV TOOLCHAIN_URL=https://artifacts.codelinaro.org/artifactory/codelinaro-toolchain-for-hexagon/${TOOLCHAIN_RELEASE}_/${TOOLCHAIN_BASENAME}.tar.zst +ENV LLVM_VERSION=22 +ENV TOOLCHAIN_RELEASE=22.1.8 +ENV TOOLCHAIN_URL=https://artifacts.codelinaro.org/artifactory/codelinaro-toolchain-for-hexagon/${TOOLCHAIN_RELEASE}________/hexagon-debs-${TOOLCHAIN_RELEASE}________.tar.gz ENV CCACHE_WRAPPERSDIR=/usr/libexec/ccache-wrappers - -RUN curl -#SL "$TOOLCHAIN_URL" | tar --zstd -xC "$TOOLCHAIN_INSTALL" -ENV PATH=$PATH:${TOOLCHAIN_INSTALL}/${TOOLCHAIN_BASENAME}/x86_64-linux-gnu/bin ENV MAKE=/usr/bin/make + +RUN curl -#SL https://apt.llvm.org/llvm.sh -o /tmp/llvm.sh && \ + chmod +x /tmp/llvm.sh && \ + DEBIAN_FRONTEND=noninteractive eatmydata /tmp/llvm.sh ${LLVM_VERSION} && \ + rm -f /tmp/llvm.sh && \ + mkdir -p /tmp/hexagon-debs && \ + curl -#SL "$TOOLCHAIN_URL" | tar -xzC /tmp/hexagon-debs && \ + DEBIAN_FRONTEND=noninteractive eatmydata \ + apt-get install -y /tmp/hexagon-debs/*.deb && \ + rm -rf /tmp/hexagon-debs + # As a final step configure the user (if env is defined) ARG USER ARG UID diff --git a/tests/docker/dockerfiles/debian-i686-cross.docker b/tests/docker/dockerfiles/debian-i686-cross.docker index 8b60c7b085..d07eac84fe 100644 --- a/tests/docker/dockerfiles/debian-i686-cross.docker +++ b/tests/docker/dockerfiles/debian-i686-cross.docker @@ -41,9 +41,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ openssh-client \ pkgconf \ python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ @@ -51,7 +48,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-tomli \ python3-venv \ python3-wheel \ - python3-yaml \ rpm2cpio \ rustc \ sed \ @@ -70,7 +66,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED -RUN /usr/bin/pip3 install meson==1.8.1 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/debian-ppc64el-cross.docker b/tests/docker/dockerfiles/debian-ppc64el-cross.docker index 2a7e8cba05..7302c8236e 100644 --- a/tests/docker/dockerfiles/debian-ppc64el-cross.docker +++ b/tests/docker/dockerfiles/debian-ppc64el-cross.docker @@ -41,9 +41,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ openssh-client \ pkgconf \ python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ @@ -51,7 +48,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-tomli \ python3-venv \ python3-wheel \ - python3-yaml \ rpm2cpio \ rustc \ sed \ @@ -70,7 +66,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED -RUN /usr/bin/pip3 install meson==1.8.1 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/debian-riscv64-cross.docker b/tests/docker/dockerfiles/debian-riscv64-cross.docker index 349de3a0bb..aa5a881cab 100644 --- a/tests/docker/dockerfiles/debian-riscv64-cross.docker +++ b/tests/docker/dockerfiles/debian-riscv64-cross.docker @@ -41,9 +41,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ openssh-client \ pkgconf \ python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ @@ -51,7 +48,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-tomli \ python3-venv \ python3-wheel \ - python3-yaml \ rpm2cpio \ rustc \ sed \ @@ -70,7 +66,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED -RUN /usr/bin/pip3 install meson==1.8.1 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/debian-s390x-cross.docker b/tests/docker/dockerfiles/debian-s390x-cross.docker index 8bc42b1e00..82eae4fc2c 100644 --- a/tests/docker/dockerfiles/debian-s390x-cross.docker +++ b/tests/docker/dockerfiles/debian-s390x-cross.docker @@ -41,9 +41,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ openssh-client \ pkgconf \ python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ @@ -51,7 +48,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-tomli \ python3-venv \ python3-wheel \ - python3-yaml \ rpm2cpio \ rustc \ sed \ @@ -70,7 +66,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED -RUN /usr/bin/pip3 install meson==1.8.1 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/debian.docker b/tests/docker/dockerfiles/debian.docker index 413986d3f0..3a4f208ccf 100644 --- a/tests/docker/dockerfiles/debian.docker +++ b/tests/docker/dockerfiles/debian.docker @@ -113,9 +113,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ openssh-client \ pkgconf \ python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ @@ -123,7 +120,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-tomli \ python3-venv \ python3-wheel \ - python3-yaml \ rpm2cpio \ rustc \ sed \ @@ -149,7 +145,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc -RUN /usr/bin/pip3 install meson==1.8.1 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/fedora-rust-nightly.docker b/tests/docker/dockerfiles/fedora-rust-nightly.docker index 4f84b80379..0f3dc370a4 100644 --- a/tests/docker/dockerfiles/fedora-rust-nightly.docker +++ b/tests/docker/dockerfiles/fedora-rust-nightly.docker @@ -104,10 +104,6 @@ exec "$@"\n' > /usr/bin/nosync && \ pkgconfig \ pulseaudio-libs-devel \ python3 \ - python3-PyYAML \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ @@ -149,7 +145,7 @@ exec "$@"\n' > /usr/bin/nosync && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc -RUN /usr/bin/pip3 install meson==1.8.1 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/fedora-win64-cross.docker b/tests/docker/dockerfiles/fedora-win64-cross.docker index c1e63feb46..6f972f6173 100644 --- a/tests/docker/dockerfiles/fedora-win64-cross.docker +++ b/tests/docker/dockerfiles/fedora-win64-cross.docker @@ -44,10 +44,6 @@ exec "$@"\n' > /usr/bin/nosync && \ ninja-build \ openssh-clients \ python3 \ - python3-PyYAML \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ @@ -70,7 +66,7 @@ exec "$@"\n' > /usr/bin/nosync && \ nosync dnf --quiet clean all -y && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED -RUN /usr/bin/pip3 install meson==1.8.1 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index a6996917c4..99bb38a366 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -104,10 +104,6 @@ exec "$@"\n' > /usr/bin/nosync && \ pkgconfig \ pulseaudio-libs-devel \ python3 \ - python3-PyYAML \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ @@ -149,7 +145,7 @@ exec "$@"\n' > /usr/bin/nosync && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc -RUN /usr/bin/pip3 install meson==1.8.1 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/opensuse-leap.docker b/tests/docker/dockerfiles/opensuse-leap.docker index fc363c647c..8d8631bbcf 100644 --- a/tests/docker/dockerfiles/opensuse-leap.docker +++ b/tests/docker/dockerfiles/opensuse-leap.docker @@ -93,12 +93,8 @@ RUN zypper update -y && \ pcre2-devel-static \ pipewire-devel \ pkgconfig \ - python3-Pillow \ - python3-PyYAML \ python3-Sphinx \ python3-base \ - python3-numpy \ - python3-opencv \ python3-pip \ python3-setuptools \ python3-sphinx_rtd_theme \ diff --git a/tests/docker/dockerfiles/ubuntu2404.docker b/tests/docker/dockerfiles/ubuntu2404.docker index 27992e34fd..a5c22231f9 100644 --- a/tests/docker/dockerfiles/ubuntu2404.docker +++ b/tests/docker/dockerfiles/ubuntu2404.docker @@ -113,9 +113,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ openssh-client \ pkgconf \ python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ @@ -123,7 +120,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-tomli \ python3-venv \ python3-wheel \ - python3-yaml \ rpm2cpio \ rustc-1.83 \ sed \ @@ -149,7 +145,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc -RUN /usr/bin/pip3 install meson==1.8.1 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/functional/aarch64/meson.build b/tests/functional/aarch64/meson.build index e81afd6c39..f0881bed16 100644 --- a/tests/functional/aarch64/meson.build +++ b/tests/functional/aarch64/meson.build @@ -25,6 +25,10 @@ tests_aarch64_system_quick = [ 'vmstate', ] +if igvm.found() + tests_aarch64_system_quick += [ 'vm_launch_update_aarch' ] +endif + tests_aarch64_system_thorough = [ 'aspeed_ast2700a1', 'aspeed_ast2700a2', diff --git a/tests/functional/aarch64/test_aspeed_ast2700a1.py b/tests/functional/aarch64/test_aspeed_ast2700a1.py index b0c08854da..2113c78db2 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700a1.py +++ b/tests/functional/aarch64/test_aspeed_ast2700a1.py @@ -81,17 +81,17 @@ class AST2x00MachineSDK(QemuSystemTest): def verify_openbmc_boot_and_login(self, name, enable_pcie=True): self.verify_openbmc_boot_start(enable_pcie) - wait_for_console_pattern(self, f'{name} login:') + wait_for_console_pattern(self, 'login:') exec_command_and_wait_for_pattern(self, 'root', 'Password:') exec_command_and_wait_for_pattern(self, '0penBmc', f'root@{name}:~#') - ASSET_SDK_V1101_AST2700A1 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.01/ast2700-a1-image.tar.gz', - '859808828531a51931aad3b4e70b28143eebb3cde1838ba7d8e7a2b844c8a1ab') + ASSET_SDK_V1103_AST2700A1 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.03/ast2700-a1-image.tar.gz', + '540961dc380709d852e957c5817cd7ee0dbb0a66f3aa17413eac7b67518afbfe') - ASSET_SDK_V1101_AST2700A1_DCSCM = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.01/ast2700-a1-dcscm-image.tar.gz', - '4654eabad75da3fd33635cd6d29b7635181daefee7294b68feb124b9d4c24116') + ASSET_SDK_V1103_AST2700A1_DCSCM = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.03/ast2700-a1-dcscm-image.tar.gz', + '5f7c139330fcefa6025bc7565a20fd5ea42cacebf810d56dc9a76b07cf69b3e1') def do_ast2700_i2c_test(self, bus_id): bus_str = str(bus_id) @@ -118,6 +118,11 @@ class AST2x00MachineSDK(QemuSystemTest): 'ip addr show dev eth2', 'inet 10.0.2.15/24') + def do_ast2700_usb_ehci_test(self): + exec_command_and_wait_for_pattern(self, + 'lsusb', + 'QEMU QEMU USB Keyboard') + def start_ast2700_test(self, name, bus_id): num_cpu = 4 load_images_list = [ @@ -127,7 +132,8 @@ class AST2x00MachineSDK(QemuSystemTest): }, { 'addr': '0x430000000', - 'file': self.scratch_file(name, 'bl31.bin') + 'file': self.scratch_file(name, 'trusted-firmware-a', + 'bl31.bin') }, { 'addr': '0x430080000', @@ -154,34 +160,36 @@ class AST2x00MachineSDK(QemuSystemTest): self.do_test_aarch64_aspeed_sdk_start( self.scratch_file(name, 'image-bmc'), bus_id) - def test_aarch64_ast2700a1_evb_sdk_v11_01(self): + def test_aarch64_ast2700a1_evb_sdk_v11_03(self): self.set_machine('ast2700a1-evb') self.require_netdev('user') - self.archive_extract(self.ASSET_SDK_V1101_AST2700A1) + self.archive_extract(self.ASSET_SDK_V1103_AST2700A1) self.vm.add_args('-device', 'e1000e,netdev=net1,bus=pcie.2') self.vm.add_args('-netdev', 'user,id=net1') + self.vm.add_args('-device', 'usb-kbd,bus=usb-bus.3') self.start_ast2700_test('ast2700-a1-image', 1) self.verify_openbmc_boot_and_login('ast2700-a1') self.do_ast2700_i2c_test(1) self.do_ast2700_pcie_test() + self.do_ast2700_usb_ehci_test() - def test_aarch64_ast2700a1_evb_sdk_vbootrom_v11_01(self): + def test_aarch64_ast2700a1_evb_sdk_vbootrom_v11_03(self): self.set_machine('ast2700a1-evb') self.require_netdev('user') - self.archive_extract(self.ASSET_SDK_V1101_AST2700A1) + self.archive_extract(self.ASSET_SDK_V1103_AST2700A1) self.vm.add_args('-device', 'e1000e,netdev=net1,bus=pcie.2') self.vm.add_args('-netdev', 'user,id=net1') self.start_ast2700_test_vbootrom('ast2700-a1-image', 1) self.verify_vbootrom_firmware_flow() self.verify_openbmc_boot_start() - def test_aarch64_ast2700a1_evb_ioexp_v11_01(self): + def test_aarch64_ast2700a1_evb_ioexp_v11_03(self): self.set_machine('ast2700a1-evb') self.require_netdev('user') - self.archive_extract(self.ASSET_SDK_V1101_AST2700A1_DCSCM) + self.archive_extract(self.ASSET_SDK_V1103_AST2700A1_DCSCM) self.vm.set_machine('ast2700a1-evb,fmc-model=w25q512jv') self.vm.add_args('-device', 'tmp105,bus=ioexp0.0,address=0x4d,id=tmp-test-16') diff --git a/tests/functional/aarch64/test_aspeed_ast2700a2.py b/tests/functional/aarch64/test_aspeed_ast2700a2.py index ed414999f4..01c7c4fdbe 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700a2.py +++ b/tests/functional/aarch64/test_aspeed_ast2700a2.py @@ -81,17 +81,17 @@ class AST2x00MachineSDK(QemuSystemTest): def verify_openbmc_boot_and_login(self, name, enable_pcie=True): self.verify_openbmc_boot_start(enable_pcie) - wait_for_console_pattern(self, f'{name} login:') + wait_for_console_pattern(self, 'login:') exec_command_and_wait_for_pattern(self, 'root', 'Password:') exec_command_and_wait_for_pattern(self, '0penBmc', f'root@{name}:~#') - ASSET_SDK_V1101_AST2700A2 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.01/ast2700-default-image.tar.gz', - 'ce89dcd995cf284d41a6a4bd17a1b97d59939f0277bfe54fdaaf30e741ce7487') + ASSET_SDK_V1103_AST2700A2 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.03/ast2700-default-image.tar.gz', + 'b91450d53da234591060cfb926fa30f7534ce20eaab766cb0f80ec332f8f0adb') - ASSET_SDK_V1101_AST2700A2_DCSCM = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.01/ast2700-dcscm-image.tar.gz', - 'b92ece9ca733dfd7a20193a12582f743b77f1898116b6d6f1abe57ac8db01c56') + ASSET_SDK_V1103_AST2700A2_DCSCM = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.03/ast2700-dcscm-image.tar.gz', + '7afd8323fc95097c14872b90d68c5cae5d078530c704591b192b74bd892c1dbf') def do_ast2700_i2c_test(self, bus_id): bus_str = str(bus_id) @@ -121,6 +121,11 @@ class AST2x00MachineSDK(QemuSystemTest): 'ip addr show dev eth2', 'inet 10.0.2.15/24') + def do_ast2700_usb_ehci_test(self): + exec_command_and_wait_for_pattern(self, + 'lsusb', + 'QEMU QEMU USB Keyboard') + def start_ast2700_test(self, name, bus_id): num_cpu = 4 load_images_list = [ @@ -130,7 +135,8 @@ class AST2x00MachineSDK(QemuSystemTest): }, { 'addr': '0x430000000', - 'file': self.scratch_file(name, 'bl31.bin') + 'file': self.scratch_file(name, 'trusted-firmware-a', + 'bl31.bin') }, { 'addr': '0x430080000', @@ -157,34 +163,36 @@ class AST2x00MachineSDK(QemuSystemTest): self.do_test_aarch64_aspeed_sdk_start( self.scratch_file(name, 'image-bmc'), bus_id) - def test_aarch64_ast2700a2_evb_sdk_v11_01(self): + def test_aarch64_ast2700a2_evb_sdk_v11_03(self): self.set_machine('ast2700a2-evb') self.require_netdev('user') - self.archive_extract(self.ASSET_SDK_V1101_AST2700A2) + self.archive_extract(self.ASSET_SDK_V1103_AST2700A2) self.vm.add_args('-device', 'e1000e,netdev=net1,bus=pcie.2') self.vm.add_args('-netdev', 'user,id=net1') + self.vm.add_args('-device', 'usb-kbd,bus=usb-bus.3') self.start_ast2700_test('ast2700-default-image', 1) self.verify_openbmc_boot_and_login('ast2700-default') self.do_ast2700_i2c_test(1) self.do_ast2700_pcie_test() + self.do_ast2700_usb_ehci_test() - def test_aarch64_ast2700a2_evb_sdk_vbootrom_v11_01(self): + def test_aarch64_ast2700a2_evb_sdk_vbootrom_v11_03(self): self.set_machine('ast2700a2-evb') self.require_netdev('user') - self.archive_extract(self.ASSET_SDK_V1101_AST2700A2) + self.archive_extract(self.ASSET_SDK_V1103_AST2700A2) self.vm.add_args('-device', 'e1000e,netdev=net1,bus=pcie.2') self.vm.add_args('-netdev', 'user,id=net1') self.start_ast2700_test_vbootrom('ast2700-default-image', 1) self.verify_vbootrom_firmware_flow() self.verify_openbmc_boot_start() - def test_aarch64_ast2700a2_evb_ioexp_v11_01(self): + def test_aarch64_ast2700a2_evb_ioexp_v11_03(self): self.set_machine('ast2700a2-evb') self.require_netdev('user') - self.archive_extract(self.ASSET_SDK_V1101_AST2700A2_DCSCM) + self.archive_extract(self.ASSET_SDK_V1103_AST2700A2_DCSCM) self.vm.set_machine('ast2700a2-evb,fmc-model=w25q512jv') self.vm.add_args('-device', 'tmp105,bus=ioexp0.0,address=0x4d,id=tmp-test-16') diff --git a/tests/functional/aarch64/test_aspeed_ast2700fc.py b/tests/functional/aarch64/test_aspeed_ast2700fc.py index df889134ed..704477d7c5 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700fc.py +++ b/tests/functional/aarch64/test_aspeed_ast2700fc.py @@ -51,7 +51,7 @@ class AST2x00MachineSDK(QemuSystemTest): self.enable_ast2700_pcie2() wait_for_console_pattern(self, 'Starting kernel ...') - wait_for_console_pattern(self, f'{name} login:') + wait_for_console_pattern(self, 'login:') exec_command_and_wait_for_pattern(self, 'root', 'Password:') exec_command_and_wait_for_pattern(self, '0penBmc', f'root@{name}:~#') @@ -66,9 +66,9 @@ class AST2x00MachineSDK(QemuSystemTest): self.vm.add_args('-device', f'loader,file={file},cpu-num={cpu_num}') - ASSET_SDK_V1101_AST2700 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.01/ast2700-default-image.tar.gz', - 'ce89dcd995cf284d41a6a4bd17a1b97d59939f0277bfe54fdaaf30e741ce7487') + ASSET_SDK_V1103_AST2700 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.03/ast2700-default-image.tar.gz', + 'b91450d53da234591060cfb926fa30f7534ce20eaab766cb0f80ec332f8f0adb') def do_ast2700_i2c_test(self): exec_command_and_wait_for_pattern(self, @@ -101,7 +101,7 @@ class AST2x00MachineSDK(QemuSystemTest): exec_command_and_wait_for_pattern(self, '\012', 'ssp_tsp:~$') exec_command_and_wait_for_pattern(self, 'version', - 'Zephyr version 3.7.1') + 'Zephyr version 3.7.2') exec_command_and_wait_for_pattern(self, 'md 72c02000 1', '[72c02000] 06020103') @@ -112,7 +112,7 @@ class AST2x00MachineSDK(QemuSystemTest): exec_command_and_wait_for_pattern(self, '\012', 'tsp:~$') exec_command_and_wait_for_pattern(self, 'version', - 'Zephyr version 3.7.1') + 'Zephyr version 3.7.2') exec_command_and_wait_for_pattern(self, 'md 72c02000 1', '[72c02000] 06020103') @@ -125,7 +125,8 @@ class AST2x00MachineSDK(QemuSystemTest): }, { 'addr': '0x430000000', - 'file': self.scratch_file(name, 'bl31.bin') + 'file': self.scratch_file(name, 'trusted-firmware-a', + 'bl31.bin') }, { 'addr': '0x430080000', @@ -153,11 +154,11 @@ class AST2x00MachineSDK(QemuSystemTest): self.do_test_aarch64_aspeed_sdk_start( self.scratch_file(name, 'image-bmc')) - def test_aarch64_ast2700fc_sdk_v11_01(self): + def test_aarch64_ast2700fc_sdk_v11_03(self): self.set_machine('ast2700fc') self.require_netdev('user') - self.archive_extract(self.ASSET_SDK_V1101_AST2700) + self.archive_extract(self.ASSET_SDK_V1103_AST2700) self.start_ast2700fc_test('ast2700-default-image') self.verify_openbmc_boot_and_login('ast2700-default') self.do_ast2700_i2c_test() @@ -165,10 +166,10 @@ class AST2x00MachineSDK(QemuSystemTest): self.do_ast2700fc_ssp_test() self.do_ast2700fc_tsp_test() - def test_aarch64_ast2700fc_sdk_vbootrom_v11_01(self): + def test_aarch64_ast2700fc_sdk_vbootrom_v11_03(self): self.set_machine('ast2700fc') - self.archive_extract(self.ASSET_SDK_V1101_AST2700) + self.archive_extract(self.ASSET_SDK_V1103_AST2700) self.start_ast2700fc_test_vbootrom('ast2700-default-image') self.verify_openbmc_boot_and_login('ast2700-default') self.do_ast2700fc_ssp_test() diff --git a/tests/functional/aarch64/test_raspi4.py b/tests/functional/aarch64/test_raspi4.py index 7a4302b0c5..bacecbbe9c 100755 --- a/tests/functional/aarch64/test_raspi4.py +++ b/tests/functional/aarch64/test_raspi4.py @@ -86,6 +86,23 @@ class Aarch64Raspi4Machine(LinuxKernelTest): 'BCM2835') exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', 'cprman@7e101000') + + # We used to get the RAM size wrong; guard against a regression + # (see git history for details). + mem_total_kb = None + cmd_output = exec_command_and_wait_for_pattern( + self, 'cat /proc/meminfo', 'MemAvailable') + for line in cmd_output.decode('ascii', errors='replace').splitlines(): + if line.startswith('MemTotal:'): + mem_total_kb = int(line.split()[1]) + break + self.assertIsNotNone(mem_total_kb, 'MemTotal line not found') + self.assertGreater(mem_total_kb, 1900000, + 'guest RAM (%d kB) is far below the ~1.9 GiB ' + 'expected for a 2 GiB raspi4b -- the second ' + 'memory node above the 1 GiB peripheral hole ' + 'is probably not being added' % mem_total_kb) + exec_command_and_wait_for_pattern(self, 'halt', 'reboot: System halted') # TODO: Raspberry Pi4 doesn't shut down properly with recent kernels # Wait for VM to shut down gracefully diff --git a/tests/functional/aarch64/test_vm_launch_update_aarch.py b/tests/functional/aarch64/test_vm_launch_update_aarch.py new file mode 100755 index 0000000000..2b3d7cf4a3 --- /dev/null +++ b/tests/functional/aarch64/test_vm_launch_update_aarch.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +# +# Check for vm-launch-update device. +# +# Copyright (c) 2026 Red Hat, Inc. +# +# Author: +# Ani Sinha +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest + +class VmLaunchUpdateDeviceCheck(QemuSystemTest): + + def aarch64_fail_test(self): + """ + Currently the device is only supported for pc platforms. + """ + self.vm.add_args('-machine', 'virt', '-device', + 'vm-launch-update,id=fwupd1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), + r'This machine does not support vm-launch-update device') + + def test_vm_launch_update(self): + self.aarch64_fail_test() + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/arm/test_aspeed_ast1030.py b/tests/functional/arm/test_aspeed_ast1030.py index 03fee55b5f..83a96ec322 100755 --- a/tests/functional/arm/test_aspeed_ast1030.py +++ b/tests/functional/arm/test_aspeed_ast1030.py @@ -12,17 +12,17 @@ from qemu_test import Asset, exec_command_and_wait_for_pattern class AST1030Machine(AspeedTest): - ASSET_ZEPHYR_3_06 = Asset( + ASSET_ZEPHYR_3_08 = Asset( ('https://github.com/AspeedTech-BMC' - '/zephyr/releases/download/v00.03.06/ast1030-evb-demo.zip'), - '056f37fcd9f165308cedca3a08f2bed37ed40c0a1402c4fa515613b80a369f38') + '/zephyr/releases/download/v00.03.08/ast1030-evb-demo.zip'), + '9eac3691bc7bce1b912bbe2ae4e36608a6532ff8d607f4d1e44b88407a48d4e5') - def test_arm_ast1030_zephyros_3_06(self): + def test_arm_ast1030_zephyros_3_08(self): self.set_machine('ast1030-evb') kernel_name = "ast1030-evb-demo/zephyr.elf" kernel_file = self.archive_extract( - self.ASSET_ZEPHYR_3_06, member=kernel_name) + self.ASSET_ZEPHYR_3_08, member=kernel_name) self.vm.set_console() self.vm.add_args('-kernel', kernel_file, '-nographic') @@ -72,7 +72,7 @@ class AST1030Machine(AspeedTest): self.vm.set_machine("ast1030-evb") kernel_name = "ast1030-evb-demo/zephyr.elf" - kernel_file = self.archive_extract(self.ASSET_ZEPHYR_3_06, + kernel_file = self.archive_extract(self.ASSET_ZEPHYR_3_08, member=kernel_name) otp_img = self.generate_otpmem_image() diff --git a/tests/functional/arm/test_aspeed_ast1060.py b/tests/functional/arm/test_aspeed_ast1060.py index 833cfb8272..d7158259e6 100755 --- a/tests/functional/arm/test_aspeed_ast1060.py +++ b/tests/functional/arm/test_aspeed_ast1060.py @@ -11,18 +11,18 @@ from qemu_test import Asset, exec_command_and_wait_for_pattern class AST1060Machine(AspeedTest): - ASSET_ASPEED_AST1060_PROT_3_05 = Asset( + ASSET_ASPEED_AST1060_PROT_3_07 = Asset( ('https://github.com/AspeedTech-BMC' - '/aspeed-zephyr-project/releases/download/v03.05' - '/ast1060_prot_v03.05.tgz'), - '63b36d7420290726ca80477de254474b7cb79539a42819bb1fe2665d598dadb5') + '/aspeed-zephyr-project/releases/download/v03.07' + '/ast1060_prot_v03.07.tgz'), + '55a7f51f0b77051a0ef2ada993a16c5033768e1b8e8be3babfc52c303eecd07f') - def test_arm_ast1060_prot_3_05(self): + def test_arm_ast1060_prot_3_07(self): self.set_machine('ast1060-evb') kernel_name = "ast1060_prot/zephyr.bin" kernel_file = self.archive_extract( - self.ASSET_ASPEED_AST1060_PROT_3_05, member=kernel_name) + self.ASSET_ASPEED_AST1060_PROT_3_07, member=kernel_name) self.vm.set_console() self.vm.add_args('-kernel', kernel_file, '-nographic') @@ -35,7 +35,7 @@ class AST1060Machine(AspeedTest): self.vm.set_machine("ast1060-evb") kernel_name = "ast1060_prot/zephyr.bin" - kernel_file = self.archive_extract(self.ASSET_ASPEED_AST1060_PROT_3_05, + kernel_file = self.archive_extract(self.ASSET_ASPEED_AST1060_PROT_3_07, member=kernel_name) otp_img = self.generate_otpmem_image() diff --git a/tests/functional/arm/test_aspeed_ast2500_sdk.py b/tests/functional/arm/test_aspeed_ast2500_sdk.py index 5ab36b99ab..95df32b84f 100755 --- a/tests/functional/arm/test_aspeed_ast2500_sdk.py +++ b/tests/functional/arm/test_aspeed_ast2500_sdk.py @@ -10,19 +10,19 @@ from aspeed import AspeedTest class AST2500Machine(AspeedTest): - ASSET_SDK_V1101_AST2500 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.01/ast2500-default-obmc.tar.gz', - '3faa1188198da2216837be4b53861c483a58c3ad63784089720bf8421e157da1') + ASSET_SDK_V1103_AST2500 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.03/ast2500-default-obmc.tar.gz', + '8e20cafddca04d73b799918d6f35b08c83c9f024e223a317b0ad71b97b84842f') def test_arm_ast2500_evb_sdk(self): self.set_machine('ast2500-evb') - self.archive_extract(self.ASSET_SDK_V1101_AST2500) + self.archive_extract(self.ASSET_SDK_V1103_AST2500) self.do_test_arm_aspeed_sdk_start( self.scratch_file("ast2500-default", "image-bmc")) - self.wait_for_console_pattern('ast2500-default login:') + self.wait_for_console_pattern('login:') if __name__ == '__main__': diff --git a/tests/functional/arm/test_aspeed_ast2500_sdk_515.py b/tests/functional/arm/test_aspeed_ast2500_sdk_515.py index 2b257986f6..516e96e52d 100755 --- a/tests/functional/arm/test_aspeed_ast2500_sdk_515.py +++ b/tests/functional/arm/test_aspeed_ast2500_sdk_515.py @@ -10,19 +10,19 @@ from aspeed import AspeedTest class AST2500Machine(AspeedTest): - ASSET_SDK_V1101_AST2500_515 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.01/ast2500-default-515-obmc.tar.gz', - 'b848ff620d2e9c83e2fb4736b4d1c39b82fdb041058cd42be42c3b177bf38eb9') + ASSET_SDK_V1103_AST2500_515 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.03/ast2500-default-515-obmc.tar.gz', + 'f17d3b0a5157bcf73c21c4981f838ea0b76c6406cc4a6409267d57d61758ebb6') def test_arm_ast2500_evb_sdk_515(self): self.set_machine('ast2500-evb') - self.archive_extract(self.ASSET_SDK_V1101_AST2500_515) + self.archive_extract(self.ASSET_SDK_V1103_AST2500_515) self.do_test_arm_aspeed_sdk_start( self.scratch_file("ast2500-default-515", "image-bmc")) - self.wait_for_console_pattern('ast2500-default-515 login:') + self.wait_for_console_pattern('login:') if __name__ == '__main__': diff --git a/tests/functional/arm/test_aspeed_ast2600_sdk.py b/tests/functional/arm/test_aspeed_ast2600_sdk.py index 01548dd135..4fc594dfd5 100755 --- a/tests/functional/arm/test_aspeed_ast2600_sdk.py +++ b/tests/functional/arm/test_aspeed_ast2600_sdk.py @@ -14,9 +14,9 @@ from qemu_test import exec_command_and_wait_for_pattern class AST2600Machine(AspeedTest): - ASSET_SDK_V1101_AST2600 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.01/ast2600-default-image.tar.gz', - '3c5b4d4ccf27b0d208a073f98426db54cd751b96143180cd15df1a83978f832c') + ASSET_SDK_V1103_AST2600 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.03/ast2600-default-image.tar.gz', + '47e3656a14bf7a4de28d3dfbf48bc2325443bc42d270f3bc82646f92f6dea165') def do_ast2600_pcie_test(self): exec_command_and_wait_for_pattern(self, @@ -49,7 +49,7 @@ class AST2600Machine(AspeedTest): self.set_machine('ast2600-evb') self.require_netdev('user') - self.archive_extract(self.ASSET_SDK_V1101_AST2600) + self.archive_extract(self.ASSET_SDK_V1103_AST2600) self.vm.add_args('-device', 'tmp105,bus=aspeed.i2c.bus.5,address=0x4d,id=tmp-test') @@ -63,7 +63,7 @@ class AST2600Machine(AspeedTest): self.do_test_arm_aspeed_sdk_start( self.scratch_file("ast2600-default-image", "image-bmc")) - self.wait_for_console_pattern('ast2600-default login:') + self.wait_for_console_pattern('login:') exec_command_and_wait_for_pattern(self, 'root', 'Password:') exec_command_and_wait_for_pattern(self, '0penBmc', diff --git a/tests/functional/arm/test_aspeed_ast2600_sdk_515.py b/tests/functional/arm/test_aspeed_ast2600_sdk_515.py index ec043e7d61..f5b14de083 100755 --- a/tests/functional/arm/test_aspeed_ast2600_sdk_515.py +++ b/tests/functional/arm/test_aspeed_ast2600_sdk_515.py @@ -10,19 +10,19 @@ from aspeed import AspeedTest class AST2600Machine(AspeedTest): - ASSET_SDK_V1101_AST2600_515 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.01/ast2600-default-515-image.tar.gz', - 'f3ccf1c08db71cf891637fc73131b80b2c0c0e005c06d5dcae0cf74fc458b43c') + ASSET_SDK_V1103_AST2600_515 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.03/ast2600-default-515-image.tar.gz', + 'c79d0197106f146476e82bb878e5438f6569bd30f3b53fbb520b59bc54f6b7dc') def test_arm_ast2600_evb_sdk_515(self): self.set_machine('ast2600-evb') - self.archive_extract(self.ASSET_SDK_V1101_AST2600_515) + self.archive_extract(self.ASSET_SDK_V1103_AST2600_515) self.do_test_arm_aspeed_sdk_start( self.scratch_file("ast2600-default-515-image", "image-bmc")) - self.wait_for_console_pattern('ast2600-default-515 login:') + self.wait_for_console_pattern('login:') if __name__ == '__main__': diff --git a/tests/functional/arm/test_aspeed_ast2600_sdk_otp.py b/tests/functional/arm/test_aspeed_ast2600_sdk_otp.py index f24dea1e8f..5813c59bd6 100755 --- a/tests/functional/arm/test_aspeed_ast2600_sdk_otp.py +++ b/tests/functional/arm/test_aspeed_ast2600_sdk_otp.py @@ -12,15 +12,15 @@ from qemu_test import exec_command_and_wait_for_pattern class AST2600Machine(AspeedTest): - ASSET_SDK_V1101_AST2600 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.01/ast2600-default-image.tar.gz', - '3c5b4d4ccf27b0d208a073f98426db54cd751b96143180cd15df1a83978f832c') + ASSET_SDK_V1103_AST2600 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.03/ast2600-default-image.tar.gz', + '47e3656a14bf7a4de28d3dfbf48bc2325443bc42d270f3bc82646f92f6dea165') def test_arm_ast2600_otp_blockdev_device(self): self.vm.set_machine("ast2600-evb") self.require_netdev('user') - image_path = self.archive_extract(self.ASSET_SDK_V1101_AST2600) + image_path = self.archive_extract(self.ASSET_SDK_V1103_AST2600) otp_img = self.generate_otpmem_image() self.vm.set_console() diff --git a/tests/functional/hexagon/meson.build b/tests/functional/hexagon/meson.build new file mode 100644 index 0000000000..2b5a1a8f26 --- /dev/null +++ b/tests/functional/hexagon/meson.build @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_hexagon_system_thorough = [ + 'arch_tests', +] diff --git a/tests/functional/hexagon/test_arch_tests.py b/tests/functional/hexagon/test_arch_tests.py new file mode 100755 index 0000000000..0834398c3b --- /dev/null +++ b/tests/functional/hexagon/test_arch_tests.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +# +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest, Asset +from qemu_test.cmd import wait_for_console_pattern + + +class ArchTestsUart(QemuSystemTest): + """ + Hexagon architecture verification tests + + These are bare-metal tests from hexagon-arch-tests that exercise + system functionality. + + Tests output results via UART. + """ + timeout = 60 + + ASSET_TARBALL = Asset( + "https://github.com/qualcomm/qemu-hexagon-testing/releases/" + "download/v0.2.5/arch_tests_uart.tar.gz", + "edb4f37b877a3a72a0e10920477458a43b40045d34398fee8cb763fefd342f4f", + ) + + def run_uart_test(self, test_name: str, + machine: str = "virt") -> None: + """ + Run an arch test binary and verify PASS via UART console output. + """ + self.set_machine(machine) + self.archive_extract(self.ASSET_TARBALL) + target_bin = self.scratch_file('arch_tests_uart_package', + 'bin', test_name) + self.vm.set_console() + self.set_vm_arg("-display", "none") + self.set_vm_arg("-kernel", target_bin) + self.vm.launch() + wait_for_console_pattern(self, "PASS") + + def test_exceptions(self) -> None: + """Tests exception delivery for trap instructions, privilege + violations, and verifies SSR cause codes and ELR values. + """ + self.run_uart_test("test_exceptions") + + def test_guest_mode(self) -> None: + """Tests guest mode entry/exit via CCR configuration, verifying + GSR fields, GELR, and guest event vector table dispatch. + """ + self.run_uart_test("test_guest_mode") + + def test_int_steering(self) -> None: + """Tests interrupt steering via priority-based routing to + specific threads using STID priority and iassignw. + """ + self.run_uart_test("test_int_steering") + + def test_cache(self) -> None: + """Tests cache operations: dckill/ickill, l2kill, dczeroa, + dccleaninva, cache disable/enable, barriers, and dcinva/dccleana. + """ + self.run_uart_test("test_cache") + + def test_l2vic(self) -> None: + """Tests the L2VIC interrupt controller: enable readback, + interrupt type readback, VID capture, and the fast interface. + """ + self.run_uart_test("test_l2vic") + + def test_threads(self) -> None: + """Tests hardware thread management: start/stop, MODECTL state, + per-thread HTID, shared memory, wait/resume, STID priority, and + SCHEDCFG/BESTWAIT readback. + """ + self.run_uart_test("test_threads") + + def test_tlb_mmu(self) -> None: + """Tests TLB/MMU operations: write/read/probe/invalidate, + global entries, multiple entries, overwrite, ASID matching, + and permission checks. + """ + self.run_uart_test("test_tlb_mmu") + + def test_user_mode(self) -> None: + """Tests user mode / privilege transitions: supervisor mode, + SSR UM/IE/XE/CE/PE bits, and the trap0 user-mode exit handler. + """ + self.run_uart_test("test_user_mode") + + +if __name__ == "__main__": + QemuSystemTest.main() diff --git a/tests/functional/meson.build b/tests/functional/meson.build index c158197c4b..c7362dd00e 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -14,6 +14,7 @@ subdir('aarch64') subdir('alpha') subdir('arm') subdir('avr') +subdir('hexagon') subdir('hppa') subdir('i386') subdir('loongarch64') diff --git a/tests/functional/migration.py b/tests/functional/migration.py index 4344e03be4..8d2428efc2 100644 --- a/tests/functional/migration.py +++ b/tests/functional/migration.py @@ -63,7 +63,8 @@ class MigrationTest(QemuSystemTest): self.assert_dest_vm(dst_vm) def migrate(self, dst_uri, src_uri=None): - dst_vm = self.get_vm('-incoming', 'defer', name="dst-qemu") + dst_vm = self.get_vm("dst-qemu") + dst_vm.add_args('-incoming', 'defer') self.configure_machine(dst_vm) dst_vm.launch() diff --git a/tests/functional/ppc64/meson.build b/tests/functional/ppc64/meson.build index f0f8ab8f61..cb3c745624 100644 --- a/tests/functional/ppc64/meson.build +++ b/tests/functional/ppc64/meson.build @@ -4,6 +4,7 @@ test_ppc64_timeouts = { 'fadump' : 480, 'hv' : 1000, 'mac99' : 120, + 'openbsd' : 240, 'powernv' : 480, 'pseries' : 480, 'replay' : 210, @@ -20,6 +21,7 @@ tests_ppc64_system_thorough = [ 'fadump', 'hv', 'mac99', + 'openbsd', 'powernv', 'pseries', 'replay', diff --git a/tests/functional/ppc64/test_openbsd.py b/tests/functional/ppc64/test_openbsd.py new file mode 100755 index 0000000000..bdbef6bf82 --- /dev/null +++ b/tests/functional/ppc64/test_openbsd.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# +# Test that OpenBSD boots on a ppc powernv machine and reaches the installer. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern + + +class OpenBSDPowerNV(QemuSystemTest): + + ASSET_MINIROOT = Asset( + 'https://kirill.korins.ky/pub/qemu-powerpc64-openbsd/miniroot79.img', + '7829e42b75d81cafd732038b9d63228b79c1f5828d8375872a4bb655e1d6b13c') + + ASSET_BOOTKERNEL = Asset( + 'https://kirill.korins.ky/pub/qemu-powerpc64-openbsd/pnor.BOOTKERNEL', + '397ce43ce61910e1a2c4f13d301f957e61513a9ec5371bc3e87d3095411fae7b') + + def test_powernv9_openbsd_installer(self): + self.set_machine('powernv9') + self.require_accelerator('tcg') + + miniroot_path = self.ASSET_MINIROOT.fetch() + bootkernel_path = self.ASSET_BOOTKERNEL.fetch() + + self.vm.set_console() + self.vm.add_args('-cpu', 'power9', + '-accel', 'tcg,thread=single', + '-smp', '1,cores=1,threads=1', + '-m', '2g', + '-kernel', bootkernel_path, + '-device', + 'ich9-ahci,id=sata0,bus=pcie.0,addr=0x0', + '-drive', + f'file={miniroot_path},format=raw,if=none,' + 'id=bootdisk,snapshot=on', + '-device', + 'ide-hd,bus=sata0.0,unit=0,drive=bootdisk,' + 'bootindex=1') + self.vm.launch() + + wait_for_console_pattern(self, 'OpenBSD 7.9 (RAMDISK)', 'panic:') + wait_for_console_pattern( + self, + '(I)nstall, (U)pgrade, (A)utoinstall or (S)hell?', + 'panic:') + + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/ppc64/test_powernv.py b/tests/functional/ppc64/test_powernv.py index bac2017e18..f3e05e4d38 100755 --- a/tests/functional/ppc64/test_powernv.py +++ b/tests/functional/ppc64/test_powernv.py @@ -90,6 +90,7 @@ class PowernvMachine(LinuxKernelTest): def test_linux_remote_interrupts(self): self.require_accelerator("tcg") + self.require_netdev('user') self.set_machine('powernv') # Have below setup in this test: diff --git a/tests/functional/pylintrc b/tests/functional/pylintrc index 949bea611f..373aabd6ca 100644 --- a/tests/functional/pylintrc +++ b/tests/functional/pylintrc @@ -79,6 +79,12 @@ disable=bad-inline-option, useless-suppression, +[TYPECHECK] + +# cv2 is a C extension module whose members are not visible to pylint +generated-members=cv2.* + + [SIMILARITIES] # Minimum lines number of a similarity. diff --git a/tests/functional/qemu_test/decorators.py b/tests/functional/qemu_test/decorators.py index fcf236ecfd..aa135acc78 100644 --- a/tests/functional/qemu_test/decorators.py +++ b/tests/functional/qemu_test/decorators.py @@ -6,6 +6,7 @@ import importlib import os import platform import resource +import subprocess from unittest import skipIf, skipUnless from .cmd import which @@ -177,3 +178,18 @@ def skipLockedMemoryTest(locked_memory): ulimit_memory == resource.RLIM_INFINITY or ulimit_memory >= locked_memory * 1024, f'Test required {locked_memory} kB of available locked memory', ) + +''' +Decorator to skip execution of a test if passwordless +sudo command is not available. +''' +def skipWithoutSudo(): + proc = subprocess.run(["sudo", "-n", "/bin/true"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True, + check=False) + + return skipUnless(proc.returncode == 0, + f'requires password-less sudo access: {proc.stdout}') diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index e4179d165c..69d3d06cc0 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -381,11 +381,12 @@ class QemuSystemTest(QemuBaseTest): if helptxt.find(devicename) < 0: self.skipTest('no support for device ' + devicename) - def _new_vm(self, name, *args): + def _new_vm(self, name, monitor_address): vm = QEMUMachine(self.qemu_bin, name=name, base_temp_dir=self.workdir, - log_dir=self.log_file()) + log_dir=self.log_file(), + monitor_address=monitor_address) self.log.debug('QEMUMachine "%s" created', name) self.log.debug('QEMUMachine "%s" temp_dir: %s', name, vm.temp_dir) @@ -394,20 +395,17 @@ class QemuSystemTest(QemuBaseTest): vm.add_args("-chardev", f"socket,id=backdoor,path={sockpath},server=on,wait=off", "-mon", "chardev=backdoor,mode=control") - - 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): + def get_vm(self, name=None, monitor_address=None): if not name: name = str(uuid.uuid4()) if self._vms.get(name) is None: - self._vms[name] = self._new_vm(name, *args) + self._vms[name] = self._new_vm(name, monitor_address) if self.cpu is not None: self._vms[name].add_args('-cpu', self.cpu) if self.machine is not None: diff --git a/tests/functional/x86_64/meson.build b/tests/functional/x86_64/meson.build index 27b31f2e96..0353b2af8e 100644 --- a/tests/functional/x86_64/meson.build +++ b/tests/functional/x86_64/meson.build @@ -28,6 +28,10 @@ if not get_option('asan') tests_x86_64_system_quick += [ 'memlock' ] endif +if igvm.found() + tests_x86_64_system_quick += [ 'vm_launch_update' ] +endif + tests_x86_64_system_thorough = [ 'acpi_bits', 'hotplug_blk', diff --git a/tests/functional/x86_64/test_vm_launch_update.py b/tests/functional/x86_64/test_vm_launch_update.py new file mode 100755 index 0000000000..aac7ef915f --- /dev/null +++ b/tests/functional/x86_64/test_vm_launch_update.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# +# Check for vm-launch-update device. +# +# Copyright (c) 2026 Red Hat, Inc. +# +# Author: +# Ani Sinha +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest +import time + +class VmLaunchUpdateDeviceCheck(QemuSystemTest): + DELAY_BOOT_SEQUENCE = 1 + + def vm_launch_update_pass(self): + """ + Basic test to make sure vm-launch-update device can be instantiated. + """ + self.vm.add_args('-device', 'vm-launch-update,id=fwupd1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertEqual(self.vm.exitcode(), 0, "QEMU exit code should be 0") + + def multiple_device_fail(self): + """ + Only one vm-launch-update device can be instantiated. Ensure failure if + user tries to create more than one device. + """ + self.vm.add_args('-device', 'vm-launch-update,id=fw1', + '-device', 'vm-launch-update,id=fw2') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), + r'at most one vm-launch-update device is permitted') + + def test_vm_launch_update(self): + self.vm_launch_update_pass() + self.multiple_device_fail() + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/lcitool/mappings.yml b/tests/lcitool/mappings.yml index 62fe60d047..1816556dde 100644 --- a/tests/lcitool/mappings.yml +++ b/tests/lcitool/mappings.yml @@ -3,6 +3,30 @@ mappings: bindgen: Ubuntu2204: + python3: + CentOSStream9: python3.11 + + python3-devel: + CentOSStream9: python3.11-devel + + python3-pip: + CentOSStream9: python3.11-pip + + python3-setuptools: + CentOSStream9: python3.11-setuptools + + python3-sphinx: + CentOSStream9: + + python3-sphinx-rtd-theme: + CentOSStream9: + + python3-venv: + CentOSStream9: python3.11 + + python3-wheel: + CentOSStream9: python3.11-wheel + meson: # Use Meson from PyPI wherever Rust is enabled Debian: @@ -23,7 +47,7 @@ mappings: pypi_mappings: # Request more recent version meson: - default: meson==1.8.1 + default: meson==1.12.0 # Drop packages that need devel headers python3-tomli: diff --git a/tests/lcitool/projects/qemu.yml b/tests/lcitool/projects/qemu.yml index 7d185cda3c..b3c9993672 100644 --- a/tests/lcitool/projects/qemu.yml +++ b/tests/lcitool/projects/qemu.yml @@ -91,11 +91,7 @@ packages: - pkg-config - pulseaudio - python3 - - python3-numpy - - python3-opencv - - python3-pillow - python3-pip - - python3-PyYAML - python3-setuptools - python3-sphinx - python3-sphinx-rtd-theme diff --git a/tests/lcitool/targets/opensuse-leap-15.yml b/tests/lcitool/targets/centos-stream-9.yml similarity index 100% rename from tests/lcitool/targets/opensuse-leap-15.yml rename to tests/lcitool/targets/centos-stream-9.yml diff --git a/tests/qemu-iotests/039 b/tests/qemu-iotests/039 index e43e7026ce..94a8bfe754 100755 --- a/tests/qemu-iotests/039 +++ b/tests/qemu-iotests/039 @@ -84,6 +84,17 @@ $QEMU_IO -r -c "read -P 0x5a 0 512" "$TEST_IMG" | _filter_qemu_io # The dirty bit must be set _qcow2_dump_header | grep incompatible_features +echo +echo "== Read-only open must not crash on close ==" + +# We must not try to write the QCOW2 header to a read-only image. +$QEMU_IMG info --image-opts \ + "driver=$IMGFMT,read-only=on,file.driver=file,file.filename=$TEST_IMG,file.read-only=off" \ + > /dev/null + +# The dirty bit must still be set: this open never wrote any guest data +_qcow2_dump_header | grep incompatible_features + echo echo "== Repairing the image file must succeed ==" diff --git a/tests/qemu-iotests/039.out b/tests/qemu-iotests/039.out index 8fdbcc528a..c66361128f 100644 --- a/tests/qemu-iotests/039.out +++ b/tests/qemu-iotests/039.out @@ -24,6 +24,9 @@ read 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) incompatible_features [0] +== Read-only open must not crash on close == +incompatible_features [0] + == Repairing the image file must succeed == ERROR cluster 5 refcount=0 reference=1 Rebuilding refcount structure diff --git a/tests/qemu-iotests/108 b/tests/qemu-iotests/108 index 54e935acf2..bf808b3512 100755 --- a/tests/qemu-iotests/108 +++ b/tests/qemu-iotests/108 @@ -34,8 +34,8 @@ _cleanup() if [ -f "$TEST_DIR/qsd.pid" ]; then qsd_pid=$(cat "$TEST_DIR/qsd.pid") kill -KILL "$qsd_pid" - fusermount -u "$TEST_DIR/fuse-export" &>/dev/null fi + fusermount -u "$TEST_DIR/fuse-export" &>/dev/null rm -f "$TEST_DIR/fuse-export" } trap "_cleanup; exit \$status" 0 1 2 3 15 diff --git a/tests/qemu-iotests/212 b/tests/qemu-iotests/212 index d4af0c4ac8..4ca6149b6b 100755 --- a/tests/qemu-iotests/212 +++ b/tests/qemu-iotests/212 @@ -133,13 +133,15 @@ with iotests.FilePath('t.parallels') as disk_path, \ # # Maximum size # + # Largest catalog parallels_open() can address. + # iotests.log("=== Maximum size ===") iotests.log("") vm.launch() vm.blockdev_create({ 'driver': imgfmt, 'file': 'node0', - 'size': 4503599627369984}) + 'size': 562949952372736}) vm.shutdown() iotests.img_info_log(disk_path) @@ -158,13 +160,15 @@ with iotests.FilePath('t.parallels') as disk_path, \ # 4. 2^63 - 512 (generally valid, but with the image header the file will # exceed 63 bits) # 5. 2^52 (512 bytes more than maximum image size) + # 6. 2^52 - 512 (wraps bat_entries to 0 at the default 1 MiB cluster size) iotests.log("=== Invalid sizes ===") iotests.log("") vm.launch() for size in [ 1234, 18446744073709551104, 9223372036854775808, - 9223372036854775296, 4503599627370497 ]: + 9223372036854775296, 4503599627370497, + 4503599627369984 ]: vm.blockdev_create({ 'driver': imgfmt, 'file': 'node0', 'size': size }) diff --git a/tests/qemu-iotests/212.out b/tests/qemu-iotests/212.out index 8102033488..d59f8ed44b 100644 --- a/tests/qemu-iotests/212.out +++ b/tests/qemu-iotests/212.out @@ -69,14 +69,14 @@ virtual size: 0 B (0 bytes) === Maximum size === -{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 4503599627369984}}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 562949952372736}}} {"return": {}} {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} image: TEST_IMG file format: IMGFMT -virtual size: 4 PiB (4503599627369984 bytes) +virtual size: 512 TiB (562949952372736 bytes) === Invalid sizes === @@ -110,6 +110,12 @@ Job failed: Image size is too large for this cluster size {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 4503599627369984}}} +{"return": {}} +Job failed: Catalog too large +{"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}}} diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 298bc483e0..bcb1ec50a9 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -981,6 +981,20 @@ _require_drivers() done } +# Skip if FUSE is unusable: not compiled in, or the export failed to +# mount. $1 is the failing 'block-export-add' reply. +_notrun_on_fuse_error() +{ + case "$1" in + *"Parameter 'type' does not accept value 'fuse'"*) + _notrun "No FUSE support" + ;; + *"Failed to mount FUSE session"*) + _notrun "FUSE not usable in this environment" + ;; + esac +} + # Check that we have a file system that allows huge (but very sparse) files # _require_large_file() diff --git a/tests/qemu-iotests/meson.build b/tests/qemu-iotests/meson.build index bc6132a0f7..8857f25f29 100644 --- a/tests/qemu-iotests/meson.build +++ b/tests/qemu-iotests/meson.build @@ -52,16 +52,17 @@ foreach driver, speed: qemu_iotests_drivers '-tap', '-' + driver, item, '--source-dir', meson.current_source_dir(), '--build-dir', meson.current_build_dir()] - # Some individual tests take as long as 45 seconds - # Bump the timeout to 3 minutes for some headroom - # on slow machines to minimize spurious failures + # Some individual tests take as long as 3 minutes + # in high load scenarios. Bump the timeout to 5 + # minutes for some headroom on slow / loaded + # machines to minimize spurious failures test('io-' + driver + '-' + item, python, args: args, depends: qemu_iotests_binaries, env: qemu_iotests_env, protocol: 'tap', - timeout: 180, + timeout: 300, suite: suites) endforeach endif @@ -94,16 +95,14 @@ foreach driver, speed: qemu_iotests_drivers '-tap', '-' + driver, item, '--source-dir', meson.current_source_dir(), '--build-dir', meson.current_build_dir()] - # Some individual tests take as long as 45 seconds - # Bump the timeout to 3 minutes for some headroom - # on slow machines to minimize spurious failures + # See earlier note about timeouts test('io-' + driver + '-' + item, python, args: args, depends: qemu_iotests_binaries, env: qemu_iotests_env, protocol: 'tap', - timeout: 180, + timeout: 300, suite: suites) endforeach endforeach diff --git a/tests/qemu-iotests/testrunner.py b/tests/qemu-iotests/testrunner.py index dbe2dddc32..cc36867719 100644 --- a/tests/qemu-iotests/testrunner.py +++ b/tests/qemu-iotests/testrunner.py @@ -26,7 +26,7 @@ import contextlib import json import shutil import sys -from multiprocessing import Pool +from multiprocessing import get_context from typing import List, Optional, Any, Sequence, Dict from testenv import TestEnv @@ -125,7 +125,7 @@ class TestRunner(contextlib.AbstractContextManager['TestRunner']): assert TestRunner.shared_self is None TestRunner.shared_self = self - with Pool(jobs) as p: + with get_context('fork').Pool(jobs) as p: results = p.starmap(self.proc_run_test, zip(tests, [test_field_width] * len(tests))) diff --git a/tests/qemu-iotests/tests/file-io-error b/tests/qemu-iotests/tests/file-io-error index fb8db73b31..0d970c102f 100755 --- a/tests/qemu-iotests/tests/file-io-error +++ b/tests/qemu-iotests/tests/file-io-error @@ -89,9 +89,7 @@ output=$(_send_qemu_cmd $QEMU_HANDLE \ 'return' \ | grep -v 'option allow_other only allowed if') -if echo "$output" | grep -q "Parameter 'type' does not accept value 'fuse'"; then - _notrun 'No FUSE support' -fi +_notrun_on_fuse_error "$output" echo "$output" echo diff --git a/tests/qemu-iotests/tests/fuse-allow-other b/tests/qemu-iotests/tests/fuse-allow-other index eaa39f8f23..50a36601d7 100755 --- a/tests/qemu-iotests/tests/fuse-allow-other +++ b/tests/qemu-iotests/tests/fuse-allow-other @@ -77,6 +77,8 @@ fuse_export_add() _notrun "allow_other not supported" fi + _notrun_on_fuse_error "$output" + echo "$output" } diff --git a/tests/qemu-iotests/tests/fuse-mmap-shared b/tests/qemu-iotests/tests/fuse-mmap-shared index 52941a3bb6..b190105894 100755 --- a/tests/qemu-iotests/tests/fuse-mmap-shared +++ b/tests/qemu-iotests/tests/fuse-mmap-shared @@ -28,9 +28,12 @@ def test_fuse_support(mount_point): }) test_qsd.stop() if 'error' in res: - assert (res['error']['desc'] == - "Parameter 'type' does not accept value 'fuse'") - iotests.notrun('No FUSE support') + desc = res['error']['desc'] + if desc == "Parameter 'type' does not accept value 'fuse'": + iotests.notrun('No FUSE support') + if 'Failed to mount FUSE session' in desc: + iotests.notrun('FUSE not usable in this environment') + assert False, desc # Shared mmap when using direct IO is only supported for Linux kernels >= 6.6 # with commit e78662e818f94 ("fuse: add a new fuse init flag to relax diff --git a/tests/qemu-iotests/tests/fuse-truncate b/tests/qemu-iotests/tests/fuse-truncate new file mode 100755 index 0000000000..7eee35111c --- /dev/null +++ b/tests/qemu-iotests/tests/fuse-truncate @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 +# group: rw +# +# Test how fuse exports behave with regard to O_TRUNC. +# +# Copyright (C) 2026 Proxmox Server Solutions GmbH +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import subprocess +from pathlib import Path + +import iotests +from iotests import qemu_img, QemuStorageDaemon + +fuse_mount_point = os.path.join(iotests.test_dir, 'export.fuse') +image_size = 1 * 1024 * 1024 +image = os.path.join(iotests.test_dir, 'image.' + iotests.imgfmt) + +def check_fuse_support(): + Path(fuse_mount_point).touch() + test_qsd = QemuStorageDaemon('--blockdev', 'null-co,node-name=node0', + qmp=True) + res = test_qsd.qmp('block-export-add', { + 'id': 'exp0', + 'type': 'fuse', + 'node-name': 'node0', + 'mountpoint': fuse_mount_point, + 'allow-other': 'off' + }) + test_qsd.stop() + os.remove(fuse_mount_point) + if 'error' in res: + assert (res['error']['desc'] == + "Parameter 'type' does not accept value 'fuse'") + iotests.notrun('No FUSE support') + +def check_sudo_support(): + try: + subprocess.run(['sudo', '-n', 'losetup', '--version'], + capture_output=True, check=True) + except (OSError, subprocess.CalledProcessError): + return False + try: + subprocess.run(['sudo', '-n', 'chmod', '--version'], + capture_output=True, check=True) + except (OSError, subprocess.CalledProcessError): + return False + return True + +check_fuse_support() + +class TestTruncateBase(iotests.QMPTestCase): + growable = False + supports_preconditions = True + + def evaluate_preconditions(self): + return + + def add_blockdev(self): + qemu_img('create', '-f', iotests.imgfmt, image, str(image_size)) + self.qsd.cmd('blockdev-add', { + 'node-name': 'node0', + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': image + } + }) + + def cleanup_blockdev(self): + os.remove(image) + + def add_export(self): + self.qsd.cmd('block-export-add', { + 'id': 'exp0', + 'type': 'fuse', + 'node-name': 'node0', + 'mountpoint': fuse_mount_point, + 'growable': self.growable, + 'writable': True, + 'allow-other': 'off' + }) + + def stop_qsd(self): + if self.qsd: + self.qsd.cmd('block-export-del', {'id': 'exp0'}) + self.qsd.stop() + self.qsd = None + + def setUp(self): + self.evaluate_preconditions() + if not self.supports_preconditions: + return + Path(fuse_mount_point).touch() + self.qsd = QemuStorageDaemon(qmp=True) + self.add_blockdev() + self.add_export() + + def tearDown(self): + if not self.supports_preconditions: + return + self.stop_qsd() + self.cleanup_blockdev() + os.remove(fuse_mount_point) + +class TestTruncateFileGrowable(TestTruncateBase): + growable = True + + def test_o_trunc(self): + with open(fuse_mount_point, 'w+b') as file: + file.seek(0, os.SEEK_END) + self.assertEqual(file.tell(), 0) + file.write(b"test") + self.stop_qsd() + +class TestTruncateFileNotGrowable(TestTruncateBase): + growable = False + + def test_o_trunc(self): + with open(fuse_mount_point, 'w+b') as file: + file.seek(0, os.SEEK_END) + self.assertEqual(file.tell(), image_size) + file.seek(0, os.SEEK_SET) + file.write(b"test") + self.stop_qsd() + +class TestTruncateBlockdev(TestTruncateBase): + growable = False + + def evaluate_preconditions(self): + self.supports_preconditions = check_sudo_support() + + def add_blockdev(self): + qemu_img('create', '-f', iotests.imgfmt, image, str(image_size)) + res = subprocess.run(['sudo', '-n', 'losetup', '--show', '-f', image], + check=True, capture_output=True, text=True) + self.loopdev = res.stdout.strip() + subprocess.run(['sudo', '-n', 'chmod', 'go+rw', self.loopdev], + check=True, capture_output=True) + self.qsd.cmd('blockdev-add', { + 'node-name': 'node0', + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'host_device', + 'filename': self.loopdev + } + }) + + def cleanup_blockdev(self): + subprocess.run(['sudo', '-n', 'losetup', '--detach', self.loopdev], + check=True, capture_output=True) + os.remove(image) + + def test_o_trunc(self): + if not self.supports_preconditions: + iotests.case_notrun('No passwordless sudo for losetup and chmod') + return + + with open(fuse_mount_point, 'w+b') as file: + file.seek(0, os.SEEK_END) + self.assertEqual(file.tell(), image_size) + file.seek(0, os.SEEK_SET) + file.write(b"test") + self.stop_qsd() + +if __name__ == '__main__': + iotests.main(supported_fmts=['raw'], + supported_protocols=['file'], + supported_platforms=['linux']) diff --git a/tests/qemu-iotests/tests/fuse-truncate.out b/tests/qemu-iotests/tests/fuse-truncate.out new file mode 100644 index 0000000000..8d7e996700 --- /dev/null +++ b/tests/qemu-iotests/tests/fuse-truncate.out @@ -0,0 +1,5 @@ +... +---------------------------------------------------------------------- +Ran 3 tests + +OK diff --git a/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test b/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test index c519e6db8c..33ff2b861f 100755 --- a/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test +++ b/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test @@ -160,12 +160,26 @@ class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase): self.vm_b.cmd('migrate-set-capabilities', capabilities=caps) + # Throttle so the chunks covering our discards (the only ones + # not skipped by the all-zero fast path) can't outrun the check + # below. + self.vm_a.cmd('migrate-set-parameters', max_bandwidth=16536) + self.vm_a.cmd('migrate', uri='exec:cat>' + fifo) self.vm_a.cmd('migrate-start-postcopy') event_resume = self.vm_b.event_wait('RESUME') self.vm_b_events.append(event_resume) + + # bitmap0 can't already have its final content: that requires + # the bit data to have actually arrived. + result = self.vm_b.qmp('x-debug-block-dirty-bitmap-sha256', + node='drive0', name='bitmap0') + assert result['return']['sha256'] != all_discards_sha256 + + self.vm_a.cmd('migrate-set-parameters', max_bandwidth=0) + return (event_resume, discards1_sha256, all_discards_sha256) def test_postcopy_success(self): @@ -186,7 +200,6 @@ class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase): downtime = event_dist(event_stop, event_resume) postcopy_time = event_dist(event_resume, event_complete) - assert downtime * 10 < postcopy_time if debug: print('downtime:', downtime) print('postcopy_time:', postcopy_time) diff --git a/tests/qemu-iotests/tests/migrate-bitmaps-test b/tests/qemu-iotests/tests/migrate-bitmaps-test index 8fb4099201..cb9154ca8d 100755 --- a/tests/qemu-iotests/tests/migrate-bitmaps-test +++ b/tests/qemu-iotests/tests/migrate-bitmaps-test @@ -206,6 +206,39 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): self.vm_b.launch() self.check_bitmap(self.vm_b, sha256 if persistent else False) + def test_migration_to_readonly_destination(self): + granularity = 512 + mig_caps = [{'capability': 'events', 'state': True}, + {'capability': 'dirty-bitmaps', 'state': True}] + + self.vm_b.add_incoming("defer") + self.vm_b.add_drive(disk_b, 'read-only=on') + + self.add_bitmap(self.vm_a, granularity, True) + self.vm_a.hmp_qemu_io('drive0', 'write 0 4096') + + self.vm_a.cmd('migrate-set-capabilities', capabilities=mig_caps) + self.vm_a.cmd('migrate', uri=mig_cmd) + while True: + event = self.vm_a.event_wait('MIGRATION') + if event['data']['status'] == 'completed': + break + self.vm_a.shutdown() + + self.vm_b.launch() + self.vm_b.cmd('migrate-set-capabilities', capabilities=mig_caps) + self.vm_b.cmd('migrate-incoming', uri=incoming_cmd) + while True: + event = self.vm_b.event_wait('MIGRATION') + if event['data']['status'] in ('completed', 'failed'): + break + + self.assert_qmp(event, 'data/status', 'failed') + + # A failed incoming load makes the destination process exit on + # its own; reap it so tearDown()'s shutdown() is a clean no-op. + self.vm_b.wait() + def inject_test_case(klass, suffix, method, *args, **kwargs): mc = operator.methodcaller(method, *args, **kwargs) diff --git a/tests/qemu-iotests/tests/migrate-bitmaps-test.out b/tests/qemu-iotests/tests/migrate-bitmaps-test.out index cafb8161f7..73e375a9d7 100644 --- a/tests/qemu-iotests/tests/migrate-bitmaps-test.out +++ b/tests/qemu-iotests/tests/migrate-bitmaps-test.out @@ -1,5 +1,5 @@ -..................................... +...................................... ---------------------------------------------------------------------- -Ran 37 tests +Ran 38 tests OK diff --git a/tests/qemu-iotests/tests/parallels-checks b/tests/qemu-iotests/tests/parallels-checks index b281246a42..d2a08049d9 100755 --- a/tests/qemu-iotests/tests/parallels-checks +++ b/tests/qemu-iotests/tests/parallels-checks @@ -44,6 +44,7 @@ _supported_os Linux SIZE=$((4 * 1024 * 1024)) IMGFMT=parallels CLUSTER_SIZE_OFFSET=28 +BAT_ENTRIES_OFFSET=32 DATA_OFF_OFFSET=48 BAT_OFFSET=64 @@ -199,6 +200,107 @@ _check_test_img -r all echo "== check first cluster ==" { $QEMU_IO -r -c "read -P 0x55 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +# Clear image +_make_test_img $SIZE + +echo "== TEST HUGE BAT TABLE OPEN ==" + +# Overflows a single read request, but stays under parallels_open()'s +# own catalog-size cap. +BAT_ENTRIES=536870896 +HEADER_SIZE=$((64 + 4 * BAT_ENTRIES)) + +echo "== advertise a BAT table larger than BDRV_REQUEST_MAX_BYTES ==" +poke_file "$TEST_IMG" "$BAT_ENTRIES_OFFSET" "\xf0\xff\xff\x1f" + +echo "== grow the file to match, without writing real data ==" +truncate -s $HEADER_SIZE "$TEST_IMG" + +echo "== open must succeed: the header/BAT read is chunked ==" +_img_info + +echo "== an unallocated cluster still reads as zeroes ==" +{ $QEMU_IO -r -c "read -P 0x00 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +# Clear image +_make_test_img $SIZE + +echo "== TEST OVERSIZED VIRTUAL DISK CHECK ==" + +BAT_ENTRIES_OFFSET=32 +NB_SECTORS_OFFSET=36 + +TRACKS=$(peek_file_le $TEST_IMG $CLUSTER_SIZE_OFFSET 4) +BAT_ENTRIES=$(peek_file_le $TEST_IMG $BAT_ENTRIES_OFFSET 4) +COVERED_SECTORS=$((BAT_ENTRIES * TRACKS)) + +echo "== advertise one more cluster than the BAT covers ==" +poke_file_le "$TEST_IMG" $NB_SECTORS_OFFSET 8 $((COVERED_SECTORS + TRACKS)) + +echo "== open must fail cleanly instead of aborting ==" +_img_info + +echo "== write into the uncovered range must fail cleanly too ==" +{ $QEMU_IO -c "write -P 0x41 $((COVERED_SECTORS * 512)) $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +# Clear image +_make_test_img $SIZE + +echo "== TEST BAT ENTRY POINTING OUTSIDE IMAGE ==" + +echo "== corrupt image: point first cluster far outside the file ==" +poke_file_le "$TEST_IMG" $BAT_OFFSET 4 1000000 + +echo "== read-only read must return zeroes, not an I/O error ==" +{ $QEMU_IO -r -c "read -P 0x00 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== write must allocate a fresh cluster instead of trusting the entry ==" +{ $QEMU_IO -c "write -P 0x77 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== file did not grow anywhere near the bogus offset ==" +file_size=`stat --printf="%s" "$TEST_IMG"` +if [ "$file_size" -lt $((16 * 1024 * 1024)) ]; then + echo "file size sane: yes" +else + echo "file size sane: no ($file_size bytes)" +fi + +echo "== data reads back correctly ==" +{ $QEMU_IO -r -c "read -P 0x77 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +# Clear image, with a small cluster size so the BAT table itself spans +# more than one cluster and there is room to point before data_off. +_make_test_img -o cluster_size=512 65536 + +SMALL_CLUSTER_SIZE=$(peek_file_le $TEST_IMG $CLUSTER_SIZE_OFFSET 4) +SMALL_CLUSTER_SIZE=$((SMALL_CLUSTER_SIZE * 512)) +DATA_OFF=$(peek_file_le $TEST_IMG $DATA_OFF_OFFSET 4) +echo "cluster size: $SMALL_CLUSTER_SIZE, data offset (sectors): $DATA_OFF" + +# Cluster index 1 starts at this byte offset, which must be < data_off +# in sectors * 512 for this test to actually exercise the bug. +VICTIM_OFFSET=$SMALL_CLUSTER_SIZE + +echo "== TEST BAT ENTRY POINTING BEFORE DATA AREA ==" + +echo "== corrupt image: point first cluster into the BAT table itself ==" +poke_file_le "$TEST_IMG" $BAT_OFFSET 4 1 + +echo "== qemu-img check detects it without repairing ==" +_check_test_img + +echo "== bytes at the victim offset before write ==" +echo "$(peek_file_le "$TEST_IMG" $VICTIM_OFFSET 4)" + +echo "== write must allocate a fresh cluster instead of clobbering the BAT ==" +{ $QEMU_IO -c "write -P 0x88 0 $SMALL_CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== bytes at the victim offset are unchanged ==" +echo "$(peek_file_le "$TEST_IMG" $VICTIM_OFFSET 4)" + +echo "== data reads back correctly ==" +{ $QEMU_IO -r -c "read -P 0x88 0 $SMALL_CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/tests/parallels-checks.out b/tests/qemu-iotests/tests/parallels-checks.out index 9793423111..c33f3852a8 100644 --- a/tests/qemu-iotests/tests/parallels-checks.out +++ b/tests/qemu-iotests/tests/parallels-checks.out @@ -129,4 +129,57 @@ No errors were found on the image. == check first cluster == read 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 +== TEST HUGE BAT TABLE OPEN == +== advertise a BAT table larger than BDRV_REQUEST_MAX_BYTES == +== grow the file to match, without writing real data == +== open must succeed: the header/BAT read is chunked == +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 4 MiB (4194304 bytes) +== an unallocated cluster still reads as zeroes == +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 +== TEST OVERSIZED VIRTUAL DISK CHECK == +== advertise one more cluster than the BAT covers == +== open must fail cleanly instead of aborting == +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Invalid image: Catalog size too small for advertised disk size +== write into the uncovered range must fail cleanly too == +qemu-io: can't open device TEST_DIR/t.parallels: Invalid image: Catalog size too small for advertised disk size +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 +== TEST BAT ENTRY POINTING OUTSIDE IMAGE == +== corrupt image: point first cluster far outside the file == +== read-only read must return zeroes, not an I/O error == +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== write must allocate a fresh cluster instead of trusting the entry == +Repairing cluster 0 is outside image +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== file did not grow anywhere near the bogus offset == +file size sane: yes +== data reads back correctly == +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536 +cluster size: 512, data offset (sectors): 2 +== TEST BAT ENTRY POINTING BEFORE DATA AREA == +== corrupt image: point first cluster into the BAT table itself == +== qemu-img check detects it without repairing == +ERROR cluster 0 is outside image + +1 errors were found on the image. +Data may be corrupted, or further writes to the image may corrupt it. +== bytes at the victim offset before write == +0 +== write must allocate a fresh cluster instead of clobbering the BAT == +Repairing cluster 0 is outside image +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== bytes at the victim offset are unchanged == +0 +== data reads back correctly == +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) *** done diff --git a/tests/qemu-iotests/tests/remove-bitmap-from-backing b/tests/qemu-iotests/tests/remove-bitmap-from-backing index 15be32dcb9..a54984fa58 100755 --- a/tests/qemu-iotests/tests/remove-bitmap-from-backing +++ b/tests/qemu-iotests/tests/remove-bitmap-from-backing @@ -35,7 +35,7 @@ qemu_img('bitmap', '--add', base, 'bitmap0') # Just assert that our method of checking bitmaps in the image works. assert 'bitmaps' in qemu_img_info(base)['format-specific']['data'] -vm = iotests.VM().add_drive(top, 'backing.node-name=base') +vm = iotests.VM().add_drive(top, 'node-name=top,backing.node-name=base') vm.launch() log('Trying to remove persistent bitmap from r-o base node, should fail:') @@ -66,6 +66,33 @@ result = vm.qmp('blockdev-reopen', **new_base_opts) if result != {'return': {}}: log('Failed to reopen: ' + str(result)) +log('Adding a persistent bitmap to the r-o base node, should fail:') +vm.qmp_log('block-dirty-bitmap-add', node='base', name='bitmap1', + persistent=True) + +log('Same add inside a transaction, preceded by an otherwise valid') +log('action: the whole transaction must fail and roll back the') +log('already-succeeded first action too:') +vm.qmp_log('transaction', actions=[ + {'type': 'block-dirty-bitmap-add', + 'data': {'node': 'top', 'name': 'bitmap2', 'persistent': True}}, + {'type': 'block-dirty-bitmap-add', + 'data': {'node': 'base', 'name': 'bitmap1', 'persistent': True}}, +]) + +log('bitmap2 on the rw top node must not have survived the rollback:') +vm.qmp_log('block-dirty-bitmap-remove', node='top', name='bitmap2') + +log('Marking the rw top node inactive:') +vm.qmp_log('blockdev-set-active', **{'node-name': 'top', 'active': False}) + +log('Adding a persistent bitmap to a rw but inactive node, should fail:') +vm.qmp_log('block-dirty-bitmap-add', node='top', name='bitmap3', + persistent=True) + +log('Reactivating the top node:') +vm.qmp_log('blockdev-set-active', **{'node-name': 'top', 'active': True}) + vm.shutdown() if 'bitmaps' in qemu_img_info(base)['format-specific']['data']: diff --git a/tests/qemu-iotests/tests/remove-bitmap-from-backing.out b/tests/qemu-iotests/tests/remove-bitmap-from-backing.out index c28af82c75..628fa737d9 100644 --- a/tests/qemu-iotests/tests/remove-bitmap-from-backing.out +++ b/tests/qemu-iotests/tests/remove-bitmap-from-backing.out @@ -1,6 +1,26 @@ Trying to remove persistent bitmap from r-o base node, should fail: {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "base"}} -{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap0' is readonly and cannot be modified"}} +{"error": {"class": "GenericError", "desc": "Cannot remove persistent bitmap 'bitmap0': no write access to node 'base'"}} Remove persistent bitmap from base node reopened to RW: {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "base"}} {"return": {}} +Adding a persistent bitmap to the r-o base node, should fail: +{"execute": "block-dirty-bitmap-add", "arguments": {"name": "bitmap1", "node": "base", "persistent": true}} +{"error": {"class": "GenericError", "desc": "Cannot add a persistent bitmap to read-only or inactive node 'base'"}} +Same add inside a transaction, preceded by an otherwise valid +action: the whole transaction must fail and roll back the +already-succeeded first action too: +{"execute": "transaction", "arguments": {"actions": [{"data": {"name": "bitmap2", "node": "top", "persistent": true}, "type": "block-dirty-bitmap-add"}, {"data": {"name": "bitmap1", "node": "base", "persistent": true}, "type": "block-dirty-bitmap-add"}]}} +{"error": {"class": "GenericError", "desc": "Cannot add a persistent bitmap to read-only or inactive node 'base'"}} +bitmap2 on the rw top node must not have survived the rollback: +{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap2", "node": "top"}} +{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap2' not found"}} +Marking the rw top node inactive: +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "top"}} +{"return": {}} +Adding a persistent bitmap to a rw but inactive node, should fail: +{"execute": "block-dirty-bitmap-add", "arguments": {"name": "bitmap3", "node": "top", "persistent": true}} +{"error": {"class": "GenericError", "desc": "Cannot add a persistent bitmap to read-only or inactive node 'top'"}} +Reactivating the top node: +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "top"}} +{"return": {}} diff --git a/tests/qtest/adc128d818-test.c b/tests/qtest/adc128d818-test.c new file mode 100644 index 0000000000..91eda5ca74 --- /dev/null +++ b/tests/qtest/adc128d818-test.c @@ -0,0 +1,856 @@ +/* + * QTest testcase for the ADC128D818 ADC + * + * Copyright (c) 2026 Meta Platforms, Inc. and affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "libqos/i2c.h" +#include "libqos/qgraph.h" +#include "libqtest-single.h" +#include "qobject/qdict.h" + +#define ADC128D818_TEST_ID "adc128d818-test" +#define ADC128D818_TEST_ADDR 0x1f + +/* Register addresses */ +#define REG_CONFIG 0x00 +#define REG_INT_STATUS 0x01 +#define REG_INT_MASK 0x03 +#define REG_CONV_RATE 0x07 +#define REG_CH_DISABLE 0x08 +#define REG_ONE_SHOT 0x09 +#define REG_DEEP_SHUTDOWN 0x0a +#define REG_ADV_CONFIG 0x0b +#define REG_BUSY_STATUS 0x0c + +/* Channel Reading Registers (16-bit, read-only) */ +#define REG_CH_READING_BASE 0x20 + +/* Limit Registers (8-bit, read/write) */ +#define REG_LIMIT_BASE 0x2a + +/* ID Registers (read-only) */ +#define REG_MANUFACTURER_ID 0x3e +#define REG_REVISION_ID 0x3f + +/* Configuration Register (0x00) bitfields */ +#define CONFIG_START BIT(0) +#define CONFIG_INT_ENABLE BIT(1) +#define CONFIG_INT_CLEAR BIT(3) +#define CONFIG_INITIALIZATION BIT(7) + +/* Advanced Configuration Register (0x0b) bitfields */ +#define ADV_CONFIG_EXT_REF_EN BIT(0) +#define ADV_CONFIG_MODE_1 (1 << 1) +#define ADV_CONFIG_MODE_2 (2 << 1) +#define ADV_CONFIG_MODE_3 (3 << 1) + +/* Number of channels */ +#define NUM_CHANNELS 8 + +/* Internal VREF in mV */ +#define INTERNAL_VREF_MV 2560 + +/* QMP helpers for setting device properties */ + +static void qmp_adc128d818_set(const char *property, int value) +{ + QDict *resp; + + resp = qmp("{ 'execute': 'qom-set', 'arguments':" + " { 'path': %s, 'property': %s, 'value': %d } }", + ADC128D818_TEST_ID, property, value); + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); +} + +static int qmp_adc128d818_get(const char *property) +{ + QDict *resp; + int ret; + + resp = qmp("{ 'execute': 'qom-get', 'arguments':" + " { 'path': %s, 'property': %s } }", + ADC128D818_TEST_ID, property); + g_assert(qdict_haskey(resp, "return")); + ret = qdict_get_int(resp, "return"); + qobject_unref(resp); + return ret; +} + +/* Manufacturer and Revision ID registers */ +static void test_id_registers(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + g_assert_cmphex(i2c_get8(dev, REG_MANUFACTURER_ID), ==, 0x01); + g_assert_cmphex(i2c_get8(dev, REG_REVISION_ID), ==, 0x09); +} + +/* Power-on-reset default values */ +static void test_defaults(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + unsigned ch; + + g_assert_cmphex(i2c_get8(dev, REG_CONFIG), ==, 0x08); + g_assert_cmphex(i2c_get8(dev, REG_INT_STATUS), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, REG_INT_MASK), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, REG_CONV_RATE), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, REG_CH_DISABLE), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, REG_DEEP_SHUTDOWN), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, REG_ADV_CONFIG), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, REG_BUSY_STATUS), ==, 0x02); + + for (ch = 0u; ch < NUM_CHANNELS; ch++) { + g_assert_cmphex(i2c_get8(dev, REG_LIMIT_BASE + ch * 2u), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, REG_LIMIT_BASE + ch * 2u + 1u), ==, 0x00); + } +} + +/* Software reset via INITIALIZATION bit */ +static void test_soft_reset(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + i2c_set8(dev, REG_INT_MASK, 0xAA); + i2c_set8(dev, REG_CH_DISABLE, 0x55); + i2c_set8(dev, REG_LIMIT_BASE, 0x42); + + g_assert_cmphex(i2c_get8(dev, REG_INT_MASK), ==, 0xAA); + g_assert_cmphex(i2c_get8(dev, REG_CH_DISABLE), ==, 0x55); + g_assert_cmphex(i2c_get8(dev, REG_LIMIT_BASE), ==, 0x42); + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + + g_assert_cmphex(i2c_get8(dev, REG_CONFIG), ==, 0x08); + g_assert_cmphex(i2c_get8(dev, REG_INT_MASK), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, REG_CH_DISABLE), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, REG_LIMIT_BASE), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, REG_BUSY_STATUS), ==, 0x02); +} + +/* Verify ain property readback via QMP */ +static void test_ain_property(void *obj, void *data, QGuestAllocator *alloc) +{ + int value; + + qmp_adc128d818_set("ain3", 1500); + value = qmp_adc128d818_get("ain3"); + g_test_message("Set ain3 = 1500 mV, readback = %d mV", value); + g_assert_cmpint(value, ==, 1500); + + qmp_adc128d818_set("temperature", 37500); + value = qmp_adc128d818_get("temperature"); + g_test_message("Set temperature = 37500 mC, readback = %d mC", value); + g_assert_cmpint(value, ==, 37500); +} + +/* Voltage conversion */ +static void test_voltage_conversion(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + qmp_adc128d818_set("ain0", 1280); + g_test_message("Injected ain0 = 1280 mV"); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("Read ch0: raw 0x%04x -> %u mV", reading, + (reading >> 4u) * INTERNAL_VREF_MV / 4096u); + g_assert_cmphex(reading, ==, 0x8000); + + qmp_adc128d818_set("ain1", 2560); + g_test_message("Injected ain1 = 2560 mV"); + reading = i2c_get16(dev, REG_CH_READING_BASE + 1u); + g_test_message("Read ch1: raw 0x%04x -> %u mV", reading, + (reading >> 4u) * INTERNAL_VREF_MV / 4096u); + g_assert_cmphex(reading, ==, 0xFFF0); + + qmp_adc128d818_set("ain2", 0); + g_test_message("Injected ain2 = 0 mV"); + reading = i2c_get16(dev, REG_CH_READING_BASE + 2u); + g_test_message("Read ch2: raw 0x%04x -> %u mV", reading, + (reading >> 4u) * INTERNAL_VREF_MV / 4096u); + g_assert_cmphex(reading, ==, 0x0000); +} + +/* Temperature conversion (mode 0, channel 7 = temperature) */ +static void +test_temperature_conversion(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + qmp_adc128d818_set("temperature", 25000); + g_test_message("Injected temperature = 25000 mC (25.0 deg C)"); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + reading = i2c_get16(dev, REG_CH_READING_BASE + 7u); + g_test_message("Read ch7: raw 0x%04x -> %d mC", reading, + (int16_t)(reading & 0xFF80u) * 500 / 128); + g_assert_cmphex(reading, ==, 0x1900); +} + +/* Channels with distinct voltages */ +static void test_all_channels(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + static const uint16_t ain_mv[NUM_CHANNELS] = { + 0, 320, 640, 960, 1280, 1920, 2240, 2560 + }; + static const uint16_t expect[NUM_CHANNELS] = { + 0x0000, 0x2000, 0x4000, 0x6000, 0x8000, 0xC000, 0xE000, 0xFFF0 + }; + uint16_t reading; + unsigned ch; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + i2c_set8(dev, REG_ADV_CONFIG, ADV_CONFIG_MODE_1); + + for (ch = 0u; ch < NUM_CHANNELS; ch++) { + char name[8]; + snprintf(name, sizeof(name), "ain%u", ch); + qmp_adc128d818_set(name, ain_mv[ch]); + } + + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + for (ch = 0u; ch < NUM_CHANNELS; ch++) { + reading = i2c_get16(dev, REG_CH_READING_BASE + ch); + g_test_message("ch%u: ain %u mV -> raw 0x%04x (expect 0x%04x)", + ch, ain_mv[ch], reading, expect[ch]); + g_assert_cmphex(reading, ==, expect[ch]); + } +} + +/* Voltage conversion edge cases */ +static void test_voltage_edges(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + i2c_set8(dev, REG_ADV_CONFIG, ADV_CONFIG_MODE_1); + + qmp_adc128d818_set("ain0", 3000); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("Over-range 3000 mV: raw 0x%04x (expect 0xFFF0)", reading); + g_assert_cmphex(reading, ==, 0xFFF0); + + qmp_adc128d818_set("ain1", 1); + reading = i2c_get16(dev, REG_CH_READING_BASE + 1u); + g_test_message("1 mV: raw 0x%04x (expect 0x0010)", reading); + g_assert_cmphex(reading, ==, 0x0010); + + qmp_adc128d818_set("ain2", 640); + reading = i2c_get16(dev, REG_CH_READING_BASE + 2u); + g_test_message("640 mV (quarter): raw 0x%04x (expect 0x4000)", reading); + g_assert_cmphex(reading, ==, 0x4000); + + qmp_adc128d818_set("ain3", 1920); + reading = i2c_get16(dev, REG_CH_READING_BASE + 3u); + g_test_message("1920 mV (3/4): raw 0x%04x (expect 0xC000)", reading); + g_assert_cmphex(reading, ==, 0xC000); +} + +/* Temperature conversion edge cases */ +static void test_temperature_edges(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + + qmp_adc128d818_set("temperature", 0); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + reading = i2c_get16(dev, REG_CH_READING_BASE + 7u); + g_test_message("0 C: raw 0x%04x (expect 0x0000)", reading); + g_assert_cmphex(reading, ==, 0x0000); + + qmp_adc128d818_set("temperature", -25000); + reading = i2c_get16(dev, REG_CH_READING_BASE + 7u); + g_test_message("-25 C: raw 0x%04x (expect 0xE700)", reading); + g_assert_cmphex(reading, ==, 0xE700); + + qmp_adc128d818_set("temperature", 127500); + reading = i2c_get16(dev, REG_CH_READING_BASE + 7u); + g_test_message("+127.5 C: raw 0x%04x (expect 0x7F80)", reading); + g_assert_cmphex(reading, ==, 0x7F80); + + qmp_adc128d818_set("temperature", -128000); + reading = i2c_get16(dev, REG_CH_READING_BASE + 7u); + g_test_message("-128 C: raw 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); + + qmp_adc128d818_set("temperature", 200000); + reading = i2c_get16(dev, REG_CH_READING_BASE + 7u); + g_test_message("200 C (clamped): raw 0x%04x (expect 0x7F80)", reading); + g_assert_cmphex(reading, ==, 0x7F80); + + qmp_adc128d818_set("temperature", -200000); + reading = i2c_get16(dev, REG_CH_READING_BASE + 7u); + g_test_message("-200 C (clamped): raw 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); +} + +/* External voltage reference */ +static void test_ext_vref(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + i2c_set8(dev, REG_ADV_CONFIG, ADV_CONFIG_MODE_1); + + qmp_adc128d818_set("ext-vref-mv", 4096); + i2c_set8(dev, REG_ADV_CONFIG, ADV_CONFIG_EXT_REF_EN | ADV_CONFIG_MODE_1); + + qmp_adc128d818_set("ain0", 1000); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("1000 mV / 4096 mV VREF: raw 0x%04x (expect 0x3E80)", + reading); + g_assert_cmphex(reading, ==, 0x3E80); + + qmp_adc128d818_set("ain1", 2048); + reading = i2c_get16(dev, REG_CH_READING_BASE + 1u); + g_test_message("2048 mV / 4096 mV VREF: raw 0x%04x (expect 0x8000)", + reading); + g_assert_cmphex(reading, ==, 0x8000); +} + +/* Interrupt status set on limit violation; persists while fault remains */ +static void test_interrupt_status(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint8_t status; + + i2c_set8(dev, REG_LIMIT_BASE, 0x10); + g_test_message("Set ch0 high limit = 0x10"); + + qmp_adc128d818_set("ain0", 2560); + g_test_message("Injected ain0 = 2560 mV (exceeds limit)"); + + i2c_set8(dev, REG_CONFIG, CONFIG_START | CONFIG_INT_ENABLE); + + status = i2c_get8(dev, REG_INT_STATUS); + g_test_message("INT_STATUS = 0x%02x (expect bit 0 set)", status); + g_assert_cmphex(status & 0x01u, ==, 0x01); + + status = i2c_get8(dev, REG_INT_STATUS); + g_test_message("INT_STATUS after re-read = 0x%02x (expect bit 0 still set)", + status); + g_assert_cmphex(status & 0x01u, ==, 0x01); + + qmp_adc128d818_set("ain0", 80); + g_test_message("Injected ain0 = 80 mV (within limit)"); + status = i2c_get8(dev, REG_INT_STATUS); + g_test_message("INT_STATUS after fault cleared = 0x%02x " + "(expect bit 0 clear)", status); + g_assert_cmphex(status & 0x01u, ==, 0x00); +} + +/* INT_CLEAR stops the round-robin monitoring loop */ +static void test_int_clear(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint8_t status; + + i2c_set8(dev, REG_LIMIT_BASE, 0x10); + qmp_adc128d818_set("ain0", 2560); + + i2c_set8(dev, REG_CONFIG, + CONFIG_START | CONFIG_INT_ENABLE | CONFIG_INT_CLEAR); + status = i2c_get8(dev, REG_INT_STATUS); + g_test_message("INT_STATUS with INT_CLEAR set = 0x%02x (expect 0x00)", + status); + g_assert_cmphex(status, ==, 0x00); + + i2c_set8(dev, REG_CONFIG, CONFIG_START | CONFIG_INT_ENABLE); + status = i2c_get8(dev, REG_INT_STATUS); + g_test_message("INT_STATUS after INT_CLEAR cleared = 0x%02x (expect bit 0)", + status); + g_assert_cmphex(status & 0x01u, ==, 0x01); +} + +/* Low-limit interrupt triggers correctly */ +static void test_low_limit(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint8_t status; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + + i2c_set8(dev, REG_LIMIT_BASE + 3u, 0x80); + g_test_message("Set ch1 low limit = 0x80"); + + i2c_set8(dev, REG_LIMIT_BASE + 5u, 0x80); + g_test_message("Set ch2 low limit = 0x80"); + + qmp_adc128d818_set("ain1", 640); + qmp_adc128d818_set("ain2", 1280); + qmp_adc128d818_set("ain0", 1280); + i2c_set8(dev, REG_CONFIG, CONFIG_START | CONFIG_INT_ENABLE); + + status = i2c_get8(dev, REG_INT_STATUS); + g_test_message("INT_STATUS = 0x%02x (expect bits 1 and 2 set)", status); + g_assert_cmphex(status & 0x02u, ==, 0x02); + g_assert_cmphex(status & 0x04u, ==, 0x04); + + g_assert_cmphex(status & 0x01u, ==, 0x00); +} + +/* Temperature high-limit interrupt with hysteresis */ +static void test_temp_hysteresis(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint8_t status; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + + i2c_set8(dev, REG_LIMIT_BASE + 7u * 2u, 0x32); + i2c_set8(dev, REG_LIMIT_BASE + 7u * 2u + 1u, 0x28); + + qmp_adc128d818_set("temperature", 25000); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + status = i2c_get8(dev, REG_INT_STATUS); + g_test_message("25 C: INT_STATUS = 0x%02x (temp bit expect clear)", status); + g_assert_cmphex(status & 0x80u, ==, 0x00); + + qmp_adc128d818_set("temperature", 55000); + status = i2c_get8(dev, REG_INT_STATUS); + g_test_message("55 C: INT_STATUS = 0x%02x (temp bit expect set)", status); + g_assert_cmphex(status & 0x80u, ==, 0x80); + + qmp_adc128d818_set("temperature", 45000); + status = i2c_get8(dev, REG_INT_STATUS); + g_test_message("45 C (hysteresis): INT_STATUS = 0x%02x " + "(temp bit expect set)", status); + g_assert_cmphex(status & 0x80u, ==, 0x80); + + qmp_adc128d818_set("temperature", 35000); + status = i2c_get8(dev, REG_INT_STATUS); + g_test_message("35 C: INT_STATUS = 0x%02x (temp bit expect clear)", status); + g_assert_cmphex(status & 0x80u, ==, 0x00); +} + +/* Channel disable prevents conversion */ +static void test_channel_disable(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + i2c_set8(dev, REG_CH_DISABLE, 0x01); + g_test_message("Disabled channel 0"); + + qmp_adc128d818_set("ain0", 1280); + g_test_message("Injected ain0 = 1280 mV (disabled)"); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("Read ch0 (disabled): raw 0x%04x", reading); + g_assert_cmphex(reading, ==, 0x0000); + + qmp_adc128d818_set("ain1", 1280); + g_test_message("Injected ain1 = 1280 mV (enabled)"); + reading = i2c_get16(dev, REG_CH_READING_BASE + 1u); + g_test_message("Read ch1 (enabled): raw 0x%04x -> %u mV", reading, + (reading >> 4u) * INTERNAL_VREF_MV / 4096u); + g_assert_cmphex(reading, ==, 0x8000); +} + +/* One-shot conversion in shutdown mode */ +static void test_one_shot(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + qmp_adc128d818_set("ain0", 1280); + g_test_message("Injected ain0 = 1280 mV (device stopped)"); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("Read ch0 before one-shot: raw 0x%04x", reading); + g_assert_cmphex(reading, ==, 0x0000); + + g_assert_cmphex(i2c_get8(dev, REG_ONE_SHOT), ==, 0x00); + + i2c_set8(dev, REG_ONE_SHOT, 0x00); + g_test_message("Triggered one-shot conversion with value 0x00"); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("Read ch0 after one-shot: raw 0x%04x -> %u mV", reading, + (reading >> 4u) * INTERNAL_VREF_MV / 4096u); + g_assert_cmphex(reading, ==, 0x8000); +} + +/* Mode 1 makes channel 7 a voltage input instead of temperature */ +static void test_mode_selection(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + i2c_set8(dev, REG_ADV_CONFIG, ADV_CONFIG_MODE_1); + g_test_message("Set mode 1 (all voltage channels)"); + + qmp_adc128d818_set("ain7", 1280); + qmp_adc128d818_set("temperature", 50000); + g_test_message("Injected ain7 = 1280 mV, temperature = 50000 mC"); + + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + reading = i2c_get16(dev, REG_CH_READING_BASE + 7u); + g_test_message("Read ch7 (mode 1): raw 0x%04x -> %u mV", reading, + (reading >> 4u) * INTERNAL_VREF_MV / 4096u); + g_assert_cmphex(reading, ==, 0x8000); +} + +/* Mode 2 - 4 pseudo-differential pairs */ +static void test_mode2_diff(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + i2c_set8(dev, REG_ADV_CONFIG, ADV_CONFIG_MODE_2); + g_test_message("Set mode 2 (4 pseudo-differential pairs)"); + + qmp_adc128d818_set("ain0", 2000); + qmp_adc128d818_set("ain1", 720); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("Pair 0 (IN0-IN1): raw 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); + + qmp_adc128d818_set("ain3", 1920); + qmp_adc128d818_set("ain2", 640); + + reading = i2c_get16(dev, REG_CH_READING_BASE + 1u); + g_test_message("Pair 1 (IN3-IN2): raw 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); + + qmp_adc128d818_set("ain4", 1500); + qmp_adc128d818_set("ain5", 220); + + reading = i2c_get16(dev, REG_CH_READING_BASE + 2u); + g_test_message("Pair 2 (IN4-IN5): raw 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); + + qmp_adc128d818_set("ain7", 2560); + qmp_adc128d818_set("ain6", 1280); + + reading = i2c_get16(dev, REG_CH_READING_BASE + 3u); + g_test_message("Pair 3 (IN7-IN6): raw 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); + + reading = i2c_get16(dev, REG_CH_READING_BASE + 4u); + g_test_message("Reserved ch4: raw 0x%04x (expect 0x0000)", reading); + g_assert_cmphex(reading, ==, 0x0000); + + qmp_adc128d818_set("ain0", 500); + qmp_adc128d818_set("ain1", 1000); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("Pair 0 negative dV: raw 0x%04x (expect 0x0000)", reading); + g_assert_cmphex(reading, ==, 0x0000); +} + +/* Mode 3 - 4 single-ended + 2 pseudo-differential pairs */ +static void test_mode3_mixed(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + i2c_set8(dev, REG_ADV_CONFIG, ADV_CONFIG_MODE_3); + g_test_message("Set mode 3 (4 single-ended + 2 differential)"); + + qmp_adc128d818_set("ain0", 1280); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("Ch0 single-ended: raw 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); + + qmp_adc128d818_set("ain4", 1500); + qmp_adc128d818_set("ain5", 220); + + reading = i2c_get16(dev, REG_CH_READING_BASE + 4u); + g_test_message("Ch4 diff (IN4-IN5): raw 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); + + qmp_adc128d818_set("ain7", 2560); + qmp_adc128d818_set("ain6", 1280); + + reading = i2c_get16(dev, REG_CH_READING_BASE + 5u); + g_test_message("Ch5 diff (IN7-IN6): raw 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); + + reading = i2c_get16(dev, REG_CH_READING_BASE + 6u); + g_test_message("Reserved ch6: raw 0x%04x (expect 0x0000)", reading); + g_assert_cmphex(reading, ==, 0x0000); + + qmp_adc128d818_set("temperature", 25000); + + reading = i2c_get16(dev, REG_CH_READING_BASE + 7u); + g_test_message("Ch7 temperature: raw 0x%04x (expect 0x1900)", reading); + g_assert_cmphex(reading, ==, 0x1900); +} + +/* Mode change resets channel readings and interrupt status */ +static void test_mode_change_reset(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + uint8_t status; + + qmp_adc128d818_set("ain0", 1280); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("Before mode change, ch0: raw 0x%04x", reading); + g_assert_cmphex(reading, !=, 0x0000); + + i2c_set8(dev, REG_CONFIG, 0x00); + i2c_set8(dev, REG_LIMIT_BASE, 0x10); + qmp_adc128d818_set("ain0", 2560); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + i2c_set8(dev, REG_CONFIG, 0x00); + i2c_set8(dev, REG_ADV_CONFIG, ADV_CONFIG_MODE_2); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("After mode change, ch0: raw 0x%04x (expect 0x0000)", + reading); + g_assert_cmphex(reading, ==, 0x0000); + + status = i2c_get8(dev, REG_INT_STATUS); + g_test_message("After mode change, INT_STATUS: 0x%02x (expect 0x00)", + status); + g_assert_cmphex(status, ==, 0x00); + + g_assert_cmphex(i2c_get8(dev, REG_LIMIT_BASE), ==, 0x10); + g_test_message("Limit register preserved after mode change"); +} + +/* QOM property changes trigger correct differential conversion */ +static void test_diff_qom_trigger(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + i2c_set8(dev, REG_ADV_CONFIG, ADV_CONFIG_MODE_2); + + qmp_adc128d818_set("ain0", 0); + qmp_adc128d818_set("ain1", 0); + qmp_adc128d818_set("ain2", 0); + qmp_adc128d818_set("ain3", 0); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + qmp_adc128d818_set("ain0", 2000); + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("After ain0=2000, ain1=0: pair0 = 0x%04x (expect 0xC800)", + reading); + g_assert_cmphex(reading, ==, 0xC800); + + qmp_adc128d818_set("ain1", 720); + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("After ain1=720: pair0 = 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); + + qmp_adc128d818_set("ain3", 1920); + qmp_adc128d818_set("ain2", 640); + reading = i2c_get16(dev, REG_CH_READING_BASE + 1u); + g_test_message("Pair 1 (IN3-IN2) via QOM: 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); +} + +/* One-shot conversion works in deep shutdown */ +static void test_deep_shutdown(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + + qmp_adc128d818_set("ain0", 1280); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_assert_cmphex(reading, ==, 0x8000); + + i2c_set8(dev, REG_DEEP_SHUTDOWN, 0x01); + g_test_message("DEEP_SHUTDOWN write while running rejected"); + g_assert_cmphex(i2c_get8(dev, REG_DEEP_SHUTDOWN), ==, 0x00); + + i2c_set8(dev, REG_CONFIG, 0x00); + i2c_set8(dev, REG_DEEP_SHUTDOWN, 0x01); + qmp_adc128d818_set("ain0", 0); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("Deep shutdown, no one-shot: raw 0x%04x (expect 0x8000)", + reading); + g_assert_cmphex(reading, ==, 0x8000); + + i2c_set8(dev, REG_ONE_SHOT, 0x01); + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("Deep shutdown one-shot: raw 0x%04x (expect 0x0000)", + reading); + g_assert_cmphex(reading, ==, 0x0000); + + g_assert_cmphex(i2c_get8(dev, REG_DEEP_SHUTDOWN), ==, 0x01); + + i2c_set8(dev, REG_DEEP_SHUTDOWN, 0x00); + qmp_adc128d818_set("ain0", 1280); + i2c_set8(dev, REG_ONE_SHOT, 0x01); + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("After exit shutdown: raw 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); +} + +/* BUSY_STATUS NOT_READY clears after first conversion */ +static void test_busy_status(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + + g_assert_cmphex(i2c_get8(dev, REG_BUSY_STATUS) & 0x02, ==, 0x02); + g_test_message("After reset: BUSY_STATUS = 0x%02x (NOT_READY set)", + i2c_get8(dev, REG_BUSY_STATUS)); + + qmp_adc128d818_set("ain0", 0); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + g_assert_cmphex(i2c_get8(dev, REG_BUSY_STATUS) & 0x02, ==, 0x00); + g_test_message("After conversion: BUSY_STATUS = 0x%02x (NOT_READY cleared)", + i2c_get8(dev, REG_BUSY_STATUS)); +} + +/* Programming Channel Disable resets channel readings */ +static void test_chan_disable_clears(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + + qmp_adc128d818_set("ain0", 1280); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + g_assert_cmphex(i2c_get16(dev, REG_CH_READING_BASE), ==, 0x8000); + + i2c_set8(dev, REG_CONFIG, 0x00); + i2c_set8(dev, REG_CH_DISABLE, 0x02); + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("After CH_DISABLE write: ch0 raw 0x%04x (expect 0x0000)", + reading); + g_assert_cmphex(reading, ==, 0x0000); +} + +/* Programming Advanced Configuration always resets channel readings */ +static void test_adv_config_clears(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + + qmp_adc128d818_set("ain0", 1280); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + g_assert_cmphex(i2c_get16(dev, REG_CH_READING_BASE), ==, 0x8000); + + i2c_set8(dev, REG_CONFIG, 0x00); + i2c_set8(dev, REG_ADV_CONFIG, 0x00); + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("After same-mode ADV_CONFIG: ch0 0x%04x (expect 0x0000)", + reading); + g_assert_cmphex(reading, ==, 0x0000); + + i2c_set8(dev, REG_CONFIG, CONFIG_START); + g_assert_cmphex(i2c_get16(dev, REG_CH_READING_BASE), ==, 0x8000); + i2c_set8(dev, REG_CONFIG, 0x00); + qmp_adc128d818_set("ext-vref-mv", 4096); + i2c_set8(dev, REG_ADV_CONFIG, ADV_CONFIG_EXT_REF_EN); + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("After ext-vref toggle: ch0 0x%04x (expect 0x0000)", + reading); + g_assert_cmphex(reading, ==, 0x0000); +} + +/* Conversion Rate register may only be programmed while in shutdown */ +static void test_conv_rate(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + + i2c_set8(dev, REG_CONV_RATE, 0x01); + g_assert_cmphex(i2c_get8(dev, REG_CONV_RATE), ==, 0x01); + + i2c_set8(dev, REG_CONFIG, CONFIG_START); + i2c_set8(dev, REG_CONV_RATE, 0x00); + g_test_message("CONV_RATE while running: 0x%02x (expect unchanged 0x01)", + i2c_get8(dev, REG_CONV_RATE)); + g_assert_cmphex(i2c_get8(dev, REG_CONV_RATE), ==, 0x01); +} + +static void adc128d818_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "id=" ADC128D818_TEST_ID + ",address=0x1f" + }; + add_qi2c_address(&opts, &(QI2CAddress) { ADC128D818_TEST_ADDR }); + + qos_node_create_driver("adc128d818", i2c_device_create); + qos_node_consumes("adc128d818", "i2c-bus", &opts); + + qos_add_test("id-registers", "adc128d818", test_id_registers, NULL); + qos_add_test("defaults", "adc128d818", test_defaults, NULL); + qos_add_test("soft-reset", "adc128d818", test_soft_reset, NULL); + qos_add_test("ain-property", "adc128d818", test_ain_property, NULL); + qos_add_test("voltage-conversion", "adc128d818", test_voltage_conversion, + NULL); + qos_add_test("temperature-conversion", "adc128d818", + test_temperature_conversion, NULL); + qos_add_test("all-channels", "adc128d818", test_all_channels, NULL); + qos_add_test("voltage-edges", "adc128d818", test_voltage_edges, NULL); + qos_add_test("temperature-edges", "adc128d818", test_temperature_edges, + NULL); + qos_add_test("ext-vref", "adc128d818", test_ext_vref, NULL); + qos_add_test("interrupt-status", "adc128d818", test_interrupt_status, NULL); + qos_add_test("int-clear", "adc128d818", test_int_clear, NULL); + qos_add_test("low-limit", "adc128d818", test_low_limit, NULL); + qos_add_test("temp-hysteresis", "adc128d818", test_temp_hysteresis, NULL); + qos_add_test("channel-disable", "adc128d818", test_channel_disable, NULL); + qos_add_test("one-shot", "adc128d818", test_one_shot, NULL); + qos_add_test("mode-selection", "adc128d818", test_mode_selection, NULL); + qos_add_test("mode2-diff", "adc128d818", test_mode2_diff, NULL); + qos_add_test("mode3-mixed", "adc128d818", test_mode3_mixed, NULL); + qos_add_test("mode-change-reset", "adc128d818", test_mode_change_reset, + NULL); + qos_add_test("diff-qom-trigger", "adc128d818", test_diff_qom_trigger, NULL); + qos_add_test("deep-shutdown", "adc128d818", test_deep_shutdown, NULL); + qos_add_test("busy-status", "adc128d818", test_busy_status, NULL); + qos_add_test("chan-disable-clears", "adc128d818", test_chan_disable_clears, + NULL); + qos_add_test("adv-config-clears", "adc128d818", test_adv_config_clears, + NULL); + qos_add_test("conv-rate", "adc128d818", test_conv_rate, NULL); +} +libqos_init(adc128d818_register_nodes); diff --git a/tests/qtest/ahci-test.c b/tests/qtest/ahci-test.c index 44799eea15..b143862ce7 100644 --- a/tests/qtest/ahci-test.c +++ b/tests/qtest/ahci-test.c @@ -905,6 +905,30 @@ static void ahci_test_flush(AHCIQState *ahci) ahci_test_nondata(ahci, CMD_FLUSH_CACHE); } +static void ahci_test_specify(AHCIQState *ahci, uint16_t sectors, + bool supported) +{ + AHCICommand *cmd; + uint8_t port; + + port = ahci_port_select(ahci); + ahci_port_clear(ahci, port); + + cmd = ahci_command_create(CMD_INIT_DP); + ahci_command_set_count(cmd, sectors); + if (!supported) { + ahci_command_expect_error(cmd, ATA_ERR_ABRT); + } + ahci_command_commit(ahci, cmd, port); + ahci_command_issue(ahci, cmd); + if (!supported) { + ASSERT_BIT_SET(ahci_px_rreg(ahci, port, AHCI_PX_TFD), + AHCI_PX_TFD_STS_ERR); + } + ahci_command_verify(ahci, cmd); + ahci_command_free(cmd); +} + static void ahci_test_max(AHCIQState *ahci) { RegD2HFIS *d2h = g_malloc0(0x20); @@ -1012,6 +1036,21 @@ static void test_identify(void) ahci_shutdown(ahci); } +static void test_specify(void) +{ + AHCIQState *ahci; + + ahci = ahci_boot_and_enable(NULL); + + /* A register FIS carries 16 bits of count, the legacy ports only eight */ + ahci_test_specify(ahci, 0, false); + ahci_test_specify(ahci, 256, false); + ahci_test_specify(ahci, 0xffff, false); + ahci_test_specify(ahci, 32, true); + + ahci_shutdown(ahci); +} + /** * Fragmented DMA test: Perform a standard 4K DMA read/write * test, but make sure the physical regions are fragmented to @@ -1565,6 +1604,31 @@ static int ahci_cb_cmp_buff(AHCIQState *ahci, AHCICommand *cmd, return 0; } +static int ahci_cb_cmp_raw(AHCIQState *ahci, AHCICommand *cmd, + const AHCIOpts *opts) +{ + unsigned char *tx = opts->opaque; + unsigned char *rx; + unsigned i, nsectors; + + if (!opts->size) { + return 0; + } + + nsectors = opts->size / ATAPI_RAW_SECTOR_SIZE; + rx = g_malloc0(opts->size); + qtest_bufread(ahci->parent->qts, opts->buffer, rx, opts->size); + /* Each raw sector carries its 2048-byte payload past a 16-byte header. */ + for (i = 0; i < nsectors; i++) { + g_assert_cmphex(memcmp(rx + i * ATAPI_RAW_SECTOR_SIZE + 16, + tx + i * ATAPI_SECTOR_SIZE, + ATAPI_SECTOR_SIZE), ==, 0); + } + g_free(rx); + + return 0; +} + static void ahci_test_cdrom(int nsectors, bool dma, uint8_t cmd, bool override_bcl, uint16_t bcl) { @@ -1625,6 +1689,47 @@ static void test_cdrom_pio_multi(void) ahci_test_cdrom_read10(3, false); } +static void ahci_test_cdrom_raw(int nsectors, bool dma) +{ + AHCIQState *ahci; + unsigned char *tx; + char *iso; + int fd; + AHCIOpts opts = { + .size = (uint64_t)ATAPI_RAW_SECTOR_SIZE * nsectors, + .atapi = true, + .atapi_dma = dma, + .atapi_raw = true, + .set_bcl = true, + .bcl = ATAPI_RAW_SECTOR_SIZE, /* one raw sector per DRQ burst */ + .post_cb = ahci_cb_cmp_raw, + }; + uint64_t iso_size = (uint64_t)ATAPI_SECTOR_SIZE * (nsectors + 1); + + fd = prepare_iso(iso_size, &tx, &iso); + opts.opaque = tx; + + ahci = ahci_boot_and_enable("-drive if=none,id=drive0,file=%s,format=raw " + "-M q35 " + "-device ide-cd,drive=drive0 ", iso); + + ahci_exec(ahci, ahci_port_select(ahci), CMD_ATAPI_READ_CD, &opts); + + g_free(tx); + ahci_shutdown(ahci); + remove_iso(fd, iso); +} + +static void test_cdrom_dma_raw(void) +{ + ahci_test_cdrom_raw(3, true); +} + +static void test_cdrom_pio_raw(void) +{ + ahci_test_cdrom_raw(3, false); +} + /* * Regression test: a buffered ATAPI read completing after a command * engine restart must not dereference the cleared cur_cmd. Cover both @@ -1688,6 +1793,212 @@ static void test_atapi_engine_restart_dma(void) test_atapi_engine_restart_in_flight(true); } +/* + * Regression test: an unplug runs no device reset, so it is the last chance to + * detach an outstanding request. Its completion would otherwise walk the + * AHCIDevice array that ahci_uninit() has freed, which each of the NCQ, DMA + * and PIO completions reaches by a different route. + * + * The ACPI ejection register is what a guest writes to finish a PCI unplug. + * -M pc is what puts it in reach: q35 has no ACPI hotplug on pcie.0, so the + * unplug never happens there. Unlike the pciehp attention button this reaches + * the unplug with no secondary bus reset, which is the ordering that leaves a + * request outstanding. + */ +static void test_unplug_in_flight(uint8_t ide_cmd) +{ + AHCIQState *ahci; + AHCICommand *cmd; + uint64_t ptr; + uint8_t port; + QTestState *qts; + + /* + * The latency keeps the backend read in flight across the unplug. A + * blkdebug breakpoint cannot stand in for it: cancelling a suspended + * request waits for it, so the unplug would never return. + */ + ahci = ahci_boot_and_enable( + "-M pc " + "-blockdev driver=null-co,node-name=drive0,read-zeroes=on," + "latency-ns=100000000 " + "-device ich9-ahci,addr=1f.2,id=ahci0 " + "-device ide-hd,drive=drive0,bus=ahci0.0 "); + qts = ahci->parent->qts; + port = ahci_port_select(ahci); + ahci_port_clear(ahci, port); + + ptr = ahci_alloc(ahci, AHCI_SECTOR_SIZE); + g_assert(ptr); + + cmd = ahci_command_create(ide_cmd); + ahci_command_adjust(cmd, 0, ptr, AHCI_SECTOR_SIZE, 0); + ahci_command_commit(ahci, cmd, port); + ahci_command_issue_async(ahci, cmd); + + /* Eject slot 0x1f of the root bus, which frees the AHCIDevice array. */ + qtest_outl(qts, 0xae10, 0); + qtest_outl(qts, 0xae08, 1u << 0x1f); + qtest_qmp_eventwait(qts, "DEVICE_DELETED"); + + /* Four times the backend latency, so the completion has surely run. */ + g_usleep(400 * 1000); + qtest_qmp_assert_success(qts, "{ 'execute': 'query-status' }"); + + ahci_command_free(cmd); + ahci_free(ahci, ptr); + ahci_shutdown(ahci); +} + +static void test_unplug_ncq(void) +{ + test_unplug_in_flight(READ_FPDMA_QUEUED); +} + +static void test_unplug_dma(void) +{ + test_unplug_in_flight(CMD_READ_DMA); +} + +static void test_unplug_pio(void) +{ + test_unplug_in_flight(CMD_READ_PIO); +} + +/* + * Regression test: a PIO write outlives the command list it was issued from. + * ide_cancel_dma_sync() does not reach s->pio_aiocb, so the second DRQ phase + * runs from the write completion after PxCLB has been unmapped and must not + * touch the command header any more. + */ +static void test_write_engine_stop_in_flight(void) +{ + AHCIQState *ahci; + AHCICommand *cmd; + unsigned char *tx; + unsigned char *rx; + uint64_t ptr; + uint8_t port; + size_t bufsize = AHCI_SECTOR_SIZE * 2; + size_t i; + + ahci = ahci_boot_and_enable("-drive file=blkdebug::%s,if=none,id=drive0," + "format=%s,cache=writeback " + "-M q35 " + "-device ide-hd,drive=drive0 ", + tmp_path, imgfmt); + port = ahci_port_select(ahci); + ahci_port_clear(ahci, port); + + tx = g_malloc(bufsize); + generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); + ptr = ahci_alloc(ahci, bufsize); + g_assert(ptr); + qtest_memwrite(ahci->parent->qts, ptr, tx, bufsize); + + /* Zero the second sector, which the abandoned command must not reach. */ + rx = g_malloc0(AHCI_SECTOR_SIZE); + ahci_io(ahci, port, CMD_WRITE_DMA, rx, AHCI_SECTOR_SIZE, 1); + + /* Suspend the backend write so the first sector stays in flight. */ + g_free(qtest_hmp(ahci->parent->qts, + "qemu-io drive0 \"break write_aio wr\"")); + + cmd = ahci_command_create(CMD_WRITE_PIO); + ahci_command_adjust(cmd, 0, ptr, bufsize, 0); + ahci_command_commit(ahci, cmd, port); + ahci_command_issue_async(ahci, cmd); + + /* Drop the command list while the write is still outstanding. */ + ahci_px_clr(ahci, port, AHCI_PX_CMD, AHCI_PX_CMD_ST); + + g_free(qtest_hmp(ahci->parent->qts, "qemu-io drive0 \"resume wr\"")); + + /* Round-trip through the device to confirm qemu is still alive. */ + ahci_px_rreg(ahci, port, AHCI_PX_TFD); + + /* + * The second DRQ phase never fetched its data, so the sector it would + * have carried has to be untouched rather than hold a copy of the first. + */ + ahci_px_set(ahci, port, AHCI_PX_CMD, AHCI_PX_CMD_ST); + memset(rx, 0xff, AHCI_SECTOR_SIZE); + ahci_io(ahci, port, CMD_READ_DMA, rx, AHCI_SECTOR_SIZE, 1); + for (i = 0; i < AHCI_SECTOR_SIZE; i++) { + g_assert_cmpint(rx[i], ==, 0); + } + + ahci_command_free(cmd); + ahci_free(ahci, ptr); + g_free(rx); + g_free(tx); + ahci_shutdown(ahci); +} + +/* + * Regression test: a multi-sector ATAPI read fetches its later sectors from + * inside the first read's completion; a concurrent drain (as a guest reset + * triggers via bdrv_drain_all_begin) must not wedge on that nested read. + * blkdebug keeps the read in flight across x-blockdev-set-iothread. + */ +static void test_atapi_drain_in_flight(bool dma) +{ + AHCIQState *ahci; + AHCICommand *cmd; + unsigned char *tx; + char *iso; + int fd; + uint8_t port; + uint64_t buffer; + uint16_t bcl = ATAPI_SECTOR_SIZE * 2; + uint64_t iso_size = (uint64_t)ATAPI_SECTOR_SIZE * 3; + + fd = prepare_iso(iso_size, &tx, &iso); + + /* 1s read delay: a wide margin so the drain starts before it completes */ + ahci = ahci_boot_and_enable( + "-blockdev driver=file,node-name=file0,filename=%s,read-only=on " + "-blockdev driver=blkdebug,node-name=cd0,image=file0,read-only=on," + "inject-error.0.event=none,inject-error.0.iotype=read," + "inject-error.0.errno=0,inject-error.0.delay-ns=1000000000 " + "-M q35 " + "-device ide-cd,drive=cd0 ", iso); + port = ahci_port_select(ahci); + + buffer = ahci_alloc(ahci, bcl); + qtest_memset(ahci->parent->qts, buffer, 0x00, bcl); + + cmd = ahci_atapi_command_create(CMD_ATAPI_READ_10, bcl, dma); + ahci_command_adjust(cmd, 0, buffer, bcl, 0); + ahci_command_commit(ahci, cmd, port); + ahci_command_issue_async(ahci, cmd); + + /* Drain (all nodes) while the delayed read is still in flight. */ + qtest_qmp_assert_success(ahci->parent->qts, + "{ 'execute': 'x-blockdev-set-iothread'," + " 'arguments': { 'node-name': 'cd0', 'iothread': null," + " 'force': true } }"); + + /* Round-trip through the device to confirm qemu is still alive. */ + ahci_px_rreg(ahci, port, AHCI_PX_TFD); + + ahci_command_free(cmd); + ahci_free(ahci, buffer); + g_free(tx); + ahci_shutdown(ahci); + remove_iso(fd, iso); +} + +static void test_atapi_drain_pio(void) +{ + test_atapi_drain_in_flight(false); +} + +static void test_atapi_drain_dma(void) +{ + test_atapi_drain_in_flight(true); +} + /* Regression test: Test that a READ_CD command with a BCL of 0 but a size of 0 * completes as a NOP instead of erroring out. */ static void test_atapi_bcl(void) @@ -2090,6 +2401,7 @@ int main(int argc, char **argv) qtest_add_func("/ahci/migrate/dma/halted", test_migrate_halted_dma); qtest_add_func("/ahci/max", test_max); + qtest_add_func("/ahci/specify", test_specify); qtest_add_func("/ahci/reset/simple", test_reset); qtest_add_func("/ahci/reset/pending_callback", test_reset_pending_callback); @@ -2100,8 +2412,10 @@ int main(int argc, char **argv) qtest_add_func("/ahci/cdrom/dma/single", test_cdrom_dma); qtest_add_func("/ahci/cdrom/dma/multi", test_cdrom_dma_multi); + qtest_add_func("/ahci/cdrom/dma/raw", test_cdrom_dma_raw); qtest_add_func("/ahci/cdrom/pio/single", test_cdrom_pio); qtest_add_func("/ahci/cdrom/pio/multi", test_cdrom_pio_multi); + qtest_add_func("/ahci/cdrom/pio/raw", test_cdrom_pio_raw); qtest_add_func("/ahci/cdrom/pio/bcl", test_atapi_bcl); qtest_add_func("/ahci/cdrom/eject", test_atapi_tray); @@ -2109,6 +2423,13 @@ int main(int argc, char **argv) test_atapi_engine_restart_pio); qtest_add_func("/ahci/cdrom/engine_restart/dma", test_atapi_engine_restart_dma); + qtest_add_func("/ahci/io/ncq/unplug", test_unplug_ncq); + qtest_add_func("/ahci/io/dma/unplug", test_unplug_dma); + qtest_add_func("/ahci/io/pio/unplug", test_unplug_pio); + qtest_add_func("/ahci/io/pio/engine_stop", + test_write_engine_stop_in_flight); + qtest_add_func("/ahci/cdrom/drain/pio", test_atapi_drain_pio); + qtest_add_func("/ahci/cdrom/drain/dma", test_atapi_drain_dma); ret = g_test_run(); diff --git a/tests/qtest/aspeed-hace-utils.c b/tests/qtest/aspeed-hace-utils.c index 25450a296b..260eec043c 100644 --- a/tests/qtest/aspeed-hace-utils.c +++ b/tests/qtest/aspeed-hace-utils.c @@ -9,6 +9,7 @@ #include "libqtest.h" #include "qemu/bitops.h" #include "qemu/bswap.h" +#include "crypto/cipher.h" #include "aspeed-hace-utils.h" /* @@ -645,3 +646,695 @@ void aspeed_test_addresses(const char *machine, const uint32_t base, qtest_quit(s); } +/* + * Crypto engine register layout (offsets from the HACE base). + */ +#define HACE_CRYPTO_SRC 0x00 +#define HACE_CRYPTO_DEST 0x04 +#define HACE_CRYPTO_CONTEXT 0x08 +#define HACE_CRYPTO_DATA_LEN 0x0c +#define HACE_CRYPTO_CMD 0x10 +#define HACE_CRYPTO_GCM_ADD_LEN 0x14 +#define HACE_CRYPTO_GCM_TAG 0x18 + +/* Crypto command bits */ +#define HACE_CMD_ENCRYPT BIT(7) +#define HACE_CMD_ISR_EN BIT(12) +#define HACE_CMD_DES_SELECT BIT(16) +#define HACE_CMD_TRIPLE_DES BIT(17) +#define HACE_CMD_SRC_SG_CTRL BIT(18) +#define HACE_CMD_DST_SG_CTRL BIT(19) +#define HACE_CMD_OP_MODE_MASK (0x7 << 4) +#define HACE_CMD_ECB (0x0 << 4) +#define HACE_CMD_CBC (0x1 << 4) +#define HACE_CMD_CTR (0x4 << 4) +#define HACE_CMD_GCM (0x5 << 4) +#define HACE_CMD_AES128 (0x0 << 2) +#define HACE_CMD_AES256 (0x2 << 2) + +/* Context buffer layout: IV (DES at +8), key at +0x10 */ +#define HACE_CTX_KEY_OFFSET 0x10 +#define HACE_CTX_SIZE 0x30 + +/* + * Crypto known-answer test vectors, taken verbatim from the Linux kernel + * crypto self-test templates in crypto/testmgr.h: + * + * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/crypto/testmgr.h?h=v6.18 + * + * The originating template is noted above each block. CTR and the longer CBC + * vectors are truncated to a single block (still a valid known-answer test as + * the first block only depends on the IV). + */ + +/* aes_tv_template[0] (FIPS-197) */ +static const uint8_t aes128_ecb_key[16] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; +static const uint8_t aes128_ecb_ptext[16] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }; +static const uint8_t aes128_ecb_ctext[16] = { + 0x69, 0xc4, 0xe0, 0xd8, 0x6a, 0x7b, 0x04, 0x30, + 0xd8, 0xcd, 0xb7, 0x80, 0x70, 0xb4, 0xc5, 0x5a }; + +/* aes_cbc_tv_template[0] (RFC 3602) */ +static const uint8_t aes128_cbc_key[16] = { + 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b, + 0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06 }; +static const uint8_t aes128_cbc_iv[16] = { + 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30, + 0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41 }; +static const uint8_t aes128_cbc_ptext[16] = { + 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x6d, 0x73, 0x67 }; +static const uint8_t aes128_cbc_ctext[16] = { + 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8, + 0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a }; +static const uint8_t aes128_cbc_ivout[16] = { + 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8, + 0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a }; + +/* des_tv_template[0] (Applied Cryptography) */ +static const uint8_t des_ecb_key[8] = { + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; +static const uint8_t des_ecb_ptext[8] = { + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xe7 }; +static const uint8_t des_ecb_ctext[8] = { + 0xc9, 0x57, 0x44, 0x25, 0x6a, 0x5e, 0xd3, 0x1d }; + +/* des_cbc_tv_template[0] (OpenSSL), first block */ +static const uint8_t des_cbc_key[8] = { + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; +static const uint8_t des_cbc_iv[8] = { + 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10 }; +static const uint8_t des_cbc_ptext[8] = { + 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x20 }; +static const uint8_t des_cbc_ctext[8] = { + 0xcc, 0xd1, 0x73, 0xff, 0xab, 0x20, 0x39, 0xf4 }; + +/* des3_ede_tv_template[0] (OpenSSL) */ +static const uint8_t tdes_ecb_key[24] = { + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10 }; +static const uint8_t tdes_ecb_ptext[8] = { + 0x73, 0x6f, 0x6d, 0x65, 0x64, 0x61, 0x74, 0x61 }; +static const uint8_t tdes_ecb_ctext[8] = { + 0x18, 0xd7, 0x48, 0xe5, 0x63, 0x62, 0x05, 0x72 }; + +/* des3_ede_cbc_tv_template[0] (OpenSSL), first block */ +static const uint8_t tdes_cbc_key[24] = { + 0xe9, 0xc0, 0xff, 0x2e, 0x76, 0x0b, 0x64, 0x24, + 0x44, 0x4d, 0x99, 0x5a, 0x12, 0xd6, 0x40, 0xc0, + 0xea, 0xc2, 0x84, 0xe8, 0x14, 0x95, 0xdb, 0xe8 }; +static const uint8_t tdes_cbc_iv[8] = { + 0x7d, 0x33, 0x88, 0x93, 0x0f, 0x93, 0xb2, 0x42 }; +static const uint8_t tdes_cbc_ptext[8] = { + 0x6f, 0x54, 0x20, 0x6f, 0x61, 0x4d, 0x79, 0x6e }; +static const uint8_t tdes_cbc_ctext[8] = { + 0x0e, 0x2d, 0xb6, 0x97, 0x3c, 0x56, 0x33, 0xf4 }; + +/* aes_ctr_tv_template[0] (NIST SP800-38A F.5.1), first block */ +static const uint8_t aes128_ctr_key[16] = { + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }; +static const uint8_t aes128_ctr_iv[16] = { + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }; +static const uint8_t aes128_ctr_ptext[16] = { + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a }; +static const uint8_t aes128_ctr_ctext[16] = { + 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, + 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce }; +static const uint8_t aes128_ctr_ivout[16] = { + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xff, 0x00 }; + +/* des_ctr_tv_template[0] (Crypto++), first block */ +static const uint8_t des_ctr_key[8] = { + 0xc9, 0x83, 0xa6, 0xc9, 0xec, 0x0f, 0x32, 0x55 }; +static const uint8_t des_ctr_iv[8] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd }; +static const uint8_t des_ctr_ptext[8] = { + 0x50, 0xb9, 0x22, 0xae, 0x17, 0x80, 0x0c, 0x75 }; +static const uint8_t des_ctr_ctext[8] = { + 0x2f, 0x96, 0x06, 0x0f, 0x50, 0xc9, 0x68, 0x03 }; +static const uint8_t des_ctr_ivout[8] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe }; + +/* des3_ede_ctr_tv_template[0] (Crypto++), first block */ +static const uint8_t tdes_ctr_key[24] = { + 0x9c, 0xd6, 0xf3, 0x9c, 0xb9, 0x5a, 0x67, 0x00, + 0x5a, 0x67, 0x00, 0x2d, 0xce, 0xeb, 0x2d, 0xce, + 0xeb, 0xb4, 0x51, 0x72, 0xb4, 0x51, 0x72, 0x1f }; +static const uint8_t tdes_ctr_iv[8] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; +static const uint8_t tdes_ctr_ptext[8] = { + 0x05, 0xec, 0x77, 0xfb, 0x42, 0xd5, 0x59, 0x20 }; +static const uint8_t tdes_ctr_ctext[8] = { + 0x07, 0xc2, 0x08, 0x20, 0x72, 0x1f, 0x49, 0xef }; +static const uint8_t tdes_ctr_ivout[8] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +/* + * aes_gcm_tv_template[2] (AES-128) and [9] (AES-256), from the McGrew & Viega + * GCM spec (also NIST SP 800-38D), no AAD. Both cases share this plaintext/IV. + */ +static const uint8_t aes_gcm_ptext[64] = { + 0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, + 0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, + 0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, + 0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72, + 0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53, + 0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25, + 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57, + 0xba, 0x63, 0x7b, 0x39, 0x1a, 0xaf, 0xd2, 0x55 }; +static const uint8_t aes_gcm_iv[12] = { + 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad, + 0xde, 0xca, 0xf8, 0x88 }; + +/* aes_gcm_tv_template[2] (AES-128) */ +static const uint8_t aes128_gcm_key[16] = { + 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, + 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08 }; +static const uint8_t aes128_gcm_ctext[64] = { + 0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24, + 0x4b, 0x72, 0x21, 0xb7, 0x84, 0xd0, 0xd4, 0x9c, + 0xe3, 0xaa, 0x21, 0x2f, 0x2c, 0x02, 0xa4, 0xe0, + 0x35, 0xc1, 0x7e, 0x23, 0x29, 0xac, 0xa1, 0x2e, + 0x21, 0xd5, 0x14, 0xb2, 0x54, 0x66, 0x93, 0x1c, + 0x7d, 0x8f, 0x6a, 0x5a, 0xac, 0x84, 0xaa, 0x05, + 0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a, 0xac, 0x97, + 0x3d, 0x58, 0xe0, 0x91, 0x47, 0x3f, 0x59, 0x85 }; +static const uint8_t aes128_gcm_tag[16] = { + 0x4d, 0x5c, 0x2a, 0xf3, 0x27, 0xcd, 0x64, 0xa6, + 0x2c, 0xf3, 0x5a, 0xbd, 0x2b, 0xa6, 0xfa, 0xb4 }; + +/* aes_gcm_tv_template[9] (AES-256) */ +static const uint8_t aes256_gcm_key[32] = { + 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, + 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08, + 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, + 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08 }; +static const uint8_t aes256_gcm_ctext[64] = { + 0x52, 0x2d, 0xc1, 0xf0, 0x99, 0x56, 0x7d, 0x07, + 0xf4, 0x7f, 0x37, 0xa3, 0x2a, 0x84, 0x42, 0x7d, + 0x64, 0x3a, 0x8c, 0xdc, 0xbf, 0xe5, 0xc0, 0xc9, + 0x75, 0x98, 0xa2, 0xbd, 0x25, 0x55, 0xd1, 0xaa, + 0x8c, 0xb0, 0x8e, 0x48, 0x59, 0x0d, 0xbb, 0x3d, + 0xa7, 0xb0, 0x8b, 0x10, 0x56, 0x82, 0x88, 0x38, + 0xc5, 0xf6, 0x1e, 0x63, 0x93, 0xba, 0x7a, 0x0a, + 0xbc, 0xc9, 0xf6, 0x62, 0x89, 0x80, 0x15, 0xad }; +static const uint8_t aes256_gcm_tag[16] = { + 0xb0, 0x94, 0xda, 0xc5, 0xd9, 0x34, 0x71, 0xbd, + 0xec, 0x1a, 0x50, 0x22, 0x70, 0xe3, 0xcc, 0x6c }; + +typedef struct CryptTest { + QCryptoCipherMode mode; + QCryptoCipherAlgo alg; + /* expected context IV after encrypt, or NULL */ + const uint8_t *iv_out; + const uint8_t *ptext; + const uint8_t *ctext; + /* expected GCM authentication tag, or NULL for non-AEAD modes */ + const uint8_t *tag; + const uint8_t *key; + const uint8_t *iv; + const char *name; + size_t keylen; + size_t taglen; + /* algorithm | mode | key size selection */ + uint32_t cmd; + size_t ivlen; + size_t len; +} CryptTest; + +static const CryptTest crypt_tests[] = { + { + .name = "aes128-ecb", + .cmd = HACE_CMD_AES128 | HACE_CMD_ECB, + .alg = QCRYPTO_CIPHER_ALGO_AES_128, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = aes128_ecb_key, + .keylen = sizeof(aes128_ecb_key), + .ptext = aes128_ecb_ptext, + .ctext = aes128_ecb_ctext, + .len = sizeof(aes128_ecb_ptext), + }, + { + .name = "aes128-cbc", + .cmd = HACE_CMD_AES128 | HACE_CMD_CBC, + .alg = QCRYPTO_CIPHER_ALGO_AES_128, + .mode = QCRYPTO_CIPHER_MODE_CBC, + .key = aes128_cbc_key, + .keylen = sizeof(aes128_cbc_key), + .iv = aes128_cbc_iv, + .ivlen = sizeof(aes128_cbc_iv), + .ptext = aes128_cbc_ptext, + .ctext = aes128_cbc_ctext, + .iv_out = aes128_cbc_ivout, + .len = sizeof(aes128_cbc_ptext), + }, + { + .name = "des-ecb", + .cmd = HACE_CMD_DES_SELECT | HACE_CMD_ECB, + .alg = QCRYPTO_CIPHER_ALGO_DES, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = des_ecb_key, + .keylen = sizeof(des_ecb_key), + .ptext = des_ecb_ptext, + .ctext = des_ecb_ctext, + .len = sizeof(des_ecb_ptext), + }, + { + .name = "des-cbc", + .cmd = HACE_CMD_DES_SELECT | HACE_CMD_CBC, + .alg = QCRYPTO_CIPHER_ALGO_DES, + .mode = QCRYPTO_CIPHER_MODE_CBC, + .key = des_cbc_key, + .keylen = sizeof(des_cbc_key), + .iv = des_cbc_iv, + .ivlen = sizeof(des_cbc_iv), + .ptext = des_cbc_ptext, + .ctext = des_cbc_ctext, + .len = sizeof(des_cbc_ptext), + }, + { + .name = "des3_ede-ecb", + .cmd = HACE_CMD_DES_SELECT | HACE_CMD_TRIPLE_DES | HACE_CMD_ECB, + .alg = QCRYPTO_CIPHER_ALGO_3DES, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = tdes_ecb_key, + .keylen = sizeof(tdes_ecb_key), + .ptext = tdes_ecb_ptext, + .ctext = tdes_ecb_ctext, + .len = sizeof(tdes_ecb_ptext), + }, + { + .name = "des3_ede-cbc", + .cmd = HACE_CMD_DES_SELECT | HACE_CMD_TRIPLE_DES | HACE_CMD_CBC, + .alg = QCRYPTO_CIPHER_ALGO_3DES, + .mode = QCRYPTO_CIPHER_MODE_CBC, + .key = tdes_cbc_key, + .keylen = sizeof(tdes_cbc_key), + .iv = tdes_cbc_iv, + .ivlen = sizeof(tdes_cbc_iv), + .ptext = tdes_cbc_ptext, + .ctext = tdes_cbc_ctext, + .len = sizeof(tdes_cbc_ptext), + }, + { + .name = "aes128-ctr", + .cmd = HACE_CMD_AES128 | HACE_CMD_CTR, + .alg = QCRYPTO_CIPHER_ALGO_AES_128, + .mode = QCRYPTO_CIPHER_MODE_CTR, + .key = aes128_ctr_key, + .keylen = sizeof(aes128_ctr_key), + .iv = aes128_ctr_iv, + .ivlen = sizeof(aes128_ctr_iv), + .ptext = aes128_ctr_ptext, + .ctext = aes128_ctr_ctext, + .iv_out = aes128_ctr_ivout, + .len = sizeof(aes128_ctr_ptext), + }, + { + .name = "des-ctr", + .cmd = HACE_CMD_DES_SELECT | HACE_CMD_CTR, + .alg = QCRYPTO_CIPHER_ALGO_DES, + .mode = QCRYPTO_CIPHER_MODE_CTR, + .key = des_ctr_key, + .keylen = sizeof(des_ctr_key), + .iv = des_ctr_iv, + .ivlen = sizeof(des_ctr_iv), + .ptext = des_ctr_ptext, + .ctext = des_ctr_ctext, + .iv_out = des_ctr_ivout, + .len = sizeof(des_ctr_ptext), + }, + { + .name = "des3_ede-ctr", + .cmd = HACE_CMD_DES_SELECT | HACE_CMD_TRIPLE_DES | HACE_CMD_CTR, + .alg = QCRYPTO_CIPHER_ALGO_3DES, + .mode = QCRYPTO_CIPHER_MODE_CTR, + .key = tdes_ctr_key, + .keylen = sizeof(tdes_ctr_key), + .iv = tdes_ctr_iv, + .ivlen = sizeof(tdes_ctr_iv), + .ptext = tdes_ctr_ptext, + .ctext = tdes_ctr_ctext, + .iv_out = tdes_ctr_ivout, + .len = sizeof(tdes_ctr_ptext), + }, + { + .name = "aes128-gcm", + .cmd = HACE_CMD_AES128 | HACE_CMD_GCM, + .alg = QCRYPTO_CIPHER_ALGO_AES_128, + .mode = QCRYPTO_CIPHER_MODE_GCM, + .key = aes128_gcm_key, + .keylen = sizeof(aes128_gcm_key), + .iv = aes_gcm_iv, + .ivlen = sizeof(aes_gcm_iv), + .ptext = aes_gcm_ptext, + .ctext = aes128_gcm_ctext, + .tag = aes128_gcm_tag, + .taglen = sizeof(aes128_gcm_tag), + .len = sizeof(aes_gcm_ptext), + }, + { + .name = "aes256-gcm", + .cmd = HACE_CMD_AES256 | HACE_CMD_GCM, + .alg = QCRYPTO_CIPHER_ALGO_AES_256, + .mode = QCRYPTO_CIPHER_MODE_GCM, + .key = aes256_gcm_key, + .keylen = sizeof(aes256_gcm_key), + .iv = aes_gcm_iv, + .ivlen = sizeof(aes_gcm_iv), + .ptext = aes_gcm_ptext, + .ctext = aes256_gcm_ctext, + .tag = aes256_gcm_tag, + .taglen = sizeof(aes256_gcm_tag), + .len = sizeof(aes_gcm_ptext), + }, +}; + +/* DRAM offsets for the crypto test source, destination and context buffers. */ +#define CRYPT_OFF_SRC 0x10000 +#define CRYPT_OFF_DST 0x20000 +#define CRYPT_OFF_CTX 0x30000 +/* Scatter-gather list offsets (each list has CRYPT_SG_FRAGS entries). */ +#define CRYPT_OFF_SRC_SG 0x40000 +#define CRYPT_OFF_DST_SG 0x50000 +/* + * The scatter-gather tests split each buffer into CRYPT_SG_FRAGS fragments, + * each placed CRYPT_SG_FRAG_STRIDE apart so the fragments never abut. The gaps + * make the test fail if the engine ignores the list and reads one contiguous + * block. + */ +#define CRYPT_SG_FRAGS 3 +#define CRYPT_SG_FRAG_STRIDE 0x1000 +/* DRAM offset for the AES-GCM authentication tag write buffer. */ +#define CRYPT_OFF_TAG 0x60000 + +/* Describes one registered crypto test (qtest_add_data_func() data pointer). */ +typedef struct AspeedCryptoTest { + const char *machine; + uint64_t dram; + uint32_t base; + int index; + bool sg; +} AspeedCryptoTest; + +/* Map a command's operation mode (HACE10[6:4]) to a CRYPT_MODE_* flag. */ +static uint32_t crypt_mode_flag(uint32_t cmd) +{ + switch (cmd & HACE_CMD_OP_MODE_MASK) { + case HACE_CMD_ECB: + return CRYPT_MODE_ECB; + case HACE_CMD_CBC: + return CRYPT_MODE_CBC; + case HACE_CMD_CTR: + return CRYPT_MODE_CTR; + case HACE_CMD_GCM: + return CRYPT_MODE_GCM; + default: + return 0; + } +} + +static void crypt_write_ctx(QTestState *s, uint64_t ctx_addr, + const CryptTest *t) +{ + size_t iv_off = (t->cmd & HACE_CMD_DES_SELECT) ? 8 : 0; + uint8_t ctx[HACE_CTX_SIZE] = { 0 }; + + if (t->iv) { + memcpy(ctx + iv_off, t->iv, t->ivlen); + } + memcpy(ctx + HACE_CTX_KEY_OFFSET, t->key, t->keylen); + qtest_memwrite(s, ctx_addr, ctx, sizeof(ctx)); +} + +/* Run one crypto operation in direct access mode and read back the result. */ +static void crypt_run_direct(QTestState *s, uint32_t base, uint64_t dram, + const CryptTest *t, bool encrypt, uint8_t *out) +{ + const uint8_t *in = encrypt ? t->ptext : t->ctext; + uint32_t cmd = t->cmd | HACE_CMD_ISR_EN; + uint64_t src = dram + CRYPT_OFF_SRC; + uint64_t dst = dram + CRYPT_OFF_DST; + uint64_t ctx = dram + CRYPT_OFF_CTX; + + if (encrypt) { + cmd |= HACE_CMD_ENCRYPT; + } + + crypt_write_ctx(s, ctx, t); + qtest_memwrite(s, src, in, t->len); + + qtest_writel(s, base + HACE_CRYPTO_SRC, (uint32_t)src); + qtest_writel(s, base + HACE_CRYPTO_DEST, (uint32_t)dst); + qtest_writel(s, base + HACE_CRYPTO_CONTEXT, (uint32_t)ctx); + qtest_writel(s, base + HACE_CRYPTO_DATA_LEN, t->len); + qtest_writel(s, base + HACE_CRYPTO_CMD, cmd); + + g_assert_cmphex(qtest_readl(s, base + HACE_STS) & HACE_CRYPTO_ISR, ==, + HACE_CRYPTO_ISR); + qtest_writel(s, base + HACE_STS, HACE_CRYPTO_ISR); + + qtest_memread(s, dst, out, t->len); +} + +/* + * Byte range [*frag_off, *frag_off + *frag_len) of fragment @index when an + * @len-byte buffer is split into CRYPT_SG_FRAGS pieces; the last piece takes + * the remainder of an uneven split. + */ +static void crypt_frag_range(uint32_t len, int index, + uint32_t *frag_off, uint32_t *frag_len) +{ + uint32_t base = len / CRYPT_SG_FRAGS; + + *frag_off = base * index; + *frag_len = (index == CRYPT_SG_FRAGS - 1) ? len - *frag_off : base; +} + +/* + * Scatter [in, len) across CRYPT_SG_FRAGS buffers based at @base_off and spaced + * CRYPT_SG_FRAG_STRIDE apart, then build the SG list describing them at @list. + * When @in is NULL only the list is built (used for the destination, which the + * engine fills in). + */ +static void crypt_make_sg(QTestState *s, uint64_t dram, uint32_t base_off, + uint64_t list, const uint8_t *in, uint32_t len) +{ + struct AspeedSgList sg[CRYPT_SG_FRAGS]; + uint32_t frag_off; + uint32_t frag_len; + uint64_t buf; + int i; + + for (i = 0; i < CRYPT_SG_FRAGS; i++) { + crypt_frag_range(len, i, &frag_off, &frag_len); + buf = dram + base_off + i * CRYPT_SG_FRAG_STRIDE; + + if (in) { + qtest_memwrite(s, buf, in + frag_off, frag_len); + } + sg[i].len = cpu_to_le32(frag_len | (i == CRYPT_SG_FRAGS - 1 ? + SG_LIST_LEN_LAST : 0)); + sg[i].addr = cpu_to_le32((uint32_t)buf); + } + + qtest_memwrite(s, list, sg, sizeof(sg)); +} + +/* Gather a scatter-gathered result back from the CRYPT_SG_FRAGS buffers. */ +static void crypt_gather_sg(QTestState *s, uint64_t dram, uint32_t base_off, + uint8_t *out, uint32_t len) +{ + uint32_t frag_off; + uint32_t frag_len; + int i; + + for (i = 0; i < CRYPT_SG_FRAGS; i++) { + crypt_frag_range(len, i, &frag_off, &frag_len); + qtest_memread(s, dram + base_off + i * CRYPT_SG_FRAG_STRIDE, + out + frag_off, frag_len); + } +} + +/* + * Run one block-cipher (ECB/CBC/CTR) operation in scatter-gather mode and read + * back the result. The source and destination are each split across + * CRYPT_SG_FRAGS non-adjacent DRAM buffers described by an SG list; the gaps + * ensure the test fails if the engine ignores the list and reads one + * contiguous block. + */ +static void crypt_run_sg(QTestState *s, uint32_t base, uint64_t dram, + const CryptTest *t, bool encrypt, uint8_t *out) +{ + const uint8_t *in = encrypt ? t->ptext : t->ctext; + uint64_t src_sg = dram + CRYPT_OFF_SRC_SG; + uint64_t dst_sg = dram + CRYPT_OFF_DST_SG; + uint64_t ctx = dram + CRYPT_OFF_CTX; + uint32_t cmd = t->cmd | HACE_CMD_ISR_EN | HACE_CMD_SRC_SG_CTRL | + HACE_CMD_DST_SG_CTRL; + + if (encrypt) { + cmd |= HACE_CMD_ENCRYPT; + } + + crypt_write_ctx(s, ctx, t); + crypt_make_sg(s, dram, CRYPT_OFF_SRC, src_sg, in, t->len); + crypt_make_sg(s, dram, CRYPT_OFF_DST, dst_sg, NULL, t->len); + + qtest_writel(s, base + HACE_CRYPTO_SRC, (uint32_t)src_sg); + qtest_writel(s, base + HACE_CRYPTO_DEST, (uint32_t)dst_sg); + qtest_writel(s, base + HACE_CRYPTO_CONTEXT, (uint32_t)ctx); + qtest_writel(s, base + HACE_CRYPTO_DATA_LEN, t->len); + qtest_writel(s, base + HACE_CRYPTO_CMD, cmd); + + g_assert_cmphex(qtest_readl(s, base + HACE_STS) & HACE_CRYPTO_ISR, ==, + HACE_CRYPTO_ISR); + qtest_writel(s, base + HACE_STS, HACE_CRYPTO_ISR); + + crypt_gather_sg(s, dram, CRYPT_OFF_DST, out, t->len); +} + +/* + * Run one AES-GCM operation in scatter-gather mode: like crypt_run_sg() but + * also program the tag write buffer (HACE18) with no associated data, and read + * the authentication tag back into @out_tag. + */ +static void crypt_run_gcm(QTestState *s, uint32_t base, uint64_t dram, + const CryptTest *t, bool encrypt, uint8_t *out, + uint8_t *out_tag) +{ + const uint8_t *in = encrypt ? t->ptext : t->ctext; + uint64_t src_sg = dram + CRYPT_OFF_SRC_SG; + uint64_t dst_sg = dram + CRYPT_OFF_DST_SG; + uint64_t ctx = dram + CRYPT_OFF_CTX; + uint32_t cmd = t->cmd | HACE_CMD_ISR_EN | HACE_CMD_SRC_SG_CTRL | + HACE_CMD_DST_SG_CTRL; + + if (encrypt) { + cmd |= HACE_CMD_ENCRYPT; + } + + crypt_write_ctx(s, ctx, t); + crypt_make_sg(s, dram, CRYPT_OFF_SRC, src_sg, in, t->len); + crypt_make_sg(s, dram, CRYPT_OFF_DST, dst_sg, NULL, t->len); + + qtest_writel(s, base + HACE_CRYPTO_SRC, (uint32_t)src_sg); + qtest_writel(s, base + HACE_CRYPTO_DEST, (uint32_t)dst_sg); + qtest_writel(s, base + HACE_CRYPTO_CONTEXT, (uint32_t)ctx); + qtest_writel(s, base + HACE_CRYPTO_DATA_LEN, t->len); + qtest_writel(s, base + HACE_CRYPTO_GCM_ADD_LEN, 0); + qtest_writel(s, base + HACE_CRYPTO_GCM_TAG, + (uint32_t)(dram + CRYPT_OFF_TAG)); + qtest_writel(s, base + HACE_CRYPTO_CMD, cmd); + + g_assert_cmphex(qtest_readl(s, base + HACE_STS) & HACE_CRYPTO_ISR, ==, + HACE_CRYPTO_ISR); + qtest_writel(s, base + HACE_STS, HACE_CRYPTO_ISR); + + crypt_gather_sg(s, dram, CRYPT_OFF_DST, out, t->len); + qtest_memread(s, dram + CRYPT_OFF_TAG, out_tag, t->taglen); +} + +static void aspeed_test_crypto(const void *data) +{ + const AspeedCryptoTest *c = data; + const CryptTest *t = &crypt_tests[c->index]; + QTestState *s = qtest_init(c->machine); + uint8_t out[64]; + uint8_t iv[16]; + size_t iv_off; + + g_assert_cmpuint(t->len, <=, sizeof(out)); + + /* Encrypt: ptext -> ctext */ + if (c->sg) { + crypt_run_sg(s, c->base, c->dram, t, true, out); + } else { + crypt_run_direct(s, c->base, c->dram, t, true, out); + } + g_assert_cmpmem(out, t->len, t->ctext, t->len); + + if (t->iv_out) { + iv_off = (t->cmd & HACE_CMD_DES_SELECT) ? 8 : 0; + qtest_memread(s, c->dram + CRYPT_OFF_CTX + iv_off, iv, t->ivlen); + g_assert_cmpmem(iv, t->ivlen, t->iv_out, t->ivlen); + } + + /* Decrypt: ctext -> ptext */ + if (c->sg) { + crypt_run_sg(s, c->base, c->dram, t, false, out); + } else { + crypt_run_direct(s, c->base, c->dram, t, false, out); + } + g_assert_cmpmem(out, t->len, t->ptext, t->len); + + qtest_quit(s); +} + +static void aspeed_test_crypto_gcm(const void *data) +{ + const AspeedCryptoTest *c = data; + const CryptTest *t = &crypt_tests[c->index]; + QTestState *s = qtest_init(c->machine); + uint8_t out[64]; + uint8_t tag[16]; + + g_assert_cmpuint(t->len, <=, sizeof(out)); + + /* Encrypt: ptext -> ctext, then check the authentication tag. */ + crypt_run_gcm(s, c->base, c->dram, t, true, out, tag); + g_assert_cmpmem(out, t->len, t->ctext, t->len); + g_assert_cmpmem(tag, t->taglen, t->tag, t->taglen); + + /* Decrypt: ctext -> ptext, the recomputed tag must match. */ + crypt_run_gcm(s, c->base, c->dram, t, false, out, tag); + g_assert_cmpmem(out, t->len, t->ptext, t->len); + g_assert_cmpmem(tag, t->taglen, t->tag, t->taglen); + + qtest_quit(s); +} + +void aspeed_add_crypto_tests(const char *prefix, const char *machine, + uint32_t base, uint64_t dram, uint32_t modes, + bool sg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(crypt_tests); i++) { + bool is_gcm = crypt_tests[i].mode == QCRYPTO_CIPHER_MODE_GCM; + g_autofree char *path = NULL; + AspeedCryptoTest *t; + + if (!(modes & crypt_mode_flag(crypt_tests[i].cmd))) { + continue; + } + + if (!qcrypto_cipher_supports(crypt_tests[i].alg, + crypt_tests[i].mode)) { + g_printerr("# skip unsupported %s\n", crypt_tests[i].name); + continue; + } + + path = g_strdup_printf("%s/hace/crypto/%s", prefix, + crypt_tests[i].name); + t = g_new0(AspeedCryptoTest, 1); + t->machine = machine; + t->base = base; + t->dram = dram; + t->index = i; + t->sg = sg; + qtest_add_data_func_full(path, t, + is_gcm ? aspeed_test_crypto_gcm : + aspeed_test_crypto, g_free); + } +} + diff --git a/tests/qtest/aspeed-hace-utils.h b/tests/qtest/aspeed-hace-utils.h index 27ab2bb975..a5601a3d65 100644 --- a/tests/qtest/aspeed-hace-utils.h +++ b/tests/qtest/aspeed-hace-utils.h @@ -79,5 +79,25 @@ void aspeed_test_sha512_accum(const char *machine, const uint32_t base, void aspeed_test_addresses(const char *machine, const uint32_t base, const struct AspeedMasks *expected); +/* + * Cipher modes a SoC's crypto engine supports, for aspeed_add_crypto_tests(). + */ +enum { + CRYPT_MODE_ECB = 1 << 0, + CRYPT_MODE_CBC = 1 << 1, + CRYPT_MODE_CTR = 1 << 2, + CRYPT_MODE_GCM = 1 << 3, +}; + +/* + * Register the crypto known-answer tests that @modes selects (a mask of + * CRYPT_MODE_*) for the given machine. Each test is named + * "/hace/crypto/". @sg selects scatter-gather mode (used by the + * AST2600 and later) instead of the AST2500 direct access mode. + */ +void aspeed_add_crypto_tests(const char *prefix, const char *machine, + uint32_t base, uint64_t dram, uint32_t modes, + bool sg); + #endif /* TESTS_ASPEED_HACE_UTILS_H */ diff --git a/tests/qtest/aspeed-smc-utils.c b/tests/qtest/aspeed-smc-utils.c index c27d09e767..146332240d 100644 --- a/tests/qtest/aspeed-smc-utils.c +++ b/tests/qtest/aspeed-smc-utils.c @@ -4,23 +4,7 @@ * * Copyright (C) 2016 IBM Corp. * - * 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. + * SPDX-License-Identifier: MIT */ #include "qemu/osdep.h" @@ -73,6 +57,28 @@ static inline uint32_t flash_readl(const AspeedSMCTestData *data, return qtest_readl(data->s, data->flash_base + offset); } +/* + * Data FIFO port, in spi_base's register bank (not flash_base). Accesses + * through the FIFO require the complete user-mode transaction (opcode, + * address, and data). Assumes CS0, whose FIFO slot is at R_DATA_FIFO. + */ +static inline void datafifo_writeb(const AspeedSMCTestData *data, + uint8_t value) +{ + qtest_writeb(data->s, data->spi_base + R_DATA_FIFO, value); +} + +static inline void datafifo_writel(const AspeedSMCTestData *data, + uint32_t value) +{ + spi_writel(data, R_DATA_FIFO, value); +} + +static inline uint32_t datafifo_readl(const AspeedSMCTestData *data) +{ + return spi_readl(data, R_DATA_FIFO); +} + static void spi_conf(const AspeedSMCTestData *data, uint32_t value) { uint32_t conf = spi_readl(data, R_CONF); @@ -107,6 +113,29 @@ static void spi_ctrl_setmode(const AspeedSMCTestData *data, uint8_t mode, spi_writel(data, ctrl_reg, ctrl); } +/* Set FREADMODE with a fast read command and 1 dummy byte */ +static void spi_ctrl_set_fast_read(const AspeedSMCTestData *data, uint8_t cmd) +{ + uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; + uint32_t ctrl = spi_readl(data, ctrl_reg); + uint32_t iomode = 0; + + if (cmd == DOR) { + iomode = CTRL_IO_DUAL_DATA; + } else if (cmd == QOR) { + iomode = CTRL_IO_QUAD_DATA; + } + + ctrl &= ~(CTRL_USERMODE | (0xff << 16) | + (0x3 << CTRL_DUMMY_LOW_SHIFT) | + (0x1 << CTRL_DUMMY_HIGH_SHIFT) | + CTRL_IO_MODE_MASK); + ctrl |= CTRL_FREADMODE | (cmd << 16) | + (1 << CTRL_DUMMY_LOW_SHIFT) | + iomode; + spi_writel(data, ctrl_reg, ctrl); +} + static void spi_ctrl_start_user(const AspeedSMCTestData *data) { uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; @@ -186,6 +215,9 @@ static void read_page_mem(const AspeedSMCTestData *data, uint32_t addr, } } +typedef void (*read_page_mem_fn)(const AspeedSMCTestData *data, + uint32_t addr, uint32_t *page); + static void write_page_mem(const AspeedSMCTestData *data, uint32_t addr, uint32_t write_value) { @@ -327,9 +359,9 @@ void aspeed_smc_test_erase_all(const void *data) flash_reset(test_data); } -void aspeed_smc_test_write_page(const void *data) +static void test_write_page(const AspeedSMCTestData *test_data, + read_page_mem_fn reader) { - const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; uint32_t my_page_addr = test_data->page_addr; uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE; uint32_t page[FLASH_PAGE_SIZE / 4]; @@ -350,13 +382,13 @@ void aspeed_smc_test_write_page(const void *data) spi_ctrl_stop_user(test_data); /* Check what was written */ - read_page(test_data, my_page_addr, page); + reader(test_data, my_page_addr, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { g_assert_cmphex(page[i], ==, my_page_addr + i * 4); } /* Check some other page. It should be full of 0xff */ - read_page(test_data, some_page_addr, page); + reader(test_data, some_page_addr, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { g_assert_cmphex(page[i], ==, 0xffffffff); } @@ -364,9 +396,14 @@ void aspeed_smc_test_write_page(const void *data) flash_reset(test_data); } -void aspeed_smc_test_read_page_mem(const void *data) +void aspeed_smc_test_write_page(const void *data) +{ + test_write_page(data, read_page); +} + +static void test_read_page_mem(const AspeedSMCTestData *test_data, + read_page_mem_fn reader) { - const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; uint32_t my_page_addr = test_data->page_addr; uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE; uint32_t page[FLASH_PAGE_SIZE / 4]; @@ -393,13 +430,13 @@ void aspeed_smc_test_read_page_mem(const void *data) spi_conf_remove(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); /* Check what was written */ - read_page_mem(test_data, my_page_addr, page); + reader(test_data, my_page_addr, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { g_assert_cmphex(page[i], ==, my_page_addr + i * 4); } /* Check some other page. It should be full of 0xff */ - read_page_mem(test_data, some_page_addr, page); + reader(test_data, some_page_addr, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { g_assert_cmphex(page[i], ==, 0xffffffff); } @@ -407,6 +444,11 @@ void aspeed_smc_test_read_page_mem(const void *data) flash_reset(test_data); } +void aspeed_smc_test_read_page_mem(const void *data) +{ + test_read_page_mem(data, read_page_mem); +} + void aspeed_smc_test_write_page_mem(const void *data) { const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; @@ -684,3 +726,205 @@ void aspeed_smc_test_write_page_qpi(const void *data) flash_reset(test_data); } +static void read_page_mem_fast_read(const AspeedSMCTestData *data, + uint32_t addr, uint32_t *page) +{ + int i; + + spi_ctrl_set_fast_read(data, FAST_READ); + + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + page[i] = make_be32(flash_readl(data, addr + i * 4)); + } +} + +void aspeed_smc_test_read_page_mem_fast_read(const void *data) +{ + test_read_page_mem(data, read_page_mem_fast_read); +} + +static void read_page_fast_read(const AspeedSMCTestData *data, + uint32_t addr, uint32_t *page) +{ + int i; + + spi_ctrl_start_user(data); + + flash_writeb(data, 0, EN_4BYTE_ADDR); + flash_writeb(data, 0, FAST_READ); + flash_writel(data, 0, make_be32(addr)); + /* 1 dummy byte for standard SPI fast-read */ + flash_writeb(data, 0, 0x00); + + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + page[i] = make_be32(flash_readl(data, 0)); + } + spi_ctrl_stop_user(data); +} + +void aspeed_smc_test_write_page_fast_read(const void *data) +{ + test_write_page(data, read_page_fast_read); +} + +static void read_page_mem_dor(const AspeedSMCTestData *data, + uint32_t addr, uint32_t *page) +{ + int i; + + spi_ctrl_set_fast_read(data, DOR); + + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + page[i] = make_be32(flash_readl(data, addr + i * 4)); + } +} + +void aspeed_smc_test_read_page_mem_dor(const void *data) +{ + test_read_page_mem(data, read_page_mem_dor); +} + +static void read_page_dor(const AspeedSMCTestData *data, + uint32_t addr, uint32_t *page) +{ + int i; + + spi_ctrl_start_user(data); + + flash_writeb(data, 0, EN_4BYTE_ADDR); + flash_writeb(data, 0, DOR); + flash_writel(data, 0, make_be32(addr)); + /* 1 dummy byte for standard SPI DOR */ + flash_writeb(data, 0, 0x00); + + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + page[i] = make_be32(flash_readl(data, 0)); + } + spi_ctrl_stop_user(data); +} + +void aspeed_smc_test_write_page_dor(const void *data) +{ + test_write_page(data, read_page_dor); +} + +static void read_page_mem_qor(const AspeedSMCTestData *data, + uint32_t addr, uint32_t *page) +{ + int i; + + spi_ctrl_set_fast_read(data, QOR); + + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + page[i] = make_be32(flash_readl(data, addr + i * 4)); + } +} + +void aspeed_smc_test_read_page_mem_qor(const void *data) +{ + test_read_page_mem(data, read_page_mem_qor); +} + +static void read_page_qor(const AspeedSMCTestData *data, + uint32_t addr, uint32_t *page) +{ + int i; + + spi_ctrl_start_user(data); + + flash_writeb(data, 0, EN_4BYTE_ADDR); + flash_writeb(data, 0, QOR); + flash_writel(data, 0, make_be32(addr)); + /* 1 dummy byte for standard SPI QOR */ + flash_writeb(data, 0, 0x00); + + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + page[i] = make_be32(flash_readl(data, 0)); + } + spi_ctrl_stop_user(data); +} + +void aspeed_smc_test_write_page_qor(const void *data) +{ + test_write_page(data, read_page_qor); +} + +void aspeed_smc_test_write_page_datafifo(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t my_page_addr = test_data->page_addr; + uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE; + uint32_t page[FLASH_PAGE_SIZE / 4]; + int i; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + /* + * Send the complete user-mode transaction (opcode, address, data) + * through the Data FIFO port. + */ + spi_ctrl_start_user(test_data); + datafifo_writeb(test_data, EN_4BYTE_ADDR); + datafifo_writeb(test_data, WREN); + datafifo_writeb(test_data, PP); + datafifo_writel(test_data, make_be32(my_page_addr)); + + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + datafifo_writel(test_data, make_be32(my_page_addr + i * 4)); + } + spi_ctrl_stop_user(test_data); + + /* Check what was written, using the regular read path */ + read_page(test_data, my_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, my_page_addr + i * 4); + } + + /* Check some other page. It should be full of 0xff */ + read_page(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + flash_reset(test_data); +} + +void aspeed_smc_test_read_page_datafifo(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t my_page_addr = test_data->page_addr; + uint32_t page[FLASH_PAGE_SIZE / 4]; + int i; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + /* Write the page the regular way */ + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, PP); + flash_writel(test_data, 0, make_be32(my_page_addr)); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(test_data, 0, make_be32(my_page_addr + i * 4)); + } + spi_ctrl_stop_user(test_data); + + /* + * Read it back through the data FIFO port, again sending the whole + * transaction (opcode, address, data) through it. + */ + spi_ctrl_start_user(test_data); + datafifo_writeb(test_data, EN_4BYTE_ADDR); + datafifo_writeb(test_data, READ); + datafifo_writel(test_data, make_be32(my_page_addr)); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + page[i] = make_be32(datafifo_readl(test_data)); + } + spi_ctrl_stop_user(test_data); + + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, my_page_addr + i * 4); + } + + flash_reset(test_data); +} diff --git a/tests/qtest/aspeed-smc-utils.h b/tests/qtest/aspeed-smc-utils.h index e2fd8ff1bd..e4f538e579 100644 --- a/tests/qtest/aspeed-smc-utils.h +++ b/tests/qtest/aspeed-smc-utils.h @@ -4,23 +4,7 @@ * * Copyright (C) 2016 IBM Corp. * - * 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. + * SPDX-License-Identifier: MIT */ #ifndef TESTS_ASPEED_SMC_UTILS_H @@ -44,7 +28,13 @@ #define CTRL_FREADMODE 0x1 #define CTRL_WRITEMODE 0x2 #define CTRL_USERMODE 0x3 +#define CTRL_IO_QUAD_DATA BIT(30) +#define CTRL_IO_DUAL_DATA BIT(29) +#define CTRL_DUMMY_LOW_SHIFT 6 +#define CTRL_DUMMY_HIGH_SHIFT 14 #define SR_WEL BIT(1) +/* Data fifo */ +#define R_DATA_FIFO 0x200 /* * Flash commands @@ -55,6 +45,9 @@ enum { WRDI = 0x4, BULK_ERASE = 0xc7, READ = 0x03, + FAST_READ = 0x0b, + DOR = 0x3b, + QOR = 0x6b, PP = 0x02, WRSR = 0x1, WREN = 0x6, @@ -90,5 +83,13 @@ void aspeed_smc_test_status_reg_write_protection(const void *data); void aspeed_smc_test_write_block_protect(const void *data); void aspeed_smc_test_write_block_protect_bottom_bit(const void *data); void aspeed_smc_test_write_page_qpi(const void *data); +void aspeed_smc_test_read_page_mem_fast_read(const void *data); +void aspeed_smc_test_write_page_fast_read(const void *data); +void aspeed_smc_test_read_page_mem_dor(const void *data); +void aspeed_smc_test_write_page_dor(const void *data); +void aspeed_smc_test_read_page_mem_qor(const void *data); +void aspeed_smc_test_write_page_qor(const void *data); +void aspeed_smc_test_write_page_datafifo(const void *data); +void aspeed_smc_test_read_page_datafifo(const void *data); #endif /* TESTS_ASPEED_SMC_UTILS_H */ diff --git a/tests/qtest/aspeed_hace-test.c b/tests/qtest/aspeed_hace-test.c index 38777020ca..42130df1e2 100644 --- a/tests/qtest/aspeed_hace-test.c +++ b/tests/qtest/aspeed_hace-test.c @@ -210,6 +210,12 @@ int main(int argc, char **argv) qtest_add_func("ast1030/hace/sha384_accum", test_sha384_accum_ast1030); qtest_add_func("ast1030/hace/sha256_accum", test_sha256_accum_ast1030); + /* The AST1030 reuses the AST2600 crypto engine (scatter-gather, CTR). */ + aspeed_add_crypto_tests("ast1030", "-machine ast1030-evb", 0x7e6d0000, + 0x00000000, + CRYPT_MODE_ECB | CRYPT_MODE_CBC | CRYPT_MODE_CTR, + true); + qtest_add_func("ast2600/hace/addresses", test_addresses_ast2600); qtest_add_func("ast2600/hace/sha512", test_sha512_ast2600); qtest_add_func("ast2600/hace/sha384", test_sha384_ast2600); @@ -224,11 +230,23 @@ int main(int argc, char **argv) qtest_add_func("ast2600/hace/sha384_accum", test_sha384_accum_ast2600); qtest_add_func("ast2600/hace/sha256_accum", test_sha256_accum_ast2600); + /* The AST2600 crypto engine uses scatter-gather mode and adds CTR. */ + aspeed_add_crypto_tests("ast2600", "-machine ast2600-evb", 0x1e6d0000, + 0x80000000, + CRYPT_MODE_ECB | CRYPT_MODE_CBC | CRYPT_MODE_CTR, + true); + qtest_add_func("ast2500/hace/addresses", test_addresses_ast2500); qtest_add_func("ast2500/hace/sha512", test_sha512_ast2500); qtest_add_func("ast2500/hace/sha256", test_sha256_ast2500); qtest_add_func("ast2500/hace/md5", test_md5_ast2500); + /* + * The AST2500 crypto engine uses direct access mode and supports ECB/CBC. + */ + aspeed_add_crypto_tests("ast2500", "-machine ast2500-evb", 0x1e6e3000, + 0x80000000, CRYPT_MODE_ECB | CRYPT_MODE_CBC, false); + qtest_add_func("ast2400/hace/addresses", test_addresses_ast2400); qtest_add_func("ast2400/hace/sha512", test_sha512_ast2400); qtest_add_func("ast2400/hace/sha256", test_sha256_ast2400); diff --git a/tests/qtest/aspeed_smc-test.c b/tests/qtest/aspeed_smc-test.c index 39af1df0ed..59c96bd68a 100644 --- a/tests/qtest/aspeed_smc-test.c +++ b/tests/qtest/aspeed_smc-test.c @@ -4,23 +4,7 @@ * * Copyright (C) 2016 IBM Corp. * - * 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. + * SPDX-License-Identifier: MIT */ #include "qemu/osdep.h" @@ -68,6 +52,23 @@ static void test_palmetto_bmc(AspeedSMCTestData *data) data, aspeed_smc_test_read_status_reg); qtest_add_data_func("/ast2400/smc/status_reg_write_protection", data, aspeed_smc_test_status_reg_write_protection); + qtest_add_data_func("/ast2400/smc/read_page_mem_fast_read", + data, aspeed_smc_test_read_page_mem_fast_read); + qtest_add_data_func("/ast2400/smc/write_page_fast_read", + data, aspeed_smc_test_write_page_fast_read); + qtest_add_data_func("/ast2400/smc/read_page_mem_dor", + data, aspeed_smc_test_read_page_mem_dor); + qtest_add_data_func("/ast2400/smc/write_page_dor", + data, aspeed_smc_test_write_page_dor); + qtest_add_data_func("/ast2400/smc/read_page_mem_qor", + data, aspeed_smc_test_read_page_mem_qor); + qtest_add_data_func("/ast2400/smc/write_page_qor", + data, aspeed_smc_test_write_page_qor); + /* + * Block protect tests must be run last because the block protect + * state is not cleared by reset_memory() and silently prevents + * subsequent flash writes. + */ qtest_add_data_func("/ast2400/smc/write_block_protect", data, aspeed_smc_test_write_block_protect); qtest_add_data_func("/ast2400/smc/write_block_protect_bottom_bit", @@ -115,6 +116,18 @@ static void test_ast2500_evb(AspeedSMCTestData *data) data, aspeed_smc_test_read_status_reg); qtest_add_data_func("/ast2500/smc/write_page_qpi", data, aspeed_smc_test_write_page_qpi); + qtest_add_data_func("/ast2500/smc/read_page_mem_fast_read", + data, aspeed_smc_test_read_page_mem_fast_read); + qtest_add_data_func("/ast2500/smc/write_page_fast_read", + data, aspeed_smc_test_write_page_fast_read); + qtest_add_data_func("/ast2500/smc/read_page_mem_dor", + data, aspeed_smc_test_read_page_mem_dor); + qtest_add_data_func("/ast2500/smc/write_page_dor", + data, aspeed_smc_test_write_page_dor); + qtest_add_data_func("/ast2500/smc/read_page_mem_qor", + data, aspeed_smc_test_read_page_mem_qor); + qtest_add_data_func("/ast2500/smc/write_page_qor", + data, aspeed_smc_test_write_page_qor); } static void test_ast2600_evb(AspeedSMCTestData *data) @@ -158,6 +171,18 @@ static void test_ast2600_evb(AspeedSMCTestData *data) data, aspeed_smc_test_read_status_reg); qtest_add_data_func("/ast2600/smc/write_page_qpi", data, aspeed_smc_test_write_page_qpi); + qtest_add_data_func("/ast2600/smc/read_page_mem_fast_read", + data, aspeed_smc_test_read_page_mem_fast_read); + qtest_add_data_func("/ast2600/smc/write_page_fast_read", + data, aspeed_smc_test_write_page_fast_read); + qtest_add_data_func("/ast2600/smc/read_page_mem_dor", + data, aspeed_smc_test_read_page_mem_dor); + qtest_add_data_func("/ast2600/smc/write_page_dor", + data, aspeed_smc_test_write_page_dor); + qtest_add_data_func("/ast2600/smc/read_page_mem_qor", + data, aspeed_smc_test_read_page_mem_qor); + qtest_add_data_func("/ast2600/smc/write_page_qor", + data, aspeed_smc_test_write_page_qor); } static void test_ast1030_evb(AspeedSMCTestData *data) @@ -201,6 +226,18 @@ static void test_ast1030_evb(AspeedSMCTestData *data) data, aspeed_smc_test_read_status_reg); qtest_add_data_func("/ast1030/smc/write_page_qpi", data, aspeed_smc_test_write_page_qpi); + qtest_add_data_func("/ast1030/smc/read_page_mem_fast_read", + data, aspeed_smc_test_read_page_mem_fast_read); + qtest_add_data_func("/ast1030/smc/write_page_fast_read", + data, aspeed_smc_test_write_page_fast_read); + qtest_add_data_func("/ast1030/smc/read_page_mem_dor", + data, aspeed_smc_test_read_page_mem_dor); + qtest_add_data_func("/ast1030/smc/write_page_dor", + data, aspeed_smc_test_write_page_dor); + qtest_add_data_func("/ast1030/smc/read_page_mem_qor", + data, aspeed_smc_test_read_page_mem_qor); + qtest_add_data_func("/ast1030/smc/write_page_qor", + data, aspeed_smc_test_write_page_qor); } int main(int argc, char **argv) diff --git a/tests/qtest/ast2700-hace-test.c b/tests/qtest/ast2700-hace-test.c index 508a34dd6c..3f0217d635 100644 --- a/tests/qtest/ast2700-hace-test.c +++ b/tests/qtest/ast2700-hace-test.c @@ -94,5 +94,14 @@ int main(int argc, char **argv) qtest_add_func("ast2700/hace/sha384_accum", test_sha384_accum_ast2700); qtest_add_func("ast2700/hace/sha256_accum", test_sha256_accum_ast2700); + /* + * The AST2700 crypto engine uses scatter-gather with 64-bit DMA and adds + * AES-GCM on top of the ECB/CBC/CTR modes shared with the AST2600. + */ + aspeed_add_crypto_tests("ast2700", "-machine ast2700-evb", 0x12070000, + 0x400000000, + CRYPT_MODE_ECB | CRYPT_MODE_CBC | CRYPT_MODE_CTR | + CRYPT_MODE_GCM, true); + return g_test_run(); } diff --git a/tests/qtest/ast2700-smc-test.c b/tests/qtest/ast2700-smc-test.c index 33fc47230e..925dbcfaaf 100644 --- a/tests/qtest/ast2700-smc-test.c +++ b/tests/qtest/ast2700-smc-test.c @@ -52,6 +52,22 @@ static void test_ast2700_evb(AspeedSMCTestData *data) data, aspeed_smc_test_read_status_reg); qtest_add_data_func("/ast2700/smc/write_page_qpi", data, aspeed_smc_test_write_page_qpi); + qtest_add_data_func("/ast2700/smc/read_page_mem_fast_read", + data, aspeed_smc_test_read_page_mem_fast_read); + qtest_add_data_func("/ast2700/smc/write_page_fast_read", + data, aspeed_smc_test_write_page_fast_read); + qtest_add_data_func("/ast2700/smc/read_page_mem_dor", + data, aspeed_smc_test_read_page_mem_dor); + qtest_add_data_func("/ast2700/smc/write_page_dor", + data, aspeed_smc_test_write_page_dor); + qtest_add_data_func("/ast2700/smc/read_page_mem_qor", + data, aspeed_smc_test_read_page_mem_qor); + qtest_add_data_func("/ast2700/smc/write_page_qor", + data, aspeed_smc_test_write_page_qor); + qtest_add_data_func("/ast2700/smc/write_page_datafifo", + data, aspeed_smc_test_write_page_datafifo); + qtest_add_data_func("/ast2700/smc/read_page_datafifo", + data, aspeed_smc_test_read_page_datafifo); } int main(int argc, char **argv) diff --git a/tests/qtest/device-plug-test.c b/tests/qtest/device-plug-test.c index 2707ee59f6..650956fa11 100644 --- a/tests/qtest/device-plug-test.c +++ b/tests/qtest/device-plug-test.c @@ -65,6 +65,10 @@ static void test_pci_unplug_request(void) } if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + if (!qtest_has_machine("pc")) { + g_test_skip("Machine 'pc' is not available"); + return; + } machine_addition = "-machine pc"; } @@ -107,6 +111,10 @@ static void test_pci_unplug_json_request(void) } if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + if (!qtest_has_machine("pc")) { + g_test_skip("Machine 'pc' is not available"); + return; + } machine_addition = "-machine pc"; } diff --git a/tests/qtest/drive_del-test.c b/tests/qtest/drive_del-test.c index 30d9451ddd..4c32da594a 100644 --- a/tests/qtest/drive_del-test.c +++ b/tests/qtest/drive_del-test.c @@ -258,6 +258,10 @@ static void test_cli_device_del(void) } if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + if (!qtest_has_machine("pc")) { + g_test_skip("Machine 'pc' is not available"); + return; + } machine_addition = "-machine pc"; } @@ -332,6 +336,10 @@ static void test_device_add_and_del(void) } if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + if (!qtest_has_machine("pc")) { + g_test_skip("Machine 'pc' is not available"); + return; + } machine_addition = "-machine pc"; } @@ -403,6 +411,10 @@ static void test_drive_add_device_add_and_del(void) } if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + if (!qtest_has_machine("pc")) { + g_test_skip("Machine 'pc' is not available"); + return; + } machine_addition = "-machine pc"; } @@ -456,6 +468,10 @@ static void test_blockdev_add_device_add_and_del(void) } if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + if (!qtest_has_machine("pc")) { + g_test_skip("Machine 'pc' is not available"); + return; + } machine_addition = "-machine pc"; } diff --git a/tests/qtest/fdc-test.c b/tests/qtest/fdc-test.c index 1b37a8a4d2..1e1dd8659d 100644 --- a/tests/qtest/fdc-test.c +++ b/tests/qtest/fdc-test.c @@ -64,6 +64,12 @@ enum { DSKCHG = 0x80, }; +enum { + ST0_IC_MASK = 0xc0, /* interrupt code */ + ST0_IC_ABNTERM = 0x40, /* abnormal termination */ + + ST1_MA = 0x01, /* missing address mark */ +}; static char *test_image; @@ -270,6 +276,21 @@ static void test_cmos(void) g_assert(cmos == 0x40 || cmos == 0x50); } +static void media_insert(void) +{ + qtest_qmp_assert_success(global_qtest, + "{'execute':'blockdev-change-medium', 'arguments':{" + " 'id':'floppy0', 'filename': %s, 'format': 'raw' }}", + test_image); +} + +static void media_eject(void) +{ + qtest_qmp_assert_success(global_qtest, + "{'execute':'eject', 'arguments':{" + " 'id':'floppy0' }}"); +} + static void test_no_media_on_start(void) { uint8_t dir; @@ -301,10 +322,7 @@ static void test_media_insert(void) /* Insert media in drive. DSKCHK should not be reset until a step pulse * is sent. */ - qtest_qmp_assert_success(global_qtest, - "{'execute':'blockdev-change-medium', 'arguments':{" - " 'id':'floppy0', 'filename': %s, 'format': 'raw' }}", - test_image); + media_insert(); dir = inb(FLOPPY_BASE + reg_dir); assert_bit_set(dir, DSKCHG); @@ -333,9 +351,7 @@ static void test_media_change(void) /* Eject the floppy and check that DSKCHG is set. Reading it out doesn't * reset the bit. */ - qtest_qmp_assert_success(global_qtest, - "{'execute':'eject', 'arguments':{" - " 'id':'floppy0' }}"); + media_eject(); dir = inb(FLOPPY_BASE + reg_dir); assert_bit_set(dir, DSKCHG); @@ -414,6 +430,9 @@ static void test_read_id(void) uint8_t st0; uint8_t msr; + /* READ ID reads an address mark, so it needs a medium in the drive. */ + media_insert(); + /* Seek to track 0 and check with READ ID */ send_seek(0); @@ -491,6 +510,42 @@ static void test_read_id(void) g_assert_cmpint(cyl, ==, 8); g_assert_cmpint(head, ==, 1); g_assert_cmpint(st0, ==, head << 2); + + /* Leave the drive empty, the way the machine starts up. */ + media_eject(); +} + +/* + * An empty drive spins no diskette, so READ ID finds no address mark and must + * terminate abnormally. Reporting success (with a made-up sector ID) would + * tell the guest that a medium is still present after it has been ejected. + */ +static void test_read_id_no_media(void) +{ + uint8_t drive = 0; + uint8_t head = 0; + uint8_t st0, st1; + + floppy_send(CMD_READ_ID); + g_assert(!get_irq(FLOPPY_IRQ)); + floppy_send(head << 2 | drive); + + while (!get_irq(FLOPPY_IRQ)) { + clock_step(1000000000LL / 50); + } + + st0 = floppy_recv(); + st1 = floppy_recv(); + floppy_recv(); /* ST2 */ + floppy_recv(); /* cylinder */ + floppy_recv(); /* head */ + floppy_recv(); /* sector */ + g_assert(get_irq(FLOPPY_IRQ)); + floppy_recv(); /* sector size */ + g_assert(!get_irq(FLOPPY_IRQ)); + + g_assert_cmpint(st0 & ST0_IC_MASK, ==, ST0_IC_ABNTERM); + g_assert_cmpint(st1 & ST1_MA, ==, ST1_MA); } static void test_read_no_dma_1(void) @@ -625,6 +680,7 @@ int main(int argc, char **argv) qtest_add_func("/fdc/sense_interrupt", test_sense_interrupt); qtest_add_func("/fdc/relative_seek", test_relative_seek); qtest_add_func("/fdc/read_id", test_read_id); + qtest_add_func("/fdc/read_id_no_media", test_read_id_no_media); qtest_add_func("/fdc/verify", test_verify); qtest_add_func("/fdc/media_insert", test_media_insert); qtest_add_func("/fdc/read_no_dma_1", test_read_no_dma_1); diff --git a/tests/qtest/fuzz-virtio-scsi-test.c b/tests/qtest/fuzz-virtio-scsi-test.c index e37b48b2cc..102a5ccb67 100644 --- a/tests/qtest/fuzz-virtio-scsi-test.c +++ b/tests/qtest/fuzz-virtio-scsi-test.c @@ -19,7 +19,7 @@ static void test_mmio_oob_from_memory_region_cache(void) { QTestState *s; - s = qtest_init("-M pc-q35-5.2 -m 512M " + s = qtest_init("-M q35 -m 512M " "-device virtio-scsi,num_queues=8,addr=03.0 "); qtest_outl(s, 0xcf8, 0x80001811); diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c index 721e78170b..92e3d9b343 100644 --- a/tests/qtest/ide-test.c +++ b/tests/qtest/ide-test.c @@ -48,6 +48,10 @@ #define ATAPI_BLOCK_SIZE 2048 +/* Raw READ CD sector: 12 sync + 4 header + 2048 data + 288 EDC/ECC. */ +#define ATAPI_RAW_SIZE 2352 +#define ATAPI_RAW_DATA 16 + /* How many bytes to receive via ATAPI PIO at one time. * Must be less than 0xFFFF. */ #define BYTE_COUNT_LIMIT 5120 @@ -91,6 +95,8 @@ enum { enum { CMD_DSM = 0x06, + CMD_READ = 0x20, /* READ SECTOR(S) */ + CMD_WRITE = 0x30, /* WRITE SECTOR(S) */ CMD_DIAGNOSE = 0x90, CMD_INIT_DP = 0x91, /* INITIALIZE DEVICE PARAMETERS */ CMD_READ_DMA = 0xc8, @@ -98,6 +104,7 @@ enum { CMD_FLUSH_CACHE = 0xe7, CMD_IDENTIFY = 0xec, CMD_PACKET = 0xa0, + CMD_IDENTIFY_PACKET = 0xa1, CMD_READ_NATIVE = 0xf8, /* READ NATIVE MAX ADDRESS */ CMDF_ABORT = 0x100, @@ -982,6 +989,41 @@ static void send_scsi_cdb_read10(QPCIDevice *dev, QPCIBar ide_bar, } } +typedef struct ReadCDCDB { + uint8_t opcode; + uint8_t sector_type; + uint32_t lba; + uint8_t length[3]; + uint8_t main_channel; + uint8_t sub_channel; + uint8_t control; +} __attribute__((__packed__)) ReadCDCDB; + +static void send_scsi_cdb_read_cd(QPCIDevice *dev, QPCIBar ide_bar, + uint64_t lba, int nblocks) +{ + ReadCDCDB pkt = { }; + int i; + + g_assert_cmpint(lba, <=, UINT32_MAX); + g_assert_cmpint(nblocks, >=, 0); + g_assert_cmpint(nblocks, <=, 0xffffff); + + /* Construct SCSI CDB packet */ + pkt.opcode = 0xbe; + pkt.lba = cpu_to_be32(lba); + pkt.length[0] = (nblocks >> 16) & 0xff; + pkt.length[1] = (nblocks >> 8) & 0xff; + pkt.length[2] = nblocks & 0xff; + pkt.main_channel = 0xf8; /* sync + headers + user data + EDC/ECC: 2352 */ + + /* Send Packet */ + for (i = 0; i < sizeof(ReadCDCDB) / 2; i++) { + qpci_io_writew(dev, ide_bar, reg_data, + le16_to_cpu(((uint16_t *)&pkt)[i])); + } +} + static void nsleep(QTestState *qts, int64_t nsecs) { const struct timespec val = { .tv_nsec = nsecs }; @@ -1034,8 +1076,14 @@ static void ide_wait_intr(QTestState *qts, int irq) g_assert_not_reached(); } -static void cdrom_pio_impl(int nblocks) +#define CDROM_PIO 0 +#define CDROM_DMA (1 << 0) +#define CDROM_RAW (1 << 1) + +static void cdrom_read_impl(int nblocks, unsigned flags) { + bool dma = flags & CDROM_DMA; + bool raw = flags & CDROM_RAW; QTestState *qts; QPCIDevice *dev; QPCIBar bmdma_bar, ide_bar; @@ -1043,8 +1091,11 @@ static void cdrom_pio_impl(int nblocks) int patt_blocks = MAX(16, nblocks); size_t patt_len = ATAPI_BLOCK_SIZE * patt_blocks; char *pattern = g_malloc(patt_len); - size_t rxsize = ATAPI_BLOCK_SIZE * nblocks; + unsigned xfer = raw ? ATAPI_RAW_SIZE : ATAPI_BLOCK_SIZE; + size_t rxsize = xfer * nblocks; uint16_t *rx = g_malloc0(rxsize); + void (*send_cdb)(QPCIDevice *, QPCIBar, uint64_t, int) = + raw ? send_scsi_cdb_read_cd : send_scsi_cdb_read10; int i, j; uint8_t data; uint16_t limit; @@ -1063,110 +1114,749 @@ static void cdrom_pio_impl(int nblocks) dev = get_pci_device(qts, &bmdma_bar, &ide_bar); qtest_irq_intercept_in(qts, "ioapic"); - /* PACKET command on device 0 */ - qpci_io_writeb(dev, ide_bar, reg_device, 0); - qpci_io_writeb(dev, ide_bar, reg_lba_middle, BYTE_COUNT_LIMIT & 0xFF); - qpci_io_writeb(dev, ide_bar, reg_lba_high, (BYTE_COUNT_LIMIT >> 8 & 0xFF)); - qpci_io_writeb(dev, ide_bar, reg_command, CMD_PACKET); - /* HP0: Check_Status_A State */ - nsleep(qts, 400); - data = ide_wait_clear(qts, BSY); - /* HP1: Send_Packet State */ - assert_bit_set(data, DRQ | DRDY); - assert_bit_clear(data, ERR | DF | BSY); + if (dma) { + uintptr_t guest_buf = guest_alloc(&guest_malloc, rxsize); + PrdtEntry prdt[1]; - /* SCSI CDB (READ10) -- read n*2048 bytes from block 0 */ - send_scsi_cdb_read10(dev, ide_bar, 0, nblocks); + prdt[0].addr = cpu_to_le32(guest_buf); + prdt[0].size = cpu_to_le32(rxsize | PRDT_EOT); - /* Read data back: occurs in bursts of 'BYTE_COUNT_LIMIT' bytes. - * If BYTE_COUNT_LIMIT is odd, we transfer BYTE_COUNT_LIMIT - 1 bytes. - * We allow an odd limit only when the remaining transfer size is - * less than BYTE_COUNT_LIMIT. However, SCSI's read10 command can only - * request n blocks, so our request size is always even. - * For this reason, we assume there is never a hanging byte to fetch. */ - g_assert(!(rxsize & 1)); - limit = BYTE_COUNT_LIMIT & ~1; - for (i = 0; i < DIV_ROUND_UP(rxsize, limit); i++) { - size_t offset = i * (limit / 2); - size_t rem = (rxsize / 2) - offset; + send_dma_request_dev(qts, dev, bmdma_bar, ide_bar, CMD_PACKET, 0, + nblocks, prdt, ARRAY_SIZE(prdt), send_cdb); - /* HP3: INTRQ_Wait */ - ide_wait_intr(qts, IDE_PRIMARY_IRQ); - - /* HP2: Check_Status_B (and clear IRQ) */ + qtest_memread(qts, guest_buf, rx, rxsize); + } else { + /* PACKET command on device 0 */ + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_lba_middle, BYTE_COUNT_LIMIT & 0xFF); + qpci_io_writeb(dev, ide_bar, reg_lba_high, + (BYTE_COUNT_LIMIT >> 8 & 0xFF)); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_PACKET); + /* HP0: Check_Status_A State */ + nsleep(qts, 400); data = ide_wait_clear(qts, BSY); + /* HP1: Send_Packet State */ assert_bit_set(data, DRQ | DRDY); assert_bit_clear(data, ERR | DF | BSY); - /* HP4: Transfer_Data */ - for (j = 0; j < MIN((limit / 2), rem); j++) { - rx[offset + j] = cpu_to_le16(qpci_io_readw(dev, ide_bar, - reg_data)); + send_cdb(dev, ide_bar, 0, nblocks); + + /* + * Read data back: occurs in bursts of 'BYTE_COUNT_LIMIT' bytes. + * If BYTE_COUNT_LIMIT is odd, we transfer BYTE_COUNT_LIMIT - 1 bytes. + * We allow an odd limit only when the remaining transfer size is + * less than BYTE_COUNT_LIMIT. However, SCSI's read10 command can only + * request n blocks, so our request size is always even. + * For this reason, we assume there is never a hanging byte to fetch. + */ + g_assert(!(rxsize & 1)); + limit = BYTE_COUNT_LIMIT & ~1; + for (i = 0; i < DIV_ROUND_UP(rxsize, limit); i++) { + size_t offset = i * (limit / 2); + size_t rem = (rxsize / 2) - offset; + + /* HP3: INTRQ_Wait */ + ide_wait_intr(qts, IDE_PRIMARY_IRQ); + + /* HP2: Check_Status_B (and clear IRQ) */ + data = ide_wait_clear(qts, BSY); + assert_bit_set(data, DRQ | DRDY); + assert_bit_clear(data, ERR | DF | BSY); + + /* HP4: Transfer_Data */ + for (j = 0; j < MIN((limit / 2), rem); j++) { + rx[offset + j] = cpu_to_le16(qpci_io_readw(dev, ide_bar, + reg_data)); + } } + + /* Check for final completion IRQ */ + ide_wait_intr(qts, IDE_PRIMARY_IRQ); + + /* Sanity check final state */ + data = ide_wait_clear(qts, DRQ); + assert_bit_set(data, DRDY); + assert_bit_clear(data, DRQ | ERR | DF | BSY); } - /* Check for final completion IRQ */ - ide_wait_intr(qts, IDE_PRIMARY_IRQ); + if (raw) { + /* The 2048-byte payload of each raw sector sits past its header. */ + for (i = 0; i < nblocks; i++) { + uint8_t *sec = (uint8_t *)rx + i * ATAPI_RAW_SIZE + ATAPI_RAW_DATA; - /* Sanity check final state */ - data = ide_wait_clear(qts, DRQ); - assert_bit_set(data, DRDY); - assert_bit_clear(data, DRQ | ERR | DF | BSY); + g_assert_cmpint(memcmp(sec, pattern + i * ATAPI_BLOCK_SIZE, + ATAPI_BLOCK_SIZE), ==, 0); + } + } else { + g_assert_cmpint(memcmp(pattern, rx, rxsize), ==, 0); + } - g_assert_cmpint(memcmp(pattern, rx, rxsize), ==, 0); g_free(pattern); g_free(rx); test_bmdma_teardown(qts); free_pci_device(dev); } +static void ide_identify_words(QPCIDevice *dev, QPCIBar ide_bar, + uint16_t buf[256]) +{ + int i; + + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_IDENTIFY); + for (i = 0; i < 256; i++) { + buf[i] = qpci_io_readw(dev, ide_bar, reg_data); + } +} + +/* Zero sectors per track has to abort (ATA-5 8.16.6), not divide by zero */ +static void test_specify_zero_sectors(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uint16_t buf[256]; + uint8_t data; + int i; + + qts = ide_test_start( + "-blockdev driver=file,node-name=hda,filename=%s " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 ", + tmp_path[0]); + + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + qpci_io_writeb(dev, ide_bar, reg_nsectors, 0); + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_INIT_DP); + + assert_bit_set(qpci_io_readb(dev, ide_bar, reg_status), ERR); + assert_bit_set(qpci_io_readb(dev, ide_bar, reg_error), ABRT); + + /* The refused request has to leave the default translation in effect */ + ide_identify_words(dev, ide_bar, buf); + g_assert_cmpint(buf[55], ==, 16); + g_assert_cmpint(buf[56], ==, 63); + + /* READ SECTOR(S) of CHS 0/0/1, which used to crash QEMU */ + qpci_io_writeb(dev, ide_bar, reg_nsectors, 1); + qpci_io_writeb(dev, ide_bar, reg_lba_low, 1); + qpci_io_writeb(dev, ide_bar, reg_lba_middle, 0); + qpci_io_writeb(dev, ide_bar, reg_lba_high, 0); + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_READ); + + data = ide_wait_clear(qts, BSY); + assert_bit_set(data, DRQ); + assert_bit_clear(data, ERR | DF); + for (i = 0; i < 256; i++) { + buf[i] = qpci_io_readw(dev, ide_bar, reg_data); + } + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), ERR | DF | DRQ); + + /* A supported translation is still accepted */ + qpci_io_writeb(dev, ide_bar, reg_nsectors, 32); + qpci_io_writeb(dev, ide_bar, reg_device, 7); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_INIT_DP); + + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), ERR); + + ide_test_quit(qts); + free_pci_device(dev); +} + +/* Addressed by LBA, so no translation can influence where it lands */ +static void ide_write_marker(QTestState *qts, QPCIDevice *dev, QPCIBar ide_bar, + uint32_t lba, const char *marker) +{ + uint16_t buf[256]; + uint8_t data; + int i; + + memset(buf, 0, sizeof(buf)); + memcpy(buf, marker, strlen(marker)); + + qpci_io_writeb(dev, ide_bar, reg_nsectors, 1); + qpci_io_writeb(dev, ide_bar, reg_lba_low, lba & 0xff); + qpci_io_writeb(dev, ide_bar, reg_lba_middle, (lba >> 8) & 0xff); + qpci_io_writeb(dev, ide_bar, reg_lba_high, (lba >> 16) & 0xff); + qpci_io_writeb(dev, ide_bar, reg_device, LBA | ((lba >> 24) & 0xf)); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_WRITE); + + data = ide_wait_clear(qts, BSY); + assert_bit_set(data, DRQ); + for (i = 0; i < 256; i++) { + qpci_io_writew(dev, ide_bar, reg_data, buf[i]); + } + data = ide_wait_clear(qts, BSY); + assert_bit_clear(data, ERR | DF | DRQ); + + qpci_io_writeb(dev, ide_bar, reg_command, CMD_FLUSH_CACHE); + data = ide_wait_clear(qts, BSY); + assert_bit_clear(data, ERR | DF); +} + +/* The marker read back names the sector the translation selected */ +static void ide_read_chs_marker(QTestState *qts, QPCIDevice *dev, + QPCIBar ide_bar, uint8_t cyl_lo, uint8_t head, + uint8_t sector, char out[9]) +{ + uint16_t buf[256]; + uint8_t data; + int i; + + qpci_io_writeb(dev, ide_bar, reg_nsectors, 1); + qpci_io_writeb(dev, ide_bar, reg_lba_low, sector); + qpci_io_writeb(dev, ide_bar, reg_lba_middle, cyl_lo); + qpci_io_writeb(dev, ide_bar, reg_lba_high, 0); + qpci_io_writeb(dev, ide_bar, reg_device, head & 0xf); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_READ); + + data = ide_wait_clear(qts, BSY); + assert_bit_set(data, DRQ); + assert_bit_clear(data, ERR | DF); + for (i = 0; i < 256; i++) { + buf[i] = qpci_io_readw(dev, ide_bar, reg_data); + } + data = ide_wait_clear(qts, BSY); + assert_bit_clear(data, ERR | DF | DRQ); + + memcpy(out, buf, 8); + out[8] = '\0'; +} + +static void ide_set_translation(QPCIDevice *dev, QPCIBar ide_bar, + uint8_t heads, uint8_t sectors) +{ + qpci_io_writeb(dev, ide_bar, reg_nsectors, sectors); + qpci_io_writeb(dev, ide_bar, reg_device, heads - 1); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_INIT_DP); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), ERR); +} + +/* CHS 0/1/1 is LBA 32 under 8/32, and LBA 63 under the drive's own 16/63 */ +#define CHS_MARKER_CUSTOM "CUSTOM__" +#define CHS_MARKER_DEFAULT "DEFAULT_" + +static void ide_prepare_markers(QTestState *qts, QPCIDevice *dev, + QPCIBar ide_bar) +{ + ide_write_marker(qts, dev, ide_bar, 32, CHS_MARKER_CUSTOM); + ide_write_marker(qts, dev, ide_bar, 63, CHS_MARKER_DEFAULT); +} + +static void ide_hmp_quiet(QTestState *qts, const char *command) +{ + g_autofree char *out = qtest_hmp(qts, "%s", command); + + g_assert_cmpstr(out, ==, ""); +} + +static char *ide_migration_status(QTestState *qts) +{ + QDict *ret; + char *status; + + ret = qtest_qmp_assert_success_ref(qts, "{ 'execute': 'query-migrate' }"); + g_assert(qdict_haskey(ret, "status")); + status = g_strdup(qdict_get_str(ret, "status")); + qobject_unref(ret); + + return status; +} + +/* Waiting for the other side's event would hang if it refuses the stream */ +static void ide_migration_wait(QTestState *qts, const char *expected) +{ + while (true) { + g_autofree char *status = ide_migration_status(qts); + + if (g_str_equal(status, expected)) { + return; + } + if (!g_str_equal(status, "setup") && !g_str_equal(status, "active") && + !g_str_equal(status, "device")) { + fprintf(stderr, "Migration status is %s, expected %s\n", + status, expected); + g_assert_not_reached(); + } + g_usleep(5000); + } +} + +static void ide_migrate(QTestState *src, QTestState *dst, const char *uri) +{ + qtest_qmp_assert_success(src, "{ 'execute': 'migrate'," + " 'arguments': { 'uri': %s } }", uri); + qtest_qmp_eventwait(src, "STOP"); + ide_migration_wait(src, "completed"); + qtest_qmp_eventwait(dst, "RESUME"); +} + +/* A translation the guest selected has to survive migration */ +static void test_migrate_chs_translation(void) +{ + QTestState *src, *dst; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + g_autofree char *mig_path = NULL; + g_autofree char *uri = NULL; + g_autofree char *dst_args = NULL; + char marker[9]; + int fd; + + fd = g_file_open_tmp("qtest-ide-migration.XXXXXX", &mig_path, NULL); + g_assert(fd >= 0); + close(fd); + uri = g_strdup_printf("unix:%s", mig_path); + + src = ide_test_start( + "-blockdev driver=file,node-name=hda,filename=%s,locking=off " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 ", + tmp_path[0]); + dev = get_pci_device(src, &bmdma_bar, &ide_bar); + + ide_prepare_markers(src, dev, ide_bar); + ide_set_translation(dev, ide_bar, 8, 32); + ide_read_chs_marker(src, dev, ide_bar, 0, 1, 1, marker); + g_assert_cmpstr(marker, ==, CHS_MARKER_CUSTOM); + + dst_args = g_strdup_printf( + "-machine pc " + "-blockdev driver=file,node-name=hda,filename=%s,locking=off " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 -incoming %s", + tmp_path[0], uri); + dst = qtest_init(dst_args); + + ide_migrate(src, dst, uri); + + /* Talk to the destination instead of the source */ + qpci_free_pc(pcibus); + pcibus = NULL; + free_pci_device(dev); + dev = get_pci_device(dst, &bmdma_bar, &ide_bar); + + ide_read_chs_marker(dst, dev, ide_bar, 0, 1, 1, marker); + g_assert_cmpstr(marker, ==, CHS_MARKER_CUSTOM); + + free_pci_device(dev); + qtest_quit(dst); + ide_test_quit(src); + unlink(mig_path); +} + +/* A translation selected after the snapshot must not outlive loading it */ +static void test_migrate_chs_snapshot(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + g_autofree char *img = NULL; + char marker[9]; + int fd; + + if (!have_qemu_img()) { + g_test_skip("QTEST_QEMU_IMG not set, snapshots need a qcow2 image"); + return; + } + + fd = g_file_open_tmp("qtest-ide-snapshot.XXXXXX", &img, NULL); + g_assert(fd >= 0); + close(fd); + g_assert(mkimg(img, "qcow2", TEST_IMAGE_SIZE / (1024 * 1024))); + + qts = ide_test_start( + "-blockdev driver=qcow2,node-name=hda,file.driver=file," + "file.filename=%s " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 ", img); + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + ide_prepare_markers(qts, dev, ide_bar); + + /* Snapshot taken while the default translation is in effect */ + ide_read_chs_marker(qts, dev, ide_bar, 0, 1, 1, marker); + g_assert_cmpstr(marker, ==, CHS_MARKER_DEFAULT); + ide_hmp_quiet(qts, "savevm s0"); + + ide_set_translation(dev, ide_bar, 8, 32); + ide_read_chs_marker(qts, dev, ide_bar, 0, 1, 1, marker); + g_assert_cmpstr(marker, ==, CHS_MARKER_CUSTOM); + + ide_hmp_quiet(qts, "loadvm s0"); + + ide_read_chs_marker(qts, dev, ide_bar, 0, 1, 1, marker); + g_assert_cmpstr(marker, ==, CHS_MARKER_DEFAULT); + + free_pci_device(dev); + ide_test_quit(qts); + unlink(img); +} + +/* A migration stream holds NUL bytes, so this cannot be a string search */ +static char *ide_stream_find(char *stream, gsize len, const char *name) +{ + gsize name_len = strlen(name); + gsize i; + + if (len < name_len) { + return NULL; + } + for (i = 0; i <= len - name_len; i++) { + if (memcmp(stream + i, name, name_len) == 0) { + return stream + i; + } + } + + return NULL; +} + +/* A translation no command could have selected has to be refused on load */ +static void test_migrate_chs_rejected(void) +{ + const char *name = "ide_drive/chs_translation"; + QTestState *src, *dst; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + g_autofree char *path = NULL; + g_autofree char *uri = NULL; + g_autofree char *dst_args = NULL; + g_autofree char *stream = NULL; + char *subsection; + gsize len; + int fd; + + fd = g_file_open_tmp("qtest-ide-stream.XXXXXX", &path, NULL); + g_assert(fd >= 0); + close(fd); + uri = g_strdup_printf("file:%s", path); + + src = ide_test_start( + "-blockdev driver=file,node-name=hda,filename=%s " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 ", + tmp_path[0]); + dev = get_pci_device(src, &bmdma_bar, &ide_bar); + + ide_set_translation(dev, ide_bar, 8, 32); + qtest_qmp_assert_success(src, "{ 'execute': 'migrate'," + " 'arguments': { 'uri': %s } }", uri); + qtest_qmp_eventwait(src, "STOP"); + ide_migration_wait(src, "completed"); + free_pci_device(dev); + ide_test_quit(src); + + /* + * Behind the name come version, heads and sectors, each big endian 32 bit. + * The name recurs in the description at the end of the stream, so the + * first match is the one carrying data. + */ + g_assert(g_file_get_contents(path, &stream, &len, NULL)); + subsection = ide_stream_find(stream, len, name); + g_assert(subsection); + g_assert_cmpint(subsection - stream + strlen(name) + 12, <=, len); + memset(subsection + strlen(name) + 8, 0, 4); + g_assert(g_file_set_contents(path, stream, len, NULL)); + + dst_args = g_strdup_printf( + "-machine pc " + "-blockdev driver=file,node-name=hda,filename=%s " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 -incoming defer", + tmp_path[0]); + dst = qtest_init(dst_args); + + qtest_qmp_assert_success(dst, "{ 'execute': 'migrate-incoming'," + " 'arguments': { 'uri': %s," + " 'exit-on-error': false } }", uri); + ide_migration_wait(dst, "failed"); + + qtest_quit(dst); + unlink(path); +} + +/* + * A device advertising UDMA5 has to claim a standard that defines it, and a + * parallel attachment has to report the cable word (ACS-3 7.12.7.47). + */ +static void test_identify_udma(bool packet) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uint16_t buf[256]; + int i; + + if (packet) { + qts = ide_test_start("-device ide-cd,bus=ide.0"); + } else { + qts = ide_test_start( + "-blockdev driver=file,node-name=hda,filename=%s " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 ", + tmp_path[0]); + } + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, + packet ? CMD_IDENTIFY_PACKET : CMD_IDENTIFY); + for (i = 0; i < 256; i++) { + buf[i] = qpci_io_readw(dev, ide_bar, reg_data); + } + + /* UDMA5 supported and selected */ + assert_bit_set(buf[88], 1 << 5); + assert_bit_set(buf[88], 1 << 13); + + /* UDMA5 arrived in ATA/ATAPI-6, so word 80 has to reach bit 6 */ + assert_bit_set(buf[80], 1 << 6); + if (packet) { + /* Bits 3:1 are obsolete in IDENTIFY PACKET DEVICE data */ + assert_bit_clear(buf[80], 0x0e); + } + + /* Word 93: reserved bit clear, fixed bit set, 80-conductor cable */ + assert_bit_clear(buf[93], 1 << 15); + assert_bit_set(buf[93], 1 << 14); + assert_bit_set(buf[93], 1 << 13); + assert_bit_set(buf[93], 1 << 0); + /* Device 0 clears the device 1 result */ + assert_bit_clear(buf[93], 0x1f00); + if (packet) { + /* the disk path has yet to gain this */ + assert_bit_set(buf[93], 1 << 3); + } + + free_pci_device(dev); + ide_test_quit(qts); +} + +static void test_identify_udma_ata(void) +{ + test_identify_udma(false); +} + +static void test_identify_udma_atapi(void) +{ + test_identify_udma(true); +} + +/* A PIO transfer window reaching past the io_buffer has to be refused */ +static void test_migrate_pio_state_rejected(void) +{ + const char *name = "ide_drive/pio_state"; + /* IDE_DMA_BUF_SECTORS * 512 + 4, the length of the streamed io_buffer */ + const gsize io_buffer_len = 256 * 512 + 4; + /* cur_io_buffer_offset and cur_io_buffer_len, big endian */ + const uint8_t in_bounds[8] = { 0, 0, 0, 0, 0, 0, 0x02, 0 }; + const uint8_t past_the_end[8] = { 0, 0x02, 0, 0x04, 0, 0, 0x10, 0 }; + QTestState *src, *dst; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + g_autofree char *path = NULL; + g_autofree char *uri = NULL; + g_autofree char *dst_args = NULL; + g_autofree char *stream = NULL; + char *window; + gsize len; + int fd; + + fd = g_file_open_tmp("qtest-ide-stream.XXXXXX", &path, NULL); + g_assert(fd >= 0); + close(fd); + uri = g_strdup_printf("file:%s", path); + + src = ide_test_start( + "-blockdev driver=file,node-name=hda,filename=%s " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 ", + tmp_path[0]); + dev = get_pci_device(src, &bmdma_bar, &ide_bar); + + /* WRITE SECTOR(S) waits in DRQ for the data, so pio_state is streamed */ + qpci_io_writeb(dev, ide_bar, reg_nsectors, 1); + qpci_io_writeb(dev, ide_bar, reg_lba_low, 0); + qpci_io_writeb(dev, ide_bar, reg_lba_middle, 0); + qpci_io_writeb(dev, ide_bar, reg_lba_high, 0); + qpci_io_writeb(dev, ide_bar, reg_device, LBA); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_WRITE); + assert_bit_set(qpci_io_readb(dev, ide_bar, reg_status), DRQ); + + qtest_qmp_assert_success(src, "{ 'execute': 'migrate'," + " 'arguments': { 'uri': %s } }", uri); + qtest_qmp_eventwait(src, "STOP"); + ide_migration_wait(src, "completed"); + free_pci_device(dev); + ide_test_quit(src); + + /* + * Behind the name come the version and req_nb_sectors as big endian 32 + * bit, then the io_buffer array, then the transfer window this rewrites. + * Asserting the window the source streamed keeps that arithmetic honest. + */ + g_assert(g_file_get_contents(path, &stream, &len, NULL)); + window = ide_stream_find(stream, len, name); + g_assert(window); + window += strlen(name) + 8 + io_buffer_len; + g_assert_cmpint(window - stream + sizeof(past_the_end), <=, len); + g_assert_cmpint(memcmp(window, in_bounds, sizeof(in_bounds)), ==, 0); + memcpy(window, past_the_end, sizeof(past_the_end)); + g_assert(g_file_set_contents(path, stream, len, NULL)); + + dst_args = g_strdup_printf( + "-machine pc " + "-blockdev driver=file,node-name=hda,filename=%s " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 -incoming defer", + tmp_path[0]); + dst = qtest_init(dst_args); + + qtest_qmp_assert_success(dst, "{ 'execute': 'migrate-incoming'," + " 'arguments': { 'uri': %s," + " 'exit-on-error': false } }", uri); + ide_migration_wait(dst, "failed"); + + qtest_quit(dst); + unlink(path); +} + +/* Words 54 to 58 follow the translation even when the data was cached first */ +static void test_specify_identify(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uint16_t buf[256]; + unsigned int cyls; + + qts = ide_test_start( + "-blockdev driver=file,node-name=hda,filename=%s " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 ", + tmp_path[0]); + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + /* Have the data built while the default translation is still in effect */ + ide_identify_words(dev, ide_bar, buf); + cyls = buf[1]; + g_assert_cmpint(buf[3], ==, 16); + g_assert_cmpint(buf[6], ==, 63); + g_assert_cmpint(buf[53] & 1, ==, 1); + g_assert_cmpint(buf[55], ==, 16); + g_assert_cmpint(buf[56], ==, 63); + g_assert_cmpint(buf[57] | (buf[58] << 16), ==, cyls * 16 * 63); + + ide_set_translation(dev, ide_bar, 8, 32); + + ide_identify_words(dev, ide_bar, buf); + g_assert_cmpint(buf[1], ==, cyls); + g_assert_cmpint(buf[3], ==, 16); + g_assert_cmpint(buf[4], ==, 512 * 63); + g_assert_cmpint(buf[6], ==, 63); + g_assert_cmpint(buf[55], ==, 8); + g_assert_cmpint(buf[56], ==, 32); + g_assert_cmpint(buf[57] | (buf[58] << 16), ==, cyls * 8 * 32); + + free_pci_device(dev); + ide_test_quit(qts); +} + +/* Words 3 and 6 keep the drive's own geometry even if built after a change */ +static void test_specify_identify_default(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uint16_t buf[256]; + + qts = ide_test_start( + "-blockdev driver=file,node-name=hda,filename=%s " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 ", + tmp_path[0]); + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + /* No IDENTIFY DEVICE before this one, so nothing was cached yet */ + ide_set_translation(dev, ide_bar, 8, 32); + ide_identify_words(dev, ide_bar, buf); + g_assert_cmpint(buf[3], ==, 16); + g_assert_cmpint(buf[4], ==, 512 * 63); + g_assert_cmpint(buf[6], ==, 63); + g_assert_cmpint(buf[55], ==, 8); + g_assert_cmpint(buf[56], ==, 32); + g_assert_cmpint(buf[57] | (buf[58] << 16), ==, buf[1] * 8 * 32); + + free_pci_device(dev); + ide_test_quit(qts); +} + +/* A hardware reset reverts the translation (ATA-5 9.1), SRST does not (9.2) */ +static void test_specify_reset(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar, ide_bar2; + uint16_t buf[256]; + char marker[9]; + + qts = ide_test_start( + "-blockdev driver=file,node-name=hda,filename=%s " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 ", + tmp_path[0]); + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + ide_bar2 = qpci_legacy_iomap(dev, IDE_BASE2); + + ide_prepare_markers(qts, dev, ide_bar); + ide_set_translation(dev, ide_bar, 8, 32); + ide_read_chs_marker(qts, dev, ide_bar, 0, 1, 1, marker); + g_assert_cmpstr(marker, ==, CHS_MARKER_CUSTOM); + + qpci_io_writeb(dev, ide_bar2, 0, IDE_CTRL_RESET); + qpci_io_writeb(dev, ide_bar2, 0, 0); + ide_wait_clear(qts, BSY); + + ide_identify_words(dev, ide_bar, buf); + g_assert_cmpint(buf[55], ==, 8); + g_assert_cmpint(buf[56], ==, 32); + ide_read_chs_marker(qts, dev, ide_bar, 0, 1, 1, marker); + g_assert_cmpstr(marker, ==, CHS_MARKER_CUSTOM); + + qtest_qmp_assert_success(qts, "{ 'execute': 'system_reset' }"); + qtest_qmp_eventwait(qts, "RESET"); + qpci_device_enable(dev); + + ide_identify_words(dev, ide_bar, buf); + g_assert_cmpint(buf[55], ==, 16); + g_assert_cmpint(buf[56], ==, 63); + ide_read_chs_marker(qts, dev, ide_bar, 0, 1, 1, marker); + g_assert_cmpstr(marker, ==, CHS_MARKER_DEFAULT); + + free_pci_device(dev); + ide_test_quit(qts); +} + static void test_cdrom_pio(void) { - cdrom_pio_impl(1); + cdrom_read_impl(1, CDROM_PIO); } static void test_cdrom_pio_large(void) { /* Test a few loops of the PIO DRQ mechanism. */ - cdrom_pio_impl(BYTE_COUNT_LIMIT * 4 / ATAPI_BLOCK_SIZE); + cdrom_read_impl(BYTE_COUNT_LIMIT * 4 / ATAPI_BLOCK_SIZE, CDROM_PIO); } - static void test_cdrom_dma(void) { - QTestState *qts; - static const size_t len = ATAPI_BLOCK_SIZE; - size_t ret; - char *pattern = g_malloc(ATAPI_BLOCK_SIZE * 16); - char *rx = g_malloc0(len); - uintptr_t guest_buf; - PrdtEntry prdt[1]; - FILE *fh; + cdrom_read_impl(1, CDROM_DMA); +} - qts = ide_test_start( - "-drive if=none,file=%s,media=cdrom,format=raw,id=sr0,index=0 " - "-device ide-cd,drive=sr0,bus=ide.0", tmp_path[0]); - qtest_irq_intercept_in(qts, "ioapic"); +static void test_cdrom_dma_large(void) +{ + cdrom_read_impl(BYTE_COUNT_LIMIT * 4 / ATAPI_BLOCK_SIZE, CDROM_DMA); +} - guest_buf = guest_alloc(&guest_malloc, len); - prdt[0].addr = cpu_to_le32(guest_buf); - prdt[0].size = cpu_to_le32(len | PRDT_EOT); +static void test_cdrom_pio_raw(void) +{ + cdrom_read_impl(4, CDROM_RAW); +} - generate_pattern(pattern, ATAPI_BLOCK_SIZE * 16, ATAPI_BLOCK_SIZE); - fh = fopen(tmp_path[0], "wb+"); - ret = fwrite(pattern, ATAPI_BLOCK_SIZE, 16, fh); - g_assert_cmpint(ret, ==, 16); - fclose(fh); - - send_dma_request(qts, CMD_PACKET, 0, 1, prdt, 1, send_scsi_cdb_read10); - - /* Read back data from guest memory into local qtest memory */ - qtest_memread(qts, guest_buf, rx, len); - g_assert_cmpint(memcmp(pattern, rx, len), ==, 0); - - g_free(pattern); - g_free(rx); - test_bmdma_teardown(qts); +static void test_cdrom_dma_raw(void) +{ + cdrom_read_impl(4, CDROM_DMA | CDROM_RAW); } int main(int argc, char **argv) @@ -1209,6 +1899,19 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); qtest_add_func("/ide/read_native", test_specify); + qtest_add_func("/ide/specify/zero_sectors", test_specify_zero_sectors); + qtest_add_func("/ide/specify/identify", test_specify_identify); + qtest_add_func("/ide/specify/identify_default", + test_specify_identify_default); + qtest_add_func("/ide/specify/reset", test_specify_reset); + qtest_add_func("/ide/migration/chs_translation", + test_migrate_chs_translation); + qtest_add_func("/ide/migration/chs_snapshot", test_migrate_chs_snapshot); + qtest_add_func("/ide/migration/chs_rejected", test_migrate_chs_rejected); + qtest_add_func("/ide/migration/pio_state_rejected", + test_migrate_pio_state_rejected); + qtest_add_func("/ide/identify/udma", test_identify_udma_ata); + qtest_add_func("/ide/identify/udma_atapi", test_identify_udma_atapi); qtest_add_func("/ide/identify", test_identify); @@ -1228,6 +1931,9 @@ int main(int argc, char **argv) qtest_add_func("/ide/cdrom/pio", test_cdrom_pio); qtest_add_func("/ide/cdrom/pio_large", test_cdrom_pio_large); qtest_add_func("/ide/cdrom/dma", test_cdrom_dma); + qtest_add_func("/ide/cdrom/dma_large", test_cdrom_dma_large); + qtest_add_func("/ide/cdrom/pio_raw", test_cdrom_pio_raw); + qtest_add_func("/ide/cdrom/dma_raw", test_cdrom_dma_raw); ret = g_test_run(); diff --git a/tests/qtest/k230-ddr-test.c b/tests/qtest/k230-ddr-test.c new file mode 100644 index 0000000000..72362929bc --- /dev/null +++ b/tests/qtest/k230-ddr-test.c @@ -0,0 +1,226 @@ +/* + * QTest testcase for the K230 DDR controller and PHY + * + * Exercises register access, reset, PHY ownership, training mailbox, and DFI + * initialization. + * + * Copyright (c) 2026 Junze Cao + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" + +#define K230_DDRC_BASE UINT64_C(0x98000000) +#define K230_DDR_PHY_BASE UINT64_C(0x9a000000) + +#define K230_DDRC_MSTR 0x000 +#define K230_DDRC_STAT 0x004 +#define K230_DDRC_DRAMTMG4 0x110 +#define K230_DDRC_DFIMISC 0x1b0 +#define K230_DDRC_DFISTAT 0x1bc +#define K230_DDRC_SWCTL 0x320 +#define K230_DDRC_SWSTAT 0x324 + +#define K230_DDRC_STAT_OPERATING_MODE_MASK 0x7 +#define K230_DDRC_DFISTAT_DFI_INIT_COMPLETE_MASK 0x1 + +#define K230_DDR_PHY_CSR(index) \ + (K230_DDR_PHY_BASE + (uint64_t)(index) * sizeof(uint32_t)) + +#define K230_DDR_PHY_ATX_IMPEDANCE 0x00043 +#define K230_DDR_PHY_TX_IMPEDANCE_CTRL1 0x10049 +#define K230_DDR_PHY_VREF_IN_GLOBAL 0x200b2 +#define K230_DDR_PHY_MICRO_CONT_MUX_SEL 0xd0000 +#define K230_DDR_PHY_TRAINING_STATUS 0xd0004 +#define K230_DDR_PHY_TRAINING_ACK 0xd0031 +#define K230_DDR_PHY_TRAINING_MESSAGE 0xd0032 +#define K230_DDR_PHY_TRAINING_TRIGGER 0xd0099 + +static void complete_phy_training(QTestState *qts) +{ + qtest_writel(qts, + K230_DDR_PHY_CSR(K230_DDR_PHY_MICRO_CONT_MUX_SEL), 1); + qtest_writel(qts, K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_TRIGGER), 9); + qtest_writel(qts, K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_TRIGGER), 1); + qtest_writel(qts, K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_TRIGGER), 0); +} + +static void test_reset_and_register_access(void) +{ + QTestState *qts = qtest_init("-machine k230"); + + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_MSTR), + ==, 0x01040000); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_STAT), + ==, 0x00000000); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_DRAMTMG4), + ==, 0x05040405); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_DFIMISC), + ==, 0x00000001); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_SWCTL), + ==, 0x00000001); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_SWSTAT), + ==, 0x00000001); + + g_assert_cmphex(qtest_readl(qts, + K230_DDR_PHY_CSR(K230_DDR_PHY_ATX_IMPEDANCE)), + ==, 0x03ff); + g_assert_cmphex(qtest_readl(qts, + K230_DDR_PHY_CSR(K230_DDR_PHY_TX_IMPEDANCE_CTRL1)), + ==, 0x0fff); + g_assert_cmphex(qtest_readl(qts, + K230_DDR_PHY_CSR(K230_DDR_PHY_VREF_IN_GLOBAL)), + ==, 0x0200); + + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_MSTR, 0x01040008); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_MSTR), + ==, 0x01040008); + + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_STAT, 0xffffffff); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_STAT), + ==, 0x00000000); + + qtest_writel(qts, K230_DDR_PHY_CSR(K230_DDR_PHY_VREF_IN_GLOBAL), + 0xffffffff); + g_assert_cmphex(qtest_readl(qts, + K230_DDR_PHY_CSR(K230_DDR_PHY_VREF_IN_GLOBAL)), + ==, 0x7fff); + + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_DRAMTMG4, 0); + + qtest_system_reset(qts); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_MSTR), + ==, 0x01040000); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_DRAMTMG4), + ==, 0x05040405); + g_assert_cmphex(qtest_readl(qts, + K230_DDR_PHY_CSR(K230_DDR_PHY_VREF_IN_GLOBAL)), + ==, 0x0200); + + qtest_quit(qts); +} + +static void test_software_update_handshake(void) +{ + QTestState *qts = qtest_init("-machine k230"); + + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_SWCTL, 0); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_SWSTAT), + ==, 0); + + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_SWCTL, 1); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_SWSTAT), + ==, 1); + + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_SWSTAT, 0); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_SWSTAT), + ==, 1); + + qtest_quit(qts); +} + +static void test_phy_ownership(void) +{ + QTestState *qts = qtest_init("-machine k230"); + uint64_t atx = K230_DDR_PHY_CSR(K230_DDR_PHY_ATX_IMPEDANCE); + uint64_t mux = K230_DDR_PHY_CSR(K230_DDR_PHY_MICRO_CONT_MUX_SEL); + + qtest_writel(qts, atx, 0x155); + g_assert_cmphex(qtest_readl(qts, atx), ==, 0x155); + + qtest_writel(qts, mux, 1); + qtest_writel(qts, atx, 0x2aa); + g_assert_cmphex(qtest_readl(qts, atx), ==, 0x155); + + qtest_writel(qts, mux, 0); + qtest_writel(qts, atx, 0x2aa); + g_assert_cmphex(qtest_readl(qts, atx), ==, 0x2aa); + + qtest_quit(qts); +} + +static void test_dfi_prerequisites(void) +{ + QTestState *qts = qtest_init("-machine k230"); + + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_DFIMISC, 0x20); + + g_assert_cmphex(qtest_readl(qts, + K230_DDRC_BASE + K230_DDRC_DFISTAT) & + K230_DDRC_DFISTAT_DFI_INIT_COMPLETE_MASK, + ==, 0); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_STAT) & + K230_DDRC_STAT_OPERATING_MODE_MASK, + ==, 0); + + qtest_quit(qts); +} + +static void test_dfi_complete_enable(void) +{ + QTestState *qts = qtest_init("-machine k230"); + + complete_phy_training(qts); + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_DFIMISC, 0x20); + + g_assert_cmphex(qtest_readl(qts, + K230_DDRC_BASE + K230_DDRC_DFISTAT) & + K230_DDRC_DFISTAT_DFI_INIT_COMPLETE_MASK, + ==, 1); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_STAT) & + K230_DDRC_STAT_OPERATING_MODE_MASK, + ==, 0); + + qtest_quit(qts); +} + +static void test_training_and_dfi_handshake(void) +{ + QTestState *qts = qtest_init("-machine k230"); + + complete_phy_training(qts); + + g_assert_cmphex(qtest_readl(qts, + K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_STATUS)), + ==, 0); + g_assert_cmphex(qtest_readl(qts, + K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_MESSAGE)), + ==, 0x07); + + qtest_writel(qts, K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_ACK), 0); + g_assert_cmphex(qtest_readl(qts, + K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_STATUS)), + ==, 1); + qtest_writel(qts, K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_ACK), 1); + + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_DFIMISC, 0x20); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_DFISTAT), + ==, 1); + + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_DFIMISC, 0); + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_DFIMISC, 1); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_STAT) & 0x7, + ==, 1); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/k230-ddr/reset-and-register-access", + test_reset_and_register_access); + qtest_add_func("/k230-ddr/software-update-handshake", + test_software_update_handshake); + qtest_add_func("/k230-ddr/phy-ownership", test_phy_ownership); + qtest_add_func("/k230-ddr/dfi-prerequisites", test_dfi_prerequisites); + qtest_add_func("/k230-ddr/dfi-complete-enable", + test_dfi_complete_enable); + qtest_add_func("/k230-ddr/training-and-dfi-handshake", + test_training_and_dfi_handshake); + + return g_test_run(); +} diff --git a/tests/qtest/k230-decomp-gzip-test.c b/tests/qtest/k230-decomp-gzip-test.c new file mode 100644 index 0000000000..e80113fd28 --- /dev/null +++ b/tests/qtest/k230-decomp-gzip-test.c @@ -0,0 +1,200 @@ +/* + * QTest testcase for K230 GZIP decompression engine + * + * Copyright (c) 2026 Tao Ding + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "libqtest.h" +#include "hw/dma/k230_gsdma.h" +#include "hw/misc/k230_decomp_gzip.h" + +#define K230_DECOMP_GZIP_BASE 0x80808000 +#define K230_GSDMA_BASE 0x80800000 +#define K230_SRAM_BASE 0x80200000 +#define TEST_LLT_ADDR0 0x01000000 +#define TEST_LLT_ADDR1 0x01000040 +#define TEST_SRC_ADDR 0x01100000 +#define TEST_DST_ADDR 0x01200000 + +#define TEST_PAYLOAD_LEN (sizeof(test_payload) - 1) + +static inline uint64_t gzip_reg(hwaddr off) +{ + return K230_DECOMP_GZIP_BASE + off; +} + +static inline uint64_t gsdma_reg(hwaddr off) +{ + return K230_GSDMA_BASE + off; +} + +static inline uint64_t gsdma_ch_reg(unsigned int ch, hwaddr off) +{ + return K230_GSDMA_BASE + K230_GSDMA_CH_BASE + + ch * K230_GSDMA_CH_STRIDE + off; +} + +static inline hwaddr k230_sram_addr(hwaddr off) +{ + return K230_SRAM_BASE + off; +} + +static inline hwaddr k230_sram_input_addr(unsigned int slot) +{ + return k230_sram_addr(K230_DECOMP_GZIP_SRAM_IN_BASE + + slot * K230_DECOMP_GZIP_BLOCK_SIZE); +} + +/* + * test_gzip_data was generated by compressing test_payload in gzip format + * using Dynamic Huffman coding. The compression method byte in the gzip + * header was then changed from the standard value 0x08 to the K230-specific + * value 0x09. + */ +static const uint8_t test_gzip_data[] = { + 0x1f, 0x8b, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x03, 0x05, 0xc1, 0xb1, 0x0d, 0x00, 0x20, + 0x08, 0x04, 0xc0, 0x9e, 0x29, 0x7e, 0x04, 0x23, + 0x63, 0x38, 0x85, 0x05, 0x21, 0x14, 0x10, 0x23, + 0x5f, 0x39, 0xbd, 0x77, 0x6b, 0xea, 0x80, 0xbf, + 0x38, 0x68, 0x5e, 0xdb, 0x19, 0xe5, 0xa0, 0x35, + 0x65, 0x4d, 0x1d, 0xf0, 0x17, 0x07, 0xcd, 0x6b, + 0x3b, 0xa3, 0x1c, 0xb4, 0xa6, 0xac, 0xa9, 0x03, + 0xfe, 0xe2, 0xa0, 0x79, 0x6d, 0x67, 0x94, 0x83, + 0xd6, 0x94, 0x35, 0x75, 0xc0, 0x5f, 0x1c, 0x34, + 0xaf, 0xed, 0x8c, 0x72, 0xd0, 0x9a, 0xb2, 0xa6, + 0x0e, 0xf8, 0x8b, 0x83, 0xe6, 0xb5, 0x9d, 0x51, + 0x0e, 0x5a, 0x53, 0xd6, 0xd4, 0x01, 0x7f, 0x71, + 0xd0, 0xbc, 0xb6, 0x33, 0xca, 0x41, 0x6b, 0xca, + 0x9a, 0x3a, 0xe0, 0x2f, 0x0e, 0x9a, 0xd7, 0x76, + 0x46, 0x39, 0x68, 0x4d, 0x59, 0x53, 0x07, 0xfc, + 0xc5, 0x41, 0xf3, 0xda, 0xce, 0x28, 0x07, 0xad, + 0x29, 0x1f, 0xdb, 0x9d, 0xbc, 0xdd, 0xc8, 0x00, + 0x00, 0x00, +}; + +static const uint8_t test_payload[] = + "K230 gzip streaming test\n" + "K230 gzip streaming test\n" + "K230 gzip streaming test\n" + "K230 gzip streaming test\n" + "K230 gzip streaming test\n" + "K230 gzip streaming test\n" + "K230 gzip streaming test\n" + "K230 gzip streaming test\n"; + +static void write_sdma_llt_node(QTestState *qts, hwaddr addr, uint32_t src, + uint32_t dst, uint32_t len, uint32_t next) +{ + qtest_writel(qts, addr + offsetof(K230GSDMALLT, cfg), 0); + qtest_writel(qts, addr + offsetof(K230GSDMALLT, src_addr), src); + qtest_writel(qts, addr + offsetof(K230GSDMALLT, line_size), len); + qtest_writel(qts, addr + offsetof(K230GSDMALLT, line_cfg), 0x1); + qtest_writel(qts, addr + offsetof(K230GSDMALLT, dst_addr), dst); + qtest_writel(qts, addr + offsetof(K230GSDMALLT, next_llt_addr), next); +} + +static void test_reset_and_rw(void) +{ + QTestState *qts = qtest_init("-machine k230"); + + g_assert_cmphex(qtest_readl(qts, gzip_reg(K230_DECOMP_GZIP_DECOMP_START)), + ==, 0); + g_assert_cmphex(qtest_readl(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_SRC_SIZE)), + ==, 0); + g_assert_cmphex(qtest_readl(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_OUT_SIZE)), + ==, 0); + g_assert_cmphex(qtest_readl(qts, gzip_reg(K230_DECOMP_GZIP_DECOMP_STAT)), + ==, 0); + + qtest_writel(qts, gzip_reg(K230_DECOMP_GZIP_DECOMP_START), + K230_DECOMP_GZIP_START); + + qtest_writel(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_SRC_SIZE), 0xffffffff); + g_assert_cmphex(qtest_readl(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_SRC_SIZE)), + ==, 0xffffffff); + + qtest_writel(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_OUT_SIZE), 0x12345678); + g_assert_cmphex(qtest_readl(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_OUT_SIZE)), + ==, 0x12345678); + + qtest_quit(qts); +} + +static void test_gsdma_handshake_flow(void) +{ + QTestState *qts = qtest_init("-machine k230"); + uint32_t stat; + uint8_t output[TEST_PAYLOAD_LEN]; + + qtest_memwrite(qts, TEST_SRC_ADDR, test_gzip_data, sizeof(test_gzip_data)); + write_sdma_llt_node(qts, TEST_LLT_ADDR0, TEST_SRC_ADDR, + k230_sram_input_addr(0), + sizeof(test_gzip_data), 0); + write_sdma_llt_node(qts, TEST_LLT_ADDR1, + k230_sram_addr(K230_DECOMP_GZIP_SRAM_OUT_BASE), + TEST_DST_ADDR, TEST_PAYLOAD_LEN, 0); + + qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_CH_EN), BIT(0) | BIT(1)); + qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CFG), + K230_GSDMA_CH0_CFG_DECOMP_CTRL_EN); + qtest_writel(qts, gsdma_ch_reg(1, K230_GSDMA_CH_CFG), 0); + qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_LLT_SADDR), TEST_LLT_ADDR0); + qtest_writel(qts, gsdma_ch_reg(1, K230_GSDMA_CH_LLT_SADDR), TEST_LLT_ADDR1); + qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CTL), K230_GSDMA_CTL_START); + qtest_writel(qts, gsdma_ch_reg(1, K230_GSDMA_CH_CTL), K230_GSDMA_CTL_START); + + g_assert_cmphex(qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_CH_EN)), + ==, BIT(0) | BIT(1)); + g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CFG)) & + K230_GSDMA_CH0_CFG_DECOMP_CTRL_EN, + ==, K230_GSDMA_CH0_CFG_DECOMP_CTRL_EN); + g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_LLT_SADDR)), + ==, TEST_LLT_ADDR0); + g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(1, K230_GSDMA_CH_LLT_SADDR)), + ==, TEST_LLT_ADDR1); + + qtest_writel(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_SRC_SIZE), + K230_DECOMP_GZIP_CTRL_EN | sizeof(test_gzip_data)); + qtest_writel(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_OUT_SIZE), + TEST_PAYLOAD_LEN); + qtest_writel(qts, gzip_reg(K230_DECOMP_GZIP_DECOMP_START), + K230_DECOMP_GZIP_START); + + stat = qtest_readl(qts, gzip_reg(K230_DECOMP_GZIP_DECOMP_STAT)); + g_assert_cmphex(stat & K230_DECOMP_GZIP_STAT_CRC_OK, ==, + K230_DECOMP_GZIP_STAT_CRC_OK); + + g_assert_cmphex(qtest_readl(qts, + gsdma_ch_reg(0, K230_GSDMA_CH_CURRENT_LLT)), + ==, TEST_LLT_ADDR0); + g_assert_cmphex(qtest_readl(qts, + gsdma_ch_reg(1, K230_GSDMA_CH_CURRENT_LLT)), + ==, TEST_LLT_ADDR1); + + stat = qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_INT_STAT)); + g_assert_cmphex(stat & K230_GSDMA_SDMA_DONE_INT(0), ==, + K230_GSDMA_SDMA_DONE_INT(0)); + g_assert_cmphex(stat & K230_GSDMA_SDMA_DONE_INT(1), ==, + K230_GSDMA_SDMA_DONE_INT(1)); + + qtest_memread(qts, TEST_DST_ADDR, output, sizeof(output)); + g_assert_cmpmem(output, sizeof(output), test_payload, TEST_PAYLOAD_LEN); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/k230-decomp-gzip/reset-and-rw", test_reset_and_rw); + qtest_add_func("/k230-decomp-gzip/gsdma-handshake-flow", + test_gsdma_handshake_flow); + + return g_test_run(); +} diff --git a/tests/qtest/k230-gsdma-test.c b/tests/qtest/k230-gsdma-test.c new file mode 100644 index 0000000000..44d4248739 --- /dev/null +++ b/tests/qtest/k230-gsdma-test.c @@ -0,0 +1,201 @@ +/* + * QTest testcase for K230 GSDMA + * + * Copyright (c) 2026 Tao Ding + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "exec/hwaddr.h" +#include "hw/dma/k230_gsdma.h" +#include "libqtest.h" + +#define K230_GSDMA_BASE 0x80800000 +#define TEST_LLT_ADDR0 0x10000 +#define TEST_LLT_ADDR1 0x10040 +#define TEST_LLT_ADDR2 0x10080 + +#define TEST_SRC_ADDR0 0x20000 +#define TEST_SRC_ADDR1 0x22000 +#define TEST_SRC_ADDR2 0x23000 +#define TEST_DST_ADDR0 0x30000 +#define TEST_DST_ADDR1 0x32000 +#define TEST_DST_ADDR2 0x33000 + +static inline uint64_t gsdma_reg(hwaddr off) +{ + return K230_GSDMA_BASE + off; +} + +static inline uint64_t gsdma_ch_reg(unsigned int ch, hwaddr off) +{ + return K230_GSDMA_BASE + K230_GSDMA_CH_BASE + + ch * K230_GSDMA_CH_STRIDE + off; +} + +static void write_sdma_llt_node_full(QTestState *qts, hwaddr addr, + uint32_t cfg, hwaddr src, + uint32_t size, hwaddr dst, + hwaddr next) +{ + qtest_writel(qts, addr + offsetof(K230GSDMALLT, cfg), cfg); + qtest_writel(qts, addr + offsetof(K230GSDMALLT, src_addr), src); + qtest_writel(qts, addr + offsetof(K230GSDMALLT, line_size), size); + qtest_writel(qts, addr + offsetof(K230GSDMALLT, line_cfg), 0x1); + qtest_writel(qts, addr + offsetof(K230GSDMALLT, dst_addr), dst); + qtest_writel(qts, addr + offsetof(K230GSDMALLT, next_llt_addr), next); +} + +static void write_sdma_llt_node(QTestState *qts, hwaddr addr, uint32_t cfg, + uint32_t next) +{ + write_sdma_llt_node_full(qts, addr, cfg, 0x11110000, 0x200, + 0x22220000, next); +} + +static void fill_pattern(uint8_t *buf, size_t size, uint8_t seed) +{ + for (size_t i = 0; i < size; i++) { + buf[i] = seed + i * 37; + } +} + +static void test_global_registers(void) +{ + QTestState *qts = qtest_init("-machine k230"); + + g_assert_cmphex(qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_CFG)), ==, + K230_GSDMA_DMA_CFG_RESET); + + qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_CH_EN), 0xff); + g_assert_cmphex(qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_CH_EN)), ==, + K230_GSDMA_DMA_CH_EN_MASK); + + qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_INT_MASK), 0x12345); + g_assert_cmphex(qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_INT_MASK)), ==, + 0x12345); + + qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_CFG), 0xa5a5a5a5); + g_assert_cmphex(qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_CFG)), ==, + 0xa5a5a5a5); + + qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_WEIGHT), 0xff55aa55); + g_assert_cmphex(qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_WEIGHT)), ==, + 0x55aa55); + + qtest_quit(qts); +} + +static void test_sdma_pause_resume(void) +{ + uint32_t int_stat; + QTestState *qts = qtest_init("-machine k230"); + + write_sdma_llt_node(qts, TEST_LLT_ADDR0, K230_GSDMA_LLT_PAUSE, + TEST_LLT_ADDR1); + write_sdma_llt_node(qts, TEST_LLT_ADDR1, K230_GSDMA_LLT_NODE_INTR, 0); + + qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_CH_EN), BIT(0)); + qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_LLT_SADDR), TEST_LLT_ADDR0); + qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CTL), K230_GSDMA_CTL_START); + + g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_STATUS)), ==, + K230_GSDMA_SDMA_STATUS_PAUSE); + g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CURRENT_LLT)), + ==, TEST_LLT_ADDR0); + + int_stat = qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_INT_STAT)); + g_assert_cmphex(int_stat & K230_GSDMA_SDMA_PAUSE_INT(0), ==, + K230_GSDMA_SDMA_PAUSE_INT(0)); + g_assert_cmphex(int_stat & K230_GSDMA_SDMA_DONE_INT(0), ==, 0); + + qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_INT_STAT), int_stat); + qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CTL), K230_GSDMA_CTL_RESUME); + + g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_STATUS)), ==, + 0); + g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CURRENT_LLT)), + ==, TEST_LLT_ADDR1); + + int_stat = qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_INT_STAT)); + g_assert_cmphex(int_stat & K230_GSDMA_SDMA_DONE_INT(0), ==, + K230_GSDMA_SDMA_DONE_INT(0)); + g_assert_cmphex(int_stat & K230_GSDMA_SDMA_ITEM_INT(0), ==, + K230_GSDMA_SDMA_ITEM_INT(0)); + + qtest_quit(qts); +} + +static void test_sdma_three_llt_copy(void) +{ + uint8_t src0[0x1000]; + uint8_t src1[1]; + uint8_t src2[511]; + uint8_t dst0[sizeof(src0)]; + uint8_t dst1[sizeof(src1)]; + uint8_t dst2[sizeof(src2)]; + uint32_t int_stat; + QTestState *qts = qtest_init("-machine k230"); + + fill_pattern(src0, sizeof(src0), 0x10); + fill_pattern(src1, sizeof(src1), 0x31); + fill_pattern(src2, sizeof(src2), 0x52); + memset(dst0, 0xa5, sizeof(dst0)); + memset(dst1, 0xa5, sizeof(dst1)); + memset(dst2, 0xa5, sizeof(dst2)); + + qtest_memwrite(qts, TEST_SRC_ADDR0, src0, sizeof(src0)); + qtest_memwrite(qts, TEST_SRC_ADDR1, src1, sizeof(src1)); + qtest_memwrite(qts, TEST_SRC_ADDR2, src2, sizeof(src2)); + qtest_memwrite(qts, TEST_DST_ADDR0, dst0, sizeof(dst0)); + qtest_memwrite(qts, TEST_DST_ADDR1, dst1, sizeof(dst1)); + qtest_memwrite(qts, TEST_DST_ADDR2, dst2, sizeof(dst2)); + + write_sdma_llt_node_full(qts, TEST_LLT_ADDR0, 0, TEST_SRC_ADDR0, + sizeof(src0), TEST_DST_ADDR0, TEST_LLT_ADDR1); + write_sdma_llt_node_full(qts, TEST_LLT_ADDR1, 0, TEST_SRC_ADDR1, + sizeof(src1), TEST_DST_ADDR1, TEST_LLT_ADDR2); + write_sdma_llt_node_full(qts, TEST_LLT_ADDR2, 0, TEST_SRC_ADDR2, + sizeof(src2), TEST_DST_ADDR2, 0); + + qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_CH_EN), BIT(0)); + qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_LLT_SADDR), TEST_LLT_ADDR0); + qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CTL), K230_GSDMA_CTL_START); + + g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_STATUS)), ==, + 0); + g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CURRENT_LLT)), + ==, TEST_LLT_ADDR2); + + int_stat = qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_INT_STAT)); + g_assert_cmphex(int_stat & K230_GSDMA_SDMA_DONE_INT(0), ==, + K230_GSDMA_SDMA_DONE_INT(0)); + g_assert_cmphex(int_stat & K230_GSDMA_SDMA_PAUSE_INT(0), ==, 0); + + memset(dst0, 0, sizeof(dst0)); + memset(dst1, 0, sizeof(dst1)); + memset(dst2, 0, sizeof(dst2)); + qtest_memread(qts, TEST_DST_ADDR0, dst0, sizeof(dst0)); + qtest_memread(qts, TEST_DST_ADDR1, dst1, sizeof(dst1)); + qtest_memread(qts, TEST_DST_ADDR2, dst2, sizeof(dst2)); + + g_assert_cmpmem(dst0, sizeof(dst0), src0, sizeof(src0)); + g_assert_cmpmem(dst1, sizeof(dst1), src1, sizeof(src1)); + g_assert_cmpmem(dst2, sizeof(dst2), src2, sizeof(src2)); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/k230-gsdma/global-registers", test_global_registers); + qtest_add_func("/k230-gsdma/sdma-pause-resume", test_sdma_pause_resume); + qtest_add_func("/k230-gsdma/sdma-three-llt-copy", + test_sdma_three_llt_copy); + + return g_test_run(); +} diff --git a/tests/qtest/k230-wdt-test.c b/tests/qtest/k230-wdt-test.c index c8eaeaf1ae..b1131f42e3 100644 --- a/tests/qtest/k230-wdt-test.c +++ b/tests/qtest/k230-wdt-test.c @@ -76,7 +76,7 @@ static void test_counter_restart(void) static void test_interrupt_mode(void) { - QTestState *qts = qtest_init("-machine k230 --trace k230_*,file=k230.log"); + QTestState *qts = qtest_init("-machine k230"); /* Set interrupt mode and enable watchdog */ qtest_writel(qts, WDT_BASE + K230_WDT_CR, diff --git a/tests/qtest/l2vic-test.c b/tests/qtest/l2vic-test.c new file mode 100644 index 0000000000..adb4dc7281 --- /dev/null +++ b/tests/qtest/l2vic-test.c @@ -0,0 +1,249 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + * + * QTest testcase for the L2VIC Interrupt Controller + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" +#include "hw/hexagon/hexagon.h" + +#include "hw/hexagon/machine_cfg_v66g_1024.h.inc" +#include "hw/hexagon/machine_cfg_v68n_1024.h.inc" + +/* L2VIC register offsets exercised by this test */ +#define L2VIC_INT_ENABLEn 0x100 /* Read/Write */ +#define L2VIC_INT_ENABLE_CLEARn 0x180 /* Write */ +#define L2VIC_INT_ENABLE_SETn 0x200 /* Write */ +#define L2VIC_INT_TYPEn 0x280 /* Read/Write */ +#define L2VIC_INT_STATUSn 0x380 /* Read */ +#define L2VIC_INT_CLEARn 0x400 /* Write */ +#define L2VIC_SOFT_INTn 0x480 /* Write */ +#define L2VIC_INT_PENDINGn 0x500 /* Read */ +#define L2VIC_INT_GRPn_0 0x600 /* Read/Write */ +#define L2VIC_INT_GRPn_1 0x680 /* Read/Write */ +#define L2VIC_INT_GRPn_2 0x700 /* Read/Write */ +#define L2VIC_INT_GRPn_3 0x780 /* Read/Write */ + +/* + * VID group readback: records which irq last fired through each VID + * group. Outputs themselves are momentary pulses (see l2vic_update()), + * so these registers -- not the qtest IRQ level snapshot -- are how the + * test observes VID steering. + */ +#define L2VIC_VID_GRP_0 0x0 +#define L2VIC_VID_GRP_1 0x4 +#define L2VIC_VID_GRP_2 0x8 +#define L2VIC_VID_GRP_3 0xC + +typedef struct { + const char *machine; + const struct hexagon_machine_config *cfg; +} L2VICMachineCfg; + +static const L2VICMachineCfg l2vic_machines[] = { + { "virt", &v68n_1024 }, + { "V66G_1024", &v66g_1024 }, +}; + +static uint32_t l2vic_read32(uint64_t base, uint32_t offset) +{ + return readl(base + offset); +} + +static void l2vic_write32(uint64_t base, uint32_t offset, uint32_t value) +{ + writel(base + offset, value); +} + +static void test_l2vic_register_access(uint64_t base) +{ + uint32_t val; + + l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0x1); + val = l2vic_read32(base, L2VIC_INT_ENABLEn); + g_assert_cmpuint(val & 0x1, ==, 0x1); + + l2vic_write32(base, L2VIC_INT_ENABLE_CLEARn, 0x1); + val = l2vic_read32(base, L2VIC_INT_ENABLEn); + g_assert_cmpuint(val & 0x1, ==, 0x0); +} + +static void test_l2vic_interrupt_enable(uint64_t base) +{ + uint32_t val; + + val = l2vic_read32(base, L2VIC_INT_ENABLEn); + g_assert_cmpuint(val, ==, 0); + + /* Enable IRQ 0 and 2 */ + l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0x5); + val = l2vic_read32(base, L2VIC_INT_ENABLEn); + g_assert_cmpuint(val & 0x5, ==, 0x5); + + /* Disable IRQ 0, leaving IRQ 2 enabled */ + l2vic_write32(base, L2VIC_INT_ENABLE_CLEARn, 0x1); + val = l2vic_read32(base, L2VIC_INT_ENABLEn); + g_assert_cmpuint(val & 0x1, ==, 0x0); + g_assert_cmpuint(val & 0x4, ==, 0x4); +} + +static void test_l2vic_basic_functionality(uint64_t base) +{ + l2vic_read32(base, L2VIC_INT_ENABLEn); + l2vic_read32(base, L2VIC_INT_PENDINGn); + l2vic_read32(base, L2VIC_INT_STATUSn); + l2vic_read32(base, L2VIC_INT_TYPEn); + + l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0); + l2vic_write32(base, L2VIC_INT_ENABLE_CLEARn, 0); +} + +/* + * IRQs 0-7 pack their group-enable/VID-select nibbles into + * L2VIC_INT_GRPn_0 (int_group_n[0]), 4 bits per irq: bit 3 enables + * VID steering, bits 0-2 select the VID group (0-3), which pulses + * output line vid+2. + */ +static void l2vic_set_vid_group(uint64_t base, int irq, int vid) +{ + uint32_t val = l2vic_read32(base, L2VIC_INT_GRPn_0); + uint32_t nibble = 0x8 | (vid & 0x7); + + val &= ~(0xFu << (irq * 4)); + val |= nibble << (irq * 4); + l2vic_write32(base, L2VIC_INT_GRPn_0, val); +} + +static void test_l2vic_irq_outputs(uint64_t base) +{ + uint32_t val; + + l2vic_write32(base, L2VIC_INT_ENABLE_CLEARn, 0xFFFFFFFF); + l2vic_write32(base, L2VIC_INT_CLEARn, 0xFFFFFFFF); + l2vic_write32(base, L2VIC_INT_TYPEn, 0); + l2vic_write32(base, L2VIC_INT_GRPn_0, 0); + + /* Group 0 / IRQ2: soft interrupts require edge-triggered config */ + l2vic_write32(base, L2VIC_INT_TYPEn, 0x1); + l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0x1); + l2vic_write32(base, L2VIC_SOFT_INTn, 0x1); + + val = l2vic_read32(base, L2VIC_INT_STATUSn); + g_assert_cmpuint(val & 0x1, ==, 0x1); + /* Default VID group (0) records the delivering irq, output line 2 */ + g_assert_cmpuint(l2vic_read32(base, L2VIC_VID_GRP_0), ==, 0); + + l2vic_write32(base, L2VIC_INT_CLEARn, 0x1); + val = l2vic_read32(base, L2VIC_INT_STATUSn); + g_assert_cmpuint(val & 0x1, ==, 0x0); + + /* IRQ1 steered to VID group 1 -> output line 3 */ + l2vic_write32(base, L2VIC_INT_TYPEn, 0x2); + l2vic_set_vid_group(base, 1, 1); + l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0x2); + l2vic_write32(base, L2VIC_SOFT_INTn, 0x2); + + val = l2vic_read32(base, L2VIC_INT_STATUSn); + g_assert_cmpuint(val & 0x2, ==, 0x2); + g_assert_cmpuint(l2vic_read32(base, L2VIC_VID_GRP_1), ==, 1); + + l2vic_write32(base, L2VIC_INT_CLEARn, 0x2); + + /* IRQ4 steered to VID group 2 -> output line 4 */ + l2vic_write32(base, L2VIC_INT_TYPEn, 0x10); + l2vic_set_vid_group(base, 4, 2); + l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0x10); + l2vic_write32(base, L2VIC_SOFT_INTn, 0x10); + + val = l2vic_read32(base, L2VIC_INT_STATUSn); + g_assert_cmpuint(val & 0x10, ==, 0x10); + g_assert_cmpuint(l2vic_read32(base, L2VIC_VID_GRP_2), ==, 4); + + l2vic_write32(base, L2VIC_INT_CLEARn, 0x10); + + /* IRQ5 steered to VID group 3 -> output line 5 */ + l2vic_write32(base, L2VIC_INT_TYPEn, 0x20); + l2vic_set_vid_group(base, 5, 3); + l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0x20); + l2vic_write32(base, L2VIC_SOFT_INTn, 0x20); + + val = l2vic_read32(base, L2VIC_INT_STATUSn); + g_assert_cmpuint(val & 0x20, ==, 0x20); + g_assert_cmpuint(l2vic_read32(base, L2VIC_VID_GRP_3), ==, 5); + + l2vic_write32(base, L2VIC_INT_CLEARn, 0x20); + + /* Restore defaults; the block below reuses IRQ 3-5 without VID steering */ + l2vic_write32(base, L2VIC_INT_GRPn_0, 0); + l2vic_write32(base, L2VIC_INT_TYPEn, 0); + + /* Multiple pending: at most one active at a time */ + l2vic_write32(base, L2VIC_INT_TYPEn, 0xF); + l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0xF); + l2vic_write32(base, L2VIC_SOFT_INTn, 0xF); + + val = l2vic_read32(base, L2VIC_INT_STATUSn); + g_assert_cmpuint(val & 0xF, !=, 0x0); + + /* + * Only one irq becomes active per delivery; each clear unblocks the + * next pending one, so drain until all four have been delivered. + */ + while ((val = l2vic_read32(base, L2VIC_INT_STATUSn)) & 0xF) { + l2vic_write32(base, L2VIC_INT_CLEARn, val & 0xF); + } + + /* Level-triggered sources ignore soft interrupts */ + l2vic_write32(base, L2VIC_INT_TYPEn, 0x0); + l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0x20); + l2vic_write32(base, L2VIC_SOFT_INTn, 0x20); + + val = l2vic_read32(base, L2VIC_INT_STATUSn); + g_assert_cmpuint(val & 0x20, ==, 0x0); + + /* Same source, now edge-triggered, does fire */ + l2vic_write32(base, L2VIC_INT_TYPEn, 0x20); + l2vic_write32(base, L2VIC_SOFT_INTn, 0x20); + val = l2vic_read32(base, L2VIC_INT_STATUSn); + g_assert_cmpuint(val & 0x20, ==, 0x20); + + l2vic_write32(base, L2VIC_INT_ENABLE_CLEARn, 0xFFFFFFFF); + l2vic_write32(base, L2VIC_INT_CLEARn, 0xFFFFFFFF); + l2vic_write32(base, L2VIC_INT_GRPn_0, 0); + l2vic_write32(base, L2VIC_INT_GRPn_1, 0); + l2vic_write32(base, L2VIC_INT_GRPn_2, 0); + l2vic_write32(base, L2VIC_INT_GRPn_3, 0); +} + +static void test_l2vic_on_machine(gconstpointer data) +{ + const L2VICMachineCfg *mc = data; + g_autofree char *args = g_strdup_printf("-machine %s", mc->machine); + uint64_t base = mc->cfg->l2vic_base; + + qtest_start(args); + + test_l2vic_register_access(base); + test_l2vic_interrupt_enable(base); + test_l2vic_basic_functionality(base); + test_l2vic_irq_outputs(base); + + qtest_end(); +} + +int main(int argc, char **argv) +{ + size_t i; + + g_test_init(&argc, &argv, NULL); + + for (i = 0; i < ARRAY_SIZE(l2vic_machines); i++) { + g_autofree char *path = g_strdup_printf("/l2vic/%s/all-tests", + l2vic_machines[i].machine); + qtest_add_data_func(path, &l2vic_machines[i], test_l2vic_on_machine); + } + + return g_test_run(); +} diff --git a/tests/qtest/launchupdate-test.c b/tests/qtest/launchupdate-test.c new file mode 100644 index 0000000000..225c843df5 --- /dev/null +++ b/tests/qtest/launchupdate-test.c @@ -0,0 +1,625 @@ +/* + * vmlaunchupdate device fwcfg test. + * + * Copyright (c) 2026 Red Hat, Inc. + * + * Author: + * Ani Sinha + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqos/libqos-pc.h" +#include "libqtest.h" +#include "standard-headers/linux/qemu_fw_cfg.h" +#include "libqos/fw_cfg.h" +#include "qemu/bswap.h" +#include "hw/misc/vmlaunchupdate.h" + +#define WAIT_SEC 10 +static bool debug; +static bool trace; +static bool confidential; + +static void test_vm_launch_update_capability(void) +{ + QFWCFG *fw_cfg; + QTestState *s; + VMLaunchUpdate launch_update; + size_t filesize; + uint64_t capabilities; + + if (!qtest_has_device("vm-launch-update")) { + g_test_skip("Device vm-launch-update is not available"); + return; + } + + s = qtest_init("-device vm-launch-update"); + fw_cfg = pc_fw_cfg_init(s); + + filesize = qfw_cfg_get_file(fw_cfg, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + capabilities = le64_to_cpu(launch_update.capabilities); + g_assert_cmpint(capabilities, ==, VM_LAUNCHUPDATE_FORMAT_IGVM); + pc_fw_cfg_uninit(fw_cfg); + qtest_quit(s); +} + + +static void test_vm_launch_update_disable(void) +{ + QFWCFG *fw_cfg; + QOSState *qs; + VMLaunchUpdate launch_update; + uint64_t control; + size_t filesize; + + if (!qtest_has_device("vm-launch-update")) { + g_test_skip("Device vm-launch-update is not available"); + return; + } + + /* use default accelerator */ + qs = qtest_pc_boot("-device vm-launch-update"); + + fw_cfg = pc_fw_cfg_init(qs->qts); + + filesize = qfw_cfg_get_file(fw_cfg, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + control = le64_to_cpu(launch_update.control); + g_assert_cmpint(VM_LAUNCHUPDATE_CTL_DISABLE & control, ==, 0); + + /* disable the device */ + memset(&launch_update, 0, sizeof(launch_update)); + launch_update.control |= VM_LAUNCHUPDATE_CTL_DISABLE; + + filesize = qfw_cfg_write_file(fw_cfg, qs, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + + /* try to clear the dsable flag */ + memset(&launch_update, 0, sizeof(launch_update)); + + filesize = qfw_cfg_write_file(fw_cfg, qs, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + + /* check if the device is still disabled */ + filesize = qfw_cfg_get_file(fw_cfg, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + control = le64_to_cpu(launch_update.control); + g_assert_cmpint(VM_LAUNCHUPDATE_CTL_DISABLE & control, ==, 1); + + pc_fw_cfg_uninit(fw_cfg); + qtest_shutdown(qs); +} + +static void check_error(void) +{ + QFWCFG *fw_cfg; + QOSState *qs; + VMLaunchUpdate launch_update; + uint16_t status; + size_t filesize; + + if (!qtest_has_device("vm-launch-update")) { + g_test_skip("Device vm-launch-update is not available"); + return; + } + + /* guest not started with IGVM and with default accelerator */ + qs = qtest_pc_boot("-device vm-launch-update"); + + fw_cfg = pc_fw_cfg_init(qs->qts); + + memset(&launch_update, 0, sizeof(launch_update)); + launch_update.fw_image_size = 50; + launch_update.fw_image_addr = cpu_to_le64(0xdeadbeef); + launch_update.control |= VM_LAUNCHUPDATE_FORMAT_IGVM; + + filesize = qfw_cfg_write_file(fw_cfg, qs, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + + memset(&launch_update, 0, sizeof(launch_update)); + filesize = qfw_cfg_get_file(fw_cfg, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + status = le64_to_cpu(launch_update.status); + /* should fail with NOT_IGVM_INIT */ + g_assert_cmpint(status, ==, VM_LAUNCHUPDATE_NOT_IGVM_INIT); + + memset(&launch_update, 0, sizeof(launch_update)); + launch_update.fw_image_size = 50; + launch_update.fw_image_addr = cpu_to_le64(0xdeadbeef); + /* control set to 0, not VM_LAUNCHUPDATE_FORMAT_IGVM */ + + filesize = qfw_cfg_write_file(fw_cfg, qs, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + + memset(&launch_update, 0, sizeof(launch_update)); + filesize = qfw_cfg_get_file(fw_cfg, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + status = le64_to_cpu(launch_update.status); + /* should fail with LOAD_FAIL since it was not IGVM format */ + g_assert_cmpint(status, ==, VM_LAUNCHUPDATE_LOAD_FAIL); +} + +static int64_t get_image_size(const char *filename) +{ + int fd; + int64_t size; + fd = open(filename, O_RDONLY | O_BINARY); + g_assert_true(fd > 0); + size = lseek(fd, 0, SEEK_END); + close(fd); + return size; +} + +static ssize_t load_image(const char *igvm_f, void **addr, size_t *size) +{ + ssize_t actsize = 0, l = 0; + int f_igvm_f; + size_t l_size; + + f_igvm_f = open(igvm_f, O_RDONLY | O_BINARY); + g_assert_true(f_igvm_f); + l_size = get_image_size(igvm_f); + g_assert_true(l_size > 0); + *addr = g_malloc0(l_size); + g_assert_true(*addr); + + while (l < l_size) { + actsize = read(f_igvm_f, *addr + l, 1); + if (actsize < 0) { + break; + } + l += actsize; + } + + close(f_igvm_f); + *size = l_size; + return actsize < 0 ? -1 : l; +} + +static guint32 match_string(char *serial_f, const char *exp_out) +{ + GError *error = NULL; + g_autofree gchar *f_contents = NULL; + g_autofree GRegex *regex = NULL; + g_autofree GMatchInfo *match_info = NULL; + gsize len; + guint32 count = 0; + gboolean ret; + + ret = g_file_get_contents(serial_f, &f_contents, &len, &error); + g_assert(ret); + g_assert_no_error(error); + + regex = g_regex_new(exp_out, G_REGEX_CASELESS, 0, &error); + g_assert_no_error(error); + + ret = g_regex_match_full(regex, f_contents, -1, 0, 0, &match_info, &error); + g_assert_no_error(error); + + while (g_match_info_matches(match_info)) { + gchar *word = g_match_info_fetch(match_info, 0); + g_free(word); + g_match_info_next(match_info, &error); + count++; + } + g_regex_unref(regex); + return count; +} + +static int wait_for_match(char *serial_f, + const char *exp_out, int64_t timeout_s, + guint32 count) +{ + time_t start, delta; + int ret = -1; + + start = time(NULL); + while (1) { + if (match_string(serial_f, exp_out) == count) { + ret = 0; + break; + } + + delta = time(NULL) - start; + if (delta >= timeout_s) { + fprintf(stderr, "timed out waiting to read serial output\n"); + break; + } + + /* wait 20 ms before trying again */ + if (false) { + fprintf(stderr, + "sleeping 20 ms before checking serial output again.\n"); + } + g_usleep(20000); + } + return ret; +} + +static void set_test_params(const char **igvm_f, const char **igvm_init, + const char **snp, const char **cgs) +{ + if (confidential) { + *snp = "-object \'{\"qom-type\":\"sev-snp-guest\",\"id\":\"lsec0\"," + "\"cbitpos\":51,\"reduced-phys-bits\":1,\"policy\":196608}\'"; + *cgs = "confidential-guest-support=lsec0"; + /* + * The following two IGVM files can be built from the source + * present in https://gitlab.com/anisinha/virt-firmware-rs . + * Typing 'make' from the top of this repository will build the + * IGVM files for both confidential and + * non-confidential tests. The IGVM files for the non-coco + * case has been checked-in into the QEMU repository for + * convenience and easy CI pipeline testing. + */ + *igvm_f = "tests/data/igvm/snptest.igvm"; /* prints 'hello world' */ + *igvm_init = "tests/data/igvm/snptest-nohello.igvm"; + } else { + *igvm_f = "tests/data/igvm/hello.igvm"; + *igvm_init = "tests/data/igvm/qemuinit.igvm"; + *snp = ""; + *cgs = ""; + } + + return; +} + +static void set_expected_out(const char **exp_out, const char **exp_out2, + const char **exp_out3) +{ + *exp_out = "Hello world!"; + *exp_out2 = "Test succeeded!"; + *exp_out3 = "boot process complete with initial igvm"; + + return; +} + +static QOSState *set_qemu_args(const char *cgs, const char *tp, char *serialf, + const char *igvm_init, const char *snp) +{ + QOSState *qs; + + if (tp) { + qs = qtest_pc_boot("-machine q35,igvm-cfg=igvm0,%s -m 1G -accel kvm " + "-device vm-launch-update %s " + "-chardev file,id=serial0,path=%s " + "-serial chardev:serial0 " + "-object igvm-cfg,id=igvm0,file=%s %s", + cgs, tp, serialf, igvm_init, snp); + } else { + qs = qtest_pc_boot("-machine q35,igvm-cfg=igvm0,%s -m 1G -accel kvm " + "-device vm-launch-update " + "-chardev file,id=serial0,path=%s " + "-serial chardev:serial0 " + "-object igvm-cfg,id=igvm0,file=%s %s", + cgs, serialf, igvm_init, snp); + } + + return qs; +} + +static void test_load_igvm(void) +{ + const char *igvm_f; + const char *igvm_init; + int ser_fd; + g_autofree void *igvm_blob = NULL; + g_autofree char *serialtmp = NULL; + const char *exp_out; + const char *exp_out2; + const char *exp_out3; + const char *tracepoints = "--trace memory_region_finalize " + "--trace qigvm_cleanup_memory -D /tmp/qemu-debug.log "; + const char *snp, *cgs; + uint64_t gaddr; + size_t igvm_sz; + size_t filesize; + QFWCFG *fw_cfg; + QOSState *qs; + VMLaunchUpdate launch_update; + + if (!trace) { + tracepoints = ""; + } + + if (!qtest_has_machine("q35")) { + g_test_skip("q35 machine not available"); + return; + } + + if (!qtest_has_accel("kvm")) { + g_test_skip("No KVM accelerator available"); + return; + } + + if (!qtest_has_device("vm-launch-update")) { + g_test_skip("Device vm-launch-update is not available"); + return; + } + + set_test_params(&igvm_f, &igvm_init, &snp, &cgs); + set_expected_out(&exp_out, &exp_out2, &exp_out3); + + if (!g_file_test(igvm_f, G_FILE_TEST_EXISTS) || + !g_file_test(igvm_init, G_FILE_TEST_EXISTS)) { + g_test_skip("igvm file bundle(s) does not exist!"); + return; + } + + ser_fd = g_file_open_tmp("launchupdate-qtest-serial-sXXXXXX", + &serialtmp, NULL); + g_assert_true(ser_fd != -1); + + if (debug) { + fprintf(stderr, "serial console file is %s\n", serialtmp); + } + + qs = set_qemu_args(cgs, tracepoints, serialtmp, igvm_init, snp); + + fw_cfg = pc_fw_cfg_init(qs->qts); + + if (debug) { + fprintf(stderr, "target endianness: %s\n", + qtest_big_endian(qs->qts) ? "big" : "little"); + } + + /* exp_out3 should be printed once from initial boot */ + g_assert_true(wait_for_match(serialtmp, exp_out3, WAIT_SEC, 1) == 0); + + if (debug) { + fprintf(stderr, "initially booted with host igvm\n"); + } + + g_assert_true(load_image(igvm_f, &igvm_blob, &igvm_sz) == igvm_sz); + + /* create a data buffer in guest memory */ + gaddr = guest_alloc(&qs->alloc, igvm_sz); + + if (debug) { + fprintf(stderr, "guest paddr: %"PRIx64 " igvm size: %lu\n", + gaddr, igvm_sz); + } + + if (debug) { + fprintf(stderr, "writing igvm file into the guest memory\n"); + } + + qtest_bufwrite(qs->qts, gaddr, igvm_blob, igvm_sz); + + if (debug) { + fprintf(stderr, + "tell hypervisor where igvm is loaded in guest memory\n"); + } + + /* now tell hypervisor where we loaded the bios */ + memset(&launch_update, 0, sizeof(launch_update)); + launch_update.fw_image_size = cpu_to_le64(igvm_sz); + launch_update.fw_image_addr = cpu_to_le64(gaddr); + launch_update.control |= VM_LAUNCHUPDATE_FORMAT_IGVM; + + filesize = qfw_cfg_write_file(fw_cfg, qs, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + + if (debug) { + fprintf(stderr, "resetting the virtual machine now\n"); + } + + qtest_system_reset(qs->qts); + + /* expected string should be printed on the console */ + g_assert_true(wait_for_match(serialtmp, exp_out, WAIT_SEC, 1) == 0); + g_assert_true(wait_for_match(serialtmp, exp_out2, WAIT_SEC, 1) == 0); + + if (debug) { + fprintf(stderr, "hello world found on console\n"); + } + + /* check if VM_LAUNCHUPDATE_CTL_HOST_IGVM function works */ + + /* set only VM_LAUNCHUPDATE_CTL_HOST_IGVM control without IGVM bundle */ + memset(&launch_update, 0, sizeof(launch_update)); + launch_update.control |= VM_LAUNCHUPDATE_CTL_HOST_IGVM; + + filesize = qfw_cfg_write_file(fw_cfg, qs, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + + /* now reset the guest */ + if (debug) { + fprintf(stderr, + "resetting again in order to restore host provided IGVM\n"); + } + qtest_system_reset(qs->qts); + + /* + * exp_out3 should be printed twice, once from initial boot, + * once from restoring host igvm. + */ + g_assert_true(wait_for_match(serialtmp, exp_out3, WAIT_SEC, 2) == 0); + + if (debug) { + fprintf(stderr, "booted with host igvm again\n"); + } + + close(ser_fd); + guest_free(&qs->alloc, gaddr); + pc_fw_cfg_uninit(fw_cfg); + /* qtest_quit() kils QEMU, first by sending SIGTERM, then SIGKILL */ + qtest_quit(qs->qts); +} + +static void test_set_ctrl_once_and_reset_to_host_igvm(void) +{ + const char *igvm_f; + const char *igvm_init; + int ser_fd; + g_autofree void *igvm_blob = NULL; + g_autofree char *serialtmp = NULL; + const char *exp_out; + const char *exp_out2; + const char *exp_out3; + const char *snp, *cgs; + uint64_t gaddr; + size_t igvm_sz; + size_t filesize; + QFWCFG *fw_cfg; + QOSState *qs; + VMLaunchUpdate launch_update; + + if (!qtest_has_machine("q35")) { + g_test_skip("q35 machine not available"); + return; + } + + if (!qtest_has_accel("kvm")) { + g_test_skip("No KVM accelerator available"); + return; + } + + if (!qtest_has_device("vm-launch-update")) { + g_test_skip("Device vm-launch-update is not available"); + return; + } + + set_test_params(&igvm_f, &igvm_init, &snp, &cgs); + set_expected_out(&exp_out, &exp_out2, &exp_out3); + + if (!g_file_test(igvm_f, G_FILE_TEST_EXISTS) || + !g_file_test(igvm_init, G_FILE_TEST_EXISTS)) { + g_test_skip("igvm file bundle(s) does not exist!"); + return; + } + + ser_fd = g_file_open_tmp("launchupdate-qtest-serial-sXXXXXX", + &serialtmp, NULL); + g_assert_true(ser_fd != -1); + + if (debug) { + fprintf(stderr, "serial console file is %s\n", serialtmp); + } + + qs = set_qemu_args(cgs, NULL, serialtmp, igvm_init, snp); + + fw_cfg = pc_fw_cfg_init(qs->qts); + + g_assert_true(wait_for_match(serialtmp, exp_out3, WAIT_SEC, 1) == 0); + + if (debug) { + fprintf(stderr, "initially booted with host igvm\n"); + } + + g_assert_true(load_image(igvm_f, &igvm_blob, &igvm_sz) == igvm_sz); + + /* create a data buffer in guest memory */ + gaddr = guest_alloc(&qs->alloc, igvm_sz); + + if (debug) { + fprintf(stderr, "guest paddr: %"PRIx64 " igvm size: %lu\n", + gaddr, igvm_sz); + } + + if (debug) { + fprintf(stderr, "writing igvm file into the guest memory\n"); + } + + qtest_bufwrite(qs->qts, gaddr, igvm_blob, igvm_sz); + + if (debug) { + fprintf(stderr, + "tell hypervisor where igvm is loaded in guest memory\n"); + } + + /* now tell hypervisor where we loaded the bios */ + memset(&launch_update, 0, sizeof(launch_update)); + launch_update.fw_image_size = cpu_to_le64(igvm_sz); + launch_update.fw_image_addr = cpu_to_le64(gaddr); + + /* set both host ctrl and format_igvm ctrl once */ + launch_update.control |= VM_LAUNCHUPDATE_FORMAT_IGVM; + launch_update.control |= VM_LAUNCHUPDATE_CTL_HOST_IGVM; + + filesize = qfw_cfg_write_file(fw_cfg, qs, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + + if (debug) { + fprintf(stderr, "resetting the virtual machine. This should load " + "user provided igvm.\n"); + } + + qtest_system_reset(qs->qts); + + /* expected string should be printed on the console */ + g_assert_true(wait_for_match(serialtmp, exp_out, WAIT_SEC, 1) == 0); + g_assert_true(wait_for_match(serialtmp, exp_out2, WAIT_SEC, 1) == 0); + + if (debug) { + fprintf(stderr, "hello world found on console\n"); + fprintf(stderr, "Now resetting again in order to reset to host igvm\n"); + } + + qtest_system_reset(qs->qts); + + /* + * exp_out3 should be printed twice, once from initial boot, + * once from restoring host igvm. + */ + g_assert_true(wait_for_match(serialtmp, exp_out3, WAIT_SEC, 2) == 0); + + if (debug) { + fprintf(stderr, "booted with host igvm\n"); + } + + close(ser_fd); + guest_free(&qs->alloc, gaddr); + pc_fw_cfg_uninit(fw_cfg); + /* qtest_quit() kils QEMU, first by sending SIGTERM, then SIGKILL */ + qtest_quit(qs->qts); +} + +int main(int argc, char **argv) +{ + const char *arch = qtest_get_arch(); + + g_test_init(&argc, &argv, NULL); + + if (strcmp(arch, "x86_64")) { + g_test_skip("vmlaunchupdate tests are only available on x86_64\n"); + return 0; + } + + g_test_add_func("/vm-launch-update/cap", test_vm_launch_update_capability); + g_test_add_func("/vm-launch-update/disabled", + test_vm_launch_update_disable); + + g_test_add_func("/vm-launch-update/errorcheck", check_error); + g_test_add_func("/vm-launch-update/load_igvm", + test_load_igvm); + g_test_add_func("/vm-launch-update/ctrl_set_once", + test_set_ctrl_once_and_reset_to_host_igvm); + + if (getenv("LAUNCHUPDATE_DEBUG")) { + debug = true; + } + if (getenv("LAUNCHUPDATE_TRACE")) { + trace = true; + } + if (getenv("COCO")) { + confidential = true; + } + + return g_test_run(); +} diff --git a/tests/qtest/libqos/ahci.c b/tests/qtest/libqos/ahci.c index 0621a6c477..50b63239c2 100644 --- a/tests/qtest/libqos/ahci.c +++ b/tests/qtest/libqos/ahci.c @@ -74,6 +74,7 @@ AHCICommandProp ahci_command_properties[] = { { .cmd = CMD_READ_MAX, .lba28 = true }, { .cmd = CMD_READ_MAX_EXT, .lba48 = true }, { .cmd = CMD_FLUSH_CACHE, .data = false }, + { .cmd = CMD_INIT_DP, .data = false }, { .cmd = CMD_PACKET, .data = true, .size = 16, .atapi = true, .pio = true }, { .cmd = CMD_PACKET_ID, .data = true, .pio = true, @@ -707,6 +708,11 @@ void ahci_exec(AHCIQState *ahci, uint8_t port, if (opts->atapi) { uint16_t bcl = opts->set_bcl ? opts->bcl : ATAPI_SECTOR_SIZE; cmd = ahci_atapi_command_create(op, bcl, opts->atapi_dma); + if (opts->atapi_raw) { + /* request full 2352-byte raw sectors; sector_size must match */ + cmd->atapi_cmd[9] = 0xf8; + cmd->sector_size = ATAPI_RAW_SECTOR_SIZE; + } } else { cmd = ahci_command_create(op); } @@ -1175,6 +1181,19 @@ void ahci_command_set_prd_size(AHCICommand *cmd, unsigned prd_size) ahci_command_set_sizes(cmd, cmd->xbytes, prd_size); } +/* For a no-data command, whose count carries an argument of its own */ +void ahci_command_set_count(AHCICommand *cmd, uint16_t count) +{ + g_assert(!cmd->props->data); + cmd->fis.count = count; +} + +void ahci_command_expect_error(AHCICommand *cmd, uint8_t err) +{ + cmd->interrupts |= AHCI_PX_IS_TFES; + cmd->errors |= err; +} + void ahci_command_adjust(AHCICommand *cmd, uint64_t offset, uint64_t buffer, uint64_t xbytes, unsigned prd_size) { diff --git a/tests/qtest/libqos/ahci.h b/tests/qtest/libqos/ahci.h index a0487a1557..6d861c79ee 100644 --- a/tests/qtest/libqos/ahci.h +++ b/tests/qtest/libqos/ahci.h @@ -242,6 +242,7 @@ #define AHCI_SECTOR_SIZE (512) #define ATAPI_SECTOR_SIZE (2048) +#define ATAPI_RAW_SECTOR_SIZE (2352) #define AHCI_SIGNATURE_CDROM (0xeb140101) #define AHCI_SIGNATURE_DISK (0x00000101) @@ -277,6 +278,7 @@ enum { CMD_READ_MAX = 0xF8, CMD_READ_MAX_EXT = 0x27, CMD_FLUSH_CACHE = 0xE7, + CMD_INIT_DP = 0x91, /* INITIALIZE DEVICE PARAMETERS */ CMD_IDENTIFY = 0xEC, CMD_PACKET = 0xA0, CMD_PACKET_ID = 0xA1, @@ -323,6 +325,9 @@ enum { #define ATA_DEVICE_DRIVE 0x10 #define ATA_DEVICE_HEAD 0x0F +/* ATA error register bits */ +#define ATA_ERR_ABRT 0x04 + /*** Structures ***/ typedef struct AHCIPortQState { @@ -485,6 +490,7 @@ typedef struct AHCIOpts { uint64_t buffer; /* Pointer to source or destination guest buffer */ bool atapi; /* ATAPI command? */ bool atapi_dma; /* Use DMA for ATAPI? */ + bool atapi_raw; /* READ CD returning 2352-byte raw sectors */ bool error; int (*pre_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *); int (*mid_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *); @@ -636,6 +642,8 @@ void ahci_command_set_size(AHCICommand *cmd, uint64_t xbytes); void ahci_command_set_prd_size(AHCICommand *cmd, unsigned prd_size); void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes, unsigned prd_size); +void ahci_command_set_count(AHCICommand *cmd, uint16_t count); +void ahci_command_expect_error(AHCICommand *cmd, uint8_t err); void ahci_command_set_acmd(AHCICommand *cmd, void *acmd); void ahci_command_enable_atapi_dma(AHCICommand *cmd); void ahci_command_adjust(AHCICommand *cmd, uint64_t lba_sect, uint64_t gbuffer, diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index c33c799c92..bec37c71b8 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -2151,8 +2151,7 @@ bool mkimg(const char *file, const char *fmt, unsigned size_mb) bool qtest_verbose(const char *domain) { - const char *log = getenv("QTEST_LOG"); - const char *found; + const gchar *found, *log = g_getenv("QTEST_LOG"); assert(domain); @@ -2178,11 +2177,11 @@ bool qtest_verbose(const char *domain) * QTEST_LOG=,- (only false for domain2) * allows other separators, except - and + */ - found = strstr(log, domain); + found = g_strstr_len(log, -1, domain); if (found) { /* reject options given twice */ - assert(!strstr(found + strlen(domain), domain)); + assert(!g_strstr_len(found + strlen(domain), -1, domain)); if (found > log) { ptrdiff_t i = found - log - 1; @@ -2196,7 +2195,7 @@ bool qtest_verbose(const char *domain) * If filtering out a specific domain, all others are * enabled. */ - return !!strstr(log, "-"); + return !!g_strstr_len(log, -1, "-"); } } diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 56ff860e21..cbdef5a545 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -61,6 +61,8 @@ qtests_i386 = \ (config_all_devices.has_key('CONFIG_Q35') ? ['e820-test'] : []) + \ (config_all_devices.has_key('CONFIG_FW_CFG_DMA') ? ['vmcoreinfo-test'] : []) + \ (config_all_devices.has_key('CONFIG_Q35') ? ['dump-test'] : []) + \ + (igvm.found() and + config_all_devices.has_key('CONFIG_FW_CFG_DMA') ? ['launchupdate-test'] : []) + \ (config_all_devices.has_key('CONFIG_I440FX') ? ['i440fx-test'] : []) + \ (config_all_devices.has_key('CONFIG_I440FX') ? ['ide-test'] : []) + \ (config_all_devices.has_key('CONFIG_I440FX') ? ['numa-test'] : []) + \ @@ -297,13 +299,15 @@ qtests_riscv64 = ['riscv-csr-test'] + \ (config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') and config_all_devices.has_key('CONFIG_RISCV_IOMMU') ? ['iommu-riscv-test'] : []) + \ - (config_all_devices.has_key('CONFIG_K230') ? ['k230-wdt-test'] : []) + (config_all_devices.has_key('CONFIG_K230') ? + ['k230-ddr-test', 'k230-wdt-test', 'k230-gsdma-test', 'k230-decomp-gzip-test'] : []) -qtests_hexagon = ['boot-serial-test'] +qtests_hexagon = ['boot-serial-test', 'l2vic-test', 'qct-qtimer-test'] qos_test_ss = ss.source_set() qos_test_ss.add( 'ac97-test.c', + 'adc128d818-test.c', 'adm1272-test.c', 'adm1266-test.c', 'ds1338-test.c', @@ -318,6 +322,8 @@ qos_test_ss.add( 'tulip-test.c', 'nvme-test.c', 'pca9552-test.c', + 'pca9554-test.c', + 'pca9555-test.c', 'pci-test.c', 'pcnet-test.c', 'rs5c372-test.c', @@ -389,9 +395,11 @@ if get_option('replication').allowed() endif qtests = { - 'aspeed_hace-test': files('aspeed-hace-utils.c', 'aspeed_hace-test.c'), + 'aspeed_hace-test': [files('aspeed-hace-utils.c', 'aspeed_hace-test.c'), + crypto], 'aspeed_smc-test': files('aspeed-smc-utils.c', 'aspeed_smc-test.c'), - 'ast2700-hace-test': files('aspeed-hace-utils.c', 'ast2700-hace-test.c'), + 'ast2700-hace-test': [files('aspeed-hace-utils.c', 'ast2700-hace-test.c'), + crypto], 'ast2700-smc-test': files('aspeed-smc-utils.c', 'ast2700-smc-test.c'), 'bios-tables-test': [io, 'boot-sector.c', 'acpi-utils.c', 'tpm-emu.c'], 'cdrom-test': files('boot-sector.c'), @@ -502,6 +510,7 @@ foreach dir : target_dirs protocol: 'tap', timeout: slow_qtests.get(test, 60), priority: slow_qtests.get(test, 60), - suite: ['qtest', 'qtest-' + target_base]) + suite: ['qtest', 'qtest-' + target_base] + + (slow_qtests.has_key(test) ? ['slow'] : [])) endforeach endforeach diff --git a/tests/qtest/migration/tls-tests.c b/tests/qtest/migration/tls-tests.c index 827cc7bcf8..9bdb1165af 100644 --- a/tests/qtest/migration/tls-tests.c +++ b/tests/qtest/migration/tls-tests.c @@ -492,6 +492,7 @@ static void test_precopy_tcp_no_tls(char *name, MigrateCommon *args) test_precopy_common(args); } +#ifdef CONFIG_TASN1 static void * migrate_hook_start_tls_x509_no_host(QTestState *from, QTestState *to) { @@ -519,7 +520,6 @@ static void test_precopy_tcp_tls_no_hostname(char *name, MigrateCommon *args) test_precopy_common(args); } -#ifdef CONFIG_TASN1 static void test_precopy_tcp_tls_x509_default_host(char *name, MigrateCommon *args) { @@ -719,8 +719,10 @@ void migration_test_add_tls(MigrationTestEnv *env) migration_test_add("/migration/precopy/tcp/no-tls", test_precopy_tcp_no_tls); +#ifdef CONFIG_TASN1 migration_test_add("/migration/precopy/tcp/tls/no-hostname", test_precopy_tcp_tls_no_hostname); +#endif /* CONFIG_TASN1 */ migration_test_add("/migration/precopy/unix/tls/psk", test_precopy_unix_tls_psk); diff --git a/tests/qtest/pca9552-test.c b/tests/qtest/pca9552-test.c index 7474957692..3718dfbd22 100644 --- a/tests/qtest/pca9552-test.c +++ b/tests/qtest/pca9552-test.c @@ -77,6 +77,76 @@ static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc) g_assert_cmphex(value, ==, 0xEF); } +/* Verify the power-on reset defaults. */ +static void test_reset_defaults(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + /* Prescalers, PWM duty cycles and LED selectors (all LEDs off) */ + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_PSC0), ==, 0xFF); + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_PWM0), ==, 0x80); + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_PSC1), ==, 0xFF); + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_PWM1), ==, 0x80); + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_LS0), ==, 0x55); + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_LS1), ==, 0x55); + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_LS2), ==, 0x55); + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_LS3), ==, 0x55); + + /* All LEDs off, so every pin floats high through its pull-up */ + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_INPUT0), ==, 0xFF); + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_INPUT1), ==, 0xFF); +} + +/* + * The PCA9552 only advances the command pointer when the AI bit is set, and + * it wraps modulo the full 10-register map. + */ +static void test_autoinc_requires_ai_bit(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *i2cdev = (QI2CDevice *)obj; + uint8_t reg; + uint8_t resp; + + /* + * With the AI bit, reading from LS3 (register 9) rolls over to INPUT0 + * (register 0), not to a sibling in a register pair. All LEDs are off + * after reset so the input ports read 0xFF. + */ + reg = PCA9552_LS3 | PCA9552_AUTOINC; + qi2c_send(i2cdev, ®, 1); + qi2c_recv(i2cdev, &resp, 1); /* LS3 */ + g_assert_cmphex(resp, ==, 0x55); + qi2c_recv(i2cdev, &resp, 1); /* wraps to INPUT0 */ + g_assert_cmphex(resp, ==, 0xFF); + qi2c_recv(i2cdev, &resp, 1); /* INPUT1 */ + g_assert_cmphex(resp, ==, 0xFF); + + /* + * Without the AI bit the pointer must not advance: repeated reads keep + * returning the same register. + */ + i2c_set8(i2cdev, PCA9552_LS0, 0x54); + reg = PCA9552_LS0; + qi2c_send(i2cdev, ®, 1); + qi2c_recv(i2cdev, &resp, 1); + g_assert_cmphex(resp, ==, 0x54); + qi2c_recv(i2cdev, &resp, 1); + g_assert_cmphex(resp, ==, 0x54); +} + +/* + * The PCA9552 decodes a 4-bit command and has no register past LS3 (9), so + * addressing register 0x0A reads back 0xFF. + */ +static void test_command_out_of_range(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + g_assert_cmphex(i2c_get8(i2cdev, 0x0A), ==, 0xFF); +} + static void pca9552_register_nodes(void) { QOSGraphEdgeOptions opts = { @@ -89,5 +159,11 @@ static void pca9552_register_nodes(void) qos_add_test("tx-rx", "pca9552", send_and_receive, NULL); qos_add_test("rx-autoinc", "pca9552", receive_autoinc, NULL); + qos_add_test("reset-defaults", "pca9552", test_reset_defaults, NULL); + qos_add_test("autoinc-requires-ai-bit", "pca9552", + test_autoinc_requires_ai_bit, NULL); + qos_add_test("command-out-of-range", "pca9552", test_command_out_of_range, + NULL); } + libqos_init(pca9552_register_nodes); diff --git a/tests/qtest/pca9554-test.c b/tests/qtest/pca9554-test.c new file mode 100644 index 0000000000..5366e71984 --- /dev/null +++ b/tests/qtest/pca9554-test.c @@ -0,0 +1,223 @@ +/* + * QTest testcase for the PCA9554/PCA9536 I/O port expanders + * + * Copyright (c) Meta Platforms, Inc. and affiliates. (http://www.meta.com) + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/gpio/pca9554_regs.h" +#include "libqos/i2c.h" +#include "libqos/qgraph.h" + +#define PCA9554_TEST_ADDR 0x20 + +/* Verify power-on reset defaults match the PCA9554 datasheet. */ +static void test_reset_defaults(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + /* All pins are inputs, pulled high, with no polarity inversion. */ + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9554_OUTPUT), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9554_POLARITY), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9554_CONFIG), ==, 0xFF); +} + +/* + * A pin configured as output (config=0) drives its OUTPUT register level onto + * the pin (push-pull), which the INPUT register reflects. A pin configured as + * input (config=1) floats high through its pull-up. + */ +static void test_output_drives_input(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + /* Low nibble output, high nibble input (pull-up). */ + i2c_set8(dev, PCA9554_CONFIG, 0xF0); + i2c_set8(dev, PCA9554_OUTPUT, 0xFA); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0xFA); + + /* All outputs, driven low then high. */ + i2c_set8(dev, PCA9554_CONFIG, 0x00); + i2c_set8(dev, PCA9554_OUTPUT, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0x00); + + i2c_set8(dev, PCA9554_OUTPUT, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0xFF); +} + +/* + * With all pins configured as inputs the pull-ups make the INPUT register read + * all ones regardless of the OUTPUT register; switching a pin to output with + * output=0 drives it low. + */ +static void test_input_pullup(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0xFF); + + i2c_set8(dev, PCA9554_OUTPUT, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0xFF); + + i2c_set8(dev, PCA9554_CONFIG, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0x00); +} + +/* + * Polarity inversion: reading INPUT returns the XOR of the pin levels and the + * polarity register. + */ +static void test_polarity_inversion(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0xFF); + + i2c_set8(dev, PCA9554_POLARITY, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0x00); + + i2c_set8(dev, PCA9554_POLARITY, 0x0F); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0xF0); +} + +/* Polarity inversion combined with output-driven pins. */ +static void test_polarity_with_output(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + i2c_set8(dev, PCA9554_CONFIG, 0x00); + i2c_set8(dev, PCA9554_OUTPUT, 0xA5); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0xA5); + + i2c_set8(dev, PCA9554_POLARITY, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0x5A); + + /* Inversion only affects the INPUT read, not the OUTPUT register. */ + g_assert_cmphex(i2c_get8(dev, PCA9554_OUTPUT), ==, 0xA5); +} + +/* + * The PCA9554 has no auto-increment: the command pointer never advances, so + * multi-byte reads and writes all target the addressed register. + */ +static void test_no_autoincrement(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint8_t buf[2]; + + /* Distinct values in adjacent registers. */ + i2c_set8(dev, PCA9554_OUTPUT, 0xAA); + i2c_set8(dev, PCA9554_POLARITY, 0x33); + + /* Two reads from OUTPUT return OUTPUT twice, not OUTPUT then POLARITY. */ + i2c_read_block(dev, PCA9554_OUTPUT, buf, 2); + g_assert_cmphex(buf[0], ==, 0xAA); + g_assert_cmphex(buf[1], ==, 0xAA); + + /* The second written byte overwrites OUTPUT; POLARITY is untouched. */ + buf[0] = 0x12; + buf[1] = 0x34; + i2c_write_block(dev, PCA9554_OUTPUT, buf, 2); + g_assert_cmphex(i2c_get8(dev, PCA9554_OUTPUT), ==, 0x34); + g_assert_cmphex(i2c_get8(dev, PCA9554_POLARITY), ==, 0x33); +} + +/* + * The PCA9536 shares the PCA9554 register map but only has four pins, so its + * reset defaults and pin logic are masked to the low nibble. + */ +static void test_pca9536_reset_defaults(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0x0F); + g_assert_cmphex(i2c_get8(dev, PCA9554_OUTPUT), ==, 0x0F); + g_assert_cmphex(i2c_get8(dev, PCA9554_POLARITY), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9554_CONFIG), ==, 0x0F); +} + +/* Only the four low pins are driven; the upper nibble stays low. */ +static void test_pca9536_output_drives_input(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + i2c_set8(dev, PCA9554_CONFIG, 0x00); + + i2c_set8(dev, PCA9554_OUTPUT, 0x0A); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0x0A); + + i2c_set8(dev, PCA9554_OUTPUT, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0x00); +} + +/* + * The four upper bits address pins that do not exist on the PCA9536, so writes + * to the register map discard them: the writable registers read back with bits + * [7:4] cleared, and driving them onto the pins never surfaces in INPUT. + */ +static void test_pca9536_ignores_upper_bits(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + /* Bits [7:4] are dropped on write; bits [3:0] survive. */ + i2c_set8(dev, PCA9554_OUTPUT, 0xFA); + g_assert_cmphex(i2c_get8(dev, PCA9554_OUTPUT), ==, 0x0A); + + i2c_set8(dev, PCA9554_POLARITY, 0xF5); + g_assert_cmphex(i2c_get8(dev, PCA9554_POLARITY), ==, 0x05); + + i2c_set8(dev, PCA9554_CONFIG, 0xF3); + g_assert_cmphex(i2c_get8(dev, PCA9554_CONFIG), ==, 0x03); + + /* + * With all four pins as outputs, driving 0xFF only affects the low + * nibble. + */ + i2c_set8(dev, PCA9554_POLARITY, 0x00); + i2c_set8(dev, PCA9554_CONFIG, 0x00); + i2c_set8(dev, PCA9554_OUTPUT, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0x0F); +} + +static void pca9554_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "address=0x20" + }; + add_qi2c_address(&opts, &(QI2CAddress) { PCA9554_TEST_ADDR }); + + qos_node_create_driver("pca9554", i2c_device_create); + qos_node_consumes("pca9554", "i2c-bus", &opts); + + qos_add_test("reset-defaults", "pca9554", test_reset_defaults, NULL); + qos_add_test("output-drives-input", "pca9554", test_output_drives_input, + NULL); + qos_add_test("input-pullup", "pca9554", test_input_pullup, NULL); + qos_add_test("polarity-inversion", "pca9554", test_polarity_inversion, + NULL); + qos_add_test("polarity-with-output", "pca9554", test_polarity_with_output, + NULL); + qos_add_test("no-autoincrement", "pca9554", test_no_autoincrement, NULL); + + qos_node_create_driver("pca9536", i2c_device_create); + qos_node_consumes("pca9536", "i2c-bus", &opts); + + qos_add_test("reset-defaults", "pca9536", test_pca9536_reset_defaults, + NULL); + qos_add_test("output-drives-input", "pca9536", + test_pca9536_output_drives_input, NULL); + qos_add_test("ignores-upper-bits", "pca9536", + test_pca9536_ignores_upper_bits, NULL); +} + +libqos_init(pca9554_register_nodes); diff --git a/tests/qtest/pca9555-test.c b/tests/qtest/pca9555-test.c new file mode 100644 index 0000000000..84d771bcbb --- /dev/null +++ b/tests/qtest/pca9555-test.c @@ -0,0 +1,251 @@ +/* + * QTest testcase for the PCA9555 16-bit I/O port expander + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/gpio/pca9552_regs.h" +#include "libqos/i2c.h" +#include "libqos/qgraph.h" + +#define PCA9555_TEST_ADDR 0x20 + +/* Verify power-on reset defaults match the PCA9555 datasheet. */ +static void test_reset_defaults(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT1), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9535_OUTPUT0), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9535_OUTPUT1), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9535_POLARITY0), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9535_POLARITY1), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9535_CONFIG0), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9535_CONFIG1), ==, 0xFF); +} + +/* + * When a pin is configured as output and driven low (output=0, config=0), + * the input register should reflect 0 for that pin. + * When driven high (output=1, config=0), input should reflect 1. + * When configured as input (config=1), PCA5555 pull-up makes it read 1. + */ +static void test_output_drives_input(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + i2c_set8(dev, PCA9535_CONFIG0, 0xF0); + i2c_set8(dev, PCA9535_OUTPUT0, 0xFA); + + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0xFA); + + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT1), ==, 0xFF); + + i2c_set8(dev, PCA9535_CONFIG0, 0x00); + i2c_set8(dev, PCA9535_OUTPUT0, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0x00); + + i2c_set8(dev, PCA9535_OUTPUT0, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0xFF); +} + +/* + * When all pins are inputs (config=0xFF) and no external driver, + * PCA9555 pull-ups should make the input register read all ones. + * Switching a pin to output mode with output=0 should drive it low. + */ +static void test_input_pullup(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT1), ==, 0xFF); + + i2c_set8(dev, PCA9535_OUTPUT0, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0xFF); + + i2c_set8(dev, PCA9535_CONFIG0, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0x00); +} + +/* + * Test that both ports are independent: changing port 0 registers + * should not affect port 1 and vice versa. + */ +static void test_port_independence(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + i2c_set8(dev, PCA9535_CONFIG0, 0x00); + i2c_set8(dev, PCA9535_OUTPUT0, 0x00); + + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT1), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9535_CONFIG1), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9535_OUTPUT1), ==, 0xFF); + + i2c_set8(dev, PCA9535_CONFIG1, 0x00); + i2c_set8(dev, PCA9535_OUTPUT1, 0xAA); + + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT1), ==, 0xAA); + g_assert_cmphex(i2c_get8(dev, PCA9535_OUTPUT0), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9535_OUTPUT1), ==, 0xAA); +} + +/* + * Polarity inversion: reading INPUT with polarity bits set should + * return the XOR of the actual input state and the polarity register. + */ +static void test_polarity_inversion(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0xFF); + + i2c_set8(dev, PCA9535_POLARITY0, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0x00); + + i2c_set8(dev, PCA9535_POLARITY0, 0x0F); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0xF0); + + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT1), ==, 0xFF); + + i2c_set8(dev, PCA9535_POLARITY1, 0xAA); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT1), ==, 0x55); +} + +/* Polarity inversion combined with output-driven pins. */ +static void test_polarity_with_output(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + i2c_set8(dev, PCA9535_CONFIG0, 0x00); + i2c_set8(dev, PCA9535_OUTPUT0, 0xA5); + + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0xA5); + + i2c_set8(dev, PCA9535_POLARITY0, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0x5A); + + g_assert_cmphex(i2c_get8(dev, PCA9535_OUTPUT0), ==, 0xA5); +} + +/* + * The PCA9555 auto-increments by toggling bit 0 of the command pointer + * within a register pair. Reading two bytes from INPUT0 should yield + * INPUT0 then INPUT1. + */ +static void test_auto_increment_read(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint8_t buf[2]; + + i2c_set8(dev, PCA9535_CONFIG0, 0x00); + i2c_set8(dev, PCA9535_CONFIG1, 0x00); + i2c_set8(dev, PCA9535_OUTPUT0, 0xAA); + i2c_set8(dev, PCA9535_OUTPUT1, 0x55); + + i2c_read_block(dev, PCA9535_INPUT0, buf, 2); + g_assert_cmphex(buf[0], ==, 0xAA); + g_assert_cmphex(buf[1], ==, 0x55); + + i2c_read_block(dev, PCA9535_OUTPUT0, buf, 2); + g_assert_cmphex(buf[0], ==, 0xAA); + g_assert_cmphex(buf[1], ==, 0x55); +} + +/* + * Auto-increment write: writing two data bytes after a command byte + * should write to port 0 then port 1 of the addressed register pair. + */ +static void test_auto_increment_write(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint8_t buf[2]; + + buf[0] = 0x12; + buf[1] = 0x34; + i2c_write_block(dev, PCA9535_OUTPUT0, buf, 2); + + g_assert_cmphex(i2c_get8(dev, PCA9535_OUTPUT0), ==, 0x12); + g_assert_cmphex(i2c_get8(dev, PCA9535_OUTPUT1), ==, 0x34); + + buf[0] = 0x0F; + buf[1] = 0xF0; + i2c_write_block(dev, PCA9535_CONFIG0, buf, 2); + + g_assert_cmphex(i2c_get8(dev, PCA9535_CONFIG0), ==, 0x0F); + g_assert_cmphex(i2c_get8(dev, PCA9535_CONFIG1), ==, 0xF0); +} + +/* + * Auto-increment toggles within the pair: starting from port 1 should + * wrap back to port 0 (toggle bit 0). + */ +static void test_auto_increment_toggle(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint8_t buf[2]; + + i2c_set8(dev, PCA9535_OUTPUT0, 0xAA); + i2c_set8(dev, PCA9535_OUTPUT1, 0x55); + + i2c_read_block(dev, PCA9535_OUTPUT1, buf, 2); + g_assert_cmphex(buf[0], ==, 0x55); + g_assert_cmphex(buf[1], ==, 0xAA); +} + +/* + * Verify the command byte wraps at 3 bits: register addresses + * beyond 7 should alias to the same register (bits [2:0] only). + */ +static void test_command_wrapping(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + i2c_set8(dev, PCA9535_OUTPUT0, 0x42); + + g_assert_cmphex(i2c_get8(dev, 0x0A), ==, 0x42); +} + +static void pca9555_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "address=0x20" + }; + add_qi2c_address(&opts, &(QI2CAddress) { PCA9555_TEST_ADDR }); + + qos_node_create_driver("pca9555", i2c_device_create); + qos_node_consumes("pca9555", "i2c-bus", &opts); + + qos_add_test("reset-defaults", "pca9555", test_reset_defaults, NULL); + qos_add_test("output-drives-input", "pca9555", test_output_drives_input, + NULL); + qos_add_test("input-pullup", "pca9555", test_input_pullup, NULL); + qos_add_test("port-independence", "pca9555", test_port_independence, NULL); + qos_add_test("polarity-inversion", "pca9555", test_polarity_inversion, + NULL); + qos_add_test("polarity-with-output", "pca9555", test_polarity_with_output, + NULL); + qos_add_test("auto-increment-read", "pca9555", test_auto_increment_read, + NULL); + qos_add_test("auto-increment-write", "pca9555", test_auto_increment_write, + NULL); + qos_add_test("auto-increment-toggle", "pca9555", test_auto_increment_toggle, + NULL); + qos_add_test("command-wrapping", "pca9555", test_command_wrapping, NULL); +} + +libqos_init(pca9555_register_nodes); diff --git a/tests/qtest/pxe-test.c b/tests/qtest/pxe-test.c index a3f900fbea..e85dec5a4e 100644 --- a/tests/qtest/pxe-test.c +++ b/tests/qtest/pxe-test.c @@ -108,6 +108,10 @@ static void test_batch(const testdef_t *tests, bool ipv6) const testdef_t *test = &tests[i]; char *testname; + if (!qtest_has_machine(test->machine)) { + continue; + } + if (!qtest_has_device(test->model)) { continue; } diff --git a/tests/qtest/qct-qtimer-test.c b/tests/qtest/qct-qtimer-test.c new file mode 100644 index 0000000000..fd8bb0af77 --- /dev/null +++ b/tests/qtest/qct-qtimer-test.c @@ -0,0 +1,385 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + * + * QTest testcase for the QCT QTimer + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" +#include "hw/hexagon/hexagon.h" +#include "qemu/bitops.h" + +#include "hw/hexagon/machine_cfg_v68n_1024.h.inc" +#include "hw/hexagon/machine_cfg_v66g_1024.h.inc" + +#define QTIMER_DEFAULT_FREQ_HZ 19200000ULL + +#define QCT_QTIMER_CNTPCT_LO (0x000) +#define QCT_QTIMER_CNTPCT_HI (0x004) +#define QCT_QTIMER_CNT_FREQ (0x010) +#define QCT_QTIMER_CNTP_CVAL_LO (0x020) +#define QCT_QTIMER_CNTP_TVAL (0x028) +#define QCT_QTIMER_CNTP_CTL (0x02c) +#define QCT_QTIMER_CNTP_CTL_ENABLE (1 << 0) + +#define QTIMER_FRAME_STRIDE 0x1000 +/* Frames instantiated by hex-subsys, and the L2VIC input frame 0 drives. */ +#define QTIMER_NR_FRAMES 3 +#define QTIMER_L2VIC_IRQ_BASE 2 + +#define QCT_QTIMER_AC_CNTFRQ (0x000) +#define QCT_QTIMER_AC_CNTSR (0x004) +#define QCT_QTIMER_AC_CNTTID_0 (0x08) +#define QCT_QTIMER_AC_CNTACR_START (0x40) +#define QCT_QTIMER_AC_CNTACR_ALL (0x3f) +#define QCT_QTIMER_AC_CNTACR_RWPT (1 << 5) /* R/W of CNTP_* regs */ +#define QCT_QTIMER_AC_CNTACR_RFRQ (1 << 2) /* R/W of CNTFRQ register */ +#define QCT_QTIMER_AC_CNTACR_RPCT (1 << 0) /* R/W of CNTPCT register */ + +static uint64_t qtimer_view_base; +static uint64_t qtimer_ac_base; + +#define TIMER_TEST_OFFSET 1000 +/* TIMER_TEST_OFFSET ticks expressed in nanoseconds of QEMU_CLOCK_VIRTUAL */ +#define TIMER_TEST_NS \ + ((TIMER_TEST_OFFSET * 1000000000ULL) / QTIMER_DEFAULT_FREQ_HZ) + +static uint32_t qtimer_read32(uint64_t base, uint32_t offset) +{ + return readl(base + offset); +} + +static void qtimer_write32(uint64_t base, uint32_t offset, uint32_t value) +{ + writel(base + offset, value); +} + +static uint64_t qtimer_read64(uint64_t base, uint32_t offset) +{ + uint32_t lo = qtimer_read32(base, offset); + uint32_t hi = qtimer_read32(base, offset + 4); + + return ((uint64_t)hi << 32) | lo; +} + +static void qtimer_write64(uint64_t base, uint32_t offset, uint64_t value) +{ + qtimer_write32(base, offset, extract64(value, 0, 32)); + qtimer_write32(base, offset + 4, extract64(value, 32, 32)); +} + +static void test_qtimer_basic_access(void) +{ + uint32_t val; + + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNT_FREQ); + g_assert_cmpuint(val, ==, QTIMER_DEFAULT_FREQ_HZ); +} + +static void test_qtimer_multiple_frames(void) +{ + uint32_t val; + uint64_t frame0_base = qtimer_view_base; + uint64_t frame1_base = qtimer_view_base + 0x1000; + + val = qtimer_read32(frame0_base, QCT_QTIMER_CNT_FREQ); + g_assert_cmpuint(val, ==, QTIMER_DEFAULT_FREQ_HZ); + + val = qtimer_read32(frame1_base, QCT_QTIMER_CNT_FREQ); + g_assert_cmpuint(val, ==, QTIMER_DEFAULT_FREQ_HZ); +} + +static void test_qtimer_register_reads(void) +{ + qtimer_read32(qtimer_view_base, QCT_QTIMER_CNT_FREQ); + qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO); + qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO); + qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTP_CTL); + qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTP_TVAL); +} + +static void test_qtimer_control_registers(void) +{ + uint32_t ctl_val; + uint64_t cval_before, cval_after; + + cval_before = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO); + + qtimer_write32(qtimer_view_base, QCT_QTIMER_CNTP_TVAL, 1000); + + qtimer_write32(qtimer_view_base, QCT_QTIMER_CNTP_CTL, 1); + ctl_val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTP_CTL); + g_assert_cmpuint(ctl_val & 1, ==, 1); + + /* CVAL should be greater than before since we set TVAL */ + cval_after = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO); + g_assert_cmpuint(cval_after, >, cval_before); + + qtimer_write32(qtimer_view_base, QCT_QTIMER_CNTP_CTL, 0); + ctl_val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTP_CTL); + g_assert_cmpuint(ctl_val & 1, ==, 0); +} + +static void test_qtimer_cval_access(void) +{ + uint64_t current_time, test_cval, read_cval; + + current_time = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO); + test_cval = current_time + 10000; + + qtimer_write64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO, test_cval); + read_cval = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO); + g_assert_cmpuint(read_cval, ==, test_cval); +} + +static void test_qtimer_counter_progression(void) +{ + uint32_t freq; + uint64_t count1, count2; + + /* + * In qtest mode the virtual clock does not advance on its own, so + * reading the counter twice must give the same value. + */ + count1 = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO); + count2 = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO); + g_assert_cmpuint(count2, ==, count1); + + freq = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNT_FREQ); + g_assert_cmpuint(freq, ==, QTIMER_DEFAULT_FREQ_HZ); +} + +static void test_qtimer_timer_behavior(void) +{ + uint64_t current_count, target_count, read_cval, new_count; + uint64_t ctl_val, count_after_disable; + + current_count = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO); + + target_count = current_count + TIMER_TEST_OFFSET; + qtimer_write64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO, target_count); + + read_cval = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO); + g_assert_cmpuint(read_cval, ==, target_count); + + qtimer_write32(qtimer_view_base, QCT_QTIMER_CNTP_CTL, 1); + + ctl_val = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CTL); + /* EN set, IMASK clear, ISTAT not yet pending */ + g_assert_cmpuint(ctl_val, ==, 0x1); + + /* Step forward but not past the target */ + qtest_clock_step(global_qtest, TIMER_TEST_NS / 2); + new_count = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO); + g_assert_cmpuint(new_count, >=, current_count); + + /* Step past the target */ + qtest_clock_step(global_qtest, TIMER_TEST_NS); + new_count = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO); + g_assert_cmpuint(new_count, >=, target_count); + + qtimer_write32(qtimer_view_base, QCT_QTIMER_CNTP_CTL, 0); + + ctl_val = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CTL); + /* EN cleared, ISTAT set since new_count >= target_count */ + g_assert_cmpuint(ctl_val, ==, 0x4); + + /* + * CNTPCT runs independently of CNTP_CTL.EN: only the compare/IRQ + * logic is gated by EN, so the counter must keep advancing. + */ + qtest_clock_step(global_qtest, TIMER_TEST_NS / 2); + count_after_disable = qtimer_read64(qtimer_view_base, + QCT_QTIMER_CNTPCT_LO); + g_assert_cmpuint(count_after_disable, >, new_count); + + /* ISTAT remains set while CNTPCT >= CVAL, even with EN=0 */ + ctl_val = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CTL); + g_assert_cmpuint(ctl_val, ==, 0x4); +} + +/* Test the access-control region: CNTFRQ, CNTSR, CNTTID_0, CNTACR frame 0 */ +static void test_qtimer_ac_region(void) +{ + uint32_t freq, sr, tid0, acr0; + + freq = qtimer_read32(qtimer_ac_base, QCT_QTIMER_AC_CNTFRQ); + g_assert_cmpuint(freq, ==, QTIMER_DEFAULT_FREQ_HZ); + + /* A write of 0 to CNTFRQ must be ignored (freq-hz must stay nonzero). */ + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTFRQ, 0); + freq = qtimer_read32(qtimer_ac_base, QCT_QTIMER_AC_CNTFRQ); + g_assert_cmpuint(freq, ==, QTIMER_DEFAULT_FREQ_HZ); + + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTSR, 0x3); + sr = qtimer_read32(qtimer_ac_base, QCT_QTIMER_AC_CNTSR); + g_assert_cmpuint(sr, ==, 0x3); + + tid0 = qtimer_read32(qtimer_ac_base, QCT_QTIMER_AC_CNTTID_0); + g_assert_cmpuint(tid0, ==, 0x111); + + /* CNTACR for frame 0 defaults to full read/write permissions. */ + acr0 = qtimer_read32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START); + g_assert_cmpuint(acr0, !=, 0); + + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START, 0); + acr0 = qtimer_read32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START); + g_assert_cmpuint(acr0, ==, 0); + + /* Restore full permissions so later view-region tests keep working. */ + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START, + QCT_QTIMER_AC_CNTACR_ALL); +} + +static void test_qtimer_access_denied(void) +{ + uint32_t acr_all = QCT_QTIMER_AC_CNTACR_ALL; + uint64_t cval, saved_cval; + uint32_t val; + + /* Park CVAL at a known nonzero value while access is still permitted. */ + saved_cval = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO) + 10000; + qtimer_write64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO, saved_cval); + g_assert_cmpuint(qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO), + ==, saved_cval); + + /* Clearing RFRQ denies CNTFRQ reads. */ + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START, + acr_all & ~QCT_QTIMER_AC_CNTACR_RFRQ); + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNT_FREQ); + g_assert_cmpuint(val, ==, 0); + + /* Clearing RPCT denies CNTPCT reads, both halves. */ + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START, + acr_all & ~QCT_QTIMER_AC_CNTACR_RPCT); + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTPCT_LO); + g_assert_cmpuint(val, ==, 0); + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTPCT_HI); + g_assert_cmpuint(val, ==, 0); + + /* + * Clearing RWPT denies the whole CNTP_* set: CVAL/TVAL/CTL reads all + * read back 0 even though CVAL holds saved_cval. + */ + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START, + acr_all & ~QCT_QTIMER_AC_CNTACR_RWPT); + cval = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO); + g_assert_cmpuint(cval, ==, 0); + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTP_TVAL); + g_assert_cmpuint(val, ==, 0); + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTP_CTL); + g_assert_cmpuint(val, ==, 0); + + /* Denied writes must be dropped rather than applied. */ + qtimer_write64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO, 0x1234); + qtimer_write32(qtimer_view_base, QCT_QTIMER_CNTP_TVAL, 0x5678); + qtimer_write32(qtimer_view_base, QCT_QTIMER_CNTP_CTL, 1); + + /* Restoring RWPT reveals that CVAL still holds the pre-denial value. */ + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START, acr_all); + cval = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO); + g_assert_cmpuint(cval, ==, saved_cval); + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTP_CTL); + g_assert_cmpuint(val & 1, ==, 0); + + /* CNTFRQ and CNTPCT work again once permissions are back. */ + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNT_FREQ); + g_assert_cmpuint(val, ==, QTIMER_DEFAULT_FREQ_HZ); + + /* + * An unimplemented offset in the view region is also an access error, + * and reads back as 0. + */ + val = qtimer_read32(qtimer_view_base, 0x100); + g_assert_cmpuint(val, ==, 0); + + /* Leave the frame with full permissions for any later test. */ + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START, acr_all); +} + +static void test_qtimer_frame_irq_routing(void) +{ + unsigned int frame, other; + uint64_t frame_base; + + qtest_irq_intercept_in(global_qtest, "/machine/l2vic"); + + for (frame = 0; frame < QTIMER_NR_FRAMES; frame++) { + frame_base = qtimer_view_base + frame * QTIMER_FRAME_STRIDE; + + qtimer_write32(frame_base, QCT_QTIMER_CNTP_TVAL, TIMER_TEST_OFFSET); + qtimer_write32(frame_base, QCT_QTIMER_CNTP_CTL, + QCT_QTIMER_CNTP_CTL_ENABLE); + g_assert_false(qtest_get_irq(global_qtest, + QTIMER_L2VIC_IRQ_BASE + frame)); + + /* Step past the deadline so the frame raises its interrupt. */ + qtest_clock_step(global_qtest, TIMER_TEST_NS * 2); + g_assert_true(qtest_get_irq(global_qtest, + QTIMER_L2VIC_IRQ_BASE + frame)); + + /* No other frame's line may be disturbed. */ + for (other = 0; other < QTIMER_NR_FRAMES; other++) { + if (other != frame) { + g_assert_false(qtest_get_irq(global_qtest, + QTIMER_L2VIC_IRQ_BASE + other)); + } + } + + /* Clearing EN drops the interrupt, leaving a clean slate. */ + qtimer_write32(frame_base, QCT_QTIMER_CNTP_CTL, 0); + g_assert_false(qtest_get_irq(global_qtest, + QTIMER_L2VIC_IRQ_BASE + frame)); + } +} + +typedef struct { + const char *machine; + const struct hexagon_machine_config *cfg; +} QtimerMachineCfg; + +static const QtimerMachineCfg qtimer_machines[] = { + { "virt", &v68n_1024 }, + { "V66G_1024", &v66g_1024 }, +}; + +static void test_qtimer_on_machine(gconstpointer data) +{ + const QtimerMachineCfg *mc = data; + g_autofree char *args = g_strdup_printf( + "-machine %s -global qct-qtimer.freq-scale=1", mc->machine); + + qtimer_view_base = mc->cfg->qtmr_region; + qtimer_ac_base = mc->cfg->csr_base; + + qtest_start(args); + + test_qtimer_basic_access(); + test_qtimer_multiple_frames(); + test_qtimer_register_reads(); + test_qtimer_control_registers(); + test_qtimer_cval_access(); + test_qtimer_counter_progression(); + test_qtimer_timer_behavior(); + test_qtimer_ac_region(); + test_qtimer_access_denied(); + test_qtimer_frame_irq_routing(); + + qtest_end(); +} + +int main(int argc, char **argv) +{ + size_t i; + + g_test_init(&argc, &argv, NULL); + + for (i = 0; i < ARRAY_SIZE(qtimer_machines); i++) { + g_autofree char *path = g_strdup_printf("/qct-qtimer/%s/all-tests", + qtimer_machines[i].machine); + qtest_add_data_func(path, &qtimer_machines[i], test_qtimer_on_machine); + } + + return g_test_run(); +} diff --git a/tests/qtest/riscv-csr-test.c b/tests/qtest/riscv-csr-test.c index bb1b0ffed3..7479a5c5bc 100644 --- a/tests/qtest/riscv-csr-test.c +++ b/tests/qtest/riscv-csr-test.c @@ -20,6 +20,12 @@ #define CSR_MVENDORID 0xf11 #define CSR_MISELECT 0x350 +#define CSR_SEED 0x015 + +#define SEED_OPST_MASK (UINT64_C(0x3) << 30) +#define SEED_OPST_ES16 (UINT64_C(0x2) << 30) +#define SEED_OPST_DEAD (UINT64_C(0x3) << 30) + static void run_test_csr(void) { uint64_t res; @@ -46,12 +52,32 @@ static void run_test_csr(void) qtest_quit(qts); } +static void run_test_seed_csr(void) +{ + uint64_t val = 0; + uint64_t opst; + QTestState *qts; + + qts = qtest_init("-machine virt -cpu tt-ascalon"); + + qtest_csr_call(qts, "get_csr", 0, CSR_SEED, &val); + + opst = val & SEED_OPST_MASK; + g_assert_true(opst == SEED_OPST_ES16 || + opst == SEED_OPST_DEAD); + + g_assert_cmphex(val >> 32, ==, 0); + + qtest_quit(qts); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); if (qtest_has_machine("virt")) { qtest_add_func("/cpu/csr", run_test_csr); + qtest_add_func("/cpu/csr/seed", run_test_seed_csr); } return g_test_run(); diff --git a/tests/qtest/usb-hcd-xhci-test.c b/tests/qtest/usb-hcd-xhci-test.c index 0cccfd85a6..b58fa1e2da 100644 --- a/tests/qtest/usb-hcd-xhci-test.c +++ b/tests/qtest/usb-hcd-xhci-test.c @@ -10,6 +10,72 @@ #include "qemu/osdep.h" #include "libqtest-single.h" #include "libqos/usb.h" +#include "qobject/qdict.h" + +static void wait_device_deleted_event(QTestState *qtest, const char *id) +{ + QDict *resp, *data; + const char *device; + + /* + * 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"); + device = data ? qdict_get_try_str(data, "device") : NULL; + if (device && !strcmp(device, id)) { + qobject_unref(resp); + break; + } + qobject_unref(resp); + } +} + +/* + * Regression test for the xHCI-PCI "host" strong-link reference cycle. + * + * The xHCI PCI wrapper embeds an xhci-core child whose strong "host" link + * points back at the PCI device, forming a refcount cycle. If + * usb_xhci_pci_exit() does not break that cycle, the device's refcount never + * reaches 0 on unplug, device_finalize() never runs, and therefore the + * DEVICE_DELETED event (emitted from device_finalize()) is never sent. + * + * This test hot-plugs an xHCI controller into an ACPI-hotpluggable bus, + * requests its removal and waits for DEVICE_DELETED. Without the fix the event + * is never delivered (device_finalize() is blocked), so the test would + * hang/time out. + */ +static void test_xhci_unplug_finalize(void) +{ + QTestState *qtest; + const char *arch = qtest_get_arch(); + + if (strcmp(arch, "i386") != 0 && strcmp(arch, "x86_64") != 0) { + g_test_skip("Test only runs on x86 (ACPI PCI hotplug)"); + return; + } + if (!qtest_has_device("nec-usb-xhci")) { + g_test_skip("Device nec-usb-xhci not available"); + return; + } + + qtest = qtest_initf("-machine pc"); + + qtest_qmp_device_add(qtest, "nec-usb-xhci", "xhci-finalize", "{}"); + + /* + * Request device removal. As the guest is not running, the unplug request + * won't be processed until the next system reset, which performs the + * removal and triggers device_finalize() (and thus DEVICE_DELETED). + */ + qtest_qmp_device_del_send(qtest, "xhci-finalize"); + qtest_system_reset_nowait(qtest); + wait_device_deleted_event(qtest, "xhci-finalize"); + + qtest_quit(qtest); +} static void test_xhci_hotplug(void) { @@ -50,6 +116,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); qtest_add_func("/xhci/pci/hotplug", test_xhci_hotplug); + qtest_add_func("/xhci/pci/unplug/finalize", test_xhci_unplug_finalize); if (qtest_has_device("usb-uas")) { qtest_add_func("/xhci/pci/hotplug/usb-uas", test_usb_uas_hotplug); } diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c index cfd3c02da4..22302bd2f8 100644 --- a/tests/qtest/virtio-9p-test.c +++ b/tests/qtest/virtio-9p-test.c @@ -38,11 +38,17 @@ /* * xattr size to be used for xattr tests * - * 64k is the max. xattr size supported by the Linux kernel, However btrfs - * for instance supports only 16219 bytes. So let's be conservative and - * just use 8k for the xattr tests. + * 64k is the max. xattr size supported by the Linux kernel on high-level VFS + * layer. However filesystems impose their own limits: + * + * - btrfs: 16219 bytes + * + * - ext4: blocksize - 56 bytes (~4KB) if no ea_inode capability enabled, + * 64k if ea_inode enabled + * + * So let's be conservative and just use 1k for the xattr tests. */ -#define TEST_XATTR_SIZE (8 * 1024) +#define TEST_XATTR_SIZE (1 * 1024) static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc) { @@ -402,7 +408,7 @@ static void do_local_xattr_limit(QVirtio9P *v9p, int max_xattr) /* * this file must be created for the test to work with the 'local' fs driver */ - g_file_set_contents(test_file, "", 0, NULL); + g_assert(g_file_set_contents(test_file, "", 0, NULL)); /* the actual test code shared with the 'synth' fs driver tests */ do_xattr_limit(v9p, max_xattr, false); diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index 32f2689273..6cc81bf854 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -203,8 +203,9 @@ endif endif ifneq ($(CROSS_CC_HAS_SVE2),) -AARCH64_TESTS += test-826 -test-826: CFLAGS += $(CROSS_CC_HAS_SVE2) +SVE2_TESTS = test-826 sve-while-ptr +$(SVE2_TESTS): CFLAGS += $(CROSS_CC_HAS_SVE2) +AARCH64_TESTS += $(SVE2_TESTS) endif TESTS += $(AARCH64_TESTS) diff --git a/tests/tcg/aarch64/sve-while-ptr.c b/tests/tcg/aarch64/sve-while-ptr.c new file mode 100644 index 0000000000..7db3b9cc03 --- /dev/null +++ b/tests/tcg/aarch64/sve-while-ptr.c @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* WHILEWR / WHILERW regression test */ + +#include +#include + +int main(int argc, char **argv) +{ + unsigned short p; + int set_vl_ret; + + set_vl_ret = prctl(PR_SVE_SET_VL, 16, 0, 0, 0, 0); + assert(set_vl_ret == 16); + + p = 0xdead; + asm("whilewr p0.s, %0, %1\n\t" + "str p0, [%2]" + : : "r"(8), "r"(11), "r"(&p) : "memory", "p0"); + assert(p == 0x1111); + + p = 0xdead; + asm("whilerw p0.s, %0, %1\n\t" + "str p0, [%2]" + : : "r"(8), "r"(11), "r"(&p) : "memory", "p0"); + assert(p == 0x1111); + + return 0; +} diff --git a/tests/tcg/hexagon/Makefile.target b/tests/tcg/hexagon/Makefile.target index 70a8ec2e7f..61adf6356e 100644 --- a/tests/tcg/hexagon/Makefile.target +++ b/tests/tcg/hexagon/Makefile.target @@ -50,10 +50,16 @@ HEX_TESTS += vector_add_int HEX_TESTS += scatter_gather HEX_TESTS += hvx_misc HEX_TESTS += hvx_histogram +HEX_TESTS += fp_hvx +HEX_TESTS += fp_hvx_cvt +HEX_TESTS += fp_hvx_cmp +HEX_TESTS += fp_hvx_disabled HEX_TESTS += invalid-slots +HEX_TESTS += valid-slots HEX_TESTS += invalid-encoding HEX_TESTS += multiple-writes HEX_TESTS += unaligned_pc +HEX_TESTS += unaligned_data HEX_TESTS += test_abs HEX_TESTS += test_bitcnt @@ -108,6 +114,7 @@ preg_alias: preg_alias.c hex_test.h read_write_overlap: read_write_overlap.c hex_test.h reg_mut: reg_mut.c hex_test.h test_pnew_jump_loads: test_pnew_jump_loads.c hex_test.h +unaligned_data: unaligned_data.c hex_test.h unaligned_pc: unaligned_pc.c # Compile for v66 so that the ELF selects a v66 CPU; the test then @@ -133,6 +140,16 @@ v68_hvx: CFLAGS += -mhvx -Wno-unused-function v69_hvx: v69_hvx.c hvx_misc.h v69_hvx: CFLAGS += -mhvx -Wno-unused-function v73_scalar: CFLAGS += -Wno-unused-function +fp_hvx: fp_hvx.c hvx_misc.h hex_test.h +fp_hvx: CFLAGS += -mhvx -mhvx-ieee-fp +fp_hvx_disabled: fp_hvx_disabled.c hvx_misc.h hex_test.h +fp_hvx_disabled: CFLAGS += -mhvx -mhvx-ieee-fp +fp_hvx_cvt: fp_hvx_cvt.c hvx_misc.h hex_test.h +fp_hvx_cvt: CFLAGS += -mhvx -mhvx-ieee-fp +fp_hvx_cmp: fp_hvx_cmp.c hvx_misc.h hex_test.h +fp_hvx_cmp: CFLAGS += -mhvx -mhvx-ieee-fp + +run-fp_hvx_disabled: QEMU_OPTS += -cpu v73,ieee-fp=false hvx_histogram: hvx_histogram.c hvx_histogram_row.S $(CC) $(CFLAGS) $(CROSS_CC_GUEST_CFLAGS) $^ -o $@ $(LDFLAGS) diff --git a/tests/tcg/hexagon/fp_hvx.c b/tests/tcg/hexagon/fp_hvx.c new file mode 100644 index 0000000000..4543a0aa8c --- /dev/null +++ b/tests/tcg/hexagon/fp_hvx.c @@ -0,0 +1,226 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include +#include +#include + +int err; +#include "hvx_misc.h" + +#if __HEXAGON_ARCH__ > 75 +#error "After v75, compiler will replace some FP HVX instructions." +#endif + +/****************************************************************************** + * NAN handling + *****************************************************************************/ + +#define isnan(X) \ + (sizeof(X) == bytes_hf ? ((raw_hf(X) & ~0x8000) > 0x7c00) : \ + ((raw_sf(X) & ~(1 << 31)) > 0x7f800000UL)) + +#define CHECK_NAN(A, DEF_NAN) (isnan(A) ? DEF_NAN : (A)) +#define NAN_SF float_sf(0x7FFFFFFF) +#define NAN_HF float_hf(0x7FFF) +#define NAN_BF float_hf(0x7FFF) + +/****************************************************************************** + * Binary operations + *****************************************************************************/ + +#define DEF_TEST_OP_2(vop, op, type_res, type_arg) \ + static void test_##vop##_##type_res##_##type_arg(void) \ + { \ + memset(expect, 0xff, sizeof(expect)); \ + memset(output, 0xff, sizeof(output)); \ + for (int i = 0; i < BUFSIZE; i++) { \ + HVX_Vector *hvx_output = (HVX_Vector *)&output[i]; \ + HVX_Vector hvx_buffer0 = *(HVX_Vector *)&buffer0[i]; \ + HVX_Vector hvx_buffer1 = *(HVX_Vector *)&buffer1[i]; \ + *hvx_output = \ + Q6_V##type_res##_##vop##_V##type_arg##V##type_arg(hvx_buffer0, \ + hvx_buffer1); \ + for (int j = 0; j < MAX_VEC_SIZE_BYTES / bytes_##type_res; j++) { \ + expect[i].type_res[j] = \ + raw_##type_res(op(float_##type_arg(buffer0[i].type_arg[j]), \ + float_##type_arg(buffer1[i].type_arg[j]))); \ + } \ + } \ + check_output_##type_res(__LINE__, BUFSIZE); \ + } + +#define SUM(X, Y, DEF_NAN) CHECK_NAN((X) + (Y), DEF_NAN) +#define SUB(X, Y, DEF_NAN) CHECK_NAN((X) - (Y), DEF_NAN) +#define MULT(X, Y, DEF_NAN) CHECK_NAN((X) * (Y), DEF_NAN) + +#define SUM_SF(X, Y) SUM(X, Y, NAN_SF) +#define SUM_HF(X, Y) SUM(X, Y, NAN_HF) +#define SUB_SF(X, Y) SUB(X, Y, NAN_SF) +#define SUB_HF(X, Y) SUB(X, Y, NAN_HF) +#define MULT_SF(X, Y) MULT(X, Y, NAN_SF) +#define MULT_HF(X, Y) MULT(X, Y, NAN_HF) + +DEF_TEST_OP_2(vadd, SUM_SF, sf, sf); +DEF_TEST_OP_2(vadd, SUM_HF, hf, hf); +DEF_TEST_OP_2(vsub, SUB_SF, sf, sf); +DEF_TEST_OP_2(vsub, SUB_HF, hf, hf); +DEF_TEST_OP_2(vmpy, MULT_SF, sf, sf); +DEF_TEST_OP_2(vmpy, MULT_HF, hf, hf); + +#define signbit_fp(X) \ + (sizeof(X) == bytes_hf ? ((raw_hf(X) & 0x8000) != 0) : \ + ((raw_sf(X) & 0x80000000) != 0)) + +#define STD_MIN(X, Y) ((X) < (Y) ? (X) : (Y)) +#define STD_MAX(X, Y) ((X) > (Y) ? (X) : (Y)) + +#define MIN(X, Y, DEF_NAN) \ + ((isnan(X) || isnan(Y)) ? DEF_NAN : \ + ((X) != (Y)) ? STD_MIN(X, Y) : (signbit_fp(X) ? (X) : (Y))) /* -0 < +0 */ +#define MAX(X, Y, DEF_NAN) \ + ((isnan(X) || isnan(Y)) ? DEF_NAN : \ + ((X) != (Y)) ? STD_MAX(X, Y) : (signbit_fp(X) ? (Y) : (X))) /* -0 < +0 */ + +#define MIN_HF(X, Y) MIN(X, Y, NAN_HF) +#define MAX_HF(X, Y) MAX(X, Y, NAN_HF) +#define MIN_SF(X, Y) MIN(X, Y, NAN_SF) +#define MAX_SF(X, Y) MAX(X, Y, NAN_SF) +#define MIN_BF(X, Y) MIN(X, Y, NAN_BF) +#define MAX_BF(X, Y) MAX(X, Y, NAN_BF) + +DEF_TEST_OP_2(vfmin, MIN_SF, sf, sf); +DEF_TEST_OP_2(vfmax, MAX_SF, sf, sf); +DEF_TEST_OP_2(vfmin, MIN_HF, hf, hf); +DEF_TEST_OP_2(vfmax, MAX_HF, hf, hf); +DEF_TEST_OP_2(vmin, MIN_BF, bf, bf); +DEF_TEST_OP_2(vmax, MAX_BF, bf, bf); + +#define DEF_TEST_OP_2_INTERLEAVED(vop, op, type_res, type_arg) \ + static void test_##vop##_##type_res##_##type_arg(void) \ + { \ + memset(expect, 0xff, sizeof(expect)); \ + memset(output, 0xff, sizeof(output)); \ + for (int i = 0; i < BUFSIZE / 2; i++) { \ + HVX_VectorPair *hvx_output = (HVX_VectorPair *)&output[2 * i]; \ + HVX_Vector hvx_buffer0 = *(HVX_Vector *)&buffer0[i]; \ + HVX_Vector hvx_buffer1 = *(HVX_Vector *)&buffer1[i]; \ + *hvx_output = \ + Q6_W##type_res##_##vop##_V##type_arg##V##type_arg(hvx_buffer0, \ + hvx_buffer1); \ + for (int j = 0; j < MAX_VEC_SIZE_BYTES / bytes_##type_res; j++) { \ + expect[2 * i].type_res[j] = \ + raw_##type_res(op(float_##type_arg(buffer0[i].type_arg[2 * j]), \ + float_##type_arg(buffer1[i].type_arg[2 * j]))); \ + expect[2 * i + 1].type_res[j] = \ + raw_##type_res(op(float_##type_arg(buffer0[i].type_arg[2 * j + 1]), \ + float_##type_arg(buffer1[i].type_arg[2 * j + 1]))); \ + } \ + } \ + check_output_##type_res(__LINE__, BUFSIZE); \ + } + +DEF_TEST_OP_2_INTERLEAVED(vadd, SUM_SF, sf, bf); +DEF_TEST_OP_2_INTERLEAVED(vsub, SUB_SF, sf, bf); +DEF_TEST_OP_2_INTERLEAVED(vmpy, MULT_SF, sf, bf); + +/****************************************************************************** + * Other tests + *****************************************************************************/ + +static void test_vdmpy_sf_hf(bool acc) +{ + memset(expect, 0xff, sizeof(expect)); + + for (int i = 0; i < BUFSIZE; i++) { + HVX_Vector hvx_buffer0 = *(HVX_Vector *)&buffer0[i]; + HVX_Vector hvx_buffer1 = *(HVX_Vector *)&buffer1[i]; + HVX_Vector *hvx_output = (HVX_Vector *)&output[i]; + + uint32_t PREFIL_VAL = 0x111222; + *hvx_output = Q6_V_vsplat_R(PREFIL_VAL); + + if (!acc) { + *hvx_output = Q6_Vsf_vdmpy_VhfVhf(hvx_buffer0, hvx_buffer1); + } else { + *hvx_output = Q6_Vsf_vdmpyacc_VsfVhfVhf(*hvx_output, hvx_buffer0, + hvx_buffer1); + } + + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { + float a1 = float_hf_to_sf(float_hf(buffer0[i].hf[2 * j + 1])); + float a2 = float_hf_to_sf(float_hf(buffer0[i].hf[2 * j])); + float a3 = float_hf_to_sf(float_hf(buffer1[i].hf[2 * j + 1])); + float a4 = float_hf_to_sf(float_hf(buffer1[i].hf[2 * j])); + /* + * Note, IEEE FP specifies +0.0 + -0.0 == +0.0. So we use -0.0 in + * the default case to preserve the zero sign. + */ + float prev = acc ? float_sf(PREFIL_VAL) : -0.0; + expect[i].sf[j] = raw_sf(CHECK_NAN((a1 * a3) + (a2 * a4) + prev, NAN_SF)); + } + } + check_output_sf(__LINE__, BUFSIZE); +} + +static void test_new(void) +{ + asm volatile("r0 = #%2\n" + "v0 = vsplat(r0)\n" + "vmem(%1 + #0) = v0\n" + "r1 = #%3\n" + "v1 = vsplat(r1)\n" + "v2 = vsplat(r1)\n" + "{\n" + " v0.sf = vadd(v1.sf, v2.sf)\n" + " vmem(%0 + #0) = v0.new\n" + "}\n" + : + : "r"(output), "r"(expect), "i"(SF_two), "i"(SF_one) + : "r0", "r1", "v0", "v1", "v2", "memory"); + check_output_w(__LINE__, 1); +} + +int main(void) +{ + init_buffers_fp(); + + /* add/sub */ + test_vadd_sf_sf(); + test_vadd_hf_hf(); + test_vsub_sf_sf(); + test_vsub_hf_hf(); + + /* multiply */ + test_vmpy_sf_sf(); + test_vmpy_hf_hf(); + + /* dot product */ + test_vdmpy_sf_hf(false); + test_vdmpy_sf_hf(true); + + test_new(); + + /* min/max */ + test_vfmin_sf_sf(); + test_vfmin_hf_hf(); + test_vfmax_sf_sf(); + test_vfmax_hf_hf(); + + /* bfloat */ + init_buffers_bf(); + test_vmin_bf_bf(); + test_vmax_bf_bf(); + test_vadd_sf_bf(); + test_vsub_sf_bf(); + test_vmpy_sf_bf(); + + puts(err ? "FAIL" : "PASS"); + return err ? 1 : 0; +} diff --git a/tests/tcg/hexagon/fp_hvx_cmp.c b/tests/tcg/hexagon/fp_hvx_cmp.c new file mode 100644 index 0000000000..63fb423941 --- /dev/null +++ b/tests/tcg/hexagon/fp_hvx_cmp.c @@ -0,0 +1,275 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include +#include +#include +#include + +#if __HEXAGON_ARCH__ > 75 +#error "After v75, compiler will replace some FP HVX instructions." +#endif + +int err; +#include "hvx_misc.h" +#include "hex_test.h" + +#define MAX_TESTS_hf (MAX_VEC_SIZE_BYTES / 2) +#define MAX_TESTS_sf (MAX_VEC_SIZE_BYTES / 4) +#define MAX_TESTS_bf (MAX_VEC_SIZE_BYTES / 2) + +#define TRUE_MASK_sf 0xffffffff +#define TRUE_MASK_hf 0xffff +#define TRUE_MASK_bf 0xffff + +static const char *comparisons[MAX_TESTS_sf][2]; +static HVX_Vector *hvx_output = (HVX_Vector *)&output[0]; +static HVX_Vector buffers[2], true_vec, false_vec; +static int exp_index; + +#define ADD_TEST_CMP(TYPE, VAL1, VAL2, EXP) do { \ + ((MMVector *)&buffers[0])->TYPE[exp_index] = VAL1; \ + ((MMVector *)&buffers[1])->TYPE[exp_index] = VAL2; \ + expect[0].TYPE[exp_index] = EXP ? TRUE_MASK_##TYPE : 0; \ + comparisons[exp_index][0] = #VAL1; \ + comparisons[exp_index][1] = #VAL2; \ + assert(exp_index < MAX_TESTS_##TYPE); \ + exp_index++; \ +} while (0) + +#define TEST_CMP_GT(TYPE, VAL1, VAL2) do { \ + ADD_TEST_CMP(TYPE, VAL1, VAL2, true); \ + ADD_TEST_CMP(TYPE, VAL2, VAL1, false); \ +} while (0) + +#define PREP_TEST() do { \ + memset(&buffers, 0, sizeof(buffers)); \ + memset(expect, 0, sizeof(expect)); \ + exp_index = 0; \ +} while (0) + +#define CHECK(TYPE, TYPESZ) do { \ + HVX_VectorPred pred = Q6_Q_vcmp_gt_V##TYPE##V##TYPE(buffers[0], buffers[1]); \ + *hvx_output = Q6_V_vmux_QVV(pred, true_vec, false_vec); \ + for (int j = 0; j < MAX_VEC_SIZE_BYTES / TYPESZ; j++) { \ + if (output[0].TYPE[j] != expect[0].TYPE[j]) { \ + printf("ERROR: expected %s %s %s\n", comparisons[j][0], \ + (expect[0].TYPE[j] != 0 ? ">" : "<="), comparisons[j][1]); \ + err++; \ + } \ + } \ +} while (0) + +static void test_cmp_sf(void) +{ + /* + * General ordering for sf: + * QNaN > SNaN > +Inf > numbers > -Inf > SNaN_neg > QNaN_neg + */ + + /* Test equality */ + PREP_TEST(); + ADD_TEST_CMP(sf, raw_sf(2.2), raw_sf(2.2), false); + ADD_TEST_CMP(sf, SF_SNaN, SF_SNaN, false); + CHECK(sf, 4); + + /* Common numbers */ + PREP_TEST(); + TEST_CMP_GT(sf, raw_sf(2.2), raw_sf(2.1)); + TEST_CMP_GT(sf, raw_sf(0), raw_sf(-2.2)); + CHECK(sf, 4); + + /* Infinity vs Infinity/NaN */ + PREP_TEST(); + TEST_CMP_GT(sf, SF_QNaN, SF_INF); + TEST_CMP_GT(sf, SF_SNaN, SF_INF); + TEST_CMP_GT(sf, SF_INF, SF_INF_neg); + TEST_CMP_GT(sf, SF_INF, SF_SNaN_neg); + TEST_CMP_GT(sf, SF_INF, SF_QNaN_neg); + TEST_CMP_GT(sf, SF_INF_neg, SF_SNaN_neg); + TEST_CMP_GT(sf, SF_INF_neg, SF_QNaN_neg); + TEST_CMP_GT(sf, SF_SNaN, SF_INF_neg); + TEST_CMP_GT(sf, SF_QNaN, SF_INF_neg); + CHECK(sf, 4); + + /* NaN vs NaN */ + PREP_TEST(); + TEST_CMP_GT(sf, SF_QNaN, SF_SNaN); + TEST_CMP_GT(sf, SF_SNaN, SF_SNaN_neg); + TEST_CMP_GT(sf, SF_SNaN_neg, SF_QNaN_neg); + CHECK(sf, 4); + + /* NaN vs non-NaN */ + PREP_TEST(); + TEST_CMP_GT(sf, SF_QNaN, SF_one); + TEST_CMP_GT(sf, SF_SNaN, SF_one); + TEST_CMP_GT(sf, SF_one, SF_QNaN_neg); + TEST_CMP_GT(sf, SF_one, SF_SNaN_neg); + CHECK(sf, 4); +} + +static void test_cmp_hf(void) +{ + /* + * General ordering for hf: + * QNaN > SNaN > +Inf > numbers > -Inf > QSNaN_neg > QNaN_neg + */ + + /* Test equality */ + PREP_TEST(); + ADD_TEST_CMP(hf, raw_hf((_Float16)2.2), raw_hf((_Float16)2.2), false); + ADD_TEST_CMP(hf, HF_SNaN, HF_SNaN, false); + CHECK(hf, 2); + + /* Common numbers */ + PREP_TEST(); + TEST_CMP_GT(hf, raw_hf((_Float16)2.2), raw_hf((_Float16)2.1)); + TEST_CMP_GT(hf, raw_hf((_Float16)0), raw_hf((_Float16) - 2.2)); + CHECK(hf, 2); + + /* Infinity vs Infinity/NaN */ + PREP_TEST(); + TEST_CMP_GT(hf, HF_QNaN, HF_INF); + TEST_CMP_GT(hf, HF_SNaN, HF_INF); + TEST_CMP_GT(hf, HF_INF, HF_INF_neg); + TEST_CMP_GT(hf, HF_INF, HF_SNaN_neg); + TEST_CMP_GT(hf, HF_INF, HF_QNaN_neg); + TEST_CMP_GT(hf, HF_INF_neg, HF_SNaN_neg); + TEST_CMP_GT(hf, HF_INF_neg, HF_QNaN_neg); + TEST_CMP_GT(hf, HF_SNaN, HF_INF_neg); + TEST_CMP_GT(hf, HF_QNaN, HF_INF_neg); + CHECK(hf, 2); + + /* NaN vs NaN */ + PREP_TEST(); + TEST_CMP_GT(hf, HF_QNaN, HF_SNaN); + TEST_CMP_GT(hf, HF_SNaN, HF_SNaN_neg); + TEST_CMP_GT(hf, HF_SNaN_neg, HF_QNaN_neg); + CHECK(hf, 2); + + /* NaN vs non-NaN */ + PREP_TEST(); + TEST_CMP_GT(hf, HF_QNaN, HF_one); + TEST_CMP_GT(hf, HF_SNaN, HF_one); + TEST_CMP_GT(hf, HF_one, HF_QNaN_neg); + TEST_CMP_GT(hf, HF_one, HF_SNaN_neg); + CHECK(hf, 2); +} + +static void test_cmp_bf(void) +{ + /* + * General ordering for bf: + * QNaN > SNaN > +Inf > numbers > -Inf > SNaN_neg > QNaN_neg + */ + + /* Test equality */ + PREP_TEST(); + ADD_TEST_CMP(bf, 0, 0, false); + ADD_TEST_CMP(bf, BF_SNaN, BF_SNaN, false); + CHECK(bf, 2); + + /* Common numbers */ + PREP_TEST(); + TEST_CMP_GT(bf, BF_two, BF_one); + TEST_CMP_GT(bf, BF_one, BF_zero); + CHECK(bf, 2); + + /* Infinity vs Infinity/NaN */ + PREP_TEST(); + TEST_CMP_GT(bf, BF_QNaN, BF_INF); + TEST_CMP_GT(bf, BF_SNaN, BF_INF); + TEST_CMP_GT(bf, BF_INF, BF_INF_neg); + TEST_CMP_GT(bf, BF_INF, BF_SNaN_neg); + TEST_CMP_GT(bf, BF_INF, BF_QNaN_neg); + TEST_CMP_GT(bf, BF_INF_neg, BF_SNaN_neg); + TEST_CMP_GT(bf, BF_INF_neg, BF_QNaN_neg); + TEST_CMP_GT(bf, BF_SNaN, BF_INF_neg); + TEST_CMP_GT(bf, BF_QNaN, BF_INF_neg); + CHECK(bf, 2); + + /* NaN vs NaN */ + PREP_TEST(); + TEST_CMP_GT(bf, BF_QNaN, BF_SNaN); + TEST_CMP_GT(bf, BF_SNaN, BF_SNaN_neg); + TEST_CMP_GT(bf, BF_SNaN_neg, BF_QNaN_neg); + CHECK(bf, 2); + + /* NaN vs non-NaN */ + PREP_TEST(); + TEST_CMP_GT(bf, BF_QNaN, BF_one); + TEST_CMP_GT(bf, BF_SNaN, BF_one); + TEST_CMP_GT(bf, BF_one, BF_QNaN_neg); + TEST_CMP_GT(bf, BF_one, BF_SNaN_neg); + CHECK(bf, 2); +} + +static void check_byte_pred(HVX_VectorPred pred, int byte_idx, uint8_t exp_mask, + int line) +{ + /* + * Note: ((uint8_t *)&pred)[N] returns the expanded value of bit N: + * 0xFF if bit is set, 0x00 if clear. + */ + for (int i = 0; i < 8; i++) { + int idx = byte_idx * 8 + i; + int val = ((uint8_t *)&pred)[idx]; + int exp = (exp_mask >> i) & 1 ? 0xff : 0x00; + if (exp != val) { + printf("ERROR line %d: pred bit %d is 0x%x, should be 0x%x\n", + line, idx, val, exp); + err++; + } + } +} + +#define CHECK_BYTE_PRED(PRED, BYTE, EXP) check_byte_pred(PRED, BYTE, EXP, __LINE__) + +static void test_cmp_variants(void) +{ + HVX_VectorPred pred; + + /* + * Setup: comparison result will have bits 4-7 set (0xF0 in pred byte 0) + * - sf[0]: SF_zero > SF_one = false -> bits 0-3 = 0 + * - sf[1]: SF_one > SF_zero = true -> bits 4-7 = 1 + */ + PREP_TEST(); + ADD_TEST_CMP(sf, SF_zero, SF_one, false); + ADD_TEST_CMP(sf, SF_one, SF_zero, true); + + /* greater and: 0xF0 & 0xF0 = 0xF0 */ + memset(&pred, 0xF0, sizeof(pred)); + pred = Q6_Q_vcmp_gtand_QVsfVsf(pred, buffers[0], buffers[1]); + CHECK_BYTE_PRED(pred, 0, 0xF0); + + /* greater or: 0x0F | 0xF0 = 0xFF */ + memset(&pred, 0x0F, sizeof(pred)); + pred = Q6_Q_vcmp_gtor_QVsfVsf(pred, buffers[0], buffers[1]); + CHECK_BYTE_PRED(pred, 0, 0xFF); + + /* greater xor: 0xFF ^ 0xF0 = 0x0F */ + memset(&pred, 0xFF, sizeof(pred)); + pred = Q6_Q_vcmp_gtxacc_QVsfVsf(pred, buffers[0], buffers[1]); + CHECK_BYTE_PRED(pred, 0, 0x0F); +} + +int main(void) +{ + memset(&true_vec, 0xff, sizeof(true_vec)); + memset(&false_vec, 0, sizeof(false_vec)); + + test_cmp_sf(); + test_cmp_hf(); + test_cmp_bf(); + test_cmp_variants(); + + puts(err ? "FAIL" : "PASS"); + return err ? 1 : 0; +} diff --git a/tests/tcg/hexagon/fp_hvx_cvt.c b/tests/tcg/hexagon/fp_hvx_cvt.c new file mode 100644 index 0000000000..7196bc9a33 --- /dev/null +++ b/tests/tcg/hexagon/fp_hvx_cvt.c @@ -0,0 +1,219 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include +#include +#include + +#if __HEXAGON_ARCH__ > 75 +#error "After v75, compiler will replace some FP HVX instructions." +#endif + +int err; +#include "hvx_misc.h" +#include "hex_test.h" + +#define NAN_BF 0x7FFF + +#define TEST_EXP(TO, FROM, VAL, EXP) do { \ + ((MMVector *)&buffer)->FROM[index] = VAL; \ + expect[0].TO[index] = EXP; \ + index++; \ +} while (0) + +#define DEF_TEST_CVT(TO, FROM, TESTS) \ + static void test_vcvt_##TO##_##FROM(void) \ + { \ + HVX_Vector *hvx_output = (HVX_Vector *)&output[0]; \ + HVX_Vector buffer; \ + int index = 0; \ + memset(&buffer, 0, sizeof(buffer)); \ + memset(expect, 0, sizeof(expect)); \ + TESTS \ + * hvx_output = Q6_V##TO##_vcvt_V##FROM(buffer); \ + check_output_##TO(__LINE__, 1); \ + } + +DEF_TEST_CVT(uh, hf, { \ + TEST_EXP(uh, hf, HF_QNaN, UINT16_MAX); \ + TEST_EXP(uh, hf, HF_SNaN, UINT16_MAX); \ + TEST_EXP(uh, hf, HF_QNaN_neg, UINT16_MAX); \ + TEST_EXP(uh, hf, HF_INF, UINT16_MAX); \ + TEST_EXP(uh, hf, HF_INF_neg, 0); \ + TEST_EXP(uh, hf, HF_neg_two, 0); \ + TEST_EXP(uh, hf, HF_zero_neg, 0); \ + TEST_EXP(uh, hf, raw_hf((_Float16)2.1), 2); \ + TEST_EXP(uh, hf, HF_one_recip, 1); \ +}) + +DEF_TEST_CVT(h, hf, { \ + TEST_EXP(h, hf, HF_QNaN, INT16_MAX); \ + TEST_EXP(h, hf, HF_SNaN, INT16_MAX); \ + TEST_EXP(h, hf, HF_QNaN_neg, INT16_MAX); \ + TEST_EXP(h, hf, HF_INF, INT16_MAX); \ + TEST_EXP(h, hf, HF_INF_neg, INT16_MIN); \ + TEST_EXP(h, hf, HF_neg_two, -2); \ + TEST_EXP(h, hf, HF_zero_neg, 0); \ + TEST_EXP(h, hf, raw_hf((_Float16)2.1), 2); \ + TEST_EXP(h, hf, HF_one_recip, 1); \ +}) + +/* + * Some cvt operations take two vectors as input and perform the following: + * VdV.TO[4*i] = OP(VuV.FROM[2*i]); + * VdV.TO[4*i+1] = OP(VuV.FROM[2*i+1]); + * VdV.TO[4*i+2] = OP(VvV.FROM[2*i]); + * VdV.TO[4*i+3] = OP(VvV.FROM[2*i+1])) + * We use bf_index and index in a way that the tests are always done either + * using the first or third line of the above snippet. + */ +#define TEST_EXP_2(TO, FROM, VAL, EXP) do { \ + ((MMVector *)&buffers[bf_index])->FROM[2 * index] = VAL; \ + expect[0].TO[(4 * index) + (2 * bf_index)] = EXP; \ + index++; \ + bf_index = (bf_index + 1) % 2; \ +} while (0) + +#define DEF_TEST_CVT_2(TO, FROM, TESTS) \ + static void test_vcvt_##TO##_##FROM(void) \ + { \ + HVX_Vector *hvx_output = (HVX_Vector *)&output[0]; \ + HVX_Vector buffers[2]; \ + int index = 0, bf_index = 0; \ + memset(&buffers, 0, sizeof(buffers)); \ + memset(expect, 0, sizeof(expect)); \ + TESTS \ + * hvx_output = Q6_V##TO##_vcvt_V##FROM##V##FROM(buffers[0], buffers[1]); \ + check_output_##TO(__LINE__, 1); \ + } + +DEF_TEST_CVT_2(ub, hf, { \ + TEST_EXP_2(ub, hf, HF_QNaN, UINT8_MAX); \ + TEST_EXP_2(ub, hf, HF_SNaN, UINT8_MAX); \ + TEST_EXP_2(ub, hf, HF_QNaN_neg, UINT8_MAX); \ + TEST_EXP_2(ub, hf, HF_INF, UINT8_MAX); \ + TEST_EXP_2(ub, hf, HF_INF_neg, 0); \ + TEST_EXP_2(ub, hf, HF_small_neg, 0); \ + TEST_EXP_2(ub, hf, HF_neg_two, 0); \ + TEST_EXP_2(ub, hf, HF_zero_neg, 0); \ + TEST_EXP_2(ub, hf, raw_hf((_Float16)2.1), 2); \ + TEST_EXP_2(ub, hf, HF_one_recip, 1); \ +}) + +DEF_TEST_CVT_2(b, hf, { \ + TEST_EXP_2(b, hf, HF_QNaN, INT8_MAX); \ + TEST_EXP_2(b, hf, HF_SNaN, INT8_MAX); \ + TEST_EXP_2(b, hf, HF_QNaN_neg, INT8_MAX); \ + TEST_EXP_2(b, hf, HF_INF, INT8_MAX); \ + TEST_EXP_2(b, hf, HF_INF_neg, INT8_MIN); \ + TEST_EXP_2(b, hf, HF_small_neg, 0); \ + TEST_EXP_2(b, hf, HF_neg_two, -2); \ + TEST_EXP_2(b, hf, HF_zero_neg, 0); \ + TEST_EXP_2(b, hf, raw_hf((_Float16)2.1), 2); \ + TEST_EXP_2(b, hf, HF_one_recip, 1); \ +}) + +#define DEF_TEST_VCONV(TO, FROM, TESTS) \ + static void test_vconv_##TO##_##FROM(void) \ + { \ + HVX_Vector *hvx_output = (HVX_Vector *)&output[0]; \ + HVX_Vector buffer; \ + int index = 0; \ + memset(&buffer, 0, sizeof(buffer)); \ + memset(expect, 0, sizeof(expect)); \ + TESTS \ + * hvx_output = Q6_V##TO##_equals_V##FROM(buffer); \ + check_output_##TO(__LINE__, 1); \ + } + +DEF_TEST_VCONV(w, sf, { \ + TEST_EXP(w, sf, SF_QNaN, INT32_MAX); \ + TEST_EXP(w, sf, SF_SNaN, INT32_MAX); \ + TEST_EXP(w, sf, SF_QNaN_neg, INT32_MIN); \ + TEST_EXP(w, sf, SF_INF, INT32_MAX); \ + TEST_EXP(w, sf, SF_INF_neg, INT32_MIN); \ + TEST_EXP(w, sf, SF_small_neg, 0); \ + TEST_EXP(w, sf, SF_neg_two, -2); \ + TEST_EXP(w, sf, SF_zero_neg, 0); \ + TEST_EXP(w, sf, raw_sf(2.1f), 2); \ + TEST_EXP(w, sf, raw_sf(2.8f), 2); \ +}) + +DEF_TEST_VCONV(h, hf, { \ + TEST_EXP(h, hf, HF_QNaN, INT16_MAX); \ + TEST_EXP(h, hf, HF_SNaN, INT16_MAX); \ + TEST_EXP(h, hf, HF_QNaN_neg, INT16_MIN); \ + TEST_EXP(h, hf, HF_INF, INT16_MAX); \ + TEST_EXP(h, hf, HF_INF_neg, INT16_MIN); \ + TEST_EXP(h, hf, HF_small_neg, 0); \ + TEST_EXP(h, hf, HF_neg_two, -2); \ + TEST_EXP(h, hf, HF_zero_neg, 0); \ + TEST_EXP(h, hf, raw_hf((_Float16)2.1), 2); \ + TEST_EXP(h, hf, raw_hf((_Float16)2.8), 2); \ +}) + +DEF_TEST_VCONV(hf, h, { \ + TEST_EXP(hf, h, 0, HF_zero); \ + TEST_EXP(hf, h, 2, HF_two); \ + TEST_EXP(hf, h, -2, HF_neg_two); \ + TEST_EXP(hf, h, 2049, raw_hf((_Float16)2048)); /* rounds DOWN */ \ + TEST_EXP(hf, h, 2051, raw_hf((_Float16)2052)); /* rounds UP */ \ +}) + +DEF_TEST_VCONV(sf, w, { \ + TEST_EXP(sf, w, 0, SF_zero); \ + TEST_EXP(sf, w, 2, SF_two); \ + TEST_EXP(sf, w, -2, SF_neg_two); \ + TEST_EXP(sf, w, 16777217, raw_sf((float)16777216)); /* rounds DOWN */ \ + TEST_EXP(sf, w, 16777219, raw_sf((float)16777220)); /* rounds UP */ \ +}) + +#define TEST_EXP_BF(VAL, EXP) do { \ + ((MMVector *)&buffers[1])->sf[index] = VAL; \ + ((MMVector *)&buffers[0])->sf[index] = VAL; \ + expect[0].bf[2 * index] = EXP; \ + expect[0].bf[2 * index + 1] = EXP; \ + index++; \ +} while (0) + +static void test_vconv_bf_sf(void) +{ + HVX_Vector *hvx_output = (HVX_Vector *)&output[0]; + HVX_Vector buffers[2]; + int index = 0; + memset(&buffers, 0, sizeof(buffers)); + memset(expect, 0, sizeof(expect)); + + TEST_EXP_BF(SF_QNaN, NAN_BF); + TEST_EXP_BF(SF_SNaN, NAN_BF); + TEST_EXP_BF(SF_QNaN_neg, NAN_BF); + TEST_EXP_BF(SF_INF, BF_INF); + TEST_EXP_BF(SF_INF_neg, BF_INF_neg); + TEST_EXP_BF(SF_one, BF_one); + TEST_EXP_BF(SF_zero_neg, BF_zero_neg); + + *hvx_output = Q6_Vbf_vcvt_VsfVsf(buffers[0], buffers[1]); + check_output_hf(__LINE__, 1); +} + +int main(void) +{ + test_vcvt_uh_hf(); + test_vcvt_h_hf(); + test_vcvt_ub_hf(); + test_vcvt_b_hf(); + test_vconv_w_sf(); + test_vconv_sf_w(); + test_vconv_h_hf(); + test_vconv_hf_h(); + test_vconv_bf_sf(); + + puts(err ? "FAIL" : "PASS"); + return err ? 1 : 0; +} diff --git a/tests/tcg/hexagon/fp_hvx_disabled.c b/tests/tcg/hexagon/fp_hvx_disabled.c new file mode 100644 index 0000000000..388a42e2b7 --- /dev/null +++ b/tests/tcg/hexagon/fp_hvx_disabled.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include + +int err; +#include "hvx_misc.h" + +static void test_disabled(void) +{ + memset(output, 0xAA, sizeof(output)); + memset(expect, 0, sizeof(expect)); + asm volatile("r0 = #0xff\n" + "v0 = vsplat(r0)\n" + "r1 = #0x1\n" + "v1 = vsplat(r1)\n" + "v2 = vsplat(r1)\n" + "v0.sf = vadd(v1.sf, v2.sf)\n" + "vmem(%0 + #0) = v0\n" + : + : "r"(output) + : "r0", "r1", "v0", "v1", "v2", "memory"); + check_output_w(__LINE__, 1); +} + +static void test_disabled_with_new(void) +{ + memset(output, 0xAA, sizeof(output)); + memset(expect, 0, sizeof(expect)); + asm volatile("r0 = #0xff\n" + "v0 = vsplat(r0)\n" + "r1 = #0x1\n" + "v1 = vsplat(r1)\n" + "v2 = vsplat(r1)\n" + "{\n" + " v0.sf = vadd(v1.sf, v2.sf)\n" + " vmem(%0 + #0) = v0.new\n" + "}\n" + : + : "r"(output) + : "r0", "r1", "v0", "v1", "v2", "memory"); + check_output_w(__LINE__, 1); +} + +int main(void) +{ + test_disabled(); + test_disabled_with_new(); + puts(err ? "FAIL" : "PASS"); + return err ? 1 : 0; +} diff --git a/tests/tcg/hexagon/hex_test.h b/tests/tcg/hexagon/hex_test.h index cfed06a58b..f86e6e1a69 100644 --- a/tests/tcg/hexagon/hex_test.h +++ b/tests/tcg/hexagon/hex_test.h @@ -19,6 +19,8 @@ #ifndef HEX_TEST_H #define HEX_TEST_H +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + static inline void __check32(int line, uint32_t val, uint32_t expect) { if (val != expect) { @@ -109,7 +111,36 @@ static inline void __check64_ne(int line, uint64_t val, uint64_t expect) "usr = r2\n\t" /* Some useful floating point values */ +const uint16_t HF_INF = 0x7c00; +const uint16_t HF_INF_neg = 0xfc00; +const uint16_t HF_QNaN = 0x7e00; +const uint16_t HF_SNaN = 0x7d00; +const uint16_t HF_SNaN_neg = 0xfd00; +const uint16_t HF_QNaN_neg = 0xfe00; +const uint16_t HF_zero = 0x0000; +const uint16_t HF_zero_neg = 0x8000; +const uint16_t HF_one = 0x3c00; +const uint16_t HF_one_recip = 0x3bf9; +const uint16_t HF_two = 0x4000; +const uint16_t HF_small_neg = 0x8010; +const uint16_t HF_any = 0x3c00; +const uint16_t HF_neg_two = 0xc000; + +const uint16_t BF_INF = 0x7f80; +const uint16_t BF_INF_neg = 0xff80; +const uint16_t BF_QNaN = 0x7fc0; +const uint16_t BF_SNaN = 0x7f81; +const uint16_t BF_QNaN_neg = 0xffc0; +const uint16_t BF_SNaN_neg = 0xff81; +const uint16_t BF_HEX_NaN = 0x7fff; +const uint16_t BF_zero = 0x0000; +const uint16_t BF_zero_neg = 0x8000; +const uint16_t BF_one = 0x3f80; +const uint16_t BF_two = 0x4000; +const uint16_t BF_four = 0x4080; + const uint32_t SF_INF = 0x7f800000; +const uint32_t SF_INF_neg = 0xff800000; const uint32_t SF_QNaN = 0x7fc00000; const uint32_t SF_QNaN_special = 0x7f800001; const uint32_t SF_SNaN = 0x7fb00000; @@ -128,6 +159,7 @@ const uint32_t SF_large_pos = 0x5afa572e; const uint32_t SF_any = 0x3f800000; const uint32_t SF_denorm = 0x00000001; const uint32_t SF_random = 0x346001d6; +const uint32_t SF_neg_two = 0xc0000000; const uint64_t DF_QNaN = 0x7ff8000000000000ULL; const uint64_t DF_SNaN = 0x7ff7000000000000ULL; diff --git a/tests/tcg/hexagon/hvx_misc.c b/tests/tcg/hexagon/hvx_misc.c index 90c3733da0..32a3661a86 100644 --- a/tests/tcg/hexagon/hvx_misc.c +++ b/tests/tcg/hexagon/hvx_misc.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include int err; @@ -315,6 +317,34 @@ TEST_VEC_OP2(vand, vand, , d, 8, &) TEST_VEC_OP2(vor, vor, , d, 8, |) TEST_VEC_OP1(vnot, vnot, , d, 8, ~) +#define TEST_VEC_ABSDIFF(NAME, INTRINSIC, SRC_FIELD, DST_FIELD, \ + CHECK_FIELD, FIELDSZ) \ +static inline void test_##NAME(void) \ +{ \ + HVX_Vector v0; \ + HVX_Vector v1; \ + HVX_Vector vres; \ + for (int i = 0; i < BUFSIZE; i++) { \ + memcpy(&v0, &buffer0[i], sizeof(MMVector)); \ + memcpy(&v1, &buffer1[i], sizeof(MMVector)); \ + vres = INTRINSIC(v0, v1); \ + memcpy(&output[i], &vres, sizeof(MMVector)); \ + } \ + for (int i = 0; i < BUFSIZE; i++) { \ + for (int j = 0; j < MAX_VEC_SIZE_BYTES / FIELDSZ; j++) { \ + int64_t diff = (int64_t)buffer0[i].SRC_FIELD[j] - \ + (int64_t)buffer1[i].SRC_FIELD[j]; \ + expect[i].DST_FIELD[j] = diff < 0 ? -diff : diff; \ + } \ + } \ + check_output_##CHECK_FIELD(__LINE__, BUFSIZE); \ +} + +TEST_VEC_ABSDIFF(vabsdiffub, Q6_Vub_vabsdiff_VubVub, ub, ub, b, 1) +TEST_VEC_ABSDIFF(vabsdiffuh, Q6_Vuh_vabsdiff_VuhVuh, uh, uh, h, 2) +TEST_VEC_ABSDIFF(vabsdiffh, Q6_Vuh_vabsdiff_VhVh, h, uh, h, 2) +TEST_VEC_ABSDIFF(vabsdiffw, Q6_Vuw_vabsdiff_VwVw, w, uw, w, 4) + TEST_PRED_OP2(pred_or, or, |, "") TEST_PRED_OP2(pred_or_n, or, |, "!") TEST_PRED_OP2(pred_and, and, &, "") @@ -386,6 +416,47 @@ static void test_vsubuwsat_dv(void) check_output_w(__LINE__, 2); } +static void test_vsubwsat(void) +{ + const int32_t x0 = INT32_MIN; + const int32_t y0 = 1; + const int32_t x1 = INT32_MAX; + const int32_t y1 = -1; + HVX_Vector v0; + HVX_Vector v1; + HVX_Vector vres; + + /* INT32_MIN - 1 underflows and must saturate to INT32_MIN */ + memset(expect, 0x12, sizeof(MMVector)); + memset(output, 0x34, sizeof(MMVector)); + + v0 = Q6_V_vsplat_R(x0); + v1 = Q6_V_vsplat_R(y0); + vres = Q6_Vw_vsub_VwVw_sat(v0, v1); + memcpy(&output[0], &vres, sizeof(MMVector)); + + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { + expect[0].w[j] = INT32_MIN; + } + + check_output_w(__LINE__, 1); + + /* INT32_MAX - (-1) overflows and must saturate to INT32_MAX */ + memset(expect, 0x12, sizeof(MMVector)); + memset(output, 0x34, sizeof(MMVector)); + + v0 = Q6_V_vsplat_R(x1); + v1 = Q6_V_vsplat_R(y1); + vres = Q6_Vw_vsub_VwVw_sat(v0, v1); + memcpy(&output[0], &vres, sizeof(MMVector)); + + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { + expect[0].w[j] = INT32_MAX; + } + + check_output_w(__LINE__, 1); +} + static void test_load_tmp_predicated(void) { void *p0 = buffer0; @@ -530,6 +601,12 @@ int main() test_vadduwsat(); test_vsubuwsat_dv(); + test_vsubwsat(); + + test_vabsdiffub(); + test_vabsdiffuh(); + test_vabsdiffh(); + test_vabsdiffw(); test_load_tmp_predicated(); test_load_cur_predicated(); diff --git a/tests/tcg/hexagon/hvx_misc.h b/tests/tcg/hexagon/hvx_misc.h index 2e868340fd..c21ea975c1 100644 --- a/tests/tcg/hexagon/hvx_misc.h +++ b/tests/tcg/hexagon/hvx_misc.h @@ -18,6 +18,8 @@ #ifndef HVX_MISC_H #define HVX_MISC_H +#include "hex_test.h" + static inline void check(int line, int i, int j, uint64_t result, uint64_t expect) { @@ -34,9 +36,12 @@ typedef union { uint64_t ud[MAX_VEC_SIZE_BYTES / 8]; int64_t d[MAX_VEC_SIZE_BYTES / 8]; uint32_t uw[MAX_VEC_SIZE_BYTES / 4]; + uint32_t sf[MAX_VEC_SIZE_BYTES / 4]; /* convenience alias */ int32_t w[MAX_VEC_SIZE_BYTES / 4]; uint16_t uh[MAX_VEC_SIZE_BYTES / 2]; + uint16_t hf[MAX_VEC_SIZE_BYTES / 2]; /* convenience alias */ int16_t h[MAX_VEC_SIZE_BYTES / 2]; + uint16_t bf[MAX_VEC_SIZE_BYTES / 2]; uint8_t ub[MAX_VEC_SIZE_BYTES / 1]; int8_t b[MAX_VEC_SIZE_BYTES / 1]; } MMVector; @@ -63,8 +68,13 @@ static inline void check_output_##FIELD(int line, size_t num_vectors) \ CHECK_OUTPUT_FUNC(d, 8) CHECK_OUTPUT_FUNC(w, 4) +CHECK_OUTPUT_FUNC(sf, 4) CHECK_OUTPUT_FUNC(h, 2) +CHECK_OUTPUT_FUNC(uh, 2) +CHECK_OUTPUT_FUNC(hf, 2) +CHECK_OUTPUT_FUNC(ub, 1) CHECK_OUTPUT_FUNC(b, 1) +CHECK_OUTPUT_FUNC(bf, 2) static inline void init_buffers(void) { @@ -81,6 +91,58 @@ static inline void init_buffers(void) } } +static const uint32_t FP_VALUES[] = { + SF_INF, SF_INF_neg, SF_QNaN, SF_QNaN_special, SF_SNaN, SF_QNaN_neg, + SF_SNaN_neg, SF_HEX_NaN, SF_zero, SF_zero_neg, SF_one, SF_one_recip, + SF_one_invsqrta, SF_two, SF_four, SF_small_neg, SF_large_pos, SF_any, + SF_denorm, SF_random, SF_neg_two, +}; +#define FP_VALUES_MAX ARRAY_SIZE(FP_VALUES) + +static const uint16_t BF_VALUES[] = { + BF_INF, BF_INF_neg, BF_QNaN, BF_SNaN, BF_QNaN_neg, BF_SNaN_neg, + BF_HEX_NaN, BF_zero, BF_zero_neg, BF_one, BF_two, BF_four, +}; +#define BF_VALUES_MAX ARRAY_SIZE(BF_VALUES) + +static inline void init_buffers_fp(void) +{ + _Static_assert(BUFSIZE * (MAX_VEC_SIZE_BYTES / 4) > + FP_VALUES_MAX * FP_VALUES_MAX, + "test arrays can't fit all FP_VALUES combinations"); + int counter1 = 0, counter2 = 0; + for (int i = 0; i < BUFSIZE; i++) { + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { + buffer0[i].sf[j] = FP_VALUES[counter1]; + buffer1[i].sf[j] = FP_VALUES[counter2]; + counter2++; + if (counter2 == FP_VALUES_MAX) { + counter2 = 0; + counter1 = (counter1 + 1) % FP_VALUES_MAX; + } + } + } +} + +static inline void init_buffers_bf(void) +{ + _Static_assert(BUFSIZE * (MAX_VEC_SIZE_BYTES / 2) > + BF_VALUES_MAX * BF_VALUES_MAX, + "test arrays can't fit all BF_VALUES combinations"); + int counter1 = 0, counter2 = 0; + for (int i = 0; i < BUFSIZE; i++) { + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 2; j++) { + buffer0[i].bf[j] = BF_VALUES[counter1]; + buffer1[i].bf[j] = BF_VALUES[counter2]; + counter2++; + if (counter2 == BF_VALUES_MAX) { + counter2 = 0; + counter1 = (counter1 + 1) % BF_VALUES_MAX; + } + } + } +} + #define VEC_OP1(ASM, EL, IN, OUT) \ asm("v2 = vmem(%0 + #0)\n\t" \ "v2" #EL " = " #ASM "(v2" #EL ")\n\t" \ @@ -175,4 +237,15 @@ static inline void test_##NAME(bool invert) \ check_output_b(__LINE__, BUFSIZE); \ } +#define float_sf(x) ({ typeof(x) _x = (x); *((float *)&(_x)); }) +#define float_hf(x) ({ typeof(x) _x = (x); *((_Float16 *) &(_x)); }) +#define float_bf(x) ({ uint32_t _u = ((uint32_t)(x)) << 16; *((float *)&(_u)); }) +#define raw_sf(x) ({ typeof(x) _x = (x); *((uint32_t *)&(_x)); }) +#define raw_hf(x) ({ typeof(x) _x = (x); *((uint16_t *)&(_x)); }) +#define raw_bf(x) ({ typeof(x) _x = (x); (uint16_t)(*((uint32_t *)&(_x)) >> 16); }) +#define float_hf_to_sf(x) ((float)x) +#define bytes_hf 2 +#define bytes_sf 4 +#define bytes_bf 2 + #endif diff --git a/tests/tcg/hexagon/invalid-slots.c b/tests/tcg/hexagon/invalid-slots.c index 607027f314..f2dace2e54 100644 --- a/tests/tcg/hexagon/invalid-slots.c +++ b/tests/tcg/hexagon/invalid-slots.c @@ -55,6 +55,75 @@ static int test_invalid_slots(void) return sig; } +/* Load then indirect jump, load encoded first: no high slot left for jump. */ +static int test_invalid_slots_highslot(void) +{ + int sig; + + asm volatile( + "r0 = #0\n" + "r1 = ##1f\n" + "memw(%1) = r1\n" + "r3 = #mem\n" + ".word 0x91834006\n" /* { r6 = memw(r3+#0); */ + ".word 0x529fc000\n" /* jumpr r31 } */ + "1:\n" + "%0 = r0\n" + : "=r"(sig) + : "r"(&resume_pc) + : "r0", "r1", "r3", "r6", "memory"); + + return sig; +} + +/* + * Three predicate-logical ops: each is restricted to slots 2 and 3, so the + * fourth-and-fifth-slot-free packet still has only two slots for three ops. + * No change-of-flow is involved, so the only reason to reject it is the slot + * conflict. + */ +static int test_invalid_slots_crslot23(void) +{ + int sig; + + asm volatile( + "r0 = #0\n" + "r1 = ##1f\n" + "memw(%1) = r1\n" + ".word 0x6b024100\n" /* { p0 = and(p1, p2); */ + ".word 0x6b224103\n" /* p3 = or(p1, p2); */ + ".word 0x6b42c301\n" /* p1 = xor(p2, p3) } */ + "1:\n" + "%0 = r0\n" + : "=r"(sig) + : "r"(&resume_pc) + : "r0", "r1", "p0", "p1", "p3", "memory"); + + return sig; +} + +/* Three transfers plus a duplex: five ops for four slots. */ +static int test_invalid_slots_five(void) +{ + int sig; + + asm volatile( + "r0 = #0\n" + "r1 = ##1f\n" + "memw(%1) = r1\n" + ".word 0x78004020\n" /* { r0 = #1; */ + ".word 0x78004041\n" /* r1 = #2; */ + ".word 0x78004062\n" /* r2 = #3; */ + ".word 0x28452856\n" /* r5 = #4; r6 = #5 } */ + "1:\n" + "%0 = r0\n" + : "=r"(sig) + : "r"(&resume_pc) + : "r0", "r1", "r2", "r5", "r6", "memory"); + + return sig; +} + int main() { struct sigaction act; @@ -65,6 +134,9 @@ int main() assert(sigaction(SIGILL, &act, NULL) == 0); assert(test_invalid_slots() == SIGILL); + assert(test_invalid_slots_highslot() == SIGILL); + assert(test_invalid_slots_crslot23() == SIGILL); + assert(test_invalid_slots_five() == SIGILL); puts("PASS"); return EXIT_SUCCESS; diff --git a/tests/tcg/hexagon/read_write_overlap.c b/tests/tcg/hexagon/read_write_overlap.c index 95c54ccd63..7eaf75f545 100644 --- a/tests/tcg/hexagon/read_write_overlap.c +++ b/tests/tcg/hexagon/read_write_overlap.c @@ -115,12 +115,59 @@ static void test_swiz(void) check32(swiz(0x11223344), 0x44332211); } +#define CMPY(NAME, ASM) \ +static inline uint32_t NAME##_rd_eq_rs(uint32_t x, uint32_t y) \ +{ \ + uint32_t res; \ + asm("r7 = %1\n\t" \ + ASM("r7", "%2") "\n\t" \ + "%0 = r7\n\t" \ + : "=r"(res) : "r"(x), "r"(y) : "r7"); \ + return res; \ +} \ +static inline uint32_t NAME##_rd_eq_rt(uint32_t x, uint32_t y) \ +{ \ + uint32_t res; \ + asm("r7 = %2\n\t" \ + ASM("%1", "r7") "\n\t" \ + "%0 = r7\n\t" \ + : "=r"(res) : "r"(x), "r"(y) : "r7"); \ + return res; \ +} + +#define CMPY_RND_SAT(RS, RT) "r7 = cmpy(" RS "," RT "):rnd:sat" +#define CMPY_S1_RND_SAT(RS, RT) "r7 = cmpy(" RS "," RT "):<<1:rnd:sat" +#define CMPYC_RND_SAT(RS, RT) "r7 = cmpy(" RS "," RT "*):rnd:sat" +#define CMPYC_S1_RND_SAT(RS, RT) "r7 = cmpy(" RS "," RT "*):<<1:rnd:sat" + +CMPY(cmpyrs_s0, CMPY_RND_SAT) +CMPY(cmpyrs_s1, CMPY_S1_RND_SAT) +CMPY(cmpyrsc_s0, CMPYC_RND_SAT) +CMPY(cmpyrsc_s1, CMPYC_S1_RND_SAT) + +static void test_cmpy(void) +{ + check32(cmpyrs_s0_rd_eq_rs(0x32195ce2, 0xef862430), 0x011b105b); + check32(cmpyrs_s0_rd_eq_rt(0x32195ce2, 0xef862430), 0x011b105b); + check32(cmpyrs_s1_rd_eq_rs(0x32195ce2, 0xef862430), 0x023520b5); + check32(cmpyrs_s1_rd_eq_rt(0x32195ce2, 0xef862430), 0x023520b5); + check32(cmpyrsc_s0_rd_eq_rs(0x32195ce2, 0xef862430), 0x0d0f09e8); + check32(cmpyrsc_s0_rd_eq_rt(0x32195ce2, 0xef862430), 0x0d0f09e8); + check32(cmpyrsc_s1_rd_eq_rs(0x32195ce2, 0xef862430), 0x1a1f13d0); + check32(cmpyrsc_s1_rd_eq_rt(0x32195ce2, 0xef862430), 0x1a1f13d0); + + /* Both halves saturate */ + check32(cmpyrs_s1_rd_eq_rs(0x80008000, 0x80008000), 0x7fff0000); + check32(cmpyrsc_s1_rd_eq_rs(0x7fff8001, 0x80017fff), 0x00008000); +} + int main() { test_insert(); test_insert_rp(); test_asr_r_svw_trun(); test_swiz(); + test_cmpy(); puts(err ? "FAIL" : "PASS"); return err ? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/tests/tcg/hexagon/unaligned_data.c b/tests/tcg/hexagon/unaligned_data.c new file mode 100644 index 0000000000..e0375faafb --- /dev/null +++ b/tests/tcg/hexagon/unaligned_data.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/* + * Test that unaligned scalar loads raise SIGBUS. + */ + +#include +#include +#include +#include +#include +#include + +int err; + +#include "hex_test.h" + +static bool sigbus_caught; +static sigjmp_buf jmp_env; +static uint64_t buf[2] = { 0, 0 }; + +static void sigbus_handler(int sig, siginfo_t *info, void *puc) +{ + check32(sig, SIGBUS); + sigbus_caught = true; + siglongjmp(jmp_env, 1); +} + +static void test_unaligned_load(int size, int offset) +{ + char *p = (char *)buf + offset; + uint32_t dummy32; + uint64_t dummy64; + + sigbus_caught = false; + if (sigsetjmp(jmp_env, 1) == 0) { + switch (size) { + case 2: + asm volatile("%[dst] = memh(%[src])\n\t" + : [dst] "=r"(dummy32) : [src] "r"(p) : "memory"); + break; + case 4: + asm volatile("%[dst] = memw(%[src])\n\t" + : [dst] "=r"(dummy32) : [src] "r"(p) : "memory"); + break; + case 8: + asm volatile("%[dst] = memd(%[src])\n\t" + : [dst] "=r"(dummy64) : [src] "r"(p) : "memory"); + break; + default: + abort(); + } + } + check32(sigbus_caught, true); +} + +static void test_unaligned_store(int size, int offset) +{ + char *p = (char *)buf + offset; + uint32_t val32 = 0x11223344; + uint64_t val64 = 0x1122334455667788ULL; + + sigbus_caught = false; + if (sigsetjmp(jmp_env, 1) == 0) { + switch (size) { + case 2: + asm volatile("memh(%[addr]) = %[val]\n\t" + : : [addr] "r"(p), [val] "r"(val32) : "memory"); + break; + case 4: + asm volatile("memw(%[addr]) = %[val]\n\t" + : : [addr] "r"(p), [val] "r"(val32) : "memory"); + break; + case 8: + asm volatile("memd(%[addr]) = %[val]\n\t" + : : [addr] "r"(p), [val] "r"(val64) : "memory"); + break; + default: + abort(); + } + } + check32(sigbus_caught, true); +} + +int main() +{ + struct sigaction act; + + act.sa_sigaction = sigbus_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; + chk_error(sigaction(SIGBUS, &act, NULL)); + + test_unaligned_load(2, 1); + test_unaligned_load(4, 1); + test_unaligned_load(4, 2); + test_unaligned_load(4, 3); + test_unaligned_load(8, 1); + test_unaligned_load(8, 4); + + test_unaligned_store(2, 1); + test_unaligned_store(4, 1); + test_unaligned_store(4, 2); + test_unaligned_store(4, 3); + test_unaligned_store(8, 1); + test_unaligned_store(8, 4); + + act.sa_handler = SIG_DFL; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + chk_error(sigaction(SIGBUS, &act, NULL)); + + puts(err ? "FAIL" : "PASS"); + return err ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/tests/tcg/hexagon/valid-slots.c b/tests/tcg/hexagon/valid-slots.c new file mode 100644 index 0000000000..70d9b0b6ac --- /dev/null +++ b/tests/tcg/hexagon/valid-slots.c @@ -0,0 +1,62 @@ +/* + * Regression tests for valid packets that qemu incorrectly rejected as + * invalid. + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +int err; + +#include "hex_test.h" + +/* volatile to keep the load from being optimized away */ +static volatile int buf[2] = { 0x1234, 0 }; + +/* Load and register transfer in one packet, load encoded first. */ +static int32_t load_imm_pair(void) +{ + int32_t out; + /* { r6 = memw(r3+#-4); r7 = #0x4ae6 } */ + asm volatile( + "{ r3 = %1 }\n\t" + ".word 0x97837fe6\n\t" + ".word 0x7845dcc7\n\t" + "{ %0 = r6 }\n\t" + : "=r"(out) : "r"(&buf[1]) : "r3", "r6", "r7"); + return out; +} + +static int32_t dcbuf[8] __attribute__((aligned(32))); + +/* Slot-0-only op (dczeroa) packed last with three transfers. */ +static void slot0_restricted(int32_t *out) +{ + asm volatile( + "{ %0 = #0x11\n\t" + " %1 = #0x22\n\t" + " %2 = #0x33\n\t" + " dczeroa(%3) }\n\t" + : "=r"(out[0]), "=r"(out[1]), "=r"(out[2]) + : "r"(dcbuf) : "memory"); +} + +int main() +{ + int32_t r[3]; + + check32(load_imm_pair(), 0x1234); + + dcbuf[0] = 0x5a5a5a5a; + slot0_restricted(r); + check32(r[0], 0x11); + check32(r[1], 0x22); + check32(r[2], 0x33); + check32(dcbuf[0], 0); /* dczeroa cleared the line */ + + puts(err ? "FAIL" : "PASS"); + return err; +} diff --git a/tests/tcg/i386/test-i386-opt-shr.c b/tests/tcg/i386/test-i386-opt-shr.c new file mode 100644 index 0000000000..9fb8d42022 --- /dev/null +++ b/tests/tcg/i386/test-i386-opt-shr.c @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Regression test for tcg optimize vs sign bit repetition counting. */ + +#include + +int main() +{ +#ifndef __x86_64__ + char test; + + asm("movw $0x4000, %%ax\n\t" + "addw %%ax, %%ax\n\t" + "cwtl\n\t" + "shrl %%eax\n\t" + "cmpw $-0x3fff, %%ax\n\t" + "setnl %%al" + : "=a"(test)); + assert(!test); +#endif + return 0; +} diff --git a/tests/tcg/loongarch64/test_bit.c b/tests/tcg/loongarch64/test_bit.c index a6d9904909..65cd6598c9 100644 --- a/tests/tcg/loongarch64/test_bit.c +++ b/tests/tcg/loongarch64/test_bit.c @@ -2,62 +2,27 @@ #include #define ARRAY_SIZE(X) (sizeof(X) / sizeof(*(X))) -#define TEST_CLO(N) \ -static uint64_t test_clo_##N(uint64_t rj) \ -{ \ - uint64_t rd = 0; \ - \ - asm volatile("clo."#N" %0, %1\n\t" \ - : "=r"(rd) \ - : "r"(rj) \ - : ); \ - return rd; \ + +#define TEST(C, I) \ +static uint64_t test_##C(uint64_t rj) \ +{ \ + uint64_t rd; \ + asm volatile(I " %0, %1\n\t" : "=r"(rd) : "r"(rj)); \ + return rd; \ } -#define TEST_CLZ(N) \ -static uint64_t test_clz_##N(uint64_t rj) \ -{ \ - uint64_t rd = 0; \ - \ - asm volatile("clz."#N" %0, %1\n\t" \ - : "=r"(rd) \ - : "r"(rj) \ - : ); \ - return rd; \ -} - -#define TEST_CTO(N) \ -static uint64_t test_cto_##N(uint64_t rj) \ -{ \ - uint64_t rd = 0; \ - \ - asm volatile("cto."#N" %0, %1\n\t" \ - : "=r"(rd) \ - : "r"(rj) \ - : ); \ - return rd; \ -} - -#define TEST_CTZ(N) \ -static uint64_t test_ctz_##N(uint64_t rj) \ -{ \ - uint64_t rd = 0; \ - \ - asm volatile("ctz."#N" %0, %1\n\t" \ - : "=r"(rd) \ - : "r"(rj) \ - : ); \ - return rd; \ -} - -TEST_CLO(w) -TEST_CLO(d) -TEST_CLZ(w) -TEST_CLZ(d) -TEST_CTO(w) -TEST_CTO(d) -TEST_CTZ(w) -TEST_CTZ(d) +TEST(clo_w, "clo.w") +TEST(clo_d, "clo.d") +TEST(clz_w, "clz.w") +TEST(clz_d, "clz.d") +TEST(cto_w, "cto.w") +TEST(cto_d, "cto.d") +TEST(ctz_w, "ctz.w") +TEST(ctz_d, "ctz.d") +TEST(bitrev_4b, "bitrev.4b") +TEST(bitrev_8b, "bitrev.8b") +TEST(bitrev_w, "bitrev.w") +TEST(bitrev_d, "bitrev.d") struct vector { uint64_t (*func)(uint64_t); @@ -74,6 +39,10 @@ static struct vector vectors[] = { {test_cto_d, 0xabd28a64000000, 0}, {test_ctz_w, 0xfaffff42392476ab, 0}, {test_ctz_d, 0xabd28a64000000, 26}, + {test_bitrev_4b, 0xdeadbeef11223344, 0xffffffff8844cc22}, + {test_bitrev_8b, 0x1122334455667788, 0x8844cc22aa66ee11}, + {test_bitrev_w, 0xdeadbeef89abcdef, 0xfffffffff7b3d591}, + {test_bitrev_d, 0x0123456789abcdef, 0xf7b3d591e6a2c480}, }; int main() diff --git a/tests/tcg/multiarch/test-plugin-syscall-filter.c b/tests/tcg/multiarch/test-plugin-syscall-filter.c index 951e338a7c..089fb2a110 100644 --- a/tests/tcg/multiarch/test-plugin-syscall-filter.c +++ b/tests/tcg/multiarch/test-plugin-syscall-filter.c @@ -23,9 +23,12 @@ int main(int argc, char *argv[]) * "linux-user/arm/cpu_loop.c:cpu_loop". * As well, some arch expect a minimum, like 4000 for mips 32 bits. * - * Therefore, we pick 4096 because, as of now, no ISA in Linux uses this - * number. This is just a test case; replace this number as needed in the - * future. + * Therefore, we pick 4096, which sits between those bounds. It is not + * unused everywhere though: mips 32 bits numbers from 4000, so 4096 is its + * getpriority. This test is unaffected because the filter also requires + * the first argument to be 0x66CCFF, so a real syscall carrying this + * number falls through untouched. This is just a test case, so replace + * this number as needed in the future. * * The corresponding syscall filter is implemented in * "tests/tcg/plugins/syscall.c". diff --git a/tests/tcg/riscv64/Makefile.softmmu-target b/tests/tcg/riscv64/Makefile.softmmu-target index 82be8a2c91..6a219c306c 100644 --- a/tests/tcg/riscv64/Makefile.softmmu-target +++ b/tests/tcg/riscv64/Makefile.softmmu-target @@ -24,6 +24,10 @@ EXTRA_RUNS += run-test-mepc-masking run-test-mepc-masking: test-mepc-masking $(call run-test, $<, $(QEMU) $(QEMU_OPTS)$<) +EXTRA_RUNS += run-test-minstret-ecall +run-test-minstret-ecall: test-minstret-ecall + $(call run-test, $<, $(QEMU) -icount shift=1 $(QEMU_OPTS)$<) + EXTRA_RUNS += run-plugin-doubletrap run-plugin-doubletrap: doubletrap $(call run-test, $<, \ @@ -41,5 +45,31 @@ comma:= , run-test-crc32: test-crc32 $(call run-test, $<, $(QEMU) -cpu rv64$(comma)xlrbr=true $(QEMU_OPTS)$<) +# Zicclsm: misaligned load/store support. Assemble one source twice: the +# default build expects every misaligned access to succeed (zicclsm=true), +# the -DZICCLSM_DISABLED build expects every one to trap (zicclsm=false). +ZICCLSM_MARCH = -march=rv64gcv_zfh +CLEANFILES += test-zicclsm test-zicclsm-off + +test-zicclsm: test-zicclsm.S $(LINK_SCRIPT) + $(CC) $(CFLAGS) $(ZICCLSM_MARCH) $< -Wa,--noexecstack -c -o test-zicclsm.o + $(LD) $(LDFLAGS) test-zicclsm.o -o $@ + +test-zicclsm-off: test-zicclsm.S $(LINK_SCRIPT) + $(CC) $(CFLAGS) $(ZICCLSM_MARCH) -DZICCLSM_DISABLED $< -Wa,--noexecstack -c -o test-zicclsm-off.o + $(LD) $(LDFLAGS) test-zicclsm-off.o -o $@ + +EXTRA_RUNS += run-test-zicclsm +run-test-zicclsm: test-zicclsm + $(call run-test, $<, $(QEMU) -cpu rv64$(comma)v=true$(comma)zfh=true$(comma)zicclsm=true $(QEMU_OPTS)$<) + +EXTRA_RUNS += run-test-zicclsm-off +run-test-zicclsm-off: test-zicclsm-off + $(call run-test, $<, $(QEMU) -cpu rv64$(comma)v=true$(comma)zfh=true$(comma)zicclsm=false $(QEMU_OPTS)$<) + +EXTRA_RUNS += run-test-misa-w +run-test-misa-w: test-misa-w + $(call run-test, $<, $(QEMU) -cpu rv64$(comma)x-misa-w=true$(comma)c=true$(comma)v=true $(QEMU_OPTS)$<) + # We don't currently support the multiarch system tests undefine MULTIARCH_TESTS diff --git a/tests/tcg/riscv64/test-minstret-ecall.S b/tests/tcg/riscv64/test-minstret-ecall.S new file mode 100644 index 0000000000..ab268f7f22 --- /dev/null +++ b/tests/tcg/riscv64/test-minstret-ecall.S @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + + .option norvc + + .text + .global _start +_start: + lla t0, trap + csrw mtvec, t0 + + /* + * The first CSR read retires after obtaining s0. The ecall does not + * retire, so the trap handler must observe a difference of one. + */ + csrr s0, minstret + ecall + sub t0, s1, s0 + li t1, 1 + bne t0, t1, fail + + li a0, 0 + j _exit + +trap: + csrr s1, minstret + csrr t0, mcause + li t1, 11 /* Environment call from M-mode */ + bne t0, t1, fail + + csrr t0, mepc + addi t0, t0, 4 + csrw mepc, t0 + mret + +fail: + li a0, 1 + +_exit: + lla a1, semiargs + li t0, 0x20026 /* ADP_Stopped_ApplicationExit */ + sd t0, 0(a1) + sd a0, 8(a1) + li a0, 0x20 /* TARGET_SYS_EXIT_EXTENDED */ + + /* Semihosting call sequence */ + .balign 16 + slli zero, zero, 0x1f + ebreak + srai zero, zero, 0x7 + j . + + .data + .balign 16 +semiargs: + .space 16 diff --git a/tests/tcg/riscv64/test-misa-w.S b/tests/tcg/riscv64/test-misa-w.S new file mode 100644 index 0000000000..7f1eb30023 --- /dev/null +++ b/tests/tcg/riscv64/test-misa-w.S @@ -0,0 +1,88 @@ +/* + * Test for MISA changing C and related IALIGN alignment cases + * + * This test verifies that the "C" extension can be cleared and set in MISA, + * that a branch to 2-byte aligned instructions can be executed when "C" is + * enabled, and that a write to MISA which would increase IALIGN and cause + * the next instruction to be unaligned is ignored. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#define RVC (1 << ('C'-'A')) +#define RVV (1 << ('V'-'A')) + +.option norvc + .text + .global _start +_start: + lla t0, trap + csrw mtvec, t0 + + csrr t0, misa + li t1, RVC + not t1, t1 + and t0, t0, t1 + csrw misa, t0 + csrr t1, misa + li a0, 2 # fail code + bne t0, t1, _exit # Could not clear RVC in MISA + + li t1, RVC + or t0, t0, t1 + csrw misa, t0 + csrr t1, misa + li a0, 3 # fail code + bne t0, t1, _exit # Could not set RVC in MISA + + j unalign +. = . + 2 +unalign: + + li t1, RVC + not t1, t1 + and t0, t0, t1 + csrw misa, t0 + csrr t1, misa + li a0, 4 # fail code + beq t0, t1, _exit # Was able to clear RVC in MISA + + li t0, (RVC|RVV) + not t0, t0 + and t0, t0, t1 + csrw misa, t0 + csrr t0, misa + li a0, 5 # fail code + bne t0, t1, _exit # MISA write was not ignored (RVV was cleared) + + j realign +. = . + 2 +realign: + + # Success! + li a0, 0 + j _exit + +trap: + # Any trap is a fail code 1 + li a0, 1 + +# Exit code in a0 +_exit: + lla a1, semiargs + li t0, 0x20026 # ADP_Stopped_ApplicationExit + sd t0, 0(a1) + sd a0, 8(a1) + li a0, 0x20 # TARGET_SYS_EXIT_EXTENDED + + # Semihosting call sequence + .balign 16 + slli zero, zero, 0x1f + ebreak + srai zero, zero, 0x7 + j . + + .data + .balign 16 +semiargs: + .space 16 diff --git a/tests/tcg/riscv64/test-zicclsm.S b/tests/tcg/riscv64/test-zicclsm.S new file mode 100644 index 0000000000..a2f217c0d1 --- /dev/null +++ b/tests/tcg/riscv64/test-zicclsm.S @@ -0,0 +1,368 @@ +/* + * Test the Zicclsm extension (misaligned load/store support). + * + * This single source is assembled twice: + * - test-zicclsm : run on a CPU with zicclsm=true. Every misaligned + * scalar integer, floating-point and vector + * load/store must complete WITHOUT raising a trap. + * - test-zicclsm-off : built with -DZICCLSM_DISABLED and run on a CPU with + * zicclsm=false. Every misaligned access must raise a + * misaligned load/store exception, with the correct + * mcause and mtval. + * + * Zicclsm governs all regular scalar loads/stores (integer and F/D/Zfh + * floating-point) as well as vector element loads/stores. Floating-point + * loads/stores (flh/flw/fld, fsh/fsw/fsd) are therefore exercised here. + * + * Atomic (A/Zacas/...) accesses are intentionally excluded: they always + * require natural alignment regardless of Zicclsm. Likewise cm.push/cm.pop + * (Zcmp) are excluded, as they are not regular loads/stores. + * + * Register conventions (persist across the whole test; the trap handler only + * clobbers t0-t4): + * s1 = expected mcause for the pending misaligned access + * s2 = expected mtval (the misaligned address) + * s3 = trap counter (incremented by the handler) + * s4 = base address of the aligned data buffer + * s5 = snapshot of s3 taken before an access, used to check the delta + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + .option norelax + .option norvc + +/* RISC-V exception causes (see target/riscv/cpu_bits.h). */ +#define CAUSE_LOAD_MISALIGNED 0x4 +#define CAUSE_STORE_MISALIGNED 0x6 + +/* + * EXPECT sets up the expectation for the access that immediately follows and + * snapshots the trap counter. + * \cause = expected mcause if the access traps + * \off = byte offset from the buffer base; also the expected mtval + */ + .macro EXPECT cause, off + li s1, \cause + addi s2, s4, \off + mv s5, s3 + .endm + +/* + * CHECK validates the outcome of the preceding access. + * - When Zicclsm is disabled, exactly one trap must have fired. + * - When Zicclsm is enabled, no trap must have fired. + */ + .macro CHECK +#ifdef ZICCLSM_DISABLED + addi s5, s5, 1 + bne s3, s5, fail +#else + bne s3, s5, fail +#endif + .endm + +/* In the enabled case, also verify the value returned by scalar loads. */ + .macro CHECK_VALUE value +#ifndef ZICCLSM_DISABLED + li t0, \value + bne a2, t0, fail +#endif + .endm + +/* Verify bytes written by an enabled scalar store. */ + .macro CHECK_BYTE off, value +#ifndef ZICCLSM_DISABLED + lbu t0, \off(s4) + li t1, \value + bne t0, t1, fail +#endif + .endm + + .text + .global _start +_start: + /* Install the trap handler. */ + lla t0, trap + csrw mtvec, t0 + + /* Enable the FP (FS) and Vector (VS) unit state so F/D/V instructions + * do not trap as illegal. 0x6600 = FS[14:13]=11 | VS[10:9]=11. */ + li t0, 0x6600 + csrs mstatus, t0 + + /* Initialise persistent state. */ + li s3, 0 /* trap counter */ + lla s4, buf /* aligned buffer base */ + + /* + * ---- Scalar integer loads ---- + * lh/lhu need 2-byte alignment; lw/lwu 4-byte; ld 8-byte. + */ + EXPECT CAUSE_LOAD_MISALIGNED, 1 + lh a2, 1(s4) + CHECK + CHECK_VALUE 0x2211 + EXPECT CAUSE_LOAD_MISALIGNED, 1 + lhu a2, 1(s4) + CHECK + CHECK_VALUE 0x2211 + + EXPECT CAUSE_LOAD_MISALIGNED, 1 + lw a2, 1(s4) + CHECK + CHECK_VALUE 0x44332211 + EXPECT CAUSE_LOAD_MISALIGNED, 3 + lw a2, 3(s4) + CHECK + CHECK_VALUE 0x66554433 + EXPECT CAUSE_LOAD_MISALIGNED, 1 + lwu a2, 1(s4) + CHECK + CHECK_VALUE 0x44332211 + + EXPECT CAUSE_LOAD_MISALIGNED, 1 + ld a2, 1(s4) + CHECK + CHECK_VALUE 0x8877665544332211 + EXPECT CAUSE_LOAD_MISALIGNED, 3 + ld a2, 3(s4) + CHECK + CHECK_VALUE 0xaa99887766554433 + EXPECT CAUSE_LOAD_MISALIGNED, 7 + ld a2, 7(s4) + CHECK + CHECK_VALUE 0xeeddccbbaa998877 + + /* + * ---- Scalar integer stores ---- + */ + li t6, 0x1122334455667788 + EXPECT CAUSE_STORE_MISALIGNED, 1 + sh t6, 1(s4) + CHECK + CHECK_BYTE 1, 0x88 + CHECK_BYTE 2, 0x77 + + EXPECT CAUSE_STORE_MISALIGNED, 1 + sw t6, 1(s4) + CHECK + CHECK_BYTE 1, 0x88 + CHECK_BYTE 2, 0x77 + CHECK_BYTE 3, 0x66 + CHECK_BYTE 4, 0x55 + EXPECT CAUSE_STORE_MISALIGNED, 3 + sw t6, 3(s4) + CHECK + CHECK_BYTE 3, 0x88 + CHECK_BYTE 4, 0x77 + CHECK_BYTE 5, 0x66 + CHECK_BYTE 6, 0x55 + + EXPECT CAUSE_STORE_MISALIGNED, 1 + sd t6, 1(s4) + CHECK + CHECK_BYTE 1, 0x88 + CHECK_BYTE 2, 0x77 + CHECK_BYTE 3, 0x66 + CHECK_BYTE 4, 0x55 + CHECK_BYTE 5, 0x44 + CHECK_BYTE 6, 0x33 + CHECK_BYTE 7, 0x22 + CHECK_BYTE 8, 0x11 + EXPECT CAUSE_STORE_MISALIGNED, 7 + sd t6, 7(s4) + CHECK + CHECK_BYTE 7, 0x88 + CHECK_BYTE 8, 0x77 + CHECK_BYTE 9, 0x66 + CHECK_BYTE 10, 0x55 + CHECK_BYTE 11, 0x44 + CHECK_BYTE 12, 0x33 + CHECK_BYTE 13, 0x22 + CHECK_BYTE 14, 0x11 + + /* + * ---- Floating-point loads ---- + * flh needs 2-byte alignment; flw 4-byte; fld 8-byte. Their alignment + * is governed by Zicclsm just like the scalar integer forms. + */ + EXPECT CAUSE_LOAD_MISALIGNED, 1 + flh fa0, 1(s4) + CHECK + + EXPECT CAUSE_LOAD_MISALIGNED, 1 + flw fa0, 1(s4) + CHECK + EXPECT CAUSE_LOAD_MISALIGNED, 3 + flw fa0, 3(s4) + CHECK + + EXPECT CAUSE_LOAD_MISALIGNED, 1 + fld fa0, 1(s4) + CHECK + EXPECT CAUSE_LOAD_MISALIGNED, 7 + fld fa0, 7(s4) + CHECK + + /* + * ---- Floating-point stores ---- + */ + EXPECT CAUSE_STORE_MISALIGNED, 1 + fsh fa0, 1(s4) + CHECK + + EXPECT CAUSE_STORE_MISALIGNED, 1 + fsw fa0, 1(s4) + CHECK + EXPECT CAUSE_STORE_MISALIGNED, 3 + fsw fa0, 3(s4) + CHECK + + EXPECT CAUSE_STORE_MISALIGNED, 1 + fsd fa0, 1(s4) + CHECK + EXPECT CAUSE_STORE_MISALIGNED, 7 + fsd fa0, 7(s4) + CHECK + + /* + * ---- Vector unit-stride loads / stores ---- + * A base address that is not aligned to the element size (SEW) is + * misaligned for the first element access. + */ + vsetvli t1, x0, e16, m1, ta, ma + EXPECT CAUSE_LOAD_MISALIGNED, 1 + addi a0, s4, 1 + vle16.v v0, (a0) + CHECK + EXPECT CAUSE_STORE_MISALIGNED, 1 + addi a0, s4, 1 + vse16.v v0, (a0) + CHECK + + vsetvli t1, x0, e32, m1, ta, ma + EXPECT CAUSE_LOAD_MISALIGNED, 1 + addi a0, s4, 1 + vle32.v v0, (a0) + CHECK + EXPECT CAUSE_STORE_MISALIGNED, 3 + addi a0, s4, 3 + vse32.v v0, (a0) + CHECK + + vsetvli t1, x0, e64, m1, ta, ma + EXPECT CAUSE_LOAD_MISALIGNED, 1 + addi a0, s4, 1 + vle64.v v0, (a0) + CHECK + EXPECT CAUSE_STORE_MISALIGNED, 7 + addi a0, s4, 7 + vse64.v v0, (a0) + CHECK + + /* + * ---- Vector strided loads / stores ---- + */ + vsetvli t1, x0, e32, m1, ta, ma + li a1, 8 /* stride in bytes */ + EXPECT CAUSE_LOAD_MISALIGNED, 1 + addi a0, s4, 1 + vlse32.v v0, (a0), a1 + CHECK + EXPECT CAUSE_STORE_MISALIGNED, 1 + addi a0, s4, 1 + vsse32.v v0, (a0), a1 + CHECK + + /* ---- Vector indexed loads / stores ---- */ + /* + * Zero indices keep the first element at the deliberately misaligned base. + */ + vmv.v.i v1, 0 + EXPECT CAUSE_LOAD_MISALIGNED, 1 + addi a0, s4, 1 + vluxei32.v v0, (a0), v1 + CHECK + EXPECT CAUSE_STORE_MISALIGNED, 1 + addi a0, s4, 1 + vsuxei32.v v0, (a0), v1 + CHECK + + /* ---- Vector segmented loads / stores ---- */ + EXPECT CAUSE_LOAD_MISALIGNED, 1 + addi a0, s4, 1 + vlseg2e32.v v0, (a0) + CHECK + EXPECT CAUSE_STORE_MISALIGNED, 1 + addi a0, s4, 1 + vsseg2e32.v v0, (a0) + CHECK + + /* + * ---- Vector whole-register load ---- + * Only the whole-register *load* forms carry an element width + * (vl1re32.v => EEW=32), so only they enforce alignment when Zicclsm is + * off. The whole-register store form (vs1r.v) is defined as EEW=8 + * (byte granular) and therefore never faults on misalignment, so it is + * not exercised here. + */ + EXPECT CAUSE_LOAD_MISALIGNED, 1 + addi a0, s4, 1 + vl1re32.v v1, (a0) + CHECK + + /* Success. */ + li a0, 0 + j _exit + + /* + * Trap handler: validate mcause and mtval against the expectation, bump + * the trap counter, then skip past the faulting instruction. The + * instruction length is decoded from its low two bits (0b11 => 4 bytes, + * otherwise a 2-byte compressed instruction). + */ + .balign 4 +trap: + csrr t0, mcause + bne t0, s1, fail + csrr t1, mtval + bne t1, s2, fail + addi s3, s3, 1 + + csrr t0, mepc + lhu t2, 0(t0) + andi t3, t2, 3 + li t4, 3 + bne t3, t4, 1f + addi t0, t0, 4 /* 32-bit instruction */ + j 2f +1: + addi t0, t0, 2 /* 16-bit compressed instruction */ +2: + csrw mepc, t0 + mret + +fail: + li a0, 1 +_exit: + lla a1, semiargs + li t0, 0x20026 /* ADP_Stopped_ApplicationExit */ + sd t0, 0(a1) + sd a0, 8(a1) + li a0, 0x20 /* TARGET_SYS_EXIT_EXTENDED */ + .balign 16 + slli zero, zero, 0x1f + ebreak + srai zero, zero, 0x7 + j . + + .data + .balign 16 +semiargs: + .space 16 + .balign 64 +buf: + .byte 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 + .byte 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff + .space 240 diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target index 0ca030ded0..97c0f02190 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target @@ -50,6 +50,7 @@ TESTS+=cvb TESTS+=ts TESTS+=ex-smc TESTS+=divide-to-integer +TESTS+=stckf cdsg: CFLAGS+=-pthread cdsg: LDFLAGS+=-pthread @@ -75,6 +76,7 @@ Z13_TESTS+=vcksm Z13_TESTS+=vstl Z13_TESTS+=vrep Z13_TESTS+=precise-smc-user +Z13_TESTS+=prno-trng $(Z13_TESTS): CFLAGS+=-march=z13 -O2 TESTS+=$(Z13_TESTS) diff --git a/tests/tcg/s390x/div.c b/tests/tcg/s390x/div.c index 6ad9900e08..124c9ecc33 100644 --- a/tests/tcg/s390x/div.c +++ b/tests/tcg/s390x/div.c @@ -1,6 +1,15 @@ #include +#include #include +/* Set asynchronously by the signal handler. */ +static volatile int signum; + +static void signal_handler(int n) +{ + signum = n; +} + static void test_dr(void) { register int32_t r0 asm("r0") = -1; @@ -65,11 +74,39 @@ static void test_dlgr(void) assert(r == 1); } +/* + * The most negative dividend divided by -1 yields a quotient that does not + * fit into 32 bits, so DR must raise a fixed-point-divide exception. + */ +static void test_dr_overflow(void) +{ + struct sigaction act = { .sa_handler = signal_handler }; + register int32_t r0 asm("r0"); + register int32_t r1 asm("r1"); + int32_t b = -1; + int err; + + err = sigaction(SIGFPE, &act, NULL); + assert(err == 0); + signum = -1; + + r0 = 0x80000000; + r1 = 0; + asm volatile("dr %[r0],%[b]" + : [r0] "+r" (r0), [r1] "+r" (r1) + : [b] "r" (b) + : "cc"); + assert(signum == SIGFPE); + + signal(SIGFPE, SIG_DFL); +} + int main(void) { test_dr(); test_dlr(); test_dsgr(); test_dlgr(); + test_dr_overflow(); return 0; } diff --git a/tests/tcg/s390x/prno-trng.c b/tests/tcg/s390x/prno-trng.c new file mode 100644 index 0000000000..43eea5db1d --- /dev/null +++ b/tests/tcg/s390x/prno-trng.c @@ -0,0 +1,67 @@ +/* + * Test that PERFORM RANDOM NUMBER OPERATION TRNG is interruptible. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include +#include +#include +#include + +static unsigned char buf1[16 * 1024 * 1024]; +static unsigned char buf2[16 * 1024 * 1024]; + +static volatile sig_atomic_t interrupted; + +static void sigprof_handler(int sig, siginfo_t *info, void *ucontext) +{ + struct ucontext *uc = ucontext; + unsigned long addr = uc->uc_mcontext.regs.psw.addr; + + if (*(unsigned short *)(addr - 4) == 0xb93c) { + interrupted++; + } +} + +static void prno_trng(void *b1, unsigned long l1, void *b2, unsigned long l2) +{ + register unsigned long r0 asm("r0") = 114; /* TRNG */ + register unsigned long r2 asm("r2") = (unsigned long)b1; + register unsigned long r3 asm("r3") = l1; + register unsigned long r4 asm("r4") = (unsigned long)b2; + register unsigned long r5 asm("r5") = l2; + + asm volatile("0: ppno %[r2],%[r4]\n" /* prno alias for old toolchains */ + " jo 0b" + : [r2] "+r" (r2), [r3] "+r" (r3) + , [r4] "+r" (r4), [r5] "+r" (r5) + : "r" (r0) + : "cc", "memory"); +} + +int main(void) +{ + struct itimerval it = { + .it_interval = { .tv_usec = 10000 }, /* 0.01s */ + .it_value = { .tv_usec = 10000 }, + }; + struct sigaction act = { + .sa_sigaction = sigprof_handler, + .sa_flags = SA_SIGINFO, + }; + int err; + + err = sigaction(SIGPROF, &act, NULL); + assert(err == 0); + err = setitimer(ITIMER_PROF, &it, NULL); + assert(err == 0); + + prno_trng(buf1, sizeof(buf1), buf2, sizeof(buf2)); + printf("interrupted %d times\n", interrupted); + assert(interrupted >= 3); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/s390x/stckf.c b/tests/tcg/s390x/stckf.c new file mode 100644 index 0000000000..51c8c9df9f --- /dev/null +++ b/tests/tcg/s390x/stckf.c @@ -0,0 +1,44 @@ +/* + * Test that a faulting STORE CLOCK FAST does not clobber the condition code. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include + +static void handle_sigsegv(int sig, siginfo_t *info, void *ucontext) +{ + mcontext_t *mcontext = &((ucontext_t *)ucontext)->uc_mcontext; + + /* The condition code must be the one set by SLGR, not garbage. */ + _exit(((mcontext->psw.mask >> 44) & 3) == 3 ? EXIT_SUCCESS : EXIT_FAILURE); +} + +int main(void) +{ + struct sigaction act = { + .sa_sigaction = handle_sigsegv, + .sa_flags = SA_SIGINFO, + }; + int err; + + err = sigaction(SIGSEGV, &act, NULL); + assert(err == 0); + + asm volatile( + "lghi %%r1,100\n" + "lghi %%r2,0\n" + "clgr %%r1,%%r2\n" /* CC_OP_LTUGTU_64 */ + /* cc_src=100 is not valid for CC_OP_SUBU */ + "ipm %%r0\n" /* force cc_src to env */ + "lghi %%r3,5\n" + "lghi %%r4,3\n" + "slgr %%r3,%%r4\n" /* CC_OP_SUBU, cc=3 */ + "lghi %%r5,0\n" + "stckf 0(%%r5)\n" /* faults; cc must stay 3 */ + : : : "r0", "r1", "r2", "r3", "r4", "r5", "cc", "memory"); + + return EXIT_FAILURE; +} diff --git a/tests/tcg/x86_64/Makefile.target b/tests/tcg/x86_64/Makefile.target index be20fc64e8..c48767fef8 100644 --- a/tests/tcg/x86_64/Makefile.target +++ b/tests/tcg/x86_64/Makefile.target @@ -15,6 +15,7 @@ X86_64_TESTS += vsyscall X86_64_TESTS += noexec X86_64_TESTS += cmpxchg X86_64_TESTS += adox +X86_64_TESTS += segment-prefixes X86_64_TESTS += test-1648 X86_64_TESTS += test-2175 X86_64_TESTS += cross-modifying-code diff --git a/tests/tcg/x86_64/segment-prefixes.c b/tests/tcg/x86_64/segment-prefixes.c new file mode 100644 index 0000000000..a7e6e285b2 --- /dev/null +++ b/tests/tcg/x86_64/segment-prefixes.c @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* See https://gitlab.com/qemu-project/qemu/-/work_items/3391 */ + +int main() +{ + int data = 0; + + /* Ensure that ignored segment override prefixes are actually ignored */ + asm volatile ( + "wrgsbase %0\n\t" + ".byte 0x65, 0x26\n\t" /* prefixes: GS + ES */ + "movb $0, 0\n\t" + ".byte 0x65, 0x2E\n\t" /* prefixes: GS + CS */ + "movb $0, 0\n\t" + ".byte 0x65, 0x36\n\t" /* prefixes: GS + SS */ + "movb $0, 0\n\t" + ".byte 0x65, 0x3E\n\t" /* prefixes: GS + DS */ + "movb $0, 0\n\t" + : + : "r" (&data) + : "memory" + ); + + return 0; +} diff --git a/tests/unit/meson.build b/tests/unit/meson.build index 5ba6b1a230..3a9866c1f2 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -75,6 +75,7 @@ if have_block 'test-blockjob': [testblock], 'test-blockjob-txn': [testblock], 'test-block-backend': [testblock], + 'test-block-accounting': [testblock], 'test-block-iothread': [testblock], 'test-write-threshold': [testblock], 'test-crypto-hash': [crypto], @@ -183,6 +184,12 @@ slow_tests = { 'test-crypto-tlscredsx509': 90, 'test-crypto-tlssession': 90, 'test-replication': 60, + 'rcutorture': 30, + 'test-rcu-list': 30, + 'test-rcu-simpleq': 30, + 'test-rcu-tailq': 30, + 'test-rcu-slist': 30, + 'test-thread-pool': 30, } foreach test_name, extra: tests @@ -204,5 +211,6 @@ foreach test_name, extra: tests protocol: 'tap', timeout: slow_tests.get(test_name, 30), priority: slow_tests.get(test_name, 30), - suite: ['unit']) + suite: ['unit'] + + (slow_tests.has_key(test_name) ? ['slow'] : [])) endforeach diff --git a/tests/unit/test-block-accounting.c b/tests/unit/test-block-accounting.c new file mode 100644 index 0000000000..7aae491cfc --- /dev/null +++ b/tests/unit/test-block-accounting.c @@ -0,0 +1,115 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * BlockAcctStats latency histogram locking regression test + * + * Copyright (c) 2026 Virtuozzo International GmbH. + * + * Regression test for missing stats->lock in + * block_latency_histogram_set()/block_latency_histograms_clear(), + * racing block_account_one_io() reading the same fields from an + * iothread. Aborts reliably before the fix, passes after it. + */ + +#include "qemu/osdep.h" +#include "block/block.h" +#include "block/accounting.h" +#include "system/block-backend.h" +#include "system/block-backend-io.h" +#include "qapi/error.h" +#include "qemu/main-loop.h" +#include "qemu/thread.h" + +#define RACE_DURATION_MS 2000 +#define NUM_READER_THREADS 8 + +static bool stop_workers; + +/* + * Different bin counts, so the writer's g_free()/g_new() churn can be + * caught mid-update. Values are small enough (nanoseconds) that plain + * back-to-back start/done calls exercise every bin without sleeping. + */ +static uint64List boundaries_a[] = { + { .next = &boundaries_a[1], .value = 1000 }, + { .next = &boundaries_a[2], .value = 5000 }, + { .next = NULL, .value = 50000 }, +}; + +static uint64List boundaries_b[] = { + { .next = &boundaries_b[1], .value = 800 }, + { .next = &boundaries_b[2], .value = 3000 }, + { .next = &boundaries_b[3], .value = 20000 }, + { .next = NULL, .value = 200000 }, +}; + +static void *writer_thread(void *opaque) +{ + BlockAcctStats *stats = opaque; + + while (!qatomic_read(&stop_workers)) { + block_latency_histogram_set(stats, BLOCK_ACCT_READ, boundaries_a); + block_latency_histogram_set(stats, BLOCK_ACCT_READ, boundaries_b); + block_latency_histograms_clear(stats); + } + + return NULL; +} + +static void *reader_thread(void *opaque) +{ + BlockAcctStats *stats = opaque; + + while (!qatomic_read(&stop_workers)) { + BlockAcctCookie cookie; + + block_acct_start(stats, &cookie, 4096, BLOCK_ACCT_READ); + block_acct_done(stats, &cookie); + } + + return NULL; +} + +static void test_latency_histogram_race(void) +{ + BlockBackend *blk = blk_new(qemu_get_aio_context(), + BLK_PERM_ALL, BLK_PERM_ALL); + BlockAcctStats *stats = blk_get_stats(blk); + QemuThread writer, readers[NUM_READER_THREADS]; + int i; + + /* Histogram has to be enabled (bins != NULL) before racing it. */ + g_assert(block_latency_histogram_set(stats, BLOCK_ACCT_READ, + boundaries_a) == 0); + + stop_workers = false; + qemu_thread_create(&writer, "hist-writer", writer_thread, stats, + QEMU_THREAD_JOINABLE); + for (i = 0; i < NUM_READER_THREADS; i++) { + qemu_thread_create(&readers[i], "hist-reader", reader_thread, stats, + QEMU_THREAD_JOINABLE); + } + + g_usleep(RACE_DURATION_MS * 1000); + qatomic_set(&stop_workers, true); + + qemu_thread_join(&writer); + for (i = 0; i < NUM_READER_THREADS; i++) { + qemu_thread_join(&readers[i]); + } + + blk_unref(blk); +} + +int main(int argc, char **argv) +{ + bdrv_init(); + qemu_init_main_loop(&error_abort); + + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/block-accounting/latency_histogram_race", + test_latency_histogram_race); + + return g_test_run(); +} diff --git a/tests/unit/test-blockjob.c b/tests/unit/test-blockjob.c index abdbe4b835..3b77d19b91 100644 --- a/tests/unit/test-blockjob.c +++ b/tests/unit/test-blockjob.c @@ -388,6 +388,105 @@ static void test_cancel_concluded(void) cancel_common(s); } +typedef struct PauseCountJob { + BlockJob common; + int n; + bool should_complete; +} PauseCountJob; + +static void pause_count_job_complete(Job *job, Error **errp) +{ + PauseCountJob *s = container_of(job, PauseCountJob, common.job); + s->should_complete = true; +} + +static int coroutine_fn pause_count_job_run(Job *job, Error **errp) +{ + PauseCountJob *s = container_of(job, PauseCountJob, common.job); + + while (!s->should_complete) { + if (job_is_cancelled(&s->common.job)) { + return 0; + } + s->n++; + /* + * Yields; while a pause is pending the yield is skipped and the job + * parks in job_pause_point() instead. + */ + job_sleep_ns(&s->common.job, 10 * 1000 * 1000); + } + + return 0; +} + +static const BlockJobDriver pause_count_job_driver = { + .job_driver = { + .instance_size = sizeof(PauseCountJob), + .free = block_job_free, + .user_resume = block_job_user_resume, + .run = pause_count_job_run, + .complete = pause_count_job_complete, + }, +}; + +/* + * A job that has reached its pause point must stay paused while a pause is + * still pending (pause_count > 0). An overlapping drain re-enters the job (one + * drain's job_resume() wakes it while the next drain's job_pause() is already + * counted); the job must not run or clear job->paused, otherwise + * job_set_aio_context() can observe paused == false and abort. + */ +static void test_pause_keeps_paused(void) +{ + BlockBackend *blk; + BlockJob *bjob; + PauseCountJob *s; + Job *job; + int n0; + + blk = create_blk(NULL); + bjob = mk_job(blk, "job0", &pause_count_job_driver, true, + JOB_MANUAL_FINALIZE | JOB_MANUAL_DISMISS); + s = container_of(bjob, PauseCountJob, common); + job = &bjob->job; + WITH_JOB_LOCK_GUARD() { + job_ref_locked(job); + } + + job_start(job); + + /* Pause the running job; it parks in job_pause_point() with paused set. */ + WITH_JOB_LOCK_GUARD() { + job_pause_locked(job); + g_assert_true(job->paused); + g_assert_cmpint(job->status, ==, JOB_STATUS_PAUSED); + } + n0 = s->n; + + /* + * Spurious wake while the pause is still pending. The job must stay parked: + * the bug clears job->paused, runs an iteration (s->n advances) and + * re-pauses, exposing a paused == false window. + */ + job_enter(job); + WITH_JOB_LOCK_GUARD() { + g_assert_true(job->paused); + } + g_assert_cmpint(s->n, ==, n0); + + /* Resume and tear down. */ + WITH_JOB_LOCK_GUARD() { + job_resume_locked(job); + } + job_cancel_sync(job, true); + WITH_JOB_LOCK_GUARD() { + Job *dummy = job; + job_dismiss_locked(&dummy, &error_abort); + job_unref_locked(job); + } + destroy_blk(blk); +} + int main(int argc, char **argv) { qemu_init_main_loop(&error_abort); @@ -402,5 +501,6 @@ int main(int argc, char **argv) g_test_add_func("/blockjob/cancel/standby", test_cancel_standby); g_test_add_func("/blockjob/cancel/pending", test_cancel_pending); g_test_add_func("/blockjob/cancel/concluded", test_cancel_concluded); + g_test_add_func("/blockjob/pause/keep_paused", test_pause_keeps_paused); return g_test_run(); } diff --git a/tests/unit/test-coroutine.c b/tests/unit/test-coroutine.c index 49d4d9b251..aa1f719b08 100644 --- a/tests/unit/test-coroutine.c +++ b/tests/unit/test-coroutine.c @@ -421,6 +421,57 @@ static void test_co_rwlock_downgrade(void) g_assert(c1_done); } +/* + * Check that a wake delivered before the sleeper parks is not lost. + * + * qemu_co_sleep_wake() is fire-and-forget: a caller cancelling a + * sleep/work loop may call it in the window after the sleeper has + * decided to sleep but before it has published itself inside + * qemu_co_sleep(). The wake must be sticky and shorten the next sleep + * rather than being dropped (which would block until the full sleep + * duration expired). + * + * No threads, timers or AioContext are needed: coroutines are + * cooperative, so ordering the wake before the sleep deterministically + * reproduces the state the racing waker would otherwise produce. + */ + +typedef struct { + QemuCoSleep w; + bool completed; +} CoSleepWakeData; + +static void coroutine_fn co_sleep_wake_entry(void *opaque) +{ + CoSleepWakeData *d = opaque; + + /* + * The wake was already delivered before we got here. qemu_co_sleep() + * must consume it and return without yielding. + */ + qemu_co_sleep(&d->w); + d->completed = true; +} + +static void test_co_sleep_wake_before_sleep(void) +{ + CoSleepWakeData d = { .w = { 0 }, .completed = false }; + Coroutine *co = qemu_coroutine_create(co_sleep_wake_entry, &d); + + /* Waker runs first, while no sleeper is parked on w. */ + qemu_co_sleep_wake(&d.w); + + /* + * Entering runs qemu_co_sleep(), which consumes the pending wake and + * returns without yielding, so the coroutine runs straight to + * completion in this single enter. With the pre-fix primitive the wake + * is dropped, qemu_co_sleep() parks, and completed stays false. + */ + qemu_coroutine_enter(co); + + g_assert(d.completed); +} + /* * Check that creation, enter, and return work */ @@ -660,6 +711,8 @@ int main(int argc, char **argv) g_test_add_func("/locking/co-mutex/lockable", test_co_mutex_lockable); g_test_add_func("/locking/co-rwlock/upgrade", test_co_rwlock_upgrade); g_test_add_func("/locking/co-rwlock/downgrade", test_co_rwlock_downgrade); + g_test_add_func("/locking/co-sleep/wake-before-sleep", + test_co_sleep_wake_before_sleep); if (g_test_perf()) { g_test_add_func("/perf/lifecycle", perf_lifecycle); g_test_add_func("/perf/nesting", perf_nesting); diff --git a/tests/unit/test-crypto-block.c b/tests/unit/test-crypto-block.c index 218e585f98..3ac7f17b2a 100644 --- a/tests/unit/test-crypto-block.c +++ b/tests/unit/test-crypto-block.c @@ -31,7 +31,8 @@ #endif #if (defined(_WIN32) || defined RUSAGE_THREAD) && \ - (defined(CONFIG_NETTLE) || defined(CONFIG_GCRYPT)) + (defined(CONFIG_NETTLE) || defined(CONFIG_GCRYPT) || \ + defined(CONFIG_GNUTLS_CRYPTO)) #define TEST_LUKS #else #undef TEST_LUKS diff --git a/tests/unit/test-crypto-cipher.c b/tests/unit/test-crypto-cipher.c index 1331d558cf..420c826df9 100644 --- a/tests/unit/test-crypto-cipher.c +++ b/tests/unit/test-crypto-cipher.c @@ -810,6 +810,230 @@ static void test_cipher_short_plaintext(void) qcrypto_cipher_free(cipher); } +typedef struct QCryptoCipherGcmTestData QCryptoCipherGcmTestData; +struct QCryptoCipherGcmTestData { + const char *path; + QCryptoCipherAlgo alg; + const char *key; + const char *iv; + /* associated data, or NULL for none */ + const char *aad; + const char *plaintext; + const char *ciphertext; + const char *tag; +}; + +/* + * AES-GCM test vectors from "The Galois/Counter Mode of Operation (GCM)" + * (McGrew & Viega, also NIST SP 800-38D), with a 96-bit IV and a 128-bit + * tag. Each entry's "Test case N" label is the numbered test case from that + * document (Appendix B / the GCM specification's test vectors). + */ +static QCryptoCipherGcmTestData gcm_test_data[] = { + { + /* Test case 2 */ + .path = "/crypto/cipher/aes-gcm-128-2", + .alg = QCRYPTO_CIPHER_ALGO_AES_128, + .key = "00000000000000000000000000000000", + .iv = "000000000000000000000000", + .plaintext = "00000000000000000000000000000000", + .ciphertext = "0388dace60b6a392f328c2b971b2fe78", + .tag = "ab6e47d42cec13bdf53a67b21257bddf", + }, + { + /* Test case 3 (no AAD) */ + .path = "/crypto/cipher/aes-gcm-128-3", + .alg = QCRYPTO_CIPHER_ALGO_AES_128, + .key = "feffe9928665731c6d6a8f9467308308", + .iv = "cafebabefacedbaddecaf888", + .plaintext = + "d9313225f88406e5a55909c5aff5269a" + "86a7a9531534f7da2e4c303d8a318a72" + "1c3c0c95956809532fcf0e2449a6b525" + "b16aedf5aa0de657ba637b391aafd255", + .ciphertext = + "42831ec2217774244b7221b784d0d49c" + "e3aa212f2c02a4e035c17e2329aca12e" + "21d514b25466931c7d8f6a5aac84aa05" + "1ba30b396a0aac973d58e091473f5985", + .tag = "4d5c2af327cd64a62cf35abd2ba6fab4", + }, + { + /* Test case 4 (with AAD) */ + .path = "/crypto/cipher/aes-gcm-128-4", + .alg = QCRYPTO_CIPHER_ALGO_AES_128, + .key = "feffe9928665731c6d6a8f9467308308", + .iv = "cafebabefacedbaddecaf888", + .aad = "feedfacedeadbeeffeedfacedeadbeefabaddad2", + .plaintext = + "d9313225f88406e5a55909c5aff5269a" + "86a7a9531534f7da2e4c303d8a318a72" + "1c3c0c95956809532fcf0e2449a6b525" + "b16aedf5aa0de657ba637b39", + .ciphertext = + "42831ec2217774244b7221b784d0d49c" + "e3aa212f2c02a4e035c17e2329aca12e" + "21d514b25466931c7d8f6a5aac84aa05" + "1ba30b396a0aac973d58e091", + .tag = "5bc94fbc3221a5db94fae95ae7121a47", + }, + { + /* Test case 15 (AES-256, no AAD) */ + .path = "/crypto/cipher/aes-gcm-256-15", + .alg = QCRYPTO_CIPHER_ALGO_AES_256, + .key = + "feffe9928665731c6d6a8f9467308308" + "feffe9928665731c6d6a8f9467308308", + .iv = "cafebabefacedbaddecaf888", + .plaintext = + "d9313225f88406e5a55909c5aff5269a" + "86a7a9531534f7da2e4c303d8a318a72" + "1c3c0c95956809532fcf0e2449a6b525" + "b16aedf5aa0de657ba637b391aafd255", + .ciphertext = + "522dc1f099567d07f47f37a32a84427d" + "643a8cdcbfe5c0c97598a2bd2555d1aa" + "8cb08e48590dbb3da7b08b1056828838" + "c5f61e6393ba7a0abcc9f662898015ad", + .tag = "b094dac5d93471bdec1a502270e3cc6c", + }, + { + /* Test case 16 (AES-256, with AAD) */ + .path = "/crypto/cipher/aes-gcm-256-16", + .alg = QCRYPTO_CIPHER_ALGO_AES_256, + .key = + "feffe9928665731c6d6a8f9467308308" + "feffe9928665731c6d6a8f9467308308", + .iv = "cafebabefacedbaddecaf888", + .aad = "feedfacedeadbeeffeedfacedeadbeefabaddad2", + .plaintext = + "d9313225f88406e5a55909c5aff5269a" + "86a7a9531534f7da2e4c303d8a318a72" + "1c3c0c95956809532fcf0e2449a6b525" + "b16aedf5aa0de657ba637b39", + .ciphertext = + "522dc1f099567d07f47f37a32a84427d" + "643a8cdcbfe5c0c97598a2bd2555d1aa" + "8cb08e48590dbb3da7b08b1056828838" + "c5f61e6393ba7a0abcc9f662", + .tag = "76fc6ece0f4e1768cddf8853bb2d551b", + }, +}; + +static void test_cipher_gcm(const void *opaque) +{ + const QCryptoCipherGcmTestData *data = opaque; + g_autofree uint8_t *key = NULL; + g_autofree uint8_t *iv = NULL; + g_autofree uint8_t *aad = NULL; + g_autofree uint8_t *ptext = NULL; + g_autofree uint8_t *ctext = NULL; + g_autofree uint8_t *tagexp = NULL; + g_autofree uint8_t *out = NULL; + uint8_t tag[16]; + size_t nkey; + size_t niv; + size_t naad = 0; + size_t nptext; + size_t nctext; + size_t ntag; + QCryptoCipher *cipher; + + nkey = unhex_string(data->key, &key); + niv = unhex_string(data->iv, &iv); + nptext = unhex_string(data->plaintext, &ptext); + nctext = unhex_string(data->ciphertext, &ctext); + ntag = unhex_string(data->tag, &tagexp); + if (data->aad) { + naad = unhex_string(data->aad, &aad); + } + + g_assert_cmpint(nptext, ==, nctext); + g_assert_cmpint(ntag, ==, sizeof(tag)); + out = g_new0(uint8_t, nptext); + + /* Encrypt: plaintext -> ciphertext, then read back the tag. */ + cipher = qcrypto_cipher_new(data->alg, QCRYPTO_CIPHER_MODE_GCM, + key, nkey, &error_abort); + g_assert(cipher != NULL); + g_assert(qcrypto_cipher_setiv(cipher, iv, niv, &error_abort) == 0); + if (naad) { + g_assert(qcrypto_cipher_setaad(cipher, aad, naad, &error_abort) == 0); + } + g_assert(qcrypto_cipher_encrypt(cipher, ptext, out, nptext, + &error_abort) == 0); + g_assert_cmpmem(out, nptext, ctext, nctext); + g_assert(qcrypto_cipher_gettag(cipher, tag, sizeof(tag), + &error_abort) == 0); + g_assert_cmpmem(tag, sizeof(tag), tagexp, ntag); + qcrypto_cipher_free(cipher); + + /* Decrypt: ciphertext -> plaintext, recomputed tag must match. */ + memset(out, 0, nptext); + cipher = qcrypto_cipher_new(data->alg, QCRYPTO_CIPHER_MODE_GCM, + key, nkey, &error_abort); + g_assert(cipher != NULL); + g_assert(qcrypto_cipher_setiv(cipher, iv, niv, &error_abort) == 0); + if (naad) { + g_assert(qcrypto_cipher_setaad(cipher, aad, naad, &error_abort) == 0); + } + g_assert(qcrypto_cipher_decrypt(cipher, ctext, out, nctext, + &error_abort) == 0); + g_assert_cmpmem(out, nctext, ptext, nptext); + g_assert(qcrypto_cipher_gettag(cipher, tag, sizeof(tag), + &error_abort) == 0); + g_assert_cmpmem(tag, sizeof(tag), tagexp, ntag); + qcrypto_cipher_free(cipher); +} + +/* + * Corrupt one ciphertext byte and confirm the recomputed GCM tag no longer + * matches: the authentication tag must detect tampering. + */ +static void test_cipher_gcm_tamper(const void *opaque) +{ + const QCryptoCipherGcmTestData *data = opaque; + g_autofree uint8_t *key = NULL; + g_autofree uint8_t *iv = NULL; + g_autofree uint8_t *aad = NULL; + g_autofree uint8_t *ctext = NULL; + g_autofree uint8_t *tagexp = NULL; + g_autofree uint8_t *out = NULL; + uint8_t tag[16]; + size_t nkey; + size_t niv; + size_t naad = 0; + size_t nctext; + size_t ntag; + QCryptoCipher *cipher; + + nkey = unhex_string(data->key, &key); + niv = unhex_string(data->iv, &iv); + nctext = unhex_string(data->ciphertext, &ctext); + ntag = unhex_string(data->tag, &tagexp); + if (data->aad) { + naad = unhex_string(data->aad, &aad); + } + out = g_new0(uint8_t, nctext); + + /* Flip one ciphertext bit before decrypting. */ + ctext[0] ^= 0x01; + + cipher = qcrypto_cipher_new(data->alg, QCRYPTO_CIPHER_MODE_GCM, + key, nkey, &error_abort); + g_assert(cipher != NULL); + g_assert(qcrypto_cipher_setiv(cipher, iv, niv, &error_abort) == 0); + if (naad) { + g_assert(qcrypto_cipher_setaad(cipher, aad, naad, &error_abort) == 0); + } + g_assert(qcrypto_cipher_decrypt(cipher, ctext, out, nctext, + &error_abort) == 0); + g_assert(qcrypto_cipher_gettag(cipher, tag, sizeof(tag), + &error_abort) == 0); + g_assert(memcmp(tag, tagexp, ntag) != 0); + qcrypto_cipher_free(cipher); +} + int main(int argc, char **argv) { size_t i; @@ -828,6 +1052,22 @@ int main(int argc, char **argv) } } + for (i = 0; i < G_N_ELEMENTS(gcm_test_data); i++) { + if (qcrypto_cipher_supports(gcm_test_data[i].alg, + QCRYPTO_CIPHER_MODE_GCM)) { + g_autofree char *tamper = g_strdup_printf("%s/tamper", + gcm_test_data[i].path); + + g_test_add_data_func(gcm_test_data[i].path, &gcm_test_data[i], + test_cipher_gcm); + g_test_add_data_func(tamper, &gcm_test_data[i], + test_cipher_gcm_tamper); + } else { + g_printerr("# skip unsupported %s:gcm\n", + QCryptoCipherAlgo_str(gcm_test_data[i].alg)); + } + } + if (qcrypto_cipher_supports(QCRYPTO_CIPHER_ALGO_AES_256, QCRYPTO_CIPHER_MODE_CBC)) { g_test_add_func("/crypto/cipher/null-iv", diff --git a/tests/unit/test-qdev.c b/tests/unit/test-qdev.c index 20eae38e03..77c3eee717 100644 --- a/tests/unit/test-qdev.c +++ b/tests/unit/test-qdev.c @@ -78,6 +78,16 @@ static void test_qdev_free_properties(void) object_unref(mt); } +static void test_qdev_double_realization(void) +{ + MyDev *mt = STATIC_TYPE(object_new(TYPE_MY_DEV)); + + qdev_realize(DEVICE(mt), NULL, &error_fatal); + qdev_realize(DEVICE(mt), NULL, &error_fatal); + object_unparent(OBJECT(mt)); + object_unref(OBJECT(mt)); +} + int main(int argc, char **argv) { @@ -90,6 +100,9 @@ int main(int argc, char **argv) g_test_add_func("/qdev/free-properties", test_qdev_free_properties); + g_test_add_func("/qdev/double-realization", + test_qdev_double_realization); + g_test_run(); return 0; diff --git a/tests/unit/test-util-filemonitor.c b/tests/unit/test-util-filemonitor.c index 02e67fc96a..972651dc1e 100644 --- a/tests/unit/test-util-filemonitor.c +++ b/tests/unit/test-util-filemonitor.c @@ -27,6 +27,10 @@ #include +#ifdef __FreeBSD__ +#include +#endif + enum { QFILE_MONITOR_TEST_OP_ADD_WATCH, QFILE_MONITOR_TEST_OP_DEL_WATCH, @@ -221,6 +225,24 @@ qemu_file_monitor_test_expect(QFileMonitorTestData *data, } +static bool +expect_broken_ignored(void) +{ +#if defined(__FreeBSD__) && __FreeBSD_version >= 1500051 + int osreldate; + + osreldate = getosreldate(); + if (osreldate == -1) { + g_printerr("Unable to call getosreldate: %s\n", strerror(errno)); + abort(); + } + return osreldate < 1501501 || (osreldate >= 1600000 && osreldate < 1600019); +#else + return false; +#endif +} + + static void test_file_monitor_events(void) { @@ -360,7 +382,7 @@ test_file_monitor_events(void) { .type = QFILE_MONITOR_TEST_OP_EVENT, .filesrc = "one.txt", .watchid = &watch4, .eventid = QFILE_MONITOR_EVENT_DELETED }, -#ifdef __FreeBSD__ +#if defined(__FreeBSD__) && __FreeBSD_version < 1500051 { .type = QFILE_MONITOR_TEST_OP_EVENT, .filesrc = "two.txt", .watchid = &watch0, .eventid = QFILE_MONITOR_EVENT_DELETED }, @@ -539,6 +561,11 @@ test_file_monitor_events(void) g_printerr("Event id=%" PRIx64 " event=%d file=%s\n", *op->watchid, op->eventid, op->filesrc); } + if (op->eventid == QFILE_MONITOR_EVENT_IGNORED && + expect_broken_ignored()) { + g_printerr("Expect ignored event to be broken, skipping\n"); + break; + } if (!qemu_file_monitor_test_expect(&data, *op->watchid, op->eventid, op->filesrc, op->swapnext)) diff --git a/tests/unit/test-util-sockets.c b/tests/unit/test-util-sockets.c index ee66d727c3..ab3f39c3ef 100644 --- a/tests/unit/test-util-sockets.c +++ b/tests/unit/test-util-sockets.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/main-loop.h" #include "qemu/sockets.h" #include "qapi/error.h" #include "socket-helpers.h" diff --git a/tests/vm/freebsd b/tests/vm/freebsd index ea09b21fbc..bfb1a85af3 100755 --- a/tests/vm/freebsd +++ b/tests/vm/freebsd @@ -28,8 +28,8 @@ class FreeBSDVM(basevm.BaseVM): name = "freebsd" arch = "x86_64" - link = "https://download.freebsd.org/releases/CI-IMAGES/14.3-RELEASE/amd64/Latest/FreeBSD-14.3-RELEASE-amd64-BASIC-CI.raw.xz" - csum = "ec0f5a4bbe63aa50a725d9fee0f1931f850e9a21cbebdadb991df00f168d6805" + link = "https://download.freebsd.org/releases/CI-IMAGES/14.4-RELEASE/amd64/Latest/FreeBSD-14.4-RELEASE-amd64-BASIC-CI.raw.xz" + csum = "627937af554eb6522891875ddc8cf5b86b40e9a12143c1b951f830b3f691de49" size = "20G" BUILD_SCRIPT = """ diff --git a/tests/vm/generated/freebsd.json b/tests/vm/generated/freebsd.json index 08b6eb6155..dfa8b41a52 100644 --- a/tests/vm/generated/freebsd.json +++ b/tests/vm/generated/freebsd.json @@ -49,14 +49,10 @@ "ncurses", "nettle", "ninja", - "opencv", "pixman", "pkgconf", "png", - "py311-numpy", - "py311-pillow", "py311-pip", - "py311-pyyaml", "py311-setuptools", "py311-sphinx", "py311-sphinx_rtd_theme", diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index d689f187c4..8e3729a54d 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -25,7 +25,7 @@ #include "trace.h" #include "standard-headers/drm/drm_fourcc.h" -EGLDisplay *qemu_egl_display; +EGLDisplay qemu_egl_display; EGLConfig qemu_egl_config; DisplayGLMode qemu_egl_mode; bool qemu_egl_angle_d3d; @@ -736,19 +736,22 @@ bool egl_init(const char *rendernode, DisplayGLMode mode, Error **errp) void egl_cleanup(void) { + if (qemu_egl_display) { + eglReleaseThread(); + } + if (qemu_egl_rn_ctx) { eglDestroyContext(qemu_egl_display, qemu_egl_rn_ctx); qemu_egl_rn_ctx = NULL; } + if (qemu_egl_display) { + eglTerminate(qemu_egl_display); + qemu_egl_display = NULL; + } + #ifdef CONFIG_GBM g_clear_pointer(&qemu_egl_rn_gbm_dev, gbm_device_destroy); g_clear_fd(&qemu_egl_rn_fd, NULL); #endif - - if (qemu_egl_display) { - eglReleaseThread(); - eglTerminate(qemu_egl_display); - qemu_egl_display = NULL; - } } diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index 7c5c9b2428..adc81f34b1 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -91,6 +91,7 @@ void gd_egl_draw(VirtualConsole *vc) } else { qemu_dmabuf_set_draw_submitted(dmabuf, false); } + qemu_console_hw_gl_block(vc->gfx.dcl.con, true); } #endif gd_egl_scanout_flush(&vc->gfx.dcl, 0, 0, vc->gfx.w, vc->gfx.h); @@ -405,14 +406,11 @@ void gd_egl_flush(DisplayChangeListener *dcl, if (vc->gfx.guest_fb.dmabuf && !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { - qemu_console_hw_gl_block(vc->gfx.dcl.con, true); qemu_dmabuf_set_draw_submitted(vc->gfx.guest_fb.dmabuf, true); gtk_egl_set_scanout_mode(vc, true); - gtk_widget_queue_draw_area(area, x, y, w, h); - return; } - gd_egl_scanout_flush(&vc->gfx.dcl, x, y, w, h); + gtk_widget_queue_draw_area(area, x, y, w, h); } void gtk_egl_init(DisplayGLMode mode) diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index 23806b9d01..29497019ee 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -86,6 +86,7 @@ void gd_gl_area_draw(VirtualConsole *vc) } else { qemu_dmabuf_set_draw_submitted(dmabuf, false); } + qemu_console_hw_gl_block(vc->gfx.dcl.con, true); } #endif @@ -163,27 +164,6 @@ void gd_gl_area_refresh(DisplayChangeListener *dcl) gd_update_monitor_refresh_rate(vc, vc->window ? vc->window : vc->gfx.drawing_area); - if (vc->gfx.guest_fb.dmabuf && - qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { - /* - * gd_egl_refresh() calls gd_egl_draw() if a DMA-BUF draw has already - * been submitted, but this function does not call gd_gl_area_draw() in - * such a case due to display corruption. - * - * Calling gd_gl_area_draw() is necessary to prevent a situation where - * there is a scheduled draw event but it won't happen bacause the window - * is currently in inactive state (minimized or tabified). If draw is not - * done for a long time, gl_block timeout and/or fence timeout (on the - * guest) will happen eventually. - * - * However, it is found that calling gd_gl_area_draw() here causes guest - * display corruption on a Wayland Compositor. The display corruption is - * more serious than the possible fence timeout so gd_gl_area_draw() is - * omitted for now. - */ - return; - } - if (!vc->gfx.gls) { if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { return; @@ -347,7 +327,6 @@ void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, if (vc->gfx.guest_fb.dmabuf && !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { - qemu_console_hw_gl_block(vc->gfx.dcl.con, true); qemu_dmabuf_set_draw_submitted(vc->gfx.guest_fb.dmabuf, true); gtk_gl_area_set_scanout_mode(vc, true); } diff --git a/ui/input-barrier.c b/ui/input-barrier.c index d07027114a..0a238d4010 100644 --- a/ui/input-barrier.c +++ b/ui/input-barrier.c @@ -87,7 +87,7 @@ static kbd_layout_t *kbd_layout; static unsigned int input_barrier_to_linux(uint16_t keyid, uint16_t keycode) { /* keycode is optional, if it is not provided use keyid */ - if (keycode && keycode <= qemu_input_map_xorgkbd_to_linux_len) { + if (keycode && keycode < qemu_input_map_xorgkbd_to_linux_len) { return qemu_input_map_xorgkbd_to_linux[keycode]; } diff --git a/ui/ui-hmp-cmds.c b/ui/ui-hmp-cmds.c index 06f4030ce4..806a7bece7 100644 --- a/ui/ui-hmp-cmds.c +++ b/ui/ui-hmp-cmds.c @@ -19,7 +19,7 @@ #endif #include "monitor/hmp.h" #include "monitor/hmp-completion.h" -#include "monitor/monitor-internal.h" +#include "monitor/monitor.h" #include "qapi/error.h" #include "qapi/qapi-commands-ui.h" #include "qobject/qdict.h" @@ -343,8 +343,8 @@ void hmp_change_vnc(Monitor *mon, const char *device, const char *target, return; } if (!arg) { - MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, parent_obj); - monitor_read_password(hmp_mon, hmp_change_read_arg, NULL); + MonitorHMP *hmp = MONITOR_HMP(mon); + monitor_read_password(hmp, hmp_change_read_arg, NULL); } else { qmp_change_vnc_password(arg, errp); } diff --git a/ui/vnc.c b/ui/vnc.c index b2b69923b7..656768f9c9 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -608,15 +608,7 @@ bool vnc_display_reload_certs(const char *id, Error **errp) 3) resolutions > 1024 */ -static int vnc_update_client(VncState *vs, int has_dirty); -static void vnc_disconnect_start(VncState *vs); - static void vnc_colordepth(VncState *vs); -static void framebuffer_update_request(VncState *vs, int incremental, - int x_position, int y_position, - int w, int h); -static void vnc_refresh(DisplayChangeListener *dcl); -static int vnc_refresh_server_surface(VncDisplay *vd); static int vnc_width(VncDisplay *vd) { @@ -1763,7 +1755,8 @@ static void check_pointer_type_change(Notifier *notifier, void *data) vs->absolute = absolute; } -static void pointer_event(VncState *vs, int button_mask, int x, int y) +static void pointer_event(VncState *vs, uint8_t button_mask, + uint16_t x, uint16_t y) { static uint32_t bmap[INPUT_BUTTON__MAX] = { [INPUT_BUTTON_LEFT] = 0x01, @@ -1841,7 +1834,7 @@ static void kbd_leds(Notifier *notifier, void *data) } } -static void do_key_event(VncState *vs, int down, int keycode, int sym) +static void do_key_event(VncState *vs, int down, int keycode, uint32_t sym) { unsigned int lnx = qemu_input_key_number_to_linux(keycode); @@ -2019,7 +2012,7 @@ static const char *code2name(int keycode) return QKeyCode_str(qemu_input_key_number_to_qcode(keycode)); } -static void key_event(VncState *vs, int down, uint32_t sym) +static void key_event(VncState *vs, bool down, uint32_t sym) { int keycode; int lsym = sym; @@ -2034,8 +2027,8 @@ static void key_event(VncState *vs, int down, uint32_t sym) do_key_event(vs, down, keycode, sym); } -static void ext_key_event(VncState *vs, int down, - uint32_t sym, uint16_t keycode) +static void ext_key_event(VncState *vs, bool down, + uint32_t sym, uint32_t keycode) { /* if the user specifies a keyboard layout, always use it */ if (keyboard_layout) { @@ -2046,8 +2039,9 @@ static void ext_key_event(VncState *vs, int down, } } -static void framebuffer_update_request(VncState *vs, int incremental, - int x, int y, int w, int h) +static void framebuffer_update_request(VncState *vs, uint8_t incremental, + uint16_t x, uint16_t y, + uint16_t w, uint16_t h) { if (incremental) { if (vs->update != VNC_STATE_UPDATE_FORCE) { @@ -2250,10 +2244,11 @@ static void send_color_map(VncState *vs) vnc_unlock_output(vs); } -static void set_pixel_format(VncState *vs, int bits_per_pixel, - int big_endian_flag, int true_color_flag, - int red_max, int green_max, int blue_max, - int red_shift, int green_shift, int blue_shift) +static void set_pixel_format(VncState *vs, uint8_t bits_per_pixel, + uint8_t big_endian_flag, uint8_t true_color_flag, + uint16_t red_max, uint16_t green_max, + uint16_t blue_max, uint8_t red_shift, + uint8_t green_shift, uint8_t blue_shift) { if (!true_color_flag) { /* Expose a reasonable default 256 color map */ @@ -2276,18 +2271,30 @@ static void set_pixel_format(VncState *vs, int bits_per_pixel, return; } + if (red_max > UINT8_MAX || green_max > UINT8_MAX || blue_max > UINT8_MAX) { + vnc_client_error(vs); + return; + } + + if (red_shift >= bits_per_pixel || red_shift >= 32 || + green_shift >= bits_per_pixel || green_shift >= 32 || + blue_shift >= bits_per_pixel || blue_shift >= 32) { + vnc_client_error(vs); + return; + } + vs->client_pf.rmax = red_max ? red_max : 0xFF; vs->client_pf.rbits = ctpopl(red_max); vs->client_pf.rshift = red_shift; - vs->client_pf.rmask = red_max << red_shift; + vs->client_pf.rmask = (uint32_t)red_max << red_shift; vs->client_pf.gmax = green_max ? green_max : 0xFF; vs->client_pf.gbits = ctpopl(green_max); vs->client_pf.gshift = green_shift; - vs->client_pf.gmask = green_max << green_shift; + vs->client_pf.gmask = (uint32_t)green_max << green_shift; vs->client_pf.bmax = blue_max ? blue_max : 0xFF; vs->client_pf.bbits = ctpopl(blue_max); vs->client_pf.bshift = blue_shift; - vs->client_pf.bmask = blue_max << blue_shift; + vs->client_pf.bmask = (uint32_t)blue_max << blue_shift; vs->client_pf.bits_per_pixel = bits_per_pixel; vs->client_pf.bytes_per_pixel = bits_per_pixel / 8; vs->client_pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel; @@ -2998,16 +3005,23 @@ void vnc_sent_lossy_rect(VncWorker *worker, int x, int y, int w, int h) } } -static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y) +static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y, + int height) { VncState *vs; int sty = y / VNC_STAT_RECT; int stx = x / VNC_STAT_RECT; int has_dirty = 0; + int rows; y = QEMU_ALIGN_DOWN(y, VNC_STAT_RECT); x = QEMU_ALIGN_DOWN(x, VNC_STAT_RECT); + rows = MIN(VNC_STAT_RECT, height - y); + if (rows <= 0) { + return 0; + } + QTAILQ_FOREACH(vs, &vd->clients, next) { VncConnection *vc = container_of(vs, VncConnection, vs); int j; @@ -3022,7 +3036,7 @@ static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y) } vc->worker.lossy_rect[sty][stx] = 0; - for (j = 0; j < VNC_STAT_RECT; ++j) { + for (j = 0; j < rows; ++j) { bitmap_set(vs->dirty[y + j], x / VNC_DIRTY_PIXELS_PER_BIT, VNC_STAT_RECT / VNC_DIRTY_PIXELS_PER_BIT); @@ -3073,7 +3087,7 @@ static int vnc_update_stats(VncDisplay *vd, struct timeval * tv) if (timercmp(&res, &VNC_REFRESH_LOSSY, >)) { rect->freq = 0; - has_dirty += vnc_refresh_lossy_rect(vd, x, y); + has_dirty += vnc_refresh_lossy_rect(vd, x, y, height); memset(rect->times, 0, sizeof (rect->times)); continue ; } diff --git a/util/cpuinfo-aarch64.c b/util/cpuinfo-aarch64.c index 288074c08f..4ce6deb7c8 100644 --- a/util/cpuinfo-aarch64.c +++ b/util/cpuinfo-aarch64.c @@ -72,6 +72,7 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) unsigned long hwcap2 = qemu_getauxval(AT_HWCAP2); info |= (hwcap2 & HWCAP2_BTI ? CPUINFO_BTI : 0); + info |= (hwcap2 & HWCAP2_CSSC ? CPUINFO_CSSC : 0); #endif #ifdef CONFIG_DARWIN info |= sysctl_for_bool("hw.optional.arm.FEAT_LSE") * CPUINFO_LSE; @@ -79,6 +80,7 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) info |= sysctl_for_bool("hw.optional.arm.FEAT_AES") * CPUINFO_AES; info |= sysctl_for_bool("hw.optional.arm.FEAT_PMULL") * CPUINFO_PMULL; info |= sysctl_for_bool("hw.optional.arm.FEAT_BTI") * CPUINFO_BTI; + info |= sysctl_for_bool("hw.optional.arm.FEAT_CSSC") * CPUINFO_CSSC; #endif #if defined(__OpenBSD__) && !defined(CONFIG_ELF_AUX_INFO) int mib[2]; diff --git a/util/cpuinfo-riscv.c b/util/cpuinfo-riscv.c index 0291b7218a..8c48b1ea91 100644 --- a/util/cpuinfo-riscv.c +++ b/util/cpuinfo-riscv.c @@ -36,7 +36,7 @@ static void sigill_handler(int signo, siginfo_t *si, void *data) /* Called both as constructor and (possibly) via other constructors. */ unsigned __attribute__((constructor)) cpuinfo_init(void) { - unsigned left = CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZBS + unsigned left = CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZBS | CPUINFO_ZBKB | CPUINFO_ZICOND | CPUINFO_ZVE64X; unsigned info = cpuinfo; @@ -60,6 +60,9 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) #if defined(__riscv_arch_test) && \ (defined(__riscv_vector) || defined(__riscv_zve64x)) info |= CPUINFO_ZVE64X; +#endif +#if defined(__riscv_arch_test) && defined(__riscv_zbkb) + info |= CPUINFO_ZBKB; #endif left &= ~info; @@ -76,7 +79,8 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) info |= pair.value & RISCV_HWPROBE_EXT_ZBA ? CPUINFO_ZBA : 0; info |= pair.value & RISCV_HWPROBE_EXT_ZBB ? CPUINFO_ZBB : 0; info |= pair.value & RISCV_HWPROBE_EXT_ZBS ? CPUINFO_ZBS : 0; - left &= ~(CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZBS); + info |= pair.value & RISCV_HWPROBE_EXT_ZBKB ? CPUINFO_ZBKB : 0; + left &= ~(CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZBS | CPUINFO_ZBKB); #ifdef RISCV_HWPROBE_EXT_ZICOND info |= pair.value & RISCV_HWPROBE_EXT_ZICOND ? CPUINFO_ZICOND : 0; left &= ~CPUINFO_ZICOND; @@ -131,6 +135,15 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) left &= ~CPUINFO_ZBS; } + if (left & CPUINFO_ZBKB) { + /* Probe for Zbkb: brev8 zero,zero. */ + got_sigill = 0; + asm volatile(".insn i 0x13, 5, zero, zero, 0x687" + : : : "memory"); + info |= got_sigill ? 0 : CPUINFO_ZBKB; + left &= ~CPUINFO_ZBKB; + } + if (left & CPUINFO_ZICOND) { /* Probe for Zicond: czero.eqz zero,zero,zero. */ got_sigill = 0; diff --git a/util/qemu-coroutine-sleep.c b/util/qemu-coroutine-sleep.c index edef117284..19ded0b6fd 100644 --- a/util/qemu-coroutine-sleep.c +++ b/util/qemu-coroutine-sleep.c @@ -18,20 +18,29 @@ static const char *qemu_co_sleep_ns__scheduled = "qemu_co_sleep_ns"; +/* + * Sentinel stored in QemuCoSleep::to_wake by qemu_co_sleep_wake() when no + * sleeper has parked yet. The next qemu_co_sleep() consumes it and returns + * without yielding, so a wake that races the arming of a sleep is never + * lost. + */ +#define QEMU_CO_SLEEP_PENDING ((Coroutine *)(uintptr_t)1) + void qemu_co_sleep_wake(QemuCoSleep *w) { Coroutine *co; - co = w->to_wake; - w->to_wake = NULL; - if (co) { - /* Write of schedule protected by barrier write in aio_co_schedule */ - const char *scheduled = qatomic_cmpxchg(&co->scheduled, - qemu_co_sleep_ns__scheduled, NULL); - - assert(scheduled == qemu_co_sleep_ns__scheduled); - aio_co_wake(co); + co = qatomic_xchg(&w->to_wake, QEMU_CO_SLEEP_PENDING); + if (co == NULL || co == QEMU_CO_SLEEP_PENDING) { + /* No sleeper, or a wake is already pending. */ + return; } + + /* Write of scheduled protected by barrier write in aio_co_schedule */ + const char *scheduled = qatomic_cmpxchg(&co->scheduled, + qemu_co_sleep_ns__scheduled, NULL); + assert(scheduled == qemu_co_sleep_ns__scheduled); + aio_co_wake(co); } static void co_sleep_cb(void *opaque) @@ -43,6 +52,7 @@ static void co_sleep_cb(void *opaque) void coroutine_fn qemu_co_sleep(QemuCoSleep *w) { Coroutine *co = qemu_coroutine_self(); + Coroutine *prev; const char *scheduled = qatomic_cmpxchg(&co->scheduled, NULL, qemu_co_sleep_ns__scheduled); @@ -53,11 +63,23 @@ void coroutine_fn qemu_co_sleep(QemuCoSleep *w) abort(); } - w->to_wake = co; + /* + * Publish ourselves as the sleeper. A wake delivered before we got here, + * or one racing this publish, leaves QEMU_CO_SLEEP_PENDING in to_wake; + * the cmpxchg then fails and we consume the wake without yielding. + */ + prev = qatomic_cmpxchg(&w->to_wake, NULL, co); + if (prev == QEMU_CO_SLEEP_PENDING) { + qatomic_set(&w->to_wake, NULL); + qatomic_set(&co->scheduled, NULL); + return; + } + assert(prev == NULL); + qemu_coroutine_yield(); - /* w->to_wake is cleared before resuming this coroutine. */ - assert(w->to_wake == NULL); + /* The waker left QEMU_CO_SLEEP_PENDING; clear it for the next sleep. */ + qatomic_set(&w->to_wake, NULL); } void coroutine_fn qemu_co_sleep_ns_wakeable(QemuCoSleep *w, @@ -70,9 +92,10 @@ void coroutine_fn qemu_co_sleep_ns_wakeable(QemuCoSleep *w, timer_mod(&ts, qemu_clock_get_ns(type) + ns); /* - * The timer will fire in the current AiOContext, so the callback - * must happen after qemu_co_sleep yields and there is no race - * between timer_mod and qemu_co_sleep. + * A wake racing with the arming of the sleep -- including the timer + * we just armed firing in another AioContext before qemu_co_sleep() + * publishes itself -- is captured by the sticky PENDING state in + * qemu_co_sleep_wake() and consumed here without yielding. */ qemu_co_sleep(w); timer_del(&ts);