diff --git a/0001-block-Fix-crash-when-loading-snapshot-on-inactive-no.patch b/0001-block-Fix-crash-when-loading-snapshot-on-inactive-no.patch new file mode 100644 index 0000000000000000000000000000000000000000..b825cd30c8529f935efe2bd334b5ce81a92a8c6f --- /dev/null +++ b/0001-block-Fix-crash-when-loading-snapshot-on-inactive-no.patch @@ -0,0 +1,53 @@ +From e2e01b3a771faded6fbc87d0eeca9612d3f0447b Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 1 Dec 2023 15:25:18 +0100 +Subject: [PATCH 001/293] block: Fix crash when loading snapshot on inactive + node + +bdrv_is_read_only() only checks if the node is configured to be +read-only eventually, but even if it returns false, writing to the node +may not be permitted at the moment (because it's inactive). + +bdrv_is_writable() checks that the node can be written to right now, and +this is what the snapshot operations really need. + +Change bdrv_can_snapshot() to use bdrv_is_writable() to fix crashes like +the following: + +$ ./qemu-system-x86_64 -hda /tmp/test.qcow2 -loadvm foo -incoming defer +qemu-system-x86_64: ../block/io.c:1990: int bdrv_co_write_req_prepare(BdrvChild *, int64_t, int64_t, BdrvTrackedRequest *, int): Assertion `!(bs->open_flags & BDRV_O_INACTIVE)' failed. + +The resulting error message after this patch isn't perfect yet, but at +least it doesn't crash any more: + +$ ./qemu-system-x86_64 -hda /tmp/test.qcow2 -loadvm foo -incoming defer +qemu-system-x86_64: Device 'ide0-hd0' is writable but does not support snapshots + +Signed-off-by: Kevin Wolf +Message-ID: <20231201142520.32255-2-kwolf@redhat.com> +Signed-off-by: Kevin Wolf +(cherry picked from commit d3007d348adaaf04ee8b099a475282034a662414) +Signed-off-by: Michael Tokarev +--- + block/snapshot.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/block/snapshot.c b/block/snapshot.c +index ec8cf48..c4d40e8 100644 +--- a/block/snapshot.c ++++ b/block/snapshot.c +@@ -196,8 +196,10 @@ bdrv_snapshot_fallback(BlockDriverState *bs) + int bdrv_can_snapshot(BlockDriverState *bs) + { + BlockDriver *drv = bs->drv; ++ + GLOBAL_STATE_CODE(); +- if (!drv || !bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) { ++ ++ if (!drv || !bdrv_is_inserted(bs) || !bdrv_is_writable(bs)) { + return 0; + } + +-- +1.8.3.1 + diff --git a/0001-virtiofsd-Drop-membership-of-all-supplementary-groups.patch b/0001-virtiofsd-Drop-membership-of-all-supplementary-groups.patch deleted file mode 100644 index 7c9b8740a2acc1c76327ada03773efa635329d8b..0000000000000000000000000000000000000000 --- a/0001-virtiofsd-Drop-membership-of-all-supplementary-groups.patch +++ /dev/null @@ -1,101 +0,0 @@ -From 449e8171f96a6a944d1f3b7d3627ae059eae21ca Mon Sep 17 00:00:00 2001 -From: Vivek Goyal -Date: Tue, 25 Jan 2022 13:51:14 -0500 -Subject: [PATCH] virtiofsd: Drop membership of all supplementary groups - (CVE-2022-0358) - -At the start, drop membership of all supplementary groups. This is -not required. - -If we have membership of "root" supplementary group and when we switch -uid/gid using setresuid/setsgid, we still retain membership of existing -supplemntary groups. And that can allow some operations which are not -normally allowed. - -For example, if root in guest creates a dir as follows. - -$ mkdir -m 03777 test_dir - -This sets SGID on dir as well as allows unprivileged users to write into -this dir. - -And now as unprivileged user open file as follows. - -$ su test -$ fd = open("test_dir/priviledge_id", O_RDWR|O_CREAT|O_EXCL, 02755); - -This will create SGID set executable in test_dir/. - -And that's a problem because now an unpriviliged user can execute it, -get egid=0 and get access to resources owned by "root" group. This is -privilege escalation. - -Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=2044863 -Fixes: CVE-2022-0358 -Reported-by: JIETAO XIAO -Suggested-by: Miklos Szeredi -Reviewed-by: Stefan Hajnoczi -Reviewed-by: Dr. David Alan Gilbert -Signed-off-by: Vivek Goyal -Message-Id: -Signed-off-by: Dr. David Alan Gilbert - dgilbert: Fixed missing {}'s style nit ---- - tools/virtiofsd/passthrough_ll.c | 27 +++++++++++++++++++++++++++ - 1 file changed, 27 insertions(+) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 64b5b4fbb1..b3d0674f6d 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -54,6 +54,7 @@ - #include - #include - #include -+#include - - #include "qemu/cutils.h" - #include "passthrough_helpers.h" -@@ -1161,6 +1162,30 @@ static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) - #define OURSYS_setresuid SYS_setresuid - #endif - -+static void drop_supplementary_groups(void) -+{ -+ int ret; -+ -+ ret = getgroups(0, NULL); -+ if (ret == -1) { -+ fuse_log(FUSE_LOG_ERR, "getgroups() failed with error=%d:%s\n", -+ errno, strerror(errno)); -+ exit(1); -+ } -+ -+ if (!ret) { -+ return; -+ } -+ -+ /* Drop all supplementary groups. We should not need it */ -+ ret = setgroups(0, NULL); -+ if (ret == -1) { -+ fuse_log(FUSE_LOG_ERR, "setgroups() failed with error=%d:%s\n", -+ errno, strerror(errno)); -+ exit(1); -+ } -+} -+ - /* - * Change to uid/gid of caller so that file is created with - * ownership of caller. -@@ -3926,6 +3951,8 @@ int main(int argc, char *argv[]) - - qemu_init_exec_dir(argv[0]); - -+ drop_supplementary_groups(); -+ - pthread_mutex_init(&lo.mutex, NULL); - lo.inodes = g_hash_table_new(lo_key_hash, lo_key_equal); - lo.root.fd = -1; --- -GitLab - diff --git a/0002-vl-Improve-error-message-for-conflicting-incoming-an.patch b/0002-vl-Improve-error-message-for-conflicting-incoming-an.patch new file mode 100644 index 0000000000000000000000000000000000000000..37e0197f8caef39417cc90414be59a6fa4aeb282 --- /dev/null +++ b/0002-vl-Improve-error-message-for-conflicting-incoming-an.patch @@ -0,0 +1,46 @@ +From 64537ff11f0078bc3d2e63ea36f4a17adb124286 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 1 Dec 2023 15:25:19 +0100 +Subject: [PATCH 002/293] vl: Improve error message for conflicting -incoming + and -loadvm + +Currently, the conflict between -incoming and -loadvm is only detected +when loading the snapshot fails because the image is still inactive for +the incoming migration. This results in a suboptimal error message: + +$ ./qemu-system-x86_64 -hda /tmp/test.qcow2 -loadvm foo -incoming defer +qemu-system-x86_64: Device 'ide0-hd0' is writable but does not support snapshots + +Catch the situation already in qemu_validate_options() to improve the +message: + +$ ./qemu-system-x86_64 -hda /tmp/test.qcow2 -loadvm foo -incoming defer +qemu-system-x86_64: 'incoming' and 'loadvm' options are mutually exclusive + +Signed-off-by: Kevin Wolf +Message-ID: <20231201142520.32255-3-kwolf@redhat.com> +Signed-off-by: Kevin Wolf +(cherry picked from commit 5a7f21efaf99c60614fe1967be1c0f9aa46c526e) +Signed-off-by: Michael Tokarev +--- + system/vl.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/system/vl.c b/system/vl.c +index 2bcd9ef..6b87bfa 100644 +--- a/system/vl.c ++++ b/system/vl.c +@@ -2426,6 +2426,10 @@ static void qemu_validate_options(const QDict *machine_opts) + } + } + ++ if (loadvm && incoming) { ++ error_report("'incoming' and 'loadvm' options are mutually exclusive"); ++ exit(EXIT_FAILURE); ++ } + if (loadvm && preconfig_requested) { + error_report("'preconfig' and 'loadvm' options are " + "mutually exclusive"); +-- +1.8.3.1 + diff --git a/0003-iotests-Basic-tests-for-internal-snapshots.patch b/0003-iotests-Basic-tests-for-internal-snapshots.patch new file mode 100644 index 0000000000000000000000000000000000000000..bfcafa2580809bf7000ab7917e2e8430baa4919c --- /dev/null +++ b/0003-iotests-Basic-tests-for-internal-snapshots.patch @@ -0,0 +1,317 @@ +From 11b0730d6085cb78cf207e5be30ef0c3867a6282 Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 1 Dec 2023 15:25:20 +0100 +Subject: [PATCH 003/293] iotests: Basic tests for internal snapshots + +We have a few test cases that include tests for corner case aspects of +internal snapshots, but nothing that tests that they actually function +as snapshots or that involves deleting a snapshot. Add a test for this +kind of basic internal snapshot functionality. + +The error cases include a regression test for the crash we just fixed +with snapshot operations on inactive images. + +Signed-off-by: Kevin Wolf +Message-ID: <20231201142520.32255-4-kwolf@redhat.com> +Signed-off-by: Kevin Wolf +(cherry picked from commit bb6e2511eb48539b7dcbcb5f47772e156b9c45d1) +Signed-off-by: Michael Tokarev +--- + tests/qemu-iotests/tests/qcow2-internal-snapshots | 170 +++++++++++++++++++++ + .../tests/qcow2-internal-snapshots.out | 107 +++++++++++++ + 2 files changed, 277 insertions(+) + create mode 100755 tests/qemu-iotests/tests/qcow2-internal-snapshots + create mode 100644 tests/qemu-iotests/tests/qcow2-internal-snapshots.out + +diff --git a/tests/qemu-iotests/tests/qcow2-internal-snapshots b/tests/qemu-iotests/tests/qcow2-internal-snapshots +new file mode 100755 +index 0000000..36523ab +--- /dev/null ++++ b/tests/qemu-iotests/tests/qcow2-internal-snapshots +@@ -0,0 +1,170 @@ ++#!/usr/bin/env bash ++# group: rw quick ++# ++# Test case for internal snapshots in qcow2 ++# ++# Copyright (C) 2023 Red Hat, Inc. ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 2 of the License, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++# ++ ++# creator ++owner=kwolf@redhat.com ++ ++seq="$(basename $0)" ++echo "QA output created by $seq" ++ ++status=1 # failure is the default! ++ ++_cleanup() ++{ ++ _cleanup_test_img ++} ++trap "_cleanup; exit \$status" 0 1 2 3 15 ++ ++# get standard environment, filters and checks ++. ../common.rc ++. ../common.filter ++ ++# This tests qcow2-specific low-level functionality ++_supported_fmt qcow2 ++_supported_proto generic ++# Internal snapshots are (currently) impossible with refcount_bits=1, ++# and generally impossible with external data files ++_unsupported_imgopts 'compat=0.10' 'refcount_bits=1[^0-9]' data_file ++ ++IMG_SIZE=64M ++ ++_qemu() ++{ ++ $QEMU -no-shutdown -nographic -monitor stdio -serial none \ ++ -blockdev file,filename="$TEST_IMG",node-name=disk0-file \ ++ -blockdev "$IMGFMT",file=disk0-file,node-name=disk0 \ ++ -object iothread,id=iothread0 \ ++ -device virtio-scsi,iothread=iothread0 \ ++ -device scsi-hd,drive=disk0,share-rw=on \ ++ "$@" 2>&1 |\ ++ _filter_qemu | _filter_hmp | _filter_qemu_io ++} ++ ++_make_test_img $IMG_SIZE ++ ++echo ++echo "=== Write some data, take a snapshot and overwrite part of it ===" ++echo ++ ++{ ++ echo 'qemu-io disk0 "write -P0x11 0 1M"' ++ # Give qemu some time to boot before saving the VM state ++ sleep 0.5 ++ echo "savevm snap0" ++ echo 'qemu-io disk0 "write -P0x22 0 512k"' ++ echo "quit" ++} | _qemu ++ ++echo ++$QEMU_IMG snapshot -l "$TEST_IMG" | _filter_date | _filter_vmstate_size ++_check_test_img ++ ++echo ++echo "=== Verify that loading the snapshot reverts to the old content ===" ++echo ++ ++{ ++ # -loadvm reverted the write from the previous QEMU instance ++ echo 'qemu-io disk0 "read -P0x11 0 1M"' ++ ++ # Verify that it works without restarting QEMU, too ++ echo 'qemu-io disk0 "write -P0x33 512k 512k"' ++ echo "loadvm snap0" ++ echo 'qemu-io disk0 "read -P0x11 0 1M"' ++ ++ # Verify COW by writing a partial cluster ++ echo 'qemu-io disk0 "write -P0x33 63k 2k"' ++ echo 'qemu-io disk0 "read -P0x11 0 63k"' ++ echo 'qemu-io disk0 "read -P0x33 63k 2k"' ++ echo 'qemu-io disk0 "read -P0x11 65k 63k"' ++ ++ # Take a second snapshot ++ echo "savevm snap1" ++ ++ echo "quit" ++} | _qemu -loadvm snap0 ++ ++echo ++$QEMU_IMG snapshot -l "$TEST_IMG" | _filter_date | _filter_vmstate_size ++_check_test_img ++ ++echo ++echo "=== qemu-img snapshot can revert to snapshots ===" ++echo ++ ++$QEMU_IMG snapshot -a snap0 "$TEST_IMG" ++$QEMU_IO -c "read -P0x11 0 1M" "$TEST_IMG" | _filter_qemu_io ++$QEMU_IMG snapshot -a snap1 "$TEST_IMG" ++$QEMU_IO \ ++ -c "read -P0x11 0 63k" \ ++ -c "read -P0x33 63k 2k" \ ++ -c "read -P0x11 65k 63k" \ ++ "$TEST_IMG" | _filter_qemu_io ++ ++echo ++echo "=== Deleting snapshots ===" ++echo ++{ ++ # The active layer stays unaffected by deleting the snapshot ++ echo "delvm snap1" ++ echo 'qemu-io disk0 "read -P0x11 0 63k"' ++ echo 'qemu-io disk0 "read -P0x33 63k 2k"' ++ echo 'qemu-io disk0 "read -P0x11 65k 63k"' ++ ++ echo "quit" ++} | _qemu ++ ++ ++echo ++$QEMU_IMG snapshot -l "$TEST_IMG" | _filter_date | _filter_vmstate_size ++_check_test_img ++ ++echo ++echo "=== Error cases ===" ++echo ++ ++# snap1 should not exist any more ++_qemu -loadvm snap1 ++ ++echo ++{ ++ echo "loadvm snap1" ++ echo "quit" ++} | _qemu ++ ++# Snapshot operations and inactive images are incompatible ++echo ++_qemu -loadvm snap0 -incoming defer ++{ ++ echo "loadvm snap0" ++ echo "delvm snap0" ++ echo "savevm snap1" ++ echo "quit" ++} | _qemu -incoming defer ++ ++# -loadvm and -preconfig are incompatible ++echo ++_qemu -loadvm snap0 -preconfig ++ ++# success, all done ++echo "*** done" ++rm -f $seq.full ++status=0 +diff --git a/tests/qemu-iotests/tests/qcow2-internal-snapshots.out b/tests/qemu-iotests/tests/qcow2-internal-snapshots.out +new file mode 100644 +index 0000000..438f535 +--- /dev/null ++++ b/tests/qemu-iotests/tests/qcow2-internal-snapshots.out +@@ -0,0 +1,107 @@ ++QA output created by qcow2-internal-snapshots ++Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 ++ ++=== Write some data, take a snapshot and overwrite part of it === ++ ++QEMU X.Y.Z monitor - type 'help' for more information ++(qemu) qemu-io disk0 "write -P0x11 0 1M" ++wrote 1048576/1048576 bytes at offset 0 ++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++(qemu) savevm snap0 ++(qemu) qemu-io disk0 "write -P0x22 0 512k" ++wrote 524288/524288 bytes at offset 0 ++512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++(qemu) quit ++ ++Snapshot list: ++ID TAG VM SIZE DATE VM CLOCK ICOUNT ++1 snap0 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000 ++No errors were found on the image. ++ ++=== Verify that loading the snapshot reverts to the old content === ++ ++QEMU X.Y.Z monitor - type 'help' for more information ++(qemu) qemu-io disk0 "read -P0x11 0 1M" ++read 1048576/1048576 bytes at offset 0 ++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++(qemu) qemu-io disk0 "write -P0x33 512k 512k" ++wrote 524288/524288 bytes at offset 524288 ++512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++(qemu) loadvm snap0 ++(qemu) qemu-io disk0 "read -P0x11 0 1M" ++read 1048576/1048576 bytes at offset 0 ++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++(qemu) qemu-io disk0 "write -P0x33 63k 2k" ++wrote 2048/2048 bytes at offset 64512 ++2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++(qemu) qemu-io disk0 "read -P0x11 0 63k" ++read 64512/64512 bytes at offset 0 ++63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++(qemu) qemu-io disk0 "read -P0x33 63k 2k" ++read 2048/2048 bytes at offset 64512 ++2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++(qemu) qemu-io disk0 "read -P0x11 65k 63k" ++read 64512/64512 bytes at offset 66560 ++63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++(qemu) savevm snap1 ++(qemu) quit ++ ++Snapshot list: ++ID TAG VM SIZE DATE VM CLOCK ICOUNT ++1 snap0 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000 ++2 snap1 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000 ++No errors were found on the image. ++ ++=== qemu-img snapshot can revert to snapshots === ++ ++read 1048576/1048576 bytes at offset 0 ++1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++read 64512/64512 bytes at offset 0 ++63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++read 2048/2048 bytes at offset 64512 ++2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++read 64512/64512 bytes at offset 66560 ++63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++ ++=== Deleting snapshots === ++ ++QEMU X.Y.Z monitor - type 'help' for more information ++(qemu) delvm snap1 ++(qemu) qemu-io disk0 "read -P0x11 0 63k" ++read 64512/64512 bytes at offset 0 ++63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++(qemu) qemu-io disk0 "read -P0x33 63k 2k" ++read 2048/2048 bytes at offset 64512 ++2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++(qemu) qemu-io disk0 "read -P0x11 65k 63k" ++read 64512/64512 bytes at offset 66560 ++63 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) ++(qemu) quit ++ ++Snapshot list: ++ID TAG VM SIZE DATE VM CLOCK ICOUNT ++1 snap0 SIZE yyyy-mm-dd hh:mm:ss 00:00:00.000 ++No errors were found on the image. ++ ++=== Error cases === ++ ++QEMU X.Y.Z monitor - type 'help' for more information ++(qemu) QEMU_PROG: Snapshot 'snap1' does not exist in one or more devices ++ ++QEMU X.Y.Z monitor - type 'help' for more information ++(qemu) loadvm snap1 ++Error: Snapshot 'snap1' does not exist in one or more devices ++(qemu) quit ++ ++QEMU_PROG: 'incoming' and 'loadvm' options are mutually exclusive ++QEMU X.Y.Z monitor - type 'help' for more information ++(qemu) loadvm snap0 ++Error: Device 'disk0' is writable but does not support snapshots ++(qemu) delvm snap0 ++Error: Device 'disk0' is writable but does not support snapshots ++(qemu) savevm snap1 ++Error: Device 'disk0' is writable but does not support snapshots ++(qemu) quit ++ ++QEMU_PROG: 'preconfig' and 'loadvm' options are mutually exclusive ++*** done +-- +1.8.3.1 + diff --git a/0004-target-riscv-kvm-do-not-use-non-portable-strerrornam.patch b/0004-target-riscv-kvm-do-not-use-non-portable-strerrornam.patch new file mode 100644 index 0000000000000000000000000000000000000000..8ea13a5a1485865d2e0b0dc898b4e56172f24f87 --- /dev/null +++ b/0004-target-riscv-kvm-do-not-use-non-portable-strerrornam.patch @@ -0,0 +1,75 @@ +From 7d6a2ce8cff462e01f59a3cd60294a26bcfe5d1e Mon Sep 17 00:00:00 2001 +From: Natanael Copa +Date: Mon, 18 Dec 2023 17:22:44 +0100 +Subject: [PATCH 004/293] target/riscv/kvm: do not use non-portable + strerrorname_np() + +strerrorname_np is non-portable and breaks building with musl libc. + +Use strerror(errno) instead, like we do other places. + +Cc: qemu-stable@nongnu.org +Fixes: commit 082e9e4a58ba (target/riscv/kvm: improve 'init_multiext_cfg' error msg) +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2041 +Buglink: https://gitlab.alpinelinux.org/alpine/aports/-/issues/15541 +Signed-off-by: Natanael Copa +Reviewed-by: Daniel Henrique Barboza +Signed-off-by: Michael Tokarev +(cherry picked from commit d424db235434b8356c6b2d9420b846c7ddcc83ea) +--- + target/riscv/kvm/kvm-cpu.c | 18 ++++++++---------- + 1 file changed, 8 insertions(+), 10 deletions(-) + +diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c +index 45b6cf1..117e33c 100644 +--- a/target/riscv/kvm/kvm-cpu.c ++++ b/target/riscv/kvm/kvm-cpu.c +@@ -832,9 +832,8 @@ static void kvm_riscv_read_multiext_legacy(RISCVCPU *cpu, + multi_ext_cfg->supported = false; + val = false; + } else { +- error_report("Unable to read ISA_EXT KVM register %s, " +- "error code: %s", multi_ext_cfg->name, +- strerrorname_np(errno)); ++ error_report("Unable to read ISA_EXT KVM register %s: %s", ++ multi_ext_cfg->name, strerror(errno)); + exit(EXIT_FAILURE); + } + } else { +@@ -895,8 +894,8 @@ static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) + * + * Error out if we get any other errno. + */ +- error_report("Error when accessing get-reg-list, code: %s", +- strerrorname_np(errno)); ++ error_report("Error when accessing get-reg-list: %s", ++ strerror(errno)); + exit(EXIT_FAILURE); + } + +@@ -905,8 +904,8 @@ static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) + reglist->n = rl_struct.n; + ret = ioctl(kvmcpu->cpufd, KVM_GET_REG_LIST, reglist); + if (ret) { +- error_report("Error when reading KVM_GET_REG_LIST, code %s ", +- strerrorname_np(errno)); ++ error_report("Error when reading KVM_GET_REG_LIST: %s", ++ strerror(errno)); + exit(EXIT_FAILURE); + } + +@@ -927,9 +926,8 @@ static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) + reg.addr = (uint64_t)&val; + ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { +- error_report("Unable to read ISA_EXT KVM register %s, " +- "error code: %s", multi_ext_cfg->name, +- strerrorname_np(errno)); ++ error_report("Unable to read ISA_EXT KVM register %s: %s", ++ multi_ext_cfg->name, strerror(errno)); + exit(EXIT_FAILURE); + } + +-- +1.8.3.1 + diff --git a/0005-include-ui-rect.h-fix-qemu_rect_init-mis-assignment.patch b/0005-include-ui-rect.h-fix-qemu_rect_init-mis-assignment.patch new file mode 100644 index 0000000000000000000000000000000000000000..cb4a071dea83647c1de517d3e7e7445ec41e20e6 --- /dev/null +++ b/0005-include-ui-rect.h-fix-qemu_rect_init-mis-assignment.patch @@ -0,0 +1,38 @@ +From a331dc62adf6da31565c13c115402f6f21589346 Mon Sep 17 00:00:00 2001 +From: Elen Avan +Date: Fri, 22 Dec 2023 22:17:21 +0300 +Subject: [PATCH 005/293] include/ui/rect.h: fix qemu_rect_init() + mis-assignment +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Elen Avan +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2051 +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2050 +Fixes: a200d53b1fde "virtio-gpu: replace PIXMAN for region/rect test" +Cc: qemu-stable@nongnu.org +Reviewed-by: Michael Tokarev +Reviewed-by: Marc-André Lureau +Signed-off-by: Michael Tokarev +(cherry picked from commit 9d5b42beb6978dc6219d5dc029c9d453c6b8d503) +--- + include/ui/rect.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/ui/rect.h b/include/ui/rect.h +index 94898f9..68f05d7 100644 +--- a/include/ui/rect.h ++++ b/include/ui/rect.h +@@ -19,7 +19,7 @@ static inline void qemu_rect_init(QemuRect *rect, + uint16_t width, uint16_t height) + { + rect->x = x; +- rect->y = x; ++ rect->y = y; + rect->width = width; + rect->height = height; + } +-- +1.8.3.1 + diff --git a/0006-configure-use-a-native-non-cross-compiler-for-linux-.patch b/0006-configure-use-a-native-non-cross-compiler-for-linux-.patch new file mode 100644 index 0000000000000000000000000000000000000000..55baf0def76905111cfcf48982c93e552168cdc5 --- /dev/null +++ b/0006-configure-use-a-native-non-cross-compiler-for-linux-.patch @@ -0,0 +1,40 @@ +From bb28ee11c2f64a1423e78ad5dfcdbcfa79ceb7d6 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Fri, 22 Dec 2023 10:55:43 +0100 +Subject: [PATCH 006/293] configure: use a native non-cross compiler for + linux-user + +Commit c2118e9e1ab ("configure: don't try a "native" cross for linux-user", +2023-11-23) sought to avoid issues with using the native compiler with a +cross-endian or cross-bitness setup. However, in doing so it ended up +requiring a cross compiler setup (and most likely a slow compiler setup) +even when building TCG tests that are native to the host architecture. +Always allow the host compiler in that case. + +Cc: qemu-stable@nongnu.org +Fixes: c2118e9e1ab ("configure: don't try a "native" cross for linux-user", 2023-11-23) +Signed-off-by: Paolo Bonzini +(cherry picked from commit 007531586aa8ef6dccdadd927b89a50af62288d1) +Signed-off-by: Michael Tokarev +--- + configure | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/configure b/configure +index bdda912..d7e0926 100755 +--- a/configure ++++ b/configure +@@ -1387,8 +1387,8 @@ probe_target_compiler() { + done + + try=cross +- # For softmmu/roms we might be able to use the host compiler +- if [ "${1%softmmu}" != "$1" ]; then ++ # For softmmu/roms also look for a bi-endian or multilib-enabled host compiler ++ if [ "${1%softmmu}" != "$1" ] || test "$target_arch" = "$cpu"; then + case "$target_arch:$cpu" in + aarch64_be:aarch64 | \ + armeb:arm | \ +-- +1.8.3.1 + diff --git a/0001-sgx-stub-fix.patch b/0007-target-i386-the-sgx_epc_get_section-stub-is-reachabl.patch similarity index 58% rename from 0001-sgx-stub-fix.patch rename to 0007-target-i386-the-sgx_epc_get_section-stub-is-reachabl.patch index ff31973ab889f66c9260ba4b70b4c5cfa041af0a..e85f21b14f63ccb45c520058f761471e7e7c5808 100644 --- a/0001-sgx-stub-fix.patch +++ b/0007-target-i386-the-sgx_epc_get_section-stub-is-reachabl.patch @@ -1,7 +1,8 @@ -From 509b6078631ad2437e1a452f749831e401fb8afb Mon Sep 17 00:00:00 2001 +From e649de10d509026343eb51159bd3791b5f9b11df Mon Sep 17 00:00:00 2001 From: Paolo Bonzini -Date: Tue, 1 Feb 2022 20:09:37 +0100 -Subject: [PATCH] target/i386: the sgx_epc_get_section stub is reachable +Date: Tue, 1 Feb 2022 20:09:41 +0100 +Subject: [PATCH 007/293] target/i386: the sgx_epc_get_section stub is + reachable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -13,9 +14,16 @@ the "real" sgx_epc_get_section does when SGX is disabled. Reported-by: Vladimír Beneš Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini +Message-ID: <20220201190941.106001-1-pbonzini@redhat.com> +Signed-off-by: Paolo Bonzini +(cherry picked from commit 219615740425d9683588207b40a365e6741691a6) +Signed-off-by: Michael Tokarev +--- + hw/i386/sgx-stub.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/i386/sgx-stub.c b/hw/i386/sgx-stub.c -index 26833eb233..16b1dfd90b 100644 +index 26833eb..16b1dfd 100644 --- a/hw/i386/sgx-stub.c +++ b/hw/i386/sgx-stub.c @@ -34,5 +34,5 @@ void pc_machine_init_sgx_epc(PCMachineState *pcms) @@ -25,3 +33,6 @@ index 26833eb233..16b1dfd90b 100644 - g_assert_not_reached(); + return true; } +-- +1.8.3.1 + diff --git a/0008-hw-net-can-sja1000-fix-bug-for-single-acceptance-fil.patch b/0008-hw-net-can-sja1000-fix-bug-for-single-acceptance-fil.patch new file mode 100644 index 0000000000000000000000000000000000000000..cdacf13a75bbacbf8da7c548fc5db27f77fe22d5 --- /dev/null +++ b/0008-hw-net-can-sja1000-fix-bug-for-single-acceptance-fil.patch @@ -0,0 +1,42 @@ +From 7b57e6d4340926b0a24853fb7d805bdaf8503eb2 Mon Sep 17 00:00:00 2001 +From: Pavel Pisa +Date: Thu, 4 Jan 2024 00:14:26 +0100 +Subject: [PATCH 008/293] hw/net/can/sja1000: fix bug for single acceptance + filter and standard frame + +A CAN sja1000 standard frame filter mask has been computed and applied +incorrectly for standard frames when single Acceptance Filter Mode +(MOD_AFM = 1) has been selected. The problem has not been found +by Linux kernel testing because it uses dual filter mode (MOD_AFM = 0) +and leaves falters fully open. + +The problem has been noticed by Grant Ramsay when testing with Zephyr +RTOS which uses single filter mode. + +Signed-off-by: Pavel Pisa +Reported-by: Grant Ramsay +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2028 +Fixes: 733210e754 ("hw/net/can: SJA1000 chip register level emulation") +Message-ID: <20240103231426.5685-1-pisa@fel.cvut.cz> +(cherry picked from commit 25145a7d7735344a469551946fc2a7f19eb4aa3d) +Signed-off-by: Michael Tokarev +--- + hw/net/can/can_sja1000.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/net/can/can_sja1000.c b/hw/net/can/can_sja1000.c +index 73201f9..575df7d 100644 +--- a/hw/net/can/can_sja1000.c ++++ b/hw/net/can/can_sja1000.c +@@ -108,7 +108,7 @@ void can_sja_single_filter(struct qemu_can_filter *filter, + } + + filter->can_mask = (uint32_t)amr[0] << 3; +- filter->can_mask |= (uint32_t)amr[1] << 5; ++ filter->can_mask |= (uint32_t)amr[1] >> 5; + filter->can_mask = ~filter->can_mask & QEMU_CAN_SFF_MASK; + if (!(amr[1] & 0x10)) { + filter->can_mask |= QEMU_CAN_RTR_FLAG; +-- +1.8.3.1 + diff --git a/0009-target-riscv-Fix-mcycle-minstret-increment-behavior.patch b/0009-target-riscv-Fix-mcycle-minstret-increment-behavior.patch new file mode 100644 index 0000000000000000000000000000000000000000..781ec503ea025d3c3bbcec4b8a3d9cb54a8cda81 --- /dev/null +++ b/0009-target-riscv-Fix-mcycle-minstret-increment-behavior.patch @@ -0,0 +1,58 @@ +From 882950efd25c03fe62fd511f184d7ed2da6f33fe Mon Sep 17 00:00:00 2001 +From: Xu Lu +Date: Tue, 26 Dec 2023 12:05:00 +0800 +Subject: [PATCH 009/293] target/riscv: Fix mcycle/minstret increment behavior + +The mcycle/minstret counter's stop flag is mistakenly updated on a copy +on stack. Thus the counter increments even when the CY/IR bit in the +mcountinhibit register is set. This commit corrects its behavior. + +Fixes: 3780e33732f88 (target/riscv: Support mcycle/minstret write operation) +Signed-off-by: Xu Lu +Reviewed-by: Daniel Henrique Barboza +Signed-off-by: Michael Tokarev +(cherry picked from commit 5cb0e7abe1635cb82e0033260dac2b910d142f8c) +Signed-off-by: Michael Tokarev +--- + target/riscv/csr.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +diff --git a/target/riscv/csr.c b/target/riscv/csr.c +index fde7ce1..c50a333 100644 +--- a/target/riscv/csr.c ++++ b/target/riscv/csr.c +@@ -907,11 +907,11 @@ static int write_mhpmcounterh(CPURISCVState *env, int csrno, target_ulong val) + static RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val, + bool upper_half, uint32_t ctr_idx) + { +- PMUCTRState counter = env->pmu_ctrs[ctr_idx]; +- target_ulong ctr_prev = upper_half ? counter.mhpmcounterh_prev : +- counter.mhpmcounter_prev; +- target_ulong ctr_val = upper_half ? counter.mhpmcounterh_val : +- counter.mhpmcounter_val; ++ PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; ++ target_ulong ctr_prev = upper_half ? counter->mhpmcounterh_prev : ++ counter->mhpmcounter_prev; ++ target_ulong ctr_val = upper_half ? counter->mhpmcounterh_val : ++ counter->mhpmcounter_val; + + if (get_field(env->mcountinhibit, BIT(ctr_idx))) { + /* +@@ -919,12 +919,12 @@ static RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val, + * stop the icount counting. Just return the counter value written by + * the supervisor to indicate that counter was not incremented. + */ +- if (!counter.started) { ++ if (!counter->started) { + *val = ctr_val; + return RISCV_EXCP_NONE; + } else { + /* Mark that the counter has been stopped */ +- counter.started = false; ++ counter->started = false; + } + } + +-- +1.8.3.1 + diff --git a/0010-chardev-char.c-fix-abstract-device-type-error-messag.patch b/0010-chardev-char.c-fix-abstract-device-type-error-messag.patch new file mode 100644 index 0000000000000000000000000000000000000000..214d9f0be5b1e8c0e8930f5d71aafd13b399a840 --- /dev/null +++ b/0010-chardev-char.c-fix-abstract-device-type-error-messag.patch @@ -0,0 +1,38 @@ +From 0965e5eda5d323a6b390c8bada365f2258bc7ca7 Mon Sep 17 00:00:00 2001 +From: Michael Tokarev +Date: Wed, 3 Jan 2024 14:37:39 +0300 +Subject: [PATCH 010/293] chardev/char.c: fix "abstract device type" error + message + +Current error message: + + qemu-system-x86_64: -chardev spice,id=foo: Parameter 'driver' expects an abstract device type + +while in fact the meaning is in reverse, -chardev expects +a non-abstract device type. + +Fixes: 777357d758d9 ("chardev: qom-ify" 2016-12-07) +Signed-off-by: Michael Tokarev +Reviewed-by: Zhao Liu +(cherry picked from commit 4ad87cd4b2254197b7ac12e3da824854e6a90f8f) +Signed-off-by: Michael Tokarev +--- + chardev/char.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/chardev/char.c b/chardev/char.c +index 996a024..119b548 100644 +--- a/chardev/char.c ++++ b/chardev/char.c +@@ -518,7 +518,7 @@ static const ChardevClass *char_get_class(const char *driver, Error **errp) + + if (object_class_is_abstract(oc)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver", +- "an abstract device type"); ++ "a non-abstract device type"); + return NULL; + } + +-- +1.8.3.1 + diff --git a/0011-audio-audio.c-remove-trailing-newline-in-error_setg.patch b/0011-audio-audio.c-remove-trailing-newline-in-error_setg.patch new file mode 100644 index 0000000000000000000000000000000000000000..429c5782a2b05b1cd3a59c9393da977147c34a69 --- /dev/null +++ b/0011-audio-audio.c-remove-trailing-newline-in-error_setg.patch @@ -0,0 +1,35 @@ +From 5713d6dd76d2bb10f8e2367512217ef49fe797ed Mon Sep 17 00:00:00 2001 +From: Michael Tokarev +Date: Wed, 3 Jan 2024 14:18:00 +0300 +Subject: [PATCH 011/293] audio/audio.c: remove trailing newline in error_setg +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +error_setg() appends newline to the formatted message. +Fixes: cb94ff5f80c5 ("audio: propagate Error * out of audio_init") + +Signed-off-by: Michael Tokarev +Reviewed-by: Philippe Mathieu-Daudé +(cherry picked from commit 09a36158c283f7448d1b00fdbb6634f05d27f922) +Signed-off-by: Michael Tokarev +--- + audio/audio.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/audio/audio.c b/audio/audio.c +index 8d1e4ad..7ac74f9 100644 +--- a/audio/audio.c ++++ b/audio/audio.c +@@ -1744,7 +1744,7 @@ static AudioState *audio_init(Audiodev *dev, Error **errp) + if (driver) { + done = !audio_driver_init(s, driver, dev, errp); + } else { +- error_setg(errp, "Unknown audio driver `%s'\n", drvname); ++ error_setg(errp, "Unknown audio driver `%s'", drvname); + } + if (!done) { + goto out; +-- +1.8.3.1 + diff --git a/0012-hw-net-cadence_gem-Fix-MDIO_OP_xxx-values.patch b/0012-hw-net-cadence_gem-Fix-MDIO_OP_xxx-values.patch new file mode 100644 index 0000000000000000000000000000000000000000..da689e13751e80068e396c4e16b9adeb08af3017 --- /dev/null +++ b/0012-hw-net-cadence_gem-Fix-MDIO_OP_xxx-values.patch @@ -0,0 +1,47 @@ +From 6e3cfd598cb9665689a485f1a25dff7c55023120 Mon Sep 17 00:00:00 2001 +From: Bin Meng +Date: Tue, 2 Jan 2024 22:18:03 +0800 +Subject: [PATCH 012/293] hw/net: cadence_gem: Fix MDIO_OP_xxx values +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Testing upstream U-Boot with 'sifive_u' machine we see: + + => dhcp + ethernet@10090000: PHY present at 0 + Could not get PHY for ethernet@10090000: addr 0 + phy_connect failed + +This has been working till QEMU 8.1 but broken since QEMU 8.2. + +Fixes: 1b09eeb122aa ("hw/net/cadence_gem: use FIELD to describe PHYMNTNC register fields") +Reported-by: Heinrich Schuchardt +Signed-off-by: Bin Meng +Reviewed-by: Philippe Mathieu-Daudé +Tested-by: Heinrich Schuchardt +Signed-off-by: Michael Tokarev +(cherry picked from commit 0c7ffc977195c1f71c8132eb5616827e589d4a0f) +Signed-off-by: Michael Tokarev +--- + hw/net/cadence_gem.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c +index 296bba2..472ce9c 100644 +--- a/hw/net/cadence_gem.c ++++ b/hw/net/cadence_gem.c +@@ -199,8 +199,8 @@ REG32(PHYMNTNC, 0x34) /* Phy Maintenance reg */ + FIELD(PHYMNTNC, PHY_ADDR, 23, 5) + FIELD(PHYMNTNC, OP, 28, 2) + FIELD(PHYMNTNC, ST, 30, 2) +-#define MDIO_OP_READ 0x3 +-#define MDIO_OP_WRITE 0x2 ++#define MDIO_OP_READ 0x2 ++#define MDIO_OP_WRITE 0x1 + + REG32(RXPAUSE, 0x38) /* RX Pause Time reg */ + REG32(TXPAUSE, 0x3c) /* TX Pause Time reg */ +-- +1.8.3.1 + diff --git a/0013-edu-fix-DMA-range-upper-bound-check.patch b/0013-edu-fix-DMA-range-upper-bound-check.patch new file mode 100644 index 0000000000000000000000000000000000000000..c15d9ce6893707e3cd65ea570f9f40e47dec30db --- /dev/null +++ b/0013-edu-fix-DMA-range-upper-bound-check.patch @@ -0,0 +1,46 @@ +From 0bf355e6ca947b119b5d6b91a349ec607a137d94 Mon Sep 17 00:00:00 2001 +From: Max Erenberg +Date: Mon, 25 Dec 2023 18:44:32 -0500 +Subject: [PATCH 013/293] edu: fix DMA range upper bound check + +The edu_check_range function checks that start <= end1 < end2, where +end1 is the upper bound (exclusive) of the guest-supplied DMA range and +end2 is the upper bound (exclusive) of the device's allowed DMA range. +When the guest tries to transfer exactly DMA_SIZE (4096) bytes, end1 +will be equal to end2, so the check fails and QEMU aborts with this +puzzling error message (newlines added for formatting): + + qemu: hardware error: EDU: DMA range + 0x0000000000040000-0x0000000000040fff out of bounds + (0x0000000000040000-0x0000000000040fff)! + +By checking end1 <= end2 instead, guests will be allowed to transfer +exactly 4096 bytes. It is not necessary to explicitly check for +start <= end1 because the previous two checks (within(addr, start, end2) +and end1 > addr) imply start < end1. + +Fixes: b30934cb52a7 ("hw: misc, add educational driver", 2015-01-21) +Signed-off-by: Max Erenberg +Signed-off-by: Michael Tokarev +(cherry picked from commit 2c5107e1b455d4a157124f021826ead4e04b4aea) +Signed-off-by: Michael Tokarev +--- + hw/misc/edu.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/misc/edu.c b/hw/misc/edu.c +index a1f8bc7..e64a246 100644 +--- a/hw/misc/edu.c ++++ b/hw/misc/edu.c +@@ -115,7 +115,7 @@ static void edu_check_range(uint64_t addr, uint64_t size1, uint64_t start, + uint64_t end2 = start + size2; + + if (within(addr, start, end2) && +- end1 > addr && within(end1, start, end2)) { ++ end1 > addr && end1 <= end2) { + return; + } + +-- +1.8.3.1 + diff --git a/0014-vfio-container-Replace-basename-with-g_path_get_base.patch b/0014-vfio-container-Replace-basename-with-g_path_get_base.patch new file mode 100644 index 0000000000000000000000000000000000000000..1a660f6cf069488e28de95c352c102fb805c23e2 --- /dev/null +++ b/0014-vfio-container-Replace-basename-with-g_path_get_base.patch @@ -0,0 +1,52 @@ +From 5f64bed67ca23976c0d00209559a35e61a6e1f96 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= +Date: Wed, 20 Dec 2023 14:53:02 +0100 +Subject: [PATCH 014/293] vfio/container: Replace basename with + g_path_get_basename +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +g_path_get_basename() is a portable utility function that has the +advantage of not modifing the string argument. It also fixes a compile +breakage with the Musl C library reported in [1]. + +[1] https://lore.kernel.org/all/20231212010228.2701544-1-raj.khem@gmail.com/ + +Reported-by: Khem Raj +Reviewed-by: Eric Auger +Reviewed-by: Zhao Liu +Reviewed-by: Zhenzhong Duan +Signed-off-by: Cédric Le Goater +(cherry picked from commit 213ae3ffda463c0503e39e0cf827511b5298c314) +Signed-off-by: Michael Tokarev +--- + hw/vfio/container.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/hw/vfio/container.c b/hw/vfio/container.c +index 2420100..adc3005 100644 +--- a/hw/vfio/container.c ++++ b/hw/vfio/container.c +@@ -848,7 +848,8 @@ static void vfio_put_base_device(VFIODevice *vbasedev) + + static int vfio_device_groupid(VFIODevice *vbasedev, Error **errp) + { +- char *tmp, group_path[PATH_MAX], *group_name; ++ char *tmp, group_path[PATH_MAX]; ++ g_autofree char *group_name = NULL; + int ret, groupid; + ssize_t len; + +@@ -864,7 +865,7 @@ static int vfio_device_groupid(VFIODevice *vbasedev, Error **errp) + + group_path[len] = 0; + +- group_name = basename(group_path); ++ group_name = g_path_get_basename(group_path); + if (sscanf(group_name, "%d", &groupid) != 1) { + error_setg_errno(errp, errno, "failed to read %s", group_path); + return -errno; +-- +1.8.3.1 + diff --git a/0015-hw-vfio-fix-iteration-over-global-VFIODevice-list.patch b/0015-hw-vfio-fix-iteration-over-global-VFIODevice-list.patch new file mode 100644 index 0000000000000000000000000000000000000000..9a13fa18163f13e45e4a07000e766aed7813747f --- /dev/null +++ b/0015-hw-vfio-fix-iteration-over-global-VFIODevice-list.patch @@ -0,0 +1,66 @@ +From ae594658663b8408299d0546a8a450efb8ad5494 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Volker=20R=C3=BCmelin?= +Date: Fri, 29 Dec 2023 21:38:54 +0100 +Subject: [PATCH 015/293] hw/vfio: fix iteration over global VFIODevice list +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Commit 3d779abafe ("vfio/common: Introduce a global VFIODevice list") +introduced a global VFIODevice list, but forgot to update the list +element field name when iterating over the new list. Change the code +to use the correct list element field. + +Fixes: 3d779abafe ("vfio/common: Introduce a global VFIODevice list") +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2061 +Signed-off-by: Volker Rümelin +Reviewed-by: Zhenzhong Duan +Reviewed-by: Cédric Le Goater +Reviewed-by: Eric Auger +(cherry picked from commit 9353b6da430f90e47f352dbf6dc31120c8914da6) +Signed-off-by: Michael Tokarev +--- + hw/vfio/common.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/hw/vfio/common.c b/hw/vfio/common.c +index e70fdf5..a5dfc2d 100644 +--- a/hw/vfio/common.c ++++ b/hw/vfio/common.c +@@ -73,7 +73,7 @@ bool vfio_mig_active(void) + return false; + } + +- QLIST_FOREACH(vbasedev, &vfio_device_list, next) { ++ QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { + if (vbasedev->migration_blocker) { + return false; + } +@@ -94,7 +94,7 @@ static bool vfio_multiple_devices_migration_is_supported(void) + unsigned int device_num = 0; + bool all_support_p2p = true; + +- QLIST_FOREACH(vbasedev, &vfio_device_list, next) { ++ QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { + if (vbasedev->migration) { + device_num++; + +@@ -1352,13 +1352,13 @@ void vfio_reset_handler(void *opaque) + { + VFIODevice *vbasedev; + +- QLIST_FOREACH(vbasedev, &vfio_device_list, next) { ++ QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { + if (vbasedev->dev->realized) { + vbasedev->ops->vfio_compute_needs_reset(vbasedev); + } + } + +- QLIST_FOREACH(vbasedev, &vfio_device_list, next) { ++ QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { + if (vbasedev->dev->realized && vbasedev->needs_reset) { + vbasedev->ops->vfio_hot_reset_multi(vbasedev); + } +-- +1.8.3.1 + diff --git a/0016-hw-intc-arm_gicv3_cpuif-handle-LPIs-in-in-the-list-r.patch b/0016-hw-intc-arm_gicv3_cpuif-handle-LPIs-in-in-the-list-r.patch new file mode 100644 index 0000000000000000000000000000000000000000..52da7d42bfdb39d228b1ca39c957eeb59a4acc1a --- /dev/null +++ b/0016-hw-intc-arm_gicv3_cpuif-handle-LPIs-in-in-the-list-r.patch @@ -0,0 +1,70 @@ +From a68fc9dbde1708f49021d44181000e543304d180 Mon Sep 17 00:00:00 2001 +From: Peter Maydell +Date: Tue, 9 Jan 2024 14:43:44 +0000 +Subject: [PATCH 016/293] hw/intc/arm_gicv3_cpuif: handle LPIs in in the list + registers + +The hypervisor can deliver (virtual) LPIs to a guest by setting up a +list register to have an intid which is an LPI. The GIC has to treat +these a little differently to standard interrupt IDs, because LPIs +have no Active state, and so the guest will only EOI them, it will +not also deactivate them. So icv_eoir_write() must do two things: + + * if the LPI ID is not in any list register, we drop the + priority but do not increment the EOI count + * if the LPI ID is in a list register, we immediately deactivate + it, regardless of the split-drop-and-deactivate control + +This can be seen in the VirtualWriteEOIR0() and VirtualWriteEOIR1() +pseudocode in the GICv3 architecture specification. + +Without this fix, potentially a hypervisor guest might stall because +LPIs get stuck in a bogus Active+Pending state. + +Cc: qemu-stable@nongnu.org +Signed-off-by: Peter Maydell +Reviewed-by: Richard Henderson +Tested-by: Miguel Luis +(cherry picked from commit 82a65e3188abebb509510b391726711606aca642) +Signed-off-by: Michael Tokarev +--- + hw/intc/arm_gicv3_cpuif.c | 17 +++++++++++++---- + 1 file changed, 13 insertions(+), 4 deletions(-) + +diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c +index ab1a005..258dee1 100644 +--- a/hw/intc/arm_gicv3_cpuif.c ++++ b/hw/intc/arm_gicv3_cpuif.c +@@ -1434,16 +1434,25 @@ static void icv_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri, + idx = icv_find_active(cs, irq); + + if (idx < 0) { +- /* No valid list register corresponding to EOI ID */ +- icv_increment_eoicount(cs); ++ /* ++ * No valid list register corresponding to EOI ID; if this is a vLPI ++ * not in the list regs then do nothing; otherwise increment EOI count ++ */ ++ if (irq < GICV3_LPI_INTID_START) { ++ icv_increment_eoicount(cs); ++ } + } else { + uint64_t lr = cs->ich_lr_el2[idx]; + int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0; + int lr_gprio = ich_lr_prio(lr) & icv_gprio_mask(cs, grp); + + if (thisgrp == grp && lr_gprio == dropprio) { +- if (!icv_eoi_split(env, cs)) { +- /* Priority drop and deactivate not split: deactivate irq now */ ++ if (!icv_eoi_split(env, cs) || irq >= GICV3_LPI_INTID_START) { ++ /* ++ * Priority drop and deactivate not split: deactivate irq now. ++ * LPIs always get their active state cleared immediately ++ * because no separate deactivate is expected. ++ */ + icv_deactivate_irq(cs, idx); + } + } +-- +1.8.3.1 + diff --git a/0017-tcg-ppc-Use-new-registers-for-LQ-destination.patch b/0017-tcg-ppc-Use-new-registers-for-LQ-destination.patch new file mode 100644 index 0000000000000000000000000000000000000000..dcddfd5f3e879991f7b81d930b4d90d62de3ac79 --- /dev/null +++ b/0017-tcg-ppc-Use-new-registers-for-LQ-destination.patch @@ -0,0 +1,151 @@ +From b88191085977d0659fab6ebdead8488e5c34c8b6 Mon Sep 17 00:00:00 2001 +From: Richard Henderson +Date: Tue, 2 Jan 2024 01:27:18 +0000 +Subject: [PATCH 017/293] tcg/ppc: Use new registers for LQ destination +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +LQ has a constraint that RTp != RA, else SIGILL. +Therefore, force the destination of INDEX_op_qemu_*_ld128 to be a +new register pair, so that it cannot overlap the input address. + +This requires new support in process_op_defs and tcg_reg_alloc_op. + +Cc: qemu-stable@nongnu.org +Fixes: 526cd4ec01f ("tcg/ppc: Support 128-bit load/store") +Reviewed-by: Philippe Mathieu-Daudé +Message-Id: <20240102013456.131846-1-richard.henderson@linaro.org> +Signed-off-by: Richard Henderson +(cherry picked from commit ca5bed07d0e7e0530c2cafbc134c4f74e582ac50) +Signed-off-by: Michael Tokarev +--- + tcg/ppc/tcg-target-con-set.h | 2 +- + tcg/ppc/tcg-target.c.inc | 3 ++- + tcg/tcg.c | 21 ++++++++++++++++----- + 3 files changed, 19 insertions(+), 7 deletions(-) + +diff --git a/tcg/ppc/tcg-target-con-set.h b/tcg/ppc/tcg-target-con-set.h +index bbd7b21..cb47b29 100644 +--- a/tcg/ppc/tcg-target-con-set.h ++++ b/tcg/ppc/tcg-target-con-set.h +@@ -35,7 +35,7 @@ C_O1_I3(v, v, v, v) + C_O1_I4(r, r, ri, rZ, rZ) + C_O1_I4(r, r, r, ri, ri) + C_O2_I1(r, r, r) +-C_O2_I1(o, m, r) ++C_N1O1_I1(o, m, r) + C_O2_I2(r, r, r, r) + C_O2_I4(r, r, rI, rZM, r, r) + C_O2_I4(r, r, r, r, rI, rZM) +diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc +index 856c3b1..5481696 100644 +--- a/tcg/ppc/tcg-target.c.inc ++++ b/tcg/ppc/tcg-target.c.inc +@@ -2595,6 +2595,7 @@ static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg datalo, TCGReg datahi, + tcg_debug_assert(!need_bswap); + tcg_debug_assert(datalo & 1); + tcg_debug_assert(datahi == datalo - 1); ++ tcg_debug_assert(!is_ld || datahi != index); + insn = is_ld ? LQ : STQ; + tcg_out32(s, insn | TAI(datahi, index, 0)); + } else { +@@ -4071,7 +4072,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) + + case INDEX_op_qemu_ld_a32_i128: + case INDEX_op_qemu_ld_a64_i128: +- return C_O2_I1(o, m, r); ++ return C_N1O1_I1(o, m, r); + case INDEX_op_qemu_st_a32_i128: + case INDEX_op_qemu_st_a64_i128: + return C_O0_I3(o, m, r); +diff --git a/tcg/tcg.c b/tcg/tcg.c +index 896a36c..e2c38f6 100644 +--- a/tcg/tcg.c ++++ b/tcg/tcg.c +@@ -653,6 +653,7 @@ static void tcg_out_movext3(TCGContext *s, const TCGMovExtend *i1, + #define C_O1_I4(O1, I1, I2, I3, I4) C_PFX5(c_o1_i4_, O1, I1, I2, I3, I4), + + #define C_N1_I2(O1, I1, I2) C_PFX3(c_n1_i2_, O1, I1, I2), ++#define C_N1O1_I1(O1, O2, I1) C_PFX3(c_n1o1_i1_, O1, O2, I1), + #define C_N2_I1(O1, O2, I1) C_PFX3(c_n2_i1_, O1, O2, I1), + + #define C_O2_I1(O1, O2, I1) C_PFX3(c_o2_i1_, O1, O2, I1), +@@ -676,6 +677,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode); + #undef C_O1_I3 + #undef C_O1_I4 + #undef C_N1_I2 ++#undef C_N1O1_I1 + #undef C_N2_I1 + #undef C_O2_I1 + #undef C_O2_I2 +@@ -696,6 +698,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode); + #define C_O1_I4(O1, I1, I2, I3, I4) { .args_ct_str = { #O1, #I1, #I2, #I3, #I4 } }, + + #define C_N1_I2(O1, I1, I2) { .args_ct_str = { "&" #O1, #I1, #I2 } }, ++#define C_N1O1_I1(O1, O2, I1) { .args_ct_str = { "&" #O1, #O2, #I1 } }, + #define C_N2_I1(O1, O2, I1) { .args_ct_str = { "&" #O1, "&" #O2, #I1 } }, + + #define C_O2_I1(O1, O2, I1) { .args_ct_str = { #O1, #O2, #I1 } }, +@@ -718,6 +721,7 @@ static const TCGTargetOpDef constraint_sets[] = { + #undef C_O1_I3 + #undef C_O1_I4 + #undef C_N1_I2 ++#undef C_N1O1_I1 + #undef C_N2_I1 + #undef C_O2_I1 + #undef C_O2_I2 +@@ -738,6 +742,7 @@ static const TCGTargetOpDef constraint_sets[] = { + #define C_O1_I4(O1, I1, I2, I3, I4) C_PFX5(c_o1_i4_, O1, I1, I2, I3, I4) + + #define C_N1_I2(O1, I1, I2) C_PFX3(c_n1_i2_, O1, I1, I2) ++#define C_N1O1_I1(O1, O2, I1) C_PFX3(c_n1o1_i1_, O1, O2, I1) + #define C_N2_I1(O1, O2, I1) C_PFX3(c_n2_i1_, O1, O2, I1) + + #define C_O2_I1(O1, O2, I1) C_PFX3(c_o2_i1_, O1, O2, I1) +@@ -2988,6 +2993,7 @@ static void process_op_defs(TCGContext *s) + .pair = 2, + .pair_index = o, + .regs = def->args_ct[o].regs << 1, ++ .newreg = def->args_ct[o].newreg, + }; + def->args_ct[o].pair = 1; + def->args_ct[o].pair_index = i; +@@ -3004,6 +3010,7 @@ static void process_op_defs(TCGContext *s) + .pair = 1, + .pair_index = o, + .regs = def->args_ct[o].regs >> 1, ++ .newreg = def->args_ct[o].newreg, + }; + def->args_ct[o].pair = 2; + def->args_ct[o].pair_index = i; +@@ -5036,17 +5043,21 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) + break; + + case 1: /* first of pair */ +- tcg_debug_assert(!arg_ct->newreg); + if (arg_ct->oalias) { + reg = new_args[arg_ct->alias_index]; +- break; ++ } else if (arg_ct->newreg) { ++ reg = tcg_reg_alloc_pair(s, arg_ct->regs, ++ i_allocated_regs | o_allocated_regs, ++ output_pref(op, k), ++ ts->indirect_base); ++ } else { ++ reg = tcg_reg_alloc_pair(s, arg_ct->regs, o_allocated_regs, ++ output_pref(op, k), ++ ts->indirect_base); + } +- reg = tcg_reg_alloc_pair(s, arg_ct->regs, o_allocated_regs, +- output_pref(op, k), ts->indirect_base); + break; + + case 2: /* second of pair */ +- tcg_debug_assert(!arg_ct->newreg); + if (arg_ct->oalias) { + reg = new_args[arg_ct->alias_index]; + } else { +-- +1.8.3.1 + diff --git a/0018-util-fix-build-with-musl-libc-on-ppc64le.patch b/0018-util-fix-build-with-musl-libc-on-ppc64le.patch new file mode 100644 index 0000000000000000000000000000000000000000..982022cc9842630f1c97775a92d2aabf3c3c934c --- /dev/null +++ b/0018-util-fix-build-with-musl-libc-on-ppc64le.patch @@ -0,0 +1,59 @@ +From 9ee4603a86624a5c9507a79086110daf830159a4 Mon Sep 17 00:00:00 2001 +From: Natanael Copa +Date: Tue, 19 Dec 2023 11:51:29 +0100 +Subject: [PATCH 018/293] util: fix build with musl libc on ppc64le + +Use PPC_FEATURE2_ISEL and PPC_FEATURE2_VEC_CRYPTO from linux headers +instead of the GNU specific PPC_FEATURE2_HAS_ISEL and +PPC_FEATURE2_HAS_VEC_CRYPTO. This fixes build with musl libc. + +Cc: qemu-stable@nongnu.org +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1861 +Signed-off-by: Natanael Copa +Fixes: 63922f467a ("tcg/ppc: Replace HAVE_ISEL macro with a variable") +Fixes: 68f340d4cd ("tcg/ppc: Enable Altivec detection") +Message-Id: <20231219105236.7059-1-ncopa@alpinelinux.org> +Signed-off-by: Richard Henderson +(cherry picked from commit 1d513e06d96697f44de4a1b85c6ff627c443e306) +Signed-off-by: Michael Tokarev +--- + util/cpuinfo-ppc.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/util/cpuinfo-ppc.c b/util/cpuinfo-ppc.c +index 1ea3db0..b2d8893 100644 +--- a/util/cpuinfo-ppc.c ++++ b/util/cpuinfo-ppc.c +@@ -6,10 +6,10 @@ + #include "qemu/osdep.h" + #include "host/cpuinfo.h" + ++#include + #ifdef CONFIG_GETAUXVAL + # include + #else +-# include + # include "elf.h" + #endif + +@@ -40,7 +40,7 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) + info |= CPUINFO_V2_06; + } + +- if (hwcap2 & PPC_FEATURE2_HAS_ISEL) { ++ if (hwcap2 & PPC_FEATURE2_ISEL) { + info |= CPUINFO_ISEL; + } + if (hwcap & PPC_FEATURE_HAS_ALTIVEC) { +@@ -53,7 +53,7 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) + * always have both anyway, since VSX came with Power7 + * and crypto came with Power8. + */ +- if (hwcap2 & PPC_FEATURE2_HAS_VEC_CRYPTO) { ++ if (hwcap2 & PPC_FEATURE2_VEC_CRYPTO) { + info |= CPUINFO_CRYPTO; + } + } +-- +1.8.3.1 + diff --git a/0019-tests-acpi-allow-tests-data-acpi-virt-SSDT.memhp-cha.patch b/0019-tests-acpi-allow-tests-data-acpi-virt-SSDT.memhp-cha.patch new file mode 100644 index 0000000000000000000000000000000000000000..2d235ea017e3b64bc0522fdc7f21f43bafe8fad5 --- /dev/null +++ b/0019-tests-acpi-allow-tests-data-acpi-virt-SSDT.memhp-cha.patch @@ -0,0 +1,23 @@ +From 4e875df3b677c7a4acd5ce1aa06839a2adf0a8da Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Wed, 13 Dec 2023 10:30:16 +0100 +Subject: [PATCH 019/293] tests/acpi: allow tests/data/acpi/virt/SSDT.memhp + changes + +Signed-off-by: Gerd Hoffmann +(cherry picked from commit ca8b0cc8e9176419960b844abb522a2298a794d6) +Signed-off-by: Michael Tokarev +--- + tests/qtest/bios-tables-test-allowed-diff.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h +index dfb8523..e569098 100644 +--- a/tests/qtest/bios-tables-test-allowed-diff.h ++++ b/tests/qtest/bios-tables-test-allowed-diff.h +@@ -1 +1,2 @@ + /* List of comma-separated changed AML files to ignore */ ++"tests/data/acpi/virt/SSDT.memhp", +-- +1.8.3.1 + diff --git a/0020-edk2-update-build-config-set-PcdUninstallMemAttrProt.patch b/0020-edk2-update-build-config-set-PcdUninstallMemAttrProt.patch new file mode 100644 index 0000000000000000000000000000000000000000..3d646711140ce6034db01f25fbf3b84e73d4d734 --- /dev/null +++ b/0020-edk2-update-build-config-set-PcdUninstallMemAttrProt.patch @@ -0,0 +1,58 @@ +From 3b1b25cdaad5cf57c476bd1ea323c086acdd5251 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Wed, 13 Dec 2023 08:57:20 +0100 +Subject: [PATCH 021/293] edk2: update build config, set + PcdUninstallMemAttrProtocol = TRUE. + +Needed to workaround buggy EFI_MEMORY_ATTRIBUTE_PROTOCOL +usage in shim.efi. + +Signed-off-by: Gerd Hoffmann +(cherry picked from commit 6f79fa5f097aa41fc96a14dfccdb0ea8d9facd6c) +Signed-off-by: Michael Tokarev +--- + roms/edk2-build.config | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/roms/edk2-build.config b/roms/edk2-build.config +index bab6a9c..0d367db 100644 +--- a/roms/edk2-build.config ++++ b/roms/edk2-build.config +@@ -22,9 +22,15 @@ SMM_REQUIRE = TRUE + [opts.armvirt.silent] + DEBUG_PRINT_ERROR_LEVEL = 0x80000000 + +-[pcds.nx.broken.grub] ++[pcds.nx.strict] ++PcdDxeNxMemoryProtectionPolicy = 0xC000000000007FD5 ++PcdUninstallMemAttrProtocol = FALSE ++ ++[pcds.nx.broken.shim.grub] + # grub.efi uses EfiLoaderData for code + PcdDxeNxMemoryProtectionPolicy = 0xC000000000007FD1 ++# shim.efi has broken MemAttr code ++PcdUninstallMemAttrProtocol = TRUE + + [pcds.workaround.202308] + PcdFirstTimeWakeUpAPsBySipi = FALSE +@@ -95,7 +101,7 @@ conf = ArmVirtPkg/ArmVirtQemu.dsc + arch = ARM + opts = common + armvirt.silent +-pcds = nx.broken.grub ++pcds = nx.broken.shim.grub + plat = ArmVirtQemu-ARM + dest = ../pc-bios + cpy1 = FV/QEMU_EFI.fd edk2-arm-code.fd +@@ -112,7 +118,7 @@ conf = ArmVirtPkg/ArmVirtQemu.dsc + arch = AARCH64 + opts = common + armvirt.silent +-pcds = nx.broken.grub ++pcds = nx.broken.shim.grub + plat = ArmVirtQemu-AARCH64 + dest = ../pc-bios + cpy1 = FV/QEMU_EFI.fd edk2-aarch64-code.fd +-- +1.8.3.1 + diff --git a/0021-tests-acpi-disallow-tests-data-acpi-virt-SSDT.memhp-.patch b/0021-tests-acpi-disallow-tests-data-acpi-virt-SSDT.memhp-.patch new file mode 100644 index 0000000000000000000000000000000000000000..4b1eb1bb68857510b4a9ce4e007d31a1a6a3ef72 --- /dev/null +++ b/0021-tests-acpi-disallow-tests-data-acpi-virt-SSDT.memhp-.patch @@ -0,0 +1,23 @@ +From 2dd8fdfe49c0178241a2292d1ea9a40f13379c47 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Wed, 13 Dec 2023 11:21:13 +0100 +Subject: [PATCH 024/293] tests/acpi: disallow tests/data/acpi/virt/SSDT.memhp + changes + +Signed-off-by: Gerd Hoffmann +(cherry picked from commit 704f7cad5105246822686f65765ab92045f71a3b) +Signed-off-by: Michael Tokarev +--- + tests/qtest/bios-tables-test-allowed-diff.h | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h +index e569098..dfb8523 100644 +--- a/tests/qtest/bios-tables-test-allowed-diff.h ++++ b/tests/qtest/bios-tables-test-allowed-diff.h +@@ -1,2 +1 @@ + /* List of comma-separated changed AML files to ignore */ +-"tests/data/acpi/virt/SSDT.memhp", +-- +1.8.3.1 + diff --git a/0022-tests-qtest-virtio-ccw-Fix-device-presence-checking.patch b/0022-tests-qtest-virtio-ccw-Fix-device-presence-checking.patch new file mode 100644 index 0000000000000000000000000000000000000000..5cae378970fb88ed7e7f564fe12eeced2f0c2318 --- /dev/null +++ b/0022-tests-qtest-virtio-ccw-Fix-device-presence-checking.patch @@ -0,0 +1,39 @@ +From 0b27f20d6a62456ae94293deb210092e6ec9949d Mon Sep 17 00:00:00 2001 +From: Samuel Tardieu +Date: Sat, 6 Jan 2024 14:01:21 +0100 +Subject: [PATCH 025/293] tests/qtest/virtio-ccw: Fix device presence checking +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +An apparent copy-paste error tests for the presence of the +virtio-rng-ccw device in order to perform tests on the virtio-scsi-ccw +device. + +Signed-off-by: Samuel Tardieu +Message-ID: <20240106130121.1244993-1-sam@rfc1149.net> +Fixes: 65331bf5d1 ("tests/qtest: Check for virtio-ccw devices before using them") +Reviewed-by: Philippe Mathieu-Daudé +Signed-off-by: Thomas Huth +(cherry picked from commit c98873ee4a0c2694aac976ab9affcf55da8b7e61) +Signed-off-by: Michael Tokarev +--- + tests/qtest/virtio-ccw-test.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/qtest/virtio-ccw-test.c b/tests/qtest/virtio-ccw-test.c +index f4f5858..7a5357c 100644 +--- a/tests/qtest/virtio-ccw-test.c ++++ b/tests/qtest/virtio-ccw-test.c +@@ -85,7 +85,7 @@ int main(int argc, char **argv) + if (qtest_has_device("virtio-rng-ccw")) { + qtest_add_func("/virtio/rng/nop", virtio_rng_nop); + } +- if (qtest_has_device("virtio-rng-ccw")) { ++ if (qtest_has_device("virtio-scsi-ccw")) { + qtest_add_func("/virtio/scsi/nop", virtio_scsi_nop); + qtest_add_func("/virtio/scsi/hotplug", virtio_scsi_hotplug); + } +-- +1.8.3.1 + diff --git a/0023-target-s390x-Fix-LAE-setting-a-wrong-access-register.patch b/0023-target-s390x-Fix-LAE-setting-a-wrong-access-register.patch new file mode 100644 index 0000000000000000000000000000000000000000..dde4790005b7313ff811870793a09e5dedf39c55 --- /dev/null +++ b/0023-target-s390x-Fix-LAE-setting-a-wrong-access-register.patch @@ -0,0 +1,45 @@ +From 08b37c90e6047d6167b3ec511cd47375c9ce1427 Mon Sep 17 00:00:00 2001 +From: Ilya Leoshkevich +Date: Thu, 11 Jan 2024 10:21:26 +0100 +Subject: [PATCH 026/293] target/s390x: Fix LAE setting a wrong access register + +LAE should set the access register corresponding to the first operand, +instead, it always modifies access register 1. + +Co-developed-by: Ido Plat +Cc: qemu-stable@nongnu.org +Fixes: a1c7610a6879 ("target-s390x: implement LAY and LAEY instructions") +Reviewed-by: David Hildenbrand +Signed-off-by: Ilya Leoshkevich +Message-ID: <20240111092328.929421-2-iii@linux.ibm.com> +Signed-off-by: Thomas Huth +(cherry picked from commit e358a25a97c71c39e3513d9b869cdb82052e50b8) +Signed-off-by: Michael Tokarev +--- + target/s390x/tcg/translate.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c +index 62ab2be..8df00b7 100644 +--- a/target/s390x/tcg/translate.c ++++ b/target/s390x/tcg/translate.c +@@ -3221,6 +3221,7 @@ static DisasJumpType op_mov2e(DisasContext *s, DisasOps *o) + { + int b2 = get_field(s, b2); + TCGv ar1 = tcg_temp_new_i64(); ++ int r1 = get_field(s, r1); + + o->out = o->in2; + o->in2 = NULL; +@@ -3244,7 +3245,7 @@ static DisasJumpType op_mov2e(DisasContext *s, DisasOps *o) + break; + } + +- tcg_gen_st32_i64(ar1, tcg_env, offsetof(CPUS390XState, aregs[1])); ++ tcg_gen_st32_i64(ar1, tcg_env, offsetof(CPUS390XState, aregs[r1])); + return DISAS_NEXT; + } + +-- +1.8.3.1 + diff --git a/0024-.gitlab-ci.d-buildtest.yml-Work-around-htags-bug-whe.patch b/0024-.gitlab-ci.d-buildtest.yml-Work-around-htags-bug-whe.patch new file mode 100644 index 0000000000000000000000000000000000000000..aeb6312da21b5f43483220abc3e8bd4e7746f931 --- /dev/null +++ b/0024-.gitlab-ci.d-buildtest.yml-Work-around-htags-bug-whe.patch @@ -0,0 +1,57 @@ +From 32ade2abef75c1acd61b491d96a67d8f745af88a Mon Sep 17 00:00:00 2001 +From: Peter Maydell +Date: Thu, 11 Jan 2024 12:55:43 +0000 +Subject: [PATCH 027/293] .gitlab-ci.d/buildtest.yml: Work around htags bug + when environment is large +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Sometimes the CI "pages" job fails with a message like this from +htags: + +$ htags -anT --tree-view=filetree -m qemu_init -t "Welcome to the QEMU sourcecode" +htags: Negative exec line limit = -371 + +This is due to a bug in hflags where if the environment is too large it +falls over: +https://lists.gnu.org/archive/html/bug-global/2024-01/msg00000.html + +This happens to us because GitLab CI puts the commit message of the +commit under test into the CI_COMMIT_MESSAGE and/or CI_COMMIT_TAG_MESSAGE +environment variables, so the job will fail if the commit happens to +have a verbose commit message. + +Work around the htags bug by unsetting these variables while running +htags. + +Cc: qemu-stable@nongnu.org +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2080 +Reviewed-by: Philippe Mathieu-Daudé +Message-ID: <20240111125543.1573473-1-peter.maydell@linaro.org> +Signed-off-by: Thomas Huth +(cherry picked from commit 52a21689cd829c1cc931b59b5ee5bdb10dd578c1) +Signed-off-by: Michael Tokarev +--- + .gitlab-ci.d/buildtest.yml | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml +index 9166394..0a01746 100644 +--- a/.gitlab-ci.d/buildtest.yml ++++ b/.gitlab-ci.d/buildtest.yml +@@ -647,7 +647,10 @@ pages: + - mkdir -p public + # HTML-ised source tree + - make gtags +- - htags -anT --tree-view=filetree -m qemu_init ++ # We unset variables to work around a bug in some htags versions ++ # which causes it to fail when the environment is large ++ - CI_COMMIT_MESSAGE= CI_COMMIT_TAG_MESSAGE= htags ++ -anT --tree-view=filetree -m qemu_init + -t "Welcome to the QEMU sourcecode" + - mv HTML public/src + # Project documentation +-- +1.8.3.1 + diff --git a/0025-readthodocs-fully-specify-a-build-environment.patch b/0025-readthodocs-fully-specify-a-build-environment.patch new file mode 100644 index 0000000000000000000000000000000000000000..b0720160502c21a1a800acf903cbf2f1a7ead40f --- /dev/null +++ b/0025-readthodocs-fully-specify-a-build-environment.patch @@ -0,0 +1,68 @@ +From 35623388b03c6c46598be61ef8d5f901c815ed7a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Alex=20Benn=C3=A9e?= +Date: Thu, 21 Dec 2023 17:42:00 +0000 +Subject: [PATCH 028/293] readthodocs: fully specify a build environment +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This is now expected by rtd so I've expanded using their example as +22.04 is one of our supported platforms. I tried to work out if there +was an easy way to re-generate a requirements.txt from our +pythondeps.toml but in the end went for the easier solution. + +Cc: +Signed-off-by: Alex Bennée +Message-Id: <20231221174200.2693694-1-alex.bennee@linaro.org> +(cherry picked from commit b16a45bc5e0e329a16af8a2e020a6e7044f9afa2) +Signed-off-by: Michael Tokarev +--- + .readthedocs.yml | 19 ++++++++++++------- + docs/requirements.txt | 2 ++ + 2 files changed, 14 insertions(+), 7 deletions(-) + create mode 100644 docs/requirements.txt + +diff --git a/.readthedocs.yml b/.readthedocs.yml +index 7fb7b8d..0b26246 100644 +--- a/.readthedocs.yml ++++ b/.readthedocs.yml +@@ -5,16 +5,21 @@ + # Required + version: 2 + ++# Set the version of Python and other tools you might need ++build: ++ os: ubuntu-22.04 ++ tools: ++ python: "3.11" ++ + # Build documentation in the docs/ directory with Sphinx + sphinx: + configuration: docs/conf.py + ++# We recommend specifying your dependencies to enable reproducible builds: ++# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html ++python: ++ install: ++ - requirements: docs/requirements.txt ++ + # We want all the document formats + formats: all +- +-# For consistency, we require that QEMU's Sphinx extensions +-# run with at least the same minimum version of Python that +-# we require for other Python in our codebase (our conf.py +-# enforces this, and some code needs it.) +-python: +- version: 3.6 +diff --git a/docs/requirements.txt b/docs/requirements.txt +new file mode 100644 +index 0000000..691e521 +--- /dev/null ++++ b/docs/requirements.txt +@@ -0,0 +1,2 @@ ++sphinx==5.3.0 ++sphinx_rtd_theme==1.1.1 +-- +1.8.3.1 + diff --git a/0026-hw-hppa-machine-Allow-up-to-3840-MB-total-memory.patch b/0026-hw-hppa-machine-Allow-up-to-3840-MB-total-memory.patch new file mode 100644 index 0000000000000000000000000000000000000000..7a033ff79f06564c70d3cb5d9743f7c63c12414c --- /dev/null +++ b/0026-hw-hppa-machine-Allow-up-to-3840-MB-total-memory.patch @@ -0,0 +1,66 @@ +From de64580f07fa4901fccc349c7f51e0e9c9e2951d Mon Sep 17 00:00:00 2001 +From: Helge Deller +Date: Sun, 31 Dec 2023 09:36:58 +0100 +Subject: [PATCH 029/293] hw/hppa/machine: Allow up to 3840 MB total memory + +The physical hardware allows DIMMs of 4 MB size and above, allowing up +to 3840 MB of memory, but is restricted by setup code to 3 GB. +Increase the limit to allow up to the maximum amount of memory. + +Btw. the memory area from 0xf000.0000 to 0xffff.ffff is reserved by +the architecture for firmware and I/O memory and can not be used for +standard memory. + +An upcoming 64-bit SeaBIOS-hppa firmware will allow more than 3.75GB +on 64-bit HPPA64. In this case the ram_max for the pa20 case will change. + +Signed-off-by: Helge Deller +Noticed-by: Nelson H. F. Beebe +Fixes: b7746b1194c8 ("hw/hppa/machine: Restrict the total memory size to 3GB") +Reviewed-by: Richard Henderson +Tested-by: Bruno Haible +(cherry picked from commit 92039f61af89629f268e04255946c2a3fa0c453f) +Signed-off-by: Michael Tokarev +--- + hw/hppa/machine.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c +index c8da7c1..b119076 100644 +--- a/hw/hppa/machine.c ++++ b/hw/hppa/machine.c +@@ -276,6 +276,7 @@ static TranslateFn *machine_HP_common_init_cpus(MachineState *machine) + unsigned int smp_cpus = machine->smp.cpus; + TranslateFn *translate; + MemoryRegion *cpu_region; ++ uint64_t ram_max; + + /* Create CPUs. */ + for (unsigned int i = 0; i < smp_cpus; i++) { +@@ -288,8 +289,10 @@ static TranslateFn *machine_HP_common_init_cpus(MachineState *machine) + */ + if (hppa_is_pa20(&cpu[0]->env)) { + translate = translate_pa20; ++ ram_max = 0xf0000000; /* 3.75 GB (limited by 32-bit firmware) */ + } else { + translate = translate_pa10; ++ ram_max = 0xf0000000; /* 3.75 GB (32-bit CPU) */ + } + + for (unsigned int i = 0; i < smp_cpus; i++) { +@@ -311,9 +314,9 @@ static TranslateFn *machine_HP_common_init_cpus(MachineState *machine) + cpu_region); + + /* Main memory region. */ +- if (machine->ram_size > 3 * GiB) { +- error_report("RAM size is currently restricted to 3GB"); +- exit(EXIT_FAILURE); ++ if (machine->ram_size > ram_max) { ++ info_report("Max RAM size limited to %" PRIu64 " MB", ram_max / MiB); ++ machine->ram_size = ram_max; + } + memory_region_add_subregion_overlap(addr_space, 0, machine->ram, -1); + +-- +1.8.3.1 + diff --git a/0027-hw-hppa-machine-Disable-default-devices-with-nodefau.patch b/0027-hw-hppa-machine-Disable-default-devices-with-nodefau.patch new file mode 100644 index 0000000000000000000000000000000000000000..c47163992ea26313541d01a87aed50d27d79332c --- /dev/null +++ b/0027-hw-hppa-machine-Disable-default-devices-with-nodefau.patch @@ -0,0 +1,64 @@ +From 4e68f4124fd8fd5a56af6e9f4d14aa61dd7f6c44 Mon Sep 17 00:00:00 2001 +From: Helge Deller +Date: Mon, 1 Jan 2024 21:47:30 +0100 +Subject: [PATCH 030/293] hw/hppa/machine: Disable default devices with + --nodefaults option + +Recognize the qemu --nodefaults option, which will disable the +following default devices on hppa: +- lsi53c895a SCSI controller, +- artist graphics card, +- LASI 82596 NIC, +- tulip PCI NIC, +- second serial PCI card, +- USB OHCI controller. + +Adding this option is very useful to allow manual testing and +debugging of the other possible devices on the command line. + +Signed-off-by: Helge Deller +Reviewed-by: Richard Henderson +(cherry picked from commit d8a3220005d74512677b181e3a32cd94b13ddf49) +Signed-off-by: Michael Tokarev +--- + hw/hppa/machine.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c +index b119076..54ca2fd 100644 +--- a/hw/hppa/machine.c ++++ b/hw/hppa/machine.c +@@ -346,8 +346,10 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus, + SysBusDevice *s; + + /* SCSI disk setup. */ +- dev = DEVICE(pci_create_simple(pci_bus, -1, "lsi53c895a")); +- lsi53c8xx_handle_legacy_cmdline(dev); ++ if (drive_get_max_bus(IF_SCSI) >= 0) { ++ dev = DEVICE(pci_create_simple(pci_bus, -1, "lsi53c895a")); ++ lsi53c8xx_handle_legacy_cmdline(dev); ++ } + + /* Graphics setup. */ + if (machine->enable_graphics && vga_interface_type != VGA_NONE) { +@@ -360,7 +362,7 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus, + } + + /* Network setup. */ +- if (enable_lasi_lan()) { ++ if (nd_table[0].used && enable_lasi_lan()) { + lasi_82596_init(addr_space, translate(NULL, LASI_LAN_HPA), + qdev_get_gpio_in(lasi_dev, LASI_IRQ_LAN_HPA)); + } +@@ -385,7 +387,7 @@ static void machine_HP_common_init_tail(MachineState *machine, PCIBus *pci_bus, + pci_set_word(&pci_dev->config[PCI_SUBSYSTEM_ID], 0x1227); /* Powerbar */ + + /* create a second serial PCI card when running Astro */ +- if (!lasi_dev) { ++ if (serial_hd(1) && !lasi_dev) { + pci_dev = pci_new(-1, "pci-serial-4x"); + qdev_prop_set_chr(DEVICE(pci_dev), "chardev1", serial_hd(1)); + qdev_prop_set_chr(DEVICE(pci_dev), "chardev2", serial_hd(2)); +-- +1.8.3.1 + diff --git a/0028-hw-pci-host-astro-Add-missing-astro-elroy-registers-.patch b/0028-hw-pci-host-astro-Add-missing-astro-elroy-registers-.patch new file mode 100644 index 0000000000000000000000000000000000000000..8620273ce87fa2741b8b3efee7c67c8cdbff1d40 --- /dev/null +++ b/0028-hw-pci-host-astro-Add-missing-astro-elroy-registers-.patch @@ -0,0 +1,103 @@ +From ad70198043559850b8d42cd1c5351ca4ee6571cb Mon Sep 17 00:00:00 2001 +From: Helge Deller +Date: Wed, 3 Jan 2024 12:45:06 +0100 +Subject: [PATCH 031/293] hw/pci-host/astro: Add missing astro & elroy + registers for NetBSD + +NetBSD accesses some astro and elroy registers which aren't accessed +by Linux yet. Add emulation for those registers to allow NetBSD to +boot further. +Please note that this patch is not sufficient to completely boot up +NetBSD on the 64-bit C3700 machine yet. + +Signed-off-by: Helge Deller +Tested-by: Bruno Haible +(cherry picked from commit 3b57c15f02050227c5c73ca97fa0dfc02f154fe9) +Signed-off-by: Michael Tokarev +--- + hw/pci-host/astro.c | 26 +++++++++++++++++++++++--- + 1 file changed, 23 insertions(+), 3 deletions(-) + +diff --git a/hw/pci-host/astro.c b/hw/pci-host/astro.c +index 7d68cce..cb2c8a8 100644 +--- a/hw/pci-host/astro.c ++++ b/hw/pci-host/astro.c +@@ -166,6 +166,8 @@ static MemTxResult elroy_chip_write_with_attrs(void *opaque, hwaddr addr, + trace_elroy_write(addr, size, val); + + switch ((addr >> 3) << 3) { ++ case 0x000: /* PCI_ID & PCI_COMMAND_STATUS_REG */ ++ break; + case 0x080: + put_val_in_int64(&s->arb_mask, addr, size, val); + break; +@@ -175,6 +177,9 @@ static MemTxResult elroy_chip_write_with_attrs(void *opaque, hwaddr addr, + case 0x200 ... 0x250 - 1: /* LMMIO, GMMIO, WLMMIO, WGMMIO, ... */ + put_val_in_arrary(s->mmio_base, 0x200, addr, size, val); + break; ++ case 0x300: /* ibase */ ++ case 0x308: /* imask */ ++ break; + case 0x0680: + put_val_in_int64(&s->error_config, addr, size, val); + break; +@@ -538,6 +543,9 @@ static MemTxResult astro_chip_read_with_attrs(void *opaque, hwaddr addr, + case 0x0030: /* HP-UX 10.20 and 11.11 reads it. No idea. */ + val = -1; + break; ++ case 0x0078: /* NetBSD reads 0x78 ? */ ++ val = -1; ++ break; + case 0x0300 ... 0x03d8: /* LMMIO_DIRECT0_BASE... */ + index = (addr - 0x300) / 8; + val = s->ioc_ranges[index]; +@@ -624,31 +632,43 @@ static MemTxResult astro_chip_write_with_attrs(void *opaque, hwaddr addr, + case 0x10220: + case 0x10230: /* HP-UX 11.11 reads it. No idea. */ + break; +- case 0x22108: /* IOC STATUS_CONTROL */ +- put_val_in_int64(&s->ioc_status_ctrl, addr, size, val); +- break; + case 0x20200 ... 0x20240 - 1: /* IOC Rope0_Control ... */ + put_val_in_arrary(s->ioc_rope_control, 0x20200, addr, size, val); + break; + case 0x20040: /* IOC Rope config */ ++ case 0x22040: + put_val_in_int64(&s->ioc_rope_config, addr, size, val); + break; + case 0x20300: ++ case 0x22300: + put_val_in_int64(&s->tlb_ibase, addr, size, val); + break; + case 0x20308: ++ case 0x22308: + put_val_in_int64(&s->tlb_imask, addr, size, val); + break; + case 0x20310: ++ case 0x22310: + put_val_in_int64(&s->tlb_pcom, addr, size, val); + /* TODO: flush iommu */ + break; + case 0x20318: ++ case 0x22318: + put_val_in_int64(&s->tlb_tcnfg, addr, size, val); + break; + case 0x20320: ++ case 0x22320: + put_val_in_int64(&s->tlb_pdir_base, addr, size, val); + break; ++ case 0x22000: /* func_id */ ++ break; ++ case 0x22008: /* func_class */ ++ break; ++ case 0x22050: /* rope_debug */ ++ break; ++ case 0x22108: /* IOC STATUS_CONTROL */ ++ put_val_in_int64(&s->ioc_status_ctrl, addr, size, val); ++ break; + /* + * empty placeholders for non-existent elroys, e.g. + * func_class, pci config & data +-- +1.8.3.1 + diff --git a/0029-hw-hppa-Move-software-power-button-address-back-into.patch b/0029-hw-hppa-Move-software-power-button-address-back-into.patch new file mode 100644 index 0000000000000000000000000000000000000000..c89b18c520e5437e6a2ea845b69395c722aa0d0d --- /dev/null +++ b/0029-hw-hppa-Move-software-power-button-address-back-into.patch @@ -0,0 +1,70 @@ +From 0f5fb24fd760bf0e693d32c6d5d7b22f035a771d Mon Sep 17 00:00:00 2001 +From: Helge Deller +Date: Wed, 3 Jan 2024 20:10:01 +0100 +Subject: [PATCH 033/293] hw/hppa: Move software power button address back into + PDC + +The various operating systems (e.g. Linux, NetBSD) have issues +mapping the power button when it's stored in page zero. +NetBSD even crashes, because it fails to map that page and then +accesses unmapped memory. + +Since we now have a consistent memory mapping of PDC in 32-bit +and 64-bit address space (the lower 32-bits of the address are in +sync) the power button can be moved back to PDC space. + +This patch fixes the power button on Linux, NetBSD and HP-UX. + +Signed-off-by: Helge Deller +Tested-by: Bruno Haible +Reviewed-by: Richard Henderson +(cherry picked from commit ed35afcb331a972210816435d6b1b5de17fc7d4f) +Signed-off-by: Michael Tokarev +--- + hw/hppa/machine.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c +index 54ca2fd..9e61162 100644 +--- a/hw/hppa/machine.c ++++ b/hw/hppa/machine.c +@@ -36,8 +36,8 @@ + + #define MIN_SEABIOS_HPPA_VERSION 12 /* require at least this fw version */ + +-/* Power button address at &PAGE0->pad[4] */ +-#define HPA_POWER_BUTTON (0x40 + 4 * sizeof(uint32_t)) ++#define HPA_POWER_BUTTON (FIRMWARE_END - 0x10) ++static hwaddr soft_power_reg; + + #define enable_lasi_lan() 0 + +@@ -45,7 +45,6 @@ static DeviceState *lasi_dev; + + static void hppa_powerdown_req(Notifier *n, void *opaque) + { +- hwaddr soft_power_reg = HPA_POWER_BUTTON; + uint32_t val; + + val = ldl_be_phys(&address_space_memory, soft_power_reg); +@@ -221,7 +220,7 @@ static FWCfgState *create_fw_cfg(MachineState *ms, PCIBus *pci_bus, + fw_cfg_add_file(fw_cfg, "/etc/hppa/machine", + g_memdup(mc->name, len), len); + +- val = cpu_to_le64(HPA_POWER_BUTTON); ++ val = cpu_to_le64(soft_power_reg); + fw_cfg_add_file(fw_cfg, "/etc/hppa/power-button-addr", + g_memdup(&val, sizeof(val)), sizeof(val)); + +@@ -295,6 +294,8 @@ static TranslateFn *machine_HP_common_init_cpus(MachineState *machine) + ram_max = 0xf0000000; /* 3.75 GB (32-bit CPU) */ + } + ++ soft_power_reg = translate(NULL, HPA_POWER_BUTTON); ++ + for (unsigned int i = 0; i < smp_cpus; i++) { + g_autofree char *name = g_strdup_printf("cpu%u-io-eir", i); + +-- +1.8.3.1 + diff --git a/0030-target-hppa-Avoid-accessing-gr0-when-raising-excepti.patch b/0030-target-hppa-Avoid-accessing-gr0-when-raising-excepti.patch new file mode 100644 index 0000000000000000000000000000000000000000..1ffd3d87fa308ef6f1dda511041e46935105cddb --- /dev/null +++ b/0030-target-hppa-Avoid-accessing-gr0-when-raising-excepti.patch @@ -0,0 +1,37 @@ +From 27cdd0ce471a6d49dd898d2c619f5f13b498daff Mon Sep 17 00:00:00 2001 +From: Helge Deller +Date: Wed, 3 Jan 2024 20:35:18 +0100 +Subject: [PATCH 034/293] target/hppa: Avoid accessing %gr0 when raising + exception + +The value of unwind_breg may reference register %r0, but we need to avoid +accessing gr0 directly and use the value 0 instead. + +At runtime I've seen unwind_breg being zero with the Linux kernel when +rfi is used to jump to smp_callin(). + +Signed-off-by: Helge Deller +Reviewed-by: Richard Henderson +Tested-by: Bruno Haible +(cherry picked from commit 5915b67013eb8c3a84e3ef05e6ba4eae55ccd173) +Signed-off-by: Michael Tokarev +--- + target/hppa/mem_helper.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c +index 4c28c58..1387f4a 100644 +--- a/target/hppa/mem_helper.c ++++ b/target/hppa/mem_helper.c +@@ -341,7 +341,7 @@ raise_exception_with_ior(CPUHPPAState *env, int excp, uintptr_t retaddr, + + cpu_restore_state(cs, retaddr); + +- b = env->gr[env->unwind_breg]; ++ b = env->unwind_breg ? env->gr[env->unwind_breg] : 0; + b >>= (env->psw & PSW_W ? 62 : 30); + env->cr[CR_IOR] |= b << 62; + +-- +1.8.3.1 + diff --git a/0031-target-hppa-Export-function-hppa_set_ior_and_isr.patch b/0031-target-hppa-Export-function-hppa_set_ior_and_isr.patch new file mode 100644 index 0000000000000000000000000000000000000000..32fc8d391a7102068e95c1ae7f28dfaa254ac624 --- /dev/null +++ b/0031-target-hppa-Export-function-hppa_set_ior_and_isr.patch @@ -0,0 +1,80 @@ +From 4b3064ec9017c065aa944dec5b956c2e7d6ce2d9 Mon Sep 17 00:00:00 2001 +From: Helge Deller +Date: Thu, 11 Jan 2024 22:50:11 +0100 +Subject: [PATCH 035/293] target/hppa: Export function hppa_set_ior_and_isr() + +Move functionality to set IOR and ISR on fault into own +function. This will be used by follow-up patches. + +Signed-off-by: Helge Deller +Reviewed-by: Richard Henderson +(cherry picked from commit 3824e0d643f34ee09e0cc75190c0c4b60928b78c) +Signed-off-by: Michael Tokarev +--- + target/hppa/cpu.h | 1 + + target/hppa/mem_helper.c | 23 ++++++++++++----------- + 2 files changed, 13 insertions(+), 11 deletions(-) + +diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h +index 8be45c6..9556e95 100644 +--- a/target/hppa/cpu.h ++++ b/target/hppa/cpu.h +@@ -385,6 +385,7 @@ void hppa_cpu_dump_state(CPUState *cs, FILE *f, int); + #ifndef CONFIG_USER_ONLY + void hppa_ptlbe(CPUHPPAState *env); + hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr); ++void hppa_set_ior_and_isr(CPUHPPAState *env, vaddr addr, bool mmu_disabled); + bool hppa_cpu_tlb_fill(CPUState *cs, vaddr address, int size, + MMUAccessType access_type, int mmu_idx, + bool probe, uintptr_t retaddr); +diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c +index 1387f4a..4fcc612 100644 +--- a/target/hppa/mem_helper.c ++++ b/target/hppa/mem_helper.c +@@ -305,14 +305,8 @@ hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) + return excp == EXCP_DTLB_MISS ? -1 : phys; + } + +-G_NORETURN static void +-raise_exception_with_ior(CPUHPPAState *env, int excp, uintptr_t retaddr, +- vaddr addr, bool mmu_disabled) ++void hppa_set_ior_and_isr(CPUHPPAState *env, vaddr addr, bool mmu_disabled) + { +- CPUState *cs = env_cpu(env); +- +- cs->exception_index = excp; +- + if (env->psw & PSW_Q) { + /* + * For pa1.x, the offset and space never overlap, and so we +@@ -339,16 +333,23 @@ raise_exception_with_ior(CPUHPPAState *env, int excp, uintptr_t retaddr, + */ + uint64_t b; + +- cpu_restore_state(cs, retaddr); +- + b = env->unwind_breg ? env->gr[env->unwind_breg] : 0; + b >>= (env->psw & PSW_W ? 62 : 30); + env->cr[CR_IOR] |= b << 62; +- +- cpu_loop_exit(cs); + } + } + } ++} ++ ++G_NORETURN static void ++raise_exception_with_ior(CPUHPPAState *env, int excp, uintptr_t retaddr, ++ vaddr addr, bool mmu_disabled) ++{ ++ CPUState *cs = env_cpu(env); ++ ++ cs->exception_index = excp; ++ hppa_set_ior_and_isr(env, addr, mmu_disabled); ++ + cpu_loop_exit_restore(cs, retaddr); + } + +-- +1.8.3.1 + diff --git a/0032-target-hppa-Fix-IOR-and-ISR-on-unaligned-access-trap.patch b/0032-target-hppa-Fix-IOR-and-ISR-on-unaligned-access-trap.patch new file mode 100644 index 0000000000000000000000000000000000000000..cdabbb8e2b3612a5319feac4a14dc6a7d57ab29c --- /dev/null +++ b/0032-target-hppa-Fix-IOR-and-ISR-on-unaligned-access-trap.patch @@ -0,0 +1,35 @@ +From abf489be5a1aced7b0995787874c679c26577403 Mon Sep 17 00:00:00 2001 +From: Helge Deller +Date: Thu, 11 Jan 2024 23:09:47 +0100 +Subject: [PATCH 036/293] target/hppa: Fix IOR and ISR on unaligned access trap + +Put correct values (depending on CPU arch) into IOR and ISR on fault. + +Signed-off-by: Helge Deller +Reviewed-by: Richard Henderson +(cherry picked from commit 910ada0225d17530188aa45afcb9412c17267f46) +Signed-off-by: Michael Tokarev +--- + target/hppa/cpu.c | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c +index 04de168..fda32d7 100644 +--- a/target/hppa/cpu.c ++++ b/target/hppa/cpu.c +@@ -110,11 +110,7 @@ void hppa_cpu_do_unaligned_access(CPUState *cs, vaddr addr, + CPUHPPAState *env = &cpu->env; + + cs->exception_index = EXCP_UNALIGN; +- if (env->psw & PSW_Q) { +- /* ??? Needs tweaking for hppa64. */ +- env->cr[CR_IOR] = addr; +- env->cr[CR_ISR] = addr >> 32; +- } ++ hppa_set_ior_and_isr(env, addr, MMU_IDX_MMU_DISABLED(mmu_idx)); + + cpu_loop_exit_restore(cs, retaddr); + } +-- +1.8.3.1 + diff --git a/0033-target-hppa-Fix-IOR-and-ISR-on-error-in-probe.patch b/0033-target-hppa-Fix-IOR-and-ISR-on-error-in-probe.patch new file mode 100644 index 0000000000000000000000000000000000000000..874b1299912df3b7372e79019d890e95a04c1112 --- /dev/null +++ b/0033-target-hppa-Fix-IOR-and-ISR-on-error-in-probe.patch @@ -0,0 +1,35 @@ +From 067aa95c4782a5de7796bc3aa1763b38c6d16613 Mon Sep 17 00:00:00 2001 +From: Helge Deller +Date: Wed, 3 Jan 2024 19:51:13 +0100 +Subject: [PATCH 037/293] target/hppa: Fix IOR and ISR on error in probe + +Put correct values (depending on CPU arch) into IOR and ISR on fault. + +Signed-off-by: Helge Deller +Reviewed-by: Richard Henderson +(cherry picked from commit 31efbe72c6cc54b9cbc2505d78870a8a87a8d392) +Signed-off-by: Michael Tokarev +--- + target/hppa/op_helper.c | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c +index 7f607c3..ce15469 100644 +--- a/target/hppa/op_helper.c ++++ b/target/hppa/op_helper.c +@@ -351,11 +351,7 @@ target_ulong HELPER(probe)(CPUHPPAState *env, target_ulong addr, + excp = hppa_get_physical_address(env, addr, mmu_idx, 0, &phys, + &prot, NULL); + if (excp >= 0) { +- if (env->psw & PSW_Q) { +- /* ??? Needs tweaking for hppa64. */ +- env->cr[CR_IOR] = addr; +- env->cr[CR_ISR] = addr >> 32; +- } ++ hppa_set_ior_and_isr(env, addr, MMU_IDX_MMU_DISABLED(mmu_idx)); + if (excp == EXCP_DTLB_MISS) { + excp = EXCP_NA_DTLB_MISS; + } +-- +1.8.3.1 + diff --git a/0034-load_elf-fix-iterator-s-type-for-elf-file-processing.patch b/0034-load_elf-fix-iterator-s-type-for-elf-file-processing.patch new file mode 100644 index 0000000000000000000000000000000000000000..ebc555c6a7e3cb7b33a4745c29c784b8c2eadca4 --- /dev/null +++ b/0034-load_elf-fix-iterator-s-type-for-elf-file-processing.patch @@ -0,0 +1,42 @@ +From 72dd722370864fbd4bedb218a8df6b71867c47e1 Mon Sep 17 00:00:00 2001 +From: Anastasia Belova +Date: Mon, 15 Jan 2024 12:22:16 +0300 +Subject: [PATCH 039/293] load_elf: fix iterator's type for elf file processing + +j is used while loading an ELF file to byteswap segments' +data. If data is larger than 2GB an overflow may happen. +So j should be elf_word. + +This commit fixes a minor bug: it's unlikely anybody is trying to +load ELF files with 2GB+ segments for wrong-endianness targets, +but if they did, it wouldn't work correctly. + +Found by Linux Verification Center (linuxtesting.org) with SVACE. + +Cc: qemu-stable@nongnu.org +Fixes: 7ef295ea5b ("loader: Add data swap option to load-elf") +Signed-off-by: Anastasia Belova +Reviewed-by: Peter Maydell +Signed-off-by: Peter Maydell +(cherry picked from commit 410c2a4d75f52f6a2fe978eda5a9b6f854afe5ea) +Signed-off-by: Michael Tokarev +--- + include/hw/elf_ops.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h +index 0a5c258..9c35d1b 100644 +--- a/include/hw/elf_ops.h ++++ b/include/hw/elf_ops.h +@@ -500,7 +500,7 @@ static ssize_t glue(load_elf, SZ)(const char *name, int fd, + } + + if (data_swab) { +- int j; ++ elf_word j; + for (j = 0; j < file_size; j += (1 << data_swab)) { + uint8_t *dp = data + j; + switch (data_swab) { +-- +1.8.3.1 + diff --git a/0035-target-i386-Do-not-re-compute-new-pc-with-CF_PCREL.patch b/0035-target-i386-Do-not-re-compute-new-pc-with-CF_PCREL.patch new file mode 100644 index 0000000000000000000000000000000000000000..dbff40ad1c4b5f5241703aa71388d8ff82f1439d --- /dev/null +++ b/0035-target-i386-Do-not-re-compute-new-pc-with-CF_PCREL.patch @@ -0,0 +1,53 @@ +From 6e8e580e3947eb77d1757a6ea59c58bd311bcb7e Mon Sep 17 00:00:00 2001 +From: Richard Henderson +Date: Tue, 2 Jan 2024 10:06:17 +1100 +Subject: [PATCH 040/293] target/i386: Do not re-compute new pc with CF_PCREL + +With PCREL, we have a page-relative view of EIP, and an +approximation of PC = EIP+CSBASE that is good enough to +detect page crossings. If we try to recompute PC after +masking EIP, we will mess up that approximation and write +a corrupt value to EIP. + +We already handled masking properly for PCREL, so the +fix in b5e0d5d2 was only needed for the !PCREL path. + +Cc: qemu-stable@nongnu.org +Fixes: b5e0d5d22fbf ("target/i386: Fix 32-bit wrapping of pc/eip computation") +Reported-by: Michael Tokarev +Signed-off-by: Richard Henderson +Message-ID: <20240101230617.129349-1-richard.henderson@linaro.org> +Signed-off-by: Paolo Bonzini +(cherry picked from commit a58506b748b8988a95f4fa1a2420ac5c17038b30) +Signed-off-by: Michael Tokarev +--- + target/i386/tcg/translate.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c +index 037bc47..e68375b 100644 +--- a/target/i386/tcg/translate.c ++++ b/target/i386/tcg/translate.c +@@ -2845,10 +2845,6 @@ static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num) + } + } + new_eip &= mask; +- new_pc = new_eip + s->cs_base; +- if (!CODE64(s)) { +- new_pc = (uint32_t)new_pc; +- } + + gen_update_cc_op(s); + set_cc_op(s, CC_OP_DYNAMIC); +@@ -2864,6 +2860,8 @@ static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num) + tcg_gen_andi_tl(cpu_eip, cpu_eip, mask); + use_goto_tb = false; + } ++ } else if (!CODE64(s)) { ++ new_pc = (uint32_t)(new_eip + s->cs_base); + } + + if (use_goto_tb && translator_use_goto_tb(&s->base, new_pc)) { +-- +1.8.3.1 + diff --git a/0036-target-i386-fix-incorrect-EIP-in-PC-relative-transla.patch b/0036-target-i386-fix-incorrect-EIP-in-PC-relative-transla.patch new file mode 100644 index 0000000000000000000000000000000000000000..47995be238b2f53a86582a6c215ce3f08a308e43 --- /dev/null +++ b/0036-target-i386-fix-incorrect-EIP-in-PC-relative-transla.patch @@ -0,0 +1,40 @@ +From 652c34cbb20bccf8d1b00863c6e3da66da299cb9 Mon Sep 17 00:00:00 2001 +From: guoguangyao +Date: Mon, 15 Jan 2024 10:08:04 +0800 +Subject: [PATCH 041/293] target/i386: fix incorrect EIP in PC-relative + translation blocks + +The PCREL patches introduced a bug when updating EIP in the !CF_PCREL case. +Using s->pc in func gen_update_eip_next() solves the problem. + +Cc: qemu-stable@nongnu.org +Fixes: b5e0d5d22fbf ("target/i386: Fix 32-bit wrapping of pc/eip computation") +Signed-off-by: guoguangyao +Reviewed-by: Richard Henderson +Message-ID: <20240115020804.30272-1-guoguangyao18@mails.ucas.ac.cn> +Signed-off-by: Paolo Bonzini +(cherry picked from commit 2926eab8969908bc068629e973062a0fb6ff3759) +Signed-off-by: Michael Tokarev +--- + target/i386/tcg/translate.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c +index e68375b..312ca0d 100644 +--- a/target/i386/tcg/translate.c ++++ b/target/i386/tcg/translate.c +@@ -566,9 +566,9 @@ static void gen_update_eip_next(DisasContext *s) + if (tb_cflags(s->base.tb) & CF_PCREL) { + tcg_gen_addi_tl(cpu_eip, cpu_eip, s->pc - s->pc_save); + } else if (CODE64(s)) { +- tcg_gen_movi_tl(cpu_eip, s->base.pc_next); ++ tcg_gen_movi_tl(cpu_eip, s->pc); + } else { +- tcg_gen_movi_tl(cpu_eip, (uint32_t)(s->base.pc_next - s->cs_base)); ++ tcg_gen_movi_tl(cpu_eip, (uint32_t)(s->pc - s->cs_base)); + } + s->pc_save = s->pc; + } +-- +1.8.3.1 + diff --git a/0037-target-i386-pcrel-store-low-bits-of-physical-address.patch b/0037-target-i386-pcrel-store-low-bits-of-physical-address.patch new file mode 100644 index 0000000000000000000000000000000000000000..fcfcbbe1d59dcb9ce82776bcef35ba5ce2355483 --- /dev/null +++ b/0037-target-i386-pcrel-store-low-bits-of-physical-address.patch @@ -0,0 +1,105 @@ +From c46f68bd7d48591c8d1cabc3b9f54c690e9c7270 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Wed, 17 Jan 2024 16:27:42 +0100 +Subject: [PATCH 042/293] target/i386: pcrel: store low bits of physical + address in data[0] + +For PC-relative translation blocks, env->eip changes during the +execution of a translation block, Therefore, QEMU must be able to +recover an instruction's PC just from the TranslationBlock struct and +the instruction data with. Because a TB will not span two pages, QEMU +stores all the low bits of EIP in the instruction data and replaces them +in x86_restore_state_to_opc. Bits 12 and higher (which may vary between +executions of a PCREL TB, since these only use the physical address in +the hash key) are kept unmodified from env->eip. The assumption is that +these bits of EIP, unlike bits 0-11, will not change as the translation +block executes. + +Unfortunately, this is incorrect when the CS base is not aligned to a page. +Then the linear address of the instructions (i.e. the one with the +CS base addred) indeed will never span two pages, but bits 12+ of EIP +can actually change. For example, if CS base is 0x80262200 and EIP = +0x6FF4, the first instruction in the translation block will be at linear +address 0x802691F4. Even a very small TB will cross to EIP = 0x7xxx, +while the linear addresses will remain comfortably within a single page. + +The fix is simply to use the low bits of the linear address for data[0], +since those don't change. Then x86_restore_state_to_opc uses tb->cs_base +to compute a temporary linear address (referring to some unknown +instruction in the TB, but with the correct values of bits 12 and higher); +the low bits are replaced with data[0], and EIP is obtained by subtracting +again the CS base. + +Huge thanks to Mark Cave-Ayland for the image and initial debugging, +and to Gitlab user @kjliew for help with bisecting another occurrence +of (hopefully!) the same bug. + +It should be relatively easy to write a testcase that performs MMIO on +an EIP with different bits 12+ than the first instruction of the translation +block; any help is welcome. + +Fixes: e3a79e0e878 ("target/i386: Enable TARGET_TB_PCREL", 2022-10-11) +Cc: qemu-stable@nongnu.org +Cc: Mark Cave-Ayland +Cc: Richard Henderson +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1759 +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1964 +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2012 +Signed-off-by: Paolo Bonzini +(cherry picked from commit 729ba8e933f8af5800c3a92b37e630e9bdaa9f1e) +Signed-off-by: Michael Tokarev +--- + target/i386/tcg/tcg-cpu.c | 20 ++++++++++++++++---- + target/i386/tcg/translate.c | 1 - + 2 files changed, 16 insertions(+), 5 deletions(-) + +diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c +index 6e881e9..1d54164 100644 +--- a/target/i386/tcg/tcg-cpu.c ++++ b/target/i386/tcg/tcg-cpu.c +@@ -68,14 +68,26 @@ static void x86_restore_state_to_opc(CPUState *cs, + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + int cc_op = data[1]; ++ uint64_t new_pc; + + if (tb_cflags(tb) & CF_PCREL) { +- env->eip = (env->eip & TARGET_PAGE_MASK) | data[0]; +- } else if (tb->flags & HF_CS64_MASK) { +- env->eip = data[0]; ++ /* ++ * data[0] in PC-relative TBs is also a linear address, i.e. an address with ++ * the CS base added, because it is not guaranteed that EIP bits 12 and higher ++ * stay the same across the translation block. Add the CS base back before ++ * replacing the low bits, and subtract it below just like for !CF_PCREL. ++ */ ++ uint64_t pc = env->eip + tb->cs_base; ++ new_pc = (pc & TARGET_PAGE_MASK) | data[0]; + } else { +- env->eip = (uint32_t)(data[0] - tb->cs_base); ++ new_pc = data[0]; + } ++ if (tb->flags & HF_CS64_MASK) { ++ env->eip = new_pc; ++ } else { ++ env->eip = (uint32_t)(new_pc - tb->cs_base); ++ } ++ + if (cc_op != CC_OP_DYNAMIC) { + env->cc_op = cc_op; + } +diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c +index 312ca0d..8fd49ff 100644 +--- a/target/i386/tcg/translate.c ++++ b/target/i386/tcg/translate.c +@@ -6972,7 +6972,6 @@ static void i386_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) + + dc->prev_insn_end = tcg_last_op(); + if (tb_cflags(dcbase->tb) & CF_PCREL) { +- pc_arg -= dc->cs_base; + pc_arg &= ~TARGET_PAGE_MASK; + } + tcg_gen_insn_start(pc_arg, dc->cc_op); +-- +1.8.3.1 + diff --git a/0038-backends-cryptodev-Do-not-ignore-throttle-backends-E.patch b/0038-backends-cryptodev-Do-not-ignore-throttle-backends-E.patch new file mode 100644 index 0000000000000000000000000000000000000000..6b9e2ff645dafa184874cb3fe00aa3a25bfdce70 --- /dev/null +++ b/0038-backends-cryptodev-Do-not-ignore-throttle-backends-E.patch @@ -0,0 +1,65 @@ +From a4daea6f9f187ab0340cd04a00fa2c4a82b68c34 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= +Date: Mon, 20 Nov 2023 15:54:16 +0100 +Subject: [PATCH 043/293] backends/cryptodev: Do not ignore throttle/backends + Errors +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Both cryptodev_backend_set_throttle() and CryptoDevBackendClass::init() +can set their Error** argument. Do not ignore them, return early +on failure. Without that, running into another failure trips +error_setv()'s assertion. Use the ERRP_GUARD() macro as suggested +in commit ae7c80a7bd ("error: New macro ERRP_GUARD()"). + +Cc: qemu-stable@nongnu.org +Fixes: e7a775fd9f ("cryptodev: Account statistics") +Fixes: 2580b452ff ("cryptodev: support QoS") +Reviewed-by: zhenwei pi +Reviewed-by: Gonglei +Reviewed-by: Markus Armbruster +Signed-off-by: Philippe Mathieu-Daudé +Message-Id: <20231120150418.93443-1-philmd@linaro.org> +(cherry picked from commit 484aecf2d3a75251b63481be2a0c3aef635002af) +Signed-off-by: Michael Tokarev +--- + backends/cryptodev.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/backends/cryptodev.c b/backends/cryptodev.c +index e5006bd..fff89fd 100644 +--- a/backends/cryptodev.c ++++ b/backends/cryptodev.c +@@ -398,6 +398,7 @@ static void cryptodev_backend_set_ops(Object *obj, Visitor *v, + static void + cryptodev_backend_complete(UserCreatable *uc, Error **errp) + { ++ ERRP_GUARD(); + CryptoDevBackend *backend = CRYPTODEV_BACKEND(uc); + CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_GET_CLASS(uc); + uint32_t services; +@@ -406,11 +407,20 @@ cryptodev_backend_complete(UserCreatable *uc, Error **errp) + QTAILQ_INIT(&backend->opinfos); + value = backend->tc.buckets[THROTTLE_OPS_TOTAL].avg; + cryptodev_backend_set_throttle(backend, THROTTLE_OPS_TOTAL, value, errp); ++ if (*errp) { ++ return; ++ } + value = backend->tc.buckets[THROTTLE_BPS_TOTAL].avg; + cryptodev_backend_set_throttle(backend, THROTTLE_BPS_TOTAL, value, errp); ++ if (*errp) { ++ return; ++ } + + if (bc->init) { + bc->init(backend, errp); ++ if (*errp) { ++ return; ++ } + } + + services = backend->conf.crypto_services; +-- +1.8.3.1 + diff --git a/0039-hw-pflash-refactor-pflash_data_write.patch b/0039-hw-pflash-refactor-pflash_data_write.patch new file mode 100644 index 0000000000000000000000000000000000000000..3de82bfd92fe17bd6f303e757b96aa99acafa275 --- /dev/null +++ b/0039-hw-pflash-refactor-pflash_data_write.patch @@ -0,0 +1,80 @@ +From 731783ff1e7fced41dc857a0e39deadaf8ae2477 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Mon, 8 Jan 2024 17:08:57 +0100 +Subject: [PATCH 044/293] hw/pflash: refactor pflash_data_write() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Move the offset calculation, do it once at the start of the function and +let the 'p' variable point directly to the memory location which should +be updated. This makes it simpler to update other buffers than +pfl->storage in an upcoming patch. No functional change. + +Signed-off-by: Gerd Hoffmann +Reviewed-by: Philippe Mathieu-Daudé +Message-ID: <20240108160900.104835-2-kraxel@redhat.com> +Signed-off-by: Philippe Mathieu-Daudé +(cherry picked from commit 3b14a555fdb627ac091559ef5931c887d06590d8) +Signed-off-by: Michael Tokarev +--- + hw/block/pflash_cfi01.c | 30 ++++++++++++++++-------------- + 1 file changed, 16 insertions(+), 14 deletions(-) + +diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c +index 62056b1..82f592d 100644 +--- a/hw/block/pflash_cfi01.c ++++ b/hw/block/pflash_cfi01.c +@@ -403,33 +403,35 @@ static void pflash_update(PFlashCFI01 *pfl, int offset, + static inline void pflash_data_write(PFlashCFI01 *pfl, hwaddr offset, + uint32_t value, int width, int be) + { +- uint8_t *p = pfl->storage; ++ uint8_t *p; + + trace_pflash_data_write(pfl->name, offset, width, value, pfl->counter); ++ p = pfl->storage + offset; ++ + switch (width) { + case 1: +- p[offset] = value; ++ p[0] = value; + break; + case 2: + if (be) { +- p[offset] = value >> 8; +- p[offset + 1] = value; ++ p[0] = value >> 8; ++ p[1] = value; + } else { +- p[offset] = value; +- p[offset + 1] = value >> 8; ++ p[0] = value; ++ p[1] = value >> 8; + } + break; + case 4: + if (be) { +- p[offset] = value >> 24; +- p[offset + 1] = value >> 16; +- p[offset + 2] = value >> 8; +- p[offset + 3] = value; ++ p[0] = value >> 24; ++ p[1] = value >> 16; ++ p[2] = value >> 8; ++ p[3] = value; + } else { +- p[offset] = value; +- p[offset + 1] = value >> 8; +- p[offset + 2] = value >> 16; +- p[offset + 3] = value >> 24; ++ p[0] = value; ++ p[1] = value >> 8; ++ p[2] = value >> 16; ++ p[3] = value >> 24; + } + break; + } +-- +1.8.3.1 + diff --git a/0040-hw-pflash-use-ldn_-be-le-_p-and-stn_-be-le-_p.patch b/0040-hw-pflash-use-ldn_-be-le-_p-and-stn_-be-le-_p.patch new file mode 100644 index 0000000000000000000000000000000000000000..522afb712e1e804bf4563e48f4584c90ff4b4f85 --- /dev/null +++ b/0040-hw-pflash-use-ldn_-be-le-_p-and-stn_-be-le-_p.patch @@ -0,0 +1,107 @@ +From 143d230d51a8f125804cb75a9bbc430e91027f61 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Mon, 8 Jan 2024 17:08:58 +0100 +Subject: [PATCH 045/293] hw/pflash: use ldn_{be,le}_p and stn_{be,le}_p +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Use the helper functions we have to read/write multi-byte values +in correct byte order. + +Suggested-by: Philippe Mathieu-Daudé +Signed-off-by: Gerd Hoffmann +Reviewed-by: Philippe Mathieu-Daudé +Message-ID: <20240108160900.104835-3-kraxel@redhat.com> +Signed-off-by: Philippe Mathieu-Daudé +(cherry picked from commit 5dd58358a57048e5ceabf5c91c0544f4f56afdcd) +Signed-off-by: Michael Tokarev +--- + hw/block/pflash_cfi01.c | 63 +++++++------------------------------------------ + 1 file changed, 8 insertions(+), 55 deletions(-) + +diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c +index 82f592d..f1a9dd5 100644 +--- a/hw/block/pflash_cfi01.c ++++ b/hw/block/pflash_cfi01.c +@@ -225,34 +225,10 @@ static uint32_t pflash_data_read(PFlashCFI01 *pfl, hwaddr offset, + uint32_t ret; + + p = pfl->storage; +- switch (width) { +- case 1: +- ret = p[offset]; +- break; +- case 2: +- if (be) { +- ret = p[offset] << 8; +- ret |= p[offset + 1]; +- } else { +- ret = p[offset]; +- ret |= p[offset + 1] << 8; +- } +- break; +- case 4: +- if (be) { +- ret = p[offset] << 24; +- ret |= p[offset + 1] << 16; +- ret |= p[offset + 2] << 8; +- ret |= p[offset + 3]; +- } else { +- ret = p[offset]; +- ret |= p[offset + 1] << 8; +- ret |= p[offset + 2] << 16; +- ret |= p[offset + 3] << 24; +- } +- break; +- default: +- abort(); ++ if (be) { ++ ret = ldn_be_p(p + offset, width); ++ } else { ++ ret = ldn_le_p(p + offset, width); + } + trace_pflash_data_read(pfl->name, offset, width, ret); + return ret; +@@ -408,34 +384,11 @@ static inline void pflash_data_write(PFlashCFI01 *pfl, hwaddr offset, + trace_pflash_data_write(pfl->name, offset, width, value, pfl->counter); + p = pfl->storage + offset; + +- switch (width) { +- case 1: +- p[0] = value; +- break; +- case 2: +- if (be) { +- p[0] = value >> 8; +- p[1] = value; +- } else { +- p[0] = value; +- p[1] = value >> 8; +- } +- break; +- case 4: +- if (be) { +- p[0] = value >> 24; +- p[1] = value >> 16; +- p[2] = value >> 8; +- p[3] = value; +- } else { +- p[0] = value; +- p[1] = value >> 8; +- p[2] = value >> 16; +- p[3] = value >> 24; +- } +- break; ++ if (be) { ++ stn_be_p(p, width, value); ++ } else { ++ stn_le_p(p, width, value); + } +- + } + + static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, +-- +1.8.3.1 + diff --git a/0041-hw-pflash-implement-update-buffer-for-block-writes.patch b/0041-hw-pflash-implement-update-buffer-for-block-writes.patch new file mode 100644 index 0000000000000000000000000000000000000000..61fcc20e80e7e6b5b28d2508007a908c83b1eb32 --- /dev/null +++ b/0041-hw-pflash-implement-update-buffer-for-block-writes.patch @@ -0,0 +1,271 @@ +From 1fc277a0e8ccb86a29c0b6ef656d90a62ef762d9 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Mon, 8 Jan 2024 17:08:59 +0100 +Subject: [PATCH 046/293] hw/pflash: implement update buffer for block writes +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add an update buffer where all block updates are staged. +Flush or discard updates properly, so we should never see +half-completed block writes in pflash storage. + +Drop a bunch of FIXME comments ;) + +Signed-off-by: Gerd Hoffmann +Reviewed-by: Philippe Mathieu-Daudé +Message-ID: <20240108160900.104835-4-kraxel@redhat.com> +Signed-off-by: Philippe Mathieu-Daudé +(cherry picked from commit 284a7ee2e290e0c9b8cd3ea6164d92386933054f) +Signed-off-by: Michael Tokarev +(Mjt: drop const in hw/block/pflash_cfi01.c for before + v8.2.0-220-g7d5dc0a367 "hw/block: Constify VMState") +--- + hw/block/pflash_cfi01.c | 110 ++++++++++++++++++++++++++++++++++++------------ + hw/block/pflash_cfi02.c | 2 +- + hw/block/trace-events | 7 ++- + 3 files changed, 89 insertions(+), 30 deletions(-) + +diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c +index f1a9dd5..5e848a9 100644 +--- a/hw/block/pflash_cfi01.c ++++ b/hw/block/pflash_cfi01.c +@@ -80,16 +80,39 @@ struct PFlashCFI01 { + uint16_t ident3; + uint8_t cfi_table[0x52]; + uint64_t counter; +- unsigned int writeblock_size; ++ uint32_t writeblock_size; + MemoryRegion mem; + char *name; + void *storage; + VMChangeStateEntry *vmstate; + bool old_multiple_chip_handling; ++ ++ /* block update buffer */ ++ unsigned char *blk_bytes; ++ uint32_t blk_offset; + }; + + static int pflash_post_load(void *opaque, int version_id); + ++static bool pflash_blk_write_state_needed(void *opaque) ++{ ++ PFlashCFI01 *pfl = opaque; ++ ++ return (pfl->blk_offset != -1); ++} ++ ++static const VMStateDescription vmstate_pflash_blk_write = { ++ .name = "pflash_cfi01_blk_write", ++ .version_id = 1, ++ .minimum_version_id = 1, ++ .needed = pflash_blk_write_state_needed, ++ .fields = (const VMStateField[]) { ++ VMSTATE_VBUFFER_UINT32(blk_bytes, PFlashCFI01, 0, NULL, writeblock_size), ++ VMSTATE_UINT32(blk_offset, PFlashCFI01), ++ VMSTATE_END_OF_LIST() ++ } ++}; ++ + static const VMStateDescription vmstate_pflash = { + .name = "pflash_cfi01", + .version_id = 1, +@@ -101,6 +124,10 @@ static const VMStateDescription vmstate_pflash = { + VMSTATE_UINT8(status, PFlashCFI01), + VMSTATE_UINT64(counter, PFlashCFI01), + VMSTATE_END_OF_LIST() ++ }, ++ .subsections = (const VMStateDescription * []) { ++ &vmstate_pflash_blk_write, ++ NULL + } + }; + +@@ -376,13 +403,55 @@ static void pflash_update(PFlashCFI01 *pfl, int offset, + } + } + ++/* copy current flash content to block update buffer */ ++static void pflash_blk_write_start(PFlashCFI01 *pfl, hwaddr offset) ++{ ++ hwaddr mask = ~(pfl->writeblock_size - 1); ++ ++ trace_pflash_write_block_start(pfl->name, pfl->counter); ++ pfl->blk_offset = offset & mask; ++ memcpy(pfl->blk_bytes, pfl->storage + pfl->blk_offset, ++ pfl->writeblock_size); ++} ++ ++/* commit block update buffer changes */ ++static void pflash_blk_write_flush(PFlashCFI01 *pfl) ++{ ++ g_assert(pfl->blk_offset != -1); ++ trace_pflash_write_block_flush(pfl->name); ++ memcpy(pfl->storage + pfl->blk_offset, pfl->blk_bytes, ++ pfl->writeblock_size); ++ pflash_update(pfl, pfl->blk_offset, pfl->writeblock_size); ++ pfl->blk_offset = -1; ++} ++ ++/* discard block update buffer changes */ ++static void pflash_blk_write_abort(PFlashCFI01 *pfl) ++{ ++ trace_pflash_write_block_abort(pfl->name); ++ pfl->blk_offset = -1; ++} ++ + static inline void pflash_data_write(PFlashCFI01 *pfl, hwaddr offset, + uint32_t value, int width, int be) + { + uint8_t *p; + +- trace_pflash_data_write(pfl->name, offset, width, value, pfl->counter); +- p = pfl->storage + offset; ++ if (pfl->blk_offset != -1) { ++ /* block write: redirect writes to block update buffer */ ++ if ((offset < pfl->blk_offset) || ++ (offset + width > pfl->blk_offset + pfl->writeblock_size)) { ++ pfl->status |= 0x10; /* Programming error */ ++ return; ++ } ++ trace_pflash_data_write_block(pfl->name, offset, width, value, ++ pfl->counter); ++ p = pfl->blk_bytes + (offset - pfl->blk_offset); ++ } else { ++ /* write directly to storage */ ++ trace_pflash_data_write(pfl->name, offset, width, value); ++ p = pfl->storage + offset; ++ } + + if (be) { + stn_be_p(p, width, value); +@@ -503,9 +572,9 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, + } else { + value = extract32(value, 0, pfl->bank_width * 8); + } +- trace_pflash_write_block(pfl->name, value); + pfl->counter = value; + pfl->wcycle++; ++ pflash_blk_write_start(pfl, offset); + break; + case 0x60: + if (cmd == 0xd0) { +@@ -536,12 +605,7 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, + switch (pfl->cmd) { + case 0xe8: /* Block write */ + /* FIXME check @offset, @width */ +- if (!pfl->ro) { +- /* +- * FIXME writing straight to memory is *wrong*. We +- * should write to a buffer, and flush it to memory +- * only on confirm command (see below). +- */ ++ if (!pfl->ro && (pfl->blk_offset != -1)) { + pflash_data_write(pfl, offset, value, width, be); + } else { + pfl->status |= 0x10; /* Programming error */ +@@ -550,18 +614,8 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, + pfl->status |= 0x80; + + if (!pfl->counter) { +- hwaddr mask = pfl->writeblock_size - 1; +- mask = ~mask; +- + trace_pflash_write(pfl->name, "block write finished"); + pfl->wcycle++; +- if (!pfl->ro) { +- /* Flush the entire write buffer onto backing storage. */ +- /* FIXME premature! */ +- pflash_update(pfl, offset & mask, pfl->writeblock_size); +- } else { +- pfl->status |= 0x10; /* Programming error */ +- } + } + + pfl->counter--; +@@ -573,20 +627,17 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, + case 3: /* Confirm mode */ + switch (pfl->cmd) { + case 0xe8: /* Block write */ +- if (cmd == 0xd0) { +- /* FIXME this is where we should write out the buffer */ ++ if ((cmd == 0xd0) && !(pfl->status & 0x10)) { ++ pflash_blk_write_flush(pfl); + pfl->wcycle = 0; + pfl->status |= 0x80; + } else { +- qemu_log_mask(LOG_UNIMP, +- "%s: Aborting write to buffer not implemented," +- " the data is already written to storage!\n" +- "Flash device reset into READ mode.\n", +- __func__); ++ pflash_blk_write_abort(pfl); + goto mode_read_array; + } + break; + default: ++ pflash_blk_write_abort(pfl); + goto error_flash; + } + break; +@@ -820,6 +871,9 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp) + pfl->cmd = 0x00; + pfl->status = 0x80; /* WSM ready */ + pflash_cfi01_fill_cfi_table(pfl); ++ ++ pfl->blk_bytes = g_malloc(pfl->writeblock_size); ++ pfl->blk_offset = -1; + } + + static void pflash_cfi01_system_reset(DeviceState *dev) +@@ -839,6 +893,8 @@ static void pflash_cfi01_system_reset(DeviceState *dev) + * This model deliberately ignores this delay. + */ + pfl->status = 0x80; ++ ++ pfl->blk_offset = -1; + } + + static Property pflash_cfi01_properties[] = { +diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c +index 2a99b28..6fa56f1 100644 +--- a/hw/block/pflash_cfi02.c ++++ b/hw/block/pflash_cfi02.c +@@ -546,7 +546,7 @@ static void pflash_write(void *opaque, hwaddr offset, uint64_t value, + } + goto reset_flash; + } +- trace_pflash_data_write(pfl->name, offset, width, value, 0); ++ trace_pflash_data_write(pfl->name, offset, width, value); + if (!pfl->ro) { + p = (uint8_t *)pfl->storage + offset; + if (pfl->be) { +diff --git a/hw/block/trace-events b/hw/block/trace-events +index bab21d3..cc9a9f2 100644 +--- a/hw/block/trace-events ++++ b/hw/block/trace-events +@@ -12,7 +12,8 @@ fdctrl_tc_pulse(int level) "TC pulse: %u" + pflash_chip_erase_invalid(const char *name, uint64_t offset) "%s: chip erase: invalid address 0x%" PRIx64 + pflash_chip_erase_start(const char *name) "%s: start chip erase" + pflash_data_read(const char *name, uint64_t offset, unsigned size, uint32_t value) "%s: data offset:0x%04"PRIx64" size:%u value:0x%04x" +-pflash_data_write(const char *name, uint64_t offset, unsigned size, uint32_t value, uint64_t counter) "%s: data offset:0x%04"PRIx64" size:%u value:0x%04x counter:0x%016"PRIx64 ++pflash_data_write(const char *name, uint64_t offset, unsigned size, uint32_t value) "%s: data offset:0x%04"PRIx64" size:%u value:0x%04x" ++pflash_data_write_block(const char *name, uint64_t offset, unsigned size, uint32_t value, uint64_t counter) "%s: data offset:0x%04"PRIx64" size:%u value:0x%04x counter:0x%016"PRIx64 + pflash_device_id(const char *name, uint16_t id) "%s: read device ID: 0x%04x" + pflash_device_info(const char *name, uint64_t offset) "%s: read device information offset:0x%04" PRIx64 + pflash_erase_complete(const char *name) "%s: sector erase complete" +@@ -32,7 +33,9 @@ pflash_unlock0_failed(const char *name, uint64_t offset, uint8_t cmd, uint16_t a + pflash_unlock1_failed(const char *name, uint64_t offset, uint8_t cmd) "%s: unlock0 failed 0x%" PRIx64 " 0x%02x" + pflash_unsupported_device_configuration(const char *name, uint8_t width, uint8_t max) "%s: unsupported device configuration: device_width:%d max_device_width:%d" + pflash_write(const char *name, const char *str) "%s: %s" +-pflash_write_block(const char *name, uint32_t value) "%s: block write: bytes:0x%x" ++pflash_write_block_start(const char *name, uint32_t value) "%s: block write start: bytes:0x%x" ++pflash_write_block_flush(const char *name) "%s: block write flush" ++pflash_write_block_abort(const char *name) "%s: block write abort" + pflash_write_block_erase(const char *name, uint64_t offset, uint64_t len) "%s: block erase offset:0x%" PRIx64 " bytes:0x%" PRIx64 + pflash_write_failed(const char *name, uint64_t offset, uint8_t cmd) "%s: command failed 0x%" PRIx64 " 0x%02x" + pflash_write_invalid(const char *name, uint8_t cmd) "%s: invalid write for command 0x%02x" +-- +1.8.3.1 + diff --git a/0042-migration-rdma-define-htonll-ntohll-only-if-not-pred.patch b/0042-migration-rdma-define-htonll-ntohll-only-if-not-pred.patch new file mode 100644 index 0000000000000000000000000000000000000000..71aaa0fd17985488506e2cdae23f23c6bee8d018 --- /dev/null +++ b/0042-migration-rdma-define-htonll-ntohll-only-if-not-pred.patch @@ -0,0 +1,50 @@ +From 3f675950dffd879ea35d387c0b6aa2136879d25f Mon Sep 17 00:00:00 2001 +From: Nick Briggs +Date: Thu, 11 Jan 2024 13:20:17 -0500 +Subject: [PATCH 047/293] migration/rdma: define htonll/ntohll only if not + predefined + +Solaris has #defines for htonll and ntohll which cause syntax errors +when compiling code that attempts to (re)define these functions.. + +Signed-off-by: Nick Briggs +Link: https://lore.kernel.org/r/65a04a7d.497ab3.3e7bef1f@gateway.sonic.net +Signed-off-by: Peter Xu +(cherry picked from commit 44ce1b5d2fc77343f6a318cb3de613336a240048) +Signed-off-by: Michael Tokarev +--- + migration/rdma.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/migration/rdma.c b/migration/rdma.c +index 04debab..4141c0b 100644 +--- a/migration/rdma.c ++++ b/migration/rdma.c +@@ -238,6 +238,7 @@ static const char *control_desc(unsigned int rdma_control) + return strs[rdma_control]; + } + ++#if !defined(htonll) + static uint64_t htonll(uint64_t v) + { + union { uint32_t lv[2]; uint64_t llv; } u; +@@ -245,13 +246,16 @@ static uint64_t htonll(uint64_t v) + u.lv[1] = htonl(v & 0xFFFFFFFFULL); + return u.llv; + } ++#endif + ++#if !defined(ntohll) + static uint64_t ntohll(uint64_t v) + { + union { uint32_t lv[2]; uint64_t llv; } u; + u.llv = v; + return ((uint64_t)ntohl(u.lv[0]) << 32) | (uint64_t) ntohl(u.lv[1]); + } ++#endif + + static void dest_block_to_network(RDMADestBlock *db) + { +-- +1.8.3.1 + diff --git a/0043-hw-scsi-esp-pci-use-correct-address-register-for-PCI.patch b/0043-hw-scsi-esp-pci-use-correct-address-register-for-PCI.patch new file mode 100644 index 0000000000000000000000000000000000000000..7a5f961e92bceea4785261c5f7a3f65308fd876d --- /dev/null +++ b/0043-hw-scsi-esp-pci-use-correct-address-register-for-PCI.patch @@ -0,0 +1,50 @@ +From 2d81285db113ed85eba43a746dcd65b8fa5ada4e Mon Sep 17 00:00:00 2001 +From: Mark Cave-Ayland +Date: Fri, 12 Jan 2024 13:15:26 +0000 +Subject: [PATCH 048/293] hw/scsi/esp-pci: use correct address register for PCI + DMA transfers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The current code in esp_pci_dma_memory_rw() sets the DMA address to the value +of the DMA_SPA (Starting Physical Address) register which is incorrect: this +means that for each callback from the SCSI layer the DMA address is set back +to the starting address. + +In the case where only a single SCSI callback occurs (currently for transfer +lengths < 128kB) this works fine, however for larger transfers the DMA address +wraps back to the initial starting address, corrupting the buffer holding the +data transferred to the guest. + +Fix esp_pci_dma_memory_rw() to use the DMA_WAC (Working Address Counter) for +the DMA address which is correctly incremented across multiple SCSI layer +transfers. + +Signed-off-by: Mark Cave-Ayland +Reviewed-by: Guenter Roeck +Tested-by: Guenter Roeck +Message-ID: <20240112131529.515642-2-mark.cave-ayland@ilande.co.uk> +Signed-off-by: Philippe Mathieu-Daudé +(cherry picked from commit 84a6835e004c257037492167d4f266dbb54dc33e) +Signed-off-by: Michael Tokarev +--- + hw/scsi/esp-pci.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/scsi/esp-pci.c b/hw/scsi/esp-pci.c +index 4e890db..ac5d752 100644 +--- a/hw/scsi/esp-pci.c ++++ b/hw/scsi/esp-pci.c +@@ -275,7 +275,7 @@ static void esp_pci_dma_memory_rw(PCIESPState *pci, uint8_t *buf, int len, + qemu_log_mask(LOG_UNIMP, "am53c974: MDL transfer not implemented\n"); + } + +- addr = pci->dma_regs[DMA_SPA]; ++ addr = pci->dma_regs[DMA_WAC]; + if (pci->dma_regs[DMA_WBC] < len) { + len = pci->dma_regs[DMA_WBC]; + } +-- +1.8.3.1 + diff --git a/0044-hw-scsi-esp-pci-generate-PCI-interrupt-from-separate.patch b/0044-hw-scsi-esp-pci-generate-PCI-interrupt-from-separate.patch new file mode 100644 index 0000000000000000000000000000000000000000..1b14aa0fc1308bf0ae0aa4c068af9d21ffcc4ace --- /dev/null +++ b/0044-hw-scsi-esp-pci-generate-PCI-interrupt-from-separate.patch @@ -0,0 +1,110 @@ +From d8e0533c94a7ddc481369742d73111531e2c38c4 Mon Sep 17 00:00:00 2001 +From: Mark Cave-Ayland +Date: Fri, 12 Jan 2024 13:15:27 +0000 +Subject: [PATCH 049/293] hw/scsi/esp-pci: generate PCI interrupt from separate + ESP and PCI sources +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The am53c974/dc390 PCI interrupt has two separate sources: the first is from the +internal ESP device, and the second is from the PCI DMA transfer logic. + +Update the ESP interrupt handler so that it sets DMA_STAT_SCSIINT rather than +driving the PCI IRQ directly, and introduce a new esp_pci_update_irq() function +to generate the correct PCI IRQ level. In particular this fixes spurious interrupts +being generated by setting DMA_STAT_DONE at the end of a transfer if DMA_CMD_INTE_D +isn't set in the DMA_CMD register. + +Signed-off-by: Mark Cave-Ayland +Reviewed-by: Guenter Roeck +Tested-by: Guenter Roeck +Message-ID: <20240112131529.515642-3-mark.cave-ayland@ilande.co.uk> +Signed-off-by: Philippe Mathieu-Daudé +(cherry picked from commit 6b41417d934b2640b7ccf893544d656eea92a2e7) +Signed-off-by: Michael Tokarev +--- + hw/scsi/esp-pci.c | 32 +++++++++++++++++++++++++++----- + 1 file changed, 27 insertions(+), 5 deletions(-) + +diff --git a/hw/scsi/esp-pci.c b/hw/scsi/esp-pci.c +index ac5d752..d29c8c2 100644 +--- a/hw/scsi/esp-pci.c ++++ b/hw/scsi/esp-pci.c +@@ -77,6 +77,29 @@ struct PCIESPState { + ESPState esp; + }; + ++static void esp_pci_update_irq(PCIESPState *pci) ++{ ++ int scsi_level = !!(pci->dma_regs[DMA_STAT] & DMA_STAT_SCSIINT); ++ int dma_level = (pci->dma_regs[DMA_CMD] & DMA_CMD_INTE_D) ? ++ !!(pci->dma_regs[DMA_STAT] & DMA_STAT_DONE) : 0; ++ int level = scsi_level || dma_level; ++ ++ pci_set_irq(PCI_DEVICE(pci), level); ++} ++ ++static void esp_irq_handler(void *opaque, int irq_num, int level) ++{ ++ PCIESPState *pci = PCI_ESP(opaque); ++ ++ if (level) { ++ pci->dma_regs[DMA_STAT] |= DMA_STAT_SCSIINT; ++ } else { ++ pci->dma_regs[DMA_STAT] &= ~DMA_STAT_SCSIINT; ++ } ++ ++ esp_pci_update_irq(pci); ++} ++ + static void esp_pci_handle_idle(PCIESPState *pci, uint32_t val) + { + ESPState *s = &pci->esp; +@@ -151,6 +174,7 @@ static void esp_pci_dma_write(PCIESPState *pci, uint32_t saddr, uint32_t val) + /* clear some bits on write */ + uint32_t mask = DMA_STAT_ERROR | DMA_STAT_ABORT | DMA_STAT_DONE; + pci->dma_regs[DMA_STAT] &= ~(val & mask); ++ esp_pci_update_irq(pci); + } + break; + default: +@@ -161,17 +185,14 @@ static void esp_pci_dma_write(PCIESPState *pci, uint32_t saddr, uint32_t val) + + static uint32_t esp_pci_dma_read(PCIESPState *pci, uint32_t saddr) + { +- ESPState *s = &pci->esp; + uint32_t val; + + val = pci->dma_regs[saddr]; + if (saddr == DMA_STAT) { +- if (s->rregs[ESP_RSTAT] & STAT_INT) { +- val |= DMA_STAT_SCSIINT; +- } + if (!(pci->sbac & SBAC_STATUS)) { + pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_ERROR | DMA_STAT_ABORT | + DMA_STAT_DONE); ++ esp_pci_update_irq(pci); + } + } + +@@ -350,6 +371,7 @@ static void esp_pci_command_complete(SCSIRequest *req, size_t resid) + esp_command_complete(req, resid); + pci->dma_regs[DMA_WBC] = 0; + pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE; ++ esp_pci_update_irq(pci); + } + + static const struct SCSIBusInfo esp_pci_scsi_info = { +@@ -386,7 +408,7 @@ static void esp_pci_scsi_realize(PCIDevice *dev, Error **errp) + "esp-io", 0x80); + + pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->io); +- s->irq = pci_allocate_irq(dev); ++ s->irq = qemu_allocate_irq(esp_irq_handler, pci, 0); + + scsi_bus_init(&s->bus, sizeof(s->bus), d, &esp_pci_scsi_info); + } +-- +1.8.3.1 + diff --git a/0045-hw-scsi-esp-pci-synchronise-setting-of-DMA_STAT_DONE.patch b/0045-hw-scsi-esp-pci-synchronise-setting-of-DMA_STAT_DONE.patch new file mode 100644 index 0000000000000000000000000000000000000000..5771485493fcfc23953826eb1e049d617b2419f7 --- /dev/null +++ b/0045-hw-scsi-esp-pci-synchronise-setting-of-DMA_STAT_DONE.patch @@ -0,0 +1,100 @@ +From 06a28b783bd406eacf452d9b0400719f3bc226c5 Mon Sep 17 00:00:00 2001 +From: Mark Cave-Ayland +Date: Fri, 12 Jan 2024 13:15:28 +0000 +Subject: [PATCH 050/293] hw/scsi/esp-pci: synchronise setting of DMA_STAT_DONE + with ESP completion interrupt +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The setting of DMA_STAT_DONE at the end of a DMA transfer can be configured to +generate an interrupt, however the Linux driver manually checks for DMA_STAT_DONE +being set and if it is, considers that a DMA transfer has completed. + +If DMA_STAT_DONE is set but the ESP device isn't indicating an interrupt then +the Linux driver considers this to be a spurious interrupt. However this can +occur in QEMU as there is a delay between the end of DMA transfer where +DMA_STAT_DONE is set, and the ESP device raising its completion interrupt. + +This appears to be an incorrect assumption in the Linux driver as the ESP and +PCI DMA interrupt sources are separate (and may not be raised exactly +together), however we can work around this by synchronising the setting of +DMA_STAT_DONE at the end of a DMA transfer with the ESP completion interrupt. + +In conjunction with the previous commit Linux is now able to correctly boot +from an am53c974 PCI SCSI device on the hppa C3700 machine without emitting +"iget: checksum invalid" and "Spurious irq, sreg=10" errors. + +Signed-off-by: Mark Cave-Ayland +Reviewed-by: Guenter Roeck +Tested-by: Guenter Roeck +Message-ID: <20240112131529.515642-4-mark.cave-ayland@ilande.co.uk> +Signed-off-by: Philippe Mathieu-Daudé +(cherry picked from commit 1e8e6644e063b20ad391140fae13d00ad7750b33) +Signed-off-by: Michael Tokarev +--- + hw/scsi/esp-pci.c | 28 +++++++++++++--------------- + 1 file changed, 13 insertions(+), 15 deletions(-) + +diff --git a/hw/scsi/esp-pci.c b/hw/scsi/esp-pci.c +index d29c8c2..b1bd43b 100644 +--- a/hw/scsi/esp-pci.c ++++ b/hw/scsi/esp-pci.c +@@ -93,6 +93,18 @@ static void esp_irq_handler(void *opaque, int irq_num, int level) + + if (level) { + pci->dma_regs[DMA_STAT] |= DMA_STAT_SCSIINT; ++ ++ /* ++ * If raising the ESP IRQ to indicate end of DMA transfer, set ++ * DMA_STAT_DONE at the same time. In theory this should be done in ++ * esp_pci_dma_memory_rw(), however there is a delay between setting ++ * DMA_STAT_DONE and the ESP IRQ arriving which is visible to the ++ * guest that can cause confusion e.g. Linux ++ */ ++ if ((pci->dma_regs[DMA_CMD] & DMA_CMD_MASK) == 0x3 && ++ pci->dma_regs[DMA_WBC] == 0) { ++ pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE; ++ } + } else { + pci->dma_regs[DMA_STAT] &= ~DMA_STAT_SCSIINT; + } +@@ -306,9 +318,6 @@ static void esp_pci_dma_memory_rw(PCIESPState *pci, uint8_t *buf, int len, + /* update status registers */ + pci->dma_regs[DMA_WBC] -= len; + pci->dma_regs[DMA_WAC] += len; +- if (pci->dma_regs[DMA_WBC] == 0) { +- pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE; +- } + } + + static void esp_pci_dma_memory_read(void *opaque, uint8_t *buf, int len) +@@ -363,24 +372,13 @@ static const VMStateDescription vmstate_esp_pci_scsi = { + } + }; + +-static void esp_pci_command_complete(SCSIRequest *req, size_t resid) +-{ +- ESPState *s = req->hba_private; +- PCIESPState *pci = container_of(s, PCIESPState, esp); +- +- esp_command_complete(req, resid); +- pci->dma_regs[DMA_WBC] = 0; +- pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE; +- esp_pci_update_irq(pci); +-} +- + static const struct SCSIBusInfo esp_pci_scsi_info = { + .tcq = false, + .max_target = ESP_MAX_DEVS, + .max_lun = 7, + + .transfer_data = esp_transfer_data, +- .complete = esp_pci_command_complete, ++ .complete = esp_command_complete, + .cancel = esp_request_cancelled, + }; + +-- +1.8.3.1 + diff --git a/0046-hw-scsi-esp-pci-set-DMA_STAT_BCMBLT-when-BLAST-comma.patch b/0046-hw-scsi-esp-pci-set-DMA_STAT_BCMBLT-when-BLAST-comma.patch new file mode 100644 index 0000000000000000000000000000000000000000..591ef5b087a2a15ff520cf3762f1e93cdc7e3bdf --- /dev/null +++ b/0046-hw-scsi-esp-pci-set-DMA_STAT_BCMBLT-when-BLAST-comma.patch @@ -0,0 +1,43 @@ +From 164e6f7d66adeaae9db54cefec9fca8941fe1e10 Mon Sep 17 00:00:00 2001 +From: Mark Cave-Ayland +Date: Fri, 12 Jan 2024 13:15:29 +0000 +Subject: [PATCH 051/293] hw/scsi/esp-pci: set DMA_STAT_BCMBLT when BLAST + command issued +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Even though the BLAST command isn't fully implemented in QEMU, the DMA_STAT_BCMBLT +bit should be set after the command has been issued to indicate that the command +has completed. + +This fixes an issue with the DC390 DOS driver which issues the BLAST command as +part of its normal error recovery routine at startup, and otherwise sits in a +tight loop waiting for DMA_STAT_BCMBLT to be set before continuing. + +Signed-off-by: Mark Cave-Ayland +Reviewed-by: Guenter Roeck +Tested-by: Guenter Roeck +Message-ID: <20240112131529.515642-5-mark.cave-ayland@ilande.co.uk> +Signed-off-by: Philippe Mathieu-Daudé +(cherry picked from commit c2d7de557d19ec76eb83b87b6bf77c8114e2f183) +Signed-off-by: Michael Tokarev +--- + hw/scsi/esp-pci.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/hw/scsi/esp-pci.c b/hw/scsi/esp-pci.c +index b1bd43b..51f0157 100644 +--- a/hw/scsi/esp-pci.c ++++ b/hw/scsi/esp-pci.c +@@ -124,6 +124,7 @@ static void esp_pci_handle_blast(PCIESPState *pci, uint32_t val) + { + trace_esp_pci_dma_blast(val); + qemu_log_mask(LOG_UNIMP, "am53c974: cmd BLAST not implemented\n"); ++ pci->dma_regs[DMA_STAT] |= DMA_STAT_BCMBLT; + } + + static void esp_pci_handle_abort(PCIESPState *pci, uint32_t val) +-- +1.8.3.1 + diff --git a/0047-s390x-pci-avoid-double-enable-disable-of-aif.patch b/0047-s390x-pci-avoid-double-enable-disable-of-aif.patch new file mode 100644 index 0000000000000000000000000000000000000000..42cc6c8735e2474b93b7eae400f34a6cefb210d0 --- /dev/null +++ b/0047-s390x-pci-avoid-double-enable-disable-of-aif.patch @@ -0,0 +1,91 @@ +From 9d6dd12b1d60f26fcefeb35a5fc27a6bdce19b65 Mon Sep 17 00:00:00 2001 +From: Matthew Rosato +Date: Thu, 18 Jan 2024 13:51:49 -0500 +Subject: [PATCH 052/293] s390x/pci: avoid double enable/disable of aif +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Use a flag to keep track of whether AIF is currently enabled. This can be +used to avoid enabling/disabling AIF multiple times as well as to determine +whether or not it should be disabled during reset processing. + +Fixes: d0bc7091c2 ("s390x/pci: enable adapter event notification for interpreted devices") +Reported-by: Cédric Le Goater +Reviewed-by: Eric Farman +Signed-off-by: Matthew Rosato +Message-ID: <20240118185151.265329-2-mjrosato@linux.ibm.com> +Reviewed-by: Cédric Le Goater +Signed-off-by: Thomas Huth +(cherry picked from commit 07b2c8e034d80ff92e202405c494d2ff80fcf848) +Signed-off-by: Michael Tokarev +--- + hw/s390x/s390-pci-kvm.c | 25 +++++++++++++++++++++++-- + include/hw/s390x/s390-pci-bus.h | 1 + + 2 files changed, 24 insertions(+), 2 deletions(-) + +diff --git a/hw/s390x/s390-pci-kvm.c b/hw/s390x/s390-pci-kvm.c +index ff41e41..1ee5104 100644 +--- a/hw/s390x/s390-pci-kvm.c ++++ b/hw/s390x/s390-pci-kvm.c +@@ -27,6 +27,7 @@ bool s390_pci_kvm_interp_allowed(void) + + int s390_pci_kvm_aif_enable(S390PCIBusDevice *pbdev, ZpciFib *fib, bool assist) + { ++ int rc; + struct kvm_s390_zpci_op args = { + .fh = pbdev->fh, + .op = KVM_S390_ZPCIOP_REG_AEN, +@@ -38,15 +39,35 @@ int s390_pci_kvm_aif_enable(S390PCIBusDevice *pbdev, ZpciFib *fib, bool assist) + .u.reg_aen.flags = (assist) ? 0 : KVM_S390_ZPCIOP_REGAEN_HOST + }; + +- return kvm_vm_ioctl(kvm_state, KVM_S390_ZPCI_OP, &args); ++ if (pbdev->aif) { ++ return -EINVAL; ++ } ++ ++ rc = kvm_vm_ioctl(kvm_state, KVM_S390_ZPCI_OP, &args); ++ if (rc == 0) { ++ pbdev->aif = true; ++ } ++ ++ return rc; + } + + int s390_pci_kvm_aif_disable(S390PCIBusDevice *pbdev) + { ++ int rc; ++ + struct kvm_s390_zpci_op args = { + .fh = pbdev->fh, + .op = KVM_S390_ZPCIOP_DEREG_AEN + }; + +- return kvm_vm_ioctl(kvm_state, KVM_S390_ZPCI_OP, &args); ++ if (!pbdev->aif) { ++ return -EINVAL; ++ } ++ ++ rc = kvm_vm_ioctl(kvm_state, KVM_S390_ZPCI_OP, &args); ++ if (rc == 0) { ++ pbdev->aif = false; ++ } ++ ++ return rc; + } +diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h +index b1bdbea..435e788 100644 +--- a/include/hw/s390x/s390-pci-bus.h ++++ b/include/hw/s390x/s390-pci-bus.h +@@ -361,6 +361,7 @@ struct S390PCIBusDevice { + bool unplug_requested; + bool interp; + bool forwarding_assist; ++ bool aif; + QTAILQ_ENTRY(S390PCIBusDevice) link; + }; + +-- +1.8.3.1 + diff --git a/0048-s390x-pci-refresh-fh-before-disabling-aif.patch b/0048-s390x-pci-refresh-fh-before-disabling-aif.patch new file mode 100644 index 0000000000000000000000000000000000000000..1019f5d639ee03e892f7f0c450d401c6e3a22a7a --- /dev/null +++ b/0048-s390x-pci-refresh-fh-before-disabling-aif.patch @@ -0,0 +1,56 @@ +From c2985e38d803e62091f77ecb372c9c5fdf997239 Mon Sep 17 00:00:00 2001 +From: Matthew Rosato +Date: Thu, 18 Jan 2024 13:51:50 -0500 +Subject: [PATCH 053/293] s390x/pci: refresh fh before disabling aif +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Typically we refresh the host fh during CLP enable, however it's possible +that the device goes through multiple reset events before the guest +performs another CLP enable. Let's handle this for now by refreshing the +host handle from vfio before disabling aif. + +Fixes: 03451953c7 ("s390x/pci: reset ISM passthrough devices on shutdown and system reset") +Reported-by: Cédric Le Goater +Reviewed-by: Eric Farman +Signed-off-by: Matthew Rosato +Message-ID: <20240118185151.265329-3-mjrosato@linux.ibm.com> +Reviewed-by: Cédric Le Goater +Signed-off-by: Thomas Huth +(cherry picked from commit 30e35258e25c75c9d799c34fd89afcafffb37084) +Signed-off-by: Michael Tokarev +--- + hw/s390x/s390-pci-kvm.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/hw/s390x/s390-pci-kvm.c b/hw/s390x/s390-pci-kvm.c +index 1ee5104..9eef4fc 100644 +--- a/hw/s390x/s390-pci-kvm.c ++++ b/hw/s390x/s390-pci-kvm.c +@@ -18,6 +18,7 @@ + #include "hw/s390x/s390-pci-bus.h" + #include "hw/s390x/s390-pci-kvm.h" + #include "hw/s390x/s390-pci-inst.h" ++#include "hw/s390x/s390-pci-vfio.h" + #include "cpu_models.h" + + bool s390_pci_kvm_interp_allowed(void) +@@ -64,6 +65,14 @@ int s390_pci_kvm_aif_disable(S390PCIBusDevice *pbdev) + return -EINVAL; + } + ++ /* ++ * The device may have already been reset but we still want to relinquish ++ * the guest ISC, so always be sure to use an up-to-date host fh. ++ */ ++ if (!s390_pci_get_host_fh(pbdev, &args.fh)) { ++ return -EPERM; ++ } ++ + rc = kvm_vm_ioctl(kvm_state, KVM_S390_ZPCI_OP, &args); + if (rc == 0) { + pbdev->aif = false; +-- +1.8.3.1 + diff --git a/0049-s390x-pci-drive-ISM-reset-from-subsystem-reset.patch b/0049-s390x-pci-drive-ISM-reset-from-subsystem-reset.patch new file mode 100644 index 0000000000000000000000000000000000000000..bc3014f41a675878b0501c103e30d3bd7dc20a7e --- /dev/null +++ b/0049-s390x-pci-drive-ISM-reset-from-subsystem-reset.patch @@ -0,0 +1,122 @@ +From 003d0c7eb449231b0cd9870e295f04840a9b5c05 Mon Sep 17 00:00:00 2001 +From: Matthew Rosato +Date: Thu, 18 Jan 2024 13:51:51 -0500 +Subject: [PATCH 054/293] s390x/pci: drive ISM reset from subsystem reset +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +ISM devices are sensitive to manipulation of the IOMMU, so the ISM device +needs to be reset before the vfio-pci device is reset (triggering a full +UNMAP). In order to ensure this occurs, trigger ISM device resets from +subsystem_reset before triggering the PCI bus reset (which will also +trigger vfio-pci reset). This only needs to be done for ISM devices +which were enabled for use by the guest. +Further, ensure that AIF is disabled as part of the reset event. + +Fixes: ef1535901a ("s390x: do a subsystem reset before the unprotect on reboot") +Fixes: 03451953c7 ("s390x/pci: reset ISM passthrough devices on shutdown and system reset") +Reported-by: Cédric Le Goater +Signed-off-by: Matthew Rosato +Message-ID: <20240118185151.265329-4-mjrosato@linux.ibm.com> +Reviewed-by: Eric Farman +Reviewed-by: Cédric Le Goater +Signed-off-by: Thomas Huth +(cherry picked from commit 68c691ca99a2538d6a53a70ce8a9ce06ee307ff1) +Signed-off-by: Michael Tokarev +--- + hw/s390x/s390-pci-bus.c | 26 +++++++++++++++++--------- + hw/s390x/s390-virtio-ccw.c | 8 ++++++++ + include/hw/s390x/s390-pci-bus.h | 1 + + 3 files changed, 26 insertions(+), 9 deletions(-) + +diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c +index 347580e..3e57d5f 100644 +--- a/hw/s390x/s390-pci-bus.c ++++ b/hw/s390x/s390-pci-bus.c +@@ -151,20 +151,12 @@ static void s390_pci_shutdown_notifier(Notifier *n, void *opaque) + pci_device_reset(pbdev->pdev); + } + +-static void s390_pci_reset_cb(void *opaque) +-{ +- S390PCIBusDevice *pbdev = opaque; +- +- pci_device_reset(pbdev->pdev); +-} +- + static void s390_pci_perform_unplug(S390PCIBusDevice *pbdev) + { + HotplugHandler *hotplug_ctrl; + + if (pbdev->pft == ZPCI_PFT_ISM) { + notifier_remove(&pbdev->shutdown_notifier); +- qemu_unregister_reset(s390_pci_reset_cb, pbdev); + } + + /* Unplug the PCI device */ +@@ -1132,7 +1124,6 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev, + if (pbdev->pft == ZPCI_PFT_ISM) { + pbdev->shutdown_notifier.notify = s390_pci_shutdown_notifier; + qemu_register_shutdown_notifier(&pbdev->shutdown_notifier); +- qemu_register_reset(s390_pci_reset_cb, pbdev); + } + } else { + pbdev->fh |= FH_SHM_EMUL; +@@ -1279,6 +1270,23 @@ static void s390_pci_enumerate_bridge(PCIBus *bus, PCIDevice *pdev, + pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, s->bus_no, 1); + } + ++void s390_pci_ism_reset(void) ++{ ++ S390pciState *s = s390_get_phb(); ++ ++ S390PCIBusDevice *pbdev, *next; ++ ++ /* Trigger reset event for each passthrough ISM device currently in-use */ ++ QTAILQ_FOREACH_SAFE(pbdev, &s->zpci_devs, link, next) { ++ if (pbdev->interp && pbdev->pft == ZPCI_PFT_ISM && ++ pbdev->fh & FH_MASK_ENABLE) { ++ s390_pci_kvm_aif_disable(pbdev); ++ ++ pci_device_reset(pbdev->pdev); ++ } ++ } ++} ++ + static void s390_pcihost_reset(DeviceState *dev) + { + S390pciState *s = S390_PCI_HOST_BRIDGE(dev); +diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c +index 7262725..2d6b866 100644 +--- a/hw/s390x/s390-virtio-ccw.c ++++ b/hw/s390x/s390-virtio-ccw.c +@@ -118,6 +118,14 @@ static void subsystem_reset(void) + DeviceState *dev; + int i; + ++ /* ++ * ISM firmware is sensitive to unexpected changes to the IOMMU, which can ++ * occur during reset of the vfio-pci device (unmap of entire aperture). ++ * Ensure any passthrough ISM devices are reset now, while CPUs are paused ++ * but before vfio-pci cleanup occurs. ++ */ ++ s390_pci_ism_reset(); ++ + for (i = 0; i < ARRAY_SIZE(reset_dev_types); i++) { + dev = DEVICE(object_resolve_path_type("", reset_dev_types[i], NULL)); + if (dev) { +diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h +index 435e788..2c43ea1 100644 +--- a/include/hw/s390x/s390-pci-bus.h ++++ b/include/hw/s390x/s390-pci-bus.h +@@ -401,5 +401,6 @@ S390PCIBusDevice *s390_pci_find_dev_by_target(S390pciState *s, + const char *target); + S390PCIBusDevice *s390_pci_find_next_avail_dev(S390pciState *s, + S390PCIBusDevice *pbdev); ++void s390_pci_ism_reset(void); + + #endif +-- +1.8.3.1 + diff --git a/0050-acpi-tests-avocado-bits-wait-for-200-seconds-for-SHU.patch b/0050-acpi-tests-avocado-bits-wait-for-200-seconds-for-SHU.patch new file mode 100644 index 0000000000000000000000000000000000000000..451a598f23c4da31a0e5c9f4038b493b7188504d --- /dev/null +++ b/0050-acpi-tests-avocado-bits-wait-for-200-seconds-for-SHU.patch @@ -0,0 +1,65 @@ +From 5e4e438f9d602f8089e88252e58c523d77947ee1 Mon Sep 17 00:00:00 2001 +From: Ani Sinha +Date: Wed, 17 Jan 2024 09:55:56 +0530 +Subject: [PATCH 055/293] acpi/tests/avocado/bits: wait for 200 seconds for + SHUTDOWN event from bits VM +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +By default, the timeout to receive any specified event from the QEMU VM is 60 +seconds set by the python avocado test framework. Please see event_wait() and +events_wait() in python/qemu/machine/machine.py. If the matching event is not +triggered within that interval, an asyncio.TimeoutError is generated. Since the +timeout for the bits avocado test is 200 secs, we need to make event_wait() +timeout of the same value as well so that an early timeout is not triggered by +the avocado framework. + +CC: peter.maydell@linaro.org +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2077 +Signed-off-by: Ani Sinha +Reviewed-by: Daniel P. Berrangé +Message-id: 20240117042556.3360190-1-anisinha@redhat.com +Signed-off-by: Peter Maydell +(cherry picked from commit 7ef4c41e91d59d72a3b8bc022a6cb3e81787a50a) +Signed-off-by: Michael Tokarev +--- + tests/avocado/acpi-bits.py | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/tests/avocado/acpi-bits.py b/tests/avocado/acpi-bits.py +index 68b9e98..efe4f52 100644 +--- a/tests/avocado/acpi-bits.py ++++ b/tests/avocado/acpi-bits.py +@@ -54,6 +54,8 @@ + deps = ["xorriso", "mformat"] # dependent tools needed in the test setup/box. + supported_platforms = ['x86_64'] # supported test platforms. + ++# default timeout of 120 secs is sometimes not enough for bits test. ++BITS_TIMEOUT = 200 + + def which(tool): + """ looks up the full path for @tool, returns None if not found +@@ -133,7 +135,7 @@ class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes + + """ + # in slower systems the test can take as long as 3 minutes to complete. +- timeout = 200 ++ timeout = BITS_TIMEOUT + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) +@@ -400,7 +402,8 @@ def test_acpi_smbios_bits(self): + + # biosbits has been configured to run all the specified test suites + # in batch mode and then automatically initiate a vm shutdown. +- # Rely on avocado's unit test timeout. +- self._vm.event_wait('SHUTDOWN') ++ # Set timeout to BITS_TIMEOUT for SHUTDOWN event from bits VM at par ++ # with the avocado test timeout. ++ self._vm.event_wait('SHUTDOWN', timeout=BITS_TIMEOUT) + self._vm.wait(timeout=None) + self.parse_log() +-- +1.8.3.1 + diff --git a/0051-accel-tcg-Revert-mapping-of-PCREL-translation-block-.patch b/0051-accel-tcg-Revert-mapping-of-PCREL-translation-block-.patch new file mode 100644 index 0000000000000000000000000000000000000000..7eabf694f156d9af2382873d197b869a71507876 --- /dev/null +++ b/0051-accel-tcg-Revert-mapping-of-PCREL-translation-block-.patch @@ -0,0 +1,91 @@ +From ef74024b76bf285e247add8538c11cb3c7399a1a Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Tue, 23 Jan 2024 12:33:31 +0100 +Subject: [PATCH 056/293] accel/tcg: Revert mapping of PCREL translation block + to multiple virtual addresses + +This is causing regressions that have not been analyzed yet. Revert the +change on stable branches. + +Cc: qemu-stable@nongnu.org +Cc: Michael Tokarev +Related: https://gitlab.com/qemu-project/qemu/-/issues/2092 +Signed-off-by: Paolo Bonzini +Signed-off-by: Michael Tokarev +--- + accel/tcg/cpu-exec.c | 4 ++-- + accel/tcg/tb-maint.c | 6 +++--- + accel/tcg/translate-all.c | 4 +--- + 3 files changed, 6 insertions(+), 8 deletions(-) + +diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c +index c938eb9..6a4af14 100644 +--- a/accel/tcg/cpu-exec.c ++++ b/accel/tcg/cpu-exec.c +@@ -183,7 +183,7 @@ static bool tb_lookup_cmp(const void *p, const void *d) + const TranslationBlock *tb = p; + const struct tb_desc *desc = d; + +- if ((tb_cflags(tb) & CF_PCREL || tb->pc == desc->pc) && ++ if (tb->pc == desc->pc && + tb_page_addr0(tb) == desc->page_addr0 && + tb->cs_base == desc->cs_base && + tb->flags == desc->flags && +@@ -233,7 +233,7 @@ static TranslationBlock *tb_htable_lookup(CPUState *cpu, vaddr pc, + return NULL; + } + desc.page_addr0 = phys_pc; +- h = tb_hash_func(phys_pc, (cflags & CF_PCREL ? 0 : pc), ++ h = tb_hash_func(phys_pc, pc, + flags, cs_base, cflags); + return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp); + } +diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c +index 3d2a896..0d069a0 100644 +--- a/accel/tcg/tb-maint.c ++++ b/accel/tcg/tb-maint.c +@@ -47,7 +47,7 @@ static bool tb_cmp(const void *ap, const void *bp) + const TranslationBlock *a = ap; + const TranslationBlock *b = bp; + +- return ((tb_cflags(a) & CF_PCREL || a->pc == b->pc) && ++ return (a->pc == b->pc && + a->cs_base == b->cs_base && + a->flags == b->flags && + (tb_cflags(a) & ~CF_INVALID) == (tb_cflags(b) & ~CF_INVALID) && +@@ -916,7 +916,7 @@ static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list) + + /* remove the TB from the hash list */ + phys_pc = tb_page_addr0(tb); +- h = tb_hash_func(phys_pc, (orig_cflags & CF_PCREL ? 0 : tb->pc), ++ h = tb_hash_func(phys_pc, tb->pc, + tb->flags, tb->cs_base, orig_cflags); + if (!qht_remove(&tb_ctx.htable, tb, h)) { + return; +@@ -983,7 +983,7 @@ TranslationBlock *tb_link_page(TranslationBlock *tb) + tb_record(tb); + + /* add in the hash table */ +- h = tb_hash_func(tb_page_addr0(tb), (tb->cflags & CF_PCREL ? 0 : tb->pc), ++ h = tb_hash_func(tb_page_addr0(tb), tb->pc, + tb->flags, tb->cs_base, tb->cflags); + qht_insert(&tb_ctx.htable, tb, h, &existing_tb); + +diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c +index 79a88f5..c1708af 100644 +--- a/accel/tcg/translate-all.c ++++ b/accel/tcg/translate-all.c +@@ -327,9 +327,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, + + gen_code_buf = tcg_ctx->code_gen_ptr; + tb->tc.ptr = tcg_splitwx_to_rx(gen_code_buf); +- if (!(cflags & CF_PCREL)) { +- tb->pc = pc; +- } ++ tb->pc = pc; + tb->cs_base = cs_base; + tb->flags = flags; + tb->cflags = cflags; +-- +1.8.3.1 + diff --git a/0052-tcg-s390x-Fix-encoding-of-VRIc-VRSa-VRSc-insns.patch b/0052-tcg-s390x-Fix-encoding-of-VRIc-VRSa-VRSc-insns.patch new file mode 100644 index 0000000000000000000000000000000000000000..57a9c09e270bceddf20c351b34b81cf6e52d3b6d --- /dev/null +++ b/0052-tcg-s390x-Fix-encoding-of-VRIc-VRSa-VRSc-insns.patch @@ -0,0 +1,62 @@ +From 005d7236dba9434afdbcb8e2e174364cd330a2c4 Mon Sep 17 00:00:00 2001 +From: Richard Henderson +Date: Wed, 17 Jan 2024 21:13:35 +0000 +Subject: [PATCH 057/293] tcg/s390x: Fix encoding of VRIc, VRSa, VRSc insns + +While the format names the second vector register 'v3', +it is still in the second position (bits 12-15) and +the argument to RXB must match. + +Example error: + - e7 00 00 10 2a 33 verllf %v16,%v0,16 + + e7 00 00 10 2c 33 verllf %v16,%v16,16 + +Cc: qemu-stable@nongnu.org +Reported-by: Michael Tokarev +Fixes: 22cb37b4172 ("tcg/s390x: Implement vector shift operations") +Fixes: 79cada8693d ("tcg/s390x: Implement tcg_out_dup*_vec") +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2054 +Reviewed-by: Thomas Huth +Tested-by: Michael Tokarev +Message-Id: <20240117213646.159697-2-richard.henderson@linaro.org> +Signed-off-by: Richard Henderson +(cherry picked from commit c1ddc18f37108498f45d57afd6bf33a23b703648) +Signed-off-by: Michael Tokarev +--- + tcg/s390x/tcg-target.c.inc | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc +index fbee43d..7f6b84a 100644 +--- a/tcg/s390x/tcg-target.c.inc ++++ b/tcg/s390x/tcg-target.c.inc +@@ -683,7 +683,7 @@ static void tcg_out_insn_VRIc(TCGContext *s, S390Opcode op, + tcg_debug_assert(is_vector_reg(v3)); + tcg_out16(s, (op & 0xff00) | ((v1 & 0xf) << 4) | (v3 & 0xf)); + tcg_out16(s, i2); +- tcg_out16(s, (op & 0x00ff) | RXB(v1, 0, v3, 0) | (m4 << 12)); ++ tcg_out16(s, (op & 0x00ff) | RXB(v1, v3, 0, 0) | (m4 << 12)); + } + + static void tcg_out_insn_VRRa(TCGContext *s, S390Opcode op, +@@ -738,7 +738,7 @@ static void tcg_out_insn_VRSa(TCGContext *s, S390Opcode op, TCGReg v1, + tcg_debug_assert(is_vector_reg(v3)); + tcg_out16(s, (op & 0xff00) | ((v1 & 0xf) << 4) | (v3 & 0xf)); + tcg_out16(s, b2 << 12 | d2); +- tcg_out16(s, (op & 0x00ff) | RXB(v1, 0, v3, 0) | (m4 << 12)); ++ tcg_out16(s, (op & 0x00ff) | RXB(v1, v3, 0, 0) | (m4 << 12)); + } + + static void tcg_out_insn_VRSb(TCGContext *s, S390Opcode op, TCGReg v1, +@@ -762,7 +762,7 @@ static void tcg_out_insn_VRSc(TCGContext *s, S390Opcode op, TCGReg r1, + tcg_debug_assert(is_vector_reg(v3)); + tcg_out16(s, (op & 0xff00) | (r1 << 4) | (v3 & 0xf)); + tcg_out16(s, b2 << 12 | d2); +- tcg_out16(s, (op & 0x00ff) | RXB(0, 0, v3, 0) | (m4 << 12)); ++ tcg_out16(s, (op & 0x00ff) | RXB(0, v3, 0, 0) | (m4 << 12)); + } + + static void tcg_out_insn_VRX(TCGContext *s, S390Opcode op, TCGReg v1, +-- +1.8.3.1 + diff --git a/0053-coroutine-ucontext-Save-fake-stack-for-pooled-corout.patch b/0053-coroutine-ucontext-Save-fake-stack-for-pooled-corout.patch new file mode 100644 index 0000000000000000000000000000000000000000..4008edfb5957c5b68c83dc5d38343c9810421bb0 --- /dev/null +++ b/0053-coroutine-ucontext-Save-fake-stack-for-pooled-corout.patch @@ -0,0 +1,108 @@ +From f413f9fcc923083a7db038e32964148cf74134e6 Mon Sep 17 00:00:00 2001 +From: Akihiko Odaki +Date: Wed, 17 Jan 2024 16:06:02 +0900 +Subject: [PATCH 058/293] coroutine-ucontext: Save fake stack for pooled + coroutine +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Coroutine may be pooled even after COROUTINE_TERMINATE if +CONFIG_COROUTINE_POOL is enabled and fake stack should be saved in +such a case to keep AddressSanitizerUseAfterReturn working. Even worse, +I'm seeing stack corruption without fake stack being saved. + +Signed-off-by: Akihiko Odaki +Reviewed-by: Marc-André Lureau +Signed-off-by: Stefan Hajnoczi +Message-ID: <20240117-asan-v2-1-26f9e1ea6e72@daynix.com> +(cherry picked from commit d9945ccda08ef83b09ac7725b6ee2d1959f2c0c0) +Signed-off-by: Michael Tokarev +--- + util/coroutine-ucontext.c | 35 ++++++++++++++++++++++++++--------- + 1 file changed, 26 insertions(+), 9 deletions(-) + +diff --git a/util/coroutine-ucontext.c b/util/coroutine-ucontext.c +index 7b304c7..8ef603d 100644 +--- a/util/coroutine-ucontext.c ++++ b/util/coroutine-ucontext.c +@@ -119,13 +119,11 @@ void finish_switch_fiber(void *fake_stack_save) + + /* always_inline is required to avoid TSan runtime fatal errors. */ + static inline __attribute__((always_inline)) +-void start_switch_fiber_asan(CoroutineAction action, void **fake_stack_save, ++void start_switch_fiber_asan(void **fake_stack_save, + const void *bottom, size_t size) + { + #ifdef CONFIG_ASAN +- __sanitizer_start_switch_fiber( +- action == COROUTINE_TERMINATE ? NULL : fake_stack_save, +- bottom, size); ++ __sanitizer_start_switch_fiber(fake_stack_save, bottom, size); + #endif + } + +@@ -165,7 +163,7 @@ static void coroutine_trampoline(int i0, int i1) + if (!sigsetjmp(self->env, 0)) { + CoroutineUContext *leaderp = get_ptr_leader(); + +- start_switch_fiber_asan(COROUTINE_YIELD, &fake_stack_save, ++ start_switch_fiber_asan(&fake_stack_save, + leaderp->stack, leaderp->stack_size); + start_switch_fiber_tsan(&fake_stack_save, self, true); /* true=caller */ + siglongjmp(*(sigjmp_buf *)co->entry_arg, 1); +@@ -226,8 +224,7 @@ Coroutine *qemu_coroutine_new(void) + + /* swapcontext() in, siglongjmp() back out */ + if (!sigsetjmp(old_env, 0)) { +- start_switch_fiber_asan(COROUTINE_YIELD, &fake_stack_save, co->stack, +- co->stack_size); ++ start_switch_fiber_asan(&fake_stack_save, co->stack, co->stack_size); + start_switch_fiber_tsan(&fake_stack_save, + co, false); /* false=not caller */ + +@@ -269,10 +266,28 @@ static inline void valgrind_stack_deregister(CoroutineUContext *co) + #endif + #endif + ++#if defined(CONFIG_ASAN) && defined(CONFIG_COROUTINE_POOL) ++static void coroutine_fn terminate_asan(void *opaque) ++{ ++ CoroutineUContext *to = DO_UPCAST(CoroutineUContext, base, opaque); ++ ++ set_current(opaque); ++ start_switch_fiber_asan(NULL, to->stack, to->stack_size); ++ G_STATIC_ASSERT(!IS_ENABLED(CONFIG_TSAN)); ++ siglongjmp(to->env, COROUTINE_ENTER); ++} ++#endif ++ + void qemu_coroutine_delete(Coroutine *co_) + { + CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_); + ++#if defined(CONFIG_ASAN) && defined(CONFIG_COROUTINE_POOL) ++ co_->entry_arg = qemu_coroutine_self(); ++ co_->entry = terminate_asan; ++ qemu_coroutine_switch(co_->entry_arg, co_, COROUTINE_ENTER); ++#endif ++ + #ifdef CONFIG_VALGRIND_H + valgrind_stack_deregister(co); + #endif +@@ -305,8 +320,10 @@ qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, + + ret = sigsetjmp(from->env, 0); + if (ret == 0) { +- start_switch_fiber_asan(action, &fake_stack_save, to->stack, +- to->stack_size); ++ start_switch_fiber_asan(IS_ENABLED(CONFIG_COROUTINE_POOL) || ++ action != COROUTINE_TERMINATE ? ++ &fake_stack_save : NULL, ++ to->stack, to->stack_size); + start_switch_fiber_tsan(&fake_stack_save, + to, false); /* false=not caller */ + siglongjmp(to->env, action); +-- +1.8.3.1 + diff --git a/0054-block-io-clear-BDRV_BLOCK_RECURSE-flag-after-recursi.patch b/0054-block-io-clear-BDRV_BLOCK_RECURSE-flag-after-recursi.patch new file mode 100644 index 0000000000000000000000000000000000000000..35dba83db3e1c83a625929bfdf09a53e486d9b36 --- /dev/null +++ b/0054-block-io-clear-BDRV_BLOCK_RECURSE-flag-after-recursi.patch @@ -0,0 +1,109 @@ +From 99dd4a1563022338229791df41b42e320a38581b Mon Sep 17 00:00:00 2001 +From: Fiona Ebner +Date: Tue, 16 Jan 2024 16:48:39 +0100 +Subject: [PATCH 059/293] block/io: clear BDRV_BLOCK_RECURSE flag after + recursing in bdrv_co_block_status + +Using fleecing backup like in [0] on a qcow2 image (with metadata +preallocation) can lead to the following assertion failure: + +> bdrv_co_do_block_status: Assertion `!(ret & BDRV_BLOCK_ZERO)' failed. + +In the reproducer [0], it happens because the BDRV_BLOCK_RECURSE flag +will be set by the qcow2 driver, so the caller will recursively check +the file child. Then the BDRV_BLOCK_ZERO set too. Later up the call +chain, in bdrv_co_do_block_status() for the snapshot-access driver, +the assertion failure will happen, because both flags are set. + +To fix it, clear the recurse flag after the recursive check was done. + +In detail: + +> #0 qcow2_co_block_status + +Returns 0x45 = BDRV_BLOCK_RECURSE | BDRV_BLOCK_DATA | +BDRV_BLOCK_OFFSET_VALID. + +> #1 bdrv_co_do_block_status + +Because of the data flag, bdrv_co_do_block_status() will now also set +BDRV_BLOCK_ALLOCATED. Because of the recurse flag, +bdrv_co_do_block_status() for the bdrv_file child will be called, +which returns 0x16 = BDRV_BLOCK_ALLOCATED | BDRV_BLOCK_OFFSET_VALID | +BDRV_BLOCK_ZERO. Now the return value inherits the zero flag. + +Returns 0x57 = BDRV_BLOCK_RECURSE | BDRV_BLOCK_DATA | +BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_ALLOCATED | BDRV_BLOCK_ZERO. + +> #2 bdrv_co_common_block_status_above +> #3 bdrv_co_block_status_above +> #4 bdrv_co_block_status +> #5 cbw_co_snapshot_block_status +> #6 bdrv_co_snapshot_block_status +> #7 snapshot_access_co_block_status +> #8 bdrv_co_do_block_status + +Return value is propagated all the way up to here, where the assertion +failure happens, because BDRV_BLOCK_RECURSE and BDRV_BLOCK_ZERO are +both set. + +> #9 bdrv_co_common_block_status_above +> #10 bdrv_co_block_status_above +> #11 block_copy_block_status +> #12 block_copy_dirty_clusters +> #13 block_copy_common +> #14 block_copy_async_co_entry +> #15 coroutine_trampoline + +[0]: + +> #!/bin/bash +> rm /tmp/disk.qcow2 +> ./qemu-img create /tmp/disk.qcow2 -o preallocation=metadata -f qcow2 1G +> ./qemu-img create /tmp/fleecing.qcow2 -f qcow2 1G +> ./qemu-img create /tmp/backup.qcow2 -f qcow2 1G +> ./qemu-system-x86_64 --qmp stdio \ +> --blockdev qcow2,node-name=node0,file.driver=file,file.filename=/tmp/disk.qcow2 \ +> --blockdev qcow2,node-name=node1,file.driver=file,file.filename=/tmp/fleecing.qcow2 \ +> --blockdev qcow2,node-name=node2,file.driver=file,file.filename=/tmp/backup.qcow2 \ +> < {"execute": "qmp_capabilities"} +> {"execute": "blockdev-add", "arguments": { "driver": "copy-before-write", "file": "node0", "target": "node1", "node-name": "node3" } } +> {"execute": "blockdev-add", "arguments": { "driver": "snapshot-access", "file": "node3", "node-name": "snap0" } } +> {"execute": "blockdev-backup", "arguments": { "device": "snap0", "target": "node1", "sync": "full", "job-id": "backup0" } } +> EOF + +Signed-off-by: Fiona Ebner +Reviewed-by: Vladimir Sementsov-Ogievskiy +Message-id: 20240116154839.401030-1-f.ebner@proxmox.com +Signed-off-by: Stefan Hajnoczi +(cherry picked from commit 8a9be7992426c8920d4178e7dca59306a18c7a3a) +Signed-off-by: Michael Tokarev +--- + block/io.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/block/io.c b/block/io.c +index 7e62fab..d202987 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -2619,6 +2619,16 @@ bdrv_co_do_block_status(BlockDriverState *bs, bool want_zero, + ret |= (ret2 & BDRV_BLOCK_ZERO); + } + } ++ ++ /* ++ * Now that the recursive search was done, clear the flag. Otherwise, ++ * with more complicated block graphs like snapshot-access -> ++ * copy-before-write -> qcow2, where the return value will be propagated ++ * further up to a parent bdrv_co_do_block_status() call, both the ++ * BDRV_BLOCK_RECURSE and BDRV_BLOCK_ZERO flags would be set, which is ++ * not allowed. ++ */ ++ ret &= ~BDRV_BLOCK_RECURSE; + } + + out: +-- +1.8.3.1 + diff --git a/0055-linux-user-Fixed-cpu-restore-with-pc-0-on-SIGBUS.patch b/0055-linux-user-Fixed-cpu-restore-with-pc-0-on-SIGBUS.patch new file mode 100644 index 0000000000000000000000000000000000000000..26f079a9e4418c8c1a92311cc7435a428eda424d --- /dev/null +++ b/0055-linux-user-Fixed-cpu-restore-with-pc-0-on-SIGBUS.patch @@ -0,0 +1,54 @@ +From 8bdd3abcf1c989ebe4428190e5789dfbbdf6c6ca Mon Sep 17 00:00:00 2001 +From: Robbin Ehn +Date: Fri, 12 Jan 2024 21:57:22 +0100 +Subject: [PATCH 060/293] linux-user: Fixed cpu restore with pc 0 on SIGBUS + +Commit f4e1168198 (linux-user: Split out host_sig{segv,bus}_handler) +introduced a bug, when returning from host_sigbus_handler the PC is +never set. Thus cpu_loop_exit_restore is called with a zero PC and +we immediate get a SIGSEGV. + +Signed-off-by: Robbin Ehn +Fixes: f4e1168198 ("linux-user: Split out host_sig{segv,bus}_handler") +Reviewed-by: Palmer Dabbelt +Message-Id: <33f27425878fb529b9e39ef22c303f6e0d90525f.camel@rivosinc.com> +Signed-off-by: Richard Henderson +(cherry picked from commit 6d913158b5023ac948b8fd649d77fc86e28072f6) +Signed-off-by: Michael Tokarev +--- + linux-user/signal.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/linux-user/signal.c b/linux-user/signal.c +index b35d1e5..c9527ad 100644 +--- a/linux-user/signal.c ++++ b/linux-user/signal.c +@@ -925,7 +925,7 @@ static void host_sigsegv_handler(CPUState *cpu, siginfo_t *info, + cpu_loop_exit_sigsegv(cpu, guest_addr, access_type, maperr, pc); + } + +-static void host_sigbus_handler(CPUState *cpu, siginfo_t *info, ++static uintptr_t host_sigbus_handler(CPUState *cpu, siginfo_t *info, + host_sigcontext *uc) + { + uintptr_t pc = host_signal_pc(uc); +@@ -947,6 +947,7 @@ static void host_sigbus_handler(CPUState *cpu, siginfo_t *info, + sigprocmask(SIG_SETMASK, host_signal_mask(uc), NULL); + cpu_loop_exit_sigbus(cpu, guest_addr, access_type, pc); + } ++ return pc; + } + + static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) +@@ -974,7 +975,7 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) + host_sigsegv_handler(cpu, info, uc); + return; + case SIGBUS: +- host_sigbus_handler(cpu, info, uc); ++ pc = host_sigbus_handler(cpu, info, uc); + sync_sig = true; + break; + case SIGILL: +-- +1.8.3.1 + diff --git a/0056-tcg-arm-Fix-SIGILL-in-tcg_out_qemu_st_direct.patch b/0056-tcg-arm-Fix-SIGILL-in-tcg_out_qemu_st_direct.patch new file mode 100644 index 0000000000000000000000000000000000000000..0d621e669b50ab0fcbad9df4a9590917c5107857 --- /dev/null +++ b/0056-tcg-arm-Fix-SIGILL-in-tcg_out_qemu_st_direct.patch @@ -0,0 +1,37 @@ +From 6f6492ab070f10843c881690cacc3bf2e051f9e5 Mon Sep 17 00:00:00 2001 +From: Joseph Burt +Date: Sun, 21 Jan 2024 21:14:39 +0000 +Subject: [PATCH 062/293] tcg/arm: Fix SIGILL in tcg_out_qemu_st_direct + +When tcg_out_qemu_st_{index,direct} were merged, the direct case for +MO_64 was omitted, causing qemu_st_i64 to be encoded as 0xffffffff due +to underflow when adding h.base and h.index. + +Fixes: 1df6d611bdc2 ("tcg/arm: Introduce HostAddress") +Signed-off-by: Joseph Burt +Message-Id: <20240121211439.100829-1-caseorum@gmail.com> +Reviewed-by: Richard Henderson +Signed-off-by: Richard Henderson +(cherry picked from commit 9f6523e8e4689cafdbed7c10b7cf7c775b5a607b) +Signed-off-by: Michael Tokarev +--- + tcg/arm/tcg-target.c.inc | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc +index fc78566..a9aa8aa 100644 +--- a/tcg/arm/tcg-target.c.inc ++++ b/tcg/arm/tcg-target.c.inc +@@ -1662,6 +1662,9 @@ static void tcg_out_qemu_st_direct(TCGContext *s, MemOp opc, TCGReg datalo, + } else { + tcg_out_strd_r(s, h.cond, datalo, h.base, h.index); + } ++ } else if (h.index < 0) { ++ tcg_out_st32_12(s, h.cond, datalo, h.base, 0); ++ tcg_out_st32_12(s, h.cond, datahi, h.base, 4); + } else if (h.index_scratch) { + tcg_out_st32_rwb(s, h.cond, datalo, h.index, h.base); + tcg_out_st32_12(s, h.cond, datahi, h.index, 4); +-- +1.8.3.1 + diff --git a/0057-virtio-net-correctly-copy-vnet-header-when-flushing-.patch b/0057-virtio-net-correctly-copy-vnet-header-when-flushing-.patch new file mode 100644 index 0000000000000000000000000000000000000000..5657b5acf3cda24d37f1f14e6a84eb16eee79140 --- /dev/null +++ b/0057-virtio-net-correctly-copy-vnet-header-when-flushing-.patch @@ -0,0 +1,74 @@ +From 939a09575fff7048446e36ce438fa7be6e251d41 Mon Sep 17 00:00:00 2001 +From: Jason Wang +Date: Tue, 2 Jan 2024 11:29:01 +0800 +Subject: [PATCH 063/293] virtio-net: correctly copy vnet header when flushing + TX + +When HASH_REPORT is negotiated, the guest_hdr_len might be larger than +the size of the mergeable rx buffer header. Using +virtio_net_hdr_mrg_rxbuf during the header swap might lead a stack +overflow in this case. Fixing this by using virtio_net_hdr_v1_hash +instead. + +Reported-by: Xiao Lei +Cc: Yuri Benditovich +Cc: qemu-stable@nongnu.org +Cc: Mauro Matteo Cascella +Fixes: CVE-2023-6693 +Fixes: e22f0603fb2f ("virtio-net: reference implementation of hash report") +Reviewed-by: Michael Tokarev +Signed-off-by: Jason Wang +(cherry picked from commit 2220e8189fb94068dbad333228659fbac819abb0) +Signed-off-by: Michael Tokarev +--- + hw/net/virtio-net.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c +index 80c56f0..73024ba 100644 +--- a/hw/net/virtio-net.c ++++ b/hw/net/virtio-net.c +@@ -674,6 +674,11 @@ static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs, + + n->mergeable_rx_bufs = mergeable_rx_bufs; + ++ /* ++ * Note: when extending the vnet header, please make sure to ++ * change the vnet header copying logic in virtio_net_flush_tx() ++ * as well. ++ */ + if (version_1) { + n->guest_hdr_len = hash_report ? + sizeof(struct virtio_net_hdr_v1_hash) : +@@ -2693,7 +2698,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) + ssize_t ret; + unsigned int out_num; + struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1], *out_sg; +- struct virtio_net_hdr_mrg_rxbuf mhdr; ++ struct virtio_net_hdr_v1_hash vhdr; + + elem = virtqueue_pop(q->tx_vq, sizeof(VirtQueueElement)); + if (!elem) { +@@ -2710,7 +2715,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) + } + + if (n->has_vnet_hdr) { +- if (iov_to_buf(out_sg, out_num, 0, &mhdr, n->guest_hdr_len) < ++ if (iov_to_buf(out_sg, out_num, 0, &vhdr, n->guest_hdr_len) < + n->guest_hdr_len) { + virtio_error(vdev, "virtio-net header incorrect"); + virtqueue_detach_element(q->tx_vq, elem, 0); +@@ -2718,8 +2723,8 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) + return -EINVAL; + } + if (n->needs_vnet_hdr_swap) { +- virtio_net_hdr_swap(vdev, (void *) &mhdr); +- sg2[0].iov_base = &mhdr; ++ virtio_net_hdr_swap(vdev, (void *) &vhdr); ++ sg2[0].iov_base = &vhdr; + sg2[0].iov_len = n->guest_hdr_len; + out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1, + out_sg, out_num, +-- +1.8.3.1 + diff --git a/0058-block-blklogwrites-Fix-a-bug-when-logging-write-zero.patch b/0058-block-blklogwrites-Fix-a-bug-when-logging-write-zero.patch new file mode 100644 index 0000000000000000000000000000000000000000..bf511a674112fd80364c4e2267049309bba5be07 --- /dev/null +++ b/0058-block-blklogwrites-Fix-a-bug-when-logging-write-zero.patch @@ -0,0 +1,107 @@ +From cf70966523fd6c13ce1fff764eac0e1bfdf785a3 Mon Sep 17 00:00:00 2001 +From: Ari Sundholm +Date: Tue, 9 Jan 2024 20:46:46 +0200 +Subject: [PATCH 064/293] block/blklogwrites: Fix a bug when logging "write + zeroes" operations. + +There is a bug in the blklogwrites driver pertaining to logging "write +zeroes" operations, causing log corruption. This can be easily observed +by setting detect-zeroes to something other than "off" for the driver. + +The issue is caused by a concurrency bug pertaining to the fact that +"write zeroes" operations have to be logged in two parts: first the log +entry metadata, then the zeroed-out region. While the log entry +metadata is being written by bdrv_co_pwritev(), another operation may +begin in the meanwhile and modify the state of the blklogwrites driver. +This is as intended by the coroutine-driven I/O model in QEMU, of +course. + +Unfortunately, this specific scenario is mishandled. A short example: + 1. Initially, in the current operation (#1), the current log sector +number in the driver state is only incremented by the number of sectors +taken by the log entry metadata, after which the log entry metadata is +written. The current operation yields. + 2. Another operation (#2) may start while the log entry metadata is +being written. It uses the current log position as the start offset for +its log entry. This is in the sector right after the operation #1 log +entry metadata, which is bad! + 3. After bdrv_co_pwritev() returns (#1), the current log sector +number is reread from the driver state in order to find out the start +offset for bdrv_co_pwrite_zeroes(). This is an obvious blunder, as the +offset will be the sector right after the (misplaced) operation #2 log +entry, which means that the zeroed-out region begins at the wrong +offset. + 4. As a result of the above, the log is corrupt. + +Fix this by only reading the driver metadata once, computing the +offsets and sizes in one go (including the optional zeroed-out region) +and setting the log sector number to the appropriate value for the next +operation in line. + +Signed-off-by: Ari Sundholm +Cc: qemu-stable@nongnu.org +Message-ID: <20240109184646.1128475-1-megari@gmx.com> +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit a9c8ea95470c27a8a02062b67f9fa6940e828ab6) +Signed-off-by: Michael Tokarev +--- + block/blklogwrites.c | 35 ++++++++++++++++++++++++++--------- + 1 file changed, 26 insertions(+), 9 deletions(-) + +diff --git a/block/blklogwrites.c b/block/blklogwrites.c +index 3678f6c..84e03f3 100644 +--- a/block/blklogwrites.c ++++ b/block/blklogwrites.c +@@ -328,22 +328,39 @@ static void coroutine_fn GRAPH_RDLOCK + blk_log_writes_co_do_log(BlkLogWritesLogReq *lr) + { + BDRVBlkLogWritesState *s = lr->bs->opaque; +- uint64_t cur_log_offset = s->cur_log_sector << s->sectorbits; + +- s->nr_entries++; +- s->cur_log_sector += +- ROUND_UP(lr->qiov->size, s->sectorsize) >> s->sectorbits; ++ /* ++ * Determine the offsets and sizes of different parts of the entry, and ++ * update the state of the driver. ++ * ++ * This needs to be done in one go, before any actual I/O is done, as the ++ * log entry may have to be written in two parts, and the state of the ++ * driver may be modified by other driver operations while waiting for the ++ * I/O to complete. ++ */ ++ const uint64_t entry_start_sector = s->cur_log_sector; ++ const uint64_t entry_offset = entry_start_sector << s->sectorbits; ++ const uint64_t qiov_aligned_size = ROUND_UP(lr->qiov->size, s->sectorsize); ++ const uint64_t entry_aligned_size = qiov_aligned_size + ++ ROUND_UP(lr->zero_size, s->sectorsize); ++ const uint64_t entry_nr_sectors = entry_aligned_size >> s->sectorbits; + +- lr->log_ret = bdrv_co_pwritev(s->log_file, cur_log_offset, lr->qiov->size, ++ s->nr_entries++; ++ s->cur_log_sector += entry_nr_sectors; ++ ++ /* ++ * Write the log entry. Note that if this is a "write zeroes" operation, ++ * only the entry header is written here, with the zeroing being done ++ * separately below. ++ */ ++ lr->log_ret = bdrv_co_pwritev(s->log_file, entry_offset, lr->qiov->size, + lr->qiov, 0); + + /* Logging for the "write zeroes" operation */ + if (lr->log_ret == 0 && lr->zero_size) { +- cur_log_offset = s->cur_log_sector << s->sectorbits; +- s->cur_log_sector += +- ROUND_UP(lr->zero_size, s->sectorsize) >> s->sectorbits; ++ const uint64_t zeroes_offset = entry_offset + qiov_aligned_size; + +- lr->log_ret = bdrv_co_pwrite_zeroes(s->log_file, cur_log_offset, ++ lr->log_ret = bdrv_co_pwrite_zeroes(s->log_file, zeroes_offset, + lr->zero_size, 0); + } + +-- +1.8.3.1 + diff --git a/0059-iotests-add-filter_qmp_generated_node_ids.patch b/0059-iotests-add-filter_qmp_generated_node_ids.patch new file mode 100644 index 0000000000000000000000000000000000000000..0d28cedcac0e366255145ab092d84b398920a3d9 --- /dev/null +++ b/0059-iotests-add-filter_qmp_generated_node_ids.patch @@ -0,0 +1,42 @@ +From d7a64c4517c2b59429ac7edea7c2b52e4806e135 Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +Date: Thu, 18 Jan 2024 09:48:21 -0500 +Subject: [PATCH 065/293] iotests: add filter_qmp_generated_node_ids() + +Add a filter function for QMP responses that contain QEMU's +automatically generated node ids. The ids change between runs and must +be masked in the reference output. + +The next commit will use this new function. + +Signed-off-by: Stefan Hajnoczi +Message-ID: <20240118144823.1497953-2-stefanha@redhat.com> +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit da62b507a20510d819bcfbe8f5e573409b954006) +Signed-off-by: Michael Tokarev +--- + tests/qemu-iotests/iotests.py | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py +index e5c5798..ea48af4 100644 +--- a/tests/qemu-iotests/iotests.py ++++ b/tests/qemu-iotests/iotests.py +@@ -651,6 +651,13 @@ def _filter(_key, value): + def filter_generated_node_ids(msg): + return re.sub("#block[0-9]+", "NODE_NAME", msg) + ++def filter_qmp_generated_node_ids(qmsg): ++ def _filter(_key, value): ++ if is_str(value): ++ return filter_generated_node_ids(value) ++ return value ++ return filter_qmp(qmsg, _filter) ++ + def filter_img_info(output: str, filename: str, + drop_child_info: bool = True) -> str: + lines = [] +-- +1.8.3.1 + diff --git a/0060-iotests-port-141-to-Python-for-reliable-QMP-testing.patch b/0060-iotests-port-141-to-Python-for-reliable-QMP-testing.patch new file mode 100644 index 0000000000000000000000000000000000000000..244013349a184f8eda1b2020f71d4cf766863b23 --- /dev/null +++ b/0060-iotests-port-141-to-Python-for-reliable-QMP-testing.patch @@ -0,0 +1,585 @@ +From 823892d19f6b79dc44ac1a885a9ceed0bd0965e7 Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +Date: Thu, 18 Jan 2024 09:48:22 -0500 +Subject: [PATCH 066/293] iotests: port 141 to Python for reliable QMP testing + +The common.qemu bash functions allow tests to interact with the QMP +monitor of a QEMU process. I spent two days trying to update 141 when +the order of the test output changed, but found it would still fail +occassionally because printf() and QMP events race with synchronous QMP +communication. + +I gave up and ported 141 to the existing Python API for QMP tests. The +Python API is less affected by the order in which QEMU prints output +because it does not print all QMP traffic by default. + +The next commit changes the order in which QMP messages are received. +Make 141 reliable first. + +Cc: Hanna Czenczek +Signed-off-by: Stefan Hajnoczi +Message-ID: <20240118144823.1497953-3-stefanha@redhat.com> +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit 9ee2dd4c22a3639c5462b3fc20df60c005c3de64) +Signed-off-by: Michael Tokarev +--- + tests/qemu-iotests/141 | 307 +++++++++++++++++++-------------------------- + tests/qemu-iotests/141.out | 200 +++++++---------------------- + 2 files changed, 176 insertions(+), 331 deletions(-) + +diff --git a/tests/qemu-iotests/141 b/tests/qemu-iotests/141 +index a37030e..a7d3985 100755 +--- a/tests/qemu-iotests/141 ++++ b/tests/qemu-iotests/141 +@@ -1,9 +1,12 @@ +-#!/usr/bin/env bash ++#!/usr/bin/env python3 + # group: rw auto quick + # + # Test case for ejecting BDSs with block jobs still running on them + # +-# Copyright (C) 2016 Red Hat, Inc. ++# Originally written in bash by Hanna Czenczek, ported to Python by Stefan ++# Hajnoczi. ++# ++# Copyright Red Hat + # + # This program is free software; you can redistribute it and/or modify + # it under the terms of the GNU General Public License as published by +@@ -19,177 +22,129 @@ + # along with this program. If not, see . + # + +-# creator +-owner=hreitz@redhat.com +- +-seq="$(basename $0)" +-echo "QA output created by $seq" +- +-status=1 # failure is the default! +- +-_cleanup() +-{ +- _cleanup_qemu +- _cleanup_test_img +- for img in "$TEST_DIR"/{b,m,o}.$IMGFMT; do +- _rm_test_img "$img" +- done +-} +-trap "_cleanup; exit \$status" 0 1 2 3 15 +- +-# get standard environment, filters and checks +-. ./common.rc +-. ./common.filter +-. ./common.qemu +- +-# Needs backing file and backing format support +-_supported_fmt qcow2 qed +-_supported_proto file +-_supported_os Linux +- +- +-test_blockjob() +-{ +- _send_qemu_cmd $QEMU_HANDLE \ +- "{'execute': 'blockdev-add', +- 'arguments': { +- 'node-name': 'drv0', +- 'driver': '$IMGFMT', +- 'file': { +- 'driver': 'file', +- 'filename': '$TEST_IMG' +- }}}" \ +- 'return' +- +- # If "$2" is an event, we may or may not see it before the +- # {"return": {}}. Therefore, filter the {"return": {}} out both +- # here and in the next command. (Naturally, if we do not see it +- # here, we will see it before the next command can be executed, +- # so it will appear in the next _send_qemu_cmd's output.) +- _send_qemu_cmd $QEMU_HANDLE \ +- "$1" \ +- "$2" \ +- | _filter_img_create | _filter_qmp_empty_return +- +- # We want this to return an error because the block job is still running +- _send_qemu_cmd $QEMU_HANDLE \ +- "{'execute': 'blockdev-del', +- 'arguments': {'node-name': 'drv0'}}" \ +- 'error' | _filter_generated_node_ids | _filter_qmp_empty_return +- +- _send_qemu_cmd $QEMU_HANDLE \ +- "{'execute': 'block-job-cancel', +- 'arguments': {'device': 'job0'}}" \ +- "$3" +- +- _send_qemu_cmd $QEMU_HANDLE \ +- "{'execute': 'blockdev-del', +- 'arguments': {'node-name': 'drv0'}}" \ +- 'return' +-} +- +- +-TEST_IMG="$TEST_DIR/b.$IMGFMT" _make_test_img 1M +-TEST_IMG="$TEST_DIR/m.$IMGFMT" _make_test_img -b "$TEST_DIR/b.$IMGFMT" -F $IMGFMT 1M +-_make_test_img -b "$TEST_DIR/m.$IMGFMT" 1M -F $IMGFMT +- +-_launch_qemu -nodefaults +- +-_send_qemu_cmd $QEMU_HANDLE \ +- "{'execute': 'qmp_capabilities'}" \ +- 'return' +- +-echo +-echo '=== Testing drive-backup ===' +-echo +- +-# drive-backup will not send BLOCK_JOB_READY by itself, and cancelling the job +-# will consequently result in BLOCK_JOB_CANCELLED being emitted. +- +-test_blockjob \ +- "{'execute': 'drive-backup', +- 'arguments': {'job-id': 'job0', +- 'device': 'drv0', +- 'target': '$TEST_DIR/o.$IMGFMT', +- 'format': '$IMGFMT', +- 'sync': 'none'}}" \ +- 'return' \ +- '"status": "null"' +- +-echo +-echo '=== Testing drive-mirror ===' +-echo +- +-# drive-mirror will send BLOCK_JOB_READY basically immediately, and cancelling +-# the job will consequently result in BLOCK_JOB_COMPLETED being emitted. +- +-test_blockjob \ +- "{'execute': 'drive-mirror', +- 'arguments': {'job-id': 'job0', +- 'device': 'drv0', +- 'target': '$TEST_DIR/o.$IMGFMT', +- 'format': '$IMGFMT', +- 'sync': 'none'}}" \ +- 'BLOCK_JOB_READY' \ +- '"status": "null"' +- +-echo +-echo '=== Testing active block-commit ===' +-echo +- +-# An active block-commit will send BLOCK_JOB_READY basically immediately, and +-# cancelling the job will consequently result in BLOCK_JOB_COMPLETED being +-# emitted. +- +-test_blockjob \ +- "{'execute': 'block-commit', +- 'arguments': {'job-id': 'job0', 'device': 'drv0'}}" \ +- 'BLOCK_JOB_READY' \ +- '"status": "null"' +- +-echo +-echo '=== Testing non-active block-commit ===' +-echo +- +-# Give block-commit something to work on, otherwise it would be done +-# immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would work just +-# fine without the block job still running. +- +-$QEMU_IO -c 'write 0 1M' "$TEST_DIR/m.$IMGFMT" | _filter_qemu_io +- +-test_blockjob \ +- "{'execute': 'block-commit', +- 'arguments': {'job-id': 'job0', +- 'device': 'drv0', +- 'top': '$TEST_DIR/m.$IMGFMT', +- 'speed': 1}}" \ +- 'return' \ +- '"status": "null"' +- +-echo +-echo '=== Testing block-stream ===' +-echo +- +-# Give block-stream something to work on, otherwise it would be done +-# immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would work just +-# fine without the block job still running. +- +-$QEMU_IO -c 'write 0 1M' "$TEST_DIR/b.$IMGFMT" | _filter_qemu_io +- +-# With some data to stream (and @speed set to 1), block-stream will not complete +-# until we send the block-job-cancel command. +- +-test_blockjob \ +- "{'execute': 'block-stream', +- 'arguments': {'job-id': 'job0', +- 'device': 'drv0', +- 'speed': 1}}" \ +- 'return' \ +- '"status": "null"' +- +-_cleanup_qemu +- +-# success, all done +-echo "*** done" +-rm -f $seq.full +-status=0 ++import iotests ++ ++# Common filters to mask values that vary in the test output ++QMP_FILTERS = [iotests.filter_qmp_testfiles, \ ++ iotests.filter_qmp_imgfmt] ++ ++ ++class TestCase: ++ def __init__(self, name, vm, image_path, cancel_event): ++ self.name = name ++ self.vm = vm ++ self.image_path = image_path ++ self.cancel_event = cancel_event ++ ++ def __enter__(self): ++ iotests.log(f'=== Testing {self.name} ===') ++ self.vm.qmp_log('blockdev-add', \ ++ node_name='drv0', \ ++ driver=iotests.imgfmt, \ ++ file={'driver': 'file', 'filename': self.image_path}, \ ++ filters=QMP_FILTERS) ++ ++ def __exit__(self, *exc_details): ++ # This is expected to fail because the job still exists ++ self.vm.qmp_log('blockdev-del', node_name='drv0', \ ++ filters=[iotests.filter_qmp_generated_node_ids]) ++ ++ self.vm.qmp_log('block-job-cancel', device='job0') ++ event = self.vm.event_wait(self.cancel_event) ++ iotests.log(event, filters=[iotests.filter_qmp_event]) ++ ++ # This time it succeeds ++ self.vm.qmp_log('blockdev-del', node_name='drv0') ++ ++ # Separate test cases in output ++ iotests.log('') ++ ++ ++def main() -> None: ++ with iotests.FilePath('bottom', 'middle', 'top', 'target') as \ ++ (bottom_path, middle_path, top_path, target_path), \ ++ iotests.VM() as vm: ++ ++ iotests.log('Creating bottom <- middle <- top backing file chain...') ++ IMAGE_SIZE='1M' ++ iotests.qemu_img_create('-f', iotests.imgfmt, bottom_path, IMAGE_SIZE) ++ iotests.qemu_img_create('-f', iotests.imgfmt, \ ++ '-F', iotests.imgfmt, \ ++ '-b', bottom_path, \ ++ middle_path, \ ++ IMAGE_SIZE) ++ iotests.qemu_img_create('-f', iotests.imgfmt, \ ++ '-F', iotests.imgfmt, \ ++ '-b', middle_path, \ ++ top_path, \ ++ IMAGE_SIZE) ++ ++ iotests.log('Starting VM...') ++ vm.add_args('-nodefaults') ++ vm.launch() ++ ++ # drive-backup will not send BLOCK_JOB_READY by itself, and cancelling ++ # the job will consequently result in BLOCK_JOB_CANCELLED being ++ # emitted. ++ with TestCase('drive-backup', vm, top_path, 'BLOCK_JOB_CANCELLED'): ++ vm.qmp_log('drive-backup', \ ++ job_id='job0', \ ++ device='drv0', \ ++ target=target_path, \ ++ format=iotests.imgfmt, \ ++ sync='none', \ ++ filters=QMP_FILTERS) ++ ++ # drive-mirror will send BLOCK_JOB_READY basically immediately, and ++ # cancelling the job will consequently result in BLOCK_JOB_COMPLETED ++ # being emitted. ++ with TestCase('drive-mirror', vm, top_path, 'BLOCK_JOB_COMPLETED'): ++ vm.qmp_log('drive-mirror', \ ++ job_id='job0', \ ++ device='drv0', \ ++ target=target_path, \ ++ format=iotests.imgfmt, \ ++ sync='none', \ ++ filters=QMP_FILTERS) ++ event = vm.event_wait('BLOCK_JOB_READY') ++ assert event is not None # silence mypy ++ iotests.log(event, filters=[iotests.filter_qmp_event]) ++ ++ # An active block-commit will send BLOCK_JOB_READY basically ++ # immediately, and cancelling the job will consequently result in ++ # BLOCK_JOB_COMPLETED being emitted. ++ with TestCase('active block-commit', vm, top_path, \ ++ 'BLOCK_JOB_COMPLETED'): ++ vm.qmp_log('block-commit', \ ++ job_id='job0', \ ++ device='drv0') ++ event = vm.event_wait('BLOCK_JOB_READY') ++ assert event is not None # silence mypy ++ iotests.log(event, filters=[iotests.filter_qmp_event]) ++ ++ # Give block-commit something to work on, otherwise it would be done ++ # immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would ++ # work just fine without the block job still running. ++ iotests.qemu_io(middle_path, '-c', f'write 0 {IMAGE_SIZE}') ++ with TestCase('non-active block-commit', vm, top_path, \ ++ 'BLOCK_JOB_CANCELLED'): ++ vm.qmp_log('block-commit', \ ++ job_id='job0', \ ++ device='drv0', \ ++ top=middle_path, \ ++ speed=1, \ ++ filters=[iotests.filter_qmp_testfiles]) ++ ++ # Give block-stream something to work on, otherwise it would be done ++ # immediately, send a BLOCK_JOB_COMPLETED and ejecting the BDS would ++ # work just fine without the block job still running. ++ iotests.qemu_io(bottom_path, '-c', f'write 0 {IMAGE_SIZE}') ++ with TestCase('block-stream', vm, top_path, 'BLOCK_JOB_CANCELLED'): ++ vm.qmp_log('block-stream', \ ++ job_id='job0', \ ++ device='drv0', \ ++ speed=1) ++ ++if __name__ == '__main__': ++ iotests.script_main(main, supported_fmts=['qcow2', 'qed'], ++ supported_protocols=['file']) +diff --git a/tests/qemu-iotests/141.out b/tests/qemu-iotests/141.out +index 63203d9..91b7ba5 100644 +--- a/tests/qemu-iotests/141.out ++++ b/tests/qemu-iotests/141.out +@@ -1,179 +1,69 @@ +-QA output created by 141 +-Formatting 'TEST_DIR/b.IMGFMT', fmt=IMGFMT size=1048576 +-Formatting 'TEST_DIR/m.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/b.IMGFMT backing_fmt=IMGFMT +-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/m.IMGFMT backing_fmt=IMGFMT +-{'execute': 'qmp_capabilities'} +-{"return": {}} +- ++Creating bottom <- middle <- top backing file chain... ++Starting VM... + === Testing drive-backup === +- +-{'execute': 'blockdev-add', +- 'arguments': { +- 'node-name': 'drv0', +- 'driver': 'IMGFMT', +- 'file': { +- 'driver': 'file', +- 'filename': 'TEST_DIR/t.IMGFMT' +- }}} +-{"return": {}} +-{'execute': 'drive-backup', +-'arguments': {'job-id': 'job0', +-'device': 'drv0', +-'target': 'TEST_DIR/o.IMGFMT', +-'format': 'IMGFMT', +-'sync': 'none'}} +-Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "job0"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} +-{'execute': 'blockdev-del', +- 'arguments': {'node-name': 'drv0'}} ++{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}} ++{"return": {}} ++{"execute": "drive-backup", "arguments": {"device": "drv0", "format": "IMGFMT", "job-id": "job0", "sync": "none", "target": "TEST_DIR/PID-target"}} ++{"return": {}} ++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}} + {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}} +-{'execute': 'block-job-cancel', +- 'arguments': {'device': 'job0'}} ++{"execute": "block-job-cancel", "arguments": {"device": "job0"}} + {"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 0, "speed": 0, "type": "backup"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} +-{'execute': 'blockdev-del', +- 'arguments': {'node-name': 'drv0'}} ++{"data": {"device": "job0", "len": 1048576, "offset": 0, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}} + {"return": {}} + + === Testing drive-mirror === +- +-{'execute': 'blockdev-add', +- 'arguments': { +- 'node-name': 'drv0', +- 'driver': 'IMGFMT', +- 'file': { +- 'driver': 'file', +- 'filename': 'TEST_DIR/t.IMGFMT' +- }}} +-{"return": {}} +-{'execute': 'drive-mirror', +-'arguments': {'job-id': 'job0', +-'device': 'drv0', +-'target': 'TEST_DIR/o.IMGFMT', +-'format': 'IMGFMT', +-'sync': 'none'}} +-Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job0"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}} +-{'execute': 'blockdev-del', +- 'arguments': {'node-name': 'drv0'}} ++{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}} ++{"return": {}} ++{"execute": "drive-mirror", "arguments": {"device": "drv0", "format": "IMGFMT", "job-id": "job0", "sync": "none", "target": "TEST_DIR/PID-target"}} ++{"return": {}} ++{"data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}} + {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: mirror"}} +-{'execute': 'block-job-cancel', +- 'arguments': {'device': 'job0'}} ++{"execute": "block-job-cancel", "arguments": {"device": "job0"}} + {"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} +-{'execute': 'blockdev-del', +- 'arguments': {'node-name': 'drv0'}} ++{"data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}} + {"return": {}} + + === Testing active block-commit === +- +-{'execute': 'blockdev-add', +- 'arguments': { +- 'node-name': 'drv0', +- 'driver': 'IMGFMT', +- 'file': { +- 'driver': 'file', +- 'filename': 'TEST_DIR/t.IMGFMT' +- }}} +-{"return": {}} +-{'execute': 'block-commit', +-'arguments': {'job-id': 'job0', 'device': 'drv0'}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job0"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} +-{'execute': 'blockdev-del', +- 'arguments': {'node-name': 'drv0'}} ++{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}} ++{"return": {}} ++{"execute": "block-commit", "arguments": {"device": "drv0", "job-id": "job0"}} ++{"return": {}} ++{"data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}} + {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: commit"}} +-{'execute': 'block-job-cancel', +- 'arguments': {'device': 'job0'}} ++{"execute": "block-job-cancel", "arguments": {"device": "job0"}} + {"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} +-{'execute': 'blockdev-del', +- 'arguments': {'node-name': 'drv0'}} ++{"data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}} + {"return": {}} + + === Testing non-active block-commit === +- +-wrote 1048576/1048576 bytes at offset 0 +-1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +-{'execute': 'blockdev-add', +- 'arguments': { +- 'node-name': 'drv0', +- 'driver': 'IMGFMT', +- 'file': { +- 'driver': 'file', +- 'filename': 'TEST_DIR/t.IMGFMT' +- }}} +-{"return": {}} +-{'execute': 'block-commit', +-'arguments': {'job-id': 'job0', +-'device': 'drv0', +-'top': 'TEST_DIR/m.IMGFMT', +-'speed': 1}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} +-{'execute': 'blockdev-del', +- 'arguments': {'node-name': 'drv0'}} ++{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}} ++{"return": {}} ++{"execute": "block-commit", "arguments": {"device": "drv0", "job-id": "job0", "speed": 1, "top": "TEST_DIR/PID-middle"}} ++{"return": {}} ++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}} + {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: commit"}} +-{'execute': 'block-job-cancel', +- 'arguments': {'device': 'job0'}} ++{"execute": "block-job-cancel", "arguments": {"device": "job0"}} + {"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "commit"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} +-{'execute': 'blockdev-del', +- 'arguments': {'node-name': 'drv0'}} ++{"data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "commit"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}} + {"return": {}} + + === Testing block-stream === +- +-wrote 1048576/1048576 bytes at offset 0 +-1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +-{'execute': 'blockdev-add', +- 'arguments': { +- 'node-name': 'drv0', +- 'driver': 'IMGFMT', +- 'file': { +- 'driver': 'file', +- 'filename': 'TEST_DIR/t.IMGFMT' +- }}} +-{"return": {}} +-{'execute': 'block-stream', +-'arguments': {'job-id': 'job0', +-'device': 'drv0', +-'speed': 1}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} +-{'execute': 'blockdev-del', +- 'arguments': {'node-name': 'drv0'}} ++{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top"}, "node-name": "drv0"}} ++{"return": {}} ++{"execute": "block-stream", "arguments": {"device": "drv0", "job-id": "job0", "speed": 1}} ++{"return": {}} ++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}} + {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: stream"}} +-{'execute': 'block-job-cancel', +- 'arguments': {'device': 'job0'}} ++{"execute": "block-job-cancel", "arguments": {"device": "job0"}} + {"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "stream"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} +-{'execute': 'blockdev-del', +- 'arguments': {'node-name': 'drv0'}} ++{"data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "stream"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} ++{"execute": "blockdev-del", "arguments": {"node-name": "drv0"}} + {"return": {}} +-*** done ++ +-- +1.8.3.1 + diff --git a/0061-monitor-only-run-coroutine-commands-in-qemu_aio_cont.patch b/0061-monitor-only-run-coroutine-commands-in-qemu_aio_cont.patch new file mode 100644 index 0000000000000000000000000000000000000000..0dbc32e10b9c71f764a55e9d37887403e42eb12e --- /dev/null +++ b/0061-monitor-only-run-coroutine-commands-in-qemu_aio_cont.patch @@ -0,0 +1,1623 @@ +From f389309d2937e66960dd371014a1971678fb4ce7 Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +Date: Thu, 18 Jan 2024 09:48:23 -0500 +Subject: [PATCH 067/293] monitor: only run coroutine commands in + qemu_aio_context + +monitor_qmp_dispatcher_co() runs in the iohandler AioContext that is not +polled during nested event loops. The coroutine currently reschedules +itself in the main loop's qemu_aio_context AioContext, which is polled +during nested event loops. One known problem is that QMP device-add +calls drain_call_rcu(), which temporarily drops the BQL, leading to all +sorts of havoc like other vCPU threads re-entering device emulation code +while another vCPU thread is waiting in device emulation code with +aio_poll(). + +Paolo Bonzini suggested running non-coroutine QMP handlers in the +iohandler AioContext. This avoids trouble with nested event loops. His +original idea was to move coroutine rescheduling to +monitor_qmp_dispatch(), but I resorted to moving it to qmp_dispatch() +because we don't know if the QMP handler needs to run in coroutine +context in monitor_qmp_dispatch(). monitor_qmp_dispatch() would have +been nicer since it's associated with the monitor implementation and not +as general as qmp_dispatch(), which is also used by qemu-ga. + +A number of qemu-iotests need updated .out files because the order of +QMP events vs QMP responses has changed. + +Solves Issue #1933. + +Cc: qemu-stable@nongnu.org +Fixes: 7bed89958bfbf40df9ca681cefbdca63abdde39d ("device_core: use drain_call_rcu in in qmp_device_add") +Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=2215192 +Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=2214985 +Buglink: https://issues.redhat.com/browse/RHEL-17369 +Signed-off-by: Stefan Hajnoczi +Message-ID: <20240118144823.1497953-4-stefanha@redhat.com> +Reviewed-by: Kevin Wolf +Tested-by: Fiona Ebner +Signed-off-by: Kevin Wolf +(cherry picked from commit effd60c878176bcaf97fa7ce2b12d04bb8ead6f7) +Signed-off-by: Michael Tokarev +--- + monitor/qmp.c | 17 ------ + qapi/qmp-dispatch.c | 24 ++++++++- + tests/qemu-iotests/060.out | 4 +- + tests/qemu-iotests/071.out | 4 +- + tests/qemu-iotests/081.out | 16 +++--- + tests/qemu-iotests/087.out | 12 ++--- + tests/qemu-iotests/108.out | 2 +- + tests/qemu-iotests/109 | 4 +- + tests/qemu-iotests/109.out | 78 ++++++++++++--------------- + tests/qemu-iotests/117.out | 2 +- + tests/qemu-iotests/120.out | 2 +- + tests/qemu-iotests/127.out | 2 +- + tests/qemu-iotests/140.out | 2 +- + tests/qemu-iotests/143.out | 2 +- + tests/qemu-iotests/156.out | 2 +- + tests/qemu-iotests/176.out | 16 +++--- + tests/qemu-iotests/182.out | 2 +- + tests/qemu-iotests/183.out | 4 +- + tests/qemu-iotests/184.out | 32 +++++------ + tests/qemu-iotests/185 | 6 +-- + tests/qemu-iotests/185.out | 45 ++++++++++++++-- + tests/qemu-iotests/191.out | 16 +++--- + tests/qemu-iotests/195.out | 16 +++--- + tests/qemu-iotests/223.out | 12 ++--- + tests/qemu-iotests/227.out | 32 +++++------ + tests/qemu-iotests/247.out | 2 +- + tests/qemu-iotests/273.out | 8 +-- + tests/qemu-iotests/308 | 4 +- + tests/qemu-iotests/308.out | 4 +- + tests/qemu-iotests/tests/file-io-error | 5 +- + tests/qemu-iotests/tests/iothreads-resize.out | 2 +- + tests/qemu-iotests/tests/qsd-jobs.out | 4 +- + 32 files changed, 205 insertions(+), 178 deletions(-) + +diff --git a/monitor/qmp.c b/monitor/qmp.c +index 6eee450..a239945 100644 +--- a/monitor/qmp.c ++++ b/monitor/qmp.c +@@ -321,14 +321,6 @@ void coroutine_fn monitor_qmp_dispatcher_co(void *data) + qemu_coroutine_yield(); + } + +- /* +- * Move the coroutine from iohandler_ctx to qemu_aio_context for +- * executing the command handler so that it can make progress if it +- * involves an AIO_WAIT_WHILE(). +- */ +- aio_co_schedule(qemu_get_aio_context(), qmp_dispatcher_co); +- qemu_coroutine_yield(); +- + /* Process request */ + if (req_obj->req) { + if (trace_event_get_state(TRACE_MONITOR_QMP_CMD_IN_BAND)) { +@@ -355,15 +347,6 @@ void coroutine_fn monitor_qmp_dispatcher_co(void *data) + } + + qmp_request_free(req_obj); +- +- /* +- * Yield and reschedule so the main loop stays responsive. +- * +- * Move back to iohandler_ctx so that nested event loops for +- * qemu_aio_context don't start new monitor commands. +- */ +- aio_co_schedule(iohandler_get_aio_context(), qmp_dispatcher_co); +- qemu_coroutine_yield(); + } + qatomic_set(&qmp_dispatcher_co, NULL); + } +diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c +index 555528b..176b549 100644 +--- a/qapi/qmp-dispatch.c ++++ b/qapi/qmp-dispatch.c +@@ -206,9 +206,31 @@ QDict *coroutine_mixed_fn qmp_dispatch(const QmpCommandList *cmds, QObject *requ + assert(!(oob && qemu_in_coroutine())); + assert(monitor_cur() == NULL); + if (!!(cmd->options & QCO_COROUTINE) == qemu_in_coroutine()) { ++ if (qemu_in_coroutine()) { ++ /* ++ * Move the coroutine from iohandler_ctx to qemu_aio_context for ++ * executing the command handler so that it can make progress if it ++ * involves an AIO_WAIT_WHILE(). ++ */ ++ aio_co_schedule(qemu_get_aio_context(), qemu_coroutine_self()); ++ qemu_coroutine_yield(); ++ } ++ + monitor_set_cur(qemu_coroutine_self(), cur_mon); + cmd->fn(args, &ret, &err); + monitor_set_cur(qemu_coroutine_self(), NULL); ++ ++ if (qemu_in_coroutine()) { ++ /* ++ * Yield and reschedule so the main loop stays responsive. ++ * ++ * Move back to iohandler_ctx so that nested event loops for ++ * qemu_aio_context don't start new monitor commands. ++ */ ++ aio_co_schedule(iohandler_get_aio_context(), ++ qemu_coroutine_self()); ++ qemu_coroutine_yield(); ++ } + } else { + /* + * Actual context doesn't match the one the command needs. +@@ -232,7 +254,7 @@ QDict *coroutine_mixed_fn qmp_dispatch(const QmpCommandList *cmds, QObject *requ + .errp = &err, + .co = qemu_coroutine_self(), + }; +- aio_bh_schedule_oneshot(qemu_get_aio_context(), do_qmp_dispatch_bh, ++ aio_bh_schedule_oneshot(iohandler_get_aio_context(), do_qmp_dispatch_bh, + &data); + qemu_coroutine_yield(); + } +diff --git a/tests/qemu-iotests/060.out b/tests/qemu-iotests/060.out +index 329977d..a37bf44 100644 +--- a/tests/qemu-iotests/060.out ++++ b/tests/qemu-iotests/060.out +@@ -421,8 +421,8 @@ QMP_VERSION + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_IMAGE_CORRUPTED", "data": {"device": "none0", "msg": "Preventing invalid write on metadata (overlaps with refcount table)", "offset": 65536, "node-name": "drive", "fatal": true, "size": 65536}} + write failed: Input/output error + {"return": ""} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + === Testing incoming inactive corrupted image === + +@@ -432,8 +432,8 @@ QMP_VERSION + qcow2: Image is corrupt: L2 table offset 0x2a2a2a00 unaligned (L1 index: 0); further non-fatal corruption events will be suppressed + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_IMAGE_CORRUPTED", "data": {"device": "", "msg": "L2 table offset 0x2a2a2a00 unaligned (L1 index: 0)", "node-name": "drive", "fatal": false}} + {"return": ""} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + corrupt: false + *** done +diff --git a/tests/qemu-iotests/071.out b/tests/qemu-iotests/071.out +index bca0c02..a2923b0 100644 +--- a/tests/qemu-iotests/071.out ++++ b/tests/qemu-iotests/071.out +@@ -45,8 +45,8 @@ QMP_VERSION + {"return": {}} + read failed: Input/output error + {"return": ""} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + + === Testing blkverify on existing block device === +@@ -84,9 +84,9 @@ wrote 512/512 bytes at offset 0 + {"return": ""} + read failed: Input/output error + {"return": ""} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + QEMU_PROG: Failed to flush the L2 table cache: Input/output error + QEMU_PROG: Failed to flush the refcount block cache: Input/output error ++{"return": {}} + + *** done +diff --git a/tests/qemu-iotests/081.out b/tests/qemu-iotests/081.out +index 615c083..aba85ea 100644 +--- a/tests/qemu-iotests/081.out ++++ b/tests/qemu-iotests/081.out +@@ -35,8 +35,8 @@ QMP_VERSION + read 10485760/10485760 bytes at offset 0 + 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + {"return": ""} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + + == using quorum rewrite corrupted mode == +@@ -67,8 +67,8 @@ QMP_VERSION + read 10485760/10485760 bytes at offset 0 + 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + {"return": ""} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + -- checking that the image has been corrected -- + read 10485760/10485760 bytes at offset 0 +@@ -106,8 +106,8 @@ QMP_VERSION + {"return": {}} + {"return": {}} + {"return": {}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + Testing: + QMP_VERSION +@@ -115,8 +115,8 @@ QMP_VERSION + {"return": {}} + {"return": {}} + {"error": {"class": "GenericError", "desc": "Cannot add a child to a quorum in blkverify mode"}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + + == dynamically removing a child from a quorum == +@@ -125,31 +125,31 @@ QMP_VERSION + {"return": {}} + {"return": {}} + {"return": {}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + Testing: + QMP_VERSION + {"return": {}} + {"return": {}} + {"error": {"class": "GenericError", "desc": "The number of children cannot be lower than the vote threshold 2"}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + Testing: + QMP_VERSION + {"return": {}} + {"error": {"class": "GenericError", "desc": "blkverify=on can only be set if there are exactly two files and vote-threshold is 2"}} + {"error": {"class": "GenericError", "desc": "Cannot find device='drive0-quorum' nor node-name='drive0-quorum'"}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + Testing: + QMP_VERSION + {"return": {}} + {"return": {}} + {"error": {"class": "GenericError", "desc": "The number of children cannot be lower than the vote threshold 2"}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + *** done +diff --git a/tests/qemu-iotests/087.out b/tests/qemu-iotests/087.out +index e1c23a6..97b6d80 100644 +--- a/tests/qemu-iotests/087.out ++++ b/tests/qemu-iotests/087.out +@@ -7,8 +7,8 @@ Testing: + QMP_VERSION + {"return": {}} + {"error": {"class": "GenericError", "desc": "'node-name' must be specified for the root node"}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + + === Duplicate ID === +@@ -18,8 +18,8 @@ QMP_VERSION + {"return": {}} + {"error": {"class": "GenericError", "desc": "node-name=disk is conflicting with a device id"}} + {"error": {"class": "GenericError", "desc": "Duplicate nodes with node-name='test-node'"}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + + === aio=native without O_DIRECT === +@@ -28,8 +28,8 @@ Testing: + QMP_VERSION + {"return": {}} + {"error": {"class": "GenericError", "desc": "aio=native was specified, but it requires cache.direct=on, which was not specified."}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + + === Encrypted image QCow === +@@ -40,8 +40,8 @@ QMP_VERSION + {"return": {}} + {"return": {}} + {"error": {"class": "GenericError", "desc": "Use of AES-CBC encrypted IMGFMT images is no longer supported in system emulators"}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + + === Encrypted image LUKS === +@@ -52,8 +52,8 @@ QMP_VERSION + {"return": {}} + {"return": {}} + {"return": {}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + + === Missing driver === +@@ -63,7 +63,7 @@ Testing: -S + QMP_VERSION + {"return": {}} + {"error": {"class": "GenericError", "desc": "Parameter 'driver' is missing"}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + *** done +diff --git a/tests/qemu-iotests/108.out b/tests/qemu-iotests/108.out +index b5401d7..b9c876b 100644 +--- a/tests/qemu-iotests/108.out ++++ b/tests/qemu-iotests/108.out +@@ -173,8 +173,8 @@ OK: Reftable is where we expect it + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "create"}} + {"return": {}} + { "execute": "quit" } +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + wrote 65536/65536 bytes at offset 0 + 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +diff --git a/tests/qemu-iotests/109 b/tests/qemu-iotests/109 +index e207a55..0fb580f 100755 +--- a/tests/qemu-iotests/109 ++++ b/tests/qemu-iotests/109 +@@ -57,13 +57,13 @@ run_qemu() + _launch_qemu -drive file="${source_img}",format=raw,cache=${CACHEMODE},aio=${AIOMODE},id=src + _send_qemu_cmd $QEMU_HANDLE "{ 'execute': 'qmp_capabilities' }" "return" + +- _send_qemu_cmd $QEMU_HANDLE \ ++ capture_events="$qmp_event" _send_qemu_cmd $QEMU_HANDLE \ + "{'execute':'drive-mirror', 'arguments':{ + 'device': 'src', 'target': '$raw_img', $qmp_format + 'mode': 'existing', 'sync': 'full'}}" \ + "return" + +- _send_qemu_cmd $QEMU_HANDLE '' "$qmp_event" ++ capture_events="$qmp_event JOB_STATUS_CHANGE" _wait_event $QEMU_HANDLE "$qmp_event" + if test "$qmp_event" = BLOCK_JOB_ERROR; then + _send_qemu_cmd $QEMU_HANDLE '' '"status": "null"' + fi +diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out +index 965c9a6..3ae8552 100644 +--- a/tests/qemu-iotests/109.out ++++ b/tests/qemu-iotests/109.out +@@ -7,7 +7,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE + { 'execute': 'qmp_capabilities' } + {"return": {}} + {'execute':'drive-mirror', 'arguments':{ +- 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', ++ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', + 'mode': 'existing', 'sync': 'full'}} + WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +@@ -23,8 +23,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed + {"execute":"query-block-jobs"} + {"return": []} + {"execute":"quit"} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + read 512/512 bytes at offset 0 + 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + { 'execute': 'qmp_capabilities' } +@@ -35,12 +35,10 @@ read 512/512 bytes at offset 0 + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} + {"execute":"query-block-jobs"} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} + {"execute":"quit"} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +@@ -50,6 +48,7 @@ read 512/512 bytes at offset 0 + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} ++{"return": {}} + Images are identical. + + === Writing a qcow2 header into raw === +@@ -59,7 +58,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE + { 'execute': 'qmp_capabilities' } + {"return": {}} + {'execute':'drive-mirror', 'arguments':{ +- 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', ++ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', + 'mode': 'existing', 'sync': 'full'}} + WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +@@ -75,8 +74,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed + {"execute":"query-block-jobs"} + {"return": []} + {"execute":"quit"} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + read 512/512 bytes at offset 0 + 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + { 'execute': 'qmp_capabilities' } +@@ -87,12 +86,10 @@ read 512/512 bytes at offset 0 + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}} + {"execute":"query-block-jobs"} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 197120, "offset": 197120, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} + {"execute":"quit"} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +@@ -102,6 +99,7 @@ read 512/512 bytes at offset 0 + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} ++{"return": {}} + Images are identical. + + === Writing a qed header into raw === +@@ -111,7 +109,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE + { 'execute': 'qmp_capabilities' } + {"return": {}} + {'execute':'drive-mirror', 'arguments':{ +- 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', ++ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', + 'mode': 'existing', 'sync': 'full'}} + WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +@@ -127,8 +125,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed + {"execute":"query-block-jobs"} + {"return": []} + {"execute":"quit"} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + read 512/512 bytes at offset 0 + 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + { 'execute': 'qmp_capabilities' } +@@ -139,12 +137,10 @@ read 512/512 bytes at offset 0 + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} + {"execute":"query-block-jobs"} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} + {"execute":"quit"} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +@@ -154,6 +150,7 @@ read 512/512 bytes at offset 0 + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} ++{"return": {}} + Images are identical. + + === Writing a vdi header into raw === +@@ -163,7 +160,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE + { 'execute': 'qmp_capabilities' } + {"return": {}} + {'execute':'drive-mirror', 'arguments':{ +- 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', ++ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', + 'mode': 'existing', 'sync': 'full'}} + WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +@@ -179,8 +176,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed + {"execute":"query-block-jobs"} + {"return": []} + {"execute":"quit"} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + read 512/512 bytes at offset 0 + 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + { 'execute': 'qmp_capabilities' } +@@ -191,12 +188,10 @@ read 512/512 bytes at offset 0 + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} + {"execute":"query-block-jobs"} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} + {"execute":"quit"} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +@@ -206,6 +201,7 @@ read 512/512 bytes at offset 0 + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} ++{"return": {}} + Images are identical. + + === Writing a vmdk header into raw === +@@ -215,7 +211,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE + { 'execute': 'qmp_capabilities' } + {"return": {}} + {'execute':'drive-mirror', 'arguments':{ +- 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', ++ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', + 'mode': 'existing', 'sync': 'full'}} + WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +@@ -231,8 +227,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed + {"execute":"query-block-jobs"} + {"return": []} + {"execute":"quit"} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + read 512/512 bytes at offset 0 + 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + { 'execute': 'qmp_capabilities' } +@@ -243,12 +239,10 @@ read 512/512 bytes at offset 0 + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} + {"execute":"query-block-jobs"} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 65536, "offset": 65536, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} + {"execute":"quit"} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +@@ -258,6 +252,7 @@ read 512/512 bytes at offset 0 + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} ++{"return": {}} + Images are identical. + + === Writing a vpc header into raw === +@@ -267,7 +262,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE + { 'execute': 'qmp_capabilities' } + {"return": {}} + {'execute':'drive-mirror', 'arguments':{ +- 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', ++ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', + 'mode': 'existing', 'sync': 'full'}} + WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +@@ -283,8 +278,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed + {"execute":"query-block-jobs"} + {"return": []} + {"execute":"quit"} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + read 512/512 bytes at offset 0 + 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + { 'execute': 'qmp_capabilities' } +@@ -295,12 +290,10 @@ read 512/512 bytes at offset 0 + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} + {"execute":"query-block-jobs"} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} + {"execute":"quit"} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +@@ -310,6 +303,7 @@ read 512/512 bytes at offset 0 + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} ++{"return": {}} + Images are identical. + + === Copying sample image empty.bochs into raw === +@@ -318,7 +312,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE + { 'execute': 'qmp_capabilities' } + {"return": {}} + {'execute':'drive-mirror', 'arguments':{ +- 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', ++ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', + 'mode': 'existing', 'sync': 'full'}} + WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +@@ -334,8 +328,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed + {"execute":"query-block-jobs"} + {"return": []} + {"execute":"quit"} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + read 512/512 bytes at offset 0 + 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + { 'execute': 'qmp_capabilities' } +@@ -346,12 +340,10 @@ read 512/512 bytes at offset 0 + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} + {"execute":"query-block-jobs"} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} + {"execute":"quit"} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +@@ -361,6 +353,7 @@ read 512/512 bytes at offset 0 + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} ++{"return": {}} + Images are identical. + + === Copying sample image iotest-dirtylog-10G-4M.vhdx into raw === +@@ -369,7 +362,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE + { 'execute': 'qmp_capabilities' } + {"return": {}} + {'execute':'drive-mirror', 'arguments':{ +- 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', ++ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', + 'mode': 'existing', 'sync': 'full'}} + WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +@@ -385,8 +378,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed + {"execute":"query-block-jobs"} + {"return": []} + {"execute":"quit"} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + read 512/512 bytes at offset 0 + 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + { 'execute': 'qmp_capabilities' } +@@ -397,12 +390,10 @@ read 512/512 bytes at offset 0 + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}} + {"execute":"query-block-jobs"} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 31457280, "offset": 31457280, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} + {"execute":"quit"} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +@@ -412,6 +403,7 @@ read 512/512 bytes at offset 0 + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} ++{"return": {}} + Images are identical. + + === Copying sample image parallels-v1 into raw === +@@ -420,7 +412,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE + { 'execute': 'qmp_capabilities' } + {"return": {}} + {'execute':'drive-mirror', 'arguments':{ +- 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', ++ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', + 'mode': 'existing', 'sync': 'full'}} + WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +@@ -436,8 +428,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed + {"execute":"query-block-jobs"} + {"return": []} + {"execute":"quit"} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + read 512/512 bytes at offset 0 + 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + { 'execute': 'qmp_capabilities' } +@@ -448,12 +440,10 @@ read 512/512 bytes at offset 0 + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} + {"execute":"query-block-jobs"} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} + {"execute":"quit"} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +@@ -463,6 +453,7 @@ read 512/512 bytes at offset 0 + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} ++{"return": {}} + Images are identical. + + === Copying sample image simple-pattern.cloop into raw === +@@ -471,7 +462,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE + { 'execute': 'qmp_capabilities' } + {"return": {}} + {'execute':'drive-mirror', 'arguments':{ +- 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', ++ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', + 'mode': 'existing', 'sync': 'full'}} + WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +@@ -487,8 +478,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed + {"execute":"query-block-jobs"} + {"return": []} + {"execute":"quit"} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + read 512/512 bytes at offset 0 + 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + { 'execute': 'qmp_capabilities' } +@@ -499,12 +490,10 @@ read 512/512 bytes at offset 0 + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}} + {"execute":"query-block-jobs"} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2048, "offset": 2048, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} + {"execute":"quit"} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +@@ -514,6 +503,7 @@ read 512/512 bytes at offset 0 + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} ++{"return": {}} + Images are identical. + + === Write legitimate MBR into raw === +@@ -522,7 +512,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE + { 'execute': 'qmp_capabilities' } + {"return": {}} + {'execute':'drive-mirror', 'arguments':{ +- 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', ++ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', + 'mode': 'existing', 'sync': 'full'}} + WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. +@@ -530,12 +520,10 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} + {"execute":"query-block-jobs"} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} + {"execute":"quit"} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +@@ -545,6 +533,7 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} ++{"return": {}} + Images are identical. + { 'execute': 'qmp_capabilities' } + {"return": {}} +@@ -554,12 +543,10 @@ Images are identical. + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} + {"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} + {"execute":"query-block-jobs"} + {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} + {"execute":"quit"} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "standby", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} +@@ -569,5 +556,6 @@ Images are identical. + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} ++{"return": {}} + Images are identical. + *** done +diff --git a/tests/qemu-iotests/117.out b/tests/qemu-iotests/117.out +index 735ffd2..1cea9e0 100644 +--- a/tests/qemu-iotests/117.out ++++ b/tests/qemu-iotests/117.out +@@ -18,8 +18,8 @@ wrote 65536/65536 bytes at offset 0 + 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + {"return": ""} + { 'execute': 'quit' } +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + No errors were found on the image. + read 65536/65536 bytes at offset 0 + 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +diff --git a/tests/qemu-iotests/120.out b/tests/qemu-iotests/120.out +index 0744c1f..35d84a5 100644 +--- a/tests/qemu-iotests/120.out ++++ b/tests/qemu-iotests/120.out +@@ -5,8 +5,8 @@ QMP_VERSION + wrote 65536/65536 bytes at offset 0 + 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + {"return": ""} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + read 65536/65536 bytes at offset 0 + 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + read 65536/65536 bytes at offset 0 +diff --git a/tests/qemu-iotests/127.out b/tests/qemu-iotests/127.out +index 1685c48..dd8c4a8 100644 +--- a/tests/qemu-iotests/127.out ++++ b/tests/qemu-iotests/127.out +@@ -28,6 +28,6 @@ wrote 42/42 bytes at offset 0 + { 'execute': 'quit' } + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "mirror"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "mirror"}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + *** done +diff --git a/tests/qemu-iotests/140.out b/tests/qemu-iotests/140.out +index 312f76d..3286644 100644 +--- a/tests/qemu-iotests/140.out ++++ b/tests/qemu-iotests/140.out +@@ -19,6 +19,6 @@ read 65536/65536 bytes at offset 0 + qemu-io: can't open device nbd+unix:///drv?socket=SOCK_DIR/nbd: Requested export not available + server reported: export 'drv' not present + { 'execute': 'quit' } +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + *** done +diff --git a/tests/qemu-iotests/143.out b/tests/qemu-iotests/143.out +index 9ec5888..d6afa32 100644 +--- a/tests/qemu-iotests/143.out ++++ b/tests/qemu-iotests/143.out +@@ -10,6 +10,6 @@ server reported: export 'no_such_export' not present + qemu-io: can't open device nbd+unix:///aa--aa1?socket=SOCK_DIR/nbd: Requested export not available + server reported: export 'aa--aa...' not present + { 'execute': 'quit' } +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + *** done +diff --git a/tests/qemu-iotests/156.out b/tests/qemu-iotests/156.out +index 4a22f0c..07e5e83 100644 +--- a/tests/qemu-iotests/156.out ++++ b/tests/qemu-iotests/156.out +@@ -72,8 +72,8 @@ read 65536/65536 bytes at offset 196608 + {"return": ""} + + { 'execute': 'quit' } +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + read 65536/65536 bytes at offset 0 + 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +diff --git a/tests/qemu-iotests/176.out b/tests/qemu-iotests/176.out +index 9d09b60..45e9153 100644 +--- a/tests/qemu-iotests/176.out ++++ b/tests/qemu-iotests/176.out +@@ -169,8 +169,8 @@ QMP_VERSION + {"return": {}} + {"return": {}} + {"return": {}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + wrote 196608/196608 bytes at offset 2147287040 + 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + wrote 131072/131072 bytes at offset 2147352576 +@@ -206,8 +206,8 @@ QMP_VERSION + {"return": {}} + {"return": {}} + {"return": {"sha256": HASH}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + === Test pass bitmap.1 === + +@@ -218,8 +218,8 @@ QMP_VERSION + {"return": {}} + {"return": {}} + {"return": {}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + wrote 196608/196608 bytes at offset 2147287040 + 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + wrote 131072/131072 bytes at offset 2147352576 +@@ -256,8 +256,8 @@ QMP_VERSION + {"return": {}} + {"return": {}} + {"return": {"sha256": HASH}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + === Test pass bitmap.2 === + +@@ -268,8 +268,8 @@ QMP_VERSION + {"return": {}} + {"return": {}} + {"return": {}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + wrote 196608/196608 bytes at offset 2147287040 + 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + wrote 131072/131072 bytes at offset 2147352576 +@@ -306,8 +306,8 @@ QMP_VERSION + {"return": {}} + {"return": {}} + {"return": {"sha256": HASH}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + === Test pass bitmap.3 === + +@@ -318,8 +318,8 @@ QMP_VERSION + {"return": {}} + {"return": {}} + {"return": {}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + wrote 196608/196608 bytes at offset 2147287040 + 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + wrote 131072/131072 bytes at offset 2147352576 +@@ -353,6 +353,6 @@ QMP_VERSION + {"return": {}} + {"return": {}} + {"return": {"sha256": HASH}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + *** done +diff --git a/tests/qemu-iotests/182.out b/tests/qemu-iotests/182.out +index 57f7265..83fc1a4 100644 +--- a/tests/qemu-iotests/182.out ++++ b/tests/qemu-iotests/182.out +@@ -53,6 +53,6 @@ Formatting 'TEST_DIR/t.qcow2.overlay', fmt=qcow2 cluster_size=65536 extended_l2= + {'execute': 'qmp_capabilities'} + {"return": {}} + {'execute': 'quit'} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + *** done +diff --git a/tests/qemu-iotests/183.out b/tests/qemu-iotests/183.out +index fd9c2e5..51aa41c 100644 +--- a/tests/qemu-iotests/183.out ++++ b/tests/qemu-iotests/183.out +@@ -53,11 +53,11 @@ wrote 65536/65536 bytes at offset 1048576 + === Shut down and check image === + + {"execute":"quit"} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + {"return": {}} + {"execute":"quit"} +-{"return": {}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + No errors were found on the image. + No errors were found on the image. + wrote 65536/65536 bytes at offset 1048576 +diff --git a/tests/qemu-iotests/184.out b/tests/qemu-iotests/184.out +index 77e5489..e8f631f 100644 +--- a/tests/qemu-iotests/184.out ++++ b/tests/qemu-iotests/184.out +@@ -90,10 +90,6 @@ Testing: + ] + } + { +- "return": { +- } +-} +-{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP +@@ -104,6 +100,10 @@ Testing: + "reason": "host-qmp-quit" + } + } ++{ ++ "return": { ++ } ++} + + + == property changes in ThrottleGroup == +@@ -170,10 +170,6 @@ Testing: + } + } + { +- "return": { +- } +-} +-{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP +@@ -184,6 +180,10 @@ Testing: + "reason": "host-qmp-quit" + } + } ++{ ++ "return": { ++ } ++} + + + == object creation/set errors == +@@ -212,10 +212,6 @@ Testing: + } + } + { +- "return": { +- } +-} +-{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP +@@ -226,6 +222,10 @@ Testing: + "reason": "host-qmp-quit" + } + } ++{ ++ "return": { ++ } ++} + + + == don't specify group == +@@ -248,10 +248,6 @@ Testing: + } + } + { +- "return": { +- } +-} +-{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP +@@ -262,6 +258,10 @@ Testing: + "reason": "host-qmp-quit" + } + } ++{ ++ "return": { ++ } ++} + + + *** done +diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185 +index 2ae0a85..17489fb 100755 +--- a/tests/qemu-iotests/185 ++++ b/tests/qemu-iotests/185 +@@ -344,14 +344,14 @@ wait_for_job_and_quit() { + + sleep 1 + ++ # List of expected events ++ capture_events='BLOCK_JOB_CANCELLED JOB_STATUS_CHANGE SHUTDOWN' ++ + _send_qemu_cmd $h \ + '{"execute": "quit"}' \ + 'return' + +- # List of expected events +- capture_events='BLOCK_JOB_CANCELLED JOB_STATUS_CHANGE SHUTDOWN' + _wait_event $h 'SHUTDOWN' +- QEMU_EVENTS= # Ignore all JOB_STATUS_CHANGE events that came before SHUTDOWN + _wait_event $h 'JOB_STATUS_CHANGE' # standby + _wait_event $h 'JOB_STATUS_CHANGE' # ready + _wait_event $h 'JOB_STATUS_CHANGE' # standby +diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out +index 7292c26..6af0953 100644 +--- a/tests/qemu-iotests/185.out ++++ b/tests/qemu-iotests/185.out +@@ -40,9 +40,16 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off comp + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} + {"return": {}} + { 'execute': 'quit' } +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "disk"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 524288, "speed": 65536, "type": "commit"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "disk"}} ++{"return": {}} + + === Start active commit job and exit qemu === + +@@ -56,9 +63,16 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off comp + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} + {"return": {}} + { 'execute': 'quit' } +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "disk"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "commit"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "disk"}} ++{"return": {}} + + === Start mirror job and exit qemu === + +@@ -75,9 +89,16 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 extended_l2=off + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} + {"return": {}} + { 'execute': 'quit' } +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "disk"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "disk"}} ++{"return": {}} + + === Start backup job and exit qemu === + +@@ -97,9 +118,16 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 extended_l2=off + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} + {"return": {}} + { 'execute': 'quit' } +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "disk"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 65536, "speed": 65536, "type": "backup"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "disk"}} ++{"return": {}} + + === Start streaming job and exit qemu === + +@@ -112,9 +140,16 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 cluster_size=65536 extended_l2=off + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} + {"return": {}} + { 'execute': 'quit' } +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "disk"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 524288, "speed": 65536, "type": "stream"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "disk"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "disk"}} ++{"return": {}} + No errors were found on the image. + + === Start mirror to throttled QSD and exit qemu === +diff --git a/tests/qemu-iotests/191.out b/tests/qemu-iotests/191.out +index ea88777..c3309e4 100644 +--- a/tests/qemu-iotests/191.out ++++ b/tests/qemu-iotests/191.out +@@ -379,10 +379,6 @@ wrote 65536/65536 bytes at offset 1048576 + } + { 'execute': 'quit' } + { +- "return": { +- } +-} +-{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP +@@ -393,6 +389,10 @@ wrote 65536/65536 bytes at offset 1048576 + "reason": "host-qmp-quit" + } + } ++{ ++ "return": { ++ } ++} + image: TEST_DIR/t.IMGFMT + file format: IMGFMT + virtual size: 64 MiB (67108864 bytes) +@@ -797,10 +797,6 @@ wrote 65536/65536 bytes at offset 1048576 + } + { 'execute': 'quit' } + { +- "return": { +- } +-} +-{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP +@@ -811,6 +807,10 @@ wrote 65536/65536 bytes at offset 1048576 + "reason": "host-qmp-quit" + } + } ++{ ++ "return": { ++ } ++} + image: TEST_DIR/t.IMGFMT + file format: IMGFMT + virtual size: 64 MiB (67108864 bytes) +diff --git a/tests/qemu-iotests/195.out b/tests/qemu-iotests/195.out +index ec84df5..91717d3 100644 +--- a/tests/qemu-iotests/195.out ++++ b/tests/qemu-iotests/195.out +@@ -18,10 +18,6 @@ Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,backing.node-name=mid + } + } + { +- "return": { +- } +-} +-{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP +@@ -32,6 +28,10 @@ Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,backing.node-name=mid + "reason": "host-qmp-quit" + } + } ++{ ++ "return": { ++ } ++} + + image: TEST_DIR/t.IMGFMT.mid + file format: IMGFMT +@@ -56,10 +56,6 @@ Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,node-name=top + } + } + { +- "return": { +- } +-} +-{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP +@@ -70,6 +66,10 @@ Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,node-name=top + "reason": "host-qmp-quit" + } + } ++{ ++ "return": { ++ } ++} + + image: TEST_DIR/t.IMGFMT + file format: IMGFMT +diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out +index e5e7f42..5f5b42e 100644 +--- a/tests/qemu-iotests/223.out ++++ b/tests/qemu-iotests/223.out +@@ -11,8 +11,8 @@ QMP_VERSION + {"return": {}} + {"return": {}} + {"return": {}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + + === Write part of the file under active bitmap === +@@ -145,14 +145,14 @@ read 2097152/2097152 bytes at offset 2097152 + + {"execute":"nbd-server-remove", + "arguments":{"name":"n"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n"}} + {"return": {}} + {"execute":"nbd-server-remove", + "arguments":{"name":"n2"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n2"}} + {"return": {}} + {"execute":"nbd-server-remove", + "arguments":{"name":"n2"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n2"}} + {"error": {"class": "GenericError", "desc": "Export 'n2' is not found"}} + {"execute":"nbd-server-stop"} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n3"}} +@@ -267,14 +267,14 @@ read 2097152/2097152 bytes at offset 2097152 + + {"execute":"nbd-server-remove", + "arguments":{"name":"n"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n"}} + {"return": {}} + {"execute":"nbd-server-remove", + "arguments":{"name":"n2"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n"}} ++{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n2"}} + {"return": {}} + {"execute":"nbd-server-remove", + "arguments":{"name":"n2"}} +-{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n2"}} + {"error": {"class": "GenericError", "desc": "Export 'n2' is not found"}} + {"execute":"nbd-server-stop"} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n3"}} +@@ -282,8 +282,8 @@ read 2097152/2097152 bytes at offset 2097152 + {"execute":"nbd-server-stop"} + {"error": {"class": "GenericError", "desc": "NBD server not running"}} + {"execute":"quit"} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + + === Use qemu-nbd as server === + +diff --git a/tests/qemu-iotests/227.out b/tests/qemu-iotests/227.out +index a947b1a..d6a1d4e 100644 +--- a/tests/qemu-iotests/227.out ++++ b/tests/qemu-iotests/227.out +@@ -55,10 +55,6 @@ Testing: -drive driver=null-co,read-zeroes=on,if=virtio + ] + } + { +- "return": { +- } +-} +-{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP +@@ -69,6 +65,10 @@ Testing: -drive driver=null-co,read-zeroes=on,if=virtio + "reason": "host-qmp-quit" + } + } ++{ ++ "return": { ++ } ++} + + + === blockstats with -drive if=none === +@@ -125,10 +125,6 @@ Testing: -drive driver=null-co,if=none + ] + } + { +- "return": { +- } +-} +-{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP +@@ -139,6 +135,10 @@ Testing: -drive driver=null-co,if=none + "reason": "host-qmp-quit" + } + } ++{ ++ "return": { ++ } ++} + + + === blockstats with -blockdev === +@@ -156,10 +156,6 @@ Testing: -blockdev driver=null-co,node-name=null + ] + } + { +- "return": { +- } +-} +-{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP +@@ -170,6 +166,10 @@ Testing: -blockdev driver=null-co,node-name=null + "reason": "host-qmp-quit" + } + } ++{ ++ "return": { ++ } ++} + + + === blockstats with -blockdev and -device === +@@ -227,10 +227,6 @@ Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-b + ] + } + { +- "return": { +- } +-} +-{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP +@@ -241,5 +237,9 @@ Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-b + "reason": "host-qmp-quit" + } + } ++{ ++ "return": { ++ } ++} + + *** done +diff --git a/tests/qemu-iotests/247.out b/tests/qemu-iotests/247.out +index e909e83..7d252e7 100644 +--- a/tests/qemu-iotests/247.out ++++ b/tests/qemu-iotests/247.out +@@ -17,6 +17,6 @@ QMP_VERSION + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 134217728, "offset": 134217728, "speed": 0, "type": "commit"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + *** done +diff --git a/tests/qemu-iotests/273.out b/tests/qemu-iotests/273.out +index 6a74a81..71843f0 100644 +--- a/tests/qemu-iotests/273.out ++++ b/tests/qemu-iotests/273.out +@@ -283,10 +283,6 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev + } + } + { +- "return": { +- } +-} +-{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP +@@ -297,5 +293,9 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev + "reason": "host-qmp-quit" + } + } ++{ ++ "return": { ++ } ++} + + *** done +diff --git a/tests/qemu-iotests/308 b/tests/qemu-iotests/308 +index de12b2b..ea81dc4 100755 +--- a/tests/qemu-iotests/308 ++++ b/tests/qemu-iotests/308 +@@ -77,6 +77,7 @@ fuse_export_add() + # $1: Export ID + fuse_export_del() + { ++ capture_events="BLOCK_EXPORT_DELETED" \ + _send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'block-export-del', + 'arguments': { +@@ -84,8 +85,7 @@ fuse_export_del() + } }" \ + 'return' + +- _send_qemu_cmd $QEMU_HANDLE \ +- '' \ ++ _wait_event $QEMU_HANDLE \ + 'BLOCK_EXPORT_DELETED' + } + +diff --git a/tests/qemu-iotests/308.out b/tests/qemu-iotests/308.out +index d576713..e5e2336 100644 +--- a/tests/qemu-iotests/308.out ++++ b/tests/qemu-iotests/308.out +@@ -165,9 +165,9 @@ OK: Post-truncate image size is as expected + + === Tear down === + {'execute': 'quit'} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export-mp"}} ++{"return": {}} + + === Compare copy with original === + Images are identical. +@@ -201,9 +201,9 @@ wrote 67108864/67108864 bytes at offset 0 + read 67108864/67108864 bytes at offset 0 + 64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + {'execute': 'quit'} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export"}} ++{"return": {}} + read 67108864/67108864 bytes at offset 0 + 64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + *** done +diff --git a/tests/qemu-iotests/tests/file-io-error b/tests/qemu-iotests/tests/file-io-error +index 88ee5f6..fb8db73 100755 +--- a/tests/qemu-iotests/tests/file-io-error ++++ b/tests/qemu-iotests/tests/file-io-error +@@ -99,13 +99,12 @@ echo + $QEMU_IO -f file -c 'write 0 64M' "$TEST_DIR/fuse-export" | _filter_qemu_io + echo + +-_send_qemu_cmd $QEMU_HANDLE \ ++capture_events=BLOCK_EXPORT_DELETED _send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'block-export-del', + 'arguments': {'id': 'exp0'}}" \ + 'return' + +-_send_qemu_cmd $QEMU_HANDLE \ +- '' \ ++_wait_event $QEMU_HANDLE \ + 'BLOCK_EXPORT_DELETED' + + _send_qemu_cmd $QEMU_HANDLE \ +diff --git a/tests/qemu-iotests/tests/iothreads-resize.out b/tests/qemu-iotests/tests/iothreads-resize.out +index 2ca5a9d..2967ac8 100644 +--- a/tests/qemu-iotests/tests/iothreads-resize.out ++++ b/tests/qemu-iotests/tests/iothreads-resize.out +@@ -3,8 +3,8 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 + QMP_VERSION + {"return": {}} + {"return": {}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} ++{"return": {}} + image: TEST_DIR/t.IMGFMT + file format: IMGFMT + virtual size: 128 MiB (134217728 bytes) +diff --git a/tests/qemu-iotests/tests/qsd-jobs.out b/tests/qemu-iotests/tests/qsd-jobs.out +index c1bc9b8..aa6b6d1 100644 +--- a/tests/qemu-iotests/tests/qsd-jobs.out ++++ b/tests/qemu-iotests/tests/qsd-jobs.out +@@ -7,8 +7,8 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/ + QMP_VERSION + {"return": {}} + {"return": {}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} ++{"return": {}} + + === Streaming can't get permission on base node === + +@@ -17,6 +17,6 @@ QMP_VERSION + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} + {"error": {"class": "GenericError", "desc": "Permission conflict on node 'fmt_base': permissions 'write' are both required by an unnamed block device (uses node 'fmt_base' as 'root' child) and unshared by stream job 'job0' (uses node 'fmt_base' as 'intermediate node' child)."}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "export1"}} ++{"return": {}} + *** done +-- +1.8.3.1 + diff --git a/0062-qtest-bump-aspeed_smc-test-timeout-to-6-minutes.patch b/0062-qtest-bump-aspeed_smc-test-timeout-to-6-minutes.patch new file mode 100644 index 0000000000000000000000000000000000000000..edb5db45cb45dc177219625ebaf8789f1a0944dc --- /dev/null +++ b/0062-qtest-bump-aspeed_smc-test-timeout-to-6-minutes.patch @@ -0,0 +1,38 @@ +From ce34d02f91a10b43940e8ed186c9c334cb571a15 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= +Date: Fri, 15 Dec 2023 08:03:51 +0100 +Subject: [PATCH 068/293] qtest: bump aspeed_smc-test timeout to 6 minutes +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +On a loaded system with --enable-debug, this test can take longer than +5 minutes. Raising the timeout to 6 minutes gives greater headroom for +such situations. + +Signed-off-by: Daniel P. Berrangé +[thuth: Increase the timeout to 6 minutes for very loaded systems] +Signed-off-by: Thomas Huth +Message-Id: <20231215070357.10888-11-thuth@redhat.com> +Signed-off-by: Alex Bennée +(cherry picked from commit e8a12fe31f776c60fec993513cd1b1e66c2b8e29) +Signed-off-by: Michael Tokarev +(Mjt: context fixup in tests/qtest/meson.build) +--- + tests/qtest/meson.build | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build +index 47dabf9..fc14ae9 100644 +--- a/tests/qtest/meson.build ++++ b/tests/qtest/meson.build +@@ -1,5 +1,6 @@ + slow_qtests = { + 'ahci-test' : 60, ++ 'aspeed_smc-test': 360, + 'bios-tables-test' : 120, + 'boot-serial-test' : 60, + 'migration-test' : 150, +-- +1.8.3.1 + diff --git a/0063-target-xtensa-fix-OOB-TLB-entry-access.patch b/0063-target-xtensa-fix-OOB-TLB-entry-access.patch new file mode 100644 index 0000000000000000000000000000000000000000..20333fed6896c3dccf2aeb994573ab09b38f38e9 --- /dev/null +++ b/0063-target-xtensa-fix-OOB-TLB-entry-access.patch @@ -0,0 +1,140 @@ +From 553e53b4421ed5b671dc79ab32e82cf7ba9ad229 Mon Sep 17 00:00:00 2001 +From: Max Filippov +Date: Fri, 15 Dec 2023 04:03:07 -0800 +Subject: [PATCH 069/293] target/xtensa: fix OOB TLB entry access + +r[id]tlb[01], [iw][id]tlb opcodes use TLB way index passed in a register +by the guest. The host uses 3 bits of the index for ITLB indexing and 4 +bits for DTLB, but there's only 7 entries in the ITLB array and 10 in +the DTLB array, so a malicious guest may trigger out-of-bound access to +these arrays. + +Change split_tlb_entry_spec return type to bool to indicate whether TLB +way passed to it is valid. Change get_tlb_entry to return NULL in case +invalid TLB way is requested. Add assertion to xtensa_tlb_get_entry that +requested TLB way and entry indices are valid. Add checks to the +[rwi]tlb helpers that requested TLB way is valid and return 0 or do +nothing when it's not. + +Cc: qemu-stable@nongnu.org +Fixes: b67ea0cd7441 ("target-xtensa: implement memory protection options") +Signed-off-by: Max Filippov +Reviewed-by: Peter Maydell +Message-id: 20231215120307.545381-1-jcmvbkbc@gmail.com +Signed-off-by: Peter Maydell +(cherry picked from commit 604927e357c2b292c70826e4ce42574ad126ef32) +Signed-off-by: Michael Tokarev +--- + target/xtensa/mmu_helper.c | 47 ++++++++++++++++++++++++++++++++++------------ + 1 file changed, 35 insertions(+), 12 deletions(-) + +diff --git a/target/xtensa/mmu_helper.c b/target/xtensa/mmu_helper.c +index 12552a3..2fda4e8 100644 +--- a/target/xtensa/mmu_helper.c ++++ b/target/xtensa/mmu_helper.c +@@ -224,22 +224,31 @@ static void split_tlb_entry_spec_way(const CPUXtensaState *env, uint32_t v, + * Split TLB address into TLB way, entry index and VPN (with index). + * See ISA, 4.6.5.5 - 4.6.5.8 for the TLB addressing format + */ +-static void split_tlb_entry_spec(CPUXtensaState *env, uint32_t v, bool dtlb, +- uint32_t *vpn, uint32_t *wi, uint32_t *ei) ++static bool split_tlb_entry_spec(CPUXtensaState *env, uint32_t v, bool dtlb, ++ uint32_t *vpn, uint32_t *wi, uint32_t *ei) + { + if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { + *wi = v & (dtlb ? 0xf : 0x7); +- split_tlb_entry_spec_way(env, v, dtlb, vpn, *wi, ei); ++ if (*wi < (dtlb ? env->config->dtlb.nways : env->config->itlb.nways)) { ++ split_tlb_entry_spec_way(env, v, dtlb, vpn, *wi, ei); ++ return true; ++ } else { ++ return false; ++ } + } else { + *vpn = v & REGION_PAGE_MASK; + *wi = 0; + *ei = (v >> 29) & 0x7; ++ return true; + } + } + + static xtensa_tlb_entry *xtensa_tlb_get_entry(CPUXtensaState *env, bool dtlb, + unsigned wi, unsigned ei) + { ++ const xtensa_tlb *tlb = dtlb ? &env->config->dtlb : &env->config->itlb; ++ ++ assert(wi < tlb->nways && ei < tlb->way_size[wi]); + return dtlb ? + env->dtlb[wi] + ei : + env->itlb[wi] + ei; +@@ -252,11 +261,14 @@ static xtensa_tlb_entry *get_tlb_entry(CPUXtensaState *env, + uint32_t wi; + uint32_t ei; + +- split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei); +- if (pwi) { +- *pwi = wi; ++ if (split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei)) { ++ if (pwi) { ++ *pwi = wi; ++ } ++ return xtensa_tlb_get_entry(env, dtlb, wi, ei); ++ } else { ++ return NULL; + } +- return xtensa_tlb_get_entry(env, dtlb, wi, ei); + } + + static void xtensa_tlb_set_entry_mmu(const CPUXtensaState *env, +@@ -482,7 +494,12 @@ uint32_t HELPER(rtlb0)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) + if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { + uint32_t wi; + const xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, &wi); +- return (entry->vaddr & get_vpn_mask(env, dtlb, wi)) | entry->asid; ++ ++ if (entry) { ++ return (entry->vaddr & get_vpn_mask(env, dtlb, wi)) | entry->asid; ++ } else { ++ return 0; ++ } + } else { + return v & REGION_PAGE_MASK; + } +@@ -491,7 +508,12 @@ uint32_t HELPER(rtlb0)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) + uint32_t HELPER(rtlb1)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) + { + const xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, NULL); +- return entry->paddr | entry->attr; ++ ++ if (entry) { ++ return entry->paddr | entry->attr; ++ } else { ++ return 0; ++ } + } + + void HELPER(itlb)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) +@@ -499,7 +521,7 @@ void HELPER(itlb)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) + if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { + uint32_t wi; + xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, &wi); +- if (entry->variable && entry->asid) { ++ if (entry && entry->variable && entry->asid) { + tlb_flush_page(env_cpu(env), entry->vaddr); + entry->asid = 0; + } +@@ -537,8 +559,9 @@ void HELPER(wtlb)(CPUXtensaState *env, uint32_t p, uint32_t v, uint32_t dtlb) + uint32_t vpn; + uint32_t wi; + uint32_t ei; +- split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei); +- xtensa_tlb_set_entry(env, dtlb, wi, ei, vpn, p); ++ if (split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei)) { ++ xtensa_tlb_set_entry(env, dtlb, wi, ei, vpn, p); ++ } + } + + /*! +-- +1.8.3.1 + diff --git a/0064-target-arm-Fix-A64-scalar-SQSHRN-and-SQRSHRN.patch b/0064-target-arm-Fix-A64-scalar-SQSHRN-and-SQRSHRN.patch new file mode 100644 index 0000000000000000000000000000000000000000..eb187a6b427d17cf8d46ca5012777c58e577e13b --- /dev/null +++ b/0064-target-arm-Fix-A64-scalar-SQSHRN-and-SQRSHRN.patch @@ -0,0 +1,52 @@ +From 570e624426421a37fa97b04b1de2d096281530bb Mon Sep 17 00:00:00 2001 +From: Peter Maydell +Date: Tue, 23 Jan 2024 15:34:16 +0000 +Subject: [PATCH 070/293] target/arm: Fix A64 scalar SQSHRN and SQRSHRN + +In commit 1b7bc9b5c8bf374dd we changed handle_vec_simd_sqshrn() so +that instead of starting with a 0 value and depositing in each new +element from the narrowing operation, it instead started with the raw +result of the narrowing operation of the first element. + +This is fine in the vector case, because the deposit operations for +the second and subsequent elements will always overwrite any higher +bits that might have been in the first element's result value in +tcg_rd. However in the scalar case we only go through this loop +once. The effect is that for a signed narrowing operation, if the +result is negative then we will now return a value where the bits +above the first element are incorrectly 1 (because the narrowfn +returns a sign-extended result, not one that is truncated to the +element size). + +Fix this by using an extract operation to get exactly the correct +bits of the output of the narrowfn for element 1, instead of a +plain move. + +Cc: qemu-stable@nongnu.org +Fixes: 1b7bc9b5c8bf374dd3 ("target/arm: Avoid tcg_const_ptr in handle_vec_simd_sqshrn") +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2089 +Signed-off-by: Peter Maydell +Reviewed-by: Richard Henderson +Message-id: 20240123153416.877308-1-peter.maydell@linaro.org +(cherry picked from commit 6fffc8378562c7fea6290c430b4f653f830a4c1a) +Signed-off-by: Michael Tokarev +--- + target/arm/tcg/translate-a64.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c +index a2e49c3..f2d05c5 100644 +--- a/target/arm/tcg/translate-a64.c ++++ b/target/arm/tcg/translate-a64.c +@@ -8221,7 +8221,7 @@ static void handle_vec_simd_sqshrn(DisasContext *s, bool is_scalar, bool is_q, + narrowfn(tcg_rd_narrowed, tcg_env, tcg_rd); + tcg_gen_extu_i32_i64(tcg_rd, tcg_rd_narrowed); + if (i == 0) { +- tcg_gen_mov_i64(tcg_final, tcg_rd); ++ tcg_gen_extract_i64(tcg_final, tcg_rd, 0, esize); + } else { + tcg_gen_deposit_i64(tcg_final, tcg_final, tcg_rd, esize * i, esize); + } +-- +1.8.3.1 + diff --git a/0065-target-arm-Fix-incorrect-aa64_tidcp1-feature-check.patch b/0065-target-arm-Fix-incorrect-aa64_tidcp1-feature-check.patch new file mode 100644 index 0000000000000000000000000000000000000000..c0b1bbf4748afeb139973f26e562c7b36580c679 --- /dev/null +++ b/0065-target-arm-Fix-incorrect-aa64_tidcp1-feature-check.patch @@ -0,0 +1,38 @@ +From 45b3ce5e83a6e6677ed9197ed203d6076d25848e Mon Sep 17 00:00:00 2001 +From: Peter Maydell +Date: Tue, 23 Jan 2024 16:03:33 +0000 +Subject: [PATCH 071/293] target/arm: Fix incorrect aa64_tidcp1 feature check + +A typo in the implementation of isar_feature_aa64_tidcp1() means we +were checking the field in the wrong ID register, so we might have +provided the feature on CPUs that don't have it and not provided +it on CPUs that should have it. Correct this bug. + +Cc: qemu-stable@nongnu.org +Fixes: 9cd0c0dec97be9 "target/arm: Implement FEAT_TIDCP1" +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2120 +Signed-off-by: Peter Maydell +Reviewed-by: Richard Henderson +Message-id: 20240123160333.958841-1-peter.maydell@linaro.org +(cherry picked from commit ee0a2e3c9d2991a11c13ffadb15e4d0add43c257) +Signed-off-by: Michael Tokarev +--- + target/arm/cpu-features.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h +index 954d358..165a497 100644 +--- a/target/arm/cpu-features.h ++++ b/target/arm/cpu-features.h +@@ -771,7 +771,7 @@ static inline bool isar_feature_aa64_hcx(const ARMISARegisters *id) + + static inline bool isar_feature_aa64_tidcp1(const ARMISARegisters *id) + { +- return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR1, TIDCP1) != 0; ++ return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, TIDCP1) != 0; + } + + static inline bool isar_feature_aa64_hafs(const ARMISARegisters *id) +-- +1.8.3.1 + diff --git a/0066-Update-version-for-8.2.1-release.patch b/0066-Update-version-for-8.2.1-release.patch new file mode 100644 index 0000000000000000000000000000000000000000..43be6ec5e6e0121d5d09b384b5b8f91c11178ed8 --- /dev/null +++ b/0066-Update-version-for-8.2.1-release.patch @@ -0,0 +1,20 @@ +From f48c205fb42be48e2e47b7e1cd9a2802e5ca17b0 Mon Sep 17 00:00:00 2001 +From: Michael Tokarev +Date: Mon, 29 Jan 2024 14:20:06 +0300 +Subject: [PATCH 072/293] Update version for 8.2.1 release + +Signed-off-by: Michael Tokarev +--- + VERSION | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/VERSION b/VERSION +index fbb9ea1..2b0aa21 100644 +--- a/VERSION ++++ b/VERSION +@@ -1 +1 @@ +-8.2.0 ++8.2.1 +-- +1.8.3.1 + diff --git a/0067-migration-Plug-memory-leak-on-HMP-migrate-error-path.patch b/0067-migration-Plug-memory-leak-on-HMP-migrate-error-path.patch new file mode 100644 index 0000000000000000000000000000000000000000..d34709ca65c6287c25b1c994ef4fa882d9c1e447 --- /dev/null +++ b/0067-migration-Plug-memory-leak-on-HMP-migrate-error-path.patch @@ -0,0 +1,45 @@ +From e589e5ade7e7dc1f14eacc1670646439e4c07284 Mon Sep 17 00:00:00 2001 +From: Markus Armbruster +Date: Wed, 17 Jan 2024 15:07:22 +0100 +Subject: [PATCH 073/293] migration: Plug memory leak on HMP migrate error path + +hmp_migrate() leaks @caps when qmp_migrate() fails. Plug the leak +with g_autoptr(). + +Fixes: 967f2de5c9ec (migration: Implement MigrateChannelList to hmp migration flow.) v8.2.0-rc0 +Fixes: CID 1533125 +Signed-off-by: Markus Armbruster +Link: https://lore.kernel.org/r/20240117140722.3979657-1-armbru@redhat.com +[peterx: fix CID number as reported by Peter Maydell] +Signed-off-by: Peter Xu +(cherry picked from commit 918f620d30a9b0095b7824b8d77a2d6059a439d9) +Signed-off-by: Michael Tokarev +--- + migration/migration-hmp-cmds.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c +index 86ae832..2faa5ca 100644 +--- a/migration/migration-hmp-cmds.c ++++ b/migration/migration-hmp-cmds.c +@@ -762,7 +762,7 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) + bool resume = qdict_get_try_bool(qdict, "resume", false); + const char *uri = qdict_get_str(qdict, "uri"); + Error *err = NULL; +- MigrationChannelList *caps = NULL; ++ g_autoptr(MigrationChannelList) caps = NULL; + g_autoptr(MigrationChannel) channel = NULL; + + if (inc) { +@@ -787,8 +787,6 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) + return; + } + +- qapi_free_MigrationChannelList(caps); +- + if (!detach) { + HMPMigrationStatus *status; + +-- +1.8.3.1 + diff --git a/0068-migration-Fix-use-after-free-of-migration-state-obje.patch b/0068-migration-Fix-use-after-free-of-migration-state-obje.patch new file mode 100644 index 0000000000000000000000000000000000000000..0ff41b2ab735d1138af7492e9a1f425d8d8cd5f8 --- /dev/null +++ b/0068-migration-Fix-use-after-free-of-migration-state-obje.patch @@ -0,0 +1,54 @@ +From 106aa13c5bbb10d1742d9f2be1ac73a4918f6a27 Mon Sep 17 00:00:00 2001 +From: Fabiano Rosas +Date: Fri, 19 Jan 2024 20:39:18 -0300 +Subject: [PATCH 074/293] migration: Fix use-after-free of migration state + object + +We're currently allowing the process_incoming_migration_bh bottom-half +to run without holding a reference to the 'current_migration' object, +which leads to a segmentation fault if the BH is still live after +migration_shutdown() has dropped the last reference to +current_migration. + +In my system the bug manifests as migrate_multifd() returning true +when it shouldn't and multifd_load_shutdown() calling +multifd_recv_terminate_threads() which crashes due to an uninitialized +multifd_recv_state. + +Fix the issue by holding a reference to the object when scheduling the +BH and dropping it before returning from the BH. The same is already +done for the cleanup_bh at migrate_fd_cleanup_schedule(). + +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1969 +Signed-off-by: Fabiano Rosas +Link: https://lore.kernel.org/r/20240119233922.32588-2-farosas@suse.de +Signed-off-by: Peter Xu +(cherry picked from commit 27eb8499edb2bc952c29ddae0bdac9fc959bf7b1) +Signed-off-by: Michael Tokarev +--- + migration/migration.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/migration/migration.c b/migration/migration.c +index 3ce04b2..ee5e0ba 100644 +--- a/migration/migration.c ++++ b/migration/migration.c +@@ -650,6 +650,7 @@ static void process_incoming_migration_bh(void *opaque) + MIGRATION_STATUS_COMPLETED); + qemu_bh_delete(mis->bh); + migration_incoming_state_destroy(); ++ object_unref(OBJECT(migrate_get_current())); + } + + static void coroutine_fn +@@ -708,6 +709,7 @@ process_incoming_migration_co(void *opaque) + } + + mis->bh = qemu_bh_new(process_incoming_migration_bh, mis); ++ object_ref(OBJECT(migrate_get_current())); + qemu_bh_schedule(mis->bh); + return; + fail: +-- +1.8.3.1 + diff --git a/0069-vfio-pci-Clear-MSI-X-IRQ-index-always.patch b/0069-vfio-pci-Clear-MSI-X-IRQ-index-always.patch new file mode 100644 index 0000000000000000000000000000000000000000..3c615d36cdcb7d5be2e27f57848d26d988c15c81 --- /dev/null +++ b/0069-vfio-pci-Clear-MSI-X-IRQ-index-always.patch @@ -0,0 +1,54 @@ +From b79a2ef0d4dc6b8a4093334db29f7c221c1ac8bd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= +Date: Thu, 25 Jan 2024 14:27:36 +0100 +Subject: [PATCH 075/293] vfio/pci: Clear MSI-X IRQ index always +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When doing device assignment of a physical device, MSI-X can be +enabled with no vectors enabled and this sets the IRQ index to +VFIO_PCI_MSIX_IRQ_INDEX. However, when MSI-X is disabled, the IRQ +index is left untouched if no vectors are in use. Then, when INTx +is enabled, the IRQ index value is considered incompatible (set to +MSI-X) and VFIO_DEVICE_SET_IRQS fails. QEMU complains with : + +qemu-system-x86_64: vfio 0000:08:00.0: Failed to set up TRIGGER eventfd signaling for interrupt INTX-0: VFIO_DEVICE_SET_IRQS failure: Invalid argument + +To avoid that, unconditionaly clear the IRQ index when MSI-X is +disabled. + +Buglink: https://issues.redhat.com/browse/RHEL-21293 +Fixes: 5ebffa4e87e7 ("vfio/pci: use an invalid fd to enable MSI-X") +Cc: Jing Liu +Cc: Alex Williamson +Reviewed-by: Alex Williamson +Signed-off-by: Cédric Le Goater +(cherry picked from commit d2b668fca5652760b435ce812a743bba03d2f316) +Signed-off-by: Michael Tokarev +--- + hw/vfio/pci.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c +index c62c02f..e167bef 100644 +--- a/hw/vfio/pci.c ++++ b/hw/vfio/pci.c +@@ -824,9 +824,11 @@ static void vfio_msix_disable(VFIOPCIDevice *vdev) + } + } + +- if (vdev->nr_vectors) { +- vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX); +- } ++ /* ++ * Always clear MSI-X IRQ index. A PF device could have enabled ++ * MSI-X with no vectors. See vfio_msix_enable(). ++ */ ++ vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX); + + vfio_msi_disable_common(vdev); + vfio_intx_enable(vdev, &err); +-- +1.8.3.1 + diff --git a/0070-Make-uri-optional-for-migrate-QAPI.patch b/0070-Make-uri-optional-for-migrate-QAPI.patch new file mode 100644 index 0000000000000000000000000000000000000000..c8db2547321f1bb98481afc6ecea0ed19997018d --- /dev/null +++ b/0070-Make-uri-optional-for-migrate-QAPI.patch @@ -0,0 +1,34 @@ +From 3837e6dd1ef56e63919b961fa6786fd566fe0311 Mon Sep 17 00:00:00 2001 +From: Het Gala +Date: Tue, 23 Jan 2024 06:42:19 +0000 +Subject: [PATCH 076/293] Make 'uri' optional for migrate QAPI + +'uri' argument should be optional, as 'uri' and 'channels' +arguments are mutally exclusive in nature. + +Fixes: 074dbce5fcce (migration: New migrate and migrate-incoming argument 'channels') +Signed-off-by: Het Gala +Link: https://lore.kernel.org/r/20240123064219.40514-1-het.gala@nutanix.com +Signed-off-by: Peter Xu +(cherry picked from commit 57fd4b4e10756448acd6c90ce041ba8dc9313efc) +Signed-off-by: Michael Tokarev +--- + qapi/migration.json | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/qapi/migration.json b/qapi/migration.json +index eb2f883..197d3fa 100644 +--- a/qapi/migration.json ++++ b/qapi/migration.json +@@ -1757,7 +1757,7 @@ + # + ## + { 'command': 'migrate', +- 'data': {'uri': 'str', ++ 'data': {'*uri': 'str', + '*channels': [ 'MigrationChannel' ], + '*blk': { 'type': 'bool', 'features': [ 'deprecated' ] }, + '*inc': { 'type': 'bool', 'features': [ 'deprecated' ] }, +-- +1.8.3.1 + diff --git a/0071-qemu-docs-Update-options-for-graphical-frontends.patch b/0071-qemu-docs-Update-options-for-graphical-frontends.patch new file mode 100644 index 0000000000000000000000000000000000000000..cf9277741e3b45d24b09b950024b068c06622ba2 --- /dev/null +++ b/0071-qemu-docs-Update-options-for-graphical-frontends.patch @@ -0,0 +1,39 @@ +From 84c9704b8e4a347ff74f17c018e58e7f90ca63fc Mon Sep 17 00:00:00 2001 +From: Yihuan Pan +Date: Mon, 22 Jan 2024 12:22:06 +0800 +Subject: [PATCH 077/293] qemu-docs: Update options for graphical frontends + +The command line options `-ctrl-grab` and `-alt-grab` have been removed +in QEMU 7.1. Instead, use the `-display sdl,grab-mod=` option +to specify the grab modifiers. + +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2103 +Signed-off-by: Yihuan Pan +Signed-off-by: Michael Tokarev +(cherry picked from commit db101376af52e81f740a27f5fa38260ad171323c) +--- + docs/system/keys.rst.inc | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/docs/system/keys.rst.inc b/docs/system/keys.rst.inc +index bd9b8e5..2e2c97a 100644 +--- a/docs/system/keys.rst.inc ++++ b/docs/system/keys.rst.inc +@@ -1,8 +1,9 @@ +-During the graphical emulation, you can use special key combinations to +-change modes. The default key mappings are shown below, but if you use +-``-alt-grab`` then the modifier is Ctrl-Alt-Shift (instead of Ctrl-Alt) +-and if you use ``-ctrl-grab`` then the modifier is the right Ctrl key +-(instead of Ctrl-Alt): ++During the graphical emulation, you can use special key combinations from ++the following table to change modes. By default the modifier is Ctrl-Alt ++(used in the table below) which can be changed with ``-display`` suboption ++``mod=`` where appropriate. For example, ``-display sdl, ++grab-mod=lshift-lctrl-lalt`` changes the modifier key to Ctrl-Alt-Shift, ++while ``-display sdl,grab-mod=rctrl`` changes it to the right Ctrl key. + + Ctrl-Alt-f + Toggle full screen +-- +1.8.3.1 + diff --git a/0072-block-blkio-Make-s-mem_region_alignment-be-64-bits.patch b/0072-block-blkio-Make-s-mem_region_alignment-be-64-bits.patch new file mode 100644 index 0000000000000000000000000000000000000000..d0848cbdcbbeccfaeca0ee2c05ba50fbee9434c7 --- /dev/null +++ b/0072-block-blkio-Make-s-mem_region_alignment-be-64-bits.patch @@ -0,0 +1,47 @@ +From b91715588a101024c644a322ad2f43af50a8d2fd Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Tue, 30 Jan 2024 12:20:01 +0000 +Subject: [PATCH 078/293] block/blkio: Make s->mem_region_alignment be 64 bits +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +With GCC 14 the code failed to compile on i686 (and was wrong for any +version of GCC): + +../block/blkio.c: In function ‘blkio_file_open’: +../block/blkio.c:857:28: error: passing argument 3 of ‘blkio_get_uint64’ from incompatible pointer type [-Wincompatible-pointer-types] + 857 | &s->mem_region_alignment); + | ^~~~~~~~~~~~~~~~~~~~~~~~ + | | + | size_t * {aka unsigned int *} +In file included from ../block/blkio.c:12: +/usr/include/blkio.h:49:67: note: expected ‘uint64_t *’ {aka ‘long long unsigned int *’} but argument is of type ‘size_t *’ {aka ‘unsigned int *’} + 49 | int blkio_get_uint64(struct blkio *b, const char *name, uint64_t *value); + | ~~~~~~~~~~^~~~~ + +Signed-off-by: Richard W.M. Jones +Message-id: 20240130122006.2977938-1-rjones@redhat.com +Signed-off-by: Stefan Hajnoczi +(cherry picked from commit 615eaeab3d318ba239d54141a4251746782f65c1) +Signed-off-by: Michael Tokarev +--- + block/blkio.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/block/blkio.c b/block/blkio.c +index 0a0a6c0..bc2f217 100644 +--- a/block/blkio.c ++++ b/block/blkio.c +@@ -68,7 +68,7 @@ typedef struct { + CoQueue bounce_available; + + /* The value of the "mem-region-alignment" property */ +- size_t mem_region_alignment; ++ uint64_t mem_region_alignment; + + /* Can we skip adding/deleting blkio_mem_regions? */ + bool needs_mem_regions; +-- +1.8.3.1 + diff --git a/0073-target-arm-fix-exception-syndrome-for-AArch32-bkpt-i.patch b/0073-target-arm-fix-exception-syndrome-for-AArch32-bkpt-i.patch new file mode 100644 index 0000000000000000000000000000000000000000..3041d4c011cef321521297a88262f2b3c3128f22 --- /dev/null +++ b/0073-target-arm-fix-exception-syndrome-for-AArch32-bkpt-i.patch @@ -0,0 +1,91 @@ +From 35a60a20f0008a39af39bf39e12a1b07889b4e56 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20Kl=C3=B6tzke?= +Date: Thu, 1 Feb 2024 10:57:19 +0000 +Subject: [PATCH 079/293] target/arm: fix exception syndrome for AArch32 bkpt + insn +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Debug exceptions that target AArch32 Hyp mode are reported differently +than on AAarch64. Internally, Qemu uses the AArch64 syndromes. Therefore +such exceptions need to be either converted to a prefetch abort +(breakpoints, vector catch) or a data abort (watchpoints). + +Cc: qemu-stable@nongnu.org +Signed-off-by: Jan Klötzke +Reviewed-by: Richard Henderson +Message-id: 20240127202758.3326381-1-jan.kloetzke@kernkonzept.com +Signed-off-by: Peter Maydell +(cherry picked from commit f670be1aad33e801779af580398895b9455747ee) +Signed-off-by: Michael Tokarev +--- + target/arm/helper.c | 18 ++++++++++++++++++ + target/arm/syndrome.h | 8 ++++++++ + 2 files changed, 26 insertions(+) + +diff --git a/target/arm/helper.c b/target/arm/helper.c +index 2746d3f..6515c5e 100644 +--- a/target/arm/helper.c ++++ b/target/arm/helper.c +@@ -10823,6 +10823,24 @@ static void arm_cpu_do_interrupt_aarch32(CPUState *cs) + } + + if (env->exception.target_el == 2) { ++ /* Debug exceptions are reported differently on AArch32 */ ++ switch (syn_get_ec(env->exception.syndrome)) { ++ case EC_BREAKPOINT: ++ case EC_BREAKPOINT_SAME_EL: ++ case EC_AA32_BKPT: ++ case EC_VECTORCATCH: ++ env->exception.syndrome = syn_insn_abort(arm_current_el(env) == 2, ++ 0, 0, 0x22); ++ break; ++ case EC_WATCHPOINT: ++ env->exception.syndrome = syn_set_ec(env->exception.syndrome, ++ EC_DATAABORT); ++ break; ++ case EC_WATCHPOINT_SAME_EL: ++ env->exception.syndrome = syn_set_ec(env->exception.syndrome, ++ EC_DATAABORT_SAME_EL); ++ break; ++ } + arm_cpu_do_interrupt_aarch32_hyp(cs); + return; + } +diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h +index 95454b5..eccb759 100644 +--- a/target/arm/syndrome.h ++++ b/target/arm/syndrome.h +@@ -25,6 +25,8 @@ + #ifndef TARGET_ARM_SYNDROME_H + #define TARGET_ARM_SYNDROME_H + ++#include "qemu/bitops.h" ++ + /* Valid Syndrome Register EC field values */ + enum arm_exception_class { + EC_UNCATEGORIZED = 0x00, +@@ -80,6 +82,7 @@ typedef enum { + SME_ET_InactiveZA, + } SMEExceptionType; + ++#define ARM_EL_EC_LENGTH 6 + #define ARM_EL_EC_SHIFT 26 + #define ARM_EL_IL_SHIFT 25 + #define ARM_EL_ISV_SHIFT 24 +@@ -91,6 +94,11 @@ static inline uint32_t syn_get_ec(uint32_t syn) + return syn >> ARM_EL_EC_SHIFT; + } + ++static inline uint32_t syn_set_ec(uint32_t syn, uint32_t ec) ++{ ++ return deposit32(syn, ARM_EL_EC_SHIFT, ARM_EL_EC_LENGTH, ec); ++} ++ + /* + * Utility functions for constructing various kinds of syndrome value. + * Note that in general we follow the AArch64 syndrome values; in a +-- +1.8.3.1 + diff --git a/0074-system-vl.c-Fix-handling-of-serial-none-serial-somet.patch b/0074-system-vl.c-Fix-handling-of-serial-none-serial-somet.patch new file mode 100644 index 0000000000000000000000000000000000000000..7d348630ea8b783dc4145712aa59e48efae08e4b --- /dev/null +++ b/0074-system-vl.c-Fix-handling-of-serial-none-serial-somet.patch @@ -0,0 +1,89 @@ +From e2a12fa4e7da627516b82ace779d7bfa641aa0de Mon Sep 17 00:00:00 2001 +From: Peter Maydell +Date: Mon, 22 Jan 2024 16:36:06 +0000 +Subject: [PATCH 080/293] system/vl.c: Fix handling of '-serial none -serial + something' +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Currently if the user passes multiple -serial options on the command +line, we mostly treat those as applying to the different serial +devices in order, so that for example + -serial stdio -serial file:filename +will connect the first serial port to stdio and the second to the +named file. + +The exception to this is the '-serial none' serial device type. This +means "don't allocate this serial device", but a bug means that +following -serial options are not correctly handled, so that + -serial none -serial stdio +has the unexpected effect that stdio is connected to the first serial +port, not the second. + +This is a very long-standing bug that dates back at least as far as +commit 998bbd74b9d81 from 2009. + +Make the 'none' serial type move forward in the indexing of serial +devices like all the other serial types, so that any subsequent +-serial options are correctly handled. + +Note that if your commandline mistakenly had a '-serial none' that +was being overridden by a following '-serial something' option, you +should delete the unnecessary '-serial none'. This will give you the +same behaviour as before, on QEMU versions both with and without this +bug fix. + +Cc: qemu-stable@nongnu.org +Reported-by: Bohdan Kostiv +Signed-off-by: Peter Maydell +Reviewed-by: Daniel P. Berrangé +Reviewed-by: Richard Henderson +Message-id: 20240122163607.459769-2-peter.maydell@linaro.org +Fixes: 998bbd74b9d81 ("default devices: core code & serial lines") +Signed-off-by: Peter Maydell +(cherry picked from commit d2019a9d0c34a4fdcb5b5df550d73040dc0637d9) +Signed-off-by: Michael Tokarev +--- + system/vl.c | 22 +++++++++++++--------- + 1 file changed, 13 insertions(+), 9 deletions(-) + +diff --git a/system/vl.c b/system/vl.c +index 6b87bfa..938b7b5 100644 +--- a/system/vl.c ++++ b/system/vl.c +@@ -1440,18 +1440,22 @@ static void qemu_create_default_devices(void) + static int serial_parse(const char *devname) + { + int index = num_serial_hds; +- char label[32]; + +- if (strcmp(devname, "none") == 0) +- return 0; +- snprintf(label, sizeof(label), "serial%d", index); + serial_hds = g_renew(Chardev *, serial_hds, index + 1); + +- serial_hds[index] = qemu_chr_new_mux_mon(label, devname, NULL); +- if (!serial_hds[index]) { +- error_report("could not connect serial device" +- " to character backend '%s'", devname); +- return -1; ++ if (strcmp(devname, "none") == 0) { ++ /* Don't allocate a serial device for this index */ ++ serial_hds[index] = NULL; ++ } else { ++ char label[32]; ++ snprintf(label, sizeof(label), "serial%d", index); ++ ++ serial_hds[index] = qemu_chr_new_mux_mon(label, devname, NULL); ++ if (!serial_hds[index]) { ++ error_report("could not connect serial device" ++ " to character backend '%s'", devname); ++ return -1; ++ } + } + num_serial_hds++; + return 0; +-- +1.8.3.1 + diff --git a/0075-qemu-options.hx-Improve-serial-option-documentation.patch b/0075-qemu-options.hx-Improve-serial-option-documentation.patch new file mode 100644 index 0000000000000000000000000000000000000000..bef94ad7b4d29e403d7a10c804090c7c44b4adca --- /dev/null +++ b/0075-qemu-options.hx-Improve-serial-option-documentation.patch @@ -0,0 +1,63 @@ +From 2d0530abe27bf5dce5aca269fbb2aa16e0ee88eb Mon Sep 17 00:00:00 2001 +From: Peter Maydell +Date: Mon, 22 Jan 2024 16:36:07 +0000 +Subject: [PATCH 081/293] qemu-options.hx: Improve -serial option documentation +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The -serial option documentation is a bit brief about '-serial none' +and '-serial null'. In particular it's not very clear about the +difference between them, and it doesn't mention that it's up to +the machine model whether '-serial none' means "don't create the +serial port" or "don't wire the serial port up to anything". + +Expand on these points. + +Signed-off-by: Peter Maydell +Reviewed-by: Daniel P. Berrangé +Reviewed-by: Philippe Mathieu-Daudé +Message-id: 20240122163607.459769-3-peter.maydell@linaro.org +(cherry picked from commit 747bfaf3a9d2f3cd51674763dc1f7575100cd200) +Signed-off-by: Michael Tokarev +--- + qemu-options.hx | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +diff --git a/qemu-options.hx b/qemu-options.hx +index 42fd09e..b6b4ad9 100644 +--- a/qemu-options.hx ++++ b/qemu-options.hx +@@ -4118,7 +4118,8 @@ SRST + This option can be used several times to simulate up to 4 serial + ports. + +- Use ``-serial none`` to disable all serial ports. ++ You can use ``-serial none`` to suppress the creation of default ++ serial devices. + + Available character devices are: + +@@ -4140,10 +4141,17 @@ SRST + [Linux only] Pseudo TTY (a new PTY is automatically allocated) + + ``none`` +- No device is allocated. ++ No device is allocated. Note that for machine types which ++ emulate systems where a serial device is always present in ++ real hardware, this may be equivalent to the ``null`` option, ++ in that the serial device is still present but all output ++ is discarded. For boards where the number of serial ports is ++ truly variable, this suppresses the creation of the device. + + ``null`` +- void device ++ A guest will see the UART or serial device as present in the ++ machine, but all output is discarded, and there is no input. ++ Conceptually equivalent to redirecting the output to ``/dev/null``. + + ``chardev:id`` + Use a named character device defined with the ``-chardev`` +-- +1.8.3.1 + diff --git a/0076-target-arm-Reinstate-vfp-property-on-AArch32-CPUs.patch b/0076-target-arm-Reinstate-vfp-property-on-AArch32-CPUs.patch new file mode 100644 index 0000000000000000000000000000000000000000..32fec8577bad54f29a2a65971c8692703a016873 --- /dev/null +++ b/0076-target-arm-Reinstate-vfp-property-on-AArch32-CPUs.patch @@ -0,0 +1,43 @@ +From de6992d390fa4e0623a135f0363b99cb6fd8ca5d Mon Sep 17 00:00:00 2001 +From: Peter Maydell +Date: Fri, 26 Jan 2024 19:34:32 +0000 +Subject: [PATCH 082/293] target/arm: Reinstate "vfp" property on AArch32 CPUs + +In commit 4315f7c614743 we restructured the logic for creating the +VFP related properties to avoid testing the aa32_simd_r32 feature on +AArch64 CPUs. However in the process we accidentally stopped +exposing the "vfp" QOM property on AArch32 TCG CPUs. + +This mostly hasn't had any ill effects because not many people want +to disable VFP, but it wasn't intentional. Reinstate the property. + +Cc: qemu-stable@nongnu.org +Fixes: 4315f7c614743 ("target/arm: Restructure has_vfp_d32 test") +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2098 +Signed-off-by: Peter Maydell +Reviewed-by: Richard Henderson +Message-id: 20240126193432.2210558-1-peter.maydell@linaro.org +(cherry picked from commit 185e3fdf8d106cb2f7d234d5e6453939c66db2a9) +Signed-off-by: Michael Tokarev +--- + target/arm/cpu.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/target/arm/cpu.c b/target/arm/cpu.c +index efb22a8..5d9bca5 100644 +--- a/target/arm/cpu.c ++++ b/target/arm/cpu.c +@@ -1615,6 +1615,10 @@ void arm_cpu_post_init(Object *obj) + } + } else if (cpu_isar_feature(aa32_vfp, cpu)) { + cpu->has_vfp = true; ++ if (tcg_enabled() || qtest_enabled()) { ++ qdev_property_add_static(DEVICE(obj), ++ &arm_cpu_has_vfp_property); ++ } + if (cpu_isar_feature(aa32_simd_r32, cpu)) { + cpu->has_vfp_d32 = true; + /* +-- +1.8.3.1 + diff --git a/0077-pci-host-designware-Limit-value-range-of-iATU-viewpo.patch b/0077-pci-host-designware-Limit-value-range-of-iATU-viewpo.patch new file mode 100644 index 0000000000000000000000000000000000000000..8a167e5bf5f80ee34c781f10708d4535c5bc5de5 --- /dev/null +++ b/0077-pci-host-designware-Limit-value-range-of-iATU-viewpo.patch @@ -0,0 +1,54 @@ +From 5f5e30229eea3971135ddc6dc70635866e605b63 Mon Sep 17 00:00:00 2001 +From: Guenter Roeck +Date: Sun, 28 Jan 2024 22:00:55 -0800 +Subject: [PATCH 083/293] pci-host: designware: Limit value range of iATU + viewport register + +The latest version of qemu (v8.2.0-869-g7a1dc45af5) crashes when booting +the mcimx7d-sabre emulation with Linux v5.11 and later. + +qemu-system-arm: ../system/memory.c:2750: memory_region_set_alias_offset: Assertion `mr->alias' failed. + +Problem is that the Designware PCIe emulation accepts the full value range +for the iATU Viewport Register. However, both hardware and emulation only +support four inbound and four outbound viewports. + +The Linux kernel determines the number of supported viewports by writing +0xff into the viewport register and reading the value back. The expected +value when reading the register is the highest supported viewport index. +Match that code by masking the supported viewport value range when the +register is written. With this change, the Linux kernel reports + +imx6q-pcie 33800000.pcie: iATU: unroll F, 4 ob, 4 ib, align 0K, limit 4G + +as expected and supported. + +Fixes: d64e5eabc4c7 ("pci: Add support for Designware IP block") +Cc: Andrey Smirnov +Cc: Nikita Ostrenkov +Signed-off-by: Guenter Roeck +Message-id: 20240129060055.2616989-1-linux@roeck-us.net +Reviewed-by: Peter Maydell +Signed-off-by: Peter Maydell +(cherry picked from commit 8a73152020337a7fbf34daf0a006d4d89ec1494e) +Signed-off-by: Michael Tokarev +--- + hw/pci-host/designware.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/hw/pci-host/designware.c b/hw/pci-host/designware.c +index f477f97..f016f02 100644 +--- a/hw/pci-host/designware.c ++++ b/hw/pci-host/designware.c +@@ -340,6 +340,8 @@ static void designware_pcie_root_config_write(PCIDevice *d, uint32_t address, + break; + + case DESIGNWARE_PCIE_ATU_VIEWPORT: ++ val &= DESIGNWARE_PCIE_ATU_REGION_INBOUND | ++ (DESIGNWARE_PCIE_NUM_VIEWPORTS - 1); + root->atu_viewport = val; + break; + +-- +1.8.3.1 + diff --git a/0078-tcg-loongarch64-Set-vector-registers-call-clobbered.patch b/0078-tcg-loongarch64-Set-vector-registers-call-clobbered.patch new file mode 100644 index 0000000000000000000000000000000000000000..22b355ac836db05fe69f08856d0d7d0662ea8dd1 --- /dev/null +++ b/0078-tcg-loongarch64-Set-vector-registers-call-clobbered.patch @@ -0,0 +1,39 @@ +From 8b7750c66f191ec830c2985dcc1382703b48a117 Mon Sep 17 00:00:00 2001 +From: Richard Henderson +Date: Fri, 2 Feb 2024 09:34:14 +1000 +Subject: [PATCH 084/293] tcg/loongarch64: Set vector registers call clobbered + +Because there are more call clobbered registers than +call saved registers, we begin with all registers as +call clobbered and then reset those that are saved. + +This was missed when we introduced the LSX support. + +Cc: qemu-stable@nongnu.org +Fixes: 16288ded944 ("tcg/loongarch64: Lower basic tcg vec ops to LSX") +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2136 +Signed-off-by: Richard Henderson +Reviewed-by: Song Gao +Message-Id: <20240201233414.500588-1-richard.henderson@linaro.org> +(cherry picked from commit 45bf0e7aa648369cf8ab2333bd20144806fc1be3) +Signed-off-by: Michael Tokarev +--- + tcg/loongarch64/tcg-target.c.inc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc +index bab0a17..dcf0205 100644 +--- a/tcg/loongarch64/tcg-target.c.inc ++++ b/tcg/loongarch64/tcg-target.c.inc +@@ -2327,7 +2327,7 @@ static void tcg_target_init(TCGContext *s) + tcg_target_available_regs[TCG_TYPE_I32] = ALL_GENERAL_REGS; + tcg_target_available_regs[TCG_TYPE_I64] = ALL_GENERAL_REGS; + +- tcg_target_call_clobber_regs = ALL_GENERAL_REGS; ++ tcg_target_call_clobber_regs = ALL_GENERAL_REGS | ALL_VECTOR_REGS; + tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S0); + tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S1); + tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S2); +-- +1.8.3.1 + diff --git a/0079-hw-scsi-lsi53c895a-add-missing-decrement-of-reentran.patch b/0079-hw-scsi-lsi53c895a-add-missing-decrement-of-reentran.patch new file mode 100644 index 0000000000000000000000000000000000000000..4c31e812ccb4092b400a991377285693baea01eb --- /dev/null +++ b/0079-hw-scsi-lsi53c895a-add-missing-decrement-of-reentran.patch @@ -0,0 +1,41 @@ +From bbfcb0f7bcc1f905eee3997e56d42bb1e97de51d Mon Sep 17 00:00:00 2001 +From: Sven Schnelle +Date: Sun, 28 Jan 2024 21:22:14 +0100 +Subject: [PATCH 086/293] hw/scsi/lsi53c895a: add missing decrement of + reentrancy counter + +When the maximum count of SCRIPTS instructions is reached, the code +stops execution and returns, but fails to decrement the reentrancy +counter. This effectively renders the SCSI controller unusable +because on next entry the reentrancy counter is still above the limit. + +This bug was seen on HP-UX 10.20 which seems to trigger SCRIPTS +loops. + +Fixes: b987718bbb ("hw/scsi/lsi53c895a: Fix reentrancy issues in the LSI controller (CVE-2023-0330)") +Signed-off-by: Sven Schnelle +Message-ID: <20240128202214.2644768-1-svens@stackframe.org> +Reviewed-by: Thomas Huth +Tested-by: Helge Deller +Signed-off-by: Thomas Huth +(cherry picked from commit 8b09b7fe47082c69295a0fc0cc01b041b6385025) +Signed-off-by: Michael Tokarev +--- + hw/scsi/lsi53c895a.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c +index 634ed49..afbea0f 100644 +--- a/hw/scsi/lsi53c895a.c ++++ b/hw/scsi/lsi53c895a.c +@@ -1159,6 +1159,7 @@ again: + lsi_script_scsi_interrupt(s, LSI_SIST0_UDC, 0); + lsi_disconnect(s); + trace_lsi_execute_script_stop(); ++ reentrancy_level--; + return; + } + insn = read_dword(s, s->dsp); +-- +1.8.3.1 + diff --git a/0080-iotests-fix-leak-of-tmpdir-in-dry-run-mode.patch b/0080-iotests-fix-leak-of-tmpdir-in-dry-run-mode.patch new file mode 100644 index 0000000000000000000000000000000000000000..c1b4a9b75367e43dfd4d85e3b4ba13651b33ea90 --- /dev/null +++ b/0080-iotests-fix-leak-of-tmpdir-in-dry-run-mode.patch @@ -0,0 +1,45 @@ +From 88555e3607d322e46e1f33a2acf9a7f4055bfde9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= +Date: Mon, 5 Feb 2024 15:40:19 +0000 +Subject: [PATCH 087/293] iotests: fix leak of tmpdir in dry-run mode +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Creating an instance of the 'TestEnv' class will create a temporary +directory. This dir is only deleted, however, in the __exit__ handler +invoked by a context manager. + +In dry-run mode, we don't use the TestEnv via a context manager, so +were leaking the temporary directory. Since meson invokes 'check' +5 times on each configure run, developers /tmp was filling up with +empty temporary directories. + +Signed-off-by: Daniel P. Berrangé +Message-ID: <20240205154019.1841037-1-berrange@redhat.com> +Reviewed-by: Michael Tokarev +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit c645bac4e06bf9642cc8e339d027a5d6ec54d811) +Signed-off-by: Michael Tokarev +--- + tests/qemu-iotests/check | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check +index f2e9d27..56d88ca 100755 +--- a/tests/qemu-iotests/check ++++ b/tests/qemu-iotests/check +@@ -184,7 +184,8 @@ if __name__ == '__main__': + sys.exit(str(e)) + + if args.dry_run: +- print('\n'.join([os.path.basename(t) for t in tests])) ++ with env: ++ print('\n'.join([os.path.basename(t) for t in tests])) + else: + with TestRunner(env, tap=args.tap, + color=args.color) as tr: +-- +1.8.3.1 + diff --git a/0081-iotests-give-tempdir-an-identifying-name.patch b/0081-iotests-give-tempdir-an-identifying-name.patch new file mode 100644 index 0000000000000000000000000000000000000000..dd7028fa0244452353e495052bcf9afb1ba42d70 --- /dev/null +++ b/0081-iotests-give-tempdir-an-identifying-name.patch @@ -0,0 +1,39 @@ +From 84c54eaeffd3caf83b0c105b904928b40bad5db9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= +Date: Mon, 5 Feb 2024 15:51:58 +0000 +Subject: [PATCH 088/293] iotests: give tempdir an identifying name +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If something goes wrong causing the iotests not to cleanup their +temporary directory, it is useful if the dir had an identifying +name to show what is to blame. + +Signed-off-by: Daniel P. Berrangé +Message-ID: <20240205155158.1843304-1-berrange@redhat.com> +Revieved-by: Michael Tokarev +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit 7d2faf0ce2ccc896ac56bc5ed2cdf4a55056a8bb) +Signed-off-by: Michael Tokarev +--- + tests/qemu-iotests/testenv.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py +index 3ff38f2..588f30a 100644 +--- a/tests/qemu-iotests/testenv.py ++++ b/tests/qemu-iotests/testenv.py +@@ -126,7 +126,7 @@ def init_directories(self) -> None: + self.tmp_sock_dir = False + Path(self.sock_dir).mkdir(parents=True, exist_ok=True) + except KeyError: +- self.sock_dir = tempfile.mkdtemp() ++ self.sock_dir = tempfile.mkdtemp(prefix="qemu-iotests-") + self.tmp_sock_dir = True + + self.sample_img_dir = os.getenv('SAMPLE_IMG_DIR', +-- +1.8.3.1 + diff --git a/0082-virtio-scsi-Attach-event-vq-notifier-with-no_poll.patch b/0082-virtio-scsi-Attach-event-vq-notifier-with-no_poll.patch new file mode 100644 index 0000000000000000000000000000000000000000..f8c9387f71c1f94893b5baa9783be0c4b6030545 --- /dev/null +++ b/0082-virtio-scsi-Attach-event-vq-notifier-with-no_poll.patch @@ -0,0 +1,72 @@ +From feb2073c866fb2cd600c6783f196139120ff2f9e Mon Sep 17 00:00:00 2001 +From: Hanna Czenczek +Date: Fri, 2 Feb 2024 16:31:56 +0100 +Subject: [PATCH 089/293] virtio-scsi: Attach event vq notifier with no_poll + +As of commit 38738f7dbbda90fbc161757b7f4be35b52205552 ("virtio-scsi: +don't waste CPU polling the event virtqueue"), we only attach an io_read +notifier for the virtio-scsi event virtqueue instead, and no polling +notifiers. During operation, the event virtqueue is typically +non-empty, but none of the buffers are intended to be used immediately. +Instead, they only get used when certain events occur. Therefore, it +makes no sense to continuously poll it when non-empty, because it is +supposed to be and stay non-empty. + +We do this by using virtio_queue_aio_attach_host_notifier_no_poll() +instead of virtio_queue_aio_attach_host_notifier() for the event +virtqueue. + +Commit 766aa2de0f29b657148e04599320d771c36fd126 ("virtio-scsi: implement +BlockDevOps->drained_begin()") however has virtio_scsi_drained_end() use +virtio_queue_aio_attach_host_notifier() for all virtqueues, including +the event virtqueue. This can lead to it being polled again, undoing +the benefit of commit 38738f7dbbda90fbc161757b7f4be35b52205552. + +Fix it by using virtio_queue_aio_attach_host_notifier_no_poll() for the +event virtqueue. + +Reported-by: Fiona Ebner +Fixes: 766aa2de0f29b657148e04599320d771c36fd126 + ("virtio-scsi: implement BlockDevOps->drained_begin()") +Reviewed-by: Stefan Hajnoczi +Tested-by: Fiona Ebner +Reviewed-by: Fiona Ebner +Signed-off-by: Hanna Czenczek +Message-ID: <20240202153158.788922-2-hreitz@redhat.com> +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit c42c3833e0cfdf2b80fb3ca410acfd392b6874ab) +Signed-off-by: Michael Tokarev +--- + hw/scsi/virtio-scsi.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c +index 9c751bf..4b0d476 100644 +--- a/hw/scsi/virtio-scsi.c ++++ b/hw/scsi/virtio-scsi.c +@@ -1149,6 +1149,7 @@ static void virtio_scsi_drained_begin(SCSIBus *bus) + static void virtio_scsi_drained_end(SCSIBus *bus) + { + VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); ++ VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); + VirtIODevice *vdev = VIRTIO_DEVICE(s); + uint32_t total_queues = VIRTIO_SCSI_VQ_NUM_FIXED + + s->parent_obj.conf.num_queues; +@@ -1166,7 +1167,11 @@ static void virtio_scsi_drained_end(SCSIBus *bus) + + for (uint32_t i = 0; i < total_queues; i++) { + VirtQueue *vq = virtio_get_queue(vdev, i); +- virtio_queue_aio_attach_host_notifier(vq, s->ctx); ++ if (vq == vs->event_vq) { ++ virtio_queue_aio_attach_host_notifier_no_poll(vq, s->ctx); ++ } else { ++ virtio_queue_aio_attach_host_notifier(vq, s->ctx); ++ } + } + } + +-- +1.8.3.1 + diff --git a/0083-virtio-Re-enable-notifications-after-drain.patch b/0083-virtio-Re-enable-notifications-after-drain.patch new file mode 100644 index 0000000000000000000000000000000000000000..65f3a7853709fddb7f27cd9afd4a7ce7a8a27a74 --- /dev/null +++ b/0083-virtio-Re-enable-notifications-after-drain.patch @@ -0,0 +1,133 @@ +From 00e50cb42941decc79b61be27a14d009f4d96695 Mon Sep 17 00:00:00 2001 +From: Hanna Czenczek +Date: Fri, 2 Feb 2024 16:31:57 +0100 +Subject: [PATCH 090/293] virtio: Re-enable notifications after drain + +During drain, we do not care about virtqueue notifications, which is why +we remove the handlers on it. When removing those handlers, whether vq +notifications are enabled or not depends on whether we were in polling +mode or not; if not, they are enabled (by default); if so, they have +been disabled by the io_poll_start callback. + +Because we do not care about those notifications after removing the +handlers, this is fine. However, we have to explicitly ensure they are +enabled when re-attaching the handlers, so we will resume receiving +notifications. We do this in virtio_queue_aio_attach_host_notifier*(). +If such a function is called while we are in a polling section, +attaching the notifiers will then invoke the io_poll_start callback, +re-disabling notifications. + +Because we will always miss virtqueue updates in the drained section, we +also need to poll the virtqueue once after attaching the notifiers. + +Buglink: https://issues.redhat.com/browse/RHEL-3934 +Signed-off-by: Hanna Czenczek +Message-ID: <20240202153158.788922-3-hreitz@redhat.com> +Reviewed-by: Stefan Hajnoczi +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit 5bdbaebcce18fe6a627cafad2043ec08f3de5744) +Signed-off-by: Michael Tokarev +--- + hw/virtio/virtio.c | 42 ++++++++++++++++++++++++++++++++++++++++++ + include/block/aio.h | 7 ++++++- + 2 files changed, 48 insertions(+), 1 deletion(-) + +diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c +index 3a160f8..356d690 100644 +--- a/hw/virtio/virtio.c ++++ b/hw/virtio/virtio.c +@@ -3556,6 +3556,17 @@ static void virtio_queue_host_notifier_aio_poll_end(EventNotifier *n) + + void virtio_queue_aio_attach_host_notifier(VirtQueue *vq, AioContext *ctx) + { ++ /* ++ * virtio_queue_aio_detach_host_notifier() can leave notifications disabled. ++ * Re-enable them. (And if detach has not been used before, notifications ++ * being enabled is still the default state while a notifier is attached; ++ * see virtio_queue_host_notifier_aio_poll_end(), which will always leave ++ * notifications enabled once the polling section is left.) ++ */ ++ if (!virtio_queue_get_notification(vq)) { ++ virtio_queue_set_notification(vq, 1); ++ } ++ + aio_set_event_notifier(ctx, &vq->host_notifier, + virtio_queue_host_notifier_read, + virtio_queue_host_notifier_aio_poll, +@@ -3563,6 +3574,13 @@ void virtio_queue_aio_attach_host_notifier(VirtQueue *vq, AioContext *ctx) + aio_set_event_notifier_poll(ctx, &vq->host_notifier, + virtio_queue_host_notifier_aio_poll_begin, + virtio_queue_host_notifier_aio_poll_end); ++ ++ /* ++ * We will have ignored notifications about new requests from the guest ++ * while no notifiers were attached, so "kick" the virt queue to process ++ * those requests now. ++ */ ++ event_notifier_set(&vq->host_notifier); + } + + /* +@@ -3573,14 +3591,38 @@ void virtio_queue_aio_attach_host_notifier(VirtQueue *vq, AioContext *ctx) + */ + void virtio_queue_aio_attach_host_notifier_no_poll(VirtQueue *vq, AioContext *ctx) + { ++ /* See virtio_queue_aio_attach_host_notifier() */ ++ if (!virtio_queue_get_notification(vq)) { ++ virtio_queue_set_notification(vq, 1); ++ } ++ + aio_set_event_notifier(ctx, &vq->host_notifier, + virtio_queue_host_notifier_read, + NULL, NULL); ++ ++ /* ++ * See virtio_queue_aio_attach_host_notifier(). ++ * Note that this may be unnecessary for the type of virtqueues this ++ * function is used for. Still, it will not hurt to have a quick look into ++ * whether we can/should process any of the virtqueue elements. ++ */ ++ event_notifier_set(&vq->host_notifier); + } + + void virtio_queue_aio_detach_host_notifier(VirtQueue *vq, AioContext *ctx) + { + aio_set_event_notifier(ctx, &vq->host_notifier, NULL, NULL, NULL); ++ ++ /* ++ * aio_set_event_notifier_poll() does not guarantee whether io_poll_end() ++ * will run after io_poll_begin(), so by removing the notifier, we do not ++ * know whether virtio_queue_host_notifier_aio_poll_end() has run after a ++ * previous virtio_queue_host_notifier_aio_poll_begin(), i.e. whether ++ * notifications are enabled or disabled. It does not really matter anyway; ++ * we just removed the notifier, so we do not care about notifications until ++ * we potentially re-attach it. The attach_host_notifier functions will ++ * ensure that notifications are enabled again when they are needed. ++ */ + } + + void virtio_queue_host_notifier_read(EventNotifier *n) +diff --git a/include/block/aio.h b/include/block/aio.h +index f08b358..795a375 100644 +--- a/include/block/aio.h ++++ b/include/block/aio.h +@@ -497,9 +497,14 @@ void aio_set_event_notifier(AioContext *ctx, + AioPollFn *io_poll, + EventNotifierHandler *io_poll_ready); + +-/* Set polling begin/end callbacks for an event notifier that has already been ++/* ++ * Set polling begin/end callbacks for an event notifier that has already been + * registered with aio_set_event_notifier. Do nothing if the event notifier is + * not registered. ++ * ++ * Note that if the io_poll_end() callback (or the entire notifier) is removed ++ * during polling, it will not be called, so an io_poll_begin() is not ++ * necessarily always followed by an io_poll_end(). + */ + void aio_set_event_notifier_poll(AioContext *ctx, + EventNotifier *notifier, +-- +1.8.3.1 + diff --git a/0084-virtio-blk-avoid-using-ioeventfd-state-in-irqfd-cond.patch b/0084-virtio-blk-avoid-using-ioeventfd-state-in-irqfd-cond.patch new file mode 100644 index 0000000000000000000000000000000000000000..2ab3f39c95f3132a506101d2634e67410f08b51f --- /dev/null +++ b/0084-virtio-blk-avoid-using-ioeventfd-state-in-irqfd-cond.patch @@ -0,0 +1,67 @@ +From c36d4d3ceeb2c197614d552fc1aab5551d7d57f9 Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +Date: Mon, 22 Jan 2024 12:26:25 -0500 +Subject: [PATCH 091/293] virtio-blk: avoid using ioeventfd state in irqfd + conditional + +Requests that complete in an IOThread use irqfd to notify the guest +while requests that complete in the main loop thread use the traditional +qdev irq code path. The reason for this conditional is that the irq code +path requires the BQL: + + if (s->ioeventfd_started && !s->ioeventfd_disabled) { + virtio_notify_irqfd(vdev, req->vq); + } else { + virtio_notify(vdev, req->vq); + } + +There is a corner case where the conditional invokes the irq code path +instead of the irqfd code path: + + static void virtio_blk_stop_ioeventfd(VirtIODevice *vdev) + { + ... + /* + * Set ->ioeventfd_started to false before draining so that host notifiers + * are not detached/attached anymore. + */ + s->ioeventfd_started = false; + + /* Wait for virtio_blk_dma_restart_bh() and in flight I/O to complete */ + blk_drain(s->conf.conf.blk); + +During blk_drain() the conditional produces the wrong result because +ioeventfd_started is false. + +Use qemu_in_iothread() instead of checking the ioeventfd state. + +Cc: qemu-stable@nongnu.org +Buglink: https://issues.redhat.com/browse/RHEL-15394 +Signed-off-by: Stefan Hajnoczi +Message-ID: <20240122172625.415386-1-stefanha@redhat.com> +Reviewed-by: Kevin Wolf +Signed-off-by: Kevin Wolf +(cherry picked from commit bfa36802d1704fc413c590ebdcc4e5ae0eacf439) +Signed-off-by: Michael Tokarev +(Mjt: fixup for v8.2.0-809-g3cdaf3dd4a + "virtio-blk: rename dataplane to ioeventfd") +--- + hw/block/virtio-blk.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c +index a1f8e15..31aac14 100644 +--- a/hw/block/virtio-blk.c ++++ b/hw/block/virtio-blk.c +@@ -65,7 +65,7 @@ static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status) + iov_discard_undo(&req->inhdr_undo); + iov_discard_undo(&req->outhdr_undo); + virtqueue_push(req->vq, &req->elem, req->in_len); +- if (s->dataplane_started && !s->dataplane_disabled) { ++ if (qemu_in_iothread()) { + virtio_blk_data_plane_notify(s->dataplane, req->vq); + } else { + virtio_notify(vdev, req->vq); +-- +1.8.3.1 + diff --git a/0085-migration-Fix-logic-of-channels-and-transport-compat.patch b/0085-migration-Fix-logic-of-channels-and-transport-compat.patch new file mode 100644 index 0000000000000000000000000000000000000000..9c33a813e8e52462f23121f5d59054db54ce6ae4 --- /dev/null +++ b/0085-migration-Fix-logic-of-channels-and-transport-compat.patch @@ -0,0 +1,71 @@ +From 76c172ffbe931c5018bd781fa565327010068b16 Mon Sep 17 00:00:00 2001 +From: Avihai Horon +Date: Thu, 25 Jan 2024 18:25:12 +0200 +Subject: [PATCH 092/293] migration: Fix logic of channels and transport + compatibility check + +The commit in the fixes line mistakenly modified the channels and +transport compatibility check logic so it now checks multi-channel +support only for socket transport type. + +Thus, running multifd migration using a transport other than socket that +is incompatible with multi-channels (such as "exec") would lead to a +segmentation fault instead of an error message. +For example: + (qemu) migrate_set_capability multifd on + (qemu) migrate -d "exec:cat > /tmp/vm_state" + Segmentation fault (core dumped) + +Fix it by checking multi-channel compatibility for all transport types. + +Cc: qemu-stable +Fixes: d95533e1cdcc ("migration: modify migration_channels_and_uri_compatible() for new QAPI syntax") +Signed-off-by: Avihai Horon +Reviewed-by: Peter Xu +Link: https://lore.kernel.org/r/20240125162528.7552-2-avihaih@nvidia.com +Signed-off-by: Peter Xu +(cherry picked from commit 3205bebd4fc6dd501fb8b10c93ddce9da18e09db) +Signed-off-by: Michael Tokarev +--- + migration/migration.c | 17 +++++++++++------ + 1 file changed, 11 insertions(+), 6 deletions(-) + +diff --git a/migration/migration.c b/migration/migration.c +index ee5e0ba..982ab85 100644 +--- a/migration/migration.c ++++ b/migration/migration.c +@@ -128,11 +128,17 @@ static bool migration_needs_multiple_sockets(void) + return migrate_multifd() || migrate_postcopy_preempt(); + } + +-static bool transport_supports_multi_channels(SocketAddress *saddr) ++static bool transport_supports_multi_channels(MigrationAddress *addr) + { +- return saddr->type == SOCKET_ADDRESS_TYPE_INET || +- saddr->type == SOCKET_ADDRESS_TYPE_UNIX || +- saddr->type == SOCKET_ADDRESS_TYPE_VSOCK; ++ if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) { ++ SocketAddress *saddr = &addr->u.socket; ++ ++ return saddr->type == SOCKET_ADDRESS_TYPE_INET || ++ saddr->type == SOCKET_ADDRESS_TYPE_UNIX || ++ saddr->type == SOCKET_ADDRESS_TYPE_VSOCK; ++ } ++ ++ return false; + } + + static bool +@@ -140,8 +146,7 @@ migration_channels_and_transport_compatible(MigrationAddress *addr, + Error **errp) + { + if (migration_needs_multiple_sockets() && +- (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) && +- !transport_supports_multi_channels(&addr->u.socket)) { ++ !transport_supports_multi_channels(addr)) { + error_setg(errp, "Migration requires multi-channel URIs (e.g. tcp)"); + return false; + } +-- +1.8.3.1 + diff --git a/0086-hw-riscv-virt-acpi-build.c-fix-leak-in-build_rhct.patch b/0086-hw-riscv-virt-acpi-build.c-fix-leak-in-build_rhct.patch new file mode 100644 index 0000000000000000000000000000000000000000..ff0eb199cdefe27958d997beb305ff9a04bded0d --- /dev/null +++ b/0086-hw-riscv-virt-acpi-build.c-fix-leak-in-build_rhct.patch @@ -0,0 +1,54 @@ +From eca4e19914baba9fd73f774eb517acdd80add813 Mon Sep 17 00:00:00 2001 +From: Daniel Henrique Barboza +Date: Mon, 22 Jan 2024 19:15:23 -0300 +Subject: [PATCH 093/293] hw/riscv/virt-acpi-build.c: fix leak in build_rhct() + +The 'isa' char pointer isn't being freed after use. + +Issue detected by Valgrind: + +==38752== 128 bytes in 1 blocks are definitely lost in loss record 3,190 of 3,884 +==38752== at 0x484280F: malloc (vg_replace_malloc.c:442) +==38752== by 0x5189619: g_malloc (gmem.c:130) +==38752== by 0x51A5BF2: g_strconcat (gstrfuncs.c:628) +==38752== by 0x6C1E3E: riscv_isa_string_ext (cpu.c:2321) +==38752== by 0x6C1E3E: riscv_isa_string (cpu.c:2343) +==38752== by 0x6BD2EA: build_rhct (virt-acpi-build.c:232) +==38752== by 0x6BD2EA: virt_acpi_build (virt-acpi-build.c:556) +==38752== by 0x6BDC86: virt_acpi_setup (virt-acpi-build.c:662) +==38752== by 0x9C8DC6: notifier_list_notify (notify.c:39) +==38752== by 0x4A595A: qdev_machine_creation_done (machine.c:1589) +==38752== by 0x61E052: qemu_machine_creation_done (vl.c:2680) +==38752== by 0x61E052: qmp_x_exit_preconfig.part.0 (vl.c:2709) +==38752== by 0x6220C6: qmp_x_exit_preconfig (vl.c:2702) +==38752== by 0x6220C6: qemu_init (vl.c:3758) +==38752== by 0x425858: main (main.c:47) + +Fixes: ebfd392893 ("hw/riscv/virt: virt-acpi-build.c: Add RHCT Table") +Signed-off-by: Daniel Henrique Barboza +Reviewed-by: Alistair Francis +Message-ID: <20240122221529.86562-2-dbarboza@ventanamicro.com> +Signed-off-by: Alistair Francis +(cherry picked from commit 1a49762c07d001ce291e4fc6773317f5611af3a4) +Signed-off-by: Michael Tokarev +(Mjt: context fixup) +--- + hw/riscv/virt-acpi-build.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c +index 7331248..171d56a 100644 +--- a/hw/riscv/virt-acpi-build.c ++++ b/hw/riscv/virt-acpi-build.c +@@ -132,7 +132,7 @@ static void build_rhct(GArray *table_data, + size_t len, aligned_len; + uint32_t isa_offset, num_rhct_nodes; + RISCVCPU *cpu; +- char *isa; ++ g_autofree char *isa = NULL; + + AcpiTable table = { .sig = "RHCT", .rev = 1, .oem_id = s->oem_id, + .oem_table_id = s->oem_table_id }; +-- +1.8.3.1 + diff --git a/0087-tests-docker-Add-sqlite3-module-to-openSUSE-Leap-con.patch b/0087-tests-docker-Add-sqlite3-module-to-openSUSE-Leap-con.patch new file mode 100644 index 0000000000000000000000000000000000000000..a3721358b267a130e8467be14988d684fecd2b30 --- /dev/null +++ b/0087-tests-docker-Add-sqlite3-module-to-openSUSE-Leap-con.patch @@ -0,0 +1,80 @@ +From cefca32a2440ffc7bfb247b064d713d9de7c4489 Mon Sep 17 00:00:00 2001 +From: Fabiano Rosas +Date: Wed, 7 Feb 2024 16:37:59 +0000 +Subject: [PATCH 094/293] tests/docker: Add sqlite3 module to openSUSE Leap + container +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Avocado needs sqlite3: + + Failed to load plugin from module "avocado.plugins.journal": + ImportError("Module 'sqlite3' is not installed. + Use: sudo zypper install python311 to install it") + +>From 'zypper info python311': + "This package supplies rich command line features provided by + readline, and sqlite3 support for the interpreter core, thus forming + a so called "extended" runtime." + +Include the appropriate package in the lcitool mappings which will +guarantee the dockerfile gets properly updated when lcitool is +run. Also include the updated dockerfile. + +Signed-off-by: Fabiano Rosas +Suggested-by: Andrea Bolognani +Reviewed-by: Philippe Mathieu-Daudé +Message-Id: <20240117164227.32143-1-farosas@suse.de> +Signed-off-by: Alex Bennée +Message-Id: <20240207163812.3231697-2-alex.bennee@linaro.org> +(cherry picked from commit 7485508341f4e8c6802f7716a64dd49a4dd28d22) +Signed-off-by: Michael Tokarev +--- + tests/docker/dockerfiles/opensuse-leap.docker | 1 + + tests/lcitool/mappings.yml | 4 ++++ + tests/lcitool/projects/qemu.yml | 1 + + 3 files changed, 6 insertions(+) + +diff --git a/tests/docker/dockerfiles/opensuse-leap.docker b/tests/docker/dockerfiles/opensuse-leap.docker +index dc0e36c..cf75338 100644 +--- a/tests/docker/dockerfiles/opensuse-leap.docker ++++ b/tests/docker/dockerfiles/opensuse-leap.docker +@@ -90,6 +90,7 @@ RUN zypper update -y && \ + pcre-devel-static \ + pipewire-devel \ + pkgconfig \ ++ python311 \ + python311-base \ + python311-pip \ + python311-setuptools \ +diff --git a/tests/lcitool/mappings.yml b/tests/lcitool/mappings.yml +index 0b90888..407c033 100644 +--- a/tests/lcitool/mappings.yml ++++ b/tests/lcitool/mappings.yml +@@ -59,6 +59,10 @@ mappings: + CentOSStream8: + OpenSUSELeap15: + ++ python3-sqlite3: ++ CentOSStream8: python38 ++ OpenSUSELeap15: python311 ++ + python3-tomli: + # test using tomllib + apk: +diff --git a/tests/lcitool/projects/qemu.yml b/tests/lcitool/projects/qemu.yml +index 82092c9..149b15d 100644 +--- a/tests/lcitool/projects/qemu.yml ++++ b/tests/lcitool/projects/qemu.yml +@@ -97,6 +97,7 @@ packages: + - python3-pip + - python3-sphinx + - python3-sphinx-rtd-theme ++ - python3-sqlite3 + - python3-tomli + - python3-venv + - rpm2cpio +-- +1.8.3.1 + diff --git a/0088-configure-run-plugin-TCG-tests-again.patch b/0088-configure-run-plugin-TCG-tests-again.patch new file mode 100644 index 0000000000000000000000000000000000000000..6921c42c4438a7e7621174376e0ae19f78e76c17 --- /dev/null +++ b/0088-configure-run-plugin-TCG-tests-again.patch @@ -0,0 +1,46 @@ +From 6eeeb8733177db7bc23fb2e7271dea759b47e4f9 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Wed, 7 Feb 2024 16:38:01 +0000 +Subject: [PATCH 095/293] configure: run plugin TCG tests again +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Commit 39fb3cfc28b ("configure: clean up plugin option handling", 2023-10-18) +dropped the CONFIG_PLUGIN line from tests/tcg/config-host.mak, due to confusion +caused by the shadowing of $config_host_mak. However, TCG tests were still +expecting it. Oops. + +Put it back, in the meanwhile the shadowing is gone so it's clear that it goes +in the tests/tcg configuration. + +Cc: +Fixes: 39fb3cfc28b ("configure: clean up plugin option handling", 2023-10-18) +Signed-off-by: Paolo Bonzini +Message-Id: <20240124115332.612162-1-pbonzini@redhat.com> +Signed-off-by: Alex Bennée +Message-Id: <20240207163812.3231697-4-alex.bennee@linaro.org> +(cherry picked from commit 15cc103362499bd94c5aec5fa66543d0de3bf4b5) +Signed-off-by: Michael Tokarev +(Mjt: context fixup) +--- + configure | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/configure b/configure +index d7e0926..d3ab436 100755 +--- a/configure ++++ b/configure +@@ -1675,6 +1675,9 @@ fi + mkdir -p tests/tcg + echo "# Automatically generated by configure - do not modify" > $config_host_mak + echo "SRC_PATH=$source_path" >> $config_host_mak ++if test "$plugins" = "yes" ; then ++ echo "CONFIG_PLUGIN=y" >> tests/tcg/$config_host_mak ++fi + + tcg_tests_targets= + for target in $target_list; do +-- +1.8.3.1 + diff --git a/0089-hw-smbios-Fix-OEM-strings-table-option-validation.patch b/0089-hw-smbios-Fix-OEM-strings-table-option-validation.patch new file mode 100644 index 0000000000000000000000000000000000000000..04011f127a9d08d83fcfa5c4f659552c3b94267f --- /dev/null +++ b/0089-hw-smbios-Fix-OEM-strings-table-option-validation.patch @@ -0,0 +1,48 @@ +From d6e07d59169e16beba99fd139d1fb99ca197c555 Mon Sep 17 00:00:00 2001 +From: Akihiko Odaki +Date: Mon, 29 Jan 2024 17:03:07 +0900 +Subject: [PATCH 096/293] hw/smbios: Fix OEM strings table option validation + +qemu_smbios_type11_opts did not have the list terminator and that +resulted in out-of-bound memory access. It also needs to have an element +for the type option. + +Cc: qemu-stable@nongnu.org +Fixes: 2d6dcbf93fb0 ("smbios: support setting OEM strings table") +Signed-off-by: Akihiko Odaki +Reviewed-by: Michael Tokarev +Reviewed-by: Ani Sinha +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael Tokarev +(cherry picked from commit cd8a35b913c24248267c682cb9a348461c106139) +--- + hw/smbios/smbios.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c +index 2a90601..522ed1e 100644 +--- a/hw/smbios/smbios.c ++++ b/hw/smbios/smbios.c +@@ -370,6 +370,11 @@ static const QemuOptDesc qemu_smbios_type8_opts[] = { + + static const QemuOptDesc qemu_smbios_type11_opts[] = { + { ++ .name = "type", ++ .type = QEMU_OPT_NUMBER, ++ .help = "SMBIOS element type", ++ }, ++ { + .name = "value", + .type = QEMU_OPT_STRING, + .help = "OEM string data", +@@ -379,6 +384,7 @@ static const QemuOptDesc qemu_smbios_type11_opts[] = { + .type = QEMU_OPT_STRING, + .help = "OEM string data from file", + }, ++ { /* end of list */ } + }; + + static const QemuOptDesc qemu_smbios_type17_opts[] = { +-- +1.8.3.1 + diff --git a/0090-hw-smbios-Fix-port-connector-option-validation.patch b/0090-hw-smbios-Fix-port-connector-option-validation.patch new file mode 100644 index 0000000000000000000000000000000000000000..146b798d4a2686e39d76c3367084e380681acd36 --- /dev/null +++ b/0090-hw-smbios-Fix-port-connector-option-validation.patch @@ -0,0 +1,48 @@ +From 9ab476c3de1c2ff5851f00671cd24d6a16efff54 Mon Sep 17 00:00:00 2001 +From: Akihiko Odaki +Date: Mon, 29 Jan 2024 17:03:08 +0900 +Subject: [PATCH 097/293] hw/smbios: Fix port connector option validation + +qemu_smbios_type8_opts did not have the list terminator and that +resulted in out-of-bound memory access. It also needs to have an element +for the type option. + +Cc: qemu-stable@nongnu.org +Fixes: fd8caa253c56 ("hw/smbios: support for type 8 (port connector)") +Signed-off-by: Akihiko Odaki +Reviewed-by: Michael Tokarev +Reviewed-by: Ani Sinha +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael Tokarev +(cherry picked from commit 196578c9d051d19c23e6c13e97b791a41b318315) +--- + hw/smbios/smbios.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c +index 522ed1e..8a44d3f 100644 +--- a/hw/smbios/smbios.c ++++ b/hw/smbios/smbios.c +@@ -347,6 +347,11 @@ static const QemuOptDesc qemu_smbios_type4_opts[] = { + + static const QemuOptDesc qemu_smbios_type8_opts[] = { + { ++ .name = "type", ++ .type = QEMU_OPT_NUMBER, ++ .help = "SMBIOS element type", ++ }, ++ { + .name = "internal_reference", + .type = QEMU_OPT_STRING, + .help = "internal reference designator", +@@ -366,6 +371,7 @@ static const QemuOptDesc qemu_smbios_type8_opts[] = { + .type = QEMU_OPT_NUMBER, + .help = "port type", + }, ++ { /* end of list */ } + }; + + static const QemuOptDesc qemu_smbios_type11_opts[] = { +-- +1.8.3.1 + diff --git a/0091-hw-net-tulip-add-chip-status-register-values.patch b/0091-hw-net-tulip-add-chip-status-register-values.patch new file mode 100644 index 0000000000000000000000000000000000000000..3c25a670a9c464b4d351216fbc589269d70f84f6 --- /dev/null +++ b/0091-hw-net-tulip-add-chip-status-register-values.patch @@ -0,0 +1,56 @@ +From 281fea01d6d71ea8f5b33390f32886f054813cf6 Mon Sep 17 00:00:00 2001 +From: Sven Schnelle +Date: Mon, 5 Feb 2024 20:47:17 +0100 +Subject: [PATCH 098/293] hw/net/tulip: add chip status register values +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Netbsd isn't able to detect a link on the emulated tulip card. That's +because netbsd reads the Chip Status Register of the Phy (address +0x14). The default phy data in the qemu tulip driver is all zero, +which means no link is established and autonegotation isn't complete. + +Therefore set the register to 0x3b40, which means: + +Link is up, Autonegotation complete, Full Duplex, 100MBit/s Link +speed. + +Also clear the mask because this register is read only. + +Signed-off-by: Sven Schnelle +Reviewed-by: Philippe Mathieu-Daudé +Reviewed-by: Helge Deller +Tested-by: Helge Deller +Signed-off-by: Helge Deller +(cherry picked from commit 9b60a3ed5569a70bbdd29e3c9ec4c5d4685c6e2c) +Signed-off-by: Michael Tokarev +--- + hw/net/tulip.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/hw/net/tulip.c b/hw/net/tulip.c +index 962086a..f21b8ca 100644 +--- a/hw/net/tulip.c ++++ b/hw/net/tulip.c +@@ -421,7 +421,7 @@ static uint16_t tulip_mdi_default[] = { + /* MDI Registers 8 - 15 */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* MDI Registers 16 - 31 */ +- 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, ++ 0x0003, 0x0000, 0x0001, 0x0000, 0x3b40, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + }; + +@@ -429,7 +429,7 @@ static uint16_t tulip_mdi_default[] = { + static const uint16_t tulip_mdi_mask[] = { + 0x0000, 0xffff, 0xffff, 0xffff, 0xc01f, 0xffff, 0xffff, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +- 0x0fff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, ++ 0x0fff, 0x0000, 0xffff, 0xffff, 0x0000, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + }; + +-- +1.8.3.1 + diff --git a/0092-tcg-Increase-width-of-temp_subindex.patch b/0092-tcg-Increase-width-of-temp_subindex.patch new file mode 100644 index 0000000000000000000000000000000000000000..0db98ddd611e94cee8b0344abc6a46e9f837bc98 --- /dev/null +++ b/0092-tcg-Increase-width-of-temp_subindex.patch @@ -0,0 +1,39 @@ +From e5f105655c226262d9d7529dc8ee70402d9ef4ad Mon Sep 17 00:00:00 2001 +From: Richard Henderson +Date: Mon, 12 Feb 2024 16:15:59 -1000 +Subject: [PATCH 099/293] tcg: Increase width of temp_subindex +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +We need values 0-3 for TCG_TYPE_I128 on 32-bit hosts. + +Cc: qemu-stable@nongnu.org +Fixes: 43eef72f4109 ("tcg: Add temp allocation for TCGv_i128") +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2159 +Signed-off-by: Richard Henderson +Reviewed-by: Philippe Mathieu-Daudé +Reviewed-by: Michael Tokarev +Tested-by: Michael Tokarev +(cherry picked from commit c0e688153f299d5d493989c80bcc84c9cf36d6a6) +Signed-off-by: Michael Tokarev +--- + include/tcg/tcg.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h +index daf2a5b..451f3fe 100644 +--- a/include/tcg/tcg.h ++++ b/include/tcg/tcg.h +@@ -412,7 +412,7 @@ typedef struct TCGTemp { + unsigned int mem_coherent:1; + unsigned int mem_allocated:1; + unsigned int temp_allocated:1; +- unsigned int temp_subindex:1; ++ unsigned int temp_subindex:2; + + int64_t val; + struct TCGTemp *mem_base; +-- +1.8.3.1 + diff --git a/0093-tcg-arm-Fix-goto_tb-for-large-translation-blocks.patch b/0093-tcg-arm-Fix-goto_tb-for-large-translation-blocks.patch new file mode 100644 index 0000000000000000000000000000000000000000..a1551f22ed3840c1f2e39fcfb6041f1457e5cc81 --- /dev/null +++ b/0093-tcg-arm-Fix-goto_tb-for-large-translation-blocks.patch @@ -0,0 +1,38 @@ +From 181e54871574bf5aa10b5a44fec94f198bc912ce Mon Sep 17 00:00:00 2001 +From: Richard Henderson +Date: Mon, 12 Feb 2024 20:38:18 +0000 +Subject: [PATCH 100/293] tcg/arm: Fix goto_tb for large translation blocks + +Correct arithmetic for separating high and low +on a large negative number. + +Cc: qemu-stable@nongnu.org +Fixes: 79ffece4447 ("tcg/arm: Implement direct branch for goto_tb") +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1714 +Signed-off-by: Richard Henderson +Reviewed-by: Michael Tokarev +(cherry picked from commit e41f1825b43796c3508ef309ed0b150ef89acc44) +Signed-off-by: Michael Tokarev +--- + tcg/arm/tcg-target.c.inc | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc +index a9aa8aa..c9a47b7 100644 +--- a/tcg/arm/tcg-target.c.inc ++++ b/tcg/arm/tcg-target.c.inc +@@ -1736,9 +1736,9 @@ static void tcg_out_goto_tb(TCGContext *s, int which) + * shifted immediate from pc. + */ + int h = -i_disp; +- int l = h & 0xfff; ++ int l = -(h & 0xfff); + +- h = encode_imm_nofail(h - l); ++ h = encode_imm_nofail(h + l); + tcg_out_dat_imm(s, COND_AL, ARITH_SUB, TCG_REG_R0, TCG_REG_PC, h); + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_R0, l); + } +-- +1.8.3.1 + diff --git a/0094-vhost-user.rst-Fix-vring-address-description.patch b/0094-vhost-user.rst-Fix-vring-address-description.patch new file mode 100644 index 0000000000000000000000000000000000000000..d908db3a533df4a82f4ae113fa214a22f91628dd --- /dev/null +++ b/0094-vhost-user.rst-Fix-vring-address-description.patch @@ -0,0 +1,38 @@ +From 17ae7ebedcbd958c03a0878ff92d970ebca123c6 Mon Sep 17 00:00:00 2001 +From: Andrey Ignatov +Date: Thu, 11 Jan 2024 16:45:55 -0800 +Subject: [PATCH 101/293] vhost-user.rst: Fix vring address description + +There is no "size" field in vring address structure. Remove it. + +Fixes: 5fc0e00291 ("Add vhost-user protocol documentation") +Signed-off-by: Andrey Ignatov +Message-Id: <20240112004555.64900-1-rdna@apple.com> +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit aa05bd9ef4073ccb72d04ad78de32916af31c7c3) +Signed-off-by: Michael Tokarev +--- + docs/interop/vhost-user.rst | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/docs/interop/vhost-user.rst b/docs/interop/vhost-user.rst +index 9f1103f..ad6e142 100644 +--- a/docs/interop/vhost-user.rst ++++ b/docs/interop/vhost-user.rst +@@ -148,9 +148,9 @@ Vring descriptor indices for packed virtqueues + A vring address description + ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +-+-------+-------+------+------------+------+-----------+-----+ +-| index | flags | size | descriptor | used | available | log | +-+-------+-------+------+------------+------+-----------+-----+ +++-------+-------+------------+------+-----------+-----+ ++| index | flags | descriptor | used | available | log | +++-------+-------+------------+------+-----------+-----+ + + :index: a 32-bit vring index + +-- +1.8.3.1 + diff --git a/0095-cxl-cdat-Handle-cdat-table-build-errors.patch b/0095-cxl-cdat-Handle-cdat-table-build-errors.patch new file mode 100644 index 0000000000000000000000000000000000000000..58679e226ee5c13a07e09a9e3b9267a07935c145 --- /dev/null +++ b/0095-cxl-cdat-Handle-cdat-table-build-errors.patch @@ -0,0 +1,42 @@ +From 89970831184893c21edcf455e0d91aaedd27a02d Mon Sep 17 00:00:00 2001 +From: Ira Weiny +Date: Fri, 26 Jan 2024 12:01:21 +0000 +Subject: [PATCH 102/293] cxl/cdat: Handle cdat table build errors + +The callback for building CDAT tables may return negative error codes. +This was previously unhandled and will result in potentially huge +allocations later on in ct3_build_cdat() + +Detect the negative error code and defer cdat building. + +Fixes: f5ee7413d592 ("hw/mem/cxl-type3: Add CXL CDAT Data Object Exchange") +Cc: Huai-Cheng Kuo +Reviewed-by: Dave Jiang +Reviewed-by: Fan Ni +Signed-off-by: Ira Weiny +Signed-off-by: Jonathan Cameron +Message-Id: <20240126120132.24248-2-Jonathan.Cameron@huawei.com> +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit c62926f730d08450502d36548e28dd727c998ace) +Signed-off-by: Michael Tokarev +--- + hw/cxl/cxl-cdat.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/cxl/cxl-cdat.c b/hw/cxl/cxl-cdat.c +index 639a2db..24829cf 100644 +--- a/hw/cxl/cxl-cdat.c ++++ b/hw/cxl/cxl-cdat.c +@@ -63,7 +63,7 @@ static void ct3_build_cdat(CDATObject *cdat, Error **errp) + cdat->built_buf_len = cdat->build_cdat_table(&cdat->built_buf, + cdat->private); + +- if (!cdat->built_buf_len) { ++ if (cdat->built_buf_len <= 0) { + /* Build later as not all data available yet */ + cdat->to_update = true; + return; +-- +1.8.3.1 + diff --git a/0096-cxl-cdat-Fix-header-sum-value-in-CDAT-checksum.patch b/0096-cxl-cdat-Fix-header-sum-value-in-CDAT-checksum.patch new file mode 100644 index 0000000000000000000000000000000000000000..ed411a6d2ec6c9643e2608063b760b34f8f2a68e --- /dev/null +++ b/0096-cxl-cdat-Fix-header-sum-value-in-CDAT-checksum.patch @@ -0,0 +1,63 @@ +From 9d8a2a8aafa575429ccb001c99827ea199fbe7a6 Mon Sep 17 00:00:00 2001 +From: Ira Weiny +Date: Fri, 26 Jan 2024 12:01:24 +0000 +Subject: [PATCH 103/293] cxl/cdat: Fix header sum value in CDAT checksum + +The addition of the DCD support for CXL type-3 devices extended the CDAT +table large enough that the checksum being returned was incorrect.[1] + +This was because the checksum value was using the header length field +rather than each of the 4 bytes of the length field. This was +previously not seen because the length of the CDAT data was less than +256 thus resulting in an equivalent checksum value. + +Properly calculate the checksum for the CDAT header. + +[1] https://lore.kernel.org/all/20231116-fix-cdat-devm-free-v1-1-b148b40707d7@intel.com/ + +Fixes: aba578bdace5 ("hw/cxl/cdat: CXL CDAT Data Object Exchange implementation") +Cc: Huai-Cheng Kuo +Signed-off-by: Ira Weiny +Reviewed-by: Dave Jiang +Reviewed-by: Fan Ni +Signed-off-by: Jonathan Cameron + +Message-Id: <20240126120132.24248-5-Jonathan.Cameron@huawei.com> +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 64fdad5e67587e88c2f1d8f294e89403856a4a31) +Signed-off-by: Michael Tokarev +--- + hw/cxl/cxl-cdat.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/hw/cxl/cxl-cdat.c b/hw/cxl/cxl-cdat.c +index 24829cf..2fea975 100644 +--- a/hw/cxl/cxl-cdat.c ++++ b/hw/cxl/cxl-cdat.c +@@ -49,6 +49,7 @@ static void ct3_build_cdat(CDATObject *cdat, Error **errp) + g_autofree CDATTableHeader *cdat_header = NULL; + g_autofree CDATEntry *cdat_st = NULL; + uint8_t sum = 0; ++ uint8_t *hdr_buf; + int ent, i; + + /* Use default table if fopen == NULL */ +@@ -95,8 +96,12 @@ static void ct3_build_cdat(CDATObject *cdat, Error **errp) + /* For now, no runtime updates */ + cdat_header->sequence = 0; + cdat_header->length += sizeof(CDATTableHeader); +- sum += cdat_header->revision + cdat_header->sequence + +- cdat_header->length; ++ ++ hdr_buf = (uint8_t *)cdat_header; ++ for (i = 0; i < sizeof(*cdat_header); i++) { ++ sum += hdr_buf[i]; ++ } ++ + /* Sum of all bytes including checksum must be 0 */ + cdat_header->checksum = ~sum + 1; + +-- +1.8.3.1 + diff --git a/0097-hw-cxl-device-read-from-register-values-in-mdev_reg_.patch b/0097-hw-cxl-device-read-from-register-values-in-mdev_reg_.patch new file mode 100644 index 0000000000000000000000000000000000000000..e36adab64e2cf9c2512b6535fae70ce054992514 --- /dev/null +++ b/0097-hw-cxl-device-read-from-register-values-in-mdev_reg_.patch @@ -0,0 +1,105 @@ +From bdd3159ad724e4e0aad571b4352b288b8f947ddd Mon Sep 17 00:00:00 2001 +From: Hyeonggon Yoo <42.hyeyoo@gmail.com> +Date: Fri, 26 Jan 2024 12:01:26 +0000 +Subject: [PATCH 104/293] hw/cxl/device: read from register values in + mdev_reg_read() + +In the current mdev_reg_read() implementation, it consistently returns +that the Media Status is Ready (01b). This was fine until commit +25a52959f99d ("hw/cxl: Add support for device sanitation") because the +media was presumed to be ready. + +However, as per the CXL 3.0 spec "8.2.9.8.5.1 Sanitize (Opcode 4400h)", +during sanitation, the Media State should be set to Disabled (11b). The +mentioned commit correctly sets it to Disabled, but mdev_reg_read() +still returns Media Status as Ready. + +To address this, update mdev_reg_read() to read register values instead +of returning dummy values. + +Note that __toggle_media() managed to not only write something +that no one read, it did it to the wrong register storage and +so changed the reported mailbox size which was definitely not +the intent. That gets fixed as a side effect of allocating +separate state storage for this register. + +Fixes: commit 25a52959f99d ("hw/cxl: Add support for device sanitation") +Signed-off-by: Hyeonggon Yoo <42.hyeyoo@gmail.com> +Reviewed-by: Fan Ni +Signed-off-by: Jonathan Cameron +Message-Id: <20240126120132.24248-7-Jonathan.Cameron@huawei.com> +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit f7509f462c788a347521f90f19d623908c4fbcc5) +Signed-off-by: Michael Tokarev +--- + hw/cxl/cxl-device-utils.c | 17 +++++++++++------ + include/hw/cxl/cxl_device.h | 9 +++++++-- + 2 files changed, 18 insertions(+), 8 deletions(-) + +diff --git a/hw/cxl/cxl-device-utils.c b/hw/cxl/cxl-device-utils.c +index 61a3c4d..40b619f 100644 +--- a/hw/cxl/cxl-device-utils.c ++++ b/hw/cxl/cxl-device-utils.c +@@ -229,12 +229,9 @@ static void mailbox_reg_write(void *opaque, hwaddr offset, uint64_t value, + + static uint64_t mdev_reg_read(void *opaque, hwaddr offset, unsigned size) + { +- uint64_t retval = 0; +- +- retval = FIELD_DP64(retval, CXL_MEM_DEV_STS, MEDIA_STATUS, 1); +- retval = FIELD_DP64(retval, CXL_MEM_DEV_STS, MBOX_READY, 1); ++ CXLDeviceState *cxl_dstate = opaque; + +- return retval; ++ return cxl_dstate->memdev_status; + } + + static void ro_reg_write(void *opaque, hwaddr offset, uint64_t value, +@@ -371,7 +368,15 @@ static void mailbox_reg_init_common(CXLDeviceState *cxl_dstate) + cxl_dstate->mbox_msi_n = msi_n; + } + +-static void memdev_reg_init_common(CXLDeviceState *cxl_dstate) { } ++static void memdev_reg_init_common(CXLDeviceState *cxl_dstate) ++{ ++ uint64_t memdev_status_reg; ++ ++ memdev_status_reg = FIELD_DP64(0, CXL_MEM_DEV_STS, MEDIA_STATUS, 1); ++ memdev_status_reg = FIELD_DP64(memdev_status_reg, CXL_MEM_DEV_STS, ++ MBOX_READY, 1); ++ cxl_dstate->memdev_status = memdev_status_reg; ++} + + void cxl_device_register_init_t3(CXLType3Dev *ct3d) + { +diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h +index befb5f8..31d2afc 100644 +--- a/include/hw/cxl/cxl_device.h ++++ b/include/hw/cxl/cxl_device.h +@@ -202,6 +202,9 @@ typedef struct cxl_device_state { + }; + }; + ++ /* Stash the memory device status value */ ++ uint64_t memdev_status; ++ + struct { + bool set; + uint64_t last_set; +@@ -353,8 +356,10 @@ static inline void __toggle_media(CXLDeviceState *cxl_dstate, int val) + { + uint64_t dev_status_reg; + +- dev_status_reg = FIELD_DP64(0, CXL_MEM_DEV_STS, MEDIA_STATUS, val); +- cxl_dstate->mbox_reg_state64[R_CXL_MEM_DEV_STS] = dev_status_reg; ++ dev_status_reg = cxl_dstate->memdev_status; ++ dev_status_reg = FIELD_DP64(dev_status_reg, CXL_MEM_DEV_STS, MEDIA_STATUS, ++ val); ++ cxl_dstate->memdev_status = dev_status_reg; + } + #define cxl_dev_disable_media(cxlds) \ + do { __toggle_media((cxlds), 0x3); } while (0) +-- +1.8.3.1 + diff --git a/0098-hw-cxl-Pass-CXLComponentState-to-cache_mem_ops.patch b/0098-hw-cxl-Pass-CXLComponentState-to-cache_mem_ops.patch new file mode 100644 index 0000000000000000000000000000000000000000..0eeb8e8d2c0ed4b656606c943d2c1606deb0b5e7 --- /dev/null +++ b/0098-hw-cxl-Pass-CXLComponentState-to-cache_mem_ops.patch @@ -0,0 +1,40 @@ +From bbe51d6ea3a9adcca3b46ce8f19b9b4ead488e28 Mon Sep 17 00:00:00 2001 +From: Li Zhijian +Date: Fri, 26 Jan 2024 12:01:27 +0000 +Subject: [PATCH 105/293] hw/cxl: Pass CXLComponentState to cache_mem_ops + +cache_mem_ops.{read,write}() interprets opaque as +CXLComponentState(cxl_cstate) instead of ComponentRegisters(cregs). + +Fortunately, cregs is the first member of cxl_cstate, so their values are +the same. + +Fixes: 9e58f52d3f8 ("hw/cxl/component: Introduce CXL components (8.1.x, 8.2.5)") +Reviewed-by: Fan Ni +Signed-off-by: Li Zhijian +Signed-off-by: Jonathan Cameron +Message-Id: <20240126120132.24248-8-Jonathan.Cameron@huawei.com> +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 729d45a6af06753d3e330f589c248fe9687c5cd5) +Signed-off-by: Michael Tokarev +--- + hw/cxl/cxl-component-utils.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/cxl/cxl-component-utils.c b/hw/cxl/cxl-component-utils.c +index 29d4774..9dfde6c 100644 +--- a/hw/cxl/cxl-component-utils.c ++++ b/hw/cxl/cxl-component-utils.c +@@ -199,7 +199,7 @@ void cxl_component_register_block_init(Object *obj, + /* io registers controls link which we don't care about in QEMU */ + memory_region_init_io(&cregs->io, obj, NULL, cregs, ".io", + CXL2_COMPONENT_IO_REGION_SIZE); +- memory_region_init_io(&cregs->cache_mem, obj, &cache_mem_ops, cregs, ++ memory_region_init_io(&cregs->cache_mem, obj, &cache_mem_ops, cxl_cstate, + ".cache_mem", CXL2_COMPONENT_CM_REGION_SIZE); + + memory_region_add_subregion(&cregs->component_registers, 0, &cregs->io); +-- +1.8.3.1 + diff --git a/0099-virtio-gpu-Correct-virgl_renderer_resource_get_info-.patch b/0099-virtio-gpu-Correct-virgl_renderer_resource_get_info-.patch new file mode 100644 index 0000000000000000000000000000000000000000..d82a2942353361e568d29226259b166a272fe27f --- /dev/null +++ b/0099-virtio-gpu-Correct-virgl_renderer_resource_get_info-.patch @@ -0,0 +1,72 @@ +From 1c38c8a24a9d053d42e3892fddf18421cf3a67d3 Mon Sep 17 00:00:00 2001 +From: Dmitry Osipenko +Date: Mon, 29 Jan 2024 10:39:21 +0300 +Subject: [PATCH 106/293] virtio-gpu: Correct + virgl_renderer_resource_get_info() error check +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +virgl_renderer_resource_get_info() returns errno and not -1 on error. +Correct the return-value check. + +Reviewed-by: Marc-André Lureau +Signed-off-by: Dmitry Osipenko +Message-Id: <20240129073921.446869-1-dmitry.osipenko@collabora.com> +Cc: qemu-stable@nongnu.org +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 574b64aa6754ba491f51024c5a823a674d48a658) +Signed-off-by: Michael Tokarev +--- + contrib/vhost-user-gpu/virgl.c | 6 +++--- + hw/display/virtio-gpu-virgl.c | 2 +- + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/contrib/vhost-user-gpu/virgl.c b/contrib/vhost-user-gpu/virgl.c +index d1ccdf7..51da0e3 100644 +--- a/contrib/vhost-user-gpu/virgl.c ++++ b/contrib/vhost-user-gpu/virgl.c +@@ -327,7 +327,7 @@ virgl_get_resource_info_modifiers(uint32_t resource_id, + #ifdef VIRGL_RENDERER_RESOURCE_INFO_EXT_VERSION + struct virgl_renderer_resource_info_ext info_ext; + ret = virgl_renderer_resource_get_info_ext(resource_id, &info_ext); +- if (ret < 0) { ++ if (ret) { + return ret; + } + +@@ -335,7 +335,7 @@ virgl_get_resource_info_modifiers(uint32_t resource_id, + *modifiers = info_ext.modifiers; + #else + ret = virgl_renderer_resource_get_info(resource_id, info); +- if (ret < 0) { ++ if (ret) { + return ret; + } + +@@ -372,7 +372,7 @@ virgl_cmd_set_scanout(VuGpu *g, + uint64_t modifiers = 0; + ret = virgl_get_resource_info_modifiers(ss.resource_id, &info, + &modifiers); +- if (ret == -1) { ++ if (ret) { + g_critical("%s: illegal resource specified %d\n", + __func__, ss.resource_id); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; +diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c +index 8bb7a2c..9f34d0e 100644 +--- a/hw/display/virtio-gpu-virgl.c ++++ b/hw/display/virtio-gpu-virgl.c +@@ -181,7 +181,7 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, + memset(&info, 0, sizeof(info)); + ret = virgl_renderer_resource_get_info(ss.resource_id, &info); + #endif +- if (ret == -1) { ++ if (ret) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: illegal resource specified %d\n", + __func__, ss.resource_id); +-- +1.8.3.1 + diff --git a/0100-virtio_iommu-Clear-IOMMUPciBus-pointer-cache-when-sy.patch b/0100-virtio_iommu-Clear-IOMMUPciBus-pointer-cache-when-sy.patch new file mode 100644 index 0000000000000000000000000000000000000000..e10e8c553888b69f21e51b98f09af80e5abeccfa --- /dev/null +++ b/0100-virtio_iommu-Clear-IOMMUPciBus-pointer-cache-when-sy.patch @@ -0,0 +1,55 @@ +From 721c3ceaeffed4d9adf9bb0062431483792c57b7 Mon Sep 17 00:00:00 2001 +From: Zhenzhong Duan +Date: Thu, 25 Jan 2024 15:37:05 +0800 +Subject: [PATCH 107/293] virtio_iommu: Clear IOMMUPciBus pointer cache when + system reset + +s->iommu_pcibus_by_bus_num is a IOMMUPciBus pointer cache indexed +by bus number, bus number may not always be a fixed value, +i.e., guest reboot to different kernel which set bus number with +different algorithm. + +This could lead to endpoint binding to wrong iommu MR in +virtio_iommu_get_endpoint(), then vfio device setup wrong +mapping from other device. + +Remove the memset in virtio_iommu_device_realize() to avoid +redundancy with memset in system reset. + +Signed-off-by: Zhenzhong Duan +Message-Id: <20240125073706.339369-2-zhenzhong.duan@intel.com> +Reviewed-by: Eric Auger +Tested-by: Eric Auger +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 9a457383ce9d309d4679b079fafb51f0a2d949aa) +Signed-off-by: Michael Tokarev +--- + hw/virtio/virtio-iommu.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c +index 9d463ef..90a7ca2 100644 +--- a/hw/virtio/virtio-iommu.c ++++ b/hw/virtio/virtio-iommu.c +@@ -1264,6 +1264,8 @@ static void virtio_iommu_system_reset(void *opaque) + + trace_virtio_iommu_system_reset(); + ++ memset(s->iommu_pcibus_by_bus_num, 0, sizeof(s->iommu_pcibus_by_bus_num)); ++ + /* + * config.bypass is sticky across device reset, but should be restored on + * system reset +@@ -1302,8 +1304,6 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) + + virtio_init(vdev, VIRTIO_ID_IOMMU, sizeof(struct virtio_iommu_config)); + +- memset(s->iommu_pcibus_by_bus_num, 0, sizeof(s->iommu_pcibus_by_bus_num)); +- + 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); +-- +1.8.3.1 + diff --git a/0101-smmu-Clear-SMMUPciBus-pointer-cache-when-system-rese.patch b/0101-smmu-Clear-SMMUPciBus-pointer-cache-when-system-rese.patch new file mode 100644 index 0000000000000000000000000000000000000000..004f4c378c0868b9423901dd04058455ff6f9e9d --- /dev/null +++ b/0101-smmu-Clear-SMMUPciBus-pointer-cache-when-system-rese.patch @@ -0,0 +1,42 @@ +From d4157195bd3b8753dc422085623e415d0bab3bb9 Mon Sep 17 00:00:00 2001 +From: Zhenzhong Duan +Date: Thu, 25 Jan 2024 15:37:06 +0800 +Subject: [PATCH 108/293] smmu: Clear SMMUPciBus pointer cache when system + reset + +s->smmu_pcibus_by_bus_num is a SMMUPciBus pointer cache indexed +by bus number, bus number may not always be a fixed value, +i.e., guest reboot to different kernel which set bus number with +different algorithm. + +This could lead to smmu_iommu_mr() providing the wrong iommu MR. + +Suggested-by: Eric Auger +Signed-off-by: Zhenzhong Duan +Message-Id: <20240125073706.339369-3-zhenzhong.duan@intel.com> +Reviewed-by: Eric Auger +Tested-by: Eric Auger +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 8a6b3f4dc95a064e88adaca86374108da0ecb38d) +Signed-off-by: Michael Tokarev +--- + hw/arm/smmu-common.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c +index 9a8ac45..f58261b 100644 +--- a/hw/arm/smmu-common.c ++++ b/hw/arm/smmu-common.c +@@ -675,6 +675,8 @@ static void smmu_base_reset_hold(Object *obj) + { + SMMUState *s = ARM_SMMU(obj); + ++ memset(s->smmu_pcibus_by_bus_num, 0, sizeof(s->smmu_pcibus_by_bus_num)); ++ + g_hash_table_remove_all(s->configs); + g_hash_table_remove_all(s->iotlb); + } +-- +1.8.3.1 + diff --git a/0102-tests-acpi-Allow-update-of-DSDT.cxl.patch b/0102-tests-acpi-Allow-update-of-DSDT.cxl.patch new file mode 100644 index 0000000000000000000000000000000000000000..538fa88482ec25904ed60ef93c1bb6578e7e986b --- /dev/null +++ b/0102-tests-acpi-Allow-update-of-DSDT.cxl.patch @@ -0,0 +1,30 @@ +From 47df9ca585cbc1c14f197af1a05a236a3fad9920 Mon Sep 17 00:00:00 2001 +From: Jonathan Cameron +Date: Fri, 26 Jan 2024 12:01:30 +0000 +Subject: [PATCH 109/293] tests/acpi: Allow update of DSDT.cxl + +The _STA value returned currently indicates the ACPI0017 device +is not enabled. Whilst this isn't a real device, setting _STA +like this may prevent an OS from enumerating it correctly and +hence from parsing the CEDT table. + +Signed-off-by: Jonathan Cameron +Message-Id: <20240126120132.24248-11-Jonathan.Cameron@huawei.com> +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit 14ec4ff3e4293635240ba5a7afe7a0f3ba447d31) +Signed-off-by: Michael Tokarev +--- + tests/qtest/bios-tables-test-allowed-diff.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h +index dfb8523..9ce0f59 100644 +--- a/tests/qtest/bios-tables-test-allowed-diff.h ++++ b/tests/qtest/bios-tables-test-allowed-diff.h +@@ -1 +1,2 @@ + /* List of comma-separated changed AML files to ignore */ ++"tests/data/acpi/q35/DSDT.cxl", +-- +1.8.3.1 + diff --git a/0103-hw-i386-Fix-_STA-return-value-for-ACPI0017.patch b/0103-hw-i386-Fix-_STA-return-value-for-ACPI0017.patch new file mode 100644 index 0000000000000000000000000000000000000000..77866597c48241d53977d8e98ea47961e4930e20 --- /dev/null +++ b/0103-hw-i386-Fix-_STA-return-value-for-ACPI0017.patch @@ -0,0 +1,46 @@ +From 02d9979ba8f0bc504be36860969c6c6921816841 Mon Sep 17 00:00:00 2001 +From: Jonathan Cameron +Date: Fri, 26 Jan 2024 12:01:31 +0000 +Subject: [PATCH 110/293] hw/i386: Fix _STA return value for ACPI0017 + +Found whilst testing a series for the linux kernel that actually +bothers to check if enabled is set. 0xB is the option used +for vast majority of DSDT entries in QEMU. +It is a little odd for a device that doesn't really exist and +is simply a hook to tell the OS there is a CEDT table but 0xB +seems a reasonable choice and avoids need to special case +this device in the OS. + +Means: +* Device present. +* Device enabled and decoding it's resources. +* Not shown in UI +* Functioning properly +* No battery (on this device!) + +Signed-off-by: Jonathan Cameron +Message-Id: <20240126120132.24248-12-Jonathan.Cameron@huawei.com> +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +(cherry picked from commit d9ae5802f656f6fb53b788747ba557a826b6e740) +Signed-off-by: Michael Tokarev +--- + hw/i386/acpi-build.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c +index 80db183..1e17834 100644 +--- a/hw/i386/acpi-build.c ++++ b/hw/i386/acpi-build.c +@@ -1415,7 +1415,7 @@ static void build_acpi0017(Aml *table) + aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0017"))); + + method = aml_method("_STA", 0, AML_NOTSERIALIZED); +- aml_append(method, aml_return(aml_int(0x01))); ++ aml_append(method, aml_return(aml_int(0x0B))); + aml_append(dev, method); + build_cxl_dsm_method(dev); + +-- +1.8.3.1 + diff --git a/0104-linux-user-aarch64-Choose-SYNC-as-the-preferred-MTE-.patch b/0104-linux-user-aarch64-Choose-SYNC-as-the-preferred-MTE-.patch new file mode 100644 index 0000000000000000000000000000000000000000..351cdbb5903f9cf67c1b40d2d8311a96b481beb0 --- /dev/null +++ b/0104-linux-user-aarch64-Choose-SYNC-as-the-preferred-MTE-.patch @@ -0,0 +1,68 @@ +From 7950913ece574bc12089cca421e1a51d891b963e Mon Sep 17 00:00:00 2001 +From: Richard Henderson +Date: Thu, 15 Feb 2024 11:30:44 +0000 +Subject: [PATCH 112/293] linux-user/aarch64: Choose SYNC as the preferred MTE + mode + +The API does not generate an error for setting ASYNC | SYNC; that merely +constrains the selection vs the per-cpu default. For qemu linux-user, +choose SYNC as the default. + +Cc: qemu-stable@nongnu.org +Reported-by: Gustavo Romero +Signed-off-by: Richard Henderson +Tested-by: Gustavo Romero +Message-id: 20240207025210.8837-2-richard.henderson@linaro.org +Signed-off-by: Peter Maydell +(cherry picked from commit 681dfc0d552963d4d598350d26097a692900b408) +Signed-off-by: Michael Tokarev +--- + linux-user/aarch64/target_prctl.h | 29 +++++++++++++++++------------ + 1 file changed, 17 insertions(+), 12 deletions(-) + +diff --git a/linux-user/aarch64/target_prctl.h b/linux-user/aarch64/target_prctl.h +index 5067e7d..aa8e203 100644 +--- a/linux-user/aarch64/target_prctl.h ++++ b/linux-user/aarch64/target_prctl.h +@@ -173,21 +173,26 @@ static abi_long do_prctl_set_tagged_addr_ctrl(CPUArchState *env, abi_long arg2) + env->tagged_addr_enable = arg2 & PR_TAGGED_ADDR_ENABLE; + + if (cpu_isar_feature(aa64_mte, cpu)) { +- switch (arg2 & PR_MTE_TCF_MASK) { +- case PR_MTE_TCF_NONE: +- case PR_MTE_TCF_SYNC: +- case PR_MTE_TCF_ASYNC: +- break; +- default: +- return -EINVAL; +- } +- + /* + * Write PR_MTE_TCF to SCTLR_EL1[TCF0]. +- * Note that the syscall values are consistent with hw. ++ * ++ * The kernel has a per-cpu configuration for the sysadmin, ++ * /sys/devices/system/cpu/cpu/mte_tcf_preferred, ++ * which qemu does not implement. ++ * ++ * Because there is no performance difference between the modes, and ++ * because SYNC is most useful for debugging MTE errors, choose SYNC ++ * as the preferred mode. With this preference, and the way the API ++ * uses only two bits, there is no way for the program to select ++ * ASYMM mode. + */ +- env->cp15.sctlr_el[1] = +- deposit64(env->cp15.sctlr_el[1], 38, 2, arg2 >> PR_MTE_TCF_SHIFT); ++ unsigned tcf = 0; ++ if (arg2 & PR_MTE_TCF_SYNC) { ++ tcf = 1; ++ } else if (arg2 & PR_MTE_TCF_ASYNC) { ++ tcf = 2; ++ } ++ env->cp15.sctlr_el[1] = deposit64(env->cp15.sctlr_el[1], 38, 2, tcf); + + /* + * Write PR_MTE_TAG to GCR_EL1[Exclude]. +-- +1.8.3.1 + diff --git a/0105-target-arm-Fix-nregs-computation-in-do_-ld-st-_zpa.patch b/0105-target-arm-Fix-nregs-computation-in-do_-ld-st-_zpa.patch new file mode 100644 index 0000000000000000000000000000000000000000..324d8ade746449152903ef966ee2eeda256c3cb2 --- /dev/null +++ b/0105-target-arm-Fix-nregs-computation-in-do_-ld-st-_zpa.patch @@ -0,0 +1,82 @@ +From 5e6e09baa516fd7c768cc1050a6ed638574e8047 Mon Sep 17 00:00:00 2001 +From: Richard Henderson +Date: Thu, 15 Feb 2024 11:30:44 +0000 +Subject: [PATCH 113/293] target/arm: Fix nregs computation in do_{ld,st}_zpa + +The field is encoded as [0-3], which is convenient for +indexing our array of function pointers, but the true +value is [1-4]. Adjust before calling do_mem_zpa. + +Add an assert, and move the comment re passing ZT to +the helper back next to the relevant code. + +Cc: qemu-stable@nongnu.org +Fixes: 206adacfb8d ("target/arm: Add mte helpers for sve scalar + int loads") +Signed-off-by: Richard Henderson +Tested-by: Gustavo Romero +Message-id: 20240207025210.8837-3-richard.henderson@linaro.org +Reviewed-by: Peter Maydell +Signed-off-by: Peter Maydell +(cherry picked from commit 64c6e7444dff64b42d11b836b9aec9acfbe8ecc2) +Signed-off-by: Michael Tokarev +--- + target/arm/tcg/translate-sve.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c +index 296e7d1..7108938 100644 +--- a/target/arm/tcg/translate-sve.c ++++ b/target/arm/tcg/translate-sve.c +@@ -4445,11 +4445,7 @@ static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, + TCGv_ptr t_pg; + int desc = 0; + +- /* +- * For e.g. LD4, there are not enough arguments to pass all 4 +- * registers as pointers, so encode the regno into the data field. +- * For consistency, do this even for LD1. +- */ ++ assert(mte_n >= 1 && mte_n <= 4); + if (s->mte_active[0]) { + int msz = dtype_msz(dtype); + +@@ -4463,6 +4459,11 @@ static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, + addr = clean_data_tbi(s, addr); + } + ++ /* ++ * For e.g. LD4, there are not enough arguments to pass all 4 ++ * registers as pointers, so encode the regno into the data field. ++ * For consistency, do this even for LD1. ++ */ + desc = simd_desc(vsz, vsz, zt | desc); + t_pg = tcg_temp_new_ptr(); + +@@ -4600,7 +4601,7 @@ static void do_ld_zpa(DisasContext *s, int zt, int pg, + * accessible via the instruction encoding. + */ + assert(fn != NULL); +- do_mem_zpa(s, zt, pg, addr, dtype, nreg, false, fn); ++ do_mem_zpa(s, zt, pg, addr, dtype, nreg + 1, false, fn); + } + + static bool trans_LD_zprr(DisasContext *s, arg_rprr_load *a) +@@ -5168,14 +5169,13 @@ static void do_st_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, + if (nreg == 0) { + /* ST1 */ + fn = fn_single[s->mte_active[0]][be][msz][esz]; +- nreg = 1; + } else { + /* ST2, ST3, ST4 -- msz == esz, enforced by encoding */ + assert(msz == esz); + fn = fn_multiple[s->mte_active[0]][be][nreg - 1][msz]; + } + assert(fn != NULL); +- do_mem_zpa(s, zt, pg, addr, msz_dtype(s, msz), nreg, true, fn); ++ do_mem_zpa(s, zt, pg, addr, msz_dtype(s, msz), nreg + 1, true, fn); + } + + static bool trans_ST_zprr(DisasContext *s, arg_rprr_store *a) +-- +1.8.3.1 + diff --git a/0106-target-arm-Adjust-and-validate-mtedesc-sizem1.patch b/0106-target-arm-Adjust-and-validate-mtedesc-sizem1.patch new file mode 100644 index 0000000000000000000000000000000000000000..0b03bcbca9fa93c82f50fdcf40e4cc24c7d070ea --- /dev/null +++ b/0106-target-arm-Adjust-and-validate-mtedesc-sizem1.patch @@ -0,0 +1,65 @@ +From 8da74af97029dc96205ed4ddd53230d6d99c15c3 Mon Sep 17 00:00:00 2001 +From: Richard Henderson +Date: Thu, 15 Feb 2024 11:30:44 +0000 +Subject: [PATCH 114/293] target/arm: Adjust and validate mtedesc sizem1 + +When we added SVE_MTEDESC_SHIFT, we effectively limited the +maximum size of MTEDESC. Adjust SIZEM1 to consume the remaining +bits (32 - 10 - 5 - 12 == 5). Assert that the data to be stored +fits within the field (expecting 8 * 4 - 1 == 31, exact fit). + +Cc: qemu-stable@nongnu.org +Reviewed-by: Peter Maydell +Signed-off-by: Richard Henderson +Tested-by: Gustavo Romero +Message-id: 20240207025210.8837-4-richard.henderson@linaro.org +Signed-off-by: Peter Maydell +(cherry picked from commit b12a7671b6099a26ce5d5ab09701f151e21c112c) +Signed-off-by: Michael Tokarev +--- + target/arm/internals.h | 2 +- + target/arm/tcg/translate-sve.c | 7 ++++--- + 2 files changed, 5 insertions(+), 4 deletions(-) + +diff --git a/target/arm/internals.h b/target/arm/internals.h +index 143d57c..8342f46 100644 +--- a/target/arm/internals.h ++++ b/target/arm/internals.h +@@ -1273,7 +1273,7 @@ FIELD(MTEDESC, TBI, 4, 2) + FIELD(MTEDESC, TCMA, 6, 2) + FIELD(MTEDESC, WRITE, 8, 1) + FIELD(MTEDESC, ALIGN, 9, 3) +-FIELD(MTEDESC, SIZEM1, 12, SIMD_DATA_BITS - 12) /* size - 1 */ ++FIELD(MTEDESC, SIZEM1, 12, SIMD_DATA_BITS - SVE_MTEDESC_SHIFT - 12) /* size - 1 */ + + bool mte_probe(CPUARMState *env, uint32_t desc, uint64_t ptr); + uint64_t mte_check(CPUARMState *env, uint32_t desc, uint64_t ptr, uintptr_t ra); +diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c +index 7108938..a88e523 100644 +--- a/target/arm/tcg/translate-sve.c ++++ b/target/arm/tcg/translate-sve.c +@@ -4443,17 +4443,18 @@ static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, + { + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr t_pg; ++ uint32_t sizem1; + int desc = 0; + + assert(mte_n >= 1 && mte_n <= 4); ++ sizem1 = (mte_n << dtype_msz(dtype)) - 1; ++ assert(sizem1 <= R_MTEDESC_SIZEM1_MASK >> R_MTEDESC_SIZEM1_SHIFT); + if (s->mte_active[0]) { +- int msz = dtype_msz(dtype); +- + desc = FIELD_DP32(desc, MTEDESC, MIDX, get_mem_index(s)); + desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); + desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); + desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write); +- desc = FIELD_DP32(desc, MTEDESC, SIZEM1, (mte_n << msz) - 1); ++ desc = FIELD_DP32(desc, MTEDESC, SIZEM1, sizem1); + desc <<= SVE_MTEDESC_SHIFT; + } else { + addr = clean_data_tbi(s, addr); +-- +1.8.3.1 + diff --git a/0107-target-arm-Split-out-make_svemte_desc.patch b/0107-target-arm-Split-out-make_svemte_desc.patch new file mode 100644 index 0000000000000000000000000000000000000000..3d85426464ac4af19526e8148e02651105a80d83 --- /dev/null +++ b/0107-target-arm-Split-out-make_svemte_desc.patch @@ -0,0 +1,162 @@ +From da804717a549118ef4fc1351540737444ffbb5b0 Mon Sep 17 00:00:00 2001 +From: Richard Henderson +Date: Thu, 15 Feb 2024 11:30:45 +0000 +Subject: [PATCH 115/293] target/arm: Split out make_svemte_desc + +Share code that creates mtedesc and embeds within simd_desc. + +Cc: qemu-stable@nongnu.org +Reviewed-by: Peter Maydell +Signed-off-by: Richard Henderson +Tested-by: Gustavo Romero +Message-id: 20240207025210.8837-5-richard.henderson@linaro.org +Signed-off-by: Peter Maydell +(cherry picked from commit 96fcc9982b4aad7aced7fbff046048bbccc6cb0c) +Signed-off-by: Michael Tokarev +--- + target/arm/tcg/translate-a64.h | 2 ++ + target/arm/tcg/translate-sme.c | 15 ++++---------- + target/arm/tcg/translate-sve.c | 47 ++++++++++++++++++++++-------------------- + 3 files changed, 31 insertions(+), 33 deletions(-) + +diff --git a/target/arm/tcg/translate-a64.h b/target/arm/tcg/translate-a64.h +index 96ba39b..7b811b8 100644 +--- a/target/arm/tcg/translate-a64.h ++++ b/target/arm/tcg/translate-a64.h +@@ -28,6 +28,8 @@ bool logic_imm_decode_wmask(uint64_t *result, unsigned int immn, + bool sve_access_check(DisasContext *s); + bool sme_enabled_check(DisasContext *s); + bool sme_enabled_check_with_svcr(DisasContext *s, unsigned); ++uint32_t make_svemte_desc(DisasContext *s, unsigned vsz, uint32_t nregs, ++ uint32_t msz, bool is_write, uint32_t data); + + /* This function corresponds to CheckStreamingSVEEnabled. */ + static inline bool sme_sm_enabled_check(DisasContext *s) +diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c +index 8f0dfc8..46c7fce 100644 +--- a/target/arm/tcg/translate-sme.c ++++ b/target/arm/tcg/translate-sme.c +@@ -206,7 +206,7 @@ static bool trans_LDST1(DisasContext *s, arg_LDST1 *a) + + TCGv_ptr t_za, t_pg; + TCGv_i64 addr; +- int svl, desc = 0; ++ uint32_t desc; + bool be = s->be_data == MO_BE; + bool mte = s->mte_active[0]; + +@@ -224,18 +224,11 @@ static bool trans_LDST1(DisasContext *s, arg_LDST1 *a) + tcg_gen_shli_i64(addr, cpu_reg(s, a->rm), a->esz); + tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, a->rn)); + +- if (mte) { +- desc = FIELD_DP32(desc, MTEDESC, MIDX, get_mem_index(s)); +- desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); +- desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); +- desc = FIELD_DP32(desc, MTEDESC, WRITE, a->st); +- desc = FIELD_DP32(desc, MTEDESC, SIZEM1, (1 << a->esz) - 1); +- desc <<= SVE_MTEDESC_SHIFT; +- } else { ++ if (!mte) { + addr = clean_data_tbi(s, addr); + } +- svl = streaming_vec_reg_size(s); +- desc = simd_desc(svl, svl, desc); ++ ++ desc = make_svemte_desc(s, streaming_vec_reg_size(s), 1, a->esz, a->st, 0); + + fns[a->esz][be][a->v][mte][a->st](tcg_env, t_za, t_pg, addr, + tcg_constant_i32(desc)); +diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c +index a88e523..508f7b6 100644 +--- a/target/arm/tcg/translate-sve.c ++++ b/target/arm/tcg/translate-sve.c +@@ -4437,18 +4437,18 @@ static const uint8_t dtype_esz[16] = { + 3, 2, 1, 3 + }; + +-static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, +- int dtype, uint32_t mte_n, bool is_write, +- gen_helper_gvec_mem *fn) ++uint32_t make_svemte_desc(DisasContext *s, unsigned vsz, uint32_t nregs, ++ uint32_t msz, bool is_write, uint32_t data) + { +- unsigned vsz = vec_full_reg_size(s); +- TCGv_ptr t_pg; + uint32_t sizem1; +- int desc = 0; ++ uint32_t desc = 0; + +- assert(mte_n >= 1 && mte_n <= 4); +- sizem1 = (mte_n << dtype_msz(dtype)) - 1; ++ /* Assert all of the data fits, with or without MTE enabled. */ ++ assert(nregs >= 1 && nregs <= 4); ++ sizem1 = (nregs << msz) - 1; + assert(sizem1 <= R_MTEDESC_SIZEM1_MASK >> R_MTEDESC_SIZEM1_SHIFT); ++ assert(data < 1u << SVE_MTEDESC_SHIFT); ++ + if (s->mte_active[0]) { + desc = FIELD_DP32(desc, MTEDESC, MIDX, get_mem_index(s)); + desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); +@@ -4456,7 +4456,18 @@ static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, + desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write); + desc = FIELD_DP32(desc, MTEDESC, SIZEM1, sizem1); + desc <<= SVE_MTEDESC_SHIFT; +- } else { ++ } ++ return simd_desc(vsz, vsz, desc | data); ++} ++ ++static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, ++ int dtype, uint32_t nregs, bool is_write, ++ gen_helper_gvec_mem *fn) ++{ ++ TCGv_ptr t_pg; ++ uint32_t desc; ++ ++ if (!s->mte_active[0]) { + addr = clean_data_tbi(s, addr); + } + +@@ -4465,7 +4476,8 @@ static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, + * registers as pointers, so encode the regno into the data field. + * For consistency, do this even for LD1. + */ +- desc = simd_desc(vsz, vsz, zt | desc); ++ desc = make_svemte_desc(s, vec_full_reg_size(s), nregs, ++ dtype_msz(dtype), is_write, zt); + t_pg = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(t_pg, tcg_env, pred_full_reg_offset(s, pg)); +@@ -5224,25 +5236,16 @@ static void do_mem_zpz(DisasContext *s, int zt, int pg, int zm, + int scale, TCGv_i64 scalar, int msz, bool is_write, + gen_helper_gvec_mem_scatter *fn) + { +- unsigned vsz = vec_full_reg_size(s); + TCGv_ptr t_zm = tcg_temp_new_ptr(); + TCGv_ptr t_pg = tcg_temp_new_ptr(); + TCGv_ptr t_zt = tcg_temp_new_ptr(); +- int desc = 0; +- +- if (s->mte_active[0]) { +- desc = FIELD_DP32(desc, MTEDESC, MIDX, get_mem_index(s)); +- desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); +- desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); +- desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write); +- desc = FIELD_DP32(desc, MTEDESC, SIZEM1, (1 << msz) - 1); +- desc <<= SVE_MTEDESC_SHIFT; +- } +- desc = simd_desc(vsz, vsz, desc | scale); ++ uint32_t desc; + + tcg_gen_addi_ptr(t_pg, tcg_env, pred_full_reg_offset(s, pg)); + tcg_gen_addi_ptr(t_zm, tcg_env, vec_full_reg_offset(s, zm)); + tcg_gen_addi_ptr(t_zt, tcg_env, vec_full_reg_offset(s, zt)); ++ ++ desc = make_svemte_desc(s, vec_full_reg_size(s), 1, msz, is_write, scale); + fn(tcg_env, t_zt, t_pg, t_zm, scalar, tcg_constant_i32(desc)); + } + +-- +1.8.3.1 + diff --git a/0108-target-arm-Handle-mte-in-do_ldrq-do_ldro.patch b/0108-target-arm-Handle-mte-in-do_ldrq-do_ldro.patch new file mode 100644 index 0000000000000000000000000000000000000000..1d98a1441ab2259aad72479a93e131130e1d6901 --- /dev/null +++ b/0108-target-arm-Handle-mte-in-do_ldrq-do_ldro.patch @@ -0,0 +1,79 @@ +From 2d1a29e3b23d833c22ef01df9b6ceb7200275fd3 Mon Sep 17 00:00:00 2001 +From: Richard Henderson +Date: Thu, 15 Feb 2024 11:30:45 +0000 +Subject: [PATCH 116/293] target/arm: Handle mte in do_ldrq, do_ldro + +These functions "use the standard load helpers", but +fail to clean_data_tbi or populate mtedesc. + +Cc: qemu-stable@nongnu.org +Reviewed-by: Peter Maydell +Signed-off-by: Richard Henderson +Tested-by: Gustavo Romero +Message-id: 20240207025210.8837-6-richard.henderson@linaro.org +Signed-off-by: Peter Maydell +(cherry picked from commit 623507ccfcfebb0f10229ae5de3f85a27fb615a7) +Signed-off-by: Michael Tokarev +--- + target/arm/tcg/translate-sve.c | 15 +++++++++++++-- + 1 file changed, 13 insertions(+), 2 deletions(-) + +diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c +index 508f7b6..ada05aa 100644 +--- a/target/arm/tcg/translate-sve.c ++++ b/target/arm/tcg/translate-sve.c +@@ -4861,8 +4861,13 @@ static void do_ldrq(DisasContext *s, int zt, int pg, TCGv_i64 addr, int dtype) + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr t_pg; + int poff; ++ uint32_t desc; + + /* Load the first quadword using the normal predicated load helpers. */ ++ if (!s->mte_active[0]) { ++ addr = clean_data_tbi(s, addr); ++ } ++ + poff = pred_full_reg_offset(s, pg); + if (vsz > 16) { + /* +@@ -4886,7 +4891,8 @@ static void do_ldrq(DisasContext *s, int zt, int pg, TCGv_i64 addr, int dtype) + + gen_helper_gvec_mem *fn + = ldr_fns[s->mte_active[0]][s->be_data == MO_BE][dtype][0]; +- fn(tcg_env, t_pg, addr, tcg_constant_i32(simd_desc(16, 16, zt))); ++ desc = make_svemte_desc(s, 16, 1, dtype_msz(dtype), false, zt); ++ fn(tcg_env, t_pg, addr, tcg_constant_i32(desc)); + + /* Replicate that first quadword. */ + if (vsz > 16) { +@@ -4929,6 +4935,7 @@ static void do_ldro(DisasContext *s, int zt, int pg, TCGv_i64 addr, int dtype) + unsigned vsz_r32; + TCGv_ptr t_pg; + int poff, doff; ++ uint32_t desc; + + if (vsz < 32) { + /* +@@ -4941,6 +4948,9 @@ static void do_ldro(DisasContext *s, int zt, int pg, TCGv_i64 addr, int dtype) + } + + /* Load the first octaword using the normal predicated load helpers. */ ++ if (!s->mte_active[0]) { ++ addr = clean_data_tbi(s, addr); ++ } + + poff = pred_full_reg_offset(s, pg); + if (vsz > 32) { +@@ -4965,7 +4975,8 @@ static void do_ldro(DisasContext *s, int zt, int pg, TCGv_i64 addr, int dtype) + + gen_helper_gvec_mem *fn + = ldr_fns[s->mte_active[0]][s->be_data == MO_BE][dtype][0]; +- fn(tcg_env, t_pg, addr, tcg_constant_i32(simd_desc(32, 32, zt))); ++ desc = make_svemte_desc(s, 32, 1, dtype_msz(dtype), false, zt); ++ fn(tcg_env, t_pg, addr, tcg_constant_i32(desc)); + + /* + * Replicate that first octaword. +-- +1.8.3.1 + diff --git a/0109-target-arm-Fix-SVE-SME-gross-MTE-suppression-checks.patch b/0109-target-arm-Fix-SVE-SME-gross-MTE-suppression-checks.patch new file mode 100644 index 0000000000000000000000000000000000000000..884447c125f08b1f2b414a6ff542e57639895ebb --- /dev/null +++ b/0109-target-arm-Fix-SVE-SME-gross-MTE-suppression-checks.patch @@ -0,0 +1,86 @@ +From 429c11c7266b14f3ce0a052cd582095fdf085d56 Mon Sep 17 00:00:00 2001 +From: Richard Henderson +Date: Thu, 15 Feb 2024 11:30:45 +0000 +Subject: [PATCH 117/293] target/arm: Fix SVE/SME gross MTE suppression checks + +The TBI and TCMA bits are located within mtedesc, not desc. + +Cc: qemu-stable@nongnu.org +Reviewed-by: Peter Maydell +Signed-off-by: Richard Henderson +Tested-by: Gustavo Romero +Message-id: 20240207025210.8837-7-richard.henderson@linaro.org +Signed-off-by: Peter Maydell +(cherry picked from commit 855f94eca80c85a99f459e36684ea2f98f6a3243) +Signed-off-by: Michael Tokarev +--- + target/arm/tcg/sme_helper.c | 8 ++++---- + target/arm/tcg/sve_helper.c | 12 ++++++------ + 2 files changed, 10 insertions(+), 10 deletions(-) + +diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c +index 1ee2690..904bfda 100644 +--- a/target/arm/tcg/sme_helper.c ++++ b/target/arm/tcg/sme_helper.c +@@ -573,8 +573,8 @@ void sme_ld1_mte(CPUARMState *env, void *za, uint64_t *vg, + desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + + /* Perform gross MTE suppression early. */ +- if (!tbi_check(desc, bit55) || +- tcma_check(desc, bit55, allocation_tag_from_addr(addr))) { ++ if (!tbi_check(mtedesc, bit55) || ++ tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) { + mtedesc = 0; + } + +@@ -750,8 +750,8 @@ void sme_st1_mte(CPUARMState *env, void *za, uint64_t *vg, target_ulong addr, + desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + + /* Perform gross MTE suppression early. */ +- if (!tbi_check(desc, bit55) || +- tcma_check(desc, bit55, allocation_tag_from_addr(addr))) { ++ if (!tbi_check(mtedesc, bit55) || ++ tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) { + mtedesc = 0; + } + +diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c +index f006d15..5699dfe 100644 +--- a/target/arm/tcg/sve_helper.c ++++ b/target/arm/tcg/sve_helper.c +@@ -5800,8 +5800,8 @@ void sve_ldN_r_mte(CPUARMState *env, uint64_t *vg, target_ulong addr, + desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + + /* Perform gross MTE suppression early. */ +- if (!tbi_check(desc, bit55) || +- tcma_check(desc, bit55, allocation_tag_from_addr(addr))) { ++ if (!tbi_check(mtedesc, bit55) || ++ tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) { + mtedesc = 0; + } + +@@ -6156,8 +6156,8 @@ void sve_ldnfff1_r_mte(CPUARMState *env, void *vg, target_ulong addr, + desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + + /* Perform gross MTE suppression early. */ +- if (!tbi_check(desc, bit55) || +- tcma_check(desc, bit55, allocation_tag_from_addr(addr))) { ++ if (!tbi_check(mtedesc, bit55) || ++ tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) { + mtedesc = 0; + } + +@@ -6410,8 +6410,8 @@ void sve_stN_r_mte(CPUARMState *env, uint64_t *vg, target_ulong addr, + desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + + /* Perform gross MTE suppression early. */ +- if (!tbi_check(desc, bit55) || +- tcma_check(desc, bit55, allocation_tag_from_addr(addr))) { ++ if (!tbi_check(mtedesc, bit55) || ++ tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) { + mtedesc = 0; + } + +-- +1.8.3.1 + diff --git a/0110-target-arm-Don-t-get-MDCR_EL2-in-pmu_counter_enabled.patch b/0110-target-arm-Don-t-get-MDCR_EL2-in-pmu_counter_enabled.patch new file mode 100644 index 0000000000000000000000000000000000000000..7d6bf40a905de3715de8465a4f8691cbafcd50b2 --- /dev/null +++ b/0110-target-arm-Don-t-get-MDCR_EL2-in-pmu_counter_enabled.patch @@ -0,0 +1,71 @@ +From f030e96d27c50a13a5295927d90853680dbc1052 Mon Sep 17 00:00:00 2001 +From: Peter Maydell +Date: Thu, 8 Feb 2024 15:33:46 +0000 +Subject: [PATCH 118/293] target/arm: Don't get MDCR_EL2 in + pmu_counter_enabled() before checking ARM_FEATURE_PMU +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +It doesn't make sense to read the value of MDCR_EL2 on a non-A-profile +CPU, and in fact if you try to do it we will assert: + +#6 0x00007ffff4b95e96 in __GI___assert_fail + (assertion=0x5555565a8c70 "!arm_feature(env, ARM_FEATURE_M)", file=0x5555565a6e5c "../../target/arm/helper.c", line=12600, function=0x5555565a9560 <__PRETTY_FUNCTION__.0> "arm_security_space_below_el3") at ./assert/assert.c:101 +#7 0x0000555555ebf412 in arm_security_space_below_el3 (env=0x555557bc8190) at ../../target/arm/helper.c:12600 +#8 0x0000555555ea6f89 in arm_is_el2_enabled (env=0x555557bc8190) at ../../target/arm/cpu.h:2595 +#9 0x0000555555ea942f in arm_mdcr_el2_eff (env=0x555557bc8190) at ../../target/arm/internals.h:1512 + +We might call pmu_counter_enabled() on an M-profile CPU (for example +from the migration pre/post hooks in machine.c); this should always +return false because these CPUs don't set ARM_FEATURE_PMU. + +Avoid the assertion by not calling arm_mdcr_el2_eff() before we +have done the early return for "PMU not present". + +This fixes an assertion failure if you try to do a loadvm or +savevm for an M-profile board. + +Cc: qemu-stable@nongnu.org +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2155 +Signed-off-by: Peter Maydell +Reviewed-by: Philippe Mathieu-Daudé +Reviewed-by: Richard Henderson +Message-id: 20240208153346.970021-1-peter.maydell@linaro.org +(cherry picked from commit ac1d88e9e7ca0bed83e91e07ce6d0597f10cc77d) +Signed-off-by: Michael Tokarev +--- + target/arm/helper.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/target/arm/helper.c b/target/arm/helper.c +index 6515c5e..df1646d 100644 +--- a/target/arm/helper.c ++++ b/target/arm/helper.c +@@ -1169,13 +1169,21 @@ static bool pmu_counter_enabled(CPUARMState *env, uint8_t counter) + bool enabled, prohibited = false, filtered; + bool secure = arm_is_secure(env); + int el = arm_current_el(env); +- uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); +- uint8_t hpmn = mdcr_el2 & MDCR_HPMN; ++ uint64_t mdcr_el2; ++ uint8_t hpmn; + ++ /* ++ * We might be called for M-profile cores where MDCR_EL2 doesn't ++ * exist and arm_mdcr_el2_eff() will assert, so this early-exit check ++ * must be before we read that value. ++ */ + if (!arm_feature(env, ARM_FEATURE_PMU)) { + return false; + } + ++ mdcr_el2 = arm_mdcr_el2_eff(env); ++ hpmn = mdcr_el2 & MDCR_HPMN; ++ + if (!arm_feature(env, ARM_FEATURE_EL2) || + (counter < hpmn || counter == 31)) { + e = env->cp15.c9_pmcr & PMCRE; +-- +1.8.3.1 + diff --git a/0111-iotests-Make-144-deterministic-again.patch b/0111-iotests-Make-144-deterministic-again.patch new file mode 100644 index 0000000000000000000000000000000000000000..0b6298d466987f10ad63bfc39efde1f905644107 --- /dev/null +++ b/0111-iotests-Make-144-deterministic-again.patch @@ -0,0 +1,75 @@ +From d5bc76fa20a8887d582710054bb3b06dd80c2aca Mon Sep 17 00:00:00 2001 +From: Kevin Wolf +Date: Fri, 9 Feb 2024 18:31:03 +0100 +Subject: [PATCH 119/293] iotests: Make 144 deterministic again + +Since commit effd60c8 changed how QMP commands are processed, the order +of the block-commit return value and job events in iotests 144 wasn't +fixed and more and caused the test to fail intermittently. + +Change the test to cache events first and then print them in a +predefined order. + +Waiting three times for JOB_STATUS_CHANGE is a bit uglier than just +waiting for the JOB_STATUS_CHANGE that has "status": "ready", but the +tooling we have doesn't seem to allow the latter easily. + +Fixes: effd60c878176bcaf97fa7ce2b12d04bb8ead6f7 +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2126 +Signed-off-by: Kevin Wolf +Reviewed-by: Stefan Hajnoczi +Message-id: 20240209173103.239994-1-kwolf@redhat.com +Signed-off-by: Peter Maydell +(cherry picked from commit cc29c12ec629ba68a4a6cb7d165c94cc8502815a) +Signed-off-by: Michael Tokarev +--- + tests/qemu-iotests/144 | 12 +++++++++++- + tests/qemu-iotests/144.out | 2 +- + 2 files changed, 12 insertions(+), 2 deletions(-) + +diff --git a/tests/qemu-iotests/144 b/tests/qemu-iotests/144 +index bdcc498..d284a0e 100755 +--- a/tests/qemu-iotests/144 ++++ b/tests/qemu-iotests/144 +@@ -83,12 +83,22 @@ echo + echo === Performing block-commit on active layer === + echo + ++capture_events="BLOCK_JOB_READY JOB_STATUS_CHANGE" ++ + # Block commit on active layer, push the new overlay into base + _send_qemu_cmd $h "{ 'execute': 'block-commit', + 'arguments': { + 'device': 'virtio0' + } +- }" "READY" ++ }" "return" ++ ++_wait_event $h "JOB_STATUS_CHANGE" ++_wait_event $h "JOB_STATUS_CHANGE" ++_wait_event $h "JOB_STATUS_CHANGE" ++ ++_wait_event $h "BLOCK_JOB_READY" ++ ++capture_events= + + _send_qemu_cmd $h "{ 'execute': 'block-job-complete', + 'arguments': { +diff --git a/tests/qemu-iotests/144.out b/tests/qemu-iotests/144.out +index b3b4812..2245ddf 100644 +--- a/tests/qemu-iotests/144.out ++++ b/tests/qemu-iotests/144.out +@@ -25,9 +25,9 @@ Formatting 'TEST_DIR/tmp.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off co + 'device': 'virtio0' + } + } ++{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "virtio0"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "virtio0"}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "virtio0"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "virtio0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} + { 'execute': 'block-job-complete', +-- +1.8.3.1 + diff --git a/0112-.gitlab-ci-windows.yml-Don-t-install-libusb-or-spice.patch b/0112-.gitlab-ci-windows.yml-Don-t-install-libusb-or-spice.patch new file mode 100644 index 0000000000000000000000000000000000000000..d23b30feda26a0608333e598ab6ea814d5b4986b --- /dev/null +++ b/0112-.gitlab-ci-windows.yml-Don-t-install-libusb-or-spice.patch @@ -0,0 +1,82 @@ +From 4d9dc117eae8a5d5464635a436349c71e2f8ce8d Mon Sep 17 00:00:00 2001 +From: Peter Maydell +Date: Thu, 15 Feb 2024 15:50:09 +0000 +Subject: [PATCH 120/293] .gitlab-ci/windows.yml: Don't install libusb or spice + packages on 32-bit +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When msys2 updated their libusb packages to libusb 1.0.27, they +dropped support for building them for mingw32, leaving only mingw64 +packages. This broke our CI job, as the 'pacman' package install now +fails with: + +error: target not found: mingw-w64-i686-libusb +error: target not found: mingw-w64-i686-usbredir + +(both these binary packages are from the libusb source package). + +Similarly, spice is now 64-bit only: +error: target not found: mingw-w64-i686-spice + +Fix this by dropping these packages from the list we install for our +msys2-32bit build. We do this with a simple mechanism for the +msys2-64bit and msys2-32bit jobs to specify a list of extra packages +to install on top of the common ones we install for both jobs. + +Cc: qemu-stable@nongnu.org +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2160 +Signed-off-by: Peter Maydell +Reviewed-by: Philippe Mathieu-Daudé +Reviewed-by: Michael Tokarev +Message-id: 20240215155009.2422335-1-peter.maydell@linaro.org +(cherry picked from commit 8e31b744fdf2c5d933681e4128acee72a83af4b8) +Signed-off-by: Michael Tokarev +--- + .gitlab-ci.d/windows.yml | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/.gitlab-ci.d/windows.yml b/.gitlab-ci.d/windows.yml +index f7645f7..5c1e385 100644 +--- a/.gitlab-ci.d/windows.yml ++++ b/.gitlab-ci.d/windows.yml +@@ -88,7 +88,6 @@ + $MINGW_TARGET-libpng + $MINGW_TARGET-libssh + $MINGW_TARGET-libtasn1 +- $MINGW_TARGET-libusb + $MINGW_TARGET-lzo2 + $MINGW_TARGET-nettle + $MINGW_TARGET-ninja +@@ -98,9 +97,8 @@ + $MINGW_TARGET-SDL2 + $MINGW_TARGET-SDL2_image + $MINGW_TARGET-snappy +- $MINGW_TARGET-spice +- $MINGW_TARGET-usbredir +- $MINGW_TARGET-zstd " ++ $MINGW_TARGET-zstd ++ $EXTRA_PACKAGES " + - Write-Output "Running build at $(Get-Date -Format u)" + - $env:CHERE_INVOKING = 'yes' # Preserve the current working directory + - $env:MSYS = 'winsymlinks:native' # Enable native Windows symlink +@@ -123,6 +121,8 @@ msys2-64bit: + variables: + MINGW_TARGET: mingw-w64-x86_64 + MSYSTEM: MINGW64 ++ # msys2 only ship these packages for 64-bit, not 32-bit ++ EXTRA_PACKAGES: $MINGW_TARGET-libusb $MINGW_TARGET-usbredir $MINGW_TARGET-spice + # do not remove "--without-default-devices"! + # commit 9f8e6cad65a6 ("gitlab-ci: Speed up the msys2-64bit job by using --without-default-devices" + # changed to compile QEMU with the --without-default-devices switch +@@ -137,5 +137,6 @@ msys2-32bit: + variables: + MINGW_TARGET: mingw-w64-i686 + MSYSTEM: MINGW32 ++ EXTRA_PACKAGES: + CONFIGURE_ARGS: --target-list=ppc64-softmmu -Ddebug=false -Doptimization=0 + TEST_ARGS: --no-suite qtest +-- +1.8.3.1 + diff --git a/0113-i386-cpu-Clear-FEAT_XSAVE_XSS_LO-HI-leafs-when-CPUID.patch b/0113-i386-cpu-Clear-FEAT_XSAVE_XSS_LO-HI-leafs-when-CPUID.patch new file mode 100644 index 0000000000000000000000000000000000000000..1385926ca05442aef2e59b21267ae6cfd3d86c03 --- /dev/null +++ b/0113-i386-cpu-Clear-FEAT_XSAVE_XSS_LO-HI-leafs-when-CPUID.patch @@ -0,0 +1,37 @@ +From 0766f137f50068e73fdca2a23b6f7b1de23d83ef Mon Sep 17 00:00:00 2001 +From: Xiaoyao Li +Date: Mon, 15 Jan 2024 04:13:24 -0500 +Subject: [PATCH 121/293] i386/cpu: Clear FEAT_XSAVE_XSS_LO/HI leafs when + CPUID_EXT_XSAVE is not available + +Leaf FEAT_XSAVE_XSS_LO and FEAT_XSAVE_XSS_HI also need to be cleared +when CPUID_EXT_XSAVE is not set. + +Fixes: 301e90675c3f ("target/i386: Enable support for XSAVES based features") +Signed-off-by: Xiaoyao Li +Reviewed-by: Yang Weijiang +Message-ID: <20240115091325.1904229-2-xiaoyao.li@intel.com> +Cc: qemu-stable@nongnu.org +Signed-off-by: Paolo Bonzini +(cherry picked from commit 81f5cad3858f27623b1b14467926032d229b76cc) +Signed-off-by: Michael Tokarev +--- + target/i386/cpu.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index cd16cb8..8b9ef21 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -6927,6 +6927,8 @@ static void x86_cpu_enable_xsave_components(X86CPU *cpu) + if (!(env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE)) { + env->features[FEAT_XSAVE_XCR0_LO] = 0; + env->features[FEAT_XSAVE_XCR0_HI] = 0; ++ env->features[FEAT_XSAVE_XSS_LO] = 0; ++ env->features[FEAT_XSAVE_XSS_HI] = 0; + return; + } + +-- +1.8.3.1 + diff --git a/0114-i386-cpu-Mask-with-XCR0-XSS-mask-for-FEAT_XSAVE_XCR0.patch b/0114-i386-cpu-Mask-with-XCR0-XSS-mask-for-FEAT_XSAVE_XCR0.patch new file mode 100644 index 0000000000000000000000000000000000000000..365ebd777c71d621679b74dad05458c2a7ca091e --- /dev/null +++ b/0114-i386-cpu-Mask-with-XCR0-XSS-mask-for-FEAT_XSAVE_XCR0.patch @@ -0,0 +1,41 @@ +From 72c4ef9da08c4e8c692fe429cfd78ec934cdfb84 Mon Sep 17 00:00:00 2001 +From: Xiaoyao Li +Date: Mon, 15 Jan 2024 04:13:25 -0500 +Subject: [PATCH 122/293] i386/cpu: Mask with XCR0/XSS mask for + FEAT_XSAVE_XCR0_HI and FEAT_XSAVE_XSS_HI leafs + +The value of FEAT_XSAVE_XCR0_HI leaf and FEAT_XSAVE_XSS_HI leaf also +need to be masked by XCR0 and XSS mask respectively, to make it +logically correct. + +Fixes: 301e90675c3f ("target/i386: Enable support for XSAVES based features") +Signed-off-by: Xiaoyao Li +Reviewed-by: Yang Weijiang +Message-ID: <20240115091325.1904229-3-xiaoyao.li@intel.com> +Cc: qemu-stable@nongnu.org +Signed-off-by: Paolo Bonzini +(cherry picked from commit a11a365159b944e05be76f3ec3b98c8b38cb70fd) +Signed-off-by: Michael Tokarev +--- + target/i386/cpu.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 8b9ef21..a66e5a3 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -6947,9 +6947,9 @@ static void x86_cpu_enable_xsave_components(X86CPU *cpu) + } + + env->features[FEAT_XSAVE_XCR0_LO] = mask & CPUID_XSTATE_XCR0_MASK; +- env->features[FEAT_XSAVE_XCR0_HI] = mask >> 32; ++ env->features[FEAT_XSAVE_XCR0_HI] = (mask & CPUID_XSTATE_XCR0_MASK) >> 32; + env->features[FEAT_XSAVE_XSS_LO] = mask & CPUID_XSTATE_XSS_MASK; +- env->features[FEAT_XSAVE_XSS_HI] = mask >> 32; ++ env->features[FEAT_XSAVE_XSS_HI] = (mask & CPUID_XSTATE_XSS_MASK) >> 32; + } + + /***** Steps involved on loading and filtering CPUID data +-- +1.8.3.1 + diff --git a/0115-i386-cpuid-Decrease-cpuid_i-when-skipping-CPUID-leaf.patch b/0115-i386-cpuid-Decrease-cpuid_i-when-skipping-CPUID-leaf.patch new file mode 100644 index 0000000000000000000000000000000000000000..28c29cbf95bb8d8f5a8b14d2f5e797b549510efc --- /dev/null +++ b/0115-i386-cpuid-Decrease-cpuid_i-when-skipping-CPUID-leaf.patch @@ -0,0 +1,38 @@ +From e8d27721cbb267fde6b7ce7e0984ec07df21d7e7 Mon Sep 17 00:00:00 2001 +From: Xiaoyao Li +Date: Wed, 24 Jan 2024 21:40:14 -0500 +Subject: [PATCH 123/293] i386/cpuid: Decrease cpuid_i when skipping CPUID leaf + 1F + +Existing code misses a decrement of cpuid_i when skip leaf 0x1F. +There's a blank CPUID entry(with leaf, subleaf as 0, and all fields +stuffed 0s) left in the CPUID array. + +It conflicts with correct CPUID leaf 0. + +Signed-off-by: Xiaoyao Li +Reviewed-by:Yang Weijiang +Message-ID: <20240125024016.2521244-2-xiaoyao.li@intel.com> +Cc: qemu-stable@nongnu.org +Signed-off-by: Paolo Bonzini +(cherry picked from commit 10f92799af8ba3c3cef2352adcd4780f13fbab31) +Signed-off-by: Michael Tokarev +--- + target/i386/kvm/kvm.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c +index 4ce8055..e68eb8f 100644 +--- a/target/i386/kvm/kvm.c ++++ b/target/i386/kvm/kvm.c +@@ -1914,6 +1914,7 @@ int kvm_arch_init_vcpu(CPUState *cs) + } + case 0x1f: + if (env->nr_dies < 2) { ++ cpuid_i--; + break; + } + /* fallthrough */ +-- +1.8.3.1 + diff --git a/0116-i386-cpuid-Move-leaf-7-to-correct-group.patch b/0116-i386-cpuid-Move-leaf-7-to-correct-group.patch new file mode 100644 index 0000000000000000000000000000000000000000..fe4047189e1f4fe01d095fc51f8ac119f3b84fe1 --- /dev/null +++ b/0116-i386-cpuid-Move-leaf-7-to-correct-group.patch @@ -0,0 +1,49 @@ +From f5dddb856cccdc491547235aeafb451acd63c8b1 Mon Sep 17 00:00:00 2001 +From: Xiaoyao Li +Date: Wed, 24 Jan 2024 21:40:16 -0500 +Subject: [PATCH 124/293] i386/cpuid: Move leaf 7 to correct group + +CPUID leaf 7 was grouped together with SGX leaf 0x12 by commit +b9edbadefb9e ("i386: Propagate SGX CPUID sub-leafs to KVM") by mistake. + +SGX leaf 0x12 has its specific logic to check if subleaf (starting from 2) +is valid or not by checking the bit 0:3 of corresponding EAX is 1 or +not. + +Leaf 7 follows the logic that EAX of subleaf 0 enumerates the maximum +valid subleaf. + +Fixes: b9edbadefb9e ("i386: Propagate SGX CPUID sub-leafs to KVM") +Signed-off-by: Xiaoyao Li +Message-ID: <20240125024016.2521244-4-xiaoyao.li@intel.com> +Cc: qemu-stable@nongnu.org +Signed-off-by: Paolo Bonzini +(cherry picked from commit 0729857c707535847d7fe31d3d91eb8b2a118e3c) +Signed-off-by: Michael Tokarev +--- + target/i386/kvm/kvm.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c +index e68eb8f..a0bc9ea 100644 +--- a/target/i386/kvm/kvm.c ++++ b/target/i386/kvm/kvm.c +@@ -1955,7 +1955,6 @@ int kvm_arch_init_vcpu(CPUState *cs) + c = &cpuid_data.entries[cpuid_i++]; + } + break; +- case 0x7: + case 0x12: + for (j = 0; ; j++) { + c->function = i; +@@ -1975,6 +1974,7 @@ int kvm_arch_init_vcpu(CPUState *cs) + c = &cpuid_data.entries[cpuid_i++]; + } + break; ++ case 0x7: + case 0x14: + case 0x1d: + case 0x1e: { +-- +1.8.3.1 + diff --git a/0117-target-i386-Generate-an-illegal-opcode-exception-on-.patch b/0117-target-i386-Generate-an-illegal-opcode-exception-on-.patch new file mode 100644 index 0000000000000000000000000000000000000000..9bc5c17d4b9582c9f902d2776a3c3faf200b71b6 --- /dev/null +++ b/0117-target-i386-Generate-an-illegal-opcode-exception-on-.patch @@ -0,0 +1,46 @@ +From 0b30735d3807640587c2bc9b19ab274e0f8eef57 Mon Sep 17 00:00:00 2001 +From: Ziqiao Kong +Date: Thu, 15 Feb 2024 17:50:17 +0800 +Subject: [PATCH 125/293] target/i386: Generate an illegal opcode exception on + cmp instructions with lock prefix + +target/i386: As specified by Intel Manual Vol2 3-180, cmp instructions +are not allowed to have lock prefix and a `UD` should be raised. Without +this patch, s1->T0 will be uninitialized and used in the case OP_CMPL. + +Signed-off-by: Ziqiao Kong +Message-ID: <20240215095015.570748-2-ziqiaokong@gmail.com> +Cc: qemu-stable@nongnu.org +Signed-off-by: Paolo Bonzini +(cherry picked from commit 99d0dcd7f102c07a510200d768cae65e5db25d23) +Signed-off-by: Michael Tokarev +--- + target/i386/tcg/translate.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c +index 8fd49ff..83c2b40 100644 +--- a/target/i386/tcg/translate.c ++++ b/target/i386/tcg/translate.c +@@ -1480,12 +1480,13 @@ static bool check_iopl(DisasContext *s) + /* if d == OR_TMP0, it means memory operand (address in A0) */ + static void gen_op(DisasContext *s1, int op, MemOp ot, int d) + { ++ /* Invalid lock prefix when destination is not memory or OP_CMPL. */ ++ if ((d != OR_TMP0 || op == OP_CMPL) && s1->prefix & PREFIX_LOCK) { ++ gen_illegal_opcode(s1); ++ return; ++ } ++ + if (d != OR_TMP0) { +- if (s1->prefix & PREFIX_LOCK) { +- /* Lock prefix when destination is not memory. */ +- gen_illegal_opcode(s1); +- return; +- } + gen_op_mov_v_reg(s1, ot, s1->T0, d); + } else if (!(s1->prefix & PREFIX_LOCK)) { + gen_op_ld_v(s1, ot, s1->T0, s1->A0); +-- +1.8.3.1 + diff --git a/0118-ui-reject-extended-clipboard-message-if-not-activate.patch b/0118-ui-reject-extended-clipboard-message-if-not-activate.patch new file mode 100644 index 0000000000000000000000000000000000000000..802475a566bc140cad8f699516b8f6e6a9ab6515 --- /dev/null +++ b/0118-ui-reject-extended-clipboard-message-if-not-activate.patch @@ -0,0 +1,42 @@ +From 4fd56da3370a7ad009a0a960e722991c9927d890 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= +Date: Mon, 15 Jan 2024 09:51:19 +0000 +Subject: [PATCH 126/293] ui: reject extended clipboard message if not + activated +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The extended clipboard message protocol requires that the client +activate the extension by requesting a psuedo encoding. If this +is not done, then any extended clipboard messages from the client +should be considered invalid and the client dropped. + +Signed-off-by: Daniel P. Berrangé +Reviewed-by: Marc-André Lureau +Message-Id: <20240115095119.654271-1-berrange@redhat.com> +(cherry picked from commit 4cba8388968b70fe20e290221dc421c717051fdd) +Signed-off-by: Michael Tokarev +--- + ui/vnc.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/ui/vnc.c b/ui/vnc.c +index 4f23a0f..3b2c71e 100644 +--- a/ui/vnc.c ++++ b/ui/vnc.c +@@ -2445,6 +2445,11 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) + } + + if (read_s32(data, 4) < 0) { ++ if (!vnc_has_feature(vs, VNC_FEATURE_CLIPBOARD_EXT)) { ++ error_report("vnc: extended clipboard message while disabled"); ++ vnc_client_error(vs); ++ break; ++ } + if (dlen < 4) { + error_report("vnc: malformed payload (header less than 4 bytes)" + " in extended clipboard pseudo-encoding."); +-- +1.8.3.1 + diff --git a/0119-ui-clipboard-mark-type-as-not-available-when-there-i.patch b/0119-ui-clipboard-mark-type-as-not-available-when-there-i.patch new file mode 100644 index 0000000000000000000000000000000000000000..cead25ec5c3d40d8a0377cc6f04ca006dc5bf1ba --- /dev/null +++ b/0119-ui-clipboard-mark-type-as-not-available-when-there-i.patch @@ -0,0 +1,90 @@ +From 480a6adc83a7bbc84bfe67229e084603dc061824 Mon Sep 17 00:00:00 2001 +From: Fiona Ebner +Date: Wed, 24 Jan 2024 11:57:48 +0100 +Subject: [PATCH 127/293] ui/clipboard: mark type as not available when there + is no data +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +With VNC, a client can send a non-extended VNC_MSG_CLIENT_CUT_TEXT +message with len=0. In qemu_clipboard_set_data(), the clipboard info +will be updated setting data to NULL (because g_memdup(data, size) +returns NULL when size is 0). If the client does not set the +VNC_ENCODING_CLIPBOARD_EXT feature when setting up the encodings, then +the 'request' callback for the clipboard peer is not initialized. +Later, because data is NULL, qemu_clipboard_request() can be reached +via vdagent_chr_write() and vdagent_clipboard_recv_request() and +there, the clipboard owner's 'request' callback will be attempted to +be called, but that is a NULL pointer. + +In particular, this can happen when using the KRDC (22.12.3) VNC +client. + +Another scenario leading to the same issue is with two clients (say +noVNC and KRDC): + +The noVNC client sets the extension VNC_FEATURE_CLIPBOARD_EXT and +initializes its cbpeer. + +The KRDC client does not, but triggers a vnc_client_cut_text() (note +it's not the _ext variant)). There, a new clipboard info with it as +the 'owner' is created and via qemu_clipboard_set_data() is called, +which in turn calls qemu_clipboard_update() with that info. + +In qemu_clipboard_update(), the notifier for the noVNC client will be +called, i.e. vnc_clipboard_notify() and also set vs->cbinfo for the +noVNC client. The 'owner' in that clipboard info is the clipboard peer +for the KRDC client, which did not initialize the 'request' function. +That sounds correct to me, it is the owner of that clipboard info. + +Then when noVNC sends a VNC_MSG_CLIENT_CUT_TEXT message (it did set +the VNC_FEATURE_CLIPBOARD_EXT feature correctly, so a check for it +passes), that clipboard info is passed to qemu_clipboard_request() and +the original segfault still happens. + +Fix the issue by handling updates with size 0 differently. In +particular, mark in the clipboard info that the type is not available. + +While at it, switch to g_memdup2(), because g_memdup() is deprecated. + +Cc: qemu-stable@nongnu.org +Fixes: CVE-2023-6683 +Reported-by: Markus Frank +Suggested-by: Marc-André Lureau +Signed-off-by: Fiona Ebner +Reviewed-by: Marc-André Lureau +Tested-by: Markus Frank +Message-ID: <20240124105749.204610-1-f.ebner@proxmox.com> +(cherry picked from commit 405484b29f6548c7b86549b0f961b906337aa68a) +Signed-off-by: Michael Tokarev +--- + ui/clipboard.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/ui/clipboard.c b/ui/clipboard.c +index 3d14bff..b3f6fa3 100644 +--- a/ui/clipboard.c ++++ b/ui/clipboard.c +@@ -163,9 +163,15 @@ void qemu_clipboard_set_data(QemuClipboardPeer *peer, + } + + g_free(info->types[type].data); +- info->types[type].data = g_memdup(data, size); +- info->types[type].size = size; +- info->types[type].available = true; ++ if (size) { ++ info->types[type].data = g_memdup2(data, size); ++ info->types[type].size = size; ++ info->types[type].available = true; ++ } else { ++ info->types[type].data = NULL; ++ info->types[type].size = 0; ++ info->types[type].available = false; ++ } + + if (update) { + qemu_clipboard_update(info); +-- +1.8.3.1 + diff --git a/0120-ui-clipboard-add-asserts-for-update-and-request.patch b/0120-ui-clipboard-add-asserts-for-update-and-request.patch new file mode 100644 index 0000000000000000000000000000000000000000..064aeec63a393a1779db91d7a83519224758fc52 --- /dev/null +++ b/0120-ui-clipboard-add-asserts-for-update-and-request.patch @@ -0,0 +1,62 @@ +From 7ff0d4d184a83ac54b917c5af577e7a32e6a6f3e Mon Sep 17 00:00:00 2001 +From: Fiona Ebner +Date: Wed, 24 Jan 2024 11:57:49 +0100 +Subject: [PATCH 128/293] ui/clipboard: add asserts for update and request +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Should an issue like CVE-2023-6683 ever appear again in the future, +it will be more obvious which assumption was violated. + +Suggested-by: Marc-André Lureau +Signed-off-by: Fiona Ebner +Reviewed-by: Marc-André Lureau +Message-ID: <20240124105749.204610-2-f.ebner@proxmox.com> +(cherry picked from commit 9c416582611b7495bdddb4c5456c7acb64b78938) +Signed-off-by: Michael Tokarev +--- + ui/clipboard.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/ui/clipboard.c b/ui/clipboard.c +index b3f6fa3..4264884 100644 +--- a/ui/clipboard.c ++++ b/ui/clipboard.c +@@ -65,12 +65,24 @@ bool qemu_clipboard_check_serial(QemuClipboardInfo *info, bool client) + + void qemu_clipboard_update(QemuClipboardInfo *info) + { ++ uint32_t type; + QemuClipboardNotify notify = { + .type = QEMU_CLIPBOARD_UPDATE_INFO, + .info = info, + }; + assert(info->selection < QEMU_CLIPBOARD_SELECTION__COUNT); + ++ for (type = 0; type < QEMU_CLIPBOARD_TYPE__COUNT; type++) { ++ /* ++ * If data is missing, the clipboard owner's 'request' callback needs to ++ * be set. Otherwise, there is no way to get the clipboard data and ++ * qemu_clipboard_request() cannot be called. ++ */ ++ if (info->types[type].available && !info->types[type].data) { ++ assert(info->owner && info->owner->request); ++ } ++ } ++ + notifier_list_notify(&clipboard_notifiers, ¬ify); + + if (cbinfo[info->selection] != info) { +@@ -132,6 +144,8 @@ void qemu_clipboard_request(QemuClipboardInfo *info, + !info->owner) + return; + ++ assert(info->owner->request); ++ + info->types[type].requested = true; + info->owner->request(info, type); + } +-- +1.8.3.1 + diff --git a/0121-ui-console-Fix-console-resize-with-placeholder-surfa.patch b/0121-ui-console-Fix-console-resize-with-placeholder-surfa.patch new file mode 100644 index 0000000000000000000000000000000000000000..e9cdaf9d982bc8187b362d732c9c75a12f22aedb --- /dev/null +++ b/0121-ui-console-Fix-console-resize-with-placeholder-surfa.patch @@ -0,0 +1,43 @@ +From 2e5c9d5462a24d54ed8df0fcbab12e3cc6b50f90 Mon Sep 17 00:00:00 2001 +From: Tianlan Zhou +Date: Thu, 8 Feb 2024 01:20:25 +0800 +Subject: [PATCH 129/293] ui/console: Fix console resize with placeholder + surface +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +In `qemu_console_resize()`, the old surface of the console is keeped if the new +console size is the same as the old one. If the old surface is a placeholder, +and the new size of console is the same as the placeholder surface (640*480), +the surface won't be replace. +In this situation, the surface's `QEMU_PLACEHOLDER_FLAG` flag is still set, so +the console won't be displayed in SDL display mode. +This patch fixes this problem by forcing a new surface if the old one is a +placeholder. + +Signed-off-by: Tianlan Zhou +Reviewed-by: Marc-André Lureau +Message-ID: <20240207172024.8-1-bobby825@126.com> +(cherry picked from commit 95b08fee8f68d284a5028d37fd28be7a70c8e92b) +Signed-off-by: Michael Tokarev +--- + ui/console.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ui/console.c b/ui/console.c +index 7db921e..83205567 100644 +--- a/ui/console.c ++++ b/ui/console.c +@@ -1577,7 +1577,7 @@ void qemu_console_resize(QemuConsole *s, int width, int height) + assert(QEMU_IS_GRAPHIC_CONSOLE(s)); + + if ((s->scanout.kind != SCANOUT_SURFACE || +- (surface && surface->flags & QEMU_ALLOCATED_FLAG)) && ++ (surface && !is_buffer_shared(surface) && !is_placeholder(surface))) && + qemu_console_get_width(s, -1) == width && + qemu_console_get_height(s, -1) == height) { + return; +-- +1.8.3.1 + diff --git a/0122-audio-Depend-on-dbus_display1_dep.patch b/0122-audio-Depend-on-dbus_display1_dep.patch new file mode 100644 index 0000000000000000000000000000000000000000..38c7a821dc02956486d28aab2223b6a260573dad --- /dev/null +++ b/0122-audio-Depend-on-dbus_display1_dep.patch @@ -0,0 +1,37 @@ +From 1766b9360c3dfe3fb18241149047dc369ff7b1ae Mon Sep 17 00:00:00 2001 +From: Akihiko Odaki +Date: Wed, 14 Feb 2024 23:03:56 +0900 +Subject: [PATCH 130/293] audio: Depend on dbus_display1_dep +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +dbusaudio needs dbus_display1_dep. + +Fixes: 739362d4205c ("audio: add "dbus" audio backend") +Signed-off-by: Akihiko Odaki +Reviewed-by: Marc-André Lureau +Message-Id: <20240214-dbus-v7-1-7eff29f04c34@daynix.com> +(cherry picked from commit d67611907590a1e6c998b7c5a5cb4394acf84329) +Signed-off-by: Michael Tokarev +--- + audio/meson.build | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/audio/meson.build b/audio/meson.build +index c8f6586..608f35e 100644 +--- a/audio/meson.build ++++ b/audio/meson.build +@@ -30,7 +30,8 @@ endforeach + + if dbus_display + module_ss = ss.source_set() +- module_ss.add(when: [gio, pixman], if_true: files('dbusaudio.c')) ++ module_ss.add(when: [gio, dbus_display1_dep, pixman], ++ if_true: files('dbusaudio.c')) + audio_modules += {'dbus': module_ss} + endif + +-- +1.8.3.1 + diff --git a/0123-meson-Explicitly-specify-dbus-display1.h-dependency.patch b/0123-meson-Explicitly-specify-dbus-display1.h-dependency.patch new file mode 100644 index 0000000000000000000000000000000000000000..ed9910d7a425d4d18c564db8a4ea9116065e184b --- /dev/null +++ b/0123-meson-Explicitly-specify-dbus-display1.h-dependency.patch @@ -0,0 +1,37 @@ +From fb22ee75b2949b57c18872f9d1b026af9f068455 Mon Sep 17 00:00:00 2001 +From: Akihiko Odaki +Date: Wed, 14 Feb 2024 23:03:57 +0900 +Subject: [PATCH 131/293] meson: Explicitly specify dbus-display1.h dependency +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Explicitly specify dbus-display1.h as a dependency so that files +depending on it will not get compiled too early. + +Fixes: 1222070e7728 ("meson: ensure dbus-display generated code is built before other units") +Signed-off-by: Akihiko Odaki +Reviewed-by: Marc-André Lureau +Message-Id: <20240214-dbus-v7-2-7eff29f04c34@daynix.com> +(cherry picked from commit 7aee57df930da2cf6361c5183aff96468ae4027d) +Signed-off-by: Michael Tokarev +--- + ui/meson.build | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ui/meson.build b/ui/meson.build +index 0ccb338..0f09d31 100644 +--- a/ui/meson.build ++++ b/ui/meson.build +@@ -92,7 +92,7 @@ if dbus_display + '--c-namespace', 'QemuDBus', + '--generate-c-code', '@BASENAME@']) + dbus_display1_lib = static_library('dbus-display1', dbus_display1, dependencies: gio) +- dbus_display1_dep = declare_dependency(link_with: dbus_display1_lib, include_directories: include_directories('.')) ++ dbus_display1_dep = declare_dependency(link_with: dbus_display1_lib, sources: dbus_display1[0]) + dbus_ss.add(when: [gio, dbus_display1_dep], + if_true: [files( + 'dbus-chardev.c', +-- +1.8.3.1 + diff --git a/0124-tests-qtest-Depend-on-dbus_display1_dep.patch b/0124-tests-qtest-Depend-on-dbus_display1_dep.patch new file mode 100644 index 0000000000000000000000000000000000000000..202d0e2e67a20c48bd845bb92d0cd45b94871f39 --- /dev/null +++ b/0124-tests-qtest-Depend-on-dbus_display1_dep.patch @@ -0,0 +1,35 @@ +From 814f887430deb390341c8585cd413e0effc94798 Mon Sep 17 00:00:00 2001 +From: Akihiko Odaki +Date: Wed, 14 Feb 2024 23:03:58 +0900 +Subject: [PATCH 132/293] tests/qtest: Depend on dbus_display1_dep +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +It ensures dbus-display1.c will not be recompiled. + +Signed-off-by: Akihiko Odaki +Reviewed-by: Marc-André Lureau +Message-Id: <20240214-dbus-v7-3-7eff29f04c34@daynix.com> +(cherry picked from commit 186acfbaf7f325833702f50f75ef5116dc29e233) +Signed-off-by: Michael Tokarev +--- + tests/qtest/meson.build | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build +index fc14ae9..f096cf3 100644 +--- a/tests/qtest/meson.build ++++ b/tests/qtest/meson.build +@@ -333,7 +333,7 @@ if vnc.found() + endif + + if dbus_display +- qtests += {'dbus-display-test': [dbus_display1, gio]} ++ qtests += {'dbus-display-test': [dbus_display1_dep, gio]} + endif + + qtest_executables = {} +-- +1.8.3.1 + diff --git a/0125-hw-hppa-Kconfig-Fix-building-with-configure-without-.patch b/0125-hw-hppa-Kconfig-Fix-building-with-configure-without-.patch new file mode 100644 index 0000000000000000000000000000000000000000..b7b1e5de3460fcaae5c25d01d77b111a0126772a --- /dev/null +++ b/0125-hw-hppa-Kconfig-Fix-building-with-configure-without-.patch @@ -0,0 +1,55 @@ +From 56ee4a67cbfcc1f0c8dab0a9fd8d09dc9cd5a1c8 Mon Sep 17 00:00:00 2001 +From: Thomas Huth +Date: Fri, 16 Feb 2024 10:16:21 +0100 +Subject: [PATCH 133/293] hw/hppa/Kconfig: Fix building with "configure + --without-default-devices" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When running "configure" with "--without-default-devices", building +of qemu-system-hppa currently fails with: + + /usr/bin/ld: libqemu-hppa-softmmu.fa.p/hw_hppa_machine.c.o: in function `machine_HP_common_init_tail': + hw/hppa/machine.c:399: undefined reference to `usb_bus_find' + /usr/bin/ld: hw/hppa/machine.c:399: undefined reference to `usb_create_simple' + /usr/bin/ld: hw/hppa/machine.c:400: undefined reference to `usb_bus_find' + /usr/bin/ld: hw/hppa/machine.c:400: undefined reference to `usb_create_simple' + collect2: error: ld returned 1 exit status + ninja: build stopped: subcommand failed. + make: *** [Makefile:162: run-ninja] Error 1 + +And after fixing this, the qemu-system-hppa binary refuses to run +due to the missing 'pci-ohci' and 'pci-serial' devices. Let's add +the right config switches to fix these problems. + +Signed-off-by: Thomas Huth +Reviewed-by: Philippe Mathieu-Daudé +Reviewed-by: Michael Tokarev +Signed-off-by: Michael Tokarev +(cherry picked from commit 04b86ccb5dc8a1fad809753cfbaafd4bb13283d4) +Signed-off-by: Michael Tokarev +--- + hw/hppa/Kconfig | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/hw/hppa/Kconfig b/hw/hppa/Kconfig +index ff8528a..dff5df7 100644 +--- a/hw/hppa/Kconfig ++++ b/hw/hppa/Kconfig +@@ -7,6 +7,7 @@ config HPPA_B160L + select DINO + select LASI + select SERIAL ++ select SERIAL_PCI + select ISA_BUS + select I8259 + select IDE_CMD646 +@@ -16,3 +17,4 @@ config HPPA_B160L + select LASIPS2 + select PARALLEL + select ARTIST ++ select USB_OHCI_PCI +-- +1.8.3.1 + diff --git a/0126-docs-system-Update-description-for-input-grab-key.patch b/0126-docs-system-Update-description-for-input-grab-key.patch new file mode 100644 index 0000000000000000000000000000000000000000..325833f50d3935976d3804df14a933ba7ac33135 --- /dev/null +++ b/0126-docs-system-Update-description-for-input-grab-key.patch @@ -0,0 +1,34 @@ +From 2da2e679d6f6aa43521acf1fb3e35799baa44d80 Mon Sep 17 00:00:00 2001 +From: Tianlan Zhou +Date: Thu, 22 Feb 2024 03:52:09 +0800 +Subject: [PATCH 134/293] docs/system: Update description for input grab key + +Input grab key should be Ctrl-Alt-g, not just Ctrl-Alt. + +Fixes: f8d2c9369b ("sdl: use ctrl-alt-g as grab hotkey") +Signed-off-by: Tianlan Zhou +Reviewed-by: Thomas Huth +Reviewed-by: Michael Tokarev +Signed-off-by: Michael Tokarev +(cherry picked from commit 4a20ac400ff0753f159071764826b20e5320cde9) +Signed-off-by: Michael Tokarev +--- + docs/system/keys.rst.inc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/docs/system/keys.rst.inc b/docs/system/keys.rst.inc +index 2e2c97a..59966a3 100644 +--- a/docs/system/keys.rst.inc ++++ b/docs/system/keys.rst.inc +@@ -29,7 +29,7 @@ Ctrl-Alt-n + *3* + Serial port + +-Ctrl-Alt ++Ctrl-Alt-g + Toggle mouse and keyboard grab. + + In the virtual consoles, you can use Ctrl-Up, Ctrl-Down, Ctrl-PageUp and +-- +1.8.3.1 + diff --git a/0127-system-vl-Update-description-for-input-grab-key.patch b/0127-system-vl-Update-description-for-input-grab-key.patch new file mode 100644 index 0000000000000000000000000000000000000000..39c0e6fafd553c09dc7baa17fe22e83c8b514d4c --- /dev/null +++ b/0127-system-vl-Update-description-for-input-grab-key.patch @@ -0,0 +1,34 @@ +From aafe8c0d12b0449b0565d8ce8112e16f4ddd3ca1 Mon Sep 17 00:00:00 2001 +From: Tianlan Zhou +Date: Thu, 22 Feb 2024 03:52:10 +0800 +Subject: [PATCH 135/293] system/vl: Update description for input grab key + +Input grab key should be Ctrl-Alt-g, not just Ctrl-Alt. + +Fixes: f8d2c9369b ("sdl: use ctrl-alt-g as grab hotkey") +Signed-off-by: Tianlan Zhou +Reviewed-by: Thomas Huth +Reviewed-by: Michael Tokarev +Signed-off-by: Michael Tokarev +(cherry picked from commit 185311130f54ead75c407cdf83004d575829b5d2) +Signed-off-by: Michael Tokarev +--- + system/vl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/system/vl.c b/system/vl.c +index 938b7b5..e18fa3c 100644 +--- a/system/vl.c ++++ b/system/vl.c +@@ -892,7 +892,7 @@ static void help(int exitcode) + printf("\nDuring emulation, the following keys are useful:\n" + "ctrl-alt-f toggle full screen\n" + "ctrl-alt-n switch to virtual console 'n'\n" +- "ctrl-alt toggle mouse and keyboard grab\n" ++ "ctrl-alt-g toggle mouse and keyboard grab\n" + "\n" + "When using -nographic, press 'ctrl-a h' to get some help.\n" + "\n" +-- +1.8.3.1 + diff --git a/0128-.gitlab-ci.d-windows.yml-Drop-msys2-32bit-job.patch b/0128-.gitlab-ci.d-windows.yml-Drop-msys2-32bit-job.patch new file mode 100644 index 0000000000000000000000000000000000000000..61b19a4059ccd91dccbde444658d795f80e70bf4 --- /dev/null +++ b/0128-.gitlab-ci.d-windows.yml-Drop-msys2-32bit-job.patch @@ -0,0 +1,57 @@ +From 01aa603fb1083da1e26f5ce548a2ab64c21e8f50 Mon Sep 17 00:00:00 2001 +From: Peter Maydell +Date: Tue, 20 Feb 2024 16:56:02 +0000 +Subject: [PATCH 136/293] .gitlab-ci.d/windows.yml: Drop msys2-32bit job +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +MSYS2 is dropping support for 32-bit Windows. This shows up for us +as various packages we were using in our CI job no longer being +available to install, which causes the job to fail. In commit +8e31b744fdf we dropped the dependency on libusb and spice, but the +dtc package has also now been removed. + +For us as QEMU upstream, "32 bit x86 hosts for system emulation" have +already been deprecated as of QEMU 8.0, so we are ready to drop them +anyway. + +Drop the msys2-32bit CI job, as the first step in doing this. + +This is cc'd to stable, because this job will also be broken for CI +on the stable branches. We can't drop 32-bit support entirely there, +but we will still be covering at least compilation for 32-bit Windows +via the cross-win32-system job. + +Cc: qemu-stable@nongnu.org +Signed-off-by: Peter Maydell +Reviewed-by: Daniel P. Berrangé +Reviewed-by: Thomas Huth +Reviewed-by: Alex Bennée +Message-id: 20240220165602.135695-1-peter.maydell@linaro.org +(cherry picked from commit 5cd3ae4903e33982e7a9bbd04674af517e796d6e) +Signed-off-by: Michael Tokarev +--- + .gitlab-ci.d/windows.yml | 9 --------- + 1 file changed, 9 deletions(-) + +diff --git a/.gitlab-ci.d/windows.yml b/.gitlab-ci.d/windows.yml +index 5c1e385..8fc0821 100644 +--- a/.gitlab-ci.d/windows.yml ++++ b/.gitlab-ci.d/windows.yml +@@ -131,12 +131,3 @@ msys2-64bit: + # qTests don't run successfully with "--without-default-devices", + # so let's exclude the qtests from CI for now. + TEST_ARGS: --no-suite qtest +- +-msys2-32bit: +- extends: .shared_msys2_builder +- variables: +- MINGW_TARGET: mingw-w64-i686 +- MSYSTEM: MINGW32 +- EXTRA_PACKAGES: +- CONFIGURE_ARGS: --target-list=ppc64-softmmu -Ddebug=false -Doptimization=0 +- TEST_ARGS: --no-suite qtest +-- +1.8.3.1 + diff --git a/0129-target-ppc-Fix-lxv-stxv-MSR-facility-check.patch b/0129-target-ppc-Fix-lxv-stxv-MSR-facility-check.patch new file mode 100644 index 0000000000000000000000000000000000000000..284822026149df004fab704e2a403bec44c1864b --- /dev/null +++ b/0129-target-ppc-Fix-lxv-stxv-MSR-facility-check.patch @@ -0,0 +1,49 @@ +From 175bdedfa96feb06c325489bf1594822b0d7e8df Mon Sep 17 00:00:00 2001 +From: Nicholas Piggin +Date: Tue, 13 Feb 2024 17:50:43 +1000 +Subject: [PATCH 137/293] target/ppc: Fix lxv/stxv MSR facility check +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The move to decodetree flipped the inequality test for the VEC / VSX +MSR facility check. + +This caused application crashes under Linux, where these facility +unavailable interrupts are used for lazy-switching of VEC/VSX register +sets. Getting the incorrect interrupt would result in wrong registers +being loaded, potentially overwriting live values and/or exposing +stale ones. + +Cc: qemu-stable@nongnu.org +Reported-by: Joel Stanley +Fixes: 70426b5bb738 ("target/ppc: moved stxvx and lxvx from legacy to decodtree") +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1769 +Reviewed-by: Harsh Prateek Bora +Tested-by: Harsh Prateek Bora +Reviewed-by: Cédric Le Goater +Tested-by: Cédric Le Goater +Signed-off-by: Nicholas Piggin + +(cherry picked from commit 2cc0e449d17310877fb28a942d4627ad22bb68ea) +Signed-off-by: Michael Tokarev +--- + target/ppc/translate/vsx-impl.c.inc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc +index 6db87ab..0266f09 100644 +--- a/target/ppc/translate/vsx-impl.c.inc ++++ b/target/ppc/translate/vsx-impl.c.inc +@@ -2268,7 +2268,7 @@ static bool do_lstxv(DisasContext *ctx, int ra, TCGv displ, + + static bool do_lstxv_D(DisasContext *ctx, arg_D *a, bool store, bool paired) + { +- if (paired || a->rt >= 32) { ++ if (paired || a->rt < 32) { + REQUIRE_VSX(ctx); + } else { + REQUIRE_VECTOR(ctx); +-- +1.8.3.1 + diff --git a/0130-target-ppc-Fix-crash-on-machine-check-caused-by-ifet.patch b/0130-target-ppc-Fix-crash-on-machine-check-caused-by-ifet.patch new file mode 100644 index 0000000000000000000000000000000000000000..2478d63eb5c884e1774dfb1a842cfcd28e092cf1 --- /dev/null +++ b/0130-target-ppc-Fix-crash-on-machine-check-caused-by-ifet.patch @@ -0,0 +1,122 @@ +From 131ed62955eed97874b14deda7256baf526a9178 Mon Sep 17 00:00:00 2001 +From: Nicholas Piggin +Date: Fri, 15 Dec 2023 18:58:49 +1000 +Subject: [PATCH 138/293] target/ppc: Fix crash on machine check caused by + ifetch +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +is_prefix_insn_excp() loads the first word of the instruction address +which caused an exception, to determine whether or not it was prefixed +so the prefix bit can be set in [H]SRR1. + +This works if the instruction image can be loaded, but if the exception +was caused by an ifetch, this load could fail and cause a recursive +exception and crash. Machine checks caused by ifetch are not excluded +from the prefix check and can crash (see issue 2108 for an example). + +Fix this by excluding machine checks caused by ifetch from the prefix +check. + +Cc: qemu-stable@nongnu.org +Acked-by: Cédric Le Goater +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2108 +Fixes: 55a7fa34f89 ("target/ppc: Machine check on invalid real address access on POWER9/10") +Fixes: 5a5d3b23cb2 ("target/ppc: Add SRR1 prefix indication to interrupt handlers") +Signed-off-by: Nicholas Piggin +(cherry picked from commit c8fd9667e5975fe2e70a906e125a758737eab707) +Signed-off-by: Michael Tokarev +--- + target/ppc/excp_helper.c | 36 +++++++++++++++++++++++++----------- + 1 file changed, 25 insertions(+), 11 deletions(-) + +diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c +index a42743a..9b8fd69 100644 +--- a/target/ppc/excp_helper.c ++++ b/target/ppc/excp_helper.c +@@ -1312,6 +1312,10 @@ static bool is_prefix_insn_excp(PowerPCCPU *cpu, int excp) + { + CPUPPCState *env = &cpu->env; + ++ if (!(env->insns_flags2 & PPC2_ISA310)) { ++ return false; ++ } ++ + if (!tcg_enabled()) { + /* + * This does not load instructions and set the prefix bit correctly +@@ -1322,6 +1326,15 @@ static bool is_prefix_insn_excp(PowerPCCPU *cpu, int excp) + } + + switch (excp) { ++ case POWERPC_EXCP_MCHECK: ++ if (!(env->error_code & PPC_BIT(42))) { ++ /* ++ * Fetch attempt caused a machine check, so attempting to fetch ++ * again would cause a recursive machine check. ++ */ ++ return false; ++ } ++ break; + case POWERPC_EXCP_HDSI: + /* HDSI PRTABLE_FAULT has the originating access type in error_code */ + if ((env->spr[SPR_HDSISR] & DSISR_PRTABLE_FAULT) && +@@ -1332,10 +1345,10 @@ static bool is_prefix_insn_excp(PowerPCCPU *cpu, int excp) + * instruction at NIP would cause recursive faults with the same + * translation). + */ +- break; ++ return false; + } +- /* fall through */ +- case POWERPC_EXCP_MCHECK: ++ break; ++ + case POWERPC_EXCP_DSI: + case POWERPC_EXCP_DSEG: + case POWERPC_EXCP_ALIGN: +@@ -1346,17 +1359,13 @@ static bool is_prefix_insn_excp(PowerPCCPU *cpu, int excp) + case POWERPC_EXCP_VPU: + case POWERPC_EXCP_VSXU: + case POWERPC_EXCP_FU: +- case POWERPC_EXCP_HV_FU: { +- uint32_t insn = ppc_ldl_code(env, env->nip); +- if (is_prefix_insn(env, insn)) { +- return true; +- } ++ case POWERPC_EXCP_HV_FU: + break; +- } + default: +- break; ++ return false; + } +- return false; ++ ++ return is_prefix_insn(env, ppc_ldl_code(env, env->nip)); + } + #else + static bool is_prefix_insn_excp(PowerPCCPU *cpu, int excp) +@@ -3224,6 +3233,7 @@ void ppc_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, + + switch (env->excp_model) { + #if defined(TARGET_PPC64) ++ case POWERPC_EXCP_POWER8: + case POWERPC_EXCP_POWER9: + case POWERPC_EXCP_POWER10: + /* +@@ -3245,6 +3255,10 @@ void ppc_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, + env->error_code |= PPC_BIT(42); + + } else { /* Fetch */ ++ /* ++ * is_prefix_insn_excp() tests !PPC_BIT(42) to avoid fetching ++ * the instruction, so that must always be clear for fetches. ++ */ + env->error_code = PPC_BIT(36) | PPC_BIT(44) | PPC_BIT(45); + } + break; +-- +1.8.3.1 + diff --git a/0131-hw-nvme-fix-invalid-endian-conversion.patch b/0131-hw-nvme-fix-invalid-endian-conversion.patch new file mode 100644 index 0000000000000000000000000000000000000000..68f62c3573539632382ba6700b938c711e09d6a5 --- /dev/null +++ b/0131-hw-nvme-fix-invalid-endian-conversion.patch @@ -0,0 +1,41 @@ +From e4e36e65c9416c225cc684d82754ba1e5c3ca8ff Mon Sep 17 00:00:00 2001 +From: Klaus Jensen +Date: Thu, 22 Feb 2024 10:29:06 +0100 +Subject: [PATCH 141/293] hw/nvme: fix invalid endian conversion +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +numcntl is one byte and so is max_vfs. Using cpu_to_le16 on big endian +hosts results in numcntl being set to 0. + +Fix by dropping the endian conversion. + +Fixes: 99f48ae7ae ("hw/nvme: Add support for Secondary Controller List") +Reported-by: Kevin Wolf +Signed-off-by: Klaus Jensen +Reviewed-by: Minwoo Im +Message-ID: <20240222-fix-sriov-numcntl-v1-1-d60bea5e72d0@samsung.com> +Signed-off-by: Philippe Mathieu-Daudé +(cherry picked from commit d2b5bb860e6c17442ad95cc275feb07c1665be5c) +Signed-off-by: Michael Tokarev +--- + hw/nvme/ctrl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c +index f026245..76fe039 100644 +--- a/hw/nvme/ctrl.c ++++ b/hw/nvme/ctrl.c +@@ -7924,7 +7924,7 @@ static void nvme_init_state(NvmeCtrl *n) + n->aer_reqs = g_new0(NvmeRequest *, n->params.aerl + 1); + QTAILQ_INIT(&n->aer_queue); + +- list->numcntl = cpu_to_le16(max_vfs); ++ list->numcntl = max_vfs; + for (i = 0; i < max_vfs; i++) { + sctrl = &list->sec[i]; + sctrl->pcid = cpu_to_le16(n->cntlid); +-- +1.8.3.1 + diff --git a/0132-pl031-Update-last-RTCLR-value-on-write-in-case-it-s-.patch b/0132-pl031-Update-last-RTCLR-value-on-write-in-case-it-s-.patch new file mode 100644 index 0000000000000000000000000000000000000000..bf99e3430ae622c796cbe40cca5627b882e29f7b --- /dev/null +++ b/0132-pl031-Update-last-RTCLR-value-on-write-in-case-it-s-.patch @@ -0,0 +1,41 @@ +From a0fb839d0a48b1d421c41e301cadb6e5cd516eed Mon Sep 17 00:00:00 2001 +From: Jessica Clarke +Date: Mon, 26 Feb 2024 14:07:24 +0000 +Subject: [PATCH 142/293] pl031: Update last RTCLR value on write in case it's + read back +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The PL031 allows you to read RTCLR, which is meant to give you the last +value written. PL031State has an lr field which is used when reading +from RTCLR, and is present in the VM migration state, but we never +actually update it, so it always reads as its initial 0 value. + +Cc: qemu-stable@nongnu.org +Signed-off-by: Jessica Clarke +Reviewed-by: Alex Bennée +Message-id: 20240222000341.1562443-1-jrtc27@jrtc27.com +Reviewed-by: Peter Maydell +Signed-off-by: Peter Maydell +(cherry picked from commit 4d28d57c9f2eb1cdf70b29cea6e50282e010075b) +Signed-off-by: Michael Tokarev +--- + hw/rtc/pl031.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/hw/rtc/pl031.c b/hw/rtc/pl031.c +index b01d0e7..2f3cd04 100644 +--- a/hw/rtc/pl031.c ++++ b/hw/rtc/pl031.c +@@ -141,6 +141,7 @@ static void pl031_write(void * opaque, hwaddr offset, + g_autofree const char *qom_path = object_get_canonical_path(opaque); + struct tm tm; + ++ s->lr = value; + s->tick_offset += value - pl031_get_count(s); + + qemu_get_timedate(&tm, s->tick_offset); +-- +1.8.3.1 + diff --git a/0133-target-i386-mask-high-bits-of-CR3-in-32-bit-mode.patch b/0133-target-i386-mask-high-bits-of-CR3-in-32-bit-mode.patch new file mode 100644 index 0000000000000000000000000000000000000000..65d9b126162cf1e346d5c0a9a447c719ce5836a0 --- /dev/null +++ b/0133-target-i386-mask-high-bits-of-CR3-in-32-bit-mode.patch @@ -0,0 +1,45 @@ +From 6ed8211379b8bbd9d4d9f56a734819945a9711d6 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Fri, 22 Dec 2023 09:27:36 +0100 +Subject: [PATCH 143/293] target/i386: mask high bits of CR3 in 32-bit mode + +CR3 bits 63:32 are ignored in 32-bit mode (either legacy 2-level +paging or PAE paging). Do this in mmu_translate() to remove +the last where get_physical_address() meaningfully drops the high +bits of the address. + +Cc: qemu-stable@nongnu.org +Suggested-by: Richard Henderson +Fixes: 4a1e9d4d11c ("target/i386: Use atomic operations for pte updates", 2022-10-18) +Signed-off-by: Paolo Bonzini +(cherry picked from commit 68fb78d7d5723066ec2cacee7d25d67a4143b42f) +Signed-off-by: Michael Tokarev +--- + target/i386/tcg/sysemu/excp_helper.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/target/i386/tcg/sysemu/excp_helper.c b/target/i386/tcg/sysemu/excp_helper.c +index 5b86f43..11126c8 100644 +--- a/target/i386/tcg/sysemu/excp_helper.c ++++ b/target/i386/tcg/sysemu/excp_helper.c +@@ -238,7 +238,7 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, + /* + * Page table level 3 + */ +- pte_addr = ((in->cr3 & ~0x1f) + ((addr >> 27) & 0x18)) & a20_mask; ++ pte_addr = ((in->cr3 & 0xffffffe0ULL) + ((addr >> 27) & 0x18)) & a20_mask; + if (!ptw_translate(&pte_trans, pte_addr)) { + return false; + } +@@ -306,7 +306,7 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, + /* + * Page table level 2 + */ +- pte_addr = ((in->cr3 & ~0xfff) + ((addr >> 20) & 0xffc)) & a20_mask; ++ pte_addr = ((in->cr3 & 0xfffff000ULL) + ((addr >> 20) & 0xffc)) & a20_mask; + if (!ptw_translate(&pte_trans, pte_addr)) { + return false; + } +-- +1.8.3.1 + diff --git a/0134-target-i386-check-validity-of-VMCB-addresses.patch b/0134-target-i386-check-validity-of-VMCB-addresses.patch new file mode 100644 index 0000000000000000000000000000000000000000..2df3783146262a2edc9de7eaa557f9c4ed394eb6 --- /dev/null +++ b/0134-target-i386-check-validity-of-VMCB-addresses.patch @@ -0,0 +1,109 @@ +From 5c4091fe07a1a4447d1db749db34da3f7c599c0d Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Fri, 22 Dec 2023 17:47:38 +0100 +Subject: [PATCH 144/293] target/i386: check validity of VMCB addresses + +MSR_VM_HSAVE_PA bits 0-11 are reserved, as are the bits above the +maximum physical address width of the processor. Setting them to +1 causes a #GP (see "15.30.4 VM_HSAVE_PA MSR" in the AMD manual). + +The same is true of VMCB addresses passed to VMRUN/VMLOAD/VMSAVE, +even though the manual is not clear on that. + +Cc: qemu-stable@nongnu.org +Fixes: 4a1e9d4d11c ("target/i386: Use atomic operations for pte updates", 2022-10-18) +Signed-off-by: Paolo Bonzini +(cherry picked from commit d09c79010ffd880dc69e7a21e3cfdef90b928fb8) +Signed-off-by: Michael Tokarev +--- + target/i386/tcg/sysemu/misc_helper.c | 3 +++ + target/i386/tcg/sysemu/svm_helper.c | 27 +++++++++++++++++++++------ + 2 files changed, 24 insertions(+), 6 deletions(-) + +diff --git a/target/i386/tcg/sysemu/misc_helper.c b/target/i386/tcg/sysemu/misc_helper.c +index e1528b7..1901712 100644 +--- a/target/i386/tcg/sysemu/misc_helper.c ++++ b/target/i386/tcg/sysemu/misc_helper.c +@@ -201,6 +201,9 @@ void helper_wrmsr(CPUX86State *env) + tlb_flush(cs); + break; + case MSR_VM_HSAVE_PA: ++ if (val & (0xfff | ((~0ULL) << env_archcpu(env)->phys_bits))) { ++ goto error; ++ } + env->vm_hsave = val; + break; + #ifdef TARGET_X86_64 +diff --git a/target/i386/tcg/sysemu/svm_helper.c b/target/i386/tcg/sysemu/svm_helper.c +index 32ff0db..5d6de22 100644 +--- a/target/i386/tcg/sysemu/svm_helper.c ++++ b/target/i386/tcg/sysemu/svm_helper.c +@@ -164,14 +164,19 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) + uint64_t new_cr3; + uint64_t new_cr4; + +- cpu_svm_check_intercept_param(env, SVM_EXIT_VMRUN, 0, GETPC()); +- + if (aflag == 2) { + addr = env->regs[R_EAX]; + } else { + addr = (uint32_t)env->regs[R_EAX]; + } + ++ /* Exceptions are checked before the intercept. */ ++ if (addr & (0xfff | ((~0ULL) << env_archcpu(env)->phys_bits))) { ++ raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); ++ } ++ ++ cpu_svm_check_intercept_param(env, SVM_EXIT_VMRUN, 0, GETPC()); ++ + qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmrun! " TARGET_FMT_lx "\n", addr); + + env->vm_vmcb = addr; +@@ -463,14 +468,19 @@ void helper_vmload(CPUX86State *env, int aflag) + int mmu_idx = MMU_PHYS_IDX; + target_ulong addr; + +- cpu_svm_check_intercept_param(env, SVM_EXIT_VMLOAD, 0, GETPC()); +- + if (aflag == 2) { + addr = env->regs[R_EAX]; + } else { + addr = (uint32_t)env->regs[R_EAX]; + } + ++ /* Exceptions are checked before the intercept. */ ++ if (addr & (0xfff | ((~0ULL) << env_archcpu(env)->phys_bits))) { ++ raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); ++ } ++ ++ cpu_svm_check_intercept_param(env, SVM_EXIT_VMLOAD, 0, GETPC()); ++ + if (virtual_vm_load_save_enabled(env, SVM_EXIT_VMLOAD, GETPC())) { + mmu_idx = MMU_NESTED_IDX; + } +@@ -519,14 +529,19 @@ void helper_vmsave(CPUX86State *env, int aflag) + int mmu_idx = MMU_PHYS_IDX; + target_ulong addr; + +- cpu_svm_check_intercept_param(env, SVM_EXIT_VMSAVE, 0, GETPC()); +- + if (aflag == 2) { + addr = env->regs[R_EAX]; + } else { + addr = (uint32_t)env->regs[R_EAX]; + } + ++ /* Exceptions are checked before the intercept. */ ++ if (addr & (0xfff | ((~0ULL) << env_archcpu(env)->phys_bits))) { ++ raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); ++ } ++ ++ cpu_svm_check_intercept_param(env, SVM_EXIT_VMSAVE, 0, GETPC()); ++ + if (virtual_vm_load_save_enabled(env, SVM_EXIT_VMSAVE, GETPC())) { + mmu_idx = MMU_NESTED_IDX; + } +-- +1.8.3.1 + diff --git a/0135-target-i386-Fix-physical-address-truncation.patch b/0135-target-i386-Fix-physical-address-truncation.patch new file mode 100644 index 0000000000000000000000000000000000000000..8e788c73c1ea8d3951f6676905c9232387b506f2 --- /dev/null +++ b/0135-target-i386-Fix-physical-address-truncation.patch @@ -0,0 +1,99 @@ +From a28b6b4e7431a7557958a8b105626a5a5958791c Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Fri, 22 Dec 2023 18:01:52 +0100 +Subject: [PATCH 145/293] target/i386: Fix physical address truncation + +The address translation logic in get_physical_address() will currently +truncate physical addresses to 32 bits unless long mode is enabled. +This is incorrect when using physical address extensions (PAE) outside +of long mode, with the result that a 32-bit operating system using PAE +to access memory above 4G will experience undefined behaviour. + +The truncation code was originally introduced in commit 33dfdb5 ("x86: +only allow real mode to access 32bit without LMA"), where it applied +only to translations performed while paging is disabled (and so cannot +affect guests using PAE). + +Commit 9828198 ("target/i386: Add MMU_PHYS_IDX and MMU_NESTED_IDX") +rearranged the code such that the truncation also applied to the use +of MMU_PHYS_IDX and MMU_NESTED_IDX. Commit 4a1e9d4 ("target/i386: Use +atomic operations for pte updates") brought this truncation into scope +for page table entry accesses, and is the first commit for which a +Windows 10 32-bit guest will reliably fail to boot if memory above 4G +is present. + +The truncation code however is not completely redundant. Even though the +maximum address size for any executed instruction is 32 bits, helpers for +operations such as BOUND, FSAVE or XSAVE may ask get_physical_address() +to translate an address outside of the 32-bit range, if invoked with an +argument that is close to the 4G boundary. Likewise for processor +accesses, for example TSS or IDT accesses, when EFER.LMA==0. + +So, move the address truncation in get_physical_address() so that it +applies to 32-bit MMU indexes, but not to MMU_PHYS_IDX and MMU_NESTED_IDX. + +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2040 +Fixes: 4a1e9d4d11c ("target/i386: Use atomic operations for pte updates", 2022-10-18) +Cc: qemu-stable@nongnu.org +Co-developed-by: Michael Brown +Signed-off-by: Michael Brown +Signed-off-by: Paolo Bonzini +(cherry picked from commit b1661801c184119a10ad6cbc3b80330fc22e7b2c) +Signed-off-by: Michael Tokarev +(Mjt: drop unrelated change in target/i386/cpu.c) +--- + target/i386/cpu.h | 6 ++++++ + target/i386/tcg/sysemu/excp_helper.c | 12 +++++------- + 2 files changed, 11 insertions(+), 7 deletions(-) + +diff --git a/target/i386/cpu.h b/target/i386/cpu.h +index ef987f3..705d925 100644 +--- a/target/i386/cpu.h ++++ b/target/i386/cpu.h +@@ -2302,6 +2302,12 @@ static inline int cpu_mmu_index(CPUX86State *env, bool ifetch) + ? MMU_KNOSMAP_IDX : MMU_KSMAP_IDX; + } + ++static inline bool is_mmu_index_32(int mmu_index) ++{ ++ assert(mmu_index < MMU_PHYS_IDX); ++ return mmu_index & 1; ++} ++ + static inline int cpu_mmu_index_kernel(CPUX86State *env) + { + return !(env->hflags & HF_SMAP_MASK) ? MMU_KNOSMAP_IDX : +diff --git a/target/i386/tcg/sysemu/excp_helper.c b/target/i386/tcg/sysemu/excp_helper.c +index 11126c8..38c313a 100644 +--- a/target/i386/tcg/sysemu/excp_helper.c ++++ b/target/i386/tcg/sysemu/excp_helper.c +@@ -557,6 +557,10 @@ static bool get_physical_address(CPUX86State *env, vaddr addr, + break; + + default: ++ if (is_mmu_index_32(mmu_idx)) { ++ addr = (uint32_t)addr; ++ } ++ + if (likely(env->cr[0] & CR0_PG_MASK)) { + in.cr3 = env->cr[3]; + in.mmu_idx = mmu_idx; +@@ -580,14 +584,8 @@ static bool get_physical_address(CPUX86State *env, vaddr addr, + break; + } + +- /* Translation disabled. */ ++ /* No translation needed. */ + out->paddr = addr & x86_get_a20_mask(env); +-#ifdef TARGET_X86_64 +- if (!(env->hflags & HF_LMA_MASK)) { +- /* Without long mode we can only address 32bits in real mode */ +- out->paddr = (uint32_t)out->paddr; +- } +-#endif + out->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + out->page_size = TARGET_PAGE_SIZE; + return true; +-- +1.8.3.1 + diff --git a/0136-target-i386-remove-unnecessary-wrong-application-of-.patch b/0136-target-i386-remove-unnecessary-wrong-application-of-.patch new file mode 100644 index 0000000000000000000000000000000000000000..82e8076da0203af1a87d119bc2ade0fa62ba4fe0 --- /dev/null +++ b/0136-target-i386-remove-unnecessary-wrong-application-of-.patch @@ -0,0 +1,113 @@ +From 6801a20ebd0e541f45855665a75571d4d24188a0 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Fri, 22 Dec 2023 09:52:27 +0100 +Subject: [PATCH 146/293] target/i386: remove unnecessary/wrong application of + the A20 mask + +If ptw_translate() does a MMU_PHYS_IDX access, the A20 mask is already +applied in get_physical_address(), which is called via probe_access_full() +and x86_cpu_tlb_fill(). + +If ptw_translate() on the other hand does a MMU_NESTED_IDX access, +the A20 mask must not be applied to the address that is looked up in +the nested page tables; it must be applied only to the addresses that +hold the NPT entries (which is achieved via MMU_PHYS_IDX, per the +previous paragraph). + +Therefore, we can remove A20 masking from the computation of the page +table entry's address, and let get_physical_address() or mmu_translate() +apply it when they know they are returning a host-physical address. + +Cc: qemu-stable@nongnu.org +Fixes: 4a1e9d4d11c ("target/i386: Use atomic operations for pte updates", 2022-10-18) +Signed-off-by: Paolo Bonzini +(cherry picked from commit a28fe7dc1939333c81b895cdced81c69eb7c5ad0) +Signed-off-by: Michael Tokarev +--- + target/i386/tcg/sysemu/excp_helper.c | 21 ++++++++------------- + 1 file changed, 8 insertions(+), 13 deletions(-) + +diff --git a/target/i386/tcg/sysemu/excp_helper.c b/target/i386/tcg/sysemu/excp_helper.c +index 38c313a..89df61e 100644 +--- a/target/i386/tcg/sysemu/excp_helper.c ++++ b/target/i386/tcg/sysemu/excp_helper.c +@@ -164,8 +164,7 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, + /* + * Page table level 5 + */ +- pte_addr = ((in->cr3 & ~0xfff) + +- (((addr >> 48) & 0x1ff) << 3)) & a20_mask; ++ pte_addr = (in->cr3 & ~0xfff) + (((addr >> 48) & 0x1ff) << 3); + if (!ptw_translate(&pte_trans, pte_addr)) { + return false; + } +@@ -189,8 +188,7 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, + /* + * Page table level 4 + */ +- pte_addr = ((pte & PG_ADDRESS_MASK) + +- (((addr >> 39) & 0x1ff) << 3)) & a20_mask; ++ pte_addr = (pte & PG_ADDRESS_MASK) + (((addr >> 39) & 0x1ff) << 3); + if (!ptw_translate(&pte_trans, pte_addr)) { + return false; + } +@@ -210,8 +208,7 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, + /* + * Page table level 3 + */ +- pte_addr = ((pte & PG_ADDRESS_MASK) + +- (((addr >> 30) & 0x1ff) << 3)) & a20_mask; ++ pte_addr = (pte & PG_ADDRESS_MASK) + (((addr >> 30) & 0x1ff) << 3); + if (!ptw_translate(&pte_trans, pte_addr)) { + return false; + } +@@ -238,7 +235,7 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, + /* + * Page table level 3 + */ +- pte_addr = ((in->cr3 & 0xffffffe0ULL) + ((addr >> 27) & 0x18)) & a20_mask; ++ pte_addr = (in->cr3 & 0xffffffe0ULL) + ((addr >> 27) & 0x18); + if (!ptw_translate(&pte_trans, pte_addr)) { + return false; + } +@@ -260,8 +257,7 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, + /* + * Page table level 2 + */ +- pte_addr = ((pte & PG_ADDRESS_MASK) + +- (((addr >> 21) & 0x1ff) << 3)) & a20_mask; ++ pte_addr = (pte & PG_ADDRESS_MASK) + (((addr >> 21) & 0x1ff) << 3); + if (!ptw_translate(&pte_trans, pte_addr)) { + return false; + } +@@ -287,8 +283,7 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, + /* + * Page table level 1 + */ +- pte_addr = ((pte & PG_ADDRESS_MASK) + +- (((addr >> 12) & 0x1ff) << 3)) & a20_mask; ++ pte_addr = (pte & PG_ADDRESS_MASK) + (((addr >> 12) & 0x1ff) << 3); + if (!ptw_translate(&pte_trans, pte_addr)) { + return false; + } +@@ -306,7 +301,7 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, + /* + * Page table level 2 + */ +- pte_addr = ((in->cr3 & 0xfffff000ULL) + ((addr >> 20) & 0xffc)) & a20_mask; ++ pte_addr = (in->cr3 & 0xfffff000ULL) + ((addr >> 20) & 0xffc); + if (!ptw_translate(&pte_trans, pte_addr)) { + return false; + } +@@ -335,7 +330,7 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, + /* + * Page table level 1 + */ +- pte_addr = ((pte & ~0xfffu) + ((addr >> 10) & 0xffc)) & a20_mask; ++ pte_addr = (pte & ~0xfffu) + ((addr >> 10) & 0xffc); + if (!ptw_translate(&pte_trans, pte_addr)) { + return false; + } +-- +1.8.3.1 + diff --git a/0137-target-i386-leave-the-A20-bit-set-in-the-final-NPT-w.patch b/0137-target-i386-leave-the-A20-bit-set-in-the-final-NPT-w.patch new file mode 100644 index 0000000000000000000000000000000000000000..3045ce14b7e4ad2f43858da7055609192845ba9f --- /dev/null +++ b/0137-target-i386-leave-the-A20-bit-set-in-the-final-NPT-w.patch @@ -0,0 +1,62 @@ +From decafac46bec616add565fcfa71ce0184850f79b Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Fri, 22 Dec 2023 09:48:35 +0100 +Subject: [PATCH 147/293] target/i386: leave the A20 bit set in the final NPT + walk + +The A20 mask is only applied to the final memory access. Nested +page tables are always walked with the raw guest-physical address. + +Unlike the previous patch, in this one the masking must be kept, but +it was done too early. + +Cc: qemu-stable@nongnu.org +Fixes: 4a1e9d4d11c ("target/i386: Use atomic operations for pte updates", 2022-10-18) +Signed-off-by: Paolo Bonzini +(cherry picked from commit b5a9de3259f4c791bde2faff086dd5737625e41e) +Signed-off-by: Michael Tokarev +--- + target/i386/tcg/sysemu/excp_helper.c | 12 +++++++----- + 1 file changed, 7 insertions(+), 5 deletions(-) + +diff --git a/target/i386/tcg/sysemu/excp_helper.c b/target/i386/tcg/sysemu/excp_helper.c +index 89df61e..e16d3a6 100644 +--- a/target/i386/tcg/sysemu/excp_helper.c ++++ b/target/i386/tcg/sysemu/excp_helper.c +@@ -134,7 +134,6 @@ static inline bool ptw_setl(const PTETranslate *in, uint32_t old, uint32_t set) + static bool mmu_translate(CPUX86State *env, const TranslateParams *in, + TranslateResult *out, TranslateFault *err) + { +- const int32_t a20_mask = x86_get_a20_mask(env); + const target_ulong addr = in->addr; + const int pg_mode = in->pg_mode; + const bool is_user = (in->mmu_idx == MMU_USER_IDX); +@@ -417,10 +416,13 @@ do_check_protect_pse36: + } + } + +- /* align to page_size */ +- paddr = (pte & a20_mask & PG_ADDRESS_MASK & ~(page_size - 1)) +- | (addr & (page_size - 1)); ++ /* merge offset within page */ ++ paddr = (pte & PG_ADDRESS_MASK & ~(page_size - 1)) | (addr & (page_size - 1)); + ++ /* ++ * Note that NPT is walked (for both paging structures and final guest ++ * addresses) using the address with the A20 bit set. ++ */ + if (in->ptw_idx == MMU_NESTED_IDX) { + CPUTLBEntryFull *full; + int flags, nested_page_size; +@@ -459,7 +461,7 @@ do_check_protect_pse36: + } + } + +- out->paddr = paddr; ++ out->paddr = paddr & x86_get_a20_mask(env); + out->prot = prot; + out->page_size = page_size; + return true; +-- +1.8.3.1 + diff --git a/0138-tests-vm-update-openbsd-image-to-7.4.patch b/0138-tests-vm-update-openbsd-image-to-7.4.patch new file mode 100644 index 0000000000000000000000000000000000000000..782d82088756b09374137035492270e03f1adfe5 --- /dev/null +++ b/0138-tests-vm-update-openbsd-image-to-7.4.patch @@ -0,0 +1,62 @@ +From 36d50b4bde643e02fe42779794a4e3c5a9488649 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Alex=20Benn=C3=A9e?= +Date: Tue, 27 Feb 2024 14:43:10 +0000 +Subject: [PATCH 148/293] tests/vm: update openbsd image to 7.4 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The old links are dead so even if we have the ISO cached we can't +finish the install. Update to the current stable and tweak the install +strings. + +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2192 +Tested-by: Thomas Huth +Reviewed-by: Thomas Huth +Signed-off-by: Alex Bennée +Message-Id: <20240227144335.1196131-5-alex.bennee@linaro.org> +(cherry picked from commit 8467ac75b3b7207a49a1c6c7b87f0f7d2d0cea18) +Signed-off-by: Michael Tokarev +--- + tests/vm/openbsd | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/tests/vm/openbsd b/tests/vm/openbsd +index 85c5bb3..85c9863 100755 +--- a/tests/vm/openbsd ++++ b/tests/vm/openbsd +@@ -22,8 +22,8 @@ class OpenBSDVM(basevm.BaseVM): + name = "openbsd" + arch = "x86_64" + +- link = "https://cdn.openbsd.org/pub/OpenBSD/7.2/amd64/install72.iso" +- csum = "0369ef40a3329efcb978c578c7fdc7bda71e502aecec930a74b44160928c91d3" ++ link = "https://cdn.openbsd.org/pub/OpenBSD/7.4/amd64/install74.iso" ++ csum = "a1001736ed9fe2307965b5fcdb426ae11f9b80d26eb21e404a705144a0a224a0" + size = "20G" + pkgs = [ + # tools +@@ -99,10 +99,10 @@ class OpenBSDVM(basevm.BaseVM): + self.console_wait_send("(I)nstall", "i\n") + self.console_wait_send("Terminal type", "xterm\n") + self.console_wait_send("System hostname", "openbsd\n") +- self.console_wait_send("Which network interface", "vio0\n") ++ self.console_wait_send("Network interface to configure", "vio0\n") + self.console_wait_send("IPv4 address", "autoconf\n") + self.console_wait_send("IPv6 address", "none\n") +- self.console_wait_send("Which network interface", "done\n") ++ self.console_wait_send("Network interface to configure", "done\n") + self.console_wait("Password for root account") + self.console_send("%s\n" % self._config["root_pass"]) + self.console_wait("Password for root account") +@@ -124,6 +124,7 @@ class OpenBSDVM(basevm.BaseVM): + self.console_wait_send("Allow root ssh login", "yes\n") + self.console_wait_send("timezone", "UTC\n") + self.console_wait_send("root disk", "\n") ++ self.console_wait_send("Encrypt the root disk with a passphrase", "no\n") + self.console_wait_send("(W)hole disk", "\n") + self.console_wait_send("(A)uto layout", "c\n") + +-- +1.8.3.1 + diff --git a/0139-tests-vm-avoid-re-building-the-VM-images-all-the-tim.patch b/0139-tests-vm-avoid-re-building-the-VM-images-all-the-tim.patch new file mode 100644 index 0000000000000000000000000000000000000000..6e1a81038546dc7b15cbe3e072af808bb1199768 --- /dev/null +++ b/0139-tests-vm-avoid-re-building-the-VM-images-all-the-tim.patch @@ -0,0 +1,63 @@ +From 6c14f9318257107f911f133aafbaf4c5f888ec61 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Alex=20Benn=C3=A9e?= +Date: Tue, 27 Feb 2024 14:43:09 +0000 +Subject: [PATCH 149/293] tests/vm: avoid re-building the VM images all the + time +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The main problem is that "check-venv" is a .PHONY target will always +evaluate and trigger a full re-build of the VM images. While its +tempting to drop it from the dependencies that does introduce a +breakage on freshly configured builds. + +Fortunately we do have the otherwise redundant --force flag for the +script which up until now was always on. If we make the usage of +--force conditional on dependencies other than check-venv triggering +the update we can avoid the costly rebuild and still run cleanly on a +fresh checkout. + +Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2118 +Reviewed-by: Thomas Huth +Signed-off-by: Alex Bennée +Message-Id: <20240227144335.1196131-4-alex.bennee@linaro.org> +(cherry picked from commit 151b7dba391fab64cc008a1fdba6ddcf6f8c39c8) +Signed-off-by: Michael Tokarev +--- + tests/vm/Makefile.include | 2 +- + tests/vm/basevm.py | 4 ++-- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/tests/vm/Makefile.include b/tests/vm/Makefile.include +index bf12e0f..ac56824 100644 +--- a/tests/vm/Makefile.include ++++ b/tests/vm/Makefile.include +@@ -102,7 +102,7 @@ $(IMAGES_DIR)/%.img: $(SRC_PATH)/tests/vm/% \ + $(if $(LOG_CONSOLE),--log-console) \ + --source-path $(SRC_PATH) \ + --image "$@" \ +- --force \ ++ $(if $(filter-out check-venv, $?), --force) \ + --build-image $@, \ + " VM-IMAGE $*") + +diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py +index 61725b8..e38159a 100644 +--- a/tests/vm/basevm.py ++++ b/tests/vm/basevm.py +@@ -644,9 +644,9 @@ def main(vmcls, config=None): + vm = vmcls(args, config=config) + if args.build_image: + if os.path.exists(args.image) and not args.force: +- sys.stderr.writelines(["Image file exists: %s\n" % args.image, ++ sys.stderr.writelines(["Image file exists, skipping build: %s\n" % args.image, + "Use --force option to overwrite\n"]) +- return 1 ++ return 0 + return vm.build_image(args.image) + if args.build_qemu: + vm.add_source_dir(args.build_qemu) +-- +1.8.3.1 + diff --git a/0140-gitlab-force-allow-use-of-pip-in-Cirrus-jobs.patch b/0140-gitlab-force-allow-use-of-pip-in-Cirrus-jobs.patch new file mode 100644 index 0000000000000000000000000000000000000000..64925cb309dcd5ca8cf7a0462e3bc1e29b0c7201 --- /dev/null +++ b/0140-gitlab-force-allow-use-of-pip-in-Cirrus-jobs.patch @@ -0,0 +1,44 @@ +From 0e33e4e78e6b63cb9db158b0b8fc871fd2fc2c16 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= +Date: Thu, 22 Feb 2024 11:40:38 +0000 +Subject: [PATCH 150/293] gitlab: force allow use of pip in Cirrus jobs +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Python is transitioning to a world where you're not allowed to use 'pip +install' outside of a virutal env by default. The rationale is to stop +use of pip clashing with distro provided python packages, which creates +a major headache on distro upgrades. + +All our CI environments, however, are 100% disposable so the upgrade +headaches don't exist. Thus we can undo the python defaults to allow +pip to work. + +Signed-off-by: Daniel P. Berrangé +Tested-by: Philippe Mathieu-Daudé +Tested-by: Thomas Huth +Message-id: 20240222114038.2348718-1-berrange@redhat.com +Signed-off-by: Peter Maydell +(cherry picked from commit a8bf9de2f4f398315ac5340e4b88c478d5457731) +Signed-off-by: Michael Tokarev +--- + .gitlab-ci.d/cirrus/build.yml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/.gitlab-ci.d/cirrus/build.yml b/.gitlab-ci.d/cirrus/build.yml +index 29d55c4..43dd52d 100644 +--- a/.gitlab-ci.d/cirrus/build.yml ++++ b/.gitlab-ci.d/cirrus/build.yml +@@ -21,7 +21,7 @@ build_task: + install_script: + - @UPDATE_COMMAND@ + - @INSTALL_COMMAND@ @PKGS@ +- - if test -n "@PYPI_PKGS@" ; then @PIP3@ install @PYPI_PKGS@ ; fi ++ - if test -n "@PYPI_PKGS@" ; then PYLIB=$(@PYTHON@ -c 'import sysconfig; print(sysconfig.get_path("stdlib"))'); rm -f $PYLIB/EXTERNALLY-MANAGED; @PIP3@ install @PYPI_PKGS@ ; fi + clone_script: + - git clone --depth 100 "$CI_REPOSITORY_URL" . + - git fetch origin "$CI_COMMIT_REF_NAME" +-- +1.8.3.1 + diff --git a/0141-hw-intc-Kconfig-Fix-GIC-settings-when-using-without-.patch b/0141-hw-intc-Kconfig-Fix-GIC-settings-when-using-without-.patch new file mode 100644 index 0000000000000000000000000000000000000000..b8eb36aa5c6719368ed7e1d31f52d7c19e6c7a02 --- /dev/null +++ b/0141-hw-intc-Kconfig-Fix-GIC-settings-when-using-without-.patch @@ -0,0 +1,60 @@ +From 829bb27765afba34a54c49c47b5c658ac84bec3a Mon Sep 17 00:00:00 2001 +From: Thomas Huth +Date: Wed, 21 Feb 2024 12:00:59 +0100 +Subject: [PATCH 151/293] hw/intc/Kconfig: Fix GIC settings when using + "--without-default-devices" + +When using "--without-default-devices", the ARM_GICV3_TCG and ARM_GIC_KVM +settings currently get disabled, though the arm virt machine is only of +very limited use in that case. This also causes the migration-test to +fail in such builds. Let's make sure that we always keep the GIC switches +enabled in the --without-default-devices builds, too. + +Message-ID: <20240221110059.152665-1-thuth@redhat.com> +Tested-by: Fabiano Rosas +Signed-off-by: Thomas Huth +(cherry picked from commit 8bd3f84d1f6fba0edebc450be6fa2c7630584df9) +Signed-off-by: Michael Tokarev +--- + hw/intc/Kconfig | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig +index 97d550b..2b5b2d2 100644 +--- a/hw/intc/Kconfig ++++ b/hw/intc/Kconfig +@@ -12,10 +12,6 @@ config IOAPIC + bool + select I8259 + +-config ARM_GIC +- bool +- select MSI_NONBROKEN +- + config OPENPIC + bool + select MSI_NONBROKEN +@@ -25,14 +21,18 @@ config APIC + select MSI_NONBROKEN + select I8259 + ++config ARM_GIC ++ bool ++ select ARM_GICV3_TCG if TCG ++ select ARM_GIC_KVM if KVM ++ select MSI_NONBROKEN ++ + config ARM_GICV3_TCG + bool +- default y + depends on ARM_GIC && TCG + + config ARM_GIC_KVM + bool +- default y + depends on ARM_GIC && KVM + + config XICS +-- +1.8.3.1 + diff --git a/0142-hw-usb-bus.c-PCAP-adding-0xA-in-Windows-version.patch b/0142-hw-usb-bus.c-PCAP-adding-0xA-in-Windows-version.patch new file mode 100644 index 0000000000000000000000000000000000000000..19ebf53a4001959f41abf3be000495e38f7f2afc --- /dev/null +++ b/0142-hw-usb-bus.c-PCAP-adding-0xA-in-Windows-version.patch @@ -0,0 +1,63 @@ +From e6ce551c75827bc8907a3898a2f5f8132ba83db4 Mon Sep 17 00:00:00 2001 +From: Benjamin David Lunt +Date: Sun, 25 Feb 2024 12:49:51 -0700 +Subject: [PATCH 152/293] hw/usb/bus.c: PCAP adding 0xA in Windows version + +Since Windows text files use CRLFs for all \n, the Windows version of QEMU +inserts a CR in the PCAP stream when a LF is encountered when using USB PCAP +files. This is due to the fact that the PCAP file is opened as TEXT instead +of BINARY. + +To show an example, when using a very common protocol to USB disks, the BBB +protocol uses a 10-byte command packet. For example, the READ_CAPACITY(10) +command will have a command block length of 10 (0xA). When this 10-byte +command (part of the 31-byte CBW) is placed into the PCAP file, the Windows +file manager inserts a 0xD before the 0xA, turning the 31-byte CBW into a +32-byte CBW. + +Actual CBW: + 0040 55 53 42 43 01 00 00 00 08 00 00 00 80 00 0a 25 USBC...........% + 0050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ............... + +PCAP CBW + 0040 55 53 42 43 01 00 00 00 08 00 00 00 80 00 0d 0a USBC............ + 0050 25 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 %.............. + +I believe simply opening the PCAP file as BINARY instead of TEXT will fix +this issue. + +Resolves: https://bugs.launchpad.net/qemu/+bug/2054889 +Signed-off-by: Benjamin David Lunt +Message-ID: <000101da6823$ce1bbf80$6a533e80$@fysnet.net> +[thuth: Break long line to avoid checkpatch.pl error] +Signed-off-by: Thomas Huth +(cherry picked from commit 5e02a4fdebc442e34c5bb05e4540f85cc6e802f0) +Signed-off-by: Michael Tokarev +--- + hw/usb/bus.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/hw/usb/bus.c b/hw/usb/bus.c +index 92d6ed5..4d4c671 100644 +--- a/hw/usb/bus.c ++++ b/hw/usb/bus.c +@@ -273,13 +273,14 @@ static void usb_qdev_realize(DeviceState *qdev, Error **errp) + } + + if (dev->pcap_filename) { +- int fd = qemu_open_old(dev->pcap_filename, O_CREAT | O_WRONLY | O_TRUNC, 0666); ++ int fd = qemu_open_old(dev->pcap_filename, ++ O_CREAT | O_WRONLY | O_TRUNC | O_BINARY, 0666); + if (fd < 0) { + error_setg(errp, "open %s failed", dev->pcap_filename); + usb_qdev_unrealize(qdev); + return; + } +- dev->pcap = fdopen(fd, "w"); ++ dev->pcap = fdopen(fd, "wb"); + usb_pcap_init(dev->pcap); + } + } +-- +1.8.3.1 + diff --git a/0143-tests-unit-test-util-sockets-Remove-temporary-file-a.patch b/0143-tests-unit-test-util-sockets-Remove-temporary-file-a.patch new file mode 100644 index 0000000000000000000000000000000000000000..30821670e5a86aa6b4784cd714feb7e8f15694e6 --- /dev/null +++ b/0143-tests-unit-test-util-sockets-Remove-temporary-file-a.patch @@ -0,0 +1,39 @@ +From 2a97c05796a42dc0d0104fef76a1ac87153c2462 Mon Sep 17 00:00:00 2001 +From: Thomas Huth +Date: Mon, 26 Feb 2024 09:27:28 +0100 +Subject: [PATCH 153/293] tests/unit/test-util-sockets: Remove temporary file + after test +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +test-util-sockets leaves the temporary socket files around in the +temporary files folder. Let's better remove them at the end of the +testing. + +Fixes: 4d3a329af5 ("tests/util-sockets: add abstract unix socket cases") +Message-ID: <20240226082728.249753-1-thuth@redhat.com> +Reviewed-by: Marc-André Lureau +Reviewed-by: Philippe Mathieu-Daudé +Signed-off-by: Thomas Huth +(cherry picked from commit f0cb6828ae34fb56fbb869bb3147a636d1c984ce) +Signed-off-by: Michael Tokarev +--- + tests/unit/test-util-sockets.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tests/unit/test-util-sockets.c b/tests/unit/test-util-sockets.c +index 63909cc..4c9dd0b 100644 +--- a/tests/unit/test-util-sockets.c ++++ b/tests/unit/test-util-sockets.c +@@ -326,6 +326,7 @@ static void test_socket_unix_abstract(void) + test_socket_unix_abstract_row(&matrix[i]); + } + ++ unlink(addr.u.q_unix.path); + g_free(addr.u.q_unix.path); + } + +-- +1.8.3.1 + diff --git a/0144-chardev-char-socket-Fix-TLS-io-channels-sending-too-.patch b/0144-chardev-char-socket-Fix-TLS-io-channels-sending-too-.patch new file mode 100644 index 0000000000000000000000000000000000000000..ced342382be9fee8ffbf24b7c31ee923ff3b9904 --- /dev/null +++ b/0144-chardev-char-socket-Fix-TLS-io-channels-sending-too-.patch @@ -0,0 +1,90 @@ +From 21214699c23b8e1fd36c2a9965f9bcdc0008f101 Mon Sep 17 00:00:00 2001 +From: Thomas Huth +Date: Thu, 29 Feb 2024 11:43:37 +0100 +Subject: [PATCH 154/293] chardev/char-socket: Fix TLS io channels sending too + much data to the backend +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Commit ffda5db65a ("io/channel-tls: fix handling of bigger read buffers") +changed the behavior of the TLS io channels to schedule a second reading +attempt if there is still incoming data pending. This caused a regression +with backends like the sclpconsole that check in their read function that +the sender does not try to write more bytes to it than the device can +currently handle. + +The problem can be reproduced like this: + + 1) In one terminal, do this: + + mkdir qemu-pki + cd qemu-pki + openssl genrsa 2048 > ca-key.pem + openssl req -new -x509 -nodes -days 365000 -key ca-key.pem -out ca-cert.pem + # enter some dummy value for the cert + openssl genrsa 2048 > server-key.pem + openssl req -new -x509 -nodes -days 365000 -key server-key.pem \ + -out server-cert.pem + # enter some other dummy values for the cert + + gnutls-serv --echo --x509cafile ca-cert.pem --x509keyfile server-key.pem \ + --x509certfile server-cert.pem -p 8338 + + 2) In another terminal, do this: + + wget https://download.fedoraproject.org/pub/fedora-secondary/releases/39/Cloud/s390x/images/Fedora-Cloud-Base-39-1.5.s390x.qcow2 + + qemu-system-s390x -nographic -nodefaults \ + -hda Fedora-Cloud-Base-39-1.5.s390x.qcow2 \ + -object tls-creds-x509,id=tls0,endpoint=client,verify-peer=false,dir=$PWD/qemu-pki \ + -chardev socket,id=tls_chardev,host=localhost,port=8338,tls-creds=tls0 \ + -device sclpconsole,chardev=tls_chardev,id=tls_serial + +QEMU then aborts after a second or two with: + + qemu-system-s390x: ../hw/char/sclpconsole.c:73: chr_read: Assertion + `size <= SIZE_BUFFER_VT220 - scon->iov_data_len' failed. + Aborted (core dumped) + +It looks like the second read does not trigger the chr_can_read() function +to be called before the second read, which should normally always be done +before sending bytes to a character device to see how much it can handle, +so the s->max_size in tcp_chr_read() still contains the old value from the +previous read. Let's make sure that we use the up-to-date value by calling +tcp_chr_read_poll() again here. + +Fixes: ffda5db65a ("io/channel-tls: fix handling of bigger read buffers") +Buglink: https://issues.redhat.com/browse/RHEL-24614 +Reviewed-by: "Daniel P. Berrangé" +Message-ID: <20240229104339.42574-1-thuth@redhat.com> +Reviewed-by: Antoine Damhet +Tested-by: Antoine Damhet +Reviewed-by: Marc-André Lureau +Signed-off-by: Thomas Huth +(cherry picked from commit 462945cd22d2bcd233401ed3aa167d83a8e35b05) +Signed-off-by: Michael Tokarev +--- + chardev/char-socket.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/chardev/char-socket.c b/chardev/char-socket.c +index 73947da..0348405 100644 +--- a/chardev/char-socket.c ++++ b/chardev/char-socket.c +@@ -492,9 +492,9 @@ static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) + s->max_size <= 0) { + return TRUE; + } +- len = sizeof(buf); +- if (len > s->max_size) { +- len = s->max_size; ++ len = tcp_chr_read_poll(opaque); ++ if (len > sizeof(buf)) { ++ len = sizeof(buf); + } + size = tcp_chr_recv(chr, (void *)buf, len); + if (size == 0 || (size == -1 && errno != EAGAIN)) { +-- +1.8.3.1 + diff --git a/0145-Update-version-for-8.2.2-release.patch b/0145-Update-version-for-8.2.2-release.patch new file mode 100644 index 0000000000000000000000000000000000000000..2bde96a8958c2f49d929cfcbc30811881296cb34 --- /dev/null +++ b/0145-Update-version-for-8.2.2-release.patch @@ -0,0 +1,20 @@ +From 11aa0b1ff115b86160c4d37e7c37e6a6b13b77ea Mon Sep 17 00:00:00 2001 +From: Michael Tokarev +Date: Mon, 4 Mar 2024 15:15:46 +0300 +Subject: [PATCH 155/293] Update version for 8.2.2 release + +Signed-off-by: Michael Tokarev +--- + VERSION | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/VERSION b/VERSION +index 2b0aa21..308c0cb 100644 +--- a/VERSION ++++ b/VERSION +@@ -1 +1 @@ +-8.2.1 ++8.2.2 +-- +1.8.3.1 + diff --git a/0146-target-i386-Add-new-CPU-model-SierraForest.patch b/0146-target-i386-Add-new-CPU-model-SierraForest.patch new file mode 100644 index 0000000000000000000000000000000000000000..28e1b8e41327bc2203d2c5aa9798689739f2c63a --- /dev/null +++ b/0146-target-i386-Add-new-CPU-model-SierraForest.patch @@ -0,0 +1,212 @@ +From acc384dec0fbaa0f6cd3532bd641a118bce98886 Mon Sep 17 00:00:00 2001 +From: Tao Su +Date: Wed, 20 Mar 2024 10:10:44 +0800 +Subject: [PATCH 156/293] target/i386: Add new CPU model SierraForest +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +commit 6e82d3b6220777667968a04c87e1667f164ebe88 upstream. + +According to table 1-2 in Intel Architecture Instruction Set Extensions and +Future Features (rev 051) [1], SierraForest has the following new features +which have already been virtualized: + +- CMPCCXADD CPUID.(EAX=7,ECX=1):EAX[bit 7] +- AVX-IFMA CPUID.(EAX=7,ECX=1):EAX[bit 23] +- AVX-VNNI-INT8 CPUID.(EAX=7,ECX=1):EDX[bit 4] +- AVX-NE-CONVERT CPUID.(EAX=7,ECX=1):EDX[bit 5] + +Add above features to new CPU model SierraForest. Comparing with GraniteRapids +CPU model, SierraForest bare-metal removes the following features: + +- HLE CPUID.(EAX=7,ECX=0):EBX[bit 4] +- RTM CPUID.(EAX=7,ECX=0):EBX[bit 11] +- AVX512F CPUID.(EAX=7,ECX=0):EBX[bit 16] +- AVX512DQ CPUID.(EAX=7,ECX=0):EBX[bit 17] +- AVX512_IFMA CPUID.(EAX=7,ECX=0):EBX[bit 21] +- AVX512CD CPUID.(EAX=7,ECX=0):EBX[bit 28] +- AVX512BW CPUID.(EAX=7,ECX=0):EBX[bit 30] +- AVX512VL CPUID.(EAX=7,ECX=0):EBX[bit 31] +- AVX512_VBMI CPUID.(EAX=7,ECX=0):ECX[bit 1] +- AVX512_VBMI2 CPUID.(EAX=7,ECX=0):ECX[bit 6] +- AVX512_VNNI CPUID.(EAX=7,ECX=0):ECX[bit 11] +- AVX512_BITALG CPUID.(EAX=7,ECX=0):ECX[bit 12] +- AVX512_VPOPCNTDQ CPUID.(EAX=7,ECX=0):ECX[bit 14] +- LA57 CPUID.(EAX=7,ECX=0):ECX[bit 16] +- TSXLDTRK CPUID.(EAX=7,ECX=0):EDX[bit 16] +- AMX-BF16 CPUID.(EAX=7,ECX=0):EDX[bit 22] +- AVX512_FP16 CPUID.(EAX=7,ECX=0):EDX[bit 23] +- AMX-TILE CPUID.(EAX=7,ECX=0):EDX[bit 24] +- AMX-INT8 CPUID.(EAX=7,ECX=0):EDX[bit 25] +- AVX512_BF16 CPUID.(EAX=7,ECX=1):EAX[bit 5] +- fast zero-length MOVSB CPUID.(EAX=7,ECX=1):EAX[bit 10] +- fast short CMPSB, SCASB CPUID.(EAX=7,ECX=1):EAX[bit 12] +- AMX-FP16 CPUID.(EAX=7,ECX=1):EAX[bit 21] +- PREFETCHI CPUID.(EAX=7,ECX=1):EDX[bit 14] +- XFD CPUID.(EAX=0xD,ECX=1):EAX[bit 4] +- EPT_PAGE_WALK_LENGTH_5 VMX_EPT_VPID_CAP(0x48c)[bit 7] + +Add all features of GraniteRapids CPU model except above features to +SierraForest CPU model. + +SierraForest doesn’t support TSX and RTM but supports TAA_NO. When RTM is +not enabled in host, KVM will not report TAA_NO. So, just don't include +TAA_NO in SierraForest CPU model. + +[1] https://cdrdv2.intel.com/v1/dl/getContent/671368 + +Intel-SIG: commit 6e82d3b62207 target/i386: Add new CPU model SierraForest. +Add SRF new ISAs backporting + +Reviewed-by: Zhao Liu +Reviewed-by: Xiaoyao Li +Signed-off-by: Tao Su +Message-ID: <20240320021044.508263-1-tao1.su@linux.intel.com> +Signed-off-by: Paolo Bonzini +[ Quanxian Wang: amend commit log ] +Signed-off-by: Quanxian Wang +--- + target/i386/cpu.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 126 insertions(+) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index a66e5a3..8131c5b 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -4100,6 +4100,132 @@ static const X86CPUDefinition builtin_x86_defs[] = { + }, + }, + { ++ .name = "SierraForest", ++ .level = 0x23, ++ .vendor = CPUID_VENDOR_INTEL, ++ .family = 6, ++ .model = 175, ++ .stepping = 0, ++ /* ++ * please keep the ascending order so that we can have a clear view of ++ * bit position of each feature. ++ */ ++ .features[FEAT_1_EDX] = ++ CPUID_FP87 | CPUID_VME | CPUID_DE | CPUID_PSE | CPUID_TSC | ++ CPUID_MSR | CPUID_PAE | CPUID_MCE | CPUID_CX8 | CPUID_APIC | ++ CPUID_SEP | CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV | ++ CPUID_PAT | CPUID_PSE36 | CPUID_CLFLUSH | CPUID_MMX | CPUID_FXSR | ++ CPUID_SSE | CPUID_SSE2, ++ .features[FEAT_1_ECX] = ++ CPUID_EXT_SSE3 | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSSE3 | ++ CPUID_EXT_FMA | CPUID_EXT_CX16 | CPUID_EXT_PCID | CPUID_EXT_SSE41 | ++ CPUID_EXT_SSE42 | CPUID_EXT_X2APIC | CPUID_EXT_MOVBE | ++ CPUID_EXT_POPCNT | CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_AES | ++ CPUID_EXT_XSAVE | CPUID_EXT_AVX | CPUID_EXT_F16C | CPUID_EXT_RDRAND, ++ .features[FEAT_8000_0001_EDX] = ++ CPUID_EXT2_SYSCALL | CPUID_EXT2_NX | CPUID_EXT2_PDPE1GB | ++ CPUID_EXT2_RDTSCP | CPUID_EXT2_LM, ++ .features[FEAT_8000_0001_ECX] = ++ CPUID_EXT3_LAHF_LM | CPUID_EXT3_ABM | CPUID_EXT3_3DNOWPREFETCH, ++ .features[FEAT_8000_0008_EBX] = ++ CPUID_8000_0008_EBX_WBNOINVD, ++ .features[FEAT_7_0_EBX] = ++ CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_AVX2 | ++ CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | ++ CPUID_7_0_EBX_INVPCID | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | ++ CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLFLUSHOPT | CPUID_7_0_EBX_CLWB | ++ CPUID_7_0_EBX_SHA_NI, ++ .features[FEAT_7_0_ECX] = ++ CPUID_7_0_ECX_UMIP | CPUID_7_0_ECX_PKU | CPUID_7_0_ECX_GFNI | ++ CPUID_7_0_ECX_VAES | CPUID_7_0_ECX_VPCLMULQDQ | ++ CPUID_7_0_ECX_RDPID | CPUID_7_0_ECX_BUS_LOCK_DETECT, ++ .features[FEAT_7_0_EDX] = ++ CPUID_7_0_EDX_FSRM | CPUID_7_0_EDX_SERIALIZE | ++ CPUID_7_0_EDX_SPEC_CTRL | CPUID_7_0_EDX_ARCH_CAPABILITIES | ++ CPUID_7_0_EDX_SPEC_CTRL_SSBD, ++ .features[FEAT_ARCH_CAPABILITIES] = ++ MSR_ARCH_CAP_RDCL_NO | MSR_ARCH_CAP_IBRS_ALL | ++ MSR_ARCH_CAP_SKIP_L1DFL_VMENTRY | MSR_ARCH_CAP_MDS_NO | ++ MSR_ARCH_CAP_PSCHANGE_MC_NO | MSR_ARCH_CAP_SBDR_SSDP_NO | ++ MSR_ARCH_CAP_FBSDP_NO | MSR_ARCH_CAP_PSDP_NO | ++ MSR_ARCH_CAP_PBRSB_NO, ++ .features[FEAT_XSAVE] = ++ CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | ++ CPUID_XSAVE_XGETBV1 | CPUID_XSAVE_XSAVES, ++ .features[FEAT_6_EAX] = ++ CPUID_6_EAX_ARAT, ++ .features[FEAT_7_1_EAX] = ++ CPUID_7_1_EAX_AVX_VNNI | CPUID_7_1_EAX_CMPCCXADD | ++ CPUID_7_1_EAX_FSRS | CPUID_7_1_EAX_AVX_IFMA, ++ .features[FEAT_7_1_EDX] = ++ CPUID_7_1_EDX_AVX_VNNI_INT8 | CPUID_7_1_EDX_AVX_NE_CONVERT, ++ .features[FEAT_7_2_EDX] = ++ CPUID_7_2_EDX_MCDT_NO, ++ .features[FEAT_VMX_BASIC] = ++ MSR_VMX_BASIC_INS_OUTS | MSR_VMX_BASIC_TRUE_CTLS, ++ .features[FEAT_VMX_ENTRY_CTLS] = ++ VMX_VM_ENTRY_LOAD_DEBUG_CONTROLS | VMX_VM_ENTRY_IA32E_MODE | ++ VMX_VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL | ++ VMX_VM_ENTRY_LOAD_IA32_PAT | VMX_VM_ENTRY_LOAD_IA32_EFER, ++ .features[FEAT_VMX_EPT_VPID_CAPS] = ++ MSR_VMX_EPT_EXECONLY | MSR_VMX_EPT_PAGE_WALK_LENGTH_4 | ++ MSR_VMX_EPT_WB | MSR_VMX_EPT_2MB | MSR_VMX_EPT_1GB | ++ MSR_VMX_EPT_INVEPT | MSR_VMX_EPT_AD_BITS | ++ MSR_VMX_EPT_INVEPT_SINGLE_CONTEXT | MSR_VMX_EPT_INVEPT_ALL_CONTEXT | ++ MSR_VMX_EPT_INVVPID | MSR_VMX_EPT_INVVPID_SINGLE_ADDR | ++ MSR_VMX_EPT_INVVPID_SINGLE_CONTEXT | ++ MSR_VMX_EPT_INVVPID_ALL_CONTEXT | ++ MSR_VMX_EPT_INVVPID_SINGLE_CONTEXT_NOGLOBALS, ++ .features[FEAT_VMX_EXIT_CTLS] = ++ VMX_VM_EXIT_SAVE_DEBUG_CONTROLS | ++ VMX_VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | ++ VMX_VM_EXIT_ACK_INTR_ON_EXIT | VMX_VM_EXIT_SAVE_IA32_PAT | ++ VMX_VM_EXIT_LOAD_IA32_PAT | VMX_VM_EXIT_SAVE_IA32_EFER | ++ VMX_VM_EXIT_LOAD_IA32_EFER | VMX_VM_EXIT_SAVE_VMX_PREEMPTION_TIMER, ++ .features[FEAT_VMX_MISC] = ++ MSR_VMX_MISC_STORE_LMA | MSR_VMX_MISC_ACTIVITY_HLT | ++ MSR_VMX_MISC_VMWRITE_VMEXIT, ++ .features[FEAT_VMX_PINBASED_CTLS] = ++ VMX_PIN_BASED_EXT_INTR_MASK | VMX_PIN_BASED_NMI_EXITING | ++ VMX_PIN_BASED_VIRTUAL_NMIS | VMX_PIN_BASED_VMX_PREEMPTION_TIMER | ++ VMX_PIN_BASED_POSTED_INTR, ++ .features[FEAT_VMX_PROCBASED_CTLS] = ++ VMX_CPU_BASED_VIRTUAL_INTR_PENDING | ++ VMX_CPU_BASED_USE_TSC_OFFSETING | VMX_CPU_BASED_HLT_EXITING | ++ VMX_CPU_BASED_INVLPG_EXITING | VMX_CPU_BASED_MWAIT_EXITING | ++ VMX_CPU_BASED_RDPMC_EXITING | VMX_CPU_BASED_RDTSC_EXITING | ++ VMX_CPU_BASED_CR3_LOAD_EXITING | VMX_CPU_BASED_CR3_STORE_EXITING | ++ VMX_CPU_BASED_CR8_LOAD_EXITING | VMX_CPU_BASED_CR8_STORE_EXITING | ++ VMX_CPU_BASED_TPR_SHADOW | VMX_CPU_BASED_VIRTUAL_NMI_PENDING | ++ VMX_CPU_BASED_MOV_DR_EXITING | VMX_CPU_BASED_UNCOND_IO_EXITING | ++ VMX_CPU_BASED_USE_IO_BITMAPS | VMX_CPU_BASED_MONITOR_TRAP_FLAG | ++ VMX_CPU_BASED_USE_MSR_BITMAPS | VMX_CPU_BASED_MONITOR_EXITING | ++ VMX_CPU_BASED_PAUSE_EXITING | ++ VMX_CPU_BASED_ACTIVATE_SECONDARY_CONTROLS, ++ .features[FEAT_VMX_SECONDARY_CTLS] = ++ VMX_SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES | ++ VMX_SECONDARY_EXEC_ENABLE_EPT | VMX_SECONDARY_EXEC_DESC | ++ VMX_SECONDARY_EXEC_RDTSCP | ++ VMX_SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE | ++ VMX_SECONDARY_EXEC_ENABLE_VPID | VMX_SECONDARY_EXEC_WBINVD_EXITING | ++ VMX_SECONDARY_EXEC_UNRESTRICTED_GUEST | ++ VMX_SECONDARY_EXEC_APIC_REGISTER_VIRT | ++ VMX_SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY | ++ VMX_SECONDARY_EXEC_RDRAND_EXITING | ++ VMX_SECONDARY_EXEC_ENABLE_INVPCID | ++ VMX_SECONDARY_EXEC_ENABLE_VMFUNC | VMX_SECONDARY_EXEC_SHADOW_VMCS | ++ VMX_SECONDARY_EXEC_RDSEED_EXITING | VMX_SECONDARY_EXEC_ENABLE_PML | ++ VMX_SECONDARY_EXEC_XSAVES, ++ .features[FEAT_VMX_VMFUNC] = ++ MSR_VMX_VMFUNC_EPT_SWITCHING, ++ .xlevel = 0x80000008, ++ .model_id = "Intel Xeon Processor (SierraForest)", ++ .versions = (X86CPUVersionDefinition[]) { ++ { .version = 1 }, ++ { /* end of list */ }, ++ }, ++ }, ++ { + .name = "Denverton", + .level = 21, + .vendor = CPUID_VENDOR_INTEL, +-- +1.8.3.1 + diff --git a/0147-target-i386-Export-RFDS-bit-to-guests.patch b/0147-target-i386-Export-RFDS-bit-to-guests.patch new file mode 100644 index 0000000000000000000000000000000000000000..6ceba48ec18c70224948f4187bdcb39102410de9 --- /dev/null +++ b/0147-target-i386-Export-RFDS-bit-to-guests.patch @@ -0,0 +1,47 @@ +From 86b65efe45023109b446d1f12aee72cc1dbc12fc Mon Sep 17 00:00:00 2001 +From: Pawan Gupta +Date: Wed, 13 Mar 2024 07:53:23 -0700 +Subject: [PATCH 157/293] target/i386: Export RFDS bit to guests + +commit 41bdd9812863c150284a9339a048ed88c40f4df7 upstream. + +Register File Data Sampling (RFDS) is a CPU side-channel vulnerability +that may expose stale register value. CPUs that set RFDS_NO bit in MSR +IA32_ARCH_CAPABILITIES indicate that they are not vulnerable to RFDS. +Similarly, RFDS_CLEAR indicates that CPU is affected by RFDS, and has +the microcode to help mitigate RFDS. + +Make RFDS_CLEAR and RFDS_NO bits available to guests. + +Intel-SIG: commit 41bdd9812863 target/i386: Export RFDS bit to guests. +Add SRF new ISAs backporting + +Signed-off-by: Pawan Gupta +Reviewed-by: Xiaoyao Li +Reviewed-by: Zhao Liu +Message-ID: <9a38877857392b5c2deae7e7db1b170d15510314.1710341348.git.pawan.kumar.gupta@linux.intel.com> +Signed-off-by: Paolo Bonzini +[ Quanxian Wang: amend commit log ] +Signed-off-by: Quanxian Wang +--- + target/i386/cpu.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 8131c5b..5c0dfee 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -1157,8 +1157,8 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + NULL, "sbdr-ssdp-no", "fbsdp-no", "psdp-no", + NULL, "fb-clear", NULL, NULL, + NULL, NULL, NULL, NULL, +- "pbrsb-no", NULL, "gds-no", NULL, +- NULL, NULL, NULL, NULL, ++ "pbrsb-no", NULL, "gds-no", "rfds-no", ++ "rfds-clear", NULL, NULL, NULL, + }, + .msr = { + .index = MSR_IA32_ARCH_CAPABILITIES, +-- +1.8.3.1 + diff --git a/0148-hw-loongarch-virt-Align-high-memory-base-address-wit.patch b/0148-hw-loongarch-virt-Align-high-memory-base-address-wit.patch new file mode 100644 index 0000000000000000000000000000000000000000..f3c50fcff78e535db57d54bbb68d3a8a83084696 --- /dev/null +++ b/0148-hw-loongarch-virt-Align-high-memory-base-address-wit.patch @@ -0,0 +1,39 @@ +From 23cd527ecf10132eac9aad08e1930c249f17cfa1 Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Mon, 27 Nov 2023 12:02:31 +0800 +Subject: [PATCH 158/293] hw/loongarch/virt: Align high memory base address + with super page size + +With LoongArch virt machine, there is low memory space with region +0--0x10000000, and high memory space with started from 0x90000000. +High memory space is aligned with 256M, it will be better if it is +aligned with 1G, which is super page aligned for 4K page size. + +Currently linux kernel and uefi bios has no limitation with high +memory base address, it is ok to set high memory base address +with 0x80000000. + +Signed-off-by: Bibo Mao +Reviewed-by: Song Gao +Message-Id: <20231127040231.4123715-1-maobibo@loongson.cn> +Signed-off-by: Song Gao +--- + include/hw/loongarch/virt.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h +index 674f465..db0831b 100644 +--- a/include/hw/loongarch/virt.h ++++ b/include/hw/loongarch/virt.h +@@ -25,7 +25,7 @@ + + #define VIRT_LOWMEM_BASE 0 + #define VIRT_LOWMEM_SIZE 0x10000000 +-#define VIRT_HIGHMEM_BASE 0x90000000 ++#define VIRT_HIGHMEM_BASE 0x80000000 + #define VIRT_GED_EVT_ADDR 0x100e0000 + #define VIRT_GED_MEM_ADDR (VIRT_GED_EVT_ADDR + ACPI_GED_EVT_SEL_LEN) + #define VIRT_GED_REG_ADDR (VIRT_GED_MEM_ADDR + MEMORY_HOTPLUG_IO_LEN) +-- +1.8.3.1 + diff --git a/0149-target-loongarch-Add-timer-information-dump-support.patch b/0149-target-loongarch-Add-timer-information-dump-support.patch new file mode 100644 index 0000000000000000000000000000000000000000..06ac33ac3396047ab04124a3fb1d15c682435a23 --- /dev/null +++ b/0149-target-loongarch-Add-timer-information-dump-support.patch @@ -0,0 +1,33 @@ +From 289d5c3c03aa20a3bdb40b466ee320a15b693a95 Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Wed, 6 Dec 2023 16:18:39 +0800 +Subject: [PATCH 159/293] target/loongarch: Add timer information dump support + +Timer emulation sometimes is problematic especially when vm is running in +kvm mode. This patch adds registers dump support relative with timer +hardware, so that it is easier to find the problems. + +Signed-off-by: Bibo Mao +Reviewed-by: Song Gao +Message-Id: <20231206081839.2290178-1-maobibo@loongson.cn> +Signed-off-by: Song Gao +--- + target/loongarch/cpu.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c +index fc07595..db9a421 100644 +--- a/target/loongarch/cpu.c ++++ b/target/loongarch/cpu.c +@@ -762,6 +762,8 @@ void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) + qemu_fprintf(f, "TLBRENTRY=%016" PRIx64 "\n", env->CSR_TLBRENTRY); + qemu_fprintf(f, "TLBRBADV=%016" PRIx64 "\n", env->CSR_TLBRBADV); + qemu_fprintf(f, "TLBRERA=%016" PRIx64 "\n", env->CSR_TLBRERA); ++ qemu_fprintf(f, "TCFG=%016" PRIx64 "\n", env->CSR_TCFG); ++ qemu_fprintf(f, "TVAL=%016" PRIx64 "\n", env->CSR_TVAL); + + /* fpr */ + if (flags & CPU_DUMP_FPU) { +-- +1.8.3.1 + diff --git a/0150-target-loongarch-meson-move-gdbstub.c-to-loongarch.s.patch b/0150-target-loongarch-meson-move-gdbstub.c-to-loongarch.s.patch new file mode 100644 index 0000000000000000000000000000000000000000..2ce4cf9e72bd6f4c29de89f07245880cb3b678a5 --- /dev/null +++ b/0150-target-loongarch-meson-move-gdbstub.c-to-loongarch.s.patch @@ -0,0 +1,42 @@ +From 3ccdbe903cdb4d4b9b9bfc4ccacd3b5e94d9ec8f Mon Sep 17 00:00:00 2001 +From: Song Gao +Date: Tue, 2 Jan 2024 10:01:59 +0800 +Subject: [PATCH 160/293] target/loongarch/meson: move gdbstub.c to + loongarch.ss +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +gdbstub.c is not specific to TCG and can be used by +other accelerators, such as KVM accelerator + +Reviewed-by: Philippe Mathieu-Daudé +Signed-off-by: Song Gao +Message-Id: <20240102020200.3462097-1-gaosong@loongson.cn> +--- + target/loongarch/meson.build | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/target/loongarch/meson.build b/target/loongarch/meson.build +index 18e8191..b3a0fb1 100644 +--- a/target/loongarch/meson.build ++++ b/target/loongarch/meson.build +@@ -3,6 +3,7 @@ gen = decodetree.process('insns.decode') + loongarch_ss = ss.source_set() + loongarch_ss.add(files( + 'cpu.c', ++ 'gdbstub.c', + )) + loongarch_tcg_ss = ss.source_set() + loongarch_tcg_ss.add(gen) +@@ -10,7 +11,6 @@ loongarch_tcg_ss.add(files( + 'fpu_helper.c', + 'op_helper.c', + 'translate.c', +- 'gdbstub.c', + 'vec_helper.c', + )) + loongarch_tcg_ss.add(zlib) +-- +1.8.3.1 + diff --git a/0151-target-loongarch-move-translate-modules-to-tcg.patch b/0151-target-loongarch-move-translate-modules-to-tcg.patch new file mode 100644 index 0000000000000000000000000000000000000000..e6366aedb407f97156cfb6caf516eeaf56ff73d3 --- /dev/null +++ b/0151-target-loongarch-move-translate-modules-to-tcg.patch @@ -0,0 +1,27885 @@ +From 9af01b78fb73fa1d5ac165d089e32a2de6483497 Mon Sep 17 00:00:00 2001 +From: Song Gao +Date: Tue, 2 Jan 2024 10:02:00 +0800 +Subject: [PATCH 161/293] target/loongarch: move translate modules to tcg/ +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Introduce the target/loongarch/tcg directory. Its purpose is to hold the TCG +code that is selected by CONFIG_TCG + +Reviewed-by: Philippe Mathieu-Daudé +Signed-off-by: Song Gao +Message-Id: <20240102020200.3462097-2-gaosong@loongson.cn> +--- + target/loongarch/constant_timer.c | 64 - + target/loongarch/csr_helper.c | 97 - + target/loongarch/fpu_helper.c | 879 ---- + target/loongarch/insn_trans/trans_arith.c.inc | 304 -- + target/loongarch/insn_trans/trans_atomic.c.inc | 111 - + target/loongarch/insn_trans/trans_bit.c.inc | 208 - + target/loongarch/insn_trans/trans_branch.c.inc | 84 - + target/loongarch/insn_trans/trans_extra.c.inc | 107 - + target/loongarch/insn_trans/trans_farith.c.inc | 207 - + target/loongarch/insn_trans/trans_fcmp.c.inc | 72 - + target/loongarch/insn_trans/trans_fcnv.c.inc | 33 - + target/loongarch/insn_trans/trans_fmemory.c.inc | 158 - + target/loongarch/insn_trans/trans_fmov.c.inc | 224 - + target/loongarch/insn_trans/trans_memory.c.inc | 194 - + target/loongarch/insn_trans/trans_privileged.c.inc | 498 -- + target/loongarch/insn_trans/trans_shift.c.inc | 99 - + target/loongarch/insn_trans/trans_vec.c.inc | 5511 -------------------- + target/loongarch/iocsr_helper.c | 68 - + target/loongarch/meson.build | 15 +- + target/loongarch/op_helper.c | 140 - + target/loongarch/tcg/constant_timer.c | 64 + + target/loongarch/tcg/csr_helper.c | 97 + + target/loongarch/tcg/fpu_helper.c | 879 ++++ + target/loongarch/tcg/insn_trans/trans_arith.c.inc | 304 ++ + target/loongarch/tcg/insn_trans/trans_atomic.c.inc | 111 + + target/loongarch/tcg/insn_trans/trans_bit.c.inc | 208 + + target/loongarch/tcg/insn_trans/trans_branch.c.inc | 84 + + target/loongarch/tcg/insn_trans/trans_extra.c.inc | 107 + + target/loongarch/tcg/insn_trans/trans_farith.c.inc | 207 + + target/loongarch/tcg/insn_trans/trans_fcmp.c.inc | 72 + + target/loongarch/tcg/insn_trans/trans_fcnv.c.inc | 33 + + .../loongarch/tcg/insn_trans/trans_fmemory.c.inc | 158 + + target/loongarch/tcg/insn_trans/trans_fmov.c.inc | 224 + + target/loongarch/tcg/insn_trans/trans_memory.c.inc | 194 + + .../tcg/insn_trans/trans_privileged.c.inc | 498 ++ + target/loongarch/tcg/insn_trans/trans_shift.c.inc | 99 + + target/loongarch/tcg/insn_trans/trans_vec.c.inc | 5511 ++++++++++++++++++++ + target/loongarch/tcg/iocsr_helper.c | 68 + + target/loongarch/tcg/meson.build | 19 + + target/loongarch/tcg/op_helper.c | 140 + + target/loongarch/tcg/tlb_helper.c | 803 +++ + target/loongarch/tcg/translate.c | 370 ++ + target/loongarch/tcg/vec_helper.c | 3494 +++++++++++++ + target/loongarch/tlb_helper.c | 803 --- + target/loongarch/translate.c | 370 -- + target/loongarch/vec_helper.c | 3494 ------------- + 46 files changed, 13745 insertions(+), 13739 deletions(-) + delete mode 100644 target/loongarch/constant_timer.c + delete mode 100644 target/loongarch/csr_helper.c + delete mode 100644 target/loongarch/fpu_helper.c + delete mode 100644 target/loongarch/insn_trans/trans_arith.c.inc + delete mode 100644 target/loongarch/insn_trans/trans_atomic.c.inc + delete mode 100644 target/loongarch/insn_trans/trans_bit.c.inc + delete mode 100644 target/loongarch/insn_trans/trans_branch.c.inc + delete mode 100644 target/loongarch/insn_trans/trans_extra.c.inc + delete mode 100644 target/loongarch/insn_trans/trans_farith.c.inc + delete mode 100644 target/loongarch/insn_trans/trans_fcmp.c.inc + delete mode 100644 target/loongarch/insn_trans/trans_fcnv.c.inc + delete mode 100644 target/loongarch/insn_trans/trans_fmemory.c.inc + delete mode 100644 target/loongarch/insn_trans/trans_fmov.c.inc + delete mode 100644 target/loongarch/insn_trans/trans_memory.c.inc + delete mode 100644 target/loongarch/insn_trans/trans_privileged.c.inc + delete mode 100644 target/loongarch/insn_trans/trans_shift.c.inc + delete mode 100644 target/loongarch/insn_trans/trans_vec.c.inc + delete mode 100644 target/loongarch/iocsr_helper.c + delete mode 100644 target/loongarch/op_helper.c + create mode 100644 target/loongarch/tcg/constant_timer.c + create mode 100644 target/loongarch/tcg/csr_helper.c + create mode 100644 target/loongarch/tcg/fpu_helper.c + create mode 100644 target/loongarch/tcg/insn_trans/trans_arith.c.inc + create mode 100644 target/loongarch/tcg/insn_trans/trans_atomic.c.inc + create mode 100644 target/loongarch/tcg/insn_trans/trans_bit.c.inc + create mode 100644 target/loongarch/tcg/insn_trans/trans_branch.c.inc + create mode 100644 target/loongarch/tcg/insn_trans/trans_extra.c.inc + create mode 100644 target/loongarch/tcg/insn_trans/trans_farith.c.inc + create mode 100644 target/loongarch/tcg/insn_trans/trans_fcmp.c.inc + create mode 100644 target/loongarch/tcg/insn_trans/trans_fcnv.c.inc + create mode 100644 target/loongarch/tcg/insn_trans/trans_fmemory.c.inc + create mode 100644 target/loongarch/tcg/insn_trans/trans_fmov.c.inc + create mode 100644 target/loongarch/tcg/insn_trans/trans_memory.c.inc + create mode 100644 target/loongarch/tcg/insn_trans/trans_privileged.c.inc + create mode 100644 target/loongarch/tcg/insn_trans/trans_shift.c.inc + create mode 100644 target/loongarch/tcg/insn_trans/trans_vec.c.inc + create mode 100644 target/loongarch/tcg/iocsr_helper.c + create mode 100644 target/loongarch/tcg/meson.build + create mode 100644 target/loongarch/tcg/op_helper.c + create mode 100644 target/loongarch/tcg/tlb_helper.c + create mode 100644 target/loongarch/tcg/translate.c + create mode 100644 target/loongarch/tcg/vec_helper.c + delete mode 100644 target/loongarch/tlb_helper.c + delete mode 100644 target/loongarch/translate.c + delete mode 100644 target/loongarch/vec_helper.c + +diff --git a/target/loongarch/constant_timer.c b/target/loongarch/constant_timer.c +deleted file mode 100644 +index 1851f53..0000000 +--- a/target/loongarch/constant_timer.c ++++ /dev/null +@@ -1,64 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * QEMU LoongArch constant timer support +- * +- * Copyright (c) 2021 Loongson Technology Corporation Limited +- */ +- +-#include "qemu/osdep.h" +-#include "qemu/timer.h" +-#include "cpu.h" +-#include "internals.h" +-#include "cpu-csr.h" +- +-#define TIMER_PERIOD 10 /* 10 ns period for 100 MHz frequency */ +-#define CONSTANT_TIMER_TICK_MASK 0xfffffffffffcUL +-#define CONSTANT_TIMER_ENABLE 0x1UL +- +-uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu) +-{ +- return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD; +-} +- +-uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu) +-{ +- uint64_t now, expire; +- +- now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); +- expire = timer_expire_time_ns(&cpu->timer); +- +- return (expire - now) / TIMER_PERIOD; +-} +- +-void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu, +- uint64_t value) +-{ +- CPULoongArchState *env = &cpu->env; +- uint64_t now, next; +- +- env->CSR_TCFG = value; +- if (value & CONSTANT_TIMER_ENABLE) { +- now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); +- next = now + (value & CONSTANT_TIMER_TICK_MASK) * TIMER_PERIOD; +- timer_mod(&cpu->timer, next); +- } else { +- timer_del(&cpu->timer); +- } +-} +- +-void loongarch_constant_timer_cb(void *opaque) +-{ +- LoongArchCPU *cpu = opaque; +- CPULoongArchState *env = &cpu->env; +- uint64_t now, next; +- +- if (FIELD_EX64(env->CSR_TCFG, CSR_TCFG, PERIODIC)) { +- now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); +- next = now + (env->CSR_TCFG & CONSTANT_TIMER_TICK_MASK) * TIMER_PERIOD; +- timer_mod(&cpu->timer, next); +- } else { +- env->CSR_TCFG = FIELD_DP64(env->CSR_TCFG, CSR_TCFG, EN, 0); +- } +- +- loongarch_cpu_set_irq(opaque, IRQ_TIMER, 1); +-} +diff --git a/target/loongarch/csr_helper.c b/target/loongarch/csr_helper.c +deleted file mode 100644 +index 5534155..0000000 +--- a/target/loongarch/csr_helper.c ++++ /dev/null +@@ -1,97 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * LoongArch emulation helpers for CSRs +- * +- * Copyright (c) 2021 Loongson Technology Corporation Limited +- */ +- +-#include "qemu/osdep.h" +-#include "qemu/main-loop.h" +-#include "cpu.h" +-#include "internals.h" +-#include "qemu/host-utils.h" +-#include "exec/helper-proto.h" +-#include "exec/exec-all.h" +-#include "exec/cpu_ldst.h" +-#include "hw/irq.h" +-#include "cpu-csr.h" +- +-target_ulong helper_csrrd_pgd(CPULoongArchState *env) +-{ +- int64_t v; +- +- if (env->CSR_TLBRERA & 0x1) { +- v = env->CSR_TLBRBADV; +- } else { +- v = env->CSR_BADV; +- } +- +- if ((v >> 63) & 0x1) { +- v = env->CSR_PGDH; +- } else { +- v = env->CSR_PGDL; +- } +- +- return v; +-} +- +-target_ulong helper_csrrd_cpuid(CPULoongArchState *env) +-{ +- LoongArchCPU *lac = env_archcpu(env); +- +- env->CSR_CPUID = CPU(lac)->cpu_index; +- +- return env->CSR_CPUID; +-} +- +-target_ulong helper_csrrd_tval(CPULoongArchState *env) +-{ +- LoongArchCPU *cpu = env_archcpu(env); +- +- return cpu_loongarch_get_constant_timer_ticks(cpu); +-} +- +-target_ulong helper_csrwr_estat(CPULoongArchState *env, target_ulong val) +-{ +- int64_t old_v = env->CSR_ESTAT; +- +- /* Only IS[1:0] can be written */ +- env->CSR_ESTAT = deposit64(env->CSR_ESTAT, 0, 2, val); +- +- return old_v; +-} +- +-target_ulong helper_csrwr_asid(CPULoongArchState *env, target_ulong val) +-{ +- int64_t old_v = env->CSR_ASID; +- +- /* Only ASID filed of CSR_ASID can be written */ +- env->CSR_ASID = deposit64(env->CSR_ASID, 0, 10, val); +- if (old_v != env->CSR_ASID) { +- tlb_flush(env_cpu(env)); +- } +- return old_v; +-} +- +-target_ulong helper_csrwr_tcfg(CPULoongArchState *env, target_ulong val) +-{ +- LoongArchCPU *cpu = env_archcpu(env); +- int64_t old_v = env->CSR_TCFG; +- +- cpu_loongarch_store_constant_timer_config(cpu, val); +- +- return old_v; +-} +- +-target_ulong helper_csrwr_ticlr(CPULoongArchState *env, target_ulong val) +-{ +- LoongArchCPU *cpu = env_archcpu(env); +- int64_t old_v = 0; +- +- if (val & 0x1) { +- qemu_mutex_lock_iothread(); +- loongarch_cpu_set_irq(cpu, IRQ_TIMER, 0); +- qemu_mutex_unlock_iothread(); +- } +- return old_v; +-} +diff --git a/target/loongarch/fpu_helper.c b/target/loongarch/fpu_helper.c +deleted file mode 100644 +index f6753c5..0000000 +--- a/target/loongarch/fpu_helper.c ++++ /dev/null +@@ -1,879 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * LoongArch float point emulation helpers for QEMU +- * +- * Copyright (c) 2021 Loongson Technology Corporation Limited +- */ +- +-#include "qemu/osdep.h" +-#include "cpu.h" +-#include "exec/helper-proto.h" +-#include "exec/exec-all.h" +-#include "exec/cpu_ldst.h" +-#include "fpu/softfloat.h" +-#include "internals.h" +- +-static inline uint64_t nanbox_s(float32 fp) +-{ +- return fp | MAKE_64BIT_MASK(32, 32); +-} +- +-/* Convert loongarch rounding mode in fcsr0 to IEEE library */ +-static const FloatRoundMode ieee_rm[4] = { +- float_round_nearest_even, +- float_round_to_zero, +- float_round_up, +- float_round_down +-}; +- +-void restore_fp_status(CPULoongArchState *env) +-{ +- set_float_rounding_mode(ieee_rm[(env->fcsr0 >> FCSR0_RM) & 0x3], +- &env->fp_status); +- set_flush_to_zero(0, &env->fp_status); +-} +- +-int ieee_ex_to_loongarch(int xcpt) +-{ +- int ret = 0; +- if (xcpt & float_flag_invalid) { +- ret |= FP_INVALID; +- } +- if (xcpt & float_flag_overflow) { +- ret |= FP_OVERFLOW; +- } +- if (xcpt & float_flag_underflow) { +- ret |= FP_UNDERFLOW; +- } +- if (xcpt & float_flag_divbyzero) { +- ret |= FP_DIV0; +- } +- if (xcpt & float_flag_inexact) { +- ret |= FP_INEXACT; +- } +- return ret; +-} +- +-static void update_fcsr0_mask(CPULoongArchState *env, uintptr_t pc, int mask) +-{ +- int flags = get_float_exception_flags(&env->fp_status); +- +- set_float_exception_flags(0, &env->fp_status); +- +- flags &= ~mask; +- +- if (!flags) { +- SET_FP_CAUSE(env->fcsr0, flags); +- return; +- } else { +- flags = ieee_ex_to_loongarch(flags); +- SET_FP_CAUSE(env->fcsr0, flags); +- } +- +- if (GET_FP_ENABLES(env->fcsr0) & flags) { +- do_raise_exception(env, EXCCODE_FPE, pc); +- } else { +- UPDATE_FP_FLAGS(env->fcsr0, flags); +- } +-} +- +-static void update_fcsr0(CPULoongArchState *env, uintptr_t pc) +-{ +- update_fcsr0_mask(env, pc, 0); +-} +- +-uint64_t helper_fadd_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) +-{ +- uint64_t fd; +- +- fd = nanbox_s(float32_add((uint32_t)fj, (uint32_t)fk, &env->fp_status)); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_fadd_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) +-{ +- uint64_t fd; +- +- fd = float64_add(fj, fk, &env->fp_status); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_fsub_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) +-{ +- uint64_t fd; +- +- fd = nanbox_s(float32_sub((uint32_t)fj, (uint32_t)fk, &env->fp_status)); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_fsub_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) +-{ +- uint64_t fd; +- +- fd = float64_sub(fj, fk, &env->fp_status); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_fmul_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) +-{ +- uint64_t fd; +- +- fd = nanbox_s(float32_mul((uint32_t)fj, (uint32_t)fk, &env->fp_status)); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_fmul_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) +-{ +- uint64_t fd; +- +- fd = float64_mul(fj, fk, &env->fp_status); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_fdiv_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) +-{ +- uint64_t fd; +- +- fd = nanbox_s(float32_div((uint32_t)fj, (uint32_t)fk, &env->fp_status)); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_fdiv_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) +-{ +- uint64_t fd; +- +- fd = float64_div(fj, fk, &env->fp_status); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_fmax_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) +-{ +- uint64_t fd; +- +- fd = nanbox_s(float32_maxnum((uint32_t)fj, (uint32_t)fk, &env->fp_status)); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_fmax_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) +-{ +- uint64_t fd; +- +- fd = float64_maxnum(fj, fk, &env->fp_status); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_fmin_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) +-{ +- uint64_t fd; +- +- fd = nanbox_s(float32_minnum((uint32_t)fj, (uint32_t)fk, &env->fp_status)); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_fmin_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) +-{ +- uint64_t fd; +- +- fd = float64_minnum(fj, fk, &env->fp_status); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_fmaxa_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) +-{ +- uint64_t fd; +- +- fd = nanbox_s(float32_maxnummag((uint32_t)fj, +- (uint32_t)fk, &env->fp_status)); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_fmaxa_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) +-{ +- uint64_t fd; +- +- fd = float64_maxnummag(fj, fk, &env->fp_status); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_fmina_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) +-{ +- uint64_t fd; +- +- fd = nanbox_s(float32_minnummag((uint32_t)fj, +- (uint32_t)fk, &env->fp_status)); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_fmina_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) +-{ +- uint64_t fd; +- +- fd = float64_minnummag(fj, fk, &env->fp_status); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_fscaleb_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) +-{ +- uint64_t fd; +- int32_t n = (int32_t)fk; +- +- fd = nanbox_s(float32_scalbn((uint32_t)fj, +- n > 0x200 ? 0x200 : +- n < -0x200 ? -0x200 : n, +- &env->fp_status)); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_fscaleb_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) +-{ +- uint64_t fd; +- int64_t n = (int64_t)fk; +- +- fd = float64_scalbn(fj, +- n > 0x1000 ? 0x1000 : +- n < -0x1000 ? -0x1000 : n, +- &env->fp_status); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_fsqrt_s(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- +- fd = nanbox_s(float32_sqrt((uint32_t)fj, &env->fp_status)); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_fsqrt_d(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- +- fd = float64_sqrt(fj, &env->fp_status); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_frecip_s(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- +- fd = nanbox_s(float32_div(float32_one, (uint32_t)fj, &env->fp_status)); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_frecip_d(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- +- fd = float64_div(float64_one, fj, &env->fp_status); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_frsqrt_s(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- uint32_t fp; +- +- fp = float32_sqrt((uint32_t)fj, &env->fp_status); +- fd = nanbox_s(float32_div(float32_one, fp, &env->fp_status)); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_frsqrt_d(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fp, fd; +- +- fp = float64_sqrt(fj, &env->fp_status); +- fd = float64_div(float64_one, fp, &env->fp_status); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_flogb_s(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- uint32_t fp; +- float_status *status = &env->fp_status; +- FloatRoundMode old_mode = get_float_rounding_mode(status); +- +- set_float_rounding_mode(float_round_down, status); +- fp = float32_log2((uint32_t)fj, status); +- fd = nanbox_s(float32_round_to_int(fp, status)); +- set_float_rounding_mode(old_mode, status); +- update_fcsr0_mask(env, GETPC(), float_flag_inexact); +- return fd; +-} +- +-uint64_t helper_flogb_d(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- float_status *status = &env->fp_status; +- FloatRoundMode old_mode = get_float_rounding_mode(status); +- +- set_float_rounding_mode(float_round_down, status); +- fd = float64_log2(fj, status); +- fd = float64_round_to_int(fd, status); +- set_float_rounding_mode(old_mode, status); +- update_fcsr0_mask(env, GETPC(), float_flag_inexact); +- return fd; +-} +- +-uint64_t helper_fclass_s(CPULoongArchState *env, uint64_t fj) +-{ +- float32 f = fj; +- bool sign = float32_is_neg(f); +- +- if (float32_is_infinity(f)) { +- return sign ? 1 << 2 : 1 << 6; +- } else if (float32_is_zero(f)) { +- return sign ? 1 << 5 : 1 << 9; +- } else if (float32_is_zero_or_denormal(f)) { +- return sign ? 1 << 4 : 1 << 8; +- } else if (float32_is_any_nan(f)) { +- float_status s = { }; /* for snan_bit_is_one */ +- return float32_is_quiet_nan(f, &s) ? 1 << 1 : 1 << 0; +- } else { +- return sign ? 1 << 3 : 1 << 7; +- } +-} +- +-uint64_t helper_fclass_d(CPULoongArchState *env, uint64_t fj) +-{ +- float64 f = fj; +- bool sign = float64_is_neg(f); +- +- if (float64_is_infinity(f)) { +- return sign ? 1 << 2 : 1 << 6; +- } else if (float64_is_zero(f)) { +- return sign ? 1 << 5 : 1 << 9; +- } else if (float64_is_zero_or_denormal(f)) { +- return sign ? 1 << 4 : 1 << 8; +- } else if (float64_is_any_nan(f)) { +- float_status s = { }; /* for snan_bit_is_one */ +- return float64_is_quiet_nan(f, &s) ? 1 << 1 : 1 << 0; +- } else { +- return sign ? 1 << 3 : 1 << 7; +- } +-} +- +-uint64_t helper_fmuladd_s(CPULoongArchState *env, uint64_t fj, +- uint64_t fk, uint64_t fa, uint32_t flag) +-{ +- uint64_t fd; +- +- fd = nanbox_s(float32_muladd((uint32_t)fj, (uint32_t)fk, +- (uint32_t)fa, flag, &env->fp_status)); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_fmuladd_d(CPULoongArchState *env, uint64_t fj, +- uint64_t fk, uint64_t fa, uint32_t flag) +-{ +- uint64_t fd; +- +- fd = float64_muladd(fj, fk, fa, flag, &env->fp_status); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-static uint64_t fcmp_common(CPULoongArchState *env, FloatRelation cmp, +- uint32_t flags) +-{ +- bool ret; +- +- switch (cmp) { +- case float_relation_less: +- ret = (flags & FCMP_LT); +- break; +- case float_relation_equal: +- ret = (flags & FCMP_EQ); +- break; +- case float_relation_greater: +- ret = (flags & FCMP_GT); +- break; +- case float_relation_unordered: +- ret = (flags & FCMP_UN); +- break; +- default: +- g_assert_not_reached(); +- } +- update_fcsr0(env, GETPC()); +- +- return ret; +-} +- +-/* fcmp_cXXX_s */ +-uint64_t helper_fcmp_c_s(CPULoongArchState *env, uint64_t fj, +- uint64_t fk, uint32_t flags) +-{ +- FloatRelation cmp = float32_compare_quiet((uint32_t)fj, +- (uint32_t)fk, &env->fp_status); +- return fcmp_common(env, cmp, flags); +-} +- +-/* fcmp_sXXX_s */ +-uint64_t helper_fcmp_s_s(CPULoongArchState *env, uint64_t fj, +- uint64_t fk, uint32_t flags) +-{ +- FloatRelation cmp = float32_compare((uint32_t)fj, +- (uint32_t)fk, &env->fp_status); +- return fcmp_common(env, cmp, flags); +-} +- +-/* fcmp_cXXX_d */ +-uint64_t helper_fcmp_c_d(CPULoongArchState *env, uint64_t fj, +- uint64_t fk, uint32_t flags) +-{ +- FloatRelation cmp = float64_compare_quiet(fj, fk, &env->fp_status); +- return fcmp_common(env, cmp, flags); +-} +- +-/* fcmp_sXXX_d */ +-uint64_t helper_fcmp_s_d(CPULoongArchState *env, uint64_t fj, +- uint64_t fk, uint32_t flags) +-{ +- FloatRelation cmp = float64_compare(fj, fk, &env->fp_status); +- return fcmp_common(env, cmp, flags); +-} +- +-/* floating point conversion */ +-uint64_t helper_fcvt_s_d(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- +- fd = nanbox_s(float64_to_float32(fj, &env->fp_status)); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_fcvt_d_s(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- +- fd = float32_to_float64((uint32_t)fj, &env->fp_status); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_ffint_s_w(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- +- fd = nanbox_s(int32_to_float32((int32_t)fj, &env->fp_status)); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_ffint_s_l(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- +- fd = nanbox_s(int64_to_float32(fj, &env->fp_status)); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_ffint_d_w(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- +- fd = int32_to_float64((int32_t)fj, &env->fp_status); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_ffint_d_l(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- +- fd = int64_to_float64(fj, &env->fp_status); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_frint_s(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- +- fd = (uint64_t)(float32_round_to_int((uint32_t)fj, &env->fp_status)); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_frint_d(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- +- fd = float64_round_to_int(fj, &env->fp_status); +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_ftintrm_l_d(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); +- +- set_float_rounding_mode(float_round_down, &env->fp_status); +- fd = float64_to_int64(fj, &env->fp_status); +- set_float_rounding_mode(old_mode, &env->fp_status); +- +- if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { +- if (float64_is_any_nan(fj)) { +- fd = 0; +- } +- } +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_ftintrm_l_s(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); +- +- set_float_rounding_mode(float_round_down, &env->fp_status); +- fd = float32_to_int64((uint32_t)fj, &env->fp_status); +- set_float_rounding_mode(old_mode, &env->fp_status); +- +- if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { +- if (float32_is_any_nan((uint32_t)fj)) { +- fd = 0; +- } +- } +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_ftintrm_w_d(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); +- +- set_float_rounding_mode(float_round_down, &env->fp_status); +- fd = (uint64_t)float64_to_int32(fj, &env->fp_status); +- set_float_rounding_mode(old_mode, &env->fp_status); +- +- if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { +- if (float64_is_any_nan(fj)) { +- fd = 0; +- } +- } +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_ftintrm_w_s(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); +- +- set_float_rounding_mode(float_round_down, &env->fp_status); +- fd = (uint64_t)float32_to_int32((uint32_t)fj, &env->fp_status); +- set_float_rounding_mode(old_mode, &env->fp_status); +- +- if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { +- if (float32_is_any_nan((uint32_t)fj)) { +- fd = 0; +- } +- } +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_ftintrp_l_d(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); +- +- set_float_rounding_mode(float_round_up, &env->fp_status); +- fd = float64_to_int64(fj, &env->fp_status); +- set_float_rounding_mode(old_mode, &env->fp_status); +- +- if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { +- if (float64_is_any_nan(fj)) { +- fd = 0; +- } +- } +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_ftintrp_l_s(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); +- +- set_float_rounding_mode(float_round_up, &env->fp_status); +- fd = float32_to_int64((uint32_t)fj, &env->fp_status); +- set_float_rounding_mode(old_mode, &env->fp_status); +- +- if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { +- if (float32_is_any_nan((uint32_t)fj)) { +- fd = 0; +- } +- } +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_ftintrp_w_d(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); +- +- set_float_rounding_mode(float_round_up, &env->fp_status); +- fd = (uint64_t)float64_to_int32(fj, &env->fp_status); +- set_float_rounding_mode(old_mode, &env->fp_status); +- +- if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { +- if (float64_is_any_nan(fj)) { +- fd = 0; +- } +- } +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_ftintrp_w_s(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); +- +- set_float_rounding_mode(float_round_up, &env->fp_status); +- fd = (uint64_t)float32_to_int32((uint32_t)fj, &env->fp_status); +- set_float_rounding_mode(old_mode, &env->fp_status); +- +- if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { +- if (float32_is_any_nan((uint32_t)fj)) { +- fd = 0; +- } +- } +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_ftintrz_l_d(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); +- +- fd = float64_to_int64_round_to_zero(fj, &env->fp_status); +- set_float_rounding_mode(old_mode, &env->fp_status); +- +- if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { +- if (float64_is_any_nan(fj)) { +- fd = 0; +- } +- } +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_ftintrz_l_s(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); +- +- fd = float32_to_int64_round_to_zero((uint32_t)fj, &env->fp_status); +- set_float_rounding_mode(old_mode, &env->fp_status); +- +- if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { +- if (float32_is_any_nan((uint32_t)fj)) { +- fd = 0; +- } +- } +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_ftintrz_w_d(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); +- +- fd = (uint64_t)float64_to_int32_round_to_zero(fj, &env->fp_status); +- set_float_rounding_mode(old_mode, &env->fp_status); +- +- if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { +- if (float64_is_any_nan(fj)) { +- fd = 0; +- } +- } +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_ftintrz_w_s(CPULoongArchState *env, uint64_t fj) +-{ +- uint32_t fd; +- FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); +- +- fd = float32_to_int32_round_to_zero((uint32_t)fj, &env->fp_status); +- set_float_rounding_mode(old_mode, &env->fp_status); +- +- if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { +- if (float32_is_any_nan((uint32_t)fj)) { +- fd = 0; +- } +- } +- update_fcsr0(env, GETPC()); +- return (uint64_t)fd; +-} +- +-uint64_t helper_ftintrne_l_d(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); +- +- set_float_rounding_mode(float_round_nearest_even, &env->fp_status); +- fd = float64_to_int64(fj, &env->fp_status); +- set_float_rounding_mode(old_mode, &env->fp_status); +- +- if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { +- if (float64_is_any_nan(fj)) { +- fd = 0; +- } +- } +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_ftintrne_l_s(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); +- +- set_float_rounding_mode(float_round_nearest_even, &env->fp_status); +- fd = float32_to_int64((uint32_t)fj, &env->fp_status); +- set_float_rounding_mode(old_mode, &env->fp_status); +- +- if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { +- if (float32_is_any_nan((uint32_t)fj)) { +- fd = 0; +- } +- } +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_ftintrne_w_d(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); +- +- set_float_rounding_mode(float_round_nearest_even, &env->fp_status); +- fd = (uint64_t)float64_to_int32(fj, &env->fp_status); +- set_float_rounding_mode(old_mode, &env->fp_status); +- +- if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { +- if (float64_is_any_nan(fj)) { +- fd = 0; +- } +- } +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_ftintrne_w_s(CPULoongArchState *env, uint64_t fj) +-{ +- uint32_t fd; +- FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); +- +- set_float_rounding_mode(float_round_nearest_even, &env->fp_status); +- fd = float32_to_int32((uint32_t)fj, &env->fp_status); +- set_float_rounding_mode(old_mode, &env->fp_status); +- +- if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { +- if (float32_is_any_nan((uint32_t)fj)) { +- fd = 0; +- } +- } +- update_fcsr0(env, GETPC()); +- return (uint64_t)fd; +-} +- +-uint64_t helper_ftint_l_d(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- +- fd = float64_to_int64(fj, &env->fp_status); +- if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { +- if (float64_is_any_nan(fj)) { +- fd = 0; +- } +- } +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_ftint_l_s(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- +- fd = float32_to_int64((uint32_t)fj, &env->fp_status); +- if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { +- if (float32_is_any_nan((uint32_t)fj)) { +- fd = 0; +- } +- } +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_ftint_w_s(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- +- fd = (uint64_t)float32_to_int32((uint32_t)fj, &env->fp_status); +- if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { +- if (float32_is_any_nan((uint32_t)fj)) { +- fd = 0; +- } +- } +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-uint64_t helper_ftint_w_d(CPULoongArchState *env, uint64_t fj) +-{ +- uint64_t fd; +- +- fd = (uint64_t)float64_to_int32(fj, &env->fp_status); +- if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { +- if (float64_is_any_nan(fj)) { +- fd = 0; +- } +- } +- update_fcsr0(env, GETPC()); +- return fd; +-} +- +-void helper_set_rounding_mode(CPULoongArchState *env) +-{ +- set_float_rounding_mode(ieee_rm[(env->fcsr0 >> FCSR0_RM) & 0x3], +- &env->fp_status); +-} +diff --git a/target/loongarch/insn_trans/trans_arith.c.inc b/target/loongarch/insn_trans/trans_arith.c.inc +deleted file mode 100644 +index 2be057e..0000000 +--- a/target/loongarch/insn_trans/trans_arith.c.inc ++++ /dev/null +@@ -1,304 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * Copyright (c) 2021 Loongson Technology Corporation Limited +- */ +- +-static bool gen_rrr(DisasContext *ctx, arg_rrr *a, +- DisasExtend src1_ext, DisasExtend src2_ext, +- DisasExtend dst_ext, void (*func)(TCGv, TCGv, TCGv)) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, dst_ext); +- TCGv src1 = gpr_src(ctx, a->rj, src1_ext); +- TCGv src2 = gpr_src(ctx, a->rk, src2_ext); +- +- func(dest, src1, src2); +- gen_set_gpr(a->rd, dest, dst_ext); +- +- return true; +-} +- +-static bool gen_rri_v(DisasContext *ctx, arg_rr_i *a, +- DisasExtend src_ext, DisasExtend dst_ext, +- void (*func)(TCGv, TCGv, TCGv)) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, dst_ext); +- TCGv src1 = gpr_src(ctx, a->rj, src_ext); +- TCGv src2 = tcg_constant_tl(a->imm); +- +- func(dest, src1, src2); +- gen_set_gpr(a->rd, dest, dst_ext); +- +- return true; +-} +- +-static bool gen_rri_c(DisasContext *ctx, arg_rr_i *a, +- DisasExtend src_ext, DisasExtend dst_ext, +- void (*func)(TCGv, TCGv, target_long)) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, dst_ext); +- TCGv src1 = gpr_src(ctx, a->rj, src_ext); +- +- func(dest, src1, a->imm); +- gen_set_gpr(a->rd, dest, dst_ext); +- +- return true; +-} +- +-static bool gen_rrr_sa(DisasContext *ctx, arg_rrr_sa *a, +- DisasExtend src_ext, DisasExtend dst_ext, +- void (*func)(TCGv, TCGv, TCGv, target_long)) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, dst_ext); +- TCGv src1 = gpr_src(ctx, a->rj, src_ext); +- TCGv src2 = gpr_src(ctx, a->rk, src_ext); +- +- func(dest, src1, src2, a->sa); +- gen_set_gpr(a->rd, dest, dst_ext); +- +- return true; +-} +- +-static bool trans_lu12i_w(DisasContext *ctx, arg_lu12i_w *a) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); +- +- tcg_gen_movi_tl(dest, a->imm << 12); +- gen_set_gpr(a->rd, dest, EXT_NONE); +- +- return true; +-} +- +-static bool gen_pc(DisasContext *ctx, arg_r_i *a, +- target_ulong (*func)(target_ulong, int)) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); +- target_ulong addr = make_address_pc(ctx, func(ctx->base.pc_next, a->imm)); +- +- tcg_gen_movi_tl(dest, addr); +- gen_set_gpr(a->rd, dest, EXT_NONE); +- +- return true; +-} +- +-static void gen_slt(TCGv dest, TCGv src1, TCGv src2) +-{ +- tcg_gen_setcond_tl(TCG_COND_LT, dest, src1, src2); +-} +- +-static void gen_sltu(TCGv dest, TCGv src1, TCGv src2) +-{ +- tcg_gen_setcond_tl(TCG_COND_LTU, dest, src1, src2); +-} +- +-static void gen_mulh_w(TCGv dest, TCGv src1, TCGv src2) +-{ +- tcg_gen_mul_i64(dest, src1, src2); +- tcg_gen_sari_i64(dest, dest, 32); +-} +- +-static void gen_mulh_d(TCGv dest, TCGv src1, TCGv src2) +-{ +- TCGv discard = tcg_temp_new(); +- tcg_gen_muls2_tl(discard, dest, src1, src2); +-} +- +-static void gen_mulh_du(TCGv dest, TCGv src1, TCGv src2) +-{ +- TCGv discard = tcg_temp_new(); +- tcg_gen_mulu2_tl(discard, dest, src1, src2); +-} +- +-static void prep_divisor_d(TCGv ret, TCGv src1, TCGv src2) +-{ +- TCGv t0 = tcg_temp_new(); +- TCGv t1 = tcg_temp_new(); +- TCGv zero = tcg_constant_tl(0); +- +- /* +- * If min / -1, set the divisor to 1. +- * This avoids potential host overflow trap and produces min. +- * If x / 0, set the divisor to 1. +- * This avoids potential host overflow trap; +- * the required result is undefined. +- */ +- tcg_gen_setcondi_tl(TCG_COND_EQ, ret, src1, INT64_MIN); +- tcg_gen_setcondi_tl(TCG_COND_EQ, t0, src2, -1); +- tcg_gen_setcondi_tl(TCG_COND_EQ, t1, src2, 0); +- tcg_gen_and_tl(ret, ret, t0); +- tcg_gen_or_tl(ret, ret, t1); +- tcg_gen_movcond_tl(TCG_COND_NE, ret, ret, zero, ret, src2); +-} +- +-static void prep_divisor_du(TCGv ret, TCGv src2) +-{ +- TCGv zero = tcg_constant_tl(0); +- TCGv one = tcg_constant_tl(1); +- +- /* +- * If x / 0, set the divisor to 1. +- * This avoids potential host overflow trap; +- * the required result is undefined. +- */ +- tcg_gen_movcond_tl(TCG_COND_EQ, ret, src2, zero, one, src2); +-} +- +-static void gen_div_d(TCGv dest, TCGv src1, TCGv src2) +-{ +- TCGv t0 = tcg_temp_new(); +- prep_divisor_d(t0, src1, src2); +- tcg_gen_div_tl(dest, src1, t0); +-} +- +-static void gen_rem_d(TCGv dest, TCGv src1, TCGv src2) +-{ +- TCGv t0 = tcg_temp_new(); +- prep_divisor_d(t0, src1, src2); +- tcg_gen_rem_tl(dest, src1, t0); +-} +- +-static void gen_div_du(TCGv dest, TCGv src1, TCGv src2) +-{ +- TCGv t0 = tcg_temp_new(); +- prep_divisor_du(t0, src2); +- tcg_gen_divu_tl(dest, src1, t0); +-} +- +-static void gen_rem_du(TCGv dest, TCGv src1, TCGv src2) +-{ +- TCGv t0 = tcg_temp_new(); +- prep_divisor_du(t0, src2); +- tcg_gen_remu_tl(dest, src1, t0); +-} +- +-static void gen_div_w(TCGv dest, TCGv src1, TCGv src2) +-{ +- TCGv t0 = tcg_temp_new(); +- /* We need not check for integer overflow for div_w. */ +- prep_divisor_du(t0, src2); +- tcg_gen_div_tl(dest, src1, t0); +-} +- +-static void gen_rem_w(TCGv dest, TCGv src1, TCGv src2) +-{ +- TCGv t0 = tcg_temp_new(); +- /* We need not check for integer overflow for rem_w. */ +- prep_divisor_du(t0, src2); +- tcg_gen_rem_tl(dest, src1, t0); +-} +- +-static void gen_alsl(TCGv dest, TCGv src1, TCGv src2, target_long sa) +-{ +- TCGv t0 = tcg_temp_new(); +- tcg_gen_shli_tl(t0, src1, sa); +- tcg_gen_add_tl(dest, t0, src2); +-} +- +-static bool trans_lu32i_d(DisasContext *ctx, arg_lu32i_d *a) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); +- TCGv src1 = gpr_src(ctx, a->rd, EXT_NONE); +- TCGv src2 = tcg_constant_tl(a->imm); +- +- if (!avail_64(ctx)) { +- return false; +- } +- +- tcg_gen_deposit_tl(dest, src1, src2, 32, 32); +- gen_set_gpr(a->rd, dest, EXT_NONE); +- +- return true; +-} +- +-static bool trans_lu52i_d(DisasContext *ctx, arg_lu52i_d *a) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv src2 = tcg_constant_tl(a->imm); +- +- if (!avail_64(ctx)) { +- return false; +- } +- +- tcg_gen_deposit_tl(dest, src1, src2, 52, 12); +- gen_set_gpr(a->rd, dest, EXT_NONE); +- +- return true; +-} +- +-static target_ulong gen_pcaddi(target_ulong pc, int imm) +-{ +- return pc + (imm << 2); +-} +- +-static target_ulong gen_pcalau12i(target_ulong pc, int imm) +-{ +- return (pc + (imm << 12)) & ~0xfff; +-} +- +-static target_ulong gen_pcaddu12i(target_ulong pc, int imm) +-{ +- return pc + (imm << 12); +-} +- +-static target_ulong gen_pcaddu18i(target_ulong pc, int imm) +-{ +- return pc + ((target_ulong)(imm) << 18); +-} +- +-static bool trans_addu16i_d(DisasContext *ctx, arg_addu16i_d *a) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- +- if (!avail_64(ctx)) { +- return false; +- } +- +- tcg_gen_addi_tl(dest, src1, a->imm << 16); +- gen_set_gpr(a->rd, dest, EXT_NONE); +- +- return true; +-} +- +-TRANS(add_w, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_SIGN, tcg_gen_add_tl) +-TRANS(add_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_add_tl) +-TRANS(sub_w, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_SIGN, tcg_gen_sub_tl) +-TRANS(sub_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_sub_tl) +-TRANS(and, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_and_tl) +-TRANS(or, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_or_tl) +-TRANS(xor, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_xor_tl) +-TRANS(nor, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_nor_tl) +-TRANS(andn, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_andc_tl) +-TRANS(orn, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_orc_tl) +-TRANS(slt, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_slt) +-TRANS(sltu, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_sltu) +-TRANS(mul_w, ALL, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_SIGN, tcg_gen_mul_tl) +-TRANS(mul_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_mul_tl) +-TRANS(mulh_w, ALL, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_NONE, gen_mulh_w) +-TRANS(mulh_wu, ALL, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_NONE, gen_mulh_w) +-TRANS(mulh_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_mulh_d) +-TRANS(mulh_du, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_mulh_du) +-TRANS(mulw_d_w, 64, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_NONE, tcg_gen_mul_tl) +-TRANS(mulw_d_wu, 64, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_NONE, tcg_gen_mul_tl) +-TRANS(div_w, ALL, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_SIGN, gen_div_w) +-TRANS(mod_w, ALL, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_SIGN, gen_rem_w) +-TRANS(div_wu, ALL, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_SIGN, gen_div_du) +-TRANS(mod_wu, ALL, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_SIGN, gen_rem_du) +-TRANS(div_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_div_d) +-TRANS(mod_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_rem_d) +-TRANS(div_du, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_div_du) +-TRANS(mod_du, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_rem_du) +-TRANS(slti, ALL, gen_rri_v, EXT_NONE, EXT_NONE, gen_slt) +-TRANS(sltui, ALL, gen_rri_v, EXT_NONE, EXT_NONE, gen_sltu) +-TRANS(addi_w, ALL, gen_rri_c, EXT_NONE, EXT_SIGN, tcg_gen_addi_tl) +-TRANS(addi_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_addi_tl) +-TRANS(alsl_w, ALL, gen_rrr_sa, EXT_NONE, EXT_SIGN, gen_alsl) +-TRANS(alsl_wu, 64, gen_rrr_sa, EXT_NONE, EXT_ZERO, gen_alsl) +-TRANS(alsl_d, 64, gen_rrr_sa, EXT_NONE, EXT_NONE, gen_alsl) +-TRANS(pcaddi, ALL, gen_pc, gen_pcaddi) +-TRANS(pcalau12i, ALL, gen_pc, gen_pcalau12i) +-TRANS(pcaddu12i, ALL, gen_pc, gen_pcaddu12i) +-TRANS(pcaddu18i, 64, gen_pc, gen_pcaddu18i) +-TRANS(andi, ALL, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_andi_tl) +-TRANS(ori, ALL, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_ori_tl) +-TRANS(xori, ALL, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_xori_tl) +diff --git a/target/loongarch/insn_trans/trans_atomic.c.inc b/target/loongarch/insn_trans/trans_atomic.c.inc +deleted file mode 100644 +index 80c2e28..0000000 +--- a/target/loongarch/insn_trans/trans_atomic.c.inc ++++ /dev/null +@@ -1,111 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * Copyright (c) 2021 Loongson Technology Corporation Limited +- */ +- +-static bool gen_ll(DisasContext *ctx, arg_rr_i *a, MemOp mop) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv t0 = make_address_i(ctx, src1, a->imm); +- +- tcg_gen_qemu_ld_i64(dest, t0, ctx->mem_idx, mop); +- tcg_gen_st_tl(t0, tcg_env, offsetof(CPULoongArchState, lladdr)); +- tcg_gen_st_tl(dest, tcg_env, offsetof(CPULoongArchState, llval)); +- gen_set_gpr(a->rd, dest, EXT_NONE); +- +- return true; +-} +- +-static bool gen_sc(DisasContext *ctx, arg_rr_i *a, MemOp mop) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv src2 = gpr_src(ctx, a->rd, EXT_NONE); +- TCGv t0 = tcg_temp_new(); +- TCGv val = tcg_temp_new(); +- +- TCGLabel *l1 = gen_new_label(); +- TCGLabel *done = gen_new_label(); +- +- tcg_gen_addi_tl(t0, src1, a->imm); +- tcg_gen_brcond_tl(TCG_COND_EQ, t0, cpu_lladdr, l1); +- tcg_gen_movi_tl(dest, 0); +- tcg_gen_br(done); +- +- gen_set_label(l1); +- tcg_gen_mov_tl(val, src2); +- /* generate cmpxchg */ +- tcg_gen_atomic_cmpxchg_tl(t0, cpu_lladdr, cpu_llval, +- val, ctx->mem_idx, mop); +- tcg_gen_setcond_tl(TCG_COND_EQ, dest, t0, cpu_llval); +- gen_set_label(done); +- gen_set_gpr(a->rd, dest, EXT_NONE); +- +- return true; +-} +- +-static bool gen_am(DisasContext *ctx, arg_rrr *a, +- void (*func)(TCGv, TCGv, TCGv, TCGArg, MemOp), +- MemOp mop) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); +- TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv val = gpr_src(ctx, a->rk, EXT_NONE); +- +- if (a->rd != 0 && (a->rj == a->rd || a->rk == a->rd)) { +- qemu_log_mask(LOG_GUEST_ERROR, +- "Warning: source register overlaps destination register" +- "in atomic insn at pc=0x" TARGET_FMT_lx "\n", +- ctx->base.pc_next - 4); +- return false; +- } +- +- addr = make_address_i(ctx, addr, 0); +- +- func(dest, addr, val, ctx->mem_idx, mop); +- gen_set_gpr(a->rd, dest, EXT_NONE); +- +- return true; +-} +- +-TRANS(ll_w, ALL, gen_ll, MO_TESL) +-TRANS(sc_w, ALL, gen_sc, MO_TESL) +-TRANS(ll_d, 64, gen_ll, MO_TEUQ) +-TRANS(sc_d, 64, gen_sc, MO_TEUQ) +-TRANS(amswap_w, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TESL) +-TRANS(amswap_d, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TEUQ) +-TRANS(amadd_w, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TESL) +-TRANS(amadd_d, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TEUQ) +-TRANS(amand_w, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TESL) +-TRANS(amand_d, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TEUQ) +-TRANS(amor_w, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TESL) +-TRANS(amor_d, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TEUQ) +-TRANS(amxor_w, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TESL) +-TRANS(amxor_d, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TEUQ) +-TRANS(ammax_w, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TESL) +-TRANS(ammax_d, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TEUQ) +-TRANS(ammin_w, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TESL) +-TRANS(ammin_d, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TEUQ) +-TRANS(ammax_wu, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TESL) +-TRANS(ammax_du, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TEUQ) +-TRANS(ammin_wu, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TESL) +-TRANS(ammin_du, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TEUQ) +-TRANS(amswap_db_w, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TESL) +-TRANS(amswap_db_d, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TEUQ) +-TRANS(amadd_db_w, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TESL) +-TRANS(amadd_db_d, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TEUQ) +-TRANS(amand_db_w, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TESL) +-TRANS(amand_db_d, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TEUQ) +-TRANS(amor_db_w, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TESL) +-TRANS(amor_db_d, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TEUQ) +-TRANS(amxor_db_w, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TESL) +-TRANS(amxor_db_d, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TEUQ) +-TRANS(ammax_db_w, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TESL) +-TRANS(ammax_db_d, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TEUQ) +-TRANS(ammin_db_w, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TESL) +-TRANS(ammin_db_d, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TEUQ) +-TRANS(ammax_db_wu, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TESL) +-TRANS(ammax_db_du, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TEUQ) +-TRANS(ammin_db_wu, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TESL) +-TRANS(ammin_db_du, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TEUQ) +diff --git a/target/loongarch/insn_trans/trans_bit.c.inc b/target/loongarch/insn_trans/trans_bit.c.inc +deleted file mode 100644 +index ee5fa00..0000000 +--- a/target/loongarch/insn_trans/trans_bit.c.inc ++++ /dev/null +@@ -1,208 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * Copyright (c) 2021 Loongson Technology Corporation Limited +- */ +- +-static bool gen_rr(DisasContext *ctx, arg_rr *a, +- DisasExtend src_ext, DisasExtend dst_ext, +- void (*func)(TCGv, TCGv)) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, dst_ext); +- TCGv src1 = gpr_src(ctx, a->rj, src_ext); +- +- func(dest, src1); +- gen_set_gpr(a->rd, dest, dst_ext); +- +- return true; +-} +- +-static void gen_bytepick_w(TCGv dest, TCGv src1, TCGv src2, target_long sa) +-{ +- tcg_gen_concat_tl_i64(dest, src1, src2); +- tcg_gen_sextract_i64(dest, dest, (32 - sa * 8), 32); +-} +- +-static void gen_bytepick_d(TCGv dest, TCGv src1, TCGv src2, target_long sa) +-{ +- tcg_gen_extract2_i64(dest, src1, src2, (64 - sa * 8)); +-} +- +-static bool gen_bstrins(DisasContext *ctx, arg_rr_ms_ls *a, +- DisasExtend dst_ext) +-{ +- TCGv src1 = gpr_src(ctx, a->rd, EXT_NONE); +- TCGv src2 = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); +- +- if (a->ls > a->ms) { +- return false; +- } +- +- tcg_gen_deposit_tl(dest, src1, src2, a->ls, a->ms - a->ls + 1); +- gen_set_gpr(a->rd, dest, dst_ext); +- return true; +-} +- +-static bool gen_bstrpick(DisasContext *ctx, arg_rr_ms_ls *a, +- DisasExtend dst_ext) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- +- if (a->ls > a->ms) { +- return false; +- } +- +- tcg_gen_extract_tl(dest, src1, a->ls, a->ms - a->ls + 1); +- gen_set_gpr(a->rd, dest, dst_ext); +- return true; +-} +- +-static void gen_clz_w(TCGv dest, TCGv src1) +-{ +- tcg_gen_clzi_tl(dest, src1, TARGET_LONG_BITS); +- tcg_gen_subi_tl(dest, dest, TARGET_LONG_BITS - 32); +-} +- +-static void gen_clo_w(TCGv dest, TCGv src1) +-{ +- tcg_gen_not_tl(dest, src1); +- tcg_gen_ext32u_tl(dest, dest); +- gen_clz_w(dest, dest); +-} +- +-static void gen_ctz_w(TCGv dest, TCGv src1) +-{ +- tcg_gen_ori_tl(dest, src1, (target_ulong)MAKE_64BIT_MASK(32, 32)); +- tcg_gen_ctzi_tl(dest, dest, TARGET_LONG_BITS); +-} +- +-static void gen_cto_w(TCGv dest, TCGv src1) +-{ +- tcg_gen_not_tl(dest, src1); +- gen_ctz_w(dest, dest); +-} +- +-static void gen_clz_d(TCGv dest, TCGv src1) +-{ +- tcg_gen_clzi_i64(dest, src1, TARGET_LONG_BITS); +-} +- +-static void gen_clo_d(TCGv dest, TCGv src1) +-{ +- tcg_gen_not_tl(dest, src1); +- gen_clz_d(dest, dest); +-} +- +-static void gen_ctz_d(TCGv dest, TCGv src1) +-{ +- tcg_gen_ctzi_tl(dest, src1, TARGET_LONG_BITS); +-} +- +-static void gen_cto_d(TCGv dest, TCGv src1) +-{ +- tcg_gen_not_tl(dest, src1); +- gen_ctz_d(dest, dest); +-} +- +-static void gen_revb_2w(TCGv dest, TCGv src1) +-{ +- tcg_gen_bswap64_i64(dest, src1); +- tcg_gen_rotri_i64(dest, dest, 32); +-} +- +-static void gen_revb_2h(TCGv dest, TCGv src1) +-{ +- TCGv mask = tcg_constant_tl(0x00FF00FF); +- TCGv t0 = tcg_temp_new(); +- TCGv t1 = tcg_temp_new(); +- +- tcg_gen_shri_tl(t0, src1, 8); +- tcg_gen_and_tl(t0, t0, mask); +- tcg_gen_and_tl(t1, src1, mask); +- tcg_gen_shli_tl(t1, t1, 8); +- tcg_gen_or_tl(dest, t0, t1); +-} +- +-static void gen_revb_4h(TCGv dest, TCGv src1) +-{ +- TCGv mask = tcg_constant_tl(0x00FF00FF00FF00FFULL); +- TCGv t0 = tcg_temp_new(); +- TCGv t1 = tcg_temp_new(); +- +- tcg_gen_shri_tl(t0, src1, 8); +- tcg_gen_and_tl(t0, t0, mask); +- tcg_gen_and_tl(t1, src1, mask); +- tcg_gen_shli_tl(t1, t1, 8); +- tcg_gen_or_tl(dest, t0, t1); +-} +- +-static void gen_revh_2w(TCGv dest, TCGv src1) +-{ +- TCGv_i64 t0 = tcg_temp_new_i64(); +- TCGv_i64 t1 = tcg_temp_new_i64(); +- TCGv_i64 mask = tcg_constant_i64(0x0000ffff0000ffffull); +- +- tcg_gen_shri_i64(t0, src1, 16); +- tcg_gen_and_i64(t1, src1, mask); +- tcg_gen_and_i64(t0, t0, mask); +- tcg_gen_shli_i64(t1, t1, 16); +- tcg_gen_or_i64(dest, t1, t0); +-} +- +-static void gen_revh_d(TCGv dest, TCGv src1) +-{ +- TCGv t0 = tcg_temp_new(); +- TCGv t1 = tcg_temp_new(); +- TCGv mask = tcg_constant_tl(0x0000FFFF0000FFFFULL); +- +- tcg_gen_shri_tl(t1, src1, 16); +- tcg_gen_and_tl(t1, t1, mask); +- tcg_gen_and_tl(t0, src1, mask); +- tcg_gen_shli_tl(t0, t0, 16); +- tcg_gen_or_tl(t0, t0, t1); +- tcg_gen_rotri_tl(dest, t0, 32); +-} +- +-static void gen_maskeqz(TCGv dest, TCGv src1, TCGv src2) +-{ +- TCGv zero = tcg_constant_tl(0); +- +- tcg_gen_movcond_tl(TCG_COND_EQ, dest, src2, zero, zero, src1); +-} +- +-static void gen_masknez(TCGv dest, TCGv src1, TCGv src2) +-{ +- TCGv zero = tcg_constant_tl(0); +- +- tcg_gen_movcond_tl(TCG_COND_NE, dest, src2, zero, zero, src1); +-} +- +-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) +-TRANS(clz_w, ALL, gen_rr, EXT_ZERO, EXT_NONE, gen_clz_w) +-TRANS(cto_w, ALL, gen_rr, EXT_NONE, EXT_NONE, gen_cto_w) +-TRANS(ctz_w, ALL, gen_rr, EXT_NONE, EXT_NONE, gen_ctz_w) +-TRANS(clo_d, 64, gen_rr, EXT_NONE, EXT_NONE, gen_clo_d) +-TRANS(clz_d, 64, gen_rr, EXT_NONE, EXT_NONE, gen_clz_d) +-TRANS(cto_d, 64, gen_rr, EXT_NONE, EXT_NONE, gen_cto_d) +-TRANS(ctz_d, 64, gen_rr, EXT_NONE, EXT_NONE, gen_ctz_d) +-TRANS(revb_2h, ALL, gen_rr, EXT_NONE, EXT_SIGN, gen_revb_2h) +-TRANS(revb_4h, 64, gen_rr, EXT_NONE, EXT_NONE, gen_revb_4h) +-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(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) +-TRANS(bytepick_d, 64, gen_rrr_sa, EXT_NONE, EXT_NONE, gen_bytepick_d) +-TRANS(bstrins_w, ALL, gen_bstrins, EXT_SIGN) +-TRANS(bstrins_d, 64, gen_bstrins, EXT_NONE) +-TRANS(bstrpick_w, ALL, gen_bstrpick, EXT_SIGN) +-TRANS(bstrpick_d, 64, gen_bstrpick, EXT_NONE) +diff --git a/target/loongarch/insn_trans/trans_branch.c.inc b/target/loongarch/insn_trans/trans_branch.c.inc +deleted file mode 100644 +index 221e515..0000000 +--- a/target/loongarch/insn_trans/trans_branch.c.inc ++++ /dev/null +@@ -1,84 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * Copyright (c) 2021 Loongson Technology Corporation Limited +- */ +- +-static bool trans_b(DisasContext *ctx, arg_b *a) +-{ +- gen_goto_tb(ctx, 0, ctx->base.pc_next + a->offs); +- ctx->base.is_jmp = DISAS_NORETURN; +- return true; +-} +- +-static bool trans_bl(DisasContext *ctx, arg_bl *a) +-{ +- tcg_gen_movi_tl(cpu_gpr[1], make_address_pc(ctx, ctx->base.pc_next + 4)); +- gen_goto_tb(ctx, 0, ctx->base.pc_next + a->offs); +- ctx->base.is_jmp = DISAS_NORETURN; +- return true; +-} +- +-static bool trans_jirl(DisasContext *ctx, arg_jirl *a) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- +- TCGv addr = make_address_i(ctx, src1, a->imm); +- tcg_gen_mov_tl(cpu_pc, addr); +- tcg_gen_movi_tl(dest, make_address_pc(ctx, ctx->base.pc_next + 4)); +- gen_set_gpr(a->rd, dest, EXT_NONE); +- tcg_gen_lookup_and_goto_ptr(); +- ctx->base.is_jmp = DISAS_NORETURN; +- return true; +-} +- +-static void gen_bc(DisasContext *ctx, TCGv src1, TCGv src2, +- target_long offs, TCGCond cond) +-{ +- TCGLabel *l = gen_new_label(); +- tcg_gen_brcond_tl(cond, src1, src2, l); +- gen_goto_tb(ctx, 1, ctx->base.pc_next + 4); +- gen_set_label(l); +- gen_goto_tb(ctx, 0, ctx->base.pc_next + offs); +- ctx->base.is_jmp = DISAS_NORETURN; +-} +- +-static bool gen_rr_bc(DisasContext *ctx, arg_rr_offs *a, TCGCond cond) +-{ +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv src2 = gpr_src(ctx, a->rd, EXT_NONE); +- +- gen_bc(ctx, src1, src2, a->offs, cond); +- return true; +-} +- +-static bool gen_rz_bc(DisasContext *ctx, arg_r_offs *a, TCGCond cond) +-{ +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv src2 = tcg_constant_tl(0); +- +- gen_bc(ctx, src1, src2, a->offs, cond); +- return true; +-} +- +-static bool gen_cz_bc(DisasContext *ctx, arg_c_offs *a, TCGCond cond) +-{ +- TCGv src1 = tcg_temp_new(); +- TCGv src2 = tcg_constant_tl(0); +- +- tcg_gen_ld8u_tl(src1, tcg_env, +- offsetof(CPULoongArchState, cf[a->cj])); +- gen_bc(ctx, src1, src2, a->offs, cond); +- return true; +-} +- +-TRANS(beq, ALL, gen_rr_bc, TCG_COND_EQ) +-TRANS(bne, ALL, gen_rr_bc, TCG_COND_NE) +-TRANS(blt, ALL, gen_rr_bc, TCG_COND_LT) +-TRANS(bge, ALL, gen_rr_bc, TCG_COND_GE) +-TRANS(bltu, ALL, gen_rr_bc, TCG_COND_LTU) +-TRANS(bgeu, ALL, gen_rr_bc, TCG_COND_GEU) +-TRANS(beqz, ALL, gen_rz_bc, TCG_COND_EQ) +-TRANS(bnez, ALL, gen_rz_bc, TCG_COND_NE) +-TRANS(bceqz, 64, gen_cz_bc, TCG_COND_EQ) +-TRANS(bcnez, 64, gen_cz_bc, TCG_COND_NE) +diff --git a/target/loongarch/insn_trans/trans_extra.c.inc b/target/loongarch/insn_trans/trans_extra.c.inc +deleted file mode 100644 +index cfa361f..0000000 +--- a/target/loongarch/insn_trans/trans_extra.c.inc ++++ /dev/null +@@ -1,107 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * Copyright (c) 2021 Loongson Technology Corporation Limited +- */ +- +-static bool trans_break(DisasContext *ctx, arg_break *a) +-{ +- generate_exception(ctx, EXCCODE_BRK); +- return true; +-} +- +-static bool trans_syscall(DisasContext *ctx, arg_syscall *a) +-{ +- generate_exception(ctx, EXCCODE_SYS); +- return true; +-} +- +-static bool trans_asrtle_d(DisasContext *ctx, arg_asrtle_d * a) +-{ +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); +- +- if (!avail_64(ctx)) { +- return false; +- } +- +- gen_helper_asrtle_d(tcg_env, src1, src2); +- return true; +-} +- +-static bool trans_asrtgt_d(DisasContext *ctx, arg_asrtgt_d * a) +-{ +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); +- +- if (!avail_64(ctx)) { +- return false; +- } +- +- gen_helper_asrtgt_d(tcg_env, src1, src2); +- return true; +-} +- +-static bool gen_rdtime(DisasContext *ctx, arg_rr *a, +- bool word, bool high) +-{ +- TCGv dst1 = gpr_dst(ctx, a->rd, EXT_NONE); +- TCGv dst2 = gpr_dst(ctx, a->rj, EXT_NONE); +- +- translator_io_start(&ctx->base); +- gen_helper_rdtime_d(dst1, tcg_env); +- if (word) { +- tcg_gen_sextract_tl(dst1, dst1, high ? 32 : 0, 32); +- } +- tcg_gen_ld_i64(dst2, tcg_env, offsetof(CPULoongArchState, CSR_TID)); +- +- return true; +-} +- +-static bool trans_rdtimel_w(DisasContext *ctx, arg_rdtimel_w *a) +-{ +- return gen_rdtime(ctx, a, 1, 0); +-} +- +-static bool trans_rdtimeh_w(DisasContext *ctx, arg_rdtimeh_w *a) +-{ +- return gen_rdtime(ctx, a, 1, 1); +-} +- +-static bool trans_rdtime_d(DisasContext *ctx, arg_rdtime_d *a) +-{ +- return gen_rdtime(ctx, a, 0, 0); +-} +- +-static bool trans_cpucfg(DisasContext *ctx, arg_cpucfg *a) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- +- gen_helper_cpucfg(dest, tcg_env, src1); +- gen_set_gpr(a->rd, dest, EXT_NONE); +- +- return true; +-} +- +-static bool gen_crc(DisasContext *ctx, arg_rrr *a, +- void (*func)(TCGv, TCGv, TCGv, TCGv), +- TCGv tsz) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, EXT_SIGN); +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); +- +- func(dest, src2, src1, tsz); +- gen_set_gpr(a->rd, dest, EXT_SIGN); +- +- return true; +-} +- +-TRANS(crc_w_b_w, 64, gen_crc, gen_helper_crc32, tcg_constant_tl(1)) +-TRANS(crc_w_h_w, 64, gen_crc, gen_helper_crc32, tcg_constant_tl(2)) +-TRANS(crc_w_w_w, 64, gen_crc, gen_helper_crc32, tcg_constant_tl(4)) +-TRANS(crc_w_d_w, 64, gen_crc, gen_helper_crc32, tcg_constant_tl(8)) +-TRANS(crcc_w_b_w, 64, gen_crc, gen_helper_crc32c, tcg_constant_tl(1)) +-TRANS(crcc_w_h_w, 64, gen_crc, gen_helper_crc32c, tcg_constant_tl(2)) +-TRANS(crcc_w_w_w, 64, gen_crc, gen_helper_crc32c, tcg_constant_tl(4)) +-TRANS(crcc_w_d_w, 64, gen_crc, gen_helper_crc32c, tcg_constant_tl(8)) +diff --git a/target/loongarch/insn_trans/trans_farith.c.inc b/target/loongarch/insn_trans/trans_farith.c.inc +deleted file mode 100644 +index f4a0dea..0000000 +--- a/target/loongarch/insn_trans/trans_farith.c.inc ++++ /dev/null +@@ -1,207 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * Copyright (c) 2021 Loongson Technology Corporation Limited +- */ +- +-#ifndef CONFIG_USER_ONLY +-#define CHECK_FPE do { \ +- if ((ctx->base.tb->flags & HW_FLAGS_EUEN_FPE) == 0) { \ +- generate_exception(ctx, EXCCODE_FPD); \ +- return true; \ +- } \ +-} while (0) +-#else +-#define CHECK_FPE +-#endif +- +-static bool gen_fff(DisasContext *ctx, arg_fff *a, +- void (*func)(TCGv, TCGv_env, TCGv, TCGv)) +-{ +- TCGv dest = get_fpr(ctx, a->fd); +- TCGv src1 = get_fpr(ctx, a->fj); +- TCGv src2 = get_fpr(ctx, a->fk); +- +- CHECK_FPE; +- +- func(dest, tcg_env, src1, src2); +- set_fpr(a->fd, dest); +- +- return true; +-} +- +-static bool gen_ff(DisasContext *ctx, arg_ff *a, +- void (*func)(TCGv, TCGv_env, TCGv)) +-{ +- TCGv dest = get_fpr(ctx, a->fd); +- TCGv src = get_fpr(ctx, a->fj); +- +- CHECK_FPE; +- +- func(dest, tcg_env, src); +- set_fpr(a->fd, dest); +- +- return true; +-} +- +-static bool gen_muladd(DisasContext *ctx, arg_ffff *a, +- void (*func)(TCGv, TCGv_env, TCGv, TCGv, TCGv, TCGv_i32), +- int flag) +-{ +- TCGv_i32 tflag = tcg_constant_i32(flag); +- TCGv dest = get_fpr(ctx, a->fd); +- TCGv src1 = get_fpr(ctx, a->fj); +- TCGv src2 = get_fpr(ctx, a->fk); +- TCGv src3 = get_fpr(ctx, a->fa); +- +- CHECK_FPE; +- +- func(dest, tcg_env, src1, src2, src3, tflag); +- set_fpr(a->fd, dest); +- +- return true; +-} +- +-static bool trans_fcopysign_s(DisasContext *ctx, arg_fcopysign_s *a) +-{ +- TCGv dest = get_fpr(ctx, a->fd); +- TCGv src1 = get_fpr(ctx, a->fk); +- TCGv src2 = get_fpr(ctx, a->fj); +- +- if (!avail_FP_SP(ctx)) { +- return false; +- } +- +- CHECK_FPE; +- +- tcg_gen_deposit_i64(dest, src1, src2, 0, 31); +- set_fpr(a->fd, dest); +- +- return true; +-} +- +-static bool trans_fcopysign_d(DisasContext *ctx, arg_fcopysign_d *a) +-{ +- TCGv dest = get_fpr(ctx, a->fd); +- TCGv src1 = get_fpr(ctx, a->fk); +- TCGv src2 = get_fpr(ctx, a->fj); +- +- if (!avail_FP_DP(ctx)) { +- return false; +- } +- +- CHECK_FPE; +- +- tcg_gen_deposit_i64(dest, src1, src2, 0, 63); +- set_fpr(a->fd, dest); +- +- return true; +-} +- +-static bool trans_fabs_s(DisasContext *ctx, arg_fabs_s *a) +-{ +- TCGv dest = get_fpr(ctx, a->fd); +- TCGv src = get_fpr(ctx, a->fj); +- +- if (!avail_FP_SP(ctx)) { +- return false; +- } +- +- CHECK_FPE; +- +- tcg_gen_andi_i64(dest, src, MAKE_64BIT_MASK(0, 31)); +- gen_nanbox_s(dest, dest); +- set_fpr(a->fd, dest); +- +- return true; +-} +- +-static bool trans_fabs_d(DisasContext *ctx, arg_fabs_d *a) +-{ +- TCGv dest = get_fpr(ctx, a->fd); +- TCGv src = get_fpr(ctx, a->fj); +- +- if (!avail_FP_DP(ctx)) { +- return false; +- } +- +- CHECK_FPE; +- +- tcg_gen_andi_i64(dest, src, MAKE_64BIT_MASK(0, 63)); +- set_fpr(a->fd, dest); +- +- return true; +-} +- +-static bool trans_fneg_s(DisasContext *ctx, arg_fneg_s *a) +-{ +- TCGv dest = get_fpr(ctx, a->fd); +- TCGv src = get_fpr(ctx, a->fj); +- +- if (!avail_FP_SP(ctx)) { +- return false; +- } +- +- CHECK_FPE; +- +- tcg_gen_xori_i64(dest, src, 0x80000000); +- gen_nanbox_s(dest, dest); +- set_fpr(a->fd, dest); +- +- return true; +-} +- +-static bool trans_fneg_d(DisasContext *ctx, arg_fneg_d *a) +-{ +- TCGv dest = get_fpr(ctx, a->fd); +- TCGv src = get_fpr(ctx, a->fj); +- +- if (!avail_FP_DP(ctx)) { +- return false; +- } +- +- CHECK_FPE; +- +- tcg_gen_xori_i64(dest, src, 0x8000000000000000LL); +- set_fpr(a->fd, dest); +- +- return true; +-} +- +-TRANS(fadd_s, FP_SP, gen_fff, gen_helper_fadd_s) +-TRANS(fadd_d, FP_DP, gen_fff, gen_helper_fadd_d) +-TRANS(fsub_s, FP_SP, gen_fff, gen_helper_fsub_s) +-TRANS(fsub_d, FP_DP, gen_fff, gen_helper_fsub_d) +-TRANS(fmul_s, FP_SP, gen_fff, gen_helper_fmul_s) +-TRANS(fmul_d, FP_DP, gen_fff, gen_helper_fmul_d) +-TRANS(fdiv_s, FP_SP, gen_fff, gen_helper_fdiv_s) +-TRANS(fdiv_d, FP_DP, gen_fff, gen_helper_fdiv_d) +-TRANS(fmax_s, FP_SP, gen_fff, gen_helper_fmax_s) +-TRANS(fmax_d, FP_DP, gen_fff, gen_helper_fmax_d) +-TRANS(fmin_s, FP_SP, gen_fff, gen_helper_fmin_s) +-TRANS(fmin_d, FP_DP, gen_fff, gen_helper_fmin_d) +-TRANS(fmaxa_s, FP_SP, gen_fff, gen_helper_fmaxa_s) +-TRANS(fmaxa_d, FP_DP, gen_fff, gen_helper_fmaxa_d) +-TRANS(fmina_s, FP_SP, gen_fff, gen_helper_fmina_s) +-TRANS(fmina_d, FP_DP, gen_fff, gen_helper_fmina_d) +-TRANS(fscaleb_s, FP_SP, gen_fff, gen_helper_fscaleb_s) +-TRANS(fscaleb_d, FP_DP, gen_fff, gen_helper_fscaleb_d) +-TRANS(fsqrt_s, FP_SP, gen_ff, gen_helper_fsqrt_s) +-TRANS(fsqrt_d, FP_DP, gen_ff, gen_helper_fsqrt_d) +-TRANS(frecip_s, FP_SP, gen_ff, gen_helper_frecip_s) +-TRANS(frecip_d, FP_DP, gen_ff, gen_helper_frecip_d) +-TRANS(frsqrt_s, FP_SP, gen_ff, gen_helper_frsqrt_s) +-TRANS(frsqrt_d, FP_DP, gen_ff, gen_helper_frsqrt_d) +-TRANS(flogb_s, FP_SP, gen_ff, gen_helper_flogb_s) +-TRANS(flogb_d, FP_DP, gen_ff, gen_helper_flogb_d) +-TRANS(fclass_s, FP_SP, gen_ff, gen_helper_fclass_s) +-TRANS(fclass_d, FP_DP, gen_ff, gen_helper_fclass_d) +-TRANS(fmadd_s, FP_SP, gen_muladd, gen_helper_fmuladd_s, 0) +-TRANS(fmadd_d, FP_DP, gen_muladd, gen_helper_fmuladd_d, 0) +-TRANS(fmsub_s, FP_SP, gen_muladd, gen_helper_fmuladd_s, float_muladd_negate_c) +-TRANS(fmsub_d, FP_DP, gen_muladd, gen_helper_fmuladd_d, float_muladd_negate_c) +-TRANS(fnmadd_s, FP_SP, gen_muladd, gen_helper_fmuladd_s, float_muladd_negate_result) +-TRANS(fnmadd_d, FP_DP, gen_muladd, gen_helper_fmuladd_d, float_muladd_negate_result) +-TRANS(fnmsub_s, FP_SP, gen_muladd, gen_helper_fmuladd_s, +- float_muladd_negate_c | float_muladd_negate_result) +-TRANS(fnmsub_d, FP_DP, gen_muladd, gen_helper_fmuladd_d, +- float_muladd_negate_c | float_muladd_negate_result) +diff --git a/target/loongarch/insn_trans/trans_fcmp.c.inc b/target/loongarch/insn_trans/trans_fcmp.c.inc +deleted file mode 100644 +index 3babf69..0000000 +--- a/target/loongarch/insn_trans/trans_fcmp.c.inc ++++ /dev/null +@@ -1,72 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * Copyright (c) 2021 Loongson Technology Corporation Limited +- */ +- +-/* bit0(signaling/quiet) bit1(lt) bit2(eq) bit3(un) bit4(neq) */ +-static uint32_t get_fcmp_flags(int cond) +-{ +- uint32_t flags = 0; +- +- if (cond & 0x1) { +- flags |= FCMP_LT; +- } +- if (cond & 0x2) { +- flags |= FCMP_EQ; +- } +- if (cond & 0x4) { +- flags |= FCMP_UN; +- } +- if (cond & 0x8) { +- flags |= FCMP_GT | FCMP_LT; +- } +- return flags; +-} +- +-static bool trans_fcmp_cond_s(DisasContext *ctx, arg_fcmp_cond_s *a) +-{ +- TCGv var, src1, src2; +- uint32_t flags; +- void (*fn)(TCGv, TCGv_env, TCGv, TCGv, TCGv_i32); +- +- if (!avail_FP_SP(ctx)) { +- return false; +- } +- +- CHECK_FPE; +- +- var = tcg_temp_new(); +- src1 = get_fpr(ctx, a->fj); +- src2 = get_fpr(ctx, a->fk); +- fn = (a->fcond & 1 ? gen_helper_fcmp_s_s : gen_helper_fcmp_c_s); +- flags = get_fcmp_flags(a->fcond >> 1); +- +- fn(var, tcg_env, src1, src2, tcg_constant_i32(flags)); +- +- tcg_gen_st8_tl(var, tcg_env, offsetof(CPULoongArchState, cf[a->cd])); +- return true; +-} +- +-static bool trans_fcmp_cond_d(DisasContext *ctx, arg_fcmp_cond_d *a) +-{ +- TCGv var, src1, src2; +- uint32_t flags; +- void (*fn)(TCGv, TCGv_env, TCGv, TCGv, TCGv_i32); +- +- if (!avail_FP_DP(ctx)) { +- return false; +- } +- +- CHECK_FPE; +- +- var = tcg_temp_new(); +- src1 = get_fpr(ctx, a->fj); +- src2 = get_fpr(ctx, a->fk); +- fn = (a->fcond & 1 ? gen_helper_fcmp_s_d : gen_helper_fcmp_c_d); +- flags = get_fcmp_flags(a->fcond >> 1); +- +- fn(var, tcg_env, src1, src2, tcg_constant_i32(flags)); +- +- tcg_gen_st8_tl(var, tcg_env, offsetof(CPULoongArchState, cf[a->cd])); +- return true; +-} +diff --git a/target/loongarch/insn_trans/trans_fcnv.c.inc b/target/loongarch/insn_trans/trans_fcnv.c.inc +deleted file mode 100644 +index 833c059..0000000 +--- a/target/loongarch/insn_trans/trans_fcnv.c.inc ++++ /dev/null +@@ -1,33 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * Copyright (c) 2021 Loongson Technology Corporation Limited +- */ +- +-TRANS(fcvt_s_d, FP_DP, gen_ff, gen_helper_fcvt_s_d) +-TRANS(fcvt_d_s, FP_DP, gen_ff, gen_helper_fcvt_d_s) +-TRANS(ftintrm_w_s, FP_SP, gen_ff, gen_helper_ftintrm_w_s) +-TRANS(ftintrm_w_d, FP_DP, gen_ff, gen_helper_ftintrm_w_d) +-TRANS(ftintrm_l_s, FP_SP, gen_ff, gen_helper_ftintrm_l_s) +-TRANS(ftintrm_l_d, FP_DP, gen_ff, gen_helper_ftintrm_l_d) +-TRANS(ftintrp_w_s, FP_SP, gen_ff, gen_helper_ftintrp_w_s) +-TRANS(ftintrp_w_d, FP_DP, gen_ff, gen_helper_ftintrp_w_d) +-TRANS(ftintrp_l_s, FP_SP, gen_ff, gen_helper_ftintrp_l_s) +-TRANS(ftintrp_l_d, FP_DP, gen_ff, gen_helper_ftintrp_l_d) +-TRANS(ftintrz_w_s, FP_SP, gen_ff, gen_helper_ftintrz_w_s) +-TRANS(ftintrz_w_d, FP_DP, gen_ff, gen_helper_ftintrz_w_d) +-TRANS(ftintrz_l_s, FP_SP, gen_ff, gen_helper_ftintrz_l_s) +-TRANS(ftintrz_l_d, FP_DP, gen_ff, gen_helper_ftintrz_l_d) +-TRANS(ftintrne_w_s, FP_SP, gen_ff, gen_helper_ftintrne_w_s) +-TRANS(ftintrne_w_d, FP_DP, gen_ff, gen_helper_ftintrne_w_d) +-TRANS(ftintrne_l_s, FP_SP, gen_ff, gen_helper_ftintrne_l_s) +-TRANS(ftintrne_l_d, FP_DP, gen_ff, gen_helper_ftintrne_l_d) +-TRANS(ftint_w_s, FP_SP, gen_ff, gen_helper_ftint_w_s) +-TRANS(ftint_w_d, FP_DP, gen_ff, gen_helper_ftint_w_d) +-TRANS(ftint_l_s, FP_SP, gen_ff, gen_helper_ftint_l_s) +-TRANS(ftint_l_d, FP_DP, gen_ff, gen_helper_ftint_l_d) +-TRANS(ffint_s_w, FP_SP, gen_ff, gen_helper_ffint_s_w) +-TRANS(ffint_s_l, FP_SP, gen_ff, gen_helper_ffint_s_l) +-TRANS(ffint_d_w, FP_DP, gen_ff, gen_helper_ffint_d_w) +-TRANS(ffint_d_l, FP_DP, gen_ff, gen_helper_ffint_d_l) +-TRANS(frint_s, FP_SP, gen_ff, gen_helper_frint_s) +-TRANS(frint_d, FP_DP, gen_ff, gen_helper_frint_d) +diff --git a/target/loongarch/insn_trans/trans_fmemory.c.inc b/target/loongarch/insn_trans/trans_fmemory.c.inc +deleted file mode 100644 +index 13452bc..0000000 +--- a/target/loongarch/insn_trans/trans_fmemory.c.inc ++++ /dev/null +@@ -1,158 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * Copyright (c) 2021 Loongson Technology Corporation Limited +- */ +- +-static void maybe_nanbox_load(TCGv freg, MemOp mop) +-{ +- if ((mop & MO_SIZE) == MO_32) { +- gen_nanbox_s(freg, freg); +- } +-} +- +-static bool gen_fload_i(DisasContext *ctx, arg_fr_i *a, MemOp mop) +-{ +- TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv dest = get_fpr(ctx, a->fd); +- +- CHECK_FPE; +- +- addr = make_address_i(ctx, addr, a->imm); +- +- tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop); +- maybe_nanbox_load(dest, mop); +- set_fpr(a->fd, dest); +- +- return true; +-} +- +-static bool gen_fstore_i(DisasContext *ctx, arg_fr_i *a, MemOp mop) +-{ +- TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv src = get_fpr(ctx, a->fd); +- +- CHECK_FPE; +- +- addr = make_address_i(ctx, addr, a->imm); +- +- tcg_gen_qemu_st_tl(src, addr, ctx->mem_idx, mop); +- +- return true; +-} +- +-static bool gen_floadx(DisasContext *ctx, arg_frr *a, MemOp mop) +-{ +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); +- TCGv dest = get_fpr(ctx, a->fd); +- TCGv addr; +- +- CHECK_FPE; +- +- addr = make_address_x(ctx, src1, src2); +- tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop); +- maybe_nanbox_load(dest, mop); +- set_fpr(a->fd, dest); +- +- return true; +-} +- +-static bool gen_fstorex(DisasContext *ctx, arg_frr *a, MemOp mop) +-{ +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); +- TCGv src3 = get_fpr(ctx, a->fd); +- TCGv addr; +- +- CHECK_FPE; +- +- addr = make_address_x(ctx, src1, src2); +- tcg_gen_qemu_st_tl(src3, addr, ctx->mem_idx, mop); +- +- return true; +-} +- +-static bool gen_fload_gt(DisasContext *ctx, arg_frr *a, MemOp mop) +-{ +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); +- TCGv dest = get_fpr(ctx, a->fd); +- TCGv addr; +- +- CHECK_FPE; +- +- gen_helper_asrtgt_d(tcg_env, src1, src2); +- addr = make_address_x(ctx, src1, src2); +- tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop); +- maybe_nanbox_load(dest, mop); +- set_fpr(a->fd, dest); +- +- return true; +-} +- +-static bool gen_fstore_gt(DisasContext *ctx, arg_frr *a, MemOp mop) +-{ +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); +- TCGv src3 = get_fpr(ctx, a->fd); +- TCGv addr; +- +- CHECK_FPE; +- +- gen_helper_asrtgt_d(tcg_env, src1, src2); +- addr = make_address_x(ctx, src1, src2); +- tcg_gen_qemu_st_tl(src3, addr, ctx->mem_idx, mop); +- +- return true; +-} +- +-static bool gen_fload_le(DisasContext *ctx, arg_frr *a, MemOp mop) +-{ +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); +- TCGv dest = get_fpr(ctx, a->fd); +- TCGv addr; +- +- CHECK_FPE; +- +- gen_helper_asrtle_d(tcg_env, src1, src2); +- addr = make_address_x(ctx, src1, src2); +- tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop); +- maybe_nanbox_load(dest, mop); +- set_fpr(a->fd, dest); +- +- return true; +-} +- +-static bool gen_fstore_le(DisasContext *ctx, arg_frr *a, MemOp mop) +-{ +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); +- TCGv src3 = get_fpr(ctx, a->fd); +- TCGv addr; +- +- CHECK_FPE; +- +- gen_helper_asrtle_d(tcg_env, src1, src2); +- addr = make_address_x(ctx, src1, src2); +- tcg_gen_qemu_st_tl(src3, addr, ctx->mem_idx, mop); +- +- return true; +-} +- +-TRANS(fld_s, FP_SP, gen_fload_i, MO_TEUL) +-TRANS(fst_s, FP_SP, gen_fstore_i, MO_TEUL) +-TRANS(fld_d, FP_DP, gen_fload_i, MO_TEUQ) +-TRANS(fst_d, FP_DP, gen_fstore_i, MO_TEUQ) +-TRANS(fldx_s, FP_SP, gen_floadx, MO_TEUL) +-TRANS(fldx_d, FP_DP, gen_floadx, MO_TEUQ) +-TRANS(fstx_s, FP_SP, gen_fstorex, MO_TEUL) +-TRANS(fstx_d, FP_DP, gen_fstorex, MO_TEUQ) +-TRANS(fldgt_s, FP_SP, gen_fload_gt, MO_TEUL) +-TRANS(fldgt_d, FP_DP, gen_fload_gt, MO_TEUQ) +-TRANS(fldle_s, FP_SP, gen_fload_le, MO_TEUL) +-TRANS(fldle_d, FP_DP, gen_fload_le, MO_TEUQ) +-TRANS(fstgt_s, FP_SP, gen_fstore_gt, MO_TEUL) +-TRANS(fstgt_d, FP_DP, gen_fstore_gt, MO_TEUQ) +-TRANS(fstle_s, FP_SP, gen_fstore_le, MO_TEUL) +-TRANS(fstle_d, FP_DP, gen_fstore_le, MO_TEUQ) +diff --git a/target/loongarch/insn_trans/trans_fmov.c.inc b/target/loongarch/insn_trans/trans_fmov.c.inc +deleted file mode 100644 +index 5cbd9d3..0000000 +--- a/target/loongarch/insn_trans/trans_fmov.c.inc ++++ /dev/null +@@ -1,224 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * Copyright (c) 2021 Loongson Technology Corporation Limited +- */ +- +-static const uint32_t fcsr_mask[4] = { +- UINT32_MAX, FCSR0_M1, FCSR0_M2, FCSR0_M3 +-}; +- +-static bool trans_fsel(DisasContext *ctx, arg_fsel *a) +-{ +- TCGv zero = tcg_constant_tl(0); +- TCGv dest = get_fpr(ctx, a->fd); +- TCGv src1 = get_fpr(ctx, a->fj); +- TCGv src2 = get_fpr(ctx, a->fk); +- TCGv cond; +- +- if (!avail_FP(ctx)) { +- return false; +- } +- +- CHECK_FPE; +- +- cond = tcg_temp_new(); +- tcg_gen_ld8u_tl(cond, tcg_env, offsetof(CPULoongArchState, cf[a->ca])); +- tcg_gen_movcond_tl(TCG_COND_EQ, dest, cond, zero, src1, src2); +- set_fpr(a->fd, dest); +- +- return true; +-} +- +-static bool gen_f2f(DisasContext *ctx, arg_ff *a, +- void (*func)(TCGv, TCGv), bool nanbox) +-{ +- TCGv dest = get_fpr(ctx, a->fd); +- TCGv src = get_fpr(ctx, a->fj); +- +- CHECK_FPE; +- +- func(dest, src); +- if (nanbox) { +- gen_nanbox_s(dest, dest); +- } +- set_fpr(a->fd, dest); +- +- return true; +-} +- +-static bool gen_r2f(DisasContext *ctx, arg_fr *a, +- void (*func)(TCGv, TCGv)) +-{ +- TCGv src = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv dest = get_fpr(ctx, a->fd); +- +- if (!avail_FP(ctx)) { +- return false; +- } +- +- CHECK_FPE; +- +- func(dest, src); +- set_fpr(a->fd, dest); +- +- return true; +-} +- +-static bool gen_f2r(DisasContext *ctx, arg_rf *a, +- void (*func)(TCGv, TCGv)) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); +- TCGv src = get_fpr(ctx, a->fj); +- +- if (!avail_FP(ctx)) { +- return false; +- } +- +- CHECK_FPE; +- +- func(dest, src); +- gen_set_gpr(a->rd, dest, EXT_NONE); +- +- return true; +-} +- +-static bool trans_movgr2fcsr(DisasContext *ctx, arg_movgr2fcsr *a) +-{ +- uint32_t mask = fcsr_mask[a->fcsrd]; +- TCGv Rj = gpr_src(ctx, a->rj, EXT_NONE); +- +- if (!avail_FP(ctx)) { +- return false; +- } +- +- CHECK_FPE; +- +- if (mask == UINT32_MAX) { +- tcg_gen_st32_i64(Rj, tcg_env, offsetof(CPULoongArchState, fcsr0)); +- } else { +- TCGv_i32 fcsr0 = tcg_temp_new_i32(); +- TCGv_i32 temp = tcg_temp_new_i32(); +- +- tcg_gen_ld_i32(fcsr0, tcg_env, offsetof(CPULoongArchState, fcsr0)); +- tcg_gen_extrl_i64_i32(temp, Rj); +- tcg_gen_andi_i32(temp, temp, mask); +- tcg_gen_andi_i32(fcsr0, fcsr0, ~mask); +- tcg_gen_or_i32(fcsr0, fcsr0, temp); +- tcg_gen_st_i32(fcsr0, tcg_env, offsetof(CPULoongArchState, fcsr0)); +- } +- +- /* +- * Install the new rounding mode to fpu_status, if changed. +- * Note that FCSR3 is exactly the rounding mode field. +- */ +- if (mask & FCSR0_M3) { +- gen_helper_set_rounding_mode(tcg_env); +- } +- return true; +-} +- +-static bool trans_movfcsr2gr(DisasContext *ctx, arg_movfcsr2gr *a) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); +- +- if (!avail_FP(ctx)) { +- return false; +- } +- +- CHECK_FPE; +- +- tcg_gen_ld32u_i64(dest, tcg_env, offsetof(CPULoongArchState, fcsr0)); +- tcg_gen_andi_i64(dest, dest, fcsr_mask[a->fcsrs]); +- gen_set_gpr(a->rd, dest, EXT_NONE); +- +- return true; +-} +- +-static void gen_movgr2fr_w(TCGv dest, TCGv src) +-{ +- tcg_gen_deposit_i64(dest, dest, src, 0, 32); +-} +- +-static void gen_movgr2frh_w(TCGv dest, TCGv src) +-{ +- tcg_gen_deposit_i64(dest, dest, src, 32, 32); +-} +- +-static void gen_movfrh2gr_s(TCGv dest, TCGv src) +-{ +- tcg_gen_sextract_tl(dest, src, 32, 32); +-} +- +-static bool trans_movfr2cf(DisasContext *ctx, arg_movfr2cf *a) +-{ +- TCGv t0; +- TCGv src = get_fpr(ctx, a->fj); +- +- if (!avail_FP(ctx)) { +- return false; +- } +- +- CHECK_FPE; +- +- t0 = tcg_temp_new(); +- tcg_gen_andi_tl(t0, src, 0x1); +- tcg_gen_st8_tl(t0, tcg_env, offsetof(CPULoongArchState, cf[a->cd & 0x7])); +- +- return true; +-} +- +-static bool trans_movcf2fr(DisasContext *ctx, arg_movcf2fr *a) +-{ +- TCGv dest = get_fpr(ctx, a->fd); +- +- if (!avail_FP(ctx)) { +- return false; +- } +- +- CHECK_FPE; +- +- tcg_gen_ld8u_tl(dest, tcg_env, +- offsetof(CPULoongArchState, cf[a->cj & 0x7])); +- set_fpr(a->fd, dest); +- +- return true; +-} +- +-static bool trans_movgr2cf(DisasContext *ctx, arg_movgr2cf *a) +-{ +- TCGv t0; +- +- if (!avail_FP(ctx)) { +- return false; +- } +- +- CHECK_FPE; +- +- t0 = tcg_temp_new(); +- tcg_gen_andi_tl(t0, gpr_src(ctx, a->rj, EXT_NONE), 0x1); +- tcg_gen_st8_tl(t0, tcg_env, offsetof(CPULoongArchState, cf[a->cd & 0x7])); +- +- return true; +-} +- +-static bool trans_movcf2gr(DisasContext *ctx, arg_movcf2gr *a) +-{ +- if (!avail_FP(ctx)) { +- return false; +- } +- +- CHECK_FPE; +- +- tcg_gen_ld8u_tl(gpr_dst(ctx, a->rd, EXT_NONE), tcg_env, +- offsetof(CPULoongArchState, cf[a->cj & 0x7])); +- return true; +-} +- +-TRANS(fmov_s, FP_SP, gen_f2f, tcg_gen_mov_tl, true) +-TRANS(fmov_d, FP_DP, gen_f2f, tcg_gen_mov_tl, false) +-TRANS(movgr2fr_w, FP_SP, gen_r2f, gen_movgr2fr_w) +-TRANS(movgr2fr_d, 64, gen_r2f, tcg_gen_mov_tl) +-TRANS(movgr2frh_w, FP_DP, gen_r2f, gen_movgr2frh_w) +-TRANS(movfr2gr_s, FP_SP, gen_f2r, tcg_gen_ext32s_tl) +-TRANS(movfr2gr_d, 64, gen_f2r, tcg_gen_mov_tl) +-TRANS(movfrh2gr_s, FP_DP, gen_f2r, gen_movfrh2gr_s) +diff --git a/target/loongarch/insn_trans/trans_memory.c.inc b/target/loongarch/insn_trans/trans_memory.c.inc +deleted file mode 100644 +index 42f4e74..0000000 +--- a/target/loongarch/insn_trans/trans_memory.c.inc ++++ /dev/null +@@ -1,194 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * Copyright (c) 2021 Loongson Technology Corporation Limited +- */ +- +-static bool gen_load(DisasContext *ctx, arg_rr_i *a, MemOp mop) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); +- TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); +- +- addr = make_address_i(ctx, addr, a->imm); +- +- tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop); +- gen_set_gpr(a->rd, dest, EXT_NONE); +- return true; +-} +- +-static bool gen_store(DisasContext *ctx, arg_rr_i *a, MemOp mop) +-{ +- TCGv data = gpr_src(ctx, a->rd, EXT_NONE); +- TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); +- +- addr = make_address_i(ctx, addr, a->imm); +- +- tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, mop); +- return true; +-} +- +-static bool gen_loadx(DisasContext *ctx, arg_rrr *a, MemOp mop) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); +- TCGv addr = make_address_x(ctx, src1, src2); +- +- tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop); +- gen_set_gpr(a->rd, dest, EXT_NONE); +- +- return true; +-} +- +-static bool gen_storex(DisasContext *ctx, arg_rrr *a, MemOp mop) +-{ +- TCGv data = gpr_src(ctx, a->rd, EXT_NONE); +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); +- TCGv addr = make_address_x(ctx, src1, src2); +- +- tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, mop); +- +- return true; +-} +- +-static bool gen_load_gt(DisasContext *ctx, arg_rrr *a, MemOp mop) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); +- +- gen_helper_asrtgt_d(tcg_env, src1, src2); +- src1 = make_address_i(ctx, src1, 0); +- tcg_gen_qemu_ld_tl(dest, src1, ctx->mem_idx, mop); +- gen_set_gpr(a->rd, dest, EXT_NONE); +- +- return true; +-} +- +-static bool gen_load_le(DisasContext *ctx, arg_rrr *a, MemOp mop) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); +- +- gen_helper_asrtle_d(tcg_env, src1, src2); +- src1 = make_address_i(ctx, src1, 0); +- tcg_gen_qemu_ld_tl(dest, src1, ctx->mem_idx, mop); +- gen_set_gpr(a->rd, dest, EXT_NONE); +- +- return true; +-} +- +-static bool gen_store_gt(DisasContext *ctx, arg_rrr *a, MemOp mop) +-{ +- TCGv data = gpr_src(ctx, a->rd, EXT_NONE); +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); +- +- gen_helper_asrtgt_d(tcg_env, src1, src2); +- src1 = make_address_i(ctx, src1, 0); +- tcg_gen_qemu_st_tl(data, src1, ctx->mem_idx, mop); +- +- return true; +-} +- +-static bool gen_store_le(DisasContext *ctx, arg_rrr *a, MemOp mop) +-{ +- TCGv data = gpr_src(ctx, a->rd, EXT_NONE); +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); +- +- gen_helper_asrtle_d(tcg_env, src1, src2); +- src1 = make_address_i(ctx, src1, 0); +- tcg_gen_qemu_st_tl(data, src1, ctx->mem_idx, mop); +- +- return true; +-} +- +-static bool trans_preld(DisasContext *ctx, arg_preld *a) +-{ +- return true; +-} +- +-static bool trans_preldx(DisasContext *ctx, arg_preldx * a) +-{ +- return true; +-} +- +-static bool trans_dbar(DisasContext *ctx, arg_dbar * a) +-{ +- tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL); +- return true; +-} +- +-static bool trans_ibar(DisasContext *ctx, arg_ibar *a) +-{ +- ctx->base.is_jmp = DISAS_STOP; +- return true; +-} +- +-static bool gen_ldptr(DisasContext *ctx, arg_rr_i *a, MemOp mop) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); +- TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); +- +- addr = make_address_i(ctx, addr, a->imm); +- +- tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop); +- gen_set_gpr(a->rd, dest, EXT_NONE); +- return true; +-} +- +-static bool gen_stptr(DisasContext *ctx, arg_rr_i *a, MemOp mop) +-{ +- TCGv data = gpr_src(ctx, a->rd, EXT_NONE); +- TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); +- +- addr = make_address_i(ctx, addr, a->imm); +- +- tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, mop); +- return true; +-} +- +-TRANS(ld_b, ALL, gen_load, MO_SB) +-TRANS(ld_h, ALL, gen_load, MO_TESW) +-TRANS(ld_w, ALL, gen_load, MO_TESL) +-TRANS(ld_d, 64, gen_load, MO_TEUQ) +-TRANS(st_b, ALL, gen_store, MO_UB) +-TRANS(st_h, ALL, gen_store, MO_TEUW) +-TRANS(st_w, ALL, gen_store, MO_TEUL) +-TRANS(st_d, 64, gen_store, MO_TEUQ) +-TRANS(ld_bu, ALL, gen_load, MO_UB) +-TRANS(ld_hu, ALL, gen_load, MO_TEUW) +-TRANS(ld_wu, 64, gen_load, MO_TEUL) +-TRANS(ldx_b, 64, gen_loadx, MO_SB) +-TRANS(ldx_h, 64, gen_loadx, MO_TESW) +-TRANS(ldx_w, 64, gen_loadx, MO_TESL) +-TRANS(ldx_d, 64, gen_loadx, MO_TEUQ) +-TRANS(stx_b, 64, gen_storex, MO_UB) +-TRANS(stx_h, 64, gen_storex, MO_TEUW) +-TRANS(stx_w, 64, gen_storex, MO_TEUL) +-TRANS(stx_d, 64, gen_storex, MO_TEUQ) +-TRANS(ldx_bu, 64, gen_loadx, MO_UB) +-TRANS(ldx_hu, 64, gen_loadx, MO_TEUW) +-TRANS(ldx_wu, 64, gen_loadx, MO_TEUL) +-TRANS(ldptr_w, 64, gen_ldptr, MO_TESL) +-TRANS(stptr_w, 64, gen_stptr, MO_TEUL) +-TRANS(ldptr_d, 64, gen_ldptr, MO_TEUQ) +-TRANS(stptr_d, 64, gen_stptr, MO_TEUQ) +-TRANS(ldgt_b, 64, gen_load_gt, MO_SB) +-TRANS(ldgt_h, 64, gen_load_gt, MO_TESW) +-TRANS(ldgt_w, 64, gen_load_gt, MO_TESL) +-TRANS(ldgt_d, 64, gen_load_gt, MO_TEUQ) +-TRANS(ldle_b, 64, gen_load_le, MO_SB) +-TRANS(ldle_h, 64, gen_load_le, MO_TESW) +-TRANS(ldle_w, 64, gen_load_le, MO_TESL) +-TRANS(ldle_d, 64, gen_load_le, MO_TEUQ) +-TRANS(stgt_b, 64, gen_store_gt, MO_UB) +-TRANS(stgt_h, 64, gen_store_gt, MO_TEUW) +-TRANS(stgt_w, 64, gen_store_gt, MO_TEUL) +-TRANS(stgt_d, 64, gen_store_gt, MO_TEUQ) +-TRANS(stle_b, 64, gen_store_le, MO_UB) +-TRANS(stle_h, 64, gen_store_le, MO_TEUW) +-TRANS(stle_w, 64, gen_store_le, MO_TEUL) +-TRANS(stle_d, 64, gen_store_le, MO_TEUQ) +diff --git a/target/loongarch/insn_trans/trans_privileged.c.inc b/target/loongarch/insn_trans/trans_privileged.c.inc +deleted file mode 100644 +index 01d4572..0000000 +--- a/target/loongarch/insn_trans/trans_privileged.c.inc ++++ /dev/null +@@ -1,498 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * Copyright (c) 2021 Loongson Technology Corporation Limited +- * +- * LoongArch translation routines for the privileged instructions. +- */ +- +-#include "cpu-csr.h" +- +-#ifdef CONFIG_USER_ONLY +- +-#define GEN_FALSE_TRANS(name) \ +-static bool trans_##name(DisasContext *ctx, arg_##name * a) \ +-{ \ +- return false; \ +-} +- +-GEN_FALSE_TRANS(csrrd) +-GEN_FALSE_TRANS(csrwr) +-GEN_FALSE_TRANS(csrxchg) +-GEN_FALSE_TRANS(iocsrrd_b) +-GEN_FALSE_TRANS(iocsrrd_h) +-GEN_FALSE_TRANS(iocsrrd_w) +-GEN_FALSE_TRANS(iocsrrd_d) +-GEN_FALSE_TRANS(iocsrwr_b) +-GEN_FALSE_TRANS(iocsrwr_h) +-GEN_FALSE_TRANS(iocsrwr_w) +-GEN_FALSE_TRANS(iocsrwr_d) +-GEN_FALSE_TRANS(tlbsrch) +-GEN_FALSE_TRANS(tlbrd) +-GEN_FALSE_TRANS(tlbwr) +-GEN_FALSE_TRANS(tlbfill) +-GEN_FALSE_TRANS(tlbclr) +-GEN_FALSE_TRANS(tlbflush) +-GEN_FALSE_TRANS(invtlb) +-GEN_FALSE_TRANS(cacop) +-GEN_FALSE_TRANS(ldpte) +-GEN_FALSE_TRANS(lddir) +-GEN_FALSE_TRANS(ertn) +-GEN_FALSE_TRANS(dbcl) +-GEN_FALSE_TRANS(idle) +- +-#else +- +-typedef void (*GenCSRRead)(TCGv dest, TCGv_ptr env); +-typedef void (*GenCSRWrite)(TCGv dest, TCGv_ptr env, TCGv src); +- +-typedef struct { +- int offset; +- int flags; +- GenCSRRead readfn; +- GenCSRWrite writefn; +-} CSRInfo; +- +-enum { +- CSRFL_READONLY = (1 << 0), +- CSRFL_EXITTB = (1 << 1), +- CSRFL_IO = (1 << 2), +-}; +- +-#define CSR_OFF_FUNCS(NAME, FL, RD, WR) \ +- [LOONGARCH_CSR_##NAME] = { \ +- .offset = offsetof(CPULoongArchState, CSR_##NAME), \ +- .flags = FL, .readfn = RD, .writefn = WR \ +- } +- +-#define CSR_OFF_ARRAY(NAME, N) \ +- [LOONGARCH_CSR_##NAME(N)] = { \ +- .offset = offsetof(CPULoongArchState, CSR_##NAME[N]), \ +- .flags = 0, .readfn = NULL, .writefn = NULL \ +- } +- +-#define CSR_OFF_FLAGS(NAME, FL) \ +- CSR_OFF_FUNCS(NAME, FL, NULL, NULL) +- +-#define CSR_OFF(NAME) \ +- CSR_OFF_FLAGS(NAME, 0) +- +-static const CSRInfo csr_info[] = { +- CSR_OFF_FLAGS(CRMD, CSRFL_EXITTB), +- CSR_OFF(PRMD), +- CSR_OFF_FLAGS(EUEN, CSRFL_EXITTB), +- CSR_OFF_FLAGS(MISC, CSRFL_READONLY), +- CSR_OFF(ECFG), +- CSR_OFF_FUNCS(ESTAT, CSRFL_EXITTB, NULL, gen_helper_csrwr_estat), +- CSR_OFF(ERA), +- CSR_OFF(BADV), +- CSR_OFF_FLAGS(BADI, CSRFL_READONLY), +- CSR_OFF(EENTRY), +- CSR_OFF(TLBIDX), +- CSR_OFF(TLBEHI), +- CSR_OFF(TLBELO0), +- CSR_OFF(TLBELO1), +- CSR_OFF_FUNCS(ASID, CSRFL_EXITTB, NULL, gen_helper_csrwr_asid), +- CSR_OFF(PGDL), +- CSR_OFF(PGDH), +- CSR_OFF_FUNCS(PGD, CSRFL_READONLY, gen_helper_csrrd_pgd, NULL), +- CSR_OFF(PWCL), +- CSR_OFF(PWCH), +- CSR_OFF(STLBPS), +- CSR_OFF(RVACFG), +- CSR_OFF_FUNCS(CPUID, CSRFL_READONLY, gen_helper_csrrd_cpuid, NULL), +- CSR_OFF_FLAGS(PRCFG1, CSRFL_READONLY), +- CSR_OFF_FLAGS(PRCFG2, CSRFL_READONLY), +- CSR_OFF_FLAGS(PRCFG3, CSRFL_READONLY), +- CSR_OFF_ARRAY(SAVE, 0), +- CSR_OFF_ARRAY(SAVE, 1), +- CSR_OFF_ARRAY(SAVE, 2), +- CSR_OFF_ARRAY(SAVE, 3), +- CSR_OFF_ARRAY(SAVE, 4), +- CSR_OFF_ARRAY(SAVE, 5), +- CSR_OFF_ARRAY(SAVE, 6), +- CSR_OFF_ARRAY(SAVE, 7), +- CSR_OFF_ARRAY(SAVE, 8), +- CSR_OFF_ARRAY(SAVE, 9), +- CSR_OFF_ARRAY(SAVE, 10), +- CSR_OFF_ARRAY(SAVE, 11), +- CSR_OFF_ARRAY(SAVE, 12), +- CSR_OFF_ARRAY(SAVE, 13), +- CSR_OFF_ARRAY(SAVE, 14), +- CSR_OFF_ARRAY(SAVE, 15), +- CSR_OFF(TID), +- CSR_OFF_FUNCS(TCFG, CSRFL_IO, NULL, gen_helper_csrwr_tcfg), +- CSR_OFF_FUNCS(TVAL, CSRFL_READONLY | CSRFL_IO, gen_helper_csrrd_tval, NULL), +- CSR_OFF(CNTC), +- CSR_OFF_FUNCS(TICLR, CSRFL_IO, NULL, gen_helper_csrwr_ticlr), +- CSR_OFF(LLBCTL), +- CSR_OFF(IMPCTL1), +- CSR_OFF(IMPCTL2), +- CSR_OFF(TLBRENTRY), +- CSR_OFF(TLBRBADV), +- CSR_OFF(TLBRERA), +- CSR_OFF(TLBRSAVE), +- CSR_OFF(TLBRELO0), +- CSR_OFF(TLBRELO1), +- CSR_OFF(TLBREHI), +- CSR_OFF(TLBRPRMD), +- CSR_OFF(MERRCTL), +- CSR_OFF(MERRINFO1), +- CSR_OFF(MERRINFO2), +- CSR_OFF(MERRENTRY), +- CSR_OFF(MERRERA), +- CSR_OFF(MERRSAVE), +- CSR_OFF(CTAG), +- CSR_OFF_ARRAY(DMW, 0), +- CSR_OFF_ARRAY(DMW, 1), +- CSR_OFF_ARRAY(DMW, 2), +- CSR_OFF_ARRAY(DMW, 3), +- CSR_OFF(DBG), +- CSR_OFF(DERA), +- CSR_OFF(DSAVE), +-}; +- +-static bool check_plv(DisasContext *ctx) +-{ +- if (ctx->plv == MMU_PLV_USER) { +- generate_exception(ctx, EXCCODE_IPE); +- return true; +- } +- return false; +-} +- +-static const CSRInfo *get_csr(unsigned csr_num) +-{ +- const CSRInfo *csr; +- +- if (csr_num >= ARRAY_SIZE(csr_info)) { +- return NULL; +- } +- csr = &csr_info[csr_num]; +- if (csr->offset == 0) { +- return NULL; +- } +- return csr; +-} +- +-static bool check_csr_flags(DisasContext *ctx, const CSRInfo *csr, bool write) +-{ +- if ((csr->flags & CSRFL_READONLY) && write) { +- return false; +- } +- if ((csr->flags & CSRFL_IO) && translator_io_start(&ctx->base)) { +- ctx->base.is_jmp = DISAS_EXIT_UPDATE; +- } else if ((csr->flags & CSRFL_EXITTB) && write) { +- ctx->base.is_jmp = DISAS_EXIT_UPDATE; +- } +- return true; +-} +- +-static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a) +-{ +- TCGv dest; +- const CSRInfo *csr; +- +- if (check_plv(ctx)) { +- return false; +- } +- csr = get_csr(a->csr); +- if (csr == NULL) { +- /* CSR is undefined: read as 0. */ +- dest = tcg_constant_tl(0); +- } else { +- check_csr_flags(ctx, csr, false); +- dest = gpr_dst(ctx, a->rd, EXT_NONE); +- if (csr->readfn) { +- csr->readfn(dest, tcg_env); +- } else { +- tcg_gen_ld_tl(dest, tcg_env, csr->offset); +- } +- } +- gen_set_gpr(a->rd, dest, EXT_NONE); +- return true; +-} +- +-static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a) +-{ +- TCGv dest, src1; +- const CSRInfo *csr; +- +- if (check_plv(ctx)) { +- return false; +- } +- csr = get_csr(a->csr); +- if (csr == NULL) { +- /* CSR is undefined: write ignored, read old_value as 0. */ +- gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE); +- return true; +- } +- if (!check_csr_flags(ctx, csr, true)) { +- /* CSR is readonly: trap. */ +- return false; +- } +- src1 = gpr_src(ctx, a->rd, EXT_NONE); +- if (csr->writefn) { +- dest = gpr_dst(ctx, a->rd, EXT_NONE); +- csr->writefn(dest, tcg_env, src1); +- } else { +- dest = tcg_temp_new(); +- tcg_gen_ld_tl(dest, tcg_env, csr->offset); +- tcg_gen_st_tl(src1, tcg_env, csr->offset); +- } +- gen_set_gpr(a->rd, dest, EXT_NONE); +- return true; +-} +- +-static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a) +-{ +- TCGv src1, mask, oldv, newv, temp; +- const CSRInfo *csr; +- +- if (check_plv(ctx)) { +- return false; +- } +- csr = get_csr(a->csr); +- if (csr == NULL) { +- /* CSR is undefined: write ignored, read old_value as 0. */ +- gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE); +- return true; +- } +- +- if (!check_csr_flags(ctx, csr, true)) { +- /* CSR is readonly: trap. */ +- return false; +- } +- +- /* So far only readonly csrs have readfn. */ +- assert(csr->readfn == NULL); +- +- src1 = gpr_src(ctx, a->rd, EXT_NONE); +- mask = gpr_src(ctx, a->rj, EXT_NONE); +- oldv = tcg_temp_new(); +- newv = tcg_temp_new(); +- temp = tcg_temp_new(); +- +- tcg_gen_ld_tl(oldv, tcg_env, csr->offset); +- tcg_gen_and_tl(newv, src1, mask); +- tcg_gen_andc_tl(temp, oldv, mask); +- tcg_gen_or_tl(newv, newv, temp); +- +- if (csr->writefn) { +- csr->writefn(oldv, tcg_env, newv); +- } else { +- tcg_gen_st_tl(newv, tcg_env, csr->offset); +- } +- gen_set_gpr(a->rd, oldv, EXT_NONE); +- return true; +-} +- +-static bool gen_iocsrrd(DisasContext *ctx, arg_rr *a, +- void (*func)(TCGv, TCGv_ptr, TCGv)) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- +- if (check_plv(ctx)) { +- return false; +- } +- func(dest, tcg_env, src1); +- return true; +-} +- +-static bool gen_iocsrwr(DisasContext *ctx, arg_rr *a, +- void (*func)(TCGv_ptr, TCGv, TCGv)) +-{ +- TCGv val = gpr_src(ctx, a->rd, EXT_NONE); +- TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); +- +- if (check_plv(ctx)) { +- return false; +- } +- func(tcg_env, addr, val); +- return true; +-} +- +-TRANS(iocsrrd_b, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_b) +-TRANS(iocsrrd_h, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_h) +-TRANS(iocsrrd_w, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_w) +-TRANS(iocsrrd_d, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_d) +-TRANS(iocsrwr_b, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_b) +-TRANS(iocsrwr_h, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_h) +-TRANS(iocsrwr_w, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_w) +-TRANS(iocsrwr_d, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_d) +- +-static void check_mmu_idx(DisasContext *ctx) +-{ +- if (ctx->mem_idx != MMU_IDX_DA) { +- tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4); +- ctx->base.is_jmp = DISAS_EXIT; +- } +-} +- +-static bool trans_tlbsrch(DisasContext *ctx, arg_tlbsrch *a) +-{ +- if (check_plv(ctx)) { +- return false; +- } +- gen_helper_tlbsrch(tcg_env); +- return true; +-} +- +-static bool trans_tlbrd(DisasContext *ctx, arg_tlbrd *a) +-{ +- if (check_plv(ctx)) { +- return false; +- } +- gen_helper_tlbrd(tcg_env); +- return true; +-} +- +-static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a) +-{ +- if (check_plv(ctx)) { +- return false; +- } +- gen_helper_tlbwr(tcg_env); +- check_mmu_idx(ctx); +- return true; +-} +- +-static bool trans_tlbfill(DisasContext *ctx, arg_tlbfill *a) +-{ +- if (check_plv(ctx)) { +- return false; +- } +- gen_helper_tlbfill(tcg_env); +- check_mmu_idx(ctx); +- return true; +-} +- +-static bool trans_tlbclr(DisasContext *ctx, arg_tlbclr *a) +-{ +- if (check_plv(ctx)) { +- return false; +- } +- gen_helper_tlbclr(tcg_env); +- check_mmu_idx(ctx); +- return true; +-} +- +-static bool trans_tlbflush(DisasContext *ctx, arg_tlbflush *a) +-{ +- if (check_plv(ctx)) { +- return false; +- } +- gen_helper_tlbflush(tcg_env); +- check_mmu_idx(ctx); +- return true; +-} +- +-static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a) +-{ +- TCGv rj = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv rk = gpr_src(ctx, a->rk, EXT_NONE); +- +- if (check_plv(ctx)) { +- return false; +- } +- +- switch (a->imm) { +- case 0: +- case 1: +- gen_helper_invtlb_all(tcg_env); +- break; +- case 2: +- gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(1)); +- break; +- case 3: +- gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(0)); +- break; +- case 4: +- gen_helper_invtlb_all_asid(tcg_env, rj); +- break; +- case 5: +- gen_helper_invtlb_page_asid(tcg_env, rj, rk); +- break; +- case 6: +- gen_helper_invtlb_page_asid_or_g(tcg_env, rj, rk); +- break; +- default: +- return false; +- } +- ctx->base.is_jmp = DISAS_STOP; +- return true; +-} +- +-static bool trans_cacop(DisasContext *ctx, arg_cacop *a) +-{ +- /* Treat the cacop as a nop */ +- if (check_plv(ctx)) { +- return false; +- } +- return true; +-} +- +-static bool trans_ldpte(DisasContext *ctx, arg_ldpte *a) +-{ +- TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx); +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- +- if (!avail_LSPW(ctx)) { +- return true; +- } +- +- if (check_plv(ctx)) { +- return false; +- } +- gen_helper_ldpte(tcg_env, src1, tcg_constant_tl(a->imm), mem_idx); +- return true; +-} +- +-static bool trans_lddir(DisasContext *ctx, arg_lddir *a) +-{ +- TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx); +- TCGv src = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); +- +- if (!avail_LSPW(ctx)) { +- return true; +- } +- +- if (check_plv(ctx)) { +- return false; +- } +- gen_helper_lddir(dest, tcg_env, src, tcg_constant_tl(a->imm), mem_idx); +- return true; +-} +- +-static bool trans_ertn(DisasContext *ctx, arg_ertn *a) +-{ +- if (check_plv(ctx)) { +- return false; +- } +- gen_helper_ertn(tcg_env); +- ctx->base.is_jmp = DISAS_EXIT; +- return true; +-} +- +-static bool trans_dbcl(DisasContext *ctx, arg_dbcl *a) +-{ +- if (check_plv(ctx)) { +- return false; +- } +- generate_exception(ctx, EXCCODE_DBP); +- return true; +-} +- +-static bool trans_idle(DisasContext *ctx, arg_idle *a) +-{ +- if (check_plv(ctx)) { +- return false; +- } +- +- tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4); +- gen_helper_idle(tcg_env); +- ctx->base.is_jmp = DISAS_NORETURN; +- return true; +-} +-#endif +diff --git a/target/loongarch/insn_trans/trans_shift.c.inc b/target/loongarch/insn_trans/trans_shift.c.inc +deleted file mode 100644 +index 2f4bd6f..0000000 +--- a/target/loongarch/insn_trans/trans_shift.c.inc ++++ /dev/null +@@ -1,99 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * Copyright (c) 2021 Loongson Technology Corporation Limited +- */ +- +-static void gen_sll_w(TCGv dest, TCGv src1, TCGv src2) +-{ +- TCGv t0 = tcg_temp_new(); +- tcg_gen_andi_tl(t0, src2, 0x1f); +- tcg_gen_shl_tl(dest, src1, t0); +-} +- +-static void gen_srl_w(TCGv dest, TCGv src1, TCGv src2) +-{ +- TCGv t0 = tcg_temp_new(); +- tcg_gen_andi_tl(t0, src2, 0x1f); +- tcg_gen_shr_tl(dest, src1, t0); +-} +- +-static void gen_sra_w(TCGv dest, TCGv src1, TCGv src2) +-{ +- TCGv t0 = tcg_temp_new(); +- tcg_gen_andi_tl(t0, src2, 0x1f); +- tcg_gen_sar_tl(dest, src1, t0); +-} +- +-static void gen_sll_d(TCGv dest, TCGv src1, TCGv src2) +-{ +- TCGv t0 = tcg_temp_new(); +- tcg_gen_andi_tl(t0, src2, 0x3f); +- tcg_gen_shl_tl(dest, src1, t0); +-} +- +-static void gen_srl_d(TCGv dest, TCGv src1, TCGv src2) +-{ +- TCGv t0 = tcg_temp_new(); +- tcg_gen_andi_tl(t0, src2, 0x3f); +- tcg_gen_shr_tl(dest, src1, t0); +-} +- +-static void gen_sra_d(TCGv dest, TCGv src1, TCGv src2) +-{ +- TCGv t0 = tcg_temp_new(); +- tcg_gen_andi_tl(t0, src2, 0x3f); +- tcg_gen_sar_tl(dest, src1, t0); +-} +- +-static void gen_rotr_w(TCGv dest, TCGv src1, TCGv src2) +-{ +- TCGv_i32 t1 = tcg_temp_new_i32(); +- TCGv_i32 t2 = tcg_temp_new_i32(); +- TCGv t0 = tcg_temp_new(); +- +- tcg_gen_andi_tl(t0, src2, 0x1f); +- +- tcg_gen_trunc_tl_i32(t1, src1); +- tcg_gen_trunc_tl_i32(t2, t0); +- +- tcg_gen_rotr_i32(t1, t1, t2); +- tcg_gen_ext_i32_tl(dest, t1); +-} +- +-static void gen_rotr_d(TCGv dest, TCGv src1, TCGv src2) +-{ +- TCGv t0 = tcg_temp_new(); +- tcg_gen_andi_tl(t0, src2, 0x3f); +- tcg_gen_rotr_tl(dest, src1, t0); +-} +- +-static bool trans_srai_w(DisasContext *ctx, arg_srai_w *a) +-{ +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); +- TCGv src1 = gpr_src(ctx, a->rj, EXT_ZERO); +- +- if (!avail_64(ctx)) { +- return false; +- } +- +- tcg_gen_sextract_tl(dest, src1, a->imm, 32 - a->imm); +- gen_set_gpr(a->rd, dest, EXT_NONE); +- +- return true; +-} +- +-TRANS(sll_w, ALL, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_sll_w) +-TRANS(srl_w, ALL, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_srl_w) +-TRANS(sra_w, ALL, gen_rrr, EXT_SIGN, EXT_NONE, EXT_SIGN, gen_sra_w) +-TRANS(sll_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_sll_d) +-TRANS(srl_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_srl_d) +-TRANS(sra_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_sra_d) +-TRANS(rotr_w, 64, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_rotr_w) +-TRANS(rotr_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_rotr_d) +-TRANS(slli_w, ALL, gen_rri_c, EXT_NONE, EXT_SIGN, tcg_gen_shli_tl) +-TRANS(slli_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_shli_tl) +-TRANS(srli_w, ALL, gen_rri_c, EXT_ZERO, EXT_SIGN, tcg_gen_shri_tl) +-TRANS(srli_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_shri_tl) +-TRANS(srai_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_sari_tl) +-TRANS(rotri_w, 64, gen_rri_v, EXT_NONE, EXT_NONE, gen_rotr_w) +-TRANS(rotri_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_rotri_tl) +diff --git a/target/loongarch/insn_trans/trans_vec.c.inc b/target/loongarch/insn_trans/trans_vec.c.inc +deleted file mode 100644 +index 92b1d22..0000000 +--- a/target/loongarch/insn_trans/trans_vec.c.inc ++++ /dev/null +@@ -1,5511 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * LoongArch vector translate functions +- * Copyright (c) 2022-2023 Loongson Technology Corporation Limited +- */ +- +-static bool check_vec(DisasContext *ctx, uint32_t oprsz) +-{ +- if ((oprsz == 16) && ((ctx->base.tb->flags & HW_FLAGS_EUEN_SXE) == 0)) { +- generate_exception(ctx, EXCCODE_SXD); +- return false; +- } +- +- if ((oprsz == 32) && ((ctx->base.tb->flags & HW_FLAGS_EUEN_ASXE) == 0)) { +- generate_exception(ctx, EXCCODE_ASXD); +- return false; +- } +- +- return true; +-} +- +-static bool gen_vvvv_ptr_vl(DisasContext *ctx, arg_vvvv *a, uint32_t oprsz, +- gen_helper_gvec_4_ptr *fn) +-{ +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- tcg_gen_gvec_4_ptr(vec_full_offset(a->vd), +- vec_full_offset(a->vj), +- vec_full_offset(a->vk), +- vec_full_offset(a->va), +- tcg_env, +- oprsz, ctx->vl / 8, 0, fn); +- return true; +-} +- +-static bool gen_vvvv_ptr(DisasContext *ctx, arg_vvvv *a, +- gen_helper_gvec_4_ptr *fn) +-{ +- return gen_vvvv_ptr_vl(ctx, a, 16, fn); +-} +- +-static bool gen_xxxx_ptr(DisasContext *ctx, arg_vvvv *a, +- gen_helper_gvec_4_ptr *fn) +-{ +- return gen_vvvv_ptr_vl(ctx, a, 32, fn); +-} +- +-static bool gen_vvvv_vl(DisasContext *ctx, arg_vvvv *a, uint32_t oprsz, +- gen_helper_gvec_4 *fn) +-{ +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- tcg_gen_gvec_4_ool(vec_full_offset(a->vd), +- vec_full_offset(a->vj), +- vec_full_offset(a->vk), +- vec_full_offset(a->va), +- oprsz, ctx->vl / 8, 0, fn); +- return true; +-} +- +-static bool gen_vvvv(DisasContext *ctx, arg_vvvv *a, +- gen_helper_gvec_4 *fn) +-{ +- return gen_vvvv_vl(ctx, a, 16, fn); +-} +- +-static bool gen_xxxx(DisasContext *ctx, arg_vvvv *a, +- gen_helper_gvec_4 *fn) +-{ +- return gen_vvvv_vl(ctx, a, 32, fn); +-} +- +-static bool gen_vvv_ptr_vl(DisasContext *ctx, arg_vvv *a, uint32_t oprsz, +- gen_helper_gvec_3_ptr *fn) +-{ +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- tcg_gen_gvec_3_ptr(vec_full_offset(a->vd), +- vec_full_offset(a->vj), +- vec_full_offset(a->vk), +- tcg_env, +- oprsz, ctx->vl / 8, 0, fn); +- return true; +-} +- +-static bool gen_vvv_ptr(DisasContext *ctx, arg_vvv *a, +- gen_helper_gvec_3_ptr *fn) +-{ +- return gen_vvv_ptr_vl(ctx, a, 16, fn); +-} +- +-static bool gen_xxx_ptr(DisasContext *ctx, arg_vvv *a, +- gen_helper_gvec_3_ptr *fn) +-{ +- return gen_vvv_ptr_vl(ctx, a, 32, fn); +-} +- +-static bool gen_vvv_vl(DisasContext *ctx, arg_vvv *a, uint32_t oprsz, +- gen_helper_gvec_3 *fn) +-{ +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- tcg_gen_gvec_3_ool(vec_full_offset(a->vd), +- vec_full_offset(a->vj), +- vec_full_offset(a->vk), +- oprsz, ctx->vl / 8, 0, fn); +- return true; +-} +- +-static bool gen_vvv(DisasContext *ctx, arg_vvv *a, gen_helper_gvec_3 *fn) +-{ +- return gen_vvv_vl(ctx, a, 16, fn); +-} +- +-static bool gen_xxx(DisasContext *ctx, arg_vvv *a, gen_helper_gvec_3 *fn) +-{ +- return gen_vvv_vl(ctx, a, 32, fn); +-} +- +-static bool gen_vv_ptr_vl(DisasContext *ctx, arg_vv *a, uint32_t oprsz, +- gen_helper_gvec_2_ptr *fn) +-{ +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- tcg_gen_gvec_2_ptr(vec_full_offset(a->vd), +- vec_full_offset(a->vj), +- tcg_env, +- oprsz, ctx->vl / 8, 0, fn); +- return true; +-} +- +-static bool gen_vv_ptr(DisasContext *ctx, arg_vv *a, +- gen_helper_gvec_2_ptr *fn) +-{ +- return gen_vv_ptr_vl(ctx, a, 16, fn); +-} +- +-static bool gen_xx_ptr(DisasContext *ctx, arg_vv *a, +- gen_helper_gvec_2_ptr *fn) +-{ +- return gen_vv_ptr_vl(ctx, a, 32, fn); +-} +- +-static bool gen_vv_vl(DisasContext *ctx, arg_vv *a, uint32_t oprsz, +- gen_helper_gvec_2 *fn) +-{ +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- tcg_gen_gvec_2_ool(vec_full_offset(a->vd), +- vec_full_offset(a->vj), +- oprsz, ctx->vl / 8, 0, fn); +- return true; +-} +- +-static bool gen_vv(DisasContext *ctx, arg_vv *a, gen_helper_gvec_2 *fn) +-{ +- return gen_vv_vl(ctx, a, 16, fn); +-} +- +-static bool gen_xx(DisasContext *ctx, arg_vv *a, gen_helper_gvec_2 *fn) +-{ +- return gen_vv_vl(ctx, a, 32, fn); +-} +- +-static bool gen_vv_i_vl(DisasContext *ctx, arg_vv_i *a, uint32_t oprsz, +- gen_helper_gvec_2i *fn) +-{ +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- tcg_gen_gvec_2i_ool(vec_full_offset(a->vd), +- vec_full_offset(a->vj), +- tcg_constant_i64(a->imm), +- oprsz, ctx->vl / 8, 0, fn); +- return true; +-} +- +-static bool gen_vv_i(DisasContext *ctx, arg_vv_i *a, gen_helper_gvec_2i *fn) +-{ +- return gen_vv_i_vl(ctx, a, 16, fn); +-} +- +-static bool gen_xx_i(DisasContext *ctx, arg_vv_i *a, gen_helper_gvec_2i *fn) +-{ +- return gen_vv_i_vl(ctx, a, 32, fn); +-} +- +-static bool gen_cv_vl(DisasContext *ctx, arg_cv *a, uint32_t sz, +- void (*func)(TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32)) +-{ +- if (!check_vec(ctx, sz)) { +- return true; +- } +- +- TCGv_i32 vj = tcg_constant_i32(a->vj); +- TCGv_i32 cd = tcg_constant_i32(a->cd); +- TCGv_i32 oprsz = tcg_constant_i32(sz); +- +- func(tcg_env, oprsz, cd, vj); +- return true; +-} +- +-static bool gen_cv(DisasContext *ctx, arg_cv *a, +- void (*func)(TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32)) +-{ +- return gen_cv_vl(ctx, a, 16, func); +-} +- +-static bool gen_cx(DisasContext *ctx, arg_cv *a, +- void (*func)(TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32)) +-{ +- return gen_cv_vl(ctx, a, 32, func); +-} +- +-static bool gvec_vvv_vl(DisasContext *ctx, arg_vvv *a, +- uint32_t oprsz, MemOp mop, +- void (*func)(unsigned, uint32_t, uint32_t, +- uint32_t, uint32_t, uint32_t)) +-{ +- uint32_t vd_ofs = vec_full_offset(a->vd); +- uint32_t vj_ofs = vec_full_offset(a->vj); +- uint32_t vk_ofs = vec_full_offset(a->vk); +- +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- func(mop, vd_ofs, vj_ofs, vk_ofs, oprsz, ctx->vl / 8); +- return true; +-} +- +-static bool gvec_vvv(DisasContext *ctx, arg_vvv *a, MemOp mop, +- void (*func)(unsigned, uint32_t, uint32_t, +- uint32_t, uint32_t, uint32_t)) +-{ +- return gvec_vvv_vl(ctx, a, 16, mop, func); +-} +- +-static bool gvec_xxx(DisasContext *ctx, arg_vvv *a, MemOp mop, +- void (*func)(unsigned, uint32_t, uint32_t, +- uint32_t, uint32_t, uint32_t)) +-{ +- return gvec_vvv_vl(ctx, a, 32, mop, func); +-} +- +-static bool gvec_vv_vl(DisasContext *ctx, arg_vv *a, +- uint32_t oprsz, MemOp mop, +- void (*func)(unsigned, uint32_t, uint32_t, +- uint32_t, uint32_t)) +-{ +- uint32_t vd_ofs = vec_full_offset(a->vd); +- uint32_t vj_ofs = vec_full_offset(a->vj); +- +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- func(mop, vd_ofs, vj_ofs, oprsz, ctx->vl / 8); +- return true; +-} +- +- +-static bool gvec_vv(DisasContext *ctx, arg_vv *a, MemOp mop, +- void (*func)(unsigned, uint32_t, uint32_t, +- uint32_t, uint32_t)) +-{ +- return gvec_vv_vl(ctx, a, 16, mop, func); +-} +- +-static bool gvec_xx(DisasContext *ctx, arg_vv *a, MemOp mop, +- void (*func)(unsigned, uint32_t, uint32_t, +- uint32_t, uint32_t)) +-{ +- return gvec_vv_vl(ctx, a, 32, mop, func); +-} +- +-static bool gvec_vv_i_vl(DisasContext *ctx, arg_vv_i *a, +- uint32_t oprsz, MemOp mop, +- void (*func)(unsigned, uint32_t, uint32_t, +- int64_t, uint32_t, uint32_t)) +-{ +- uint32_t vd_ofs = vec_full_offset(a->vd); +- uint32_t vj_ofs = vec_full_offset(a->vj); +- +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- func(mop, vd_ofs, vj_ofs, a->imm, oprsz, ctx->vl / 8); +- return true; +-} +- +-static bool gvec_vv_i(DisasContext *ctx, arg_vv_i *a, MemOp mop, +- void (*func)(unsigned, uint32_t, uint32_t, +- int64_t, uint32_t, uint32_t)) +-{ +- return gvec_vv_i_vl(ctx, a, 16, mop, func); +-} +- +-static bool gvec_xx_i(DisasContext *ctx, arg_vv_i *a, MemOp mop, +- void (*func)(unsigned, uint32_t, uint32_t, +- int64_t, uint32_t, uint32_t)) +-{ +- return gvec_vv_i_vl(ctx,a, 32, mop, func); +-} +- +-static bool gvec_subi_vl(DisasContext *ctx, arg_vv_i *a, +- uint32_t oprsz, MemOp mop) +-{ +- uint32_t vd_ofs = vec_full_offset(a->vd); +- uint32_t vj_ofs = vec_full_offset(a->vj); +- +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- tcg_gen_gvec_addi(mop, vd_ofs, vj_ofs, -a->imm, oprsz, ctx->vl / 8); +- return true; +-} +- +-static bool gvec_subi(DisasContext *ctx, arg_vv_i *a, MemOp mop) +-{ +- return gvec_subi_vl(ctx, a, 16, mop); +-} +- +-static bool gvec_xsubi(DisasContext *ctx, arg_vv_i *a, MemOp mop) +-{ +- return gvec_subi_vl(ctx, a, 32, mop); +-} +- +-TRANS(vadd_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_add) +-TRANS(vadd_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_add) +-TRANS(vadd_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_add) +-TRANS(vadd_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_add) +-TRANS(xvadd_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_add) +-TRANS(xvadd_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_add) +-TRANS(xvadd_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_add) +-TRANS(xvadd_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_add) +- +-static bool gen_vaddsub_q_vl(DisasContext *ctx, arg_vvv *a, uint32_t oprsz, +- void (*func)(TCGv_i64, TCGv_i64, TCGv_i64, +- TCGv_i64, TCGv_i64, TCGv_i64)) +-{ +- int i; +- TCGv_i64 rh, rl, ah, al, bh, bl; +- +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- rh = tcg_temp_new_i64(); +- rl = tcg_temp_new_i64(); +- ah = tcg_temp_new_i64(); +- al = tcg_temp_new_i64(); +- bh = tcg_temp_new_i64(); +- bl = tcg_temp_new_i64(); +- +- for (i = 0; i < oprsz / 16; i++) { +- get_vreg64(ah, a->vj, 1 + i * 2); +- get_vreg64(al, a->vj, i * 2); +- get_vreg64(bh, a->vk, 1 + i * 2); +- get_vreg64(bl, a->vk, i * 2); +- +- func(rl, rh, al, ah, bl, bh); +- +- set_vreg64(rh, a->vd, 1 + i * 2); +- set_vreg64(rl, a->vd, i * 2); +- } +- return true; +-} +- +-static bool gen_vaddsub_q(DisasContext *ctx, arg_vvv *a, +- void (*func)(TCGv_i64, TCGv_i64, TCGv_i64, +- TCGv_i64, TCGv_i64, TCGv_i64)) +-{ +- return gen_vaddsub_q_vl(ctx, a, 16, func); +-} +- +-static bool gen_xvaddsub_q(DisasContext *ctx, arg_vvv *a, +- void (*func)(TCGv_i64, TCGv_i64, TCGv_i64, +- TCGv_i64, TCGv_i64, TCGv_i64)) +-{ +- return gen_vaddsub_q_vl(ctx, a, 32, func); +-} +- +-TRANS(vsub_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_sub) +-TRANS(vsub_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_sub) +-TRANS(vsub_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_sub) +-TRANS(vsub_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_sub) +-TRANS(xvsub_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_sub) +-TRANS(xvsub_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_sub) +-TRANS(xvsub_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_sub) +-TRANS(xvsub_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_sub) +- +-TRANS(vadd_q, LSX, gen_vaddsub_q, tcg_gen_add2_i64) +-TRANS(vsub_q, LSX, gen_vaddsub_q, tcg_gen_sub2_i64) +-TRANS(xvadd_q, LASX, gen_xvaddsub_q, tcg_gen_add2_i64) +-TRANS(xvsub_q, LASX, gen_xvaddsub_q, tcg_gen_sub2_i64) +- +-TRANS(vaddi_bu, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_addi) +-TRANS(vaddi_hu, LSX, gvec_vv_i, MO_16, tcg_gen_gvec_addi) +-TRANS(vaddi_wu, LSX, gvec_vv_i, MO_32, tcg_gen_gvec_addi) +-TRANS(vaddi_du, LSX, gvec_vv_i, MO_64, tcg_gen_gvec_addi) +-TRANS(vsubi_bu, LSX, gvec_subi, MO_8) +-TRANS(vsubi_hu, LSX, gvec_subi, MO_16) +-TRANS(vsubi_wu, LSX, gvec_subi, MO_32) +-TRANS(vsubi_du, LSX, gvec_subi, MO_64) +-TRANS(xvaddi_bu, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_addi) +-TRANS(xvaddi_hu, LASX, gvec_xx_i, MO_16, tcg_gen_gvec_addi) +-TRANS(xvaddi_wu, LASX, gvec_xx_i, MO_32, tcg_gen_gvec_addi) +-TRANS(xvaddi_du, LASX, gvec_xx_i, MO_64, tcg_gen_gvec_addi) +-TRANS(xvsubi_bu, LASX, gvec_xsubi, MO_8) +-TRANS(xvsubi_hu, LASX, gvec_xsubi, MO_16) +-TRANS(xvsubi_wu, LASX, gvec_xsubi, MO_32) +-TRANS(xvsubi_du, LASX, gvec_xsubi, MO_64) +- +-TRANS(vneg_b, LSX, gvec_vv, MO_8, tcg_gen_gvec_neg) +-TRANS(vneg_h, LSX, gvec_vv, MO_16, tcg_gen_gvec_neg) +-TRANS(vneg_w, LSX, gvec_vv, MO_32, tcg_gen_gvec_neg) +-TRANS(vneg_d, LSX, gvec_vv, MO_64, tcg_gen_gvec_neg) +-TRANS(xvneg_b, LASX, gvec_xx, MO_8, tcg_gen_gvec_neg) +-TRANS(xvneg_h, LASX, gvec_xx, MO_16, tcg_gen_gvec_neg) +-TRANS(xvneg_w, LASX, gvec_xx, MO_32, tcg_gen_gvec_neg) +-TRANS(xvneg_d, LASX, gvec_xx, MO_64, tcg_gen_gvec_neg) +- +-TRANS(vsadd_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_ssadd) +-TRANS(vsadd_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_ssadd) +-TRANS(vsadd_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_ssadd) +-TRANS(vsadd_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_ssadd) +-TRANS(vsadd_bu, LSX, gvec_vvv, MO_8, tcg_gen_gvec_usadd) +-TRANS(vsadd_hu, LSX, gvec_vvv, MO_16, tcg_gen_gvec_usadd) +-TRANS(vsadd_wu, LSX, gvec_vvv, MO_32, tcg_gen_gvec_usadd) +-TRANS(vsadd_du, LSX, gvec_vvv, MO_64, tcg_gen_gvec_usadd) +-TRANS(vssub_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_sssub) +-TRANS(vssub_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_sssub) +-TRANS(vssub_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_sssub) +-TRANS(vssub_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_sssub) +-TRANS(vssub_bu, LSX, gvec_vvv, MO_8, tcg_gen_gvec_ussub) +-TRANS(vssub_hu, LSX, gvec_vvv, MO_16, tcg_gen_gvec_ussub) +-TRANS(vssub_wu, LSX, gvec_vvv, MO_32, tcg_gen_gvec_ussub) +-TRANS(vssub_du, LSX, gvec_vvv, MO_64, tcg_gen_gvec_ussub) +- +-TRANS(xvsadd_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_ssadd) +-TRANS(xvsadd_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_ssadd) +-TRANS(xvsadd_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_ssadd) +-TRANS(xvsadd_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_ssadd) +-TRANS(xvsadd_bu, LASX, gvec_xxx, MO_8, tcg_gen_gvec_usadd) +-TRANS(xvsadd_hu, LASX, gvec_xxx, MO_16, tcg_gen_gvec_usadd) +-TRANS(xvsadd_wu, LASX, gvec_xxx, MO_32, tcg_gen_gvec_usadd) +-TRANS(xvsadd_du, LASX, gvec_xxx, MO_64, tcg_gen_gvec_usadd) +-TRANS(xvssub_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_sssub) +-TRANS(xvssub_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_sssub) +-TRANS(xvssub_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_sssub) +-TRANS(xvssub_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_sssub) +-TRANS(xvssub_bu, LASX, gvec_xxx, MO_8, tcg_gen_gvec_ussub) +-TRANS(xvssub_hu, LASX, gvec_xxx, MO_16, tcg_gen_gvec_ussub) +-TRANS(xvssub_wu, LASX, gvec_xxx, MO_32, tcg_gen_gvec_ussub) +-TRANS(xvssub_du, LASX, gvec_xxx, MO_64, tcg_gen_gvec_ussub) +- +-TRANS(vhaddw_h_b, LSX, gen_vvv, gen_helper_vhaddw_h_b) +-TRANS(vhaddw_w_h, LSX, gen_vvv, gen_helper_vhaddw_w_h) +-TRANS(vhaddw_d_w, LSX, gen_vvv, gen_helper_vhaddw_d_w) +-TRANS(vhaddw_q_d, LSX, gen_vvv, gen_helper_vhaddw_q_d) +-TRANS(vhaddw_hu_bu, LSX, gen_vvv, gen_helper_vhaddw_hu_bu) +-TRANS(vhaddw_wu_hu, LSX, gen_vvv, gen_helper_vhaddw_wu_hu) +-TRANS(vhaddw_du_wu, LSX, gen_vvv, gen_helper_vhaddw_du_wu) +-TRANS(vhaddw_qu_du, LSX, gen_vvv, gen_helper_vhaddw_qu_du) +-TRANS(vhsubw_h_b, LSX, gen_vvv, gen_helper_vhsubw_h_b) +-TRANS(vhsubw_w_h, LSX, gen_vvv, gen_helper_vhsubw_w_h) +-TRANS(vhsubw_d_w, LSX, gen_vvv, gen_helper_vhsubw_d_w) +-TRANS(vhsubw_q_d, LSX, gen_vvv, gen_helper_vhsubw_q_d) +-TRANS(vhsubw_hu_bu, LSX, gen_vvv, gen_helper_vhsubw_hu_bu) +-TRANS(vhsubw_wu_hu, LSX, gen_vvv, gen_helper_vhsubw_wu_hu) +-TRANS(vhsubw_du_wu, LSX, gen_vvv, gen_helper_vhsubw_du_wu) +-TRANS(vhsubw_qu_du, LSX, gen_vvv, gen_helper_vhsubw_qu_du) +- +-TRANS(xvhaddw_h_b, LASX, gen_xxx, gen_helper_vhaddw_h_b) +-TRANS(xvhaddw_w_h, LASX, gen_xxx, gen_helper_vhaddw_w_h) +-TRANS(xvhaddw_d_w, LASX, gen_xxx, gen_helper_vhaddw_d_w) +-TRANS(xvhaddw_q_d, LASX, gen_xxx, gen_helper_vhaddw_q_d) +-TRANS(xvhaddw_hu_bu, LASX, gen_xxx, gen_helper_vhaddw_hu_bu) +-TRANS(xvhaddw_wu_hu, LASX, gen_xxx, gen_helper_vhaddw_wu_hu) +-TRANS(xvhaddw_du_wu, LASX, gen_xxx, gen_helper_vhaddw_du_wu) +-TRANS(xvhaddw_qu_du, LASX, gen_xxx, gen_helper_vhaddw_qu_du) +-TRANS(xvhsubw_h_b, LASX, gen_xxx, gen_helper_vhsubw_h_b) +-TRANS(xvhsubw_w_h, LASX, gen_xxx, gen_helper_vhsubw_w_h) +-TRANS(xvhsubw_d_w, LASX, gen_xxx, gen_helper_vhsubw_d_w) +-TRANS(xvhsubw_q_d, LASX, gen_xxx, gen_helper_vhsubw_q_d) +-TRANS(xvhsubw_hu_bu, LASX, gen_xxx, gen_helper_vhsubw_hu_bu) +-TRANS(xvhsubw_wu_hu, LASX, gen_xxx, gen_helper_vhsubw_wu_hu) +-TRANS(xvhsubw_du_wu, LASX, gen_xxx, gen_helper_vhsubw_du_wu) +-TRANS(xvhsubw_qu_du, LASX, gen_xxx, gen_helper_vhsubw_qu_du) +- +-static void gen_vaddwev_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, t2; +- +- int halfbits = 4 << vece; +- +- t1 = tcg_temp_new_vec_matching(a); +- t2 = tcg_temp_new_vec_matching(b); +- +- /* Sign-extend the even elements from a */ +- tcg_gen_shli_vec(vece, t1, a, halfbits); +- tcg_gen_sari_vec(vece, t1, t1, halfbits); +- +- /* Sign-extend the even elements from b */ +- tcg_gen_shli_vec(vece, t2, b, halfbits); +- tcg_gen_sari_vec(vece, t2, t2, halfbits); +- +- tcg_gen_add_vec(vece, t, t1, t2); +-} +- +-static void gen_vaddwev_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1, t2; +- +- t1 = tcg_temp_new_i32(); +- t2 = tcg_temp_new_i32(); +- tcg_gen_ext16s_i32(t1, a); +- tcg_gen_ext16s_i32(t2, b); +- tcg_gen_add_i32(t, t1, t2); +-} +- +-static void gen_vaddwev_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1, t2; +- +- t1 = tcg_temp_new_i64(); +- t2 = tcg_temp_new_i64(); +- tcg_gen_ext32s_i64(t1, a); +- tcg_gen_ext32s_i64(t2, b); +- tcg_gen_add_i64(t, t1, t2); +-} +- +-static void do_vaddwev_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_shli_vec, INDEX_op_sari_vec, INDEX_op_add_vec, 0 +- }; +- static const GVecGen3 op[4] = { +- { +- .fniv = gen_vaddwev_s, +- .fno = gen_helper_vaddwev_h_b, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vaddwev_w_h, +- .fniv = gen_vaddwev_s, +- .fno = gen_helper_vaddwev_w_h, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vaddwev_d_w, +- .fniv = gen_vaddwev_s, +- .fno = gen_helper_vaddwev_d_w, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- { +- .fno = gen_helper_vaddwev_q_d, +- .vece = MO_128 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vaddwev_h_b, LSX, gvec_vvv, MO_8, do_vaddwev_s) +-TRANS(vaddwev_w_h, LSX, gvec_vvv, MO_16, do_vaddwev_s) +-TRANS(vaddwev_d_w, LSX, gvec_vvv, MO_32, do_vaddwev_s) +-TRANS(vaddwev_q_d, LSX, gvec_vvv, MO_64, do_vaddwev_s) +-TRANS(xvaddwev_h_b, LASX, gvec_xxx, MO_8, do_vaddwev_s) +-TRANS(xvaddwev_w_h, LASX, gvec_xxx, MO_16, do_vaddwev_s) +-TRANS(xvaddwev_d_w, LASX, gvec_xxx, MO_32, do_vaddwev_s) +-TRANS(xvaddwev_q_d, LASX, gvec_xxx, MO_64, do_vaddwev_s) +- +-static void gen_vaddwod_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1, t2; +- +- t1 = tcg_temp_new_i32(); +- t2 = tcg_temp_new_i32(); +- tcg_gen_sari_i32(t1, a, 16); +- tcg_gen_sari_i32(t2, b, 16); +- tcg_gen_add_i32(t, t1, t2); +-} +- +-static void gen_vaddwod_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1, t2; +- +- t1 = tcg_temp_new_i64(); +- t2 = tcg_temp_new_i64(); +- tcg_gen_sari_i64(t1, a, 32); +- tcg_gen_sari_i64(t2, b, 32); +- tcg_gen_add_i64(t, t1, t2); +-} +- +-static void gen_vaddwod_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, t2; +- +- int halfbits = 4 << vece; +- +- t1 = tcg_temp_new_vec_matching(a); +- t2 = tcg_temp_new_vec_matching(b); +- +- /* Sign-extend the odd elements for vector */ +- tcg_gen_sari_vec(vece, t1, a, halfbits); +- tcg_gen_sari_vec(vece, t2, b, halfbits); +- +- tcg_gen_add_vec(vece, t, t1, t2); +-} +- +-static void do_vaddwod_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_sari_vec, INDEX_op_add_vec, 0 +- }; +- static const GVecGen3 op[4] = { +- { +- .fniv = gen_vaddwod_s, +- .fno = gen_helper_vaddwod_h_b, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vaddwod_w_h, +- .fniv = gen_vaddwod_s, +- .fno = gen_helper_vaddwod_w_h, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vaddwod_d_w, +- .fniv = gen_vaddwod_s, +- .fno = gen_helper_vaddwod_d_w, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- { +- .fno = gen_helper_vaddwod_q_d, +- .vece = MO_128 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vaddwod_h_b, LSX, gvec_vvv, MO_8, do_vaddwod_s) +-TRANS(vaddwod_w_h, LSX, gvec_vvv, MO_16, do_vaddwod_s) +-TRANS(vaddwod_d_w, LSX, gvec_vvv, MO_32, do_vaddwod_s) +-TRANS(vaddwod_q_d, LSX, gvec_vvv, MO_64, do_vaddwod_s) +-TRANS(xvaddwod_h_b, LASX, gvec_xxx, MO_8, do_vaddwod_s) +-TRANS(xvaddwod_w_h, LASX, gvec_xxx, MO_16, do_vaddwod_s) +-TRANS(xvaddwod_d_w, LASX, gvec_xxx, MO_32, do_vaddwod_s) +-TRANS(xvaddwod_q_d, LASX, gvec_xxx, MO_64, do_vaddwod_s) +- +- +-static void gen_vsubwev_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, t2; +- +- int halfbits = 4 << vece; +- +- t1 = tcg_temp_new_vec_matching(a); +- t2 = tcg_temp_new_vec_matching(b); +- +- /* Sign-extend the even elements from a */ +- tcg_gen_shli_vec(vece, t1, a, halfbits); +- tcg_gen_sari_vec(vece, t1, t1, halfbits); +- +- /* Sign-extend the even elements from b */ +- tcg_gen_shli_vec(vece, t2, b, halfbits); +- tcg_gen_sari_vec(vece, t2, t2, halfbits); +- +- tcg_gen_sub_vec(vece, t, t1, t2); +-} +- +-static void gen_vsubwev_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1, t2; +- +- t1 = tcg_temp_new_i32(); +- t2 = tcg_temp_new_i32(); +- tcg_gen_ext16s_i32(t1, a); +- tcg_gen_ext16s_i32(t2, b); +- tcg_gen_sub_i32(t, t1, t2); +-} +- +-static void gen_vsubwev_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1, t2; +- +- t1 = tcg_temp_new_i64(); +- t2 = tcg_temp_new_i64(); +- tcg_gen_ext32s_i64(t1, a); +- tcg_gen_ext32s_i64(t2, b); +- tcg_gen_sub_i64(t, t1, t2); +-} +- +-static void do_vsubwev_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_shli_vec, INDEX_op_sari_vec, INDEX_op_sub_vec, 0 +- }; +- static const GVecGen3 op[4] = { +- { +- .fniv = gen_vsubwev_s, +- .fno = gen_helper_vsubwev_h_b, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vsubwev_w_h, +- .fniv = gen_vsubwev_s, +- .fno = gen_helper_vsubwev_w_h, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vsubwev_d_w, +- .fniv = gen_vsubwev_s, +- .fno = gen_helper_vsubwev_d_w, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- { +- .fno = gen_helper_vsubwev_q_d, +- .vece = MO_128 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vsubwev_h_b, LSX, gvec_vvv, MO_8, do_vsubwev_s) +-TRANS(vsubwev_w_h, LSX, gvec_vvv, MO_16, do_vsubwev_s) +-TRANS(vsubwev_d_w, LSX, gvec_vvv, MO_32, do_vsubwev_s) +-TRANS(vsubwev_q_d, LSX, gvec_vvv, MO_64, do_vsubwev_s) +-TRANS(xvsubwev_h_b, LASX, gvec_xxx, MO_8, do_vsubwev_s) +-TRANS(xvsubwev_w_h, LASX, gvec_xxx, MO_16, do_vsubwev_s) +-TRANS(xvsubwev_d_w, LASX, gvec_xxx, MO_32, do_vsubwev_s) +-TRANS(xvsubwev_q_d, LASX, gvec_xxx, MO_64, do_vsubwev_s) +- +-static void gen_vsubwod_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, t2; +- +- int halfbits = 4 << vece; +- +- t1 = tcg_temp_new_vec_matching(a); +- t2 = tcg_temp_new_vec_matching(b); +- +- /* Sign-extend the odd elements for vector */ +- tcg_gen_sari_vec(vece, t1, a, halfbits); +- tcg_gen_sari_vec(vece, t2, b, halfbits); +- +- tcg_gen_sub_vec(vece, t, t1, t2); +-} +- +-static void gen_vsubwod_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1, t2; +- +- t1 = tcg_temp_new_i32(); +- t2 = tcg_temp_new_i32(); +- tcg_gen_sari_i32(t1, a, 16); +- tcg_gen_sari_i32(t2, b, 16); +- tcg_gen_sub_i32(t, t1, t2); +-} +- +-static void gen_vsubwod_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1, t2; +- +- t1 = tcg_temp_new_i64(); +- t2 = tcg_temp_new_i64(); +- tcg_gen_sari_i64(t1, a, 32); +- tcg_gen_sari_i64(t2, b, 32); +- tcg_gen_sub_i64(t, t1, t2); +-} +- +-static void do_vsubwod_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_sari_vec, INDEX_op_sub_vec, 0 +- }; +- static const GVecGen3 op[4] = { +- { +- .fniv = gen_vsubwod_s, +- .fno = gen_helper_vsubwod_h_b, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vsubwod_w_h, +- .fniv = gen_vsubwod_s, +- .fno = gen_helper_vsubwod_w_h, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vsubwod_d_w, +- .fniv = gen_vsubwod_s, +- .fno = gen_helper_vsubwod_d_w, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- { +- .fno = gen_helper_vsubwod_q_d, +- .vece = MO_128 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vsubwod_h_b, LSX, gvec_vvv, MO_8, do_vsubwod_s) +-TRANS(vsubwod_w_h, LSX, gvec_vvv, MO_16, do_vsubwod_s) +-TRANS(vsubwod_d_w, LSX, gvec_vvv, MO_32, do_vsubwod_s) +-TRANS(vsubwod_q_d, LSX, gvec_vvv, MO_64, do_vsubwod_s) +-TRANS(xvsubwod_h_b, LASX, gvec_xxx, MO_8, do_vsubwod_s) +-TRANS(xvsubwod_w_h, LASX, gvec_xxx, MO_16, do_vsubwod_s) +-TRANS(xvsubwod_d_w, LASX, gvec_xxx, MO_32, do_vsubwod_s) +-TRANS(xvsubwod_q_d, LASX, gvec_xxx, MO_64, do_vsubwod_s) +- +-static void gen_vaddwev_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, t2, t3; +- +- t1 = tcg_temp_new_vec_matching(a); +- t2 = tcg_temp_new_vec_matching(b); +- t3 = tcg_constant_vec_matching(t, vece, MAKE_64BIT_MASK(0, 4 << vece)); +- tcg_gen_and_vec(vece, t1, a, t3); +- tcg_gen_and_vec(vece, t2, b, t3); +- tcg_gen_add_vec(vece, t, t1, t2); +-} +- +-static void gen_vaddwev_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1, t2; +- +- t1 = tcg_temp_new_i32(); +- t2 = tcg_temp_new_i32(); +- tcg_gen_ext16u_i32(t1, a); +- tcg_gen_ext16u_i32(t2, b); +- tcg_gen_add_i32(t, t1, t2); +-} +- +-static void gen_vaddwev_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1, t2; +- +- t1 = tcg_temp_new_i64(); +- t2 = tcg_temp_new_i64(); +- tcg_gen_ext32u_i64(t1, a); +- tcg_gen_ext32u_i64(t2, b); +- tcg_gen_add_i64(t, t1, t2); +-} +- +-static void do_vaddwev_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_add_vec, 0 +- }; +- static const GVecGen3 op[4] = { +- { +- .fniv = gen_vaddwev_u, +- .fno = gen_helper_vaddwev_h_bu, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vaddwev_w_hu, +- .fniv = gen_vaddwev_u, +- .fno = gen_helper_vaddwev_w_hu, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vaddwev_d_wu, +- .fniv = gen_vaddwev_u, +- .fno = gen_helper_vaddwev_d_wu, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- { +- .fno = gen_helper_vaddwev_q_du, +- .vece = MO_128 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vaddwev_h_bu, LSX, gvec_vvv, MO_8, do_vaddwev_u) +-TRANS(vaddwev_w_hu, LSX, gvec_vvv, MO_16, do_vaddwev_u) +-TRANS(vaddwev_d_wu, LSX, gvec_vvv, MO_32, do_vaddwev_u) +-TRANS(vaddwev_q_du, LSX, gvec_vvv, MO_64, do_vaddwev_u) +-TRANS(xvaddwev_h_bu, LASX, gvec_xxx, MO_8, do_vaddwev_u) +-TRANS(xvaddwev_w_hu, LASX, gvec_xxx, MO_16, do_vaddwev_u) +-TRANS(xvaddwev_d_wu, LASX, gvec_xxx, MO_32, do_vaddwev_u) +-TRANS(xvaddwev_q_du, LASX, gvec_xxx, MO_64, do_vaddwev_u) +- +-static void gen_vaddwod_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, t2; +- +- int halfbits = 4 << vece; +- +- t1 = tcg_temp_new_vec_matching(a); +- t2 = tcg_temp_new_vec_matching(b); +- +- /* Zero-extend the odd elements for vector */ +- tcg_gen_shri_vec(vece, t1, a, halfbits); +- tcg_gen_shri_vec(vece, t2, b, halfbits); +- +- tcg_gen_add_vec(vece, t, t1, t2); +-} +- +-static void gen_vaddwod_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1, t2; +- +- t1 = tcg_temp_new_i32(); +- t2 = tcg_temp_new_i32(); +- tcg_gen_shri_i32(t1, a, 16); +- tcg_gen_shri_i32(t2, b, 16); +- tcg_gen_add_i32(t, t1, t2); +-} +- +-static void gen_vaddwod_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1, t2; +- +- t1 = tcg_temp_new_i64(); +- t2 = tcg_temp_new_i64(); +- tcg_gen_shri_i64(t1, a, 32); +- tcg_gen_shri_i64(t2, b, 32); +- tcg_gen_add_i64(t, t1, t2); +-} +- +-static void do_vaddwod_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_shri_vec, INDEX_op_add_vec, 0 +- }; +- static const GVecGen3 op[4] = { +- { +- .fniv = gen_vaddwod_u, +- .fno = gen_helper_vaddwod_h_bu, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vaddwod_w_hu, +- .fniv = gen_vaddwod_u, +- .fno = gen_helper_vaddwod_w_hu, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vaddwod_d_wu, +- .fniv = gen_vaddwod_u, +- .fno = gen_helper_vaddwod_d_wu, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- { +- .fno = gen_helper_vaddwod_q_du, +- .vece = MO_128 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vaddwod_h_bu, LSX, gvec_vvv, MO_8, do_vaddwod_u) +-TRANS(vaddwod_w_hu, LSX, gvec_vvv, MO_16, do_vaddwod_u) +-TRANS(vaddwod_d_wu, LSX, gvec_vvv, MO_32, do_vaddwod_u) +-TRANS(vaddwod_q_du, LSX, gvec_vvv, MO_64, do_vaddwod_u) +-TRANS(xvaddwod_h_bu, LASX, gvec_xxx, MO_8, do_vaddwod_u) +-TRANS(xvaddwod_w_hu, LASX, gvec_xxx, MO_16, do_vaddwod_u) +-TRANS(xvaddwod_d_wu, LASX, gvec_xxx, MO_32, do_vaddwod_u) +-TRANS(xvaddwod_q_du, LASX, gvec_xxx, MO_64, do_vaddwod_u) +- +-static void gen_vsubwev_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, t2, t3; +- +- t1 = tcg_temp_new_vec_matching(a); +- t2 = tcg_temp_new_vec_matching(b); +- t3 = tcg_constant_vec_matching(t, vece, MAKE_64BIT_MASK(0, 4 << vece)); +- tcg_gen_and_vec(vece, t1, a, t3); +- tcg_gen_and_vec(vece, t2, b, t3); +- tcg_gen_sub_vec(vece, t, t1, t2); +-} +- +-static void gen_vsubwev_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1, t2; +- +- t1 = tcg_temp_new_i32(); +- t2 = tcg_temp_new_i32(); +- tcg_gen_ext16u_i32(t1, a); +- tcg_gen_ext16u_i32(t2, b); +- tcg_gen_sub_i32(t, t1, t2); +-} +- +-static void gen_vsubwev_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1, t2; +- +- t1 = tcg_temp_new_i64(); +- t2 = tcg_temp_new_i64(); +- tcg_gen_ext32u_i64(t1, a); +- tcg_gen_ext32u_i64(t2, b); +- tcg_gen_sub_i64(t, t1, t2); +-} +- +-static void do_vsubwev_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_sub_vec, 0 +- }; +- static const GVecGen3 op[4] = { +- { +- .fniv = gen_vsubwev_u, +- .fno = gen_helper_vsubwev_h_bu, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vsubwev_w_hu, +- .fniv = gen_vsubwev_u, +- .fno = gen_helper_vsubwev_w_hu, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vsubwev_d_wu, +- .fniv = gen_vsubwev_u, +- .fno = gen_helper_vsubwev_d_wu, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- { +- .fno = gen_helper_vsubwev_q_du, +- .vece = MO_128 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vsubwev_h_bu, LSX, gvec_vvv, MO_8, do_vsubwev_u) +-TRANS(vsubwev_w_hu, LSX, gvec_vvv, MO_16, do_vsubwev_u) +-TRANS(vsubwev_d_wu, LSX, gvec_vvv, MO_32, do_vsubwev_u) +-TRANS(vsubwev_q_du, LSX, gvec_vvv, MO_64, do_vsubwev_u) +-TRANS(xvsubwev_h_bu, LASX, gvec_xxx, MO_8, do_vsubwev_u) +-TRANS(xvsubwev_w_hu, LASX, gvec_xxx, MO_16, do_vsubwev_u) +-TRANS(xvsubwev_d_wu, LASX, gvec_xxx, MO_32, do_vsubwev_u) +-TRANS(xvsubwev_q_du, LASX, gvec_xxx, MO_64, do_vsubwev_u) +- +-static void gen_vsubwod_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, t2; +- +- int halfbits = 4 << vece; +- +- t1 = tcg_temp_new_vec_matching(a); +- t2 = tcg_temp_new_vec_matching(b); +- +- /* Zero-extend the odd elements for vector */ +- tcg_gen_shri_vec(vece, t1, a, halfbits); +- tcg_gen_shri_vec(vece, t2, b, halfbits); +- +- tcg_gen_sub_vec(vece, t, t1, t2); +-} +- +-static void gen_vsubwod_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1, t2; +- +- t1 = tcg_temp_new_i32(); +- t2 = tcg_temp_new_i32(); +- tcg_gen_shri_i32(t1, a, 16); +- tcg_gen_shri_i32(t2, b, 16); +- tcg_gen_sub_i32(t, t1, t2); +-} +- +-static void gen_vsubwod_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1, t2; +- +- t1 = tcg_temp_new_i64(); +- t2 = tcg_temp_new_i64(); +- tcg_gen_shri_i64(t1, a, 32); +- tcg_gen_shri_i64(t2, b, 32); +- tcg_gen_sub_i64(t, t1, t2); +-} +- +-static void do_vsubwod_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_shri_vec, INDEX_op_sub_vec, 0 +- }; +- static const GVecGen3 op[4] = { +- { +- .fniv = gen_vsubwod_u, +- .fno = gen_helper_vsubwod_h_bu, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vsubwod_w_hu, +- .fniv = gen_vsubwod_u, +- .fno = gen_helper_vsubwod_w_hu, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vsubwod_d_wu, +- .fniv = gen_vsubwod_u, +- .fno = gen_helper_vsubwod_d_wu, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- { +- .fno = gen_helper_vsubwod_q_du, +- .vece = MO_128 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vsubwod_h_bu, LSX, gvec_vvv, MO_8, do_vsubwod_u) +-TRANS(vsubwod_w_hu, LSX, gvec_vvv, MO_16, do_vsubwod_u) +-TRANS(vsubwod_d_wu, LSX, gvec_vvv, MO_32, do_vsubwod_u) +-TRANS(vsubwod_q_du, LSX, gvec_vvv, MO_64, do_vsubwod_u) +-TRANS(xvsubwod_h_bu, LASX, gvec_xxx, MO_8, do_vsubwod_u) +-TRANS(xvsubwod_w_hu, LASX, gvec_xxx, MO_16, do_vsubwod_u) +-TRANS(xvsubwod_d_wu, LASX, gvec_xxx, MO_32, do_vsubwod_u) +-TRANS(xvsubwod_q_du, LASX, gvec_xxx, MO_64, do_vsubwod_u) +- +-static void gen_vaddwev_u_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, t2, t3; +- +- int halfbits = 4 << vece; +- +- t1 = tcg_temp_new_vec_matching(a); +- t2 = tcg_temp_new_vec_matching(b); +- t3 = tcg_constant_vec_matching(t, vece, MAKE_64BIT_MASK(0, halfbits)); +- +- /* Zero-extend the even elements from a */ +- tcg_gen_and_vec(vece, t1, a, t3); +- +- /* Sign-extend the even elements from b */ +- tcg_gen_shli_vec(vece, t2, b, halfbits); +- tcg_gen_sari_vec(vece, t2, t2, halfbits); +- +- tcg_gen_add_vec(vece, t, t1, t2); +-} +- +-static void gen_vaddwev_w_hu_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1, t2; +- +- t1 = tcg_temp_new_i32(); +- t2 = tcg_temp_new_i32(); +- tcg_gen_ext16u_i32(t1, a); +- tcg_gen_ext16s_i32(t2, b); +- tcg_gen_add_i32(t, t1, t2); +-} +- +-static void gen_vaddwev_d_wu_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1, t2; +- +- t1 = tcg_temp_new_i64(); +- t2 = tcg_temp_new_i64(); +- tcg_gen_ext32u_i64(t1, a); +- tcg_gen_ext32s_i64(t2, b); +- tcg_gen_add_i64(t, t1, t2); +-} +- +-static void do_vaddwev_u_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_shli_vec, INDEX_op_sari_vec, INDEX_op_add_vec, 0 +- }; +- static const GVecGen3 op[4] = { +- { +- .fniv = gen_vaddwev_u_s, +- .fno = gen_helper_vaddwev_h_bu_b, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vaddwev_w_hu_h, +- .fniv = gen_vaddwev_u_s, +- .fno = gen_helper_vaddwev_w_hu_h, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vaddwev_d_wu_w, +- .fniv = gen_vaddwev_u_s, +- .fno = gen_helper_vaddwev_d_wu_w, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- { +- .fno = gen_helper_vaddwev_q_du_d, +- .vece = MO_128 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vaddwev_h_bu_b, LSX, gvec_vvv, MO_8, do_vaddwev_u_s) +-TRANS(vaddwev_w_hu_h, LSX, gvec_vvv, MO_16, do_vaddwev_u_s) +-TRANS(vaddwev_d_wu_w, LSX, gvec_vvv, MO_32, do_vaddwev_u_s) +-TRANS(vaddwev_q_du_d, LSX, gvec_vvv, MO_64, do_vaddwev_u_s) +-TRANS(xvaddwev_h_bu_b, LASX, gvec_xxx, MO_8, do_vaddwev_u_s) +-TRANS(xvaddwev_w_hu_h, LASX, gvec_xxx, MO_16, do_vaddwev_u_s) +-TRANS(xvaddwev_d_wu_w, LASX, gvec_xxx, MO_32, do_vaddwev_u_s) +-TRANS(xvaddwev_q_du_d, LASX, gvec_xxx, MO_64, do_vaddwev_u_s) +- +-static void gen_vaddwod_u_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, t2; +- +- int halfbits = 4 << vece; +- +- t1 = tcg_temp_new_vec_matching(a); +- t2 = tcg_temp_new_vec_matching(b); +- +- /* Zero-extend the odd elements from a */ +- tcg_gen_shri_vec(vece, t1, a, halfbits); +- /* Sign-extend the odd elements from b */ +- tcg_gen_sari_vec(vece, t2, b, halfbits); +- +- tcg_gen_add_vec(vece, t, t1, t2); +-} +- +-static void gen_vaddwod_w_hu_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1, t2; +- +- t1 = tcg_temp_new_i32(); +- t2 = tcg_temp_new_i32(); +- tcg_gen_shri_i32(t1, a, 16); +- tcg_gen_sari_i32(t2, b, 16); +- tcg_gen_add_i32(t, t1, t2); +-} +- +-static void gen_vaddwod_d_wu_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1, t2; +- +- t1 = tcg_temp_new_i64(); +- t2 = tcg_temp_new_i64(); +- tcg_gen_shri_i64(t1, a, 32); +- tcg_gen_sari_i64(t2, b, 32); +- tcg_gen_add_i64(t, t1, t2); +-} +- +-static void do_vaddwod_u_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_shri_vec, INDEX_op_sari_vec, INDEX_op_add_vec, 0 +- }; +- static const GVecGen3 op[4] = { +- { +- .fniv = gen_vaddwod_u_s, +- .fno = gen_helper_vaddwod_h_bu_b, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vaddwod_w_hu_h, +- .fniv = gen_vaddwod_u_s, +- .fno = gen_helper_vaddwod_w_hu_h, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vaddwod_d_wu_w, +- .fniv = gen_vaddwod_u_s, +- .fno = gen_helper_vaddwod_d_wu_w, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- { +- .fno = gen_helper_vaddwod_q_du_d, +- .vece = MO_128 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vaddwod_h_bu_b, LSX, gvec_vvv, MO_8, do_vaddwod_u_s) +-TRANS(vaddwod_w_hu_h, LSX, gvec_vvv, MO_16, do_vaddwod_u_s) +-TRANS(vaddwod_d_wu_w, LSX, gvec_vvv, MO_32, do_vaddwod_u_s) +-TRANS(vaddwod_q_du_d, LSX, gvec_vvv, MO_64, do_vaddwod_u_s) +-TRANS(xvaddwod_h_bu_b, LSX, gvec_xxx, MO_8, do_vaddwod_u_s) +-TRANS(xvaddwod_w_hu_h, LSX, gvec_xxx, MO_16, do_vaddwod_u_s) +-TRANS(xvaddwod_d_wu_w, LSX, gvec_xxx, MO_32, do_vaddwod_u_s) +-TRANS(xvaddwod_q_du_d, LSX, gvec_xxx, MO_64, do_vaddwod_u_s) +- +-static void do_vavg(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b, +- void (*gen_shr_vec)(unsigned, TCGv_vec, +- TCGv_vec, int64_t), +- void (*gen_round_vec)(unsigned, TCGv_vec, +- TCGv_vec, TCGv_vec)) +-{ +- TCGv_vec tmp = tcg_temp_new_vec_matching(t); +- gen_round_vec(vece, tmp, a, b); +- tcg_gen_and_vec(vece, tmp, tmp, tcg_constant_vec_matching(t, vece, 1)); +- gen_shr_vec(vece, a, a, 1); +- gen_shr_vec(vece, b, b, 1); +- tcg_gen_add_vec(vece, t, a, b); +- tcg_gen_add_vec(vece, t, t, tmp); +-} +- +-static void gen_vavg_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- do_vavg(vece, t, a, b, tcg_gen_sari_vec, tcg_gen_and_vec); +-} +- +-static void gen_vavg_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- do_vavg(vece, t, a, b, tcg_gen_shri_vec, tcg_gen_and_vec); +-} +- +-static void gen_vavgr_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- do_vavg(vece, t, a, b, tcg_gen_sari_vec, tcg_gen_or_vec); +-} +- +-static void gen_vavgr_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- do_vavg(vece, t, a, b, tcg_gen_shri_vec, tcg_gen_or_vec); +-} +- +-static void do_vavg_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_sari_vec, INDEX_op_add_vec, 0 +- }; +- static const GVecGen3 op[4] = { +- { +- .fniv = gen_vavg_s, +- .fno = gen_helper_vavg_b, +- .opt_opc = vecop_list, +- .vece = MO_8 +- }, +- { +- .fniv = gen_vavg_s, +- .fno = gen_helper_vavg_h, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fniv = gen_vavg_s, +- .fno = gen_helper_vavg_w, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fniv = gen_vavg_s, +- .fno = gen_helper_vavg_d, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-static void do_vavg_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_shri_vec, INDEX_op_add_vec, 0 +- }; +- static const GVecGen3 op[4] = { +- { +- .fniv = gen_vavg_u, +- .fno = gen_helper_vavg_bu, +- .opt_opc = vecop_list, +- .vece = MO_8 +- }, +- { +- .fniv = gen_vavg_u, +- .fno = gen_helper_vavg_hu, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fniv = gen_vavg_u, +- .fno = gen_helper_vavg_wu, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fniv = gen_vavg_u, +- .fno = gen_helper_vavg_du, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vavg_b, LSX, gvec_vvv, MO_8, do_vavg_s) +-TRANS(vavg_h, LSX, gvec_vvv, MO_16, do_vavg_s) +-TRANS(vavg_w, LSX, gvec_vvv, MO_32, do_vavg_s) +-TRANS(vavg_d, LSX, gvec_vvv, MO_64, do_vavg_s) +-TRANS(vavg_bu, LSX, gvec_vvv, MO_8, do_vavg_u) +-TRANS(vavg_hu, LSX, gvec_vvv, MO_16, do_vavg_u) +-TRANS(vavg_wu, LSX, gvec_vvv, MO_32, do_vavg_u) +-TRANS(vavg_du, LSX, gvec_vvv, MO_64, do_vavg_u) +-TRANS(xvavg_b, LASX, gvec_xxx, MO_8, do_vavg_s) +-TRANS(xvavg_h, LASX, gvec_xxx, MO_16, do_vavg_s) +-TRANS(xvavg_w, LASX, gvec_xxx, MO_32, do_vavg_s) +-TRANS(xvavg_d, LASX, gvec_xxx, MO_64, do_vavg_s) +-TRANS(xvavg_bu, LASX, gvec_xxx, MO_8, do_vavg_u) +-TRANS(xvavg_hu, LASX, gvec_xxx, MO_16, do_vavg_u) +-TRANS(xvavg_wu, LASX, gvec_xxx, MO_32, do_vavg_u) +-TRANS(xvavg_du, LASX, gvec_xxx, MO_64, do_vavg_u) +- +-static void do_vavgr_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_sari_vec, INDEX_op_add_vec, 0 +- }; +- static const GVecGen3 op[4] = { +- { +- .fniv = gen_vavgr_s, +- .fno = gen_helper_vavgr_b, +- .opt_opc = vecop_list, +- .vece = MO_8 +- }, +- { +- .fniv = gen_vavgr_s, +- .fno = gen_helper_vavgr_h, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fniv = gen_vavgr_s, +- .fno = gen_helper_vavgr_w, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fniv = gen_vavgr_s, +- .fno = gen_helper_vavgr_d, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-static void do_vavgr_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_shri_vec, INDEX_op_add_vec, 0 +- }; +- static const GVecGen3 op[4] = { +- { +- .fniv = gen_vavgr_u, +- .fno = gen_helper_vavgr_bu, +- .opt_opc = vecop_list, +- .vece = MO_8 +- }, +- { +- .fniv = gen_vavgr_u, +- .fno = gen_helper_vavgr_hu, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fniv = gen_vavgr_u, +- .fno = gen_helper_vavgr_wu, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fniv = gen_vavgr_u, +- .fno = gen_helper_vavgr_du, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vavgr_b, LSX, gvec_vvv, MO_8, do_vavgr_s) +-TRANS(vavgr_h, LSX, gvec_vvv, MO_16, do_vavgr_s) +-TRANS(vavgr_w, LSX, gvec_vvv, MO_32, do_vavgr_s) +-TRANS(vavgr_d, LSX, gvec_vvv, MO_64, do_vavgr_s) +-TRANS(vavgr_bu, LSX, gvec_vvv, MO_8, do_vavgr_u) +-TRANS(vavgr_hu, LSX, gvec_vvv, MO_16, do_vavgr_u) +-TRANS(vavgr_wu, LSX, gvec_vvv, MO_32, do_vavgr_u) +-TRANS(vavgr_du, LSX, gvec_vvv, MO_64, do_vavgr_u) +-TRANS(xvavgr_b, LASX, gvec_xxx, MO_8, do_vavgr_s) +-TRANS(xvavgr_h, LASX, gvec_xxx, MO_16, do_vavgr_s) +-TRANS(xvavgr_w, LASX, gvec_xxx, MO_32, do_vavgr_s) +-TRANS(xvavgr_d, LASX, gvec_xxx, MO_64, do_vavgr_s) +-TRANS(xvavgr_bu, LASX, gvec_xxx, MO_8, do_vavgr_u) +-TRANS(xvavgr_hu, LASX, gvec_xxx, MO_16, do_vavgr_u) +-TRANS(xvavgr_wu, LASX, gvec_xxx, MO_32, do_vavgr_u) +-TRANS(xvavgr_du, LASX, gvec_xxx, MO_64, do_vavgr_u) +- +-static void gen_vabsd_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- tcg_gen_smax_vec(vece, t, a, b); +- tcg_gen_smin_vec(vece, a, a, b); +- tcg_gen_sub_vec(vece, t, t, a); +-} +- +-static void do_vabsd_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_smax_vec, INDEX_op_smin_vec, INDEX_op_sub_vec, 0 +- }; +- static const GVecGen3 op[4] = { +- { +- .fniv = gen_vabsd_s, +- .fno = gen_helper_vabsd_b, +- .opt_opc = vecop_list, +- .vece = MO_8 +- }, +- { +- .fniv = gen_vabsd_s, +- .fno = gen_helper_vabsd_h, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fniv = gen_vabsd_s, +- .fno = gen_helper_vabsd_w, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fniv = gen_vabsd_s, +- .fno = gen_helper_vabsd_d, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-static void gen_vabsd_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- tcg_gen_umax_vec(vece, t, a, b); +- tcg_gen_umin_vec(vece, a, a, b); +- tcg_gen_sub_vec(vece, t, t, a); +-} +- +-static void do_vabsd_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_umax_vec, INDEX_op_umin_vec, INDEX_op_sub_vec, 0 +- }; +- static const GVecGen3 op[4] = { +- { +- .fniv = gen_vabsd_u, +- .fno = gen_helper_vabsd_bu, +- .opt_opc = vecop_list, +- .vece = MO_8 +- }, +- { +- .fniv = gen_vabsd_u, +- .fno = gen_helper_vabsd_hu, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fniv = gen_vabsd_u, +- .fno = gen_helper_vabsd_wu, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fniv = gen_vabsd_u, +- .fno = gen_helper_vabsd_du, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vabsd_b, LSX, gvec_vvv, MO_8, do_vabsd_s) +-TRANS(vabsd_h, LSX, gvec_vvv, MO_16, do_vabsd_s) +-TRANS(vabsd_w, LSX, gvec_vvv, MO_32, do_vabsd_s) +-TRANS(vabsd_d, LSX, gvec_vvv, MO_64, do_vabsd_s) +-TRANS(vabsd_bu, LSX, gvec_vvv, MO_8, do_vabsd_u) +-TRANS(vabsd_hu, LSX, gvec_vvv, MO_16, do_vabsd_u) +-TRANS(vabsd_wu, LSX, gvec_vvv, MO_32, do_vabsd_u) +-TRANS(vabsd_du, LSX, gvec_vvv, MO_64, do_vabsd_u) +-TRANS(xvabsd_b, LASX, gvec_xxx, MO_8, do_vabsd_s) +-TRANS(xvabsd_h, LASX, gvec_xxx, MO_16, do_vabsd_s) +-TRANS(xvabsd_w, LASX, gvec_xxx, MO_32, do_vabsd_s) +-TRANS(xvabsd_d, LASX, gvec_xxx, MO_64, do_vabsd_s) +-TRANS(xvabsd_bu, LASX, gvec_xxx, MO_8, do_vabsd_u) +-TRANS(xvabsd_hu, LASX, gvec_xxx, MO_16, do_vabsd_u) +-TRANS(xvabsd_wu, LASX, gvec_xxx, MO_32, do_vabsd_u) +-TRANS(xvabsd_du, LASX, gvec_xxx, MO_64, do_vabsd_u) +- +-static void gen_vadda(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, t2; +- +- t1 = tcg_temp_new_vec_matching(a); +- t2 = tcg_temp_new_vec_matching(b); +- +- tcg_gen_abs_vec(vece, t1, a); +- tcg_gen_abs_vec(vece, t2, b); +- tcg_gen_add_vec(vece, t, t1, t2); +-} +- +-static void do_vadda(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_abs_vec, INDEX_op_add_vec, 0 +- }; +- static const GVecGen3 op[4] = { +- { +- .fniv = gen_vadda, +- .fno = gen_helper_vadda_b, +- .opt_opc = vecop_list, +- .vece = MO_8 +- }, +- { +- .fniv = gen_vadda, +- .fno = gen_helper_vadda_h, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fniv = gen_vadda, +- .fno = gen_helper_vadda_w, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fniv = gen_vadda, +- .fno = gen_helper_vadda_d, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vadda_b, LSX, gvec_vvv, MO_8, do_vadda) +-TRANS(vadda_h, LSX, gvec_vvv, MO_16, do_vadda) +-TRANS(vadda_w, LSX, gvec_vvv, MO_32, do_vadda) +-TRANS(vadda_d, LSX, gvec_vvv, MO_64, do_vadda) +-TRANS(xvadda_b, LASX, gvec_xxx, MO_8, do_vadda) +-TRANS(xvadda_h, LASX, gvec_xxx, MO_16, do_vadda) +-TRANS(xvadda_w, LASX, gvec_xxx, MO_32, do_vadda) +-TRANS(xvadda_d, LASX, gvec_xxx, MO_64, do_vadda) +- +-TRANS(vmax_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_smax) +-TRANS(vmax_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_smax) +-TRANS(vmax_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_smax) +-TRANS(vmax_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_smax) +-TRANS(vmax_bu, LSX, gvec_vvv, MO_8, tcg_gen_gvec_umax) +-TRANS(vmax_hu, LSX, gvec_vvv, MO_16, tcg_gen_gvec_umax) +-TRANS(vmax_wu, LSX, gvec_vvv, MO_32, tcg_gen_gvec_umax) +-TRANS(vmax_du, LSX, gvec_vvv, MO_64, tcg_gen_gvec_umax) +-TRANS(xvmax_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_smax) +-TRANS(xvmax_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_smax) +-TRANS(xvmax_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_smax) +-TRANS(xvmax_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_smax) +-TRANS(xvmax_bu, LASX, gvec_xxx, MO_8, tcg_gen_gvec_umax) +-TRANS(xvmax_hu, LASX, gvec_xxx, MO_16, tcg_gen_gvec_umax) +-TRANS(xvmax_wu, LASX, gvec_xxx, MO_32, tcg_gen_gvec_umax) +-TRANS(xvmax_du, LASX, gvec_xxx, MO_64, tcg_gen_gvec_umax) +- +-TRANS(vmin_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_smin) +-TRANS(vmin_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_smin) +-TRANS(vmin_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_smin) +-TRANS(vmin_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_smin) +-TRANS(vmin_bu, LSX, gvec_vvv, MO_8, tcg_gen_gvec_umin) +-TRANS(vmin_hu, LSX, gvec_vvv, MO_16, tcg_gen_gvec_umin) +-TRANS(vmin_wu, LSX, gvec_vvv, MO_32, tcg_gen_gvec_umin) +-TRANS(vmin_du, LSX, gvec_vvv, MO_64, tcg_gen_gvec_umin) +-TRANS(xvmin_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_smin) +-TRANS(xvmin_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_smin) +-TRANS(xvmin_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_smin) +-TRANS(xvmin_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_smin) +-TRANS(xvmin_bu, LASX, gvec_xxx, MO_8, tcg_gen_gvec_umin) +-TRANS(xvmin_hu, LASX, gvec_xxx, MO_16, tcg_gen_gvec_umin) +-TRANS(xvmin_wu, LASX, gvec_xxx, MO_32, tcg_gen_gvec_umin) +-TRANS(xvmin_du, LASX, gvec_xxx, MO_64, tcg_gen_gvec_umin) +- +-static void gen_vmini_s(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) +-{ +- tcg_gen_smin_vec(vece, t, a, tcg_constant_vec_matching(t, vece, imm)); +-} +- +-static void gen_vmini_u(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) +-{ +- tcg_gen_umin_vec(vece, t, a, tcg_constant_vec_matching(t, vece, imm)); +-} +- +-static void gen_vmaxi_s(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) +-{ +- tcg_gen_smax_vec(vece, t, a, tcg_constant_vec_matching(t, vece, imm)); +-} +- +-static void gen_vmaxi_u(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) +-{ +- tcg_gen_umax_vec(vece, t, a, tcg_constant_vec_matching(t, vece, imm)); +-} +- +-static void do_vmini_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- int64_t imm, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_smin_vec, 0 +- }; +- static const GVecGen2i op[4] = { +- { +- .fniv = gen_vmini_s, +- .fnoi = gen_helper_vmini_b, +- .opt_opc = vecop_list, +- .vece = MO_8 +- }, +- { +- .fniv = gen_vmini_s, +- .fnoi = gen_helper_vmini_h, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fniv = gen_vmini_s, +- .fnoi = gen_helper_vmini_w, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fniv = gen_vmini_s, +- .fnoi = gen_helper_vmini_d, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op[vece]); +-} +- +-static void do_vmini_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- int64_t imm, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_umin_vec, 0 +- }; +- static const GVecGen2i op[4] = { +- { +- .fniv = gen_vmini_u, +- .fnoi = gen_helper_vmini_bu, +- .opt_opc = vecop_list, +- .vece = MO_8 +- }, +- { +- .fniv = gen_vmini_u, +- .fnoi = gen_helper_vmini_hu, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fniv = gen_vmini_u, +- .fnoi = gen_helper_vmini_wu, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fniv = gen_vmini_u, +- .fnoi = gen_helper_vmini_du, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op[vece]); +-} +- +-TRANS(vmini_b, LSX, gvec_vv_i, MO_8, do_vmini_s) +-TRANS(vmini_h, LSX, gvec_vv_i, MO_16, do_vmini_s) +-TRANS(vmini_w, LSX, gvec_vv_i, MO_32, do_vmini_s) +-TRANS(vmini_d, LSX, gvec_vv_i, MO_64, do_vmini_s) +-TRANS(vmini_bu, LSX, gvec_vv_i, MO_8, do_vmini_u) +-TRANS(vmini_hu, LSX, gvec_vv_i, MO_16, do_vmini_u) +-TRANS(vmini_wu, LSX, gvec_vv_i, MO_32, do_vmini_u) +-TRANS(vmini_du, LSX, gvec_vv_i, MO_64, do_vmini_u) +-TRANS(xvmini_b, LASX, gvec_xx_i, MO_8, do_vmini_s) +-TRANS(xvmini_h, LASX, gvec_xx_i, MO_16, do_vmini_s) +-TRANS(xvmini_w, LASX, gvec_xx_i, MO_32, do_vmini_s) +-TRANS(xvmini_d, LASX, gvec_xx_i, MO_64, do_vmini_s) +-TRANS(xvmini_bu, LASX, gvec_xx_i, MO_8, do_vmini_u) +-TRANS(xvmini_hu, LASX, gvec_xx_i, MO_16, do_vmini_u) +-TRANS(xvmini_wu, LASX, gvec_xx_i, MO_32, do_vmini_u) +-TRANS(xvmini_du, LASX, gvec_xx_i, MO_64, do_vmini_u) +- +-static void do_vmaxi_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- int64_t imm, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_smax_vec, 0 +- }; +- static const GVecGen2i op[4] = { +- { +- .fniv = gen_vmaxi_s, +- .fnoi = gen_helper_vmaxi_b, +- .opt_opc = vecop_list, +- .vece = MO_8 +- }, +- { +- .fniv = gen_vmaxi_s, +- .fnoi = gen_helper_vmaxi_h, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fniv = gen_vmaxi_s, +- .fnoi = gen_helper_vmaxi_w, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fniv = gen_vmaxi_s, +- .fnoi = gen_helper_vmaxi_d, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op[vece]); +-} +- +-static void do_vmaxi_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- int64_t imm, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_umax_vec, 0 +- }; +- static const GVecGen2i op[4] = { +- { +- .fniv = gen_vmaxi_u, +- .fnoi = gen_helper_vmaxi_bu, +- .opt_opc = vecop_list, +- .vece = MO_8 +- }, +- { +- .fniv = gen_vmaxi_u, +- .fnoi = gen_helper_vmaxi_hu, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fniv = gen_vmaxi_u, +- .fnoi = gen_helper_vmaxi_wu, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fniv = gen_vmaxi_u, +- .fnoi = gen_helper_vmaxi_du, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op[vece]); +-} +- +-TRANS(vmaxi_b, LSX, gvec_vv_i, MO_8, do_vmaxi_s) +-TRANS(vmaxi_h, LSX, gvec_vv_i, MO_16, do_vmaxi_s) +-TRANS(vmaxi_w, LSX, gvec_vv_i, MO_32, do_vmaxi_s) +-TRANS(vmaxi_d, LSX, gvec_vv_i, MO_64, do_vmaxi_s) +-TRANS(vmaxi_bu, LSX, gvec_vv_i, MO_8, do_vmaxi_u) +-TRANS(vmaxi_hu, LSX, gvec_vv_i, MO_16, do_vmaxi_u) +-TRANS(vmaxi_wu, LSX, gvec_vv_i, MO_32, do_vmaxi_u) +-TRANS(vmaxi_du, LSX, gvec_vv_i, MO_64, do_vmaxi_u) +-TRANS(xvmaxi_b, LASX, gvec_xx_i, MO_8, do_vmaxi_s) +-TRANS(xvmaxi_h, LASX, gvec_xx_i, MO_16, do_vmaxi_s) +-TRANS(xvmaxi_w, LASX, gvec_xx_i, MO_32, do_vmaxi_s) +-TRANS(xvmaxi_d, LASX, gvec_xx_i, MO_64, do_vmaxi_s) +-TRANS(xvmaxi_bu, LASX, gvec_xx_i, MO_8, do_vmaxi_u) +-TRANS(xvmaxi_hu, LASX, gvec_xx_i, MO_16, do_vmaxi_u) +-TRANS(xvmaxi_wu, LASX, gvec_xx_i, MO_32, do_vmaxi_u) +-TRANS(xvmaxi_du, LASX, gvec_xx_i, MO_64, do_vmaxi_u) +- +-TRANS(vmul_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_mul) +-TRANS(vmul_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_mul) +-TRANS(vmul_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_mul) +-TRANS(vmul_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_mul) +-TRANS(xvmul_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_mul) +-TRANS(xvmul_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_mul) +-TRANS(xvmul_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_mul) +-TRANS(xvmul_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_mul) +- +-static void gen_vmuh_w(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 discard = tcg_temp_new_i32(); +- tcg_gen_muls2_i32(discard, t, a, b); +-} +- +-static void gen_vmuh_d(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 discard = tcg_temp_new_i64(); +- tcg_gen_muls2_i64(discard, t, a, b); +-} +- +-static void do_vmuh_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const GVecGen3 op[4] = { +- { +- .fno = gen_helper_vmuh_b, +- .vece = MO_8 +- }, +- { +- .fno = gen_helper_vmuh_h, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vmuh_w, +- .fno = gen_helper_vmuh_w, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vmuh_d, +- .fno = gen_helper_vmuh_d, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vmuh_b, LSX, gvec_vvv, MO_8, do_vmuh_s) +-TRANS(vmuh_h, LSX, gvec_vvv, MO_16, do_vmuh_s) +-TRANS(vmuh_w, LSX, gvec_vvv, MO_32, do_vmuh_s) +-TRANS(vmuh_d, LSX, gvec_vvv, MO_64, do_vmuh_s) +-TRANS(xvmuh_b, LASX, gvec_xxx, MO_8, do_vmuh_s) +-TRANS(xvmuh_h, LASX, gvec_xxx, MO_16, do_vmuh_s) +-TRANS(xvmuh_w, LASX, gvec_xxx, MO_32, do_vmuh_s) +-TRANS(xvmuh_d, LASX, gvec_xxx, MO_64, do_vmuh_s) +- +-static void gen_vmuh_wu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 discard = tcg_temp_new_i32(); +- tcg_gen_mulu2_i32(discard, t, a, b); +-} +- +-static void gen_vmuh_du(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 discard = tcg_temp_new_i64(); +- tcg_gen_mulu2_i64(discard, t, a, b); +-} +- +-static void do_vmuh_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const GVecGen3 op[4] = { +- { +- .fno = gen_helper_vmuh_bu, +- .vece = MO_8 +- }, +- { +- .fno = gen_helper_vmuh_hu, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vmuh_wu, +- .fno = gen_helper_vmuh_wu, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vmuh_du, +- .fno = gen_helper_vmuh_du, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vmuh_bu, LSX, gvec_vvv, MO_8, do_vmuh_u) +-TRANS(vmuh_hu, LSX, gvec_vvv, MO_16, do_vmuh_u) +-TRANS(vmuh_wu, LSX, gvec_vvv, MO_32, do_vmuh_u) +-TRANS(vmuh_du, LSX, gvec_vvv, MO_64, do_vmuh_u) +-TRANS(xvmuh_bu, LASX, gvec_xxx, MO_8, do_vmuh_u) +-TRANS(xvmuh_hu, LASX, gvec_xxx, MO_16, do_vmuh_u) +-TRANS(xvmuh_wu, LASX, gvec_xxx, MO_32, do_vmuh_u) +-TRANS(xvmuh_du, LASX, gvec_xxx, MO_64, do_vmuh_u) +- +-static void gen_vmulwev_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, t2; +- int halfbits = 4 << vece; +- +- t1 = tcg_temp_new_vec_matching(a); +- t2 = tcg_temp_new_vec_matching(b); +- tcg_gen_shli_vec(vece, t1, a, halfbits); +- tcg_gen_sari_vec(vece, t1, t1, halfbits); +- tcg_gen_shli_vec(vece, t2, b, halfbits); +- tcg_gen_sari_vec(vece, t2, t2, halfbits); +- tcg_gen_mul_vec(vece, t, t1, t2); +-} +- +-static void gen_vmulwev_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1, t2; +- +- t1 = tcg_temp_new_i32(); +- t2 = tcg_temp_new_i32(); +- tcg_gen_ext16s_i32(t1, a); +- tcg_gen_ext16s_i32(t2, b); +- tcg_gen_mul_i32(t, t1, t2); +-} +- +-static void gen_vmulwev_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1, t2; +- +- t1 = tcg_temp_new_i64(); +- t2 = tcg_temp_new_i64(); +- tcg_gen_ext32s_i64(t1, a); +- tcg_gen_ext32s_i64(t2, b); +- tcg_gen_mul_i64(t, t1, t2); +-} +- +-static void do_vmulwev_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_shli_vec, INDEX_op_sari_vec, INDEX_op_mul_vec, 0 +- }; +- static const GVecGen3 op[3] = { +- { +- .fniv = gen_vmulwev_s, +- .fno = gen_helper_vmulwev_h_b, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vmulwev_w_h, +- .fniv = gen_vmulwev_s, +- .fno = gen_helper_vmulwev_w_h, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vmulwev_d_w, +- .fniv = gen_vmulwev_s, +- .fno = gen_helper_vmulwev_d_w, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vmulwev_h_b, LSX, gvec_vvv, MO_8, do_vmulwev_s) +-TRANS(vmulwev_w_h, LSX, gvec_vvv, MO_16, do_vmulwev_s) +-TRANS(vmulwev_d_w, LSX, gvec_vvv, MO_32, do_vmulwev_s) +-TRANS(xvmulwev_h_b, LASX, gvec_xxx, MO_8, do_vmulwev_s) +-TRANS(xvmulwev_w_h, LASX, gvec_xxx, MO_16, do_vmulwev_s) +-TRANS(xvmulwev_d_w, LASX, gvec_xxx, MO_32, do_vmulwev_s) +- +-static void tcg_gen_mulus2_i64(TCGv_i64 rl, TCGv_i64 rh, +- TCGv_i64 arg1, TCGv_i64 arg2) +-{ +- tcg_gen_mulsu2_i64(rl, rh, arg2, arg1); +-} +- +-static bool gen_vmul_q_vl(DisasContext *ctx, +- arg_vvv *a, uint32_t oprsz, int idx1, int idx2, +- void (*func)(TCGv_i64, TCGv_i64, +- TCGv_i64, TCGv_i64)) +-{ +- TCGv_i64 rh, rl, arg1, arg2; +- int i; +- +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- rh = tcg_temp_new_i64(); +- rl = tcg_temp_new_i64(); +- arg1 = tcg_temp_new_i64(); +- arg2 = tcg_temp_new_i64(); +- +- for (i = 0; i < oprsz / 16; i++) { +- get_vreg64(arg1, a->vj, 2 * i + idx1); +- get_vreg64(arg2, a->vk, 2 * i + idx2); +- +- func(rl, rh, arg1, arg2); +- +- set_vreg64(rh, a->vd, 2 * i + 1); +- set_vreg64(rl, a->vd, 2 * i); +- } +- +- return true; +-} +- +-static bool gen_vmul_q(DisasContext *ctx, arg_vvv *a, int idx1, int idx2, +- void (*func)(TCGv_i64, TCGv_i64, +- TCGv_i64, TCGv_i64)) +-{ +- return gen_vmul_q_vl(ctx, a, 16, idx1, idx2, func); +-} +- +-static bool gen_xvmul_q(DisasContext *ctx, arg_vvv *a, int idx1, int idx2, +- void (*func)(TCGv_i64, TCGv_i64, +- TCGv_i64, TCGv_i64)) +-{ +- return gen_vmul_q_vl(ctx, a, 32, idx1, idx2, func); +-} +- +-TRANS(vmulwev_q_d, LSX, gen_vmul_q, 0, 0, tcg_gen_muls2_i64) +-TRANS(vmulwod_q_d, LSX, gen_vmul_q, 1, 1, tcg_gen_muls2_i64) +-TRANS(vmulwev_q_du, LSX, gen_vmul_q, 0, 0, tcg_gen_mulu2_i64) +-TRANS(vmulwod_q_du, LSX, gen_vmul_q, 1, 1, tcg_gen_mulu2_i64) +-TRANS(vmulwev_q_du_d, LSX, gen_vmul_q, 0, 0, tcg_gen_mulus2_i64) +-TRANS(vmulwod_q_du_d, LSX, gen_vmul_q, 1, 1, tcg_gen_mulus2_i64) +-TRANS(xvmulwev_q_d, LASX, gen_xvmul_q, 0, 0, tcg_gen_muls2_i64) +-TRANS(xvmulwod_q_d, LASX, gen_xvmul_q, 1, 1, tcg_gen_muls2_i64) +-TRANS(xvmulwev_q_du, LASX, gen_xvmul_q, 0, 0, tcg_gen_mulu2_i64) +-TRANS(xvmulwod_q_du, LASX, gen_xvmul_q, 1, 1, tcg_gen_mulu2_i64) +-TRANS(xvmulwev_q_du_d, LASX, gen_xvmul_q, 0, 0, tcg_gen_mulus2_i64) +-TRANS(xvmulwod_q_du_d, LASX, gen_xvmul_q, 1, 1, tcg_gen_mulus2_i64) +- +-static void gen_vmulwod_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, t2; +- int halfbits = 4 << vece; +- +- t1 = tcg_temp_new_vec_matching(a); +- t2 = tcg_temp_new_vec_matching(b); +- tcg_gen_sari_vec(vece, t1, a, halfbits); +- tcg_gen_sari_vec(vece, t2, b, halfbits); +- tcg_gen_mul_vec(vece, t, t1, t2); +-} +- +-static void gen_vmulwod_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1, t2; +- +- t1 = tcg_temp_new_i32(); +- t2 = tcg_temp_new_i32(); +- tcg_gen_sari_i32(t1, a, 16); +- tcg_gen_sari_i32(t2, b, 16); +- tcg_gen_mul_i32(t, t1, t2); +-} +- +-static void gen_vmulwod_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1, t2; +- +- t1 = tcg_temp_new_i64(); +- t2 = tcg_temp_new_i64(); +- tcg_gen_sari_i64(t1, a, 32); +- tcg_gen_sari_i64(t2, b, 32); +- tcg_gen_mul_i64(t, t1, t2); +-} +- +-static void do_vmulwod_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_sari_vec, INDEX_op_mul_vec, 0 +- }; +- static const GVecGen3 op[3] = { +- { +- .fniv = gen_vmulwod_s, +- .fno = gen_helper_vmulwod_h_b, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vmulwod_w_h, +- .fniv = gen_vmulwod_s, +- .fno = gen_helper_vmulwod_w_h, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vmulwod_d_w, +- .fniv = gen_vmulwod_s, +- .fno = gen_helper_vmulwod_d_w, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vmulwod_h_b, LSX, gvec_vvv, MO_8, do_vmulwod_s) +-TRANS(vmulwod_w_h, LSX, gvec_vvv, MO_16, do_vmulwod_s) +-TRANS(vmulwod_d_w, LSX, gvec_vvv, MO_32, do_vmulwod_s) +-TRANS(xvmulwod_h_b, LASX, gvec_xxx, MO_8, do_vmulwod_s) +-TRANS(xvmulwod_w_h, LASX, gvec_xxx, MO_16, do_vmulwod_s) +-TRANS(xvmulwod_d_w, LASX, gvec_xxx, MO_32, do_vmulwod_s) +- +-static void gen_vmulwev_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, t2, mask; +- +- t1 = tcg_temp_new_vec_matching(a); +- t2 = tcg_temp_new_vec_matching(b); +- mask = tcg_constant_vec_matching(t, vece, MAKE_64BIT_MASK(0, 4 << vece)); +- tcg_gen_and_vec(vece, t1, a, mask); +- tcg_gen_and_vec(vece, t2, b, mask); +- tcg_gen_mul_vec(vece, t, t1, t2); +-} +- +-static void gen_vmulwev_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1, t2; +- +- t1 = tcg_temp_new_i32(); +- t2 = tcg_temp_new_i32(); +- tcg_gen_ext16u_i32(t1, a); +- tcg_gen_ext16u_i32(t2, b); +- tcg_gen_mul_i32(t, t1, t2); +-} +- +-static void gen_vmulwev_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1, t2; +- +- t1 = tcg_temp_new_i64(); +- t2 = tcg_temp_new_i64(); +- tcg_gen_ext32u_i64(t1, a); +- tcg_gen_ext32u_i64(t2, b); +- tcg_gen_mul_i64(t, t1, t2); +-} +- +-static void do_vmulwev_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_mul_vec, 0 +- }; +- static const GVecGen3 op[3] = { +- { +- .fniv = gen_vmulwev_u, +- .fno = gen_helper_vmulwev_h_bu, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vmulwev_w_hu, +- .fniv = gen_vmulwev_u, +- .fno = gen_helper_vmulwev_w_hu, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vmulwev_d_wu, +- .fniv = gen_vmulwev_u, +- .fno = gen_helper_vmulwev_d_wu, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vmulwev_h_bu, LSX, gvec_vvv, MO_8, do_vmulwev_u) +-TRANS(vmulwev_w_hu, LSX, gvec_vvv, MO_16, do_vmulwev_u) +-TRANS(vmulwev_d_wu, LSX, gvec_vvv, MO_32, do_vmulwev_u) +-TRANS(xvmulwev_h_bu, LASX, gvec_xxx, MO_8, do_vmulwev_u) +-TRANS(xvmulwev_w_hu, LASX, gvec_xxx, MO_16, do_vmulwev_u) +-TRANS(xvmulwev_d_wu, LASX, gvec_xxx, MO_32, do_vmulwev_u) +- +-static void gen_vmulwod_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, t2; +- int halfbits = 4 << vece; +- +- t1 = tcg_temp_new_vec_matching(a); +- t2 = tcg_temp_new_vec_matching(b); +- tcg_gen_shri_vec(vece, t1, a, halfbits); +- tcg_gen_shri_vec(vece, t2, b, halfbits); +- tcg_gen_mul_vec(vece, t, t1, t2); +-} +- +-static void gen_vmulwod_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1, t2; +- +- t1 = tcg_temp_new_i32(); +- t2 = tcg_temp_new_i32(); +- tcg_gen_shri_i32(t1, a, 16); +- tcg_gen_shri_i32(t2, b, 16); +- tcg_gen_mul_i32(t, t1, t2); +-} +- +-static void gen_vmulwod_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1, t2; +- +- t1 = tcg_temp_new_i64(); +- t2 = tcg_temp_new_i64(); +- tcg_gen_shri_i64(t1, a, 32); +- tcg_gen_shri_i64(t2, b, 32); +- tcg_gen_mul_i64(t, t1, t2); +-} +- +-static void do_vmulwod_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_shri_vec, INDEX_op_mul_vec, 0 +- }; +- static const GVecGen3 op[3] = { +- { +- .fniv = gen_vmulwod_u, +- .fno = gen_helper_vmulwod_h_bu, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vmulwod_w_hu, +- .fniv = gen_vmulwod_u, +- .fno = gen_helper_vmulwod_w_hu, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vmulwod_d_wu, +- .fniv = gen_vmulwod_u, +- .fno = gen_helper_vmulwod_d_wu, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vmulwod_h_bu, LSX, gvec_vvv, MO_8, do_vmulwod_u) +-TRANS(vmulwod_w_hu, LSX, gvec_vvv, MO_16, do_vmulwod_u) +-TRANS(vmulwod_d_wu, LSX, gvec_vvv, MO_32, do_vmulwod_u) +-TRANS(xvmulwod_h_bu, LASX, gvec_xxx, MO_8, do_vmulwod_u) +-TRANS(xvmulwod_w_hu, LASX, gvec_xxx, MO_16, do_vmulwod_u) +-TRANS(xvmulwod_d_wu, LASX, gvec_xxx, MO_32, do_vmulwod_u) +- +-static void gen_vmulwev_u_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, t2, mask; +- int halfbits = 4 << vece; +- +- t1 = tcg_temp_new_vec_matching(a); +- t2 = tcg_temp_new_vec_matching(b); +- mask = tcg_constant_vec_matching(t, vece, MAKE_64BIT_MASK(0, 4 << vece)); +- tcg_gen_and_vec(vece, t1, a, mask); +- tcg_gen_shli_vec(vece, t2, b, halfbits); +- tcg_gen_sari_vec(vece, t2, t2, halfbits); +- tcg_gen_mul_vec(vece, t, t1, t2); +-} +- +-static void gen_vmulwev_w_hu_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1, t2; +- +- t1 = tcg_temp_new_i32(); +- t2 = tcg_temp_new_i32(); +- tcg_gen_ext16u_i32(t1, a); +- tcg_gen_ext16s_i32(t2, b); +- tcg_gen_mul_i32(t, t1, t2); +-} +- +-static void gen_vmulwev_d_wu_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1, t2; +- +- t1 = tcg_temp_new_i64(); +- t2 = tcg_temp_new_i64(); +- tcg_gen_ext32u_i64(t1, a); +- tcg_gen_ext32s_i64(t2, b); +- tcg_gen_mul_i64(t, t1, t2); +-} +- +-static void do_vmulwev_u_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_shli_vec, INDEX_op_sari_vec, INDEX_op_mul_vec, 0 +- }; +- static const GVecGen3 op[3] = { +- { +- .fniv = gen_vmulwev_u_s, +- .fno = gen_helper_vmulwev_h_bu_b, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vmulwev_w_hu_h, +- .fniv = gen_vmulwev_u_s, +- .fno = gen_helper_vmulwev_w_hu_h, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vmulwev_d_wu_w, +- .fniv = gen_vmulwev_u_s, +- .fno = gen_helper_vmulwev_d_wu_w, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vmulwev_h_bu_b, LSX, gvec_vvv, MO_8, do_vmulwev_u_s) +-TRANS(vmulwev_w_hu_h, LSX, gvec_vvv, MO_16, do_vmulwev_u_s) +-TRANS(vmulwev_d_wu_w, LSX, gvec_vvv, MO_32, do_vmulwev_u_s) +-TRANS(xvmulwev_h_bu_b, LASX, gvec_xxx, MO_8, do_vmulwev_u_s) +-TRANS(xvmulwev_w_hu_h, LASX, gvec_xxx, MO_16, do_vmulwev_u_s) +-TRANS(xvmulwev_d_wu_w, LASX, gvec_xxx, MO_32, do_vmulwev_u_s) +- +-static void gen_vmulwod_u_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, t2; +- int halfbits = 4 << vece; +- +- t1 = tcg_temp_new_vec_matching(a); +- t2 = tcg_temp_new_vec_matching(b); +- tcg_gen_shri_vec(vece, t1, a, halfbits); +- tcg_gen_sari_vec(vece, t2, b, halfbits); +- tcg_gen_mul_vec(vece, t, t1, t2); +-} +- +-static void gen_vmulwod_w_hu_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1, t2; +- +- t1 = tcg_temp_new_i32(); +- t2 = tcg_temp_new_i32(); +- tcg_gen_shri_i32(t1, a, 16); +- tcg_gen_sari_i32(t2, b, 16); +- tcg_gen_mul_i32(t, t1, t2); +-} +-static void gen_vmulwod_d_wu_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1, t2; +- +- t1 = tcg_temp_new_i64(); +- t2 = tcg_temp_new_i64(); +- tcg_gen_shri_i64(t1, a, 32); +- tcg_gen_sari_i64(t2, b, 32); +- tcg_gen_mul_i64(t, t1, t2); +-} +- +-static void do_vmulwod_u_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_shri_vec, INDEX_op_sari_vec, INDEX_op_mul_vec, 0 +- }; +- static const GVecGen3 op[3] = { +- { +- .fniv = gen_vmulwod_u_s, +- .fno = gen_helper_vmulwod_h_bu_b, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vmulwod_w_hu_h, +- .fniv = gen_vmulwod_u_s, +- .fno = gen_helper_vmulwod_w_hu_h, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vmulwod_d_wu_w, +- .fniv = gen_vmulwod_u_s, +- .fno = gen_helper_vmulwod_d_wu_w, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vmulwod_h_bu_b, LSX, gvec_vvv, MO_8, do_vmulwod_u_s) +-TRANS(vmulwod_w_hu_h, LSX, gvec_vvv, MO_16, do_vmulwod_u_s) +-TRANS(vmulwod_d_wu_w, LSX, gvec_vvv, MO_32, do_vmulwod_u_s) +-TRANS(xvmulwod_h_bu_b, LASX, gvec_xxx, MO_8, do_vmulwod_u_s) +-TRANS(xvmulwod_w_hu_h, LASX, gvec_xxx, MO_16, do_vmulwod_u_s) +-TRANS(xvmulwod_d_wu_w, LASX, gvec_xxx, MO_32, do_vmulwod_u_s) +- +-static void gen_vmadd(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1; +- +- t1 = tcg_temp_new_vec_matching(t); +- tcg_gen_mul_vec(vece, t1, a, b); +- tcg_gen_add_vec(vece, t, t, t1); +-} +- +-static void gen_vmadd_w(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1; +- +- t1 = tcg_temp_new_i32(); +- tcg_gen_mul_i32(t1, a, b); +- tcg_gen_add_i32(t, t, t1); +-} +- +-static void gen_vmadd_d(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1; +- +- t1 = tcg_temp_new_i64(); +- tcg_gen_mul_i64(t1, a, b); +- tcg_gen_add_i64(t, t, t1); +-} +- +-static void do_vmadd(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_mul_vec, INDEX_op_add_vec, 0 +- }; +- static const GVecGen3 op[4] = { +- { +- .fniv = gen_vmadd, +- .fno = gen_helper_vmadd_b, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_8 +- }, +- { +- .fniv = gen_vmadd, +- .fno = gen_helper_vmadd_h, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vmadd_w, +- .fniv = gen_vmadd, +- .fno = gen_helper_vmadd_w, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vmadd_d, +- .fniv = gen_vmadd, +- .fno = gen_helper_vmadd_d, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vmadd_b, LSX, gvec_vvv, MO_8, do_vmadd) +-TRANS(vmadd_h, LSX, gvec_vvv, MO_16, do_vmadd) +-TRANS(vmadd_w, LSX, gvec_vvv, MO_32, do_vmadd) +-TRANS(vmadd_d, LSX, gvec_vvv, MO_64, do_vmadd) +-TRANS(xvmadd_b, LASX, gvec_xxx, MO_8, do_vmadd) +-TRANS(xvmadd_h, LASX, gvec_xxx, MO_16, do_vmadd) +-TRANS(xvmadd_w, LASX, gvec_xxx, MO_32, do_vmadd) +-TRANS(xvmadd_d, LASX, gvec_xxx, MO_64, do_vmadd) +- +-static void gen_vmsub(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1; +- +- t1 = tcg_temp_new_vec_matching(t); +- tcg_gen_mul_vec(vece, t1, a, b); +- tcg_gen_sub_vec(vece, t, t, t1); +-} +- +-static void gen_vmsub_w(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1; +- +- t1 = tcg_temp_new_i32(); +- tcg_gen_mul_i32(t1, a, b); +- tcg_gen_sub_i32(t, t, t1); +-} +- +-static void gen_vmsub_d(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1; +- +- t1 = tcg_temp_new_i64(); +- tcg_gen_mul_i64(t1, a, b); +- tcg_gen_sub_i64(t, t, t1); +-} +- +-static void do_vmsub(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_mul_vec, INDEX_op_sub_vec, 0 +- }; +- static const GVecGen3 op[4] = { +- { +- .fniv = gen_vmsub, +- .fno = gen_helper_vmsub_b, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_8 +- }, +- { +- .fniv = gen_vmsub, +- .fno = gen_helper_vmsub_h, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vmsub_w, +- .fniv = gen_vmsub, +- .fno = gen_helper_vmsub_w, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vmsub_d, +- .fniv = gen_vmsub, +- .fno = gen_helper_vmsub_d, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vmsub_b, LSX, gvec_vvv, MO_8, do_vmsub) +-TRANS(vmsub_h, LSX, gvec_vvv, MO_16, do_vmsub) +-TRANS(vmsub_w, LSX, gvec_vvv, MO_32, do_vmsub) +-TRANS(vmsub_d, LSX, gvec_vvv, MO_64, do_vmsub) +-TRANS(xvmsub_b, LASX, gvec_xxx, MO_8, do_vmsub) +-TRANS(xvmsub_h, LASX, gvec_xxx, MO_16, do_vmsub) +-TRANS(xvmsub_w, LASX, gvec_xxx, MO_32, do_vmsub) +-TRANS(xvmsub_d, LASX, gvec_xxx, MO_64, do_vmsub) +- +-static void gen_vmaddwev_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, t2, t3; +- int halfbits = 4 << vece; +- +- t1 = tcg_temp_new_vec_matching(a); +- t2 = tcg_temp_new_vec_matching(b); +- t3 = tcg_temp_new_vec_matching(t); +- tcg_gen_shli_vec(vece, t1, a, halfbits); +- tcg_gen_sari_vec(vece, t1, t1, halfbits); +- tcg_gen_shli_vec(vece, t2, b, halfbits); +- tcg_gen_sari_vec(vece, t2, t2, halfbits); +- tcg_gen_mul_vec(vece, t3, t1, t2); +- tcg_gen_add_vec(vece, t, t, t3); +-} +- +-static void gen_vmaddwev_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1; +- +- t1 = tcg_temp_new_i32(); +- gen_vmulwev_w_h(t1, a, b); +- tcg_gen_add_i32(t, t, t1); +-} +- +-static void gen_vmaddwev_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1; +- +- t1 = tcg_temp_new_i64(); +- gen_vmulwev_d_w(t1, a, b); +- tcg_gen_add_i64(t, t, t1); +-} +- +-static void do_vmaddwev_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_shli_vec, INDEX_op_sari_vec, +- INDEX_op_mul_vec, INDEX_op_add_vec, 0 +- }; +- static const GVecGen3 op[3] = { +- { +- .fniv = gen_vmaddwev_s, +- .fno = gen_helper_vmaddwev_h_b, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vmaddwev_w_h, +- .fniv = gen_vmaddwev_s, +- .fno = gen_helper_vmaddwev_w_h, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vmaddwev_d_w, +- .fniv = gen_vmaddwev_s, +- .fno = gen_helper_vmaddwev_d_w, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vmaddwev_h_b, LSX, gvec_vvv, MO_8, do_vmaddwev_s) +-TRANS(vmaddwev_w_h, LSX, gvec_vvv, MO_16, do_vmaddwev_s) +-TRANS(vmaddwev_d_w, LSX, gvec_vvv, MO_32, do_vmaddwev_s) +-TRANS(xvmaddwev_h_b, LASX, gvec_xxx, MO_8, do_vmaddwev_s) +-TRANS(xvmaddwev_w_h, LASX, gvec_xxx, MO_16, do_vmaddwev_s) +-TRANS(xvmaddwev_d_w, LASX, gvec_xxx, MO_32, do_vmaddwev_s) +- +-static bool gen_vmadd_q_vl(DisasContext * ctx, +- arg_vvv *a, uint32_t oprsz, int idx1, int idx2, +- void (*func)(TCGv_i64, TCGv_i64, +- TCGv_i64, TCGv_i64)) +-{ +- TCGv_i64 rh, rl, arg1, arg2, th, tl; +- int i; +- +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- rh = tcg_temp_new_i64(); +- rl = tcg_temp_new_i64(); +- arg1 = tcg_temp_new_i64(); +- arg2 = tcg_temp_new_i64(); +- th = tcg_temp_new_i64(); +- tl = tcg_temp_new_i64(); +- +- for (i = 0; i < oprsz / 16; i++) { +- get_vreg64(arg1, a->vj, 2 * i + idx1); +- get_vreg64(arg2, a->vk, 2 * i + idx2); +- get_vreg64(rh, a->vd, 2 * i + 1); +- get_vreg64(rl, a->vd, 2 * i); +- +- func(tl, th, arg1, arg2); +- tcg_gen_add2_i64(rl, rh, rl, rh, tl, th); +- +- set_vreg64(rh, a->vd, 2 * i + 1); +- set_vreg64(rl, a->vd, 2 * i); +- } +- +- return true; +-} +- +-static bool gen_vmadd_q(DisasContext *ctx, arg_vvv *a, int idx1, int idx2, +- void (*func)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) +-{ +- return gen_vmadd_q_vl(ctx, a, 16, idx1, idx2, func); +-} +- +-static bool gen_xvmadd_q(DisasContext *ctx, arg_vvv *a, int idx1, int idx2, +- void (*func)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) +-{ +- return gen_vmadd_q_vl(ctx, a, 32, idx1, idx2, func); +-} +- +-TRANS(vmaddwev_q_d, LSX, gen_vmadd_q, 0, 0, tcg_gen_muls2_i64) +-TRANS(vmaddwod_q_d, LSX, gen_vmadd_q, 1, 1, tcg_gen_muls2_i64) +-TRANS(vmaddwev_q_du, LSX, gen_vmadd_q, 0, 0, tcg_gen_mulu2_i64) +-TRANS(vmaddwod_q_du, LSX, gen_vmadd_q, 1, 1, tcg_gen_mulu2_i64) +-TRANS(vmaddwev_q_du_d, LSX, gen_vmadd_q, 0, 0, tcg_gen_mulus2_i64) +-TRANS(vmaddwod_q_du_d, LSX, gen_vmadd_q, 1, 1, tcg_gen_mulus2_i64) +-TRANS(xvmaddwev_q_d, LASX, gen_xvmadd_q, 0, 0, tcg_gen_muls2_i64) +-TRANS(xvmaddwod_q_d, LASX, gen_xvmadd_q, 1, 1, tcg_gen_muls2_i64) +-TRANS(xvmaddwev_q_du, LASX, gen_xvmadd_q, 0, 0, tcg_gen_mulu2_i64) +-TRANS(xvmaddwod_q_du, LASX, gen_xvmadd_q, 1, 1, tcg_gen_mulu2_i64) +-TRANS(xvmaddwev_q_du_d, LASX, gen_xvmadd_q, 0, 0, tcg_gen_mulus2_i64) +-TRANS(xvmaddwod_q_du_d, LASX, gen_xvmadd_q, 1, 1, tcg_gen_mulus2_i64) +- +-static void gen_vmaddwod_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, t2, t3; +- int halfbits = 4 << vece; +- +- t1 = tcg_temp_new_vec_matching(a); +- t2 = tcg_temp_new_vec_matching(b); +- t3 = tcg_temp_new_vec_matching(t); +- tcg_gen_sari_vec(vece, t1, a, halfbits); +- tcg_gen_sari_vec(vece, t2, b, halfbits); +- tcg_gen_mul_vec(vece, t3, t1, t2); +- tcg_gen_add_vec(vece, t, t, t3); +-} +- +-static void gen_vmaddwod_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1; +- +- t1 = tcg_temp_new_i32(); +- gen_vmulwod_w_h(t1, a, b); +- tcg_gen_add_i32(t, t, t1); +-} +- +-static void gen_vmaddwod_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1; +- +- t1 = tcg_temp_new_i64(); +- gen_vmulwod_d_w(t1, a, b); +- tcg_gen_add_i64(t, t, t1); +-} +- +-static void do_vmaddwod_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_sari_vec, INDEX_op_mul_vec, INDEX_op_add_vec, 0 +- }; +- static const GVecGen3 op[3] = { +- { +- .fniv = gen_vmaddwod_s, +- .fno = gen_helper_vmaddwod_h_b, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vmaddwod_w_h, +- .fniv = gen_vmaddwod_s, +- .fno = gen_helper_vmaddwod_w_h, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vmaddwod_d_w, +- .fniv = gen_vmaddwod_s, +- .fno = gen_helper_vmaddwod_d_w, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vmaddwod_h_b, LSX, gvec_vvv, MO_8, do_vmaddwod_s) +-TRANS(vmaddwod_w_h, LSX, gvec_vvv, MO_16, do_vmaddwod_s) +-TRANS(vmaddwod_d_w, LSX, gvec_vvv, MO_32, do_vmaddwod_s) +-TRANS(xvmaddwod_h_b, LASX, gvec_xxx, MO_8, do_vmaddwod_s) +-TRANS(xvmaddwod_w_h, LASX, gvec_xxx, MO_16, do_vmaddwod_s) +-TRANS(xvmaddwod_d_w, LASX, gvec_xxx, MO_32, do_vmaddwod_s) +- +-static void gen_vmaddwev_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, t2, mask; +- +- t1 = tcg_temp_new_vec_matching(t); +- t2 = tcg_temp_new_vec_matching(b); +- mask = tcg_constant_vec_matching(t, vece, MAKE_64BIT_MASK(0, 4 << vece)); +- tcg_gen_and_vec(vece, t1, a, mask); +- tcg_gen_and_vec(vece, t2, b, mask); +- tcg_gen_mul_vec(vece, t1, t1, t2); +- tcg_gen_add_vec(vece, t, t, t1); +-} +- +-static void gen_vmaddwev_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1; +- +- t1 = tcg_temp_new_i32(); +- gen_vmulwev_w_hu(t1, a, b); +- tcg_gen_add_i32(t, t, t1); +-} +- +-static void gen_vmaddwev_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1; +- +- t1 = tcg_temp_new_i64(); +- gen_vmulwev_d_wu(t1, a, b); +- tcg_gen_add_i64(t, t, t1); +-} +- +-static void do_vmaddwev_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_mul_vec, INDEX_op_add_vec, 0 +- }; +- static const GVecGen3 op[3] = { +- { +- .fniv = gen_vmaddwev_u, +- .fno = gen_helper_vmaddwev_h_bu, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vmaddwev_w_hu, +- .fniv = gen_vmaddwev_u, +- .fno = gen_helper_vmaddwev_w_hu, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vmaddwev_d_wu, +- .fniv = gen_vmaddwev_u, +- .fno = gen_helper_vmaddwev_d_wu, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vmaddwev_h_bu, LSX, gvec_vvv, MO_8, do_vmaddwev_u) +-TRANS(vmaddwev_w_hu, LSX, gvec_vvv, MO_16, do_vmaddwev_u) +-TRANS(vmaddwev_d_wu, LSX, gvec_vvv, MO_32, do_vmaddwev_u) +-TRANS(xvmaddwev_h_bu, LASX, gvec_xxx, MO_8, do_vmaddwev_u) +-TRANS(xvmaddwev_w_hu, LASX, gvec_xxx, MO_16, do_vmaddwev_u) +-TRANS(xvmaddwev_d_wu, LASX, gvec_xxx, MO_32, do_vmaddwev_u) +- +-static void gen_vmaddwod_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, t2, t3; +- int halfbits = 4 << vece; +- +- t1 = tcg_temp_new_vec_matching(a); +- t2 = tcg_temp_new_vec_matching(b); +- t3 = tcg_temp_new_vec_matching(t); +- tcg_gen_shri_vec(vece, t1, a, halfbits); +- tcg_gen_shri_vec(vece, t2, b, halfbits); +- tcg_gen_mul_vec(vece, t3, t1, t2); +- tcg_gen_add_vec(vece, t, t, t3); +-} +- +-static void gen_vmaddwod_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1; +- +- t1 = tcg_temp_new_i32(); +- gen_vmulwod_w_hu(t1, a, b); +- tcg_gen_add_i32(t, t, t1); +-} +- +-static void gen_vmaddwod_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1; +- +- t1 = tcg_temp_new_i64(); +- gen_vmulwod_d_wu(t1, a, b); +- tcg_gen_add_i64(t, t, t1); +-} +- +-static void do_vmaddwod_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_shri_vec, INDEX_op_mul_vec, INDEX_op_add_vec, 0 +- }; +- static const GVecGen3 op[3] = { +- { +- .fniv = gen_vmaddwod_u, +- .fno = gen_helper_vmaddwod_h_bu, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vmaddwod_w_hu, +- .fniv = gen_vmaddwod_u, +- .fno = gen_helper_vmaddwod_w_hu, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vmaddwod_d_wu, +- .fniv = gen_vmaddwod_u, +- .fno = gen_helper_vmaddwod_d_wu, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vmaddwod_h_bu, LSX, gvec_vvv, MO_8, do_vmaddwod_u) +-TRANS(vmaddwod_w_hu, LSX, gvec_vvv, MO_16, do_vmaddwod_u) +-TRANS(vmaddwod_d_wu, LSX, gvec_vvv, MO_32, do_vmaddwod_u) +-TRANS(xvmaddwod_h_bu, LASX, gvec_xxx, MO_8, do_vmaddwod_u) +-TRANS(xvmaddwod_w_hu, LASX, gvec_xxx, MO_16, do_vmaddwod_u) +-TRANS(xvmaddwod_d_wu, LASX, gvec_xxx, MO_32, do_vmaddwod_u) +- +-static void gen_vmaddwev_u_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, t2, mask; +- int halfbits = 4 << vece; +- +- t1 = tcg_temp_new_vec_matching(a); +- t2 = tcg_temp_new_vec_matching(b); +- mask = tcg_constant_vec_matching(t, vece, MAKE_64BIT_MASK(0, 4 << vece)); +- tcg_gen_and_vec(vece, t1, a, mask); +- tcg_gen_shli_vec(vece, t2, b, halfbits); +- tcg_gen_sari_vec(vece, t2, t2, halfbits); +- tcg_gen_mul_vec(vece, t1, t1, t2); +- tcg_gen_add_vec(vece, t, t, t1); +-} +- +-static void gen_vmaddwev_w_hu_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1; +- +- t1 = tcg_temp_new_i32(); +- gen_vmulwev_w_hu_h(t1, a, b); +- tcg_gen_add_i32(t, t, t1); +-} +- +-static void gen_vmaddwev_d_wu_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1; +- +- t1 = tcg_temp_new_i64(); +- gen_vmulwev_d_wu_w(t1, a, b); +- tcg_gen_add_i64(t, t, t1); +-} +- +-static void do_vmaddwev_u_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_shli_vec, INDEX_op_sari_vec, +- INDEX_op_mul_vec, INDEX_op_add_vec, 0 +- }; +- static const GVecGen3 op[3] = { +- { +- .fniv = gen_vmaddwev_u_s, +- .fno = gen_helper_vmaddwev_h_bu_b, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vmaddwev_w_hu_h, +- .fniv = gen_vmaddwev_u_s, +- .fno = gen_helper_vmaddwev_w_hu_h, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vmaddwev_d_wu_w, +- .fniv = gen_vmaddwev_u_s, +- .fno = gen_helper_vmaddwev_d_wu_w, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vmaddwev_h_bu_b, LSX, gvec_vvv, MO_8, do_vmaddwev_u_s) +-TRANS(vmaddwev_w_hu_h, LSX, gvec_vvv, MO_16, do_vmaddwev_u_s) +-TRANS(vmaddwev_d_wu_w, LSX, gvec_vvv, MO_32, do_vmaddwev_u_s) +-TRANS(xvmaddwev_h_bu_b, LASX, gvec_xxx, MO_8, do_vmaddwev_u_s) +-TRANS(xvmaddwev_w_hu_h, LASX, gvec_xxx, MO_16, do_vmaddwev_u_s) +-TRANS(xvmaddwev_d_wu_w, LASX, gvec_xxx, MO_32, do_vmaddwev_u_s) +- +-static void gen_vmaddwod_u_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, t2, t3; +- int halfbits = 4 << vece; +- +- t1 = tcg_temp_new_vec_matching(a); +- t2 = tcg_temp_new_vec_matching(b); +- t3 = tcg_temp_new_vec_matching(t); +- tcg_gen_shri_vec(vece, t1, a, halfbits); +- tcg_gen_sari_vec(vece, t2, b, halfbits); +- tcg_gen_mul_vec(vece, t3, t1, t2); +- tcg_gen_add_vec(vece, t, t, t3); +-} +- +-static void gen_vmaddwod_w_hu_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) +-{ +- TCGv_i32 t1; +- +- t1 = tcg_temp_new_i32(); +- gen_vmulwod_w_hu_h(t1, a, b); +- tcg_gen_add_i32(t, t, t1); +-} +- +-static void gen_vmaddwod_d_wu_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) +-{ +- TCGv_i64 t1; +- +- t1 = tcg_temp_new_i64(); +- gen_vmulwod_d_wu_w(t1, a, b); +- tcg_gen_add_i64(t, t, t1); +-} +- +-static void do_vmaddwod_u_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_shri_vec, INDEX_op_sari_vec, +- INDEX_op_mul_vec, INDEX_op_add_vec, 0 +- }; +- static const GVecGen3 op[3] = { +- { +- .fniv = gen_vmaddwod_u_s, +- .fno = gen_helper_vmaddwod_h_bu_b, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fni4 = gen_vmaddwod_w_hu_h, +- .fniv = gen_vmaddwod_u_s, +- .fno = gen_helper_vmaddwod_w_hu_h, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fni8 = gen_vmaddwod_d_wu_w, +- .fniv = gen_vmaddwod_u_s, +- .fno = gen_helper_vmaddwod_d_wu_w, +- .load_dest = true, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vmaddwod_h_bu_b, LSX, gvec_vvv, MO_8, do_vmaddwod_u_s) +-TRANS(vmaddwod_w_hu_h, LSX, gvec_vvv, MO_16, do_vmaddwod_u_s) +-TRANS(vmaddwod_d_wu_w, LSX, gvec_vvv, MO_32, do_vmaddwod_u_s) +-TRANS(xvmaddwod_h_bu_b, LASX, gvec_xxx, MO_8, do_vmaddwod_u_s) +-TRANS(xvmaddwod_w_hu_h, LASX, gvec_xxx, MO_16, do_vmaddwod_u_s) +-TRANS(xvmaddwod_d_wu_w, LASX, gvec_xxx, MO_32, do_vmaddwod_u_s) +- +-TRANS(vdiv_b, LSX, gen_vvv, gen_helper_vdiv_b) +-TRANS(vdiv_h, LSX, gen_vvv, gen_helper_vdiv_h) +-TRANS(vdiv_w, LSX, gen_vvv, gen_helper_vdiv_w) +-TRANS(vdiv_d, LSX, gen_vvv, gen_helper_vdiv_d) +-TRANS(vdiv_bu, LSX, gen_vvv, gen_helper_vdiv_bu) +-TRANS(vdiv_hu, LSX, gen_vvv, gen_helper_vdiv_hu) +-TRANS(vdiv_wu, LSX, gen_vvv, gen_helper_vdiv_wu) +-TRANS(vdiv_du, LSX, gen_vvv, gen_helper_vdiv_du) +-TRANS(vmod_b, LSX, gen_vvv, gen_helper_vmod_b) +-TRANS(vmod_h, LSX, gen_vvv, gen_helper_vmod_h) +-TRANS(vmod_w, LSX, gen_vvv, gen_helper_vmod_w) +-TRANS(vmod_d, LSX, gen_vvv, gen_helper_vmod_d) +-TRANS(vmod_bu, LSX, gen_vvv, gen_helper_vmod_bu) +-TRANS(vmod_hu, LSX, gen_vvv, gen_helper_vmod_hu) +-TRANS(vmod_wu, LSX, gen_vvv, gen_helper_vmod_wu) +-TRANS(vmod_du, LSX, gen_vvv, gen_helper_vmod_du) +-TRANS(xvdiv_b, LASX, gen_xxx, gen_helper_vdiv_b) +-TRANS(xvdiv_h, LASX, gen_xxx, gen_helper_vdiv_h) +-TRANS(xvdiv_w, LASX, gen_xxx, gen_helper_vdiv_w) +-TRANS(xvdiv_d, LASX, gen_xxx, gen_helper_vdiv_d) +-TRANS(xvdiv_bu, LASX, gen_xxx, gen_helper_vdiv_bu) +-TRANS(xvdiv_hu, LASX, gen_xxx, gen_helper_vdiv_hu) +-TRANS(xvdiv_wu, LASX, gen_xxx, gen_helper_vdiv_wu) +-TRANS(xvdiv_du, LASX, gen_xxx, gen_helper_vdiv_du) +-TRANS(xvmod_b, LASX, gen_xxx, gen_helper_vmod_b) +-TRANS(xvmod_h, LASX, gen_xxx, gen_helper_vmod_h) +-TRANS(xvmod_w, LASX, gen_xxx, gen_helper_vmod_w) +-TRANS(xvmod_d, LASX, gen_xxx, gen_helper_vmod_d) +-TRANS(xvmod_bu, LASX, gen_xxx, gen_helper_vmod_bu) +-TRANS(xvmod_hu, LASX, gen_xxx, gen_helper_vmod_hu) +-TRANS(xvmod_wu, LASX, gen_xxx, gen_helper_vmod_wu) +-TRANS(xvmod_du, LASX, gen_xxx, gen_helper_vmod_du) +- +-static void gen_vsat_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec max) +-{ +- TCGv_vec min; +- +- min = tcg_temp_new_vec_matching(t); +- tcg_gen_not_vec(vece, min, max); +- tcg_gen_smax_vec(vece, t, a, min); +- tcg_gen_smin_vec(vece, t, t, max); +-} +- +-static void do_vsat_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- int64_t imm, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_smax_vec, INDEX_op_smin_vec, 0 +- }; +- static const GVecGen2s op[4] = { +- { +- .fniv = gen_vsat_s, +- .fno = gen_helper_vsat_b, +- .opt_opc = vecop_list, +- .vece = MO_8 +- }, +- { +- .fniv = gen_vsat_s, +- .fno = gen_helper_vsat_h, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fniv = gen_vsat_s, +- .fno = gen_helper_vsat_w, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fniv = gen_vsat_s, +- .fno = gen_helper_vsat_d, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_2s(vd_ofs, vj_ofs, oprsz, maxsz, +- tcg_constant_i64((1ll<< imm) -1), &op[vece]); +-} +- +-TRANS(vsat_b, LSX, gvec_vv_i, MO_8, do_vsat_s) +-TRANS(vsat_h, LSX, gvec_vv_i, MO_16, do_vsat_s) +-TRANS(vsat_w, LSX, gvec_vv_i, MO_32, do_vsat_s) +-TRANS(vsat_d, LSX, gvec_vv_i, MO_64, do_vsat_s) +-TRANS(xvsat_b, LASX, gvec_xx_i, MO_8, do_vsat_s) +-TRANS(xvsat_h, LASX, gvec_xx_i, MO_16, do_vsat_s) +-TRANS(xvsat_w, LASX, gvec_xx_i, MO_32, do_vsat_s) +-TRANS(xvsat_d, LASX, gvec_xx_i, MO_64, do_vsat_s) +- +-static void gen_vsat_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec max) +-{ +- tcg_gen_umin_vec(vece, t, a, max); +-} +- +-static void do_vsat_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- int64_t imm, uint32_t oprsz, uint32_t maxsz) +-{ +- uint64_t max; +- static const TCGOpcode vecop_list[] = { +- INDEX_op_umin_vec, 0 +- }; +- static const GVecGen2s op[4] = { +- { +- .fniv = gen_vsat_u, +- .fno = gen_helper_vsat_bu, +- .opt_opc = vecop_list, +- .vece = MO_8 +- }, +- { +- .fniv = gen_vsat_u, +- .fno = gen_helper_vsat_hu, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fniv = gen_vsat_u, +- .fno = gen_helper_vsat_wu, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fniv = gen_vsat_u, +- .fno = gen_helper_vsat_du, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- max = (imm == 0x3f) ? UINT64_MAX : (1ull << (imm + 1)) - 1; +- tcg_gen_gvec_2s(vd_ofs, vj_ofs, oprsz, maxsz, +- tcg_constant_i64(max), &op[vece]); +-} +- +-TRANS(vsat_bu, LSX, gvec_vv_i, MO_8, do_vsat_u) +-TRANS(vsat_hu, LSX, gvec_vv_i, MO_16, do_vsat_u) +-TRANS(vsat_wu, LSX, gvec_vv_i, MO_32, do_vsat_u) +-TRANS(vsat_du, LSX, gvec_vv_i, MO_64, do_vsat_u) +-TRANS(xvsat_bu, LASX, gvec_xx_i, MO_8, do_vsat_u) +-TRANS(xvsat_hu, LASX, gvec_xx_i, MO_16, do_vsat_u) +-TRANS(xvsat_wu, LASX, gvec_xx_i, MO_32, do_vsat_u) +-TRANS(xvsat_du, LASX, gvec_xx_i, MO_64, do_vsat_u) +- +-TRANS(vexth_h_b, LSX, gen_vv, gen_helper_vexth_h_b) +-TRANS(vexth_w_h, LSX, gen_vv, gen_helper_vexth_w_h) +-TRANS(vexth_d_w, LSX, gen_vv, gen_helper_vexth_d_w) +-TRANS(vexth_q_d, LSX, gen_vv, gen_helper_vexth_q_d) +-TRANS(vexth_hu_bu, LSX, gen_vv, gen_helper_vexth_hu_bu) +-TRANS(vexth_wu_hu, LSX, gen_vv, gen_helper_vexth_wu_hu) +-TRANS(vexth_du_wu, LSX, gen_vv, gen_helper_vexth_du_wu) +-TRANS(vexth_qu_du, LSX, gen_vv, gen_helper_vexth_qu_du) +-TRANS(xvexth_h_b, LASX, gen_xx, gen_helper_vexth_h_b) +-TRANS(xvexth_w_h, LASX, gen_xx, gen_helper_vexth_w_h) +-TRANS(xvexth_d_w, LASX, gen_xx, gen_helper_vexth_d_w) +-TRANS(xvexth_q_d, LASX, gen_xx, gen_helper_vexth_q_d) +-TRANS(xvexth_hu_bu, LASX, gen_xx, gen_helper_vexth_hu_bu) +-TRANS(xvexth_wu_hu, LASX, gen_xx, gen_helper_vexth_wu_hu) +-TRANS(xvexth_du_wu, LASX, gen_xx, gen_helper_vexth_du_wu) +-TRANS(xvexth_qu_du, LASX, gen_xx, gen_helper_vexth_qu_du) +- +-TRANS(vext2xv_h_b, LASX, gen_xx, gen_helper_vext2xv_h_b) +-TRANS(vext2xv_w_b, LASX, gen_xx, gen_helper_vext2xv_w_b) +-TRANS(vext2xv_d_b, LASX, gen_xx, gen_helper_vext2xv_d_b) +-TRANS(vext2xv_w_h, LASX, gen_xx, gen_helper_vext2xv_w_h) +-TRANS(vext2xv_d_h, LASX, gen_xx, gen_helper_vext2xv_d_h) +-TRANS(vext2xv_d_w, LASX, gen_xx, gen_helper_vext2xv_d_w) +-TRANS(vext2xv_hu_bu, LASX, gen_xx, gen_helper_vext2xv_hu_bu) +-TRANS(vext2xv_wu_bu, LASX, gen_xx, gen_helper_vext2xv_wu_bu) +-TRANS(vext2xv_du_bu, LASX, gen_xx, gen_helper_vext2xv_du_bu) +-TRANS(vext2xv_wu_hu, LASX, gen_xx, gen_helper_vext2xv_wu_hu) +-TRANS(vext2xv_du_hu, LASX, gen_xx, gen_helper_vext2xv_du_hu) +-TRANS(vext2xv_du_wu, LASX, gen_xx, gen_helper_vext2xv_du_wu) +- +-static void gen_vsigncov(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- TCGv_vec t1, zero; +- +- t1 = tcg_temp_new_vec_matching(t); +- zero = tcg_constant_vec_matching(t, vece, 0); +- +- tcg_gen_neg_vec(vece, t1, b); +- tcg_gen_cmpsel_vec(TCG_COND_LT, vece, t, a, zero, t1, b); +- tcg_gen_cmpsel_vec(TCG_COND_EQ, vece, t, a, zero, zero, t); +-} +- +-static void do_vsigncov(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_neg_vec, INDEX_op_cmpsel_vec, 0 +- }; +- static const GVecGen3 op[4] = { +- { +- .fniv = gen_vsigncov, +- .fno = gen_helper_vsigncov_b, +- .opt_opc = vecop_list, +- .vece = MO_8 +- }, +- { +- .fniv = gen_vsigncov, +- .fno = gen_helper_vsigncov_h, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fniv = gen_vsigncov, +- .fno = gen_helper_vsigncov_w, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fniv = gen_vsigncov, +- .fno = gen_helper_vsigncov_d, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vsigncov_b, LSX, gvec_vvv, MO_8, do_vsigncov) +-TRANS(vsigncov_h, LSX, gvec_vvv, MO_16, do_vsigncov) +-TRANS(vsigncov_w, LSX, gvec_vvv, MO_32, do_vsigncov) +-TRANS(vsigncov_d, LSX, gvec_vvv, MO_64, do_vsigncov) +-TRANS(xvsigncov_b, LASX, gvec_xxx, MO_8, do_vsigncov) +-TRANS(xvsigncov_h, LASX, gvec_xxx, MO_16, do_vsigncov) +-TRANS(xvsigncov_w, LASX, gvec_xxx, MO_32, do_vsigncov) +-TRANS(xvsigncov_d, LASX, gvec_xxx, MO_64, do_vsigncov) +- +-TRANS(vmskltz_b, LSX, gen_vv, gen_helper_vmskltz_b) +-TRANS(vmskltz_h, LSX, gen_vv, gen_helper_vmskltz_h) +-TRANS(vmskltz_w, LSX, gen_vv, gen_helper_vmskltz_w) +-TRANS(vmskltz_d, LSX, gen_vv, gen_helper_vmskltz_d) +-TRANS(vmskgez_b, LSX, gen_vv, gen_helper_vmskgez_b) +-TRANS(vmsknz_b, LSX, gen_vv, gen_helper_vmsknz_b) +-TRANS(xvmskltz_b, LASX, gen_xx, gen_helper_vmskltz_b) +-TRANS(xvmskltz_h, LASX, gen_xx, gen_helper_vmskltz_h) +-TRANS(xvmskltz_w, LASX, gen_xx, gen_helper_vmskltz_w) +-TRANS(xvmskltz_d, LASX, gen_xx, gen_helper_vmskltz_d) +-TRANS(xvmskgez_b, LASX, gen_xx, gen_helper_vmskgez_b) +-TRANS(xvmsknz_b, LASX, gen_xx, gen_helper_vmsknz_b) +- +-#define EXPAND_BYTE(bit) ((uint64_t)(bit ? 0xff : 0)) +- +-static uint64_t vldi_get_value(DisasContext *ctx, uint32_t imm) +-{ +- int mode; +- uint64_t data, t; +- +- /* +- * imm bit [11:8] is mode, mode value is 0-12. +- * other values are invalid. +- */ +- mode = (imm >> 8) & 0xf; +- t = imm & 0xff; +- switch (mode) { +- case 0: +- /* data: {2{24'0, imm[7:0]}} */ +- data = (t << 32) | t ; +- break; +- case 1: +- /* data: {2{16'0, imm[7:0], 8'0}} */ +- data = (t << 24) | (t << 8); +- break; +- case 2: +- /* data: {2{8'0, imm[7:0], 16'0}} */ +- data = (t << 48) | (t << 16); +- break; +- case 3: +- /* data: {2{imm[7:0], 24'0}} */ +- data = (t << 56) | (t << 24); +- break; +- case 4: +- /* data: {4{8'0, imm[7:0]}} */ +- data = (t << 48) | (t << 32) | (t << 16) | t; +- break; +- case 5: +- /* data: {4{imm[7:0], 8'0}} */ +- data = (t << 56) |(t << 40) | (t << 24) | (t << 8); +- break; +- case 6: +- /* data: {2{16'0, imm[7:0], 8'1}} */ +- data = (t << 40) | ((uint64_t)0xff << 32) | (t << 8) | 0xff; +- break; +- case 7: +- /* data: {2{8'0, imm[7:0], 16'1}} */ +- data = (t << 48) | ((uint64_t)0xffff << 32) | (t << 16) | 0xffff; +- break; +- case 8: +- /* data: {8{imm[7:0]}} */ +- data =(t << 56) | (t << 48) | (t << 40) | (t << 32) | +- (t << 24) | (t << 16) | (t << 8) | t; +- break; +- case 9: +- /* data: {{8{imm[7]}, ..., 8{imm[0]}}} */ +- { +- uint64_t b0,b1,b2,b3,b4,b5,b6,b7; +- b0 = t& 0x1; +- b1 = (t & 0x2) >> 1; +- b2 = (t & 0x4) >> 2; +- b3 = (t & 0x8) >> 3; +- b4 = (t & 0x10) >> 4; +- b5 = (t & 0x20) >> 5; +- b6 = (t & 0x40) >> 6; +- b7 = (t & 0x80) >> 7; +- data = (EXPAND_BYTE(b7) << 56) | +- (EXPAND_BYTE(b6) << 48) | +- (EXPAND_BYTE(b5) << 40) | +- (EXPAND_BYTE(b4) << 32) | +- (EXPAND_BYTE(b3) << 24) | +- (EXPAND_BYTE(b2) << 16) | +- (EXPAND_BYTE(b1) << 8) | +- EXPAND_BYTE(b0); +- } +- break; +- case 10: +- /* data: {2{imm[7], ~imm[6], {5{imm[6]}}, imm[5:0], 19'0}} */ +- { +- uint64_t b6, b7; +- uint64_t t0, t1; +- b6 = (imm & 0x40) >> 6; +- b7 = (imm & 0x80) >> 7; +- t0 = (imm & 0x3f); +- t1 = (b7 << 6) | ((1-b6) << 5) | (uint64_t)(b6 ? 0x1f : 0); +- data = (t1 << 57) | (t0 << 51) | (t1 << 25) | (t0 << 19); +- } +- break; +- case 11: +- /* data: {32'0, imm[7], ~{imm[6]}, 5{imm[6]}, imm[5:0], 19'0} */ +- { +- uint64_t b6,b7; +- uint64_t t0, t1; +- b6 = (imm & 0x40) >> 6; +- b7 = (imm & 0x80) >> 7; +- t0 = (imm & 0x3f); +- t1 = (b7 << 6) | ((1-b6) << 5) | (b6 ? 0x1f : 0); +- data = (t1 << 25) | (t0 << 19); +- } +- break; +- case 12: +- /* data: {imm[7], ~imm[6], 8{imm[6]}, imm[5:0], 48'0} */ +- { +- uint64_t b6,b7; +- uint64_t t0, t1; +- b6 = (imm & 0x40) >> 6; +- b7 = (imm & 0x80) >> 7; +- t0 = (imm & 0x3f); +- t1 = (b7 << 9) | ((1-b6) << 8) | (b6 ? 0xff : 0); +- data = (t1 << 54) | (t0 << 48); +- } +- break; +- default: +- generate_exception(ctx, EXCCODE_INE); +- g_assert_not_reached(); +- } +- return data; +-} +- +-static bool gen_vldi(DisasContext *ctx, arg_vldi *a, uint32_t oprsz) +-{ +- int sel, vece; +- uint64_t value; +- +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- sel = (a->imm >> 12) & 0x1; +- +- if (sel) { +- value = vldi_get_value(ctx, a->imm); +- vece = MO_64; +- } else { +- value = ((int32_t)(a->imm << 22)) >> 22; +- vece = (a->imm >> 10) & 0x3; +- } +- +- tcg_gen_gvec_dup_i64(vece, vec_full_offset(a->vd), oprsz, ctx->vl/8, +- tcg_constant_i64(value)); +- return true; +-} +- +-TRANS(vldi, LSX, gen_vldi, 16) +-TRANS(xvldi, LASX, gen_vldi, 32) +- +-static bool gen_vandn_v(DisasContext *ctx, arg_vvv *a, uint32_t oprsz) +-{ +- uint32_t vd_ofs, vj_ofs, vk_ofs; +- +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- vd_ofs = vec_full_offset(a->vd); +- vj_ofs = vec_full_offset(a->vj); +- vk_ofs = vec_full_offset(a->vk); +- +- tcg_gen_gvec_andc(MO_64, vd_ofs, vk_ofs, vj_ofs, oprsz, ctx->vl / 8); +- return true; +-} +- +-static void gen_vnori(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) +-{ +- TCGv_vec t1; +- +- t1 = tcg_constant_vec_matching(t, vece, imm); +- tcg_gen_nor_vec(vece, t, a, t1); +-} +- +-static void gen_vnori_b(TCGv_i64 t, TCGv_i64 a, int64_t imm) +-{ +- tcg_gen_movi_i64(t, dup_const(MO_8, imm)); +- tcg_gen_nor_i64(t, a, t); +-} +- +-static void do_vnori_b(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- int64_t imm, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_nor_vec, 0 +- }; +- static const GVecGen2i op = { +- .fni8 = gen_vnori_b, +- .fniv = gen_vnori, +- .fnoi = gen_helper_vnori_b, +- .opt_opc = vecop_list, +- .vece = MO_8 +- }; +- +- tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op); +-} +- +-TRANS(vand_v, LSX, gvec_vvv, MO_64, tcg_gen_gvec_and) +-TRANS(vor_v, LSX, gvec_vvv, MO_64, tcg_gen_gvec_or) +-TRANS(vxor_v, LSX, gvec_vvv, MO_64, tcg_gen_gvec_xor) +-TRANS(vnor_v, LSX, gvec_vvv, MO_64, tcg_gen_gvec_nor) +-TRANS(vandn_v, LSX, gen_vandn_v, 16) +-TRANS(vorn_v, LSX, gvec_vvv, MO_64, tcg_gen_gvec_orc) +-TRANS(vandi_b, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_andi) +-TRANS(vori_b, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_ori) +-TRANS(vxori_b, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_xori) +-TRANS(vnori_b, LSX, gvec_vv_i, MO_8, do_vnori_b) +-TRANS(xvand_v, LASX, gvec_xxx, MO_64, tcg_gen_gvec_and) +-TRANS(xvor_v, LASX, gvec_xxx, MO_64, tcg_gen_gvec_or) +-TRANS(xvxor_v, LASX, gvec_xxx, MO_64, tcg_gen_gvec_xor) +-TRANS(xvnor_v, LASX, gvec_xxx, MO_64, tcg_gen_gvec_nor) +-TRANS(xvandn_v, LASX, gen_vandn_v, 32) +-TRANS(xvorn_v, LASX, gvec_xxx, MO_64, tcg_gen_gvec_orc) +-TRANS(xvandi_b, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_andi) +-TRANS(xvori_b, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_ori) +-TRANS(xvxori_b, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_xori) +-TRANS(xvnori_b, LASX, gvec_xx_i, MO_8, do_vnori_b) +- +-TRANS(vsll_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_shlv) +-TRANS(vsll_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_shlv) +-TRANS(vsll_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_shlv) +-TRANS(vsll_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_shlv) +-TRANS(vslli_b, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_shli) +-TRANS(vslli_h, LSX, gvec_vv_i, MO_16, tcg_gen_gvec_shli) +-TRANS(vslli_w, LSX, gvec_vv_i, MO_32, tcg_gen_gvec_shli) +-TRANS(vslli_d, LSX, gvec_vv_i, MO_64, tcg_gen_gvec_shli) +-TRANS(xvsll_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_shlv) +-TRANS(xvsll_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_shlv) +-TRANS(xvsll_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_shlv) +-TRANS(xvsll_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_shlv) +-TRANS(xvslli_b, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_shli) +-TRANS(xvslli_h, LASX, gvec_xx_i, MO_16, tcg_gen_gvec_shli) +-TRANS(xvslli_w, LASX, gvec_xx_i, MO_32, tcg_gen_gvec_shli) +-TRANS(xvslli_d, LASX, gvec_xx_i, MO_64, tcg_gen_gvec_shli) +- +-TRANS(vsrl_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_shrv) +-TRANS(vsrl_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_shrv) +-TRANS(vsrl_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_shrv) +-TRANS(vsrl_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_shrv) +-TRANS(vsrli_b, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_shri) +-TRANS(vsrli_h, LSX, gvec_vv_i, MO_16, tcg_gen_gvec_shri) +-TRANS(vsrli_w, LSX, gvec_vv_i, MO_32, tcg_gen_gvec_shri) +-TRANS(vsrli_d, LSX, gvec_vv_i, MO_64, tcg_gen_gvec_shri) +-TRANS(xvsrl_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_shrv) +-TRANS(xvsrl_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_shrv) +-TRANS(xvsrl_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_shrv) +-TRANS(xvsrl_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_shrv) +-TRANS(xvsrli_b, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_shri) +-TRANS(xvsrli_h, LASX, gvec_xx_i, MO_16, tcg_gen_gvec_shri) +-TRANS(xvsrli_w, LASX, gvec_xx_i, MO_32, tcg_gen_gvec_shri) +-TRANS(xvsrli_d, LASX, gvec_xx_i, MO_64, tcg_gen_gvec_shri) +- +-TRANS(vsra_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_sarv) +-TRANS(vsra_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_sarv) +-TRANS(vsra_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_sarv) +-TRANS(vsra_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_sarv) +-TRANS(vsrai_b, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_sari) +-TRANS(vsrai_h, LSX, gvec_vv_i, MO_16, tcg_gen_gvec_sari) +-TRANS(vsrai_w, LSX, gvec_vv_i, MO_32, tcg_gen_gvec_sari) +-TRANS(vsrai_d, LSX, gvec_vv_i, MO_64, tcg_gen_gvec_sari) +-TRANS(xvsra_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_sarv) +-TRANS(xvsra_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_sarv) +-TRANS(xvsra_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_sarv) +-TRANS(xvsra_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_sarv) +-TRANS(xvsrai_b, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_sari) +-TRANS(xvsrai_h, LASX, gvec_xx_i, MO_16, tcg_gen_gvec_sari) +-TRANS(xvsrai_w, LASX, gvec_xx_i, MO_32, tcg_gen_gvec_sari) +-TRANS(xvsrai_d, LASX, gvec_xx_i, MO_64, tcg_gen_gvec_sari) +- +-TRANS(vrotr_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_rotrv) +-TRANS(vrotr_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_rotrv) +-TRANS(vrotr_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_rotrv) +-TRANS(vrotr_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_rotrv) +-TRANS(vrotri_b, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_rotri) +-TRANS(vrotri_h, LSX, gvec_vv_i, MO_16, tcg_gen_gvec_rotri) +-TRANS(vrotri_w, LSX, gvec_vv_i, MO_32, tcg_gen_gvec_rotri) +-TRANS(vrotri_d, LSX, gvec_vv_i, MO_64, tcg_gen_gvec_rotri) +-TRANS(xvrotr_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_rotrv) +-TRANS(xvrotr_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_rotrv) +-TRANS(xvrotr_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_rotrv) +-TRANS(xvrotr_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_rotrv) +-TRANS(xvrotri_b, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_rotri) +-TRANS(xvrotri_h, LASX, gvec_xx_i, MO_16, tcg_gen_gvec_rotri) +-TRANS(xvrotri_w, LASX, gvec_xx_i, MO_32, tcg_gen_gvec_rotri) +-TRANS(xvrotri_d, LASX, gvec_xx_i, MO_64, tcg_gen_gvec_rotri) +- +-TRANS(vsllwil_h_b, LSX, gen_vv_i, gen_helper_vsllwil_h_b) +-TRANS(vsllwil_w_h, LSX, gen_vv_i, gen_helper_vsllwil_w_h) +-TRANS(vsllwil_d_w, LSX, gen_vv_i, gen_helper_vsllwil_d_w) +-TRANS(vextl_q_d, LSX, gen_vv, gen_helper_vextl_q_d) +-TRANS(vsllwil_hu_bu, LSX, gen_vv_i, gen_helper_vsllwil_hu_bu) +-TRANS(vsllwil_wu_hu, LSX, gen_vv_i, gen_helper_vsllwil_wu_hu) +-TRANS(vsllwil_du_wu, LSX, gen_vv_i, gen_helper_vsllwil_du_wu) +-TRANS(vextl_qu_du, LSX, gen_vv, gen_helper_vextl_qu_du) +-TRANS(xvsllwil_h_b, LASX, gen_xx_i, gen_helper_vsllwil_h_b) +-TRANS(xvsllwil_w_h, LASX, gen_xx_i, gen_helper_vsllwil_w_h) +-TRANS(xvsllwil_d_w, LASX, gen_xx_i, gen_helper_vsllwil_d_w) +-TRANS(xvextl_q_d, LASX, gen_xx, gen_helper_vextl_q_d) +-TRANS(xvsllwil_hu_bu, LASX, gen_xx_i, gen_helper_vsllwil_hu_bu) +-TRANS(xvsllwil_wu_hu, LASX, gen_xx_i, gen_helper_vsllwil_wu_hu) +-TRANS(xvsllwil_du_wu, LASX, gen_xx_i, gen_helper_vsllwil_du_wu) +-TRANS(xvextl_qu_du, LASX, gen_xx, gen_helper_vextl_qu_du) +- +-TRANS(vsrlr_b, LSX, gen_vvv, gen_helper_vsrlr_b) +-TRANS(vsrlr_h, LSX, gen_vvv, gen_helper_vsrlr_h) +-TRANS(vsrlr_w, LSX, gen_vvv, gen_helper_vsrlr_w) +-TRANS(vsrlr_d, LSX, gen_vvv, gen_helper_vsrlr_d) +-TRANS(vsrlri_b, LSX, gen_vv_i, gen_helper_vsrlri_b) +-TRANS(vsrlri_h, LSX, gen_vv_i, gen_helper_vsrlri_h) +-TRANS(vsrlri_w, LSX, gen_vv_i, gen_helper_vsrlri_w) +-TRANS(vsrlri_d, LSX, gen_vv_i, gen_helper_vsrlri_d) +-TRANS(xvsrlr_b, LASX, gen_xxx, gen_helper_vsrlr_b) +-TRANS(xvsrlr_h, LASX, gen_xxx, gen_helper_vsrlr_h) +-TRANS(xvsrlr_w, LASX, gen_xxx, gen_helper_vsrlr_w) +-TRANS(xvsrlr_d, LASX, gen_xxx, gen_helper_vsrlr_d) +-TRANS(xvsrlri_b, LASX, gen_xx_i, gen_helper_vsrlri_b) +-TRANS(xvsrlri_h, LASX, gen_xx_i, gen_helper_vsrlri_h) +-TRANS(xvsrlri_w, LASX, gen_xx_i, gen_helper_vsrlri_w) +-TRANS(xvsrlri_d, LASX, gen_xx_i, gen_helper_vsrlri_d) +- +-TRANS(vsrar_b, LSX, gen_vvv, gen_helper_vsrar_b) +-TRANS(vsrar_h, LSX, gen_vvv, gen_helper_vsrar_h) +-TRANS(vsrar_w, LSX, gen_vvv, gen_helper_vsrar_w) +-TRANS(vsrar_d, LSX, gen_vvv, gen_helper_vsrar_d) +-TRANS(vsrari_b, LSX, gen_vv_i, gen_helper_vsrari_b) +-TRANS(vsrari_h, LSX, gen_vv_i, gen_helper_vsrari_h) +-TRANS(vsrari_w, LSX, gen_vv_i, gen_helper_vsrari_w) +-TRANS(vsrari_d, LSX, gen_vv_i, gen_helper_vsrari_d) +-TRANS(xvsrar_b, LASX, gen_xxx, gen_helper_vsrar_b) +-TRANS(xvsrar_h, LASX, gen_xxx, gen_helper_vsrar_h) +-TRANS(xvsrar_w, LASX, gen_xxx, gen_helper_vsrar_w) +-TRANS(xvsrar_d, LASX, gen_xxx, gen_helper_vsrar_d) +-TRANS(xvsrari_b, LASX, gen_xx_i, gen_helper_vsrari_b) +-TRANS(xvsrari_h, LASX, gen_xx_i, gen_helper_vsrari_h) +-TRANS(xvsrari_w, LASX, gen_xx_i, gen_helper_vsrari_w) +-TRANS(xvsrari_d, LASX, gen_xx_i, gen_helper_vsrari_d) +- +-TRANS(vsrln_b_h, LSX, gen_vvv, gen_helper_vsrln_b_h) +-TRANS(vsrln_h_w, LSX, gen_vvv, gen_helper_vsrln_h_w) +-TRANS(vsrln_w_d, LSX, gen_vvv, gen_helper_vsrln_w_d) +-TRANS(vsran_b_h, LSX, gen_vvv, gen_helper_vsran_b_h) +-TRANS(vsran_h_w, LSX, gen_vvv, gen_helper_vsran_h_w) +-TRANS(vsran_w_d, LSX, gen_vvv, gen_helper_vsran_w_d) +-TRANS(xvsrln_b_h, LASX, gen_xxx, gen_helper_vsrln_b_h) +-TRANS(xvsrln_h_w, LASX, gen_xxx, gen_helper_vsrln_h_w) +-TRANS(xvsrln_w_d, LASX, gen_xxx, gen_helper_vsrln_w_d) +-TRANS(xvsran_b_h, LASX, gen_xxx, gen_helper_vsran_b_h) +-TRANS(xvsran_h_w, LASX, gen_xxx, gen_helper_vsran_h_w) +-TRANS(xvsran_w_d, LASX, gen_xxx, gen_helper_vsran_w_d) +- +-TRANS(vsrlni_b_h, LSX, gen_vv_i, gen_helper_vsrlni_b_h) +-TRANS(vsrlni_h_w, LSX, gen_vv_i, gen_helper_vsrlni_h_w) +-TRANS(vsrlni_w_d, LSX, gen_vv_i, gen_helper_vsrlni_w_d) +-TRANS(vsrlni_d_q, LSX, gen_vv_i, gen_helper_vsrlni_d_q) +-TRANS(vsrani_b_h, LSX, gen_vv_i, gen_helper_vsrani_b_h) +-TRANS(vsrani_h_w, LSX, gen_vv_i, gen_helper_vsrani_h_w) +-TRANS(vsrani_w_d, LSX, gen_vv_i, gen_helper_vsrani_w_d) +-TRANS(vsrani_d_q, LSX, gen_vv_i, gen_helper_vsrani_d_q) +-TRANS(xvsrlni_b_h, LASX, gen_xx_i, gen_helper_vsrlni_b_h) +-TRANS(xvsrlni_h_w, LASX, gen_xx_i, gen_helper_vsrlni_h_w) +-TRANS(xvsrlni_w_d, LASX, gen_xx_i, gen_helper_vsrlni_w_d) +-TRANS(xvsrlni_d_q, LASX, gen_xx_i, gen_helper_vsrlni_d_q) +-TRANS(xvsrani_b_h, LASX, gen_xx_i, gen_helper_vsrani_b_h) +-TRANS(xvsrani_h_w, LASX, gen_xx_i, gen_helper_vsrani_h_w) +-TRANS(xvsrani_w_d, LASX, gen_xx_i, gen_helper_vsrani_w_d) +-TRANS(xvsrani_d_q, LASX, gen_xx_i, gen_helper_vsrani_d_q) +- +-TRANS(vsrlrn_b_h, LSX, gen_vvv, gen_helper_vsrlrn_b_h) +-TRANS(vsrlrn_h_w, LSX, gen_vvv, gen_helper_vsrlrn_h_w) +-TRANS(vsrlrn_w_d, LSX, gen_vvv, gen_helper_vsrlrn_w_d) +-TRANS(vsrarn_b_h, LSX, gen_vvv, gen_helper_vsrarn_b_h) +-TRANS(vsrarn_h_w, LSX, gen_vvv, gen_helper_vsrarn_h_w) +-TRANS(vsrarn_w_d, LSX, gen_vvv, gen_helper_vsrarn_w_d) +-TRANS(xvsrlrn_b_h, LASX, gen_xxx, gen_helper_vsrlrn_b_h) +-TRANS(xvsrlrn_h_w, LASX, gen_xxx, gen_helper_vsrlrn_h_w) +-TRANS(xvsrlrn_w_d, LASX, gen_xxx, gen_helper_vsrlrn_w_d) +-TRANS(xvsrarn_b_h, LASX, gen_xxx, gen_helper_vsrarn_b_h) +-TRANS(xvsrarn_h_w, LASX, gen_xxx, gen_helper_vsrarn_h_w) +-TRANS(xvsrarn_w_d, LASX, gen_xxx, gen_helper_vsrarn_w_d) +- +-TRANS(vsrlrni_b_h, LSX, gen_vv_i, gen_helper_vsrlrni_b_h) +-TRANS(vsrlrni_h_w, LSX, gen_vv_i, gen_helper_vsrlrni_h_w) +-TRANS(vsrlrni_w_d, LSX, gen_vv_i, gen_helper_vsrlrni_w_d) +-TRANS(vsrlrni_d_q, LSX, gen_vv_i, gen_helper_vsrlrni_d_q) +-TRANS(vsrarni_b_h, LSX, gen_vv_i, gen_helper_vsrarni_b_h) +-TRANS(vsrarni_h_w, LSX, gen_vv_i, gen_helper_vsrarni_h_w) +-TRANS(vsrarni_w_d, LSX, gen_vv_i, gen_helper_vsrarni_w_d) +-TRANS(vsrarni_d_q, LSX, gen_vv_i, gen_helper_vsrarni_d_q) +-TRANS(xvsrlrni_b_h, LASX, gen_xx_i, gen_helper_vsrlrni_b_h) +-TRANS(xvsrlrni_h_w, LASX, gen_xx_i, gen_helper_vsrlrni_h_w) +-TRANS(xvsrlrni_w_d, LASX, gen_xx_i, gen_helper_vsrlrni_w_d) +-TRANS(xvsrlrni_d_q, LASX, gen_xx_i, gen_helper_vsrlrni_d_q) +-TRANS(xvsrarni_b_h, LASX, gen_xx_i, gen_helper_vsrarni_b_h) +-TRANS(xvsrarni_h_w, LASX, gen_xx_i, gen_helper_vsrarni_h_w) +-TRANS(xvsrarni_w_d, LASX, gen_xx_i, gen_helper_vsrarni_w_d) +-TRANS(xvsrarni_d_q, LASX, gen_xx_i, gen_helper_vsrarni_d_q) +- +-TRANS(vssrln_b_h, LSX, gen_vvv, gen_helper_vssrln_b_h) +-TRANS(vssrln_h_w, LSX, gen_vvv, gen_helper_vssrln_h_w) +-TRANS(vssrln_w_d, LSX, gen_vvv, gen_helper_vssrln_w_d) +-TRANS(vssran_b_h, LSX, gen_vvv, gen_helper_vssran_b_h) +-TRANS(vssran_h_w, LSX, gen_vvv, gen_helper_vssran_h_w) +-TRANS(vssran_w_d, LSX, gen_vvv, gen_helper_vssran_w_d) +-TRANS(vssrln_bu_h, LSX, gen_vvv, gen_helper_vssrln_bu_h) +-TRANS(vssrln_hu_w, LSX, gen_vvv, gen_helper_vssrln_hu_w) +-TRANS(vssrln_wu_d, LSX, gen_vvv, gen_helper_vssrln_wu_d) +-TRANS(vssran_bu_h, LSX, gen_vvv, gen_helper_vssran_bu_h) +-TRANS(vssran_hu_w, LSX, gen_vvv, gen_helper_vssran_hu_w) +-TRANS(vssran_wu_d, LSX, gen_vvv, gen_helper_vssran_wu_d) +-TRANS(xvssrln_b_h, LASX, gen_xxx, gen_helper_vssrln_b_h) +-TRANS(xvssrln_h_w, LASX, gen_xxx, gen_helper_vssrln_h_w) +-TRANS(xvssrln_w_d, LASX, gen_xxx, gen_helper_vssrln_w_d) +-TRANS(xvssran_b_h, LASX, gen_xxx, gen_helper_vssran_b_h) +-TRANS(xvssran_h_w, LASX, gen_xxx, gen_helper_vssran_h_w) +-TRANS(xvssran_w_d, LASX, gen_xxx, gen_helper_vssran_w_d) +-TRANS(xvssrln_bu_h, LASX, gen_xxx, gen_helper_vssrln_bu_h) +-TRANS(xvssrln_hu_w, LASX, gen_xxx, gen_helper_vssrln_hu_w) +-TRANS(xvssrln_wu_d, LASX, gen_xxx, gen_helper_vssrln_wu_d) +-TRANS(xvssran_bu_h, LASX, gen_xxx, gen_helper_vssran_bu_h) +-TRANS(xvssran_hu_w, LASX, gen_xxx, gen_helper_vssran_hu_w) +-TRANS(xvssran_wu_d, LASX, gen_xxx, gen_helper_vssran_wu_d) +- +-TRANS(vssrlni_b_h, LSX, gen_vv_i, gen_helper_vssrlni_b_h) +-TRANS(vssrlni_h_w, LSX, gen_vv_i, gen_helper_vssrlni_h_w) +-TRANS(vssrlni_w_d, LSX, gen_vv_i, gen_helper_vssrlni_w_d) +-TRANS(vssrlni_d_q, LSX, gen_vv_i, gen_helper_vssrlni_d_q) +-TRANS(vssrani_b_h, LSX, gen_vv_i, gen_helper_vssrani_b_h) +-TRANS(vssrani_h_w, LSX, gen_vv_i, gen_helper_vssrani_h_w) +-TRANS(vssrani_w_d, LSX, gen_vv_i, gen_helper_vssrani_w_d) +-TRANS(vssrani_d_q, LSX, gen_vv_i, gen_helper_vssrani_d_q) +-TRANS(vssrlni_bu_h, LSX, gen_vv_i, gen_helper_vssrlni_bu_h) +-TRANS(vssrlni_hu_w, LSX, gen_vv_i, gen_helper_vssrlni_hu_w) +-TRANS(vssrlni_wu_d, LSX, gen_vv_i, gen_helper_vssrlni_wu_d) +-TRANS(vssrlni_du_q, LSX, gen_vv_i, gen_helper_vssrlni_du_q) +-TRANS(vssrani_bu_h, LSX, gen_vv_i, gen_helper_vssrani_bu_h) +-TRANS(vssrani_hu_w, LSX, gen_vv_i, gen_helper_vssrani_hu_w) +-TRANS(vssrani_wu_d, LSX, gen_vv_i, gen_helper_vssrani_wu_d) +-TRANS(vssrani_du_q, LSX, gen_vv_i, gen_helper_vssrani_du_q) +-TRANS(xvssrlni_b_h, LASX, gen_xx_i, gen_helper_vssrlni_b_h) +-TRANS(xvssrlni_h_w, LASX, gen_xx_i, gen_helper_vssrlni_h_w) +-TRANS(xvssrlni_w_d, LASX, gen_xx_i, gen_helper_vssrlni_w_d) +-TRANS(xvssrlni_d_q, LASX, gen_xx_i, gen_helper_vssrlni_d_q) +-TRANS(xvssrani_b_h, LASX, gen_xx_i, gen_helper_vssrani_b_h) +-TRANS(xvssrani_h_w, LASX, gen_xx_i, gen_helper_vssrani_h_w) +-TRANS(xvssrani_w_d, LASX, gen_xx_i, gen_helper_vssrani_w_d) +-TRANS(xvssrani_d_q, LASX, gen_xx_i, gen_helper_vssrani_d_q) +-TRANS(xvssrlni_bu_h, LASX, gen_xx_i, gen_helper_vssrlni_bu_h) +-TRANS(xvssrlni_hu_w, LASX, gen_xx_i, gen_helper_vssrlni_hu_w) +-TRANS(xvssrlni_wu_d, LASX, gen_xx_i, gen_helper_vssrlni_wu_d) +-TRANS(xvssrlni_du_q, LASX, gen_xx_i, gen_helper_vssrlni_du_q) +-TRANS(xvssrani_bu_h, LASX, gen_xx_i, gen_helper_vssrani_bu_h) +-TRANS(xvssrani_hu_w, LASX, gen_xx_i, gen_helper_vssrani_hu_w) +-TRANS(xvssrani_wu_d, LASX, gen_xx_i, gen_helper_vssrani_wu_d) +-TRANS(xvssrani_du_q, LASX, gen_xx_i, gen_helper_vssrani_du_q) +- +-TRANS(vssrlrn_b_h, LSX, gen_vvv, gen_helper_vssrlrn_b_h) +-TRANS(vssrlrn_h_w, LSX, gen_vvv, gen_helper_vssrlrn_h_w) +-TRANS(vssrlrn_w_d, LSX, gen_vvv, gen_helper_vssrlrn_w_d) +-TRANS(vssrarn_b_h, LSX, gen_vvv, gen_helper_vssrarn_b_h) +-TRANS(vssrarn_h_w, LSX, gen_vvv, gen_helper_vssrarn_h_w) +-TRANS(vssrarn_w_d, LSX, gen_vvv, gen_helper_vssrarn_w_d) +-TRANS(vssrlrn_bu_h, LSX, gen_vvv, gen_helper_vssrlrn_bu_h) +-TRANS(vssrlrn_hu_w, LSX, gen_vvv, gen_helper_vssrlrn_hu_w) +-TRANS(vssrlrn_wu_d, LSX, gen_vvv, gen_helper_vssrlrn_wu_d) +-TRANS(vssrarn_bu_h, LSX, gen_vvv, gen_helper_vssrarn_bu_h) +-TRANS(vssrarn_hu_w, LSX, gen_vvv, gen_helper_vssrarn_hu_w) +-TRANS(vssrarn_wu_d, LSX, gen_vvv, gen_helper_vssrarn_wu_d) +-TRANS(xvssrlrn_b_h, LASX, gen_xxx, gen_helper_vssrlrn_b_h) +-TRANS(xvssrlrn_h_w, LASX, gen_xxx, gen_helper_vssrlrn_h_w) +-TRANS(xvssrlrn_w_d, LASX, gen_xxx, gen_helper_vssrlrn_w_d) +-TRANS(xvssrarn_b_h, LASX, gen_xxx, gen_helper_vssrarn_b_h) +-TRANS(xvssrarn_h_w, LASX, gen_xxx, gen_helper_vssrarn_h_w) +-TRANS(xvssrarn_w_d, LASX, gen_xxx, gen_helper_vssrarn_w_d) +-TRANS(xvssrlrn_bu_h, LASX, gen_xxx, gen_helper_vssrlrn_bu_h) +-TRANS(xvssrlrn_hu_w, LASX, gen_xxx, gen_helper_vssrlrn_hu_w) +-TRANS(xvssrlrn_wu_d, LASX, gen_xxx, gen_helper_vssrlrn_wu_d) +-TRANS(xvssrarn_bu_h, LASX, gen_xxx, gen_helper_vssrarn_bu_h) +-TRANS(xvssrarn_hu_w, LASX, gen_xxx, gen_helper_vssrarn_hu_w) +-TRANS(xvssrarn_wu_d, LASX, gen_xxx, gen_helper_vssrarn_wu_d) +- +-TRANS(vssrlrni_b_h, LSX, gen_vv_i, gen_helper_vssrlrni_b_h) +-TRANS(vssrlrni_h_w, LSX, gen_vv_i, gen_helper_vssrlrni_h_w) +-TRANS(vssrlrni_w_d, LSX, gen_vv_i, gen_helper_vssrlrni_w_d) +-TRANS(vssrlrni_d_q, LSX, gen_vv_i, gen_helper_vssrlrni_d_q) +-TRANS(vssrarni_b_h, LSX, gen_vv_i, gen_helper_vssrarni_b_h) +-TRANS(vssrarni_h_w, LSX, gen_vv_i, gen_helper_vssrarni_h_w) +-TRANS(vssrarni_w_d, LSX, gen_vv_i, gen_helper_vssrarni_w_d) +-TRANS(vssrarni_d_q, LSX, gen_vv_i, gen_helper_vssrarni_d_q) +-TRANS(vssrlrni_bu_h, LSX, gen_vv_i, gen_helper_vssrlrni_bu_h) +-TRANS(vssrlrni_hu_w, LSX, gen_vv_i, gen_helper_vssrlrni_hu_w) +-TRANS(vssrlrni_wu_d, LSX, gen_vv_i, gen_helper_vssrlrni_wu_d) +-TRANS(vssrlrni_du_q, LSX, gen_vv_i, gen_helper_vssrlrni_du_q) +-TRANS(vssrarni_bu_h, LSX, gen_vv_i, gen_helper_vssrarni_bu_h) +-TRANS(vssrarni_hu_w, LSX, gen_vv_i, gen_helper_vssrarni_hu_w) +-TRANS(vssrarni_wu_d, LSX, gen_vv_i, gen_helper_vssrarni_wu_d) +-TRANS(vssrarni_du_q, LSX, gen_vv_i, gen_helper_vssrarni_du_q) +-TRANS(xvssrlrni_b_h, LASX, gen_xx_i, gen_helper_vssrlrni_b_h) +-TRANS(xvssrlrni_h_w, LASX, gen_xx_i, gen_helper_vssrlrni_h_w) +-TRANS(xvssrlrni_w_d, LASX, gen_xx_i, gen_helper_vssrlrni_w_d) +-TRANS(xvssrlrni_d_q, LASX, gen_xx_i, gen_helper_vssrlrni_d_q) +-TRANS(xvssrarni_b_h, LASX, gen_xx_i, gen_helper_vssrarni_b_h) +-TRANS(xvssrarni_h_w, LASX, gen_xx_i, gen_helper_vssrarni_h_w) +-TRANS(xvssrarni_w_d, LASX, gen_xx_i, gen_helper_vssrarni_w_d) +-TRANS(xvssrarni_d_q, LASX, gen_xx_i, gen_helper_vssrarni_d_q) +-TRANS(xvssrlrni_bu_h, LASX, gen_xx_i, gen_helper_vssrlrni_bu_h) +-TRANS(xvssrlrni_hu_w, LASX, gen_xx_i, gen_helper_vssrlrni_hu_w) +-TRANS(xvssrlrni_wu_d, LASX, gen_xx_i, gen_helper_vssrlrni_wu_d) +-TRANS(xvssrlrni_du_q, LASX, gen_xx_i, gen_helper_vssrlrni_du_q) +-TRANS(xvssrarni_bu_h, LASX, gen_xx_i, gen_helper_vssrarni_bu_h) +-TRANS(xvssrarni_hu_w, LASX, gen_xx_i, gen_helper_vssrarni_hu_w) +-TRANS(xvssrarni_wu_d, LASX, gen_xx_i, gen_helper_vssrarni_wu_d) +-TRANS(xvssrarni_du_q, LASX, gen_xx_i, gen_helper_vssrarni_du_q) +- +-TRANS(vclo_b, LSX, gen_vv, gen_helper_vclo_b) +-TRANS(vclo_h, LSX, gen_vv, gen_helper_vclo_h) +-TRANS(vclo_w, LSX, gen_vv, gen_helper_vclo_w) +-TRANS(vclo_d, LSX, gen_vv, gen_helper_vclo_d) +-TRANS(vclz_b, LSX, gen_vv, gen_helper_vclz_b) +-TRANS(vclz_h, LSX, gen_vv, gen_helper_vclz_h) +-TRANS(vclz_w, LSX, gen_vv, gen_helper_vclz_w) +-TRANS(vclz_d, LSX, gen_vv, gen_helper_vclz_d) +-TRANS(xvclo_b, LASX, gen_xx, gen_helper_vclo_b) +-TRANS(xvclo_h, LASX, gen_xx, gen_helper_vclo_h) +-TRANS(xvclo_w, LASX, gen_xx, gen_helper_vclo_w) +-TRANS(xvclo_d, LASX, gen_xx, gen_helper_vclo_d) +-TRANS(xvclz_b, LASX, gen_xx, gen_helper_vclz_b) +-TRANS(xvclz_h, LASX, gen_xx, gen_helper_vclz_h) +-TRANS(xvclz_w, LASX, gen_xx, gen_helper_vclz_w) +-TRANS(xvclz_d, LASX, gen_xx, gen_helper_vclz_d) +- +-TRANS(vpcnt_b, LSX, gen_vv, gen_helper_vpcnt_b) +-TRANS(vpcnt_h, LSX, gen_vv, gen_helper_vpcnt_h) +-TRANS(vpcnt_w, LSX, gen_vv, gen_helper_vpcnt_w) +-TRANS(vpcnt_d, LSX, gen_vv, gen_helper_vpcnt_d) +-TRANS(xvpcnt_b, LASX, gen_xx, gen_helper_vpcnt_b) +-TRANS(xvpcnt_h, LASX, gen_xx, gen_helper_vpcnt_h) +-TRANS(xvpcnt_w, LASX, gen_xx, gen_helper_vpcnt_w) +-TRANS(xvpcnt_d, LASX, gen_xx, gen_helper_vpcnt_d) +- +-static void do_vbit(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b, +- void (*func)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec)) +-{ +- TCGv_vec mask, lsh, t1, one; +- +- lsh = tcg_temp_new_vec_matching(t); +- t1 = tcg_temp_new_vec_matching(t); +- mask = tcg_constant_vec_matching(t, vece, (8 << vece) - 1); +- one = tcg_constant_vec_matching(t, vece, 1); +- +- tcg_gen_and_vec(vece, lsh, b, mask); +- tcg_gen_shlv_vec(vece, t1, one, lsh); +- func(vece, t, a, t1); +-} +- +-static void gen_vbitclr(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- do_vbit(vece, t, a, b, tcg_gen_andc_vec); +-} +- +-static void gen_vbitset(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- do_vbit(vece, t, a, b, tcg_gen_or_vec); +-} +- +-static void gen_vbitrev(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) +-{ +- do_vbit(vece, t, a, b, tcg_gen_xor_vec); +-} +- +-static void do_vbitclr(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_shlv_vec, INDEX_op_andc_vec, 0 +- }; +- static const GVecGen3 op[4] = { +- { +- .fniv = gen_vbitclr, +- .fno = gen_helper_vbitclr_b, +- .opt_opc = vecop_list, +- .vece = MO_8 +- }, +- { +- .fniv = gen_vbitclr, +- .fno = gen_helper_vbitclr_h, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fniv = gen_vbitclr, +- .fno = gen_helper_vbitclr_w, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fniv = gen_vbitclr, +- .fno = gen_helper_vbitclr_d, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vbitclr_b, LSX, gvec_vvv, MO_8, do_vbitclr) +-TRANS(vbitclr_h, LSX, gvec_vvv, MO_16, do_vbitclr) +-TRANS(vbitclr_w, LSX, gvec_vvv, MO_32, do_vbitclr) +-TRANS(vbitclr_d, LSX, gvec_vvv, MO_64, do_vbitclr) +-TRANS(xvbitclr_b, LASX, gvec_xxx, MO_8, do_vbitclr) +-TRANS(xvbitclr_h, LASX, gvec_xxx, MO_16, do_vbitclr) +-TRANS(xvbitclr_w, LASX, gvec_xxx, MO_32, do_vbitclr) +-TRANS(xvbitclr_d, LASX, gvec_xxx, MO_64, do_vbitclr) +- +-static void do_vbiti(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm, +- void (*func)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec)) +-{ +- int lsh; +- TCGv_vec t1, one; +- +- lsh = imm & ((8 << vece) -1); +- t1 = tcg_temp_new_vec_matching(t); +- one = tcg_constant_vec_matching(t, vece, 1); +- +- tcg_gen_shli_vec(vece, t1, one, lsh); +- func(vece, t, a, t1); +-} +- +-static void gen_vbitclri(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) +-{ +- do_vbiti(vece, t, a, imm, tcg_gen_andc_vec); +-} +- +-static void gen_vbitseti(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) +-{ +- do_vbiti(vece, t, a, imm, tcg_gen_or_vec); +-} +- +-static void gen_vbitrevi(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) +-{ +- do_vbiti(vece, t, a, imm, tcg_gen_xor_vec); +-} +- +-static void do_vbitclri(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- int64_t imm, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_shli_vec, INDEX_op_andc_vec, 0 +- }; +- static const GVecGen2i op[4] = { +- { +- .fniv = gen_vbitclri, +- .fnoi = gen_helper_vbitclri_b, +- .opt_opc = vecop_list, +- .vece = MO_8 +- }, +- { +- .fniv = gen_vbitclri, +- .fnoi = gen_helper_vbitclri_h, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fniv = gen_vbitclri, +- .fnoi = gen_helper_vbitclri_w, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fniv = gen_vbitclri, +- .fnoi = gen_helper_vbitclri_d, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op[vece]); +-} +- +-TRANS(vbitclri_b, LSX, gvec_vv_i, MO_8, do_vbitclri) +-TRANS(vbitclri_h, LSX, gvec_vv_i, MO_16, do_vbitclri) +-TRANS(vbitclri_w, LSX, gvec_vv_i, MO_32, do_vbitclri) +-TRANS(vbitclri_d, LSX, gvec_vv_i, MO_64, do_vbitclri) +-TRANS(xvbitclri_b, LASX, gvec_xx_i, MO_8, do_vbitclri) +-TRANS(xvbitclri_h, LASX, gvec_xx_i, MO_16, do_vbitclri) +-TRANS(xvbitclri_w, LASX, gvec_xx_i, MO_32, do_vbitclri) +-TRANS(xvbitclri_d, LASX, gvec_xx_i, MO_64, do_vbitclri) +- +-static void do_vbitset(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_shlv_vec, 0 +- }; +- static const GVecGen3 op[4] = { +- { +- .fniv = gen_vbitset, +- .fno = gen_helper_vbitset_b, +- .opt_opc = vecop_list, +- .vece = MO_8 +- }, +- { +- .fniv = gen_vbitset, +- .fno = gen_helper_vbitset_h, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fniv = gen_vbitset, +- .fno = gen_helper_vbitset_w, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fniv = gen_vbitset, +- .fno = gen_helper_vbitset_d, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vbitset_b, LSX, gvec_vvv, MO_8, do_vbitset) +-TRANS(vbitset_h, LSX, gvec_vvv, MO_16, do_vbitset) +-TRANS(vbitset_w, LSX, gvec_vvv, MO_32, do_vbitset) +-TRANS(vbitset_d, LSX, gvec_vvv, MO_64, do_vbitset) +-TRANS(xvbitset_b, LASX, gvec_xxx, MO_8, do_vbitset) +-TRANS(xvbitset_h, LASX, gvec_xxx, MO_16, do_vbitset) +-TRANS(xvbitset_w, LASX, gvec_xxx, MO_32, do_vbitset) +-TRANS(xvbitset_d, LASX, gvec_xxx, MO_64, do_vbitset) +- +-static void do_vbitseti(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- int64_t imm, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_shli_vec, 0 +- }; +- static const GVecGen2i op[4] = { +- { +- .fniv = gen_vbitseti, +- .fnoi = gen_helper_vbitseti_b, +- .opt_opc = vecop_list, +- .vece = MO_8 +- }, +- { +- .fniv = gen_vbitseti, +- .fnoi = gen_helper_vbitseti_h, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fniv = gen_vbitseti, +- .fnoi = gen_helper_vbitseti_w, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fniv = gen_vbitseti, +- .fnoi = gen_helper_vbitseti_d, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op[vece]); +-} +- +-TRANS(vbitseti_b, LSX, gvec_vv_i, MO_8, do_vbitseti) +-TRANS(vbitseti_h, LSX, gvec_vv_i, MO_16, do_vbitseti) +-TRANS(vbitseti_w, LSX, gvec_vv_i, MO_32, do_vbitseti) +-TRANS(vbitseti_d, LSX, gvec_vv_i, MO_64, do_vbitseti) +-TRANS(xvbitseti_b, LASX, gvec_xx_i, MO_8, do_vbitseti) +-TRANS(xvbitseti_h, LASX, gvec_xx_i, MO_16, do_vbitseti) +-TRANS(xvbitseti_w, LASX, gvec_xx_i, MO_32, do_vbitseti) +-TRANS(xvbitseti_d, LASX, gvec_xx_i, MO_64, do_vbitseti) +- +-static void do_vbitrev(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_shlv_vec, 0 +- }; +- static const GVecGen3 op[4] = { +- { +- .fniv = gen_vbitrev, +- .fno = gen_helper_vbitrev_b, +- .opt_opc = vecop_list, +- .vece = MO_8 +- }, +- { +- .fniv = gen_vbitrev, +- .fno = gen_helper_vbitrev_h, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fniv = gen_vbitrev, +- .fno = gen_helper_vbitrev_w, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fniv = gen_vbitrev, +- .fno = gen_helper_vbitrev_d, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); +-} +- +-TRANS(vbitrev_b, LSX, gvec_vvv, MO_8, do_vbitrev) +-TRANS(vbitrev_h, LSX, gvec_vvv, MO_16, do_vbitrev) +-TRANS(vbitrev_w, LSX, gvec_vvv, MO_32, do_vbitrev) +-TRANS(vbitrev_d, LSX, gvec_vvv, MO_64, do_vbitrev) +-TRANS(xvbitrev_b, LASX, gvec_xxx, MO_8, do_vbitrev) +-TRANS(xvbitrev_h, LASX, gvec_xxx, MO_16, do_vbitrev) +-TRANS(xvbitrev_w, LASX, gvec_xxx, MO_32, do_vbitrev) +-TRANS(xvbitrev_d, LASX, gvec_xxx, MO_64, do_vbitrev) +- +-static void do_vbitrevi(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, +- int64_t imm, uint32_t oprsz, uint32_t maxsz) +-{ +- static const TCGOpcode vecop_list[] = { +- INDEX_op_shli_vec, 0 +- }; +- static const GVecGen2i op[4] = { +- { +- .fniv = gen_vbitrevi, +- .fnoi = gen_helper_vbitrevi_b, +- .opt_opc = vecop_list, +- .vece = MO_8 +- }, +- { +- .fniv = gen_vbitrevi, +- .fnoi = gen_helper_vbitrevi_h, +- .opt_opc = vecop_list, +- .vece = MO_16 +- }, +- { +- .fniv = gen_vbitrevi, +- .fnoi = gen_helper_vbitrevi_w, +- .opt_opc = vecop_list, +- .vece = MO_32 +- }, +- { +- .fniv = gen_vbitrevi, +- .fnoi = gen_helper_vbitrevi_d, +- .opt_opc = vecop_list, +- .vece = MO_64 +- }, +- }; +- +- tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op[vece]); +-} +- +-TRANS(vbitrevi_b, LSX, gvec_vv_i, MO_8, do_vbitrevi) +-TRANS(vbitrevi_h, LSX, gvec_vv_i, MO_16, do_vbitrevi) +-TRANS(vbitrevi_w, LSX, gvec_vv_i, MO_32, do_vbitrevi) +-TRANS(vbitrevi_d, LSX, gvec_vv_i, MO_64, do_vbitrevi) +-TRANS(xvbitrevi_b, LASX, gvec_xx_i, MO_8, do_vbitrevi) +-TRANS(xvbitrevi_h, LASX, gvec_xx_i, MO_16, do_vbitrevi) +-TRANS(xvbitrevi_w, LASX, gvec_xx_i, MO_32, do_vbitrevi) +-TRANS(xvbitrevi_d, LASX, gvec_xx_i, MO_64, do_vbitrevi) +- +-TRANS(vfrstp_b, LSX, gen_vvv, gen_helper_vfrstp_b) +-TRANS(vfrstp_h, LSX, gen_vvv, gen_helper_vfrstp_h) +-TRANS(vfrstpi_b, LSX, gen_vv_i, gen_helper_vfrstpi_b) +-TRANS(vfrstpi_h, LSX, gen_vv_i, gen_helper_vfrstpi_h) +-TRANS(xvfrstp_b, LASX, gen_xxx, gen_helper_vfrstp_b) +-TRANS(xvfrstp_h, LASX, gen_xxx, gen_helper_vfrstp_h) +-TRANS(xvfrstpi_b, LASX, gen_xx_i, gen_helper_vfrstpi_b) +-TRANS(xvfrstpi_h, LASX, gen_xx_i, gen_helper_vfrstpi_h) +- +-TRANS(vfadd_s, LSX, gen_vvv_ptr, gen_helper_vfadd_s) +-TRANS(vfadd_d, LSX, gen_vvv_ptr, gen_helper_vfadd_d) +-TRANS(vfsub_s, LSX, gen_vvv_ptr, gen_helper_vfsub_s) +-TRANS(vfsub_d, LSX, gen_vvv_ptr, gen_helper_vfsub_d) +-TRANS(vfmul_s, LSX, gen_vvv_ptr, gen_helper_vfmul_s) +-TRANS(vfmul_d, LSX, gen_vvv_ptr, gen_helper_vfmul_d) +-TRANS(vfdiv_s, LSX, gen_vvv_ptr, gen_helper_vfdiv_s) +-TRANS(vfdiv_d, LSX, gen_vvv_ptr, gen_helper_vfdiv_d) +-TRANS(xvfadd_s, LASX, gen_xxx_ptr, gen_helper_vfadd_s) +-TRANS(xvfadd_d, LASX, gen_xxx_ptr, gen_helper_vfadd_d) +-TRANS(xvfsub_s, LASX, gen_xxx_ptr, gen_helper_vfsub_s) +-TRANS(xvfsub_d, LASX, gen_xxx_ptr, gen_helper_vfsub_d) +-TRANS(xvfmul_s, LASX, gen_xxx_ptr, gen_helper_vfmul_s) +-TRANS(xvfmul_d, LASX, gen_xxx_ptr, gen_helper_vfmul_d) +-TRANS(xvfdiv_s, LASX, gen_xxx_ptr, gen_helper_vfdiv_s) +-TRANS(xvfdiv_d, LASX, gen_xxx_ptr, gen_helper_vfdiv_d) +- +-TRANS(vfmadd_s, LSX, gen_vvvv_ptr, gen_helper_vfmadd_s) +-TRANS(vfmadd_d, LSX, gen_vvvv_ptr, gen_helper_vfmadd_d) +-TRANS(vfmsub_s, LSX, gen_vvvv_ptr, gen_helper_vfmsub_s) +-TRANS(vfmsub_d, LSX, gen_vvvv_ptr, gen_helper_vfmsub_d) +-TRANS(vfnmadd_s, LSX, gen_vvvv_ptr, gen_helper_vfnmadd_s) +-TRANS(vfnmadd_d, LSX, gen_vvvv_ptr, gen_helper_vfnmadd_d) +-TRANS(vfnmsub_s, LSX, gen_vvvv_ptr, gen_helper_vfnmsub_s) +-TRANS(vfnmsub_d, LSX, gen_vvvv_ptr, gen_helper_vfnmsub_d) +-TRANS(xvfmadd_s, LASX, gen_xxxx_ptr, gen_helper_vfmadd_s) +-TRANS(xvfmadd_d, LASX, gen_xxxx_ptr, gen_helper_vfmadd_d) +-TRANS(xvfmsub_s, LASX, gen_xxxx_ptr, gen_helper_vfmsub_s) +-TRANS(xvfmsub_d, LASX, gen_xxxx_ptr, gen_helper_vfmsub_d) +-TRANS(xvfnmadd_s, LASX, gen_xxxx_ptr, gen_helper_vfnmadd_s) +-TRANS(xvfnmadd_d, LASX, gen_xxxx_ptr, gen_helper_vfnmadd_d) +-TRANS(xvfnmsub_s, LASX, gen_xxxx_ptr, gen_helper_vfnmsub_s) +-TRANS(xvfnmsub_d, LASX, gen_xxxx_ptr, gen_helper_vfnmsub_d) +- +-TRANS(vfmax_s, LSX, gen_vvv_ptr, gen_helper_vfmax_s) +-TRANS(vfmax_d, LSX, gen_vvv_ptr, gen_helper_vfmax_d) +-TRANS(vfmin_s, LSX, gen_vvv_ptr, gen_helper_vfmin_s) +-TRANS(vfmin_d, LSX, gen_vvv_ptr, gen_helper_vfmin_d) +-TRANS(xvfmax_s, LASX, gen_xxx_ptr, gen_helper_vfmax_s) +-TRANS(xvfmax_d, LASX, gen_xxx_ptr, gen_helper_vfmax_d) +-TRANS(xvfmin_s, LASX, gen_xxx_ptr, gen_helper_vfmin_s) +-TRANS(xvfmin_d, LASX, gen_xxx_ptr, gen_helper_vfmin_d) +- +-TRANS(vfmaxa_s, LSX, gen_vvv_ptr, gen_helper_vfmaxa_s) +-TRANS(vfmaxa_d, LSX, gen_vvv_ptr, gen_helper_vfmaxa_d) +-TRANS(vfmina_s, LSX, gen_vvv_ptr, gen_helper_vfmina_s) +-TRANS(vfmina_d, LSX, gen_vvv_ptr, gen_helper_vfmina_d) +-TRANS(xvfmaxa_s, LASX, gen_xxx_ptr, gen_helper_vfmaxa_s) +-TRANS(xvfmaxa_d, LASX, gen_xxx_ptr, gen_helper_vfmaxa_d) +-TRANS(xvfmina_s, LASX, gen_xxx_ptr, gen_helper_vfmina_s) +-TRANS(xvfmina_d, LASX, gen_xxx_ptr, gen_helper_vfmina_d) +- +-TRANS(vflogb_s, LSX, gen_vv_ptr, gen_helper_vflogb_s) +-TRANS(vflogb_d, LSX, gen_vv_ptr, gen_helper_vflogb_d) +-TRANS(xvflogb_s, LASX, gen_xx_ptr, gen_helper_vflogb_s) +-TRANS(xvflogb_d, LASX, gen_xx_ptr, gen_helper_vflogb_d) +- +-TRANS(vfclass_s, LSX, gen_vv_ptr, gen_helper_vfclass_s) +-TRANS(vfclass_d, LSX, gen_vv_ptr, gen_helper_vfclass_d) +-TRANS(xvfclass_s, LASX, gen_xx_ptr, gen_helper_vfclass_s) +-TRANS(xvfclass_d, LASX, gen_xx_ptr, gen_helper_vfclass_d) +- +-TRANS(vfsqrt_s, LSX, gen_vv_ptr, gen_helper_vfsqrt_s) +-TRANS(vfsqrt_d, LSX, gen_vv_ptr, gen_helper_vfsqrt_d) +-TRANS(vfrecip_s, LSX, gen_vv_ptr, gen_helper_vfrecip_s) +-TRANS(vfrecip_d, LSX, gen_vv_ptr, gen_helper_vfrecip_d) +-TRANS(vfrsqrt_s, LSX, gen_vv_ptr, gen_helper_vfrsqrt_s) +-TRANS(vfrsqrt_d, LSX, gen_vv_ptr, gen_helper_vfrsqrt_d) +-TRANS(xvfsqrt_s, LASX, gen_xx_ptr, gen_helper_vfsqrt_s) +-TRANS(xvfsqrt_d, LASX, gen_xx_ptr, gen_helper_vfsqrt_d) +-TRANS(xvfrecip_s, LASX, gen_xx_ptr, gen_helper_vfrecip_s) +-TRANS(xvfrecip_d, LASX, gen_xx_ptr, gen_helper_vfrecip_d) +-TRANS(xvfrsqrt_s, LASX, gen_xx_ptr, gen_helper_vfrsqrt_s) +-TRANS(xvfrsqrt_d, LASX, gen_xx_ptr, gen_helper_vfrsqrt_d) +- +-TRANS(vfcvtl_s_h, LSX, gen_vv_ptr, gen_helper_vfcvtl_s_h) +-TRANS(vfcvth_s_h, LSX, gen_vv_ptr, gen_helper_vfcvth_s_h) +-TRANS(vfcvtl_d_s, LSX, gen_vv_ptr, gen_helper_vfcvtl_d_s) +-TRANS(vfcvth_d_s, LSX, gen_vv_ptr, gen_helper_vfcvth_d_s) +-TRANS(vfcvt_h_s, LSX, gen_vvv_ptr, gen_helper_vfcvt_h_s) +-TRANS(vfcvt_s_d, LSX, gen_vvv_ptr, gen_helper_vfcvt_s_d) +-TRANS(xvfcvtl_s_h, LASX, gen_xx_ptr, gen_helper_vfcvtl_s_h) +-TRANS(xvfcvth_s_h, LASX, gen_xx_ptr, gen_helper_vfcvth_s_h) +-TRANS(xvfcvtl_d_s, LASX, gen_xx_ptr, gen_helper_vfcvtl_d_s) +-TRANS(xvfcvth_d_s, LASX, gen_xx_ptr, gen_helper_vfcvth_d_s) +-TRANS(xvfcvt_h_s, LASX, gen_xxx_ptr, gen_helper_vfcvt_h_s) +-TRANS(xvfcvt_s_d, LASX, gen_xxx_ptr, gen_helper_vfcvt_s_d) +- +-TRANS(vfrintrne_s, LSX, gen_vv_ptr, gen_helper_vfrintrne_s) +-TRANS(vfrintrne_d, LSX, gen_vv_ptr, gen_helper_vfrintrne_d) +-TRANS(vfrintrz_s, LSX, gen_vv_ptr, gen_helper_vfrintrz_s) +-TRANS(vfrintrz_d, LSX, gen_vv_ptr, gen_helper_vfrintrz_d) +-TRANS(vfrintrp_s, LSX, gen_vv_ptr, gen_helper_vfrintrp_s) +-TRANS(vfrintrp_d, LSX, gen_vv_ptr, gen_helper_vfrintrp_d) +-TRANS(vfrintrm_s, LSX, gen_vv_ptr, gen_helper_vfrintrm_s) +-TRANS(vfrintrm_d, LSX, gen_vv_ptr, gen_helper_vfrintrm_d) +-TRANS(vfrint_s, LSX, gen_vv_ptr, gen_helper_vfrint_s) +-TRANS(vfrint_d, LSX, gen_vv_ptr, gen_helper_vfrint_d) +-TRANS(xvfrintrne_s, LASX, gen_xx_ptr, gen_helper_vfrintrne_s) +-TRANS(xvfrintrne_d, LASX, gen_xx_ptr, gen_helper_vfrintrne_d) +-TRANS(xvfrintrz_s, LASX, gen_xx_ptr, gen_helper_vfrintrz_s) +-TRANS(xvfrintrz_d, LASX, gen_xx_ptr, gen_helper_vfrintrz_d) +-TRANS(xvfrintrp_s, LASX, gen_xx_ptr, gen_helper_vfrintrp_s) +-TRANS(xvfrintrp_d, LASX, gen_xx_ptr, gen_helper_vfrintrp_d) +-TRANS(xvfrintrm_s, LASX, gen_xx_ptr, gen_helper_vfrintrm_s) +-TRANS(xvfrintrm_d, LASX, gen_xx_ptr, gen_helper_vfrintrm_d) +-TRANS(xvfrint_s, LASX, gen_xx_ptr, gen_helper_vfrint_s) +-TRANS(xvfrint_d, LASX, gen_xx_ptr, gen_helper_vfrint_d) +- +-TRANS(vftintrne_w_s, LSX, gen_vv_ptr, gen_helper_vftintrne_w_s) +-TRANS(vftintrne_l_d, LSX, gen_vv_ptr, gen_helper_vftintrne_l_d) +-TRANS(vftintrz_w_s, LSX, gen_vv_ptr, gen_helper_vftintrz_w_s) +-TRANS(vftintrz_l_d, LSX, gen_vv_ptr, gen_helper_vftintrz_l_d) +-TRANS(vftintrp_w_s, LSX, gen_vv_ptr, gen_helper_vftintrp_w_s) +-TRANS(vftintrp_l_d, LSX, gen_vv_ptr, gen_helper_vftintrp_l_d) +-TRANS(vftintrm_w_s, LSX, gen_vv_ptr, gen_helper_vftintrm_w_s) +-TRANS(vftintrm_l_d, LSX, gen_vv_ptr, gen_helper_vftintrm_l_d) +-TRANS(vftint_w_s, LSX, gen_vv_ptr, gen_helper_vftint_w_s) +-TRANS(vftint_l_d, LSX, gen_vv_ptr, gen_helper_vftint_l_d) +-TRANS(vftintrz_wu_s, LSX, gen_vv_ptr, gen_helper_vftintrz_wu_s) +-TRANS(vftintrz_lu_d, LSX, gen_vv_ptr, gen_helper_vftintrz_lu_d) +-TRANS(vftint_wu_s, LSX, gen_vv_ptr, gen_helper_vftint_wu_s) +-TRANS(vftint_lu_d, LSX, gen_vv_ptr, gen_helper_vftint_lu_d) +-TRANS(vftintrne_w_d, LSX, gen_vvv_ptr, gen_helper_vftintrne_w_d) +-TRANS(vftintrz_w_d, LSX, gen_vvv_ptr, gen_helper_vftintrz_w_d) +-TRANS(vftintrp_w_d, LSX, gen_vvv_ptr, gen_helper_vftintrp_w_d) +-TRANS(vftintrm_w_d, LSX, gen_vvv_ptr, gen_helper_vftintrm_w_d) +-TRANS(vftint_w_d, LSX, gen_vvv_ptr, gen_helper_vftint_w_d) +-TRANS(vftintrnel_l_s, LSX, gen_vv_ptr, gen_helper_vftintrnel_l_s) +-TRANS(vftintrneh_l_s, LSX, gen_vv_ptr, gen_helper_vftintrneh_l_s) +-TRANS(vftintrzl_l_s, LSX, gen_vv_ptr, gen_helper_vftintrzl_l_s) +-TRANS(vftintrzh_l_s, LSX, gen_vv_ptr, gen_helper_vftintrzh_l_s) +-TRANS(vftintrpl_l_s, LSX, gen_vv_ptr, gen_helper_vftintrpl_l_s) +-TRANS(vftintrph_l_s, LSX, gen_vv_ptr, gen_helper_vftintrph_l_s) +-TRANS(vftintrml_l_s, LSX, gen_vv_ptr, gen_helper_vftintrml_l_s) +-TRANS(vftintrmh_l_s, LSX, gen_vv_ptr, gen_helper_vftintrmh_l_s) +-TRANS(vftintl_l_s, LSX, gen_vv_ptr, gen_helper_vftintl_l_s) +-TRANS(vftinth_l_s, LSX, gen_vv_ptr, gen_helper_vftinth_l_s) +-TRANS(xvftintrne_w_s, LASX, gen_xx_ptr, gen_helper_vftintrne_w_s) +-TRANS(xvftintrne_l_d, LASX, gen_xx_ptr, gen_helper_vftintrne_l_d) +-TRANS(xvftintrz_w_s, LASX, gen_xx_ptr, gen_helper_vftintrz_w_s) +-TRANS(xvftintrz_l_d, LASX, gen_xx_ptr, gen_helper_vftintrz_l_d) +-TRANS(xvftintrp_w_s, LASX, gen_xx_ptr, gen_helper_vftintrp_w_s) +-TRANS(xvftintrp_l_d, LASX, gen_xx_ptr, gen_helper_vftintrp_l_d) +-TRANS(xvftintrm_w_s, LASX, gen_xx_ptr, gen_helper_vftintrm_w_s) +-TRANS(xvftintrm_l_d, LASX, gen_xx_ptr, gen_helper_vftintrm_l_d) +-TRANS(xvftint_w_s, LASX, gen_xx_ptr, gen_helper_vftint_w_s) +-TRANS(xvftint_l_d, LASX, gen_xx_ptr, gen_helper_vftint_l_d) +-TRANS(xvftintrz_wu_s, LASX, gen_xx_ptr, gen_helper_vftintrz_wu_s) +-TRANS(xvftintrz_lu_d, LASX, gen_xx_ptr, gen_helper_vftintrz_lu_d) +-TRANS(xvftint_wu_s, LASX, gen_xx_ptr, gen_helper_vftint_wu_s) +-TRANS(xvftint_lu_d, LASX, gen_xx_ptr, gen_helper_vftint_lu_d) +-TRANS(xvftintrne_w_d, LASX, gen_xxx_ptr, gen_helper_vftintrne_w_d) +-TRANS(xvftintrz_w_d, LASX, gen_xxx_ptr, gen_helper_vftintrz_w_d) +-TRANS(xvftintrp_w_d, LASX, gen_xxx_ptr, gen_helper_vftintrp_w_d) +-TRANS(xvftintrm_w_d, LASX, gen_xxx_ptr, gen_helper_vftintrm_w_d) +-TRANS(xvftint_w_d, LASX, gen_xxx_ptr, gen_helper_vftint_w_d) +-TRANS(xvftintrnel_l_s, LASX, gen_xx_ptr, gen_helper_vftintrnel_l_s) +-TRANS(xvftintrneh_l_s, LASX, gen_xx_ptr, gen_helper_vftintrneh_l_s) +-TRANS(xvftintrzl_l_s, LASX, gen_xx_ptr, gen_helper_vftintrzl_l_s) +-TRANS(xvftintrzh_l_s, LASX, gen_xx_ptr, gen_helper_vftintrzh_l_s) +-TRANS(xvftintrpl_l_s, LASX, gen_xx_ptr, gen_helper_vftintrpl_l_s) +-TRANS(xvftintrph_l_s, LASX, gen_xx_ptr, gen_helper_vftintrph_l_s) +-TRANS(xvftintrml_l_s, LASX, gen_xx_ptr, gen_helper_vftintrml_l_s) +-TRANS(xvftintrmh_l_s, LASX, gen_xx_ptr, gen_helper_vftintrmh_l_s) +-TRANS(xvftintl_l_s, LASX, gen_xx_ptr, gen_helper_vftintl_l_s) +-TRANS(xvftinth_l_s, LASX, gen_xx_ptr, gen_helper_vftinth_l_s) +- +-TRANS(vffint_s_w, LSX, gen_vv_ptr, gen_helper_vffint_s_w) +-TRANS(vffint_d_l, LSX, gen_vv_ptr, gen_helper_vffint_d_l) +-TRANS(vffint_s_wu, LSX, gen_vv_ptr, gen_helper_vffint_s_wu) +-TRANS(vffint_d_lu, LSX, gen_vv_ptr, gen_helper_vffint_d_lu) +-TRANS(vffintl_d_w, LSX, gen_vv_ptr, gen_helper_vffintl_d_w) +-TRANS(vffinth_d_w, LSX, gen_vv_ptr, gen_helper_vffinth_d_w) +-TRANS(vffint_s_l, LSX, gen_vvv_ptr, gen_helper_vffint_s_l) +-TRANS(xvffint_s_w, LASX, gen_xx_ptr, gen_helper_vffint_s_w) +-TRANS(xvffint_d_l, LASX, gen_xx_ptr, gen_helper_vffint_d_l) +-TRANS(xvffint_s_wu, LASX, gen_xx_ptr, gen_helper_vffint_s_wu) +-TRANS(xvffint_d_lu, LASX, gen_xx_ptr, gen_helper_vffint_d_lu) +-TRANS(xvffintl_d_w, LASX, gen_xx_ptr, gen_helper_vffintl_d_w) +-TRANS(xvffinth_d_w, LASX, gen_xx_ptr, gen_helper_vffinth_d_w) +-TRANS(xvffint_s_l, LASX, gen_xxx_ptr, gen_helper_vffint_s_l) +- +-static bool do_cmp_vl(DisasContext *ctx, arg_vvv *a, +- uint32_t oprsz, MemOp mop, TCGCond cond) +-{ +- uint32_t vd_ofs, vj_ofs, vk_ofs; +- +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- vd_ofs = vec_full_offset(a->vd); +- vj_ofs = vec_full_offset(a->vj); +- vk_ofs = vec_full_offset(a->vk); +- +- tcg_gen_gvec_cmp(cond, mop, vd_ofs, vj_ofs, vk_ofs, oprsz, ctx->vl / 8); +- return true; +-} +- +-static bool do_cmp(DisasContext *ctx, arg_vvv *a, +- MemOp mop, TCGCond cond) +-{ +- return do_cmp_vl(ctx, a, 16, mop, cond); +-} +- +-static bool do_xcmp(DisasContext *ctx, arg_vvv *a, +- MemOp mop, TCGCond cond) +-{ +- return do_cmp_vl(ctx, a, 32, mop, cond); +-} +- +-static bool do_cmpi_vl(DisasContext *ctx, arg_vv_i *a, +- uint32_t oprsz, MemOp mop, TCGCond cond) +-{ +- uint32_t vd_ofs, vj_ofs; +- +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- vd_ofs = vec_full_offset(a->vd); +- vj_ofs = vec_full_offset(a->vj); +- +- tcg_gen_gvec_cmpi(cond, mop, vd_ofs, vj_ofs, a->imm, oprsz, ctx->vl / 8); +- return true; +-} +- +-static bool do_cmpi(DisasContext *ctx, arg_vv_i *a, +- MemOp mop, TCGCond cond) +-{ +- return do_cmpi_vl(ctx, a, 16, mop, cond); +-} +- +-static bool do_xcmpi(DisasContext *ctx, arg_vv_i *a, +- MemOp mop, TCGCond cond) +-{ +- return do_cmpi_vl(ctx, a, 32, mop, cond); +-} +- +-TRANS(vseq_b, LSX, do_cmp, MO_8, TCG_COND_EQ) +-TRANS(vseq_h, LSX, do_cmp, MO_16, TCG_COND_EQ) +-TRANS(vseq_w, LSX, do_cmp, MO_32, TCG_COND_EQ) +-TRANS(vseq_d, LSX, do_cmp, MO_64, TCG_COND_EQ) +-TRANS(vseqi_b, LSX, do_cmpi, MO_8, TCG_COND_EQ) +-TRANS(vseqi_h, LSX, do_cmpi, MO_16, TCG_COND_EQ) +-TRANS(vseqi_w, LSX, do_cmpi, MO_32, TCG_COND_EQ) +-TRANS(vseqi_d, LSX, do_cmpi, MO_64, TCG_COND_EQ) +-TRANS(xvseq_b, LASX, do_xcmp, MO_8, TCG_COND_EQ) +-TRANS(xvseq_h, LASX, do_xcmp, MO_16, TCG_COND_EQ) +-TRANS(xvseq_w, LASX, do_xcmp, MO_32, TCG_COND_EQ) +-TRANS(xvseq_d, LASX, do_xcmp, MO_64, TCG_COND_EQ) +-TRANS(xvseqi_b, LASX, do_xcmpi, MO_8, TCG_COND_EQ) +-TRANS(xvseqi_h, LASX, do_xcmpi, MO_16, TCG_COND_EQ) +-TRANS(xvseqi_w, LASX, do_xcmpi, MO_32, TCG_COND_EQ) +-TRANS(xvseqi_d, LASX, do_xcmpi, MO_64, TCG_COND_EQ) +- +-TRANS(vsle_b, LSX, do_cmp, MO_8, TCG_COND_LE) +-TRANS(vsle_h, LSX, do_cmp, MO_16, TCG_COND_LE) +-TRANS(vsle_w, LSX, do_cmp, MO_32, TCG_COND_LE) +-TRANS(vsle_d, LSX, do_cmp, MO_64, TCG_COND_LE) +-TRANS(vslei_b, LSX, do_cmpi, MO_8, TCG_COND_LE) +-TRANS(vslei_h, LSX, do_cmpi, MO_16, TCG_COND_LE) +-TRANS(vslei_w, LSX, do_cmpi, MO_32, TCG_COND_LE) +-TRANS(vslei_d, LSX, do_cmpi, MO_64, TCG_COND_LE) +-TRANS(vsle_bu, LSX, do_cmp, MO_8, TCG_COND_LEU) +-TRANS(vsle_hu, LSX, do_cmp, MO_16, TCG_COND_LEU) +-TRANS(vsle_wu, LSX, do_cmp, MO_32, TCG_COND_LEU) +-TRANS(vsle_du, LSX, do_cmp, MO_64, TCG_COND_LEU) +-TRANS(vslei_bu, LSX, do_cmpi, MO_8, TCG_COND_LEU) +-TRANS(vslei_hu, LSX, do_cmpi, MO_16, TCG_COND_LEU) +-TRANS(vslei_wu, LSX, do_cmpi, MO_32, TCG_COND_LEU) +-TRANS(vslei_du, LSX, do_cmpi, MO_64, TCG_COND_LEU) +-TRANS(xvsle_b, LASX, do_xcmp, MO_8, TCG_COND_LE) +-TRANS(xvsle_h, LASX, do_xcmp, MO_16, TCG_COND_LE) +-TRANS(xvsle_w, LASX, do_xcmp, MO_32, TCG_COND_LE) +-TRANS(xvsle_d, LASX, do_xcmp, MO_64, TCG_COND_LE) +-TRANS(xvslei_b, LASX, do_xcmpi, MO_8, TCG_COND_LE) +-TRANS(xvslei_h, LASX, do_xcmpi, MO_16, TCG_COND_LE) +-TRANS(xvslei_w, LASX, do_xcmpi, MO_32, TCG_COND_LE) +-TRANS(xvslei_d, LASX, do_xcmpi, MO_64, TCG_COND_LE) +-TRANS(xvsle_bu, LASX, do_xcmp, MO_8, TCG_COND_LEU) +-TRANS(xvsle_hu, LASX, do_xcmp, MO_16, TCG_COND_LEU) +-TRANS(xvsle_wu, LASX, do_xcmp, MO_32, TCG_COND_LEU) +-TRANS(xvsle_du, LASX, do_xcmp, MO_64, TCG_COND_LEU) +-TRANS(xvslei_bu, LASX, do_xcmpi, MO_8, TCG_COND_LEU) +-TRANS(xvslei_hu, LASX, do_xcmpi, MO_16, TCG_COND_LEU) +-TRANS(xvslei_wu, LASX, do_xcmpi, MO_32, TCG_COND_LEU) +-TRANS(xvslei_du, LASX, do_xcmpi, MO_64, TCG_COND_LEU) +- +-TRANS(vslt_b, LSX, do_cmp, MO_8, TCG_COND_LT) +-TRANS(vslt_h, LSX, do_cmp, MO_16, TCG_COND_LT) +-TRANS(vslt_w, LSX, do_cmp, MO_32, TCG_COND_LT) +-TRANS(vslt_d, LSX, do_cmp, MO_64, TCG_COND_LT) +-TRANS(vslti_b, LSX, do_cmpi, MO_8, TCG_COND_LT) +-TRANS(vslti_h, LSX, do_cmpi, MO_16, TCG_COND_LT) +-TRANS(vslti_w, LSX, do_cmpi, MO_32, TCG_COND_LT) +-TRANS(vslti_d, LSX, do_cmpi, MO_64, TCG_COND_LT) +-TRANS(vslt_bu, LSX, do_cmp, MO_8, TCG_COND_LTU) +-TRANS(vslt_hu, LSX, do_cmp, MO_16, TCG_COND_LTU) +-TRANS(vslt_wu, LSX, do_cmp, MO_32, TCG_COND_LTU) +-TRANS(vslt_du, LSX, do_cmp, MO_64, TCG_COND_LTU) +-TRANS(vslti_bu, LSX, do_cmpi, MO_8, TCG_COND_LTU) +-TRANS(vslti_hu, LSX, do_cmpi, MO_16, TCG_COND_LTU) +-TRANS(vslti_wu, LSX, do_cmpi, MO_32, TCG_COND_LTU) +-TRANS(vslti_du, LSX, do_cmpi, MO_64, TCG_COND_LTU) +-TRANS(xvslt_b, LASX, do_xcmp, MO_8, TCG_COND_LT) +-TRANS(xvslt_h, LASX, do_xcmp, MO_16, TCG_COND_LT) +-TRANS(xvslt_w, LASX, do_xcmp, MO_32, TCG_COND_LT) +-TRANS(xvslt_d, LASX, do_xcmp, MO_64, TCG_COND_LT) +-TRANS(xvslti_b, LASX, do_xcmpi, MO_8, TCG_COND_LT) +-TRANS(xvslti_h, LASX, do_xcmpi, MO_16, TCG_COND_LT) +-TRANS(xvslti_w, LASX, do_xcmpi, MO_32, TCG_COND_LT) +-TRANS(xvslti_d, LASX, do_xcmpi, MO_64, TCG_COND_LT) +-TRANS(xvslt_bu, LASX, do_xcmp, MO_8, TCG_COND_LTU) +-TRANS(xvslt_hu, LASX, do_xcmp, MO_16, TCG_COND_LTU) +-TRANS(xvslt_wu, LASX, do_xcmp, MO_32, TCG_COND_LTU) +-TRANS(xvslt_du, LASX, do_xcmp, MO_64, TCG_COND_LTU) +-TRANS(xvslti_bu, LASX, do_xcmpi, MO_8, TCG_COND_LTU) +-TRANS(xvslti_hu, LASX, do_xcmpi, MO_16, TCG_COND_LTU) +-TRANS(xvslti_wu, LASX, do_xcmpi, MO_32, TCG_COND_LTU) +-TRANS(xvslti_du, LASX, do_xcmpi, MO_64, TCG_COND_LTU) +- +-static bool do_vfcmp_cond_s(DisasContext *ctx, arg_vvv_fcond *a, uint32_t sz) +-{ +- uint32_t flags; +- void (*fn)(TCGv_env, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32); +- TCGv_i32 vd = tcg_constant_i32(a->vd); +- TCGv_i32 vj = tcg_constant_i32(a->vj); +- TCGv_i32 vk = tcg_constant_i32(a->vk); +- TCGv_i32 oprsz = tcg_constant_i32(sz); +- +- if (!check_vec(ctx, sz)) { +- return true; +- } +- +- fn = (a->fcond & 1 ? gen_helper_vfcmp_s_s : gen_helper_vfcmp_c_s); +- flags = get_fcmp_flags(a->fcond >> 1); +- fn(tcg_env, oprsz, vd, vj, vk, tcg_constant_i32(flags)); +- +- return true; +-} +- +-static bool do_vfcmp_cond_d(DisasContext *ctx, arg_vvv_fcond *a, uint32_t sz) +-{ +- uint32_t flags; +- void (*fn)(TCGv_env, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32); +- TCGv_i32 vd = tcg_constant_i32(a->vd); +- TCGv_i32 vj = tcg_constant_i32(a->vj); +- TCGv_i32 vk = tcg_constant_i32(a->vk); +- TCGv_i32 oprsz = tcg_constant_i32(sz); +- +- if (!check_vec(ctx, sz)) { +- return true; +- } +- +- fn = (a->fcond & 1 ? gen_helper_vfcmp_s_d : gen_helper_vfcmp_c_d); +- flags = get_fcmp_flags(a->fcond >> 1); +- fn(tcg_env, oprsz, vd, vj, vk, tcg_constant_i32(flags)); +- +- return true; +-} +- +-TRANS(vfcmp_cond_s, LSX, do_vfcmp_cond_s, 16) +-TRANS(vfcmp_cond_d, LSX, do_vfcmp_cond_d, 16) +-TRANS(xvfcmp_cond_s, LASX, do_vfcmp_cond_s, 32) +-TRANS(xvfcmp_cond_d, LASX, do_vfcmp_cond_d, 32) +- +-static bool do_vbitsel_v(DisasContext *ctx, arg_vvvv *a, uint32_t oprsz) +-{ +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- tcg_gen_gvec_bitsel(MO_64, vec_full_offset(a->vd), vec_full_offset(a->va), +- vec_full_offset(a->vk), vec_full_offset(a->vj), +- oprsz, ctx->vl / 8); +- return true; +-} +- +-TRANS(vbitsel_v, LSX, do_vbitsel_v, 16) +-TRANS(xvbitsel_v, LASX, do_vbitsel_v, 32) +- +-static void gen_vbitseli(unsigned vece, TCGv_vec a, TCGv_vec b, int64_t imm) +-{ +- tcg_gen_bitsel_vec(vece, a, a, tcg_constant_vec_matching(a, vece, imm), b); +-} +- +-static bool do_vbitseli_b(DisasContext *ctx, arg_vv_i *a, uint32_t oprsz) +-{ +- static const GVecGen2i op = { +- .fniv = gen_vbitseli, +- .fnoi = gen_helper_vbitseli_b, +- .vece = MO_8, +- .load_dest = true +- }; +- +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- tcg_gen_gvec_2i(vec_full_offset(a->vd), vec_full_offset(a->vj), +- oprsz, ctx->vl / 8, a->imm , &op); +- return true; +-} +- +-TRANS(vbitseli_b, LSX, do_vbitseli_b, 16) +-TRANS(xvbitseli_b, LASX, do_vbitseli_b, 32) +- +-#define VSET(NAME, COND) \ +-static bool trans_## NAME (DisasContext *ctx, arg_cv *a) \ +-{ \ +- TCGv_i64 t1, al, ah; \ +- \ +- al = tcg_temp_new_i64(); \ +- ah = tcg_temp_new_i64(); \ +- t1 = tcg_temp_new_i64(); \ +- \ +- get_vreg64(ah, a->vj, 1); \ +- get_vreg64(al, a->vj, 0); \ +- \ +- if (!avail_LSX(ctx)) { \ +- return false; \ +- } \ +- \ +- if (!check_vec(ctx, 16)) { \ +- return true; \ +- } \ +- \ +- tcg_gen_or_i64(t1, al, ah); \ +- tcg_gen_setcondi_i64(COND, t1, t1, 0); \ +- tcg_gen_st8_tl(t1, tcg_env, offsetof(CPULoongArchState, cf[a->cd & 0x7])); \ +- \ +- return true; \ +-} +- +-VSET(vseteqz_v, TCG_COND_EQ) +-VSET(vsetnez_v, TCG_COND_NE) +- +-TRANS(vsetanyeqz_b, LSX, gen_cv, gen_helper_vsetanyeqz_b) +-TRANS(vsetanyeqz_h, LSX, gen_cv, gen_helper_vsetanyeqz_h) +-TRANS(vsetanyeqz_w, LSX, gen_cv, gen_helper_vsetanyeqz_w) +-TRANS(vsetanyeqz_d, LSX, gen_cv, gen_helper_vsetanyeqz_d) +-TRANS(vsetallnez_b, LSX, gen_cv, gen_helper_vsetallnez_b) +-TRANS(vsetallnez_h, LSX, gen_cv, gen_helper_vsetallnez_h) +-TRANS(vsetallnez_w, LSX, gen_cv, gen_helper_vsetallnez_w) +-TRANS(vsetallnez_d, LSX, gen_cv, gen_helper_vsetallnez_d) +- +-#define XVSET(NAME, COND) \ +-static bool trans_## NAME(DisasContext *ctx, arg_cv * a) \ +-{ \ +- TCGv_i64 t1, t2, d[4]; \ +- \ +- d[0] = tcg_temp_new_i64(); \ +- d[1] = tcg_temp_new_i64(); \ +- d[2] = tcg_temp_new_i64(); \ +- d[3] = tcg_temp_new_i64(); \ +- t1 = tcg_temp_new_i64(); \ +- t2 = tcg_temp_new_i64(); \ +- \ +- get_vreg64(d[0], a->vj, 0); \ +- get_vreg64(d[1], a->vj, 1); \ +- get_vreg64(d[2], a->vj, 2); \ +- get_vreg64(d[3], a->vj, 3); \ +- \ +- if (!avail_LASX(ctx)) { \ +- return false; \ +- } \ +- \ +- if (!check_vec(ctx, 32)) { \ +- return true; \ +- } \ +- \ +- tcg_gen_or_i64(t1, d[0], d[1]); \ +- tcg_gen_or_i64(t2, d[2], d[3]); \ +- tcg_gen_or_i64(t1, t2, t1); \ +- tcg_gen_setcondi_i64(COND, t1, t1, 0); \ +- tcg_gen_st8_tl(t1, tcg_env, offsetof(CPULoongArchState, cf[a->cd & 0x7])); \ +- \ +- return true; \ +-} +- +-XVSET(xvseteqz_v, TCG_COND_EQ) +-XVSET(xvsetnez_v, TCG_COND_NE) +- +-TRANS(xvsetanyeqz_b, LASX, gen_cx, gen_helper_vsetanyeqz_b) +-TRANS(xvsetanyeqz_h, LASX, gen_cx, gen_helper_vsetanyeqz_h) +-TRANS(xvsetanyeqz_w, LASX, gen_cx, gen_helper_vsetanyeqz_w) +-TRANS(xvsetanyeqz_d, LASX, gen_cx, gen_helper_vsetanyeqz_d) +-TRANS(xvsetallnez_b, LASX, gen_cx, gen_helper_vsetallnez_b) +-TRANS(xvsetallnez_h, LASX, gen_cx, gen_helper_vsetallnez_h) +-TRANS(xvsetallnez_w, LASX, gen_cx, gen_helper_vsetallnez_w) +-TRANS(xvsetallnez_d, LASX, gen_cx, gen_helper_vsetallnez_d) +- +-static bool gen_g2v_vl(DisasContext *ctx, arg_vr_i *a, uint32_t oprsz, MemOp mop, +- void (*func)(TCGv, TCGv_ptr, tcg_target_long)) +-{ +- TCGv src = gpr_src(ctx, a->rj, EXT_NONE); +- +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- func(src, tcg_env, vec_reg_offset(a->vd, a->imm, mop)); +- +- return true; +-} +- +-static bool gen_g2v(DisasContext *ctx, arg_vr_i *a, MemOp mop, +- void (*func)(TCGv, TCGv_ptr, tcg_target_long)) +-{ +- return gen_g2v_vl(ctx, a, 16, mop, func); +-} +- +-static bool gen_g2x(DisasContext *ctx, arg_vr_i *a, MemOp mop, +- void (*func)(TCGv, TCGv_ptr, tcg_target_long)) +-{ +- return gen_g2v_vl(ctx, a, 32, mop, func); +-} +- +-TRANS(vinsgr2vr_b, LSX, gen_g2v, MO_8, tcg_gen_st8_i64) +-TRANS(vinsgr2vr_h, LSX, gen_g2v, MO_16, tcg_gen_st16_i64) +-TRANS(vinsgr2vr_w, LSX, gen_g2v, MO_32, tcg_gen_st32_i64) +-TRANS(vinsgr2vr_d, LSX, gen_g2v, MO_64, tcg_gen_st_i64) +-TRANS(xvinsgr2vr_w, LASX, gen_g2x, MO_32, tcg_gen_st32_i64) +-TRANS(xvinsgr2vr_d, LASX, gen_g2x, MO_64, tcg_gen_st_i64) +- +-static bool gen_v2g_vl(DisasContext *ctx, arg_rv_i *a, uint32_t oprsz, MemOp mop, +- void (*func)(TCGv, TCGv_ptr, tcg_target_long)) +-{ +- TCGv dst = gpr_dst(ctx, a->rd, EXT_NONE); +- +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- func(dst, tcg_env, vec_reg_offset(a->vj, a->imm, mop)); +- +- return true; +-} +- +-static bool gen_v2g(DisasContext *ctx, arg_rv_i *a, MemOp mop, +- void (*func)(TCGv, TCGv_ptr, tcg_target_long)) +-{ +- return gen_v2g_vl(ctx, a, 16, mop, func); +-} +- +-static bool gen_x2g(DisasContext *ctx, arg_rv_i *a, MemOp mop, +- void (*func)(TCGv, TCGv_ptr, tcg_target_long)) +-{ +- return gen_v2g_vl(ctx, a, 32, mop, func); +-} +- +-TRANS(vpickve2gr_b, LSX, gen_v2g, MO_8, tcg_gen_ld8s_i64) +-TRANS(vpickve2gr_h, LSX, gen_v2g, MO_16, tcg_gen_ld16s_i64) +-TRANS(vpickve2gr_w, LSX, gen_v2g, MO_32, tcg_gen_ld32s_i64) +-TRANS(vpickve2gr_d, LSX, gen_v2g, MO_64, tcg_gen_ld_i64) +-TRANS(vpickve2gr_bu, LSX, gen_v2g, MO_8, tcg_gen_ld8u_i64) +-TRANS(vpickve2gr_hu, LSX, gen_v2g, MO_16, tcg_gen_ld16u_i64) +-TRANS(vpickve2gr_wu, LSX, gen_v2g, MO_32, tcg_gen_ld32u_i64) +-TRANS(vpickve2gr_du, LSX, gen_v2g, MO_64, tcg_gen_ld_i64) +-TRANS(xvpickve2gr_w, LASX, gen_x2g, MO_32, tcg_gen_ld32s_i64) +-TRANS(xvpickve2gr_d, LASX, gen_x2g, MO_64, tcg_gen_ld_i64) +-TRANS(xvpickve2gr_wu, LASX, gen_x2g, MO_32, tcg_gen_ld32u_i64) +-TRANS(xvpickve2gr_du, LASX, gen_x2g, MO_64, tcg_gen_ld_i64) +- +-static bool gvec_dup_vl(DisasContext *ctx, arg_vr *a, +- uint32_t oprsz, MemOp mop) +-{ +- TCGv src = gpr_src(ctx, a->rj, EXT_NONE); +- +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- tcg_gen_gvec_dup_i64(mop, vec_full_offset(a->vd), +- oprsz, ctx->vl/8, src); +- return true; +-} +- +-static bool gvec_dup(DisasContext *ctx, arg_vr *a, MemOp mop) +-{ +- return gvec_dup_vl(ctx, a, 16, mop); +-} +- +-static bool gvec_dupx(DisasContext *ctx, arg_vr *a, MemOp mop) +-{ +- return gvec_dup_vl(ctx, a, 32, mop); +-} +- +-TRANS(vreplgr2vr_b, LSX, gvec_dup, MO_8) +-TRANS(vreplgr2vr_h, LSX, gvec_dup, MO_16) +-TRANS(vreplgr2vr_w, LSX, gvec_dup, MO_32) +-TRANS(vreplgr2vr_d, LSX, gvec_dup, MO_64) +-TRANS(xvreplgr2vr_b, LASX, gvec_dupx, MO_8) +-TRANS(xvreplgr2vr_h, LASX, gvec_dupx, MO_16) +-TRANS(xvreplgr2vr_w, LASX, gvec_dupx, MO_32) +-TRANS(xvreplgr2vr_d, LASX, gvec_dupx, MO_64) +- +-static bool trans_vreplvei_b(DisasContext *ctx, arg_vv_i *a) +-{ +- if (!avail_LSX(ctx)) { +- return false; +- } +- +- if (!check_vec(ctx, 16)) { +- return true; +- } +- +- tcg_gen_gvec_dup_mem(MO_8,vec_full_offset(a->vd), +- offsetof(CPULoongArchState, +- fpr[a->vj].vreg.B((a->imm))), +- 16, ctx->vl/8); +- return true; +-} +- +-static bool trans_vreplvei_h(DisasContext *ctx, arg_vv_i *a) +-{ +- if (!avail_LSX(ctx)) { +- return false; +- } +- +- if (!check_vec(ctx, 16)) { +- return true; +- } +- +- tcg_gen_gvec_dup_mem(MO_16, vec_full_offset(a->vd), +- offsetof(CPULoongArchState, +- fpr[a->vj].vreg.H((a->imm))), +- 16, ctx->vl/8); +- return true; +-} +-static bool trans_vreplvei_w(DisasContext *ctx, arg_vv_i *a) +-{ +- if (!avail_LSX(ctx)) { +- return false; +- } +- +- if (!check_vec(ctx, 16)) { +- return true; +- } +- +- tcg_gen_gvec_dup_mem(MO_32, vec_full_offset(a->vd), +- offsetof(CPULoongArchState, +- fpr[a->vj].vreg.W((a->imm))), +- 16, ctx->vl/8); +- return true; +-} +-static bool trans_vreplvei_d(DisasContext *ctx, arg_vv_i *a) +-{ +- if (!avail_LSX(ctx)) { +- return false; +- } +- +- if (!check_vec(ctx, 16)) { +- return true; +- } +- +- tcg_gen_gvec_dup_mem(MO_64, vec_full_offset(a->vd), +- offsetof(CPULoongArchState, +- fpr[a->vj].vreg.D((a->imm))), +- 16, ctx->vl/8); +- return true; +-} +- +-static bool gen_vreplve_vl(DisasContext *ctx, arg_vvr *a, +- uint32_t oprsz, int vece, int bit, +- void (*func)(TCGv_i64, TCGv_ptr, tcg_target_long)) +-{ +- int i; +- TCGv_i64 t0 = tcg_temp_new_i64(); +- TCGv_ptr t1 = tcg_temp_new_ptr(); +- TCGv_i64 t2 = tcg_temp_new_i64(); +- +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- tcg_gen_andi_i64(t0, gpr_src(ctx, a->rk, EXT_NONE), (LSX_LEN / bit) - 1); +- tcg_gen_shli_i64(t0, t0, vece); +- if (HOST_BIG_ENDIAN) { +- tcg_gen_xori_i64(t0, t0, vece << ((LSX_LEN / bit) - 1)); +- } +- +- tcg_gen_trunc_i64_ptr(t1, t0); +- tcg_gen_add_ptr(t1, t1, tcg_env); +- +- for (i = 0; i < oprsz; i += 16) { +- func(t2, t1, vec_full_offset(a->vj) + i); +- tcg_gen_gvec_dup_i64(vece, vec_full_offset(a->vd) + i, 16, 16, t2); +- } +- +- return true; +-} +- +-static bool gen_vreplve(DisasContext *ctx, arg_vvr *a, int vece, int bit, +- void (*func)(TCGv_i64, TCGv_ptr, tcg_target_long)) +-{ +- return gen_vreplve_vl(ctx, a, 16, vece, bit, func); +-} +- +-static bool gen_xvreplve(DisasContext *ctx, arg_vvr *a, int vece, int bit, +- void (*func)(TCGv_i64, TCGv_ptr, tcg_target_long)) +-{ +- return gen_vreplve_vl(ctx, a, 32, vece, bit, func); +-} +- +-TRANS(vreplve_b, LSX, gen_vreplve, MO_8, 8, tcg_gen_ld8u_i64) +-TRANS(vreplve_h, LSX, gen_vreplve, MO_16, 16, tcg_gen_ld16u_i64) +-TRANS(vreplve_w, LSX, gen_vreplve, MO_32, 32, tcg_gen_ld32u_i64) +-TRANS(vreplve_d, LSX, gen_vreplve, MO_64, 64, tcg_gen_ld_i64) +-TRANS(xvreplve_b, LASX, gen_xvreplve, MO_8, 8, tcg_gen_ld8u_i64) +-TRANS(xvreplve_h, LASX, gen_xvreplve, MO_16, 16, tcg_gen_ld16u_i64) +-TRANS(xvreplve_w, LASX, gen_xvreplve, MO_32, 32, tcg_gen_ld32u_i64) +-TRANS(xvreplve_d, LASX, gen_xvreplve, MO_64, 64, tcg_gen_ld_i64) +- +-static bool gen_xvrepl128(DisasContext *ctx, arg_vv_i *a, MemOp mop) +-{ +- int i; +- +- if (!check_vec(ctx, 32)) { +- return true; +- } +- +- for (i = 0; i < 32; i += 16) { +- tcg_gen_gvec_dup_mem(mop, vec_full_offset(a->vd) + i, +- vec_reg_offset(a->vj, a->imm, mop) + i, 16, 16); +- +- } +- return true; +-} +- +-TRANS(xvrepl128vei_b, LASX, gen_xvrepl128, MO_8) +-TRANS(xvrepl128vei_h, LASX, gen_xvrepl128, MO_16) +-TRANS(xvrepl128vei_w, LASX, gen_xvrepl128, MO_32) +-TRANS(xvrepl128vei_d, LASX, gen_xvrepl128, MO_64) +- +-static bool gen_xvreplve0(DisasContext *ctx, arg_vv *a, MemOp mop) +-{ +- if (!check_vec(ctx, 32)) { +- return true; +- } +- +- tcg_gen_gvec_dup_mem(mop, vec_full_offset(a->vd), +- vec_full_offset(a->vj), 32, 32); +- return true; +-} +- +-TRANS(xvreplve0_b, LASX, gen_xvreplve0, MO_8) +-TRANS(xvreplve0_h, LASX, gen_xvreplve0, MO_16) +-TRANS(xvreplve0_w, LASX, gen_xvreplve0, MO_32) +-TRANS(xvreplve0_d, LASX, gen_xvreplve0, MO_64) +-TRANS(xvreplve0_q, LASX, gen_xvreplve0, MO_128) +- +-TRANS(xvinsve0_w, LASX, gen_xx_i, gen_helper_xvinsve0_w) +-TRANS(xvinsve0_d, LASX, gen_xx_i, gen_helper_xvinsve0_d) +- +-TRANS(xvpickve_w, LASX, gen_xx_i, gen_helper_xvpickve_w) +-TRANS(xvpickve_d, LASX, gen_xx_i, gen_helper_xvpickve_d) +- +-static bool do_vbsll_v(DisasContext *ctx, arg_vv_i *a, uint32_t oprsz) +-{ +- int i, ofs; +- +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- for (i = 0; i < oprsz / 16; i++) { +- TCGv desthigh = tcg_temp_new_i64(); +- TCGv destlow = tcg_temp_new_i64(); +- TCGv high = tcg_temp_new_i64(); +- TCGv low = tcg_temp_new_i64(); +- +- get_vreg64(low, a->vj, 2 * i); +- +- ofs = ((a->imm) & 0xf) * 8; +- if (ofs < 64) { +- get_vreg64(high, a->vj, 2 * i + 1); +- tcg_gen_extract2_i64(desthigh, low, high, 64 - ofs); +- tcg_gen_shli_i64(destlow, low, ofs); +- } else { +- tcg_gen_shli_i64(desthigh, low, ofs - 64); +- destlow = tcg_constant_i64(0); +- } +- set_vreg64(desthigh, a->vd, 2 * i + 1); +- set_vreg64(destlow, a->vd, 2 * i); +- } +- +- return true; +-} +- +-static bool do_vbsrl_v(DisasContext *ctx, arg_vv_i *a, uint32_t oprsz) +-{ +- int i, ofs; +- +- if (!check_vec(ctx, 32)) { +- return true; +- } +- +- for (i = 0; i < oprsz / 16; i++) { +- TCGv desthigh = tcg_temp_new_i64(); +- TCGv destlow = tcg_temp_new_i64(); +- TCGv high = tcg_temp_new_i64(); +- TCGv low = tcg_temp_new_i64(); +- get_vreg64(high, a->vj, 2 * i + 1); +- +- ofs = ((a->imm) & 0xf) * 8; +- if (ofs < 64) { +- get_vreg64(low, a->vj, 2 * i); +- tcg_gen_extract2_i64(destlow, low, high, ofs); +- tcg_gen_shri_i64(desthigh, high, ofs); +- } else { +- tcg_gen_shri_i64(destlow, high, ofs - 64); +- desthigh = tcg_constant_i64(0); +- } +- set_vreg64(desthigh, a->vd, 2 * i + 1); +- set_vreg64(destlow, a->vd, 2 * i); +- } +- +- return true; +-} +- +-TRANS(vbsll_v, LSX, do_vbsll_v, 16) +-TRANS(vbsrl_v, LSX, do_vbsrl_v, 16) +-TRANS(xvbsll_v, LASX, do_vbsll_v, 32) +-TRANS(xvbsrl_v, LASX, do_vbsrl_v, 32) +- +-TRANS(vpackev_b, LSX, gen_vvv, gen_helper_vpackev_b) +-TRANS(vpackev_h, LSX, gen_vvv, gen_helper_vpackev_h) +-TRANS(vpackev_w, LSX, gen_vvv, gen_helper_vpackev_w) +-TRANS(vpackev_d, LSX, gen_vvv, gen_helper_vpackev_d) +-TRANS(vpackod_b, LSX, gen_vvv, gen_helper_vpackod_b) +-TRANS(vpackod_h, LSX, gen_vvv, gen_helper_vpackod_h) +-TRANS(vpackod_w, LSX, gen_vvv, gen_helper_vpackod_w) +-TRANS(vpackod_d, LSX, gen_vvv, gen_helper_vpackod_d) +-TRANS(xvpackev_b, LASX, gen_xxx, gen_helper_vpackev_b) +-TRANS(xvpackev_h, LASX, gen_xxx, gen_helper_vpackev_h) +-TRANS(xvpackev_w, LASX, gen_xxx, gen_helper_vpackev_w) +-TRANS(xvpackev_d, LASX, gen_xxx, gen_helper_vpackev_d) +-TRANS(xvpackod_b, LASX, gen_xxx, gen_helper_vpackod_b) +-TRANS(xvpackod_h, LASX, gen_xxx, gen_helper_vpackod_h) +-TRANS(xvpackod_w, LASX, gen_xxx, gen_helper_vpackod_w) +-TRANS(xvpackod_d, LASX, gen_xxx, gen_helper_vpackod_d) +- +-TRANS(vpickev_b, LSX, gen_vvv, gen_helper_vpickev_b) +-TRANS(vpickev_h, LSX, gen_vvv, gen_helper_vpickev_h) +-TRANS(vpickev_w, LSX, gen_vvv, gen_helper_vpickev_w) +-TRANS(vpickev_d, LSX, gen_vvv, gen_helper_vpickev_d) +-TRANS(vpickod_b, LSX, gen_vvv, gen_helper_vpickod_b) +-TRANS(vpickod_h, LSX, gen_vvv, gen_helper_vpickod_h) +-TRANS(vpickod_w, LSX, gen_vvv, gen_helper_vpickod_w) +-TRANS(vpickod_d, LSX, gen_vvv, gen_helper_vpickod_d) +-TRANS(xvpickev_b, LASX, gen_xxx, gen_helper_vpickev_b) +-TRANS(xvpickev_h, LASX, gen_xxx, gen_helper_vpickev_h) +-TRANS(xvpickev_w, LASX, gen_xxx, gen_helper_vpickev_w) +-TRANS(xvpickev_d, LASX, gen_xxx, gen_helper_vpickev_d) +-TRANS(xvpickod_b, LASX, gen_xxx, gen_helper_vpickod_b) +-TRANS(xvpickod_h, LASX, gen_xxx, gen_helper_vpickod_h) +-TRANS(xvpickod_w, LASX, gen_xxx, gen_helper_vpickod_w) +-TRANS(xvpickod_d, LASX, gen_xxx, gen_helper_vpickod_d) +- +-TRANS(vilvl_b, LSX, gen_vvv, gen_helper_vilvl_b) +-TRANS(vilvl_h, LSX, gen_vvv, gen_helper_vilvl_h) +-TRANS(vilvl_w, LSX, gen_vvv, gen_helper_vilvl_w) +-TRANS(vilvl_d, LSX, gen_vvv, gen_helper_vilvl_d) +-TRANS(vilvh_b, LSX, gen_vvv, gen_helper_vilvh_b) +-TRANS(vilvh_h, LSX, gen_vvv, gen_helper_vilvh_h) +-TRANS(vilvh_w, LSX, gen_vvv, gen_helper_vilvh_w) +-TRANS(vilvh_d, LSX, gen_vvv, gen_helper_vilvh_d) +-TRANS(xvilvl_b, LASX, gen_xxx, gen_helper_vilvl_b) +-TRANS(xvilvl_h, LASX, gen_xxx, gen_helper_vilvl_h) +-TRANS(xvilvl_w, LASX, gen_xxx, gen_helper_vilvl_w) +-TRANS(xvilvl_d, LASX, gen_xxx, gen_helper_vilvl_d) +-TRANS(xvilvh_b, LASX, gen_xxx, gen_helper_vilvh_b) +-TRANS(xvilvh_h, LASX, gen_xxx, gen_helper_vilvh_h) +-TRANS(xvilvh_w, LASX, gen_xxx, gen_helper_vilvh_w) +-TRANS(xvilvh_d, LASX, gen_xxx, gen_helper_vilvh_d) +- +-TRANS(vshuf_b, LSX, gen_vvvv, gen_helper_vshuf_b) +-TRANS(vshuf_h, LSX, gen_vvv, gen_helper_vshuf_h) +-TRANS(vshuf_w, LSX, gen_vvv, gen_helper_vshuf_w) +-TRANS(vshuf_d, LSX, gen_vvv, gen_helper_vshuf_d) +-TRANS(xvshuf_b, LASX, gen_xxxx, gen_helper_vshuf_b) +-TRANS(xvshuf_h, LASX, gen_xxx, gen_helper_vshuf_h) +-TRANS(xvshuf_w, LASX, gen_xxx, gen_helper_vshuf_w) +-TRANS(xvshuf_d, LASX, gen_xxx, gen_helper_vshuf_d) +-TRANS(vshuf4i_b, LSX, gen_vv_i, gen_helper_vshuf4i_b) +-TRANS(vshuf4i_h, LSX, gen_vv_i, gen_helper_vshuf4i_h) +-TRANS(vshuf4i_w, LSX, gen_vv_i, gen_helper_vshuf4i_w) +-TRANS(vshuf4i_d, LSX, gen_vv_i, gen_helper_vshuf4i_d) +-TRANS(xvshuf4i_b, LASX, gen_xx_i, gen_helper_vshuf4i_b) +-TRANS(xvshuf4i_h, LASX, gen_xx_i, gen_helper_vshuf4i_h) +-TRANS(xvshuf4i_w, LASX, gen_xx_i, gen_helper_vshuf4i_w) +-TRANS(xvshuf4i_d, LASX, gen_xx_i, gen_helper_vshuf4i_d) +- +-TRANS(xvperm_w, LASX, gen_xxx, gen_helper_vperm_w) +-TRANS(vpermi_w, LSX, gen_vv_i, gen_helper_vpermi_w) +-TRANS(xvpermi_w, LASX, gen_xx_i, gen_helper_vpermi_w) +-TRANS(xvpermi_d, LASX, gen_xx_i, gen_helper_vpermi_d) +-TRANS(xvpermi_q, LASX, gen_xx_i, gen_helper_vpermi_q) +- +-TRANS(vextrins_b, LSX, gen_vv_i, gen_helper_vextrins_b) +-TRANS(vextrins_h, LSX, gen_vv_i, gen_helper_vextrins_h) +-TRANS(vextrins_w, LSX, gen_vv_i, gen_helper_vextrins_w) +-TRANS(vextrins_d, LSX, gen_vv_i, gen_helper_vextrins_d) +-TRANS(xvextrins_b, LASX, gen_xx_i, gen_helper_vextrins_b) +-TRANS(xvextrins_h, LASX, gen_xx_i, gen_helper_vextrins_h) +-TRANS(xvextrins_w, LASX, gen_xx_i, gen_helper_vextrins_w) +-TRANS(xvextrins_d, LASX, gen_xx_i, gen_helper_vextrins_d) +- +-static bool trans_vld(DisasContext *ctx, arg_vr_i *a) +-{ +- TCGv addr; +- TCGv_i64 rl, rh; +- TCGv_i128 val; +- +- if (!avail_LSX(ctx)) { +- return false; +- } +- +- if (!check_vec(ctx, 16)) { +- return true; +- } +- +- addr = gpr_src(ctx, a->rj, EXT_NONE); +- val = tcg_temp_new_i128(); +- rl = tcg_temp_new_i64(); +- rh = tcg_temp_new_i64(); +- +- addr = make_address_i(ctx, addr, a->imm); +- +- tcg_gen_qemu_ld_i128(val, addr, ctx->mem_idx, MO_128 | MO_TE); +- tcg_gen_extr_i128_i64(rl, rh, val); +- set_vreg64(rh, a->vd, 1); +- set_vreg64(rl, a->vd, 0); +- +- return true; +-} +- +-static bool trans_vst(DisasContext *ctx, arg_vr_i *a) +-{ +- TCGv addr; +- TCGv_i128 val; +- TCGv_i64 ah, al; +- +- if (!avail_LSX(ctx)) { +- return false; +- } +- +- if (!check_vec(ctx, 16)) { +- return true; +- } +- +- addr = gpr_src(ctx, a->rj, EXT_NONE); +- val = tcg_temp_new_i128(); +- ah = tcg_temp_new_i64(); +- al = tcg_temp_new_i64(); +- +- addr = make_address_i(ctx, addr, a->imm); +- +- get_vreg64(ah, a->vd, 1); +- get_vreg64(al, a->vd, 0); +- tcg_gen_concat_i64_i128(val, al, ah); +- tcg_gen_qemu_st_i128(val, addr, ctx->mem_idx, MO_128 | MO_TE); +- +- return true; +-} +- +-static bool trans_vldx(DisasContext *ctx, arg_vrr *a) +-{ +- TCGv addr, src1, src2; +- TCGv_i64 rl, rh; +- TCGv_i128 val; +- +- if (!avail_LSX(ctx)) { +- return false; +- } +- +- if (!check_vec(ctx, 16)) { +- return true; +- } +- +- src1 = gpr_src(ctx, a->rj, EXT_NONE); +- src2 = gpr_src(ctx, a->rk, EXT_NONE); +- val = tcg_temp_new_i128(); +- rl = tcg_temp_new_i64(); +- rh = tcg_temp_new_i64(); +- +- addr = make_address_x(ctx, src1, src2); +- tcg_gen_qemu_ld_i128(val, addr, ctx->mem_idx, MO_128 | MO_TE); +- tcg_gen_extr_i128_i64(rl, rh, val); +- set_vreg64(rh, a->vd, 1); +- set_vreg64(rl, a->vd, 0); +- +- return true; +-} +- +-static bool trans_vstx(DisasContext *ctx, arg_vrr *a) +-{ +- TCGv addr, src1, src2; +- TCGv_i64 ah, al; +- TCGv_i128 val; +- +- if (!avail_LSX(ctx)) { +- return false; +- } +- +- if (!check_vec(ctx, 16)) { +- return true; +- } +- +- src1 = gpr_src(ctx, a->rj, EXT_NONE); +- src2 = gpr_src(ctx, a->rk, EXT_NONE); +- val = tcg_temp_new_i128(); +- ah = tcg_temp_new_i64(); +- al = tcg_temp_new_i64(); +- +- addr = make_address_x(ctx, src1, src2); +- get_vreg64(ah, a->vd, 1); +- get_vreg64(al, a->vd, 0); +- tcg_gen_concat_i64_i128(val, al, ah); +- tcg_gen_qemu_st_i128(val, addr, ctx->mem_idx, MO_128 | MO_TE); +- +- return true; +-} +- +-static bool do_vldrepl_vl(DisasContext *ctx, arg_vr_i *a, +- uint32_t oprsz, MemOp mop) +-{ +- TCGv addr; +- TCGv_i64 val; +- +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- addr = gpr_src(ctx, a->rj, EXT_NONE); +- val = tcg_temp_new_i64(); +- +- addr = make_address_i(ctx, addr, a->imm); +- +- tcg_gen_qemu_ld_i64(val, addr, ctx->mem_idx, mop); +- tcg_gen_gvec_dup_i64(mop, vec_full_offset(a->vd), oprsz, ctx->vl / 8, val); +- +- return true; +-} +- +-static bool do_vldrepl(DisasContext *ctx, arg_vr_i *a, MemOp mop) +-{ +- return do_vldrepl_vl(ctx, a, 16, mop); +-} +- +-static bool do_xvldrepl(DisasContext *ctx, arg_vr_i *a, MemOp mop) +-{ +- return do_vldrepl_vl(ctx, a, 32, mop); +-} +- +-TRANS(vldrepl_b, LSX, do_vldrepl, MO_8) +-TRANS(vldrepl_h, LSX, do_vldrepl, MO_16) +-TRANS(vldrepl_w, LSX, do_vldrepl, MO_32) +-TRANS(vldrepl_d, LSX, do_vldrepl, MO_64) +-TRANS(xvldrepl_b, LASX, do_xvldrepl, MO_8) +-TRANS(xvldrepl_h, LASX, do_xvldrepl, MO_16) +-TRANS(xvldrepl_w, LASX, do_xvldrepl, MO_32) +-TRANS(xvldrepl_d, LASX, do_xvldrepl, MO_64) +- +-static bool do_vstelm_vl(DisasContext *ctx, +- arg_vr_ii *a, uint32_t oprsz, MemOp mop) +-{ +- TCGv addr; +- TCGv_i64 val; +- +- if (!check_vec(ctx, oprsz)) { +- return true; +- } +- +- addr = gpr_src(ctx, a->rj, EXT_NONE); +- val = tcg_temp_new_i64(); +- +- addr = make_address_i(ctx, addr, a->imm); +- tcg_gen_ld_i64(val, tcg_env, vec_reg_offset(a->vd, a->imm2, mop)); +- tcg_gen_qemu_st_i64(val, addr, ctx->mem_idx, mop); +- return true; +-} +- +-static bool do_vstelm(DisasContext *ctx, arg_vr_ii *a, MemOp mop) +-{ +- return do_vstelm_vl(ctx, a, 16, mop); +-} +- +-static bool do_xvstelm(DisasContext *ctx, arg_vr_ii *a, MemOp mop) +-{ +- return do_vstelm_vl(ctx, a, 32, mop); +-} +- +-TRANS(vstelm_b, LSX, do_vstelm, MO_8) +-TRANS(vstelm_h, LSX, do_vstelm, MO_16) +-TRANS(vstelm_w, LSX, do_vstelm, MO_32) +-TRANS(vstelm_d, LSX, do_vstelm, MO_64) +-TRANS(xvstelm_b, LASX, do_xvstelm, MO_8) +-TRANS(xvstelm_h, LASX, do_xvstelm, MO_16) +-TRANS(xvstelm_w, LASX, do_xvstelm, MO_32) +-TRANS(xvstelm_d, LASX, do_xvstelm, MO_64) +- +-static bool gen_lasx_memory(DisasContext *ctx, arg_vr_i *a, +- void (*func)(DisasContext *, int, TCGv)) +-{ +- TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv temp = NULL; +- +- if (!check_vec(ctx, 32)) { +- return true; +- } +- +- if (a->imm) { +- temp = tcg_temp_new(); +- tcg_gen_addi_tl(temp, addr, a->imm); +- addr = temp; +- } +- +- func(ctx, a->vd, addr); +- return true; +-} +- +-static void gen_xvld(DisasContext *ctx, int vreg, TCGv addr) +-{ +- int i; +- TCGv temp = tcg_temp_new(); +- TCGv dest = tcg_temp_new(); +- +- tcg_gen_qemu_ld_i64(dest, addr, ctx->mem_idx, MO_TEUQ); +- set_vreg64(dest, vreg, 0); +- +- for (i = 1; i < 4; i++) { +- tcg_gen_addi_tl(temp, addr, 8 * i); +- tcg_gen_qemu_ld_i64(dest, temp, ctx->mem_idx, MO_TEUQ); +- set_vreg64(dest, vreg, i); +- } +-} +- +-static void gen_xvst(DisasContext * ctx, int vreg, TCGv addr) +-{ +- int i; +- TCGv temp = tcg_temp_new(); +- TCGv dest = tcg_temp_new(); +- +- get_vreg64(dest, vreg, 0); +- tcg_gen_qemu_st_i64(dest, addr, ctx->mem_idx, MO_TEUQ); +- +- for (i = 1; i < 4; i++) { +- tcg_gen_addi_tl(temp, addr, 8 * i); +- get_vreg64(dest, vreg, i); +- tcg_gen_qemu_st_i64(dest, temp, ctx->mem_idx, MO_TEUQ); +- } +-} +- +-TRANS(xvld, LASX, gen_lasx_memory, gen_xvld) +-TRANS(xvst, LASX, gen_lasx_memory, gen_xvst) +- +-static bool gen_lasx_memoryx(DisasContext *ctx, arg_vrr *a, +- void (*func)(DisasContext*, int, TCGv)) +-{ +- TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); +- TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); +- TCGv addr = tcg_temp_new(); +- +- if (!check_vec(ctx, 32)) { +- return true; +- } +- +- tcg_gen_add_tl(addr, src1, src2); +- func(ctx, a->vd, addr); +- +- return true; +-} +- +-TRANS(xvldx, LASX, gen_lasx_memoryx, gen_xvld) +-TRANS(xvstx, LASX, gen_lasx_memoryx, gen_xvst) +diff --git a/target/loongarch/iocsr_helper.c b/target/loongarch/iocsr_helper.c +deleted file mode 100644 +index 6cd01d5..0000000 +--- a/target/loongarch/iocsr_helper.c ++++ /dev/null +@@ -1,68 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * Copyright (c) 2021 Loongson Technology Corporation Limited +- * +- * Helpers for IOCSR reads/writes +- */ +- +-#include "qemu/osdep.h" +-#include "cpu.h" +-#include "qemu/host-utils.h" +-#include "exec/helper-proto.h" +-#include "exec/exec-all.h" +-#include "exec/cpu_ldst.h" +- +-#define GET_MEMTXATTRS(cas) \ +- ((MemTxAttrs){.requester_id = env_cpu(cas)->cpu_index}) +- +-uint64_t helper_iocsrrd_b(CPULoongArchState *env, target_ulong r_addr) +-{ +- return address_space_ldub(&env->address_space_iocsr, r_addr, +- GET_MEMTXATTRS(env), NULL); +-} +- +-uint64_t helper_iocsrrd_h(CPULoongArchState *env, target_ulong r_addr) +-{ +- return address_space_lduw(&env->address_space_iocsr, r_addr, +- GET_MEMTXATTRS(env), NULL); +-} +- +-uint64_t helper_iocsrrd_w(CPULoongArchState *env, target_ulong r_addr) +-{ +- return address_space_ldl(&env->address_space_iocsr, r_addr, +- GET_MEMTXATTRS(env), NULL); +-} +- +-uint64_t helper_iocsrrd_d(CPULoongArchState *env, target_ulong r_addr) +-{ +- return address_space_ldq(&env->address_space_iocsr, r_addr, +- GET_MEMTXATTRS(env), NULL); +-} +- +-void helper_iocsrwr_b(CPULoongArchState *env, target_ulong w_addr, +- target_ulong val) +-{ +- address_space_stb(&env->address_space_iocsr, w_addr, +- val, GET_MEMTXATTRS(env), NULL); +-} +- +-void helper_iocsrwr_h(CPULoongArchState *env, target_ulong w_addr, +- target_ulong val) +-{ +- address_space_stw(&env->address_space_iocsr, w_addr, +- val, GET_MEMTXATTRS(env), NULL); +-} +- +-void helper_iocsrwr_w(CPULoongArchState *env, target_ulong w_addr, +- target_ulong val) +-{ +- address_space_stl(&env->address_space_iocsr, w_addr, +- val, GET_MEMTXATTRS(env), NULL); +-} +- +-void helper_iocsrwr_d(CPULoongArchState *env, target_ulong w_addr, +- target_ulong val) +-{ +- address_space_stq(&env->address_space_iocsr, w_addr, +- val, GET_MEMTXATTRS(env), NULL); +-} +diff --git a/target/loongarch/meson.build b/target/loongarch/meson.build +index b3a0fb1..e84e4c5 100644 +--- a/target/loongarch/meson.build ++++ b/target/loongarch/meson.build +@@ -5,29 +5,16 @@ loongarch_ss.add(files( + 'cpu.c', + 'gdbstub.c', + )) +-loongarch_tcg_ss = ss.source_set() +-loongarch_tcg_ss.add(gen) +-loongarch_tcg_ss.add(files( +- 'fpu_helper.c', +- 'op_helper.c', +- 'translate.c', +- 'vec_helper.c', +-)) +-loongarch_tcg_ss.add(zlib) + + loongarch_system_ss = ss.source_set() + loongarch_system_ss.add(files( + 'loongarch-qmp-cmds.c', + 'machine.c', +- 'tlb_helper.c', +- 'constant_timer.c', +- 'csr_helper.c', +- 'iocsr_helper.c', + )) + + common_ss.add(when: 'CONFIG_LOONGARCH_DIS', if_true: [files('disas.c'), gen]) + +-loongarch_ss.add_all(when: 'CONFIG_TCG', if_true: [loongarch_tcg_ss]) ++subdir('tcg') + + target_arch += {'loongarch': loongarch_ss} + target_system_arch += {'loongarch': loongarch_system_ss} +diff --git a/target/loongarch/op_helper.c b/target/loongarch/op_helper.c +deleted file mode 100644 +index fe79c62..0000000 +--- a/target/loongarch/op_helper.c ++++ /dev/null +@@ -1,140 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * LoongArch emulation helpers for QEMU. +- * +- * Copyright (c) 2021 Loongson Technology Corporation Limited +- */ +- +-#include "qemu/osdep.h" +-#include "qemu/log.h" +-#include "cpu.h" +-#include "qemu/host-utils.h" +-#include "exec/helper-proto.h" +-#include "exec/exec-all.h" +-#include "exec/cpu_ldst.h" +-#include "internals.h" +-#include "qemu/crc32c.h" +-#include +-#include "cpu-csr.h" +- +-/* Exceptions helpers */ +-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) +-{ +- if (rj > rk) { +- env->CSR_BADV = rj; +- do_raise_exception(env, EXCCODE_BCE, GETPC()); +- } +-} +- +-void helper_asrtgt_d(CPULoongArchState *env, target_ulong rj, target_ulong rk) +-{ +- if (rj <= rk) { +- env->CSR_BADV = rj; +- do_raise_exception(env, EXCCODE_BCE, GETPC()); +- } +-} +- +-target_ulong helper_crc32(target_ulong val, target_ulong m, uint64_t sz) +-{ +- uint8_t buf[8]; +- target_ulong mask = ((sz * 8) == 64) ? -1ULL : ((1ULL << (sz * 8)) - 1); +- +- m &= mask; +- stq_le_p(buf, m); +- return (int32_t) (crc32(val ^ 0xffffffff, buf, sz) ^ 0xffffffff); +-} +- +-target_ulong helper_crc32c(target_ulong val, target_ulong m, uint64_t sz) +-{ +- uint8_t buf[8]; +- target_ulong mask = ((sz * 8) == 64) ? -1ULL : ((1ULL << (sz * 8)) - 1); +- m &= mask; +- stq_le_p(buf, m); +- return (int32_t) (crc32c(val, buf, sz) ^ 0xffffffff); +-} +- +-target_ulong helper_cpucfg(CPULoongArchState *env, target_ulong rj) +-{ +- return rj >= ARRAY_SIZE(env->cpucfg) ? 0 : env->cpucfg[rj]; +-} +- +-uint64_t helper_rdtime_d(CPULoongArchState *env) +-{ +-#ifdef CONFIG_USER_ONLY +- return cpu_get_host_ticks(); +-#else +- uint64_t plv; +- LoongArchCPU *cpu = env_archcpu(env); +- +- plv = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV); +- if (extract64(env->CSR_MISC, R_CSR_MISC_DRDTL_SHIFT + plv, 1)) { +- do_raise_exception(env, EXCCODE_IPE, GETPC()); +- } +- +- return cpu_loongarch_get_constant_timer_counter(cpu); +-#endif +-} +- +-#ifndef CONFIG_USER_ONLY +-void helper_ertn(CPULoongArchState *env) +-{ +- uint64_t csr_pplv, csr_pie; +- if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) { +- csr_pplv = FIELD_EX64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PPLV); +- csr_pie = FIELD_EX64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PIE); +- +- env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 0); +- env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DA, 0); +- env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PG, 1); +- set_pc(env, env->CSR_TLBRERA); +- qemu_log_mask(CPU_LOG_INT, "%s: TLBRERA " TARGET_FMT_lx "\n", +- __func__, env->CSR_TLBRERA); +- } else { +- csr_pplv = FIELD_EX64(env->CSR_PRMD, CSR_PRMD, PPLV); +- csr_pie = FIELD_EX64(env->CSR_PRMD, CSR_PRMD, PIE); +- +- set_pc(env, env->CSR_ERA); +- qemu_log_mask(CPU_LOG_INT, "%s: ERA " TARGET_FMT_lx "\n", +- __func__, env->CSR_ERA); +- } +- env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, csr_pplv); +- env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, csr_pie); +- +- env->lladdr = 1; +-} +- +-void helper_idle(CPULoongArchState *env) +-{ +- CPUState *cs = env_cpu(env); +- +- cs->halted = 1; +- do_raise_exception(env, EXCP_HLT, 0); +-} +-#endif +diff --git a/target/loongarch/tcg/constant_timer.c b/target/loongarch/tcg/constant_timer.c +new file mode 100644 +index 0000000..1851f53 +--- /dev/null ++++ b/target/loongarch/tcg/constant_timer.c +@@ -0,0 +1,64 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * QEMU LoongArch constant timer support ++ * ++ * Copyright (c) 2021 Loongson Technology Corporation Limited ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/timer.h" ++#include "cpu.h" ++#include "internals.h" ++#include "cpu-csr.h" ++ ++#define TIMER_PERIOD 10 /* 10 ns period for 100 MHz frequency */ ++#define CONSTANT_TIMER_TICK_MASK 0xfffffffffffcUL ++#define CONSTANT_TIMER_ENABLE 0x1UL ++ ++uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu) ++{ ++ return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD; ++} ++ ++uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu) ++{ ++ uint64_t now, expire; ++ ++ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); ++ expire = timer_expire_time_ns(&cpu->timer); ++ ++ return (expire - now) / TIMER_PERIOD; ++} ++ ++void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu, ++ uint64_t value) ++{ ++ CPULoongArchState *env = &cpu->env; ++ uint64_t now, next; ++ ++ env->CSR_TCFG = value; ++ if (value & CONSTANT_TIMER_ENABLE) { ++ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); ++ next = now + (value & CONSTANT_TIMER_TICK_MASK) * TIMER_PERIOD; ++ timer_mod(&cpu->timer, next); ++ } else { ++ timer_del(&cpu->timer); ++ } ++} ++ ++void loongarch_constant_timer_cb(void *opaque) ++{ ++ LoongArchCPU *cpu = opaque; ++ CPULoongArchState *env = &cpu->env; ++ uint64_t now, next; ++ ++ if (FIELD_EX64(env->CSR_TCFG, CSR_TCFG, PERIODIC)) { ++ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); ++ next = now + (env->CSR_TCFG & CONSTANT_TIMER_TICK_MASK) * TIMER_PERIOD; ++ timer_mod(&cpu->timer, next); ++ } else { ++ env->CSR_TCFG = FIELD_DP64(env->CSR_TCFG, CSR_TCFG, EN, 0); ++ } ++ ++ loongarch_cpu_set_irq(opaque, IRQ_TIMER, 1); ++} +diff --git a/target/loongarch/tcg/csr_helper.c b/target/loongarch/tcg/csr_helper.c +new file mode 100644 +index 0000000..5534155 +--- /dev/null ++++ b/target/loongarch/tcg/csr_helper.c +@@ -0,0 +1,97 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * LoongArch emulation helpers for CSRs ++ * ++ * Copyright (c) 2021 Loongson Technology Corporation Limited ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/main-loop.h" ++#include "cpu.h" ++#include "internals.h" ++#include "qemu/host-utils.h" ++#include "exec/helper-proto.h" ++#include "exec/exec-all.h" ++#include "exec/cpu_ldst.h" ++#include "hw/irq.h" ++#include "cpu-csr.h" ++ ++target_ulong helper_csrrd_pgd(CPULoongArchState *env) ++{ ++ int64_t v; ++ ++ if (env->CSR_TLBRERA & 0x1) { ++ v = env->CSR_TLBRBADV; ++ } else { ++ v = env->CSR_BADV; ++ } ++ ++ if ((v >> 63) & 0x1) { ++ v = env->CSR_PGDH; ++ } else { ++ v = env->CSR_PGDL; ++ } ++ ++ return v; ++} ++ ++target_ulong helper_csrrd_cpuid(CPULoongArchState *env) ++{ ++ LoongArchCPU *lac = env_archcpu(env); ++ ++ env->CSR_CPUID = CPU(lac)->cpu_index; ++ ++ return env->CSR_CPUID; ++} ++ ++target_ulong helper_csrrd_tval(CPULoongArchState *env) ++{ ++ LoongArchCPU *cpu = env_archcpu(env); ++ ++ return cpu_loongarch_get_constant_timer_ticks(cpu); ++} ++ ++target_ulong helper_csrwr_estat(CPULoongArchState *env, target_ulong val) ++{ ++ int64_t old_v = env->CSR_ESTAT; ++ ++ /* Only IS[1:0] can be written */ ++ env->CSR_ESTAT = deposit64(env->CSR_ESTAT, 0, 2, val); ++ ++ return old_v; ++} ++ ++target_ulong helper_csrwr_asid(CPULoongArchState *env, target_ulong val) ++{ ++ int64_t old_v = env->CSR_ASID; ++ ++ /* Only ASID filed of CSR_ASID can be written */ ++ env->CSR_ASID = deposit64(env->CSR_ASID, 0, 10, val); ++ if (old_v != env->CSR_ASID) { ++ tlb_flush(env_cpu(env)); ++ } ++ return old_v; ++} ++ ++target_ulong helper_csrwr_tcfg(CPULoongArchState *env, target_ulong val) ++{ ++ LoongArchCPU *cpu = env_archcpu(env); ++ int64_t old_v = env->CSR_TCFG; ++ ++ cpu_loongarch_store_constant_timer_config(cpu, val); ++ ++ return old_v; ++} ++ ++target_ulong helper_csrwr_ticlr(CPULoongArchState *env, target_ulong val) ++{ ++ LoongArchCPU *cpu = env_archcpu(env); ++ int64_t old_v = 0; ++ ++ if (val & 0x1) { ++ qemu_mutex_lock_iothread(); ++ loongarch_cpu_set_irq(cpu, IRQ_TIMER, 0); ++ qemu_mutex_unlock_iothread(); ++ } ++ return old_v; ++} +diff --git a/target/loongarch/tcg/fpu_helper.c b/target/loongarch/tcg/fpu_helper.c +new file mode 100644 +index 0000000..f6753c5 +--- /dev/null ++++ b/target/loongarch/tcg/fpu_helper.c +@@ -0,0 +1,879 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * LoongArch float point emulation helpers for QEMU ++ * ++ * Copyright (c) 2021 Loongson Technology Corporation Limited ++ */ ++ ++#include "qemu/osdep.h" ++#include "cpu.h" ++#include "exec/helper-proto.h" ++#include "exec/exec-all.h" ++#include "exec/cpu_ldst.h" ++#include "fpu/softfloat.h" ++#include "internals.h" ++ ++static inline uint64_t nanbox_s(float32 fp) ++{ ++ return fp | MAKE_64BIT_MASK(32, 32); ++} ++ ++/* Convert loongarch rounding mode in fcsr0 to IEEE library */ ++static const FloatRoundMode ieee_rm[4] = { ++ float_round_nearest_even, ++ float_round_to_zero, ++ float_round_up, ++ float_round_down ++}; ++ ++void restore_fp_status(CPULoongArchState *env) ++{ ++ set_float_rounding_mode(ieee_rm[(env->fcsr0 >> FCSR0_RM) & 0x3], ++ &env->fp_status); ++ set_flush_to_zero(0, &env->fp_status); ++} ++ ++int ieee_ex_to_loongarch(int xcpt) ++{ ++ int ret = 0; ++ if (xcpt & float_flag_invalid) { ++ ret |= FP_INVALID; ++ } ++ if (xcpt & float_flag_overflow) { ++ ret |= FP_OVERFLOW; ++ } ++ if (xcpt & float_flag_underflow) { ++ ret |= FP_UNDERFLOW; ++ } ++ if (xcpt & float_flag_divbyzero) { ++ ret |= FP_DIV0; ++ } ++ if (xcpt & float_flag_inexact) { ++ ret |= FP_INEXACT; ++ } ++ return ret; ++} ++ ++static void update_fcsr0_mask(CPULoongArchState *env, uintptr_t pc, int mask) ++{ ++ int flags = get_float_exception_flags(&env->fp_status); ++ ++ set_float_exception_flags(0, &env->fp_status); ++ ++ flags &= ~mask; ++ ++ if (!flags) { ++ SET_FP_CAUSE(env->fcsr0, flags); ++ return; ++ } else { ++ flags = ieee_ex_to_loongarch(flags); ++ SET_FP_CAUSE(env->fcsr0, flags); ++ } ++ ++ if (GET_FP_ENABLES(env->fcsr0) & flags) { ++ do_raise_exception(env, EXCCODE_FPE, pc); ++ } else { ++ UPDATE_FP_FLAGS(env->fcsr0, flags); ++ } ++} ++ ++static void update_fcsr0(CPULoongArchState *env, uintptr_t pc) ++{ ++ update_fcsr0_mask(env, pc, 0); ++} ++ ++uint64_t helper_fadd_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) ++{ ++ uint64_t fd; ++ ++ fd = nanbox_s(float32_add((uint32_t)fj, (uint32_t)fk, &env->fp_status)); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_fadd_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) ++{ ++ uint64_t fd; ++ ++ fd = float64_add(fj, fk, &env->fp_status); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_fsub_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) ++{ ++ uint64_t fd; ++ ++ fd = nanbox_s(float32_sub((uint32_t)fj, (uint32_t)fk, &env->fp_status)); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_fsub_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) ++{ ++ uint64_t fd; ++ ++ fd = float64_sub(fj, fk, &env->fp_status); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_fmul_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) ++{ ++ uint64_t fd; ++ ++ fd = nanbox_s(float32_mul((uint32_t)fj, (uint32_t)fk, &env->fp_status)); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_fmul_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) ++{ ++ uint64_t fd; ++ ++ fd = float64_mul(fj, fk, &env->fp_status); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_fdiv_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) ++{ ++ uint64_t fd; ++ ++ fd = nanbox_s(float32_div((uint32_t)fj, (uint32_t)fk, &env->fp_status)); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_fdiv_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) ++{ ++ uint64_t fd; ++ ++ fd = float64_div(fj, fk, &env->fp_status); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_fmax_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) ++{ ++ uint64_t fd; ++ ++ fd = nanbox_s(float32_maxnum((uint32_t)fj, (uint32_t)fk, &env->fp_status)); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_fmax_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) ++{ ++ uint64_t fd; ++ ++ fd = float64_maxnum(fj, fk, &env->fp_status); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_fmin_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) ++{ ++ uint64_t fd; ++ ++ fd = nanbox_s(float32_minnum((uint32_t)fj, (uint32_t)fk, &env->fp_status)); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_fmin_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) ++{ ++ uint64_t fd; ++ ++ fd = float64_minnum(fj, fk, &env->fp_status); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_fmaxa_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) ++{ ++ uint64_t fd; ++ ++ fd = nanbox_s(float32_maxnummag((uint32_t)fj, ++ (uint32_t)fk, &env->fp_status)); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_fmaxa_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) ++{ ++ uint64_t fd; ++ ++ fd = float64_maxnummag(fj, fk, &env->fp_status); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_fmina_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) ++{ ++ uint64_t fd; ++ ++ fd = nanbox_s(float32_minnummag((uint32_t)fj, ++ (uint32_t)fk, &env->fp_status)); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_fmina_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) ++{ ++ uint64_t fd; ++ ++ fd = float64_minnummag(fj, fk, &env->fp_status); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_fscaleb_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) ++{ ++ uint64_t fd; ++ int32_t n = (int32_t)fk; ++ ++ fd = nanbox_s(float32_scalbn((uint32_t)fj, ++ n > 0x200 ? 0x200 : ++ n < -0x200 ? -0x200 : n, ++ &env->fp_status)); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_fscaleb_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) ++{ ++ uint64_t fd; ++ int64_t n = (int64_t)fk; ++ ++ fd = float64_scalbn(fj, ++ n > 0x1000 ? 0x1000 : ++ n < -0x1000 ? -0x1000 : n, ++ &env->fp_status); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_fsqrt_s(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ ++ fd = nanbox_s(float32_sqrt((uint32_t)fj, &env->fp_status)); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_fsqrt_d(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ ++ fd = float64_sqrt(fj, &env->fp_status); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_frecip_s(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ ++ fd = nanbox_s(float32_div(float32_one, (uint32_t)fj, &env->fp_status)); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_frecip_d(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ ++ fd = float64_div(float64_one, fj, &env->fp_status); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_frsqrt_s(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ uint32_t fp; ++ ++ fp = float32_sqrt((uint32_t)fj, &env->fp_status); ++ fd = nanbox_s(float32_div(float32_one, fp, &env->fp_status)); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_frsqrt_d(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fp, fd; ++ ++ fp = float64_sqrt(fj, &env->fp_status); ++ fd = float64_div(float64_one, fp, &env->fp_status); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_flogb_s(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ uint32_t fp; ++ float_status *status = &env->fp_status; ++ FloatRoundMode old_mode = get_float_rounding_mode(status); ++ ++ set_float_rounding_mode(float_round_down, status); ++ fp = float32_log2((uint32_t)fj, status); ++ fd = nanbox_s(float32_round_to_int(fp, status)); ++ set_float_rounding_mode(old_mode, status); ++ update_fcsr0_mask(env, GETPC(), float_flag_inexact); ++ return fd; ++} ++ ++uint64_t helper_flogb_d(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ float_status *status = &env->fp_status; ++ FloatRoundMode old_mode = get_float_rounding_mode(status); ++ ++ set_float_rounding_mode(float_round_down, status); ++ fd = float64_log2(fj, status); ++ fd = float64_round_to_int(fd, status); ++ set_float_rounding_mode(old_mode, status); ++ update_fcsr0_mask(env, GETPC(), float_flag_inexact); ++ return fd; ++} ++ ++uint64_t helper_fclass_s(CPULoongArchState *env, uint64_t fj) ++{ ++ float32 f = fj; ++ bool sign = float32_is_neg(f); ++ ++ if (float32_is_infinity(f)) { ++ return sign ? 1 << 2 : 1 << 6; ++ } else if (float32_is_zero(f)) { ++ return sign ? 1 << 5 : 1 << 9; ++ } else if (float32_is_zero_or_denormal(f)) { ++ return sign ? 1 << 4 : 1 << 8; ++ } else if (float32_is_any_nan(f)) { ++ float_status s = { }; /* for snan_bit_is_one */ ++ return float32_is_quiet_nan(f, &s) ? 1 << 1 : 1 << 0; ++ } else { ++ return sign ? 1 << 3 : 1 << 7; ++ } ++} ++ ++uint64_t helper_fclass_d(CPULoongArchState *env, uint64_t fj) ++{ ++ float64 f = fj; ++ bool sign = float64_is_neg(f); ++ ++ if (float64_is_infinity(f)) { ++ return sign ? 1 << 2 : 1 << 6; ++ } else if (float64_is_zero(f)) { ++ return sign ? 1 << 5 : 1 << 9; ++ } else if (float64_is_zero_or_denormal(f)) { ++ return sign ? 1 << 4 : 1 << 8; ++ } else if (float64_is_any_nan(f)) { ++ float_status s = { }; /* for snan_bit_is_one */ ++ return float64_is_quiet_nan(f, &s) ? 1 << 1 : 1 << 0; ++ } else { ++ return sign ? 1 << 3 : 1 << 7; ++ } ++} ++ ++uint64_t helper_fmuladd_s(CPULoongArchState *env, uint64_t fj, ++ uint64_t fk, uint64_t fa, uint32_t flag) ++{ ++ uint64_t fd; ++ ++ fd = nanbox_s(float32_muladd((uint32_t)fj, (uint32_t)fk, ++ (uint32_t)fa, flag, &env->fp_status)); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_fmuladd_d(CPULoongArchState *env, uint64_t fj, ++ uint64_t fk, uint64_t fa, uint32_t flag) ++{ ++ uint64_t fd; ++ ++ fd = float64_muladd(fj, fk, fa, flag, &env->fp_status); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++static uint64_t fcmp_common(CPULoongArchState *env, FloatRelation cmp, ++ uint32_t flags) ++{ ++ bool ret; ++ ++ switch (cmp) { ++ case float_relation_less: ++ ret = (flags & FCMP_LT); ++ break; ++ case float_relation_equal: ++ ret = (flags & FCMP_EQ); ++ break; ++ case float_relation_greater: ++ ret = (flags & FCMP_GT); ++ break; ++ case float_relation_unordered: ++ ret = (flags & FCMP_UN); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ update_fcsr0(env, GETPC()); ++ ++ return ret; ++} ++ ++/* fcmp_cXXX_s */ ++uint64_t helper_fcmp_c_s(CPULoongArchState *env, uint64_t fj, ++ uint64_t fk, uint32_t flags) ++{ ++ FloatRelation cmp = float32_compare_quiet((uint32_t)fj, ++ (uint32_t)fk, &env->fp_status); ++ return fcmp_common(env, cmp, flags); ++} ++ ++/* fcmp_sXXX_s */ ++uint64_t helper_fcmp_s_s(CPULoongArchState *env, uint64_t fj, ++ uint64_t fk, uint32_t flags) ++{ ++ FloatRelation cmp = float32_compare((uint32_t)fj, ++ (uint32_t)fk, &env->fp_status); ++ return fcmp_common(env, cmp, flags); ++} ++ ++/* fcmp_cXXX_d */ ++uint64_t helper_fcmp_c_d(CPULoongArchState *env, uint64_t fj, ++ uint64_t fk, uint32_t flags) ++{ ++ FloatRelation cmp = float64_compare_quiet(fj, fk, &env->fp_status); ++ return fcmp_common(env, cmp, flags); ++} ++ ++/* fcmp_sXXX_d */ ++uint64_t helper_fcmp_s_d(CPULoongArchState *env, uint64_t fj, ++ uint64_t fk, uint32_t flags) ++{ ++ FloatRelation cmp = float64_compare(fj, fk, &env->fp_status); ++ return fcmp_common(env, cmp, flags); ++} ++ ++/* floating point conversion */ ++uint64_t helper_fcvt_s_d(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ ++ fd = nanbox_s(float64_to_float32(fj, &env->fp_status)); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_fcvt_d_s(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ ++ fd = float32_to_float64((uint32_t)fj, &env->fp_status); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_ffint_s_w(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ ++ fd = nanbox_s(int32_to_float32((int32_t)fj, &env->fp_status)); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_ffint_s_l(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ ++ fd = nanbox_s(int64_to_float32(fj, &env->fp_status)); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_ffint_d_w(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ ++ fd = int32_to_float64((int32_t)fj, &env->fp_status); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_ffint_d_l(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ ++ fd = int64_to_float64(fj, &env->fp_status); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_frint_s(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ ++ fd = (uint64_t)(float32_round_to_int((uint32_t)fj, &env->fp_status)); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_frint_d(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ ++ fd = float64_round_to_int(fj, &env->fp_status); ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_ftintrm_l_d(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); ++ ++ set_float_rounding_mode(float_round_down, &env->fp_status); ++ fd = float64_to_int64(fj, &env->fp_status); ++ set_float_rounding_mode(old_mode, &env->fp_status); ++ ++ if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { ++ if (float64_is_any_nan(fj)) { ++ fd = 0; ++ } ++ } ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_ftintrm_l_s(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); ++ ++ set_float_rounding_mode(float_round_down, &env->fp_status); ++ fd = float32_to_int64((uint32_t)fj, &env->fp_status); ++ set_float_rounding_mode(old_mode, &env->fp_status); ++ ++ if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { ++ if (float32_is_any_nan((uint32_t)fj)) { ++ fd = 0; ++ } ++ } ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_ftintrm_w_d(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); ++ ++ set_float_rounding_mode(float_round_down, &env->fp_status); ++ fd = (uint64_t)float64_to_int32(fj, &env->fp_status); ++ set_float_rounding_mode(old_mode, &env->fp_status); ++ ++ if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { ++ if (float64_is_any_nan(fj)) { ++ fd = 0; ++ } ++ } ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_ftintrm_w_s(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); ++ ++ set_float_rounding_mode(float_round_down, &env->fp_status); ++ fd = (uint64_t)float32_to_int32((uint32_t)fj, &env->fp_status); ++ set_float_rounding_mode(old_mode, &env->fp_status); ++ ++ if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { ++ if (float32_is_any_nan((uint32_t)fj)) { ++ fd = 0; ++ } ++ } ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_ftintrp_l_d(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); ++ ++ set_float_rounding_mode(float_round_up, &env->fp_status); ++ fd = float64_to_int64(fj, &env->fp_status); ++ set_float_rounding_mode(old_mode, &env->fp_status); ++ ++ if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { ++ if (float64_is_any_nan(fj)) { ++ fd = 0; ++ } ++ } ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_ftintrp_l_s(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); ++ ++ set_float_rounding_mode(float_round_up, &env->fp_status); ++ fd = float32_to_int64((uint32_t)fj, &env->fp_status); ++ set_float_rounding_mode(old_mode, &env->fp_status); ++ ++ if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { ++ if (float32_is_any_nan((uint32_t)fj)) { ++ fd = 0; ++ } ++ } ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_ftintrp_w_d(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); ++ ++ set_float_rounding_mode(float_round_up, &env->fp_status); ++ fd = (uint64_t)float64_to_int32(fj, &env->fp_status); ++ set_float_rounding_mode(old_mode, &env->fp_status); ++ ++ if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { ++ if (float64_is_any_nan(fj)) { ++ fd = 0; ++ } ++ } ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_ftintrp_w_s(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); ++ ++ set_float_rounding_mode(float_round_up, &env->fp_status); ++ fd = (uint64_t)float32_to_int32((uint32_t)fj, &env->fp_status); ++ set_float_rounding_mode(old_mode, &env->fp_status); ++ ++ if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { ++ if (float32_is_any_nan((uint32_t)fj)) { ++ fd = 0; ++ } ++ } ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_ftintrz_l_d(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); ++ ++ fd = float64_to_int64_round_to_zero(fj, &env->fp_status); ++ set_float_rounding_mode(old_mode, &env->fp_status); ++ ++ if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { ++ if (float64_is_any_nan(fj)) { ++ fd = 0; ++ } ++ } ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_ftintrz_l_s(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); ++ ++ fd = float32_to_int64_round_to_zero((uint32_t)fj, &env->fp_status); ++ set_float_rounding_mode(old_mode, &env->fp_status); ++ ++ if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { ++ if (float32_is_any_nan((uint32_t)fj)) { ++ fd = 0; ++ } ++ } ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_ftintrz_w_d(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); ++ ++ fd = (uint64_t)float64_to_int32_round_to_zero(fj, &env->fp_status); ++ set_float_rounding_mode(old_mode, &env->fp_status); ++ ++ if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { ++ if (float64_is_any_nan(fj)) { ++ fd = 0; ++ } ++ } ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_ftintrz_w_s(CPULoongArchState *env, uint64_t fj) ++{ ++ uint32_t fd; ++ FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); ++ ++ fd = float32_to_int32_round_to_zero((uint32_t)fj, &env->fp_status); ++ set_float_rounding_mode(old_mode, &env->fp_status); ++ ++ if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { ++ if (float32_is_any_nan((uint32_t)fj)) { ++ fd = 0; ++ } ++ } ++ update_fcsr0(env, GETPC()); ++ return (uint64_t)fd; ++} ++ ++uint64_t helper_ftintrne_l_d(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); ++ ++ set_float_rounding_mode(float_round_nearest_even, &env->fp_status); ++ fd = float64_to_int64(fj, &env->fp_status); ++ set_float_rounding_mode(old_mode, &env->fp_status); ++ ++ if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { ++ if (float64_is_any_nan(fj)) { ++ fd = 0; ++ } ++ } ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_ftintrne_l_s(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); ++ ++ set_float_rounding_mode(float_round_nearest_even, &env->fp_status); ++ fd = float32_to_int64((uint32_t)fj, &env->fp_status); ++ set_float_rounding_mode(old_mode, &env->fp_status); ++ ++ if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { ++ if (float32_is_any_nan((uint32_t)fj)) { ++ fd = 0; ++ } ++ } ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_ftintrne_w_d(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); ++ ++ set_float_rounding_mode(float_round_nearest_even, &env->fp_status); ++ fd = (uint64_t)float64_to_int32(fj, &env->fp_status); ++ set_float_rounding_mode(old_mode, &env->fp_status); ++ ++ if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { ++ if (float64_is_any_nan(fj)) { ++ fd = 0; ++ } ++ } ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_ftintrne_w_s(CPULoongArchState *env, uint64_t fj) ++{ ++ uint32_t fd; ++ FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); ++ ++ set_float_rounding_mode(float_round_nearest_even, &env->fp_status); ++ fd = float32_to_int32((uint32_t)fj, &env->fp_status); ++ set_float_rounding_mode(old_mode, &env->fp_status); ++ ++ if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { ++ if (float32_is_any_nan((uint32_t)fj)) { ++ fd = 0; ++ } ++ } ++ update_fcsr0(env, GETPC()); ++ return (uint64_t)fd; ++} ++ ++uint64_t helper_ftint_l_d(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ ++ fd = float64_to_int64(fj, &env->fp_status); ++ if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { ++ if (float64_is_any_nan(fj)) { ++ fd = 0; ++ } ++ } ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_ftint_l_s(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ ++ fd = float32_to_int64((uint32_t)fj, &env->fp_status); ++ if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { ++ if (float32_is_any_nan((uint32_t)fj)) { ++ fd = 0; ++ } ++ } ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_ftint_w_s(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ ++ fd = (uint64_t)float32_to_int32((uint32_t)fj, &env->fp_status); ++ if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { ++ if (float32_is_any_nan((uint32_t)fj)) { ++ fd = 0; ++ } ++ } ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++uint64_t helper_ftint_w_d(CPULoongArchState *env, uint64_t fj) ++{ ++ uint64_t fd; ++ ++ fd = (uint64_t)float64_to_int32(fj, &env->fp_status); ++ if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { ++ if (float64_is_any_nan(fj)) { ++ fd = 0; ++ } ++ } ++ update_fcsr0(env, GETPC()); ++ return fd; ++} ++ ++void helper_set_rounding_mode(CPULoongArchState *env) ++{ ++ set_float_rounding_mode(ieee_rm[(env->fcsr0 >> FCSR0_RM) & 0x3], ++ &env->fp_status); ++} +diff --git a/target/loongarch/tcg/insn_trans/trans_arith.c.inc b/target/loongarch/tcg/insn_trans/trans_arith.c.inc +new file mode 100644 +index 0000000..2be057e +--- /dev/null ++++ b/target/loongarch/tcg/insn_trans/trans_arith.c.inc +@@ -0,0 +1,304 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (c) 2021 Loongson Technology Corporation Limited ++ */ ++ ++static bool gen_rrr(DisasContext *ctx, arg_rrr *a, ++ DisasExtend src1_ext, DisasExtend src2_ext, ++ DisasExtend dst_ext, void (*func)(TCGv, TCGv, TCGv)) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, dst_ext); ++ TCGv src1 = gpr_src(ctx, a->rj, src1_ext); ++ TCGv src2 = gpr_src(ctx, a->rk, src2_ext); ++ ++ func(dest, src1, src2); ++ gen_set_gpr(a->rd, dest, dst_ext); ++ ++ return true; ++} ++ ++static bool gen_rri_v(DisasContext *ctx, arg_rr_i *a, ++ DisasExtend src_ext, DisasExtend dst_ext, ++ void (*func)(TCGv, TCGv, TCGv)) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, dst_ext); ++ TCGv src1 = gpr_src(ctx, a->rj, src_ext); ++ TCGv src2 = tcg_constant_tl(a->imm); ++ ++ func(dest, src1, src2); ++ gen_set_gpr(a->rd, dest, dst_ext); ++ ++ return true; ++} ++ ++static bool gen_rri_c(DisasContext *ctx, arg_rr_i *a, ++ DisasExtend src_ext, DisasExtend dst_ext, ++ void (*func)(TCGv, TCGv, target_long)) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, dst_ext); ++ TCGv src1 = gpr_src(ctx, a->rj, src_ext); ++ ++ func(dest, src1, a->imm); ++ gen_set_gpr(a->rd, dest, dst_ext); ++ ++ return true; ++} ++ ++static bool gen_rrr_sa(DisasContext *ctx, arg_rrr_sa *a, ++ DisasExtend src_ext, DisasExtend dst_ext, ++ void (*func)(TCGv, TCGv, TCGv, target_long)) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, dst_ext); ++ TCGv src1 = gpr_src(ctx, a->rj, src_ext); ++ TCGv src2 = gpr_src(ctx, a->rk, src_ext); ++ ++ func(dest, src1, src2, a->sa); ++ gen_set_gpr(a->rd, dest, dst_ext); ++ ++ return true; ++} ++ ++static bool trans_lu12i_w(DisasContext *ctx, arg_lu12i_w *a) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ ++ tcg_gen_movi_tl(dest, a->imm << 12); ++ gen_set_gpr(a->rd, dest, EXT_NONE); ++ ++ return true; ++} ++ ++static bool gen_pc(DisasContext *ctx, arg_r_i *a, ++ target_ulong (*func)(target_ulong, int)) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ target_ulong addr = make_address_pc(ctx, func(ctx->base.pc_next, a->imm)); ++ ++ tcg_gen_movi_tl(dest, addr); ++ gen_set_gpr(a->rd, dest, EXT_NONE); ++ ++ return true; ++} ++ ++static void gen_slt(TCGv dest, TCGv src1, TCGv src2) ++{ ++ tcg_gen_setcond_tl(TCG_COND_LT, dest, src1, src2); ++} ++ ++static void gen_sltu(TCGv dest, TCGv src1, TCGv src2) ++{ ++ tcg_gen_setcond_tl(TCG_COND_LTU, dest, src1, src2); ++} ++ ++static void gen_mulh_w(TCGv dest, TCGv src1, TCGv src2) ++{ ++ tcg_gen_mul_i64(dest, src1, src2); ++ tcg_gen_sari_i64(dest, dest, 32); ++} ++ ++static void gen_mulh_d(TCGv dest, TCGv src1, TCGv src2) ++{ ++ TCGv discard = tcg_temp_new(); ++ tcg_gen_muls2_tl(discard, dest, src1, src2); ++} ++ ++static void gen_mulh_du(TCGv dest, TCGv src1, TCGv src2) ++{ ++ TCGv discard = tcg_temp_new(); ++ tcg_gen_mulu2_tl(discard, dest, src1, src2); ++} ++ ++static void prep_divisor_d(TCGv ret, TCGv src1, TCGv src2) ++{ ++ TCGv t0 = tcg_temp_new(); ++ TCGv t1 = tcg_temp_new(); ++ TCGv zero = tcg_constant_tl(0); ++ ++ /* ++ * If min / -1, set the divisor to 1. ++ * This avoids potential host overflow trap and produces min. ++ * If x / 0, set the divisor to 1. ++ * This avoids potential host overflow trap; ++ * the required result is undefined. ++ */ ++ tcg_gen_setcondi_tl(TCG_COND_EQ, ret, src1, INT64_MIN); ++ tcg_gen_setcondi_tl(TCG_COND_EQ, t0, src2, -1); ++ tcg_gen_setcondi_tl(TCG_COND_EQ, t1, src2, 0); ++ tcg_gen_and_tl(ret, ret, t0); ++ tcg_gen_or_tl(ret, ret, t1); ++ tcg_gen_movcond_tl(TCG_COND_NE, ret, ret, zero, ret, src2); ++} ++ ++static void prep_divisor_du(TCGv ret, TCGv src2) ++{ ++ TCGv zero = tcg_constant_tl(0); ++ TCGv one = tcg_constant_tl(1); ++ ++ /* ++ * If x / 0, set the divisor to 1. ++ * This avoids potential host overflow trap; ++ * the required result is undefined. ++ */ ++ tcg_gen_movcond_tl(TCG_COND_EQ, ret, src2, zero, one, src2); ++} ++ ++static void gen_div_d(TCGv dest, TCGv src1, TCGv src2) ++{ ++ TCGv t0 = tcg_temp_new(); ++ prep_divisor_d(t0, src1, src2); ++ tcg_gen_div_tl(dest, src1, t0); ++} ++ ++static void gen_rem_d(TCGv dest, TCGv src1, TCGv src2) ++{ ++ TCGv t0 = tcg_temp_new(); ++ prep_divisor_d(t0, src1, src2); ++ tcg_gen_rem_tl(dest, src1, t0); ++} ++ ++static void gen_div_du(TCGv dest, TCGv src1, TCGv src2) ++{ ++ TCGv t0 = tcg_temp_new(); ++ prep_divisor_du(t0, src2); ++ tcg_gen_divu_tl(dest, src1, t0); ++} ++ ++static void gen_rem_du(TCGv dest, TCGv src1, TCGv src2) ++{ ++ TCGv t0 = tcg_temp_new(); ++ prep_divisor_du(t0, src2); ++ tcg_gen_remu_tl(dest, src1, t0); ++} ++ ++static void gen_div_w(TCGv dest, TCGv src1, TCGv src2) ++{ ++ TCGv t0 = tcg_temp_new(); ++ /* We need not check for integer overflow for div_w. */ ++ prep_divisor_du(t0, src2); ++ tcg_gen_div_tl(dest, src1, t0); ++} ++ ++static void gen_rem_w(TCGv dest, TCGv src1, TCGv src2) ++{ ++ TCGv t0 = tcg_temp_new(); ++ /* We need not check for integer overflow for rem_w. */ ++ prep_divisor_du(t0, src2); ++ tcg_gen_rem_tl(dest, src1, t0); ++} ++ ++static void gen_alsl(TCGv dest, TCGv src1, TCGv src2, target_long sa) ++{ ++ TCGv t0 = tcg_temp_new(); ++ tcg_gen_shli_tl(t0, src1, sa); ++ tcg_gen_add_tl(dest, t0, src2); ++} ++ ++static bool trans_lu32i_d(DisasContext *ctx, arg_lu32i_d *a) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ TCGv src1 = gpr_src(ctx, a->rd, EXT_NONE); ++ TCGv src2 = tcg_constant_tl(a->imm); ++ ++ if (!avail_64(ctx)) { ++ return false; ++ } ++ ++ tcg_gen_deposit_tl(dest, src1, src2, 32, 32); ++ gen_set_gpr(a->rd, dest, EXT_NONE); ++ ++ return true; ++} ++ ++static bool trans_lu52i_d(DisasContext *ctx, arg_lu52i_d *a) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv src2 = tcg_constant_tl(a->imm); ++ ++ if (!avail_64(ctx)) { ++ return false; ++ } ++ ++ tcg_gen_deposit_tl(dest, src1, src2, 52, 12); ++ gen_set_gpr(a->rd, dest, EXT_NONE); ++ ++ return true; ++} ++ ++static target_ulong gen_pcaddi(target_ulong pc, int imm) ++{ ++ return pc + (imm << 2); ++} ++ ++static target_ulong gen_pcalau12i(target_ulong pc, int imm) ++{ ++ return (pc + (imm << 12)) & ~0xfff; ++} ++ ++static target_ulong gen_pcaddu12i(target_ulong pc, int imm) ++{ ++ return pc + (imm << 12); ++} ++ ++static target_ulong gen_pcaddu18i(target_ulong pc, int imm) ++{ ++ return pc + ((target_ulong)(imm) << 18); ++} ++ ++static bool trans_addu16i_d(DisasContext *ctx, arg_addu16i_d *a) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ ++ if (!avail_64(ctx)) { ++ return false; ++ } ++ ++ tcg_gen_addi_tl(dest, src1, a->imm << 16); ++ gen_set_gpr(a->rd, dest, EXT_NONE); ++ ++ return true; ++} ++ ++TRANS(add_w, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_SIGN, tcg_gen_add_tl) ++TRANS(add_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_add_tl) ++TRANS(sub_w, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_SIGN, tcg_gen_sub_tl) ++TRANS(sub_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_sub_tl) ++TRANS(and, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_and_tl) ++TRANS(or, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_or_tl) ++TRANS(xor, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_xor_tl) ++TRANS(nor, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_nor_tl) ++TRANS(andn, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_andc_tl) ++TRANS(orn, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_orc_tl) ++TRANS(slt, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_slt) ++TRANS(sltu, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_sltu) ++TRANS(mul_w, ALL, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_SIGN, tcg_gen_mul_tl) ++TRANS(mul_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, tcg_gen_mul_tl) ++TRANS(mulh_w, ALL, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_NONE, gen_mulh_w) ++TRANS(mulh_wu, ALL, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_NONE, gen_mulh_w) ++TRANS(mulh_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_mulh_d) ++TRANS(mulh_du, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_mulh_du) ++TRANS(mulw_d_w, 64, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_NONE, tcg_gen_mul_tl) ++TRANS(mulw_d_wu, 64, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_NONE, tcg_gen_mul_tl) ++TRANS(div_w, ALL, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_SIGN, gen_div_w) ++TRANS(mod_w, ALL, gen_rrr, EXT_SIGN, EXT_SIGN, EXT_SIGN, gen_rem_w) ++TRANS(div_wu, ALL, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_SIGN, gen_div_du) ++TRANS(mod_wu, ALL, gen_rrr, EXT_ZERO, EXT_ZERO, EXT_SIGN, gen_rem_du) ++TRANS(div_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_div_d) ++TRANS(mod_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_rem_d) ++TRANS(div_du, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_div_du) ++TRANS(mod_du, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_rem_du) ++TRANS(slti, ALL, gen_rri_v, EXT_NONE, EXT_NONE, gen_slt) ++TRANS(sltui, ALL, gen_rri_v, EXT_NONE, EXT_NONE, gen_sltu) ++TRANS(addi_w, ALL, gen_rri_c, EXT_NONE, EXT_SIGN, tcg_gen_addi_tl) ++TRANS(addi_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_addi_tl) ++TRANS(alsl_w, ALL, gen_rrr_sa, EXT_NONE, EXT_SIGN, gen_alsl) ++TRANS(alsl_wu, 64, gen_rrr_sa, EXT_NONE, EXT_ZERO, gen_alsl) ++TRANS(alsl_d, 64, gen_rrr_sa, EXT_NONE, EXT_NONE, gen_alsl) ++TRANS(pcaddi, ALL, gen_pc, gen_pcaddi) ++TRANS(pcalau12i, ALL, gen_pc, gen_pcalau12i) ++TRANS(pcaddu12i, ALL, gen_pc, gen_pcaddu12i) ++TRANS(pcaddu18i, 64, gen_pc, gen_pcaddu18i) ++TRANS(andi, ALL, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_andi_tl) ++TRANS(ori, ALL, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_ori_tl) ++TRANS(xori, ALL, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_xori_tl) +diff --git a/target/loongarch/tcg/insn_trans/trans_atomic.c.inc b/target/loongarch/tcg/insn_trans/trans_atomic.c.inc +new file mode 100644 +index 0000000..80c2e28 +--- /dev/null ++++ b/target/loongarch/tcg/insn_trans/trans_atomic.c.inc +@@ -0,0 +1,111 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (c) 2021 Loongson Technology Corporation Limited ++ */ ++ ++static bool gen_ll(DisasContext *ctx, arg_rr_i *a, MemOp mop) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv t0 = make_address_i(ctx, src1, a->imm); ++ ++ tcg_gen_qemu_ld_i64(dest, t0, ctx->mem_idx, mop); ++ tcg_gen_st_tl(t0, tcg_env, offsetof(CPULoongArchState, lladdr)); ++ tcg_gen_st_tl(dest, tcg_env, offsetof(CPULoongArchState, llval)); ++ gen_set_gpr(a->rd, dest, EXT_NONE); ++ ++ return true; ++} ++ ++static bool gen_sc(DisasContext *ctx, arg_rr_i *a, MemOp mop) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv src2 = gpr_src(ctx, a->rd, EXT_NONE); ++ TCGv t0 = tcg_temp_new(); ++ TCGv val = tcg_temp_new(); ++ ++ TCGLabel *l1 = gen_new_label(); ++ TCGLabel *done = gen_new_label(); ++ ++ tcg_gen_addi_tl(t0, src1, a->imm); ++ tcg_gen_brcond_tl(TCG_COND_EQ, t0, cpu_lladdr, l1); ++ tcg_gen_movi_tl(dest, 0); ++ tcg_gen_br(done); ++ ++ gen_set_label(l1); ++ tcg_gen_mov_tl(val, src2); ++ /* generate cmpxchg */ ++ tcg_gen_atomic_cmpxchg_tl(t0, cpu_lladdr, cpu_llval, ++ val, ctx->mem_idx, mop); ++ tcg_gen_setcond_tl(TCG_COND_EQ, dest, t0, cpu_llval); ++ gen_set_label(done); ++ gen_set_gpr(a->rd, dest, EXT_NONE); ++ ++ return true; ++} ++ ++static bool gen_am(DisasContext *ctx, arg_rrr *a, ++ void (*func)(TCGv, TCGv, TCGv, TCGArg, MemOp), ++ MemOp mop) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv val = gpr_src(ctx, a->rk, EXT_NONE); ++ ++ if (a->rd != 0 && (a->rj == a->rd || a->rk == a->rd)) { ++ qemu_log_mask(LOG_GUEST_ERROR, ++ "Warning: source register overlaps destination register" ++ "in atomic insn at pc=0x" TARGET_FMT_lx "\n", ++ ctx->base.pc_next - 4); ++ return false; ++ } ++ ++ addr = make_address_i(ctx, addr, 0); ++ ++ func(dest, addr, val, ctx->mem_idx, mop); ++ gen_set_gpr(a->rd, dest, EXT_NONE); ++ ++ return true; ++} ++ ++TRANS(ll_w, ALL, gen_ll, MO_TESL) ++TRANS(sc_w, ALL, gen_sc, MO_TESL) ++TRANS(ll_d, 64, gen_ll, MO_TEUQ) ++TRANS(sc_d, 64, gen_sc, MO_TEUQ) ++TRANS(amswap_w, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TESL) ++TRANS(amswap_d, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TEUQ) ++TRANS(amadd_w, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TESL) ++TRANS(amadd_d, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TEUQ) ++TRANS(amand_w, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TESL) ++TRANS(amand_d, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TEUQ) ++TRANS(amor_w, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TESL) ++TRANS(amor_d, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TEUQ) ++TRANS(amxor_w, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TESL) ++TRANS(amxor_d, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TEUQ) ++TRANS(ammax_w, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TESL) ++TRANS(ammax_d, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TEUQ) ++TRANS(ammin_w, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TESL) ++TRANS(ammin_d, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TEUQ) ++TRANS(ammax_wu, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TESL) ++TRANS(ammax_du, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TEUQ) ++TRANS(ammin_wu, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TESL) ++TRANS(ammin_du, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TEUQ) ++TRANS(amswap_db_w, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TESL) ++TRANS(amswap_db_d, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TEUQ) ++TRANS(amadd_db_w, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TESL) ++TRANS(amadd_db_d, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TEUQ) ++TRANS(amand_db_w, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TESL) ++TRANS(amand_db_d, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TEUQ) ++TRANS(amor_db_w, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TESL) ++TRANS(amor_db_d, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TEUQ) ++TRANS(amxor_db_w, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TESL) ++TRANS(amxor_db_d, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TEUQ) ++TRANS(ammax_db_w, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TESL) ++TRANS(ammax_db_d, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TEUQ) ++TRANS(ammin_db_w, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TESL) ++TRANS(ammin_db_d, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TEUQ) ++TRANS(ammax_db_wu, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TESL) ++TRANS(ammax_db_du, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TEUQ) ++TRANS(ammin_db_wu, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TESL) ++TRANS(ammin_db_du, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TEUQ) +diff --git a/target/loongarch/tcg/insn_trans/trans_bit.c.inc b/target/loongarch/tcg/insn_trans/trans_bit.c.inc +new file mode 100644 +index 0000000..ee5fa00 +--- /dev/null ++++ b/target/loongarch/tcg/insn_trans/trans_bit.c.inc +@@ -0,0 +1,208 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (c) 2021 Loongson Technology Corporation Limited ++ */ ++ ++static bool gen_rr(DisasContext *ctx, arg_rr *a, ++ DisasExtend src_ext, DisasExtend dst_ext, ++ void (*func)(TCGv, TCGv)) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, dst_ext); ++ TCGv src1 = gpr_src(ctx, a->rj, src_ext); ++ ++ func(dest, src1); ++ gen_set_gpr(a->rd, dest, dst_ext); ++ ++ return true; ++} ++ ++static void gen_bytepick_w(TCGv dest, TCGv src1, TCGv src2, target_long sa) ++{ ++ tcg_gen_concat_tl_i64(dest, src1, src2); ++ tcg_gen_sextract_i64(dest, dest, (32 - sa * 8), 32); ++} ++ ++static void gen_bytepick_d(TCGv dest, TCGv src1, TCGv src2, target_long sa) ++{ ++ tcg_gen_extract2_i64(dest, src1, src2, (64 - sa * 8)); ++} ++ ++static bool gen_bstrins(DisasContext *ctx, arg_rr_ms_ls *a, ++ DisasExtend dst_ext) ++{ ++ TCGv src1 = gpr_src(ctx, a->rd, EXT_NONE); ++ TCGv src2 = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ ++ if (a->ls > a->ms) { ++ return false; ++ } ++ ++ tcg_gen_deposit_tl(dest, src1, src2, a->ls, a->ms - a->ls + 1); ++ gen_set_gpr(a->rd, dest, dst_ext); ++ return true; ++} ++ ++static bool gen_bstrpick(DisasContext *ctx, arg_rr_ms_ls *a, ++ DisasExtend dst_ext) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ ++ if (a->ls > a->ms) { ++ return false; ++ } ++ ++ tcg_gen_extract_tl(dest, src1, a->ls, a->ms - a->ls + 1); ++ gen_set_gpr(a->rd, dest, dst_ext); ++ return true; ++} ++ ++static void gen_clz_w(TCGv dest, TCGv src1) ++{ ++ tcg_gen_clzi_tl(dest, src1, TARGET_LONG_BITS); ++ tcg_gen_subi_tl(dest, dest, TARGET_LONG_BITS - 32); ++} ++ ++static void gen_clo_w(TCGv dest, TCGv src1) ++{ ++ tcg_gen_not_tl(dest, src1); ++ tcg_gen_ext32u_tl(dest, dest); ++ gen_clz_w(dest, dest); ++} ++ ++static void gen_ctz_w(TCGv dest, TCGv src1) ++{ ++ tcg_gen_ori_tl(dest, src1, (target_ulong)MAKE_64BIT_MASK(32, 32)); ++ tcg_gen_ctzi_tl(dest, dest, TARGET_LONG_BITS); ++} ++ ++static void gen_cto_w(TCGv dest, TCGv src1) ++{ ++ tcg_gen_not_tl(dest, src1); ++ gen_ctz_w(dest, dest); ++} ++ ++static void gen_clz_d(TCGv dest, TCGv src1) ++{ ++ tcg_gen_clzi_i64(dest, src1, TARGET_LONG_BITS); ++} ++ ++static void gen_clo_d(TCGv dest, TCGv src1) ++{ ++ tcg_gen_not_tl(dest, src1); ++ gen_clz_d(dest, dest); ++} ++ ++static void gen_ctz_d(TCGv dest, TCGv src1) ++{ ++ tcg_gen_ctzi_tl(dest, src1, TARGET_LONG_BITS); ++} ++ ++static void gen_cto_d(TCGv dest, TCGv src1) ++{ ++ tcg_gen_not_tl(dest, src1); ++ gen_ctz_d(dest, dest); ++} ++ ++static void gen_revb_2w(TCGv dest, TCGv src1) ++{ ++ tcg_gen_bswap64_i64(dest, src1); ++ tcg_gen_rotri_i64(dest, dest, 32); ++} ++ ++static void gen_revb_2h(TCGv dest, TCGv src1) ++{ ++ TCGv mask = tcg_constant_tl(0x00FF00FF); ++ TCGv t0 = tcg_temp_new(); ++ TCGv t1 = tcg_temp_new(); ++ ++ tcg_gen_shri_tl(t0, src1, 8); ++ tcg_gen_and_tl(t0, t0, mask); ++ tcg_gen_and_tl(t1, src1, mask); ++ tcg_gen_shli_tl(t1, t1, 8); ++ tcg_gen_or_tl(dest, t0, t1); ++} ++ ++static void gen_revb_4h(TCGv dest, TCGv src1) ++{ ++ TCGv mask = tcg_constant_tl(0x00FF00FF00FF00FFULL); ++ TCGv t0 = tcg_temp_new(); ++ TCGv t1 = tcg_temp_new(); ++ ++ tcg_gen_shri_tl(t0, src1, 8); ++ tcg_gen_and_tl(t0, t0, mask); ++ tcg_gen_and_tl(t1, src1, mask); ++ tcg_gen_shli_tl(t1, t1, 8); ++ tcg_gen_or_tl(dest, t0, t1); ++} ++ ++static void gen_revh_2w(TCGv dest, TCGv src1) ++{ ++ TCGv_i64 t0 = tcg_temp_new_i64(); ++ TCGv_i64 t1 = tcg_temp_new_i64(); ++ TCGv_i64 mask = tcg_constant_i64(0x0000ffff0000ffffull); ++ ++ tcg_gen_shri_i64(t0, src1, 16); ++ tcg_gen_and_i64(t1, src1, mask); ++ tcg_gen_and_i64(t0, t0, mask); ++ tcg_gen_shli_i64(t1, t1, 16); ++ tcg_gen_or_i64(dest, t1, t0); ++} ++ ++static void gen_revh_d(TCGv dest, TCGv src1) ++{ ++ TCGv t0 = tcg_temp_new(); ++ TCGv t1 = tcg_temp_new(); ++ TCGv mask = tcg_constant_tl(0x0000FFFF0000FFFFULL); ++ ++ tcg_gen_shri_tl(t1, src1, 16); ++ tcg_gen_and_tl(t1, t1, mask); ++ tcg_gen_and_tl(t0, src1, mask); ++ tcg_gen_shli_tl(t0, t0, 16); ++ tcg_gen_or_tl(t0, t0, t1); ++ tcg_gen_rotri_tl(dest, t0, 32); ++} ++ ++static void gen_maskeqz(TCGv dest, TCGv src1, TCGv src2) ++{ ++ TCGv zero = tcg_constant_tl(0); ++ ++ tcg_gen_movcond_tl(TCG_COND_EQ, dest, src2, zero, zero, src1); ++} ++ ++static void gen_masknez(TCGv dest, TCGv src1, TCGv src2) ++{ ++ TCGv zero = tcg_constant_tl(0); ++ ++ tcg_gen_movcond_tl(TCG_COND_NE, dest, src2, zero, zero, src1); ++} ++ ++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) ++TRANS(clz_w, ALL, gen_rr, EXT_ZERO, EXT_NONE, gen_clz_w) ++TRANS(cto_w, ALL, gen_rr, EXT_NONE, EXT_NONE, gen_cto_w) ++TRANS(ctz_w, ALL, gen_rr, EXT_NONE, EXT_NONE, gen_ctz_w) ++TRANS(clo_d, 64, gen_rr, EXT_NONE, EXT_NONE, gen_clo_d) ++TRANS(clz_d, 64, gen_rr, EXT_NONE, EXT_NONE, gen_clz_d) ++TRANS(cto_d, 64, gen_rr, EXT_NONE, EXT_NONE, gen_cto_d) ++TRANS(ctz_d, 64, gen_rr, EXT_NONE, EXT_NONE, gen_ctz_d) ++TRANS(revb_2h, ALL, gen_rr, EXT_NONE, EXT_SIGN, gen_revb_2h) ++TRANS(revb_4h, 64, gen_rr, EXT_NONE, EXT_NONE, gen_revb_4h) ++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(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) ++TRANS(bytepick_d, 64, gen_rrr_sa, EXT_NONE, EXT_NONE, gen_bytepick_d) ++TRANS(bstrins_w, ALL, gen_bstrins, EXT_SIGN) ++TRANS(bstrins_d, 64, gen_bstrins, EXT_NONE) ++TRANS(bstrpick_w, ALL, gen_bstrpick, EXT_SIGN) ++TRANS(bstrpick_d, 64, gen_bstrpick, EXT_NONE) +diff --git a/target/loongarch/tcg/insn_trans/trans_branch.c.inc b/target/loongarch/tcg/insn_trans/trans_branch.c.inc +new file mode 100644 +index 0000000..221e515 +--- /dev/null ++++ b/target/loongarch/tcg/insn_trans/trans_branch.c.inc +@@ -0,0 +1,84 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (c) 2021 Loongson Technology Corporation Limited ++ */ ++ ++static bool trans_b(DisasContext *ctx, arg_b *a) ++{ ++ gen_goto_tb(ctx, 0, ctx->base.pc_next + a->offs); ++ ctx->base.is_jmp = DISAS_NORETURN; ++ return true; ++} ++ ++static bool trans_bl(DisasContext *ctx, arg_bl *a) ++{ ++ tcg_gen_movi_tl(cpu_gpr[1], make_address_pc(ctx, ctx->base.pc_next + 4)); ++ gen_goto_tb(ctx, 0, ctx->base.pc_next + a->offs); ++ ctx->base.is_jmp = DISAS_NORETURN; ++ return true; ++} ++ ++static bool trans_jirl(DisasContext *ctx, arg_jirl *a) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ ++ TCGv addr = make_address_i(ctx, src1, a->imm); ++ tcg_gen_mov_tl(cpu_pc, addr); ++ tcg_gen_movi_tl(dest, make_address_pc(ctx, ctx->base.pc_next + 4)); ++ gen_set_gpr(a->rd, dest, EXT_NONE); ++ tcg_gen_lookup_and_goto_ptr(); ++ ctx->base.is_jmp = DISAS_NORETURN; ++ return true; ++} ++ ++static void gen_bc(DisasContext *ctx, TCGv src1, TCGv src2, ++ target_long offs, TCGCond cond) ++{ ++ TCGLabel *l = gen_new_label(); ++ tcg_gen_brcond_tl(cond, src1, src2, l); ++ gen_goto_tb(ctx, 1, ctx->base.pc_next + 4); ++ gen_set_label(l); ++ gen_goto_tb(ctx, 0, ctx->base.pc_next + offs); ++ ctx->base.is_jmp = DISAS_NORETURN; ++} ++ ++static bool gen_rr_bc(DisasContext *ctx, arg_rr_offs *a, TCGCond cond) ++{ ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv src2 = gpr_src(ctx, a->rd, EXT_NONE); ++ ++ gen_bc(ctx, src1, src2, a->offs, cond); ++ return true; ++} ++ ++static bool gen_rz_bc(DisasContext *ctx, arg_r_offs *a, TCGCond cond) ++{ ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv src2 = tcg_constant_tl(0); ++ ++ gen_bc(ctx, src1, src2, a->offs, cond); ++ return true; ++} ++ ++static bool gen_cz_bc(DisasContext *ctx, arg_c_offs *a, TCGCond cond) ++{ ++ TCGv src1 = tcg_temp_new(); ++ TCGv src2 = tcg_constant_tl(0); ++ ++ tcg_gen_ld8u_tl(src1, tcg_env, ++ offsetof(CPULoongArchState, cf[a->cj])); ++ gen_bc(ctx, src1, src2, a->offs, cond); ++ return true; ++} ++ ++TRANS(beq, ALL, gen_rr_bc, TCG_COND_EQ) ++TRANS(bne, ALL, gen_rr_bc, TCG_COND_NE) ++TRANS(blt, ALL, gen_rr_bc, TCG_COND_LT) ++TRANS(bge, ALL, gen_rr_bc, TCG_COND_GE) ++TRANS(bltu, ALL, gen_rr_bc, TCG_COND_LTU) ++TRANS(bgeu, ALL, gen_rr_bc, TCG_COND_GEU) ++TRANS(beqz, ALL, gen_rz_bc, TCG_COND_EQ) ++TRANS(bnez, ALL, gen_rz_bc, TCG_COND_NE) ++TRANS(bceqz, 64, gen_cz_bc, TCG_COND_EQ) ++TRANS(bcnez, 64, gen_cz_bc, TCG_COND_NE) +diff --git a/target/loongarch/tcg/insn_trans/trans_extra.c.inc b/target/loongarch/tcg/insn_trans/trans_extra.c.inc +new file mode 100644 +index 0000000..cfa361f +--- /dev/null ++++ b/target/loongarch/tcg/insn_trans/trans_extra.c.inc +@@ -0,0 +1,107 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (c) 2021 Loongson Technology Corporation Limited ++ */ ++ ++static bool trans_break(DisasContext *ctx, arg_break *a) ++{ ++ generate_exception(ctx, EXCCODE_BRK); ++ return true; ++} ++ ++static bool trans_syscall(DisasContext *ctx, arg_syscall *a) ++{ ++ generate_exception(ctx, EXCCODE_SYS); ++ return true; ++} ++ ++static bool trans_asrtle_d(DisasContext *ctx, arg_asrtle_d * a) ++{ ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); ++ ++ if (!avail_64(ctx)) { ++ return false; ++ } ++ ++ gen_helper_asrtle_d(tcg_env, src1, src2); ++ return true; ++} ++ ++static bool trans_asrtgt_d(DisasContext *ctx, arg_asrtgt_d * a) ++{ ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); ++ ++ if (!avail_64(ctx)) { ++ return false; ++ } ++ ++ gen_helper_asrtgt_d(tcg_env, src1, src2); ++ return true; ++} ++ ++static bool gen_rdtime(DisasContext *ctx, arg_rr *a, ++ bool word, bool high) ++{ ++ TCGv dst1 = gpr_dst(ctx, a->rd, EXT_NONE); ++ TCGv dst2 = gpr_dst(ctx, a->rj, EXT_NONE); ++ ++ translator_io_start(&ctx->base); ++ gen_helper_rdtime_d(dst1, tcg_env); ++ if (word) { ++ tcg_gen_sextract_tl(dst1, dst1, high ? 32 : 0, 32); ++ } ++ tcg_gen_ld_i64(dst2, tcg_env, offsetof(CPULoongArchState, CSR_TID)); ++ ++ return true; ++} ++ ++static bool trans_rdtimel_w(DisasContext *ctx, arg_rdtimel_w *a) ++{ ++ return gen_rdtime(ctx, a, 1, 0); ++} ++ ++static bool trans_rdtimeh_w(DisasContext *ctx, arg_rdtimeh_w *a) ++{ ++ return gen_rdtime(ctx, a, 1, 1); ++} ++ ++static bool trans_rdtime_d(DisasContext *ctx, arg_rdtime_d *a) ++{ ++ return gen_rdtime(ctx, a, 0, 0); ++} ++ ++static bool trans_cpucfg(DisasContext *ctx, arg_cpucfg *a) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ ++ gen_helper_cpucfg(dest, tcg_env, src1); ++ gen_set_gpr(a->rd, dest, EXT_NONE); ++ ++ return true; ++} ++ ++static bool gen_crc(DisasContext *ctx, arg_rrr *a, ++ void (*func)(TCGv, TCGv, TCGv, TCGv), ++ TCGv tsz) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, EXT_SIGN); ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); ++ ++ func(dest, src2, src1, tsz); ++ gen_set_gpr(a->rd, dest, EXT_SIGN); ++ ++ return true; ++} ++ ++TRANS(crc_w_b_w, 64, gen_crc, gen_helper_crc32, tcg_constant_tl(1)) ++TRANS(crc_w_h_w, 64, gen_crc, gen_helper_crc32, tcg_constant_tl(2)) ++TRANS(crc_w_w_w, 64, gen_crc, gen_helper_crc32, tcg_constant_tl(4)) ++TRANS(crc_w_d_w, 64, gen_crc, gen_helper_crc32, tcg_constant_tl(8)) ++TRANS(crcc_w_b_w, 64, gen_crc, gen_helper_crc32c, tcg_constant_tl(1)) ++TRANS(crcc_w_h_w, 64, gen_crc, gen_helper_crc32c, tcg_constant_tl(2)) ++TRANS(crcc_w_w_w, 64, gen_crc, gen_helper_crc32c, tcg_constant_tl(4)) ++TRANS(crcc_w_d_w, 64, gen_crc, gen_helper_crc32c, tcg_constant_tl(8)) +diff --git a/target/loongarch/tcg/insn_trans/trans_farith.c.inc b/target/loongarch/tcg/insn_trans/trans_farith.c.inc +new file mode 100644 +index 0000000..f4a0dea +--- /dev/null ++++ b/target/loongarch/tcg/insn_trans/trans_farith.c.inc +@@ -0,0 +1,207 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (c) 2021 Loongson Technology Corporation Limited ++ */ ++ ++#ifndef CONFIG_USER_ONLY ++#define CHECK_FPE do { \ ++ if ((ctx->base.tb->flags & HW_FLAGS_EUEN_FPE) == 0) { \ ++ generate_exception(ctx, EXCCODE_FPD); \ ++ return true; \ ++ } \ ++} while (0) ++#else ++#define CHECK_FPE ++#endif ++ ++static bool gen_fff(DisasContext *ctx, arg_fff *a, ++ void (*func)(TCGv, TCGv_env, TCGv, TCGv)) ++{ ++ TCGv dest = get_fpr(ctx, a->fd); ++ TCGv src1 = get_fpr(ctx, a->fj); ++ TCGv src2 = get_fpr(ctx, a->fk); ++ ++ CHECK_FPE; ++ ++ func(dest, tcg_env, src1, src2); ++ set_fpr(a->fd, dest); ++ ++ return true; ++} ++ ++static bool gen_ff(DisasContext *ctx, arg_ff *a, ++ void (*func)(TCGv, TCGv_env, TCGv)) ++{ ++ TCGv dest = get_fpr(ctx, a->fd); ++ TCGv src = get_fpr(ctx, a->fj); ++ ++ CHECK_FPE; ++ ++ func(dest, tcg_env, src); ++ set_fpr(a->fd, dest); ++ ++ return true; ++} ++ ++static bool gen_muladd(DisasContext *ctx, arg_ffff *a, ++ void (*func)(TCGv, TCGv_env, TCGv, TCGv, TCGv, TCGv_i32), ++ int flag) ++{ ++ TCGv_i32 tflag = tcg_constant_i32(flag); ++ TCGv dest = get_fpr(ctx, a->fd); ++ TCGv src1 = get_fpr(ctx, a->fj); ++ TCGv src2 = get_fpr(ctx, a->fk); ++ TCGv src3 = get_fpr(ctx, a->fa); ++ ++ CHECK_FPE; ++ ++ func(dest, tcg_env, src1, src2, src3, tflag); ++ set_fpr(a->fd, dest); ++ ++ return true; ++} ++ ++static bool trans_fcopysign_s(DisasContext *ctx, arg_fcopysign_s *a) ++{ ++ TCGv dest = get_fpr(ctx, a->fd); ++ TCGv src1 = get_fpr(ctx, a->fk); ++ TCGv src2 = get_fpr(ctx, a->fj); ++ ++ if (!avail_FP_SP(ctx)) { ++ return false; ++ } ++ ++ CHECK_FPE; ++ ++ tcg_gen_deposit_i64(dest, src1, src2, 0, 31); ++ set_fpr(a->fd, dest); ++ ++ return true; ++} ++ ++static bool trans_fcopysign_d(DisasContext *ctx, arg_fcopysign_d *a) ++{ ++ TCGv dest = get_fpr(ctx, a->fd); ++ TCGv src1 = get_fpr(ctx, a->fk); ++ TCGv src2 = get_fpr(ctx, a->fj); ++ ++ if (!avail_FP_DP(ctx)) { ++ return false; ++ } ++ ++ CHECK_FPE; ++ ++ tcg_gen_deposit_i64(dest, src1, src2, 0, 63); ++ set_fpr(a->fd, dest); ++ ++ return true; ++} ++ ++static bool trans_fabs_s(DisasContext *ctx, arg_fabs_s *a) ++{ ++ TCGv dest = get_fpr(ctx, a->fd); ++ TCGv src = get_fpr(ctx, a->fj); ++ ++ if (!avail_FP_SP(ctx)) { ++ return false; ++ } ++ ++ CHECK_FPE; ++ ++ tcg_gen_andi_i64(dest, src, MAKE_64BIT_MASK(0, 31)); ++ gen_nanbox_s(dest, dest); ++ set_fpr(a->fd, dest); ++ ++ return true; ++} ++ ++static bool trans_fabs_d(DisasContext *ctx, arg_fabs_d *a) ++{ ++ TCGv dest = get_fpr(ctx, a->fd); ++ TCGv src = get_fpr(ctx, a->fj); ++ ++ if (!avail_FP_DP(ctx)) { ++ return false; ++ } ++ ++ CHECK_FPE; ++ ++ tcg_gen_andi_i64(dest, src, MAKE_64BIT_MASK(0, 63)); ++ set_fpr(a->fd, dest); ++ ++ return true; ++} ++ ++static bool trans_fneg_s(DisasContext *ctx, arg_fneg_s *a) ++{ ++ TCGv dest = get_fpr(ctx, a->fd); ++ TCGv src = get_fpr(ctx, a->fj); ++ ++ if (!avail_FP_SP(ctx)) { ++ return false; ++ } ++ ++ CHECK_FPE; ++ ++ tcg_gen_xori_i64(dest, src, 0x80000000); ++ gen_nanbox_s(dest, dest); ++ set_fpr(a->fd, dest); ++ ++ return true; ++} ++ ++static bool trans_fneg_d(DisasContext *ctx, arg_fneg_d *a) ++{ ++ TCGv dest = get_fpr(ctx, a->fd); ++ TCGv src = get_fpr(ctx, a->fj); ++ ++ if (!avail_FP_DP(ctx)) { ++ return false; ++ } ++ ++ CHECK_FPE; ++ ++ tcg_gen_xori_i64(dest, src, 0x8000000000000000LL); ++ set_fpr(a->fd, dest); ++ ++ return true; ++} ++ ++TRANS(fadd_s, FP_SP, gen_fff, gen_helper_fadd_s) ++TRANS(fadd_d, FP_DP, gen_fff, gen_helper_fadd_d) ++TRANS(fsub_s, FP_SP, gen_fff, gen_helper_fsub_s) ++TRANS(fsub_d, FP_DP, gen_fff, gen_helper_fsub_d) ++TRANS(fmul_s, FP_SP, gen_fff, gen_helper_fmul_s) ++TRANS(fmul_d, FP_DP, gen_fff, gen_helper_fmul_d) ++TRANS(fdiv_s, FP_SP, gen_fff, gen_helper_fdiv_s) ++TRANS(fdiv_d, FP_DP, gen_fff, gen_helper_fdiv_d) ++TRANS(fmax_s, FP_SP, gen_fff, gen_helper_fmax_s) ++TRANS(fmax_d, FP_DP, gen_fff, gen_helper_fmax_d) ++TRANS(fmin_s, FP_SP, gen_fff, gen_helper_fmin_s) ++TRANS(fmin_d, FP_DP, gen_fff, gen_helper_fmin_d) ++TRANS(fmaxa_s, FP_SP, gen_fff, gen_helper_fmaxa_s) ++TRANS(fmaxa_d, FP_DP, gen_fff, gen_helper_fmaxa_d) ++TRANS(fmina_s, FP_SP, gen_fff, gen_helper_fmina_s) ++TRANS(fmina_d, FP_DP, gen_fff, gen_helper_fmina_d) ++TRANS(fscaleb_s, FP_SP, gen_fff, gen_helper_fscaleb_s) ++TRANS(fscaleb_d, FP_DP, gen_fff, gen_helper_fscaleb_d) ++TRANS(fsqrt_s, FP_SP, gen_ff, gen_helper_fsqrt_s) ++TRANS(fsqrt_d, FP_DP, gen_ff, gen_helper_fsqrt_d) ++TRANS(frecip_s, FP_SP, gen_ff, gen_helper_frecip_s) ++TRANS(frecip_d, FP_DP, gen_ff, gen_helper_frecip_d) ++TRANS(frsqrt_s, FP_SP, gen_ff, gen_helper_frsqrt_s) ++TRANS(frsqrt_d, FP_DP, gen_ff, gen_helper_frsqrt_d) ++TRANS(flogb_s, FP_SP, gen_ff, gen_helper_flogb_s) ++TRANS(flogb_d, FP_DP, gen_ff, gen_helper_flogb_d) ++TRANS(fclass_s, FP_SP, gen_ff, gen_helper_fclass_s) ++TRANS(fclass_d, FP_DP, gen_ff, gen_helper_fclass_d) ++TRANS(fmadd_s, FP_SP, gen_muladd, gen_helper_fmuladd_s, 0) ++TRANS(fmadd_d, FP_DP, gen_muladd, gen_helper_fmuladd_d, 0) ++TRANS(fmsub_s, FP_SP, gen_muladd, gen_helper_fmuladd_s, float_muladd_negate_c) ++TRANS(fmsub_d, FP_DP, gen_muladd, gen_helper_fmuladd_d, float_muladd_negate_c) ++TRANS(fnmadd_s, FP_SP, gen_muladd, gen_helper_fmuladd_s, float_muladd_negate_result) ++TRANS(fnmadd_d, FP_DP, gen_muladd, gen_helper_fmuladd_d, float_muladd_negate_result) ++TRANS(fnmsub_s, FP_SP, gen_muladd, gen_helper_fmuladd_s, ++ float_muladd_negate_c | float_muladd_negate_result) ++TRANS(fnmsub_d, FP_DP, gen_muladd, gen_helper_fmuladd_d, ++ float_muladd_negate_c | float_muladd_negate_result) +diff --git a/target/loongarch/tcg/insn_trans/trans_fcmp.c.inc b/target/loongarch/tcg/insn_trans/trans_fcmp.c.inc +new file mode 100644 +index 0000000..3babf69 +--- /dev/null ++++ b/target/loongarch/tcg/insn_trans/trans_fcmp.c.inc +@@ -0,0 +1,72 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (c) 2021 Loongson Technology Corporation Limited ++ */ ++ ++/* bit0(signaling/quiet) bit1(lt) bit2(eq) bit3(un) bit4(neq) */ ++static uint32_t get_fcmp_flags(int cond) ++{ ++ uint32_t flags = 0; ++ ++ if (cond & 0x1) { ++ flags |= FCMP_LT; ++ } ++ if (cond & 0x2) { ++ flags |= FCMP_EQ; ++ } ++ if (cond & 0x4) { ++ flags |= FCMP_UN; ++ } ++ if (cond & 0x8) { ++ flags |= FCMP_GT | FCMP_LT; ++ } ++ return flags; ++} ++ ++static bool trans_fcmp_cond_s(DisasContext *ctx, arg_fcmp_cond_s *a) ++{ ++ TCGv var, src1, src2; ++ uint32_t flags; ++ void (*fn)(TCGv, TCGv_env, TCGv, TCGv, TCGv_i32); ++ ++ if (!avail_FP_SP(ctx)) { ++ return false; ++ } ++ ++ CHECK_FPE; ++ ++ var = tcg_temp_new(); ++ src1 = get_fpr(ctx, a->fj); ++ src2 = get_fpr(ctx, a->fk); ++ fn = (a->fcond & 1 ? gen_helper_fcmp_s_s : gen_helper_fcmp_c_s); ++ flags = get_fcmp_flags(a->fcond >> 1); ++ ++ fn(var, tcg_env, src1, src2, tcg_constant_i32(flags)); ++ ++ tcg_gen_st8_tl(var, tcg_env, offsetof(CPULoongArchState, cf[a->cd])); ++ return true; ++} ++ ++static bool trans_fcmp_cond_d(DisasContext *ctx, arg_fcmp_cond_d *a) ++{ ++ TCGv var, src1, src2; ++ uint32_t flags; ++ void (*fn)(TCGv, TCGv_env, TCGv, TCGv, TCGv_i32); ++ ++ if (!avail_FP_DP(ctx)) { ++ return false; ++ } ++ ++ CHECK_FPE; ++ ++ var = tcg_temp_new(); ++ src1 = get_fpr(ctx, a->fj); ++ src2 = get_fpr(ctx, a->fk); ++ fn = (a->fcond & 1 ? gen_helper_fcmp_s_d : gen_helper_fcmp_c_d); ++ flags = get_fcmp_flags(a->fcond >> 1); ++ ++ fn(var, tcg_env, src1, src2, tcg_constant_i32(flags)); ++ ++ tcg_gen_st8_tl(var, tcg_env, offsetof(CPULoongArchState, cf[a->cd])); ++ return true; ++} +diff --git a/target/loongarch/tcg/insn_trans/trans_fcnv.c.inc b/target/loongarch/tcg/insn_trans/trans_fcnv.c.inc +new file mode 100644 +index 0000000..833c059 +--- /dev/null ++++ b/target/loongarch/tcg/insn_trans/trans_fcnv.c.inc +@@ -0,0 +1,33 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (c) 2021 Loongson Technology Corporation Limited ++ */ ++ ++TRANS(fcvt_s_d, FP_DP, gen_ff, gen_helper_fcvt_s_d) ++TRANS(fcvt_d_s, FP_DP, gen_ff, gen_helper_fcvt_d_s) ++TRANS(ftintrm_w_s, FP_SP, gen_ff, gen_helper_ftintrm_w_s) ++TRANS(ftintrm_w_d, FP_DP, gen_ff, gen_helper_ftintrm_w_d) ++TRANS(ftintrm_l_s, FP_SP, gen_ff, gen_helper_ftintrm_l_s) ++TRANS(ftintrm_l_d, FP_DP, gen_ff, gen_helper_ftintrm_l_d) ++TRANS(ftintrp_w_s, FP_SP, gen_ff, gen_helper_ftintrp_w_s) ++TRANS(ftintrp_w_d, FP_DP, gen_ff, gen_helper_ftintrp_w_d) ++TRANS(ftintrp_l_s, FP_SP, gen_ff, gen_helper_ftintrp_l_s) ++TRANS(ftintrp_l_d, FP_DP, gen_ff, gen_helper_ftintrp_l_d) ++TRANS(ftintrz_w_s, FP_SP, gen_ff, gen_helper_ftintrz_w_s) ++TRANS(ftintrz_w_d, FP_DP, gen_ff, gen_helper_ftintrz_w_d) ++TRANS(ftintrz_l_s, FP_SP, gen_ff, gen_helper_ftintrz_l_s) ++TRANS(ftintrz_l_d, FP_DP, gen_ff, gen_helper_ftintrz_l_d) ++TRANS(ftintrne_w_s, FP_SP, gen_ff, gen_helper_ftintrne_w_s) ++TRANS(ftintrne_w_d, FP_DP, gen_ff, gen_helper_ftintrne_w_d) ++TRANS(ftintrne_l_s, FP_SP, gen_ff, gen_helper_ftintrne_l_s) ++TRANS(ftintrne_l_d, FP_DP, gen_ff, gen_helper_ftintrne_l_d) ++TRANS(ftint_w_s, FP_SP, gen_ff, gen_helper_ftint_w_s) ++TRANS(ftint_w_d, FP_DP, gen_ff, gen_helper_ftint_w_d) ++TRANS(ftint_l_s, FP_SP, gen_ff, gen_helper_ftint_l_s) ++TRANS(ftint_l_d, FP_DP, gen_ff, gen_helper_ftint_l_d) ++TRANS(ffint_s_w, FP_SP, gen_ff, gen_helper_ffint_s_w) ++TRANS(ffint_s_l, FP_SP, gen_ff, gen_helper_ffint_s_l) ++TRANS(ffint_d_w, FP_DP, gen_ff, gen_helper_ffint_d_w) ++TRANS(ffint_d_l, FP_DP, gen_ff, gen_helper_ffint_d_l) ++TRANS(frint_s, FP_SP, gen_ff, gen_helper_frint_s) ++TRANS(frint_d, FP_DP, gen_ff, gen_helper_frint_d) +diff --git a/target/loongarch/tcg/insn_trans/trans_fmemory.c.inc b/target/loongarch/tcg/insn_trans/trans_fmemory.c.inc +new file mode 100644 +index 0000000..13452bc +--- /dev/null ++++ b/target/loongarch/tcg/insn_trans/trans_fmemory.c.inc +@@ -0,0 +1,158 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (c) 2021 Loongson Technology Corporation Limited ++ */ ++ ++static void maybe_nanbox_load(TCGv freg, MemOp mop) ++{ ++ if ((mop & MO_SIZE) == MO_32) { ++ gen_nanbox_s(freg, freg); ++ } ++} ++ ++static bool gen_fload_i(DisasContext *ctx, arg_fr_i *a, MemOp mop) ++{ ++ TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv dest = get_fpr(ctx, a->fd); ++ ++ CHECK_FPE; ++ ++ addr = make_address_i(ctx, addr, a->imm); ++ ++ tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop); ++ maybe_nanbox_load(dest, mop); ++ set_fpr(a->fd, dest); ++ ++ return true; ++} ++ ++static bool gen_fstore_i(DisasContext *ctx, arg_fr_i *a, MemOp mop) ++{ ++ TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv src = get_fpr(ctx, a->fd); ++ ++ CHECK_FPE; ++ ++ addr = make_address_i(ctx, addr, a->imm); ++ ++ tcg_gen_qemu_st_tl(src, addr, ctx->mem_idx, mop); ++ ++ return true; ++} ++ ++static bool gen_floadx(DisasContext *ctx, arg_frr *a, MemOp mop) ++{ ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); ++ TCGv dest = get_fpr(ctx, a->fd); ++ TCGv addr; ++ ++ CHECK_FPE; ++ ++ addr = make_address_x(ctx, src1, src2); ++ tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop); ++ maybe_nanbox_load(dest, mop); ++ set_fpr(a->fd, dest); ++ ++ return true; ++} ++ ++static bool gen_fstorex(DisasContext *ctx, arg_frr *a, MemOp mop) ++{ ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); ++ TCGv src3 = get_fpr(ctx, a->fd); ++ TCGv addr; ++ ++ CHECK_FPE; ++ ++ addr = make_address_x(ctx, src1, src2); ++ tcg_gen_qemu_st_tl(src3, addr, ctx->mem_idx, mop); ++ ++ return true; ++} ++ ++static bool gen_fload_gt(DisasContext *ctx, arg_frr *a, MemOp mop) ++{ ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); ++ TCGv dest = get_fpr(ctx, a->fd); ++ TCGv addr; ++ ++ CHECK_FPE; ++ ++ gen_helper_asrtgt_d(tcg_env, src1, src2); ++ addr = make_address_x(ctx, src1, src2); ++ tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop); ++ maybe_nanbox_load(dest, mop); ++ set_fpr(a->fd, dest); ++ ++ return true; ++} ++ ++static bool gen_fstore_gt(DisasContext *ctx, arg_frr *a, MemOp mop) ++{ ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); ++ TCGv src3 = get_fpr(ctx, a->fd); ++ TCGv addr; ++ ++ CHECK_FPE; ++ ++ gen_helper_asrtgt_d(tcg_env, src1, src2); ++ addr = make_address_x(ctx, src1, src2); ++ tcg_gen_qemu_st_tl(src3, addr, ctx->mem_idx, mop); ++ ++ return true; ++} ++ ++static bool gen_fload_le(DisasContext *ctx, arg_frr *a, MemOp mop) ++{ ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); ++ TCGv dest = get_fpr(ctx, a->fd); ++ TCGv addr; ++ ++ CHECK_FPE; ++ ++ gen_helper_asrtle_d(tcg_env, src1, src2); ++ addr = make_address_x(ctx, src1, src2); ++ tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop); ++ maybe_nanbox_load(dest, mop); ++ set_fpr(a->fd, dest); ++ ++ return true; ++} ++ ++static bool gen_fstore_le(DisasContext *ctx, arg_frr *a, MemOp mop) ++{ ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); ++ TCGv src3 = get_fpr(ctx, a->fd); ++ TCGv addr; ++ ++ CHECK_FPE; ++ ++ gen_helper_asrtle_d(tcg_env, src1, src2); ++ addr = make_address_x(ctx, src1, src2); ++ tcg_gen_qemu_st_tl(src3, addr, ctx->mem_idx, mop); ++ ++ return true; ++} ++ ++TRANS(fld_s, FP_SP, gen_fload_i, MO_TEUL) ++TRANS(fst_s, FP_SP, gen_fstore_i, MO_TEUL) ++TRANS(fld_d, FP_DP, gen_fload_i, MO_TEUQ) ++TRANS(fst_d, FP_DP, gen_fstore_i, MO_TEUQ) ++TRANS(fldx_s, FP_SP, gen_floadx, MO_TEUL) ++TRANS(fldx_d, FP_DP, gen_floadx, MO_TEUQ) ++TRANS(fstx_s, FP_SP, gen_fstorex, MO_TEUL) ++TRANS(fstx_d, FP_DP, gen_fstorex, MO_TEUQ) ++TRANS(fldgt_s, FP_SP, gen_fload_gt, MO_TEUL) ++TRANS(fldgt_d, FP_DP, gen_fload_gt, MO_TEUQ) ++TRANS(fldle_s, FP_SP, gen_fload_le, MO_TEUL) ++TRANS(fldle_d, FP_DP, gen_fload_le, MO_TEUQ) ++TRANS(fstgt_s, FP_SP, gen_fstore_gt, MO_TEUL) ++TRANS(fstgt_d, FP_DP, gen_fstore_gt, MO_TEUQ) ++TRANS(fstle_s, FP_SP, gen_fstore_le, MO_TEUL) ++TRANS(fstle_d, FP_DP, gen_fstore_le, MO_TEUQ) +diff --git a/target/loongarch/tcg/insn_trans/trans_fmov.c.inc b/target/loongarch/tcg/insn_trans/trans_fmov.c.inc +new file mode 100644 +index 0000000..5cbd9d3 +--- /dev/null ++++ b/target/loongarch/tcg/insn_trans/trans_fmov.c.inc +@@ -0,0 +1,224 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (c) 2021 Loongson Technology Corporation Limited ++ */ ++ ++static const uint32_t fcsr_mask[4] = { ++ UINT32_MAX, FCSR0_M1, FCSR0_M2, FCSR0_M3 ++}; ++ ++static bool trans_fsel(DisasContext *ctx, arg_fsel *a) ++{ ++ TCGv zero = tcg_constant_tl(0); ++ TCGv dest = get_fpr(ctx, a->fd); ++ TCGv src1 = get_fpr(ctx, a->fj); ++ TCGv src2 = get_fpr(ctx, a->fk); ++ TCGv cond; ++ ++ if (!avail_FP(ctx)) { ++ return false; ++ } ++ ++ CHECK_FPE; ++ ++ cond = tcg_temp_new(); ++ tcg_gen_ld8u_tl(cond, tcg_env, offsetof(CPULoongArchState, cf[a->ca])); ++ tcg_gen_movcond_tl(TCG_COND_EQ, dest, cond, zero, src1, src2); ++ set_fpr(a->fd, dest); ++ ++ return true; ++} ++ ++static bool gen_f2f(DisasContext *ctx, arg_ff *a, ++ void (*func)(TCGv, TCGv), bool nanbox) ++{ ++ TCGv dest = get_fpr(ctx, a->fd); ++ TCGv src = get_fpr(ctx, a->fj); ++ ++ CHECK_FPE; ++ ++ func(dest, src); ++ if (nanbox) { ++ gen_nanbox_s(dest, dest); ++ } ++ set_fpr(a->fd, dest); ++ ++ return true; ++} ++ ++static bool gen_r2f(DisasContext *ctx, arg_fr *a, ++ void (*func)(TCGv, TCGv)) ++{ ++ TCGv src = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv dest = get_fpr(ctx, a->fd); ++ ++ if (!avail_FP(ctx)) { ++ return false; ++ } ++ ++ CHECK_FPE; ++ ++ func(dest, src); ++ set_fpr(a->fd, dest); ++ ++ return true; ++} ++ ++static bool gen_f2r(DisasContext *ctx, arg_rf *a, ++ void (*func)(TCGv, TCGv)) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ TCGv src = get_fpr(ctx, a->fj); ++ ++ if (!avail_FP(ctx)) { ++ return false; ++ } ++ ++ CHECK_FPE; ++ ++ func(dest, src); ++ gen_set_gpr(a->rd, dest, EXT_NONE); ++ ++ return true; ++} ++ ++static bool trans_movgr2fcsr(DisasContext *ctx, arg_movgr2fcsr *a) ++{ ++ uint32_t mask = fcsr_mask[a->fcsrd]; ++ TCGv Rj = gpr_src(ctx, a->rj, EXT_NONE); ++ ++ if (!avail_FP(ctx)) { ++ return false; ++ } ++ ++ CHECK_FPE; ++ ++ if (mask == UINT32_MAX) { ++ tcg_gen_st32_i64(Rj, tcg_env, offsetof(CPULoongArchState, fcsr0)); ++ } else { ++ TCGv_i32 fcsr0 = tcg_temp_new_i32(); ++ TCGv_i32 temp = tcg_temp_new_i32(); ++ ++ tcg_gen_ld_i32(fcsr0, tcg_env, offsetof(CPULoongArchState, fcsr0)); ++ tcg_gen_extrl_i64_i32(temp, Rj); ++ tcg_gen_andi_i32(temp, temp, mask); ++ tcg_gen_andi_i32(fcsr0, fcsr0, ~mask); ++ tcg_gen_or_i32(fcsr0, fcsr0, temp); ++ tcg_gen_st_i32(fcsr0, tcg_env, offsetof(CPULoongArchState, fcsr0)); ++ } ++ ++ /* ++ * Install the new rounding mode to fpu_status, if changed. ++ * Note that FCSR3 is exactly the rounding mode field. ++ */ ++ if (mask & FCSR0_M3) { ++ gen_helper_set_rounding_mode(tcg_env); ++ } ++ return true; ++} ++ ++static bool trans_movfcsr2gr(DisasContext *ctx, arg_movfcsr2gr *a) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ ++ if (!avail_FP(ctx)) { ++ return false; ++ } ++ ++ CHECK_FPE; ++ ++ tcg_gen_ld32u_i64(dest, tcg_env, offsetof(CPULoongArchState, fcsr0)); ++ tcg_gen_andi_i64(dest, dest, fcsr_mask[a->fcsrs]); ++ gen_set_gpr(a->rd, dest, EXT_NONE); ++ ++ return true; ++} ++ ++static void gen_movgr2fr_w(TCGv dest, TCGv src) ++{ ++ tcg_gen_deposit_i64(dest, dest, src, 0, 32); ++} ++ ++static void gen_movgr2frh_w(TCGv dest, TCGv src) ++{ ++ tcg_gen_deposit_i64(dest, dest, src, 32, 32); ++} ++ ++static void gen_movfrh2gr_s(TCGv dest, TCGv src) ++{ ++ tcg_gen_sextract_tl(dest, src, 32, 32); ++} ++ ++static bool trans_movfr2cf(DisasContext *ctx, arg_movfr2cf *a) ++{ ++ TCGv t0; ++ TCGv src = get_fpr(ctx, a->fj); ++ ++ if (!avail_FP(ctx)) { ++ return false; ++ } ++ ++ CHECK_FPE; ++ ++ t0 = tcg_temp_new(); ++ tcg_gen_andi_tl(t0, src, 0x1); ++ tcg_gen_st8_tl(t0, tcg_env, offsetof(CPULoongArchState, cf[a->cd & 0x7])); ++ ++ return true; ++} ++ ++static bool trans_movcf2fr(DisasContext *ctx, arg_movcf2fr *a) ++{ ++ TCGv dest = get_fpr(ctx, a->fd); ++ ++ if (!avail_FP(ctx)) { ++ return false; ++ } ++ ++ CHECK_FPE; ++ ++ tcg_gen_ld8u_tl(dest, tcg_env, ++ offsetof(CPULoongArchState, cf[a->cj & 0x7])); ++ set_fpr(a->fd, dest); ++ ++ return true; ++} ++ ++static bool trans_movgr2cf(DisasContext *ctx, arg_movgr2cf *a) ++{ ++ TCGv t0; ++ ++ if (!avail_FP(ctx)) { ++ return false; ++ } ++ ++ CHECK_FPE; ++ ++ t0 = tcg_temp_new(); ++ tcg_gen_andi_tl(t0, gpr_src(ctx, a->rj, EXT_NONE), 0x1); ++ tcg_gen_st8_tl(t0, tcg_env, offsetof(CPULoongArchState, cf[a->cd & 0x7])); ++ ++ return true; ++} ++ ++static bool trans_movcf2gr(DisasContext *ctx, arg_movcf2gr *a) ++{ ++ if (!avail_FP(ctx)) { ++ return false; ++ } ++ ++ CHECK_FPE; ++ ++ tcg_gen_ld8u_tl(gpr_dst(ctx, a->rd, EXT_NONE), tcg_env, ++ offsetof(CPULoongArchState, cf[a->cj & 0x7])); ++ return true; ++} ++ ++TRANS(fmov_s, FP_SP, gen_f2f, tcg_gen_mov_tl, true) ++TRANS(fmov_d, FP_DP, gen_f2f, tcg_gen_mov_tl, false) ++TRANS(movgr2fr_w, FP_SP, gen_r2f, gen_movgr2fr_w) ++TRANS(movgr2fr_d, 64, gen_r2f, tcg_gen_mov_tl) ++TRANS(movgr2frh_w, FP_DP, gen_r2f, gen_movgr2frh_w) ++TRANS(movfr2gr_s, FP_SP, gen_f2r, tcg_gen_ext32s_tl) ++TRANS(movfr2gr_d, 64, gen_f2r, tcg_gen_mov_tl) ++TRANS(movfrh2gr_s, FP_DP, gen_f2r, gen_movfrh2gr_s) +diff --git a/target/loongarch/tcg/insn_trans/trans_memory.c.inc b/target/loongarch/tcg/insn_trans/trans_memory.c.inc +new file mode 100644 +index 0000000..42f4e74 +--- /dev/null ++++ b/target/loongarch/tcg/insn_trans/trans_memory.c.inc +@@ -0,0 +1,194 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (c) 2021 Loongson Technology Corporation Limited ++ */ ++ ++static bool gen_load(DisasContext *ctx, arg_rr_i *a, MemOp mop) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); ++ ++ addr = make_address_i(ctx, addr, a->imm); ++ ++ tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop); ++ gen_set_gpr(a->rd, dest, EXT_NONE); ++ return true; ++} ++ ++static bool gen_store(DisasContext *ctx, arg_rr_i *a, MemOp mop) ++{ ++ TCGv data = gpr_src(ctx, a->rd, EXT_NONE); ++ TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); ++ ++ addr = make_address_i(ctx, addr, a->imm); ++ ++ tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, mop); ++ return true; ++} ++ ++static bool gen_loadx(DisasContext *ctx, arg_rrr *a, MemOp mop) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); ++ TCGv addr = make_address_x(ctx, src1, src2); ++ ++ tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop); ++ gen_set_gpr(a->rd, dest, EXT_NONE); ++ ++ return true; ++} ++ ++static bool gen_storex(DisasContext *ctx, arg_rrr *a, MemOp mop) ++{ ++ TCGv data = gpr_src(ctx, a->rd, EXT_NONE); ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); ++ TCGv addr = make_address_x(ctx, src1, src2); ++ ++ tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, mop); ++ ++ return true; ++} ++ ++static bool gen_load_gt(DisasContext *ctx, arg_rrr *a, MemOp mop) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); ++ ++ gen_helper_asrtgt_d(tcg_env, src1, src2); ++ src1 = make_address_i(ctx, src1, 0); ++ tcg_gen_qemu_ld_tl(dest, src1, ctx->mem_idx, mop); ++ gen_set_gpr(a->rd, dest, EXT_NONE); ++ ++ return true; ++} ++ ++static bool gen_load_le(DisasContext *ctx, arg_rrr *a, MemOp mop) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); ++ ++ gen_helper_asrtle_d(tcg_env, src1, src2); ++ src1 = make_address_i(ctx, src1, 0); ++ tcg_gen_qemu_ld_tl(dest, src1, ctx->mem_idx, mop); ++ gen_set_gpr(a->rd, dest, EXT_NONE); ++ ++ return true; ++} ++ ++static bool gen_store_gt(DisasContext *ctx, arg_rrr *a, MemOp mop) ++{ ++ TCGv data = gpr_src(ctx, a->rd, EXT_NONE); ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); ++ ++ gen_helper_asrtgt_d(tcg_env, src1, src2); ++ src1 = make_address_i(ctx, src1, 0); ++ tcg_gen_qemu_st_tl(data, src1, ctx->mem_idx, mop); ++ ++ return true; ++} ++ ++static bool gen_store_le(DisasContext *ctx, arg_rrr *a, MemOp mop) ++{ ++ TCGv data = gpr_src(ctx, a->rd, EXT_NONE); ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); ++ ++ gen_helper_asrtle_d(tcg_env, src1, src2); ++ src1 = make_address_i(ctx, src1, 0); ++ tcg_gen_qemu_st_tl(data, src1, ctx->mem_idx, mop); ++ ++ return true; ++} ++ ++static bool trans_preld(DisasContext *ctx, arg_preld *a) ++{ ++ return true; ++} ++ ++static bool trans_preldx(DisasContext *ctx, arg_preldx * a) ++{ ++ return true; ++} ++ ++static bool trans_dbar(DisasContext *ctx, arg_dbar * a) ++{ ++ tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL); ++ return true; ++} ++ ++static bool trans_ibar(DisasContext *ctx, arg_ibar *a) ++{ ++ ctx->base.is_jmp = DISAS_STOP; ++ return true; ++} ++ ++static bool gen_ldptr(DisasContext *ctx, arg_rr_i *a, MemOp mop) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); ++ ++ addr = make_address_i(ctx, addr, a->imm); ++ ++ tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop); ++ gen_set_gpr(a->rd, dest, EXT_NONE); ++ return true; ++} ++ ++static bool gen_stptr(DisasContext *ctx, arg_rr_i *a, MemOp mop) ++{ ++ TCGv data = gpr_src(ctx, a->rd, EXT_NONE); ++ TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); ++ ++ addr = make_address_i(ctx, addr, a->imm); ++ ++ tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, mop); ++ return true; ++} ++ ++TRANS(ld_b, ALL, gen_load, MO_SB) ++TRANS(ld_h, ALL, gen_load, MO_TESW) ++TRANS(ld_w, ALL, gen_load, MO_TESL) ++TRANS(ld_d, 64, gen_load, MO_TEUQ) ++TRANS(st_b, ALL, gen_store, MO_UB) ++TRANS(st_h, ALL, gen_store, MO_TEUW) ++TRANS(st_w, ALL, gen_store, MO_TEUL) ++TRANS(st_d, 64, gen_store, MO_TEUQ) ++TRANS(ld_bu, ALL, gen_load, MO_UB) ++TRANS(ld_hu, ALL, gen_load, MO_TEUW) ++TRANS(ld_wu, 64, gen_load, MO_TEUL) ++TRANS(ldx_b, 64, gen_loadx, MO_SB) ++TRANS(ldx_h, 64, gen_loadx, MO_TESW) ++TRANS(ldx_w, 64, gen_loadx, MO_TESL) ++TRANS(ldx_d, 64, gen_loadx, MO_TEUQ) ++TRANS(stx_b, 64, gen_storex, MO_UB) ++TRANS(stx_h, 64, gen_storex, MO_TEUW) ++TRANS(stx_w, 64, gen_storex, MO_TEUL) ++TRANS(stx_d, 64, gen_storex, MO_TEUQ) ++TRANS(ldx_bu, 64, gen_loadx, MO_UB) ++TRANS(ldx_hu, 64, gen_loadx, MO_TEUW) ++TRANS(ldx_wu, 64, gen_loadx, MO_TEUL) ++TRANS(ldptr_w, 64, gen_ldptr, MO_TESL) ++TRANS(stptr_w, 64, gen_stptr, MO_TEUL) ++TRANS(ldptr_d, 64, gen_ldptr, MO_TEUQ) ++TRANS(stptr_d, 64, gen_stptr, MO_TEUQ) ++TRANS(ldgt_b, 64, gen_load_gt, MO_SB) ++TRANS(ldgt_h, 64, gen_load_gt, MO_TESW) ++TRANS(ldgt_w, 64, gen_load_gt, MO_TESL) ++TRANS(ldgt_d, 64, gen_load_gt, MO_TEUQ) ++TRANS(ldle_b, 64, gen_load_le, MO_SB) ++TRANS(ldle_h, 64, gen_load_le, MO_TESW) ++TRANS(ldle_w, 64, gen_load_le, MO_TESL) ++TRANS(ldle_d, 64, gen_load_le, MO_TEUQ) ++TRANS(stgt_b, 64, gen_store_gt, MO_UB) ++TRANS(stgt_h, 64, gen_store_gt, MO_TEUW) ++TRANS(stgt_w, 64, gen_store_gt, MO_TEUL) ++TRANS(stgt_d, 64, gen_store_gt, MO_TEUQ) ++TRANS(stle_b, 64, gen_store_le, MO_UB) ++TRANS(stle_h, 64, gen_store_le, MO_TEUW) ++TRANS(stle_w, 64, gen_store_le, MO_TEUL) ++TRANS(stle_d, 64, gen_store_le, MO_TEUQ) +diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc +new file mode 100644 +index 0000000..01d4572 +--- /dev/null ++++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc +@@ -0,0 +1,498 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (c) 2021 Loongson Technology Corporation Limited ++ * ++ * LoongArch translation routines for the privileged instructions. ++ */ ++ ++#include "cpu-csr.h" ++ ++#ifdef CONFIG_USER_ONLY ++ ++#define GEN_FALSE_TRANS(name) \ ++static bool trans_##name(DisasContext *ctx, arg_##name * a) \ ++{ \ ++ return false; \ ++} ++ ++GEN_FALSE_TRANS(csrrd) ++GEN_FALSE_TRANS(csrwr) ++GEN_FALSE_TRANS(csrxchg) ++GEN_FALSE_TRANS(iocsrrd_b) ++GEN_FALSE_TRANS(iocsrrd_h) ++GEN_FALSE_TRANS(iocsrrd_w) ++GEN_FALSE_TRANS(iocsrrd_d) ++GEN_FALSE_TRANS(iocsrwr_b) ++GEN_FALSE_TRANS(iocsrwr_h) ++GEN_FALSE_TRANS(iocsrwr_w) ++GEN_FALSE_TRANS(iocsrwr_d) ++GEN_FALSE_TRANS(tlbsrch) ++GEN_FALSE_TRANS(tlbrd) ++GEN_FALSE_TRANS(tlbwr) ++GEN_FALSE_TRANS(tlbfill) ++GEN_FALSE_TRANS(tlbclr) ++GEN_FALSE_TRANS(tlbflush) ++GEN_FALSE_TRANS(invtlb) ++GEN_FALSE_TRANS(cacop) ++GEN_FALSE_TRANS(ldpte) ++GEN_FALSE_TRANS(lddir) ++GEN_FALSE_TRANS(ertn) ++GEN_FALSE_TRANS(dbcl) ++GEN_FALSE_TRANS(idle) ++ ++#else ++ ++typedef void (*GenCSRRead)(TCGv dest, TCGv_ptr env); ++typedef void (*GenCSRWrite)(TCGv dest, TCGv_ptr env, TCGv src); ++ ++typedef struct { ++ int offset; ++ int flags; ++ GenCSRRead readfn; ++ GenCSRWrite writefn; ++} CSRInfo; ++ ++enum { ++ CSRFL_READONLY = (1 << 0), ++ CSRFL_EXITTB = (1 << 1), ++ CSRFL_IO = (1 << 2), ++}; ++ ++#define CSR_OFF_FUNCS(NAME, FL, RD, WR) \ ++ [LOONGARCH_CSR_##NAME] = { \ ++ .offset = offsetof(CPULoongArchState, CSR_##NAME), \ ++ .flags = FL, .readfn = RD, .writefn = WR \ ++ } ++ ++#define CSR_OFF_ARRAY(NAME, N) \ ++ [LOONGARCH_CSR_##NAME(N)] = { \ ++ .offset = offsetof(CPULoongArchState, CSR_##NAME[N]), \ ++ .flags = 0, .readfn = NULL, .writefn = NULL \ ++ } ++ ++#define CSR_OFF_FLAGS(NAME, FL) \ ++ CSR_OFF_FUNCS(NAME, FL, NULL, NULL) ++ ++#define CSR_OFF(NAME) \ ++ CSR_OFF_FLAGS(NAME, 0) ++ ++static const CSRInfo csr_info[] = { ++ CSR_OFF_FLAGS(CRMD, CSRFL_EXITTB), ++ CSR_OFF(PRMD), ++ CSR_OFF_FLAGS(EUEN, CSRFL_EXITTB), ++ CSR_OFF_FLAGS(MISC, CSRFL_READONLY), ++ CSR_OFF(ECFG), ++ CSR_OFF_FUNCS(ESTAT, CSRFL_EXITTB, NULL, gen_helper_csrwr_estat), ++ CSR_OFF(ERA), ++ CSR_OFF(BADV), ++ CSR_OFF_FLAGS(BADI, CSRFL_READONLY), ++ CSR_OFF(EENTRY), ++ CSR_OFF(TLBIDX), ++ CSR_OFF(TLBEHI), ++ CSR_OFF(TLBELO0), ++ CSR_OFF(TLBELO1), ++ CSR_OFF_FUNCS(ASID, CSRFL_EXITTB, NULL, gen_helper_csrwr_asid), ++ CSR_OFF(PGDL), ++ CSR_OFF(PGDH), ++ CSR_OFF_FUNCS(PGD, CSRFL_READONLY, gen_helper_csrrd_pgd, NULL), ++ CSR_OFF(PWCL), ++ CSR_OFF(PWCH), ++ CSR_OFF(STLBPS), ++ CSR_OFF(RVACFG), ++ CSR_OFF_FUNCS(CPUID, CSRFL_READONLY, gen_helper_csrrd_cpuid, NULL), ++ CSR_OFF_FLAGS(PRCFG1, CSRFL_READONLY), ++ CSR_OFF_FLAGS(PRCFG2, CSRFL_READONLY), ++ CSR_OFF_FLAGS(PRCFG3, CSRFL_READONLY), ++ CSR_OFF_ARRAY(SAVE, 0), ++ CSR_OFF_ARRAY(SAVE, 1), ++ CSR_OFF_ARRAY(SAVE, 2), ++ CSR_OFF_ARRAY(SAVE, 3), ++ CSR_OFF_ARRAY(SAVE, 4), ++ CSR_OFF_ARRAY(SAVE, 5), ++ CSR_OFF_ARRAY(SAVE, 6), ++ CSR_OFF_ARRAY(SAVE, 7), ++ CSR_OFF_ARRAY(SAVE, 8), ++ CSR_OFF_ARRAY(SAVE, 9), ++ CSR_OFF_ARRAY(SAVE, 10), ++ CSR_OFF_ARRAY(SAVE, 11), ++ CSR_OFF_ARRAY(SAVE, 12), ++ CSR_OFF_ARRAY(SAVE, 13), ++ CSR_OFF_ARRAY(SAVE, 14), ++ CSR_OFF_ARRAY(SAVE, 15), ++ CSR_OFF(TID), ++ CSR_OFF_FUNCS(TCFG, CSRFL_IO, NULL, gen_helper_csrwr_tcfg), ++ CSR_OFF_FUNCS(TVAL, CSRFL_READONLY | CSRFL_IO, gen_helper_csrrd_tval, NULL), ++ CSR_OFF(CNTC), ++ CSR_OFF_FUNCS(TICLR, CSRFL_IO, NULL, gen_helper_csrwr_ticlr), ++ CSR_OFF(LLBCTL), ++ CSR_OFF(IMPCTL1), ++ CSR_OFF(IMPCTL2), ++ CSR_OFF(TLBRENTRY), ++ CSR_OFF(TLBRBADV), ++ CSR_OFF(TLBRERA), ++ CSR_OFF(TLBRSAVE), ++ CSR_OFF(TLBRELO0), ++ CSR_OFF(TLBRELO1), ++ CSR_OFF(TLBREHI), ++ CSR_OFF(TLBRPRMD), ++ CSR_OFF(MERRCTL), ++ CSR_OFF(MERRINFO1), ++ CSR_OFF(MERRINFO2), ++ CSR_OFF(MERRENTRY), ++ CSR_OFF(MERRERA), ++ CSR_OFF(MERRSAVE), ++ CSR_OFF(CTAG), ++ CSR_OFF_ARRAY(DMW, 0), ++ CSR_OFF_ARRAY(DMW, 1), ++ CSR_OFF_ARRAY(DMW, 2), ++ CSR_OFF_ARRAY(DMW, 3), ++ CSR_OFF(DBG), ++ CSR_OFF(DERA), ++ CSR_OFF(DSAVE), ++}; ++ ++static bool check_plv(DisasContext *ctx) ++{ ++ if (ctx->plv == MMU_PLV_USER) { ++ generate_exception(ctx, EXCCODE_IPE); ++ return true; ++ } ++ return false; ++} ++ ++static const CSRInfo *get_csr(unsigned csr_num) ++{ ++ const CSRInfo *csr; ++ ++ if (csr_num >= ARRAY_SIZE(csr_info)) { ++ return NULL; ++ } ++ csr = &csr_info[csr_num]; ++ if (csr->offset == 0) { ++ return NULL; ++ } ++ return csr; ++} ++ ++static bool check_csr_flags(DisasContext *ctx, const CSRInfo *csr, bool write) ++{ ++ if ((csr->flags & CSRFL_READONLY) && write) { ++ return false; ++ } ++ if ((csr->flags & CSRFL_IO) && translator_io_start(&ctx->base)) { ++ ctx->base.is_jmp = DISAS_EXIT_UPDATE; ++ } else if ((csr->flags & CSRFL_EXITTB) && write) { ++ ctx->base.is_jmp = DISAS_EXIT_UPDATE; ++ } ++ return true; ++} ++ ++static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a) ++{ ++ TCGv dest; ++ const CSRInfo *csr; ++ ++ if (check_plv(ctx)) { ++ return false; ++ } ++ csr = get_csr(a->csr); ++ if (csr == NULL) { ++ /* CSR is undefined: read as 0. */ ++ dest = tcg_constant_tl(0); ++ } else { ++ check_csr_flags(ctx, csr, false); ++ dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ if (csr->readfn) { ++ csr->readfn(dest, tcg_env); ++ } else { ++ tcg_gen_ld_tl(dest, tcg_env, csr->offset); ++ } ++ } ++ gen_set_gpr(a->rd, dest, EXT_NONE); ++ return true; ++} ++ ++static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a) ++{ ++ TCGv dest, src1; ++ const CSRInfo *csr; ++ ++ if (check_plv(ctx)) { ++ return false; ++ } ++ csr = get_csr(a->csr); ++ if (csr == NULL) { ++ /* CSR is undefined: write ignored, read old_value as 0. */ ++ gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE); ++ return true; ++ } ++ if (!check_csr_flags(ctx, csr, true)) { ++ /* CSR is readonly: trap. */ ++ return false; ++ } ++ src1 = gpr_src(ctx, a->rd, EXT_NONE); ++ if (csr->writefn) { ++ dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ csr->writefn(dest, tcg_env, src1); ++ } else { ++ dest = tcg_temp_new(); ++ tcg_gen_ld_tl(dest, tcg_env, csr->offset); ++ tcg_gen_st_tl(src1, tcg_env, csr->offset); ++ } ++ gen_set_gpr(a->rd, dest, EXT_NONE); ++ return true; ++} ++ ++static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a) ++{ ++ TCGv src1, mask, oldv, newv, temp; ++ const CSRInfo *csr; ++ ++ if (check_plv(ctx)) { ++ return false; ++ } ++ csr = get_csr(a->csr); ++ if (csr == NULL) { ++ /* CSR is undefined: write ignored, read old_value as 0. */ ++ gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE); ++ return true; ++ } ++ ++ if (!check_csr_flags(ctx, csr, true)) { ++ /* CSR is readonly: trap. */ ++ return false; ++ } ++ ++ /* So far only readonly csrs have readfn. */ ++ assert(csr->readfn == NULL); ++ ++ src1 = gpr_src(ctx, a->rd, EXT_NONE); ++ mask = gpr_src(ctx, a->rj, EXT_NONE); ++ oldv = tcg_temp_new(); ++ newv = tcg_temp_new(); ++ temp = tcg_temp_new(); ++ ++ tcg_gen_ld_tl(oldv, tcg_env, csr->offset); ++ tcg_gen_and_tl(newv, src1, mask); ++ tcg_gen_andc_tl(temp, oldv, mask); ++ tcg_gen_or_tl(newv, newv, temp); ++ ++ if (csr->writefn) { ++ csr->writefn(oldv, tcg_env, newv); ++ } else { ++ tcg_gen_st_tl(newv, tcg_env, csr->offset); ++ } ++ gen_set_gpr(a->rd, oldv, EXT_NONE); ++ return true; ++} ++ ++static bool gen_iocsrrd(DisasContext *ctx, arg_rr *a, ++ void (*func)(TCGv, TCGv_ptr, TCGv)) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ ++ if (check_plv(ctx)) { ++ return false; ++ } ++ func(dest, tcg_env, src1); ++ return true; ++} ++ ++static bool gen_iocsrwr(DisasContext *ctx, arg_rr *a, ++ void (*func)(TCGv_ptr, TCGv, TCGv)) ++{ ++ TCGv val = gpr_src(ctx, a->rd, EXT_NONE); ++ TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); ++ ++ if (check_plv(ctx)) { ++ return false; ++ } ++ func(tcg_env, addr, val); ++ return true; ++} ++ ++TRANS(iocsrrd_b, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_b) ++TRANS(iocsrrd_h, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_h) ++TRANS(iocsrrd_w, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_w) ++TRANS(iocsrrd_d, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_d) ++TRANS(iocsrwr_b, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_b) ++TRANS(iocsrwr_h, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_h) ++TRANS(iocsrwr_w, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_w) ++TRANS(iocsrwr_d, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_d) ++ ++static void check_mmu_idx(DisasContext *ctx) ++{ ++ if (ctx->mem_idx != MMU_IDX_DA) { ++ tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4); ++ ctx->base.is_jmp = DISAS_EXIT; ++ } ++} ++ ++static bool trans_tlbsrch(DisasContext *ctx, arg_tlbsrch *a) ++{ ++ if (check_plv(ctx)) { ++ return false; ++ } ++ gen_helper_tlbsrch(tcg_env); ++ return true; ++} ++ ++static bool trans_tlbrd(DisasContext *ctx, arg_tlbrd *a) ++{ ++ if (check_plv(ctx)) { ++ return false; ++ } ++ gen_helper_tlbrd(tcg_env); ++ return true; ++} ++ ++static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a) ++{ ++ if (check_plv(ctx)) { ++ return false; ++ } ++ gen_helper_tlbwr(tcg_env); ++ check_mmu_idx(ctx); ++ return true; ++} ++ ++static bool trans_tlbfill(DisasContext *ctx, arg_tlbfill *a) ++{ ++ if (check_plv(ctx)) { ++ return false; ++ } ++ gen_helper_tlbfill(tcg_env); ++ check_mmu_idx(ctx); ++ return true; ++} ++ ++static bool trans_tlbclr(DisasContext *ctx, arg_tlbclr *a) ++{ ++ if (check_plv(ctx)) { ++ return false; ++ } ++ gen_helper_tlbclr(tcg_env); ++ check_mmu_idx(ctx); ++ return true; ++} ++ ++static bool trans_tlbflush(DisasContext *ctx, arg_tlbflush *a) ++{ ++ if (check_plv(ctx)) { ++ return false; ++ } ++ gen_helper_tlbflush(tcg_env); ++ check_mmu_idx(ctx); ++ return true; ++} ++ ++static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a) ++{ ++ TCGv rj = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv rk = gpr_src(ctx, a->rk, EXT_NONE); ++ ++ if (check_plv(ctx)) { ++ return false; ++ } ++ ++ switch (a->imm) { ++ case 0: ++ case 1: ++ gen_helper_invtlb_all(tcg_env); ++ break; ++ case 2: ++ gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(1)); ++ break; ++ case 3: ++ gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(0)); ++ break; ++ case 4: ++ gen_helper_invtlb_all_asid(tcg_env, rj); ++ break; ++ case 5: ++ gen_helper_invtlb_page_asid(tcg_env, rj, rk); ++ break; ++ case 6: ++ gen_helper_invtlb_page_asid_or_g(tcg_env, rj, rk); ++ break; ++ default: ++ return false; ++ } ++ ctx->base.is_jmp = DISAS_STOP; ++ return true; ++} ++ ++static bool trans_cacop(DisasContext *ctx, arg_cacop *a) ++{ ++ /* Treat the cacop as a nop */ ++ if (check_plv(ctx)) { ++ return false; ++ } ++ return true; ++} ++ ++static bool trans_ldpte(DisasContext *ctx, arg_ldpte *a) ++{ ++ TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx); ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ ++ if (!avail_LSPW(ctx)) { ++ return true; ++ } ++ ++ if (check_plv(ctx)) { ++ return false; ++ } ++ gen_helper_ldpte(tcg_env, src1, tcg_constant_tl(a->imm), mem_idx); ++ return true; ++} ++ ++static bool trans_lddir(DisasContext *ctx, arg_lddir *a) ++{ ++ TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx); ++ TCGv src = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ ++ if (!avail_LSPW(ctx)) { ++ return true; ++ } ++ ++ if (check_plv(ctx)) { ++ return false; ++ } ++ gen_helper_lddir(dest, tcg_env, src, tcg_constant_tl(a->imm), mem_idx); ++ return true; ++} ++ ++static bool trans_ertn(DisasContext *ctx, arg_ertn *a) ++{ ++ if (check_plv(ctx)) { ++ return false; ++ } ++ gen_helper_ertn(tcg_env); ++ ctx->base.is_jmp = DISAS_EXIT; ++ return true; ++} ++ ++static bool trans_dbcl(DisasContext *ctx, arg_dbcl *a) ++{ ++ if (check_plv(ctx)) { ++ return false; ++ } ++ generate_exception(ctx, EXCCODE_DBP); ++ return true; ++} ++ ++static bool trans_idle(DisasContext *ctx, arg_idle *a) ++{ ++ if (check_plv(ctx)) { ++ return false; ++ } ++ ++ tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4); ++ gen_helper_idle(tcg_env); ++ ctx->base.is_jmp = DISAS_NORETURN; ++ return true; ++} ++#endif +diff --git a/target/loongarch/tcg/insn_trans/trans_shift.c.inc b/target/loongarch/tcg/insn_trans/trans_shift.c.inc +new file mode 100644 +index 0000000..2f4bd6f +--- /dev/null ++++ b/target/loongarch/tcg/insn_trans/trans_shift.c.inc +@@ -0,0 +1,99 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (c) 2021 Loongson Technology Corporation Limited ++ */ ++ ++static void gen_sll_w(TCGv dest, TCGv src1, TCGv src2) ++{ ++ TCGv t0 = tcg_temp_new(); ++ tcg_gen_andi_tl(t0, src2, 0x1f); ++ tcg_gen_shl_tl(dest, src1, t0); ++} ++ ++static void gen_srl_w(TCGv dest, TCGv src1, TCGv src2) ++{ ++ TCGv t0 = tcg_temp_new(); ++ tcg_gen_andi_tl(t0, src2, 0x1f); ++ tcg_gen_shr_tl(dest, src1, t0); ++} ++ ++static void gen_sra_w(TCGv dest, TCGv src1, TCGv src2) ++{ ++ TCGv t0 = tcg_temp_new(); ++ tcg_gen_andi_tl(t0, src2, 0x1f); ++ tcg_gen_sar_tl(dest, src1, t0); ++} ++ ++static void gen_sll_d(TCGv dest, TCGv src1, TCGv src2) ++{ ++ TCGv t0 = tcg_temp_new(); ++ tcg_gen_andi_tl(t0, src2, 0x3f); ++ tcg_gen_shl_tl(dest, src1, t0); ++} ++ ++static void gen_srl_d(TCGv dest, TCGv src1, TCGv src2) ++{ ++ TCGv t0 = tcg_temp_new(); ++ tcg_gen_andi_tl(t0, src2, 0x3f); ++ tcg_gen_shr_tl(dest, src1, t0); ++} ++ ++static void gen_sra_d(TCGv dest, TCGv src1, TCGv src2) ++{ ++ TCGv t0 = tcg_temp_new(); ++ tcg_gen_andi_tl(t0, src2, 0x3f); ++ tcg_gen_sar_tl(dest, src1, t0); ++} ++ ++static void gen_rotr_w(TCGv dest, TCGv src1, TCGv src2) ++{ ++ TCGv_i32 t1 = tcg_temp_new_i32(); ++ TCGv_i32 t2 = tcg_temp_new_i32(); ++ TCGv t0 = tcg_temp_new(); ++ ++ tcg_gen_andi_tl(t0, src2, 0x1f); ++ ++ tcg_gen_trunc_tl_i32(t1, src1); ++ tcg_gen_trunc_tl_i32(t2, t0); ++ ++ tcg_gen_rotr_i32(t1, t1, t2); ++ tcg_gen_ext_i32_tl(dest, t1); ++} ++ ++static void gen_rotr_d(TCGv dest, TCGv src1, TCGv src2) ++{ ++ TCGv t0 = tcg_temp_new(); ++ tcg_gen_andi_tl(t0, src2, 0x3f); ++ tcg_gen_rotr_tl(dest, src1, t0); ++} ++ ++static bool trans_srai_w(DisasContext *ctx, arg_srai_w *a) ++{ ++ TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_ZERO); ++ ++ if (!avail_64(ctx)) { ++ return false; ++ } ++ ++ tcg_gen_sextract_tl(dest, src1, a->imm, 32 - a->imm); ++ gen_set_gpr(a->rd, dest, EXT_NONE); ++ ++ return true; ++} ++ ++TRANS(sll_w, ALL, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_sll_w) ++TRANS(srl_w, ALL, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_srl_w) ++TRANS(sra_w, ALL, gen_rrr, EXT_SIGN, EXT_NONE, EXT_SIGN, gen_sra_w) ++TRANS(sll_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_sll_d) ++TRANS(srl_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_srl_d) ++TRANS(sra_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_sra_d) ++TRANS(rotr_w, 64, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_rotr_w) ++TRANS(rotr_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_rotr_d) ++TRANS(slli_w, ALL, gen_rri_c, EXT_NONE, EXT_SIGN, tcg_gen_shli_tl) ++TRANS(slli_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_shli_tl) ++TRANS(srli_w, ALL, gen_rri_c, EXT_ZERO, EXT_SIGN, tcg_gen_shri_tl) ++TRANS(srli_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_shri_tl) ++TRANS(srai_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_sari_tl) ++TRANS(rotri_w, 64, gen_rri_v, EXT_NONE, EXT_NONE, gen_rotr_w) ++TRANS(rotri_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_rotri_tl) +diff --git a/target/loongarch/tcg/insn_trans/trans_vec.c.inc b/target/loongarch/tcg/insn_trans/trans_vec.c.inc +new file mode 100644 +index 0000000..92b1d22 +--- /dev/null ++++ b/target/loongarch/tcg/insn_trans/trans_vec.c.inc +@@ -0,0 +1,5511 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * LoongArch vector translate functions ++ * Copyright (c) 2022-2023 Loongson Technology Corporation Limited ++ */ ++ ++static bool check_vec(DisasContext *ctx, uint32_t oprsz) ++{ ++ if ((oprsz == 16) && ((ctx->base.tb->flags & HW_FLAGS_EUEN_SXE) == 0)) { ++ generate_exception(ctx, EXCCODE_SXD); ++ return false; ++ } ++ ++ if ((oprsz == 32) && ((ctx->base.tb->flags & HW_FLAGS_EUEN_ASXE) == 0)) { ++ generate_exception(ctx, EXCCODE_ASXD); ++ return false; ++ } ++ ++ return true; ++} ++ ++static bool gen_vvvv_ptr_vl(DisasContext *ctx, arg_vvvv *a, uint32_t oprsz, ++ gen_helper_gvec_4_ptr *fn) ++{ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ tcg_gen_gvec_4_ptr(vec_full_offset(a->vd), ++ vec_full_offset(a->vj), ++ vec_full_offset(a->vk), ++ vec_full_offset(a->va), ++ tcg_env, ++ oprsz, ctx->vl / 8, 0, fn); ++ return true; ++} ++ ++static bool gen_vvvv_ptr(DisasContext *ctx, arg_vvvv *a, ++ gen_helper_gvec_4_ptr *fn) ++{ ++ return gen_vvvv_ptr_vl(ctx, a, 16, fn); ++} ++ ++static bool gen_xxxx_ptr(DisasContext *ctx, arg_vvvv *a, ++ gen_helper_gvec_4_ptr *fn) ++{ ++ return gen_vvvv_ptr_vl(ctx, a, 32, fn); ++} ++ ++static bool gen_vvvv_vl(DisasContext *ctx, arg_vvvv *a, uint32_t oprsz, ++ gen_helper_gvec_4 *fn) ++{ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ tcg_gen_gvec_4_ool(vec_full_offset(a->vd), ++ vec_full_offset(a->vj), ++ vec_full_offset(a->vk), ++ vec_full_offset(a->va), ++ oprsz, ctx->vl / 8, 0, fn); ++ return true; ++} ++ ++static bool gen_vvvv(DisasContext *ctx, arg_vvvv *a, ++ gen_helper_gvec_4 *fn) ++{ ++ return gen_vvvv_vl(ctx, a, 16, fn); ++} ++ ++static bool gen_xxxx(DisasContext *ctx, arg_vvvv *a, ++ gen_helper_gvec_4 *fn) ++{ ++ return gen_vvvv_vl(ctx, a, 32, fn); ++} ++ ++static bool gen_vvv_ptr_vl(DisasContext *ctx, arg_vvv *a, uint32_t oprsz, ++ gen_helper_gvec_3_ptr *fn) ++{ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ tcg_gen_gvec_3_ptr(vec_full_offset(a->vd), ++ vec_full_offset(a->vj), ++ vec_full_offset(a->vk), ++ tcg_env, ++ oprsz, ctx->vl / 8, 0, fn); ++ return true; ++} ++ ++static bool gen_vvv_ptr(DisasContext *ctx, arg_vvv *a, ++ gen_helper_gvec_3_ptr *fn) ++{ ++ return gen_vvv_ptr_vl(ctx, a, 16, fn); ++} ++ ++static bool gen_xxx_ptr(DisasContext *ctx, arg_vvv *a, ++ gen_helper_gvec_3_ptr *fn) ++{ ++ return gen_vvv_ptr_vl(ctx, a, 32, fn); ++} ++ ++static bool gen_vvv_vl(DisasContext *ctx, arg_vvv *a, uint32_t oprsz, ++ gen_helper_gvec_3 *fn) ++{ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ tcg_gen_gvec_3_ool(vec_full_offset(a->vd), ++ vec_full_offset(a->vj), ++ vec_full_offset(a->vk), ++ oprsz, ctx->vl / 8, 0, fn); ++ return true; ++} ++ ++static bool gen_vvv(DisasContext *ctx, arg_vvv *a, gen_helper_gvec_3 *fn) ++{ ++ return gen_vvv_vl(ctx, a, 16, fn); ++} ++ ++static bool gen_xxx(DisasContext *ctx, arg_vvv *a, gen_helper_gvec_3 *fn) ++{ ++ return gen_vvv_vl(ctx, a, 32, fn); ++} ++ ++static bool gen_vv_ptr_vl(DisasContext *ctx, arg_vv *a, uint32_t oprsz, ++ gen_helper_gvec_2_ptr *fn) ++{ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ tcg_gen_gvec_2_ptr(vec_full_offset(a->vd), ++ vec_full_offset(a->vj), ++ tcg_env, ++ oprsz, ctx->vl / 8, 0, fn); ++ return true; ++} ++ ++static bool gen_vv_ptr(DisasContext *ctx, arg_vv *a, ++ gen_helper_gvec_2_ptr *fn) ++{ ++ return gen_vv_ptr_vl(ctx, a, 16, fn); ++} ++ ++static bool gen_xx_ptr(DisasContext *ctx, arg_vv *a, ++ gen_helper_gvec_2_ptr *fn) ++{ ++ return gen_vv_ptr_vl(ctx, a, 32, fn); ++} ++ ++static bool gen_vv_vl(DisasContext *ctx, arg_vv *a, uint32_t oprsz, ++ gen_helper_gvec_2 *fn) ++{ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ tcg_gen_gvec_2_ool(vec_full_offset(a->vd), ++ vec_full_offset(a->vj), ++ oprsz, ctx->vl / 8, 0, fn); ++ return true; ++} ++ ++static bool gen_vv(DisasContext *ctx, arg_vv *a, gen_helper_gvec_2 *fn) ++{ ++ return gen_vv_vl(ctx, a, 16, fn); ++} ++ ++static bool gen_xx(DisasContext *ctx, arg_vv *a, gen_helper_gvec_2 *fn) ++{ ++ return gen_vv_vl(ctx, a, 32, fn); ++} ++ ++static bool gen_vv_i_vl(DisasContext *ctx, arg_vv_i *a, uint32_t oprsz, ++ gen_helper_gvec_2i *fn) ++{ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ tcg_gen_gvec_2i_ool(vec_full_offset(a->vd), ++ vec_full_offset(a->vj), ++ tcg_constant_i64(a->imm), ++ oprsz, ctx->vl / 8, 0, fn); ++ return true; ++} ++ ++static bool gen_vv_i(DisasContext *ctx, arg_vv_i *a, gen_helper_gvec_2i *fn) ++{ ++ return gen_vv_i_vl(ctx, a, 16, fn); ++} ++ ++static bool gen_xx_i(DisasContext *ctx, arg_vv_i *a, gen_helper_gvec_2i *fn) ++{ ++ return gen_vv_i_vl(ctx, a, 32, fn); ++} ++ ++static bool gen_cv_vl(DisasContext *ctx, arg_cv *a, uint32_t sz, ++ void (*func)(TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32)) ++{ ++ if (!check_vec(ctx, sz)) { ++ return true; ++ } ++ ++ TCGv_i32 vj = tcg_constant_i32(a->vj); ++ TCGv_i32 cd = tcg_constant_i32(a->cd); ++ TCGv_i32 oprsz = tcg_constant_i32(sz); ++ ++ func(tcg_env, oprsz, cd, vj); ++ return true; ++} ++ ++static bool gen_cv(DisasContext *ctx, arg_cv *a, ++ void (*func)(TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32)) ++{ ++ return gen_cv_vl(ctx, a, 16, func); ++} ++ ++static bool gen_cx(DisasContext *ctx, arg_cv *a, ++ void (*func)(TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32)) ++{ ++ return gen_cv_vl(ctx, a, 32, func); ++} ++ ++static bool gvec_vvv_vl(DisasContext *ctx, arg_vvv *a, ++ uint32_t oprsz, MemOp mop, ++ void (*func)(unsigned, uint32_t, uint32_t, ++ uint32_t, uint32_t, uint32_t)) ++{ ++ uint32_t vd_ofs = vec_full_offset(a->vd); ++ uint32_t vj_ofs = vec_full_offset(a->vj); ++ uint32_t vk_ofs = vec_full_offset(a->vk); ++ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ func(mop, vd_ofs, vj_ofs, vk_ofs, oprsz, ctx->vl / 8); ++ return true; ++} ++ ++static bool gvec_vvv(DisasContext *ctx, arg_vvv *a, MemOp mop, ++ void (*func)(unsigned, uint32_t, uint32_t, ++ uint32_t, uint32_t, uint32_t)) ++{ ++ return gvec_vvv_vl(ctx, a, 16, mop, func); ++} ++ ++static bool gvec_xxx(DisasContext *ctx, arg_vvv *a, MemOp mop, ++ void (*func)(unsigned, uint32_t, uint32_t, ++ uint32_t, uint32_t, uint32_t)) ++{ ++ return gvec_vvv_vl(ctx, a, 32, mop, func); ++} ++ ++static bool gvec_vv_vl(DisasContext *ctx, arg_vv *a, ++ uint32_t oprsz, MemOp mop, ++ void (*func)(unsigned, uint32_t, uint32_t, ++ uint32_t, uint32_t)) ++{ ++ uint32_t vd_ofs = vec_full_offset(a->vd); ++ uint32_t vj_ofs = vec_full_offset(a->vj); ++ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ func(mop, vd_ofs, vj_ofs, oprsz, ctx->vl / 8); ++ return true; ++} ++ ++ ++static bool gvec_vv(DisasContext *ctx, arg_vv *a, MemOp mop, ++ void (*func)(unsigned, uint32_t, uint32_t, ++ uint32_t, uint32_t)) ++{ ++ return gvec_vv_vl(ctx, a, 16, mop, func); ++} ++ ++static bool gvec_xx(DisasContext *ctx, arg_vv *a, MemOp mop, ++ void (*func)(unsigned, uint32_t, uint32_t, ++ uint32_t, uint32_t)) ++{ ++ return gvec_vv_vl(ctx, a, 32, mop, func); ++} ++ ++static bool gvec_vv_i_vl(DisasContext *ctx, arg_vv_i *a, ++ uint32_t oprsz, MemOp mop, ++ void (*func)(unsigned, uint32_t, uint32_t, ++ int64_t, uint32_t, uint32_t)) ++{ ++ uint32_t vd_ofs = vec_full_offset(a->vd); ++ uint32_t vj_ofs = vec_full_offset(a->vj); ++ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ func(mop, vd_ofs, vj_ofs, a->imm, oprsz, ctx->vl / 8); ++ return true; ++} ++ ++static bool gvec_vv_i(DisasContext *ctx, arg_vv_i *a, MemOp mop, ++ void (*func)(unsigned, uint32_t, uint32_t, ++ int64_t, uint32_t, uint32_t)) ++{ ++ return gvec_vv_i_vl(ctx, a, 16, mop, func); ++} ++ ++static bool gvec_xx_i(DisasContext *ctx, arg_vv_i *a, MemOp mop, ++ void (*func)(unsigned, uint32_t, uint32_t, ++ int64_t, uint32_t, uint32_t)) ++{ ++ return gvec_vv_i_vl(ctx,a, 32, mop, func); ++} ++ ++static bool gvec_subi_vl(DisasContext *ctx, arg_vv_i *a, ++ uint32_t oprsz, MemOp mop) ++{ ++ uint32_t vd_ofs = vec_full_offset(a->vd); ++ uint32_t vj_ofs = vec_full_offset(a->vj); ++ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ tcg_gen_gvec_addi(mop, vd_ofs, vj_ofs, -a->imm, oprsz, ctx->vl / 8); ++ return true; ++} ++ ++static bool gvec_subi(DisasContext *ctx, arg_vv_i *a, MemOp mop) ++{ ++ return gvec_subi_vl(ctx, a, 16, mop); ++} ++ ++static bool gvec_xsubi(DisasContext *ctx, arg_vv_i *a, MemOp mop) ++{ ++ return gvec_subi_vl(ctx, a, 32, mop); ++} ++ ++TRANS(vadd_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_add) ++TRANS(vadd_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_add) ++TRANS(vadd_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_add) ++TRANS(vadd_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_add) ++TRANS(xvadd_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_add) ++TRANS(xvadd_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_add) ++TRANS(xvadd_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_add) ++TRANS(xvadd_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_add) ++ ++static bool gen_vaddsub_q_vl(DisasContext *ctx, arg_vvv *a, uint32_t oprsz, ++ void (*func)(TCGv_i64, TCGv_i64, TCGv_i64, ++ TCGv_i64, TCGv_i64, TCGv_i64)) ++{ ++ int i; ++ TCGv_i64 rh, rl, ah, al, bh, bl; ++ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ rh = tcg_temp_new_i64(); ++ rl = tcg_temp_new_i64(); ++ ah = tcg_temp_new_i64(); ++ al = tcg_temp_new_i64(); ++ bh = tcg_temp_new_i64(); ++ bl = tcg_temp_new_i64(); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ get_vreg64(ah, a->vj, 1 + i * 2); ++ get_vreg64(al, a->vj, i * 2); ++ get_vreg64(bh, a->vk, 1 + i * 2); ++ get_vreg64(bl, a->vk, i * 2); ++ ++ func(rl, rh, al, ah, bl, bh); ++ ++ set_vreg64(rh, a->vd, 1 + i * 2); ++ set_vreg64(rl, a->vd, i * 2); ++ } ++ return true; ++} ++ ++static bool gen_vaddsub_q(DisasContext *ctx, arg_vvv *a, ++ void (*func)(TCGv_i64, TCGv_i64, TCGv_i64, ++ TCGv_i64, TCGv_i64, TCGv_i64)) ++{ ++ return gen_vaddsub_q_vl(ctx, a, 16, func); ++} ++ ++static bool gen_xvaddsub_q(DisasContext *ctx, arg_vvv *a, ++ void (*func)(TCGv_i64, TCGv_i64, TCGv_i64, ++ TCGv_i64, TCGv_i64, TCGv_i64)) ++{ ++ return gen_vaddsub_q_vl(ctx, a, 32, func); ++} ++ ++TRANS(vsub_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_sub) ++TRANS(vsub_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_sub) ++TRANS(vsub_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_sub) ++TRANS(vsub_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_sub) ++TRANS(xvsub_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_sub) ++TRANS(xvsub_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_sub) ++TRANS(xvsub_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_sub) ++TRANS(xvsub_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_sub) ++ ++TRANS(vadd_q, LSX, gen_vaddsub_q, tcg_gen_add2_i64) ++TRANS(vsub_q, LSX, gen_vaddsub_q, tcg_gen_sub2_i64) ++TRANS(xvadd_q, LASX, gen_xvaddsub_q, tcg_gen_add2_i64) ++TRANS(xvsub_q, LASX, gen_xvaddsub_q, tcg_gen_sub2_i64) ++ ++TRANS(vaddi_bu, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_addi) ++TRANS(vaddi_hu, LSX, gvec_vv_i, MO_16, tcg_gen_gvec_addi) ++TRANS(vaddi_wu, LSX, gvec_vv_i, MO_32, tcg_gen_gvec_addi) ++TRANS(vaddi_du, LSX, gvec_vv_i, MO_64, tcg_gen_gvec_addi) ++TRANS(vsubi_bu, LSX, gvec_subi, MO_8) ++TRANS(vsubi_hu, LSX, gvec_subi, MO_16) ++TRANS(vsubi_wu, LSX, gvec_subi, MO_32) ++TRANS(vsubi_du, LSX, gvec_subi, MO_64) ++TRANS(xvaddi_bu, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_addi) ++TRANS(xvaddi_hu, LASX, gvec_xx_i, MO_16, tcg_gen_gvec_addi) ++TRANS(xvaddi_wu, LASX, gvec_xx_i, MO_32, tcg_gen_gvec_addi) ++TRANS(xvaddi_du, LASX, gvec_xx_i, MO_64, tcg_gen_gvec_addi) ++TRANS(xvsubi_bu, LASX, gvec_xsubi, MO_8) ++TRANS(xvsubi_hu, LASX, gvec_xsubi, MO_16) ++TRANS(xvsubi_wu, LASX, gvec_xsubi, MO_32) ++TRANS(xvsubi_du, LASX, gvec_xsubi, MO_64) ++ ++TRANS(vneg_b, LSX, gvec_vv, MO_8, tcg_gen_gvec_neg) ++TRANS(vneg_h, LSX, gvec_vv, MO_16, tcg_gen_gvec_neg) ++TRANS(vneg_w, LSX, gvec_vv, MO_32, tcg_gen_gvec_neg) ++TRANS(vneg_d, LSX, gvec_vv, MO_64, tcg_gen_gvec_neg) ++TRANS(xvneg_b, LASX, gvec_xx, MO_8, tcg_gen_gvec_neg) ++TRANS(xvneg_h, LASX, gvec_xx, MO_16, tcg_gen_gvec_neg) ++TRANS(xvneg_w, LASX, gvec_xx, MO_32, tcg_gen_gvec_neg) ++TRANS(xvneg_d, LASX, gvec_xx, MO_64, tcg_gen_gvec_neg) ++ ++TRANS(vsadd_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_ssadd) ++TRANS(vsadd_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_ssadd) ++TRANS(vsadd_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_ssadd) ++TRANS(vsadd_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_ssadd) ++TRANS(vsadd_bu, LSX, gvec_vvv, MO_8, tcg_gen_gvec_usadd) ++TRANS(vsadd_hu, LSX, gvec_vvv, MO_16, tcg_gen_gvec_usadd) ++TRANS(vsadd_wu, LSX, gvec_vvv, MO_32, tcg_gen_gvec_usadd) ++TRANS(vsadd_du, LSX, gvec_vvv, MO_64, tcg_gen_gvec_usadd) ++TRANS(vssub_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_sssub) ++TRANS(vssub_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_sssub) ++TRANS(vssub_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_sssub) ++TRANS(vssub_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_sssub) ++TRANS(vssub_bu, LSX, gvec_vvv, MO_8, tcg_gen_gvec_ussub) ++TRANS(vssub_hu, LSX, gvec_vvv, MO_16, tcg_gen_gvec_ussub) ++TRANS(vssub_wu, LSX, gvec_vvv, MO_32, tcg_gen_gvec_ussub) ++TRANS(vssub_du, LSX, gvec_vvv, MO_64, tcg_gen_gvec_ussub) ++ ++TRANS(xvsadd_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_ssadd) ++TRANS(xvsadd_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_ssadd) ++TRANS(xvsadd_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_ssadd) ++TRANS(xvsadd_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_ssadd) ++TRANS(xvsadd_bu, LASX, gvec_xxx, MO_8, tcg_gen_gvec_usadd) ++TRANS(xvsadd_hu, LASX, gvec_xxx, MO_16, tcg_gen_gvec_usadd) ++TRANS(xvsadd_wu, LASX, gvec_xxx, MO_32, tcg_gen_gvec_usadd) ++TRANS(xvsadd_du, LASX, gvec_xxx, MO_64, tcg_gen_gvec_usadd) ++TRANS(xvssub_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_sssub) ++TRANS(xvssub_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_sssub) ++TRANS(xvssub_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_sssub) ++TRANS(xvssub_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_sssub) ++TRANS(xvssub_bu, LASX, gvec_xxx, MO_8, tcg_gen_gvec_ussub) ++TRANS(xvssub_hu, LASX, gvec_xxx, MO_16, tcg_gen_gvec_ussub) ++TRANS(xvssub_wu, LASX, gvec_xxx, MO_32, tcg_gen_gvec_ussub) ++TRANS(xvssub_du, LASX, gvec_xxx, MO_64, tcg_gen_gvec_ussub) ++ ++TRANS(vhaddw_h_b, LSX, gen_vvv, gen_helper_vhaddw_h_b) ++TRANS(vhaddw_w_h, LSX, gen_vvv, gen_helper_vhaddw_w_h) ++TRANS(vhaddw_d_w, LSX, gen_vvv, gen_helper_vhaddw_d_w) ++TRANS(vhaddw_q_d, LSX, gen_vvv, gen_helper_vhaddw_q_d) ++TRANS(vhaddw_hu_bu, LSX, gen_vvv, gen_helper_vhaddw_hu_bu) ++TRANS(vhaddw_wu_hu, LSX, gen_vvv, gen_helper_vhaddw_wu_hu) ++TRANS(vhaddw_du_wu, LSX, gen_vvv, gen_helper_vhaddw_du_wu) ++TRANS(vhaddw_qu_du, LSX, gen_vvv, gen_helper_vhaddw_qu_du) ++TRANS(vhsubw_h_b, LSX, gen_vvv, gen_helper_vhsubw_h_b) ++TRANS(vhsubw_w_h, LSX, gen_vvv, gen_helper_vhsubw_w_h) ++TRANS(vhsubw_d_w, LSX, gen_vvv, gen_helper_vhsubw_d_w) ++TRANS(vhsubw_q_d, LSX, gen_vvv, gen_helper_vhsubw_q_d) ++TRANS(vhsubw_hu_bu, LSX, gen_vvv, gen_helper_vhsubw_hu_bu) ++TRANS(vhsubw_wu_hu, LSX, gen_vvv, gen_helper_vhsubw_wu_hu) ++TRANS(vhsubw_du_wu, LSX, gen_vvv, gen_helper_vhsubw_du_wu) ++TRANS(vhsubw_qu_du, LSX, gen_vvv, gen_helper_vhsubw_qu_du) ++ ++TRANS(xvhaddw_h_b, LASX, gen_xxx, gen_helper_vhaddw_h_b) ++TRANS(xvhaddw_w_h, LASX, gen_xxx, gen_helper_vhaddw_w_h) ++TRANS(xvhaddw_d_w, LASX, gen_xxx, gen_helper_vhaddw_d_w) ++TRANS(xvhaddw_q_d, LASX, gen_xxx, gen_helper_vhaddw_q_d) ++TRANS(xvhaddw_hu_bu, LASX, gen_xxx, gen_helper_vhaddw_hu_bu) ++TRANS(xvhaddw_wu_hu, LASX, gen_xxx, gen_helper_vhaddw_wu_hu) ++TRANS(xvhaddw_du_wu, LASX, gen_xxx, gen_helper_vhaddw_du_wu) ++TRANS(xvhaddw_qu_du, LASX, gen_xxx, gen_helper_vhaddw_qu_du) ++TRANS(xvhsubw_h_b, LASX, gen_xxx, gen_helper_vhsubw_h_b) ++TRANS(xvhsubw_w_h, LASX, gen_xxx, gen_helper_vhsubw_w_h) ++TRANS(xvhsubw_d_w, LASX, gen_xxx, gen_helper_vhsubw_d_w) ++TRANS(xvhsubw_q_d, LASX, gen_xxx, gen_helper_vhsubw_q_d) ++TRANS(xvhsubw_hu_bu, LASX, gen_xxx, gen_helper_vhsubw_hu_bu) ++TRANS(xvhsubw_wu_hu, LASX, gen_xxx, gen_helper_vhsubw_wu_hu) ++TRANS(xvhsubw_du_wu, LASX, gen_xxx, gen_helper_vhsubw_du_wu) ++TRANS(xvhsubw_qu_du, LASX, gen_xxx, gen_helper_vhsubw_qu_du) ++ ++static void gen_vaddwev_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, t2; ++ ++ int halfbits = 4 << vece; ++ ++ t1 = tcg_temp_new_vec_matching(a); ++ t2 = tcg_temp_new_vec_matching(b); ++ ++ /* Sign-extend the even elements from a */ ++ tcg_gen_shli_vec(vece, t1, a, halfbits); ++ tcg_gen_sari_vec(vece, t1, t1, halfbits); ++ ++ /* Sign-extend the even elements from b */ ++ tcg_gen_shli_vec(vece, t2, b, halfbits); ++ tcg_gen_sari_vec(vece, t2, t2, halfbits); ++ ++ tcg_gen_add_vec(vece, t, t1, t2); ++} ++ ++static void gen_vaddwev_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1, t2; ++ ++ t1 = tcg_temp_new_i32(); ++ t2 = tcg_temp_new_i32(); ++ tcg_gen_ext16s_i32(t1, a); ++ tcg_gen_ext16s_i32(t2, b); ++ tcg_gen_add_i32(t, t1, t2); ++} ++ ++static void gen_vaddwev_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1, t2; ++ ++ t1 = tcg_temp_new_i64(); ++ t2 = tcg_temp_new_i64(); ++ tcg_gen_ext32s_i64(t1, a); ++ tcg_gen_ext32s_i64(t2, b); ++ tcg_gen_add_i64(t, t1, t2); ++} ++ ++static void do_vaddwev_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_shli_vec, INDEX_op_sari_vec, INDEX_op_add_vec, 0 ++ }; ++ static const GVecGen3 op[4] = { ++ { ++ .fniv = gen_vaddwev_s, ++ .fno = gen_helper_vaddwev_h_b, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vaddwev_w_h, ++ .fniv = gen_vaddwev_s, ++ .fno = gen_helper_vaddwev_w_h, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vaddwev_d_w, ++ .fniv = gen_vaddwev_s, ++ .fno = gen_helper_vaddwev_d_w, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ { ++ .fno = gen_helper_vaddwev_q_d, ++ .vece = MO_128 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vaddwev_h_b, LSX, gvec_vvv, MO_8, do_vaddwev_s) ++TRANS(vaddwev_w_h, LSX, gvec_vvv, MO_16, do_vaddwev_s) ++TRANS(vaddwev_d_w, LSX, gvec_vvv, MO_32, do_vaddwev_s) ++TRANS(vaddwev_q_d, LSX, gvec_vvv, MO_64, do_vaddwev_s) ++TRANS(xvaddwev_h_b, LASX, gvec_xxx, MO_8, do_vaddwev_s) ++TRANS(xvaddwev_w_h, LASX, gvec_xxx, MO_16, do_vaddwev_s) ++TRANS(xvaddwev_d_w, LASX, gvec_xxx, MO_32, do_vaddwev_s) ++TRANS(xvaddwev_q_d, LASX, gvec_xxx, MO_64, do_vaddwev_s) ++ ++static void gen_vaddwod_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1, t2; ++ ++ t1 = tcg_temp_new_i32(); ++ t2 = tcg_temp_new_i32(); ++ tcg_gen_sari_i32(t1, a, 16); ++ tcg_gen_sari_i32(t2, b, 16); ++ tcg_gen_add_i32(t, t1, t2); ++} ++ ++static void gen_vaddwod_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1, t2; ++ ++ t1 = tcg_temp_new_i64(); ++ t2 = tcg_temp_new_i64(); ++ tcg_gen_sari_i64(t1, a, 32); ++ tcg_gen_sari_i64(t2, b, 32); ++ tcg_gen_add_i64(t, t1, t2); ++} ++ ++static void gen_vaddwod_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, t2; ++ ++ int halfbits = 4 << vece; ++ ++ t1 = tcg_temp_new_vec_matching(a); ++ t2 = tcg_temp_new_vec_matching(b); ++ ++ /* Sign-extend the odd elements for vector */ ++ tcg_gen_sari_vec(vece, t1, a, halfbits); ++ tcg_gen_sari_vec(vece, t2, b, halfbits); ++ ++ tcg_gen_add_vec(vece, t, t1, t2); ++} ++ ++static void do_vaddwod_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_sari_vec, INDEX_op_add_vec, 0 ++ }; ++ static const GVecGen3 op[4] = { ++ { ++ .fniv = gen_vaddwod_s, ++ .fno = gen_helper_vaddwod_h_b, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vaddwod_w_h, ++ .fniv = gen_vaddwod_s, ++ .fno = gen_helper_vaddwod_w_h, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vaddwod_d_w, ++ .fniv = gen_vaddwod_s, ++ .fno = gen_helper_vaddwod_d_w, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ { ++ .fno = gen_helper_vaddwod_q_d, ++ .vece = MO_128 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vaddwod_h_b, LSX, gvec_vvv, MO_8, do_vaddwod_s) ++TRANS(vaddwod_w_h, LSX, gvec_vvv, MO_16, do_vaddwod_s) ++TRANS(vaddwod_d_w, LSX, gvec_vvv, MO_32, do_vaddwod_s) ++TRANS(vaddwod_q_d, LSX, gvec_vvv, MO_64, do_vaddwod_s) ++TRANS(xvaddwod_h_b, LASX, gvec_xxx, MO_8, do_vaddwod_s) ++TRANS(xvaddwod_w_h, LASX, gvec_xxx, MO_16, do_vaddwod_s) ++TRANS(xvaddwod_d_w, LASX, gvec_xxx, MO_32, do_vaddwod_s) ++TRANS(xvaddwod_q_d, LASX, gvec_xxx, MO_64, do_vaddwod_s) ++ ++ ++static void gen_vsubwev_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, t2; ++ ++ int halfbits = 4 << vece; ++ ++ t1 = tcg_temp_new_vec_matching(a); ++ t2 = tcg_temp_new_vec_matching(b); ++ ++ /* Sign-extend the even elements from a */ ++ tcg_gen_shli_vec(vece, t1, a, halfbits); ++ tcg_gen_sari_vec(vece, t1, t1, halfbits); ++ ++ /* Sign-extend the even elements from b */ ++ tcg_gen_shli_vec(vece, t2, b, halfbits); ++ tcg_gen_sari_vec(vece, t2, t2, halfbits); ++ ++ tcg_gen_sub_vec(vece, t, t1, t2); ++} ++ ++static void gen_vsubwev_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1, t2; ++ ++ t1 = tcg_temp_new_i32(); ++ t2 = tcg_temp_new_i32(); ++ tcg_gen_ext16s_i32(t1, a); ++ tcg_gen_ext16s_i32(t2, b); ++ tcg_gen_sub_i32(t, t1, t2); ++} ++ ++static void gen_vsubwev_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1, t2; ++ ++ t1 = tcg_temp_new_i64(); ++ t2 = tcg_temp_new_i64(); ++ tcg_gen_ext32s_i64(t1, a); ++ tcg_gen_ext32s_i64(t2, b); ++ tcg_gen_sub_i64(t, t1, t2); ++} ++ ++static void do_vsubwev_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_shli_vec, INDEX_op_sari_vec, INDEX_op_sub_vec, 0 ++ }; ++ static const GVecGen3 op[4] = { ++ { ++ .fniv = gen_vsubwev_s, ++ .fno = gen_helper_vsubwev_h_b, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vsubwev_w_h, ++ .fniv = gen_vsubwev_s, ++ .fno = gen_helper_vsubwev_w_h, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vsubwev_d_w, ++ .fniv = gen_vsubwev_s, ++ .fno = gen_helper_vsubwev_d_w, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ { ++ .fno = gen_helper_vsubwev_q_d, ++ .vece = MO_128 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vsubwev_h_b, LSX, gvec_vvv, MO_8, do_vsubwev_s) ++TRANS(vsubwev_w_h, LSX, gvec_vvv, MO_16, do_vsubwev_s) ++TRANS(vsubwev_d_w, LSX, gvec_vvv, MO_32, do_vsubwev_s) ++TRANS(vsubwev_q_d, LSX, gvec_vvv, MO_64, do_vsubwev_s) ++TRANS(xvsubwev_h_b, LASX, gvec_xxx, MO_8, do_vsubwev_s) ++TRANS(xvsubwev_w_h, LASX, gvec_xxx, MO_16, do_vsubwev_s) ++TRANS(xvsubwev_d_w, LASX, gvec_xxx, MO_32, do_vsubwev_s) ++TRANS(xvsubwev_q_d, LASX, gvec_xxx, MO_64, do_vsubwev_s) ++ ++static void gen_vsubwod_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, t2; ++ ++ int halfbits = 4 << vece; ++ ++ t1 = tcg_temp_new_vec_matching(a); ++ t2 = tcg_temp_new_vec_matching(b); ++ ++ /* Sign-extend the odd elements for vector */ ++ tcg_gen_sari_vec(vece, t1, a, halfbits); ++ tcg_gen_sari_vec(vece, t2, b, halfbits); ++ ++ tcg_gen_sub_vec(vece, t, t1, t2); ++} ++ ++static void gen_vsubwod_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1, t2; ++ ++ t1 = tcg_temp_new_i32(); ++ t2 = tcg_temp_new_i32(); ++ tcg_gen_sari_i32(t1, a, 16); ++ tcg_gen_sari_i32(t2, b, 16); ++ tcg_gen_sub_i32(t, t1, t2); ++} ++ ++static void gen_vsubwod_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1, t2; ++ ++ t1 = tcg_temp_new_i64(); ++ t2 = tcg_temp_new_i64(); ++ tcg_gen_sari_i64(t1, a, 32); ++ tcg_gen_sari_i64(t2, b, 32); ++ tcg_gen_sub_i64(t, t1, t2); ++} ++ ++static void do_vsubwod_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_sari_vec, INDEX_op_sub_vec, 0 ++ }; ++ static const GVecGen3 op[4] = { ++ { ++ .fniv = gen_vsubwod_s, ++ .fno = gen_helper_vsubwod_h_b, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vsubwod_w_h, ++ .fniv = gen_vsubwod_s, ++ .fno = gen_helper_vsubwod_w_h, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vsubwod_d_w, ++ .fniv = gen_vsubwod_s, ++ .fno = gen_helper_vsubwod_d_w, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ { ++ .fno = gen_helper_vsubwod_q_d, ++ .vece = MO_128 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vsubwod_h_b, LSX, gvec_vvv, MO_8, do_vsubwod_s) ++TRANS(vsubwod_w_h, LSX, gvec_vvv, MO_16, do_vsubwod_s) ++TRANS(vsubwod_d_w, LSX, gvec_vvv, MO_32, do_vsubwod_s) ++TRANS(vsubwod_q_d, LSX, gvec_vvv, MO_64, do_vsubwod_s) ++TRANS(xvsubwod_h_b, LASX, gvec_xxx, MO_8, do_vsubwod_s) ++TRANS(xvsubwod_w_h, LASX, gvec_xxx, MO_16, do_vsubwod_s) ++TRANS(xvsubwod_d_w, LASX, gvec_xxx, MO_32, do_vsubwod_s) ++TRANS(xvsubwod_q_d, LASX, gvec_xxx, MO_64, do_vsubwod_s) ++ ++static void gen_vaddwev_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, t2, t3; ++ ++ t1 = tcg_temp_new_vec_matching(a); ++ t2 = tcg_temp_new_vec_matching(b); ++ t3 = tcg_constant_vec_matching(t, vece, MAKE_64BIT_MASK(0, 4 << vece)); ++ tcg_gen_and_vec(vece, t1, a, t3); ++ tcg_gen_and_vec(vece, t2, b, t3); ++ tcg_gen_add_vec(vece, t, t1, t2); ++} ++ ++static void gen_vaddwev_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1, t2; ++ ++ t1 = tcg_temp_new_i32(); ++ t2 = tcg_temp_new_i32(); ++ tcg_gen_ext16u_i32(t1, a); ++ tcg_gen_ext16u_i32(t2, b); ++ tcg_gen_add_i32(t, t1, t2); ++} ++ ++static void gen_vaddwev_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1, t2; ++ ++ t1 = tcg_temp_new_i64(); ++ t2 = tcg_temp_new_i64(); ++ tcg_gen_ext32u_i64(t1, a); ++ tcg_gen_ext32u_i64(t2, b); ++ tcg_gen_add_i64(t, t1, t2); ++} ++ ++static void do_vaddwev_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_add_vec, 0 ++ }; ++ static const GVecGen3 op[4] = { ++ { ++ .fniv = gen_vaddwev_u, ++ .fno = gen_helper_vaddwev_h_bu, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vaddwev_w_hu, ++ .fniv = gen_vaddwev_u, ++ .fno = gen_helper_vaddwev_w_hu, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vaddwev_d_wu, ++ .fniv = gen_vaddwev_u, ++ .fno = gen_helper_vaddwev_d_wu, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ { ++ .fno = gen_helper_vaddwev_q_du, ++ .vece = MO_128 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vaddwev_h_bu, LSX, gvec_vvv, MO_8, do_vaddwev_u) ++TRANS(vaddwev_w_hu, LSX, gvec_vvv, MO_16, do_vaddwev_u) ++TRANS(vaddwev_d_wu, LSX, gvec_vvv, MO_32, do_vaddwev_u) ++TRANS(vaddwev_q_du, LSX, gvec_vvv, MO_64, do_vaddwev_u) ++TRANS(xvaddwev_h_bu, LASX, gvec_xxx, MO_8, do_vaddwev_u) ++TRANS(xvaddwev_w_hu, LASX, gvec_xxx, MO_16, do_vaddwev_u) ++TRANS(xvaddwev_d_wu, LASX, gvec_xxx, MO_32, do_vaddwev_u) ++TRANS(xvaddwev_q_du, LASX, gvec_xxx, MO_64, do_vaddwev_u) ++ ++static void gen_vaddwod_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, t2; ++ ++ int halfbits = 4 << vece; ++ ++ t1 = tcg_temp_new_vec_matching(a); ++ t2 = tcg_temp_new_vec_matching(b); ++ ++ /* Zero-extend the odd elements for vector */ ++ tcg_gen_shri_vec(vece, t1, a, halfbits); ++ tcg_gen_shri_vec(vece, t2, b, halfbits); ++ ++ tcg_gen_add_vec(vece, t, t1, t2); ++} ++ ++static void gen_vaddwod_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1, t2; ++ ++ t1 = tcg_temp_new_i32(); ++ t2 = tcg_temp_new_i32(); ++ tcg_gen_shri_i32(t1, a, 16); ++ tcg_gen_shri_i32(t2, b, 16); ++ tcg_gen_add_i32(t, t1, t2); ++} ++ ++static void gen_vaddwod_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1, t2; ++ ++ t1 = tcg_temp_new_i64(); ++ t2 = tcg_temp_new_i64(); ++ tcg_gen_shri_i64(t1, a, 32); ++ tcg_gen_shri_i64(t2, b, 32); ++ tcg_gen_add_i64(t, t1, t2); ++} ++ ++static void do_vaddwod_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_shri_vec, INDEX_op_add_vec, 0 ++ }; ++ static const GVecGen3 op[4] = { ++ { ++ .fniv = gen_vaddwod_u, ++ .fno = gen_helper_vaddwod_h_bu, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vaddwod_w_hu, ++ .fniv = gen_vaddwod_u, ++ .fno = gen_helper_vaddwod_w_hu, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vaddwod_d_wu, ++ .fniv = gen_vaddwod_u, ++ .fno = gen_helper_vaddwod_d_wu, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ { ++ .fno = gen_helper_vaddwod_q_du, ++ .vece = MO_128 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vaddwod_h_bu, LSX, gvec_vvv, MO_8, do_vaddwod_u) ++TRANS(vaddwod_w_hu, LSX, gvec_vvv, MO_16, do_vaddwod_u) ++TRANS(vaddwod_d_wu, LSX, gvec_vvv, MO_32, do_vaddwod_u) ++TRANS(vaddwod_q_du, LSX, gvec_vvv, MO_64, do_vaddwod_u) ++TRANS(xvaddwod_h_bu, LASX, gvec_xxx, MO_8, do_vaddwod_u) ++TRANS(xvaddwod_w_hu, LASX, gvec_xxx, MO_16, do_vaddwod_u) ++TRANS(xvaddwod_d_wu, LASX, gvec_xxx, MO_32, do_vaddwod_u) ++TRANS(xvaddwod_q_du, LASX, gvec_xxx, MO_64, do_vaddwod_u) ++ ++static void gen_vsubwev_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, t2, t3; ++ ++ t1 = tcg_temp_new_vec_matching(a); ++ t2 = tcg_temp_new_vec_matching(b); ++ t3 = tcg_constant_vec_matching(t, vece, MAKE_64BIT_MASK(0, 4 << vece)); ++ tcg_gen_and_vec(vece, t1, a, t3); ++ tcg_gen_and_vec(vece, t2, b, t3); ++ tcg_gen_sub_vec(vece, t, t1, t2); ++} ++ ++static void gen_vsubwev_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1, t2; ++ ++ t1 = tcg_temp_new_i32(); ++ t2 = tcg_temp_new_i32(); ++ tcg_gen_ext16u_i32(t1, a); ++ tcg_gen_ext16u_i32(t2, b); ++ tcg_gen_sub_i32(t, t1, t2); ++} ++ ++static void gen_vsubwev_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1, t2; ++ ++ t1 = tcg_temp_new_i64(); ++ t2 = tcg_temp_new_i64(); ++ tcg_gen_ext32u_i64(t1, a); ++ tcg_gen_ext32u_i64(t2, b); ++ tcg_gen_sub_i64(t, t1, t2); ++} ++ ++static void do_vsubwev_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_sub_vec, 0 ++ }; ++ static const GVecGen3 op[4] = { ++ { ++ .fniv = gen_vsubwev_u, ++ .fno = gen_helper_vsubwev_h_bu, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vsubwev_w_hu, ++ .fniv = gen_vsubwev_u, ++ .fno = gen_helper_vsubwev_w_hu, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vsubwev_d_wu, ++ .fniv = gen_vsubwev_u, ++ .fno = gen_helper_vsubwev_d_wu, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ { ++ .fno = gen_helper_vsubwev_q_du, ++ .vece = MO_128 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vsubwev_h_bu, LSX, gvec_vvv, MO_8, do_vsubwev_u) ++TRANS(vsubwev_w_hu, LSX, gvec_vvv, MO_16, do_vsubwev_u) ++TRANS(vsubwev_d_wu, LSX, gvec_vvv, MO_32, do_vsubwev_u) ++TRANS(vsubwev_q_du, LSX, gvec_vvv, MO_64, do_vsubwev_u) ++TRANS(xvsubwev_h_bu, LASX, gvec_xxx, MO_8, do_vsubwev_u) ++TRANS(xvsubwev_w_hu, LASX, gvec_xxx, MO_16, do_vsubwev_u) ++TRANS(xvsubwev_d_wu, LASX, gvec_xxx, MO_32, do_vsubwev_u) ++TRANS(xvsubwev_q_du, LASX, gvec_xxx, MO_64, do_vsubwev_u) ++ ++static void gen_vsubwod_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, t2; ++ ++ int halfbits = 4 << vece; ++ ++ t1 = tcg_temp_new_vec_matching(a); ++ t2 = tcg_temp_new_vec_matching(b); ++ ++ /* Zero-extend the odd elements for vector */ ++ tcg_gen_shri_vec(vece, t1, a, halfbits); ++ tcg_gen_shri_vec(vece, t2, b, halfbits); ++ ++ tcg_gen_sub_vec(vece, t, t1, t2); ++} ++ ++static void gen_vsubwod_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1, t2; ++ ++ t1 = tcg_temp_new_i32(); ++ t2 = tcg_temp_new_i32(); ++ tcg_gen_shri_i32(t1, a, 16); ++ tcg_gen_shri_i32(t2, b, 16); ++ tcg_gen_sub_i32(t, t1, t2); ++} ++ ++static void gen_vsubwod_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1, t2; ++ ++ t1 = tcg_temp_new_i64(); ++ t2 = tcg_temp_new_i64(); ++ tcg_gen_shri_i64(t1, a, 32); ++ tcg_gen_shri_i64(t2, b, 32); ++ tcg_gen_sub_i64(t, t1, t2); ++} ++ ++static void do_vsubwod_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_shri_vec, INDEX_op_sub_vec, 0 ++ }; ++ static const GVecGen3 op[4] = { ++ { ++ .fniv = gen_vsubwod_u, ++ .fno = gen_helper_vsubwod_h_bu, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vsubwod_w_hu, ++ .fniv = gen_vsubwod_u, ++ .fno = gen_helper_vsubwod_w_hu, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vsubwod_d_wu, ++ .fniv = gen_vsubwod_u, ++ .fno = gen_helper_vsubwod_d_wu, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ { ++ .fno = gen_helper_vsubwod_q_du, ++ .vece = MO_128 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vsubwod_h_bu, LSX, gvec_vvv, MO_8, do_vsubwod_u) ++TRANS(vsubwod_w_hu, LSX, gvec_vvv, MO_16, do_vsubwod_u) ++TRANS(vsubwod_d_wu, LSX, gvec_vvv, MO_32, do_vsubwod_u) ++TRANS(vsubwod_q_du, LSX, gvec_vvv, MO_64, do_vsubwod_u) ++TRANS(xvsubwod_h_bu, LASX, gvec_xxx, MO_8, do_vsubwod_u) ++TRANS(xvsubwod_w_hu, LASX, gvec_xxx, MO_16, do_vsubwod_u) ++TRANS(xvsubwod_d_wu, LASX, gvec_xxx, MO_32, do_vsubwod_u) ++TRANS(xvsubwod_q_du, LASX, gvec_xxx, MO_64, do_vsubwod_u) ++ ++static void gen_vaddwev_u_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, t2, t3; ++ ++ int halfbits = 4 << vece; ++ ++ t1 = tcg_temp_new_vec_matching(a); ++ t2 = tcg_temp_new_vec_matching(b); ++ t3 = tcg_constant_vec_matching(t, vece, MAKE_64BIT_MASK(0, halfbits)); ++ ++ /* Zero-extend the even elements from a */ ++ tcg_gen_and_vec(vece, t1, a, t3); ++ ++ /* Sign-extend the even elements from b */ ++ tcg_gen_shli_vec(vece, t2, b, halfbits); ++ tcg_gen_sari_vec(vece, t2, t2, halfbits); ++ ++ tcg_gen_add_vec(vece, t, t1, t2); ++} ++ ++static void gen_vaddwev_w_hu_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1, t2; ++ ++ t1 = tcg_temp_new_i32(); ++ t2 = tcg_temp_new_i32(); ++ tcg_gen_ext16u_i32(t1, a); ++ tcg_gen_ext16s_i32(t2, b); ++ tcg_gen_add_i32(t, t1, t2); ++} ++ ++static void gen_vaddwev_d_wu_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1, t2; ++ ++ t1 = tcg_temp_new_i64(); ++ t2 = tcg_temp_new_i64(); ++ tcg_gen_ext32u_i64(t1, a); ++ tcg_gen_ext32s_i64(t2, b); ++ tcg_gen_add_i64(t, t1, t2); ++} ++ ++static void do_vaddwev_u_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_shli_vec, INDEX_op_sari_vec, INDEX_op_add_vec, 0 ++ }; ++ static const GVecGen3 op[4] = { ++ { ++ .fniv = gen_vaddwev_u_s, ++ .fno = gen_helper_vaddwev_h_bu_b, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vaddwev_w_hu_h, ++ .fniv = gen_vaddwev_u_s, ++ .fno = gen_helper_vaddwev_w_hu_h, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vaddwev_d_wu_w, ++ .fniv = gen_vaddwev_u_s, ++ .fno = gen_helper_vaddwev_d_wu_w, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ { ++ .fno = gen_helper_vaddwev_q_du_d, ++ .vece = MO_128 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vaddwev_h_bu_b, LSX, gvec_vvv, MO_8, do_vaddwev_u_s) ++TRANS(vaddwev_w_hu_h, LSX, gvec_vvv, MO_16, do_vaddwev_u_s) ++TRANS(vaddwev_d_wu_w, LSX, gvec_vvv, MO_32, do_vaddwev_u_s) ++TRANS(vaddwev_q_du_d, LSX, gvec_vvv, MO_64, do_vaddwev_u_s) ++TRANS(xvaddwev_h_bu_b, LASX, gvec_xxx, MO_8, do_vaddwev_u_s) ++TRANS(xvaddwev_w_hu_h, LASX, gvec_xxx, MO_16, do_vaddwev_u_s) ++TRANS(xvaddwev_d_wu_w, LASX, gvec_xxx, MO_32, do_vaddwev_u_s) ++TRANS(xvaddwev_q_du_d, LASX, gvec_xxx, MO_64, do_vaddwev_u_s) ++ ++static void gen_vaddwod_u_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, t2; ++ ++ int halfbits = 4 << vece; ++ ++ t1 = tcg_temp_new_vec_matching(a); ++ t2 = tcg_temp_new_vec_matching(b); ++ ++ /* Zero-extend the odd elements from a */ ++ tcg_gen_shri_vec(vece, t1, a, halfbits); ++ /* Sign-extend the odd elements from b */ ++ tcg_gen_sari_vec(vece, t2, b, halfbits); ++ ++ tcg_gen_add_vec(vece, t, t1, t2); ++} ++ ++static void gen_vaddwod_w_hu_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1, t2; ++ ++ t1 = tcg_temp_new_i32(); ++ t2 = tcg_temp_new_i32(); ++ tcg_gen_shri_i32(t1, a, 16); ++ tcg_gen_sari_i32(t2, b, 16); ++ tcg_gen_add_i32(t, t1, t2); ++} ++ ++static void gen_vaddwod_d_wu_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1, t2; ++ ++ t1 = tcg_temp_new_i64(); ++ t2 = tcg_temp_new_i64(); ++ tcg_gen_shri_i64(t1, a, 32); ++ tcg_gen_sari_i64(t2, b, 32); ++ tcg_gen_add_i64(t, t1, t2); ++} ++ ++static void do_vaddwod_u_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_shri_vec, INDEX_op_sari_vec, INDEX_op_add_vec, 0 ++ }; ++ static const GVecGen3 op[4] = { ++ { ++ .fniv = gen_vaddwod_u_s, ++ .fno = gen_helper_vaddwod_h_bu_b, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vaddwod_w_hu_h, ++ .fniv = gen_vaddwod_u_s, ++ .fno = gen_helper_vaddwod_w_hu_h, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vaddwod_d_wu_w, ++ .fniv = gen_vaddwod_u_s, ++ .fno = gen_helper_vaddwod_d_wu_w, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ { ++ .fno = gen_helper_vaddwod_q_du_d, ++ .vece = MO_128 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vaddwod_h_bu_b, LSX, gvec_vvv, MO_8, do_vaddwod_u_s) ++TRANS(vaddwod_w_hu_h, LSX, gvec_vvv, MO_16, do_vaddwod_u_s) ++TRANS(vaddwod_d_wu_w, LSX, gvec_vvv, MO_32, do_vaddwod_u_s) ++TRANS(vaddwod_q_du_d, LSX, gvec_vvv, MO_64, do_vaddwod_u_s) ++TRANS(xvaddwod_h_bu_b, LSX, gvec_xxx, MO_8, do_vaddwod_u_s) ++TRANS(xvaddwod_w_hu_h, LSX, gvec_xxx, MO_16, do_vaddwod_u_s) ++TRANS(xvaddwod_d_wu_w, LSX, gvec_xxx, MO_32, do_vaddwod_u_s) ++TRANS(xvaddwod_q_du_d, LSX, gvec_xxx, MO_64, do_vaddwod_u_s) ++ ++static void do_vavg(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b, ++ void (*gen_shr_vec)(unsigned, TCGv_vec, ++ TCGv_vec, int64_t), ++ void (*gen_round_vec)(unsigned, TCGv_vec, ++ TCGv_vec, TCGv_vec)) ++{ ++ TCGv_vec tmp = tcg_temp_new_vec_matching(t); ++ gen_round_vec(vece, tmp, a, b); ++ tcg_gen_and_vec(vece, tmp, tmp, tcg_constant_vec_matching(t, vece, 1)); ++ gen_shr_vec(vece, a, a, 1); ++ gen_shr_vec(vece, b, b, 1); ++ tcg_gen_add_vec(vece, t, a, b); ++ tcg_gen_add_vec(vece, t, t, tmp); ++} ++ ++static void gen_vavg_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ do_vavg(vece, t, a, b, tcg_gen_sari_vec, tcg_gen_and_vec); ++} ++ ++static void gen_vavg_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ do_vavg(vece, t, a, b, tcg_gen_shri_vec, tcg_gen_and_vec); ++} ++ ++static void gen_vavgr_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ do_vavg(vece, t, a, b, tcg_gen_sari_vec, tcg_gen_or_vec); ++} ++ ++static void gen_vavgr_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ do_vavg(vece, t, a, b, tcg_gen_shri_vec, tcg_gen_or_vec); ++} ++ ++static void do_vavg_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_sari_vec, INDEX_op_add_vec, 0 ++ }; ++ static const GVecGen3 op[4] = { ++ { ++ .fniv = gen_vavg_s, ++ .fno = gen_helper_vavg_b, ++ .opt_opc = vecop_list, ++ .vece = MO_8 ++ }, ++ { ++ .fniv = gen_vavg_s, ++ .fno = gen_helper_vavg_h, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fniv = gen_vavg_s, ++ .fno = gen_helper_vavg_w, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fniv = gen_vavg_s, ++ .fno = gen_helper_vavg_d, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++static void do_vavg_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_shri_vec, INDEX_op_add_vec, 0 ++ }; ++ static const GVecGen3 op[4] = { ++ { ++ .fniv = gen_vavg_u, ++ .fno = gen_helper_vavg_bu, ++ .opt_opc = vecop_list, ++ .vece = MO_8 ++ }, ++ { ++ .fniv = gen_vavg_u, ++ .fno = gen_helper_vavg_hu, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fniv = gen_vavg_u, ++ .fno = gen_helper_vavg_wu, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fniv = gen_vavg_u, ++ .fno = gen_helper_vavg_du, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vavg_b, LSX, gvec_vvv, MO_8, do_vavg_s) ++TRANS(vavg_h, LSX, gvec_vvv, MO_16, do_vavg_s) ++TRANS(vavg_w, LSX, gvec_vvv, MO_32, do_vavg_s) ++TRANS(vavg_d, LSX, gvec_vvv, MO_64, do_vavg_s) ++TRANS(vavg_bu, LSX, gvec_vvv, MO_8, do_vavg_u) ++TRANS(vavg_hu, LSX, gvec_vvv, MO_16, do_vavg_u) ++TRANS(vavg_wu, LSX, gvec_vvv, MO_32, do_vavg_u) ++TRANS(vavg_du, LSX, gvec_vvv, MO_64, do_vavg_u) ++TRANS(xvavg_b, LASX, gvec_xxx, MO_8, do_vavg_s) ++TRANS(xvavg_h, LASX, gvec_xxx, MO_16, do_vavg_s) ++TRANS(xvavg_w, LASX, gvec_xxx, MO_32, do_vavg_s) ++TRANS(xvavg_d, LASX, gvec_xxx, MO_64, do_vavg_s) ++TRANS(xvavg_bu, LASX, gvec_xxx, MO_8, do_vavg_u) ++TRANS(xvavg_hu, LASX, gvec_xxx, MO_16, do_vavg_u) ++TRANS(xvavg_wu, LASX, gvec_xxx, MO_32, do_vavg_u) ++TRANS(xvavg_du, LASX, gvec_xxx, MO_64, do_vavg_u) ++ ++static void do_vavgr_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_sari_vec, INDEX_op_add_vec, 0 ++ }; ++ static const GVecGen3 op[4] = { ++ { ++ .fniv = gen_vavgr_s, ++ .fno = gen_helper_vavgr_b, ++ .opt_opc = vecop_list, ++ .vece = MO_8 ++ }, ++ { ++ .fniv = gen_vavgr_s, ++ .fno = gen_helper_vavgr_h, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fniv = gen_vavgr_s, ++ .fno = gen_helper_vavgr_w, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fniv = gen_vavgr_s, ++ .fno = gen_helper_vavgr_d, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++static void do_vavgr_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_shri_vec, INDEX_op_add_vec, 0 ++ }; ++ static const GVecGen3 op[4] = { ++ { ++ .fniv = gen_vavgr_u, ++ .fno = gen_helper_vavgr_bu, ++ .opt_opc = vecop_list, ++ .vece = MO_8 ++ }, ++ { ++ .fniv = gen_vavgr_u, ++ .fno = gen_helper_vavgr_hu, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fniv = gen_vavgr_u, ++ .fno = gen_helper_vavgr_wu, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fniv = gen_vavgr_u, ++ .fno = gen_helper_vavgr_du, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vavgr_b, LSX, gvec_vvv, MO_8, do_vavgr_s) ++TRANS(vavgr_h, LSX, gvec_vvv, MO_16, do_vavgr_s) ++TRANS(vavgr_w, LSX, gvec_vvv, MO_32, do_vavgr_s) ++TRANS(vavgr_d, LSX, gvec_vvv, MO_64, do_vavgr_s) ++TRANS(vavgr_bu, LSX, gvec_vvv, MO_8, do_vavgr_u) ++TRANS(vavgr_hu, LSX, gvec_vvv, MO_16, do_vavgr_u) ++TRANS(vavgr_wu, LSX, gvec_vvv, MO_32, do_vavgr_u) ++TRANS(vavgr_du, LSX, gvec_vvv, MO_64, do_vavgr_u) ++TRANS(xvavgr_b, LASX, gvec_xxx, MO_8, do_vavgr_s) ++TRANS(xvavgr_h, LASX, gvec_xxx, MO_16, do_vavgr_s) ++TRANS(xvavgr_w, LASX, gvec_xxx, MO_32, do_vavgr_s) ++TRANS(xvavgr_d, LASX, gvec_xxx, MO_64, do_vavgr_s) ++TRANS(xvavgr_bu, LASX, gvec_xxx, MO_8, do_vavgr_u) ++TRANS(xvavgr_hu, LASX, gvec_xxx, MO_16, do_vavgr_u) ++TRANS(xvavgr_wu, LASX, gvec_xxx, MO_32, do_vavgr_u) ++TRANS(xvavgr_du, LASX, gvec_xxx, MO_64, do_vavgr_u) ++ ++static void gen_vabsd_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ tcg_gen_smax_vec(vece, t, a, b); ++ tcg_gen_smin_vec(vece, a, a, b); ++ tcg_gen_sub_vec(vece, t, t, a); ++} ++ ++static void do_vabsd_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_smax_vec, INDEX_op_smin_vec, INDEX_op_sub_vec, 0 ++ }; ++ static const GVecGen3 op[4] = { ++ { ++ .fniv = gen_vabsd_s, ++ .fno = gen_helper_vabsd_b, ++ .opt_opc = vecop_list, ++ .vece = MO_8 ++ }, ++ { ++ .fniv = gen_vabsd_s, ++ .fno = gen_helper_vabsd_h, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fniv = gen_vabsd_s, ++ .fno = gen_helper_vabsd_w, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fniv = gen_vabsd_s, ++ .fno = gen_helper_vabsd_d, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++static void gen_vabsd_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ tcg_gen_umax_vec(vece, t, a, b); ++ tcg_gen_umin_vec(vece, a, a, b); ++ tcg_gen_sub_vec(vece, t, t, a); ++} ++ ++static void do_vabsd_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_umax_vec, INDEX_op_umin_vec, INDEX_op_sub_vec, 0 ++ }; ++ static const GVecGen3 op[4] = { ++ { ++ .fniv = gen_vabsd_u, ++ .fno = gen_helper_vabsd_bu, ++ .opt_opc = vecop_list, ++ .vece = MO_8 ++ }, ++ { ++ .fniv = gen_vabsd_u, ++ .fno = gen_helper_vabsd_hu, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fniv = gen_vabsd_u, ++ .fno = gen_helper_vabsd_wu, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fniv = gen_vabsd_u, ++ .fno = gen_helper_vabsd_du, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vabsd_b, LSX, gvec_vvv, MO_8, do_vabsd_s) ++TRANS(vabsd_h, LSX, gvec_vvv, MO_16, do_vabsd_s) ++TRANS(vabsd_w, LSX, gvec_vvv, MO_32, do_vabsd_s) ++TRANS(vabsd_d, LSX, gvec_vvv, MO_64, do_vabsd_s) ++TRANS(vabsd_bu, LSX, gvec_vvv, MO_8, do_vabsd_u) ++TRANS(vabsd_hu, LSX, gvec_vvv, MO_16, do_vabsd_u) ++TRANS(vabsd_wu, LSX, gvec_vvv, MO_32, do_vabsd_u) ++TRANS(vabsd_du, LSX, gvec_vvv, MO_64, do_vabsd_u) ++TRANS(xvabsd_b, LASX, gvec_xxx, MO_8, do_vabsd_s) ++TRANS(xvabsd_h, LASX, gvec_xxx, MO_16, do_vabsd_s) ++TRANS(xvabsd_w, LASX, gvec_xxx, MO_32, do_vabsd_s) ++TRANS(xvabsd_d, LASX, gvec_xxx, MO_64, do_vabsd_s) ++TRANS(xvabsd_bu, LASX, gvec_xxx, MO_8, do_vabsd_u) ++TRANS(xvabsd_hu, LASX, gvec_xxx, MO_16, do_vabsd_u) ++TRANS(xvabsd_wu, LASX, gvec_xxx, MO_32, do_vabsd_u) ++TRANS(xvabsd_du, LASX, gvec_xxx, MO_64, do_vabsd_u) ++ ++static void gen_vadda(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, t2; ++ ++ t1 = tcg_temp_new_vec_matching(a); ++ t2 = tcg_temp_new_vec_matching(b); ++ ++ tcg_gen_abs_vec(vece, t1, a); ++ tcg_gen_abs_vec(vece, t2, b); ++ tcg_gen_add_vec(vece, t, t1, t2); ++} ++ ++static void do_vadda(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_abs_vec, INDEX_op_add_vec, 0 ++ }; ++ static const GVecGen3 op[4] = { ++ { ++ .fniv = gen_vadda, ++ .fno = gen_helper_vadda_b, ++ .opt_opc = vecop_list, ++ .vece = MO_8 ++ }, ++ { ++ .fniv = gen_vadda, ++ .fno = gen_helper_vadda_h, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fniv = gen_vadda, ++ .fno = gen_helper_vadda_w, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fniv = gen_vadda, ++ .fno = gen_helper_vadda_d, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vadda_b, LSX, gvec_vvv, MO_8, do_vadda) ++TRANS(vadda_h, LSX, gvec_vvv, MO_16, do_vadda) ++TRANS(vadda_w, LSX, gvec_vvv, MO_32, do_vadda) ++TRANS(vadda_d, LSX, gvec_vvv, MO_64, do_vadda) ++TRANS(xvadda_b, LASX, gvec_xxx, MO_8, do_vadda) ++TRANS(xvadda_h, LASX, gvec_xxx, MO_16, do_vadda) ++TRANS(xvadda_w, LASX, gvec_xxx, MO_32, do_vadda) ++TRANS(xvadda_d, LASX, gvec_xxx, MO_64, do_vadda) ++ ++TRANS(vmax_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_smax) ++TRANS(vmax_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_smax) ++TRANS(vmax_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_smax) ++TRANS(vmax_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_smax) ++TRANS(vmax_bu, LSX, gvec_vvv, MO_8, tcg_gen_gvec_umax) ++TRANS(vmax_hu, LSX, gvec_vvv, MO_16, tcg_gen_gvec_umax) ++TRANS(vmax_wu, LSX, gvec_vvv, MO_32, tcg_gen_gvec_umax) ++TRANS(vmax_du, LSX, gvec_vvv, MO_64, tcg_gen_gvec_umax) ++TRANS(xvmax_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_smax) ++TRANS(xvmax_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_smax) ++TRANS(xvmax_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_smax) ++TRANS(xvmax_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_smax) ++TRANS(xvmax_bu, LASX, gvec_xxx, MO_8, tcg_gen_gvec_umax) ++TRANS(xvmax_hu, LASX, gvec_xxx, MO_16, tcg_gen_gvec_umax) ++TRANS(xvmax_wu, LASX, gvec_xxx, MO_32, tcg_gen_gvec_umax) ++TRANS(xvmax_du, LASX, gvec_xxx, MO_64, tcg_gen_gvec_umax) ++ ++TRANS(vmin_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_smin) ++TRANS(vmin_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_smin) ++TRANS(vmin_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_smin) ++TRANS(vmin_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_smin) ++TRANS(vmin_bu, LSX, gvec_vvv, MO_8, tcg_gen_gvec_umin) ++TRANS(vmin_hu, LSX, gvec_vvv, MO_16, tcg_gen_gvec_umin) ++TRANS(vmin_wu, LSX, gvec_vvv, MO_32, tcg_gen_gvec_umin) ++TRANS(vmin_du, LSX, gvec_vvv, MO_64, tcg_gen_gvec_umin) ++TRANS(xvmin_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_smin) ++TRANS(xvmin_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_smin) ++TRANS(xvmin_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_smin) ++TRANS(xvmin_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_smin) ++TRANS(xvmin_bu, LASX, gvec_xxx, MO_8, tcg_gen_gvec_umin) ++TRANS(xvmin_hu, LASX, gvec_xxx, MO_16, tcg_gen_gvec_umin) ++TRANS(xvmin_wu, LASX, gvec_xxx, MO_32, tcg_gen_gvec_umin) ++TRANS(xvmin_du, LASX, gvec_xxx, MO_64, tcg_gen_gvec_umin) ++ ++static void gen_vmini_s(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) ++{ ++ tcg_gen_smin_vec(vece, t, a, tcg_constant_vec_matching(t, vece, imm)); ++} ++ ++static void gen_vmini_u(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) ++{ ++ tcg_gen_umin_vec(vece, t, a, tcg_constant_vec_matching(t, vece, imm)); ++} ++ ++static void gen_vmaxi_s(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) ++{ ++ tcg_gen_smax_vec(vece, t, a, tcg_constant_vec_matching(t, vece, imm)); ++} ++ ++static void gen_vmaxi_u(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) ++{ ++ tcg_gen_umax_vec(vece, t, a, tcg_constant_vec_matching(t, vece, imm)); ++} ++ ++static void do_vmini_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ int64_t imm, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_smin_vec, 0 ++ }; ++ static const GVecGen2i op[4] = { ++ { ++ .fniv = gen_vmini_s, ++ .fnoi = gen_helper_vmini_b, ++ .opt_opc = vecop_list, ++ .vece = MO_8 ++ }, ++ { ++ .fniv = gen_vmini_s, ++ .fnoi = gen_helper_vmini_h, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fniv = gen_vmini_s, ++ .fnoi = gen_helper_vmini_w, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fniv = gen_vmini_s, ++ .fnoi = gen_helper_vmini_d, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op[vece]); ++} ++ ++static void do_vmini_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ int64_t imm, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_umin_vec, 0 ++ }; ++ static const GVecGen2i op[4] = { ++ { ++ .fniv = gen_vmini_u, ++ .fnoi = gen_helper_vmini_bu, ++ .opt_opc = vecop_list, ++ .vece = MO_8 ++ }, ++ { ++ .fniv = gen_vmini_u, ++ .fnoi = gen_helper_vmini_hu, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fniv = gen_vmini_u, ++ .fnoi = gen_helper_vmini_wu, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fniv = gen_vmini_u, ++ .fnoi = gen_helper_vmini_du, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op[vece]); ++} ++ ++TRANS(vmini_b, LSX, gvec_vv_i, MO_8, do_vmini_s) ++TRANS(vmini_h, LSX, gvec_vv_i, MO_16, do_vmini_s) ++TRANS(vmini_w, LSX, gvec_vv_i, MO_32, do_vmini_s) ++TRANS(vmini_d, LSX, gvec_vv_i, MO_64, do_vmini_s) ++TRANS(vmini_bu, LSX, gvec_vv_i, MO_8, do_vmini_u) ++TRANS(vmini_hu, LSX, gvec_vv_i, MO_16, do_vmini_u) ++TRANS(vmini_wu, LSX, gvec_vv_i, MO_32, do_vmini_u) ++TRANS(vmini_du, LSX, gvec_vv_i, MO_64, do_vmini_u) ++TRANS(xvmini_b, LASX, gvec_xx_i, MO_8, do_vmini_s) ++TRANS(xvmini_h, LASX, gvec_xx_i, MO_16, do_vmini_s) ++TRANS(xvmini_w, LASX, gvec_xx_i, MO_32, do_vmini_s) ++TRANS(xvmini_d, LASX, gvec_xx_i, MO_64, do_vmini_s) ++TRANS(xvmini_bu, LASX, gvec_xx_i, MO_8, do_vmini_u) ++TRANS(xvmini_hu, LASX, gvec_xx_i, MO_16, do_vmini_u) ++TRANS(xvmini_wu, LASX, gvec_xx_i, MO_32, do_vmini_u) ++TRANS(xvmini_du, LASX, gvec_xx_i, MO_64, do_vmini_u) ++ ++static void do_vmaxi_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ int64_t imm, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_smax_vec, 0 ++ }; ++ static const GVecGen2i op[4] = { ++ { ++ .fniv = gen_vmaxi_s, ++ .fnoi = gen_helper_vmaxi_b, ++ .opt_opc = vecop_list, ++ .vece = MO_8 ++ }, ++ { ++ .fniv = gen_vmaxi_s, ++ .fnoi = gen_helper_vmaxi_h, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fniv = gen_vmaxi_s, ++ .fnoi = gen_helper_vmaxi_w, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fniv = gen_vmaxi_s, ++ .fnoi = gen_helper_vmaxi_d, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op[vece]); ++} ++ ++static void do_vmaxi_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ int64_t imm, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_umax_vec, 0 ++ }; ++ static const GVecGen2i op[4] = { ++ { ++ .fniv = gen_vmaxi_u, ++ .fnoi = gen_helper_vmaxi_bu, ++ .opt_opc = vecop_list, ++ .vece = MO_8 ++ }, ++ { ++ .fniv = gen_vmaxi_u, ++ .fnoi = gen_helper_vmaxi_hu, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fniv = gen_vmaxi_u, ++ .fnoi = gen_helper_vmaxi_wu, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fniv = gen_vmaxi_u, ++ .fnoi = gen_helper_vmaxi_du, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op[vece]); ++} ++ ++TRANS(vmaxi_b, LSX, gvec_vv_i, MO_8, do_vmaxi_s) ++TRANS(vmaxi_h, LSX, gvec_vv_i, MO_16, do_vmaxi_s) ++TRANS(vmaxi_w, LSX, gvec_vv_i, MO_32, do_vmaxi_s) ++TRANS(vmaxi_d, LSX, gvec_vv_i, MO_64, do_vmaxi_s) ++TRANS(vmaxi_bu, LSX, gvec_vv_i, MO_8, do_vmaxi_u) ++TRANS(vmaxi_hu, LSX, gvec_vv_i, MO_16, do_vmaxi_u) ++TRANS(vmaxi_wu, LSX, gvec_vv_i, MO_32, do_vmaxi_u) ++TRANS(vmaxi_du, LSX, gvec_vv_i, MO_64, do_vmaxi_u) ++TRANS(xvmaxi_b, LASX, gvec_xx_i, MO_8, do_vmaxi_s) ++TRANS(xvmaxi_h, LASX, gvec_xx_i, MO_16, do_vmaxi_s) ++TRANS(xvmaxi_w, LASX, gvec_xx_i, MO_32, do_vmaxi_s) ++TRANS(xvmaxi_d, LASX, gvec_xx_i, MO_64, do_vmaxi_s) ++TRANS(xvmaxi_bu, LASX, gvec_xx_i, MO_8, do_vmaxi_u) ++TRANS(xvmaxi_hu, LASX, gvec_xx_i, MO_16, do_vmaxi_u) ++TRANS(xvmaxi_wu, LASX, gvec_xx_i, MO_32, do_vmaxi_u) ++TRANS(xvmaxi_du, LASX, gvec_xx_i, MO_64, do_vmaxi_u) ++ ++TRANS(vmul_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_mul) ++TRANS(vmul_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_mul) ++TRANS(vmul_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_mul) ++TRANS(vmul_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_mul) ++TRANS(xvmul_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_mul) ++TRANS(xvmul_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_mul) ++TRANS(xvmul_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_mul) ++TRANS(xvmul_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_mul) ++ ++static void gen_vmuh_w(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 discard = tcg_temp_new_i32(); ++ tcg_gen_muls2_i32(discard, t, a, b); ++} ++ ++static void gen_vmuh_d(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 discard = tcg_temp_new_i64(); ++ tcg_gen_muls2_i64(discard, t, a, b); ++} ++ ++static void do_vmuh_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const GVecGen3 op[4] = { ++ { ++ .fno = gen_helper_vmuh_b, ++ .vece = MO_8 ++ }, ++ { ++ .fno = gen_helper_vmuh_h, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vmuh_w, ++ .fno = gen_helper_vmuh_w, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vmuh_d, ++ .fno = gen_helper_vmuh_d, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vmuh_b, LSX, gvec_vvv, MO_8, do_vmuh_s) ++TRANS(vmuh_h, LSX, gvec_vvv, MO_16, do_vmuh_s) ++TRANS(vmuh_w, LSX, gvec_vvv, MO_32, do_vmuh_s) ++TRANS(vmuh_d, LSX, gvec_vvv, MO_64, do_vmuh_s) ++TRANS(xvmuh_b, LASX, gvec_xxx, MO_8, do_vmuh_s) ++TRANS(xvmuh_h, LASX, gvec_xxx, MO_16, do_vmuh_s) ++TRANS(xvmuh_w, LASX, gvec_xxx, MO_32, do_vmuh_s) ++TRANS(xvmuh_d, LASX, gvec_xxx, MO_64, do_vmuh_s) ++ ++static void gen_vmuh_wu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 discard = tcg_temp_new_i32(); ++ tcg_gen_mulu2_i32(discard, t, a, b); ++} ++ ++static void gen_vmuh_du(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 discard = tcg_temp_new_i64(); ++ tcg_gen_mulu2_i64(discard, t, a, b); ++} ++ ++static void do_vmuh_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const GVecGen3 op[4] = { ++ { ++ .fno = gen_helper_vmuh_bu, ++ .vece = MO_8 ++ }, ++ { ++ .fno = gen_helper_vmuh_hu, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vmuh_wu, ++ .fno = gen_helper_vmuh_wu, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vmuh_du, ++ .fno = gen_helper_vmuh_du, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vmuh_bu, LSX, gvec_vvv, MO_8, do_vmuh_u) ++TRANS(vmuh_hu, LSX, gvec_vvv, MO_16, do_vmuh_u) ++TRANS(vmuh_wu, LSX, gvec_vvv, MO_32, do_vmuh_u) ++TRANS(vmuh_du, LSX, gvec_vvv, MO_64, do_vmuh_u) ++TRANS(xvmuh_bu, LASX, gvec_xxx, MO_8, do_vmuh_u) ++TRANS(xvmuh_hu, LASX, gvec_xxx, MO_16, do_vmuh_u) ++TRANS(xvmuh_wu, LASX, gvec_xxx, MO_32, do_vmuh_u) ++TRANS(xvmuh_du, LASX, gvec_xxx, MO_64, do_vmuh_u) ++ ++static void gen_vmulwev_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, t2; ++ int halfbits = 4 << vece; ++ ++ t1 = tcg_temp_new_vec_matching(a); ++ t2 = tcg_temp_new_vec_matching(b); ++ tcg_gen_shli_vec(vece, t1, a, halfbits); ++ tcg_gen_sari_vec(vece, t1, t1, halfbits); ++ tcg_gen_shli_vec(vece, t2, b, halfbits); ++ tcg_gen_sari_vec(vece, t2, t2, halfbits); ++ tcg_gen_mul_vec(vece, t, t1, t2); ++} ++ ++static void gen_vmulwev_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1, t2; ++ ++ t1 = tcg_temp_new_i32(); ++ t2 = tcg_temp_new_i32(); ++ tcg_gen_ext16s_i32(t1, a); ++ tcg_gen_ext16s_i32(t2, b); ++ tcg_gen_mul_i32(t, t1, t2); ++} ++ ++static void gen_vmulwev_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1, t2; ++ ++ t1 = tcg_temp_new_i64(); ++ t2 = tcg_temp_new_i64(); ++ tcg_gen_ext32s_i64(t1, a); ++ tcg_gen_ext32s_i64(t2, b); ++ tcg_gen_mul_i64(t, t1, t2); ++} ++ ++static void do_vmulwev_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_shli_vec, INDEX_op_sari_vec, INDEX_op_mul_vec, 0 ++ }; ++ static const GVecGen3 op[3] = { ++ { ++ .fniv = gen_vmulwev_s, ++ .fno = gen_helper_vmulwev_h_b, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vmulwev_w_h, ++ .fniv = gen_vmulwev_s, ++ .fno = gen_helper_vmulwev_w_h, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vmulwev_d_w, ++ .fniv = gen_vmulwev_s, ++ .fno = gen_helper_vmulwev_d_w, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vmulwev_h_b, LSX, gvec_vvv, MO_8, do_vmulwev_s) ++TRANS(vmulwev_w_h, LSX, gvec_vvv, MO_16, do_vmulwev_s) ++TRANS(vmulwev_d_w, LSX, gvec_vvv, MO_32, do_vmulwev_s) ++TRANS(xvmulwev_h_b, LASX, gvec_xxx, MO_8, do_vmulwev_s) ++TRANS(xvmulwev_w_h, LASX, gvec_xxx, MO_16, do_vmulwev_s) ++TRANS(xvmulwev_d_w, LASX, gvec_xxx, MO_32, do_vmulwev_s) ++ ++static void tcg_gen_mulus2_i64(TCGv_i64 rl, TCGv_i64 rh, ++ TCGv_i64 arg1, TCGv_i64 arg2) ++{ ++ tcg_gen_mulsu2_i64(rl, rh, arg2, arg1); ++} ++ ++static bool gen_vmul_q_vl(DisasContext *ctx, ++ arg_vvv *a, uint32_t oprsz, int idx1, int idx2, ++ void (*func)(TCGv_i64, TCGv_i64, ++ TCGv_i64, TCGv_i64)) ++{ ++ TCGv_i64 rh, rl, arg1, arg2; ++ int i; ++ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ rh = tcg_temp_new_i64(); ++ rl = tcg_temp_new_i64(); ++ arg1 = tcg_temp_new_i64(); ++ arg2 = tcg_temp_new_i64(); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ get_vreg64(arg1, a->vj, 2 * i + idx1); ++ get_vreg64(arg2, a->vk, 2 * i + idx2); ++ ++ func(rl, rh, arg1, arg2); ++ ++ set_vreg64(rh, a->vd, 2 * i + 1); ++ set_vreg64(rl, a->vd, 2 * i); ++ } ++ ++ return true; ++} ++ ++static bool gen_vmul_q(DisasContext *ctx, arg_vvv *a, int idx1, int idx2, ++ void (*func)(TCGv_i64, TCGv_i64, ++ TCGv_i64, TCGv_i64)) ++{ ++ return gen_vmul_q_vl(ctx, a, 16, idx1, idx2, func); ++} ++ ++static bool gen_xvmul_q(DisasContext *ctx, arg_vvv *a, int idx1, int idx2, ++ void (*func)(TCGv_i64, TCGv_i64, ++ TCGv_i64, TCGv_i64)) ++{ ++ return gen_vmul_q_vl(ctx, a, 32, idx1, idx2, func); ++} ++ ++TRANS(vmulwev_q_d, LSX, gen_vmul_q, 0, 0, tcg_gen_muls2_i64) ++TRANS(vmulwod_q_d, LSX, gen_vmul_q, 1, 1, tcg_gen_muls2_i64) ++TRANS(vmulwev_q_du, LSX, gen_vmul_q, 0, 0, tcg_gen_mulu2_i64) ++TRANS(vmulwod_q_du, LSX, gen_vmul_q, 1, 1, tcg_gen_mulu2_i64) ++TRANS(vmulwev_q_du_d, LSX, gen_vmul_q, 0, 0, tcg_gen_mulus2_i64) ++TRANS(vmulwod_q_du_d, LSX, gen_vmul_q, 1, 1, tcg_gen_mulus2_i64) ++TRANS(xvmulwev_q_d, LASX, gen_xvmul_q, 0, 0, tcg_gen_muls2_i64) ++TRANS(xvmulwod_q_d, LASX, gen_xvmul_q, 1, 1, tcg_gen_muls2_i64) ++TRANS(xvmulwev_q_du, LASX, gen_xvmul_q, 0, 0, tcg_gen_mulu2_i64) ++TRANS(xvmulwod_q_du, LASX, gen_xvmul_q, 1, 1, tcg_gen_mulu2_i64) ++TRANS(xvmulwev_q_du_d, LASX, gen_xvmul_q, 0, 0, tcg_gen_mulus2_i64) ++TRANS(xvmulwod_q_du_d, LASX, gen_xvmul_q, 1, 1, tcg_gen_mulus2_i64) ++ ++static void gen_vmulwod_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, t2; ++ int halfbits = 4 << vece; ++ ++ t1 = tcg_temp_new_vec_matching(a); ++ t2 = tcg_temp_new_vec_matching(b); ++ tcg_gen_sari_vec(vece, t1, a, halfbits); ++ tcg_gen_sari_vec(vece, t2, b, halfbits); ++ tcg_gen_mul_vec(vece, t, t1, t2); ++} ++ ++static void gen_vmulwod_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1, t2; ++ ++ t1 = tcg_temp_new_i32(); ++ t2 = tcg_temp_new_i32(); ++ tcg_gen_sari_i32(t1, a, 16); ++ tcg_gen_sari_i32(t2, b, 16); ++ tcg_gen_mul_i32(t, t1, t2); ++} ++ ++static void gen_vmulwod_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1, t2; ++ ++ t1 = tcg_temp_new_i64(); ++ t2 = tcg_temp_new_i64(); ++ tcg_gen_sari_i64(t1, a, 32); ++ tcg_gen_sari_i64(t2, b, 32); ++ tcg_gen_mul_i64(t, t1, t2); ++} ++ ++static void do_vmulwod_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_sari_vec, INDEX_op_mul_vec, 0 ++ }; ++ static const GVecGen3 op[3] = { ++ { ++ .fniv = gen_vmulwod_s, ++ .fno = gen_helper_vmulwod_h_b, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vmulwod_w_h, ++ .fniv = gen_vmulwod_s, ++ .fno = gen_helper_vmulwod_w_h, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vmulwod_d_w, ++ .fniv = gen_vmulwod_s, ++ .fno = gen_helper_vmulwod_d_w, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vmulwod_h_b, LSX, gvec_vvv, MO_8, do_vmulwod_s) ++TRANS(vmulwod_w_h, LSX, gvec_vvv, MO_16, do_vmulwod_s) ++TRANS(vmulwod_d_w, LSX, gvec_vvv, MO_32, do_vmulwod_s) ++TRANS(xvmulwod_h_b, LASX, gvec_xxx, MO_8, do_vmulwod_s) ++TRANS(xvmulwod_w_h, LASX, gvec_xxx, MO_16, do_vmulwod_s) ++TRANS(xvmulwod_d_w, LASX, gvec_xxx, MO_32, do_vmulwod_s) ++ ++static void gen_vmulwev_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, t2, mask; ++ ++ t1 = tcg_temp_new_vec_matching(a); ++ t2 = tcg_temp_new_vec_matching(b); ++ mask = tcg_constant_vec_matching(t, vece, MAKE_64BIT_MASK(0, 4 << vece)); ++ tcg_gen_and_vec(vece, t1, a, mask); ++ tcg_gen_and_vec(vece, t2, b, mask); ++ tcg_gen_mul_vec(vece, t, t1, t2); ++} ++ ++static void gen_vmulwev_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1, t2; ++ ++ t1 = tcg_temp_new_i32(); ++ t2 = tcg_temp_new_i32(); ++ tcg_gen_ext16u_i32(t1, a); ++ tcg_gen_ext16u_i32(t2, b); ++ tcg_gen_mul_i32(t, t1, t2); ++} ++ ++static void gen_vmulwev_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1, t2; ++ ++ t1 = tcg_temp_new_i64(); ++ t2 = tcg_temp_new_i64(); ++ tcg_gen_ext32u_i64(t1, a); ++ tcg_gen_ext32u_i64(t2, b); ++ tcg_gen_mul_i64(t, t1, t2); ++} ++ ++static void do_vmulwev_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_mul_vec, 0 ++ }; ++ static const GVecGen3 op[3] = { ++ { ++ .fniv = gen_vmulwev_u, ++ .fno = gen_helper_vmulwev_h_bu, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vmulwev_w_hu, ++ .fniv = gen_vmulwev_u, ++ .fno = gen_helper_vmulwev_w_hu, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vmulwev_d_wu, ++ .fniv = gen_vmulwev_u, ++ .fno = gen_helper_vmulwev_d_wu, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vmulwev_h_bu, LSX, gvec_vvv, MO_8, do_vmulwev_u) ++TRANS(vmulwev_w_hu, LSX, gvec_vvv, MO_16, do_vmulwev_u) ++TRANS(vmulwev_d_wu, LSX, gvec_vvv, MO_32, do_vmulwev_u) ++TRANS(xvmulwev_h_bu, LASX, gvec_xxx, MO_8, do_vmulwev_u) ++TRANS(xvmulwev_w_hu, LASX, gvec_xxx, MO_16, do_vmulwev_u) ++TRANS(xvmulwev_d_wu, LASX, gvec_xxx, MO_32, do_vmulwev_u) ++ ++static void gen_vmulwod_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, t2; ++ int halfbits = 4 << vece; ++ ++ t1 = tcg_temp_new_vec_matching(a); ++ t2 = tcg_temp_new_vec_matching(b); ++ tcg_gen_shri_vec(vece, t1, a, halfbits); ++ tcg_gen_shri_vec(vece, t2, b, halfbits); ++ tcg_gen_mul_vec(vece, t, t1, t2); ++} ++ ++static void gen_vmulwod_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1, t2; ++ ++ t1 = tcg_temp_new_i32(); ++ t2 = tcg_temp_new_i32(); ++ tcg_gen_shri_i32(t1, a, 16); ++ tcg_gen_shri_i32(t2, b, 16); ++ tcg_gen_mul_i32(t, t1, t2); ++} ++ ++static void gen_vmulwod_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1, t2; ++ ++ t1 = tcg_temp_new_i64(); ++ t2 = tcg_temp_new_i64(); ++ tcg_gen_shri_i64(t1, a, 32); ++ tcg_gen_shri_i64(t2, b, 32); ++ tcg_gen_mul_i64(t, t1, t2); ++} ++ ++static void do_vmulwod_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_shri_vec, INDEX_op_mul_vec, 0 ++ }; ++ static const GVecGen3 op[3] = { ++ { ++ .fniv = gen_vmulwod_u, ++ .fno = gen_helper_vmulwod_h_bu, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vmulwod_w_hu, ++ .fniv = gen_vmulwod_u, ++ .fno = gen_helper_vmulwod_w_hu, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vmulwod_d_wu, ++ .fniv = gen_vmulwod_u, ++ .fno = gen_helper_vmulwod_d_wu, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vmulwod_h_bu, LSX, gvec_vvv, MO_8, do_vmulwod_u) ++TRANS(vmulwod_w_hu, LSX, gvec_vvv, MO_16, do_vmulwod_u) ++TRANS(vmulwod_d_wu, LSX, gvec_vvv, MO_32, do_vmulwod_u) ++TRANS(xvmulwod_h_bu, LASX, gvec_xxx, MO_8, do_vmulwod_u) ++TRANS(xvmulwod_w_hu, LASX, gvec_xxx, MO_16, do_vmulwod_u) ++TRANS(xvmulwod_d_wu, LASX, gvec_xxx, MO_32, do_vmulwod_u) ++ ++static void gen_vmulwev_u_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, t2, mask; ++ int halfbits = 4 << vece; ++ ++ t1 = tcg_temp_new_vec_matching(a); ++ t2 = tcg_temp_new_vec_matching(b); ++ mask = tcg_constant_vec_matching(t, vece, MAKE_64BIT_MASK(0, 4 << vece)); ++ tcg_gen_and_vec(vece, t1, a, mask); ++ tcg_gen_shli_vec(vece, t2, b, halfbits); ++ tcg_gen_sari_vec(vece, t2, t2, halfbits); ++ tcg_gen_mul_vec(vece, t, t1, t2); ++} ++ ++static void gen_vmulwev_w_hu_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1, t2; ++ ++ t1 = tcg_temp_new_i32(); ++ t2 = tcg_temp_new_i32(); ++ tcg_gen_ext16u_i32(t1, a); ++ tcg_gen_ext16s_i32(t2, b); ++ tcg_gen_mul_i32(t, t1, t2); ++} ++ ++static void gen_vmulwev_d_wu_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1, t2; ++ ++ t1 = tcg_temp_new_i64(); ++ t2 = tcg_temp_new_i64(); ++ tcg_gen_ext32u_i64(t1, a); ++ tcg_gen_ext32s_i64(t2, b); ++ tcg_gen_mul_i64(t, t1, t2); ++} ++ ++static void do_vmulwev_u_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_shli_vec, INDEX_op_sari_vec, INDEX_op_mul_vec, 0 ++ }; ++ static const GVecGen3 op[3] = { ++ { ++ .fniv = gen_vmulwev_u_s, ++ .fno = gen_helper_vmulwev_h_bu_b, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vmulwev_w_hu_h, ++ .fniv = gen_vmulwev_u_s, ++ .fno = gen_helper_vmulwev_w_hu_h, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vmulwev_d_wu_w, ++ .fniv = gen_vmulwev_u_s, ++ .fno = gen_helper_vmulwev_d_wu_w, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vmulwev_h_bu_b, LSX, gvec_vvv, MO_8, do_vmulwev_u_s) ++TRANS(vmulwev_w_hu_h, LSX, gvec_vvv, MO_16, do_vmulwev_u_s) ++TRANS(vmulwev_d_wu_w, LSX, gvec_vvv, MO_32, do_vmulwev_u_s) ++TRANS(xvmulwev_h_bu_b, LASX, gvec_xxx, MO_8, do_vmulwev_u_s) ++TRANS(xvmulwev_w_hu_h, LASX, gvec_xxx, MO_16, do_vmulwev_u_s) ++TRANS(xvmulwev_d_wu_w, LASX, gvec_xxx, MO_32, do_vmulwev_u_s) ++ ++static void gen_vmulwod_u_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, t2; ++ int halfbits = 4 << vece; ++ ++ t1 = tcg_temp_new_vec_matching(a); ++ t2 = tcg_temp_new_vec_matching(b); ++ tcg_gen_shri_vec(vece, t1, a, halfbits); ++ tcg_gen_sari_vec(vece, t2, b, halfbits); ++ tcg_gen_mul_vec(vece, t, t1, t2); ++} ++ ++static void gen_vmulwod_w_hu_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1, t2; ++ ++ t1 = tcg_temp_new_i32(); ++ t2 = tcg_temp_new_i32(); ++ tcg_gen_shri_i32(t1, a, 16); ++ tcg_gen_sari_i32(t2, b, 16); ++ tcg_gen_mul_i32(t, t1, t2); ++} ++static void gen_vmulwod_d_wu_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1, t2; ++ ++ t1 = tcg_temp_new_i64(); ++ t2 = tcg_temp_new_i64(); ++ tcg_gen_shri_i64(t1, a, 32); ++ tcg_gen_sari_i64(t2, b, 32); ++ tcg_gen_mul_i64(t, t1, t2); ++} ++ ++static void do_vmulwod_u_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_shri_vec, INDEX_op_sari_vec, INDEX_op_mul_vec, 0 ++ }; ++ static const GVecGen3 op[3] = { ++ { ++ .fniv = gen_vmulwod_u_s, ++ .fno = gen_helper_vmulwod_h_bu_b, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vmulwod_w_hu_h, ++ .fniv = gen_vmulwod_u_s, ++ .fno = gen_helper_vmulwod_w_hu_h, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vmulwod_d_wu_w, ++ .fniv = gen_vmulwod_u_s, ++ .fno = gen_helper_vmulwod_d_wu_w, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vmulwod_h_bu_b, LSX, gvec_vvv, MO_8, do_vmulwod_u_s) ++TRANS(vmulwod_w_hu_h, LSX, gvec_vvv, MO_16, do_vmulwod_u_s) ++TRANS(vmulwod_d_wu_w, LSX, gvec_vvv, MO_32, do_vmulwod_u_s) ++TRANS(xvmulwod_h_bu_b, LASX, gvec_xxx, MO_8, do_vmulwod_u_s) ++TRANS(xvmulwod_w_hu_h, LASX, gvec_xxx, MO_16, do_vmulwod_u_s) ++TRANS(xvmulwod_d_wu_w, LASX, gvec_xxx, MO_32, do_vmulwod_u_s) ++ ++static void gen_vmadd(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1; ++ ++ t1 = tcg_temp_new_vec_matching(t); ++ tcg_gen_mul_vec(vece, t1, a, b); ++ tcg_gen_add_vec(vece, t, t, t1); ++} ++ ++static void gen_vmadd_w(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1; ++ ++ t1 = tcg_temp_new_i32(); ++ tcg_gen_mul_i32(t1, a, b); ++ tcg_gen_add_i32(t, t, t1); ++} ++ ++static void gen_vmadd_d(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1; ++ ++ t1 = tcg_temp_new_i64(); ++ tcg_gen_mul_i64(t1, a, b); ++ tcg_gen_add_i64(t, t, t1); ++} ++ ++static void do_vmadd(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_mul_vec, INDEX_op_add_vec, 0 ++ }; ++ static const GVecGen3 op[4] = { ++ { ++ .fniv = gen_vmadd, ++ .fno = gen_helper_vmadd_b, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_8 ++ }, ++ { ++ .fniv = gen_vmadd, ++ .fno = gen_helper_vmadd_h, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vmadd_w, ++ .fniv = gen_vmadd, ++ .fno = gen_helper_vmadd_w, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vmadd_d, ++ .fniv = gen_vmadd, ++ .fno = gen_helper_vmadd_d, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vmadd_b, LSX, gvec_vvv, MO_8, do_vmadd) ++TRANS(vmadd_h, LSX, gvec_vvv, MO_16, do_vmadd) ++TRANS(vmadd_w, LSX, gvec_vvv, MO_32, do_vmadd) ++TRANS(vmadd_d, LSX, gvec_vvv, MO_64, do_vmadd) ++TRANS(xvmadd_b, LASX, gvec_xxx, MO_8, do_vmadd) ++TRANS(xvmadd_h, LASX, gvec_xxx, MO_16, do_vmadd) ++TRANS(xvmadd_w, LASX, gvec_xxx, MO_32, do_vmadd) ++TRANS(xvmadd_d, LASX, gvec_xxx, MO_64, do_vmadd) ++ ++static void gen_vmsub(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1; ++ ++ t1 = tcg_temp_new_vec_matching(t); ++ tcg_gen_mul_vec(vece, t1, a, b); ++ tcg_gen_sub_vec(vece, t, t, t1); ++} ++ ++static void gen_vmsub_w(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1; ++ ++ t1 = tcg_temp_new_i32(); ++ tcg_gen_mul_i32(t1, a, b); ++ tcg_gen_sub_i32(t, t, t1); ++} ++ ++static void gen_vmsub_d(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1; ++ ++ t1 = tcg_temp_new_i64(); ++ tcg_gen_mul_i64(t1, a, b); ++ tcg_gen_sub_i64(t, t, t1); ++} ++ ++static void do_vmsub(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_mul_vec, INDEX_op_sub_vec, 0 ++ }; ++ static const GVecGen3 op[4] = { ++ { ++ .fniv = gen_vmsub, ++ .fno = gen_helper_vmsub_b, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_8 ++ }, ++ { ++ .fniv = gen_vmsub, ++ .fno = gen_helper_vmsub_h, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vmsub_w, ++ .fniv = gen_vmsub, ++ .fno = gen_helper_vmsub_w, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vmsub_d, ++ .fniv = gen_vmsub, ++ .fno = gen_helper_vmsub_d, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vmsub_b, LSX, gvec_vvv, MO_8, do_vmsub) ++TRANS(vmsub_h, LSX, gvec_vvv, MO_16, do_vmsub) ++TRANS(vmsub_w, LSX, gvec_vvv, MO_32, do_vmsub) ++TRANS(vmsub_d, LSX, gvec_vvv, MO_64, do_vmsub) ++TRANS(xvmsub_b, LASX, gvec_xxx, MO_8, do_vmsub) ++TRANS(xvmsub_h, LASX, gvec_xxx, MO_16, do_vmsub) ++TRANS(xvmsub_w, LASX, gvec_xxx, MO_32, do_vmsub) ++TRANS(xvmsub_d, LASX, gvec_xxx, MO_64, do_vmsub) ++ ++static void gen_vmaddwev_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, t2, t3; ++ int halfbits = 4 << vece; ++ ++ t1 = tcg_temp_new_vec_matching(a); ++ t2 = tcg_temp_new_vec_matching(b); ++ t3 = tcg_temp_new_vec_matching(t); ++ tcg_gen_shli_vec(vece, t1, a, halfbits); ++ tcg_gen_sari_vec(vece, t1, t1, halfbits); ++ tcg_gen_shli_vec(vece, t2, b, halfbits); ++ tcg_gen_sari_vec(vece, t2, t2, halfbits); ++ tcg_gen_mul_vec(vece, t3, t1, t2); ++ tcg_gen_add_vec(vece, t, t, t3); ++} ++ ++static void gen_vmaddwev_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1; ++ ++ t1 = tcg_temp_new_i32(); ++ gen_vmulwev_w_h(t1, a, b); ++ tcg_gen_add_i32(t, t, t1); ++} ++ ++static void gen_vmaddwev_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1; ++ ++ t1 = tcg_temp_new_i64(); ++ gen_vmulwev_d_w(t1, a, b); ++ tcg_gen_add_i64(t, t, t1); ++} ++ ++static void do_vmaddwev_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_shli_vec, INDEX_op_sari_vec, ++ INDEX_op_mul_vec, INDEX_op_add_vec, 0 ++ }; ++ static const GVecGen3 op[3] = { ++ { ++ .fniv = gen_vmaddwev_s, ++ .fno = gen_helper_vmaddwev_h_b, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vmaddwev_w_h, ++ .fniv = gen_vmaddwev_s, ++ .fno = gen_helper_vmaddwev_w_h, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vmaddwev_d_w, ++ .fniv = gen_vmaddwev_s, ++ .fno = gen_helper_vmaddwev_d_w, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vmaddwev_h_b, LSX, gvec_vvv, MO_8, do_vmaddwev_s) ++TRANS(vmaddwev_w_h, LSX, gvec_vvv, MO_16, do_vmaddwev_s) ++TRANS(vmaddwev_d_w, LSX, gvec_vvv, MO_32, do_vmaddwev_s) ++TRANS(xvmaddwev_h_b, LASX, gvec_xxx, MO_8, do_vmaddwev_s) ++TRANS(xvmaddwev_w_h, LASX, gvec_xxx, MO_16, do_vmaddwev_s) ++TRANS(xvmaddwev_d_w, LASX, gvec_xxx, MO_32, do_vmaddwev_s) ++ ++static bool gen_vmadd_q_vl(DisasContext * ctx, ++ arg_vvv *a, uint32_t oprsz, int idx1, int idx2, ++ void (*func)(TCGv_i64, TCGv_i64, ++ TCGv_i64, TCGv_i64)) ++{ ++ TCGv_i64 rh, rl, arg1, arg2, th, tl; ++ int i; ++ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ rh = tcg_temp_new_i64(); ++ rl = tcg_temp_new_i64(); ++ arg1 = tcg_temp_new_i64(); ++ arg2 = tcg_temp_new_i64(); ++ th = tcg_temp_new_i64(); ++ tl = tcg_temp_new_i64(); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ get_vreg64(arg1, a->vj, 2 * i + idx1); ++ get_vreg64(arg2, a->vk, 2 * i + idx2); ++ get_vreg64(rh, a->vd, 2 * i + 1); ++ get_vreg64(rl, a->vd, 2 * i); ++ ++ func(tl, th, arg1, arg2); ++ tcg_gen_add2_i64(rl, rh, rl, rh, tl, th); ++ ++ set_vreg64(rh, a->vd, 2 * i + 1); ++ set_vreg64(rl, a->vd, 2 * i); ++ } ++ ++ return true; ++} ++ ++static bool gen_vmadd_q(DisasContext *ctx, arg_vvv *a, int idx1, int idx2, ++ void (*func)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) ++{ ++ return gen_vmadd_q_vl(ctx, a, 16, idx1, idx2, func); ++} ++ ++static bool gen_xvmadd_q(DisasContext *ctx, arg_vvv *a, int idx1, int idx2, ++ void (*func)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) ++{ ++ return gen_vmadd_q_vl(ctx, a, 32, idx1, idx2, func); ++} ++ ++TRANS(vmaddwev_q_d, LSX, gen_vmadd_q, 0, 0, tcg_gen_muls2_i64) ++TRANS(vmaddwod_q_d, LSX, gen_vmadd_q, 1, 1, tcg_gen_muls2_i64) ++TRANS(vmaddwev_q_du, LSX, gen_vmadd_q, 0, 0, tcg_gen_mulu2_i64) ++TRANS(vmaddwod_q_du, LSX, gen_vmadd_q, 1, 1, tcg_gen_mulu2_i64) ++TRANS(vmaddwev_q_du_d, LSX, gen_vmadd_q, 0, 0, tcg_gen_mulus2_i64) ++TRANS(vmaddwod_q_du_d, LSX, gen_vmadd_q, 1, 1, tcg_gen_mulus2_i64) ++TRANS(xvmaddwev_q_d, LASX, gen_xvmadd_q, 0, 0, tcg_gen_muls2_i64) ++TRANS(xvmaddwod_q_d, LASX, gen_xvmadd_q, 1, 1, tcg_gen_muls2_i64) ++TRANS(xvmaddwev_q_du, LASX, gen_xvmadd_q, 0, 0, tcg_gen_mulu2_i64) ++TRANS(xvmaddwod_q_du, LASX, gen_xvmadd_q, 1, 1, tcg_gen_mulu2_i64) ++TRANS(xvmaddwev_q_du_d, LASX, gen_xvmadd_q, 0, 0, tcg_gen_mulus2_i64) ++TRANS(xvmaddwod_q_du_d, LASX, gen_xvmadd_q, 1, 1, tcg_gen_mulus2_i64) ++ ++static void gen_vmaddwod_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, t2, t3; ++ int halfbits = 4 << vece; ++ ++ t1 = tcg_temp_new_vec_matching(a); ++ t2 = tcg_temp_new_vec_matching(b); ++ t3 = tcg_temp_new_vec_matching(t); ++ tcg_gen_sari_vec(vece, t1, a, halfbits); ++ tcg_gen_sari_vec(vece, t2, b, halfbits); ++ tcg_gen_mul_vec(vece, t3, t1, t2); ++ tcg_gen_add_vec(vece, t, t, t3); ++} ++ ++static void gen_vmaddwod_w_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1; ++ ++ t1 = tcg_temp_new_i32(); ++ gen_vmulwod_w_h(t1, a, b); ++ tcg_gen_add_i32(t, t, t1); ++} ++ ++static void gen_vmaddwod_d_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1; ++ ++ t1 = tcg_temp_new_i64(); ++ gen_vmulwod_d_w(t1, a, b); ++ tcg_gen_add_i64(t, t, t1); ++} ++ ++static void do_vmaddwod_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_sari_vec, INDEX_op_mul_vec, INDEX_op_add_vec, 0 ++ }; ++ static const GVecGen3 op[3] = { ++ { ++ .fniv = gen_vmaddwod_s, ++ .fno = gen_helper_vmaddwod_h_b, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vmaddwod_w_h, ++ .fniv = gen_vmaddwod_s, ++ .fno = gen_helper_vmaddwod_w_h, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vmaddwod_d_w, ++ .fniv = gen_vmaddwod_s, ++ .fno = gen_helper_vmaddwod_d_w, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vmaddwod_h_b, LSX, gvec_vvv, MO_8, do_vmaddwod_s) ++TRANS(vmaddwod_w_h, LSX, gvec_vvv, MO_16, do_vmaddwod_s) ++TRANS(vmaddwod_d_w, LSX, gvec_vvv, MO_32, do_vmaddwod_s) ++TRANS(xvmaddwod_h_b, LASX, gvec_xxx, MO_8, do_vmaddwod_s) ++TRANS(xvmaddwod_w_h, LASX, gvec_xxx, MO_16, do_vmaddwod_s) ++TRANS(xvmaddwod_d_w, LASX, gvec_xxx, MO_32, do_vmaddwod_s) ++ ++static void gen_vmaddwev_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, t2, mask; ++ ++ t1 = tcg_temp_new_vec_matching(t); ++ t2 = tcg_temp_new_vec_matching(b); ++ mask = tcg_constant_vec_matching(t, vece, MAKE_64BIT_MASK(0, 4 << vece)); ++ tcg_gen_and_vec(vece, t1, a, mask); ++ tcg_gen_and_vec(vece, t2, b, mask); ++ tcg_gen_mul_vec(vece, t1, t1, t2); ++ tcg_gen_add_vec(vece, t, t, t1); ++} ++ ++static void gen_vmaddwev_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1; ++ ++ t1 = tcg_temp_new_i32(); ++ gen_vmulwev_w_hu(t1, a, b); ++ tcg_gen_add_i32(t, t, t1); ++} ++ ++static void gen_vmaddwev_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1; ++ ++ t1 = tcg_temp_new_i64(); ++ gen_vmulwev_d_wu(t1, a, b); ++ tcg_gen_add_i64(t, t, t1); ++} ++ ++static void do_vmaddwev_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_mul_vec, INDEX_op_add_vec, 0 ++ }; ++ static const GVecGen3 op[3] = { ++ { ++ .fniv = gen_vmaddwev_u, ++ .fno = gen_helper_vmaddwev_h_bu, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vmaddwev_w_hu, ++ .fniv = gen_vmaddwev_u, ++ .fno = gen_helper_vmaddwev_w_hu, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vmaddwev_d_wu, ++ .fniv = gen_vmaddwev_u, ++ .fno = gen_helper_vmaddwev_d_wu, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vmaddwev_h_bu, LSX, gvec_vvv, MO_8, do_vmaddwev_u) ++TRANS(vmaddwev_w_hu, LSX, gvec_vvv, MO_16, do_vmaddwev_u) ++TRANS(vmaddwev_d_wu, LSX, gvec_vvv, MO_32, do_vmaddwev_u) ++TRANS(xvmaddwev_h_bu, LASX, gvec_xxx, MO_8, do_vmaddwev_u) ++TRANS(xvmaddwev_w_hu, LASX, gvec_xxx, MO_16, do_vmaddwev_u) ++TRANS(xvmaddwev_d_wu, LASX, gvec_xxx, MO_32, do_vmaddwev_u) ++ ++static void gen_vmaddwod_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, t2, t3; ++ int halfbits = 4 << vece; ++ ++ t1 = tcg_temp_new_vec_matching(a); ++ t2 = tcg_temp_new_vec_matching(b); ++ t3 = tcg_temp_new_vec_matching(t); ++ tcg_gen_shri_vec(vece, t1, a, halfbits); ++ tcg_gen_shri_vec(vece, t2, b, halfbits); ++ tcg_gen_mul_vec(vece, t3, t1, t2); ++ tcg_gen_add_vec(vece, t, t, t3); ++} ++ ++static void gen_vmaddwod_w_hu(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1; ++ ++ t1 = tcg_temp_new_i32(); ++ gen_vmulwod_w_hu(t1, a, b); ++ tcg_gen_add_i32(t, t, t1); ++} ++ ++static void gen_vmaddwod_d_wu(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1; ++ ++ t1 = tcg_temp_new_i64(); ++ gen_vmulwod_d_wu(t1, a, b); ++ tcg_gen_add_i64(t, t, t1); ++} ++ ++static void do_vmaddwod_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_shri_vec, INDEX_op_mul_vec, INDEX_op_add_vec, 0 ++ }; ++ static const GVecGen3 op[3] = { ++ { ++ .fniv = gen_vmaddwod_u, ++ .fno = gen_helper_vmaddwod_h_bu, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vmaddwod_w_hu, ++ .fniv = gen_vmaddwod_u, ++ .fno = gen_helper_vmaddwod_w_hu, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vmaddwod_d_wu, ++ .fniv = gen_vmaddwod_u, ++ .fno = gen_helper_vmaddwod_d_wu, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vmaddwod_h_bu, LSX, gvec_vvv, MO_8, do_vmaddwod_u) ++TRANS(vmaddwod_w_hu, LSX, gvec_vvv, MO_16, do_vmaddwod_u) ++TRANS(vmaddwod_d_wu, LSX, gvec_vvv, MO_32, do_vmaddwod_u) ++TRANS(xvmaddwod_h_bu, LASX, gvec_xxx, MO_8, do_vmaddwod_u) ++TRANS(xvmaddwod_w_hu, LASX, gvec_xxx, MO_16, do_vmaddwod_u) ++TRANS(xvmaddwod_d_wu, LASX, gvec_xxx, MO_32, do_vmaddwod_u) ++ ++static void gen_vmaddwev_u_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, t2, mask; ++ int halfbits = 4 << vece; ++ ++ t1 = tcg_temp_new_vec_matching(a); ++ t2 = tcg_temp_new_vec_matching(b); ++ mask = tcg_constant_vec_matching(t, vece, MAKE_64BIT_MASK(0, 4 << vece)); ++ tcg_gen_and_vec(vece, t1, a, mask); ++ tcg_gen_shli_vec(vece, t2, b, halfbits); ++ tcg_gen_sari_vec(vece, t2, t2, halfbits); ++ tcg_gen_mul_vec(vece, t1, t1, t2); ++ tcg_gen_add_vec(vece, t, t, t1); ++} ++ ++static void gen_vmaddwev_w_hu_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1; ++ ++ t1 = tcg_temp_new_i32(); ++ gen_vmulwev_w_hu_h(t1, a, b); ++ tcg_gen_add_i32(t, t, t1); ++} ++ ++static void gen_vmaddwev_d_wu_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1; ++ ++ t1 = tcg_temp_new_i64(); ++ gen_vmulwev_d_wu_w(t1, a, b); ++ tcg_gen_add_i64(t, t, t1); ++} ++ ++static void do_vmaddwev_u_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_shli_vec, INDEX_op_sari_vec, ++ INDEX_op_mul_vec, INDEX_op_add_vec, 0 ++ }; ++ static const GVecGen3 op[3] = { ++ { ++ .fniv = gen_vmaddwev_u_s, ++ .fno = gen_helper_vmaddwev_h_bu_b, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vmaddwev_w_hu_h, ++ .fniv = gen_vmaddwev_u_s, ++ .fno = gen_helper_vmaddwev_w_hu_h, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vmaddwev_d_wu_w, ++ .fniv = gen_vmaddwev_u_s, ++ .fno = gen_helper_vmaddwev_d_wu_w, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vmaddwev_h_bu_b, LSX, gvec_vvv, MO_8, do_vmaddwev_u_s) ++TRANS(vmaddwev_w_hu_h, LSX, gvec_vvv, MO_16, do_vmaddwev_u_s) ++TRANS(vmaddwev_d_wu_w, LSX, gvec_vvv, MO_32, do_vmaddwev_u_s) ++TRANS(xvmaddwev_h_bu_b, LASX, gvec_xxx, MO_8, do_vmaddwev_u_s) ++TRANS(xvmaddwev_w_hu_h, LASX, gvec_xxx, MO_16, do_vmaddwev_u_s) ++TRANS(xvmaddwev_d_wu_w, LASX, gvec_xxx, MO_32, do_vmaddwev_u_s) ++ ++static void gen_vmaddwod_u_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, t2, t3; ++ int halfbits = 4 << vece; ++ ++ t1 = tcg_temp_new_vec_matching(a); ++ t2 = tcg_temp_new_vec_matching(b); ++ t3 = tcg_temp_new_vec_matching(t); ++ tcg_gen_shri_vec(vece, t1, a, halfbits); ++ tcg_gen_sari_vec(vece, t2, b, halfbits); ++ tcg_gen_mul_vec(vece, t3, t1, t2); ++ tcg_gen_add_vec(vece, t, t, t3); ++} ++ ++static void gen_vmaddwod_w_hu_h(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) ++{ ++ TCGv_i32 t1; ++ ++ t1 = tcg_temp_new_i32(); ++ gen_vmulwod_w_hu_h(t1, a, b); ++ tcg_gen_add_i32(t, t, t1); ++} ++ ++static void gen_vmaddwod_d_wu_w(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) ++{ ++ TCGv_i64 t1; ++ ++ t1 = tcg_temp_new_i64(); ++ gen_vmulwod_d_wu_w(t1, a, b); ++ tcg_gen_add_i64(t, t, t1); ++} ++ ++static void do_vmaddwod_u_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_shri_vec, INDEX_op_sari_vec, ++ INDEX_op_mul_vec, INDEX_op_add_vec, 0 ++ }; ++ static const GVecGen3 op[3] = { ++ { ++ .fniv = gen_vmaddwod_u_s, ++ .fno = gen_helper_vmaddwod_h_bu_b, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fni4 = gen_vmaddwod_w_hu_h, ++ .fniv = gen_vmaddwod_u_s, ++ .fno = gen_helper_vmaddwod_w_hu_h, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fni8 = gen_vmaddwod_d_wu_w, ++ .fniv = gen_vmaddwod_u_s, ++ .fno = gen_helper_vmaddwod_d_wu_w, ++ .load_dest = true, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vmaddwod_h_bu_b, LSX, gvec_vvv, MO_8, do_vmaddwod_u_s) ++TRANS(vmaddwod_w_hu_h, LSX, gvec_vvv, MO_16, do_vmaddwod_u_s) ++TRANS(vmaddwod_d_wu_w, LSX, gvec_vvv, MO_32, do_vmaddwod_u_s) ++TRANS(xvmaddwod_h_bu_b, LASX, gvec_xxx, MO_8, do_vmaddwod_u_s) ++TRANS(xvmaddwod_w_hu_h, LASX, gvec_xxx, MO_16, do_vmaddwod_u_s) ++TRANS(xvmaddwod_d_wu_w, LASX, gvec_xxx, MO_32, do_vmaddwod_u_s) ++ ++TRANS(vdiv_b, LSX, gen_vvv, gen_helper_vdiv_b) ++TRANS(vdiv_h, LSX, gen_vvv, gen_helper_vdiv_h) ++TRANS(vdiv_w, LSX, gen_vvv, gen_helper_vdiv_w) ++TRANS(vdiv_d, LSX, gen_vvv, gen_helper_vdiv_d) ++TRANS(vdiv_bu, LSX, gen_vvv, gen_helper_vdiv_bu) ++TRANS(vdiv_hu, LSX, gen_vvv, gen_helper_vdiv_hu) ++TRANS(vdiv_wu, LSX, gen_vvv, gen_helper_vdiv_wu) ++TRANS(vdiv_du, LSX, gen_vvv, gen_helper_vdiv_du) ++TRANS(vmod_b, LSX, gen_vvv, gen_helper_vmod_b) ++TRANS(vmod_h, LSX, gen_vvv, gen_helper_vmod_h) ++TRANS(vmod_w, LSX, gen_vvv, gen_helper_vmod_w) ++TRANS(vmod_d, LSX, gen_vvv, gen_helper_vmod_d) ++TRANS(vmod_bu, LSX, gen_vvv, gen_helper_vmod_bu) ++TRANS(vmod_hu, LSX, gen_vvv, gen_helper_vmod_hu) ++TRANS(vmod_wu, LSX, gen_vvv, gen_helper_vmod_wu) ++TRANS(vmod_du, LSX, gen_vvv, gen_helper_vmod_du) ++TRANS(xvdiv_b, LASX, gen_xxx, gen_helper_vdiv_b) ++TRANS(xvdiv_h, LASX, gen_xxx, gen_helper_vdiv_h) ++TRANS(xvdiv_w, LASX, gen_xxx, gen_helper_vdiv_w) ++TRANS(xvdiv_d, LASX, gen_xxx, gen_helper_vdiv_d) ++TRANS(xvdiv_bu, LASX, gen_xxx, gen_helper_vdiv_bu) ++TRANS(xvdiv_hu, LASX, gen_xxx, gen_helper_vdiv_hu) ++TRANS(xvdiv_wu, LASX, gen_xxx, gen_helper_vdiv_wu) ++TRANS(xvdiv_du, LASX, gen_xxx, gen_helper_vdiv_du) ++TRANS(xvmod_b, LASX, gen_xxx, gen_helper_vmod_b) ++TRANS(xvmod_h, LASX, gen_xxx, gen_helper_vmod_h) ++TRANS(xvmod_w, LASX, gen_xxx, gen_helper_vmod_w) ++TRANS(xvmod_d, LASX, gen_xxx, gen_helper_vmod_d) ++TRANS(xvmod_bu, LASX, gen_xxx, gen_helper_vmod_bu) ++TRANS(xvmod_hu, LASX, gen_xxx, gen_helper_vmod_hu) ++TRANS(xvmod_wu, LASX, gen_xxx, gen_helper_vmod_wu) ++TRANS(xvmod_du, LASX, gen_xxx, gen_helper_vmod_du) ++ ++static void gen_vsat_s(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec max) ++{ ++ TCGv_vec min; ++ ++ min = tcg_temp_new_vec_matching(t); ++ tcg_gen_not_vec(vece, min, max); ++ tcg_gen_smax_vec(vece, t, a, min); ++ tcg_gen_smin_vec(vece, t, t, max); ++} ++ ++static void do_vsat_s(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ int64_t imm, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_smax_vec, INDEX_op_smin_vec, 0 ++ }; ++ static const GVecGen2s op[4] = { ++ { ++ .fniv = gen_vsat_s, ++ .fno = gen_helper_vsat_b, ++ .opt_opc = vecop_list, ++ .vece = MO_8 ++ }, ++ { ++ .fniv = gen_vsat_s, ++ .fno = gen_helper_vsat_h, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fniv = gen_vsat_s, ++ .fno = gen_helper_vsat_w, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fniv = gen_vsat_s, ++ .fno = gen_helper_vsat_d, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_2s(vd_ofs, vj_ofs, oprsz, maxsz, ++ tcg_constant_i64((1ll<< imm) -1), &op[vece]); ++} ++ ++TRANS(vsat_b, LSX, gvec_vv_i, MO_8, do_vsat_s) ++TRANS(vsat_h, LSX, gvec_vv_i, MO_16, do_vsat_s) ++TRANS(vsat_w, LSX, gvec_vv_i, MO_32, do_vsat_s) ++TRANS(vsat_d, LSX, gvec_vv_i, MO_64, do_vsat_s) ++TRANS(xvsat_b, LASX, gvec_xx_i, MO_8, do_vsat_s) ++TRANS(xvsat_h, LASX, gvec_xx_i, MO_16, do_vsat_s) ++TRANS(xvsat_w, LASX, gvec_xx_i, MO_32, do_vsat_s) ++TRANS(xvsat_d, LASX, gvec_xx_i, MO_64, do_vsat_s) ++ ++static void gen_vsat_u(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec max) ++{ ++ tcg_gen_umin_vec(vece, t, a, max); ++} ++ ++static void do_vsat_u(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ int64_t imm, uint32_t oprsz, uint32_t maxsz) ++{ ++ uint64_t max; ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_umin_vec, 0 ++ }; ++ static const GVecGen2s op[4] = { ++ { ++ .fniv = gen_vsat_u, ++ .fno = gen_helper_vsat_bu, ++ .opt_opc = vecop_list, ++ .vece = MO_8 ++ }, ++ { ++ .fniv = gen_vsat_u, ++ .fno = gen_helper_vsat_hu, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fniv = gen_vsat_u, ++ .fno = gen_helper_vsat_wu, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fniv = gen_vsat_u, ++ .fno = gen_helper_vsat_du, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ max = (imm == 0x3f) ? UINT64_MAX : (1ull << (imm + 1)) - 1; ++ tcg_gen_gvec_2s(vd_ofs, vj_ofs, oprsz, maxsz, ++ tcg_constant_i64(max), &op[vece]); ++} ++ ++TRANS(vsat_bu, LSX, gvec_vv_i, MO_8, do_vsat_u) ++TRANS(vsat_hu, LSX, gvec_vv_i, MO_16, do_vsat_u) ++TRANS(vsat_wu, LSX, gvec_vv_i, MO_32, do_vsat_u) ++TRANS(vsat_du, LSX, gvec_vv_i, MO_64, do_vsat_u) ++TRANS(xvsat_bu, LASX, gvec_xx_i, MO_8, do_vsat_u) ++TRANS(xvsat_hu, LASX, gvec_xx_i, MO_16, do_vsat_u) ++TRANS(xvsat_wu, LASX, gvec_xx_i, MO_32, do_vsat_u) ++TRANS(xvsat_du, LASX, gvec_xx_i, MO_64, do_vsat_u) ++ ++TRANS(vexth_h_b, LSX, gen_vv, gen_helper_vexth_h_b) ++TRANS(vexth_w_h, LSX, gen_vv, gen_helper_vexth_w_h) ++TRANS(vexth_d_w, LSX, gen_vv, gen_helper_vexth_d_w) ++TRANS(vexth_q_d, LSX, gen_vv, gen_helper_vexth_q_d) ++TRANS(vexth_hu_bu, LSX, gen_vv, gen_helper_vexth_hu_bu) ++TRANS(vexth_wu_hu, LSX, gen_vv, gen_helper_vexth_wu_hu) ++TRANS(vexth_du_wu, LSX, gen_vv, gen_helper_vexth_du_wu) ++TRANS(vexth_qu_du, LSX, gen_vv, gen_helper_vexth_qu_du) ++TRANS(xvexth_h_b, LASX, gen_xx, gen_helper_vexth_h_b) ++TRANS(xvexth_w_h, LASX, gen_xx, gen_helper_vexth_w_h) ++TRANS(xvexth_d_w, LASX, gen_xx, gen_helper_vexth_d_w) ++TRANS(xvexth_q_d, LASX, gen_xx, gen_helper_vexth_q_d) ++TRANS(xvexth_hu_bu, LASX, gen_xx, gen_helper_vexth_hu_bu) ++TRANS(xvexth_wu_hu, LASX, gen_xx, gen_helper_vexth_wu_hu) ++TRANS(xvexth_du_wu, LASX, gen_xx, gen_helper_vexth_du_wu) ++TRANS(xvexth_qu_du, LASX, gen_xx, gen_helper_vexth_qu_du) ++ ++TRANS(vext2xv_h_b, LASX, gen_xx, gen_helper_vext2xv_h_b) ++TRANS(vext2xv_w_b, LASX, gen_xx, gen_helper_vext2xv_w_b) ++TRANS(vext2xv_d_b, LASX, gen_xx, gen_helper_vext2xv_d_b) ++TRANS(vext2xv_w_h, LASX, gen_xx, gen_helper_vext2xv_w_h) ++TRANS(vext2xv_d_h, LASX, gen_xx, gen_helper_vext2xv_d_h) ++TRANS(vext2xv_d_w, LASX, gen_xx, gen_helper_vext2xv_d_w) ++TRANS(vext2xv_hu_bu, LASX, gen_xx, gen_helper_vext2xv_hu_bu) ++TRANS(vext2xv_wu_bu, LASX, gen_xx, gen_helper_vext2xv_wu_bu) ++TRANS(vext2xv_du_bu, LASX, gen_xx, gen_helper_vext2xv_du_bu) ++TRANS(vext2xv_wu_hu, LASX, gen_xx, gen_helper_vext2xv_wu_hu) ++TRANS(vext2xv_du_hu, LASX, gen_xx, gen_helper_vext2xv_du_hu) ++TRANS(vext2xv_du_wu, LASX, gen_xx, gen_helper_vext2xv_du_wu) ++ ++static void gen_vsigncov(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ TCGv_vec t1, zero; ++ ++ t1 = tcg_temp_new_vec_matching(t); ++ zero = tcg_constant_vec_matching(t, vece, 0); ++ ++ tcg_gen_neg_vec(vece, t1, b); ++ tcg_gen_cmpsel_vec(TCG_COND_LT, vece, t, a, zero, t1, b); ++ tcg_gen_cmpsel_vec(TCG_COND_EQ, vece, t, a, zero, zero, t); ++} ++ ++static void do_vsigncov(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_neg_vec, INDEX_op_cmpsel_vec, 0 ++ }; ++ static const GVecGen3 op[4] = { ++ { ++ .fniv = gen_vsigncov, ++ .fno = gen_helper_vsigncov_b, ++ .opt_opc = vecop_list, ++ .vece = MO_8 ++ }, ++ { ++ .fniv = gen_vsigncov, ++ .fno = gen_helper_vsigncov_h, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fniv = gen_vsigncov, ++ .fno = gen_helper_vsigncov_w, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fniv = gen_vsigncov, ++ .fno = gen_helper_vsigncov_d, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vsigncov_b, LSX, gvec_vvv, MO_8, do_vsigncov) ++TRANS(vsigncov_h, LSX, gvec_vvv, MO_16, do_vsigncov) ++TRANS(vsigncov_w, LSX, gvec_vvv, MO_32, do_vsigncov) ++TRANS(vsigncov_d, LSX, gvec_vvv, MO_64, do_vsigncov) ++TRANS(xvsigncov_b, LASX, gvec_xxx, MO_8, do_vsigncov) ++TRANS(xvsigncov_h, LASX, gvec_xxx, MO_16, do_vsigncov) ++TRANS(xvsigncov_w, LASX, gvec_xxx, MO_32, do_vsigncov) ++TRANS(xvsigncov_d, LASX, gvec_xxx, MO_64, do_vsigncov) ++ ++TRANS(vmskltz_b, LSX, gen_vv, gen_helper_vmskltz_b) ++TRANS(vmskltz_h, LSX, gen_vv, gen_helper_vmskltz_h) ++TRANS(vmskltz_w, LSX, gen_vv, gen_helper_vmskltz_w) ++TRANS(vmskltz_d, LSX, gen_vv, gen_helper_vmskltz_d) ++TRANS(vmskgez_b, LSX, gen_vv, gen_helper_vmskgez_b) ++TRANS(vmsknz_b, LSX, gen_vv, gen_helper_vmsknz_b) ++TRANS(xvmskltz_b, LASX, gen_xx, gen_helper_vmskltz_b) ++TRANS(xvmskltz_h, LASX, gen_xx, gen_helper_vmskltz_h) ++TRANS(xvmskltz_w, LASX, gen_xx, gen_helper_vmskltz_w) ++TRANS(xvmskltz_d, LASX, gen_xx, gen_helper_vmskltz_d) ++TRANS(xvmskgez_b, LASX, gen_xx, gen_helper_vmskgez_b) ++TRANS(xvmsknz_b, LASX, gen_xx, gen_helper_vmsknz_b) ++ ++#define EXPAND_BYTE(bit) ((uint64_t)(bit ? 0xff : 0)) ++ ++static uint64_t vldi_get_value(DisasContext *ctx, uint32_t imm) ++{ ++ int mode; ++ uint64_t data, t; ++ ++ /* ++ * imm bit [11:8] is mode, mode value is 0-12. ++ * other values are invalid. ++ */ ++ mode = (imm >> 8) & 0xf; ++ t = imm & 0xff; ++ switch (mode) { ++ case 0: ++ /* data: {2{24'0, imm[7:0]}} */ ++ data = (t << 32) | t ; ++ break; ++ case 1: ++ /* data: {2{16'0, imm[7:0], 8'0}} */ ++ data = (t << 24) | (t << 8); ++ break; ++ case 2: ++ /* data: {2{8'0, imm[7:0], 16'0}} */ ++ data = (t << 48) | (t << 16); ++ break; ++ case 3: ++ /* data: {2{imm[7:0], 24'0}} */ ++ data = (t << 56) | (t << 24); ++ break; ++ case 4: ++ /* data: {4{8'0, imm[7:0]}} */ ++ data = (t << 48) | (t << 32) | (t << 16) | t; ++ break; ++ case 5: ++ /* data: {4{imm[7:0], 8'0}} */ ++ data = (t << 56) |(t << 40) | (t << 24) | (t << 8); ++ break; ++ case 6: ++ /* data: {2{16'0, imm[7:0], 8'1}} */ ++ data = (t << 40) | ((uint64_t)0xff << 32) | (t << 8) | 0xff; ++ break; ++ case 7: ++ /* data: {2{8'0, imm[7:0], 16'1}} */ ++ data = (t << 48) | ((uint64_t)0xffff << 32) | (t << 16) | 0xffff; ++ break; ++ case 8: ++ /* data: {8{imm[7:0]}} */ ++ data =(t << 56) | (t << 48) | (t << 40) | (t << 32) | ++ (t << 24) | (t << 16) | (t << 8) | t; ++ break; ++ case 9: ++ /* data: {{8{imm[7]}, ..., 8{imm[0]}}} */ ++ { ++ uint64_t b0,b1,b2,b3,b4,b5,b6,b7; ++ b0 = t& 0x1; ++ b1 = (t & 0x2) >> 1; ++ b2 = (t & 0x4) >> 2; ++ b3 = (t & 0x8) >> 3; ++ b4 = (t & 0x10) >> 4; ++ b5 = (t & 0x20) >> 5; ++ b6 = (t & 0x40) >> 6; ++ b7 = (t & 0x80) >> 7; ++ data = (EXPAND_BYTE(b7) << 56) | ++ (EXPAND_BYTE(b6) << 48) | ++ (EXPAND_BYTE(b5) << 40) | ++ (EXPAND_BYTE(b4) << 32) | ++ (EXPAND_BYTE(b3) << 24) | ++ (EXPAND_BYTE(b2) << 16) | ++ (EXPAND_BYTE(b1) << 8) | ++ EXPAND_BYTE(b0); ++ } ++ break; ++ case 10: ++ /* data: {2{imm[7], ~imm[6], {5{imm[6]}}, imm[5:0], 19'0}} */ ++ { ++ uint64_t b6, b7; ++ uint64_t t0, t1; ++ b6 = (imm & 0x40) >> 6; ++ b7 = (imm & 0x80) >> 7; ++ t0 = (imm & 0x3f); ++ t1 = (b7 << 6) | ((1-b6) << 5) | (uint64_t)(b6 ? 0x1f : 0); ++ data = (t1 << 57) | (t0 << 51) | (t1 << 25) | (t0 << 19); ++ } ++ break; ++ case 11: ++ /* data: {32'0, imm[7], ~{imm[6]}, 5{imm[6]}, imm[5:0], 19'0} */ ++ { ++ uint64_t b6,b7; ++ uint64_t t0, t1; ++ b6 = (imm & 0x40) >> 6; ++ b7 = (imm & 0x80) >> 7; ++ t0 = (imm & 0x3f); ++ t1 = (b7 << 6) | ((1-b6) << 5) | (b6 ? 0x1f : 0); ++ data = (t1 << 25) | (t0 << 19); ++ } ++ break; ++ case 12: ++ /* data: {imm[7], ~imm[6], 8{imm[6]}, imm[5:0], 48'0} */ ++ { ++ uint64_t b6,b7; ++ uint64_t t0, t1; ++ b6 = (imm & 0x40) >> 6; ++ b7 = (imm & 0x80) >> 7; ++ t0 = (imm & 0x3f); ++ t1 = (b7 << 9) | ((1-b6) << 8) | (b6 ? 0xff : 0); ++ data = (t1 << 54) | (t0 << 48); ++ } ++ break; ++ default: ++ generate_exception(ctx, EXCCODE_INE); ++ g_assert_not_reached(); ++ } ++ return data; ++} ++ ++static bool gen_vldi(DisasContext *ctx, arg_vldi *a, uint32_t oprsz) ++{ ++ int sel, vece; ++ uint64_t value; ++ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ sel = (a->imm >> 12) & 0x1; ++ ++ if (sel) { ++ value = vldi_get_value(ctx, a->imm); ++ vece = MO_64; ++ } else { ++ value = ((int32_t)(a->imm << 22)) >> 22; ++ vece = (a->imm >> 10) & 0x3; ++ } ++ ++ tcg_gen_gvec_dup_i64(vece, vec_full_offset(a->vd), oprsz, ctx->vl/8, ++ tcg_constant_i64(value)); ++ return true; ++} ++ ++TRANS(vldi, LSX, gen_vldi, 16) ++TRANS(xvldi, LASX, gen_vldi, 32) ++ ++static bool gen_vandn_v(DisasContext *ctx, arg_vvv *a, uint32_t oprsz) ++{ ++ uint32_t vd_ofs, vj_ofs, vk_ofs; ++ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ vd_ofs = vec_full_offset(a->vd); ++ vj_ofs = vec_full_offset(a->vj); ++ vk_ofs = vec_full_offset(a->vk); ++ ++ tcg_gen_gvec_andc(MO_64, vd_ofs, vk_ofs, vj_ofs, oprsz, ctx->vl / 8); ++ return true; ++} ++ ++static void gen_vnori(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) ++{ ++ TCGv_vec t1; ++ ++ t1 = tcg_constant_vec_matching(t, vece, imm); ++ tcg_gen_nor_vec(vece, t, a, t1); ++} ++ ++static void gen_vnori_b(TCGv_i64 t, TCGv_i64 a, int64_t imm) ++{ ++ tcg_gen_movi_i64(t, dup_const(MO_8, imm)); ++ tcg_gen_nor_i64(t, a, t); ++} ++ ++static void do_vnori_b(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ int64_t imm, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_nor_vec, 0 ++ }; ++ static const GVecGen2i op = { ++ .fni8 = gen_vnori_b, ++ .fniv = gen_vnori, ++ .fnoi = gen_helper_vnori_b, ++ .opt_opc = vecop_list, ++ .vece = MO_8 ++ }; ++ ++ tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op); ++} ++ ++TRANS(vand_v, LSX, gvec_vvv, MO_64, tcg_gen_gvec_and) ++TRANS(vor_v, LSX, gvec_vvv, MO_64, tcg_gen_gvec_or) ++TRANS(vxor_v, LSX, gvec_vvv, MO_64, tcg_gen_gvec_xor) ++TRANS(vnor_v, LSX, gvec_vvv, MO_64, tcg_gen_gvec_nor) ++TRANS(vandn_v, LSX, gen_vandn_v, 16) ++TRANS(vorn_v, LSX, gvec_vvv, MO_64, tcg_gen_gvec_orc) ++TRANS(vandi_b, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_andi) ++TRANS(vori_b, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_ori) ++TRANS(vxori_b, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_xori) ++TRANS(vnori_b, LSX, gvec_vv_i, MO_8, do_vnori_b) ++TRANS(xvand_v, LASX, gvec_xxx, MO_64, tcg_gen_gvec_and) ++TRANS(xvor_v, LASX, gvec_xxx, MO_64, tcg_gen_gvec_or) ++TRANS(xvxor_v, LASX, gvec_xxx, MO_64, tcg_gen_gvec_xor) ++TRANS(xvnor_v, LASX, gvec_xxx, MO_64, tcg_gen_gvec_nor) ++TRANS(xvandn_v, LASX, gen_vandn_v, 32) ++TRANS(xvorn_v, LASX, gvec_xxx, MO_64, tcg_gen_gvec_orc) ++TRANS(xvandi_b, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_andi) ++TRANS(xvori_b, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_ori) ++TRANS(xvxori_b, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_xori) ++TRANS(xvnori_b, LASX, gvec_xx_i, MO_8, do_vnori_b) ++ ++TRANS(vsll_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_shlv) ++TRANS(vsll_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_shlv) ++TRANS(vsll_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_shlv) ++TRANS(vsll_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_shlv) ++TRANS(vslli_b, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_shli) ++TRANS(vslli_h, LSX, gvec_vv_i, MO_16, tcg_gen_gvec_shli) ++TRANS(vslli_w, LSX, gvec_vv_i, MO_32, tcg_gen_gvec_shli) ++TRANS(vslli_d, LSX, gvec_vv_i, MO_64, tcg_gen_gvec_shli) ++TRANS(xvsll_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_shlv) ++TRANS(xvsll_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_shlv) ++TRANS(xvsll_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_shlv) ++TRANS(xvsll_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_shlv) ++TRANS(xvslli_b, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_shli) ++TRANS(xvslli_h, LASX, gvec_xx_i, MO_16, tcg_gen_gvec_shli) ++TRANS(xvslli_w, LASX, gvec_xx_i, MO_32, tcg_gen_gvec_shli) ++TRANS(xvslli_d, LASX, gvec_xx_i, MO_64, tcg_gen_gvec_shli) ++ ++TRANS(vsrl_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_shrv) ++TRANS(vsrl_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_shrv) ++TRANS(vsrl_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_shrv) ++TRANS(vsrl_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_shrv) ++TRANS(vsrli_b, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_shri) ++TRANS(vsrli_h, LSX, gvec_vv_i, MO_16, tcg_gen_gvec_shri) ++TRANS(vsrli_w, LSX, gvec_vv_i, MO_32, tcg_gen_gvec_shri) ++TRANS(vsrli_d, LSX, gvec_vv_i, MO_64, tcg_gen_gvec_shri) ++TRANS(xvsrl_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_shrv) ++TRANS(xvsrl_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_shrv) ++TRANS(xvsrl_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_shrv) ++TRANS(xvsrl_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_shrv) ++TRANS(xvsrli_b, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_shri) ++TRANS(xvsrli_h, LASX, gvec_xx_i, MO_16, tcg_gen_gvec_shri) ++TRANS(xvsrli_w, LASX, gvec_xx_i, MO_32, tcg_gen_gvec_shri) ++TRANS(xvsrli_d, LASX, gvec_xx_i, MO_64, tcg_gen_gvec_shri) ++ ++TRANS(vsra_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_sarv) ++TRANS(vsra_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_sarv) ++TRANS(vsra_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_sarv) ++TRANS(vsra_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_sarv) ++TRANS(vsrai_b, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_sari) ++TRANS(vsrai_h, LSX, gvec_vv_i, MO_16, tcg_gen_gvec_sari) ++TRANS(vsrai_w, LSX, gvec_vv_i, MO_32, tcg_gen_gvec_sari) ++TRANS(vsrai_d, LSX, gvec_vv_i, MO_64, tcg_gen_gvec_sari) ++TRANS(xvsra_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_sarv) ++TRANS(xvsra_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_sarv) ++TRANS(xvsra_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_sarv) ++TRANS(xvsra_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_sarv) ++TRANS(xvsrai_b, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_sari) ++TRANS(xvsrai_h, LASX, gvec_xx_i, MO_16, tcg_gen_gvec_sari) ++TRANS(xvsrai_w, LASX, gvec_xx_i, MO_32, tcg_gen_gvec_sari) ++TRANS(xvsrai_d, LASX, gvec_xx_i, MO_64, tcg_gen_gvec_sari) ++ ++TRANS(vrotr_b, LSX, gvec_vvv, MO_8, tcg_gen_gvec_rotrv) ++TRANS(vrotr_h, LSX, gvec_vvv, MO_16, tcg_gen_gvec_rotrv) ++TRANS(vrotr_w, LSX, gvec_vvv, MO_32, tcg_gen_gvec_rotrv) ++TRANS(vrotr_d, LSX, gvec_vvv, MO_64, tcg_gen_gvec_rotrv) ++TRANS(vrotri_b, LSX, gvec_vv_i, MO_8, tcg_gen_gvec_rotri) ++TRANS(vrotri_h, LSX, gvec_vv_i, MO_16, tcg_gen_gvec_rotri) ++TRANS(vrotri_w, LSX, gvec_vv_i, MO_32, tcg_gen_gvec_rotri) ++TRANS(vrotri_d, LSX, gvec_vv_i, MO_64, tcg_gen_gvec_rotri) ++TRANS(xvrotr_b, LASX, gvec_xxx, MO_8, tcg_gen_gvec_rotrv) ++TRANS(xvrotr_h, LASX, gvec_xxx, MO_16, tcg_gen_gvec_rotrv) ++TRANS(xvrotr_w, LASX, gvec_xxx, MO_32, tcg_gen_gvec_rotrv) ++TRANS(xvrotr_d, LASX, gvec_xxx, MO_64, tcg_gen_gvec_rotrv) ++TRANS(xvrotri_b, LASX, gvec_xx_i, MO_8, tcg_gen_gvec_rotri) ++TRANS(xvrotri_h, LASX, gvec_xx_i, MO_16, tcg_gen_gvec_rotri) ++TRANS(xvrotri_w, LASX, gvec_xx_i, MO_32, tcg_gen_gvec_rotri) ++TRANS(xvrotri_d, LASX, gvec_xx_i, MO_64, tcg_gen_gvec_rotri) ++ ++TRANS(vsllwil_h_b, LSX, gen_vv_i, gen_helper_vsllwil_h_b) ++TRANS(vsllwil_w_h, LSX, gen_vv_i, gen_helper_vsllwil_w_h) ++TRANS(vsllwil_d_w, LSX, gen_vv_i, gen_helper_vsllwil_d_w) ++TRANS(vextl_q_d, LSX, gen_vv, gen_helper_vextl_q_d) ++TRANS(vsllwil_hu_bu, LSX, gen_vv_i, gen_helper_vsllwil_hu_bu) ++TRANS(vsllwil_wu_hu, LSX, gen_vv_i, gen_helper_vsllwil_wu_hu) ++TRANS(vsllwil_du_wu, LSX, gen_vv_i, gen_helper_vsllwil_du_wu) ++TRANS(vextl_qu_du, LSX, gen_vv, gen_helper_vextl_qu_du) ++TRANS(xvsllwil_h_b, LASX, gen_xx_i, gen_helper_vsllwil_h_b) ++TRANS(xvsllwil_w_h, LASX, gen_xx_i, gen_helper_vsllwil_w_h) ++TRANS(xvsllwil_d_w, LASX, gen_xx_i, gen_helper_vsllwil_d_w) ++TRANS(xvextl_q_d, LASX, gen_xx, gen_helper_vextl_q_d) ++TRANS(xvsllwil_hu_bu, LASX, gen_xx_i, gen_helper_vsllwil_hu_bu) ++TRANS(xvsllwil_wu_hu, LASX, gen_xx_i, gen_helper_vsllwil_wu_hu) ++TRANS(xvsllwil_du_wu, LASX, gen_xx_i, gen_helper_vsllwil_du_wu) ++TRANS(xvextl_qu_du, LASX, gen_xx, gen_helper_vextl_qu_du) ++ ++TRANS(vsrlr_b, LSX, gen_vvv, gen_helper_vsrlr_b) ++TRANS(vsrlr_h, LSX, gen_vvv, gen_helper_vsrlr_h) ++TRANS(vsrlr_w, LSX, gen_vvv, gen_helper_vsrlr_w) ++TRANS(vsrlr_d, LSX, gen_vvv, gen_helper_vsrlr_d) ++TRANS(vsrlri_b, LSX, gen_vv_i, gen_helper_vsrlri_b) ++TRANS(vsrlri_h, LSX, gen_vv_i, gen_helper_vsrlri_h) ++TRANS(vsrlri_w, LSX, gen_vv_i, gen_helper_vsrlri_w) ++TRANS(vsrlri_d, LSX, gen_vv_i, gen_helper_vsrlri_d) ++TRANS(xvsrlr_b, LASX, gen_xxx, gen_helper_vsrlr_b) ++TRANS(xvsrlr_h, LASX, gen_xxx, gen_helper_vsrlr_h) ++TRANS(xvsrlr_w, LASX, gen_xxx, gen_helper_vsrlr_w) ++TRANS(xvsrlr_d, LASX, gen_xxx, gen_helper_vsrlr_d) ++TRANS(xvsrlri_b, LASX, gen_xx_i, gen_helper_vsrlri_b) ++TRANS(xvsrlri_h, LASX, gen_xx_i, gen_helper_vsrlri_h) ++TRANS(xvsrlri_w, LASX, gen_xx_i, gen_helper_vsrlri_w) ++TRANS(xvsrlri_d, LASX, gen_xx_i, gen_helper_vsrlri_d) ++ ++TRANS(vsrar_b, LSX, gen_vvv, gen_helper_vsrar_b) ++TRANS(vsrar_h, LSX, gen_vvv, gen_helper_vsrar_h) ++TRANS(vsrar_w, LSX, gen_vvv, gen_helper_vsrar_w) ++TRANS(vsrar_d, LSX, gen_vvv, gen_helper_vsrar_d) ++TRANS(vsrari_b, LSX, gen_vv_i, gen_helper_vsrari_b) ++TRANS(vsrari_h, LSX, gen_vv_i, gen_helper_vsrari_h) ++TRANS(vsrari_w, LSX, gen_vv_i, gen_helper_vsrari_w) ++TRANS(vsrari_d, LSX, gen_vv_i, gen_helper_vsrari_d) ++TRANS(xvsrar_b, LASX, gen_xxx, gen_helper_vsrar_b) ++TRANS(xvsrar_h, LASX, gen_xxx, gen_helper_vsrar_h) ++TRANS(xvsrar_w, LASX, gen_xxx, gen_helper_vsrar_w) ++TRANS(xvsrar_d, LASX, gen_xxx, gen_helper_vsrar_d) ++TRANS(xvsrari_b, LASX, gen_xx_i, gen_helper_vsrari_b) ++TRANS(xvsrari_h, LASX, gen_xx_i, gen_helper_vsrari_h) ++TRANS(xvsrari_w, LASX, gen_xx_i, gen_helper_vsrari_w) ++TRANS(xvsrari_d, LASX, gen_xx_i, gen_helper_vsrari_d) ++ ++TRANS(vsrln_b_h, LSX, gen_vvv, gen_helper_vsrln_b_h) ++TRANS(vsrln_h_w, LSX, gen_vvv, gen_helper_vsrln_h_w) ++TRANS(vsrln_w_d, LSX, gen_vvv, gen_helper_vsrln_w_d) ++TRANS(vsran_b_h, LSX, gen_vvv, gen_helper_vsran_b_h) ++TRANS(vsran_h_w, LSX, gen_vvv, gen_helper_vsran_h_w) ++TRANS(vsran_w_d, LSX, gen_vvv, gen_helper_vsran_w_d) ++TRANS(xvsrln_b_h, LASX, gen_xxx, gen_helper_vsrln_b_h) ++TRANS(xvsrln_h_w, LASX, gen_xxx, gen_helper_vsrln_h_w) ++TRANS(xvsrln_w_d, LASX, gen_xxx, gen_helper_vsrln_w_d) ++TRANS(xvsran_b_h, LASX, gen_xxx, gen_helper_vsran_b_h) ++TRANS(xvsran_h_w, LASX, gen_xxx, gen_helper_vsran_h_w) ++TRANS(xvsran_w_d, LASX, gen_xxx, gen_helper_vsran_w_d) ++ ++TRANS(vsrlni_b_h, LSX, gen_vv_i, gen_helper_vsrlni_b_h) ++TRANS(vsrlni_h_w, LSX, gen_vv_i, gen_helper_vsrlni_h_w) ++TRANS(vsrlni_w_d, LSX, gen_vv_i, gen_helper_vsrlni_w_d) ++TRANS(vsrlni_d_q, LSX, gen_vv_i, gen_helper_vsrlni_d_q) ++TRANS(vsrani_b_h, LSX, gen_vv_i, gen_helper_vsrani_b_h) ++TRANS(vsrani_h_w, LSX, gen_vv_i, gen_helper_vsrani_h_w) ++TRANS(vsrani_w_d, LSX, gen_vv_i, gen_helper_vsrani_w_d) ++TRANS(vsrani_d_q, LSX, gen_vv_i, gen_helper_vsrani_d_q) ++TRANS(xvsrlni_b_h, LASX, gen_xx_i, gen_helper_vsrlni_b_h) ++TRANS(xvsrlni_h_w, LASX, gen_xx_i, gen_helper_vsrlni_h_w) ++TRANS(xvsrlni_w_d, LASX, gen_xx_i, gen_helper_vsrlni_w_d) ++TRANS(xvsrlni_d_q, LASX, gen_xx_i, gen_helper_vsrlni_d_q) ++TRANS(xvsrani_b_h, LASX, gen_xx_i, gen_helper_vsrani_b_h) ++TRANS(xvsrani_h_w, LASX, gen_xx_i, gen_helper_vsrani_h_w) ++TRANS(xvsrani_w_d, LASX, gen_xx_i, gen_helper_vsrani_w_d) ++TRANS(xvsrani_d_q, LASX, gen_xx_i, gen_helper_vsrani_d_q) ++ ++TRANS(vsrlrn_b_h, LSX, gen_vvv, gen_helper_vsrlrn_b_h) ++TRANS(vsrlrn_h_w, LSX, gen_vvv, gen_helper_vsrlrn_h_w) ++TRANS(vsrlrn_w_d, LSX, gen_vvv, gen_helper_vsrlrn_w_d) ++TRANS(vsrarn_b_h, LSX, gen_vvv, gen_helper_vsrarn_b_h) ++TRANS(vsrarn_h_w, LSX, gen_vvv, gen_helper_vsrarn_h_w) ++TRANS(vsrarn_w_d, LSX, gen_vvv, gen_helper_vsrarn_w_d) ++TRANS(xvsrlrn_b_h, LASX, gen_xxx, gen_helper_vsrlrn_b_h) ++TRANS(xvsrlrn_h_w, LASX, gen_xxx, gen_helper_vsrlrn_h_w) ++TRANS(xvsrlrn_w_d, LASX, gen_xxx, gen_helper_vsrlrn_w_d) ++TRANS(xvsrarn_b_h, LASX, gen_xxx, gen_helper_vsrarn_b_h) ++TRANS(xvsrarn_h_w, LASX, gen_xxx, gen_helper_vsrarn_h_w) ++TRANS(xvsrarn_w_d, LASX, gen_xxx, gen_helper_vsrarn_w_d) ++ ++TRANS(vsrlrni_b_h, LSX, gen_vv_i, gen_helper_vsrlrni_b_h) ++TRANS(vsrlrni_h_w, LSX, gen_vv_i, gen_helper_vsrlrni_h_w) ++TRANS(vsrlrni_w_d, LSX, gen_vv_i, gen_helper_vsrlrni_w_d) ++TRANS(vsrlrni_d_q, LSX, gen_vv_i, gen_helper_vsrlrni_d_q) ++TRANS(vsrarni_b_h, LSX, gen_vv_i, gen_helper_vsrarni_b_h) ++TRANS(vsrarni_h_w, LSX, gen_vv_i, gen_helper_vsrarni_h_w) ++TRANS(vsrarni_w_d, LSX, gen_vv_i, gen_helper_vsrarni_w_d) ++TRANS(vsrarni_d_q, LSX, gen_vv_i, gen_helper_vsrarni_d_q) ++TRANS(xvsrlrni_b_h, LASX, gen_xx_i, gen_helper_vsrlrni_b_h) ++TRANS(xvsrlrni_h_w, LASX, gen_xx_i, gen_helper_vsrlrni_h_w) ++TRANS(xvsrlrni_w_d, LASX, gen_xx_i, gen_helper_vsrlrni_w_d) ++TRANS(xvsrlrni_d_q, LASX, gen_xx_i, gen_helper_vsrlrni_d_q) ++TRANS(xvsrarni_b_h, LASX, gen_xx_i, gen_helper_vsrarni_b_h) ++TRANS(xvsrarni_h_w, LASX, gen_xx_i, gen_helper_vsrarni_h_w) ++TRANS(xvsrarni_w_d, LASX, gen_xx_i, gen_helper_vsrarni_w_d) ++TRANS(xvsrarni_d_q, LASX, gen_xx_i, gen_helper_vsrarni_d_q) ++ ++TRANS(vssrln_b_h, LSX, gen_vvv, gen_helper_vssrln_b_h) ++TRANS(vssrln_h_w, LSX, gen_vvv, gen_helper_vssrln_h_w) ++TRANS(vssrln_w_d, LSX, gen_vvv, gen_helper_vssrln_w_d) ++TRANS(vssran_b_h, LSX, gen_vvv, gen_helper_vssran_b_h) ++TRANS(vssran_h_w, LSX, gen_vvv, gen_helper_vssran_h_w) ++TRANS(vssran_w_d, LSX, gen_vvv, gen_helper_vssran_w_d) ++TRANS(vssrln_bu_h, LSX, gen_vvv, gen_helper_vssrln_bu_h) ++TRANS(vssrln_hu_w, LSX, gen_vvv, gen_helper_vssrln_hu_w) ++TRANS(vssrln_wu_d, LSX, gen_vvv, gen_helper_vssrln_wu_d) ++TRANS(vssran_bu_h, LSX, gen_vvv, gen_helper_vssran_bu_h) ++TRANS(vssran_hu_w, LSX, gen_vvv, gen_helper_vssran_hu_w) ++TRANS(vssran_wu_d, LSX, gen_vvv, gen_helper_vssran_wu_d) ++TRANS(xvssrln_b_h, LASX, gen_xxx, gen_helper_vssrln_b_h) ++TRANS(xvssrln_h_w, LASX, gen_xxx, gen_helper_vssrln_h_w) ++TRANS(xvssrln_w_d, LASX, gen_xxx, gen_helper_vssrln_w_d) ++TRANS(xvssran_b_h, LASX, gen_xxx, gen_helper_vssran_b_h) ++TRANS(xvssran_h_w, LASX, gen_xxx, gen_helper_vssran_h_w) ++TRANS(xvssran_w_d, LASX, gen_xxx, gen_helper_vssran_w_d) ++TRANS(xvssrln_bu_h, LASX, gen_xxx, gen_helper_vssrln_bu_h) ++TRANS(xvssrln_hu_w, LASX, gen_xxx, gen_helper_vssrln_hu_w) ++TRANS(xvssrln_wu_d, LASX, gen_xxx, gen_helper_vssrln_wu_d) ++TRANS(xvssran_bu_h, LASX, gen_xxx, gen_helper_vssran_bu_h) ++TRANS(xvssran_hu_w, LASX, gen_xxx, gen_helper_vssran_hu_w) ++TRANS(xvssran_wu_d, LASX, gen_xxx, gen_helper_vssran_wu_d) ++ ++TRANS(vssrlni_b_h, LSX, gen_vv_i, gen_helper_vssrlni_b_h) ++TRANS(vssrlni_h_w, LSX, gen_vv_i, gen_helper_vssrlni_h_w) ++TRANS(vssrlni_w_d, LSX, gen_vv_i, gen_helper_vssrlni_w_d) ++TRANS(vssrlni_d_q, LSX, gen_vv_i, gen_helper_vssrlni_d_q) ++TRANS(vssrani_b_h, LSX, gen_vv_i, gen_helper_vssrani_b_h) ++TRANS(vssrani_h_w, LSX, gen_vv_i, gen_helper_vssrani_h_w) ++TRANS(vssrani_w_d, LSX, gen_vv_i, gen_helper_vssrani_w_d) ++TRANS(vssrani_d_q, LSX, gen_vv_i, gen_helper_vssrani_d_q) ++TRANS(vssrlni_bu_h, LSX, gen_vv_i, gen_helper_vssrlni_bu_h) ++TRANS(vssrlni_hu_w, LSX, gen_vv_i, gen_helper_vssrlni_hu_w) ++TRANS(vssrlni_wu_d, LSX, gen_vv_i, gen_helper_vssrlni_wu_d) ++TRANS(vssrlni_du_q, LSX, gen_vv_i, gen_helper_vssrlni_du_q) ++TRANS(vssrani_bu_h, LSX, gen_vv_i, gen_helper_vssrani_bu_h) ++TRANS(vssrani_hu_w, LSX, gen_vv_i, gen_helper_vssrani_hu_w) ++TRANS(vssrani_wu_d, LSX, gen_vv_i, gen_helper_vssrani_wu_d) ++TRANS(vssrani_du_q, LSX, gen_vv_i, gen_helper_vssrani_du_q) ++TRANS(xvssrlni_b_h, LASX, gen_xx_i, gen_helper_vssrlni_b_h) ++TRANS(xvssrlni_h_w, LASX, gen_xx_i, gen_helper_vssrlni_h_w) ++TRANS(xvssrlni_w_d, LASX, gen_xx_i, gen_helper_vssrlni_w_d) ++TRANS(xvssrlni_d_q, LASX, gen_xx_i, gen_helper_vssrlni_d_q) ++TRANS(xvssrani_b_h, LASX, gen_xx_i, gen_helper_vssrani_b_h) ++TRANS(xvssrani_h_w, LASX, gen_xx_i, gen_helper_vssrani_h_w) ++TRANS(xvssrani_w_d, LASX, gen_xx_i, gen_helper_vssrani_w_d) ++TRANS(xvssrani_d_q, LASX, gen_xx_i, gen_helper_vssrani_d_q) ++TRANS(xvssrlni_bu_h, LASX, gen_xx_i, gen_helper_vssrlni_bu_h) ++TRANS(xvssrlni_hu_w, LASX, gen_xx_i, gen_helper_vssrlni_hu_w) ++TRANS(xvssrlni_wu_d, LASX, gen_xx_i, gen_helper_vssrlni_wu_d) ++TRANS(xvssrlni_du_q, LASX, gen_xx_i, gen_helper_vssrlni_du_q) ++TRANS(xvssrani_bu_h, LASX, gen_xx_i, gen_helper_vssrani_bu_h) ++TRANS(xvssrani_hu_w, LASX, gen_xx_i, gen_helper_vssrani_hu_w) ++TRANS(xvssrani_wu_d, LASX, gen_xx_i, gen_helper_vssrani_wu_d) ++TRANS(xvssrani_du_q, LASX, gen_xx_i, gen_helper_vssrani_du_q) ++ ++TRANS(vssrlrn_b_h, LSX, gen_vvv, gen_helper_vssrlrn_b_h) ++TRANS(vssrlrn_h_w, LSX, gen_vvv, gen_helper_vssrlrn_h_w) ++TRANS(vssrlrn_w_d, LSX, gen_vvv, gen_helper_vssrlrn_w_d) ++TRANS(vssrarn_b_h, LSX, gen_vvv, gen_helper_vssrarn_b_h) ++TRANS(vssrarn_h_w, LSX, gen_vvv, gen_helper_vssrarn_h_w) ++TRANS(vssrarn_w_d, LSX, gen_vvv, gen_helper_vssrarn_w_d) ++TRANS(vssrlrn_bu_h, LSX, gen_vvv, gen_helper_vssrlrn_bu_h) ++TRANS(vssrlrn_hu_w, LSX, gen_vvv, gen_helper_vssrlrn_hu_w) ++TRANS(vssrlrn_wu_d, LSX, gen_vvv, gen_helper_vssrlrn_wu_d) ++TRANS(vssrarn_bu_h, LSX, gen_vvv, gen_helper_vssrarn_bu_h) ++TRANS(vssrarn_hu_w, LSX, gen_vvv, gen_helper_vssrarn_hu_w) ++TRANS(vssrarn_wu_d, LSX, gen_vvv, gen_helper_vssrarn_wu_d) ++TRANS(xvssrlrn_b_h, LASX, gen_xxx, gen_helper_vssrlrn_b_h) ++TRANS(xvssrlrn_h_w, LASX, gen_xxx, gen_helper_vssrlrn_h_w) ++TRANS(xvssrlrn_w_d, LASX, gen_xxx, gen_helper_vssrlrn_w_d) ++TRANS(xvssrarn_b_h, LASX, gen_xxx, gen_helper_vssrarn_b_h) ++TRANS(xvssrarn_h_w, LASX, gen_xxx, gen_helper_vssrarn_h_w) ++TRANS(xvssrarn_w_d, LASX, gen_xxx, gen_helper_vssrarn_w_d) ++TRANS(xvssrlrn_bu_h, LASX, gen_xxx, gen_helper_vssrlrn_bu_h) ++TRANS(xvssrlrn_hu_w, LASX, gen_xxx, gen_helper_vssrlrn_hu_w) ++TRANS(xvssrlrn_wu_d, LASX, gen_xxx, gen_helper_vssrlrn_wu_d) ++TRANS(xvssrarn_bu_h, LASX, gen_xxx, gen_helper_vssrarn_bu_h) ++TRANS(xvssrarn_hu_w, LASX, gen_xxx, gen_helper_vssrarn_hu_w) ++TRANS(xvssrarn_wu_d, LASX, gen_xxx, gen_helper_vssrarn_wu_d) ++ ++TRANS(vssrlrni_b_h, LSX, gen_vv_i, gen_helper_vssrlrni_b_h) ++TRANS(vssrlrni_h_w, LSX, gen_vv_i, gen_helper_vssrlrni_h_w) ++TRANS(vssrlrni_w_d, LSX, gen_vv_i, gen_helper_vssrlrni_w_d) ++TRANS(vssrlrni_d_q, LSX, gen_vv_i, gen_helper_vssrlrni_d_q) ++TRANS(vssrarni_b_h, LSX, gen_vv_i, gen_helper_vssrarni_b_h) ++TRANS(vssrarni_h_w, LSX, gen_vv_i, gen_helper_vssrarni_h_w) ++TRANS(vssrarni_w_d, LSX, gen_vv_i, gen_helper_vssrarni_w_d) ++TRANS(vssrarni_d_q, LSX, gen_vv_i, gen_helper_vssrarni_d_q) ++TRANS(vssrlrni_bu_h, LSX, gen_vv_i, gen_helper_vssrlrni_bu_h) ++TRANS(vssrlrni_hu_w, LSX, gen_vv_i, gen_helper_vssrlrni_hu_w) ++TRANS(vssrlrni_wu_d, LSX, gen_vv_i, gen_helper_vssrlrni_wu_d) ++TRANS(vssrlrni_du_q, LSX, gen_vv_i, gen_helper_vssrlrni_du_q) ++TRANS(vssrarni_bu_h, LSX, gen_vv_i, gen_helper_vssrarni_bu_h) ++TRANS(vssrarni_hu_w, LSX, gen_vv_i, gen_helper_vssrarni_hu_w) ++TRANS(vssrarni_wu_d, LSX, gen_vv_i, gen_helper_vssrarni_wu_d) ++TRANS(vssrarni_du_q, LSX, gen_vv_i, gen_helper_vssrarni_du_q) ++TRANS(xvssrlrni_b_h, LASX, gen_xx_i, gen_helper_vssrlrni_b_h) ++TRANS(xvssrlrni_h_w, LASX, gen_xx_i, gen_helper_vssrlrni_h_w) ++TRANS(xvssrlrni_w_d, LASX, gen_xx_i, gen_helper_vssrlrni_w_d) ++TRANS(xvssrlrni_d_q, LASX, gen_xx_i, gen_helper_vssrlrni_d_q) ++TRANS(xvssrarni_b_h, LASX, gen_xx_i, gen_helper_vssrarni_b_h) ++TRANS(xvssrarni_h_w, LASX, gen_xx_i, gen_helper_vssrarni_h_w) ++TRANS(xvssrarni_w_d, LASX, gen_xx_i, gen_helper_vssrarni_w_d) ++TRANS(xvssrarni_d_q, LASX, gen_xx_i, gen_helper_vssrarni_d_q) ++TRANS(xvssrlrni_bu_h, LASX, gen_xx_i, gen_helper_vssrlrni_bu_h) ++TRANS(xvssrlrni_hu_w, LASX, gen_xx_i, gen_helper_vssrlrni_hu_w) ++TRANS(xvssrlrni_wu_d, LASX, gen_xx_i, gen_helper_vssrlrni_wu_d) ++TRANS(xvssrlrni_du_q, LASX, gen_xx_i, gen_helper_vssrlrni_du_q) ++TRANS(xvssrarni_bu_h, LASX, gen_xx_i, gen_helper_vssrarni_bu_h) ++TRANS(xvssrarni_hu_w, LASX, gen_xx_i, gen_helper_vssrarni_hu_w) ++TRANS(xvssrarni_wu_d, LASX, gen_xx_i, gen_helper_vssrarni_wu_d) ++TRANS(xvssrarni_du_q, LASX, gen_xx_i, gen_helper_vssrarni_du_q) ++ ++TRANS(vclo_b, LSX, gen_vv, gen_helper_vclo_b) ++TRANS(vclo_h, LSX, gen_vv, gen_helper_vclo_h) ++TRANS(vclo_w, LSX, gen_vv, gen_helper_vclo_w) ++TRANS(vclo_d, LSX, gen_vv, gen_helper_vclo_d) ++TRANS(vclz_b, LSX, gen_vv, gen_helper_vclz_b) ++TRANS(vclz_h, LSX, gen_vv, gen_helper_vclz_h) ++TRANS(vclz_w, LSX, gen_vv, gen_helper_vclz_w) ++TRANS(vclz_d, LSX, gen_vv, gen_helper_vclz_d) ++TRANS(xvclo_b, LASX, gen_xx, gen_helper_vclo_b) ++TRANS(xvclo_h, LASX, gen_xx, gen_helper_vclo_h) ++TRANS(xvclo_w, LASX, gen_xx, gen_helper_vclo_w) ++TRANS(xvclo_d, LASX, gen_xx, gen_helper_vclo_d) ++TRANS(xvclz_b, LASX, gen_xx, gen_helper_vclz_b) ++TRANS(xvclz_h, LASX, gen_xx, gen_helper_vclz_h) ++TRANS(xvclz_w, LASX, gen_xx, gen_helper_vclz_w) ++TRANS(xvclz_d, LASX, gen_xx, gen_helper_vclz_d) ++ ++TRANS(vpcnt_b, LSX, gen_vv, gen_helper_vpcnt_b) ++TRANS(vpcnt_h, LSX, gen_vv, gen_helper_vpcnt_h) ++TRANS(vpcnt_w, LSX, gen_vv, gen_helper_vpcnt_w) ++TRANS(vpcnt_d, LSX, gen_vv, gen_helper_vpcnt_d) ++TRANS(xvpcnt_b, LASX, gen_xx, gen_helper_vpcnt_b) ++TRANS(xvpcnt_h, LASX, gen_xx, gen_helper_vpcnt_h) ++TRANS(xvpcnt_w, LASX, gen_xx, gen_helper_vpcnt_w) ++TRANS(xvpcnt_d, LASX, gen_xx, gen_helper_vpcnt_d) ++ ++static void do_vbit(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b, ++ void (*func)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec)) ++{ ++ TCGv_vec mask, lsh, t1, one; ++ ++ lsh = tcg_temp_new_vec_matching(t); ++ t1 = tcg_temp_new_vec_matching(t); ++ mask = tcg_constant_vec_matching(t, vece, (8 << vece) - 1); ++ one = tcg_constant_vec_matching(t, vece, 1); ++ ++ tcg_gen_and_vec(vece, lsh, b, mask); ++ tcg_gen_shlv_vec(vece, t1, one, lsh); ++ func(vece, t, a, t1); ++} ++ ++static void gen_vbitclr(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ do_vbit(vece, t, a, b, tcg_gen_andc_vec); ++} ++ ++static void gen_vbitset(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ do_vbit(vece, t, a, b, tcg_gen_or_vec); ++} ++ ++static void gen_vbitrev(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b) ++{ ++ do_vbit(vece, t, a, b, tcg_gen_xor_vec); ++} ++ ++static void do_vbitclr(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_shlv_vec, INDEX_op_andc_vec, 0 ++ }; ++ static const GVecGen3 op[4] = { ++ { ++ .fniv = gen_vbitclr, ++ .fno = gen_helper_vbitclr_b, ++ .opt_opc = vecop_list, ++ .vece = MO_8 ++ }, ++ { ++ .fniv = gen_vbitclr, ++ .fno = gen_helper_vbitclr_h, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fniv = gen_vbitclr, ++ .fno = gen_helper_vbitclr_w, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fniv = gen_vbitclr, ++ .fno = gen_helper_vbitclr_d, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vbitclr_b, LSX, gvec_vvv, MO_8, do_vbitclr) ++TRANS(vbitclr_h, LSX, gvec_vvv, MO_16, do_vbitclr) ++TRANS(vbitclr_w, LSX, gvec_vvv, MO_32, do_vbitclr) ++TRANS(vbitclr_d, LSX, gvec_vvv, MO_64, do_vbitclr) ++TRANS(xvbitclr_b, LASX, gvec_xxx, MO_8, do_vbitclr) ++TRANS(xvbitclr_h, LASX, gvec_xxx, MO_16, do_vbitclr) ++TRANS(xvbitclr_w, LASX, gvec_xxx, MO_32, do_vbitclr) ++TRANS(xvbitclr_d, LASX, gvec_xxx, MO_64, do_vbitclr) ++ ++static void do_vbiti(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm, ++ void (*func)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec)) ++{ ++ int lsh; ++ TCGv_vec t1, one; ++ ++ lsh = imm & ((8 << vece) -1); ++ t1 = tcg_temp_new_vec_matching(t); ++ one = tcg_constant_vec_matching(t, vece, 1); ++ ++ tcg_gen_shli_vec(vece, t1, one, lsh); ++ func(vece, t, a, t1); ++} ++ ++static void gen_vbitclri(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) ++{ ++ do_vbiti(vece, t, a, imm, tcg_gen_andc_vec); ++} ++ ++static void gen_vbitseti(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) ++{ ++ do_vbiti(vece, t, a, imm, tcg_gen_or_vec); ++} ++ ++static void gen_vbitrevi(unsigned vece, TCGv_vec t, TCGv_vec a, int64_t imm) ++{ ++ do_vbiti(vece, t, a, imm, tcg_gen_xor_vec); ++} ++ ++static void do_vbitclri(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ int64_t imm, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_shli_vec, INDEX_op_andc_vec, 0 ++ }; ++ static const GVecGen2i op[4] = { ++ { ++ .fniv = gen_vbitclri, ++ .fnoi = gen_helper_vbitclri_b, ++ .opt_opc = vecop_list, ++ .vece = MO_8 ++ }, ++ { ++ .fniv = gen_vbitclri, ++ .fnoi = gen_helper_vbitclri_h, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fniv = gen_vbitclri, ++ .fnoi = gen_helper_vbitclri_w, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fniv = gen_vbitclri, ++ .fnoi = gen_helper_vbitclri_d, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op[vece]); ++} ++ ++TRANS(vbitclri_b, LSX, gvec_vv_i, MO_8, do_vbitclri) ++TRANS(vbitclri_h, LSX, gvec_vv_i, MO_16, do_vbitclri) ++TRANS(vbitclri_w, LSX, gvec_vv_i, MO_32, do_vbitclri) ++TRANS(vbitclri_d, LSX, gvec_vv_i, MO_64, do_vbitclri) ++TRANS(xvbitclri_b, LASX, gvec_xx_i, MO_8, do_vbitclri) ++TRANS(xvbitclri_h, LASX, gvec_xx_i, MO_16, do_vbitclri) ++TRANS(xvbitclri_w, LASX, gvec_xx_i, MO_32, do_vbitclri) ++TRANS(xvbitclri_d, LASX, gvec_xx_i, MO_64, do_vbitclri) ++ ++static void do_vbitset(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_shlv_vec, 0 ++ }; ++ static const GVecGen3 op[4] = { ++ { ++ .fniv = gen_vbitset, ++ .fno = gen_helper_vbitset_b, ++ .opt_opc = vecop_list, ++ .vece = MO_8 ++ }, ++ { ++ .fniv = gen_vbitset, ++ .fno = gen_helper_vbitset_h, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fniv = gen_vbitset, ++ .fno = gen_helper_vbitset_w, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fniv = gen_vbitset, ++ .fno = gen_helper_vbitset_d, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vbitset_b, LSX, gvec_vvv, MO_8, do_vbitset) ++TRANS(vbitset_h, LSX, gvec_vvv, MO_16, do_vbitset) ++TRANS(vbitset_w, LSX, gvec_vvv, MO_32, do_vbitset) ++TRANS(vbitset_d, LSX, gvec_vvv, MO_64, do_vbitset) ++TRANS(xvbitset_b, LASX, gvec_xxx, MO_8, do_vbitset) ++TRANS(xvbitset_h, LASX, gvec_xxx, MO_16, do_vbitset) ++TRANS(xvbitset_w, LASX, gvec_xxx, MO_32, do_vbitset) ++TRANS(xvbitset_d, LASX, gvec_xxx, MO_64, do_vbitset) ++ ++static void do_vbitseti(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ int64_t imm, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_shli_vec, 0 ++ }; ++ static const GVecGen2i op[4] = { ++ { ++ .fniv = gen_vbitseti, ++ .fnoi = gen_helper_vbitseti_b, ++ .opt_opc = vecop_list, ++ .vece = MO_8 ++ }, ++ { ++ .fniv = gen_vbitseti, ++ .fnoi = gen_helper_vbitseti_h, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fniv = gen_vbitseti, ++ .fnoi = gen_helper_vbitseti_w, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fniv = gen_vbitseti, ++ .fnoi = gen_helper_vbitseti_d, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op[vece]); ++} ++ ++TRANS(vbitseti_b, LSX, gvec_vv_i, MO_8, do_vbitseti) ++TRANS(vbitseti_h, LSX, gvec_vv_i, MO_16, do_vbitseti) ++TRANS(vbitseti_w, LSX, gvec_vv_i, MO_32, do_vbitseti) ++TRANS(vbitseti_d, LSX, gvec_vv_i, MO_64, do_vbitseti) ++TRANS(xvbitseti_b, LASX, gvec_xx_i, MO_8, do_vbitseti) ++TRANS(xvbitseti_h, LASX, gvec_xx_i, MO_16, do_vbitseti) ++TRANS(xvbitseti_w, LASX, gvec_xx_i, MO_32, do_vbitseti) ++TRANS(xvbitseti_d, LASX, gvec_xx_i, MO_64, do_vbitseti) ++ ++static void do_vbitrev(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ uint32_t vk_ofs, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_shlv_vec, 0 ++ }; ++ static const GVecGen3 op[4] = { ++ { ++ .fniv = gen_vbitrev, ++ .fno = gen_helper_vbitrev_b, ++ .opt_opc = vecop_list, ++ .vece = MO_8 ++ }, ++ { ++ .fniv = gen_vbitrev, ++ .fno = gen_helper_vbitrev_h, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fniv = gen_vbitrev, ++ .fno = gen_helper_vbitrev_w, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fniv = gen_vbitrev, ++ .fno = gen_helper_vbitrev_d, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_3(vd_ofs, vj_ofs, vk_ofs, oprsz, maxsz, &op[vece]); ++} ++ ++TRANS(vbitrev_b, LSX, gvec_vvv, MO_8, do_vbitrev) ++TRANS(vbitrev_h, LSX, gvec_vvv, MO_16, do_vbitrev) ++TRANS(vbitrev_w, LSX, gvec_vvv, MO_32, do_vbitrev) ++TRANS(vbitrev_d, LSX, gvec_vvv, MO_64, do_vbitrev) ++TRANS(xvbitrev_b, LASX, gvec_xxx, MO_8, do_vbitrev) ++TRANS(xvbitrev_h, LASX, gvec_xxx, MO_16, do_vbitrev) ++TRANS(xvbitrev_w, LASX, gvec_xxx, MO_32, do_vbitrev) ++TRANS(xvbitrev_d, LASX, gvec_xxx, MO_64, do_vbitrev) ++ ++static void do_vbitrevi(unsigned vece, uint32_t vd_ofs, uint32_t vj_ofs, ++ int64_t imm, uint32_t oprsz, uint32_t maxsz) ++{ ++ static const TCGOpcode vecop_list[] = { ++ INDEX_op_shli_vec, 0 ++ }; ++ static const GVecGen2i op[4] = { ++ { ++ .fniv = gen_vbitrevi, ++ .fnoi = gen_helper_vbitrevi_b, ++ .opt_opc = vecop_list, ++ .vece = MO_8 ++ }, ++ { ++ .fniv = gen_vbitrevi, ++ .fnoi = gen_helper_vbitrevi_h, ++ .opt_opc = vecop_list, ++ .vece = MO_16 ++ }, ++ { ++ .fniv = gen_vbitrevi, ++ .fnoi = gen_helper_vbitrevi_w, ++ .opt_opc = vecop_list, ++ .vece = MO_32 ++ }, ++ { ++ .fniv = gen_vbitrevi, ++ .fnoi = gen_helper_vbitrevi_d, ++ .opt_opc = vecop_list, ++ .vece = MO_64 ++ }, ++ }; ++ ++ tcg_gen_gvec_2i(vd_ofs, vj_ofs, oprsz, maxsz, imm, &op[vece]); ++} ++ ++TRANS(vbitrevi_b, LSX, gvec_vv_i, MO_8, do_vbitrevi) ++TRANS(vbitrevi_h, LSX, gvec_vv_i, MO_16, do_vbitrevi) ++TRANS(vbitrevi_w, LSX, gvec_vv_i, MO_32, do_vbitrevi) ++TRANS(vbitrevi_d, LSX, gvec_vv_i, MO_64, do_vbitrevi) ++TRANS(xvbitrevi_b, LASX, gvec_xx_i, MO_8, do_vbitrevi) ++TRANS(xvbitrevi_h, LASX, gvec_xx_i, MO_16, do_vbitrevi) ++TRANS(xvbitrevi_w, LASX, gvec_xx_i, MO_32, do_vbitrevi) ++TRANS(xvbitrevi_d, LASX, gvec_xx_i, MO_64, do_vbitrevi) ++ ++TRANS(vfrstp_b, LSX, gen_vvv, gen_helper_vfrstp_b) ++TRANS(vfrstp_h, LSX, gen_vvv, gen_helper_vfrstp_h) ++TRANS(vfrstpi_b, LSX, gen_vv_i, gen_helper_vfrstpi_b) ++TRANS(vfrstpi_h, LSX, gen_vv_i, gen_helper_vfrstpi_h) ++TRANS(xvfrstp_b, LASX, gen_xxx, gen_helper_vfrstp_b) ++TRANS(xvfrstp_h, LASX, gen_xxx, gen_helper_vfrstp_h) ++TRANS(xvfrstpi_b, LASX, gen_xx_i, gen_helper_vfrstpi_b) ++TRANS(xvfrstpi_h, LASX, gen_xx_i, gen_helper_vfrstpi_h) ++ ++TRANS(vfadd_s, LSX, gen_vvv_ptr, gen_helper_vfadd_s) ++TRANS(vfadd_d, LSX, gen_vvv_ptr, gen_helper_vfadd_d) ++TRANS(vfsub_s, LSX, gen_vvv_ptr, gen_helper_vfsub_s) ++TRANS(vfsub_d, LSX, gen_vvv_ptr, gen_helper_vfsub_d) ++TRANS(vfmul_s, LSX, gen_vvv_ptr, gen_helper_vfmul_s) ++TRANS(vfmul_d, LSX, gen_vvv_ptr, gen_helper_vfmul_d) ++TRANS(vfdiv_s, LSX, gen_vvv_ptr, gen_helper_vfdiv_s) ++TRANS(vfdiv_d, LSX, gen_vvv_ptr, gen_helper_vfdiv_d) ++TRANS(xvfadd_s, LASX, gen_xxx_ptr, gen_helper_vfadd_s) ++TRANS(xvfadd_d, LASX, gen_xxx_ptr, gen_helper_vfadd_d) ++TRANS(xvfsub_s, LASX, gen_xxx_ptr, gen_helper_vfsub_s) ++TRANS(xvfsub_d, LASX, gen_xxx_ptr, gen_helper_vfsub_d) ++TRANS(xvfmul_s, LASX, gen_xxx_ptr, gen_helper_vfmul_s) ++TRANS(xvfmul_d, LASX, gen_xxx_ptr, gen_helper_vfmul_d) ++TRANS(xvfdiv_s, LASX, gen_xxx_ptr, gen_helper_vfdiv_s) ++TRANS(xvfdiv_d, LASX, gen_xxx_ptr, gen_helper_vfdiv_d) ++ ++TRANS(vfmadd_s, LSX, gen_vvvv_ptr, gen_helper_vfmadd_s) ++TRANS(vfmadd_d, LSX, gen_vvvv_ptr, gen_helper_vfmadd_d) ++TRANS(vfmsub_s, LSX, gen_vvvv_ptr, gen_helper_vfmsub_s) ++TRANS(vfmsub_d, LSX, gen_vvvv_ptr, gen_helper_vfmsub_d) ++TRANS(vfnmadd_s, LSX, gen_vvvv_ptr, gen_helper_vfnmadd_s) ++TRANS(vfnmadd_d, LSX, gen_vvvv_ptr, gen_helper_vfnmadd_d) ++TRANS(vfnmsub_s, LSX, gen_vvvv_ptr, gen_helper_vfnmsub_s) ++TRANS(vfnmsub_d, LSX, gen_vvvv_ptr, gen_helper_vfnmsub_d) ++TRANS(xvfmadd_s, LASX, gen_xxxx_ptr, gen_helper_vfmadd_s) ++TRANS(xvfmadd_d, LASX, gen_xxxx_ptr, gen_helper_vfmadd_d) ++TRANS(xvfmsub_s, LASX, gen_xxxx_ptr, gen_helper_vfmsub_s) ++TRANS(xvfmsub_d, LASX, gen_xxxx_ptr, gen_helper_vfmsub_d) ++TRANS(xvfnmadd_s, LASX, gen_xxxx_ptr, gen_helper_vfnmadd_s) ++TRANS(xvfnmadd_d, LASX, gen_xxxx_ptr, gen_helper_vfnmadd_d) ++TRANS(xvfnmsub_s, LASX, gen_xxxx_ptr, gen_helper_vfnmsub_s) ++TRANS(xvfnmsub_d, LASX, gen_xxxx_ptr, gen_helper_vfnmsub_d) ++ ++TRANS(vfmax_s, LSX, gen_vvv_ptr, gen_helper_vfmax_s) ++TRANS(vfmax_d, LSX, gen_vvv_ptr, gen_helper_vfmax_d) ++TRANS(vfmin_s, LSX, gen_vvv_ptr, gen_helper_vfmin_s) ++TRANS(vfmin_d, LSX, gen_vvv_ptr, gen_helper_vfmin_d) ++TRANS(xvfmax_s, LASX, gen_xxx_ptr, gen_helper_vfmax_s) ++TRANS(xvfmax_d, LASX, gen_xxx_ptr, gen_helper_vfmax_d) ++TRANS(xvfmin_s, LASX, gen_xxx_ptr, gen_helper_vfmin_s) ++TRANS(xvfmin_d, LASX, gen_xxx_ptr, gen_helper_vfmin_d) ++ ++TRANS(vfmaxa_s, LSX, gen_vvv_ptr, gen_helper_vfmaxa_s) ++TRANS(vfmaxa_d, LSX, gen_vvv_ptr, gen_helper_vfmaxa_d) ++TRANS(vfmina_s, LSX, gen_vvv_ptr, gen_helper_vfmina_s) ++TRANS(vfmina_d, LSX, gen_vvv_ptr, gen_helper_vfmina_d) ++TRANS(xvfmaxa_s, LASX, gen_xxx_ptr, gen_helper_vfmaxa_s) ++TRANS(xvfmaxa_d, LASX, gen_xxx_ptr, gen_helper_vfmaxa_d) ++TRANS(xvfmina_s, LASX, gen_xxx_ptr, gen_helper_vfmina_s) ++TRANS(xvfmina_d, LASX, gen_xxx_ptr, gen_helper_vfmina_d) ++ ++TRANS(vflogb_s, LSX, gen_vv_ptr, gen_helper_vflogb_s) ++TRANS(vflogb_d, LSX, gen_vv_ptr, gen_helper_vflogb_d) ++TRANS(xvflogb_s, LASX, gen_xx_ptr, gen_helper_vflogb_s) ++TRANS(xvflogb_d, LASX, gen_xx_ptr, gen_helper_vflogb_d) ++ ++TRANS(vfclass_s, LSX, gen_vv_ptr, gen_helper_vfclass_s) ++TRANS(vfclass_d, LSX, gen_vv_ptr, gen_helper_vfclass_d) ++TRANS(xvfclass_s, LASX, gen_xx_ptr, gen_helper_vfclass_s) ++TRANS(xvfclass_d, LASX, gen_xx_ptr, gen_helper_vfclass_d) ++ ++TRANS(vfsqrt_s, LSX, gen_vv_ptr, gen_helper_vfsqrt_s) ++TRANS(vfsqrt_d, LSX, gen_vv_ptr, gen_helper_vfsqrt_d) ++TRANS(vfrecip_s, LSX, gen_vv_ptr, gen_helper_vfrecip_s) ++TRANS(vfrecip_d, LSX, gen_vv_ptr, gen_helper_vfrecip_d) ++TRANS(vfrsqrt_s, LSX, gen_vv_ptr, gen_helper_vfrsqrt_s) ++TRANS(vfrsqrt_d, LSX, gen_vv_ptr, gen_helper_vfrsqrt_d) ++TRANS(xvfsqrt_s, LASX, gen_xx_ptr, gen_helper_vfsqrt_s) ++TRANS(xvfsqrt_d, LASX, gen_xx_ptr, gen_helper_vfsqrt_d) ++TRANS(xvfrecip_s, LASX, gen_xx_ptr, gen_helper_vfrecip_s) ++TRANS(xvfrecip_d, LASX, gen_xx_ptr, gen_helper_vfrecip_d) ++TRANS(xvfrsqrt_s, LASX, gen_xx_ptr, gen_helper_vfrsqrt_s) ++TRANS(xvfrsqrt_d, LASX, gen_xx_ptr, gen_helper_vfrsqrt_d) ++ ++TRANS(vfcvtl_s_h, LSX, gen_vv_ptr, gen_helper_vfcvtl_s_h) ++TRANS(vfcvth_s_h, LSX, gen_vv_ptr, gen_helper_vfcvth_s_h) ++TRANS(vfcvtl_d_s, LSX, gen_vv_ptr, gen_helper_vfcvtl_d_s) ++TRANS(vfcvth_d_s, LSX, gen_vv_ptr, gen_helper_vfcvth_d_s) ++TRANS(vfcvt_h_s, LSX, gen_vvv_ptr, gen_helper_vfcvt_h_s) ++TRANS(vfcvt_s_d, LSX, gen_vvv_ptr, gen_helper_vfcvt_s_d) ++TRANS(xvfcvtl_s_h, LASX, gen_xx_ptr, gen_helper_vfcvtl_s_h) ++TRANS(xvfcvth_s_h, LASX, gen_xx_ptr, gen_helper_vfcvth_s_h) ++TRANS(xvfcvtl_d_s, LASX, gen_xx_ptr, gen_helper_vfcvtl_d_s) ++TRANS(xvfcvth_d_s, LASX, gen_xx_ptr, gen_helper_vfcvth_d_s) ++TRANS(xvfcvt_h_s, LASX, gen_xxx_ptr, gen_helper_vfcvt_h_s) ++TRANS(xvfcvt_s_d, LASX, gen_xxx_ptr, gen_helper_vfcvt_s_d) ++ ++TRANS(vfrintrne_s, LSX, gen_vv_ptr, gen_helper_vfrintrne_s) ++TRANS(vfrintrne_d, LSX, gen_vv_ptr, gen_helper_vfrintrne_d) ++TRANS(vfrintrz_s, LSX, gen_vv_ptr, gen_helper_vfrintrz_s) ++TRANS(vfrintrz_d, LSX, gen_vv_ptr, gen_helper_vfrintrz_d) ++TRANS(vfrintrp_s, LSX, gen_vv_ptr, gen_helper_vfrintrp_s) ++TRANS(vfrintrp_d, LSX, gen_vv_ptr, gen_helper_vfrintrp_d) ++TRANS(vfrintrm_s, LSX, gen_vv_ptr, gen_helper_vfrintrm_s) ++TRANS(vfrintrm_d, LSX, gen_vv_ptr, gen_helper_vfrintrm_d) ++TRANS(vfrint_s, LSX, gen_vv_ptr, gen_helper_vfrint_s) ++TRANS(vfrint_d, LSX, gen_vv_ptr, gen_helper_vfrint_d) ++TRANS(xvfrintrne_s, LASX, gen_xx_ptr, gen_helper_vfrintrne_s) ++TRANS(xvfrintrne_d, LASX, gen_xx_ptr, gen_helper_vfrintrne_d) ++TRANS(xvfrintrz_s, LASX, gen_xx_ptr, gen_helper_vfrintrz_s) ++TRANS(xvfrintrz_d, LASX, gen_xx_ptr, gen_helper_vfrintrz_d) ++TRANS(xvfrintrp_s, LASX, gen_xx_ptr, gen_helper_vfrintrp_s) ++TRANS(xvfrintrp_d, LASX, gen_xx_ptr, gen_helper_vfrintrp_d) ++TRANS(xvfrintrm_s, LASX, gen_xx_ptr, gen_helper_vfrintrm_s) ++TRANS(xvfrintrm_d, LASX, gen_xx_ptr, gen_helper_vfrintrm_d) ++TRANS(xvfrint_s, LASX, gen_xx_ptr, gen_helper_vfrint_s) ++TRANS(xvfrint_d, LASX, gen_xx_ptr, gen_helper_vfrint_d) ++ ++TRANS(vftintrne_w_s, LSX, gen_vv_ptr, gen_helper_vftintrne_w_s) ++TRANS(vftintrne_l_d, LSX, gen_vv_ptr, gen_helper_vftintrne_l_d) ++TRANS(vftintrz_w_s, LSX, gen_vv_ptr, gen_helper_vftintrz_w_s) ++TRANS(vftintrz_l_d, LSX, gen_vv_ptr, gen_helper_vftintrz_l_d) ++TRANS(vftintrp_w_s, LSX, gen_vv_ptr, gen_helper_vftintrp_w_s) ++TRANS(vftintrp_l_d, LSX, gen_vv_ptr, gen_helper_vftintrp_l_d) ++TRANS(vftintrm_w_s, LSX, gen_vv_ptr, gen_helper_vftintrm_w_s) ++TRANS(vftintrm_l_d, LSX, gen_vv_ptr, gen_helper_vftintrm_l_d) ++TRANS(vftint_w_s, LSX, gen_vv_ptr, gen_helper_vftint_w_s) ++TRANS(vftint_l_d, LSX, gen_vv_ptr, gen_helper_vftint_l_d) ++TRANS(vftintrz_wu_s, LSX, gen_vv_ptr, gen_helper_vftintrz_wu_s) ++TRANS(vftintrz_lu_d, LSX, gen_vv_ptr, gen_helper_vftintrz_lu_d) ++TRANS(vftint_wu_s, LSX, gen_vv_ptr, gen_helper_vftint_wu_s) ++TRANS(vftint_lu_d, LSX, gen_vv_ptr, gen_helper_vftint_lu_d) ++TRANS(vftintrne_w_d, LSX, gen_vvv_ptr, gen_helper_vftintrne_w_d) ++TRANS(vftintrz_w_d, LSX, gen_vvv_ptr, gen_helper_vftintrz_w_d) ++TRANS(vftintrp_w_d, LSX, gen_vvv_ptr, gen_helper_vftintrp_w_d) ++TRANS(vftintrm_w_d, LSX, gen_vvv_ptr, gen_helper_vftintrm_w_d) ++TRANS(vftint_w_d, LSX, gen_vvv_ptr, gen_helper_vftint_w_d) ++TRANS(vftintrnel_l_s, LSX, gen_vv_ptr, gen_helper_vftintrnel_l_s) ++TRANS(vftintrneh_l_s, LSX, gen_vv_ptr, gen_helper_vftintrneh_l_s) ++TRANS(vftintrzl_l_s, LSX, gen_vv_ptr, gen_helper_vftintrzl_l_s) ++TRANS(vftintrzh_l_s, LSX, gen_vv_ptr, gen_helper_vftintrzh_l_s) ++TRANS(vftintrpl_l_s, LSX, gen_vv_ptr, gen_helper_vftintrpl_l_s) ++TRANS(vftintrph_l_s, LSX, gen_vv_ptr, gen_helper_vftintrph_l_s) ++TRANS(vftintrml_l_s, LSX, gen_vv_ptr, gen_helper_vftintrml_l_s) ++TRANS(vftintrmh_l_s, LSX, gen_vv_ptr, gen_helper_vftintrmh_l_s) ++TRANS(vftintl_l_s, LSX, gen_vv_ptr, gen_helper_vftintl_l_s) ++TRANS(vftinth_l_s, LSX, gen_vv_ptr, gen_helper_vftinth_l_s) ++TRANS(xvftintrne_w_s, LASX, gen_xx_ptr, gen_helper_vftintrne_w_s) ++TRANS(xvftintrne_l_d, LASX, gen_xx_ptr, gen_helper_vftintrne_l_d) ++TRANS(xvftintrz_w_s, LASX, gen_xx_ptr, gen_helper_vftintrz_w_s) ++TRANS(xvftintrz_l_d, LASX, gen_xx_ptr, gen_helper_vftintrz_l_d) ++TRANS(xvftintrp_w_s, LASX, gen_xx_ptr, gen_helper_vftintrp_w_s) ++TRANS(xvftintrp_l_d, LASX, gen_xx_ptr, gen_helper_vftintrp_l_d) ++TRANS(xvftintrm_w_s, LASX, gen_xx_ptr, gen_helper_vftintrm_w_s) ++TRANS(xvftintrm_l_d, LASX, gen_xx_ptr, gen_helper_vftintrm_l_d) ++TRANS(xvftint_w_s, LASX, gen_xx_ptr, gen_helper_vftint_w_s) ++TRANS(xvftint_l_d, LASX, gen_xx_ptr, gen_helper_vftint_l_d) ++TRANS(xvftintrz_wu_s, LASX, gen_xx_ptr, gen_helper_vftintrz_wu_s) ++TRANS(xvftintrz_lu_d, LASX, gen_xx_ptr, gen_helper_vftintrz_lu_d) ++TRANS(xvftint_wu_s, LASX, gen_xx_ptr, gen_helper_vftint_wu_s) ++TRANS(xvftint_lu_d, LASX, gen_xx_ptr, gen_helper_vftint_lu_d) ++TRANS(xvftintrne_w_d, LASX, gen_xxx_ptr, gen_helper_vftintrne_w_d) ++TRANS(xvftintrz_w_d, LASX, gen_xxx_ptr, gen_helper_vftintrz_w_d) ++TRANS(xvftintrp_w_d, LASX, gen_xxx_ptr, gen_helper_vftintrp_w_d) ++TRANS(xvftintrm_w_d, LASX, gen_xxx_ptr, gen_helper_vftintrm_w_d) ++TRANS(xvftint_w_d, LASX, gen_xxx_ptr, gen_helper_vftint_w_d) ++TRANS(xvftintrnel_l_s, LASX, gen_xx_ptr, gen_helper_vftintrnel_l_s) ++TRANS(xvftintrneh_l_s, LASX, gen_xx_ptr, gen_helper_vftintrneh_l_s) ++TRANS(xvftintrzl_l_s, LASX, gen_xx_ptr, gen_helper_vftintrzl_l_s) ++TRANS(xvftintrzh_l_s, LASX, gen_xx_ptr, gen_helper_vftintrzh_l_s) ++TRANS(xvftintrpl_l_s, LASX, gen_xx_ptr, gen_helper_vftintrpl_l_s) ++TRANS(xvftintrph_l_s, LASX, gen_xx_ptr, gen_helper_vftintrph_l_s) ++TRANS(xvftintrml_l_s, LASX, gen_xx_ptr, gen_helper_vftintrml_l_s) ++TRANS(xvftintrmh_l_s, LASX, gen_xx_ptr, gen_helper_vftintrmh_l_s) ++TRANS(xvftintl_l_s, LASX, gen_xx_ptr, gen_helper_vftintl_l_s) ++TRANS(xvftinth_l_s, LASX, gen_xx_ptr, gen_helper_vftinth_l_s) ++ ++TRANS(vffint_s_w, LSX, gen_vv_ptr, gen_helper_vffint_s_w) ++TRANS(vffint_d_l, LSX, gen_vv_ptr, gen_helper_vffint_d_l) ++TRANS(vffint_s_wu, LSX, gen_vv_ptr, gen_helper_vffint_s_wu) ++TRANS(vffint_d_lu, LSX, gen_vv_ptr, gen_helper_vffint_d_lu) ++TRANS(vffintl_d_w, LSX, gen_vv_ptr, gen_helper_vffintl_d_w) ++TRANS(vffinth_d_w, LSX, gen_vv_ptr, gen_helper_vffinth_d_w) ++TRANS(vffint_s_l, LSX, gen_vvv_ptr, gen_helper_vffint_s_l) ++TRANS(xvffint_s_w, LASX, gen_xx_ptr, gen_helper_vffint_s_w) ++TRANS(xvffint_d_l, LASX, gen_xx_ptr, gen_helper_vffint_d_l) ++TRANS(xvffint_s_wu, LASX, gen_xx_ptr, gen_helper_vffint_s_wu) ++TRANS(xvffint_d_lu, LASX, gen_xx_ptr, gen_helper_vffint_d_lu) ++TRANS(xvffintl_d_w, LASX, gen_xx_ptr, gen_helper_vffintl_d_w) ++TRANS(xvffinth_d_w, LASX, gen_xx_ptr, gen_helper_vffinth_d_w) ++TRANS(xvffint_s_l, LASX, gen_xxx_ptr, gen_helper_vffint_s_l) ++ ++static bool do_cmp_vl(DisasContext *ctx, arg_vvv *a, ++ uint32_t oprsz, MemOp mop, TCGCond cond) ++{ ++ uint32_t vd_ofs, vj_ofs, vk_ofs; ++ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ vd_ofs = vec_full_offset(a->vd); ++ vj_ofs = vec_full_offset(a->vj); ++ vk_ofs = vec_full_offset(a->vk); ++ ++ tcg_gen_gvec_cmp(cond, mop, vd_ofs, vj_ofs, vk_ofs, oprsz, ctx->vl / 8); ++ return true; ++} ++ ++static bool do_cmp(DisasContext *ctx, arg_vvv *a, ++ MemOp mop, TCGCond cond) ++{ ++ return do_cmp_vl(ctx, a, 16, mop, cond); ++} ++ ++static bool do_xcmp(DisasContext *ctx, arg_vvv *a, ++ MemOp mop, TCGCond cond) ++{ ++ return do_cmp_vl(ctx, a, 32, mop, cond); ++} ++ ++static bool do_cmpi_vl(DisasContext *ctx, arg_vv_i *a, ++ uint32_t oprsz, MemOp mop, TCGCond cond) ++{ ++ uint32_t vd_ofs, vj_ofs; ++ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ vd_ofs = vec_full_offset(a->vd); ++ vj_ofs = vec_full_offset(a->vj); ++ ++ tcg_gen_gvec_cmpi(cond, mop, vd_ofs, vj_ofs, a->imm, oprsz, ctx->vl / 8); ++ return true; ++} ++ ++static bool do_cmpi(DisasContext *ctx, arg_vv_i *a, ++ MemOp mop, TCGCond cond) ++{ ++ return do_cmpi_vl(ctx, a, 16, mop, cond); ++} ++ ++static bool do_xcmpi(DisasContext *ctx, arg_vv_i *a, ++ MemOp mop, TCGCond cond) ++{ ++ return do_cmpi_vl(ctx, a, 32, mop, cond); ++} ++ ++TRANS(vseq_b, LSX, do_cmp, MO_8, TCG_COND_EQ) ++TRANS(vseq_h, LSX, do_cmp, MO_16, TCG_COND_EQ) ++TRANS(vseq_w, LSX, do_cmp, MO_32, TCG_COND_EQ) ++TRANS(vseq_d, LSX, do_cmp, MO_64, TCG_COND_EQ) ++TRANS(vseqi_b, LSX, do_cmpi, MO_8, TCG_COND_EQ) ++TRANS(vseqi_h, LSX, do_cmpi, MO_16, TCG_COND_EQ) ++TRANS(vseqi_w, LSX, do_cmpi, MO_32, TCG_COND_EQ) ++TRANS(vseqi_d, LSX, do_cmpi, MO_64, TCG_COND_EQ) ++TRANS(xvseq_b, LASX, do_xcmp, MO_8, TCG_COND_EQ) ++TRANS(xvseq_h, LASX, do_xcmp, MO_16, TCG_COND_EQ) ++TRANS(xvseq_w, LASX, do_xcmp, MO_32, TCG_COND_EQ) ++TRANS(xvseq_d, LASX, do_xcmp, MO_64, TCG_COND_EQ) ++TRANS(xvseqi_b, LASX, do_xcmpi, MO_8, TCG_COND_EQ) ++TRANS(xvseqi_h, LASX, do_xcmpi, MO_16, TCG_COND_EQ) ++TRANS(xvseqi_w, LASX, do_xcmpi, MO_32, TCG_COND_EQ) ++TRANS(xvseqi_d, LASX, do_xcmpi, MO_64, TCG_COND_EQ) ++ ++TRANS(vsle_b, LSX, do_cmp, MO_8, TCG_COND_LE) ++TRANS(vsle_h, LSX, do_cmp, MO_16, TCG_COND_LE) ++TRANS(vsle_w, LSX, do_cmp, MO_32, TCG_COND_LE) ++TRANS(vsle_d, LSX, do_cmp, MO_64, TCG_COND_LE) ++TRANS(vslei_b, LSX, do_cmpi, MO_8, TCG_COND_LE) ++TRANS(vslei_h, LSX, do_cmpi, MO_16, TCG_COND_LE) ++TRANS(vslei_w, LSX, do_cmpi, MO_32, TCG_COND_LE) ++TRANS(vslei_d, LSX, do_cmpi, MO_64, TCG_COND_LE) ++TRANS(vsle_bu, LSX, do_cmp, MO_8, TCG_COND_LEU) ++TRANS(vsle_hu, LSX, do_cmp, MO_16, TCG_COND_LEU) ++TRANS(vsle_wu, LSX, do_cmp, MO_32, TCG_COND_LEU) ++TRANS(vsle_du, LSX, do_cmp, MO_64, TCG_COND_LEU) ++TRANS(vslei_bu, LSX, do_cmpi, MO_8, TCG_COND_LEU) ++TRANS(vslei_hu, LSX, do_cmpi, MO_16, TCG_COND_LEU) ++TRANS(vslei_wu, LSX, do_cmpi, MO_32, TCG_COND_LEU) ++TRANS(vslei_du, LSX, do_cmpi, MO_64, TCG_COND_LEU) ++TRANS(xvsle_b, LASX, do_xcmp, MO_8, TCG_COND_LE) ++TRANS(xvsle_h, LASX, do_xcmp, MO_16, TCG_COND_LE) ++TRANS(xvsle_w, LASX, do_xcmp, MO_32, TCG_COND_LE) ++TRANS(xvsle_d, LASX, do_xcmp, MO_64, TCG_COND_LE) ++TRANS(xvslei_b, LASX, do_xcmpi, MO_8, TCG_COND_LE) ++TRANS(xvslei_h, LASX, do_xcmpi, MO_16, TCG_COND_LE) ++TRANS(xvslei_w, LASX, do_xcmpi, MO_32, TCG_COND_LE) ++TRANS(xvslei_d, LASX, do_xcmpi, MO_64, TCG_COND_LE) ++TRANS(xvsle_bu, LASX, do_xcmp, MO_8, TCG_COND_LEU) ++TRANS(xvsle_hu, LASX, do_xcmp, MO_16, TCG_COND_LEU) ++TRANS(xvsle_wu, LASX, do_xcmp, MO_32, TCG_COND_LEU) ++TRANS(xvsle_du, LASX, do_xcmp, MO_64, TCG_COND_LEU) ++TRANS(xvslei_bu, LASX, do_xcmpi, MO_8, TCG_COND_LEU) ++TRANS(xvslei_hu, LASX, do_xcmpi, MO_16, TCG_COND_LEU) ++TRANS(xvslei_wu, LASX, do_xcmpi, MO_32, TCG_COND_LEU) ++TRANS(xvslei_du, LASX, do_xcmpi, MO_64, TCG_COND_LEU) ++ ++TRANS(vslt_b, LSX, do_cmp, MO_8, TCG_COND_LT) ++TRANS(vslt_h, LSX, do_cmp, MO_16, TCG_COND_LT) ++TRANS(vslt_w, LSX, do_cmp, MO_32, TCG_COND_LT) ++TRANS(vslt_d, LSX, do_cmp, MO_64, TCG_COND_LT) ++TRANS(vslti_b, LSX, do_cmpi, MO_8, TCG_COND_LT) ++TRANS(vslti_h, LSX, do_cmpi, MO_16, TCG_COND_LT) ++TRANS(vslti_w, LSX, do_cmpi, MO_32, TCG_COND_LT) ++TRANS(vslti_d, LSX, do_cmpi, MO_64, TCG_COND_LT) ++TRANS(vslt_bu, LSX, do_cmp, MO_8, TCG_COND_LTU) ++TRANS(vslt_hu, LSX, do_cmp, MO_16, TCG_COND_LTU) ++TRANS(vslt_wu, LSX, do_cmp, MO_32, TCG_COND_LTU) ++TRANS(vslt_du, LSX, do_cmp, MO_64, TCG_COND_LTU) ++TRANS(vslti_bu, LSX, do_cmpi, MO_8, TCG_COND_LTU) ++TRANS(vslti_hu, LSX, do_cmpi, MO_16, TCG_COND_LTU) ++TRANS(vslti_wu, LSX, do_cmpi, MO_32, TCG_COND_LTU) ++TRANS(vslti_du, LSX, do_cmpi, MO_64, TCG_COND_LTU) ++TRANS(xvslt_b, LASX, do_xcmp, MO_8, TCG_COND_LT) ++TRANS(xvslt_h, LASX, do_xcmp, MO_16, TCG_COND_LT) ++TRANS(xvslt_w, LASX, do_xcmp, MO_32, TCG_COND_LT) ++TRANS(xvslt_d, LASX, do_xcmp, MO_64, TCG_COND_LT) ++TRANS(xvslti_b, LASX, do_xcmpi, MO_8, TCG_COND_LT) ++TRANS(xvslti_h, LASX, do_xcmpi, MO_16, TCG_COND_LT) ++TRANS(xvslti_w, LASX, do_xcmpi, MO_32, TCG_COND_LT) ++TRANS(xvslti_d, LASX, do_xcmpi, MO_64, TCG_COND_LT) ++TRANS(xvslt_bu, LASX, do_xcmp, MO_8, TCG_COND_LTU) ++TRANS(xvslt_hu, LASX, do_xcmp, MO_16, TCG_COND_LTU) ++TRANS(xvslt_wu, LASX, do_xcmp, MO_32, TCG_COND_LTU) ++TRANS(xvslt_du, LASX, do_xcmp, MO_64, TCG_COND_LTU) ++TRANS(xvslti_bu, LASX, do_xcmpi, MO_8, TCG_COND_LTU) ++TRANS(xvslti_hu, LASX, do_xcmpi, MO_16, TCG_COND_LTU) ++TRANS(xvslti_wu, LASX, do_xcmpi, MO_32, TCG_COND_LTU) ++TRANS(xvslti_du, LASX, do_xcmpi, MO_64, TCG_COND_LTU) ++ ++static bool do_vfcmp_cond_s(DisasContext *ctx, arg_vvv_fcond *a, uint32_t sz) ++{ ++ uint32_t flags; ++ void (*fn)(TCGv_env, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32); ++ TCGv_i32 vd = tcg_constant_i32(a->vd); ++ TCGv_i32 vj = tcg_constant_i32(a->vj); ++ TCGv_i32 vk = tcg_constant_i32(a->vk); ++ TCGv_i32 oprsz = tcg_constant_i32(sz); ++ ++ if (!check_vec(ctx, sz)) { ++ return true; ++ } ++ ++ fn = (a->fcond & 1 ? gen_helper_vfcmp_s_s : gen_helper_vfcmp_c_s); ++ flags = get_fcmp_flags(a->fcond >> 1); ++ fn(tcg_env, oprsz, vd, vj, vk, tcg_constant_i32(flags)); ++ ++ return true; ++} ++ ++static bool do_vfcmp_cond_d(DisasContext *ctx, arg_vvv_fcond *a, uint32_t sz) ++{ ++ uint32_t flags; ++ void (*fn)(TCGv_env, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32); ++ TCGv_i32 vd = tcg_constant_i32(a->vd); ++ TCGv_i32 vj = tcg_constant_i32(a->vj); ++ TCGv_i32 vk = tcg_constant_i32(a->vk); ++ TCGv_i32 oprsz = tcg_constant_i32(sz); ++ ++ if (!check_vec(ctx, sz)) { ++ return true; ++ } ++ ++ fn = (a->fcond & 1 ? gen_helper_vfcmp_s_d : gen_helper_vfcmp_c_d); ++ flags = get_fcmp_flags(a->fcond >> 1); ++ fn(tcg_env, oprsz, vd, vj, vk, tcg_constant_i32(flags)); ++ ++ return true; ++} ++ ++TRANS(vfcmp_cond_s, LSX, do_vfcmp_cond_s, 16) ++TRANS(vfcmp_cond_d, LSX, do_vfcmp_cond_d, 16) ++TRANS(xvfcmp_cond_s, LASX, do_vfcmp_cond_s, 32) ++TRANS(xvfcmp_cond_d, LASX, do_vfcmp_cond_d, 32) ++ ++static bool do_vbitsel_v(DisasContext *ctx, arg_vvvv *a, uint32_t oprsz) ++{ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ tcg_gen_gvec_bitsel(MO_64, vec_full_offset(a->vd), vec_full_offset(a->va), ++ vec_full_offset(a->vk), vec_full_offset(a->vj), ++ oprsz, ctx->vl / 8); ++ return true; ++} ++ ++TRANS(vbitsel_v, LSX, do_vbitsel_v, 16) ++TRANS(xvbitsel_v, LASX, do_vbitsel_v, 32) ++ ++static void gen_vbitseli(unsigned vece, TCGv_vec a, TCGv_vec b, int64_t imm) ++{ ++ tcg_gen_bitsel_vec(vece, a, a, tcg_constant_vec_matching(a, vece, imm), b); ++} ++ ++static bool do_vbitseli_b(DisasContext *ctx, arg_vv_i *a, uint32_t oprsz) ++{ ++ static const GVecGen2i op = { ++ .fniv = gen_vbitseli, ++ .fnoi = gen_helper_vbitseli_b, ++ .vece = MO_8, ++ .load_dest = true ++ }; ++ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ tcg_gen_gvec_2i(vec_full_offset(a->vd), vec_full_offset(a->vj), ++ oprsz, ctx->vl / 8, a->imm , &op); ++ return true; ++} ++ ++TRANS(vbitseli_b, LSX, do_vbitseli_b, 16) ++TRANS(xvbitseli_b, LASX, do_vbitseli_b, 32) ++ ++#define VSET(NAME, COND) \ ++static bool trans_## NAME (DisasContext *ctx, arg_cv *a) \ ++{ \ ++ TCGv_i64 t1, al, ah; \ ++ \ ++ al = tcg_temp_new_i64(); \ ++ ah = tcg_temp_new_i64(); \ ++ t1 = tcg_temp_new_i64(); \ ++ \ ++ get_vreg64(ah, a->vj, 1); \ ++ get_vreg64(al, a->vj, 0); \ ++ \ ++ if (!avail_LSX(ctx)) { \ ++ return false; \ ++ } \ ++ \ ++ if (!check_vec(ctx, 16)) { \ ++ return true; \ ++ } \ ++ \ ++ tcg_gen_or_i64(t1, al, ah); \ ++ tcg_gen_setcondi_i64(COND, t1, t1, 0); \ ++ tcg_gen_st8_tl(t1, tcg_env, offsetof(CPULoongArchState, cf[a->cd & 0x7])); \ ++ \ ++ return true; \ ++} ++ ++VSET(vseteqz_v, TCG_COND_EQ) ++VSET(vsetnez_v, TCG_COND_NE) ++ ++TRANS(vsetanyeqz_b, LSX, gen_cv, gen_helper_vsetanyeqz_b) ++TRANS(vsetanyeqz_h, LSX, gen_cv, gen_helper_vsetanyeqz_h) ++TRANS(vsetanyeqz_w, LSX, gen_cv, gen_helper_vsetanyeqz_w) ++TRANS(vsetanyeqz_d, LSX, gen_cv, gen_helper_vsetanyeqz_d) ++TRANS(vsetallnez_b, LSX, gen_cv, gen_helper_vsetallnez_b) ++TRANS(vsetallnez_h, LSX, gen_cv, gen_helper_vsetallnez_h) ++TRANS(vsetallnez_w, LSX, gen_cv, gen_helper_vsetallnez_w) ++TRANS(vsetallnez_d, LSX, gen_cv, gen_helper_vsetallnez_d) ++ ++#define XVSET(NAME, COND) \ ++static bool trans_## NAME(DisasContext *ctx, arg_cv * a) \ ++{ \ ++ TCGv_i64 t1, t2, d[4]; \ ++ \ ++ d[0] = tcg_temp_new_i64(); \ ++ d[1] = tcg_temp_new_i64(); \ ++ d[2] = tcg_temp_new_i64(); \ ++ d[3] = tcg_temp_new_i64(); \ ++ t1 = tcg_temp_new_i64(); \ ++ t2 = tcg_temp_new_i64(); \ ++ \ ++ get_vreg64(d[0], a->vj, 0); \ ++ get_vreg64(d[1], a->vj, 1); \ ++ get_vreg64(d[2], a->vj, 2); \ ++ get_vreg64(d[3], a->vj, 3); \ ++ \ ++ if (!avail_LASX(ctx)) { \ ++ return false; \ ++ } \ ++ \ ++ if (!check_vec(ctx, 32)) { \ ++ return true; \ ++ } \ ++ \ ++ tcg_gen_or_i64(t1, d[0], d[1]); \ ++ tcg_gen_or_i64(t2, d[2], d[3]); \ ++ tcg_gen_or_i64(t1, t2, t1); \ ++ tcg_gen_setcondi_i64(COND, t1, t1, 0); \ ++ tcg_gen_st8_tl(t1, tcg_env, offsetof(CPULoongArchState, cf[a->cd & 0x7])); \ ++ \ ++ return true; \ ++} ++ ++XVSET(xvseteqz_v, TCG_COND_EQ) ++XVSET(xvsetnez_v, TCG_COND_NE) ++ ++TRANS(xvsetanyeqz_b, LASX, gen_cx, gen_helper_vsetanyeqz_b) ++TRANS(xvsetanyeqz_h, LASX, gen_cx, gen_helper_vsetanyeqz_h) ++TRANS(xvsetanyeqz_w, LASX, gen_cx, gen_helper_vsetanyeqz_w) ++TRANS(xvsetanyeqz_d, LASX, gen_cx, gen_helper_vsetanyeqz_d) ++TRANS(xvsetallnez_b, LASX, gen_cx, gen_helper_vsetallnez_b) ++TRANS(xvsetallnez_h, LASX, gen_cx, gen_helper_vsetallnez_h) ++TRANS(xvsetallnez_w, LASX, gen_cx, gen_helper_vsetallnez_w) ++TRANS(xvsetallnez_d, LASX, gen_cx, gen_helper_vsetallnez_d) ++ ++static bool gen_g2v_vl(DisasContext *ctx, arg_vr_i *a, uint32_t oprsz, MemOp mop, ++ void (*func)(TCGv, TCGv_ptr, tcg_target_long)) ++{ ++ TCGv src = gpr_src(ctx, a->rj, EXT_NONE); ++ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ func(src, tcg_env, vec_reg_offset(a->vd, a->imm, mop)); ++ ++ return true; ++} ++ ++static bool gen_g2v(DisasContext *ctx, arg_vr_i *a, MemOp mop, ++ void (*func)(TCGv, TCGv_ptr, tcg_target_long)) ++{ ++ return gen_g2v_vl(ctx, a, 16, mop, func); ++} ++ ++static bool gen_g2x(DisasContext *ctx, arg_vr_i *a, MemOp mop, ++ void (*func)(TCGv, TCGv_ptr, tcg_target_long)) ++{ ++ return gen_g2v_vl(ctx, a, 32, mop, func); ++} ++ ++TRANS(vinsgr2vr_b, LSX, gen_g2v, MO_8, tcg_gen_st8_i64) ++TRANS(vinsgr2vr_h, LSX, gen_g2v, MO_16, tcg_gen_st16_i64) ++TRANS(vinsgr2vr_w, LSX, gen_g2v, MO_32, tcg_gen_st32_i64) ++TRANS(vinsgr2vr_d, LSX, gen_g2v, MO_64, tcg_gen_st_i64) ++TRANS(xvinsgr2vr_w, LASX, gen_g2x, MO_32, tcg_gen_st32_i64) ++TRANS(xvinsgr2vr_d, LASX, gen_g2x, MO_64, tcg_gen_st_i64) ++ ++static bool gen_v2g_vl(DisasContext *ctx, arg_rv_i *a, uint32_t oprsz, MemOp mop, ++ void (*func)(TCGv, TCGv_ptr, tcg_target_long)) ++{ ++ TCGv dst = gpr_dst(ctx, a->rd, EXT_NONE); ++ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ func(dst, tcg_env, vec_reg_offset(a->vj, a->imm, mop)); ++ ++ return true; ++} ++ ++static bool gen_v2g(DisasContext *ctx, arg_rv_i *a, MemOp mop, ++ void (*func)(TCGv, TCGv_ptr, tcg_target_long)) ++{ ++ return gen_v2g_vl(ctx, a, 16, mop, func); ++} ++ ++static bool gen_x2g(DisasContext *ctx, arg_rv_i *a, MemOp mop, ++ void (*func)(TCGv, TCGv_ptr, tcg_target_long)) ++{ ++ return gen_v2g_vl(ctx, a, 32, mop, func); ++} ++ ++TRANS(vpickve2gr_b, LSX, gen_v2g, MO_8, tcg_gen_ld8s_i64) ++TRANS(vpickve2gr_h, LSX, gen_v2g, MO_16, tcg_gen_ld16s_i64) ++TRANS(vpickve2gr_w, LSX, gen_v2g, MO_32, tcg_gen_ld32s_i64) ++TRANS(vpickve2gr_d, LSX, gen_v2g, MO_64, tcg_gen_ld_i64) ++TRANS(vpickve2gr_bu, LSX, gen_v2g, MO_8, tcg_gen_ld8u_i64) ++TRANS(vpickve2gr_hu, LSX, gen_v2g, MO_16, tcg_gen_ld16u_i64) ++TRANS(vpickve2gr_wu, LSX, gen_v2g, MO_32, tcg_gen_ld32u_i64) ++TRANS(vpickve2gr_du, LSX, gen_v2g, MO_64, tcg_gen_ld_i64) ++TRANS(xvpickve2gr_w, LASX, gen_x2g, MO_32, tcg_gen_ld32s_i64) ++TRANS(xvpickve2gr_d, LASX, gen_x2g, MO_64, tcg_gen_ld_i64) ++TRANS(xvpickve2gr_wu, LASX, gen_x2g, MO_32, tcg_gen_ld32u_i64) ++TRANS(xvpickve2gr_du, LASX, gen_x2g, MO_64, tcg_gen_ld_i64) ++ ++static bool gvec_dup_vl(DisasContext *ctx, arg_vr *a, ++ uint32_t oprsz, MemOp mop) ++{ ++ TCGv src = gpr_src(ctx, a->rj, EXT_NONE); ++ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ tcg_gen_gvec_dup_i64(mop, vec_full_offset(a->vd), ++ oprsz, ctx->vl/8, src); ++ return true; ++} ++ ++static bool gvec_dup(DisasContext *ctx, arg_vr *a, MemOp mop) ++{ ++ return gvec_dup_vl(ctx, a, 16, mop); ++} ++ ++static bool gvec_dupx(DisasContext *ctx, arg_vr *a, MemOp mop) ++{ ++ return gvec_dup_vl(ctx, a, 32, mop); ++} ++ ++TRANS(vreplgr2vr_b, LSX, gvec_dup, MO_8) ++TRANS(vreplgr2vr_h, LSX, gvec_dup, MO_16) ++TRANS(vreplgr2vr_w, LSX, gvec_dup, MO_32) ++TRANS(vreplgr2vr_d, LSX, gvec_dup, MO_64) ++TRANS(xvreplgr2vr_b, LASX, gvec_dupx, MO_8) ++TRANS(xvreplgr2vr_h, LASX, gvec_dupx, MO_16) ++TRANS(xvreplgr2vr_w, LASX, gvec_dupx, MO_32) ++TRANS(xvreplgr2vr_d, LASX, gvec_dupx, MO_64) ++ ++static bool trans_vreplvei_b(DisasContext *ctx, arg_vv_i *a) ++{ ++ if (!avail_LSX(ctx)) { ++ return false; ++ } ++ ++ if (!check_vec(ctx, 16)) { ++ return true; ++ } ++ ++ tcg_gen_gvec_dup_mem(MO_8,vec_full_offset(a->vd), ++ offsetof(CPULoongArchState, ++ fpr[a->vj].vreg.B((a->imm))), ++ 16, ctx->vl/8); ++ return true; ++} ++ ++static bool trans_vreplvei_h(DisasContext *ctx, arg_vv_i *a) ++{ ++ if (!avail_LSX(ctx)) { ++ return false; ++ } ++ ++ if (!check_vec(ctx, 16)) { ++ return true; ++ } ++ ++ tcg_gen_gvec_dup_mem(MO_16, vec_full_offset(a->vd), ++ offsetof(CPULoongArchState, ++ fpr[a->vj].vreg.H((a->imm))), ++ 16, ctx->vl/8); ++ return true; ++} ++static bool trans_vreplvei_w(DisasContext *ctx, arg_vv_i *a) ++{ ++ if (!avail_LSX(ctx)) { ++ return false; ++ } ++ ++ if (!check_vec(ctx, 16)) { ++ return true; ++ } ++ ++ tcg_gen_gvec_dup_mem(MO_32, vec_full_offset(a->vd), ++ offsetof(CPULoongArchState, ++ fpr[a->vj].vreg.W((a->imm))), ++ 16, ctx->vl/8); ++ return true; ++} ++static bool trans_vreplvei_d(DisasContext *ctx, arg_vv_i *a) ++{ ++ if (!avail_LSX(ctx)) { ++ return false; ++ } ++ ++ if (!check_vec(ctx, 16)) { ++ return true; ++ } ++ ++ tcg_gen_gvec_dup_mem(MO_64, vec_full_offset(a->vd), ++ offsetof(CPULoongArchState, ++ fpr[a->vj].vreg.D((a->imm))), ++ 16, ctx->vl/8); ++ return true; ++} ++ ++static bool gen_vreplve_vl(DisasContext *ctx, arg_vvr *a, ++ uint32_t oprsz, int vece, int bit, ++ void (*func)(TCGv_i64, TCGv_ptr, tcg_target_long)) ++{ ++ int i; ++ TCGv_i64 t0 = tcg_temp_new_i64(); ++ TCGv_ptr t1 = tcg_temp_new_ptr(); ++ TCGv_i64 t2 = tcg_temp_new_i64(); ++ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ tcg_gen_andi_i64(t0, gpr_src(ctx, a->rk, EXT_NONE), (LSX_LEN / bit) - 1); ++ tcg_gen_shli_i64(t0, t0, vece); ++ if (HOST_BIG_ENDIAN) { ++ tcg_gen_xori_i64(t0, t0, vece << ((LSX_LEN / bit) - 1)); ++ } ++ ++ tcg_gen_trunc_i64_ptr(t1, t0); ++ tcg_gen_add_ptr(t1, t1, tcg_env); ++ ++ for (i = 0; i < oprsz; i += 16) { ++ func(t2, t1, vec_full_offset(a->vj) + i); ++ tcg_gen_gvec_dup_i64(vece, vec_full_offset(a->vd) + i, 16, 16, t2); ++ } ++ ++ return true; ++} ++ ++static bool gen_vreplve(DisasContext *ctx, arg_vvr *a, int vece, int bit, ++ void (*func)(TCGv_i64, TCGv_ptr, tcg_target_long)) ++{ ++ return gen_vreplve_vl(ctx, a, 16, vece, bit, func); ++} ++ ++static bool gen_xvreplve(DisasContext *ctx, arg_vvr *a, int vece, int bit, ++ void (*func)(TCGv_i64, TCGv_ptr, tcg_target_long)) ++{ ++ return gen_vreplve_vl(ctx, a, 32, vece, bit, func); ++} ++ ++TRANS(vreplve_b, LSX, gen_vreplve, MO_8, 8, tcg_gen_ld8u_i64) ++TRANS(vreplve_h, LSX, gen_vreplve, MO_16, 16, tcg_gen_ld16u_i64) ++TRANS(vreplve_w, LSX, gen_vreplve, MO_32, 32, tcg_gen_ld32u_i64) ++TRANS(vreplve_d, LSX, gen_vreplve, MO_64, 64, tcg_gen_ld_i64) ++TRANS(xvreplve_b, LASX, gen_xvreplve, MO_8, 8, tcg_gen_ld8u_i64) ++TRANS(xvreplve_h, LASX, gen_xvreplve, MO_16, 16, tcg_gen_ld16u_i64) ++TRANS(xvreplve_w, LASX, gen_xvreplve, MO_32, 32, tcg_gen_ld32u_i64) ++TRANS(xvreplve_d, LASX, gen_xvreplve, MO_64, 64, tcg_gen_ld_i64) ++ ++static bool gen_xvrepl128(DisasContext *ctx, arg_vv_i *a, MemOp mop) ++{ ++ int i; ++ ++ if (!check_vec(ctx, 32)) { ++ return true; ++ } ++ ++ for (i = 0; i < 32; i += 16) { ++ tcg_gen_gvec_dup_mem(mop, vec_full_offset(a->vd) + i, ++ vec_reg_offset(a->vj, a->imm, mop) + i, 16, 16); ++ ++ } ++ return true; ++} ++ ++TRANS(xvrepl128vei_b, LASX, gen_xvrepl128, MO_8) ++TRANS(xvrepl128vei_h, LASX, gen_xvrepl128, MO_16) ++TRANS(xvrepl128vei_w, LASX, gen_xvrepl128, MO_32) ++TRANS(xvrepl128vei_d, LASX, gen_xvrepl128, MO_64) ++ ++static bool gen_xvreplve0(DisasContext *ctx, arg_vv *a, MemOp mop) ++{ ++ if (!check_vec(ctx, 32)) { ++ return true; ++ } ++ ++ tcg_gen_gvec_dup_mem(mop, vec_full_offset(a->vd), ++ vec_full_offset(a->vj), 32, 32); ++ return true; ++} ++ ++TRANS(xvreplve0_b, LASX, gen_xvreplve0, MO_8) ++TRANS(xvreplve0_h, LASX, gen_xvreplve0, MO_16) ++TRANS(xvreplve0_w, LASX, gen_xvreplve0, MO_32) ++TRANS(xvreplve0_d, LASX, gen_xvreplve0, MO_64) ++TRANS(xvreplve0_q, LASX, gen_xvreplve0, MO_128) ++ ++TRANS(xvinsve0_w, LASX, gen_xx_i, gen_helper_xvinsve0_w) ++TRANS(xvinsve0_d, LASX, gen_xx_i, gen_helper_xvinsve0_d) ++ ++TRANS(xvpickve_w, LASX, gen_xx_i, gen_helper_xvpickve_w) ++TRANS(xvpickve_d, LASX, gen_xx_i, gen_helper_xvpickve_d) ++ ++static bool do_vbsll_v(DisasContext *ctx, arg_vv_i *a, uint32_t oprsz) ++{ ++ int i, ofs; ++ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ TCGv desthigh = tcg_temp_new_i64(); ++ TCGv destlow = tcg_temp_new_i64(); ++ TCGv high = tcg_temp_new_i64(); ++ TCGv low = tcg_temp_new_i64(); ++ ++ get_vreg64(low, a->vj, 2 * i); ++ ++ ofs = ((a->imm) & 0xf) * 8; ++ if (ofs < 64) { ++ get_vreg64(high, a->vj, 2 * i + 1); ++ tcg_gen_extract2_i64(desthigh, low, high, 64 - ofs); ++ tcg_gen_shli_i64(destlow, low, ofs); ++ } else { ++ tcg_gen_shli_i64(desthigh, low, ofs - 64); ++ destlow = tcg_constant_i64(0); ++ } ++ set_vreg64(desthigh, a->vd, 2 * i + 1); ++ set_vreg64(destlow, a->vd, 2 * i); ++ } ++ ++ return true; ++} ++ ++static bool do_vbsrl_v(DisasContext *ctx, arg_vv_i *a, uint32_t oprsz) ++{ ++ int i, ofs; ++ ++ if (!check_vec(ctx, 32)) { ++ return true; ++ } ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ TCGv desthigh = tcg_temp_new_i64(); ++ TCGv destlow = tcg_temp_new_i64(); ++ TCGv high = tcg_temp_new_i64(); ++ TCGv low = tcg_temp_new_i64(); ++ get_vreg64(high, a->vj, 2 * i + 1); ++ ++ ofs = ((a->imm) & 0xf) * 8; ++ if (ofs < 64) { ++ get_vreg64(low, a->vj, 2 * i); ++ tcg_gen_extract2_i64(destlow, low, high, ofs); ++ tcg_gen_shri_i64(desthigh, high, ofs); ++ } else { ++ tcg_gen_shri_i64(destlow, high, ofs - 64); ++ desthigh = tcg_constant_i64(0); ++ } ++ set_vreg64(desthigh, a->vd, 2 * i + 1); ++ set_vreg64(destlow, a->vd, 2 * i); ++ } ++ ++ return true; ++} ++ ++TRANS(vbsll_v, LSX, do_vbsll_v, 16) ++TRANS(vbsrl_v, LSX, do_vbsrl_v, 16) ++TRANS(xvbsll_v, LASX, do_vbsll_v, 32) ++TRANS(xvbsrl_v, LASX, do_vbsrl_v, 32) ++ ++TRANS(vpackev_b, LSX, gen_vvv, gen_helper_vpackev_b) ++TRANS(vpackev_h, LSX, gen_vvv, gen_helper_vpackev_h) ++TRANS(vpackev_w, LSX, gen_vvv, gen_helper_vpackev_w) ++TRANS(vpackev_d, LSX, gen_vvv, gen_helper_vpackev_d) ++TRANS(vpackod_b, LSX, gen_vvv, gen_helper_vpackod_b) ++TRANS(vpackod_h, LSX, gen_vvv, gen_helper_vpackod_h) ++TRANS(vpackod_w, LSX, gen_vvv, gen_helper_vpackod_w) ++TRANS(vpackod_d, LSX, gen_vvv, gen_helper_vpackod_d) ++TRANS(xvpackev_b, LASX, gen_xxx, gen_helper_vpackev_b) ++TRANS(xvpackev_h, LASX, gen_xxx, gen_helper_vpackev_h) ++TRANS(xvpackev_w, LASX, gen_xxx, gen_helper_vpackev_w) ++TRANS(xvpackev_d, LASX, gen_xxx, gen_helper_vpackev_d) ++TRANS(xvpackod_b, LASX, gen_xxx, gen_helper_vpackod_b) ++TRANS(xvpackod_h, LASX, gen_xxx, gen_helper_vpackod_h) ++TRANS(xvpackod_w, LASX, gen_xxx, gen_helper_vpackod_w) ++TRANS(xvpackod_d, LASX, gen_xxx, gen_helper_vpackod_d) ++ ++TRANS(vpickev_b, LSX, gen_vvv, gen_helper_vpickev_b) ++TRANS(vpickev_h, LSX, gen_vvv, gen_helper_vpickev_h) ++TRANS(vpickev_w, LSX, gen_vvv, gen_helper_vpickev_w) ++TRANS(vpickev_d, LSX, gen_vvv, gen_helper_vpickev_d) ++TRANS(vpickod_b, LSX, gen_vvv, gen_helper_vpickod_b) ++TRANS(vpickod_h, LSX, gen_vvv, gen_helper_vpickod_h) ++TRANS(vpickod_w, LSX, gen_vvv, gen_helper_vpickod_w) ++TRANS(vpickod_d, LSX, gen_vvv, gen_helper_vpickod_d) ++TRANS(xvpickev_b, LASX, gen_xxx, gen_helper_vpickev_b) ++TRANS(xvpickev_h, LASX, gen_xxx, gen_helper_vpickev_h) ++TRANS(xvpickev_w, LASX, gen_xxx, gen_helper_vpickev_w) ++TRANS(xvpickev_d, LASX, gen_xxx, gen_helper_vpickev_d) ++TRANS(xvpickod_b, LASX, gen_xxx, gen_helper_vpickod_b) ++TRANS(xvpickod_h, LASX, gen_xxx, gen_helper_vpickod_h) ++TRANS(xvpickod_w, LASX, gen_xxx, gen_helper_vpickod_w) ++TRANS(xvpickod_d, LASX, gen_xxx, gen_helper_vpickod_d) ++ ++TRANS(vilvl_b, LSX, gen_vvv, gen_helper_vilvl_b) ++TRANS(vilvl_h, LSX, gen_vvv, gen_helper_vilvl_h) ++TRANS(vilvl_w, LSX, gen_vvv, gen_helper_vilvl_w) ++TRANS(vilvl_d, LSX, gen_vvv, gen_helper_vilvl_d) ++TRANS(vilvh_b, LSX, gen_vvv, gen_helper_vilvh_b) ++TRANS(vilvh_h, LSX, gen_vvv, gen_helper_vilvh_h) ++TRANS(vilvh_w, LSX, gen_vvv, gen_helper_vilvh_w) ++TRANS(vilvh_d, LSX, gen_vvv, gen_helper_vilvh_d) ++TRANS(xvilvl_b, LASX, gen_xxx, gen_helper_vilvl_b) ++TRANS(xvilvl_h, LASX, gen_xxx, gen_helper_vilvl_h) ++TRANS(xvilvl_w, LASX, gen_xxx, gen_helper_vilvl_w) ++TRANS(xvilvl_d, LASX, gen_xxx, gen_helper_vilvl_d) ++TRANS(xvilvh_b, LASX, gen_xxx, gen_helper_vilvh_b) ++TRANS(xvilvh_h, LASX, gen_xxx, gen_helper_vilvh_h) ++TRANS(xvilvh_w, LASX, gen_xxx, gen_helper_vilvh_w) ++TRANS(xvilvh_d, LASX, gen_xxx, gen_helper_vilvh_d) ++ ++TRANS(vshuf_b, LSX, gen_vvvv, gen_helper_vshuf_b) ++TRANS(vshuf_h, LSX, gen_vvv, gen_helper_vshuf_h) ++TRANS(vshuf_w, LSX, gen_vvv, gen_helper_vshuf_w) ++TRANS(vshuf_d, LSX, gen_vvv, gen_helper_vshuf_d) ++TRANS(xvshuf_b, LASX, gen_xxxx, gen_helper_vshuf_b) ++TRANS(xvshuf_h, LASX, gen_xxx, gen_helper_vshuf_h) ++TRANS(xvshuf_w, LASX, gen_xxx, gen_helper_vshuf_w) ++TRANS(xvshuf_d, LASX, gen_xxx, gen_helper_vshuf_d) ++TRANS(vshuf4i_b, LSX, gen_vv_i, gen_helper_vshuf4i_b) ++TRANS(vshuf4i_h, LSX, gen_vv_i, gen_helper_vshuf4i_h) ++TRANS(vshuf4i_w, LSX, gen_vv_i, gen_helper_vshuf4i_w) ++TRANS(vshuf4i_d, LSX, gen_vv_i, gen_helper_vshuf4i_d) ++TRANS(xvshuf4i_b, LASX, gen_xx_i, gen_helper_vshuf4i_b) ++TRANS(xvshuf4i_h, LASX, gen_xx_i, gen_helper_vshuf4i_h) ++TRANS(xvshuf4i_w, LASX, gen_xx_i, gen_helper_vshuf4i_w) ++TRANS(xvshuf4i_d, LASX, gen_xx_i, gen_helper_vshuf4i_d) ++ ++TRANS(xvperm_w, LASX, gen_xxx, gen_helper_vperm_w) ++TRANS(vpermi_w, LSX, gen_vv_i, gen_helper_vpermi_w) ++TRANS(xvpermi_w, LASX, gen_xx_i, gen_helper_vpermi_w) ++TRANS(xvpermi_d, LASX, gen_xx_i, gen_helper_vpermi_d) ++TRANS(xvpermi_q, LASX, gen_xx_i, gen_helper_vpermi_q) ++ ++TRANS(vextrins_b, LSX, gen_vv_i, gen_helper_vextrins_b) ++TRANS(vextrins_h, LSX, gen_vv_i, gen_helper_vextrins_h) ++TRANS(vextrins_w, LSX, gen_vv_i, gen_helper_vextrins_w) ++TRANS(vextrins_d, LSX, gen_vv_i, gen_helper_vextrins_d) ++TRANS(xvextrins_b, LASX, gen_xx_i, gen_helper_vextrins_b) ++TRANS(xvextrins_h, LASX, gen_xx_i, gen_helper_vextrins_h) ++TRANS(xvextrins_w, LASX, gen_xx_i, gen_helper_vextrins_w) ++TRANS(xvextrins_d, LASX, gen_xx_i, gen_helper_vextrins_d) ++ ++static bool trans_vld(DisasContext *ctx, arg_vr_i *a) ++{ ++ TCGv addr; ++ TCGv_i64 rl, rh; ++ TCGv_i128 val; ++ ++ if (!avail_LSX(ctx)) { ++ return false; ++ } ++ ++ if (!check_vec(ctx, 16)) { ++ return true; ++ } ++ ++ addr = gpr_src(ctx, a->rj, EXT_NONE); ++ val = tcg_temp_new_i128(); ++ rl = tcg_temp_new_i64(); ++ rh = tcg_temp_new_i64(); ++ ++ addr = make_address_i(ctx, addr, a->imm); ++ ++ tcg_gen_qemu_ld_i128(val, addr, ctx->mem_idx, MO_128 | MO_TE); ++ tcg_gen_extr_i128_i64(rl, rh, val); ++ set_vreg64(rh, a->vd, 1); ++ set_vreg64(rl, a->vd, 0); ++ ++ return true; ++} ++ ++static bool trans_vst(DisasContext *ctx, arg_vr_i *a) ++{ ++ TCGv addr; ++ TCGv_i128 val; ++ TCGv_i64 ah, al; ++ ++ if (!avail_LSX(ctx)) { ++ return false; ++ } ++ ++ if (!check_vec(ctx, 16)) { ++ return true; ++ } ++ ++ addr = gpr_src(ctx, a->rj, EXT_NONE); ++ val = tcg_temp_new_i128(); ++ ah = tcg_temp_new_i64(); ++ al = tcg_temp_new_i64(); ++ ++ addr = make_address_i(ctx, addr, a->imm); ++ ++ get_vreg64(ah, a->vd, 1); ++ get_vreg64(al, a->vd, 0); ++ tcg_gen_concat_i64_i128(val, al, ah); ++ tcg_gen_qemu_st_i128(val, addr, ctx->mem_idx, MO_128 | MO_TE); ++ ++ return true; ++} ++ ++static bool trans_vldx(DisasContext *ctx, arg_vrr *a) ++{ ++ TCGv addr, src1, src2; ++ TCGv_i64 rl, rh; ++ TCGv_i128 val; ++ ++ if (!avail_LSX(ctx)) { ++ return false; ++ } ++ ++ if (!check_vec(ctx, 16)) { ++ return true; ++ } ++ ++ src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ src2 = gpr_src(ctx, a->rk, EXT_NONE); ++ val = tcg_temp_new_i128(); ++ rl = tcg_temp_new_i64(); ++ rh = tcg_temp_new_i64(); ++ ++ addr = make_address_x(ctx, src1, src2); ++ tcg_gen_qemu_ld_i128(val, addr, ctx->mem_idx, MO_128 | MO_TE); ++ tcg_gen_extr_i128_i64(rl, rh, val); ++ set_vreg64(rh, a->vd, 1); ++ set_vreg64(rl, a->vd, 0); ++ ++ return true; ++} ++ ++static bool trans_vstx(DisasContext *ctx, arg_vrr *a) ++{ ++ TCGv addr, src1, src2; ++ TCGv_i64 ah, al; ++ TCGv_i128 val; ++ ++ if (!avail_LSX(ctx)) { ++ return false; ++ } ++ ++ if (!check_vec(ctx, 16)) { ++ return true; ++ } ++ ++ src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ src2 = gpr_src(ctx, a->rk, EXT_NONE); ++ val = tcg_temp_new_i128(); ++ ah = tcg_temp_new_i64(); ++ al = tcg_temp_new_i64(); ++ ++ addr = make_address_x(ctx, src1, src2); ++ get_vreg64(ah, a->vd, 1); ++ get_vreg64(al, a->vd, 0); ++ tcg_gen_concat_i64_i128(val, al, ah); ++ tcg_gen_qemu_st_i128(val, addr, ctx->mem_idx, MO_128 | MO_TE); ++ ++ return true; ++} ++ ++static bool do_vldrepl_vl(DisasContext *ctx, arg_vr_i *a, ++ uint32_t oprsz, MemOp mop) ++{ ++ TCGv addr; ++ TCGv_i64 val; ++ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ addr = gpr_src(ctx, a->rj, EXT_NONE); ++ val = tcg_temp_new_i64(); ++ ++ addr = make_address_i(ctx, addr, a->imm); ++ ++ tcg_gen_qemu_ld_i64(val, addr, ctx->mem_idx, mop); ++ tcg_gen_gvec_dup_i64(mop, vec_full_offset(a->vd), oprsz, ctx->vl / 8, val); ++ ++ return true; ++} ++ ++static bool do_vldrepl(DisasContext *ctx, arg_vr_i *a, MemOp mop) ++{ ++ return do_vldrepl_vl(ctx, a, 16, mop); ++} ++ ++static bool do_xvldrepl(DisasContext *ctx, arg_vr_i *a, MemOp mop) ++{ ++ return do_vldrepl_vl(ctx, a, 32, mop); ++} ++ ++TRANS(vldrepl_b, LSX, do_vldrepl, MO_8) ++TRANS(vldrepl_h, LSX, do_vldrepl, MO_16) ++TRANS(vldrepl_w, LSX, do_vldrepl, MO_32) ++TRANS(vldrepl_d, LSX, do_vldrepl, MO_64) ++TRANS(xvldrepl_b, LASX, do_xvldrepl, MO_8) ++TRANS(xvldrepl_h, LASX, do_xvldrepl, MO_16) ++TRANS(xvldrepl_w, LASX, do_xvldrepl, MO_32) ++TRANS(xvldrepl_d, LASX, do_xvldrepl, MO_64) ++ ++static bool do_vstelm_vl(DisasContext *ctx, ++ arg_vr_ii *a, uint32_t oprsz, MemOp mop) ++{ ++ TCGv addr; ++ TCGv_i64 val; ++ ++ if (!check_vec(ctx, oprsz)) { ++ return true; ++ } ++ ++ addr = gpr_src(ctx, a->rj, EXT_NONE); ++ val = tcg_temp_new_i64(); ++ ++ addr = make_address_i(ctx, addr, a->imm); ++ tcg_gen_ld_i64(val, tcg_env, vec_reg_offset(a->vd, a->imm2, mop)); ++ tcg_gen_qemu_st_i64(val, addr, ctx->mem_idx, mop); ++ return true; ++} ++ ++static bool do_vstelm(DisasContext *ctx, arg_vr_ii *a, MemOp mop) ++{ ++ return do_vstelm_vl(ctx, a, 16, mop); ++} ++ ++static bool do_xvstelm(DisasContext *ctx, arg_vr_ii *a, MemOp mop) ++{ ++ return do_vstelm_vl(ctx, a, 32, mop); ++} ++ ++TRANS(vstelm_b, LSX, do_vstelm, MO_8) ++TRANS(vstelm_h, LSX, do_vstelm, MO_16) ++TRANS(vstelm_w, LSX, do_vstelm, MO_32) ++TRANS(vstelm_d, LSX, do_vstelm, MO_64) ++TRANS(xvstelm_b, LASX, do_xvstelm, MO_8) ++TRANS(xvstelm_h, LASX, do_xvstelm, MO_16) ++TRANS(xvstelm_w, LASX, do_xvstelm, MO_32) ++TRANS(xvstelm_d, LASX, do_xvstelm, MO_64) ++ ++static bool gen_lasx_memory(DisasContext *ctx, arg_vr_i *a, ++ void (*func)(DisasContext *, int, TCGv)) ++{ ++ TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv temp = NULL; ++ ++ if (!check_vec(ctx, 32)) { ++ return true; ++ } ++ ++ if (a->imm) { ++ temp = tcg_temp_new(); ++ tcg_gen_addi_tl(temp, addr, a->imm); ++ addr = temp; ++ } ++ ++ func(ctx, a->vd, addr); ++ return true; ++} ++ ++static void gen_xvld(DisasContext *ctx, int vreg, TCGv addr) ++{ ++ int i; ++ TCGv temp = tcg_temp_new(); ++ TCGv dest = tcg_temp_new(); ++ ++ tcg_gen_qemu_ld_i64(dest, addr, ctx->mem_idx, MO_TEUQ); ++ set_vreg64(dest, vreg, 0); ++ ++ for (i = 1; i < 4; i++) { ++ tcg_gen_addi_tl(temp, addr, 8 * i); ++ tcg_gen_qemu_ld_i64(dest, temp, ctx->mem_idx, MO_TEUQ); ++ set_vreg64(dest, vreg, i); ++ } ++} ++ ++static void gen_xvst(DisasContext * ctx, int vreg, TCGv addr) ++{ ++ int i; ++ TCGv temp = tcg_temp_new(); ++ TCGv dest = tcg_temp_new(); ++ ++ get_vreg64(dest, vreg, 0); ++ tcg_gen_qemu_st_i64(dest, addr, ctx->mem_idx, MO_TEUQ); ++ ++ for (i = 1; i < 4; i++) { ++ tcg_gen_addi_tl(temp, addr, 8 * i); ++ get_vreg64(dest, vreg, i); ++ tcg_gen_qemu_st_i64(dest, temp, ctx->mem_idx, MO_TEUQ); ++ } ++} ++ ++TRANS(xvld, LASX, gen_lasx_memory, gen_xvld) ++TRANS(xvst, LASX, gen_lasx_memory, gen_xvst) ++ ++static bool gen_lasx_memoryx(DisasContext *ctx, arg_vrr *a, ++ void (*func)(DisasContext*, int, TCGv)) ++{ ++ TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); ++ TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); ++ TCGv addr = tcg_temp_new(); ++ ++ if (!check_vec(ctx, 32)) { ++ return true; ++ } ++ ++ tcg_gen_add_tl(addr, src1, src2); ++ func(ctx, a->vd, addr); ++ ++ return true; ++} ++ ++TRANS(xvldx, LASX, gen_lasx_memoryx, gen_xvld) ++TRANS(xvstx, LASX, gen_lasx_memoryx, gen_xvst) +diff --git a/target/loongarch/tcg/iocsr_helper.c b/target/loongarch/tcg/iocsr_helper.c +new file mode 100644 +index 0000000..6cd01d5 +--- /dev/null ++++ b/target/loongarch/tcg/iocsr_helper.c +@@ -0,0 +1,68 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (c) 2021 Loongson Technology Corporation Limited ++ * ++ * Helpers for IOCSR reads/writes ++ */ ++ ++#include "qemu/osdep.h" ++#include "cpu.h" ++#include "qemu/host-utils.h" ++#include "exec/helper-proto.h" ++#include "exec/exec-all.h" ++#include "exec/cpu_ldst.h" ++ ++#define GET_MEMTXATTRS(cas) \ ++ ((MemTxAttrs){.requester_id = env_cpu(cas)->cpu_index}) ++ ++uint64_t helper_iocsrrd_b(CPULoongArchState *env, target_ulong r_addr) ++{ ++ return address_space_ldub(&env->address_space_iocsr, r_addr, ++ GET_MEMTXATTRS(env), NULL); ++} ++ ++uint64_t helper_iocsrrd_h(CPULoongArchState *env, target_ulong r_addr) ++{ ++ return address_space_lduw(&env->address_space_iocsr, r_addr, ++ GET_MEMTXATTRS(env), NULL); ++} ++ ++uint64_t helper_iocsrrd_w(CPULoongArchState *env, target_ulong r_addr) ++{ ++ return address_space_ldl(&env->address_space_iocsr, r_addr, ++ GET_MEMTXATTRS(env), NULL); ++} ++ ++uint64_t helper_iocsrrd_d(CPULoongArchState *env, target_ulong r_addr) ++{ ++ return address_space_ldq(&env->address_space_iocsr, r_addr, ++ GET_MEMTXATTRS(env), NULL); ++} ++ ++void helper_iocsrwr_b(CPULoongArchState *env, target_ulong w_addr, ++ target_ulong val) ++{ ++ address_space_stb(&env->address_space_iocsr, w_addr, ++ val, GET_MEMTXATTRS(env), NULL); ++} ++ ++void helper_iocsrwr_h(CPULoongArchState *env, target_ulong w_addr, ++ target_ulong val) ++{ ++ address_space_stw(&env->address_space_iocsr, w_addr, ++ val, GET_MEMTXATTRS(env), NULL); ++} ++ ++void helper_iocsrwr_w(CPULoongArchState *env, target_ulong w_addr, ++ target_ulong val) ++{ ++ address_space_stl(&env->address_space_iocsr, w_addr, ++ val, GET_MEMTXATTRS(env), NULL); ++} ++ ++void helper_iocsrwr_d(CPULoongArchState *env, target_ulong w_addr, ++ target_ulong val) ++{ ++ address_space_stq(&env->address_space_iocsr, w_addr, ++ val, GET_MEMTXATTRS(env), NULL); ++} +diff --git a/target/loongarch/tcg/meson.build b/target/loongarch/tcg/meson.build +new file mode 100644 +index 0000000..1a3cd58 +--- /dev/null ++++ b/target/loongarch/tcg/meson.build +@@ -0,0 +1,19 @@ ++if 'CONFIG_TCG' not in config_all ++ subdir_done() ++endif ++ ++loongarch_ss.add([zlib, gen]) ++ ++loongarch_ss.add(files( ++ 'fpu_helper.c', ++ 'op_helper.c', ++ 'translate.c', ++ 'vec_helper.c', ++)) ++ ++loongarch_system_ss.add(files( ++ 'constant_timer.c', ++ 'csr_helper.c', ++ 'iocsr_helper.c', ++ 'tlb_helper.c', ++)) +diff --git a/target/loongarch/tcg/op_helper.c b/target/loongarch/tcg/op_helper.c +new file mode 100644 +index 0000000..fe79c62 +--- /dev/null ++++ b/target/loongarch/tcg/op_helper.c +@@ -0,0 +1,140 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * LoongArch emulation helpers for QEMU. ++ * ++ * Copyright (c) 2021 Loongson Technology Corporation Limited ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/log.h" ++#include "cpu.h" ++#include "qemu/host-utils.h" ++#include "exec/helper-proto.h" ++#include "exec/exec-all.h" ++#include "exec/cpu_ldst.h" ++#include "internals.h" ++#include "qemu/crc32c.h" ++#include ++#include "cpu-csr.h" ++ ++/* Exceptions helpers */ ++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) ++{ ++ if (rj > rk) { ++ env->CSR_BADV = rj; ++ do_raise_exception(env, EXCCODE_BCE, GETPC()); ++ } ++} ++ ++void helper_asrtgt_d(CPULoongArchState *env, target_ulong rj, target_ulong rk) ++{ ++ if (rj <= rk) { ++ env->CSR_BADV = rj; ++ do_raise_exception(env, EXCCODE_BCE, GETPC()); ++ } ++} ++ ++target_ulong helper_crc32(target_ulong val, target_ulong m, uint64_t sz) ++{ ++ uint8_t buf[8]; ++ target_ulong mask = ((sz * 8) == 64) ? -1ULL : ((1ULL << (sz * 8)) - 1); ++ ++ m &= mask; ++ stq_le_p(buf, m); ++ return (int32_t) (crc32(val ^ 0xffffffff, buf, sz) ^ 0xffffffff); ++} ++ ++target_ulong helper_crc32c(target_ulong val, target_ulong m, uint64_t sz) ++{ ++ uint8_t buf[8]; ++ target_ulong mask = ((sz * 8) == 64) ? -1ULL : ((1ULL << (sz * 8)) - 1); ++ m &= mask; ++ stq_le_p(buf, m); ++ return (int32_t) (crc32c(val, buf, sz) ^ 0xffffffff); ++} ++ ++target_ulong helper_cpucfg(CPULoongArchState *env, target_ulong rj) ++{ ++ return rj >= ARRAY_SIZE(env->cpucfg) ? 0 : env->cpucfg[rj]; ++} ++ ++uint64_t helper_rdtime_d(CPULoongArchState *env) ++{ ++#ifdef CONFIG_USER_ONLY ++ return cpu_get_host_ticks(); ++#else ++ uint64_t plv; ++ LoongArchCPU *cpu = env_archcpu(env); ++ ++ plv = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV); ++ if (extract64(env->CSR_MISC, R_CSR_MISC_DRDTL_SHIFT + plv, 1)) { ++ do_raise_exception(env, EXCCODE_IPE, GETPC()); ++ } ++ ++ return cpu_loongarch_get_constant_timer_counter(cpu); ++#endif ++} ++ ++#ifndef CONFIG_USER_ONLY ++void helper_ertn(CPULoongArchState *env) ++{ ++ uint64_t csr_pplv, csr_pie; ++ if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) { ++ csr_pplv = FIELD_EX64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PPLV); ++ csr_pie = FIELD_EX64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PIE); ++ ++ env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 0); ++ env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DA, 0); ++ env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PG, 1); ++ set_pc(env, env->CSR_TLBRERA); ++ qemu_log_mask(CPU_LOG_INT, "%s: TLBRERA " TARGET_FMT_lx "\n", ++ __func__, env->CSR_TLBRERA); ++ } else { ++ csr_pplv = FIELD_EX64(env->CSR_PRMD, CSR_PRMD, PPLV); ++ csr_pie = FIELD_EX64(env->CSR_PRMD, CSR_PRMD, PIE); ++ ++ set_pc(env, env->CSR_ERA); ++ qemu_log_mask(CPU_LOG_INT, "%s: ERA " TARGET_FMT_lx "\n", ++ __func__, env->CSR_ERA); ++ } ++ env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, csr_pplv); ++ env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, csr_pie); ++ ++ env->lladdr = 1; ++} ++ ++void helper_idle(CPULoongArchState *env) ++{ ++ CPUState *cs = env_cpu(env); ++ ++ cs->halted = 1; ++ do_raise_exception(env, EXCP_HLT, 0); ++} ++#endif +diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c +new file mode 100644 +index 0000000..449043c +--- /dev/null ++++ b/target/loongarch/tcg/tlb_helper.c +@@ -0,0 +1,803 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * QEMU LoongArch TLB helpers ++ * ++ * Copyright (c) 2021 Loongson Technology Corporation Limited ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/guest-random.h" ++ ++#include "cpu.h" ++#include "internals.h" ++#include "exec/helper-proto.h" ++#include "exec/exec-all.h" ++#include "exec/cpu_ldst.h" ++#include "exec/log.h" ++#include "cpu-csr.h" ++ ++enum { ++ TLBRET_MATCH = 0, ++ TLBRET_BADADDR = 1, ++ TLBRET_NOMATCH = 2, ++ TLBRET_INVALID = 3, ++ TLBRET_DIRTY = 4, ++ TLBRET_RI = 5, ++ TLBRET_XI = 6, ++ TLBRET_PE = 7, ++}; ++ ++static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, ++ int *prot, target_ulong address, ++ int access_type, int index, int mmu_idx) ++{ ++ LoongArchTLB *tlb = &env->tlb[index]; ++ uint64_t plv = mmu_idx; ++ uint64_t tlb_entry, tlb_ppn; ++ uint8_t tlb_ps, n, tlb_v, tlb_d, tlb_plv, tlb_nx, tlb_nr, tlb_rplv; ++ ++ if (index >= LOONGARCH_STLB) { ++ tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); ++ } else { ++ tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); ++ } ++ n = (address >> tlb_ps) & 0x1;/* Odd or even */ ++ ++ tlb_entry = n ? tlb->tlb_entry1 : tlb->tlb_entry0; ++ tlb_v = FIELD_EX64(tlb_entry, TLBENTRY, V); ++ tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D); ++ tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV); ++ if (is_la64(env)) { ++ tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN); ++ tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY_64, NX); ++ tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY_64, NR); ++ tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY_64, RPLV); ++ } else { ++ tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_32, PPN); ++ tlb_nx = 0; ++ tlb_nr = 0; ++ tlb_rplv = 0; ++ } ++ ++ /* Remove sw bit between bit12 -- bit PS*/ ++ tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) -1)); ++ ++ /* Check access rights */ ++ if (!tlb_v) { ++ return TLBRET_INVALID; ++ } ++ ++ if (access_type == MMU_INST_FETCH && tlb_nx) { ++ return TLBRET_XI; ++ } ++ ++ if (access_type == MMU_DATA_LOAD && tlb_nr) { ++ return TLBRET_RI; ++ } ++ ++ if (((tlb_rplv == 0) && (plv > tlb_plv)) || ++ ((tlb_rplv == 1) && (plv != tlb_plv))) { ++ return TLBRET_PE; ++ } ++ ++ if ((access_type == MMU_DATA_STORE) && !tlb_d) { ++ return TLBRET_DIRTY; ++ } ++ ++ *physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) | ++ (address & MAKE_64BIT_MASK(0, tlb_ps)); ++ *prot = PAGE_READ; ++ if (tlb_d) { ++ *prot |= PAGE_WRITE; ++ } ++ if (!tlb_nx) { ++ *prot |= PAGE_EXEC; ++ } ++ return TLBRET_MATCH; ++} ++ ++/* ++ * One tlb entry holds an adjacent odd/even pair, the vpn is the ++ * content of the virtual page number divided by 2. So the ++ * compare vpn is bit[47:15] for 16KiB page. while the vppn ++ * field in tlb entry contains bit[47:13], so need adjust. ++ * virt_vpn = vaddr[47:13] ++ */ ++static bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, ++ int *index) ++{ ++ LoongArchTLB *tlb; ++ uint16_t csr_asid, tlb_asid, stlb_idx; ++ uint8_t tlb_e, tlb_ps, tlb_g, stlb_ps; ++ int i, compare_shift; ++ uint64_t vpn, tlb_vppn; ++ ++ csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); ++ stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); ++ vpn = (vaddr & TARGET_VIRT_MASK) >> (stlb_ps + 1); ++ stlb_idx = vpn & 0xff; /* VA[25:15] <==> TLBIDX.index for 16KiB Page */ ++ compare_shift = stlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; ++ ++ /* Search STLB */ ++ for (i = 0; i < 8; ++i) { ++ tlb = &env->tlb[i * 256 + stlb_idx]; ++ tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); ++ if (tlb_e) { ++ tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); ++ tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); ++ tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); ++ ++ if ((tlb_g == 1 || tlb_asid == csr_asid) && ++ (vpn == (tlb_vppn >> compare_shift))) { ++ *index = i * 256 + stlb_idx; ++ return true; ++ } ++ } ++ } ++ ++ /* Search MTLB */ ++ for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; ++i) { ++ tlb = &env->tlb[i]; ++ tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); ++ if (tlb_e) { ++ tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); ++ tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); ++ tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); ++ tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); ++ compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; ++ vpn = (vaddr & TARGET_VIRT_MASK) >> (tlb_ps + 1); ++ if ((tlb_g == 1 || tlb_asid == csr_asid) && ++ (vpn == (tlb_vppn >> compare_shift))) { ++ *index = i; ++ return true; ++ } ++ } ++ } ++ return false; ++} ++ ++static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, ++ int *prot, target_ulong address, ++ MMUAccessType access_type, int mmu_idx) ++{ ++ int index, match; ++ ++ match = loongarch_tlb_search(env, address, &index); ++ if (match) { ++ return loongarch_map_tlb_entry(env, physical, prot, ++ address, access_type, index, mmu_idx); ++ } ++ ++ return TLBRET_NOMATCH; ++} ++ ++static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, ++ target_ulong dmw) ++{ ++ if (is_la64(env)) { ++ return va & TARGET_VIRT_MASK; ++ } else { ++ uint32_t pseg = FIELD_EX32(dmw, CSR_DMW_32, PSEG); ++ return (va & MAKE_64BIT_MASK(0, R_CSR_DMW_32_VSEG_SHIFT)) | \ ++ (pseg << R_CSR_DMW_32_VSEG_SHIFT); ++ } ++} ++ ++static int get_physical_address(CPULoongArchState *env, hwaddr *physical, ++ int *prot, target_ulong address, ++ MMUAccessType access_type, int mmu_idx) ++{ ++ int user_mode = mmu_idx == MMU_IDX_USER; ++ int kernel_mode = mmu_idx == MMU_IDX_KERNEL; ++ uint32_t plv, base_c, base_v; ++ int64_t addr_high; ++ uint8_t da = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, DA); ++ uint8_t pg = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG); ++ ++ /* Check PG and DA */ ++ if (da & !pg) { ++ *physical = address & TARGET_PHYS_MASK; ++ *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; ++ return TLBRET_MATCH; ++ } ++ ++ plv = kernel_mode | (user_mode << R_CSR_DMW_PLV3_SHIFT); ++ if (is_la64(env)) { ++ base_v = address >> R_CSR_DMW_64_VSEG_SHIFT; ++ } else { ++ base_v = address >> R_CSR_DMW_32_VSEG_SHIFT; ++ } ++ /* Check direct map window */ ++ for (int i = 0; i < 4; i++) { ++ if (is_la64(env)) { ++ base_c = FIELD_EX64(env->CSR_DMW[i], CSR_DMW_64, VSEG); ++ } else { ++ base_c = FIELD_EX64(env->CSR_DMW[i], CSR_DMW_32, VSEG); ++ } ++ if ((plv & env->CSR_DMW[i]) && (base_c == base_v)) { ++ *physical = dmw_va2pa(env, address, env->CSR_DMW[i]); ++ *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; ++ return TLBRET_MATCH; ++ } ++ } ++ ++ /* Check valid extension */ ++ addr_high = sextract64(address, TARGET_VIRT_ADDR_SPACE_BITS, 16); ++ if (!(addr_high == 0 || addr_high == -1)) { ++ return TLBRET_BADADDR; ++ } ++ ++ /* Mapped address */ ++ return loongarch_map_address(env, physical, prot, address, ++ access_type, mmu_idx); ++} ++ ++hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) ++{ ++ LoongArchCPU *cpu = LOONGARCH_CPU(cs); ++ CPULoongArchState *env = &cpu->env; ++ hwaddr phys_addr; ++ int prot; ++ ++ if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD, ++ cpu_mmu_index(env, false)) != 0) { ++ return -1; ++ } ++ return phys_addr; ++} ++ ++static void raise_mmu_exception(CPULoongArchState *env, target_ulong address, ++ MMUAccessType access_type, int tlb_error) ++{ ++ CPUState *cs = env_cpu(env); ++ ++ switch (tlb_error) { ++ default: ++ case TLBRET_BADADDR: ++ cs->exception_index = access_type == MMU_INST_FETCH ++ ? EXCCODE_ADEF : EXCCODE_ADEM; ++ break; ++ case TLBRET_NOMATCH: ++ /* No TLB match for a mapped address */ ++ if (access_type == MMU_DATA_LOAD) { ++ cs->exception_index = EXCCODE_PIL; ++ } else if (access_type == MMU_DATA_STORE) { ++ cs->exception_index = EXCCODE_PIS; ++ } else if (access_type == MMU_INST_FETCH) { ++ cs->exception_index = EXCCODE_PIF; ++ } ++ env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 1); ++ break; ++ case TLBRET_INVALID: ++ /* TLB match with no valid bit */ ++ if (access_type == MMU_DATA_LOAD) { ++ cs->exception_index = EXCCODE_PIL; ++ } else if (access_type == MMU_DATA_STORE) { ++ cs->exception_index = EXCCODE_PIS; ++ } else if (access_type == MMU_INST_FETCH) { ++ cs->exception_index = EXCCODE_PIF; ++ } ++ break; ++ case TLBRET_DIRTY: ++ /* TLB match but 'D' bit is cleared */ ++ cs->exception_index = EXCCODE_PME; ++ break; ++ case TLBRET_XI: ++ /* Execute-Inhibit Exception */ ++ cs->exception_index = EXCCODE_PNX; ++ break; ++ case TLBRET_RI: ++ /* Read-Inhibit Exception */ ++ cs->exception_index = EXCCODE_PNR; ++ break; ++ case TLBRET_PE: ++ /* Privileged Exception */ ++ cs->exception_index = EXCCODE_PPI; ++ break; ++ } ++ ++ if (tlb_error == TLBRET_NOMATCH) { ++ env->CSR_TLBRBADV = address; ++ if (is_la64(env)) { ++ env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI_64, ++ VPPN, extract64(address, 13, 35)); ++ } else { ++ env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI_32, ++ VPPN, extract64(address, 13, 19)); ++ } ++ } else { ++ if (!FIELD_EX64(env->CSR_DBG, CSR_DBG, DST)) { ++ env->CSR_BADV = address; ++ } ++ env->CSR_TLBEHI = address & (TARGET_PAGE_MASK << 1); ++ } ++} ++ ++static void invalidate_tlb_entry(CPULoongArchState *env, int index) ++{ ++ target_ulong addr, mask, pagesize; ++ uint8_t tlb_ps; ++ LoongArchTLB *tlb = &env->tlb[index]; ++ ++ int mmu_idx = cpu_mmu_index(env, false); ++ uint8_t tlb_v0 = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, V); ++ uint8_t tlb_v1 = FIELD_EX64(tlb->tlb_entry1, TLBENTRY, V); ++ uint64_t tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); ++ ++ if (index >= LOONGARCH_STLB) { ++ tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); ++ } else { ++ tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); ++ } ++ pagesize = MAKE_64BIT_MASK(tlb_ps, 1); ++ mask = MAKE_64BIT_MASK(0, tlb_ps + 1); ++ ++ if (tlb_v0) { ++ addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & ~mask; /* even */ ++ tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize, ++ mmu_idx, TARGET_LONG_BITS); ++ } ++ ++ if (tlb_v1) { ++ addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & pagesize; /* odd */ ++ tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize, ++ mmu_idx, TARGET_LONG_BITS); ++ } ++} ++ ++static void invalidate_tlb(CPULoongArchState *env, int index) ++{ ++ LoongArchTLB *tlb; ++ uint16_t csr_asid, tlb_asid, tlb_g; ++ ++ csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); ++ tlb = &env->tlb[index]; ++ tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); ++ tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); ++ if (tlb_g == 0 && tlb_asid != csr_asid) { ++ return; ++ } ++ invalidate_tlb_entry(env, index); ++} ++ ++static void fill_tlb_entry(CPULoongArchState *env, int index) ++{ ++ LoongArchTLB *tlb = &env->tlb[index]; ++ uint64_t lo0, lo1, csr_vppn; ++ uint16_t csr_asid; ++ uint8_t csr_ps; ++ ++ if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) { ++ csr_ps = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, PS); ++ if (is_la64(env)) { ++ csr_vppn = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI_64, VPPN); ++ } else { ++ csr_vppn = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI_32, VPPN); ++ } ++ lo0 = env->CSR_TLBRELO0; ++ lo1 = env->CSR_TLBRELO1; ++ } else { ++ csr_ps = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, PS); ++ if (is_la64(env)) { ++ csr_vppn = FIELD_EX64(env->CSR_TLBEHI, CSR_TLBEHI_64, VPPN); ++ } else { ++ csr_vppn = FIELD_EX64(env->CSR_TLBEHI, CSR_TLBEHI_32, VPPN); ++ } ++ lo0 = env->CSR_TLBELO0; ++ lo1 = env->CSR_TLBELO1; ++ } ++ ++ if (csr_ps == 0) { ++ qemu_log_mask(CPU_LOG_MMU, "page size is 0\n"); ++ } ++ ++ /* Only MTLB has the ps fields */ ++ if (index >= LOONGARCH_STLB) { ++ tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, PS, csr_ps); ++ } ++ ++ tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, VPPN, csr_vppn); ++ tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 1); ++ csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); ++ tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, ASID, csr_asid); ++ ++ tlb->tlb_entry0 = lo0; ++ tlb->tlb_entry1 = lo1; ++} ++ ++/* Return an random value between low and high */ ++static uint32_t get_random_tlb(uint32_t low, uint32_t high) ++{ ++ uint32_t val; ++ ++ qemu_guest_getrandom_nofail(&val, sizeof(val)); ++ return val % (high - low + 1) + low; ++} ++ ++void helper_tlbsrch(CPULoongArchState *env) ++{ ++ int index, match; ++ ++ if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) { ++ match = loongarch_tlb_search(env, env->CSR_TLBREHI, &index); ++ } else { ++ match = loongarch_tlb_search(env, env->CSR_TLBEHI, &index); ++ } ++ ++ if (match) { ++ env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX, index); ++ env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 0); ++ return; ++ } ++ ++ env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1); ++} ++ ++void helper_tlbrd(CPULoongArchState *env) ++{ ++ LoongArchTLB *tlb; ++ int index; ++ uint8_t tlb_ps, tlb_e; ++ ++ index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); ++ tlb = &env->tlb[index]; ++ ++ if (index >= LOONGARCH_STLB) { ++ tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); ++ } else { ++ tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); ++ } ++ tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); ++ ++ if (!tlb_e) { ++ /* Invalid TLB entry */ ++ env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1); ++ env->CSR_ASID = FIELD_DP64(env->CSR_ASID, CSR_ASID, ASID, 0); ++ env->CSR_TLBEHI = 0; ++ env->CSR_TLBELO0 = 0; ++ env->CSR_TLBELO1 = 0; ++ env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, PS, 0); ++ } else { ++ /* Valid TLB entry */ ++ env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 0); ++ env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, ++ PS, (tlb_ps & 0x3f)); ++ env->CSR_TLBEHI = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN) << ++ R_TLB_MISC_VPPN_SHIFT; ++ env->CSR_TLBELO0 = tlb->tlb_entry0; ++ env->CSR_TLBELO1 = tlb->tlb_entry1; ++ } ++} ++ ++void helper_tlbwr(CPULoongArchState *env) ++{ ++ int index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); ++ ++ invalidate_tlb(env, index); ++ ++ if (FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, NE)) { ++ env->tlb[index].tlb_misc = FIELD_DP64(env->tlb[index].tlb_misc, ++ TLB_MISC, E, 0); ++ return; ++ } ++ ++ fill_tlb_entry(env, index); ++} ++ ++void helper_tlbfill(CPULoongArchState *env) ++{ ++ uint64_t address, entryhi; ++ int index, set, stlb_idx; ++ uint16_t pagesize, stlb_ps; ++ ++ if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) { ++ entryhi = env->CSR_TLBREHI; ++ pagesize = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, PS); ++ } else { ++ entryhi = env->CSR_TLBEHI; ++ pagesize = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, PS); ++ } ++ ++ stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); ++ ++ if (pagesize == stlb_ps) { ++ /* Only write into STLB bits [47:13] */ ++ address = entryhi & ~MAKE_64BIT_MASK(0, R_CSR_TLBEHI_64_VPPN_SHIFT); ++ ++ /* Choose one set ramdomly */ ++ set = get_random_tlb(0, 7); ++ ++ /* Index in one set */ ++ stlb_idx = (address >> (stlb_ps + 1)) & 0xff; /* [0,255] */ ++ ++ index = set * 256 + stlb_idx; ++ } else { ++ /* Only write into MTLB */ ++ index = get_random_tlb(LOONGARCH_STLB, LOONGARCH_TLB_MAX - 1); ++ } ++ ++ invalidate_tlb(env, index); ++ fill_tlb_entry(env, index); ++} ++ ++void helper_tlbclr(CPULoongArchState *env) ++{ ++ LoongArchTLB *tlb; ++ int i, index; ++ uint16_t csr_asid, tlb_asid, tlb_g; ++ ++ csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); ++ index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); ++ ++ if (index < LOONGARCH_STLB) { ++ /* STLB. One line per operation */ ++ for (i = 0; i < 8; i++) { ++ tlb = &env->tlb[i * 256 + (index % 256)]; ++ tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); ++ tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); ++ if (!tlb_g && tlb_asid == csr_asid) { ++ tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); ++ } ++ } ++ } else if (index < LOONGARCH_TLB_MAX) { ++ /* All MTLB entries */ ++ for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) { ++ tlb = &env->tlb[i]; ++ tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); ++ tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); ++ if (!tlb_g && tlb_asid == csr_asid) { ++ tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); ++ } ++ } ++ } ++ ++ tlb_flush(env_cpu(env)); ++} ++ ++void helper_tlbflush(CPULoongArchState *env) ++{ ++ int i, index; ++ ++ index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); ++ ++ if (index < LOONGARCH_STLB) { ++ /* STLB. One line per operation */ ++ for (i = 0; i < 8; i++) { ++ int s_idx = i * 256 + (index % 256); ++ env->tlb[s_idx].tlb_misc = FIELD_DP64(env->tlb[s_idx].tlb_misc, ++ TLB_MISC, E, 0); ++ } ++ } else if (index < LOONGARCH_TLB_MAX) { ++ /* All MTLB entries */ ++ for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) { ++ env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc, ++ TLB_MISC, E, 0); ++ } ++ } ++ ++ tlb_flush(env_cpu(env)); ++} ++ ++void helper_invtlb_all(CPULoongArchState *env) ++{ ++ for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { ++ env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc, ++ TLB_MISC, E, 0); ++ } ++ tlb_flush(env_cpu(env)); ++} ++ ++void helper_invtlb_all_g(CPULoongArchState *env, uint32_t g) ++{ ++ for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { ++ LoongArchTLB *tlb = &env->tlb[i]; ++ uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); ++ ++ if (tlb_g == g) { ++ tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); ++ } ++ } ++ tlb_flush(env_cpu(env)); ++} ++ ++void helper_invtlb_all_asid(CPULoongArchState *env, target_ulong info) ++{ ++ uint16_t asid = info & R_CSR_ASID_ASID_MASK; ++ ++ for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { ++ LoongArchTLB *tlb = &env->tlb[i]; ++ uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); ++ uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); ++ ++ if (!tlb_g && (tlb_asid == asid)) { ++ tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); ++ } ++ } ++ tlb_flush(env_cpu(env)); ++} ++ ++void helper_invtlb_page_asid(CPULoongArchState *env, target_ulong info, ++ target_ulong addr) ++{ ++ uint16_t asid = info & 0x3ff; ++ ++ for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { ++ LoongArchTLB *tlb = &env->tlb[i]; ++ uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); ++ uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); ++ uint64_t vpn, tlb_vppn; ++ uint8_t tlb_ps, compare_shift; ++ ++ if (i >= LOONGARCH_STLB) { ++ tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); ++ } else { ++ tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); ++ } ++ tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); ++ vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1); ++ compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; ++ ++ if (!tlb_g && (tlb_asid == asid) && ++ (vpn == (tlb_vppn >> compare_shift))) { ++ tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); ++ } ++ } ++ tlb_flush(env_cpu(env)); ++} ++ ++void helper_invtlb_page_asid_or_g(CPULoongArchState *env, ++ target_ulong info, target_ulong addr) ++{ ++ uint16_t asid = info & 0x3ff; ++ ++ for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { ++ LoongArchTLB *tlb = &env->tlb[i]; ++ uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); ++ uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); ++ uint64_t vpn, tlb_vppn; ++ uint8_t tlb_ps, compare_shift; ++ ++ if (i >= LOONGARCH_STLB) { ++ tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); ++ } else { ++ tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); ++ } ++ tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); ++ vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1); ++ compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; ++ ++ if ((tlb_g || (tlb_asid == asid)) && ++ (vpn == (tlb_vppn >> compare_shift))) { ++ tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); ++ } ++ } ++ tlb_flush(env_cpu(env)); ++} ++ ++bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, ++ MMUAccessType access_type, int mmu_idx, ++ bool probe, uintptr_t retaddr) ++{ ++ LoongArchCPU *cpu = LOONGARCH_CPU(cs); ++ CPULoongArchState *env = &cpu->env; ++ hwaddr physical; ++ int prot; ++ int ret; ++ ++ /* Data access */ ++ ret = get_physical_address(env, &physical, &prot, address, ++ access_type, mmu_idx); ++ ++ if (ret == TLBRET_MATCH) { ++ tlb_set_page(cs, address & TARGET_PAGE_MASK, ++ physical & TARGET_PAGE_MASK, prot, ++ mmu_idx, TARGET_PAGE_SIZE); ++ qemu_log_mask(CPU_LOG_MMU, ++ "%s address=%" VADDR_PRIx " physical " HWADDR_FMT_plx ++ " prot %d\n", __func__, address, physical, prot); ++ return true; ++ } else { ++ qemu_log_mask(CPU_LOG_MMU, ++ "%s address=%" VADDR_PRIx " ret %d\n", __func__, address, ++ ret); ++ } ++ if (probe) { ++ return false; ++ } ++ raise_mmu_exception(env, address, access_type, ret); ++ cpu_loop_exit_restore(cs, retaddr); ++} ++ ++target_ulong helper_lddir(CPULoongArchState *env, target_ulong base, ++ target_ulong level, uint32_t mem_idx) ++{ ++ CPUState *cs = env_cpu(env); ++ target_ulong badvaddr, index, phys, ret; ++ int shift; ++ uint64_t dir_base, dir_width; ++ bool huge = (base >> LOONGARCH_PAGE_HUGE_SHIFT) & 0x1; ++ ++ badvaddr = env->CSR_TLBRBADV; ++ base = base & TARGET_PHYS_MASK; ++ ++ /* 0:64bit, 1:128bit, 2:192bit, 3:256bit */ ++ shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH); ++ shift = (shift + 1) * 3; ++ ++ if (huge) { ++ return base; ++ } ++ switch (level) { ++ case 1: ++ dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_BASE); ++ dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_WIDTH); ++ break; ++ case 2: ++ dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_BASE); ++ dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_WIDTH); ++ break; ++ case 3: ++ dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_BASE); ++ dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_WIDTH); ++ break; ++ case 4: ++ dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_BASE); ++ dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_WIDTH); ++ break; ++ default: ++ do_raise_exception(env, EXCCODE_INE, GETPC()); ++ return 0; ++ } ++ index = (badvaddr >> dir_base) & ((1 << dir_width) - 1); ++ phys = base | index << shift; ++ ret = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; ++ return ret; ++} ++ ++void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, ++ uint32_t mem_idx) ++{ ++ CPUState *cs = env_cpu(env); ++ target_ulong phys, tmp0, ptindex, ptoffset0, ptoffset1, ps, badv; ++ int shift; ++ bool huge = (base >> LOONGARCH_PAGE_HUGE_SHIFT) & 0x1; ++ uint64_t ptbase = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE); ++ uint64_t ptwidth = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH); ++ ++ base = base & TARGET_PHYS_MASK; ++ ++ if (huge) { ++ /* Huge Page. base is paddr */ ++ tmp0 = base ^ (1 << LOONGARCH_PAGE_HUGE_SHIFT); ++ /* Move Global bit */ ++ tmp0 = ((tmp0 & (1 << LOONGARCH_HGLOBAL_SHIFT)) >> ++ LOONGARCH_HGLOBAL_SHIFT) << R_TLBENTRY_G_SHIFT | ++ (tmp0 & (~(1 << LOONGARCH_HGLOBAL_SHIFT))); ++ ps = ptbase + ptwidth - 1; ++ if (odd) { ++ tmp0 += MAKE_64BIT_MASK(ps, 1); ++ } ++ } else { ++ /* 0:64bit, 1:128bit, 2:192bit, 3:256bit */ ++ shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH); ++ shift = (shift + 1) * 3; ++ badv = env->CSR_TLBRBADV; ++ ++ ptindex = (badv >> ptbase) & ((1 << ptwidth) - 1); ++ ptindex = ptindex & ~0x1; /* clear bit 0 */ ++ ptoffset0 = ptindex << shift; ++ ptoffset1 = (ptindex + 1) << shift; ++ ++ phys = base | (odd ? ptoffset1 : ptoffset0); ++ tmp0 = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; ++ ps = ptbase; ++ } ++ ++ if (odd) { ++ env->CSR_TLBRELO1 = tmp0; ++ } else { ++ env->CSR_TLBRELO0 = tmp0; ++ } ++ env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, PS, ps); ++} +diff --git a/target/loongarch/tcg/translate.c b/target/loongarch/tcg/translate.c +new file mode 100644 +index 0000000..21f4db6 +--- /dev/null ++++ b/target/loongarch/tcg/translate.c +@@ -0,0 +1,370 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * LoongArch emulation for QEMU - main translation routines. ++ * ++ * Copyright (c) 2021 Loongson Technology Corporation Limited ++ */ ++ ++#include "qemu/osdep.h" ++#include "cpu.h" ++#include "tcg/tcg-op.h" ++#include "tcg/tcg-op-gvec.h" ++#include "exec/translation-block.h" ++#include "exec/translator.h" ++#include "exec/helper-proto.h" ++#include "exec/helper-gen.h" ++#include "exec/log.h" ++#include "qemu/qemu-print.h" ++#include "fpu/softfloat.h" ++#include "translate.h" ++#include "internals.h" ++#include "vec.h" ++ ++/* Global register indices */ ++TCGv cpu_gpr[32], cpu_pc; ++static TCGv cpu_lladdr, cpu_llval; ++ ++#define HELPER_H "helper.h" ++#include "exec/helper-info.c.inc" ++#undef HELPER_H ++ ++#define DISAS_STOP DISAS_TARGET_0 ++#define DISAS_EXIT DISAS_TARGET_1 ++#define DISAS_EXIT_UPDATE DISAS_TARGET_2 ++ ++static inline int vec_full_offset(int regno) ++{ ++ return offsetof(CPULoongArchState, fpr[regno]); ++} ++ ++static inline int vec_reg_offset(int regno, int index, MemOp mop) ++{ ++ const uint8_t size = 1 << mop; ++ int offs = index * size; ++ ++ if (HOST_BIG_ENDIAN && size < 8 ) { ++ offs ^= (8 - size); ++ } ++ ++ return offs + vec_full_offset(regno); ++} ++ ++static inline void get_vreg64(TCGv_i64 dest, int regno, int index) ++{ ++ tcg_gen_ld_i64(dest, tcg_env, ++ offsetof(CPULoongArchState, fpr[regno].vreg.D(index))); ++} ++ ++static inline void set_vreg64(TCGv_i64 src, int regno, int index) ++{ ++ tcg_gen_st_i64(src, tcg_env, ++ offsetof(CPULoongArchState, fpr[regno].vreg.D(index))); ++} ++ ++static inline int plus_1(DisasContext *ctx, int x) ++{ ++ return x + 1; ++} ++ ++static inline int shl_1(DisasContext *ctx, int x) ++{ ++ return x << 1; ++} ++ ++static inline int shl_2(DisasContext *ctx, int x) ++{ ++ return x << 2; ++} ++ ++static inline int shl_3(DisasContext *ctx, int x) ++{ ++ return x << 3; ++} ++ ++/* ++ * LoongArch the upper 32 bits are undefined ("can be any value"). ++ * QEMU chooses to nanbox, because it is most likely to show guest bugs early. ++ */ ++static void gen_nanbox_s(TCGv_i64 out, TCGv_i64 in) ++{ ++ tcg_gen_ori_i64(out, in, MAKE_64BIT_MASK(32, 32)); ++} ++ ++void generate_exception(DisasContext *ctx, int excp) ++{ ++ tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); ++ gen_helper_raise_exception(tcg_env, tcg_constant_i32(excp)); ++ ctx->base.is_jmp = DISAS_NORETURN; ++} ++ ++static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) ++{ ++ if (ctx->va32) { ++ dest = (uint32_t) dest; ++ } ++ ++ if (translator_use_goto_tb(&ctx->base, dest)) { ++ tcg_gen_goto_tb(n); ++ tcg_gen_movi_tl(cpu_pc, dest); ++ tcg_gen_exit_tb(ctx->base.tb, n); ++ } else { ++ tcg_gen_movi_tl(cpu_pc, dest); ++ tcg_gen_lookup_and_goto_ptr(); ++ } ++} ++ ++static void loongarch_tr_init_disas_context(DisasContextBase *dcbase, ++ CPUState *cs) ++{ ++ int64_t bound; ++ CPULoongArchState *env = cpu_env(cs); ++ DisasContext *ctx = container_of(dcbase, DisasContext, base); ++ ++ ctx->page_start = ctx->base.pc_first & TARGET_PAGE_MASK; ++ ctx->plv = ctx->base.tb->flags & HW_FLAGS_PLV_MASK; ++ if (ctx->base.tb->flags & HW_FLAGS_CRMD_PG) { ++ ctx->mem_idx = ctx->plv; ++ } else { ++ ctx->mem_idx = MMU_IDX_DA; ++ } ++ ++ /* Bound the number of insns to execute to those left on the page. */ ++ bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4; ++ ctx->base.max_insns = MIN(ctx->base.max_insns, bound); ++ ++ if (FIELD_EX64(env->cpucfg[2], CPUCFG2, LSX)) { ++ ctx->vl = LSX_LEN; ++ } ++ ++ if (FIELD_EX64(env->cpucfg[2], CPUCFG2, LASX)) { ++ ctx->vl = LASX_LEN; ++ } ++ ++ ctx->la64 = is_la64(env); ++ ctx->va32 = (ctx->base.tb->flags & HW_FLAGS_VA32) != 0; ++ ++ ctx->zero = tcg_constant_tl(0); ++ ++ ctx->cpucfg1 = env->cpucfg[1]; ++ ctx->cpucfg2 = env->cpucfg[2]; ++} ++ ++static void loongarch_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) ++{ ++} ++ ++static void loongarch_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) ++{ ++ DisasContext *ctx = container_of(dcbase, DisasContext, base); ++ ++ tcg_gen_insn_start(ctx->base.pc_next); ++} ++ ++/* ++ * Wrappers for getting reg values. ++ * ++ * The $zero register does not have cpu_gpr[0] allocated -- we supply the ++ * constant zero as a source, and an uninitialized sink as destination. ++ * ++ * Further, we may provide an extension for word operations. ++ */ ++static TCGv gpr_src(DisasContext *ctx, int reg_num, DisasExtend src_ext) ++{ ++ TCGv t; ++ ++ if (reg_num == 0) { ++ return ctx->zero; ++ } ++ ++ switch (src_ext) { ++ case EXT_NONE: ++ return cpu_gpr[reg_num]; ++ case EXT_SIGN: ++ t = tcg_temp_new(); ++ tcg_gen_ext32s_tl(t, cpu_gpr[reg_num]); ++ return t; ++ case EXT_ZERO: ++ t = tcg_temp_new(); ++ tcg_gen_ext32u_tl(t, cpu_gpr[reg_num]); ++ return t; ++ } ++ g_assert_not_reached(); ++} ++ ++static TCGv gpr_dst(DisasContext *ctx, int reg_num, DisasExtend dst_ext) ++{ ++ if (reg_num == 0 || dst_ext) { ++ return tcg_temp_new(); ++ } ++ return cpu_gpr[reg_num]; ++} ++ ++static void gen_set_gpr(int reg_num, TCGv t, DisasExtend dst_ext) ++{ ++ if (reg_num != 0) { ++ switch (dst_ext) { ++ case EXT_NONE: ++ tcg_gen_mov_tl(cpu_gpr[reg_num], t); ++ break; ++ case EXT_SIGN: ++ tcg_gen_ext32s_tl(cpu_gpr[reg_num], t); ++ break; ++ case EXT_ZERO: ++ tcg_gen_ext32u_tl(cpu_gpr[reg_num], t); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ } ++} ++ ++static TCGv get_fpr(DisasContext *ctx, int reg_num) ++{ ++ TCGv t = tcg_temp_new(); ++ tcg_gen_ld_i64(t, tcg_env, ++ offsetof(CPULoongArchState, fpr[reg_num].vreg.D(0))); ++ return t; ++} ++ ++static void set_fpr(int reg_num, TCGv val) ++{ ++ tcg_gen_st_i64(val, tcg_env, ++ offsetof(CPULoongArchState, fpr[reg_num].vreg.D(0))); ++} ++ ++static TCGv make_address_x(DisasContext *ctx, TCGv base, TCGv addend) ++{ ++ TCGv temp = NULL; ++ ++ if (addend || ctx->va32) { ++ temp = tcg_temp_new(); ++ } ++ if (addend) { ++ tcg_gen_add_tl(temp, base, addend); ++ base = temp; ++ } ++ if (ctx->va32) { ++ tcg_gen_ext32u_tl(temp, base); ++ base = temp; ++ } ++ return base; ++} ++ ++static TCGv make_address_i(DisasContext *ctx, TCGv base, target_long ofs) ++{ ++ TCGv addend = ofs ? tcg_constant_tl(ofs) : NULL; ++ return make_address_x(ctx, base, addend); ++} ++ ++static uint64_t make_address_pc(DisasContext *ctx, uint64_t addr) ++{ ++ if (ctx->va32) { ++ addr = (int32_t)addr; ++ } ++ return addr; ++} ++ ++#include "decode-insns.c.inc" ++#include "insn_trans/trans_arith.c.inc" ++#include "insn_trans/trans_shift.c.inc" ++#include "insn_trans/trans_bit.c.inc" ++#include "insn_trans/trans_memory.c.inc" ++#include "insn_trans/trans_atomic.c.inc" ++#include "insn_trans/trans_extra.c.inc" ++#include "insn_trans/trans_farith.c.inc" ++#include "insn_trans/trans_fcmp.c.inc" ++#include "insn_trans/trans_fcnv.c.inc" ++#include "insn_trans/trans_fmov.c.inc" ++#include "insn_trans/trans_fmemory.c.inc" ++#include "insn_trans/trans_branch.c.inc" ++#include "insn_trans/trans_privileged.c.inc" ++#include "insn_trans/trans_vec.c.inc" ++ ++static void loongarch_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) ++{ ++ CPULoongArchState *env = cpu_env(cs); ++ DisasContext *ctx = container_of(dcbase, DisasContext, base); ++ ++ ctx->opcode = translator_ldl(env, &ctx->base, ctx->base.pc_next); ++ ++ if (!decode(ctx, ctx->opcode)) { ++ qemu_log_mask(LOG_UNIMP, "Error: unknown opcode. " ++ TARGET_FMT_lx ": 0x%x\n", ++ ctx->base.pc_next, ctx->opcode); ++ generate_exception(ctx, EXCCODE_INE); ++ } ++ ++ ctx->base.pc_next += 4; ++ ++ if (ctx->va32) { ++ ctx->base.pc_next = (uint32_t)ctx->base.pc_next; ++ } ++} ++ ++static void loongarch_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) ++{ ++ DisasContext *ctx = container_of(dcbase, DisasContext, base); ++ ++ switch (ctx->base.is_jmp) { ++ case DISAS_STOP: ++ tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); ++ tcg_gen_lookup_and_goto_ptr(); ++ break; ++ case DISAS_TOO_MANY: ++ gen_goto_tb(ctx, 0, ctx->base.pc_next); ++ break; ++ case DISAS_NORETURN: ++ break; ++ case DISAS_EXIT_UPDATE: ++ tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); ++ QEMU_FALLTHROUGH; ++ case DISAS_EXIT: ++ tcg_gen_exit_tb(NULL, 0); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++} ++ ++static void loongarch_tr_disas_log(const DisasContextBase *dcbase, ++ CPUState *cpu, FILE *logfile) ++{ ++ qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); ++ target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); ++} ++ ++static const TranslatorOps loongarch_tr_ops = { ++ .init_disas_context = loongarch_tr_init_disas_context, ++ .tb_start = loongarch_tr_tb_start, ++ .insn_start = loongarch_tr_insn_start, ++ .translate_insn = loongarch_tr_translate_insn, ++ .tb_stop = loongarch_tr_tb_stop, ++ .disas_log = loongarch_tr_disas_log, ++}; ++ ++void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, ++ target_ulong pc, void *host_pc) ++{ ++ DisasContext ctx; ++ ++ translator_loop(cs, tb, max_insns, pc, host_pc, ++ &loongarch_tr_ops, &ctx.base); ++} ++ ++void loongarch_translate_init(void) ++{ ++ int i; ++ ++ cpu_gpr[0] = NULL; ++ for (i = 1; i < 32; i++) { ++ cpu_gpr[i] = tcg_global_mem_new(tcg_env, ++ offsetof(CPULoongArchState, gpr[i]), ++ regnames[i]); ++ } ++ ++ cpu_pc = tcg_global_mem_new(tcg_env, offsetof(CPULoongArchState, pc), "pc"); ++ cpu_lladdr = tcg_global_mem_new(tcg_env, ++ offsetof(CPULoongArchState, lladdr), "lladdr"); ++ cpu_llval = tcg_global_mem_new(tcg_env, ++ offsetof(CPULoongArchState, llval), "llval"); ++} +diff --git a/target/loongarch/tcg/vec_helper.c b/target/loongarch/tcg/vec_helper.c +new file mode 100644 +index 0000000..3faf52c +--- /dev/null ++++ b/target/loongarch/tcg/vec_helper.c +@@ -0,0 +1,3494 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * QEMU LoongArch vector helper functions. ++ * ++ * Copyright (c) 2022-2023 Loongson Technology Corporation Limited ++ */ ++ ++#include "qemu/osdep.h" ++#include "cpu.h" ++#include "exec/exec-all.h" ++#include "exec/helper-proto.h" ++#include "fpu/softfloat.h" ++#include "internals.h" ++#include "tcg/tcg.h" ++#include "vec.h" ++#include "tcg/tcg-gvec-desc.h" ++ ++#define DO_ODD_EVEN(NAME, BIT, E1, E2, DO_OP) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ typedef __typeof(Vd->E1(0)) TD; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E1(i) = DO_OP((TD)Vj->E2(2 * i + 1), (TD)Vk->E2(2 * i)); \ ++ } \ ++} ++ ++DO_ODD_EVEN(vhaddw_h_b, 16, H, B, DO_ADD) ++DO_ODD_EVEN(vhaddw_w_h, 32, W, H, DO_ADD) ++DO_ODD_EVEN(vhaddw_d_w, 64, D, W, DO_ADD) ++ ++void HELPER(vhaddw_q_d)(void *vd, void *vj, void *vk, uint32_t desc) ++{ ++ int i; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ VReg *Vk = (VReg *)vk; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16 ; i++) { ++ Vd->Q(i) = int128_add(int128_makes64(Vj->D(2 * i + 1)), ++ int128_makes64(Vk->D(2 * i))); ++ } ++} ++ ++DO_ODD_EVEN(vhsubw_h_b, 16, H, B, DO_SUB) ++DO_ODD_EVEN(vhsubw_w_h, 32, W, H, DO_SUB) ++DO_ODD_EVEN(vhsubw_d_w, 64, D, W, DO_SUB) ++ ++void HELPER(vhsubw_q_d)(void *vd, void *vj, void *vk, uint32_t desc) ++{ ++ int i; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ VReg *Vk = (VReg *)vk; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ Vd->Q(i) = int128_sub(int128_makes64(Vj->D(2 * i + 1)), ++ int128_makes64(Vk->D(2 * i))); ++ } ++} ++ ++DO_ODD_EVEN(vhaddw_hu_bu, 16, UH, UB, DO_ADD) ++DO_ODD_EVEN(vhaddw_wu_hu, 32, UW, UH, DO_ADD) ++DO_ODD_EVEN(vhaddw_du_wu, 64, UD, UW, DO_ADD) ++ ++void HELPER(vhaddw_qu_du)(void *vd, void *vj, void *vk, uint32_t desc) ++{ ++ int i; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ VReg *Vk = (VReg *)vk; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i ++) { ++ Vd->Q(i) = int128_add(int128_make64(Vj->UD(2 * i + 1)), ++ int128_make64(Vk->UD(2 * i))); ++ } ++} ++ ++DO_ODD_EVEN(vhsubw_hu_bu, 16, UH, UB, DO_SUB) ++DO_ODD_EVEN(vhsubw_wu_hu, 32, UW, UH, DO_SUB) ++DO_ODD_EVEN(vhsubw_du_wu, 64, UD, UW, DO_SUB) ++ ++void HELPER(vhsubw_qu_du)(void *vd, void *vj, void *vk, uint32_t desc) ++{ ++ int i; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ VReg *Vk = (VReg *)vk; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ Vd->Q(i) = int128_sub(int128_make64(Vj->UD(2 * i + 1)), ++ int128_make64(Vk->UD(2 * i))); ++ } ++} ++ ++#define DO_EVEN(NAME, BIT, E1, E2, DO_OP) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ typedef __typeof(Vd->E1(0)) TD; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E1(i) = DO_OP((TD)Vj->E2(2 * i) ,(TD)Vk->E2(2 * i)); \ ++ } \ ++} ++ ++#define DO_ODD(NAME, BIT, E1, E2, DO_OP) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ typedef __typeof(Vd->E1(0)) TD; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E1(i) = DO_OP((TD)Vj->E2(2 * i + 1), (TD)Vk->E2(2 * i + 1)); \ ++ } \ ++} ++ ++void HELPER(vaddwev_q_d)(void *vd, void *vj, void *vk, uint32_t desc) ++{ ++ int i; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ VReg *Vk = (VReg *)vk; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ Vd->Q(i) = int128_add(int128_makes64(Vj->D(2 * i)), ++ int128_makes64(Vk->D(2 * i))); ++ } ++} ++ ++DO_EVEN(vaddwev_h_b, 16, H, B, DO_ADD) ++DO_EVEN(vaddwev_w_h, 32, W, H, DO_ADD) ++DO_EVEN(vaddwev_d_w, 64, D, W, DO_ADD) ++ ++void HELPER(vaddwod_q_d)(void *vd, void *vj, void *vk, uint32_t desc) ++{ ++ int i; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ VReg *Vk = (VReg *)vk; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ Vd->Q(i) = int128_add(int128_makes64(Vj->D(2 * i +1)), ++ int128_makes64(Vk->D(2 * i +1))); ++ } ++} ++ ++DO_ODD(vaddwod_h_b, 16, H, B, DO_ADD) ++DO_ODD(vaddwod_w_h, 32, W, H, DO_ADD) ++DO_ODD(vaddwod_d_w, 64, D, W, DO_ADD) ++ ++void HELPER(vsubwev_q_d)(void *vd, void *vj, void *vk, uint32_t desc) ++{ ++ int i; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ VReg *Vk = (VReg *)vk; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ Vd->Q(i) = int128_sub(int128_makes64(Vj->D(2 * i)), ++ int128_makes64(Vk->D(2 * i))); ++ } ++} ++ ++DO_EVEN(vsubwev_h_b, 16, H, B, DO_SUB) ++DO_EVEN(vsubwev_w_h, 32, W, H, DO_SUB) ++DO_EVEN(vsubwev_d_w, 64, D, W, DO_SUB) ++ ++void HELPER(vsubwod_q_d)(void *vd, void *vj, void *vk, uint32_t desc) ++{ ++ int i; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ VReg *Vk = (VReg *)vk; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ Vd->Q(i) = int128_sub(int128_makes64(Vj->D(2 * i + 1)), ++ int128_makes64(Vk->D(2 * i + 1))); ++ } ++} ++ ++DO_ODD(vsubwod_h_b, 16, H, B, DO_SUB) ++DO_ODD(vsubwod_w_h, 32, W, H, DO_SUB) ++DO_ODD(vsubwod_d_w, 64, D, W, DO_SUB) ++ ++void HELPER(vaddwev_q_du)(void *vd, void *vj, void *vk, uint32_t desc) ++{ ++ int i; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ VReg *Vk = (VReg *)vk; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ Vd->Q(i) = int128_add(int128_make64(Vj->UD(2 * i)), ++ int128_make64(Vk->UD(2 * i))); ++ } ++} ++ ++DO_EVEN(vaddwev_h_bu, 16, UH, UB, DO_ADD) ++DO_EVEN(vaddwev_w_hu, 32, UW, UH, DO_ADD) ++DO_EVEN(vaddwev_d_wu, 64, UD, UW, DO_ADD) ++ ++void HELPER(vaddwod_q_du)(void *vd, void *vj, void *vk, uint32_t desc) ++{ ++ int i; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ VReg *Vk = (VReg *)vk; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ Vd->Q(i) = int128_add(int128_make64(Vj->UD(2 * i + 1)), ++ int128_make64(Vk->UD(2 * i + 1))); ++ } ++} ++ ++DO_ODD(vaddwod_h_bu, 16, UH, UB, DO_ADD) ++DO_ODD(vaddwod_w_hu, 32, UW, UH, DO_ADD) ++DO_ODD(vaddwod_d_wu, 64, UD, UW, DO_ADD) ++ ++void HELPER(vsubwev_q_du)(void *vd, void *vj, void *vk, uint32_t desc) ++{ ++ int i; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ VReg *Vk = (VReg *)vk; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ Vd->Q(i) = int128_sub(int128_make64(Vj->UD(2 * i)), ++ int128_make64(Vk->UD(2 * i))); ++ } ++} ++ ++DO_EVEN(vsubwev_h_bu, 16, UH, UB, DO_SUB) ++DO_EVEN(vsubwev_w_hu, 32, UW, UH, DO_SUB) ++DO_EVEN(vsubwev_d_wu, 64, UD, UW, DO_SUB) ++ ++void HELPER(vsubwod_q_du)(void *vd, void *vj, void *vk, uint32_t desc) ++{ ++ int i; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ VReg *Vk = (VReg *)vk; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ Vd->Q(i) = int128_sub(int128_make64(Vj->UD(2 * i + 1)), ++ int128_make64(Vk->UD(2 * i + 1))); ++ } ++} ++ ++DO_ODD(vsubwod_h_bu, 16, UH, UB, DO_SUB) ++DO_ODD(vsubwod_w_hu, 32, UW, UH, DO_SUB) ++DO_ODD(vsubwod_d_wu, 64, UD, UW, DO_SUB) ++ ++#define DO_EVEN_U_S(NAME, BIT, ES1, EU1, ES2, EU2, DO_OP) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ typedef __typeof(Vd->ES1(0)) TDS; \ ++ typedef __typeof(Vd->EU1(0)) TDU; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->ES1(i) = DO_OP((TDU)Vj->EU2(2 * i) ,(TDS)Vk->ES2(2 * i)); \ ++ } \ ++} ++ ++#define DO_ODD_U_S(NAME, BIT, ES1, EU1, ES2, EU2, DO_OP) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ typedef __typeof(Vd->ES1(0)) TDS; \ ++ typedef __typeof(Vd->EU1(0)) TDU; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->ES1(i) = DO_OP((TDU)Vj->EU2(2 * i + 1), (TDS)Vk->ES2(2 * i + 1)); \ ++ } \ ++} ++ ++void HELPER(vaddwev_q_du_d)(void *vd, void *vj, void *vk, uint32_t desc) ++{ ++ int i; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ VReg *Vk = (VReg *)vk; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ Vd->Q(i) = int128_add(int128_make64(Vj->UD(2 * i)), ++ int128_makes64(Vk->D(2 * i))); ++ } ++} ++ ++DO_EVEN_U_S(vaddwev_h_bu_b, 16, H, UH, B, UB, DO_ADD) ++DO_EVEN_U_S(vaddwev_w_hu_h, 32, W, UW, H, UH, DO_ADD) ++DO_EVEN_U_S(vaddwev_d_wu_w, 64, D, UD, W, UW, DO_ADD) ++ ++void HELPER(vaddwod_q_du_d)(void *vd, void *vj, void *vk, uint32_t desc) ++{ ++ int i; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ VReg *Vk = (VReg *)vk; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ Vd->Q(i) = int128_add(int128_make64(Vj->UD(2 * i + 1)), ++ int128_makes64(Vk->D(2 * i + 1))); ++ } ++} ++ ++DO_ODD_U_S(vaddwod_h_bu_b, 16, H, UH, B, UB, DO_ADD) ++DO_ODD_U_S(vaddwod_w_hu_h, 32, W, UW, H, UH, DO_ADD) ++DO_ODD_U_S(vaddwod_d_wu_w, 64, D, UD, W, UW, DO_ADD) ++ ++#define DO_3OP(NAME, BIT, E, DO_OP) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E(i) = DO_OP(Vj->E(i), Vk->E(i)); \ ++ } \ ++} ++ ++DO_3OP(vavg_b, 8, B, DO_VAVG) ++DO_3OP(vavg_h, 16, H, DO_VAVG) ++DO_3OP(vavg_w, 32, W, DO_VAVG) ++DO_3OP(vavg_d, 64, D, DO_VAVG) ++DO_3OP(vavgr_b, 8, B, DO_VAVGR) ++DO_3OP(vavgr_h, 16, H, DO_VAVGR) ++DO_3OP(vavgr_w, 32, W, DO_VAVGR) ++DO_3OP(vavgr_d, 64, D, DO_VAVGR) ++DO_3OP(vavg_bu, 8, UB, DO_VAVG) ++DO_3OP(vavg_hu, 16, UH, DO_VAVG) ++DO_3OP(vavg_wu, 32, UW, DO_VAVG) ++DO_3OP(vavg_du, 64, UD, DO_VAVG) ++DO_3OP(vavgr_bu, 8, UB, DO_VAVGR) ++DO_3OP(vavgr_hu, 16, UH, DO_VAVGR) ++DO_3OP(vavgr_wu, 32, UW, DO_VAVGR) ++DO_3OP(vavgr_du, 64, UD, DO_VAVGR) ++ ++DO_3OP(vabsd_b, 8, B, DO_VABSD) ++DO_3OP(vabsd_h, 16, H, DO_VABSD) ++DO_3OP(vabsd_w, 32, W, DO_VABSD) ++DO_3OP(vabsd_d, 64, D, DO_VABSD) ++DO_3OP(vabsd_bu, 8, UB, DO_VABSD) ++DO_3OP(vabsd_hu, 16, UH, DO_VABSD) ++DO_3OP(vabsd_wu, 32, UW, DO_VABSD) ++DO_3OP(vabsd_du, 64, UD, DO_VABSD) ++ ++#define DO_VADDA(NAME, BIT, E) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E(i) = DO_VABS(Vj->E(i)) + DO_VABS(Vk->E(i)); \ ++ } \ ++} ++ ++DO_VADDA(vadda_b, 8, B) ++DO_VADDA(vadda_h, 16, H) ++DO_VADDA(vadda_w, 32, W) ++DO_VADDA(vadda_d, 64, D) ++ ++#define VMINMAXI(NAME, BIT, E, DO_OP) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ typedef __typeof(Vd->E(0)) TD; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E(i) = DO_OP(Vj->E(i), (TD)imm); \ ++ } \ ++} ++ ++VMINMAXI(vmini_b, 8, B, DO_MIN) ++VMINMAXI(vmini_h, 16, H, DO_MIN) ++VMINMAXI(vmini_w, 32, W, DO_MIN) ++VMINMAXI(vmini_d, 64, D, DO_MIN) ++VMINMAXI(vmaxi_b, 8, B, DO_MAX) ++VMINMAXI(vmaxi_h, 16, H, DO_MAX) ++VMINMAXI(vmaxi_w, 32, W, DO_MAX) ++VMINMAXI(vmaxi_d, 64, D, DO_MAX) ++VMINMAXI(vmini_bu, 8, UB, DO_MIN) ++VMINMAXI(vmini_hu, 16, UH, DO_MIN) ++VMINMAXI(vmini_wu, 32, UW, DO_MIN) ++VMINMAXI(vmini_du, 64, UD, DO_MIN) ++VMINMAXI(vmaxi_bu, 8, UB, DO_MAX) ++VMINMAXI(vmaxi_hu, 16, UH, DO_MAX) ++VMINMAXI(vmaxi_wu, 32, UW, DO_MAX) ++VMINMAXI(vmaxi_du, 64, UD, DO_MAX) ++ ++#define DO_VMUH(NAME, BIT, E1, E2, DO_OP) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ typedef __typeof(Vd->E1(0)) T; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E2(i) = ((T)Vj->E2(i)) * ((T)Vk->E2(i)) >> BIT; \ ++ } \ ++} ++ ++void HELPER(vmuh_d)(void *vd, void *vj, void *vk, uint32_t desc) ++{ ++ int i; ++ uint64_t l, h; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ VReg *Vk = (VReg *)vk; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 8; i++) { ++ muls64(&l, &h, Vj->D(i), Vk->D(i)); ++ Vd->D(i) = h; ++ } ++} ++ ++DO_VMUH(vmuh_b, 8, H, B, DO_MUH) ++DO_VMUH(vmuh_h, 16, W, H, DO_MUH) ++DO_VMUH(vmuh_w, 32, D, W, DO_MUH) ++ ++void HELPER(vmuh_du)(void *vd, void *vj, void *vk, uint32_t desc) ++{ ++ int i; ++ uint64_t l, h; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ VReg *Vk = (VReg *)vk; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 8; i++) { ++ mulu64(&l, &h, Vj->D(i), Vk->D(i)); ++ Vd->D(i) = h; ++ } ++} ++ ++DO_VMUH(vmuh_bu, 8, UH, UB, DO_MUH) ++DO_VMUH(vmuh_hu, 16, UW, UH, DO_MUH) ++DO_VMUH(vmuh_wu, 32, UD, UW, DO_MUH) ++ ++DO_EVEN(vmulwev_h_b, 16, H, B, DO_MUL) ++DO_EVEN(vmulwev_w_h, 32, W, H, DO_MUL) ++DO_EVEN(vmulwev_d_w, 64, D, W, DO_MUL) ++ ++DO_ODD(vmulwod_h_b, 16, H, B, DO_MUL) ++DO_ODD(vmulwod_w_h, 32, W, H, DO_MUL) ++DO_ODD(vmulwod_d_w, 64, D, W, DO_MUL) ++ ++DO_EVEN(vmulwev_h_bu, 16, UH, UB, DO_MUL) ++DO_EVEN(vmulwev_w_hu, 32, UW, UH, DO_MUL) ++DO_EVEN(vmulwev_d_wu, 64, UD, UW, DO_MUL) ++ ++DO_ODD(vmulwod_h_bu, 16, UH, UB, DO_MUL) ++DO_ODD(vmulwod_w_hu, 32, UW, UH, DO_MUL) ++DO_ODD(vmulwod_d_wu, 64, UD, UW, DO_MUL) ++ ++DO_EVEN_U_S(vmulwev_h_bu_b, 16, H, UH, B, UB, DO_MUL) ++DO_EVEN_U_S(vmulwev_w_hu_h, 32, W, UW, H, UH, DO_MUL) ++DO_EVEN_U_S(vmulwev_d_wu_w, 64, D, UD, W, UW, DO_MUL) ++ ++DO_ODD_U_S(vmulwod_h_bu_b, 16, H, UH, B, UB, DO_MUL) ++DO_ODD_U_S(vmulwod_w_hu_h, 32, W, UW, H, UH, DO_MUL) ++DO_ODD_U_S(vmulwod_d_wu_w, 64, D, UD, W, UW, DO_MUL) ++ ++#define VMADDSUB(NAME, BIT, E, DO_OP) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E(i) = DO_OP(Vd->E(i), Vj->E(i) ,Vk->E(i)); \ ++ } \ ++} ++ ++VMADDSUB(vmadd_b, 8, B, DO_MADD) ++VMADDSUB(vmadd_h, 16, H, DO_MADD) ++VMADDSUB(vmadd_w, 32, W, DO_MADD) ++VMADDSUB(vmadd_d, 64, D, DO_MADD) ++VMADDSUB(vmsub_b, 8, B, DO_MSUB) ++VMADDSUB(vmsub_h, 16, H, DO_MSUB) ++VMADDSUB(vmsub_w, 32, W, DO_MSUB) ++VMADDSUB(vmsub_d, 64, D, DO_MSUB) ++ ++#define VMADDWEV(NAME, BIT, E1, E2, DO_OP) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ typedef __typeof(Vd->E1(0)) TD; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E1(i) += DO_OP((TD)Vj->E2(2 * i), (TD)Vk->E2(2 * i)); \ ++ } \ ++} ++ ++VMADDWEV(vmaddwev_h_b, 16, H, B, DO_MUL) ++VMADDWEV(vmaddwev_w_h, 32, W, H, DO_MUL) ++VMADDWEV(vmaddwev_d_w, 64, D, W, DO_MUL) ++VMADDWEV(vmaddwev_h_bu, 16, UH, UB, DO_MUL) ++VMADDWEV(vmaddwev_w_hu, 32, UW, UH, DO_MUL) ++VMADDWEV(vmaddwev_d_wu, 64, UD, UW, DO_MUL) ++ ++#define VMADDWOD(NAME, BIT, E1, E2, DO_OP) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ typedef __typeof(Vd->E1(0)) TD; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E1(i) += DO_OP((TD)Vj->E2(2 * i + 1), \ ++ (TD)Vk->E2(2 * i + 1)); \ ++ } \ ++} ++ ++VMADDWOD(vmaddwod_h_b, 16, H, B, DO_MUL) ++VMADDWOD(vmaddwod_w_h, 32, W, H, DO_MUL) ++VMADDWOD(vmaddwod_d_w, 64, D, W, DO_MUL) ++VMADDWOD(vmaddwod_h_bu, 16, UH, UB, DO_MUL) ++VMADDWOD(vmaddwod_w_hu, 32, UW, UH, DO_MUL) ++VMADDWOD(vmaddwod_d_wu, 64, UD, UW, DO_MUL) ++ ++#define VMADDWEV_U_S(NAME, BIT, ES1, EU1, ES2, EU2, DO_OP) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ typedef __typeof(Vd->ES1(0)) TS1; \ ++ typedef __typeof(Vd->EU1(0)) TU1; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->ES1(i) += DO_OP((TU1)Vj->EU2(2 * i), \ ++ (TS1)Vk->ES2(2 * i)); \ ++ } \ ++} ++ ++VMADDWEV_U_S(vmaddwev_h_bu_b, 16, H, UH, B, UB, DO_MUL) ++VMADDWEV_U_S(vmaddwev_w_hu_h, 32, W, UW, H, UH, DO_MUL) ++VMADDWEV_U_S(vmaddwev_d_wu_w, 64, D, UD, W, UW, DO_MUL) ++ ++#define VMADDWOD_U_S(NAME, BIT, ES1, EU1, ES2, EU2, DO_OP) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ typedef __typeof(Vd->ES1(0)) TS1; \ ++ typedef __typeof(Vd->EU1(0)) TU1; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->ES1(i) += DO_OP((TU1)Vj->EU2(2 * i + 1), \ ++ (TS1)Vk->ES2(2 * i + 1)); \ ++ } \ ++} ++ ++VMADDWOD_U_S(vmaddwod_h_bu_b, 16, H, UH, B, UB, DO_MUL) ++VMADDWOD_U_S(vmaddwod_w_hu_h, 32, W, UW, H, UH, DO_MUL) ++VMADDWOD_U_S(vmaddwod_d_wu_w, 64, D, UD, W, UW, DO_MUL) ++ ++#define VDIV(NAME, BIT, E, DO_OP) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E(i) = DO_OP(Vj->E(i), Vk->E(i)); \ ++ } \ ++} ++ ++VDIV(vdiv_b, 8, B, DO_DIV) ++VDIV(vdiv_h, 16, H, DO_DIV) ++VDIV(vdiv_w, 32, W, DO_DIV) ++VDIV(vdiv_d, 64, D, DO_DIV) ++VDIV(vdiv_bu, 8, UB, DO_DIVU) ++VDIV(vdiv_hu, 16, UH, DO_DIVU) ++VDIV(vdiv_wu, 32, UW, DO_DIVU) ++VDIV(vdiv_du, 64, UD, DO_DIVU) ++VDIV(vmod_b, 8, B, DO_REM) ++VDIV(vmod_h, 16, H, DO_REM) ++VDIV(vmod_w, 32, W, DO_REM) ++VDIV(vmod_d, 64, D, DO_REM) ++VDIV(vmod_bu, 8, UB, DO_REMU) ++VDIV(vmod_hu, 16, UH, DO_REMU) ++VDIV(vmod_wu, 32, UW, DO_REMU) ++VDIV(vmod_du, 64, UD, DO_REMU) ++ ++#define VSAT_S(NAME, BIT, E) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t max, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ typedef __typeof(Vd->E(0)) TD; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E(i) = Vj->E(i) > (TD)max ? (TD)max : \ ++ Vj->E(i) < (TD)~max ? (TD)~max: Vj->E(i); \ ++ } \ ++} ++ ++VSAT_S(vsat_b, 8, B) ++VSAT_S(vsat_h, 16, H) ++VSAT_S(vsat_w, 32, W) ++VSAT_S(vsat_d, 64, D) ++ ++#define VSAT_U(NAME, BIT, E) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t max, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ typedef __typeof(Vd->E(0)) TD; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E(i) = Vj->E(i) > (TD)max ? (TD)max : Vj->E(i); \ ++ } \ ++} ++ ++VSAT_U(vsat_bu, 8, UB) ++VSAT_U(vsat_hu, 16, UH) ++VSAT_U(vsat_wu, 32, UW) ++VSAT_U(vsat_du, 64, UD) ++ ++#define VEXTH(NAME, BIT, E1, E2) \ ++void HELPER(NAME)(void *vd, void *vj, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ Vd->E1(j + i * ofs) = Vj->E2(j + ofs + ofs * 2 * i); \ ++ } \ ++ } \ ++} ++ ++void HELPER(vexth_q_d)(void *vd, void *vj, uint32_t desc) ++{ ++ int i; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ Vd->Q(i) = int128_makes64(Vj->D(2 * i + 1)); ++ } ++} ++ ++void HELPER(vexth_qu_du)(void *vd, void *vj, uint32_t desc) ++{ ++ int i; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ Vd->Q(i) = int128_make64(Vj->UD(2 * i + 1)); ++ } ++} ++ ++VEXTH(vexth_h_b, 16, H, B) ++VEXTH(vexth_w_h, 32, W, H) ++VEXTH(vexth_d_w, 64, D, W) ++VEXTH(vexth_hu_bu, 16, UH, UB) ++VEXTH(vexth_wu_hu, 32, UW, UH) ++VEXTH(vexth_du_wu, 64, UD, UW) ++ ++#define VEXT2XV(NAME, BIT, E1, E2) \ ++void HELPER(NAME)(void *vd, void *vj, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ temp.E1(i) = Vj->E2(i); \ ++ } \ ++ *Vd = temp; \ ++} ++ ++VEXT2XV(vext2xv_h_b, 16, H, B) ++VEXT2XV(vext2xv_w_b, 32, W, B) ++VEXT2XV(vext2xv_d_b, 64, D, B) ++VEXT2XV(vext2xv_w_h, 32, W, H) ++VEXT2XV(vext2xv_d_h, 64, D, H) ++VEXT2XV(vext2xv_d_w, 64, D, W) ++VEXT2XV(vext2xv_hu_bu, 16, UH, UB) ++VEXT2XV(vext2xv_wu_bu, 32, UW, UB) ++VEXT2XV(vext2xv_du_bu, 64, UD, UB) ++VEXT2XV(vext2xv_wu_hu, 32, UW, UH) ++VEXT2XV(vext2xv_du_hu, 64, UD, UH) ++VEXT2XV(vext2xv_du_wu, 64, UD, UW) ++ ++DO_3OP(vsigncov_b, 8, B, DO_SIGNCOV) ++DO_3OP(vsigncov_h, 16, H, DO_SIGNCOV) ++DO_3OP(vsigncov_w, 32, W, DO_SIGNCOV) ++DO_3OP(vsigncov_d, 64, D, DO_SIGNCOV) ++ ++static uint64_t do_vmskltz_b(int64_t val) ++{ ++ uint64_t m = 0x8080808080808080ULL; ++ uint64_t c = val & m; ++ c |= c << 7; ++ c |= c << 14; ++ c |= c << 28; ++ return c >> 56; ++} ++ ++void HELPER(vmskltz_b)(void *vd, void *vj, uint32_t desc) ++{ ++ int i; ++ uint16_t temp = 0; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ temp = 0; ++ temp = do_vmskltz_b(Vj->D(2 * i)); ++ temp |= (do_vmskltz_b(Vj->D(2 * i + 1)) << 8); ++ Vd->D(2 * i) = temp; ++ Vd->D(2 * i + 1) = 0; ++ } ++} ++ ++static uint64_t do_vmskltz_h(int64_t val) ++{ ++ uint64_t m = 0x8000800080008000ULL; ++ uint64_t c = val & m; ++ c |= c << 15; ++ c |= c << 30; ++ return c >> 60; ++} ++ ++void HELPER(vmskltz_h)(void *vd, void *vj, uint32_t desc) ++{ ++ int i; ++ uint16_t temp = 0; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ temp = 0; ++ temp = do_vmskltz_h(Vj->D(2 * i)); ++ temp |= (do_vmskltz_h(Vj->D(2 * i + 1)) << 4); ++ Vd->D(2 * i) = temp; ++ Vd->D(2 * i + 1) = 0; ++ } ++} ++ ++static uint64_t do_vmskltz_w(int64_t val) ++{ ++ uint64_t m = 0x8000000080000000ULL; ++ uint64_t c = val & m; ++ c |= c << 31; ++ return c >> 62; ++} ++ ++void HELPER(vmskltz_w)(void *vd, void *vj, uint32_t desc) ++{ ++ int i; ++ uint16_t temp = 0; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ temp = 0; ++ temp = do_vmskltz_w(Vj->D(2 * i)); ++ temp |= (do_vmskltz_w(Vj->D(2 * i + 1)) << 2); ++ Vd->D(2 * i) = temp; ++ Vd->D(2 * i + 1) = 0; ++ } ++} ++ ++static uint64_t do_vmskltz_d(int64_t val) ++{ ++ return (uint64_t)val >> 63; ++} ++void HELPER(vmskltz_d)(void *vd, void *vj, uint32_t desc) ++{ ++ int i; ++ uint16_t temp = 0; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ temp = 0; ++ temp = do_vmskltz_d(Vj->D(2 * i)); ++ temp |= (do_vmskltz_d(Vj->D(2 * i + 1)) << 1); ++ Vd->D(2 * i) = temp; ++ Vd->D(2 * i + 1) = 0; ++ } ++} ++ ++void HELPER(vmskgez_b)(void *vd, void *vj, uint32_t desc) ++{ ++ int i; ++ uint16_t temp = 0; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ temp = 0; ++ temp = do_vmskltz_b(Vj->D(2 * i)); ++ temp |= (do_vmskltz_b(Vj->D(2 * i + 1)) << 8); ++ Vd->D(2 * i) = (uint16_t)(~temp); ++ Vd->D(2 * i + 1) = 0; ++ } ++} ++ ++static uint64_t do_vmskez_b(uint64_t a) ++{ ++ uint64_t m = 0x7f7f7f7f7f7f7f7fULL; ++ uint64_t c = ~(((a & m) + m) | a | m); ++ c |= c << 7; ++ c |= c << 14; ++ c |= c << 28; ++ return c >> 56; ++} ++ ++void HELPER(vmsknz_b)(void *vd, void *vj, uint32_t desc) ++{ ++ int i; ++ uint16_t temp = 0; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ temp = 0; ++ temp = do_vmskez_b(Vj->D(2 * i)); ++ temp |= (do_vmskez_b(Vj->D(2 * i + 1)) << 8); ++ Vd->D(2 * i) = (uint16_t)(~temp); ++ Vd->D(2 * i + 1) = 0; ++ } ++} ++ ++void HELPER(vnori_b)(void *vd, void *vj, uint64_t imm, uint32_t desc) ++{ ++ int i; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ ++ for (i = 0; i < simd_oprsz(desc); i++) { ++ Vd->B(i) = ~(Vj->B(i) | (uint8_t)imm); ++ } ++} ++ ++#define VSLLWIL(NAME, BIT, E1, E2) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ typedef __typeof(temp.E1(0)) TD; \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ temp.E1(j + ofs * i) = (TD)Vj->E2(j + ofs * 2 * i) << (imm % BIT); \ ++ } \ ++ } \ ++ *Vd = temp; \ ++} ++ ++ ++void HELPER(vextl_q_d)(void *vd, void *vj, uint32_t desc) ++{ ++ int i; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ Vd->Q(i) = int128_makes64(Vj->D(2 * i)); ++ } ++} ++ ++void HELPER(vextl_qu_du)(void *vd, void *vj, uint32_t desc) ++{ ++ int i; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ Vd->Q(i) = int128_make64(Vj->UD(2 * i)); ++ } ++} ++ ++VSLLWIL(vsllwil_h_b, 16, H, B) ++VSLLWIL(vsllwil_w_h, 32, W, H) ++VSLLWIL(vsllwil_d_w, 64, D, W) ++VSLLWIL(vsllwil_hu_bu, 16, UH, UB) ++VSLLWIL(vsllwil_wu_hu, 32, UW, UH) ++VSLLWIL(vsllwil_du_wu, 64, UD, UW) ++ ++#define do_vsrlr(E, T) \ ++static T do_vsrlr_ ##E(T s1, int sh) \ ++{ \ ++ if (sh == 0) { \ ++ return s1; \ ++ } else { \ ++ return (s1 >> sh) + ((s1 >> (sh - 1)) & 0x1); \ ++ } \ ++} ++ ++do_vsrlr(B, uint8_t) ++do_vsrlr(H, uint16_t) ++do_vsrlr(W, uint32_t) ++do_vsrlr(D, uint64_t) ++ ++#define VSRLR(NAME, BIT, T, E) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E(i) = do_vsrlr_ ## E(Vj->E(i), ((T)Vk->E(i))%BIT); \ ++ } \ ++} ++ ++VSRLR(vsrlr_b, 8, uint8_t, B) ++VSRLR(vsrlr_h, 16, uint16_t, H) ++VSRLR(vsrlr_w, 32, uint32_t, W) ++VSRLR(vsrlr_d, 64, uint64_t, D) ++ ++#define VSRLRI(NAME, BIT, E) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E(i) = do_vsrlr_ ## E(Vj->E(i), imm); \ ++ } \ ++} ++ ++VSRLRI(vsrlri_b, 8, B) ++VSRLRI(vsrlri_h, 16, H) ++VSRLRI(vsrlri_w, 32, W) ++VSRLRI(vsrlri_d, 64, D) ++ ++#define do_vsrar(E, T) \ ++static T do_vsrar_ ##E(T s1, int sh) \ ++{ \ ++ if (sh == 0) { \ ++ return s1; \ ++ } else { \ ++ return (s1 >> sh) + ((s1 >> (sh - 1)) & 0x1); \ ++ } \ ++} ++ ++do_vsrar(B, int8_t) ++do_vsrar(H, int16_t) ++do_vsrar(W, int32_t) ++do_vsrar(D, int64_t) ++ ++#define VSRAR(NAME, BIT, T, E) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E(i) = do_vsrar_ ## E(Vj->E(i), ((T)Vk->E(i))%BIT); \ ++ } \ ++} ++ ++VSRAR(vsrar_b, 8, uint8_t, B) ++VSRAR(vsrar_h, 16, uint16_t, H) ++VSRAR(vsrar_w, 32, uint32_t, W) ++VSRAR(vsrar_d, 64, uint64_t, D) ++ ++#define VSRARI(NAME, BIT, E) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E(i) = do_vsrar_ ## E(Vj->E(i), imm); \ ++ } \ ++} ++ ++VSRARI(vsrari_b, 8, B) ++VSRARI(vsrari_h, 16, H) ++VSRARI(vsrari_w, 32, W) ++VSRARI(vsrari_d, 64, D) ++ ++#define VSRLN(NAME, BIT, E1, E2) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ Vd->E1(j + ofs * 2 * i) = R_SHIFT(Vj->E2(j + ofs * i), \ ++ Vk->E2(j + ofs * i) % BIT); \ ++ } \ ++ Vd->D(2 * i + 1) = 0; \ ++ } \ ++} ++ ++VSRLN(vsrln_b_h, 16, B, UH) ++VSRLN(vsrln_h_w, 32, H, UW) ++VSRLN(vsrln_w_d, 64, W, UD) ++ ++#define VSRAN(NAME, BIT, E1, E2, E3) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ Vd->E1(j + ofs * 2 * i) = R_SHIFT(Vj->E2(j + ofs * i), \ ++ Vk->E3(j + ofs * i) % BIT); \ ++ } \ ++ Vd->D(2 * i + 1) = 0; \ ++ } \ ++} ++ ++VSRAN(vsran_b_h, 16, B, H, UH) ++VSRAN(vsran_h_w, 32, H, W, UW) ++VSRAN(vsran_w_d, 64, W, D, UD) ++ ++#define VSRLNI(NAME, BIT, E1, E2) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ temp.E1(j + ofs * 2 * i) = R_SHIFT(Vj->E2(j + ofs * i), imm); \ ++ temp.E1(j + ofs * (2 * i + 1)) = R_SHIFT(Vd->E2(j + ofs * i), \ ++ imm); \ ++ } \ ++ } \ ++ *Vd = temp; \ ++} ++ ++void HELPER(vsrlni_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) ++{ ++ int i; ++ VReg temp = {}; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ ++ for (i = 0; i < 2; i++) { ++ temp.D(2 * i) = int128_getlo(int128_urshift(Vj->Q(i), imm % 128)); ++ temp.D(2 * i +1) = int128_getlo(int128_urshift(Vd->Q(i), imm % 128)); ++ } ++ *Vd = temp; ++} ++ ++VSRLNI(vsrlni_b_h, 16, B, UH) ++VSRLNI(vsrlni_h_w, 32, H, UW) ++VSRLNI(vsrlni_w_d, 64, W, UD) ++ ++#define VSRANI(NAME, BIT, E1, E2) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ temp.E1(j + ofs * 2 * i) = R_SHIFT(Vj->E2(j + ofs * i), imm); \ ++ temp.E1(j + ofs * (2 * i + 1)) = R_SHIFT(Vd->E2(j + ofs * i), \ ++ imm); \ ++ } \ ++ } \ ++ *Vd = temp; \ ++} ++ ++void HELPER(vsrani_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) ++{ ++ int i; ++ VReg temp = {}; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ ++ for (i = 0; i < 2; i++) { ++ temp.D(2 * i) = int128_getlo(int128_rshift(Vj->Q(i), imm % 128)); ++ temp.D(2 * i + 1) = int128_getlo(int128_rshift(Vd->Q(i), imm % 128)); ++ } ++ *Vd = temp; ++} ++ ++VSRANI(vsrani_b_h, 16, B, H) ++VSRANI(vsrani_h_w, 32, H, W) ++VSRANI(vsrani_w_d, 64, W, D) ++ ++#define VSRLRN(NAME, BIT, E1, E2, E3) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ Vd->E1(j + ofs * 2 * i) = do_vsrlr_ ##E2(Vj->E2(j + ofs * i), \ ++ Vk->E3(j + ofs * i) % BIT); \ ++ } \ ++ Vd->D(2 * i + 1) = 0; \ ++ } \ ++} ++ ++VSRLRN(vsrlrn_b_h, 16, B, H, UH) ++VSRLRN(vsrlrn_h_w, 32, H, W, UW) ++VSRLRN(vsrlrn_w_d, 64, W, D, UD) ++ ++#define VSRARN(NAME, BIT, E1, E2, E3) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ Vd->E1(j + ofs * 2 * i) = do_vsrar_ ## E2(Vj->E2(j + ofs * i), \ ++ Vk->E3(j + ofs * i) % BIT); \ ++ } \ ++ Vd->D(2 * i + 1) = 0; \ ++ } \ ++} ++ ++VSRARN(vsrarn_b_h, 16, B, H, UH) ++VSRARN(vsrarn_h_w, 32, H, W, UW) ++VSRARN(vsrarn_w_d, 64, W, D, UD) ++ ++#define VSRLRNI(NAME, BIT, E1, E2) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ temp.E1(j + ofs * 2 * i) = do_vsrlr_ ## E2(Vj->E2(j + ofs * i), imm); \ ++ temp.E1(j + ofs * (2 * i + 1)) = do_vsrlr_ ## E2(Vd->E2(j + ofs * i), \ ++ imm); \ ++ } \ ++ } \ ++ *Vd = temp; \ ++} ++ ++void HELPER(vsrlrni_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) ++{ ++ int i; ++ VReg temp = {}; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ Int128 r[4]; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ if (imm == 0) { ++ temp.D(2 * i) = int128_getlo(Vj->Q(i)); ++ temp.D(2 * i + 1) = int128_getlo(Vd->Q(i)); ++ } else { ++ r[2 * i] = int128_and(int128_urshift(Vj->Q(i), (imm - 1)), ++ int128_one()); ++ r[2 * i + 1] = int128_and(int128_urshift(Vd->Q(i), (imm - 1)), ++ int128_one()); ++ temp.D(2 * i) = int128_getlo(int128_add(int128_urshift(Vj->Q(i), ++ imm), r[2 * i])); ++ temp.D(2 * i + 1) = int128_getlo(int128_add(int128_urshift(Vd->Q(i), ++ imm), r[ 2 * i + 1])); ++ } ++ } ++ *Vd = temp; ++} ++ ++VSRLRNI(vsrlrni_b_h, 16, B, H) ++VSRLRNI(vsrlrni_h_w, 32, H, W) ++VSRLRNI(vsrlrni_w_d, 64, W, D) ++ ++#define VSRARNI(NAME, BIT, E1, E2) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ temp.E1(j + ofs * 2 * i) = do_vsrar_ ## E2(Vj->E2(j + ofs * i), imm); \ ++ temp.E1(j + ofs * (2 * i + 1)) = do_vsrar_ ## E2(Vd->E2(j + ofs * i), \ ++ imm); \ ++ } \ ++ } \ ++ *Vd = temp; \ ++} ++ ++void HELPER(vsrarni_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) ++{ ++ int i; ++ VReg temp = {}; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ Int128 r[4]; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ if (imm == 0) { ++ temp.D(2 * i) = int128_getlo(Vj->Q(i)); ++ temp.D(2 * i + 1) = int128_getlo(Vd->Q(i)); ++ } else { ++ r[2 * i] = int128_and(int128_rshift(Vj->Q(i), (imm - 1)), ++ int128_one()); ++ r[2 * i + 1] = int128_and(int128_rshift(Vd->Q(i), (imm - 1)), ++ int128_one()); ++ temp.D(2 * i) = int128_getlo(int128_add(int128_rshift(Vj->Q(i), ++ imm), r[2 * i])); ++ temp.D(2 * i + 1) = int128_getlo(int128_add(int128_rshift(Vd->Q(i), ++ imm), r[2 * i + 1])); ++ } ++ } ++ *Vd = temp; ++} ++ ++VSRARNI(vsrarni_b_h, 16, B, H) ++VSRARNI(vsrarni_h_w, 32, H, W) ++VSRARNI(vsrarni_w_d, 64, W, D) ++ ++#define SSRLNS(NAME, T1, T2, T3) \ ++static T1 do_ssrlns_ ## NAME(T2 e2, int sa, int sh) \ ++{ \ ++ T1 shft_res; \ ++ if (sa == 0) { \ ++ shft_res = e2; \ ++ } else { \ ++ shft_res = (((T1)e2) >> sa); \ ++ } \ ++ T3 mask; \ ++ mask = (1ull << sh) -1; \ ++ if (shft_res > mask) { \ ++ return mask; \ ++ } else { \ ++ return shft_res; \ ++ } \ ++} ++ ++SSRLNS(B, uint16_t, int16_t, uint8_t) ++SSRLNS(H, uint32_t, int32_t, uint16_t) ++SSRLNS(W, uint64_t, int64_t, uint32_t) ++ ++#define VSSRLN(NAME, BIT, E1, E2, E3) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ Vd->E1(j + ofs * 2 * i) = do_ssrlns_ ## E1(Vj->E2(j + ofs * i), \ ++ Vk->E3(j + ofs * i) % BIT, \ ++ BIT / 2 - 1); \ ++ } \ ++ Vd->D(2 * i + 1) = 0; \ ++ } \ ++} ++ ++VSSRLN(vssrln_b_h, 16, B, H, UH) ++VSSRLN(vssrln_h_w, 32, H, W, UW) ++VSSRLN(vssrln_w_d, 64, W, D, UD) ++ ++#define SSRANS(E, T1, T2) \ ++static T1 do_ssrans_ ## E(T1 e2, int sa, int sh) \ ++{ \ ++ T1 shft_res; \ ++ if (sa == 0) { \ ++ shft_res = e2; \ ++ } else { \ ++ shft_res = e2 >> sa; \ ++ } \ ++ T2 mask; \ ++ mask = (1ll << sh) - 1; \ ++ if (shft_res > mask) { \ ++ return mask; \ ++ } else if (shft_res < -(mask + 1)) { \ ++ return ~mask; \ ++ } else { \ ++ return shft_res; \ ++ } \ ++} ++ ++SSRANS(B, int16_t, int8_t) ++SSRANS(H, int32_t, int16_t) ++SSRANS(W, int64_t, int32_t) ++ ++#define VSSRAN(NAME, BIT, E1, E2, E3) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ Vd->E1(j + ofs * 2 * i) = do_ssrans_ ## E1(Vj->E2(j + ofs * i), \ ++ Vk->E3(j + ofs * i) % BIT, \ ++ BIT / 2 - 1); \ ++ } \ ++ Vd->D(2 * i + 1) = 0; \ ++ } \ ++} ++ ++VSSRAN(vssran_b_h, 16, B, H, UH) ++VSSRAN(vssran_h_w, 32, H, W, UW) ++VSSRAN(vssran_w_d, 64, W, D, UD) ++ ++#define SSRLNU(E, T1, T2, T3) \ ++static T1 do_ssrlnu_ ## E(T3 e2, int sa, int sh) \ ++{ \ ++ T1 shft_res; \ ++ if (sa == 0) { \ ++ shft_res = e2; \ ++ } else { \ ++ shft_res = (((T1)e2) >> sa); \ ++ } \ ++ T2 mask; \ ++ mask = (1ull << sh) - 1; \ ++ if (shft_res > mask) { \ ++ return mask; \ ++ } else { \ ++ return shft_res; \ ++ } \ ++} ++ ++SSRLNU(B, uint16_t, uint8_t, int16_t) ++SSRLNU(H, uint32_t, uint16_t, int32_t) ++SSRLNU(W, uint64_t, uint32_t, int64_t) ++ ++#define VSSRLNU(NAME, BIT, E1, E2, E3) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ Vd->E1(j + ofs * 2 * i) = do_ssrlnu_ ## E1(Vj->E2(j + ofs * i), \ ++ Vk->E3(j + ofs * i) % BIT, \ ++ BIT / 2); \ ++ } \ ++ Vd->D(2 * i + 1) = 0; \ ++ } \ ++} ++ ++VSSRLNU(vssrln_bu_h, 16, B, H, UH) ++VSSRLNU(vssrln_hu_w, 32, H, W, UW) ++VSSRLNU(vssrln_wu_d, 64, W, D, UD) ++ ++#define SSRANU(E, T1, T2, T3) \ ++static T1 do_ssranu_ ## E(T3 e2, int sa, int sh) \ ++{ \ ++ T1 shft_res; \ ++ if (sa == 0) { \ ++ shft_res = e2; \ ++ } else { \ ++ shft_res = e2 >> sa; \ ++ } \ ++ if (e2 < 0) { \ ++ shft_res = 0; \ ++ } \ ++ T2 mask; \ ++ mask = (1ull << sh) - 1; \ ++ if (shft_res > mask) { \ ++ return mask; \ ++ } else { \ ++ return shft_res; \ ++ } \ ++} ++ ++SSRANU(B, uint16_t, uint8_t, int16_t) ++SSRANU(H, uint32_t, uint16_t, int32_t) ++SSRANU(W, uint64_t, uint32_t, int64_t) ++ ++#define VSSRANU(NAME, BIT, E1, E2, E3) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ Vd->E1(j + ofs * 2 * i) = do_ssranu_ ## E1(Vj->E2(j + ofs * i), \ ++ Vk->E3(j + ofs * i) % BIT, \ ++ BIT / 2); \ ++ } \ ++ Vd->D(2 * i + 1) = 0; \ ++ } \ ++} ++ ++VSSRANU(vssran_bu_h, 16, B, H, UH) ++VSSRANU(vssran_hu_w, 32, H, W, UW) ++VSSRANU(vssran_wu_d, 64, W, D, UD) ++ ++#define VSSRLNI(NAME, BIT, E1, E2) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ temp.E1(j + ofs * 2 * i) = do_ssrlns_ ## E1(Vj->E2(j + ofs * i), \ ++ imm, BIT / 2 - 1); \ ++ temp.E1(j + ofs * (2 * i + 1)) = do_ssrlns_ ## E1(Vd->E2(j + ofs * i), \ ++ imm, BIT / 2 - 1); \ ++ } \ ++ } \ ++ *Vd = temp; \ ++} ++ ++static void do_vssrlni_q(VReg *Vd, VReg *Vj, ++ uint64_t imm, int idx, Int128 mask) ++{ ++ Int128 shft_res1, shft_res2; ++ ++ if (imm == 0) { ++ shft_res1 = Vj->Q(idx); ++ shft_res2 = Vd->Q(idx); ++ } else { ++ shft_res1 = int128_urshift(Vj->Q(idx), imm); ++ shft_res2 = int128_urshift(Vd->Q(idx), imm); ++ } ++ ++ if (int128_ult(mask, shft_res1)) { ++ Vd->D(idx * 2) = int128_getlo(mask); ++ }else { ++ Vd->D(idx * 2) = int128_getlo(shft_res1); ++ } ++ ++ if (int128_ult(mask, shft_res2)) { ++ Vd->D(idx * 2 + 1) = int128_getlo(mask); ++ }else { ++ Vd->D(idx * 2 + 1) = int128_getlo(shft_res2); ++ } ++} ++ ++void HELPER(vssrlni_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) ++{ ++ int i; ++ Int128 mask; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ mask = int128_sub(int128_lshift(int128_one(), 63), int128_one()); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ do_vssrlni_q(Vd, Vj, imm, i, mask); ++ } ++} ++ ++VSSRLNI(vssrlni_b_h, 16, B, H) ++VSSRLNI(vssrlni_h_w, 32, H, W) ++VSSRLNI(vssrlni_w_d, 64, W, D) ++ ++#define VSSRANI(NAME, BIT, E1, E2) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ temp.E1(j + ofs * 2 * i) = do_ssrans_ ## E1(Vj->E2(j + ofs * i), \ ++ imm, BIT / 2 - 1); \ ++ temp.E1(j + ofs * (2 * i + 1)) = do_ssrans_ ## E1(Vd->E2(j + ofs * i), \ ++ imm, BIT / 2 - 1); \ ++ } \ ++ } \ ++ *Vd = temp; \ ++} ++ ++static void do_vssrani_d_q(VReg *Vd, VReg *Vj, ++ uint64_t imm, int idx, Int128 mask, Int128 min) ++{ ++ Int128 shft_res1, shft_res2; ++ ++ if (imm == 0) { ++ shft_res1 = Vj->Q(idx); ++ shft_res2 = Vd->Q(idx); ++ } else { ++ shft_res1 = int128_rshift(Vj->Q(idx), imm); ++ shft_res2 = int128_rshift(Vd->Q(idx), imm); ++ } ++ ++ if (int128_gt(shft_res1, mask)) { ++ Vd->D(idx * 2) = int128_getlo(mask); ++ } else if (int128_lt(shft_res1, int128_neg(min))) { ++ Vd->D(idx * 2) = int128_getlo(min); ++ } else { ++ Vd->D(idx * 2) = int128_getlo(shft_res1); ++ } ++ ++ if (int128_gt(shft_res2, mask)) { ++ Vd->D(idx * 2 + 1) = int128_getlo(mask); ++ } else if (int128_lt(shft_res2, int128_neg(min))) { ++ Vd->D(idx * 2 + 1) = int128_getlo(min); ++ } else { ++ Vd->D(idx * 2 + 1) = int128_getlo(shft_res2); ++ } ++} ++ ++void HELPER(vssrani_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) ++{ ++ int i; ++ Int128 mask, min; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ mask = int128_sub(int128_lshift(int128_one(), 63), int128_one()); ++ min = int128_lshift(int128_one(), 63); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ do_vssrani_d_q(Vd, Vj, imm, i, mask, min); ++ } ++} ++ ++ ++VSSRANI(vssrani_b_h, 16, B, H) ++VSSRANI(vssrani_h_w, 32, H, W) ++VSSRANI(vssrani_w_d, 64, W, D) ++ ++#define VSSRLNUI(NAME, BIT, E1, E2) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ temp.E1(j + ofs * 2 * i) = do_ssrlnu_ ## E1(Vj->E2(j + ofs * i), \ ++ imm, BIT / 2); \ ++ temp.E1(j + ofs * (2 * i + 1)) = do_ssrlnu_ ## E1(Vd->E2(j + ofs * i), \ ++ imm, BIT / 2); \ ++ } \ ++ } \ ++ *Vd = temp; \ ++} ++ ++void HELPER(vssrlni_du_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) ++{ ++ int i; ++ Int128 mask; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ mask = int128_sub(int128_lshift(int128_one(), 64), int128_one()); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ do_vssrlni_q(Vd, Vj, imm, i, mask); ++ } ++} ++ ++VSSRLNUI(vssrlni_bu_h, 16, B, H) ++VSSRLNUI(vssrlni_hu_w, 32, H, W) ++VSSRLNUI(vssrlni_wu_d, 64, W, D) ++ ++#define VSSRANUI(NAME, BIT, E1, E2) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ temp.E1(j + ofs * 2 * i) = do_ssranu_ ## E1(Vj->E2(j + ofs * i), \ ++ imm, BIT / 2); \ ++ temp.E1(j + ofs * (2 * i + 1)) = do_ssranu_ ## E1(Vd->E2(j + ofs * i), \ ++ imm, BIT / 2); \ ++ } \ ++ } \ ++ *Vd = temp; \ ++} ++ ++static void do_vssrani_du_q(VReg *Vd, VReg *Vj, ++ uint64_t imm, int idx, Int128 mask) ++{ ++ Int128 shft_res1, shft_res2; ++ ++ if (imm == 0) { ++ shft_res1 = Vj->Q(idx); ++ shft_res2 = Vd->Q(idx); ++ } else { ++ shft_res1 = int128_rshift(Vj->Q(idx), imm); ++ shft_res2 = int128_rshift(Vd->Q(idx), imm); ++ } ++ ++ if (int128_lt(Vj->Q(idx), int128_zero())) { ++ shft_res1 = int128_zero(); ++ } ++ ++ if (int128_lt(Vd->Q(idx), int128_zero())) { ++ shft_res2 = int128_zero(); ++ } ++ if (int128_ult(mask, shft_res1)) { ++ Vd->D(idx * 2) = int128_getlo(mask); ++ }else { ++ Vd->D(idx * 2) = int128_getlo(shft_res1); ++ } ++ ++ if (int128_ult(mask, shft_res2)) { ++ Vd->D(idx * 2 + 1) = int128_getlo(mask); ++ }else { ++ Vd->D(idx * 2 + 1) = int128_getlo(shft_res2); ++ } ++ ++} ++ ++void HELPER(vssrani_du_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) ++{ ++ int i; ++ Int128 mask; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ mask = int128_sub(int128_lshift(int128_one(), 64), int128_one()); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ do_vssrani_du_q(Vd, Vj, imm, i, mask); ++ } ++} ++ ++VSSRANUI(vssrani_bu_h, 16, B, H) ++VSSRANUI(vssrani_hu_w, 32, H, W) ++VSSRANUI(vssrani_wu_d, 64, W, D) ++ ++#define SSRLRNS(E1, E2, T1, T2, T3) \ ++static T1 do_ssrlrns_ ## E1(T2 e2, int sa, int sh) \ ++{ \ ++ T1 shft_res; \ ++ \ ++ shft_res = do_vsrlr_ ## E2(e2, sa); \ ++ T1 mask; \ ++ mask = (1ull << sh) - 1; \ ++ if (shft_res > mask) { \ ++ return mask; \ ++ } else { \ ++ return shft_res; \ ++ } \ ++} ++ ++SSRLRNS(B, H, uint16_t, int16_t, uint8_t) ++SSRLRNS(H, W, uint32_t, int32_t, uint16_t) ++SSRLRNS(W, D, uint64_t, int64_t, uint32_t) ++ ++#define VSSRLRN(NAME, BIT, E1, E2, E3) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ Vd->E1(j + ofs * 2 * i) = do_ssrlrns_ ## E1(Vj->E2(j + ofs * i), \ ++ Vk->E3(j + ofs * i) % BIT, \ ++ BIT / 2 - 1); \ ++ } \ ++ Vd->D(2 * i + 1) = 0; \ ++ } \ ++} ++ ++VSSRLRN(vssrlrn_b_h, 16, B, H, UH) ++VSSRLRN(vssrlrn_h_w, 32, H, W, UW) ++VSSRLRN(vssrlrn_w_d, 64, W, D, UD) ++ ++#define SSRARNS(E1, E2, T1, T2) \ ++static T1 do_ssrarns_ ## E1(T1 e2, int sa, int sh) \ ++{ \ ++ T1 shft_res; \ ++ \ ++ shft_res = do_vsrar_ ## E2(e2, sa); \ ++ T2 mask; \ ++ mask = (1ll << sh) - 1; \ ++ if (shft_res > mask) { \ ++ return mask; \ ++ } else if (shft_res < -(mask +1)) { \ ++ return ~mask; \ ++ } else { \ ++ return shft_res; \ ++ } \ ++} ++ ++SSRARNS(B, H, int16_t, int8_t) ++SSRARNS(H, W, int32_t, int16_t) ++SSRARNS(W, D, int64_t, int32_t) ++ ++#define VSSRARN(NAME, BIT, E1, E2, E3) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ Vd->E1(j + ofs * 2 * i) = do_ssrarns_ ## E1(Vj->E2(j + ofs * i), \ ++ Vk->E3(j + ofs * i) % BIT, \ ++ BIT/ 2 - 1); \ ++ } \ ++ Vd->D(2 * i + 1) = 0; \ ++ } \ ++} ++ ++VSSRARN(vssrarn_b_h, 16, B, H, UH) ++VSSRARN(vssrarn_h_w, 32, H, W, UW) ++VSSRARN(vssrarn_w_d, 64, W, D, UD) ++ ++#define SSRLRNU(E1, E2, T1, T2, T3) \ ++static T1 do_ssrlrnu_ ## E1(T3 e2, int sa, int sh) \ ++{ \ ++ T1 shft_res; \ ++ \ ++ shft_res = do_vsrlr_ ## E2(e2, sa); \ ++ \ ++ T2 mask; \ ++ mask = (1ull << sh) - 1; \ ++ if (shft_res > mask) { \ ++ return mask; \ ++ } else { \ ++ return shft_res; \ ++ } \ ++} ++ ++SSRLRNU(B, H, uint16_t, uint8_t, int16_t) ++SSRLRNU(H, W, uint32_t, uint16_t, int32_t) ++SSRLRNU(W, D, uint64_t, uint32_t, int64_t) ++ ++#define VSSRLRNU(NAME, BIT, E1, E2, E3) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ Vd->E1(j + ofs * 2 * i) = do_ssrlrnu_ ## E1(Vj->E2(j + ofs * i), \ ++ Vk->E3(j + ofs * i) % BIT, \ ++ BIT / 2); \ ++ } \ ++ Vd->D(2 * i + 1) = 0; \ ++ } \ ++} ++ ++VSSRLRNU(vssrlrn_bu_h, 16, B, H, UH) ++VSSRLRNU(vssrlrn_hu_w, 32, H, W, UW) ++VSSRLRNU(vssrlrn_wu_d, 64, W, D, UD) ++ ++#define SSRARNU(E1, E2, T1, T2, T3) \ ++static T1 do_ssrarnu_ ## E1(T3 e2, int sa, int sh) \ ++{ \ ++ T1 shft_res; \ ++ \ ++ if (e2 < 0) { \ ++ shft_res = 0; \ ++ } else { \ ++ shft_res = do_vsrar_ ## E2(e2, sa); \ ++ } \ ++ T2 mask; \ ++ mask = (1ull << sh) - 1; \ ++ if (shft_res > mask) { \ ++ return mask; \ ++ } else { \ ++ return shft_res; \ ++ } \ ++} ++ ++SSRARNU(B, H, uint16_t, uint8_t, int16_t) ++SSRARNU(H, W, uint32_t, uint16_t, int32_t) ++SSRARNU(W, D, uint64_t, uint32_t, int64_t) ++ ++#define VSSRARNU(NAME, BIT, E1, E2, E3) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ Vd->E1(j + ofs * 2 * i) = do_ssrarnu_ ## E1(Vj->E2(j + ofs * i), \ ++ Vk->E3(j + ofs * i) % BIT, \ ++ BIT / 2); \ ++ } \ ++ Vd->D(2 * i + 1) = 0; \ ++ } \ ++} ++ ++VSSRARNU(vssrarn_bu_h, 16, B, H, UH) ++VSSRARNU(vssrarn_hu_w, 32, H, W, UW) ++VSSRARNU(vssrarn_wu_d, 64, W, D, UD) ++ ++#define VSSRLRNI(NAME, BIT, E1, E2) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ temp.E1(j + ofs * 2 * i) = do_ssrlrns_ ## E1(Vj->E2(j + ofs * i), \ ++ imm, BIT / 2 - 1); \ ++ temp.E1(j + ofs * (2 * i + 1)) = do_ssrlrns_ ## E1(Vd->E2(j + ofs * i), \ ++ imm, BIT / 2 - 1); \ ++ } \ ++ } \ ++ *Vd = temp; \ ++} ++ ++static void do_vssrlrni_q(VReg *Vd, VReg * Vj, ++ uint64_t imm, int idx, Int128 mask) ++{ ++ Int128 shft_res1, shft_res2, r1, r2; ++ if (imm == 0) { ++ shft_res1 = Vj->Q(idx); ++ shft_res2 = Vd->Q(idx); ++ } else { ++ r1 = int128_and(int128_urshift(Vj->Q(idx), (imm - 1)), int128_one()); ++ r2 = int128_and(int128_urshift(Vd->Q(idx), (imm - 1)), int128_one()); ++ shft_res1 = (int128_add(int128_urshift(Vj->Q(idx), imm), r1)); ++ shft_res2 = (int128_add(int128_urshift(Vd->Q(idx), imm), r2)); ++ } ++ ++ if (int128_ult(mask, shft_res1)) { ++ Vd->D(idx * 2) = int128_getlo(mask); ++ }else { ++ Vd->D(idx * 2) = int128_getlo(shft_res1); ++ } ++ ++ if (int128_ult(mask, shft_res2)) { ++ Vd->D(idx * 2 + 1) = int128_getlo(mask); ++ }else { ++ Vd->D(idx * 2 + 1) = int128_getlo(shft_res2); ++ } ++} ++ ++void HELPER(vssrlrni_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) ++{ ++ int i; ++ Int128 mask; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ mask = int128_sub(int128_lshift(int128_one(), 63), int128_one()); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ do_vssrlrni_q(Vd, Vj, imm, i, mask); ++ } ++} ++ ++VSSRLRNI(vssrlrni_b_h, 16, B, H) ++VSSRLRNI(vssrlrni_h_w, 32, H, W) ++VSSRLRNI(vssrlrni_w_d, 64, W, D) ++ ++#define VSSRARNI(NAME, BIT, E1, E2) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ temp.E1(j + ofs * 2 * i) = do_ssrarns_ ## E1(Vj->E2(j + ofs * i), \ ++ imm, BIT / 2 - 1); \ ++ temp.E1(j + ofs * (2 * i + 1)) = do_ssrarns_ ## E1(Vd->E2(j + ofs * i), \ ++ imm, BIT / 2 - 1); \ ++ } \ ++ } \ ++ *Vd = temp; \ ++} ++ ++static void do_vssrarni_d_q(VReg *Vd, VReg *Vj, ++ uint64_t imm, int idx, Int128 mask1, Int128 mask2) ++{ ++ Int128 shft_res1, shft_res2, r1, r2; ++ ++ if (imm == 0) { ++ shft_res1 = Vj->Q(idx); ++ shft_res2 = Vd->Q(idx); ++ } else { ++ r1 = int128_and(int128_rshift(Vj->Q(idx), (imm - 1)), int128_one()); ++ r2 = int128_and(int128_rshift(Vd->Q(idx), (imm - 1)), int128_one()); ++ shft_res1 = int128_add(int128_rshift(Vj->Q(idx), imm), r1); ++ shft_res2 = int128_add(int128_rshift(Vd->Q(idx), imm), r2); ++ } ++ if (int128_gt(shft_res1, mask1)) { ++ Vd->D(idx * 2) = int128_getlo(mask1); ++ } else if (int128_lt(shft_res1, int128_neg(mask2))) { ++ Vd->D(idx * 2) = int128_getlo(mask2); ++ } else { ++ Vd->D(idx * 2) = int128_getlo(shft_res1); ++ } ++ ++ if (int128_gt(shft_res2, mask1)) { ++ Vd->D(idx * 2 + 1) = int128_getlo(mask1); ++ } else if (int128_lt(shft_res2, int128_neg(mask2))) { ++ Vd->D(idx * 2 + 1) = int128_getlo(mask2); ++ } else { ++ Vd->D(idx * 2 + 1) = int128_getlo(shft_res2); ++ } ++} ++ ++void HELPER(vssrarni_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) ++{ ++ int i; ++ Int128 mask1, mask2; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ mask1 = int128_sub(int128_lshift(int128_one(), 63), int128_one()); ++ mask2 = int128_lshift(int128_one(), 63); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ do_vssrarni_d_q(Vd, Vj, imm, i, mask1, mask2); ++ } ++} ++ ++VSSRARNI(vssrarni_b_h, 16, B, H) ++VSSRARNI(vssrarni_h_w, 32, H, W) ++VSSRARNI(vssrarni_w_d, 64, W, D) ++ ++#define VSSRLRNUI(NAME, BIT, E1, E2) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ temp.E1(j + ofs * 2 * i) = do_ssrlrnu_ ## E1(Vj->E2(j + ofs * i), \ ++ imm, BIT / 2); \ ++ temp.E1(j + ofs * (2 * i + 1)) = do_ssrlrnu_ ## E1(Vd->E2(j + ofs * i), \ ++ imm, BIT / 2); \ ++ } \ ++ } \ ++ *Vd = temp; \ ++} ++ ++void HELPER(vssrlrni_du_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) ++{ ++ int i; ++ Int128 mask; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ mask = int128_sub(int128_lshift(int128_one(), 64), int128_one()); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ do_vssrlrni_q(Vd, Vj, imm, i, mask); ++ } ++} ++ ++VSSRLRNUI(vssrlrni_bu_h, 16, B, H) ++VSSRLRNUI(vssrlrni_hu_w, 32, H, W) ++VSSRLRNUI(vssrlrni_wu_d, 64, W, D) ++ ++#define VSSRARNUI(NAME, BIT, E1, E2) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ temp.E1(j + ofs * 2 * i) = do_ssrarnu_ ## E1(Vj->E2(j + ofs * i), \ ++ imm, BIT / 2); \ ++ temp.E1(j + ofs * (2 * i + 1)) = do_ssrarnu_ ## E1(Vd->E2(j + ofs * i), \ ++ imm, BIT / 2); \ ++ } \ ++ } \ ++ *Vd = temp; \ ++} ++ ++static void do_vssrarni_du_q(VReg *Vd, VReg *Vj, ++ uint64_t imm, int idx, Int128 mask1, Int128 mask2) ++{ ++ Int128 shft_res1, shft_res2, r1, r2; ++ ++ if (imm == 0) { ++ shft_res1 = Vj->Q(idx); ++ shft_res2 = Vd->Q(idx); ++ } else { ++ r1 = int128_and(int128_rshift(Vj->Q(idx), (imm - 1)), int128_one()); ++ r2 = int128_and(int128_rshift(Vd->Q(idx), (imm - 1)), int128_one()); ++ shft_res1 = int128_add(int128_rshift(Vj->Q(idx), imm), r1); ++ shft_res2 = int128_add(int128_rshift(Vd->Q(idx), imm), r2); ++ } ++ ++ if (int128_lt(Vj->Q(idx), int128_zero())) { ++ shft_res1 = int128_zero(); ++ } ++ if (int128_lt(Vd->Q(idx), int128_zero())) { ++ shft_res2 = int128_zero(); ++ } ++ ++ if (int128_gt(shft_res1, mask1)) { ++ Vd->D(idx * 2) = int128_getlo(mask1); ++ } else if (int128_lt(shft_res1, int128_neg(mask2))) { ++ Vd->D(idx * 2) = int128_getlo(mask2); ++ } else { ++ Vd->D(idx * 2) = int128_getlo(shft_res1); ++ } ++ ++ if (int128_gt(shft_res2, mask1)) { ++ Vd->D(idx * 2 + 1) = int128_getlo(mask1); ++ } else if (int128_lt(shft_res2, int128_neg(mask2))) { ++ Vd->D(idx * 2 + 1) = int128_getlo(mask2); ++ } else { ++ Vd->D(idx * 2 + 1) = int128_getlo(shft_res2); ++ } ++} ++ ++void HELPER(vssrarni_du_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) ++{ ++ int i; ++ Int128 mask1, mask2; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ mask1 = int128_sub(int128_lshift(int128_one(), 64), int128_one()); ++ mask2 = int128_lshift(int128_one(), 64); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ do_vssrarni_du_q(Vd, Vj, imm, i, mask1, mask2); ++ } ++} ++ ++VSSRARNUI(vssrarni_bu_h, 16, B, H) ++VSSRARNUI(vssrarni_hu_w, 32, H, W) ++VSSRARNUI(vssrarni_wu_d, 64, W, D) ++ ++#define DO_2OP(NAME, BIT, E, DO_OP) \ ++void HELPER(NAME)(void *vd, void *vj, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) \ ++ { \ ++ Vd->E(i) = DO_OP(Vj->E(i)); \ ++ } \ ++} ++ ++DO_2OP(vclo_b, 8, UB, DO_CLO_B) ++DO_2OP(vclo_h, 16, UH, DO_CLO_H) ++DO_2OP(vclo_w, 32, UW, DO_CLO_W) ++DO_2OP(vclo_d, 64, UD, DO_CLO_D) ++DO_2OP(vclz_b, 8, UB, DO_CLZ_B) ++DO_2OP(vclz_h, 16, UH, DO_CLZ_H) ++DO_2OP(vclz_w, 32, UW, DO_CLZ_W) ++DO_2OP(vclz_d, 64, UD, DO_CLZ_D) ++ ++#define VPCNT(NAME, BIT, E, FN) \ ++void HELPER(NAME)(void *vd, void *vj, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) \ ++ { \ ++ Vd->E(i) = FN(Vj->E(i)); \ ++ } \ ++} ++ ++VPCNT(vpcnt_b, 8, UB, ctpop8) ++VPCNT(vpcnt_h, 16, UH, ctpop16) ++VPCNT(vpcnt_w, 32, UW, ctpop32) ++VPCNT(vpcnt_d, 64, UD, ctpop64) ++ ++#define DO_BIT(NAME, BIT, E, DO_OP) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E(i) = DO_OP(Vj->E(i), Vk->E(i)%BIT); \ ++ } \ ++} ++ ++DO_BIT(vbitclr_b, 8, UB, DO_BITCLR) ++DO_BIT(vbitclr_h, 16, UH, DO_BITCLR) ++DO_BIT(vbitclr_w, 32, UW, DO_BITCLR) ++DO_BIT(vbitclr_d, 64, UD, DO_BITCLR) ++DO_BIT(vbitset_b, 8, UB, DO_BITSET) ++DO_BIT(vbitset_h, 16, UH, DO_BITSET) ++DO_BIT(vbitset_w, 32, UW, DO_BITSET) ++DO_BIT(vbitset_d, 64, UD, DO_BITSET) ++DO_BIT(vbitrev_b, 8, UB, DO_BITREV) ++DO_BIT(vbitrev_h, 16, UH, DO_BITREV) ++DO_BIT(vbitrev_w, 32, UW, DO_BITREV) ++DO_BIT(vbitrev_d, 64, UD, DO_BITREV) ++ ++#define DO_BITI(NAME, BIT, E, DO_OP) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E(i) = DO_OP(Vj->E(i), imm); \ ++ } \ ++} ++ ++DO_BITI(vbitclri_b, 8, UB, DO_BITCLR) ++DO_BITI(vbitclri_h, 16, UH, DO_BITCLR) ++DO_BITI(vbitclri_w, 32, UW, DO_BITCLR) ++DO_BITI(vbitclri_d, 64, UD, DO_BITCLR) ++DO_BITI(vbitseti_b, 8, UB, DO_BITSET) ++DO_BITI(vbitseti_h, 16, UH, DO_BITSET) ++DO_BITI(vbitseti_w, 32, UW, DO_BITSET) ++DO_BITI(vbitseti_d, 64, UD, DO_BITSET) ++DO_BITI(vbitrevi_b, 8, UB, DO_BITREV) ++DO_BITI(vbitrevi_h, 16, UH, DO_BITREV) ++DO_BITI(vbitrevi_w, 32, UW, DO_BITREV) ++DO_BITI(vbitrevi_d, 64, UD, DO_BITREV) ++ ++#define VFRSTP(NAME, BIT, MASK, E) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i, j, m, ofs; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ m = Vk->E(i * ofs) & MASK; \ ++ for (j = 0; j < ofs; j++) { \ ++ if (Vj->E(j + ofs * i) < 0) { \ ++ break; \ ++ } \ ++ } \ ++ Vd->E(m + i * ofs) = j; \ ++ } \ ++} ++ ++VFRSTP(vfrstp_b, 8, 0xf, B) ++VFRSTP(vfrstp_h, 16, 0x7, H) ++ ++#define VFRSTPI(NAME, BIT, E) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ ++{ \ ++ int i, j, m, ofs; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ m = imm % ofs; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ if (Vj->E(j + ofs * i) < 0) { \ ++ break; \ ++ } \ ++ } \ ++ Vd->E(m + i * ofs) = j; \ ++ } \ ++} ++ ++VFRSTPI(vfrstpi_b, 8, B) ++VFRSTPI(vfrstpi_h, 16, H) ++ ++static void vec_update_fcsr0_mask(CPULoongArchState *env, ++ uintptr_t pc, int mask) ++{ ++ int flags = get_float_exception_flags(&env->fp_status); ++ ++ set_float_exception_flags(0, &env->fp_status); ++ ++ flags &= ~mask; ++ ++ if (flags) { ++ flags = ieee_ex_to_loongarch(flags); ++ UPDATE_FP_CAUSE(env->fcsr0, flags); ++ } ++ ++ if (GET_FP_ENABLES(env->fcsr0) & flags) { ++ do_raise_exception(env, EXCCODE_FPE, pc); ++ } else { ++ UPDATE_FP_FLAGS(env->fcsr0, flags); ++ } ++} ++ ++static void vec_update_fcsr0(CPULoongArchState *env, uintptr_t pc) ++{ ++ vec_update_fcsr0_mask(env, pc, 0); ++} ++ ++static inline void vec_clear_cause(CPULoongArchState *env) ++{ ++ SET_FP_CAUSE(env->fcsr0, 0); ++} ++ ++#define DO_3OP_F(NAME, BIT, E, FN) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, \ ++ CPULoongArchState *env, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ vec_clear_cause(env); \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E(i) = FN(Vj->E(i), Vk->E(i), &env->fp_status); \ ++ vec_update_fcsr0(env, GETPC()); \ ++ } \ ++} ++ ++DO_3OP_F(vfadd_s, 32, UW, float32_add) ++DO_3OP_F(vfadd_d, 64, UD, float64_add) ++DO_3OP_F(vfsub_s, 32, UW, float32_sub) ++DO_3OP_F(vfsub_d, 64, UD, float64_sub) ++DO_3OP_F(vfmul_s, 32, UW, float32_mul) ++DO_3OP_F(vfmul_d, 64, UD, float64_mul) ++DO_3OP_F(vfdiv_s, 32, UW, float32_div) ++DO_3OP_F(vfdiv_d, 64, UD, float64_div) ++DO_3OP_F(vfmax_s, 32, UW, float32_maxnum) ++DO_3OP_F(vfmax_d, 64, UD, float64_maxnum) ++DO_3OP_F(vfmin_s, 32, UW, float32_minnum) ++DO_3OP_F(vfmin_d, 64, UD, float64_minnum) ++DO_3OP_F(vfmaxa_s, 32, UW, float32_maxnummag) ++DO_3OP_F(vfmaxa_d, 64, UD, float64_maxnummag) ++DO_3OP_F(vfmina_s, 32, UW, float32_minnummag) ++DO_3OP_F(vfmina_d, 64, UD, float64_minnummag) ++ ++#define DO_4OP_F(NAME, BIT, E, FN, flags) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, void *va, \ ++ CPULoongArchState *env, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ VReg *Va = (VReg *)va; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ vec_clear_cause(env); \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E(i) = FN(Vj->E(i), Vk->E(i), Va->E(i), flags, &env->fp_status); \ ++ vec_update_fcsr0(env, GETPC()); \ ++ } \ ++} ++ ++DO_4OP_F(vfmadd_s, 32, UW, float32_muladd, 0) ++DO_4OP_F(vfmadd_d, 64, UD, float64_muladd, 0) ++DO_4OP_F(vfmsub_s, 32, UW, float32_muladd, float_muladd_negate_c) ++DO_4OP_F(vfmsub_d, 64, UD, float64_muladd, float_muladd_negate_c) ++DO_4OP_F(vfnmadd_s, 32, UW, float32_muladd, float_muladd_negate_result) ++DO_4OP_F(vfnmadd_d, 64, UD, float64_muladd, float_muladd_negate_result) ++DO_4OP_F(vfnmsub_s, 32, UW, float32_muladd, ++ float_muladd_negate_c | float_muladd_negate_result) ++DO_4OP_F(vfnmsub_d, 64, UD, float64_muladd, ++ float_muladd_negate_c | float_muladd_negate_result) ++ ++#define DO_2OP_F(NAME, BIT, E, FN) \ ++void HELPER(NAME)(void *vd, void *vj, \ ++ CPULoongArchState *env, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ vec_clear_cause(env); \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E(i) = FN(env, Vj->E(i)); \ ++ } \ ++} ++ ++#define FLOGB(BIT, T) \ ++static T do_flogb_## BIT(CPULoongArchState *env, T fj) \ ++{ \ ++ T fp, fd; \ ++ float_status *status = &env->fp_status; \ ++ FloatRoundMode old_mode = get_float_rounding_mode(status); \ ++ \ ++ set_float_rounding_mode(float_round_down, status); \ ++ fp = float ## BIT ##_log2(fj, status); \ ++ fd = float ## BIT ##_round_to_int(fp, status); \ ++ set_float_rounding_mode(old_mode, status); \ ++ vec_update_fcsr0_mask(env, GETPC(), float_flag_inexact); \ ++ return fd; \ ++} ++ ++FLOGB(32, uint32_t) ++FLOGB(64, uint64_t) ++ ++#define FCLASS(NAME, BIT, E, FN) \ ++void HELPER(NAME)(void *vd, void *vj, \ ++ CPULoongArchState *env, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E(i) = FN(env, Vj->E(i)); \ ++ } \ ++} ++ ++FCLASS(vfclass_s, 32, UW, helper_fclass_s) ++FCLASS(vfclass_d, 64, UD, helper_fclass_d) ++ ++#define FSQRT(BIT, T) \ ++static T do_fsqrt_## BIT(CPULoongArchState *env, T fj) \ ++{ \ ++ T fd; \ ++ fd = float ## BIT ##_sqrt(fj, &env->fp_status); \ ++ vec_update_fcsr0(env, GETPC()); \ ++ return fd; \ ++} ++ ++FSQRT(32, uint32_t) ++FSQRT(64, uint64_t) ++ ++#define FRECIP(BIT, T) \ ++static T do_frecip_## BIT(CPULoongArchState *env, T fj) \ ++{ \ ++ T fd; \ ++ fd = float ## BIT ##_div(float ## BIT ##_one, fj, &env->fp_status); \ ++ vec_update_fcsr0(env, GETPC()); \ ++ return fd; \ ++} ++ ++FRECIP(32, uint32_t) ++FRECIP(64, uint64_t) ++ ++#define FRSQRT(BIT, T) \ ++static T do_frsqrt_## BIT(CPULoongArchState *env, T fj) \ ++{ \ ++ T fd, fp; \ ++ fp = float ## BIT ##_sqrt(fj, &env->fp_status); \ ++ fd = float ## BIT ##_div(float ## BIT ##_one, fp, &env->fp_status); \ ++ vec_update_fcsr0(env, GETPC()); \ ++ return fd; \ ++} ++ ++FRSQRT(32, uint32_t) ++FRSQRT(64, uint64_t) ++ ++DO_2OP_F(vflogb_s, 32, UW, do_flogb_32) ++DO_2OP_F(vflogb_d, 64, UD, do_flogb_64) ++DO_2OP_F(vfsqrt_s, 32, UW, do_fsqrt_32) ++DO_2OP_F(vfsqrt_d, 64, UD, do_fsqrt_64) ++DO_2OP_F(vfrecip_s, 32, UW, do_frecip_32) ++DO_2OP_F(vfrecip_d, 64, UD, do_frecip_64) ++DO_2OP_F(vfrsqrt_s, 32, UW, do_frsqrt_32) ++DO_2OP_F(vfrsqrt_d, 64, UD, do_frsqrt_64) ++ ++static uint32_t float16_cvt_float32(uint16_t h, float_status *status) ++{ ++ return float16_to_float32(h, true, status); ++} ++static uint64_t float32_cvt_float64(uint32_t s, float_status *status) ++{ ++ return float32_to_float64(s, status); ++} ++ ++static uint16_t float32_cvt_float16(uint32_t s, float_status *status) ++{ ++ return float32_to_float16(s, true, status); ++} ++static uint32_t float64_cvt_float32(uint64_t d, float_status *status) ++{ ++ return float64_to_float32(d, status); ++} ++ ++void HELPER(vfcvtl_s_h)(void *vd, void *vj, ++ CPULoongArchState *env, uint32_t desc) ++{ ++ int i, j, ofs; ++ VReg temp = {}; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ ofs = LSX_LEN / 32; ++ vec_clear_cause(env); ++ for (i = 0; i < oprsz / 16; i++) { ++ for (j = 0; j < ofs; j++) { ++ temp.UW(j + ofs * i) =float16_cvt_float32(Vj->UH(j + ofs * 2 * i), ++ &env->fp_status); ++ } ++ vec_update_fcsr0(env, GETPC()); ++ } ++ *Vd = temp; ++} ++ ++void HELPER(vfcvtl_d_s)(void *vd, void *vj, ++ CPULoongArchState *env, uint32_t desc) ++{ ++ int i, j, ofs; ++ VReg temp = {}; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ ofs = LSX_LEN / 64; ++ vec_clear_cause(env); ++ for (i = 0; i < oprsz / 16; i++) { ++ for (j = 0; j < ofs; j++) { ++ temp.UD(j + ofs * i) = float32_cvt_float64(Vj->UW(j + ofs * 2 * i), ++ &env->fp_status); ++ } ++ vec_update_fcsr0(env, GETPC()); ++ } ++ *Vd = temp; ++} ++ ++void HELPER(vfcvth_s_h)(void *vd, void *vj, ++ CPULoongArchState *env, uint32_t desc) ++{ ++ int i, j, ofs; ++ VReg temp = {}; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ ofs = LSX_LEN / 32; ++ vec_clear_cause(env); ++ for (i = 0; i < oprsz / 16; i++) { ++ for (j = 0; j < ofs; j++) { ++ temp.UW(j + ofs * i) = float16_cvt_float32(Vj->UH(j + ofs * (2 * i + 1)), ++ &env->fp_status); ++ } ++ vec_update_fcsr0(env, GETPC()); ++ } ++ *Vd = temp; ++} ++ ++void HELPER(vfcvth_d_s)(void *vd, void *vj, ++ CPULoongArchState *env, uint32_t desc) ++{ ++ int i, j, ofs; ++ VReg temp = {}; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ ofs = LSX_LEN / 64; ++ vec_clear_cause(env); ++ for (i = 0; i < oprsz / 16; i++) { ++ for (j = 0; j < ofs; j++) { ++ temp.UD(j + ofs * i) = float32_cvt_float64(Vj->UW(j + ofs * (2 * i + 1)), ++ &env->fp_status); ++ } ++ vec_update_fcsr0(env, GETPC()); ++ } ++ *Vd = temp; ++} ++ ++void HELPER(vfcvt_h_s)(void *vd, void *vj, void *vk, ++ CPULoongArchState *env, uint32_t desc) ++{ ++ int i, j, ofs; ++ VReg temp = {}; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ VReg *Vk = (VReg *)vk; ++ int oprsz = simd_oprsz(desc); ++ ++ ofs = LSX_LEN / 32; ++ vec_clear_cause(env); ++ for(i = 0; i < oprsz / 16; i++) { ++ for (j = 0; j < ofs; j++) { ++ temp.UH(j + ofs * (2 * i + 1)) = float32_cvt_float16(Vj->UW(j + ofs * i), ++ &env->fp_status); ++ temp.UH(j + ofs * 2 * i) = float32_cvt_float16(Vk->UW(j + ofs * i), ++ &env->fp_status); ++ } ++ vec_update_fcsr0(env, GETPC()); ++ } ++ *Vd = temp; ++} ++ ++void HELPER(vfcvt_s_d)(void *vd, void *vj, void *vk, ++ CPULoongArchState *env, uint32_t desc) ++{ ++ int i, j, ofs; ++ VReg temp = {}; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ VReg *Vk = (VReg *)vk; ++ int oprsz = simd_oprsz(desc); ++ ++ ofs = LSX_LEN / 64; ++ vec_clear_cause(env); ++ for(i = 0; i < oprsz / 16; i++) { ++ for (j = 0; j < ofs; j++) { ++ temp.UW(j + ofs * (2 * i + 1)) = float64_cvt_float32(Vj->UD(j + ofs * i), ++ &env->fp_status); ++ temp.UW(j + ofs * 2 * i) = float64_cvt_float32(Vk->UD(j + ofs * i), ++ &env->fp_status); ++ } ++ vec_update_fcsr0(env, GETPC()); ++ } ++ *Vd = temp; ++} ++ ++void HELPER(vfrint_s)(void *vd, void *vj, ++ CPULoongArchState *env, uint32_t desc) ++{ ++ int i; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ vec_clear_cause(env); ++ for (i = 0; i < oprsz / 4; i++) { ++ Vd->W(i) = float32_round_to_int(Vj->UW(i), &env->fp_status); ++ vec_update_fcsr0(env, GETPC()); ++ } ++} ++ ++void HELPER(vfrint_d)(void *vd, void *vj, ++ CPULoongArchState *env, uint32_t desc) ++{ ++ int i; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ vec_clear_cause(env); ++ for (i = 0; i < oprsz / 8; i++) { ++ Vd->D(i) = float64_round_to_int(Vj->UD(i), &env->fp_status); ++ vec_update_fcsr0(env, GETPC()); ++ } ++} ++ ++#define FCVT_2OP(NAME, BIT, E, MODE) \ ++void HELPER(NAME)(void *vd, void *vj, \ ++ CPULoongArchState *env, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ vec_clear_cause(env); \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); \ ++ set_float_rounding_mode(MODE, &env->fp_status); \ ++ Vd->E(i) = float## BIT ## _round_to_int(Vj->E(i), &env->fp_status); \ ++ set_float_rounding_mode(old_mode, &env->fp_status); \ ++ vec_update_fcsr0(env, GETPC()); \ ++ } \ ++} ++ ++FCVT_2OP(vfrintrne_s, 32, UW, float_round_nearest_even) ++FCVT_2OP(vfrintrne_d, 64, UD, float_round_nearest_even) ++FCVT_2OP(vfrintrz_s, 32, UW, float_round_to_zero) ++FCVT_2OP(vfrintrz_d, 64, UD, float_round_to_zero) ++FCVT_2OP(vfrintrp_s, 32, UW, float_round_up) ++FCVT_2OP(vfrintrp_d, 64, UD, float_round_up) ++FCVT_2OP(vfrintrm_s, 32, UW, float_round_down) ++FCVT_2OP(vfrintrm_d, 64, UD, float_round_down) ++ ++#define FTINT(NAME, FMT1, FMT2, T1, T2, MODE) \ ++static T2 do_ftint ## NAME(CPULoongArchState *env, T1 fj) \ ++{ \ ++ T2 fd; \ ++ FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); \ ++ \ ++ set_float_rounding_mode(MODE, &env->fp_status); \ ++ fd = do_## FMT1 ##_to_## FMT2(env, fj); \ ++ set_float_rounding_mode(old_mode, &env->fp_status); \ ++ return fd; \ ++} ++ ++#define DO_FTINT(FMT1, FMT2, T1, T2) \ ++static T2 do_## FMT1 ##_to_## FMT2(CPULoongArchState *env, T1 fj) \ ++{ \ ++ T2 fd; \ ++ \ ++ fd = FMT1 ##_to_## FMT2(fj, &env->fp_status); \ ++ if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { \ ++ if (FMT1 ##_is_any_nan(fj)) { \ ++ fd = 0; \ ++ } \ ++ } \ ++ vec_update_fcsr0(env, GETPC()); \ ++ return fd; \ ++} ++ ++DO_FTINT(float32, int32, uint32_t, uint32_t) ++DO_FTINT(float64, int64, uint64_t, uint64_t) ++DO_FTINT(float32, uint32, uint32_t, uint32_t) ++DO_FTINT(float64, uint64, uint64_t, uint64_t) ++DO_FTINT(float64, int32, uint64_t, uint32_t) ++DO_FTINT(float32, int64, uint32_t, uint64_t) ++ ++FTINT(rne_w_s, float32, int32, uint32_t, uint32_t, float_round_nearest_even) ++FTINT(rne_l_d, float64, int64, uint64_t, uint64_t, float_round_nearest_even) ++FTINT(rp_w_s, float32, int32, uint32_t, uint32_t, float_round_up) ++FTINT(rp_l_d, float64, int64, uint64_t, uint64_t, float_round_up) ++FTINT(rz_w_s, float32, int32, uint32_t, uint32_t, float_round_to_zero) ++FTINT(rz_l_d, float64, int64, uint64_t, uint64_t, float_round_to_zero) ++FTINT(rm_w_s, float32, int32, uint32_t, uint32_t, float_round_down) ++FTINT(rm_l_d, float64, int64, uint64_t, uint64_t, float_round_down) ++ ++DO_2OP_F(vftintrne_w_s, 32, UW, do_ftintrne_w_s) ++DO_2OP_F(vftintrne_l_d, 64, UD, do_ftintrne_l_d) ++DO_2OP_F(vftintrp_w_s, 32, UW, do_ftintrp_w_s) ++DO_2OP_F(vftintrp_l_d, 64, UD, do_ftintrp_l_d) ++DO_2OP_F(vftintrz_w_s, 32, UW, do_ftintrz_w_s) ++DO_2OP_F(vftintrz_l_d, 64, UD, do_ftintrz_l_d) ++DO_2OP_F(vftintrm_w_s, 32, UW, do_ftintrm_w_s) ++DO_2OP_F(vftintrm_l_d, 64, UD, do_ftintrm_l_d) ++DO_2OP_F(vftint_w_s, 32, UW, do_float32_to_int32) ++DO_2OP_F(vftint_l_d, 64, UD, do_float64_to_int64) ++ ++FTINT(rz_wu_s, float32, uint32, uint32_t, uint32_t, float_round_to_zero) ++FTINT(rz_lu_d, float64, uint64, uint64_t, uint64_t, float_round_to_zero) ++ ++DO_2OP_F(vftintrz_wu_s, 32, UW, do_ftintrz_wu_s) ++DO_2OP_F(vftintrz_lu_d, 64, UD, do_ftintrz_lu_d) ++DO_2OP_F(vftint_wu_s, 32, UW, do_float32_to_uint32) ++DO_2OP_F(vftint_lu_d, 64, UD, do_float64_to_uint64) ++ ++FTINT(rm_w_d, float64, int32, uint64_t, uint32_t, float_round_down) ++FTINT(rp_w_d, float64, int32, uint64_t, uint32_t, float_round_up) ++FTINT(rz_w_d, float64, int32, uint64_t, uint32_t, float_round_to_zero) ++FTINT(rne_w_d, float64, int32, uint64_t, uint32_t, float_round_nearest_even) ++ ++#define FTINT_W_D(NAME, FN) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, \ ++ CPULoongArchState *env, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / 64; \ ++ vec_clear_cause(env); \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ temp.W(j + ofs * (2 * i + 1)) = FN(env, Vj->UD(j + ofs * i)); \ ++ temp.W(j + ofs * 2 * i) = FN(env, Vk->UD(j + ofs * i)); \ ++ } \ ++ } \ ++ *Vd = temp; \ ++} ++ ++FTINT_W_D(vftint_w_d, do_float64_to_int32) ++FTINT_W_D(vftintrm_w_d, do_ftintrm_w_d) ++FTINT_W_D(vftintrp_w_d, do_ftintrp_w_d) ++FTINT_W_D(vftintrz_w_d, do_ftintrz_w_d) ++FTINT_W_D(vftintrne_w_d, do_ftintrne_w_d) ++ ++FTINT(rml_l_s, float32, int64, uint32_t, uint64_t, float_round_down) ++FTINT(rpl_l_s, float32, int64, uint32_t, uint64_t, float_round_up) ++FTINT(rzl_l_s, float32, int64, uint32_t, uint64_t, float_round_to_zero) ++FTINT(rnel_l_s, float32, int64, uint32_t, uint64_t, float_round_nearest_even) ++FTINT(rmh_l_s, float32, int64, uint32_t, uint64_t, float_round_down) ++FTINT(rph_l_s, float32, int64, uint32_t, uint64_t, float_round_up) ++FTINT(rzh_l_s, float32, int64, uint32_t, uint64_t, float_round_to_zero) ++FTINT(rneh_l_s, float32, int64, uint32_t, uint64_t, float_round_nearest_even) ++ ++#define FTINTL_L_S(NAME, FN) \ ++void HELPER(NAME)(void *vd, void *vj, \ ++ CPULoongArchState *env, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg temp; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / 64; \ ++ vec_clear_cause(env); \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ temp.D(j + ofs * i) = FN(env, Vj->UW(j + ofs * 2 * i)); \ ++ } \ ++ } \ ++ *Vd = temp; \ ++} ++ ++FTINTL_L_S(vftintl_l_s, do_float32_to_int64) ++FTINTL_L_S(vftintrml_l_s, do_ftintrml_l_s) ++FTINTL_L_S(vftintrpl_l_s, do_ftintrpl_l_s) ++FTINTL_L_S(vftintrzl_l_s, do_ftintrzl_l_s) ++FTINTL_L_S(vftintrnel_l_s, do_ftintrnel_l_s) ++ ++#define FTINTH_L_S(NAME, FN) \ ++void HELPER(NAME)(void *vd, void *vj, \ ++ CPULoongArchState *env, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / 64; \ ++ vec_clear_cause(env); \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ temp.D(j + ofs * i) = FN(env, Vj->UW(j + ofs * (2 * i + 1))); \ ++ } \ ++ } \ ++ *Vd = temp; \ ++} ++ ++FTINTH_L_S(vftinth_l_s, do_float32_to_int64) ++FTINTH_L_S(vftintrmh_l_s, do_ftintrmh_l_s) ++FTINTH_L_S(vftintrph_l_s, do_ftintrph_l_s) ++FTINTH_L_S(vftintrzh_l_s, do_ftintrzh_l_s) ++FTINTH_L_S(vftintrneh_l_s, do_ftintrneh_l_s) ++ ++#define FFINT(NAME, FMT1, FMT2, T1, T2) \ ++static T2 do_ffint_ ## NAME(CPULoongArchState *env, T1 fj) \ ++{ \ ++ T2 fd; \ ++ \ ++ fd = FMT1 ##_to_## FMT2(fj, &env->fp_status); \ ++ vec_update_fcsr0(env, GETPC()); \ ++ return fd; \ ++} ++ ++FFINT(s_w, int32, float32, int32_t, uint32_t) ++FFINT(d_l, int64, float64, int64_t, uint64_t) ++FFINT(s_wu, uint32, float32, uint32_t, uint32_t) ++FFINT(d_lu, uint64, float64, uint64_t, uint64_t) ++ ++DO_2OP_F(vffint_s_w, 32, W, do_ffint_s_w) ++DO_2OP_F(vffint_d_l, 64, D, do_ffint_d_l) ++DO_2OP_F(vffint_s_wu, 32, UW, do_ffint_s_wu) ++DO_2OP_F(vffint_d_lu, 64, UD, do_ffint_d_lu) ++ ++void HELPER(vffintl_d_w)(void *vd, void *vj, ++ CPULoongArchState *env, uint32_t desc) ++{ ++ int i, j, ofs; ++ VReg temp = {}; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ ofs = LSX_LEN / 64; ++ vec_clear_cause(env); ++ for (i = 0; i < oprsz / 16; i++) { ++ for (j = 0; j < ofs; j++) { ++ temp.D(j + ofs * i) = int32_to_float64(Vj->W(j + ofs * 2 * i), ++ &env->fp_status); ++ } ++ vec_update_fcsr0(env, GETPC()); ++ } ++ *Vd = temp; ++} ++ ++void HELPER(vffinth_d_w)(void *vd, void *vj, ++ CPULoongArchState *env, uint32_t desc) ++{ ++ int i, j, ofs; ++ VReg temp = {}; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ ofs = LSX_LEN / 64; ++ vec_clear_cause(env); ++ for (i = 0; i < oprsz /16; i++) { ++ for (j = 0; j < ofs; j++) { ++ temp.D(j + ofs * i) = int32_to_float64(Vj->W(j + ofs * (2 * i + 1)), ++ &env->fp_status); ++ } ++ vec_update_fcsr0(env, GETPC()); ++ } ++ *Vd = temp; ++} ++ ++void HELPER(vffint_s_l)(void *vd, void *vj, void *vk, ++ CPULoongArchState *env, uint32_t desc) ++{ ++ int i, j, ofs; ++ VReg temp = {}; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ VReg *Vk = (VReg *)vk; ++ int oprsz = simd_oprsz(desc); ++ ++ ofs = LSX_LEN / 64; ++ vec_clear_cause(env); ++ for (i = 0; i < oprsz / 16; i++) { ++ for (j = 0; j < ofs; j++) { ++ temp.W(j + ofs * (2 * i + 1)) = int64_to_float32(Vj->D(j + ofs * i), ++ &env->fp_status); ++ temp.W(j + ofs * 2 * i) = int64_to_float32(Vk->D(j + ofs * i), ++ &env->fp_status); ++ } ++ vec_update_fcsr0(env, GETPC()); ++ } ++ *Vd = temp; ++} ++ ++#define VCMPI(NAME, BIT, E, DO_OP) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ typedef __typeof(Vd->E(0)) TD; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E(i) = DO_OP(Vj->E(i), (TD)imm); \ ++ } \ ++} ++ ++VCMPI(vseqi_b, 8, B, VSEQ) ++VCMPI(vseqi_h, 16, H, VSEQ) ++VCMPI(vseqi_w, 32, W, VSEQ) ++VCMPI(vseqi_d, 64, D, VSEQ) ++VCMPI(vslei_b, 8, B, VSLE) ++VCMPI(vslei_h, 16, H, VSLE) ++VCMPI(vslei_w, 32, W, VSLE) ++VCMPI(vslei_d, 64, D, VSLE) ++VCMPI(vslei_bu, 8, UB, VSLE) ++VCMPI(vslei_hu, 16, UH, VSLE) ++VCMPI(vslei_wu, 32, UW, VSLE) ++VCMPI(vslei_du, 64, UD, VSLE) ++VCMPI(vslti_b, 8, B, VSLT) ++VCMPI(vslti_h, 16, H, VSLT) ++VCMPI(vslti_w, 32, W, VSLT) ++VCMPI(vslti_d, 64, D, VSLT) ++VCMPI(vslti_bu, 8, UB, VSLT) ++VCMPI(vslti_hu, 16, UH, VSLT) ++VCMPI(vslti_wu, 32, UW, VSLT) ++VCMPI(vslti_du, 64, UD, VSLT) ++ ++static uint64_t vfcmp_common(CPULoongArchState *env, ++ FloatRelation cmp, uint32_t flags) ++{ ++ uint64_t ret = 0; ++ ++ switch (cmp) { ++ case float_relation_less: ++ ret = (flags & FCMP_LT); ++ break; ++ case float_relation_equal: ++ ret = (flags & FCMP_EQ); ++ break; ++ case float_relation_greater: ++ ret = (flags & FCMP_GT); ++ break; ++ case float_relation_unordered: ++ ret = (flags & FCMP_UN); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ ++ if (ret) { ++ ret = -1; ++ } ++ ++ return ret; ++} ++ ++#define VFCMP(NAME, BIT, E, FN) \ ++void HELPER(NAME)(CPULoongArchState *env, uint32_t oprsz, \ ++ uint32_t vd, uint32_t vj, uint32_t vk, uint32_t flags) \ ++{ \ ++ int i; \ ++ VReg t; \ ++ VReg *Vd = &(env->fpr[vd].vreg); \ ++ VReg *Vj = &(env->fpr[vj].vreg); \ ++ VReg *Vk = &(env->fpr[vk].vreg); \ ++ \ ++ vec_clear_cause(env); \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ FloatRelation cmp; \ ++ cmp = FN(Vj->E(i), Vk->E(i), &env->fp_status); \ ++ t.E(i) = vfcmp_common(env, cmp, flags); \ ++ vec_update_fcsr0(env, GETPC()); \ ++ } \ ++ *Vd = t; \ ++} ++ ++VFCMP(vfcmp_c_s, 32, UW, float32_compare_quiet) ++VFCMP(vfcmp_s_s, 32, UW, float32_compare) ++VFCMP(vfcmp_c_d, 64, UD, float64_compare_quiet) ++VFCMP(vfcmp_s_d, 64, UD, float64_compare) ++ ++void HELPER(vbitseli_b)(void *vd, void *vj, uint64_t imm, uint32_t desc) ++{ ++ int i; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ ++ for (i = 0; i < simd_oprsz(desc); i++) { ++ Vd->B(i) = (~Vd->B(i) & Vj->B(i)) | (Vd->B(i) & imm); ++ } ++} ++ ++/* Copy from target/arm/tcg/sve_helper.c */ ++static inline bool do_match2(uint64_t n, uint64_t m0, uint64_t m1, int esz) ++{ ++ int bits = 8 << esz; ++ uint64_t ones = dup_const(esz, 1); ++ uint64_t signs = ones << (bits - 1); ++ uint64_t cmp0, cmp1; ++ ++ cmp1 = dup_const(esz, n); ++ cmp0 = cmp1 ^ m0; ++ cmp1 = cmp1 ^ m1; ++ cmp0 = (cmp0 - ones) & ~cmp0; ++ cmp1 = (cmp1 - ones) & ~cmp1; ++ return (cmp0 | cmp1) & signs; ++} ++ ++#define SETANYEQZ(NAME, MO) \ ++void HELPER(NAME)(CPULoongArchState *env, \ ++ uint32_t oprsz, uint32_t cd, uint32_t vj) \ ++{ \ ++ VReg *Vj = &(env->fpr[vj].vreg); \ ++ \ ++ env->cf[cd & 0x7] = do_match2(0, Vj->D(0), Vj->D(1), MO); \ ++ if (oprsz == 32) { \ ++ env->cf[cd & 0x7] = env->cf[cd & 0x7] || \ ++ do_match2(0, Vj->D(2), Vj->D(3), MO); \ ++ } \ ++} ++ ++SETANYEQZ(vsetanyeqz_b, MO_8) ++SETANYEQZ(vsetanyeqz_h, MO_16) ++SETANYEQZ(vsetanyeqz_w, MO_32) ++SETANYEQZ(vsetanyeqz_d, MO_64) ++ ++#define SETALLNEZ(NAME, MO) \ ++void HELPER(NAME)(CPULoongArchState *env, \ ++ uint32_t oprsz, uint32_t cd, uint32_t vj) \ ++{ \ ++ VReg *Vj = &(env->fpr[vj].vreg); \ ++ \ ++ env->cf[cd & 0x7]= !do_match2(0, Vj->D(0), Vj->D(1), MO); \ ++ if (oprsz == 32) { \ ++ env->cf[cd & 0x7] = env->cf[cd & 0x7] && \ ++ !do_match2(0, Vj->D(2), Vj->D(3), MO); \ ++ } \ ++} ++ ++SETALLNEZ(vsetallnez_b, MO_8) ++SETALLNEZ(vsetallnez_h, MO_16) ++SETALLNEZ(vsetallnez_w, MO_32) ++SETALLNEZ(vsetallnez_d, MO_64) ++ ++#define XVINSVE0(NAME, E, MASK) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ ++{ \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ Vd->E(imm & MASK) = Vj->E(0); \ ++} ++ ++XVINSVE0(xvinsve0_w, W, 0x7) ++XVINSVE0(xvinsve0_d, D, 0x3) ++ ++#define XVPICKVE(NAME, E, BIT, MASK) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ Vd->E(0) = Vj->E(imm & MASK); \ ++ for (i = 1; i < oprsz / (BIT / 8); i++) { \ ++ Vd->E(i) = 0; \ ++ } \ ++} ++ ++XVPICKVE(xvpickve_w, W, 32, 0x7) ++XVPICKVE(xvpickve_d, D, 64, 0x3) ++ ++#define VPACKEV(NAME, BIT, E) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ temp.E(2 * i + 1) = Vj->E(2 * i); \ ++ temp.E(2 *i) = Vk->E(2 * i); \ ++ } \ ++ *Vd = temp; \ ++} ++ ++VPACKEV(vpackev_b, 16, B) ++VPACKEV(vpackev_h, 32, H) ++VPACKEV(vpackev_w, 64, W) ++VPACKEV(vpackev_d, 128, D) ++ ++#define VPACKOD(NAME, BIT, E) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ temp.E(2 * i + 1) = Vj->E(2 * i + 1); \ ++ temp.E(2 * i) = Vk->E(2 * i + 1); \ ++ } \ ++ *Vd = temp; \ ++} ++ ++VPACKOD(vpackod_b, 16, B) ++VPACKOD(vpackod_h, 32, H) ++VPACKOD(vpackod_w, 64, W) ++VPACKOD(vpackod_d, 128, D) ++ ++#define VPICKEV(NAME, BIT, E) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ temp.E(j + ofs * (2 * i + 1)) = Vj->E(2 * (j + ofs * i)); \ ++ temp.E(j + ofs * 2 * i) = Vk->E(2 * (j + ofs * i)); \ ++ } \ ++ } \ ++ *Vd = temp; \ ++} ++ ++VPICKEV(vpickev_b, 16, B) ++VPICKEV(vpickev_h, 32, H) ++VPICKEV(vpickev_w, 64, W) ++VPICKEV(vpickev_d, 128, D) ++ ++#define VPICKOD(NAME, BIT, E) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ temp.E(j + ofs * (2 * i + 1)) = Vj->E(2 * (j + ofs * i) + 1); \ ++ temp.E(j + ofs * 2 * i) = Vk->E(2 * (j + ofs * i) + 1); \ ++ } \ ++ } \ ++ *Vd = temp; \ ++} ++ ++VPICKOD(vpickod_b, 16, B) ++VPICKOD(vpickod_h, 32, H) ++VPICKOD(vpickod_w, 64, W) ++VPICKOD(vpickod_d, 128, D) ++ ++#define VILVL(NAME, BIT, E) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ temp.E(2 * (j + ofs * i) + 1) = Vj->E(j + ofs * 2 * i); \ ++ temp.E(2 * (j + ofs * i)) = Vk->E(j + ofs * 2 * i); \ ++ } \ ++ } \ ++ *Vd = temp; \ ++} ++ ++VILVL(vilvl_b, 16, B) ++VILVL(vilvl_h, 32, H) ++VILVL(vilvl_w, 64, W) ++VILVL(vilvl_d, 128, D) ++ ++#define VILVH(NAME, BIT, E) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i, j, ofs; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ ofs = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ for (j = 0; j < ofs; j++) { \ ++ temp.E(2 * (j + ofs * i) + 1) = Vj->E(j + ofs * (2 * i + 1)); \ ++ temp.E(2 * (j + ofs * i)) = Vk->E(j + ofs * (2 * i + 1)); \ ++ } \ ++ } \ ++ *Vd = temp; \ ++} ++ ++VILVH(vilvh_b, 16, B) ++VILVH(vilvh_h, 32, H) ++VILVH(vilvh_w, 64, W) ++VILVH(vilvh_d, 128, D) ++ ++void HELPER(vshuf_b)(void *vd, void *vj, void *vk, void *va, uint32_t desc) ++{ ++ int i, j, m; ++ VReg temp = {}; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ VReg *Vk = (VReg *)vk; ++ VReg *Va = (VReg *)va; ++ int oprsz = simd_oprsz(desc); ++ ++ m = LSX_LEN / 8; ++ for (i = 0; i < (oprsz / 16) * m; i++) { ++ j = i < m ? 0 : 1; ++ uint64_t k = (uint8_t)Va->B(i) % (2 * m); ++ temp.B(i) = k < m ? Vk->B(k + j * m): Vj->B(k + (j - 1) * m); ++ } ++ *Vd = temp; ++} ++ ++#define VSHUF(NAME, BIT, E) \ ++void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ ++{ \ ++ int i, j, m; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ VReg *Vk = (VReg *)vk; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ m = LSX_LEN / BIT; \ ++ for (i = 0; i < (oprsz / 16) * m; i++) { \ ++ j = i < m ? 0 : 1; \ ++ uint64_t k = ((uint8_t)Vd->E(i)) % (2 * m); \ ++ temp.E(i) = k < m ? Vk->E(k + j * m) : Vj->E(k + (j - 1) * m); \ ++ } \ ++ *Vd = temp; \ ++} ++ ++VSHUF(vshuf_h, 16, H) ++VSHUF(vshuf_w, 32, W) ++VSHUF(vshuf_d, 64, D) ++ ++#define VSHUF4I(NAME, BIT, E) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ ++{ \ ++ int i, j, max; \ ++ VReg temp = {}; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ max = LSX_LEN / BIT; \ ++ for (i = 0; i < oprsz / (BIT / 8); i++) { \ ++ j = i < max ? 1 : 2; \ ++ temp.E(i) = Vj->E(SHF_POS(i - ((j -1)* max), imm) + (j - 1) * max); \ ++ } \ ++ *Vd = temp; \ ++} ++ ++VSHUF4I(vshuf4i_b, 8, B) ++VSHUF4I(vshuf4i_h, 16, H) ++VSHUF4I(vshuf4i_w, 32, W) ++ ++void HELPER(vshuf4i_d)(void *vd, void *vj, uint64_t imm, uint32_t desc) ++{ ++ int i; ++ VReg temp = {}; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ temp.D(2 * i) = (imm & 2 ? Vj : Vd)->D((imm & 1) + 2 * i); ++ temp.D(2 * i + 1) = (imm & 8 ? Vj : Vd)->D(((imm >> 2) & 1) + 2 * i); ++ } ++ *Vd = temp; ++} ++ ++void HELPER(vperm_w)(void *vd, void *vj, void *vk, uint32_t desc) ++{ ++ int i, m; ++ VReg temp = {}; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ VReg *Vk = (VReg *)vk; ++ ++ m = LASX_LEN / 32; ++ for (i = 0; i < m ; i++) { ++ uint64_t k = (uint8_t)Vk->W(i) % 8; ++ temp.W(i) = Vj->W(k); ++ } ++ *Vd = temp; ++} ++ ++void HELPER(vpermi_w)(void *vd, void *vj, uint64_t imm, uint32_t desc) ++{ ++ int i; ++ VReg temp = {}; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ int oprsz = simd_oprsz(desc); ++ ++ for (i = 0; i < oprsz / 16; i++) { ++ temp.W(4 * i) = Vj->W((imm & 0x3) + 4 * i); ++ temp.W(4 * i + 1) = Vj->W(((imm >> 2) & 0x3) + 4 * i); ++ temp.W(4 * i + 2) = Vd->W(((imm >> 4) & 0x3) + 4 * i); ++ temp.W(4 * i + 3) = Vd->W(((imm >> 6) & 0x3) + 4 * i); ++ } ++ *Vd = temp; ++} ++ ++void HELPER(vpermi_d)(void *vd, void *vj, uint64_t imm, uint32_t desc) ++{ ++ VReg temp = {}; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ ++ temp.D(0) = Vj->D(imm & 0x3); ++ temp.D(1) = Vj->D((imm >> 2) & 0x3); ++ temp.D(2) = Vj->D((imm >> 4) & 0x3); ++ temp.D(3) = Vj->D((imm >> 6) & 0x3); ++ *Vd = temp; ++} ++ ++void HELPER(vpermi_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) ++{ ++ int i; ++ VReg temp; ++ VReg *Vd = (VReg *)vd; ++ VReg *Vj = (VReg *)vj; ++ ++ for (i = 0; i < 2; i++, imm >>= 4) { ++ temp.Q(i) = (imm & 2 ? Vd: Vj)->Q(imm & 1); ++ } ++ *Vd = temp; ++} ++ ++#define VEXTRINS(NAME, BIT, E, MASK) \ ++void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ ++{ \ ++ int i, ins, extr, max; \ ++ VReg *Vd = (VReg *)vd; \ ++ VReg *Vj = (VReg *)vj; \ ++ int oprsz = simd_oprsz(desc); \ ++ \ ++ max = LSX_LEN / BIT; \ ++ ins = (imm >> 4) & MASK; \ ++ extr = imm & MASK; \ ++ for (i = 0; i < oprsz / 16; i++) { \ ++ Vd->E(ins + i * max) = Vj->E(extr + i * max); \ ++ } \ ++} ++ ++VEXTRINS(vextrins_b, 8, B, 0xf) ++VEXTRINS(vextrins_h, 16, H, 0x7) ++VEXTRINS(vextrins_w, 32, W, 0x3) ++VEXTRINS(vextrins_d, 64, D, 0x1) +diff --git a/target/loongarch/tlb_helper.c b/target/loongarch/tlb_helper.c +deleted file mode 100644 +index 449043c..0000000 +--- a/target/loongarch/tlb_helper.c ++++ /dev/null +@@ -1,803 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * QEMU LoongArch TLB helpers +- * +- * Copyright (c) 2021 Loongson Technology Corporation Limited +- * +- */ +- +-#include "qemu/osdep.h" +-#include "qemu/guest-random.h" +- +-#include "cpu.h" +-#include "internals.h" +-#include "exec/helper-proto.h" +-#include "exec/exec-all.h" +-#include "exec/cpu_ldst.h" +-#include "exec/log.h" +-#include "cpu-csr.h" +- +-enum { +- TLBRET_MATCH = 0, +- TLBRET_BADADDR = 1, +- TLBRET_NOMATCH = 2, +- TLBRET_INVALID = 3, +- TLBRET_DIRTY = 4, +- TLBRET_RI = 5, +- TLBRET_XI = 6, +- TLBRET_PE = 7, +-}; +- +-static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, +- int *prot, target_ulong address, +- int access_type, int index, int mmu_idx) +-{ +- LoongArchTLB *tlb = &env->tlb[index]; +- uint64_t plv = mmu_idx; +- uint64_t tlb_entry, tlb_ppn; +- uint8_t tlb_ps, n, tlb_v, tlb_d, tlb_plv, tlb_nx, tlb_nr, tlb_rplv; +- +- if (index >= LOONGARCH_STLB) { +- tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); +- } else { +- tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); +- } +- n = (address >> tlb_ps) & 0x1;/* Odd or even */ +- +- tlb_entry = n ? tlb->tlb_entry1 : tlb->tlb_entry0; +- tlb_v = FIELD_EX64(tlb_entry, TLBENTRY, V); +- tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D); +- tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV); +- if (is_la64(env)) { +- tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN); +- tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY_64, NX); +- tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY_64, NR); +- tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY_64, RPLV); +- } else { +- tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_32, PPN); +- tlb_nx = 0; +- tlb_nr = 0; +- tlb_rplv = 0; +- } +- +- /* Remove sw bit between bit12 -- bit PS*/ +- tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) -1)); +- +- /* Check access rights */ +- if (!tlb_v) { +- return TLBRET_INVALID; +- } +- +- if (access_type == MMU_INST_FETCH && tlb_nx) { +- return TLBRET_XI; +- } +- +- if (access_type == MMU_DATA_LOAD && tlb_nr) { +- return TLBRET_RI; +- } +- +- if (((tlb_rplv == 0) && (plv > tlb_plv)) || +- ((tlb_rplv == 1) && (plv != tlb_plv))) { +- return TLBRET_PE; +- } +- +- if ((access_type == MMU_DATA_STORE) && !tlb_d) { +- return TLBRET_DIRTY; +- } +- +- *physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) | +- (address & MAKE_64BIT_MASK(0, tlb_ps)); +- *prot = PAGE_READ; +- if (tlb_d) { +- *prot |= PAGE_WRITE; +- } +- if (!tlb_nx) { +- *prot |= PAGE_EXEC; +- } +- return TLBRET_MATCH; +-} +- +-/* +- * One tlb entry holds an adjacent odd/even pair, the vpn is the +- * content of the virtual page number divided by 2. So the +- * compare vpn is bit[47:15] for 16KiB page. while the vppn +- * field in tlb entry contains bit[47:13], so need adjust. +- * virt_vpn = vaddr[47:13] +- */ +-static bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, +- int *index) +-{ +- LoongArchTLB *tlb; +- uint16_t csr_asid, tlb_asid, stlb_idx; +- uint8_t tlb_e, tlb_ps, tlb_g, stlb_ps; +- int i, compare_shift; +- uint64_t vpn, tlb_vppn; +- +- csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); +- stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); +- vpn = (vaddr & TARGET_VIRT_MASK) >> (stlb_ps + 1); +- stlb_idx = vpn & 0xff; /* VA[25:15] <==> TLBIDX.index for 16KiB Page */ +- compare_shift = stlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; +- +- /* Search STLB */ +- for (i = 0; i < 8; ++i) { +- tlb = &env->tlb[i * 256 + stlb_idx]; +- tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); +- if (tlb_e) { +- tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); +- tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); +- tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); +- +- if ((tlb_g == 1 || tlb_asid == csr_asid) && +- (vpn == (tlb_vppn >> compare_shift))) { +- *index = i * 256 + stlb_idx; +- return true; +- } +- } +- } +- +- /* Search MTLB */ +- for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; ++i) { +- tlb = &env->tlb[i]; +- tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); +- if (tlb_e) { +- tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); +- tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); +- tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); +- tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); +- compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; +- vpn = (vaddr & TARGET_VIRT_MASK) >> (tlb_ps + 1); +- if ((tlb_g == 1 || tlb_asid == csr_asid) && +- (vpn == (tlb_vppn >> compare_shift))) { +- *index = i; +- return true; +- } +- } +- } +- return false; +-} +- +-static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, +- int *prot, target_ulong address, +- MMUAccessType access_type, int mmu_idx) +-{ +- int index, match; +- +- match = loongarch_tlb_search(env, address, &index); +- if (match) { +- return loongarch_map_tlb_entry(env, physical, prot, +- address, access_type, index, mmu_idx); +- } +- +- return TLBRET_NOMATCH; +-} +- +-static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, +- target_ulong dmw) +-{ +- if (is_la64(env)) { +- return va & TARGET_VIRT_MASK; +- } else { +- uint32_t pseg = FIELD_EX32(dmw, CSR_DMW_32, PSEG); +- return (va & MAKE_64BIT_MASK(0, R_CSR_DMW_32_VSEG_SHIFT)) | \ +- (pseg << R_CSR_DMW_32_VSEG_SHIFT); +- } +-} +- +-static int get_physical_address(CPULoongArchState *env, hwaddr *physical, +- int *prot, target_ulong address, +- MMUAccessType access_type, int mmu_idx) +-{ +- int user_mode = mmu_idx == MMU_IDX_USER; +- int kernel_mode = mmu_idx == MMU_IDX_KERNEL; +- uint32_t plv, base_c, base_v; +- int64_t addr_high; +- uint8_t da = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, DA); +- uint8_t pg = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG); +- +- /* Check PG and DA */ +- if (da & !pg) { +- *physical = address & TARGET_PHYS_MASK; +- *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; +- return TLBRET_MATCH; +- } +- +- plv = kernel_mode | (user_mode << R_CSR_DMW_PLV3_SHIFT); +- if (is_la64(env)) { +- base_v = address >> R_CSR_DMW_64_VSEG_SHIFT; +- } else { +- base_v = address >> R_CSR_DMW_32_VSEG_SHIFT; +- } +- /* Check direct map window */ +- for (int i = 0; i < 4; i++) { +- if (is_la64(env)) { +- base_c = FIELD_EX64(env->CSR_DMW[i], CSR_DMW_64, VSEG); +- } else { +- base_c = FIELD_EX64(env->CSR_DMW[i], CSR_DMW_32, VSEG); +- } +- if ((plv & env->CSR_DMW[i]) && (base_c == base_v)) { +- *physical = dmw_va2pa(env, address, env->CSR_DMW[i]); +- *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; +- return TLBRET_MATCH; +- } +- } +- +- /* Check valid extension */ +- addr_high = sextract64(address, TARGET_VIRT_ADDR_SPACE_BITS, 16); +- if (!(addr_high == 0 || addr_high == -1)) { +- return TLBRET_BADADDR; +- } +- +- /* Mapped address */ +- return loongarch_map_address(env, physical, prot, address, +- access_type, mmu_idx); +-} +- +-hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) +-{ +- LoongArchCPU *cpu = LOONGARCH_CPU(cs); +- CPULoongArchState *env = &cpu->env; +- hwaddr phys_addr; +- int prot; +- +- if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD, +- cpu_mmu_index(env, false)) != 0) { +- return -1; +- } +- return phys_addr; +-} +- +-static void raise_mmu_exception(CPULoongArchState *env, target_ulong address, +- MMUAccessType access_type, int tlb_error) +-{ +- CPUState *cs = env_cpu(env); +- +- switch (tlb_error) { +- default: +- case TLBRET_BADADDR: +- cs->exception_index = access_type == MMU_INST_FETCH +- ? EXCCODE_ADEF : EXCCODE_ADEM; +- break; +- case TLBRET_NOMATCH: +- /* No TLB match for a mapped address */ +- if (access_type == MMU_DATA_LOAD) { +- cs->exception_index = EXCCODE_PIL; +- } else if (access_type == MMU_DATA_STORE) { +- cs->exception_index = EXCCODE_PIS; +- } else if (access_type == MMU_INST_FETCH) { +- cs->exception_index = EXCCODE_PIF; +- } +- env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 1); +- break; +- case TLBRET_INVALID: +- /* TLB match with no valid bit */ +- if (access_type == MMU_DATA_LOAD) { +- cs->exception_index = EXCCODE_PIL; +- } else if (access_type == MMU_DATA_STORE) { +- cs->exception_index = EXCCODE_PIS; +- } else if (access_type == MMU_INST_FETCH) { +- cs->exception_index = EXCCODE_PIF; +- } +- break; +- case TLBRET_DIRTY: +- /* TLB match but 'D' bit is cleared */ +- cs->exception_index = EXCCODE_PME; +- break; +- case TLBRET_XI: +- /* Execute-Inhibit Exception */ +- cs->exception_index = EXCCODE_PNX; +- break; +- case TLBRET_RI: +- /* Read-Inhibit Exception */ +- cs->exception_index = EXCCODE_PNR; +- break; +- case TLBRET_PE: +- /* Privileged Exception */ +- cs->exception_index = EXCCODE_PPI; +- break; +- } +- +- if (tlb_error == TLBRET_NOMATCH) { +- env->CSR_TLBRBADV = address; +- if (is_la64(env)) { +- env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI_64, +- VPPN, extract64(address, 13, 35)); +- } else { +- env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI_32, +- VPPN, extract64(address, 13, 19)); +- } +- } else { +- if (!FIELD_EX64(env->CSR_DBG, CSR_DBG, DST)) { +- env->CSR_BADV = address; +- } +- env->CSR_TLBEHI = address & (TARGET_PAGE_MASK << 1); +- } +-} +- +-static void invalidate_tlb_entry(CPULoongArchState *env, int index) +-{ +- target_ulong addr, mask, pagesize; +- uint8_t tlb_ps; +- LoongArchTLB *tlb = &env->tlb[index]; +- +- int mmu_idx = cpu_mmu_index(env, false); +- uint8_t tlb_v0 = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, V); +- uint8_t tlb_v1 = FIELD_EX64(tlb->tlb_entry1, TLBENTRY, V); +- uint64_t tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); +- +- if (index >= LOONGARCH_STLB) { +- tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); +- } else { +- tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); +- } +- pagesize = MAKE_64BIT_MASK(tlb_ps, 1); +- mask = MAKE_64BIT_MASK(0, tlb_ps + 1); +- +- if (tlb_v0) { +- addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & ~mask; /* even */ +- tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize, +- mmu_idx, TARGET_LONG_BITS); +- } +- +- if (tlb_v1) { +- addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & pagesize; /* odd */ +- tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize, +- mmu_idx, TARGET_LONG_BITS); +- } +-} +- +-static void invalidate_tlb(CPULoongArchState *env, int index) +-{ +- LoongArchTLB *tlb; +- uint16_t csr_asid, tlb_asid, tlb_g; +- +- csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); +- tlb = &env->tlb[index]; +- tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); +- tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); +- if (tlb_g == 0 && tlb_asid != csr_asid) { +- return; +- } +- invalidate_tlb_entry(env, index); +-} +- +-static void fill_tlb_entry(CPULoongArchState *env, int index) +-{ +- LoongArchTLB *tlb = &env->tlb[index]; +- uint64_t lo0, lo1, csr_vppn; +- uint16_t csr_asid; +- uint8_t csr_ps; +- +- if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) { +- csr_ps = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, PS); +- if (is_la64(env)) { +- csr_vppn = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI_64, VPPN); +- } else { +- csr_vppn = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI_32, VPPN); +- } +- lo0 = env->CSR_TLBRELO0; +- lo1 = env->CSR_TLBRELO1; +- } else { +- csr_ps = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, PS); +- if (is_la64(env)) { +- csr_vppn = FIELD_EX64(env->CSR_TLBEHI, CSR_TLBEHI_64, VPPN); +- } else { +- csr_vppn = FIELD_EX64(env->CSR_TLBEHI, CSR_TLBEHI_32, VPPN); +- } +- lo0 = env->CSR_TLBELO0; +- lo1 = env->CSR_TLBELO1; +- } +- +- if (csr_ps == 0) { +- qemu_log_mask(CPU_LOG_MMU, "page size is 0\n"); +- } +- +- /* Only MTLB has the ps fields */ +- if (index >= LOONGARCH_STLB) { +- tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, PS, csr_ps); +- } +- +- tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, VPPN, csr_vppn); +- tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 1); +- csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); +- tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, ASID, csr_asid); +- +- tlb->tlb_entry0 = lo0; +- tlb->tlb_entry1 = lo1; +-} +- +-/* Return an random value between low and high */ +-static uint32_t get_random_tlb(uint32_t low, uint32_t high) +-{ +- uint32_t val; +- +- qemu_guest_getrandom_nofail(&val, sizeof(val)); +- return val % (high - low + 1) + low; +-} +- +-void helper_tlbsrch(CPULoongArchState *env) +-{ +- int index, match; +- +- if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) { +- match = loongarch_tlb_search(env, env->CSR_TLBREHI, &index); +- } else { +- match = loongarch_tlb_search(env, env->CSR_TLBEHI, &index); +- } +- +- if (match) { +- env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX, index); +- env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 0); +- return; +- } +- +- env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1); +-} +- +-void helper_tlbrd(CPULoongArchState *env) +-{ +- LoongArchTLB *tlb; +- int index; +- uint8_t tlb_ps, tlb_e; +- +- index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); +- tlb = &env->tlb[index]; +- +- if (index >= LOONGARCH_STLB) { +- tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); +- } else { +- tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); +- } +- tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); +- +- if (!tlb_e) { +- /* Invalid TLB entry */ +- env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1); +- env->CSR_ASID = FIELD_DP64(env->CSR_ASID, CSR_ASID, ASID, 0); +- env->CSR_TLBEHI = 0; +- env->CSR_TLBELO0 = 0; +- env->CSR_TLBELO1 = 0; +- env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, PS, 0); +- } else { +- /* Valid TLB entry */ +- env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 0); +- env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, +- PS, (tlb_ps & 0x3f)); +- env->CSR_TLBEHI = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN) << +- R_TLB_MISC_VPPN_SHIFT; +- env->CSR_TLBELO0 = tlb->tlb_entry0; +- env->CSR_TLBELO1 = tlb->tlb_entry1; +- } +-} +- +-void helper_tlbwr(CPULoongArchState *env) +-{ +- int index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); +- +- invalidate_tlb(env, index); +- +- if (FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, NE)) { +- env->tlb[index].tlb_misc = FIELD_DP64(env->tlb[index].tlb_misc, +- TLB_MISC, E, 0); +- return; +- } +- +- fill_tlb_entry(env, index); +-} +- +-void helper_tlbfill(CPULoongArchState *env) +-{ +- uint64_t address, entryhi; +- int index, set, stlb_idx; +- uint16_t pagesize, stlb_ps; +- +- if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) { +- entryhi = env->CSR_TLBREHI; +- pagesize = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, PS); +- } else { +- entryhi = env->CSR_TLBEHI; +- pagesize = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, PS); +- } +- +- stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); +- +- if (pagesize == stlb_ps) { +- /* Only write into STLB bits [47:13] */ +- address = entryhi & ~MAKE_64BIT_MASK(0, R_CSR_TLBEHI_64_VPPN_SHIFT); +- +- /* Choose one set ramdomly */ +- set = get_random_tlb(0, 7); +- +- /* Index in one set */ +- stlb_idx = (address >> (stlb_ps + 1)) & 0xff; /* [0,255] */ +- +- index = set * 256 + stlb_idx; +- } else { +- /* Only write into MTLB */ +- index = get_random_tlb(LOONGARCH_STLB, LOONGARCH_TLB_MAX - 1); +- } +- +- invalidate_tlb(env, index); +- fill_tlb_entry(env, index); +-} +- +-void helper_tlbclr(CPULoongArchState *env) +-{ +- LoongArchTLB *tlb; +- int i, index; +- uint16_t csr_asid, tlb_asid, tlb_g; +- +- csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); +- index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); +- +- if (index < LOONGARCH_STLB) { +- /* STLB. One line per operation */ +- for (i = 0; i < 8; i++) { +- tlb = &env->tlb[i * 256 + (index % 256)]; +- tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); +- tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); +- if (!tlb_g && tlb_asid == csr_asid) { +- tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); +- } +- } +- } else if (index < LOONGARCH_TLB_MAX) { +- /* All MTLB entries */ +- for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) { +- tlb = &env->tlb[i]; +- tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); +- tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); +- if (!tlb_g && tlb_asid == csr_asid) { +- tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); +- } +- } +- } +- +- tlb_flush(env_cpu(env)); +-} +- +-void helper_tlbflush(CPULoongArchState *env) +-{ +- int i, index; +- +- index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); +- +- if (index < LOONGARCH_STLB) { +- /* STLB. One line per operation */ +- for (i = 0; i < 8; i++) { +- int s_idx = i * 256 + (index % 256); +- env->tlb[s_idx].tlb_misc = FIELD_DP64(env->tlb[s_idx].tlb_misc, +- TLB_MISC, E, 0); +- } +- } else if (index < LOONGARCH_TLB_MAX) { +- /* All MTLB entries */ +- for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) { +- env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc, +- TLB_MISC, E, 0); +- } +- } +- +- tlb_flush(env_cpu(env)); +-} +- +-void helper_invtlb_all(CPULoongArchState *env) +-{ +- for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { +- env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc, +- TLB_MISC, E, 0); +- } +- tlb_flush(env_cpu(env)); +-} +- +-void helper_invtlb_all_g(CPULoongArchState *env, uint32_t g) +-{ +- for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { +- LoongArchTLB *tlb = &env->tlb[i]; +- uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); +- +- if (tlb_g == g) { +- tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); +- } +- } +- tlb_flush(env_cpu(env)); +-} +- +-void helper_invtlb_all_asid(CPULoongArchState *env, target_ulong info) +-{ +- uint16_t asid = info & R_CSR_ASID_ASID_MASK; +- +- for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { +- LoongArchTLB *tlb = &env->tlb[i]; +- uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); +- uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); +- +- if (!tlb_g && (tlb_asid == asid)) { +- tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); +- } +- } +- tlb_flush(env_cpu(env)); +-} +- +-void helper_invtlb_page_asid(CPULoongArchState *env, target_ulong info, +- target_ulong addr) +-{ +- uint16_t asid = info & 0x3ff; +- +- for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { +- LoongArchTLB *tlb = &env->tlb[i]; +- uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); +- uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); +- uint64_t vpn, tlb_vppn; +- uint8_t tlb_ps, compare_shift; +- +- if (i >= LOONGARCH_STLB) { +- tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); +- } else { +- tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); +- } +- tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); +- vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1); +- compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; +- +- if (!tlb_g && (tlb_asid == asid) && +- (vpn == (tlb_vppn >> compare_shift))) { +- tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); +- } +- } +- tlb_flush(env_cpu(env)); +-} +- +-void helper_invtlb_page_asid_or_g(CPULoongArchState *env, +- target_ulong info, target_ulong addr) +-{ +- uint16_t asid = info & 0x3ff; +- +- for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { +- LoongArchTLB *tlb = &env->tlb[i]; +- uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); +- uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); +- uint64_t vpn, tlb_vppn; +- uint8_t tlb_ps, compare_shift; +- +- if (i >= LOONGARCH_STLB) { +- tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); +- } else { +- tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); +- } +- tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); +- vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1); +- compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; +- +- if ((tlb_g || (tlb_asid == asid)) && +- (vpn == (tlb_vppn >> compare_shift))) { +- tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); +- } +- } +- tlb_flush(env_cpu(env)); +-} +- +-bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, +- MMUAccessType access_type, int mmu_idx, +- bool probe, uintptr_t retaddr) +-{ +- LoongArchCPU *cpu = LOONGARCH_CPU(cs); +- CPULoongArchState *env = &cpu->env; +- hwaddr physical; +- int prot; +- int ret; +- +- /* Data access */ +- ret = get_physical_address(env, &physical, &prot, address, +- access_type, mmu_idx); +- +- if (ret == TLBRET_MATCH) { +- tlb_set_page(cs, address & TARGET_PAGE_MASK, +- physical & TARGET_PAGE_MASK, prot, +- mmu_idx, TARGET_PAGE_SIZE); +- qemu_log_mask(CPU_LOG_MMU, +- "%s address=%" VADDR_PRIx " physical " HWADDR_FMT_plx +- " prot %d\n", __func__, address, physical, prot); +- return true; +- } else { +- qemu_log_mask(CPU_LOG_MMU, +- "%s address=%" VADDR_PRIx " ret %d\n", __func__, address, +- ret); +- } +- if (probe) { +- return false; +- } +- raise_mmu_exception(env, address, access_type, ret); +- cpu_loop_exit_restore(cs, retaddr); +-} +- +-target_ulong helper_lddir(CPULoongArchState *env, target_ulong base, +- target_ulong level, uint32_t mem_idx) +-{ +- CPUState *cs = env_cpu(env); +- target_ulong badvaddr, index, phys, ret; +- int shift; +- uint64_t dir_base, dir_width; +- bool huge = (base >> LOONGARCH_PAGE_HUGE_SHIFT) & 0x1; +- +- badvaddr = env->CSR_TLBRBADV; +- base = base & TARGET_PHYS_MASK; +- +- /* 0:64bit, 1:128bit, 2:192bit, 3:256bit */ +- shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH); +- shift = (shift + 1) * 3; +- +- if (huge) { +- return base; +- } +- switch (level) { +- case 1: +- dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_BASE); +- dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_WIDTH); +- break; +- case 2: +- dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_BASE); +- dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_WIDTH); +- break; +- case 3: +- dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_BASE); +- dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_WIDTH); +- break; +- case 4: +- dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_BASE); +- dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_WIDTH); +- break; +- default: +- do_raise_exception(env, EXCCODE_INE, GETPC()); +- return 0; +- } +- index = (badvaddr >> dir_base) & ((1 << dir_width) - 1); +- phys = base | index << shift; +- ret = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; +- return ret; +-} +- +-void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, +- uint32_t mem_idx) +-{ +- CPUState *cs = env_cpu(env); +- target_ulong phys, tmp0, ptindex, ptoffset0, ptoffset1, ps, badv; +- int shift; +- bool huge = (base >> LOONGARCH_PAGE_HUGE_SHIFT) & 0x1; +- uint64_t ptbase = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE); +- uint64_t ptwidth = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH); +- +- base = base & TARGET_PHYS_MASK; +- +- if (huge) { +- /* Huge Page. base is paddr */ +- tmp0 = base ^ (1 << LOONGARCH_PAGE_HUGE_SHIFT); +- /* Move Global bit */ +- tmp0 = ((tmp0 & (1 << LOONGARCH_HGLOBAL_SHIFT)) >> +- LOONGARCH_HGLOBAL_SHIFT) << R_TLBENTRY_G_SHIFT | +- (tmp0 & (~(1 << LOONGARCH_HGLOBAL_SHIFT))); +- ps = ptbase + ptwidth - 1; +- if (odd) { +- tmp0 += MAKE_64BIT_MASK(ps, 1); +- } +- } else { +- /* 0:64bit, 1:128bit, 2:192bit, 3:256bit */ +- shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH); +- shift = (shift + 1) * 3; +- badv = env->CSR_TLBRBADV; +- +- ptindex = (badv >> ptbase) & ((1 << ptwidth) - 1); +- ptindex = ptindex & ~0x1; /* clear bit 0 */ +- ptoffset0 = ptindex << shift; +- ptoffset1 = (ptindex + 1) << shift; +- +- phys = base | (odd ? ptoffset1 : ptoffset0); +- tmp0 = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; +- ps = ptbase; +- } +- +- if (odd) { +- env->CSR_TLBRELO1 = tmp0; +- } else { +- env->CSR_TLBRELO0 = tmp0; +- } +- env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, PS, ps); +-} +diff --git a/target/loongarch/translate.c b/target/loongarch/translate.c +deleted file mode 100644 +index 21f4db6..0000000 +--- a/target/loongarch/translate.c ++++ /dev/null +@@ -1,370 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * LoongArch emulation for QEMU - main translation routines. +- * +- * Copyright (c) 2021 Loongson Technology Corporation Limited +- */ +- +-#include "qemu/osdep.h" +-#include "cpu.h" +-#include "tcg/tcg-op.h" +-#include "tcg/tcg-op-gvec.h" +-#include "exec/translation-block.h" +-#include "exec/translator.h" +-#include "exec/helper-proto.h" +-#include "exec/helper-gen.h" +-#include "exec/log.h" +-#include "qemu/qemu-print.h" +-#include "fpu/softfloat.h" +-#include "translate.h" +-#include "internals.h" +-#include "vec.h" +- +-/* Global register indices */ +-TCGv cpu_gpr[32], cpu_pc; +-static TCGv cpu_lladdr, cpu_llval; +- +-#define HELPER_H "helper.h" +-#include "exec/helper-info.c.inc" +-#undef HELPER_H +- +-#define DISAS_STOP DISAS_TARGET_0 +-#define DISAS_EXIT DISAS_TARGET_1 +-#define DISAS_EXIT_UPDATE DISAS_TARGET_2 +- +-static inline int vec_full_offset(int regno) +-{ +- return offsetof(CPULoongArchState, fpr[regno]); +-} +- +-static inline int vec_reg_offset(int regno, int index, MemOp mop) +-{ +- const uint8_t size = 1 << mop; +- int offs = index * size; +- +- if (HOST_BIG_ENDIAN && size < 8 ) { +- offs ^= (8 - size); +- } +- +- return offs + vec_full_offset(regno); +-} +- +-static inline void get_vreg64(TCGv_i64 dest, int regno, int index) +-{ +- tcg_gen_ld_i64(dest, tcg_env, +- offsetof(CPULoongArchState, fpr[regno].vreg.D(index))); +-} +- +-static inline void set_vreg64(TCGv_i64 src, int regno, int index) +-{ +- tcg_gen_st_i64(src, tcg_env, +- offsetof(CPULoongArchState, fpr[regno].vreg.D(index))); +-} +- +-static inline int plus_1(DisasContext *ctx, int x) +-{ +- return x + 1; +-} +- +-static inline int shl_1(DisasContext *ctx, int x) +-{ +- return x << 1; +-} +- +-static inline int shl_2(DisasContext *ctx, int x) +-{ +- return x << 2; +-} +- +-static inline int shl_3(DisasContext *ctx, int x) +-{ +- return x << 3; +-} +- +-/* +- * LoongArch the upper 32 bits are undefined ("can be any value"). +- * QEMU chooses to nanbox, because it is most likely to show guest bugs early. +- */ +-static void gen_nanbox_s(TCGv_i64 out, TCGv_i64 in) +-{ +- tcg_gen_ori_i64(out, in, MAKE_64BIT_MASK(32, 32)); +-} +- +-void generate_exception(DisasContext *ctx, int excp) +-{ +- tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); +- gen_helper_raise_exception(tcg_env, tcg_constant_i32(excp)); +- ctx->base.is_jmp = DISAS_NORETURN; +-} +- +-static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) +-{ +- if (ctx->va32) { +- dest = (uint32_t) dest; +- } +- +- if (translator_use_goto_tb(&ctx->base, dest)) { +- tcg_gen_goto_tb(n); +- tcg_gen_movi_tl(cpu_pc, dest); +- tcg_gen_exit_tb(ctx->base.tb, n); +- } else { +- tcg_gen_movi_tl(cpu_pc, dest); +- tcg_gen_lookup_and_goto_ptr(); +- } +-} +- +-static void loongarch_tr_init_disas_context(DisasContextBase *dcbase, +- CPUState *cs) +-{ +- int64_t bound; +- CPULoongArchState *env = cpu_env(cs); +- DisasContext *ctx = container_of(dcbase, DisasContext, base); +- +- ctx->page_start = ctx->base.pc_first & TARGET_PAGE_MASK; +- ctx->plv = ctx->base.tb->flags & HW_FLAGS_PLV_MASK; +- if (ctx->base.tb->flags & HW_FLAGS_CRMD_PG) { +- ctx->mem_idx = ctx->plv; +- } else { +- ctx->mem_idx = MMU_IDX_DA; +- } +- +- /* Bound the number of insns to execute to those left on the page. */ +- bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4; +- ctx->base.max_insns = MIN(ctx->base.max_insns, bound); +- +- if (FIELD_EX64(env->cpucfg[2], CPUCFG2, LSX)) { +- ctx->vl = LSX_LEN; +- } +- +- if (FIELD_EX64(env->cpucfg[2], CPUCFG2, LASX)) { +- ctx->vl = LASX_LEN; +- } +- +- ctx->la64 = is_la64(env); +- ctx->va32 = (ctx->base.tb->flags & HW_FLAGS_VA32) != 0; +- +- ctx->zero = tcg_constant_tl(0); +- +- ctx->cpucfg1 = env->cpucfg[1]; +- ctx->cpucfg2 = env->cpucfg[2]; +-} +- +-static void loongarch_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) +-{ +-} +- +-static void loongarch_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) +-{ +- DisasContext *ctx = container_of(dcbase, DisasContext, base); +- +- tcg_gen_insn_start(ctx->base.pc_next); +-} +- +-/* +- * Wrappers for getting reg values. +- * +- * The $zero register does not have cpu_gpr[0] allocated -- we supply the +- * constant zero as a source, and an uninitialized sink as destination. +- * +- * Further, we may provide an extension for word operations. +- */ +-static TCGv gpr_src(DisasContext *ctx, int reg_num, DisasExtend src_ext) +-{ +- TCGv t; +- +- if (reg_num == 0) { +- return ctx->zero; +- } +- +- switch (src_ext) { +- case EXT_NONE: +- return cpu_gpr[reg_num]; +- case EXT_SIGN: +- t = tcg_temp_new(); +- tcg_gen_ext32s_tl(t, cpu_gpr[reg_num]); +- return t; +- case EXT_ZERO: +- t = tcg_temp_new(); +- tcg_gen_ext32u_tl(t, cpu_gpr[reg_num]); +- return t; +- } +- g_assert_not_reached(); +-} +- +-static TCGv gpr_dst(DisasContext *ctx, int reg_num, DisasExtend dst_ext) +-{ +- if (reg_num == 0 || dst_ext) { +- return tcg_temp_new(); +- } +- return cpu_gpr[reg_num]; +-} +- +-static void gen_set_gpr(int reg_num, TCGv t, DisasExtend dst_ext) +-{ +- if (reg_num != 0) { +- switch (dst_ext) { +- case EXT_NONE: +- tcg_gen_mov_tl(cpu_gpr[reg_num], t); +- break; +- case EXT_SIGN: +- tcg_gen_ext32s_tl(cpu_gpr[reg_num], t); +- break; +- case EXT_ZERO: +- tcg_gen_ext32u_tl(cpu_gpr[reg_num], t); +- break; +- default: +- g_assert_not_reached(); +- } +- } +-} +- +-static TCGv get_fpr(DisasContext *ctx, int reg_num) +-{ +- TCGv t = tcg_temp_new(); +- tcg_gen_ld_i64(t, tcg_env, +- offsetof(CPULoongArchState, fpr[reg_num].vreg.D(0))); +- return t; +-} +- +-static void set_fpr(int reg_num, TCGv val) +-{ +- tcg_gen_st_i64(val, tcg_env, +- offsetof(CPULoongArchState, fpr[reg_num].vreg.D(0))); +-} +- +-static TCGv make_address_x(DisasContext *ctx, TCGv base, TCGv addend) +-{ +- TCGv temp = NULL; +- +- if (addend || ctx->va32) { +- temp = tcg_temp_new(); +- } +- if (addend) { +- tcg_gen_add_tl(temp, base, addend); +- base = temp; +- } +- if (ctx->va32) { +- tcg_gen_ext32u_tl(temp, base); +- base = temp; +- } +- return base; +-} +- +-static TCGv make_address_i(DisasContext *ctx, TCGv base, target_long ofs) +-{ +- TCGv addend = ofs ? tcg_constant_tl(ofs) : NULL; +- return make_address_x(ctx, base, addend); +-} +- +-static uint64_t make_address_pc(DisasContext *ctx, uint64_t addr) +-{ +- if (ctx->va32) { +- addr = (int32_t)addr; +- } +- return addr; +-} +- +-#include "decode-insns.c.inc" +-#include "insn_trans/trans_arith.c.inc" +-#include "insn_trans/trans_shift.c.inc" +-#include "insn_trans/trans_bit.c.inc" +-#include "insn_trans/trans_memory.c.inc" +-#include "insn_trans/trans_atomic.c.inc" +-#include "insn_trans/trans_extra.c.inc" +-#include "insn_trans/trans_farith.c.inc" +-#include "insn_trans/trans_fcmp.c.inc" +-#include "insn_trans/trans_fcnv.c.inc" +-#include "insn_trans/trans_fmov.c.inc" +-#include "insn_trans/trans_fmemory.c.inc" +-#include "insn_trans/trans_branch.c.inc" +-#include "insn_trans/trans_privileged.c.inc" +-#include "insn_trans/trans_vec.c.inc" +- +-static void loongarch_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) +-{ +- CPULoongArchState *env = cpu_env(cs); +- DisasContext *ctx = container_of(dcbase, DisasContext, base); +- +- ctx->opcode = translator_ldl(env, &ctx->base, ctx->base.pc_next); +- +- if (!decode(ctx, ctx->opcode)) { +- qemu_log_mask(LOG_UNIMP, "Error: unknown opcode. " +- TARGET_FMT_lx ": 0x%x\n", +- ctx->base.pc_next, ctx->opcode); +- generate_exception(ctx, EXCCODE_INE); +- } +- +- ctx->base.pc_next += 4; +- +- if (ctx->va32) { +- ctx->base.pc_next = (uint32_t)ctx->base.pc_next; +- } +-} +- +-static void loongarch_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) +-{ +- DisasContext *ctx = container_of(dcbase, DisasContext, base); +- +- switch (ctx->base.is_jmp) { +- case DISAS_STOP: +- tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); +- tcg_gen_lookup_and_goto_ptr(); +- break; +- case DISAS_TOO_MANY: +- gen_goto_tb(ctx, 0, ctx->base.pc_next); +- break; +- case DISAS_NORETURN: +- break; +- case DISAS_EXIT_UPDATE: +- tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); +- QEMU_FALLTHROUGH; +- case DISAS_EXIT: +- tcg_gen_exit_tb(NULL, 0); +- break; +- default: +- g_assert_not_reached(); +- } +-} +- +-static void loongarch_tr_disas_log(const DisasContextBase *dcbase, +- CPUState *cpu, FILE *logfile) +-{ +- qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); +- target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); +-} +- +-static const TranslatorOps loongarch_tr_ops = { +- .init_disas_context = loongarch_tr_init_disas_context, +- .tb_start = loongarch_tr_tb_start, +- .insn_start = loongarch_tr_insn_start, +- .translate_insn = loongarch_tr_translate_insn, +- .tb_stop = loongarch_tr_tb_stop, +- .disas_log = loongarch_tr_disas_log, +-}; +- +-void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, +- target_ulong pc, void *host_pc) +-{ +- DisasContext ctx; +- +- translator_loop(cs, tb, max_insns, pc, host_pc, +- &loongarch_tr_ops, &ctx.base); +-} +- +-void loongarch_translate_init(void) +-{ +- int i; +- +- cpu_gpr[0] = NULL; +- for (i = 1; i < 32; i++) { +- cpu_gpr[i] = tcg_global_mem_new(tcg_env, +- offsetof(CPULoongArchState, gpr[i]), +- regnames[i]); +- } +- +- cpu_pc = tcg_global_mem_new(tcg_env, offsetof(CPULoongArchState, pc), "pc"); +- cpu_lladdr = tcg_global_mem_new(tcg_env, +- offsetof(CPULoongArchState, lladdr), "lladdr"); +- cpu_llval = tcg_global_mem_new(tcg_env, +- offsetof(CPULoongArchState, llval), "llval"); +-} +diff --git a/target/loongarch/vec_helper.c b/target/loongarch/vec_helper.c +deleted file mode 100644 +index 3faf52c..0000000 +--- a/target/loongarch/vec_helper.c ++++ /dev/null +@@ -1,3494 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * QEMU LoongArch vector helper functions. +- * +- * Copyright (c) 2022-2023 Loongson Technology Corporation Limited +- */ +- +-#include "qemu/osdep.h" +-#include "cpu.h" +-#include "exec/exec-all.h" +-#include "exec/helper-proto.h" +-#include "fpu/softfloat.h" +-#include "internals.h" +-#include "tcg/tcg.h" +-#include "vec.h" +-#include "tcg/tcg-gvec-desc.h" +- +-#define DO_ODD_EVEN(NAME, BIT, E1, E2, DO_OP) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- typedef __typeof(Vd->E1(0)) TD; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E1(i) = DO_OP((TD)Vj->E2(2 * i + 1), (TD)Vk->E2(2 * i)); \ +- } \ +-} +- +-DO_ODD_EVEN(vhaddw_h_b, 16, H, B, DO_ADD) +-DO_ODD_EVEN(vhaddw_w_h, 32, W, H, DO_ADD) +-DO_ODD_EVEN(vhaddw_d_w, 64, D, W, DO_ADD) +- +-void HELPER(vhaddw_q_d)(void *vd, void *vj, void *vk, uint32_t desc) +-{ +- int i; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- VReg *Vk = (VReg *)vk; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16 ; i++) { +- Vd->Q(i) = int128_add(int128_makes64(Vj->D(2 * i + 1)), +- int128_makes64(Vk->D(2 * i))); +- } +-} +- +-DO_ODD_EVEN(vhsubw_h_b, 16, H, B, DO_SUB) +-DO_ODD_EVEN(vhsubw_w_h, 32, W, H, DO_SUB) +-DO_ODD_EVEN(vhsubw_d_w, 64, D, W, DO_SUB) +- +-void HELPER(vhsubw_q_d)(void *vd, void *vj, void *vk, uint32_t desc) +-{ +- int i; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- VReg *Vk = (VReg *)vk; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- Vd->Q(i) = int128_sub(int128_makes64(Vj->D(2 * i + 1)), +- int128_makes64(Vk->D(2 * i))); +- } +-} +- +-DO_ODD_EVEN(vhaddw_hu_bu, 16, UH, UB, DO_ADD) +-DO_ODD_EVEN(vhaddw_wu_hu, 32, UW, UH, DO_ADD) +-DO_ODD_EVEN(vhaddw_du_wu, 64, UD, UW, DO_ADD) +- +-void HELPER(vhaddw_qu_du)(void *vd, void *vj, void *vk, uint32_t desc) +-{ +- int i; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- VReg *Vk = (VReg *)vk; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i ++) { +- Vd->Q(i) = int128_add(int128_make64(Vj->UD(2 * i + 1)), +- int128_make64(Vk->UD(2 * i))); +- } +-} +- +-DO_ODD_EVEN(vhsubw_hu_bu, 16, UH, UB, DO_SUB) +-DO_ODD_EVEN(vhsubw_wu_hu, 32, UW, UH, DO_SUB) +-DO_ODD_EVEN(vhsubw_du_wu, 64, UD, UW, DO_SUB) +- +-void HELPER(vhsubw_qu_du)(void *vd, void *vj, void *vk, uint32_t desc) +-{ +- int i; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- VReg *Vk = (VReg *)vk; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- Vd->Q(i) = int128_sub(int128_make64(Vj->UD(2 * i + 1)), +- int128_make64(Vk->UD(2 * i))); +- } +-} +- +-#define DO_EVEN(NAME, BIT, E1, E2, DO_OP) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- typedef __typeof(Vd->E1(0)) TD; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E1(i) = DO_OP((TD)Vj->E2(2 * i) ,(TD)Vk->E2(2 * i)); \ +- } \ +-} +- +-#define DO_ODD(NAME, BIT, E1, E2, DO_OP) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- typedef __typeof(Vd->E1(0)) TD; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E1(i) = DO_OP((TD)Vj->E2(2 * i + 1), (TD)Vk->E2(2 * i + 1)); \ +- } \ +-} +- +-void HELPER(vaddwev_q_d)(void *vd, void *vj, void *vk, uint32_t desc) +-{ +- int i; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- VReg *Vk = (VReg *)vk; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- Vd->Q(i) = int128_add(int128_makes64(Vj->D(2 * i)), +- int128_makes64(Vk->D(2 * i))); +- } +-} +- +-DO_EVEN(vaddwev_h_b, 16, H, B, DO_ADD) +-DO_EVEN(vaddwev_w_h, 32, W, H, DO_ADD) +-DO_EVEN(vaddwev_d_w, 64, D, W, DO_ADD) +- +-void HELPER(vaddwod_q_d)(void *vd, void *vj, void *vk, uint32_t desc) +-{ +- int i; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- VReg *Vk = (VReg *)vk; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- Vd->Q(i) = int128_add(int128_makes64(Vj->D(2 * i +1)), +- int128_makes64(Vk->D(2 * i +1))); +- } +-} +- +-DO_ODD(vaddwod_h_b, 16, H, B, DO_ADD) +-DO_ODD(vaddwod_w_h, 32, W, H, DO_ADD) +-DO_ODD(vaddwod_d_w, 64, D, W, DO_ADD) +- +-void HELPER(vsubwev_q_d)(void *vd, void *vj, void *vk, uint32_t desc) +-{ +- int i; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- VReg *Vk = (VReg *)vk; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- Vd->Q(i) = int128_sub(int128_makes64(Vj->D(2 * i)), +- int128_makes64(Vk->D(2 * i))); +- } +-} +- +-DO_EVEN(vsubwev_h_b, 16, H, B, DO_SUB) +-DO_EVEN(vsubwev_w_h, 32, W, H, DO_SUB) +-DO_EVEN(vsubwev_d_w, 64, D, W, DO_SUB) +- +-void HELPER(vsubwod_q_d)(void *vd, void *vj, void *vk, uint32_t desc) +-{ +- int i; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- VReg *Vk = (VReg *)vk; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- Vd->Q(i) = int128_sub(int128_makes64(Vj->D(2 * i + 1)), +- int128_makes64(Vk->D(2 * i + 1))); +- } +-} +- +-DO_ODD(vsubwod_h_b, 16, H, B, DO_SUB) +-DO_ODD(vsubwod_w_h, 32, W, H, DO_SUB) +-DO_ODD(vsubwod_d_w, 64, D, W, DO_SUB) +- +-void HELPER(vaddwev_q_du)(void *vd, void *vj, void *vk, uint32_t desc) +-{ +- int i; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- VReg *Vk = (VReg *)vk; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- Vd->Q(i) = int128_add(int128_make64(Vj->UD(2 * i)), +- int128_make64(Vk->UD(2 * i))); +- } +-} +- +-DO_EVEN(vaddwev_h_bu, 16, UH, UB, DO_ADD) +-DO_EVEN(vaddwev_w_hu, 32, UW, UH, DO_ADD) +-DO_EVEN(vaddwev_d_wu, 64, UD, UW, DO_ADD) +- +-void HELPER(vaddwod_q_du)(void *vd, void *vj, void *vk, uint32_t desc) +-{ +- int i; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- VReg *Vk = (VReg *)vk; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- Vd->Q(i) = int128_add(int128_make64(Vj->UD(2 * i + 1)), +- int128_make64(Vk->UD(2 * i + 1))); +- } +-} +- +-DO_ODD(vaddwod_h_bu, 16, UH, UB, DO_ADD) +-DO_ODD(vaddwod_w_hu, 32, UW, UH, DO_ADD) +-DO_ODD(vaddwod_d_wu, 64, UD, UW, DO_ADD) +- +-void HELPER(vsubwev_q_du)(void *vd, void *vj, void *vk, uint32_t desc) +-{ +- int i; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- VReg *Vk = (VReg *)vk; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- Vd->Q(i) = int128_sub(int128_make64(Vj->UD(2 * i)), +- int128_make64(Vk->UD(2 * i))); +- } +-} +- +-DO_EVEN(vsubwev_h_bu, 16, UH, UB, DO_SUB) +-DO_EVEN(vsubwev_w_hu, 32, UW, UH, DO_SUB) +-DO_EVEN(vsubwev_d_wu, 64, UD, UW, DO_SUB) +- +-void HELPER(vsubwod_q_du)(void *vd, void *vj, void *vk, uint32_t desc) +-{ +- int i; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- VReg *Vk = (VReg *)vk; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- Vd->Q(i) = int128_sub(int128_make64(Vj->UD(2 * i + 1)), +- int128_make64(Vk->UD(2 * i + 1))); +- } +-} +- +-DO_ODD(vsubwod_h_bu, 16, UH, UB, DO_SUB) +-DO_ODD(vsubwod_w_hu, 32, UW, UH, DO_SUB) +-DO_ODD(vsubwod_d_wu, 64, UD, UW, DO_SUB) +- +-#define DO_EVEN_U_S(NAME, BIT, ES1, EU1, ES2, EU2, DO_OP) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- typedef __typeof(Vd->ES1(0)) TDS; \ +- typedef __typeof(Vd->EU1(0)) TDU; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->ES1(i) = DO_OP((TDU)Vj->EU2(2 * i) ,(TDS)Vk->ES2(2 * i)); \ +- } \ +-} +- +-#define DO_ODD_U_S(NAME, BIT, ES1, EU1, ES2, EU2, DO_OP) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- typedef __typeof(Vd->ES1(0)) TDS; \ +- typedef __typeof(Vd->EU1(0)) TDU; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->ES1(i) = DO_OP((TDU)Vj->EU2(2 * i + 1), (TDS)Vk->ES2(2 * i + 1)); \ +- } \ +-} +- +-void HELPER(vaddwev_q_du_d)(void *vd, void *vj, void *vk, uint32_t desc) +-{ +- int i; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- VReg *Vk = (VReg *)vk; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- Vd->Q(i) = int128_add(int128_make64(Vj->UD(2 * i)), +- int128_makes64(Vk->D(2 * i))); +- } +-} +- +-DO_EVEN_U_S(vaddwev_h_bu_b, 16, H, UH, B, UB, DO_ADD) +-DO_EVEN_U_S(vaddwev_w_hu_h, 32, W, UW, H, UH, DO_ADD) +-DO_EVEN_U_S(vaddwev_d_wu_w, 64, D, UD, W, UW, DO_ADD) +- +-void HELPER(vaddwod_q_du_d)(void *vd, void *vj, void *vk, uint32_t desc) +-{ +- int i; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- VReg *Vk = (VReg *)vk; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- Vd->Q(i) = int128_add(int128_make64(Vj->UD(2 * i + 1)), +- int128_makes64(Vk->D(2 * i + 1))); +- } +-} +- +-DO_ODD_U_S(vaddwod_h_bu_b, 16, H, UH, B, UB, DO_ADD) +-DO_ODD_U_S(vaddwod_w_hu_h, 32, W, UW, H, UH, DO_ADD) +-DO_ODD_U_S(vaddwod_d_wu_w, 64, D, UD, W, UW, DO_ADD) +- +-#define DO_3OP(NAME, BIT, E, DO_OP) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E(i) = DO_OP(Vj->E(i), Vk->E(i)); \ +- } \ +-} +- +-DO_3OP(vavg_b, 8, B, DO_VAVG) +-DO_3OP(vavg_h, 16, H, DO_VAVG) +-DO_3OP(vavg_w, 32, W, DO_VAVG) +-DO_3OP(vavg_d, 64, D, DO_VAVG) +-DO_3OP(vavgr_b, 8, B, DO_VAVGR) +-DO_3OP(vavgr_h, 16, H, DO_VAVGR) +-DO_3OP(vavgr_w, 32, W, DO_VAVGR) +-DO_3OP(vavgr_d, 64, D, DO_VAVGR) +-DO_3OP(vavg_bu, 8, UB, DO_VAVG) +-DO_3OP(vavg_hu, 16, UH, DO_VAVG) +-DO_3OP(vavg_wu, 32, UW, DO_VAVG) +-DO_3OP(vavg_du, 64, UD, DO_VAVG) +-DO_3OP(vavgr_bu, 8, UB, DO_VAVGR) +-DO_3OP(vavgr_hu, 16, UH, DO_VAVGR) +-DO_3OP(vavgr_wu, 32, UW, DO_VAVGR) +-DO_3OP(vavgr_du, 64, UD, DO_VAVGR) +- +-DO_3OP(vabsd_b, 8, B, DO_VABSD) +-DO_3OP(vabsd_h, 16, H, DO_VABSD) +-DO_3OP(vabsd_w, 32, W, DO_VABSD) +-DO_3OP(vabsd_d, 64, D, DO_VABSD) +-DO_3OP(vabsd_bu, 8, UB, DO_VABSD) +-DO_3OP(vabsd_hu, 16, UH, DO_VABSD) +-DO_3OP(vabsd_wu, 32, UW, DO_VABSD) +-DO_3OP(vabsd_du, 64, UD, DO_VABSD) +- +-#define DO_VADDA(NAME, BIT, E) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E(i) = DO_VABS(Vj->E(i)) + DO_VABS(Vk->E(i)); \ +- } \ +-} +- +-DO_VADDA(vadda_b, 8, B) +-DO_VADDA(vadda_h, 16, H) +-DO_VADDA(vadda_w, 32, W) +-DO_VADDA(vadda_d, 64, D) +- +-#define VMINMAXI(NAME, BIT, E, DO_OP) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- typedef __typeof(Vd->E(0)) TD; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E(i) = DO_OP(Vj->E(i), (TD)imm); \ +- } \ +-} +- +-VMINMAXI(vmini_b, 8, B, DO_MIN) +-VMINMAXI(vmini_h, 16, H, DO_MIN) +-VMINMAXI(vmini_w, 32, W, DO_MIN) +-VMINMAXI(vmini_d, 64, D, DO_MIN) +-VMINMAXI(vmaxi_b, 8, B, DO_MAX) +-VMINMAXI(vmaxi_h, 16, H, DO_MAX) +-VMINMAXI(vmaxi_w, 32, W, DO_MAX) +-VMINMAXI(vmaxi_d, 64, D, DO_MAX) +-VMINMAXI(vmini_bu, 8, UB, DO_MIN) +-VMINMAXI(vmini_hu, 16, UH, DO_MIN) +-VMINMAXI(vmini_wu, 32, UW, DO_MIN) +-VMINMAXI(vmini_du, 64, UD, DO_MIN) +-VMINMAXI(vmaxi_bu, 8, UB, DO_MAX) +-VMINMAXI(vmaxi_hu, 16, UH, DO_MAX) +-VMINMAXI(vmaxi_wu, 32, UW, DO_MAX) +-VMINMAXI(vmaxi_du, 64, UD, DO_MAX) +- +-#define DO_VMUH(NAME, BIT, E1, E2, DO_OP) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- typedef __typeof(Vd->E1(0)) T; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E2(i) = ((T)Vj->E2(i)) * ((T)Vk->E2(i)) >> BIT; \ +- } \ +-} +- +-void HELPER(vmuh_d)(void *vd, void *vj, void *vk, uint32_t desc) +-{ +- int i; +- uint64_t l, h; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- VReg *Vk = (VReg *)vk; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 8; i++) { +- muls64(&l, &h, Vj->D(i), Vk->D(i)); +- Vd->D(i) = h; +- } +-} +- +-DO_VMUH(vmuh_b, 8, H, B, DO_MUH) +-DO_VMUH(vmuh_h, 16, W, H, DO_MUH) +-DO_VMUH(vmuh_w, 32, D, W, DO_MUH) +- +-void HELPER(vmuh_du)(void *vd, void *vj, void *vk, uint32_t desc) +-{ +- int i; +- uint64_t l, h; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- VReg *Vk = (VReg *)vk; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 8; i++) { +- mulu64(&l, &h, Vj->D(i), Vk->D(i)); +- Vd->D(i) = h; +- } +-} +- +-DO_VMUH(vmuh_bu, 8, UH, UB, DO_MUH) +-DO_VMUH(vmuh_hu, 16, UW, UH, DO_MUH) +-DO_VMUH(vmuh_wu, 32, UD, UW, DO_MUH) +- +-DO_EVEN(vmulwev_h_b, 16, H, B, DO_MUL) +-DO_EVEN(vmulwev_w_h, 32, W, H, DO_MUL) +-DO_EVEN(vmulwev_d_w, 64, D, W, DO_MUL) +- +-DO_ODD(vmulwod_h_b, 16, H, B, DO_MUL) +-DO_ODD(vmulwod_w_h, 32, W, H, DO_MUL) +-DO_ODD(vmulwod_d_w, 64, D, W, DO_MUL) +- +-DO_EVEN(vmulwev_h_bu, 16, UH, UB, DO_MUL) +-DO_EVEN(vmulwev_w_hu, 32, UW, UH, DO_MUL) +-DO_EVEN(vmulwev_d_wu, 64, UD, UW, DO_MUL) +- +-DO_ODD(vmulwod_h_bu, 16, UH, UB, DO_MUL) +-DO_ODD(vmulwod_w_hu, 32, UW, UH, DO_MUL) +-DO_ODD(vmulwod_d_wu, 64, UD, UW, DO_MUL) +- +-DO_EVEN_U_S(vmulwev_h_bu_b, 16, H, UH, B, UB, DO_MUL) +-DO_EVEN_U_S(vmulwev_w_hu_h, 32, W, UW, H, UH, DO_MUL) +-DO_EVEN_U_S(vmulwev_d_wu_w, 64, D, UD, W, UW, DO_MUL) +- +-DO_ODD_U_S(vmulwod_h_bu_b, 16, H, UH, B, UB, DO_MUL) +-DO_ODD_U_S(vmulwod_w_hu_h, 32, W, UW, H, UH, DO_MUL) +-DO_ODD_U_S(vmulwod_d_wu_w, 64, D, UD, W, UW, DO_MUL) +- +-#define VMADDSUB(NAME, BIT, E, DO_OP) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E(i) = DO_OP(Vd->E(i), Vj->E(i) ,Vk->E(i)); \ +- } \ +-} +- +-VMADDSUB(vmadd_b, 8, B, DO_MADD) +-VMADDSUB(vmadd_h, 16, H, DO_MADD) +-VMADDSUB(vmadd_w, 32, W, DO_MADD) +-VMADDSUB(vmadd_d, 64, D, DO_MADD) +-VMADDSUB(vmsub_b, 8, B, DO_MSUB) +-VMADDSUB(vmsub_h, 16, H, DO_MSUB) +-VMADDSUB(vmsub_w, 32, W, DO_MSUB) +-VMADDSUB(vmsub_d, 64, D, DO_MSUB) +- +-#define VMADDWEV(NAME, BIT, E1, E2, DO_OP) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- typedef __typeof(Vd->E1(0)) TD; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E1(i) += DO_OP((TD)Vj->E2(2 * i), (TD)Vk->E2(2 * i)); \ +- } \ +-} +- +-VMADDWEV(vmaddwev_h_b, 16, H, B, DO_MUL) +-VMADDWEV(vmaddwev_w_h, 32, W, H, DO_MUL) +-VMADDWEV(vmaddwev_d_w, 64, D, W, DO_MUL) +-VMADDWEV(vmaddwev_h_bu, 16, UH, UB, DO_MUL) +-VMADDWEV(vmaddwev_w_hu, 32, UW, UH, DO_MUL) +-VMADDWEV(vmaddwev_d_wu, 64, UD, UW, DO_MUL) +- +-#define VMADDWOD(NAME, BIT, E1, E2, DO_OP) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- typedef __typeof(Vd->E1(0)) TD; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E1(i) += DO_OP((TD)Vj->E2(2 * i + 1), \ +- (TD)Vk->E2(2 * i + 1)); \ +- } \ +-} +- +-VMADDWOD(vmaddwod_h_b, 16, H, B, DO_MUL) +-VMADDWOD(vmaddwod_w_h, 32, W, H, DO_MUL) +-VMADDWOD(vmaddwod_d_w, 64, D, W, DO_MUL) +-VMADDWOD(vmaddwod_h_bu, 16, UH, UB, DO_MUL) +-VMADDWOD(vmaddwod_w_hu, 32, UW, UH, DO_MUL) +-VMADDWOD(vmaddwod_d_wu, 64, UD, UW, DO_MUL) +- +-#define VMADDWEV_U_S(NAME, BIT, ES1, EU1, ES2, EU2, DO_OP) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- typedef __typeof(Vd->ES1(0)) TS1; \ +- typedef __typeof(Vd->EU1(0)) TU1; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->ES1(i) += DO_OP((TU1)Vj->EU2(2 * i), \ +- (TS1)Vk->ES2(2 * i)); \ +- } \ +-} +- +-VMADDWEV_U_S(vmaddwev_h_bu_b, 16, H, UH, B, UB, DO_MUL) +-VMADDWEV_U_S(vmaddwev_w_hu_h, 32, W, UW, H, UH, DO_MUL) +-VMADDWEV_U_S(vmaddwev_d_wu_w, 64, D, UD, W, UW, DO_MUL) +- +-#define VMADDWOD_U_S(NAME, BIT, ES1, EU1, ES2, EU2, DO_OP) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- typedef __typeof(Vd->ES1(0)) TS1; \ +- typedef __typeof(Vd->EU1(0)) TU1; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->ES1(i) += DO_OP((TU1)Vj->EU2(2 * i + 1), \ +- (TS1)Vk->ES2(2 * i + 1)); \ +- } \ +-} +- +-VMADDWOD_U_S(vmaddwod_h_bu_b, 16, H, UH, B, UB, DO_MUL) +-VMADDWOD_U_S(vmaddwod_w_hu_h, 32, W, UW, H, UH, DO_MUL) +-VMADDWOD_U_S(vmaddwod_d_wu_w, 64, D, UD, W, UW, DO_MUL) +- +-#define VDIV(NAME, BIT, E, DO_OP) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E(i) = DO_OP(Vj->E(i), Vk->E(i)); \ +- } \ +-} +- +-VDIV(vdiv_b, 8, B, DO_DIV) +-VDIV(vdiv_h, 16, H, DO_DIV) +-VDIV(vdiv_w, 32, W, DO_DIV) +-VDIV(vdiv_d, 64, D, DO_DIV) +-VDIV(vdiv_bu, 8, UB, DO_DIVU) +-VDIV(vdiv_hu, 16, UH, DO_DIVU) +-VDIV(vdiv_wu, 32, UW, DO_DIVU) +-VDIV(vdiv_du, 64, UD, DO_DIVU) +-VDIV(vmod_b, 8, B, DO_REM) +-VDIV(vmod_h, 16, H, DO_REM) +-VDIV(vmod_w, 32, W, DO_REM) +-VDIV(vmod_d, 64, D, DO_REM) +-VDIV(vmod_bu, 8, UB, DO_REMU) +-VDIV(vmod_hu, 16, UH, DO_REMU) +-VDIV(vmod_wu, 32, UW, DO_REMU) +-VDIV(vmod_du, 64, UD, DO_REMU) +- +-#define VSAT_S(NAME, BIT, E) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t max, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- typedef __typeof(Vd->E(0)) TD; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E(i) = Vj->E(i) > (TD)max ? (TD)max : \ +- Vj->E(i) < (TD)~max ? (TD)~max: Vj->E(i); \ +- } \ +-} +- +-VSAT_S(vsat_b, 8, B) +-VSAT_S(vsat_h, 16, H) +-VSAT_S(vsat_w, 32, W) +-VSAT_S(vsat_d, 64, D) +- +-#define VSAT_U(NAME, BIT, E) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t max, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- typedef __typeof(Vd->E(0)) TD; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E(i) = Vj->E(i) > (TD)max ? (TD)max : Vj->E(i); \ +- } \ +-} +- +-VSAT_U(vsat_bu, 8, UB) +-VSAT_U(vsat_hu, 16, UH) +-VSAT_U(vsat_wu, 32, UW) +-VSAT_U(vsat_du, 64, UD) +- +-#define VEXTH(NAME, BIT, E1, E2) \ +-void HELPER(NAME)(void *vd, void *vj, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- Vd->E1(j + i * ofs) = Vj->E2(j + ofs + ofs * 2 * i); \ +- } \ +- } \ +-} +- +-void HELPER(vexth_q_d)(void *vd, void *vj, uint32_t desc) +-{ +- int i; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- Vd->Q(i) = int128_makes64(Vj->D(2 * i + 1)); +- } +-} +- +-void HELPER(vexth_qu_du)(void *vd, void *vj, uint32_t desc) +-{ +- int i; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- Vd->Q(i) = int128_make64(Vj->UD(2 * i + 1)); +- } +-} +- +-VEXTH(vexth_h_b, 16, H, B) +-VEXTH(vexth_w_h, 32, W, H) +-VEXTH(vexth_d_w, 64, D, W) +-VEXTH(vexth_hu_bu, 16, UH, UB) +-VEXTH(vexth_wu_hu, 32, UW, UH) +-VEXTH(vexth_du_wu, 64, UD, UW) +- +-#define VEXT2XV(NAME, BIT, E1, E2) \ +-void HELPER(NAME)(void *vd, void *vj, uint32_t desc) \ +-{ \ +- int i; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- temp.E1(i) = Vj->E2(i); \ +- } \ +- *Vd = temp; \ +-} +- +-VEXT2XV(vext2xv_h_b, 16, H, B) +-VEXT2XV(vext2xv_w_b, 32, W, B) +-VEXT2XV(vext2xv_d_b, 64, D, B) +-VEXT2XV(vext2xv_w_h, 32, W, H) +-VEXT2XV(vext2xv_d_h, 64, D, H) +-VEXT2XV(vext2xv_d_w, 64, D, W) +-VEXT2XV(vext2xv_hu_bu, 16, UH, UB) +-VEXT2XV(vext2xv_wu_bu, 32, UW, UB) +-VEXT2XV(vext2xv_du_bu, 64, UD, UB) +-VEXT2XV(vext2xv_wu_hu, 32, UW, UH) +-VEXT2XV(vext2xv_du_hu, 64, UD, UH) +-VEXT2XV(vext2xv_du_wu, 64, UD, UW) +- +-DO_3OP(vsigncov_b, 8, B, DO_SIGNCOV) +-DO_3OP(vsigncov_h, 16, H, DO_SIGNCOV) +-DO_3OP(vsigncov_w, 32, W, DO_SIGNCOV) +-DO_3OP(vsigncov_d, 64, D, DO_SIGNCOV) +- +-static uint64_t do_vmskltz_b(int64_t val) +-{ +- uint64_t m = 0x8080808080808080ULL; +- uint64_t c = val & m; +- c |= c << 7; +- c |= c << 14; +- c |= c << 28; +- return c >> 56; +-} +- +-void HELPER(vmskltz_b)(void *vd, void *vj, uint32_t desc) +-{ +- int i; +- uint16_t temp = 0; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- temp = 0; +- temp = do_vmskltz_b(Vj->D(2 * i)); +- temp |= (do_vmskltz_b(Vj->D(2 * i + 1)) << 8); +- Vd->D(2 * i) = temp; +- Vd->D(2 * i + 1) = 0; +- } +-} +- +-static uint64_t do_vmskltz_h(int64_t val) +-{ +- uint64_t m = 0x8000800080008000ULL; +- uint64_t c = val & m; +- c |= c << 15; +- c |= c << 30; +- return c >> 60; +-} +- +-void HELPER(vmskltz_h)(void *vd, void *vj, uint32_t desc) +-{ +- int i; +- uint16_t temp = 0; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- temp = 0; +- temp = do_vmskltz_h(Vj->D(2 * i)); +- temp |= (do_vmskltz_h(Vj->D(2 * i + 1)) << 4); +- Vd->D(2 * i) = temp; +- Vd->D(2 * i + 1) = 0; +- } +-} +- +-static uint64_t do_vmskltz_w(int64_t val) +-{ +- uint64_t m = 0x8000000080000000ULL; +- uint64_t c = val & m; +- c |= c << 31; +- return c >> 62; +-} +- +-void HELPER(vmskltz_w)(void *vd, void *vj, uint32_t desc) +-{ +- int i; +- uint16_t temp = 0; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- temp = 0; +- temp = do_vmskltz_w(Vj->D(2 * i)); +- temp |= (do_vmskltz_w(Vj->D(2 * i + 1)) << 2); +- Vd->D(2 * i) = temp; +- Vd->D(2 * i + 1) = 0; +- } +-} +- +-static uint64_t do_vmskltz_d(int64_t val) +-{ +- return (uint64_t)val >> 63; +-} +-void HELPER(vmskltz_d)(void *vd, void *vj, uint32_t desc) +-{ +- int i; +- uint16_t temp = 0; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- temp = 0; +- temp = do_vmskltz_d(Vj->D(2 * i)); +- temp |= (do_vmskltz_d(Vj->D(2 * i + 1)) << 1); +- Vd->D(2 * i) = temp; +- Vd->D(2 * i + 1) = 0; +- } +-} +- +-void HELPER(vmskgez_b)(void *vd, void *vj, uint32_t desc) +-{ +- int i; +- uint16_t temp = 0; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- temp = 0; +- temp = do_vmskltz_b(Vj->D(2 * i)); +- temp |= (do_vmskltz_b(Vj->D(2 * i + 1)) << 8); +- Vd->D(2 * i) = (uint16_t)(~temp); +- Vd->D(2 * i + 1) = 0; +- } +-} +- +-static uint64_t do_vmskez_b(uint64_t a) +-{ +- uint64_t m = 0x7f7f7f7f7f7f7f7fULL; +- uint64_t c = ~(((a & m) + m) | a | m); +- c |= c << 7; +- c |= c << 14; +- c |= c << 28; +- return c >> 56; +-} +- +-void HELPER(vmsknz_b)(void *vd, void *vj, uint32_t desc) +-{ +- int i; +- uint16_t temp = 0; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- temp = 0; +- temp = do_vmskez_b(Vj->D(2 * i)); +- temp |= (do_vmskez_b(Vj->D(2 * i + 1)) << 8); +- Vd->D(2 * i) = (uint16_t)(~temp); +- Vd->D(2 * i + 1) = 0; +- } +-} +- +-void HELPER(vnori_b)(void *vd, void *vj, uint64_t imm, uint32_t desc) +-{ +- int i; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- +- for (i = 0; i < simd_oprsz(desc); i++) { +- Vd->B(i) = ~(Vj->B(i) | (uint8_t)imm); +- } +-} +- +-#define VSLLWIL(NAME, BIT, E1, E2) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- typedef __typeof(temp.E1(0)) TD; \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- temp.E1(j + ofs * i) = (TD)Vj->E2(j + ofs * 2 * i) << (imm % BIT); \ +- } \ +- } \ +- *Vd = temp; \ +-} +- +- +-void HELPER(vextl_q_d)(void *vd, void *vj, uint32_t desc) +-{ +- int i; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- Vd->Q(i) = int128_makes64(Vj->D(2 * i)); +- } +-} +- +-void HELPER(vextl_qu_du)(void *vd, void *vj, uint32_t desc) +-{ +- int i; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- Vd->Q(i) = int128_make64(Vj->UD(2 * i)); +- } +-} +- +-VSLLWIL(vsllwil_h_b, 16, H, B) +-VSLLWIL(vsllwil_w_h, 32, W, H) +-VSLLWIL(vsllwil_d_w, 64, D, W) +-VSLLWIL(vsllwil_hu_bu, 16, UH, UB) +-VSLLWIL(vsllwil_wu_hu, 32, UW, UH) +-VSLLWIL(vsllwil_du_wu, 64, UD, UW) +- +-#define do_vsrlr(E, T) \ +-static T do_vsrlr_ ##E(T s1, int sh) \ +-{ \ +- if (sh == 0) { \ +- return s1; \ +- } else { \ +- return (s1 >> sh) + ((s1 >> (sh - 1)) & 0x1); \ +- } \ +-} +- +-do_vsrlr(B, uint8_t) +-do_vsrlr(H, uint16_t) +-do_vsrlr(W, uint32_t) +-do_vsrlr(D, uint64_t) +- +-#define VSRLR(NAME, BIT, T, E) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E(i) = do_vsrlr_ ## E(Vj->E(i), ((T)Vk->E(i))%BIT); \ +- } \ +-} +- +-VSRLR(vsrlr_b, 8, uint8_t, B) +-VSRLR(vsrlr_h, 16, uint16_t, H) +-VSRLR(vsrlr_w, 32, uint32_t, W) +-VSRLR(vsrlr_d, 64, uint64_t, D) +- +-#define VSRLRI(NAME, BIT, E) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E(i) = do_vsrlr_ ## E(Vj->E(i), imm); \ +- } \ +-} +- +-VSRLRI(vsrlri_b, 8, B) +-VSRLRI(vsrlri_h, 16, H) +-VSRLRI(vsrlri_w, 32, W) +-VSRLRI(vsrlri_d, 64, D) +- +-#define do_vsrar(E, T) \ +-static T do_vsrar_ ##E(T s1, int sh) \ +-{ \ +- if (sh == 0) { \ +- return s1; \ +- } else { \ +- return (s1 >> sh) + ((s1 >> (sh - 1)) & 0x1); \ +- } \ +-} +- +-do_vsrar(B, int8_t) +-do_vsrar(H, int16_t) +-do_vsrar(W, int32_t) +-do_vsrar(D, int64_t) +- +-#define VSRAR(NAME, BIT, T, E) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E(i) = do_vsrar_ ## E(Vj->E(i), ((T)Vk->E(i))%BIT); \ +- } \ +-} +- +-VSRAR(vsrar_b, 8, uint8_t, B) +-VSRAR(vsrar_h, 16, uint16_t, H) +-VSRAR(vsrar_w, 32, uint32_t, W) +-VSRAR(vsrar_d, 64, uint64_t, D) +- +-#define VSRARI(NAME, BIT, E) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E(i) = do_vsrar_ ## E(Vj->E(i), imm); \ +- } \ +-} +- +-VSRARI(vsrari_b, 8, B) +-VSRARI(vsrari_h, 16, H) +-VSRARI(vsrari_w, 32, W) +-VSRARI(vsrari_d, 64, D) +- +-#define VSRLN(NAME, BIT, E1, E2) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- Vd->E1(j + ofs * 2 * i) = R_SHIFT(Vj->E2(j + ofs * i), \ +- Vk->E2(j + ofs * i) % BIT); \ +- } \ +- Vd->D(2 * i + 1) = 0; \ +- } \ +-} +- +-VSRLN(vsrln_b_h, 16, B, UH) +-VSRLN(vsrln_h_w, 32, H, UW) +-VSRLN(vsrln_w_d, 64, W, UD) +- +-#define VSRAN(NAME, BIT, E1, E2, E3) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- Vd->E1(j + ofs * 2 * i) = R_SHIFT(Vj->E2(j + ofs * i), \ +- Vk->E3(j + ofs * i) % BIT); \ +- } \ +- Vd->D(2 * i + 1) = 0; \ +- } \ +-} +- +-VSRAN(vsran_b_h, 16, B, H, UH) +-VSRAN(vsran_h_w, 32, H, W, UW) +-VSRAN(vsran_w_d, 64, W, D, UD) +- +-#define VSRLNI(NAME, BIT, E1, E2) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- temp.E1(j + ofs * 2 * i) = R_SHIFT(Vj->E2(j + ofs * i), imm); \ +- temp.E1(j + ofs * (2 * i + 1)) = R_SHIFT(Vd->E2(j + ofs * i), \ +- imm); \ +- } \ +- } \ +- *Vd = temp; \ +-} +- +-void HELPER(vsrlni_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +-{ +- int i; +- VReg temp = {}; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- +- for (i = 0; i < 2; i++) { +- temp.D(2 * i) = int128_getlo(int128_urshift(Vj->Q(i), imm % 128)); +- temp.D(2 * i +1) = int128_getlo(int128_urshift(Vd->Q(i), imm % 128)); +- } +- *Vd = temp; +-} +- +-VSRLNI(vsrlni_b_h, 16, B, UH) +-VSRLNI(vsrlni_h_w, 32, H, UW) +-VSRLNI(vsrlni_w_d, 64, W, UD) +- +-#define VSRANI(NAME, BIT, E1, E2) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- temp.E1(j + ofs * 2 * i) = R_SHIFT(Vj->E2(j + ofs * i), imm); \ +- temp.E1(j + ofs * (2 * i + 1)) = R_SHIFT(Vd->E2(j + ofs * i), \ +- imm); \ +- } \ +- } \ +- *Vd = temp; \ +-} +- +-void HELPER(vsrani_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +-{ +- int i; +- VReg temp = {}; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- +- for (i = 0; i < 2; i++) { +- temp.D(2 * i) = int128_getlo(int128_rshift(Vj->Q(i), imm % 128)); +- temp.D(2 * i + 1) = int128_getlo(int128_rshift(Vd->Q(i), imm % 128)); +- } +- *Vd = temp; +-} +- +-VSRANI(vsrani_b_h, 16, B, H) +-VSRANI(vsrani_h_w, 32, H, W) +-VSRANI(vsrani_w_d, 64, W, D) +- +-#define VSRLRN(NAME, BIT, E1, E2, E3) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- Vd->E1(j + ofs * 2 * i) = do_vsrlr_ ##E2(Vj->E2(j + ofs * i), \ +- Vk->E3(j + ofs * i) % BIT); \ +- } \ +- Vd->D(2 * i + 1) = 0; \ +- } \ +-} +- +-VSRLRN(vsrlrn_b_h, 16, B, H, UH) +-VSRLRN(vsrlrn_h_w, 32, H, W, UW) +-VSRLRN(vsrlrn_w_d, 64, W, D, UD) +- +-#define VSRARN(NAME, BIT, E1, E2, E3) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- Vd->E1(j + ofs * 2 * i) = do_vsrar_ ## E2(Vj->E2(j + ofs * i), \ +- Vk->E3(j + ofs * i) % BIT); \ +- } \ +- Vd->D(2 * i + 1) = 0; \ +- } \ +-} +- +-VSRARN(vsrarn_b_h, 16, B, H, UH) +-VSRARN(vsrarn_h_w, 32, H, W, UW) +-VSRARN(vsrarn_w_d, 64, W, D, UD) +- +-#define VSRLRNI(NAME, BIT, E1, E2) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- temp.E1(j + ofs * 2 * i) = do_vsrlr_ ## E2(Vj->E2(j + ofs * i), imm); \ +- temp.E1(j + ofs * (2 * i + 1)) = do_vsrlr_ ## E2(Vd->E2(j + ofs * i), \ +- imm); \ +- } \ +- } \ +- *Vd = temp; \ +-} +- +-void HELPER(vsrlrni_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +-{ +- int i; +- VReg temp = {}; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- Int128 r[4]; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- if (imm == 0) { +- temp.D(2 * i) = int128_getlo(Vj->Q(i)); +- temp.D(2 * i + 1) = int128_getlo(Vd->Q(i)); +- } else { +- r[2 * i] = int128_and(int128_urshift(Vj->Q(i), (imm - 1)), +- int128_one()); +- r[2 * i + 1] = int128_and(int128_urshift(Vd->Q(i), (imm - 1)), +- int128_one()); +- temp.D(2 * i) = int128_getlo(int128_add(int128_urshift(Vj->Q(i), +- imm), r[2 * i])); +- temp.D(2 * i + 1) = int128_getlo(int128_add(int128_urshift(Vd->Q(i), +- imm), r[ 2 * i + 1])); +- } +- } +- *Vd = temp; +-} +- +-VSRLRNI(vsrlrni_b_h, 16, B, H) +-VSRLRNI(vsrlrni_h_w, 32, H, W) +-VSRLRNI(vsrlrni_w_d, 64, W, D) +- +-#define VSRARNI(NAME, BIT, E1, E2) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- temp.E1(j + ofs * 2 * i) = do_vsrar_ ## E2(Vj->E2(j + ofs * i), imm); \ +- temp.E1(j + ofs * (2 * i + 1)) = do_vsrar_ ## E2(Vd->E2(j + ofs * i), \ +- imm); \ +- } \ +- } \ +- *Vd = temp; \ +-} +- +-void HELPER(vsrarni_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +-{ +- int i; +- VReg temp = {}; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- Int128 r[4]; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- if (imm == 0) { +- temp.D(2 * i) = int128_getlo(Vj->Q(i)); +- temp.D(2 * i + 1) = int128_getlo(Vd->Q(i)); +- } else { +- r[2 * i] = int128_and(int128_rshift(Vj->Q(i), (imm - 1)), +- int128_one()); +- r[2 * i + 1] = int128_and(int128_rshift(Vd->Q(i), (imm - 1)), +- int128_one()); +- temp.D(2 * i) = int128_getlo(int128_add(int128_rshift(Vj->Q(i), +- imm), r[2 * i])); +- temp.D(2 * i + 1) = int128_getlo(int128_add(int128_rshift(Vd->Q(i), +- imm), r[2 * i + 1])); +- } +- } +- *Vd = temp; +-} +- +-VSRARNI(vsrarni_b_h, 16, B, H) +-VSRARNI(vsrarni_h_w, 32, H, W) +-VSRARNI(vsrarni_w_d, 64, W, D) +- +-#define SSRLNS(NAME, T1, T2, T3) \ +-static T1 do_ssrlns_ ## NAME(T2 e2, int sa, int sh) \ +-{ \ +- T1 shft_res; \ +- if (sa == 0) { \ +- shft_res = e2; \ +- } else { \ +- shft_res = (((T1)e2) >> sa); \ +- } \ +- T3 mask; \ +- mask = (1ull << sh) -1; \ +- if (shft_res > mask) { \ +- return mask; \ +- } else { \ +- return shft_res; \ +- } \ +-} +- +-SSRLNS(B, uint16_t, int16_t, uint8_t) +-SSRLNS(H, uint32_t, int32_t, uint16_t) +-SSRLNS(W, uint64_t, int64_t, uint32_t) +- +-#define VSSRLN(NAME, BIT, E1, E2, E3) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- Vd->E1(j + ofs * 2 * i) = do_ssrlns_ ## E1(Vj->E2(j + ofs * i), \ +- Vk->E3(j + ofs * i) % BIT, \ +- BIT / 2 - 1); \ +- } \ +- Vd->D(2 * i + 1) = 0; \ +- } \ +-} +- +-VSSRLN(vssrln_b_h, 16, B, H, UH) +-VSSRLN(vssrln_h_w, 32, H, W, UW) +-VSSRLN(vssrln_w_d, 64, W, D, UD) +- +-#define SSRANS(E, T1, T2) \ +-static T1 do_ssrans_ ## E(T1 e2, int sa, int sh) \ +-{ \ +- T1 shft_res; \ +- if (sa == 0) { \ +- shft_res = e2; \ +- } else { \ +- shft_res = e2 >> sa; \ +- } \ +- T2 mask; \ +- mask = (1ll << sh) - 1; \ +- if (shft_res > mask) { \ +- return mask; \ +- } else if (shft_res < -(mask + 1)) { \ +- return ~mask; \ +- } else { \ +- return shft_res; \ +- } \ +-} +- +-SSRANS(B, int16_t, int8_t) +-SSRANS(H, int32_t, int16_t) +-SSRANS(W, int64_t, int32_t) +- +-#define VSSRAN(NAME, BIT, E1, E2, E3) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- Vd->E1(j + ofs * 2 * i) = do_ssrans_ ## E1(Vj->E2(j + ofs * i), \ +- Vk->E3(j + ofs * i) % BIT, \ +- BIT / 2 - 1); \ +- } \ +- Vd->D(2 * i + 1) = 0; \ +- } \ +-} +- +-VSSRAN(vssran_b_h, 16, B, H, UH) +-VSSRAN(vssran_h_w, 32, H, W, UW) +-VSSRAN(vssran_w_d, 64, W, D, UD) +- +-#define SSRLNU(E, T1, T2, T3) \ +-static T1 do_ssrlnu_ ## E(T3 e2, int sa, int sh) \ +-{ \ +- T1 shft_res; \ +- if (sa == 0) { \ +- shft_res = e2; \ +- } else { \ +- shft_res = (((T1)e2) >> sa); \ +- } \ +- T2 mask; \ +- mask = (1ull << sh) - 1; \ +- if (shft_res > mask) { \ +- return mask; \ +- } else { \ +- return shft_res; \ +- } \ +-} +- +-SSRLNU(B, uint16_t, uint8_t, int16_t) +-SSRLNU(H, uint32_t, uint16_t, int32_t) +-SSRLNU(W, uint64_t, uint32_t, int64_t) +- +-#define VSSRLNU(NAME, BIT, E1, E2, E3) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- Vd->E1(j + ofs * 2 * i) = do_ssrlnu_ ## E1(Vj->E2(j + ofs * i), \ +- Vk->E3(j + ofs * i) % BIT, \ +- BIT / 2); \ +- } \ +- Vd->D(2 * i + 1) = 0; \ +- } \ +-} +- +-VSSRLNU(vssrln_bu_h, 16, B, H, UH) +-VSSRLNU(vssrln_hu_w, 32, H, W, UW) +-VSSRLNU(vssrln_wu_d, 64, W, D, UD) +- +-#define SSRANU(E, T1, T2, T3) \ +-static T1 do_ssranu_ ## E(T3 e2, int sa, int sh) \ +-{ \ +- T1 shft_res; \ +- if (sa == 0) { \ +- shft_res = e2; \ +- } else { \ +- shft_res = e2 >> sa; \ +- } \ +- if (e2 < 0) { \ +- shft_res = 0; \ +- } \ +- T2 mask; \ +- mask = (1ull << sh) - 1; \ +- if (shft_res > mask) { \ +- return mask; \ +- } else { \ +- return shft_res; \ +- } \ +-} +- +-SSRANU(B, uint16_t, uint8_t, int16_t) +-SSRANU(H, uint32_t, uint16_t, int32_t) +-SSRANU(W, uint64_t, uint32_t, int64_t) +- +-#define VSSRANU(NAME, BIT, E1, E2, E3) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- Vd->E1(j + ofs * 2 * i) = do_ssranu_ ## E1(Vj->E2(j + ofs * i), \ +- Vk->E3(j + ofs * i) % BIT, \ +- BIT / 2); \ +- } \ +- Vd->D(2 * i + 1) = 0; \ +- } \ +-} +- +-VSSRANU(vssran_bu_h, 16, B, H, UH) +-VSSRANU(vssran_hu_w, 32, H, W, UW) +-VSSRANU(vssran_wu_d, 64, W, D, UD) +- +-#define VSSRLNI(NAME, BIT, E1, E2) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- temp.E1(j + ofs * 2 * i) = do_ssrlns_ ## E1(Vj->E2(j + ofs * i), \ +- imm, BIT / 2 - 1); \ +- temp.E1(j + ofs * (2 * i + 1)) = do_ssrlns_ ## E1(Vd->E2(j + ofs * i), \ +- imm, BIT / 2 - 1); \ +- } \ +- } \ +- *Vd = temp; \ +-} +- +-static void do_vssrlni_q(VReg *Vd, VReg *Vj, +- uint64_t imm, int idx, Int128 mask) +-{ +- Int128 shft_res1, shft_res2; +- +- if (imm == 0) { +- shft_res1 = Vj->Q(idx); +- shft_res2 = Vd->Q(idx); +- } else { +- shft_res1 = int128_urshift(Vj->Q(idx), imm); +- shft_res2 = int128_urshift(Vd->Q(idx), imm); +- } +- +- if (int128_ult(mask, shft_res1)) { +- Vd->D(idx * 2) = int128_getlo(mask); +- }else { +- Vd->D(idx * 2) = int128_getlo(shft_res1); +- } +- +- if (int128_ult(mask, shft_res2)) { +- Vd->D(idx * 2 + 1) = int128_getlo(mask); +- }else { +- Vd->D(idx * 2 + 1) = int128_getlo(shft_res2); +- } +-} +- +-void HELPER(vssrlni_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +-{ +- int i; +- Int128 mask; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- mask = int128_sub(int128_lshift(int128_one(), 63), int128_one()); +- +- for (i = 0; i < oprsz / 16; i++) { +- do_vssrlni_q(Vd, Vj, imm, i, mask); +- } +-} +- +-VSSRLNI(vssrlni_b_h, 16, B, H) +-VSSRLNI(vssrlni_h_w, 32, H, W) +-VSSRLNI(vssrlni_w_d, 64, W, D) +- +-#define VSSRANI(NAME, BIT, E1, E2) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- temp.E1(j + ofs * 2 * i) = do_ssrans_ ## E1(Vj->E2(j + ofs * i), \ +- imm, BIT / 2 - 1); \ +- temp.E1(j + ofs * (2 * i + 1)) = do_ssrans_ ## E1(Vd->E2(j + ofs * i), \ +- imm, BIT / 2 - 1); \ +- } \ +- } \ +- *Vd = temp; \ +-} +- +-static void do_vssrani_d_q(VReg *Vd, VReg *Vj, +- uint64_t imm, int idx, Int128 mask, Int128 min) +-{ +- Int128 shft_res1, shft_res2; +- +- if (imm == 0) { +- shft_res1 = Vj->Q(idx); +- shft_res2 = Vd->Q(idx); +- } else { +- shft_res1 = int128_rshift(Vj->Q(idx), imm); +- shft_res2 = int128_rshift(Vd->Q(idx), imm); +- } +- +- if (int128_gt(shft_res1, mask)) { +- Vd->D(idx * 2) = int128_getlo(mask); +- } else if (int128_lt(shft_res1, int128_neg(min))) { +- Vd->D(idx * 2) = int128_getlo(min); +- } else { +- Vd->D(idx * 2) = int128_getlo(shft_res1); +- } +- +- if (int128_gt(shft_res2, mask)) { +- Vd->D(idx * 2 + 1) = int128_getlo(mask); +- } else if (int128_lt(shft_res2, int128_neg(min))) { +- Vd->D(idx * 2 + 1) = int128_getlo(min); +- } else { +- Vd->D(idx * 2 + 1) = int128_getlo(shft_res2); +- } +-} +- +-void HELPER(vssrani_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +-{ +- int i; +- Int128 mask, min; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- mask = int128_sub(int128_lshift(int128_one(), 63), int128_one()); +- min = int128_lshift(int128_one(), 63); +- +- for (i = 0; i < oprsz / 16; i++) { +- do_vssrani_d_q(Vd, Vj, imm, i, mask, min); +- } +-} +- +- +-VSSRANI(vssrani_b_h, 16, B, H) +-VSSRANI(vssrani_h_w, 32, H, W) +-VSSRANI(vssrani_w_d, 64, W, D) +- +-#define VSSRLNUI(NAME, BIT, E1, E2) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- temp.E1(j + ofs * 2 * i) = do_ssrlnu_ ## E1(Vj->E2(j + ofs * i), \ +- imm, BIT / 2); \ +- temp.E1(j + ofs * (2 * i + 1)) = do_ssrlnu_ ## E1(Vd->E2(j + ofs * i), \ +- imm, BIT / 2); \ +- } \ +- } \ +- *Vd = temp; \ +-} +- +-void HELPER(vssrlni_du_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +-{ +- int i; +- Int128 mask; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- mask = int128_sub(int128_lshift(int128_one(), 64), int128_one()); +- +- for (i = 0; i < oprsz / 16; i++) { +- do_vssrlni_q(Vd, Vj, imm, i, mask); +- } +-} +- +-VSSRLNUI(vssrlni_bu_h, 16, B, H) +-VSSRLNUI(vssrlni_hu_w, 32, H, W) +-VSSRLNUI(vssrlni_wu_d, 64, W, D) +- +-#define VSSRANUI(NAME, BIT, E1, E2) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- temp.E1(j + ofs * 2 * i) = do_ssranu_ ## E1(Vj->E2(j + ofs * i), \ +- imm, BIT / 2); \ +- temp.E1(j + ofs * (2 * i + 1)) = do_ssranu_ ## E1(Vd->E2(j + ofs * i), \ +- imm, BIT / 2); \ +- } \ +- } \ +- *Vd = temp; \ +-} +- +-static void do_vssrani_du_q(VReg *Vd, VReg *Vj, +- uint64_t imm, int idx, Int128 mask) +-{ +- Int128 shft_res1, shft_res2; +- +- if (imm == 0) { +- shft_res1 = Vj->Q(idx); +- shft_res2 = Vd->Q(idx); +- } else { +- shft_res1 = int128_rshift(Vj->Q(idx), imm); +- shft_res2 = int128_rshift(Vd->Q(idx), imm); +- } +- +- if (int128_lt(Vj->Q(idx), int128_zero())) { +- shft_res1 = int128_zero(); +- } +- +- if (int128_lt(Vd->Q(idx), int128_zero())) { +- shft_res2 = int128_zero(); +- } +- if (int128_ult(mask, shft_res1)) { +- Vd->D(idx * 2) = int128_getlo(mask); +- }else { +- Vd->D(idx * 2) = int128_getlo(shft_res1); +- } +- +- if (int128_ult(mask, shft_res2)) { +- Vd->D(idx * 2 + 1) = int128_getlo(mask); +- }else { +- Vd->D(idx * 2 + 1) = int128_getlo(shft_res2); +- } +- +-} +- +-void HELPER(vssrani_du_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +-{ +- int i; +- Int128 mask; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- mask = int128_sub(int128_lshift(int128_one(), 64), int128_one()); +- +- for (i = 0; i < oprsz / 16; i++) { +- do_vssrani_du_q(Vd, Vj, imm, i, mask); +- } +-} +- +-VSSRANUI(vssrani_bu_h, 16, B, H) +-VSSRANUI(vssrani_hu_w, 32, H, W) +-VSSRANUI(vssrani_wu_d, 64, W, D) +- +-#define SSRLRNS(E1, E2, T1, T2, T3) \ +-static T1 do_ssrlrns_ ## E1(T2 e2, int sa, int sh) \ +-{ \ +- T1 shft_res; \ +- \ +- shft_res = do_vsrlr_ ## E2(e2, sa); \ +- T1 mask; \ +- mask = (1ull << sh) - 1; \ +- if (shft_res > mask) { \ +- return mask; \ +- } else { \ +- return shft_res; \ +- } \ +-} +- +-SSRLRNS(B, H, uint16_t, int16_t, uint8_t) +-SSRLRNS(H, W, uint32_t, int32_t, uint16_t) +-SSRLRNS(W, D, uint64_t, int64_t, uint32_t) +- +-#define VSSRLRN(NAME, BIT, E1, E2, E3) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- Vd->E1(j + ofs * 2 * i) = do_ssrlrns_ ## E1(Vj->E2(j + ofs * i), \ +- Vk->E3(j + ofs * i) % BIT, \ +- BIT / 2 - 1); \ +- } \ +- Vd->D(2 * i + 1) = 0; \ +- } \ +-} +- +-VSSRLRN(vssrlrn_b_h, 16, B, H, UH) +-VSSRLRN(vssrlrn_h_w, 32, H, W, UW) +-VSSRLRN(vssrlrn_w_d, 64, W, D, UD) +- +-#define SSRARNS(E1, E2, T1, T2) \ +-static T1 do_ssrarns_ ## E1(T1 e2, int sa, int sh) \ +-{ \ +- T1 shft_res; \ +- \ +- shft_res = do_vsrar_ ## E2(e2, sa); \ +- T2 mask; \ +- mask = (1ll << sh) - 1; \ +- if (shft_res > mask) { \ +- return mask; \ +- } else if (shft_res < -(mask +1)) { \ +- return ~mask; \ +- } else { \ +- return shft_res; \ +- } \ +-} +- +-SSRARNS(B, H, int16_t, int8_t) +-SSRARNS(H, W, int32_t, int16_t) +-SSRARNS(W, D, int64_t, int32_t) +- +-#define VSSRARN(NAME, BIT, E1, E2, E3) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- Vd->E1(j + ofs * 2 * i) = do_ssrarns_ ## E1(Vj->E2(j + ofs * i), \ +- Vk->E3(j + ofs * i) % BIT, \ +- BIT/ 2 - 1); \ +- } \ +- Vd->D(2 * i + 1) = 0; \ +- } \ +-} +- +-VSSRARN(vssrarn_b_h, 16, B, H, UH) +-VSSRARN(vssrarn_h_w, 32, H, W, UW) +-VSSRARN(vssrarn_w_d, 64, W, D, UD) +- +-#define SSRLRNU(E1, E2, T1, T2, T3) \ +-static T1 do_ssrlrnu_ ## E1(T3 e2, int sa, int sh) \ +-{ \ +- T1 shft_res; \ +- \ +- shft_res = do_vsrlr_ ## E2(e2, sa); \ +- \ +- T2 mask; \ +- mask = (1ull << sh) - 1; \ +- if (shft_res > mask) { \ +- return mask; \ +- } else { \ +- return shft_res; \ +- } \ +-} +- +-SSRLRNU(B, H, uint16_t, uint8_t, int16_t) +-SSRLRNU(H, W, uint32_t, uint16_t, int32_t) +-SSRLRNU(W, D, uint64_t, uint32_t, int64_t) +- +-#define VSSRLRNU(NAME, BIT, E1, E2, E3) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- Vd->E1(j + ofs * 2 * i) = do_ssrlrnu_ ## E1(Vj->E2(j + ofs * i), \ +- Vk->E3(j + ofs * i) % BIT, \ +- BIT / 2); \ +- } \ +- Vd->D(2 * i + 1) = 0; \ +- } \ +-} +- +-VSSRLRNU(vssrlrn_bu_h, 16, B, H, UH) +-VSSRLRNU(vssrlrn_hu_w, 32, H, W, UW) +-VSSRLRNU(vssrlrn_wu_d, 64, W, D, UD) +- +-#define SSRARNU(E1, E2, T1, T2, T3) \ +-static T1 do_ssrarnu_ ## E1(T3 e2, int sa, int sh) \ +-{ \ +- T1 shft_res; \ +- \ +- if (e2 < 0) { \ +- shft_res = 0; \ +- } else { \ +- shft_res = do_vsrar_ ## E2(e2, sa); \ +- } \ +- T2 mask; \ +- mask = (1ull << sh) - 1; \ +- if (shft_res > mask) { \ +- return mask; \ +- } else { \ +- return shft_res; \ +- } \ +-} +- +-SSRARNU(B, H, uint16_t, uint8_t, int16_t) +-SSRARNU(H, W, uint32_t, uint16_t, int32_t) +-SSRARNU(W, D, uint64_t, uint32_t, int64_t) +- +-#define VSSRARNU(NAME, BIT, E1, E2, E3) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- Vd->E1(j + ofs * 2 * i) = do_ssrarnu_ ## E1(Vj->E2(j + ofs * i), \ +- Vk->E3(j + ofs * i) % BIT, \ +- BIT / 2); \ +- } \ +- Vd->D(2 * i + 1) = 0; \ +- } \ +-} +- +-VSSRARNU(vssrarn_bu_h, 16, B, H, UH) +-VSSRARNU(vssrarn_hu_w, 32, H, W, UW) +-VSSRARNU(vssrarn_wu_d, 64, W, D, UD) +- +-#define VSSRLRNI(NAME, BIT, E1, E2) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- temp.E1(j + ofs * 2 * i) = do_ssrlrns_ ## E1(Vj->E2(j + ofs * i), \ +- imm, BIT / 2 - 1); \ +- temp.E1(j + ofs * (2 * i + 1)) = do_ssrlrns_ ## E1(Vd->E2(j + ofs * i), \ +- imm, BIT / 2 - 1); \ +- } \ +- } \ +- *Vd = temp; \ +-} +- +-static void do_vssrlrni_q(VReg *Vd, VReg * Vj, +- uint64_t imm, int idx, Int128 mask) +-{ +- Int128 shft_res1, shft_res2, r1, r2; +- if (imm == 0) { +- shft_res1 = Vj->Q(idx); +- shft_res2 = Vd->Q(idx); +- } else { +- r1 = int128_and(int128_urshift(Vj->Q(idx), (imm - 1)), int128_one()); +- r2 = int128_and(int128_urshift(Vd->Q(idx), (imm - 1)), int128_one()); +- shft_res1 = (int128_add(int128_urshift(Vj->Q(idx), imm), r1)); +- shft_res2 = (int128_add(int128_urshift(Vd->Q(idx), imm), r2)); +- } +- +- if (int128_ult(mask, shft_res1)) { +- Vd->D(idx * 2) = int128_getlo(mask); +- }else { +- Vd->D(idx * 2) = int128_getlo(shft_res1); +- } +- +- if (int128_ult(mask, shft_res2)) { +- Vd->D(idx * 2 + 1) = int128_getlo(mask); +- }else { +- Vd->D(idx * 2 + 1) = int128_getlo(shft_res2); +- } +-} +- +-void HELPER(vssrlrni_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +-{ +- int i; +- Int128 mask; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- mask = int128_sub(int128_lshift(int128_one(), 63), int128_one()); +- +- for (i = 0; i < oprsz / 16; i++) { +- do_vssrlrni_q(Vd, Vj, imm, i, mask); +- } +-} +- +-VSSRLRNI(vssrlrni_b_h, 16, B, H) +-VSSRLRNI(vssrlrni_h_w, 32, H, W) +-VSSRLRNI(vssrlrni_w_d, 64, W, D) +- +-#define VSSRARNI(NAME, BIT, E1, E2) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- temp.E1(j + ofs * 2 * i) = do_ssrarns_ ## E1(Vj->E2(j + ofs * i), \ +- imm, BIT / 2 - 1); \ +- temp.E1(j + ofs * (2 * i + 1)) = do_ssrarns_ ## E1(Vd->E2(j + ofs * i), \ +- imm, BIT / 2 - 1); \ +- } \ +- } \ +- *Vd = temp; \ +-} +- +-static void do_vssrarni_d_q(VReg *Vd, VReg *Vj, +- uint64_t imm, int idx, Int128 mask1, Int128 mask2) +-{ +- Int128 shft_res1, shft_res2, r1, r2; +- +- if (imm == 0) { +- shft_res1 = Vj->Q(idx); +- shft_res2 = Vd->Q(idx); +- } else { +- r1 = int128_and(int128_rshift(Vj->Q(idx), (imm - 1)), int128_one()); +- r2 = int128_and(int128_rshift(Vd->Q(idx), (imm - 1)), int128_one()); +- shft_res1 = int128_add(int128_rshift(Vj->Q(idx), imm), r1); +- shft_res2 = int128_add(int128_rshift(Vd->Q(idx), imm), r2); +- } +- if (int128_gt(shft_res1, mask1)) { +- Vd->D(idx * 2) = int128_getlo(mask1); +- } else if (int128_lt(shft_res1, int128_neg(mask2))) { +- Vd->D(idx * 2) = int128_getlo(mask2); +- } else { +- Vd->D(idx * 2) = int128_getlo(shft_res1); +- } +- +- if (int128_gt(shft_res2, mask1)) { +- Vd->D(idx * 2 + 1) = int128_getlo(mask1); +- } else if (int128_lt(shft_res2, int128_neg(mask2))) { +- Vd->D(idx * 2 + 1) = int128_getlo(mask2); +- } else { +- Vd->D(idx * 2 + 1) = int128_getlo(shft_res2); +- } +-} +- +-void HELPER(vssrarni_d_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +-{ +- int i; +- Int128 mask1, mask2; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- mask1 = int128_sub(int128_lshift(int128_one(), 63), int128_one()); +- mask2 = int128_lshift(int128_one(), 63); +- +- for (i = 0; i < oprsz / 16; i++) { +- do_vssrarni_d_q(Vd, Vj, imm, i, mask1, mask2); +- } +-} +- +-VSSRARNI(vssrarni_b_h, 16, B, H) +-VSSRARNI(vssrarni_h_w, 32, H, W) +-VSSRARNI(vssrarni_w_d, 64, W, D) +- +-#define VSSRLRNUI(NAME, BIT, E1, E2) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- temp.E1(j + ofs * 2 * i) = do_ssrlrnu_ ## E1(Vj->E2(j + ofs * i), \ +- imm, BIT / 2); \ +- temp.E1(j + ofs * (2 * i + 1)) = do_ssrlrnu_ ## E1(Vd->E2(j + ofs * i), \ +- imm, BIT / 2); \ +- } \ +- } \ +- *Vd = temp; \ +-} +- +-void HELPER(vssrlrni_du_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +-{ +- int i; +- Int128 mask; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- mask = int128_sub(int128_lshift(int128_one(), 64), int128_one()); +- +- for (i = 0; i < oprsz / 16; i++) { +- do_vssrlrni_q(Vd, Vj, imm, i, mask); +- } +-} +- +-VSSRLRNUI(vssrlrni_bu_h, 16, B, H) +-VSSRLRNUI(vssrlrni_hu_w, 32, H, W) +-VSSRLRNUI(vssrlrni_wu_d, 64, W, D) +- +-#define VSSRARNUI(NAME, BIT, E1, E2) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- temp.E1(j + ofs * 2 * i) = do_ssrarnu_ ## E1(Vj->E2(j + ofs * i), \ +- imm, BIT / 2); \ +- temp.E1(j + ofs * (2 * i + 1)) = do_ssrarnu_ ## E1(Vd->E2(j + ofs * i), \ +- imm, BIT / 2); \ +- } \ +- } \ +- *Vd = temp; \ +-} +- +-static void do_vssrarni_du_q(VReg *Vd, VReg *Vj, +- uint64_t imm, int idx, Int128 mask1, Int128 mask2) +-{ +- Int128 shft_res1, shft_res2, r1, r2; +- +- if (imm == 0) { +- shft_res1 = Vj->Q(idx); +- shft_res2 = Vd->Q(idx); +- } else { +- r1 = int128_and(int128_rshift(Vj->Q(idx), (imm - 1)), int128_one()); +- r2 = int128_and(int128_rshift(Vd->Q(idx), (imm - 1)), int128_one()); +- shft_res1 = int128_add(int128_rshift(Vj->Q(idx), imm), r1); +- shft_res2 = int128_add(int128_rshift(Vd->Q(idx), imm), r2); +- } +- +- if (int128_lt(Vj->Q(idx), int128_zero())) { +- shft_res1 = int128_zero(); +- } +- if (int128_lt(Vd->Q(idx), int128_zero())) { +- shft_res2 = int128_zero(); +- } +- +- if (int128_gt(shft_res1, mask1)) { +- Vd->D(idx * 2) = int128_getlo(mask1); +- } else if (int128_lt(shft_res1, int128_neg(mask2))) { +- Vd->D(idx * 2) = int128_getlo(mask2); +- } else { +- Vd->D(idx * 2) = int128_getlo(shft_res1); +- } +- +- if (int128_gt(shft_res2, mask1)) { +- Vd->D(idx * 2 + 1) = int128_getlo(mask1); +- } else if (int128_lt(shft_res2, int128_neg(mask2))) { +- Vd->D(idx * 2 + 1) = int128_getlo(mask2); +- } else { +- Vd->D(idx * 2 + 1) = int128_getlo(shft_res2); +- } +-} +- +-void HELPER(vssrarni_du_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +-{ +- int i; +- Int128 mask1, mask2; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- mask1 = int128_sub(int128_lshift(int128_one(), 64), int128_one()); +- mask2 = int128_lshift(int128_one(), 64); +- +- for (i = 0; i < oprsz / 16; i++) { +- do_vssrarni_du_q(Vd, Vj, imm, i, mask1, mask2); +- } +-} +- +-VSSRARNUI(vssrarni_bu_h, 16, B, H) +-VSSRARNUI(vssrarni_hu_w, 32, H, W) +-VSSRARNUI(vssrarni_wu_d, 64, W, D) +- +-#define DO_2OP(NAME, BIT, E, DO_OP) \ +-void HELPER(NAME)(void *vd, void *vj, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) \ +- { \ +- Vd->E(i) = DO_OP(Vj->E(i)); \ +- } \ +-} +- +-DO_2OP(vclo_b, 8, UB, DO_CLO_B) +-DO_2OP(vclo_h, 16, UH, DO_CLO_H) +-DO_2OP(vclo_w, 32, UW, DO_CLO_W) +-DO_2OP(vclo_d, 64, UD, DO_CLO_D) +-DO_2OP(vclz_b, 8, UB, DO_CLZ_B) +-DO_2OP(vclz_h, 16, UH, DO_CLZ_H) +-DO_2OP(vclz_w, 32, UW, DO_CLZ_W) +-DO_2OP(vclz_d, 64, UD, DO_CLZ_D) +- +-#define VPCNT(NAME, BIT, E, FN) \ +-void HELPER(NAME)(void *vd, void *vj, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) \ +- { \ +- Vd->E(i) = FN(Vj->E(i)); \ +- } \ +-} +- +-VPCNT(vpcnt_b, 8, UB, ctpop8) +-VPCNT(vpcnt_h, 16, UH, ctpop16) +-VPCNT(vpcnt_w, 32, UW, ctpop32) +-VPCNT(vpcnt_d, 64, UD, ctpop64) +- +-#define DO_BIT(NAME, BIT, E, DO_OP) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E(i) = DO_OP(Vj->E(i), Vk->E(i)%BIT); \ +- } \ +-} +- +-DO_BIT(vbitclr_b, 8, UB, DO_BITCLR) +-DO_BIT(vbitclr_h, 16, UH, DO_BITCLR) +-DO_BIT(vbitclr_w, 32, UW, DO_BITCLR) +-DO_BIT(vbitclr_d, 64, UD, DO_BITCLR) +-DO_BIT(vbitset_b, 8, UB, DO_BITSET) +-DO_BIT(vbitset_h, 16, UH, DO_BITSET) +-DO_BIT(vbitset_w, 32, UW, DO_BITSET) +-DO_BIT(vbitset_d, 64, UD, DO_BITSET) +-DO_BIT(vbitrev_b, 8, UB, DO_BITREV) +-DO_BIT(vbitrev_h, 16, UH, DO_BITREV) +-DO_BIT(vbitrev_w, 32, UW, DO_BITREV) +-DO_BIT(vbitrev_d, 64, UD, DO_BITREV) +- +-#define DO_BITI(NAME, BIT, E, DO_OP) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E(i) = DO_OP(Vj->E(i), imm); \ +- } \ +-} +- +-DO_BITI(vbitclri_b, 8, UB, DO_BITCLR) +-DO_BITI(vbitclri_h, 16, UH, DO_BITCLR) +-DO_BITI(vbitclri_w, 32, UW, DO_BITCLR) +-DO_BITI(vbitclri_d, 64, UD, DO_BITCLR) +-DO_BITI(vbitseti_b, 8, UB, DO_BITSET) +-DO_BITI(vbitseti_h, 16, UH, DO_BITSET) +-DO_BITI(vbitseti_w, 32, UW, DO_BITSET) +-DO_BITI(vbitseti_d, 64, UD, DO_BITSET) +-DO_BITI(vbitrevi_b, 8, UB, DO_BITREV) +-DO_BITI(vbitrevi_h, 16, UH, DO_BITREV) +-DO_BITI(vbitrevi_w, 32, UW, DO_BITREV) +-DO_BITI(vbitrevi_d, 64, UD, DO_BITREV) +- +-#define VFRSTP(NAME, BIT, MASK, E) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i, j, m, ofs; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- m = Vk->E(i * ofs) & MASK; \ +- for (j = 0; j < ofs; j++) { \ +- if (Vj->E(j + ofs * i) < 0) { \ +- break; \ +- } \ +- } \ +- Vd->E(m + i * ofs) = j; \ +- } \ +-} +- +-VFRSTP(vfrstp_b, 8, 0xf, B) +-VFRSTP(vfrstp_h, 16, 0x7, H) +- +-#define VFRSTPI(NAME, BIT, E) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +-{ \ +- int i, j, m, ofs; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- m = imm % ofs; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- if (Vj->E(j + ofs * i) < 0) { \ +- break; \ +- } \ +- } \ +- Vd->E(m + i * ofs) = j; \ +- } \ +-} +- +-VFRSTPI(vfrstpi_b, 8, B) +-VFRSTPI(vfrstpi_h, 16, H) +- +-static void vec_update_fcsr0_mask(CPULoongArchState *env, +- uintptr_t pc, int mask) +-{ +- int flags = get_float_exception_flags(&env->fp_status); +- +- set_float_exception_flags(0, &env->fp_status); +- +- flags &= ~mask; +- +- if (flags) { +- flags = ieee_ex_to_loongarch(flags); +- UPDATE_FP_CAUSE(env->fcsr0, flags); +- } +- +- if (GET_FP_ENABLES(env->fcsr0) & flags) { +- do_raise_exception(env, EXCCODE_FPE, pc); +- } else { +- UPDATE_FP_FLAGS(env->fcsr0, flags); +- } +-} +- +-static void vec_update_fcsr0(CPULoongArchState *env, uintptr_t pc) +-{ +- vec_update_fcsr0_mask(env, pc, 0); +-} +- +-static inline void vec_clear_cause(CPULoongArchState *env) +-{ +- SET_FP_CAUSE(env->fcsr0, 0); +-} +- +-#define DO_3OP_F(NAME, BIT, E, FN) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, \ +- CPULoongArchState *env, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- vec_clear_cause(env); \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E(i) = FN(Vj->E(i), Vk->E(i), &env->fp_status); \ +- vec_update_fcsr0(env, GETPC()); \ +- } \ +-} +- +-DO_3OP_F(vfadd_s, 32, UW, float32_add) +-DO_3OP_F(vfadd_d, 64, UD, float64_add) +-DO_3OP_F(vfsub_s, 32, UW, float32_sub) +-DO_3OP_F(vfsub_d, 64, UD, float64_sub) +-DO_3OP_F(vfmul_s, 32, UW, float32_mul) +-DO_3OP_F(vfmul_d, 64, UD, float64_mul) +-DO_3OP_F(vfdiv_s, 32, UW, float32_div) +-DO_3OP_F(vfdiv_d, 64, UD, float64_div) +-DO_3OP_F(vfmax_s, 32, UW, float32_maxnum) +-DO_3OP_F(vfmax_d, 64, UD, float64_maxnum) +-DO_3OP_F(vfmin_s, 32, UW, float32_minnum) +-DO_3OP_F(vfmin_d, 64, UD, float64_minnum) +-DO_3OP_F(vfmaxa_s, 32, UW, float32_maxnummag) +-DO_3OP_F(vfmaxa_d, 64, UD, float64_maxnummag) +-DO_3OP_F(vfmina_s, 32, UW, float32_minnummag) +-DO_3OP_F(vfmina_d, 64, UD, float64_minnummag) +- +-#define DO_4OP_F(NAME, BIT, E, FN, flags) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, void *va, \ +- CPULoongArchState *env, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- VReg *Va = (VReg *)va; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- vec_clear_cause(env); \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E(i) = FN(Vj->E(i), Vk->E(i), Va->E(i), flags, &env->fp_status); \ +- vec_update_fcsr0(env, GETPC()); \ +- } \ +-} +- +-DO_4OP_F(vfmadd_s, 32, UW, float32_muladd, 0) +-DO_4OP_F(vfmadd_d, 64, UD, float64_muladd, 0) +-DO_4OP_F(vfmsub_s, 32, UW, float32_muladd, float_muladd_negate_c) +-DO_4OP_F(vfmsub_d, 64, UD, float64_muladd, float_muladd_negate_c) +-DO_4OP_F(vfnmadd_s, 32, UW, float32_muladd, float_muladd_negate_result) +-DO_4OP_F(vfnmadd_d, 64, UD, float64_muladd, float_muladd_negate_result) +-DO_4OP_F(vfnmsub_s, 32, UW, float32_muladd, +- float_muladd_negate_c | float_muladd_negate_result) +-DO_4OP_F(vfnmsub_d, 64, UD, float64_muladd, +- float_muladd_negate_c | float_muladd_negate_result) +- +-#define DO_2OP_F(NAME, BIT, E, FN) \ +-void HELPER(NAME)(void *vd, void *vj, \ +- CPULoongArchState *env, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- vec_clear_cause(env); \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E(i) = FN(env, Vj->E(i)); \ +- } \ +-} +- +-#define FLOGB(BIT, T) \ +-static T do_flogb_## BIT(CPULoongArchState *env, T fj) \ +-{ \ +- T fp, fd; \ +- float_status *status = &env->fp_status; \ +- FloatRoundMode old_mode = get_float_rounding_mode(status); \ +- \ +- set_float_rounding_mode(float_round_down, status); \ +- fp = float ## BIT ##_log2(fj, status); \ +- fd = float ## BIT ##_round_to_int(fp, status); \ +- set_float_rounding_mode(old_mode, status); \ +- vec_update_fcsr0_mask(env, GETPC(), float_flag_inexact); \ +- return fd; \ +-} +- +-FLOGB(32, uint32_t) +-FLOGB(64, uint64_t) +- +-#define FCLASS(NAME, BIT, E, FN) \ +-void HELPER(NAME)(void *vd, void *vj, \ +- CPULoongArchState *env, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E(i) = FN(env, Vj->E(i)); \ +- } \ +-} +- +-FCLASS(vfclass_s, 32, UW, helper_fclass_s) +-FCLASS(vfclass_d, 64, UD, helper_fclass_d) +- +-#define FSQRT(BIT, T) \ +-static T do_fsqrt_## BIT(CPULoongArchState *env, T fj) \ +-{ \ +- T fd; \ +- fd = float ## BIT ##_sqrt(fj, &env->fp_status); \ +- vec_update_fcsr0(env, GETPC()); \ +- return fd; \ +-} +- +-FSQRT(32, uint32_t) +-FSQRT(64, uint64_t) +- +-#define FRECIP(BIT, T) \ +-static T do_frecip_## BIT(CPULoongArchState *env, T fj) \ +-{ \ +- T fd; \ +- fd = float ## BIT ##_div(float ## BIT ##_one, fj, &env->fp_status); \ +- vec_update_fcsr0(env, GETPC()); \ +- return fd; \ +-} +- +-FRECIP(32, uint32_t) +-FRECIP(64, uint64_t) +- +-#define FRSQRT(BIT, T) \ +-static T do_frsqrt_## BIT(CPULoongArchState *env, T fj) \ +-{ \ +- T fd, fp; \ +- fp = float ## BIT ##_sqrt(fj, &env->fp_status); \ +- fd = float ## BIT ##_div(float ## BIT ##_one, fp, &env->fp_status); \ +- vec_update_fcsr0(env, GETPC()); \ +- return fd; \ +-} +- +-FRSQRT(32, uint32_t) +-FRSQRT(64, uint64_t) +- +-DO_2OP_F(vflogb_s, 32, UW, do_flogb_32) +-DO_2OP_F(vflogb_d, 64, UD, do_flogb_64) +-DO_2OP_F(vfsqrt_s, 32, UW, do_fsqrt_32) +-DO_2OP_F(vfsqrt_d, 64, UD, do_fsqrt_64) +-DO_2OP_F(vfrecip_s, 32, UW, do_frecip_32) +-DO_2OP_F(vfrecip_d, 64, UD, do_frecip_64) +-DO_2OP_F(vfrsqrt_s, 32, UW, do_frsqrt_32) +-DO_2OP_F(vfrsqrt_d, 64, UD, do_frsqrt_64) +- +-static uint32_t float16_cvt_float32(uint16_t h, float_status *status) +-{ +- return float16_to_float32(h, true, status); +-} +-static uint64_t float32_cvt_float64(uint32_t s, float_status *status) +-{ +- return float32_to_float64(s, status); +-} +- +-static uint16_t float32_cvt_float16(uint32_t s, float_status *status) +-{ +- return float32_to_float16(s, true, status); +-} +-static uint32_t float64_cvt_float32(uint64_t d, float_status *status) +-{ +- return float64_to_float32(d, status); +-} +- +-void HELPER(vfcvtl_s_h)(void *vd, void *vj, +- CPULoongArchState *env, uint32_t desc) +-{ +- int i, j, ofs; +- VReg temp = {}; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- ofs = LSX_LEN / 32; +- vec_clear_cause(env); +- for (i = 0; i < oprsz / 16; i++) { +- for (j = 0; j < ofs; j++) { +- temp.UW(j + ofs * i) =float16_cvt_float32(Vj->UH(j + ofs * 2 * i), +- &env->fp_status); +- } +- vec_update_fcsr0(env, GETPC()); +- } +- *Vd = temp; +-} +- +-void HELPER(vfcvtl_d_s)(void *vd, void *vj, +- CPULoongArchState *env, uint32_t desc) +-{ +- int i, j, ofs; +- VReg temp = {}; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- ofs = LSX_LEN / 64; +- vec_clear_cause(env); +- for (i = 0; i < oprsz / 16; i++) { +- for (j = 0; j < ofs; j++) { +- temp.UD(j + ofs * i) = float32_cvt_float64(Vj->UW(j + ofs * 2 * i), +- &env->fp_status); +- } +- vec_update_fcsr0(env, GETPC()); +- } +- *Vd = temp; +-} +- +-void HELPER(vfcvth_s_h)(void *vd, void *vj, +- CPULoongArchState *env, uint32_t desc) +-{ +- int i, j, ofs; +- VReg temp = {}; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- ofs = LSX_LEN / 32; +- vec_clear_cause(env); +- for (i = 0; i < oprsz / 16; i++) { +- for (j = 0; j < ofs; j++) { +- temp.UW(j + ofs * i) = float16_cvt_float32(Vj->UH(j + ofs * (2 * i + 1)), +- &env->fp_status); +- } +- vec_update_fcsr0(env, GETPC()); +- } +- *Vd = temp; +-} +- +-void HELPER(vfcvth_d_s)(void *vd, void *vj, +- CPULoongArchState *env, uint32_t desc) +-{ +- int i, j, ofs; +- VReg temp = {}; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- ofs = LSX_LEN / 64; +- vec_clear_cause(env); +- for (i = 0; i < oprsz / 16; i++) { +- for (j = 0; j < ofs; j++) { +- temp.UD(j + ofs * i) = float32_cvt_float64(Vj->UW(j + ofs * (2 * i + 1)), +- &env->fp_status); +- } +- vec_update_fcsr0(env, GETPC()); +- } +- *Vd = temp; +-} +- +-void HELPER(vfcvt_h_s)(void *vd, void *vj, void *vk, +- CPULoongArchState *env, uint32_t desc) +-{ +- int i, j, ofs; +- VReg temp = {}; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- VReg *Vk = (VReg *)vk; +- int oprsz = simd_oprsz(desc); +- +- ofs = LSX_LEN / 32; +- vec_clear_cause(env); +- for(i = 0; i < oprsz / 16; i++) { +- for (j = 0; j < ofs; j++) { +- temp.UH(j + ofs * (2 * i + 1)) = float32_cvt_float16(Vj->UW(j + ofs * i), +- &env->fp_status); +- temp.UH(j + ofs * 2 * i) = float32_cvt_float16(Vk->UW(j + ofs * i), +- &env->fp_status); +- } +- vec_update_fcsr0(env, GETPC()); +- } +- *Vd = temp; +-} +- +-void HELPER(vfcvt_s_d)(void *vd, void *vj, void *vk, +- CPULoongArchState *env, uint32_t desc) +-{ +- int i, j, ofs; +- VReg temp = {}; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- VReg *Vk = (VReg *)vk; +- int oprsz = simd_oprsz(desc); +- +- ofs = LSX_LEN / 64; +- vec_clear_cause(env); +- for(i = 0; i < oprsz / 16; i++) { +- for (j = 0; j < ofs; j++) { +- temp.UW(j + ofs * (2 * i + 1)) = float64_cvt_float32(Vj->UD(j + ofs * i), +- &env->fp_status); +- temp.UW(j + ofs * 2 * i) = float64_cvt_float32(Vk->UD(j + ofs * i), +- &env->fp_status); +- } +- vec_update_fcsr0(env, GETPC()); +- } +- *Vd = temp; +-} +- +-void HELPER(vfrint_s)(void *vd, void *vj, +- CPULoongArchState *env, uint32_t desc) +-{ +- int i; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- vec_clear_cause(env); +- for (i = 0; i < oprsz / 4; i++) { +- Vd->W(i) = float32_round_to_int(Vj->UW(i), &env->fp_status); +- vec_update_fcsr0(env, GETPC()); +- } +-} +- +-void HELPER(vfrint_d)(void *vd, void *vj, +- CPULoongArchState *env, uint32_t desc) +-{ +- int i; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- vec_clear_cause(env); +- for (i = 0; i < oprsz / 8; i++) { +- Vd->D(i) = float64_round_to_int(Vj->UD(i), &env->fp_status); +- vec_update_fcsr0(env, GETPC()); +- } +-} +- +-#define FCVT_2OP(NAME, BIT, E, MODE) \ +-void HELPER(NAME)(void *vd, void *vj, \ +- CPULoongArchState *env, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- vec_clear_cause(env); \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); \ +- set_float_rounding_mode(MODE, &env->fp_status); \ +- Vd->E(i) = float## BIT ## _round_to_int(Vj->E(i), &env->fp_status); \ +- set_float_rounding_mode(old_mode, &env->fp_status); \ +- vec_update_fcsr0(env, GETPC()); \ +- } \ +-} +- +-FCVT_2OP(vfrintrne_s, 32, UW, float_round_nearest_even) +-FCVT_2OP(vfrintrne_d, 64, UD, float_round_nearest_even) +-FCVT_2OP(vfrintrz_s, 32, UW, float_round_to_zero) +-FCVT_2OP(vfrintrz_d, 64, UD, float_round_to_zero) +-FCVT_2OP(vfrintrp_s, 32, UW, float_round_up) +-FCVT_2OP(vfrintrp_d, 64, UD, float_round_up) +-FCVT_2OP(vfrintrm_s, 32, UW, float_round_down) +-FCVT_2OP(vfrintrm_d, 64, UD, float_round_down) +- +-#define FTINT(NAME, FMT1, FMT2, T1, T2, MODE) \ +-static T2 do_ftint ## NAME(CPULoongArchState *env, T1 fj) \ +-{ \ +- T2 fd; \ +- FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); \ +- \ +- set_float_rounding_mode(MODE, &env->fp_status); \ +- fd = do_## FMT1 ##_to_## FMT2(env, fj); \ +- set_float_rounding_mode(old_mode, &env->fp_status); \ +- return fd; \ +-} +- +-#define DO_FTINT(FMT1, FMT2, T1, T2) \ +-static T2 do_## FMT1 ##_to_## FMT2(CPULoongArchState *env, T1 fj) \ +-{ \ +- T2 fd; \ +- \ +- fd = FMT1 ##_to_## FMT2(fj, &env->fp_status); \ +- if (get_float_exception_flags(&env->fp_status) & (float_flag_invalid)) { \ +- if (FMT1 ##_is_any_nan(fj)) { \ +- fd = 0; \ +- } \ +- } \ +- vec_update_fcsr0(env, GETPC()); \ +- return fd; \ +-} +- +-DO_FTINT(float32, int32, uint32_t, uint32_t) +-DO_FTINT(float64, int64, uint64_t, uint64_t) +-DO_FTINT(float32, uint32, uint32_t, uint32_t) +-DO_FTINT(float64, uint64, uint64_t, uint64_t) +-DO_FTINT(float64, int32, uint64_t, uint32_t) +-DO_FTINT(float32, int64, uint32_t, uint64_t) +- +-FTINT(rne_w_s, float32, int32, uint32_t, uint32_t, float_round_nearest_even) +-FTINT(rne_l_d, float64, int64, uint64_t, uint64_t, float_round_nearest_even) +-FTINT(rp_w_s, float32, int32, uint32_t, uint32_t, float_round_up) +-FTINT(rp_l_d, float64, int64, uint64_t, uint64_t, float_round_up) +-FTINT(rz_w_s, float32, int32, uint32_t, uint32_t, float_round_to_zero) +-FTINT(rz_l_d, float64, int64, uint64_t, uint64_t, float_round_to_zero) +-FTINT(rm_w_s, float32, int32, uint32_t, uint32_t, float_round_down) +-FTINT(rm_l_d, float64, int64, uint64_t, uint64_t, float_round_down) +- +-DO_2OP_F(vftintrne_w_s, 32, UW, do_ftintrne_w_s) +-DO_2OP_F(vftintrne_l_d, 64, UD, do_ftintrne_l_d) +-DO_2OP_F(vftintrp_w_s, 32, UW, do_ftintrp_w_s) +-DO_2OP_F(vftintrp_l_d, 64, UD, do_ftintrp_l_d) +-DO_2OP_F(vftintrz_w_s, 32, UW, do_ftintrz_w_s) +-DO_2OP_F(vftintrz_l_d, 64, UD, do_ftintrz_l_d) +-DO_2OP_F(vftintrm_w_s, 32, UW, do_ftintrm_w_s) +-DO_2OP_F(vftintrm_l_d, 64, UD, do_ftintrm_l_d) +-DO_2OP_F(vftint_w_s, 32, UW, do_float32_to_int32) +-DO_2OP_F(vftint_l_d, 64, UD, do_float64_to_int64) +- +-FTINT(rz_wu_s, float32, uint32, uint32_t, uint32_t, float_round_to_zero) +-FTINT(rz_lu_d, float64, uint64, uint64_t, uint64_t, float_round_to_zero) +- +-DO_2OP_F(vftintrz_wu_s, 32, UW, do_ftintrz_wu_s) +-DO_2OP_F(vftintrz_lu_d, 64, UD, do_ftintrz_lu_d) +-DO_2OP_F(vftint_wu_s, 32, UW, do_float32_to_uint32) +-DO_2OP_F(vftint_lu_d, 64, UD, do_float64_to_uint64) +- +-FTINT(rm_w_d, float64, int32, uint64_t, uint32_t, float_round_down) +-FTINT(rp_w_d, float64, int32, uint64_t, uint32_t, float_round_up) +-FTINT(rz_w_d, float64, int32, uint64_t, uint32_t, float_round_to_zero) +-FTINT(rne_w_d, float64, int32, uint64_t, uint32_t, float_round_nearest_even) +- +-#define FTINT_W_D(NAME, FN) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, \ +- CPULoongArchState *env, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / 64; \ +- vec_clear_cause(env); \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- temp.W(j + ofs * (2 * i + 1)) = FN(env, Vj->UD(j + ofs * i)); \ +- temp.W(j + ofs * 2 * i) = FN(env, Vk->UD(j + ofs * i)); \ +- } \ +- } \ +- *Vd = temp; \ +-} +- +-FTINT_W_D(vftint_w_d, do_float64_to_int32) +-FTINT_W_D(vftintrm_w_d, do_ftintrm_w_d) +-FTINT_W_D(vftintrp_w_d, do_ftintrp_w_d) +-FTINT_W_D(vftintrz_w_d, do_ftintrz_w_d) +-FTINT_W_D(vftintrne_w_d, do_ftintrne_w_d) +- +-FTINT(rml_l_s, float32, int64, uint32_t, uint64_t, float_round_down) +-FTINT(rpl_l_s, float32, int64, uint32_t, uint64_t, float_round_up) +-FTINT(rzl_l_s, float32, int64, uint32_t, uint64_t, float_round_to_zero) +-FTINT(rnel_l_s, float32, int64, uint32_t, uint64_t, float_round_nearest_even) +-FTINT(rmh_l_s, float32, int64, uint32_t, uint64_t, float_round_down) +-FTINT(rph_l_s, float32, int64, uint32_t, uint64_t, float_round_up) +-FTINT(rzh_l_s, float32, int64, uint32_t, uint64_t, float_round_to_zero) +-FTINT(rneh_l_s, float32, int64, uint32_t, uint64_t, float_round_nearest_even) +- +-#define FTINTL_L_S(NAME, FN) \ +-void HELPER(NAME)(void *vd, void *vj, \ +- CPULoongArchState *env, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg temp; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / 64; \ +- vec_clear_cause(env); \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- temp.D(j + ofs * i) = FN(env, Vj->UW(j + ofs * 2 * i)); \ +- } \ +- } \ +- *Vd = temp; \ +-} +- +-FTINTL_L_S(vftintl_l_s, do_float32_to_int64) +-FTINTL_L_S(vftintrml_l_s, do_ftintrml_l_s) +-FTINTL_L_S(vftintrpl_l_s, do_ftintrpl_l_s) +-FTINTL_L_S(vftintrzl_l_s, do_ftintrzl_l_s) +-FTINTL_L_S(vftintrnel_l_s, do_ftintrnel_l_s) +- +-#define FTINTH_L_S(NAME, FN) \ +-void HELPER(NAME)(void *vd, void *vj, \ +- CPULoongArchState *env, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / 64; \ +- vec_clear_cause(env); \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- temp.D(j + ofs * i) = FN(env, Vj->UW(j + ofs * (2 * i + 1))); \ +- } \ +- } \ +- *Vd = temp; \ +-} +- +-FTINTH_L_S(vftinth_l_s, do_float32_to_int64) +-FTINTH_L_S(vftintrmh_l_s, do_ftintrmh_l_s) +-FTINTH_L_S(vftintrph_l_s, do_ftintrph_l_s) +-FTINTH_L_S(vftintrzh_l_s, do_ftintrzh_l_s) +-FTINTH_L_S(vftintrneh_l_s, do_ftintrneh_l_s) +- +-#define FFINT(NAME, FMT1, FMT2, T1, T2) \ +-static T2 do_ffint_ ## NAME(CPULoongArchState *env, T1 fj) \ +-{ \ +- T2 fd; \ +- \ +- fd = FMT1 ##_to_## FMT2(fj, &env->fp_status); \ +- vec_update_fcsr0(env, GETPC()); \ +- return fd; \ +-} +- +-FFINT(s_w, int32, float32, int32_t, uint32_t) +-FFINT(d_l, int64, float64, int64_t, uint64_t) +-FFINT(s_wu, uint32, float32, uint32_t, uint32_t) +-FFINT(d_lu, uint64, float64, uint64_t, uint64_t) +- +-DO_2OP_F(vffint_s_w, 32, W, do_ffint_s_w) +-DO_2OP_F(vffint_d_l, 64, D, do_ffint_d_l) +-DO_2OP_F(vffint_s_wu, 32, UW, do_ffint_s_wu) +-DO_2OP_F(vffint_d_lu, 64, UD, do_ffint_d_lu) +- +-void HELPER(vffintl_d_w)(void *vd, void *vj, +- CPULoongArchState *env, uint32_t desc) +-{ +- int i, j, ofs; +- VReg temp = {}; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- ofs = LSX_LEN / 64; +- vec_clear_cause(env); +- for (i = 0; i < oprsz / 16; i++) { +- for (j = 0; j < ofs; j++) { +- temp.D(j + ofs * i) = int32_to_float64(Vj->W(j + ofs * 2 * i), +- &env->fp_status); +- } +- vec_update_fcsr0(env, GETPC()); +- } +- *Vd = temp; +-} +- +-void HELPER(vffinth_d_w)(void *vd, void *vj, +- CPULoongArchState *env, uint32_t desc) +-{ +- int i, j, ofs; +- VReg temp = {}; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- ofs = LSX_LEN / 64; +- vec_clear_cause(env); +- for (i = 0; i < oprsz /16; i++) { +- for (j = 0; j < ofs; j++) { +- temp.D(j + ofs * i) = int32_to_float64(Vj->W(j + ofs * (2 * i + 1)), +- &env->fp_status); +- } +- vec_update_fcsr0(env, GETPC()); +- } +- *Vd = temp; +-} +- +-void HELPER(vffint_s_l)(void *vd, void *vj, void *vk, +- CPULoongArchState *env, uint32_t desc) +-{ +- int i, j, ofs; +- VReg temp = {}; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- VReg *Vk = (VReg *)vk; +- int oprsz = simd_oprsz(desc); +- +- ofs = LSX_LEN / 64; +- vec_clear_cause(env); +- for (i = 0; i < oprsz / 16; i++) { +- for (j = 0; j < ofs; j++) { +- temp.W(j + ofs * (2 * i + 1)) = int64_to_float32(Vj->D(j + ofs * i), +- &env->fp_status); +- temp.W(j + ofs * 2 * i) = int64_to_float32(Vk->D(j + ofs * i), +- &env->fp_status); +- } +- vec_update_fcsr0(env, GETPC()); +- } +- *Vd = temp; +-} +- +-#define VCMPI(NAME, BIT, E, DO_OP) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- typedef __typeof(Vd->E(0)) TD; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- Vd->E(i) = DO_OP(Vj->E(i), (TD)imm); \ +- } \ +-} +- +-VCMPI(vseqi_b, 8, B, VSEQ) +-VCMPI(vseqi_h, 16, H, VSEQ) +-VCMPI(vseqi_w, 32, W, VSEQ) +-VCMPI(vseqi_d, 64, D, VSEQ) +-VCMPI(vslei_b, 8, B, VSLE) +-VCMPI(vslei_h, 16, H, VSLE) +-VCMPI(vslei_w, 32, W, VSLE) +-VCMPI(vslei_d, 64, D, VSLE) +-VCMPI(vslei_bu, 8, UB, VSLE) +-VCMPI(vslei_hu, 16, UH, VSLE) +-VCMPI(vslei_wu, 32, UW, VSLE) +-VCMPI(vslei_du, 64, UD, VSLE) +-VCMPI(vslti_b, 8, B, VSLT) +-VCMPI(vslti_h, 16, H, VSLT) +-VCMPI(vslti_w, 32, W, VSLT) +-VCMPI(vslti_d, 64, D, VSLT) +-VCMPI(vslti_bu, 8, UB, VSLT) +-VCMPI(vslti_hu, 16, UH, VSLT) +-VCMPI(vslti_wu, 32, UW, VSLT) +-VCMPI(vslti_du, 64, UD, VSLT) +- +-static uint64_t vfcmp_common(CPULoongArchState *env, +- FloatRelation cmp, uint32_t flags) +-{ +- uint64_t ret = 0; +- +- switch (cmp) { +- case float_relation_less: +- ret = (flags & FCMP_LT); +- break; +- case float_relation_equal: +- ret = (flags & FCMP_EQ); +- break; +- case float_relation_greater: +- ret = (flags & FCMP_GT); +- break; +- case float_relation_unordered: +- ret = (flags & FCMP_UN); +- break; +- default: +- g_assert_not_reached(); +- } +- +- if (ret) { +- ret = -1; +- } +- +- return ret; +-} +- +-#define VFCMP(NAME, BIT, E, FN) \ +-void HELPER(NAME)(CPULoongArchState *env, uint32_t oprsz, \ +- uint32_t vd, uint32_t vj, uint32_t vk, uint32_t flags) \ +-{ \ +- int i; \ +- VReg t; \ +- VReg *Vd = &(env->fpr[vd].vreg); \ +- VReg *Vj = &(env->fpr[vj].vreg); \ +- VReg *Vk = &(env->fpr[vk].vreg); \ +- \ +- vec_clear_cause(env); \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- FloatRelation cmp; \ +- cmp = FN(Vj->E(i), Vk->E(i), &env->fp_status); \ +- t.E(i) = vfcmp_common(env, cmp, flags); \ +- vec_update_fcsr0(env, GETPC()); \ +- } \ +- *Vd = t; \ +-} +- +-VFCMP(vfcmp_c_s, 32, UW, float32_compare_quiet) +-VFCMP(vfcmp_s_s, 32, UW, float32_compare) +-VFCMP(vfcmp_c_d, 64, UD, float64_compare_quiet) +-VFCMP(vfcmp_s_d, 64, UD, float64_compare) +- +-void HELPER(vbitseli_b)(void *vd, void *vj, uint64_t imm, uint32_t desc) +-{ +- int i; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- +- for (i = 0; i < simd_oprsz(desc); i++) { +- Vd->B(i) = (~Vd->B(i) & Vj->B(i)) | (Vd->B(i) & imm); +- } +-} +- +-/* Copy from target/arm/tcg/sve_helper.c */ +-static inline bool do_match2(uint64_t n, uint64_t m0, uint64_t m1, int esz) +-{ +- int bits = 8 << esz; +- uint64_t ones = dup_const(esz, 1); +- uint64_t signs = ones << (bits - 1); +- uint64_t cmp0, cmp1; +- +- cmp1 = dup_const(esz, n); +- cmp0 = cmp1 ^ m0; +- cmp1 = cmp1 ^ m1; +- cmp0 = (cmp0 - ones) & ~cmp0; +- cmp1 = (cmp1 - ones) & ~cmp1; +- return (cmp0 | cmp1) & signs; +-} +- +-#define SETANYEQZ(NAME, MO) \ +-void HELPER(NAME)(CPULoongArchState *env, \ +- uint32_t oprsz, uint32_t cd, uint32_t vj) \ +-{ \ +- VReg *Vj = &(env->fpr[vj].vreg); \ +- \ +- env->cf[cd & 0x7] = do_match2(0, Vj->D(0), Vj->D(1), MO); \ +- if (oprsz == 32) { \ +- env->cf[cd & 0x7] = env->cf[cd & 0x7] || \ +- do_match2(0, Vj->D(2), Vj->D(3), MO); \ +- } \ +-} +- +-SETANYEQZ(vsetanyeqz_b, MO_8) +-SETANYEQZ(vsetanyeqz_h, MO_16) +-SETANYEQZ(vsetanyeqz_w, MO_32) +-SETANYEQZ(vsetanyeqz_d, MO_64) +- +-#define SETALLNEZ(NAME, MO) \ +-void HELPER(NAME)(CPULoongArchState *env, \ +- uint32_t oprsz, uint32_t cd, uint32_t vj) \ +-{ \ +- VReg *Vj = &(env->fpr[vj].vreg); \ +- \ +- env->cf[cd & 0x7]= !do_match2(0, Vj->D(0), Vj->D(1), MO); \ +- if (oprsz == 32) { \ +- env->cf[cd & 0x7] = env->cf[cd & 0x7] && \ +- !do_match2(0, Vj->D(2), Vj->D(3), MO); \ +- } \ +-} +- +-SETALLNEZ(vsetallnez_b, MO_8) +-SETALLNEZ(vsetallnez_h, MO_16) +-SETALLNEZ(vsetallnez_w, MO_32) +-SETALLNEZ(vsetallnez_d, MO_64) +- +-#define XVINSVE0(NAME, E, MASK) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +-{ \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- Vd->E(imm & MASK) = Vj->E(0); \ +-} +- +-XVINSVE0(xvinsve0_w, W, 0x7) +-XVINSVE0(xvinsve0_d, D, 0x3) +- +-#define XVPICKVE(NAME, E, BIT, MASK) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +-{ \ +- int i; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- Vd->E(0) = Vj->E(imm & MASK); \ +- for (i = 1; i < oprsz / (BIT / 8); i++) { \ +- Vd->E(i) = 0; \ +- } \ +-} +- +-XVPICKVE(xvpickve_w, W, 32, 0x7) +-XVPICKVE(xvpickve_d, D, 64, 0x3) +- +-#define VPACKEV(NAME, BIT, E) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- temp.E(2 * i + 1) = Vj->E(2 * i); \ +- temp.E(2 *i) = Vk->E(2 * i); \ +- } \ +- *Vd = temp; \ +-} +- +-VPACKEV(vpackev_b, 16, B) +-VPACKEV(vpackev_h, 32, H) +-VPACKEV(vpackev_w, 64, W) +-VPACKEV(vpackev_d, 128, D) +- +-#define VPACKOD(NAME, BIT, E) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- temp.E(2 * i + 1) = Vj->E(2 * i + 1); \ +- temp.E(2 * i) = Vk->E(2 * i + 1); \ +- } \ +- *Vd = temp; \ +-} +- +-VPACKOD(vpackod_b, 16, B) +-VPACKOD(vpackod_h, 32, H) +-VPACKOD(vpackod_w, 64, W) +-VPACKOD(vpackod_d, 128, D) +- +-#define VPICKEV(NAME, BIT, E) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- temp.E(j + ofs * (2 * i + 1)) = Vj->E(2 * (j + ofs * i)); \ +- temp.E(j + ofs * 2 * i) = Vk->E(2 * (j + ofs * i)); \ +- } \ +- } \ +- *Vd = temp; \ +-} +- +-VPICKEV(vpickev_b, 16, B) +-VPICKEV(vpickev_h, 32, H) +-VPICKEV(vpickev_w, 64, W) +-VPICKEV(vpickev_d, 128, D) +- +-#define VPICKOD(NAME, BIT, E) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- temp.E(j + ofs * (2 * i + 1)) = Vj->E(2 * (j + ofs * i) + 1); \ +- temp.E(j + ofs * 2 * i) = Vk->E(2 * (j + ofs * i) + 1); \ +- } \ +- } \ +- *Vd = temp; \ +-} +- +-VPICKOD(vpickod_b, 16, B) +-VPICKOD(vpickod_h, 32, H) +-VPICKOD(vpickod_w, 64, W) +-VPICKOD(vpickod_d, 128, D) +- +-#define VILVL(NAME, BIT, E) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- temp.E(2 * (j + ofs * i) + 1) = Vj->E(j + ofs * 2 * i); \ +- temp.E(2 * (j + ofs * i)) = Vk->E(j + ofs * 2 * i); \ +- } \ +- } \ +- *Vd = temp; \ +-} +- +-VILVL(vilvl_b, 16, B) +-VILVL(vilvl_h, 32, H) +-VILVL(vilvl_w, 64, W) +-VILVL(vilvl_d, 128, D) +- +-#define VILVH(NAME, BIT, E) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i, j, ofs; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- ofs = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- for (j = 0; j < ofs; j++) { \ +- temp.E(2 * (j + ofs * i) + 1) = Vj->E(j + ofs * (2 * i + 1)); \ +- temp.E(2 * (j + ofs * i)) = Vk->E(j + ofs * (2 * i + 1)); \ +- } \ +- } \ +- *Vd = temp; \ +-} +- +-VILVH(vilvh_b, 16, B) +-VILVH(vilvh_h, 32, H) +-VILVH(vilvh_w, 64, W) +-VILVH(vilvh_d, 128, D) +- +-void HELPER(vshuf_b)(void *vd, void *vj, void *vk, void *va, uint32_t desc) +-{ +- int i, j, m; +- VReg temp = {}; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- VReg *Vk = (VReg *)vk; +- VReg *Va = (VReg *)va; +- int oprsz = simd_oprsz(desc); +- +- m = LSX_LEN / 8; +- for (i = 0; i < (oprsz / 16) * m; i++) { +- j = i < m ? 0 : 1; +- uint64_t k = (uint8_t)Va->B(i) % (2 * m); +- temp.B(i) = k < m ? Vk->B(k + j * m): Vj->B(k + (j - 1) * m); +- } +- *Vd = temp; +-} +- +-#define VSHUF(NAME, BIT, E) \ +-void HELPER(NAME)(void *vd, void *vj, void *vk, uint32_t desc) \ +-{ \ +- int i, j, m; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- VReg *Vk = (VReg *)vk; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- m = LSX_LEN / BIT; \ +- for (i = 0; i < (oprsz / 16) * m; i++) { \ +- j = i < m ? 0 : 1; \ +- uint64_t k = ((uint8_t)Vd->E(i)) % (2 * m); \ +- temp.E(i) = k < m ? Vk->E(k + j * m) : Vj->E(k + (j - 1) * m); \ +- } \ +- *Vd = temp; \ +-} +- +-VSHUF(vshuf_h, 16, H) +-VSHUF(vshuf_w, 32, W) +-VSHUF(vshuf_d, 64, D) +- +-#define VSHUF4I(NAME, BIT, E) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +-{ \ +- int i, j, max; \ +- VReg temp = {}; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- max = LSX_LEN / BIT; \ +- for (i = 0; i < oprsz / (BIT / 8); i++) { \ +- j = i < max ? 1 : 2; \ +- temp.E(i) = Vj->E(SHF_POS(i - ((j -1)* max), imm) + (j - 1) * max); \ +- } \ +- *Vd = temp; \ +-} +- +-VSHUF4I(vshuf4i_b, 8, B) +-VSHUF4I(vshuf4i_h, 16, H) +-VSHUF4I(vshuf4i_w, 32, W) +- +-void HELPER(vshuf4i_d)(void *vd, void *vj, uint64_t imm, uint32_t desc) +-{ +- int i; +- VReg temp = {}; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- temp.D(2 * i) = (imm & 2 ? Vj : Vd)->D((imm & 1) + 2 * i); +- temp.D(2 * i + 1) = (imm & 8 ? Vj : Vd)->D(((imm >> 2) & 1) + 2 * i); +- } +- *Vd = temp; +-} +- +-void HELPER(vperm_w)(void *vd, void *vj, void *vk, uint32_t desc) +-{ +- int i, m; +- VReg temp = {}; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- VReg *Vk = (VReg *)vk; +- +- m = LASX_LEN / 32; +- for (i = 0; i < m ; i++) { +- uint64_t k = (uint8_t)Vk->W(i) % 8; +- temp.W(i) = Vj->W(k); +- } +- *Vd = temp; +-} +- +-void HELPER(vpermi_w)(void *vd, void *vj, uint64_t imm, uint32_t desc) +-{ +- int i; +- VReg temp = {}; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- int oprsz = simd_oprsz(desc); +- +- for (i = 0; i < oprsz / 16; i++) { +- temp.W(4 * i) = Vj->W((imm & 0x3) + 4 * i); +- temp.W(4 * i + 1) = Vj->W(((imm >> 2) & 0x3) + 4 * i); +- temp.W(4 * i + 2) = Vd->W(((imm >> 4) & 0x3) + 4 * i); +- temp.W(4 * i + 3) = Vd->W(((imm >> 6) & 0x3) + 4 * i); +- } +- *Vd = temp; +-} +- +-void HELPER(vpermi_d)(void *vd, void *vj, uint64_t imm, uint32_t desc) +-{ +- VReg temp = {}; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- +- temp.D(0) = Vj->D(imm & 0x3); +- temp.D(1) = Vj->D((imm >> 2) & 0x3); +- temp.D(2) = Vj->D((imm >> 4) & 0x3); +- temp.D(3) = Vj->D((imm >> 6) & 0x3); +- *Vd = temp; +-} +- +-void HELPER(vpermi_q)(void *vd, void *vj, uint64_t imm, uint32_t desc) +-{ +- int i; +- VReg temp; +- VReg *Vd = (VReg *)vd; +- VReg *Vj = (VReg *)vj; +- +- for (i = 0; i < 2; i++, imm >>= 4) { +- temp.Q(i) = (imm & 2 ? Vd: Vj)->Q(imm & 1); +- } +- *Vd = temp; +-} +- +-#define VEXTRINS(NAME, BIT, E, MASK) \ +-void HELPER(NAME)(void *vd, void *vj, uint64_t imm, uint32_t desc) \ +-{ \ +- int i, ins, extr, max; \ +- VReg *Vd = (VReg *)vd; \ +- VReg *Vj = (VReg *)vj; \ +- int oprsz = simd_oprsz(desc); \ +- \ +- max = LSX_LEN / BIT; \ +- ins = (imm >> 4) & MASK; \ +- extr = imm & MASK; \ +- for (i = 0; i < oprsz / 16; i++) { \ +- Vd->E(ins + i * max) = Vj->E(extr + i * max); \ +- } \ +-} +- +-VEXTRINS(vextrins_b, 8, B, 0xf) +-VEXTRINS(vextrins_h, 16, H, 0x7) +-VEXTRINS(vextrins_w, 32, W, 0x3) +-VEXTRINS(vextrins_d, 64, D, 0x1) +-- +1.8.3.1 + diff --git a/0152-linux-headers-Update-to-Linux-v6.7-rc5.patch b/0152-linux-headers-Update-to-Linux-v6.7-rc5.patch new file mode 100644 index 0000000000000000000000000000000000000000..fbed22766e509dcabf0a9b0a7f330dd70cf39911 --- /dev/null +++ b/0152-linux-headers-Update-to-Linux-v6.7-rc5.patch @@ -0,0 +1,1108 @@ +From bbc1dee97b5a31586e87824abfc84d911ed97d4f Mon Sep 17 00:00:00 2001 +From: Daniel Henrique Barboza +Date: Mon, 18 Dec 2023 17:43:18 -0300 +Subject: [PATCH 162/293] linux-headers: Update to Linux v6.7-rc5 + +We'll add a new RISC-V linux-header file, but first let's update all +headers. + +Headers for 'asm-loongarch' were added in this update. + +Signed-off-by: Daniel Henrique Barboza +Acked-by: Alistair Francis +Message-ID: <20231218204321.75757-2-dbarboza@ventanamicro.com> +Signed-off-by: Alistair Francis +--- + include/standard-headers/drm/drm_fourcc.h | 2 + + include/standard-headers/linux/pci_regs.h | 24 +++- + include/standard-headers/linux/vhost_types.h | 7 + + include/standard-headers/linux/virtio_config.h | 5 + + include/standard-headers/linux/virtio_pci.h | 11 ++ + linux-headers/asm-arm64/kvm.h | 32 +++++ + linux-headers/asm-generic/unistd.h | 14 +- + linux-headers/asm-loongarch/bitsperlong.h | 1 + + linux-headers/asm-loongarch/kvm.h | 108 +++++++++++++++ + linux-headers/asm-loongarch/mman.h | 1 + + linux-headers/asm-loongarch/unistd.h | 5 + + linux-headers/asm-mips/unistd_n32.h | 4 + + linux-headers/asm-mips/unistd_n64.h | 4 + + linux-headers/asm-mips/unistd_o32.h | 4 + + linux-headers/asm-powerpc/unistd_32.h | 4 + + linux-headers/asm-powerpc/unistd_64.h | 4 + + linux-headers/asm-riscv/kvm.h | 12 ++ + linux-headers/asm-s390/unistd_32.h | 4 + + linux-headers/asm-s390/unistd_64.h | 4 + + linux-headers/asm-x86/unistd_32.h | 4 + + linux-headers/asm-x86/unistd_64.h | 3 + + linux-headers/asm-x86/unistd_x32.h | 3 + + linux-headers/linux/iommufd.h | 180 ++++++++++++++++++++++++- + linux-headers/linux/kvm.h | 11 ++ + linux-headers/linux/psp-sev.h | 1 + + linux-headers/linux/stddef.h | 9 +- + linux-headers/linux/userfaultfd.h | 9 +- + linux-headers/linux/vfio.h | 47 +++++-- + linux-headers/linux/vhost.h | 8 ++ + 29 files changed, 498 insertions(+), 27 deletions(-) + create mode 100644 linux-headers/asm-loongarch/bitsperlong.h + create mode 100644 linux-headers/asm-loongarch/kvm.h + create mode 100644 linux-headers/asm-loongarch/mman.h + create mode 100644 linux-headers/asm-loongarch/unistd.h + +diff --git a/include/standard-headers/drm/drm_fourcc.h b/include/standard-headers/drm/drm_fourcc.h +index 72279f4..3afb701 100644 +--- a/include/standard-headers/drm/drm_fourcc.h ++++ b/include/standard-headers/drm/drm_fourcc.h +@@ -322,6 +322,8 @@ extern "C" { + * index 1 = Cr:Cb plane, [39:0] Cr1:Cb1:Cr0:Cb0 little endian + */ + #define DRM_FORMAT_NV15 fourcc_code('N', 'V', '1', '5') /* 2x2 subsampled Cr:Cb plane */ ++#define DRM_FORMAT_NV20 fourcc_code('N', 'V', '2', '0') /* 2x1 subsampled Cr:Cb plane */ ++#define DRM_FORMAT_NV30 fourcc_code('N', 'V', '3', '0') /* non-subsampled Cr:Cb plane */ + + /* + * 2 plane YCbCr MSB aligned +diff --git a/include/standard-headers/linux/pci_regs.h b/include/standard-headers/linux/pci_regs.h +index e5f558d..a391932 100644 +--- a/include/standard-headers/linux/pci_regs.h ++++ b/include/standard-headers/linux/pci_regs.h +@@ -80,6 +80,7 @@ + #define PCI_HEADER_TYPE_NORMAL 0 + #define PCI_HEADER_TYPE_BRIDGE 1 + #define PCI_HEADER_TYPE_CARDBUS 2 ++#define PCI_HEADER_TYPE_MFD 0x80 /* Multi-Function Device (possible) */ + + #define PCI_BIST 0x0f /* 8 bits */ + #define PCI_BIST_CODE_MASK 0x0f /* Return result */ +@@ -637,6 +638,7 @@ + #define PCI_EXP_RTCAP 0x1e /* Root Capabilities */ + #define PCI_EXP_RTCAP_CRSVIS 0x0001 /* CRS Software Visibility capability */ + #define PCI_EXP_RTSTA 0x20 /* Root Status */ ++#define PCI_EXP_RTSTA_PME_RQ_ID 0x0000ffff /* PME Requester ID */ + #define PCI_EXP_RTSTA_PME 0x00010000 /* PME status */ + #define PCI_EXP_RTSTA_PENDING 0x00020000 /* PME pending */ + /* +@@ -930,12 +932,13 @@ + + /* Process Address Space ID */ + #define PCI_PASID_CAP 0x04 /* PASID feature register */ +-#define PCI_PASID_CAP_EXEC 0x02 /* Exec permissions Supported */ +-#define PCI_PASID_CAP_PRIV 0x04 /* Privilege Mode Supported */ ++#define PCI_PASID_CAP_EXEC 0x0002 /* Exec permissions Supported */ ++#define PCI_PASID_CAP_PRIV 0x0004 /* Privilege Mode Supported */ ++#define PCI_PASID_CAP_WIDTH 0x1f00 + #define PCI_PASID_CTRL 0x06 /* PASID control register */ +-#define PCI_PASID_CTRL_ENABLE 0x01 /* Enable bit */ +-#define PCI_PASID_CTRL_EXEC 0x02 /* Exec permissions Enable */ +-#define PCI_PASID_CTRL_PRIV 0x04 /* Privilege Mode Enable */ ++#define PCI_PASID_CTRL_ENABLE 0x0001 /* Enable bit */ ++#define PCI_PASID_CTRL_EXEC 0x0002 /* Exec permissions Enable */ ++#define PCI_PASID_CTRL_PRIV 0x0004 /* Privilege Mode Enable */ + #define PCI_EXT_CAP_PASID_SIZEOF 8 + + /* Single Root I/O Virtualization */ +@@ -975,6 +978,8 @@ + #define PCI_LTR_VALUE_MASK 0x000003ff + #define PCI_LTR_SCALE_MASK 0x00001c00 + #define PCI_LTR_SCALE_SHIFT 10 ++#define PCI_LTR_NOSNOOP_VALUE 0x03ff0000 /* Max No-Snoop Latency Value */ ++#define PCI_LTR_NOSNOOP_SCALE 0x1c000000 /* Scale for Max Value */ + #define PCI_EXT_CAP_LTR_SIZEOF 8 + + /* Access Control Service */ +@@ -1042,9 +1047,16 @@ + #define PCI_EXP_DPC_STATUS 0x08 /* DPC Status */ + #define PCI_EXP_DPC_STATUS_TRIGGER 0x0001 /* Trigger Status */ + #define PCI_EXP_DPC_STATUS_TRIGGER_RSN 0x0006 /* Trigger Reason */ ++#define PCI_EXP_DPC_STATUS_TRIGGER_RSN_UNCOR 0x0000 /* Uncorrectable error */ ++#define PCI_EXP_DPC_STATUS_TRIGGER_RSN_NFE 0x0002 /* Rcvd ERR_NONFATAL */ ++#define PCI_EXP_DPC_STATUS_TRIGGER_RSN_FE 0x0004 /* Rcvd ERR_FATAL */ ++#define PCI_EXP_DPC_STATUS_TRIGGER_RSN_IN_EXT 0x0006 /* Reason in Trig Reason Extension field */ + #define PCI_EXP_DPC_STATUS_INTERRUPT 0x0008 /* Interrupt Status */ + #define PCI_EXP_DPC_RP_BUSY 0x0010 /* Root Port Busy */ + #define PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT 0x0060 /* Trig Reason Extension */ ++#define PCI_EXP_DPC_STATUS_TRIGGER_RSN_RP_PIO 0x0000 /* RP PIO error */ ++#define PCI_EXP_DPC_STATUS_TRIGGER_RSN_SW_TRIGGER 0x0020 /* DPC SW Trigger bit */ ++#define PCI_EXP_DPC_RP_PIO_FEP 0x1f00 /* RP PIO First Err Ptr */ + + #define PCI_EXP_DPC_SOURCE_ID 0x0A /* DPC Source Identifier */ + +@@ -1088,6 +1100,8 @@ + #define PCI_L1SS_CTL1_LTR_L12_TH_VALUE 0x03ff0000 /* LTR_L1.2_THRESHOLD_Value */ + #define PCI_L1SS_CTL1_LTR_L12_TH_SCALE 0xe0000000 /* LTR_L1.2_THRESHOLD_Scale */ + #define PCI_L1SS_CTL2 0x0c /* Control 2 Register */ ++#define PCI_L1SS_CTL2_T_PWR_ON_SCALE 0x00000003 /* T_POWER_ON Scale */ ++#define PCI_L1SS_CTL2_T_PWR_ON_VALUE 0x000000f8 /* T_POWER_ON Value */ + + /* Designated Vendor-Specific (DVSEC, PCI_EXT_CAP_ID_DVSEC) */ + #define PCI_DVSEC_HEADER1 0x4 /* Designated Vendor-Specific Header1 */ +diff --git a/include/standard-headers/linux/vhost_types.h b/include/standard-headers/linux/vhost_types.h +index 5ad07e1..fd54044 100644 +--- a/include/standard-headers/linux/vhost_types.h ++++ b/include/standard-headers/linux/vhost_types.h +@@ -185,5 +185,12 @@ struct vhost_vdpa_iova_range { + * DRIVER_OK + */ + #define VHOST_BACKEND_F_ENABLE_AFTER_DRIVER_OK 0x6 ++/* Device may expose the virtqueue's descriptor area, driver area and ++ * device area to a different group for ASID binding than where its ++ * buffers may reside. Requires VHOST_BACKEND_F_IOTLB_ASID. ++ */ ++#define VHOST_BACKEND_F_DESC_ASID 0x7 ++/* IOTLB don't flush memory mapping across device reset */ ++#define VHOST_BACKEND_F_IOTLB_PERSIST 0x8 + + #endif +diff --git a/include/standard-headers/linux/virtio_config.h b/include/standard-headers/linux/virtio_config.h +index 8a7d0dc..bfd1ca6 100644 +--- a/include/standard-headers/linux/virtio_config.h ++++ b/include/standard-headers/linux/virtio_config.h +@@ -103,6 +103,11 @@ + */ + #define VIRTIO_F_NOTIFICATION_DATA 38 + ++/* This feature indicates that the driver uses the data provided by the device ++ * as a virtqueue identifier in available buffer notifications. ++ */ ++#define VIRTIO_F_NOTIF_CONFIG_DATA 39 ++ + /* + * This feature indicates that the driver can reset a queue individually. + */ +diff --git a/include/standard-headers/linux/virtio_pci.h b/include/standard-headers/linux/virtio_pci.h +index be912cf..b7fdfd0 100644 +--- a/include/standard-headers/linux/virtio_pci.h ++++ b/include/standard-headers/linux/virtio_pci.h +@@ -166,6 +166,17 @@ struct virtio_pci_common_cfg { + uint32_t queue_used_hi; /* read-write */ + }; + ++/* ++ * Warning: do not use sizeof on this: use offsetofend for ++ * specific fields you need. ++ */ ++struct virtio_pci_modern_common_cfg { ++ struct virtio_pci_common_cfg cfg; ++ ++ uint16_t queue_notify_data; /* read-write */ ++ uint16_t queue_reset; /* read-write */ ++}; ++ + /* Fields in VIRTIO_PCI_CAP_PCI_CFG: */ + struct virtio_pci_cfg_cap { + struct virtio_pci_cap cap; +diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h +index 38e5957..c59ea55 100644 +--- a/linux-headers/asm-arm64/kvm.h ++++ b/linux-headers/asm-arm64/kvm.h +@@ -491,6 +491,38 @@ struct kvm_smccc_filter { + #define KVM_HYPERCALL_EXIT_SMC (1U << 0) + #define KVM_HYPERCALL_EXIT_16BIT (1U << 1) + ++/* ++ * Get feature ID registers userspace writable mask. ++ * ++ * From DDI0487J.a, D19.2.66 ("ID_AA64MMFR2_EL1, AArch64 Memory Model ++ * Feature Register 2"): ++ * ++ * "The Feature ID space is defined as the System register space in ++ * AArch64 with op0==3, op1=={0, 1, 3}, CRn==0, CRm=={0-7}, ++ * op2=={0-7}." ++ * ++ * This covers all currently known R/O registers that indicate ++ * anything useful feature wise, including the ID registers. ++ * ++ * If we ever need to introduce a new range, it will be described as ++ * such in the range field. ++ */ ++#define KVM_ARM_FEATURE_ID_RANGE_IDX(op0, op1, crn, crm, op2) \ ++ ({ \ ++ __u64 __op1 = (op1) & 3; \ ++ __op1 -= (__op1 == 3); \ ++ (__op1 << 6 | ((crm) & 7) << 3 | (op2)); \ ++ }) ++ ++#define KVM_ARM_FEATURE_ID_RANGE 0 ++#define KVM_ARM_FEATURE_ID_RANGE_SIZE (3 * 8 * 8) ++ ++struct reg_mask_range { ++ __u64 addr; /* Pointer to mask array */ ++ __u32 range; /* Requested range */ ++ __u32 reserved[13]; ++}; ++ + #endif + + #endif /* __ARM_KVM_H__ */ +diff --git a/linux-headers/asm-generic/unistd.h b/linux-headers/asm-generic/unistd.h +index abe087c..756b013 100644 +--- a/linux-headers/asm-generic/unistd.h ++++ b/linux-headers/asm-generic/unistd.h +@@ -71,7 +71,7 @@ __SYSCALL(__NR_fremovexattr, sys_fremovexattr) + #define __NR_getcwd 17 + __SYSCALL(__NR_getcwd, sys_getcwd) + #define __NR_lookup_dcookie 18 +-__SC_COMP(__NR_lookup_dcookie, sys_lookup_dcookie, compat_sys_lookup_dcookie) ++__SYSCALL(__NR_lookup_dcookie, sys_ni_syscall) + #define __NR_eventfd2 19 + __SYSCALL(__NR_eventfd2, sys_eventfd2) + #define __NR_epoll_create1 20 +@@ -816,15 +816,21 @@ __SYSCALL(__NR_process_mrelease, sys_process_mrelease) + __SYSCALL(__NR_futex_waitv, sys_futex_waitv) + #define __NR_set_mempolicy_home_node 450 + __SYSCALL(__NR_set_mempolicy_home_node, sys_set_mempolicy_home_node) +- + #define __NR_cachestat 451 + __SYSCALL(__NR_cachestat, sys_cachestat) +- + #define __NR_fchmodat2 452 + __SYSCALL(__NR_fchmodat2, sys_fchmodat2) ++#define __NR_map_shadow_stack 453 ++__SYSCALL(__NR_map_shadow_stack, sys_map_shadow_stack) ++#define __NR_futex_wake 454 ++__SYSCALL(__NR_futex_wake, sys_futex_wake) ++#define __NR_futex_wait 455 ++__SYSCALL(__NR_futex_wait, sys_futex_wait) ++#define __NR_futex_requeue 456 ++__SYSCALL(__NR_futex_requeue, sys_futex_requeue) + + #undef __NR_syscalls +-#define __NR_syscalls 453 ++#define __NR_syscalls 457 + + /* + * 32 bit systems traditionally used different +diff --git a/linux-headers/asm-loongarch/bitsperlong.h b/linux-headers/asm-loongarch/bitsperlong.h +new file mode 100644 +index 0000000..6dc0bb0 +--- /dev/null ++++ b/linux-headers/asm-loongarch/bitsperlong.h +@@ -0,0 +1 @@ ++#include +diff --git a/linux-headers/asm-loongarch/kvm.h b/linux-headers/asm-loongarch/kvm.h +new file mode 100644 +index 0000000..c6ad2ee +--- /dev/null ++++ b/linux-headers/asm-loongarch/kvm.h +@@ -0,0 +1,108 @@ ++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ ++/* ++ * Copyright (C) 2020-2023 Loongson Technology Corporation Limited ++ */ ++ ++#ifndef __UAPI_ASM_LOONGARCH_KVM_H ++#define __UAPI_ASM_LOONGARCH_KVM_H ++ ++#include ++ ++/* ++ * KVM LoongArch specific structures and definitions. ++ * ++ * Some parts derived from the x86 version of this file. ++ */ ++ ++#define __KVM_HAVE_READONLY_MEM ++ ++#define KVM_COALESCED_MMIO_PAGE_OFFSET 1 ++#define KVM_DIRTY_LOG_PAGE_OFFSET 64 ++ ++/* ++ * for KVM_GET_REGS and KVM_SET_REGS ++ */ ++struct kvm_regs { ++ /* out (KVM_GET_REGS) / in (KVM_SET_REGS) */ ++ __u64 gpr[32]; ++ __u64 pc; ++}; ++ ++/* ++ * for KVM_GET_FPU and KVM_SET_FPU ++ */ ++struct kvm_fpu { ++ __u32 fcsr; ++ __u64 fcc; /* 8x8 */ ++ struct kvm_fpureg { ++ __u64 val64[4]; ++ } fpr[32]; ++}; ++ ++/* ++ * For LoongArch, we use KVM_SET_ONE_REG and KVM_GET_ONE_REG to access various ++ * registers. The id field is broken down as follows: ++ * ++ * bits[63..52] - As per linux/kvm.h ++ * bits[51..32] - Must be zero. ++ * bits[31..16] - Register set. ++ * ++ * Register set = 0: GP registers from kvm_regs (see definitions below). ++ * ++ * Register set = 1: CSR registers. ++ * ++ * Register set = 2: KVM specific registers (see definitions below). ++ * ++ * Register set = 3: FPU / SIMD registers (see definitions below). ++ * ++ * Other sets registers may be added in the future. Each set would ++ * have its own identifier in bits[31..16]. ++ */ ++ ++#define KVM_REG_LOONGARCH_GPR (KVM_REG_LOONGARCH | 0x00000ULL) ++#define KVM_REG_LOONGARCH_CSR (KVM_REG_LOONGARCH | 0x10000ULL) ++#define KVM_REG_LOONGARCH_KVM (KVM_REG_LOONGARCH | 0x20000ULL) ++#define KVM_REG_LOONGARCH_FPSIMD (KVM_REG_LOONGARCH | 0x30000ULL) ++#define KVM_REG_LOONGARCH_CPUCFG (KVM_REG_LOONGARCH | 0x40000ULL) ++#define KVM_REG_LOONGARCH_MASK (KVM_REG_LOONGARCH | 0x70000ULL) ++#define KVM_CSR_IDX_MASK 0x7fff ++#define KVM_CPUCFG_IDX_MASK 0x7fff ++ ++/* ++ * KVM_REG_LOONGARCH_KVM - KVM specific control registers. ++ */ ++ ++#define KVM_REG_LOONGARCH_COUNTER (KVM_REG_LOONGARCH_KVM | KVM_REG_SIZE_U64 | 1) ++#define KVM_REG_LOONGARCH_VCPU_RESET (KVM_REG_LOONGARCH_KVM | KVM_REG_SIZE_U64 | 2) ++ ++#define LOONGARCH_REG_SHIFT 3 ++#define LOONGARCH_REG_64(TYPE, REG) (TYPE | KVM_REG_SIZE_U64 | (REG << LOONGARCH_REG_SHIFT)) ++#define KVM_IOC_CSRID(REG) LOONGARCH_REG_64(KVM_REG_LOONGARCH_CSR, REG) ++#define KVM_IOC_CPUCFG(REG) LOONGARCH_REG_64(KVM_REG_LOONGARCH_CPUCFG, REG) ++ ++struct kvm_debug_exit_arch { ++}; ++ ++/* for KVM_SET_GUEST_DEBUG */ ++struct kvm_guest_debug_arch { ++}; ++ ++/* definition of registers in kvm_run */ ++struct kvm_sync_regs { ++}; ++ ++/* dummy definition */ ++struct kvm_sregs { ++}; ++ ++struct kvm_iocsr_entry { ++ __u32 addr; ++ __u32 pad; ++ __u64 data; ++}; ++ ++#define KVM_NR_IRQCHIPS 1 ++#define KVM_IRQCHIP_NUM_PINS 64 ++#define KVM_MAX_CORES 256 ++ ++#endif /* __UAPI_ASM_LOONGARCH_KVM_H */ +diff --git a/linux-headers/asm-loongarch/mman.h b/linux-headers/asm-loongarch/mman.h +new file mode 100644 +index 0000000..8eebf89 +--- /dev/null ++++ b/linux-headers/asm-loongarch/mman.h +@@ -0,0 +1 @@ ++#include +diff --git a/linux-headers/asm-loongarch/unistd.h b/linux-headers/asm-loongarch/unistd.h +new file mode 100644 +index 0000000..fcb6689 +--- /dev/null ++++ b/linux-headers/asm-loongarch/unistd.h +@@ -0,0 +1,5 @@ ++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ ++#define __ARCH_WANT_SYS_CLONE ++#define __ARCH_WANT_SYS_CLONE3 ++ ++#include +diff --git a/linux-headers/asm-mips/unistd_n32.h b/linux-headers/asm-mips/unistd_n32.h +index 46d8500..994b6f0 100644 +--- a/linux-headers/asm-mips/unistd_n32.h ++++ b/linux-headers/asm-mips/unistd_n32.h +@@ -381,5 +381,9 @@ + #define __NR_set_mempolicy_home_node (__NR_Linux + 450) + #define __NR_cachestat (__NR_Linux + 451) + #define __NR_fchmodat2 (__NR_Linux + 452) ++#define __NR_map_shadow_stack (__NR_Linux + 453) ++#define __NR_futex_wake (__NR_Linux + 454) ++#define __NR_futex_wait (__NR_Linux + 455) ++#define __NR_futex_requeue (__NR_Linux + 456) + + #endif /* _ASM_UNISTD_N32_H */ +diff --git a/linux-headers/asm-mips/unistd_n64.h b/linux-headers/asm-mips/unistd_n64.h +index c2f7ac6..41dcf58 100644 +--- a/linux-headers/asm-mips/unistd_n64.h ++++ b/linux-headers/asm-mips/unistd_n64.h +@@ -357,5 +357,9 @@ + #define __NR_set_mempolicy_home_node (__NR_Linux + 450) + #define __NR_cachestat (__NR_Linux + 451) + #define __NR_fchmodat2 (__NR_Linux + 452) ++#define __NR_map_shadow_stack (__NR_Linux + 453) ++#define __NR_futex_wake (__NR_Linux + 454) ++#define __NR_futex_wait (__NR_Linux + 455) ++#define __NR_futex_requeue (__NR_Linux + 456) + + #endif /* _ASM_UNISTD_N64_H */ +diff --git a/linux-headers/asm-mips/unistd_o32.h b/linux-headers/asm-mips/unistd_o32.h +index 757c68f..ae9d334 100644 +--- a/linux-headers/asm-mips/unistd_o32.h ++++ b/linux-headers/asm-mips/unistd_o32.h +@@ -427,5 +427,9 @@ + #define __NR_set_mempolicy_home_node (__NR_Linux + 450) + #define __NR_cachestat (__NR_Linux + 451) + #define __NR_fchmodat2 (__NR_Linux + 452) ++#define __NR_map_shadow_stack (__NR_Linux + 453) ++#define __NR_futex_wake (__NR_Linux + 454) ++#define __NR_futex_wait (__NR_Linux + 455) ++#define __NR_futex_requeue (__NR_Linux + 456) + + #endif /* _ASM_UNISTD_O32_H */ +diff --git a/linux-headers/asm-powerpc/unistd_32.h b/linux-headers/asm-powerpc/unistd_32.h +index 8ef94bb..b9b23d6 100644 +--- a/linux-headers/asm-powerpc/unistd_32.h ++++ b/linux-headers/asm-powerpc/unistd_32.h +@@ -434,6 +434,10 @@ + #define __NR_set_mempolicy_home_node 450 + #define __NR_cachestat 451 + #define __NR_fchmodat2 452 ++#define __NR_map_shadow_stack 453 ++#define __NR_futex_wake 454 ++#define __NR_futex_wait 455 ++#define __NR_futex_requeue 456 + + + #endif /* _ASM_UNISTD_32_H */ +diff --git a/linux-headers/asm-powerpc/unistd_64.h b/linux-headers/asm-powerpc/unistd_64.h +index 0e7ee43..cbb4b3e 100644 +--- a/linux-headers/asm-powerpc/unistd_64.h ++++ b/linux-headers/asm-powerpc/unistd_64.h +@@ -406,6 +406,10 @@ + #define __NR_set_mempolicy_home_node 450 + #define __NR_cachestat 451 + #define __NR_fchmodat2 452 ++#define __NR_map_shadow_stack 453 ++#define __NR_futex_wake 454 ++#define __NR_futex_wait 455 ++#define __NR_futex_requeue 456 + + + #endif /* _ASM_UNISTD_64_H */ +diff --git a/linux-headers/asm-riscv/kvm.h b/linux-headers/asm-riscv/kvm.h +index 992c5e4..60d3b21 100644 +--- a/linux-headers/asm-riscv/kvm.h ++++ b/linux-headers/asm-riscv/kvm.h +@@ -80,6 +80,7 @@ struct kvm_riscv_csr { + unsigned long sip; + unsigned long satp; + unsigned long scounteren; ++ unsigned long senvcfg; + }; + + /* AIA CSR registers for KVM_GET_ONE_REG and KVM_SET_ONE_REG */ +@@ -93,6 +94,11 @@ struct kvm_riscv_aia_csr { + unsigned long iprio2h; + }; + ++/* Smstateen CSR for KVM_GET_ONE_REG and KVM_SET_ONE_REG */ ++struct kvm_riscv_smstateen_csr { ++ unsigned long sstateen0; ++}; ++ + /* TIMER registers for KVM_GET_ONE_REG and KVM_SET_ONE_REG */ + struct kvm_riscv_timer { + __u64 frequency; +@@ -131,6 +137,8 @@ enum KVM_RISCV_ISA_EXT_ID { + KVM_RISCV_ISA_EXT_ZICSR, + KVM_RISCV_ISA_EXT_ZIFENCEI, + KVM_RISCV_ISA_EXT_ZIHPM, ++ KVM_RISCV_ISA_EXT_SMSTATEEN, ++ KVM_RISCV_ISA_EXT_ZICOND, + KVM_RISCV_ISA_EXT_MAX, + }; + +@@ -148,6 +156,7 @@ enum KVM_RISCV_SBI_EXT_ID { + KVM_RISCV_SBI_EXT_PMU, + KVM_RISCV_SBI_EXT_EXPERIMENTAL, + KVM_RISCV_SBI_EXT_VENDOR, ++ KVM_RISCV_SBI_EXT_DBCN, + KVM_RISCV_SBI_EXT_MAX, + }; + +@@ -178,10 +187,13 @@ enum KVM_RISCV_SBI_EXT_ID { + #define KVM_REG_RISCV_CSR (0x03 << KVM_REG_RISCV_TYPE_SHIFT) + #define KVM_REG_RISCV_CSR_GENERAL (0x0 << KVM_REG_RISCV_SUBTYPE_SHIFT) + #define KVM_REG_RISCV_CSR_AIA (0x1 << KVM_REG_RISCV_SUBTYPE_SHIFT) ++#define KVM_REG_RISCV_CSR_SMSTATEEN (0x2 << KVM_REG_RISCV_SUBTYPE_SHIFT) + #define KVM_REG_RISCV_CSR_REG(name) \ + (offsetof(struct kvm_riscv_csr, name) / sizeof(unsigned long)) + #define KVM_REG_RISCV_CSR_AIA_REG(name) \ + (offsetof(struct kvm_riscv_aia_csr, name) / sizeof(unsigned long)) ++#define KVM_REG_RISCV_CSR_SMSTATEEN_REG(name) \ ++ (offsetof(struct kvm_riscv_smstateen_csr, name) / sizeof(unsigned long)) + + /* Timer registers are mapped as type 4 */ + #define KVM_REG_RISCV_TIMER (0x04 << KVM_REG_RISCV_TYPE_SHIFT) +diff --git a/linux-headers/asm-s390/unistd_32.h b/linux-headers/asm-s390/unistd_32.h +index 716fa36..c093e6d 100644 +--- a/linux-headers/asm-s390/unistd_32.h ++++ b/linux-headers/asm-s390/unistd_32.h +@@ -425,5 +425,9 @@ + #define __NR_set_mempolicy_home_node 450 + #define __NR_cachestat 451 + #define __NR_fchmodat2 452 ++#define __NR_map_shadow_stack 453 ++#define __NR_futex_wake 454 ++#define __NR_futex_wait 455 ++#define __NR_futex_requeue 456 + + #endif /* _ASM_S390_UNISTD_32_H */ +diff --git a/linux-headers/asm-s390/unistd_64.h b/linux-headers/asm-s390/unistd_64.h +index b2a11b1..114c056 100644 +--- a/linux-headers/asm-s390/unistd_64.h ++++ b/linux-headers/asm-s390/unistd_64.h +@@ -373,5 +373,9 @@ + #define __NR_set_mempolicy_home_node 450 + #define __NR_cachestat 451 + #define __NR_fchmodat2 452 ++#define __NR_map_shadow_stack 453 ++#define __NR_futex_wake 454 ++#define __NR_futex_wait 455 ++#define __NR_futex_requeue 456 + + #endif /* _ASM_S390_UNISTD_64_H */ +diff --git a/linux-headers/asm-x86/unistd_32.h b/linux-headers/asm-x86/unistd_32.h +index d749ad1..329649c 100644 +--- a/linux-headers/asm-x86/unistd_32.h ++++ b/linux-headers/asm-x86/unistd_32.h +@@ -443,6 +443,10 @@ + #define __NR_set_mempolicy_home_node 450 + #define __NR_cachestat 451 + #define __NR_fchmodat2 452 ++#define __NR_map_shadow_stack 453 ++#define __NR_futex_wake 454 ++#define __NR_futex_wait 455 ++#define __NR_futex_requeue 456 + + + #endif /* _ASM_UNISTD_32_H */ +diff --git a/linux-headers/asm-x86/unistd_64.h b/linux-headers/asm-x86/unistd_64.h +index cea6728..4583606 100644 +--- a/linux-headers/asm-x86/unistd_64.h ++++ b/linux-headers/asm-x86/unistd_64.h +@@ -366,6 +366,9 @@ + #define __NR_cachestat 451 + #define __NR_fchmodat2 452 + #define __NR_map_shadow_stack 453 ++#define __NR_futex_wake 454 ++#define __NR_futex_wait 455 ++#define __NR_futex_requeue 456 + + + #endif /* _ASM_UNISTD_64_H */ +diff --git a/linux-headers/asm-x86/unistd_x32.h b/linux-headers/asm-x86/unistd_x32.h +index 5b2e79b..146d74d 100644 +--- a/linux-headers/asm-x86/unistd_x32.h ++++ b/linux-headers/asm-x86/unistd_x32.h +@@ -318,6 +318,9 @@ + #define __NR_set_mempolicy_home_node (__X32_SYSCALL_BIT + 450) + #define __NR_cachestat (__X32_SYSCALL_BIT + 451) + #define __NR_fchmodat2 (__X32_SYSCALL_BIT + 452) ++#define __NR_futex_wake (__X32_SYSCALL_BIT + 454) ++#define __NR_futex_wait (__X32_SYSCALL_BIT + 455) ++#define __NR_futex_requeue (__X32_SYSCALL_BIT + 456) + #define __NR_rt_sigaction (__X32_SYSCALL_BIT + 512) + #define __NR_rt_sigreturn (__X32_SYSCALL_BIT + 513) + #define __NR_ioctl (__X32_SYSCALL_BIT + 514) +diff --git a/linux-headers/linux/iommufd.h b/linux-headers/linux/iommufd.h +index 218bf7a..806d98d 100644 +--- a/linux-headers/linux/iommufd.h ++++ b/linux-headers/linux/iommufd.h +@@ -47,6 +47,8 @@ enum { + IOMMUFD_CMD_VFIO_IOAS, + IOMMUFD_CMD_HWPT_ALLOC, + IOMMUFD_CMD_GET_HW_INFO, ++ IOMMUFD_CMD_HWPT_SET_DIRTY_TRACKING, ++ IOMMUFD_CMD_HWPT_GET_DIRTY_BITMAP, + }; + + /** +@@ -348,19 +350,85 @@ struct iommu_vfio_ioas { + #define IOMMU_VFIO_IOAS _IO(IOMMUFD_TYPE, IOMMUFD_CMD_VFIO_IOAS) + + /** ++ * enum iommufd_hwpt_alloc_flags - Flags for HWPT allocation ++ * @IOMMU_HWPT_ALLOC_NEST_PARENT: If set, allocate a HWPT that can serve as ++ * the parent HWPT in a nesting configuration. ++ * @IOMMU_HWPT_ALLOC_DIRTY_TRACKING: Dirty tracking support for device IOMMU is ++ * enforced on device attachment ++ */ ++enum iommufd_hwpt_alloc_flags { ++ IOMMU_HWPT_ALLOC_NEST_PARENT = 1 << 0, ++ IOMMU_HWPT_ALLOC_DIRTY_TRACKING = 1 << 1, ++}; ++ ++/** ++ * enum iommu_hwpt_vtd_s1_flags - Intel VT-d stage-1 page table ++ * entry attributes ++ * @IOMMU_VTD_S1_SRE: Supervisor request ++ * @IOMMU_VTD_S1_EAFE: Extended access enable ++ * @IOMMU_VTD_S1_WPE: Write protect enable ++ */ ++enum iommu_hwpt_vtd_s1_flags { ++ IOMMU_VTD_S1_SRE = 1 << 0, ++ IOMMU_VTD_S1_EAFE = 1 << 1, ++ IOMMU_VTD_S1_WPE = 1 << 2, ++}; ++ ++/** ++ * struct iommu_hwpt_vtd_s1 - Intel VT-d stage-1 page table ++ * info (IOMMU_HWPT_DATA_VTD_S1) ++ * @flags: Combination of enum iommu_hwpt_vtd_s1_flags ++ * @pgtbl_addr: The base address of the stage-1 page table. ++ * @addr_width: The address width of the stage-1 page table ++ * @__reserved: Must be 0 ++ */ ++struct iommu_hwpt_vtd_s1 { ++ __aligned_u64 flags; ++ __aligned_u64 pgtbl_addr; ++ __u32 addr_width; ++ __u32 __reserved; ++}; ++ ++/** ++ * enum iommu_hwpt_data_type - IOMMU HWPT Data Type ++ * @IOMMU_HWPT_DATA_NONE: no data ++ * @IOMMU_HWPT_DATA_VTD_S1: Intel VT-d stage-1 page table ++ */ ++enum iommu_hwpt_data_type { ++ IOMMU_HWPT_DATA_NONE, ++ IOMMU_HWPT_DATA_VTD_S1, ++}; ++ ++/** + * struct iommu_hwpt_alloc - ioctl(IOMMU_HWPT_ALLOC) + * @size: sizeof(struct iommu_hwpt_alloc) +- * @flags: Must be 0 ++ * @flags: Combination of enum iommufd_hwpt_alloc_flags + * @dev_id: The device to allocate this HWPT for +- * @pt_id: The IOAS to connect this HWPT to ++ * @pt_id: The IOAS or HWPT to connect this HWPT to + * @out_hwpt_id: The ID of the new HWPT + * @__reserved: Must be 0 ++ * @data_type: One of enum iommu_hwpt_data_type ++ * @data_len: Length of the type specific data ++ * @data_uptr: User pointer to the type specific data + * + * Explicitly allocate a hardware page table object. This is the same object + * type that is returned by iommufd_device_attach() and represents the + * underlying iommu driver's iommu_domain kernel object. + * +- * A HWPT will be created with the IOVA mappings from the given IOAS. ++ * A kernel-managed HWPT will be created with the mappings from the given ++ * IOAS via the @pt_id. The @data_type for this allocation must be set to ++ * IOMMU_HWPT_DATA_NONE. The HWPT can be allocated as a parent HWPT for a ++ * nesting configuration by passing IOMMU_HWPT_ALLOC_NEST_PARENT via @flags. ++ * ++ * A user-managed nested HWPT will be created from a given parent HWPT via ++ * @pt_id, in which the parent HWPT must be allocated previously via the ++ * same ioctl from a given IOAS (@pt_id). In this case, the @data_type ++ * must be set to a pre-defined type corresponding to an I/O page table ++ * type supported by the underlying IOMMU hardware. ++ * ++ * If the @data_type is set to IOMMU_HWPT_DATA_NONE, @data_len and ++ * @data_uptr should be zero. Otherwise, both @data_len and @data_uptr ++ * must be given. + */ + struct iommu_hwpt_alloc { + __u32 size; +@@ -369,13 +437,26 @@ struct iommu_hwpt_alloc { + __u32 pt_id; + __u32 out_hwpt_id; + __u32 __reserved; ++ __u32 data_type; ++ __u32 data_len; ++ __aligned_u64 data_uptr; + }; + #define IOMMU_HWPT_ALLOC _IO(IOMMUFD_TYPE, IOMMUFD_CMD_HWPT_ALLOC) + + /** ++ * enum iommu_hw_info_vtd_flags - Flags for VT-d hw_info ++ * @IOMMU_HW_INFO_VTD_ERRATA_772415_SPR17: If set, disallow read-only mappings ++ * on a nested_parent domain. ++ * https://www.intel.com/content/www/us/en/content-details/772415/content-details.html ++ */ ++enum iommu_hw_info_vtd_flags { ++ IOMMU_HW_INFO_VTD_ERRATA_772415_SPR17 = 1 << 0, ++}; ++ ++/** + * struct iommu_hw_info_vtd - Intel VT-d hardware information + * +- * @flags: Must be 0 ++ * @flags: Combination of enum iommu_hw_info_vtd_flags + * @__reserved: Must be 0 + * + * @cap_reg: Value of Intel VT-d capability register defined in VT-d spec +@@ -405,6 +486,20 @@ enum iommu_hw_info_type { + }; + + /** ++ * enum iommufd_hw_capabilities ++ * @IOMMU_HW_CAP_DIRTY_TRACKING: IOMMU hardware support for dirty tracking ++ * If available, it means the following APIs ++ * are supported: ++ * ++ * IOMMU_HWPT_GET_DIRTY_BITMAP ++ * IOMMU_HWPT_SET_DIRTY_TRACKING ++ * ++ */ ++enum iommufd_hw_capabilities { ++ IOMMU_HW_CAP_DIRTY_TRACKING = 1 << 0, ++}; ++ ++/** + * struct iommu_hw_info - ioctl(IOMMU_GET_HW_INFO) + * @size: sizeof(struct iommu_hw_info) + * @flags: Must be 0 +@@ -415,6 +510,8 @@ enum iommu_hw_info_type { + * the iommu type specific hardware information data + * @out_data_type: Output the iommu hardware info type as defined in the enum + * iommu_hw_info_type. ++ * @out_capabilities: Output the generic iommu capability info type as defined ++ * in the enum iommu_hw_capabilities. + * @__reserved: Must be 0 + * + * Query an iommu type specific hardware information data from an iommu behind +@@ -439,6 +536,81 @@ struct iommu_hw_info { + __aligned_u64 data_uptr; + __u32 out_data_type; + __u32 __reserved; ++ __aligned_u64 out_capabilities; + }; + #define IOMMU_GET_HW_INFO _IO(IOMMUFD_TYPE, IOMMUFD_CMD_GET_HW_INFO) ++ ++/* ++ * enum iommufd_hwpt_set_dirty_tracking_flags - Flags for steering dirty ++ * tracking ++ * @IOMMU_HWPT_DIRTY_TRACKING_ENABLE: Enable dirty tracking ++ */ ++enum iommufd_hwpt_set_dirty_tracking_flags { ++ IOMMU_HWPT_DIRTY_TRACKING_ENABLE = 1, ++}; ++ ++/** ++ * struct iommu_hwpt_set_dirty_tracking - ioctl(IOMMU_HWPT_SET_DIRTY_TRACKING) ++ * @size: sizeof(struct iommu_hwpt_set_dirty_tracking) ++ * @flags: Combination of enum iommufd_hwpt_set_dirty_tracking_flags ++ * @hwpt_id: HW pagetable ID that represents the IOMMU domain ++ * @__reserved: Must be 0 ++ * ++ * Toggle dirty tracking on an HW pagetable. ++ */ ++struct iommu_hwpt_set_dirty_tracking { ++ __u32 size; ++ __u32 flags; ++ __u32 hwpt_id; ++ __u32 __reserved; ++}; ++#define IOMMU_HWPT_SET_DIRTY_TRACKING _IO(IOMMUFD_TYPE, \ ++ IOMMUFD_CMD_HWPT_SET_DIRTY_TRACKING) ++ ++/** ++ * enum iommufd_hwpt_get_dirty_bitmap_flags - Flags for getting dirty bits ++ * @IOMMU_HWPT_GET_DIRTY_BITMAP_NO_CLEAR: Just read the PTEs without clearing ++ * any dirty bits metadata. This flag ++ * can be passed in the expectation ++ * where the next operation is an unmap ++ * of the same IOVA range. ++ * ++ */ ++enum iommufd_hwpt_get_dirty_bitmap_flags { ++ IOMMU_HWPT_GET_DIRTY_BITMAP_NO_CLEAR = 1, ++}; ++ ++/** ++ * struct iommu_hwpt_get_dirty_bitmap - ioctl(IOMMU_HWPT_GET_DIRTY_BITMAP) ++ * @size: sizeof(struct iommu_hwpt_get_dirty_bitmap) ++ * @hwpt_id: HW pagetable ID that represents the IOMMU domain ++ * @flags: Combination of enum iommufd_hwpt_get_dirty_bitmap_flags ++ * @__reserved: Must be 0 ++ * @iova: base IOVA of the bitmap first bit ++ * @length: IOVA range size ++ * @page_size: page size granularity of each bit in the bitmap ++ * @data: bitmap where to set the dirty bits. The bitmap bits each ++ * represent a page_size which you deviate from an arbitrary iova. ++ * ++ * Checking a given IOVA is dirty: ++ * ++ * data[(iova / page_size) / 64] & (1ULL << ((iova / page_size) % 64)) ++ * ++ * Walk the IOMMU pagetables for a given IOVA range to return a bitmap ++ * with the dirty IOVAs. In doing so it will also by default clear any ++ * dirty bit metadata set in the IOPTE. ++ */ ++struct iommu_hwpt_get_dirty_bitmap { ++ __u32 size; ++ __u32 hwpt_id; ++ __u32 flags; ++ __u32 __reserved; ++ __aligned_u64 iova; ++ __aligned_u64 length; ++ __aligned_u64 page_size; ++ __aligned_u64 data; ++}; ++#define IOMMU_HWPT_GET_DIRTY_BITMAP _IO(IOMMUFD_TYPE, \ ++ IOMMUFD_CMD_HWPT_GET_DIRTY_BITMAP) ++ + #endif +diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h +index 0d74ee9..549fea3 100644 +--- a/linux-headers/linux/kvm.h ++++ b/linux-headers/linux/kvm.h +@@ -264,6 +264,7 @@ struct kvm_xen_exit { + #define KVM_EXIT_RISCV_SBI 35 + #define KVM_EXIT_RISCV_CSR 36 + #define KVM_EXIT_NOTIFY 37 ++#define KVM_EXIT_LOONGARCH_IOCSR 38 + + /* For KVM_EXIT_INTERNAL_ERROR */ + /* Emulate instruction failed. */ +@@ -336,6 +337,13 @@ struct kvm_run { + __u32 len; + __u8 is_write; + } mmio; ++ /* KVM_EXIT_LOONGARCH_IOCSR */ ++ struct { ++ __u64 phys_addr; ++ __u8 data[8]; ++ __u32 len; ++ __u8 is_write; ++ } iocsr_io; + /* KVM_EXIT_HYPERCALL */ + struct { + __u64 nr; +@@ -1188,6 +1196,7 @@ struct kvm_ppc_resize_hpt { + #define KVM_CAP_COUNTER_OFFSET 227 + #define KVM_CAP_ARM_EAGER_SPLIT_CHUNK_SIZE 228 + #define KVM_CAP_ARM_SUPPORTED_BLOCK_SIZES 229 ++#define KVM_CAP_ARM_SUPPORTED_REG_MASK_RANGES 230 + + #ifdef KVM_CAP_IRQ_ROUTING + +@@ -1358,6 +1367,7 @@ struct kvm_dirty_tlb { + #define KVM_REG_ARM64 0x6000000000000000ULL + #define KVM_REG_MIPS 0x7000000000000000ULL + #define KVM_REG_RISCV 0x8000000000000000ULL ++#define KVM_REG_LOONGARCH 0x9000000000000000ULL + + #define KVM_REG_SIZE_SHIFT 52 + #define KVM_REG_SIZE_MASK 0x00f0000000000000ULL +@@ -1558,6 +1568,7 @@ struct kvm_s390_ucas_mapping { + #define KVM_ARM_MTE_COPY_TAGS _IOR(KVMIO, 0xb4, struct kvm_arm_copy_mte_tags) + /* Available with KVM_CAP_COUNTER_OFFSET */ + #define KVM_ARM_SET_COUNTER_OFFSET _IOW(KVMIO, 0xb5, struct kvm_arm_counter_offset) ++#define KVM_ARM_GET_REG_WRITABLE_MASKS _IOR(KVMIO, 0xb6, struct reg_mask_range) + + /* ioctl for vm fd */ + #define KVM_CREATE_DEVICE _IOWR(KVMIO, 0xe0, struct kvm_create_device) +diff --git a/linux-headers/linux/psp-sev.h b/linux-headers/linux/psp-sev.h +index 12ccb70..bcb2133 100644 +--- a/linux-headers/linux/psp-sev.h ++++ b/linux-headers/linux/psp-sev.h +@@ -68,6 +68,7 @@ typedef enum { + SEV_RET_INVALID_PARAM, + SEV_RET_RESOURCE_LIMIT, + SEV_RET_SECURE_DATA_INVALID, ++ SEV_RET_INVALID_KEY = 0x27, + SEV_RET_MAX, + } sev_ret_code; + +diff --git a/linux-headers/linux/stddef.h b/linux-headers/linux/stddef.h +index 9bb0708..bf9749d 100644 +--- a/linux-headers/linux/stddef.h ++++ b/linux-headers/linux/stddef.h +@@ -27,8 +27,13 @@ + union { \ + struct { MEMBERS } ATTRS; \ + struct TAG { MEMBERS } ATTRS NAME; \ +- } ++ } ATTRS + ++#ifdef __cplusplus ++/* sizeof(struct{}) is 1 in C++, not 0, can't use C version of the macro. */ ++#define __DECLARE_FLEX_ARRAY(T, member) \ ++ T member[0] ++#else + /** + * __DECLARE_FLEX_ARRAY() - Declare a flexible array usable in a union + * +@@ -49,3 +54,5 @@ + #ifndef __counted_by + #define __counted_by(m) + #endif ++ ++#endif /* _LINUX_STDDEF_H */ +diff --git a/linux-headers/linux/userfaultfd.h b/linux-headers/linux/userfaultfd.h +index 59978fb..953c75f 100644 +--- a/linux-headers/linux/userfaultfd.h ++++ b/linux-headers/linux/userfaultfd.h +@@ -40,7 +40,8 @@ + UFFD_FEATURE_EXACT_ADDRESS | \ + UFFD_FEATURE_WP_HUGETLBFS_SHMEM | \ + UFFD_FEATURE_WP_UNPOPULATED | \ +- UFFD_FEATURE_POISON) ++ UFFD_FEATURE_POISON | \ ++ UFFD_FEATURE_WP_ASYNC) + #define UFFD_API_IOCTLS \ + ((__u64)1 << _UFFDIO_REGISTER | \ + (__u64)1 << _UFFDIO_UNREGISTER | \ +@@ -216,6 +217,11 @@ struct uffdio_api { + * (i.e. empty ptes). This will be the default behavior for shmem + * & hugetlbfs, so this flag only affects anonymous memory behavior + * when userfault write-protection mode is registered. ++ * ++ * UFFD_FEATURE_WP_ASYNC indicates that userfaultfd write-protection ++ * asynchronous mode is supported in which the write fault is ++ * automatically resolved and write-protection is un-set. ++ * It implies UFFD_FEATURE_WP_UNPOPULATED. + */ + #define UFFD_FEATURE_PAGEFAULT_FLAG_WP (1<<0) + #define UFFD_FEATURE_EVENT_FORK (1<<1) +@@ -232,6 +238,7 @@ struct uffdio_api { + #define UFFD_FEATURE_WP_HUGETLBFS_SHMEM (1<<12) + #define UFFD_FEATURE_WP_UNPOPULATED (1<<13) + #define UFFD_FEATURE_POISON (1<<14) ++#define UFFD_FEATURE_WP_ASYNC (1<<15) + __u64 features; + + __u64 ioctls; +diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h +index acf72b4..8e175ece3 100644 +--- a/linux-headers/linux/vfio.h ++++ b/linux-headers/linux/vfio.h +@@ -277,8 +277,8 @@ struct vfio_region_info { + #define VFIO_REGION_INFO_FLAG_CAPS (1 << 3) /* Info supports caps */ + __u32 index; /* Region index */ + __u32 cap_offset; /* Offset within info struct of first cap */ +- __u64 size; /* Region size (bytes) */ +- __u64 offset; /* Region offset from start of device fd */ ++ __aligned_u64 size; /* Region size (bytes) */ ++ __aligned_u64 offset; /* Region offset from start of device fd */ + }; + #define VFIO_DEVICE_GET_REGION_INFO _IO(VFIO_TYPE, VFIO_BASE + 8) + +@@ -294,8 +294,8 @@ struct vfio_region_info { + #define VFIO_REGION_INFO_CAP_SPARSE_MMAP 1 + + struct vfio_region_sparse_mmap_area { +- __u64 offset; /* Offset of mmap'able area within region */ +- __u64 size; /* Size of mmap'able area */ ++ __aligned_u64 offset; /* Offset of mmap'able area within region */ ++ __aligned_u64 size; /* Size of mmap'able area */ + }; + + struct vfio_region_info_cap_sparse_mmap { +@@ -450,9 +450,9 @@ struct vfio_device_migration_info { + VFIO_DEVICE_STATE_V1_RESUMING) + + __u32 reserved; +- __u64 pending_bytes; +- __u64 data_offset; +- __u64 data_size; ++ __aligned_u64 pending_bytes; ++ __aligned_u64 data_offset; ++ __aligned_u64 data_size; + }; + + /* +@@ -476,7 +476,7 @@ struct vfio_device_migration_info { + + struct vfio_region_info_cap_nvlink2_ssatgt { + struct vfio_info_cap_header header; +- __u64 tgt; ++ __aligned_u64 tgt; + }; + + /* +@@ -816,7 +816,7 @@ struct vfio_device_gfx_plane_info { + __u32 drm_plane_type; /* type of plane: DRM_PLANE_TYPE_* */ + /* out */ + __u32 drm_format; /* drm format of plane */ +- __u64 drm_format_mod; /* tiled mode */ ++ __aligned_u64 drm_format_mod; /* tiled mode */ + __u32 width; /* width of plane */ + __u32 height; /* height of plane */ + __u32 stride; /* stride of plane */ +@@ -829,6 +829,7 @@ struct vfio_device_gfx_plane_info { + __u32 region_index; /* region index */ + __u32 dmabuf_id; /* dma-buf id */ + }; ++ __u32 reserved; + }; + + #define VFIO_DEVICE_QUERY_GFX_PLANE _IO(VFIO_TYPE, VFIO_BASE + 14) +@@ -863,9 +864,10 @@ struct vfio_device_ioeventfd { + #define VFIO_DEVICE_IOEVENTFD_32 (1 << 2) /* 4-byte write */ + #define VFIO_DEVICE_IOEVENTFD_64 (1 << 3) /* 8-byte write */ + #define VFIO_DEVICE_IOEVENTFD_SIZE_MASK (0xf) +- __u64 offset; /* device fd offset of write */ +- __u64 data; /* data to be written */ ++ __aligned_u64 offset; /* device fd offset of write */ ++ __aligned_u64 data; /* data to be written */ + __s32 fd; /* -1 for de-assignment */ ++ __u32 reserved; + }; + + #define VFIO_DEVICE_IOEVENTFD _IO(VFIO_TYPE, VFIO_BASE + 16) +@@ -1434,6 +1436,27 @@ struct vfio_device_feature_mig_data_size { + + #define VFIO_DEVICE_FEATURE_MIG_DATA_SIZE 9 + ++/** ++ * Upon VFIO_DEVICE_FEATURE_SET, set or clear the BUS mastering for the device ++ * based on the operation specified in op flag. ++ * ++ * The functionality is incorporated for devices that needs bus master control, ++ * but the in-band device interface lacks the support. Consequently, it is not ++ * applicable to PCI devices, as bus master control for PCI devices is managed ++ * in-band through the configuration space. At present, this feature is supported ++ * only for CDX devices. ++ * When the device's BUS MASTER setting is configured as CLEAR, it will result in ++ * blocking all incoming DMA requests from the device. On the other hand, configuring ++ * the device's BUS MASTER setting as SET (enable) will grant the device the ++ * capability to perform DMA to the host memory. ++ */ ++struct vfio_device_feature_bus_master { ++ __u32 op; ++#define VFIO_DEVICE_FEATURE_CLEAR_MASTER 0 /* Clear Bus Master */ ++#define VFIO_DEVICE_FEATURE_SET_MASTER 1 /* Set Bus Master */ ++}; ++#define VFIO_DEVICE_FEATURE_BUS_MASTER 10 ++ + /* -------- API for Type1 VFIO IOMMU -------- */ + + /** +@@ -1449,7 +1472,7 @@ struct vfio_iommu_type1_info { + __u32 flags; + #define VFIO_IOMMU_INFO_PGSIZES (1 << 0) /* supported page sizes info */ + #define VFIO_IOMMU_INFO_CAPS (1 << 1) /* Info supports caps */ +- __u64 iova_pgsizes; /* Bitmap of supported page sizes */ ++ __aligned_u64 iova_pgsizes; /* Bitmap of supported page sizes */ + __u32 cap_offset; /* Offset within info struct of first cap */ + __u32 pad; + }; +diff --git a/linux-headers/linux/vhost.h b/linux-headers/linux/vhost.h +index f5c48b6..649560c 100644 +--- a/linux-headers/linux/vhost.h ++++ b/linux-headers/linux/vhost.h +@@ -219,4 +219,12 @@ + */ + #define VHOST_VDPA_RESUME _IO(VHOST_VIRTIO, 0x7E) + ++/* Get the group for the descriptor table including driver & device areas ++ * of a virtqueue: read index, write group in num. ++ * The virtqueue index is stored in the index field of vhost_vring_state. ++ * The group ID of the descriptor table for this specific virtqueue ++ * is returned via num field of vhost_vring_state. ++ */ ++#define VHOST_VDPA_GET_VRING_DESC_GROUP _IOWR(VHOST_VIRTIO, 0x7F, \ ++ struct vhost_vring_state) + #endif +-- +1.8.3.1 + diff --git a/0153-linux-headers-Synchronize-linux-headers-from-linux-v.patch b/0153-linux-headers-Synchronize-linux-headers-from-linux-v.patch new file mode 100644 index 0000000000000000000000000000000000000000..2e00665bf0f7e3afef08656e3c0a1c52b50bf34d --- /dev/null +++ b/0153-linux-headers-Synchronize-linux-headers-from-linux-v.patch @@ -0,0 +1,57 @@ +From 9a9f7dc1d32aae12505088b1ab7d2986d947ac30 Mon Sep 17 00:00:00 2001 +From: Tianrui Zhao +Date: Fri, 5 Jan 2024 15:57:56 +0800 +Subject: [PATCH 163/293] linux-headers: Synchronize linux headers from linux + v6.7.0-rc8 + +Use the scripts/update-linux-headers.sh to synchronize linux +headers from linux v6.7.0-rc8. We mainly want to add the +loongarch linux headers and then add the loongarch kvm support +based on it. + +Signed-off-by: Tianrui Zhao +Acked-by: Song Gao +Message-Id: <20240105075804.1228596-2-zhaotianrui@loongson.cn> +Signed-off-by: Song Gao +--- + include/standard-headers/linux/fuse.h | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/include/standard-headers/linux/fuse.h b/include/standard-headers/linux/fuse.h +index 6b97938..fc0dcd1 100644 +--- a/include/standard-headers/linux/fuse.h ++++ b/include/standard-headers/linux/fuse.h +@@ -209,7 +209,7 @@ + * - add FUSE_HAS_EXPIRE_ONLY + * + * 7.39 +- * - add FUSE_DIRECT_IO_RELAX ++ * - add FUSE_DIRECT_IO_ALLOW_MMAP + * - add FUSE_STATX and related structures + */ + +@@ -405,8 +405,7 @@ struct fuse_file_lock { + * FUSE_CREATE_SUPP_GROUP: add supplementary group info to create, mkdir, + * symlink and mknod (single group that matches parent) + * FUSE_HAS_EXPIRE_ONLY: kernel supports expiry-only entry invalidation +- * FUSE_DIRECT_IO_RELAX: relax restrictions in FOPEN_DIRECT_IO mode, for now +- * allow shared mmap ++ * FUSE_DIRECT_IO_ALLOW_MMAP: allow shared mmap in FOPEN_DIRECT_IO mode. + */ + #define FUSE_ASYNC_READ (1 << 0) + #define FUSE_POSIX_LOCKS (1 << 1) +@@ -445,7 +444,10 @@ struct fuse_file_lock { + #define FUSE_HAS_INODE_DAX (1ULL << 33) + #define FUSE_CREATE_SUPP_GROUP (1ULL << 34) + #define FUSE_HAS_EXPIRE_ONLY (1ULL << 35) +-#define FUSE_DIRECT_IO_RELAX (1ULL << 36) ++#define FUSE_DIRECT_IO_ALLOW_MMAP (1ULL << 36) ++ ++/* Obsolete alias for FUSE_DIRECT_IO_ALLOW_MMAP */ ++#define FUSE_DIRECT_IO_RELAX FUSE_DIRECT_IO_ALLOW_MMAP + + /** + * CUSE INIT request/reply flags +-- +1.8.3.1 + diff --git a/0154-target-loongarch-Define-some-kvm_arch-interfaces.patch b/0154-target-loongarch-Define-some-kvm_arch-interfaces.patch new file mode 100644 index 0000000000000000000000000000000000000000..b9633230108ddd8885abaa6e2c9c2c710245e7a5 --- /dev/null +++ b/0154-target-loongarch-Define-some-kvm_arch-interfaces.patch @@ -0,0 +1,162 @@ +From 6295d46c364084e3a733d04e24840daa407cbab5 Mon Sep 17 00:00:00 2001 +From: Tianrui Zhao +Date: Fri, 5 Jan 2024 15:57:57 +0800 +Subject: [PATCH 164/293] target/loongarch: Define some kvm_arch interfaces + +Define some functions in target/loongarch/kvm/kvm.c, +such as kvm_arch_put_registers, kvm_arch_get_registers +and kvm_arch_handle_exit, etc. which are needed by +kvm/kvm-all.c. Now the most functions has no content +and they will be implemented in the next patches. + +Signed-off-by: Tianrui Zhao +Signed-off-by: xianglai li +Reviewed-by: Richard Henderson +Reviewed-by: Song Gao +Message-Id: <20240105075804.1228596-3-zhaotianrui@loongson.cn> +Signed-off-by: Song Gao +--- + target/loongarch/kvm/kvm.c | 131 +++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 131 insertions(+) + create mode 100644 target/loongarch/kvm/kvm.c + +diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c +new file mode 100644 +index 0000000..0d67322 +--- /dev/null ++++ b/target/loongarch/kvm/kvm.c +@@ -0,0 +1,131 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * QEMU LoongArch KVM ++ * ++ * Copyright (c) 2023 Loongson Technology Corporation Limited ++ */ ++ ++#include "qemu/osdep.h" ++#include ++#include ++ ++#include "qemu/timer.h" ++#include "qemu/error-report.h" ++#include "qemu/main-loop.h" ++#include "sysemu/sysemu.h" ++#include "sysemu/kvm.h" ++#include "sysemu/kvm_int.h" ++#include "hw/pci/pci.h" ++#include "exec/memattrs.h" ++#include "exec/address-spaces.h" ++#include "hw/boards.h" ++#include "hw/irq.h" ++#include "qemu/log.h" ++#include "hw/loader.h" ++#include "migration/migration.h" ++#include "sysemu/runstate.h" ++#include "cpu-csr.h" ++#include "kvm_loongarch.h" ++ ++static bool cap_has_mp_state; ++const KVMCapabilityInfo kvm_arch_required_capabilities[] = { ++ KVM_CAP_LAST_INFO ++}; ++ ++int kvm_arch_get_registers(CPUState *cs) ++{ ++ return 0; ++} ++int kvm_arch_put_registers(CPUState *cs, int level) ++{ ++ return 0; ++} ++ ++int kvm_arch_init_vcpu(CPUState *cs) ++{ ++ return 0; ++} ++ ++int kvm_arch_destroy_vcpu(CPUState *cs) ++{ ++ return 0; ++} ++ ++unsigned long kvm_arch_vcpu_id(CPUState *cs) ++{ ++ return cs->cpu_index; ++} ++ ++int kvm_arch_release_virq_post(int virq) ++{ ++ return 0; ++} ++ ++int kvm_arch_msi_data_to_gsi(uint32_t data) ++{ ++ abort(); ++} ++ ++int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, ++ uint64_t address, uint32_t data, PCIDevice *dev) ++{ ++ return 0; ++} ++ ++int kvm_arch_add_msi_route_post(struct kvm_irq_routing_entry *route, ++ int vector, PCIDevice *dev) ++{ ++ return 0; ++} ++ ++void kvm_arch_init_irq_routing(KVMState *s) ++{ ++} ++ ++int kvm_arch_get_default_type(MachineState *ms) ++{ ++ return 0; ++} ++ ++int kvm_arch_init(MachineState *ms, KVMState *s) ++{ ++ return 0; ++} ++ ++int kvm_arch_irqchip_create(KVMState *s) ++{ ++ return 0; ++} ++ ++void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) ++{ ++} ++ ++MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run) ++{ ++ return MEMTXATTRS_UNSPECIFIED; ++} ++ ++int kvm_arch_process_async_events(CPUState *cs) ++{ ++ return cs->halted; ++} ++ ++bool kvm_arch_stop_on_emulation_error(CPUState *cs) ++{ ++ return true; ++} ++ ++bool kvm_arch_cpu_check_are_resettable(void) ++{ ++ return true; ++} ++ ++int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) ++{ ++ return 0; ++} ++ ++void kvm_arch_accel_class_init(ObjectClass *oc) ++{ ++} +-- +1.8.3.1 + diff --git a/0155-target-loongarch-Supplement-vcpu-env-initial-when-vc.patch b/0155-target-loongarch-Supplement-vcpu-env-initial-when-vc.patch new file mode 100644 index 0000000000000000000000000000000000000000..23334491a84288be6ec0972d6a8b88166aee0809 --- /dev/null +++ b/0155-target-loongarch-Supplement-vcpu-env-initial-when-vc.patch @@ -0,0 +1,60 @@ +From ecca5952a131a81d0e0453a532c086df9d0df92d Mon Sep 17 00:00:00 2001 +From: Tianrui Zhao +Date: Fri, 5 Jan 2024 15:57:58 +0800 +Subject: [PATCH 165/293] target/loongarch: Supplement vcpu env initial when + vcpu reset + +Supplement vcpu env initial when vcpu reset, including +init vcpu CSR_CPUID,CSR_TID to cpu->cpu_index. The two +regs will be used in kvm_get/set_csr_ioctl. + +Signed-off-by: Tianrui Zhao +Signed-off-by: xianglai li +Reviewed-by: Song Gao +Message-Id: <20240105075804.1228596-4-zhaotianrui@loongson.cn> +Signed-off-by: Song Gao +--- + target/loongarch/cpu.c | 2 ++ + target/loongarch/cpu.h | 2 +- + 2 files changed, 3 insertions(+), 1 deletion(-) + +diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c +index db9a421..0215927 100644 +--- a/target/loongarch/cpu.c ++++ b/target/loongarch/cpu.c +@@ -531,10 +531,12 @@ static void loongarch_cpu_reset_hold(Object *obj) + + env->CSR_ESTAT = env->CSR_ESTAT & (~MAKE_64BIT_MASK(0, 2)); + env->CSR_RVACFG = FIELD_DP64(env->CSR_RVACFG, CSR_RVACFG, RBITS, 0); ++ env->CSR_CPUID = cs->cpu_index; + env->CSR_TCFG = FIELD_DP64(env->CSR_TCFG, CSR_TCFG, EN, 0); + env->CSR_LLBCTL = FIELD_DP64(env->CSR_LLBCTL, CSR_LLBCTL, KLO, 0); + env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 0); + env->CSR_MERRCTL = FIELD_DP64(env->CSR_MERRCTL, CSR_MERRCTL, ISMERR, 0); ++ env->CSR_TID = cs->cpu_index; + + env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, TLB_TYPE, 2); + env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, MTLB_ENTRY, 63); +diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h +index 00d1fba..f6d5ef0 100644 +--- a/target/loongarch/cpu.h ++++ b/target/loongarch/cpu.h +@@ -319,6 +319,7 @@ typedef struct CPUArchState { + uint64_t CSR_PWCH; + uint64_t CSR_STLBPS; + uint64_t CSR_RVACFG; ++ uint64_t CSR_CPUID; + uint64_t CSR_PRCFG1; + uint64_t CSR_PRCFG2; + uint64_t CSR_PRCFG3; +@@ -350,7 +351,6 @@ typedef struct CPUArchState { + uint64_t CSR_DBG; + uint64_t CSR_DERA; + uint64_t CSR_DSAVE; +- uint64_t CSR_CPUID; + + #ifndef CONFIG_USER_ONLY + LoongArchTLB tlb[LOONGARCH_TLB_MAX]; +-- +1.8.3.1 + diff --git a/0156-target-loongarch-Implement-kvm-get-set-registers.patch b/0156-target-loongarch-Implement-kvm-get-set-registers.patch new file mode 100644 index 0000000000000000000000000000000000000000..0880a8d2190433f67e643b4dc7bb8b49d57e90a7 --- /dev/null +++ b/0156-target-loongarch-Implement-kvm-get-set-registers.patch @@ -0,0 +1,724 @@ +From 9de4586ccf82f7630d4c00360af60957e52c857d Mon Sep 17 00:00:00 2001 +From: Tianrui Zhao +Date: Fri, 5 Jan 2024 15:57:59 +0800 +Subject: [PATCH 166/293] target/loongarch: Implement kvm get/set registers + +Implement kvm_arch_get/set_registers interfaces, many regs +can be get/set in the function, such as core regs, csr regs, +fpu regs, mp state, etc. + +Signed-off-by: Tianrui Zhao +Signed-off-by: xianglai li +Reviewed-by: Song Gao +Change-Id: Ia8fc48fe08b1768853f7729e77d37cdf270031e4 +Message-Id: <20240105075804.1228596-5-zhaotianrui@loongson.cn> +Signed-off-by: Song Gao +--- + meson.build | 1 + + target/loongarch/cpu.c | 3 + + target/loongarch/cpu.h | 1 + + target/loongarch/internals.h | 5 +- + target/loongarch/kvm/kvm.c | 580 +++++++++++++++++++++++++++++++++++++++++- + target/loongarch/trace-events | 11 + + target/loongarch/trace.h | 1 + + 7 files changed, 599 insertions(+), 3 deletions(-) + create mode 100644 target/loongarch/trace-events + create mode 100644 target/loongarch/trace.h + +diff --git a/meson.build b/meson.build +index 6c77d96..445f2b7 100644 +--- a/meson.build ++++ b/meson.build +@@ -3358,6 +3358,7 @@ if have_system or have_user + 'target/hppa', + 'target/i386', + 'target/i386/kvm', ++ 'target/loongarch', + 'target/mips/tcg', + 'target/nios2', + 'target/ppc', +diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c +index 0215927..275833e 100644 +--- a/target/loongarch/cpu.c ++++ b/target/loongarch/cpu.c +@@ -553,6 +553,9 @@ static void loongarch_cpu_reset_hold(Object *obj) + #ifndef CONFIG_USER_ONLY + env->pc = 0x1c000000; + memset(env->tlb, 0, sizeof(env->tlb)); ++ if (kvm_enabled()) { ++ kvm_arch_reset_vcpu(env); ++ } + #endif + + restore_fp_status(env); +diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h +index f6d5ef0..f4a89bd 100644 +--- a/target/loongarch/cpu.h ++++ b/target/loongarch/cpu.h +@@ -360,6 +360,7 @@ typedef struct CPUArchState { + MemoryRegion iocsr_mem; + bool load_elf; + uint64_t elf_address; ++ uint32_t mp_state; + /* Store ipistate to access from this struct */ + DeviceState *ipistate; + #endif +diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h +index c492863..0beb034 100644 +--- a/target/loongarch/internals.h ++++ b/target/loongarch/internals.h +@@ -31,8 +31,10 @@ void G_NORETURN do_raise_exception(CPULoongArchState *env, + + const char *loongarch_exception_name(int32_t exception); + ++#ifdef CONFIG_TCG + int ieee_ex_to_loongarch(int xcpt); + void restore_fp_status(CPULoongArchState *env); ++#endif + + #ifndef CONFIG_USER_ONLY + extern const VMStateDescription vmstate_loongarch_cpu; +@@ -44,12 +46,13 @@ uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu); + uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu); + void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu, + uint64_t value); +- ++#ifdef CONFIG_TCG + bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, + MMUAccessType access_type, int mmu_idx, + bool probe, uintptr_t retaddr); + + hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); ++#endif + #endif /* !CONFIG_USER_ONLY */ + + uint64_t read_fcc(CPULoongArchState *env); +diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c +index 0d67322..e7c9ef8 100644 +--- a/target/loongarch/kvm/kvm.c ++++ b/target/loongarch/kvm/kvm.c +@@ -26,19 +26,595 @@ + #include "sysemu/runstate.h" + #include "cpu-csr.h" + #include "kvm_loongarch.h" ++#include "trace.h" + + static bool cap_has_mp_state; + const KVMCapabilityInfo kvm_arch_required_capabilities[] = { + KVM_CAP_LAST_INFO + }; + ++static int kvm_loongarch_get_regs_core(CPUState *cs) ++{ ++ int ret = 0; ++ int i; ++ struct kvm_regs regs; ++ LoongArchCPU *cpu = LOONGARCH_CPU(cs); ++ CPULoongArchState *env = &cpu->env; ++ ++ /* Get the current register set as KVM seems it */ ++ ret = kvm_vcpu_ioctl(cs, KVM_GET_REGS, ®s); ++ if (ret < 0) { ++ trace_kvm_failed_get_regs_core(strerror(errno)); ++ return ret; ++ } ++ /* gpr[0] value is always 0 */ ++ env->gpr[0] = 0; ++ for (i = 1; i < 32; i++) { ++ env->gpr[i] = regs.gpr[i]; ++ } ++ ++ env->pc = regs.pc; ++ return ret; ++} ++ ++static int kvm_loongarch_put_regs_core(CPUState *cs) ++{ ++ int ret = 0; ++ int i; ++ struct kvm_regs regs; ++ LoongArchCPU *cpu = LOONGARCH_CPU(cs); ++ CPULoongArchState *env = &cpu->env; ++ ++ /* Set the registers based on QEMU's view of things */ ++ for (i = 0; i < 32; i++) { ++ regs.gpr[i] = env->gpr[i]; ++ } ++ ++ regs.pc = env->pc; ++ ret = kvm_vcpu_ioctl(cs, KVM_SET_REGS, ®s); ++ if (ret < 0) { ++ trace_kvm_failed_put_regs_core(strerror(errno)); ++ } ++ ++ return ret; ++} ++ ++static int kvm_loongarch_get_csr(CPUState *cs) ++{ ++ int ret = 0; ++ LoongArchCPU *cpu = LOONGARCH_CPU(cs); ++ CPULoongArchState *env = &cpu->env; ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_CRMD), ++ &env->CSR_CRMD); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PRMD), ++ &env->CSR_PRMD); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_EUEN), ++ &env->CSR_EUEN); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_MISC), ++ &env->CSR_MISC); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_ECFG), ++ &env->CSR_ECFG); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_ESTAT), ++ &env->CSR_ESTAT); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_ERA), ++ &env->CSR_ERA); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_BADV), ++ &env->CSR_BADV); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_BADI), ++ &env->CSR_BADI); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_EENTRY), ++ &env->CSR_EENTRY); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBIDX), ++ &env->CSR_TLBIDX); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBEHI), ++ &env->CSR_TLBEHI); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBELO0), ++ &env->CSR_TLBELO0); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBELO1), ++ &env->CSR_TLBELO1); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_ASID), ++ &env->CSR_ASID); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PGDL), ++ &env->CSR_PGDL); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PGDH), ++ &env->CSR_PGDH); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PGD), ++ &env->CSR_PGD); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PWCL), ++ &env->CSR_PWCL); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PWCH), ++ &env->CSR_PWCH); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_STLBPS), ++ &env->CSR_STLBPS); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_RVACFG), ++ &env->CSR_RVACFG); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_CPUID), ++ &env->CSR_CPUID); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PRCFG1), ++ &env->CSR_PRCFG1); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PRCFG2), ++ &env->CSR_PRCFG2); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PRCFG3), ++ &env->CSR_PRCFG3); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(0)), ++ &env->CSR_SAVE[0]); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(1)), ++ &env->CSR_SAVE[1]); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(2)), ++ &env->CSR_SAVE[2]); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(3)), ++ &env->CSR_SAVE[3]); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(4)), ++ &env->CSR_SAVE[4]); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(5)), ++ &env->CSR_SAVE[5]); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(6)), ++ &env->CSR_SAVE[6]); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(7)), ++ &env->CSR_SAVE[7]); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TID), ++ &env->CSR_TID); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_CNTC), ++ &env->CSR_CNTC); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TICLR), ++ &env->CSR_TICLR); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_LLBCTL), ++ &env->CSR_LLBCTL); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_IMPCTL1), ++ &env->CSR_IMPCTL1); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_IMPCTL2), ++ &env->CSR_IMPCTL2); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRENTRY), ++ &env->CSR_TLBRENTRY); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRBADV), ++ &env->CSR_TLBRBADV); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRERA), ++ &env->CSR_TLBRERA); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRSAVE), ++ &env->CSR_TLBRSAVE); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRELO0), ++ &env->CSR_TLBRELO0); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRELO1), ++ &env->CSR_TLBRELO1); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBREHI), ++ &env->CSR_TLBREHI); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRPRMD), ++ &env->CSR_TLBRPRMD); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_DMW(0)), ++ &env->CSR_DMW[0]); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_DMW(1)), ++ &env->CSR_DMW[1]); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_DMW(2)), ++ &env->CSR_DMW[2]); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_DMW(3)), ++ &env->CSR_DMW[3]); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TVAL), ++ &env->CSR_TVAL); ++ ++ ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TCFG), ++ &env->CSR_TCFG); ++ ++ return ret; ++} ++ ++static int kvm_loongarch_put_csr(CPUState *cs) ++{ ++ int ret = 0; ++ LoongArchCPU *cpu = LOONGARCH_CPU(cs); ++ CPULoongArchState *env = &cpu->env; ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_CRMD), ++ &env->CSR_CRMD); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PRMD), ++ &env->CSR_PRMD); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_EUEN), ++ &env->CSR_EUEN); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_MISC), ++ &env->CSR_MISC); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_ECFG), ++ &env->CSR_ECFG); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_ESTAT), ++ &env->CSR_ESTAT); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_ERA), ++ &env->CSR_ERA); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_BADV), ++ &env->CSR_BADV); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_BADI), ++ &env->CSR_BADI); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_EENTRY), ++ &env->CSR_EENTRY); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBIDX), ++ &env->CSR_TLBIDX); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBEHI), ++ &env->CSR_TLBEHI); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBELO0), ++ &env->CSR_TLBELO0); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBELO1), ++ &env->CSR_TLBELO1); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_ASID), ++ &env->CSR_ASID); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PGDL), ++ &env->CSR_PGDL); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PGDH), ++ &env->CSR_PGDH); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PGD), ++ &env->CSR_PGD); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PWCL), ++ &env->CSR_PWCL); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PWCH), ++ &env->CSR_PWCH); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_STLBPS), ++ &env->CSR_STLBPS); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_RVACFG), ++ &env->CSR_RVACFG); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_CPUID), ++ &env->CSR_CPUID); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PRCFG1), ++ &env->CSR_PRCFG1); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PRCFG2), ++ &env->CSR_PRCFG2); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PRCFG3), ++ &env->CSR_PRCFG3); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(0)), ++ &env->CSR_SAVE[0]); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(1)), ++ &env->CSR_SAVE[1]); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(2)), ++ &env->CSR_SAVE[2]); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(3)), ++ &env->CSR_SAVE[3]); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(4)), ++ &env->CSR_SAVE[4]); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(5)), ++ &env->CSR_SAVE[5]); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(6)), ++ &env->CSR_SAVE[6]); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_SAVE(7)), ++ &env->CSR_SAVE[7]); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TID), ++ &env->CSR_TID); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_CNTC), ++ &env->CSR_CNTC); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TICLR), ++ &env->CSR_TICLR); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_LLBCTL), ++ &env->CSR_LLBCTL); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_IMPCTL1), ++ &env->CSR_IMPCTL1); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_IMPCTL2), ++ &env->CSR_IMPCTL2); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRENTRY), ++ &env->CSR_TLBRENTRY); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRBADV), ++ &env->CSR_TLBRBADV); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRERA), ++ &env->CSR_TLBRERA); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRSAVE), ++ &env->CSR_TLBRSAVE); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRELO0), ++ &env->CSR_TLBRELO0); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRELO1), ++ &env->CSR_TLBRELO1); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBREHI), ++ &env->CSR_TLBREHI); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TLBRPRMD), ++ &env->CSR_TLBRPRMD); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_DMW(0)), ++ &env->CSR_DMW[0]); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_DMW(1)), ++ &env->CSR_DMW[1]); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_DMW(2)), ++ &env->CSR_DMW[2]); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_DMW(3)), ++ &env->CSR_DMW[3]); ++ /* ++ * timer cfg must be put at last since it is used to enable ++ * guest timer ++ */ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TVAL), ++ &env->CSR_TVAL); ++ ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TCFG), ++ &env->CSR_TCFG); ++ return ret; ++} ++ ++static int kvm_loongarch_get_regs_fp(CPUState *cs) ++{ ++ int ret, i; ++ struct kvm_fpu fpu; ++ ++ LoongArchCPU *cpu = LOONGARCH_CPU(cs); ++ CPULoongArchState *env = &cpu->env; ++ ++ ret = kvm_vcpu_ioctl(cs, KVM_GET_FPU, &fpu); ++ if (ret < 0) { ++ trace_kvm_failed_get_fpu(strerror(errno)); ++ return ret; ++ } ++ ++ env->fcsr0 = fpu.fcsr; ++ for (i = 0; i < 32; i++) { ++ env->fpr[i].vreg.UD[0] = fpu.fpr[i].val64[0]; ++ } ++ for (i = 0; i < 8; i++) { ++ env->cf[i] = fpu.fcc & 0xFF; ++ fpu.fcc = fpu.fcc >> 8; ++ } ++ ++ return ret; ++} ++ ++static int kvm_loongarch_put_regs_fp(CPUState *cs) ++{ ++ int ret, i; ++ struct kvm_fpu fpu; ++ ++ LoongArchCPU *cpu = LOONGARCH_CPU(cs); ++ CPULoongArchState *env = &cpu->env; ++ ++ fpu.fcsr = env->fcsr0; ++ fpu.fcc = 0; ++ for (i = 0; i < 32; i++) { ++ fpu.fpr[i].val64[0] = env->fpr[i].vreg.UD[0]; ++ } ++ ++ for (i = 0; i < 8; i++) { ++ fpu.fcc |= env->cf[i] << (8 * i); ++ } ++ ++ ret = kvm_vcpu_ioctl(cs, KVM_SET_FPU, &fpu); ++ if (ret < 0) { ++ trace_kvm_failed_put_fpu(strerror(errno)); ++ } ++ ++ return ret; ++} ++ ++void kvm_arch_reset_vcpu(CPULoongArchState *env) ++{ ++ env->mp_state = KVM_MP_STATE_RUNNABLE; ++} ++ ++static int kvm_loongarch_get_mpstate(CPUState *cs) ++{ ++ int ret = 0; ++ struct kvm_mp_state mp_state; ++ LoongArchCPU *cpu = LOONGARCH_CPU(cs); ++ CPULoongArchState *env = &cpu->env; ++ ++ if (cap_has_mp_state) { ++ ret = kvm_vcpu_ioctl(cs, KVM_GET_MP_STATE, &mp_state); ++ if (ret) { ++ trace_kvm_failed_get_mpstate(strerror(errno)); ++ return ret; ++ } ++ env->mp_state = mp_state.mp_state; ++ } ++ ++ return ret; ++} ++ ++static int kvm_loongarch_put_mpstate(CPUState *cs) ++{ ++ int ret = 0; ++ ++ LoongArchCPU *cpu = LOONGARCH_CPU(cs); ++ CPULoongArchState *env = &cpu->env; ++ ++ struct kvm_mp_state mp_state = { ++ .mp_state = env->mp_state ++ }; ++ ++ if (cap_has_mp_state) { ++ ret = kvm_vcpu_ioctl(cs, KVM_SET_MP_STATE, &mp_state); ++ if (ret) { ++ trace_kvm_failed_put_mpstate(strerror(errno)); ++ } ++ } ++ ++ return ret; ++} ++ ++static int kvm_loongarch_get_cpucfg(CPUState *cs) ++{ ++ int i, ret = 0; ++ uint64_t val; ++ LoongArchCPU *cpu = LOONGARCH_CPU(cs); ++ CPULoongArchState *env = &cpu->env; ++ ++ for (i = 0; i < 21; i++) { ++ ret = kvm_get_one_reg(cs, KVM_IOC_CPUCFG(i), &val); ++ if (ret < 0) { ++ trace_kvm_failed_get_cpucfg(strerror(errno)); ++ } ++ env->cpucfg[i] = (uint32_t)val; ++ } ++ return ret; ++} ++ ++static int kvm_loongarch_put_cpucfg(CPUState *cs) ++{ ++ int i, ret = 0; ++ LoongArchCPU *cpu = LOONGARCH_CPU(cs); ++ CPULoongArchState *env = &cpu->env; ++ uint64_t val; ++ ++ for (i = 0; i < 21; i++) { ++ val = env->cpucfg[i]; ++ /* LSX and LASX and LBT are not supported in kvm now */ ++ if (i == 2) { ++ val &= ~(BIT(R_CPUCFG2_LSX_SHIFT) | BIT(R_CPUCFG2_LASX_SHIFT)); ++ val &= ~(BIT(R_CPUCFG2_LBT_X86_SHIFT) | ++ BIT(R_CPUCFG2_LBT_ARM_SHIFT) | ++ BIT(R_CPUCFG2_LBT_MIPS_SHIFT)); ++ } ++ ret = kvm_set_one_reg(cs, KVM_IOC_CPUCFG(i), &val); ++ if (ret < 0) { ++ trace_kvm_failed_put_cpucfg(strerror(errno)); ++ } ++ } ++ return ret; ++} ++ + int kvm_arch_get_registers(CPUState *cs) + { +- return 0; ++ int ret; ++ ++ ret = kvm_loongarch_get_regs_core(cs); ++ if (ret) { ++ return ret; ++ } ++ ++ ret = kvm_loongarch_get_csr(cs); ++ if (ret) { ++ return ret; ++ } ++ ++ ret = kvm_loongarch_get_regs_fp(cs); ++ if (ret) { ++ return ret; ++ } ++ ++ ret = kvm_loongarch_get_mpstate(cs); ++ if (ret) { ++ return ret; ++ } ++ ++ ret = kvm_loongarch_get_cpucfg(cs); ++ return ret; + } ++ + int kvm_arch_put_registers(CPUState *cs, int level) + { +- return 0; ++ int ret; ++ ++ ret = kvm_loongarch_put_regs_core(cs); ++ if (ret) { ++ return ret; ++ } ++ ++ ret = kvm_loongarch_put_csr(cs); ++ if (ret) { ++ return ret; ++ } ++ ++ ret = kvm_loongarch_put_regs_fp(cs); ++ if (ret) { ++ return ret; ++ } ++ ++ ret = kvm_loongarch_put_mpstate(cs); ++ if (ret) { ++ return ret; ++ } ++ ++ ret = kvm_loongarch_put_cpucfg(cs); ++ return ret; + } + + int kvm_arch_init_vcpu(CPUState *cs) +diff --git a/target/loongarch/trace-events b/target/loongarch/trace-events +new file mode 100644 +index 0000000..6827ab5 +--- /dev/null ++++ b/target/loongarch/trace-events +@@ -0,0 +1,11 @@ ++# See docs/devel/tracing.rst for syntax documentation. ++ ++#kvm.c ++kvm_failed_get_regs_core(const char *msg) "Failed to get core regs from KVM: %s" ++kvm_failed_put_regs_core(const char *msg) "Failed to put core regs into KVM: %s" ++kvm_failed_get_fpu(const char *msg) "Failed to get fpu from KVM: %s" ++kvm_failed_put_fpu(const char *msg) "Failed to put fpu into KVM: %s" ++kvm_failed_get_mpstate(const char *msg) "Failed to get mp_state from KVM: %s" ++kvm_failed_put_mpstate(const char *msg) "Failed to put mp_state into KVM: %s" ++kvm_failed_get_cpucfg(const char *msg) "Failed to get cpucfg from KVM: %s" ++kvm_failed_put_cpucfg(const char *msg) "Failed to put cpucfg into KVM: %s" +diff --git a/target/loongarch/trace.h b/target/loongarch/trace.h +new file mode 100644 +index 0000000..c2ecb78 +--- /dev/null ++++ b/target/loongarch/trace.h +@@ -0,0 +1 @@ ++#include "trace/trace-target_loongarch.h" +-- +1.8.3.1 + diff --git a/0157-target-loongarch-Implement-kvm_arch_init-function.patch b/0157-target-loongarch-Implement-kvm_arch_init-function.patch new file mode 100644 index 0000000000000000000000000000000000000000..c50a7c664c236b0bad65b527cce27de1084475db --- /dev/null +++ b/0157-target-loongarch-Implement-kvm_arch_init-function.patch @@ -0,0 +1,33 @@ +From 43df22a11a199d14cf22c0cda1d88613016cc2a2 Mon Sep 17 00:00:00 2001 +From: Tianrui Zhao +Date: Fri, 5 Jan 2024 15:58:00 +0800 +Subject: [PATCH 167/293] target/loongarch: Implement kvm_arch_init function + +Implement the kvm_arch_init of loongarch, in the function, the +KVM_CAP_MP_STATE cap is checked by kvm ioctl. + +Signed-off-by: Tianrui Zhao +Signed-off-by: xianglai li +Reviewed-by: Richard Henderson +Reviewed-by: Song Gao +Message-Id: <20240105075804.1228596-6-zhaotianrui@loongson.cn> +Signed-off-by: Song Gao +--- + target/loongarch/kvm/kvm.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c +index e7c9ef8..29944b9 100644 +--- a/target/loongarch/kvm/kvm.c ++++ b/target/loongarch/kvm/kvm.c +@@ -665,6 +665,7 @@ int kvm_arch_get_default_type(MachineState *ms) + + int kvm_arch_init(MachineState *ms, KVMState *s) + { ++ cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE); + return 0; + } + +-- +1.8.3.1 + diff --git a/0158-target-loongarch-Implement-kvm_arch_init_vcpu.patch b/0158-target-loongarch-Implement-kvm_arch_init_vcpu.patch new file mode 100644 index 0000000000000000000000000000000000000000..53cef48e9049c3463904386aeffc3ada3aff0ff0 --- /dev/null +++ b/0158-target-loongarch-Implement-kvm_arch_init_vcpu.patch @@ -0,0 +1,87 @@ +From 1750164e5521ad2b853be4575c4c410f02db46c6 Mon Sep 17 00:00:00 2001 +From: Tianrui Zhao +Date: Fri, 5 Jan 2024 15:58:01 +0800 +Subject: [PATCH 168/293] target/loongarch: Implement kvm_arch_init_vcpu + +Implement kvm_arch_init_vcpu interface for loongarch, +in this function, we register VM change state handler. +And when VM state changes to running, the counter value +should be put into kvm to keep consistent with kvm, +and when state change to stop, counter value should be +refreshed from kvm. + +Signed-off-by: Tianrui Zhao +Signed-off-by: xianglai li +Reviewed-by: Song Gao +Message-Id: <20240105075804.1228596-7-zhaotianrui@loongson.cn> +Signed-off-by: Song Gao +--- + target/loongarch/cpu.h | 2 ++ + target/loongarch/kvm/kvm.c | 23 +++++++++++++++++++++++ + target/loongarch/trace-events | 2 ++ + 3 files changed, 27 insertions(+) + +diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h +index f4a89bd..8ebd6fa 100644 +--- a/target/loongarch/cpu.h ++++ b/target/loongarch/cpu.h +@@ -381,6 +381,8 @@ struct ArchCPU { + + /* 'compatible' string for this CPU for Linux device trees */ + const char *dtb_compatible; ++ /* used by KVM_REG_LOONGARCH_COUNTER ioctl to access guest time counters */ ++ uint64_t kvm_state_counter; + }; + + /** +diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c +index 29944b9..85e7aeb 100644 +--- a/target/loongarch/kvm/kvm.c ++++ b/target/loongarch/kvm/kvm.c +@@ -617,8 +617,31 @@ int kvm_arch_put_registers(CPUState *cs, int level) + return ret; + } + ++static void kvm_loongarch_vm_stage_change(void *opaque, bool running, ++ RunState state) ++{ ++ int ret; ++ CPUState *cs = opaque; ++ LoongArchCPU *cpu = LOONGARCH_CPU(cs); ++ ++ if (running) { ++ ret = kvm_set_one_reg(cs, KVM_REG_LOONGARCH_COUNTER, ++ &cpu->kvm_state_counter); ++ if (ret < 0) { ++ trace_kvm_failed_put_counter(strerror(errno)); ++ } ++ } else { ++ ret = kvm_get_one_reg(cs, KVM_REG_LOONGARCH_COUNTER, ++ &cpu->kvm_state_counter); ++ if (ret < 0) { ++ trace_kvm_failed_get_counter(strerror(errno)); ++ } ++ } ++} ++ + int kvm_arch_init_vcpu(CPUState *cs) + { ++ qemu_add_vm_change_state_handler(kvm_loongarch_vm_stage_change, cs); + return 0; + } + +diff --git a/target/loongarch/trace-events b/target/loongarch/trace-events +index 6827ab5..937c3c7 100644 +--- a/target/loongarch/trace-events ++++ b/target/loongarch/trace-events +@@ -7,5 +7,7 @@ kvm_failed_get_fpu(const char *msg) "Failed to get fpu from KVM: %s" + kvm_failed_put_fpu(const char *msg) "Failed to put fpu into KVM: %s" + kvm_failed_get_mpstate(const char *msg) "Failed to get mp_state from KVM: %s" + kvm_failed_put_mpstate(const char *msg) "Failed to put mp_state into KVM: %s" ++kvm_failed_get_counter(const char *msg) "Failed to get counter from KVM: %s" ++kvm_failed_put_counter(const char *msg) "Failed to put counter into KVM: %s" + kvm_failed_get_cpucfg(const char *msg) "Failed to get cpucfg from KVM: %s" + kvm_failed_put_cpucfg(const char *msg) "Failed to put cpucfg into KVM: %s" +-- +1.8.3.1 + diff --git a/0159-target-loongarch-Implement-kvm_arch_handle_exit.patch b/0159-target-loongarch-Implement-kvm_arch_handle_exit.patch new file mode 100644 index 0000000000000000000000000000000000000000..2c9b3f9bc65f1eb35c355937d23b8e8dd6f14192 --- /dev/null +++ b/0159-target-loongarch-Implement-kvm_arch_handle_exit.patch @@ -0,0 +1,68 @@ +From 7bad21439762c381162f0846a74427bff3f270d3 Mon Sep 17 00:00:00 2001 +From: Tianrui Zhao +Date: Fri, 5 Jan 2024 15:58:02 +0800 +Subject: [PATCH 169/293] target/loongarch: Implement kvm_arch_handle_exit + +Implement kvm_arch_handle_exit for loongarch. In this +function, the KVM_EXIT_LOONGARCH_IOCSR is handled, +we read or write the iocsr address space by the addr, +length and is_write argument in kvm_run. + +Signed-off-by: Tianrui Zhao +Signed-off-by: xianglai li +Reviewed-by: Richard Henderson +Reviewed-by: Song Gao +Message-Id: <20240105075804.1228596-8-zhaotianrui@loongson.cn> +Signed-off-by: Song Gao +--- + target/loongarch/kvm/kvm.c | 24 +++++++++++++++++++++++- + target/loongarch/trace-events | 1 + + 2 files changed, 24 insertions(+), 1 deletion(-) + +diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c +index 85e7aeb..d2dab3f 100644 +--- a/target/loongarch/kvm/kvm.c ++++ b/target/loongarch/kvm/kvm.c +@@ -723,7 +723,29 @@ bool kvm_arch_cpu_check_are_resettable(void) + + int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) + { +- return 0; ++ int ret = 0; ++ LoongArchCPU *cpu = LOONGARCH_CPU(cs); ++ CPULoongArchState *env = &cpu->env; ++ MemTxAttrs attrs = {}; ++ ++ attrs.requester_id = env_cpu(env)->cpu_index; ++ ++ trace_kvm_arch_handle_exit(run->exit_reason); ++ switch (run->exit_reason) { ++ case KVM_EXIT_LOONGARCH_IOCSR: ++ address_space_rw(&env->address_space_iocsr, ++ run->iocsr_io.phys_addr, ++ attrs, ++ run->iocsr_io.data, ++ run->iocsr_io.len, ++ run->iocsr_io.is_write); ++ break; ++ default: ++ ret = -1; ++ warn_report("KVM: unknown exit reason %d", run->exit_reason); ++ break; ++ } ++ return ret; + } + + void kvm_arch_accel_class_init(ObjectClass *oc) +diff --git a/target/loongarch/trace-events b/target/loongarch/trace-events +index 937c3c7..0218398 100644 +--- a/target/loongarch/trace-events ++++ b/target/loongarch/trace-events +@@ -11,3 +11,4 @@ kvm_failed_get_counter(const char *msg) "Failed to get counter from KVM: %s" + kvm_failed_put_counter(const char *msg) "Failed to put counter into KVM: %s" + kvm_failed_get_cpucfg(const char *msg) "Failed to get cpucfg from KVM: %s" + kvm_failed_put_cpucfg(const char *msg) "Failed to put cpucfg into KVM: %s" ++kvm_arch_handle_exit(int num) "kvm arch handle exit, the reason number: %d" +-- +1.8.3.1 + diff --git a/0160-target-loongarch-Restrict-TCG-specific-code.patch b/0160-target-loongarch-Restrict-TCG-specific-code.patch new file mode 100644 index 0000000000000000000000000000000000000000..1717063b8099181a23fd6e535010327983df4d9b --- /dev/null +++ b/0160-target-loongarch-Restrict-TCG-specific-code.patch @@ -0,0 +1,110 @@ +From f178925f4237a13254d8b605cb2867b4751bd889 Mon Sep 17 00:00:00 2001 +From: Tianrui Zhao +Date: Wed, 10 Jan 2024 10:41:51 +0100 +Subject: [PATCH 170/293] target/loongarch: Restrict TCG-specific code + +In preparation of supporting KVM in the next commit. + +Conflict: +--- + target/loongarch/cpu.c | 30 +++++++++++++++++++++--------- + 1 file changed, 21 insertions(+), 9 deletions(-) + +diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c +index 275833e..60f2636 100644 +--- a/target/loongarch/cpu.c ++++ b/target/loongarch/cpu.c +@@ -11,15 +11,18 @@ + #include "qapi/error.h" + #include "qemu/module.h" + #include "sysemu/qtest.h" +-#include "exec/cpu_ldst.h" ++#include "sysemu/tcg.h" + #include "exec/exec-all.h" + #include "cpu.h" + #include "internals.h" + #include "fpu/softfloat-helpers.h" + #include "cpu-csr.h" + #include "sysemu/reset.h" +-#include "tcg/tcg.h" + #include "vec.h" ++#ifdef CONFIG_TCG ++#include "exec/cpu_ldst.h" ++#include "tcg/tcg.h" ++#endif + + const char * const regnames[32] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", +@@ -108,12 +111,13 @@ void loongarch_cpu_set_irq(void *opaque, int irq, int level) + return; + } + +- env->CSR_ESTAT = deposit64(env->CSR_ESTAT, irq, 1, level != 0); +- +- if (FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS)) { +- cpu_interrupt(cs, CPU_INTERRUPT_HARD); +- } else { +- cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); ++ if (tcg_enabled()) { ++ env->CSR_ESTAT = deposit64(env->CSR_ESTAT, irq, 1, level != 0); ++ if (FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS)) { ++ cpu_interrupt(cs, CPU_INTERRUPT_HARD); ++ } else { ++ cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); ++ } + } + } + +@@ -138,7 +142,10 @@ static inline bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env) + + return (pending & status) != 0; + } ++#endif + ++#ifdef CONFIG_TCG ++#ifndef CONFIG_USER_ONLY + static void loongarch_cpu_do_interrupt(CPUState *cs) + { + LoongArchCPU *cpu = LOONGARCH_CPU(cs); +@@ -320,7 +327,6 @@ static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request) + } + #endif + +-#ifdef CONFIG_TCG + static void loongarch_cpu_synchronize_from_tb(CPUState *cs, + const TranslationBlock *tb) + { +@@ -558,7 +564,9 @@ static void loongarch_cpu_reset_hold(Object *obj) + } + #endif + ++#ifdef CONFIG_TCG + restore_fp_status(env); ++#endif + cs->exception_index = -1; + } + +@@ -701,8 +709,10 @@ static void loongarch_cpu_init(Object *obj) + CPULoongArchState *env = &cpu->env; + + qdev_init_gpio_in(DEVICE(cpu), loongarch_cpu_set_irq, N_IRQS); ++#ifdef CONFIG_TCG + timer_init_ns(&cpu->timer, QEMU_CLOCK_VIRTUAL, + &loongarch_constant_timer_cb, cpu); ++#endif + memory_region_init_io(&env->system_iocsr, OBJECT(cpu), NULL, + env, "iocsr", UINT64_MAX); + address_space_init(&env->address_space_iocsr, &env->system_iocsr, "IOCSR"); +@@ -802,7 +812,9 @@ static struct TCGCPUOps loongarch_tcg_ops = { + #include "hw/core/sysemu-cpu-ops.h" + + static const struct SysemuCPUOps loongarch_sysemu_ops = { ++#ifdef CONFIG_TCG + .get_phys_page_debug = loongarch_cpu_get_phys_page_debug, ++#endif + }; + + static int64_t loongarch_cpu_get_arch_id(CPUState *cs) +-- +1.8.3.1 + diff --git a/0161-target-loongarch-Implement-set-vcpu-intr-for-kvm.patch b/0161-target-loongarch-Implement-set-vcpu-intr-for-kvm.patch new file mode 100644 index 0000000000000000000000000000000000000000..5a58a8e33e6a495db866e582ea3254b041b3a87e --- /dev/null +++ b/0161-target-loongarch-Implement-set-vcpu-intr-for-kvm.patch @@ -0,0 +1,122 @@ +From a616f6dcf7bd84d572da86290fcdf544e2410b41 Mon Sep 17 00:00:00 2001 +From: Tianrui Zhao +Date: Wed, 10 Jan 2024 10:41:52 +0100 +Subject: [PATCH 171/293] target/loongarch: Implement set vcpu intr for kvm +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Implement loongarch kvm set vcpu interrupt interface, +when a irq is set in vcpu, we use the KVM_INTERRUPT +ioctl to set intr into kvm. + +Signed-off-by: Tianrui Zhao +Signed-off-by: xianglai li +Reviewed-by: Song Gao +Message-ID: <20240105075804.1228596-9-zhaotianrui@loongson.cn> +[PMD: Split from bigger patch, part 2] +Signed-off-by: Philippe Mathieu-Daudé +Message-Id: <20240110094152.52138-2-philmd@linaro.org> +Signed-off-by: Song Gao +--- + target/loongarch/cpu.c | 9 ++++++++- + target/loongarch/kvm/kvm.c | 15 +++++++++++++++ + target/loongarch/kvm/kvm_loongarch.h | 16 ++++++++++++++++ + target/loongarch/trace-events | 1 + + 4 files changed, 40 insertions(+), 1 deletion(-) + create mode 100644 target/loongarch/kvm/kvm_loongarch.h + +diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c +index 60f2636..4134143 100644 +--- a/target/loongarch/cpu.c ++++ b/target/loongarch/cpu.c +@@ -12,6 +12,8 @@ + #include "qemu/module.h" + #include "sysemu/qtest.h" + #include "sysemu/tcg.h" ++#include "sysemu/kvm.h" ++#include "kvm/kvm_loongarch.h" + #include "exec/exec-all.h" + #include "cpu.h" + #include "internals.h" +@@ -19,6 +21,9 @@ + #include "cpu-csr.h" + #include "sysemu/reset.h" + #include "vec.h" ++#ifdef CONFIG_KVM ++#include ++#endif + #ifdef CONFIG_TCG + #include "exec/cpu_ldst.h" + #include "tcg/tcg.h" +@@ -111,7 +116,9 @@ void loongarch_cpu_set_irq(void *opaque, int irq, int level) + return; + } + +- if (tcg_enabled()) { ++ if (kvm_enabled()) { ++ kvm_loongarch_set_interrupt(cpu, irq, level); ++ } else if (tcg_enabled()) { + env->CSR_ESTAT = deposit64(env->CSR_ESTAT, irq, 1, level != 0); + if (FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS)) { + cpu_interrupt(cs, CPU_INTERRUPT_HARD); +diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c +index d2dab3f..bd33ec2 100644 +--- a/target/loongarch/kvm/kvm.c ++++ b/target/loongarch/kvm/kvm.c +@@ -748,6 +748,21 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) + return ret; + } + ++int kvm_loongarch_set_interrupt(LoongArchCPU *cpu, int irq, int level) ++{ ++ struct kvm_interrupt intr; ++ CPUState *cs = CPU(cpu); ++ ++ if (level) { ++ intr.irq = irq; ++ } else { ++ intr.irq = -irq; ++ } ++ ++ trace_kvm_set_intr(irq, level); ++ return kvm_vcpu_ioctl(cs, KVM_INTERRUPT, &intr); ++} ++ + void kvm_arch_accel_class_init(ObjectClass *oc) + { + } +diff --git a/target/loongarch/kvm/kvm_loongarch.h b/target/loongarch/kvm/kvm_loongarch.h +new file mode 100644 +index 0000000..d945b6b +--- /dev/null ++++ b/target/loongarch/kvm/kvm_loongarch.h +@@ -0,0 +1,16 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * QEMU LoongArch kvm interface ++ * ++ * Copyright (c) 2023 Loongson Technology Corporation Limited ++ */ ++ ++#include "cpu.h" ++ ++#ifndef QEMU_KVM_LOONGARCH_H ++#define QEMU_KVM_LOONGARCH_H ++ ++int kvm_loongarch_set_interrupt(LoongArchCPU *cpu, int irq, int level); ++void kvm_arch_reset_vcpu(CPULoongArchState *env); ++ ++#endif +diff --git a/target/loongarch/trace-events b/target/loongarch/trace-events +index 0218398..dea11ed 100644 +--- a/target/loongarch/trace-events ++++ b/target/loongarch/trace-events +@@ -12,3 +12,4 @@ kvm_failed_put_counter(const char *msg) "Failed to put counter into KVM: %s" + kvm_failed_get_cpucfg(const char *msg) "Failed to get cpucfg from KVM: %s" + kvm_failed_put_cpucfg(const char *msg) "Failed to put cpucfg into KVM: %s" + kvm_arch_handle_exit(int num) "kvm arch handle exit, the reason number: %d" ++kvm_set_intr(int irq, int level) "kvm set interrupt, irq num: %d, level: %d" +-- +1.8.3.1 + diff --git a/0162-target-loongarch-Add-loongarch-kvm-into-meson-build.patch b/0162-target-loongarch-Add-loongarch-kvm-into-meson-build.patch new file mode 100644 index 0000000000000000000000000000000000000000..feddfd95d259da0deb384d0212b7a51d7bee2c0b --- /dev/null +++ b/0162-target-loongarch-Add-loongarch-kvm-into-meson-build.patch @@ -0,0 +1,56 @@ +From 18985e1df4b75b3cf442c7a503d1092457000f0a Mon Sep 17 00:00:00 2001 +From: Tianrui Zhao +Date: Fri, 5 Jan 2024 15:58:04 +0800 +Subject: [PATCH 172/293] target/loongarch: Add loongarch kvm into meson build + +Add kvm.c into meson.build to compile it when kvm +is configed. Meanwhile in meson.build, we set the +kvm_targets to loongarch64-softmmu when the cpu is +loongarch. And fix the compiling error when config +is enable-kvm,disable-tcg. + +Signed-off-by: Tianrui Zhao +Signed-off-by: xianglai li +Reviewed-by: Richard Henderson +Reviewed-by: Song Gao +Message-Id: <20240105075804.1228596-10-zhaotianrui@loongson.cn> +Signed-off-by: Song Gao +--- + meson.build | 2 ++ + target/loongarch/kvm/meson.build | 1 + + target/loongarch/meson.build | 1 + + 3 files changed, 4 insertions(+) + create mode 100644 target/loongarch/kvm/meson.build + +diff --git a/meson.build b/meson.build +index 445f2b7..0c62b41 100644 +--- a/meson.build ++++ b/meson.build +@@ -114,6 +114,8 @@ elif cpu in ['riscv32'] + kvm_targets = ['riscv32-softmmu'] + elif cpu in ['riscv64'] + kvm_targets = ['riscv64-softmmu'] ++elif cpu in ['loongarch64'] ++ kvm_targets = ['loongarch64-softmmu'] + else + kvm_targets = [] + endif +diff --git a/target/loongarch/kvm/meson.build b/target/loongarch/kvm/meson.build +new file mode 100644 +index 0000000..2266de6 +--- /dev/null ++++ b/target/loongarch/kvm/meson.build +@@ -0,0 +1 @@ ++loongarch_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c')) +diff --git a/target/loongarch/meson.build b/target/loongarch/meson.build +index e84e4c5..db310f6 100644 +--- a/target/loongarch/meson.build ++++ b/target/loongarch/meson.build +@@ -18,3 +18,4 @@ subdir('tcg') + + target_arch += {'loongarch': loongarch_ss} + target_system_arch += {'loongarch': loongarch_system_ss} ++subdir('kvm') +-- +1.8.3.1 + diff --git a/0163-hw-intc-loongarch_ipi-Use-MemTxAttrs-interface-for-i.patch b/0163-hw-intc-loongarch_ipi-Use-MemTxAttrs-interface-for-i.patch new file mode 100644 index 0000000000000000000000000000000000000000..206f576ac1c7493a94d641ae1e25bcd6306636c8 --- /dev/null +++ b/0163-hw-intc-loongarch_ipi-Use-MemTxAttrs-interface-for-i.patch @@ -0,0 +1,287 @@ +From cb6d7101b94bfaed19c2a208990345c854d014e4 Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Wed, 13 Dec 2023 12:12:01 +0800 +Subject: [PATCH 173/293] hw/intc/loongarch_ipi: Use MemTxAttrs interface for + ipi ops + +There are two interface pairs for MemoryRegionOps, read/write and +read_with_attrs/write_with_attrs. The later is better for ipi device +emulation since initial cpu can be parsed from attrs.requester_id. + +And requester_id can be overrided for IOCSR_IPI_SEND and mail_send +function when it is to forward message to another vcpu. + +Signed-off-by: Bibo Mao +Reviewed-by: Song Gao +Message-Id: <20231215100333.3933632-2-maobibo@loongson.cn> +Signed-off-by: Song Gao +--- + hw/intc/loongarch_ipi.c | 136 +++++++++++++++++++++++++++--------------------- + 1 file changed, 77 insertions(+), 59 deletions(-) + +diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c +index 67858b5..221246c 100644 +--- a/hw/intc/loongarch_ipi.c ++++ b/hw/intc/loongarch_ipi.c +@@ -17,14 +17,16 @@ + #include "target/loongarch/internals.h" + #include "trace.h" + +-static void loongarch_ipi_writel(void *, hwaddr, uint64_t, unsigned); +- +-static uint64_t loongarch_ipi_readl(void *opaque, hwaddr addr, unsigned size) ++static MemTxResult loongarch_ipi_readl(void *opaque, hwaddr addr, ++ uint64_t *data, ++ unsigned size, MemTxAttrs attrs) + { +- IPICore *s = opaque; ++ IPICore *s; ++ LoongArchIPI *ipi = opaque; + uint64_t ret = 0; + int index = 0; + ++ s = &ipi->ipi_core; + addr &= 0xff; + switch (addr) { + case CORE_STATUS_OFF: +@@ -49,10 +51,12 @@ static uint64_t loongarch_ipi_readl(void *opaque, hwaddr addr, unsigned size) + } + + trace_loongarch_ipi_read(size, (uint64_t)addr, ret); +- return ret; ++ *data = ret; ++ return MEMTX_OK; + } + +-static void send_ipi_data(CPULoongArchState *env, uint64_t val, hwaddr addr) ++static void send_ipi_data(CPULoongArchState *env, uint64_t val, hwaddr addr, ++ MemTxAttrs attrs) + { + int i, mask = 0, data = 0; + +@@ -62,7 +66,7 @@ static void send_ipi_data(CPULoongArchState *env, uint64_t val, hwaddr addr) + */ + if ((val >> 27) & 0xf) { + data = address_space_ldl(&env->address_space_iocsr, addr, +- MEMTXATTRS_UNSPECIFIED, NULL); ++ attrs, NULL); + for (i = 0; i < 4; i++) { + /* get mask for byte writing */ + if (val & (0x1 << (27 + i))) { +@@ -74,7 +78,7 @@ static void send_ipi_data(CPULoongArchState *env, uint64_t val, hwaddr addr) + data &= mask; + data |= (val >> 32) & ~mask; + address_space_stl(&env->address_space_iocsr, addr, +- data, MEMTXATTRS_UNSPECIFIED, NULL); ++ data, attrs, NULL); + } + + static int archid_cmp(const void *a, const void *b) +@@ -103,80 +107,72 @@ static CPUState *ipi_getcpu(int arch_id) + CPUArchId *archid; + + archid = find_cpu_by_archid(machine, arch_id); +- return CPU(archid->cpu); +-} +- +-static void ipi_send(uint64_t val) +-{ +- uint32_t cpuid; +- uint8_t vector; +- CPUState *cs; +- LoongArchCPU *cpu; +- LoongArchIPI *s; +- +- cpuid = extract32(val, 16, 10); +- if (cpuid >= LOONGARCH_MAX_CPUS) { +- trace_loongarch_ipi_unsupported_cpuid("IOCSR_IPI_SEND", cpuid); +- return; ++ if (archid) { ++ return CPU(archid->cpu); + } + +- /* IPI status vector */ +- vector = extract8(val, 0, 5); +- +- cs = ipi_getcpu(cpuid); +- cpu = LOONGARCH_CPU(cs); +- s = LOONGARCH_IPI(cpu->env.ipistate); +- loongarch_ipi_writel(&s->ipi_core, CORE_SET_OFF, BIT(vector), 4); ++ return NULL; + } + +-static void mail_send(uint64_t val) ++static MemTxResult mail_send(uint64_t val, MemTxAttrs attrs) + { + uint32_t cpuid; + hwaddr addr; +- CPULoongArchState *env; + CPUState *cs; +- LoongArchCPU *cpu; + + cpuid = extract32(val, 16, 10); + if (cpuid >= LOONGARCH_MAX_CPUS) { + trace_loongarch_ipi_unsupported_cpuid("IOCSR_MAIL_SEND", cpuid); +- return; ++ return MEMTX_DECODE_ERROR; + } + +- addr = 0x1020 + (val & 0x1c); + cs = ipi_getcpu(cpuid); +- cpu = LOONGARCH_CPU(cs); +- env = &cpu->env; +- send_ipi_data(env, val, addr); ++ if (cs == NULL) { ++ return MEMTX_DECODE_ERROR; ++ } ++ ++ /* override requester_id */ ++ addr = SMP_IPI_MAILBOX + CORE_BUF_20 + (val & 0x1c); ++ attrs.requester_id = cs->cpu_index; ++ send_ipi_data(&LOONGARCH_CPU(cs)->env, val, addr, attrs); ++ return MEMTX_OK; + } + +-static void any_send(uint64_t val) ++static MemTxResult any_send(uint64_t val, MemTxAttrs attrs) + { + uint32_t cpuid; + hwaddr addr; +- CPULoongArchState *env; + CPUState *cs; +- LoongArchCPU *cpu; + + cpuid = extract32(val, 16, 10); + if (cpuid >= LOONGARCH_MAX_CPUS) { + trace_loongarch_ipi_unsupported_cpuid("IOCSR_ANY_SEND", cpuid); +- return; ++ return MEMTX_DECODE_ERROR; + } + +- addr = val & 0xffff; + cs = ipi_getcpu(cpuid); +- cpu = LOONGARCH_CPU(cs); +- env = &cpu->env; +- send_ipi_data(env, val, addr); ++ if (cs == NULL) { ++ return MEMTX_DECODE_ERROR; ++ } ++ ++ /* override requester_id */ ++ addr = val & 0xffff; ++ attrs.requester_id = cs->cpu_index; ++ send_ipi_data(&LOONGARCH_CPU(cs)->env, val, addr, attrs); ++ return MEMTX_OK; + } + +-static void loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val, +- unsigned size) ++static MemTxResult loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val, ++ unsigned size, MemTxAttrs attrs) + { +- IPICore *s = opaque; ++ LoongArchIPI *ipi = opaque; ++ IPICore *s; + int index = 0; ++ uint32_t cpuid; ++ uint8_t vector; ++ CPUState *cs; + ++ s = &ipi->ipi_core; + addr &= 0xff; + trace_loongarch_ipi_write(size, (uint64_t)addr, val); + switch (addr) { +@@ -203,17 +199,35 @@ static void loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val, + s->buf[index] = val; + break; + case IOCSR_IPI_SEND: +- ipi_send(val); ++ cpuid = extract32(val, 16, 10); ++ if (cpuid >= LOONGARCH_MAX_CPUS) { ++ trace_loongarch_ipi_unsupported_cpuid("IOCSR_IPI_SEND", cpuid); ++ return MEMTX_DECODE_ERROR; ++ } ++ ++ /* IPI status vector */ ++ vector = extract8(val, 0, 5); ++ cs = ipi_getcpu(cpuid); ++ if (cs == NULL) { ++ return MEMTX_DECODE_ERROR; ++ } ++ ++ /* override requester_id */ ++ attrs.requester_id = cs->cpu_index; ++ ipi = LOONGARCH_IPI(LOONGARCH_CPU(cs)->env.ipistate); ++ loongarch_ipi_writel(ipi, CORE_SET_OFF, BIT(vector), 4, attrs); + break; + default: + qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr); + break; + } ++ ++ return MEMTX_OK; + } + + static const MemoryRegionOps loongarch_ipi_ops = { +- .read = loongarch_ipi_readl, +- .write = loongarch_ipi_writel, ++ .read_with_attrs = loongarch_ipi_readl, ++ .write_with_attrs = loongarch_ipi_writel, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 4, +@@ -222,24 +236,28 @@ static const MemoryRegionOps loongarch_ipi_ops = { + }; + + /* mail send and any send only support writeq */ +-static void loongarch_ipi_writeq(void *opaque, hwaddr addr, uint64_t val, +- unsigned size) ++static MemTxResult loongarch_ipi_writeq(void *opaque, hwaddr addr, uint64_t val, ++ unsigned size, MemTxAttrs attrs) + { ++ MemTxResult ret = MEMTX_OK; ++ + addr &= 0xfff; + switch (addr) { + case MAIL_SEND_OFFSET: +- mail_send(val); ++ ret = mail_send(val, attrs); + break; + case ANY_SEND_OFFSET: +- any_send(val); ++ ret = any_send(val, attrs); + break; + default: + break; + } ++ ++ return ret; + } + + static const MemoryRegionOps loongarch_ipi64_ops = { +- .write = loongarch_ipi_writeq, ++ .write_with_attrs = loongarch_ipi_writeq, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .valid.min_access_size = 8, +@@ -253,7 +271,7 @@ static void loongarch_ipi_init(Object *obj) + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->ipi_iocsr_mem, obj, &loongarch_ipi_ops, +- &s->ipi_core, "loongarch_ipi_iocsr", 0x48); ++ s, "loongarch_ipi_iocsr", 0x48); + + /* loongarch_ipi_iocsr performs re-entrant IO through ipi_send */ + s->ipi_iocsr_mem.disable_reentrancy_guard = true; +@@ -261,7 +279,7 @@ static void loongarch_ipi_init(Object *obj) + sysbus_init_mmio(sbd, &s->ipi_iocsr_mem); + + memory_region_init_io(&s->ipi64_iocsr_mem, obj, &loongarch_ipi64_ops, +- &s->ipi_core, "loongarch_ipi64_iocsr", 0x118); ++ s, "loongarch_ipi64_iocsr", 0x118); + sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem); + qdev_init_gpio_out(DEVICE(obj), &s->ipi_core.irq, 1); + } +-- +1.8.3.1 + diff --git a/0164-hw-loongarch-virt-Set-iocsr-address-space-per-board-.patch b/0164-hw-loongarch-virt-Set-iocsr-address-space-per-board-.patch new file mode 100644 index 0000000000000000000000000000000000000000..611fedf7649d56615f271c5e04f877de17e8fba6 --- /dev/null +++ b/0164-hw-loongarch-virt-Set-iocsr-address-space-per-board-.patch @@ -0,0 +1,558 @@ +From 8dcb2d7c9fd5691a337c1a1c59d3d8d68bd1922c Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Wed, 13 Dec 2023 12:13:14 +0800 +Subject: [PATCH 174/293] hw/loongarch/virt: Set iocsr address space per-board + rather than percpu + +LoongArch system has iocsr address space, most iocsr registers are +per-board, however some iocsr register spaces banked for percpu such +as ipi mailbox and extioi interrupt status. For banked iocsr space, +each cpu has the same iocsr space, but separate data. + +This patch changes iocsr address space per-board rather percpu, +for iocsr registers specified for cpu, MemTxAttrs.requester_id +can be parsed for the cpu. With this patches, the total address space +on board will be simple, only iocsr address space and system memory, +rather than the number of cpu and system memory. + +confict: + ++<<<<<<< HEAD + + .version_id = 1, + + .minimum_version_id = 1, + + .fields = (VMStateField[]) { + + VMSTATE_STRUCT(ipi_core, LoongArchIPI, 0, vmstate_ipi_core, IPICore), +++======= ++ .version_id = 2, ++ .minimum_version_id = 2, ++ .fields = (const VMStateField[]) { ++ VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongArchIPI, num_cpu, ++ vmstate_ipi_core, IPICore), +++>>>>>>> hw/loongarch/virt: Set iocsr address space per-board rather than percpu + +solve: +save: hw/loongarch/virt: Set iocsr address space per-board rather than percpu + +Signed-off-by: Bibo Mao +Reviewed-by: Song Gao +Message-Id: <20231215100333.3933632-3-maobibo@loongson.cn> +Signed-off-by: Song Gao +--- + hw/intc/loongarch_extioi.c | 3 -- + hw/intc/loongarch_ipi.c | 63 ++++++++++++++++++------- + hw/loongarch/virt.c | 91 +++++++++++++++++++++++++++---------- + include/hw/intc/loongarch_extioi.h | 1 - + include/hw/intc/loongarch_ipi.h | 3 +- + include/hw/loongarch/virt.h | 3 ++ + target/loongarch/cpu.c | 48 ------------------- + target/loongarch/cpu.h | 4 +- + target/loongarch/kvm/kvm.c | 2 +- + target/loongarch/tcg/iocsr_helper.c | 16 +++---- + 10 files changed, 129 insertions(+), 105 deletions(-) + +diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c +index 24fb3af..77b4776 100644 +--- a/hw/intc/loongarch_extioi.c ++++ b/hw/intc/loongarch_extioi.c +@@ -282,9 +282,6 @@ static void loongarch_extioi_instance_init(Object *obj) + qdev_init_gpio_in(DEVICE(obj), extioi_setirq, EXTIOI_IRQS); + + for (cpu = 0; cpu < EXTIOI_CPUS; cpu++) { +- memory_region_init_io(&s->extioi_iocsr_mem[cpu], OBJECT(s), &extioi_ops, +- s, "extioi_iocsr", 0x900); +- sysbus_init_mmio(dev, &s->extioi_iocsr_mem[cpu]); + for (pin = 0; pin < LS3A_INTC_IP; pin++) { + qdev_init_gpio_out(DEVICE(obj), &s->parent_irq[cpu][pin], 1); + } +diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c +index 221246c..e228669 100644 +--- a/hw/intc/loongarch_ipi.c ++++ b/hw/intc/loongarch_ipi.c +@@ -9,6 +9,7 @@ + #include "hw/sysbus.h" + #include "hw/intc/loongarch_ipi.h" + #include "hw/irq.h" ++#include "hw/qdev-properties.h" + #include "qapi/error.h" + #include "qemu/log.h" + #include "exec/address-spaces.h" +@@ -26,7 +27,7 @@ static MemTxResult loongarch_ipi_readl(void *opaque, hwaddr addr, + uint64_t ret = 0; + int index = 0; + +- s = &ipi->ipi_core; ++ s = &ipi->cpu[attrs.requester_id]; + addr &= 0xff; + switch (addr) { + case CORE_STATUS_OFF: +@@ -65,7 +66,7 @@ static void send_ipi_data(CPULoongArchState *env, uint64_t val, hwaddr addr, + * if the mask is 0, we need not to do anything. + */ + if ((val >> 27) & 0xf) { +- data = address_space_ldl(&env->address_space_iocsr, addr, ++ data = address_space_ldl(env->address_space_iocsr, addr, + attrs, NULL); + for (i = 0; i < 4; i++) { + /* get mask for byte writing */ +@@ -77,7 +78,7 @@ static void send_ipi_data(CPULoongArchState *env, uint64_t val, hwaddr addr, + + data &= mask; + data |= (val >> 32) & ~mask; +- address_space_stl(&env->address_space_iocsr, addr, ++ address_space_stl(env->address_space_iocsr, addr, + data, attrs, NULL); + } + +@@ -172,7 +173,7 @@ static MemTxResult loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val, + uint8_t vector; + CPUState *cs; + +- s = &ipi->ipi_core; ++ s = &ipi->cpu[attrs.requester_id]; + addr &= 0xff; + trace_loongarch_ipi_write(size, (uint64_t)addr, val); + switch (addr) { +@@ -214,7 +215,6 @@ static MemTxResult loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val, + + /* override requester_id */ + attrs.requester_id = cs->cpu_index; +- ipi = LOONGARCH_IPI(LOONGARCH_CPU(cs)->env.ipistate); + loongarch_ipi_writel(ipi, CORE_SET_OFF, BIT(vector), 4, attrs); + break; + default: +@@ -265,12 +265,18 @@ static const MemoryRegionOps loongarch_ipi64_ops = { + .endianness = DEVICE_LITTLE_ENDIAN, + }; + +-static void loongarch_ipi_init(Object *obj) ++static void loongarch_ipi_realize(DeviceState *dev, Error **errp) + { +- LoongArchIPI *s = LOONGARCH_IPI(obj); +- SysBusDevice *sbd = SYS_BUS_DEVICE(obj); ++ LoongArchIPI *s = LOONGARCH_IPI(dev); ++ SysBusDevice *sbd = SYS_BUS_DEVICE(dev); ++ int i; ++ ++ if (s->num_cpu == 0) { ++ error_setg(errp, "num-cpu must be at least 1"); ++ return; ++ } + +- memory_region_init_io(&s->ipi_iocsr_mem, obj, &loongarch_ipi_ops, ++ memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev), &loongarch_ipi_ops, + s, "loongarch_ipi_iocsr", 0x48); + + /* loongarch_ipi_iocsr performs re-entrant IO through ipi_send */ +@@ -278,10 +284,20 @@ static void loongarch_ipi_init(Object *obj) + + sysbus_init_mmio(sbd, &s->ipi_iocsr_mem); + +- memory_region_init_io(&s->ipi64_iocsr_mem, obj, &loongarch_ipi64_ops, ++ memory_region_init_io(&s->ipi64_iocsr_mem, OBJECT(dev), ++ &loongarch_ipi64_ops, + s, "loongarch_ipi64_iocsr", 0x118); + sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem); +- qdev_init_gpio_out(DEVICE(obj), &s->ipi_core.irq, 1); ++ ++ s->cpu = g_new0(IPICore, s->num_cpu); ++ if (s->cpu == NULL) { ++ error_setg(errp, "Memory allocation for ExtIOICore faile"); ++ return; ++ } ++ ++ for (i = 0; i < s->num_cpu; i++) { ++ qdev_init_gpio_out(dev, &s->cpu[i].irq, 1); ++ } + } + + static const VMStateDescription vmstate_ipi_core = { +@@ -300,27 +316,42 @@ static const VMStateDescription vmstate_ipi_core = { + + static const VMStateDescription vmstate_loongarch_ipi = { + .name = TYPE_LOONGARCH_IPI, +- .version_id = 1, +- .minimum_version_id = 1, +- .fields = (VMStateField[]) { +- VMSTATE_STRUCT(ipi_core, LoongArchIPI, 0, vmstate_ipi_core, IPICore), ++ .version_id = 2, ++ .minimum_version_id = 2, ++ .fields = (const VMStateField[]) { ++ VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongArchIPI, num_cpu, ++ vmstate_ipi_core, IPICore), + VMSTATE_END_OF_LIST() + } + }; + ++static Property ipi_properties[] = { ++ DEFINE_PROP_UINT32("num-cpu", LoongArchIPI, num_cpu, 1), ++ DEFINE_PROP_END_OF_LIST(), ++}; ++ + static void loongarch_ipi_class_init(ObjectClass *klass, void *data) + { + DeviceClass *dc = DEVICE_CLASS(klass); + ++ dc->realize = loongarch_ipi_realize; ++ device_class_set_props(dc, ipi_properties); + dc->vmsd = &vmstate_loongarch_ipi; + } + ++static void loongarch_ipi_finalize(Object *obj) ++{ ++ LoongArchIPI *s = LOONGARCH_IPI(obj); ++ ++ g_free(s->cpu); ++} ++ + static const TypeInfo loongarch_ipi_info = { + .name = TYPE_LOONGARCH_IPI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(LoongArchIPI), +- .instance_init = loongarch_ipi_init, + .class_init = loongarch_ipi_class_init, ++ .instance_finalize = loongarch_ipi_finalize, + }; + + static void loongarch_ipi_register_types(void) +diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c +index 4b7dc67..13d19b6 100644 +--- a/hw/loongarch/virt.c ++++ b/hw/loongarch/virt.c +@@ -535,9 +535,6 @@ static void loongarch_irq_init(LoongArchMachineState *lams) + CPUState *cpu_state; + int cpu, pin, i, start, num; + +- extioi = qdev_new(TYPE_LOONGARCH_EXTIOI); +- sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal); +- + /* + * The connection of interrupts: + * +-----+ +---------+ +-------+ +@@ -559,36 +556,36 @@ static void loongarch_irq_init(LoongArchMachineState *lams) + * | UARTs | | Devices | | Devices | + * +--------+ +---------+ +---------+ + */ ++ ++ /* Create IPI device */ ++ ipi = qdev_new(TYPE_LOONGARCH_IPI); ++ qdev_prop_set_uint32(ipi, "num-cpu", ms->smp.cpus); ++ sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal); ++ ++ /* IPI iocsr memory region */ ++ memory_region_add_subregion(&lams->system_iocsr, SMP_IPI_MAILBOX, ++ sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 0)); ++ memory_region_add_subregion(&lams->system_iocsr, MAIL_SEND_ADDR, ++ sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 1)); ++ + for (cpu = 0; cpu < ms->smp.cpus; cpu++) { + cpu_state = qemu_get_cpu(cpu); + cpudev = DEVICE(cpu_state); + lacpu = LOONGARCH_CPU(cpu_state); + env = &(lacpu->env); +- +- ipi = qdev_new(TYPE_LOONGARCH_IPI); +- sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal); ++ env->address_space_iocsr = &lams->as_iocsr; + + /* connect ipi irq to cpu irq */ +- qdev_connect_gpio_out(ipi, 0, qdev_get_gpio_in(cpudev, IRQ_IPI)); +- /* IPI iocsr memory region */ +- memory_region_add_subregion(&env->system_iocsr, SMP_IPI_MAILBOX, +- sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), +- 0)); +- memory_region_add_subregion(&env->system_iocsr, MAIL_SEND_ADDR, +- sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), +- 1)); +- /* +- * extioi iocsr memory region +- * only one extioi is added on loongarch virt machine +- * external device interrupt can only be routed to cpu 0-3 +- */ +- if (cpu < EXTIOI_CPUS) +- memory_region_add_subregion(&env->system_iocsr, APIC_BASE, +- sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), +- cpu)); ++ qdev_connect_gpio_out(ipi, cpu, qdev_get_gpio_in(cpudev, IRQ_IPI)); + env->ipistate = ipi; + } + ++ /* Create EXTIOI device */ ++ extioi = qdev_new(TYPE_LOONGARCH_EXTIOI); ++ sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal); ++ memory_region_add_subregion(&lams->system_iocsr, APIC_BASE, ++ sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 0)); ++ + /* + * connect ext irq to the cpu irq + * cpu_pin[9:2] <= intc_pin[7:0] +@@ -733,6 +730,43 @@ static void loongarch_direct_kernel_boot(LoongArchMachineState *lams, + } + } + ++static void loongarch_qemu_write(void *opaque, hwaddr addr, ++ uint64_t val, unsigned size) ++{ ++} ++ ++static uint64_t loongarch_qemu_read(void *opaque, hwaddr addr, unsigned size) ++{ ++ switch (addr) { ++ case VERSION_REG: ++ return 0x11ULL; ++ case FEATURE_REG: ++ return 1ULL << IOCSRF_MSI | 1ULL << IOCSRF_EXTIOI | ++ 1ULL << IOCSRF_CSRIPI; ++ case VENDOR_REG: ++ return 0x6e6f73676e6f6f4cULL; /* "Loongson" */ ++ case CPUNAME_REG: ++ return 0x303030354133ULL; /* "3A5000" */ ++ case MISC_FUNC_REG: ++ return 1ULL << IOCSRM_EXTIOI_EN; ++ } ++ return 0ULL; ++} ++ ++static const MemoryRegionOps loongarch_qemu_ops = { ++ .read = loongarch_qemu_read, ++ .write = loongarch_qemu_write, ++ .endianness = DEVICE_LITTLE_ENDIAN, ++ .valid = { ++ .min_access_size = 4, ++ .max_access_size = 8, ++ }, ++ .impl = { ++ .min_access_size = 8, ++ .max_access_size = 8, ++ }, ++}; ++ + static void loongarch_init(MachineState *machine) + { + LoongArchCPU *lacpu; +@@ -761,8 +795,17 @@ static void loongarch_init(MachineState *machine) + exit(1); + } + create_fdt(lams); +- /* Init CPUs */ + ++ /* Create IOCSR space */ ++ memory_region_init_io(&lams->system_iocsr, OBJECT(machine), NULL, ++ machine, "iocsr", UINT64_MAX); ++ address_space_init(&lams->as_iocsr, &lams->system_iocsr, "IOCSR"); ++ memory_region_init_io(&lams->iocsr_mem, OBJECT(machine), ++ &loongarch_qemu_ops, ++ machine, "iocsr_misc", 0x428); ++ memory_region_add_subregion(&lams->system_iocsr, 0, &lams->iocsr_mem); ++ ++ /* Init CPUs */ + possible_cpus = mc->possible_cpu_arch_ids(machine); + for (i = 0; i < possible_cpus->len; i++) { + cpu = cpu_create(machine->cpu_type); +diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h +index fbdef9a..110e5e8 100644 +--- a/include/hw/intc/loongarch_extioi.h ++++ b/include/hw/intc/loongarch_extioi.h +@@ -58,7 +58,6 @@ struct LoongArchExtIOI { + uint8_t sw_coremap[EXTIOI_IRQS]; + qemu_irq parent_irq[EXTIOI_CPUS][LS3A_INTC_IP]; + qemu_irq irq[EXTIOI_IRQS]; +- MemoryRegion extioi_iocsr_mem[EXTIOI_CPUS]; + MemoryRegion extioi_system_mem; + }; + #endif /* LOONGARCH_EXTIOI_H */ +diff --git a/include/hw/intc/loongarch_ipi.h b/include/hw/intc/loongarch_ipi.h +index 6c61947..1c1e834 100644 +--- a/include/hw/intc/loongarch_ipi.h ++++ b/include/hw/intc/loongarch_ipi.h +@@ -47,7 +47,8 @@ struct LoongArchIPI { + SysBusDevice parent_obj; + MemoryRegion ipi_iocsr_mem; + MemoryRegion ipi64_iocsr_mem; +- IPICore ipi_core; ++ uint32_t num_cpu; ++ IPICore *cpu; + }; + + #endif +diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h +index db0831b..6ef9a92 100644 +--- a/include/hw/loongarch/virt.h ++++ b/include/hw/loongarch/virt.h +@@ -50,6 +50,9 @@ struct LoongArchMachineState { + DeviceState *platform_bus_dev; + PCIBus *pci_bus; + PFlashCFI01 *flash; ++ MemoryRegion system_iocsr; ++ MemoryRegion iocsr_mem; ++ AddressSpace as_iocsr; + }; + + #define TYPE_LOONGARCH_MACHINE MACHINE_TYPE_NAME("virt") +diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c +index 4134143..6611d13 100644 +--- a/target/loongarch/cpu.c ++++ b/target/loongarch/cpu.c +@@ -602,47 +602,6 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) + lacc->parent_realize(dev, errp); + } + +-#ifndef CONFIG_USER_ONLY +-static void loongarch_qemu_write(void *opaque, hwaddr addr, +- uint64_t val, unsigned size) +-{ +- qemu_log_mask(LOG_UNIMP, "[%s]: Unimplemented reg 0x%" HWADDR_PRIx "\n", +- __func__, addr); +-} +- +-static uint64_t loongarch_qemu_read(void *opaque, hwaddr addr, unsigned size) +-{ +- switch (addr) { +- case VERSION_REG: +- return 0x11ULL; +- case FEATURE_REG: +- return 1ULL << IOCSRF_MSI | 1ULL << IOCSRF_EXTIOI | +- 1ULL << IOCSRF_CSRIPI; +- case VENDOR_REG: +- return 0x6e6f73676e6f6f4cULL; /* "Loongson" */ +- case CPUNAME_REG: +- return 0x303030354133ULL; /* "3A5000" */ +- case MISC_FUNC_REG: +- return 1ULL << IOCSRM_EXTIOI_EN; +- } +- return 0ULL; +-} +- +-static const MemoryRegionOps loongarch_qemu_ops = { +- .read = loongarch_qemu_read, +- .write = loongarch_qemu_write, +- .endianness = DEVICE_LITTLE_ENDIAN, +- .valid = { +- .min_access_size = 4, +- .max_access_size = 8, +- }, +- .impl = { +- .min_access_size = 8, +- .max_access_size = 8, +- }, +-}; +-#endif +- + static bool loongarch_get_lsx(Object *obj, Error **errp) + { + LoongArchCPU *cpu = LOONGARCH_CPU(obj); +@@ -713,19 +672,12 @@ static void loongarch_cpu_init(Object *obj) + { + #ifndef CONFIG_USER_ONLY + LoongArchCPU *cpu = LOONGARCH_CPU(obj); +- CPULoongArchState *env = &cpu->env; + + qdev_init_gpio_in(DEVICE(cpu), loongarch_cpu_set_irq, N_IRQS); + #ifdef CONFIG_TCG + timer_init_ns(&cpu->timer, QEMU_CLOCK_VIRTUAL, + &loongarch_constant_timer_cb, cpu); + #endif +- memory_region_init_io(&env->system_iocsr, OBJECT(cpu), NULL, +- env, "iocsr", UINT64_MAX); +- address_space_init(&env->address_space_iocsr, &env->system_iocsr, "IOCSR"); +- memory_region_init_io(&env->iocsr_mem, OBJECT(cpu), &loongarch_qemu_ops, +- NULL, "iocsr_misc", 0x428); +- memory_region_add_subregion(&env->system_iocsr, 0, &env->iocsr_mem); + #endif + } + +diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h +index 8ebd6fa..4aba8ab 100644 +--- a/target/loongarch/cpu.h ++++ b/target/loongarch/cpu.h +@@ -355,9 +355,7 @@ typedef struct CPUArchState { + #ifndef CONFIG_USER_ONLY + LoongArchTLB tlb[LOONGARCH_TLB_MAX]; + +- AddressSpace address_space_iocsr; +- MemoryRegion system_iocsr; +- MemoryRegion iocsr_mem; ++ AddressSpace *address_space_iocsr; + bool load_elf; + uint64_t elf_address; + uint32_t mp_state; +diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c +index bd33ec2..84bcdf5 100644 +--- a/target/loongarch/kvm/kvm.c ++++ b/target/loongarch/kvm/kvm.c +@@ -733,7 +733,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) + trace_kvm_arch_handle_exit(run->exit_reason); + switch (run->exit_reason) { + case KVM_EXIT_LOONGARCH_IOCSR: +- address_space_rw(&env->address_space_iocsr, ++ address_space_rw(env->address_space_iocsr, + run->iocsr_io.phys_addr, + attrs, + run->iocsr_io.data, +diff --git a/target/loongarch/tcg/iocsr_helper.c b/target/loongarch/tcg/iocsr_helper.c +index 6cd01d5..b6916f5 100644 +--- a/target/loongarch/tcg/iocsr_helper.c ++++ b/target/loongarch/tcg/iocsr_helper.c +@@ -17,52 +17,52 @@ + + uint64_t helper_iocsrrd_b(CPULoongArchState *env, target_ulong r_addr) + { +- return address_space_ldub(&env->address_space_iocsr, r_addr, ++ return address_space_ldub(env->address_space_iocsr, r_addr, + GET_MEMTXATTRS(env), NULL); + } + + uint64_t helper_iocsrrd_h(CPULoongArchState *env, target_ulong r_addr) + { +- return address_space_lduw(&env->address_space_iocsr, r_addr, ++ return address_space_lduw(env->address_space_iocsr, r_addr, + GET_MEMTXATTRS(env), NULL); + } + + uint64_t helper_iocsrrd_w(CPULoongArchState *env, target_ulong r_addr) + { +- return address_space_ldl(&env->address_space_iocsr, r_addr, ++ return address_space_ldl(env->address_space_iocsr, r_addr, + GET_MEMTXATTRS(env), NULL); + } + + uint64_t helper_iocsrrd_d(CPULoongArchState *env, target_ulong r_addr) + { +- return address_space_ldq(&env->address_space_iocsr, r_addr, ++ return address_space_ldq(env->address_space_iocsr, r_addr, + GET_MEMTXATTRS(env), NULL); + } + + void helper_iocsrwr_b(CPULoongArchState *env, target_ulong w_addr, + target_ulong val) + { +- address_space_stb(&env->address_space_iocsr, w_addr, ++ address_space_stb(env->address_space_iocsr, w_addr, + val, GET_MEMTXATTRS(env), NULL); + } + + void helper_iocsrwr_h(CPULoongArchState *env, target_ulong w_addr, + target_ulong val) + { +- address_space_stw(&env->address_space_iocsr, w_addr, ++ address_space_stw(env->address_space_iocsr, w_addr, + val, GET_MEMTXATTRS(env), NULL); + } + + void helper_iocsrwr_w(CPULoongArchState *env, target_ulong w_addr, + target_ulong val) + { +- address_space_stl(&env->address_space_iocsr, w_addr, ++ address_space_stl(env->address_space_iocsr, w_addr, + val, GET_MEMTXATTRS(env), NULL); + } + + void helper_iocsrwr_d(CPULoongArchState *env, target_ulong w_addr, + target_ulong val) + { +- address_space_stq(&env->address_space_iocsr, w_addr, ++ address_space_stq(env->address_space_iocsr, w_addr, + val, GET_MEMTXATTRS(env), NULL); + } +-- +1.8.3.1 + diff --git a/0165-hw-intc-loongarch_extioi-Add-dynamic-cpu-number-supp.patch b/0165-hw-intc-loongarch_extioi-Add-dynamic-cpu-number-supp.patch new file mode 100644 index 0000000000000000000000000000000000000000..dc9c8cbcc210f9387180c772e66e7daec14985de --- /dev/null +++ b/0165-hw-intc-loongarch_extioi-Add-dynamic-cpu-number-supp.patch @@ -0,0 +1,298 @@ +From 1371453256d44a02fe4c6fe9c42724e85e56fe52 Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Fri, 15 Dec 2023 11:07:36 +0800 +Subject: [PATCH 175/293] hw/intc/loongarch_extioi: Add dynamic cpu number + support + +On LoongArch physical machine, one extioi interrupt controller only +supports 4 cpus. With processor more than 4 cpus, there are multiple +extioi interrupt controllers; if interrupts need to be routed to +other cpus, they are forwarded from extioi node0 to other extioi nodes. + +On virt machine model, there is simple extioi interrupt device model. +All cpus can access register of extioi interrupt controller, however +interrupt can only be route to 4 vcpu for compatible with old kernel. + +This patch adds dynamic cpu number support about extioi interrupt. +With old kernel legacy extioi model is used, however kernel can detect +and choose new route method in future, so that interrupt can be routed to +all vcpus. + +confict: + +++<<<<<<< HEAD + + .fields = (VMStateField[]) { +++======= ++ .fields = (const VMStateField[]) { ++ VMSTATE_UINT32_ARRAY(coreisr, ExtIOICore, EXTIOI_IRQS_GROUP_COUNT), ++ VMSTATE_END_OF_LIST() ++ } ++ }; ++ ++ static const VMStateDescription vmstate_loongarch_extioi = { ++ .name = TYPE_LOONGARCH_EXTIOI, ++ .version_id = 2, ++ .minimum_version_id = 2, ++ .fields = (const VMStateField[]) { +++>>>>>>> hw/intc/loongarch_extioi: Add dynamic cpu number support + +solve: + +save: hw/intc/loongarch_extioi: Add dynamic cpu number support + +Signed-off-by: Bibo Mao +Reviewed-by: Song Gao +Message-Id: <20231215100333.3933632-4-maobibo@loongson.cn> +Signed-off-by: Song Gao +--- + hw/intc/loongarch_extioi.c | 109 ++++++++++++++++++++++++------------- + hw/loongarch/virt.c | 3 +- + include/hw/intc/loongarch_extioi.h | 11 +++- + 3 files changed, 82 insertions(+), 41 deletions(-) + +diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c +index 77b4776..28802bf 100644 +--- a/hw/intc/loongarch_extioi.c ++++ b/hw/intc/loongarch_extioi.c +@@ -8,6 +8,7 @@ + #include "qemu/osdep.h" + #include "qemu/module.h" + #include "qemu/log.h" ++#include "qapi/error.h" + #include "hw/irq.h" + #include "hw/sysbus.h" + #include "hw/loongarch/virt.h" +@@ -32,23 +33,23 @@ static void extioi_update_irq(LoongArchExtIOI *s, int irq, int level) + if (((s->enable[irq_index]) & irq_mask) == 0) { + return; + } +- s->coreisr[cpu][irq_index] |= irq_mask; +- found = find_first_bit(s->sw_isr[cpu][ipnum], EXTIOI_IRQS); +- set_bit(irq, s->sw_isr[cpu][ipnum]); ++ s->cpu[cpu].coreisr[irq_index] |= irq_mask; ++ found = find_first_bit(s->cpu[cpu].sw_isr[ipnum], EXTIOI_IRQS); ++ set_bit(irq, s->cpu[cpu].sw_isr[ipnum]); + if (found < EXTIOI_IRQS) { + /* other irq is handling, need not update parent irq level */ + return; + } + } else { +- s->coreisr[cpu][irq_index] &= ~irq_mask; +- clear_bit(irq, s->sw_isr[cpu][ipnum]); +- found = find_first_bit(s->sw_isr[cpu][ipnum], EXTIOI_IRQS); ++ s->cpu[cpu].coreisr[irq_index] &= ~irq_mask; ++ clear_bit(irq, s->cpu[cpu].sw_isr[ipnum]); ++ found = find_first_bit(s->cpu[cpu].sw_isr[ipnum], EXTIOI_IRQS); + if (found < EXTIOI_IRQS) { + /* other irq is handling, need not update parent irq level */ + return; + } + } +- qemu_set_irq(s->parent_irq[cpu][ipnum], level); ++ qemu_set_irq(s->cpu[cpu].parent_irq[ipnum], level); + } + + static void extioi_setirq(void *opaque, int irq, int level) +@@ -96,7 +97,7 @@ static MemTxResult extioi_readw(void *opaque, hwaddr addr, uint64_t *data, + index = (offset - EXTIOI_COREISR_START) >> 2; + /* using attrs to get current cpu index */ + cpu = attrs.requester_id; +- *data = s->coreisr[cpu][index]; ++ *data = s->cpu[cpu].coreisr[index]; + break; + case EXTIOI_COREMAP_START ... EXTIOI_COREMAP_END - 1: + index = (offset - EXTIOI_COREMAP_START) >> 2; +@@ -189,8 +190,8 @@ static MemTxResult extioi_writew(void *opaque, hwaddr addr, + index = (offset - EXTIOI_COREISR_START) >> 2; + /* using attrs to get current cpu index */ + cpu = attrs.requester_id; +- old_data = s->coreisr[cpu][index]; +- s->coreisr[cpu][index] = old_data & ~val; ++ old_data = s->cpu[cpu].coreisr[index]; ++ s->cpu[cpu].coreisr[index] = old_data & ~val; + /* write 1 to clear interrupt */ + old_data &= val; + irq = ctz32(old_data); +@@ -248,14 +249,61 @@ static const MemoryRegionOps extioi_ops = { + .endianness = DEVICE_LITTLE_ENDIAN, + }; + +-static const VMStateDescription vmstate_loongarch_extioi = { +- .name = TYPE_LOONGARCH_EXTIOI, ++static void loongarch_extioi_realize(DeviceState *dev, Error **errp) ++{ ++ LoongArchExtIOI *s = LOONGARCH_EXTIOI(dev); ++ SysBusDevice *sbd = SYS_BUS_DEVICE(dev); ++ int i, pin; ++ ++ if (s->num_cpu == 0) { ++ error_setg(errp, "num-cpu must be at least 1"); ++ return; ++ } ++ ++ for (i = 0; i < EXTIOI_IRQS; i++) { ++ sysbus_init_irq(sbd, &s->irq[i]); ++ } ++ ++ qdev_init_gpio_in(dev, extioi_setirq, EXTIOI_IRQS); ++ memory_region_init_io(&s->extioi_system_mem, OBJECT(s), &extioi_ops, ++ s, "extioi_system_mem", 0x900); ++ sysbus_init_mmio(sbd, &s->extioi_system_mem); ++ s->cpu = g_new0(ExtIOICore, s->num_cpu); ++ if (s->cpu == NULL) { ++ error_setg(errp, "Memory allocation for ExtIOICore faile"); ++ return; ++ } ++ ++ for (i = 0; i < s->num_cpu; i++) { ++ for (pin = 0; pin < LS3A_INTC_IP; pin++) { ++ qdev_init_gpio_out(dev, &s->cpu[i].parent_irq[pin], 1); ++ } ++ } ++} ++ ++static void loongarch_extioi_finalize(Object *obj) ++{ ++ LoongArchExtIOI *s = LOONGARCH_EXTIOI(obj); ++ ++ g_free(s->cpu); ++} ++ ++static const VMStateDescription vmstate_extioi_core = { ++ .name = "extioi-core", + .version_id = 1, + .minimum_version_id = 1, +- .fields = (VMStateField[]) { ++ .fields = (const VMStateField[]) { ++ VMSTATE_UINT32_ARRAY(coreisr, ExtIOICore, EXTIOI_IRQS_GROUP_COUNT), ++ VMSTATE_END_OF_LIST() ++ } ++}; ++ ++static const VMStateDescription vmstate_loongarch_extioi = { ++ .name = TYPE_LOONGARCH_EXTIOI, ++ .version_id = 2, ++ .minimum_version_id = 2, ++ .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(bounce, LoongArchExtIOI, EXTIOI_IRQS_GROUP_COUNT), +- VMSTATE_UINT32_2DARRAY(coreisr, LoongArchExtIOI, EXTIOI_CPUS, +- EXTIOI_IRQS_GROUP_COUNT), + VMSTATE_UINT32_ARRAY(nodetype, LoongArchExtIOI, + EXTIOI_IRQS_NODETYPE_COUNT / 2), + VMSTATE_UINT32_ARRAY(enable, LoongArchExtIOI, EXTIOI_IRQS / 32), +@@ -265,45 +313,32 @@ static const VMStateDescription vmstate_loongarch_extioi = { + VMSTATE_UINT8_ARRAY(sw_ipmap, LoongArchExtIOI, EXTIOI_IRQS_IPMAP_SIZE), + VMSTATE_UINT8_ARRAY(sw_coremap, LoongArchExtIOI, EXTIOI_IRQS), + ++ VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongArchExtIOI, num_cpu, ++ vmstate_extioi_core, ExtIOICore), + VMSTATE_END_OF_LIST() + } + }; + +-static void loongarch_extioi_instance_init(Object *obj) +-{ +- SysBusDevice *dev = SYS_BUS_DEVICE(obj); +- LoongArchExtIOI *s = LOONGARCH_EXTIOI(obj); +- int i, cpu, pin; +- +- for (i = 0; i < EXTIOI_IRQS; i++) { +- sysbus_init_irq(dev, &s->irq[i]); +- } +- +- qdev_init_gpio_in(DEVICE(obj), extioi_setirq, EXTIOI_IRQS); +- +- for (cpu = 0; cpu < EXTIOI_CPUS; cpu++) { +- for (pin = 0; pin < LS3A_INTC_IP; pin++) { +- qdev_init_gpio_out(DEVICE(obj), &s->parent_irq[cpu][pin], 1); +- } +- } +- memory_region_init_io(&s->extioi_system_mem, OBJECT(s), &extioi_ops, +- s, "extioi_system_mem", 0x900); +- sysbus_init_mmio(dev, &s->extioi_system_mem); +-} ++static Property extioi_properties[] = { ++ DEFINE_PROP_UINT32("num-cpu", LoongArchExtIOI, num_cpu, 1), ++ DEFINE_PROP_END_OF_LIST(), ++}; + + static void loongarch_extioi_class_init(ObjectClass *klass, void *data) + { + DeviceClass *dc = DEVICE_CLASS(klass); + ++ dc->realize = loongarch_extioi_realize; ++ device_class_set_props(dc, extioi_properties); + dc->vmsd = &vmstate_loongarch_extioi; + } + + static const TypeInfo loongarch_extioi_info = { + .name = TYPE_LOONGARCH_EXTIOI, + .parent = TYPE_SYS_BUS_DEVICE, +- .instance_init = loongarch_extioi_instance_init, + .instance_size = sizeof(struct LoongArchExtIOI), + .class_init = loongarch_extioi_class_init, ++ .instance_finalize = loongarch_extioi_finalize, + }; + + static void loongarch_extioi_register_types(void) +diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c +index 13d19b6..c9a680e 100644 +--- a/hw/loongarch/virt.c ++++ b/hw/loongarch/virt.c +@@ -582,6 +582,7 @@ static void loongarch_irq_init(LoongArchMachineState *lams) + + /* Create EXTIOI device */ + extioi = qdev_new(TYPE_LOONGARCH_EXTIOI); ++ qdev_prop_set_uint32(extioi, "num-cpu", ms->smp.cpus); + sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal); + memory_region_add_subregion(&lams->system_iocsr, APIC_BASE, + sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 0)); +@@ -590,7 +591,7 @@ static void loongarch_irq_init(LoongArchMachineState *lams) + * connect ext irq to the cpu irq + * cpu_pin[9:2] <= intc_pin[7:0] + */ +- for (cpu = 0; cpu < MIN(ms->smp.cpus, EXTIOI_CPUS); cpu++) { ++ for (cpu = 0; cpu < ms->smp.cpus; cpu++) { + cpudev = DEVICE(qemu_get_cpu(cpu)); + for (pin = 0; pin < LS3A_INTC_IP; pin++) { + qdev_connect_gpio_out(extioi, (cpu * 8 + pin), +diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h +index 110e5e8..a0a46b8 100644 +--- a/include/hw/intc/loongarch_extioi.h ++++ b/include/hw/intc/loongarch_extioi.h +@@ -40,24 +40,29 @@ + #define EXTIOI_COREMAP_START (0xC00 - APIC_OFFSET) + #define EXTIOI_COREMAP_END (0xD00 - APIC_OFFSET) + ++typedef struct ExtIOICore { ++ uint32_t coreisr[EXTIOI_IRQS_GROUP_COUNT]; ++ DECLARE_BITMAP(sw_isr[LS3A_INTC_IP], EXTIOI_IRQS); ++ qemu_irq parent_irq[LS3A_INTC_IP]; ++} ExtIOICore; ++ + #define TYPE_LOONGARCH_EXTIOI "loongarch.extioi" + OBJECT_DECLARE_SIMPLE_TYPE(LoongArchExtIOI, LOONGARCH_EXTIOI) + struct LoongArchExtIOI { + SysBusDevice parent_obj; ++ uint32_t num_cpu; + /* hardware state */ + uint32_t nodetype[EXTIOI_IRQS_NODETYPE_COUNT / 2]; + uint32_t bounce[EXTIOI_IRQS_GROUP_COUNT]; + uint32_t isr[EXTIOI_IRQS / 32]; +- uint32_t coreisr[EXTIOI_CPUS][EXTIOI_IRQS_GROUP_COUNT]; + uint32_t enable[EXTIOI_IRQS / 32]; + uint32_t ipmap[EXTIOI_IRQS_IPMAP_SIZE / 4]; + uint32_t coremap[EXTIOI_IRQS / 4]; + uint32_t sw_pending[EXTIOI_IRQS / 32]; +- DECLARE_BITMAP(sw_isr[EXTIOI_CPUS][LS3A_INTC_IP], EXTIOI_IRQS); + uint8_t sw_ipmap[EXTIOI_IRQS_IPMAP_SIZE]; + uint8_t sw_coremap[EXTIOI_IRQS]; +- qemu_irq parent_irq[EXTIOI_CPUS][LS3A_INTC_IP]; + qemu_irq irq[EXTIOI_IRQS]; ++ ExtIOICore *cpu; + MemoryRegion extioi_system_mem; + }; + #endif /* LOONGARCH_EXTIOI_H */ +-- +1.8.3.1 + diff --git a/0166-hw-intc-loongarch_extioi-Add-vmstate-post_load-suppo.patch b/0166-hw-intc-loongarch_extioi-Add-vmstate-post_load-suppo.patch new file mode 100644 index 0000000000000000000000000000000000000000..e27d832ef00e17a0615e9d65cc09657ddaea75f9 --- /dev/null +++ b/0166-hw-intc-loongarch_extioi-Add-vmstate-post_load-suppo.patch @@ -0,0 +1,194 @@ +From 5202b8db3da24f8d94e761835040cf39457adb49 Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Fri, 15 Dec 2023 17:42:58 +0800 +Subject: [PATCH 176/293] hw/intc/loongarch_extioi: Add vmstate post_load + support + +There are elements sw_ipmap and sw_coremap, which is usd to speed +up irq injection flow. They are saved and restored in vmstate during +migration, indeed they can calculated from hw registers. Here +post_load is added for get sw_ipmap and sw_coremap from extioi hw +state. + +Signed-off-by: Bibo Mao +Reviewed-by: Song Gao +Message-Id: <20231215100333.3933632-5-maobibo@loongson.cn> +Signed-off-by: Song Gao +--- + hw/intc/loongarch_extioi.c | 120 ++++++++++++++++++++++++++++----------------- + 1 file changed, 76 insertions(+), 44 deletions(-) + +diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c +index 28802bf..bdfa3b4 100644 +--- a/hw/intc/loongarch_extioi.c ++++ b/hw/intc/loongarch_extioi.c +@@ -130,12 +130,66 @@ static inline void extioi_enable_irq(LoongArchExtIOI *s, int index,\ + } + } + ++static inline void extioi_update_sw_coremap(LoongArchExtIOI *s, int irq, ++ uint64_t val, bool notify) ++{ ++ int i, cpu; ++ ++ /* ++ * loongarch only support little endian, ++ * so we paresd the value with little endian. ++ */ ++ val = cpu_to_le64(val); ++ ++ for (i = 0; i < 4; i++) { ++ cpu = val & 0xff; ++ cpu = ctz32(cpu); ++ cpu = (cpu >= 4) ? 0 : cpu; ++ val = val >> 8; ++ ++ if (s->sw_coremap[irq + i] == cpu) { ++ continue; ++ } ++ ++ if (notify && test_bit(irq, (unsigned long *)s->isr)) { ++ /* ++ * lower irq at old cpu and raise irq at new cpu ++ */ ++ extioi_update_irq(s, irq + i, 0); ++ s->sw_coremap[irq + i] = cpu; ++ extioi_update_irq(s, irq + i, 1); ++ } else { ++ s->sw_coremap[irq + i] = cpu; ++ } ++ } ++} ++ ++static inline void extioi_update_sw_ipmap(LoongArchExtIOI *s, int index, ++ uint64_t val) ++{ ++ int i; ++ uint8_t ipnum; ++ ++ /* ++ * loongarch only support little endian, ++ * so we paresd the value with little endian. ++ */ ++ val = cpu_to_le64(val); ++ for (i = 0; i < 4; i++) { ++ ipnum = val & 0xff; ++ ipnum = ctz32(ipnum); ++ ipnum = (ipnum >= 4) ? 0 : ipnum; ++ s->sw_ipmap[index * 4 + i] = ipnum; ++ val = val >> 8; ++ } ++} ++ + static MemTxResult extioi_writew(void *opaque, hwaddr addr, + uint64_t val, unsigned size, + MemTxAttrs attrs) + { + LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); +- int i, cpu, index, old_data, irq; ++ int cpu, index, old_data, irq; + uint32_t offset; + + trace_loongarch_extioi_writew(addr, val); +@@ -153,20 +207,7 @@ static MemTxResult extioi_writew(void *opaque, hwaddr addr, + */ + index = (offset - EXTIOI_IPMAP_START) >> 2; + s->ipmap[index] = val; +- /* +- * loongarch only support little endian, +- * so we paresd the value with little endian. +- */ +- val = cpu_to_le64(val); +- for (i = 0; i < 4; i++) { +- uint8_t ipnum; +- ipnum = val & 0xff; +- ipnum = ctz32(ipnum); +- ipnum = (ipnum >= 4) ? 0 : ipnum; +- s->sw_ipmap[index * 4 + i] = ipnum; +- val = val >> 8; +- } +- ++ extioi_update_sw_ipmap(s, index, val); + break; + case EXTIOI_ENABLE_START ... EXTIOI_ENABLE_END - 1: + index = (offset - EXTIOI_ENABLE_START) >> 2; +@@ -205,33 +246,8 @@ static MemTxResult extioi_writew(void *opaque, hwaddr addr, + irq = offset - EXTIOI_COREMAP_START; + index = irq / 4; + s->coremap[index] = val; +- /* +- * loongarch only support little endian, +- * so we paresd the value with little endian. +- */ +- val = cpu_to_le64(val); +- +- for (i = 0; i < 4; i++) { +- cpu = val & 0xff; +- cpu = ctz32(cpu); +- cpu = (cpu >= 4) ? 0 : cpu; +- val = val >> 8; +- +- if (s->sw_coremap[irq + i] == cpu) { +- continue; +- } +- +- if (test_bit(irq, (unsigned long *)s->isr)) { +- /* +- * lower irq at old cpu and raise irq at new cpu +- */ +- extioi_update_irq(s, irq + i, 0); +- s->sw_coremap[irq + i] = cpu; +- extioi_update_irq(s, irq + i, 1); +- } else { +- s->sw_coremap[irq + i] = cpu; +- } +- } ++ ++ extioi_update_sw_coremap(s, irq, val, true); + break; + default: + break; +@@ -288,6 +304,23 @@ static void loongarch_extioi_finalize(Object *obj) + g_free(s->cpu); + } + ++static int vmstate_extioi_post_load(void *opaque, int version_id) ++{ ++ LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); ++ int i, start_irq; ++ ++ for (i = 0; i < (EXTIOI_IRQS / 4); i++) { ++ start_irq = i * 4; ++ extioi_update_sw_coremap(s, start_irq, s->coremap[i], false); ++ } ++ ++ for (i = 0; i < (EXTIOI_IRQS_IPMAP_SIZE / 4); i++) { ++ extioi_update_sw_ipmap(s, i, s->ipmap[i]); ++ } ++ ++ return 0; ++} ++ + static const VMStateDescription vmstate_extioi_core = { + .name = "extioi-core", + .version_id = 1, +@@ -302,6 +335,7 @@ static const VMStateDescription vmstate_loongarch_extioi = { + .name = TYPE_LOONGARCH_EXTIOI, + .version_id = 2, + .minimum_version_id = 2, ++ .post_load = vmstate_extioi_post_load, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(bounce, LoongArchExtIOI, EXTIOI_IRQS_GROUP_COUNT), + VMSTATE_UINT32_ARRAY(nodetype, LoongArchExtIOI, +@@ -310,8 +344,6 @@ static const VMStateDescription vmstate_loongarch_extioi = { + VMSTATE_UINT32_ARRAY(isr, LoongArchExtIOI, EXTIOI_IRQS / 32), + VMSTATE_UINT32_ARRAY(ipmap, LoongArchExtIOI, EXTIOI_IRQS_IPMAP_SIZE / 4), + VMSTATE_UINT32_ARRAY(coremap, LoongArchExtIOI, EXTIOI_IRQS / 4), +- VMSTATE_UINT8_ARRAY(sw_ipmap, LoongArchExtIOI, EXTIOI_IRQS_IPMAP_SIZE), +- VMSTATE_UINT8_ARRAY(sw_coremap, LoongArchExtIOI, EXTIOI_IRQS), + + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongArchExtIOI, num_cpu, + vmstate_extioi_core, ExtIOICore), +-- +1.8.3.1 + diff --git a/0167-configure-Add-linux-header-compile-support-for-Loong.patch b/0167-configure-Add-linux-header-compile-support-for-Loong.patch new file mode 100644 index 0000000000000000000000000000000000000000..6e6f72adbd4099c2a648967f1b66b10f356d7832 --- /dev/null +++ b/0167-configure-Add-linux-header-compile-support-for-Loong.patch @@ -0,0 +1,41 @@ +From f8d33f5e009dbc9cf698f02306559042449b7edd Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Tue, 16 Jan 2024 09:39:52 +0800 +Subject: [PATCH 177/293] configure: Add linux header compile support for + LoongArch +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When compiling qemu with system KVM mode for LoongArch, header files +in directory linux-headers/asm-loongarch should be used firstly. +Otherwise it fails to find kvm.h on system with old glibc, since +latest kernel header files are not installed. + +This patch adds linux_arch definition for LoongArch system so that +header files in directory linux-headers/asm-loongarch can be included. + +Fixes: 714b03c125 ("target/loongarch: Add loongarch kvm into meson build") +Signed-off-by: Bibo Mao +Reviewed-by: Philippe Mathieu-Daudé +Message-ID: <20240116013952.264474-1-maobibo@loongson.cn> +Signed-off-by: Philippe Mathieu-Daudé +--- + configure | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/configure b/configure +index d3ab436..0609aa9 100755 +--- a/configure ++++ b/configure +@@ -445,6 +445,7 @@ case "$cpu" in + loongarch*) + cpu=loongarch64 + host_arch=loongarch64 ++ linux_arch=loongarch + ;; + + mips64*) +-- +1.8.3.1 + diff --git a/0168-target-loongarch-Set-cpuid-CSR-register-only-once-wi.patch b/0168-target-loongarch-Set-cpuid-CSR-register-only-once-wi.patch new file mode 100644 index 0000000000000000000000000000000000000000..bb45f25d635f3ee2f64eb2ff5ff8bebcde77c253 --- /dev/null +++ b/0168-target-loongarch-Set-cpuid-CSR-register-only-once-wi.patch @@ -0,0 +1,57 @@ +From ca7498455d5a5bc210e566ea2d2366be333eebe6 Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Mon, 15 Jan 2024 16:51:21 +0800 +Subject: [PATCH 178/293] target/loongarch: Set cpuid CSR register only once + with kvm mode + +CSR cpuid register is used for routing irq to different vcpus, its +value is kept unchanged since poweron. So it is not necessary to +set CSR cpuid register after system resets, and it is only set at +vm creation stage. + +Signed-off-by: Bibo Mao +Reviewed-by: Song Gao +Message-Id: <20240115085121.180524-1-maobibo@loongson.cn> +Signed-off-by: Song Gao +--- + target/loongarch/kvm/kvm.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c +index 84bcdf5..2230f02 100644 +--- a/target/loongarch/kvm/kvm.c ++++ b/target/loongarch/kvm/kvm.c +@@ -250,7 +250,7 @@ static int kvm_loongarch_get_csr(CPUState *cs) + return ret; + } + +-static int kvm_loongarch_put_csr(CPUState *cs) ++static int kvm_loongarch_put_csr(CPUState *cs, int level) + { + int ret = 0; + LoongArchCPU *cpu = LOONGARCH_CPU(cs); +@@ -322,8 +322,11 @@ static int kvm_loongarch_put_csr(CPUState *cs) + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_RVACFG), + &env->CSR_RVACFG); + +- ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_CPUID), ++ /* CPUID is constant after poweron, it should be set only once */ ++ if (level >= KVM_PUT_FULL_STATE) { ++ ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_CPUID), + &env->CSR_CPUID); ++ } + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_PRCFG1), + &env->CSR_PRCFG1); +@@ -598,7 +601,7 @@ int kvm_arch_put_registers(CPUState *cs, int level) + return ret; + } + +- ret = kvm_loongarch_put_csr(cs); ++ ret = kvm_loongarch_put_csr(cs, level); + if (ret) { + return ret; + } +-- +1.8.3.1 + diff --git a/0169-target-loongarch-kvm-Enable-LSX-LASX-extension.patch b/0169-target-loongarch-kvm-Enable-LSX-LASX-extension.patch new file mode 100644 index 0000000000000000000000000000000000000000..ef95924e245fd1ef64242d1c5d5960861deb594d --- /dev/null +++ b/0169-target-loongarch-kvm-Enable-LSX-LASX-extension.patch @@ -0,0 +1,98 @@ +From 800546d35c63ca459a8d03b6306fd114dea6c38a Mon Sep 17 00:00:00 2001 +From: Song Gao +Date: Mon, 22 Jan 2024 17:02:06 +0800 +Subject: [PATCH 179/293] target/loongarch/kvm: Enable LSX/LASX extension + +The kernel had already support LSX and LASX [1], +but QEMU is disable LSX/LASX for kvm. This patch adds +kvm_check_cpucfg2() to check CPUCFG2. + +[1]: https://lore.kernel.org/all/CABgObfZHRf7E_7Jk4uPRmSyxTy3EiuuYwHC35jQncNL9s-zTDA@mail.gmail.com/ + +Signed-off-by: Song Gao +Reviewed-by: Bibo Mao +Message-Id: <20240122090206.1083584-1-gaosong@loongson.cn> +--- + linux-headers/asm-loongarch/kvm.h | 1 + + target/loongarch/kvm/kvm.c | 45 +++++++++++++++++++++++++++++++++------ + 2 files changed, 39 insertions(+), 7 deletions(-) + +diff --git a/linux-headers/asm-loongarch/kvm.h b/linux-headers/asm-loongarch/kvm.h +index c6ad2ee..923d0bd 100644 +--- a/linux-headers/asm-loongarch/kvm.h ++++ b/linux-headers/asm-loongarch/kvm.h +@@ -79,6 +79,7 @@ struct kvm_fpu { + #define LOONGARCH_REG_64(TYPE, REG) (TYPE | KVM_REG_SIZE_U64 | (REG << LOONGARCH_REG_SHIFT)) + #define KVM_IOC_CSRID(REG) LOONGARCH_REG_64(KVM_REG_LOONGARCH_CSR, REG) + #define KVM_IOC_CPUCFG(REG) LOONGARCH_REG_64(KVM_REG_LOONGARCH_CPUCFG, REG) ++#define KVM_LOONGARCH_VCPU_CPUCFG 0 + + struct kvm_debug_exit_arch { + }; +diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c +index 2230f02..c19978a 100644 +--- a/target/loongarch/kvm/kvm.c ++++ b/target/loongarch/kvm/kvm.c +@@ -540,6 +540,38 @@ static int kvm_loongarch_get_cpucfg(CPUState *cs) + return ret; + } + ++static int kvm_check_cpucfg2(CPUState *cs) ++{ ++ int ret; ++ uint64_t val; ++ struct kvm_device_attr attr = { ++ .group = KVM_LOONGARCH_VCPU_CPUCFG, ++ .attr = 2, ++ .addr = (uint64_t)&val, ++ }; ++ LoongArchCPU *cpu = LOONGARCH_CPU(cs); ++ CPULoongArchState *env = &cpu->env; ++ ++ ret = kvm_vcpu_ioctl(cs, KVM_HAS_DEVICE_ATTR, &attr); ++ ++ if (!ret) { ++ kvm_vcpu_ioctl(cs, KVM_GET_DEVICE_ATTR, &attr); ++ env->cpucfg[2] &= val; ++ ++ if (FIELD_EX32(env->cpucfg[2], CPUCFG2, FP)) { ++ /* The FP minimal version is 1. */ ++ env->cpucfg[2] = FIELD_DP32(env->cpucfg[2], CPUCFG2, FP_VER, 1); ++ } ++ ++ if (FIELD_EX32(env->cpucfg[2], CPUCFG2, LLFTP)) { ++ /* The LLFTP minimal version is 1. */ ++ env->cpucfg[2] = FIELD_DP32(env->cpucfg[2], CPUCFG2, LLFTP_VER, 1); ++ } ++ } ++ ++ return ret; ++} ++ + static int kvm_loongarch_put_cpucfg(CPUState *cs) + { + int i, ret = 0; +@@ -548,14 +580,13 @@ static int kvm_loongarch_put_cpucfg(CPUState *cs) + uint64_t val; + + for (i = 0; i < 21; i++) { ++ if (i == 2) { ++ ret = kvm_check_cpucfg2(cs); ++ if (ret) { ++ return ret; ++ } ++ } + val = env->cpucfg[i]; +- /* LSX and LASX and LBT are not supported in kvm now */ +- if (i == 2) { +- val &= ~(BIT(R_CPUCFG2_LSX_SHIFT) | BIT(R_CPUCFG2_LASX_SHIFT)); +- val &= ~(BIT(R_CPUCFG2_LBT_X86_SHIFT) | +- BIT(R_CPUCFG2_LBT_ARM_SHIFT) | +- BIT(R_CPUCFG2_LBT_MIPS_SHIFT)); +- } + ret = kvm_set_one_reg(cs, KVM_IOC_CPUCFG(i), &val); + if (ret < 0) { + trace_kvm_failed_put_cpucfg(strerror(errno)); +-- +1.8.3.1 + diff --git a/0170-target-loongarch-Fix-qtest-test-hmp-error-when-KVM-o.patch b/0170-target-loongarch-Fix-qtest-test-hmp-error-when-KVM-o.patch new file mode 100644 index 0000000000000000000000000000000000000000..54c8bf601d5e2ed304cb733f1c080cdf9dc3b449 --- /dev/null +++ b/0170-target-loongarch-Fix-qtest-test-hmp-error-when-KVM-o.patch @@ -0,0 +1,570 @@ +From 1d921b47aeb51564426cb691900d77aac422f75b Mon Sep 17 00:00:00 2001 +From: Song Gao +Date: Thu, 25 Jan 2024 14:14:01 +0800 +Subject: [PATCH 180/293] target/loongarch: Fix qtest test-hmp error when + KVM-only build + +The cc->sysemu_ops->get_phys_page_debug() is NULL when +KVM-only build. this patch fixes it. + +Signed-off-by: Song Gao +Tested-by: Bibo Mao +Message-Id: <20240125061401.52526-1-gaosong@loongson.cn> +--- + target/loongarch/cpu.c | 2 - + target/loongarch/cpu_helper.c | 231 ++++++++++++++++++++++++++++++++++++++ + target/loongarch/internals.h | 20 +++- + target/loongarch/meson.build | 1 + + target/loongarch/tcg/tlb_helper.c | 230 ------------------------------------- + 5 files changed, 250 insertions(+), 234 deletions(-) + create mode 100644 target/loongarch/cpu_helper.c + +diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c +index 6611d13..b098b1c 100644 +--- a/target/loongarch/cpu.c ++++ b/target/loongarch/cpu.c +@@ -771,9 +771,7 @@ static struct TCGCPUOps loongarch_tcg_ops = { + #include "hw/core/sysemu-cpu-ops.h" + + static const struct SysemuCPUOps loongarch_sysemu_ops = { +-#ifdef CONFIG_TCG + .get_phys_page_debug = loongarch_cpu_get_phys_page_debug, +-#endif + }; + + static int64_t loongarch_cpu_get_arch_id(CPUState *cs) +diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c +new file mode 100644 +index 0000000..f68d63f +--- /dev/null ++++ b/target/loongarch/cpu_helper.c +@@ -0,0 +1,231 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * LoongArch CPU helpers for qemu ++ * ++ * Copyright (c) 2024 Loongson Technology Corporation Limited ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "cpu.h" ++#include "internals.h" ++#include "cpu-csr.h" ++ ++static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, ++ int *prot, target_ulong address, ++ int access_type, int index, int mmu_idx) ++{ ++ LoongArchTLB *tlb = &env->tlb[index]; ++ uint64_t plv = mmu_idx; ++ uint64_t tlb_entry, tlb_ppn; ++ uint8_t tlb_ps, n, tlb_v, tlb_d, tlb_plv, tlb_nx, tlb_nr, tlb_rplv; ++ ++ if (index >= LOONGARCH_STLB) { ++ tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); ++ } else { ++ tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); ++ } ++ n = (address >> tlb_ps) & 0x1;/* Odd or even */ ++ ++ tlb_entry = n ? tlb->tlb_entry1 : tlb->tlb_entry0; ++ tlb_v = FIELD_EX64(tlb_entry, TLBENTRY, V); ++ tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D); ++ tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV); ++ if (is_la64(env)) { ++ tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN); ++ tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY_64, NX); ++ tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY_64, NR); ++ tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY_64, RPLV); ++ } else { ++ tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_32, PPN); ++ tlb_nx = 0; ++ tlb_nr = 0; ++ tlb_rplv = 0; ++ } ++ ++ /* Remove sw bit between bit12 -- bit PS*/ ++ tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) -1)); ++ ++ /* Check access rights */ ++ if (!tlb_v) { ++ return TLBRET_INVALID; ++ } ++ ++ if (access_type == MMU_INST_FETCH && tlb_nx) { ++ return TLBRET_XI; ++ } ++ ++ if (access_type == MMU_DATA_LOAD && tlb_nr) { ++ return TLBRET_RI; ++ } ++ ++ if (((tlb_rplv == 0) && (plv > tlb_plv)) || ++ ((tlb_rplv == 1) && (plv != tlb_plv))) { ++ return TLBRET_PE; ++ } ++ ++ if ((access_type == MMU_DATA_STORE) && !tlb_d) { ++ return TLBRET_DIRTY; ++ } ++ ++ *physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) | ++ (address & MAKE_64BIT_MASK(0, tlb_ps)); ++ *prot = PAGE_READ; ++ if (tlb_d) { ++ *prot |= PAGE_WRITE; ++ } ++ if (!tlb_nx) { ++ *prot |= PAGE_EXEC; ++ } ++ return TLBRET_MATCH; ++} ++ ++/* ++ * One tlb entry holds an adjacent odd/even pair, the vpn is the ++ * content of the virtual page number divided by 2. So the ++ * compare vpn is bit[47:15] for 16KiB page. while the vppn ++ * field in tlb entry contains bit[47:13], so need adjust. ++ * virt_vpn = vaddr[47:13] ++ */ ++bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, ++ int *index) ++{ ++ LoongArchTLB *tlb; ++ uint16_t csr_asid, tlb_asid, stlb_idx; ++ uint8_t tlb_e, tlb_ps, tlb_g, stlb_ps; ++ int i, compare_shift; ++ uint64_t vpn, tlb_vppn; ++ ++ csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); ++ stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); ++ vpn = (vaddr & TARGET_VIRT_MASK) >> (stlb_ps + 1); ++ stlb_idx = vpn & 0xff; /* VA[25:15] <==> TLBIDX.index for 16KiB Page */ ++ compare_shift = stlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; ++ ++ /* Search STLB */ ++ for (i = 0; i < 8; ++i) { ++ tlb = &env->tlb[i * 256 + stlb_idx]; ++ tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); ++ if (tlb_e) { ++ tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); ++ tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); ++ tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); ++ ++ if ((tlb_g == 1 || tlb_asid == csr_asid) && ++ (vpn == (tlb_vppn >> compare_shift))) { ++ *index = i * 256 + stlb_idx; ++ return true; ++ } ++ } ++ } ++ ++ /* Search MTLB */ ++ for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; ++i) { ++ tlb = &env->tlb[i]; ++ tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); ++ if (tlb_e) { ++ tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); ++ tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); ++ tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); ++ tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); ++ compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; ++ vpn = (vaddr & TARGET_VIRT_MASK) >> (tlb_ps + 1); ++ if ((tlb_g == 1 || tlb_asid == csr_asid) && ++ (vpn == (tlb_vppn >> compare_shift))) { ++ *index = i; ++ return true; ++ } ++ } ++ } ++ return false; ++} ++ ++static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, ++ int *prot, target_ulong address, ++ MMUAccessType access_type, int mmu_idx) ++{ ++ int index, match; ++ ++ match = loongarch_tlb_search(env, address, &index); ++ if (match) { ++ return loongarch_map_tlb_entry(env, physical, prot, ++ address, access_type, index, mmu_idx); ++ } ++ ++ return TLBRET_NOMATCH; ++} ++ ++static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, ++ target_ulong dmw) ++{ ++ if (is_la64(env)) { ++ return va & TARGET_VIRT_MASK; ++ } else { ++ uint32_t pseg = FIELD_EX32(dmw, CSR_DMW_32, PSEG); ++ return (va & MAKE_64BIT_MASK(0, R_CSR_DMW_32_VSEG_SHIFT)) | \ ++ (pseg << R_CSR_DMW_32_VSEG_SHIFT); ++ } ++} ++ ++int get_physical_address(CPULoongArchState *env, hwaddr *physical, ++ int *prot, target_ulong address, ++ MMUAccessType access_type, int mmu_idx) ++{ ++ int user_mode = mmu_idx == MMU_IDX_USER; ++ int kernel_mode = mmu_idx == MMU_IDX_KERNEL; ++ uint32_t plv, base_c, base_v; ++ int64_t addr_high; ++ uint8_t da = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, DA); ++ uint8_t pg = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG); ++ ++ /* Check PG and DA */ ++ if (da & !pg) { ++ *physical = address & TARGET_PHYS_MASK; ++ *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; ++ return TLBRET_MATCH; ++ } ++ ++ plv = kernel_mode | (user_mode << R_CSR_DMW_PLV3_SHIFT); ++ if (is_la64(env)) { ++ base_v = address >> R_CSR_DMW_64_VSEG_SHIFT; ++ } else { ++ base_v = address >> R_CSR_DMW_32_VSEG_SHIFT; ++ } ++ /* Check direct map window */ ++ for (int i = 0; i < 4; i++) { ++ if (is_la64(env)) { ++ base_c = FIELD_EX64(env->CSR_DMW[i], CSR_DMW_64, VSEG); ++ } else { ++ base_c = FIELD_EX64(env->CSR_DMW[i], CSR_DMW_32, VSEG); ++ } ++ if ((plv & env->CSR_DMW[i]) && (base_c == base_v)) { ++ *physical = dmw_va2pa(env, address, env->CSR_DMW[i]); ++ *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; ++ return TLBRET_MATCH; ++ } ++ } ++ ++ /* Check valid extension */ ++ addr_high = sextract64(address, TARGET_VIRT_ADDR_SPACE_BITS, 16); ++ if (!(addr_high == 0 || addr_high == -1)) { ++ return TLBRET_BADADDR; ++ } ++ ++ /* Mapped address */ ++ return loongarch_map_address(env, physical, prot, address, ++ access_type, mmu_idx); ++} ++ ++hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) ++{ ++ LoongArchCPU *cpu = LOONGARCH_CPU(cs); ++ CPULoongArchState *env = &cpu->env; ++ hwaddr phys_addr; ++ int prot; ++ ++ if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD, ++ cpu_mmu_index(env, false)) != 0) { ++ return -1; ++ } ++ return phys_addr; ++} +diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h +index 0beb034..a2fc54c 100644 +--- a/target/loongarch/internals.h ++++ b/target/loongarch/internals.h +@@ -37,6 +37,17 @@ void restore_fp_status(CPULoongArchState *env); + #endif + + #ifndef CONFIG_USER_ONLY ++enum { ++ TLBRET_MATCH = 0, ++ TLBRET_BADADDR = 1, ++ TLBRET_NOMATCH = 2, ++ TLBRET_INVALID = 3, ++ TLBRET_DIRTY = 4, ++ TLBRET_RI = 5, ++ TLBRET_XI = 6, ++ TLBRET_PE = 7, ++}; ++ + extern const VMStateDescription vmstate_loongarch_cpu; + + void loongarch_cpu_set_irq(void *opaque, int irq, int level); +@@ -46,12 +57,17 @@ uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu); + uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu); + void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu, + uint64_t value); ++bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, ++ int *index); ++int get_physical_address(CPULoongArchState *env, hwaddr *physical, ++ int *prot, target_ulong address, ++ MMUAccessType access_type, int mmu_idx); ++hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); ++ + #ifdef CONFIG_TCG + bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, + MMUAccessType access_type, int mmu_idx, + bool probe, uintptr_t retaddr); +- +-hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); + #endif + #endif /* !CONFIG_USER_ONLY */ + +diff --git a/target/loongarch/meson.build b/target/loongarch/meson.build +index db310f6..e002e9a 100644 +--- a/target/loongarch/meson.build ++++ b/target/loongarch/meson.build +@@ -8,6 +8,7 @@ loongarch_ss.add(files( + + loongarch_system_ss = ss.source_set() + loongarch_system_ss.add(files( ++ 'cpu_helper.c', + 'loongarch-qmp-cmds.c', + 'machine.c', + )) +diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c +index 449043c..804ab7a 100644 +--- a/target/loongarch/tcg/tlb_helper.c ++++ b/target/loongarch/tcg/tlb_helper.c +@@ -17,236 +17,6 @@ + #include "exec/log.h" + #include "cpu-csr.h" + +-enum { +- TLBRET_MATCH = 0, +- TLBRET_BADADDR = 1, +- TLBRET_NOMATCH = 2, +- TLBRET_INVALID = 3, +- TLBRET_DIRTY = 4, +- TLBRET_RI = 5, +- TLBRET_XI = 6, +- TLBRET_PE = 7, +-}; +- +-static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, +- int *prot, target_ulong address, +- int access_type, int index, int mmu_idx) +-{ +- LoongArchTLB *tlb = &env->tlb[index]; +- uint64_t plv = mmu_idx; +- uint64_t tlb_entry, tlb_ppn; +- uint8_t tlb_ps, n, tlb_v, tlb_d, tlb_plv, tlb_nx, tlb_nr, tlb_rplv; +- +- if (index >= LOONGARCH_STLB) { +- tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); +- } else { +- tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); +- } +- n = (address >> tlb_ps) & 0x1;/* Odd or even */ +- +- tlb_entry = n ? tlb->tlb_entry1 : tlb->tlb_entry0; +- tlb_v = FIELD_EX64(tlb_entry, TLBENTRY, V); +- tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D); +- tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV); +- if (is_la64(env)) { +- tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN); +- tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY_64, NX); +- tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY_64, NR); +- tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY_64, RPLV); +- } else { +- tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_32, PPN); +- tlb_nx = 0; +- tlb_nr = 0; +- tlb_rplv = 0; +- } +- +- /* Remove sw bit between bit12 -- bit PS*/ +- tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) -1)); +- +- /* Check access rights */ +- if (!tlb_v) { +- return TLBRET_INVALID; +- } +- +- if (access_type == MMU_INST_FETCH && tlb_nx) { +- return TLBRET_XI; +- } +- +- if (access_type == MMU_DATA_LOAD && tlb_nr) { +- return TLBRET_RI; +- } +- +- if (((tlb_rplv == 0) && (plv > tlb_plv)) || +- ((tlb_rplv == 1) && (plv != tlb_plv))) { +- return TLBRET_PE; +- } +- +- if ((access_type == MMU_DATA_STORE) && !tlb_d) { +- return TLBRET_DIRTY; +- } +- +- *physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) | +- (address & MAKE_64BIT_MASK(0, tlb_ps)); +- *prot = PAGE_READ; +- if (tlb_d) { +- *prot |= PAGE_WRITE; +- } +- if (!tlb_nx) { +- *prot |= PAGE_EXEC; +- } +- return TLBRET_MATCH; +-} +- +-/* +- * One tlb entry holds an adjacent odd/even pair, the vpn is the +- * content of the virtual page number divided by 2. So the +- * compare vpn is bit[47:15] for 16KiB page. while the vppn +- * field in tlb entry contains bit[47:13], so need adjust. +- * virt_vpn = vaddr[47:13] +- */ +-static bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, +- int *index) +-{ +- LoongArchTLB *tlb; +- uint16_t csr_asid, tlb_asid, stlb_idx; +- uint8_t tlb_e, tlb_ps, tlb_g, stlb_ps; +- int i, compare_shift; +- uint64_t vpn, tlb_vppn; +- +- csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); +- stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); +- vpn = (vaddr & TARGET_VIRT_MASK) >> (stlb_ps + 1); +- stlb_idx = vpn & 0xff; /* VA[25:15] <==> TLBIDX.index for 16KiB Page */ +- compare_shift = stlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; +- +- /* Search STLB */ +- for (i = 0; i < 8; ++i) { +- tlb = &env->tlb[i * 256 + stlb_idx]; +- tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); +- if (tlb_e) { +- tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); +- tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); +- tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); +- +- if ((tlb_g == 1 || tlb_asid == csr_asid) && +- (vpn == (tlb_vppn >> compare_shift))) { +- *index = i * 256 + stlb_idx; +- return true; +- } +- } +- } +- +- /* Search MTLB */ +- for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; ++i) { +- tlb = &env->tlb[i]; +- tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); +- if (tlb_e) { +- tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); +- tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); +- tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); +- tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); +- compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; +- vpn = (vaddr & TARGET_VIRT_MASK) >> (tlb_ps + 1); +- if ((tlb_g == 1 || tlb_asid == csr_asid) && +- (vpn == (tlb_vppn >> compare_shift))) { +- *index = i; +- return true; +- } +- } +- } +- return false; +-} +- +-static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, +- int *prot, target_ulong address, +- MMUAccessType access_type, int mmu_idx) +-{ +- int index, match; +- +- match = loongarch_tlb_search(env, address, &index); +- if (match) { +- return loongarch_map_tlb_entry(env, physical, prot, +- address, access_type, index, mmu_idx); +- } +- +- return TLBRET_NOMATCH; +-} +- +-static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, +- target_ulong dmw) +-{ +- if (is_la64(env)) { +- return va & TARGET_VIRT_MASK; +- } else { +- uint32_t pseg = FIELD_EX32(dmw, CSR_DMW_32, PSEG); +- return (va & MAKE_64BIT_MASK(0, R_CSR_DMW_32_VSEG_SHIFT)) | \ +- (pseg << R_CSR_DMW_32_VSEG_SHIFT); +- } +-} +- +-static int get_physical_address(CPULoongArchState *env, hwaddr *physical, +- int *prot, target_ulong address, +- MMUAccessType access_type, int mmu_idx) +-{ +- int user_mode = mmu_idx == MMU_IDX_USER; +- int kernel_mode = mmu_idx == MMU_IDX_KERNEL; +- uint32_t plv, base_c, base_v; +- int64_t addr_high; +- uint8_t da = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, DA); +- uint8_t pg = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG); +- +- /* Check PG and DA */ +- if (da & !pg) { +- *physical = address & TARGET_PHYS_MASK; +- *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; +- return TLBRET_MATCH; +- } +- +- plv = kernel_mode | (user_mode << R_CSR_DMW_PLV3_SHIFT); +- if (is_la64(env)) { +- base_v = address >> R_CSR_DMW_64_VSEG_SHIFT; +- } else { +- base_v = address >> R_CSR_DMW_32_VSEG_SHIFT; +- } +- /* Check direct map window */ +- for (int i = 0; i < 4; i++) { +- if (is_la64(env)) { +- base_c = FIELD_EX64(env->CSR_DMW[i], CSR_DMW_64, VSEG); +- } else { +- base_c = FIELD_EX64(env->CSR_DMW[i], CSR_DMW_32, VSEG); +- } +- if ((plv & env->CSR_DMW[i]) && (base_c == base_v)) { +- *physical = dmw_va2pa(env, address, env->CSR_DMW[i]); +- *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; +- return TLBRET_MATCH; +- } +- } +- +- /* Check valid extension */ +- addr_high = sextract64(address, TARGET_VIRT_ADDR_SPACE_BITS, 16); +- if (!(addr_high == 0 || addr_high == -1)) { +- return TLBRET_BADADDR; +- } +- +- /* Mapped address */ +- return loongarch_map_address(env, physical, prot, address, +- access_type, mmu_idx); +-} +- +-hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) +-{ +- LoongArchCPU *cpu = LOONGARCH_CPU(cs); +- CPULoongArchState *env = &cpu->env; +- hwaddr phys_addr; +- int prot; +- +- if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD, +- cpu_mmu_index(env, false)) != 0) { +- return -1; +- } +- return phys_addr; +-} +- + static void raise_mmu_exception(CPULoongArchState *env, target_ulong address, + MMUAccessType access_type, int tlb_error) + { +-- +1.8.3.1 + diff --git a/0171-loongarch-Change-the-UEFI-loading-mode-to-loongarch.patch b/0171-loongarch-Change-the-UEFI-loading-mode-to-loongarch.patch new file mode 100644 index 0000000000000000000000000000000000000000..28f6869ee85c366059cf8ad7038b75349bedc109 --- /dev/null +++ b/0171-loongarch-Change-the-UEFI-loading-mode-to-loongarch.patch @@ -0,0 +1,287 @@ +From 484be2c61b9b2c9b5ac54ccb7c615e964f4797c0 Mon Sep 17 00:00:00 2001 +From: Xianglai Li +Date: Mon, 19 Feb 2024 18:34:14 +0800 +Subject: [PATCH 181/293] loongarch: Change the UEFI loading mode to loongarch +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The UEFI loading mode in loongarch is very different +from that in other architectures:loongarch's UEFI code +is in rom, while other architectures' UEFI code is in flash. + +loongarch UEFI can be loaded as follows: +-machine virt,pflash=pflash0-format +-bios ./QEMU_EFI.fd + +Other architectures load UEFI using the following methods: +-machine virt,pflash0=pflash0-format,pflash1=pflash1-format + +loongarch's UEFI loading method makes qemu and libvirt incompatible +when using NVRAM, and the cost of loongarch's current loading method +far outweighs the benefits, so we decided to use the same UEFI loading +scheme as other architectures. + +Cc: Andrea Bolognani +Cc: maobibo@loongson.cn +Cc: Philippe Mathieu-Daudé +Cc: Song Gao +Cc: zhaotianrui@loongson.cn +Signed-off-by: Xianglai Li +Tested-by: Andrea Bolognani +Reviewed-by: Song Gao +Message-Id: <0bd892aa9b88e0f4cc904cb70efd0251fc1cde29.1708336919.git.lixianglai@loongson.cn> +Signed-off-by: Song Gao +--- + hw/loongarch/acpi-build.c | 29 +++++++++++-- + hw/loongarch/virt.c | 101 ++++++++++++++++++++++++++++++++------------ + include/hw/loongarch/virt.h | 10 +++-- + 3 files changed, 107 insertions(+), 33 deletions(-) + +diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c +index ae292fc..f990405 100644 +--- a/hw/loongarch/acpi-build.c ++++ b/hw/loongarch/acpi-build.c +@@ -314,16 +314,39 @@ static void build_pci_device_aml(Aml *scope, LoongArchMachineState *lams) + static void build_flash_aml(Aml *scope, LoongArchMachineState *lams) + { + Aml *dev, *crs; ++ MemoryRegion *flash_mem; + +- hwaddr flash_base = VIRT_FLASH_BASE; +- hwaddr flash_size = VIRT_FLASH_SIZE; ++ hwaddr flash0_base; ++ hwaddr flash0_size; ++ ++ hwaddr flash1_base; ++ hwaddr flash1_size; ++ ++ flash_mem = pflash_cfi01_get_memory(lams->flash[0]); ++ flash0_base = flash_mem->addr; ++ flash0_size = memory_region_size(flash_mem); ++ ++ flash_mem = pflash_cfi01_get_memory(lams->flash[1]); ++ flash1_base = flash_mem->addr; ++ flash1_size = memory_region_size(flash_mem); + + dev = aml_device("FLS0"); + aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015"))); + aml_append(dev, aml_name_decl("_UID", aml_int(0))); + + crs = aml_resource_template(); +- aml_append(crs, aml_memory32_fixed(flash_base, flash_size, AML_READ_WRITE)); ++ aml_append(crs, aml_memory32_fixed(flash0_base, flash0_size, ++ AML_READ_WRITE)); ++ aml_append(dev, aml_name_decl("_CRS", crs)); ++ aml_append(scope, dev); ++ ++ dev = aml_device("FLS1"); ++ aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015"))); ++ aml_append(dev, aml_name_decl("_UID", aml_int(1))); ++ ++ crs = aml_resource_template(); ++ aml_append(crs, aml_memory32_fixed(flash1_base, flash1_size, ++ AML_READ_WRITE)); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(scope, dev); + } +diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c +index c9a680e..6ef40fa 100644 +--- a/hw/loongarch/virt.c ++++ b/hw/loongarch/virt.c +@@ -54,7 +54,9 @@ struct loaderparams { + const char *initrd_filename; + }; + +-static void virt_flash_create(LoongArchMachineState *lams) ++static PFlashCFI01 *virt_flash_create1(LoongArchMachineState *lams, ++ const char *name, ++ const char *alias_prop_name) + { + DeviceState *dev = qdev_new(TYPE_PFLASH_CFI01); + +@@ -66,45 +68,78 @@ static void virt_flash_create(LoongArchMachineState *lams) + qdev_prop_set_uint16(dev, "id1", 0x18); + qdev_prop_set_uint16(dev, "id2", 0x00); + qdev_prop_set_uint16(dev, "id3", 0x00); +- qdev_prop_set_string(dev, "name", "virt.flash"); +- object_property_add_child(OBJECT(lams), "virt.flash", OBJECT(dev)); +- object_property_add_alias(OBJECT(lams), "pflash", ++ qdev_prop_set_string(dev, "name", name); ++ object_property_add_child(OBJECT(lams), name, OBJECT(dev)); ++ object_property_add_alias(OBJECT(lams), alias_prop_name, + OBJECT(dev), "drive"); ++ return PFLASH_CFI01(dev); ++} + +- lams->flash = PFLASH_CFI01(dev); ++static void virt_flash_create(LoongArchMachineState *lams) ++{ ++ lams->flash[0] = virt_flash_create1(lams, "virt.flash0", "pflash0"); ++ lams->flash[1] = virt_flash_create1(lams, "virt.flash1", "pflash1"); + } + +-static void virt_flash_map(LoongArchMachineState *lams, +- MemoryRegion *sysmem) ++static void virt_flash_map1(PFlashCFI01 *flash, ++ hwaddr base, hwaddr size, ++ MemoryRegion *sysmem) + { +- PFlashCFI01 *flash = lams->flash; + DeviceState *dev = DEVICE(flash); +- hwaddr base = VIRT_FLASH_BASE; +- hwaddr size = VIRT_FLASH_SIZE; ++ BlockBackend *blk; ++ hwaddr real_size = size; ++ ++ blk = pflash_cfi01_get_blk(flash); ++ if (blk) { ++ real_size = blk_getlength(blk); ++ assert(real_size && real_size <= size); ++ } + +- assert(QEMU_IS_ALIGNED(size, VIRT_FLASH_SECTOR_SIZE)); +- assert(size / VIRT_FLASH_SECTOR_SIZE <= UINT32_MAX); ++ assert(QEMU_IS_ALIGNED(real_size, VIRT_FLASH_SECTOR_SIZE)); ++ assert(real_size / VIRT_FLASH_SECTOR_SIZE <= UINT32_MAX); + +- qdev_prop_set_uint32(dev, "num-blocks", size / VIRT_FLASH_SECTOR_SIZE); ++ qdev_prop_set_uint32(dev, "num-blocks", real_size / VIRT_FLASH_SECTOR_SIZE); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + memory_region_add_subregion(sysmem, base, + sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0)); ++} + ++static void virt_flash_map(LoongArchMachineState *lams, ++ MemoryRegion *sysmem) ++{ ++ PFlashCFI01 *flash0 = lams->flash[0]; ++ PFlashCFI01 *flash1 = lams->flash[1]; ++ ++ virt_flash_map1(flash0, VIRT_FLASH0_BASE, VIRT_FLASH0_SIZE, sysmem); ++ virt_flash_map1(flash1, VIRT_FLASH1_BASE, VIRT_FLASH1_SIZE, sysmem); + } + + static void fdt_add_flash_node(LoongArchMachineState *lams) + { + MachineState *ms = MACHINE(lams); + char *nodename; ++ MemoryRegion *flash_mem; ++ ++ hwaddr flash0_base; ++ hwaddr flash0_size; + +- hwaddr flash_base = VIRT_FLASH_BASE; +- hwaddr flash_size = VIRT_FLASH_SIZE; ++ hwaddr flash1_base; ++ hwaddr flash1_size; + +- nodename = g_strdup_printf("/flash@%" PRIx64, flash_base); ++ flash_mem = pflash_cfi01_get_memory(lams->flash[0]); ++ flash0_base = flash_mem->addr; ++ flash0_size = memory_region_size(flash_mem); ++ ++ flash_mem = pflash_cfi01_get_memory(lams->flash[1]); ++ flash1_base = flash_mem->addr; ++ flash1_size = memory_region_size(flash_mem); ++ ++ nodename = g_strdup_printf("/flash@%" PRIx64, flash0_base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "cfi-flash"); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", +- 2, flash_base, 2, flash_size); ++ 2, flash0_base, 2, flash0_size, ++ 2, flash1_base, 2, flash1_size); + qemu_fdt_setprop_cell(ms->fdt, nodename, "bank-width", 4); + g_free(nodename); + } +@@ -639,12 +674,32 @@ static void loongarch_firmware_init(LoongArchMachineState *lams) + { + char *filename = MACHINE(lams)->firmware; + char *bios_name = NULL; +- int bios_size; ++ int bios_size, i; ++ BlockBackend *pflash_blk0; ++ MemoryRegion *mr; + + lams->bios_loaded = false; + ++ /* Map legacy -drive if=pflash to machine properties */ ++ for (i = 0; i < ARRAY_SIZE(lams->flash); i++) { ++ pflash_cfi01_legacy_drive(lams->flash[i], ++ drive_get(IF_PFLASH, 0, i)); ++ } ++ + virt_flash_map(lams, get_system_memory()); + ++ pflash_blk0 = pflash_cfi01_get_blk(lams->flash[0]); ++ ++ if (pflash_blk0) { ++ if (filename) { ++ error_report("cannot use both '-bios' and '-drive if=pflash'" ++ "options at once"); ++ exit(1); ++ } ++ lams->bios_loaded = true; ++ return; ++ } ++ + if (filename) { + bios_name = qemu_find_file(QEMU_FILE_TYPE_BIOS, filename); + if (!bios_name) { +@@ -652,21 +707,15 @@ static void loongarch_firmware_init(LoongArchMachineState *lams) + exit(1); + } + +- bios_size = load_image_targphys(bios_name, VIRT_BIOS_BASE, VIRT_BIOS_SIZE); ++ mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(lams->flash[0]), 0); ++ bios_size = load_image_mr(bios_name, mr); + if (bios_size < 0) { + error_report("Could not load ROM image '%s'", bios_name); + exit(1); + } +- + g_free(bios_name); +- +- memory_region_init_ram(&lams->bios, NULL, "loongarch.bios", +- VIRT_BIOS_SIZE, &error_fatal); +- memory_region_set_readonly(&lams->bios, true); +- memory_region_add_subregion(get_system_memory(), VIRT_BIOS_BASE, &lams->bios); + lams->bios_loaded = true; + } +- + } + + static void reset_load_elf(void *opaque) +diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h +index 6ef9a92..252f7df 100644 +--- a/include/hw/loongarch/virt.h ++++ b/include/hw/loongarch/virt.h +@@ -18,10 +18,12 @@ + + #define VIRT_FWCFG_BASE 0x1e020000UL + #define VIRT_BIOS_BASE 0x1c000000UL +-#define VIRT_BIOS_SIZE (4 * MiB) ++#define VIRT_BIOS_SIZE (16 * MiB) + #define VIRT_FLASH_SECTOR_SIZE (128 * KiB) +-#define VIRT_FLASH_BASE 0x1d000000UL +-#define VIRT_FLASH_SIZE (16 * MiB) ++#define VIRT_FLASH0_BASE VIRT_BIOS_BASE ++#define VIRT_FLASH0_SIZE VIRT_BIOS_SIZE ++#define VIRT_FLASH1_BASE 0x1d000000UL ++#define VIRT_FLASH1_SIZE (16 * MiB) + + #define VIRT_LOWMEM_BASE 0 + #define VIRT_LOWMEM_SIZE 0x10000000 +@@ -49,7 +51,7 @@ struct LoongArchMachineState { + int fdt_size; + DeviceState *platform_bus_dev; + PCIBus *pci_bus; +- PFlashCFI01 *flash; ++ PFlashCFI01 *flash[2]; + MemoryRegion system_iocsr; + MemoryRegion iocsr_mem; + AddressSpace as_iocsr; +-- +1.8.3.1 + diff --git a/0172-target-loongarch-Fix-tlb-huge-page-loading-issue.patch b/0172-target-loongarch-Fix-tlb-huge-page-loading-issue.patch new file mode 100644 index 0000000000000000000000000000000000000000..8214f3c3a0d4f1b345f540f73f33108990ffa73b --- /dev/null +++ b/0172-target-loongarch-Fix-tlb-huge-page-loading-issue.patch @@ -0,0 +1,208 @@ +From b99489d37aa0ee611c67be81e162e36566437d58 Mon Sep 17 00:00:00 2001 +From: Xianglai Li +Date: Mon, 18 Mar 2024 15:03:32 +0800 +Subject: [PATCH 182/293] target/loongarch: Fix tlb huge page loading issue + +When we use qemu tcg simulation, the page size of bios is 4KB. +When using the level 2 super huge page (page size is 1G) to create the page table, +it is found that the content of the corresponding address space is abnormal, +resulting in the bios can not start the operating system and graphical interface normally. + +The lddir and ldpte instruction emulation has +a problem with the use of super huge page processing above level 2. +The page size is not correctly calculated, +resulting in the wrong page size of the table entry found by tlb. + +Signed-off-by: Xianglai Li +Reviewed-by: Richard Henderson +Signed-off-by: Song Gao +Message-Id: <20240318070332.1273939-1-lixianglai@loongson.cn> +--- + target/loongarch/cpu-csr.h | 3 + + target/loongarch/internals.h | 5 -- + target/loongarch/tcg/tlb_helper.c | 113 ++++++++++++++++++++++++++------------ + 3 files changed, 82 insertions(+), 39 deletions(-) + +diff --git a/target/loongarch/cpu-csr.h b/target/loongarch/cpu-csr.h +index c59d7a9..0834e91 100644 +--- a/target/loongarch/cpu-csr.h ++++ b/target/loongarch/cpu-csr.h +@@ -67,6 +67,9 @@ FIELD(TLBENTRY, D, 1, 1) + FIELD(TLBENTRY, PLV, 2, 2) + FIELD(TLBENTRY, MAT, 4, 2) + FIELD(TLBENTRY, G, 6, 1) ++FIELD(TLBENTRY, HUGE, 6, 1) ++FIELD(TLBENTRY, HGLOBAL, 12, 1) ++FIELD(TLBENTRY, LEVEL, 13, 2) + FIELD(TLBENTRY_32, PPN, 8, 24) + FIELD(TLBENTRY_64, PPN, 12, 36) + FIELD(TLBENTRY_64, NR, 61, 1) +diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h +index a2fc54c..944153b 100644 +--- a/target/loongarch/internals.h ++++ b/target/loongarch/internals.h +@@ -16,11 +16,6 @@ + #define TARGET_PHYS_MASK MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS) + #define TARGET_VIRT_MASK MAKE_64BIT_MASK(0, TARGET_VIRT_ADDR_SPACE_BITS) + +-/* Global bit used for lddir/ldpte */ +-#define LOONGARCH_PAGE_HUGE_SHIFT 6 +-/* Global bit for huge page */ +-#define LOONGARCH_HGLOBAL_SHIFT 12 +- + void loongarch_translate_init(void); + + void loongarch_cpu_dump_state(CPUState *cpu, FILE *f, int flags); +diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c +index 804ab7a..eedd1ac 100644 +--- a/target/loongarch/tcg/tlb_helper.c ++++ b/target/loongarch/tcg/tlb_helper.c +@@ -17,6 +17,34 @@ + #include "exec/log.h" + #include "cpu-csr.h" + ++static void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, ++ uint64_t *dir_width, target_ulong level) ++{ ++ switch (level) { ++ case 1: ++ *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_BASE); ++ *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_WIDTH); ++ break; ++ case 2: ++ *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_BASE); ++ *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_WIDTH); ++ break; ++ case 3: ++ *dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_BASE); ++ *dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_WIDTH); ++ break; ++ case 4: ++ *dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_BASE); ++ *dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_WIDTH); ++ break; ++ default: ++ /* level may be zero for ldpte */ ++ *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE); ++ *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH); ++ break; ++ } ++} ++ + static void raise_mmu_exception(CPULoongArchState *env, target_ulong address, + MMUAccessType access_type, int tlb_error) + { +@@ -486,7 +514,25 @@ target_ulong helper_lddir(CPULoongArchState *env, target_ulong base, + target_ulong badvaddr, index, phys, ret; + int shift; + uint64_t dir_base, dir_width; +- bool huge = (base >> LOONGARCH_PAGE_HUGE_SHIFT) & 0x1; ++ ++ if (unlikely((level == 0) || (level > 4))) { ++ qemu_log_mask(LOG_GUEST_ERROR, ++ "Attepted LDDIR with level %"PRId64"\n", level); ++ return base; ++ } ++ ++ if (FIELD_EX64(base, TLBENTRY, HUGE)) { ++ if (unlikely(level == 4)) { ++ qemu_log_mask(LOG_GUEST_ERROR, ++ "Attempted use of level 4 huge page\n"); ++ } ++ ++ if (FIELD_EX64(base, TLBENTRY, LEVEL)) { ++ return base; ++ } else { ++ return FIELD_DP64(base, TLBENTRY, LEVEL, level); ++ } ++ } + + badvaddr = env->CSR_TLBRBADV; + base = base & TARGET_PHYS_MASK; +@@ -495,30 +541,7 @@ target_ulong helper_lddir(CPULoongArchState *env, target_ulong base, + shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH); + shift = (shift + 1) * 3; + +- if (huge) { +- return base; +- } +- switch (level) { +- case 1: +- dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_BASE); +- dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_WIDTH); +- break; +- case 2: +- dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_BASE); +- dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_WIDTH); +- break; +- case 3: +- dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_BASE); +- dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_WIDTH); +- break; +- case 4: +- dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_BASE); +- dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_WIDTH); +- break; +- default: +- do_raise_exception(env, EXCCODE_INE, GETPC()); +- return 0; +- } ++ get_dir_base_width(env, &dir_base, &dir_width, level); + index = (badvaddr >> dir_base) & ((1 << dir_width) - 1); + phys = base | index << shift; + ret = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; +@@ -531,20 +554,42 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, + CPUState *cs = env_cpu(env); + target_ulong phys, tmp0, ptindex, ptoffset0, ptoffset1, ps, badv; + int shift; +- bool huge = (base >> LOONGARCH_PAGE_HUGE_SHIFT) & 0x1; + uint64_t ptbase = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE); + uint64_t ptwidth = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH); ++ uint64_t dir_base, dir_width; + ++ /* ++ * The parameter "base" has only two types, ++ * one is the page table base address, ++ * whose bit 6 should be 0, ++ * and the other is the huge page entry, ++ * whose bit 6 should be 1. ++ */ + base = base & TARGET_PHYS_MASK; ++ if (FIELD_EX64(base, TLBENTRY, HUGE)) { ++ /* ++ * Gets the huge page level and Gets huge page size. ++ * Clears the huge page level information in the entry. ++ * Clears huge page bit. ++ * Move HGLOBAL bit to GLOBAL bit. ++ */ ++ get_dir_base_width(env, &dir_base, &dir_width, ++ FIELD_EX64(base, TLBENTRY, LEVEL)); ++ ++ base = FIELD_DP64(base, TLBENTRY, LEVEL, 0); ++ base = FIELD_DP64(base, TLBENTRY, HUGE, 0); ++ if (FIELD_EX64(base, TLBENTRY, HGLOBAL)) { ++ base = FIELD_DP64(base, TLBENTRY, HGLOBAL, 0); ++ base = FIELD_DP64(base, TLBENTRY, G, 1); ++ } + +- if (huge) { +- /* Huge Page. base is paddr */ +- tmp0 = base ^ (1 << LOONGARCH_PAGE_HUGE_SHIFT); +- /* Move Global bit */ +- tmp0 = ((tmp0 & (1 << LOONGARCH_HGLOBAL_SHIFT)) >> +- LOONGARCH_HGLOBAL_SHIFT) << R_TLBENTRY_G_SHIFT | +- (tmp0 & (~(1 << LOONGARCH_HGLOBAL_SHIFT))); +- ps = ptbase + ptwidth - 1; ++ ps = dir_base + dir_width - 1; ++ /* ++ * Huge pages are evenly split into parity pages ++ * when loaded into the tlb, ++ * so the tlb page size needs to be divided by 2. ++ */ ++ tmp0 = base; + if (odd) { + tmp0 += MAKE_64BIT_MASK(ps, 1); + } +-- +1.8.3.1 + diff --git a/0173-target-loongarch-Fix-qemu-loongarch64-hang-when-exec.patch b/0173-target-loongarch-Fix-qemu-loongarch64-hang-when-exec.patch new file mode 100644 index 0000000000000000000000000000000000000000..e0aaabe12bcf8566ae0db9a1671b973b3e846c68 --- /dev/null +++ b/0173-target-loongarch-Fix-qemu-loongarch64-hang-when-exec.patch @@ -0,0 +1,45 @@ +From b4c4a14780fc23730594824c7fe0e073fd063ac3 Mon Sep 17 00:00:00 2001 +From: Song Gao +Date: Wed, 20 Mar 2024 09:39:55 +0800 +Subject: [PATCH 183/293] target/loongarch: Fix qemu-loongarch64 hang when + executing 'll.d $t0, $t0, 0' + +On gen_ll, if a->imm is zero, make_address_x return src1, +but the load to destination may clobber src1. We use a new +destination to fix this problem. + +Fixes: c5af6628f4be (target/loongarch: Extract make_address_i() helper) +Reviewed-by: Richard Henderson +Suggested-by: Richard Henderson +Signed-off-by: Song Gao +Message-Id: <20240320013955.1561311-1-gaosong@loongson.cn> +--- + target/loongarch/tcg/insn_trans/trans_atomic.c.inc | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/target/loongarch/tcg/insn_trans/trans_atomic.c.inc b/target/loongarch/tcg/insn_trans/trans_atomic.c.inc +index 80c2e28..974bc2a 100644 +--- a/target/loongarch/tcg/insn_trans/trans_atomic.c.inc ++++ b/target/loongarch/tcg/insn_trans/trans_atomic.c.inc +@@ -5,14 +5,14 @@ + + static bool gen_ll(DisasContext *ctx, arg_rr_i *a, MemOp mop) + { +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); ++ TCGv t1 = tcg_temp_new(); + TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); + TCGv t0 = make_address_i(ctx, src1, a->imm); + +- tcg_gen_qemu_ld_i64(dest, t0, ctx->mem_idx, mop); ++ tcg_gen_qemu_ld_i64(t1, t0, ctx->mem_idx, mop); + tcg_gen_st_tl(t0, tcg_env, offsetof(CPULoongArchState, lladdr)); +- tcg_gen_st_tl(dest, tcg_env, offsetof(CPULoongArchState, llval)); +- gen_set_gpr(a->rd, dest, EXT_NONE); ++ tcg_gen_st_tl(t1, tcg_env, offsetof(CPULoongArchState, llval)); ++ gen_set_gpr(a->rd, t1, EXT_NONE); + + return true; + } +-- +1.8.3.1 + diff --git a/0174-target-loongarch-kvm-Add-software-breakpoint-support.patch b/0174-target-loongarch-kvm-Add-software-breakpoint-support.patch new file mode 100644 index 0000000000000000000000000000000000000000..f229d0558860114f9be1f644d2677b999f1905a8 --- /dev/null +++ b/0174-target-loongarch-kvm-Add-software-breakpoint-support.patch @@ -0,0 +1,132 @@ +From 1729bc04cb9ff298bbe543dec2c7cbaa316cbf9c Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Sun, 18 Feb 2024 15:00:25 +0800 +Subject: [PATCH 184/293] target/loongarch/kvm: Add software breakpoint support + +With KVM virtualization, debug exception is passthrough to +to guest kernel rather than host mode. Here hypercall +instruction with special hypercall code is used for sw +breakpoint usage. + +Now only software breakpoint is supported, and itt is allowed +to insert/remove software breakpoint. Later hardware breakpoint +will be added. + +Signed-off-by: Bibo Mao +--- + target/loongarch/kvm/kvm.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 77 insertions(+) + +diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c +index c19978a..49d0207 100644 +--- a/target/loongarch/kvm/kvm.c ++++ b/target/loongarch/kvm/kvm.c +@@ -29,6 +29,7 @@ + #include "trace.h" + + static bool cap_has_mp_state; ++static unsigned int brk_insn; + const KVMCapabilityInfo kvm_arch_required_capabilities[] = { + KVM_CAP_LAST_INFO + }; +@@ -675,7 +676,14 @@ static void kvm_loongarch_vm_stage_change(void *opaque, bool running, + + int kvm_arch_init_vcpu(CPUState *cs) + { ++ uint64_t val; ++ + qemu_add_vm_change_state_handler(kvm_loongarch_vm_stage_change, cs); ++ ++ if (!kvm_get_one_reg(cs, KVM_REG_LOONGARCH_DEBUG_INST, &val)) { ++ brk_insn = val; ++ } ++ + return 0; + } + +@@ -755,6 +763,68 @@ bool kvm_arch_cpu_check_are_resettable(void) + return true; + } + ++ ++void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg) ++{ ++ if (kvm_sw_breakpoints_active(cpu)) { ++ dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; ++ } ++} ++ ++int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) ++{ ++ if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 0) || ++ cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&brk_insn, 4, 1)) { ++ error_report("%s failed", __func__); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) ++{ ++ static uint32_t brk; ++ ++ if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&brk, 4, 0) || ++ brk != brk_insn || ++ cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 1)) { ++ error_report("%s failed", __func__); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++int kvm_arch_insert_hw_breakpoint(vaddr addr, vaddr len, int type) ++{ ++ return -ENOSYS; ++} ++ ++int kvm_arch_remove_hw_breakpoint(vaddr addr, vaddr len, int type) ++{ ++ return -ENOSYS; ++} ++ ++void kvm_arch_remove_all_hw_breakpoints(void) ++{ ++} ++ ++static bool kvm_loongarch_handle_debug(CPUState *cs, struct kvm_run *run) ++{ ++ LoongArchCPU *cpu = LOONGARCH_CPU(cs); ++ CPULoongArchState *env = &cpu->env; ++ ++ kvm_cpu_synchronize_state(cs); ++ if (cs->singlestep_enabled) { ++ return true; ++ } ++ ++ if (kvm_find_sw_breakpoint(cs, env->pc)) { ++ return true; ++ } ++ ++ return false; ++} ++ + int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) + { + int ret = 0; +@@ -774,6 +844,13 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) + run->iocsr_io.len, + run->iocsr_io.is_write); + break; ++ ++ case KVM_EXIT_DEBUG: ++ if (kvm_loongarch_handle_debug(cs, run)) { ++ ret = EXCP_DEBUG; ++ } ++ break; ++ + default: + ret = -1; + warn_report("KVM: unknown exit reason %d", run->exit_reason); +-- +1.8.3.1 + diff --git a/0175-hw-intc-loongarch_extioi-Add-virt-extension-support.patch b/0175-hw-intc-loongarch_extioi-Add-virt-extension-support.patch new file mode 100644 index 0000000000000000000000000000000000000000..3ce3fa9f7bf95a2661b52a4720a50ead74d7c929 --- /dev/null +++ b/0175-hw-intc-loongarch_extioi-Add-virt-extension-support.patch @@ -0,0 +1,452 @@ +From c7c10a9c47c6403f9ae30dbd8c0f56d23de2980e Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Mon, 11 Mar 2024 15:01:31 +0800 +Subject: [PATCH 185/293] hw/intc/loongarch_extioi: Add virt extension support + +With hardware extioi, irq can be routed to four vcpus with hardware +extioi. This patch adds virt extension support, sot that irq can +be routed to 256 vcpus. + +Signed-off-by: Song Gao +Signed-off-by: Bibo Mao +--- + hw/intc/loongarch_extioi.c | 98 +++++++++++++++++++++++++++++-- + hw/loongarch/virt.c | 114 +++++++++++++++++++++++++++++++++---- + include/hw/intc/loongarch_extioi.h | 22 ++++++- + include/hw/loongarch/virt.h | 3 + + target/loongarch/cpu.h | 1 + + 5 files changed, 221 insertions(+), 17 deletions(-) + +diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c +index bdfa3b4..a892edc 100644 +--- a/hw/intc/loongarch_extioi.c ++++ b/hw/intc/loongarch_extioi.c +@@ -55,6 +55,11 @@ static void extioi_update_irq(LoongArchExtIOI *s, int irq, int level) + static void extioi_setirq(void *opaque, int irq, int level) + { + LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); ++ ++ if (s->status & BIT(EXTIOI_ENABLE)) { ++ return; ++ } ++ + trace_loongarch_extioi_setirq(irq, level); + if (level) { + /* +@@ -143,10 +148,13 @@ static inline void extioi_update_sw_coremap(LoongArchExtIOI *s, int irq, + + for (i = 0; i < 4; i++) { + cpu = val & 0xff; +- cpu = ctz32(cpu); +- cpu = (cpu >= 4) ? 0 : cpu; + val = val >> 8; + ++ if (!(s->status & BIT(EXTIOI_ENABLE_CPU_ENCODE))) { ++ cpu = ctz32(cpu); ++ cpu = (cpu >= 4) ? 0 : cpu; ++ } ++ + if (s->sw_coremap[irq + i] == cpu) { + continue; + } +@@ -177,8 +185,12 @@ static inline void extioi_update_sw_ipmap(LoongArchExtIOI *s, int index, + val = cpu_to_le64(val); + for (i = 0; i < 4; i++) { + ipnum = val & 0xff; +- ipnum = ctz32(ipnum); +- ipnum = (ipnum >= 4) ? 0 : ipnum; ++ if (s->status & EXTIOI_ENABLE_INT_ENCODE) { ++ ipnum = (ipnum >= 8) ? 0 : ipnum; ++ } else { ++ ipnum = ctz32(ipnum); ++ ipnum = (ipnum >= 4) ? 0 : ipnum; ++ } + s->sw_ipmap[index * 4 + i] = ipnum; + val = val >> 8; + } +@@ -265,6 +277,61 @@ static const MemoryRegionOps extioi_ops = { + .endianness = DEVICE_LITTLE_ENDIAN, + }; + ++static MemTxResult extioi_virt_readw(void *opaque, hwaddr addr, uint64_t *data, ++ unsigned size, MemTxAttrs attrs) ++{ ++ LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); ++ ++ switch (addr) { ++ case EXTIOI_VIRT_FEATURES: ++ *data = s->features; ++ break; ++ case EXTIOI_VIRT_CONFIG: ++ *data = s->status; ++ break; ++ default: ++ break; ++ } ++ ++ return MEMTX_OK; ++} ++ ++static MemTxResult extioi_virt_writew(void *opaque, hwaddr addr, ++ uint64_t val, unsigned size, ++ MemTxAttrs attrs) ++{ ++ LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); ++ ++ switch (addr) { ++ case EXTIOI_VIRT_FEATURES: ++ return MEMTX_ACCESS_ERROR; ++ ++ case EXTIOI_VIRT_CONFIG: ++ /* ++ * extioi features can only be set at disabled status ++ */ ++ if ((s->status & BIT(EXTIOI_ENABLE)) && val) { ++ return MEMTX_ACCESS_ERROR; ++ } ++ ++ s->status = val & s->features; ++ break; ++ default: ++ break; ++ } ++ return MEMTX_OK; ++} ++ ++static const MemoryRegionOps extioi_virt_ops = { ++ .read_with_attrs = extioi_virt_readw, ++ .write_with_attrs = extioi_virt_writew, ++ .impl.min_access_size = 4, ++ .impl.max_access_size = 4, ++ .valid.min_access_size = 4, ++ .valid.max_access_size = 8, ++ .endianness = DEVICE_LITTLE_ENDIAN, ++}; ++ + static void loongarch_extioi_realize(DeviceState *dev, Error **errp) + { + LoongArchExtIOI *s = LOONGARCH_EXTIOI(dev); +@@ -284,6 +351,16 @@ static void loongarch_extioi_realize(DeviceState *dev, Error **errp) + memory_region_init_io(&s->extioi_system_mem, OBJECT(s), &extioi_ops, + s, "extioi_system_mem", 0x900); + sysbus_init_mmio(sbd, &s->extioi_system_mem); ++ ++ if (s->features & BIT(EXTIOI_HAS_VIRT_EXTENSION)) { ++ memory_region_init_io(&s->virt_extend, OBJECT(s), &extioi_virt_ops, ++ s, "extioi_virt", EXTIOI_VIRT_SIZE); ++ sysbus_init_mmio(sbd, &s->virt_extend); ++ s->features |= EXTIOI_VIRT_HAS_FEATURES; ++ } else { ++ s->status |= BIT(EXTIOI_ENABLE); ++ } ++ + s->cpu = g_new0(ExtIOICore, s->num_cpu); + if (s->cpu == NULL) { + error_setg(errp, "Memory allocation for ExtIOICore faile"); +@@ -304,6 +381,14 @@ static void loongarch_extioi_finalize(Object *obj) + g_free(s->cpu); + } + ++static void loongarch_extioi_reset(DeviceState *d) ++{ ++ LoongArchExtIOI *s = LOONGARCH_EXTIOI(d); ++ ++ /* use legacy interrupt routing method by default */ ++ s->status = 0; ++} ++ + static int vmstate_extioi_post_load(void *opaque, int version_id) + { + LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); +@@ -347,12 +432,16 @@ static const VMStateDescription vmstate_loongarch_extioi = { + + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongArchExtIOI, num_cpu, + vmstate_extioi_core, ExtIOICore), ++ VMSTATE_UINT32(features, LoongArchExtIOI), ++ VMSTATE_UINT32(status, LoongArchExtIOI), + VMSTATE_END_OF_LIST() + } + }; + + static Property extioi_properties[] = { + DEFINE_PROP_UINT32("num-cpu", LoongArchExtIOI, num_cpu, 1), ++ DEFINE_PROP_BIT("has-virtualization-extension", LoongArchExtIOI, features, ++ EXTIOI_HAS_VIRT_EXTENSION, 0), + DEFINE_PROP_END_OF_LIST(), + }; + +@@ -361,6 +450,7 @@ static void loongarch_extioi_class_init(ObjectClass *klass, void *data) + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = loongarch_extioi_realize; ++ dc->reset = loongarch_extioi_reset; + device_class_set_props(dc, extioi_properties); + dc->vmsd = &vmstate_loongarch_extioi; + } +diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c +index 6ef40fa..0035964 100644 +--- a/hw/loongarch/virt.c ++++ b/hw/loongarch/virt.c +@@ -15,6 +15,7 @@ + #include "sysemu/runstate.h" + #include "sysemu/reset.h" + #include "sysemu/rtc.h" ++#include "sysemu/kvm.h" + #include "hw/loongarch/virt.h" + #include "exec/address-spaces.h" + #include "hw/irq.h" +@@ -618,9 +619,18 @@ static void loongarch_irq_init(LoongArchMachineState *lams) + /* Create EXTIOI device */ + extioi = qdev_new(TYPE_LOONGARCH_EXTIOI); + qdev_prop_set_uint32(extioi, "num-cpu", ms->smp.cpus); ++ if (lams->v_eiointc) { ++ qdev_prop_set_bit(extioi, "has-virtualization-extension", true); ++ } + sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal); ++ + memory_region_add_subregion(&lams->system_iocsr, APIC_BASE, + sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 0)); ++ if (lams->v_eiointc) { ++ memory_region_add_subregion(&lams->system_iocsr, EXTIOI_VIRT_BASE, ++ sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 1)); ++ } ++ lams->extioi = extioi; + + /* + * connect ext irq to the cpu irq +@@ -780,32 +790,87 @@ static void loongarch_direct_kernel_boot(LoongArchMachineState *lams, + } + } + +-static void loongarch_qemu_write(void *opaque, hwaddr addr, +- uint64_t val, unsigned size) ++static MemTxResult loongarch_qemu_write(void *opaque, hwaddr addr, uint64_t val, ++ unsigned size, MemTxAttrs attrs) + { ++ LoongArchMachineState *lams = LOONGARCH_MACHINE(opaque); ++ uint64_t features; ++ ++ switch (addr) { ++ case MISC_FUNC_REG: ++ if (!lams->v_eiointc) { ++ return MEMTX_OK; ++ } ++ ++ features = address_space_ldl(&lams->as_iocsr, ++ EXTIOI_VIRT_BASE + EXTIOI_VIRT_CONFIG, ++ attrs, NULL); ++ if (val & BIT_ULL(IOCSRM_EXTIOI_EN)) { ++ features |= BIT(EXTIOI_ENABLE); ++ } ++ if (val & BIT_ULL(IOCSRM_EXTIOI_INT_ENCODE)) { ++ features |= BIT(EXTIOI_ENABLE_INT_ENCODE); ++ } ++ ++ address_space_stl(&lams->as_iocsr, ++ EXTIOI_VIRT_BASE + EXTIOI_VIRT_CONFIG, ++ features, attrs, NULL); ++ } ++ ++ return MEMTX_OK; + } + +-static uint64_t loongarch_qemu_read(void *opaque, hwaddr addr, unsigned size) ++static MemTxResult loongarch_qemu_read(void *opaque, hwaddr addr, ++ uint64_t *data, ++ unsigned size, MemTxAttrs attrs) + { ++ LoongArchMachineState *lams = LOONGARCH_MACHINE(opaque); ++ uint64_t ret = 0; ++ int features; ++ + switch (addr) { + case VERSION_REG: +- return 0x11ULL; ++ ret = 0x11ULL; ++ break; + case FEATURE_REG: +- return 1ULL << IOCSRF_MSI | 1ULL << IOCSRF_EXTIOI | +- 1ULL << IOCSRF_CSRIPI; ++ ret = 1ULL << IOCSRF_MSI | 1ULL << IOCSRF_EXTIOI | ++ 1ULL << IOCSRF_CSRIPI; ++ if (kvm_enabled()) { ++ ret |= 1ULL << IOCSRF_VM; ++ } ++ break; + case VENDOR_REG: +- return 0x6e6f73676e6f6f4cULL; /* "Loongson" */ ++ ret = 0x6e6f73676e6f6f4cULL; /* "Loongson" */ ++ break; + case CPUNAME_REG: +- return 0x303030354133ULL; /* "3A5000" */ ++ ret = 0x303030354133ULL; /* "3A5000" */ ++ break; + case MISC_FUNC_REG: +- return 1ULL << IOCSRM_EXTIOI_EN; ++ if (!lams->v_eiointc) { ++ ret |= BIT_ULL(IOCSRM_EXTIOI_EN); ++ break; ++ } ++ ++ features = address_space_ldl(&lams->as_iocsr, ++ EXTIOI_VIRT_BASE + EXTIOI_VIRT_CONFIG, ++ attrs, NULL); ++ if (features & BIT(EXTIOI_ENABLE)) { ++ ret |= BIT_ULL(IOCSRM_EXTIOI_EN); ++ } ++ ++ if (features & BIT(EXTIOI_ENABLE_INT_ENCODE)) { ++ ret |= BIT_ULL(IOCSRM_EXTIOI_INT_ENCODE); ++ } ++ break; + } +- return 0ULL; ++ ++ *data = ret; ++ return MEMTX_OK; + } + + static const MemoryRegionOps loongarch_qemu_ops = { +- .read = loongarch_qemu_read, +- .write = loongarch_qemu_write, ++ .read_with_attrs = loongarch_qemu_read, ++ .write_with_attrs = loongarch_qemu_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, +@@ -1010,6 +1075,11 @@ static void loongarch_machine_initfn(Object *obj) + { + LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); + ++ if (kvm_enabled()) { ++ lams->v_eiointc = true; ++ } else { ++ lams->v_eiointc = false; ++ } + lams->acpi = ON_OFF_AUTO_AUTO; + lams->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6); + lams->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8); +@@ -1163,6 +1233,20 @@ static int64_t virt_get_default_cpu_node_id(const MachineState *ms, int idx) + return nidx; + } + ++static bool virt_get_v_eiointc(Object *obj, Error **errp) ++{ ++ LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); ++ ++ return lams->v_eiointc; ++} ++ ++static void virt_set_v_eiointc(Object *obj, bool value, Error **errp) ++{ ++ LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); ++ ++ lams->v_eiointc = value; ++} ++ + static void loongarch_class_init(ObjectClass *oc, void *data) + { + MachineClass *mc = MACHINE_CLASS(oc); +@@ -1201,6 +1285,12 @@ static void loongarch_class_init(ObjectClass *oc, void *data) + #ifdef CONFIG_TPM + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); + #endif ++ ++ object_class_property_add_bool(oc, "v-eiointc", virt_get_v_eiointc, ++ virt_set_v_eiointc); ++ object_class_property_set_description(oc, "v-eiointc", ++ "Set on/off to enable/disable The virt" ++ "LoongArch Extend I/O Interrupt Controller. "); + } + + static const TypeInfo loongarch_machine_types[] = { +diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h +index a0a46b8..3742fce 100644 +--- a/include/hw/intc/loongarch_extioi.h ++++ b/include/hw/intc/loongarch_extioi.h +@@ -36,10 +36,27 @@ + #define EXTIOI_ISR_START (0x700 - APIC_OFFSET) + #define EXTIOI_ISR_END (0x720 - APIC_OFFSET) + #define EXTIOI_COREISR_START (0x800 - APIC_OFFSET) +-#define EXTIOI_COREISR_END (0xB20 - APIC_OFFSET) ++#define EXTIOI_COREISR_END (0x820 - APIC_OFFSET) + #define EXTIOI_COREMAP_START (0xC00 - APIC_OFFSET) + #define EXTIOI_COREMAP_END (0xD00 - APIC_OFFSET) + ++#define EXTIOI_VIRT_BASE (0x40000000) ++#define EXTIOI_VIRT_SIZE (0x1000) ++#define EXTIOI_VIRT_FEATURES (0x0) ++#define EXTIOI_HAS_VIRT_EXTENSION (0) ++#define EXTIOI_HAS_ENABLE_OPTION (1) ++#define EXTIOI_HAS_INT_ENCODE (2) ++#define EXTIOI_HAS_CPU_ENCODE (3) ++#define EXTIOI_VIRT_HAS_FEATURES (BIT(EXTIOI_HAS_VIRT_EXTENSION) \ ++ | BIT(EXTIOI_HAS_INT_ENCODE) \ ++ | BIT(EXTIOI_HAS_CPU_ENCODE)) ++#define EXTIOI_VIRT_CONFIG (0x4) ++#define EXTIOI_ENABLE (1) ++#define EXTIOI_ENABLE_INT_ENCODE (2) ++#define EXTIOI_ENABLE_CPU_ENCODE (3) ++#define EXTIOI_VIRT_COREMAP_START (0x40) ++#define EXTIOI_VIRT_COREMAP_END (0x240) ++ + typedef struct ExtIOICore { + uint32_t coreisr[EXTIOI_IRQS_GROUP_COUNT]; + DECLARE_BITMAP(sw_isr[LS3A_INTC_IP], EXTIOI_IRQS); +@@ -51,6 +68,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(LoongArchExtIOI, LOONGARCH_EXTIOI) + struct LoongArchExtIOI { + SysBusDevice parent_obj; + uint32_t num_cpu; ++ uint32_t features; ++ uint32_t status; + /* hardware state */ + uint32_t nodetype[EXTIOI_IRQS_NODETYPE_COUNT / 2]; + uint32_t bounce[EXTIOI_IRQS_GROUP_COUNT]; +@@ -64,5 +83,6 @@ struct LoongArchExtIOI { + qemu_irq irq[EXTIOI_IRQS]; + ExtIOICore *cpu; + MemoryRegion extioi_system_mem; ++ MemoryRegion virt_extend; + }; + #endif /* LOONGARCH_EXTIOI_H */ +diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h +index 252f7df..d48aa57 100644 +--- a/include/hw/loongarch/virt.h ++++ b/include/hw/loongarch/virt.h +@@ -40,6 +40,7 @@ struct LoongArchMachineState { + MemoryRegion highmem; + MemoryRegion bios; + bool bios_loaded; ++ bool v_eiointc; + /* State for other subsystems/APIs: */ + FWCfgState *fw_cfg; + Notifier machine_done; +@@ -50,11 +51,13 @@ struct LoongArchMachineState { + DeviceState *acpi_ged; + int fdt_size; + DeviceState *platform_bus_dev; ++ DeviceState *extioi; + PCIBus *pci_bus; + PFlashCFI01 *flash[2]; + MemoryRegion system_iocsr; + MemoryRegion iocsr_mem; + AddressSpace as_iocsr; ++ int features; + }; + + #define TYPE_LOONGARCH_MACHINE MACHINE_TYPE_NAME("virt") +diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h +index 4aba8ab..4749d41 100644 +--- a/target/loongarch/cpu.h ++++ b/target/loongarch/cpu.h +@@ -36,6 +36,7 @@ + #define CPUNAME_REG 0x20 + #define MISC_FUNC_REG 0x420 + #define IOCSRM_EXTIOI_EN 48 ++#define IOCSRM_EXTIOI_INT_ENCODE 49 + + #define IOCSR_MEM_SIZE 0x428 + +-- +1.8.3.1 + diff --git a/0176-target-loongarch-kvm-sync-kernel-header-files.patch b/0176-target-loongarch-kvm-sync-kernel-header-files.patch new file mode 100644 index 0000000000000000000000000000000000000000..a8a3e031f523e28185b065a31fecd652bd288095 --- /dev/null +++ b/0176-target-loongarch-kvm-sync-kernel-header-files.patch @@ -0,0 +1,41 @@ +From 7e11828f4012fd0472184923a6b73b0f9eec534b Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Wed, 13 Mar 2024 10:04:33 +0800 +Subject: [PATCH 186/293] target/loongarch/kvm: sync kernel header files + +sync kernel header files. + +Signed-off-by: Bibo Mao +--- + linux-headers/asm-loongarch/kvm.h | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/linux-headers/asm-loongarch/kvm.h b/linux-headers/asm-loongarch/kvm.h +index 923d0bd..4cec8c1 100644 +--- a/linux-headers/asm-loongarch/kvm.h ++++ b/linux-headers/asm-loongarch/kvm.h +@@ -15,10 +15,12 @@ + */ + + #define __KVM_HAVE_READONLY_MEM ++#define __KVM_HAVE_GUEST_DEBUG + + #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 + #define KVM_DIRTY_LOG_PAGE_OFFSET 64 + ++#define KVM_GUESTDBG_USE_SW_BP 0x00010000 + /* + * for KVM_GET_REGS and KVM_SET_REGS + */ +@@ -74,6 +76,8 @@ struct kvm_fpu { + + #define KVM_REG_LOONGARCH_COUNTER (KVM_REG_LOONGARCH_KVM | KVM_REG_SIZE_U64 | 1) + #define KVM_REG_LOONGARCH_VCPU_RESET (KVM_REG_LOONGARCH_KVM | KVM_REG_SIZE_U64 | 2) ++/* Debugging: Special instruction for software breakpoint */ ++#define KVM_REG_LOONGARCH_DEBUG_INST (KVM_REG_LOONGARCH_KVM | KVM_REG_SIZE_U64 | 3) + + #define LOONGARCH_REG_SHIFT 3 + #define LOONGARCH_REG_64(TYPE, REG) (TYPE | KVM_REG_SIZE_U64 | (REG << LOONGARCH_REG_SHIFT)) +-- +1.8.3.1 + diff --git a/0177-hw-intc-loongarch_extioi-Add-virt-extension-support-.patch b/0177-hw-intc-loongarch_extioi-Add-virt-extension-support-.patch new file mode 100644 index 0000000000000000000000000000000000000000..c24944b2369064fb8b251368c56b0a7d06aae9aa --- /dev/null +++ b/0177-hw-intc-loongarch_extioi-Add-virt-extension-support-.patch @@ -0,0 +1,257 @@ +From c652cc1eb711edf00b7448c0eb8177bab80f0bea Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Wed, 13 Mar 2024 17:46:19 +0800 +Subject: [PATCH 187/293] hw/intc/loongarch_extioi: Add virt extension support + v2 + +fix 954c513fa3846f34ecd81e4eb25457ed49362b39 +"Add virt extension support" in v2 version. + +Signed-off-by: Bibo Mao +--- + hw/intc/loongarch_extioi.c | 24 +++++--------- + hw/loongarch/virt.c | 64 +++++++++++++++++++++----------------- + include/hw/intc/loongarch_extioi.h | 3 +- + include/hw/loongarch/virt.h | 2 +- + 4 files changed, 47 insertions(+), 46 deletions(-) + +diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c +index a892edc..fa23e24 100644 +--- a/hw/intc/loongarch_extioi.c ++++ b/hw/intc/loongarch_extioi.c +@@ -55,11 +55,6 @@ static void extioi_update_irq(LoongArchExtIOI *s, int irq, int level) + static void extioi_setirq(void *opaque, int irq, int level) + { + LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); +- +- if (s->status & BIT(EXTIOI_ENABLE)) { +- return; +- } +- + trace_loongarch_extioi_setirq(irq, level); + if (level) { + /* +@@ -148,18 +143,17 @@ static inline void extioi_update_sw_coremap(LoongArchExtIOI *s, int irq, + + for (i = 0; i < 4; i++) { + cpu = val & 0xff; +- val = val >> 8; +- +- if (!(s->status & BIT(EXTIOI_ENABLE_CPU_ENCODE))) { ++ if (!(s->status & BIT(EXTIOI_ENABLE_CPU_ENCODE))) { + cpu = ctz32(cpu); + cpu = (cpu >= 4) ? 0 : cpu; + } ++ val = val >> 8; + + if (s->sw_coremap[irq + i] == cpu) { + continue; + } + +- if (notify && test_bit(irq, (unsigned long *)s->isr)) { ++ if (notify && test_bit(irq + i, (unsigned long *)s->isr)) { + /* + * lower irq at old cpu and raise irq at new cpu + */ +@@ -185,12 +179,8 @@ static inline void extioi_update_sw_ipmap(LoongArchExtIOI *s, int index, + val = cpu_to_le64(val); + for (i = 0; i < 4; i++) { + ipnum = val & 0xff; +- if (s->status & EXTIOI_ENABLE_INT_ENCODE) { +- ipnum = (ipnum >= 8) ? 0 : ipnum; +- } else { +- ipnum = ctz32(ipnum); +- ipnum = (ipnum >= 4) ? 0 : ipnum; +- } ++ ipnum = ctz32(ipnum); ++ ipnum = (ipnum >= 4) ? 0 : ipnum; + s->sw_ipmap[index * 4 + i] = ipnum; + val = val >> 8; + } +@@ -386,7 +376,9 @@ static void loongarch_extioi_reset(DeviceState *d) + LoongArchExtIOI *s = LOONGARCH_EXTIOI(d); + + /* use legacy interrupt routing method by default */ +- s->status = 0; ++ if (s->features & BIT(EXTIOI_HAS_VIRT_EXTENSION)) { ++ s->status = 0; ++ } + } + + static int vmstate_extioi_post_load(void *opaque, int version_id) +diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c +index 0035964..01e59f3 100644 +--- a/hw/loongarch/virt.c ++++ b/hw/loongarch/virt.c +@@ -15,6 +15,7 @@ + #include "sysemu/runstate.h" + #include "sysemu/reset.h" + #include "sysemu/rtc.h" ++#include "sysemu/tcg.h" + #include "sysemu/kvm.h" + #include "hw/loongarch/virt.h" + #include "exec/address-spaces.h" +@@ -55,6 +56,31 @@ struct loaderparams { + const char *initrd_filename; + }; + ++static bool virt_is_veiointc_enabled(LoongArchMachineState *lams) ++{ ++ if (lams->veiointc == ON_OFF_AUTO_OFF) { ++ return false; ++ } ++ return true; ++} ++ ++static void virt_get_veiointc(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); ++ OnOffAuto veiointc = lams->veiointc; ++ ++ visit_type_OnOffAuto(v, name, &veiointc, errp); ++} ++ ++static void virt_set_veiointc(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); ++ ++ visit_type_OnOffAuto(v, name, &lams->veiointc, errp); ++} ++ + static PFlashCFI01 *virt_flash_create1(LoongArchMachineState *lams, + const char *name, + const char *alias_prop_name) +@@ -619,14 +645,14 @@ static void loongarch_irq_init(LoongArchMachineState *lams) + /* Create EXTIOI device */ + extioi = qdev_new(TYPE_LOONGARCH_EXTIOI); + qdev_prop_set_uint32(extioi, "num-cpu", ms->smp.cpus); +- if (lams->v_eiointc) { ++ if (virt_is_veiointc_enabled(lams)) { + qdev_prop_set_bit(extioi, "has-virtualization-extension", true); + } + sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal); + + memory_region_add_subregion(&lams->system_iocsr, APIC_BASE, + sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 0)); +- if (lams->v_eiointc) { ++ if (virt_is_veiointc_enabled(lams)) { + memory_region_add_subregion(&lams->system_iocsr, EXTIOI_VIRT_BASE, + sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 1)); + } +@@ -798,7 +824,7 @@ static MemTxResult loongarch_qemu_write(void *opaque, hwaddr addr, uint64_t val, + + switch (addr) { + case MISC_FUNC_REG: +- if (!lams->v_eiointc) { ++ if (!virt_is_veiointc_enabled(lams)) { + return MEMTX_OK; + } + +@@ -846,7 +872,7 @@ static MemTxResult loongarch_qemu_read(void *opaque, hwaddr addr, + ret = 0x303030354133ULL; /* "3A5000" */ + break; + case MISC_FUNC_REG: +- if (!lams->v_eiointc) { ++ if (!virt_is_veiointc_enabled(lams)) { + ret |= BIT_ULL(IOCSRM_EXTIOI_EN); + break; + } +@@ -1075,10 +1101,8 @@ static void loongarch_machine_initfn(Object *obj) + { + LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); + +- if (kvm_enabled()) { +- lams->v_eiointc = true; +- } else { +- lams->v_eiointc = false; ++ if (tcg_enabled()) { ++ lams->veiointc = ON_OFF_AUTO_OFF; + } + lams->acpi = ON_OFF_AUTO_AUTO; + lams->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6); +@@ -1233,20 +1257,6 @@ static int64_t virt_get_default_cpu_node_id(const MachineState *ms, int idx) + return nidx; + } + +-static bool virt_get_v_eiointc(Object *obj, Error **errp) +-{ +- LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); +- +- return lams->v_eiointc; +-} +- +-static void virt_set_v_eiointc(Object *obj, bool value, Error **errp) +-{ +- LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); +- +- lams->v_eiointc = value; +-} +- + static void loongarch_class_init(ObjectClass *oc, void *data) + { + MachineClass *mc = MACHINE_CLASS(oc); +@@ -1281,16 +1291,14 @@ static void loongarch_class_init(ObjectClass *oc, void *data) + NULL, NULL); + object_class_property_set_description(oc, "acpi", + "Enable ACPI"); ++ object_class_property_add(oc, "v-eiointc", "OnOffAuto", ++ virt_get_veiointc, virt_set_veiointc, NULL, NULL); ++ object_class_property_set_description(oc, "v-eiointc", ++ "Enable Virt Extend I/O Interrupt Controller"); + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); + #ifdef CONFIG_TPM + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); + #endif +- +- object_class_property_add_bool(oc, "v-eiointc", virt_get_v_eiointc, +- virt_set_v_eiointc); +- object_class_property_set_description(oc, "v-eiointc", +- "Set on/off to enable/disable The virt" +- "LoongArch Extend I/O Interrupt Controller. "); + } + + static const TypeInfo loongarch_machine_types[] = { +diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h +index 3742fce..1cf5bf9 100644 +--- a/include/hw/intc/loongarch_extioi.h ++++ b/include/hw/intc/loongarch_extioi.h +@@ -36,7 +36,7 @@ + #define EXTIOI_ISR_START (0x700 - APIC_OFFSET) + #define EXTIOI_ISR_END (0x720 - APIC_OFFSET) + #define EXTIOI_COREISR_START (0x800 - APIC_OFFSET) +-#define EXTIOI_COREISR_END (0x820 - APIC_OFFSET) ++#define EXTIOI_COREISR_END (0xB20 - APIC_OFFSET) + #define EXTIOI_COREMAP_START (0xC00 - APIC_OFFSET) + #define EXTIOI_COREMAP_END (0xD00 - APIC_OFFSET) + +@@ -48,6 +48,7 @@ + #define EXTIOI_HAS_INT_ENCODE (2) + #define EXTIOI_HAS_CPU_ENCODE (3) + #define EXTIOI_VIRT_HAS_FEATURES (BIT(EXTIOI_HAS_VIRT_EXTENSION) \ ++ | BIT(EXTIOI_HAS_ENABLE_OPTION)\ + | BIT(EXTIOI_HAS_INT_ENCODE) \ + | BIT(EXTIOI_HAS_CPU_ENCODE)) + #define EXTIOI_VIRT_CONFIG (0x4) +diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h +index d48aa57..99447fd 100644 +--- a/include/hw/loongarch/virt.h ++++ b/include/hw/loongarch/virt.h +@@ -40,12 +40,12 @@ struct LoongArchMachineState { + MemoryRegion highmem; + MemoryRegion bios; + bool bios_loaded; +- bool v_eiointc; + /* State for other subsystems/APIs: */ + FWCfgState *fw_cfg; + Notifier machine_done; + Notifier powerdown_notifier; + OnOffAuto acpi; ++ OnOffAuto veiointc; + char *oem_id; + char *oem_table_id; + DeviceState *acpi_ged; +-- +1.8.3.1 + diff --git a/0178-target-loongarch-kvm-Implement-LoongArch-PMU-extensi.patch b/0178-target-loongarch-kvm-Implement-LoongArch-PMU-extensi.patch new file mode 100644 index 0000000000000000000000000000000000000000..d6273e9412c30a5fd07713657915f3a6bdf9b545 --- /dev/null +++ b/0178-target-loongarch-kvm-Implement-LoongArch-PMU-extensi.patch @@ -0,0 +1,230 @@ +From cd979dcfd9148e29e3603cf1473c1c27c98a10ee Mon Sep 17 00:00:00 2001 +From: Song Gao +Date: Fri, 14 Jun 2024 09:38:48 +0800 +Subject: [PATCH 188/293] target/loongarch/kvm: Implement LoongArch PMU + extension. + +Implement PMU extension for LoongArch kvm mode. Use OnOffAuto type +variable pmu to check the PMU feature. If the PMU Feature is not supported +with KVM host, it reports error if there is pmu=on command line. + +If there is no any command line about pmu parameter, it checks whether +KVM host supports the PMU Feature and set the corresponding value in cpucfg. + +Signed-off-by: Song Gao +Message-Id: <20240614013848.53474-1-gaosong@loongson.cn> +--- + linux-headers/asm-loongarch/kvm.h | 3 ++ + target/loongarch/cpu.c | 64 +++++++++++++++++++++++++++++++---- + target/loongarch/cpu.h | 6 ++++ + target/loongarch/kvm/kvm.c | 16 +++++++++ + target/loongarch/kvm/kvm_loongarch.h | 16 +++++++++ + target/loongarch/loongarch-qmp-cmds.c | 2 +- + 6 files changed, 99 insertions(+), 8 deletions(-) + +diff --git a/linux-headers/asm-loongarch/kvm.h b/linux-headers/asm-loongarch/kvm.h +index 4cec8c1..b40b640 100644 +--- a/linux-headers/asm-loongarch/kvm.h ++++ b/linux-headers/asm-loongarch/kvm.h +@@ -85,6 +85,9 @@ struct kvm_fpu { + #define KVM_IOC_CPUCFG(REG) LOONGARCH_REG_64(KVM_REG_LOONGARCH_CPUCFG, REG) + #define KVM_LOONGARCH_VCPU_CPUCFG 0 + ++#define KVM_LOONGARCH_VM_FEAT_CTRL 1000 ++#define KVM_LOONGARCH_VM_FEAT_PMU 1000 ++ + struct kvm_debug_exit_arch { + }; + +diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c +index b098b1c..17d9448 100644 +--- a/target/loongarch/cpu.c ++++ b/target/loongarch/cpu.c +@@ -582,6 +582,35 @@ static void loongarch_cpu_disas_set_info(CPUState *s, disassemble_info *info) + info->print_insn = print_insn_loongarch; + } + ++static void loongarch_cpu_check_pmu(CPUState *cs, Error **errp) ++{ ++ LoongArchCPU *cpu = LOONGARCH_CPU(cs); ++ bool kvm_supported; ++ ++ kvm_supported = kvm_feature_supported(cs, LOONGARCH_FEATURE_PMU); ++ if (cpu->pmu == ON_OFF_AUTO_ON) { ++ if (kvm_supported) { ++ cpu->env.cpucfg[6] = FIELD_DP32(cpu->env.cpucfg[6], CPUCFG6, PMP, 1); ++ cpu->env.cpucfg[6] = FIELD_DP32(cpu->env.cpucfg[6], CPUCFG6, PMNUM, 3); ++ cpu->env.cpucfg[6] = FIELD_DP32(cpu->env.cpucfg[6], CPUCFG6, PMBITS, 63); ++ cpu->env.cpucfg[6] = FIELD_DP32(cpu->env.cpucfg[6], CPUCFG6, UPM, 1); ++ } else { ++ error_setg(errp, "'pmu' feature not supported by KVM on this host."); ++ return; ++ } ++ } else if ((cpu->pmu == ON_OFF_AUTO_AUTO) && kvm_supported) { ++ cpu->env.cpucfg[6] = FIELD_DP32(cpu->env.cpucfg[6], CPUCFG6, PMP, 1); ++ cpu->env.cpucfg[6] = FIELD_DP32(cpu->env.cpucfg[6], CPUCFG6, PMNUM, 3); ++ cpu->env.cpucfg[6] = FIELD_DP32(cpu->env.cpucfg[6], CPUCFG6, PMBITS, 63); ++ cpu->env.cpucfg[6] = FIELD_DP32(cpu->env.cpucfg[6], CPUCFG6, UPM, 1); ++ } ++} ++ ++static void loongarch_cpu_feature_realize(CPUState *cs, Error **errp) ++{ ++ loongarch_cpu_check_pmu(cs, errp); ++} ++ + static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) + { + CPUState *cs = CPU(dev); +@@ -595,6 +624,11 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) + } + + loongarch_cpu_register_gdb_regs_for_features(cs); ++ loongarch_cpu_feature_realize(cs, &local_err); ++ if (local_err != NULL) { ++ error_propagate(errp, local_err); ++ return; ++ } + + cpu_reset(cs); + qemu_init_vcpu(cs); +@@ -654,17 +688,33 @@ static void loongarch_set_lasx(Object *obj, bool value, Error **errp) + } + } + ++static bool loongarch_get_pmu(Object *obj, Error **errp) ++{ ++ return LOONGARCH_CPU(obj)->pmu != ON_OFF_AUTO_OFF; ++} ++ ++static void loongarch_set_pmu(Object *obj, bool value, Error **errp) ++{ ++ LoongArchCPU *cpu = LOONGARCH_CPU(obj); ++ ++ cpu->pmu = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; ++} ++ + void loongarch_cpu_post_init(Object *obj) + { + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + +- if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) { +- object_property_add_bool(obj, "lsx", loongarch_get_lsx, +- loongarch_set_lsx); +- } +- if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LASX)) { +- object_property_add_bool(obj, "lasx", loongarch_get_lasx, +- loongarch_set_lasx); ++ object_property_add_bool(obj, "lsx", loongarch_get_lsx, ++ loongarch_set_lsx); ++ object_property_add_bool(obj, "lasx", loongarch_get_lasx, ++ loongarch_set_lasx); ++ ++ if (kvm_enabled()) { ++ cpu->pmu = ON_OFF_AUTO_AUTO; ++ object_property_add_bool(obj, "pmu", loongarch_get_pmu, ++ loongarch_set_pmu); ++ } else { ++ cpu->pmu = ON_OFF_AUTO_OFF; + } + } + +diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h +index 4749d41..f07ed49 100644 +--- a/target/loongarch/cpu.h ++++ b/target/loongarch/cpu.h +@@ -18,6 +18,7 @@ + #endif + #include "cpu-csr.h" + #include "cpu-qom.h" ++#include "qapi/qapi-types-common.h" + + #define IOCSRF_TEMP 0 + #define IOCSRF_NODECNT 1 +@@ -281,6 +282,10 @@ struct LoongArchTLB { + }; + typedef struct LoongArchTLB LoongArchTLB; + ++enum loongarch_features { ++ LOONGARCH_FEATURE_PMU, ++}; ++ + typedef struct CPUArchState { + uint64_t gpr[32]; + uint64_t pc; +@@ -377,6 +382,7 @@ struct ArchCPU { + CPULoongArchState env; + QEMUTimer timer; + uint32_t phy_id; ++ OnOffAuto pmu; + + /* 'compatible' string for this CPU for Linux device trees */ + const char *dtb_compatible; +diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c +index 49d0207..afcd917 100644 +--- a/target/loongarch/kvm/kvm.c ++++ b/target/loongarch/kvm/kvm.c +@@ -874,6 +874,22 @@ int kvm_loongarch_set_interrupt(LoongArchCPU *cpu, int irq, int level) + return kvm_vcpu_ioctl(cs, KVM_INTERRUPT, &intr); + } + ++bool kvm_feature_supported(CPUState *cs, enum loongarch_features feature) ++{ ++ struct kvm_device_attr attr; ++ int ret; ++ ++ switch (feature) { ++ case LOONGARCH_FEATURE_PMU: ++ attr.group = KVM_LOONGARCH_VM_FEAT_CTRL; ++ attr.attr = KVM_LOONGARCH_VM_FEAT_PMU; ++ ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); ++ return (ret == 0); ++ default: ++ return false; ++ } ++} ++ + void kvm_arch_accel_class_init(ObjectClass *oc) + { + } +diff --git a/target/loongarch/kvm/kvm_loongarch.h b/target/loongarch/kvm/kvm_loongarch.h +index d945b6b..bdb4f18 100644 +--- a/target/loongarch/kvm/kvm_loongarch.h ++++ b/target/loongarch/kvm/kvm_loongarch.h +@@ -13,4 +13,20 @@ + int kvm_loongarch_set_interrupt(LoongArchCPU *cpu, int irq, int level); + void kvm_arch_reset_vcpu(CPULoongArchState *env); + ++#ifdef CONFIG_KVM ++/* ++ * kvm_feature_supported: ++ * ++ * Returns: true if KVM supports specified feature ++ * and false otherwise. ++ */ ++bool kvm_feature_supported(CPUState *cs, enum loongarch_features feature); ++#else ++static inline bool kvm_feature_supported(CPUState *cs, ++ enum loongarch_features feature) ++{ ++ return false; ++} ++#endif ++ + #endif +diff --git a/target/loongarch/loongarch-qmp-cmds.c b/target/loongarch/loongarch-qmp-cmds.c +index 645672f..de92ec6 100644 +--- a/target/loongarch/loongarch-qmp-cmds.c ++++ b/target/loongarch/loongarch-qmp-cmds.c +@@ -42,7 +42,7 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) + } + + static const char *cpu_model_advertised_features[] = { +- "lsx", "lasx", NULL ++ "lsx", "lasx", "pmu", NULL + }; + + CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, +-- +1.8.3.1 + diff --git a/0179-target-loongarch-Fix-qemu-system-loongarch64-assert-.patch b/0179-target-loongarch-Fix-qemu-system-loongarch64-assert-.patch new file mode 100644 index 0000000000000000000000000000000000000000..75df19c95abfbed3c52d6cfda612fbb85e0645b5 --- /dev/null +++ b/0179-target-loongarch-Fix-qemu-system-loongarch64-assert-.patch @@ -0,0 +1,131 @@ +From fdb37262586c8838d7f7351dd824a48cd82dca64 Mon Sep 17 00:00:00 2001 +From: Song Gao +Date: Tue, 2 Apr 2024 09:39:36 +0800 +Subject: [PATCH 189/293] target/loongarch: Fix qemu-system-loongarch64 assert + failed with the option '-d int' +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +qemu-system-loongarch64 assert failed with the option '-d int', +the helper_idle() raise an exception EXCP_HLT, but the exception name is undefined. + +Signed-off-by: Song Gao +Reviewed-by: Philippe Mathieu-Daudé +Message-Id: <20240321123606.1704900-1-gaosong@loongson.cn> +--- + target/loongarch/cpu.c | 74 +++++++++++++++++++++++++++----------------------- + 1 file changed, 40 insertions(+), 34 deletions(-) + +diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c +index 17d9448..c223712 100644 +--- a/target/loongarch/cpu.c ++++ b/target/loongarch/cpu.c +@@ -43,33 +43,45 @@ const char * const fregnames[32] = { + "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", + }; + +-static const char * const excp_names[] = { +- [EXCCODE_INT] = "Interrupt", +- [EXCCODE_PIL] = "Page invalid exception for load", +- [EXCCODE_PIS] = "Page invalid exception for store", +- [EXCCODE_PIF] = "Page invalid exception for fetch", +- [EXCCODE_PME] = "Page modified exception", +- [EXCCODE_PNR] = "Page Not Readable exception", +- [EXCCODE_PNX] = "Page Not Executable exception", +- [EXCCODE_PPI] = "Page Privilege error", +- [EXCCODE_ADEF] = "Address error for instruction fetch", +- [EXCCODE_ADEM] = "Address error for Memory access", +- [EXCCODE_SYS] = "Syscall", +- [EXCCODE_BRK] = "Break", +- [EXCCODE_INE] = "Instruction Non-Existent", +- [EXCCODE_IPE] = "Instruction privilege error", +- [EXCCODE_FPD] = "Floating Point Disabled", +- [EXCCODE_FPE] = "Floating Point Exception", +- [EXCCODE_DBP] = "Debug breakpoint", +- [EXCCODE_BCE] = "Bound Check Exception", +- [EXCCODE_SXD] = "128 bit vector instructions Disable exception", +- [EXCCODE_ASXD] = "256 bit vector instructions Disable exception", ++struct TypeExcp { ++ int32_t exccode; ++ const char * const name; ++}; ++ ++static const struct TypeExcp excp_names[] = { ++ {EXCCODE_INT, "Interrupt"}, ++ {EXCCODE_PIL, "Page invalid exception for load"}, ++ {EXCCODE_PIS, "Page invalid exception for store"}, ++ {EXCCODE_PIF, "Page invalid exception for fetch"}, ++ {EXCCODE_PME, "Page modified exception"}, ++ {EXCCODE_PNR, "Page Not Readable exception"}, ++ {EXCCODE_PNX, "Page Not Executable exception"}, ++ {EXCCODE_PPI, "Page Privilege error"}, ++ {EXCCODE_ADEF, "Address error for instruction fetch"}, ++ {EXCCODE_ADEM, "Address error for Memory access"}, ++ {EXCCODE_SYS, "Syscall"}, ++ {EXCCODE_BRK, "Break"}, ++ {EXCCODE_INE, "Instruction Non-Existent"}, ++ {EXCCODE_IPE, "Instruction privilege error"}, ++ {EXCCODE_FPD, "Floating Point Disabled"}, ++ {EXCCODE_FPE, "Floating Point Exception"}, ++ {EXCCODE_DBP, "Debug breakpoint"}, ++ {EXCCODE_BCE, "Bound Check Exception"}, ++ {EXCCODE_SXD, "128 bit vector instructions Disable exception"}, ++ {EXCCODE_ASXD, "256 bit vector instructions Disable exception"}, ++ {EXCP_HLT, "EXCP_HLT"}, + }; + + const char *loongarch_exception_name(int32_t exception) + { +- assert(excp_names[exception]); +- return excp_names[exception]; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(excp_names); i++) { ++ if (excp_names[i].exccode == exception) { ++ return excp_names[i].name; ++ } ++ } ++ return "Unknown"; + } + + void G_NORETURN do_raise_exception(CPULoongArchState *env, +@@ -78,7 +90,7 @@ void G_NORETURN do_raise_exception(CPULoongArchState *env, + { + CPUState *cs = env_cpu(env); + +- qemu_log_mask(CPU_LOG_INT, "%s: %d (%s)\n", ++ qemu_log_mask(CPU_LOG_INT, "%s: expection: %d (%s)\n", + __func__, + exception, + loongarch_exception_name(exception)); +@@ -159,22 +171,16 @@ static void loongarch_cpu_do_interrupt(CPUState *cs) + CPULoongArchState *env = &cpu->env; + bool update_badinstr = 1; + int cause = -1; +- const char *name; + bool tlbfill = FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR); + uint32_t vec_size = FIELD_EX64(env->CSR_ECFG, CSR_ECFG, VS); + + if (cs->exception_index != EXCCODE_INT) { +- if (cs->exception_index < 0 || +- cs->exception_index >= ARRAY_SIZE(excp_names)) { +- name = "unknown"; +- } else { +- name = excp_names[cs->exception_index]; +- } +- + qemu_log_mask(CPU_LOG_INT, + "%s enter: pc " TARGET_FMT_lx " ERA " TARGET_FMT_lx +- " TLBRERA " TARGET_FMT_lx " %s exception\n", __func__, +- env->pc, env->CSR_ERA, env->CSR_TLBRERA, name); ++ " TLBRERA " TARGET_FMT_lx " exception: %d (%s)\n", ++ __func__, env->pc, env->CSR_ERA, env->CSR_TLBRERA, ++ cs->exception_index, ++ loongarch_exception_name(cs->exception_index)); + } + + switch (cs->exception_index) { +-- +1.8.3.1 + diff --git a/0180-target-loongarch-kvm-Fix-VM-recovery-from-disk-failu.patch b/0180-target-loongarch-kvm-Fix-VM-recovery-from-disk-failu.patch new file mode 100644 index 0000000000000000000000000000000000000000..6ff6f1b8a1c7ea32584e4a985d9a88f320199425 --- /dev/null +++ b/0180-target-loongarch-kvm-Fix-VM-recovery-from-disk-failu.patch @@ -0,0 +1,43 @@ +From aedd1399a605898177af4bc4675450c9ada23296 Mon Sep 17 00:00:00 2001 +From: Song Gao +Date: Wed, 8 May 2024 10:47:32 +0800 +Subject: [PATCH 190/293] target/loongarch/kvm: Fix VM recovery from disk + failures + +vmstate does not save kvm_state_conter, +which can cause VM recovery from disk to fail. + +Signed-off-by: Song Gao +Acked-by: Peter Xu +Message-Id: <20240508024732.3127792-1-gaosong@loongson.cn> +--- + target/loongarch/machine.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/target/loongarch/machine.c b/target/loongarch/machine.c +index 1c4e01d..5a7df71 100644 +--- a/target/loongarch/machine.c ++++ b/target/loongarch/machine.c +@@ -125,8 +125,8 @@ const VMStateDescription vmstate_tlb = { + /* LoongArch CPU state */ + const VMStateDescription vmstate_loongarch_cpu = { + .name = "cpu", +- .version_id = 1, +- .minimum_version_id = 1, ++ .version_id = 2, ++ .minimum_version_id = 2, + .fields = (VMStateField[]) { + VMSTATE_UINTTL_ARRAY(env.gpr, LoongArchCPU, 32), + VMSTATE_UINTTL(env.pc, LoongArchCPU), +@@ -191,6 +191,8 @@ const VMStateDescription vmstate_loongarch_cpu = { + VMSTATE_STRUCT_ARRAY(env.tlb, LoongArchCPU, LOONGARCH_TLB_MAX, + 0, vmstate_tlb, LoongArchTLB), + ++ VMSTATE_UINT64(kvm_state_counter, LoongArchCPU), ++ + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription*[]) { +-- +1.8.3.1 + diff --git a/0181-target-loongarch-kvm-fpu-save-the-vreg-registers-hig.patch b/0181-target-loongarch-kvm-fpu-save-the-vreg-registers-hig.patch new file mode 100644 index 0000000000000000000000000000000000000000..7d3555fa2fd5b95e5020f83ae3008d67f90f5f0a --- /dev/null +++ b/0181-target-loongarch-kvm-fpu-save-the-vreg-registers-hig.patch @@ -0,0 +1,43 @@ +From 30220a25e28fd7faa80825fcdcafecdc3128a88f Mon Sep 17 00:00:00 2001 +From: Song Gao +Date: Tue, 14 May 2024 19:07:52 +0800 +Subject: [PATCH 191/293] target/loongarch/kvm: fpu save the vreg registers + high 192bit + +On kvm side, get_fpu/set_fpu save the vreg registers high 192bits, +but QEMU missing. + +Signed-off-by: Song Gao +Reviewed-by: Bibo Mao +Message-Id: <20240514110752.989572-1-gaosong@loongson.cn> +--- + target/loongarch/kvm/kvm.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c +index afcd917..3306cb5 100644 +--- a/target/loongarch/kvm/kvm.c ++++ b/target/loongarch/kvm/kvm.c +@@ -444,6 +444,9 @@ static int kvm_loongarch_get_regs_fp(CPUState *cs) + env->fcsr0 = fpu.fcsr; + for (i = 0; i < 32; i++) { + env->fpr[i].vreg.UD[0] = fpu.fpr[i].val64[0]; ++ env->fpr[i].vreg.UD[1] = fpu.fpr[i].val64[1]; ++ env->fpr[i].vreg.UD[2] = fpu.fpr[i].val64[2]; ++ env->fpr[i].vreg.UD[3] = fpu.fpr[i].val64[3]; + } + for (i = 0; i < 8; i++) { + env->cf[i] = fpu.fcc & 0xFF; +@@ -465,6 +468,9 @@ static int kvm_loongarch_put_regs_fp(CPUState *cs) + fpu.fcc = 0; + for (i = 0; i < 32; i++) { + fpu.fpr[i].val64[0] = env->fpr[i].vreg.UD[0]; ++ fpu.fpr[i].val64[1] = env->fpr[i].vreg.UD[1]; ++ fpu.fpr[i].val64[2] = env->fpr[i].vreg.UD[2]; ++ fpu.fpr[i].val64[3] = env->fpr[i].vreg.UD[3]; + } + + for (i = 0; i < 8; i++) { +-- +1.8.3.1 + diff --git a/0182-target-i386-add-support-for-LAM-in-CPUID-enumeration.patch b/0182-target-i386-add-support-for-LAM-in-CPUID-enumeration.patch new file mode 100644 index 0000000000000000000000000000000000000000..81d71de690b64a5de9ac60c5dde8572c6653c168 --- /dev/null +++ b/0182-target-i386-add-support-for-LAM-in-CPUID-enumeration.patch @@ -0,0 +1,69 @@ +From 420c8697531bf3ab2bd799c62a4a072b4c8668fe Mon Sep 17 00:00:00 2001 +From: Robert Hoo +Date: Fri, 12 Jan 2024 14:00:41 +0800 +Subject: [PATCH 192/293] target/i386: add support for LAM in CPUID enumeration + +commit ba6780905943696d790cc880c8e5684b51f027fe upstream. + +Linear Address Masking (LAM) is a new Intel CPU feature, which allows +software to use of the untranslated address bits for metadata. + +The bit definition: +CPUID.(EAX=7,ECX=1):EAX[26] + +Add CPUID definition for LAM. + +Note LAM feature is not supported for TCG of target-i386, LAM CPIUD bit +will not be added to TCG_7_1_EAX_FEATURES. + +More info can be found in Intel ISE Chapter "LINEAR ADDRESS MASKING(LAM)" +https://cdrdv2.intel.com/v1/dl/getContent/671368 + +Intel-SIG: commit ba6780905943 target/i386: add support for LAM in CPUID +enumeration +Backport Qemu Linear Address Masking (LAM) support. + +Signed-off-by: Robert Hoo +Co-developed-by: Binbin Wu +Signed-off-by: Binbin Wu +Tested-by: Xuelian Guo +Reviewed-by: Xiaoyao Li +Reviewed-by: Zhao Liu +Message-ID: <20240112060042.19925-2-binbin.wu@linux.intel.com> +Signed-off-by: Paolo Bonzini +[ Zhiquan Li: amend commit log ] +Signed-off-by: Zhiquan Li +--- + target/i386/cpu.c | 2 +- + target/i386/cpu.h | 2 ++ + 2 files changed, 3 insertions(+), 1 deletion(-) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 5c0dfee..b031d5b 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -967,7 +967,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { + "fsrc", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, "amx-fp16", NULL, "avx-ifma", +- NULL, NULL, NULL, NULL, ++ NULL, NULL, "lam", NULL, + NULL, NULL, NULL, NULL, + }, + .cpuid = { +diff --git a/target/i386/cpu.h b/target/i386/cpu.h +index 705d925..d361f83 100644 +--- a/target/i386/cpu.h ++++ b/target/i386/cpu.h +@@ -925,6 +925,8 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, + #define CPUID_7_1_EAX_AMX_FP16 (1U << 21) + /* Support for VPMADD52[H,L]UQ */ + #define CPUID_7_1_EAX_AVX_IFMA (1U << 23) ++/* Linear Address Masking */ ++#define CPUID_7_1_EAX_LAM (1U << 26) + + /* Support for VPDPB[SU,UU,SS]D[,S] */ + #define CPUID_7_1_EDX_AVX_VNNI_INT8 (1U << 4) +-- +1.8.3.1 + diff --git a/0183-target-i386-add-control-bits-support-for-LAM.patch b/0183-target-i386-add-control-bits-support-for-LAM.patch new file mode 100644 index 0000000000000000000000000000000000000000..6c1d0cf6c3228e2b27d1315025dcc9119abc744e --- /dev/null +++ b/0183-target-i386-add-control-bits-support-for-LAM.patch @@ -0,0 +1,99 @@ +From aabf8b96bfbacd88b0fbe833078acf248be69b1d Mon Sep 17 00:00:00 2001 +From: Binbin Wu +Date: Fri, 12 Jan 2024 14:00:42 +0800 +Subject: [PATCH 193/293] target/i386: add control bits support for LAM + +commit 0117067131f99acaab4f4d2cca0290c5510e37cf upstream. + +LAM uses CR3[61] and CR3[62] to configure/enable LAM on user pointers. +LAM uses CR4[28] to configure/enable LAM on supervisor pointers. + +For CR3 LAM bits, no additional handling needed: +- TCG + LAM is not supported for TCG of target-i386. helper_write_crN() and + helper_vmrun() check max physical address bits before calling + cpu_x86_update_cr3(), no change needed, i.e. CR3 LAM bits are not allowed + to be set in TCG. +- gdbstub + x86_cpu_gdb_write_register() will call cpu_x86_update_cr3() to update cr3. + Allow gdb to set the LAM bit(s) to CR3, if vcpu doesn't support LAM, + KVM_SET_SREGS will fail as other reserved bits. + +For CR4 LAM bit, its reservation depends on vcpu supporting LAM feature or +not. +- TCG + LAM is not supported for TCG of target-i386. helper_write_crN() and + helper_vmrun() check CR4 reserved bit before calling cpu_x86_update_cr4(), + i.e. CR4 LAM bit is not allowed to be set in TCG. +- gdbstub + x86_cpu_gdb_write_register() will call cpu_x86_update_cr4() to update cr4. + Mask out LAM bit on CR4 if vcpu doesn't support LAM. +- x86_cpu_reset_hold() doesn't need special handling. + +Intel-SIG: commit 0117067131f9 target/i386: add control bits support for +LAM +Backport Qemu Linear Address Masking (LAM) support. + +Signed-off-by: Binbin Wu +Tested-by: Xuelian Guo +Reviewed-by: Xiaoyao Li +Reviewed-by: Zhao Liu +Message-ID: <20240112060042.19925-3-binbin.wu@linux.intel.com> +Signed-off-by: Paolo Bonzini +[ Zhiquan Li: amend commit log ] +Signed-off-by: Zhiquan Li +--- + target/i386/cpu.h | 7 ++++++- + target/i386/helper.c | 4 ++++ + 2 files changed, 10 insertions(+), 1 deletion(-) + +diff --git a/target/i386/cpu.h b/target/i386/cpu.h +index d361f83..2e166a7 100644 +--- a/target/i386/cpu.h ++++ b/target/i386/cpu.h +@@ -261,6 +261,7 @@ typedef enum X86Seg { + #define CR4_SMAP_MASK (1U << 21) + #define CR4_PKE_MASK (1U << 22) + #define CR4_PKS_MASK (1U << 24) ++#define CR4_LAM_SUP_MASK (1U << 28) + + #define CR4_RESERVED_MASK \ + (~(target_ulong)(CR4_VME_MASK | CR4_PVI_MASK | CR4_TSD_MASK \ +@@ -269,7 +270,8 @@ typedef enum X86Seg { + | CR4_OSFXSR_MASK | CR4_OSXMMEXCPT_MASK | CR4_UMIP_MASK \ + | CR4_LA57_MASK \ + | CR4_FSGSBASE_MASK | CR4_PCIDE_MASK | CR4_OSXSAVE_MASK \ +- | CR4_SMEP_MASK | CR4_SMAP_MASK | CR4_PKE_MASK | CR4_PKS_MASK)) ++ | CR4_SMEP_MASK | CR4_SMAP_MASK | CR4_PKE_MASK | CR4_PKS_MASK \ ++ | CR4_LAM_SUP_MASK)) + + #define DR6_BD (1 << 13) + #define DR6_BS (1 << 14) +@@ -2527,6 +2529,9 @@ static inline uint64_t cr4_reserved_bits(CPUX86State *env) + if (!(env->features[FEAT_7_0_ECX] & CPUID_7_0_ECX_PKS)) { + reserved_bits |= CR4_PKS_MASK; + } ++ if (!(env->features[FEAT_7_1_EAX] & CPUID_7_1_EAX_LAM)) { ++ reserved_bits |= CR4_LAM_SUP_MASK; ++ } + return reserved_bits; + } + +diff --git a/target/i386/helper.c b/target/i386/helper.c +index 2070dd0..1da7a7d 100644 +--- a/target/i386/helper.c ++++ b/target/i386/helper.c +@@ -219,6 +219,10 @@ void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4) + new_cr4 &= ~CR4_PKS_MASK; + } + ++ if (!(env->features[FEAT_7_1_EAX] & CPUID_7_1_EAX_LAM)) { ++ new_cr4 &= ~CR4_LAM_SUP_MASK; ++ } ++ + env->cr[4] = new_cr4; + env->hflags = hflags; + +-- +1.8.3.1 + diff --git a/0184-target-i386-Introduce-Icelake-Server-v7-to-enable-TS.patch b/0184-target-i386-Introduce-Icelake-Server-v7-to-enable-TS.patch new file mode 100644 index 0000000000000000000000000000000000000000..e3b565c98393009556165c43f0d08164bd3a95c9 --- /dev/null +++ b/0184-target-i386-Introduce-Icelake-Server-v7-to-enable-TS.patch @@ -0,0 +1,65 @@ +From 80a505605d6bf3d27e224650394b4465ca7f15af Mon Sep 17 00:00:00 2001 +From: Zhenzhong Duan +Date: Wed, 20 Mar 2024 17:31:38 +0800 +Subject: [PATCH 194/293] target/i386: Introduce Icelake-Server-v7 to enable + TSX + +commit c895fa54e3060c5ac6f3888dce96c9b78626072b upstream. + +When start L2 guest with both L1/L2 using Icelake-Server-v3 or above, +QEMU reports below warning: + +"warning: host doesn't support requested feature: MSR(10AH).taa-no [bit 8]" + +Reason is QEMU Icelake-Server-v3 has TSX feature disabled but enables taa-no +bit. It's meaningless that TSX isn't supported but still claim TSX is secure. +So L1 KVM doesn't expose taa-no to L2 if TSX is unsupported, then starting L2 +triggers the warning. + +Fix it by introducing a new version Icelake-Server-v7 which has both TSX +and taa-no features. Then guest can use TSX securely when it see taa-no. + +This matches the production Icelake which supports TSX and isn't susceptible +to TSX Async Abort (TAA) vulnerabilities, a.k.a, taa-no. + +Ideally, TSX should have being enabled together with taa-no since v3, but for +compatibility, we'd better to add v7 to enable it. + +Fixes: d965dc35592d ("target/i386: Add ARCH_CAPABILITIES related bits into Icelake-Server CPU model") + +Intel-SIG: commit c895fa54e306 target/i386: Introduce Icelake-Server-v7 to enable TSX. + +Tested-by: Xiangfei Ma +Signed-off-by: Zhenzhong Duan +Message-ID: <20240320093138.80267-2-zhenzhong.duan@intel.com> +Signed-off-by: Paolo Bonzini +[ Quanxian Wang: amend commit log ] +Signed-off-by: Quanxian Wang +--- + target/i386/cpu.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index b031d5b..ffa2dae 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -3822,6 +3822,16 @@ static const X86CPUDefinition builtin_x86_defs[] = { + { /* end of list */ } + }, + }, ++ { ++ .version = 7, ++ .note = "TSX, taa-no", ++ .props = (PropValue[]) { ++ /* Restore TSX features removed by -v2 above */ ++ { "hle", "on" }, ++ { "rtm", "on" }, ++ { /* end of list */ } ++ }, ++ }, + { /* end of list */ } + } + }, +-- +1.8.3.1 + diff --git a/0185-target-i386-Introduce-SapphireRapids-v3-to-add-missi.patch b/0185-target-i386-Introduce-SapphireRapids-v3-to-add-missi.patch new file mode 100644 index 0000000000000000000000000000000000000000..1ea8fb6c8aa82016fdfbcb08380405086483be19 --- /dev/null +++ b/0185-target-i386-Introduce-SapphireRapids-v3-to-add-missi.patch @@ -0,0 +1,47 @@ +From f7cccf45b1de006f68f22fb0c9062324119504ed Mon Sep 17 00:00:00 2001 +From: Lei Wang +Date: Wed, 24 Apr 2024 03:29:12 -0400 +Subject: [PATCH 195/293] target/i386: Introduce SapphireRapids-v3 to add + missing features + +commit b10b2481738304db13d28252e86c10555121a5b3 upstream. + +Add the missing features(ss, tsc-adjust, cldemote, movdiri, movdir64b) in +the SapphireRapids-v3 CPU model. + +Intel-SIG: commit b10b24817383 target/i386: Introduce SapphireRapids-v3 to add missing features + +Signed-off-by: Lei Wang +Message-ID: <20240424072912.43188-1-lei4.wang@intel.com> +Signed-off-by: Paolo Bonzini +[jz: amend commit log] +Signed-off-by: Jason Zeng +--- + target/i386/cpu.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index ffa2dae..f764e82 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -3970,6 +3970,17 @@ static const X86CPUDefinition builtin_x86_defs[] = { + { /* end of list */ } + } + }, ++ { ++ .version = 3, ++ .props = (PropValue[]) { ++ { "ss", "on" }, ++ { "tsc-adjust", "on" }, ++ { "cldemote", "on" }, ++ { "movdiri", "on" }, ++ { "movdir64b", "on" }, ++ { /* end of list */ } ++ } ++ }, + { /* end of list */ } + } + }, +-- +1.8.3.1 + diff --git a/0186-target-i386-add-guest-phys-bits-cpu-property.patch b/0186-target-i386-add-guest-phys-bits-cpu-property.patch new file mode 100644 index 0000000000000000000000000000000000000000..e9dcbf778979d2275307703ee7049f2ac9d1263c --- /dev/null +++ b/0186-target-i386-add-guest-phys-bits-cpu-property.patch @@ -0,0 +1,106 @@ +From 882f1ee12dd7e54cbe5116d3eba72f43f1a49631 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Mon, 18 Mar 2024 16:53:36 +0100 +Subject: [PATCH 196/293] target/i386: add guest-phys-bits cpu property + +commit 513ba32dccc659c80722b3a43233b26eaa50309a upstream. + +Allows to set guest-phys-bits (cpuid leaf 80000008, eax[23:16]) +via -cpu $model,guest-phys-bits=$nr. + +Intel-SIG: commit 513ba32dccc6 target/i386: add guest-phys-bits cpu property + +Signed-off-by: Gerd Hoffmann +Message-ID: <20240318155336.156197-3-kraxel@redhat.com> +Reviewed-by: Zhao Liu +Signed-off-by: Paolo Bonzini +[jz: compatible property for 9.0 machines not included] +Signed-off-by: Jason Zeng +--- + target/i386/cpu.c | 22 ++++++++++++++++++++++ + target/i386/cpu.h | 8 ++++++++ + 2 files changed, 30 insertions(+) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index f764e82..ffdaf16 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -6713,6 +6713,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, + if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) { + /* 64 bit processor */ + *eax |= (cpu_x86_virtual_addr_width(env) << 8); ++ *eax |= (cpu->guest_phys_bits << 16); + } + *ebx = env->features[FEAT_8000_0008_EBX]; + if (cs->nr_cores * cs->nr_threads > 1) { +@@ -7467,6 +7468,14 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) + goto out; + } + ++ if (cpu->guest_phys_bits == -1) { ++ /* ++ * If it was not set by the user, or by the accelerator via ++ * cpu_exec_realizefn, clear. ++ */ ++ cpu->guest_phys_bits = 0; ++ } ++ + if (cpu->ucode_rev == 0) { + /* + * The default is the same as KVM's. Note that this check +@@ -7517,6 +7526,14 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) + if (cpu->phys_bits == 0) { + cpu->phys_bits = TCG_PHYS_ADDR_BITS; + } ++ if (cpu->guest_phys_bits && ++ (cpu->guest_phys_bits > cpu->phys_bits || ++ cpu->guest_phys_bits < 32)) { ++ error_setg(errp, "guest-phys-bits should be between 32 and %u " ++ " (but is %u)", ++ cpu->phys_bits, cpu->guest_phys_bits); ++ return; ++ } + } else { + /* For 32 bit systems don't use the user set value, but keep + * phys_bits consistent with what we tell the guest. +@@ -7525,6 +7542,10 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) + error_setg(errp, "phys-bits is not user-configurable in 32 bit"); + return; + } ++ if (cpu->guest_phys_bits != 0) { ++ error_setg(errp, "guest-phys-bits is not user-configurable in 32 bit"); ++ return; ++ } + + if (env->features[FEAT_1_EDX] & (CPUID_PSE36 | CPUID_PAE)) { + cpu->phys_bits = 36; +@@ -8013,6 +8034,7 @@ static Property x86_cpu_properties[] = { + DEFINE_PROP_BOOL("x-force-features", X86CPU, force_features, false), + DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true), + DEFINE_PROP_UINT32("phys-bits", X86CPU, phys_bits, 0), ++ DEFINE_PROP_UINT32("guest-phys-bits", X86CPU, guest_phys_bits, -1), + DEFINE_PROP_BOOL("host-phys-bits", X86CPU, host_phys_bits, false), + DEFINE_PROP_UINT8("host-phys-bits-limit", X86CPU, host_phys_bits_limit, 0), + DEFINE_PROP_BOOL("fill-mtrr-mask", X86CPU, fill_mtrr_mask, true), +diff --git a/target/i386/cpu.h b/target/i386/cpu.h +index 2e166a7..877fc2b 100644 +--- a/target/i386/cpu.h ++++ b/target/i386/cpu.h +@@ -2023,6 +2023,14 @@ struct ArchCPU { + /* Number of physical address bits supported */ + uint32_t phys_bits; + ++ /* ++ * Number of guest physical address bits available. Usually this is ++ * identical to host physical address bits. With NPT or EPT 4-level ++ * paging, guest physical address space might be restricted to 48 bits ++ * even if the host cpu supports more physical address bits. ++ */ ++ uint32_t guest_phys_bits; ++ + /* in order to simplify APIC support, we leave this pointer to the + user */ + struct DeviceState *apic_state; +-- +1.8.3.1 + diff --git a/0187-kvm-add-support-for-guest-physical-bits.patch b/0187-kvm-add-support-for-guest-physical-bits.patch new file mode 100644 index 0000000000000000000000000000000000000000..29a78423eed1b3ce47517a237898736a9af95c28 --- /dev/null +++ b/0187-kvm-add-support-for-guest-physical-bits.patch @@ -0,0 +1,112 @@ +From aebdb4506a35e9ebfd7a86ed8b86466772285100 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Mon, 18 Mar 2024 16:53:35 +0100 +Subject: [PATCH 197/293] kvm: add support for guest physical bits + +commit 0d08c423688edcca857f88dab20f1fc56de2b281 upstream. + +Query kvm for supported guest physical address bits, in cpuid +function 80000008, eax[23:16]. Usually this is identical to host +physical address bits. With NPT or EPT being used this might be +restricted to 48 (max 4-level paging address space size) even if +the host cpu supports more physical address bits. + +When set pass this to the guest, using cpuid too. Guest firmware +can use this to figure how big the usable guest physical address +space is, so PCI bar mapping are actually reachable. + +Intel-SIG: commit 0d08c423688e kvm: add support for guest physical bits + +Signed-off-by: Gerd Hoffmann +Reviewed-by: Xiaoyao Li +Reviewed-by: Zhao Liu +Message-ID: <20240318155336.156197-2-kraxel@redhat.com> +Signed-off-by: Paolo Bonzini +[jz: amend commit log] +Signed-off-by: Jason Zeng +--- + target/i386/kvm/kvm-cpu.c | 50 +++++++++++++++++++++++++++++++++++++++-------- + 1 file changed, 42 insertions(+), 8 deletions(-) + +diff --git a/target/i386/kvm/kvm-cpu.c b/target/i386/kvm/kvm-cpu.c +index 9c791b7..f76972e 100644 +--- a/target/i386/kvm/kvm-cpu.c ++++ b/target/i386/kvm/kvm-cpu.c +@@ -18,10 +18,32 @@ + #include "kvm_i386.h" + #include "hw/core/accel-cpu.h" + ++static void kvm_set_guest_phys_bits(CPUState *cs) ++{ ++ X86CPU *cpu = X86_CPU(cs); ++ uint32_t eax, guest_phys_bits; ++ ++ eax = kvm_arch_get_supported_cpuid(cs->kvm_state, 0x80000008, 0, R_EAX); ++ guest_phys_bits = (eax >> 16) & 0xff; ++ if (!guest_phys_bits) { ++ return; ++ } ++ cpu->guest_phys_bits = guest_phys_bits; ++ if (cpu->guest_phys_bits > cpu->phys_bits) { ++ cpu->guest_phys_bits = cpu->phys_bits; ++ } ++ ++ if (cpu->host_phys_bits && cpu->host_phys_bits_limit && ++ cpu->guest_phys_bits > cpu->host_phys_bits_limit) { ++ cpu->guest_phys_bits = cpu->host_phys_bits_limit; ++ } ++} ++ + static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) + { + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; ++ bool ret; + + /* + * The realize order is important, since x86_cpu_realize() checks if +@@ -32,13 +54,15 @@ static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) + * + * realize order: + * +- * x86_cpu_realize(): +- * -> x86_cpu_expand_features() +- * -> cpu_exec_realizefn(): +- * -> accel_cpu_common_realize() +- * kvm_cpu_realizefn() -> host_cpu_realizefn() +- * -> cpu_common_realizefn() +- * -> check/update ucode_rev, phys_bits, mwait ++ * x86_cpu_realizefn(): ++ * x86_cpu_expand_features() ++ * cpu_exec_realizefn(): ++ * 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) + */ + if (cpu->max_features) { + if (enable_cpu_pm && kvm_has_waitpkg()) { +@@ -50,7 +74,17 @@ static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) + MSR_IA32_UCODE_REV); + } + } +- return host_cpu_realizefn(cs, errp); ++ ret = host_cpu_realizefn(cs, errp); ++ if (!ret) { ++ return ret; ++ } ++ ++ if ((env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) && ++ cpu->guest_phys_bits == -1) { ++ kvm_set_guest_phys_bits(cs); ++ } ++ ++ return true; + } + + static bool lmce_supported(void) +-- +1.8.3.1 + diff --git a/0188-support-vpsp.patch b/0188-support-vpsp.patch new file mode 100644 index 0000000000000000000000000000000000000000..63aa59878d43d4535092d82f042aecc5d01544f3 --- /dev/null +++ b/0188-support-vpsp.patch @@ -0,0 +1,191 @@ +From 5c688679ed8afe0082e2770439a6e10fff04ee75 Mon Sep 17 00:00:00 2001 +From: xiongmengbiao +Date: Thu, 30 Nov 2023 13:47:21 +0800 +Subject: [PATCH 198/293] support vpsp + +simulate a psp misc device for support tkm's key isolation + +Change-Id: I4d9fb5a8722e90a62c52eb97069c613834ced63f +Signed-off-by: xiongmengbiao +--- + hw/misc/Kconfig | 4 ++ + hw/misc/meson.build | 1 + + hw/misc/psp.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 146 insertions(+) + create mode 100644 hw/misc/psp.c + +diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig +index cc8a8c1..2ea5c68 100644 +--- a/hw/misc/Kconfig ++++ b/hw/misc/Kconfig +@@ -200,4 +200,8 @@ config IOSB + config XLNX_VERSAL_TRNG + bool + ++config PSP_DEV ++ bool ++ default y ++ + source macio/Kconfig +diff --git a/hw/misc/meson.build b/hw/misc/meson.build +index 36c20d5..28cba0a 100644 +--- a/hw/misc/meson.build ++++ b/hw/misc/meson.build +@@ -9,6 +9,7 @@ system_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c')) + system_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c')) + system_ss.add(when: 'CONFIG_LED', if_true: files('led.c')) + system_ss.add(when: 'CONFIG_PVPANIC_COMMON', if_true: files('pvpanic.c')) ++system_ss.add(when: 'CONFIG_PSP_DEV', if_true: files('psp.c')) + + # ARM devices + system_ss.add(when: 'CONFIG_PL310', if_true: files('arm_l2x0.c')) +diff --git a/hw/misc/psp.c b/hw/misc/psp.c +new file mode 100644 +index 0000000..da0a69e +--- /dev/null ++++ b/hw/misc/psp.c +@@ -0,0 +1,141 @@ ++/* ++ * hygon psp device emulation ++ * ++ * Copyright 2024 HYGON Corp. ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or (at ++ * your option) any later version. See the COPYING file in the top-level ++ * directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/compiler.h" ++#include "qemu/error-report.h" ++#include "qapi/error.h" ++#include "migration/vmstate.h" ++#include "hw/qdev-properties.h" ++#include "sysemu/runstate.h" ++#include ++ ++#define TYPE_PSP_DEV "psp" ++OBJECT_DECLARE_SIMPLE_TYPE(PSPDevState, PSP_DEV) ++ ++struct PSPDevState { ++ /* Private */ ++ DeviceState pdev; ++ ++ /* Public */ ++ Notifier shutdown_notifier; ++ int dev_fd; ++ uint8_t enabled; ++ ++ /** ++ * vid is used to identify a virtual machine in qemu. ++ * When a virtual machine accesses a tkm key, ++ * the TKM module uses different key spaces based on different vids. ++ */ ++ uint32_t vid; ++}; ++ ++#define PSP_DEV_PATH "/dev/hygon_psp_config" ++#define HYGON_PSP_IOC_TYPE 'H' ++#define PSP_IOC_MUTEX_ENABLE _IOWR(HYGON_PSP_IOC_TYPE, 1, NULL) ++#define PSP_IOC_MUTEX_DISABLE _IOWR(HYGON_PSP_IOC_TYPE, 2, NULL) ++#define PSP_IOC_VPSP_OPT _IOWR(HYGON_PSP_IOC_TYPE, 3, NULL) ++ ++enum VPSP_DEV_CTRL_OPCODE { ++ VPSP_OP_VID_ADD, ++ VPSP_OP_VID_DEL, ++}; ++ ++struct psp_dev_ctrl { ++ unsigned char op; ++ union { ++ unsigned int vid; ++ unsigned char reserved[128]; ++ } data; ++}; ++ ++static void psp_dev_destroy(PSPDevState *state) ++{ ++ struct psp_dev_ctrl ctrl = { 0 }; ++ if (state && state->dev_fd >= 0) { ++ if (state->enabled) { ++ ctrl.op = VPSP_OP_VID_DEL; ++ if (ioctl(state->dev_fd, PSP_IOC_VPSP_OPT, &ctrl) < 0) { ++ error_report("VPSP_OP_VID_DEL: %d", -errno); ++ } else { ++ state->enabled = false; ++ } ++ } ++ qemu_close(state->dev_fd); ++ state->dev_fd = -1; ++ } ++} ++ ++/** ++ * Guest OS performs shut down operations through 'shutdown' and 'powerdown' event. ++ * The 'powerdown' event will also trigger 'shutdown' in the end, ++ * so only attention to the 'shutdown' event. ++ * ++ * When Guest OS trigger 'reboot' or 'reset' event, to do nothing. ++*/ ++static void psp_dev_shutdown_notify(Notifier *notifier, void *data) ++{ ++ PSPDevState *state = container_of(notifier, PSPDevState, shutdown_notifier); ++ psp_dev_destroy(state); ++} ++ ++static void psp_dev_realize(DeviceState *dev, Error **errp) ++{ ++ struct psp_dev_ctrl ctrl = { 0 }; ++ PSPDevState *state = PSP_DEV(dev); ++ ++ state->dev_fd = qemu_open_old(PSP_DEV_PATH, O_RDWR); ++ if (state->dev_fd < 0) { ++ error_setg(errp, "fail to open %s, errno %d.", PSP_DEV_PATH, errno); ++ goto end; ++ } ++ ++ ctrl.op = VPSP_OP_VID_ADD; ++ ctrl.data.vid = state->vid; ++ if (ioctl(state->dev_fd, PSP_IOC_VPSP_OPT, &ctrl) < 0) { ++ error_setg(errp, "psp_dev_realize VPSP_OP_VID_ADD vid %d, return %d", ctrl.data.vid, -errno); ++ goto end; ++ } ++ ++ state->enabled = true; ++ state->shutdown_notifier.notify = psp_dev_shutdown_notify; ++ qemu_register_shutdown_notifier(&state->shutdown_notifier); ++end: ++ return; ++} ++ ++static struct Property psp_dev_properties[] = { ++ DEFINE_PROP_UINT32("vid", PSPDevState, vid, 0), ++ DEFINE_PROP_END_OF_LIST(), ++}; ++ ++static void psp_dev_class_init(ObjectClass *klass, void *data) ++{ ++ DeviceClass *dc = DEVICE_CLASS(klass); ++ ++ dc->desc = "PSP Device"; ++ dc->realize = psp_dev_realize; ++ set_bit(DEVICE_CATEGORY_MISC, dc->categories); ++ device_class_set_props(dc, psp_dev_properties); ++} ++ ++static const TypeInfo psp_dev_info = { ++ .name = TYPE_PSP_DEV, ++ .parent = TYPE_DEVICE, ++ .instance_size = sizeof(PSPDevState), ++ .class_init = psp_dev_class_init, ++}; ++ ++static void psp_dev_register_types(void) ++{ ++ type_register_static(&psp_dev_info); ++} ++ ++type_init(psp_dev_register_types) +-- +1.8.3.1 + diff --git a/0189-doc-update-AMD-SEV-to-include-Live-migration-flow.patch b/0189-doc-update-AMD-SEV-to-include-Live-migration-flow.patch new file mode 100644 index 0000000000000000000000000000000000000000..abfef166f95abe4fcd582a771b0ece3ffddc6943 --- /dev/null +++ b/0189-doc-update-AMD-SEV-to-include-Live-migration-flow.patch @@ -0,0 +1,69 @@ +From ff30577f3ebef6b2e57c31909e0a87fa96f43a46 Mon Sep 17 00:00:00 2001 +From: Brijesh Singh +Date: Thu, 7 May 2020 22:26:17 +0000 +Subject: [PATCH 199/293] doc: update AMD SEV to include Live migration flow + +cherry-picked from https://github.com/AMDESE/qemu/commit/0e2b3d80e3. + +Reviewed-by: Dr. David Alan Gilbert +Signed-off-by: Brijesh Singh +Signed-off-by: Ashish Kalra +Signed-off-by: hanliyang +--- + docs/system/i386/amd-memory-encryption.rst | 40 +++++++++++++++++++++++++++++- + 1 file changed, 39 insertions(+), 1 deletion(-) + +diff --git a/docs/system/i386/amd-memory-encryption.rst b/docs/system/i386/amd-memory-encryption.rst +index e9bc142..b7e3f46 100644 +--- a/docs/system/i386/amd-memory-encryption.rst ++++ b/docs/system/i386/amd-memory-encryption.rst +@@ -177,7 +177,45 @@ TODO + Live Migration + --------------- + +-TODO ++AMD SEV encrypts the memory of VMs and because a different key is used ++in each VM, the hypervisor will be unable to simply copy the ++ciphertext from one VM to another to migrate the VM. Instead the AMD SEV Key ++Management API provides sets of function which the hypervisor can use ++to package a guest page for migration, while maintaining the confidentiality ++provided by AMD SEV. ++ ++SEV guest VMs have the concept of private and shared memory. The private ++memory is encrypted with the guest-specific key, while shared memory may ++be encrypted with the hypervisor key. The migration APIs provided by the ++SEV API spec should be used for migrating the private pages. The ++KVM_GET_PAGE_ENC_BITMAP ioctl can be used to get the guest page encryption ++bitmap. The bitmap can be used to check if the given guest page is ++private or shared. ++ ++Before initiating the migration, we need to know the targets machine's public ++Diffie-Hellman key (PDH) and certificate chain. It can be retrieved ++with the 'query-sev-capabilities' QMP command or using the sev-tool. The ++migrate-set-parameter can be used to pass the target machine's PDH and ++certificate chain. ++ ++During the migration flow, the SEND_START is called on the source hypervisor ++to create an outgoing encryption context. The SEV guest policy dictates whether ++the certificate passed through the migrate-sev-set-info command will be ++validated. SEND_UPDATE_DATA is called to encrypt the guest private pages. ++After migration is completed, SEND_FINISH is called to destroy the encryption ++context and make the VM non-runnable to protect it against cloning. ++ ++On the target machine, RECEIVE_START is called first to create an ++incoming encryption context. The RECEIVE_UPDATE_DATA is called to copy ++the received encrypted page into guest memory. After migration has ++completed, RECEIVE_FINISH is called to make the VM runnable. ++ ++For more information about the migration see SEV API Appendix A ++Usage flow (Live migration section). ++ ++NOTE: ++To protect against the memory clone SEV APIs are designed to make the VM ++unrunnable in case of the migration failure. + + References + ---------- +-- +1.8.3.1 + diff --git a/0190-migration.json-add-AMD-SEV-specific-migration-parame.patch b/0190-migration.json-add-AMD-SEV-specific-migration-parame.patch new file mode 100644 index 0000000000000000000000000000000000000000..4ca77fceab90aad88722d5ca77484156c434e56d --- /dev/null +++ b/0190-migration.json-add-AMD-SEV-specific-migration-parame.patch @@ -0,0 +1,266 @@ +From 0499615720dea35526bd7ef18449240f8cdfe630 Mon Sep 17 00:00:00 2001 +From: Brijesh Singh +Date: Tue, 27 Jul 2021 11:27:00 +0000 +Subject: [PATCH 200/293] migration.json: add AMD SEV specific migration + parameters + +cherry-picked from https://github.com/AMDESE/qemu/commit/d6a23bde6b6e. + +AMD SEV migration flow requires that target machine's public Diffie-Hellman +key (PDH) and certificate chain must be passed before initiating the guest +migration. User can use QMP 'migrate-set-parameters' to pass the certificate +chain. The certificate chain will be used while creating the outgoing +encryption context. + +Signed-off-by: Brijesh Singh +Signed-off-by: Ashish Kalra +[ Fix conflicts and qapi errors. ] +Signed-off-by: hanliyang +--- + migration/migration-hmp-cmds.c | 28 ++++++++++++++++++++ + migration/options.c | 60 ++++++++++++++++++++++++++++++++++++++++++ + qapi/migration.json | 41 ++++++++++++++++++++++++++--- + 3 files changed, 126 insertions(+), 3 deletions(-) + +diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c +index 2faa5ca..58da696 100644 +--- a/migration/migration-hmp-cmds.c ++++ b/migration/migration-hmp-cmds.c +@@ -392,6 +392,19 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) + monitor_printf(mon, "%s: %s\n", + MigrationParameter_str(MIGRATION_PARAMETER_MODE), + qapi_enum_lookup(&MigMode_lookup, params->mode)); ++ ++ assert(params->sev_pdh); ++ monitor_printf(mon, "%s: %s\n", ++ MigrationParameter_str(MIGRATION_PARAMETER_SEV_PDH), ++ params->sev_pdh); ++ assert(params->sev_plat_cert); ++ monitor_printf(mon, "%s: %s\n", ++ MigrationParameter_str(MIGRATION_PARAMETER_SEV_PLAT_CERT), ++ params->sev_plat_cert); ++ assert(params->sev_amd_cert); ++ monitor_printf(mon, "%s: %s\n", ++ MigrationParameter_str(MIGRATION_PARAMETER_SEV_AMD_CERT), ++ params->sev_amd_cert); + } + + qapi_free_MigrationParameters(params); +@@ -679,6 +692,21 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) + p->has_mode = true; + visit_type_MigMode(v, param, &p->mode, &err); + break; ++ case MIGRATION_PARAMETER_SEV_PDH: ++ p->sev_pdh = g_new0(StrOrNull, 1); ++ p->sev_pdh->type = QTYPE_QSTRING; ++ visit_type_str(v, param, &p->sev_pdh->u.s, &err); ++ break; ++ case MIGRATION_PARAMETER_SEV_PLAT_CERT: ++ p->sev_plat_cert = g_new0(StrOrNull, 1); ++ p->sev_plat_cert->type = QTYPE_QSTRING; ++ visit_type_str(v, param, &p->sev_plat_cert->u.s, &err); ++ break; ++ case MIGRATION_PARAMETER_SEV_AMD_CERT: ++ p->sev_amd_cert = g_new0(StrOrNull, 1); ++ p->sev_amd_cert->type = QTYPE_QSTRING; ++ visit_type_str(v, param, &p->sev_amd_cert->u.s, &err); ++ break; + default: + assert(0); + } +diff --git a/migration/options.c b/migration/options.c +index 8d8ec73..70f6beb 100644 +--- a/migration/options.c ++++ b/migration/options.c +@@ -179,6 +179,9 @@ Property migration_properties[] = { + DEFINE_PROP_MIG_MODE("mode", MigrationState, + parameters.mode, + MIG_MODE_NORMAL), ++ DEFINE_PROP_STRING("sev-pdh", MigrationState, parameters.sev_pdh), ++ DEFINE_PROP_STRING("sev-plat-cert", MigrationState, parameters.sev_plat_cert), ++ DEFINE_PROP_STRING("sev-amd-cert", MigrationState, parameters.sev_amd_cert), + + /* Migration capabilities */ + DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE), +@@ -997,6 +1000,9 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) + params->announce_rounds = s->parameters.announce_rounds; + params->has_announce_step = true; + params->announce_step = s->parameters.announce_step; ++ params->sev_pdh = g_strdup(s->parameters.sev_pdh); ++ params->sev_plat_cert = g_strdup(s->parameters.sev_plat_cert); ++ params->sev_amd_cert = g_strdup(s->parameters.sev_amd_cert); + + if (s->parameters.has_block_bitmap_mapping) { + params->has_block_bitmap_mapping = true; +@@ -1047,6 +1053,10 @@ void migrate_params_init(MigrationParameters *params) + params->has_x_vcpu_dirty_limit_period = true; + params->has_vcpu_dirty_limit = true; + params->has_mode = true; ++ ++ params->sev_pdh = g_strdup(""); ++ params->sev_plat_cert = g_strdup(""); ++ params->sev_amd_cert = g_strdup(""); + } + + /* +@@ -1348,6 +1358,19 @@ static void migrate_params_test_apply(MigrateSetParameters *params, + if (params->has_mode) { + dest->mode = params->mode; + } ++ ++ if (params->sev_pdh) { ++ assert(params->sev_pdh->type == QTYPE_QSTRING); ++ dest->sev_pdh = params->sev_pdh->u.s; ++ } ++ if (params->sev_plat_cert) { ++ assert(params->sev_plat_cert->type == QTYPE_QSTRING); ++ dest->sev_plat_cert = params->sev_plat_cert->u.s; ++ } ++ if (params->sev_amd_cert) { ++ assert(params->sev_amd_cert->type == QTYPE_QSTRING); ++ dest->sev_amd_cert = params->sev_amd_cert->u.s; ++ } + } + + static void migrate_params_apply(MigrateSetParameters *params, Error **errp) +@@ -1492,6 +1515,22 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) + if (params->has_mode) { + s->parameters.mode = params->mode; + } ++ ++ if (params->sev_pdh) { ++ g_free(s->parameters.sev_pdh); ++ assert(params->sev_pdh->type == QTYPE_QSTRING); ++ s->parameters.sev_pdh = g_strdup(params->sev_pdh->u.s); ++ } ++ if (params->sev_plat_cert) { ++ g_free(s->parameters.sev_plat_cert); ++ assert(params->sev_plat_cert->type == QTYPE_QSTRING); ++ s->parameters.sev_plat_cert = g_strdup(params->sev_plat_cert->u.s); ++ } ++ if (params->sev_amd_cert) { ++ g_free(s->parameters.sev_amd_cert); ++ assert(params->sev_amd_cert->type == QTYPE_QSTRING); ++ s->parameters.sev_amd_cert = g_strdup(params->sev_amd_cert->u.s); ++ } + } + + void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp) +@@ -1517,6 +1556,27 @@ void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp) + params->tls_authz->type = QTYPE_QSTRING; + params->tls_authz->u.s = strdup(""); + } ++ /* TODO Rewrite "" to null instead */ ++ if (params->sev_pdh ++ && params->sev_pdh->type == QTYPE_QNULL) { ++ qobject_unref(params->sev_pdh->u.n); ++ params->sev_pdh->type = QTYPE_QSTRING; ++ params->sev_pdh->u.s = strdup(""); ++ } ++ /* TODO Rewrite "" to null instead */ ++ if (params->sev_plat_cert ++ && params->sev_plat_cert->type == QTYPE_QNULL) { ++ qobject_unref(params->sev_plat_cert->u.n); ++ params->sev_plat_cert->type = QTYPE_QSTRING; ++ params->sev_plat_cert->u.s = strdup(""); ++ } ++ /* TODO Rewrite "" to null instead */ ++ if (params->sev_amd_cert ++ && params->sev_amd_cert->type == QTYPE_QNULL) { ++ qobject_unref(params->sev_amd_cert->u.n); ++ params->sev_amd_cert->type = QTYPE_QSTRING; ++ params->sev_amd_cert->u.s = strdup(""); ++ } + + migrate_params_test_apply(params, &tmp); + +diff --git a/qapi/migration.json b/qapi/migration.json +index 197d3fa..d9b25ef 100644 +--- a/qapi/migration.json ++++ b/qapi/migration.json +@@ -874,6 +874,15 @@ + # @mode: Migration mode. See description in @MigMode. Default is 'normal'. + # (Since 8.2) + # ++# @sev-pdh: The target host platform diffie-hellman key encoded in base64 ++# (Since 4.2) ++# ++# @sev-plat-cert: The target host platform certificate chain encoded in base64 ++# (Since 4.2) ++# ++# @sev-amd-cert: AMD certificate chain which include ASK and OCA encoded in ++# base64 (Since 4.2) ++# + # Features: + # + # @deprecated: Member @block-incremental is deprecated. Use +@@ -907,7 +916,8 @@ + 'block-bitmap-mapping', + { 'name': 'x-vcpu-dirty-limit-period', 'features': ['unstable'] }, + 'vcpu-dirty-limit', +- 'mode'] } ++ 'mode', ++ 'sev-pdh', 'sev-plat-cert', 'sev-amd-cert'] } + + ## + # @MigrateSetParameters: +@@ -1062,6 +1072,15 @@ + # @mode: Migration mode. See description in @MigMode. Default is 'normal'. + # (Since 8.2) + # ++# @sev-pdh: The target host platform diffie-hellman key encoded in base64 ++# (Since 4.2) ++# ++# @sev-plat-cert: The target host platform certificate chain encoded in base64 ++# (Since 4.2) ++# ++# @sev-amd-cert: AMD certificate chain which include ASK and OCA encoded in ++# base64 (Since 4.2) ++# + # Features: + # + # @deprecated: Member @block-incremental is deprecated. Use +@@ -1115,7 +1134,11 @@ + '*x-vcpu-dirty-limit-period': { 'type': 'uint64', + 'features': [ 'unstable' ] }, + '*vcpu-dirty-limit': 'uint64', +- '*mode': 'MigMode'} } ++ '*mode': 'MigMode', ++ '*sev-pdh': 'StrOrNull', ++ '*sev-plat-cert': 'StrOrNull', ++ '*sev-amd-cert' : 'StrOrNull' } } ++ + + ## + # @migrate-set-parameters: +@@ -1290,6 +1313,15 @@ + # @mode: Migration mode. See description in @MigMode. Default is 'normal'. + # (Since 8.2) + # ++# @sev-pdh: The target host platform diffie-hellman key encoded in base64 ++# (Since 4.2) ++# ++# @sev-plat-cert: The target host platform certificate chain encoded in base64 ++# (Since 4.2) ++# ++# @sev-amd-cert: AMD certificate chain which include ASK and OCA encoded in ++# base64 (Since 4.2) ++# + # Features: + # + # @deprecated: Member @block-incremental is deprecated. Use +@@ -1340,7 +1372,10 @@ + '*x-vcpu-dirty-limit-period': { 'type': 'uint64', + 'features': [ 'unstable' ] }, + '*vcpu-dirty-limit': 'uint64', +- '*mode': 'MigMode'} } ++ '*mode': 'MigMode', ++ '*sev-pdh': 'str', ++ '*sev-plat-cert': 'str', ++ '*sev-amd-cert' : 'str'} } + + ## + # @query-migrate-parameters: +-- +1.8.3.1 + diff --git a/0191-confidential-guest-support-introduce-ConfidentialGue.patch b/0191-confidential-guest-support-introduce-ConfidentialGue.patch new file mode 100644 index 0000000000000000000000000000000000000000..f6b6f4dbb4629e11576f881f56a1e674497d187b --- /dev/null +++ b/0191-confidential-guest-support-introduce-ConfidentialGue.patch @@ -0,0 +1,67 @@ +From dacd8c4764e41e0b8e6fe46fe29f9e65ca1f5625 Mon Sep 17 00:00:00 2001 +From: Brijesh Singh +Date: Tue, 27 Jul 2021 11:41:37 +0000 +Subject: [PATCH 201/293] confidential guest support: introduce + ConfidentialGuestMemoryEncryptionOps for encrypted VMs + +cherry-picked from https://github.com/AMDESE/qemu/commit/74fce7be9bd. + +When memory encryption is enabled in VM, the guest RAM will be encrypted +with the guest-specific key, to protect the confidentiality of data while +in transit we need to platform specific hooks to save or migrate the +guest RAM. + +Introduce the new ConfidentialGuestMemoryEncryptionOps in this patch +which will be later used by the encrypted guest for migration. + +Signed-off-by: Brijesh Singh +Co-developed-by: Ashish Kalra +Signed-off-by: Ashish Kalra +Signed-off-by: hanliyang +--- + include/exec/confidential-guest-support.h | 27 +++++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +diff --git a/include/exec/confidential-guest-support.h b/include/exec/confidential-guest-support.h +index ba2dd4b..343f686 100644 +--- a/include/exec/confidential-guest-support.h ++++ b/include/exec/confidential-guest-support.h +@@ -53,8 +53,35 @@ struct ConfidentialGuestSupport { + bool ready; + }; + ++/** ++ * The functions registers with ConfidentialGuestMemoryEncryptionOps will be ++ * used during the encrypted guest migration. ++ */ ++struct ConfidentialGuestMemoryEncryptionOps { ++ /* Initialize the platform specific state before starting the migration */ ++ int (*save_setup)(const char *pdh, const char *plat_cert, ++ const char *amd_cert); ++ ++ /* Write the encrypted page and metadata associated with it */ ++ int (*save_outgoing_page)(QEMUFile *f, uint8_t *ptr, uint32_t size, ++ uint64_t *bytes_sent); ++ ++ /* Load the incoming encrypted page into guest memory */ ++ int (*load_incoming_page)(QEMUFile *f, uint8_t *ptr); ++ ++ /* Check if gfn is in shared/unencrypted region */ ++ bool (*is_gfn_in_unshared_region)(unsigned long gfn); ++ ++ /* Write the shared regions list */ ++ int (*save_outgoing_shared_regions_list)(QEMUFile *f); ++ ++ /* Load the shared regions list */ ++ int (*load_incoming_shared_regions_list)(QEMUFile *f); ++}; ++ + typedef struct ConfidentialGuestSupportClass { + ObjectClass parent; ++ struct ConfidentialGuestMemoryEncryptionOps *memory_encryption_ops; + } ConfidentialGuestSupportClass; + + #endif /* !CONFIG_USER_ONLY */ +-- +1.8.3.1 + diff --git a/0192-target-i386-sev-provide-callback-to-setup-outgoing-c.patch b/0192-target-i386-sev-provide-callback-to-setup-outgoing-c.patch new file mode 100644 index 0000000000000000000000000000000000000000..e7db25aeda98b8d741c64040e5478484a590b238 --- /dev/null +++ b/0192-target-i386-sev-provide-callback-to-setup-outgoing-c.patch @@ -0,0 +1,136 @@ +From dbd192392ad43804584938db8179f5861940dcd4 Mon Sep 17 00:00:00 2001 +From: Brijesh Singh +Date: Tue, 27 Jul 2021 12:10:23 +0000 +Subject: [PATCH 202/293] target/i386: sev: provide callback to setup outgoing + context + +cherry-picked from https://github.com/AMDESE/qemu/commit/7521883afc0. + +The user provides the target machine's Platform Diffie-Hellman key (PDH) +and certificate chain before starting the SEV guest migration. Cache the +certificate chain as we need them while creating the outgoing context. + +Signed-off-by: Brijesh Singh +Co-developed-by: Ashish Kalra +Signed-off-by: Ashish Kalra +[ Fix conflict. ] +Signed-off-by: hanliyang +--- + target/i386/sev.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ + target/i386/sev.h | 2 ++ + 2 files changed, 61 insertions(+) + +diff --git a/target/i386/sev.c b/target/i386/sev.c +index 9a71246..3ab02f4 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -73,6 +73,12 @@ struct SevGuestState { + int sev_fd; + SevState state; + gchar *measurement; ++ guchar *remote_pdh; ++ size_t remote_pdh_len; ++ guchar *remote_plat_cert; ++ size_t remote_plat_cert_len; ++ guchar *amd_cert; ++ size_t amd_cert_len; + + uint32_t reset_cs; + uint32_t reset_ip; +@@ -157,6 +163,12 @@ static const char *const sev_fw_errlist[] = { + + #define SEV_FW_MAX_ERROR ARRAY_SIZE(sev_fw_errlist) + ++#define SEV_FW_BLOB_MAX_SIZE 0x4000 /* 16KB */ ++ ++static struct ConfidentialGuestMemoryEncryptionOps sev_memory_encryption_ops = { ++ .save_setup = sev_save_setup, ++}; ++ + static int + sev_ioctl(int fd, int cmd, void *data, int *error) + { +@@ -906,6 +918,48 @@ sev_vm_state_change(void *opaque, bool running, RunState state) + } + } + ++static inline bool check_blob_length(size_t value) ++{ ++ if (value > SEV_FW_BLOB_MAX_SIZE) { ++ error_report("invalid length max=%d got=%ld", ++ SEV_FW_BLOB_MAX_SIZE, value); ++ return false; ++ } ++ ++ return true; ++} ++ ++int sev_save_setup(const char *pdh, const char *plat_cert, ++ const char *amd_cert) ++{ ++ SevGuestState *s = sev_guest; ++ ++ s->remote_pdh = g_base64_decode(pdh, &s->remote_pdh_len); ++ if (!check_blob_length(s->remote_pdh_len)) { ++ goto error; ++ } ++ ++ s->remote_plat_cert = g_base64_decode(plat_cert, ++ &s->remote_plat_cert_len); ++ if (!check_blob_length(s->remote_plat_cert_len)) { ++ goto error; ++ } ++ ++ s->amd_cert = g_base64_decode(amd_cert, &s->amd_cert_len); ++ if (!check_blob_length(s->amd_cert_len)) { ++ goto error; ++ } ++ ++ return 0; ++ ++error: ++ g_free(s->remote_pdh); ++ g_free(s->remote_plat_cert); ++ g_free(s->amd_cert); ++ ++ return 1; ++} ++ + int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) + { + SevGuestState *sev +@@ -920,6 +974,9 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) + return 0; + } + ++ ConfidentialGuestSupportClass *cgs_class = ++ (ConfidentialGuestSupportClass *) object_get_class(OBJECT(cgs)); ++ + ret = ram_block_discard_disable(true); + if (ret) { + error_report("%s: cannot disable RAM discard", __func__); +@@ -1013,6 +1070,8 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) + qemu_add_machine_init_done_notifier(&sev_machine_done_notify); + qemu_add_vm_change_state_handler(sev_vm_state_change, sev); + ++ cgs_class->memory_encryption_ops = &sev_memory_encryption_ops; ++ + cgs->ready = true; + + return 0; +diff --git a/target/i386/sev.h b/target/i386/sev.h +index e7499c9..e96de02 100644 +--- a/target/i386/sev.h ++++ b/target/i386/sev.h +@@ -51,6 +51,8 @@ uint32_t sev_get_reduced_phys_bits(void); + bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp); + + int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp); ++int sev_save_setup(const char *pdh, const char *plat_cert, ++ const char *amd_cert); + int sev_inject_launch_secret(const char *hdr, const char *secret, + uint64_t gpa, Error **errp); + +-- +1.8.3.1 + diff --git a/0193-target-i386-sev-do-not-create-launch-context-for-an-.patch b/0193-target-i386-sev-do-not-create-launch-context-for-an-.patch new file mode 100644 index 0000000000000000000000000000000000000000..680f23e5a9056acddf1a96730e7de2d245d92e3c --- /dev/null +++ b/0193-target-i386-sev-do-not-create-launch-context-for-an-.patch @@ -0,0 +1,49 @@ +From 85eccfb010be99335ab4465fff0504dd2f7acc09 Mon Sep 17 00:00:00 2001 +From: Brijesh Singh +Date: Tue, 27 Jul 2021 12:16:09 +0000 +Subject: [PATCH 203/293] target/i386: sev: do not create launch context for an + incoming guest + +cherry-picked from https://github.com/AMDESE/qemu/commit/b85694233495. + +The LAUNCH_START is used for creating an encryption context to encrypt +newly created guest, for an incoming guest the RECEIVE_START should be +used. + +Reviewed-by: Dr. David Alan Gilbert +Signed-off-by: Brijesh Singh +Signed-off-by: Ashish Kalra +[ Fix conflict. ] +Signed-off-by: hanliyang +--- + target/i386/sev.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +diff --git a/target/i386/sev.c b/target/i386/sev.c +index 3ab02f4..b56cbdb 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -1060,10 +1060,16 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) + goto err; + } + +- ret = sev_launch_start(sev); +- if (ret) { +- error_setg(errp, "%s: failed to create encryption context", __func__); +- goto err; ++ /* ++ * The LAUNCH context is used for new guest, if its an incoming guest ++ * then RECEIVE context will be created after the connection is established. ++ */ ++ if (!runstate_check(RUN_STATE_INMIGRATE)) { ++ ret = sev_launch_start(sev); ++ if (ret) { ++ error_setg(errp, "%s: failed to create encryption context", __func__); ++ goto err; ++ } + } + + ram_block_notifier_add(&sev_ram_notifier); +-- +1.8.3.1 + diff --git a/0194-target-i386-sev-add-support-to-encrypt-the-outgoing-.patch b/0194-target-i386-sev-add-support-to-encrypt-the-outgoing-.patch new file mode 100644 index 0000000000000000000000000000000000000000..1a9bf60419b044d9fe1fd8533300eca763c34d75 --- /dev/null +++ b/0194-target-i386-sev-add-support-to-encrypt-the-outgoing-.patch @@ -0,0 +1,320 @@ +From 32124419dfb142e6c0d23b8a7917ce8566a4160b Mon Sep 17 00:00:00 2001 +From: Brijesh Singh +Date: Tue, 27 Jul 2021 12:55:25 +0000 +Subject: [PATCH 204/293] target/i386: sev: add support to encrypt the outgoing + page + +cherry-picked from https://github.com/AMDESE/qemu/commit/5187c6f86bd. + +The sev_save_outgoing_page() provide the implementation to encrypt the +guest private pages during the transit. The routines uses the SEND_START +command to create the outgoing encryption context on the first call then +uses the SEND_UPDATE_DATA command to encrypt the data before writing it +to the socket. While encrypting the data SEND_UPDATE_DATA produces some +metadata (e.g MAC, IV). The metadata is also sent to the target machine. +After migration is completed, we issue the SEND_FINISH command to transition +the SEV guest state from sending to unrunnable state. + +Signed-off-by: Brijesh Singh +Co-developed-by: Ashish Kalra +Signed-off-by: Ashish Kalra +[ Fix conflict. ] +Signed-off-by: hanliyang +--- + target/i386/sev.c | 219 +++++++++++++++++++++++++++++++++++++++++++++++ + target/i386/sev.h | 2 + + target/i386/trace-events | 3 + + 3 files changed, 224 insertions(+) + +diff --git a/target/i386/sev.c b/target/i386/sev.c +index b56cbdb..617587c 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -31,6 +31,8 @@ + #include "sysemu/runstate.h" + #include "trace.h" + #include "migration/blocker.h" ++#include "migration/qemu-file.h" ++#include "migration/misc.h" + #include "qom/object.h" + #include "monitor/monitor.h" + #include "monitor/hmp-target.h" +@@ -79,6 +81,8 @@ struct SevGuestState { + size_t remote_plat_cert_len; + guchar *amd_cert; + size_t amd_cert_len; ++ gchar *send_packet_hdr; ++ size_t send_packet_hdr_len; + + uint32_t reset_cs; + uint32_t reset_ip; +@@ -167,6 +171,7 @@ static const char *const sev_fw_errlist[] = { + + static struct ConfidentialGuestMemoryEncryptionOps sev_memory_encryption_ops = { + .save_setup = sev_save_setup, ++ .save_outgoing_page = sev_save_outgoing_page, + }; + + static int +@@ -960,6 +965,38 @@ error: + return 1; + } + ++static void ++sev_send_finish(void) ++{ ++ int ret, error; ++ ++ trace_kvm_sev_send_finish(); ++ ret = sev_ioctl(sev_guest->sev_fd, KVM_SEV_SEND_FINISH, 0, &error); ++ if (ret) { ++ error_report("%s: SEND_FINISH ret=%d fw_error=%d '%s'", ++ __func__, ret, error, fw_error_to_str(error)); ++ } ++ ++ g_free(sev_guest->send_packet_hdr); ++ sev_set_guest_state(sev_guest, SEV_STATE_RUNNING); ++} ++ ++static void ++sev_migration_state_notifier(Notifier *notifier, void *data) ++{ ++ MigrationState *s = data; ++ ++ if (migration_has_finished(s) || ++ migration_in_postcopy_after_devices(s) || ++ migration_has_failed(s)) { ++ if (sev_check_state(sev_guest, SEV_STATE_SEND_UPDATE)) { ++ sev_send_finish(); ++ } ++ } ++} ++ ++static Notifier sev_migration_state; ++ + int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) + { + SevGuestState *sev +@@ -1075,6 +1112,7 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) + ram_block_notifier_add(&sev_ram_notifier); + qemu_add_machine_init_done_notifier(&sev_machine_done_notify); + qemu_add_vm_change_state_handler(sev_vm_state_change, sev); ++ migration_add_notifier(&sev_migration_state, sev_migration_state_notifier); + + cgs_class->memory_encryption_ops = &sev_memory_encryption_ops; + +@@ -1316,6 +1354,187 @@ int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size) + return 0; + } + ++static int ++sev_get_send_session_length(void) ++{ ++ int ret, fw_err = 0; ++ struct kvm_sev_send_start start = {}; ++ ++ ret = sev_ioctl(sev_guest->sev_fd, KVM_SEV_SEND_START, &start, &fw_err); ++ if (fw_err != SEV_RET_INVALID_LEN) { ++ ret = -1; ++ error_report("%s: failed to get session length ret=%d fw_error=%d '%s'", ++ __func__, ret, fw_err, fw_error_to_str(fw_err)); ++ goto err; ++ } ++ ++ ret = start.session_len; ++err: ++ return ret; ++} ++ ++static int ++sev_send_start(SevGuestState *s, QEMUFile *f, uint64_t *bytes_sent) ++{ ++ gsize pdh_len = 0, plat_cert_len; ++ int session_len, ret, fw_error; ++ struct kvm_sev_send_start start = { }; ++ guchar *pdh = NULL, *plat_cert = NULL, *session = NULL; ++ Error *local_err = NULL; ++ ++ if (!s->remote_pdh || !s->remote_plat_cert || !s->amd_cert_len) { ++ error_report("%s: missing remote PDH or PLAT_CERT", __func__); ++ return 1; ++ } ++ ++ start.pdh_cert_uaddr = (uintptr_t) s->remote_pdh; ++ start.pdh_cert_len = s->remote_pdh_len; ++ ++ start.plat_certs_uaddr = (uintptr_t)s->remote_plat_cert; ++ start.plat_certs_len = s->remote_plat_cert_len; ++ ++ start.amd_certs_uaddr = (uintptr_t)s->amd_cert; ++ start.amd_certs_len = s->amd_cert_len; ++ ++ /* get the session length */ ++ session_len = sev_get_send_session_length(); ++ if (session_len < 0) { ++ ret = 1; ++ goto err; ++ } ++ ++ session = g_new0(guchar, session_len); ++ start.session_uaddr = (unsigned long)session; ++ start.session_len = session_len; ++ ++ /* Get our PDH certificate */ ++ ret = sev_get_pdh_info(s->sev_fd, &pdh, &pdh_len, ++ &plat_cert, &plat_cert_len, &local_err); ++ if (ret) { ++ error_report("Failed to get our PDH cert"); ++ goto err; ++ } ++ ++ trace_kvm_sev_send_start(start.pdh_cert_uaddr, start.pdh_cert_len, ++ start.plat_certs_uaddr, start.plat_certs_len, ++ start.amd_certs_uaddr, start.amd_certs_len); ++ ++ ret = sev_ioctl(s->sev_fd, KVM_SEV_SEND_START, &start, &fw_error); ++ if (ret < 0) { ++ error_report("%s: SEND_START ret=%d fw_error=%d '%s'", ++ __func__, ret, fw_error, fw_error_to_str(fw_error)); ++ goto err; ++ } ++ ++ qemu_put_be32(f, start.policy); ++ qemu_put_be32(f, pdh_len); ++ qemu_put_buffer(f, (uint8_t *)pdh, pdh_len); ++ qemu_put_be32(f, start.session_len); ++ qemu_put_buffer(f, (uint8_t *)start.session_uaddr, start.session_len); ++ *bytes_sent = 12 + pdh_len + start.session_len; ++ ++ sev_set_guest_state(s, SEV_STATE_SEND_UPDATE); ++ ++err: ++ g_free(pdh); ++ g_free(plat_cert); ++ return ret; ++} ++ ++static int ++sev_send_get_packet_len(int *fw_err) ++{ ++ int ret; ++ struct kvm_sev_send_update_data update = { 0, }; ++ ++ ret = sev_ioctl(sev_guest->sev_fd, KVM_SEV_SEND_UPDATE_DATA, ++ &update, fw_err); ++ if (*fw_err != SEV_RET_INVALID_LEN) { ++ ret = -1; ++ error_report("%s: failed to get session length ret=%d fw_error=%d '%s'", ++ __func__, ret, *fw_err, fw_error_to_str(*fw_err)); ++ goto err; ++ } ++ ++ ret = update.hdr_len; ++ ++err: ++ return ret; ++} ++ ++static int ++sev_send_update_data(SevGuestState *s, QEMUFile *f, uint8_t *ptr, uint32_t size, ++ uint64_t *bytes_sent) ++{ ++ int ret, fw_error; ++ guchar *trans; ++ struct kvm_sev_send_update_data update = { }; ++ ++ /* ++ * If this is first call then query the packet header bytes and allocate ++ * the packet buffer. ++ */ ++ if (!s->send_packet_hdr) { ++ s->send_packet_hdr_len = sev_send_get_packet_len(&fw_error); ++ if (s->send_packet_hdr_len < 1) { ++ error_report("%s: SEND_UPDATE fw_error=%d '%s'", ++ __func__, fw_error, fw_error_to_str(fw_error)); ++ return 1; ++ } ++ ++ s->send_packet_hdr = g_new(gchar, s->send_packet_hdr_len); ++ } ++ ++ /* allocate transport buffer */ ++ trans = g_new(guchar, size); ++ ++ update.hdr_uaddr = (uintptr_t)s->send_packet_hdr; ++ update.hdr_len = s->send_packet_hdr_len; ++ update.guest_uaddr = (uintptr_t)ptr; ++ update.guest_len = size; ++ update.trans_uaddr = (uintptr_t)trans; ++ update.trans_len = size; ++ ++ trace_kvm_sev_send_update_data(ptr, trans, size); ++ ++ ret = sev_ioctl(s->sev_fd, KVM_SEV_SEND_UPDATE_DATA, &update, &fw_error); ++ if (ret) { ++ error_report("%s: SEND_UPDATE_DATA ret=%d fw_error=%d '%s'", ++ __func__, ret, fw_error, fw_error_to_str(fw_error)); ++ goto err; ++ } ++ ++ qemu_put_be32(f, update.hdr_len); ++ qemu_put_buffer(f, (uint8_t *)update.hdr_uaddr, update.hdr_len); ++ *bytes_sent = 4 + update.hdr_len; ++ ++ qemu_put_be32(f, update.trans_len); ++ qemu_put_buffer(f, (uint8_t *)update.trans_uaddr, update.trans_len); ++ *bytes_sent += (4 + update.trans_len); ++ ++err: ++ g_free(trans); ++ return ret; ++} ++ ++int sev_save_outgoing_page(QEMUFile *f, uint8_t *ptr, ++ uint32_t sz, uint64_t *bytes_sent) ++{ ++ SevGuestState *s = sev_guest; ++ ++ /* ++ * If this is a first buffer then create outgoing encryption context ++ * and write our PDH, policy and session data. ++ */ ++ if (!sev_check_state(s, SEV_STATE_SEND_UPDATE) && ++ sev_send_start(s, f, bytes_sent)) { ++ error_report("Failed to create outgoing context"); ++ return 1; ++ } ++ ++ return sev_send_update_data(s, f, ptr, sz, bytes_sent); ++} ++ + static const QemuUUID sev_hash_table_header_guid = { + .data = UUID_LE(0x9438d606, 0x4f22, 0x4cc9, 0xb4, 0x79, 0xa7, 0x93, + 0xd4, 0x11, 0xfd, 0x21) +diff --git a/target/i386/sev.h b/target/i386/sev.h +index e96de02..463e94b 100644 +--- a/target/i386/sev.h ++++ b/target/i386/sev.h +@@ -53,6 +53,8 @@ bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp); + int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp); + int sev_save_setup(const char *pdh, const char *plat_cert, + const char *amd_cert); ++int sev_save_outgoing_page(QEMUFile *f, uint8_t *ptr, ++ uint32_t size, uint64_t *bytes_sent); + int sev_inject_launch_secret(const char *hdr, const char *secret, + uint64_t gpa, Error **errp); + +diff --git a/target/i386/trace-events b/target/i386/trace-events +index 2cd8726..e8d4aec 100644 +--- a/target/i386/trace-events ++++ b/target/i386/trace-events +@@ -11,3 +11,6 @@ kvm_sev_launch_measurement(const char *value) "data %s" + kvm_sev_launch_finish(void) "" + kvm_sev_launch_secret(uint64_t hpa, uint64_t hva, uint64_t secret, int len) "hpa 0x%" PRIx64 " hva 0x%" PRIx64 " data 0x%" PRIx64 " len %d" + kvm_sev_attestation_report(const char *mnonce, const char *data) "mnonce %s data %s" ++kvm_sev_send_start(uint64_t pdh, int l1, uint64_t plat, int l2, uint64_t amd, int l3) "pdh 0x%" PRIx64 " len %d plat 0x%" PRIx64 " len %d amd 0x%" PRIx64 " len %d" ++kvm_sev_send_update_data(void *src, void *dst, int len) "guest %p trans %p len %d" ++kvm_sev_send_finish(void) "" +-- +1.8.3.1 + diff --git a/0195-target-i386-sev-add-support-to-load-incoming-encrypt.patch b/0195-target-i386-sev-add-support-to-load-incoming-encrypt.patch new file mode 100644 index 0000000000000000000000000000000000000000..785a1b1e0e38f98d299a956507c2e9e6a85dfca3 --- /dev/null +++ b/0195-target-i386-sev-add-support-to-load-incoming-encrypt.patch @@ -0,0 +1,222 @@ +From 814f0c308cf71dcd076f23e9fe554c2b5f56c5bf Mon Sep 17 00:00:00 2001 +From: Brijesh Singh +Date: Tue, 27 Jul 2021 13:00:50 +0000 +Subject: [PATCH 205/293] target/i386: sev: add support to load incoming + encrypted page + +cherry-picked from https://github.com/AMDESE/qemu/commit/e86e5dccb045. + +The sev_load_incoming_page() provide the implementation to read the +incoming guest private pages from the socket and load it into the guest +memory. The routines uses the RECEIVE_START command to create the +incoming encryption context on the first call then uses the +RECEIEVE_UPDATE_DATA command to load the encrypted pages into the guest +memory. After migration is completed, we issue the RECEIVE_FINISH command +to transition the SEV guest to the runnable state so that it can be +executed. + +Signed-off-by: Brijesh Singh +Co-developed-by: Ashish Kalra +Signed-off-by: Ashish Kalra +[ Fix conflicts. ] +Signed-off-by: hanliyang +--- + target/i386/sev.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++- + target/i386/sev.h | 1 + + target/i386/trace-events | 3 ++ + 3 files changed, 140 insertions(+), 1 deletion(-) + +diff --git a/target/i386/sev.c b/target/i386/sev.c +index 617587c..ee42edf 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -172,6 +172,7 @@ static const char *const sev_fw_errlist[] = { + static struct ConfidentialGuestMemoryEncryptionOps sev_memory_encryption_ops = { + .save_setup = sev_save_setup, + .save_outgoing_page = sev_save_outgoing_page, ++ .load_incoming_page = sev_load_incoming_page, + }; + + static int +@@ -911,13 +912,33 @@ sev_launch_finish(SevGuestState *sev) + migrate_add_blocker(&sev_mig_blocker, &error_fatal); + } + ++static int ++sev_receive_finish(SevGuestState *s) ++{ ++ int error, ret = 1; ++ ++ trace_kvm_sev_receive_finish(); ++ ret = sev_ioctl(s->sev_fd, KVM_SEV_RECEIVE_FINISH, 0, &error); ++ if (ret) { ++ error_report("%s: RECEIVE_FINISH ret=%d fw_error=%d '%s'", ++ __func__, ret, error, fw_error_to_str(error)); ++ goto err; ++ } ++ ++ sev_set_guest_state(s, SEV_STATE_RUNNING); ++err: ++ return ret; ++} ++ + static void + sev_vm_state_change(void *opaque, bool running, RunState state) + { + SevGuestState *sev = opaque; + + if (running) { +- if (!sev_check_state(sev, SEV_STATE_RUNNING)) { ++ if (sev_check_state(sev, SEV_STATE_RECEIVE_UPDATE)) { ++ sev_receive_finish(sev); ++ } else if (!sev_check_state(sev, SEV_STATE_RUNNING)) { + sev_launch_finish(sev); + } + } +@@ -1535,6 +1556,120 @@ int sev_save_outgoing_page(QEMUFile *f, uint8_t *ptr, + return sev_send_update_data(s, f, ptr, sz, bytes_sent); + } + ++static int ++sev_receive_start(SevGuestState *sev, QEMUFile *f) ++{ ++ int ret = 1; ++ int fw_error; ++ struct kvm_sev_receive_start start = { }; ++ gchar *session = NULL, *pdh_cert = NULL; ++ ++ /* get SEV guest handle */ ++ start.handle = object_property_get_int(OBJECT(sev), "handle", ++ &error_abort); ++ ++ /* get the source policy */ ++ start.policy = qemu_get_be32(f); ++ ++ /* get source PDH key */ ++ start.pdh_len = qemu_get_be32(f); ++ if (!check_blob_length(start.pdh_len)) { ++ return 1; ++ } ++ ++ pdh_cert = g_new(gchar, start.pdh_len); ++ qemu_get_buffer(f, (uint8_t *)pdh_cert, start.pdh_len); ++ start.pdh_uaddr = (uintptr_t)pdh_cert; ++ ++ /* get source session data */ ++ start.session_len = qemu_get_be32(f); ++ if (!check_blob_length(start.session_len)) { ++ return 1; ++ } ++ session = g_new(gchar, start.session_len); ++ qemu_get_buffer(f, (uint8_t *)session, start.session_len); ++ start.session_uaddr = (uintptr_t)session; ++ ++ trace_kvm_sev_receive_start(start.policy, session, pdh_cert); ++ ++ ret = sev_ioctl(sev_guest->sev_fd, KVM_SEV_RECEIVE_START, ++ &start, &fw_error); ++ if (ret < 0) { ++ error_report("Error RECEIVE_START ret=%d fw_error=%d '%s'", ++ ret, fw_error, fw_error_to_str(fw_error)); ++ goto err; ++ } ++ ++ object_property_set_int(OBJECT(sev), "handle", start.handle, &error_abort); ++ sev_set_guest_state(sev, SEV_STATE_RECEIVE_UPDATE); ++err: ++ g_free(session); ++ g_free(pdh_cert); ++ ++ return ret; ++} ++ ++static int sev_receive_update_data(QEMUFile *f, uint8_t *ptr) ++{ ++ int ret = 1, fw_error = 0; ++ gchar *hdr = NULL, *trans = NULL; ++ struct kvm_sev_receive_update_data update = {}; ++ ++ /* get packet header */ ++ update.hdr_len = qemu_get_be32(f); ++ if (!check_blob_length(update.hdr_len)) { ++ return 1; ++ } ++ ++ hdr = g_new(gchar, update.hdr_len); ++ qemu_get_buffer(f, (uint8_t *)hdr, update.hdr_len); ++ update.hdr_uaddr = (uintptr_t)hdr; ++ ++ /* get transport buffer */ ++ update.trans_len = qemu_get_be32(f); ++ if (!check_blob_length(update.trans_len)) { ++ goto err; ++ } ++ ++ trans = g_new(gchar, update.trans_len); ++ update.trans_uaddr = (uintptr_t)trans; ++ qemu_get_buffer(f, (uint8_t *)update.trans_uaddr, update.trans_len); ++ ++ update.guest_uaddr = (uintptr_t) ptr; ++ update.guest_len = update.trans_len; ++ ++ trace_kvm_sev_receive_update_data(trans, ptr, update.guest_len, ++ hdr, update.hdr_len); ++ ++ ret = sev_ioctl(sev_guest->sev_fd, KVM_SEV_RECEIVE_UPDATE_DATA, ++ &update, &fw_error); ++ if (ret) { ++ error_report("Error RECEIVE_UPDATE_DATA ret=%d fw_error=%d '%s'", ++ ret, fw_error, fw_error_to_str(fw_error)); ++ goto err; ++ } ++err: ++ g_free(trans); ++ g_free(hdr); ++ return ret; ++} ++ ++int sev_load_incoming_page(QEMUFile *f, uint8_t *ptr) ++{ ++ SevGuestState *s = sev_guest; ++ ++ /* ++ * If this is first buffer and SEV is not in recieiving state then ++ * use RECEIVE_START command to create a encryption context. ++ */ ++ if (!sev_check_state(s, SEV_STATE_RECEIVE_UPDATE) && ++ sev_receive_start(s, f)) { ++ return 1; ++ } ++ ++ return sev_receive_update_data(f, ptr); ++} ++ + static const QemuUUID sev_hash_table_header_guid = { + .data = UUID_LE(0x9438d606, 0x4f22, 0x4cc9, 0xb4, 0x79, 0xa7, 0x93, + 0xd4, 0x11, 0xfd, 0x21) +diff --git a/target/i386/sev.h b/target/i386/sev.h +index 463e94b..d94da29 100644 +--- a/target/i386/sev.h ++++ b/target/i386/sev.h +@@ -55,6 +55,7 @@ int sev_save_setup(const char *pdh, const char *plat_cert, + const char *amd_cert); + int sev_save_outgoing_page(QEMUFile *f, uint8_t *ptr, + uint32_t size, uint64_t *bytes_sent); ++int sev_load_incoming_page(QEMUFile *f, uint8_t *ptr); + int sev_inject_launch_secret(const char *hdr, const char *secret, + uint64_t gpa, Error **errp); + +diff --git a/target/i386/trace-events b/target/i386/trace-events +index e8d4aec..475de65 100644 +--- a/target/i386/trace-events ++++ b/target/i386/trace-events +@@ -14,3 +14,6 @@ kvm_sev_attestation_report(const char *mnonce, const char *data) "mnonce %s data + kvm_sev_send_start(uint64_t pdh, int l1, uint64_t plat, int l2, uint64_t amd, int l3) "pdh 0x%" PRIx64 " len %d plat 0x%" PRIx64 " len %d amd 0x%" PRIx64 " len %d" + kvm_sev_send_update_data(void *src, void *dst, int len) "guest %p trans %p len %d" + kvm_sev_send_finish(void) "" ++kvm_sev_receive_start(int policy, void *session, void *pdh) "policy 0x%x session %p pdh %p" ++kvm_sev_receive_update_data(void *src, void *dst, int len, void *hdr, int hdr_len) "guest %p trans %p len %d hdr %p hdr_len %d" ++kvm_sev_receive_finish(void) "" +-- +1.8.3.1 + diff --git a/0196-kvm-Add-support-for-SEV-shared-regions-list-and-KVM_.patch b/0196-kvm-Add-support-for-SEV-shared-regions-list-and-KVM_.patch new file mode 100644 index 0000000000000000000000000000000000000000..70d7c77184cfe217f5eba7ee5518628d2bb7e7e6 --- /dev/null +++ b/0196-kvm-Add-support-for-SEV-shared-regions-list-and-KVM_.patch @@ -0,0 +1,315 @@ +From 493ec7bb50b5020429809cacdbcbad1c5b2f5b65 Mon Sep 17 00:00:00 2001 +From: Ashish Kalra +Date: Tue, 27 Jul 2021 15:05:49 +0000 +Subject: [PATCH 206/293] kvm: Add support for SEV shared regions list and + KVM_EXIT_HYPERCALL. + +cherry-picked from https://github.com/AMDESE/qemu/commit/fcbbd9b19ac. + +KVM_HC_MAP_GPA_RANGE hypercall is used by the SEV guest to notify a +change in the page encryption status to the hypervisor. The hypercall +should be invoked only when the encryption attribute is changed from +encrypted -> decrypted and vice versa. By default all guest pages are +considered encrypted. + +The hypercall exits to userspace with KVM_EXIT_HYPERCALL exit code, +currently this is used only by SEV guests for guest page encryptiion +status tracking. Add support to handle this exit and invoke SEV +shared regions list handlers. + +Add support for SEV guest shared regions and implementation of the +SEV shared regions list. + +Signed-off-by: Ashish Kalra +[ Fix conflicts. ] +Signed-off-by: hanliyang +--- + linux-headers/linux/kvm.h | 3 ++ + target/i386/kvm/kvm.c | 48 ++++++++++++++++++++ + target/i386/kvm/sev-stub.c | 11 +++++ + target/i386/sev.c | 106 +++++++++++++++++++++++++++++++++++++++++++++ + target/i386/sev.h | 3 ++ + 5 files changed, 171 insertions(+) + +diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h +index 549fea3..9758e8f 100644 +--- a/linux-headers/linux/kvm.h ++++ b/linux-headers/linux/kvm.h +@@ -346,6 +346,7 @@ struct kvm_run { + } iocsr_io; + /* KVM_EXIT_HYPERCALL */ + struct { ++#define KVM_HC_MAP_GPA_RANGE 12 + __u64 nr; + __u64 args[6]; + __u64 ret; +@@ -1198,6 +1199,8 @@ struct kvm_ppc_resize_hpt { + #define KVM_CAP_ARM_SUPPORTED_BLOCK_SIZES 229 + #define KVM_CAP_ARM_SUPPORTED_REG_MASK_RANGES 230 + ++#define KVM_EXIT_HYPERCALL_VALID_MASK (1 << KVM_HC_MAP_GPA_RANGE) ++ + #ifdef KVM_CAP_IRQ_ROUTING + + struct kvm_irq_routing_irqchip { +diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c +index a0bc9ea..82f6d3b 100644 +--- a/target/i386/kvm/kvm.c ++++ b/target/i386/kvm/kvm.c +@@ -148,6 +148,7 @@ static int has_xcrs; + static int has_sregs2; + static int has_exception_payload; + static int has_triple_fault_event; ++static int has_map_gpa_range; + + static bool has_msr_mcg_ext_ctl; + +@@ -2191,6 +2192,17 @@ int kvm_arch_init_vcpu(CPUState *cs) + c->eax = MAX(c->eax, KVM_CPUID_SIGNATURE | 0x10); + } + ++ if (sev_enabled()) { ++ c = cpuid_find_entry(&cpuid_data.cpuid, ++ KVM_CPUID_FEATURES | kvm_base, 0); ++ if (c) { ++ c->eax |= (1 << KVM_FEATURE_MIGRATION_CONTROL); ++ if (has_map_gpa_range) { ++ c->eax |= (1 << KVM_FEATURE_HC_MAP_GPA_RANGE); ++ } ++ } ++ } ++ + cpuid_data.cpuid.nent = cpuid_i; + + cpuid_data.cpuid.padding = 0; +@@ -2584,6 +2596,17 @@ int kvm_arch_init(MachineState *ms, KVMState *s) + #endif + } + ++ has_map_gpa_range = kvm_check_extension(s, KVM_CAP_EXIT_HYPERCALL); ++ if (has_map_gpa_range) { ++ ret = kvm_vm_enable_cap(s, KVM_CAP_EXIT_HYPERCALL, 0, ++ KVM_EXIT_HYPERCALL_VALID_MASK); ++ if (ret < 0) { ++ error_report("kvm: Failed to enable MAP_GPA_RANGE cap: %s", ++ strerror(-ret)); ++ return ret; ++ } ++ } ++ + ret = kvm_get_supported_msrs(s); + if (ret < 0) { + return ret; +@@ -4936,6 +4959,28 @@ static int kvm_handle_tpr_access(X86CPU *cpu) + return 1; + } + ++static int kvm_handle_exit_hypercall(X86CPU *cpu, struct kvm_run *run) ++{ ++ /* ++ * Currently this exit is only used by SEV guests for ++ * guest page encryption status tracking. ++ */ ++ if (run->hypercall.nr == KVM_HC_MAP_GPA_RANGE) { ++ unsigned long enc = run->hypercall.args[2]; ++ unsigned long gpa = run->hypercall.args[0]; ++ unsigned long npages = run->hypercall.args[1]; ++ unsigned long gfn_start = gpa >> TARGET_PAGE_BITS; ++ unsigned long gfn_end = gfn_start + npages; ++ ++ if (enc) { ++ sev_remove_shared_regions_list(gfn_start, gfn_end); ++ } else { ++ sev_add_shared_regions_list(gfn_start, gfn_end); ++ } ++ } ++ return 0; ++} ++ + int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) + { + static const uint8_t int3 = 0xcc; +@@ -5359,6 +5404,9 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) + ret = kvm_xen_handle_exit(cpu, &run->xen); + break; + #endif ++ case KVM_EXIT_HYPERCALL: ++ ret = kvm_handle_exit_hypercall(cpu, run); ++ break; + default: + fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason); + ret = -1; +diff --git a/target/i386/kvm/sev-stub.c b/target/i386/kvm/sev-stub.c +index 1be5341..1282d24 100644 +--- a/target/i386/kvm/sev-stub.c ++++ b/target/i386/kvm/sev-stub.c +@@ -19,3 +19,14 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) + /* If we get here, cgs must be some non-SEV thing */ + return 0; + } ++ ++int sev_remove_shared_regions_list(unsigned long gfn_start, ++ unsigned long gfn_end) ++{ ++ return 0; ++} ++ ++int sev_add_shared_regions_list(unsigned long gfn_start, unsigned long gfn_end) ++{ ++ return 0; ++} +diff --git a/target/i386/sev.c b/target/i386/sev.c +index ee42edf..bd00a28 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -44,6 +44,11 @@ + #define TYPE_SEV_GUEST "sev-guest" + OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST) + ++struct shared_region { ++ unsigned long gfn_start, gfn_end; ++ QTAILQ_ENTRY(shared_region) list; ++}; ++ + + /** + * SevGuestState: +@@ -87,6 +92,8 @@ struct SevGuestState { + uint32_t reset_cs; + uint32_t reset_ip; + bool reset_data_valid; ++ ++ QTAILQ_HEAD(, shared_region) shared_regions_list; + }; + + #define DEFAULT_GUEST_POLICY 0x1 /* disable debug */ +@@ -1136,6 +1143,7 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) + migration_add_notifier(&sev_migration_state, sev_migration_state_notifier); + + cgs_class->memory_encryption_ops = &sev_memory_encryption_ops; ++ QTAILQ_INIT(&sev->shared_regions_list); + + cgs->ready = true; + +@@ -1670,6 +1678,104 @@ int sev_load_incoming_page(QEMUFile *f, uint8_t *ptr) + return sev_receive_update_data(f, ptr); + } + ++int sev_remove_shared_regions_list(unsigned long start, unsigned long end) ++{ ++ SevGuestState *s = sev_guest; ++ struct shared_region *pos; ++ ++ QTAILQ_FOREACH(pos, &s->shared_regions_list, list) { ++ unsigned long l, r; ++ unsigned long curr_gfn_end = pos->gfn_end; ++ ++ /* ++ * Find if any intersection exists ? ++ * left bound for intersecting segment ++ */ ++ l = MAX(start, pos->gfn_start); ++ /* right bound for intersecting segment */ ++ r = MIN(end, pos->gfn_end); ++ if (l <= r) { ++ if (pos->gfn_start == l && pos->gfn_end == r) { ++ QTAILQ_REMOVE(&s->shared_regions_list, pos, list); ++ } else if (l == pos->gfn_start) { ++ pos->gfn_start = r; ++ } else if (r == pos->gfn_end) { ++ pos->gfn_end = l; ++ } else { ++ /* Do a de-merge -- split linked list nodes */ ++ struct shared_region *shrd_region; ++ ++ pos->gfn_end = l; ++ shrd_region = g_malloc0(sizeof(*shrd_region)); ++ if (!shrd_region) { ++ return 0; ++ } ++ shrd_region->gfn_start = r; ++ shrd_region->gfn_end = curr_gfn_end; ++ QTAILQ_INSERT_AFTER(&s->shared_regions_list, pos, ++ shrd_region, list); ++ } ++ } ++ if (end <= curr_gfn_end) { ++ break; ++ } ++ } ++ return 0; ++} ++ ++int sev_add_shared_regions_list(unsigned long start, unsigned long end) ++{ ++ struct shared_region *shrd_region; ++ struct shared_region *pos; ++ SevGuestState *s = sev_guest; ++ ++ if (QTAILQ_EMPTY(&s->shared_regions_list)) { ++ shrd_region = g_malloc0(sizeof(*shrd_region)); ++ if (!shrd_region) { ++ return -1; ++ } ++ shrd_region->gfn_start = start; ++ shrd_region->gfn_end = end; ++ QTAILQ_INSERT_TAIL(&s->shared_regions_list, shrd_region, list); ++ return 0; ++ } ++ ++ /* ++ * shared regions list is a sorted list in ascending order ++ * of guest PA's and also merges consecutive range of guest PA's ++ */ ++ QTAILQ_FOREACH(pos, &s->shared_regions_list, list) { ++ /* handle duplicate overlapping regions */ ++ if (start >= pos->gfn_start && end <= pos->gfn_end) { ++ return 0; ++ } ++ if (pos->gfn_end < start) { ++ continue; ++ } ++ /* merge consecutive guest PA(s) -- forward merge */ ++ if (pos->gfn_start <= start && pos->gfn_end >= start) { ++ pos->gfn_end = end; ++ return 0; ++ } ++ break; ++ } ++ /* ++ * Add a new node ++ */ ++ shrd_region = g_malloc0(sizeof(*shrd_region)); ++ if (!shrd_region) { ++ return -1; ++ } ++ shrd_region->gfn_start = start; ++ shrd_region->gfn_end = end; ++ if (pos) { ++ QTAILQ_INSERT_BEFORE(pos, shrd_region, list); ++ } else { ++ QTAILQ_INSERT_TAIL(&s->shared_regions_list, shrd_region, list); ++ } ++ return 1; ++} ++ + static const QemuUUID sev_hash_table_header_guid = { + .data = UUID_LE(0x9438d606, 0x4f22, 0x4cc9, 0xb4, 0x79, 0xa7, 0x93, + 0xd4, 0x11, 0xfd, 0x21) +diff --git a/target/i386/sev.h b/target/i386/sev.h +index d94da29..acf69d4 100644 +--- a/target/i386/sev.h ++++ b/target/i386/sev.h +@@ -61,6 +61,9 @@ int sev_inject_launch_secret(const char *hdr, const char *secret, + + int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size); + void sev_es_set_reset_vector(CPUState *cpu); ++int sev_remove_shared_regions_list(unsigned long gfn_start, ++ unsigned long gfn_end); ++int sev_add_shared_regions_list(unsigned long gfn_start, unsigned long gfn_end); + + int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp); + +-- +1.8.3.1 + diff --git a/0197-migration-add-support-to-migrate-shared-regions-list.patch b/0197-migration-add-support-to-migrate-shared-regions-list.patch new file mode 100644 index 0000000000000000000000000000000000000000..b14d8ceee8d6029b3866413d1560916bde17e016 --- /dev/null +++ b/0197-migration-add-support-to-migrate-shared-regions-list.patch @@ -0,0 +1,120 @@ +From 54a29f988c761e7da755da938b52f96d2662a99f Mon Sep 17 00:00:00 2001 +From: Brijesh Singh +Date: Tue, 27 Jul 2021 16:31:36 +0000 +Subject: [PATCH 207/293] migration: add support to migrate shared regions list + +cherry-picked from https://github.com/AMDESE/qemu/commit/9236f522e48b6. + +When memory encryption is enabled, the hypervisor maintains a shared +regions list which is referred by hypervisor during migration to check +if page is private or shared. This list is built during the VM bootup and +must be migrated to the target host so that hypervisor on target host can +use it for future migration. + +Signed-off-by: Brijesh Singh +Co-developed-by: Ashish Kalra +Signed-off-by: Ashish Kalra +[ Fix conflicts. ] +Signed-off-by: hanliyang +--- + include/exec/confidential-guest-support.h | 2 +- + target/i386/sev.c | 45 +++++++++++++++++++++++++++++++ + target/i386/sev.h | 2 ++ + 3 files changed, 48 insertions(+), 1 deletion(-) + +diff --git a/include/exec/confidential-guest-support.h b/include/exec/confidential-guest-support.h +index 343f686..dd4887f 100644 +--- a/include/exec/confidential-guest-support.h ++++ b/include/exec/confidential-guest-support.h +@@ -73,7 +73,7 @@ struct ConfidentialGuestMemoryEncryptionOps { + bool (*is_gfn_in_unshared_region)(unsigned long gfn); + + /* Write the shared regions list */ +- int (*save_outgoing_shared_regions_list)(QEMUFile *f); ++ int (*save_outgoing_shared_regions_list)(QEMUFile *f, uint64_t *bytes_sent); + + /* Load the shared regions list */ + int (*load_incoming_shared_regions_list)(QEMUFile *f); +diff --git a/target/i386/sev.c b/target/i386/sev.c +index bd00a28..aeb3e32 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -176,10 +176,15 @@ static const char *const sev_fw_errlist[] = { + + #define SEV_FW_BLOB_MAX_SIZE 0x4000 /* 16KB */ + ++#define SHARED_REGION_LIST_CONT 0x1 ++#define SHARED_REGION_LIST_END 0x2 ++ + static struct ConfidentialGuestMemoryEncryptionOps sev_memory_encryption_ops = { + .save_setup = sev_save_setup, + .save_outgoing_page = sev_save_outgoing_page, + .load_incoming_page = sev_load_incoming_page, ++ .save_outgoing_shared_regions_list = sev_save_outgoing_shared_regions_list, ++ .load_incoming_shared_regions_list = sev_load_incoming_shared_regions_list, + }; + + static int +@@ -1776,6 +1781,46 @@ int sev_add_shared_regions_list(unsigned long start, unsigned long end) + return 1; + } + ++int sev_save_outgoing_shared_regions_list(QEMUFile *f, uint64_t *bytes_sent) ++{ ++ SevGuestState *s = sev_guest; ++ struct shared_region *pos; ++ ++ QTAILQ_FOREACH(pos, &s->shared_regions_list, list) { ++ qemu_put_be32(f, SHARED_REGION_LIST_CONT); ++ qemu_put_be32(f, pos->gfn_start); ++ qemu_put_be32(f, pos->gfn_end); ++ *bytes_sent += 12; ++ } ++ ++ qemu_put_be32(f, SHARED_REGION_LIST_END); ++ *bytes_sent += 4; ++ return 0; ++} ++ ++int sev_load_incoming_shared_regions_list(QEMUFile *f) ++{ ++ SevGuestState *s = sev_guest; ++ struct shared_region *shrd_region; ++ int status; ++ ++ status = qemu_get_be32(f); ++ while (status == SHARED_REGION_LIST_CONT) { ++ ++ shrd_region = g_malloc0(sizeof(*shrd_region)); ++ if (!shrd_region) { ++ return 0; ++ } ++ shrd_region->gfn_start = qemu_get_be32(f); ++ shrd_region->gfn_end = qemu_get_be32(f); ++ ++ QTAILQ_INSERT_TAIL(&s->shared_regions_list, shrd_region, list); ++ ++ status = qemu_get_be32(f); ++ } ++ return 0; ++} ++ + static const QemuUUID sev_hash_table_header_guid = { + .data = UUID_LE(0x9438d606, 0x4f22, 0x4cc9, 0xb4, 0x79, 0xa7, 0x93, + 0xd4, 0x11, 0xfd, 0x21) +diff --git a/target/i386/sev.h b/target/i386/sev.h +index acf69d4..5b4231c 100644 +--- a/target/i386/sev.h ++++ b/target/i386/sev.h +@@ -64,6 +64,8 @@ void sev_es_set_reset_vector(CPUState *cpu); + int sev_remove_shared_regions_list(unsigned long gfn_start, + unsigned long gfn_end); + int sev_add_shared_regions_list(unsigned long gfn_start, unsigned long gfn_end); ++int sev_save_outgoing_shared_regions_list(QEMUFile *f, uint64_t *bytes_sent); ++int sev_load_incoming_shared_regions_list(QEMUFile *f); + + int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp); + +-- +1.8.3.1 + diff --git a/0198-migration-ram-add-support-to-send-encrypted-pages.patch b/0198-migration-ram-add-support-to-send-encrypted-pages.patch new file mode 100644 index 0000000000000000000000000000000000000000..d3e682d557b156e7e2331eff244d6ce977c41ea6 --- /dev/null +++ b/0198-migration-ram-add-support-to-send-encrypted-pages.patch @@ -0,0 +1,343 @@ +From 2b93c1ead8fdf9730e6d2ea5799bac409c87825c Mon Sep 17 00:00:00 2001 +From: Brijesh Singh +Date: Tue, 27 Jul 2021 16:53:19 +0000 +Subject: [PATCH 208/293] migration/ram: add support to send encrypted pages + +cherry-picked from https://github.com/AMDESE/qemu/commit/2d6bda0d4cf. + +When memory encryption is enabled, the guest memory will be encrypted with +the guest specific key. The patch introduces RAM_SAVE_FLAG_ENCRYPTED_PAGE +flag to distinguish the encrypted data from plaintext. Encrypted pages +may need special handling. The sev_save_outgoing_page() is used +by the sender to write the encrypted pages onto the socket, similarly the +sev_load_incoming_page() is used by the target to read the +encrypted pages from the socket and load into the guest memory. + +Signed-off-by: Brijesh Singh +Co-developed-by: Ashish Kalra +Signed-off-by: Ashish Kalra +[ Fix conflicts. ] +Signed-off-by: hanliyang +--- + migration/migration.h | 2 + + migration/ram.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++++- + target/i386/sev.c | 14 ++++ + target/i386/sev.h | 4 ++ + 4 files changed, 192 insertions(+), 2 deletions(-) + +diff --git a/migration/migration.h b/migration/migration.h +index cf2c9c8..65f5599 100644 +--- a/migration/migration.h ++++ b/migration/migration.h +@@ -550,4 +550,6 @@ void migration_rp_kick(MigrationState *s); + + int migration_stop_vm(RunState state); + ++bool memcrypt_enabled(void); ++ + #endif +diff --git a/migration/ram.c b/migration/ram.c +index 8c7886a..317aea5 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -63,6 +63,10 @@ + #include "options.h" + #include "sysemu/dirtylimit.h" + #include "sysemu/kvm.h" ++#include "exec/confidential-guest-support.h" ++ ++/* Defines RAM_SAVE_ENCRYPTED_PAGE and RAM_SAVE_SHARED_REGION_LIST */ ++#include "target/i386/sev.h" + + #include "hw/boards.h" /* for machine_dump_guest_core() */ + +@@ -92,7 +96,16 @@ + /* 0x80 is reserved in rdma.h for RAM_SAVE_FLAG_HOOK */ + #define RAM_SAVE_FLAG_COMPRESS_PAGE 0x100 + #define RAM_SAVE_FLAG_MULTIFD_FLUSH 0x200 +-/* We can't use any flag that is bigger than 0x200 */ ++#define RAM_SAVE_FLAG_ENCRYPTED_DATA 0x400 ++ ++bool memcrypt_enabled(void) ++{ ++ MachineState *ms = MACHINE(qdev_get_machine()); ++ if(ms->cgs) ++ return ms->cgs->ready; ++ else ++ return false; ++} + + XBZRLECacheStats xbzrle_counters; + +@@ -1205,6 +1218,88 @@ static int save_normal_page(PageSearchStatus *pss, RAMBlock *block, + } + + /** ++ * ram_save_encrypted_page - send the given encrypted page to the stream ++ */ ++static int ram_save_encrypted_page(RAMState *rs, PageSearchStatus *pss) ++{ ++ QEMUFile *file = pss->pss_channel; ++ int ret; ++ uint8_t *p; ++ RAMBlock *block = pss->block; ++ ram_addr_t offset = ((ram_addr_t)pss->page) << TARGET_PAGE_BITS; ++ uint64_t bytes_xmit = 0; ++ MachineState *ms = MACHINE(qdev_get_machine()); ++ ConfidentialGuestSupportClass *cgs_class = ++ (ConfidentialGuestSupportClass *) object_get_class(OBJECT(ms->cgs)); ++ struct ConfidentialGuestMemoryEncryptionOps *ops = ++ cgs_class->memory_encryption_ops; ++ ++ p = block->host + offset; ++ trace_ram_save_page(block->idstr, (uint64_t)offset, p); ++ ++ ram_transferred_add(save_page_header(pss, file, block, ++ offset | RAM_SAVE_FLAG_ENCRYPTED_DATA)); ++ qemu_put_be32(file, RAM_SAVE_ENCRYPTED_PAGE); ++ ret = ops->save_outgoing_page(file, p, TARGET_PAGE_SIZE, &bytes_xmit); ++ if (ret) { ++ return -1; ++ } ++ ram_transferred_add(4 + bytes_xmit); ++ stat64_add(&mig_stats.normal_pages, 1); ++ ++ return 1; ++} ++ ++/** ++ * ram_save_shared_region_list: send the shared region list ++ */ ++static int ram_save_shared_region_list(RAMState *rs, QEMUFile *f) ++{ ++ int ret; ++ uint64_t bytes_xmit = 0; ++ PageSearchStatus *pss = &rs->pss[RAM_CHANNEL_PRECOPY]; ++ MachineState *ms = MACHINE(qdev_get_machine()); ++ ConfidentialGuestSupportClass *cgs_class = ++ (ConfidentialGuestSupportClass *) object_get_class(OBJECT(ms->cgs)); ++ struct ConfidentialGuestMemoryEncryptionOps *ops = ++ cgs_class->memory_encryption_ops; ++ ++ ram_transferred_add(save_page_header(pss, f, ++ pss->last_sent_block, ++ RAM_SAVE_FLAG_ENCRYPTED_DATA)); ++ qemu_put_be32(f, RAM_SAVE_SHARED_REGIONS_LIST); ++ ret = ops->save_outgoing_shared_regions_list(f, &bytes_xmit); ++ if (ret < 0) { ++ return ret; ++ } ++ ram_transferred_add(4 + bytes_xmit); ++ ++ return 0; ++} ++ ++static int load_encrypted_data(QEMUFile *f, uint8_t *ptr) ++{ ++ MachineState *ms = MACHINE(qdev_get_machine()); ++ ConfidentialGuestSupportClass *cgs_class = ++ (ConfidentialGuestSupportClass *) object_get_class(OBJECT(ms->cgs)); ++ struct ConfidentialGuestMemoryEncryptionOps *ops = ++ cgs_class->memory_encryption_ops; ++ ++ int flag; ++ ++ flag = qemu_get_be32(f); ++ ++ if (flag == RAM_SAVE_ENCRYPTED_PAGE) { ++ return ops->load_incoming_page(f, ptr); ++ } else if (flag == RAM_SAVE_SHARED_REGIONS_LIST) { ++ return ops->load_incoming_shared_regions_list(f); ++ } else { ++ error_report("unknown encrypted flag %x", flag); ++ return 1; ++ } ++} ++ ++/** + * ram_save_page: send the given page to the stream + * + * Returns the number of pages written. +@@ -2035,6 +2130,35 @@ static bool save_compress_page(RAMState *rs, PageSearchStatus *pss, + } + + /** ++ * encrypted_test_list: check if the page is encrypted ++ * ++ * Returns a bool indicating whether the page is encrypted. ++ */ ++static bool encrypted_test_list(RAMState *rs, RAMBlock *block, ++ unsigned long page) ++{ ++ MachineState *ms = MACHINE(qdev_get_machine()); ++ ConfidentialGuestSupportClass *cgs_class = ++ (ConfidentialGuestSupportClass *) object_get_class(OBJECT(ms->cgs)); ++ struct ConfidentialGuestMemoryEncryptionOps *ops = ++ cgs_class->memory_encryption_ops; ++ unsigned long gfn; ++ ++ /* ROM devices contains the unencrypted data */ ++ if (memory_region_is_rom(block->mr)) { ++ return false; ++ } ++ ++ /* ++ * Translate page in ram_addr_t address space to GPA address ++ * space using memory region. ++ */ ++ gfn = page + (block->mr->addr >> TARGET_PAGE_BITS); ++ ++ return ops->is_gfn_in_unshared_region(gfn); ++} ++ ++/** + * ram_save_target_page_legacy: save one target page + * + * Returns the number of pages written +@@ -2052,6 +2176,17 @@ static int ram_save_target_page_legacy(RAMState *rs, PageSearchStatus *pss) + return res; + } + ++ /* ++ * If memory encryption is enabled then use memory encryption APIs ++ * to write the outgoing buffer to the wire. The encryption APIs ++ * will take care of accessing the guest memory and re-encrypt it ++ * for the transport purposes. ++ */ ++ if (memcrypt_enabled() && ++ encrypted_test_list(rs, pss->block, pss->page)) { ++ return ram_save_encrypted_page(rs, pss); ++ } ++ + if (save_compress_page(rs, pss, offset)) { + return 1; + } +@@ -2917,6 +3052,18 @@ void qemu_guest_free_page_hint(void *addr, size_t len) + } + } + ++static int ram_encrypted_save_setup(void) ++{ ++ MachineState *ms = MACHINE(qdev_get_machine()); ++ ConfidentialGuestSupportClass *cgs_class = ++ (ConfidentialGuestSupportClass *) object_get_class(OBJECT(ms->cgs)); ++ struct ConfidentialGuestMemoryEncryptionOps *ops = ++ cgs_class->memory_encryption_ops; ++ MigrationParameters *p = &migrate_get_current()->parameters; ++ ++ return ops->save_setup(p->sev_pdh, p->sev_plat_cert, p->sev_amd_cert); ++} ++ + /* + * Each of ram_save_setup, ram_save_iterate and ram_save_complete has + * long-running RCU critical section. When rcu-reclaims in the code +@@ -2952,6 +3099,13 @@ static int ram_save_setup(QEMUFile *f, void *opaque) + (*rsp)->pss[RAM_CHANNEL_PRECOPY].pss_channel = f; + + WITH_RCU_READ_LOCK_GUARD() { ++ ++ if (memcrypt_enabled()) { ++ if (ram_encrypted_save_setup()) { ++ return -1; ++ } ++ } ++ + qemu_put_be64(f, ram_bytes_total_with_ignored() + | RAM_SAVE_FLAG_MEM_SIZE); + +@@ -3181,6 +3335,15 @@ static int ram_save_complete(QEMUFile *f, void *opaque) + qemu_file_set_error(f, ret); + return ret; + } ++ ++ /* send the shared regions list */ ++ if (memcrypt_enabled()) { ++ ret = ram_save_shared_region_list(rs, f); ++ if (ret < 0) { ++ qemu_file_set_error(f, ret); ++ return ret; ++ } ++ } + } + + ret = multifd_send_sync_main(rs->pss[RAM_CHANNEL_PRECOPY].pss_channel); +@@ -3918,7 +4081,8 @@ static int ram_load_precopy(QEMUFile *f) + } + + if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE | +- RAM_SAVE_FLAG_COMPRESS_PAGE | RAM_SAVE_FLAG_XBZRLE)) { ++ RAM_SAVE_FLAG_COMPRESS_PAGE | RAM_SAVE_FLAG_XBZRLE | ++ RAM_SAVE_FLAG_ENCRYPTED_DATA)) { + RAMBlock *block = ram_block_from_stream(mis, f, flags, + RAM_CHANNEL_PRECOPY); + +@@ -4011,6 +4175,12 @@ static int ram_load_precopy(QEMUFile *f) + qemu_file_set_error(f, ret); + } + break; ++ case RAM_SAVE_FLAG_ENCRYPTED_DATA: ++ if (load_encrypted_data(f, host)) { ++ error_report("Failed to load encrypted data"); ++ ret = -EINVAL; ++ } ++ break; + default: + error_report("Unknown combination of migration flags: 0x%x", flags); + ret = -EINVAL; +diff --git a/target/i386/sev.c b/target/i386/sev.c +index aeb3e32..8511756 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -183,6 +183,7 @@ static struct ConfidentialGuestMemoryEncryptionOps sev_memory_encryption_ops = { + .save_setup = sev_save_setup, + .save_outgoing_page = sev_save_outgoing_page, + .load_incoming_page = sev_load_incoming_page, ++ .is_gfn_in_unshared_region = sev_is_gfn_in_unshared_region, + .save_outgoing_shared_regions_list = sev_save_outgoing_shared_regions_list, + .load_incoming_shared_regions_list = sev_load_incoming_shared_regions_list, + }; +@@ -1821,6 +1822,19 @@ int sev_load_incoming_shared_regions_list(QEMUFile *f) + return 0; + } + ++bool sev_is_gfn_in_unshared_region(unsigned long gfn) ++{ ++ SevGuestState *s = sev_guest; ++ struct shared_region *pos; ++ ++ QTAILQ_FOREACH(pos, &s->shared_regions_list, list) { ++ if (gfn >= pos->gfn_start && gfn < pos->gfn_end) { ++ return false; ++ } ++ } ++ return true; ++} ++ + static const QemuUUID sev_hash_table_header_guid = { + .data = UUID_LE(0x9438d606, 0x4f22, 0x4cc9, 0xb4, 0x79, 0xa7, 0x93, + 0xd4, 0x11, 0xfd, 0x21) +diff --git a/target/i386/sev.h b/target/i386/sev.h +index 5b4231c..b9c2afb 100644 +--- a/target/i386/sev.h ++++ b/target/i386/sev.h +@@ -38,6 +38,9 @@ typedef struct SevKernelLoaderContext { + size_t cmdline_size; + } SevKernelLoaderContext; + ++#define RAM_SAVE_ENCRYPTED_PAGE 0x1 ++#define RAM_SAVE_SHARED_REGIONS_LIST 0x2 ++ + #ifdef CONFIG_SEV + bool sev_enabled(void); + bool sev_es_enabled(void); +@@ -66,6 +69,7 @@ int sev_remove_shared_regions_list(unsigned long gfn_start, + int sev_add_shared_regions_list(unsigned long gfn_start, unsigned long gfn_end); + int sev_save_outgoing_shared_regions_list(QEMUFile *f, uint64_t *bytes_sent); + int sev_load_incoming_shared_regions_list(QEMUFile *f); ++bool sev_is_gfn_in_unshared_region(unsigned long gfn); + + int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp); + +-- +1.8.3.1 + diff --git a/0199-migration-ram-Force-encrypted-status-for-flash0-flas.patch b/0199-migration-ram-Force-encrypted-status-for-flash0-flas.patch new file mode 100644 index 0000000000000000000000000000000000000000..f71a1a631f3d8ca6bcd48ccb28b88826e45f66e6 --- /dev/null +++ b/0199-migration-ram-Force-encrypted-status-for-flash0-flas.patch @@ -0,0 +1,44 @@ +From 388bb911357c1cf7d2b03e31bebe9670e3139408 Mon Sep 17 00:00:00 2001 +From: Ashish Kalra +Date: Tue, 27 Jul 2021 18:05:25 +0000 +Subject: [PATCH 209/293] migration/ram: Force encrypted status for flash0 & + flash1 devices. + +cherry-picked from https://github.com/AMDESE/qemu/commit/803d6a4c8d. + +Currently OVMF clears the C-bit and marks NonExistent memory space +as decrypted in the page encryption bitmap. By marking the +NonExistent memory space as decrypted it gurantees any future MMIO adds +will work correctly, but this marks flash0 device space as decrypted. +At reset the SEV core will be in forced encrypted state, so this +decrypted marking of flash0 device space will cause VCPU reset to fail +as flash0 device pages will be migrated incorrectly. + +Signed-off-by: Ashish Kalra +Signed-off-by: hanliyang +--- + migration/ram.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/migration/ram.c b/migration/ram.c +index 317aea5..09faa85 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -2149,6 +2149,14 @@ static bool encrypted_test_list(RAMState *rs, RAMBlock *block, + return false; + } + ++ if (!strcmp(memory_region_name(block->mr), "system.flash0")) { ++ return true; ++ } ++ ++ if (!strcmp(memory_region_name(block->mr), "system.flash1")) { ++ return false; ++ } ++ + /* + * Translate page in ram_addr_t address space to GPA address + * space using memory region. +-- +1.8.3.1 + diff --git a/0200-kvm-Add-support-for-userspace-MSR-filtering-and-hand.patch b/0200-kvm-Add-support-for-userspace-MSR-filtering-and-hand.patch new file mode 100644 index 0000000000000000000000000000000000000000..e78204a999b02fae68db7f0fb133b67986f516d5 --- /dev/null +++ b/0200-kvm-Add-support-for-userspace-MSR-filtering-and-hand.patch @@ -0,0 +1,123 @@ +From bfa2537c9ae8d8db06cb6fd585a556b2104b4c4d Mon Sep 17 00:00:00 2001 +From: Ashish Kalra +Date: Tue, 27 Jul 2021 17:59:33 +0000 +Subject: [PATCH 210/293] kvm: Add support for userspace MSR filtering and + handling of MSR_KVM_MIGRATION_CONTROL. + +cherry-picked from https://github.com/AMDESE/qemu/commit/67935c3fd5f. + +Add support for userspace MSR filtering using KVM_X86_SET_MSR_FILTER +ioctl and handling of MSRs in userspace. Currently this is only used +for SEV guests which use MSR_KVM_MIGRATION_CONTROL to indicate if the +guest is enabled and ready for migration. + +KVM arch code calls into SEV guest specific code to delete the +SEV migrate blocker which has been setup at SEV_LAUNCH_FINISH. + +Signed-off-by: Ashish Kalra +[ Fix conflicts. ] +Signed-off-by: hanliyang +--- + target/i386/kvm/kvm.c | 35 +++++++++++++++++++++++++++++++++++ + target/i386/kvm/sev-stub.c | 4 ++++ + target/i386/sev.c | 6 ++++++ + target/i386/sev.h | 1 + + 4 files changed, 46 insertions(+) + +diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c +index 82f6d3b..a5a755d 100644 +--- a/target/i386/kvm/kvm.c ++++ b/target/i386/kvm/kvm.c +@@ -2488,6 +2488,32 @@ static bool kvm_rdmsr_core_thread_count(X86CPU *cpu, uint32_t msr, + return true; + } + ++/* ++ * Currently this exit is only used by SEV guests for ++ * MSR_KVM_MIGRATION_CONTROL to indicate if the guest ++ * is ready for migration. ++ */ ++static uint64_t msr_kvm_migration_control; ++ ++static bool kvm_rdmsr_kvm_migration_control(X86CPU *cpu, uint32_t msr, ++ uint64_t *val) ++{ ++ *val = msr_kvm_migration_control; ++ ++ return true; ++} ++ ++static bool kvm_wrmsr_kvm_migration_control(X86CPU *cpu, uint32_t msr, ++ uint64_t val) ++{ ++ msr_kvm_migration_control = val; ++ ++ if (val == KVM_MIGRATION_READY) ++ sev_del_migrate_blocker(); ++ ++ return true; ++} ++ + static Notifier smram_machine_done; + static KVMMemoryListener smram_listener; + static AddressSpace smram_address_space; +@@ -2735,6 +2761,15 @@ int kvm_arch_init(MachineState *ms, KVMState *s) + strerror(-ret)); + exit(1); + } ++ ++ r = kvm_filter_msr(s, MSR_KVM_MIGRATION_CONTROL, ++ kvm_rdmsr_kvm_migration_control, ++ kvm_wrmsr_kvm_migration_control); ++ if (!r) { ++ error_report("Could not install MSR_KVM_MIGRATION_CONTROL handler: %s", ++ strerror(-ret)); ++ exit(1); ++ } + } + + return 0; +diff --git a/target/i386/kvm/sev-stub.c b/target/i386/kvm/sev-stub.c +index 1282d24..9989968 100644 +--- a/target/i386/kvm/sev-stub.c ++++ b/target/i386/kvm/sev-stub.c +@@ -30,3 +30,7 @@ int sev_add_shared_regions_list(unsigned long gfn_start, unsigned long gfn_end) + { + return 0; + } ++ ++void sev_del_migrate_blocker(void) ++{ ++} +diff --git a/target/i386/sev.c b/target/i386/sev.c +index 8511756..aeeb479 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -925,6 +925,12 @@ sev_launch_finish(SevGuestState *sev) + migrate_add_blocker(&sev_mig_blocker, &error_fatal); + } + ++void ++sev_del_migrate_blocker(void) ++{ ++ migrate_del_blocker(&sev_mig_blocker); ++} ++ + static int + sev_receive_finish(SevGuestState *s) + { +diff --git a/target/i386/sev.h b/target/i386/sev.h +index b9c2afb..84e3bdf 100644 +--- a/target/i386/sev.h ++++ b/target/i386/sev.h +@@ -70,6 +70,7 @@ int sev_add_shared_regions_list(unsigned long gfn_start, unsigned long gfn_end); + int sev_save_outgoing_shared_regions_list(QEMUFile *f, uint64_t *bytes_sent); + int sev_load_incoming_shared_regions_list(QEMUFile *f); + bool sev_is_gfn_in_unshared_region(unsigned long gfn); ++void sev_del_migrate_blocker(void); + + int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp); + +-- +1.8.3.1 + diff --git a/0201-target-i386-sev-Return-0-if-sev_send_get_packet_len-.patch b/0201-target-i386-sev-Return-0-if-sev_send_get_packet_len-.patch new file mode 100644 index 0000000000000000000000000000000000000000..dc9d8048e4c4c51d52ec0b30e4750c702dbb41bd --- /dev/null +++ b/0201-target-i386-sev-Return-0-if-sev_send_get_packet_len-.patch @@ -0,0 +1,60 @@ +From ddc1237fe7657246829340cca45f855030cb7159 Mon Sep 17 00:00:00 2001 +From: hanliyang +Date: Wed, 31 Jan 2024 07:26:57 +0800 +Subject: [PATCH 211/293] target/i386: sev: Return 0 if + sev_send_get_packet_len() fails + +The send_packet_hdr_len of struct SEVState is of type size_t +which is an unsigned class type. If the send_packet_hdr_len +is assigned as -1, then it will be a huge number and the QEMU +process will crash when allocating packet buffer with the +huge size. + +For example, the following code could cause crash described +above. + + ``` + static int + sev_send_update_data(SEVState *s, QEMUFile *f, uint8_t *ptr, uint32_t size, + uint64_t *bytes_sent) + { + + ...... + + if (!s->send_packet_hdr) { + s->send_packet_hdr_len = sev_send_get_packet_len(&fw_error); + if (s->send_packet_hdr_len < 1) { + error_report("%s: SEND_UPDATE fw_error=%d '%s'", + __func__, fw_error, fw_error_to_str(fw_error)); + return 1; + } + + s->send_packet_hdr = g_new(gchar, s->send_packet_hdr_len); + } + + ...... + + } + ``` + +Signed-off-by: hanliyang +--- + target/i386/sev.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/target/i386/sev.c b/target/i386/sev.c +index aeeb479..b1b26b8 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -1491,7 +1491,7 @@ sev_send_get_packet_len(int *fw_err) + ret = sev_ioctl(sev_guest->sev_fd, KVM_SEV_SEND_UPDATE_DATA, + &update, fw_err); + if (*fw_err != SEV_RET_INVALID_LEN) { +- ret = -1; ++ ret = 0; + error_report("%s: failed to get session length ret=%d fw_error=%d '%s'", + __func__, ret, *fw_err, fw_error_to_str(*fw_err)); + goto err; +-- +1.8.3.1 + diff --git a/0202-migration-ram-Force-encrypted-status-for-VGA-vram.patch b/0202-migration-ram-Force-encrypted-status-for-VGA-vram.patch new file mode 100644 index 0000000000000000000000000000000000000000..64b489f847af680ba4be9725223a3238465c9b5f --- /dev/null +++ b/0202-migration-ram-Force-encrypted-status-for-VGA-vram.patch @@ -0,0 +1,32 @@ +From 478d0a924da5e32ba7b707c98798f25d66e80c42 Mon Sep 17 00:00:00 2001 +From: hanliyang +Date: Tue, 8 Dec 2020 22:57:46 -0500 +Subject: [PATCH 212/293] migration/ram: Force encrypted status for VGA vram + +The VGA vram memory region act as frame buffer of VM. This memory +is decrypted in the QEMU process. For CSV VM live migration, we +should avoid memory encryption status check on VGA vram. + +Signed-off-by: hanliyang +--- + migration/ram.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/migration/ram.c b/migration/ram.c +index 09faa85..f711738 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -2157,6 +2157,10 @@ static bool encrypted_test_list(RAMState *rs, RAMBlock *block, + return false; + } + ++ if (!strcmp(memory_region_name(block->mr), "vga.vram")) { ++ return false; ++ } ++ + /* + * Translate page in ram_addr_t address space to GPA address + * space using memory region. +-- +1.8.3.1 + diff --git a/0203-target-i386-sev-Clear-shared_regions_list-when-reboo.patch b/0203-target-i386-sev-Clear-shared_regions_list-when-reboo.patch new file mode 100644 index 0000000000000000000000000000000000000000..960299866a771932dd278b8f754c763f2a0cfd64 --- /dev/null +++ b/0203-target-i386-sev-Clear-shared_regions_list-when-reboo.patch @@ -0,0 +1,57 @@ +From 1a43356669c8b2e2b8d0b7168d3f9cddc84cac9a Mon Sep 17 00:00:00 2001 +From: hanliyang +Date: Sun, 16 Jan 2022 19:57:58 -0500 +Subject: [PATCH 213/293] target/i386: sev: Clear shared_regions_list when + reboot CSV Guest + +Also fix memory leak in sev_remove_shared_regions_list(). + +Signed-off-by: hanliyang +--- + target/i386/kvm/kvm.c | 5 +++++ + target/i386/sev.c | 5 +++-- + 2 files changed, 8 insertions(+), 2 deletions(-) + +diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c +index a5a755d..5730d0e 100644 +--- a/target/i386/kvm/kvm.c ++++ b/target/i386/kvm/kvm.c +@@ -2270,6 +2270,11 @@ void kvm_arch_reset_vcpu(X86CPU *cpu) + env->mp_state = KVM_MP_STATE_RUNNABLE; + } + ++ if (cpu_is_bsp(cpu) && ++ sev_enabled() && has_map_gpa_range) { ++ sev_remove_shared_regions_list(0, -1); ++ } ++ + /* enabled by default */ + env->poll_control_msr = 1; + +diff --git a/target/i386/sev.c b/target/i386/sev.c +index b1b26b8..594f034 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -1693,9 +1693,9 @@ int sev_load_incoming_page(QEMUFile *f, uint8_t *ptr) + int sev_remove_shared_regions_list(unsigned long start, unsigned long end) + { + SevGuestState *s = sev_guest; +- struct shared_region *pos; ++ struct shared_region *pos, *next_pos; + +- QTAILQ_FOREACH(pos, &s->shared_regions_list, list) { ++ QTAILQ_FOREACH_SAFE(pos, &s->shared_regions_list, list, next_pos) { + unsigned long l, r; + unsigned long curr_gfn_end = pos->gfn_end; + +@@ -1709,6 +1709,7 @@ int sev_remove_shared_regions_list(unsigned long start, unsigned long end) + if (l <= r) { + if (pos->gfn_start == l && pos->gfn_end == r) { + QTAILQ_REMOVE(&s->shared_regions_list, pos, list); ++ g_free(pos); + } else if (l == pos->gfn_start) { + pos->gfn_start = r; + } else if (r == pos->gfn_end) { +-- +1.8.3.1 + diff --git a/0204-migration-ram-Fix-calculation-of-gfn-correpond-to-a-.patch b/0204-migration-ram-Fix-calculation-of-gfn-correpond-to-a-.patch new file mode 100644 index 0000000000000000000000000000000000000000..2dbeb7699dbc4e1cf06fc4023dd41edcce709b32 --- /dev/null +++ b/0204-migration-ram-Fix-calculation-of-gfn-correpond-to-a-.patch @@ -0,0 +1,57 @@ +From 982df892d3b3896a8379447bc8b85ce84d733a1f Mon Sep 17 00:00:00 2001 +From: hanliyang +Date: Sun, 16 Jan 2022 20:05:02 -0500 +Subject: [PATCH 214/293] migration/ram: Fix calculation of gfn correpond to a + page in ramblock + +A RAMBlock contains a host memory region which may consist of many +discontiguous MemoryRegion in AddressSpace of a Guest, so we cannot +get gpa by MemoryRegion.addr. Since KVM memslot records the relationship +between gpa and hva, so we can pass the hva of page in RAMBlock to +kvm_phisical_memory_addr_from_host() to get the expected gpa. + +Signed-off-by: hanliyang +--- + migration/ram.c | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +diff --git a/migration/ram.c b/migration/ram.c +index f711738..22f07a0 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -67,6 +67,7 @@ + + /* Defines RAM_SAVE_ENCRYPTED_PAGE and RAM_SAVE_SHARED_REGION_LIST */ + #include "target/i386/sev.h" ++#include "sysemu/kvm.h" + + #include "hw/boards.h" /* for machine_dump_guest_core() */ + +@@ -2143,6 +2144,8 @@ static bool encrypted_test_list(RAMState *rs, RAMBlock *block, + struct ConfidentialGuestMemoryEncryptionOps *ops = + cgs_class->memory_encryption_ops; + unsigned long gfn; ++ hwaddr paddr = 0; ++ int ret; + + /* ROM devices contains the unencrypted data */ + if (memory_region_is_rom(block->mr)) { +@@ -2165,7 +2168,14 @@ static bool encrypted_test_list(RAMState *rs, RAMBlock *block, + * Translate page in ram_addr_t address space to GPA address + * space using memory region. + */ +- gfn = page + (block->mr->addr >> TARGET_PAGE_BITS); ++ if (kvm_enabled()) { ++ ret = kvm_physical_memory_addr_from_host(kvm_state, ++ block->host + (page << TARGET_PAGE_BITS), &paddr); ++ if (ret == 0) { ++ return false; ++ } ++ } ++ gfn = paddr >> TARGET_PAGE_BITS; + + return ops->is_gfn_in_unshared_region(gfn); + } +-- +1.8.3.1 + diff --git a/0205-target-i386-Introduce-header-file-csv.h.patch b/0205-target-i386-Introduce-header-file-csv.h.patch new file mode 100644 index 0000000000000000000000000000000000000000..88ff1531940facb90c3c69cc0f83e616a4c5e9a7 --- /dev/null +++ b/0205-target-i386-Introduce-header-file-csv.h.patch @@ -0,0 +1,107 @@ +From 576385738a1c163c11acba84594178ee47383fdd Mon Sep 17 00:00:00 2001 +From: hanliyang +Date: Thu, 14 Mar 2024 19:21:11 +0800 +Subject: [PATCH 215/293] target/i386: Introduce header file csv.h + +This header file is used to provide common helper functions +and data structures for Hygon CSV. + +Signed-off-by: hanliyang +--- + configs/devices/i386-softmmu/default.mak | 1 + + hw/i386/Kconfig | 5 ++++ + target/i386/csv.h | 47 ++++++++++++++++++++++++++++++++ + 3 files changed, 53 insertions(+) + create mode 100644 target/i386/csv.h + +diff --git a/configs/devices/i386-softmmu/default.mak b/configs/devices/i386-softmmu/default.mak +index 598c664..db83ffc 100644 +--- a/configs/devices/i386-softmmu/default.mak ++++ b/configs/devices/i386-softmmu/default.mak +@@ -23,6 +23,7 @@ + #CONFIG_TPM_TIS_ISA=n + #CONFIG_VTD=n + #CONFIG_SGX=n ++#CONFIG_CSV=n + + # Boards: + # +diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig +index 5585079..08f3ae4 100644 +--- a/hw/i386/Kconfig ++++ b/hw/i386/Kconfig +@@ -10,6 +10,10 @@ config SGX + bool + depends on KVM + ++config CSV ++ bool ++ depends on SEV ++ + config PC + bool + imply APPLESMC +@@ -26,6 +30,7 @@ config PC + imply QXL + imply SEV + imply SGX ++ imply CSV + imply TEST_DEVICES + imply TPM_CRB + imply TPM_TIS_ISA +diff --git a/target/i386/csv.h b/target/i386/csv.h +new file mode 100644 +index 0000000..f935bab +--- /dev/null ++++ b/target/i386/csv.h +@@ -0,0 +1,47 @@ ++/* ++ * QEMU CSV support ++ * ++ * Copyright: Hygon Info Technologies Ltd. 2022 ++ * ++ * Author: ++ * Jiang Xin ++ * ++ * 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 I386_CSV_H ++#define I386_CSV_H ++ ++#ifdef CONFIG_CSV ++ ++#include "cpu.h" ++ ++#define CPUID_VENDOR_HYGON_EBX 0x6f677948 /* "Hygo" */ ++#define CPUID_VENDOR_HYGON_ECX 0x656e6975 /* "uine" */ ++#define CPUID_VENDOR_HYGON_EDX 0x6e65476e /* "nGen" */ ++ ++static bool __attribute__((unused)) is_hygon_cpu(void) ++{ ++ uint32_t ebx = 0; ++ uint32_t ecx = 0; ++ uint32_t edx = 0; ++ ++ host_cpuid(0, 0, NULL, &ebx, &ecx, &edx); ++ ++ if (ebx == CPUID_VENDOR_HYGON_EBX && ++ ecx == CPUID_VENDOR_HYGON_ECX && ++ edx == CPUID_VENDOR_HYGON_EDX) ++ return true; ++ else ++ return false; ++} ++ ++#else ++ ++#define is_hygon_cpu() (false) ++ ++#endif ++ ++#endif +-- +1.8.3.1 + diff --git a/0206-target-i386-csv-Read-cert-chain-from-file-when-prepa.patch b/0206-target-i386-csv-Read-cert-chain-from-file-when-prepa.patch new file mode 100644 index 0000000000000000000000000000000000000000..ef456de02fa6d652c54e3c0381103ba63bfb3cea --- /dev/null +++ b/0206-target-i386-csv-Read-cert-chain-from-file-when-prepa.patch @@ -0,0 +1,140 @@ +From 6cd1b91414784f2651c279814663e30d9b028e57 Mon Sep 17 00:00:00 2001 +From: hanliyang +Date: Mon, 13 Nov 2023 21:55:33 +0000 +Subject: [PATCH 216/293] target/i386: csv: Read cert chain from file when + prepared for CSV live migration + +The cert chain is too long when encoded with base64, use the filename +of cert chain instead of the encoded string when prepared for CSV live +migration. + +[ Fix conflicts. ] +Signed-off-by: hanliyang +--- + qapi/migration.json | 24 +++++++++++++++--------- + target/i386/sev.c | 30 ++++++++++++++++++++++++++---- + 2 files changed, 41 insertions(+), 13 deletions(-) + +diff --git a/qapi/migration.json b/qapi/migration.json +index d9b25ef..3c4724d 100644 +--- a/qapi/migration.json ++++ b/qapi/migration.json +@@ -874,14 +874,16 @@ + # @mode: Migration mode. See description in @MigMode. Default is 'normal'. + # (Since 8.2) + # +-# @sev-pdh: The target host platform diffie-hellman key encoded in base64 ++# @sev-pdh: The target host platform diffie-hellman key encoded in base64, or ++# pdh filename for hygon + # (Since 4.2) + # +-# @sev-plat-cert: The target host platform certificate chain encoded in base64 ++# @sev-plat-cert: The target host platform certificate chain encoded in base64, ++# or plat cert filename for hygon + # (Since 4.2) + # + # @sev-amd-cert: AMD certificate chain which include ASK and OCA encoded in +-# base64 (Since 4.2) ++# base64, or vendor cert filename for hygon (Since 4.2) + # + # Features: + # +@@ -1072,14 +1074,16 @@ + # @mode: Migration mode. See description in @MigMode. Default is 'normal'. + # (Since 8.2) + # +-# @sev-pdh: The target host platform diffie-hellman key encoded in base64 ++# @sev-pdh: The target host platform diffie-hellman key encoded in base64, or ++# pdh filename for hygon + # (Since 4.2) + # +-# @sev-plat-cert: The target host platform certificate chain encoded in base64 ++# @sev-plat-cert: The target host platform certificate chain encoded in base64, ++# or plat cert filename for hygon + # (Since 4.2) + # + # @sev-amd-cert: AMD certificate chain which include ASK and OCA encoded in +-# base64 (Since 4.2) ++# base64, or vendor cert filename for hygon (Since 4.2) + # + # Features: + # +@@ -1313,14 +1317,16 @@ + # @mode: Migration mode. See description in @MigMode. Default is 'normal'. + # (Since 8.2) + # +-# @sev-pdh: The target host platform diffie-hellman key encoded in base64 ++# @sev-pdh: The target host platform diffie-hellman key encoded in base64, or ++# pdh filename for hygon + # (Since 4.2) + # +-# @sev-plat-cert: The target host platform certificate chain encoded in base64 ++# @sev-plat-cert: The target host platform certificate chain encoded in base64, ++# or plat cert filename for hygon + # (Since 4.2) + # + # @sev-amd-cert: AMD certificate chain which include ASK and OCA encoded in +-# base64 (Since 4.2) ++# base64, or vendor cert filename for hygon (Since 4.2) + # + # Features: + # +diff --git a/target/i386/sev.c b/target/i386/sev.c +index 594f034..ab7893f 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -27,6 +27,7 @@ + #include "crypto/hash.h" + #include "sysemu/kvm.h" + #include "sev.h" ++#include "csv.h" + #include "sysemu/sysemu.h" + #include "sysemu/runstate.h" + #include "trace.h" +@@ -979,18 +980,39 @@ int sev_save_setup(const char *pdh, const char *plat_cert, + { + SevGuestState *s = sev_guest; + +- s->remote_pdh = g_base64_decode(pdh, &s->remote_pdh_len); ++ if (is_hygon_cpu()) { ++ if (sev_read_file_base64(pdh, &s->remote_pdh, ++ &s->remote_pdh_len) < 0) { ++ goto error; ++ } ++ } else { ++ s->remote_pdh = g_base64_decode(pdh, &s->remote_pdh_len); ++ } + if (!check_blob_length(s->remote_pdh_len)) { + goto error; + } + +- s->remote_plat_cert = g_base64_decode(plat_cert, +- &s->remote_plat_cert_len); ++ if (is_hygon_cpu()) { ++ if (sev_read_file_base64(plat_cert, &s->remote_plat_cert, ++ &s->remote_plat_cert_len) < 0) { ++ goto error; ++ } ++ } else { ++ s->remote_plat_cert = g_base64_decode(plat_cert, ++ &s->remote_plat_cert_len); ++ } + if (!check_blob_length(s->remote_plat_cert_len)) { + goto error; + } + +- s->amd_cert = g_base64_decode(amd_cert, &s->amd_cert_len); ++ if (is_hygon_cpu()) { ++ if (sev_read_file_base64(amd_cert, &s->amd_cert, ++ &s->amd_cert_len) < 0) { ++ goto error; ++ } ++ } else { ++ s->amd_cert = g_base64_decode(amd_cert, &s->amd_cert_len); ++ } + if (!check_blob_length(s->amd_cert_len)) { + goto error; + } +-- +1.8.3.1 + diff --git a/0207-target-i386-csv-add-support-to-queue-the-outgoing-pa.patch b/0207-target-i386-csv-add-support-to-queue-the-outgoing-pa.patch new file mode 100644 index 0000000000000000000000000000000000000000..bd0122c2a925e26c92bd094ce826c5626bbff872 --- /dev/null +++ b/0207-target-i386-csv-add-support-to-queue-the-outgoing-pa.patch @@ -0,0 +1,259 @@ +From 459ef550486fbf7211707bda276a348a9711e0da Mon Sep 17 00:00:00 2001 +From: fangbaoshun +Date: Mon, 2 Aug 2021 11:00:07 +0800 +Subject: [PATCH 217/293] target/i386: csv: add support to queue the outgoing + page into a list + +The csv_queue_outgoing_page() provide the implementation to queue the +guest private pages during transmission. The routines queues the outgoing +pages into a listi, and then issues the KVM_CSV_COMMAND_BATCH command to +encrypt the pages togather before writing them to the socket. + +Signed-off-by: hanliyang +--- + include/exec/confidential-guest-support.h | 3 + + linux-headers/linux/kvm.h | 6 ++ + target/i386/csv.h | 11 ++ + target/i386/sev.c | 161 ++++++++++++++++++++++++++++++ + 4 files changed, 181 insertions(+) + +diff --git a/include/exec/confidential-guest-support.h b/include/exec/confidential-guest-support.h +index dd4887f..8949568 100644 +--- a/include/exec/confidential-guest-support.h ++++ b/include/exec/confidential-guest-support.h +@@ -77,6 +77,9 @@ struct ConfidentialGuestMemoryEncryptionOps { + + /* Load the shared regions list */ + int (*load_incoming_shared_regions_list)(QEMUFile *f); ++ ++ /* Queue the encrypted page and metadata associated with it into a list */ ++ int (*queue_outgoing_page)(uint8_t *ptr, uint32_t size, uint64_t addr); + }; + + typedef struct ConfidentialGuestSupportClass { +diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h +index 9758e8f..a61be97 100644 +--- a/linux-headers/linux/kvm.h ++++ b/linux-headers/linux/kvm.h +@@ -2024,6 +2024,12 @@ struct kvm_sev_receive_update_data { + __u32 trans_len; + }; + ++struct kvm_csv_batch_list_node { ++ __u64 cmd_data_addr; ++ __u64 addr; ++ __u64 next_cmd_addr; ++}; ++ + #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) + #define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1) + #define KVM_DEV_ASSIGN_MASK_INTX (1 << 2) +diff --git a/target/i386/csv.h b/target/i386/csv.h +index f935bab..4c1ef20 100644 +--- a/target/i386/csv.h ++++ b/target/i386/csv.h +@@ -44,4 +44,15 @@ static bool __attribute__((unused)) is_hygon_cpu(void) + + #endif + ++typedef struct CsvBatchCmdList CsvBatchCmdList; ++typedef void (*CsvDestroyCmdNodeFn) (void *data); ++ ++struct CsvBatchCmdList { ++ struct kvm_csv_batch_list_node *head; ++ struct kvm_csv_batch_list_node *tail; ++ CsvDestroyCmdNodeFn destroy_fn; ++}; ++ ++int csv_queue_outgoing_page(uint8_t *ptr, uint32_t sz, uint64_t addr); ++ + #endif +diff --git a/target/i386/sev.c b/target/i386/sev.c +index ab7893f..027249e 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -95,6 +95,9 @@ struct SevGuestState { + bool reset_data_valid; + + QTAILQ_HEAD(, shared_region) shared_regions_list; ++ ++ /* link list used for HYGON CSV */ ++ CsvBatchCmdList *csv_batch_cmd_list; + }; + + #define DEFAULT_GUEST_POLICY 0x1 /* disable debug */ +@@ -187,6 +190,7 @@ static struct ConfidentialGuestMemoryEncryptionOps sev_memory_encryption_ops = { + .is_gfn_in_unshared_region = sev_is_gfn_in_unshared_region, + .save_outgoing_shared_regions_list = sev_save_outgoing_shared_regions_list, + .load_incoming_shared_regions_list = sev_load_incoming_shared_regions_list, ++ .queue_outgoing_page = csv_queue_outgoing_page, + }; + + static int +@@ -1864,6 +1868,163 @@ bool sev_is_gfn_in_unshared_region(unsigned long gfn) + return true; + } + ++static CsvBatchCmdList * ++csv_batch_cmd_list_create(struct kvm_csv_batch_list_node *head, ++ CsvDestroyCmdNodeFn func) ++{ ++ CsvBatchCmdList *csv_batch_cmd_list = ++ g_malloc0(sizeof(*csv_batch_cmd_list)); ++ ++ if (!csv_batch_cmd_list) { ++ return NULL; ++ } ++ ++ csv_batch_cmd_list->head = head; ++ csv_batch_cmd_list->tail = head; ++ csv_batch_cmd_list->destroy_fn = func; ++ ++ return csv_batch_cmd_list; ++} ++ ++static int ++csv_batch_cmd_list_add_after(CsvBatchCmdList *list, ++ struct kvm_csv_batch_list_node *new_node) ++{ ++ list->tail->next_cmd_addr = (__u64)new_node; ++ list->tail = new_node; ++ ++ return 0; ++} ++ ++static struct kvm_csv_batch_list_node * ++csv_batch_cmd_list_node_create(uint64_t cmd_data_addr, uint64_t addr) ++{ ++ struct kvm_csv_batch_list_node *new_node = ++ g_malloc0(sizeof(struct kvm_csv_batch_list_node)); ++ ++ if (!new_node) { ++ return NULL; ++ } ++ ++ new_node->cmd_data_addr = cmd_data_addr; ++ new_node->addr = addr; ++ new_node->next_cmd_addr = 0; ++ ++ return new_node; ++} ++ ++static int csv_batch_cmd_list_destroy(CsvBatchCmdList *list) ++{ ++ struct kvm_csv_batch_list_node *node = list->head; ++ ++ while (node != NULL) { ++ if (list->destroy_fn != NULL) ++ list->destroy_fn((void *)node->cmd_data_addr); ++ ++ list->head = (struct kvm_csv_batch_list_node *)node->next_cmd_addr; ++ g_free(node); ++ node = list->head; ++ } ++ ++ g_free(list); ++ return 0; ++} ++ ++static void send_update_data_free(void *data) ++{ ++ struct kvm_sev_send_update_data *update = ++ (struct kvm_sev_send_update_data *)data; ++ g_free((guchar *)update->hdr_uaddr); ++ g_free((guchar *)update->trans_uaddr); ++ g_free(update); ++} ++ ++static int ++csv_send_queue_data(SevGuestState *s, uint8_t *ptr, ++ uint32_t size, uint64_t addr) ++{ ++ int ret = 0; ++ int fw_error; ++ guchar *trans; ++ guchar *packet_hdr; ++ struct kvm_sev_send_update_data *update; ++ struct kvm_csv_batch_list_node *new_node = NULL; ++ ++ /* If this is first call then query the packet header bytes and allocate ++ * the packet buffer. ++ */ ++ if (s->send_packet_hdr_len < 1) { ++ s->send_packet_hdr_len = sev_send_get_packet_len(&fw_error); ++ if (s->send_packet_hdr_len < 1) { ++ error_report("%s: SEND_UPDATE fw_error=%d '%s'", ++ __func__, fw_error, fw_error_to_str(fw_error)); ++ return 1; ++ } ++ } ++ ++ packet_hdr = g_new(guchar, s->send_packet_hdr_len); ++ memset(packet_hdr, 0, s->send_packet_hdr_len); ++ ++ update = g_new0(struct kvm_sev_send_update_data, 1); ++ ++ /* allocate transport buffer */ ++ trans = g_new(guchar, size); ++ ++ update->hdr_uaddr = (unsigned long)packet_hdr; ++ update->hdr_len = s->send_packet_hdr_len; ++ update->guest_uaddr = (unsigned long)ptr; ++ update->guest_len = size; ++ update->trans_uaddr = (unsigned long)trans; ++ update->trans_len = size; ++ ++ new_node = csv_batch_cmd_list_node_create((uint64_t)update, addr); ++ if (!new_node) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ if (s->csv_batch_cmd_list == NULL) { ++ s->csv_batch_cmd_list = csv_batch_cmd_list_create(new_node, ++ send_update_data_free); ++ if (s->csv_batch_cmd_list == NULL) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ } else { ++ /* Add new_node's command address to the last_node */ ++ csv_batch_cmd_list_add_after(s->csv_batch_cmd_list, new_node); ++ } ++ ++ trace_kvm_sev_send_update_data(ptr, trans, size); ++ ++ return ret; ++ ++err: ++ g_free(trans); ++ g_free(update); ++ g_free(packet_hdr); ++ g_free(new_node); ++ if (s->csv_batch_cmd_list) { ++ csv_batch_cmd_list_destroy(s->csv_batch_cmd_list); ++ s->csv_batch_cmd_list = NULL; ++ } ++ return ret; ++} ++ ++int ++csv_queue_outgoing_page(uint8_t *ptr, uint32_t sz, uint64_t addr) ++{ ++ SevGuestState *s = sev_guest; ++ ++ /* Only support for HYGON CSV */ ++ if (!is_hygon_cpu()) { ++ error_report("Only support enqueue pages for HYGON CSV"); ++ return -EINVAL; ++ } ++ ++ return csv_send_queue_data(s, ptr, sz, addr); ++} ++ + static const QemuUUID sev_hash_table_header_guid = { + .data = UUID_LE(0x9438d606, 0x4f22, 0x4cc9, 0xb4, 0x79, 0xa7, 0x93, + 0xd4, 0x11, 0xfd, 0x21) +-- +1.8.3.1 + diff --git a/0208-target-i386-csv-add-support-to-encrypt-the-outgoing-.patch b/0208-target-i386-csv-add-support-to-encrypt-the-outgoing-.patch new file mode 100644 index 0000000000000000000000000000000000000000..344ebe0019a14ad4464e2c715561295168a58899 --- /dev/null +++ b/0208-target-i386-csv-add-support-to-encrypt-the-outgoing-.patch @@ -0,0 +1,207 @@ +From 98a84342005697d79cdf1edbff23ed12ae857f05 Mon Sep 17 00:00:00 2001 +From: fangbaoshun +Date: Mon, 2 Aug 2021 11:41:58 +0800 +Subject: [PATCH 218/293] target/i386: csv: add support to encrypt the outgoing + pages in the list queued before. + +The csv_save_queued_outgoing_pages() provide the implementation to encrypt +the guest private pages during transmission. The routines uses SEND_START +command to create the outgoing encryption context on the first call then +uses COMMAND_BATCH command to send the SEND_UPDATE_DATA commands queued +in the list to encrypt the data before writing it to the socket. While +encrypting the data SEND_UPDATE_DATA produces some metadata (e.g MAC, IV). +The metadata is also sent to the target machine. After migration is completed, +we issue the SEND_FINISH command to transition the SEV guest state from sending +to unrunnable state. + +Signed-off-by: hanliyang +--- + include/exec/confidential-guest-support.h | 4 ++ + linux-headers/linux/kvm.h | 8 +++ + target/i386/csv.h | 1 + + target/i386/sev.c | 88 +++++++++++++++++++++++++++++++ + target/i386/sev.h | 3 ++ + 5 files changed, 104 insertions(+) + +diff --git a/include/exec/confidential-guest-support.h b/include/exec/confidential-guest-support.h +index 8949568..c84f8c1 100644 +--- a/include/exec/confidential-guest-support.h ++++ b/include/exec/confidential-guest-support.h +@@ -80,6 +80,10 @@ struct ConfidentialGuestMemoryEncryptionOps { + + /* Queue the encrypted page and metadata associated with it into a list */ + int (*queue_outgoing_page)(uint8_t *ptr, uint32_t size, uint64_t addr); ++ ++ /* Write the list queued with encrypted pages and metadata associated ++ * with them */ ++ int (*save_queued_outgoing_pages)(QEMUFile *f, uint64_t *bytes_sent); + }; + + typedef struct ConfidentialGuestSupportClass { +diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h +index a61be97..8595dd5 100644 +--- a/linux-headers/linux/kvm.h ++++ b/linux-headers/linux/kvm.h +@@ -1928,6 +1928,9 @@ enum sev_cmd_id { + /* Guest Migration Extension */ + KVM_SEV_SEND_CANCEL, + ++ /* Hygon CSV batch command */ ++ KVM_CSV_COMMAND_BATCH = 0x18, ++ + KVM_SEV_NR_MAX, + }; + +@@ -2030,6 +2033,11 @@ struct kvm_csv_batch_list_node { + __u64 next_cmd_addr; + }; + ++struct kvm_csv_command_batch { ++ __u32 command_id; ++ __u64 csv_batch_list_uaddr; ++}; ++ + #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) + #define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1) + #define KVM_DEV_ASSIGN_MASK_INTX (1 << 2) +diff --git a/target/i386/csv.h b/target/i386/csv.h +index 4c1ef20..2a3a311 100644 +--- a/target/i386/csv.h ++++ b/target/i386/csv.h +@@ -54,5 +54,6 @@ struct CsvBatchCmdList { + }; + + int csv_queue_outgoing_page(uint8_t *ptr, uint32_t sz, uint64_t addr); ++int csv_save_queued_outgoing_pages(QEMUFile *f, uint64_t *bytes_sent); + + #endif +diff --git a/target/i386/sev.c b/target/i386/sev.c +index 027249e..cfbc9fb 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -191,6 +191,7 @@ static struct ConfidentialGuestMemoryEncryptionOps sev_memory_encryption_ops = { + .save_outgoing_shared_regions_list = sev_save_outgoing_shared_regions_list, + .load_incoming_shared_regions_list = sev_load_incoming_shared_regions_list, + .queue_outgoing_page = csv_queue_outgoing_page, ++ .save_queued_outgoing_pages = csv_save_queued_outgoing_pages, + }; + + static int +@@ -2011,6 +2012,69 @@ err: + return ret; + } + ++static int ++csv_command_batch(uint32_t cmd_id, uint64_t head_uaddr, int *fw_err) ++{ ++ int ret; ++ struct kvm_csv_command_batch command_batch = { }; ++ ++ command_batch.command_id = cmd_id; ++ command_batch.csv_batch_list_uaddr = head_uaddr; ++ ++ ret = sev_ioctl(sev_guest->sev_fd, KVM_CSV_COMMAND_BATCH, ++ &command_batch, fw_err); ++ if (ret) { ++ error_report("%s: COMMAND_BATCH ret=%d fw_err=%d '%s'", ++ __func__, ret, *fw_err, fw_error_to_str(*fw_err)); ++ } ++ ++ return ret; ++} ++ ++static int ++csv_send_update_data_batch(SevGuestState *s, QEMUFile *f, uint64_t *bytes_sent) ++{ ++ int ret, fw_error = 0; ++ struct kvm_sev_send_update_data *update; ++ struct kvm_csv_batch_list_node *node; ++ ++ ret = csv_command_batch(KVM_SEV_SEND_UPDATE_DATA, ++ (uint64_t)s->csv_batch_cmd_list->head, &fw_error); ++ if (ret) { ++ error_report("%s: csv_command_batch ret=%d fw_error=%d '%s'", ++ __func__, ret, fw_error, fw_error_to_str(fw_error)); ++ goto err; ++ } ++ ++ for (node = s->csv_batch_cmd_list->head; ++ node != NULL; ++ node = (struct kvm_csv_batch_list_node *)node->next_cmd_addr) { ++ if (node != s->csv_batch_cmd_list->head) { ++ /* head's page header is saved before send_update_data */ ++ qemu_put_be64(f, node->addr); ++ *bytes_sent += 8; ++ if (node->next_cmd_addr != 0) ++ qemu_put_be32(f, RAM_SAVE_ENCRYPTED_PAGE_BATCH); ++ else ++ qemu_put_be32(f, RAM_SAVE_ENCRYPTED_PAGE_BATCH_END); ++ *bytes_sent += 4; ++ } ++ update = (struct kvm_sev_send_update_data *)node->cmd_data_addr; ++ qemu_put_be32(f, update->hdr_len); ++ qemu_put_buffer(f, (uint8_t *)update->hdr_uaddr, update->hdr_len); ++ *bytes_sent += (4 + update->hdr_len); ++ ++ qemu_put_be32(f, update->trans_len); ++ qemu_put_buffer(f, (uint8_t *)update->trans_uaddr, update->trans_len); ++ *bytes_sent += (4 + update->trans_len); ++ } ++ ++err: ++ csv_batch_cmd_list_destroy(s->csv_batch_cmd_list); ++ s->csv_batch_cmd_list = NULL; ++ return ret; ++} ++ + int + csv_queue_outgoing_page(uint8_t *ptr, uint32_t sz, uint64_t addr) + { +@@ -2025,6 +2089,30 @@ csv_queue_outgoing_page(uint8_t *ptr, uint32_t sz, uint64_t addr) + return csv_send_queue_data(s, ptr, sz, addr); + } + ++int ++csv_save_queued_outgoing_pages(QEMUFile *f, uint64_t *bytes_sent) ++{ ++ SevGuestState *s = sev_guest; ++ ++ /* Only support for HYGON CSV */ ++ if (!is_hygon_cpu()) { ++ error_report("Only support transfer queued pages for HYGON CSV"); ++ return -EINVAL; ++ } ++ ++ /* ++ * If this is a first buffer then create outgoing encryption context ++ * and write our PDH, policy and session data. ++ */ ++ if (!sev_check_state(s, SEV_STATE_SEND_UPDATE) && ++ sev_send_start(s, f, bytes_sent)) { ++ error_report("Failed to create outgoing context"); ++ return 1; ++ } ++ ++ return csv_send_update_data_batch(s, f, bytes_sent); ++} ++ + static const QemuUUID sev_hash_table_header_guid = { + .data = UUID_LE(0x9438d606, 0x4f22, 0x4cc9, 0xb4, 0x79, 0xa7, 0x93, + 0xd4, 0x11, 0xfd, 0x21) +diff --git a/target/i386/sev.h b/target/i386/sev.h +index 84e3bdf..f788611 100644 +--- a/target/i386/sev.h ++++ b/target/i386/sev.h +@@ -41,6 +41,9 @@ typedef struct SevKernelLoaderContext { + #define RAM_SAVE_ENCRYPTED_PAGE 0x1 + #define RAM_SAVE_SHARED_REGIONS_LIST 0x2 + ++#define RAM_SAVE_ENCRYPTED_PAGE_BATCH 0x4 ++#define RAM_SAVE_ENCRYPTED_PAGE_BATCH_END 0x5 ++ + #ifdef CONFIG_SEV + bool sev_enabled(void); + bool sev_es_enabled(void); +-- +1.8.3.1 + diff --git a/0209-target-i386-csv-add-support-to-queue-the-incoming-pa.patch b/0209-target-i386-csv-add-support-to-queue-the-incoming-pa.patch new file mode 100644 index 0000000000000000000000000000000000000000..26d986f3d3e19a27292f25254f281cb439823a6a --- /dev/null +++ b/0209-target-i386-csv-add-support-to-queue-the-incoming-pa.patch @@ -0,0 +1,170 @@ +From ec780ff02209bc4fed0f517e4b8cc5962182e428 Mon Sep 17 00:00:00 2001 +From: fangbaoshun +Date: Mon, 2 Aug 2021 13:49:48 +0800 +Subject: [PATCH 219/293] target/i386: csv: add support to queue the incoming + page into a list + +The csv_queue_incoming_page() provide the implementation to queue the +guest private pages during transmission. The routines queues the incoming +socket which contains the guest private pages into a list then uses the +COMMAND_BATCH command to load the encrypted pages into the guest memory. + +Signed-off-by: hanliyang +--- + include/exec/confidential-guest-support.h | 3 + + target/i386/csv.h | 1 + + target/i386/sev.c | 92 +++++++++++++++++++++++++++++++ + 3 files changed, 96 insertions(+) + +diff --git a/include/exec/confidential-guest-support.h b/include/exec/confidential-guest-support.h +index c84f8c1..101cc52 100644 +--- a/include/exec/confidential-guest-support.h ++++ b/include/exec/confidential-guest-support.h +@@ -84,6 +84,9 @@ struct ConfidentialGuestMemoryEncryptionOps { + /* Write the list queued with encrypted pages and metadata associated + * with them */ + int (*save_queued_outgoing_pages)(QEMUFile *f, uint64_t *bytes_sent); ++ ++ /* Queue the incoming encrypted page into a list */ ++ int (*queue_incoming_page)(QEMUFile *f, uint8_t *ptr); + }; + + typedef struct ConfidentialGuestSupportClass { +diff --git a/target/i386/csv.h b/target/i386/csv.h +index 2a3a311..d1bcc8b 100644 +--- a/target/i386/csv.h ++++ b/target/i386/csv.h +@@ -55,5 +55,6 @@ struct CsvBatchCmdList { + + int csv_queue_outgoing_page(uint8_t *ptr, uint32_t sz, uint64_t addr); + int csv_save_queued_outgoing_pages(QEMUFile *f, uint64_t *bytes_sent); ++int csv_queue_incoming_page(QEMUFile *f, uint8_t *ptr); + + #endif +diff --git a/target/i386/sev.c b/target/i386/sev.c +index cfbc9fb..97e2e71 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -192,6 +192,7 @@ static struct ConfidentialGuestMemoryEncryptionOps sev_memory_encryption_ops = { + .load_incoming_shared_regions_list = sev_load_incoming_shared_regions_list, + .queue_outgoing_page = csv_queue_outgoing_page, + .save_queued_outgoing_pages = csv_save_queued_outgoing_pages, ++ .queue_incoming_page = csv_queue_incoming_page, + }; + + static int +@@ -1940,6 +1941,15 @@ static void send_update_data_free(void *data) + g_free(update); + } + ++static void receive_update_data_free(void *data) ++{ ++ struct kvm_sev_receive_update_data *update = ++ (struct kvm_sev_receive_update_data *)data; ++ g_free((guchar *)update->hdr_uaddr); ++ g_free((guchar *)update->trans_uaddr); ++ g_free(update); ++} ++ + static int + csv_send_queue_data(SevGuestState *s, uint8_t *ptr, + uint32_t size, uint64_t addr) +@@ -2013,6 +2023,66 @@ err: + } + + static int ++csv_receive_queue_data(SevGuestState *s, QEMUFile *f, uint8_t *ptr) ++{ ++ int ret = 0; ++ gchar *hdr = NULL, *trans = NULL; ++ struct kvm_sev_receive_update_data *update; ++ struct kvm_csv_batch_list_node *new_node = NULL; ++ ++ update = g_new0(struct kvm_sev_receive_update_data, 1); ++ /* get packet header */ ++ update->hdr_len = qemu_get_be32(f); ++ hdr = g_new(gchar, update->hdr_len); ++ qemu_get_buffer(f, (uint8_t *)hdr, update->hdr_len); ++ update->hdr_uaddr = (unsigned long)hdr; ++ ++ /* get transport buffer */ ++ update->trans_len = qemu_get_be32(f); ++ trans = g_new(gchar, update->trans_len); ++ update->trans_uaddr = (unsigned long)trans; ++ qemu_get_buffer(f, (uint8_t *)update->trans_uaddr, update->trans_len); ++ ++ /* set guest address,guest len is page_size */ ++ update->guest_uaddr = (uint64_t)ptr; ++ update->guest_len = TARGET_PAGE_SIZE; ++ ++ new_node = csv_batch_cmd_list_node_create((uint64_t)update, 0); ++ if (!new_node) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ if (s->csv_batch_cmd_list == NULL) { ++ s->csv_batch_cmd_list = csv_batch_cmd_list_create(new_node, ++ receive_update_data_free); ++ if (s->csv_batch_cmd_list == NULL) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ } else { ++ /* Add new_node's command address to the last_node */ ++ csv_batch_cmd_list_add_after(s->csv_batch_cmd_list, new_node); ++ } ++ ++ trace_kvm_sev_receive_update_data(trans, (void *)ptr, update->guest_len, ++ (void *)hdr, update->hdr_len); ++ ++ return ret; ++ ++err: ++ g_free(trans); ++ g_free(update); ++ g_free(hdr); ++ g_free(new_node); ++ if (s->csv_batch_cmd_list) { ++ csv_batch_cmd_list_destroy(s->csv_batch_cmd_list); ++ s->csv_batch_cmd_list = NULL; ++ } ++ return ret; ++} ++ ++static int + csv_command_batch(uint32_t cmd_id, uint64_t head_uaddr, int *fw_err) + { + int ret; +@@ -2089,6 +2159,28 @@ csv_queue_outgoing_page(uint8_t *ptr, uint32_t sz, uint64_t addr) + return csv_send_queue_data(s, ptr, sz, addr); + } + ++int csv_queue_incoming_page(QEMUFile *f, uint8_t *ptr) ++{ ++ SevGuestState *s = sev_guest; ++ ++ /* Only support for HYGON CSV */ ++ if (!is_hygon_cpu()) { ++ error_report("Only support enqueue received pages for HYGON CSV"); ++ return -EINVAL; ++ } ++ ++ /* ++ * If this is first buffer and SEV is not in recieiving state then ++ * use RECEIVE_START command to create a encryption context. ++ */ ++ if (!sev_check_state(s, SEV_STATE_RECEIVE_UPDATE) && ++ sev_receive_start(s, f)) { ++ return 1; ++ } ++ ++ return csv_receive_queue_data(s, f, ptr); ++} ++ + int + csv_save_queued_outgoing_pages(QEMUFile *f, uint64_t *bytes_sent) + { +-- +1.8.3.1 + diff --git a/0210-target-i386-csv-add-support-to-load-incoming-encrypt.patch b/0210-target-i386-csv-add-support-to-load-incoming-encrypt.patch new file mode 100644 index 0000000000000000000000000000000000000000..f18ad465a71ed8816071dfe0a4c07cb404b943f2 --- /dev/null +++ b/0210-target-i386-csv-add-support-to-load-incoming-encrypt.patch @@ -0,0 +1,107 @@ +From 2e1b9eedc61e2643aa74a3dd20abe6eef41fa492 Mon Sep 17 00:00:00 2001 +From: fangbaoshun +Date: Mon, 2 Aug 2021 14:11:43 +0800 +Subject: [PATCH 220/293] target/i386: csv: add support to load incoming + encrypted pages queued in the CMD list + +The csv_load_queued_incoming_pages() provide the implementation to read the +incoming guest private pages from the socket queued in the CMD list and load +them into the guest memory. The routines uses the RECEIVE_START command to +create the incoming encryption context on the first call then uses the +COMMAND_BATCH carried with RECEIEVE_UPDATE_DATA commands to load the encrypted +pages into the guest memory. After migration is completed, we issue the +RECEIVE_FINISH command to transition the SEV guest to the runnable state +so that it can be executed. + +Signed-off-by: hanliyang +--- + include/exec/confidential-guest-support.h | 3 +++ + target/i386/csv.h | 1 + + target/i386/sev.c | 32 +++++++++++++++++++++++++++++++ + 3 files changed, 36 insertions(+) + +diff --git a/include/exec/confidential-guest-support.h b/include/exec/confidential-guest-support.h +index 101cc52..cb14b81 100644 +--- a/include/exec/confidential-guest-support.h ++++ b/include/exec/confidential-guest-support.h +@@ -87,6 +87,9 @@ struct ConfidentialGuestMemoryEncryptionOps { + + /* Queue the incoming encrypted page into a list */ + int (*queue_incoming_page)(QEMUFile *f, uint8_t *ptr); ++ ++ /* Load the incoming encrypted pages queued in list into guest memory */ ++ int (*load_queued_incoming_pages)(QEMUFile *f); + }; + + typedef struct ConfidentialGuestSupportClass { +diff --git a/target/i386/csv.h b/target/i386/csv.h +index d1bcc8b..977f08b 100644 +--- a/target/i386/csv.h ++++ b/target/i386/csv.h +@@ -56,5 +56,6 @@ struct CsvBatchCmdList { + int csv_queue_outgoing_page(uint8_t *ptr, uint32_t sz, uint64_t addr); + int csv_save_queued_outgoing_pages(QEMUFile *f, uint64_t *bytes_sent); + int csv_queue_incoming_page(QEMUFile *f, uint8_t *ptr); ++int csv_load_queued_incoming_pages(QEMUFile *f); + + #endif +diff --git a/target/i386/sev.c b/target/i386/sev.c +index 97e2e71..8e5da51 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -193,6 +193,7 @@ static struct ConfidentialGuestMemoryEncryptionOps sev_memory_encryption_ops = { + .queue_outgoing_page = csv_queue_outgoing_page, + .save_queued_outgoing_pages = csv_save_queued_outgoing_pages, + .queue_incoming_page = csv_queue_incoming_page, ++ .load_queued_incoming_pages = csv_load_queued_incoming_pages, + }; + + static int +@@ -2145,6 +2146,24 @@ err: + return ret; + } + ++static int ++csv_receive_update_data_batch(SevGuestState *s) ++{ ++ int ret; ++ int fw_error; ++ ++ ret = csv_command_batch(KVM_SEV_RECEIVE_UPDATE_DATA, ++ (uint64_t)s->csv_batch_cmd_list->head, &fw_error); ++ if (ret) { ++ error_report("%s: csv_command_batch ret=%d fw_error=%d '%s'", ++ __func__, ret, fw_error, fw_error_to_str(fw_error)); ++ } ++ ++ csv_batch_cmd_list_destroy(s->csv_batch_cmd_list); ++ s->csv_batch_cmd_list = NULL; ++ return ret; ++} ++ + int + csv_queue_outgoing_page(uint8_t *ptr, uint32_t sz, uint64_t addr) + { +@@ -2205,6 +2224,19 @@ csv_save_queued_outgoing_pages(QEMUFile *f, uint64_t *bytes_sent) + return csv_send_update_data_batch(s, f, bytes_sent); + } + ++int csv_load_queued_incoming_pages(QEMUFile *f) ++{ ++ SevGuestState *s = sev_guest; ++ ++ /* Only support for HYGON CSV */ ++ if (!is_hygon_cpu()) { ++ error_report("Only support load queued pages for HYGON CSV"); ++ return -EINVAL; ++ } ++ ++ return csv_receive_update_data_batch(s); ++} ++ + static const QemuUUID sev_hash_table_header_guid = { + .data = UUID_LE(0x9438d606, 0x4f22, 0x4cc9, 0xb4, 0x79, 0xa7, 0x93, + 0xd4, 0x11, 0xfd, 0x21) +-- +1.8.3.1 + diff --git a/0211-migration-ram-Accelerate-the-transmission-of-CSV-gue.patch b/0211-migration-ram-Accelerate-the-transmission-of-CSV-gue.patch new file mode 100644 index 0000000000000000000000000000000000000000..be1c55a8e2b6fbffa31cc53bb0a0a4c68dce44e6 --- /dev/null +++ b/0211-migration-ram-Accelerate-the-transmission-of-CSV-gue.patch @@ -0,0 +1,208 @@ +From f2e133370d44aaa49a74ee1481dc469619adef53 Mon Sep 17 00:00:00 2001 +From: fangbaoshun +Date: Mon, 2 Aug 2021 14:35:51 +0800 +Subject: [PATCH 221/293] migration/ram: Accelerate the transmission of CSV + guest's encrypted pages + +When memory encryption is enabled, the guest memory will be encrypted with +the guest specific key. The patch introduces an accelerate solution which +queued the pages into list and send them togather by COMMAND_BATCH. + +Signed-off-by: hanliyang +--- + configs/devices/i386-softmmu/default.mak | 1 + + hw/i386/Kconfig | 5 ++ + migration/ram.c | 119 +++++++++++++++++++++++++++++++ + target/i386/csv.h | 2 + + 4 files changed, 127 insertions(+) + +diff --git a/configs/devices/i386-softmmu/default.mak b/configs/devices/i386-softmmu/default.mak +index db83ffc..e948e54 100644 +--- a/configs/devices/i386-softmmu/default.mak ++++ b/configs/devices/i386-softmmu/default.mak +@@ -24,6 +24,7 @@ + #CONFIG_VTD=n + #CONFIG_SGX=n + #CONFIG_CSV=n ++#CONFIG_HYGON_CSV_MIG_ACCEL=n + + # Boards: + # +diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig +index 08f3ae4..682e324 100644 +--- a/hw/i386/Kconfig ++++ b/hw/i386/Kconfig +@@ -12,8 +12,13 @@ config SGX + + config CSV + bool ++ select HYGON_CSV_MIG_ACCEL + depends on SEV + ++config HYGON_CSV_MIG_ACCEL ++ bool ++ depends on CSV ++ + config PC + bool + imply APPLESMC +diff --git a/migration/ram.c b/migration/ram.c +index 22f07a0..be8dca3 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -67,6 +67,7 @@ + + /* Defines RAM_SAVE_ENCRYPTED_PAGE and RAM_SAVE_SHARED_REGION_LIST */ + #include "target/i386/sev.h" ++#include "target/i386/csv.h" + #include "sysemu/kvm.h" + + #include "hw/boards.h" /* for machine_dump_guest_core() */ +@@ -2334,6 +2335,112 @@ out: + return ret; + } + ++#ifdef CONFIG_HYGON_CSV_MIG_ACCEL ++/** ++ * ram_save_encrypted_pages_in_batch: send the given encrypted pages to ++ * the stream. ++ * ++ * Sending pages of 4K size in batch. The saving stops at the end of ++ * the block. ++ * ++ * The caller must be with ram_state.bitmap_mutex held to call this ++ * function. ++ * ++ * Returns the number of pages written or negative on error ++ * ++ * @rs: current RAM state ++ * @pss: data about the page we want to send ++ */ ++static int ++ram_save_encrypted_pages_in_batch(RAMState *rs, PageSearchStatus *pss) ++{ ++ bool page_dirty; ++ int ret; ++ int tmppages, pages = 0; ++ uint8_t *p; ++ uint32_t host_len = 0; ++ uint64_t bytes_xmit = 0; ++ ram_addr_t offset, start_offset = 0; ++ MachineState *ms = MACHINE(qdev_get_machine()); ++ ConfidentialGuestSupportClass *cgs_class = ++ (ConfidentialGuestSupportClass *)object_get_class(OBJECT(ms->cgs)); ++ struct ConfidentialGuestMemoryEncryptionOps *ops = ++ cgs_class->memory_encryption_ops; ++ ++ do { ++ page_dirty = migration_bitmap_clear_dirty(rs, pss->block, pss->page); ++ ++ /* Check the pages is dirty and if it is send it */ ++ if (page_dirty) { ++ /* Process the unencrypted page */ ++ if (!encrypted_test_list(rs, pss->block, pss->page)) { ++ tmppages = migration_ops->ram_save_target_page(rs, pss); ++ } else { ++ /* Caculate the offset and host virtual address of the page */ ++ offset = ((ram_addr_t)pss->page) << TARGET_PAGE_BITS; ++ p = pss->block->host + offset; ++ ++ /* Record the offset and host virtual address of the first ++ * page in this loop which will be used below. ++ */ ++ if (host_len == 0) { ++ start_offset = offset | RAM_SAVE_FLAG_ENCRYPTED_DATA; ++ } else { ++ offset |= (RAM_SAVE_FLAG_ENCRYPTED_DATA | RAM_SAVE_FLAG_CONTINUE); ++ } ++ ++ /* Queue the outgoing page if the page is not zero page. ++ * If the queued pages are up to the outgoing page window size, ++ * process them below. ++ */ ++ if (ops->queue_outgoing_page(p, TARGET_PAGE_SIZE, offset)) ++ return -1; ++ ++ tmppages = 1; ++ host_len += TARGET_PAGE_SIZE; ++ ++ stat64_add(&mig_stats.normal_pages, 1); ++ } ++ } else { ++ tmppages = 0; ++ } ++ ++ if (tmppages >= 0) { ++ pages += tmppages; ++ } else { ++ return tmppages; ++ } ++ ++ pss_find_next_dirty(pss); ++ } while (offset_in_ramblock(pss->block, ++ ((ram_addr_t)pss->page) << TARGET_PAGE_BITS) && ++ host_len < CSV_OUTGOING_PAGE_WINDOW_SIZE); ++ ++ /* Check if there are any queued pages */ ++ if (host_len != 0) { ++ ram_transferred_add(save_page_header(pss, pss->pss_channel, ++ pss->block, start_offset)); ++ /* if only one page queued, flag is BATCH_END, else flag is BATCH */ ++ if (host_len > TARGET_PAGE_SIZE) ++ qemu_put_be32(pss->pss_channel, RAM_SAVE_ENCRYPTED_PAGE_BATCH); ++ else ++ qemu_put_be32(pss->pss_channel, RAM_SAVE_ENCRYPTED_PAGE_BATCH_END); ++ ram_transferred_add(4); ++ /* Process the queued pages in batch */ ++ ret = ops->save_queued_outgoing_pages(pss->pss_channel, &bytes_xmit); ++ if (ret) { ++ return -1; ++ } ++ ram_transferred_add(bytes_xmit); ++ } ++ ++ /* The offset we leave with is the last one we looked at */ ++ pss->page--; ++ ++ return pages; ++} ++#endif ++ + /** + * ram_save_host_page: save a whole host page + * +@@ -2369,6 +2476,18 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss) + return 0; + } + ++#ifdef CONFIG_HYGON_CSV_MIG_ACCEL ++ /* ++ * If command_batch function is enabled and memory encryption is enabled ++ * then use command batch APIs to accelerate the sending process ++ * to write the outgoing buffer to the wire. The encryption APIs ++ * will re-encrypt the data with transport key so that data is prototect ++ * on the wire. ++ */ ++ if (memcrypt_enabled() && is_hygon_cpu() && !migration_in_postcopy()) ++ return ram_save_encrypted_pages_in_batch(rs, pss); ++#endif ++ + /* Update host page boundary information */ + pss_host_page_prepare(pss); + +diff --git a/target/i386/csv.h b/target/i386/csv.h +index 977f08b..74a54f9 100644 +--- a/target/i386/csv.h ++++ b/target/i386/csv.h +@@ -44,6 +44,8 @@ static bool __attribute__((unused)) is_hygon_cpu(void) + + #endif + ++#define CSV_OUTGOING_PAGE_WINDOW_SIZE (4094 * TARGET_PAGE_SIZE) ++ + typedef struct CsvBatchCmdList CsvBatchCmdList; + typedef void (*CsvDestroyCmdNodeFn) (void *data); + +-- +1.8.3.1 + diff --git a/0212-migration-ram-Accelerate-the-loading-of-CSV-guest-s-.patch b/0212-migration-ram-Accelerate-the-loading-of-CSV-guest-s-.patch new file mode 100644 index 0000000000000000000000000000000000000000..8869ea3e5d06b1e8af02fda943ad309668638b1c --- /dev/null +++ b/0212-migration-ram-Accelerate-the-loading-of-CSV-guest-s-.patch @@ -0,0 +1,37 @@ +From d96daa85a535a5363592de1c3784ae744a2c4115 Mon Sep 17 00:00:00 2001 +From: fangbaoshun +Date: Mon, 2 Aug 2021 14:49:45 +0800 +Subject: [PATCH 222/293] migration/ram: Accelerate the loading of CSV guest's + encrypted pages + +When memory encryption is enabled, the guest memory will be encrypted with +the guest specific key. The patch introduces an accelerate solution which +queued the pages into list and load them togather by COMMAND_BATCH. + +Signed-off-by: hanliyang +--- + migration/ram.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/migration/ram.c b/migration/ram.c +index be8dca3..c7245aa 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -1295,6 +1295,14 @@ static int load_encrypted_data(QEMUFile *f, uint8_t *ptr) + return ops->load_incoming_page(f, ptr); + } else if (flag == RAM_SAVE_SHARED_REGIONS_LIST) { + return ops->load_incoming_shared_regions_list(f); ++ } else if (flag == RAM_SAVE_ENCRYPTED_PAGE_BATCH) { ++ return ops->queue_incoming_page(f, ptr); ++ } else if (flag == RAM_SAVE_ENCRYPTED_PAGE_BATCH_END) { ++ if (ops->queue_incoming_page(f, ptr)) { ++ error_report("Failed to queue incoming data"); ++ return -EINVAL; ++ } ++ return ops->load_queued_incoming_pages(f); + } else { + error_report("unknown encrypted flag %x", flag); + return 1; +-- +1.8.3.1 + diff --git a/0213-target-i386-csv-Add-support-for-migrate-VMSA-for-CSV.patch b/0213-target-i386-csv-Add-support-for-migrate-VMSA-for-CSV.patch new file mode 100644 index 0000000000000000000000000000000000000000..000986aed5c2f2f4094ba00d055f9d030156a7d9 --- /dev/null +++ b/0213-target-i386-csv-Add-support-for-migrate-VMSA-for-CSV.patch @@ -0,0 +1,434 @@ +From c9e9fc38be4fdf97fdc6f6adce29699f3bef01da Mon Sep 17 00:00:00 2001 +From: hanliyang +Date: Tue, 7 Jun 2022 15:19:32 +0800 +Subject: [PATCH 223/293] target/i386: csv: Add support for migrate VMSA for + CSV2 guest + +CSV2 can protect guest's cpu state through memory encryption. Each +vcpu has its corresponding memory, which is also called VMSA, and +is encrypted by guest's specific encrytion key. + +When CSV2 guest exit to host, the vcpu's state will be encrypted +and saved to VMSA, and the VMSA will be decrypted and loaded to cpu +when the guest's vcpu running at next time. + +If user wants to migrate one CSV2 guest to target machine, the VMSA +of the vcpus also should be migrated to target. CSV firmware provides +SEND_UPDATE_VMSA/RECEIVE_UPDATE_VMSA API through which VMSA can be +converted into secure data and transmitted to the remote end (for +example, network transmission). + +The migration of cpu state is identified by CPUState.cpu_index which +may not equals to vcpu id from KVM's perspective. + +When migrate the VMSA, the source QEMU will invoke SEND_UPDATE_VMSA to +generate data correspond to VMSA, after target QEMU received the data, +it will calc target vcpu id in the KVM by CPUState.cpu_index, and then +invoke RECEIVE_UPDATE_VMSA to restore VMSA correspond to vcpu. + +Signed-off-by: hanliyang +--- + include/exec/confidential-guest-support.h | 6 + + linux-headers/linux/kvm.h | 16 +++ + migration/ram.c | 42 +++++++ + target/i386/csv.h | 2 + + target/i386/sev.c | 201 ++++++++++++++++++++++++++++++ + target/i386/sev.h | 1 + + target/i386/trace-events | 2 + + 7 files changed, 270 insertions(+) + +diff --git a/include/exec/confidential-guest-support.h b/include/exec/confidential-guest-support.h +index cb14b81..2cba276 100644 +--- a/include/exec/confidential-guest-support.h ++++ b/include/exec/confidential-guest-support.h +@@ -90,6 +90,12 @@ struct ConfidentialGuestMemoryEncryptionOps { + + /* Load the incoming encrypted pages queued in list into guest memory */ + int (*load_queued_incoming_pages)(QEMUFile *f); ++ ++ /* Write the encrypted cpu state */ ++ int (*save_outgoing_cpu_state)(QEMUFile *f, uint64_t *bytes_sent); ++ ++ /* Load the encrypted cpu state */ ++ int (*load_incoming_cpu_state)(QEMUFile *f); + }; + + typedef struct ConfidentialGuestSupportClass { +diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h +index 8595dd5..2ed7ae4 100644 +--- a/linux-headers/linux/kvm.h ++++ b/linux-headers/linux/kvm.h +@@ -2009,6 +2009,14 @@ struct kvm_sev_send_update_data { + __u32 trans_len; + }; + ++struct kvm_sev_send_update_vmsa { ++ __u32 vcpu_id; ++ __u64 hdr_uaddr; ++ __u32 hdr_len; ++ __u64 trans_uaddr; ++ __u32 trans_len; ++}; ++ + struct kvm_sev_receive_start { + __u32 handle; + __u32 policy; +@@ -2027,6 +2035,14 @@ struct kvm_sev_receive_update_data { + __u32 trans_len; + }; + ++struct kvm_sev_receive_update_vmsa { ++ __u32 vcpu_id; ++ __u64 hdr_uaddr; ++ __u32 hdr_len; ++ __u64 trans_uaddr; ++ __u32 trans_len; ++}; ++ + struct kvm_csv_batch_list_node { + __u64 cmd_data_addr; + __u64 addr; +diff --git a/migration/ram.c b/migration/ram.c +index c7245aa..198b060 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -1279,6 +1279,33 @@ static int ram_save_shared_region_list(RAMState *rs, QEMUFile *f) + return 0; + } + ++/** ++ * ram_save_encrypted_cpu_state: send the encrypted cpu state ++ */ ++static int ram_save_encrypted_cpu_state(RAMState *rs, QEMUFile *f) ++{ ++ int ret; ++ uint64_t bytes_xmit = 0; ++ PageSearchStatus *pss = &rs->pss[RAM_CHANNEL_PRECOPY]; ++ MachineState *ms = MACHINE(qdev_get_machine()); ++ ConfidentialGuestSupportClass *cgs_class = ++ (ConfidentialGuestSupportClass *) object_get_class(OBJECT(ms->cgs)); ++ struct ConfidentialGuestMemoryEncryptionOps *ops = ++ cgs_class->memory_encryption_ops; ++ ++ ram_transferred_add(save_page_header(pss, f, ++ pss->last_sent_block, ++ RAM_SAVE_FLAG_ENCRYPTED_DATA)); ++ qemu_put_be32(f, RAM_SAVE_ENCRYPTED_CPU_STATE); ++ ret = ops->save_outgoing_cpu_state(f, &bytes_xmit); ++ if (ret < 0) { ++ return ret; ++ } ++ ram_transferred_add(4 + bytes_xmit); ++ ++ return 0; ++} ++ + static int load_encrypted_data(QEMUFile *f, uint8_t *ptr) + { + MachineState *ms = MACHINE(qdev_get_machine()); +@@ -1303,6 +1330,8 @@ static int load_encrypted_data(QEMUFile *f, uint8_t *ptr) + return -EINVAL; + } + return ops->load_queued_incoming_pages(f); ++ } else if (flag == RAM_SAVE_ENCRYPTED_CPU_STATE) { ++ return ops->load_incoming_cpu_state(f); + } else { + error_report("unknown encrypted flag %x", flag); + return 1; +@@ -3492,6 +3521,19 @@ static int ram_save_complete(QEMUFile *f, void *opaque) + qemu_file_set_error(f, ret); + return ret; + } ++ ++ /* ++ * send the encrypted cpu state, for example, CSV2 guest's ++ * vmsa for each vcpu. ++ */ ++ if (is_hygon_cpu()) { ++ ret = ram_save_encrypted_cpu_state(rs, f); ++ if (ret < 0) { ++ error_report("Failed to save encrypted cpu state"); ++ qemu_file_set_error(f, ret); ++ return ret; ++ } ++ } + } + } + +diff --git a/target/i386/csv.h b/target/i386/csv.h +index 74a54f9..47741a0 100644 +--- a/target/i386/csv.h ++++ b/target/i386/csv.h +@@ -59,5 +59,7 @@ int csv_queue_outgoing_page(uint8_t *ptr, uint32_t sz, uint64_t addr); + int csv_save_queued_outgoing_pages(QEMUFile *f, uint64_t *bytes_sent); + int csv_queue_incoming_page(QEMUFile *f, uint8_t *ptr); + int csv_load_queued_incoming_pages(QEMUFile *f); ++int csv_save_outgoing_cpu_state(QEMUFile *f, uint64_t *bytes_sent); ++int csv_load_incoming_cpu_state(QEMUFile *f); + + #endif +diff --git a/target/i386/sev.c b/target/i386/sev.c +index 8e5da51..52693ae 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -90,6 +90,10 @@ struct SevGuestState { + gchar *send_packet_hdr; + size_t send_packet_hdr_len; + ++ /* needed by live migration of HYGON CSV2 guest */ ++ gchar *send_vmsa_packet_hdr; ++ size_t send_vmsa_packet_hdr_len; ++ + uint32_t reset_cs; + uint32_t reset_ip; + bool reset_data_valid; +@@ -183,6 +187,9 @@ static const char *const sev_fw_errlist[] = { + #define SHARED_REGION_LIST_CONT 0x1 + #define SHARED_REGION_LIST_END 0x2 + ++#define ENCRYPTED_CPU_STATE_CONT 0x1 ++#define ENCRYPTED_CPU_STATE_END 0x2 ++ + static struct ConfidentialGuestMemoryEncryptionOps sev_memory_encryption_ops = { + .save_setup = sev_save_setup, + .save_outgoing_page = sev_save_outgoing_page, +@@ -194,6 +201,8 @@ static struct ConfidentialGuestMemoryEncryptionOps sev_memory_encryption_ops = { + .save_queued_outgoing_pages = csv_save_queued_outgoing_pages, + .queue_incoming_page = csv_queue_incoming_page, + .load_queued_incoming_pages = csv_load_queued_incoming_pages, ++ .save_outgoing_cpu_state = csv_save_outgoing_cpu_state, ++ .load_incoming_cpu_state = csv_load_incoming_cpu_state, + }; + + static int +@@ -1047,6 +1056,9 @@ sev_send_finish(void) + } + + g_free(sev_guest->send_packet_hdr); ++ if (sev_es_enabled() && is_hygon_cpu()) { ++ g_free(sev_guest->send_vmsa_packet_hdr); ++ } + sev_set_guest_state(sev_guest, SEV_STATE_RUNNING); + } + +@@ -2237,6 +2249,195 @@ int csv_load_queued_incoming_pages(QEMUFile *f) + return csv_receive_update_data_batch(s); + } + ++static int ++sev_send_vmsa_get_packet_len(int *fw_err) ++{ ++ int ret; ++ struct kvm_sev_send_update_vmsa update = { 0, }; ++ ++ ret = sev_ioctl(sev_guest->sev_fd, KVM_SEV_SEND_UPDATE_VMSA, ++ &update, fw_err); ++ if (*fw_err != SEV_RET_INVALID_LEN) { ++ ret = 0; ++ error_report("%s: failed to get session length ret=%d fw_error=%d '%s'", ++ __func__, ret, *fw_err, fw_error_to_str(*fw_err)); ++ goto err; ++ } ++ ++ ret = update.hdr_len; ++ ++err: ++ return ret; ++} ++ ++static int ++sev_send_update_vmsa(SevGuestState *s, QEMUFile *f, uint32_t cpu_id, ++ uint32_t cpu_index, uint32_t size, uint64_t *bytes_sent) ++{ ++ int ret, fw_error; ++ guchar *trans = NULL; ++ struct kvm_sev_send_update_vmsa update = {}; ++ ++ /* ++ * If this is first call then query the packet header bytes and allocate ++ * the packet buffer. ++ */ ++ if (!s->send_vmsa_packet_hdr) { ++ s->send_vmsa_packet_hdr_len = sev_send_vmsa_get_packet_len(&fw_error); ++ if (s->send_vmsa_packet_hdr_len < 1) { ++ error_report("%s: SEND_UPDATE_VMSA fw_error=%d '%s'", ++ __func__, fw_error, fw_error_to_str(fw_error)); ++ return 1; ++ } ++ ++ s->send_vmsa_packet_hdr = g_new(gchar, s->send_vmsa_packet_hdr_len); ++ } ++ ++ /* allocate transport buffer */ ++ trans = g_new(guchar, size); ++ ++ update.vcpu_id = cpu_id; ++ update.hdr_uaddr = (uintptr_t)s->send_vmsa_packet_hdr; ++ update.hdr_len = s->send_vmsa_packet_hdr_len; ++ update.trans_uaddr = (uintptr_t)trans; ++ update.trans_len = size; ++ ++ trace_kvm_sev_send_update_vmsa(cpu_id, cpu_index, trans, size); ++ ++ ret = sev_ioctl(s->sev_fd, KVM_SEV_SEND_UPDATE_VMSA, &update, &fw_error); ++ if (ret) { ++ error_report("%s: SEND_UPDATE_VMSA ret=%d fw_error=%d '%s'", ++ __func__, ret, fw_error, fw_error_to_str(fw_error)); ++ goto err; ++ } ++ ++ /* ++ * Migration of vCPU's VMState according to the instance_id ++ * (i.e. CPUState.cpu_index) ++ */ ++ qemu_put_be32(f, sizeof(uint32_t)); ++ qemu_put_buffer(f, (uint8_t *)&cpu_index, sizeof(uint32_t)); ++ *bytes_sent += 4 + sizeof(uint32_t); ++ ++ qemu_put_be32(f, update.hdr_len); ++ qemu_put_buffer(f, (uint8_t *)update.hdr_uaddr, update.hdr_len); ++ *bytes_sent += 4 + update.hdr_len; ++ ++ qemu_put_be32(f, update.trans_len); ++ qemu_put_buffer(f, (uint8_t *)update.trans_uaddr, update.trans_len); ++ *bytes_sent += 4 + update.trans_len; ++ ++err: ++ g_free(trans); ++ return ret; ++} ++ ++int csv_save_outgoing_cpu_state(QEMUFile *f, uint64_t *bytes_sent) ++{ ++ SevGuestState *s = sev_guest; ++ CPUState *cpu; ++ int ret = 0; ++ ++ /* Only support migrate VMSAs for HYGON CSV2 guest */ ++ if (!sev_es_enabled() || !is_hygon_cpu()) { ++ return 0; ++ } ++ ++ CPU_FOREACH(cpu) { ++ qemu_put_be32(f, ENCRYPTED_CPU_STATE_CONT); ++ *bytes_sent += 4; ++ ret = sev_send_update_vmsa(s, f, kvm_arch_vcpu_id(cpu), ++ cpu->cpu_index, TARGET_PAGE_SIZE, bytes_sent); ++ if (ret) { ++ goto err; ++ } ++ } ++ ++ qemu_put_be32(f, ENCRYPTED_CPU_STATE_END); ++ *bytes_sent += 4; ++ ++err: ++ return ret; ++} ++ ++static int sev_receive_update_vmsa(QEMUFile *f) ++{ ++ int ret = 1, fw_error = 0; ++ CPUState *cpu; ++ uint32_t cpu_index, cpu_id = 0; ++ gchar *hdr = NULL, *trans = NULL; ++ struct kvm_sev_receive_update_vmsa update = {}; ++ ++ /* get cpu index buffer */ ++ assert(qemu_get_be32(f) == sizeof(uint32_t)); ++ qemu_get_buffer(f, (uint8_t *)&cpu_index, sizeof(uint32_t)); ++ ++ CPU_FOREACH(cpu) { ++ if (cpu->cpu_index == cpu_index) { ++ cpu_id = kvm_arch_vcpu_id(cpu); ++ break; ++ } ++ } ++ update.vcpu_id = cpu_id; ++ ++ /* get packet header */ ++ update.hdr_len = qemu_get_be32(f); ++ if (!check_blob_length(update.hdr_len)) { ++ return 1; ++ } ++ ++ hdr = g_new(gchar, update.hdr_len); ++ qemu_get_buffer(f, (uint8_t *)hdr, update.hdr_len); ++ update.hdr_uaddr = (uintptr_t)hdr; ++ ++ /* get transport buffer */ ++ update.trans_len = qemu_get_be32(f); ++ if (!check_blob_length(update.trans_len)) { ++ goto err; ++ } ++ ++ trans = g_new(gchar, update.trans_len); ++ update.trans_uaddr = (uintptr_t)trans; ++ qemu_get_buffer(f, (uint8_t *)update.trans_uaddr, update.trans_len); ++ ++ trace_kvm_sev_receive_update_vmsa(cpu_id, cpu_index, ++ trans, update.trans_len, hdr, update.hdr_len); ++ ++ ret = sev_ioctl(sev_guest->sev_fd, KVM_SEV_RECEIVE_UPDATE_VMSA, ++ &update, &fw_error); ++ if (ret) { ++ error_report("Error RECEIVE_UPDATE_VMSA ret=%d fw_error=%d '%s'", ++ ret, fw_error, fw_error_to_str(fw_error)); ++ } ++ ++err: ++ g_free(trans); ++ g_free(hdr); ++ return ret; ++} ++ ++int csv_load_incoming_cpu_state(QEMUFile *f) ++{ ++ int status, ret = 0; ++ ++ /* Only support migrate VMSAs for HYGON CSV2 guest */ ++ if (!sev_es_enabled() || !is_hygon_cpu()) { ++ return 0; ++ } ++ ++ status = qemu_get_be32(f); ++ while (status == ENCRYPTED_CPU_STATE_CONT) { ++ ret = sev_receive_update_vmsa(f); ++ if (ret) { ++ break; ++ } ++ ++ status = qemu_get_be32(f); ++ } ++ ++ return ret; ++} ++ + static const QemuUUID sev_hash_table_header_guid = { + .data = UUID_LE(0x9438d606, 0x4f22, 0x4cc9, 0xb4, 0x79, 0xa7, 0x93, + 0xd4, 0x11, 0xfd, 0x21) +diff --git a/target/i386/sev.h b/target/i386/sev.h +index f788611..209c92f 100644 +--- a/target/i386/sev.h ++++ b/target/i386/sev.h +@@ -43,6 +43,7 @@ typedef struct SevKernelLoaderContext { + + #define RAM_SAVE_ENCRYPTED_PAGE_BATCH 0x4 + #define RAM_SAVE_ENCRYPTED_PAGE_BATCH_END 0x5 ++#define RAM_SAVE_ENCRYPTED_CPU_STATE 0x6 + + #ifdef CONFIG_SEV + bool sev_enabled(void); +diff --git a/target/i386/trace-events b/target/i386/trace-events +index 475de65..87b765c 100644 +--- a/target/i386/trace-events ++++ b/target/i386/trace-events +@@ -17,3 +17,5 @@ kvm_sev_send_finish(void) "" + kvm_sev_receive_start(int policy, void *session, void *pdh) "policy 0x%x session %p pdh %p" + kvm_sev_receive_update_data(void *src, void *dst, int len, void *hdr, int hdr_len) "guest %p trans %p len %d hdr %p hdr_len %d" + kvm_sev_receive_finish(void) "" ++kvm_sev_send_update_vmsa(uint32_t cpu_id, uint32_t cpu_index, void *dst, int len) "cpu_id %d cpu_index %d trans %p len %d" ++kvm_sev_receive_update_vmsa(uint32_t cpu_id, uint32_t cpu_index, void *src, int len, void *hdr, int hdr_len) "cpu_id %d cpu_index %d trans %p len %d hdr %p hdr_len %d" +-- +1.8.3.1 + diff --git a/0214-target-i386-get-set-migrate-GHCB-state.patch b/0214-target-i386-get-set-migrate-GHCB-state.patch new file mode 100644 index 0000000000000000000000000000000000000000..1774fcb5608b096c8d3b4a1a4c01c6462130e41a --- /dev/null +++ b/0214-target-i386-get-set-migrate-GHCB-state.patch @@ -0,0 +1,190 @@ +From 6503910c5467f56bd09a94af3c13b7b7284447f7 Mon Sep 17 00:00:00 2001 +From: panpingsheng +Date: Sat, 12 Jun 2021 15:15:29 +0800 +Subject: [PATCH 224/293] target/i386: get/set/migrate GHCB state + +GHCB state is necessary to CSV2 guest when migrating to target. + +Add GHCB related definition, it also adds corresponding part +to kvm_get/put, and vmstate. + +Signed-off-by: hanliyang +--- + linux-headers/linux/kvm.h | 2 ++ + target/i386/cpu.h | 5 +++++ + target/i386/kvm/kvm.c | 11 +++++++++++ + target/i386/kvm/sev-stub.c | 2 ++ + target/i386/machine.c | 24 ++++++++++++++++++++++++ + target/i386/sev.c | 10 ++++++++++ + target/i386/sev.h | 2 ++ + 7 files changed, 56 insertions(+) + +diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h +index 2ed7ae4..bdcd7a0 100644 +--- a/linux-headers/linux/kvm.h ++++ b/linux-headers/linux/kvm.h +@@ -1199,6 +1199,8 @@ struct kvm_ppc_resize_hpt { + #define KVM_CAP_ARM_SUPPORTED_BLOCK_SIZES 229 + #define KVM_CAP_ARM_SUPPORTED_REG_MASK_RANGES 230 + ++#define KVM_CAP_SEV_ES_GHCB 500 ++ + #define KVM_EXIT_HYPERCALL_VALID_MASK (1 << KVM_HC_MAP_GPA_RANGE) + + #ifdef KVM_CAP_IRQ_ROUTING +diff --git a/target/i386/cpu.h b/target/i386/cpu.h +index 877fc2b..9fc24f7 100644 +--- a/target/i386/cpu.h ++++ b/target/i386/cpu.h +@@ -521,6 +521,8 @@ typedef enum X86Seg { + + #define MSR_VM_HSAVE_PA 0xc0010117 + ++#define MSR_AMD64_SEV_ES_GHCB 0xc0010130 ++ + #define MSR_IA32_XFD 0x000001c4 + #define MSR_IA32_XFD_ERR 0x000001c5 + +@@ -1888,6 +1890,9 @@ typedef struct CPUArchState { + + /* Number of dies within this CPU package. */ + unsigned nr_dies; ++ ++ /* GHCB guest physical address info */ ++ uint64_t ghcb_gpa; + } CPUX86State; + + struct kvm_msrs; +diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c +index 5730d0e..9e65242 100644 +--- a/target/i386/kvm/kvm.c ++++ b/target/i386/kvm/kvm.c +@@ -3625,6 +3625,10 @@ static int kvm_put_msrs(X86CPU *cpu, int level) + } + } + ++ if (sev_kvm_has_msr_ghcb) { ++ kvm_msr_entry_add(cpu, MSR_AMD64_SEV_ES_GHCB, env->ghcb_gpa); ++ } ++ + return kvm_buf_set_msrs(cpu); + } + +@@ -3999,6 +4003,10 @@ static int kvm_get_msrs(X86CPU *cpu) + } + } + ++ if (sev_kvm_has_msr_ghcb) { ++ kvm_msr_entry_add(cpu, MSR_AMD64_SEV_ES_GHCB, 0); ++ } ++ + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_MSRS, cpu->kvm_msr_buf); + if (ret < 0) { + return ret; +@@ -4319,6 +4327,9 @@ static int kvm_get_msrs(X86CPU *cpu) + case MSR_ARCH_LBR_INFO_0 ... MSR_ARCH_LBR_INFO_0 + 31: + env->lbr_records[index - MSR_ARCH_LBR_INFO_0].info = msrs[i].data; + break; ++ case MSR_AMD64_SEV_ES_GHCB: ++ env->ghcb_gpa = msrs[i].data; ++ break; + } + } + +diff --git a/target/i386/kvm/sev-stub.c b/target/i386/kvm/sev-stub.c +index 9989968..a0aac11 100644 +--- a/target/i386/kvm/sev-stub.c ++++ b/target/i386/kvm/sev-stub.c +@@ -14,6 +14,8 @@ + #include "qemu/osdep.h" + #include "sev.h" + ++bool sev_kvm_has_msr_ghcb; ++ + int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) + { + /* If we get here, cgs must be some non-SEV thing */ +diff --git a/target/i386/machine.c b/target/i386/machine.c +index a1041ef..9a1cb8f 100644 +--- a/target/i386/machine.c ++++ b/target/i386/machine.c +@@ -1605,6 +1605,27 @@ static const VMStateDescription vmstate_triple_fault = { + } + }; + ++#if defined(CONFIG_KVM) && defined(TARGET_X86_64) ++static bool msr_ghcb_gpa_needed(void *opaque) ++{ ++ X86CPU *cpu = opaque; ++ CPUX86State *env = &cpu->env; ++ ++ return env->ghcb_gpa != 0; ++} ++ ++static const VMStateDescription vmstate_msr_ghcb_gpa = { ++ .name = "cpu/svm_msr_ghcb_gpa", ++ .version_id = 1, ++ .minimum_version_id = 1, ++ .needed = msr_ghcb_gpa_needed, ++ .fields = (VMStateField[]) { ++ VMSTATE_UINT64(env.ghcb_gpa, X86CPU), ++ VMSTATE_END_OF_LIST() ++ } ++}; ++#endif ++ + const VMStateDescription vmstate_x86_cpu = { + .name = "cpu", + .version_id = 12, +@@ -1751,6 +1772,9 @@ const VMStateDescription vmstate_x86_cpu = { + #endif + &vmstate_arch_lbr, + &vmstate_triple_fault, ++#if defined(CONFIG_KVM) && defined(TARGET_X86_64) ++ &vmstate_msr_ghcb_gpa, ++#endif + NULL + } + }; +diff --git a/target/i386/sev.c b/target/i386/sev.c +index 52693ae..71d317e 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -152,6 +152,8 @@ QEMU_BUILD_BUG_ON(sizeof(PaddedSevHashTable) % 16 != 0); + static SevGuestState *sev_guest; + static Error *sev_mig_blocker; + ++bool sev_kvm_has_msr_ghcb; ++ + static const char *const sev_fw_errlist[] = { + [SEV_RET_SUCCESS] = "", + [SEV_RET_INVALID_PLATFORM_STATE] = "Platform state is invalid", +@@ -1198,6 +1200,14 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) + cgs_class->memory_encryption_ops = &sev_memory_encryption_ops; + QTAILQ_INIT(&sev->shared_regions_list); + ++ /* Determine whether support MSR_AMD64_SEV_ES_GHCB */ ++ if (sev_es_enabled()) { ++ sev_kvm_has_msr_ghcb = ++ kvm_vm_check_extension(kvm_state, KVM_CAP_SEV_ES_GHCB); ++ } else { ++ sev_kvm_has_msr_ghcb = false; ++ } ++ + cgs->ready = true; + + return 0; +diff --git a/target/i386/sev.h b/target/i386/sev.h +index 209c92f..0bfe387 100644 +--- a/target/i386/sev.h ++++ b/target/i386/sev.h +@@ -78,4 +78,6 @@ void sev_del_migrate_blocker(void); + + int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp); + ++extern bool sev_kvm_has_msr_ghcb; ++ + #endif +-- +1.8.3.1 + diff --git a/0215-target-i386-kvm-Fix-the-resettable-info-when-emulate.patch b/0215-target-i386-kvm-Fix-the-resettable-info-when-emulate.patch new file mode 100644 index 0000000000000000000000000000000000000000..6f96b88e20a0d67c8bd58ff47c0106cba9b6781d --- /dev/null +++ b/0215-target-i386-kvm-Fix-the-resettable-info-when-emulate.patch @@ -0,0 +1,179 @@ +From 641fe421b552336c5dfb7f3e6840903e3e5b95ba Mon Sep 17 00:00:00 2001 +From: hanliyang +Date: Sun, 19 Jun 2022 16:49:45 +0800 +Subject: [PATCH 225/293] target/i386/kvm: Fix the resettable info when emulate + Hygon CSV2 guest + +SEV-ES guest will be terminated by QEMU when receive reboot request. +In order to support reboot for CSV2 guest, report resettable in +kvm_arch_cpu_check_are_resettable(). But the CSV2 guest is still not +resettable if it was migrated to target machine. + +Signed-off-by: hanliyang +--- + target/i386/csv-sysemu-stub.c | 16 ++++++++++++++++ + target/i386/csv.c | 20 ++++++++++++++++++++ + target/i386/csv.h | 2 ++ + target/i386/kvm/csv-stub.c | 17 +++++++++++++++++ + target/i386/kvm/kvm.c | 4 ++++ + target/i386/kvm/meson.build | 1 + + target/i386/meson.build | 1 + + target/i386/sev.c | 9 +++++++++ + 8 files changed, 70 insertions(+) + create mode 100644 target/i386/csv-sysemu-stub.c + create mode 100644 target/i386/csv.c + create mode 100644 target/i386/kvm/csv-stub.c + +diff --git a/target/i386/csv-sysemu-stub.c b/target/i386/csv-sysemu-stub.c +new file mode 100644 +index 0000000..5874e4c +--- /dev/null ++++ b/target/i386/csv-sysemu-stub.c +@@ -0,0 +1,16 @@ ++/* ++ * QEMU CSV system stub ++ * ++ * Copyright: Hygon Info Technologies Ltd. 2022 ++ * ++ * Author: ++ * Jiang Xin ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "sev.h" ++#include "csv.h" +diff --git a/target/i386/csv.c b/target/i386/csv.c +new file mode 100644 +index 0000000..88fb05a +--- /dev/null ++++ b/target/i386/csv.c +@@ -0,0 +1,20 @@ ++/* ++ * QEMU CSV support ++ * ++ * Copyright: Hygon Info Technologies Ltd. 2022 ++ * ++ * Author: ++ * Jiang Xin ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++ ++#include "cpu.h" ++#include "sev.h" ++#include "csv.h" ++ ++bool csv_kvm_cpu_reset_inhibit; +diff --git a/target/i386/csv.h b/target/i386/csv.h +index 47741a0..ac4bb5b 100644 +--- a/target/i386/csv.h ++++ b/target/i386/csv.h +@@ -46,6 +46,8 @@ static bool __attribute__((unused)) is_hygon_cpu(void) + + #define CSV_OUTGOING_PAGE_WINDOW_SIZE (4094 * TARGET_PAGE_SIZE) + ++extern bool csv_kvm_cpu_reset_inhibit; ++ + typedef struct CsvBatchCmdList CsvBatchCmdList; + typedef void (*CsvDestroyCmdNodeFn) (void *data); + +diff --git a/target/i386/kvm/csv-stub.c b/target/i386/kvm/csv-stub.c +new file mode 100644 +index 0000000..4d1376f +--- /dev/null ++++ b/target/i386/kvm/csv-stub.c +@@ -0,0 +1,17 @@ ++/* ++ * QEMU CSV stub ++ * ++ * Copyright Hygon Info Technologies Ltd. 2024 ++ * ++ * Authors: ++ * Han Liyang ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "csv.h" ++ ++bool csv_kvm_cpu_reset_inhibit; +diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c +index 9e65242..2866a6d 100644 +--- a/target/i386/kvm/kvm.c ++++ b/target/i386/kvm/kvm.c +@@ -32,6 +32,7 @@ + #include "sysemu/runstate.h" + #include "kvm_i386.h" + #include "sev.h" ++#include "csv.h" + #include "xen-emu.h" + #include "hyperv.h" + #include "hyperv-proto.h" +@@ -5710,6 +5711,9 @@ bool kvm_has_waitpkg(void) + + bool kvm_arch_cpu_check_are_resettable(void) + { ++ if (is_hygon_cpu()) ++ return !csv_kvm_cpu_reset_inhibit; ++ + return !sev_es_enabled(); + } + +diff --git a/target/i386/kvm/meson.build b/target/i386/kvm/meson.build +index 84d9143..3c3f8cf 100644 +--- a/target/i386/kvm/meson.build ++++ b/target/i386/kvm/meson.build +@@ -8,6 +8,7 @@ i386_kvm_ss.add(files( + i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files('xen-emu.c')) + + i386_kvm_ss.add(when: 'CONFIG_SEV', if_false: files('sev-stub.c')) ++i386_kvm_ss.add(when: 'CONFIG_CSV', if_false: files('csv-stub.c')) + + i386_system_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c'), if_false: files('hyperv-stub.c')) + +diff --git a/target/i386/meson.build b/target/i386/meson.build +index 7c74bfa..594a0a6 100644 +--- a/target/i386/meson.build ++++ b/target/i386/meson.build +@@ -21,6 +21,7 @@ i386_system_ss.add(files( + 'cpu-sysemu.c', + )) + i386_system_ss.add(when: 'CONFIG_SEV', if_true: files('sev.c'), if_false: files('sev-sysemu-stub.c')) ++i386_system_ss.add(when: 'CONFIG_CSV', if_true: files('csv.c'), if_false: files('csv-sysemu-stub.c')) + + i386_user_ss = ss.source_set() + +diff --git a/target/i386/sev.c b/target/i386/sev.c +index 71d317e..3406861 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -1190,6 +1190,15 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) + error_setg(errp, "%s: failed to create encryption context", __func__); + goto err; + } ++ } else { ++ /* ++ * The CSV2 guest is not resettable after migrated to target machine, ++ * set csv_kvm_cpu_reset_inhibit to true to indicate the CSV2 guest is ++ * not resettable. ++ */ ++ if (is_hygon_cpu() && sev_es_enabled()) { ++ csv_kvm_cpu_reset_inhibit = true; ++ } + } + + ram_block_notifier_add(&sev_ram_notifier); +-- +1.8.3.1 + diff --git a/0216-kvm-Add-support-for-CSV2-reboot.patch b/0216-kvm-Add-support-for-CSV2-reboot.patch new file mode 100644 index 0000000000000000000000000000000000000000..ea7bb857a0cd5b35837514a7bd2e1c6732699bf9 --- /dev/null +++ b/0216-kvm-Add-support-for-CSV2-reboot.patch @@ -0,0 +1,170 @@ +From 63ef0cc04f7e80891de6aa80910980ebf3ff758f Mon Sep 17 00:00:00 2001 +From: hanliyang +Date: Thu, 15 Apr 2021 08:32:24 -0400 +Subject: [PATCH 226/293] kvm: Add support for CSV2 reboot + +Linux will set vcpu.arch.guest_state_protected to true after execute +LAUNCH_UPDATE_VMSA successfully, and then KVM will prevent any changes +to VMCB State Save Area. + +In order to support CSV2 guest reboot, calls cpus_control_pre_system_reset() +to set vcpu.arch.guest_state_protected to false, and calls +cpus_control_post_system_reset() to restore VMSA of guest's vcpu with +data generated by LAUNCH_UPDATE_VMSA. + +In addition, for memory encrypted guest, additional works may be +required during system reset, such as flushing the cache. The function +cpus_control_post_system_reset() hints linux to flush caches of guest +memory. + +Signed-off-by: hanliyang +--- + accel/kvm/kvm-accel-ops.c | 3 +++ + accel/kvm/kvm-all.c | 10 ++++++++++ + accel/kvm/kvm-cpus.h | 3 +++ + include/sysemu/accel-ops.h | 3 +++ + include/sysemu/cpus.h | 2 ++ + linux-headers/linux/kvm.h | 4 ++++ + system/cpus.c | 14 ++++++++++++++ + system/runstate.c | 4 ++++ + 8 files changed, 43 insertions(+) + +diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c +index 6195150..54f1902 100644 +--- a/accel/kvm/kvm-accel-ops.c ++++ b/accel/kvm/kvm-accel-ops.c +@@ -112,6 +112,9 @@ static void kvm_accel_ops_class_init(ObjectClass *oc, void *data) + ops->remove_breakpoint = kvm_remove_breakpoint; + ops->remove_all_breakpoints = kvm_remove_all_breakpoints; + #endif ++ ++ ops->control_pre_system_reset = kvm_cpus_control_pre_system_reset; ++ ops->control_post_system_reset = kvm_cpus_control_post_system_reset; + } + + static const TypeInfo kvm_accel_ops_type = { +diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c +index e39a810..e5ed69c 100644 +--- a/accel/kvm/kvm-all.c ++++ b/accel/kvm/kvm-all.c +@@ -2761,6 +2761,16 @@ void kvm_cpu_synchronize_pre_loadvm(CPUState *cpu) + run_on_cpu(cpu, do_kvm_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL); + } + ++void kvm_cpus_control_pre_system_reset(void) ++{ ++ kvm_vm_ioctl(kvm_state, KVM_CONTROL_VCPU_PRE_SYSTEM_RESET, NULL); ++} ++ ++void kvm_cpus_control_post_system_reset(void) ++{ ++ kvm_vm_ioctl(kvm_state, KVM_CONTROL_VCPU_POST_SYSTEM_RESET, NULL); ++} ++ + #ifdef KVM_HAVE_MCE_INJECTION + static __thread void *pending_sigbus_addr; + static __thread int pending_sigbus_code; +diff --git a/accel/kvm/kvm-cpus.h b/accel/kvm/kvm-cpus.h +index ca40add..27b9d0d 100644 +--- a/accel/kvm/kvm-cpus.h ++++ b/accel/kvm/kvm-cpus.h +@@ -23,4 +23,7 @@ int kvm_insert_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len); + int kvm_remove_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len); + void kvm_remove_all_breakpoints(CPUState *cpu); + ++void kvm_cpus_control_pre_system_reset(void); ++void kvm_cpus_control_post_system_reset(void); ++ + #endif /* KVM_CPUS_H */ +diff --git a/include/sysemu/accel-ops.h b/include/sysemu/accel-ops.h +index ef91fc2..7a32e7f 100644 +--- a/include/sysemu/accel-ops.h ++++ b/include/sysemu/accel-ops.h +@@ -53,6 +53,9 @@ struct AccelOpsClass { + int (*insert_breakpoint)(CPUState *cpu, int type, vaddr addr, vaddr len); + int (*remove_breakpoint)(CPUState *cpu, int type, vaddr addr, vaddr len); + void (*remove_all_breakpoints)(CPUState *cpu); ++ ++ void (*control_pre_system_reset)(void); ++ void (*control_post_system_reset)(void); + }; + + #endif /* ACCEL_OPS_H */ +diff --git a/include/sysemu/cpus.h b/include/sysemu/cpus.h +index b4a566c..f24d27d 100644 +--- a/include/sysemu/cpus.h ++++ b/include/sysemu/cpus.h +@@ -44,6 +44,8 @@ extern int icount_align_option; + void qemu_cpu_kick_self(void); + + bool cpus_are_resettable(void); ++void cpus_control_pre_system_reset(void); ++void cpus_control_post_system_reset(void); + + void cpu_synchronize_all_states(void); + void cpu_synchronize_all_post_reset(void); +diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h +index bdcd7a0..372d0bd 100644 +--- a/linux-headers/linux/kvm.h ++++ b/linux-headers/linux/kvm.h +@@ -1583,6 +1583,10 @@ struct kvm_s390_ucas_mapping { + #define KVM_GET_DEVICE_ATTR _IOW(KVMIO, 0xe2, struct kvm_device_attr) + #define KVM_HAS_DEVICE_ATTR _IOW(KVMIO, 0xe3, struct kvm_device_attr) + ++/* ioctls for control vcpu setup during system reset */ ++#define KVM_CONTROL_VCPU_PRE_SYSTEM_RESET _IO(KVMIO, 0xe8) ++#define KVM_CONTROL_VCPU_POST_SYSTEM_RESET _IO(KVMIO, 0xe9) ++ + /* + * ioctls for vcpu fds + */ +diff --git a/system/cpus.c b/system/cpus.c +index a444a74..cbeec13 100644 +--- a/system/cpus.c ++++ b/system/cpus.c +@@ -193,6 +193,20 @@ void cpu_synchronize_pre_loadvm(CPUState *cpu) + } + } + ++void cpus_control_pre_system_reset(void) ++{ ++ if (cpus_accel->control_pre_system_reset) { ++ cpus_accel->control_pre_system_reset(); ++ } ++} ++ ++void cpus_control_post_system_reset(void) ++{ ++ if (cpus_accel->control_post_system_reset) { ++ cpus_accel->control_post_system_reset(); ++ } ++} ++ + bool cpus_are_resettable(void) + { + if (cpus_accel->cpus_are_resettable) { +diff --git a/system/runstate.c b/system/runstate.c +index ea9d6c2..365f2f4 100644 +--- a/system/runstate.c ++++ b/system/runstate.c +@@ -486,6 +486,8 @@ void qemu_system_reset(ShutdownCause reason) + + mc = current_machine ? MACHINE_GET_CLASS(current_machine) : NULL; + ++ cpus_control_pre_system_reset(); ++ + cpu_synchronize_all_states(); + + if (mc && mc->reset) { +@@ -502,6 +504,8 @@ void qemu_system_reset(ShutdownCause reason) + qapi_event_send_reset(shutdown_caused_by_guest(reason), reason); + } + cpu_synchronize_all_post_reset(); ++ ++ cpus_control_post_system_reset(); + } + + /* +-- +1.8.3.1 + diff --git a/0217-target-i386-csv-Add-CSV3-context.patch b/0217-target-i386-csv-Add-CSV3-context.patch new file mode 100644 index 0000000000000000000000000000000000000000..0d664295babfefa6f5974ebe90d9b515cdfcb5f7 --- /dev/null +++ b/0217-target-i386-csv-Add-CSV3-context.patch @@ -0,0 +1,86 @@ +From 87af3b6cab2252a01cb2f2eee7a85f74d3e90e0b Mon Sep 17 00:00:00 2001 +From: jiangxin +Date: Tue, 24 Aug 2021 14:57:28 +0800 +Subject: [PATCH 227/293] target/i386: csv: Add CSV3 context + +CSV/CSV2/CSV3 are the secure virtualization features on Hygon CPUs. +The CSV and CSV2 are compatible with the AMD SEV and SEV-ES, +respectively. From CSV3, we introduced more secure features to +protect the guest, users can bit 6 of the guest policy to run a +CSV3 guest. + +Add the context and the build option. + +Signed-off-by: Xin Jiang +Signed-off-by: hanliyang +--- + target/i386/csv.c | 11 +++++++++++ + target/i386/csv.h | 18 ++++++++++++++++++ + 2 files changed, 29 insertions(+) + +diff --git a/target/i386/csv.c b/target/i386/csv.c +index 88fb05a..9a1de04 100644 +--- a/target/i386/csv.c ++++ b/target/i386/csv.c +@@ -18,3 +18,14 @@ + #include "csv.h" + + bool csv_kvm_cpu_reset_inhibit; ++ ++Csv3GuestState csv3_guest = { 0 }; ++ ++bool ++csv3_enabled(void) ++{ ++ if (!is_hygon_cpu()) ++ return false; ++ ++ return sev_es_enabled() && (csv3_guest.policy & GUEST_POLICY_CSV3_BIT); ++} +diff --git a/target/i386/csv.h b/target/i386/csv.h +index ac4bb5b..7852fb8 100644 +--- a/target/i386/csv.h ++++ b/target/i386/csv.h +@@ -14,6 +14,8 @@ + #ifndef I386_CSV_H + #define I386_CSV_H + ++#include "qapi/qapi-commands-misc-target.h" ++ + #ifdef CONFIG_CSV + + #include "cpu.h" +@@ -38,9 +40,12 @@ static bool __attribute__((unused)) is_hygon_cpu(void) + return false; + } + ++bool csv3_enabled(void); ++ + #else + + #define is_hygon_cpu() (false) ++#define csv3_enabled() (false) + + #endif + +@@ -64,4 +69,17 @@ int csv_load_queued_incoming_pages(QEMUFile *f); + int csv_save_outgoing_cpu_state(QEMUFile *f, uint64_t *bytes_sent); + int csv_load_incoming_cpu_state(QEMUFile *f); + ++/* CSV3 */ ++#define GUEST_POLICY_CSV3_BIT (1 << 6) ++ ++struct Csv3GuestState { ++ uint32_t policy; ++ int sev_fd; ++ void *state; ++}; ++ ++typedef struct Csv3GuestState Csv3GuestState; ++ ++extern struct Csv3GuestState csv3_guest; ++ + #endif +-- +1.8.3.1 + diff --git a/0218-target-i386-csv-Add-command-to-initialize-CSV3-conte.patch b/0218-target-i386-csv-Add-command-to-initialize-CSV3-conte.patch new file mode 100644 index 0000000000000000000000000000000000000000..2212c96a0d8cc36f6e50e65263b98ed6f93cff17 --- /dev/null +++ b/0218-target-i386-csv-Add-command-to-initialize-CSV3-conte.patch @@ -0,0 +1,202 @@ +From 38d00f2f50a36352047f8ab60a62b211ffe6ca2c Mon Sep 17 00:00:00 2001 +From: jiangxin +Date: Wed, 25 Aug 2021 11:07:41 +0800 +Subject: [PATCH 228/293] target/i386: csv: Add command to initialize CSV3 + context + +When CSV3 is enabled, KVM_CSV3_INIT command is used to initialize +the platform, which is implemented by reusing the SEV API framework +and extending the functionality. + +The KVM_CSV3_INIT command should be performed earlier than +any other command. + +Signed-off-by: Xin Jiang +Signed-off-by: hanliyang +--- + linux-headers/linux/kvm.h | 11 +++++++++++ + target/i386/csv-sysemu-stub.c | 5 +++++ + target/i386/csv.c | 45 +++++++++++++++++++++++++++++++++++++++++++ + target/i386/csv.h | 4 ++++ + target/i386/sev.c | 17 ++++++++++++++++ + target/i386/sev.h | 7 +++++++ + 6 files changed, 89 insertions(+) + +diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h +index 372d0bd..d976908 100644 +--- a/linux-headers/linux/kvm.h ++++ b/linux-headers/linux/kvm.h +@@ -2060,6 +2060,17 @@ struct kvm_csv_command_batch { + __u64 csv_batch_list_uaddr; + }; + ++/* CSV3 command */ ++enum csv3_cmd_id { ++ KVM_CSV3_NR_MIN = 0xc0, ++ ++ KVM_CSV3_INIT = KVM_CSV3_NR_MIN, ++}; ++ ++struct kvm_csv3_init_data { ++ __u64 nodemask; ++}; ++ + #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) + #define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1) + #define KVM_DEV_ASSIGN_MASK_INTX (1 << 2) +diff --git a/target/i386/csv-sysemu-stub.c b/target/i386/csv-sysemu-stub.c +index 5874e4c..72f0f5c 100644 +--- a/target/i386/csv-sysemu-stub.c ++++ b/target/i386/csv-sysemu-stub.c +@@ -14,3 +14,8 @@ + #include "qemu/osdep.h" + #include "sev.h" + #include "csv.h" ++ ++int csv3_init(uint32_t policy, int fd, void *state, struct sev_ops *ops) ++{ ++ return 0; ++} +diff --git a/target/i386/csv.c b/target/i386/csv.c +index 9a1de04..f02aadb 100644 +--- a/target/i386/csv.c ++++ b/target/i386/csv.c +@@ -12,6 +12,13 @@ + */ + + #include "qemu/osdep.h" ++#include "qemu/error-report.h" ++ ++#include ++ ++#ifdef CONFIG_NUMA ++#include ++#endif + + #include "cpu.h" + #include "sev.h" +@@ -21,6 +28,44 @@ bool csv_kvm_cpu_reset_inhibit; + + Csv3GuestState csv3_guest = { 0 }; + ++int ++csv3_init(uint32_t policy, int fd, void *state, struct sev_ops *ops) ++{ ++ int fw_error; ++ int ret; ++ struct kvm_csv3_init_data data = { 0 }; ++ ++#ifdef CONFIG_NUMA ++ int mode; ++ unsigned long nodemask; ++ ++ /* Set flags as 0 to retrieve the default NUMA policy. */ ++ ret = get_mempolicy(&mode, &nodemask, sizeof(nodemask) * 8, NULL, 0); ++ if (ret == 0 && (mode == MPOL_BIND)) ++ data.nodemask = nodemask; ++#endif ++ ++ if (!ops || !ops->sev_ioctl || !ops->fw_error_to_str) ++ return -1; ++ ++ csv3_guest.policy = policy; ++ if (csv3_enabled()) { ++ ret = ops->sev_ioctl(fd, KVM_CSV3_INIT, &data, &fw_error); ++ if (ret) { ++ csv3_guest.policy = 0; ++ error_report("%s: Fail to initialize ret=%d fw_error=%d '%s'", ++ __func__, ret, fw_error, ops->fw_error_to_str(fw_error)); ++ return -1; ++ } ++ ++ csv3_guest.sev_fd = fd; ++ csv3_guest.state = state; ++ csv3_guest.sev_ioctl = ops->sev_ioctl; ++ csv3_guest.fw_error_to_str = ops->fw_error_to_str; ++ } ++ return 0; ++} ++ + bool + csv3_enabled(void) + { +diff --git a/target/i386/csv.h b/target/i386/csv.h +index 7852fb8..cf125fe 100644 +--- a/target/i386/csv.h ++++ b/target/i386/csv.h +@@ -15,6 +15,7 @@ + #define I386_CSV_H + + #include "qapi/qapi-commands-misc-target.h" ++#include "sev.h" + + #ifdef CONFIG_CSV + +@@ -76,10 +77,13 @@ struct Csv3GuestState { + uint32_t policy; + int sev_fd; + void *state; ++ int (*sev_ioctl)(int fd, int cmd, void *data, int *error); ++ const char *(*fw_error_to_str)(int code); + }; + + typedef struct Csv3GuestState Csv3GuestState; + + extern struct Csv3GuestState csv3_guest; ++extern int csv3_init(uint32_t policy, int fd, void *state, struct sev_ops *ops); + + #endif +diff --git a/target/i386/sev.c b/target/i386/sev.c +index 3406861..50f3429 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -1180,6 +1180,18 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) + goto err; + } + ++ /* Support CSV3 */ ++ if (!ret && cmd == KVM_SEV_ES_INIT) { ++ ret = csv3_init(sev_guest->policy, sev->sev_fd, (void *)&sev->state, &sev_ops); ++ if (ret) { ++ error_setg(errp, "%s: failed to init csv3 context", __func__); ++ goto err; ++ } ++ /* The CSV3 guest is not resettable */ ++ if (csv3_enabled()) ++ csv_kvm_cpu_reset_inhibit = true; ++ } ++ + /* + * The LAUNCH context is used for new guest, if its an incoming guest + * then RECEIVE context will be created after the connection is established. +@@ -2589,6 +2601,11 @@ bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) + return ret; + } + ++struct sev_ops sev_ops = { ++ .sev_ioctl = sev_ioctl, ++ .fw_error_to_str = fw_error_to_str, ++}; ++ + static void + sev_register_types(void) + { +diff --git a/target/i386/sev.h b/target/i386/sev.h +index 0bfe387..e91431e 100644 +--- a/target/i386/sev.h ++++ b/target/i386/sev.h +@@ -80,4 +80,11 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp); + + extern bool sev_kvm_has_msr_ghcb; + ++struct sev_ops { ++ int (*sev_ioctl)(int fd, int cmd, void *data, int *error); ++ const char *(*fw_error_to_str)(int code); ++}; ++ ++extern struct sev_ops sev_ops; ++ + #endif +-- +1.8.3.1 + diff --git a/0219-target-i386-csv-Add-command-to-load-data-to-CSV3-gue.patch b/0219-target-i386-csv-Add-command-to-load-data-to-CSV3-gue.patch new file mode 100644 index 0000000000000000000000000000000000000000..a8876e5210d904198ee55ce71a9338a976e41333 --- /dev/null +++ b/0219-target-i386-csv-Add-command-to-load-data-to-CSV3-gue.patch @@ -0,0 +1,166 @@ +From e7d21782f961f95f3fb2fa93b6135f51976ea040 Mon Sep 17 00:00:00 2001 +From: jiangxin +Date: Wed, 25 Aug 2021 09:59:16 +0800 +Subject: [PATCH 229/293] target/i386: csv: Add command to load data to CSV3 + guest memory + +The KVM_CSV3_LAUNCH_ENCRYPT_DATA command is used to load data to an +encrypted guest memory in an isolated memory region that guest owns. + +Signed-off-by: Xin Jiang +Signed-off-by: hanliyang +--- + linux-headers/linux/kvm.h | 7 +++++ + target/i386/csv-sysemu-stub.c | 5 ++++ + target/i386/csv.c | 69 +++++++++++++++++++++++++++++++++++++++++++ + target/i386/csv.h | 2 ++ + target/i386/trace-events | 3 ++ + 5 files changed, 86 insertions(+) + +diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h +index d976908..fa7b415 100644 +--- a/linux-headers/linux/kvm.h ++++ b/linux-headers/linux/kvm.h +@@ -2065,6 +2065,13 @@ enum csv3_cmd_id { + KVM_CSV3_NR_MIN = 0xc0, + + KVM_CSV3_INIT = KVM_CSV3_NR_MIN, ++ KVM_CSV3_LAUNCH_ENCRYPT_DATA, ++}; ++ ++struct kvm_csv3_launch_encrypt_data { ++ __u64 gpa; ++ __u64 uaddr; ++ __u32 len; + }; + + struct kvm_csv3_init_data { +diff --git a/target/i386/csv-sysemu-stub.c b/target/i386/csv-sysemu-stub.c +index 72f0f5c..b0ccbd2 100644 +--- a/target/i386/csv-sysemu-stub.c ++++ b/target/i386/csv-sysemu-stub.c +@@ -19,3 +19,8 @@ int csv3_init(uint32_t policy, int fd, void *state, struct sev_ops *ops) + { + return 0; + } ++ ++int csv3_load_data(uint64_t gpa, uint8_t *ptr, uint64_t len, Error **errp) ++{ ++ g_assert_not_reached(); ++} +diff --git a/target/i386/csv.c b/target/i386/csv.c +index f02aadb..0e3f447 100644 +--- a/target/i386/csv.c ++++ b/target/i386/csv.c +@@ -13,6 +13,7 @@ + + #include "qemu/osdep.h" + #include "qemu/error-report.h" ++#include "qapi/error.h" + + #include + +@@ -20,6 +21,7 @@ + #include + #endif + ++#include "trace.h" + #include "cpu.h" + #include "sev.h" + #include "csv.h" +@@ -74,3 +76,70 @@ csv3_enabled(void) + + return sev_es_enabled() && (csv3_guest.policy & GUEST_POLICY_CSV3_BIT); + } ++ ++static bool ++csv3_check_state(SevState state) ++{ ++ return *((SevState *)csv3_guest.state) == state ? true : false; ++} ++ ++static int ++csv3_ioctl(int cmd, void *data, int *error) ++{ ++ if (csv3_guest.sev_ioctl) ++ return csv3_guest.sev_ioctl(csv3_guest.sev_fd, cmd, data, error); ++ else ++ return -1; ++} ++ ++static const char * ++fw_error_to_str(int code) ++{ ++ if (csv3_guest.fw_error_to_str) ++ return csv3_guest.fw_error_to_str(code); ++ else ++ return NULL; ++} ++ ++static int ++csv3_launch_encrypt_data(uint64_t gpa, uint8_t *addr, uint64_t len) ++{ ++ int ret, fw_error; ++ struct kvm_csv3_launch_encrypt_data update; ++ ++ if (!addr || !len) { ++ return 1; ++ } ++ ++ update.gpa = (__u64)gpa; ++ update.uaddr = (__u64)(unsigned long)addr; ++ update.len = len; ++ trace_kvm_csv3_launch_encrypt_data(gpa, addr, len); ++ ret = csv3_ioctl(KVM_CSV3_LAUNCH_ENCRYPT_DATA, &update, &fw_error); ++ if (ret) { ++ error_report("%s: CSV3 LAUNCH_ENCRYPT_DATA ret=%d fw_error=%d '%s'", ++ __func__, ret, fw_error, fw_error_to_str(fw_error)); ++ } ++ ++ return ret; ++} ++ ++int ++csv3_load_data(uint64_t gpa, uint8_t *ptr, uint64_t len, Error **errp) ++{ ++ int ret = 0; ++ ++ if (!csv3_enabled()) { ++ error_setg(errp, "%s: CSV3 is not enabled", __func__); ++ return -1; ++ } ++ ++ /* if CSV3 is in update state then load the data to secure memory */ ++ if (csv3_check_state(SEV_STATE_LAUNCH_UPDATE)) { ++ ret = csv3_launch_encrypt_data(gpa, ptr, len); ++ if (ret) ++ error_setg(errp, "%s: CSV3 fail to encrypt data", __func__); ++ } ++ ++ return ret; ++} +diff --git a/target/i386/csv.h b/target/i386/csv.h +index cf125fe..928774f 100644 +--- a/target/i386/csv.h ++++ b/target/i386/csv.h +@@ -86,4 +86,6 @@ typedef struct Csv3GuestState Csv3GuestState; + extern struct Csv3GuestState csv3_guest; + extern int csv3_init(uint32_t policy, int fd, void *state, struct sev_ops *ops); + ++int csv3_load_data(uint64_t gpa, uint8_t *ptr, uint64_t len, Error **errp); ++ + #endif +diff --git a/target/i386/trace-events b/target/i386/trace-events +index 87b765c..e07061b 100644 +--- a/target/i386/trace-events ++++ b/target/i386/trace-events +@@ -19,3 +19,6 @@ kvm_sev_receive_update_data(void *src, void *dst, int len, void *hdr, int hdr_le + kvm_sev_receive_finish(void) "" + kvm_sev_send_update_vmsa(uint32_t cpu_id, uint32_t cpu_index, void *dst, int len) "cpu_id %d cpu_index %d trans %p len %d" + kvm_sev_receive_update_vmsa(uint32_t cpu_id, uint32_t cpu_index, void *src, int len, void *hdr, int hdr_len) "cpu_id %d cpu_index %d trans %p len %d hdr %p hdr_len %d" ++ ++# csv.c ++kvm_csv3_launch_encrypt_data(uint64_t gpa, void *addr, uint64_t len) "gpa 0x%" PRIx64 "addr %p len 0x%" PRIu64 +-- +1.8.3.1 + diff --git a/0220-target-i386-csv-Add-command-to-load-vmcb-to-CSV3-gue.patch b/0220-target-i386-csv-Add-command-to-load-vmcb-to-CSV3-gue.patch new file mode 100644 index 0000000000000000000000000000000000000000..87bde58cd69f0cdfa954d4016e5289d8b14e0a61 --- /dev/null +++ b/0220-target-i386-csv-Add-command-to-load-vmcb-to-CSV3-gue.patch @@ -0,0 +1,108 @@ +From 70f106ff7e9aaf80a4e114d3027c5b9e41a3fdee Mon Sep 17 00:00:00 2001 +From: jiangxin +Date: Wed, 25 Aug 2021 12:25:05 +0800 +Subject: [PATCH 230/293] target/i386: csv: Add command to load vmcb to CSV3 + guest memory + +The KVM_CSV3_LAUNCH_ENCRYPT_VMCB command is used to load and encrypt +the initial VMCB data to secure memory in an isolated region that +guest owns. + +Signed-off-by: Xin Jiang +Signed-off-by: hanliyang +--- + linux-headers/linux/kvm.h | 1 + + target/i386/csv-sysemu-stub.c | 5 +++++ + target/i386/csv.c | 21 +++++++++++++++++++++ + target/i386/csv.h | 1 + + target/i386/sev.c | 8 ++++++-- + 5 files changed, 34 insertions(+), 2 deletions(-) + +diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h +index fa7b415..061b2e7 100644 +--- a/linux-headers/linux/kvm.h ++++ b/linux-headers/linux/kvm.h +@@ -2066,6 +2066,7 @@ enum csv3_cmd_id { + + KVM_CSV3_INIT = KVM_CSV3_NR_MIN, + KVM_CSV3_LAUNCH_ENCRYPT_DATA, ++ KVM_CSV3_LAUNCH_ENCRYPT_VMCB, + }; + + struct kvm_csv3_launch_encrypt_data { +diff --git a/target/i386/csv-sysemu-stub.c b/target/i386/csv-sysemu-stub.c +index b0ccbd2..23d885f 100644 +--- a/target/i386/csv-sysemu-stub.c ++++ b/target/i386/csv-sysemu-stub.c +@@ -24,3 +24,8 @@ int csv3_load_data(uint64_t gpa, uint8_t *ptr, uint64_t len, Error **errp) + { + g_assert_not_reached(); + } ++ ++int csv3_launch_encrypt_vmcb(void) ++{ ++ g_assert_not_reached(); ++} +diff --git a/target/i386/csv.c b/target/i386/csv.c +index 0e3f447..f423b89 100644 +--- a/target/i386/csv.c ++++ b/target/i386/csv.c +@@ -143,3 +143,24 @@ csv3_load_data(uint64_t gpa, uint8_t *ptr, uint64_t len, Error **errp) + + return ret; + } ++ ++int ++csv3_launch_encrypt_vmcb(void) ++{ ++ int ret, fw_error; ++ ++ if (!csv3_enabled()) { ++ error_report("%s: CSV3 is not enabled",__func__); ++ return -1; ++ } ++ ++ ret = csv3_ioctl(KVM_CSV3_LAUNCH_ENCRYPT_VMCB, NULL, &fw_error); ++ if (ret) { ++ error_report("%s: CSV3 LAUNCH_ENCRYPT_VMCB ret=%d fw_error=%d '%s'", ++ __func__, ret, fw_error, fw_error_to_str(fw_error)); ++ goto err; ++ } ++ ++err: ++ return ret; ++} +diff --git a/target/i386/csv.h b/target/i386/csv.h +index 928774f..6444d54 100644 +--- a/target/i386/csv.h ++++ b/target/i386/csv.h +@@ -85,6 +85,7 @@ typedef struct Csv3GuestState Csv3GuestState; + + extern struct Csv3GuestState csv3_guest; + extern int csv3_init(uint32_t policy, int fd, void *state, struct sev_ops *ops); ++extern int csv3_launch_encrypt_vmcb(void); + + int csv3_load_data(uint64_t gpa, uint8_t *ptr, uint64_t len, Error **errp); + +diff --git a/target/i386/sev.c b/target/i386/sev.c +index 50f3429..b77572f 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -857,8 +857,12 @@ sev_launch_get_measure(Notifier *notifier, void *unused) + } + + if (sev_es_enabled()) { +- /* measure all the VM save areas before getting launch_measure */ +- ret = sev_launch_update_vmsa(sev); ++ if (csv3_enabled()) { ++ ret = csv3_launch_encrypt_vmcb(); ++ } else { ++ /* measure all the VM save areas before getting launch_measure */ ++ ret = sev_launch_update_vmsa(sev); ++ } + if (ret) { + exit(1); + } +-- +1.8.3.1 + diff --git a/0221-target-i386-cpu-Populate-CPUID-0x8000_001F-when-CSV3.patch b/0221-target-i386-cpu-Populate-CPUID-0x8000_001F-when-CSV3.patch new file mode 100644 index 0000000000000000000000000000000000000000..978e286438c364926068364e898913caf6a74603 --- /dev/null +++ b/0221-target-i386-cpu-Populate-CPUID-0x8000_001F-when-CSV3.patch @@ -0,0 +1,41 @@ +From 59fb06b7cca8a87c9b3ab69c99649990843d1611 Mon Sep 17 00:00:00 2001 +From: jiangxin +Date: Tue, 24 Aug 2021 17:31:28 +0800 +Subject: [PATCH 231/293] target/i386: cpu: Populate CPUID 0x8000_001F when + CSV3 is active + +On Hygon platform, bit 30 of EAX indicates whether +this feature is supported in hardware. + +When CSV3 is active, CPUID 0x8000_001F provides +information for it. + +Signed-off-by: Xin Jiang +Signed-off-by: hanliyang +--- + target/i386/cpu.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index ffdaf16..a373d8e 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -29,6 +29,7 @@ + #include "hvf/hvf-i386.h" + #include "kvm/kvm_i386.h" + #include "sev.h" ++#include "csv.h" + #include "qapi/error.h" + #include "qemu/error-report.h" + #include "qapi/qapi-visit-machine.h" +@@ -6808,6 +6809,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, + if (sev_enabled()) { + *eax = 0x2; + *eax |= sev_es_enabled() ? 0x8 : 0; ++ *eax |= csv3_enabled() ? 0x40000000 : 0; /* bit 30 for CSV3 */ + *ebx = sev_get_cbit_position() & 0x3f; /* EBX[5:0] */ + *ebx |= (sev_get_reduced_phys_bits() & 0x3f) << 6; /* EBX[11:6] */ + } +-- +1.8.3.1 + diff --git a/0222-target-i386-csv-Do-not-register-unregister-guest-sec.patch b/0222-target-i386-csv-Do-not-register-unregister-guest-sec.patch new file mode 100644 index 0000000000000000000000000000000000000000..73e6ec223356d029ac6a1dbe999713b578f859f7 --- /dev/null +++ b/0222-target-i386-csv-Do-not-register-unregister-guest-sec.patch @@ -0,0 +1,35 @@ +From e0b432bb2db670059702311dc48749f83ab5a9a9 Mon Sep 17 00:00:00 2001 +From: jiangxin +Date: Wed, 25 Aug 2021 12:36:00 +0800 +Subject: [PATCH 232/293] target/i386: csv: Do not register/unregister guest + secure memory for CSV3 guest + +CSV3's guest memory is allocated by firmware in secure processor +from dedicated memory reserved upon system boot up, consequently +it is not necessary to add notifier to pin/unpin memory. + +Signed-off-by: Xin Jiang +Signed-off-by: hanliyang +--- + target/i386/sev.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/target/i386/sev.c b/target/i386/sev.c +index b77572f..eb1026b 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -1217,7 +1217,10 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) + } + } + +- ram_block_notifier_add(&sev_ram_notifier); ++ /* CSV3 guest do not need notifier to reg/unreg memory */ ++ if (!csv3_enabled()) { ++ ram_block_notifier_add(&sev_ram_notifier); ++ } + qemu_add_machine_init_done_notifier(&sev_machine_done_notify); + qemu_add_vm_change_state_handler(sev_vm_state_change, sev); + migration_add_notifier(&sev_migration_state, sev_migration_state_notifier); +-- +1.8.3.1 + diff --git a/0223-target-i386-csv-Load-initial-image-to-private-memory.patch b/0223-target-i386-csv-Load-initial-image-to-private-memory.patch new file mode 100644 index 0000000000000000000000000000000000000000..de7466d303709f84a5b91a8317329b6c250d3ec4 --- /dev/null +++ b/0223-target-i386-csv-Load-initial-image-to-private-memory.patch @@ -0,0 +1,52 @@ +From 54026b362117c16811e0012b972bed0c25d48254 Mon Sep 17 00:00:00 2001 +From: jiangxin +Date: Wed, 25 Aug 2021 14:29:40 +0800 +Subject: [PATCH 233/293] target/i386: csv: Load initial image to private + memory for CSV3 guest + +The initial image of CSV3 guest should be loaded into private memory +before boot the guest. + +Add APIs to implement the image load. + +Signed-off-by: Xin Jiang +Signed-off-by: hanliyang +--- + hw/i386/pc_sysfw.c | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c +index c8d9e71..2bbcbb8 100644 +--- a/hw/i386/pc_sysfw.c ++++ b/hw/i386/pc_sysfw.c +@@ -37,6 +37,7 @@ + #include "hw/block/flash.h" + #include "sysemu/kvm.h" + #include "sev.h" ++#include "csv.h" + + #define FLASH_SECTOR_SIZE 4096 + +@@ -263,7 +264,18 @@ void x86_firmware_configure(void *ptr, int size) + error_report("failed to locate and/or save reset vector"); + exit(1); + } ++ if (csv3_enabled()) { ++ ram_addr_t offset = 0; ++ MemoryRegion *mr; + +- sev_encrypt_flash(ptr, size, &error_fatal); ++ mr = memory_region_from_host(ptr, &offset); ++ if (!mr) { ++ error_report("failed to get memory region of flash"); ++ exit(1); ++ } ++ csv3_load_data(mr->addr + offset, ptr, size, &error_fatal); ++ } else { ++ sev_encrypt_flash(ptr, size, &error_fatal); ++ } + } + } +-- +1.8.3.1 + diff --git a/0224-vga-Force-full-update-for-CSV3-guest.patch b/0224-vga-Force-full-update-for-CSV3-guest.patch new file mode 100644 index 0000000000000000000000000000000000000000..e671c11443c2435abb3a010e3a13f323a1da2847 --- /dev/null +++ b/0224-vga-Force-full-update-for-CSV3-guest.patch @@ -0,0 +1,128 @@ +From e2717a7e847de3884927aa50de562b1e611c521a Mon Sep 17 00:00:00 2001 +From: Xin Jiang +Date: Thu, 13 Jul 2023 09:35:10 +0800 +Subject: [PATCH 234/293] vga: Force full update for CSV3 guest + +As CSV3's NPT(nested page table) is managed by firmware, VMM is hard +to track the dirty pages of vga buffer. Although VMM could perform +a command to firmware to update read/write attribute of vga buffer +in NPT, it costs more time due to communication between VMM and +firmware. So the simplest method is to fully update vga buffer +always. + +Signed-off-by: Xin Jiang +Signed-off-by: hanliyang +--- + accel/kvm/kvm-all.c | 1 + + accel/stubs/kvm-stub.c | 1 + + hw/display/vga.c | 7 +++++++ + include/sysemu/kvm.h | 8 ++++++++ + target/i386/csv.c | 3 +++ + 5 files changed, 20 insertions(+) + +diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c +index e5ed69c..25d23bb 100644 +--- a/accel/kvm/kvm-all.c ++++ b/accel/kvm/kvm-all.c +@@ -98,6 +98,7 @@ bool kvm_allowed; + bool kvm_readonly_mem_allowed; + bool kvm_vm_attributes_allowed; + bool kvm_msi_use_devid; ++bool kvm_csv3_allowed; + bool kvm_has_guest_debug; + static int kvm_sstep_flags; + static bool kvm_immediate_exit; +diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c +index 1b37d9a..45b23f6 100644 +--- a/accel/stubs/kvm-stub.c ++++ b/accel/stubs/kvm-stub.c +@@ -24,6 +24,7 @@ bool kvm_gsi_direct_mapping; + bool kvm_allowed; + bool kvm_readonly_mem_allowed; + bool kvm_msi_use_devid; ++bool kvm_csv3_allowed; + + void kvm_flush_coalesced_mmio_buffer(void) + { +diff --git a/hw/display/vga.c b/hw/display/vga.c +index 37557c3..d70226a 100644 +--- a/hw/display/vga.c ++++ b/hw/display/vga.c +@@ -39,6 +39,8 @@ + #include "migration/vmstate.h" + #include "trace.h" + ++#include "sysemu/kvm.h" ++ + //#define DEBUG_VGA_MEM + //#define DEBUG_VGA_REG + +@@ -1783,6 +1785,11 @@ static void vga_update_display(void *opaque) + s->cursor_blink_time = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); + full_update = 1; + } ++ ++ /* Force to full update in CSV guest. */ ++ if (kvm_csv3_enabled()) ++ full_update = 1; ++ + switch(graphic_mode) { + case GMODE_TEXT: + vga_draw_text(s, full_update); +diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h +index d614878..1e15cfe 100644 +--- a/include/sysemu/kvm.h ++++ b/include/sysemu/kvm.h +@@ -42,6 +42,7 @@ extern bool kvm_gsi_routing_allowed; + extern bool kvm_gsi_direct_mapping; + extern bool kvm_readonly_mem_allowed; + extern bool kvm_msi_use_devid; ++extern bool kvm_csv3_allowed; + + #define kvm_enabled() (kvm_allowed) + /** +@@ -143,6 +144,12 @@ extern bool kvm_msi_use_devid; + */ + #define kvm_msi_devid_required() (kvm_msi_use_devid) + ++/** ++ * kvm_csv3_enabled: ++ * Returns: true if CSV3 feature is used for the VM. ++ */ ++#define kvm_csv3_enabled() (kvm_csv3_allowed) ++ + #else + + #define kvm_enabled() (0) +@@ -157,6 +164,7 @@ extern bool kvm_msi_use_devid; + #define kvm_gsi_direct_mapping() (false) + #define kvm_readonly_mem_enabled() (false) + #define kvm_msi_devid_required() (false) ++#define kvm_csv3_enabled() (false) + + #endif /* CONFIG_KVM_IS_POSSIBLE */ + +diff --git a/target/i386/csv.c b/target/i386/csv.c +index f423b89..70900be 100644 +--- a/target/i386/csv.c ++++ b/target/i386/csv.c +@@ -14,6 +14,7 @@ + #include "qemu/osdep.h" + #include "qemu/error-report.h" + #include "qapi/error.h" ++#include "sysemu/kvm.h" + + #include + +@@ -60,6 +61,8 @@ csv3_init(uint32_t policy, int fd, void *state, struct sev_ops *ops) + return -1; + } + ++ kvm_csv3_allowed = true; ++ + csv3_guest.sev_fd = fd; + csv3_guest.state = state; + csv3_guest.sev_ioctl = ops->sev_ioctl; +-- +1.8.3.1 + diff --git a/0225-vfio-Only-map-shared-region-for-CSV3-virtual-machine.patch b/0225-vfio-Only-map-shared-region-for-CSV3-virtual-machine.patch new file mode 100644 index 0000000000000000000000000000000000000000..303aba0b626bb2fbcf1da164f9f09acd76bc912a --- /dev/null +++ b/0225-vfio-Only-map-shared-region-for-CSV3-virtual-machine.patch @@ -0,0 +1,397 @@ +From 201ac3f953d0d30fba075fa74deca9030b242016 Mon Sep 17 00:00:00 2001 +From: liuyafei +Date: Mon, 22 May 2023 20:37:40 +0800 +Subject: [PATCH 235/293] vfio: Only map shared region for CSV3 virtual machine + +qemu vfio listener map/unmap all of the virtual machine's memory. +It does not work for CSV3 virtual machine, as only shared memory +should be accessed by device. + +Signed-off-by: liuyafei +Signed-off-by: hanliyang +--- + hw/vfio/container.c | 46 ++++++++++++++- + include/exec/memory.h | 11 ++++ + system/memory.c | 18 ++++++ + target/i386/csv-sysemu-stub.c | 10 ++++ + target/i386/csv.c | 134 ++++++++++++++++++++++++++++++++++++++++++ + target/i386/csv.h | 12 ++++ + target/i386/kvm/kvm.c | 2 + + 7 files changed, 230 insertions(+), 3 deletions(-) + +diff --git a/hw/vfio/container.c b/hw/vfio/container.c +index adc3005..0d6e6b4 100644 +--- a/hw/vfio/container.c ++++ b/hw/vfio/container.c +@@ -30,6 +30,7 @@ + #include "qemu/error-report.h" + #include "qemu/range.h" + #include "sysemu/reset.h" ++#include "sysemu/kvm.h" + #include "trace.h" + #include "qapi/error.h" + #include "migration/migration.h" +@@ -468,6 +469,32 @@ static void vfio_free_container(VFIOContainer *container) + g_free(container); + } + ++static SharedRegionListener *g_shl; ++ ++static void shared_memory_listener_register(MemoryListener *listener, ++ AddressSpace *as) ++{ ++ SharedRegionListener *shl; ++ ++ shl = g_new0(SharedRegionListener, 1); ++ ++ shl->listener = listener; ++ shl->as = as; ++ ++ shared_region_register_listener(shl); ++ g_shl = shl; ++} ++ ++static void shared_memory_listener_unregister(void) ++{ ++ SharedRegionListener *shl = g_shl; ++ ++ shared_region_unregister_listener(shl); ++ ++ g_free(shl); ++ g_shl = NULL; ++} ++ + static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, + Error **errp) + { +@@ -613,7 +640,12 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, + + container->listener = vfio_memory_listener; + +- memory_listener_register(&container->listener, container->space->as); ++ if (kvm_csv3_enabled()) { ++ shared_memory_listener_register(&container->listener, ++ container->space->as); ++ } else { ++ memory_listener_register(&container->listener, container->space->as); ++ } + + if (container->error) { + ret = -1; +@@ -629,7 +661,11 @@ listener_release_exit: + QLIST_REMOVE(group, container_next); + QLIST_REMOVE(container, next); + vfio_kvm_device_del_group(group); +- memory_listener_unregister(&container->listener); ++ if (kvm_csv3_enabled()) { ++ shared_memory_listener_unregister(); ++ } else { ++ memory_listener_unregister(&container->listener); ++ } + if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU || + container->iommu_type == VFIO_SPAPR_TCE_IOMMU) { + vfio_spapr_container_deinit(container); +@@ -663,7 +699,11 @@ static void vfio_disconnect_container(VFIOGroup *group) + * group. + */ + if (QLIST_EMPTY(&container->group_list)) { +- memory_listener_unregister(&container->listener); ++ if (kvm_csv3_enabled()) { ++ shared_memory_listener_unregister(); ++ } else { ++ memory_listener_unregister(&container->listener); ++ } + if (container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU || + container->iommu_type == VFIO_SPAPR_TCE_IOMMU) { + vfio_spapr_container_deinit(container); +diff --git a/include/exec/memory.h b/include/exec/memory.h +index 831f7c9..3e65d8d 100644 +--- a/include/exec/memory.h ++++ b/include/exec/memory.h +@@ -775,6 +775,17 @@ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, + ram_addr_t *ram_addr, bool *read_only, + bool *mr_has_discard_manager); + ++typedef struct SharedRegionListener SharedRegionListener; ++struct SharedRegionListener { ++ MemoryListener *listener; ++ AddressSpace *as; ++ QTAILQ_ENTRY(SharedRegionListener) next; ++}; ++ ++void shared_region_register_listener(SharedRegionListener *shl); ++void shared_region_unregister_listener(SharedRegionListener *shl); ++void *shared_region_listeners_get(void); ++ + typedef struct CoalescedMemoryRange CoalescedMemoryRange; + typedef struct MemoryRegionIoeventfd MemoryRegionIoeventfd; + +diff --git a/system/memory.c b/system/memory.c +index 798b6c0..2ffb878 100644 +--- a/system/memory.c ++++ b/system/memory.c +@@ -48,6 +48,9 @@ static QTAILQ_HEAD(, MemoryListener) memory_listeners + static QTAILQ_HEAD(, AddressSpace) address_spaces + = QTAILQ_HEAD_INITIALIZER(address_spaces); + ++static QTAILQ_HEAD(, SharedRegionListener) shared_region_listeners ++ = QTAILQ_HEAD_INITIALIZER(shared_region_listeners); ++ + static GHashTable *flat_views; + + typedef struct AddrRange AddrRange; +@@ -2226,6 +2229,21 @@ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, + return true; + } + ++void shared_region_register_listener(SharedRegionListener *shl) ++{ ++ QTAILQ_INSERT_TAIL(&shared_region_listeners, shl, next); ++} ++ ++void shared_region_unregister_listener(SharedRegionListener *shl) ++{ ++ QTAILQ_REMOVE(&shared_region_listeners, shl, next); ++} ++ ++void *shared_region_listeners_get(void) ++{ ++ return &shared_region_listeners; ++} ++ + void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client) + { + uint8_t mask = 1 << client; +diff --git a/target/i386/csv-sysemu-stub.c b/target/i386/csv-sysemu-stub.c +index 23d885f..db22c29 100644 +--- a/target/i386/csv-sysemu-stub.c ++++ b/target/i386/csv-sysemu-stub.c +@@ -29,3 +29,13 @@ int csv3_launch_encrypt_vmcb(void) + { + g_assert_not_reached(); + } ++ ++int csv3_shared_region_dma_map(uint64_t start, uint64_t end) ++{ ++ return 0; ++} ++ ++void csv3_shared_region_dma_unmap(uint64_t start, uint64_t end) ++{ ++ ++} +diff --git a/target/i386/csv.c b/target/i386/csv.c +index 70900be..5823c89 100644 +--- a/target/i386/csv.c ++++ b/target/i386/csv.c +@@ -15,6 +15,7 @@ + #include "qemu/error-report.h" + #include "qapi/error.h" + #include "sysemu/kvm.h" ++#include "exec/address-spaces.h" + + #include + +@@ -67,6 +68,8 @@ csv3_init(uint32_t policy, int fd, void *state, struct sev_ops *ops) + csv3_guest.state = state; + csv3_guest.sev_ioctl = ops->sev_ioctl; + csv3_guest.fw_error_to_str = ops->fw_error_to_str; ++ QTAILQ_INIT(&csv3_guest.dma_map_regions_list); ++ qemu_mutex_init(&csv3_guest.dma_map_regions_list_mutex); + } + return 0; + } +@@ -167,3 +170,134 @@ csv3_launch_encrypt_vmcb(void) + err: + return ret; + } ++ ++int csv3_shared_region_dma_map(uint64_t start, uint64_t end) ++{ ++ MemoryRegionSection section; ++ AddressSpace *as; ++ QTAILQ_HEAD(, SharedRegionListener) *shared_region_listeners; ++ SharedRegionListener *shl; ++ MemoryListener *listener; ++ uint64_t size; ++ Csv3GuestState *s = &csv3_guest; ++ struct dma_map_region *region, *pos; ++ int ret = 0; ++ ++ if (!csv3_enabled()) ++ return 0; ++ ++ if (end <= start) ++ return 0; ++ ++ shared_region_listeners = shared_region_listeners_get(); ++ if (QTAILQ_EMPTY(shared_region_listeners)) ++ return 0; ++ ++ size = end - start; ++ ++ qemu_mutex_lock(&s->dma_map_regions_list_mutex); ++ QTAILQ_FOREACH(pos, &s->dma_map_regions_list, list) { ++ if (start >= (pos->start + pos->size)) { ++ continue; ++ } else if ((start + size) <= pos->start) { ++ break; ++ } else { ++ goto end; ++ } ++ } ++ QTAILQ_FOREACH(shl, shared_region_listeners, next) { ++ listener = shl->listener; ++ as = shl->as; ++ section = memory_region_find(as->root, start, size); ++ if (!section.mr) { ++ goto end; ++ } ++ ++ if (!memory_region_is_ram(section.mr)) { ++ memory_region_unref(section.mr); ++ goto end; ++ } ++ ++ if (listener->region_add) { ++ listener->region_add(listener, §ion); ++ } ++ memory_region_unref(section.mr); ++ } ++ ++ region = g_malloc0(sizeof(*region)); ++ if (!region) { ++ ret = -1; ++ goto end; ++ } ++ region->start = start; ++ region->size = size; ++ ++ if (pos) { ++ QTAILQ_INSERT_BEFORE(pos, region, list); ++ } else { ++ QTAILQ_INSERT_TAIL(&s->dma_map_regions_list, region, list); ++ } ++ ++end: ++ qemu_mutex_unlock(&s->dma_map_regions_list_mutex); ++ return ret; ++} ++ ++void csv3_shared_region_dma_unmap(uint64_t start, uint64_t end) ++{ ++ MemoryRegionSection section; ++ AddressSpace *as; ++ QTAILQ_HEAD(, SharedRegionListener) *shared_region_listeners; ++ SharedRegionListener *shl; ++ MemoryListener *listener; ++ uint64_t size; ++ Csv3GuestState *s = &csv3_guest; ++ struct dma_map_region *pos, *next_pos; ++ ++ if (!csv3_enabled()) ++ return; ++ ++ if (end <= start) ++ return; ++ ++ shared_region_listeners = shared_region_listeners_get(); ++ if (QTAILQ_EMPTY(shared_region_listeners)) ++ return; ++ ++ size = end - start; ++ ++ qemu_mutex_lock(&s->dma_map_regions_list_mutex); ++ QTAILQ_FOREACH_SAFE(pos, &s->dma_map_regions_list, list, next_pos) { ++ uint64_t l, r; ++ uint64_t curr_end = pos->start + pos->size; ++ ++ l = MAX(start, pos->start); ++ r = MIN(start + size, pos->start + pos->size); ++ if (l < r) { ++ if ((start <= pos->start) && (start + size >= pos->start + pos->size)) { ++ QTAILQ_FOREACH(shl, shared_region_listeners, next) { ++ listener = shl->listener; ++ as = shl->as; ++ section = memory_region_find(as->root, pos->start, pos->size); ++ if (!section.mr) { ++ goto end; ++ } ++ if (listener->region_del) { ++ listener->region_del(listener, §ion); ++ } ++ memory_region_unref(section.mr); ++ } ++ ++ QTAILQ_REMOVE(&s->dma_map_regions_list, pos, list); ++ g_free(pos); ++ } ++ break; ++ } ++ if ((start + size) <= curr_end) { ++ break; ++ } ++ } ++end: ++ qemu_mutex_unlock(&s->dma_map_regions_list_mutex); ++ return; ++} +diff --git a/target/i386/csv.h b/target/i386/csv.h +index 6444d54..0c402ce 100644 +--- a/target/i386/csv.h ++++ b/target/i386/csv.h +@@ -15,6 +15,8 @@ + #define I386_CSV_H + + #include "qapi/qapi-commands-misc-target.h" ++#include "qemu/thread.h" ++#include "qemu/queue.h" + #include "sev.h" + + #ifdef CONFIG_CSV +@@ -73,12 +75,19 @@ int csv_load_incoming_cpu_state(QEMUFile *f); + /* CSV3 */ + #define GUEST_POLICY_CSV3_BIT (1 << 6) + ++struct dma_map_region { ++ uint64_t start, size; ++ QTAILQ_ENTRY(dma_map_region) list; ++}; ++ + struct Csv3GuestState { + uint32_t policy; + int sev_fd; + void *state; + int (*sev_ioctl)(int fd, int cmd, void *data, int *error); + const char *(*fw_error_to_str)(int code); ++ QTAILQ_HEAD(, dma_map_region) dma_map_regions_list; ++ QemuMutex dma_map_regions_list_mutex; + }; + + typedef struct Csv3GuestState Csv3GuestState; +@@ -89,4 +98,7 @@ extern int csv3_launch_encrypt_vmcb(void); + + int csv3_load_data(uint64_t gpa, uint8_t *ptr, uint64_t len, Error **errp); + ++int csv3_shared_region_dma_map(uint64_t start, uint64_t end); ++void csv3_shared_region_dma_unmap(uint64_t start, uint64_t end); ++ + #endif +diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c +index 2866a6d..925f4f8 100644 +--- a/target/i386/kvm/kvm.c ++++ b/target/i386/kvm/kvm.c +@@ -5026,8 +5026,10 @@ static int kvm_handle_exit_hypercall(X86CPU *cpu, struct kvm_run *run) + + if (enc) { + sev_remove_shared_regions_list(gfn_start, gfn_end); ++ csv3_shared_region_dma_unmap(gpa, gfn_end << TARGET_PAGE_BITS); + } else { + sev_add_shared_regions_list(gfn_start, gfn_end); ++ csv3_shared_region_dma_map(gpa, gfn_end << TARGET_PAGE_BITS); + } + } + return 0; +-- +1.8.3.1 + diff --git a/0226-linux-headers-update-kernel-headers-to-include-CSV3-.patch b/0226-linux-headers-update-kernel-headers-to-include-CSV3-.patch new file mode 100644 index 0000000000000000000000000000000000000000..5353a8637c068baac44a8034fe2d9e3d52df68d1 --- /dev/null +++ b/0226-linux-headers-update-kernel-headers-to-include-CSV3-.patch @@ -0,0 +1,79 @@ +From dc0446c0bfdd396b36e32a087d662e6923a54d8e Mon Sep 17 00:00:00 2001 +From: jiangxin +Date: Fri, 17 Jun 2022 09:25:19 +0800 +Subject: [PATCH 236/293] linux-headers: update kernel headers to include CSV3 + migration cmds + +Four new migration commands are added to support CSV3 migration. + +KVM_CSV3_SEND_ENCRYPT_DATA/KVM_CSV3_RECEIVE_ENCRYPT_DATA cmds are +used to migrate guest's pages. + +KVM_CSV3_SEND_ENCRYPT_CONTEXT/KVM_CSV3_RECEIVE_ENCRYPT_CONTEXT cmds +are used to migration guest's runtime context. + +Signed-off-by: Xin Jiang +Signed-off-by: hanliyang +--- + linux-headers/linux/kvm.h | 38 ++++++++++++++++++++++++++++++++++++++ + 1 file changed, 38 insertions(+) + +diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h +index 061b2e7..6edf0b3 100644 +--- a/linux-headers/linux/kvm.h ++++ b/linux-headers/linux/kvm.h +@@ -2067,6 +2067,12 @@ enum csv3_cmd_id { + KVM_CSV3_INIT = KVM_CSV3_NR_MIN, + KVM_CSV3_LAUNCH_ENCRYPT_DATA, + KVM_CSV3_LAUNCH_ENCRYPT_VMCB, ++ KVM_CSV3_SEND_ENCRYPT_DATA, ++ KVM_CSV3_SEND_ENCRYPT_CONTEXT, ++ KVM_CSV3_RECEIVE_ENCRYPT_DATA, ++ KVM_CSV3_RECEIVE_ENCRYPT_CONTEXT, ++ ++ KVM_CSV3_NR_MAX, + }; + + struct kvm_csv3_launch_encrypt_data { +@@ -2079,6 +2085,38 @@ struct kvm_csv3_init_data { + __u64 nodemask; + }; + ++struct kvm_csv3_send_encrypt_data { ++ __u64 hdr_uaddr; ++ __u32 hdr_len; ++ __u64 guest_addr_data; ++ __u32 guest_addr_len; ++ __u64 trans_uaddr; ++ __u32 trans_len; ++}; ++ ++struct kvm_csv3_send_encrypt_context { ++ __u64 hdr_uaddr; ++ __u32 hdr_len; ++ __u64 trans_uaddr; ++ __u32 trans_len; ++}; ++ ++struct kvm_csv3_receive_encrypt_data { ++ __u64 hdr_uaddr; ++ __u32 hdr_len; ++ __u64 guest_addr_data; ++ __u32 guest_addr_len; ++ __u64 trans_uaddr; ++ __u32 trans_len; ++}; ++ ++struct kvm_csv3_receive_encrypt_context { ++ __u64 hdr_uaddr; ++ __u32 hdr_len; ++ __u64 trans_uaddr; ++ __u32 trans_len; ++}; ++ + #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) + #define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1) + #define KVM_DEV_ASSIGN_MASK_INTX (1 << 2) +-- +1.8.3.1 + diff --git a/0227-target-i386-csv-Add-support-to-migrate-the-outgoing-.patch b/0227-target-i386-csv-Add-support-to-migrate-the-outgoing-.patch new file mode 100644 index 0000000000000000000000000000000000000000..6fc9638e5ed11195a1ba827f60c72ec685c10241 --- /dev/null +++ b/0227-target-i386-csv-Add-support-to-migrate-the-outgoing-.patch @@ -0,0 +1,454 @@ +From 70bfd06354b84594921edafc787cc8b56e38d32d Mon Sep 17 00:00:00 2001 +From: jiangxin +Date: Fri, 17 Jun 2022 09:37:56 +0800 +Subject: [PATCH 237/293] target/i386: csv: Add support to migrate the outgoing + page for CSV3 guest + +The csv3_send_encrypt_data() provides the method to encrypt the +guest's private pages during migration. The routine is similar to +CSV2's. Usually, it starts with a SEND_START command to create the +migration context. Then SEND_ENCRYPT_DATA command is performed to +encrypt guest pages. After migration is completed, a SEND_FINISH +command is performed to the firmware. + +Signed-off-by: Jiang Xin +Signed-off-by: hanliyang +--- + migration/ram.c | 87 ++++++++++++++++++++++ + target/i386/csv.c | 184 +++++++++++++++++++++++++++++++++++++++++++++++ + target/i386/csv.h | 22 ++++++ + target/i386/sev.c | 14 +++- + target/i386/sev.h | 1 + + target/i386/trace-events | 1 + + 6 files changed, 308 insertions(+), 1 deletion(-) + +diff --git a/migration/ram.c b/migration/ram.c +index 198b060..71353bc 100644 +--- a/migration/ram.c ++++ b/migration/ram.c +@@ -2479,6 +2479,90 @@ ram_save_encrypted_pages_in_batch(RAMState *rs, PageSearchStatus *pss) + #endif + + /** ++ * ram_save_csv3_pages - send the given csv3 VM pages to the stream ++ */ ++static int ram_save_csv3_pages(RAMState *rs, PageSearchStatus *pss) ++{ ++ bool page_dirty; ++ int ret; ++ int tmppages, pages = 0; ++ uint8_t *p; ++ uint32_t host_len = 0; ++ uint64_t bytes_xmit = 0; ++ RAMBlock *block = pss->block; ++ ram_addr_t offset = 0; ++ hwaddr paddr = RAM_ADDR_INVALID; ++ MachineState *ms = MACHINE(qdev_get_machine()); ++ ConfidentialGuestSupportClass *cgs_class = ++ (ConfidentialGuestSupportClass *) object_get_class(OBJECT(ms->cgs)); ++ struct ConfidentialGuestMemoryEncryptionOps *ops = ++ cgs_class->memory_encryption_ops; ++ ++ if (!kvm_csv3_enabled()) ++ return 0; ++ ++ do { ++ page_dirty = migration_bitmap_clear_dirty(rs, block, pss->page); ++ ++ /* Check the pages is dirty and if it is send it */ ++ if (page_dirty) { ++ ret = kvm_physical_memory_addr_from_host(kvm_state, ++ block->host + (pss->page << TARGET_PAGE_BITS), &paddr); ++ /* Process ROM or MMIO */ ++ if (paddr == RAM_ADDR_INVALID || ++ memory_region_is_rom(block->mr)) { ++ tmppages = migration_ops->ram_save_target_page(rs, pss); ++ } else { ++ /* Caculate the offset and host virtual address of the page */ ++ offset = pss->page << TARGET_PAGE_BITS; ++ p = block->host + offset; ++ ++ if (ops->queue_outgoing_page(p, TARGET_PAGE_SIZE, offset)) ++ return -1; ++ ++ tmppages = 1; ++ host_len += TARGET_PAGE_SIZE; ++ ++ stat64_add(&mig_stats.normal_pages, 1); ++ } ++ } else { ++ tmppages = 0; ++ } ++ ++ if (tmppages >= 0) { ++ pages += tmppages; ++ } else { ++ return tmppages; ++ } ++ ++ pss_find_next_dirty(pss); ++ } while (offset_in_ramblock(block, ++ ((ram_addr_t)pss->page) << TARGET_PAGE_BITS) && ++ host_len < CSV3_OUTGOING_PAGE_WINDOW_SIZE); ++ ++ /* Check if there are any queued pages */ ++ if (host_len != 0) { ++ /* Always set offset as 0 for csv3. */ ++ ram_transferred_add(save_page_header(pss, pss->pss_channel, ++ block, 0 | RAM_SAVE_FLAG_ENCRYPTED_DATA)); ++ ++ qemu_put_be32(pss->pss_channel, RAM_SAVE_ENCRYPTED_PAGE); ++ ram_transferred_add(4); ++ /* Process the queued pages in batch */ ++ ret = ops->save_queued_outgoing_pages(pss->pss_channel, &bytes_xmit); ++ if (ret) { ++ return -1; ++ } ++ ram_transferred_add(bytes_xmit); ++ } ++ ++ /* The offset we leave with is the last one we looked at */ ++ pss->page--; ++ ++ return pages; ++} ++ ++/** + * ram_save_host_page: save a whole host page + * + * Starting at *offset send pages up to the end of the current host +@@ -2513,6 +2597,9 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss) + return 0; + } + ++ if (kvm_csv3_enabled()) ++ return ram_save_csv3_pages(rs, pss); ++ + #ifdef CONFIG_HYGON_CSV_MIG_ACCEL + /* + * If command_batch function is enabled and memory encryption is enabled +diff --git a/target/i386/csv.c b/target/i386/csv.c +index 5823c89..ffa5a73 100644 +--- a/target/i386/csv.c ++++ b/target/i386/csv.c +@@ -16,8 +16,13 @@ + #include "qapi/error.h" + #include "sysemu/kvm.h" + #include "exec/address-spaces.h" ++#include "migration/blocker.h" ++#include "migration/qemu-file.h" ++#include "migration/misc.h" ++#include "monitor/monitor.h" + + #include ++#include + + #ifdef CONFIG_NUMA + #include +@@ -30,6 +35,19 @@ + + bool csv_kvm_cpu_reset_inhibit; + ++struct ConfidentialGuestMemoryEncryptionOps csv3_memory_encryption_ops = { ++ .save_setup = sev_save_setup, ++ .save_outgoing_page = NULL, ++ .is_gfn_in_unshared_region = NULL, ++ .save_outgoing_shared_regions_list = sev_save_outgoing_shared_regions_list, ++ .load_incoming_shared_regions_list = sev_load_incoming_shared_regions_list, ++ .queue_outgoing_page = csv3_queue_outgoing_page, ++ .save_queued_outgoing_pages = csv3_save_queued_outgoing_pages, ++}; ++ ++#define CSV3_OUTGOING_PAGE_NUM \ ++ (CSV3_OUTGOING_PAGE_WINDOW_SIZE / TARGET_PAGE_SIZE) ++ + Csv3GuestState csv3_guest = { 0 }; + + int +@@ -70,6 +88,7 @@ csv3_init(uint32_t policy, int fd, void *state, struct sev_ops *ops) + csv3_guest.fw_error_to_str = ops->fw_error_to_str; + QTAILQ_INIT(&csv3_guest.dma_map_regions_list); + qemu_mutex_init(&csv3_guest.dma_map_regions_list_mutex); ++ csv3_guest.sev_send_start = ops->sev_send_start; + } + return 0; + } +@@ -301,3 +320,168 @@ end: + qemu_mutex_unlock(&s->dma_map_regions_list_mutex); + return; + } ++ ++static inline hwaddr csv3_hva_to_gfn(uint8_t *ptr) ++{ ++ ram_addr_t offset = RAM_ADDR_INVALID; ++ ++ kvm_physical_memory_addr_from_host(kvm_state, ptr, &offset); ++ ++ return offset >> TARGET_PAGE_BITS; ++} ++ ++static int ++csv3_send_start(QEMUFile *f, uint64_t *bytes_sent) ++{ ++ if (csv3_guest.sev_send_start) ++ return csv3_guest.sev_send_start(f, bytes_sent); ++ else ++ return -1; ++} ++ ++static int ++csv3_send_get_packet_len(int *fw_err) ++{ ++ int ret; ++ struct kvm_csv3_send_encrypt_data update = {0}; ++ ++ update.hdr_len = 0; ++ update.trans_len = 0; ++ ret = csv3_ioctl(KVM_CSV3_SEND_ENCRYPT_DATA, &update, fw_err); ++ if (*fw_err != SEV_RET_INVALID_LEN) { ++ error_report("%s: failed to get session length ret=%d fw_error=%d '%s'", ++ __func__, ret, *fw_err, fw_error_to_str(*fw_err)); ++ ret = 0; ++ goto err; ++ } ++ ++ if (update.hdr_len <= INT_MAX) ++ ret = update.hdr_len; ++ else ++ ret = 0; ++ ++err: ++ return ret; ++} ++ ++static int ++csv3_send_encrypt_data(Csv3GuestState *s, QEMUFile *f, ++ uint8_t *ptr, uint32_t size, uint64_t *bytes_sent) ++{ ++ int ret, fw_error = 0; ++ guchar *trans; ++ uint32_t guest_addr_entry_num; ++ uint32_t i; ++ struct kvm_csv3_send_encrypt_data update = { }; ++ ++ /* ++ * If this is first call then query the packet header bytes and allocate ++ * the packet buffer. ++ */ ++ if (!s->send_packet_hdr) { ++ s->send_packet_hdr_len = csv3_send_get_packet_len(&fw_error); ++ if (s->send_packet_hdr_len < 1) { ++ error_report("%s: SEND_UPDATE fw_error=%d '%s'", ++ __func__, fw_error, fw_error_to_str(fw_error)); ++ return 1; ++ } ++ ++ s->send_packet_hdr = g_new(gchar, s->send_packet_hdr_len); ++ } ++ ++ if (!s->guest_addr_len || !s->guest_addr_data) { ++ error_report("%s: invalid host address or size", __func__); ++ return 1; ++ } else { ++ guest_addr_entry_num = s->guest_addr_len / sizeof(struct guest_addr_entry); ++ } ++ ++ /* allocate transport buffer */ ++ trans = g_new(guchar, guest_addr_entry_num * TARGET_PAGE_SIZE); ++ ++ update.hdr_uaddr = (uintptr_t)s->send_packet_hdr; ++ update.hdr_len = s->send_packet_hdr_len; ++ update.guest_addr_data = (uintptr_t)s->guest_addr_data; ++ update.guest_addr_len = s->guest_addr_len; ++ update.trans_uaddr = (uintptr_t)trans; ++ update.trans_len = guest_addr_entry_num * TARGET_PAGE_SIZE; ++ ++ trace_kvm_csv3_send_encrypt_data(trans, update.trans_len); ++ ++ ret = csv3_ioctl(KVM_CSV3_SEND_ENCRYPT_DATA, &update, &fw_error); ++ if (ret) { ++ error_report("%s: SEND_ENCRYPT_DATA ret=%d fw_error=%d '%s'", ++ __func__, ret, fw_error, fw_error_to_str(fw_error)); ++ goto err; ++ } ++ ++ for (i = 0; i < guest_addr_entry_num; i++) { ++ if (s->guest_addr_data[i].share) ++ memcpy(trans + i * TARGET_PAGE_SIZE, (guchar *)s->guest_hva_data[i].hva, ++ TARGET_PAGE_SIZE); ++ } ++ ++ qemu_put_be32(f, update.hdr_len); ++ qemu_put_buffer(f, (uint8_t *)update.hdr_uaddr, update.hdr_len); ++ *bytes_sent += 4 + update.hdr_len; ++ ++ qemu_put_be32(f, update.guest_addr_len); ++ qemu_put_buffer(f, (uint8_t *)update.guest_addr_data, update.guest_addr_len); ++ *bytes_sent += 4 + update.guest_addr_len; ++ ++ qemu_put_be32(f, update.trans_len); ++ qemu_put_buffer(f, (uint8_t *)update.trans_uaddr, update.trans_len); ++ *bytes_sent += (4 + update.trans_len); ++ ++err: ++ s->guest_addr_len = 0; ++ g_free(trans); ++ return ret; ++} ++ ++int ++csv3_queue_outgoing_page(uint8_t *ptr, uint32_t sz, uint64_t addr) ++{ ++ Csv3GuestState *s = &csv3_guest; ++ uint32_t i = 0; ++ ++ (void) addr; ++ ++ if (!s->guest_addr_data) { ++ s->guest_hva_data = g_new0(struct guest_hva_entry, CSV3_OUTGOING_PAGE_NUM); ++ s->guest_addr_data = g_new0(struct guest_addr_entry, CSV3_OUTGOING_PAGE_NUM); ++ s->guest_addr_len = 0; ++ } ++ ++ if (s->guest_addr_len >= sizeof(struct guest_addr_entry) * CSV3_OUTGOING_PAGE_NUM) { ++ error_report("Failed to queue outgoing page"); ++ return 1; ++ } ++ ++ i = s->guest_addr_len / sizeof(struct guest_addr_entry); ++ s->guest_hva_data[i].hva = (uintptr_t)ptr; ++ s->guest_addr_data[i].share = 0; ++ s->guest_addr_data[i].reserved = 0; ++ s->guest_addr_data[i].gfn = csv3_hva_to_gfn(ptr); ++ s->guest_addr_len += sizeof(struct guest_addr_entry); ++ ++ return 0; ++} ++ ++int ++csv3_save_queued_outgoing_pages(QEMUFile *f, uint64_t *bytes_sent) ++{ ++ Csv3GuestState *s = &csv3_guest; ++ ++ /* ++ * If this is a first buffer then create outgoing encryption context ++ * and write our PDH, policy and session data. ++ */ ++ if (!csv3_check_state(SEV_STATE_SEND_UPDATE) && ++ csv3_send_start(f, bytes_sent)) { ++ error_report("Failed to create outgoing context"); ++ return 1; ++ } ++ ++ return csv3_send_encrypt_data(s, f, NULL, 0, bytes_sent); ++} +diff --git a/target/i386/csv.h b/target/i386/csv.h +index 0c402ce..e808bea 100644 +--- a/target/i386/csv.h ++++ b/target/i386/csv.h +@@ -80,6 +80,18 @@ struct dma_map_region { + QTAILQ_ENTRY(dma_map_region) list; + }; + ++#define CSV3_OUTGOING_PAGE_WINDOW_SIZE (512 * TARGET_PAGE_SIZE) ++ ++struct guest_addr_entry { ++ uint64_t share: 1; ++ uint64_t reserved: 11; ++ uint64_t gfn: 52; ++}; ++ ++struct guest_hva_entry { ++ uint64_t hva; ++}; ++ + struct Csv3GuestState { + uint32_t policy; + int sev_fd; +@@ -88,11 +100,19 @@ struct Csv3GuestState { + const char *(*fw_error_to_str)(int code); + QTAILQ_HEAD(, dma_map_region) dma_map_regions_list; + QemuMutex dma_map_regions_list_mutex; ++ gchar *send_packet_hdr; ++ size_t send_packet_hdr_len; ++ struct guest_hva_entry *guest_hva_data; ++ struct guest_addr_entry *guest_addr_data; ++ size_t guest_addr_len; ++ ++ int (*sev_send_start)(QEMUFile *f, uint64_t *bytes_sent); + }; + + typedef struct Csv3GuestState Csv3GuestState; + + extern struct Csv3GuestState csv3_guest; ++extern struct ConfidentialGuestMemoryEncryptionOps csv3_memory_encryption_ops; + extern int csv3_init(uint32_t policy, int fd, void *state, struct sev_ops *ops); + extern int csv3_launch_encrypt_vmcb(void); + +@@ -100,5 +120,7 @@ int csv3_load_data(uint64_t gpa, uint8_t *ptr, uint64_t len, Error **errp); + + int csv3_shared_region_dma_map(uint64_t start, uint64_t end); + void csv3_shared_region_dma_unmap(uint64_t start, uint64_t end); ++int csv3_queue_outgoing_page(uint8_t *ptr, uint32_t sz, uint64_t addr); ++int csv3_save_queued_outgoing_pages(QEMUFile *f, uint64_t *bytes_sent); + + #endif +diff --git a/target/i386/sev.c b/target/i386/sev.c +index eb1026b..465b62c 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -1225,7 +1225,11 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) + qemu_add_vm_change_state_handler(sev_vm_state_change, sev); + migration_add_notifier(&sev_migration_state, sev_migration_state_notifier); + +- cgs_class->memory_encryption_ops = &sev_memory_encryption_ops; ++ if (csv3_enabled()) { ++ cgs_class->memory_encryption_ops = &csv3_memory_encryption_ops; ++ } else { ++ cgs_class->memory_encryption_ops = &sev_memory_encryption_ops; ++ } + QTAILQ_INIT(&sev->shared_regions_list); + + /* Determine whether support MSR_AMD64_SEV_ES_GHCB */ +@@ -2608,9 +2612,17 @@ bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) + return ret; + } + ++static int _sev_send_start(QEMUFile *f, uint64_t *bytes_sent) ++{ ++ SevGuestState *s = sev_guest; ++ ++ return sev_send_start(s, f, bytes_sent); ++} ++ + struct sev_ops sev_ops = { + .sev_ioctl = sev_ioctl, + .fw_error_to_str = fw_error_to_str, ++ .sev_send_start = _sev_send_start, + }; + + static void +diff --git a/target/i386/sev.h b/target/i386/sev.h +index e91431e..8ccef22 100644 +--- a/target/i386/sev.h ++++ b/target/i386/sev.h +@@ -83,6 +83,7 @@ extern bool sev_kvm_has_msr_ghcb; + struct sev_ops { + int (*sev_ioctl)(int fd, int cmd, void *data, int *error); + const char *(*fw_error_to_str)(int code); ++ int (*sev_send_start)(QEMUFile *f, uint64_t *bytes_sent); + }; + + extern struct sev_ops sev_ops; +diff --git a/target/i386/trace-events b/target/i386/trace-events +index e07061b..6ebb644 100644 +--- a/target/i386/trace-events ++++ b/target/i386/trace-events +@@ -22,3 +22,4 @@ kvm_sev_receive_update_vmsa(uint32_t cpu_id, uint32_t cpu_index, void *src, int + + # csv.c + kvm_csv3_launch_encrypt_data(uint64_t gpa, void *addr, uint64_t len) "gpa 0x%" PRIx64 "addr %p len 0x%" PRIu64 ++kvm_csv3_send_encrypt_data(void *dst, int len) "trans %p len %d" +-- +1.8.3.1 + diff --git a/0228-target-i386-csv-Add-support-to-migrate-the-incoming-.patch b/0228-target-i386-csv-Add-support-to-migrate-the-incoming-.patch new file mode 100644 index 0000000000000000000000000000000000000000..b46b9e719856fcfeb998a115b245f60b49864703 --- /dev/null +++ b/0228-target-i386-csv-Add-support-to-migrate-the-incoming-.patch @@ -0,0 +1,205 @@ +From 3079e7490ee07f78222de62f16e1ba350cf2b0b4 Mon Sep 17 00:00:00 2001 +From: jiangxin +Date: Fri, 17 Jun 2022 09:45:45 +0800 +Subject: [PATCH 238/293] target/i386: csv: Add support to migrate the incoming + page for CSV3 guest + +The csv3_receive_encrypt_data() provides the method to read incoming +guest private pages from socket and load them into guest memory. +The routine is similar to CSV2's. Usually, it starts with a RECEIVE +START command to create the migration context. Then RECEIVE ENCRYPT +DATA command is performed to let the firmware load incoming pages +into guest memory. After migration is completed, a RECEIVE FINISH +command is performed to the firmware. + +Signed-off-by: Jiang Xin +Signed-off-by: hanliyang +--- + target/i386/csv.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++ + target/i386/csv.h | 2 ++ + target/i386/sev.c | 8 +++++ + target/i386/sev.h | 1 + + target/i386/trace-events | 1 + + 5 files changed, 99 insertions(+) + +diff --git a/target/i386/csv.c b/target/i386/csv.c +index ffa5a73..81407e3 100644 +--- a/target/i386/csv.c ++++ b/target/i386/csv.c +@@ -38,11 +38,14 @@ bool csv_kvm_cpu_reset_inhibit; + struct ConfidentialGuestMemoryEncryptionOps csv3_memory_encryption_ops = { + .save_setup = sev_save_setup, + .save_outgoing_page = NULL, ++ .load_incoming_page = csv3_load_incoming_page, + .is_gfn_in_unshared_region = NULL, + .save_outgoing_shared_regions_list = sev_save_outgoing_shared_regions_list, + .load_incoming_shared_regions_list = sev_load_incoming_shared_regions_list, + .queue_outgoing_page = csv3_queue_outgoing_page, + .save_queued_outgoing_pages = csv3_save_queued_outgoing_pages, ++ .queue_incoming_page = NULL, ++ .load_queued_incoming_pages = NULL, + }; + + #define CSV3_OUTGOING_PAGE_NUM \ +@@ -89,6 +92,7 @@ csv3_init(uint32_t policy, int fd, void *state, struct sev_ops *ops) + QTAILQ_INIT(&csv3_guest.dma_map_regions_list); + qemu_mutex_init(&csv3_guest.dma_map_regions_list_mutex); + csv3_guest.sev_send_start = ops->sev_send_start; ++ csv3_guest.sev_receive_start = ops->sev_receive_start; + } + return 0; + } +@@ -485,3 +489,86 @@ csv3_save_queued_outgoing_pages(QEMUFile *f, uint64_t *bytes_sent) + + return csv3_send_encrypt_data(s, f, NULL, 0, bytes_sent); + } ++ ++static int ++csv3_receive_start(QEMUFile *f) ++{ ++ if (csv3_guest.sev_receive_start) ++ return csv3_guest.sev_receive_start(f); ++ else ++ return -1; ++} ++ ++static int csv3_receive_encrypt_data(QEMUFile *f, uint8_t *ptr) ++{ ++ int ret = 1, fw_error = 0; ++ uint32_t i, guest_addr_entry_num; ++ gchar *hdr = NULL, *trans = NULL; ++ struct guest_addr_entry *guest_addr_data; ++ struct kvm_csv3_receive_encrypt_data update = {}; ++ void *hva = NULL; ++ MemoryRegion *mr = NULL; ++ ++ /* get packet header */ ++ update.hdr_len = qemu_get_be32(f); ++ ++ hdr = g_new(gchar, update.hdr_len); ++ qemu_get_buffer(f, (uint8_t *)hdr, update.hdr_len); ++ update.hdr_uaddr = (uintptr_t)hdr; ++ ++ /* get guest addr data */ ++ update.guest_addr_len = qemu_get_be32(f); ++ ++ guest_addr_data = (struct guest_addr_entry *)g_new(gchar, update.guest_addr_len); ++ qemu_get_buffer(f, (uint8_t *)guest_addr_data, update.guest_addr_len); ++ update.guest_addr_data = (uintptr_t)guest_addr_data; ++ ++ /* get transport buffer */ ++ update.trans_len = qemu_get_be32(f); ++ ++ trans = g_new(gchar, update.trans_len); ++ update.trans_uaddr = (uintptr_t)trans; ++ qemu_get_buffer(f, (uint8_t *)update.trans_uaddr, update.trans_len); ++ ++ /* update share memory. */ ++ guest_addr_entry_num = update.guest_addr_len / sizeof(struct guest_addr_entry); ++ for (i = 0; i < guest_addr_entry_num; i++) { ++ if (guest_addr_data[i].share) { ++ hva = gpa2hva(&mr, ++ ((uint64_t)guest_addr_data[i].gfn << TARGET_PAGE_BITS), ++ TARGET_PAGE_SIZE, ++ NULL); ++ if (hva) ++ memcpy(hva, trans + i * TARGET_PAGE_SIZE, TARGET_PAGE_SIZE); ++ } ++ } ++ ++ trace_kvm_csv3_receive_encrypt_data(trans, update.trans_len, hdr, update.hdr_len); ++ ++ ret = csv3_ioctl(KVM_CSV3_RECEIVE_ENCRYPT_DATA, &update, &fw_error); ++ if (ret) { ++ error_report("Error RECEIVE_ENCRYPT_DATA ret=%d fw_error=%d '%s'", ++ ret, fw_error, fw_error_to_str(fw_error)); ++ goto err; ++ } ++ ++err: ++ g_free(trans); ++ g_free(guest_addr_data); ++ g_free(hdr); ++ return ret; ++} ++ ++int csv3_load_incoming_page(QEMUFile *f, uint8_t *ptr) ++{ ++ /* ++ * If this is first buffer and SEV is not in recieiving state then ++ * use RECEIVE_START command to create a encryption context. ++ */ ++ if (!csv3_check_state(SEV_STATE_RECEIVE_UPDATE) && ++ csv3_receive_start(f)) { ++ return 1; ++ } ++ ++ return csv3_receive_encrypt_data(f, ptr); ++} +diff --git a/target/i386/csv.h b/target/i386/csv.h +index e808bea..b0adae0 100644 +--- a/target/i386/csv.h ++++ b/target/i386/csv.h +@@ -107,6 +107,7 @@ struct Csv3GuestState { + size_t guest_addr_len; + + int (*sev_send_start)(QEMUFile *f, uint64_t *bytes_sent); ++ int (*sev_receive_start)(QEMUFile *f); + }; + + typedef struct Csv3GuestState Csv3GuestState; +@@ -120,6 +121,7 @@ int csv3_load_data(uint64_t gpa, uint8_t *ptr, uint64_t len, Error **errp); + + int csv3_shared_region_dma_map(uint64_t start, uint64_t end); + void csv3_shared_region_dma_unmap(uint64_t start, uint64_t end); ++int csv3_load_incoming_page(QEMUFile *f, uint8_t *ptr); + int csv3_queue_outgoing_page(uint8_t *ptr, uint32_t sz, uint64_t addr); + int csv3_save_queued_outgoing_pages(QEMUFile *f, uint64_t *bytes_sent); + +diff --git a/target/i386/sev.c b/target/i386/sev.c +index 465b62c..337f544 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -2619,10 +2619,18 @@ static int _sev_send_start(QEMUFile *f, uint64_t *bytes_sent) + return sev_send_start(s, f, bytes_sent); + } + ++static int _sev_receive_start(QEMUFile *f) ++{ ++ SevGuestState *s = sev_guest; ++ ++ return sev_receive_start(s, f); ++} ++ + struct sev_ops sev_ops = { + .sev_ioctl = sev_ioctl, + .fw_error_to_str = fw_error_to_str, + .sev_send_start = _sev_send_start, ++ .sev_receive_start = _sev_receive_start, + }; + + static void +diff --git a/target/i386/sev.h b/target/i386/sev.h +index 8ccef22..647b426 100644 +--- a/target/i386/sev.h ++++ b/target/i386/sev.h +@@ -84,6 +84,7 @@ struct sev_ops { + int (*sev_ioctl)(int fd, int cmd, void *data, int *error); + const char *(*fw_error_to_str)(int code); + int (*sev_send_start)(QEMUFile *f, uint64_t *bytes_sent); ++ int (*sev_receive_start)(QEMUFile *f); + }; + + extern struct sev_ops sev_ops; +diff --git a/target/i386/trace-events b/target/i386/trace-events +index 6ebb644..9609fe3 100644 +--- a/target/i386/trace-events ++++ b/target/i386/trace-events +@@ -23,3 +23,4 @@ kvm_sev_receive_update_vmsa(uint32_t cpu_id, uint32_t cpu_index, void *src, int + # csv.c + kvm_csv3_launch_encrypt_data(uint64_t gpa, void *addr, uint64_t len) "gpa 0x%" PRIx64 "addr %p len 0x%" PRIu64 + kvm_csv3_send_encrypt_data(void *dst, int len) "trans %p len %d" ++kvm_csv3_receive_encrypt_data(void *dst, int len, void *hdr, int hdr_len) "trans %p len %d hdr %p hdr_len %d" +-- +1.8.3.1 + diff --git a/0229-target-i386-csv-Add-support-to-migrate-the-outgoing-.patch b/0229-target-i386-csv-Add-support-to-migrate-the-outgoing-.patch new file mode 100644 index 0000000000000000000000000000000000000000..642dac6e61f398423fc106e5f288cf0fcae7fe01 --- /dev/null +++ b/0229-target-i386-csv-Add-support-to-migrate-the-outgoing-.patch @@ -0,0 +1,139 @@ +From 709d9a2bbbde08209c706ee17351d4a6028fff1f Mon Sep 17 00:00:00 2001 +From: jiangxin +Date: Fri, 17 Jun 2022 09:52:31 +0800 +Subject: [PATCH 239/293] target/i386: csv: Add support to migrate the outgoing + context for CSV3 guest + +CSV3 needs to migrate guest cpu's context pages. Prior to migration +of the context, it should query transfer buffer length and header +data length by SEND ENCRYPT CONTEXT command. New migration flag +RAM_SAVE_ENCRYPTED_CSV3_CONTEXT is defined for CSV3. + +Signed-off-by: Jiang Xin +Signed-off-by: hanliyang +--- + target/i386/csv.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++ + target/i386/csv.h | 1 + + target/i386/trace-events | 1 + + 3 files changed, 83 insertions(+) + +diff --git a/target/i386/csv.c b/target/i386/csv.c +index 81407e3..1560db6 100644 +--- a/target/i386/csv.c ++++ b/target/i386/csv.c +@@ -46,6 +46,7 @@ struct ConfidentialGuestMemoryEncryptionOps csv3_memory_encryption_ops = { + .save_queued_outgoing_pages = csv3_save_queued_outgoing_pages, + .queue_incoming_page = NULL, + .load_queued_incoming_pages = NULL, ++ .save_outgoing_cpu_state = csv3_save_outgoing_context, + }; + + #define CSV3_OUTGOING_PAGE_NUM \ +@@ -572,3 +573,83 @@ int csv3_load_incoming_page(QEMUFile *f, uint8_t *ptr) + + return csv3_receive_encrypt_data(f, ptr); + } ++ ++static int ++csv3_send_get_context_len(int *fw_err, int *context_len, int *hdr_len) ++{ ++ int ret = 0; ++ struct kvm_csv3_send_encrypt_context update = { 0 }; ++ ++ ret = csv3_ioctl(KVM_CSV3_SEND_ENCRYPT_CONTEXT, &update, fw_err); ++ if (*fw_err != SEV_RET_INVALID_LEN) { ++ error_report("%s: failed to get context length ret=%d fw_error=%d '%s'", ++ __func__, ret, *fw_err, fw_error_to_str(*fw_err)); ++ ret = -1; ++ goto err; ++ } ++ ++ if (update.trans_len <= INT_MAX && update.hdr_len <= INT_MAX) { ++ *context_len = update.trans_len; ++ *hdr_len = update.hdr_len; ++ } ++ ret = 0; ++err: ++ return ret; ++} ++ ++static int ++csv3_send_encrypt_context(Csv3GuestState *s, QEMUFile *f, uint64_t *bytes_sent) ++{ ++ int ret, fw_error = 0; ++ int context_len = 0; ++ int hdr_len = 0; ++ guchar *trans; ++ guchar *hdr; ++ struct kvm_csv3_send_encrypt_context update = { }; ++ ++ ret = csv3_send_get_context_len(&fw_error, &context_len, &hdr_len); ++ if (context_len < 1 || hdr_len < 1) { ++ error_report("%s: fail to get context length fw_error=%d '%s'", ++ __func__, fw_error, fw_error_to_str(fw_error)); ++ return 1; ++ } ++ ++ /* allocate transport buffer */ ++ trans = g_new(guchar, context_len); ++ hdr = g_new(guchar, hdr_len); ++ ++ update.hdr_uaddr = (uintptr_t)hdr; ++ update.hdr_len = hdr_len; ++ update.trans_uaddr = (uintptr_t)trans; ++ update.trans_len = context_len; ++ ++ trace_kvm_csv3_send_encrypt_context(trans, update.trans_len); ++ ++ ret = csv3_ioctl(KVM_CSV3_SEND_ENCRYPT_CONTEXT, &update, &fw_error); ++ if (ret) { ++ error_report("%s: SEND_ENCRYPT_CONTEXT ret=%d fw_error=%d '%s'", ++ __func__, ret, fw_error, fw_error_to_str(fw_error)); ++ goto err; ++ } ++ ++ qemu_put_be32(f, update.hdr_len); ++ qemu_put_buffer(f, (uint8_t *)update.hdr_uaddr, update.hdr_len); ++ *bytes_sent += 4 + update.hdr_len; ++ ++ qemu_put_be32(f, update.trans_len); ++ qemu_put_buffer(f, (uint8_t *)update.trans_uaddr, update.trans_len); ++ *bytes_sent += 4 + update.trans_len; ++ ++err: ++ g_free(trans); ++ g_free(hdr); ++ return ret; ++} ++ ++int csv3_save_outgoing_context(QEMUFile *f, uint64_t *bytes_sent) ++{ ++ Csv3GuestState *s = &csv3_guest; ++ ++ /* send csv3 context. */ ++ return csv3_send_encrypt_context(s, f, bytes_sent); ++} +diff --git a/target/i386/csv.h b/target/i386/csv.h +index b0adae0..e9b8e00 100644 +--- a/target/i386/csv.h ++++ b/target/i386/csv.h +@@ -124,5 +124,6 @@ void csv3_shared_region_dma_unmap(uint64_t start, uint64_t end); + int csv3_load_incoming_page(QEMUFile *f, uint8_t *ptr); + int csv3_queue_outgoing_page(uint8_t *ptr, uint32_t sz, uint64_t addr); + int csv3_save_queued_outgoing_pages(QEMUFile *f, uint64_t *bytes_sent); ++int csv3_save_outgoing_context(QEMUFile *f, uint64_t *bytes_sent); + + #endif +diff --git a/target/i386/trace-events b/target/i386/trace-events +index 9609fe3..31a2418 100644 +--- a/target/i386/trace-events ++++ b/target/i386/trace-events +@@ -23,4 +23,5 @@ kvm_sev_receive_update_vmsa(uint32_t cpu_id, uint32_t cpu_index, void *src, int + # csv.c + kvm_csv3_launch_encrypt_data(uint64_t gpa, void *addr, uint64_t len) "gpa 0x%" PRIx64 "addr %p len 0x%" PRIu64 + kvm_csv3_send_encrypt_data(void *dst, int len) "trans %p len %d" ++kvm_csv3_send_encrypt_context(void *dst, int len) "trans %p len %d" + kvm_csv3_receive_encrypt_data(void *dst, int len, void *hdr, int hdr_len) "trans %p len %d hdr %p hdr_len %d" +-- +1.8.3.1 + diff --git a/0230-target-i386-csv-Add-support-to-migrate-the-incoming-.patch b/0230-target-i386-csv-Add-support-to-migrate-the-incoming-.patch new file mode 100644 index 0000000000000000000000000000000000000000..5608124b1c3404ef68dc143e4c71de6eec892227 --- /dev/null +++ b/0230-target-i386-csv-Add-support-to-migrate-the-incoming-.patch @@ -0,0 +1,110 @@ +From 54e03311a01d11debdd264930c790564bdb61381 Mon Sep 17 00:00:00 2001 +From: jiangxin +Date: Fri, 17 Jun 2022 10:00:46 +0800 +Subject: [PATCH 240/293] target/i386: csv: Add support to migrate the incoming + context for CSV3 guest + +The csv3_load_incoming_context() provides the method to read incoming +guest's context from socket. It loads them into guest private memory. +This is the last step during migration and RECEIVE FINISH command is +performed by then to complete the whole migration. + +Signed-off-by: Jiang Xin +Signed-off-by: hanliyang +--- + target/i386/csv.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ + target/i386/csv.h | 1 + + target/i386/trace-events | 1 + + 3 files changed, 47 insertions(+) + +diff --git a/target/i386/csv.c b/target/i386/csv.c +index 1560db6..0593f9b 100644 +--- a/target/i386/csv.c ++++ b/target/i386/csv.c +@@ -47,6 +47,7 @@ struct ConfidentialGuestMemoryEncryptionOps csv3_memory_encryption_ops = { + .queue_incoming_page = NULL, + .load_queued_incoming_pages = NULL, + .save_outgoing_cpu_state = csv3_save_outgoing_context, ++ .load_incoming_cpu_state = csv3_load_incoming_context, + }; + + #define CSV3_OUTGOING_PAGE_NUM \ +@@ -646,6 +647,42 @@ err: + return ret; + } + ++static int ++csv3_receive_encrypt_context(Csv3GuestState *s, QEMUFile *f) ++{ ++ int ret = 1, fw_error = 0; ++ gchar *hdr = NULL, *trans = NULL; ++ struct kvm_csv3_receive_encrypt_context update = {}; ++ ++ /* get packet header */ ++ update.hdr_len = qemu_get_be32(f); ++ ++ hdr = g_new(gchar, update.hdr_len); ++ qemu_get_buffer(f, (uint8_t *)hdr, update.hdr_len); ++ update.hdr_uaddr = (uintptr_t)hdr; ++ ++ /* get transport buffer */ ++ update.trans_len = qemu_get_be32(f); ++ ++ trans = g_new(gchar, update.trans_len); ++ update.trans_uaddr = (uintptr_t)trans; ++ qemu_get_buffer(f, (uint8_t *)update.trans_uaddr, update.trans_len); ++ ++ trace_kvm_csv3_receive_encrypt_context(trans, update.trans_len, hdr, update.hdr_len); ++ ++ ret = csv3_ioctl(KVM_CSV3_RECEIVE_ENCRYPT_CONTEXT, &update, &fw_error); ++ if (ret) { ++ error_report("Error RECEIVE_ENCRYPT_CONTEXT ret=%d fw_error=%d '%s'", ++ ret, fw_error, fw_error_to_str(fw_error)); ++ goto err; ++ } ++ ++err: ++ g_free(trans); ++ g_free(hdr); ++ return ret; ++} ++ + int csv3_save_outgoing_context(QEMUFile *f, uint64_t *bytes_sent) + { + Csv3GuestState *s = &csv3_guest; +@@ -653,3 +690,11 @@ int csv3_save_outgoing_context(QEMUFile *f, uint64_t *bytes_sent) + /* send csv3 context. */ + return csv3_send_encrypt_context(s, f, bytes_sent); + } ++ ++int csv3_load_incoming_context(QEMUFile *f) ++{ ++ Csv3GuestState *s = &csv3_guest; ++ ++ /* receive csv3 context. */ ++ return csv3_receive_encrypt_context(s, f); ++} +diff --git a/target/i386/csv.h b/target/i386/csv.h +index e9b8e00..bbe3724 100644 +--- a/target/i386/csv.h ++++ b/target/i386/csv.h +@@ -122,6 +122,7 @@ int csv3_load_data(uint64_t gpa, uint8_t *ptr, uint64_t len, Error **errp); + int csv3_shared_region_dma_map(uint64_t start, uint64_t end); + void csv3_shared_region_dma_unmap(uint64_t start, uint64_t end); + int csv3_load_incoming_page(QEMUFile *f, uint8_t *ptr); ++int csv3_load_incoming_context(QEMUFile *f); + int csv3_queue_outgoing_page(uint8_t *ptr, uint32_t sz, uint64_t addr); + int csv3_save_queued_outgoing_pages(QEMUFile *f, uint64_t *bytes_sent); + int csv3_save_outgoing_context(QEMUFile *f, uint64_t *bytes_sent); +diff --git a/target/i386/trace-events b/target/i386/trace-events +index 31a2418..515441c 100644 +--- a/target/i386/trace-events ++++ b/target/i386/trace-events +@@ -25,3 +25,4 @@ kvm_csv3_launch_encrypt_data(uint64_t gpa, void *addr, uint64_t len) "gpa 0x%" P + kvm_csv3_send_encrypt_data(void *dst, int len) "trans %p len %d" + kvm_csv3_send_encrypt_context(void *dst, int len) "trans %p len %d" + kvm_csv3_receive_encrypt_data(void *dst, int len, void *hdr, int hdr_len) "trans %p len %d hdr %p hdr_len %d" ++kvm_csv3_receive_encrypt_context(void *dst, int len, void *hdr, int hdr_len) "trans %p len %d hdr %p hdr_len %d" +-- +1.8.3.1 + diff --git a/0231-target-i386-sev-Fix-incompatibility-between-SEV-and-.patch b/0231-target-i386-sev-Fix-incompatibility-between-SEV-and-.patch new file mode 100644 index 0000000000000000000000000000000000000000..352ddf01b76e9926f1492aa83a8f222862a1f3c5 --- /dev/null +++ b/0231-target-i386-sev-Fix-incompatibility-between-SEV-and-.patch @@ -0,0 +1,37 @@ +From 4130a65b96413c814cf32b7d4d72dbf523796a86 Mon Sep 17 00:00:00 2001 +From: hanliyang +Date: Fri, 1 Mar 2024 14:12:44 +0800 +Subject: [PATCH 241/293] target/i386: sev: Fix incompatibility between SEV and + CSV on the GET_ID API + +If the length of GET_ID request is too small, Hygon CSV will return +SEV_RET_INVALID_PARAM. This return code doesn't comply with SEV API +Spec. + +Hygon will consider to fix the compitibility issue of return value +of the GET_ID API, so also check whether the return value is +SEV_RET_INVALID_LEN on Hygon CPUs. + +Signed-off-by: hanliyang +Change-Id: I204e69817fbb97c6c81bea086af53d4c312895b4 +--- + target/i386/sev.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/target/i386/sev.c b/target/i386/sev.c +index 337f544..95b16cc 100644 +--- a/target/i386/sev.c ++++ b/target/i386/sev.c +@@ -589,7 +589,8 @@ static int sev_get_cpu0_id(int fd, guchar **id, size_t *id_len, Error **errp) + + /* query the ID length */ + r = sev_platform_ioctl(fd, SEV_GET_ID2, &get_id2, &err); +- if (r < 0 && err != SEV_RET_INVALID_LEN) { ++ if (r < 0 && err != SEV_RET_INVALID_LEN && ++ !(is_hygon_cpu() && err == SEV_RET_INVALID_PARAM)) { + error_setg(errp, "SEV: Failed to get ID ret=%d fw_err=%d (%s)", + r, err, fw_error_to_str(err)); + return 1; +-- +1.8.3.1 + diff --git a/0232-target-i386-sev-Add-support-for-reuse-ASID-for-diffe.patch b/0232-target-i386-sev-Add-support-for-reuse-ASID-for-diffe.patch new file mode 100644 index 0000000000000000000000000000000000000000..894de2dbe57ebe44115bb3df919e6ed2d162c793 --- /dev/null +++ b/0232-target-i386-sev-Add-support-for-reuse-ASID-for-diffe.patch @@ -0,0 +1,188 @@ +From f49ae1db13f64cf331b357df888bbe01c90da72b Mon Sep 17 00:00:00 2001 +From: appleLin +Date: Wed, 3 Aug 2022 21:02:41 +0800 +Subject: [PATCH 242/293] target/i386: sev: Add support for reuse ASID for + different CSV guests + +In you want to reuse one ASID for many CSV guests, you should provide a +label (i.e. userid) and the length of the label when launch CSV guest. +The CSV guests which were provided the same userid will share the same +ASID. + +Signed-off-by: hanliyang +Change-Id: I929a7489b310f08535df67c231ee7b3cd9cee51e +--- + linux-headers/linux/kvm.h | 5 +++++ + qapi/qom.json | 5 ++++- + qemu-options.hx | 5 ++++- + target/i386/csv.h | 5 +++-- + target/i386/sev.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++- + 5 files changed, 62 insertions(+), 5 deletions(-) + +diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h +index 6edf0b3..f67a7dd 100644 +--- a/linux-headers/linux/kvm.h ++++ b/linux-headers/linux/kvm.h +@@ -2060,6 +2060,11 @@ struct kvm_csv_command_batch { + __u64 csv_batch_list_uaddr; + }; + ++struct kvm_csv_init { ++ __u64 userid_addr; ++ __u32 len; ++}; ++ + /* CSV3 command */ + enum csv3_cmd_id { + KVM_CSV3_NR_MIN = 0xc0, +diff --git a/qapi/qom.json b/qapi/qom.json +index c53ef97..89a2516 100644 +--- a/qapi/qom.json ++++ b/qapi/qom.json +@@ -866,6 +866,8 @@ + # designated guest firmware page for measured boot with -kernel + # (default: false) (since 6.2) + # ++# @user-id: the user id of the guest owner, only support on Hygon CPUs ++# + # Since: 2.12 + ## + { 'struct': 'SevGuestProperties', +@@ -876,7 +878,8 @@ + '*handle': 'uint32', + '*cbitpos': 'uint32', + 'reduced-phys-bits': 'uint32', +- '*kernel-hashes': 'bool' } } ++ '*kernel-hashes': 'bool', ++ '*user-id': 'str' } } + + ## + # @ThreadContextProperties: +diff --git a/qemu-options.hx b/qemu-options.hx +index b6b4ad9..c260117 100644 +--- a/qemu-options.hx ++++ b/qemu-options.hx +@@ -5645,7 +5645,7 @@ SRST + -object secret,id=sec0,keyid=secmaster0,format=base64,\\ + data=$SECRET,iv=$(user_id); ++} ++ ++static void ++sev_guest_set_user_id(Object *obj, const char *value, Error **errp) ++{ ++ SevGuestState *s = SEV_GUEST(obj); ++ ++ s->user_id = g_strdup(value); ++} ++ ++static char * + sev_guest_get_sev_device(Object *obj, Error **errp) + { + SevGuestState *sev = SEV_GUEST(obj); +@@ -426,6 +443,11 @@ sev_guest_class_init(ObjectClass *oc, void *data) + sev_guest_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_str(oc, "user-id", ++ sev_guest_get_user_id, ++ sev_guest_set_user_id); ++ object_class_property_set_description(oc, "user-id", ++ "user id of the guest owner"); + } + + static void +@@ -1178,7 +1200,30 @@ int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) + } + + trace_kvm_sev_init(); +- ret = sev_ioctl(sev->sev_fd, cmd, NULL, &fw_error); ++ ++ /* Only support reuse asid for CSV/CSV2 guest */ ++ if (is_hygon_cpu() && ++ (sev_guest->policy & GUEST_POLICY_REUSE_ASID) && ++ !(sev_guest->policy & GUEST_POLICY_CSV3_BIT)) { ++ char *user_id = NULL; ++ struct kvm_csv_init *init_cmd_buf = NULL; ++ ++ user_id = object_property_get_str(OBJECT(sev), "user-id", NULL); ++ if (user_id && strlen(user_id)) { ++ init_cmd_buf = g_new0(struct kvm_csv_init, 1); ++ init_cmd_buf->len = strlen(user_id); ++ init_cmd_buf->userid_addr = (__u64)user_id; ++ } ++ ret = sev_ioctl(sev->sev_fd, cmd, init_cmd_buf, &fw_error); ++ ++ if (user_id) { ++ g_free(user_id); ++ g_free(init_cmd_buf); ++ } ++ } else { ++ ret = sev_ioctl(sev->sev_fd, cmd, NULL, &fw_error); ++ } ++ + 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)); +-- +1.8.3.1 + diff --git a/0233-target-i386-Add-Hygon-Dhyana-v3-CPU-model.patch b/0233-target-i386-Add-Hygon-Dhyana-v3-CPU-model.patch new file mode 100644 index 0000000000000000000000000000000000000000..29de8fd807fd5f2036908b55cfdc36c09ec2e568 --- /dev/null +++ b/0233-target-i386-Add-Hygon-Dhyana-v3-CPU-model.patch @@ -0,0 +1,43 @@ +From 77dff465daa65a3dac8f0987fe08162d80a78fc1 Mon Sep 17 00:00:00 2001 +From: Yanjing Zhou +Date: Tue, 16 Apr 2024 15:27:33 +0800 +Subject: [PATCH 243/293] target/i386: Add Hygon Dhyana-v3 CPU model + +Add the following feature bits for Dhyana CPU model: +perfctr-core, clzero, xsaveerptr, aes, pclmulqdq, sha-ni + +Disable xsaves feature bit for Erratum 1386 + +Signed-off-by: Yanjing Zhou +--- + target/i386/cpu.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index a373d8e..61f81ad 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -4805,6 +4805,20 @@ static const X86CPUDefinition builtin_x86_defs[] = { + { /* end of list */ } + }, + }, ++ { .version = 3, ++ .props = (PropValue[]) { ++ { "xsaves", "off" }, ++ { "perfctr-core", "on" }, ++ { "clzero", "on" }, ++ { "xsaveerptr", "on" }, ++ { "aes", "on" }, ++ { "pclmulqdq", "on" }, ++ { "sha-ni", "on" }, ++ { "model-id", ++ "Hygon Dhyana-v3 processor" }, ++ { /* end of list */ } ++ }, ++ }, + { /* end of list */ } + } + }, +-- +1.8.3.1 + diff --git a/0234-target-i386-Add-new-Hygon-Dharma-CPU-model.patch b/0234-target-i386-Add-new-Hygon-Dharma-CPU-model.patch new file mode 100644 index 0000000000000000000000000000000000000000..8fd1626a2e973d554b9ac77664cdf93a975a68f4 --- /dev/null +++ b/0234-target-i386-Add-new-Hygon-Dharma-CPU-model.patch @@ -0,0 +1,133 @@ +From 035ce0bc5b3f6e1621bd11fbc9218dba8ee88fc9 Mon Sep 17 00:00:00 2001 +From: Yanjing Zhou +Date: Tue, 16 Apr 2024 16:05:00 +0800 +Subject: [PATCH 244/293] target/i386: Add new Hygon 'Dharma' CPU model + +Add the following feature bits compare to Dhyana CPU model: +stibp, ibrs, umip, ssbd + +Signed-off-by: Yanjing Zhou +--- + target/i386/cpu.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 99 insertions(+) + +diff --git a/target/i386/cpu.c b/target/i386/cpu.c +index 61f81ad..8649f9e 100644 +--- a/target/i386/cpu.c ++++ b/target/i386/cpu.c +@@ -2163,6 +2163,56 @@ static const CPUCaches epyc_genoa_cache_info = { + }, + }; + ++static const CPUCaches dharma_cache_info = { ++ .l1d_cache = &(CPUCacheInfo) { ++ .type = DATA_CACHE, ++ .level = 1, ++ .size = 32 * KiB, ++ .line_size = 64, ++ .associativity = 8, ++ .partitions = 1, ++ .sets = 64, ++ .lines_per_tag = 1, ++ .self_init = 1, ++ .no_invd_sharing = true, ++ }, ++ .l1i_cache = &(CPUCacheInfo) { ++ .type = INSTRUCTION_CACHE, ++ .level = 1, ++ .size = 32 * KiB, ++ .line_size = 64, ++ .associativity = 8, ++ .partitions = 1, ++ .sets = 64, ++ .lines_per_tag = 1, ++ .self_init = 1, ++ .no_invd_sharing = true, ++ }, ++ .l2_cache = &(CPUCacheInfo) { ++ .type = UNIFIED_CACHE, ++ .level = 2, ++ .size = 512 * KiB, ++ .line_size = 64, ++ .associativity = 8, ++ .partitions = 1, ++ .sets = 1024, ++ .lines_per_tag = 1, ++ }, ++ .l3_cache = &(CPUCacheInfo) { ++ .type = UNIFIED_CACHE, ++ .level = 3, ++ .size = 16 * MiB, ++ .line_size = 64, ++ .associativity = 16, ++ .partitions = 1, ++ .sets = 16384, ++ .lines_per_tag = 1, ++ .self_init = true, ++ .inclusive = true, ++ .complex_indexing = true, ++ }, ++}; ++ + /* The following VMX features are not supported by KVM and are left out in the + * CPU definitions: + * +@@ -5050,6 +5100,55 @@ static const X86CPUDefinition builtin_x86_defs[] = { + .model_id = "AMD EPYC-Genoa Processor", + .cache_info = &epyc_genoa_cache_info, + }, ++ { ++ .name = "Dharma", ++ .level = 0xd, ++ .vendor = CPUID_VENDOR_HYGON, ++ .family = 24, ++ .model = 4, ++ .stepping = 0, ++ .features[FEAT_1_EDX] = ++ CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | CPUID_CLFLUSH | ++ CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | CPUID_PGE | ++ CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | CPUID_MCE | ++ CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | CPUID_DE | ++ CPUID_VME | CPUID_FP87, ++ .features[FEAT_1_ECX] = ++ CPUID_EXT_RDRAND | CPUID_EXT_F16C | CPUID_EXT_AVX | ++ CPUID_EXT_XSAVE | CPUID_EXT_AES | CPUID_EXT_POPCNT | ++ CPUID_EXT_MOVBE | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 | ++ CPUID_EXT_CX16 | CPUID_EXT_FMA | CPUID_EXT_SSSE3 | ++ CPUID_EXT_MONITOR | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3, ++ .features[FEAT_8000_0001_EDX] = ++ CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_PDPE1GB | ++ CPUID_EXT2_FFXSR | CPUID_EXT2_MMXEXT | CPUID_EXT2_NX | ++ CPUID_EXT2_SYSCALL, ++ .features[FEAT_8000_0001_ECX] = ++ CPUID_EXT3_OSVW | CPUID_EXT3_3DNOWPREFETCH | ++ CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_SSE4A | CPUID_EXT3_ABM | ++ CPUID_EXT3_CR8LEG | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM | ++ CPUID_EXT3_TOPOEXT | CPUID_EXT3_PERFCORE, ++ .features[FEAT_8000_0008_EBX] = ++ CPUID_8000_0008_EBX_CLZERO | CPUID_8000_0008_EBX_XSAVEERPTR | ++ CPUID_8000_0008_EBX_IBPB | CPUID_8000_0008_EBX_IBRS | ++ CPUID_8000_0008_EBX_STIBP | CPUID_8000_0008_EBX_AMD_SSBD, ++ .features[FEAT_7_0_EBX] = ++ CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_AVX2 | ++ CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_RDSEED | ++ CPUID_7_0_EBX_ADX | CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLFLUSHOPT | ++ CPUID_7_0_EBX_SHA_NI, ++ .features[FEAT_7_0_ECX] = CPUID_7_0_ECX_UMIP, ++ .features[FEAT_XSAVE] = ++ CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | ++ CPUID_XSAVE_XGETBV1, ++ .features[FEAT_6_EAX] = ++ CPUID_6_EAX_ARAT, ++ .features[FEAT_SVM] = ++ CPUID_SVM_NPT | CPUID_SVM_NRIPSAVE, ++ .xlevel = 0x8000001E, ++ .model_id = "Hygon Dharma Processor", ++ .cache_info = &dharma_cache_info, ++ }, + }; + + /* +-- +1.8.3.1 + diff --git a/0235-vfio-Add-vfio-based-mediated-hct-support.patch b/0235-vfio-Add-vfio-based-mediated-hct-support.patch new file mode 100644 index 0000000000000000000000000000000000000000..2fcbf5d80f4911d7f95eebdd96a75e43ee9dcabf --- /dev/null +++ b/0235-vfio-Add-vfio-based-mediated-hct-support.patch @@ -0,0 +1,591 @@ +From ca383d757fa3a1bd6f0af2d82555502e0fd75e4b Mon Sep 17 00:00:00 2001 +From: Yabin Li +Date: Fri, 4 Aug 2023 21:09:08 +0800 +Subject: [PATCH 245/293] vfio: Add vfio based mediated hct support. + +add device hct used for simulate hygon ccp + +Signed-off-by: liyabin +Signed-off-by: yangdepei +Change-Id: I8606ad46b4fa3671233597e89c14589b96a9081b +--- + hw/vfio/Kconfig | 6 + + hw/vfio/hct.c | 540 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + hw/vfio/meson.build | 1 + + 3 files changed, 547 insertions(+) + create mode 100644 hw/vfio/hct.c + +diff --git a/hw/vfio/Kconfig b/hw/vfio/Kconfig +index 7cdba05..5f0d3c2 100644 +--- a/hw/vfio/Kconfig ++++ b/hw/vfio/Kconfig +@@ -41,3 +41,9 @@ config VFIO_IGD + bool + default y if PC_PCI + depends on VFIO_PCI ++ ++config VFIO_HCT ++ bool ++ default y ++ select VFIO ++ depends on LINUX && PCI +diff --git a/hw/vfio/hct.c b/hw/vfio/hct.c +new file mode 100644 +index 0000000..fb42927 +--- /dev/null ++++ b/hw/vfio/hct.c +@@ -0,0 +1,540 @@ ++/* ++ * vfio based mediated ccp(hct) assignment support ++ * ++ * Copyright 2023 HYGON Corp. ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or (at ++ * your option) any later version. See the COPYING file in the top-level ++ * directory. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "qemu/osdep.h" ++#include "qemu/queue.h" ++#include "qemu/main-loop.h" ++#include "qemu/log.h" ++#include "trace.h" ++#include "hw/pci/pci.h" ++#include "hw/vfio/pci.h" ++#include "qemu/range.h" ++#include "sysemu/kvm.h" ++#include "hw/pci/msi.h" ++#include "qemu/error-report.h" ++#include "qapi/error.h" ++#include "hw/qdev-properties.h" ++ ++#define MAX_CCP_CNT 48 ++#define PAGE_SIZE 4096 ++#define HCT_SHARED_MEMORY_SIZE (PAGE_SIZE * MAX_CCP_CNT) ++#define CCP_INDEX_BYTES 4 ++#define PATH_MAX 4096 ++#define TYPE_HCT_DEV "hct" ++#define PCI_HCT_DEV(obj) OBJECT_CHECK(HCTDevState, (obj), TYPE_HCT_DEV) ++#define HCT_MMIO_SIZE (1 << 20) ++#define HCT_MAX_PASID (1 << 8) ++ ++#define PCI_VENDOR_ID_HYGON_CCP 0x1d94 ++#define PCI_DEVICE_ID_HYGON_CCP 0x1468 ++ ++#define HCT_SHARE_DEV "/dev/hct_share" ++ ++#define HCT_VERSION_STRING "0.5" ++#define DEF_VERSION_STRING "0.1" ++#define VERSION_SIZE 16 ++ ++#define HCT_SHARE_IOC_TYPE 'C' ++#define HCT_SHARE_OP_TYPE 0x01 ++#define HCT_SHARE_OP _IOWR(HCT_SHARE_IOC_TYPE, \ ++ HCT_SHARE_OP_TYPE, \ ++ struct hct_dev_ctrl) ++#define HCT_SHARE_OP_DMA_MAP 0x01 ++#define HCT_SHARE_OP_GET_ID 0x03 ++#define HCT_SHARE_OP_GET_PASID 0x04 ++#define HCT_SHARE_OP_DMA_UNMAP 0x05 ++#define HCT_SHARE_OP_GET_VERSION 0x06 ++ ++/* BARS */ ++#define HCT_REG_BAR_IDX 2 ++#define HCT_SHARED_BAR_IDX 3 ++#define HCT_PASID_BAR_IDX 4 ++ ++#define PASID_OFFSET 40 ++ ++static volatile struct hct_data { ++ int init; ++ int hct_fd; ++ unsigned long pasid; ++ uint8_t *pasid_memory; ++ uint8_t *hct_shared_memory; ++ uint8_t ccp_index[MAX_CCP_CNT]; ++ uint8_t ccp_cnt; ++} hct_data; ++ ++typedef struct SharedDevice { ++ PCIDevice dev; ++ int shared_memory_offset; ++} SharedDevice; ++ ++typedef struct HctDevState { ++ SharedDevice sdev; ++ VFIODevice vdev; ++ MemoryRegion mmio; ++ MemoryRegion shared; ++ MemoryRegion pasid; ++ void *maps[PCI_NUM_REGIONS]; ++} HCTDevState; ++ ++struct hct_dev_ctrl { ++ unsigned char op; ++ unsigned char rsvd[3]; ++ union { ++ unsigned char version[VERSION_SIZE]; ++ struct { ++ unsigned long vaddr; ++ unsigned long iova; ++ unsigned long size; ++ }; ++ unsigned int id; ++ }; ++}; ++ ++static int pasid_get_and_init(HCTDevState *state) ++{ ++ struct hct_dev_ctrl ctrl; ++ int ret; ++ ++ ctrl.op = HCT_SHARE_OP_GET_PASID; ++ ctrl.id = -1; ++ ret = ioctl(hct_data.hct_fd, HCT_SHARE_OP, &ctrl); ++ if (ret < 0) { ++ ret = -errno; ++ error_report("GET_PASID fail: %d", -errno); ++ goto out; ++ } ++ ++ *hct_data.pasid_memory = ctrl.id; ++ hct_data.pasid = ctrl.id; ++ ++out: ++ return ret; ++} ++ ++static const MemoryRegionOps hct_mmio_ops = { ++ .endianness = DEVICE_NATIVE_ENDIAN, ++ .valid = ++ { ++ .min_access_size = 4, ++ .max_access_size = 4, ++ }, ++}; ++ ++static void vfio_hct_detach_device(HCTDevState *state) ++{ ++ vfio_detach_device(&state->vdev); ++ g_free(state->vdev.name); ++} ++ ++static void vfio_hct_exit(PCIDevice *dev) ++{ ++ HCTDevState *state = PCI_HCT_DEV(dev); ++ ++ vfio_hct_detach_device(state); ++ ++ if (hct_data.hct_fd) { ++ qemu_close(hct_data.hct_fd); ++ hct_data.hct_fd = 0; ++ } ++} ++ ++static Property vfio_hct_properties[] = { ++ DEFINE_PROP_STRING("sysfsdev", HCTDevState, vdev.sysfsdev), ++ DEFINE_PROP_END_OF_LIST(), ++}; ++ ++static void vfio_ccp_compute_needs_reset(VFIODevice *vdev) ++{ ++ vdev->needs_reset = false; ++} ++ ++struct VFIODeviceOps vfio_ccp_ops = { ++ .vfio_compute_needs_reset = vfio_ccp_compute_needs_reset, ++}; ++ ++/* create BAR2, BAR3 and BAR4 space for the virtual machine. */ ++static int vfio_hct_region_mmap(HCTDevState *state) ++{ ++ int ret; ++ int i; ++ struct vfio_region_info *info; ++ ++ for (i = 0; i < PCI_ROM_SLOT; i++) { ++ ret = vfio_get_region_info(&state->vdev, i, &info); ++ if (ret) ++ goto out; ++ ++ if (info->size) { ++ state->maps[i] = mmap(NULL, info->size, PROT_READ | PROT_WRITE, ++ MAP_SHARED, state->vdev.fd, info->offset); ++ if (state->maps[i] == MAP_FAILED) { ++ ret = -errno; ++ g_free(info); ++ error_report("vfio mmap fail\n"); ++ goto out; ++ } ++ } ++ g_free(info); ++ } ++ ++ memory_region_init_io(&state->mmio, OBJECT(state), &hct_mmio_ops, state, ++ "hct mmio", HCT_MMIO_SIZE); ++ memory_region_init_ram_device_ptr(&state->mmio, OBJECT(state), "hct mmio", ++ HCT_MMIO_SIZE, ++ state->maps[HCT_REG_BAR_IDX]); ++ ++ memory_region_init_io(&state->shared, OBJECT(state), &hct_mmio_ops, state, ++ "hct shared memory", PAGE_SIZE); ++ memory_region_init_ram_device_ptr( ++ &state->shared, OBJECT(state), "hct shared memory", PAGE_SIZE, ++ (void *)hct_data.hct_shared_memory + ++ state->sdev.shared_memory_offset * PAGE_SIZE); ++ ++ memory_region_init_io(&state->pasid, OBJECT(state), &hct_mmio_ops, state, ++ "hct pasid", PAGE_SIZE); ++ memory_region_init_ram_device_ptr(&state->pasid, OBJECT(state), "hct pasid", ++ PAGE_SIZE, hct_data.pasid_memory); ++ ++ pci_register_bar(&state->sdev.dev, HCT_REG_BAR_IDX, ++ PCI_BASE_ADDRESS_SPACE_MEMORY, &state->mmio); ++ pci_register_bar(&state->sdev.dev, HCT_SHARED_BAR_IDX, ++ PCI_BASE_ADDRESS_SPACE_MEMORY, &state->shared); ++ pci_register_bar(&state->sdev.dev, HCT_PASID_BAR_IDX, ++ PCI_BASE_ADDRESS_SPACE_MEMORY, &state->pasid); ++out: ++ return ret; ++} ++ ++static int hct_check_duplicated_index(int index) ++{ ++ int cnt; ++ for (cnt = 0; cnt < hct_data.ccp_cnt; cnt++) { ++ if (hct_data.ccp_index[cnt] == index) { ++ error_report("many mdev shouldn't be mapped to one ccp in a " ++ "virtual machine!\n"); ++ return -1; ++ } ++ } ++ ++ hct_data.ccp_index[hct_data.ccp_cnt++] = index; ++ return 0; ++} ++ ++static int hct_get_ccp_index(HCTDevState *state) ++{ ++ char path[PATH_MAX]; ++ char buf[CCP_INDEX_BYTES]; ++ int fd; ++ int ret; ++ int ccp_index; ++ ++ snprintf(path, PATH_MAX, "%s/vendor/id", state->vdev.sysfsdev); ++ fd = qemu_open_old(path, O_RDONLY); ++ if (fd < 0) { ++ error_report("open %s fail\n", path); ++ return -errno; ++ } ++ ++ ret = read(fd, buf, sizeof(buf)); ++ if (ret < 0) { ++ ret = -errno; ++ error_report("read %s fail\n", path); ++ goto out; ++ } ++ ++ if (1 != sscanf(buf, "%d", &ccp_index)) { ++ ret = -errno; ++ error_report("format addr %s fail\n", buf); ++ goto out; ++ } ++ ++ if (!hct_check_duplicated_index(ccp_index)) { ++ state->sdev.shared_memory_offset = ccp_index; ++ } else { ++ ret = -1; ++ } ++ ++out: ++ qemu_close(fd); ++ return ret; ++} ++ ++static int hct_api_version_check(void) ++{ ++ struct hct_dev_ctrl ctrl; ++ int ret; ++ ++ ctrl.op = HCT_SHARE_OP_GET_VERSION; ++ memcpy(ctrl.version, DEF_VERSION_STRING, sizeof(DEF_VERSION_STRING)); ++ ret = ioctl(hct_data.hct_fd, HCT_SHARE_OP, &ctrl); ++ if (ret < 0) { ++ error_report("ret %d, errno %d: fail to get hct.ko version.\n", ret, ++ errno); ++ return -1; ++ } else if (memcmp(ctrl.version, HCT_VERSION_STRING, ++ sizeof(HCT_VERSION_STRING)) < 0) { ++ error_report("The hct.ko version is %s, please upgrade to version %s " ++ "or higher.\n", ++ ctrl.version, HCT_VERSION_STRING); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int hct_shared_memory_init(void) ++{ ++ int ret = 0; ++ ++ hct_data.hct_shared_memory = ++ mmap(NULL, HCT_SHARED_MEMORY_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, ++ hct_data.hct_fd, 0); ++ if (hct_data.hct_shared_memory == MAP_FAILED) { ++ ret = -errno; ++ error_report("map hct shared memory fail\n"); ++ goto out; ++ } ++ ++out: ++ return ret; ++} ++ ++static void hct_listener_region_add(MemoryListener *listener, ++ MemoryRegionSection *section) ++{ ++ struct hct_dev_ctrl ctrl; ++ hwaddr iova; ++ Int128 llend, llsize; ++ void *vaddr; ++ int ret; ++ ++ iova = REAL_HOST_PAGE_ALIGN(section->offset_within_address_space); ++ llend = int128_make64(section->offset_within_address_space); ++ llend = int128_add(llend, section->size); ++ llend = int128_add(llend, int128_exts64(qemu_real_host_page_mask())); ++ ++ if (int128_ge(int128_make64(iova), llend)) { ++ return; ++ } ++ ++ if (!section->mr->ram) { ++ return; ++ } ++ ++ vaddr = memory_region_get_ram_ptr(section->mr) + ++ section->offset_within_region + ++ (iova - section->offset_within_address_space); ++ llsize = int128_sub(llend, int128_make64(iova)); ++ ++ ctrl.op = HCT_SHARE_OP_DMA_MAP; ++ ctrl.iova = iova | (hct_data.pasid << PASID_OFFSET); ++ ctrl.vaddr = (uint64_t)vaddr; ++ ctrl.size = llsize; ++ ret = ioctl(hct_data.hct_fd, HCT_SHARE_OP, &ctrl); ++ if (ret < 0) ++ error_report("VFIO_MAP_DMA: %d, iova=%lx", -errno, iova); ++} ++ ++static void hct_listener_region_del(MemoryListener *listener, ++ MemoryRegionSection *section) ++{ ++ struct hct_dev_ctrl ctrl; ++ hwaddr iova; ++ Int128 llend, llsize; ++ int ret; ++ ++ iova = REAL_HOST_PAGE_ALIGN(section->offset_within_address_space); ++ llend = int128_make64(section->offset_within_address_space); ++ llend = int128_add(llend, section->size); ++ llend = int128_add(llend, int128_exts64(qemu_real_host_page_mask())); ++ ++ if (int128_ge(int128_make64(iova), llend)) { ++ return; ++ } ++ ++ if (!section->mr->ram) { ++ return; ++ } ++ ++ llsize = int128_sub(llend, int128_make64(iova)); ++ ++ ctrl.op = HCT_SHARE_OP_DMA_UNMAP; ++ ctrl.iova = iova | (hct_data.pasid << PASID_OFFSET); ++ ctrl.size = llsize; ++ ret = ioctl(hct_data.hct_fd, HCT_SHARE_OP, &ctrl); ++ if (ret < 0) ++ error_report("VFIO_UNMAP_DMA: %d", -errno); ++} ++ ++static MemoryListener hct_memory_listener = { ++ .region_add = hct_listener_region_add, ++ .region_del = hct_listener_region_del, ++}; ++ ++static void hct_data_uninit(HCTDevState *state) ++{ ++ if (hct_data.hct_fd) { ++ qemu_close(hct_data.hct_fd); ++ hct_data.hct_fd = 0; ++ } ++ ++ if (hct_data.pasid) { ++ hct_data.pasid = 0; ++ } ++ ++ if (hct_data.pasid_memory) { ++ munmap(hct_data.pasid_memory, PAGE_SIZE); ++ hct_data.pasid_memory = NULL; ++ } ++ ++ if (hct_data.hct_shared_memory) { ++ munmap((void *)hct_data.hct_shared_memory, HCT_SHARED_MEMORY_SIZE); ++ hct_data.hct_shared_memory = NULL; ++ } ++ ++ memory_listener_unregister(&hct_memory_listener); ++} ++ ++static int hct_data_init(HCTDevState *state) ++{ ++ int ret; ++ ++ if (hct_data.init == 0) { ++ ++ hct_data.hct_fd = qemu_open_old(HCT_SHARE_DEV, O_RDWR); ++ if (hct_data.hct_fd < 0) { ++ error_report("fail to open %s, errno %d.", HCT_SHARE_DEV, errno); ++ ret = -errno; ++ goto out; ++ } ++ ++ /* The hct.ko version number needs not to be less than 0.2. */ ++ ret = hct_api_version_check(); ++ if (ret) ++ goto out; ++ ++ /* assign a page to the virtual BAR3 of each CCP. */ ++ ret = hct_shared_memory_init(); ++ if (ret) ++ goto out; ++ ++ hct_data.pasid_memory = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, ++ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); ++ if (hct_data.pasid_memory < 0) ++ goto unmap_shared_memory_exit; ++ ++ /* assign a unique pasid to each virtual machine. */ ++ ret = pasid_get_and_init(state); ++ if (ret < 0) ++ goto unmap_pasid_memory_exit; ++ ++ /* perform DMA_MAP and DMA_UNMAP operations on all memories of the ++ * virtual machine. */ ++ memory_listener_register(&hct_memory_listener, &address_space_memory); ++ ++ hct_data.init = 1; ++ } ++ ++ return hct_get_ccp_index(state); ++ ++unmap_pasid_memory_exit: ++ munmap(hct_data.pasid_memory, PAGE_SIZE); ++ ++unmap_shared_memory_exit: ++ munmap((void *)hct_data.hct_shared_memory, HCT_SHARED_MEMORY_SIZE); ++ ++out: ++ return ret; ++} ++ ++/* When device is loaded */ ++static void vfio_hct_realize(PCIDevice *pci_dev, Error **errp) ++{ ++ int ret; ++ char *mdevid; ++ Error *err = NULL; ++ HCTDevState *state = PCI_HCT_DEV(pci_dev); ++ ++ /* parsing mdev device name from startup scripts */ ++ mdevid = g_path_get_basename(state->vdev.sysfsdev); ++ state->vdev.name = g_strdup_printf("%s", mdevid); ++ ++ ret = hct_data_init(state); ++ if (ret < 0) { ++ g_free(state->vdev.name); ++ goto out; ++ } ++ ++ ret = vfio_attach_device(state->vdev.name, &state->vdev, ++ pci_device_iommu_address_space(pci_dev), &err); ++ ++ if (ret) { ++ error_report("attach device failed, name = %s", state->vdev.name); ++ goto data_uninit_out; ++ } ++ ++ state->vdev.ops = &vfio_ccp_ops; ++ state->vdev.dev = &state->sdev.dev.qdev; ++ ++ ret = vfio_hct_region_mmap(state); ++ if (ret < 0) ++ goto detach_device_out; ++ ++ return; ++ ++detach_device_out: ++ vfio_hct_detach_device(state); ++ ++data_uninit_out: ++ hct_data_uninit(state); ++ ++out: ++ return; ++} ++ ++static void hct_dev_class_init(ObjectClass *klass, void *data) ++{ ++ DeviceClass *dc = DEVICE_CLASS(klass); ++ PCIDeviceClass *pdc = PCI_DEVICE_CLASS(klass); ++ ++ dc->desc = "HCT Device"; ++ device_class_set_props(dc, vfio_hct_properties); ++ ++ pdc->realize = vfio_hct_realize; ++ pdc->exit = vfio_hct_exit; ++ pdc->vendor_id = PCI_VENDOR_ID_HYGON_CCP; ++ pdc->device_id = PCI_DEVICE_ID_HYGON_CCP; ++ pdc->class_id = PCI_CLASS_CRYPT_OTHER; ++ set_bit(DEVICE_CATEGORY_MISC, dc->categories); ++ ++ return; ++} ++ ++static const TypeInfo pci_hct_info = { ++ .name = TYPE_HCT_DEV, ++ .parent = TYPE_PCI_DEVICE, ++ .instance_size = sizeof(HCTDevState), ++ .class_init = hct_dev_class_init, ++ .interfaces = ++ (InterfaceInfo[]){ ++ {INTERFACE_CONVENTIONAL_PCI_DEVICE}, ++ {}, ++ }, ++}; ++ ++static void hct_register_types(void) { type_register_static(&pci_hct_info); } ++ ++type_init(hct_register_types); +diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build +index 2a6912c..b1db4c8 100644 +--- a/hw/vfio/meson.build ++++ b/hw/vfio/meson.build +@@ -17,5 +17,6 @@ vfio_ss.add(when: 'CONFIG_VFIO_XGMAC', if_true: files('calxeda-xgmac.c')) + vfio_ss.add(when: 'CONFIG_VFIO_AMD_XGBE', if_true: files('amd-xgbe.c')) + vfio_ss.add(when: 'CONFIG_VFIO_AP', if_true: files('ap.c')) + vfio_ss.add(when: 'CONFIG_VFIO_IGD', if_true: files('igd.c')) ++vfio_ss.add(when: 'CONFIG_VFIO_HCT', if_true: files('hct.c')) + + specific_ss.add_all(when: 'CONFIG_VFIO', if_true: vfio_ss) +-- +1.8.3.1 + diff --git a/0236-hw-net-virtio-net-Update-event-idx-if-guest-has-made.patch b/0236-hw-net-virtio-net-Update-event-idx-if-guest-has-made.patch new file mode 100644 index 0000000000000000000000000000000000000000..68fe5c5d44a4872df27940100145cbf6e8c7b7a7 --- /dev/null +++ b/0236-hw-net-virtio-net-Update-event-idx-if-guest-has-made.patch @@ -0,0 +1,31 @@ +From 5da94be3c83d1c4598d7d8fd061122db1df8db2a Mon Sep 17 00:00:00 2001 +From: eastmoutain <14304864+eastmoutain@user.noreply.gitee.com> +Date: Mon, 17 Jun 2024 10:00:46 +0800 +Subject: [PATCH 246/293] hw/net: virtio-net: Update event idx if guest has + made extra buffers during double check + +If guest has made some buffers available during double check, +but the total buffer size available is lower than @bufsize, +notify the guest with the latest available idx(event idx) +seen by the host. + +Signed-off-by: yangwencheng +--- + hw/net/virtio-net.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c +index 73024ba..5b0d814 100644 +--- a/hw/net/virtio-net.c ++++ b/hw/net/virtio-net.c +@@ -1653,6 +1653,7 @@ static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize) + if (virtio_queue_empty(q->rx_vq) || + (n->mergeable_rx_bufs && + !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) { ++ virtio_queue_set_notification(q->rx_vq, 1); + return 0; + } + } +-- +1.8.3.1 + diff --git a/0237-target-i386-csv-Release-CSV3-shared-pages-after-unma.patch b/0237-target-i386-csv-Release-CSV3-shared-pages-after-unma.patch new file mode 100644 index 0000000000000000000000000000000000000000..f545bf8cb1ebbe1650adb52df9ff7f20bcca70f4 --- /dev/null +++ b/0237-target-i386-csv-Release-CSV3-shared-pages-after-unma.patch @@ -0,0 +1,130 @@ +From 78c9048df55f4d8791ef3c79dbf652f8b682cd12 Mon Sep 17 00:00:00 2001 +From: eastmoutain <14304864+eastmoutain@user.noreply.gitee.com> +Date: Mon, 20 May 2024 21:12:23 +0800 +Subject: [PATCH 247/293] target/i386: csv: Release CSV3 shared pages after + unmapping DMA + +The shared pages are created for Device DMA access, release them +once DMA mapping is removed. + +Signed-off-by: yangwencheng +--- + linux-headers/linux/kvm.h | 9 +++++++++ + target/i386/csv-sysemu-stub.c | 5 +++++ + target/i386/csv.c | 34 ++++++++++++++++++++++++++++++++++ + target/i386/csv.h | 1 + + target/i386/kvm/kvm.c | 1 + + 5 files changed, 50 insertions(+) + +diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h +index f67a7dd..36da75b 100644 +--- a/linux-headers/linux/kvm.h ++++ b/linux-headers/linux/kvm.h +@@ -2076,6 +2076,7 @@ enum csv3_cmd_id { + KVM_CSV3_SEND_ENCRYPT_CONTEXT, + KVM_CSV3_RECEIVE_ENCRYPT_DATA, + KVM_CSV3_RECEIVE_ENCRYPT_CONTEXT, ++ KVM_CSV3_HANDLE_MEMORY, + + KVM_CSV3_NR_MAX, + }; +@@ -2122,6 +2123,14 @@ struct kvm_csv3_receive_encrypt_context { + __u32 trans_len; + }; + ++#define KVM_CSV3_RELEASE_SHARED_MEMORY (0x0001) ++ ++struct kvm_csv3_handle_memory { ++ __u64 gpa; ++ __u32 num_pages; ++ __u32 opcode; ++}; ++ + #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) + #define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1) + #define KVM_DEV_ASSIGN_MASK_INTX (1 << 2) +diff --git a/target/i386/csv-sysemu-stub.c b/target/i386/csv-sysemu-stub.c +index db22c29..f3224a0 100644 +--- a/target/i386/csv-sysemu-stub.c ++++ b/target/i386/csv-sysemu-stub.c +@@ -39,3 +39,8 @@ void csv3_shared_region_dma_unmap(uint64_t start, uint64_t end) + { + + } ++ ++void csv3_shared_region_relese(uint64_t gpa, uint32_t num_pages) ++{ ++ ++} +diff --git a/target/i386/csv.c b/target/i386/csv.c +index 0593f9b..a869cc2 100644 +--- a/target/i386/csv.c ++++ b/target/i386/csv.c +@@ -268,6 +268,40 @@ end: + return ret; + } + ++void csv3_shared_region_release(uint64_t gpa, uint32_t num_pages) ++{ ++ struct kvm_csv3_handle_memory mem = { 0 }; ++ MemoryRegion *mr = NULL; ++ void *hva; ++ int ret; ++ ++ if (!csv3_enabled()) ++ return; ++ ++ if (!gpa || !num_pages) ++ return; ++ ++ mem.gpa = (__u64)gpa; ++ mem.num_pages = (__u32)num_pages; ++ mem.opcode = (__u32)KVM_CSV3_RELEASE_SHARED_MEMORY; ++ ++ /* unpin the pages */ ++ ret = csv3_ioctl(KVM_CSV3_HANDLE_MEMORY, &mem, NULL); ++ if (ret <= 0) { ++ if (ret < 0) ++ error_report("%s: CSV3 unpin failed ret %d", __func__, ret); ++ return; ++ } ++ ++ /* drop the pages */ ++ hva = gpa2hva(&mr, gpa, num_pages << TARGET_PAGE_BITS, NULL); ++ if (hva) { ++ ret = madvise(hva, num_pages << TARGET_PAGE_BITS, MADV_DONTNEED); ++ if (ret) ++ error_report("%s: madvise failed %d", __func__, ret); ++ } ++} ++ + void csv3_shared_region_dma_unmap(uint64_t start, uint64_t end) + { + MemoryRegionSection section; +diff --git a/target/i386/csv.h b/target/i386/csv.h +index e5e05d0..a32588a 100644 +--- a/target/i386/csv.h ++++ b/target/i386/csv.h +@@ -122,6 +122,7 @@ int csv3_load_data(uint64_t gpa, uint8_t *ptr, uint64_t len, Error **errp); + + int csv3_shared_region_dma_map(uint64_t start, uint64_t end); + void csv3_shared_region_dma_unmap(uint64_t start, uint64_t end); ++void csv3_shared_region_release(uint64_t gpa, uint32_t num_pages); + int csv3_load_incoming_page(QEMUFile *f, uint8_t *ptr); + int csv3_load_incoming_context(QEMUFile *f); + int csv3_queue_outgoing_page(uint8_t *ptr, uint32_t sz, uint64_t addr); +diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c +index 925f4f8..fdceecc 100644 +--- a/target/i386/kvm/kvm.c ++++ b/target/i386/kvm/kvm.c +@@ -5027,6 +5027,7 @@ static int kvm_handle_exit_hypercall(X86CPU *cpu, struct kvm_run *run) + if (enc) { + sev_remove_shared_regions_list(gfn_start, gfn_end); + csv3_shared_region_dma_unmap(gpa, gfn_end << TARGET_PAGE_BITS); ++ csv3_shared_region_release(gpa, npages); + } else { + sev_add_shared_regions_list(gfn_start, gfn_end); + csv3_shared_region_dma_map(gpa, gfn_end << TARGET_PAGE_BITS); +-- +1.8.3.1 + diff --git a/0238-virtio-net-Fix-network-stall-at-the-host-side-waitin.patch b/0238-virtio-net-Fix-network-stall-at-the-host-side-waitin.patch new file mode 100644 index 0000000000000000000000000000000000000000..f791ecffb370b8168a46168a709b4ea50fa23749 --- /dev/null +++ b/0238-virtio-net-Fix-network-stall-at-the-host-side-waitin.patch @@ -0,0 +1,339 @@ +From ffab93e9daccdbb7352b64a5a18346a978c302de Mon Sep 17 00:00:00 2001 +From: thomas +Date: Fri, 12 Jul 2024 11:10:53 +0800 +Subject: [PATCH 248/293] virtio-net: Fix network stall at the host side + waiting for kick + +Patch 06b12970174 ("virtio-net: fix network stall under load") +added double-check to test whether the available buffer size +can satisfy the request or not, in case the guest has added +some buffers to the avail ring simultaneously after the first +check. It will be lucky if the available buffer size becomes +okay after the double-check, then the host can send the packet +to the guest. If the buffer size still can't satisfy the request, +even if the guest has added some buffers, viritio-net would +stall at the host side forever. + +The patch enables notification and checks whether the guest has +added some buffers since last check of available buffers when +the available buffers are insufficient. If no buffer is added, +return false, else recheck the available buffers in the loop. +If the available buffers are sufficient, disable notification +and return true. + +Changes: +1. Change the return type of virtqueue_get_avail_bytes() from void + to int, it returns an opaque that represents the shadow_avail_idx + of the virtqueue on success, else -1 on error. +2. Add a new API: virtio_queue_enable_notification_and_check(), + it takes an opaque as input arg which is returned from + virtqueue_get_avail_bytes(). It enables notification firstly, + then checks whether the guest has added some buffers since + last check of available buffers or not by virtio_queue_poll(), + return ture if yes. + +The patch also reverts patch "06b12970174". +The patch also reverts patch +1052-hw-net-virtio-net-Update-event-idx-if-guest-has-made.patch. + +The case below can reproduce the stall. + + Guest 0 + +--------+ + | iperf | + ---------------> | server | + Host | +--------+ + +--------+ | ... + | iperf |---- + | client |---- Guest n + +--------+ | +--------+ + | | iperf | + ---------------> | server | + +--------+ + +Boot many guests from qemu with virtio network: + qemu ... -netdev tap,id=net_x \ + -device virtio-net-pci-non-transitional,\ + iommu_platform=on,mac=xx:xx:xx:xx:xx:xx,netdev=net_x + +Each guest acts as iperf server with commands below: + iperf3 -s -D -i 10 -p 8001 + iperf3 -s -D -i 10 -p 8002 + +The host as iperf client: + iperf3 -c guest_IP -p 8001 -i 30 -w 256k -P 20 -t 40000 + iperf3 -c guest_IP -p 8002 -i 30 -w 256k -P 20 -t 40000 + +After some time, the host loses connection to the guest, +the guest can send packet to the host, but can't receive +packet from the host. + +It's more likely to happen if SWIOTLB is enabled in the guest, +allocating and freeing bounce buffer takes some CPU ticks, +copying from/to bounce buffer takes more CPU ticks, compared +with that there is no bounce buffer in the guest. +Once the rate of producing packets from the host approximates +the rate of receiveing packets in the guest, the guest would +loop in NAPI. + + receive packets --- + | | + v | + free buf virtnet_poll + | | + v | + add buf to avail ring --- + | + | need kick the host? + | NAPI continues + v + receive packets --- + | | + v | + free buf virtnet_poll + | | + v | + add buf to avail ring --- + | + v + ... ... + +On the other hand, the host fetches free buf from avail +ring, if the buf in the avail ring is not enough, the +host notifies the guest the event by writing the avail +idx read from avail ring to the event idx of used ring, +then the host goes to sleep, waiting for the kick signal +from the guest. + +Once the guest finds the host is waiting for kick singal +(in virtqueue_kick_prepare_split()), it kicks the host. + +The host may stall forever at the sequences below: + + Host Guest + ------------ ----------- + fetch buf, send packet receive packet --- + ... ... | + fetch buf, send packet add buf | + ... add buf virtnet_poll + buf not enough avail idx-> add buf | + read avail idx add buf | + add buf --- + receive packet --- + write event idx ... | + wait for kick add buf virtnet_poll + ... | + --- + no more packet, exit NAPI + +In the first loop of NAPI above, indicated in the range of +virtnet_poll above, the host is sending packets while the +guest is receiving packets and adding buffers. + step 1: The buf is not enough, for example, a big packet + needs 5 buf, but the available buf count is 3. + The host read current avail idx. + step 2: The guest adds some buf, then checks whether the + host is waiting for kick signal, not at this time. + The used ring is not empty, the guest continues + the second loop of NAPI. + step 3: The host writes the avail idx read from avail + ring to used ring as event idx via + virtio_queue_set_notification(q->rx_vq, 1). + step 4: At the end of the second loop of NAPI, recheck + whether kick is needed, as the event idx in the + used ring written by the host is beyound the + range of kick condition, the guest will not + send kick signal to the host. + +Fixes: 06b12970174 ("virtio-net: fix network stall under load") +Cc: qemu-stable@nongnu.org +Signed-off-by: Wencheng Yang +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Jason Wang +--- + hw/net/virtio-net.c | 29 +++++++++++---------- + hw/virtio/virtio.c | 64 +++++++++++++++++++++++++++++++++++++++++++--- + include/hw/virtio/virtio.h | 18 ++++++++++--- + 3 files changed, 91 insertions(+), 20 deletions(-) + +diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c +index 5b0d814..b6574f9 100644 +--- a/hw/net/virtio-net.c ++++ b/hw/net/virtio-net.c +@@ -1640,25 +1640,28 @@ static bool virtio_net_can_receive(NetClientState *nc) + + static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize) + { ++ int opaque; ++ unsigned int in_bytes; + VirtIONet *n = q->n; +- if (virtio_queue_empty(q->rx_vq) || +- (n->mergeable_rx_bufs && +- !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) { +- virtio_queue_set_notification(q->rx_vq, 1); +- +- /* To avoid a race condition where the guest has made some buffers +- * available after the above check but before notification was +- * enabled, check for available buffers again. +- */ +- if (virtio_queue_empty(q->rx_vq) || +- (n->mergeable_rx_bufs && +- !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) { +- virtio_queue_set_notification(q->rx_vq, 1); ++ ++ while (virtio_queue_empty(q->rx_vq) || n->mergeable_rx_bufs) { ++ opaque = virtqueue_get_avail_bytes(q->rx_vq, &in_bytes, NULL, ++ bufsize, 0); ++ /* Buffer is enough, disable notifiaction */ ++ if (bufsize <= in_bytes) { ++ break; ++ } ++ ++ if (virtio_queue_enable_notification_and_check(q->rx_vq, opaque)) { ++ /* Guest has added some buffers, try again */ ++ continue; ++ } else { + return 0; + } + } + + virtio_queue_set_notification(q->rx_vq, 0); ++ + return 1; + } + +diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c +index 356d690..09e2de6 100644 +--- a/hw/virtio/virtio.c ++++ b/hw/virtio/virtio.c +@@ -744,6 +744,60 @@ int virtio_queue_empty(VirtQueue *vq) + } + } + ++static bool virtio_queue_split_poll(VirtQueue *vq, unsigned shadow_idx) ++{ ++ if (unlikely(!vq->vring.avail)) { ++ return false; ++ } ++ ++ return (uint16_t)shadow_idx != vring_avail_idx(vq); ++} ++ ++static bool virtio_queue_packed_poll(VirtQueue *vq, unsigned shadow_idx) ++{ ++ VRingPackedDesc desc; ++ VRingMemoryRegionCaches *caches; ++ ++ if (unlikely(!vq->vring.desc)) { ++ return false; ++ } ++ ++ caches = vring_get_region_caches(vq); ++ if (!caches) { ++ return false; ++ } ++ ++ vring_packed_desc_read(vq->vdev, &desc, &caches->desc, ++ shadow_idx, true); ++ ++ return is_desc_avail(desc.flags, vq->shadow_avail_wrap_counter); ++} ++ ++static bool virtio_queue_poll(VirtQueue *vq, unsigned shadow_idx) ++{ ++ if (virtio_device_disabled(vq->vdev)) { ++ return false; ++ } ++ ++ if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) { ++ return virtio_queue_packed_poll(vq, shadow_idx); ++ } else { ++ return virtio_queue_split_poll(vq, shadow_idx); ++ } ++} ++ ++bool virtio_queue_enable_notification_and_check(VirtQueue *vq, ++ int opaque) ++{ ++ virtio_queue_set_notification(vq, 1); ++ ++ if (opaque >= 0) { ++ return virtio_queue_poll(vq, (unsigned)opaque); ++ } else { ++ return false; ++ } ++} ++ + static void virtqueue_unmap_sg(VirtQueue *vq, const VirtQueueElement *elem, + unsigned int len) + { +@@ -1323,9 +1377,9 @@ err: + goto done; + } + +-void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, +- unsigned int *out_bytes, +- unsigned max_in_bytes, unsigned max_out_bytes) ++int virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, ++ unsigned int *out_bytes, unsigned max_in_bytes, ++ unsigned max_out_bytes) + { + uint16_t desc_size; + VRingMemoryRegionCaches *caches; +@@ -1358,7 +1412,7 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, + caches); + } + +- return; ++ return (int)vq->shadow_avail_idx; + err: + if (in_bytes) { + *in_bytes = 0; +@@ -1366,6 +1420,8 @@ err: + if (out_bytes) { + *out_bytes = 0; + } ++ ++ return -1; + } + + int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes, +diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h +index c8f7285..d2f4ed1 100644 +--- a/include/hw/virtio/virtio.h ++++ b/include/hw/virtio/virtio.h +@@ -270,9 +270,13 @@ void qemu_put_virtqueue_element(VirtIODevice *vdev, QEMUFile *f, + VirtQueueElement *elem); + int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes, + unsigned int out_bytes); +-void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, +- unsigned int *out_bytes, +- unsigned max_in_bytes, unsigned max_out_bytes); ++/** ++ * Return <0 on error or an opaque >=0 to pass to ++ * virtio_queue_enable_notification_and_check on success. ++ */ ++int virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, ++ unsigned int *out_bytes, unsigned max_in_bytes, ++ unsigned max_out_bytes); + + void virtio_notify_irqfd(VirtIODevice *vdev, VirtQueue *vq); + void virtio_notify(VirtIODevice *vdev, VirtQueue *vq); +@@ -306,6 +310,14 @@ int virtio_queue_ready(VirtQueue *vq); + + int virtio_queue_empty(VirtQueue *vq); + ++/** ++ * Enable notification and check whether guest has added some ++ * buffers since last call to virtqueue_get_avail_bytes. ++ * ++ * @opaque: value returned from virtqueue_get_avail_bytes ++ */ ++bool virtio_queue_enable_notification_and_check(VirtQueue *vq, ++ int opaque); + /* Host binding interface. */ + + uint32_t virtio_config_readb(VirtIODevice *vdev, uint32_t addr); +-- +1.8.3.1 + diff --git a/0239-devel-8.2-Hygon-HCT-Fix-for-vfio-based-mediated-hct.patch b/0239-devel-8.2-Hygon-HCT-Fix-for-vfio-based-mediated-hct.patch new file mode 100644 index 0000000000000000000000000000000000000000..468a414b38a64cb220d76d19fc3b1bfe68879dde --- /dev/null +++ b/0239-devel-8.2-Hygon-HCT-Fix-for-vfio-based-mediated-hct.patch @@ -0,0 +1,28 @@ +From 7718d53612105ea2325a751af111d2136f0f37cf Mon Sep 17 00:00:00 2001 +From: Xingrui Yi +Date: Tue, 24 Sep 2024 14:11:51 +0800 +Subject: [PATCH 249/293] [devel-8.2][Hygon][HCT] Fix for vfio based mediated + hct + +Fix for vfio based mediated hct in index_value + +Signed-off-by: Mingkai Xu +--- + hw/vfio/hct.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/hw/vfio/hct.c b/hw/vfio/hct.c +index fb42927..2f40bf5 100644 +--- a/hw/vfio/hct.c ++++ b/hw/vfio/hct.c +@@ -248,6 +248,7 @@ static int hct_get_ccp_index(HCTDevState *state) + return -errno; + } + ++ memset(buf, 0, sizeof(buf)); + ret = read(fd, buf, sizeof(buf)); + if (ret < 0) { + ret = -errno; +-- +1.8.3.1 + diff --git a/0240-update-to-qemu-9.1-with-directory-hw-loongarch.patch b/0240-update-to-qemu-9.1-with-directory-hw-loongarch.patch new file mode 100644 index 0000000000000000000000000000000000000000..018f19e008cf5664c3542e5c5cf6696ea48ba374 --- /dev/null +++ b/0240-update-to-qemu-9.1-with-directory-hw-loongarch.patch @@ -0,0 +1,2651 @@ +From bf153c2e1b3d171127307d2decd006fc5a5af230 Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Thu, 19 Sep 2024 20:39:18 +0800 +Subject: [PATCH 250/293] update to qemu 9.1 with directory hw/loongarch + +Synchronize the upstream patch related to qemu 9.1 loongarch, +Since only loongarch related codes are updated, +not all codes can not be split into independent patches. +Here, loongarch related patches in the upstream patch are split +and merged into a large patch. + +Signed-off-by: Xianglai Li +--- + hw/loongarch/Kconfig | 2 +- + hw/loongarch/acpi-build.c | 178 ++++--- + hw/loongarch/boot.c | 339 +++++++++++++ + hw/loongarch/fw_cfg.c | 2 +- + hw/loongarch/fw_cfg.h | 2 +- + hw/loongarch/meson.build | 1 + + hw/loongarch/virt.c | 979 +++++++++++++++++++++---------------- + include/hw/intc/loongarch_extioi.h | 22 +- + include/hw/loongarch/boot.h | 119 +++++ + include/hw/loongarch/virt.h | 20 +- + include/hw/pci-host/ls7a.h | 11 +- + target/loongarch/cpu.h | 1 + + 12 files changed, 1154 insertions(+), 522 deletions(-) + create mode 100644 hw/loongarch/boot.c + create mode 100644 include/hw/loongarch/boot.h + +diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig +index 5727efe..9fa8f82 100644 +--- a/hw/loongarch/Kconfig ++++ b/hw/loongarch/Kconfig +@@ -2,9 +2,9 @@ config LOONGARCH_VIRT + bool + select PCI + select PCI_EXPRESS_GENERIC_BRIDGE +- imply VIRTIO_VGA + imply PCI_DEVICES + imply NVDIMM ++ imply TPM_TIS_SYSBUS + select SERIAL + select VIRTIO_PCI + select PLATFORM_BUS +diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c +index f990405..a41e4c2 100644 +--- a/hw/loongarch/acpi-build.c ++++ b/hw/loongarch/acpi-build.c +@@ -31,6 +31,7 @@ + + #include "hw/acpi/generic_event_device.h" + #include "hw/pci-host/gpex.h" ++#include "sysemu/sysemu.h" + #include "sysemu/tpm.h" + #include "hw/platform-bus.h" + #include "hw/acpi/aml-build.h" +@@ -105,14 +106,15 @@ build_facs(GArray *table_data) + + /* build MADT */ + static void +-build_madt(GArray *table_data, BIOSLinker *linker, LoongArchMachineState *lams) ++build_madt(GArray *table_data, BIOSLinker *linker, ++ LoongArchVirtMachineState *lvms) + { +- MachineState *ms = MACHINE(lams); ++ MachineState *ms = MACHINE(lvms); + MachineClass *mc = MACHINE_GET_CLASS(ms); + const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms); + int i, arch_id; +- AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lams->oem_id, +- .oem_table_id = lams->oem_table_id }; ++ AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lvms->oem_id, ++ .oem_table_id = lvms->oem_table_id }; + + acpi_table_begin(&table, table_data); + +@@ -165,13 +167,14 @@ static void + build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) + { + int i, arch_id, node_id; +- uint64_t mem_len, mem_base; +- int nb_numa_nodes = machine->numa_state->num_nodes; +- LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); +- MachineClass *mc = MACHINE_GET_CLASS(lams); ++ hwaddr len, base, gap; ++ NodeInfo *numa_info; ++ int nodes, nb_numa_nodes = machine->numa_state->num_nodes; ++ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); ++ MachineClass *mc = MACHINE_GET_CLASS(lvms); + const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(machine); +- AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = lams->oem_id, +- .oem_table_id = lams->oem_table_id }; ++ AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = lvms->oem_id, ++ .oem_table_id = lvms->oem_table_id }; + + acpi_table_begin(&table, table_data); + build_append_int_noprefix(table_data, 1, 4); /* Reserved */ +@@ -195,35 +198,44 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) + build_append_int_noprefix(table_data, 0, 4); /* Reserved */ + } + +- /* Node0 */ +- build_srat_memory(table_data, VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, +- 0, MEM_AFFINITY_ENABLED); +- mem_base = VIRT_HIGHMEM_BASE; +- if (!nb_numa_nodes) { +- mem_len = machine->ram_size - VIRT_LOWMEM_SIZE; +- } else { +- mem_len = machine->numa_state->nodes[0].node_mem - VIRT_LOWMEM_SIZE; ++ base = VIRT_LOWMEM_BASE; ++ gap = VIRT_LOWMEM_SIZE; ++ numa_info = machine->numa_state->nodes; ++ nodes = nb_numa_nodes; ++ if (!nodes) { ++ nodes = 1; + } +- if (mem_len) +- build_srat_memory(table_data, mem_base, mem_len, 0, MEM_AFFINITY_ENABLED); +- +- /* Node1 - Nodemax */ +- if (nb_numa_nodes) { +- mem_base += mem_len; +- for (i = 1; i < nb_numa_nodes; ++i) { +- if (machine->numa_state->nodes[i].node_mem > 0) { +- build_srat_memory(table_data, mem_base, +- machine->numa_state->nodes[i].node_mem, i, +- MEM_AFFINITY_ENABLED); +- mem_base += machine->numa_state->nodes[i].node_mem; +- } ++ ++ for (i = 0; i < nodes; i++) { ++ if (nb_numa_nodes) { ++ len = numa_info[i].node_mem; ++ } else { ++ len = machine->ram_size; ++ } ++ ++ /* ++ * memory for the node splited into two part ++ * lowram: [base, +gap) ++ * highram: [VIRT_HIGHMEM_BASE, +(len - gap)) ++ */ ++ if (len >= gap) { ++ build_srat_memory(table_data, base, gap, i, MEM_AFFINITY_ENABLED); ++ len -= gap; ++ base = VIRT_HIGHMEM_BASE; ++ gap = machine->ram_size - VIRT_LOWMEM_SIZE; ++ } ++ ++ if (len) { ++ build_srat_memory(table_data, base, len, i, MEM_AFFINITY_ENABLED); ++ base += len; ++ gap -= len; + } + } + + if (machine->device_memory) { + build_srat_memory(table_data, machine->device_memory->base, + memory_region_size(&machine->device_memory->mr), +- nb_numa_nodes - 1, ++ nodes - 1, + MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); + } + +@@ -241,23 +253,27 @@ struct AcpiBuildState { + MemoryRegion *linker_mr; + } AcpiBuildState; + +-static void build_uart_device_aml(Aml *table) ++static void build_uart_device_aml(Aml *table, int index) + { + Aml *dev; + Aml *crs; + Aml *pkg0, *pkg1, *pkg2; +- uint32_t uart_irq = VIRT_UART_IRQ; +- +- Aml *scope = aml_scope("_SB"); +- dev = aml_device("COMA"); ++ Aml *scope; ++ uint32_t uart_irq; ++ uint64_t base; ++ ++ uart_irq = VIRT_UART_IRQ + index; ++ base = VIRT_UART_BASE + index * VIRT_UART_SIZE; ++ scope = aml_scope("_SB"); ++ dev = aml_device("COM%d", index); + aml_append(dev, aml_name_decl("_HID", aml_string("PNP0501"))); +- aml_append(dev, aml_name_decl("_UID", aml_int(0))); ++ aml_append(dev, aml_name_decl("_UID", aml_int(index))); + aml_append(dev, aml_name_decl("_CCA", aml_int(1))); + crs = aml_resource_template(); + aml_append(crs, + aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, + AML_NON_CACHEABLE, AML_READ_WRITE, +- 0, VIRT_UART_BASE, VIRT_UART_BASE + VIRT_UART_SIZE - 1, ++ 0, base, base + VIRT_UART_SIZE - 1, + 0, VIRT_UART_SIZE)); + aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, + AML_SHARED, &uart_irq, 1)); +@@ -279,13 +295,13 @@ static void + build_la_ged_aml(Aml *dsdt, MachineState *machine) + { + uint32_t event; +- LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); ++ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); + + build_ged_aml(dsdt, "\\_SB."GED_DEVICE, +- HOTPLUG_HANDLER(lams->acpi_ged), ++ HOTPLUG_HANDLER(lvms->acpi_ged), + VIRT_SCI_IRQ, AML_SYSTEM_MEMORY, + VIRT_GED_EVT_ADDR); +- event = object_property_get_uint(OBJECT(lams->acpi_ged), ++ event = object_property_get_uint(OBJECT(lvms->acpi_ged), + "ged-event", &error_abort); + if (event & ACPI_GED_MEM_HOTPLUG_EVT) { + build_memory_hotplug_aml(dsdt, machine->ram_slots, "\\_SB", NULL, +@@ -295,7 +311,7 @@ build_la_ged_aml(Aml *dsdt, MachineState *machine) + acpi_dsdt_add_power_button(dsdt); + } + +-static void build_pci_device_aml(Aml *scope, LoongArchMachineState *lams) ++static void build_pci_device_aml(Aml *scope, LoongArchVirtMachineState *lvms) + { + struct GPEXConfig cfg = { + .mmio64.base = VIRT_PCI_MEM_BASE, +@@ -305,13 +321,13 @@ static void build_pci_device_aml(Aml *scope, LoongArchMachineState *lams) + .ecam.base = VIRT_PCI_CFG_BASE, + .ecam.size = VIRT_PCI_CFG_SIZE, + .irq = VIRT_GSI_BASE + VIRT_DEVICE_IRQS, +- .bus = lams->pci_bus, ++ .bus = lvms->pci_bus, + }; + + acpi_dsdt_add_gpex(scope, &cfg); + } + +-static void build_flash_aml(Aml *scope, LoongArchMachineState *lams) ++static void build_flash_aml(Aml *scope, LoongArchVirtMachineState *lvms) + { + Aml *dev, *crs; + MemoryRegion *flash_mem; +@@ -322,11 +338,11 @@ static void build_flash_aml(Aml *scope, LoongArchMachineState *lams) + hwaddr flash1_base; + hwaddr flash1_size; + +- flash_mem = pflash_cfi01_get_memory(lams->flash[0]); ++ flash_mem = pflash_cfi01_get_memory(lvms->flash[0]); + flash0_base = flash_mem->addr; + flash0_size = memory_region_size(flash_mem); + +- flash_mem = pflash_cfi01_get_memory(lams->flash[1]); ++ flash_mem = pflash_cfi01_get_memory(lvms->flash[1]); + flash1_base = flash_mem->addr; + flash1_size = memory_region_size(flash_mem); + +@@ -352,7 +368,7 @@ static void build_flash_aml(Aml *scope, LoongArchMachineState *lams) + } + + #ifdef CONFIG_TPM +-static void acpi_dsdt_add_tpm(Aml *scope, LoongArchMachineState *vms) ++static void acpi_dsdt_add_tpm(Aml *scope, LoongArchVirtMachineState *vms) + { + PlatformBusDevice *pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev); + hwaddr pbus_base = VIRT_PLATFORM_BUS_BASEADDRESS; +@@ -390,19 +406,21 @@ static void acpi_dsdt_add_tpm(Aml *scope, LoongArchMachineState *vms) + static void + build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine) + { ++ int i; + Aml *dsdt, *scope, *pkg; +- LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); +- AcpiTable table = { .sig = "DSDT", .rev = 1, .oem_id = lams->oem_id, +- .oem_table_id = lams->oem_table_id }; ++ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); ++ AcpiTable table = { .sig = "DSDT", .rev = 1, .oem_id = lvms->oem_id, ++ .oem_table_id = lvms->oem_table_id }; + + acpi_table_begin(&table, table_data); + dsdt = init_aml_allocator(); +- build_uart_device_aml(dsdt); +- build_pci_device_aml(dsdt, lams); ++ for (i = 0; i < VIRT_UART_COUNT; i++) ++ build_uart_device_aml(dsdt, i); ++ build_pci_device_aml(dsdt, lvms); + build_la_ged_aml(dsdt, machine); +- build_flash_aml(dsdt, lams); ++ build_flash_aml(dsdt, lvms); + #ifdef CONFIG_TPM +- acpi_dsdt_add_tpm(dsdt, lams); ++ acpi_dsdt_add_tpm(dsdt, lvms); + #endif + /* System State Package */ + scope = aml_scope("\\"); +@@ -421,7 +439,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine) + + static void acpi_build(AcpiBuildTables *tables, MachineState *machine) + { +- LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); ++ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); + GArray *table_offsets; + AcpiFadtData fadt_data; + unsigned facs, rsdt, dsdt; +@@ -455,28 +473,29 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) + fadt_data.dsdt_tbl_offset = &dsdt; + fadt_data.xdsdt_tbl_offset = &dsdt; + build_fadt(tables_blob, tables->linker, &fadt_data, +- lams->oem_id, lams->oem_table_id); ++ lvms->oem_id, lvms->oem_table_id); + + acpi_add_table(table_offsets, tables_blob); +- build_madt(tables_blob, tables->linker, lams); ++ build_madt(tables_blob, tables->linker, lvms); + + acpi_add_table(table_offsets, tables_blob); + build_pptt(tables_blob, tables->linker, machine, +- lams->oem_id, lams->oem_table_id); ++ lvms->oem_id, lvms->oem_table_id); + + acpi_add_table(table_offsets, tables_blob); + build_srat(tables_blob, tables->linker, machine); ++ acpi_add_table(table_offsets, tables_blob); + + if (machine->numa_state->num_nodes) { + if (machine->numa_state->have_numa_distance) { + acpi_add_table(table_offsets, tables_blob); +- build_slit(tables_blob, tables->linker, machine, lams->oem_id, +- lams->oem_table_id); ++ build_slit(tables_blob, tables->linker, machine, lvms->oem_id, ++ lvms->oem_table_id); + } + if (machine->numa_state->hmat_enabled) { + acpi_add_table(table_offsets, tables_blob); + build_hmat(tables_blob, tables->linker, machine->numa_state, +- lams->oem_id, lams->oem_table_id); ++ lvms->oem_id, lvms->oem_table_id); + } + } + +@@ -486,8 +505,8 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) + .base = cpu_to_le64(VIRT_PCI_CFG_BASE), + .size = cpu_to_le64(VIRT_PCI_CFG_SIZE), + }; +- build_mcfg(tables_blob, tables->linker, &mcfg, lams->oem_id, +- lams->oem_table_id); ++ build_mcfg(tables_blob, tables->linker, &mcfg, lvms->oem_id, ++ lvms->oem_table_id); + } + + #ifdef CONFIG_TPM +@@ -495,8 +514,8 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) + if (tpm_get_version(tpm_find()) == TPM_VERSION_2_0) { + acpi_add_table(table_offsets, tables_blob); + build_tpm2(tables_blob, tables->linker, +- tables->tcpalog, lams->oem_id, +- lams->oem_table_id); ++ tables->tcpalog, lvms->oem_id, ++ lvms->oem_table_id); + } + #endif + /* Add tables supplied by user (if any) */ +@@ -510,13 +529,13 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) + /* RSDT is pointed to by RSDP */ + rsdt = tables_blob->len; + build_rsdt(tables_blob, tables->linker, table_offsets, +- lams->oem_id, lams->oem_table_id); ++ lvms->oem_id, lvms->oem_table_id); + + /* RSDP is in FSEG memory, so allocate it separately */ + { + AcpiRsdpData rsdp_data = { + .revision = 0, +- .oem_id = lams->oem_id, ++ .oem_id = lvms->oem_id, + .xsdt_tbl_offset = NULL, + .rsdt_tbl_offset = &rsdt, + }; +@@ -532,7 +551,7 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) + " migration may not work", + tables_blob->len, ACPI_BUILD_TABLE_SIZE / 2); + error_printf("Try removing CPUs, NUMA nodes, memory slots" +- " or PCI bridges."); ++ " or PCI bridges.\n"); + } + + acpi_align_size(tables->linker->cmd_blob, ACPI_BUILD_ALIGN_SIZE); +@@ -587,23 +606,31 @@ static const VMStateDescription vmstate_acpi_build = { + .name = "acpi_build", + .version_id = 1, + .minimum_version_id = 1, +- .fields = (VMStateField[]) { ++ .fields = (const VMStateField[]) { + VMSTATE_UINT8(patched, AcpiBuildState), + VMSTATE_END_OF_LIST() + }, + }; + +-void loongarch_acpi_setup(LoongArchMachineState *lams) ++static bool loongarch_is_acpi_enabled(LoongArchVirtMachineState *lvms) ++{ ++ if (lvms->acpi == ON_OFF_AUTO_OFF) { ++ return false; ++ } ++ return true; ++} ++ ++void loongarch_acpi_setup(LoongArchVirtMachineState *lvms) + { + AcpiBuildTables tables; + AcpiBuildState *build_state; + +- if (!lams->fw_cfg) { ++ if (!lvms->fw_cfg) { + ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n"); + return; + } + +- if (!loongarch_is_acpi_enabled(lams)) { ++ if (!loongarch_is_acpi_enabled(lvms)) { + ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n"); + return; + } +@@ -611,7 +638,7 @@ void loongarch_acpi_setup(LoongArchMachineState *lams) + build_state = g_malloc0(sizeof *build_state); + + acpi_build_tables_init(&tables); +- acpi_build(&tables, MACHINE(lams)); ++ acpi_build(&tables, MACHINE(lvms)); + + /* Now expose it all to Guest */ + build_state->table_mr = acpi_add_rom_blob(acpi_build_update, +@@ -627,6 +654,9 @@ void loongarch_acpi_setup(LoongArchMachineState *lams) + build_state, tables.rsdp, + ACPI_BUILD_RSDP_FILE); + ++ fw_cfg_add_file(lvms->fw_cfg, ACPI_BUILD_TPMLOG_FILE, tables.tcpalog->data, ++ acpi_data_len(tables.tcpalog)); ++ + qemu_register_reset(acpi_build_reset, build_state); + acpi_build_reset(build_state); + vmstate_register(NULL, 0, &vmstate_acpi_build, build_state); +diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c +new file mode 100644 +index 0000000..cb66870 +--- /dev/null ++++ b/hw/loongarch/boot.c +@@ -0,0 +1,339 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * LoongArch boot helper functions. ++ * ++ * Copyright (c) 2023 Loongson Technology Corporation Limited ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/units.h" ++#include "target/loongarch/cpu.h" ++#include "hw/loongarch/virt.h" ++#include "hw/loader.h" ++#include "elf.h" ++#include "qemu/error-report.h" ++#include "sysemu/reset.h" ++#include "sysemu/qtest.h" ++ ++struct memmap_entry *memmap_table; ++unsigned memmap_entries; ++ ++ram_addr_t initrd_offset; ++uint64_t initrd_size; ++ ++static const unsigned int slave_boot_code[] = { ++ /* Configure reset ebase. */ ++ 0x0400302c, /* csrwr $t0, LOONGARCH_CSR_EENTRY */ ++ ++ /* Disable interrupt. */ ++ 0x0380100c, /* ori $t0, $zero,0x4 */ ++ 0x04000180, /* csrxchg $zero, $t0, LOONGARCH_CSR_CRMD */ ++ ++ /* Clear mailbox. */ ++ 0x1400002d, /* lu12i.w $t1, 1(0x1) */ ++ 0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */ ++ 0x06481da0, /* iocsrwr.d $zero, $t1 */ ++ ++ /* Enable IPI interrupt. */ ++ 0x1400002c, /* lu12i.w $t0, 1(0x1) */ ++ 0x0400118c, /* csrxchg $t0, $t0, LOONGARCH_CSR_ECFG */ ++ 0x02fffc0c, /* addi.d $t0, $r0,-1(0xfff) */ ++ 0x1400002d, /* lu12i.w $t1, 1(0x1) */ ++ 0x038011ad, /* ori $t1, $t1, CORE_EN_OFF */ ++ 0x064819ac, /* iocsrwr.w $t0, $t1 */ ++ 0x1400002d, /* lu12i.w $t1, 1(0x1) */ ++ 0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */ ++ ++ /* Wait for wakeup <.L11>: */ ++ 0x06488000, /* idle 0x0 */ ++ 0x03400000, /* andi $zero, $zero, 0x0 */ ++ 0x064809ac, /* iocsrrd.w $t0, $t1 */ ++ 0x43fff59f, /* beqz $t0, -12(0x7ffff4) # 48 <.L11> */ ++ ++ /* Read and clear IPI interrupt. */ ++ 0x1400002d, /* lu12i.w $t1, 1(0x1) */ ++ 0x064809ac, /* iocsrrd.w $t0, $t1 */ ++ 0x1400002d, /* lu12i.w $t1, 1(0x1) */ ++ 0x038031ad, /* ori $t1, $t1, CORE_CLEAR_OFF */ ++ 0x064819ac, /* iocsrwr.w $t0, $t1 */ ++ ++ /* Disable IPI interrupt. */ ++ 0x1400002c, /* lu12i.w $t0, 1(0x1) */ ++ 0x04001180, /* csrxchg $zero, $t0, LOONGARCH_CSR_ECFG */ ++ ++ /* Read mail buf and jump to specified entry */ ++ 0x1400002d, /* lu12i.w $t1, 1(0x1) */ ++ 0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */ ++ 0x06480dac, /* iocsrrd.d $t0, $t1 */ ++ 0x00150181, /* move $ra, $t0 */ ++ 0x4c000020, /* jirl $zero, $ra,0 */ ++}; ++ ++static inline void *guidcpy(void *dst, const void *src) ++{ ++ return memcpy(dst, src, sizeof(efi_guid_t)); ++} ++ ++static void init_efi_boot_memmap(struct efi_system_table *systab, ++ void *p, void *start) ++{ ++ unsigned i; ++ struct efi_boot_memmap *boot_memmap = p; ++ efi_guid_t tbl_guid = LINUX_EFI_BOOT_MEMMAP_GUID; ++ ++ /* efi_configuration_table 1 */ ++ guidcpy(&systab->tables[0].guid, &tbl_guid); ++ systab->tables[0].table = (struct efi_configuration_table *)(p - start); ++ systab->nr_tables = 1; ++ ++ boot_memmap->desc_size = sizeof(efi_memory_desc_t); ++ boot_memmap->desc_ver = 1; ++ boot_memmap->map_size = 0; ++ ++ efi_memory_desc_t *map = p + sizeof(struct efi_boot_memmap); ++ for (i = 0; i < memmap_entries; i++) { ++ map = (void *)boot_memmap + sizeof(*map); ++ map[i].type = memmap_table[i].type; ++ map[i].phys_addr = ROUND_UP(memmap_table[i].address, 64 * KiB); ++ map[i].num_pages = ROUND_DOWN(memmap_table[i].address + ++ memmap_table[i].length - map[i].phys_addr, 64 * KiB); ++ p += sizeof(efi_memory_desc_t); ++ } ++} ++ ++static void init_efi_initrd_table(struct efi_system_table *systab, ++ void *p, void *start) ++{ ++ efi_guid_t tbl_guid = LINUX_EFI_INITRD_MEDIA_GUID; ++ struct efi_initrd *initrd_table = p; ++ ++ /* efi_configuration_table 2 */ ++ guidcpy(&systab->tables[1].guid, &tbl_guid); ++ systab->tables[1].table = (struct efi_configuration_table *)(p - start); ++ systab->nr_tables = 2; ++ ++ initrd_table->base = initrd_offset; ++ initrd_table->size = initrd_size; ++} ++ ++static void init_efi_fdt_table(struct efi_system_table *systab) ++{ ++ efi_guid_t tbl_guid = DEVICE_TREE_GUID; ++ ++ /* efi_configuration_table 3 */ ++ guidcpy(&systab->tables[2].guid, &tbl_guid); ++ systab->tables[2].table = (void *)FDT_BASE; ++ systab->nr_tables = 3; ++} ++ ++static void init_systab(struct loongarch_boot_info *info, void *p, void *start) ++{ ++ void *bp_tables_start; ++ struct efi_system_table *systab = p; ++ ++ info->a2 = p - start; ++ ++ systab->hdr.signature = EFI_SYSTEM_TABLE_SIGNATURE; ++ systab->hdr.revision = EFI_SPECIFICATION_VERSION; ++ systab->hdr.revision = sizeof(struct efi_system_table), ++ systab->fw_revision = FW_VERSION << 16 | FW_PATCHLEVEL << 8; ++ systab->runtime = 0; ++ systab->boottime = 0; ++ systab->nr_tables = 0; ++ ++ p += ROUND_UP(sizeof(struct efi_system_table), 64 * KiB); ++ ++ systab->tables = p; ++ bp_tables_start = p; ++ ++ init_efi_boot_memmap(systab, p, start); ++ p += ROUND_UP(sizeof(struct efi_boot_memmap) + ++ sizeof(efi_memory_desc_t) * memmap_entries, 64 * KiB); ++ init_efi_initrd_table(systab, p, start); ++ p += ROUND_UP(sizeof(struct efi_initrd), 64 * KiB); ++ init_efi_fdt_table(systab); ++ ++ systab->tables = (struct efi_configuration_table *)(bp_tables_start - start); ++} ++ ++static void init_cmdline(struct loongarch_boot_info *info, void *p, void *start) ++{ ++ hwaddr cmdline_addr = p - start; ++ ++ info->a0 = 1; ++ info->a1 = cmdline_addr; ++ ++ g_strlcpy(p, info->kernel_cmdline, COMMAND_LINE_SIZE); ++} ++ ++static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr) ++{ ++ return addr & MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS); ++} ++ ++static int64_t load_kernel_info(struct loongarch_boot_info *info) ++{ ++ uint64_t kernel_entry, kernel_low, kernel_high; ++ ssize_t kernel_size; ++ ++ kernel_size = load_elf(info->kernel_filename, NULL, ++ cpu_loongarch_virt_to_phys, NULL, ++ &kernel_entry, &kernel_low, ++ &kernel_high, NULL, 0, ++ EM_LOONGARCH, 1, 0); ++ ++ if (kernel_size < 0) { ++ error_report("could not load kernel '%s': %s", ++ info->kernel_filename, ++ load_elf_strerror(kernel_size)); ++ exit(1); ++ } ++ ++ if (info->initrd_filename) { ++ initrd_size = get_image_size(info->initrd_filename); ++ if (initrd_size > 0) { ++ initrd_offset = ROUND_UP(kernel_high + 4 * kernel_size, 64 * KiB); ++ ++ if (initrd_offset + initrd_size > info->ram_size) { ++ error_report("memory too small for initial ram disk '%s'", ++ info->initrd_filename); ++ exit(1); ++ } ++ ++ initrd_size = load_image_targphys(info->initrd_filename, initrd_offset, ++ info->ram_size - initrd_offset); ++ } ++ ++ if (initrd_size == (target_ulong)-1) { ++ error_report("could not load initial ram disk '%s'", ++ info->initrd_filename); ++ exit(1); ++ } ++ } else { ++ initrd_size = 0; ++ } ++ ++ return kernel_entry; ++} ++ ++static void reset_load_elf(void *opaque) ++{ ++ LoongArchCPU *cpu = opaque; ++ CPULoongArchState *env = &cpu->env; ++ ++ cpu_reset(CPU(cpu)); ++ if (env->load_elf) { ++ if (cpu == LOONGARCH_CPU(first_cpu)) { ++ env->gpr[4] = env->boot_info->a0; ++ env->gpr[5] = env->boot_info->a1; ++ env->gpr[6] = env->boot_info->a2; ++ } ++ cpu_set_pc(CPU(cpu), env->elf_address); ++ } ++} ++ ++static void fw_cfg_add_kernel_info(struct loongarch_boot_info *info, ++ FWCfgState *fw_cfg) ++{ ++ /* ++ * Expose the kernel, the command line, and the initrd in fw_cfg. ++ * We don't process them here at all, it's all left to the ++ * firmware. ++ */ ++ load_image_to_fw_cfg(fw_cfg, ++ FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA, ++ info->kernel_filename, ++ false); ++ ++ if (info->initrd_filename) { ++ load_image_to_fw_cfg(fw_cfg, ++ FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA, ++ info->initrd_filename, false); ++ } ++ ++ if (info->kernel_cmdline) { ++ fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, ++ strlen(info->kernel_cmdline) + 1); ++ fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, ++ info->kernel_cmdline); ++ } ++} ++ ++static void loongarch_firmware_boot(LoongArchVirtMachineState *lvms, ++ struct loongarch_boot_info *info) ++{ ++ fw_cfg_add_kernel_info(info, lvms->fw_cfg); ++} ++ ++static void init_boot_rom(struct loongarch_boot_info *info, void *p) ++{ ++ void *start = p; ++ ++ init_cmdline(info, p, start); ++ p += COMMAND_LINE_SIZE; ++ ++ init_systab(info, p, start); ++} ++ ++static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info) ++{ ++ void *p, *bp; ++ int64_t kernel_addr = 0; ++ LoongArchCPU *lacpu; ++ CPUState *cs; ++ ++ if (info->kernel_filename) { ++ kernel_addr = load_kernel_info(info); ++ } else { ++ if(!qtest_enabled()) { ++ error_report("Need kernel filename\n"); ++ exit(1); ++ } ++ } ++ ++ /* Load cmdline and system tables at [0 - 1 MiB] */ ++ p = g_malloc0(1 * MiB); ++ bp = p; ++ init_boot_rom(info, p); ++ rom_add_blob_fixed_as("boot_info", bp, 1 * MiB, 0, &address_space_memory); ++ ++ /* Load slave boot code at pflash0 . */ ++ void *boot_code = g_malloc0(VIRT_FLASH0_SIZE); ++ memcpy(boot_code, &slave_boot_code, sizeof(slave_boot_code)); ++ rom_add_blob_fixed("boot_code", boot_code, VIRT_FLASH0_SIZE, VIRT_FLASH0_BASE); ++ ++ CPU_FOREACH(cs) { ++ lacpu = LOONGARCH_CPU(cs); ++ lacpu->env.load_elf = true; ++ if (cs == first_cpu) { ++ lacpu->env.elf_address = kernel_addr; ++ } else { ++ lacpu->env.elf_address = VIRT_FLASH0_BASE; ++ } ++ lacpu->env.boot_info = info; ++ } ++ ++ g_free(boot_code); ++ g_free(bp); ++} ++ ++void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info) ++{ ++ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(ms); ++ int i; ++ ++ /* register reset function */ ++ for (i = 0; i < ms->smp.cpus; i++) { ++ qemu_register_reset(reset_load_elf, LOONGARCH_CPU(qemu_get_cpu(i))); ++ } ++ ++ info->kernel_filename = ms->kernel_filename; ++ info->kernel_cmdline = ms->kernel_cmdline; ++ info->initrd_filename = ms->initrd_filename; ++ ++ if (lvms->bios_loaded) { ++ loongarch_firmware_boot(lvms, info); ++ } else { ++ loongarch_direct_kernel_boot(info); ++ } ++} +diff --git a/hw/loongarch/fw_cfg.c b/hw/loongarch/fw_cfg.c +index f15a174..35aeb2d 100644 +--- a/hw/loongarch/fw_cfg.c ++++ b/hw/loongarch/fw_cfg.c +@@ -17,7 +17,7 @@ static void fw_cfg_boot_set(void *opaque, const char *boot_device, + fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); + } + +-FWCfgState *loongarch_fw_cfg_init(ram_addr_t ram_size, MachineState *ms) ++FWCfgState *virt_fw_cfg_init(ram_addr_t ram_size, MachineState *ms) + { + FWCfgState *fw_cfg; + int max_cpus = ms->smp.max_cpus; +diff --git a/hw/loongarch/fw_cfg.h b/hw/loongarch/fw_cfg.h +index 7c0de4d..27ee682 100644 +--- a/hw/loongarch/fw_cfg.h ++++ b/hw/loongarch/fw_cfg.h +@@ -11,5 +11,5 @@ + #include "hw/boards.h" + #include "hw/nvram/fw_cfg.h" + +-FWCfgState *loongarch_fw_cfg_init(ram_addr_t ram_size, MachineState *ms); ++FWCfgState *virt_fw_cfg_init(ram_addr_t ram_size, MachineState *ms); + #endif +diff --git a/hw/loongarch/meson.build b/hw/loongarch/meson.build +index c042150..d306d82 100644 +--- a/hw/loongarch/meson.build ++++ b/hw/loongarch/meson.build +@@ -1,6 +1,7 @@ + loongarch_ss = ss.source_set() + loongarch_ss.add(files( + 'fw_cfg.c', ++ 'boot.c', + )) + loongarch_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: [files('virt.c'), fdt]) + loongarch_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-build.c')) +diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c +index 01e59f3..4e77901 100644 +--- a/hw/loongarch/virt.c ++++ b/hw/loongarch/virt.c +@@ -10,13 +10,13 @@ + #include "qapi/error.h" + #include "hw/boards.h" + #include "hw/char/serial.h" ++#include "sysemu/kvm.h" ++#include "sysemu/tcg.h" + #include "sysemu/sysemu.h" + #include "sysemu/qtest.h" + #include "sysemu/runstate.h" + #include "sysemu/reset.h" + #include "sysemu/rtc.h" +-#include "sysemu/tcg.h" +-#include "sysemu/kvm.h" + #include "hw/loongarch/virt.h" + #include "exec/address-spaces.h" + #include "hw/irq.h" +@@ -46,42 +46,36 @@ + #include "sysemu/tpm.h" + #include "sysemu/block-backend.h" + #include "hw/block/flash.h" ++#include "hw/virtio/virtio-iommu.h" + #include "qemu/error-report.h" ++#include "qemu/guest-random.h" + +- +-struct loaderparams { +- uint64_t ram_size; +- const char *kernel_filename; +- const char *kernel_cmdline; +- const char *initrd_filename; +-}; +- +-static bool virt_is_veiointc_enabled(LoongArchMachineState *lams) ++static bool virt_is_veiointc_enabled(LoongArchVirtMachineState *lvms) + { +- if (lams->veiointc == ON_OFF_AUTO_OFF) { ++ if (lvms->veiointc == ON_OFF_AUTO_OFF) { + return false; + } + return true; + } + + static void virt_get_veiointc(Object *obj, Visitor *v, const char *name, +- void *opaque, Error **errp) ++ void *opaque, Error **errp) + { +- LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); +- OnOffAuto veiointc = lams->veiointc; ++ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); ++ OnOffAuto veiointc = lvms->veiointc; + + visit_type_OnOffAuto(v, name, &veiointc, errp); + } + + static void virt_set_veiointc(Object *obj, Visitor *v, const char *name, +- void *opaque, Error **errp) ++ void *opaque, Error **errp) + { +- LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); ++ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + +- visit_type_OnOffAuto(v, name, &lams->veiointc, errp); ++ visit_type_OnOffAuto(v, name, &lvms->veiointc, errp); + } + +-static PFlashCFI01 *virt_flash_create1(LoongArchMachineState *lams, ++static PFlashCFI01 *virt_flash_create1(LoongArchVirtMachineState *lvms, + const char *name, + const char *alias_prop_name) + { +@@ -96,16 +90,16 @@ static PFlashCFI01 *virt_flash_create1(LoongArchMachineState *lams, + qdev_prop_set_uint16(dev, "id2", 0x00); + qdev_prop_set_uint16(dev, "id3", 0x00); + qdev_prop_set_string(dev, "name", name); +- object_property_add_child(OBJECT(lams), name, OBJECT(dev)); +- object_property_add_alias(OBJECT(lams), alias_prop_name, ++ object_property_add_child(OBJECT(lvms), name, OBJECT(dev)); ++ object_property_add_alias(OBJECT(lvms), alias_prop_name, + OBJECT(dev), "drive"); + return PFLASH_CFI01(dev); + } + +-static void virt_flash_create(LoongArchMachineState *lams) ++static void virt_flash_create(LoongArchVirtMachineState *lvms) + { +- lams->flash[0] = virt_flash_create1(lams, "virt.flash0", "pflash0"); +- lams->flash[1] = virt_flash_create1(lams, "virt.flash1", "pflash1"); ++ lvms->flash[0] = virt_flash_create1(lvms, "virt.flash0", "pflash0"); ++ lvms->flash[1] = virt_flash_create1(lvms, "virt.flash1", "pflash1"); + } + + static void virt_flash_map1(PFlashCFI01 *flash, +@@ -131,19 +125,114 @@ static void virt_flash_map1(PFlashCFI01 *flash, + sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0)); + } + +-static void virt_flash_map(LoongArchMachineState *lams, ++static void virt_flash_map(LoongArchVirtMachineState *lvms, + MemoryRegion *sysmem) + { +- PFlashCFI01 *flash0 = lams->flash[0]; +- PFlashCFI01 *flash1 = lams->flash[1]; ++ PFlashCFI01 *flash0 = lvms->flash[0]; ++ PFlashCFI01 *flash1 = lvms->flash[1]; + + virt_flash_map1(flash0, VIRT_FLASH0_BASE, VIRT_FLASH0_SIZE, sysmem); + virt_flash_map1(flash1, VIRT_FLASH1_BASE, VIRT_FLASH1_SIZE, sysmem); + } + +-static void fdt_add_flash_node(LoongArchMachineState *lams) ++static void fdt_add_cpuic_node(LoongArchVirtMachineState *lvms, ++ uint32_t *cpuintc_phandle) ++{ ++ MachineState *ms = MACHINE(lvms); ++ char *nodename; ++ ++ *cpuintc_phandle = qemu_fdt_alloc_phandle(ms->fdt); ++ nodename = g_strdup_printf("/cpuic"); ++ qemu_fdt_add_subnode(ms->fdt, nodename); ++ qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *cpuintc_phandle); ++ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", ++ "loongson,cpu-interrupt-controller"); ++ qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); ++ qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 1); ++ g_free(nodename); ++} ++ ++static void fdt_add_eiointc_node(LoongArchVirtMachineState *lvms, ++ uint32_t *cpuintc_phandle, ++ uint32_t *eiointc_phandle) ++{ ++ MachineState *ms = MACHINE(lvms); ++ char *nodename; ++ hwaddr extioi_base = APIC_BASE; ++ hwaddr extioi_size = EXTIOI_SIZE; ++ ++ *eiointc_phandle = qemu_fdt_alloc_phandle(ms->fdt); ++ nodename = g_strdup_printf("/eiointc@%" PRIx64, extioi_base); ++ qemu_fdt_add_subnode(ms->fdt, nodename); ++ qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *eiointc_phandle); ++ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", ++ "loongson,ls2k2000-eiointc"); ++ qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); ++ qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 1); ++ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", ++ *cpuintc_phandle); ++ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupts", 3); ++ qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0x0, ++ extioi_base, 0x0, extioi_size); ++ g_free(nodename); ++} ++ ++static void fdt_add_pch_pic_node(LoongArchVirtMachineState *lvms, ++ uint32_t *eiointc_phandle, ++ uint32_t *pch_pic_phandle) ++{ ++ MachineState *ms = MACHINE(lvms); ++ char *nodename; ++ hwaddr pch_pic_base = VIRT_PCH_REG_BASE; ++ hwaddr pch_pic_size = VIRT_PCH_REG_SIZE; ++ ++ *pch_pic_phandle = qemu_fdt_alloc_phandle(ms->fdt); ++ nodename = g_strdup_printf("/platic@%" PRIx64, pch_pic_base); ++ qemu_fdt_add_subnode(ms->fdt, nodename); ++ qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *pch_pic_phandle); ++ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", ++ "loongson,pch-pic-1.0"); ++ qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0, ++ pch_pic_base, 0, pch_pic_size); ++ qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); ++ qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 2); ++ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", ++ *eiointc_phandle); ++ qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,pic-base-vec", 0); ++ g_free(nodename); ++} ++ ++static void fdt_add_pch_msi_node(LoongArchVirtMachineState *lvms, ++ uint32_t *eiointc_phandle, ++ uint32_t *pch_msi_phandle) ++{ ++ MachineState *ms = MACHINE(lvms); ++ char *nodename; ++ hwaddr pch_msi_base = VIRT_PCH_MSI_ADDR_LOW; ++ hwaddr pch_msi_size = VIRT_PCH_MSI_SIZE; ++ ++ *pch_msi_phandle = qemu_fdt_alloc_phandle(ms->fdt); ++ nodename = g_strdup_printf("/msi@%" PRIx64, pch_msi_base); ++ qemu_fdt_add_subnode(ms->fdt, nodename); ++ qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *pch_msi_phandle); ++ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", ++ "loongson,pch-msi-1.0"); ++ qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", ++ 0, pch_msi_base, ++ 0, pch_msi_size); ++ qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); ++ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", ++ *eiointc_phandle); ++ qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,msi-base-vec", ++ VIRT_PCH_PIC_IRQ_NUM); ++ qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,msi-num-vecs", ++ EXTIOI_IRQS - VIRT_PCH_PIC_IRQ_NUM); ++ g_free(nodename); ++} ++ ++static void fdt_add_flash_node(LoongArchVirtMachineState *lvms) + { +- MachineState *ms = MACHINE(lams); ++ MachineState *ms = MACHINE(lvms); + char *nodename; + MemoryRegion *flash_mem; + +@@ -153,11 +242,11 @@ static void fdt_add_flash_node(LoongArchMachineState *lams) + hwaddr flash1_base; + hwaddr flash1_size; + +- flash_mem = pflash_cfi01_get_memory(lams->flash[0]); ++ flash_mem = pflash_cfi01_get_memory(lvms->flash[0]); + flash0_base = flash_mem->addr; + flash0_size = memory_region_size(flash_mem); + +- flash_mem = pflash_cfi01_get_memory(lams->flash[1]); ++ flash_mem = pflash_cfi01_get_memory(lvms->flash[1]); + flash1_base = flash_mem->addr; + flash1_size = memory_region_size(flash_mem); + +@@ -171,41 +260,53 @@ static void fdt_add_flash_node(LoongArchMachineState *lams) + g_free(nodename); + } + +-static void fdt_add_rtc_node(LoongArchMachineState *lams) ++static void fdt_add_rtc_node(LoongArchVirtMachineState *lvms, ++ uint32_t *pch_pic_phandle) + { + char *nodename; + hwaddr base = VIRT_RTC_REG_BASE; + hwaddr size = VIRT_RTC_LEN; +- MachineState *ms = MACHINE(lams); ++ MachineState *ms = MACHINE(lvms); + + nodename = g_strdup_printf("/rtc@%" PRIx64, base); + qemu_fdt_add_subnode(ms->fdt, nodename); +- qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "loongson,ls7a-rtc"); ++ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", ++ "loongson,ls7a-rtc"); + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 2, size); ++ qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", ++ VIRT_RTC_IRQ - VIRT_GSI_BASE , 0x4); ++ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", ++ *pch_pic_phandle); + g_free(nodename); + } + +-static void fdt_add_uart_node(LoongArchMachineState *lams) ++static void fdt_add_uart_node(LoongArchVirtMachineState *lvms, ++ uint32_t *pch_pic_phandle, hwaddr base, ++ int irq, bool chosen) + { + char *nodename; +- hwaddr base = VIRT_UART_BASE; + hwaddr size = VIRT_UART_SIZE; +- MachineState *ms = MACHINE(lams); ++ MachineState *ms = MACHINE(lvms); + + nodename = g_strdup_printf("/serial@%" PRIx64, base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "ns16550a"); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0x0, base, 0x0, size); + qemu_fdt_setprop_cell(ms->fdt, nodename, "clock-frequency", 100000000); +- qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename); ++ if (chosen) ++ qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename); ++ qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq, 0x4); ++ qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", ++ *pch_pic_phandle); + g_free(nodename); + } + +-static void create_fdt(LoongArchMachineState *lams) ++static void create_fdt(LoongArchVirtMachineState *lvms) + { +- MachineState *ms = MACHINE(lams); ++ MachineState *ms = MACHINE(lvms); ++ uint8_t rng_seed[32]; + +- ms->fdt = create_device_tree(&lams->fdt_size); ++ ms->fdt = create_device_tree(&lvms->fdt_size); + if (!ms->fdt) { + error_report("create_device_tree() failed"); + exit(1); +@@ -217,12 +318,16 @@ static void create_fdt(LoongArchMachineState *lams) + qemu_fdt_setprop_cell(ms->fdt, "/", "#address-cells", 0x2); + qemu_fdt_setprop_cell(ms->fdt, "/", "#size-cells", 0x2); + qemu_fdt_add_subnode(ms->fdt, "/chosen"); ++ ++ /* Pass seed to RNG */ ++ qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); ++ qemu_fdt_setprop(ms->fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); + } + +-static void fdt_add_cpu_nodes(const LoongArchMachineState *lams) ++static void fdt_add_cpu_nodes(const LoongArchVirtMachineState *lvms) + { + int num; +- const MachineState *ms = MACHINE(lams); ++ const MachineState *ms = MACHINE(lvms); + int smp_cpus = ms->smp.cpus; + + qemu_fdt_add_subnode(ms->fdt, "/cpus"); +@@ -276,11 +381,11 @@ static void fdt_add_cpu_nodes(const LoongArchMachineState *lams) + } + } + +-static void fdt_add_fw_cfg_node(const LoongArchMachineState *lams) ++static void fdt_add_fw_cfg_node(const LoongArchVirtMachineState *lvms) + { + char *nodename; + hwaddr base = VIRT_FWCFG_BASE; +- const MachineState *ms = MACHINE(lams); ++ const MachineState *ms = MACHINE(lvms); + + nodename = g_strdup_printf("/fw_cfg@%" PRIx64, base); + qemu_fdt_add_subnode(ms->fdt, nodename); +@@ -292,7 +397,62 @@ static void fdt_add_fw_cfg_node(const LoongArchMachineState *lams) + g_free(nodename); + } + +-static void fdt_add_pcie_node(const LoongArchMachineState *lams) ++static void fdt_add_pcie_irq_map_node(const LoongArchVirtMachineState *lvms, ++ char *nodename, ++ uint32_t *pch_pic_phandle) ++{ ++ int pin, dev; ++ uint32_t irq_map_stride = 0; ++ uint32_t full_irq_map[GPEX_NUM_IRQS *GPEX_NUM_IRQS * 10] = {}; ++ uint32_t *irq_map = full_irq_map; ++ const MachineState *ms = MACHINE(lvms); ++ ++ /* This code creates a standard swizzle of interrupts such that ++ * each device's first interrupt is based on it's PCI_SLOT number. ++ * (See pci_swizzle_map_irq_fn()) ++ * ++ * We only need one entry per interrupt in the table (not one per ++ * possible slot) seeing the interrupt-map-mask will allow the table ++ * to wrap to any number of devices. ++ */ ++ ++ for (dev = 0; dev < GPEX_NUM_IRQS; dev++) { ++ int devfn = dev * 0x8; ++ ++ for (pin = 0; pin < GPEX_NUM_IRQS; pin++) { ++ int irq_nr = 16 + ((pin + PCI_SLOT(devfn)) % GPEX_NUM_IRQS); ++ int i = 0; ++ ++ /* Fill PCI address cells */ ++ irq_map[i] = cpu_to_be32(devfn << 8); ++ i += 3; ++ ++ /* Fill PCI Interrupt cells */ ++ irq_map[i] = cpu_to_be32(pin + 1); ++ i += 1; ++ ++ /* Fill interrupt controller phandle and cells */ ++ irq_map[i++] = cpu_to_be32(*pch_pic_phandle); ++ irq_map[i++] = cpu_to_be32(irq_nr); ++ ++ if (!irq_map_stride) { ++ irq_map_stride = i; ++ } ++ irq_map += irq_map_stride; ++ } ++ } ++ ++ ++ qemu_fdt_setprop(ms->fdt, nodename, "interrupt-map", full_irq_map, ++ GPEX_NUM_IRQS * GPEX_NUM_IRQS * ++ irq_map_stride * sizeof(uint32_t)); ++ qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupt-map-mask", ++ 0x1800, 0, 0, 0x7); ++} ++ ++static void fdt_add_pcie_node(const LoongArchVirtMachineState *lvms, ++ uint32_t *pch_pic_phandle, ++ uint32_t *pch_msi_phandle) + { + char *nodename; + hwaddr base_mmio = VIRT_PCI_MEM_BASE; +@@ -303,7 +463,7 @@ static void fdt_add_pcie_node(const LoongArchMachineState *lams) + hwaddr size_pcie = VIRT_PCI_CFG_SIZE; + hwaddr base = base_pcie; + +- const MachineState *ms = MACHINE(lams); ++ const MachineState *ms = MACHINE(lvms); + + nodename = g_strdup_printf("/pcie@%" PRIx64, base); + qemu_fdt_add_subnode(ms->fdt, nodename); +@@ -323,34 +483,11 @@ static void fdt_add_pcie_node(const LoongArchMachineState *lams) + 2, base_pio, 2, size_pio, + 1, FDT_PCI_RANGE_MMIO, 2, base_mmio, + 2, base_mmio, 2, size_mmio); +- g_free(nodename); +-} ++ qemu_fdt_setprop_cells(ms->fdt, nodename, "msi-map", ++ 0, *pch_msi_phandle, 0, 0x10000); + +-static void fdt_add_irqchip_node(LoongArchMachineState *lams) +-{ +- MachineState *ms = MACHINE(lams); +- char *nodename; +- uint32_t irqchip_phandle; ++ fdt_add_pcie_irq_map_node(lvms, nodename, pch_pic_phandle); + +- irqchip_phandle = qemu_fdt_alloc_phandle(ms->fdt); +- qemu_fdt_setprop_cell(ms->fdt, "/", "interrupt-parent", irqchip_phandle); +- +- nodename = g_strdup_printf("/intc@%lx", VIRT_IOAPIC_REG_BASE); +- qemu_fdt_add_subnode(ms->fdt, nodename); +- qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 3); +- qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); +- qemu_fdt_setprop_cell(ms->fdt, nodename, "#address-cells", 0x2); +- qemu_fdt_setprop_cell(ms->fdt, nodename, "#size-cells", 0x2); +- qemu_fdt_setprop(ms->fdt, nodename, "ranges", NULL, 0); +- +- qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", +- "loongarch,ls7a"); +- +- qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", +- 2, VIRT_IOAPIC_REG_BASE, +- 2, PCH_PIC_ROUTE_ENTRY_OFFSET); +- +- qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", irqchip_phandle); + g_free(nodename); + } + +@@ -360,7 +497,8 @@ static void fdt_add_memory_node(MachineState *ms, + char *nodename = g_strdup_printf("/memory@%" PRIx64, base); + + qemu_fdt_add_subnode(ms->fdt, nodename); +- qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 2, base, 2, size); ++ qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", base >> 32, base, ++ size >> 32, size); + qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "memory"); + + if (ms->numa_state && ms->numa_state->num_nodes) { +@@ -370,58 +508,89 @@ static void fdt_add_memory_node(MachineState *ms, + g_free(nodename); + } + +-static void virt_build_smbios(LoongArchMachineState *lams) ++static void fdt_add_memory_nodes(MachineState *ms) + { +- MachineState *ms = MACHINE(lams); +- MachineClass *mc = MACHINE_GET_CLASS(lams); ++ hwaddr base, size, ram_size, gap; ++ int i, nb_numa_nodes, nodes; ++ NodeInfo *numa_info; ++ ++ ram_size = ms->ram_size; ++ base = VIRT_LOWMEM_BASE; ++ gap = VIRT_LOWMEM_SIZE; ++ nodes = nb_numa_nodes = ms->numa_state->num_nodes; ++ numa_info = ms->numa_state->nodes; ++ if (!nodes) { ++ nodes = 1; ++ } ++ ++ for (i = 0; i < nodes; i++) { ++ if (nb_numa_nodes) { ++ size = numa_info[i].node_mem; ++ } else { ++ size = ram_size; ++ } ++ ++ /* ++ * memory for the node splited into two part ++ * lowram: [base, +gap) ++ * highram: [VIRT_HIGHMEM_BASE, +(len - gap)) ++ */ ++ if (size >= gap) { ++ fdt_add_memory_node(ms, base, gap, i); ++ size -= gap; ++ base = VIRT_HIGHMEM_BASE; ++ gap = ram_size - VIRT_LOWMEM_SIZE; ++ } ++ ++ if (size) { ++ fdt_add_memory_node(ms, base, size, i); ++ base += size; ++ gap -= size; ++ } ++ } ++} ++ ++static void virt_build_smbios(LoongArchVirtMachineState *lvms) ++{ ++ MachineState *ms = MACHINE(lvms); ++ MachineClass *mc = MACHINE_GET_CLASS(lvms); + uint8_t *smbios_tables, *smbios_anchor; + size_t smbios_tables_len, smbios_anchor_len; + const char *product = "QEMU Virtual Machine"; + +- if (!lams->fw_cfg) { ++ if (!lvms->fw_cfg) { + return; + } + + smbios_set_defaults("QEMU", product, mc->name, false, + true, SMBIOS_ENTRY_POINT_TYPE_64); +- + smbios_get_tables(ms, NULL, 0, &smbios_tables, &smbios_tables_len, + &smbios_anchor, &smbios_anchor_len, &error_fatal); + + if (smbios_anchor) { +- fw_cfg_add_file(lams->fw_cfg, "etc/smbios/smbios-tables", ++ fw_cfg_add_file(lvms->fw_cfg, "etc/smbios/smbios-tables", + smbios_tables, smbios_tables_len); +- fw_cfg_add_file(lams->fw_cfg, "etc/smbios/smbios-anchor", ++ fw_cfg_add_file(lvms->fw_cfg, "etc/smbios/smbios-anchor", + smbios_anchor, smbios_anchor_len); + } + } + +-static void virt_machine_done(Notifier *notifier, void *data) ++static void virt_done(Notifier *notifier, void *data) + { +- LoongArchMachineState *lams = container_of(notifier, +- LoongArchMachineState, machine_done); +- virt_build_smbios(lams); +- loongarch_acpi_setup(lams); ++ LoongArchVirtMachineState *lvms = container_of(notifier, ++ LoongArchVirtMachineState, machine_done); ++ virt_build_smbios(lvms); ++ loongarch_acpi_setup(lvms); + } + + static void virt_powerdown_req(Notifier *notifier, void *opaque) + { +- LoongArchMachineState *s = container_of(notifier, +- LoongArchMachineState, powerdown_notifier); ++ LoongArchVirtMachineState *s; + ++ s = container_of(notifier, LoongArchVirtMachineState, powerdown_notifier); + acpi_send_event(s->acpi_ged, ACPI_POWER_DOWN_STATUS); + } + +-struct memmap_entry { +- uint64_t address; +- uint64_t length; +- uint32_t type; +- uint32_t reserved; +-}; +- +-static struct memmap_entry *memmap_table; +-static unsigned memmap_entries; +- + static void memmap_add_entry(uint64_t address, uint64_t length, uint32_t type) + { + /* Ensure there are no duplicate entries. */ +@@ -438,35 +607,11 @@ static void memmap_add_entry(uint64_t address, uint64_t length, uint32_t type) + memmap_entries++; + } + +-static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr) +-{ +- return addr & MAKE_64BIT_MASK(0, TARGET_PHYS_ADDR_SPACE_BITS); +-} +- +-static int64_t load_kernel_info(const struct loaderparams *loaderparams) +-{ +- uint64_t kernel_entry, kernel_low, kernel_high; +- ssize_t kernel_size; +- +- kernel_size = load_elf(loaderparams->kernel_filename, NULL, +- cpu_loongarch_virt_to_phys, NULL, +- &kernel_entry, &kernel_low, +- &kernel_high, NULL, 0, +- EM_LOONGARCH, 1, 0); +- +- if (kernel_size < 0) { +- error_report("could not load kernel '%s': %s", +- loaderparams->kernel_filename, +- load_elf_strerror(kernel_size)); +- exit(1); +- } +- return kernel_entry; +-} +- +-static DeviceState *create_acpi_ged(DeviceState *pch_pic, LoongArchMachineState *lams) ++static DeviceState *create_acpi_ged(DeviceState *pch_pic, ++ LoongArchVirtMachineState *lvms) + { + DeviceState *dev; +- MachineState *ms = MACHINE(lams); ++ MachineState *ms = MACHINE(lvms); + uint32_t event = ACPI_GED_PWR_DOWN_EVT; + + if (ms->ram_slots) { +@@ -513,9 +658,12 @@ static DeviceState *create_platform_bus(DeviceState *pch_pic) + return dev; + } + +-static void loongarch_devices_init(DeviceState *pch_pic, LoongArchMachineState *lams) ++static void virt_devices_init(DeviceState *pch_pic, ++ LoongArchVirtMachineState *lvms, ++ uint32_t *pch_pic_phandle, ++ uint32_t *pch_msi_phandle) + { +- MachineClass *mc = MACHINE_GET_CLASS(lams); ++ MachineClass *mc = MACHINE_GET_CLASS(lvms); + DeviceState *gpex_dev; + SysBusDevice *d; + PCIBus *pci_bus; +@@ -527,7 +675,7 @@ static void loongarch_devices_init(DeviceState *pch_pic, LoongArchMachineState * + d = SYS_BUS_DEVICE(gpex_dev); + sysbus_realize_and_unref(d, &error_fatal); + pci_bus = PCI_HOST_BRIDGE(gpex_dev)->bus; +- lams->pci_bus = pci_bus; ++ lvms->pci_bus = pci_bus; + + /* Map only part size_ecam bytes of ECAM space */ + ecam_alias = g_new0(MemoryRegion, 1); +@@ -559,13 +707,23 @@ static void loongarch_devices_init(DeviceState *pch_pic, LoongArchMachineState * + gpex_set_irq_num(GPEX_HOST(gpex_dev), i, 16 + i); + } + +- serial_mm_init(get_system_memory(), VIRT_UART_BASE, 0, +- qdev_get_gpio_in(pch_pic, +- VIRT_UART_IRQ - VIRT_GSI_BASE), +- 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN); +- fdt_add_uart_node(lams); ++ /* Add pcie node */ ++ fdt_add_pcie_node(lvms, pch_pic_phandle, pch_msi_phandle); + +- /* Network init */ ++ /* ++ * Create uart fdt node in reverse order so that they appear ++ * in the finished device tree lowest address first ++ */ ++ for (i = VIRT_UART_COUNT; i --> 0;) { ++ hwaddr base = VIRT_UART_BASE + i * VIRT_UART_SIZE; ++ int irq = VIRT_UART_IRQ + i - VIRT_GSI_BASE; ++ serial_mm_init(get_system_memory(), base, 0, ++ qdev_get_gpio_in(pch_pic, irq), ++ 115200, serial_hd(i), DEVICE_LITTLE_ENDIAN); ++ fdt_add_uart_node(lvms, pch_pic_phandle, base, irq, i == 0); ++ } ++ ++ /* Network init */ + for (i = 0; i < nb_nics; i++) { + pci_nic_init_nofail(&nd_table[i], pci_bus, mc->default_nic, NULL); + } +@@ -578,17 +736,17 @@ static void loongarch_devices_init(DeviceState *pch_pic, LoongArchMachineState * + sysbus_create_simple("ls7a_rtc", VIRT_RTC_REG_BASE, + qdev_get_gpio_in(pch_pic, + VIRT_RTC_IRQ - VIRT_GSI_BASE)); +- fdt_add_rtc_node(lams); ++ fdt_add_rtc_node(lvms, pch_pic_phandle); + + /* acpi ged */ +- lams->acpi_ged = create_acpi_ged(pch_pic, lams); ++ lvms->acpi_ged = create_acpi_ged(pch_pic, lvms); + /* platform bus */ +- lams->platform_bus_dev = create_platform_bus(pch_pic); ++ lvms->platform_bus_dev = create_platform_bus(pch_pic); + } + +-static void loongarch_irq_init(LoongArchMachineState *lams) ++static void virt_irq_init(LoongArchVirtMachineState *lvms) + { +- MachineState *ms = MACHINE(lams); ++ MachineState *ms = MACHINE(lvms); + DeviceState *pch_pic, *pch_msi, *cpudev; + DeviceState *ipi, *extioi; + SysBusDevice *d; +@@ -596,27 +754,50 @@ static void loongarch_irq_init(LoongArchMachineState *lams) + CPULoongArchState *env; + CPUState *cpu_state; + int cpu, pin, i, start, num; ++ uint32_t cpuintc_phandle, eiointc_phandle, pch_pic_phandle, pch_msi_phandle; + + /* +- * The connection of interrupts: +- * +-----+ +---------+ +-------+ +- * | IPI |--> | CPUINTC | <-- | Timer | +- * +-----+ +---------+ +-------+ +- * ^ +- * | +- * +---------+ +- * | EIOINTC | +- * +---------+ +- * ^ ^ +- * | | +- * +---------+ +---------+ +- * | PCH-PIC | | PCH-MSI | +- * +---------+ +---------+ +- * ^ ^ ^ +- * | | | +- * +--------+ +---------+ +---------+ +- * | UARTs | | Devices | | Devices | +- * +--------+ +---------+ +---------+ ++ * Extended IRQ model. ++ * | ++ * +-----------+ +-------------|--------+ +-----------+ ++ * | IPI/Timer | --> | CPUINTC(0-3)|(4-255) | <-- | IPI/Timer | ++ * +-----------+ +-------------|--------+ +-----------+ ++ * ^ | ++ * | ++ * +---------+ ++ * | EIOINTC | ++ * +---------+ ++ * ^ ^ ++ * | | ++ * +---------+ +---------+ ++ * | PCH-PIC | | PCH-MSI | ++ * +---------+ +---------+ ++ * ^ ^ ^ ++ * | | | ++ * +--------+ +---------+ +---------+ ++ * | UARTs | | Devices | | Devices | ++ * +--------+ +---------+ +---------+ ++ * ++ * Virt extended IRQ model. ++ * ++ * +-----+ +---------------+ +-------+ ++ * | IPI |--> | CPUINTC(0-255)| <-- | Timer | ++ * +-----+ +---------------+ +-------+ ++ * ^ ++ * | ++ * +-----------+ ++ * | V-EIOINTC | ++ * +-----------+ ++ * ^ ^ ++ * | | ++ * +---------+ +---------+ ++ * | PCH-PIC | | PCH-MSI | ++ * +---------+ +---------+ ++ * ^ ^ ^ ++ * | | | ++ * +--------+ +---------+ +---------+ ++ * | UARTs | | Devices | | Devices | ++ * +--------+ +---------+ +---------+ + */ + + /* Create IPI device */ +@@ -625,17 +806,20 @@ static void loongarch_irq_init(LoongArchMachineState *lams) + sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal); + + /* IPI iocsr memory region */ +- memory_region_add_subregion(&lams->system_iocsr, SMP_IPI_MAILBOX, ++ memory_region_add_subregion(&lvms->system_iocsr, SMP_IPI_MAILBOX, + sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 0)); +- memory_region_add_subregion(&lams->system_iocsr, MAIL_SEND_ADDR, ++ memory_region_add_subregion(&lvms->system_iocsr, MAIL_SEND_ADDR, + sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 1)); + ++ /* Add cpu interrupt-controller */ ++ fdt_add_cpuic_node(lvms, &cpuintc_phandle); ++ + for (cpu = 0; cpu < ms->smp.cpus; cpu++) { + cpu_state = qemu_get_cpu(cpu); + cpudev = DEVICE(cpu_state); + lacpu = LOONGARCH_CPU(cpu_state); + env = &(lacpu->env); +- env->address_space_iocsr = &lams->as_iocsr; ++ env->address_space_iocsr = &lvms->as_iocsr; + + /* connect ipi irq to cpu irq */ + qdev_connect_gpio_out(ipi, cpu, qdev_get_gpio_in(cpudev, IRQ_IPI)); +@@ -645,18 +829,16 @@ static void loongarch_irq_init(LoongArchMachineState *lams) + /* Create EXTIOI device */ + extioi = qdev_new(TYPE_LOONGARCH_EXTIOI); + qdev_prop_set_uint32(extioi, "num-cpu", ms->smp.cpus); +- if (virt_is_veiointc_enabled(lams)) { ++ if (virt_is_veiointc_enabled(lvms)) { + qdev_prop_set_bit(extioi, "has-virtualization-extension", true); + } + sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal); +- +- memory_region_add_subregion(&lams->system_iocsr, APIC_BASE, +- sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 0)); +- if (virt_is_veiointc_enabled(lams)) { +- memory_region_add_subregion(&lams->system_iocsr, EXTIOI_VIRT_BASE, +- sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 1)); ++ memory_region_add_subregion(&lvms->system_iocsr, APIC_BASE, ++ sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 0)); ++ if (virt_is_veiointc_enabled(lvms)) { ++ memory_region_add_subregion(&lvms->system_iocsr, EXTIOI_VIRT_BASE, ++ sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 1)); + } +- lams->extioi = extioi; + + /* + * connect ext irq to the cpu irq +@@ -670,6 +852,9 @@ static void loongarch_irq_init(LoongArchMachineState *lams) + } + } + ++ /* Add Extend I/O Interrupt Controller node */ ++ fdt_add_eiointc_node(lvms, &cpuintc_phandle, &eiointc_phandle); ++ + pch_pic = qdev_new(TYPE_LOONGARCH_PCH_PIC); + num = VIRT_PCH_PIC_IRQ_NUM; + qdev_prop_set_uint32(pch_pic, "pch_pic_irq_num", num); +@@ -689,6 +874,9 @@ static void loongarch_irq_init(LoongArchMachineState *lams) + qdev_connect_gpio_out(DEVICE(d), i, qdev_get_gpio_in(extioi, i)); + } + ++ /* Add PCH PIC node */ ++ fdt_add_pch_pic_node(lvms, &eiointc_phandle, &pch_pic_phandle); ++ + pch_msi = qdev_new(TYPE_LOONGARCH_PCH_MSI); + start = num; + num = EXTIOI_IRQS - start; +@@ -703,28 +891,31 @@ static void loongarch_irq_init(LoongArchMachineState *lams) + qdev_get_gpio_in(extioi, i + start)); + } + +- loongarch_devices_init(pch_pic, lams); ++ /* Add PCH MSI node */ ++ fdt_add_pch_msi_node(lvms, &eiointc_phandle, &pch_msi_phandle); ++ ++ virt_devices_init(pch_pic, lvms, &pch_pic_phandle, &pch_msi_phandle); + } + +-static void loongarch_firmware_init(LoongArchMachineState *lams) ++static void virt_firmware_init(LoongArchVirtMachineState *lvms) + { +- char *filename = MACHINE(lams)->firmware; ++ char *filename = MACHINE(lvms)->firmware; + char *bios_name = NULL; + int bios_size, i; + BlockBackend *pflash_blk0; + MemoryRegion *mr; + +- lams->bios_loaded = false; ++ lvms->bios_loaded = false; + + /* Map legacy -drive if=pflash to machine properties */ +- for (i = 0; i < ARRAY_SIZE(lams->flash); i++) { +- pflash_cfi01_legacy_drive(lams->flash[i], ++ for (i = 0; i < ARRAY_SIZE(lvms->flash); i++) { ++ pflash_cfi01_legacy_drive(lvms->flash[i], + drive_get(IF_PFLASH, 0, i)); + } + +- virt_flash_map(lams, get_system_memory()); ++ virt_flash_map(lvms, get_system_memory()); + +- pflash_blk0 = pflash_cfi01_get_blk(lams->flash[0]); ++ pflash_blk0 = pflash_cfi01_get_blk(lvms->flash[0]); + + if (pflash_blk0) { + if (filename) { +@@ -732,7 +923,7 @@ static void loongarch_firmware_init(LoongArchMachineState *lams) + "options at once"); + exit(1); + } +- lams->bios_loaded = true; ++ lvms->bios_loaded = true; + return; + } + +@@ -743,92 +934,31 @@ static void loongarch_firmware_init(LoongArchMachineState *lams) + exit(1); + } + +- mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(lams->flash[0]), 0); ++ mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(lvms->flash[0]), 0); + bios_size = load_image_mr(bios_name, mr); + if (bios_size < 0) { + error_report("Could not load ROM image '%s'", bios_name); + exit(1); + } + g_free(bios_name); +- lams->bios_loaded = true; ++ lvms->bios_loaded = true; + } + } + +-static void reset_load_elf(void *opaque) ++static MemTxResult virt_iocsr_misc_write(void *opaque, hwaddr addr, ++ uint64_t val, unsigned size, ++ MemTxAttrs attrs) + { +- LoongArchCPU *cpu = opaque; +- CPULoongArchState *env = &cpu->env; +- +- cpu_reset(CPU(cpu)); +- if (env->load_elf) { +- cpu_set_pc(CPU(cpu), env->elf_address); +- } +-} +- +-static void fw_cfg_add_kernel_info(const struct loaderparams *loaderparams, +- FWCfgState *fw_cfg) +-{ +- /* +- * Expose the kernel, the command line, and the initrd in fw_cfg. +- * We don't process them here at all, it's all left to the +- * firmware. +- */ +- load_image_to_fw_cfg(fw_cfg, +- FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA, +- loaderparams->kernel_filename, +- false); +- +- if (loaderparams->initrd_filename) { +- load_image_to_fw_cfg(fw_cfg, +- FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA, +- loaderparams->initrd_filename, false); +- } +- +- if (loaderparams->kernel_cmdline) { +- fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, +- strlen(loaderparams->kernel_cmdline) + 1); +- fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, +- loaderparams->kernel_cmdline); +- } +-} +- +-static void loongarch_firmware_boot(LoongArchMachineState *lams, +- const struct loaderparams *loaderparams) +-{ +- fw_cfg_add_kernel_info(loaderparams, lams->fw_cfg); +-} +- +-static void loongarch_direct_kernel_boot(LoongArchMachineState *lams, +- const struct loaderparams *loaderparams) +-{ +- MachineState *machine = MACHINE(lams); +- int64_t kernel_addr = 0; +- LoongArchCPU *lacpu; +- int i; +- +- kernel_addr = load_kernel_info(loaderparams); +- if (!machine->firmware) { +- for (i = 0; i < machine->smp.cpus; i++) { +- lacpu = LOONGARCH_CPU(qemu_get_cpu(i)); +- lacpu->env.load_elf = true; +- lacpu->env.elf_address = kernel_addr; +- } +- } +-} +- +-static MemTxResult loongarch_qemu_write(void *opaque, hwaddr addr, uint64_t val, +- unsigned size, MemTxAttrs attrs) +-{ +- LoongArchMachineState *lams = LOONGARCH_MACHINE(opaque); ++ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(opaque); + uint64_t features; + + switch (addr) { + case MISC_FUNC_REG: +- if (!virt_is_veiointc_enabled(lams)) { ++ if (!virt_is_veiointc_enabled(lvms)) { + return MEMTX_OK; + } + +- features = address_space_ldl(&lams->as_iocsr, ++ features = address_space_ldl(&lvms->as_iocsr, + EXTIOI_VIRT_BASE + EXTIOI_VIRT_CONFIG, + attrs, NULL); + if (val & BIT_ULL(IOCSRM_EXTIOI_EN)) { +@@ -838,19 +968,22 @@ static MemTxResult loongarch_qemu_write(void *opaque, hwaddr addr, uint64_t val, + features |= BIT(EXTIOI_ENABLE_INT_ENCODE); + } + +- address_space_stl(&lams->as_iocsr, ++ address_space_stl(&lvms->as_iocsr, + EXTIOI_VIRT_BASE + EXTIOI_VIRT_CONFIG, + features, attrs, NULL); ++ break; ++ default: ++ g_assert_not_reached(); + } + + return MEMTX_OK; + } + +-static MemTxResult loongarch_qemu_read(void *opaque, hwaddr addr, +- uint64_t *data, +- unsigned size, MemTxAttrs attrs) ++static MemTxResult virt_iocsr_misc_read(void *opaque, hwaddr addr, ++ uint64_t *data, ++ unsigned size, MemTxAttrs attrs) + { +- LoongArchMachineState *lams = LOONGARCH_MACHINE(opaque); ++ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(opaque); + uint64_t ret = 0; + int features; + +@@ -859,10 +992,9 @@ static MemTxResult loongarch_qemu_read(void *opaque, hwaddr addr, + ret = 0x11ULL; + break; + case FEATURE_REG: +- ret = 1ULL << IOCSRF_MSI | 1ULL << IOCSRF_EXTIOI | +- 1ULL << IOCSRF_CSRIPI; ++ ret = BIT(IOCSRF_MSI) | BIT(IOCSRF_EXTIOI) | BIT(IOCSRF_CSRIPI); + if (kvm_enabled()) { +- ret |= 1ULL << IOCSRF_VM; ++ ret |= BIT(IOCSRF_VM); + } + break; + case VENDOR_REG: +@@ -872,31 +1004,32 @@ static MemTxResult loongarch_qemu_read(void *opaque, hwaddr addr, + ret = 0x303030354133ULL; /* "3A5000" */ + break; + case MISC_FUNC_REG: +- if (!virt_is_veiointc_enabled(lams)) { ++ if (!virt_is_veiointc_enabled(lvms)) { + ret |= BIT_ULL(IOCSRM_EXTIOI_EN); + break; + } + +- features = address_space_ldl(&lams->as_iocsr, ++ features = address_space_ldl(&lvms->as_iocsr, + EXTIOI_VIRT_BASE + EXTIOI_VIRT_CONFIG, + attrs, NULL); + if (features & BIT(EXTIOI_ENABLE)) { + ret |= BIT_ULL(IOCSRM_EXTIOI_EN); + } +- + if (features & BIT(EXTIOI_ENABLE_INT_ENCODE)) { + ret |= BIT_ULL(IOCSRM_EXTIOI_INT_ENCODE); + } + break; ++ default: ++ g_assert_not_reached(); + } + + *data = ret; + return MEMTX_OK; + } + +-static const MemoryRegionOps loongarch_qemu_ops = { +- .read_with_attrs = loongarch_qemu_read, +- .write_with_attrs = loongarch_qemu_write, ++static const MemoryRegionOps virt_iocsr_misc_ops = { ++ .read_with_attrs = virt_iocsr_misc_read, ++ .write_with_attrs = virt_iocsr_misc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, +@@ -908,43 +1041,87 @@ static const MemoryRegionOps loongarch_qemu_ops = { + }, + }; + +-static void loongarch_init(MachineState *machine) ++static void fw_cfg_add_memory(MachineState *ms) ++{ ++ hwaddr base, size, ram_size, gap; ++ int nb_numa_nodes, nodes; ++ NodeInfo *numa_info; ++ ++ ram_size = ms->ram_size; ++ base = VIRT_LOWMEM_BASE; ++ gap = VIRT_LOWMEM_SIZE; ++ nodes = nb_numa_nodes = ms->numa_state->num_nodes; ++ numa_info = ms->numa_state->nodes; ++ if (!nodes) { ++ nodes = 1; ++ } ++ ++ /* add fw_cfg memory map of node0 */ ++ if (nb_numa_nodes) { ++ size = numa_info[0].node_mem; ++ } else { ++ size = ram_size; ++ } ++ ++ if (size >= gap) { ++ memmap_add_entry(base, gap, 1); ++ size -= gap; ++ base = VIRT_HIGHMEM_BASE; ++ } ++ ++ if (size) { ++ memmap_add_entry(base, size, 1); ++ base += size; ++ } ++ ++ if (nodes < 2) { ++ return; ++ } ++ ++ /* add fw_cfg memory map of other nodes */ ++ if (numa_info[0].node_mem < gap && ram_size > gap) { ++ /* ++ * memory map for the maining nodes splited into two part ++ * lowram: [base, +(gap - numa_info[0].node_mem)) ++ * highram: [VIRT_HIGHMEM_BASE, +(ram_size - gap)) ++ */ ++ memmap_add_entry(base, gap - numa_info[0].node_mem, 1); ++ size = ram_size - gap; ++ base = VIRT_HIGHMEM_BASE; ++ } else { ++ size = ram_size - numa_info[0].node_mem; ++ } ++ ++ if (size) ++ memmap_add_entry(base, size, 1); ++} ++ ++static void virt_init(MachineState *machine) + { + LoongArchCPU *lacpu; + const char *cpu_model = machine->cpu_type; +- ram_addr_t offset = 0; +- ram_addr_t ram_size = machine->ram_size; +- uint64_t highram_size = 0, phyAddr = 0; + MemoryRegion *address_space_mem = get_system_memory(); +- LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); +- int nb_numa_nodes = machine->numa_state->num_nodes; +- NodeInfo *numa_info = machine->numa_state->nodes; ++ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); + int i; +- hwaddr fdt_base; ++ hwaddr base, size, ram_size = machine->ram_size; + const CPUArchIdList *possible_cpus; + MachineClass *mc = MACHINE_GET_CLASS(machine); + CPUState *cpu; +- char *ramName = NULL; +- struct loaderparams loaderparams = { }; + + if (!cpu_model) { + cpu_model = LOONGARCH_CPU_TYPE_NAME("la464"); + } + +- if (ram_size < 1 * GiB) { +- error_report("ram_size must be greater than 1G."); +- exit(1); +- } +- create_fdt(lams); ++ create_fdt(lvms); + + /* Create IOCSR space */ +- memory_region_init_io(&lams->system_iocsr, OBJECT(machine), NULL, ++ memory_region_init_io(&lvms->system_iocsr, OBJECT(machine), NULL, + machine, "iocsr", UINT64_MAX); +- address_space_init(&lams->as_iocsr, &lams->system_iocsr, "IOCSR"); +- memory_region_init_io(&lams->iocsr_mem, OBJECT(machine), +- &loongarch_qemu_ops, ++ address_space_init(&lvms->as_iocsr, &lvms->system_iocsr, "IOCSR"); ++ memory_region_init_io(&lvms->iocsr_mem, OBJECT(machine), ++ &virt_iocsr_misc_ops, + machine, "iocsr_misc", 0x428); +- memory_region_add_subregion(&lams->system_iocsr, 0, &lams->iocsr_mem); ++ memory_region_add_subregion(&lvms->system_iocsr, 0, &lvms->iocsr_mem); + + /* Init CPUs */ + possible_cpus = mc->possible_cpu_arch_ids(machine); +@@ -955,49 +1132,32 @@ static void loongarch_init(MachineState *machine) + lacpu = LOONGARCH_CPU(cpu); + lacpu->phy_id = machine->possible_cpus->cpus[i].arch_id; + } +- fdt_add_cpu_nodes(lams); ++ fdt_add_cpu_nodes(lvms); ++ fdt_add_memory_nodes(machine); ++ fw_cfg_add_memory(machine); + + /* Node0 memory */ +- memmap_add_entry(VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, 1); +- fdt_add_memory_node(machine, VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, 0); +- memory_region_init_alias(&lams->lowmem, NULL, "loongarch.node0.lowram", +- machine->ram, offset, VIRT_LOWMEM_SIZE); +- memory_region_add_subregion(address_space_mem, phyAddr, &lams->lowmem); +- +- offset += VIRT_LOWMEM_SIZE; +- if (nb_numa_nodes > 0) { +- assert(numa_info[0].node_mem > VIRT_LOWMEM_SIZE); +- highram_size = numa_info[0].node_mem - VIRT_LOWMEM_SIZE; +- } else { +- highram_size = ram_size - VIRT_LOWMEM_SIZE; ++ size = ram_size; ++ base = VIRT_LOWMEM_BASE; ++ if (size > VIRT_LOWMEM_SIZE) { ++ size = VIRT_LOWMEM_SIZE; + } +- phyAddr = VIRT_HIGHMEM_BASE; +- memmap_add_entry(phyAddr, highram_size, 1); +- fdt_add_memory_node(machine, phyAddr, highram_size, 0); +- memory_region_init_alias(&lams->highmem, NULL, "loongarch.node0.highram", +- machine->ram, offset, highram_size); +- memory_region_add_subregion(address_space_mem, phyAddr, &lams->highmem); +- +- /* Node1 - Nodemax memory */ +- offset += highram_size; +- phyAddr += highram_size; +- +- for (i = 1; i < nb_numa_nodes; i++) { +- MemoryRegion *nodemem = g_new(MemoryRegion, 1); +- ramName = g_strdup_printf("loongarch.node%d.ram", i); +- memory_region_init_alias(nodemem, NULL, ramName, machine->ram, +- offset, numa_info[i].node_mem); +- memory_region_add_subregion(address_space_mem, phyAddr, nodemem); +- memmap_add_entry(phyAddr, numa_info[i].node_mem, 1); +- fdt_add_memory_node(machine, phyAddr, numa_info[i].node_mem, i); +- offset += numa_info[i].node_mem; +- phyAddr += numa_info[i].node_mem; ++ ++ memory_region_init_alias(&lvms->lowmem, NULL, "loongarch.lowram", ++ machine->ram, base, size); ++ memory_region_add_subregion(address_space_mem, base, &lvms->lowmem); ++ base += size; ++ if (ram_size - size) { ++ base = VIRT_HIGHMEM_BASE; ++ memory_region_init_alias(&lvms->highmem, NULL, "loongarch.highram", ++ machine->ram, VIRT_LOWMEM_BASE + size, ram_size - size); ++ memory_region_add_subregion(address_space_mem, base, &lvms->highmem); ++ base += ram_size - size; + } + + /* initialize device memory address space */ + if (machine->ram_size < machine->maxram_size) { + ram_addr_t device_mem_size = machine->maxram_size - machine->ram_size; +- hwaddr device_mem_base; + + if (machine->ram_slots > ACPI_MAX_RAM_SLOTS) { + error_report("unsupported amount of memory slots: %"PRIu64, +@@ -1011,55 +1171,35 @@ static void loongarch_init(MachineState *machine) + "%d bytes", TARGET_PAGE_SIZE); + exit(EXIT_FAILURE); + } +- /* device memory base is the top of high memory address. */ +- device_mem_base = ROUND_UP(VIRT_HIGHMEM_BASE + highram_size, 1 * GiB); +- machine_memory_devices_init(machine, device_mem_base, device_mem_size); ++ machine_memory_devices_init(machine, base, device_mem_size); + } + + /* load the BIOS image. */ +- loongarch_firmware_init(lams); ++ virt_firmware_init(lvms); + + /* fw_cfg init */ +- lams->fw_cfg = loongarch_fw_cfg_init(ram_size, machine); +- rom_set_fw(lams->fw_cfg); +- if (lams->fw_cfg != NULL) { +- fw_cfg_add_file(lams->fw_cfg, "etc/memmap", ++ lvms->fw_cfg = virt_fw_cfg_init(ram_size, machine); ++ rom_set_fw(lvms->fw_cfg); ++ if (lvms->fw_cfg != NULL) { ++ fw_cfg_add_file(lvms->fw_cfg, "etc/memmap", + memmap_table, + sizeof(struct memmap_entry) * (memmap_entries)); + } +- fdt_add_fw_cfg_node(lams); +- loaderparams.ram_size = ram_size; +- loaderparams.kernel_filename = machine->kernel_filename; +- loaderparams.kernel_cmdline = machine->kernel_cmdline; +- loaderparams.initrd_filename = machine->initrd_filename; +- /* load the kernel. */ +- if (loaderparams.kernel_filename) { +- if (lams->bios_loaded) { +- loongarch_firmware_boot(lams, &loaderparams); +- } else { +- loongarch_direct_kernel_boot(lams, &loaderparams); +- } +- } +- fdt_add_flash_node(lams); +- /* register reset function */ +- for (i = 0; i < machine->smp.cpus; i++) { +- lacpu = LOONGARCH_CPU(qemu_get_cpu(i)); +- qemu_register_reset(reset_load_elf, lacpu); +- } ++ fdt_add_fw_cfg_node(lvms); ++ fdt_add_flash_node(lvms); ++ + /* Initialize the IO interrupt subsystem */ +- loongarch_irq_init(lams); +- fdt_add_irqchip_node(lams); +- platform_bus_add_all_fdt_nodes(machine->fdt, "/intc", ++ virt_irq_init(lvms); ++ platform_bus_add_all_fdt_nodes(machine->fdt, "/platic", + VIRT_PLATFORM_BUS_BASEADDRESS, + VIRT_PLATFORM_BUS_SIZE, + VIRT_PLATFORM_BUS_IRQ); +- lams->machine_done.notify = virt_machine_done; +- qemu_add_machine_init_done_notifier(&lams->machine_done); ++ lvms->machine_done.notify = virt_done; ++ qemu_add_machine_init_done_notifier(&lvms->machine_done); + /* connect powerdown request */ +- lams->powerdown_notifier.notify = virt_powerdown_req; +- qemu_register_powerdown_notifier(&lams->powerdown_notifier); ++ lvms->powerdown_notifier.notify = virt_powerdown_req; ++ qemu_register_powerdown_notifier(&lvms->powerdown_notifier); + +- fdt_add_pcie_node(lams); + /* + * Since lowmem region starts from 0 and Linux kernel legacy start address + * at 2 MiB, FDT base address is located at 1 MiB to avoid NULL pointer +@@ -1067,47 +1207,44 @@ static void loongarch_init(MachineState *machine) + * Put the FDT into the memory map as a ROM image: this will ensure + * the FDT is copied again upon reset, even if addr points into RAM. + */ +- fdt_base = 1 * MiB; +- qemu_fdt_dumpdtb(machine->fdt, lams->fdt_size); +- rom_add_blob_fixed("fdt", machine->fdt, lams->fdt_size, fdt_base); ++ qemu_fdt_dumpdtb(machine->fdt, lvms->fdt_size); ++ rom_add_blob_fixed_as("fdt", machine->fdt, lvms->fdt_size, FDT_BASE, ++ &address_space_memory); ++ qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, ++ rom_ptr_for_as(&address_space_memory, FDT_BASE, lvms->fdt_size)); ++ ++ lvms->bootinfo.ram_size = ram_size; ++ loongarch_load_kernel(machine, &lvms->bootinfo); + } + +-bool loongarch_is_acpi_enabled(LoongArchMachineState *lams) ++static void virt_get_acpi(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) + { +- if (lams->acpi == ON_OFF_AUTO_OFF) { +- return false; +- } +- return true; +-} +- +-static void loongarch_get_acpi(Object *obj, Visitor *v, const char *name, +- void *opaque, Error **errp) +-{ +- LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); +- OnOffAuto acpi = lams->acpi; ++ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); ++ OnOffAuto acpi = lvms->acpi; + + visit_type_OnOffAuto(v, name, &acpi, errp); + } + +-static void loongarch_set_acpi(Object *obj, Visitor *v, const char *name, ++static void virt_set_acpi(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) + { +- LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); ++ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + +- visit_type_OnOffAuto(v, name, &lams->acpi, errp); ++ visit_type_OnOffAuto(v, name, &lvms->acpi, errp); + } + +-static void loongarch_machine_initfn(Object *obj) ++static void virt_initfn(Object *obj) + { +- LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); ++ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + + if (tcg_enabled()) { +- lams->veiointc = ON_OFF_AUTO_OFF; ++ lvms->veiointc = ON_OFF_AUTO_OFF; + } +- lams->acpi = ON_OFF_AUTO_AUTO; +- lams->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6); +- lams->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8); +- virt_flash_create(lams); ++ lvms->acpi = ON_OFF_AUTO_AUTO; ++ lvms->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6); ++ lvms->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8); ++ virt_flash_create(lvms); + } + + static bool memhp_type_supported(DeviceState *dev) +@@ -1123,7 +1260,7 @@ static void virt_mem_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, + pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL, errp); + } + +-static void virt_machine_device_pre_plug(HotplugHandler *hotplug_dev, ++static void virt_device_pre_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) + { + if (memhp_type_supported(dev)) { +@@ -1134,14 +1271,14 @@ static void virt_machine_device_pre_plug(HotplugHandler *hotplug_dev, + static void virt_mem_unplug_request(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) + { +- LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev); ++ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); + + /* the acpi ged is always exist */ +- hotplug_handler_unplug_request(HOTPLUG_HANDLER(lams->acpi_ged), dev, ++ hotplug_handler_unplug_request(HOTPLUG_HANDLER(lvms->acpi_ged), dev, + errp); + } + +-static void virt_machine_device_unplug_request(HotplugHandler *hotplug_dev, ++static void virt_device_unplug_request(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) + { + if (memhp_type_supported(dev)) { +@@ -1152,14 +1289,14 @@ static void virt_machine_device_unplug_request(HotplugHandler *hotplug_dev, + static void virt_mem_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) + { +- LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev); ++ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); + +- hotplug_handler_unplug(HOTPLUG_HANDLER(lams->acpi_ged), dev, errp); +- pc_dimm_unplug(PC_DIMM(dev), MACHINE(lams)); ++ hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, errp); ++ pc_dimm_unplug(PC_DIMM(dev), MACHINE(lvms)); + qdev_unrealize(dev); + } + +-static void virt_machine_device_unplug(HotplugHandler *hotplug_dev, ++static void virt_device_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) + { + if (memhp_type_supported(dev)) { +@@ -1170,35 +1307,37 @@ static void virt_machine_device_unplug(HotplugHandler *hotplug_dev, + static void virt_mem_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) + { +- LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev); ++ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); + +- pc_dimm_plug(PC_DIMM(dev), MACHINE(lams)); +- hotplug_handler_plug(HOTPLUG_HANDLER(lams->acpi_ged), ++ pc_dimm_plug(PC_DIMM(dev), MACHINE(lvms)); ++ hotplug_handler_plug(HOTPLUG_HANDLER(lvms->acpi_ged), + dev, &error_abort); + } + +-static void loongarch_machine_device_plug_cb(HotplugHandler *hotplug_dev, ++static void virt_device_plug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) + { +- LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev); +- MachineClass *mc = MACHINE_GET_CLASS(lams); ++ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); ++ MachineClass *mc = MACHINE_GET_CLASS(lvms); ++ PlatformBusDevice *pbus; + + if (device_is_dynamic_sysbus(mc, dev)) { +- if (lams->platform_bus_dev) { +- platform_bus_link_device(PLATFORM_BUS_DEVICE(lams->platform_bus_dev), +- SYS_BUS_DEVICE(dev)); ++ if (lvms->platform_bus_dev) { ++ pbus = PLATFORM_BUS_DEVICE(lvms->platform_bus_dev); ++ platform_bus_link_device(pbus, SYS_BUS_DEVICE(dev)); + } + } else if (memhp_type_supported(dev)) { + virt_mem_plug(hotplug_dev, dev, errp); + } + } + +-static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine, +- DeviceState *dev) ++static HotplugHandler *virt_get_hotplug_handler(MachineState *machine, ++ DeviceState *dev) + { + MachineClass *mc = MACHINE_GET_CLASS(machine); + + if (device_is_dynamic_sysbus(mc, dev) || ++ object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI) || + memhp_type_supported(dev)) { + return HOTPLUG_HANDLER(machine); + } +@@ -1234,8 +1373,8 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) + return ms->possible_cpus; + } + +-static CpuInstanceProperties +-virt_cpu_index_to_props(MachineState *ms, unsigned cpu_index) ++static CpuInstanceProperties virt_cpu_index_to_props(MachineState *ms, ++ unsigned cpu_index) + { + MachineClass *mc = MACHINE_GET_CLASS(ms); + const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms); +@@ -1246,25 +1385,22 @@ virt_cpu_index_to_props(MachineState *ms, unsigned cpu_index) + + static int64_t virt_get_default_cpu_node_id(const MachineState *ms, int idx) + { +- int64_t nidx = 0; ++ int64_t socket_id; + + if (ms->numa_state->num_nodes) { +- nidx = idx / (ms->smp.cpus / ms->numa_state->num_nodes); +- if (ms->numa_state->num_nodes <= nidx) { +- nidx = ms->numa_state->num_nodes - 1; +- } ++ socket_id = ms->possible_cpus->cpus[idx].props.socket_id; ++ return socket_id % ms->numa_state->num_nodes; ++ } else { ++ return 0; + } +- return nidx; + } + +-static void loongarch_class_init(ObjectClass *oc, void *data) ++static void virt_class_init(ObjectClass *oc, void *data) + { + MachineClass *mc = MACHINE_CLASS(oc); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); + +- mc->desc = "Loongson-3A5000 LS7A1000 machine"; +- mc->init = loongarch_init; +- mc->default_ram_size = 1 * GiB; ++ mc->init = virt_init; + mc->default_cpu_type = LOONGARCH_CPU_TYPE_NAME("la464"); + mc->default_ram_id = "loongarch.ram"; + mc->max_cpus = LOONGARCH_MAX_CPUS; +@@ -1279,35 +1415,36 @@ static void loongarch_class_init(ObjectClass *oc, void *data) + mc->numa_mem_supported = true; + mc->auto_enable_numa_with_memhp = true; + mc->auto_enable_numa_with_memdev = true; +- mc->get_hotplug_handler = virt_machine_get_hotplug_handler; ++ mc->get_hotplug_handler = virt_get_hotplug_handler; + mc->default_nic = "virtio-net-pci"; +- hc->plug = loongarch_machine_device_plug_cb; +- hc->pre_plug = virt_machine_device_pre_plug; +- hc->unplug_request = virt_machine_device_unplug_request; +- hc->unplug = virt_machine_device_unplug; ++ hc->plug = virt_device_plug_cb; ++ hc->pre_plug = virt_device_pre_plug; ++ hc->unplug_request = virt_device_unplug_request; ++ hc->unplug = virt_device_unplug; + + object_class_property_add(oc, "acpi", "OnOffAuto", +- loongarch_get_acpi, loongarch_set_acpi, ++ virt_get_acpi, virt_set_acpi, + NULL, NULL); + object_class_property_set_description(oc, "acpi", + "Enable ACPI"); + object_class_property_add(oc, "v-eiointc", "OnOffAuto", +- virt_get_veiointc, virt_set_veiointc, NULL, NULL); ++ virt_get_veiointc, virt_set_veiointc, ++ NULL, NULL); + object_class_property_set_description(oc, "v-eiointc", +- "Enable Virt Extend I/O Interrupt Controller"); ++ "Enable Virt Extend I/O Interrupt Controller."); + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); + #ifdef CONFIG_TPM + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); + #endif + } + +-static const TypeInfo loongarch_machine_types[] = { ++static const TypeInfo virt_machine_types[] = { + { +- .name = TYPE_LOONGARCH_MACHINE, ++ .name = TYPE_LOONGARCH_VIRT_MACHINE, + .parent = TYPE_MACHINE, +- .instance_size = sizeof(LoongArchMachineState), +- .class_init = loongarch_class_init, +- .instance_init = loongarch_machine_initfn, ++ .instance_size = sizeof(LoongArchVirtMachineState), ++ .class_init = virt_class_init, ++ .instance_init = virt_initfn, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } +@@ -1315,4 +1452,4 @@ static const TypeInfo loongarch_machine_types[] = { + } + }; + +-DEFINE_TYPES(loongarch_machine_types) ++DEFINE_TYPES(virt_machine_types) +diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h +index 1cf5bf9..626a37d 100644 +--- a/include/hw/intc/loongarch_extioi.h ++++ b/include/hw/intc/loongarch_extioi.h +@@ -39,22 +39,22 @@ + #define EXTIOI_COREISR_END (0xB20 - APIC_OFFSET) + #define EXTIOI_COREMAP_START (0xC00 - APIC_OFFSET) + #define EXTIOI_COREMAP_END (0xD00 - APIC_OFFSET) ++#define EXTIOI_SIZE 0x800 + + #define EXTIOI_VIRT_BASE (0x40000000) + #define EXTIOI_VIRT_SIZE (0x1000) + #define EXTIOI_VIRT_FEATURES (0x0) +-#define EXTIOI_HAS_VIRT_EXTENSION (0) +-#define EXTIOI_HAS_ENABLE_OPTION (1) +-#define EXTIOI_HAS_INT_ENCODE (2) +-#define EXTIOI_HAS_CPU_ENCODE (3) +-#define EXTIOI_VIRT_HAS_FEATURES (BIT(EXTIOI_HAS_VIRT_EXTENSION) \ +- | BIT(EXTIOI_HAS_ENABLE_OPTION)\ +- | BIT(EXTIOI_HAS_INT_ENCODE) \ +- | BIT(EXTIOI_HAS_CPU_ENCODE)) ++#define EXTIOI_HAS_VIRT_EXTENSION (0) ++#define EXTIOI_HAS_ENABLE_OPTION (1) ++#define EXTIOI_HAS_INT_ENCODE (2) ++#define EXTIOI_HAS_CPU_ENCODE (3) ++#define EXTIOI_VIRT_HAS_FEATURES (BIT(EXTIOI_HAS_VIRT_EXTENSION) \ ++ | BIT(EXTIOI_HAS_ENABLE_OPTION) \ ++ | BIT(EXTIOI_HAS_CPU_ENCODE)) + #define EXTIOI_VIRT_CONFIG (0x4) +-#define EXTIOI_ENABLE (1) +-#define EXTIOI_ENABLE_INT_ENCODE (2) +-#define EXTIOI_ENABLE_CPU_ENCODE (3) ++#define EXTIOI_ENABLE (1) ++#define EXTIOI_ENABLE_INT_ENCODE (2) ++#define EXTIOI_ENABLE_CPU_ENCODE (3) + #define EXTIOI_VIRT_COREMAP_START (0x40) + #define EXTIOI_VIRT_COREMAP_END (0x240) + +diff --git a/include/hw/loongarch/boot.h b/include/hw/loongarch/boot.h +new file mode 100644 +index 0000000..b3b870d +--- /dev/null ++++ b/include/hw/loongarch/boot.h +@@ -0,0 +1,119 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Definitions for LoongArch boot. ++ * ++ * Copyright (C) 2023 Loongson Technology Corporation Limited ++ */ ++ ++#ifndef HW_LOONGARCH_BOOT_H ++#define HW_LOONGARCH_BOOT_H ++ ++/* UEFI 2.10 */ ++#define EFI_SYSTEM_TABLE_SIGNATURE 0x5453595320494249 ++#define EFI_2_100_SYSTEM_TABLE_REVISION ((2<<16) | (100)) ++#define EFI_SPECIFICATION_VERSION EFI_SYSTEM_TABLE_REVISION ++#define EFI_SYSTEM_TABLE_REVISION EFI_2_100_SYSTEM_TABLE_REVISION ++ ++#define FW_VERSION 0x1 ++#define FW_PATCHLEVEL 0x0 ++ ++typedef struct { ++ uint8_t b[16]; ++} efi_guid_t QEMU_ALIGNED(8); ++ ++#define EFI_GUID(a, b, c, d...) (efi_guid_t){ { \ ++ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ ++ (b) & 0xff, ((b) >> 8) & 0xff, \ ++ (c) & 0xff, ((c) >> 8) & 0xff, d } } ++ ++#define LINUX_EFI_BOOT_MEMMAP_GUID \ ++ EFI_GUID(0x800f683f, 0xd08b, 0x423a, 0xa2, 0x93, \ ++ 0x96, 0x5c, 0x3c, 0x6f, 0xe2, 0xb4) ++ ++#define LINUX_EFI_INITRD_MEDIA_GUID \ ++ EFI_GUID(0x5568e427, 0x68fc, 0x4f3d, 0xac, 0x74, \ ++ 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68) ++ ++#define DEVICE_TREE_GUID \ ++ EFI_GUID(0xb1b621d5, 0xf19c, 0x41a5, 0x83, 0x0b, \ ++ 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0) ++ ++struct efi_config_table { ++ efi_guid_t guid; ++ uint64_t *ptr; ++ const char name[16]; ++}; ++ ++typedef struct { ++ uint64_t signature; ++ uint32_t revision; ++ uint32_t headersize; ++ uint32_t crc32; ++ uint32_t reserved; ++} efi_table_hdr_t; ++ ++struct efi_configuration_table { ++ efi_guid_t guid; ++ void *table; ++}; ++ ++struct efi_system_table { ++ efi_table_hdr_t hdr; ++ uint64_t fw_vendor; /* physical addr of CHAR16 vendor string */ ++ uint32_t fw_revision; ++ uint64_t con_in_handle; ++ uint64_t *con_in; ++ uint64_t con_out_handle; ++ uint64_t *con_out; ++ uint64_t stderr_handle; ++ uint64_t stderr_placeholder; ++ uint64_t *runtime; ++ uint64_t *boottime; ++ uint64_t nr_tables; ++ struct efi_configuration_table *tables; ++}; ++ ++typedef struct { ++ uint32_t type; ++ uint32_t pad; ++ uint64_t phys_addr; ++ uint64_t virt_addr; ++ uint64_t num_pages; ++ uint64_t attribute; ++} efi_memory_desc_t; ++ ++struct efi_boot_memmap { ++ uint64_t map_size; ++ uint64_t desc_size; ++ uint32_t desc_ver; ++ uint64_t map_key; ++ uint64_t buff_size; ++ efi_memory_desc_t map[32]; ++}; ++ ++struct efi_initrd { ++ uint64_t base; ++ uint64_t size; ++}; ++ ++struct loongarch_boot_info { ++ uint64_t ram_size; ++ const char *kernel_filename; ++ const char *kernel_cmdline; ++ const char *initrd_filename; ++ uint64_t a0, a1, a2; ++}; ++ ++extern struct memmap_entry *memmap_table; ++extern unsigned memmap_entries; ++ ++struct memmap_entry { ++ uint64_t address; ++ uint64_t length; ++ uint32_t type; ++ uint32_t reserved; ++}; ++ ++void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info); ++ ++#endif /* HW_LOONGARCH_BOOT_H */ +diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h +index 99447fd..c373e48 100644 +--- a/include/hw/loongarch/virt.h ++++ b/include/hw/loongarch/virt.h +@@ -11,15 +11,15 @@ + #include "target/loongarch/cpu.h" + #include "hw/boards.h" + #include "qemu/queue.h" +-#include "hw/intc/loongarch_ipi.h" + #include "hw/block/flash.h" ++#include "hw/loongarch/boot.h" + + #define LOONGARCH_MAX_CPUS 256 + + #define VIRT_FWCFG_BASE 0x1e020000UL + #define VIRT_BIOS_BASE 0x1c000000UL + #define VIRT_BIOS_SIZE (16 * MiB) +-#define VIRT_FLASH_SECTOR_SIZE (128 * KiB) ++#define VIRT_FLASH_SECTOR_SIZE (256 * KiB) + #define VIRT_FLASH0_BASE VIRT_BIOS_BASE + #define VIRT_FLASH0_SIZE VIRT_BIOS_SIZE + #define VIRT_FLASH1_BASE 0x1d000000UL +@@ -32,7 +32,11 @@ + #define VIRT_GED_MEM_ADDR (VIRT_GED_EVT_ADDR + ACPI_GED_EVT_SEL_LEN) + #define VIRT_GED_REG_ADDR (VIRT_GED_MEM_ADDR + MEMORY_HOTPLUG_IO_LEN) + +-struct LoongArchMachineState { ++#define COMMAND_LINE_SIZE 512 ++ ++#define FDT_BASE 0x100000 ++ ++struct LoongArchVirtMachineState { + /*< private >*/ + MachineState parent_obj; + +@@ -51,17 +55,15 @@ struct LoongArchMachineState { + DeviceState *acpi_ged; + int fdt_size; + DeviceState *platform_bus_dev; +- DeviceState *extioi; + PCIBus *pci_bus; + PFlashCFI01 *flash[2]; + MemoryRegion system_iocsr; + MemoryRegion iocsr_mem; + AddressSpace as_iocsr; +- int features; ++ struct loongarch_boot_info bootinfo; + }; + +-#define TYPE_LOONGARCH_MACHINE MACHINE_TYPE_NAME("virt") +-OBJECT_DECLARE_SIMPLE_TYPE(LoongArchMachineState, LOONGARCH_MACHINE) +-bool loongarch_is_acpi_enabled(LoongArchMachineState *lams); +-void loongarch_acpi_setup(LoongArchMachineState *lams); ++#define TYPE_LOONGARCH_VIRT_MACHINE MACHINE_TYPE_NAME("virt") ++OBJECT_DECLARE_SIMPLE_TYPE(LoongArchVirtMachineState, LOONGARCH_VIRT_MACHINE) ++void loongarch_acpi_setup(LoongArchVirtMachineState *lvms); + #endif +diff --git a/include/hw/pci-host/ls7a.h b/include/hw/pci-host/ls7a.h +index e753449..79d4ea8 100644 +--- a/include/hw/pci-host/ls7a.h ++++ b/include/hw/pci-host/ls7a.h +@@ -24,6 +24,8 @@ + #define VIRT_PCH_REG_BASE 0x10000000UL + #define VIRT_IOAPIC_REG_BASE (VIRT_PCH_REG_BASE) + #define VIRT_PCH_MSI_ADDR_LOW 0x2FF00000UL ++#define VIRT_PCH_REG_SIZE 0x400 ++#define VIRT_PCH_MSI_SIZE 0x8 + + /* + * GSI_BASE is hard-coded with 64 in linux kernel, else kernel fails to boot +@@ -34,17 +36,18 @@ + #define VIRT_PCH_PIC_IRQ_NUM 32 + #define VIRT_GSI_BASE 64 + #define VIRT_DEVICE_IRQS 16 ++#define VIRT_UART_COUNT 4 + #define VIRT_UART_IRQ (VIRT_GSI_BASE + 2) + #define VIRT_UART_BASE 0x1fe001e0 +-#define VIRT_UART_SIZE 0X100 +-#define VIRT_RTC_IRQ (VIRT_GSI_BASE + 3) ++#define VIRT_UART_SIZE 0x100 ++#define VIRT_RTC_IRQ (VIRT_GSI_BASE + 6) + #define VIRT_MISC_REG_BASE (VIRT_PCH_REG_BASE + 0x00080000) + #define VIRT_RTC_REG_BASE (VIRT_MISC_REG_BASE + 0x00050100) + #define VIRT_RTC_LEN 0x100 +-#define VIRT_SCI_IRQ (VIRT_GSI_BASE + 4) ++#define VIRT_SCI_IRQ (VIRT_GSI_BASE + 7) + + #define VIRT_PLATFORM_BUS_BASEADDRESS 0x16000000 + #define VIRT_PLATFORM_BUS_SIZE 0x2000000 + #define VIRT_PLATFORM_BUS_NUM_IRQS 2 +-#define VIRT_PLATFORM_BUS_IRQ (VIRT_GSI_BASE + 5) ++#define VIRT_PLATFORM_BUS_IRQ (VIRT_GSI_BASE + 8) + #endif +diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h +index f07ed49..b5dc107 100644 +--- a/target/loongarch/cpu.h ++++ b/target/loongarch/cpu.h +@@ -367,6 +367,7 @@ typedef struct CPUArchState { + uint32_t mp_state; + /* Store ipistate to access from this struct */ + DeviceState *ipistate; ++ struct loongarch_boot_info *boot_info; + #endif + } CPULoongArchState; + +-- +1.8.3.1 + diff --git a/0241-target-loongarch-Support-QMP-dump-guest-memory.patch b/0241-target-loongarch-Support-QMP-dump-guest-memory.patch new file mode 100644 index 0000000000000000000000000000000000000000..1e1130aeabf30ff36e1ca39bf705312cc73a5e81 --- /dev/null +++ b/0241-target-loongarch-Support-QMP-dump-guest-memory.patch @@ -0,0 +1,240 @@ +From 445ca6fc9d1b9108f1e9c1458c9f7b640c69d20a Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Thu, 19 Sep 2024 21:08:07 +0800 +Subject: [PATCH 251/293] target/loongarch: Support QMP dump-guest-memory + +commit 32c22cc47cf9b99d53aa698c612a215609fdb6c7 upstream + +Add the support needed for creating prstatus elf notes. This allows +us to use QMP dump-guest-memory. + +Now ELF notes of LoongArch only supports general elf notes, LSX and +LASX is not supported, since it is mainly used to dump guest memory. + +Signed-off-by: Bibo Mao +Reviewed-by: Song Gao +Tested-by: Song Gao +Message-Id: <20240822065245.2286214-1-maobibo@loongson.cn> +Signed-off-by: Song Gao + +Signed-off-by: Xianglai Li +--- + target/loongarch/arch_dump.c | 167 +++++++++++++++++++++++++++++++++++++++++++ + target/loongarch/cpu.c | 1 + + target/loongarch/internals.h | 2 + + target/loongarch/meson.build | 1 + + 4 files changed, 171 insertions(+) + create mode 100644 target/loongarch/arch_dump.c + +diff --git a/target/loongarch/arch_dump.c b/target/loongarch/arch_dump.c +new file mode 100644 +index 0000000..4986db9 +--- /dev/null ++++ b/target/loongarch/arch_dump.c +@@ -0,0 +1,167 @@ ++/* ++ * Support for writing ELF notes for LoongArch architectures ++ * ++ * Copyright (c) 2023 Loongarch Technology ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2 or later, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "cpu.h" ++#include "elf.h" ++#include "sysemu/dump.h" ++#include "internals.h" ++ ++/* struct user_pt_regs from arch/loongarch/include/uapi/asm/ptrace.h */ ++struct loongarch_user_regs { ++ uint64_t gpr[32]; ++ uint64_t pad1[1]; ++ /* Special CSR registers. */ ++ uint64_t csr_era; ++ uint64_t csr_badv; ++ uint64_t pad2[10]; ++} QEMU_PACKED; ++ ++QEMU_BUILD_BUG_ON(sizeof(struct loongarch_user_regs) != 360); ++ ++/* struct elf_prstatus from include/uapi/linux/elfcore.h */ ++struct loongarch_elf_prstatus { ++ char pad1[32]; /* 32 == offsetof(struct elf_prstatus, pr_pid) */ ++ uint32_t pr_pid; ++ /* ++ * 76 == offsetof(struct elf_prstatus, pr_reg) - ++ * offsetof(struct elf_prstatus, pr_ppid) ++ */ ++ char pad2[76]; ++ struct loongarch_user_regs pr_reg; ++ uint32_t pr_fpvalid; ++ char pad3[4]; ++} QEMU_PACKED; ++ ++QEMU_BUILD_BUG_ON(sizeof(struct loongarch_elf_prstatus) != 480); ++ ++/* struct user_fp_state from arch/loongarch/include/uapi/asm/ptrace.h */ ++struct loongarch_fpu_struct { ++ uint64_t fpr[32]; ++ uint64_t fcc; ++ unsigned int fcsr; ++} QEMU_PACKED; ++ ++QEMU_BUILD_BUG_ON(sizeof(struct loongarch_fpu_struct) != 268); ++ ++struct loongarch_note { ++ Elf64_Nhdr hdr; ++ char name[8]; /* align_up(sizeof("CORE"), 4) */ ++ union { ++ struct loongarch_elf_prstatus prstatus; ++ struct loongarch_fpu_struct fpu; ++ }; ++} QEMU_PACKED; ++ ++#define LOONGARCH_NOTE_HEADER_SIZE offsetof(struct loongarch_note, prstatus) ++#define LOONGARCH_PRSTATUS_NOTE_SIZE \ ++ (LOONGARCH_NOTE_HEADER_SIZE + sizeof(struct loongarch_elf_prstatus)) ++#define LOONGARCH_PRFPREG_NOTE_SIZE \ ++ (LOONGARCH_NOTE_HEADER_SIZE + sizeof(struct loongarch_fpu_struct)) ++ ++static void loongarch_note_init(struct loongarch_note *note, DumpState *s, ++ const char *name, Elf64_Word namesz, ++ Elf64_Word type, Elf64_Word descsz) ++{ ++ memset(note, 0, sizeof(*note)); ++ ++ note->hdr.n_namesz = cpu_to_dump32(s, namesz); ++ note->hdr.n_descsz = cpu_to_dump32(s, descsz); ++ note->hdr.n_type = cpu_to_dump32(s, type); ++ ++ memcpy(note->name, name, namesz); ++} ++ ++static int loongarch_write_elf64_fprpreg(WriteCoreDumpFunction f, ++ CPULoongArchState *env, int cpuid, ++ DumpState *s) ++{ ++ struct loongarch_note note; ++ int ret, i; ++ ++ loongarch_note_init(¬e, s, "CORE", 5, NT_PRFPREG, sizeof(note.fpu)); ++ note.fpu.fcsr = cpu_to_dump64(s, env->fcsr0); ++ ++ for (i = 0; i < 8; i++) { ++ note.fpu.fcc |= env->cf[i] << (8 * i); ++ } ++ note.fpu.fcc = cpu_to_dump64(s, note.fpu.fcc); ++ ++ for (i = 0; i < 32; ++i) { ++ note.fpu.fpr[i] = cpu_to_dump64(s, env->fpr[i].vreg.UD[0]); ++ } ++ ++ ret = f(¬e, LOONGARCH_PRFPREG_NOTE_SIZE, s); ++ if (ret < 0) { ++ return -1; ++ } ++ ++ return 0; ++} ++ ++int loongarch_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, ++ int cpuid, DumpState *s) ++{ ++ struct loongarch_note note; ++ CPULoongArchState *env = &LOONGARCH_CPU(cs)->env; ++ int ret, i; ++ ++ loongarch_note_init(¬e, s, "CORE", 5, NT_PRSTATUS, ++ sizeof(note.prstatus)); ++ note.prstatus.pr_pid = cpu_to_dump32(s, cpuid); ++ note.prstatus.pr_fpvalid = cpu_to_dump32(s, 1); ++ ++ for (i = 0; i < 32; ++i) { ++ note.prstatus.pr_reg.gpr[i] = cpu_to_dump64(s, env->gpr[i]); ++ } ++ note.prstatus.pr_reg.csr_era = cpu_to_dump64(s, env->CSR_ERA); ++ note.prstatus.pr_reg.csr_badv = cpu_to_dump64(s, env->CSR_BADV); ++ ret = f(¬e, LOONGARCH_PRSTATUS_NOTE_SIZE, s); ++ if (ret < 0) { ++ return -1; ++ } ++ ++ ret = loongarch_write_elf64_fprpreg(f, env, cpuid, s); ++ if (ret < 0) { ++ return -1; ++ } ++ ++ return ret; ++} ++ ++int cpu_get_dump_info(ArchDumpInfo *info, ++ const GuestPhysBlockList *guest_phys_blocks) ++{ ++ info->d_machine = EM_LOONGARCH; ++ info->d_endian = ELFDATA2LSB; ++ info->d_class = ELFCLASS64; ++ ++ return 0; ++} ++ ++ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) ++{ ++ size_t note_size = 0; ++ ++ if (class == ELFCLASS64) { ++ note_size = LOONGARCH_PRSTATUS_NOTE_SIZE + LOONGARCH_PRFPREG_NOTE_SIZE; ++ } ++ ++ return note_size * nr_cpus; ++} +diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c +index c223712..20ad11d 100644 +--- a/target/loongarch/cpu.c ++++ b/target/loongarch/cpu.c +@@ -827,6 +827,7 @@ static struct TCGCPUOps loongarch_tcg_ops = { + #include "hw/core/sysemu-cpu-ops.h" + + static const struct SysemuCPUOps loongarch_sysemu_ops = { ++ .write_elf64_note = loongarch_cpu_write_elf64_note, + .get_phys_page_debug = loongarch_cpu_get_phys_page_debug, + }; + +diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h +index 944153b..1a02427 100644 +--- a/target/loongarch/internals.h ++++ b/target/loongarch/internals.h +@@ -72,5 +72,7 @@ void write_fcc(CPULoongArchState *env, uint64_t val); + int loongarch_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n); + int loongarch_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n); + void loongarch_cpu_register_gdb_regs_for_features(CPUState *cs); ++int loongarch_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cpu, ++ int cpuid, DumpState *s); + + #endif +diff --git a/target/loongarch/meson.build b/target/loongarch/meson.build +index e002e9a..7817318 100644 +--- a/target/loongarch/meson.build ++++ b/target/loongarch/meson.build +@@ -8,6 +8,7 @@ loongarch_ss.add(files( + + loongarch_system_ss = ss.source_set() + loongarch_system_ss.add(files( ++ 'arch_dump.c', + 'cpu_helper.c', + 'loongarch-qmp-cmds.c', + 'machine.c', +-- +1.8.3.1 + diff --git a/0242-target-loongarch-Add-compatible-support-about-VM-reb.patch b/0242-target-loongarch-Add-compatible-support-about-VM-reb.patch new file mode 100644 index 0000000000000000000000000000000000000000..a1e7ddc9f6a158223ecabf3e9a706ed1bd139a36 --- /dev/null +++ b/0242-target-loongarch-Add-compatible-support-about-VM-reb.patch @@ -0,0 +1,54 @@ +From 8c93a158e1203d34342f400b807ba5a3674a3a64 Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Fri, 20 Sep 2024 08:46:31 +0800 +Subject: [PATCH 252/293] target/loongarch: Add compatible support about VM + reboot + +commit a840d70ee474c514b939f6f16fd51396c73d01c7 upstream + +With edk2-stable202408 LoongArch UEFI bios, CSR PGD register is set only +if its value is equal to zero for boot cpu, it causes reboot issue. Since +CSR PGD register is changed with linux kernel, UEFI BIOS cannot use it. + +Add workaround to clear CSR registers relative with TLB in function +loongarch_cpu_reset_hold(), so that VM can reboot with edk2-stable202408 +UEFI bios. + +Signed-off-by: Bibo Mao +Reviewed-by: Song Gao +Message-Id: <20240827035807.3326293-1-maobibo@loongson.cn> +Signed-off-by: Song Gao + +Signed-off-by: Xianglai Li +--- + target/loongarch/cpu.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c +index 20ad11d..a196235 100644 +--- a/target/loongarch/cpu.c ++++ b/target/loongarch/cpu.c +@@ -556,6 +556,20 @@ static void loongarch_cpu_reset_hold(Object *obj) + env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 0); + env->CSR_MERRCTL = FIELD_DP64(env->CSR_MERRCTL, CSR_MERRCTL, ISMERR, 0); + env->CSR_TID = cs->cpu_index; ++ /* ++ * Workaround for edk2-stable202408, CSR PGD register is set only if ++ * its value is equal to zero for boot cpu, it causes reboot issue. ++ * ++ * Here clear CSR registers relative with TLB. ++ */ ++ env->CSR_PGDH = 0; ++ env->CSR_PGDL = 0; ++ env->CSR_PWCL = 0; ++ env->CSR_PWCH = 0; ++ env->CSR_STLBPS = 0; ++ env->CSR_EENTRY = 0; ++ env->CSR_TLBRENTRY = 0; ++ env->CSR_MERRENTRY = 0; + + env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, TLB_TYPE, 2); + env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, MTLB_ENTRY, 63); +-- +1.8.3.1 + diff --git a/0243-target-loongarch-kvm-Add-vCPU-reset-function.patch b/0243-target-loongarch-kvm-Add-vCPU-reset-function.patch new file mode 100644 index 0000000000000000000000000000000000000000..7d9ec09f266cce7d19ee4b6a031de8dfda79c6ea --- /dev/null +++ b/0243-target-loongarch-kvm-Add-vCPU-reset-function.patch @@ -0,0 +1,70 @@ +From b4a015628ef073925aedd41636c19c38177710cd Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Fri, 20 Sep 2024 08:48:08 +0800 +Subject: [PATCH 253/293] target/loongarch/kvm: Add vCPU reset function + +commit a724f5a84ef027cd481a18eda67ea2de58282c3e upstream + +KVM provides interface KVM_REG_LOONGARCH_VCPU_RESET to reset vCPU, +it can be used to clear internal state about kvm kernel. vCPU reset +function is added here for kvm mode. + +Signed-off-by: Bibo Mao +Reviewed-by: Song Gao +Message-Id: <20240822022827.2273534-1-maobibo@loongson.cn> +Signed-off-by: Song Gao + +Signed-off-by: Xianglai Li +--- + target/loongarch/cpu.c | 2 +- + target/loongarch/kvm/kvm.c | 5 ++++- + target/loongarch/kvm/kvm_loongarch.h | 2 +- + 3 files changed, 6 insertions(+), 3 deletions(-) + +diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c +index a196235..8fb8195 100644 +--- a/target/loongarch/cpu.c ++++ b/target/loongarch/cpu.c +@@ -587,7 +587,7 @@ static void loongarch_cpu_reset_hold(Object *obj) + env->pc = 0x1c000000; + memset(env->tlb, 0, sizeof(env->tlb)); + if (kvm_enabled()) { +- kvm_arch_reset_vcpu(env); ++ kvm_arch_reset_vcpu(cs); + } + #endif + +diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c +index 3306cb5..30ce4d7 100644 +--- a/target/loongarch/kvm/kvm.c ++++ b/target/loongarch/kvm/kvm.c +@@ -485,9 +485,12 @@ static int kvm_loongarch_put_regs_fp(CPUState *cs) + return ret; + } + +-void kvm_arch_reset_vcpu(CPULoongArchState *env) ++void kvm_arch_reset_vcpu(CPUState *cs) + { ++ CPULoongArchState *env = cpu_env(cs); ++ + env->mp_state = KVM_MP_STATE_RUNNABLE; ++ kvm_set_one_reg(cs, KVM_REG_LOONGARCH_VCPU_RESET, 0); + } + + static int kvm_loongarch_get_mpstate(CPUState *cs) +diff --git a/target/loongarch/kvm/kvm_loongarch.h b/target/loongarch/kvm/kvm_loongarch.h +index bdb4f18..506a5c9 100644 +--- a/target/loongarch/kvm/kvm_loongarch.h ++++ b/target/loongarch/kvm/kvm_loongarch.h +@@ -11,7 +11,7 @@ + #define QEMU_KVM_LOONGARCH_H + + int kvm_loongarch_set_interrupt(LoongArchCPU *cpu, int irq, int level); +-void kvm_arch_reset_vcpu(CPULoongArchState *env); ++void kvm_arch_reset_vcpu(CPUState *cs); + + #ifdef CONFIG_KVM + /* +-- +1.8.3.1 + diff --git a/0244-acpi-ged-Add-macro-for-acpi-sleep-control-register.patch b/0244-acpi-ged-Add-macro-for-acpi-sleep-control-register.patch new file mode 100644 index 0000000000000000000000000000000000000000..60ec46f6e7a007b394b2f4c77f798cc1f68b34d8 --- /dev/null +++ b/0244-acpi-ged-Add-macro-for-acpi-sleep-control-register.patch @@ -0,0 +1,57 @@ +From 26a032f50241c971f94c40a9921694ef065d6712 Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Wed, 18 Sep 2024 09:42:05 +0800 +Subject: [PATCH 254/293] acpi: ged: Add macro for acpi sleep control register + +Macro definition is added for acpi sleep control register, ged emulation +driver can use the macro , also it can be used in FDT table if ged is +exposed with FDT table. + +Signed-off-by: Bibo Mao +Reviewed-by: Igor Mammedov +Message-Id: <20240918014206.2165821-2-maobibo@loongson.cn> +Signed-off-by: Song Gao +Signed-off-by: Xianglai Li +--- + hw/acpi/generic_event_device.c | 6 +++--- + include/hw/acpi/generic_event_device.h | 7 +++++-- + 2 files changed, 8 insertions(+), 5 deletions(-) + +diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c +index a3d3163..889ae96 100644 +--- a/hw/acpi/generic_event_device.c ++++ b/hw/acpi/generic_event_device.c +@@ -197,9 +197,9 @@ static void ged_regs_write(void *opaque, hwaddr addr, uint64_t data, + + switch (addr) { + case ACPI_GED_REG_SLEEP_CTL: +- slp_typ = (data >> 2) & 0x07; +- slp_en = (data >> 5) & 0x01; +- if (slp_en && slp_typ == 5) { ++ slp_typ = (data >> ACPI_GED_SLP_TYP_POS) & ACPI_GED_SLP_TYP_MASK; ++ slp_en = !!(data & ACPI_GED_SLP_EN); ++ if (slp_en && slp_typ == ACPI_GED_SLP_TYP_S5) { + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + } + return; +diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h +index ba84ce0..3006916 100644 +--- a/include/hw/acpi/generic_event_device.h ++++ b/include/hw/acpi/generic_event_device.h +@@ -80,8 +80,11 @@ OBJECT_DECLARE_SIMPLE_TYPE(AcpiGedState, ACPI_GED) + /* ACPI_GED_REG_RESET value for reset*/ + #define ACPI_GED_RESET_VALUE 0x42 + +-/* ACPI_GED_REG_SLEEP_CTL.SLP_TYP value for S5 (aka poweroff) */ +-#define ACPI_GED_SLP_TYP_S5 0x05 ++/* [ACPI 5.0 Chapter 4.8.3.7] Sleep Control and Status Register */ ++#define ACPI_GED_SLP_TYP_POS 0x2 /* SLP_TYPx Bit Offset */ ++#define ACPI_GED_SLP_TYP_MASK 0x07 /* SLP_TYPx 3-bit mask */ ++#define ACPI_GED_SLP_TYP_S5 0x05 /* System _S5 State (Soft Off) */ ++#define ACPI_GED_SLP_EN 0x20 /* SLP_EN write-only bit */ + + #define GED_DEVICE "GED" + #define AML_GED_EVT_REG "EREG" +-- +1.8.3.1 + diff --git a/0245-hw-loongarch-virt-Add-FDT-table-support-with-acpi-ge.patch b/0245-hw-loongarch-virt-Add-FDT-table-support-with-acpi-ge.patch new file mode 100644 index 0000000000000000000000000000000000000000..510d489eebc424cb73de19b5a28c2c500bcbd340 --- /dev/null +++ b/0245-hw-loongarch-virt-Add-FDT-table-support-with-acpi-ge.patch @@ -0,0 +1,84 @@ +From aefe3c66d895b51b3fd209fcc78ed44e2d55677b Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Wed, 18 Sep 2024 09:42:06 +0800 +Subject: [PATCH 255/293] hw/loongarch/virt: Add FDT table support with acpi + ged pm register + +ACPI ged is used for power management on LoongArch virt platform, in +general it is parsed from acpi table. However if system boot directly from +elf kernel, no UEFI bios is provided and acpi table cannot be used also. + +Here acpi ged pm register is exposed with FDT table, it is compatbile +with syscon method in FDT table, only that acpi ged pm register is accessed +with 8-bit mode, rather with 32-bit mode. + +Signed-off-by: Bibo Mao +Reviewed-by: Song Gao +Tested-by: Song Gao +Message-Id: <20240918014206.2165821-3-maobibo@loongson.cn> +Signed-off-by: Song Gao +Signed-off-by: Xianglai Li +--- + hw/loongarch/virt.c | 39 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 39 insertions(+) + +diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c +index 4e77901..0866022 100644 +--- a/hw/loongarch/virt.c ++++ b/hw/loongarch/virt.c +@@ -280,6 +280,44 @@ static void fdt_add_rtc_node(LoongArchVirtMachineState *lvms, + g_free(nodename); + } + ++static void fdt_add_ged_reset(LoongArchVirtMachineState *lvms) ++{ ++ char *name; ++ uint32_t ged_handle; ++ MachineState *ms = MACHINE(lvms); ++ hwaddr base = VIRT_GED_REG_ADDR; ++ hwaddr size = ACPI_GED_REG_COUNT; ++ ++ ged_handle = qemu_fdt_alloc_phandle(ms->fdt); ++ name = g_strdup_printf("/ged@%" PRIx64, base); ++ qemu_fdt_add_subnode(ms->fdt, name); ++ qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon"); ++ qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0x0, base, 0x0, size); ++ /* 8 bit registers */ ++ qemu_fdt_setprop_cell(ms->fdt, name, "reg-shift", 0); ++ qemu_fdt_setprop_cell(ms->fdt, name, "reg-io-width", 1); ++ qemu_fdt_setprop_cell(ms->fdt, name, "phandle", ged_handle); ++ ged_handle = qemu_fdt_get_phandle(ms->fdt, name); ++ g_free(name); ++ ++ name = g_strdup_printf("/reboot"); ++ qemu_fdt_add_subnode(ms->fdt, name); ++ qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon-reboot"); ++ qemu_fdt_setprop_cell(ms->fdt, name, "regmap", ged_handle); ++ qemu_fdt_setprop_cell(ms->fdt, name, "offset", ACPI_GED_REG_RESET); ++ qemu_fdt_setprop_cell(ms->fdt, name, "value", ACPI_GED_RESET_VALUE); ++ g_free(name); ++ ++ name = g_strdup_printf("/poweroff"); ++ qemu_fdt_add_subnode(ms->fdt, name); ++ qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon-poweroff"); ++ qemu_fdt_setprop_cell(ms->fdt, name, "regmap", ged_handle); ++ qemu_fdt_setprop_cell(ms->fdt, name, "offset", ACPI_GED_REG_SLEEP_CTL); ++ qemu_fdt_setprop_cell(ms->fdt, name, "value", ACPI_GED_SLP_EN | ++ (ACPI_GED_SLP_TYP_S5 << ACPI_GED_SLP_TYP_POS)); ++ g_free(name); ++} ++ + static void fdt_add_uart_node(LoongArchVirtMachineState *lvms, + uint32_t *pch_pic_phandle, hwaddr base, + int irq, bool chosen) +@@ -737,6 +775,7 @@ static void virt_devices_init(DeviceState *pch_pic, + qdev_get_gpio_in(pch_pic, + VIRT_RTC_IRQ - VIRT_GSI_BASE)); + fdt_add_rtc_node(lvms, pch_pic_phandle); ++ fdt_add_ged_reset(lvms); + + /* acpi ged */ + lvms->acpi_ged = create_acpi_ged(pch_pic, lvms); +-- +1.8.3.1 + diff --git a/0246-target-loongarch-Avoid-bits-shift-exceeding-width-of.patch b/0246-target-loongarch-Avoid-bits-shift-exceeding-width-of.patch new file mode 100644 index 0000000000000000000000000000000000000000..b9acca0cc14b263dc822713ea8cbb3152cd2fba5 --- /dev/null +++ b/0246-target-loongarch-Avoid-bits-shift-exceeding-width-of.patch @@ -0,0 +1,42 @@ +From d68ae5b853de6af35bcc601d548db09745e300ef Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Sat, 14 Sep 2024 14:46:45 +0800 +Subject: [PATCH 256/293] target/loongarch: Avoid bits shift exceeding width of + bool type + +Variable env->cf[i] is defined as bool type, it is treated as int type +with shift operation. However the max possible width is 56 for the shift +operation, exceeding the width of int type. And there is existing api +read_fcc() which is converted to u64 type with bitwise shift, it can be +used to dump fp registers into coredump note segment. + +Resolves: Coverity CID 1561133 +Signed-off-by: Bibo Mao +Reviewed-by: Richard Henderson +Message-Id: <20240914064645.2099169-1-maobibo@loongson.cn> +Signed-off-by: Song Gao +Signed-off-by: Xianglai Li +--- + target/loongarch/arch_dump.c | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +diff --git a/target/loongarch/arch_dump.c b/target/loongarch/arch_dump.c +index 4986db9..d9e1120 100644 +--- a/target/loongarch/arch_dump.c ++++ b/target/loongarch/arch_dump.c +@@ -97,11 +97,7 @@ static int loongarch_write_elf64_fprpreg(WriteCoreDumpFunction f, + + loongarch_note_init(¬e, s, "CORE", 5, NT_PRFPREG, sizeof(note.fpu)); + note.fpu.fcsr = cpu_to_dump64(s, env->fcsr0); +- +- for (i = 0; i < 8; i++) { +- note.fpu.fcc |= env->cf[i] << (8 * i); +- } +- note.fpu.fcc = cpu_to_dump64(s, note.fpu.fcc); ++ note.fpu.fcc = cpu_to_dump64(s, read_fcc(env)); + + for (i = 0; i < 32; ++i) { + note.fpu.fpr[i] = cpu_to_dump64(s, env->fpr[i].vreg.UD[0]); +-- +1.8.3.1 + diff --git a/0247-target-loongarch-Add-loongson-binary-translation-fea.patch b/0247-target-loongarch-Add-loongson-binary-translation-fea.patch new file mode 100644 index 0000000000000000000000000000000000000000..aca61f009a6e6fe42663141ec59f81a1542e5880 --- /dev/null +++ b/0247-target-loongarch-Add-loongson-binary-translation-fea.patch @@ -0,0 +1,223 @@ +From 684d6e0fb7ce89282e763dc8f2cdc829c5faefa9 Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Sun, 29 Sep 2024 15:04:04 +0800 +Subject: [PATCH 257/293] target/loongarch: Add loongson binary translation + feature + +Loongson Binary Translation (LBT) is used to accelerate binary +translation, which contains 4 scratch registers (scr0 to scr3), x86/ARM +eflags (eflags) and x87 fpu stack pointer (ftop). + +Now LBT feature is added in kvm mode, not supported in TCG mode since +it is not emulated. Feature variable lbt is added with OnOffAuto type, +If lbt feature is not supported with KVM host, it reports error if there +is lbt=on command line. + +If there is no any command line about lbt parameter, it checks whether +KVM host supports lbt feature and set the corresponding value in cpucfg. + +Signed-off-by: Bibo Mao +Reviewed-by: Song Gao +Message-Id: <20240929070405.235200-2-maobibo@loongson.cn> +Signed-off-by: Song Gao +Signed-off-by: Xianglai Li +--- + target/loongarch/cpu.c | 22 +++++++++++ + target/loongarch/cpu.h | 4 +- + target/loongarch/kvm/kvm.c | 73 +++++++++++++++++++++++++++-------- + target/loongarch/loongarch-qmp-cmds.c | 2 +- + 4 files changed, 82 insertions(+), 19 deletions(-) + +diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c +index 8fb8195..4eecd6f 100644 +--- a/target/loongarch/cpu.c ++++ b/target/loongarch/cpu.c +@@ -708,6 +708,18 @@ static void loongarch_set_lasx(Object *obj, bool value, Error **errp) + } + } + ++static bool loongarch_get_lbt(Object *obj, Error **errp) ++{ ++ return LOONGARCH_CPU(obj)->lbt != ON_OFF_AUTO_OFF; ++} ++ ++static void loongarch_set_lbt(Object *obj, bool value, Error **errp) ++{ ++ LoongArchCPU *cpu = LOONGARCH_CPU(obj); ++ ++ cpu->lbt = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; ++} ++ + static bool loongarch_get_pmu(Object *obj, Error **errp) + { + return LOONGARCH_CPU(obj)->pmu != ON_OFF_AUTO_OFF; +@@ -728,6 +740,16 @@ void loongarch_cpu_post_init(Object *obj) + loongarch_set_lsx); + object_property_add_bool(obj, "lasx", loongarch_get_lasx, + loongarch_set_lasx); ++ /* lbt is enabled only in kvm mode, not supported in tcg mode */ ++ if (kvm_enabled()) { ++ cpu->lbt = ON_OFF_AUTO_AUTO; ++ object_property_add_bool(obj, "lbt", loongarch_get_lbt, ++ loongarch_set_lbt); ++ object_property_set_description(obj, "lbt", ++ "Set off to disable Binary Tranlation."); ++ } else { ++ cpu->lbt = ON_OFF_AUTO_OFF; ++ } + + if (kvm_enabled()) { + cpu->pmu = ON_OFF_AUTO_AUTO; +diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h +index b5dc107..a0cc9b5 100644 +--- a/target/loongarch/cpu.h ++++ b/target/loongarch/cpu.h +@@ -156,6 +156,7 @@ FIELD(CPUCFG2, LLFTP_VER, 15, 3) + FIELD(CPUCFG2, LBT_X86, 18, 1) + FIELD(CPUCFG2, LBT_ARM, 19, 1) + FIELD(CPUCFG2, LBT_MIPS, 20, 1) ++FIELD(CPUCFG2, LBT_ALL, 18, 3) + FIELD(CPUCFG2, LSPW, 21, 1) + FIELD(CPUCFG2, LAM, 22, 1) + +@@ -283,7 +284,7 @@ struct LoongArchTLB { + typedef struct LoongArchTLB LoongArchTLB; + + enum loongarch_features { +- LOONGARCH_FEATURE_PMU, ++ LOONGARCH_FEATURE_LBT, /* loongson binary translation extension */ + }; + + typedef struct CPUArchState { +@@ -383,6 +384,7 @@ struct ArchCPU { + CPULoongArchState env; + QEMUTimer timer; + uint32_t phy_id; ++ OnOffAuto lbt; + OnOffAuto pmu; + + /* 'compatible' string for this CPU for Linux device trees */ +diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c +index 30ce4d7..4cf3788 100644 +--- a/target/loongarch/kvm/kvm.c ++++ b/target/loongarch/kvm/kvm.c +@@ -9,6 +9,7 @@ + #include + #include + ++#include "qapi/error.h" + #include "qemu/timer.h" + #include "qemu/error-report.h" + #include "qemu/main-loop.h" +@@ -683,17 +684,71 @@ static void kvm_loongarch_vm_stage_change(void *opaque, bool running, + } + } + ++static bool kvm_feature_supported(CPUState *cs, enum loongarch_features feature) ++{ ++ int ret; ++ struct kvm_device_attr attr; ++ ++ switch (feature) { ++ case LOONGARCH_FEATURE_LBT: ++ /* ++ * Return all if all the LBT features are supported such as: ++ * KVM_LOONGARCH_VM_FEAT_X86BT ++ * KVM_LOONGARCH_VM_FEAT_ARMBT ++ * KVM_LOONGARCH_VM_FEAT_MIPSBT ++ */ ++ attr.group = KVM_LOONGARCH_VM_FEAT_CTRL; ++ attr.attr = KVM_LOONGARCH_VM_FEAT_X86BT; ++ ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); ++ attr.attr = KVM_LOONGARCH_VM_FEAT_ARMBT; ++ ret |= kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); ++ attr.attr = KVM_LOONGARCH_VM_FEAT_MIPSBT; ++ ret |= kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); ++ return (ret == 0); ++ default: ++ return false; ++ } ++} ++ ++static int kvm_cpu_check_lbt(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_LBT); ++ if (cpu->lbt == ON_OFF_AUTO_ON) { ++ if (kvm_supported) { ++ env->cpucfg[2] = FIELD_DP32(env->cpucfg[2], CPUCFG2, LBT_ALL, 7); ++ } else { ++ error_setg(errp, "'lbt' feature not supported by KVM on this host"); ++ return -ENOTSUP; ++ } ++ } else if ((cpu->lbt == ON_OFF_AUTO_AUTO) && kvm_supported) { ++ env->cpucfg[2] = FIELD_DP32(env->cpucfg[2], CPUCFG2, LBT_ALL, 7); ++ } ++ ++ return 0; ++} ++ + int kvm_arch_init_vcpu(CPUState *cs) + { + uint64_t val; ++ int ret; ++ Error *local_err = NULL; + ++ ret = 0; + qemu_add_vm_change_state_handler(kvm_loongarch_vm_stage_change, cs); + + if (!kvm_get_one_reg(cs, KVM_REG_LOONGARCH_DEBUG_INST, &val)) { + brk_insn = val; + } + +- return 0; ++ ret = kvm_cpu_check_lbt(cs, &local_err); ++ if (ret < 0) { ++ error_report_err(local_err); ++ } ++ return ret; + } + + int kvm_arch_destroy_vcpu(CPUState *cs) +@@ -883,22 +938,6 @@ int kvm_loongarch_set_interrupt(LoongArchCPU *cpu, int irq, int level) + return kvm_vcpu_ioctl(cs, KVM_INTERRUPT, &intr); + } + +-bool kvm_feature_supported(CPUState *cs, enum loongarch_features feature) +-{ +- struct kvm_device_attr attr; +- int ret; +- +- switch (feature) { +- case LOONGARCH_FEATURE_PMU: +- attr.group = KVM_LOONGARCH_VM_FEAT_CTRL; +- attr.attr = KVM_LOONGARCH_VM_FEAT_PMU; +- ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); +- return (ret == 0); +- default: +- return false; +- } +-} +- + void kvm_arch_accel_class_init(ObjectClass *oc) + { + } +diff --git a/target/loongarch/loongarch-qmp-cmds.c b/target/loongarch/loongarch-qmp-cmds.c +index de92ec6..dc78a3f 100644 +--- a/target/loongarch/loongarch-qmp-cmds.c ++++ b/target/loongarch/loongarch-qmp-cmds.c +@@ -42,7 +42,7 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) + } + + static const char *cpu_model_advertised_features[] = { +- "lsx", "lasx", "pmu", NULL ++ "lsx", "lasx", "lbt", "pmu", NULL + }; + + CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, +-- +1.8.3.1 + diff --git a/0248-target-loongarch-Implement-lbt-registers-save-restor.patch b/0248-target-loongarch-Implement-lbt-registers-save-restor.patch new file mode 100644 index 0000000000000000000000000000000000000000..951337ef55ae636d99565b645309ef8f7c850b55 --- /dev/null +++ b/0248-target-loongarch-Implement-lbt-registers-save-restor.patch @@ -0,0 +1,184 @@ +From 8867580f618a28bf58d665bc045898d825ef7715 Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Sun, 29 Sep 2024 15:04:05 +0800 +Subject: [PATCH 258/293] target/loongarch: Implement lbt registers + save/restore function + +Six registers scr0 - scr3, eflags and ftop are added in percpu vmstate. +And two functions kvm_loongarch_get_lbt/kvm_loongarch_put_lbt are added +to save/restore lbt registers. + +Signed-off-by: Bibo Mao +Reviewed-by: Song Gao +Message-Id: <20240929070405.235200-3-maobibo@loongson.cn> +Signed-off-by: Song Gao +Signed-off-by: Xianglai Li +--- + target/loongarch/cpu.h | 12 +++++++++ + target/loongarch/kvm/kvm.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++ + target/loongarch/machine.c | 25 +++++++++++++++++++ + 3 files changed, 99 insertions(+) + +diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h +index a0cc9b5..41f9521 100644 +--- a/target/loongarch/cpu.h ++++ b/target/loongarch/cpu.h +@@ -287,6 +287,17 @@ enum loongarch_features { + LOONGARCH_FEATURE_LBT, /* loongson binary translation extension */ + }; + ++typedef struct LoongArchBT { ++ /* scratch registers */ ++ uint64_t scr0; ++ uint64_t scr1; ++ uint64_t scr2; ++ uint64_t scr3; ++ /* loongarch eflags */ ++ uint32_t eflags; ++ uint32_t ftop; ++} lbt_t; ++ + typedef struct CPUArchState { + uint64_t gpr[32]; + uint64_t pc; +@@ -296,6 +307,7 @@ typedef struct CPUArchState { + bool cf[8]; + + uint32_t fcsr0; ++ lbt_t lbt; + uint32_t fcsr0_mask; + + uint32_t cpucfg[21]; +diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c +index 4cf3788..111ef50 100644 +--- a/target/loongarch/kvm/kvm.c ++++ b/target/loongarch/kvm/kvm.c +@@ -486,6 +486,58 @@ static int kvm_loongarch_put_regs_fp(CPUState *cs) + return ret; + } + ++static int kvm_loongarch_put_lbt(CPUState *cs) ++{ ++ CPULoongArchState *env = cpu_env(cs); ++ uint64_t val; ++ int ret; ++ ++ /* check whether vm support LBT firstly */ ++ if (FIELD_EX32(env->cpucfg[2], CPUCFG2, LBT_ALL) != 7) { ++ return 0; ++ } ++ ++ /* set six LBT registers including scr0-scr3, eflags, ftop */ ++ ret = kvm_set_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR0, &env->lbt.scr0); ++ ret |= kvm_set_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR1, &env->lbt.scr1); ++ ret |= kvm_set_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR2, &env->lbt.scr2); ++ ret |= kvm_set_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR3, &env->lbt.scr3); ++ /* ++ * Be cautious, KVM_REG_LOONGARCH_LBT_FTOP is defined as 64-bit however ++ * lbt.ftop is 32-bit; the same with KVM_REG_LOONGARCH_LBT_EFLAGS register ++ */ ++ val = env->lbt.eflags; ++ ret |= kvm_set_one_reg(cs, KVM_REG_LOONGARCH_LBT_EFLAGS, &val); ++ val = env->lbt.ftop; ++ ret |= kvm_set_one_reg(cs, KVM_REG_LOONGARCH_LBT_FTOP, &val); ++ ++ return ret; ++} ++ ++static int kvm_loongarch_get_lbt(CPUState *cs) ++{ ++ CPULoongArchState *env = cpu_env(cs); ++ uint64_t val; ++ int ret; ++ ++ /* check whether vm support LBT firstly */ ++ if (FIELD_EX32(env->cpucfg[2], CPUCFG2, LBT_ALL) != 7) { ++ return 0; ++ } ++ ++ /* get six LBT registers including scr0-scr3, eflags, ftop */ ++ ret = kvm_get_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR0, &env->lbt.scr0); ++ ret |= kvm_get_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR1, &env->lbt.scr1); ++ ret |= kvm_get_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR2, &env->lbt.scr2); ++ ret |= kvm_get_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR3, &env->lbt.scr3); ++ ret |= kvm_get_one_reg(cs, KVM_REG_LOONGARCH_LBT_EFLAGS, &val); ++ env->lbt.eflags = (uint32_t)val; ++ ret |= kvm_get_one_reg(cs, KVM_REG_LOONGARCH_LBT_FTOP, &val); ++ env->lbt.ftop = (uint32_t)val; ++ ++ return ret; ++} ++ + void kvm_arch_reset_vcpu(CPUState *cs) + { + CPULoongArchState *env = cpu_env(cs); +@@ -625,6 +677,11 @@ int kvm_arch_get_registers(CPUState *cs) + return ret; + } + ++ ret = kvm_loongarch_get_lbt(cs); ++ if (ret) { ++ return ret; ++ } ++ + ret = kvm_loongarch_get_mpstate(cs); + if (ret) { + return ret; +@@ -653,6 +710,11 @@ int kvm_arch_put_registers(CPUState *cs, int level) + return ret; + } + ++ ret = kvm_loongarch_put_lbt(cs); ++ if (ret) { ++ return ret; ++ } ++ + ret = kvm_loongarch_put_mpstate(cs); + if (ret) { + return ret; +diff --git a/target/loongarch/machine.c b/target/loongarch/machine.c +index 5a7df71..fc666a6 100644 +--- a/target/loongarch/machine.c ++++ b/target/loongarch/machine.c +@@ -109,6 +109,30 @@ static const VMStateDescription vmstate_lasx = { + }, + }; + ++static bool lbt_needed(void *opaque) ++{ ++ LoongArchCPU *cpu = opaque; ++ ++ return !!FIELD_EX64(cpu->env.cpucfg[2], CPUCFG2, LBT_ALL); ++} ++ ++static const VMStateDescription vmstate_lbt = { ++ .name = "cpu/lbt", ++ .version_id = 0, ++ .minimum_version_id = 0, ++ .needed = lbt_needed, ++ .fields = (const VMStateField[]) { ++ VMSTATE_UINT64(env.lbt.scr0, LoongArchCPU), ++ VMSTATE_UINT64(env.lbt.scr1, LoongArchCPU), ++ VMSTATE_UINT64(env.lbt.scr2, LoongArchCPU), ++ VMSTATE_UINT64(env.lbt.scr3, LoongArchCPU), ++ VMSTATE_UINT32(env.lbt.eflags, LoongArchCPU), ++ VMSTATE_UINT32(env.lbt.ftop, LoongArchCPU), ++ VMSTATE_END_OF_LIST() ++ }, ++}; ++ ++ + /* TLB state */ + const VMStateDescription vmstate_tlb = { + .name = "cpu/tlb", +@@ -199,6 +223,7 @@ const VMStateDescription vmstate_loongarch_cpu = { + &vmstate_fpu, + &vmstate_lsx, + &vmstate_lasx, ++ &vmstate_lbt, + NULL + } + }; +-- +1.8.3.1 + diff --git a/0249-target-loongarch-kvm-Implement-LoongArch-PMU-extensi.patch b/0249-target-loongarch-kvm-Implement-LoongArch-PMU-extensi.patch new file mode 100644 index 0000000000000000000000000000000000000000..73a21638e5e6f6c927cab45cdf7efaeeb66852bb --- /dev/null +++ b/0249-target-loongarch-kvm-Implement-LoongArch-PMU-extensi.patch @@ -0,0 +1,210 @@ +From 4ecd0fda563d6267e802526cb57067d8db12c102 Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Wed, 18 Sep 2024 16:23:15 +0800 +Subject: [PATCH 259/293] target/loongarch/kvm: Implement LoongArch PMU + extension + +Implement PMU extension for LoongArch kvm mode. Use OnOffAuto type +variable pmu to check the PMU feature. If the PMU Feature is not supported +with KVM host, it reports error if there is pmu=on command line. + +If there is no any command line about pmu parameter, it checks whether +KVM host supports the PMU Feature and set the corresponding value in cpucfg. + +This patch is based on lbt patch located at + https://lore.kernel.org/qemu-devel/20240904061859.86615-1-maobibo@loongson.cn + +Co-developed-by: Song Gao +Signed-off-by: Bibo Mao +Reviewed-by: Song Gao +Message-Id: <20240918082315.2345034-1-maobibo@loongson.cn> +Signed-off-by: Song Gao +Signed-off-by: Xianglai Li +--- + target/loongarch/cpu.c | 42 +++--------------------------------- + target/loongarch/cpu.h | 1 + + target/loongarch/kvm/kvm.c | 41 +++++++++++++++++++++++++++++++++++ + target/loongarch/kvm/kvm_loongarch.h | 16 -------------- + 4 files changed, 45 insertions(+), 55 deletions(-) + +diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c +index 4eecd6f..130b0c7 100644 +--- a/target/loongarch/cpu.c ++++ b/target/loongarch/cpu.c +@@ -602,35 +602,6 @@ static void loongarch_cpu_disas_set_info(CPUState *s, disassemble_info *info) + info->print_insn = print_insn_loongarch; + } + +-static void loongarch_cpu_check_pmu(CPUState *cs, Error **errp) +-{ +- LoongArchCPU *cpu = LOONGARCH_CPU(cs); +- bool kvm_supported; +- +- kvm_supported = kvm_feature_supported(cs, LOONGARCH_FEATURE_PMU); +- if (cpu->pmu == ON_OFF_AUTO_ON) { +- if (kvm_supported) { +- cpu->env.cpucfg[6] = FIELD_DP32(cpu->env.cpucfg[6], CPUCFG6, PMP, 1); +- cpu->env.cpucfg[6] = FIELD_DP32(cpu->env.cpucfg[6], CPUCFG6, PMNUM, 3); +- cpu->env.cpucfg[6] = FIELD_DP32(cpu->env.cpucfg[6], CPUCFG6, PMBITS, 63); +- cpu->env.cpucfg[6] = FIELD_DP32(cpu->env.cpucfg[6], CPUCFG6, UPM, 1); +- } else { +- error_setg(errp, "'pmu' feature not supported by KVM on this host."); +- return; +- } +- } else if ((cpu->pmu == ON_OFF_AUTO_AUTO) && kvm_supported) { +- cpu->env.cpucfg[6] = FIELD_DP32(cpu->env.cpucfg[6], CPUCFG6, PMP, 1); +- cpu->env.cpucfg[6] = FIELD_DP32(cpu->env.cpucfg[6], CPUCFG6, PMNUM, 3); +- cpu->env.cpucfg[6] = FIELD_DP32(cpu->env.cpucfg[6], CPUCFG6, PMBITS, 63); +- cpu->env.cpucfg[6] = FIELD_DP32(cpu->env.cpucfg[6], CPUCFG6, UPM, 1); +- } +-} +- +-static void loongarch_cpu_feature_realize(CPUState *cs, Error **errp) +-{ +- loongarch_cpu_check_pmu(cs, errp); +-} +- + static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) + { + CPUState *cs = CPU(dev); +@@ -644,11 +615,6 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) + } + + loongarch_cpu_register_gdb_regs_for_features(cs); +- loongarch_cpu_feature_realize(cs, &local_err); +- if (local_err != NULL) { +- error_propagate(errp, local_err); +- return; +- } + + cpu_reset(cs); + qemu_init_vcpu(cs); +@@ -747,16 +713,14 @@ void loongarch_cpu_post_init(Object *obj) + loongarch_set_lbt); + object_property_set_description(obj, "lbt", + "Set off to disable Binary Tranlation."); +- } else { +- cpu->lbt = ON_OFF_AUTO_OFF; +- } + +- if (kvm_enabled()) { + cpu->pmu = ON_OFF_AUTO_AUTO; + object_property_add_bool(obj, "pmu", loongarch_get_pmu, + loongarch_set_pmu); ++ object_property_set_description(obj, "pmu", ++ "Set off to performance monitor unit."); + } else { +- cpu->pmu = ON_OFF_AUTO_OFF; ++ cpu->lbt = ON_OFF_AUTO_OFF; + } + } + +diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h +index 41f9521..a9c2693 100644 +--- a/target/loongarch/cpu.h ++++ b/target/loongarch/cpu.h +@@ -285,6 +285,7 @@ typedef struct LoongArchTLB LoongArchTLB; + + enum loongarch_features { + LOONGARCH_FEATURE_LBT, /* loongson binary translation extension */ ++ LOONGARCH_FEATURE_PMU, + }; + + typedef struct LoongArchBT { +diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c +index 111ef50..df7ae65 100644 +--- a/target/loongarch/kvm/kvm.c ++++ b/target/loongarch/kvm/kvm.c +@@ -767,9 +767,18 @@ static bool kvm_feature_supported(CPUState *cs, enum loongarch_features feature) + attr.attr = KVM_LOONGARCH_VM_FEAT_MIPSBT; + ret |= kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); + return (ret == 0); ++ ++ case LOONGARCH_FEATURE_PMU: ++ attr.group = KVM_LOONGARCH_VM_FEAT_CTRL; ++ attr.attr = KVM_LOONGARCH_VM_FEAT_PMU; ++ ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); ++ return (ret == 0); ++ + default: + return false; + } ++ ++ return false; + } + + static int kvm_cpu_check_lbt(CPUState *cs, Error **errp) +@@ -793,6 +802,32 @@ static int kvm_cpu_check_lbt(CPUState *cs, Error **errp) + return 0; + } + ++static int kvm_cpu_check_pmu(CPUState *cs, Error **errp) ++{ ++ LoongArchCPU *cpu = LOONGARCH_CPU(cs); ++ CPULoongArchState *env = cpu_env(cs); ++ bool kvm_supported; ++ ++ kvm_supported = kvm_feature_supported(cs, LOONGARCH_FEATURE_PMU); ++ if (cpu->pmu == ON_OFF_AUTO_ON) { ++ if (!kvm_supported) { ++ error_setg(errp, "'pmu' feature not supported by KVM on the host"); ++ return -ENOTSUP; ++ } ++ } else if (cpu->pmu != ON_OFF_AUTO_AUTO) { ++ /* disable pmu if ON_OFF_AUTO_OFF is set */ ++ kvm_supported = false; ++ } ++ ++ if (kvm_supported) { ++ env->cpucfg[6] = FIELD_DP32(env->cpucfg[6], CPUCFG6, PMP, 1); ++ env->cpucfg[6] = FIELD_DP32(env->cpucfg[6], CPUCFG6, PMNUM, 3); ++ env->cpucfg[6] = FIELD_DP32(env->cpucfg[6], CPUCFG6, PMBITS, 63); ++ env->cpucfg[6] = FIELD_DP32(env->cpucfg[6], CPUCFG6, UPM, 1); ++ } ++ return 0; ++} ++ + int kvm_arch_init_vcpu(CPUState *cs) + { + uint64_t val; +@@ -810,6 +845,12 @@ int kvm_arch_init_vcpu(CPUState *cs) + if (ret < 0) { + error_report_err(local_err); + } ++ ++ ret = kvm_cpu_check_pmu(cs, &local_err); ++ if (ret < 0) { ++ error_report_err(local_err); ++ } ++ + return ret; + } + +diff --git a/target/loongarch/kvm/kvm_loongarch.h b/target/loongarch/kvm/kvm_loongarch.h +index 506a5c9..1051a34 100644 +--- a/target/loongarch/kvm/kvm_loongarch.h ++++ b/target/loongarch/kvm/kvm_loongarch.h +@@ -13,20 +13,4 @@ + int kvm_loongarch_set_interrupt(LoongArchCPU *cpu, int irq, int level); + void kvm_arch_reset_vcpu(CPUState *cs); + +-#ifdef CONFIG_KVM +-/* +- * kvm_feature_supported: +- * +- * Returns: true if KVM supports specified feature +- * and false otherwise. +- */ +-bool kvm_feature_supported(CPUState *cs, enum loongarch_features feature); +-#else +-static inline bool kvm_feature_supported(CPUState *cs, +- enum loongarch_features feature) +-{ +- return false; +-} +-#endif +- + #endif +-- +1.8.3.1 + diff --git a/0250-linux-headers-loongarch-Add-kvm_para.h-and-unistd_64.patch b/0250-linux-headers-loongarch-Add-kvm_para.h-and-unistd_64.patch new file mode 100644 index 0000000000000000000000000000000000000000..fa86ea03208b949f570610b5b5d561c62579c064 --- /dev/null +++ b/0250-linux-headers-loongarch-Add-kvm_para.h-and-unistd_64.patch @@ -0,0 +1,40 @@ +From 8bf8aa8a3f8bb2b56c2dc88ae4ff02ab7cdae6e9 Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Thu, 17 Oct 2024 10:07:07 +0800 +Subject: [PATCH 260/293] linux-headers: loongarch: Add kvm_para.h and + unistd_64.h + +KVM LBT supports on LoongArch depends on the linux-header file +kvm_para.h, also unistd_64.h is required by unistd.h on LoongArch +since 6.11, otherwise there will be compiling error such as: + +linux-headers/asm/unistd.h:3:10: fatal error: asm/unistd_64.h: No such file or directory + #include + +Signed-off-by: Bibo Mao +Acked-by: Song Gao +Message-Id: <20241017020708.1728620-2-maobibo@loongson.cn> +Signed-off-by: Song Gao +Signed-off-by: Xianglai Li +--- + scripts/update-linux-headers.sh | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh +index 34295c0..88c76b8 100755 +--- a/scripts/update-linux-headers.sh ++++ b/scripts/update-linux-headers.sh +@@ -156,6 +156,10 @@ for arch in $ARCHLIST; do + cp_portable "$tmpdir/bootparam.h" \ + "$output/include/standard-headers/asm-$arch" + fi ++ if [ $arch = loongarch ]; then ++ cp "$hdrdir/include/asm/kvm_para.h" "$output/linux-headers/asm-loongarch/" ++ cp "$hdrdir/include/asm/unistd_64.h" "$output/linux-headers/asm-loongarch/" ++ fi + done + + rm -rf "$output/linux-headers/linux" +-- +1.8.3.1 + diff --git a/0251-target-loongarch-Add-steal-time-support-on-migration.patch b/0251-target-loongarch-Add-steal-time-support-on-migration.patch new file mode 100644 index 0000000000000000000000000000000000000000..d2479bbb924102fb649b393c0390ea0e194858bd --- /dev/null +++ b/0251-target-loongarch-Add-steal-time-support-on-migration.patch @@ -0,0 +1,152 @@ +From 643c325e6a3bdca7b69eee8b567a164a55ad4469 Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Mon, 30 Sep 2024 14:40:40 +0800 +Subject: [PATCH 261/293] target/loongarch: Add steal time support on migration + +With pv steal time supported, VM machine needs get physical address +of each vcpu and notify new host during migration. Here two +functions kvm_get_stealtime/kvm_set_stealtime, and guest steal time +physical address is only updated on KVM_PUT_FULL_STATE stage. + +Signed-off-by: Bibo Mao +Reviewed-by: Song Gao +Message-Id: <20240930064040.753929-1-maobibo@loongson.cn> +Signed-off-by: Song Gao +Signed-off-by: Xianglai Li +--- + target/loongarch/cpu.h | 3 +++ + target/loongarch/kvm/kvm.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++ + target/loongarch/machine.c | 6 +++-- + 3 files changed, 72 insertions(+), 2 deletions(-) + +diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h +index a9c2693..d922fe3 100644 +--- a/target/loongarch/cpu.h ++++ b/target/loongarch/cpu.h +@@ -371,6 +371,9 @@ typedef struct CPUArchState { + uint64_t CSR_DBG; + uint64_t CSR_DERA; + uint64_t CSR_DSAVE; ++ struct { ++ uint64_t guest_addr; ++ } stealtime; + + #ifndef CONFIG_USER_ONLY + LoongArchTLB tlb[LOONGARCH_TLB_MAX]; +diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c +index df7ae65..f98abf0 100644 +--- a/target/loongarch/kvm/kvm.c ++++ b/target/loongarch/kvm/kvm.c +@@ -35,6 +35,55 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { + KVM_CAP_LAST_INFO + }; + ++static int kvm_get_stealtime(CPUState *cs) ++{ ++ CPULoongArchState *env = cpu_env(cs); ++ int err; ++ struct kvm_device_attr attr = { ++ .group = KVM_LOONGARCH_VCPU_PVTIME_CTRL, ++ .attr = KVM_LOONGARCH_VCPU_PVTIME_GPA, ++ .addr = (uint64_t)&env->stealtime.guest_addr, ++ }; ++ ++ err = kvm_vcpu_ioctl(cs, KVM_HAS_DEVICE_ATTR, attr); ++ if (err) { ++ return 0; ++ } ++ ++ err = kvm_vcpu_ioctl(cs, KVM_GET_DEVICE_ATTR, attr); ++ if (err) { ++ error_report("PVTIME: KVM_GET_DEVICE_ATTR: %s", strerror(errno)); ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int kvm_set_stealtime(CPUState *cs) ++{ ++ CPULoongArchState *env = cpu_env(cs); ++ int err; ++ struct kvm_device_attr attr = { ++ .group = KVM_LOONGARCH_VCPU_PVTIME_CTRL, ++ .attr = KVM_LOONGARCH_VCPU_PVTIME_GPA, ++ .addr = (uint64_t)&env->stealtime.guest_addr, ++ }; ++ ++ err = kvm_vcpu_ioctl(cs, KVM_HAS_DEVICE_ATTR, attr); ++ if (err) { ++ return 0; ++ } ++ ++ err = kvm_vcpu_ioctl(cs, KVM_SET_DEVICE_ATTR, attr); ++ if (err) { ++ error_report("PVTIME: KVM_SET_DEVICE_ATTR %s with gpa "TARGET_FMT_lx, ++ strerror(errno), env->stealtime.guest_addr); ++ return err; ++ } ++ ++ return 0; ++} ++ + static int kvm_loongarch_get_regs_core(CPUState *cs) + { + int ret = 0; +@@ -682,6 +731,11 @@ int kvm_arch_get_registers(CPUState *cs) + return ret; + } + ++ ret = kvm_get_stealtime(cs); ++ if (ret) { ++ return ret; ++ } ++ + ret = kvm_loongarch_get_mpstate(cs); + if (ret) { + return ret; +@@ -715,6 +769,17 @@ int kvm_arch_put_registers(CPUState *cs, int level) + return ret; + } + ++ if (level >= KVM_PUT_FULL_STATE) { ++ /* ++ * only KVM_PUT_FULL_STATE is required, kvm kernel will clear ++ * guest_addr for KVM_PUT_RESET_STATE ++ */ ++ ret = kvm_set_stealtime(cs); ++ if (ret) { ++ return ret; ++ } ++ } ++ + ret = kvm_loongarch_put_mpstate(cs); + if (ret) { + return ret; +diff --git a/target/loongarch/machine.c b/target/loongarch/machine.c +index fc666a6..dc76845 100644 +--- a/target/loongarch/machine.c ++++ b/target/loongarch/machine.c +@@ -149,8 +149,8 @@ const VMStateDescription vmstate_tlb = { + /* LoongArch CPU state */ + const VMStateDescription vmstate_loongarch_cpu = { + .name = "cpu", +- .version_id = 2, +- .minimum_version_id = 2, ++ .version_id = 3, ++ .minimum_version_id = 3, + .fields = (VMStateField[]) { + VMSTATE_UINTTL_ARRAY(env.gpr, LoongArchCPU, 32), + VMSTATE_UINTTL(env.pc, LoongArchCPU), +@@ -216,6 +216,8 @@ const VMStateDescription vmstate_loongarch_cpu = { + 0, vmstate_tlb, LoongArchTLB), + + VMSTATE_UINT64(kvm_state_counter, LoongArchCPU), ++ /* PV steal time */ ++ VMSTATE_UINT64(env.stealtime.guest_addr, LoongArchCPU), + + VMSTATE_END_OF_LIST() + }, +-- +1.8.3.1 + diff --git a/0252-sync-kernel-headers.patch b/0252-sync-kernel-headers.patch new file mode 100644 index 0000000000000000000000000000000000000000..09f4cba3ac213c7ef42c40d2cd8b2351bec007b3 --- /dev/null +++ b/0252-sync-kernel-headers.patch @@ -0,0 +1,116 @@ +From e4444965b9e1fdf8cfc86c7a748bcb28f1476b55 Mon Sep 17 00:00:00 2001 +From: Xianglai Li +Date: Sat, 26 Oct 2024 17:08:30 +0800 +Subject: [PATCH 262/293] sync kernel headers + +Signed-off-by: Xianglai Li +--- + linux-headers/asm-loongarch/bitsperlong.h | 8 +++++++ + linux-headers/asm-loongarch/kvm.h | 40 ++++++++++++++++++++++++++++--- + linux-headers/asm-loongarch/unistd.h | 1 + + 3 files changed, 46 insertions(+), 3 deletions(-) + +diff --git a/linux-headers/asm-loongarch/bitsperlong.h b/linux-headers/asm-loongarch/bitsperlong.h +index 6dc0bb0..00b4ba1 100644 +--- a/linux-headers/asm-loongarch/bitsperlong.h ++++ b/linux-headers/asm-loongarch/bitsperlong.h +@@ -1 +1,9 @@ ++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ ++#ifndef __ASM_LOONGARCH_BITSPERLONG_H ++#define __ASM_LOONGARCH_BITSPERLONG_H ++ ++#define __BITS_PER_LONG (__SIZEOF_LONG__ * 8) ++ + #include ++ ++#endif /* __ASM_LOONGARCH_BITSPERLONG_H */ +diff --git a/linux-headers/asm-loongarch/kvm.h b/linux-headers/asm-loongarch/kvm.h +index b40b640..d619b94 100644 +--- a/linux-headers/asm-loongarch/kvm.h ++++ b/linux-headers/asm-loongarch/kvm.h +@@ -19,8 +19,10 @@ + + #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 + #define KVM_DIRTY_LOG_PAGE_OFFSET 64 ++#define __KVM_HAVE_IRQ_LINE + + #define KVM_GUESTDBG_USE_SW_BP 0x00010000 ++ + /* + * for KVM_GET_REGS and KVM_SET_REGS + */ +@@ -66,6 +68,7 @@ struct kvm_fpu { + #define KVM_REG_LOONGARCH_KVM (KVM_REG_LOONGARCH | 0x20000ULL) + #define KVM_REG_LOONGARCH_FPSIMD (KVM_REG_LOONGARCH | 0x30000ULL) + #define KVM_REG_LOONGARCH_CPUCFG (KVM_REG_LOONGARCH | 0x40000ULL) ++#define KVM_REG_LOONGARCH_LBT (KVM_REG_LOONGARCH | 0x50000ULL) + #define KVM_REG_LOONGARCH_MASK (KVM_REG_LOONGARCH | 0x70000ULL) + #define KVM_CSR_IDX_MASK 0x7fff + #define KVM_CPUCFG_IDX_MASK 0x7fff +@@ -79,14 +82,34 @@ struct kvm_fpu { + /* Debugging: Special instruction for software breakpoint */ + #define KVM_REG_LOONGARCH_DEBUG_INST (KVM_REG_LOONGARCH_KVM | KVM_REG_SIZE_U64 | 3) + ++/* LBT registers */ ++#define KVM_REG_LOONGARCH_LBT_SCR0 (KVM_REG_LOONGARCH_LBT | KVM_REG_SIZE_U64 | 1) ++#define KVM_REG_LOONGARCH_LBT_SCR1 (KVM_REG_LOONGARCH_LBT | KVM_REG_SIZE_U64 | 2) ++#define KVM_REG_LOONGARCH_LBT_SCR2 (KVM_REG_LOONGARCH_LBT | KVM_REG_SIZE_U64 | 3) ++#define KVM_REG_LOONGARCH_LBT_SCR3 (KVM_REG_LOONGARCH_LBT | KVM_REG_SIZE_U64 | 4) ++#define KVM_REG_LOONGARCH_LBT_EFLAGS (KVM_REG_LOONGARCH_LBT | KVM_REG_SIZE_U64 | 5) ++#define KVM_REG_LOONGARCH_LBT_FTOP (KVM_REG_LOONGARCH_LBT | KVM_REG_SIZE_U64 | 6) ++ + #define LOONGARCH_REG_SHIFT 3 + #define LOONGARCH_REG_64(TYPE, REG) (TYPE | KVM_REG_SIZE_U64 | (REG << LOONGARCH_REG_SHIFT)) + #define KVM_IOC_CSRID(REG) LOONGARCH_REG_64(KVM_REG_LOONGARCH_CSR, REG) + #define KVM_IOC_CPUCFG(REG) LOONGARCH_REG_64(KVM_REG_LOONGARCH_CPUCFG, REG) +-#define KVM_LOONGARCH_VCPU_CPUCFG 0 + +-#define KVM_LOONGARCH_VM_FEAT_CTRL 1000 +-#define KVM_LOONGARCH_VM_FEAT_PMU 1000 ++/* Device Control API on vm fd */ ++#define KVM_LOONGARCH_VM_FEAT_CTRL 0 ++#define KVM_LOONGARCH_VM_FEAT_LSX 0 ++#define KVM_LOONGARCH_VM_FEAT_LASX 1 ++#define KVM_LOONGARCH_VM_FEAT_X86BT 2 ++#define KVM_LOONGARCH_VM_FEAT_ARMBT 3 ++#define KVM_LOONGARCH_VM_FEAT_MIPSBT 4 ++#define KVM_LOONGARCH_VM_FEAT_PMU 5 ++#define KVM_LOONGARCH_VM_FEAT_PV_IPI 6 ++#define KVM_LOONGARCH_VM_FEAT_PV_STEALTIME 7 ++ ++/* Device Control API on vcpu fd */ ++#define KVM_LOONGARCH_VCPU_CPUCFG 0 ++#define KVM_LOONGARCH_VCPU_PVTIME_CTRL 1 ++#define KVM_LOONGARCH_VCPU_PVTIME_GPA 0 + + struct kvm_debug_exit_arch { + }; +@@ -113,4 +136,15 @@ struct kvm_iocsr_entry { + #define KVM_IRQCHIP_NUM_PINS 64 + #define KVM_MAX_CORES 256 + ++#define KVM_LOONGARCH_VM_HAVE_IRQCHIP 0x40000001 ++ ++#define KVM_DEV_LOONGARCH_IPI_GRP_REGS 0x40000002 ++ ++#define KVM_DEV_LOONGARCH_EXTIOI_GRP_REGS 0x40000003 ++ ++#define KVM_DEV_LOONGARCH_PCH_PIC_GRP_CTRL 0x40000004 ++#define KVM_DEV_LOONGARCH_PCH_PIC_CTRL_INIT 0 ++ ++#define KVM_DEV_LOONGARCH_PCH_PIC_GRP_REGS 0x40000005 ++ + #endif /* __UAPI_ASM_LOONGARCH_KVM_H */ +diff --git a/linux-headers/asm-loongarch/unistd.h b/linux-headers/asm-loongarch/unistd.h +index fcb6689..b344b1f 100644 +--- a/linux-headers/asm-loongarch/unistd.h ++++ b/linux-headers/asm-loongarch/unistd.h +@@ -1,4 +1,5 @@ + /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ ++#define __ARCH_WANT_NEW_STAT + #define __ARCH_WANT_SYS_CLONE + #define __ARCH_WANT_SYS_CLONE3 + +-- +1.8.3.1 + diff --git a/0253-accel-kvm-Extract-common-KVM-vCPU-creation-parking-c.patch b/0253-accel-kvm-Extract-common-KVM-vCPU-creation-parking-c.patch new file mode 100644 index 0000000000000000000000000000000000000000..7508c965933068e2bbaeff5ea8e62996eb6b4132 --- /dev/null +++ b/0253-accel-kvm-Extract-common-KVM-vCPU-creation-parking-c.patch @@ -0,0 +1,234 @@ +From 85a7a5758fb7584de140d3847bd327362ccf0925 Mon Sep 17 00:00:00 2001 +From: Salil Mehta +Date: Tue, 16 Jul 2024 12:14:56 +0100 +Subject: [PATCH 263/293] accel/kvm: Extract common KVM vCPU {creation,parking} + code + +KVM vCPU creation is done once during the vCPU realization when Qemu vCPU thread +is spawned. This is common to all the architectures as of now. + +Hot-unplug of vCPU results in destruction of the vCPU object in QOM but the +corresponding KVM vCPU object in the Host KVM is not destroyed as KVM doesn't +support vCPU removal. Therefore, its representative KVM vCPU object/context in +Qemu is parked. + +Refactor architecture common logic so that some APIs could be reused by vCPU +Hotplug code of some architectures likes ARM, Loongson etc. Update new/old APIs +with trace events. New APIs qemu_{create,park,unpark}_vcpu() can be externally +called. No functional change is intended here. + +Signed-off-by: Salil Mehta +Reviewed-by: Gavin Shan +Tested-by: Vishnu Pajjuri +Reviewed-by: Jonathan Cameron +Tested-by: Xianglai Li +Tested-by: Miguel Luis +Reviewed-by: Shaoqin Huang +Reviewed-by: Vishnu Pajjuri +Reviewed-by: Nicholas Piggin +Tested-by: Zhao Liu +Reviewed-by: Zhao Liu +Reviewed-by: Harsh Prateek Bora +Reviewed-by: Igor Mammedov +Message-Id: <20240716111502.202344-2-salil.mehta@huawei.com> +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +Signed-off-by: Xianglai Li +--- + accel/kvm/kvm-all.c | 95 +++++++++++++++++++++++++++++++++----------------- + accel/kvm/trace-events | 4 +++ + include/sysemu/kvm.h | 25 +++++++++++++ + 3 files changed, 92 insertions(+), 32 deletions(-) + +diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c +index 25d23bb..169d828 100644 +--- a/accel/kvm/kvm-all.c ++++ b/accel/kvm/kvm-all.c +@@ -325,14 +325,71 @@ err: + return ret; + } + ++void kvm_park_vcpu(CPUState *cpu) ++{ ++ struct KVMParkedVcpu *vcpu; ++ ++ trace_kvm_park_vcpu(cpu->cpu_index, kvm_arch_vcpu_id(cpu)); ++ ++ vcpu = g_malloc0(sizeof(*vcpu)); ++ vcpu->vcpu_id = kvm_arch_vcpu_id(cpu); ++ vcpu->kvm_fd = cpu->kvm_fd; ++ QLIST_INSERT_HEAD(&kvm_state->kvm_parked_vcpus, vcpu, node); ++} ++ ++int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id) ++{ ++ struct KVMParkedVcpu *cpu; ++ int kvm_fd = -ENOENT; ++ ++ QLIST_FOREACH(cpu, &s->kvm_parked_vcpus, node) { ++ if (cpu->vcpu_id == vcpu_id) { ++ QLIST_REMOVE(cpu, node); ++ kvm_fd = cpu->kvm_fd; ++ g_free(cpu); ++ } ++ } ++ ++ trace_kvm_unpark_vcpu(vcpu_id, kvm_fd > 0 ? "unparked" : "!found parked"); ++ ++ return kvm_fd; ++} ++ ++int kvm_create_vcpu(CPUState *cpu) ++{ ++ unsigned long vcpu_id = kvm_arch_vcpu_id(cpu); ++ KVMState *s = kvm_state; ++ int kvm_fd; ++ ++ /* check if the KVM vCPU already exist but is parked */ ++ kvm_fd = kvm_unpark_vcpu(s, vcpu_id); ++ if (kvm_fd < 0) { ++ /* vCPU not parked: create a new KVM vCPU */ ++ kvm_fd = kvm_vm_ioctl(s, KVM_CREATE_VCPU, vcpu_id); ++ if (kvm_fd < 0) { ++ error_report("KVM_CREATE_VCPU IOCTL failed for vCPU %lu", vcpu_id); ++ return kvm_fd; ++ } ++ } ++ ++ cpu->kvm_fd = kvm_fd; ++ cpu->kvm_state = s; ++ cpu->vcpu_dirty = true; ++ cpu->dirty_pages = 0; ++ cpu->throttle_us_per_full = 0; ++ ++ trace_kvm_create_vcpu(cpu->cpu_index, vcpu_id, kvm_fd); ++ ++ return 0; ++} ++ + static int do_kvm_destroy_vcpu(CPUState *cpu) + { + KVMState *s = kvm_state; + long mmap_size; +- struct KVMParkedVcpu *vcpu = NULL; + int ret = 0; + +- DPRINTF("kvm_destroy_vcpu\n"); ++ trace_kvm_destroy_vcpu(cpu->cpu_index, kvm_arch_vcpu_id(cpu)); + + ret = kvm_arch_destroy_vcpu(cpu); + if (ret < 0) { +@@ -358,10 +415,7 @@ static int do_kvm_destroy_vcpu(CPUState *cpu) + } + } + +- vcpu = g_malloc0(sizeof(*vcpu)); +- vcpu->vcpu_id = kvm_arch_vcpu_id(cpu); +- vcpu->kvm_fd = cpu->kvm_fd; +- QLIST_INSERT_HEAD(&kvm_state->kvm_parked_vcpus, vcpu, node); ++ kvm_park_vcpu(cpu); + err: + return ret; + } +@@ -374,24 +428,6 @@ void kvm_destroy_vcpu(CPUState *cpu) + } + } + +-static int kvm_get_vcpu(KVMState *s, unsigned long vcpu_id) +-{ +- struct KVMParkedVcpu *cpu; +- +- QLIST_FOREACH(cpu, &s->kvm_parked_vcpus, node) { +- if (cpu->vcpu_id == vcpu_id) { +- int kvm_fd; +- +- QLIST_REMOVE(cpu, node); +- kvm_fd = cpu->kvm_fd; +- g_free(cpu); +- return kvm_fd; +- } +- } +- +- return kvm_vm_ioctl(s, KVM_CREATE_VCPU, (void *)vcpu_id); +-} +- + int kvm_init_vcpu(CPUState *cpu, Error **errp) + { + KVMState *s = kvm_state; +@@ -400,19 +436,14 @@ int kvm_init_vcpu(CPUState *cpu, Error **errp) + + trace_kvm_init_vcpu(cpu->cpu_index, kvm_arch_vcpu_id(cpu)); + +- ret = kvm_get_vcpu(s, kvm_arch_vcpu_id(cpu)); ++ ret = kvm_create_vcpu(cpu); + if (ret < 0) { +- error_setg_errno(errp, -ret, "kvm_init_vcpu: kvm_get_vcpu failed (%lu)", ++ error_setg_errno(errp, -ret, ++ "kvm_init_vcpu: kvm_create_vcpu failed (%lu)", + kvm_arch_vcpu_id(cpu)); + goto err; + } + +- cpu->kvm_fd = ret; +- cpu->kvm_state = s; +- cpu->vcpu_dirty = true; +- cpu->dirty_pages = 0; +- cpu->throttle_us_per_full = 0; +- + mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0); + if (mmap_size < 0) { + ret = mmap_size; +diff --git a/accel/kvm/trace-events b/accel/kvm/trace-events +index 399aaeb..d6109f2 100644 +--- a/accel/kvm/trace-events ++++ b/accel/kvm/trace-events +@@ -9,6 +9,10 @@ kvm_device_ioctl(int fd, int type, void *arg) "dev fd %d, type 0x%x, arg %p" + kvm_failed_reg_get(uint64_t id, const char *msg) "Warning: Unable to retrieve ONEREG %" PRIu64 " from KVM: %s" + kvm_failed_reg_set(uint64_t id, const char *msg) "Warning: Unable to set ONEREG %" PRIu64 " to KVM: %s" + kvm_init_vcpu(int cpu_index, unsigned long arch_cpu_id) "index: %d id: %lu" ++kvm_create_vcpu(int cpu_index, unsigned long arch_cpu_id, int kvm_fd) "index: %d, id: %lu, kvm fd: %d" ++kvm_park_vcpu(int cpu_index, unsigned long arch_cpu_id) "index: %d id: %lu" ++kvm_destroy_vcpu(int cpu_index, unsigned long arch_cpu_id) "index: %d id: %lu" ++kvm_unpark_vcpu(unsigned long arch_cpu_id, const char *msg) "id: %lu %s" + kvm_irqchip_commit_routes(void) "" + kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d" + kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d" +diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h +index 1e15cfe..4d00ade 100644 +--- a/include/sysemu/kvm.h ++++ b/include/sysemu/kvm.h +@@ -321,6 +321,31 @@ int kvm_create_device(KVMState *s, uint64_t type, bool test); + */ + bool kvm_device_supported(int vmfd, uint64_t type); + ++/** ++ * kvm_create_vcpu - Gets a parked KVM vCPU or creates a KVM vCPU ++ * @cpu: QOM CPUState object for which KVM vCPU has to be fetched/created. ++ * ++ * @returns: 0 when success, errno (<0) when failed. ++ */ ++int kvm_create_vcpu(CPUState *cpu); ++ ++/** ++ * kvm_park_vcpu - Park QEMU KVM vCPU context ++ * @cpu: QOM CPUState object for which QEMU KVM vCPU context has to be parked. ++ * ++ * @returns: none ++ */ ++void kvm_park_vcpu(CPUState *cpu); ++ ++/** ++ * kvm_unpark_vcpu - unpark QEMU KVM vCPU context ++ * @s: KVM State ++ * @vcpu_id: Architecture vCPU ID of the parked vCPU ++ * ++ * @returns: KVM fd ++ */ ++int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id); ++ + /* Arch specific hooks */ + + extern const KVMCapabilityInfo kvm_arch_required_capabilities[]; +-- +1.8.3.1 + diff --git a/0254-hw-acpi-Move-CPU-ctrl-dev-MMIO-region-len-macro-to-c.patch b/0254-hw-acpi-Move-CPU-ctrl-dev-MMIO-region-len-macro-to-c.patch new file mode 100644 index 0000000000000000000000000000000000000000..0ed215fef8178c3dfab849f3af66c09687f9e50a --- /dev/null +++ b/0254-hw-acpi-Move-CPU-ctrl-dev-MMIO-region-len-macro-to-c.patch @@ -0,0 +1,62 @@ +From dde5f21c4a47595a47b2865820d8ede9d4c9b01f Mon Sep 17 00:00:00 2001 +From: Salil Mehta +Date: Tue, 16 Jul 2024 12:14:57 +0100 +Subject: [PATCH 264/293] hw/acpi: Move CPU ctrl-dev MMIO region len macro to + common header file +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +CPU ctrl-dev MMIO region length could be used in ACPI GED and various other +architecture specific places. Move ACPI_CPU_HOTPLUG_REG_LEN macro to more +appropriate common header file. + +Signed-off-by: Salil Mehta +Reviewed-by: Alex Bennée +Reviewed-by: Jonathan Cameron +Reviewed-by: Gavin Shan +Reviewed-by: David Hildenbrand +Reviewed-by: Shaoqin Huang +Tested-by: Vishnu Pajjuri +Tested-by: Xianglai Li +Tested-by: Miguel Luis +Tested-by: Zhao Liu +Reviewed-by: Zhao Liu +Reviewed-by: Igor Mammedov +Message-Id: <20240716111502.202344-3-salil.mehta@huawei.com> +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +Signed-off-by: Xianglai Li +--- + hw/acpi/cpu.c | 1 - + include/hw/acpi/cpu.h | 2 ++ + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c +index 011d2c6..1a4c2a8 100644 +--- a/hw/acpi/cpu.c ++++ b/hw/acpi/cpu.c +@@ -7,7 +7,6 @@ + #include "trace.h" + #include "sysemu/numa.h" + +-#define ACPI_CPU_HOTPLUG_REG_LEN 12 + #define ACPI_CPU_SELECTOR_OFFSET_WR 0 + #define ACPI_CPU_FLAGS_OFFSET_RW 4 + #define ACPI_CPU_CMD_OFFSET_WR 5 +diff --git a/include/hw/acpi/cpu.h b/include/hw/acpi/cpu.h +index bc90166..f4a28df 100644 +--- a/include/hw/acpi/cpu.h ++++ b/include/hw/acpi/cpu.h +@@ -18,6 +18,8 @@ + #include "hw/boards.h" + #include "hw/hotplug.h" + ++#define ACPI_CPU_HOTPLUG_REG_LEN 12 ++ + typedef struct AcpiCpuStatus { + struct CPUState *cpu; + uint64_t arch_id; +-- +1.8.3.1 + diff --git a/0255-hw-acpi-Update-ACPI-GED-framework-to-support-vCPU-Ho.patch b/0255-hw-acpi-Update-ACPI-GED-framework-to-support-vCPU-Ho.patch new file mode 100644 index 0000000000000000000000000000000000000000..1d4d1f1dc00eeffff80e8c796360bdb569b154ba --- /dev/null +++ b/0255-hw-acpi-Update-ACPI-GED-framework-to-support-vCPU-Ho.patch @@ -0,0 +1,215 @@ +From 7948a3bd1a76f3eff1187fa838cc2f8b3bd37798 Mon Sep 17 00:00:00 2001 +From: Salil Mehta +Date: Tue, 16 Jul 2024 12:14:58 +0100 +Subject: [PATCH 265/293] hw/acpi: Update ACPI GED framework to support vCPU + Hotplug + +ACPI GED (as described in the ACPI 6.4 spec) uses an interrupt listed in the +_CRS object of GED to intimate OSPM about an event. Later then demultiplexes the +notified event by evaluating ACPI _EVT method to know the type of event. Use +ACPI GED to also notify the guest kernel about any CPU hot(un)plug events. + +Note, GED interface is used by many hotplug events like memory hotplug, NVDIMM +hotplug and non-hotplug events like system power down event. Each of these can +be selected using a bit in the 32 bit GED IO interface. A bit has been reserved +for the CPU hotplug event. + +ACPI CPU hotplug related initialization should only happen if ACPI_CPU_HOTPLUG +support has been enabled for particular architecture. Add cpu_hotplug_hw_init() +stub to avoid compilation break. + +Co-developed-by: Keqian Zhu +Signed-off-by: Keqian Zhu +Signed-off-by: Salil Mehta +Reviewed-by: Jonathan Cameron +Reviewed-by: Gavin Shan +Reviewed-by: David Hildenbrand +Reviewed-by: Shaoqin Huang +Tested-by: Vishnu Pajjuri +Tested-by: Xianglai Li +Tested-by: Miguel Luis +Reviewed-by: Vishnu Pajjuri +Tested-by: Zhao Liu +Reviewed-by: Zhao Liu +Message-Id: <20240716111502.202344-4-salil.mehta@huawei.com> +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +Acked-by: Igor Mammedov +Signed-off-by: Xianglai Li +--- + docs/specs/acpi_hw_reduced_hotplug.rst | 3 ++- + hw/acpi/acpi-cpu-hotplug-stub.c | 6 +++++ + hw/acpi/generic_event_device.c | 47 ++++++++++++++++++++++++++++++++++ + include/hw/acpi/generic_event_device.h | 4 +++ + 4 files changed, 59 insertions(+), 1 deletion(-) + +diff --git a/docs/specs/acpi_hw_reduced_hotplug.rst b/docs/specs/acpi_hw_reduced_hotplug.rst +index 0bd3f93..3acd6fc 100644 +--- a/docs/specs/acpi_hw_reduced_hotplug.rst ++++ b/docs/specs/acpi_hw_reduced_hotplug.rst +@@ -64,7 +64,8 @@ GED IO interface (4 byte access) + 0: Memory hotplug event + 1: System power down event + 2: NVDIMM hotplug event +- 3-31: Reserved ++ 3: CPU hotplug event ++ 4-31: Reserved + + **write_access:** + +diff --git a/hw/acpi/acpi-cpu-hotplug-stub.c b/hw/acpi/acpi-cpu-hotplug-stub.c +index 3fc4b14..c6c61bb 100644 +--- a/hw/acpi/acpi-cpu-hotplug-stub.c ++++ b/hw/acpi/acpi-cpu-hotplug-stub.c +@@ -19,6 +19,12 @@ void legacy_acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner, + return; + } + ++void cpu_hotplug_hw_init(MemoryRegion *as, Object *owner, ++ CPUHotplugState *state, hwaddr base_addr) ++{ ++ return; ++} ++ + void acpi_cpu_ospm_status(CPUHotplugState *cpu_st, ACPIOSTInfoList ***list) + { + return; +diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c +index 889ae96..d797df0 100644 +--- a/hw/acpi/generic_event_device.c ++++ b/hw/acpi/generic_event_device.c +@@ -25,6 +25,7 @@ static const uint32_t ged_supported_events[] = { + ACPI_GED_MEM_HOTPLUG_EVT, + ACPI_GED_PWR_DOWN_EVT, + ACPI_GED_NVDIMM_HOTPLUG_EVT, ++ ACPI_GED_CPU_HOTPLUG_EVT, + }; + + /* +@@ -234,6 +235,8 @@ static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev, + } else { + acpi_memory_plug_cb(hotplug_dev, &s->memhp_state, dev, errp); + } ++ } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { ++ acpi_cpu_plug_cb(hotplug_dev, &s->cpuhp_state, dev, errp); + } else { + error_setg(errp, "virt: device plug request for unsupported device" + " type: %s", object_get_typename(OBJECT(dev))); +@@ -248,6 +251,8 @@ static void acpi_ged_unplug_request_cb(HotplugHandler *hotplug_dev, + if ((object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) && + !(object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)))) { + acpi_memory_unplug_request_cb(hotplug_dev, &s->memhp_state, dev, errp); ++ } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { ++ acpi_cpu_unplug_request_cb(hotplug_dev, &s->cpuhp_state, dev, errp); + } else { + error_setg(errp, "acpi: device unplug request for unsupported device" + " type: %s", object_get_typename(OBJECT(dev))); +@@ -261,6 +266,8 @@ static void acpi_ged_unplug_cb(HotplugHandler *hotplug_dev, + + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + acpi_memory_unplug_cb(&s->memhp_state, dev, errp); ++ } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { ++ acpi_cpu_unplug_cb(&s->cpuhp_state, dev, errp); + } else { + error_setg(errp, "acpi: device unplug for unsupported device" + " type: %s", object_get_typename(OBJECT(dev))); +@@ -272,6 +279,7 @@ static void acpi_ged_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list) + AcpiGedState *s = ACPI_GED(adev); + + acpi_memory_ospm_status(&s->memhp_state, list); ++ acpi_cpu_ospm_status(&s->cpuhp_state, list); + } + + static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev) +@@ -286,6 +294,8 @@ static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev) + sel = ACPI_GED_PWR_DOWN_EVT; + } else if (ev & ACPI_NVDIMM_HOTPLUG_STATUS) { + sel = ACPI_GED_NVDIMM_HOTPLUG_EVT; ++ } else if (ev & ACPI_CPU_HOTPLUG_STATUS) { ++ sel = ACPI_GED_CPU_HOTPLUG_EVT; + } else { + /* Unknown event. Return without generating interrupt. */ + warn_report("GED: Unsupported event %d. No irq injected", ev); +@@ -371,6 +381,42 @@ static const VMStateDescription vmstate_acpi_ged = { + } + }; + ++static void acpi_ged_realize(DeviceState *dev, Error **errp) ++{ ++ SysBusDevice *sbd = SYS_BUS_DEVICE(dev); ++ AcpiGedState *s = ACPI_GED(dev); ++ uint32_t ged_events; ++ int i; ++ ++ ged_events = ctpop32(s->ged_event_bitmap); ++ ++ for (i = 0; i < ARRAY_SIZE(ged_supported_events) && ged_events; i++) { ++ uint32_t event = s->ged_event_bitmap & ged_supported_events[i]; ++ ++ if (!event) { ++ continue; ++ } ++ ++ switch (event) { ++ case ACPI_GED_CPU_HOTPLUG_EVT: ++ /* initialize CPU Hotplug related regions */ ++ memory_region_init(&s->container_cpuhp, OBJECT(dev), ++ "cpuhp container", ++ ACPI_CPU_HOTPLUG_REG_LEN); ++ sysbus_init_mmio(sbd, &s->container_cpuhp); ++ cpu_hotplug_hw_init(&s->container_cpuhp, OBJECT(dev), ++ &s->cpuhp_state, 0); ++ break; ++ } ++ ged_events--; ++ } ++ ++ if (ged_events) { ++ error_report("Unsupported events specified"); ++ abort(); ++ } ++} ++ + static void acpi_ged_initfn(Object *obj) + { + DeviceState *dev = DEVICE(obj); +@@ -411,6 +457,7 @@ static void acpi_ged_class_init(ObjectClass *class, void *data) + dc->desc = "ACPI Generic Event Device"; + device_class_set_props(dc, acpi_ged_properties); + dc->vmsd = &vmstate_acpi_ged; ++ dc->realize = acpi_ged_realize; + + hc->plug = acpi_ged_device_plug_cb; + hc->unplug_request = acpi_ged_unplug_request_cb; +diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h +index 3006916..c4e0e96 100644 +--- a/include/hw/acpi/generic_event_device.h ++++ b/include/hw/acpi/generic_event_device.h +@@ -62,6 +62,7 @@ + #include "hw/sysbus.h" + #include "hw/acpi/memory_hotplug.h" + #include "hw/acpi/ghes.h" ++#include "hw/acpi/cpu.h" + #include "qom/object.h" + + #define ACPI_POWER_BUTTON_DEVICE "PWRB" +@@ -98,6 +99,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(AcpiGedState, ACPI_GED) + #define ACPI_GED_MEM_HOTPLUG_EVT 0x1 + #define ACPI_GED_PWR_DOWN_EVT 0x2 + #define ACPI_GED_NVDIMM_HOTPLUG_EVT 0x4 ++#define ACPI_GED_CPU_HOTPLUG_EVT 0x8 + + typedef struct GEDState { + MemoryRegion evt; +@@ -109,6 +111,8 @@ struct AcpiGedState { + SysBusDevice parent_obj; + MemHotplugState memhp_state; + MemoryRegion container_memhp; ++ CPUHotplugState cpuhp_state; ++ MemoryRegion container_cpuhp; + GEDState ged_state; + uint32_t ged_event_bitmap; + qemu_irq irq; +-- +1.8.3.1 + diff --git a/0256-hw-acpi-Update-GED-_EVT-method-AML-with-CPU-scan.patch b/0256-hw-acpi-Update-GED-_EVT-method-AML-with-CPU-scan.patch new file mode 100644 index 0000000000000000000000000000000000000000..6485d284af89812532bcca2c5c0000d82ffd66a3 --- /dev/null +++ b/0256-hw-acpi-Update-GED-_EVT-method-AML-with-CPU-scan.patch @@ -0,0 +1,67 @@ +From f22e0aac1d5727564e7a28cbe578592da07aaa88 Mon Sep 17 00:00:00 2001 +From: Salil Mehta +Date: Tue, 16 Jul 2024 12:14:59 +0100 +Subject: [PATCH 266/293] hw/acpi: Update GED _EVT method AML with CPU scan + +OSPM evaluates _EVT method to map the event. The CPU hotplug event eventually +results in start of the CPU scan. Scan figures out the CPU and the kind of +event(plug/unplug) and notifies it back to the guest. Update the GED AML _EVT +method with the call to method \\_SB.CPUS.CSCN (via \\_SB.GED.CSCN) + +Architecture specific code [1] might initialize its CPUs AML code by calling +common function build_cpus_aml() like below for ARM: + +build_cpus_aml(scope, ms, opts, xx_madt_cpu_entry, memmap[VIRT_CPUHP_ACPI].base, + "\\_SB", "\\_SB.GED.CSCN", AML_SYSTEM_MEMORY); + +[1] https://lore.kernel.org/qemu-devel/20240613233639.202896-13-salil.mehta@huawei.com/ + +Co-developed-by: Keqian Zhu +Signed-off-by: Keqian Zhu +Signed-off-by: Salil Mehta +Reviewed-by: Jonathan Cameron +Reviewed-by: Gavin Shan +Tested-by: Vishnu Pajjuri +Tested-by: Xianglai Li +Tested-by: Miguel Luis +Reviewed-by: Shaoqin Huang +Tested-by: Zhao Liu +Reviewed-by: Igor Mammedov +Message-Id: <20240716111502.202344-5-salil.mehta@huawei.com> +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +Signed-off-by: Xianglai Li +--- + hw/acpi/generic_event_device.c | 3 +++ + include/hw/acpi/generic_event_device.h | 1 + + 2 files changed, 4 insertions(+) + +diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c +index d797df0..48f03cd 100644 +--- a/hw/acpi/generic_event_device.c ++++ b/hw/acpi/generic_event_device.c +@@ -108,6 +108,9 @@ void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev, + aml_append(if_ctx, aml_call0(MEMORY_DEVICES_CONTAINER "." + MEMORY_SLOT_SCAN_METHOD)); + break; ++ case ACPI_GED_CPU_HOTPLUG_EVT: ++ aml_append(if_ctx, aml_call0(AML_GED_EVT_CPU_SCAN_METHOD)); ++ break; + case ACPI_GED_PWR_DOWN_EVT: + aml_append(if_ctx, + aml_notify(aml_name(ACPI_POWER_BUTTON_DEVICE), +diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h +index c4e0e96..d2dac87 100644 +--- a/include/hw/acpi/generic_event_device.h ++++ b/include/hw/acpi/generic_event_device.h +@@ -90,6 +90,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(AcpiGedState, ACPI_GED) + #define GED_DEVICE "GED" + #define AML_GED_EVT_REG "EREG" + #define AML_GED_EVT_SEL "ESEL" ++#define AML_GED_EVT_CPU_SCAN_METHOD "\\_SB.GED.CSCN" + + /* + * Platforms need to specify the GED event bitmap +-- +1.8.3.1 + diff --git a/0257-hw-acpi-Update-CPUs-AML-with-cpu-ctrl-dev-change.patch b/0257-hw-acpi-Update-CPUs-AML-with-cpu-ctrl-dev-change.patch new file mode 100644 index 0000000000000000000000000000000000000000..e178c3967b7e3882a3d8665c40fbf9dfac5604ae --- /dev/null +++ b/0257-hw-acpi-Update-CPUs-AML-with-cpu-ctrl-dev-change.patch @@ -0,0 +1,110 @@ +From 6a0903c0c6ce0d3b2a833f612f5ac76b81bfb39c Mon Sep 17 00:00:00 2001 +From: Salil Mehta +Date: Tue, 16 Jul 2024 12:15:00 +0100 +Subject: [PATCH 267/293] hw/acpi: Update CPUs AML with cpu-(ctrl)dev change + +CPUs Control device(\\_SB.PCI0) register interface for the x86 arch is IO port +based and existing CPUs AML code assumes _CRS objects would evaluate to a system +resource which describes IO Port address. But on ARM arch CPUs control +device(\\_SB.PRES) register interface is memory-mapped hence _CRS object should +evaluate to system resource which describes memory-mapped base address. Update +build CPUs AML function to accept both IO/MEMORY region spaces and accordingly +update the _CRS object. + +Co-developed-by: Keqian Zhu +Signed-off-by: Keqian Zhu +Signed-off-by: Salil Mehta +Reviewed-by: Gavin Shan +Tested-by: Vishnu Pajjuri +Reviewed-by: Jonathan Cameron +Tested-by: Xianglai Li +Tested-by: Miguel Luis +Reviewed-by: Shaoqin Huang +Tested-by: Zhao Liu +Reviewed-by: Igor Mammedov +Message-Id: <20240716111502.202344-6-salil.mehta@huawei.com> +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +Signed-off-by: Xianglai Li +--- + hw/acpi/cpu.c | 17 +++++++++++++---- + hw/i386/acpi-build.c | 3 ++- + include/hw/acpi/cpu.h | 5 +++-- + 3 files changed, 18 insertions(+), 7 deletions(-) + +diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c +index 1a4c2a8..b48a0e7 100644 +--- a/hw/acpi/cpu.c ++++ b/hw/acpi/cpu.c +@@ -338,9 +338,10 @@ const VMStateDescription vmstate_cpu_hotplug = { + #define CPU_FW_EJECT_EVENT "CEJF" + + void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, +- build_madt_cpu_fn build_madt_cpu, hwaddr io_base, ++ build_madt_cpu_fn build_madt_cpu, hwaddr base_addr, + const char *res_root, +- const char *event_handler_method) ++ const char *event_handler_method, ++ AmlRegionSpace rs) + { + Aml *ifctx; + Aml *field; +@@ -364,14 +365,22 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, + aml_name_decl("_UID", aml_string("CPU Hotplug resources"))); + aml_append(cpu_ctrl_dev, aml_mutex(CPU_LOCK, 0)); + ++ assert((rs == AML_SYSTEM_IO) || (rs == AML_SYSTEM_MEMORY)); ++ + crs = aml_resource_template(); +- aml_append(crs, aml_io(AML_DECODE16, io_base, io_base, 1, ++ if (rs == AML_SYSTEM_IO) { ++ aml_append(crs, aml_io(AML_DECODE16, base_addr, base_addr, 1, + ACPI_CPU_HOTPLUG_REG_LEN)); ++ } else if (rs == AML_SYSTEM_MEMORY) { ++ aml_append(crs, aml_memory32_fixed(base_addr, ++ ACPI_CPU_HOTPLUG_REG_LEN, AML_READ_WRITE)); ++ } ++ + aml_append(cpu_ctrl_dev, aml_name_decl("_CRS", crs)); + + /* declare CPU hotplug MMIO region with related access fields */ + aml_append(cpu_ctrl_dev, +- aml_operation_region("PRST", AML_SYSTEM_IO, aml_int(io_base), ++ aml_operation_region("PRST", rs, aml_int(base_addr), + ACPI_CPU_HOTPLUG_REG_LEN)); + + field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK, +diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c +index 1e17834..0627b40 100644 +--- a/hw/i386/acpi-build.c ++++ b/hw/i386/acpi-build.c +@@ -1546,7 +1546,8 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, + .fw_unplugs_cpu = pm->smi_on_cpu_unplug, + }; + build_cpus_aml(dsdt, machine, opts, pc_madt_cpu_entry, +- pm->cpu_hp_io_base, "\\_SB.PCI0", "\\_GPE._E02"); ++ pm->cpu_hp_io_base, "\\_SB.PCI0", "\\_GPE._E02", ++ AML_SYSTEM_IO); + } + + if (pcms->memhp_io_base && nr_mem) { +diff --git a/include/hw/acpi/cpu.h b/include/hw/acpi/cpu.h +index f4a28df..d2ca2ee 100644 +--- a/include/hw/acpi/cpu.h ++++ b/include/hw/acpi/cpu.h +@@ -62,9 +62,10 @@ typedef void (*build_madt_cpu_fn)(int uid, const CPUArchIdList *apic_ids, + GArray *entry, bool force_enabled); + + void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, +- build_madt_cpu_fn build_madt_cpu, hwaddr io_base, ++ build_madt_cpu_fn build_madt_cpu, hwaddr base_addr, + const char *res_root, +- const char *event_handler_method); ++ const char *event_handler_method, ++ AmlRegionSpace rs); + + void acpi_cpu_ospm_status(CPUHotplugState *cpu_st, ACPIOSTInfoList ***list); + +-- +1.8.3.1 + diff --git a/0258-physmem-Add-helper-function-to-destroy-CPU-AddressSp.patch b/0258-physmem-Add-helper-function-to-destroy-CPU-AddressSp.patch new file mode 100644 index 0000000000000000000000000000000000000000..7a76431baecb7f589e9f854c3f41fcc19cc46e17 --- /dev/null +++ b/0258-physmem-Add-helper-function-to-destroy-CPU-AddressSp.patch @@ -0,0 +1,109 @@ +From d6ad8126f17f6fa9c7334c75c4b6a73caf40cf6b Mon Sep 17 00:00:00 2001 +From: Salil Mehta +Date: Tue, 16 Jul 2024 12:15:01 +0100 +Subject: [PATCH 268/293] physmem: Add helper function to destroy CPU + AddressSpace + +Virtual CPU Hot-unplug leads to unrealization of a CPU object. This also +involves destruction of the CPU AddressSpace. Add common function to help +destroy the CPU AddressSpace. + +Signed-off-by: Salil Mehta +Tested-by: Vishnu Pajjuri +Reviewed-by: Gavin Shan +Tested-by: Xianglai Li +Tested-by: Miguel Luis +Reviewed-by: Shaoqin Huang +Tested-by: Zhao Liu +Acked-by: Igor Mammedov +Message-Id: <20240716111502.202344-7-salil.mehta@huawei.com> +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +Signed-off-by: Xianglai Li +--- + include/exec/cpu-common.h | 8 ++++++++ + include/hw/core/cpu.h | 1 + + system/physmem.c | 29 +++++++++++++++++++++++++++++ + 3 files changed, 38 insertions(+) + +diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h +index 41115d8..2a3d4aa 100644 +--- a/include/exec/cpu-common.h ++++ b/include/exec/cpu-common.h +@@ -139,6 +139,14 @@ size_t qemu_ram_pagesize_largest(void); + */ + void cpu_address_space_init(CPUState *cpu, int asidx, + const char *prefix, MemoryRegion *mr); ++/** ++ * cpu_address_space_destroy: ++ * @cpu: CPU for which address space needs to be destroyed ++ * @asidx: integer index of this address space ++ * ++ * Note that with KVM only one address space is supported. ++ */ ++void cpu_address_space_destroy(CPUState *cpu, int asidx); + + void cpu_physical_memory_rw(hwaddr addr, void *buf, + hwaddr len, bool is_write); +diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h +index c0c8320..0ae9efa 100644 +--- a/include/hw/core/cpu.h ++++ b/include/hw/core/cpu.h +@@ -496,6 +496,7 @@ struct CPUState { + QSIMPLEQ_HEAD(, qemu_work_item) work_list; + + CPUAddressSpace *cpu_ases; ++ int cpu_ases_count; + int num_ases; + AddressSpace *as; + MemoryRegion *memory; +diff --git a/system/physmem.c b/system/physmem.c +index a63853a..d74f6d4 100644 +--- a/system/physmem.c ++++ b/system/physmem.c +@@ -761,6 +761,7 @@ void cpu_address_space_init(CPUState *cpu, int asidx, + + if (!cpu->cpu_ases) { + cpu->cpu_ases = g_new0(CPUAddressSpace, cpu->num_ases); ++ cpu->cpu_ases_count = cpu->num_ases; + } + + newas = &cpu->cpu_ases[asidx]; +@@ -774,6 +775,34 @@ void cpu_address_space_init(CPUState *cpu, int asidx, + } + } + ++void cpu_address_space_destroy(CPUState *cpu, int asidx) ++{ ++ CPUAddressSpace *cpuas; ++ ++ assert(cpu->cpu_ases); ++ assert(asidx >= 0 && asidx < cpu->num_ases); ++ /* KVM cannot currently support multiple address spaces. */ ++ assert(asidx == 0 || !kvm_enabled()); ++ ++ cpuas = &cpu->cpu_ases[asidx]; ++ if (tcg_enabled()) { ++ memory_listener_unregister(&cpuas->tcg_as_listener); ++ } ++ ++ address_space_destroy(cpuas->as); ++ g_free_rcu(cpuas->as, rcu); ++ ++ if (asidx == 0) { ++ /* reset the convenience alias for address space 0 */ ++ cpu->as = NULL; ++ } ++ ++ if (--cpu->cpu_ases_count == 0) { ++ g_free(cpu->cpu_ases); ++ cpu->cpu_ases = NULL; ++ } ++} ++ + AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx) + { + /* Return the AddressSpace corresponding to the specified index */ +-- +1.8.3.1 + diff --git a/0259-gdbstub-Add-helper-function-to-unregister-GDB-regist.patch b/0259-gdbstub-Add-helper-function-to-unregister-GDB-regist.patch new file mode 100644 index 0000000000000000000000000000000000000000..90e68b5e9e9f94c0cfb8fbbb3e317b80e320ea8b --- /dev/null +++ b/0259-gdbstub-Add-helper-function-to-unregister-GDB-regist.patch @@ -0,0 +1,92 @@ +From c481143b781b61e56dd0f14e61f9d0ed14baf07a Mon Sep 17 00:00:00 2001 +From: Salil Mehta +Date: Tue, 16 Jul 2024 12:15:02 +0100 +Subject: [PATCH 269/293] gdbstub: Add helper function to unregister GDB + register space + +Add common function to help unregister the GDB register space. This shall be +done in context to the CPU unrealization. + +Note: These are common functions exported to arch specific code. For example, +for ARM this code is being referred in associated arch specific patch-set: + +Link: https://lore.kernel.org/qemu-devel/20230926103654.34424-1-salil.mehta@huawei.com/ + +Signed-off-by: Salil Mehta +Tested-by: Vishnu Pajjuri +Reviewed-by: Gavin Shan +Tested-by: Xianglai Li +Tested-by: Miguel Luis +Reviewed-by: Shaoqin Huang +Reviewed-by: Vishnu Pajjuri +Tested-by: Zhao Liu +Acked-by: Igor Mammedov +Message-Id: <20240716111502.202344-8-salil.mehta@huawei.com> +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Michael S. Tsirkin +Signed-off-by: Xianglai Li +--- + gdbstub/gdbstub.c | 13 +++++++++++++ + hw/core/cpu-common.c | 4 ++++ + include/exec/gdbstub.h | 6 ++++++ + 3 files changed, 23 insertions(+) + +diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c +index 46d752b..31c3dae 100644 +--- a/gdbstub/gdbstub.c ++++ b/gdbstub/gdbstub.c +@@ -582,6 +582,19 @@ void gdb_register_coprocessor(CPUState *cpu, + } + } + ++void gdb_unregister_coprocessor_all(CPUState *cpu) ++{ ++ /* ++ * Safe to nuke everything. GDBRegisterState::xml is static const char so ++ * it won't be freed ++ */ ++ g_array_free(cpu->gdb_regs, true); ++ ++ cpu->gdb_regs = NULL; ++ cpu->gdb_num_regs = 0; ++ cpu->gdb_num_g_regs = 0; ++} ++ + static void gdb_process_breakpoint_remove_all(GDBProcess *p) + { + CPUState *cpu = gdb_get_first_cpu_in_process(p); +diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c +index 82dae51..e36ca2c 100644 +--- a/hw/core/cpu-common.c ++++ b/hw/core/cpu-common.c +@@ -262,6 +262,10 @@ static void cpu_common_finalize(Object *obj) + { + CPUState *cpu = CPU(obj); + ++ /* If cleanup didn't happen in context to gdb_unregister_coprocessor_all */ ++ if (cpu->gdb_regs) { ++ g_array_free(cpu->gdb_regs, TRUE); ++ } + qemu_lockcnt_destroy(&cpu->in_ioctl_lock); + qemu_mutex_destroy(&cpu->work_mutex); + } +diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h +index d8a3c56..e2e8dff 100644 +--- a/include/exec/gdbstub.h ++++ b/include/exec/gdbstub.h +@@ -41,6 +41,12 @@ void gdb_register_coprocessor(CPUState *cpu, + int num_regs, const char *xml, int g_pos); + + /** ++ * gdb_unregister_coprocessor_all() - unregisters supplemental set of registers ++ * @cpu - the CPU associated with registers ++ */ ++void gdb_unregister_coprocessor_all(CPUState *cpu); ++ ++/** + * gdbserver_start: start the gdb server + * @port_or_device: connection spec for gdb + * +-- +1.8.3.1 + diff --git a/0260-accel-kvm-kvm-all-Fixes-the-missing-break-in-vCPU-un.patch b/0260-accel-kvm-kvm-all-Fixes-the-missing-break-in-vCPU-un.patch new file mode 100644 index 0000000000000000000000000000000000000000..ee61b17f65ab362829df94514cc0e510514157c4 --- /dev/null +++ b/0260-accel-kvm-kvm-all-Fixes-the-missing-break-in-vCPU-un.patch @@ -0,0 +1,46 @@ +From 1630c693b5fbf399374bcdd606195d125f5331cd Mon Sep 17 00:00:00 2001 +From: Salil Mehta +Date: Thu, 1 Aug 2024 10:15:03 +0100 +Subject: [PATCH 270/293] accel/kvm/kvm-all: Fixes the missing break in vCPU + unpark logic +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Loop should exit prematurely on successfully finding out the parked vCPU (struct +KVMParkedVcpu) in the 'struct KVMState' maintained 'kvm_parked_vcpus' list of +parked vCPUs. + +Fixes: Coverity CID 1558552 +Fixes: 08c3286822 ("accel/kvm: Extract common KVM vCPU {creation,parking} code") +Reported-by: Peter Maydell +Signed-off-by: Salil Mehta +Reviewed-by: Philippe Mathieu-Daudé +Reviewed-by: Gavin Shan +Reviewed-by: Zhao Liu +Reviewed-by: Igor Mammedov +Message-id: 20240725145132.99355-1-salil.mehta@huawei.com +Suggested-by: Peter Maydell +Message-ID: +Signed-off-by: Salil Mehta +Signed-off-by: Peter Maydell +Signed-off-by: Xianglai Li +--- + accel/kvm/kvm-all.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c +index 169d828..6db6085 100644 +--- a/accel/kvm/kvm-all.c ++++ b/accel/kvm/kvm-all.c +@@ -347,6 +347,7 @@ int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id) + QLIST_REMOVE(cpu, node); + kvm_fd = cpu->kvm_fd; + g_free(cpu); ++ break; + } + } + +-- +1.8.3.1 + diff --git a/0261-hw-loongarch-virt-Add-CPU-topology-support.patch b/0261-hw-loongarch-virt-Add-CPU-topology-support.patch new file mode 100644 index 0000000000000000000000000000000000000000..91768971c2aebc6749c48981fc8410990be1829e --- /dev/null +++ b/0261-hw-loongarch-virt-Add-CPU-topology-support.patch @@ -0,0 +1,277 @@ +From 0b5fe1586fc3c26f15d75e9a69d68e04f07ecd92 Mon Sep 17 00:00:00 2001 +From: Xianglai Li +Date: Sat, 26 Oct 2024 15:12:35 +0800 +Subject: [PATCH 271/293] hw/loongarch/virt: Add CPU topology support + +Add topological relationships for Loongarch VCPU and initialize +topology member variables. Also physical cpu id calculation +method comes from its topo information. + +Co-developed-by: Xianglai Li +Signed-off-by: Bibo Mao +Signed-off-by: Xianglai Li +--- + docs/system/loongarch/virt.rst | 31 ++++++++++++++++ + hw/loongarch/virt.c | 82 ++++++++++++++++++++++++++++++++++-------- + target/loongarch/cpu.c | 12 +++++++ + target/loongarch/cpu.h | 11 ++++++ + 4 files changed, 122 insertions(+), 14 deletions(-) + +diff --git a/docs/system/loongarch/virt.rst b/docs/system/loongarch/virt.rst +index c37268b..aa4719d 100644 +--- a/docs/system/loongarch/virt.rst ++++ b/docs/system/loongarch/virt.rst +@@ -28,6 +28,37 @@ The ``qemu-system-loongarch64`` provides emulation for virt + machine. You can specify the machine type ``virt`` and + cpu type ``la464``. + ++CPU Topology ++------------ ++ ++The ``LA464`` type CPUs have the concept of Socket Core and Thread. ++ ++For example: ++ ++``-smp 1,maxcpus=M,sockets=S,cores=C,threads=T`` ++ ++The above parameters indicate that the machine has a maximum of ``M`` vCPUs and ++``S`` sockets, each socket has ``C`` cores, each core has ``T`` threads, ++and each thread corresponds to a vCPU. ++ ++Then ``M`` ``S`` ``C`` ``T`` has the following relationship: ++ ++``M = S * C * T`` ++ ++In the CPU topology relationship, When we know the ``socket_id`` ``core_id`` ++and ``thread_id`` of the CPU, we can calculate its ``arch_id``: ++ ++``arch_id = (socket_id * S) + (core_id * C) + (thread_id * T)`` ++ ++Similarly, when we know the ``arch_id`` of the CPU, ++we can also get its ``socket_id`` ``core_id`` and ``thread_id``: ++ ++``socket_id = arch_id / (C * T)`` ++ ++``core_id = (arch_id / T) % C`` ++ ++``thread_id = arch_id % T`` ++ + Boot options + ------------ + +diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c +index 0866022..3b298c2 100644 +--- a/hw/loongarch/virt.c ++++ b/hw/loongarch/virt.c +@@ -1143,9 +1143,7 @@ static void virt_init(MachineState *machine) + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); + int i; + hwaddr base, size, ram_size = machine->ram_size; +- const CPUArchIdList *possible_cpus; + MachineClass *mc = MACHINE_GET_CLASS(machine); +- CPUState *cpu; + + if (!cpu_model) { + cpu_model = LOONGARCH_CPU_TYPE_NAME("la464"); +@@ -1163,14 +1161,39 @@ static void virt_init(MachineState *machine) + memory_region_add_subregion(&lvms->system_iocsr, 0, &lvms->iocsr_mem); + + /* Init CPUs */ +- possible_cpus = mc->possible_cpu_arch_ids(machine); +- for (i = 0; i < possible_cpus->len; i++) { +- cpu = cpu_create(machine->cpu_type); +- cpu->cpu_index = i; +- machine->possible_cpus->cpus[i].cpu = OBJECT(cpu); +- lacpu = LOONGARCH_CPU(cpu); ++ mc->possible_cpu_arch_ids(machine); ++ for (i = 0; i < machine->smp.cpus; i++) { ++ Object *cpuobj; ++ cpuobj = object_new(machine->cpu_type); ++ lacpu = LOONGARCH_CPU(cpuobj); ++ + lacpu->phy_id = machine->possible_cpus->cpus[i].arch_id; ++ object_property_set_int(cpuobj, "socket-id", ++ machine->possible_cpus->cpus[i].props.socket_id, ++ NULL); ++ object_property_set_int(cpuobj, "core-id", ++ machine->possible_cpus->cpus[i].props.core_id, ++ NULL); ++ object_property_set_int(cpuobj, "thread-id", ++ machine->possible_cpus->cpus[i].props.thread_id, ++ NULL); ++ /* ++ * The CPU in place at the time of machine startup will also enter ++ * the CPU hot-plug process when it is created, but at this time, ++ * the GED device has not been created, resulting in exit in the CPU ++ * hot-plug process, which can avoid the incumbent CPU repeatedly ++ * applying for resources. ++ * ++ * The interrupt resource of the in-place CPU will be requested at ++ * the current function call loongarch_irq_init(). ++ * ++ * The interrupt resource of the subsequently inserted CPU will be ++ * requested in the CPU hot-plug process. ++ */ ++ qdev_realize(DEVICE(cpuobj), NULL, &error_fatal); ++ object_unref(cpuobj); + } ++ + fdt_add_cpu_nodes(lvms); + fdt_add_memory_nodes(machine); + fw_cfg_add_memory(machine); +@@ -1286,6 +1309,27 @@ static void virt_initfn(Object *obj) + virt_flash_create(lvms); + } + ++static int virt_get_arch_id_from_topo(MachineState *ms, LoongArchCPUTopo *topo) ++{ ++ int arch_id, sock_vcpu_num, core_vcpu_num; ++ ++ /* ++ * calculate total logical cpus across socket/core/thread. ++ * For more information on how to calculate the arch_id, ++ * you can refer to the CPU Topology chapter of the ++ * docs/system/loongarch/virt.rst document. ++ */ ++ sock_vcpu_num = topo->socket_id * (ms->smp.threads * ms->smp.cores); ++ core_vcpu_num = topo->core_id * ms->smp.threads; ++ ++ /* get vcpu-id(logical cpu index) for this vcpu from this topology */ ++ arch_id = (sock_vcpu_num + core_vcpu_num) + topo->thread_id; ++ ++ assert(arch_id >= 0 && arch_id < ms->possible_cpus->len); ++ ++ return arch_id; ++} ++ + static bool memhp_type_supported(DeviceState *dev) + { + /* we only support pc dimm now */ +@@ -1383,10 +1427,19 @@ static HotplugHandler *virt_get_hotplug_handler(MachineState *machine, + return NULL; + } + ++static void virt_get_cpu_topo_from_index(MachineState *ms, ++ LoongArchCPUTopo *topo, int index) ++{ ++ topo->socket_id = index / (ms->smp.cores * ms->smp.threads); ++ topo->core_id = index / ms->smp.threads % ms->smp.cores; ++ topo->thread_id = index % ms->smp.threads; ++} ++ + static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) + { + int n; + unsigned int max_cpus = ms->smp.max_cpus; ++ LoongArchCPUTopo topo; + + if (ms->possible_cpus) { + assert(ms->possible_cpus->len == max_cpus); +@@ -1397,17 +1450,18 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) + sizeof(CPUArchId) * max_cpus); + ms->possible_cpus->len = max_cpus; + for (n = 0; n < ms->possible_cpus->len; n++) { ++ ms->possible_cpus->cpus[n].vcpus_count = ms->smp.threads; + ms->possible_cpus->cpus[n].type = ms->cpu_type; +- ms->possible_cpus->cpus[n].arch_id = n; ++ virt_get_cpu_topo_from_index(ms, &topo, n); + + ms->possible_cpus->cpus[n].props.has_socket_id = true; +- ms->possible_cpus->cpus[n].props.socket_id = +- n / (ms->smp.cores * ms->smp.threads); ++ ms->possible_cpus->cpus[n].props.socket_id = topo.socket_id; + ms->possible_cpus->cpus[n].props.has_core_id = true; +- ms->possible_cpus->cpus[n].props.core_id = +- n / ms->smp.threads % ms->smp.cores; ++ ms->possible_cpus->cpus[n].props.core_id = topo.core_id; + ms->possible_cpus->cpus[n].props.has_thread_id = true; +- ms->possible_cpus->cpus[n].props.thread_id = n % ms->smp.threads; ++ ms->possible_cpus->cpus[n].props.thread_id = topo.thread_id; ++ ms->possible_cpus->cpus[n].arch_id = ++ virt_get_arch_id_from_topo(ms, &topo); + } + return ms->possible_cpus; + } +diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c +index 130b0c7..b85a48b 100644 +--- a/target/loongarch/cpu.c ++++ b/target/loongarch/cpu.c +@@ -16,6 +16,7 @@ + #include "kvm/kvm_loongarch.h" + #include "exec/exec-all.h" + #include "cpu.h" ++#include "hw/qdev-properties.h" + #include "internals.h" + #include "fpu/softfloat-helpers.h" + #include "cpu-csr.h" +@@ -839,6 +840,15 @@ static int64_t loongarch_cpu_get_arch_id(CPUState *cs) + } + #endif + ++static Property loongarch_cpu_properties[] = { ++ DEFINE_PROP_INT32("socket-id", LoongArchCPU, socket_id, 0), ++ DEFINE_PROP_INT32("core-id", LoongArchCPU, core_id, 0), ++ DEFINE_PROP_INT32("thread-id", LoongArchCPU, thread_id, 0), ++ DEFINE_PROP_INT32("node-id", LoongArchCPU, node_id, CPU_UNSET_NUMA_NODE_ID), ++ ++ DEFINE_PROP_END_OF_LIST() ++}; ++ + static void loongarch_cpu_class_init(ObjectClass *c, void *data) + { + LoongArchCPUClass *lacc = LOONGARCH_CPU_CLASS(c); +@@ -846,6 +856,7 @@ static void loongarch_cpu_class_init(ObjectClass *c, void *data) + DeviceClass *dc = DEVICE_CLASS(c); + ResettableClass *rc = RESETTABLE_CLASS(c); + ++ device_class_set_props(dc, loongarch_cpu_properties); + device_class_set_parent_realize(dc, loongarch_cpu_realizefn, + &lacc->parent_realize); + resettable_class_set_parent_phases(rc, NULL, loongarch_cpu_reset_hold, NULL, +@@ -869,6 +880,7 @@ static void loongarch_cpu_class_init(ObjectClass *c, void *data) + #ifdef CONFIG_TCG + cc->tcg_ops = &loongarch_tcg_ops; + #endif ++ dc->user_creatable = true; + } + + static const gchar *loongarch32_gdb_arch_name(CPUState *cs) +diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h +index d922fe3..4a3ae6f 100644 +--- a/target/loongarch/cpu.h ++++ b/target/loongarch/cpu.h +@@ -388,6 +388,12 @@ typedef struct CPUArchState { + #endif + } CPULoongArchState; + ++typedef struct LoongArchCPUTopo { ++ int32_t socket_id; /* socket-id of this VCPU */ ++ int32_t core_id; /* core-id of this VCPU */ ++ int32_t thread_id; /* thread-id of this VCPU */ ++} LoongArchCPUTopo; ++ + /** + * LoongArchCPU: + * @env: #CPULoongArchState +@@ -400,6 +406,10 @@ struct ArchCPU { + CPULoongArchState env; + QEMUTimer timer; + uint32_t phy_id; ++ int32_t socket_id; /* socket-id of this VCPU */ ++ int32_t core_id; /* core-id of this VCPU */ ++ int32_t thread_id; /* thread-id of this VCPU */ ++ int32_t node_id; /* NUMA node this CPU belongs to */ + OnOffAuto lbt; + OnOffAuto pmu; + +@@ -420,6 +430,7 @@ struct LoongArchCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; ++ DeviceUnrealize parent_unrealize; + ResettablePhases parent_phases; + }; + +-- +1.8.3.1 + diff --git a/0262-Add-basic-CPU-plug-support.patch b/0262-Add-basic-CPU-plug-support.patch new file mode 100644 index 0000000000000000000000000000000000000000..9d130d7a1a89209526b77fb78534e56bff1539c8 --- /dev/null +++ b/0262-Add-basic-CPU-plug-support.patch @@ -0,0 +1,344 @@ +From bef4a8598a5f345267e492d43a41912676ebd5d7 Mon Sep 17 00:00:00 2001 +From: Xianglai Li +Date: Sat, 26 Oct 2024 15:13:59 +0800 +Subject: [PATCH 272/293] Add basic CPU plug support + +Implement interface for cpu hotplug function, and enable cpu hotplug +feature on virt machine. + +Co-developed-by: Xianglai Li +Signed-off-by: Bibo Mao +Signed-off-by: Xianglai Li +--- + hw/loongarch/Kconfig | 1 + + hw/loongarch/virt.c | 191 +++++++++++++++++++++++++++++++++++++++++++- + include/hw/loongarch/virt.h | 2 + + target/loongarch/cpu.c | 13 +++ + 4 files changed, 205 insertions(+), 2 deletions(-) + +diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig +index 9fa8f82..bf3ad0c 100644 +--- a/hw/loongarch/Kconfig ++++ b/hw/loongarch/Kconfig +@@ -14,6 +14,7 @@ config LOONGARCH_VIRT + select LOONGARCH_EXTIOI + select LS7A_RTC + select SMBIOS ++ select ACPI_CPU_HOTPLUG + select ACPI_PCI + select ACPI_HW_REDUCED + select FW_CFG_DMA +diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c +index 3b298c2..777c1c7 100644 +--- a/hw/loongarch/virt.c ++++ b/hw/loongarch/virt.c +@@ -841,7 +841,7 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) + + /* Create IPI device */ + ipi = qdev_new(TYPE_LOONGARCH_IPI); +- qdev_prop_set_uint32(ipi, "num-cpu", ms->smp.cpus); ++ qdev_prop_set_uint32(ipi, "num-cpu", ms->smp.max_cpus); + sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal); + + /* IPI iocsr memory region */ +@@ -865,9 +865,11 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) + env->ipistate = ipi; + } + ++ lvms->ipi = ipi; ++ + /* Create EXTIOI device */ + extioi = qdev_new(TYPE_LOONGARCH_EXTIOI); +- qdev_prop_set_uint32(extioi, "num-cpu", ms->smp.cpus); ++ qdev_prop_set_uint32(extioi, "num-cpu", ms->smp.max_cpus); + if (virt_is_veiointc_enabled(lvms)) { + qdev_prop_set_bit(extioi, "has-virtualization-extension", true); + } +@@ -891,6 +893,8 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) + } + } + ++ lvms->extioi = extioi; ++ + /* Add Extend I/O Interrupt Controller node */ + fdt_add_eiointc_node(lvms, &cpuintc_phandle, &eiointc_phandle); + +@@ -1330,6 +1334,179 @@ static int virt_get_arch_id_from_topo(MachineState *ms, LoongArchCPUTopo *topo) + return arch_id; + } + ++/* find cpu slot in machine->possible_cpus by arch_id */ ++static CPUArchId *virt_find_cpu_slot(MachineState *ms, int arch_id, int *index) ++{ ++ int n; ++ for (n = 0; n < ms->possible_cpus->len; n++) { ++ if (ms->possible_cpus->cpus[n].arch_id == arch_id) { ++ if (index) { ++ *index = n; ++ } ++ return &ms->possible_cpus->cpus[n]; ++ } ++ } ++ ++ return NULL; ++} ++ ++static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, ++ DeviceState *dev, Error **errp) ++{ ++ MachineState *ms = MACHINE(OBJECT(hotplug_dev)); ++ MachineClass *mc = MACHINE_GET_CLASS(hotplug_dev); ++ LoongArchCPU *cpu = LOONGARCH_CPU(dev); ++ CPUState *cs = CPU(dev); ++ CPUArchId *cpu_slot; ++ Error *local_err = NULL; ++ LoongArchCPUTopo topo; ++ int arch_id, index; ++ ++ if (dev->hotplugged && !mc->has_hotpluggable_cpus) { ++ error_setg(&local_err, "CPU hotplug not supported for this machine"); ++ goto out; ++ } ++ ++ /* sanity check the cpu */ ++ if (!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) { ++ error_setg(&local_err, "Invalid CPU type, expected cpu type: '%s'", ++ ms->cpu_type); ++ goto out; ++ } ++ ++ if ((cpu->thread_id < 0) || (cpu->thread_id >= ms->smp.threads)) { ++ error_setg(&local_err, ++ "Invalid thread-id %u specified, must be in range 1:%u", ++ cpu->thread_id, ms->smp.threads - 1); ++ goto out; ++ } ++ ++ if ((cpu->core_id < 0) || (cpu->core_id >= ms->smp.cores)) { ++ error_setg(&local_err, ++ "Invalid core-id %u specified, must be in range 1:%u", ++ cpu->core_id, ms->smp.cores - 1); ++ goto out; ++ } ++ ++ if ((cpu->socket_id < 0) || (cpu->socket_id >= ms->smp.sockets)) { ++ error_setg(&local_err, ++ "Invalid socket-id %u specified, must be in range 1:%u", ++ cpu->socket_id, ms->smp.sockets - 1); ++ goto out; ++ } ++ ++ topo.socket_id = cpu->socket_id; ++ topo.core_id = cpu->core_id; ++ topo.thread_id = cpu->thread_id; ++ arch_id = virt_get_arch_id_from_topo(ms, &topo); ++ cpu_slot = virt_find_cpu_slot(ms, arch_id, &index); ++ if (CPU(cpu_slot->cpu)) { ++ error_setg(&local_err, ++ "cpu(id%d=%d:%d:%d) with arch-id %" PRIu64 " exists", ++ cs->cpu_index, cpu->socket_id, cpu->core_id, ++ cpu->thread_id, cpu_slot->arch_id); ++ goto out; ++ } ++ cpu->phy_id = arch_id; ++ /* ++ * update cpu_index calculation method since it is easily used as index ++ * with possible_cpus array by function virt_cpu_index_to_props ++ */ ++ cs->cpu_index = index; ++ numa_cpu_pre_plug(cpu_slot, dev, &local_err); ++ return ; ++ ++out: ++ error_propagate(errp, local_err); ++} ++ ++static void virt_cpu_unplug_request(HotplugHandler *hotplug_dev, ++ DeviceState *dev, Error **errp) ++{ ++ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); ++ Error *local_err = NULL; ++ HotplugHandlerClass *hhc; ++ LoongArchCPU *cpu = LOONGARCH_CPU(dev); ++ CPUState *cs = CPU(dev); ++ ++ if (!lvms->acpi_ged) { ++ error_setg(&local_err, "CPU hot unplug not supported without ACPI"); ++ error_propagate(errp, local_err); ++ return; ++ } ++ ++ if (cs->cpu_index == 0) { ++ error_setg(&local_err, ++ "hot-unplug of boot cpu(id%d=%d:%d:%d) not supported", ++ cs->cpu_index, cpu->socket_id, ++ cpu->core_id, cpu->thread_id); ++ error_propagate(errp, local_err); ++ return; ++ } ++ ++ hhc = HOTPLUG_HANDLER_GET_CLASS(lvms->acpi_ged); ++ hhc->unplug_request(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &local_err); ++} ++ ++static void virt_cpu_unplug(HotplugHandler *hotplug_dev, ++ DeviceState *dev, Error **errp) ++{ ++ CPUArchId *cpu_slot; ++ HotplugHandlerClass *hhc; ++ Error *local_err = NULL; ++ LoongArchCPU *cpu = LOONGARCH_CPU(dev); ++ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); ++ ++ hhc = HOTPLUG_HANDLER_GET_CLASS(lvms->acpi_ged); ++ hhc->unplug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return; ++ } ++ ++ cpu_slot = virt_find_cpu_slot(MACHINE(lvms), cpu->phy_id, NULL); ++ cpu_slot->cpu = NULL; ++ return; ++} ++ ++static void virt_cpu_plug(HotplugHandler *hotplug_dev, ++ DeviceState *dev, Error **errp) ++{ ++ CPUArchId *cpu_slot; ++ HotplugHandlerClass *hhc; ++ Error *local_err = NULL; ++ LoongArchCPU *cpu = LOONGARCH_CPU(dev); ++ CPUState *cs = CPU(cpu); ++ CPULoongArchState *env; ++ LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); ++ int pin; ++ ++ if (lvms->acpi_ged) { ++ env = &(cpu->env); ++ env->address_space_iocsr = &lvms->as_iocsr; ++ ++ /* connect ipi irq to cpu irq, logic cpu index used here */ ++ qdev_connect_gpio_out(lvms->ipi, cs->cpu_index, ++ qdev_get_gpio_in(dev, IRQ_IPI)); ++ env->ipistate = lvms->ipi; ++ ++ for (pin = 0; pin < LS3A_INTC_IP; pin++) { ++ qdev_connect_gpio_out(lvms->extioi, (cs->cpu_index * 8 + pin), ++ qdev_get_gpio_in(dev, pin + 2)); ++ } ++ hhc = HOTPLUG_HANDLER_GET_CLASS(lvms->acpi_ged); ++ hhc->plug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return; ++ } ++ } ++ ++ cpu_slot = virt_find_cpu_slot(MACHINE(lvms), cpu->phy_id, NULL); ++ cpu_slot->cpu = OBJECT(dev); ++ return; ++} ++ + static bool memhp_type_supported(DeviceState *dev) + { + /* we only support pc dimm now */ +@@ -1348,6 +1525,8 @@ static void virt_device_pre_plug(HotplugHandler *hotplug_dev, + { + if (memhp_type_supported(dev)) { + virt_mem_pre_plug(hotplug_dev, dev, errp); ++ } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) { ++ virt_cpu_pre_plug(hotplug_dev, dev, errp); + } + } + +@@ -1366,6 +1545,8 @@ static void virt_device_unplug_request(HotplugHandler *hotplug_dev, + { + if (memhp_type_supported(dev)) { + virt_mem_unplug_request(hotplug_dev, dev, errp); ++ } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) { ++ virt_cpu_unplug_request(hotplug_dev, dev, errp); + } + } + +@@ -1384,6 +1565,8 @@ static void virt_device_unplug(HotplugHandler *hotplug_dev, + { + if (memhp_type_supported(dev)) { + virt_mem_unplug(hotplug_dev, dev, errp); ++ } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) { ++ virt_cpu_unplug(hotplug_dev, dev, errp); + } + } + +@@ -1411,6 +1594,8 @@ static void virt_device_plug_cb(HotplugHandler *hotplug_dev, + } + } else if (memhp_type_supported(dev)) { + virt_mem_plug(hotplug_dev, dev, errp); ++ } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) { ++ virt_cpu_plug(hotplug_dev, dev, errp); + } + } + +@@ -1420,6 +1605,7 @@ static HotplugHandler *virt_get_hotplug_handler(MachineState *machine, + MachineClass *mc = MACHINE_GET_CLASS(machine); + + if (device_is_dynamic_sysbus(mc, dev) || ++ object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU) || + object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI) || + memhp_type_supported(dev)) { + return HOTPLUG_HANDLER(machine); +@@ -1508,6 +1694,7 @@ static void virt_class_init(ObjectClass *oc, void *data) + mc->numa_mem_supported = true; + mc->auto_enable_numa_with_memhp = true; + mc->auto_enable_numa_with_memdev = true; ++ mc->has_hotpluggable_cpus = true; + mc->get_hotplug_handler = virt_get_hotplug_handler; + mc->default_nic = "virtio-net-pci"; + hc->plug = virt_device_plug_cb; +diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h +index c373e48..773e43b 100644 +--- a/include/hw/loongarch/virt.h ++++ b/include/hw/loongarch/virt.h +@@ -61,6 +61,8 @@ struct LoongArchVirtMachineState { + MemoryRegion iocsr_mem; + AddressSpace as_iocsr; + struct loongarch_boot_info bootinfo; ++ DeviceState *ipi; ++ DeviceState *extioi; + }; + + #define TYPE_LOONGARCH_VIRT_MACHINE MACHINE_TYPE_NAME("virt") +diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c +index b85a48b..78f4a95 100644 +--- a/target/loongarch/cpu.c ++++ b/target/loongarch/cpu.c +@@ -623,6 +623,17 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) + lacc->parent_realize(dev, errp); + } + ++static void loongarch_cpu_unrealizefn(DeviceState *dev) ++{ ++ LoongArchCPUClass *mcc = LOONGARCH_CPU_GET_CLASS(dev); ++ ++#ifndef CONFIG_USER_ONLY ++ cpu_remove_sync(CPU(dev)); ++#endif ++ ++ mcc->parent_unrealize(dev); ++} ++ + static bool loongarch_get_lsx(Object *obj, Error **errp) + { + LoongArchCPU *cpu = LOONGARCH_CPU(obj); +@@ -859,6 +870,8 @@ static void loongarch_cpu_class_init(ObjectClass *c, void *data) + device_class_set_props(dc, loongarch_cpu_properties); + device_class_set_parent_realize(dc, loongarch_cpu_realizefn, + &lacc->parent_realize); ++ device_class_set_parent_unrealize(dc, loongarch_cpu_unrealizefn, ++ &lacc->parent_unrealize); + resettable_class_set_parent_phases(rc, NULL, loongarch_cpu_reset_hold, NULL, + &lacc->parent_phases); + +-- +1.8.3.1 + diff --git a/0263-hw-loongarch-virt-Update-the-ACPI-table-for-hotplug-.patch b/0263-hw-loongarch-virt-Update-the-ACPI-table-for-hotplug-.patch new file mode 100644 index 0000000000000000000000000000000000000000..9a80e68459a09452f3cfb61fa9b4be47d3129a4c --- /dev/null +++ b/0263-hw-loongarch-virt-Update-the-ACPI-table-for-hotplug-.patch @@ -0,0 +1,142 @@ +From c28dc32ab267b439eb48c8903005ffb71eac53e8 Mon Sep 17 00:00:00 2001 +From: Xianglai Li +Date: Sat, 26 Oct 2024 15:15:22 +0800 +Subject: [PATCH 273/293] hw/loongarch/virt: Update the ACPI table for hotplug + cpu + +On LoongArch virt machine, ACPI GED hardware is used for cpu +hotplug, here cpu hotplug support feature is added on GED device, +also cpu scan and reject method is added about CPU device in +DSDT table. + +Co-developed-by: Xianglai Li +Signed-off-by: Bibo Mao +Signed-off-by: Xianglai Li +--- + hw/loongarch/acpi-build.c | 35 +++++++++++++++++++++++++++++++++-- + hw/loongarch/virt.c | 10 ++++++++++ + include/hw/loongarch/virt.h | 1 + + 3 files changed, 44 insertions(+), 2 deletions(-) + +diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c +index a41e4c2..c291cf6 100644 +--- a/hw/loongarch/acpi-build.c ++++ b/hw/loongarch/acpi-build.c +@@ -47,6 +47,22 @@ + #define ACPI_BUILD_DPRINTF(fmt, ...) + #endif + ++static void virt_madt_cpu_entry(int uid, ++ const CPUArchIdList *apic_ids, ++ GArray *entry, bool force_enabled) ++{ ++ uint32_t flags, apic_id = apic_ids->cpus[uid].arch_id; ++ ++ flags = apic_ids->cpus[uid].cpu || force_enabled ? 1 /* Enabled */ : 0; ++ ++ /* Rev 1.0b, Table 5-13 Processor Local APIC Structure */ ++ build_append_int_noprefix(entry, 0, 1); /* Type */ ++ build_append_int_noprefix(entry, 8, 1); /* Length */ ++ build_append_int_noprefix(entry, uid, 1); /* ACPI Processor ID */ ++ build_append_int_noprefix(entry, apic_id, 1); /* APIC ID */ ++ build_append_int_noprefix(entry, flags, 4); /* Flags */ ++} ++ + /* build FADT */ + static void init_common_fadt_data(AcpiFadtData *data) + { +@@ -123,15 +139,17 @@ build_madt(GArray *table_data, BIOSLinker *linker, + build_append_int_noprefix(table_data, 1 /* PCAT_COMPAT */, 4); /* Flags */ + + for (i = 0; i < arch_ids->len; i++) { ++ uint32_t flags; ++ + /* Processor Core Interrupt Controller Structure */ + arch_id = arch_ids->cpus[i].arch_id; +- ++ flags = arch_ids->cpus[i].cpu ? 1 : 0; + build_append_int_noprefix(table_data, 17, 1); /* Type */ + build_append_int_noprefix(table_data, 15, 1); /* Length */ + build_append_int_noprefix(table_data, 1, 1); /* Version */ + build_append_int_noprefix(table_data, i, 4); /* ACPI Processor ID */ + build_append_int_noprefix(table_data, arch_id, 4); /* Core ID */ +- build_append_int_noprefix(table_data, 1, 4); /* Flags */ ++ build_append_int_noprefix(table_data, flags, 4); /* Flags */ + } + + /* Extend I/O Interrupt Controller Structure */ +@@ -296,6 +314,7 @@ build_la_ged_aml(Aml *dsdt, MachineState *machine) + { + uint32_t event; + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); ++ CPUHotplugFeatures opts; + + build_ged_aml(dsdt, "\\_SB."GED_DEVICE, + HOTPLUG_HANDLER(lvms->acpi_ged), +@@ -308,6 +327,18 @@ build_la_ged_aml(Aml *dsdt, MachineState *machine) + AML_SYSTEM_MEMORY, + VIRT_GED_MEM_ADDR); + } ++ ++ if (event & ACPI_GED_CPU_HOTPLUG_EVT) { ++ opts.acpi_1_compatible = false; ++ opts.has_legacy_cphp = false; ++ opts.fw_unplugs_cpu = false; ++ opts.smi_path = NULL; ++ ++ build_cpus_aml(dsdt, machine, opts, virt_madt_cpu_entry, ++ VIRT_GED_CPUHP_ADDR, "\\_SB", ++ AML_GED_EVT_CPU_SCAN_METHOD, AML_SYSTEM_MEMORY); ++ } ++ + acpi_dsdt_add_power_button(dsdt); + } + +diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c +index 777c1c7..4f17079 100644 +--- a/hw/loongarch/virt.c ++++ b/hw/loongarch/virt.c +@@ -650,11 +650,17 @@ static DeviceState *create_acpi_ged(DeviceState *pch_pic, + { + DeviceState *dev; + MachineState *ms = MACHINE(lvms); ++ MachineClass *mc = MACHINE_GET_CLASS(lvms); + uint32_t event = ACPI_GED_PWR_DOWN_EVT; + + if (ms->ram_slots) { + event |= ACPI_GED_MEM_HOTPLUG_EVT; + } ++ ++ if (mc->has_hotpluggable_cpus) { ++ event |= ACPI_GED_CPU_HOTPLUG_EVT; ++ } ++ + dev = qdev_new(TYPE_ACPI_GED); + qdev_prop_set_uint32(dev, "ged-event", event); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); +@@ -666,6 +672,10 @@ static DeviceState *create_acpi_ged(DeviceState *pch_pic, + /* ged regs used for reset and power down */ + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, VIRT_GED_REG_ADDR); + ++ if (mc->has_hotpluggable_cpus) { ++ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 3, VIRT_GED_CPUHP_ADDR); ++ } ++ + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, + qdev_get_gpio_in(pch_pic, VIRT_SCI_IRQ - VIRT_GSI_BASE)); + return dev; +diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h +index 773e43b..25abc23 100644 +--- a/include/hw/loongarch/virt.h ++++ b/include/hw/loongarch/virt.h +@@ -31,6 +31,7 @@ + #define VIRT_GED_EVT_ADDR 0x100e0000 + #define VIRT_GED_MEM_ADDR (VIRT_GED_EVT_ADDR + ACPI_GED_EVT_SEL_LEN) + #define VIRT_GED_REG_ADDR (VIRT_GED_MEM_ADDR + MEMORY_HOTPLUG_IO_LEN) ++#define VIRT_GED_CPUHP_ADDR (VIRT_GED_REG_ADDR + ACPI_GED_REG_COUNT) + + #define COMMAND_LINE_SIZE 512 + +-- +1.8.3.1 + diff --git a/0264-hw-loongarch-Add-KVM-IPI-device-support.patch b/0264-hw-loongarch-Add-KVM-IPI-device-support.patch new file mode 100644 index 0000000000000000000000000000000000000000..dfd45c23d5f99e4f6f2c5acf8b3da3c915c18cbf --- /dev/null +++ b/0264-hw-loongarch-Add-KVM-IPI-device-support.patch @@ -0,0 +1,443 @@ +From a7d2b2fa658476a09027adb9c4068c03582ea59e Mon Sep 17 00:00:00 2001 +From: Xianglai Li +Date: Thu, 7 Mar 2024 19:38:41 +0800 +Subject: [PATCH 274/293] hw/loongarch: Add KVM IPI device support + +Added ipi interrupt controller for kvm emulation. +The main process is to send the command word for +creating an ipi device to the kernel. +When the VM is saved, the ioctl obtains the ipi +interrupt controller data in the kernel and saves it. +When the VM is recovered, the saved data is sent to the kernel. + +Signed-off-by: Tianrui Zhao +Signed-off-by: Xianglai Li +--- + hw/intc/Kconfig | 3 + + hw/intc/loongarch_ipi_kvm.c | 207 ++++++++++++++++++++++++++++++++++++++ + hw/intc/meson.build | 1 + + hw/loongarch/Kconfig | 1 + + hw/loongarch/virt.c | 49 +++++---- + include/hw/intc/loongarch_ipi.h | 22 ++++ + linux-headers/asm-loongarch/kvm.h | 3 + + linux-headers/linux/kvm.h | 2 + + target/loongarch/kvm/kvm.c | 5 + + 9 files changed, 271 insertions(+), 22 deletions(-) + create mode 100644 hw/intc/loongarch_ipi_kvm.c + +diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig +index 2b5b2d2..eddea0f 100644 +--- a/hw/intc/Kconfig ++++ b/hw/intc/Kconfig +@@ -93,6 +93,9 @@ config NIOS2_VIC + config LOONGARCH_IPI + bool + ++config LOONGARCH_IPI_KVM ++ bool ++ + config LOONGARCH_PCH_PIC + bool + select UNIMP +diff --git a/hw/intc/loongarch_ipi_kvm.c b/hw/intc/loongarch_ipi_kvm.c +new file mode 100644 +index 0000000..fd308eb +--- /dev/null ++++ b/hw/intc/loongarch_ipi_kvm.c +@@ -0,0 +1,207 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * LoongArch kvm ipi interrupt support ++ * ++ * Copyright (C) 2024 Loongson Technology Corporation Limited ++ */ ++ ++#include "qemu/osdep.h" ++#include "hw/qdev-properties.h" ++#include "qemu/typedefs.h" ++#include "hw/intc/loongarch_ipi.h" ++#include "hw/sysbus.h" ++#include "linux/kvm.h" ++#include "migration/vmstate.h" ++#include "qapi/error.h" ++#include "sysemu/kvm.h" ++ ++#define IPI_DEV_FD_UNDEF -1 ++ ++static void kvm_ipi_access_regs(int fd, uint64_t addr, ++ uint32_t *val, int is_write) ++{ ++ kvm_device_access(fd, KVM_DEV_LOONGARCH_IPI_GRP_REGS, ++ addr, val, is_write, &error_abort); ++} ++ ++static int kvm_loongarch_ipi_pre_save(void *opaque) ++{ ++ KVMLoongArchIPI *ipi = (KVMLoongArchIPI *)opaque; ++ KVMLoongArchIPIClass *ipi_class = KVM_LOONGARCH_IPI_GET_CLASS(ipi); ++ IPICore *cpu; ++ uint64_t attr; ++ int cpu_id = 0; ++ int fd = ipi_class->dev_fd; ++ ++ for (cpu_id = 0; cpu_id < ipi->num_cpu; cpu_id++) { ++ cpu = &ipi->cpu[cpu_id]; ++ attr = (cpu_id << 16) | CORE_STATUS_OFF; ++ kvm_ipi_access_regs(fd, attr, &cpu->status, false); ++ ++ attr = (cpu_id << 16) | CORE_EN_OFF; ++ kvm_ipi_access_regs(fd, attr, &cpu->en, false); ++ ++ attr = (cpu_id << 16) | CORE_SET_OFF; ++ kvm_ipi_access_regs(fd, attr, &cpu->set, false); ++ ++ attr = (cpu_id << 16) | CORE_CLEAR_OFF; ++ kvm_ipi_access_regs(fd, attr, &cpu->clear, false); ++ ++ attr = (cpu_id << 16) | CORE_BUF_20; ++ kvm_ipi_access_regs(fd, attr, &cpu->buf[0], false); ++ ++ attr = (cpu_id << 16) | CORE_BUF_28; ++ kvm_ipi_access_regs(fd, attr, &cpu->buf[2], false); ++ ++ attr = (cpu_id << 16) | CORE_BUF_30; ++ kvm_ipi_access_regs(fd, attr, &cpu->buf[4], false); ++ ++ attr = (cpu_id << 16) | CORE_BUF_38; ++ kvm_ipi_access_regs(fd, attr, &cpu->buf[6], false); ++ } ++ ++ return 0; ++} ++ ++static int kvm_loongarch_ipi_post_load(void *opaque, int version_id) ++{ ++ KVMLoongArchIPI *ipi = (KVMLoongArchIPI *)opaque; ++ KVMLoongArchIPIClass *ipi_class = KVM_LOONGARCH_IPI_GET_CLASS(ipi); ++ IPICore *cpu; ++ uint64_t attr; ++ int cpu_id = 0; ++ int fd = ipi_class->dev_fd; ++ ++ for (cpu_id = 0; cpu_id < ipi->num_cpu; cpu_id++) { ++ cpu = &ipi->cpu[cpu_id]; ++ attr = (cpu_id << 16) | CORE_STATUS_OFF; ++ kvm_ipi_access_regs(fd, attr, &cpu->status, true); ++ ++ attr = (cpu_id << 16) | CORE_EN_OFF; ++ kvm_ipi_access_regs(fd, attr, &cpu->en, true); ++ ++ attr = (cpu_id << 16) | CORE_SET_OFF; ++ kvm_ipi_access_regs(fd, attr, &cpu->set, true); ++ ++ attr = (cpu_id << 16) | CORE_CLEAR_OFF; ++ kvm_ipi_access_regs(fd, attr, &cpu->clear, true); ++ ++ attr = (cpu_id << 16) | CORE_BUF_20; ++ kvm_ipi_access_regs(fd, attr, &cpu->buf[0], true); ++ ++ attr = (cpu_id << 16) | CORE_BUF_28; ++ kvm_ipi_access_regs(fd, attr, &cpu->buf[2], true); ++ ++ attr = (cpu_id << 16) | CORE_BUF_30; ++ kvm_ipi_access_regs(fd, attr, &cpu->buf[4], true); ++ ++ attr = (cpu_id << 16) | CORE_BUF_38; ++ kvm_ipi_access_regs(fd, attr, &cpu->buf[6], true); ++ } ++ ++ return 0; ++} ++ ++static void kvm_loongarch_ipi_realize(DeviceState *dev, Error **errp) ++{ ++ KVMLoongArchIPI *ipi = KVM_LOONGARCH_IPI(dev); ++ KVMLoongArchIPIClass *ipi_class = KVM_LOONGARCH_IPI_GET_CLASS(dev); ++ struct kvm_create_device cd = {0}; ++ Error *err = NULL; ++ int ret; ++ ++ if (ipi->num_cpu == 0) { ++ error_setg(errp, "num-cpu must be at least 1"); ++ return; ++ } ++ ++ ipi_class->parent_realize(dev, &err); ++ if (err) { ++ error_propagate(errp, err); ++ return; ++ } ++ ++ ipi->cpu = g_new0(IPICore, ipi->num_cpu); ++ if (ipi->cpu == NULL) { ++ error_setg(errp, "Memory allocation for ExtIOICore faile"); ++ return; ++ } ++ ++ if (!ipi_class->is_created) { ++ cd.type = KVM_DEV_TYPE_LA_IPI; ++ ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd); ++ if (ret < 0) { ++ error_setg_errno(errp, errno, "Creating the KVM device failed"); ++ return; ++ } ++ ipi_class->is_created = true; ++ ipi_class->dev_fd = cd.fd; ++ fprintf(stdout, "Create LoongArch IPI irqchip in KVM done!\n"); ++ } ++ ++ assert(ipi_class->dev_fd != IPI_DEV_FD_UNDEF); ++} ++ ++static Property kvm_loongarch_ipi_properties[] = { ++ DEFINE_PROP_UINT32("num-cpu", KVMLoongArchIPI, num_cpu, 1), ++ DEFINE_PROP_END_OF_LIST() ++}; ++ ++static const VMStateDescription vmstate_kvm_ipi_core = { ++ .name = "kvm-ipi-single", ++ .version_id = 1, ++ .minimum_version_id = 1, ++ .fields = (VMStateField[]) { ++ VMSTATE_UINT32(status, IPICore), ++ VMSTATE_UINT32(en, IPICore), ++ VMSTATE_UINT32(set, IPICore), ++ VMSTATE_UINT32(clear, IPICore), ++ VMSTATE_UINT32_ARRAY(buf, IPICore, 8), ++ VMSTATE_END_OF_LIST() ++ } ++}; ++ ++static const VMStateDescription vmstate_kvm_loongarch_ipi = { ++ .name = TYPE_KVM_LOONGARCH_IPI, ++ .version_id = 1, ++ .minimum_version_id = 1, ++ .pre_save = kvm_loongarch_ipi_pre_save, ++ .post_load = kvm_loongarch_ipi_post_load, ++ .fields = (VMStateField[]) { ++ VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, KVMLoongArchIPI, num_cpu, ++ vmstate_kvm_ipi_core, IPICore), ++ ++ VMSTATE_END_OF_LIST() ++ } ++}; ++ ++static void kvm_loongarch_ipi_class_init(ObjectClass *oc, void *data) ++{ ++ DeviceClass *dc = DEVICE_CLASS(oc); ++ KVMLoongArchIPIClass *ipi_class = KVM_LOONGARCH_IPI_CLASS(oc); ++ ++ ipi_class->parent_realize = dc->realize; ++ dc->realize = kvm_loongarch_ipi_realize; ++ ++ ipi_class->is_created = false; ++ ipi_class->dev_fd = IPI_DEV_FD_UNDEF; ++ ++ device_class_set_props(dc, kvm_loongarch_ipi_properties); ++ ++ dc->vmsd = &vmstate_kvm_loongarch_ipi; ++} ++ ++static const TypeInfo kvm_loongarch_ipi_info = { ++ .name = TYPE_KVM_LOONGARCH_IPI, ++ .parent = TYPE_SYS_BUS_DEVICE, ++ .instance_size = sizeof(KVMLoongArchIPI), ++ .class_size = sizeof(KVMLoongArchIPIClass), ++ .class_init = kvm_loongarch_ipi_class_init, ++}; ++ ++static void kvm_loongarch_ipi_register_types(void) ++{ ++ type_register_static(&kvm_loongarch_ipi_info); ++} ++ ++type_init(kvm_loongarch_ipi_register_types) +diff --git a/hw/intc/meson.build b/hw/intc/meson.build +index ed35594..9deeeb5 100644 +--- a/hw/intc/meson.build ++++ b/hw/intc/meson.build +@@ -70,6 +70,7 @@ specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'], + specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c')) + specific_ss.add(when: 'CONFIG_NIOS2_VIC', if_true: files('nios2_vic.c')) + specific_ss.add(when: 'CONFIG_LOONGARCH_IPI', if_true: files('loongarch_ipi.c')) ++specific_ss.add(when: 'CONFIG_LOONGARCH_IPI_KVM', if_true: files('loongarch_ipi_kvm.c')) + specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c')) + specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_msi.c')) + specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extioi.c')) +diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig +index bf3ad0c..c6e4976 100644 +--- a/hw/loongarch/Kconfig ++++ b/hw/loongarch/Kconfig +@@ -12,6 +12,7 @@ config LOONGARCH_VIRT + select LOONGARCH_PCH_PIC + select LOONGARCH_PCH_MSI + select LOONGARCH_EXTIOI ++ select LOONGARCH_IPI_KVM if KVM + select LS7A_RTC + select SMBIOS + select ACPI_CPU_HOTPLUG +diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c +index 4f17079..a570b0d 100644 +--- a/hw/loongarch/virt.c ++++ b/hw/loongarch/virt.c +@@ -49,6 +49,7 @@ + #include "hw/virtio/virtio-iommu.h" + #include "qemu/error-report.h" + #include "qemu/guest-random.h" ++#include "sysemu/kvm.h" + + static bool virt_is_veiointc_enabled(LoongArchVirtMachineState *lvms) + { +@@ -849,16 +850,21 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) + * +--------+ +---------+ +---------+ + */ + +- /* Create IPI device */ +- ipi = qdev_new(TYPE_LOONGARCH_IPI); +- qdev_prop_set_uint32(ipi, "num-cpu", ms->smp.max_cpus); +- sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal); +- +- /* IPI iocsr memory region */ +- memory_region_add_subregion(&lvms->system_iocsr, SMP_IPI_MAILBOX, +- sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 0)); +- memory_region_add_subregion(&lvms->system_iocsr, MAIL_SEND_ADDR, +- sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 1)); ++ if (kvm_enabled() && kvm_irqchip_in_kernel()) { ++ ipi = qdev_new(TYPE_KVM_LOONGARCH_IPI); ++ qdev_prop_set_int32(ipi, "num-cpu", ms->smp.max_cpus); ++ sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal); ++ } else { ++ ipi = qdev_new(TYPE_LOONGARCH_IPI); ++ qdev_prop_set_uint32(ipi, "num-cpu", ms->smp.max_cpus); ++ sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal); ++ ++ /* IPI iocsr memory region */ ++ memory_region_add_subregion(&lvms->system_iocsr, SMP_IPI_MAILBOX, ++ sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 0)); ++ memory_region_add_subregion(&lvms->system_iocsr, MAIL_SEND_ADDR, ++ sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 1)); ++ } + + /* Add cpu interrupt-controller */ + fdt_add_cpuic_node(lvms, &cpuintc_phandle); +@@ -869,10 +875,6 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) + lacpu = LOONGARCH_CPU(cpu_state); + env = &(lacpu->env); + env->address_space_iocsr = &lvms->as_iocsr; +- +- /* connect ipi irq to cpu irq */ +- qdev_connect_gpio_out(ipi, cpu, qdev_get_gpio_in(cpudev, IRQ_IPI)); +- env->ipistate = ipi; + } + + lvms->ipi = ipi; +@@ -1495,15 +1497,18 @@ static void virt_cpu_plug(HotplugHandler *hotplug_dev, + env = &(cpu->env); + env->address_space_iocsr = &lvms->as_iocsr; + +- /* connect ipi irq to cpu irq, logic cpu index used here */ +- qdev_connect_gpio_out(lvms->ipi, cs->cpu_index, +- qdev_get_gpio_in(dev, IRQ_IPI)); +- env->ipistate = lvms->ipi; + +- for (pin = 0; pin < LS3A_INTC_IP; pin++) { +- qdev_connect_gpio_out(lvms->extioi, (cs->cpu_index * 8 + pin), +- qdev_get_gpio_in(dev, pin + 2)); +- } ++ env->ipistate = lvms->ipi; ++ if (!(kvm_enabled() && kvm_irqchip_in_kernel())) { ++ /* connect ipi irq to cpu irq, logic cpu index used here */ ++ qdev_connect_gpio_out(lvms->ipi, cs->cpu_index, ++ qdev_get_gpio_in(dev, IRQ_IPI)); ++ ++ for (pin = 0; pin < LS3A_INTC_IP; pin++) { ++ qdev_connect_gpio_out(lvms->extioi, (cs->cpu_index * 8 + pin), ++ qdev_get_gpio_in(dev, pin + 2)); ++ } ++ } + hhc = HOTPLUG_HANDLER_GET_CLASS(lvms->acpi_ged); + hhc->plug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &local_err); + if (local_err) { +diff --git a/include/hw/intc/loongarch_ipi.h b/include/hw/intc/loongarch_ipi.h +index 1c1e834..a1a4a21 100644 +--- a/include/hw/intc/loongarch_ipi.h ++++ b/include/hw/intc/loongarch_ipi.h +@@ -32,6 +32,7 @@ + + #define TYPE_LOONGARCH_IPI "loongarch_ipi" + OBJECT_DECLARE_SIMPLE_TYPE(LoongArchIPI, LOONGARCH_IPI) ++#define TYPE_KVM_LOONGARCH_IPI "loongarch-ipi-kvm" + + typedef struct IPICore { + uint32_t status; +@@ -51,4 +52,25 @@ struct LoongArchIPI { + IPICore *cpu; + }; + ++struct KVMLoongArchIPI { ++ SysBusDevice parent_obj; ++ uint32_t num_cpu; ++ IPICore *cpu; ++}; ++typedef struct KVMLoongArchIPI KVMLoongArchIPI; ++DECLARE_INSTANCE_CHECKER(KVMLoongArchIPI, KVM_LOONGARCH_IPI, ++ TYPE_KVM_LOONGARCH_IPI) ++ ++struct KVMLoongArchIPIClass { ++ SysBusDeviceClass parent_class; ++ DeviceRealize parent_realize; ++ ++ bool is_created; ++ int dev_fd; ++ ++}; ++typedef struct KVMLoongArchIPIClass KVMLoongArchIPIClass; ++DECLARE_CLASS_CHECKERS(KVMLoongArchIPIClass, KVM_LOONGARCH_IPI, ++ TYPE_KVM_LOONGARCH_IPI) ++ + #endif +diff --git a/linux-headers/asm-loongarch/kvm.h b/linux-headers/asm-loongarch/kvm.h +index d619b94..5fbf4a6 100644 +--- a/linux-headers/asm-loongarch/kvm.h ++++ b/linux-headers/asm-loongarch/kvm.h +@@ -147,4 +147,7 @@ struct kvm_iocsr_entry { + + #define KVM_DEV_LOONGARCH_PCH_PIC_GRP_REGS 0x40000005 + ++#define KVM_LOONGARCH_VM_HAVE_IRQCHIP 0x40000001 ++ ++#define KVM_DEV_LOONGARCH_IPI_GRP_REGS 0x40000002 + #endif /* __UAPI_ASM_LOONGARCH_KVM_H */ +diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h +index 36da75b..dbe953d 100644 +--- a/linux-headers/linux/kvm.h ++++ b/linux-headers/linux/kvm.h +@@ -1464,6 +1464,8 @@ enum kvm_device_type { + #define KVM_DEV_TYPE_ARM_PV_TIME KVM_DEV_TYPE_ARM_PV_TIME + KVM_DEV_TYPE_RISCV_AIA, + #define KVM_DEV_TYPE_RISCV_AIA KVM_DEV_TYPE_RISCV_AIA ++ KVM_DEV_TYPE_LA_IPI, ++#define KVM_DEV_TYPE_LA_IPI KVM_DEV_TYPE_LA_IPI + KVM_DEV_TYPE_MAX, + }; + +diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c +index f98abf0..e2e77ab 100644 +--- a/target/loongarch/kvm/kvm.c ++++ b/target/loongarch/kvm/kvm.c +@@ -962,7 +962,12 @@ int kvm_arch_get_default_type(MachineState *ms) + + int kvm_arch_init(MachineState *ms, KVMState *s) + { ++ s->kernel_irqchip_allowed = false; + cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE); ++ if(!kvm_vm_check_attr(kvm_state, KVM_LOONGARCH_VM_HAVE_IRQCHIP, KVM_LOONGARCH_VM_HAVE_IRQCHIP)) { ++ s->kernel_irqchip_allowed = false; ++ } ++ + return 0; + } + +-- +1.8.3.1 + diff --git a/0265-hw-loongarch-Add-KVM-extioi-device-support.patch b/0265-hw-loongarch-Add-KVM-extioi-device-support.patch new file mode 100644 index 0000000000000000000000000000000000000000..4ca1ec5e85c460869737b791dc2bb32d54775ef2 --- /dev/null +++ b/0265-hw-loongarch-Add-KVM-extioi-device-support.patch @@ -0,0 +1,393 @@ +From bda408a1dafde7261253f184d39d993a14327c73 Mon Sep 17 00:00:00 2001 +From: Xianglai Li +Date: Thu, 11 Jul 2024 20:11:31 +0800 +Subject: [PATCH 275/293] hw/loongarch: Add KVM extioi device support + +Added extioi interrupt controller for kvm emulation. +The main process is to send the command word for +creating an extioi device to the kernel. +When the VM is saved, the ioctl obtains the related +data of the extioi interrupt controller in the kernel +and saves it. When the VM is recovered, the saved data +is sent to the kernel. + +Signed-off-by: Tianrui Zhao +Signed-off-by: Xianglai Li +--- + hw/intc/Kconfig | 3 + + hw/intc/loongarch_extioi_kvm.c | 150 +++++++++++++++++++++++++++++++++++++ + hw/intc/meson.build | 1 + + hw/loongarch/Kconfig | 1 + + hw/loongarch/virt.c | 48 ++++++------ + include/hw/intc/loongarch_extioi.h | 34 ++++++++- + include/hw/loongarch/virt.h | 15 ++++ + linux-headers/asm-loongarch/kvm.h | 3 + + linux-headers/linux/kvm.h | 2 + + 9 files changed, 233 insertions(+), 24 deletions(-) + create mode 100644 hw/intc/loongarch_extioi_kvm.c + +diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig +index eddea0f..3bf0c82 100644 +--- a/hw/intc/Kconfig ++++ b/hw/intc/Kconfig +@@ -107,3 +107,6 @@ config LOONGARCH_PCH_MSI + + config LOONGARCH_EXTIOI + bool ++ ++config LOONGARCH_EXTIOI_KVM ++ bool +diff --git a/hw/intc/loongarch_extioi_kvm.c b/hw/intc/loongarch_extioi_kvm.c +new file mode 100644 +index 0000000..f5bbc33 +--- /dev/null ++++ b/hw/intc/loongarch_extioi_kvm.c +@@ -0,0 +1,150 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * LoongArch kvm extioi interrupt support ++ * ++ * Copyright (C) 2024 Loongson Technology Corporation Limited ++ */ ++ ++#include "qemu/osdep.h" ++#include "hw/qdev-properties.h" ++#include "qemu/typedefs.h" ++#include "hw/intc/loongarch_extioi.h" ++#include "hw/sysbus.h" ++#include "linux/kvm.h" ++#include "migration/vmstate.h" ++#include "qapi/error.h" ++#include "sysemu/kvm.h" ++ ++static void kvm_extioi_access_regs(int fd, uint64_t addr, ++ void *val, int is_write) ++{ ++ kvm_device_access(fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_REGS, ++ addr, val, is_write, &error_abort); ++} ++ ++static int kvm_loongarch_extioi_pre_save(void *opaque) ++{ ++ KVMLoongArchExtIOI *s = (KVMLoongArchExtIOI *)opaque; ++ KVMLoongArchExtIOIClass *class = KVM_LOONGARCH_EXTIOI_GET_CLASS(s); ++ int fd = class->dev_fd; ++ ++ kvm_extioi_access_regs(fd, EXTIOI_NODETYPE_START, ++ (void *)s->nodetype, false); ++ kvm_extioi_access_regs(fd, EXTIOI_IPMAP_START, (void *)s->ipmap, false); ++ kvm_extioi_access_regs(fd, EXTIOI_ENABLE_START, (void *)s->enable, false); ++ kvm_extioi_access_regs(fd, EXTIOI_BOUNCE_START, (void *)s->bounce, false); ++ kvm_extioi_access_regs(fd, EXTIOI_ISR_START, (void *)s->isr, false); ++ kvm_extioi_access_regs(fd, EXTIOI_COREMAP_START, ++ (void *)s->coremap, false); ++ kvm_extioi_access_regs(fd, EXTIOI_SW_COREMAP_FLAG, ++ (void *)s->sw_coremap, false); ++ kvm_extioi_access_regs(fd, EXTIOI_COREISR_START, ++ (void *)s->coreisr, false); ++ ++ return 0; ++} ++ ++static int kvm_loongarch_extioi_post_load(void *opaque, int version_id) ++{ ++ KVMLoongArchExtIOI *s = (KVMLoongArchExtIOI *)opaque; ++ KVMLoongArchExtIOIClass *class = KVM_LOONGARCH_EXTIOI_GET_CLASS(s); ++ int fd = class->dev_fd; ++ ++ kvm_extioi_access_regs(fd, EXTIOI_NODETYPE_START, ++ (void *)s->nodetype, true); ++ kvm_extioi_access_regs(fd, EXTIOI_IPMAP_START, (void *)s->ipmap, true); ++ kvm_extioi_access_regs(fd, EXTIOI_ENABLE_START, (void *)s->enable, true); ++ kvm_extioi_access_regs(fd, EXTIOI_BOUNCE_START, (void *)s->bounce, true); ++ kvm_extioi_access_regs(fd, EXTIOI_ISR_START, (void *)s->isr, true); ++ kvm_extioi_access_regs(fd, EXTIOI_COREMAP_START, (void *)s->coremap, true); ++ kvm_extioi_access_regs(fd, EXTIOI_SW_COREMAP_FLAG, ++ (void *)s->sw_coremap, true); ++ kvm_extioi_access_regs(fd, EXTIOI_COREISR_START, (void *)s->coreisr, true); ++ ++ return 0; ++} ++ ++static void kvm_loongarch_extioi_realize(DeviceState *dev, Error **errp) ++{ ++ KVMLoongArchExtIOIClass *extioi_class = KVM_LOONGARCH_EXTIOI_GET_CLASS(dev); ++ struct kvm_create_device cd = {0}; ++ Error *err = NULL; ++ int ret,i; ++ ++ extioi_class->parent_realize(dev, &err); ++ if (err) { ++ error_propagate(errp, err); ++ return; ++ } ++ ++ if (!extioi_class->is_created) { ++ cd.type = KVM_DEV_TYPE_LA_EXTIOI; ++ ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd); ++ if (ret < 0) { ++ error_setg_errno(errp, errno, ++ "Creating the KVM extioi device failed"); ++ return; ++ } ++ extioi_class->is_created = true; ++ extioi_class->dev_fd = cd.fd; ++ fprintf(stdout, "Create LoongArch extioi irqchip in KVM done!\n"); ++ } ++ ++ kvm_async_interrupts_allowed = true; ++ kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled(); ++ if (kvm_has_gsi_routing()) { ++ for (i = 0; i < 64; ++i) { ++ kvm_irqchip_add_irq_route(kvm_state, i, 0, i); ++ } ++ kvm_gsi_routing_allowed = true; ++ } ++} ++ ++static const VMStateDescription vmstate_kvm_extioi_core = { ++ .name = "kvm-extioi-single", ++ .version_id = 1, ++ .minimum_version_id = 1, ++ .pre_save = kvm_loongarch_extioi_pre_save, ++ .post_load = kvm_loongarch_extioi_post_load, ++ .fields = (VMStateField[]) { ++ VMSTATE_UINT32_ARRAY(nodetype, KVMLoongArchExtIOI, ++ EXTIOI_IRQS_NODETYPE_COUNT / 2), ++ VMSTATE_UINT32_ARRAY(bounce, KVMLoongArchExtIOI, ++ EXTIOI_IRQS_GROUP_COUNT), ++ VMSTATE_UINT32_ARRAY(isr, KVMLoongArchExtIOI, EXTIOI_IRQS / 32), ++ VMSTATE_UINT32_2DARRAY(coreisr, KVMLoongArchExtIOI, EXTIOI_CPUS, ++ EXTIOI_IRQS_GROUP_COUNT), ++ VMSTATE_UINT32_ARRAY(enable, KVMLoongArchExtIOI, EXTIOI_IRQS / 32), ++ VMSTATE_UINT32_ARRAY(ipmap, KVMLoongArchExtIOI, ++ EXTIOI_IRQS_IPMAP_SIZE / 4), ++ VMSTATE_UINT32_ARRAY(coremap, KVMLoongArchExtIOI, EXTIOI_IRQS / 4), ++ VMSTATE_UINT8_ARRAY(sw_coremap, KVMLoongArchExtIOI, EXTIOI_IRQS), ++ VMSTATE_END_OF_LIST() ++ } ++}; ++ ++static void kvm_loongarch_extioi_class_init(ObjectClass *oc, void *data) ++{ ++ DeviceClass *dc = DEVICE_CLASS(oc); ++ KVMLoongArchExtIOIClass *extioi_class = KVM_LOONGARCH_EXTIOI_CLASS(oc); ++ ++ extioi_class->parent_realize = dc->realize; ++ dc->realize = kvm_loongarch_extioi_realize; ++ extioi_class->is_created = false; ++ dc->vmsd = &vmstate_kvm_extioi_core; ++} ++ ++static const TypeInfo kvm_loongarch_extioi_info = { ++ .name = TYPE_KVM_LOONGARCH_EXTIOI, ++ .parent = TYPE_SYS_BUS_DEVICE, ++ .instance_size = sizeof(KVMLoongArchExtIOI), ++ .class_size = sizeof(KVMLoongArchExtIOIClass), ++ .class_init = kvm_loongarch_extioi_class_init, ++}; ++ ++static void kvm_loongarch_extioi_register_types(void) ++{ ++ type_register_static(&kvm_loongarch_extioi_info); ++} ++ ++type_init(kvm_loongarch_extioi_register_types) +diff --git a/hw/intc/meson.build b/hw/intc/meson.build +index 9deeeb5..a37d7da 100644 +--- a/hw/intc/meson.build ++++ b/hw/intc/meson.build +@@ -74,3 +74,4 @@ specific_ss.add(when: 'CONFIG_LOONGARCH_IPI_KVM', if_true: files('loongarch_ipi_ + specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c')) + specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_msi.c')) + specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extioi.c')) ++specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI_KVM', if_true: files('loongarch_extioi_kvm.c')) +diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig +index c6e4976..fc86381 100644 +--- a/hw/loongarch/Kconfig ++++ b/hw/loongarch/Kconfig +@@ -13,6 +13,7 @@ config LOONGARCH_VIRT + select LOONGARCH_PCH_MSI + select LOONGARCH_EXTIOI + select LOONGARCH_IPI_KVM if KVM ++ select LOONGARCH_EXTIOI_KVM if KVM + select LS7A_RTC + select SMBIOS + select ACPI_CPU_HOTPLUG +diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c +index a570b0d..0b0eb05 100644 +--- a/hw/loongarch/virt.c ++++ b/hw/loongarch/virt.c +@@ -879,29 +879,33 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) + + lvms->ipi = ipi; + +- /* Create EXTIOI device */ +- extioi = qdev_new(TYPE_LOONGARCH_EXTIOI); +- qdev_prop_set_uint32(extioi, "num-cpu", ms->smp.max_cpus); +- if (virt_is_veiointc_enabled(lvms)) { +- qdev_prop_set_bit(extioi, "has-virtualization-extension", true); +- } +- sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal); +- memory_region_add_subregion(&lvms->system_iocsr, APIC_BASE, +- sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 0)); +- if (virt_is_veiointc_enabled(lvms)) { +- memory_region_add_subregion(&lvms->system_iocsr, EXTIOI_VIRT_BASE, +- sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 1)); +- } ++ if (kvm_enabled() && kvm_irqchip_in_kernel()) { ++ extioi = qdev_new(TYPE_KVM_LOONGARCH_EXTIOI); ++ sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal); ++ } else { ++ extioi = qdev_new(TYPE_LOONGARCH_EXTIOI); ++ qdev_prop_set_uint32(extioi, "num-cpu", ms->smp.max_cpus); ++ if (virt_is_veiointc_enabled(lvms)) { ++ qdev_prop_set_bit(extioi, "has-virtualization-extension", true); ++ } ++ sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal); ++ memory_region_add_subregion(&lvms->system_iocsr, APIC_BASE, ++ sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 0)); ++ if (virt_is_veiointc_enabled(lvms)) { ++ memory_region_add_subregion(&lvms->system_iocsr, EXTIOI_VIRT_BASE, ++ sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 1)); ++ } + +- /* +- * connect ext irq to the cpu irq +- * cpu_pin[9:2] <= intc_pin[7:0] +- */ +- for (cpu = 0; cpu < ms->smp.cpus; cpu++) { +- cpudev = DEVICE(qemu_get_cpu(cpu)); +- for (pin = 0; pin < LS3A_INTC_IP; pin++) { +- qdev_connect_gpio_out(extioi, (cpu * 8 + pin), +- qdev_get_gpio_in(cpudev, pin + 2)); ++ /* ++ * connect ext irq to the cpu irq ++ * cpu_pin[9:2] <= intc_pin[7:0] ++ */ ++ for (cpu = 0; cpu < ms->smp.cpus; cpu++) { ++ cpudev = DEVICE(qemu_get_cpu(cpu)); ++ for (pin = 0; pin < LS3A_INTC_IP; pin++) { ++ qdev_connect_gpio_out(extioi, (cpu * 8 + pin), ++ qdev_get_gpio_in(cpudev, pin + 2)); ++ } + } + } + +diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h +index 626a37d..e8378f60 100644 +--- a/include/hw/intc/loongarch_extioi.h ++++ b/include/hw/intc/loongarch_extioi.h +@@ -15,7 +15,7 @@ + #define EXTIOI_IRQS (256) + #define EXTIOI_IRQS_BITMAP_SIZE (256 / 8) + /* irq from EXTIOI is routed to no more than 4 cpus */ +-#define EXTIOI_CPUS (4) ++#define EXTIOI_CPUS (256) + /* map to ipnum per 32 irqs */ + #define EXTIOI_IRQS_IPMAP_SIZE (256 / 32) + #define EXTIOI_IRQS_COREMAP_SIZE 256 +@@ -58,13 +58,16 @@ + #define EXTIOI_VIRT_COREMAP_START (0x40) + #define EXTIOI_VIRT_COREMAP_END (0x240) + ++#define EXTIOI_SW_COREMAP_FLAG (1 << 0) ++ + typedef struct ExtIOICore { + uint32_t coreisr[EXTIOI_IRQS_GROUP_COUNT]; + DECLARE_BITMAP(sw_isr[LS3A_INTC_IP], EXTIOI_IRQS); + qemu_irq parent_irq[LS3A_INTC_IP]; + } ExtIOICore; + +-#define TYPE_LOONGARCH_EXTIOI "loongarch.extioi" ++#define TYPE_LOONGARCH_EXTIOI "loongarch-extioi" ++#define TYPE_KVM_LOONGARCH_EXTIOI "loongarch-kvm-extioi" + OBJECT_DECLARE_SIMPLE_TYPE(LoongArchExtIOI, LOONGARCH_EXTIOI) + struct LoongArchExtIOI { + SysBusDevice parent_obj; +@@ -86,4 +89,31 @@ struct LoongArchExtIOI { + MemoryRegion extioi_system_mem; + MemoryRegion virt_extend; + }; ++ ++struct KVMLoongArchExtIOI { ++ SysBusDevice parent_obj; ++ /* hardware state */ ++ uint32_t nodetype[EXTIOI_IRQS_NODETYPE_COUNT / 2]; ++ uint32_t bounce[EXTIOI_IRQS_GROUP_COUNT]; ++ uint32_t isr[EXTIOI_IRQS / 32]; ++ uint32_t coreisr[EXTIOI_CPUS][EXTIOI_IRQS_GROUP_COUNT]; ++ uint32_t enable[EXTIOI_IRQS / 32]; ++ uint32_t ipmap[EXTIOI_IRQS_IPMAP_SIZE / 4]; ++ uint32_t coremap[EXTIOI_IRQS / 4]; ++ uint8_t sw_coremap[EXTIOI_IRQS]; ++}; ++typedef struct KVMLoongArchExtIOI KVMLoongArchExtIOI; ++DECLARE_INSTANCE_CHECKER(KVMLoongArchExtIOI, KVM_LOONGARCH_EXTIOI, ++ TYPE_KVM_LOONGARCH_EXTIOI) ++ ++struct KVMLoongArchExtIOIClass { ++ SysBusDeviceClass parent_class; ++ DeviceRealize parent_realize; ++ ++ bool is_created; ++ int dev_fd; ++}; ++typedef struct KVMLoongArchExtIOIClass KVMLoongArchExtIOIClass; ++DECLARE_CLASS_CHECKERS(KVMLoongArchExtIOIClass, KVM_LOONGARCH_EXTIOI, ++ TYPE_KVM_LOONGARCH_EXTIOI) + #endif /* LOONGARCH_EXTIOI_H */ +diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h +index 25abc23..dce7c50 100644 +--- a/include/hw/loongarch/virt.h ++++ b/include/hw/loongarch/virt.h +@@ -37,6 +37,21 @@ + + #define FDT_BASE 0x100000 + ++/* KVM_IRQ_LINE irq field index values */ ++#define KVM_LOONGARCH_IRQ_TYPE_SHIFT 24 ++#define KVM_LOONGARCH_IRQ_TYPE_MASK 0xff ++#define KVM_LOONGARCH_IRQ_VCPU_SHIFT 16 ++#define KVM_LOONGARCH_IRQ_VCPU_MASK 0xff ++#define KVM_LOONGARCH_IRQ_NUM_SHIFT 0 ++#define KVM_LOONGARCH_IRQ_NUM_MASK 0xffff ++ ++/* irq_type field */ ++#define KVM_LOONGARCH_IRQ_TYPE_CPU_IP 0 ++#define KVM_LOONGARCH_IRQ_TYPE_CPU_IO 1 ++#define KVM_LOONGARCH_IRQ_TYPE_HT 2 ++#define KVM_LOONGARCH_IRQ_TYPE_MSI 3 ++#define KVM_LOONGARCH_IRQ_TYPE_IOAPIC 4 ++ + struct LoongArchVirtMachineState { + /*< private >*/ + MachineState parent_obj; +diff --git a/linux-headers/asm-loongarch/kvm.h b/linux-headers/asm-loongarch/kvm.h +index 5fbf4a6..b4f8c2e 100644 +--- a/linux-headers/asm-loongarch/kvm.h ++++ b/linux-headers/asm-loongarch/kvm.h +@@ -150,4 +150,7 @@ struct kvm_iocsr_entry { + #define KVM_LOONGARCH_VM_HAVE_IRQCHIP 0x40000001 + + #define KVM_DEV_LOONGARCH_IPI_GRP_REGS 0x40000002 ++ ++#define KVM_DEV_LOONGARCH_EXTIOI_GRP_REGS 0x40000003 ++ + #endif /* __UAPI_ASM_LOONGARCH_KVM_H */ +diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h +index dbe953d..788457f 100644 +--- a/linux-headers/linux/kvm.h ++++ b/linux-headers/linux/kvm.h +@@ -1466,6 +1466,8 @@ enum kvm_device_type { + #define KVM_DEV_TYPE_RISCV_AIA KVM_DEV_TYPE_RISCV_AIA + KVM_DEV_TYPE_LA_IPI, + #define KVM_DEV_TYPE_LA_IPI KVM_DEV_TYPE_LA_IPI ++ KVM_DEV_TYPE_LA_EXTIOI, ++#define KVM_DEV_TYPE_LA_EXTIOI KVM_DEV_TYPE_LA_EXTIOI + KVM_DEV_TYPE_MAX, + }; + +-- +1.8.3.1 + diff --git a/0266-hw-loongarch-Add-KVM-pch-pic-device-support.patch b/0266-hw-loongarch-Add-KVM-pch-pic-device-support.patch new file mode 100644 index 0000000000000000000000000000000000000000..18f2e3b8a67eab3013bbc48c0ae8ebdfca056e5f --- /dev/null +++ b/0266-hw-loongarch-Add-KVM-pch-pic-device-support.patch @@ -0,0 +1,491 @@ +From 8d4e22880d1a1d5eb0f49a1bf247f49f2b9300b2 Mon Sep 17 00:00:00 2001 +From: Xianglai Li +Date: Thu, 7 Mar 2024 20:11:36 +0800 +Subject: [PATCH 276/293] hw/loongarch: Add KVM pch pic device support + +Added pch_pic interrupt controller for kvm emulation. +The main process is to send the command word for +creating an pch_pic device to the kernel, +Delivers the pch pic interrupt controller configuration +register base address to the kernel. +When the VM is saved, the ioctl obtains the pch_pic +interrupt controller data in the kernel and saves it. +When the VM is recovered, the saved data is sent to the kernel. + +Signed-off-by: Xianglai Li +--- + hw/intc/Kconfig | 3 + + hw/intc/loongarch_pch_pic.c | 20 +++- + hw/intc/loongarch_pch_pic_kvm.c | 189 ++++++++++++++++++++++++++++++++++++ + hw/intc/meson.build | 1 + + hw/loongarch/Kconfig | 1 + + hw/loongarch/virt.c | 62 ++++++------ + include/hw/intc/loongarch_pch_pic.h | 51 +++++++++- + linux-headers/asm-loongarch/kvm.h | 5 + + linux-headers/linux/kvm.h | 2 + + 9 files changed, 303 insertions(+), 31 deletions(-) + create mode 100644 hw/intc/loongarch_pch_pic_kvm.c + +diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig +index 3bf0c82..9574ab4 100644 +--- a/hw/intc/Kconfig ++++ b/hw/intc/Kconfig +@@ -100,6 +100,9 @@ config LOONGARCH_PCH_PIC + bool + select UNIMP + ++config LOONGARCH_PCH_PIC_KVM ++ bool ++ + config LOONGARCH_PCH_MSI + select MSI_NONBROKEN + bool +diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c +index 6aa4cad..f801cfe 100644 +--- a/hw/intc/loongarch_pch_pic.c ++++ b/hw/intc/loongarch_pch_pic.c +@@ -16,18 +16,27 @@ + #include "migration/vmstate.h" + #include "trace.h" + #include "qapi/error.h" ++#include "sysemu/kvm.h" + + static void pch_pic_update_irq(LoongArchPCHPIC *s, uint64_t mask, int level) + { + uint64_t val; + int irq; ++ int kvm_irq; + + if (level) { + val = mask & s->intirr & ~s->int_mask; + if (val) { + irq = ctz64(val); + s->intisr |= MAKE_64BIT_MASK(irq, 1); +- qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 1); ++ if (kvm_enabled() && kvm_irqchip_in_kernel()) { ++ kvm_irq = ( ++ KVM_LOONGARCH_IRQ_TYPE_IOAPIC << KVM_LOONGARCH_IRQ_TYPE_SHIFT) ++ | (0 << KVM_LOONGARCH_IRQ_VCPU_SHIFT) | s->htmsi_vector[irq]; ++ kvm_set_irq(kvm_state, kvm_irq, !!level); ++ } else { ++ qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 1); ++ } + } + } else { + /* +@@ -38,7 +47,14 @@ static void pch_pic_update_irq(LoongArchPCHPIC *s, uint64_t mask, int level) + if (val) { + irq = ctz64(val); + s->intisr &= ~MAKE_64BIT_MASK(irq, 1); +- qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0); ++ if (kvm_enabled() && kvm_irqchip_in_kernel()) { ++ kvm_irq = ( ++ KVM_LOONGARCH_IRQ_TYPE_IOAPIC << KVM_LOONGARCH_IRQ_TYPE_SHIFT) ++ | (0 << KVM_LOONGARCH_IRQ_VCPU_SHIFT) | s->htmsi_vector[irq]; ++ kvm_set_irq(kvm_state, kvm_irq, !!level); ++ } else { ++ qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0); ++ } + } + } + } +diff --git a/hw/intc/loongarch_pch_pic_kvm.c b/hw/intc/loongarch_pch_pic_kvm.c +new file mode 100644 +index 0000000..8f66d9a +--- /dev/null ++++ b/hw/intc/loongarch_pch_pic_kvm.c +@@ -0,0 +1,189 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * LoongArch kvm pch pic interrupt support ++ * ++ * Copyright (C) 2024 Loongson Technology Corporation Limited ++ */ ++ ++#include "qemu/osdep.h" ++#include "hw/qdev-properties.h" ++#include "qemu/typedefs.h" ++#include "hw/intc/loongarch_pch_pic.h" ++#include "hw/sysbus.h" ++#include "linux/kvm.h" ++#include "migration/vmstate.h" ++#include "qapi/error.h" ++#include "sysemu/kvm.h" ++#include "hw/loongarch/virt.h" ++#include "hw/pci-host/ls7a.h" ++#include "qemu/error-report.h" ++ ++static void kvm_pch_pic_access_regs(int fd, uint64_t addr, ++ void *val, int is_write) ++{ ++ kvm_device_access(fd, KVM_DEV_LOONGARCH_PCH_PIC_GRP_REGS, ++ addr, val, is_write, &error_abort); ++} ++ ++static int kvm_loongarch_pch_pic_pre_save(void *opaque) ++{ ++ KVMLoongArchPCHPIC *s = (KVMLoongArchPCHPIC *)opaque; ++ KVMLoongArchPCHPICClass *class = KVM_LOONGARCH_PCH_PIC_GET_CLASS(s); ++ int fd = class->dev_fd; ++ ++ kvm_pch_pic_access_regs(fd, PCH_PIC_MASK_START, ++ (void *)&s->int_mask, false); ++ kvm_pch_pic_access_regs(fd, PCH_PIC_HTMSI_EN_START, ++ (void *)&s->htmsi_en, false); ++ kvm_pch_pic_access_regs(fd, PCH_PIC_EDGE_START, ++ (void *)&s->intedge, false); ++ kvm_pch_pic_access_regs(fd, PCH_PIC_AUTO_CTRL0_START, ++ (void *)&s->auto_crtl0, false); ++ kvm_pch_pic_access_regs(fd, PCH_PIC_AUTO_CTRL1_START, ++ (void *)&s->auto_crtl1, false); ++ kvm_pch_pic_access_regs(fd, PCH_PIC_ROUTE_ENTRY_START, ++ (void *)s->route_entry, false); ++ kvm_pch_pic_access_regs(fd, PCH_PIC_HTMSI_VEC_START, ++ (void *)s->htmsi_vector, false); ++ kvm_pch_pic_access_regs(fd, PCH_PIC_INT_IRR_START, ++ (void *)&s->intirr, false); ++ kvm_pch_pic_access_regs(fd, PCH_PIC_INT_ISR_START, ++ (void *)&s->intisr, false); ++ kvm_pch_pic_access_regs(fd, PCH_PIC_POLARITY_START, ++ (void *)&s->int_polarity, false); ++ ++ return 0; ++} ++ ++static int kvm_loongarch_pch_pic_post_load(void *opaque, int version_id) ++{ ++ KVMLoongArchPCHPIC *s = (KVMLoongArchPCHPIC *)opaque; ++ KVMLoongArchPCHPICClass *class = KVM_LOONGARCH_PCH_PIC_GET_CLASS(s); ++ int fd = class->dev_fd; ++ ++ kvm_pch_pic_access_regs(fd, PCH_PIC_MASK_START, ++ (void *)&s->int_mask, true); ++ kvm_pch_pic_access_regs(fd, PCH_PIC_HTMSI_EN_START, ++ (void *)&s->htmsi_en, true); ++ kvm_pch_pic_access_regs(fd, PCH_PIC_EDGE_START, ++ (void *)&s->intedge, true); ++ kvm_pch_pic_access_regs(fd, PCH_PIC_AUTO_CTRL0_START, ++ (void *)&s->auto_crtl0, true); ++ kvm_pch_pic_access_regs(fd, PCH_PIC_AUTO_CTRL1_START, ++ (void *)&s->auto_crtl1, true); ++ kvm_pch_pic_access_regs(fd, PCH_PIC_ROUTE_ENTRY_START, ++ (void *)s->route_entry, true); ++ kvm_pch_pic_access_regs(fd, PCH_PIC_HTMSI_VEC_START, ++ (void *)s->htmsi_vector, true); ++ kvm_pch_pic_access_regs(fd, PCH_PIC_INT_IRR_START, ++ (void *)&s->intirr, true); ++ kvm_pch_pic_access_regs(fd, PCH_PIC_INT_ISR_START, ++ (void *)&s->intisr, true); ++ kvm_pch_pic_access_regs(fd, PCH_PIC_POLARITY_START, ++ (void *)&s->int_polarity, true); ++ ++ return 0; ++} ++ ++static void kvm_pch_pic_handler(void *opaque, int irq, int level) ++{ ++ int kvm_irq; ++ ++ if (kvm_enabled()) { ++ kvm_irq = \ ++ (KVM_LOONGARCH_IRQ_TYPE_IOAPIC << KVM_LOONGARCH_IRQ_TYPE_SHIFT) ++ | (0 << KVM_LOONGARCH_IRQ_VCPU_SHIFT) | irq; ++ kvm_set_irq(kvm_state, kvm_irq, !!level); ++ } ++} ++ ++static void kvm_loongarch_pch_pic_realize(DeviceState *dev, Error **errp) ++{ ++ KVMLoongArchPCHPICClass *pch_pic_class = ++ KVM_LOONGARCH_PCH_PIC_GET_CLASS(dev); ++ struct kvm_create_device cd = {0}; ++ uint64_t pch_pic_base = VIRT_PCH_REG_BASE; ++ Error *err = NULL; ++ int ret; ++ ++ pch_pic_class->parent_realize(dev, &err); ++ if (err) { ++ error_propagate(errp, err); ++ return; ++ } ++ ++ if (!pch_pic_class->is_created) { ++ cd.type = KVM_DEV_TYPE_LA_PCH_PIC; ++ ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd); ++ if (ret < 0) { ++ error_setg_errno(errp, errno, ++ "Creating the KVM pch pic device failed"); ++ return; ++ } ++ pch_pic_class->is_created = true; ++ pch_pic_class->dev_fd = cd.fd; ++ fprintf(stdout, "Create LoongArch pch pic irqchip in KVM done!\n"); ++ ++ ret = kvm_device_access(cd.fd, KVM_DEV_LOONGARCH_PCH_PIC_GRP_CTRL, ++ KVM_DEV_LOONGARCH_PCH_PIC_CTRL_INIT, ++ &pch_pic_base, true, NULL); ++ if (ret < 0) { ++ error_report( ++ "KVM EXTIOI: failed to set the base address of EXTIOI"); ++ exit(1); ++ } ++ ++ qdev_init_gpio_in(dev, kvm_pch_pic_handler, VIRT_PCH_PIC_IRQ_NUM); ++ } ++} ++ ++static const VMStateDescription vmstate_kvm_loongarch_pch_pic = { ++ .name = TYPE_LOONGARCH_PCH_PIC, ++ .version_id = 1, ++ .minimum_version_id = 1, ++ .pre_save = kvm_loongarch_pch_pic_pre_save, ++ .post_load = kvm_loongarch_pch_pic_post_load, ++ .fields = (const VMStateField[]) { ++ VMSTATE_UINT64(int_mask, KVMLoongArchPCHPIC), ++ VMSTATE_UINT64(htmsi_en, KVMLoongArchPCHPIC), ++ VMSTATE_UINT64(intedge, KVMLoongArchPCHPIC), ++ VMSTATE_UINT64(intclr, KVMLoongArchPCHPIC), ++ VMSTATE_UINT64(auto_crtl0, KVMLoongArchPCHPIC), ++ VMSTATE_UINT64(auto_crtl1, KVMLoongArchPCHPIC), ++ VMSTATE_UINT8_ARRAY(route_entry, KVMLoongArchPCHPIC, 64), ++ VMSTATE_UINT8_ARRAY(htmsi_vector, KVMLoongArchPCHPIC, 64), ++ VMSTATE_UINT64(last_intirr, KVMLoongArchPCHPIC), ++ VMSTATE_UINT64(intirr, KVMLoongArchPCHPIC), ++ VMSTATE_UINT64(intisr, KVMLoongArchPCHPIC), ++ VMSTATE_UINT64(int_polarity, KVMLoongArchPCHPIC), ++ VMSTATE_END_OF_LIST() ++ } ++}; ++ ++ ++static void kvm_loongarch_pch_pic_class_init(ObjectClass *oc, void *data) ++{ ++ DeviceClass *dc = DEVICE_CLASS(oc); ++ KVMLoongArchPCHPICClass *pch_pic_class = KVM_LOONGARCH_PCH_PIC_CLASS(oc); ++ ++ pch_pic_class->parent_realize = dc->realize; ++ dc->realize = kvm_loongarch_pch_pic_realize; ++ pch_pic_class->is_created = false; ++ dc->vmsd = &vmstate_kvm_loongarch_pch_pic; ++ ++} ++ ++static const TypeInfo kvm_loongarch_pch_pic_info = { ++ .name = TYPE_KVM_LOONGARCH_PCH_PIC, ++ .parent = TYPE_SYS_BUS_DEVICE, ++ .instance_size = sizeof(KVMLoongArchPCHPIC), ++ .class_size = sizeof(KVMLoongArchPCHPICClass), ++ .class_init = kvm_loongarch_pch_pic_class_init, ++}; ++ ++static void kvm_loongarch_pch_pic_register_types(void) ++{ ++ type_register_static(&kvm_loongarch_pch_pic_info); ++} ++ ++type_init(kvm_loongarch_pch_pic_register_types) +diff --git a/hw/intc/meson.build b/hw/intc/meson.build +index a37d7da..49b4501 100644 +--- a/hw/intc/meson.build ++++ b/hw/intc/meson.build +@@ -75,3 +75,4 @@ specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_ + specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_msi.c')) + specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extioi.c')) + specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI_KVM', if_true: files('loongarch_extioi_kvm.c')) ++specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC_KVM', if_true: files('loongarch_pch_pic_kvm.c')) +diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig +index fc86381..9b687b4 100644 +--- a/hw/loongarch/Kconfig ++++ b/hw/loongarch/Kconfig +@@ -13,6 +13,7 @@ config LOONGARCH_VIRT + select LOONGARCH_PCH_MSI + select LOONGARCH_EXTIOI + select LOONGARCH_IPI_KVM if KVM ++ select LOONGARCH_PCH_PIC_KVM if KVM + select LOONGARCH_EXTIOI_KVM if KVM + select LS7A_RTC + select SMBIOS +diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c +index 0b0eb05..2e6d324 100644 +--- a/hw/loongarch/virt.c ++++ b/hw/loongarch/virt.c +@@ -914,42 +914,48 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) + /* Add Extend I/O Interrupt Controller node */ + fdt_add_eiointc_node(lvms, &cpuintc_phandle, &eiointc_phandle); + +- pch_pic = qdev_new(TYPE_LOONGARCH_PCH_PIC); +- num = VIRT_PCH_PIC_IRQ_NUM; +- qdev_prop_set_uint32(pch_pic, "pch_pic_irq_num", num); +- d = SYS_BUS_DEVICE(pch_pic); +- sysbus_realize_and_unref(d, &error_fatal); +- memory_region_add_subregion(get_system_memory(), VIRT_IOAPIC_REG_BASE, +- sysbus_mmio_get_region(d, 0)); +- memory_region_add_subregion(get_system_memory(), ++ if (kvm_enabled() && kvm_irqchip_in_kernel()) { ++ pch_pic = qdev_new(TYPE_KVM_LOONGARCH_PCH_PIC); ++ sysbus_realize_and_unref(SYS_BUS_DEVICE(pch_pic), &error_fatal); ++ } else { ++ pch_pic = qdev_new(TYPE_LOONGARCH_PCH_PIC); ++ num = VIRT_PCH_PIC_IRQ_NUM; ++ qdev_prop_set_uint32(pch_pic, "pch_pic_irq_num", num); ++ d = SYS_BUS_DEVICE(pch_pic); ++ sysbus_realize_and_unref(d, &error_fatal); ++ memory_region_add_subregion(get_system_memory(), VIRT_IOAPIC_REG_BASE, ++ sysbus_mmio_get_region(d, 0)); ++ memory_region_add_subregion(get_system_memory(), + VIRT_IOAPIC_REG_BASE + PCH_PIC_ROUTE_ENTRY_OFFSET, + sysbus_mmio_get_region(d, 1)); +- memory_region_add_subregion(get_system_memory(), +- VIRT_IOAPIC_REG_BASE + PCH_PIC_INT_STATUS_LO, +- sysbus_mmio_get_region(d, 2)); + +- /* Connect pch_pic irqs to extioi */ +- for (i = 0; i < num; i++) { +- qdev_connect_gpio_out(DEVICE(d), i, qdev_get_gpio_in(extioi, i)); ++ memory_region_add_subregion(get_system_memory(), ++ VIRT_IOAPIC_REG_BASE + PCH_PIC_INT_STATUS_LO, ++ sysbus_mmio_get_region(d, 2)); ++ ++ /* Connect pch_pic irqs to extioi */ ++ for (i = 0; i < num; i++) { ++ qdev_connect_gpio_out(DEVICE(d), i, qdev_get_gpio_in(extioi, i)); ++ } ++ ++ pch_msi = qdev_new(TYPE_LOONGARCH_PCH_MSI); ++ start = num; ++ num = EXTIOI_IRQS - start; ++ qdev_prop_set_uint32(pch_msi, "msi_irq_base", start); ++ qdev_prop_set_uint32(pch_msi, "msi_irq_num", num); ++ d = SYS_BUS_DEVICE(pch_msi); ++ sysbus_realize_and_unref(d, &error_fatal); ++ sysbus_mmio_map(d, 0, VIRT_PCH_MSI_ADDR_LOW); ++ for (i = 0; i < num; i++) { ++ /* Connect pch_msi irqs to extioi */ ++ qdev_connect_gpio_out(DEVICE(d), i, ++ qdev_get_gpio_in(extioi, i + start)); ++ } + } + + /* Add PCH PIC node */ + fdt_add_pch_pic_node(lvms, &eiointc_phandle, &pch_pic_phandle); + +- pch_msi = qdev_new(TYPE_LOONGARCH_PCH_MSI); +- start = num; +- num = EXTIOI_IRQS - start; +- qdev_prop_set_uint32(pch_msi, "msi_irq_base", start); +- qdev_prop_set_uint32(pch_msi, "msi_irq_num", num); +- d = SYS_BUS_DEVICE(pch_msi); +- sysbus_realize_and_unref(d, &error_fatal); +- sysbus_mmio_map(d, 0, VIRT_PCH_MSI_ADDR_LOW); +- for (i = 0; i < num; i++) { +- /* Connect pch_msi irqs to extioi */ +- qdev_connect_gpio_out(DEVICE(d), i, +- qdev_get_gpio_in(extioi, i + start)); +- } +- + /* Add PCH MSI node */ + fdt_add_pch_msi_node(lvms, &eiointc_phandle, &pch_msi_phandle); + +diff --git a/include/hw/intc/loongarch_pch_pic.h b/include/hw/intc/loongarch_pch_pic.h +index d5437e8..77f4cd7 100644 +--- a/include/hw/intc/loongarch_pch_pic.h ++++ b/include/hw/intc/loongarch_pch_pic.h +@@ -7,7 +7,8 @@ + + #include "hw/sysbus.h" + +-#define TYPE_LOONGARCH_PCH_PIC "loongarch_pch_pic" ++#define TYPE_LOONGARCH_PCH_PIC "loongarch_pch_pic" ++#define TYPE_KVM_LOONGARCH_PCH_PIC "loongarch_kvm_pch_pic" + #define PCH_PIC_NAME(name) TYPE_LOONGARCH_PCH_PIC#name + OBJECT_DECLARE_SIMPLE_TYPE(LoongArchPCHPIC, LOONGARCH_PCH_PIC) + +@@ -37,6 +38,19 @@ OBJECT_DECLARE_SIMPLE_TYPE(LoongArchPCHPIC, LOONGARCH_PCH_PIC) + #define PCH_PIC_INT_POL_LO 0x3e0 + #define PCH_PIC_INT_POL_HI 0x3e4 + ++#define PCH_PIC_INT_ID_START PCH_PIC_INT_ID_LO ++#define PCH_PIC_MASK_START PCH_PIC_INT_MASK_LO ++#define PCH_PIC_HTMSI_EN_START PCH_PIC_HTMSI_EN_LO ++#define PCH_PIC_EDGE_START PCH_PIC_INT_EDGE_LO ++#define PCH_PIC_CLEAR_START PCH_PIC_INT_CLEAR_LO ++#define PCH_PIC_AUTO_CTRL0_START PCH_PIC_AUTO_CTRL0_LO ++#define PCH_PIC_AUTO_CTRL1_START PCH_PIC_AUTO_CTRL1_LO ++#define PCH_PIC_ROUTE_ENTRY_START PCH_PIC_ROUTE_ENTRY_OFFSET ++#define PCH_PIC_HTMSI_VEC_START PCH_PIC_HTMSI_VEC_OFFSET ++#define PCH_PIC_INT_IRR_START 0x380 ++#define PCH_PIC_INT_ISR_START PCH_PIC_INT_STATUS_LO ++#define PCH_PIC_POLARITY_START PCH_PIC_INT_POL_LO ++ + #define STATUS_LO_START 0 + #define STATUS_HI_START 0x4 + #define POL_LO_START 0x40 +@@ -67,3 +81,38 @@ struct LoongArchPCHPIC { + MemoryRegion iomem8; + unsigned int irq_num; + }; ++ ++struct KVMLoongArchPCHPIC { ++ SysBusDevice parent_obj; ++ uint64_t int_mask; /*0x020 interrupt mask register*/ ++ uint64_t htmsi_en; /*0x040 1=msi*/ ++ uint64_t intedge; /*0x060 edge=1 level =0*/ ++ uint64_t intclr; /*0x080 for clean edge int,set 1 clean,set 0 is noused*/ ++ uint64_t auto_crtl0; /*0x0c0*/ ++ uint64_t auto_crtl1; /*0x0e0*/ ++ uint64_t last_intirr; /* edge detection */ ++ uint64_t intirr; /* 0x380 interrupt request register */ ++ uint64_t intisr; /* 0x3a0 interrupt service register */ ++ /* ++ * 0x3e0 interrupt level polarity selection ++ * register 0 for high level trigger ++ */ ++ uint64_t int_polarity; ++ ++ uint8_t route_entry[64]; /*0x100 - 0x138*/ ++ uint8_t htmsi_vector[64]; /*0x200 - 0x238*/ ++}; ++typedef struct KVMLoongArchPCHPIC KVMLoongArchPCHPIC; ++DECLARE_INSTANCE_CHECKER(KVMLoongArchPCHPIC, KVM_LOONGARCH_PCH_PIC, ++ TYPE_KVM_LOONGARCH_PCH_PIC) ++ ++struct KVMLoongArchPCHPICClass { ++ SysBusDeviceClass parent_class; ++ DeviceRealize parent_realize; ++ ++ bool is_created; ++ int dev_fd; ++}; ++typedef struct KVMLoongArchPCHPICClass KVMLoongArchPCHPICClass; ++DECLARE_CLASS_CHECKERS(KVMLoongArchPCHPICClass, KVM_LOONGARCH_PCH_PIC, ++ TYPE_KVM_LOONGARCH_PCH_PIC) +diff --git a/linux-headers/asm-loongarch/kvm.h b/linux-headers/asm-loongarch/kvm.h +index b4f8c2e..f109ed4 100644 +--- a/linux-headers/asm-loongarch/kvm.h ++++ b/linux-headers/asm-loongarch/kvm.h +@@ -153,4 +153,9 @@ struct kvm_iocsr_entry { + + #define KVM_DEV_LOONGARCH_EXTIOI_GRP_REGS 0x40000003 + ++#define KVM_DEV_LOONGARCH_PCH_PIC_GRP_CTRL 0x40000004 ++#define KVM_DEV_LOONGARCH_PCH_PIC_CTRL_INIT 0 ++ ++#define KVM_DEV_LOONGARCH_PCH_PIC_GRP_REGS 0x40000005 ++ + #endif /* __UAPI_ASM_LOONGARCH_KVM_H */ +diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h +index 788457f..f390989 100644 +--- a/linux-headers/linux/kvm.h ++++ b/linux-headers/linux/kvm.h +@@ -1464,6 +1464,8 @@ enum kvm_device_type { + #define KVM_DEV_TYPE_ARM_PV_TIME KVM_DEV_TYPE_ARM_PV_TIME + KVM_DEV_TYPE_RISCV_AIA, + #define KVM_DEV_TYPE_RISCV_AIA KVM_DEV_TYPE_RISCV_AIA ++ KVM_DEV_TYPE_LA_PCH_PIC = 0x100, ++#define KVM_DEV_TYPE_LA_PCH_PIC KVM_DEV_TYPE_LA_PCH_PIC + KVM_DEV_TYPE_LA_IPI, + #define KVM_DEV_TYPE_LA_IPI KVM_DEV_TYPE_LA_IPI + KVM_DEV_TYPE_LA_EXTIOI, +-- +1.8.3.1 + diff --git a/0267-hw-loongarch-Add-KVM-pch-msi-device-support.patch b/0267-hw-loongarch-Add-KVM-pch-msi-device-support.patch new file mode 100644 index 0000000000000000000000000000000000000000..b5a787929ac070a230a1a5583c578655fa634080 --- /dev/null +++ b/0267-hw-loongarch-Add-KVM-pch-msi-device-support.patch @@ -0,0 +1,173 @@ +From f0f2e7082e05f79525c7e7d59335335f67f8bdef Mon Sep 17 00:00:00 2001 +From: Xianglai Li +Date: Thu, 7 Mar 2024 20:19:03 +0800 +Subject: [PATCH 277/293] hw/loongarch: Add KVM pch msi device support + +Added pch_msi interrupt controller handling +during kernel emulation of irq chip. + +Signed-off-by: Xianglai Li +--- + hw/intc/loongarch_pch_msi.c | 42 +++++++++++++++++++++++++++---------- + hw/loongarch/virt.c | 28 ++++++++++++++----------- + include/hw/intc/loongarch_pch_msi.h | 2 +- + target/loongarch/kvm/kvm.c | 2 +- + 4 files changed, 49 insertions(+), 25 deletions(-) + +diff --git a/hw/intc/loongarch_pch_msi.c b/hw/intc/loongarch_pch_msi.c +index ecf3ed0..bab6f85 100644 +--- a/hw/intc/loongarch_pch_msi.c ++++ b/hw/intc/loongarch_pch_msi.c +@@ -2,7 +2,7 @@ + /* + * QEMU Loongson 7A1000 msi interrupt controller. + * +- * Copyright (C) 2021 Loongson Technology Corporation Limited ++ * Copyright (C) 2024 Loongson Technology Corporation Limited + */ + + #include "qemu/osdep.h" +@@ -14,6 +14,8 @@ + #include "hw/misc/unimp.h" + #include "migration/vmstate.h" + #include "trace.h" ++#include "sysemu/kvm.h" ++#include "hw/loongarch/virt.h" + + static uint64_t loongarch_msi_mem_read(void *opaque, hwaddr addr, unsigned size) + { +@@ -26,14 +28,24 @@ static void loongarch_msi_mem_write(void *opaque, hwaddr addr, + LoongArchPCHMSI *s = (LoongArchPCHMSI *)opaque; + int irq_num; + +- /* +- * vector number is irq number from upper extioi intc +- * need subtract irq base to get msi vector offset +- */ +- irq_num = (val & 0xff) - s->irq_base; +- trace_loongarch_msi_set_irq(irq_num); +- assert(irq_num < s->irq_num); +- qemu_set_irq(s->pch_msi_irq[irq_num], 1); ++ MSIMessage msg = { ++ .address = addr, ++ .data = val, ++ }; ++ ++ if (kvm_enabled() && kvm_irqchip_in_kernel()) { ++ kvm_irqchip_send_msi(kvm_state, msg); ++ } else { ++ /* ++ * vector number is irq number from upper extioi intc ++ * need subtract irq base to get msi vector offset ++ */ ++ irq_num = (val & 0xff) - s->irq_base; ++ trace_loongarch_msi_set_irq(irq_num); ++ assert(irq_num < s->irq_num); ++ ++ qemu_set_irq(s->pch_msi_irq[irq_num], 1); ++ } + } + + static const MemoryRegionOps loongarch_pch_msi_ops = { +@@ -45,8 +57,16 @@ static const MemoryRegionOps loongarch_pch_msi_ops = { + static void pch_msi_irq_handler(void *opaque, int irq, int level) + { + LoongArchPCHMSI *s = LOONGARCH_PCH_MSI(opaque); +- +- qemu_set_irq(s->pch_msi_irq[irq], level); ++ MSIMessage msg = { ++ .address = 0, ++ .data = irq, ++ }; ++ ++ if (kvm_enabled() && kvm_irqchip_in_kernel()) { ++ kvm_irqchip_send_msi(kvm_state, msg); ++ } else { ++ qemu_set_irq(s->pch_msi_irq[irq], level); ++ } + } + + static void loongarch_pch_msi_realize(DeviceState *dev, Error **errp) +diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c +index 2e6d324..c7492e5 100644 +--- a/hw/loongarch/virt.c ++++ b/hw/loongarch/virt.c +@@ -937,29 +937,33 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) + for (i = 0; i < num; i++) { + qdev_connect_gpio_out(DEVICE(d), i, qdev_get_gpio_in(extioi, i)); + } ++ } + +- pch_msi = qdev_new(TYPE_LOONGARCH_PCH_MSI); +- start = num; +- num = EXTIOI_IRQS - start; +- qdev_prop_set_uint32(pch_msi, "msi_irq_base", start); +- qdev_prop_set_uint32(pch_msi, "msi_irq_num", num); +- d = SYS_BUS_DEVICE(pch_msi); +- sysbus_realize_and_unref(d, &error_fatal); +- sysbus_mmio_map(d, 0, VIRT_PCH_MSI_ADDR_LOW); ++ /* Add PCH PIC node */ ++ fdt_add_pch_pic_node(lvms, &eiointc_phandle, &pch_pic_phandle); ++ ++ pch_msi = qdev_new(TYPE_LOONGARCH_PCH_MSI); ++ num = VIRT_PCH_PIC_IRQ_NUM; ++ start = num; ++ num = EXTIOI_IRQS - start; ++ qdev_prop_set_uint32(pch_msi, "msi_irq_base", start); ++ qdev_prop_set_uint32(pch_msi, "msi_irq_num", num); ++ d = SYS_BUS_DEVICE(pch_msi); ++ sysbus_realize_and_unref(d, &error_fatal); ++ sysbus_mmio_map(d, 0, VIRT_PCH_MSI_ADDR_LOW); ++ if (!(kvm_enabled() && kvm_irqchip_in_kernel())) { ++ /* Connect pch_msi irqs to extioi */ + for (i = 0; i < num; i++) { +- /* Connect pch_msi irqs to extioi */ + qdev_connect_gpio_out(DEVICE(d), i, + qdev_get_gpio_in(extioi, i + start)); + } + } + +- /* Add PCH PIC node */ +- fdt_add_pch_pic_node(lvms, &eiointc_phandle, &pch_pic_phandle); +- + /* Add PCH MSI node */ + fdt_add_pch_msi_node(lvms, &eiointc_phandle, &pch_msi_phandle); + + virt_devices_init(pch_pic, lvms, &pch_pic_phandle, &pch_msi_phandle); ++ + } + + static void virt_firmware_init(LoongArchVirtMachineState *lvms) +diff --git a/include/hw/intc/loongarch_pch_msi.h b/include/hw/intc/loongarch_pch_msi.h +index b8586fb..fd4ea97 100644 +--- a/include/hw/intc/loongarch_pch_msi.h ++++ b/include/hw/intc/loongarch_pch_msi.h +@@ -7,7 +7,7 @@ + + #include "hw/sysbus.h" + +-#define TYPE_LOONGARCH_PCH_MSI "loongarch_pch_msi" ++#define TYPE_LOONGARCH_PCH_MSI "loongarch_pch_msi" + OBJECT_DECLARE_SIMPLE_TYPE(LoongArchPCHMSI, LOONGARCH_PCH_MSI) + + /* MSI irq start from 32 to 255 */ +diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c +index e2e77ab..0ab1ee8 100644 +--- a/target/loongarch/kvm/kvm.c ++++ b/target/loongarch/kvm/kvm.c +@@ -962,11 +962,11 @@ int kvm_arch_get_default_type(MachineState *ms) + + int kvm_arch_init(MachineState *ms, KVMState *s) + { +- s->kernel_irqchip_allowed = false; + cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE); + if(!kvm_vm_check_attr(kvm_state, KVM_LOONGARCH_VM_HAVE_IRQCHIP, KVM_LOONGARCH_VM_HAVE_IRQCHIP)) { + s->kernel_irqchip_allowed = false; + } ++ s->kernel_irqchip_allowed = false; + + return 0; + } +-- +1.8.3.1 + diff --git a/0268-target-loongarch-Add-TCG-macro-in-structure-CPUArchS.patch b/0268-target-loongarch-Add-TCG-macro-in-structure-CPUArchS.patch new file mode 100644 index 0000000000000000000000000000000000000000..50e6c25dabe3bc69f19daeed46fd095ad3579b30 --- /dev/null +++ b/0268-target-loongarch-Add-TCG-macro-in-structure-CPUArchS.patch @@ -0,0 +1,216 @@ +From 036f2d681ad8cfa3aad7b3fc54c641ed261a4b02 Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Mon, 6 May 2024 09:19:12 +0800 +Subject: [PATCH 278/293] target/loongarch: Add TCG macro in structure + CPUArchState + +In structure CPUArchState some struct elements are only used in TCG +mode, and it is not used in KVM mode. Macro CONFIG_TCG is added to +make it simpiler in KVM mode, also there is the same modification +in c code when these structure elements are used. + +When VM runs in KVM mode, TLB entries are not used and do not need +migrate. It is only useful when it runs in TCG mode. + +Signed-off-by: Bibo Mao +Reviewed-by: Richard Henderson +Message-Id: <20240506011912.2108842-1-maobibo@loongson.cn> +Signed-off-by: Song Gao +Signed-off-by: Xianglai Li +--- + target/loongarch/cpu.c | 7 +++++-- + target/loongarch/cpu.h | 15 ++++++++++----- + target/loongarch/cpu_helper.c | 9 +++++++++ + target/loongarch/machine.c | 30 +++++++++++++++++++++++++----- + 4 files changed, 49 insertions(+), 12 deletions(-) + +diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c +index 78f4a95..2a562af 100644 +--- a/target/loongarch/cpu.c ++++ b/target/loongarch/cpu.c +@@ -527,7 +527,9 @@ static void loongarch_cpu_reset_hold(Object *obj) + lacc->parent_phases.hold(obj); + } + ++#ifdef CONFIG_TCG + env->fcsr0_mask = FCSR0_M1 | FCSR0_M2 | FCSR0_M3; ++#endif + env->fcsr0 = 0x0; + + int n; +@@ -586,7 +588,9 @@ static void loongarch_cpu_reset_hold(Object *obj) + + #ifndef CONFIG_USER_ONLY + env->pc = 0x1c000000; ++#ifdef CONFIG_TCG + memset(env->tlb, 0, sizeof(env->tlb)); ++#endif + if (kvm_enabled()) { + kvm_arch_reset_vcpu(cs); + } +@@ -776,8 +780,7 @@ void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) + int i; + + qemu_fprintf(f, " PC=%016" PRIx64 " ", env->pc); +- qemu_fprintf(f, " FCSR0 0x%08x fp_status 0x%02x\n", env->fcsr0, +- get_float_exception_flags(&env->fp_status)); ++ qemu_fprintf(f, " FCSR0 0x%08x\n", env->fcsr0); + + /* gpr */ + for (i = 0; i < 32; i++) { +diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h +index 4a3ae6f..9afa831 100644 +--- a/target/loongarch/cpu.h ++++ b/target/loongarch/cpu.h +@@ -275,6 +275,7 @@ union fpr_t { + VReg vreg; + }; + ++#ifdef CONFIG_TCG + struct LoongArchTLB { + uint64_t tlb_misc; + /* Fields corresponding to CSR_TLBELO0/1 */ +@@ -282,6 +283,7 @@ struct LoongArchTLB { + uint64_t tlb_entry1; + }; + typedef struct LoongArchTLB LoongArchTLB; ++#endif + + enum loongarch_features { + LOONGARCH_FEATURE_LBT, /* loongson binary translation extension */ +@@ -304,18 +306,13 @@ typedef struct CPUArchState { + uint64_t pc; + + fpr_t fpr[32]; +- float_status fp_status; + bool cf[8]; + + uint32_t fcsr0; + lbt_t lbt; +- uint32_t fcsr0_mask; + + uint32_t cpucfg[21]; + +- uint64_t lladdr; /* LL virtual address compared against SC */ +- uint64_t llval; +- + /* LoongArch CSRs */ + uint64_t CSR_CRMD; + uint64_t CSR_PRMD; +@@ -375,8 +372,16 @@ typedef struct CPUArchState { + uint64_t guest_addr; + } stealtime; + ++#ifdef CONFIG_TCG ++ float_status fp_status; ++ uint32_t fcsr0_mask; ++ uint64_t lladdr; /* LL virtual address compared against SC */ ++ uint64_t llval; ++#endif + #ifndef CONFIG_USER_ONLY ++#ifdef CONFIG_TCG + LoongArchTLB tlb[LOONGARCH_TLB_MAX]; ++#endif + + AddressSpace *address_space_iocsr; + bool load_elf; +diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c +index f68d63f..39037ee 100644 +--- a/target/loongarch/cpu_helper.c ++++ b/target/loongarch/cpu_helper.c +@@ -11,6 +11,7 @@ + #include "internals.h" + #include "cpu-csr.h" + ++#ifdef CONFIG_TCG + static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + int access_type, int index, int mmu_idx) +@@ -154,6 +155,14 @@ static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, + + return TLBRET_NOMATCH; + } ++#else ++static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, ++ int *prot, target_ulong address, ++ MMUAccessType access_type, int mmu_idx) ++{ ++ return TLBRET_NOMATCH; ++} ++#endif + + static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, + target_ulong dmw) +diff --git a/target/loongarch/machine.c b/target/loongarch/machine.c +index dc76845..818aa71 100644 +--- a/target/loongarch/machine.c ++++ b/target/loongarch/machine.c +@@ -8,6 +8,7 @@ + #include "qemu/osdep.h" + #include "cpu.h" + #include "migration/cpu.h" ++#include "sysemu/tcg.h" + #include "vec.h" + + static const VMStateDescription vmstate_fpu_reg = { +@@ -133,9 +134,15 @@ static const VMStateDescription vmstate_lbt = { + }; + + ++#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) ++static bool tlb_needed(void *opaque) ++{ ++ return tcg_enabled(); ++} ++ + /* TLB state */ +-const VMStateDescription vmstate_tlb = { +- .name = "cpu/tlb", ++static const VMStateDescription vmstate_tlb_entry = { ++ .name = "cpu/tlb_entry", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { +@@ -146,6 +153,19 @@ const VMStateDescription vmstate_tlb = { + } + }; + ++static const VMStateDescription vmstate_tlb = { ++ .name = "cpu/tlb", ++ .version_id = 0, ++ .minimum_version_id = 0, ++ .needed = tlb_needed, ++ .fields = (const VMStateField[]) { ++ VMSTATE_STRUCT_ARRAY(env.tlb, LoongArchCPU, LOONGARCH_TLB_MAX, ++ 0, vmstate_tlb_entry, LoongArchTLB), ++ VMSTATE_END_OF_LIST() ++ } ++}; ++#endif ++ + /* LoongArch CPU state */ + const VMStateDescription vmstate_loongarch_cpu = { + .name = "cpu", +@@ -211,9 +231,6 @@ const VMStateDescription vmstate_loongarch_cpu = { + VMSTATE_UINT64(env.CSR_DBG, LoongArchCPU), + VMSTATE_UINT64(env.CSR_DERA, LoongArchCPU), + VMSTATE_UINT64(env.CSR_DSAVE, LoongArchCPU), +- /* TLB */ +- VMSTATE_STRUCT_ARRAY(env.tlb, LoongArchCPU, LOONGARCH_TLB_MAX, +- 0, vmstate_tlb, LoongArchTLB), + + VMSTATE_UINT64(kvm_state_counter, LoongArchCPU), + /* PV steal time */ +@@ -225,6 +242,9 @@ const VMStateDescription vmstate_loongarch_cpu = { + &vmstate_fpu, + &vmstate_lsx, + &vmstate_lasx, ++#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) ++ &vmstate_tlb, ++#endif + &vmstate_lbt, + NULL + } +-- +1.8.3.1 + diff --git a/0269-target-loongarch-Put-cpucfg-operation-before-CSR-reg.patch b/0269-target-loongarch-Put-cpucfg-operation-before-CSR-reg.patch new file mode 100644 index 0000000000000000000000000000000000000000..688298daa8194c3ff24af3baaeed864655ccdb6a --- /dev/null +++ b/0269-target-loongarch-Put-cpucfg-operation-before-CSR-reg.patch @@ -0,0 +1,75 @@ +From 075a7610239e1a8c46c6f479cbc4871620b5b62c Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Sun, 28 Apr 2024 11:16:51 +0800 +Subject: [PATCH 279/293] target/loongarch: Put cpucfg operation before CSR + register + +On Loongarch, cpucfg is register for cpu feature, some other registers +depend on cpucfg feature such as perf CSR registers. Here put cpucfg +read/write operations before CSR register, so that KVM knows how many +perf CSR registers are valid from pre-set cpucfg feature information. + +Signed-off-by: Bibo Mao +Reviewed-by: Song Gao +Message-Id: <20240428031651.1354587-1-maobibo@loongson.cn> +Signed-off-by: Song Gao +Signed-off-by: Xianglai Li +--- + target/loongarch/kvm/kvm.c | 20 ++++++++++---------- + 1 file changed, 10 insertions(+), 10 deletions(-) + +diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c +index 0ab1ee8..213cf6a 100644 +--- a/target/loongarch/kvm/kvm.c ++++ b/target/loongarch/kvm/kvm.c +@@ -716,6 +716,11 @@ int kvm_arch_get_registers(CPUState *cs) + return ret; + } + ++ ret = kvm_loongarch_get_cpucfg(cs); ++ if (ret) { ++ return ret; ++ } ++ + ret = kvm_loongarch_get_csr(cs); + if (ret) { + return ret; +@@ -737,11 +742,6 @@ int kvm_arch_get_registers(CPUState *cs) + } + + ret = kvm_loongarch_get_mpstate(cs); +- if (ret) { +- return ret; +- } +- +- ret = kvm_loongarch_get_cpucfg(cs); + return ret; + } + +@@ -754,6 +754,11 @@ int kvm_arch_put_registers(CPUState *cs, int level) + return ret; + } + ++ ret = kvm_loongarch_put_cpucfg(cs); ++ if (ret) { ++ return ret; ++ } ++ + ret = kvm_loongarch_put_csr(cs, level); + if (ret) { + return ret; +@@ -781,11 +786,6 @@ int kvm_arch_put_registers(CPUState *cs, int level) + } + + ret = kvm_loongarch_put_mpstate(cs); +- if (ret) { +- return ret; +- } +- +- ret = kvm_loongarch_put_cpucfg(cs); + return ret; + } + +-- +1.8.3.1 + diff --git a/0270-target-loongarch-fixed-a-multi-core-boot-issue.patch b/0270-target-loongarch-fixed-a-multi-core-boot-issue.patch new file mode 100644 index 0000000000000000000000000000000000000000..63acf00b34d21ed2a5c41841365c708c064a3e05 --- /dev/null +++ b/0270-target-loongarch-fixed-a-multi-core-boot-issue.patch @@ -0,0 +1,65 @@ +From 3154802985caaf4241cb59fac3abe63ada5a57ff Mon Sep 17 00:00:00 2001 +From: Xianglai Li +Date: Mon, 28 Oct 2024 20:19:01 +0800 +Subject: [PATCH 280/293] target/loongarch: fixed a multi-core boot issue + +Fixed multiple cpu startup errors and +reboot failure after cpu plug. + +Signed-off-by: Xianglai Li +--- + hw/loongarch/boot.c | 2 +- + hw/loongarch/virt.c | 6 +++++- + include/hw/loongarch/virt.h | 1 + + 3 files changed, 7 insertions(+), 2 deletions(-) + +diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c +index cb66870..fb9496d 100644 +--- a/hw/loongarch/boot.c ++++ b/hw/loongarch/boot.c +@@ -216,7 +216,7 @@ static int64_t load_kernel_info(struct loongarch_boot_info *info) + return kernel_entry; + } + +-static void reset_load_elf(void *opaque) ++void reset_load_elf(void *opaque) + { + LoongArchCPU *cpu = opaque; + CPULoongArchState *env = &cpu->env; +diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c +index c7492e5..556af5c 100644 +--- a/hw/loongarch/virt.c ++++ b/hw/loongarch/virt.c +@@ -875,6 +875,10 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) + lacpu = LOONGARCH_CPU(cpu_state); + env = &(lacpu->env); + env->address_space_iocsr = &lvms->as_iocsr; ++ ++ /* connect ipi irq to cpu irq */ ++ qdev_connect_gpio_out(ipi, cpu, qdev_get_gpio_in(cpudev, IRQ_IPI)); ++ env->ipistate = ipi; + } + + lvms->ipi = ipi; +@@ -1511,7 +1515,7 @@ static void virt_cpu_plug(HotplugHandler *hotplug_dev, + env = &(cpu->env); + env->address_space_iocsr = &lvms->as_iocsr; + +- ++ qemu_register_reset(reset_load_elf, LOONGARCH_CPU(qemu_get_cpu(cs->cpu_index))); + env->ipistate = lvms->ipi; + if (!(kvm_enabled() && kvm_irqchip_in_kernel())) { + /* connect ipi irq to cpu irq, logic cpu index used here */ +diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h +index dce7c50..eb021e2 100644 +--- a/include/hw/loongarch/virt.h ++++ b/include/hw/loongarch/virt.h +@@ -84,4 +84,5 @@ struct LoongArchVirtMachineState { + #define TYPE_LOONGARCH_VIRT_MACHINE MACHINE_TYPE_NAME("virt") + OBJECT_DECLARE_SIMPLE_TYPE(LoongArchVirtMachineState, LOONGARCH_VIRT_MACHINE) + void loongarch_acpi_setup(LoongArchVirtMachineState *lvms); ++void reset_load_elf(void *opaque); + #endif +-- +1.8.3.1 + diff --git a/0271-hw-loongarch-boot-Use-warn_report-when-no-kernel-fil.patch b/0271-hw-loongarch-boot-Use-warn_report-when-no-kernel-fil.patch new file mode 100644 index 0000000000000000000000000000000000000000..5c16aad648528c87415e599407316ba98841eacd --- /dev/null +++ b/0271-hw-loongarch-boot-Use-warn_report-when-no-kernel-fil.patch @@ -0,0 +1,45 @@ +From 21a0b23baabb2f3d7b8bcbf3945bd0a626399739 Mon Sep 17 00:00:00 2001 +From: Xianglai Li +Date: Tue, 29 Oct 2024 15:00:44 +0800 +Subject: [PATCH 281/293] hw/loongarch/boot: Use warn_report when no kernel + filename +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When we run “qemu-system-loongarch64 -qmp stdio -vnc none -S”, +we get an error message “Need kernel filename” and then we can't use qmp cmd to query some information. +So, we just throw a warning and then the cpus starts running from address VIRT_FLASH0_BASE. + +Signed-off-by: Song Gao +Signed-off-by: Xianglai Li +--- + hw/loongarch/boot.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c +index fb9496d..53dcefb 100644 +--- a/hw/loongarch/boot.c ++++ b/hw/loongarch/boot.c +@@ -278,7 +278,7 @@ static void init_boot_rom(struct loongarch_boot_info *info, void *p) + static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info) + { + void *p, *bp; +- int64_t kernel_addr = 0; ++ int64_t kernel_addr = VIRT_FLASH0_BASE; + LoongArchCPU *lacpu; + CPUState *cs; + +@@ -286,8 +286,7 @@ static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info) + kernel_addr = load_kernel_info(info); + } else { + if(!qtest_enabled()) { +- error_report("Need kernel filename\n"); +- exit(1); ++ warn_report("No kernel provided, booting from flash drive."); + } + } + +-- +1.8.3.1 + diff --git a/0272-target-loongarch-fix-Werror-maybe-uninitialized-fals.patch b/0272-target-loongarch-fix-Werror-maybe-uninitialized-fals.patch new file mode 100644 index 0000000000000000000000000000000000000000..9259e535bb466e17b692b10946b1af4a0dc7f0c9 --- /dev/null +++ b/0272-target-loongarch-fix-Werror-maybe-uninitialized-fals.patch @@ -0,0 +1,73 @@ +From 8817f63ff4efca3ab18057cee236e3e27cd19257 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= +Date: Tue, 24 Sep 2024 15:49:47 +0400 +Subject: [PATCH 282/293] target/loongarch: fix -Werror=maybe-uninitialized + false-positive +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +../target/loongarch/gdbstub.c:55:20: error: ‘val’ may be used uninitialized [-Werror=maybe-uninitialized] + 55 | return gdb_get_reg32(mem_buf, val); + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~ +../target/loongarch/gdbstub.c:39:18: note: ‘val’ was declared here + 39 | uint64_t val; + +Signed-off-by: Marc-André Lureau +Reviewed-by: Vladimir Sementsov-Ogievskiy +Signed-off-by: Xianglai Li +--- + target/loongarch/gdbstub.c | 29 +++++++++++++++-------------- + 1 file changed, 15 insertions(+), 14 deletions(-) + +diff --git a/target/loongarch/gdbstub.c b/target/loongarch/gdbstub.c +index 5fc2f19..f8e3324 100644 +--- a/target/loongarch/gdbstub.c ++++ b/target/loongarch/gdbstub.c +@@ -33,28 +33,29 @@ void write_fcc(CPULoongArchState *env, uint64_t val) + + int loongarch_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) + { +- LoongArchCPU *cpu = LOONGARCH_CPU(cs); +- CPULoongArchState *env = &cpu->env; +- uint64_t val; +- +- if (0 <= n && n < 32) { +- val = env->gpr[n]; +- } else if (n == 32) { +- /* orig_a0 */ +- val = 0; +- } else if (n == 33) { +- val = env->pc; +- } else if (n == 34) { +- val = env->CSR_BADV; +- } ++ CPULoongArchState *env = cpu_env(cs); + + if (0 <= n && n <= 34) { ++ uint64_t val; ++ ++ if (n < 32) { ++ val = env->gpr[n]; ++ } else if (n == 32) { ++ /* orig_a0 */ ++ val = 0; ++ } else if (n == 33) { ++ val = env->pc; ++ } else /* if (n == 34) */ { ++ val = env->CSR_BADV; ++ } ++ + if (is_la64(env)) { + return gdb_get_reg64(mem_buf, val); + } else { + return gdb_get_reg32(mem_buf, val); + } + } ++ + return 0; + } + +-- +1.8.3.1 + diff --git a/0273-target-loongarch-Use-explicit-little-endian-LD-ST-AP.patch b/0273-target-loongarch-Use-explicit-little-endian-LD-ST-AP.patch new file mode 100644 index 0000000000000000000000000000000000000000..370862fd64e1ffb562074e34d5c6282cc7281113 --- /dev/null +++ b/0273-target-loongarch-Use-explicit-little-endian-LD-ST-AP.patch @@ -0,0 +1,65 @@ +From 38205f31340519c705349682fadeea2b17f68cbd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= +Date: Fri, 4 Oct 2024 11:59:56 +0200 +Subject: [PATCH 283/293] target/loongarch: Use explicit little-endian LD/ST + API +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The LoongArch architecture uses little endianness. Directly +use the little-endian LD/ST API. + +Mechanical change using: + + $ end=le; \ + for acc in uw w l q tul; do \ + sed -i -e "s/ld${acc}_p(/ld${acc}_${end}_p(/" \ + -e "s/st${acc}_p(/st${acc}_${end}_p(/" \ + $(git grep -wlE '(ld|st)t?u?[wlq]_p' target/loongarch/); \ + done + +Signed-off-by: Philippe Mathieu-Daudé +Reviewed-by: Richard Henderson +Message-Id: <20241004163042.85922-13-philmd@linaro.org> +Signed-off-by: Xianglai Li +--- + target/loongarch/gdbstub.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/target/loongarch/gdbstub.c b/target/loongarch/gdbstub.c +index f8e3324..cc72680 100644 +--- a/target/loongarch/gdbstub.c ++++ b/target/loongarch/gdbstub.c +@@ -68,10 +68,10 @@ int loongarch_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) + int length = 0; + + if (is_la64(env)) { +- tmp = ldq_p(mem_buf); ++ tmp = ldq_le_p(mem_buf); + read_length = 8; + } else { +- tmp = ldl_p(mem_buf); ++ tmp = ldl_le_p(mem_buf); + read_length = 4; + } + +@@ -104,13 +104,13 @@ static int loongarch_gdb_set_fpu(CPULoongArchState *env, + int length = 0; + + if (0 <= n && n < 32) { +- env->fpr[n].vreg.D(0) = ldq_p(mem_buf); ++ env->fpr[n].vreg.D(0) = ldq_le_p(mem_buf); + length = 8; + } else if (32 <= n && n < 40) { + env->cf[n - 32] = ldub_p(mem_buf); + length = 1; + } else if (n == 40) { +- env->fcsr0 = ldl_p(mem_buf); ++ env->fcsr0 = ldl_le_p(mem_buf); + length = 4; + } + return length; +-- +1.8.3.1 + diff --git a/0274-target-loongarch-Remove-avail_64-in-trans_srai_w-and.patch b/0274-target-loongarch-Remove-avail_64-in-trans_srai_w-and.patch new file mode 100644 index 0000000000000000000000000000000000000000..a47ea4c436c675d0ad381f7facdfb5ef9da95fee --- /dev/null +++ b/0274-target-loongarch-Remove-avail_64-in-trans_srai_w-and.patch @@ -0,0 +1,56 @@ +From 3e9ae64333b6b29e0fb862acbfd99c6daa432be1 Mon Sep 17 00:00:00 2001 +From: Feiyang Chen +Date: Fri, 28 Jun 2024 13:33:57 +1000 +Subject: [PATCH 284/293] target/loongarch: Remove avail_64 in trans_srai_w() + and simplify it + +Since srai.w is a valid instruction on la32, remove the avail_64 check +and simplify trans_srai_w(). + +Fixes: c0c0461e3a06 ("target/loongarch: Add avail_64 to check la64-only instructions") +Reviewed-by: Richard Henderson +Signed-off-by: Feiyang Chen +Message-Id: <20240628033357.50027-1-chris.chenfeiyang@gmail.com> +Signed-off-by: Song Gao +Signed-off-by: Xianglai Li +--- + target/loongarch/tcg/insn_trans/trans_shift.c.inc | 15 +++------------ + 1 file changed, 3 insertions(+), 12 deletions(-) + +diff --git a/target/loongarch/tcg/insn_trans/trans_shift.c.inc b/target/loongarch/tcg/insn_trans/trans_shift.c.inc +index 2f4bd6f..3773077 100644 +--- a/target/loongarch/tcg/insn_trans/trans_shift.c.inc ++++ b/target/loongarch/tcg/insn_trans/trans_shift.c.inc +@@ -67,19 +67,9 @@ static void gen_rotr_d(TCGv dest, TCGv src1, TCGv src2) + tcg_gen_rotr_tl(dest, src1, t0); + } + +-static bool trans_srai_w(DisasContext *ctx, arg_srai_w *a) ++static void gen_sari_w(TCGv dest, TCGv src1, target_long imm) + { +- TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); +- TCGv src1 = gpr_src(ctx, a->rj, EXT_ZERO); +- +- if (!avail_64(ctx)) { +- return false; +- } +- +- tcg_gen_sextract_tl(dest, src1, a->imm, 32 - a->imm); +- gen_set_gpr(a->rd, dest, EXT_NONE); +- +- return true; ++ tcg_gen_sextract_tl(dest, src1, imm, 32 - imm); + } + + TRANS(sll_w, ALL, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_sll_w) +@@ -94,6 +84,7 @@ TRANS(slli_w, ALL, gen_rri_c, EXT_NONE, EXT_SIGN, tcg_gen_shli_tl) + TRANS(slli_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_shli_tl) + TRANS(srli_w, ALL, gen_rri_c, EXT_ZERO, EXT_SIGN, tcg_gen_shri_tl) + TRANS(srli_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_shri_tl) ++TRANS(srai_w, ALL, gen_rri_c, EXT_NONE, EXT_NONE, gen_sari_w) + TRANS(srai_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_sari_tl) + TRANS(rotri_w, 64, gen_rri_v, EXT_NONE, EXT_NONE, gen_rotr_w) + TRANS(rotri_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_rotri_tl) +-- +1.8.3.1 + diff --git a/0275-target-loongarch-Set-CSR_PRCFG1-and-CSR_PRCFG2-value.patch b/0275-target-loongarch-Set-CSR_PRCFG1-and-CSR_PRCFG2-value.patch new file mode 100644 index 0000000000000000000000000000000000000000..39498ddfaf68ed665832f9527b5062c3310e63a1 --- /dev/null +++ b/0275-target-loongarch-Set-CSR_PRCFG1-and-CSR_PRCFG2-value.patch @@ -0,0 +1,56 @@ +From f2afd64d1f35e1bfbd2836c45d7d8d9871a55bc5 Mon Sep 17 00:00:00 2001 +From: Song Gao +Date: Fri, 5 Jul 2024 10:18:38 +0800 +Subject: [PATCH 285/293] target/loongarch: Set CSR_PRCFG1 and CSR_PRCFG2 + values + +We set the value of register CSR_PRCFG3, but left out CSR_PRCFG1 +and CSR_PRCFG2. Set CSR_PRCFG1 and CSR_PRCFG2 according to the +default values of the physical machine. + +Signed-off-by: Song Gao +Reviewed-by: Bibo Mao +Message-Id: <20240705021839.1004374-1-gaosong@loongson.cn> +Signed-off-by: Xianglai Li +--- + target/loongarch/cpu.c | 17 ++++++++++++----- + 1 file changed, 12 insertions(+), 5 deletions(-) + +diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c +index 2a562af..b80dde5 100644 +--- a/target/loongarch/cpu.c ++++ b/target/loongarch/cpu.c +@@ -463,6 +463,18 @@ static void loongarch_la464_initfn(Object *obj) + env->cpucfg[20] = data; + + env->CSR_ASID = FIELD_DP64(0, CSR_ASID, ASIDBITS, 0xa); ++ ++ env->CSR_PRCFG1 = FIELD_DP64(env->CSR_PRCFG1, CSR_PRCFG1, SAVE_NUM, 8); ++ env->CSR_PRCFG1 = FIELD_DP64(env->CSR_PRCFG1, CSR_PRCFG1, TIMER_BITS, 0x2f); ++ env->CSR_PRCFG1 = FIELD_DP64(env->CSR_PRCFG1, CSR_PRCFG1, VSMAX, 7); ++ ++ env->CSR_PRCFG2 = 0x3ffff000; ++ ++ env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, TLB_TYPE, 2); ++ env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, MTLB_ENTRY, 63); ++ env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_WAYS, 7); ++ env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_SETS, 8); ++ + loongarch_cpu_post_init(obj); + } + +@@ -574,11 +586,6 @@ static void loongarch_cpu_reset_hold(Object *obj) + env->CSR_TLBRENTRY = 0; + env->CSR_MERRENTRY = 0; + +- env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, TLB_TYPE, 2); +- env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, MTLB_ENTRY, 63); +- env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_WAYS, 7); +- env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_SETS, 8); +- + for (n = 0; n < 4; n++) { + env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV0, 0); + env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV1, 0); +-- +1.8.3.1 + diff --git a/0276-target-loongarch-fix-a-wrong-print-in-cpu-dump.patch b/0276-target-loongarch-fix-a-wrong-print-in-cpu-dump.patch new file mode 100644 index 0000000000000000000000000000000000000000..43d971858864f8ef04cf3fea6a4e5d49b88bbf1a --- /dev/null +++ b/0276-target-loongarch-fix-a-wrong-print-in-cpu-dump.patch @@ -0,0 +1,49 @@ +From 4e2073adb37295cab18c97ed5101fafd8bb28872 Mon Sep 17 00:00:00 2001 +From: Gao Jiazhen +Date: Thu, 12 Sep 2024 11:27:12 +0800 +Subject: [PATCH 286/293] target/loongarch: fix a wrong print in cpu dump + +cherry picked from commit 78f932ea1f7b3b9b0ac628dc2a91281318fe51fa + +description: + loongarch_cpu_dump_state() want to dump all loongarch cpu +state registers, but there is a tiny typographical error when +printing "PRCFG2". + +Cc: qemu-stable@nongnu.org +Signed-off-by: lanyanzhi +Reviewed-by: Richard Henderson +Reviewed-by: Song Gao +Message-Id: <20240604073831.666690-1-lanyanzhi22b@ict.ac.cn> +Signed-off-by: Song Gao +Signed-off-by: Gao Jiazhen +Signed-off-by: Xianglai Li +--- + target/loongarch/cpu.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c +index b80dde5..bfc7df3 100644 +--- a/target/loongarch/cpu.c ++++ b/target/loongarch/cpu.c +@@ -91,7 +91,7 @@ void G_NORETURN do_raise_exception(CPULoongArchState *env, + { + CPUState *cs = env_cpu(env); + +- qemu_log_mask(CPU_LOG_INT, "%s: expection: %d (%s)\n", ++ qemu_log_mask(CPU_LOG_INT, "%s: exception: %d (%s)\n", + __func__, + exception, + loongarch_exception_name(exception)); +@@ -810,7 +810,7 @@ void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) + qemu_fprintf(f, "EENTRY=%016" PRIx64 "\n", env->CSR_EENTRY); + qemu_fprintf(f, "PRCFG1=%016" PRIx64 ", PRCFG2=%016" PRIx64 "," + " PRCFG3=%016" PRIx64 "\n", +- env->CSR_PRCFG1, env->CSR_PRCFG3, env->CSR_PRCFG3); ++ env->CSR_PRCFG1, env->CSR_PRCFG2, env->CSR_PRCFG3); + qemu_fprintf(f, "TLBRENTRY=%016" PRIx64 "\n", env->CSR_TLBRENTRY); + qemu_fprintf(f, "TLBRBADV=%016" PRIx64 "\n", env->CSR_TLBRBADV); + qemu_fprintf(f, "TLBRERA=%016" PRIx64 "\n", env->CSR_TLBRERA); +-- +1.8.3.1 + diff --git a/0277-loongarch-switch-boards-to-default-y.patch b/0277-loongarch-switch-boards-to-default-y.patch new file mode 100644 index 0000000000000000000000000000000000000000..cb5a1dce81df95b306addd10c72890bd1ccfecd0 --- /dev/null +++ b/0277-loongarch-switch-boards-to-default-y.patch @@ -0,0 +1,60 @@ +From 7b1de30924d4569bd64a52fd7b4499d2ab90ffe3 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Thu, 25 Jan 2024 13:36:37 +0100 +Subject: [PATCH 287/293] loongarch: switch boards to "default y" + +Some targets use "default y" for boards to filter out those that require +TCG. For consistency we are switching all other targets to do the same. +Continue with Loongarch. + +No changes to generated config-devices.mak file. + +Signed-off-by: Paolo Bonzini +Signed-off-by: Xianglai Li +--- + .gitlab-ci.d/buildtest.yml | 2 ++ + configs/devices/loongarch64-softmmu/default.mak | 6 +++++- + hw/loongarch/Kconfig | 2 ++ + 3 files changed, 9 insertions(+), 1 deletion(-) + +diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml +index 0a01746..e39e327 100644 +--- a/.gitlab-ci.d/buildtest.yml ++++ b/.gitlab-ci.d/buildtest.yml +@@ -579,6 +579,8 @@ build-tci: + - make check-tcg + + # Check our reduced build configurations ++# requires libfdt: aarch64, arm, i386, loongarch64, x86_64 ++# does not build without boards: i386, loongarch64, x86_64 + build-without-defaults: + extends: .native_build_job_template + needs: +diff --git a/configs/devices/loongarch64-softmmu/default.mak b/configs/devices/loongarch64-softmmu/default.mak +index 928bc11..ffe7058 100644 +--- a/configs/devices/loongarch64-softmmu/default.mak ++++ b/configs/devices/loongarch64-softmmu/default.mak +@@ -1,3 +1,7 @@ + # Default configuration for loongarch64-softmmu + +-CONFIG_LOONGARCH_VIRT=y ++# Uncomment the following lines to disable these optional devices: ++# CONFIG_PCI_DEVICES=n ++ ++# Boards are selected by default, uncomment to keep out of the build. ++# CONFIG_LOONGARCH_VIRT=n +diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig +index 9b687b4..16c854c 100644 +--- a/hw/loongarch/Kconfig ++++ b/hw/loongarch/Kconfig +@@ -1,5 +1,7 @@ + config LOONGARCH_VIRT + bool ++ default y ++ depends on LOONGARCH64 + select PCI + select PCI_EXPRESS_GENERIC_BRIDGE + imply PCI_DEVICES +-- +1.8.3.1 + diff --git a/0278-tests-libqos-Add-loongarch-virt-machine-node.patch b/0278-tests-libqos-Add-loongarch-virt-machine-node.patch new file mode 100644 index 0000000000000000000000000000000000000000..272288f1be6f4dd12b553548715fe3f815eb62ab --- /dev/null +++ b/0278-tests-libqos-Add-loongarch-virt-machine-node.patch @@ -0,0 +1,162 @@ +From 6c733d918decbc5db37c1110ff96cde50d29d118 Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Tue, 28 May 2024 16:20:53 +0800 +Subject: [PATCH 288/293] tests/libqos: Add loongarch virt machine node + +Add loongarch virt machine to the graph. It is a modified copy of +the existing riscv virtmachine in riscv-virt-machine.c + +It contains a generic-pcihost controller, and an extra function +loongarch_config_qpci_bus() to configure GPEX pci host controller +information, such as ecam and pio_base addresses. + +Also hotplug handle checking about TYPE_VIRTIO_IOMMU_PCI device is +added on loongarch virt machine, since virtio_mmu_pci device requires +it. + +Signed-off-by: Bibo Mao +Acked-by: Thomas Huth +Message-Id: <20240528082053.938564-1-maobibo@loongson.cn> +Signed-off-by: Song Gao +Signed-off-by: Xianglai Li +--- + tests/qtest/libqos/loongarch-virt-machine.c | 114 ++++++++++++++++++++++++++++ + tests/qtest/libqos/meson.build | 1 + + 2 files changed, 115 insertions(+) + create mode 100644 tests/qtest/libqos/loongarch-virt-machine.c + +diff --git a/tests/qtest/libqos/loongarch-virt-machine.c b/tests/qtest/libqos/loongarch-virt-machine.c +new file mode 100644 +index 0000000..c12089c +--- /dev/null ++++ b/tests/qtest/libqos/loongarch-virt-machine.c +@@ -0,0 +1,114 @@ ++/* ++ * libqos driver framework ++ * ++ * Copyright (c) 2018 Emanuele Giuseppe Esposito ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License version 2.1 as published by the Free Software Foundation. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see ++ */ ++ ++#include "qemu/osdep.h" ++#include "../libqtest.h" ++#include "qemu/module.h" ++#include "libqos-malloc.h" ++#include "qgraph.h" ++#include "virtio-mmio.h" ++#include "generic-pcihost.h" ++#include "hw/pci/pci_regs.h" ++ ++#define LOONGARCH_PAGE_SIZE 0x1000 ++#define LOONGARCH_VIRT_RAM_ADDR 0x100000 ++#define LOONGARCH_VIRT_RAM_SIZE 0xFF00000 ++ ++#define LOONGARCH_VIRT_PIO_BASE 0x18000000 ++#define LOONGARCH_VIRT_PCIE_PIO_OFFSET 0x4000 ++#define LOONGARCH_VIRT_PCIE_PIO_LIMIT 0x10000 ++#define LOONGARCH_VIRT_PCIE_ECAM_BASE 0x20000000 ++#define LOONGARCH_VIRT_PCIE_MMIO32_BASE 0x40000000 ++#define LOONGARCH_VIRT_PCIE_MMIO32_LIMIT 0x80000000 ++ ++typedef struct QVirtMachine QVirtMachine; ++ ++struct QVirtMachine { ++ QOSGraphObject obj; ++ QGuestAllocator alloc; ++ QVirtioMMIODevice virtio_mmio; ++ QGenericPCIHost bridge; ++}; ++ ++static void virt_destructor(QOSGraphObject *obj) ++{ ++ QVirtMachine *machine = (QVirtMachine *) obj; ++ alloc_destroy(&machine->alloc); ++} ++ ++static void *virt_get_driver(void *object, const char *interface) ++{ ++ QVirtMachine *machine = object; ++ if (!g_strcmp0(interface, "memory")) { ++ return &machine->alloc; ++ } ++ ++ fprintf(stderr, "%s not present in loongarch/virtio\n", interface); ++ g_assert_not_reached(); ++} ++ ++static QOSGraphObject *virt_get_device(void *obj, const char *device) ++{ ++ QVirtMachine *machine = obj; ++ if (!g_strcmp0(device, "generic-pcihost")) { ++ return &machine->bridge.obj; ++ } else if (!g_strcmp0(device, "virtio-mmio")) { ++ return &machine->virtio_mmio.obj; ++ } ++ ++ fprintf(stderr, "%s not present in loongarch/virt\n", device); ++ g_assert_not_reached(); ++} ++ ++static void loongarch_config_qpci_bus(QGenericPCIBus *qpci) ++{ ++ qpci->gpex_pio_base = LOONGARCH_VIRT_PIO_BASE; ++ qpci->bus.pio_alloc_ptr = LOONGARCH_VIRT_PCIE_PIO_OFFSET; ++ qpci->bus.pio_limit = LOONGARCH_VIRT_PCIE_PIO_LIMIT; ++ qpci->bus.mmio_alloc_ptr = LOONGARCH_VIRT_PCIE_MMIO32_BASE; ++ qpci->bus.mmio_limit = LOONGARCH_VIRT_PCIE_MMIO32_LIMIT; ++ qpci->ecam_alloc_ptr = LOONGARCH_VIRT_PCIE_ECAM_BASE; ++} ++ ++static void *qos_create_machine_loongarch_virt(QTestState *qts) ++{ ++ QVirtMachine *machine = g_new0(QVirtMachine, 1); ++ ++ alloc_init(&machine->alloc, 0, ++ LOONGARCH_VIRT_RAM_ADDR, ++ LOONGARCH_VIRT_RAM_ADDR + LOONGARCH_VIRT_RAM_SIZE, ++ LOONGARCH_PAGE_SIZE); ++ ++ qos_create_generic_pcihost(&machine->bridge, qts, &machine->alloc); ++ loongarch_config_qpci_bus(&machine->bridge.pci); ++ ++ machine->obj.get_device = virt_get_device; ++ machine->obj.get_driver = virt_get_driver; ++ machine->obj.destructor = virt_destructor; ++ return machine; ++} ++ ++static void virt_machine_register_nodes(void) ++{ ++ qos_node_create_machine_args("loongarch64/virt", ++ qos_create_machine_loongarch_virt, ++ " -cpu la464"); ++ qos_node_contains("loongarch64/virt", "generic-pcihost", NULL); ++} ++ ++libqos_init(virt_machine_register_nodes); +diff --git a/tests/qtest/libqos/meson.build b/tests/qtest/libqos/meson.build +index 90aae42..482c9b2 100644 +--- a/tests/qtest/libqos/meson.build ++++ b/tests/qtest/libqos/meson.build +@@ -60,6 +60,7 @@ libqos_srcs = files( + 'arm-xilinx-zynq-a9-machine.c', + 'ppc64_pseries-machine.c', + 'x86_64_pc-machine.c', ++ 'loongarch-virt-machine.c', + ) + + if have_virtfs +-- +1.8.3.1 + diff --git a/0279-target-loongarch-kvm-Add-software-breakpoint-support.patch b/0279-target-loongarch-kvm-Add-software-breakpoint-support.patch new file mode 100644 index 0000000000000000000000000000000000000000..9f9a231d5f57189b1bdd71038d7ffb1c24930fd5 --- /dev/null +++ b/0279-target-loongarch-kvm-Add-software-breakpoint-support.patch @@ -0,0 +1,39 @@ +From 718f9f6636aa7957550bddec46d434c91c0199f8 Mon Sep 17 00:00:00 2001 +From: Bibo Mao +Date: Fri, 7 Jun 2024 11:50:16 +0800 +Subject: [PATCH 289/293] target/loongarch/kvm: Add software breakpoint support + +With KVM virtualization, debug exception is injected to guest kernel +rather than host for normal break intruction. Here hypercall +instruction with special code is used for sw breakpoint usage, +and detailed instruction comes from kvm kernel with user API +KVM_REG_LOONGARCH_DEBUG_INST. + +Now only software breakpoint is supported, and it is allowed to +insert/remove software breakpoint. We can debug guest kernel with gdb +method after kernel is loaded, hardware breakpoint will be added in later. + +Signed-off-by: Bibo Mao +Reviewed-by: Song Gao +Tested-by: Song Gao +Message-Id: <20240607035016.2975799-1-maobibo@loongson.cn> +Signed-off-by: Song Gao +Signed-off-by: Xianglai Li +--- + configs/targets/loongarch64-softmmu.mak | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/configs/targets/loongarch64-softmmu.mak b/configs/targets/loongarch64-softmmu.mak +index f23780f..0034c33 100644 +--- a/configs/targets/loongarch64-softmmu.mak ++++ b/configs/targets/loongarch64-softmmu.mak +@@ -1,5 +1,6 @@ + TARGET_ARCH=loongarch64 + TARGET_BASE_ARCH=loongarch ++TARGET_KVM_HAVE_GUEST_DEBUG=y + TARGET_SUPPORTS_MTTCG=y + TARGET_XML_FILES= gdb-xml/loongarch-base32.xml gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu.xml + TARGET_NEED_FDT=y +-- +1.8.3.1 + diff --git a/0280-enable-the-irqchip-simulation-function.patch b/0280-enable-the-irqchip-simulation-function.patch new file mode 100644 index 0000000000000000000000000000000000000000..dca6c800a6773249c615c27f22b1e6b4ce64bebd --- /dev/null +++ b/0280-enable-the-irqchip-simulation-function.patch @@ -0,0 +1,58 @@ +From 2fe48cc942c4d4100679ef11f614fb4803d440c8 Mon Sep 17 00:00:00 2001 +From: Xianglai Li +Date: Mon, 4 Nov 2024 16:42:15 +0800 +Subject: [PATCH 290/293] enable the irqchip simulation function + +Enable the irqchip kernel simulation function, +Fixed a bug when using irqchip emulation. + +Signed-off-by: Xianglai Li +--- + hw/loongarch/virt.c | 11 ++++++++--- + target/loongarch/kvm/kvm.c | 1 - + 2 files changed, 8 insertions(+), 4 deletions(-) + +diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c +index 556af5c..d165c23 100644 +--- a/hw/loongarch/virt.c ++++ b/hw/loongarch/virt.c +@@ -864,6 +864,14 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) + sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 0)); + memory_region_add_subregion(&lvms->system_iocsr, MAIL_SEND_ADDR, + sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 1)); ++ ++ for (cpu = 0; cpu < ms->smp.cpus; cpu++) { ++ cpu_state = qemu_get_cpu(cpu); ++ cpudev = DEVICE(cpu_state); ++ ++ /* connect ipi irq to cpu irq */ ++ qdev_connect_gpio_out(ipi, cpu, qdev_get_gpio_in(cpudev, IRQ_IPI)); ++ } + } + + /* Add cpu interrupt-controller */ +@@ -875,9 +883,6 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) + lacpu = LOONGARCH_CPU(cpu_state); + env = &(lacpu->env); + env->address_space_iocsr = &lvms->as_iocsr; +- +- /* connect ipi irq to cpu irq */ +- qdev_connect_gpio_out(ipi, cpu, qdev_get_gpio_in(cpudev, IRQ_IPI)); + env->ipistate = ipi; + } + +diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c +index 213cf6a..719d6c2 100644 +--- a/target/loongarch/kvm/kvm.c ++++ b/target/loongarch/kvm/kvm.c +@@ -966,7 +966,6 @@ int kvm_arch_init(MachineState *ms, KVMState *s) + if(!kvm_vm_check_attr(kvm_state, KVM_LOONGARCH_VM_HAVE_IRQCHIP, KVM_LOONGARCH_VM_HAVE_IRQCHIP)) { + s->kernel_irqchip_allowed = false; + } +- s->kernel_irqchip_allowed = false; + + return 0; + } +-- +1.8.3.1 + diff --git a/0281-hw-i386-pc-add-mem2-option-for-qemu.patch b/0281-hw-i386-pc-add-mem2-option-for-qemu.patch new file mode 100644 index 0000000000000000000000000000000000000000..8358c061d8323ec420152a030cc4af94d62aabbd --- /dev/null +++ b/0281-hw-i386-pc-add-mem2-option-for-qemu.patch @@ -0,0 +1,311 @@ +From a0bd22be312157e80e2300f0001342625592db3c Mon Sep 17 00:00:00 2001 +From: xiongmengbiao +Date: Wed, 29 May 2024 00:05:44 +0800 +Subject: [PATCH 291/293] hw/i386/pc: add mem2 option for qemu + +The '-mem2' option is used to create a set of hugepages +of memory and map them to a fixed address range of the guest. + +This allows some devices to easily obtain continuous host +physical address ranges for performing DMA operations. + +Signed-off-by: xiongmengbiao +--- + hw/i386/pc.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + include/hw/boards.h | 2 + + qemu-options.hx | 12 +++++ + system/vl.c | 67 ++++++++++++++++++++++++++- + 4 files changed, 209 insertions(+), 1 deletion(-) + +diff --git a/hw/i386/pc.c b/hw/i386/pc.c +index 29b9964..2bf0341 100644 +--- a/hw/i386/pc.c ++++ b/hw/i386/pc.c +@@ -743,6 +743,119 @@ void xen_load_linux(PCMachineState *pcms) + x86ms->fw_cfg = fw_cfg; + } + ++static int try_create_2MB_page(uint32_t page_num) ++{ ++ char nr_hp_num_s[256] = {0}; ++ char free_hp_num_s[256] = {0}; ++ const char *nr_hugepages_dir = "/sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages"; ++ const char *free_hugepages_dir = "/sys/kernel/mm/hugepages/hugepages-2048kB/free_hugepages"; ++ int nr_hp_num = -1, free_hp_num = -1, ret = -1; ++ int nr_fd = qemu_open_old(nr_hugepages_dir, O_RDWR); ++ int free_fd = qemu_open_old(free_hugepages_dir, O_RDONLY); ++ ++ if (nr_fd < 0 || free_fd < 0) { ++ error_report("%s: qemu_open failed: %s\n", __func__, strerror(errno)); ++ goto end; ++ } ++ ++ if (read(nr_fd, nr_hp_num_s, 256) < 0) ++ goto end; ++ if (read(free_fd, free_hp_num_s, 256) < 0) ++ goto end; ++ ++ nr_hp_num = atoi(nr_hp_num_s); ++ free_hp_num = atoi(free_hp_num_s); ++ if (nr_hp_num < 0 || free_hp_num < 0) ++ goto end; ++ ++ if (page_num <= free_hp_num) { ++ ret = 0; ++ goto end; ++ } ++ ++ nr_hp_num += (page_num - free_hp_num); ++ snprintf (nr_hp_num_s, 256, "%d", nr_hp_num); ++ if (write(nr_fd, nr_hp_num_s, strlen(nr_hp_num_s)) < 0) ++ goto end; ++ ++ ret = 0; ++end: ++ if (nr_fd >= 0) ++ qemu_close(nr_fd); ++ if (free_fd >= 0) ++ qemu_close(free_fd); ++ return ret; ++} ++ ++#define HUGEPAGE_NUM_MAX 128 ++#define HUGEPAGE_SIZE (1024*1024*2) ++static void mem2_init(MachineState *ms, MemoryRegion *system_memory) ++{ ++ MemoryRegion *mem2_mr[HUGEPAGE_NUM_MAX] = {NULL}; ++ char mr_name[128] = {0}; ++ void *ram = NULL; ++ int ret = 0, lock_fd = 0; ++ const char *lock_file = "/sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages"; ++ uint32_t page_num = ms->ram2_size / HUGEPAGE_SIZE, i; ++ ++ if (HUGEPAGE_NUM_MAX < page_num) { ++ error_report("\"-mem2 'size='\" needs to Less than %dM\n", ++ (HUGEPAGE_SIZE * HUGEPAGE_NUM_MAX) / (1024 * 1024)); ++ exit(EXIT_FAILURE); ++ } ++ ++ // Apply for hugepages from OS and use them, which needs to be synchronized ++ lock_fd = qemu_open_old(lock_file, O_WRONLY); ++ if (lock_fd < 0) { ++ error_report("%s: open %s failed: %s\n", __func__, lock_file, strerror(errno)); ++ exit(EXIT_FAILURE); ++ } ++ ++ // Non-blocking ++ while (qemu_lock_fd(lock_fd, 0, 0, true)) { ++ if (errno != EACCES && errno != EAGAIN) { ++ error_report("qemu_lock_fd failed: %s\n", strerror(errno)); ++ exit(EXIT_FAILURE); ++ } ++ } ++ ++ /** try to create hugepage. ++ * If there are enough free hugepages, then do nothing. ++ */ ++ ret = try_create_2MB_page(page_num); ++ if (ret) { ++ error_report("%s: Failed to allocate hugepage\n", __func__); ++ goto unlock; ++ } ++ ++ for (i = 0; i < page_num; ++i) { ++ mem2_mr[i] = g_malloc(sizeof(MemoryRegion)); ++ ram = mmap(NULL, HUGEPAGE_SIZE, PROT_READ | PROT_WRITE, ++ MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE | MAP_HUGETLB, -1, 0); ++ if (ram == MAP_FAILED) { ++ error_report("%s: mmap failed: %s", __func__, strerror(errno)); ++ goto unlock; ++ } ++ ++ sprintf(mr_name, "mem2-%d", i); ++ memory_region_init_ram_ptr(mem2_mr[i], NULL, mr_name, HUGEPAGE_SIZE, ram); ++ memory_region_add_subregion(system_memory, ms->ram2_base + (i * HUGEPAGE_SIZE), mem2_mr[i]); ++ } ++ ++ ret = 0; ++unlock: ++ qemu_unlock_fd(lock_fd, 0, 0); ++ qemu_close(lock_fd); ++ ++ if (ret) { ++ for (i = 0; i < page_num; ++i) { ++ if (mem2_mr[i]) ++ g_free(mem2_mr[i]); ++ } ++ exit(EXIT_FAILURE); ++ } ++} ++ + #define PC_ROM_MIN_VGA 0xc0000 + #define PC_ROM_MIN_OPTION 0xc8000 + #define PC_ROM_MAX 0xe0000 +@@ -965,6 +1078,22 @@ void pc_memory_init(PCMachineState *pcms, + E820_RAM); + } + ++ if (machine->ram2_size && machine->ram2_base) { ++ if (0x100000000ULL + x86ms->above_4g_mem_size > machine->ram2_base) { ++ error_report("\"-mem2 'base'\" needs to greater 0x%llx\n", ++ 0x100000000ULL + x86ms->above_4g_mem_size); ++ exit(EXIT_FAILURE); ++ } ++ if (machine->ram2_base & (HUGEPAGE_SIZE - 1) || ++ machine->ram2_size & (HUGEPAGE_SIZE - 1)) { ++ error_report("\"-mem2 'base|size'\" needs to aligned to 0x%x\n", HUGEPAGE_SIZE); ++ exit(EXIT_FAILURE); ++ } ++ ++ mem2_init(machine, system_memory); ++ e820_add_entry(machine->ram2_base, machine->ram2_size, E820_RAM); ++ } ++ + if (pcms->sgx_epc.size != 0) { + e820_add_entry(pcms->sgx_epc.base, pcms->sgx_epc.size, E820_RESERVED); + } +diff --git a/include/hw/boards.h b/include/hw/boards.h +index da85f86..8ac8cad 100644 +--- a/include/hw/boards.h ++++ b/include/hw/boards.h +@@ -389,6 +389,8 @@ struct MachineState { + + ram_addr_t ram_size; + ram_addr_t maxram_size; ++ ram_addr_t ram2_base; ++ ram_addr_t ram2_size; + uint64_t ram_slots; + BootConfiguration boot_config; + char *kernel_filename; +diff --git a/qemu-options.hx b/qemu-options.hx +index c260117..caeca1d 100644 +--- a/qemu-options.hx ++++ b/qemu-options.hx +@@ -5856,6 +5856,18 @@ SRST + (qemu) qom-set /objects/iothread1 poll-max-ns 100000 + ERST + ++DEF("mem2", HAS_ARG, QEMU_OPTION_mem2, ++ "-mem2 base=addr[G],size=n[MG]\n" ++ " Map guest memory using host hugepages\n" ++ " base: starting position of guest physical address\n" ++ " size: the size of mmaped memory\n" ++ "NOTE: Both `base` and `size` need to be aligned according to 2MB\n", ++ QEMU_ARCH_I386) ++SRST ++``-mem2 base=addr[G],size=n[MG]`` ++ Map the host's large page memory at the specified guest address ++ so that some devices can use larger contiguous physical memory. ++ERST + + HXCOMM This is the last statement. Insert new options before this line! + +diff --git a/system/vl.c b/system/vl.c +index e18fa3c..101c2df 100644 +--- a/system/vl.c ++++ b/system/vl.c +@@ -503,6 +503,23 @@ static QemuOptsList qemu_action_opts = { + }, + }; + ++static QemuOptsList qemu_mem2_opts = { ++ .name = "mem2", ++ .merge_lists = true, ++ .head = QTAILQ_HEAD_INITIALIZER(qemu_mem2_opts.head), ++ .desc = { ++ { ++ .name = "base", ++ .type = QEMU_OPT_SIZE, ++ }, ++ { ++ .name = "size", ++ .type = QEMU_OPT_SIZE, ++ }, ++ { /* end of list */ } ++ }, ++}; ++ + const char *qemu_get_vm_name(void) + { + return qemu_name; +@@ -2090,6 +2107,45 @@ static void parse_memory_options(void) + loc_pop(&loc); + } + ++static void parse_mem2_options(void) ++{ ++ uint64_t sz, base; ++ const char *sz_str = NULL, *base_str = NULL; ++ QemuOpts *opts = qemu_find_opts_singleton("mem2"); ++ Location loc; ++ ++ loc_push_none(&loc); ++ qemu_opts_loc_restore(opts); ++ ++ base_str = qemu_opt_get(opts, "base"); ++ sz_str = qemu_opt_get(opts, "size"); ++ ++ if (!base_str && !sz_str) ++ return; ++ ++ if ((!base_str || !*base_str) ++ || (!sz_str || !*sz_str)) { ++ error_report("missing 'base' or 'size' argument for -mem2 option"); ++ exit(EXIT_FAILURE); ++ } ++ ++ base = qemu_opt_get_size(opts, "base", 0); ++ if (!base) { ++ error_report("invalid 'base' value\n"); ++ exit(EXIT_FAILURE); ++ } ++ current_machine->ram2_base = base; ++ ++ sz = qemu_opt_get_size(opts, "size", 0); ++ if (!sz) { ++ error_report("invalid 'size' value\n"); ++ exit(EXIT_FAILURE); ++ } ++ current_machine->ram2_size = sz; ++ ++ loc_pop(&loc); ++} ++ + static void qemu_create_machine(QDict *qdict) + { + MachineClass *machine_class = select_machine(qdict, &error_fatal); +@@ -2776,6 +2832,7 @@ void qemu_init(int argc, char **argv) + qemu_add_opts(&qemu_semihosting_config_opts); + qemu_add_opts(&qemu_fw_cfg_opts); + qemu_add_opts(&qemu_action_opts); ++ qemu_add_opts(&qemu_mem2_opts); + qemu_add_run_with_opts(); + module_call_init(MODULE_INIT_OPTS); + +@@ -3635,7 +3692,13 @@ void qemu_init(int argc, char **argv) + break; + } + #endif /* CONFIG_POSIX */ +- ++ case QEMU_OPTION_mem2: ++ opts = qemu_opts_parse_noisily(qemu_find_opts("mem2"), ++ optarg, false); ++ if (!opts) { ++ exit(EXIT_FAILURE); ++ } ++ break; + default: + error_report("Option not supported in this build"); + exit(1); +@@ -3686,6 +3749,8 @@ void qemu_init(int argc, char **argv) + + qemu_create_machine(machine_opts_dict); + ++ parse_mem2_options(); ++ + suspend_mux_open(); + + qemu_disable_default_devices(); +-- +1.8.3.1 + diff --git a/0282-hw-misc-psp-support-tkm-use-mem2-memory.patch b/0282-hw-misc-psp-support-tkm-use-mem2-memory.patch new file mode 100644 index 0000000000000000000000000000000000000000..4c71c1edcb2baf425ce3652b7e0a05b3f080a5b8 --- /dev/null +++ b/0282-hw-misc-psp-support-tkm-use-mem2-memory.patch @@ -0,0 +1,124 @@ +From f0fa769d19ee604c0cc026f3ccfabd5d932cb681 Mon Sep 17 00:00:00 2001 +From: xiongmengbiao +Date: Wed, 29 May 2024 15:18:55 +0800 +Subject: [PATCH 292/293] hw/misc/psp: support tkm use mem2 memory + +Change-Id: I04c76eb27eb7c097922d61b893a47b18f55bcd66 +Signed-off-by: xiongmengbiao +--- + hw/misc/psp.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 63 insertions(+), 1 deletion(-) + +diff --git a/hw/misc/psp.c b/hw/misc/psp.c +index da0a69e..85e4455 100644 +--- a/hw/misc/psp.c ++++ b/hw/misc/psp.c +@@ -15,6 +15,9 @@ + #include "migration/vmstate.h" + #include "hw/qdev-properties.h" + #include "sysemu/runstate.h" ++#include "exec/memory.h" ++#include "exec/address-spaces.h" ++#include "hw/i386/e820_memory_layout.h" + #include + + #define TYPE_PSP_DEV "psp" +@@ -46,14 +49,24 @@ struct PSPDevState { + enum VPSP_DEV_CTRL_OPCODE { + VPSP_OP_VID_ADD, + VPSP_OP_VID_DEL, ++ VPSP_OP_SET_DEFAULT_VID_PERMISSION, ++ VPSP_OP_GET_DEFAULT_VID_PERMISSION, ++ VPSP_OP_SET_GPA, + }; + + struct psp_dev_ctrl { + unsigned char op; ++ unsigned char resv[3]; + union { + unsigned int vid; ++ // Set or check the permissions for the default VID ++ unsigned int def_vid_perm; ++ struct { ++ uint64_t gpa_start; ++ uint64_t gpa_end; ++ } gpa; + unsigned char reserved[128]; +- } data; ++ } __attribute__ ((packed)) data; + }; + + static void psp_dev_destroy(PSPDevState *state) +@@ -86,10 +99,32 @@ static void psp_dev_shutdown_notify(Notifier *notifier, void *data) + psp_dev_destroy(state); + } + ++static MemoryRegion *find_memory_region_by_name(MemoryRegion *root, const char *name) { ++ MemoryRegion *subregion; ++ MemoryRegion *result; ++ ++ if (strcmp(root->name, name) == 0) ++ return root; ++ ++ QTAILQ_FOREACH(subregion, &root->subregions, subregions_link) { ++ result = find_memory_region_by_name(subregion, name); ++ if (result) { ++ return result; ++ } ++ } ++ ++ return NULL; ++} ++ + static void psp_dev_realize(DeviceState *dev, Error **errp) + { ++ int i; ++ char mr_name[128] = {0}; + struct psp_dev_ctrl ctrl = { 0 }; + PSPDevState *state = PSP_DEV(dev); ++ MemoryRegion *root_mr = get_system_memory(); ++ MemoryRegion *find_mr = NULL; ++ uint64_t ram2_start = 0, ram2_end = 0; + + state->dev_fd = qemu_open_old(PSP_DEV_PATH, O_RDWR); + if (state->dev_fd < 0) { +@@ -104,9 +139,36 @@ static void psp_dev_realize(DeviceState *dev, Error **errp) + goto end; + } + ++ for (i = 0 ;; ++i) { ++ sprintf(mr_name, "mem2-%d", i); ++ find_mr = find_memory_region_by_name(root_mr, mr_name); ++ if (!find_mr) ++ break; ++ ++ if (!ram2_start) ++ ram2_start = find_mr->addr; ++ ram2_end = find_mr->addr + find_mr->size - 1; ++ } ++ ++ if (ram2_start != ram2_end) { ++ ctrl.op = VPSP_OP_SET_GPA; ++ ctrl.data.gpa.gpa_start = ram2_start; ++ ctrl.data.gpa.gpa_end = ram2_end; ++ if (ioctl(state->dev_fd, PSP_IOC_VPSP_OPT, &ctrl) < 0) { ++ error_setg(errp, "psp_dev_realize VPSP_OP_SET_GPA (start 0x%lx, end 0x%lx), return %d", ++ ram2_start, ram2_end, -errno); ++ goto del_vid; ++ } ++ } ++ + state->enabled = true; + state->shutdown_notifier.notify = psp_dev_shutdown_notify; + qemu_register_shutdown_notifier(&state->shutdown_notifier); ++ ++ return; ++del_vid: ++ ctrl.op = VPSP_OP_VID_DEL; ++ ioctl(state->dev_fd, PSP_IOC_VPSP_OPT, &ctrl); + end: + return; + } +-- +1.8.3.1 + diff --git a/0283-hw-misc-psp-Pin-the-hugepage-memory-specified-by-mem.patch b/0283-hw-misc-psp-Pin-the-hugepage-memory-specified-by-mem.patch new file mode 100644 index 0000000000000000000000000000000000000000..85120216f1e4df83c3a956519c55b1d372671cdf --- /dev/null +++ b/0283-hw-misc-psp-Pin-the-hugepage-memory-specified-by-mem.patch @@ -0,0 +1,213 @@ +From 25cde42855442fd9a7f00255c936838ebc5cfb45 Mon Sep 17 00:00:00 2001 +From: niuyongwen +Date: Sun, 29 Sep 2024 09:45:15 +0800 +Subject: [PATCH 293/293] hw/misc/psp: Pin the hugepage memory specified by + mem2 during use for psp + +Change-Id: I3ada4a3f9d12361dc11318c56c1601dfdc4b2ed7 +Signed-off-by: niuyongwen +--- + hw/misc/psp.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++-------- + 1 file changed, 121 insertions(+), 17 deletions(-) + +diff --git a/hw/misc/psp.c b/hw/misc/psp.c +index 85e4455..c2af21d 100644 +--- a/hw/misc/psp.c ++++ b/hw/misc/psp.c +@@ -17,6 +17,7 @@ + #include "sysemu/runstate.h" + #include "exec/memory.h" + #include "exec/address-spaces.h" ++#include "exec/ramblock.h" + #include "hw/i386/e820_memory_layout.h" + #include + +@@ -38,6 +39,8 @@ struct PSPDevState { + * the TKM module uses different key spaces based on different vids. + */ + uint32_t vid; ++ /* pinned hugepage numbers */ ++ int hp_num; + }; + + #define PSP_DEV_PATH "/dev/hygon_psp_config" +@@ -45,6 +48,8 @@ struct PSPDevState { + #define PSP_IOC_MUTEX_ENABLE _IOWR(HYGON_PSP_IOC_TYPE, 1, NULL) + #define PSP_IOC_MUTEX_DISABLE _IOWR(HYGON_PSP_IOC_TYPE, 2, NULL) + #define PSP_IOC_VPSP_OPT _IOWR(HYGON_PSP_IOC_TYPE, 3, NULL) ++#define PSP_IOC_PIN_USER_PAGE _IOWR(HYGON_PSP_IOC_TYPE, 4, NULL) ++#define PSP_IOC_UNPIN_USER_PAGE _IOWR(HYGON_PSP_IOC_TYPE, 5, NULL) + + enum VPSP_DEV_CTRL_OPCODE { + VPSP_OP_VID_ADD, +@@ -69,6 +74,109 @@ struct psp_dev_ctrl { + } __attribute__ ((packed)) data; + }; + ++static MemoryRegion *find_memory_region_by_name(MemoryRegion *root, const char *name) { ++ MemoryRegion *subregion; ++ MemoryRegion *result; ++ ++ if (strcmp(root->name, name) == 0) ++ return root; ++ ++ QTAILQ_FOREACH(subregion, &root->subregions, subregions_link) { ++ result = find_memory_region_by_name(subregion, name); ++ if (result) { ++ return result; ++ } ++ } ++ ++ return NULL; ++} ++ ++static int pin_user_hugepage(int fd, uint64_t vaddr) ++{ ++ int ret; ++ ++ ret = ioctl(fd, PSP_IOC_PIN_USER_PAGE, vaddr); ++ /* 22: Invalid argument, some old kernel doesn't support this ioctl command */ ++ if (ret != 0 && errno == EINVAL) { ++ ret = 0; ++ } ++ return ret; ++} ++ ++static int unpin_user_hugepage(int fd, uint64_t vaddr) ++{ ++ int ret; ++ ++ ret = ioctl(fd, PSP_IOC_UNPIN_USER_PAGE, vaddr); ++ /* 22: Invalid argument, some old kernel doesn't support this ioctl command */ ++ if (ret != 0 && errno == EINVAL) { ++ ret = 0; ++ } ++ return ret; ++} ++ ++static int pin_psp_user_hugepages(struct PSPDevState *state, MemoryRegion *root) ++{ ++ int ret = 0; ++ char mr_name[128] = {0}; ++ int i, pinned_num; ++ MemoryRegion *find_mr = NULL; ++ ++ for (i = 0 ; i < state->hp_num; ++i) { ++ sprintf(mr_name, "mem2-%d", i); ++ find_mr = find_memory_region_by_name(root, mr_name); ++ if (!find_mr) { ++ error_report("fail to find memory region by name %s.", mr_name); ++ ret = -ENOMEM; ++ goto end; ++ } ++ ++ ret = pin_user_hugepage(state->dev_fd, (uint64_t)find_mr->ram_block->host); ++ if (ret) { ++ error_report("fail to pin_user_hugepage, ret: %d.", ret); ++ goto end; ++ } ++ } ++end: ++ if (ret) { ++ pinned_num = i; ++ for (i = 0 ; i < pinned_num; ++i) { ++ sprintf(mr_name, "mem2-%d", i); ++ find_mr = find_memory_region_by_name(root, mr_name); ++ if (!find_mr) { ++ continue; ++ } ++ unpin_user_hugepage(state->dev_fd, (uint64_t)find_mr->ram_block->host); ++ } ++ ++ } ++ return ret; ++} ++ ++static int unpin_psp_user_hugepages(struct PSPDevState *state, MemoryRegion *root) ++{ ++ int ret = 0; ++ char mr_name[128] = {0}; ++ int i; ++ MemoryRegion *find_mr = NULL; ++ ++ for (i = 0 ; i < state->hp_num; ++i) { ++ sprintf(mr_name, "mem2-%d", i); ++ find_mr = find_memory_region_by_name(root, mr_name); ++ if (!find_mr) { ++ continue; ++ } ++ ++ ret = unpin_user_hugepage(state->dev_fd, (uint64_t)find_mr->ram_block->host); ++ if (ret) { ++ error_report("fail to unpin_user_hugepage, ret: %d.", ret); ++ goto end; ++ } ++ } ++end: ++ return ret; ++} ++ + static void psp_dev_destroy(PSPDevState *state) + { + struct psp_dev_ctrl ctrl = { 0 }; +@@ -77,6 +185,11 @@ static void psp_dev_destroy(PSPDevState *state) + ctrl.op = VPSP_OP_VID_DEL; + if (ioctl(state->dev_fd, PSP_IOC_VPSP_OPT, &ctrl) < 0) { + error_report("VPSP_OP_VID_DEL: %d", -errno); ++ } ++ ++ /* Unpin hugepage memory */ ++ if (unpin_psp_user_hugepages(state, get_system_memory())) { ++ error_report("unpin_psp_user_hugepages failed"); + } else { + state->enabled = false; + } +@@ -99,23 +212,6 @@ static void psp_dev_shutdown_notify(Notifier *notifier, void *data) + psp_dev_destroy(state); + } + +-static MemoryRegion *find_memory_region_by_name(MemoryRegion *root, const char *name) { +- MemoryRegion *subregion; +- MemoryRegion *result; +- +- if (strcmp(root->name, name) == 0) +- return root; +- +- QTAILQ_FOREACH(subregion, &root->subregions, subregions_link) { +- result = find_memory_region_by_name(subregion, name); +- if (result) { +- return result; +- } +- } +- +- return NULL; +-} +- + static void psp_dev_realize(DeviceState *dev, Error **errp) + { + int i; +@@ -150,6 +246,8 @@ static void psp_dev_realize(DeviceState *dev, Error **errp) + ram2_end = find_mr->addr + find_mr->size - 1; + } + ++ state->hp_num = i; ++ + if (ram2_start != ram2_end) { + ctrl.op = VPSP_OP_SET_GPA; + ctrl.data.gpa.gpa_start = ram2_start; +@@ -159,6 +257,12 @@ static void psp_dev_realize(DeviceState *dev, Error **errp) + ram2_start, ram2_end, -errno); + goto del_vid; + } ++ ++ /* Pin hugepage memory */ ++ if(pin_psp_user_hugepages(state, root_mr)) { ++ error_setg(errp, "pin_psp_user_hugepages failed."); ++ goto del_vid; ++ } + } + + state->enabled = true; +-- +1.8.3.1 + diff --git a/0284-fix-potential-use-after-free-with-dbus-shared-memory.patch b/0284-fix-potential-use-after-free-with-dbus-shared-memory.patch new file mode 100644 index 0000000000000000000000000000000000000000..b183644223b6f939744b5593d1836c90a34f8d57 --- /dev/null +++ b/0284-fix-potential-use-after-free-with-dbus-shared-memory.patch @@ -0,0 +1,165 @@ +From 448119b4bdf75ec88d5dbbe1238a6dba6b1863aa Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= +Date: Tue, 8 Oct 2024 16:50:13 +0400 +Subject: [PATCH] ui/win32: fix potential use-after-free with dbus shared + memory +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +DisplaySurface may be free before the pixman image is freed, since the +image is refcounted and used by different objects, including pending +dbus messages. + +Furthermore, setting the destroy function in +create_displaysurface_from() isn't appropriate, as it may not be used, +and may be overriden as in ramfb. + +Set the destroy function when the shared handle is set, use the HANDLE +directly for destroy data, using a single common helper +qemu_pixman_win32_image_destroy(). + +Signed-off-by: Marc-André Lureau +Reviewed-by: Akihiko Odaki +Message-ID: <20241008125028.1177932-5-marcandre.lureau@redhat.com> +--- + hw/display/virtio-gpu.c | 14 ++------------ + include/ui/qemu-pixman.h | 2 ++ + ui/console.c | 24 ++---------------------- + ui/qemu-pixman.c | 15 +++++++++++++++ + 4 files changed, 21 insertions(+), 34 deletions(-) + +diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c +index b016d3bac85..d4f946eb763 100644 +--- a/hw/display/virtio-gpu.c ++++ b/hw/display/virtio-gpu.c +@@ -239,16 +239,6 @@ static uint32_t calc_image_hostmem(pixman_format_code_t pformat, + return height * stride; + } + +-#ifdef WIN32 +-static void +-win32_pixman_image_destroy(pixman_image_t *image, void *data) +-{ +- HANDLE handle = data; +- +- qemu_win32_map_free(pixman_image_get_data(image), handle, &error_warn); +-} +-#endif +- + static void virtio_gpu_resource_create_2d(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) + { +@@ -309,7 +299,7 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g, + bits, c2d.height ? res->hostmem / c2d.height : 0); + #ifdef WIN32 + if (res->image) { +- pixman_image_set_destroy_function(res->image, win32_pixman_image_destroy, res->handle); ++ pixman_image_set_destroy_function(res->image, qemu_pixman_win32_image_destroy, res->handle); + } + #endif + } +@@ -1303,7 +1293,7 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, + return -EINVAL; + } + #ifdef WIN32 +- pixman_image_set_destroy_function(res->image, win32_pixman_image_destroy, res->handle); ++ pixman_image_set_destroy_function(res->image, qemu_pixman_win32_image_destroy, res->handle); + #endif + + res->addrs = g_new(uint64_t, res->iov_cnt); +diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h +index ef13a8210cc..e3dd72b9e38 100644 +--- a/include/ui/qemu-pixman.h ++++ b/include/ui/qemu-pixman.h +@@ -97,6 +97,8 @@ void qemu_pixman_glyph_render(pixman_image_t *glyph, + + void qemu_pixman_image_unref(pixman_image_t *image); + ++void qemu_pixman_win32_image_destroy(pixman_image_t *image, void *data); ++ + G_DEFINE_AUTOPTR_CLEANUP_FUNC(pixman_image_t, qemu_pixman_image_unref) + + #endif /* QEMU_PIXMAN_H */ +diff --git a/ui/console.c b/ui/console.c +index 832055675c5..c462b8f2280 100644 +--- a/ui/console.c ++++ b/ui/console.c +@@ -504,24 +504,6 @@ void qemu_displaysurface_win32_set_handle(DisplaySurface *surface, + surface->handle = h; + surface->handle_offset = offset; + } +- +-static void +-win32_pixman_image_destroy(pixman_image_t *image, void *data) +-{ +- DisplaySurface *surface = data; +- +- if (!surface->handle) { +- return; +- } +- +- assert(surface->handle_offset == 0); +- +- qemu_win32_map_free( +- pixman_image_get_data(surface->image), +- surface->handle, +- &error_warn +- ); +-} + #endif + + DisplaySurface *qemu_create_displaysurface(int width, int height) +@@ -547,6 +529,8 @@ DisplaySurface *qemu_create_displaysurface(int width, int height) + + #ifdef WIN32 + qemu_displaysurface_win32_set_handle(surface, handle, 0); ++ pixman_image_set_destroy_function(surface->image, ++ qemu_pixman_win32_image_destroy, handle); + #endif + return surface; + } +@@ -562,10 +546,6 @@ DisplaySurface *qemu_create_displaysurface_from(int width, int height, + width, height, + (void *)data, linesize); + assert(surface->image != NULL); +-#ifdef WIN32 +- pixman_image_set_destroy_function(surface->image, +- win32_pixman_image_destroy, surface); +-#endif + + return surface; + } +diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c +index 5ca55dd1998..de6c88151c2 100644 +--- a/ui/qemu-pixman.c ++++ b/ui/qemu-pixman.c +@@ -4,6 +4,7 @@ + */ + + #include "qemu/osdep.h" ++#include "qapi/error.h" + #include "ui/console.h" + #include "standard-headers/drm/drm_fourcc.h" + #include "trace.h" +@@ -268,3 +269,17 @@ void qemu_pixman_glyph_render(pixman_image_t *glyph, + pixman_image_unref(ibg); + } + #endif /* CONFIG_PIXMAN */ ++ ++#ifdef WIN32 ++void ++qemu_pixman_win32_image_destroy(pixman_image_t *image, void *data) ++{ ++ HANDLE handle = data; ++ ++ qemu_win32_map_free( ++ pixman_image_get_data(image), ++ handle, ++ &error_warn ++ ); ++} ++#endif +-- +Gitee + diff --git a/qemu-6.2.0.tar.xz b/qemu-8.2.0.tar.xz similarity index 82% rename from qemu-6.2.0.tar.xz rename to qemu-8.2.0.tar.xz index da2f1cd0f62dc3048369fa1b4c459d005b104a79..6d13b5dd8d4d58c99a8d664ca6f207c9ae5c59b7 100644 Binary files a/qemu-6.2.0.tar.xz and b/qemu-8.2.0.tar.xz differ diff --git a/qemu.spec b/qemu.spec index d6569c49a077818f35c45d31b4eb198f24d5c818..13f07bff8ca89a55dd4c5cc38b2ff6b318b9a5fc 100644 --- a/qemu.spec +++ b/qemu.spec @@ -1,10 +1,24 @@ +%define anolis_release 25 %bcond_with check +%global all_system_emu_support 0 +%if 0%{?all_system_emu_support} != 1 +%ifarch x86_64 +%global target_list x86_64-linux-user,x86_64-softmmu +%endif +%ifarch aarch64 +%global target_list aarch64-linux-user,aarch64_be-linux-user,aarch64-softmmu +%endif +%ifarch loongarch64 +%global target_list loongarch64-linux-user,loongarch64-softmmu +%endif + +%endif %global libfdt_version 1.6.0 -%global libseccomp_version 2.4.0 +%global libseccomp_version 2.3.0 %global libusbx_version 1.0.23 -%global meson_version 0.58.2 -%global usbredir_version 0.7.1 +%global meson_version 0.61.3 +%global usbredir_version 0.6 %global ipxe_version 20200823-5.git4bd064de %global have_memlock_limits 0 @@ -19,6 +33,9 @@ %ifarch riscv64 %global kvm_package system-riscv %endif +%ifarch loongarch64 +%global kvm_package system-loongarch64 +%endif %global modprobe_kvm_conf %{_sourcedir}/kvm.conf %ifarch x86_64 @@ -37,13 +54,10 @@ # Matches numactl ExcludeArch %global have_numactl 1 -%ifarch %{arm} -%global have_numactl 0 -%endif # Matches spice ExclusiveArch %global have_spice 0 -%ifarch x86_64 aarch64 +%ifarch x86_64 aarch64 loongarch64 %global have_spice 1 %endif @@ -52,7 +66,7 @@ %global have_liburing 1 -%global have_virgl 1 +%global have_virgl 0 %global have_pmem 0 %ifarch x86_64 @@ -71,13 +85,15 @@ %global have_edk2 1 # All modules should be listed here. +%ifarch loongarch64 %define have_block_rbd 1 +%else +%define have_block_rbd 0 +%endif %global have_block_gluster 1 -%define have_block_nfs 1 - -%define have_capstone_devel 0 +%define have_block_nfs 0 %define have_librdma 1 @@ -125,17 +141,17 @@ %define requires_ui_egl_headless Requires: %{name}-ui-egl-headless = %{evr} %define requires_ui_opengl Requires: %{name}-ui-opengl = %{evr} %define requires_device_display_virtio_gpu Requires: %{name}-device-display-virtio-gpu = %{evr} -%define requires_device_display_virtio_gpu_gl Requires: %{name}-device-display-virtio-gpu-gl = %{evr} %define requires_device_display_virtio_gpu_pci Requires: %{name}-device-display-virtio-gpu-pci = %{evr} -%define requires_device_display_virtio_gpu_pci_gl Requires: %{name}-device-display-virtio-gpu-pci-gl = %{evr} %define requires_device_display_virtio_gpu_ccw Requires: %{name}-device-display-virtio-gpu-ccw = %{evr} %define requires_device_display_virtio_vga Requires: %{name}-device-display-virtio-vga = %{evr} %define requires_device_display_virtio_vga_gl Requires: %{name}-device-display-virtio-vga-gl = %{evr} %if %{have_virgl} %define requires_device_display_vhost_user_gpu Requires: %{name}-device-display-vhost-user-gpu = %{evr} +%define obsoletes_device_display_vhost_user_gpu %{nil} %else %define requires_device_display_vhost_user_gpu %{nil} +%define obsoletes_device_display_vhost_user_gpu Obsoletes: %{name}-device-display-vhost-user-gpu < %{evr} %endif %if %{have_jack} @@ -145,6 +161,9 @@ %define requires_audio_jack %{nil} %endif +%define requires_audio_dbus Requires: %{name}-audio-dbus = %{evr} +%define requires_ui_dbus Requires: %{name}-ui-dbus = %{evr} + %if %{have_spice} %define requires_ui_spice_app Requires: %{name}-ui-spice-app = %{evr} %define requires_ui_spice_core Requires: %{name}-ui-spice-core = %{evr} @@ -180,14 +199,16 @@ %{requires_ui_spice_core} \ %{requires_char_baum} \ %{requires_char_spice} \ +%ifnarch aarch64 loongarch64 \ %{requires_device_display_qxl} \ +%endif \ %{requires_device_display_vhost_user_gpu} \ %{requires_device_display_virtio_gpu} \ -%{requires_device_display_virtio_gpu_gl} \ %{requires_device_display_virtio_gpu_pci} \ -%{requires_device_display_virtio_gpu_pci_gl} \ +%ifnarch aarch64 loongarch64 \ %{requires_device_display_virtio_vga} \ %{requires_device_display_virtio_vga_gl} \ +%endif \ %{requires_device_usb_host} \ %{requires_device_usb_redirect} \ %{requires_device_usb_smartcard} \ @@ -196,25 +217,59 @@ %global obsoletes_some_modules \ %{obsoletes_block_gluster} \ %{obsoletes_block_rbd} \ -%{obsoletes_block_rbd} \ +%{obsoletes_device_display_vhost_user_gpu} \ +%{obsoletes_block_nfs} \ Obsoletes: %{name}-system-lm32 <= %{epoch}:%{version}-%{release} \ Obsoletes: %{name}-system-lm32-core <= %{epoch}:%{version}-%{release} \ Obsoletes: %{name}-system-moxie <= %{epoch}:%{version}-%{release} \ Obsoletes: %{name}-system-moxie-core <= %{epoch}:%{version}-%{release} \ Obsoletes: %{name}-system-unicore32 <= %{epoch}:%{version}-%{release} \ -Obsoletes: %{name}-system-unicore32-core <= %{epoch}:%{version}-%{release} +Obsoletes: %{name}-system-unicore32-core <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-alpha <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-avr <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-cris <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-m68k <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-microblaze <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-mips <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-nios2 <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-or1k <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-ppc <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-rx <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-s390x <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-sh4 <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-sparc <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-tricore <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-xtensa <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-alpha-core <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-avr-core <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-cris-core <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-m68k-core <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-microblaze-core <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-mips-core <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-nios2-core <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-or1k-core <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-ppc-core <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-rx-core <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-s390x-core <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-sh4-core <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-sparc-core <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-tricore-core <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-xtensa-core <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-hppa <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-system-hppa-core <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-ui-sdl <= %{epoch}:%{version}-%{release} \ +Obsoletes: %{name}-audio-sdl <= %{epoch}:%{version}-%{release} -%define anolis_release 2 Summary: QEMU is a FAST! processor emulator Name: qemu -Version: 6.2.0 +Version: 8.2.0 Release: %{anolis_release}%{?dist} Epoch: 2 License: GPLv2 and BSD and MIT and CC-BY URL: http://www.qemu.org/ -Source0: http://wiki.qemu-project.org/download/%{name}-%{version}.tar.xz +Source0: https://download.qemu.org/%{name}-%{version}.tar.xz Source10: qemu-guest-agent.service Source11: 99-qemu-guest-agent.rules @@ -227,11 +282,297 @@ Source27: kvm.conf Source31: kvm-x86.conf Source36: README.tests -Patch0001: 0001-sgx-stub-fix.patch - -Patch0002: 0001-virtiofsd-Drop-membership-of-all-supplementary-groups.patch +#qemu patch +Patch0001: 0001-block-Fix-crash-when-loading-snapshot-on-inactive-no.patch +Patch0002: 0002-vl-Improve-error-message-for-conflicting-incoming-an.patch +Patch0003: 0003-iotests-Basic-tests-for-internal-snapshots.patch +Patch0004: 0004-target-riscv-kvm-do-not-use-non-portable-strerrornam.patch +Patch0005: 0005-include-ui-rect.h-fix-qemu_rect_init-mis-assignment.patch +Patch0006: 0006-configure-use-a-native-non-cross-compiler-for-linux-.patch +Patch0007: 0007-target-i386-the-sgx_epc_get_section-stub-is-reachabl.patch +Patch0008: 0008-hw-net-can-sja1000-fix-bug-for-single-acceptance-fil.patch +Patch0009: 0009-target-riscv-Fix-mcycle-minstret-increment-behavior.patch +Patch0010: 0010-chardev-char.c-fix-abstract-device-type-error-messag.patch +Patch0011: 0011-audio-audio.c-remove-trailing-newline-in-error_setg.patch +Patch0012: 0012-hw-net-cadence_gem-Fix-MDIO_OP_xxx-values.patch +Patch0013: 0013-edu-fix-DMA-range-upper-bound-check.patch +Patch0014: 0014-vfio-container-Replace-basename-with-g_path_get_base.patch +Patch0015: 0015-hw-vfio-fix-iteration-over-global-VFIODevice-list.patch +Patch0016: 0016-hw-intc-arm_gicv3_cpuif-handle-LPIs-in-in-the-list-r.patch +Patch0017: 0017-tcg-ppc-Use-new-registers-for-LQ-destination.patch +Patch0018: 0018-util-fix-build-with-musl-libc-on-ppc64le.patch +Patch0019: 0019-tests-acpi-allow-tests-data-acpi-virt-SSDT.memhp-cha.patch +Patch0020: 0020-edk2-update-build-config-set-PcdUninstallMemAttrProt.patch +Patch0021: 0021-tests-acpi-disallow-tests-data-acpi-virt-SSDT.memhp-.patch +Patch0022: 0022-tests-qtest-virtio-ccw-Fix-device-presence-checking.patch +Patch0023: 0023-target-s390x-Fix-LAE-setting-a-wrong-access-register.patch +Patch0024: 0024-.gitlab-ci.d-buildtest.yml-Work-around-htags-bug-whe.patch +Patch0025: 0025-readthodocs-fully-specify-a-build-environment.patch +Patch0026: 0026-hw-hppa-machine-Allow-up-to-3840-MB-total-memory.patch +Patch0027: 0027-hw-hppa-machine-Disable-default-devices-with-nodefau.patch +Patch0028: 0028-hw-pci-host-astro-Add-missing-astro-elroy-registers-.patch +Patch0029: 0029-hw-hppa-Move-software-power-button-address-back-into.patch +Patch0030: 0030-target-hppa-Avoid-accessing-gr0-when-raising-excepti.patch +Patch0031: 0031-target-hppa-Export-function-hppa_set_ior_and_isr.patch +Patch0032: 0032-target-hppa-Fix-IOR-and-ISR-on-unaligned-access-trap.patch +Patch0033: 0033-target-hppa-Fix-IOR-and-ISR-on-error-in-probe.patch +Patch0034: 0034-load_elf-fix-iterator-s-type-for-elf-file-processing.patch +Patch0035: 0035-target-i386-Do-not-re-compute-new-pc-with-CF_PCREL.patch +Patch0036: 0036-target-i386-fix-incorrect-EIP-in-PC-relative-transla.patch +Patch0037: 0037-target-i386-pcrel-store-low-bits-of-physical-address.patch +Patch0038: 0038-backends-cryptodev-Do-not-ignore-throttle-backends-E.patch +Patch0039: 0039-hw-pflash-refactor-pflash_data_write.patch +Patch0040: 0040-hw-pflash-use-ldn_-be-le-_p-and-stn_-be-le-_p.patch +Patch0041: 0041-hw-pflash-implement-update-buffer-for-block-writes.patch +Patch0042: 0042-migration-rdma-define-htonll-ntohll-only-if-not-pred.patch +Patch0043: 0043-hw-scsi-esp-pci-use-correct-address-register-for-PCI.patch +Patch0044: 0044-hw-scsi-esp-pci-generate-PCI-interrupt-from-separate.patch +Patch0045: 0045-hw-scsi-esp-pci-synchronise-setting-of-DMA_STAT_DONE.patch +Patch0046: 0046-hw-scsi-esp-pci-set-DMA_STAT_BCMBLT-when-BLAST-comma.patch +Patch0047: 0047-s390x-pci-avoid-double-enable-disable-of-aif.patch +Patch0048: 0048-s390x-pci-refresh-fh-before-disabling-aif.patch +Patch0049: 0049-s390x-pci-drive-ISM-reset-from-subsystem-reset.patch +Patch0050: 0050-acpi-tests-avocado-bits-wait-for-200-seconds-for-SHU.patch +Patch0051: 0051-accel-tcg-Revert-mapping-of-PCREL-translation-block-.patch +Patch0052: 0052-tcg-s390x-Fix-encoding-of-VRIc-VRSa-VRSc-insns.patch +Patch0053: 0053-coroutine-ucontext-Save-fake-stack-for-pooled-corout.patch +Patch0054: 0054-block-io-clear-BDRV_BLOCK_RECURSE-flag-after-recursi.patch +Patch0055: 0055-linux-user-Fixed-cpu-restore-with-pc-0-on-SIGBUS.patch +Patch0056: 0056-tcg-arm-Fix-SIGILL-in-tcg_out_qemu_st_direct.patch +Patch0057: 0057-virtio-net-correctly-copy-vnet-header-when-flushing-.patch +Patch0058: 0058-block-blklogwrites-Fix-a-bug-when-logging-write-zero.patch +Patch0059: 0059-iotests-add-filter_qmp_generated_node_ids.patch +Patch0060: 0060-iotests-port-141-to-Python-for-reliable-QMP-testing.patch +Patch0061: 0061-monitor-only-run-coroutine-commands-in-qemu_aio_cont.patch +Patch0062: 0062-qtest-bump-aspeed_smc-test-timeout-to-6-minutes.patch +Patch0063: 0063-target-xtensa-fix-OOB-TLB-entry-access.patch +Patch0064: 0064-target-arm-Fix-A64-scalar-SQSHRN-and-SQRSHRN.patch +Patch0065: 0065-target-arm-Fix-incorrect-aa64_tidcp1-feature-check.patch +Patch0066: 0066-Update-version-for-8.2.1-release.patch +Patch0067: 0067-migration-Plug-memory-leak-on-HMP-migrate-error-path.patch +Patch0068: 0068-migration-Fix-use-after-free-of-migration-state-obje.patch +Patch0069: 0069-vfio-pci-Clear-MSI-X-IRQ-index-always.patch +Patch0070: 0070-Make-uri-optional-for-migrate-QAPI.patch +Patch0071: 0071-qemu-docs-Update-options-for-graphical-frontends.patch +Patch0072: 0072-block-blkio-Make-s-mem_region_alignment-be-64-bits.patch +Patch0073: 0073-target-arm-fix-exception-syndrome-for-AArch32-bkpt-i.patch +Patch0074: 0074-system-vl.c-Fix-handling-of-serial-none-serial-somet.patch +Patch0075: 0075-qemu-options.hx-Improve-serial-option-documentation.patch +Patch0076: 0076-target-arm-Reinstate-vfp-property-on-AArch32-CPUs.patch +Patch0077: 0077-pci-host-designware-Limit-value-range-of-iATU-viewpo.patch +Patch0078: 0078-tcg-loongarch64-Set-vector-registers-call-clobbered.patch +Patch0079: 0079-hw-scsi-lsi53c895a-add-missing-decrement-of-reentran.patch +Patch0080: 0080-iotests-fix-leak-of-tmpdir-in-dry-run-mode.patch +Patch0081: 0081-iotests-give-tempdir-an-identifying-name.patch +Patch0082: 0082-virtio-scsi-Attach-event-vq-notifier-with-no_poll.patch +Patch0083: 0083-virtio-Re-enable-notifications-after-drain.patch +Patch0084: 0084-virtio-blk-avoid-using-ioeventfd-state-in-irqfd-cond.patch +Patch0085: 0085-migration-Fix-logic-of-channels-and-transport-compat.patch +Patch0086: 0086-hw-riscv-virt-acpi-build.c-fix-leak-in-build_rhct.patch +Patch0087: 0087-tests-docker-Add-sqlite3-module-to-openSUSE-Leap-con.patch +Patch0088: 0088-configure-run-plugin-TCG-tests-again.patch +Patch0089: 0089-hw-smbios-Fix-OEM-strings-table-option-validation.patch +Patch0090: 0090-hw-smbios-Fix-port-connector-option-validation.patch +Patch0091: 0091-hw-net-tulip-add-chip-status-register-values.patch +Patch0092: 0092-tcg-Increase-width-of-temp_subindex.patch +Patch0093: 0093-tcg-arm-Fix-goto_tb-for-large-translation-blocks.patch +Patch0094: 0094-vhost-user.rst-Fix-vring-address-description.patch +Patch0095: 0095-cxl-cdat-Handle-cdat-table-build-errors.patch +Patch0096: 0096-cxl-cdat-Fix-header-sum-value-in-CDAT-checksum.patch +Patch0097: 0097-hw-cxl-device-read-from-register-values-in-mdev_reg_.patch +Patch0098: 0098-hw-cxl-Pass-CXLComponentState-to-cache_mem_ops.patch +Patch0099: 0099-virtio-gpu-Correct-virgl_renderer_resource_get_info-.patch +Patch0100: 0100-virtio_iommu-Clear-IOMMUPciBus-pointer-cache-when-sy.patch +Patch0101: 0101-smmu-Clear-SMMUPciBus-pointer-cache-when-system-rese.patch +Patch0102: 0102-tests-acpi-Allow-update-of-DSDT.cxl.patch +Patch0103: 0103-hw-i386-Fix-_STA-return-value-for-ACPI0017.patch +Patch0104: 0104-linux-user-aarch64-Choose-SYNC-as-the-preferred-MTE-.patch +Patch0105: 0105-target-arm-Fix-nregs-computation-in-do_-ld-st-_zpa.patch +Patch0106: 0106-target-arm-Adjust-and-validate-mtedesc-sizem1.patch +Patch0107: 0107-target-arm-Split-out-make_svemte_desc.patch +Patch0108: 0108-target-arm-Handle-mte-in-do_ldrq-do_ldro.patch +Patch0109: 0109-target-arm-Fix-SVE-SME-gross-MTE-suppression-checks.patch +Patch0110: 0110-target-arm-Don-t-get-MDCR_EL2-in-pmu_counter_enabled.patch +Patch0111: 0111-iotests-Make-144-deterministic-again.patch +Patch0112: 0112-.gitlab-ci-windows.yml-Don-t-install-libusb-or-spice.patch +Patch0113: 0113-i386-cpu-Clear-FEAT_XSAVE_XSS_LO-HI-leafs-when-CPUID.patch +Patch0114: 0114-i386-cpu-Mask-with-XCR0-XSS-mask-for-FEAT_XSAVE_XCR0.patch +Patch0115: 0115-i386-cpuid-Decrease-cpuid_i-when-skipping-CPUID-leaf.patch +Patch0116: 0116-i386-cpuid-Move-leaf-7-to-correct-group.patch +Patch0117: 0117-target-i386-Generate-an-illegal-opcode-exception-on-.patch +Patch0118: 0118-ui-reject-extended-clipboard-message-if-not-activate.patch +Patch0119: 0119-ui-clipboard-mark-type-as-not-available-when-there-i.patch +Patch0120: 0120-ui-clipboard-add-asserts-for-update-and-request.patch +Patch0121: 0121-ui-console-Fix-console-resize-with-placeholder-surfa.patch +Patch0122: 0122-audio-Depend-on-dbus_display1_dep.patch +Patch0123: 0123-meson-Explicitly-specify-dbus-display1.h-dependency.patch +Patch0124: 0124-tests-qtest-Depend-on-dbus_display1_dep.patch +Patch0125: 0125-hw-hppa-Kconfig-Fix-building-with-configure-without-.patch +Patch0126: 0126-docs-system-Update-description-for-input-grab-key.patch +Patch0127: 0127-system-vl-Update-description-for-input-grab-key.patch +Patch0128: 0128-.gitlab-ci.d-windows.yml-Drop-msys2-32bit-job.patch +Patch0129: 0129-target-ppc-Fix-lxv-stxv-MSR-facility-check.patch +Patch0130: 0130-target-ppc-Fix-crash-on-machine-check-caused-by-ifet.patch +Patch0131: 0131-hw-nvme-fix-invalid-endian-conversion.patch +Patch0132: 0132-pl031-Update-last-RTCLR-value-on-write-in-case-it-s-.patch +Patch0133: 0133-target-i386-mask-high-bits-of-CR3-in-32-bit-mode.patch +Patch0134: 0134-target-i386-check-validity-of-VMCB-addresses.patch +Patch0135: 0135-target-i386-Fix-physical-address-truncation.patch +Patch0136: 0136-target-i386-remove-unnecessary-wrong-application-of-.patch +Patch0137: 0137-target-i386-leave-the-A20-bit-set-in-the-final-NPT-w.patch +Patch0138: 0138-tests-vm-update-openbsd-image-to-7.4.patch +Patch0139: 0139-tests-vm-avoid-re-building-the-VM-images-all-the-tim.patch +Patch0140: 0140-gitlab-force-allow-use-of-pip-in-Cirrus-jobs.patch +Patch0141: 0141-hw-intc-Kconfig-Fix-GIC-settings-when-using-without-.patch +Patch0142: 0142-hw-usb-bus.c-PCAP-adding-0xA-in-Windows-version.patch +Patch0143: 0143-tests-unit-test-util-sockets-Remove-temporary-file-a.patch +Patch0144: 0144-chardev-char-socket-Fix-TLS-io-channels-sending-too-.patch +Patch0145: 0145-Update-version-for-8.2.2-release.patch +Patch0146: 0146-target-i386-Add-new-CPU-model-SierraForest.patch +Patch0147: 0147-target-i386-Export-RFDS-bit-to-guests.patch +Patch0148: 0148-hw-loongarch-virt-Align-high-memory-base-address-wit.patch +Patch0149: 0149-target-loongarch-Add-timer-information-dump-support.patch +Patch0150: 0150-target-loongarch-meson-move-gdbstub.c-to-loongarch.s.patch +Patch0151: 0151-target-loongarch-move-translate-modules-to-tcg.patch +Patch0152: 0152-linux-headers-Update-to-Linux-v6.7-rc5.patch +Patch0153: 0153-linux-headers-Synchronize-linux-headers-from-linux-v.patch +Patch0154: 0154-target-loongarch-Define-some-kvm_arch-interfaces.patch +Patch0155: 0155-target-loongarch-Supplement-vcpu-env-initial-when-vc.patch +Patch0156: 0156-target-loongarch-Implement-kvm-get-set-registers.patch +Patch0157: 0157-target-loongarch-Implement-kvm_arch_init-function.patch +Patch0158: 0158-target-loongarch-Implement-kvm_arch_init_vcpu.patch +Patch0159: 0159-target-loongarch-Implement-kvm_arch_handle_exit.patch +Patch0160: 0160-target-loongarch-Restrict-TCG-specific-code.patch +Patch0161: 0161-target-loongarch-Implement-set-vcpu-intr-for-kvm.patch +Patch0162: 0162-target-loongarch-Add-loongarch-kvm-into-meson-build.patch +Patch0163: 0163-hw-intc-loongarch_ipi-Use-MemTxAttrs-interface-for-i.patch +Patch0164: 0164-hw-loongarch-virt-Set-iocsr-address-space-per-board-.patch +Patch0165: 0165-hw-intc-loongarch_extioi-Add-dynamic-cpu-number-supp.patch +Patch0166: 0166-hw-intc-loongarch_extioi-Add-vmstate-post_load-suppo.patch +Patch0167: 0167-configure-Add-linux-header-compile-support-for-Loong.patch +Patch0168: 0168-target-loongarch-Set-cpuid-CSR-register-only-once-wi.patch +Patch0169: 0169-target-loongarch-kvm-Enable-LSX-LASX-extension.patch +Patch0170: 0170-target-loongarch-Fix-qtest-test-hmp-error-when-KVM-o.patch +Patch0171: 0171-loongarch-Change-the-UEFI-loading-mode-to-loongarch.patch +Patch0172: 0172-target-loongarch-Fix-tlb-huge-page-loading-issue.patch +Patch0173: 0173-target-loongarch-Fix-qemu-loongarch64-hang-when-exec.patch +Patch0174: 0174-target-loongarch-kvm-Add-software-breakpoint-support.patch +Patch0175: 0175-hw-intc-loongarch_extioi-Add-virt-extension-support.patch +Patch0176: 0176-target-loongarch-kvm-sync-kernel-header-files.patch +Patch0177: 0177-hw-intc-loongarch_extioi-Add-virt-extension-support-.patch +Patch0178: 0178-target-loongarch-kvm-Implement-LoongArch-PMU-extensi.patch +Patch0179: 0179-target-loongarch-Fix-qemu-system-loongarch64-assert-.patch +Patch0180: 0180-target-loongarch-kvm-Fix-VM-recovery-from-disk-failu.patch +Patch0181: 0181-target-loongarch-kvm-fpu-save-the-vreg-registers-hig.patch +Patch0182: 0182-target-i386-add-support-for-LAM-in-CPUID-enumeration.patch +Patch0183: 0183-target-i386-add-control-bits-support-for-LAM.patch +Patch0184: 0184-target-i386-Introduce-Icelake-Server-v7-to-enable-TS.patch +Patch0185: 0185-target-i386-Introduce-SapphireRapids-v3-to-add-missi.patch +Patch0186: 0186-target-i386-add-guest-phys-bits-cpu-property.patch +Patch0187: 0187-kvm-add-support-for-guest-physical-bits.patch +Patch0188: 0188-support-vpsp.patch +Patch0189: 0189-doc-update-AMD-SEV-to-include-Live-migration-flow.patch +Patch0190: 0190-migration.json-add-AMD-SEV-specific-migration-parame.patch +Patch0191: 0191-confidential-guest-support-introduce-ConfidentialGue.patch +Patch0192: 0192-target-i386-sev-provide-callback-to-setup-outgoing-c.patch +Patch0193: 0193-target-i386-sev-do-not-create-launch-context-for-an-.patch +Patch0194: 0194-target-i386-sev-add-support-to-encrypt-the-outgoing-.patch +Patch0195: 0195-target-i386-sev-add-support-to-load-incoming-encrypt.patch +Patch0196: 0196-kvm-Add-support-for-SEV-shared-regions-list-and-KVM_.patch +Patch0197: 0197-migration-add-support-to-migrate-shared-regions-list.patch +Patch0198: 0198-migration-ram-add-support-to-send-encrypted-pages.patch +Patch0199: 0199-migration-ram-Force-encrypted-status-for-flash0-flas.patch +Patch0200: 0200-kvm-Add-support-for-userspace-MSR-filtering-and-hand.patch +Patch0201: 0201-target-i386-sev-Return-0-if-sev_send_get_packet_len-.patch +Patch0202: 0202-migration-ram-Force-encrypted-status-for-VGA-vram.patch +Patch0203: 0203-target-i386-sev-Clear-shared_regions_list-when-reboo.patch +Patch0204: 0204-migration-ram-Fix-calculation-of-gfn-correpond-to-a-.patch +Patch0205: 0205-target-i386-Introduce-header-file-csv.h.patch +Patch0206: 0206-target-i386-csv-Read-cert-chain-from-file-when-prepa.patch +Patch0207: 0207-target-i386-csv-add-support-to-queue-the-outgoing-pa.patch +Patch0208: 0208-target-i386-csv-add-support-to-encrypt-the-outgoing-.patch +Patch0209: 0209-target-i386-csv-add-support-to-queue-the-incoming-pa.patch +Patch0210: 0210-target-i386-csv-add-support-to-load-incoming-encrypt.patch +Patch0211: 0211-migration-ram-Accelerate-the-transmission-of-CSV-gue.patch +Patch0212: 0212-migration-ram-Accelerate-the-loading-of-CSV-guest-s-.patch +Patch0213: 0213-target-i386-csv-Add-support-for-migrate-VMSA-for-CSV.patch +Patch0214: 0214-target-i386-get-set-migrate-GHCB-state.patch +Patch0215: 0215-target-i386-kvm-Fix-the-resettable-info-when-emulate.patch +Patch0216: 0216-kvm-Add-support-for-CSV2-reboot.patch +Patch0217: 0217-target-i386-csv-Add-CSV3-context.patch +Patch0218: 0218-target-i386-csv-Add-command-to-initialize-CSV3-conte.patch +Patch0219: 0219-target-i386-csv-Add-command-to-load-data-to-CSV3-gue.patch +Patch0220: 0220-target-i386-csv-Add-command-to-load-vmcb-to-CSV3-gue.patch +Patch0221: 0221-target-i386-cpu-Populate-CPUID-0x8000_001F-when-CSV3.patch +Patch0222: 0222-target-i386-csv-Do-not-register-unregister-guest-sec.patch +Patch0223: 0223-target-i386-csv-Load-initial-image-to-private-memory.patch +Patch0224: 0224-vga-Force-full-update-for-CSV3-guest.patch +Patch0225: 0225-vfio-Only-map-shared-region-for-CSV3-virtual-machine.patch +Patch0226: 0226-linux-headers-update-kernel-headers-to-include-CSV3-.patch +Patch0227: 0227-target-i386-csv-Add-support-to-migrate-the-outgoing-.patch +Patch0228: 0228-target-i386-csv-Add-support-to-migrate-the-incoming-.patch +Patch0229: 0229-target-i386-csv-Add-support-to-migrate-the-outgoing-.patch +Patch0230: 0230-target-i386-csv-Add-support-to-migrate-the-incoming-.patch +Patch0231: 0231-target-i386-sev-Fix-incompatibility-between-SEV-and-.patch +Patch0232: 0232-target-i386-sev-Add-support-for-reuse-ASID-for-diffe.patch +Patch0233: 0233-target-i386-Add-Hygon-Dhyana-v3-CPU-model.patch +Patch0234: 0234-target-i386-Add-new-Hygon-Dharma-CPU-model.patch +Patch0235: 0235-vfio-Add-vfio-based-mediated-hct-support.patch +Patch0236: 0236-hw-net-virtio-net-Update-event-idx-if-guest-has-made.patch +Patch0237: 0237-target-i386-csv-Release-CSV3-shared-pages-after-unma.patch +Patch0238: 0238-virtio-net-Fix-network-stall-at-the-host-side-waitin.patch +Patch0239: 0239-devel-8.2-Hygon-HCT-Fix-for-vfio-based-mediated-hct.patch +Patch0240: 0240-update-to-qemu-9.1-with-directory-hw-loongarch.patch +Patch0241: 0241-target-loongarch-Support-QMP-dump-guest-memory.patch +Patch0242: 0242-target-loongarch-Add-compatible-support-about-VM-reb.patch +Patch0243: 0243-target-loongarch-kvm-Add-vCPU-reset-function.patch +Patch0244: 0244-acpi-ged-Add-macro-for-acpi-sleep-control-register.patch +Patch0245: 0245-hw-loongarch-virt-Add-FDT-table-support-with-acpi-ge.patch +Patch0246: 0246-target-loongarch-Avoid-bits-shift-exceeding-width-of.patch +Patch0247: 0247-target-loongarch-Add-loongson-binary-translation-fea.patch +Patch0248: 0248-target-loongarch-Implement-lbt-registers-save-restor.patch +Patch0249: 0249-target-loongarch-kvm-Implement-LoongArch-PMU-extensi.patch +Patch0250: 0250-linux-headers-loongarch-Add-kvm_para.h-and-unistd_64.patch +Patch0251: 0251-target-loongarch-Add-steal-time-support-on-migration.patch +Patch0252: 0252-sync-kernel-headers.patch +Patch0253: 0253-accel-kvm-Extract-common-KVM-vCPU-creation-parking-c.patch +Patch0254: 0254-hw-acpi-Move-CPU-ctrl-dev-MMIO-region-len-macro-to-c.patch +Patch0255: 0255-hw-acpi-Update-ACPI-GED-framework-to-support-vCPU-Ho.patch +Patch0256: 0256-hw-acpi-Update-GED-_EVT-method-AML-with-CPU-scan.patch +Patch0257: 0257-hw-acpi-Update-CPUs-AML-with-cpu-ctrl-dev-change.patch +Patch0258: 0258-physmem-Add-helper-function-to-destroy-CPU-AddressSp.patch +Patch0259: 0259-gdbstub-Add-helper-function-to-unregister-GDB-regist.patch +Patch0260: 0260-accel-kvm-kvm-all-Fixes-the-missing-break-in-vCPU-un.patch +Patch0261: 0261-hw-loongarch-virt-Add-CPU-topology-support.patch +Patch0262: 0262-Add-basic-CPU-plug-support.patch +Patch0263: 0263-hw-loongarch-virt-Update-the-ACPI-table-for-hotplug-.patch +Patch0264: 0264-hw-loongarch-Add-KVM-IPI-device-support.patch +Patch0265: 0265-hw-loongarch-Add-KVM-extioi-device-support.patch +Patch0266: 0266-hw-loongarch-Add-KVM-pch-pic-device-support.patch +Patch0267: 0267-hw-loongarch-Add-KVM-pch-msi-device-support.patch +Patch0268: 0268-target-loongarch-Add-TCG-macro-in-structure-CPUArchS.patch +Patch0269: 0269-target-loongarch-Put-cpucfg-operation-before-CSR-reg.patch +Patch0270: 0270-target-loongarch-fixed-a-multi-core-boot-issue.patch +Patch0271: 0271-hw-loongarch-boot-Use-warn_report-when-no-kernel-fil.patch +Patch0272: 0272-target-loongarch-fix-Werror-maybe-uninitialized-fals.patch +Patch0273: 0273-target-loongarch-Use-explicit-little-endian-LD-ST-AP.patch +Patch0274: 0274-target-loongarch-Remove-avail_64-in-trans_srai_w-and.patch +Patch0275: 0275-target-loongarch-Set-CSR_PRCFG1-and-CSR_PRCFG2-value.patch +Patch0276: 0276-target-loongarch-fix-a-wrong-print-in-cpu-dump.patch +Patch0277: 0277-loongarch-switch-boards-to-default-y.patch +Patch0278: 0278-tests-libqos-Add-loongarch-virt-machine-node.patch +Patch0279: 0279-target-loongarch-kvm-Add-software-breakpoint-support.patch +Patch0280: 0280-enable-the-irqchip-simulation-function.patch +Patch0281: 0281-hw-i386-pc-add-mem2-option-for-qemu.patch +Patch0282: 0282-hw-misc-psp-support-tkm-use-mem2-memory.patch +Patch0283: 0283-hw-misc-psp-Pin-the-hugepage-memory-specified-by-mem.patch +Patch0284: 0284-fix-potential-use-after-free-with-dbus-shared-memory.patch + +ExclusiveArch: x86_64 aarch64 loongarch64 BuildRequires: meson >= %{meson_version} +BuildRequires: bison +BuildRequires: flex BuildRequires: zlib-devel BuildRequires: glib2-devel BuildRequires: gnutls-devel @@ -339,12 +680,8 @@ BuildRequires: libcacard-devel # virgl 3d support BuildRequires: virglrenderer-devel %endif -%if %{have_capstone_devel} # preferred disassembler for TCG BuildRequires: capstone-devel -%endif -# parallels disk images require libxml2 -BuildRequires: libxml2-devel # qemu-ga BuildRequires: libudev-devel # qauth infrastructure @@ -371,31 +708,26 @@ BuildRequires: fuse3-devel BuildRequires: glibc-static pcre-static glib2-static zlib-static %endif -Requires: %{name}-user = %{epoch}:%{version}-%{release} -Requires: %{name}-system-aarch64 = %{epoch}:%{version}-%{release} -Requires: %{name}-system-alpha = %{epoch}:%{version}-%{release} -Requires: %{name}-system-arm = %{epoch}:%{version}-%{release} -Requires: %{name}-system-avr = %{epoch}:%{version}-%{release} -Requires: %{name}-system-cris = %{epoch}:%{version}-%{release} -Requires: %{name}-system-m68k = %{epoch}:%{version}-%{release} -Requires: %{name}-system-microblaze = %{epoch}:%{version}-%{release} -Requires: %{name}-system-mips = %{epoch}:%{version}-%{release} -Requires: %{name}-system-nios2 = %{epoch}:%{version}-%{release} -Requires: %{name}-system-or1k = %{epoch}:%{version}-%{release} -Requires: %{name}-system-ppc = %{epoch}:%{version}-%{release} -Requires: %{name}-system-riscv = %{epoch}:%{version}-%{release} -Requires: %{name}-system-rx = %{epoch}:%{version}-%{release} -Requires: %{name}-system-s390x = %{epoch}:%{version}-%{release} -Requires: %{name}-system-sh4 = %{epoch}:%{version}-%{release} -Requires: %{name}-system-sparc = %{epoch}:%{version}-%{release} -Requires: %{name}-system-tricore = %{epoch}:%{version}-%{release} -Requires: %{name}-system-x86 = %{epoch}:%{version}-%{release} -Requires: %{name}-system-xtensa = %{epoch}:%{version}-%{release} -Requires: %{name}-img = %{epoch}:%{version}-%{release} -Requires: %{name}-tools = %{epoch}:%{version}-%{release} -Requires: qemu-pr-helper = %{epoch}:%{version}-%{release} -Requires: vhostuser-backend(fs) +Requires: %{name}-user = %{EVR} +%ifarch aarch64 +Requires: %{name}-system-aarch64 = %{EVR} +%endif +%ifarch loongarch64 +Requires: %{name}-system-loongarch64 = %{EVR} +%endif +%ifarch riscv +Requires: %{name}-system-riscv = %{EVR} +%endif +%ifarch x86_64 +Requires: %{name}-system-x86 = %{EVR} +%endif +Requires: %{name}-img = %{EVR} +Requires: %{name}-tools = %{EVR} +Requires: qemu-pr-helper = %{EVR} +# Begin with Anolis 23, qemu-kvm was changed to a meta package rather than +# a package containing actual files. So conflicting with older distro. +Conflicts: (qemu-kvm < %{EVR} and anolis-release < 23) %description %{name} is an open source virtualizer that provides hardware @@ -413,6 +745,8 @@ Requires(preun): systemd-units Requires(postun): systemd-units %{obsoletes_some_modules} Requires: ipxe-roms-qemu >= %{ipxe_version} +Obsoletes: qemu-kvm-common < %{EVR} + %description common %{name} is an open source virtualizer that provides hardware emulation for the KVM hypervisor. @@ -422,6 +756,8 @@ This package provides documentation and auxiliary programs used with %{name}. %package docs Summary: %{name} documentation +Obsoletes: qemu-kvm-docs < %{EVR} + %description docs %{name}-docs provides documentation files regarding %{name}. @@ -449,6 +785,7 @@ This package does not need to be installed on the host OS. %package tools Summary: %{name} support tools +Obsoletes: qemu-kvm-tools < %{EVR} %description tools %{name}-tools provides various tools related to %{name} usage. @@ -459,19 +796,10 @@ Summary: qemu-pr-helper utility for %{name} This package provides the qemu-pr-helper utility that is required for certain SCSI features. - -%package -n qemu-virtiofsd -Summary: QEMU virtio-fs shared file system daemon -Provides: vhostuser-backend(fs) -%description -n qemu-virtiofsd -This package provides virtiofsd daemon. This program is a vhost-user backend -that implements the virtio-fs device that is used for sharing a host directory -tree with a guest. - - %package tests Summary: tests for the %{name} package -Requires: %{name} = %{epoch}:%{version}-%{release} +Requires: %{name} = %{EVR} +Obsoletes: qemu-kvm-tests < %{EVR} %define testsdir %{_libdir}/%{name}/tests-src @@ -485,7 +813,8 @@ tests, or qemu-iotests. %package block-curl Summary: QEMU CURL block driver -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-block-curl < %{EVR} %description block-curl This package provides the additional CURL block driver for QEMU. @@ -495,7 +824,8 @@ http, https, ftp and other transports provided by the CURL library. %package block-iscsi Summary: QEMU iSCSI block driver -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-block-iscsi < %{EVR} %description block-iscsi This package provides the additional iSCSI block driver for QEMU. @@ -505,7 +835,8 @@ Install this package if you want to access iSCSI volumes. %if %{have_block_rbd} %package block-rbd Summary: QEMU Ceph/RBD block driver -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-block-rbd < %{EVR} %description block-rbd This package provides the additional Ceph/RBD block driver for QEMU. @@ -516,7 +847,8 @@ using the rbd protocol. %package block-ssh Summary: QEMU SSH block driver -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-block-ssh < %{EVR} %description block-ssh This package provides the additional SSH block driver for QEMU. @@ -527,10 +859,11 @@ the Secure Shell (SSH) protocol. %if %{have_opengl} %package ui-opengl Summary: QEMU opengl support -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} Requires: mesa-libGL Requires: mesa-libEGL Requires: mesa-dri-drivers +Obsoletes: qemu-kvm-ui-opengl < %{EVR} %description ui-opengl This package provides opengl support. %endif @@ -538,7 +871,8 @@ This package provides opengl support. %package block-dmg Summary: QEMU block driver for DMG disk images -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-block-dmg < %{EVR} %description block-dmg This package provides the additional DMG block driver for QEMU. @@ -548,7 +882,8 @@ Install this package if you want to open '.dmg' files. %if %{have_block_gluster} %package block-gluster Summary: QEMU Gluster block driver -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-block-gluster < %{EVR} %description block-gluster This package provides the additional Gluster block driver for QEMU. @@ -559,7 +894,8 @@ Install this package if you want to access remote Gluster storage. %if %{have_block_nfs} %package block-nfs Summary: QEMU NFS block driver -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-block-nfs < %{EVR} %description block-nfs This package provides the additional NFS block driver for QEMU. @@ -570,26 +906,37 @@ Install this package if you want to access remote NFS storage. %package audio-alsa Summary: QEMU ALSA audio driver -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-audio-alsa < %{EVR} %description audio-alsa This package provides the additional ALSA audio driver for QEMU. +%package audio-dbus +Summary: QEMU D-Bus audio driver +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-audio-dbus < %{EVR} +%description audio-dbus +This package provides the additional D-Bus audio driver for QEMU. + %package audio-oss Summary: QEMU OSS audio driver -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-audio-oss < %{EVR} %description audio-oss This package provides the additional OSS audio driver for QEMU. %package audio-pa Summary: QEMU PulseAudio audio driver -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-audio-pa < %{EVR} %description audio-pa This package provides the additional PulseAudi audio driver for QEMU. %if %{have_jack} %package audio-jack Summary: QEMU Jack audio driver -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-audio-jack < %{EVR} %description audio-jack This package provides the additional Jack audio driver for QEMU. %endif @@ -597,96 +944,127 @@ This package provides the additional Jack audio driver for QEMU. %package ui-curses Summary: QEMU curses UI driver -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-ui-curses < %{EVR} %description ui-curses This package provides the additional curses UI for QEMU. + +%package ui-dbus +Summary: QEMU D-Bus UI driver +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-ui-dbus < %{EVR} +%description ui-dbus +This package provides the additional D-Bus UI for QEMU. + + %package ui-gtk Summary: QEMU GTK UI driver -Requires: %{name}-common = %{epoch}:%{version}-%{release} -Requires: %{name}-ui-opengl = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Requires: %{name}-ui-opengl = %{EVR} +Obsoletes: qemu-kvm-ui-gtk < %{EVR} %description ui-gtk This package provides the additional GTK UI for QEMU. %package ui-egl-headless Summary: QEMU EGL headless driver -Requires: %{name}-common = %{epoch}:%{version}-%{release} -Requires: %{name}-ui-opengl = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Requires: %{name}-ui-opengl = %{EVR} +Obsoletes: qemu-kvm-ui-egl-headless < %{EVR} %description ui-egl-headless This package provides the additional egl-headless UI for QEMU. %package char-baum Summary: QEMU Baum chardev driver -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-char-baum < %{EVR} %description char-baum This package provides the Baum chardev driver for QEMU. %package device-display-virtio-gpu Summary: QEMU virtio-gpu display device -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-device-display-virtio-gpu < %{EVR} %description device-display-virtio-gpu This package provides the virtio-gpu display device for QEMU. +%if %{have_virgl} %package device-display-virtio-gpu-gl Summary: QEMU virtio-gpu-gl display device -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-device-display-virtio-gpu-gl < %{EVR} %description device-display-virtio-gpu-gl This package provides the virtio-gpu-gl display device for QEMU. +%endif %package device-display-virtio-gpu-pci Summary: QEMU virtio-gpu-pci display device -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-device-display-virtio-gpu-pci < %{EVR} %description device-display-virtio-gpu-pci This package provides the virtio-gpu-pci display device for QEMU. +%if %{have_virgl} %package device-display-virtio-gpu-pci-gl Summary: QEMU virtio-gpu-pci-gl display device -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-device-display-virtio-gpu-pci-gl < %{EVR} %description device-display-virtio-gpu-pci-gl This package provides the virtio-gpu-pci-gl display device for QEMU. +%endif %package device-display-virtio-gpu-ccw Summary: QEMU virtio-gpu-ccw display device -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-device-display-virtio-gpu-ccw < %{EVR} %description device-display-virtio-gpu-ccw This package provides the virtio-gpu-ccw display device for QEMU. +# virtio-vga only support legacy firmware.Aarch64 cannot support. +%ifnarch aarch64 loongarch64 %package device-display-virtio-vga Summary: QEMU virtio-vga display device -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-device-display-virtio-vga < %{EVR} %description device-display-virtio-vga This package provides the virtio-vga display device for QEMU. %package device-display-virtio-vga-gl Summary: QEMU virtio-vga-gl display device -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-device-display-virtio-vga-gl < %{EVR} %description device-display-virtio-vga-gl This package provides the virtio-vga-gl display device for QEMU. +%endif %package device-usb-host Summary: QEMU usb host device -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-device-usb-host < %{EVR} %description device-usb-host This package provides the USB pass through driver for QEMU. %package device-usb-redirect Summary: QEMU usbredir device -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-device-usb-redirect < %{EVR} %description device-usb-redirect This package provides the usbredir device for QEMU. %package device-usb-smartcard Summary: QEMU USB smartcard device -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-device-usb-smartcard < %{EVR} %description device-usb-smartcard This package provides the USB smartcard device for QEMU. %if %{have_virgl} %package device-display-vhost-user-gpu Summary: QEMU QXL display device -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-device-display-vhost-user-gpu < %{EVR} %description device-display-vhost-user-gpu This package provides the vhost-user-gpu display device for QEMU. %endif @@ -694,37 +1072,45 @@ This package provides the vhost-user-gpu display device for QEMU. %if %{have_spice} %package ui-spice-core Summary: QEMU spice-core UI driver -Requires: %{name}-common = %{epoch}:%{version}-%{release} -Requires: %{name}-ui-opengl = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Requires: %{name}-ui-opengl = %{EVR} +Obsoletes: qemu-kvm-ui-spice-core < %{EVR} %description ui-spice-core This package provides the additional spice-core UI for QEMU. %package ui-spice-app Summary: QEMU spice-app UI driver -Requires: %{name}-common = %{epoch}:%{version}-%{release} -Requires: %{name}-ui-spice-core = %{epoch}:%{version}-%{release} -Requires: %{name}-char-spice = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Requires: %{name}-ui-spice-core = %{EVR} +Requires: %{name}-char-spice = %{EVR} +Obsoletes: qemu-kvm-ui-spice-app < %{EVR} %description ui-spice-app This package provides the additional spice-app UI for QEMU. +# qxl is a vga compatible device which only legacy firmware need +%ifnarch aarch64 loongarch64 %package device-display-qxl Summary: QEMU QXL display device -Requires: %{name}-common = %{epoch}:%{version}-%{release} -Requires: %{name}-ui-spice-core = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Requires: %{name}-ui-spice-core = %{EVR} +Obsoletes: qemu-kvm-device-display-qxl < %{EVR} %description device-display-qxl This package provides the QXL display device for QEMU. +%endif %package char-spice Summary: QEMU spice chardev driver -Requires: %{name}-common = %{epoch}:%{version}-%{release} -Requires: %{name}-ui-spice-core = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Requires: %{name}-ui-spice-core = %{EVR} +Obsoletes: qemu-kvm-char-spice < %{EVR} %description char-spice This package provides the spice chardev driver for QEMU. %package audio-spice Summary: QEMU spice audio driver -Requires: %{name}-common = %{epoch}:%{version}-%{release} -Requires: %{name}-ui-spice-core = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Requires: %{name}-ui-spice-core = %{EVR} +Obsoletes: qemu-kvm-audio-spice < %{EVR} %description audio-spice This package provides the spice audio driver for QEMU. %endif @@ -733,7 +1119,7 @@ This package provides the spice audio driver for QEMU. %if %{have_kvm} %package kvm Summary: QEMU metapackage for KVM support -Requires: qemu-%{kvm_package} = %{epoch}:%{version}-%{release} +Requires: qemu-%{kvm_package} = %{EVR} %description kvm This is a meta-package that provides a qemu-system- package for native architectures where kvm can be enabled. For example, in an x86 system, this @@ -742,7 +1128,7 @@ will install qemu-system-x86 %package kvm-core Summary: QEMU metapackage for KVM support -Requires: qemu-%{kvm_package}-core = %{epoch}:%{version}-%{release} +Requires: qemu-%{kvm_package}-core = %{EVR} %description kvm-core This is a meta-package that provides a qemu-system--core package for native architectures where kvm can be enabled. For example, in an @@ -752,14 +1138,14 @@ x86 system, this will install qemu-system-x86-core %package user Summary: QEMU user mode emulation of qemu targets -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} %description user This package provides the user mode emulation of qemu targets %package user-binfmt Summary: QEMU user mode emulation of qemu targets -Requires: %{name}-user = %{epoch}:%{version}-%{release} +Requires: %{name}-user = %{EVR} Requires(post): systemd-units Requires(postun): systemd-units %description user-binfmt @@ -768,7 +1154,8 @@ This package provides the user mode emulation of qemu targets %if %{user_static} %package user-static Summary: QEMU user mode emulation of qemu targets static build -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} +Obsoletes: qemu-kvm-user-static < %{EVR} Requires(post): systemd-units Requires(postun): systemd-units %description user-static @@ -777,265 +1164,63 @@ static binaries %endif +%ifarch aarch64 %package system-aarch64 Summary: QEMU system emulator for AArch64 -Requires: %{name}-system-aarch64-core = %{epoch}:%{version}-%{release} +Requires: %{name}-system-aarch64-core = %{EVR} %{requires_all_modules} %description system-aarch64 This package provides the QEMU system emulator for AArch64. %package system-aarch64-core Summary: QEMU system emulator for AArch64 -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} %if %{have_edk2} Requires: edk2-aarch64 %endif %description system-aarch64-core This package provides the QEMU system emulator for AArch64. +%endif -%package system-alpha -Summary: QEMU system emulator for Alpha -Requires: %{name}-system-alpha-core = %{epoch}:%{version}-%{release} -%{requires_all_modules} -%description system-alpha -This package provides the QEMU system emulator for Alpha systems. - -%package system-alpha-core -Summary: QEMU system emulator for Alpha -Requires: %{name}-common = %{epoch}:%{version}-%{release} -%description system-alpha-core -This package provides the QEMU system emulator for Alpha systems. - - -%package system-arm -Summary: QEMU system emulator for ARM -Requires: %{name}-system-arm-core = %{epoch}:%{version}-%{release} -%{requires_all_modules} -%description system-arm -This package provides the QEMU system emulator for ARM systems. - -%package system-arm-core -Summary: QEMU system emulator for ARM -Requires: %{name}-common = %{epoch}:%{version}-%{release} -%description system-arm-core -This package provides the QEMU system emulator for ARM boards. - - -%package system-avr -Summary: QEMU system emulator for AVR -Requires: %{name}-system-avr-core = %{epoch}:%{version}-%{release} -%{requires_all_modules} -%description system-avr -This package provides the QEMU system emulator for AVR systems. - -%package system-avr-core -Summary: QEMU system emulator for AVR -Requires: %{name}-common = %{epoch}:%{version}-%{release} -%description system-avr-core -This package provides the QEMU system emulator for AVR systems. - - -%package system-cris -Summary: QEMU system emulator for CRIS -Requires: %{name}-system-cris-core = %{epoch}:%{version}-%{release} -%{requires_all_modules} -%description system-cris -This package provides the system emulator for CRIS systems. - -%package system-cris-core -Summary: QEMU system emulator for CRIS -Requires: %{name}-common = %{epoch}:%{version}-%{release} -%description system-cris-core -This package provides the system emulator for CRIS boards. - - -%package system-hppa -Summary: QEMU system emulator for HPPA -Requires: %{name}-system-hppa-core = %{epoch}:%{version}-%{release} -%{requires_all_modules} -%description system-hppa -This package provides the QEMU system emulator for HPPA. - -%package system-hppa-core -Summary: QEMU system emulator for hppa -Requires: %{name}-common = %{epoch}:%{version}-%{release} -%description system-hppa-core -This package provides the QEMU system emulator for HPPA. - - -%package system-m68k -Summary: QEMU system emulator for ColdFire (m68k) -Requires: %{name}-system-m68k-core = %{epoch}:%{version}-%{release} -%{requires_all_modules} -%description system-m68k -This package provides the QEMU system emulator for ColdFire boards. - -%package system-m68k-core -Summary: QEMU system emulator for ColdFire (m68k) -Requires: %{name}-common = %{epoch}:%{version}-%{release} -%description system-m68k-core -This package provides the QEMU system emulator for ColdFire boards. - - -%package system-microblaze -Summary: QEMU system emulator for Microblaze -Requires: %{name}-system-microblaze-core = %{epoch}:%{version}-%{release} -%{requires_all_modules} -%description system-microblaze -This package provides the QEMU system emulator for Microblaze boards. - -%package system-microblaze-core -Summary: QEMU system emulator for Microblaze -Requires: %{name}-common = %{epoch}:%{version}-%{release} -%description system-microblaze-core -This package provides the QEMU system emulator for Microblaze boards. - - -%package system-mips -Summary: QEMU system emulator for MIPS -Requires: %{name}-system-mips-core = %{epoch}:%{version}-%{release} -%{requires_all_modules} -%description system-mips -This package provides the QEMU system emulator for MIPS systems. - -%package system-mips-core -Summary: QEMU system emulator for MIPS -Requires: %{name}-common = %{epoch}:%{version}-%{release} -%description system-mips-core -This package provides the QEMU system emulator for MIPS systems. - - -%package system-nios2 -Summary: QEMU system emulator for nios2 -Requires: %{name}-system-nios2-core = %{epoch}:%{version}-%{release} -%{requires_all_modules} -%description system-nios2 -This package provides the QEMU system emulator for NIOS2. - -%package system-nios2-core -Summary: QEMU system emulator for nios2 -Requires: %{name}-common = %{epoch}:%{version}-%{release} -%description system-nios2-core -This package provides the QEMU system emulator for NIOS2. - - -%package system-or1k -Summary: QEMU system emulator for OpenRisc32 -Requires: %{name}-system-or1k-core = %{epoch}:%{version}-%{release} -%{requires_all_modules} -%description system-or1k -This package provides the QEMU system emulator for OpenRisc32 boards. - -%package system-or1k-core -Summary: QEMU system emulator for OpenRisc32 -Requires: %{name}-common = %{epoch}:%{version}-%{release} -%description system-or1k-core -This package provides the QEMU system emulator for OpenRisc32 boards. - - -%package system-ppc -Summary: QEMU system emulator for PPC -Requires: %{name}-system-ppc-core = %{epoch}:%{version}-%{release} +%ifarch loongarch64 +%package system-loongarch64 +Summary: QEMU system emulator for LoongArch (LA64) +Requires: %{name}-system-loongarch64-core = %{EVR} %{requires_all_modules} -%description system-ppc -This package provides the QEMU system emulator for PPC and PPC64 systems. - -%package system-ppc-core -Summary: QEMU system emulator for PPC -Requires: %{name}-common = %{epoch}:%{version}-%{release} -Requires: seavgabios-bin -%description system-ppc-core -This package provides the QEMU system emulator for PPC and PPC64 systems. +%description system-loongarch64 +This package provides the QEMU system emulator for Loongson boards. +%package system-loongarch64-core +%if %{have_edk2} +Requires: edk2-loongarch64 +%endif +Summary: QEMU system emulator for LoongArch (LA64) +Requires: %{name}-common = %{EVR} +%description system-loongarch64-core +This package provides the QEMU system emulator for Loongson boards. +%endif +%ifarch riscv %package system-riscv Summary: QEMU system emulator for RISC-V -Requires: %{name}-system-riscv-core = %{epoch}:%{version}-%{release} +Requires: %{name}-system-riscv-core = %{EVR} %{requires_all_modules} %description system-riscv This package provides the QEMU system emulator for RISC-V systems. %package system-riscv-core Summary: QEMU system emulator for RISC-V -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} %description system-riscv-core This package provides the QEMU system emulator for RISC-V systems. +%endif -%package system-rx -Summary: QEMU system emulator for RX -Requires: %{name}-system-rx-core = %{epoch}:%{version}-%{release} -%{requires_all_modules} -%description system-rx -This package provides the QEMU system emulator for RX systems. - -%package system-rx-core -Summary: QEMU system emulator for RX -Requires: %{name}-common = %{epoch}:%{version}-%{release} -%description system-rx-core -This package provides the QEMU system emulator for RX systems. - - -%package system-s390x -Summary: QEMU system emulator for S390 -Requires: %{name}-system-s390x-core = %{epoch}:%{version}-%{release} -%{requires_all_modules} -%description system-s390x -This package provides the QEMU system emulator for S390 systems. - -%package system-s390x-core -Summary: QEMU system emulator for S390 -Requires: %{name}-common = %{epoch}:%{version}-%{release} -%description system-s390x-core -This package provides the QEMU system emulator for S390 systems. - - -%package system-sh4 -Summary: QEMU system emulator for SH4 -Requires: %{name}-system-sh4-core = %{epoch}:%{version}-%{release} -%{requires_all_modules} -%description system-sh4 -This package provides the QEMU system emulator for SH4 boards. - -%package system-sh4-core -Summary: QEMU system emulator for SH4 -Requires: %{name}-common = %{epoch}:%{version}-%{release} -%description system-sh4-core -This package provides the QEMU system emulator for SH4 boards. - - -%package system-sparc -Summary: QEMU system emulator for SPARC -Requires: %{name}-system-sparc-core = %{epoch}:%{version}-%{release} -%{requires_all_modules} -%description system-sparc -This package provides the QEMU system emulator for SPARC and SPARC64 systems. - -%package system-sparc-core -Summary: QEMU system emulator for SPARC -Requires: %{name}-common = %{epoch}:%{version}-%{release} -%description system-sparc-core -This package provides the QEMU system emulator for SPARC and SPARC64 systems. - - -%package system-tricore -Summary: QEMU system emulator for tricore -Requires: %{name}-system-tricore-core = %{epoch}:%{version}-%{release} -%{requires_all_modules} -%description system-tricore -This package provides the QEMU system emulator for Tricore. - -%package system-tricore-core -Summary: QEMU system emulator for tricore -Requires: %{name}-common = %{epoch}:%{version}-%{release} -%description system-tricore-core -This package provides the QEMU system emulator for Tricore. - - +%ifarch x86_64 %package system-x86 Summary: QEMU system emulator for x86 -Requires: %{name}-system-x86-core = %{epoch}:%{version}-%{release} +Requires: %{name}-system-x86-core = %{EVR} %{requires_all_modules} %description system-x86 This package provides the QEMU system emulator for x86. When being run in a x86 @@ -1044,7 +1229,7 @@ platform. %package system-x86-core Summary: QEMU system emulator for x86 -Requires: %{name}-common = %{epoch}:%{version}-%{release} +Requires: %{name}-common = %{EVR} Requires: seabios-bin Requires: sgabios-bin Requires: seavgabios-bin @@ -1055,22 +1240,7 @@ Requires: edk2-ovmf This package provides the QEMU system emulator for x86. When being run in a x86 machine that supports it, this package also provides the KVM virtualization platform. - - -%package system-xtensa -Summary: QEMU system emulator for Xtensa -Requires: %{name}-system-xtensa-core = %{epoch}:%{version}-%{release} -%{requires_all_modules} -%description system-xtensa -This package provides the QEMU system emulator for Xtensa boards. - -%package system-xtensa-core -Summary: QEMU system emulator for Xtensa -Requires: %{name}-common = %{epoch}:%{version}-%{release} -%description system-xtensa-core -This package provides the QEMU system emulator for Xtensa boards. - - +%endif %prep @@ -1122,7 +1292,6 @@ mkdir -p %{static_builddir} --disable-gtk \\\ --disable-guest-agent \\\ --disable-guest-agent-msi \\\ - --disable-hax \\\ --disable-hvf \\\ --disable-iconv \\\ --disable-kvm \\\ @@ -1133,7 +1302,6 @@ mkdir -p %{static_builddir} --disable-libssh \\\ --disable-libudev \\\ --disable-libusb \\\ - --disable-libxml2 \\\ --disable-linux-aio \\\ --disable-linux-io-uring \\\ --disable-linux-user \\\ @@ -1174,7 +1342,6 @@ mkdir -p %{static_builddir} --disable-spice \\\ --disable-spice-protocol \\\ --disable-strip \\\ - --disable-system \\\ --disable-tcg \\\ --disable-tools \\\ --disable-tpm \\\ @@ -1186,17 +1353,13 @@ mkdir -p %{static_builddir} --disable-vhost-crypto \\\ --disable-vhost-kernel \\\ --disable-vhost-net \\\ - --disable-vhost-scsi \\\ --disable-vhost-user \\\ --disable-vhost-user-blk-server \\\ --disable-vhost-vdpa \\\ - --disable-vhost-vsock \\\ --disable-virglrenderer \\\ --disable-virtfs \\\ - --disable-virtiofsd \\\ --disable-vnc \\\ --disable-vnc-jpeg \\\ - --disable-vnc-png \\\ --disable-vnc-sasl \\\ --disable-vte \\\ --disable-vvfat \\\ @@ -1204,10 +1367,8 @@ mkdir -p %{static_builddir} --disable-whpx \\\ --disable-xen \\\ --disable-xen-pci-passthrough \\\ - --disable-xfsctl \\\ --disable-xkbcommon \\\ --disable-zstd \\\ - --with-git-submodules=ignore \\\ --without-default-devices run_configure() { @@ -1223,18 +1384,12 @@ run_configure() { --docdir="%{_docdir}" \ --libexecdir="%{_libexecdir}" \ --extra-ldflags="%{build_ldflags}" \ -%ifnarch %{arm} --extra-cflags="%{optflags}" \ -%else - --extra-cflags="%{optflags} -DSTAP_SDT_ARG_CONSTRAINT=g" \ -%endif --with-pkgversion="%{name}-%{version}-%{release}" \ --with-suffix="%{name}" \ --firmwarepath="%firmwaredirs" \ - --meson="%{__meson}" \ --enable-trace-backends=dtrace \ --with-coroutine=ucontext \ - --with-git=git \ --tls-priority=@QEMU,SYSTEM \ %{disable_everything} \ "$@" @@ -1263,9 +1418,10 @@ run_configure \ %endif --enable-bpf \ --enable-cap-ng \ - --enable-capstone=auto \ + --enable-capstone \ --enable-coroutine-pool \ --enable-curl \ + --enable-dbus-display \ --enable-debug-info \ --enable-docs \ %if %{have_fdt} @@ -1307,25 +1463,21 @@ run_configure \ %if 0%{?must_remember_to_add_this_in_qemu_6_2} --enable-selinux \ %endif - --enable-slirp=system \ + --enable-slirp \ --enable-slirp-smbd \ --enable-snappy \ - --enable-system \ --enable-tcg \ --enable-tools \ --enable-tpm \ %if %{have_usbredir} --enable-usb-redir \ %endif - --enable-virtiofsd \ --enable-vhost-kernel \ --enable-vhost-net \ --enable-vhost-user \ --enable-vhost-user-blk-server \ --enable-vhost-vdpa \ - --enable-vhost-vsock \ --enable-vnc \ - --enable-vnc-png \ --enable-vnc-sasl \ %if %{enable_werror} --enable-werror \ @@ -1334,7 +1486,9 @@ run_configure \ \ \ --audio-drv-list=pa,alsa,%{?jack_drv}oss \ + %if 0%{?all_system_emu_support} == 1 --target-list-exclude=moxie-softmmu \ + %endif --with-default-devices \ --enable-auth-pam \ --enable-bochs \ @@ -1352,7 +1506,6 @@ run_configure \ --enable-libnfs \ %endif --enable-libudev \ - --enable-libxml2 \ %if %{have_liburing} --enable-linux-io-uring \ %endif @@ -1364,7 +1517,6 @@ run_configure \ %if %{have_librdma} --enable-pvrdma \ %endif - --enable-qcow1 \ --enable-qed \ --enable-qom-cast-debug \ --enable-replication \ @@ -1376,7 +1528,6 @@ run_configure \ --enable-usb-redir \ --enable-vdi \ --enable-vhost-crypto \ - --enable-vhost-scsi \ %if %{have_virgl} --enable-virglrenderer \ %endif @@ -1483,7 +1634,7 @@ install -D -p -m 0644 %{modprobe_kvm_conf} %{buildroot}%{_sysconfdir}/modprobe.d %endif # Copy some static data into place -install -D -p -m 0644 -t %{buildroot}%{qemudocdir} README.rst COPYING COPYING.LIB LICENSE docs/interop/qmp-spec.txt +install -D -p -m 0644 -t %{buildroot}%{qemudocdir} README.rst COPYING COPYING.LIB LICENSE docs/interop/qmp-spec.rst install -D -p -m 0644 qemu.sasl %{buildroot}%{_sysconfdir}/sasl2/%{name}.conf install -m 0644 scripts/dump-guest-memory.py %{buildroot}%{_datadir}/%{name} @@ -1540,8 +1691,10 @@ rm -rf %{buildroot}%{qemudocdir}/specs # Provided by package ipxe rm -rf %{buildroot}%{_datadir}/%{name}/pxe*rom rm -rf %{buildroot}%{_datadir}/%{name}/efi*rom +%ifnarch loongarch64 # Provided by package seavgabios rm -rf %{buildroot}%{_datadir}/%{name}/vgabios*bin +%endif # Provided by package seabios rm -rf %{buildroot}%{_datadir}/%{name}/bios*.bin # Provided by package sgabios @@ -1550,6 +1703,21 @@ rm -rf %{buildroot}%{_datadir}/%{name}/sgabios.bin rm -rf %{buildroot}%{_datadir}/%{name}/edk2* rm -rf %{buildroot}%{_datadir}/%{name}/firmware +# remove unwanted arch files +rm -f %{buildroot}%{_bindir}/qemu-system-{alpha,avr,cris,hppa,m68k,microblaze,microblazeel,mips*,nios2,or1k,ppc*,rx,s390x,sh4*,sparc*,tricore,xtensa*} +rm -f %{buildroot}%{_mandir}/man1/qemu-system-{alpha,avr,cris,hppa,m68k,microblaze,microblazeel,mips*,nios2,or1k,ppc*,rx,s390x,sh4*,sparc*,tricore,xtensa*} +rm -f %{buildroot}%{_datadir}/%{name}/QEMU,cgthree.bin +rm -f %{buildroot}%{_datadir}/%{name}/QEMU,tcx.bin +rm -f %{buildroot}%{_datadir}/%{name}/{bamboo.dtb,canyonlands.dtb,hppa-firmware.img,openbios-ppc,openbios-sparc32,openbios-sparc64,palcode-clipper,petalogix-ml605.dtb,petalogix-s3adsp1800.dtb,qemu_vga.ndrv,s390-ccw.img,s390-netboot.img,skiboot.lid,slof.bin,u-boot-sam460-20100605.bin,u-boot.e500,vof-nvram.bin,vof.bin} +rm -f %{buildroot}/%{_datadir}/systemtap/tapset/qemu-system-{alpha,avr,cris,hppa,m68k,microblaze,microblazeel,mips*,nios2,or1k,ppc*,rx,s390x,sh4*,sparc*,tricore,xtensa*}*.stp + +# remove unused header files +rm -f %{buildroot}%{_includedir}/qemu-plugin.h + +%ifarch aarch64 loongarch64 +rm -f %{buildroot}%{_libdir}/%{name}/hw-display-qxl.so +%endif + # Fedora specific stuff below %find_lang %{name} @@ -1690,12 +1858,6 @@ useradd -r -u 107 -g qemu -G kvm -d / -s /sbin/nologin \ %{_mandir}/man8/qemu-pr-helper.8* -%files -n qemu-virtiofsd -%{_mandir}/man1/virtiofsd.1* -%{_libexecdir}/virtiofsd -%{_datadir}/qemu/vhost-user/50-qemu-virtiofsd.json - - %files tools %{_bindir}/qemu-keymap %{_bindir}/qemu-edid @@ -1735,12 +1897,33 @@ useradd -r -u 107 -g qemu -G kvm -d / -s /sbin/nologin \ %endif %config(noreplace) %{_sysconfdir}/sasl2/%{name}.conf - %{_datadir}/applications/qemu.desktop %exclude %{_datadir}/%{name}/qemu-nsis.bmp %{_libexecdir}/virtfs-proxy-helper %{_mandir}/man1/virtfs-proxy-helper.1* +%{_datadir}/%{name}/npcm7xx_bootrom.bin +%{_datadir}/%{name}/opensbi-*.bin +%{_datadir}/%{name}/kvmvapic.bin +%{_datadir}/%{name}/linuxboot.bin +%{_datadir}/%{name}/multiboot.bin +%{_datadir}/%{name}/multiboot_dma.bin +%{_datadir}/%{name}/pvh.bin +%{_datadir}/%{name}/qboot.rom + +%ifarch loongarch64 +# Provided by package seavgabios +%{_datadir}/%{name}/vgabios-ati.bin +%{_datadir}/%{name}/vgabios-bochs-display.bin +%{_datadir}/%{name}/vgabios-cirrus.bin +%{_datadir}/%{name}/vgabios-qxl.bin +%{_datadir}/%{name}/vgabios-ramfb.bin +%{_datadir}/%{name}/vgabios-stdvga.bin +%{_datadir}/%{name}/vgabios-virtio.bin +%{_datadir}/%{name}/vgabios-vmware.bin +%{_datadir}/%{name}/vgabios.bin +%endif + %files tests %{testsdir} @@ -1776,6 +1959,8 @@ useradd -r -u 107 -g qemu -G kvm -d / -s /sbin/nologin \ %files audio-alsa %{_libdir}/%{name}/audio-alsa.so +%files audio-dbus +%{_libdir}/%{name}/audio-dbus.so %files audio-oss %{_libdir}/%{name}/audio-oss.so %files audio-pa @@ -1788,6 +1973,8 @@ useradd -r -u 107 -g qemu -G kvm -d / -s /sbin/nologin \ %files ui-curses %{_libdir}/%{name}/ui-curses.so +%files ui-dbus +%{_libdir}/%{name}/ui-dbus.so %files ui-gtk %{_libdir}/%{name}/ui-gtk.so %files ui-egl-headless @@ -1799,18 +1986,24 @@ useradd -r -u 107 -g qemu -G kvm -d / -s /sbin/nologin \ %files device-display-virtio-gpu %{_libdir}/%{name}/hw-display-virtio-gpu.so +%if %{have_virgl} %files device-display-virtio-gpu-gl %{_libdir}/%{name}/hw-display-virtio-gpu-gl.so +%endif %files device-display-virtio-gpu-pci %{_libdir}/%{name}/hw-display-virtio-gpu-pci.so +%if %{have_virgl} %files device-display-virtio-gpu-pci-gl %{_libdir}/%{name}/hw-display-virtio-gpu-pci-gl.so +%endif %files device-display-virtio-gpu-ccw %{_libdir}/%{name}/hw-s390x-virtio-gpu-ccw.so +%ifnarch aarch64 loongarch64 %files device-display-virtio-vga %{_libdir}/%{name}/hw-display-virtio-vga.so %files device-display-virtio-vga-gl %{_libdir}/%{name}/hw-display-virtio-vga-gl.so +%endif %files device-usb-host %{_libdir}/%{name}/hw-usb-host.so %files device-usb-redirect @@ -1830,8 +2023,10 @@ useradd -r -u 107 -g qemu -G kvm -d / -s /sbin/nologin \ %{_libdir}/%{name}/audio-spice.so %files char-spice %{_libdir}/%{name}/chardev-spice.so +%ifnarch aarch64 loongarch64 %files device-display-qxl %{_libdir}/%{name}/hw-display-qxl.so +%endif %files ui-spice-core %{_libdir}/%{name}/ui-spice-core.so %files ui-spice-app @@ -1849,61 +2044,19 @@ useradd -r -u 107 -g qemu -G kvm -d / -s /sbin/nologin \ %files user -%{_bindir}/qemu-i386 +%ifarch x86_64 %{_bindir}/qemu-x86_64 +%{_datadir}/systemtap/tapset/qemu-x86_64*.stp +%endif +%ifarch aarch64 %{_bindir}/qemu-aarch64 %{_bindir}/qemu-aarch64_be -%{_bindir}/qemu-alpha -%{_bindir}/qemu-arm -%{_bindir}/qemu-armeb -%{_bindir}/qemu-cris -%{_bindir}/qemu-hppa -%{_bindir}/qemu-hexagon -%{_bindir}/qemu-m68k -%{_bindir}/qemu-microblaze -%{_bindir}/qemu-microblazeel -%{_bindir}/qemu-mips -%{_bindir}/qemu-mipsel -%{_bindir}/qemu-mips64 -%{_bindir}/qemu-mips64el -%{_bindir}/qemu-mipsn32 -%{_bindir}/qemu-mipsn32el -%{_bindir}/qemu-nios2 -%{_bindir}/qemu-or1k -%{_bindir}/qemu-ppc -%{_bindir}/qemu-ppc64 -%{_bindir}/qemu-ppc64le -%{_bindir}/qemu-riscv32 -%{_bindir}/qemu-riscv64 -%{_bindir}/qemu-s390x -%{_bindir}/qemu-sh4 -%{_bindir}/qemu-sh4eb -%{_bindir}/qemu-sparc -%{_bindir}/qemu-sparc32plus -%{_bindir}/qemu-sparc64 -%{_bindir}/qemu-xtensa -%{_bindir}/qemu-xtensaeb - -%{_datadir}/systemtap/tapset/qemu-i386*.stp -%{_datadir}/systemtap/tapset/qemu-x86_64*.stp %{_datadir}/systemtap/tapset/qemu-aarch64*.stp -%{_datadir}/systemtap/tapset/qemu-alpha*.stp -%{_datadir}/systemtap/tapset/qemu-arm*.stp -%{_datadir}/systemtap/tapset/qemu-cris*.stp -%{_datadir}/systemtap/tapset/qemu-hppa*.stp -%{_datadir}/systemtap/tapset/qemu-hexagon*.stp -%{_datadir}/systemtap/tapset/qemu-m68k*.stp -%{_datadir}/systemtap/tapset/qemu-microblaze*.stp -%{_datadir}/systemtap/tapset/qemu-mips*.stp -%{_datadir}/systemtap/tapset/qemu-nios2*.stp -%{_datadir}/systemtap/tapset/qemu-or1k*.stp -%{_datadir}/systemtap/tapset/qemu-ppc*.stp -%{_datadir}/systemtap/tapset/qemu-riscv*.stp -%{_datadir}/systemtap/tapset/qemu-s390x*.stp -%{_datadir}/systemtap/tapset/qemu-sh4*.stp -%{_datadir}/systemtap/tapset/qemu-sparc*.stp -%{_datadir}/systemtap/tapset/qemu-xtensa*.stp - +%endif +%ifarch loongarch64 +%{_bindir}/qemu-loongarch64 +%{_datadir}/systemtap/tapset/qemu-loongarch64*.stp +%endif %files user-binfmt %{_exec_prefix}/lib/binfmt.d/qemu-*-dynamic.conf @@ -1915,205 +2068,303 @@ useradd -r -u 107 -g qemu -G kvm -d / -s /sbin/nologin \ %{_datadir}/systemtap/tapset/qemu-*-static.stp %endif - +%ifarch aarch64 %files system-aarch64 %files system-aarch64-core %{_bindir}/qemu-system-aarch64 %{_datadir}/systemtap/tapset/qemu-system-aarch64*.stp %{_mandir}/man1/qemu-system-aarch64.1* - -%files system-alpha -%files system-alpha-core -%{_bindir}/qemu-system-alpha -%{_datadir}/systemtap/tapset/qemu-system-alpha*.stp -%{_mandir}/man1/qemu-system-alpha.1* -%{_datadir}/%{name}/palcode-clipper - - -%files system-arm -%files system-arm-core -%{_bindir}/qemu-system-arm -%{_datadir}/%{name}/npcm7xx_bootrom.bin -%{_datadir}/systemtap/tapset/qemu-system-arm*.stp -%{_mandir}/man1/qemu-system-arm.1* - - -%files system-avr -%files system-avr-core -%{_bindir}/qemu-system-avr -%{_datadir}/systemtap/tapset/qemu-system-avr*.stp -%{_mandir}/man1/qemu-system-avr.1* - - -%files system-cris -%files system-cris-core -%{_bindir}/qemu-system-cris -%{_datadir}/systemtap/tapset/qemu-system-cris*.stp -%{_mandir}/man1/qemu-system-cris.1* - - -%files system-hppa -%files system-hppa-core -%{_bindir}/qemu-system-hppa -%{_datadir}/systemtap/tapset/qemu-system-hppa*.stp -%{_mandir}/man1/qemu-system-hppa.1* -%{_datadir}/%{name}/hppa-firmware.img - - -%files system-m68k -%files system-m68k-core -%{_bindir}/qemu-system-m68k -%{_datadir}/systemtap/tapset/qemu-system-m68k*.stp -%{_mandir}/man1/qemu-system-m68k.1* - - -%files system-microblaze -%files system-microblaze-core -%{_bindir}/qemu-system-microblaze -%{_bindir}/qemu-system-microblazeel -%{_datadir}/systemtap/tapset/qemu-system-microblaze*.stp -%{_mandir}/man1/qemu-system-microblaze.1* -%{_mandir}/man1/qemu-system-microblazeel.1* -%{_datadir}/%{name}/petalogix*.dtb - - -%files system-mips -%files system-mips-core -%{_bindir}/qemu-system-mips -%{_bindir}/qemu-system-mipsel -%{_bindir}/qemu-system-mips64 -%{_bindir}/qemu-system-mips64el -%{_datadir}/systemtap/tapset/qemu-system-mips*.stp -%{_mandir}/man1/qemu-system-mips.1* -%{_mandir}/man1/qemu-system-mipsel.1* -%{_mandir}/man1/qemu-system-mips64el.1* -%{_mandir}/man1/qemu-system-mips64.1* - - -%files system-nios2 -%files system-nios2-core -%{_bindir}/qemu-system-nios2 -%{_datadir}/systemtap/tapset/qemu-system-nios2*.stp -%{_mandir}/man1/qemu-system-nios2.1* - - -%files system-or1k -%files system-or1k-core -%{_bindir}/qemu-system-or1k -%{_datadir}/systemtap/tapset/qemu-system-or1k*.stp -%{_mandir}/man1/qemu-system-or1k.1* - - -%files system-ppc -%files system-ppc-core -%{_bindir}/qemu-system-ppc -%{_bindir}/qemu-system-ppc64 -%{_datadir}/systemtap/tapset/qemu-system-ppc*.stp -%{_mandir}/man1/qemu-system-ppc.1* -%{_mandir}/man1/qemu-system-ppc64.1* -%{_datadir}/%{name}/bamboo.dtb -%{_datadir}/%{name}/canyonlands.dtb -%{_datadir}/%{name}/qemu_vga.ndrv -%{_datadir}/%{name}/skiboot.lid -%{_datadir}/%{name}/u-boot.e500 -%{_datadir}/%{name}/u-boot-sam460-20100605.bin -%{_datadir}/%{name}/slof.bin -%{_datadir}/%{name}/openbios-ppc -%if %{have_memlock_limits} -%{_sysconfdir}/security/limits.d/95-kvm-memlock.conf %endif +%ifarch loongarch64 +%files system-loongarch64 +%files system-loongarch64-core +%{_bindir}/qemu-system-loongarch64 +%{_datadir}/systemtap/tapset/qemu-system-loongarch64*.stp +%{_mandir}/man1/qemu-system-loongarch64.1* +%endif +%ifarch riscv %files system-riscv %files system-riscv-core %{_bindir}/qemu-system-riscv32 %{_bindir}/qemu-system-riscv64 %{_datadir}/%{name}/opensbi-riscv*.bin -%{_datadir}/%{name}/opensbi-riscv*.elf %{_datadir}/systemtap/tapset/qemu-system-riscv*.stp %{_mandir}/man1/qemu-system-riscv*.1* +%endif - -%files system-rx -%files system-rx-core -%{_bindir}/qemu-system-rx -%{_datadir}/systemtap/tapset/qemu-system-rx*.stp -%{_mandir}/man1/qemu-system-rx.1* - - -%files system-s390x -%files system-s390x-core -%{_bindir}/qemu-system-s390x -%{_datadir}/systemtap/tapset/qemu-system-s390x*.stp -%{_mandir}/man1/qemu-system-s390x.1* -%{_datadir}/%{name}/s390-ccw.img -%{_datadir}/%{name}/s390-netboot.img - - -%files system-sh4 -%files system-sh4-core -%{_bindir}/qemu-system-sh4 -%{_bindir}/qemu-system-sh4eb -%{_datadir}/systemtap/tapset/qemu-system-sh4*.stp -%{_mandir}/man1/qemu-system-sh4.1* -%{_mandir}/man1/qemu-system-sh4eb.1* - - -%files system-sparc -%files system-sparc-core -%{_bindir}/qemu-system-sparc -%{_bindir}/qemu-system-sparc64 -%{_datadir}/systemtap/tapset/qemu-system-sparc*.stp -%{_mandir}/man1/qemu-system-sparc.1* -%{_mandir}/man1/qemu-system-sparc64.1* -%{_datadir}/%{name}/QEMU,tcx.bin -%{_datadir}/%{name}/QEMU,cgthree.bin -%{_datadir}/%{name}/openbios-sparc32 -%{_datadir}/%{name}/openbios-sparc64 - -%files system-tricore -%files system-tricore-core -%{_bindir}/qemu-system-tricore -%{_datadir}/systemtap/tapset/qemu-system-tricore*.stp -%{_mandir}/man1/qemu-system-tricore.1* - - +%ifarch x86_64 %files system-x86 %files system-x86-core -%{_bindir}/qemu-system-i386 %{_bindir}/qemu-system-x86_64 -%{_libdir}/%{name}/accel-tcg-i386.so %{_libdir}/%{name}/accel-tcg-x86_64.so -%{_datadir}/systemtap/tapset/qemu-system-i386*.stp %{_datadir}/systemtap/tapset/qemu-system-x86_64*.stp -%{_mandir}/man1/qemu-system-i386.1* %{_mandir}/man1/qemu-system-x86_64.1* -%{_datadir}/%{name}/kvmvapic.bin -%{_datadir}/%{name}/linuxboot.bin -%{_datadir}/%{name}/multiboot.bin -%{_datadir}/%{name}/multiboot_dma.bin -%{_datadir}/%{name}/pvh.bin -%{_datadir}/%{name}/qboot.rom %if %{need_qemu_kvm} %{_bindir}/qemu-kvm %{_mandir}/man1/qemu-kvm.1* %endif +%endif - -%files system-xtensa -%files system-xtensa-core -%{_bindir}/qemu-system-xtensa -%{_bindir}/qemu-system-xtensaeb -%{_datadir}/systemtap/tapset/qemu-system-xtensa*.stp -%{_mandir}/man1/qemu-system-xtensa.1* -%{_mandir}/man1/qemu-system-xtensaeb.1* # endif !tools_only %endif - %changelog -* Tue Nov 22 2022 mgb01105731 - 6.2.0-2 +* Sat Nov 30 2024 Xianglai Li - 2:8.2.0-25 +- Remove loongarch qemu's dependency on the seavgabios package. + +* Wed Nov 27 2024 Xianglai Li - 2:8.2.0-24 +- Enable spice for loongarch64. + +* Wed Nov 27 2024 mgb01105731 - 2:8.2.0-23 +- Fix vm cannot boot on arm64 plateform + +* Mon Nov 15 2024 Chang Gao - 2:8.2.0-22 +- Disable other platform support when running on specified arch. + +* Tue Nov 14 2024 Xuchun Shang - 2:8.2.0-21 +- Update the src package and release for version 21 + +* Tue Oct 29 2024 Xuchun Shang - 2:8.2.0-20 +- Update the src package and release for version 20 + +* Thu Sep 26 2024 Wenlong Zhang - 2:8.2.0-19 +- enable block_rbd for loongarch64 + +* Sun Aug 18 2024 Wencheng Yang - 2:8.2.0-18 +- Patch1054: 1054-virtio-net-Fix-network-stall-at-the-host-side-waitin.patch + (Fix network stall at the host side waiting for kick) + +* Mon Jun 20 2024 Wencheng Yang - 2:8.2.0-17 +- Patch1053: 1053-target-i386-csv-Release-CSV3-shared-pages-after-unma.patch + (Release CSV3 shared pages after unmapping DMA) + +* Mon Jun 17 2024 Wencheng Yang - 2:8.2.0-16 +- Patch1052: 1052-hw-net-virtio-net-Update-event-idx-if-guest-has-made.patch + (Update virtio-net event idx after double check) + +* Fri Jun 7 2024 Depei Yang -2.8.2.0-15 +- Patch1051: 1051-vfio-Add-vfio-based-mediated-hct-support.patch + (Add HCT support for hygon platform) + +* Wed May 22 2024 Song Gao -2.8.2.0-14 +- Patch0036: 0036-target-loongarch-kvm-Fix-VM-recovery-from-disk-failu.patch +- Patch0037: 0037-target-loongarch-kvm-fpu-save-the-vreg-registers-hig.patch + (Fix migration bugs and remove unused requires) + +* Mon May 20 2024 Song Gao -2.8.2.0-13 +- Fix system-loongarch64-core requires + +* Tue Apr 19 2024 Yanjing Zhou - 2:8.2.0-12 +- Patch1049: 1049-target-i386-Add-Hygon-Dhyana-v3-CPU-model.patch +- Patch1050: 1050-target-i386-Add-new-Hygon-Dharma-CPU-model.patch + (Add Hygon Dhyana-v3 and Dharma CPU model) + +* Mon Apr 15 2024 Liyang Han - 2:8.2.0-11 +- Patch1047: 1047-target-i386-sev-Fix-incompatibility-between-SEV-and-.patch +- Patch1048: 1048-target-i386-sev-Add-support-for-reuse-ASID-for-diffe.patch + (Fix GET_ID API incompatibility issue, support CSV reuse ASID) + +* Thu Apr 11 2024 Liyang Han - 2:8.2.0-10 +- Patch1033: 1033-target-i386-csv-Add-CSV3-context.patch +- Patch1034: 1034-target-i386-csv-Add-command-to-initialize-CSV3-conte.patch +- Patch1035: 1035-target-i386-csv-Add-command-to-load-data-to-CSV3-gue.patch +- Patch1036: 1036-target-i386-csv-Add-command-to-load-vmcb-to-CSV3-gue.patch +- Patch1037: 1037-target-i386-cpu-Populate-CPUID-0x8000_001F-when-CSV3.patch +- Patch1038: 1038-target-i386-csv-Do-not-register-unregister-guest-sec.patch +- Patch1039: 1039-target-i386-csv-Load-initial-image-to-private-memory.patch +- Patch1040: 1040-vga-Force-full-update-for-CSV3-guest.patch +- Patch1041: 1041-vfio-Only-map-shared-region-for-CSV3-virtual-machine.patch +- Patch1042: 1042-linux-headers-update-kernel-headers-to-include-CSV3-.patch +- Patch1043: 1043-target-i386-csv-Add-support-to-migrate-the-outgoing-.patch +- Patch1044: 1044-target-i386-csv-Add-support-to-migrate-the-incoming-.patch +- Patch1045: 1045-target-i386-csv-Add-support-to-migrate-the-outgoing-.patch +- Patch1046: 1046-target-i386-csv-Add-support-to-migrate-the-incoming-.patch + (Support boot CSV3 guest, CSV3 live migration) + +* Thu Apr 11 2024 Liyang Han - 2:8.2.0-9 +- Patch1005: 1005-doc-update-AMD-SEV-to-include-Live-migration-flow.patch +- Patch1006: 1006-migration.json-add-AMD-SEV-specific-migration-parame.patch +- Patch1007: 1007-confidential-guest-support-introduce-ConfidentialGue.patch +- Patch1008: 1008-target-i386-sev-provide-callback-to-setup-outgoing-c.patch +- Patch1009: 1009-target-i386-sev-do-not-create-launch-context-for-an-.patch +- Patch1010: 1010-target-i386-sev-add-support-to-encrypt-the-outgoing-.patch +- Patch1011: 1011-target-i386-sev-add-support-to-load-incoming-encrypt.patch +- Patch1012: 1012-kvm-Add-support-for-SEV-shared-regions-list-and-KVM_.patch +- Patch1013: 1013-migration-add-support-to-migrate-shared-regions-list.patch +- Patch1014: 1014-migration-ram-add-support-to-send-encrypted-pages.patch +- Patch1015: 1015-migration-ram-Force-encrypted-status-for-flash0-flas.patch +- Patch1016: 1016-kvm-Add-support-for-userspace-MSR-filtering-and-hand.patch +- Patch1017: 1017-target-i386-sev-Return-0-if-sev_send_get_packet_len-.patch +- Patch1018: 1018-migration-ram-Force-encrypted-status-for-VGA-vram.patch +- Patch1019: 1019-target-i386-sev-Clear-shared_regions_list-when-reboo.patch +- Patch1020: 1020-migration-ram-Fix-calculation-of-gfn-correpond-to-a-.patch +- Patch1021: 1021-target-i386-Introduce-header-file-csv.h.patch +- Patch1022: 1022-target-i386-csv-Read-cert-chain-from-file-when-prepa.patch +- Patch1023: 1023-target-i386-csv-add-support-to-queue-the-outgoing-pa.patch +- Patch1024: 1024-target-i386-csv-add-support-to-encrypt-the-outgoing-.patch +- Patch1025: 1025-target-i386-csv-add-support-to-queue-the-incoming-pa.patch +- Patch1026: 1026-target-i386-csv-add-support-to-load-incoming-encrypt.patch +- Patch1027: 1027-migration-ram-Accelerate-the-transmission-of-CSV-gue.patch +- Patch1028: 1028-migration-ram-Accelerate-the-loading-of-CSV-guest-s-.patch +- Patch1029: 1029-target-i386-csv-Add-support-for-migrate-VMSA-for-CSV.patch +- Patch1030: 1030-target-i386-get-set-migrate-GHCB-state.patch +- Patch1031: 1031-target-i386-kvm-Fix-the-resettable-info-when-emulate.patch +- Patch1032: 1032-kvm-Add-support-for-CSV2-reboot.patch + (Support CSV/CSV2 live migration, CSV2 reboot) + +* Sun Apr 7 2024 Mengbiao Xiong - 2:8.2.0-8 +- Patch0035: 0035-newfeature-support-vpsp.patch + (Support tkm key isolation) + +* Sun Apr 7 2024 Song Gao - 2:8.2.0-7 + Enable build LoongArch kvm_package. + +* Sun Apr 7 2024 Song Gao - 2:8.2.0-6 +- Remove unused vhostuser-backend and subpackage qemu-virtiofsd. + +* Tue Apr 2 2024 Song Gao - 2:8.2.0-5 +- Patch0003: 0003-hw-loongarch-virt-Align-high-memory-base-address-wit.patch +- Patch0004: 0004-target-loongarch-Add-timer-information-dump-support.patch +- Patch0005: 0005-target-loongarch-meson-move-gdbstub.c-to-loongarch.s.patch +- Patch0006: 0006-target-loongarch-move-translate-modules-to-tcg.patch +- Patch0007: 0007-linux-headers-Update-to-Linux-v6.7-rc5.patch +- Patch0008: 0008-linux-headers-Synchronize-linux-headers-from-linux-v.patch +- Patch0009: 0009-target-loongarch-Define-some-kvm_arch-interfaces.patch +- Patch0010: 0010-target-loongarch-Supplement-vcpu-env-initial-when-vc.patch +- Patch0011: 0011-target-loongarch-Implement-kvm-get-set-registers.patch +- Patch0012: 0012-target-loongarch-Implement-kvm_arch_init-function.patch +- Patch0013: 0013-target-loongarch-Implement-kvm_arch_init_vcpu.patch +- Patch0014: 0014-target-loongarch-Implement-kvm_arch_handle_exit.patch +- Patch0015: 0015-target-loongarch-Restrict-TCG-specific-code.patch +- Patch0016: 0016-target-loongarch-Implement-set-vcpu-intr-for-kvm.patch +- Patch0017: 0017-target-loongarch-Add-loongarch-kvm-into-meson-build.patch +- Patch0018: 0018-hw-intc-loongarch_ipi-Use-MemTxAttrs-interface-for-i.patch +- Patch0019: 0019-hw-loongarch-virt-Set-iocsr-address-space-per-board-.patch +- Patch0020: 0020-hw-intc-loongarch_extioi-Add-dynamic-cpu-number-supp.patch +- Patch0021: 0021-hw-intc-loongarch_extioi-Add-vmstate-post_load-suppo.patch +- Patch0022: 0022-configure-Add-linux-header-compile-support-for-Loong.patch +- Patch0023: 0023-target-loongarch-Set-cpuid-CSR-register-only-once-wi.patch +- Patch0024: 0024-target-loongarch-kvm-Enable-LSX-LASX-extension.patch +- Patch0025: 0025-target-loongarch-Fix-qtest-test-hmp-error-when-KVM-o.patch +- Patch0026: 0026-loongarch-Change-the-UEFI-loading-mode-to-loongarch.patch +- Patch0027: 0027-target-loongarch-Fix-tlb-huge-page-loading-issue.patch +- Patch0028: 0028-target-loongarch-Fix-qemu-loongarch64-hang-when-exec.patch +- Patch0029: 0029-target-loongarch-kvm-Add-software-breakpoint-support.patch +- Patch0030: 0030-hw-intc-loongarch_extioi-Add-virt-extension-support.patch +- Patch0031: 0031-target-loongarch-kvm-sync-kernel-header-files.patch +- Patch0032: 0032-hw-intc-loongarch_extioi-Add-virt-extension-support-.patch +- Patch0033: 0033-target-loongarch-kvm-Add-pmu-support.patch +- Patch0034: 0034-target-loongarch-Fix-qemu-system-loongarch64-assert-.patch + (Add LoongArch features and bugfixes) + +* Tue Apr 2 2024 Song Gao - 2:8.2.0-4 +- Enable build LoongArch + +* Wed Mar 27 2024 Aubrey Li - 2:8.2.0-3 +- Patch1001: 1001-i386-cpu-Clear-FEAT_XSAVE_XSS_LO-HI-leafs-when-CPUID.patch +- Patch1002: 1002-i386-cpu-Mask-with-XCR0-XSS-mask-for-FEAT_XSAVE_XCR0.patch +- Patch1003: 1003-i386-cpuid-Decrease-cpuid_i-when-skipping-CPUID-leaf.patch +- Patch1004: 1004-i386-cpuid-Move-leaf-7-to-correct-group.patch + (Backport i386/cpu fixes from upstream 8.2.2) + +* Wed Mar 27 2024 Chang Gao - 2:8.2.0-2 +- Fix requires mismatch + +* Wed Mar 20 2024 Jacob Wang - 2:8.2.0-1 +- Update to 8.2.0 + Remove subpackage of virtiofsd since QEMU upstream deleted the C impl of + virtiofsd entirely. The alternative is to rewrite it separately in rust. + +* Sat Mar 06 2024 Liyang Han - 15:7.2.6-8 +- Patch0046: 0046-target-i386-sev-Fix-incompatibility-between-SEV-and-.patch + (Fix incompatibility between SEV and CSV on the GET_ID API) + +* Wed Jan 22 2024 Liyang Han - 15:7.2.6-7 +- Patch0045: 0045-anolis-target-i386-sev-Add-support-for-reuse-ASID-fo.patch + (Support reuse ASID for CSV guests) + +* Fri Jan 19 2024 Chang Gao - 15:7.2.6-6 +- Fix crash when revert snapshot + +* Thu Nov 30 2023 Xin Jiang - 15:7.2.6-5 +- Patch0038: 0038-anolis-vfio-only-map-shared-region-for-CSV-virtual-m.patch +- Patch0039: 0039-anolis-linux-headers-update-kernel-headers-to-includ.patch +- Patch0040: 0040-anolis-csv-i386-add-support-to-migrate-the-outgoing-.patch +- Patch0041: 0041-anolis-csv-i386-add-support-to-migrate-the-incoming-.patch +- Patch0042: 0042-anolis-csv-i386-add-support-to-migrate-the-outgoing-.patch +- Patch0043: 0043-anolis-csv-i386-add-support-to-migrate-the-incoming-.patch + (Support CSV3 live migration) + +* Tue Nov 22 2023 Liyang Han - 15:7.2.6-4 +- Patch0010: 0010-doc-update-AMD-SEV-to-include-Live-migration-flow.patch +- Patch0011: 0011-migration.json-add-AMD-SEV-specific-migration-parame.patch +- Patch0012: 0012-confidential-guest-support-introduce-ConfidentialGue.patch +- Patch0013: 0013-target-i386-sev-provide-callback-to-setup-outgoing-c.patch +- Patch0014: 0014-target-i386-sev-do-not-create-launch-context-for-an-.patch +- Patch0015: 0015-target-i386-sev-add-support-to-encrypt-the-outgoing-.patch +- Patch0016: 0016-target-i386-sev-add-support-to-load-incoming-encrypt.patch +- Patch0017: 0017-kvm-Add-support-for-SEV-shared-regions-list-and-KVM_.patch +- Patch0018: 0018-migration-add-support-to-migrate-shared-regions-list.patch +- Patch0019: 0019-migration-ram-add-support-to-send-encrypted-pages.patch +- Patch0020: 0020-migration-ram-Force-encrypted-status-for-flash0-flas.patch +- Patch0021: 0021-migration-for-SEV-live-migration-bump-downtime-limit.patch +- Patch0022: 0022-kvm-Add-support-for-userspace-MSR-filtering-and-hand.patch +- Patch0023: 0023-anolis-migration-ram-Force-encrypted-status-for-VGA-.patch +- Patch0024: 0024-anolis-target-i386-sev-Clear-shared_regions_list-whe.patch +- Patch0025: 0025-anolis-migration-ram-Fix-calculation-of-gfn-correpon.patch +- Patch0026: 0026-anolis-target-i386-csv-Move-is_hygon_cpu-to-header-f.patch +- Patch0027: 0027-anolis-target-i386-csv-Read-cert-chain-from-file-whe.patch +- Patch0028: 0028-anolis-target-i386-csv-add-support-to-queue-the-outg.patch +- Patch0029: 0029-anolis-target-i386-csv-add-support-to-encrypt-the-ou.patch +- Patch0030: 0030-anolis-target-i386-csv-add-support-to-queue-the-inco.patch +- Patch0031: 0031-anolis-target-i386-csv-add-support-to-load-incoming-.patch +- Patch0032: 0032-anolis-migration-ram-Accelerate-the-transmission-of-.patch +- Patch0033: 0033-anolis-migration-ram-Accelerate-the-loading-of-CSV-g.patch +- Patch0034: 0034-anolis-target-i386-csv-Add-support-for-migrate-VMSA-.patch +- Patch0035: 0035-anolis-target-i386-get-set-migrate-GHCB-state.patch +- Patch0036: 0036-anolis-target-i386-kvm-Return-resettable-when-emulat.patch +- Patch0037: 0037-anolis-kvm-Add-support-for-CSV2-reboot.patch + (Support Hygon CSV/CSV2 live migration, CSV2 reboot) + +* Thu Nov 16 2023 Wenlong Zhang - 15:7.2.6-3 +- disable build for loongarch + +* Tue Oct 17 2023 Xin Jiang - 15:7.2.6-2 +- Patch0002: 0002-anolis-csv-i386-add-CSV-context.patch +- Patch0003: 0003-anolis-csv-i386-add-command-to-initialize-CSV-contex.patch +- Patch0004: 0004-anolis-csv-i386-add-command-to-load-data-to-guest-me.patch +- Patch0005: 0005-anolis-csv-i386-add-command-to-load-vmcb-to-guest-me.patch +- Patch0006: 0006-anolis-cpu-i386-populate-CPUID-0x8000_001F-when-CSV-.patch +- Patch0007: 0007-anolis-csv-i386-CSV-guest-do-not-need-register-unreg.patch +- Patch0008: 0008-anolis-target-i386-csv-load-initial-image-to-private.patch +- Patch0009: 0009-anolis-vga-force-full-update-for-CSV-guest.patch + (Support Hygon CSV3 feature) + +* Mon Oct 16 2023 Funda Wang - 15:7.2.6-1 +- New version 7.2.6 + +* Wed Jul 12 2023 Funda Wang - 2:7.2.4-1 +- New version 7.2.4 + +* Mon Jun 19 2023 happy_orange - 2:7.2.0-5 +- add obsoletes to drop the unexisted package + +* Mon May 08 2023 mgb01105731 - 2:7.2.0-4 +- rebuild with device-mapper-multipath update + +* Mon Mar 20 2023 Chunmei Xu - 2:7.2.0-3 +- remove unwanted arches support + +* Thu Mar 16 2023 Chunmei Xu - 2:7.2.0-2 +- disable virgl, block_rbd and block_nfs + +* Mon Feb 13 2023 Kun(llfl) - 2:7.2.0-1 +- Update to 7.2.0 + +* Tue Nov 22 2022 mgb01105731 - 2:6.2.0-2 - remove sdl & change bios,slof Requires -* Tue Mar 15 2022 forrest_ly - 6.2.0-1 -- Init for Anolis OS 23 +* Tue Mar 15 2022 forrest_ly - 2:6.2.0-1 +- Init for Anolis OS 23