Merge tag 's390x-20260629' of https://gitlab.com/cohuck/qemu into staging Fix a potential way for an s390x/kvm guest to crash QEMU while filling a STSI buffer. # -----BEGIN PGP SIGNATURE----- # # iIgEABYKADAWIQRpo7U29cv8ZSCAJsHeiLtWQd5mwQUCakJyoxIcY29odWNrQHJl # ZGhhdC5jb20ACgkQ3oi7VkHeZsGXNQEA1pKKaD39nYxYTa3/wRQFIaa+eQ8q8IsH # GrbveEStsgwBAKWj6+tAgb4SQDdugu9UADMgdvZyAewmbYzL29/qn1MB # =Gqpa # -----END PGP SIGNATURE----- # gpg: Signature made Mon 29 Jun 2026 15:26:59 CEST # gpg: using EDDSA key 69A3B536F5CBFC65208026C1DE88BB5641DE66C1 # gpg: issuer "cohuck@redhat.com" # gpg: Good signature from "Cornelia Huck <conny@cornelia-huck.de>" [unknown] # gpg: aka "Cornelia Huck <cohuck@kernel.org>" [unknown] # gpg: aka "Cornelia Huck <cornelia.huck@de.ibm.com>" [full] # gpg: aka "Cornelia Huck <huckc@linux.vnet.ibm.com>" [full] # gpg: aka "Cornelia Huck <cohuck@redhat.com>" [unknown] # gpg: WARNING: The key's User ID is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: C3D0 D66D C362 4FF6 A8C0 18CE DECF 6B93 C6F0 2FAF # Subkey fingerprint: 69A3 B536 F5CB FC65 2080 26C1 DE88 BB56 41DE 66C1 * tag 's390x-20260629' of https://gitlab.com/cohuck/qemu: s390x/kvm: clamp stsi 3.2.2 size Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
diff --git a/.gitlab-ci.d/base.yml b/.gitlab-ci.d/base.yml index 714d2ed..8fe0080 100644 --- a/.gitlab-ci.d/base.yml +++ b/.gitlab-ci.d/base.yml
@@ -134,3 +134,51 @@ QEMU_CI_CONTAINER_TAG: $CI_COMMIT_REF_SLUG - when: on_success + +# Any job running meson should capture meson logs +# by default. Some jobs might override the artifacts +# to capture further files +.base_meson_job_template: + extends: .base_job_template + artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" + when: always + expire_in: 7 days + paths: + - build/meson-logs + - ci-runner-env + reports: + junit: build/meson-logs/*.junit.xml + before_script: + - source scripts/ci/gitlab-ci-section + - section_start setup "Pre-script setup" + - JOBS=$(expr $(nproc) + 1) + # Prevent logs (if any) from prior build job artifacts + # from being duplicated in the new job artifacts + - rm -f build/meson-logs/* + - mkdir -p ci-runner-env + - test -f /packages.txt && cp /packages.txt ci-runner-env/packages.txt + - cp /proc/mounts ci-runner-env/mounts.txt + - cp /proc/cpuinfo ci-runner-env/cpuinfo.txt + - cp /proc/meminfo ci-runner-env/meminfo.txt + - ls -l /dev > ci-runner-env/devices.txt + - uname -a > ci-runner-env/kernel.txt + - id -a > ci-runner-env/user.txt + - printenv | sort > ci-runner-env/environ.txt + - section_end setup + +.base_meson_ccache_job_template: + extends: .base_meson_job_template + cache: + paths: + - ccache + key: "$CI_JOB_NAME" + when: always + script: + - export CCACHE_BASEDIR="$(pwd)" + - export CCACHE_DIR="$CCACHE_BASEDIR/ccache" + - export CCACHE_MAXSIZE="500M" + - export PATH="$CCACHE_WRAPPERSDIR:$PATH" + - ccache --zero-stats + after_script: + - ccache --show-stats
diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index 0050586..b5f3760 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml
@@ -1,42 +1,13 @@ -# Any job running meson should capture meson logs -# by default. Some jobs might override the artifacts -# to capture further files -.meson_job_template: - extends: .base_job_template - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" - when: always - expire_in: 7 days - paths: - - build/meson-logs - reports: - junit: build/meson-logs/*.junit.xml - .native_build_job_template: - extends: .meson_job_template + extends: .base_meson_ccache_job_template stage: build image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG - cache: - paths: - - ccache - key: "$CI_JOB_NAME" - when: always - before_script: - - source scripts/ci/gitlab-ci-section - - section_start setup "Pre-script setup" - - JOBS=$(expr $(nproc) + 1) - - cat /packages.txt - - section_end setup script: - - export CCACHE_BASEDIR="$(pwd)" - - export CCACHE_DIR="$CCACHE_BASEDIR/ccache" - - export CCACHE_MAXSIZE="500M" - - export PATH="$CCACHE_WRAPPERSDIR:$PATH" + - !reference [.base_meson_ccache_job_template, script] - du -sh .git - mkdir build - cd build - - ccache --zero-stats - section_start configure "Running configure" - ../configure --enable-werror --disable-docs --enable-fdt=system --disable-debug-info @@ -57,7 +28,6 @@ $MAKE -j"$JOBS" $MAKE_CHECK_ARGS ; fi - section_end test - - ccache --show-stats # We jump some hoops in common_test_job_template to avoid # rebuilding all the object files we skip in the artifacts @@ -68,6 +38,7 @@ paths: - build - .git-submodule-status + - ci-runner-env exclude: - build/**/*.p - build/**/*.a.p @@ -75,9 +46,18 @@ - build/**/*.c.o.d .common_test_job_template: - extends: .meson_job_template + extends: .base_meson_job_template stage: test image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG + artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" + when: always + expire_in: 7 days + paths: + - build/meson-logs + - build/tests/functional/*/*/*.log + reports: + junit: build/meson-logs/*.junit.xml script: - source scripts/ci/gitlab-ci-section - section_start buildenv "Setting up to run tests" @@ -98,11 +78,6 @@ .native_test_job_template: extends: .common_test_job_template - before_script: - # Prevent logs from the build job that run earlier - # from being duplicated in the test job artifacts - - rm -f build/meson-logs/* - .functional_test_job_template: extends: .common_test_job_template @@ -111,6 +86,12 @@ paths: - ${CI_PROJECT_DIR}/functional-cache policy: pull-push + before_script: + - export QEMU_TEST_ALLOW_UNTRUSTED_CODE=1 + - export QEMU_TEST_CACHE_DIR=${CI_PROJECT_DIR}/functional-cache + # Prevent logs from the build job that run earlier + # from being duplicated in the test job artifacts + - rm -f build/meson-logs/* artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" when: always @@ -118,29 +99,21 @@ paths: - build/meson-logs - build/tests/functional/*/*/*.log + - ci-runner-env reports: junit: build/meson-logs/*.junit.xml - before_script: - - export QEMU_TEST_ALLOW_UNTRUSTED_CODE=1 - - export QEMU_TEST_CACHE_DIR=${CI_PROJECT_DIR}/functional-cache - # Prevent logs from the build job that run earlier - # from being duplicated in the test job artifacts - - rm -f build/meson-logs/* after_script: - cd build - du -chs ${CI_PROJECT_DIR}/*-cache variables: QEMU_JOB_FUNCTIONAL: 1 + QEMU_TEST_ALLOW_UNTRUSTED_CODE: 1 + QEMU_TEST_CACHE_DIR: ${CI_PROJECT_DIR}/functional-cache .wasm_build_job_template: - extends: .meson_job_template + extends: .base_meson_job_template stage: build image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG - before_script: - - source scripts/ci/gitlab-ci-section - - section_start setup "Pre-script setup" - - JOBS=$(expr $(nproc) + 1) - - section_end setup script: - du -sh .git - mkdir build
diff --git a/.gitlab-ci.d/crossbuild-template.yml b/.gitlab-ci.d/crossbuild-template.yml index 58136d0..9442381 100644 --- a/.gitlab-ci.d/crossbuild-template.yml +++ b/.gitlab-ci.d/crossbuild-template.yml
@@ -1,27 +1,12 @@ .cross_system_build_job: - extends: .base_job_template + extends: .base_meson_ccache_job_template stage: build image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG - cache: - paths: - - ccache - key: "$CI_JOB_NAME" - when: always timeout: 80m - before_script: - - source scripts/ci/gitlab-ci-section - - section_start setup "Pre-script setup" - - JOBS=$(expr $(nproc) + 1) - - cat /packages.txt - - section_end setup script: - - export CCACHE_BASEDIR="$(pwd)" - - export CCACHE_DIR="$CCACHE_BASEDIR/ccache" - - export CCACHE_MAXSIZE="500M" - - export PATH="$CCACHE_WRAPPERSDIR:$PATH" + - !reference [.base_meson_ccache_job_template, script] - mkdir build - cd build - - ccache --zero-stats - section_start configure "Running configure" - ../configure --enable-werror --disable-docs --enable-fdt=system --disable-user $QEMU_CONFIGURE_OPTS $EXTRA_CONFIGURE_OPTS @@ -46,7 +31,6 @@ mv -v qemu-setup*.exe qemu-setup-${version}.exe; fi - section_end installer - - ccache --show-stats # Job to cross-build specific accelerators. # @@ -54,22 +38,12 @@ # KVM), and set extra options (such disabling other accelerators) via the # $EXTRA_CONFIGURE_OPTS variable. .cross_accel_build_job: - extends: .base_job_template + extends: .base_meson_ccache_job_template stage: build image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG timeout: 60m - cache: - paths: - - ccache/ - key: "$CI_JOB_NAME" - before_script: - - source scripts/ci/gitlab-ci-section - - JOBS=$(expr $(nproc) + 1) script: - - export CCACHE_BASEDIR="$(pwd)" - - export CCACHE_DIR="$CCACHE_BASEDIR/ccache" - - export CCACHE_MAXSIZE="500M" - - export PATH="$CCACHE_WRAPPERSDIR:$PATH" + - !reference [.base_meson_ccache_job_template, script] - mkdir build - cd build - section_start configure "Running configure" @@ -87,20 +61,11 @@ - section_end test .cross_user_build_job: - extends: .base_job_template + extends: .base_meson_ccache_job_template stage: build image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG - cache: - paths: - - ccache/ - key: "$CI_JOB_NAME" - before_script: - - source scripts/ci/gitlab-ci-section - - JOBS=$(expr $(nproc) + 1) script: - - export CCACHE_BASEDIR="$(pwd)" - - export CCACHE_DIR="$CCACHE_BASEDIR/ccache" - - export CCACHE_MAXSIZE="500M" + - !reference [.base_meson_ccache_job_template, script] - mkdir build - cd build - section_start configure "Running configure" @@ -119,15 +84,3 @@ $MAKE -j"$JOBS" $MAKE_CHECK_ARGS ; fi - section_end test - -# We can still run some tests on some of our cross build jobs. They can add this -# template to their extends to save the build logs and test results -.cross_test_artifacts: - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" - when: always - expire_in: 7 days - paths: - - build/meson-logs - reports: - junit: build/meson-logs/*.junit.xml
diff --git a/.gitlab-ci.d/macos.yml b/.gitlab-ci.d/macos.yml index 84ad249..53b6e6c 100644 --- a/.gitlab-ci.d/macos.yml +++ b/.gitlab-ci.d/macos.yml
@@ -29,7 +29,6 @@ - cd build - ../configure --enable-werror $CONFIGURE_ARGS || { cat config.log meson-logs/meson-log.txt; exit 1; } - $MAKE -j$(sysctl -n hw.ncpu) - - for TARGET in $TEST_BINARIES ; do $MAKE $TARGET ; done - for TARGET in $TEST_TARGETS ; do $MAKE $TARGET ; done aarch64-macos-15-build: @@ -46,8 +45,7 @@ --cross-prefix-i386=i686-elf- --cross-prefix-x86_64=x86_64-elf- --disable-plugins - TEST_BINARIES: qemu-system-aarch64 qemu-system-i386 qemu-system-x86_64 - TEST_TARGETS: check-unit run-tcg-tests-aarch64-softmmu run-tcg-tests-i386-softmmu run-tcg-tests-x86_64-softmmu + TEST_TARGETS: check-unit check-tcg aarch64-macos-26-build: extends: .macos_job_template @@ -64,5 +62,4 @@ --cross-prefix-i386=i686-elf- --cross-prefix-x86_64=x86_64-elf- --disable-plugins - TEST_BINARIES: qemu-system-aarch64 qemu-system-i386 qemu-system-x86_64 - TEST_TARGETS: check-unit run-tcg-tests-aarch64-softmmu run-tcg-tests-i386-softmmu run-tcg-tests-x86_64-softmmu + TEST_TARGETS: check-unit check-tcg
diff --git a/.gitlab/issue_templates/bug.md b/.gitlab/issue_templates/bug.md index e20f586..faeeb00 100644 --- a/.gitlab/issue_templates/bug.md +++ b/.gitlab/issue_templates/bug.md
@@ -13,8 +13,9 @@ See https://www.qemu.org/contribute/report-a-bug/ for additional guidance. -If this is a security issue, please consult -https://www.qemu.org/contribute/security-process/ +If this is a security issue, ensure this ticket is marked 'confidential' +before submission. See https://www.qemu.org/contribute/security-process/ +for additional guidance --> ## Host environment @@ -49,6 +50,12 @@ 2. 3. +<!-- +Note: if this issue was discovered with the assistance of automated +tooling LLM, static analysis, fuzzers), the reporter must disclose +that in the description. The steps to reproduce, and any other findings, +must be fully validated by the user of the tool prior to submission. +--> ## Additional information
diff --git a/MAINTAINERS b/MAINTAINERS index 72357df..3b305d8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS
@@ -256,8 +256,12 @@ F: tests/tcg/hexagon/ F: disas/hexagon.c F: configs/targets/hexagon-linux-user.mak +F: configs/targets/hexagon-softmmu.mak F: tests/docker/dockerfiles/debian-hexagon-cross.docker F: gdbstub/gdb-xml/hexagon*.xml +F: docs/system/target-hexagon.rst +F: docs/system/hexagon/ +F: docs/devel/hexagon-sys.rst T: git https://github.com/qualcomm/qemu.git hex-next Hexagon idef-parser @@ -1341,6 +1345,18 @@ F: roms/seabios-hppa/ F: tests/functional/hppa/ +Hexagon Machines +---------------- +V66G_1024, V68N_1024, sa8775-cdsp0 +M: Brian Cain <brian.cain@oss.qualcomm.com> +R: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com> +S: Supported +F: hw/hexagon/ +F: include/hw/hexagon/ +F: configs/devices/hexagon-softmmu/default.mak +F: docs/system/hexagon/ +F: docs/devel/hexagon-sys.rst + LoongArch Machines ------------------ Virt
diff --git a/accel/accel-irq.c b/accel/accel-irq.c index 7f864e3..830be27 100644 --- a/accel/accel-irq.c +++ b/accel/accel-irq.c
@@ -10,19 +10,18 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "hw/pci/msi.h" #include "system/kvm.h" #include "system/mshv.h" #include "system/accel-irq.h" -int accel_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev) +int accel_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev) { -#ifdef CONFIG_MSHV_IS_POSSIBLE if (mshv_msi_via_irqfd_enabled()) { - return mshv_irqchip_add_msi_route(vector, dev); + return mshv_irqchip_add_msi_route(c, vector, dev); } -#endif if (kvm_enabled()) { return kvm_irqchip_add_msi_route(c, vector, dev); } @@ -31,36 +30,28 @@ int accel_irqchip_update_msi_route(int vector, MSIMessage msg, PCIDevice *dev) { -#ifdef CONFIG_MSHV_IS_POSSIBLE if (mshv_msi_via_irqfd_enabled()) { return mshv_irqchip_update_msi_route(vector, msg, dev); } -#endif if (kvm_enabled()) { return kvm_irqchip_update_msi_route(kvm_state, vector, msg, dev); } return -ENOSYS; } -void accel_irqchip_commit_route_changes(KVMRouteChange *c) +void accel_irqchip_commit_route_changes(AccelRouteChange *c) { -#ifdef CONFIG_MSHV_IS_POSSIBLE - if (mshv_msi_via_irqfd_enabled()) { - mshv_irqchip_commit_routes(); - } -#endif - if (kvm_enabled()) { - kvm_irqchip_commit_route_changes(c); + if (c->changes) { + accel_irqchip_commit_routes(); + c->changes = 0; } } void accel_irqchip_commit_routes(void) { -#ifdef CONFIG_MSHV_IS_POSSIBLE if (mshv_msi_via_irqfd_enabled()) { - mshv_irqchip_commit_routes(); + mshv_irqchip_commit_routes(mshv_state); } -#endif if (kvm_enabled()) { kvm_irqchip_commit_routes(kvm_state); } @@ -68,11 +59,9 @@ void accel_irqchip_release_virq(int virq) { -#ifdef CONFIG_MSHV_IS_POSSIBLE if (mshv_msi_via_irqfd_enabled()) { - mshv_irqchip_release_virq(virq); + mshv_irqchip_release_virq(mshv_state, virq); } -#endif if (kvm_enabled()) { kvm_irqchip_release_virq(kvm_state, virq); } @@ -81,11 +70,9 @@ int accel_irqchip_add_irqfd_notifier_gsi(EventNotifier *n, EventNotifier *rn, int virq) { -#ifdef CONFIG_MSHV_IS_POSSIBLE if (mshv_msi_via_irqfd_enabled()) { return mshv_irqchip_add_irqfd_notifier_gsi(n, rn, virq); } -#endif if (kvm_enabled()) { return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, rn, virq); } @@ -94,13 +81,29 @@ int accel_irqchip_remove_irqfd_notifier_gsi(EventNotifier *n, int virq) { -#ifdef CONFIG_MSHV_IS_POSSIBLE if (mshv_msi_via_irqfd_enabled()) { return mshv_irqchip_remove_irqfd_notifier_gsi(n, virq); } -#endif if (kvm_enabled()) { return kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n, virq); } return -ENOSYS; } + +inline AccelRouteChange accel_irqchip_begin_route_changes(void) +{ + if (mshv_msi_via_irqfd_enabled()) { + return (AccelRouteChange) { + .accel = ACCEL(mshv_state), + .changes = 0, + }; + } + if (kvm_enabled()) { + return (AccelRouteChange) { + .accel = ACCEL(kvm_state), + .changes = 0, + }; + } + error_report("can't initiate route change, no accel irqchip available"); + abort(); +}
diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 8ddfbaf..5d55cb4 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c
@@ -2359,11 +2359,11 @@ return kvm_vm_ioctl(s, KVM_SIGNAL_MSI, &msi); } -int kvm_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev) +int kvm_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev) { struct kvm_irq_routing_entry kroute = {}; int virq; - KVMState *s = c->s; + KVMState *s = KVM_STATE(c->accel); MSIMessage msg = {0, 0}; if (pci_available && dev) { @@ -2506,7 +2506,7 @@ abort(); } -int kvm_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev) +int kvm_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev) { return -ENOSYS; }
diff --git a/accel/mshv/irq.c b/accel/mshv/irq.c index 3c238c3..4828ac5 100644 --- a/accel/mshv/irq.c +++ b/accel/mshv/irq.c
@@ -25,177 +25,6 @@ #define MSHV_IRQFD_RESAMPLE_FLAG (1 << MSHV_IRQFD_BIT_RESAMPLE) #define MSHV_IRQFD_BIT_DEASSIGN_FLAG (1 << MSHV_IRQFD_BIT_DEASSIGN) -static MshvMsiControl *msi_control; -static QemuMutex msi_control_mutex; - -void mshv_init_msicontrol(void) -{ - qemu_mutex_init(&msi_control_mutex); - msi_control = g_new0(MshvMsiControl, 1); - msi_control->gsi_routes = g_hash_table_new(g_direct_hash, g_direct_equal); - msi_control->updated = false; -} - -static int set_msi_routing(uint32_t gsi, uint64_t addr, uint32_t data) -{ - struct mshv_user_irq_entry *entry; - uint32_t high_addr = addr >> 32; - uint32_t low_addr = addr & 0xFFFFFFFF; - GHashTable *gsi_routes; - - trace_mshv_set_msi_routing(gsi, addr, data); - - if (gsi >= MSHV_MAX_MSI_ROUTES) { - error_report("gsi >= MSHV_MAX_MSI_ROUTES"); - return -1; - } - - assert(msi_control); - - WITH_QEMU_LOCK_GUARD(&msi_control_mutex) { - gsi_routes = msi_control->gsi_routes; - entry = g_hash_table_lookup(gsi_routes, GINT_TO_POINTER(gsi)); - - if (entry - && entry->address_hi == high_addr - && entry->address_lo == low_addr - && entry->data == data) - { - /* nothing to update */ - return 0; - } - - /* free old entry */ - g_free(entry); - - /* create new entry */ - entry = g_new0(struct mshv_user_irq_entry, 1); - entry->gsi = gsi; - entry->address_hi = high_addr; - entry->address_lo = low_addr; - entry->data = data; - - g_hash_table_insert(gsi_routes, GINT_TO_POINTER(gsi), entry); - msi_control->updated = true; - } - - return 0; -} - -static int add_msi_routing(uint64_t addr, uint32_t data) -{ - struct mshv_user_irq_entry *route_entry; - uint32_t high_addr = addr >> 32; - uint32_t low_addr = addr & 0xFFFFFFFF; - int gsi; - GHashTable *gsi_routes; - - trace_mshv_add_msi_routing(addr, data); - - assert(msi_control); - - WITH_QEMU_LOCK_GUARD(&msi_control_mutex) { - /* find an empty slot */ - gsi = 0; - gsi_routes = msi_control->gsi_routes; - while (gsi < MSHV_MAX_MSI_ROUTES) { - route_entry = g_hash_table_lookup(gsi_routes, GINT_TO_POINTER(gsi)); - if (!route_entry) { - break; - } - gsi++; - } - if (gsi >= MSHV_MAX_MSI_ROUTES) { - error_report("No empty gsi slot available"); - return -1; - } - - /* create new entry */ - route_entry = g_new0(struct mshv_user_irq_entry, 1); - route_entry->gsi = gsi; - route_entry->address_hi = high_addr; - route_entry->address_lo = low_addr; - route_entry->data = data; - - g_hash_table_insert(gsi_routes, GINT_TO_POINTER(gsi), route_entry); - msi_control->updated = true; - } - - return gsi; -} - -static int commit_msi_routing_table(int vm_fd) -{ - guint len; - int i, ret; - size_t table_size; - struct mshv_user_irq_table *table; - GHashTableIter iter; - gpointer key, value; - - assert(msi_control); - - WITH_QEMU_LOCK_GUARD(&msi_control_mutex) { - if (!msi_control->updated) { - /* nothing to update */ - return 0; - } - - /* Calculate the size of the table */ - len = g_hash_table_size(msi_control->gsi_routes); - table_size = sizeof(struct mshv_user_irq_table) - + len * sizeof(struct mshv_user_irq_entry); - table = g_malloc0(table_size); - - g_hash_table_iter_init(&iter, msi_control->gsi_routes); - i = 0; - while (g_hash_table_iter_next(&iter, &key, &value)) { - struct mshv_user_irq_entry *entry = value; - table->entries[i] = *entry; - i++; - } - table->nr = i; - - trace_mshv_commit_msi_routing_table(vm_fd, len); - - ret = ioctl(vm_fd, MSHV_SET_MSI_ROUTING, table); - g_free(table); - if (ret < 0) { - error_report("Failed to commit msi routing table"); - return -1; - } - msi_control->updated = false; - } - return 0; -} - -static int remove_msi_routing(uint32_t gsi) -{ - struct mshv_user_irq_entry *route_entry; - GHashTable *gsi_routes; - - trace_mshv_remove_msi_routing(gsi); - - if (gsi >= MSHV_MAX_MSI_ROUTES) { - error_report("Invalid GSI: %u", gsi); - return -1; - } - - assert(msi_control); - - WITH_QEMU_LOCK_GUARD(&msi_control_mutex) { - gsi_routes = msi_control->gsi_routes; - route_entry = g_hash_table_lookup(gsi_routes, GINT_TO_POINTER(gsi)); - if (route_entry) { - g_hash_table_remove(gsi_routes, GINT_TO_POINTER(gsi)); - g_free(route_entry); - msi_control->updated = true; - } - } - - return 0; -} - /* Pass an eventfd which is to be used for injecting interrupts from userland */ static int irqfd(int vm_fd, int fd, int resample_fd, uint32_t gsi, uint32_t flags) @@ -278,35 +107,155 @@ return register_irqfd(vm_fd, fd, virq); } - -int mshv_irqchip_add_msi_route(int vector, PCIDevice *dev) +static int irqchip_allocate_gsi(MshvState *s, int *gsi) { - MSIMessage msg = { 0, 0 }; - int virq = 0; + int next_gsi; - if (pci_available && dev) { - msg = pci_get_msi_message(dev, vector); - virq = add_msi_routing(msg.address, le32_to_cpu(msg.data)); + /* Return the lowest unused GSI in the bitmap */ + next_gsi = find_first_zero_bit(s->used_gsi_bitmap, s->gsi_count); + if (next_gsi >= s->gsi_count) { + return -ENOSPC; } - return virq; + *gsi = next_gsi; + + return 0; } -void mshv_irqchip_release_virq(int virq) +static void irqchip_release_gsi(MshvState *s, int gsi) { - remove_msi_routing(virq); + clear_bit(gsi, s->used_gsi_bitmap); +} + +static void add_routing_entry(MshvState *s, struct mshv_user_irq_entry *entry) +{ + struct mshv_user_irq_entry *new; + int n, size; + + if (s->irq_routes->nr == s->nr_allocated_irq_routes) { + n = s->nr_allocated_irq_routes * 2; + if (n < MSHV_MIN_ALLOCATED_MSI_ROUTES) { + n = MSHV_MIN_ALLOCATED_MSI_ROUTES; + } + size = sizeof(struct mshv_user_irq_table); + size += n * sizeof(*new); + s->irq_routes = g_realloc(s->irq_routes, size); + s->nr_allocated_irq_routes = n; + } + + n = s->irq_routes->nr; + s->irq_routes->nr++; + new = &s->irq_routes->entries[n]; + + *new = *entry; + + set_bit(entry->gsi, s->used_gsi_bitmap); + + trace_mshv_add_msi_routing(entry->address_lo | entry->address_hi, + entry->data); +} + +int mshv_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev) +{ + struct mshv_user_irq_entry entry = { 0 }; + MSIMessage msg = { 0 }; + uint32_t data, high_addr, low_addr; + int gsi, ret; + MshvState *s = MSHV_STATE(c->accel); + + if (!pci_available || !dev) { + return 0; + } + + msg = pci_get_msi_message(dev, vector); + + ret = irqchip_allocate_gsi(mshv_state, &gsi); + if (ret < 0) { + error_report("Could not allocate GSI for MSI route"); + return -1; + } + high_addr = msg.address >> 32; + low_addr = msg.address & 0xFFFFFFFF; + data = le32_to_cpu(msg.data); + + entry.gsi = gsi; + entry.address_hi = high_addr; + entry.address_lo = low_addr; + entry.data = data; + + if (s->irq_routes->nr < s->gsi_count) { + add_routing_entry(s, &entry); + c->changes++; + } else { + irqchip_release_gsi(s, gsi); + return -ENOSPC; + } + + return gsi; +} + +void mshv_irqchip_release_virq(MshvState *s, int virq) +{ + struct mshv_user_irq_entry *e; + int i; + + for (i = 0; i < s->irq_routes->nr; i++) { + e = &s->irq_routes->entries[i]; + if (e->gsi == virq) { + s->irq_routes->nr--; + *e = s->irq_routes->entries[s->irq_routes->nr]; + } + } + irqchip_release_gsi(s, virq); + + trace_mshv_remove_msi_routing(virq); +} + +static int update_routing_entry(MshvState *s, + struct mshv_user_irq_entry *new_entry) +{ + struct mshv_user_irq_entry *entry; + int n; + + for (n = 0; n < s->irq_routes->nr; n++) { + entry = &s->irq_routes->entries[n]; + if (entry->gsi != new_entry->gsi) { + continue; + } + + if (!memcmp(entry, new_entry, sizeof *entry)) { + return 0; + } + + *entry = *new_entry; + + return 0; + } + + return -ESRCH; } int mshv_irqchip_update_msi_route(int virq, MSIMessage msg, PCIDevice *dev) { + uint32_t addr_hi = msg.address >> 32; + uint32_t addr_lo = msg.address & 0xFFFFFFFF; + uint32_t data = le32_to_cpu(msg.data); + struct mshv_user_irq_entry entry = { + .gsi = virq, + .address_hi = addr_hi, + .address_lo = addr_lo, + .data = data, + }; int ret; - ret = set_msi_routing(virq, msg.address, le32_to_cpu(msg.data)); + ret = update_routing_entry(mshv_state, &entry); if (ret < 0) { - error_report("Failed to set msi routing"); - return -1; + error_report("Failed to set msi routing for gsi %d", virq); + abort(); } + trace_mshv_set_msi_routing(virq, msg.address, data); + return 0; } @@ -347,16 +296,17 @@ return 0; } -void mshv_irqchip_commit_routes(void) +void mshv_irqchip_commit_routes(MshvState *s) { int ret; - int vm_fd = mshv_state->vm; + int vm_fd = s->vm; - ret = commit_msi_routing_table(vm_fd); + ret = ioctl(vm_fd, MSHV_SET_MSI_ROUTING, s->irq_routes); if (ret < 0) { error_report("Failed to commit msi routing table"); abort(); } + trace_mshv_commit_msi_routing_table(vm_fd, s->irq_routes->nr); } int mshv_irqchip_add_irqfd_notifier_gsi(const EventNotifier *event, @@ -372,27 +322,45 @@ return irqchip_update_irqfd_notifier_gsi(event, NULL, virq, false); } -int mshv_reserve_ioapic_msi_routes(int vm_fd) +static int mshv_reserve_ioapic_msi_routes(MshvState *s) { - int ret, gsi; + int ret, i; + int gsi = 0; + struct mshv_user_irq_entry blank_entry = { 0 }; /* * Reserve GSI 0-23 for IOAPIC pins, to avoid conflicts of legacy * peripherals with MSI-X devices */ - for (gsi = 0; gsi < IOAPIC_NUM_PINS; gsi++) { - ret = add_msi_routing(0, 0); + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + /* ret = add_msi_routing(0, 0); */ + ret = irqchip_allocate_gsi(s, &gsi); if (ret < 0) { - error_report("Failed to reserve GSI %d", gsi); + error_report("Failed to reserve GSI %d: %s", gsi, strerror(-ret)); return -1; } + blank_entry.gsi = gsi; + add_routing_entry(s, &blank_entry); } - ret = commit_msi_routing_table(vm_fd); - if (ret < 0) { - error_report("Failed to commit reserved IOAPIC MSI routes"); - return -1; - } + mshv_irqchip_commit_routes(s); return 0; } + +void mshv_init_irq_routing(MshvState *s) +{ + int ret; + int gsi_count = MSHV_MAX_MSI_ROUTES; + + s->irq_routes = g_malloc0(sizeof(*s->irq_routes)); + s->nr_allocated_irq_routes = 0; + s->gsi_count = gsi_count; + s->used_gsi_bitmap = bitmap_new(gsi_count); + + ret = mshv_reserve_ioapic_msi_routes(s); + if (ret < 0) { + error_report("Failed to reserve IOAPIC MSI routes"); + abort(); + } +}
diff --git a/accel/mshv/mem.c b/accel/mshv/mem.c index e55c38d..820f87e 100644 --- a/accel/mshv/mem.c +++ b/accel/mshv/mem.c
@@ -12,10 +12,13 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" +#include "qapi/error.h" #include "linux/mshv.h" #include "system/address-spaces.h" #include "system/mshv.h" #include "system/mshv_int.h" +#include "hw/hyperv/hvhdk_mini.h" +#include "system/physmem.h" #include "exec/memattrs.h" #include <sys/ioctl.h> #include "trace.h" @@ -211,3 +214,211 @@ abort(); } } + +static int enable_dirty_page_tracking(int vm_fd) +{ + int ret; + struct hv_input_set_partition_property in = {0}; + struct mshv_root_hvcall args = {0}; + + in.property_code = HV_PARTITION_PROPERTY_GPA_PAGE_ACCESS_TRACKING; + in.property_value = 1; + + args.code = HVCALL_SET_PARTITION_PROPERTY; + args.in_sz = sizeof(in); + args.in_ptr = (uint64_t)∈ + + ret = mshv_hvcall(vm_fd, &args); + if (ret < 0) { + error_report("Failed to enable dirty page tracking: %s", + strerror(errno)); + return -1; + } + + return 0; +} + +/* + * Retrieve dirty page bitmap for a GPA range, clearing the dirty bits + * atomically. Large ranges are handled in batches. + */ +static int get_dirty_log(int vm_fd, uint64_t base_pfn, uint64_t page_count, + unsigned long *bitmap, size_t bitmap_size) +{ + uint64_t batch, bitmap_offset, completed = 0; + struct mshv_gpap_access_bitmap args = {0}; + int ret; + + QEMU_BUILD_BUG_ON(MSHV_DIRTY_PAGES_BATCH_SIZE % BITS_PER_LONG != 0); + assert(bitmap_size >= ROUND_UP(page_count, BITS_PER_LONG) / 8); + + while (completed < page_count) { + batch = MIN(MSHV_DIRTY_PAGES_BATCH_SIZE, page_count - completed); + bitmap_offset = completed / BITS_PER_LONG; + + args.access_type = MSHV_GPAP_ACCESS_TYPE_DIRTY; + args.access_op = MSHV_GPAP_ACCESS_OP_CLEAR; + args.page_count = batch; + args.gpap_base = base_pfn + completed; + args.bitmap_ptr = (uint64_t)(bitmap + bitmap_offset); + + ret = ioctl(vm_fd, MSHV_GET_GPAP_ACCESS_BITMAP, &args); + if (ret < 0) { + error_report("Failed to get dirty log (base_pfn=0x%" PRIx64 + " batch=%" PRIu64 "): %s", + base_pfn + completed, batch, strerror(errno)); + return -1; + } + completed += batch; + } + + return 0; +} + +bool mshv_log_global_start(MemoryListener *listener, Error **errp) +{ + int ret; + + ret = enable_dirty_page_tracking(mshv_state->vm); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to enable dirty page tracking"); + return false; + } + return true; +} + +static int disable_dirty_page_tracking(int vm_fd) +{ + int ret; + struct hv_input_set_partition_property in = {0}; + struct mshv_root_hvcall args = {0}; + + in.property_code = HV_PARTITION_PROPERTY_GPA_PAGE_ACCESS_TRACKING; + in.property_value = 0; + + args.code = HVCALL_SET_PARTITION_PROPERTY; + args.in_sz = sizeof(in); + args.in_ptr = (uint64_t)∈ + + ret = mshv_hvcall(vm_fd, &args); + if (ret < 0) { + error_report("Failed to disable dirty page tracking: %s", + strerror(errno)); + return -1; + } + + return 0; +} + +static int set_dirty_pages(int vm_fd, uint64_t base_pfn, uint64_t page_count) +{ + uint64_t batch, completed = 0; + unsigned long bitmap[MSHV_DIRTY_PAGES_BATCH_SIZE / BITS_PER_LONG]; + struct mshv_gpap_access_bitmap args = {0}; + int ret; + + while (completed < page_count) { + batch = MIN(MSHV_DIRTY_PAGES_BATCH_SIZE, page_count - completed); + + args.access_type = MSHV_GPAP_ACCESS_TYPE_DIRTY; + args.access_op = MSHV_GPAP_ACCESS_OP_SET; + args.page_count = batch; + args.gpap_base = base_pfn + completed; + args.bitmap_ptr = (uint64_t)bitmap; + + ret = ioctl(vm_fd, MSHV_GET_GPAP_ACCESS_BITMAP, &args); + if (ret < 0) { + error_report("Failed to set dirty pages (base_pfn=0x%" PRIx64 + " batch=%" PRIu64 "): %s", + base_pfn + completed, batch, strerror(errno)); + return -1; + } + completed += batch; + } + + return 0; +} + +static bool set_dirty_bits_cb(Int128 start, Int128 len, const MemoryRegion *mr, + hwaddr offset_in_region, void *opaque) +{ + int ret, *errp = opaque; + hwaddr gpa, size; + uint64_t page_count, base_pfn; + + gpa = int128_get64(start); + size = int128_get64(len); + page_count = size >> MSHV_PAGE_SHIFT; + base_pfn = gpa >> MSHV_PAGE_SHIFT; + + if (!mr->ram || mr->readonly) { + return false; + } + + if (page_count == 0) { + return false; + } + + ret = set_dirty_pages(mshv_state->vm, base_pfn, page_count); + + /* true aborts the iteration, which is what we want if there's an error */ + if (ret < 0) { + *errp = ret; + return true; + } + + return false; +} + +void mshv_log_global_stop(MemoryListener *listener) +{ + int err = 0; + /* MSHV requires all dirty bits to be set before disabling tracking. */ + FlatView *fv = address_space_to_flatview(&address_space_memory); + flatview_for_each_range(fv, set_dirty_bits_cb, &err); + + if (err < 0) { + error_report("Failed to set dirty bits before disabling tracking"); + } + + disable_dirty_page_tracking(mshv_state->vm); +} + +void mshv_log_sync(MemoryListener *listener, MemoryRegionSection *section) +{ + hwaddr size, start_addr, mr_offset; + uint64_t page_count, base_pfn; + size_t bitmap_size; + unsigned long *bitmap; + ram_addr_t ram_addr; + int ret; + MemoryRegion *mr = section->mr; + + if (!memory_region_is_ram(mr) || memory_region_is_rom(mr)) { + return; + } + + size = align_section(section, &start_addr); + if (!size) { + return; + } + + page_count = size >> MSHV_PAGE_SHIFT; + base_pfn = start_addr >> MSHV_PAGE_SHIFT; + bitmap_size = ROUND_UP(page_count, BITS_PER_LONG) / 8; + bitmap = g_malloc0(bitmap_size); + + ret = get_dirty_log(mshv_state->vm, base_pfn, page_count, bitmap, + bitmap_size); + if (ret < 0) { + g_free(bitmap); + return; + } + + mr_offset = section->offset_within_region + start_addr - + section->offset_within_address_space; + ram_addr = memory_region_get_ram_addr(mr) + mr_offset; + + physical_memory_set_dirty_lebitmap(bitmap, ram_addr, page_count); + g_free(bitmap); +}
diff --git a/accel/mshv/meson.build b/accel/mshv/meson.build index c1b1787..e433187 100644 --- a/accel/mshv/meson.build +++ b/accel/mshv/meson.build
@@ -1,6 +1,5 @@ system_ss.add(when: 'CONFIG_MSHV', if_true: files( 'irq.c', 'mem.c', - 'msr.c', 'mshv-all.c' ))
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c index 58d8638..9452504 100644 --- a/accel/mshv/mshv-all.c +++ b/accel/mshv/mshv-all.c
@@ -43,10 +43,6 @@ #include <err.h> #include <sys/ioctl.h> -#define TYPE_MSHV_ACCEL ACCEL_CLASS_NAME("mshv") - -DECLARE_INSTANCE_CHECKER(MshvState, MSHV_STATE, TYPE_MSHV_ACCEL) - bool mshv_allowed; MshvState *mshv_state; @@ -110,21 +106,136 @@ return 0; } +static int get_host_partition_property(int mshv_fd, uint32_t property_code, + uint64_t *value) +{ + int ret; + struct hv_input_get_partition_property in = {0}; + struct hv_output_get_partition_property out = {0}; + struct mshv_root_hvcall args = {0}; + + in.property_code = property_code; + + args.code = HVCALL_GET_PARTITION_PROPERTY; + args.in_sz = sizeof(in); + args.in_ptr = (uint64_t)∈ + args.out_sz = sizeof(out); + args.out_ptr = (uint64_t)&out; + + ret = ioctl(mshv_fd, MSHV_ROOT_HVCALL, &args); + if (ret < 0) { + error_report("Failed to get host partition property bank: %s", + strerror(errno)); + return -1; + } + + *value = out.property_value; + return 0; +} + +static int get_partition_property(int vm_fd, uint32_t feature_bank, + uint64_t *value) +{ + struct hv_input_get_partition_property in = {0}; + struct hv_output_get_partition_property out = {0}; + struct mshv_root_hvcall args = {0}; + int ret; + + in.property_code = feature_bank; + + args.code = HVCALL_GET_PARTITION_PROPERTY; + args.in_sz = sizeof(in); + args.in_ptr = (uint64_t)∈ + args.out_sz = sizeof(out); + args.out_ptr = (uint64_t)&out; + + ret = ioctl(vm_fd, MSHV_ROOT_HVCALL, &args); + if (ret < 0) { + error_report("Failed to get guest partition property bank: %s", + strerror(errno)); + return -1; + } + + *value = out.property_value; + return 0; +} + +static int get_proc_features(int vm_fd, + union hv_partition_processor_features *features) +{ + int ret; + + ret = get_partition_property(vm_fd, + HV_PARTITION_PROPERTY_PROCESSOR_FEATURES0, + features[0].as_uint64); + if (ret < 0) { + error_report("Failed to get processor features bank 0"); + return -1; + } + + ret = get_partition_property(vm_fd, + HV_PARTITION_PROPERTY_PROCESSOR_FEATURES1, + features[1].as_uint64); + if (ret < 0) { + error_report("Failed to get processor features bank 1"); + return -1; + } + + return 0; +} + static int create_partition(int mshv_fd, int *vm_fd) { int ret; - struct mshv_create_partition args = {0}; + uint64_t pt_flags, host_proc_features; + union hv_partition_processor_xsave_features disabled_xsave_features; + union hv_partition_processor_features disabled_partition_features = {0}; + + struct mshv_create_partition_v2 args = {0}; + + QEMU_BUILD_BUG_ON(MSHV_NUM_CPU_FEATURES_BANKS != 2); /* Initialize pt_flags with the desired features */ - uint64_t pt_flags = (1ULL << MSHV_PT_BIT_LAPIC) | - (1ULL << MSHV_PT_BIT_X2APIC) | - (1ULL << MSHV_PT_BIT_GPA_SUPER_PAGES); + pt_flags = (1ULL << MSHV_PT_BIT_LAPIC) | + (1ULL << MSHV_PT_BIT_X2APIC) | + (1ULL << MSHV_PT_BIT_GPA_SUPER_PAGES) | + (1ULL << MSHV_PT_BIT_CPU_AND_XSAVE_FEATURES); - /* Set default isolation type */ - uint64_t pt_isolation = MSHV_PT_ISOLATION_NONE; + /* enable all */ + disabled_xsave_features.as_uint64 = 0; + /* + * query host for supported processor features and disable unsupported + * features: (0 means supported, 1 means disabled, hence the negation) + */ + ret = get_host_partition_property(mshv_fd, + HV_PARTITION_PROPERTY_PROCESSOR_FEATURES0, + &host_proc_features); + if (ret < 0) { + error_report("Failed to get host processor feature bank 0"); + return -1; + } + args.pt_cpu_fbanks[0] = ~host_proc_features; + + ret = get_host_partition_property(mshv_fd, + HV_PARTITION_PROPERTY_PROCESSOR_FEATURES1, + &host_proc_features); + if (ret < 0) { + error_report("Failed to get host processor feature bank 1"); + return -1; + } + args.pt_cpu_fbanks[1] = ~host_proc_features; + + /* arch-specific features we disable regardless of host support */ + mshv_arch_disable_partition_proc_features(&disabled_partition_features); + args.pt_cpu_fbanks[0] |= disabled_partition_features.as_uint64[0]; + args.pt_cpu_fbanks[1] |= disabled_partition_features.as_uint64[1]; + + /* populate args structure */ args.pt_flags = pt_flags; - args.pt_isolation = pt_isolation; + args.pt_isolation = MSHV_PT_ISOLATION_NONE; + args.pt_disabled_xsave = disabled_xsave_features.as_uint64; + args.pt_num_cpu_fbanks = MSHV_NUM_CPU_FEATURES_BANKS; ret = ioctl(mshv_fd, MSHV_CREATE_PARTITION, &args); if (ret < 0) { @@ -204,11 +315,6 @@ return -1; } - ret = mshv_reserve_ioapic_msi_routes(*vm_fd); - if (ret < 0) { - return -1; - } - ret = mshv_arch_post_init_vm(*vm_fd); if (ret < 0) { return -1; @@ -371,6 +477,9 @@ .region_del = mem_region_del, .eventfd_add = mem_ioeventfd_add, .eventfd_del = mem_ioeventfd_del, + .log_sync = mshv_log_sync, + .log_global_start = mshv_log_global_start, + .log_global_stop = mshv_log_global_stop, }; static MemoryListener mshv_io_listener = { @@ -415,13 +524,13 @@ int ret; cpu->accel = g_new0(AccelCPUState, 1); - mshv_arch_init_vcpu(cpu); ret = mshv_create_vcpu(vm_fd, vp_index, &cpu->accel->cpufd); if (ret < 0) { return -1; } + mshv_arch_init_vcpu(cpu); cpu->accel->dirty = true; return 0; @@ -450,8 +559,6 @@ mshv_init_mmio_emu(); - mshv_init_msicontrol(); - ret = create_vm(mshv_fd, &vm_fd); if (ret < 0) { close(mshv_fd); @@ -467,11 +574,19 @@ s->vm = vm_fd; s->fd = mshv_fd; + + ret = get_proc_features(vm_fd, &s->processor_features); + if (ret < 0) { + return -1; + } + s->nr_as = 1; s->as = g_new0(MshvAddressSpace, s->nr_as); mshv_state = s; + mshv_init_irq_routing(s); + register_mshv_memory_listener(s, &s->memory_listener, &address_space_memory, 0, "mshv-memory"); memory_listener_register(&mshv_io_listener, &address_space_io); @@ -503,7 +618,7 @@ do { if (cpu->accel->dirty) { - ret = mshv_arch_put_registers(cpu); + ret = mshv_arch_store_vcpu_state(cpu); if (ret) { error_report("Failed to put registers after init: %s", strerror(-ret)); @@ -625,7 +740,7 @@ static void do_mshv_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg) { - int ret = mshv_arch_put_registers(cpu); + int ret = mshv_arch_store_vcpu_state(cpu); if (ret < 0) { error_report("Failed to put registers after init: %s", strerror(-ret)); abort(); @@ -641,7 +756,7 @@ static void mshv_cpu_synchronize_post_reset(CPUState *cpu) { - int ret = mshv_arch_put_registers(cpu); + int ret = mshv_arch_store_vcpu_state(cpu); if (ret) { error_report("Failed to put registers after reset: %s", strerror(-ret)); @@ -665,7 +780,7 @@ static void do_mshv_cpu_synchronize(CPUState *cpu, run_on_cpu_data arg) { if (!cpu->accel->dirty) { - int ret = mshv_load_regs(cpu); + int ret = mshv_arch_load_vcpu_state(cpu); if (ret < 0) { error_report("Failed to load registers for vcpu %d", cpu->cpu_index);
diff --git a/accel/mshv/msr.c b/accel/mshv/msr.c deleted file mode 100644 index e6e5bae..0000000 --- a/accel/mshv/msr.c +++ /dev/null
@@ -1,375 +0,0 @@ -/* - * QEMU MSHV support - * - * Copyright Microsoft, Corp. 2025 - * - * Authors: Magnus Kulke <magnuskulke@microsoft.com> - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#include "qemu/osdep.h" -#include "system/mshv.h" -#include "system/mshv_int.h" -#include "hw/hyperv/hvgdk_mini.h" -#include "linux/mshv.h" -#include "qemu/error-report.h" - -static uint32_t supported_msrs[64] = { - IA32_MSR_TSC, - IA32_MSR_EFER, - IA32_MSR_KERNEL_GS_BASE, - IA32_MSR_APIC_BASE, - IA32_MSR_PAT, - IA32_MSR_SYSENTER_CS, - IA32_MSR_SYSENTER_ESP, - IA32_MSR_SYSENTER_EIP, - IA32_MSR_STAR, - IA32_MSR_LSTAR, - IA32_MSR_CSTAR, - IA32_MSR_SFMASK, - IA32_MSR_MTRR_DEF_TYPE, - IA32_MSR_MTRR_PHYSBASE0, - IA32_MSR_MTRR_PHYSMASK0, - IA32_MSR_MTRR_PHYSBASE1, - IA32_MSR_MTRR_PHYSMASK1, - IA32_MSR_MTRR_PHYSBASE2, - IA32_MSR_MTRR_PHYSMASK2, - IA32_MSR_MTRR_PHYSBASE3, - IA32_MSR_MTRR_PHYSMASK3, - IA32_MSR_MTRR_PHYSBASE4, - IA32_MSR_MTRR_PHYSMASK4, - IA32_MSR_MTRR_PHYSBASE5, - IA32_MSR_MTRR_PHYSMASK5, - IA32_MSR_MTRR_PHYSBASE6, - IA32_MSR_MTRR_PHYSMASK6, - IA32_MSR_MTRR_PHYSBASE7, - IA32_MSR_MTRR_PHYSMASK7, - IA32_MSR_MTRR_FIX64K_00000, - IA32_MSR_MTRR_FIX16K_80000, - IA32_MSR_MTRR_FIX16K_A0000, - IA32_MSR_MTRR_FIX4K_C0000, - IA32_MSR_MTRR_FIX4K_C8000, - IA32_MSR_MTRR_FIX4K_D0000, - IA32_MSR_MTRR_FIX4K_D8000, - IA32_MSR_MTRR_FIX4K_E0000, - IA32_MSR_MTRR_FIX4K_E8000, - IA32_MSR_MTRR_FIX4K_F0000, - IA32_MSR_MTRR_FIX4K_F8000, - IA32_MSR_TSC_AUX, - IA32_MSR_DEBUG_CTL, - HV_X64_MSR_GUEST_OS_ID, - HV_X64_MSR_SINT0, - HV_X64_MSR_SINT1, - HV_X64_MSR_SINT2, - HV_X64_MSR_SINT3, - HV_X64_MSR_SINT4, - HV_X64_MSR_SINT5, - HV_X64_MSR_SINT6, - HV_X64_MSR_SINT7, - HV_X64_MSR_SINT8, - HV_X64_MSR_SINT9, - HV_X64_MSR_SINT10, - HV_X64_MSR_SINT11, - HV_X64_MSR_SINT12, - HV_X64_MSR_SINT13, - HV_X64_MSR_SINT14, - HV_X64_MSR_SINT15, - HV_X64_MSR_SCONTROL, - HV_X64_MSR_SIEFP, - HV_X64_MSR_SIMP, - HV_X64_MSR_REFERENCE_TSC, - HV_X64_MSR_EOM, -}; -static const size_t msr_count = ARRAY_SIZE(supported_msrs); - -static int compare_msr_index(const void *a, const void *b) -{ - return *(uint32_t *)a - *(uint32_t *)b; -} - -__attribute__((constructor)) -static void init_sorted_msr_map(void) -{ - qsort(supported_msrs, msr_count, sizeof(uint32_t), compare_msr_index); -} - -static int mshv_is_supported_msr(uint32_t msr) -{ - return bsearch(&msr, supported_msrs, msr_count, sizeof(uint32_t), - compare_msr_index) != NULL; -} - -static int mshv_msr_to_hv_reg_name(uint32_t msr, uint32_t *hv_reg) -{ - switch (msr) { - case IA32_MSR_TSC: - *hv_reg = HV_X64_REGISTER_TSC; - return 0; - case IA32_MSR_EFER: - *hv_reg = HV_X64_REGISTER_EFER; - return 0; - case IA32_MSR_KERNEL_GS_BASE: - *hv_reg = HV_X64_REGISTER_KERNEL_GS_BASE; - return 0; - case IA32_MSR_APIC_BASE: - *hv_reg = HV_X64_REGISTER_APIC_BASE; - return 0; - case IA32_MSR_PAT: - *hv_reg = HV_X64_REGISTER_PAT; - return 0; - case IA32_MSR_SYSENTER_CS: - *hv_reg = HV_X64_REGISTER_SYSENTER_CS; - return 0; - case IA32_MSR_SYSENTER_ESP: - *hv_reg = HV_X64_REGISTER_SYSENTER_ESP; - return 0; - case IA32_MSR_SYSENTER_EIP: - *hv_reg = HV_X64_REGISTER_SYSENTER_EIP; - return 0; - case IA32_MSR_STAR: - *hv_reg = HV_X64_REGISTER_STAR; - return 0; - case IA32_MSR_LSTAR: - *hv_reg = HV_X64_REGISTER_LSTAR; - return 0; - case IA32_MSR_CSTAR: - *hv_reg = HV_X64_REGISTER_CSTAR; - return 0; - case IA32_MSR_SFMASK: - *hv_reg = HV_X64_REGISTER_SFMASK; - return 0; - case IA32_MSR_MTRR_CAP: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_CAP; - return 0; - case IA32_MSR_MTRR_DEF_TYPE: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_DEF_TYPE; - return 0; - case IA32_MSR_MTRR_PHYSBASE0: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE0; - return 0; - case IA32_MSR_MTRR_PHYSMASK0: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK0; - return 0; - case IA32_MSR_MTRR_PHYSBASE1: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE1; - return 0; - case IA32_MSR_MTRR_PHYSMASK1: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK1; - return 0; - case IA32_MSR_MTRR_PHYSBASE2: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE2; - return 0; - case IA32_MSR_MTRR_PHYSMASK2: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK2; - return 0; - case IA32_MSR_MTRR_PHYSBASE3: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE3; - return 0; - case IA32_MSR_MTRR_PHYSMASK3: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK3; - return 0; - case IA32_MSR_MTRR_PHYSBASE4: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE4; - return 0; - case IA32_MSR_MTRR_PHYSMASK4: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK4; - return 0; - case IA32_MSR_MTRR_PHYSBASE5: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE5; - return 0; - case IA32_MSR_MTRR_PHYSMASK5: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK5; - return 0; - case IA32_MSR_MTRR_PHYSBASE6: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE6; - return 0; - case IA32_MSR_MTRR_PHYSMASK6: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK6; - return 0; - case IA32_MSR_MTRR_PHYSBASE7: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE7; - return 0; - case IA32_MSR_MTRR_PHYSMASK7: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK7; - return 0; - case IA32_MSR_MTRR_FIX64K_00000: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX64K00000; - return 0; - case IA32_MSR_MTRR_FIX16K_80000: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX16K80000; - return 0; - case IA32_MSR_MTRR_FIX16K_A0000: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX16KA0000; - return 0; - case IA32_MSR_MTRR_FIX4K_C0000: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KC0000; - return 0; - case IA32_MSR_MTRR_FIX4K_C8000: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KC8000; - return 0; - case IA32_MSR_MTRR_FIX4K_D0000: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KD0000; - return 0; - case IA32_MSR_MTRR_FIX4K_D8000: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KD8000; - return 0; - case IA32_MSR_MTRR_FIX4K_E0000: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KE0000; - return 0; - case IA32_MSR_MTRR_FIX4K_E8000: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KE8000; - return 0; - case IA32_MSR_MTRR_FIX4K_F0000: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KF0000; - return 0; - case IA32_MSR_MTRR_FIX4K_F8000: - *hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KF8000; - return 0; - case IA32_MSR_TSC_AUX: - *hv_reg = HV_X64_REGISTER_TSC_AUX; - return 0; - case IA32_MSR_BNDCFGS: - *hv_reg = HV_X64_REGISTER_BNDCFGS; - return 0; - case IA32_MSR_DEBUG_CTL: - *hv_reg = HV_X64_REGISTER_DEBUG_CTL; - return 0; - case IA32_MSR_TSC_ADJUST: - *hv_reg = HV_X64_REGISTER_TSC_ADJUST; - return 0; - case IA32_MSR_SPEC_CTRL: - *hv_reg = HV_X64_REGISTER_SPEC_CTRL; - return 0; - case HV_X64_MSR_GUEST_OS_ID: - *hv_reg = HV_REGISTER_GUEST_OS_ID; - return 0; - case HV_X64_MSR_SINT0: - *hv_reg = HV_REGISTER_SINT0; - return 0; - case HV_X64_MSR_SINT1: - *hv_reg = HV_REGISTER_SINT1; - return 0; - case HV_X64_MSR_SINT2: - *hv_reg = HV_REGISTER_SINT2; - return 0; - case HV_X64_MSR_SINT3: - *hv_reg = HV_REGISTER_SINT3; - return 0; - case HV_X64_MSR_SINT4: - *hv_reg = HV_REGISTER_SINT4; - return 0; - case HV_X64_MSR_SINT5: - *hv_reg = HV_REGISTER_SINT5; - return 0; - case HV_X64_MSR_SINT6: - *hv_reg = HV_REGISTER_SINT6; - return 0; - case HV_X64_MSR_SINT7: - *hv_reg = HV_REGISTER_SINT7; - return 0; - case HV_X64_MSR_SINT8: - *hv_reg = HV_REGISTER_SINT8; - return 0; - case HV_X64_MSR_SINT9: - *hv_reg = HV_REGISTER_SINT9; - return 0; - case HV_X64_MSR_SINT10: - *hv_reg = HV_REGISTER_SINT10; - return 0; - case HV_X64_MSR_SINT11: - *hv_reg = HV_REGISTER_SINT11; - return 0; - case HV_X64_MSR_SINT12: - *hv_reg = HV_REGISTER_SINT12; - return 0; - case HV_X64_MSR_SINT13: - *hv_reg = HV_REGISTER_SINT13; - return 0; - case HV_X64_MSR_SINT14: - *hv_reg = HV_REGISTER_SINT14; - return 0; - case HV_X64_MSR_SINT15: - *hv_reg = HV_REGISTER_SINT15; - return 0; - case IA32_MSR_MISC_ENABLE: - *hv_reg = HV_X64_REGISTER_MSR_IA32_MISC_ENABLE; - return 0; - case HV_X64_MSR_SCONTROL: - *hv_reg = HV_REGISTER_SCONTROL; - return 0; - case HV_X64_MSR_SIEFP: - *hv_reg = HV_REGISTER_SIEFP; - return 0; - case HV_X64_MSR_SIMP: - *hv_reg = HV_REGISTER_SIMP; - return 0; - case HV_X64_MSR_REFERENCE_TSC: - *hv_reg = HV_REGISTER_REFERENCE_TSC; - return 0; - case HV_X64_MSR_EOM: - *hv_reg = HV_REGISTER_EOM; - return 0; - default: - error_report("failed to map MSR %u to HV register name", msr); - return -1; - } -} - -static int set_msrs(const CPUState *cpu, GList *msrs) -{ - size_t n_msrs; - GList *entries; - MshvMsrEntry *entry; - enum hv_register_name name; - struct hv_register_assoc *assoc; - int ret; - size_t i = 0; - - n_msrs = g_list_length(msrs); - hv_register_assoc *assocs = g_new0(hv_register_assoc, n_msrs); - - entries = msrs; - for (const GList *elem = entries; elem != NULL; elem = elem->next) { - entry = elem->data; - ret = mshv_msr_to_hv_reg_name(entry->index, &name); - if (ret < 0) { - g_free(assocs); - return ret; - } - assoc = &assocs[i]; - assoc->name = name; - /* the union has been initialized to 0 */ - assoc->value.reg64 = entry->data; - i++; - } - ret = mshv_set_generic_regs(cpu, assocs, n_msrs); - g_free(assocs); - if (ret < 0) { - error_report("failed to set msrs"); - return -1; - } - return 0; -} - - -int mshv_configure_msr(const CPUState *cpu, const MshvMsrEntry *msrs, - size_t n_msrs) -{ - GList *valid_msrs = NULL; - uint32_t msr_index; - int ret; - - for (size_t i = 0; i < n_msrs; i++) { - msr_index = msrs[i].index; - /* check whether index of msrs is in SUPPORTED_MSRS */ - if (mshv_is_supported_msr(msr_index)) { - valid_msrs = g_list_append(valid_msrs, (void *) &msrs[i]); - } - } - - ret = set_msrs(cpu, valid_msrs); - g_list_free(valid_msrs); - - return ret; -}
diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index c4617ca..32b4b07 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c
@@ -44,7 +44,7 @@ return 1; } -int kvm_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev) +int kvm_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev) { return -ENOSYS; }
diff --git a/accel/stubs/mshv-stub.c b/accel/stubs/mshv-stub.c index e499b19..566a17f 100644 --- a/accel/stubs/mshv-stub.c +++ b/accel/stubs/mshv-stub.c
@@ -13,13 +13,14 @@ #include "system/mshv.h" bool mshv_allowed; +MshvState *mshv_state; -int mshv_irqchip_add_msi_route(int vector, PCIDevice *dev) +int mshv_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev) { return -ENOSYS; } -void mshv_irqchip_release_virq(int virq) +void mshv_irqchip_release_virq(MshvState *s, int virq) { } @@ -28,7 +29,7 @@ return -ENOSYS; } -void mshv_irqchip_commit_routes(void) +void mshv_irqchip_commit_routes(MshvState *s) { }
diff --git a/configs/devices/hexagon-softmmu/default.mak b/configs/devices/hexagon-softmmu/default.mak new file mode 100644 index 0000000..6f20077 --- /dev/null +++ b/configs/devices/hexagon-softmmu/default.mak
@@ -0,0 +1,7 @@ +# Default configuration for hexagon-softmmu + +# Uncomment the following lines to disable these optional devices: + +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_HEX_VIRT=y +# CONFIG_HEX_DSP=y
diff --git a/configs/meson/emscripten.txt b/configs/meson/emscripten.txt index 4230e88..56c3976 100644 --- a/configs/meson/emscripten.txt +++ b/configs/meson/emscripten.txt
@@ -6,3 +6,8 @@ # configuration so explicitly add the flag here. c_link_args = ['-pthread','-sASYNCIFY=1','-sPROXY_TO_PTHREAD=1','-sFORCE_FILESYSTEM','-sALLOW_TABLE_GROWTH','-sTOTAL_MEMORY=2GB','-sWASM_BIGINT','-sEXPORT_ES6=1','-sASYNCIFY_IMPORTS=ffi_call_js','-sEXPORTED_RUNTIME_METHODS=addFunction,removeFunction,TTY,FS'] cpp_link_args = ['-pthread','-sASYNCIFY=1','-sPROXY_TO_PTHREAD=1','-sFORCE_FILESYSTEM','-sALLOW_TABLE_GROWTH','-sTOTAL_MEMORY=2GB','-sWASM_BIGINT','-sEXPORT_ES6=1','-sASYNCIFY_IMPORTS=ffi_call_js','-sEXPORTED_RUNTIME_METHODS=addFunction,removeFunction,TTY,FS'] + +[project options] +# Workaround for failure to find glib-2.0 when building idef parser, +# this may be a bug in the cross-build setup used by emscripten: +hexagon_idef_parser = false
diff --git a/configs/targets/hexagon-softmmu.mak b/configs/targets/hexagon-softmmu.mak new file mode 100644 index 0000000..a77c100 --- /dev/null +++ b/configs/targets/hexagon-softmmu.mak
@@ -0,0 +1,8 @@ +# Default configuration for hexagon-softmmu + +TARGET_ARCH=hexagon +TARGET_XML_FILES=hexagon-core.xml hexagon-hvx.xml +TARGET_LONG_BITS=32 +TARGET_NOT_USING_LEGACY_LDST_PHYS_API=y +TARGET_NOT_USING_LEGACY_NATIVE_ENDIAN_API=y +TARGET_NEED_FDT=y
diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 97750f5..cf3e1a0 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst
@@ -249,6 +249,13 @@ and is currently unmaintained. The machine is scheduled to be removed as it appears to have no users. +``memory-encryption`` machine property (since 11.1) +''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use ``confidential-guest-support`` instead. The ``memory-encryption`` object +was an early implementation of memory encryption support in QEMU, but it has +been superseded by the more comprehensive ``confidential-guest-support`` +object. Backend options ---------------
diff --git a/docs/devel/hexagon-sys.rst b/docs/devel/hexagon-sys.rst new file mode 100644 index 0000000..92ebc32 --- /dev/null +++ b/docs/devel/hexagon-sys.rst
@@ -0,0 +1,112 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +.. _Hexagon-System-arch: + +Hexagon System Architecture +=========================== + +The hexagon architecture has some unique elements which are described here. + +Interrupts +---------- +When interrupts arrive at a Hexagon DSP core, they are priority-steered to +be handled by an eligible hardware thread with the lowest priority. + +Memory +------ +Each hardware thread has an ``SSR.ASID`` field that contains its Address +Space Identifier. This value is catenated with a 32-bit virtual address - +the MMU can then resolve this extended virtual address to a physical address. + +TLBs +---- +The format of a TLB entry is shown below. + +.. note:: + The Small Core DSPs have a different TLB format which is not yet + supported. + +.. admonition:: Diagram + + .. code:: text + + 6 5 4 3 + 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |v|g|x|A|A| | | + |a|l|P|1|0| ASID | Virtual Page | + |l|b| | | | | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + 3 2 1 0 + 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | | | | | | | + |x|w|r|u|Cacheab| Physical Page |S| + | | | | | | | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +* ASID: the address-space identifier +* A1, A0: the behavior of these cache line attributes are not modeled by QEMU. +* xP: the extra-physical bit is the most significant physical address bit. +* S: the S bit and the LSBs of the physical page indicate the page size +* val: this is the 'valid' bit, when set it indicates that page matching + should consider this entry. + +.. list-table:: Page sizes + :widths: 25 25 50 + :header-rows: 1 + + * - S-bit + - Phys page LSBs + - Page size + * - 1 + - N/A + - 4kb + * - 0 + - 0b1 + - 16kb + * - 0 + - 0b10 + - 64kb + * - 0 + - 0b100 + - 256kb + * - 0 + - 0b1000 + - 1MB + * - 0 + - 0b10000 + - 4MB + * - 0 + - 0b100000 + - 16MB + +* glb: if the global bit is set, the ASID is not considered when matching + TLBs. +* Cacheab: the cacheability attributes of TLBs are not modeled, these bits + are ignored. +* RWX: read-, write-, execute-, enable bits. Indicates if user programs + are permitted to read/write/execute the given page. +* U: indicates if user programs can access this page. + +Scheduler +--------- +The Hexagon system architecture has a feature to assist the guest OS +task scheduler. The guest OS can enable this feature by setting +``SCHEDCFG.EN``. The ``BESTWAIT`` register is programmed by the guest OS +to indicate the priority of the highest priority task waiting to run on a +hardware thread. The reschedule interrupt is triggered when any hardware +thread's priority in ``STID.PRIO`` is worse than the ``BESTWAIT``. When +it is triggered, the ``BESTWAIT.PRIO`` value is reset to 0x1ff. + +HVX Coprocessor +--------------- +The Supervisor Status Register field ``SSR.XA`` binds a DSP hardware thread +to one of the eight possible HVX contexts. The guest OS is responsible for +managing this resource. + +.. seealso:: + + ``target/hexagon/README`` in the QEMU source tree for more info about Hexagon.
diff --git a/docs/devel/index-internals.rst b/docs/devel/index-internals.rst index 198f155..b89bab9 100644 --- a/docs/devel/index-internals.rst +++ b/docs/devel/index-internals.rst
@@ -14,6 +14,7 @@ block-coroutine-wrapper clocks ebpf_rss + hexagon-sys migration/index multi-process reset
diff --git a/docs/system/hexagon/cdsp.rst b/docs/system/hexagon/cdsp.rst new file mode 100644 index 0000000..2375292 --- /dev/null +++ b/docs/system/hexagon/cdsp.rst
@@ -0,0 +1,12 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Compute DSP +=========== + +A Hexagon CDSP is designed as a computation offload device for an SoC. The +``V66G_1024`` machine contains: + +* L2VIC interrupt controller +* QTimer timer device + +This machine will support any Hexagon CPU, but will default to ``v66``.
diff --git a/docs/system/hexagon/emulation.rst b/docs/system/hexagon/emulation.rst new file mode 100644 index 0000000..36f54f0 --- /dev/null +++ b/docs/system/hexagon/emulation.rst
@@ -0,0 +1,15 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +.. _Hexagon Emulation: + +Hexagon CPU architecture support +================================ + +QEMU's TCG emulation includes support for v65, v66, v67, v68, v69, v71, v73. +It also has support for the following architecture extensions: + +- HVX (Hexagon Vector eXtensions) + +For information on the specifics of the HVX extension, please refer +to the `Qualcomm Hexagon V73 HVX Programmer's Reference Manual +<https://docs.qualcomm.com/bundle/publicresource/80-N2040-53.pdf>`_.
diff --git a/docs/system/introduction.rst b/docs/system/introduction.rst index bec7291..8d9ef61 100644 --- a/docs/system/introduction.rst +++ b/docs/system/introduction.rst
@@ -1,6 +1,15 @@ Introduction ============ +.. note:: + QEMU supports many different target CPU architectures and many + different machine types for each architecture. Options, properties, + and command lines that work for one architecture or machine type + will not necessarily work on another. The examples shown in this + manual are specific to the architecture and machine type being + demonstrated. When in doubt, consult the documentation for your + specific target architecture and machine type. + .. _Accelerators: Virtualisation Accelerators
diff --git a/docs/system/target-hexagon.rst b/docs/system/target-hexagon.rst new file mode 100644 index 0000000..416b8f7 --- /dev/null +++ b/docs/system/target-hexagon.rst
@@ -0,0 +1,103 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +.. _Hexagon-System-emulator: + +Hexagon System emulator +----------------------- + +Use the ``qemu-system-hexagon`` executable to simulate a 32-bit Hexagon +machine. + +Hexagon Machines +================ + +Hexagon DSPs are suited to various functions and generally appear in a +"DSP subsystem" of a larger system-on-chip (SoC). + +Hexagon DSPs are often included in a subsystem that looks like the diagram +below. Instructions are loaded into DDR before the DSP is brought out of +reset and the first instructions are fetched from DDR via the EVB/reset vector. + +In a real system, a TBU/SMMU would normally arbitrate AXI accesses but +we don't have a need to model that for QEMU. + +Hexagon DSP cores use simultaneous multithreading (SMT) with as many as 8 +hardware threads. + +.. admonition:: Diagram + + .. code:: text + + AHB (local) bus AXI (global) bus + │ │ + │ │ + ┌─────────┐ │ ┌─────────────────┐ │ + │ L2VIC ├──┤ │ │ │ + │ ├──┼───────► ├───────┤ + └─────▲───┘ │ │ Hexagon DSP │ │ + │ │ │ │ │ ┌─────┐ + │ │ │ N threads │ │ │ DDR │ + │ ├───────┤ │ │ │ │ + ┌────┴──┐ │ │ │ ├────────┤ │ + │QTimer ├───┤ │ │ │ │ │ + │ │ │ │ │ │ │ │ + └───────┘ │ │ ┌─────────┐ │ │ │ │ + │ │ ┌─────────┐│ │ │ │ │ + ┌───────┐ │ │ │ HVX xM ││ │ │ │ │ + │QDSP6SS├───┤ │ │ │┘ │ │ │ │ + └───────┘ │ │ └─────────┘ │ │ └─────┘ + │ │ │ │ + ┌───────┐ │ └─────────────────┘ │ + │ CSR ├───┤ + └───────┘ │ ┌──────┐ ┌───────────┐ + │ │ TCM │ │ VTCM │ + │ │ │ │ + └──────┘ │ │ + │ │ + │ │ + │ │ + └───────────┘ + +Components +---------- +Other than l2vic and HVX, the components below are not implemented in QEMU. + +* L2VIC: the L2 vectored interrupt controller. Supports 1024 input + interrupts, edge- or level-triggered. The core ISA has system registers + ``VID``, ``VID1`` which read through to the L2VIC device. +* QTimer: ARMSSE-based programmable timer device. Its interrupts are + wired to the L2VIC. System registers ``TIMER``, ``UTIMER`` read + through to the QTimer device. +* QDSP6SS: DSP subsystem features, accessible to the entire SoC, including + DSP NMI, watchdog, reset, etc. +* CSR: Configuration/Status Registers. +* TCM: DSP-exclusive tightly-coupled memory. This memory can be used for + DSPs when isolated from DDR and in some bootstrapping modes. +* VTCM: DSP-exclusive vector tightly-coupled memory. This memory is accessed + by some HVX instructions. +* HVX: the vector coprocessor supports 64 and 128-byte vector registers. + 64-byte mode is not implemented in QEMU. + + +Bootstrapping +------------- +Hexagon systems do not generally have access to a block device. So, for +QEMU the typical use case involves loading a binary or ELF file into memory +and executing from the indicated start address:: + + $ qemu-system-hexagon -kernel ./prog -append 'arg1 arg2' + +Semihosting +----------- +Hexagon supports a semihosting interface similar to other architectures'. +The ``trap0`` instruction can activate these semihosting calls so that the +guest software can access the host console and filesystem. Semihosting +is not yet implemented in QEMU hexagon. + + +Hexagon Features +================ +.. toctree:: + hexagon/emulation + hexagon/cdsp +
diff --git a/docs/system/targets.rst b/docs/system/targets.rst index 5b12858..5ebdd0f 100644 --- a/docs/system/targets.rst +++ b/docs/system/targets.rst
@@ -30,3 +30,4 @@ target-sparc64 target-i386 target-xtensa + target-hexagon
diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h index e8d0661..1d93114 100644 --- a/fsdev/file-op-9p.h +++ b/fsdev/file-op-9p.h
@@ -81,6 +81,11 @@ #define V9FS_SEC_MASK 0x0000003C +/* + * Limits the maximum amount of simultaneously open xattr FIDs to prevent + * host memory exhaustion (as each xattr FID contains a xattr value buffer). + */ +#define V9FS_MAX_XATTR_DEFAULT 1024 typedef struct FileOperations FileOperations; typedef struct XattrOperations XattrOperations; @@ -96,6 +101,8 @@ FsThrottle fst; mode_t fmode; mode_t dmode; + /* temporary storage for parse_opts only */ + uint32_t max_xattr; } FsDriverEntry; struct FsContext { @@ -109,6 +116,10 @@ void *private; mode_t fmode; mode_t dmode; + /* max. amount of simultaneously open xattr FIDs */ + uint32_t xattr_fid_limit; + /* current amount of open xattr FIDs */ + uint32_t xattr_fid_count; }; struct V9fsPath {
diff --git a/fsdev/qemu-fsdev-opts.c b/fsdev/qemu-fsdev-opts.c index 07a18c6..c2c1e83 100644 --- a/fsdev/qemu-fsdev-opts.c +++ b/fsdev/qemu-fsdev-opts.c
@@ -46,6 +46,9 @@ }, { .name = "dmode", .type = QEMU_OPT_NUMBER, + }, { + .name = "max_xattr", + .type = QEMU_OPT_NUMBER, }, THROTTLE_OPTS, @@ -92,6 +95,9 @@ }, { .name = "dmode", .type = QEMU_OPT_NUMBER, + }, { + .name = "max_xattr", + .type = QEMU_OPT_NUMBER, }, { /*End of list */ }
diff --git a/fsdev/qemu-fsdev.c b/fsdev/qemu-fsdev.c index 57877da..f97103c 100644 --- a/fsdev/qemu-fsdev.c +++ b/fsdev/qemu-fsdev.c
@@ -45,7 +45,7 @@ static QTAILQ_HEAD(, FsDriverListEntry) fsdriver_entries = QTAILQ_HEAD_INITIALIZER(fsdriver_entries); -#define COMMON_FS_DRIVER_OPTIONS "id", "fsdriver", "readonly" +#define COMMON_FS_DRIVER_OPTIONS "id", "fsdriver", "readonly", "max_xattr" static FsDriverTable FsDrivers[] = { {
diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index aa48306..ee592b6 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c
@@ -775,8 +775,11 @@ { if (fid_type == P9_FID_DIR) { return dirfd(fs->dir.stream); - } else { + } else if (fid_type == P9_FID_FILE) { return fs->fd; + } else { + errno = EBADF; + return -1; } } @@ -1527,6 +1530,15 @@ const char *path = qemu_opt_get(opts, "path"); const char *multidevs = qemu_opt_get(opts, "multidevs"); + uint64_t val = qemu_opt_get_number(opts, "max_xattr", + V9FS_MAX_XATTR_DEFAULT); + if (val > UINT32_MAX) { + error_setg(errp, "max_xattr value '%s' too large", + qemu_opt_get(opts, "max_xattr")); + return -1; + } + fse->max_xattr = val; + if (!sec_model) { error_setg(errp, "security_model property not set"); error_append_security_model_hint(errp);
diff --git a/hw/9pfs/9p-synth.c b/hw/9pfs/9p-synth.c index b3743f6..3b3654b 100644 --- a/hw/9pfs/9p-synth.c +++ b/hw/9pfs/9p-synth.c
@@ -25,6 +25,8 @@ #include "qemu/rcu_queue.h" #include "qemu/cutils.h" #include "system/qtest.h" +#include "qapi/error.h" +#include "qemu/option.h" /* Root node for synth file system */ static V9fsSynthNode synth_root = { @@ -475,15 +477,15 @@ const char *name, void *value, size_t size, int flags) { - errno = ENOTSUP; - return -1; + /* pretend it worked */ + return 0; } static int synth_lremovexattr(FsContext *ctx, V9fsPath *path, const char *name) { - errno = ENOTSUP; - return -1; + /* pretend it worked */ + return 0; } static int synth_name_to_path(FsContext *ctx, V9fsPath *dir_path, @@ -563,6 +565,19 @@ return 1; } +/* transmits internal xattr counter to client */ +static ssize_t v9fs_synth_read_xattr_count(void *buf, int len, off_t offset, + void *arg) +{ + FsContext *ctx = arg; + size_t local_count = ctx->xattr_fid_count; + if (len < (int)sizeof(size_t)) { + return -ENOSPC; + } + memcpy(buf, &local_count, sizeof(size_t)); + return sizeof(size_t); +} + static int synth_init(FsContext *ctx, Error **errp) { QLIST_INIT(&synth_root.child); @@ -624,17 +639,45 @@ g_free(name); } } + + /* Directory for internal statistic queries */ + { + V9fsSynthNode *stat_dir = NULL; + ret = qemu_v9fs_synth_mkdir(NULL, 0755, "stat", &stat_dir); + assert(!ret); + + /* File for internal xattr count query */ + ret = qemu_v9fs_synth_add_file(stat_dir, 0444, "xattr_count", + v9fs_synth_read_xattr_count, + NULL, ctx); + assert(!ret); + } } return 0; } +static int synth_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp) +{ + uint64_t val = qemu_opt_get_number(opts, "max_xattr", + V9FS_MAX_XATTR_DEFAULT); + if (val > UINT32_MAX) { + error_setg(errp, "max_xattr value '%s' too large", + qemu_opt_get(opts, "max_xattr")); + return -1; + } + fse->max_xattr = val; + + return 0; +} + static bool synth_has_valid_file_handle(int fid_type, V9fsFidOpenState *fs) { return false; } FileOperations synth_ops = { + .parse_opts = synth_parse_opts, .init = synth_init, .lstat = synth_lstat, .readlink = synth_readlink,
diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index b486cce..3119f01 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c
@@ -265,6 +265,31 @@ return str->size; } +static int xattr_fid_count_inc(V9fsPDU *pdu) +{ + V9fsState *s = pdu->s; + + if (s->ctx.xattr_fid_limit > 0 && + s->ctx.xattr_fid_count >= s->ctx.xattr_fid_limit) { + error_report_once("9pfs: xattr_fid_count limit exceeded " + "(configurable by option 'max_xattr')."); + return -ENOSPC; + } + s->ctx.xattr_fid_count++; + return 0; +} + +static void xattr_fid_count_decr(V9fsPDU *pdu) +{ + V9fsState *s = pdu->s; + + if (s->ctx.xattr_fid_count > 0) { + s->ctx.xattr_fid_count--; + } else { + error_report_once("9pfs: xattr_fid_count underflow detected"); + } +} + /* * returns 0 if fid got re-opened, 1 if not, < 0 on error */ @@ -397,6 +422,7 @@ } } else if (fidp->fid_type == P9_FID_XATTR) { retval = v9fs_xattr_fid_clunk(pdu, fidp); + xattr_fid_count_decr(pdu); } v9fs_path_free(&fidp->path); g_free(fidp); @@ -634,6 +660,14 @@ fidp->clunked = true; put_fid(pdu, fidp); } + + /* + * Explicitly reset the xattr FID counter. + * + * free_fid() already decrements the counter for each P9_FID_XATTR, so the + * counter should already be zero, hence this is just a defensive measure. + */ + s->ctx.xattr_fid_count = 0; } #define P9_QID_TYPE_DIR 0x80 @@ -1469,6 +1503,16 @@ goto out; } + /* cap msize to transport's theoretical limit */ + if (s->transport->msize_limit) { + size_t limit = s->transport->msize_limit(s); + if (s->msize > limit) { + s->msize = limit; + warn_report_once("9p: client msize capped to %zu (transport limit)", + limit); + } + } + /* 8192 is the default msize of Linux clients */ if (s->msize <= 8192 && !(s->ctx.export_flags & V9FS_NO_PERF_WARN)) { warn_report_once( @@ -2261,10 +2305,15 @@ err = -ENOENT; goto out_nofid; } + if (!fid_has_valid_file_handle(pdu->s, fidp)) { + err = -EBADF; + goto out; + } err = v9fs_co_fsync(pdu, fidp, datasync); if (!err) { err = offset; } +out: put_fid(pdu, fidp); out_nofid: pdu_complete(pdu, err); @@ -2669,6 +2718,7 @@ uint32_t max_count; V9fsPDU *pdu = opaque; V9fsState *s = pdu->s; + size_t max_resp_sz; retval = pdu_unmarshal(pdu, offset, "dqd", &fid, &initial_offset, &max_count); @@ -2677,9 +2727,28 @@ } trace_v9fs_readdir(pdu->tag, pdu->id, fid, initial_offset, max_count); + max_resp_sz = s->msize; + + /* + * Constrain max_count to transport's current, actual response buffer size. + * A bad client might provide a response buffer < msize. + */ + if (s->transport->response_buffer_size) { + size_t buf_size = s->transport->response_buffer_size(pdu); + if (max_resp_sz > buf_size) { + max_resp_sz = buf_size; + } + } + /* Enough space for a R_readdir header: size[4] Rreaddir tag[2] count[4] */ - if (max_count > s->msize - 11) { - max_count = s->msize - 11; + if (max_resp_sz > 11) { + max_resp_sz -= 11; + } else { + max_resp_sz = 0; + } + + if (max_count > max_resp_sz) { + max_count = max_resp_sz; warn_report_once( "9p: bad client: T_readdir with count > msize - 11" ); @@ -3576,6 +3645,10 @@ } /* do we need to sync the file? */ if (donttouch_stat(&v9stat)) { + if (!fid_has_valid_file_handle(s, fidp)) { + err = -EBADF; + goto out; + } err = v9fs_co_fsync(pdu, fidp, 0); goto out; } @@ -3835,6 +3908,10 @@ err = -ENOENT; goto out_nofid; } + if (!fid_has_valid_file_handle(pdu->s, fidp)) { + err = -EBADF; + goto out; + } err = v9fs_co_fstat(pdu, fidp, &stbuf); if (err < 0) { goto out; @@ -3880,6 +3957,10 @@ err = -ENOENT; goto out_nofid; } + if (!fid_has_valid_file_handle(pdu->s, fidp)) { + err = -EBADF; + goto out; + } err = v9fs_co_fstat(pdu, fidp, &stbuf); if (err < 0) { goto out; @@ -3993,6 +4074,14 @@ clunk_fid(s, xattr_fidp->fid); goto out; } + + /* Check xattr FID limit */ + err = xattr_fid_count_inc(pdu); + if (err < 0) { + clunk_fid(s, xattr_fidp->fid); + goto out; + } + /* * Read the xattr value */ @@ -4000,6 +4089,7 @@ xattr_fidp->fid_type = P9_FID_XATTR; xattr_fidp->fs.xattr.xattrwalk_fid = true; xattr_fidp->fs.xattr.value = g_malloc0(size); + if (size) { err = v9fs_co_llistxattr(pdu, &xattr_fidp->path, xattr_fidp->fs.xattr.value, @@ -4026,6 +4116,14 @@ clunk_fid(s, xattr_fidp->fid); goto out; } + + /* Check xattr FID limit */ + err = xattr_fid_count_inc(pdu); + if (err < 0) { + clunk_fid(s, xattr_fidp->fid); + goto out; + } + /* * Read the xattr value */ @@ -4033,6 +4131,7 @@ xattr_fidp->fid_type = P9_FID_XATTR; xattr_fidp->fs.xattr.xattrwalk_fid = true; xattr_fidp->fs.xattr.value = g_malloc0(size); + if (size) { err = v9fs_co_lgetxattr(pdu, &xattr_fidp->path, &name, xattr_fidp->fs.xattr.value, @@ -4134,6 +4233,12 @@ goto out_put_fid; } + /* Check xattr FID limit */ + err = xattr_fid_count_inc(pdu); + if (err < 0) { + goto out_put_fid; + } + /* Make the file fid point to xattr */ xattr_fidp = file_fidp; xattr_fidp->fid_type = P9_FID_XATTR; @@ -4395,6 +4500,10 @@ s->reclaiming = false; + /* init xattr FID limit from fsdev config */ + s->ctx.xattr_fid_limit = fse->max_xattr; + s->ctx.xattr_fid_count = 0; + rc = 0; out: if (rc) {
diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index b2df659..1a30966 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h
@@ -481,6 +481,8 @@ void (*init_out_iov_from_pdu)(V9fsPDU *pdu, struct iovec **piov, unsigned int *pniov, size_t size); void (*push_and_notify)(V9fsPDU *pdu); + size_t (*msize_limit)(V9fsState *s); + size_t (*response_buffer_size)(V9fsPDU *pdu); }; #endif
diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index 9f70e23..50dc930 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c
@@ -192,12 +192,29 @@ *pniov = elem->out_num; } +static size_t virtio_9p_msize_limit(V9fsState *s) +{ + const size_t guestPageSize = 4096; + return (VIRTQUEUE_MAX_SIZE - 2) * guestPageSize; +} + +static size_t virtio_9p_response_buffer_size(V9fsPDU *pdu) +{ + V9fsState *s = pdu->s; + V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); + VirtQueueElement *elem = v->elems[pdu->idx]; + + return iov_size(elem->in_sg, elem->in_num); +} + static const V9fsTransport virtio_9p_transport = { .pdu_vmarshal = virtio_pdu_vmarshal, .pdu_vunmarshal = virtio_pdu_vunmarshal, .init_in_iov_from_pdu = virtio_init_in_iov_from_pdu, .init_out_iov_from_pdu = virtio_init_out_iov_from_pdu, .push_and_notify = virtio_9p_push_and_notify, + .msize_limit = virtio_9p_msize_limit, + .response_buffer_size = virtio_9p_response_buffer_size, }; static void virtio_9p_device_realize(DeviceState *dev, Error **errp)
diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c index ca0fff5..24c90d9 100644 --- a/hw/9pfs/xen-9p-backend.c +++ b/hw/9pfs/xen-9p-backend.c
@@ -250,12 +250,43 @@ qemu_bh_schedule(ring->bh); } +static size_t xen_9p_msize_limit(V9fsState *s) +{ + Xen9pfsDev *xen_9pfs = container_of(s, Xen9pfsDev, state); + size_t limit; + int i; + + if (!xen_9pfs->num_rings) { + return 0; + } + + limit = XEN_FLEX_RING_SIZE(xen_9pfs->rings[0].ring_order); + for (i = 1; i < xen_9pfs->num_rings; i++) { + limit = MIN(limit, XEN_FLEX_RING_SIZE(xen_9pfs->rings[i].ring_order)); + } + + return limit; +} + +static size_t xen_9pfs_response_buffer_size(V9fsPDU *pdu) +{ + Xen9pfsDev *priv = container_of(pdu->s, Xen9pfsDev, state); + Xen9pfsRing *ring = &priv->rings[pdu->tag % priv->num_rings]; + struct iovec in_sg[2]; + int num; + + xen_9pfs_in_sg(ring, in_sg, &num, pdu->idx, 0); + return iov_size(in_sg, num); +} + static const V9fsTransport xen_9p_transport = { .pdu_vmarshal = xen_9pfs_pdu_vmarshal, .pdu_vunmarshal = xen_9pfs_pdu_vunmarshal, .init_in_iov_from_pdu = xen_9pfs_init_in_iov_from_pdu, .init_out_iov_from_pdu = xen_9pfs_init_out_iov_from_pdu, .push_and_notify = xen_9pfs_push_and_notify, + .msize_limit = xen_9p_msize_limit, + .response_buffer_size = xen_9pfs_response_buffer_size, }; static int xen_9pfs_init(struct XenLegacyDevice *xendev)
diff --git a/hw/Kconfig b/hw/Kconfig index c109f55..c92ca2b 100644 --- a/hw/Kconfig +++ b/hw/Kconfig
@@ -53,6 +53,7 @@ source cpu/Kconfig source alpha/Kconfig source avr/Kconfig +source hexagon/Kconfig source hppa/Kconfig source i386/Kconfig source loongarch/Kconfig
diff --git a/hw/core/machine.c b/hw/core/machine.c index 9a10e45..5b97df6 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c
@@ -497,6 +497,8 @@ Object *cgs = object_resolve_path_component(object_get_objects_root(), value); + warn_report("memory-encryption is deprecated, use confidential-guest-support instead"); + if (!cgs) { error_setg(errp, "No such memory encryption object '%s'", value); return;
diff --git a/hw/hexagon/Kconfig b/hw/hexagon/Kconfig new file mode 100644 index 0000000..52065ab --- /dev/null +++ b/hw/hexagon/Kconfig
@@ -0,0 +1,14 @@ +config HEX_DSP + bool + default y + depends on HEXAGON + +config HEX_VIRT + bool + default y + depends on HEX_DSP && FDT + select DEVICE_TREE + select VIRTIO_MMIO + select PL011 + select VIRTIO_BLK + select VIRTIO_SCSI
diff --git a/hw/hexagon/hexagon_dsp.c b/hw/hexagon/hexagon_dsp.c new file mode 100644 index 0000000..aa49399 --- /dev/null +++ b/hw/hexagon/hexagon_dsp.c
@@ -0,0 +1,225 @@ +/* + * Hexagon DSP Subsystem emulation. This represents a generic DSP + * subsystem with few peripherals, like the Compute DSP. + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "system/address-spaces.h" +#include "hw/core/boards.h" +#include "hw/core/qdev-properties.h" +#include "hw/hexagon/hexagon.h" +#include "hw/hexagon/hexagon_globalreg.h" +#include "hw/hexagon/hexagon_tlb.h" +#include "hw/core/loader.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "elf.h" +#include "cpu.h" +#include "migration/cpu.h" +#include "system/system.h" +#include "target/hexagon/internal.h" +#include "system/physmem.h" +#include "system/reset.h" + +#include "machine_cfg_v66g_1024.h.inc" + +#define TYPE_HEXAGON_DSP_MACHINE "hexagon-dsp-machine" +OBJECT_DECLARE_SIMPLE_TYPE(HexagonDspMachineState, HEXAGON_DSP_MACHINE) + +struct HexagonDspMachineState { + HexagonCommonMachineState parent_obj; + + hwaddr isdb_secure_flag; + hwaddr isdb_trusted_flag; +}; + +static HexagonDspMachineState *current_dms; + +static void hex_symbol_callback(const char *st_name, int st_info, + uint64_t st_value, uint64_t st_size) +{ + if (!g_strcmp0("isdb_secure_flag", st_name)) { + current_dms->isdb_secure_flag = st_value; + } + if (!g_strcmp0("isdb_trusted_flag", st_name)) { + current_dms->isdb_trusted_flag = st_value; + } +} + +/* Board init. */ +static struct hexagon_board_boot_info hexagon_binfo; + +static void hexagon_load_kernel(HexagonDspMachineState *dms, HexagonCPU *cpu) +{ + uint64_t pentry; + long kernel_size; + + current_dms = dms; + kernel_size = load_elf_ram_sym(hexagon_binfo.kernel_filename, NULL, NULL, + NULL, &pentry, NULL, NULL, + &hexagon_binfo.kernel_elf_flags, 0, EM_HEXAGON, 0, 0, + &address_space_memory, false, hex_symbol_callback); + current_dms = NULL; + + if (kernel_size <= 0) { + error_report("no kernel file '%s'", + hexagon_binfo.kernel_filename); + exit(1); + } + + qdev_prop_set_uint32(DEVICE(cpu), "exec-start-addr", pentry); +} + +static void hexagon_init_bootstrap(HexagonDspMachineState *dms, HexagonCPU *cpu) +{ + MachineState *machine = MACHINE(dms); + + if (machine->kernel_filename) { + uint32_t mem = 1; + + hexagon_load_kernel(dms, cpu); + if (dms->isdb_secure_flag) { + physical_memory_write(dms->isdb_secure_flag, + &mem, sizeof(mem)); + } + if (dms->isdb_trusted_flag) { + physical_memory_write(dms->isdb_trusted_flag, + &mem, sizeof(mem)); + } + } +} + +static void do_cpu_reset(void *opaque) +{ + HexagonCPU *cpu = opaque; + CPUState *cs = CPU(cpu); + cpu_reset(cs); +} + +static void hexagon_common_init(MachineState *machine, Rev_t rev, + const struct hexagon_machine_config *m_cfg) +{ + HexagonCommonMachineState *hms = HEXAGON_COMMON_MACHINE(machine); + HexagonDspMachineState *dms = HEXAGON_DSP_MACHINE(machine); + MemoryRegion *address_space; + DeviceState *glob_regs_dev; + DeviceState *tlb_dev; + + memset(&hexagon_binfo, 0, sizeof(hexagon_binfo)); + if (machine->kernel_filename) { + hexagon_binfo.ram_size = machine->ram_size; + hexagon_binfo.kernel_filename = machine->kernel_filename; + } + + machine->enable_graphics = 0; + + address_space = get_system_memory(); + + memory_region_init_rom(&hms->cfgtable_rom, NULL, "config_table.rom", + sizeof(m_cfg->cfgtable), &error_fatal); + memory_region_add_subregion(address_space, m_cfg->cfgbase, + &hms->cfgtable_rom); + + memory_region_init_ram(&hms->ram, NULL, "ddr.ram", + machine->ram_size, &error_fatal); + memory_region_add_subregion(address_space, 0x0, &hms->ram); + + glob_regs_dev = qdev_new(TYPE_HEXAGON_GLOBALREG); + object_property_add_child(OBJECT(machine), "global-regs", + OBJECT(glob_regs_dev)); + qdev_prop_set_uint64(glob_regs_dev, "config-table-addr", m_cfg->cfgbase); + qdev_prop_set_uint32(glob_regs_dev, "dsp-rev", rev); + sysbus_realize_and_unref(SYS_BUS_DEVICE(glob_regs_dev), &error_fatal); + + tlb_dev = qdev_new(TYPE_HEXAGON_TLB); + object_property_add_child(OBJECT(machine), "tlb", OBJECT(tlb_dev)); + qdev_prop_set_uint32(tlb_dev, "num-entries", + m_cfg->cfgtable.jtlb_size_entries); + sysbus_realize_and_unref(SYS_BUS_DEVICE(tlb_dev), &error_fatal); + + for (int i = 0; i < machine->smp.cpus; i++) { + HexagonCPU *cpu = HEXAGON_CPU(object_new(machine->cpu_type)); + qemu_register_reset(do_cpu_reset, cpu); + + /* + * CPU #0 is the only CPU running at boot, others must be + * explicitly enabled via start instruction. + */ + qdev_prop_set_bit(DEVICE(cpu), "start-powered-off", (i != 0)); + if (i == 0) { + hexagon_init_bootstrap(dms, cpu); + } + object_property_set_link(OBJECT(cpu), "global-regs", + OBJECT(glob_regs_dev), &error_fatal); + object_property_set_link(OBJECT(cpu), "tlb", + OBJECT(tlb_dev), &error_fatal); + qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal); + } + + rom_add_blob_fixed_as("config_table.rom", &m_cfg->cfgtable, + sizeof(m_cfg->cfgtable), m_cfg->cfgbase, + &address_space_memory); +} + +static void init_mc(MachineClass *mc) +{ + mc->block_default_type = IF_SD; + mc->default_ram_size = 4 * GiB; + mc->no_parallel = 1; + mc->no_floppy = 1; + mc->no_cdrom = 1; + mc->no_serial = 1; + mc->is_default = false; + mc->max_cpus = 8; +} + +/* ----------------------------------------------------------------- */ +/* Core-specific configuration settings are defined below this line. */ +/* Config table values defined in machine_configs.h.inc */ +/* ----------------------------------------------------------------- */ + +static void v66g_1024_config_init(MachineState *machine) +{ + hexagon_common_init(machine, v66_rev, &v66g_1024); +} + +static void v66g_1024_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Hexagon V66G_1024"; + mc->init = v66g_1024_config_init; + init_mc(mc); + mc->is_default = true; + mc->default_cpu_type = TYPE_HEXAGON_CPU_V66; + mc->default_cpus = 4; +} + +static const TypeInfo hexagon_machine_types[] = { + { + .name = TYPE_HEXAGON_COMMON_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(HexagonCommonMachineState), + .abstract = true, + }, + { + .name = TYPE_HEXAGON_DSP_MACHINE, + .parent = TYPE_HEXAGON_COMMON_MACHINE, + .instance_size = sizeof(HexagonDspMachineState), + .abstract = true, + }, + { + .name = MACHINE_TYPE_NAME("V66G_1024"), + .parent = TYPE_HEXAGON_DSP_MACHINE, + .class_init = v66g_1024_init, + }, +}; + +DEFINE_TYPES(hexagon_machine_types)
diff --git a/hw/hexagon/hexagon_globalreg.c b/hw/hexagon/hexagon_globalreg.c new file mode 100644 index 0000000..b5e5913 --- /dev/null +++ b/hw/hexagon/hexagon_globalreg.c
@@ -0,0 +1,316 @@ +/* + * Hexagon Global Registers + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/hexagon/hexagon.h" +#include "hw/hexagon/hexagon_globalreg.h" +#include "hw/core/qdev-properties.h" +#include "hw/core/sysbus.h" +#include "hw/core/resettable.h" +#include "migration/vmstate.h" +#include "qom/object.h" +#include "target/hexagon/cpu.h" +#include "target/hexagon/hex_regs.h" +#include "qemu/log.h" +#include "trace.h" +#include "qapi/error.h" + +#define IMMUTABLE (~0) +#define INVALID_REG_VAL 0xdeadbeef + +static const char *hex_sreg_names[] = { + [HEX_SREG_SGP0] = "sgp0", + [HEX_SREG_SGP1] = "sgp1", + [HEX_SREG_STID] = "stid", + [HEX_SREG_ELR] = "elr", + [HEX_SREG_BADVA0] = "badva0", + [HEX_SREG_BADVA1] = "badva1", + [HEX_SREG_SSR] = "ssr", + [HEX_SREG_CCR] = "ccr", + [HEX_SREG_HTID] = "htid", + [HEX_SREG_BADVA] = "badva", + [HEX_SREG_IMASK] = "imask", + [HEX_SREG_GEVB] = "gevb", + [HEX_SREG_EVB] = "evb", + [HEX_SREG_MODECTL] = "modectl", + [HEX_SREG_SYSCFG] = "syscfg", + [HEX_SREG_IPENDAD] = "ipendad", + [HEX_SREG_VID] = "vid", + [HEX_SREG_VID1] = "vid1", + [HEX_SREG_BESTWAIT] = "bestwait", + [HEX_SREG_IEL] = "iel", + [HEX_SREG_SCHEDCFG] = "schedcfg", + [HEX_SREG_IAHL] = "iahl", + [HEX_SREG_CFGBASE] = "cfgbase", + [HEX_SREG_DIAG] = "diag", + [HEX_SREG_REV] = "rev", + [HEX_SREG_PCYCLELO] = "pcyclelo", + [HEX_SREG_PCYCLEHI] = "pcyclehi", + [HEX_SREG_ISDBST] = "isdbst", + [HEX_SREG_ISDBCFG0] = "isdbcfg0", + [HEX_SREG_ISDBCFG1] = "isdbcfg1", + [HEX_SREG_LIVELOCK] = "livelock", + [HEX_SREG_BRKPTPC0] = "brkptpc0", + [HEX_SREG_BRKPTCFG0] = "brkptcfg0", + [HEX_SREG_BRKPTPC1] = "brkptpc1", + [HEX_SREG_BRKPTCFG1] = "brkptcfg1", + [HEX_SREG_ISDBMBXIN] = "isdbmbxin", + [HEX_SREG_ISDBMBXOUT] = "isdbmbxout", + [HEX_SREG_ISDBEN] = "isdben", + [HEX_SREG_ISDBGPR] = "isdbgpr", + [HEX_SREG_PMUCNT4] = "pmucnt4", + [HEX_SREG_PMUCNT5] = "pmucnt5", + [HEX_SREG_PMUCNT6] = "pmucnt6", + [HEX_SREG_PMUCNT7] = "pmucnt7", + [HEX_SREG_PMUCNT0] = "pmucnt0", + [HEX_SREG_PMUCNT1] = "pmucnt1", + [HEX_SREG_PMUCNT2] = "pmucnt2", + [HEX_SREG_PMUCNT3] = "pmucnt3", + [HEX_SREG_PMUEVTCFG] = "pmuevtcfg", + [HEX_SREG_PMUSTID0] = "pmustid0", + [HEX_SREG_PMUEVTCFG1] = "pmuevtcfg1", + [HEX_SREG_PMUSTID1] = "pmustid1", + [HEX_SREG_TIMERLO] = "timerlo", + [HEX_SREG_TIMERHI] = "timerhi", + [HEX_SREG_PMUCFG] = "pmucfg", + [HEX_SREG_S59] = "s59", + [HEX_SREG_S60] = "s60", + [HEX_SREG_S61] = "s61", + [HEX_SREG_S62] = "s62", + [HEX_SREG_S63] = "s63", +}; + +static const char *get_sreg_name(uint32_t reg) +{ + if (reg < ARRAY_SIZE(hex_sreg_names) && hex_sreg_names[reg]) { + return hex_sreg_names[reg]; + } + return "UNKNOWN"; +} + +/* Global system register mutability masks */ +static const uint32_t global_sreg_immut_masks[NUM_SREGS] = { + [HEX_SREG_EVB] = 0x000000ff, + [HEX_SREG_MODECTL] = IMMUTABLE, + [HEX_SREG_SYSCFG] = 0x80001c00, + [HEX_SREG_IPENDAD] = IMMUTABLE, + [HEX_SREG_VID] = 0xfc00fc00, + [HEX_SREG_VID1] = 0xfc00fc00, + [HEX_SREG_BESTWAIT] = 0xfffffe00, + [HEX_SREG_IAHL] = 0x00000000, + [HEX_SREG_SCHEDCFG] = 0xfffffee0, + [HEX_SREG_CFGBASE] = IMMUTABLE, + [HEX_SREG_DIAG] = 0x00000000, + [HEX_SREG_REV] = IMMUTABLE, + [HEX_SREG_ISDBST] = IMMUTABLE, + [HEX_SREG_ISDBCFG0] = 0xe0000000, + [HEX_SREG_BRKPTPC0] = 0x00000003, + [HEX_SREG_BRKPTCFG0] = 0xfc007000, + [HEX_SREG_BRKPTPC1] = 0x00000003, + [HEX_SREG_BRKPTCFG1] = 0xfc007000, + [HEX_SREG_ISDBMBXIN] = IMMUTABLE, + [HEX_SREG_ISDBMBXOUT] = 0x00000000, + [HEX_SREG_ISDBEN] = 0xfffffffe, + [HEX_SREG_TIMERLO] = IMMUTABLE, + [HEX_SREG_TIMERHI] = IMMUTABLE, +}; + +static void hexagon_globalreg_init(Object *obj) +{ + HexagonGlobalRegState *s = HEXAGON_GLOBALREG(obj); + + memset(s->regs, 0, sizeof(s->regs)); +} + +static inline uint32_t apply_write_mask(uint32_t new_val, uint32_t cur_val, + uint32_t reg_mask) +{ + if (reg_mask) { + return (new_val & ~reg_mask) | (cur_val & reg_mask); + } + return new_val; +} + +uint32_t hexagon_globalreg_read(HexagonGlobalRegState *s, uint32_t reg, + uint32_t htid) +{ + uint32_t value; + + if (!s) { + return 0; + } + g_assert(reg < NUM_SREGS); + g_assert(reg >= HEX_SREG_GLB_START); + + value = s->regs[reg]; + + trace_hexagon_globalreg_read(htid, get_sreg_name(reg), value); + return value; +} + +void hexagon_globalreg_write(HexagonGlobalRegState *s, uint32_t reg, + uint32_t value, uint32_t htid) +{ + if (!s) { + return; + } + g_assert(reg < NUM_SREGS); + g_assert(reg >= HEX_SREG_GLB_START); + s->regs[reg] = value; + trace_hexagon_globalreg_write(htid, get_sreg_name(reg), value); +} + +uint32_t hexagon_globalreg_masked_value(HexagonGlobalRegState *s, uint32_t reg, + uint32_t value) +{ + uint32_t reg_mask; + + if (!s) { + return value; + } + g_assert(reg < NUM_SREGS); + g_assert(reg >= HEX_SREG_GLB_START); + reg_mask = global_sreg_immut_masks[reg]; + return reg_mask == IMMUTABLE ? + s->regs[reg] : + apply_write_mask(value, s->regs[reg], reg_mask); +} + +void hexagon_globalreg_write_masked(HexagonGlobalRegState *s, uint32_t reg, + uint32_t value) +{ + if (!s) { + return; + } + s->regs[reg] = hexagon_globalreg_masked_value(s, reg, value); +} + +uint64_t hexagon_globalreg_get_pcycle_base(HexagonGlobalRegState *s) +{ + g_assert(s); + return s->g_pcycle_base; +} + +void hexagon_globalreg_set_pcycle_base(HexagonGlobalRegState *s, + uint64_t value) +{ + g_assert(s); + s->g_pcycle_base = value; +} + +static void do_hexagon_globalreg_reset(HexagonGlobalRegState *s) +{ + uint32_t isdben_val = 0; + + g_assert(s); + memset(s->regs, 0, sizeof(s->regs)); + + s->g_pcycle_base = 0; + + s->regs[HEX_SREG_EVB] = s->boot_evb; + s->regs[HEX_SREG_CFGBASE] = HEXAGON_CFG_ADDR_BASE(s->config_table_addr); + s->regs[HEX_SREG_REV] = s->dsp_rev; + + if (s->isdben_etm_enable) { + isdben_val |= (1 << 0); /* ETM enable bit */ + } + if (s->isdben_dfd_enable) { + isdben_val |= (1 << 1); /* DFD enable bit */ + } + if (s->isdben_trusted) { + isdben_val |= (1 << 2); /* Trusted bit */ + } + if (s->isdben_secure) { + isdben_val |= (1 << 3); /* Secure bit */ + } + s->regs[HEX_SREG_ISDBEN] = isdben_val; + s->regs[HEX_SREG_MODECTL] = 0x1; + + /* + * These register indices are placeholders in these arrays + * and their actual values are synthesized from state elsewhere. + * We can initialize these with invalid values so that if we + * mistakenly generate reads, they will look obviously wrong. + */ + s->regs[HEX_SREG_PCYCLELO] = INVALID_REG_VAL; + s->regs[HEX_SREG_PCYCLEHI] = INVALID_REG_VAL; + s->regs[HEX_SREG_TIMERLO] = INVALID_REG_VAL; + s->regs[HEX_SREG_TIMERHI] = INVALID_REG_VAL; + s->regs[HEX_SREG_PMUCNT0] = INVALID_REG_VAL; + s->regs[HEX_SREG_PMUCNT1] = INVALID_REG_VAL; + s->regs[HEX_SREG_PMUCNT2] = INVALID_REG_VAL; + s->regs[HEX_SREG_PMUCNT3] = INVALID_REG_VAL; + s->regs[HEX_SREG_PMUCNT4] = INVALID_REG_VAL; + s->regs[HEX_SREG_PMUCNT5] = INVALID_REG_VAL; + s->regs[HEX_SREG_PMUCNT6] = INVALID_REG_VAL; + s->regs[HEX_SREG_PMUCNT7] = INVALID_REG_VAL; +} + +static void hexagon_globalreg_reset_hold(Object *obj, ResetType type) +{ + HexagonGlobalRegState *s = HEXAGON_GLOBALREG(obj); + do_hexagon_globalreg_reset(s); +} + +static const VMStateDescription vmstate_hexagon_globalreg = { + .name = "hexagon_globalreg", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]){ + VMSTATE_UINT32_ARRAY(regs, HexagonGlobalRegState, NUM_SREGS), + VMSTATE_UINT64(g_pcycle_base, HexagonGlobalRegState), + VMSTATE_UINT32(boot_evb, HexagonGlobalRegState), + VMSTATE_UINT64(config_table_addr, HexagonGlobalRegState), + VMSTATE_UINT32(dsp_rev, HexagonGlobalRegState), + VMSTATE_BOOL(isdben_etm_enable, HexagonGlobalRegState), + VMSTATE_BOOL(isdben_dfd_enable, HexagonGlobalRegState), + VMSTATE_BOOL(isdben_trusted, HexagonGlobalRegState), + VMSTATE_BOOL(isdben_secure, HexagonGlobalRegState), + VMSTATE_END_OF_LIST() + } +}; + +static const Property hexagon_globalreg_properties[] = { + DEFINE_PROP_UINT32("boot-evb", HexagonGlobalRegState, boot_evb, 0x0), + DEFINE_PROP_UINT64("config-table-addr", HexagonGlobalRegState, + config_table_addr, 0xffffffffULL), + DEFINE_PROP_UINT32("dsp-rev", HexagonGlobalRegState, dsp_rev, 0), + DEFINE_PROP_BOOL("isdben-etm-enable", HexagonGlobalRegState, + isdben_etm_enable, false), + DEFINE_PROP_BOOL("isdben-dfd-enable", HexagonGlobalRegState, + isdben_dfd_enable, false), + DEFINE_PROP_BOOL("isdben-trusted", HexagonGlobalRegState, + isdben_trusted, false), + DEFINE_PROP_BOOL("isdben-secure", HexagonGlobalRegState, + isdben_secure, false), +}; + +static void hexagon_globalreg_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = hexagon_globalreg_reset_hold; + dc->vmsd = &vmstate_hexagon_globalreg; + dc->user_creatable = false; + device_class_set_props(dc, hexagon_globalreg_properties); +} + +static const TypeInfo hexagon_globalreg_info = { + .name = TYPE_HEXAGON_GLOBALREG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HexagonGlobalRegState), + .instance_init = hexagon_globalreg_init, + .class_init = hexagon_globalreg_class_init, +}; + +static void hexagon_globalreg_register_types(void) +{ + type_register_static(&hexagon_globalreg_info); +} + +type_init(hexagon_globalreg_register_types)
diff --git a/hw/hexagon/hexagon_tlb.c b/hw/hexagon/hexagon_tlb.c new file mode 100644 index 0000000..b6d4aff --- /dev/null +++ b/hw/hexagon/hexagon_tlb.c
@@ -0,0 +1,467 @@ +/* + * Hexagon TLB QOM Device + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/hexagon/hexagon_tlb.h" +#include "hw/core/qdev-properties.h" +#include "hw/core/resettable.h" +#include "migration/vmstate.h" +#include "monitor/monitor.h" +#include "qapi/error.h" +#include "exec/page-protection.h" +#include "exec/target_page.h" +#include "target/hexagon/cpu.h" +#include "target/hexagon/cpu_bits.h" + +/* PTE (TLB entry) field extraction */ +#define GET_PTE_PPD(entry) extract64((entry), 0, 24) +#define GET_PTE_C(entry) extract64((entry), 24, 4) +#define GET_PTE_U(entry) extract64((entry), 28, 1) +#define GET_PTE_R(entry) extract64((entry), 29, 1) +#define GET_PTE_W(entry) extract64((entry), 30, 1) +#define GET_PTE_X(entry) extract64((entry), 31, 1) +#define GET_PTE_VPN(entry) extract64((entry), 32, 20) +#define GET_PTE_ASID(entry) extract64((entry), 52, 7) +#define GET_PTE_ATR0(entry) extract64((entry), 59, 1) +#define GET_PTE_ATR1(entry) extract64((entry), 60, 1) +#define GET_PTE_PA35(entry) extract64((entry), 61, 1) +#define GET_PTE_G(entry) extract64((entry), 62, 1) +#define GET_PTE_V(entry) extract64((entry), 63, 1) + +/* PPD (physical page descriptor) */ +static inline uint64_t GET_PPD(uint64_t entry) +{ + return GET_PTE_PPD(entry) | (GET_PTE_PA35(entry) << 24); +} + +#define NO_ASID (1 << 8) + +typedef enum { + PGSIZE_4K, + PGSIZE_16K, + PGSIZE_64K, + PGSIZE_256K, + PGSIZE_1M, + PGSIZE_4M, + PGSIZE_16M, + PGSIZE_64M, + PGSIZE_256M, + PGSIZE_1G, +} tlb_pgsize_t; + +#define NUM_PGSIZE_TYPES (PGSIZE_1G + 1) + +static const char *pgsize_str[NUM_PGSIZE_TYPES] = { + "4K", + "16K", + "64K", + "256K", + "1M", + "4M", + "16M", + "64M", + "256M", + "1G", +}; + +#define INVALID_MASK 0xffffffffLL + +static const uint64_t encmask_2_mask[] = { + 0x0fffLL, /* 4k, 0000 */ + 0x3fffLL, /* 16k, 0001 */ + 0xffffLL, /* 64k, 0010 */ + 0x3ffffLL, /* 256k, 0011 */ + 0xfffffLL, /* 1m, 0100 */ + 0x3fffffLL, /* 4m, 0101 */ + 0xffffffLL, /* 16m, 0110 */ + 0x3ffffffLL, /* 64m, 0111 */ + 0xfffffffLL, /* 256m, 1000 */ + 0x3fffffffLL, /* 1g, 1001 */ + INVALID_MASK, /* RSVD, 1010 */ +}; + +static inline tlb_pgsize_t hex_tlb_pgsize_type(uint64_t entry) +{ + if (entry == 0) { + qemu_log_mask(CPU_LOG_MMU, "%s: Supplied TLB entry was 0!\n", + __func__); + return 0; + } + tlb_pgsize_t size = ctz64(entry); + g_assert(size < NUM_PGSIZE_TYPES); + return size; +} + +static inline uint64_t hex_tlb_page_size_bytes(uint64_t entry) +{ + return 1ull << (qemu_target_page_bits() + 2 * hex_tlb_pgsize_type(entry)); +} + +static inline uint64_t hex_tlb_phys_page_num(uint64_t entry) +{ + uint32_t ppd = GET_PPD(entry); + return ppd >> 1; +} + +static inline uint64_t hex_tlb_phys_addr(uint64_t entry) +{ + uint64_t pagemask = encmask_2_mask[hex_tlb_pgsize_type(entry)]; + uint64_t pagenum = hex_tlb_phys_page_num(entry); + uint64_t PA = (pagenum << qemu_target_page_bits()) & (~pagemask); + return PA; +} + +static inline uint64_t hex_tlb_virt_addr(uint64_t entry) +{ + return (uint64_t)GET_PTE_VPN(entry) << qemu_target_page_bits(); +} + +bool hexagon_tlb_dump_entry(Monitor *mon, uint64_t entry) +{ + if (GET_PTE_V(entry)) { + uint64_t PA = hex_tlb_phys_addr(entry); + uint64_t VA = hex_tlb_virt_addr(entry); + monitor_printf(mon, "0x%016" PRIx64 ": ", entry); + monitor_printf(mon, "V:%" PRId64 " G:%" PRId64 + " A1:%" PRId64 " A0:%" PRId64, + GET_PTE_V(entry), + GET_PTE_G(entry), + GET_PTE_ATR1(entry), + GET_PTE_ATR0(entry)); + monitor_printf(mon, " ASID:0x%02" PRIx64 " VA:0x%08" PRIx64, + GET_PTE_ASID(entry), VA); + monitor_printf(mon, + " X:%" PRId64 " W:%" PRId64 " R:%" PRId64 + " U:%" PRId64 " C:%" PRId64, + GET_PTE_X(entry), + GET_PTE_W(entry), + GET_PTE_R(entry), + GET_PTE_U(entry), + GET_PTE_C(entry)); + monitor_printf(mon, " PA:0x%09" PRIx64 " SZ:%s (0x%" PRIx64 ")", + PA, pgsize_str[hex_tlb_pgsize_type(entry)], + hex_tlb_page_size_bytes(entry)); + monitor_printf(mon, "\n"); + return true; + } + + /* Not valid */ + return false; +} + +static inline bool hex_tlb_entry_match_noperm(uint64_t entry, uint32_t asid, + uint64_t VA) +{ + if (GET_PTE_V(entry)) { + if (GET_PTE_G(entry)) { + /* Global entry - ignore ASID */ + } else if (asid != NO_ASID) { + uint32_t tlb_asid = GET_PTE_ASID(entry); + if (tlb_asid != asid) { + return false; + } + } + + uint64_t page_size = hex_tlb_page_size_bytes(entry); + uint64_t page_start = + ROUND_DOWN(hex_tlb_virt_addr(entry), page_size); + if (page_start <= VA && VA < page_start + page_size) { + return true; + } + } + return false; +} + +static inline void hex_tlb_entry_get_perm(uint64_t entry, + MMUAccessType access_type, + int mmu_idx, int *prot, + int32_t *excp, int *cause_code) +{ + bool perm_x = GET_PTE_X(entry); + bool perm_w = GET_PTE_W(entry); + bool perm_r = GET_PTE_R(entry); + bool perm_u = GET_PTE_U(entry); + bool user_idx = mmu_idx == MMU_USER_IDX; + + if (mmu_idx == MMU_KERNEL_IDX) { + *prot = PAGE_VALID | PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return; + } + + *prot = PAGE_VALID; + switch (access_type) { + case MMU_INST_FETCH: + if (user_idx && !perm_u) { + *excp = HEX_EVENT_PRECISE; + *cause_code = HEX_CAUSE_FETCH_NO_UPAGE; + } else if (!perm_x) { + *excp = HEX_EVENT_PRECISE; + *cause_code = HEX_CAUSE_FETCH_NO_XPAGE; + } + break; + case MMU_DATA_LOAD: + if (user_idx && !perm_u) { + *excp = HEX_EVENT_PRECISE; + *cause_code = HEX_CAUSE_PRIV_NO_UREAD; + } else if (!perm_r) { + *excp = HEX_EVENT_PRECISE; + *cause_code = HEX_CAUSE_PRIV_NO_READ; + } + break; + case MMU_DATA_STORE: + if (user_idx && !perm_u) { + *excp = HEX_EVENT_PRECISE; + *cause_code = HEX_CAUSE_PRIV_NO_UWRITE; + } else if (!perm_w) { + *excp = HEX_EVENT_PRECISE; + *cause_code = HEX_CAUSE_PRIV_NO_WRITE; + } + break; + } + + if (!user_idx || perm_u) { + if (perm_x) { + *prot |= PAGE_EXEC; + } + if (perm_r) { + *prot |= PAGE_READ; + } + if (perm_w) { + *prot |= PAGE_WRITE; + } + } +} + +static inline bool hex_tlb_entry_match(uint64_t entry, uint8_t asid, + uint32_t VA, + MMUAccessType access_type, hwaddr *PA, + int *prot, uint64_t *size, + int32_t *excp, int *cause_code, + int mmu_idx) +{ + if (hex_tlb_entry_match_noperm(entry, asid, VA)) { + hex_tlb_entry_get_perm(entry, access_type, mmu_idx, prot, excp, + cause_code); + *PA = hex_tlb_phys_addr(entry); + *size = hex_tlb_page_size_bytes(entry); + return true; + } + return false; +} + +static bool hex_tlb_is_match(uint64_t entry1, uint64_t entry2, + bool consider_gbit) +{ + bool valid1 = GET_PTE_V(entry1); + bool valid2 = GET_PTE_V(entry2); + uint64_t size1 = hex_tlb_page_size_bytes(entry1); + uint64_t vaddr1 = ROUND_DOWN(hex_tlb_virt_addr(entry1), size1); + uint64_t size2 = hex_tlb_page_size_bytes(entry2); + uint64_t vaddr2 = ROUND_DOWN(hex_tlb_virt_addr(entry2), size2); + int asid1 = GET_PTE_ASID(entry1); + int asid2 = GET_PTE_ASID(entry2); + bool gbit1 = GET_PTE_G(entry1); + bool gbit2 = GET_PTE_G(entry2); + + if (!valid1 || !valid2) { + return false; + } + + if (((vaddr1 <= vaddr2) && (vaddr2 < (vaddr1 + size1))) || + ((vaddr2 <= vaddr1) && (vaddr1 < (vaddr2 + size2)))) { + if (asid1 == asid2) { + return true; + } + if ((consider_gbit && gbit1) || gbit2) { + return true; + } + } + return false; +} + +/* Public API */ + +uint64_t hexagon_tlb_read(HexagonTLBState *tlb, uint32_t index) +{ + g_assert(index < tlb->num_entries); + return tlb->entries[index]; +} + +void hexagon_tlb_write(HexagonTLBState *tlb, uint32_t index, uint64_t value) +{ + g_assert(index < tlb->num_entries); + tlb->entries[index] = value; +} + +bool hexagon_tlb_find_match(HexagonTLBState *tlb, uint32_t asid, + uint32_t VA, MMUAccessType access_type, + hwaddr *PA, int *prot, uint64_t *size, + int32_t *excp, int *cause_code, int mmu_idx) +{ + *PA = 0; + *prot = 0; + *size = 0; + *excp = 0; + *cause_code = 0; + + for (uint32_t i = 0; i < tlb->num_entries; i++) { + if (hex_tlb_entry_match(tlb->entries[i], asid, VA, access_type, + PA, prot, size, excp, cause_code, mmu_idx)) { + return true; + } + } + return false; +} + +uint32_t hexagon_tlb_lookup(HexagonTLBState *tlb, uint32_t asid, + uint32_t VA, int *cause_code) +{ + uint32_t not_found = 0x80000000; + uint32_t idx = not_found; + + for (uint32_t i = 0; i < tlb->num_entries; i++) { + uint64_t entry = tlb->entries[i]; + if (hex_tlb_entry_match_noperm(entry, asid, VA)) { + if (idx != not_found) { + *cause_code = HEX_CAUSE_IMPRECISE_MULTI_TLB_MATCH; + break; + } + idx = i; + } + } + + if (idx == not_found) { + qemu_log_mask(CPU_LOG_MMU, + "%s: 0x%" PRIx32 ", 0x%08" PRIx32 " => NOT FOUND\n", + __func__, asid, VA); + } else { + qemu_log_mask(CPU_LOG_MMU, + "%s: 0x%" PRIx32 ", 0x%08" PRIx32 " => %d\n", + __func__, asid, VA, idx); + } + + return idx; +} + +/* + * Return codes: + * 0 or positive index of match + * -1 multiple matches + * -2 no match + */ +int hexagon_tlb_check_overlap(HexagonTLBState *tlb, uint64_t entry, + uint64_t index) +{ + int matches = 0; + int last_match = 0; + + for (uint32_t i = 0; i < tlb->num_entries; i++) { + if (hex_tlb_is_match(entry, tlb->entries[i], false)) { + matches++; + last_match = i; + } + } + + if (matches == 1) { + return last_match; + } + if (matches == 0) { + return -2; + } + return -1; +} + +void hexagon_tlb_dump(Monitor *mon, HexagonTLBState *tlb) +{ + for (uint32_t i = 0; i < tlb->num_entries; i++) { + hexagon_tlb_dump_entry(mon, tlb->entries[i]); + } +} + +uint32_t hexagon_tlb_get_num_entries(HexagonTLBState *tlb) +{ + return tlb->num_entries; +} + +/* QOM lifecycle */ + +static void hexagon_tlb_init(Object *obj) +{ +} + +static void hexagon_tlb_realize(DeviceState *dev, Error **errp) +{ + HexagonTLBState *s = HEXAGON_TLB(dev); + + if (s->num_entries == 0 || s->num_entries > MAX_TLB_ENTRIES) { + error_setg(errp, "Invalid TLB num-entries: %" PRIu32, + s->num_entries); + return; + } + s->entries = g_new0(uint64_t, s->num_entries); +} + +static void hexagon_tlb_unrealize(DeviceState *dev) +{ + HexagonTLBState *s = HEXAGON_TLB(dev); + g_free(s->entries); + s->entries = NULL; +} + +static void hexagon_tlb_reset_hold(Object *obj, ResetType type) +{ + HexagonTLBState *s = HEXAGON_TLB(obj); + if (s->entries) { + memset(s->entries, 0, sizeof(uint64_t) * s->num_entries); + } +} + +static const VMStateDescription vmstate_hexagon_tlb = { + .name = "hexagon-tlb", + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(num_entries, HexagonTLBState), + VMSTATE_VARRAY_UINT32_ALLOC(entries, HexagonTLBState, num_entries, + 0, vmstate_info_uint64, uint64_t), + VMSTATE_END_OF_LIST() + }, +}; + +static const Property hexagon_tlb_properties[] = { + DEFINE_PROP_UINT32("num-entries", HexagonTLBState, num_entries, + MAX_TLB_ENTRIES), +}; + +static void hexagon_tlb_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->realize = hexagon_tlb_realize; + dc->unrealize = hexagon_tlb_unrealize; + rc->phases.hold = hexagon_tlb_reset_hold; + dc->vmsd = &vmstate_hexagon_tlb; + dc->user_creatable = false; + device_class_set_props(dc, hexagon_tlb_properties); +} + +static const TypeInfo hexagon_tlb_info = { + .name = TYPE_HEXAGON_TLB, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HexagonTLBState), + .instance_init = hexagon_tlb_init, + .class_init = hexagon_tlb_class_init, +}; + +static void hexagon_tlb_register_types(void) +{ + type_register_static(&hexagon_tlb_info); +} + +type_init(hexagon_tlb_register_types)
diff --git a/hw/hexagon/machine_cfg_sa8775_cdsp0.h.inc b/hw/hexagon/machine_cfg_sa8775_cdsp0.h.inc new file mode 100644 index 0000000..442cbe3 --- /dev/null +++ b/hw/hexagon/machine_cfg_sa8775_cdsp0.h.inc
@@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +static const struct hexagon_machine_config SA8775P_cdsp0 = { + .cfgbase = 0x24000000 + 0x180000, + .l2tcm_size = 0x00000000, + .l2vic_base = 0x26300000 + 0x90000, + .l2vic_size = 0x00001000, + .csr_base = 0x26300000, + .qtmr_region = 0x26300000 + 0xA1000, + .cfgtable = { + .l2tcm_base = 0x00002400, + .reserved0 = 0x00000000, + .subsystem_base = 0x00002638, + .etm_base = 0x00002419, + .l2cfg_base = 0x0000241a, + .reserved1 = 0x0000241b, + .l1s0_base = 0x00002500, + .axi2_lowaddr = 0x00000000, + .streamer_base = 0x00000000, + .reserved2 = 0x00000000, + .fastl2vic_base = 0x0000241e, + .jtlb_size_entries = 0x00000080, + .coproc_present = 0x00000001, + .ext_contexts = 0x00000004, + .vtcm_base = 0x00002500, + .vtcm_size_kb = 0x00002000, + .l2tag_size = 0x00000400, + .l2ecomem_size = 0x00000000, + .thread_enable_mask = 0x0000003f, + .eccreg_base = 0x0000241f, + .l2line_size = 0x00000080, + .tiny_core = 0x00000000, + .l2itcm_size = 0x00000000, + .l2itcm_base = 0x00002400, + .reserved3 = 0x00000000, + .dtm_present = 0x00000000, + .dma_version = 0x00000003, + .hvx_vec_log_length = 0x00000007, + .core_id = 0x00000000, + .core_count = 0x00000000, + .coproc2_reg0 = 0x00000040, + .coproc2_reg1 = 0x00000020, + .v2x_mode = 0x00000001, + .coproc2_reg2 = 0x00000008, + .coproc2_reg3 = 0x00000020, + .coproc2_reg4 = 0x00000000, + .coproc2_reg5 = 0x00000002, + .coproc2_reg6 = 0x00000016, + .coproc2_reg7 = 0x00000006, + .acd_preset = 0x00000001, + .mnd_preset = 0x00000000, + .l1d_size_kb = 0x00000010, + .l1i_size_kb = 0x00000020, + .l1d_write_policy = 0x00000002, + .vtcm_bank_width = 0x00000080, + .reserved4 = 0x00000001, + .reserved5 = 0x00000000, + .reserved6 = 0x00000003, + .coproc2_cvt_mpy_size = 0x0000000a, + .consistency_domain = 0x000000e0, + .capacity_domain = 0x00000080, + .axi3_lowaddr = 0x00000000, + }, +};
diff --git a/hw/hexagon/machine_cfg_v66g_1024.h.inc b/hw/hexagon/machine_cfg_v66g_1024.h.inc new file mode 100644 index 0000000..cc4d89b --- /dev/null +++ b/hw/hexagon/machine_cfg_v66g_1024.h.inc
@@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +static const struct hexagon_machine_config v66g_1024 = { + .cfgbase = 0xd8180000, + .l2tcm_size = 0x00000000, + .l2vic_base = 0xfc910000, + .l2vic_size = 0x00001000, + .csr_base = 0xfc900000, + .qtmr_region = 0xfc921000, + .cfgtable = { + .l2tcm_base = 0x0000d800, + .reserved0 = 0x0000d400, + .subsystem_base = 0x0000fc90, + .etm_base = 0x0000d805, + .l2cfg_base = 0x0000d81a, + .reserved1 = 0x00000000, + .l1s0_base = 0x0000d820, + .axi2_lowaddr = 0x00003000, + .streamer_base = 0x00000000, + .reserved2 = 0x0000d819, + .fastl2vic_base = 0x0000d81e, + .jtlb_size_entries = 0x00000080, + .coproc_present = 0x00000001, + .ext_contexts = 0x00000004, + .vtcm_base = 0x0000d820, + .vtcm_size_kb = 0x00000100, + .l2tag_size = 0x00000400, + .l2ecomem_size = 0x00000400, + .thread_enable_mask = 0x0000000f, + .eccreg_base = 0x0000d81f, + .l2line_size = 0x00000080, + .tiny_core = 0x00000000, + .l2itcm_size = 0x00000000, + .l2itcm_base = 0x0000d820, + .reserved3 = 0x00000000, + .dtm_present = 0x00000000, + .dma_version = 0x00000000, + .hvx_vec_log_length = 0x00000080, + .core_id = 0x00000000, + .core_count = 0x00000000, + .coproc2_reg0 = 0x00000000, + .coproc2_reg1 = 0x00000000, + .v2x_mode = 0x00000000, + .coproc2_reg2 = 0x00000000, + .coproc2_reg3 = 0x00000000, + .coproc2_reg4 = 0x00000000, + .coproc2_reg5 = 0x00000000, + .coproc2_reg6 = 0x00000000, + .coproc2_reg7 = 0x00000000, + .acd_preset = 0x00000000, + .mnd_preset = 0x00000000, + .l1d_size_kb = 0x00000000, + .l1i_size_kb = 0x00000000, + .l1d_write_policy = 0x00000000, + .vtcm_bank_width = 0x00000000, + .reserved4 = 0x00000000, + .reserved5 = 0x00000000, + .reserved6 = 0x00000000, + .coproc2_cvt_mpy_size = 0x00000000, + .consistency_domain = 0x00000000, + .capacity_domain = 0x00000000, + .axi3_lowaddr = 0x00000000, + }, +};
diff --git a/hw/hexagon/machine_cfg_v68n_1024.h.inc b/hw/hexagon/machine_cfg_v68n_1024.h.inc new file mode 100644 index 0000000..0c79689 --- /dev/null +++ b/hw/hexagon/machine_cfg_v68n_1024.h.inc
@@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +static const struct hexagon_machine_config v68n_1024 = { + .cfgbase = 0xde000000, + .l2tcm_size = 0x00000000, + .l2vic_base = 0xfc910000, + .l2vic_size = 0x00001000, + .csr_base = 0xfc900000, + .qtmr_region = 0xfc921000, + .cfgtable = { + .l2tcm_base = 0x0000d800, + .reserved0 = 0x00000000, + .subsystem_base = 0x0000fc90, + .etm_base = 0x0000d819, + .l2cfg_base = 0x0000d81a, + .reserved1 = 0x00000000, + .l1s0_base = 0x0000d840, + .axi2_lowaddr = 0x00003000, + .streamer_base = 0x0000d81c, + .reserved2 = 0x0000d81d, + .fastl2vic_base = 0x0000d81e, + .jtlb_size_entries = 0x00000080, + .coproc_present = 0x00000001, + .ext_contexts = 0x00000004, + .vtcm_base = 0x0000d840, + .vtcm_size_kb = 0x00001000, + .l2tag_size = 0x00000400, + .l2ecomem_size = 0x00000400, + .thread_enable_mask = 0x0000003f, + .eccreg_base = 0x0000d81f, + .l2line_size = 0x00000080, + .tiny_core = 0x00000000, + .l2itcm_size = 0x00000000, + .l2itcm_base = 0x0000d820, + .reserved3 = 0x00000000, + .dtm_present = 0x00000000, + .dma_version = 0x00000001, + .hvx_vec_log_length = 0x00000007, + .core_id = 0x00000000, + .core_count = 0x00000000, + .coproc2_reg0 = 0x00000040, + .coproc2_reg1 = 0x00000020, + .v2x_mode = 0x1f1f1f1f, + .coproc2_reg2 = 0x1f1f1f1f, + .coproc2_reg3 = 0x1f1f1f1f, + .coproc2_reg4 = 0x1f1f1f1f, + .coproc2_reg5 = 0x1f1f1f1f, + .coproc2_reg6 = 0x1f1f1f1f, + .coproc2_reg7 = 0x1f1f1f1f, + .acd_preset = 0x1f1f1f1f, + .mnd_preset = 0x1f1f1f1f, + .l1d_size_kb = 0x1f1f1f1f, + .l1i_size_kb = 0x1f1f1f1f, + .l1d_write_policy = 0x1f1f1f1f, + .vtcm_bank_width = 0x1f1f1f1f, + .reserved4 = 0x1f1f1f1f, + .reserved5 = 0x1f1f1f1f, + .reserved6 = 0x1f1f1f1f, + .coproc2_cvt_mpy_size = 0x1f1f1f1f, + .consistency_domain = 0x1f1f1f1f, + .capacity_domain = 0x1f1f1f1f, + .axi3_lowaddr = 0x1f1f1f1f, + }, +};
diff --git a/hw/hexagon/meson.build b/hw/hexagon/meson.build new file mode 100644 index 0000000..bade3a3 --- /dev/null +++ b/hw/hexagon/meson.build
@@ -0,0 +1,7 @@ +hexagon_ss = ss.source_set() +hexagon_ss.add(files('hexagon_tlb.c')) +hexagon_ss.add(files('hexagon_globalreg.c')) +hexagon_ss.add(when: 'CONFIG_HEX_DSP', if_true: files('hexagon_dsp.c')) +hexagon_ss.add(when: 'CONFIG_HEX_VIRT', if_true: files('virt.c')) + +hw_arch += {'hexagon': hexagon_ss}
diff --git a/hw/hexagon/trace-events b/hw/hexagon/trace-events new file mode 100644 index 0000000..5d623ed --- /dev/null +++ b/hw/hexagon/trace-events
@@ -0,0 +1,3 @@ +# Hexagon global register access +hexagon_globalreg_read(uint32_t htid, const char *reg_name, uint32_t value) "htid=%u reg=%s value=0x%x" +hexagon_globalreg_write(uint32_t htid, const char *reg_name, uint32_t value) "htid=%u reg=%s value=0x%x"
diff --git a/hw/hexagon/trace.h b/hw/hexagon/trace.h new file mode 100644 index 0000000..9e0b39f --- /dev/null +++ b/hw/hexagon/trace.h
@@ -0,0 +1,2 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "trace/trace-hw_hexagon.h"
diff --git a/hw/hexagon/virt.c b/hw/hexagon/virt.c new file mode 100644 index 0000000..a363899 --- /dev/null +++ b/hw/hexagon/virt.c
@@ -0,0 +1,347 @@ +/* + * Hexagon virt emulation + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/hexagon/virt.h" +#include "elf.h" +#include "hw/char/pl011.h" +#include "hw/core/clock.h" +#include "hw/core/sysbus-fdt.h" +#include "hw/hexagon/hexagon.h" +#include "hw/hexagon/hexagon_globalreg.h" +#include "hw/hexagon/hexagon_tlb.h" +#include "hw/core/loader.h" +#include "hw/core/qdev-properties.h" +#include "hw/core/qdev-clock.h" +#include "hw/core/register.h" +#include "qemu/error-report.h" +#include "qemu/guest-random.h" +#include "qemu/units.h" +#include "machine_cfg_v68n_1024.h.inc" +#include "system/address-spaces.h" +#include "system/device_tree.h" +#include "system/reset.h" +#include "system/system.h" +#include <libfdt.h> + +enum { + VIRT_UART0, + VIRT_FDT, +}; + +static const MemMapEntry base_memmap[] = { + [VIRT_UART0] = { 0x10000000, 0x00000200 }, + [VIRT_FDT] = { 0x99800000, 0x00400000 }, +}; + + +static void create_fdt(HexagonVirtMachineState *vms) +{ + MachineState *ms = MACHINE(vms); + void *fdt = create_device_tree(&vms->fdt_size); + uint8_t rng_seed[32]; + + if (!fdt) { + error_report("create_device_tree() failed"); + exit(1); + } + + ms->fdt = fdt; + + qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2); + qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x1); + qemu_fdt_setprop_string(fdt, "/", "model", "hexagon-virt,qemu"); + qemu_fdt_setprop_string(fdt, "/", "compatible", "qcom,sm8150"); + + qemu_fdt_add_subnode(fdt, "/soc"); + qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2); + qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x1); + qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0); + + qemu_fdt_add_subnode(fdt, "/chosen"); + qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); + qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); +} + +static void fdt_add_hvx(HexagonVirtMachineState *vms, + const struct hexagon_machine_config *m_cfg) +{ + const MachineState *ms = MACHINE(vms); + uint32_t vtcm_size_bytes = m_cfg->cfgtable.vtcm_size_kb * 1024; + if (vtcm_size_bytes > 0) { + memory_region_init_ram(&vms->vtcm, NULL, "vtcm.ram", vtcm_size_bytes, + &error_fatal); + memory_region_add_subregion(vms->sys, m_cfg->cfgtable.vtcm_base << 16, + &vms->vtcm); + + qemu_fdt_add_subnode(ms->fdt, "/soc/vtcm"); + qemu_fdt_setprop_string(ms->fdt, "/soc/vtcm", "compatible", + "qcom,hexagon_vtcm"); + + assert(sizeof(m_cfg->cfgtable.vtcm_base) == sizeof(uint32_t)); + qemu_fdt_setprop_cells(ms->fdt, "/soc/vtcm", "reg", 0, + m_cfg->cfgtable.vtcm_base << 16, + vtcm_size_bytes); + } + + if (m_cfg->cfgtable.ext_contexts > 0) { + qemu_fdt_add_subnode(ms->fdt, "/soc/hvx"); + qemu_fdt_setprop_string(ms->fdt, "/soc/hvx", "compatible", + "qcom,hexagon-hvx"); + qemu_fdt_setprop_cells(ms->fdt, "/soc/hvx", "qcom,hvx-max-ctxts", + m_cfg->cfgtable.ext_contexts); + qemu_fdt_setprop_cells(ms->fdt, "/soc/hvx", "qcom,hvx-vlength", + m_cfg->cfgtable.hvx_vec_log_length); + } +} + +static int32_t fdt_add_clocks(const HexagonVirtMachineState *vms) +{ + MachineState *ms = MACHINE(vms); + int32_t clk_phandle = qemu_fdt_alloc_phandle(ms->fdt); + + qemu_fdt_add_subnode(ms->fdt, "/apb-pclk"); + qemu_fdt_setprop_string(ms->fdt, "/apb-pclk", "compatible", "fixed-clock"); + qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "#clock-cells", 0x0); + qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "clock-frequency", 24000000); + qemu_fdt_setprop_string(ms->fdt, "/apb-pclk", "clock-output-names", + "clk24mhz"); + qemu_fdt_setprop_cell(ms->fdt, "/apb-pclk", "phandle", clk_phandle); + + return clk_phandle; +} + +static void fdt_add_uart(const HexagonVirtMachineState *vms, int uart, + int32_t clk_phandle) +{ + char *nodename; + hwaddr base = base_memmap[uart].base; + hwaddr size = base_memmap[uart].size; + assert(uart == 0); + const char compat[] = "arm,pl011\0arm,primecell"; + const char clocknames[] = "uartclk\0apb_pclk"; + MachineState *ms = MACHINE(vms); + DeviceState *dev; + SysBusDevice *s; + + dev = qdev_new(TYPE_PL011); + s = SYS_BUS_DEVICE(dev); + qdev_prop_set_chr(dev, "chardev", serial_hd(0)); + qdev_connect_clock_in(dev, "clk", vms->apb_clk); + sysbus_realize_and_unref(s, &error_fatal); + sysbus_mmio_map(s, 0, base); + + nodename = g_strdup_printf("/pl011@%" PRIx64, base); + qemu_fdt_add_subnode(ms->fdt, nodename); + + /* Note that we can't use setprop_string because of the embedded NUL */ + qemu_fdt_setprop(ms->fdt, nodename, "compatible", compat, sizeof(compat)); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0, base, size); + qemu_fdt_setprop_cells(ms->fdt, nodename, "clocks", clk_phandle, + clk_phandle); + qemu_fdt_setprop(ms->fdt, nodename, "clock-names", clocknames, + sizeof(clocknames)); + + qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename); + qemu_fdt_add_subnode(ms->fdt, "/aliases"); + qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial0", nodename); + + g_free(nodename); +} + +static void fdt_add_cpu_nodes(const HexagonVirtMachineState *vms) +{ + MachineState *ms = MACHINE(vms); + qemu_fdt_add_subnode(ms->fdt, "/cpus"); + qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#address-cells", 0x1); + qemu_fdt_setprop_cell(ms->fdt, "/cpus", "#size-cells", 0x0); + + /* cpu nodes */ + for (int num = ms->smp.cpus - 1; num >= 0; num--) { + char *nodename = g_strdup_printf("/cpus/cpu@%d", num); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "cpu"); + qemu_fdt_setprop_cell(ms->fdt, nodename, "reg", num); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", + qemu_fdt_alloc_phandle(ms->fdt)); + g_free(nodename); + } +} + + + +static void virt_instance_init(Object *obj) +{ + HexagonVirtMachineState *vms = HEXAGON_VIRT_MACHINE(obj); + + create_fdt(vms); +} + +void hexagon_load_fdt(const HexagonVirtMachineState *vms) +{ + MachineState *ms = MACHINE(vms); + hwaddr fdt_addr = base_memmap[VIRT_FDT].base; + uint32_t fdtsize = vms->fdt_size; + + g_assert(fdtsize <= base_memmap[VIRT_FDT].size); + /* copy in the device tree */ + rom_add_blob_fixed_as("fdt", ms->fdt, fdtsize, fdt_addr, + &address_space_memory); + qemu_register_reset_nosnapshotload( + qemu_fdt_randomize_seeds, + rom_ptr_for_as(&address_space_memory, fdt_addr, fdtsize)); +} + +static uint64_t load_kernel(const HexagonVirtMachineState *vms) +{ + MachineState *ms = MACHINE(vms); + uint64_t entry = 0; + if (load_elf_ram_sym(ms->kernel_filename, NULL, NULL, NULL, &entry, NULL, + NULL, NULL, 0, EM_HEXAGON, 0, 0, &address_space_memory, + false, NULL) > 0) { + return entry; + } + error_report("error loading '%s'", ms->kernel_filename); + exit(1); +} + +static uint64_t load_bios(HexagonVirtMachineState *vms) +{ + MachineState *ms = MACHINE(vms); + uint64_t bios_addr = 0x0; /* Load BIOS at reset vector address 0x0 */ + int bios_size; + + bios_size = load_image_targphys(ms->firmware ?: "", + bios_addr, 64 * 1024, NULL); + if (bios_size < 0) { + error_report("Could not load BIOS '%s'", ms->firmware ?: ""); + exit(1); + } + + return bios_addr; /* Return entry point at address 0x0 */ +} + +static void do_cpu_reset(void *opaque) +{ + HexagonCPU *cpu = opaque; + CPUState *cs = CPU(cpu); + cpu_reset(cs); +} + +static void virt_init(MachineState *ms) +{ + HexagonVirtMachineState *vms = HEXAGON_VIRT_MACHINE(ms); + const struct hexagon_machine_config *m_cfg = &v68n_1024; + DeviceState *gsregs_dev; + DeviceState *tlb_dev; + DeviceState *cpu0; + int32_t clk_phandle; + + qemu_fdt_setprop_string(ms->fdt, "/chosen", "bootargs", ms->kernel_cmdline); + + vms->sys = get_system_memory(); + + /* Create APB clock for peripherals */ + vms->apb_clk = clock_new(OBJECT(ms), "apb-pclk"); + clock_set_hz(vms->apb_clk, 24000000); + + memory_region_init_ram(&vms->parent_obj.ram, NULL, "ddr.ram", + ms->ram_size, &error_fatal); + memory_region_add_subregion(vms->sys, 0x0, &vms->parent_obj.ram); + + if (m_cfg->l2tcm_size) { + memory_region_init_ram(&vms->tcm, NULL, "tcm.ram", m_cfg->l2tcm_size, + &error_fatal); + memory_region_add_subregion(vms->sys, m_cfg->cfgtable.l2tcm_base << 16, + &vms->tcm); + } + + memory_region_init_rom(&vms->parent_obj.cfgtable_rom, NULL, + "config_table.rom", sizeof(m_cfg->cfgtable), + &error_fatal); + memory_region_add_subregion(vms->sys, m_cfg->cfgbase, + &vms->parent_obj.cfgtable_rom); + fdt_add_hvx(vms, m_cfg); + + gsregs_dev = qdev_new(TYPE_HEXAGON_GLOBALREG); + object_property_add_child(OBJECT(ms), "global-regs", OBJECT(gsregs_dev)); + qdev_prop_set_uint64(gsregs_dev, "config-table-addr", m_cfg->cfgbase); + qdev_prop_set_uint32(gsregs_dev, "dsp-rev", v68_rev); + sysbus_realize_and_unref(SYS_BUS_DEVICE(gsregs_dev), &error_fatal); + + tlb_dev = qdev_new(TYPE_HEXAGON_TLB); + object_property_add_child(OBJECT(ms), "tlb", OBJECT(tlb_dev)); + qdev_prop_set_uint32(tlb_dev, "num-entries", + m_cfg->cfgtable.jtlb_size_entries); + sysbus_realize_and_unref(SYS_BUS_DEVICE(tlb_dev), &error_fatal); + + cpu0 = NULL; + for (int i = 0; i < ms->smp.cpus; i++) { + HexagonCPU *cpu = HEXAGON_CPU(object_new(ms->cpu_type)); + qemu_register_reset(do_cpu_reset, cpu); + + if (i == 0) { + cpu0 = DEVICE(cpu); + if (ms->kernel_filename) { + uint64_t entry = load_kernel(vms); + qdev_prop_set_uint32(cpu0, "exec-start-addr", entry); + } else if (ms->firmware) { + uint64_t entry = load_bios(vms); + qdev_prop_set_uint32(cpu0, "exec-start-addr", entry); + } + } + qdev_prop_set_uint32(DEVICE(cpu), "htid", i); + qdev_prop_set_bit(DEVICE(cpu), "start-powered-off", (i != 0)); + object_property_set_link(OBJECT(cpu), "global-regs", + OBJECT(gsregs_dev), &error_fatal); + object_property_set_link(OBJECT(cpu), "tlb", + OBJECT(tlb_dev), &error_fatal); + + qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal); + } + fdt_add_cpu_nodes(vms); + clk_phandle = fdt_add_clocks(vms); + fdt_add_uart(vms, VIRT_UART0, clk_phandle); + + rom_add_blob_fixed_as("config_table.rom", &m_cfg->cfgtable, + sizeof(m_cfg->cfgtable), m_cfg->cfgbase, + &address_space_memory); + + hexagon_load_fdt(vms); +} + + +static void virt_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Hexagon Virtual Machine"; + mc->init = virt_init; + mc->default_cpu_type = HEXAGON_CPU_TYPE_NAME("v68"); + mc->default_ram_size = 4 * GiB; + mc->max_cpus = 8; + mc->default_cpus = 8; + mc->is_default = false; + mc->default_kernel_irqchip_split = false; + mc->block_default_type = IF_VIRTIO; + mc->default_boot_order = NULL; + mc->no_cdrom = 1; + mc->numa_mem_supported = false; + mc->default_nic = "virtio-mmio-bus"; +} + + +static const TypeInfo virt_machine_types[] = { { + .name = TYPE_HEXAGON_VIRT_MACHINE, + .parent = TYPE_HEXAGON_COMMON_MACHINE, + .instance_size = sizeof(HexagonVirtMachineState), + .class_init = virt_class_init, + .instance_init = virt_instance_init, +} }; + +DEFINE_TYPES(virt_machine_types)
diff --git a/hw/meson.build b/hw/meson.build index ef65ba5..7fa81db 100644 --- a/hw/meson.build +++ b/hw/meson.build
@@ -3,6 +3,7 @@ subdir('arm') subdir('avr') subdir('hppa') +subdir('hexagon') subdir('xenpv') # i386 uses it subdir('i386') subdir('loongarch')
diff --git a/hw/misc/ivshmem-pci.c b/hw/misc/ivshmem-pci.c index c987eeb..536475e 100644 --- a/hw/misc/ivshmem-pci.c +++ b/hw/misc/ivshmem-pci.c
@@ -26,7 +26,7 @@ #include "hw/core/qdev-properties-system.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" -#include "system/kvm.h" +#include "system/accel-irq.h" #include "migration/blocker.h" #include "migration/vmstate.h" #include "qemu/error-report.h" @@ -424,19 +424,19 @@ Error **errp) { PCIDevice *pdev = PCI_DEVICE(s); - KVMRouteChange c; + AccelRouteChange c; int ret; IVSHMEM_DPRINTF("ivshmem_add_kvm_msi_virq vector:%d\n", vector); assert(!s->msi_vectors[vector].pdev); - c = kvm_irqchip_begin_route_changes(kvm_state); + c = accel_irqchip_begin_route_changes(); ret = kvm_irqchip_add_msi_route(&c, vector, pdev); if (ret < 0) { error_setg(errp, "kvm_irqchip_add_msi_route failed"); return; } - kvm_irqchip_commit_route_changes(&c); + accel_irqchip_commit_route_changes(&c); s->msi_vectors[vector].virq = ret; s->msi_vectors[vector].pdev = pdev;
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 7d57eb4..4d822b9 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c
@@ -38,6 +38,7 @@ #include "qemu/module.h" #include "qemu/range.h" #include "qemu/units.h" +#include "system/accel-irq.h" #include "system/kvm.h" #include "system/runstate.h" #include "pci.h" @@ -50,7 +51,7 @@ #include "vfio-helpers.h" /* Protected by BQL */ -static KVMRouteChange vfio_route_change; +static AccelRouteChange vfio_route_change; static void vfio_disable_interrupts(VFIOPCIDevice *vdev); static void vfio_mmap_set_enabled(VFIOPCIDevice *vdev, bool enabled); @@ -688,9 +689,9 @@ if (vdev->defer_kvm_irq_routing) { vfio_pci_add_kvm_msi_virq(vdev, vector, nr, true); } else { - vfio_route_change = kvm_irqchip_begin_route_changes(kvm_state); + vfio_route_change = accel_irqchip_begin_route_changes(); vfio_pci_add_kvm_msi_virq(vdev, vector, nr, true); - kvm_irqchip_commit_route_changes(&vfio_route_change); + accel_irqchip_commit_route_changes(&vfio_route_change); vfio_connect_kvm_msi_virq(vector, nr); } } @@ -789,7 +790,7 @@ { assert(!vdev->defer_kvm_irq_routing); vdev->defer_kvm_irq_routing = true; - vfio_route_change = kvm_irqchip_begin_route_changes(kvm_state); + vfio_route_change = accel_irqchip_begin_route_changes(); } void vfio_pci_commit_kvm_msi_virq_batch(VFIOPCIDevice *vdev) @@ -799,7 +800,7 @@ assert(vdev->defer_kvm_irq_routing); vdev->defer_kvm_irq_routing = false; - kvm_irqchip_commit_route_changes(&vfio_route_change); + accel_irqchip_commit_route_changes(&vfio_route_change); for (i = 0; i < vdev->nr_vectors; i++) { vfio_connect_kvm_msi_virq(&vdev->msi_vectors[i], i);
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index fd97195..6f5db5f 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c
@@ -869,7 +869,7 @@ int ret; if (irqfd->users == 0) { - KVMRouteChange c = kvm_irqchip_begin_route_changes(kvm_state); + AccelRouteChange c = accel_irqchip_begin_route_changes(); ret = accel_irqchip_add_msi_route(&c, vector, &proxy->pci_dev); if (ret < 0) { return ret; @@ -2669,4 +2669,3 @@ } type_init(virtio_pci_register_types) -
diff --git a/include/accel/accel-route.h b/include/accel/accel-route.h new file mode 100644 index 0000000..07fac27 --- /dev/null +++ b/include/accel/accel-route.h
@@ -0,0 +1,17 @@ +/* + * Accelerator MSI route change tracking + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ACCEL_ROUTE_H +#define ACCEL_ROUTE_H + +#include "qemu/accel.h" + +typedef struct AccelRouteChange { + AccelState *accel; + int changes; +} AccelRouteChange; + +#endif /* ACCEL_ROUTE_H */
diff --git a/include/hw/hexagon/hexagon.h b/include/hw/hexagon/hexagon.h new file mode 100644 index 0000000..1034b09 --- /dev/null +++ b/include/hw/hexagon/hexagon.h
@@ -0,0 +1,161 @@ +/* + * Hexagon Baseboard System emulation. + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + +#ifndef HW_HEXAGON_H +#define HW_HEXAGON_H + +#include "system/memory.h" +#include "hw/core/boards.h" + +struct hexagon_board_boot_info { + uint64_t ram_size; + const char *kernel_filename; + uint32_t kernel_elf_flags; +}; + +typedef enum { + unknown_rev = 0, + v66_rev = 0xa666, + v67_rev = 0x2667, + v68_rev = 0x8d68, + v69_rev = 0x8c69, + v71_rev = 0x8c71, + v73_rev = 0x8c73, + v73m_rev = 0xcc73, +} Rev_t; +#define HEXAGON_LATEST_REV v73 +#define HEXAGON_LATEST_REV_UPPER V73 + +/* + * Config table address bases represent bits [35:16]. + */ +#define HEXAGON_CFG_ADDR_BASE(addr) (((addr) >> 16) & 0x0fffff) + +#define HEXAGON_CFGSPACE_ENTRIES (128) + +union hexagon_config_table { + struct { + /* Base address of L2TCM space */ + uint32_t l2tcm_base; + uint32_t reserved0; + /* Base address of subsystem space */ + uint32_t subsystem_base; + /* Base address of ETM space */ + uint32_t etm_base; + /* Base address of L2 configuration space */ + uint32_t l2cfg_base; + uint32_t reserved1; + /* Base address of L1S */ + uint32_t l1s0_base; + /* Base address of AXI2 */ + uint32_t axi2_lowaddr; + /* Base address of streamer base */ + uint32_t streamer_base; + uint32_t reserved2; + /* Base address of fast L2VIC */ + uint32_t fastl2vic_base; + /* Number of entries in JTLB */ + uint32_t jtlb_size_entries; + /* Coprocessor type */ + uint32_t coproc_present; + /* Number of extension execution contexts available */ + uint32_t ext_contexts; + /* Base address of Hexagon Vector Tightly Coupled Memory (VTCM) */ + uint32_t vtcm_base; + /* Size of VTCM (in KB) */ + uint32_t vtcm_size_kb; + /* L2 tag size */ + uint32_t l2tag_size; + /* Amount of physical L2 memory in released version */ + uint32_t l2ecomem_size; + /* Hardware threads available on the core */ + uint32_t thread_enable_mask; + /* Base address of the ECC registers */ + uint32_t eccreg_base; + /* L2 line size */ + uint32_t l2line_size; + /* Small Core processor (also implies audio extension) */ + uint32_t tiny_core; + /* Size of L2TCM */ + uint32_t l2itcm_size; + /* Base address of L2-ITCM */ + uint32_t l2itcm_base; + uint32_t reserved3; + /* DTM is present */ + uint32_t dtm_present; + /* Version of the DMA */ + uint32_t dma_version; + /* Native HVX vector length in log of bytes */ + uint32_t hvx_vec_log_length; + /* Core ID of the multi-core */ + uint32_t core_id; + /* Number of multi-core cores */ + uint32_t core_count; + uint32_t coproc2_reg0; + uint32_t coproc2_reg1; + /* Supported HVX vector length */ + uint32_t v2x_mode; + uint32_t coproc2_reg2; + uint32_t coproc2_reg3; + uint32_t coproc2_reg4; + uint32_t coproc2_reg5; + uint32_t coproc2_reg6; + uint32_t coproc2_reg7; + /* Voltage droop mitigation technique parameter */ + uint32_t acd_preset; + /* Voltage droop mitigation technique parameter */ + uint32_t mnd_preset; + /* L1 data cache size (in KB) */ + uint32_t l1d_size_kb; + /* L1 instruction cache size in (KB) */ + uint32_t l1i_size_kb; + /* L1 data cache write policy: see HexagonL1WritePolicy */ + uint32_t l1d_write_policy; + /* VTCM bank width */ + uint32_t vtcm_bank_width; + uint32_t reserved4; + uint32_t reserved5; + uint32_t reserved6; + uint32_t coproc2_cvt_mpy_size; + uint32_t consistency_domain; + uint32_t capacity_domain; + uint32_t axi3_lowaddr; + uint32_t coproc2_int8_subcolumns; + uint32_t corecfg_present; + uint32_t coproc2_fp16_acc_exp; + uint32_t AXIM2_secondary_base; + }; + uint32_t raw[HEXAGON_CFGSPACE_ENTRIES]; +}; + +struct hexagon_machine_config { + /* Base address of config table */ + uint32_t cfgbase; + /* Size of L2 TCM */ + uint32_t l2tcm_size; + /* Base address of L2VIC */ + uint32_t l2vic_base; + /* Size of L2VIC region */ + uint32_t l2vic_size; + /* QTimer csr base */ + uint32_t csr_base; + uint32_t qtmr_region; + union hexagon_config_table cfgtable; +}; + +#define TYPE_HEXAGON_COMMON_MACHINE "hexagon-common-machine" +OBJECT_DECLARE_SIMPLE_TYPE(HexagonCommonMachineState, HEXAGON_COMMON_MACHINE) + +struct HexagonCommonMachineState { + MachineState parent_obj; + + MemoryRegion ram; + MemoryRegion cfgtable_rom; +}; + +#endif
diff --git a/include/hw/hexagon/hexagon_globalreg.h b/include/hw/hexagon/hexagon_globalreg.h new file mode 100644 index 0000000..9500998 --- /dev/null +++ b/include/hw/hexagon/hexagon_globalreg.h
@@ -0,0 +1,55 @@ +/* + * Hexagon Global Registers QOM Object + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HEXAGON_GLOBALREG_H +#define HEXAGON_GLOBALREG_H + +#include "hw/core/qdev.h" +#include "hw/core/sysbus.h" +#include "qom/object.h" +#include "target/hexagon/cpu.h" + +#define TYPE_HEXAGON_GLOBALREG "hexagon-globalreg" +OBJECT_DECLARE_SIMPLE_TYPE(HexagonGlobalRegState, HEXAGON_GLOBALREG) + +struct HexagonGlobalRegState { + SysBusDevice parent_obj; + + /* Array of system registers */ + uint32_t regs[NUM_SREGS]; + + /* Global performance cycle counter base */ + uint64_t g_pcycle_base; + + /* Properties for global register reset values */ + uint32_t boot_evb; /* Boot Exception Vector Base (HEX_SREG_EVB) */ + uint64_t config_table_addr; /* Configuration table base */ + uint32_t dsp_rev; /* DSP revision register (HEX_SREG_REV) */ + + /* ISDB properties */ + bool isdben_etm_enable; /* ISDB ETM enable bit */ + bool isdben_dfd_enable; /* ISDB DFD enable bit */ + bool isdben_trusted; /* ISDB trusted mode bit */ + bool isdben_secure; /* ISDB secure mode bit */ +}; + +/* Public interface functions */ +uint32_t hexagon_globalreg_read(HexagonGlobalRegState *s, uint32_t reg, + uint32_t htid); +void hexagon_globalreg_write(HexagonGlobalRegState *s, uint32_t reg, + uint32_t value, uint32_t htid); +uint32_t hexagon_globalreg_masked_value(HexagonGlobalRegState *s, uint32_t reg, + uint32_t value); +void hexagon_globalreg_write_masked(HexagonGlobalRegState *s, uint32_t reg, + uint32_t value); + +/* Global performance cycle counter access */ +uint64_t hexagon_globalreg_get_pcycle_base(HexagonGlobalRegState *s); +void hexagon_globalreg_set_pcycle_base(HexagonGlobalRegState *s, + uint64_t value); + +#endif /* HEXAGON_GLOBALREG_H */
diff --git a/include/hw/hexagon/hexagon_tlb.h b/include/hw/hexagon/hexagon_tlb.h new file mode 100644 index 0000000..90d9ed8 --- /dev/null +++ b/include/hw/hexagon/hexagon_tlb.h
@@ -0,0 +1,46 @@ +/* + * Hexagon TLB QOM Device + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_HEXAGON_TLB_H +#define HW_HEXAGON_TLB_H + +#include "hw/core/sysbus.h" +#include "qom/object.h" +#include "exec/hwaddr.h" +#include "exec/mmu-access-type.h" +#include "monitor/monitor.h" +#define TYPE_HEXAGON_TLB "hexagon-tlb" +OBJECT_DECLARE_SIMPLE_TYPE(HexagonTLBState, HEXAGON_TLB) + +struct HexagonTLBState { + SysBusDevice parent_obj; + + uint32_t num_entries; + uint64_t *entries; +}; + +uint64_t hexagon_tlb_read(HexagonTLBState *tlb, uint32_t index); +void hexagon_tlb_write(HexagonTLBState *tlb, uint32_t index, uint64_t value); + +bool hexagon_tlb_find_match(HexagonTLBState *tlb, uint32_t asid, + uint32_t VA, MMUAccessType access_type, + hwaddr *PA, int *prot, uint64_t *size, + int32_t *excp, int *cause_code, int mmu_idx); + +uint32_t hexagon_tlb_lookup(HexagonTLBState *tlb, uint32_t asid, + uint32_t VA, int *cause_code); + +int hexagon_tlb_check_overlap(HexagonTLBState *tlb, uint64_t entry, + uint64_t index); + +void hexagon_tlb_dump(Monitor *mon, HexagonTLBState *tlb); + +bool hexagon_tlb_dump_entry(Monitor *mon, uint64_t entry); + +uint32_t hexagon_tlb_get_num_entries(HexagonTLBState *tlb); + +#endif /* HW_HEXAGON_TLB_H */
diff --git a/include/hw/hexagon/virt.h b/include/hw/hexagon/virt.h new file mode 100644 index 0000000..fcb4776 --- /dev/null +++ b/include/hw/hexagon/virt.h
@@ -0,0 +1,30 @@ +/* + * Definitions for hexagon virt board. + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_HEXAGONVIRT_H +#define HW_HEXAGONVIRT_H + +#include "hw/hexagon/hexagon.h" +#include "target/hexagon/cpu.h" + +struct HexagonVirtMachineState { + HexagonCommonMachineState parent_obj; + + int fdt_size; + MemoryRegion *sys; + MemoryRegion tcm; + MemoryRegion vtcm; + MemoryRegion bios; + Clock *apb_clk; +}; + +void hexagon_load_fdt(const struct HexagonVirtMachineState *vms); + +#define TYPE_HEXAGON_VIRT_MACHINE MACHINE_TYPE_NAME("virt") +OBJECT_DECLARE_SIMPLE_TYPE(HexagonVirtMachineState, HEXAGON_VIRT_MACHINE) + +#endif /* HW_HEXAGONVIRT_H */
diff --git a/include/hw/hyperv/hvgdk_mini.h b/include/hw/hyperv/hvgdk_mini.h index c3a8f33..f8838a3 100644 --- a/include/hw/hyperv/hvgdk_mini.h +++ b/include/hw/hyperv/hvgdk_mini.h
@@ -9,9 +9,25 @@ #define MSHV_IOCTL 0xB8 +/* Hyper-V specific model specific registers (MSRs) */ + +/* HV_X64_SYNTHETIC_MSR */ +#define HV_X64_MSR_GUEST_OS_ID 0x40000000 +#define HV_X64_MSR_HYPERCALL 0x40000001 +#define HV_X64_MSR_VP_INDEX 0x40000002 +#define HV_X64_MSR_RESET 0x40000003 +#define HV_X64_MSR_VP_RUNTIME 0x40000010 +#define HV_X64_MSR_TIME_REF_COUNT 0x40000020 +#define HV_X64_MSR_REFERENCE_TSC 0x40000021 +#define HV_X64_MSR_TSC_FREQUENCY 0x40000022 +#define HV_X64_MSR_APIC_FREQUENCY 0x40000023 + typedef enum hv_register_name { /* Pending Interruption Register */ HV_REGISTER_PENDING_INTERRUPTION = 0x00010002, + HV_REGISTER_INTERRUPT_STATE = 0x00010003, + HV_REGISTER_PENDING_EVENT0 = 0x00010004, + HV_REGISTER_PENDING_EVENT1 = 0x00010005, /* X64 User-Mode Registers */ HV_X64_REGISTER_RAX = 0x00020000, @@ -154,10 +170,22 @@ HV_X64_REGISTER_SPEC_CTRL = 0x00080084, HV_X64_REGISTER_TSC_ADJUST = 0x00080096, + /* CET / Shadow Stack */ + HV_X64_REGISTER_U_XSS = 0x0008008B, + HV_X64_REGISTER_U_CET = 0x0008008C, + HV_X64_REGISTER_S_CET = 0x0008008D, + HV_X64_REGISTER_SSP = 0x0008008E, + HV_X64_REGISTER_PL0_SSP = 0x0008008F, + HV_X64_REGISTER_PL1_SSP = 0x00080090, + HV_X64_REGISTER_PL2_SSP = 0x00080091, + HV_X64_REGISTER_PL3_SSP = 0x00080092, + HV_X64_REGISTER_INTERRUPT_SSP_TABLE_ADDR = 0x00080093, + /* Other MSRs */ HV_X64_REGISTER_MSR_IA32_MISC_ENABLE = 0x000800A0, /* Misc */ + HV_X64_REGISTER_HYPERCALL = 0x00090001, HV_REGISTER_GUEST_OS_ID = 0x00090002, HV_REGISTER_REFERENCE_TSC = 0x00090017, @@ -454,6 +482,8 @@ struct hv_register_assoc elements[]; } QEMU_PACKED hv_input_set_vp_registers; +#define MSHV_VP_MAX_REGISTERS 128 + union hv_interrupt_control { uint64_t as_uint64; struct { @@ -474,6 +504,109 @@ uint16_t rsvd_z1; } QEMU_PACKED; +/* Flags for dirty mask of hv_vp_register_page */ +enum hv_x64_register_class_type { + HV_X64_REGISTER_CLASS_GENERAL = 0, + HV_X64_REGISTER_CLASS_IP = 1, + HV_X64_REGISTER_CLASS_XMM = 2, + HV_X64_REGISTER_CLASS_SEGMENT = 3, + HV_X64_REGISTER_CLASS_FLAGS = 4, +}; + +#define HV_VP_REGISTER_PAGE_MAX_VECTOR_COUNT 7 + +union hv_vp_register_page_interrupt_vectors { + uint64_t as_uint64; + struct { + uint8_t vector_count; + uint8_t vector[HV_VP_REGISTER_PAGE_MAX_VECTOR_COUNT]; + }; +}; + +struct hv_vp_register_page { + uint16_t version; + uint8_t isvalid; + uint8_t rsvdz; + uint32_t dirty; + + union { + struct { + /* General purpose registers (HV_X64_REGISTER_CLASS_GENERAL) */ + union { + struct { + uint64_t rax; + uint64_t rcx; + uint64_t rdx; + uint64_t rbx; + uint64_t rsp; + uint64_t rbp; + uint64_t rsi; + uint64_t rdi; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + } QEMU_PACKED; + + uint64_t gp_registers[16]; + }; + /* Instruction pointer (HV_X64_REGISTER_CLASS_IP) */ + uint64_t rip; + /* Flags (HV_X64_REGISTER_CLASS_FLAGS) */ + uint64_t rflags; + } QEMU_PACKED; + + uint64_t registers[18]; + }; + uint8_t reserved[8]; + /* Volatile XMM registers (HV_X64_REGISTER_CLASS_XMM) */ + union { + struct { + struct hv_u128 xmm0; + struct hv_u128 xmm1; + struct hv_u128 xmm2; + struct hv_u128 xmm3; + struct hv_u128 xmm4; + struct hv_u128 xmm5; + } QEMU_PACKED; + + struct hv_u128 xmm_registers[6]; + }; + /* Segment registers (HV_X64_REGISTER_CLASS_SEGMENT) */ + union { + struct { + struct hv_x64_segment_register es; + struct hv_x64_segment_register cs; + struct hv_x64_segment_register ss; + struct hv_x64_segment_register ds; + struct hv_x64_segment_register fs; + struct hv_x64_segment_register gs; + } QEMU_PACKED; + + struct hv_x64_segment_register segment_registers[6]; + }; + /* Misc. control registers (cannot be set via this interface) */ + uint64_t cr0; + uint64_t cr3; + uint64_t cr4; + uint64_t cr8; + uint64_t efer; + uint64_t dr7; + union hv_x64_pending_interruption_register pending_interruption; + union hv_x64_interrupt_state_register interrupt_state; + uint64_t instruction_emulation_hints; + uint64_t xfem; + + uint8_t reserved1[0x100]; + + /* Interrupts injected as part of HvCallDispatchVp. */ + union hv_vp_register_page_interrupt_vectors interrupt_vectors; +} QEMU_PACKED; + /* /dev/mshv */ #define MSHV_CREATE_PARTITION _IOW(MSHV_IOCTL, 0x00, struct mshv_create_partition) #define MSHV_CREATE_VP _IOW(MSHV_IOCTL, 0x01, struct mshv_create_vp)
diff --git a/include/hw/hyperv/hvhdk.h b/include/hw/hyperv/hvhdk.h index 41af743..9e6dcb2 100644 --- a/include/hw/hyperv/hvhdk.h +++ b/include/hw/hyperv/hvhdk.h
@@ -11,6 +11,16 @@ #define HV_PARTITION_SYNTHETIC_PROCESSOR_FEATURES_BANKS 1 +struct hv_input_get_partition_property { + uint64_t partition_id; + uint32_t property_code; /* enum hv_partition_property_code */ + uint32_t padding; +} QEMU_PACKED; + +struct hv_output_get_partition_property { + uint64_t property_value; +} QEMU_PACKED; + struct hv_input_set_partition_property { uint64_t partition_id; uint32_t property_code; /* enum hv_partition_property_code */ @@ -161,6 +171,191 @@ }; }; +union hv_partition_processor_xsave_features { + struct { + uint64_t xsave_support:1; + uint64_t xsaveopt_support:1; + uint64_t avx_support:1; + uint64_t avx2_support:1; + uint64_t fma_support:1; + uint64_t mpx_support:1; + uint64_t avx512_support:1; + uint64_t avx512_dq_support:1; + uint64_t avx512_cd_support:1; + uint64_t avx512_bw_support:1; + uint64_t avx512_vl_support:1; + uint64_t xsave_comp_support:1; + uint64_t xsave_supervisor_support:1; + uint64_t xcr1_support:1; + uint64_t avx512_bitalg_support:1; + uint64_t avx512_i_fma_support:1; + uint64_t avx512_v_bmi_support:1; + uint64_t avx512_v_bmi2_support:1; + uint64_t avx512_vnni_support:1; + uint64_t gfni_support:1; + uint64_t vaes_support:1; + uint64_t avx512_v_popcntdq_support:1; + uint64_t vpclmulqdq_support:1; + uint64_t avx512_bf16_support:1; + uint64_t avx512_vp2_intersect_support:1; + uint64_t avx512_fp16_support:1; + uint64_t xfd_support:1; + uint64_t amx_tile_support:1; + uint64_t amx_bf16_support:1; + uint64_t amx_int8_support:1; + uint64_t avx_vnni_support:1; + uint64_t avx_ifma_support:1; + uint64_t avx_ne_convert_support:1; + uint64_t avx_vnni_int8_support:1; + uint64_t avx_vnni_int16_support:1; + uint64_t avx10_1_256_support:1; + uint64_t avx10_1_512_support:1; + uint64_t amx_fp16_support:1; + uint64_t reserved1:26; + }; + uint64_t as_uint64; +}; + +#define HV_PARTITION_PROCESSOR_FEATURES_BANKS 2 +#define HV_PARTITION_PROCESSOR_FEATURES_RESERVEDBANK1_BITFIELD_COUNT 4 + + +union hv_partition_processor_features { + uint64_t as_uint64[HV_PARTITION_PROCESSOR_FEATURES_BANKS]; + struct { + uint64_t sse3_support:1; + uint64_t lahf_sahf_support:1; + uint64_t ssse3_support:1; + uint64_t sse4_1_support:1; + uint64_t sse4_2_support:1; + uint64_t sse4a_support:1; + uint64_t xop_support:1; + uint64_t pop_cnt_support:1; + uint64_t cmpxchg16b_support:1; + uint64_t altmovcr8_support:1; + uint64_t lzcnt_support:1; + uint64_t mis_align_sse_support:1; + uint64_t mmx_ext_support:1; + uint64_t amd3dnow_support:1; + uint64_t extended_amd3dnow_support:1; + uint64_t page_1gb_support:1; + uint64_t aes_support:1; + uint64_t pclmulqdq_support:1; + uint64_t pcid_support:1; + uint64_t fma4_support:1; + uint64_t f16c_support:1; + uint64_t rd_rand_support:1; + uint64_t rd_wr_fs_gs_support:1; + uint64_t smep_support:1; + uint64_t enhanced_fast_string_support:1; + uint64_t bmi1_support:1; + uint64_t bmi2_support:1; + uint64_t hle_support_deprecated:1; + uint64_t rtm_support_deprecated:1; + uint64_t movbe_support:1; + uint64_t npiep1_support:1; + uint64_t dep_x87_fpu_save_support:1; + uint64_t rd_seed_support:1; + uint64_t adx_support:1; + uint64_t intel_prefetch_support:1; + uint64_t smap_support:1; + uint64_t hle_support:1; + uint64_t rtm_support:1; + uint64_t rdtscp_support:1; + uint64_t clflushopt_support:1; + uint64_t clwb_support:1; + uint64_t sha_support:1; + uint64_t x87_pointers_saved_support:1; + uint64_t invpcid_support:1; + uint64_t ibrs_support:1; + uint64_t stibp_support:1; + uint64_t ibpb_support:1; + uint64_t unrestricted_guest_support:1; + uint64_t mdd_support:1; + uint64_t fast_short_rep_mov_support:1; + uint64_t l1dcache_flush_support:1; + uint64_t rdcl_no_support:1; + uint64_t ibrs_all_support:1; + uint64_t skip_l1df_support:1; + uint64_t ssb_no_support:1; + uint64_t rsb_a_no_support:1; + uint64_t virt_spec_ctrl_support:1; + uint64_t rd_pid_support:1; + uint64_t umip_support:1; + uint64_t mbs_no_support:1; + uint64_t mb_clear_support:1; + uint64_t taa_no_support:1; + uint64_t tsx_ctrl_support:1; + uint64_t reserved_bank0:1; + + /* N.B. Begin bank 1 processor features. */ + uint64_t a_count_m_count_support:1; + uint64_t tsc_invariant_support:1; + uint64_t cl_zero_support:1; + uint64_t rdpru_support:1; + uint64_t la57_support:1; + uint64_t mbec_support:1; + uint64_t nested_virt_support:1; + uint64_t psfd_support:1; + uint64_t cet_ss_support:1; + uint64_t cet_ibt_support:1; + uint64_t vmx_exception_inject_support:1; + uint64_t enqcmd_support:1; + uint64_t umwait_tpause_support:1; + uint64_t movdiri_support:1; + uint64_t movdir64b_support:1; + uint64_t cldemote_support:1; + uint64_t serialize_support:1; + uint64_t tsc_deadline_tmr_support:1; + uint64_t tsc_adjust_support:1; + uint64_t fzl_rep_movsb:1; + uint64_t fs_rep_stosb:1; + uint64_t fs_rep_cmpsb:1; + uint64_t tsx_ld_trk_support:1; + uint64_t vmx_ins_outs_exit_info_support:1; + uint64_t hlat_support:1; + uint64_t sbdr_ssdp_no_support:1; + uint64_t fbsdp_no_support:1; + uint64_t psdp_no_support:1; + uint64_t fb_clear_support:1; + uint64_t btc_no_support:1; + uint64_t ibpb_rsb_flush_support:1; + uint64_t stibp_always_on_support:1; + uint64_t perf_global_ctrl_support:1; + uint64_t npt_execute_only_support:1; + uint64_t npt_ad_flags_support:1; + uint64_t npt1_gb_page_support:1; + uint64_t amd_processor_topology_node_id_support:1; + uint64_t local_machine_check_support:1; + uint64_t extended_topology_leaf_fp256_amd_support:1; + uint64_t gds_no_support:1; + uint64_t cmpccxadd_support:1; + uint64_t tsc_aux_virtualization_support:1; + uint64_t rmp_query_support:1; + uint64_t bhi_no_support:1; + uint64_t bhi_dis_support:1; + uint64_t prefetch_i_support:1; + uint64_t sha512_support:1; + uint64_t mitigation_ctrl_support:1; + uint64_t rfds_no_support:1; + uint64_t rfds_clear_support:1; + uint64_t sm3_support:1; + uint64_t sm4_support:1; + uint64_t secure_avic_support:1; + uint64_t guest_intercept_ctrl_support:1; + uint64_t sbpb_supported:1; + uint64_t ibpb_br_type_supported:1; + uint64_t srso_no_supported:1; + uint64_t srso_user_kernel_no_supported:1; + uint64_t vrew_clear_supported:1; + uint64_t tsa_l1_no_supported:1; + uint64_t tsa_sq_no_supported:1; + uint64_t lass_support:1; + uint64_t idle_hlt_intercept_support:1; + uint64_t msr_list_support:1; + } QEMU_PACKED; +}; + enum hv_translate_gva_result_code { HV_TRANSLATE_GVA_SUCCESS = 0,
diff --git a/include/system/accel-irq.h b/include/system/accel-irq.h index a2caa06..fc94c54 100644 --- a/include/system/accel-irq.h +++ b/include/system/accel-irq.h
@@ -25,9 +25,11 @@ return mshv_msi_via_irqfd_enabled() || kvm_irqchip_is_split(); } -int accel_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev); +AccelRouteChange accel_irqchip_begin_route_changes(void); +int accel_irqchip_add_msi_route(AccelRouteChange *c, int vector, + PCIDevice *dev); int accel_irqchip_update_msi_route(int vector, MSIMessage msg, PCIDevice *dev); -void accel_irqchip_commit_route_changes(KVMRouteChange *c); +void accel_irqchip_commit_route_changes(AccelRouteChange *c); void accel_irqchip_commit_routes(void); void accel_irqchip_release_virq(int virq); int accel_irqchip_add_irqfd_notifier_gsi(EventNotifier *n, EventNotifier *rn,
diff --git a/include/system/kvm.h b/include/system/kvm.h index 5fa33ed..cdd1856 100644 --- a/include/system/kvm.h +++ b/include/system/kvm.h
@@ -18,6 +18,7 @@ #include "exec/memattrs.h" #include "qemu/accel.h" +#include "accel/accel-route.h" #include "qom/object.h" #ifdef COMPILING_PER_TARGET @@ -183,11 +184,6 @@ typedef struct Notifier Notifier; typedef struct NotifierWithReturn NotifierWithReturn; -typedef struct KVMRouteChange { - KVMState *s; - int changes; -} KVMRouteChange; - /* external API */ unsigned int kvm_get_max_memslots(void); @@ -466,7 +462,7 @@ /** * kvm_irqchip_add_msi_route - Add MSI route for specific vector - * @c: KVMRouteChange instance. + * @c: AccelRouteChange instance. * @vector: which vector to add. This can be either MSI/MSIX * vector. The function will automatically detect whether * MSI/MSIX is enabled, and fetch corresponding MSI @@ -475,24 +471,11 @@ * as @NULL, an empty MSI message will be inited. * @return: virq (>=0) when success, errno (<0) when failed. */ -int kvm_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev); +int kvm_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev); int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg, PCIDevice *dev); void kvm_irqchip_commit_routes(KVMState *s); -static inline KVMRouteChange kvm_irqchip_begin_route_changes(KVMState *s) -{ - return (KVMRouteChange) { .s = s, .changes = 0 }; -} - -static inline void kvm_irqchip_commit_route_changes(KVMRouteChange *c) -{ - if (c->changes) { - kvm_irqchip_commit_routes(c->s); - c->changes = 0; - } -} - int kvm_irqchip_get_virq(KVMState *s); void kvm_irqchip_release_virq(KVMState *s, int virq);
diff --git a/include/system/mshv.h b/include/system/mshv.h index 75286ba..46fe3fc 100644 --- a/include/system/mshv.h +++ b/include/system/mshv.h
@@ -21,6 +21,7 @@ #include "qapi/qapi-types-common.h" #include "system/memory.h" #include "accel/accel-ops.h" +#include "accel/accel-route.h" #ifdef COMPILING_PER_TARGET #ifdef CONFIG_MSHV @@ -32,6 +33,7 @@ #endif #define MSHV_MAX_MSI_ROUTES 4096 +#define MSHV_MIN_ALLOCATED_MSI_ROUTES 64 #define MSHV_PAGE_SHIFT 12 @@ -44,7 +46,13 @@ #define mshv_msi_via_irqfd_enabled() mshv_enabled() #endif +#define TYPE_MSHV_ACCEL ACCEL_CLASS_NAME("mshv") + typedef struct MshvState MshvState; + +DECLARE_INSTANCE_CHECKER(MshvState, MSHV_STATE, + TYPE_MSHV_ACCEL) + extern MshvState *mshv_state; /* interrupt */ @@ -52,12 +60,16 @@ uint32_t vp_index, bool logical_destination_mode, bool level_triggered); -int mshv_irqchip_add_msi_route(int vector, PCIDevice *dev); +int mshv_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev); int mshv_irqchip_update_msi_route(int virq, MSIMessage msg, PCIDevice *dev); -void mshv_irqchip_commit_routes(void); -void mshv_irqchip_release_virq(int virq); +void mshv_irqchip_release_virq(MshvState *s, int virq); +void mshv_irqchip_commit_routes(MshvState *s); int mshv_irqchip_add_irqfd_notifier_gsi(const EventNotifier *n, const EventNotifier *rn, int virq); int mshv_irqchip_remove_irqfd_notifier_gsi(const EventNotifier *n, int virq); +void mshv_init_irq_routing(MshvState *s); + +/* cpuid */ +uint32_t mshv_get_supported_cpuid(uint32_t func, uint32_t idx, int reg); #endif
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h index 35386c4..ba30738 100644 --- a/include/system/mshv_int.h +++ b/include/system/mshv_int.h
@@ -14,8 +14,25 @@ #ifndef QEMU_MSHV_INT_H #define QEMU_MSHV_INT_H +#include "hw/hyperv/hvhdk.h" + #define MSHV_MSR_ENTRIES_COUNT 64 +/* + * Interruption-type encoding, used by the hypervisor in + * hv_x64_pending_interruption_register.interruption_type + * See TLFS 6.0 section 7.9.2, p55 + * https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/tlfs/tlfs + */ +#define MSHV_HV_INTERRUPTION_TYPE_EXT_INT 0 +#define MSHV_HV_INTERRUPTION_TYPE_NMI 2 +#define MSHV_HV_INTERRUPTION_TYPE_HW_EXC 3 +#define MSHV_HV_INTERRUPTION_TYPE_SW_INT 4 +#define MSHV_HV_INTERRUPTION_TYPE_PRIV_SW_EXC 5 +#define MSHV_HV_INTERRUPTION_TYPE_SW_EXC 6 + +#define MSHV_DIRTY_PAGES_BATCH_SIZE 0x10000 + typedef struct hyperv_message hv_message; typedef struct MshvHvCallArgs { @@ -48,6 +65,12 @@ int nr_as; MshvAddressSpace *as; int fd; + /* irqchip routing */ + struct mshv_user_irq_table *irq_routes; + int nr_allocated_irq_routes; + unsigned long *used_gsi_bitmap; + unsigned int gsi_count; + union hv_partition_processor_features processor_features; }; typedef struct MshvMsiControl { @@ -58,19 +81,6 @@ #define mshv_vcpufd(cpu) (cpu->accel->cpufd) /* cpu */ -typedef struct MshvFPU { - uint8_t fpr[8][16]; - uint16_t fcw; - uint16_t fsw; - uint8_t ftwx; - uint8_t pad1; - uint16_t last_opcode; - uint64_t last_ip; - uint64_t last_dp; - uint8_t xmm[16][16]; - uint32_t mxcsr; - uint32_t pad2; -} MshvFPU; typedef enum MshvVmExit { MshvVmExitIgnore = 0, @@ -81,19 +91,20 @@ void mshv_init_mmio_emu(void); int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd); void mshv_remove_vcpu(int vm_fd, int cpu_fd); -int mshv_configure_vcpu(const CPUState *cpu, const MshvFPU *fpu, uint64_t xcr0); -int mshv_get_standard_regs(CPUState *cpu); -int mshv_get_special_regs(CPUState *cpu); +int mshv_configure_vcpu(const CPUState *cpu); int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit); -int mshv_load_regs(CPUState *cpu); -int mshv_store_regs(CPUState *cpu); int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs, size_t n_regs); -int mshv_arch_put_registers(const CPUState *cpu); +int mshv_get_generic_regs(CPUState *cpu, hv_register_assoc *assocs, + size_t n_regs); +int mshv_arch_store_vcpu_state(const CPUState *cpu); +int mshv_arch_load_vcpu_state(CPUState *cpu); void mshv_arch_init_vcpu(CPUState *cpu); void mshv_arch_destroy_vcpu(CPUState *cpu); void mshv_arch_amend_proc_features( union hv_partition_synthetic_processor_features *features); +void mshv_arch_disable_partition_proc_features( + union hv_partition_processor_features *disabled_features); int mshv_arch_post_init_vm(int vm_fd); typedef struct mshv_root_hvcall mshv_root_hvcall; @@ -113,24 +124,13 @@ bool is_secure_mode); void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section, bool add); +void mshv_log_sync(MemoryListener *listener, MemoryRegionSection *section); +bool mshv_log_global_start(MemoryListener *listener, Error **errp); +void mshv_log_global_stop(MemoryListener *listener); /* msr */ -typedef struct MshvMsrEntry { - uint32_t index; - uint32_t reserved; - uint64_t data; -} MshvMsrEntry; - -typedef struct MshvMsrEntries { - MshvMsrEntry entries[MSHV_MSR_ENTRIES_COUNT]; - uint32_t nmsrs; -} MshvMsrEntries; - -int mshv_configure_msr(const CPUState *cpu, const MshvMsrEntry *msrs, - size_t n_msrs); - -/* interrupt */ -void mshv_init_msicontrol(void); -int mshv_reserve_ioapic_msi_routes(int vm_fd); +int mshv_init_msrs(const CPUState *cpu); +int mshv_get_msrs(CPUState *cpu); +int mshv_set_msrs(const CPUState *cpu); #endif
diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c index 9464246..0958c51 100644 --- a/linux-user/hexagon/cpu_loop.c +++ b/linux-user/hexagon/cpu_loop.c
@@ -22,6 +22,7 @@ #include "qemu.h" #include "user-internals.h" #include "user/cpu_loop.h" +#include "target/hexagon/internal.h" #include "signal-common.h" #include "internal.h" @@ -60,6 +61,28 @@ env->gpr[0] = ret; } break; + case HEX_EVENT_PRECISE: + switch (env->cause_code) { + case HEX_CAUSE_FETCH_NO_UPAGE: + case HEX_CAUSE_PRIV_NO_UREAD: + case HEX_CAUSE_PRIV_NO_UWRITE: + force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_MAPERR, + env->gpr[HEX_REG_PC]); + + break; + case HEX_CAUSE_PRIV_USER_NO_GINSN: + case HEX_CAUSE_PRIV_USER_NO_SINSN: + case HEX_CAUSE_INVALID_PACKET: + force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, + env->gpr[HEX_REG_PC]); + break; + default: + EXCP_DUMP(env, "\nqemu: unhandled CPU precise exception " + "cause code 0x%x - aborting\n", + env->cause_code); + exit(EXIT_FAILURE); + } + break; case HEX_CAUSE_PC_NOT_ALIGNED: force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN, env->gpr[HEX_REG_R31]);
diff --git a/meson.build b/meson.build index 0d1df06..164328d 100644 --- a/meson.build +++ b/meson.build
@@ -3601,6 +3601,7 @@ 'hw/display', 'hw/dma', 'hw/fsi', + 'hw/hexagon', 'hw/hyperv', 'hw/i2c', 'hw/i3c',
diff --git a/python/qemu/machine/machine.py b/python/qemu/machine/machine.py index ebb58d5..3670c8c 100644 --- a/python/qemu/machine/machine.py +++ b/python/qemu/machine/machine.py
@@ -292,8 +292,8 @@ def _load_io_log(self) -> None: self._iolog = iolog.read() @property - def _base_args(self) -> List[str]: - args = ['-display', 'none', '-vga', 'none'] + def _harness_args(self) -> List[str]: + args: List[str] = [] if self._qmp_set: if self._sock_pair: @@ -306,12 +306,17 @@ def _base_args(self) -> List[str]: moncdev = f"socket,id=mon,path={self._monitor_address}" args.extend(['-chardev', moncdev, '-mon', 'chardev=mon,mode=control']) + return args - if self._machine is not None: - args.extend(['-machine', self._machine]) + def _console_args(self, interactive: bool = False) -> List[str]: + args: List[str] = [] + # redirect pre_console_index serials to null for _ in range(self._console_index): args.extend(['-serial', 'null']) - if self._console_set: + + if interactive: + args.extend(['-serial', 'mon:stdio']) + elif self._console_set: assert self._cons_sock_pair is not None fd = self._cons_sock_pair[0].fileno() chardev = f"socket,id=console,fd={fd}" @@ -324,6 +329,13 @@ def _base_args(self) -> List[str]: return args @property + def _base_args(self) -> List[str]: + args: List[str] = ['-display', 'none', '-vga', 'none'] + if self._machine is not None: + args.extend(['-machine', self._machine]) + return args + + @property def args(self) -> List[str]: """Returns the list of arguments given to the QEMU binary.""" return self._args @@ -366,6 +378,8 @@ def _pre_launch(self) -> None: self._qemu_full_args = tuple(chain( self._wrapper, [self._binary], + self._harness_args, + self._console_args(), self._base_args, self._args )) @@ -474,6 +488,14 @@ def _launch(self) -> None: """ self._pre_launch() LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args)) + # Log a simplified, developer-runnable command: + # Exclude harness-managed infrastructure args (harness_args) + # and wrapper. + debug_cmd = [self._binary] + debug_cmd.extend(self._console_args(interactive=True)) + debug_cmd.extend(self._base_args) + debug_cmd.extend(self._args) + LOG.debug('Developer-runnable command: %r', ' '.join(debug_cmd)) # Cleaning up of this subprocess is guaranteed by _do_shutdown. # pylint: disable=consider-using-with
diff --git a/qemu-options.hx b/qemu-options.hx index c799286..e44b47d 100644 --- a/qemu-options.hx +++ b/qemu-options.hx
@@ -36,7 +36,8 @@ " dea-key-wrap=on|off controls support for DEA key wrapping (default=on)\n" " suppress-vmdesc=on|off disables self-describing migration (default=off)\n" " nvdimm=on|off controls NVDIMM support (default=off)\n" - " memory-encryption=<id> memory encryption object to use (default=none)\n" + " confidential-guest-support=<id> specifies confidential guest support object (default=none)\n" + " memory-encryption=<id> (memory-encryption is the alias of confidential-guest-support, recommend to use confidential-guest-support)\n" " hmat=on|off controls ACPI HMAT support (default=off)\n" " spcr=on|off controls ACPI SPCR support (default=on)\n" #ifdef CONFIG_POSIX @@ -100,8 +101,12 @@ ``nvdimm=on|off`` Enables or disables NVDIMM support. The default is off. + ``confidential-guest-support=<id>`` + confidential guest support object to use. The default is none. + ``memory-encryption=<id>`` - Memory encryption object to use. The default is none. + The alias of ``confidential-guest-support``. Recommend to use + confidential-guest-support. ``hmat=on|off`` Enables or disables ACPI Heterogeneous Memory Attribute Table @@ -1935,19 +1940,19 @@ DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev, "-fsdev local,id=id,path=path,security_model=mapped-xattr|mapped-file|passthrough|none\n" - " [,writeout=immediate][,readonly=on][,fmode=fmode][,dmode=dmode]\n" + " [,writeout=immediate][,readonly=on][,fmode=fmode][,dmode=dmode][,max_xattr=max]\n" " [[,throttling.bps-total=b]|[[,throttling.bps-read=r][,throttling.bps-write=w]]]\n" " [[,throttling.iops-total=i]|[[,throttling.iops-read=r][,throttling.iops-write=w]]]\n" " [[,throttling.bps-total-max=bm]|[[,throttling.bps-read-max=rm][,throttling.bps-write-max=wm]]]\n" " [[,throttling.iops-total-max=im]|[[,throttling.iops-read-max=irm][,throttling.iops-write-max=iwm]]]\n" " [[,throttling.iops-size=is]]\n" - "-fsdev synth,id=id\n", + "-fsdev synth,id=id[,max_xattr=max]\n", QEMU_ARCH_ALL) SRST -``-fsdev local,id=id,path=path,security_model=security_model [,writeout=writeout][,readonly=on][,fmode=fmode][,dmode=dmode] [,throttling.option=value[,throttling.option=value[,...]]]`` +``-fsdev local,id=id,path=path,security_model=security_model [,writeout=writeout][,readonly=on][,fmode=fmode][,dmode=dmode][,max_xattr=max] [,throttling.option=value[,throttling.option=value[,...]]]`` \ -``-fsdev synth,id=id[,readonly=on]`` +``-fsdev synth,id=id[,readonly=on][,max_xattr=max]`` Define a new file system device. Valid options are: ``local`` @@ -2021,6 +2026,12 @@ Let every is bytes of a request count as a new request for iops throttling purposes. + ``max_xattr=max`` + Specifies the maximum number of concurrent xattr FIDs allowed for + this export. The default is 1024. Set to 0 for allowing an infinite + number of xattr FIDs. This limit prevents host memory exhaustion + attacks by capping the number of simultaneous xattr FIDs. + -fsdev option is used along with -device driver "virtio-9p-...". ``-device virtio-9p-type,fsdev=id,mount_tag=mount_tag`` @@ -2040,14 +2051,14 @@ DEF("virtfs", HAS_ARG, QEMU_OPTION_virtfs, "-virtfs local,path=path,mount_tag=tag,security_model=mapped-xattr|mapped-file|passthrough|none\n" - " [,id=id][,writeout=immediate][,readonly=on][,fmode=fmode][,dmode=dmode][,multidevs=remap|forbid|warn]\n" - "-virtfs synth,mount_tag=tag[,id=id][,readonly=on]\n", + " [,id=id][,writeout=immediate][,readonly=on][,fmode=fmode][,dmode=dmode][,multidevs=remap|forbid|warn][,max_xattr=max]\n" + "-virtfs synth,mount_tag=tag[,id=id][,readonly=on][,max_xattr=max]\n", QEMU_ARCH_ALL) SRST -``-virtfs local,path=path,mount_tag=mount_tag ,security_model=security_model[,writeout=writeout][,readonly=on] [,fmode=fmode][,dmode=dmode][,multidevs=multidevs]`` +``-virtfs local,path=path,mount_tag=mount_tag ,security_model=security_model[,writeout=writeout][,readonly=on] [,fmode=fmode][,dmode=dmode][,multidevs=multidevs][,max_xattr=max]`` \ -``-virtfs synth,mount_tag=mount_tag`` +``-virtfs synth,mount_tag=mount_tag[,max_xattr=max]`` Define a new virtual filesystem device and expose it to the guest using a virtio-9p-device (a.k.a. 9pfs), which essentially means that a certain directory on host is made directly accessible by guest as a pass-through @@ -2113,6 +2124,12 @@ Specifies the tag name to be used by the guest to mount this export point. + ``max_xattr=max`` + Specifies the maximum number of concurrent xattr FIDs allowed for + this export. The default is 1024. Set to 0 for allowing an infinite + number of xattr FIDs. This limit prevents host memory exhaustion + attacks by capping the number of simultaneous xattr FIDs. + ``multidevs=remap|forbid|warn`` Specifies how to deal with multiple devices being shared with the same 9p export in order to avoid file ID collisions on guest. @@ -6364,9 +6381,50 @@ # |qemu_system_x86| \\ ...... \\ -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1 \\ - -machine ...,memory-encryption=sev0 \\ + -machine ...,confidential-guest-support=sev0 \\ ..... + ``-object tdx-guest,id=id,[attributes=attrs,sept-ve-disable=on|off,mrconfigid=sha384_digest,mrowner=sha384_digest,mrownerconfig=sha384_digest,quote-generation-socket=socketaddr]`` + Create an Intel Trusted Domain eXtensions (TDX) guest object, which is + the type of ``confidential-guest-support`` object. When pass the object + ID to machine's ``confidential-guest-support`` property, it can create + a TDX guest. + + The ``attributes`` property is a 64-bit integer, which specifies the + TD attributes of the TD. + + The ``sept-ve-disable`` property controls the bit 28 of TD attributes + specifically. When it's on, the EPT violation conversion to #VE on + guest access of PENDING pages is disabled. Some guest OS (e.g., Linux + TD guest) may require this to be set, otherwise they refuse to boot. + The default value is on. + + The ``mrconfigid`` property is base64 encoded SHA384 digest, which + provides the ID for non-owner-defined configuration of the guest TD, + e.g., run-time or OS configuration. The default value is all zeros. + + The ``mrowner`` property is base64 encoded SHA384 digest, which + provides the ID for guest TD's owner. The default value is all zeros. + + The ``mrownerconfig`` property is base64 encoded SHA384 digest, which + provides the ID for owner-defined configuration of the guest TD, e.g., + the configuration specific to the workload rather than the run-time of + OS. The default value is all zeros. + + The ``quote-generation-socket`` property specifies the socket address + of the Quote Generation Service (QGS). QGS is a daemon running on the + host. QEMU forwards the <GetQuote> request from TD guest to QGS and + sents the reply (which contains generated QUOTE on success) from QGS + to guest TD. + + .. parsed-literal:: + + # |qemu_system_x86| \\ + ...... \\ + -object '{"qom-type":"tdx-guest","id":"tdx","quote-generation-socket":{"type":"unix","path":"/var/run/qgs.socket"}}' \\ + -machine ...,confidential-guest-support=tdx \\ + ...... + ``-object igvm-cfg,file=file`` Create an IGVM configuration object that defines the initial state of the guest using a file in that conforms to the Independent Guest
diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index dfc95a0..00fed79 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c
@@ -71,7 +71,7 @@ QEMU_ARCH_SPARC | \ QEMU_ARCH_XTENSA) #define QEMU_ARCH_VIRTIO_CCW (QEMU_ARCH_S390X) -#define QEMU_ARCH_VIRTIO_MMIO (QEMU_ARCH_M68K) +#define QEMU_ARCH_VIRTIO_MMIO (QEMU_ARCH_M68K | QEMU_ARCH_HEXAGON) /* Please keep this table sorted by typename. */ static const QDevAlias qdev_alias_table[] = {
diff --git a/system/runstate.c b/system/runstate.c index 18e585b..08acf80 100644 --- a/system/runstate.c +++ b/system/runstate.c
@@ -706,7 +706,7 @@ " error code: 0x%" PRIx32 " error message:\"%s\"\n", info->u.tdx.error_code, message); g_free(message); - if (info->u.tdx.gpa != -1ull) { + if (info->u.tdx.has_gpa) { qemu_log_mask(LOG_GUEST_ERROR, "Additional error information " "can be found at gpa page: 0x%" PRIx64 "\n", info->u.tdx.gpa);
diff --git a/system/vl.c b/system/vl.c index 1c0da7d..1d14e2e 100644 --- a/system/vl.c +++ b/system/vl.c
@@ -3260,7 +3260,7 @@ QemuOpts *fsdev; QemuOpts *device; const char *writeout, *sock_fd, *socket, *path, *security_model, - *multidevs; + *multidevs, *max_xattr_str; olist = qemu_find_opts("virtfs"); if (!olist) { @@ -3324,6 +3324,11 @@ if (multidevs) { qemu_opt_set(fsdev, "multidevs", multidevs, &error_abort); } + max_xattr_str = qemu_opt_get(opts, "max_xattr"); + if (max_xattr_str) { + qemu_opt_set(fsdev, "max_xattr", max_xattr_str, + &error_abort); + } device = qemu_opts_create(qemu_find_opts("device"), NULL, 0, &error_abort); qemu_opt_set(device, "driver", "virtio-9p-pci", &error_abort);
diff --git a/target/Kconfig b/target/Kconfig index 3c73e3b..0288a3f 100644 --- a/target/Kconfig +++ b/target/Kconfig
@@ -16,6 +16,7 @@ source sparc/Kconfig source tricore/Kconfig source xtensa/Kconfig +source hexagon/Kconfig config TARGET_BIG_ENDIAN bool
diff --git a/target/hexagon/Kconfig b/target/hexagon/Kconfig new file mode 100644 index 0000000..7e556f3 --- /dev/null +++ b/target/hexagon/Kconfig
@@ -0,0 +1,2 @@ +config HEXAGON + bool
diff --git a/target/hexagon/arch.c b/target/hexagon/arch.c index e17e714..0a400bf 100644 --- a/target/hexagon/arch.c +++ b/target/hexagon/arch.c
@@ -208,6 +208,11 @@ * model it in qemu user mode. */ #define RAISE_FP_EXCEPTION do {} while (0) +#else + /* + * To be implemented. + */ +#define RAISE_FP_EXCEPTION do { g_assert_not_reached(); } while (0) #endif #define SOFTFLOAT_TEST_FLAG(FLAG, MYF, MYE) \
diff --git a/target/hexagon/attribs_def.h.inc b/target/hexagon/attribs_def.h.inc index 9e3a05f..6c55063 100644 --- a/target/hexagon/attribs_def.h.inc +++ b/target/hexagon/attribs_def.h.inc
@@ -52,6 +52,9 @@ DEF_ATTRIB(REGWRSIZE_8B, "Memory width is 8 bytes", "", "") DEF_ATTRIB(MEMLIKE, "Memory-like instruction", "", "") DEF_ATTRIB(MEMLIKE_PACKET_RULES, "follows Memory-like packet rules", "", "") +DEF_ATTRIB(CACHEOP, "Cache operation", "", "") +DEF_ATTRIB(COPBYADDRESS, "Cache operation by address", "", "") +DEF_ATTRIB(COPBYIDX, "Cache operation by index", "", "") DEF_ATTRIB(RELEASE, "Releases a lock", "", "") DEF_ATTRIB(ACQUIRE, "Acquires a lock", "", "") @@ -101,7 +104,9 @@ /* access to implicit registers */ DEF_ATTRIB(IMPLICIT_WRITES_LR, "Writes the link register", "", "UREG.LR") +DEF_ATTRIB(IMPLICIT_READS_PC, "Reads the program counter", "UREG.PC", "") DEF_ATTRIB(IMPLICIT_WRITES_SP, "Writes the stack pointer", "", "UREG.SP") +DEF_ATTRIB(IMPLICIT_READS_SP, "Reads the stack pointer", "UREG.SP", "") DEF_ATTRIB(IMPLICIT_WRITES_FP, "Writes the frame pointer", "", "UREG.FP") DEF_ATTRIB(IMPLICIT_WRITES_LC0, "Writes loop count for loop 0", "", "UREG.LC0") DEF_ATTRIB(IMPLICIT_WRITES_LC1, "Writes loop count for loop 1", "", "UREG.LC1") @@ -111,13 +116,19 @@ DEF_ATTRIB(IMPLICIT_WRITES_P1, "Writes Predicate 1", "", "UREG.P1") DEF_ATTRIB(IMPLICIT_WRITES_P2, "Writes Predicate 1", "", "UREG.P2") DEF_ATTRIB(IMPLICIT_WRITES_P3, "May write Predicate 3", "", "UREG.P3") -DEF_ATTRIB(IMPLICIT_READS_PC, "Reads the PC register", "", "") -DEF_ATTRIB(IMPLICIT_READS_P0, "Reads the P0 register", "", "") -DEF_ATTRIB(IMPLICIT_READS_P1, "Reads the P1 register", "", "") -DEF_ATTRIB(IMPLICIT_READS_P2, "Reads the P2 register", "", "") -DEF_ATTRIB(IMPLICIT_READS_P3, "Reads the P3 register", "", "") +DEF_ATTRIB(IMPLICIT_READS_P0, "Reads Predicate 0", "UREG.P0", "") +DEF_ATTRIB(IMPLICIT_READS_P1, "Reads Predicate 1", "UREG.P1", "") +DEF_ATTRIB(IMPLICIT_READS_P3, "Reads Predicate 3", "UREG.P3", "") DEF_ATTRIB(IMPLICIT_WRITES_USR, "May write USR", "", "") -DEF_ATTRIB(IMPLICIT_READS_SP, "Reads the SP register", "", "") +DEF_ATTRIB(IMPLICIT_WRITES_CCR, "Writes CCR register", "", "UREG.CCR") +DEF_ATTRIB(IMPLICIT_WRITES_GOSP, "Writes GOSP register", "", "UREG.GOSP") +DEF_ATTRIB(IMPLICIT_WRITES_SSR, "Writes SSR register", "", "UREG.SSR") +DEF_ATTRIB(IMPLICIT_WRITES_SGP0, "Writes SGP0 register", "", "UREG.SGP0") +DEF_ATTRIB(IMPLICIT_WRITES_SGP1, "Writes SGP1 register", "", "UREG.SGP1") +DEF_ATTRIB(IMPLICIT_WRITES_IMASK_ANYTHREAD, + "Writes IMASK for any thread", "", "") +DEF_ATTRIB(IMPLICIT_WRITES_STID_PRIO_ANYTHREAD, + "Writes STID priority for any thread", "", "") DEF_ATTRIB(COMMUTES, "The operation is communitive", "", "") DEF_ATTRIB(DEALLOCRET, "dealloc_return", "", "") DEF_ATTRIB(DEALLOCFRAME, "deallocframe", "", "") @@ -137,9 +148,14 @@ DEF_ATTRIB(RESTRICT_NOSLOT1, "No slot 1 instruction in parallel", "", "") DEF_ATTRIB(RESTRICT_PREFERSLOT0, "Try to encode into slot 0", "", "") DEF_ATTRIB(RESTRICT_PACKET_AXOK, "May exist with A-type or X-type", "", "") +DEF_ATTRIB(RESTRICT_SLOT1_AOK, "Slot 1 is allowed", "", "") DEF_ATTRIB(ICOP, "Instruction cache op", "", "") +DEF_ATTRIB(EXCEPTION_SWI, "Software interrupt exception", "", "") +DEF_ATTRIB(DMA, "DMA instruction", "", "") +DEF_ATTRIB(NO_TIMING_LOG, "Does not get logged to the timing model", "", "") + DEF_ATTRIB(HWLOOP0_END, "Ends HW loop0", "", "") DEF_ATTRIB(HWLOOP1_END, "Ends HW loop1", "", "") DEF_ATTRIB(RET_TYPE, "return type", "", "") @@ -151,6 +167,10 @@ DEF_ATTRIB(L2FETCH, "Instruction is l2fetch type", "", "") +DEF_ATTRIB(DCTAGOP, "Data cache tag operation", "", "") +DEF_ATTRIB(ICTAGOP, "Instruction cache tag operation", "", "") +DEF_ATTRIB(L2TAGOP, "L2 cache tag operation", "", "") + DEF_ATTRIB(ICINVA, "icinva", "", "") DEF_ATTRIB(DCCLEANINVA, "dccleaninva", "", "") @@ -166,6 +186,9 @@ DEF_ATTRIB(NOTE_NVSLOT0, "Can execute only in slot 0 (ST)", "", "") DEF_ATTRIB(NOTE_NOVP, "Cannot be paired with a HVX permute instruction", "", "") DEF_ATTRIB(NOTE_VA_UNARY, "Combined with HVX ALU op (must be unary)", "", "") +DEF_ATTRIB(NOTE_SLOT1_AOK, "Slot 1 is allowed", "", "") +DEF_ATTRIB(NOTE_GUEST, "Guest mode instruction", "", "") +DEF_ATTRIB(NOTE_BADTAG_UNDEF, "Bad tag results in undefined behavior", "", "") /* V6 MMVector Notes for Documentation */ DEF_ATTRIB(NOTE_SHIFT_RESOURCE, "Uses the HVX shift resource.", "", "")
diff --git a/target/hexagon/cpu-param.h b/target/hexagon/cpu-param.h index 1f0f22a..bfe9a86 100644 --- a/target/hexagon/cpu-param.h +++ b/target/hexagon/cpu-param.h
@@ -18,7 +18,7 @@ #ifndef HEXAGON_CPU_PARAM_H #define HEXAGON_CPU_PARAM_H -#define TARGET_PAGE_BITS 16 /* 64K pages */ +#define TARGET_PAGE_BITS 12 /* 4K pages */ #define TARGET_VIRT_ADDR_SPACE_BITS 32
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 2c53f2c..42d93e5 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c
@@ -16,16 +16,34 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "qemu/qemu-print.h" #include "cpu.h" #include "internal.h" +#include "exec/cputlb.h" #include "exec/translation-block.h" #include "qapi/error.h" #include "hw/core/qdev-properties.h" #include "fpu/softfloat-helpers.h" +#include "hw/hexagon/hexagon_tlb.h" #include "tcg/tcg.h" #include "exec/gdbstub.h" #include "accel/tcg/cpu-ops.h" +#include "cpu_helper.h" +#include "hex_mmu.h" + +#ifndef CONFIG_USER_ONLY +#include "macros.h" +#include "sys_macros.h" +#include "accel/tcg/cpu-ldst.h" +#include "qemu/main-loop.h" +#include "hex_interrupts.h" +#include "hexswi.h" +#include "exec/cpu-interrupt.h" +#include "exec/page-protection.h" +#include "exec/target_page.h" +#include "hw/hexagon/hexagon_globalreg.h" +#endif static ObjectClass *hexagon_cpu_class_by_name(const char *cpu_model) { @@ -43,6 +61,14 @@ } static const Property hexagon_cpu_properties[] = { +#ifndef CONFIG_USER_ONLY + DEFINE_PROP_LINK("tlb", HexagonCPU, tlb, TYPE_HEXAGON_TLB, + HexagonTLBState *), + DEFINE_PROP_UINT32("exec-start-addr", HexagonCPU, boot_addr, 0xffffffff), + DEFINE_PROP_LINK("global-regs", HexagonCPU, globalregs, + TYPE_HEXAGON_GLOBALREG, HexagonGlobalRegState *), + DEFINE_PROP_UINT32("htid", HexagonCPU, htid, 0), +#endif DEFINE_PROP_BOOL("lldb-compat", HexagonCPU, lldb_compat, false), DEFINE_PROP_UNSIGNED("lldb-stack-adjust", HexagonCPU, lldb_stack_adjust, 0, qdev_prop_uint32, target_ulong), @@ -60,6 +86,35 @@ "c24", "c25", "c26", "c27", "c28", "c29", "c30", "c31", }; +#ifndef CONFIG_USER_ONLY +const char * const hexagon_sregnames[] = { + "sgp0", "sgp1", "stid", "elr", "badva0", + "badva1", "ssr", "ccr", "htid", "badva", + "imask", "gevb", "vwctrl", "s13", "s14", + "s15", "evb", "modectl", "syscfg", "segment", + "ipendad", "vid", "vid1", "bestwait", "s24", + "schedcfg", "s26", "cfgbase", "diag", "rev", + "pcyclelo", "pcyclehi", "isdbst", "isdbcfg0", "isdbcfg1", + "livelock", "brkptpc0", "brkptcfg0", "brkptpc1", "brkptcfg1", + "isdbmbxin", "isdbmbxout", "isdben", "isdbgpr", "pmucnt4", + "pmucnt5", "pmucnt6", "pmucnt7", "pmucnt0", "pmucnt1", + "pmucnt2", "pmucnt3", "pmuevtcfg", "pmustid0", "pmuevtcfg1", + "pmustid1", "timerlo", "timerhi", "pmucfg", "s59", + "s60", "s61", "s62", "s63", +}; + +G_STATIC_ASSERT(NUM_SREGS == ARRAY_SIZE(hexagon_sregnames)); + +const char * const hexagon_gregnames[] = { + "gelr", "gsr", "gosp", "gbadva", "gcommit1t", + "gcommit2t", "gcommit3t", "gcommit4t", "gcommit5t", "gcommit6t", + "gpcycle1t", "gpcycle2t", "gpcycle3t", "gpcycle4t", "gpcycle5t", + "gpcycle6t", "gpmucnt4", "gpmucnt5", "gpmucnt6", "gpmucnt7", + "gcommit7t", "gcommit8t", "gpcycle7t", "gpcycle8t", "gpcyclelo", + "gpcyclehi", "gpmucnt0", "gpmucnt1", "gpmucnt2", "gpmucnt3", + "g30", "g31", +}; +#endif /* * One of the main debugging techniques is to use "-d cpu" and compare against * LLDB output when single stepping. However, the target and qemu put the @@ -110,6 +165,14 @@ hexagon_regnames[regnum], value); } +#ifndef CONFIG_USER_ONLY +static void print_t_sreg(FILE *f, const CPUHexagonState *env, int regnum) +{ + qemu_fprintf(f, " %s = 0x" TARGET_FMT_lx "\n", + hexagon_sregnames[regnum], env->t_sreg[regnum]); +} +#endif + static void print_vreg(FILE *f, CPUHexagonState *env, int regnum, bool skip_if_zero) { @@ -209,8 +272,7 @@ qemu_fprintf(f, " cs0 = 0x00000000\n"); qemu_fprintf(f, " cs1 = 0x00000000\n"); #else - print_reg(f, env, HEX_REG_CAUSE); - print_reg(f, env, HEX_REG_BADVA); + print_t_sreg(f, env, HEX_SREG_BADVA); print_reg(f, env, HEX_REG_CS0); print_reg(f, env, HEX_REG_CS1); #endif @@ -261,6 +323,14 @@ hexagon_raise_exception_err(env, HEX_CAUSE_PC_NOT_ALIGNED, 0); } +#ifndef CONFIG_USER_ONLY + hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, MMU_INDEX, + cpu_mmu_index(env_cpu(env), false)); + hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, PCYCLE_ENABLED, 1); +#else + hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, MMU_INDEX, MMU_USER_IDX); +#endif + return (TCGTBCPUState){ .pc = pc, .flags = hex_flags }; } @@ -271,6 +341,36 @@ cpu_env(cs)->gpr[HEX_REG_PC] = tb->pc; } +#ifndef CONFIG_USER_ONLY +bool hexagon_thread_is_enabled(CPUHexagonState *env) +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t modectl; + uint32_t thread_enabled_mask; + bool E_bit; + + if (!cpu->globalregs) { + return true; + } + modectl = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL, + env->threadId); + thread_enabled_mask = GET_FIELD(MODECTL_E, modectl); + E_bit = thread_enabled_mask & (0x1 << env->threadId); + + return E_bit; +} + +static bool hexagon_cpu_has_work(CPUState *cs) +{ + CPUHexagonState *env = cpu_env(cs); + + return hexagon_thread_is_enabled(env) && + (cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_SWI + | CPU_INTERRUPT_K0_UNLOCK | CPU_INTERRUPT_TLB_UNLOCK)); +} +#endif + static void hexagon_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data) @@ -278,11 +378,36 @@ cpu_env(cs)->gpr[HEX_REG_PC] = data[0]; } + +#ifndef CONFIG_USER_ONLY +void hexagon_cpu_soft_reset(CPUHexagonState *env) +{ + HexagonCPU *cpu; + + BQL_LOCK_GUARD(); + env->t_sreg[HEX_SREG_SSR] = 0; + hexagon_ssr_set_cause(env, HEX_CAUSE_RESET); + + cpu = env_archcpu(env); + if (cpu->globalregs) { + uint32_t evb = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_EVB, + env->threadId); + env->gpr[HEX_REG_PC] = evb; + } else { + env->gpr[HEX_REG_PC] = cpu->boot_addr; + } +} +#endif + static void hexagon_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); HexagonCPUClass *mcc = HEXAGON_CPU_GET_CLASS(obj); CPUHexagonState *env = cpu_env(cs); +#ifndef CONFIG_USER_ONLY + HexagonCPU *cpu = HEXAGON_CPU(cs); +#endif if (mcc->parent_phases.hold) { mcc->parent_phases.hold(obj, type); @@ -292,6 +417,22 @@ set_float_detect_tininess(float_tininess_before_rounding, &env->fp_status); /* Default NaN value: sign bit set, all frac bits set */ set_float_default_nan_pattern(0b11111111, &env->fp_status); +#ifndef CONFIG_USER_ONLY + memset(env->t_sreg, 0, sizeof(uint32_t) * NUM_SREGS); + memset(env->greg, 0, sizeof(uint32_t) * NUM_GREGS); + env->wait_next_pc = 0; + env->tlb_lock_state = HEX_LOCK_UNLOCKED; + env->k0_lock_state = HEX_LOCK_UNLOCKED; + env->tlb_lock_count = 0; + env->k0_lock_count = 0; + env->next_PC = 0; + + env->t_sreg[HEX_SREG_HTID] = cpu->htid; + env->threadId = cpu->htid; + hexagon_cpu_soft_reset(env); + env->cause_code = HEX_EVENT_NONE; + env->gpr[HEX_REG_PC] = cpu->boot_addr; +#endif } static void hexagon_cpu_disas_set_info(const CPUState *cs, @@ -319,21 +460,263 @@ hexagon_hvx_gdb_write_register, gdb_find_static_feature("hexagon-hvx.xml")); - qemu_init_vcpu(cs); - cpu_reset(cs); +#ifndef CONFIG_USER_ONLY + if (!HEXAGON_CPU(dev)->tlb) { + error_setg(errp, "hexagon cpu requires 'tlb' link property to be set"); + return; + } +#endif + qemu_init_vcpu(cs); + + cpu_reset(cs); mcc->parent_realize(dev, errp); } static int hexagon_cpu_mmu_index(CPUState *cs, bool ifetch) { +#ifndef CONFIG_USER_ONLY + CPUHexagonState *env = cpu_env(cs); + HexagonCPU *cpu = HEXAGON_CPU(cs); + int cpu_mode; + + BQL_LOCK_GUARD(); + if (cpu->globalregs) { + uint32_t syscfg = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SYSCFG, + env->threadId); + uint8_t mmuen = GET_SYSCFG_FIELD(SYSCFG_MMUEN, syscfg); + if (!mmuen) { + return MMU_KERNEL_IDX; + } + } + + cpu_mode = get_cpu_mode(env); + if (cpu_mode == HEX_CPU_MODE_MONITOR) { + return MMU_KERNEL_IDX; + } else if (cpu_mode == HEX_CPU_MODE_GUEST) { + return MMU_GUEST_IDX; + } +#endif + return MMU_USER_IDX; } +#ifndef CONFIG_USER_ONLY +static void hexagon_cpu_set_irq(void *opaque, int irq, int level) +{ + HexagonCPU *cpu = HEXAGON_CPU(opaque); + CPUState *cs = CPU(cpu); + CPUHexagonState *env = cpu_env(cs); + + switch (irq) { + case HEXAGON_CPU_IRQ_0 ... HEXAGON_CPU_IRQ_7: + qemu_log_mask(CPU_LOG_INT, "%s: irq %d, level %d\n", + __func__, irq, level); + if (level) { + hex_raise_interrupts(env, 1 << irq, CPU_INTERRUPT_HARD); + } + break; + default: + g_assert_not_reached(); + } +} +#endif + static void hexagon_cpu_init(Object *obj) { +#ifndef CONFIG_USER_ONLY + HexagonCPU *cpu = HEXAGON_CPU(obj); + qdev_init_gpio_in(DEVICE(cpu), hexagon_cpu_set_irq, 8); +#endif } +#ifndef CONFIG_USER_ONLY +static bool get_physical_address(CPUHexagonState *env, hwaddr *phys, int *prot, + uint64_t *size, int32_t *excp, + uint32_t address, + MMUAccessType access_type, int mmu_idx) + +{ + if (hexagon_cpu_mmu_enabled(env)) { + return hex_tlb_find_match(env, address, access_type, phys, prot, size, + excp, mmu_idx); + } else { + *phys = address & 0xFFFFFFFF; + *prot = PAGE_VALID | PAGE_READ | PAGE_WRITE | PAGE_EXEC; + *size = TARGET_PAGE_SIZE; + return true; + } +} + +/* qemu seems to only want to know about TARGET_PAGE_SIZE pages */ +static void find_qemu_subpage(vaddr *addr, hwaddr *phys, uint64_t page_size) +{ + vaddr page_start = *addr & ~((vaddr)(page_size - 1)); + vaddr offset = ((*addr - page_start) / TARGET_PAGE_SIZE) * TARGET_PAGE_SIZE; + *addr = page_start + offset; + *phys += offset; +} + +static hwaddr hexagon_cpu_get_phys_addr_debug(CPUState *cs, vaddr addr) +{ + CPUHexagonState *env = cpu_env(cs); + hwaddr phys_addr; + int prot; + uint64_t page_size = 0; + int32_t excp = 0; + int mmu_idx = MMU_KERNEL_IDX; + + if (get_physical_address(env, &phys_addr, &prot, &page_size, &excp, + addr, 0, mmu_idx)) { + find_qemu_subpage(&addr, &phys_addr, page_size); + return phys_addr; + } + + return -1; +} + + +#define INVALID_BADVA 0xbadabada + +static void set_badva_regs(CPUHexagonState *env, uint32_t VA, int slot, + MMUAccessType access_type) +{ + env->t_sreg[HEX_SREG_BADVA] = VA; + + if (access_type == MMU_INST_FETCH || slot == 0) { + env->t_sreg[HEX_SREG_BADVA0] = VA; + env->t_sreg[HEX_SREG_BADVA1] = INVALID_BADVA; + SET_SSR_FIELD(env, SSR_V0, 1); + SET_SSR_FIELD(env, SSR_V1, 0); + SET_SSR_FIELD(env, SSR_BVS, 0); + } else if (slot == 1) { + env->t_sreg[HEX_SREG_BADVA0] = INVALID_BADVA; + env->t_sreg[HEX_SREG_BADVA1] = VA; + SET_SSR_FIELD(env, SSR_V0, 0); + SET_SSR_FIELD(env, SSR_V1, 1); + SET_SSR_FIELD(env, SSR_BVS, 1); + } else { + g_assert_not_reached(); + } +} + +static void raise_tlbmiss_exception(CPUState *cs, uint32_t VA, int slot, + MMUAccessType access_type) +{ + CPUHexagonState *env = cpu_env(cs); + + set_badva_regs(env, VA, slot, access_type); + + switch (access_type) { + case MMU_INST_FETCH: + cs->exception_index = HEX_EVENT_TLB_MISS_X; + if ((VA & ~TARGET_PAGE_MASK) == 0) { + env->cause_code = HEX_CAUSE_TLBMISSX_CAUSE_NEXTPAGE; + } else { + env->cause_code = HEX_CAUSE_TLBMISSX_CAUSE_NORMAL; + } + break; + case MMU_DATA_LOAD: + cs->exception_index = HEX_EVENT_TLB_MISS_RW; + env->cause_code = HEX_CAUSE_TLBMISSRW_CAUSE_READ; + break; + case MMU_DATA_STORE: + cs->exception_index = HEX_EVENT_TLB_MISS_RW; + env->cause_code = HEX_CAUSE_TLBMISSRW_CAUSE_WRITE; + break; + } +} + +static void raise_perm_exception(CPUState *cs, uint32_t VA, int slot, + MMUAccessType access_type, int32_t excp) +{ + CPUHexagonState *env = cpu_env(cs); + + set_badva_regs(env, VA, slot, access_type); + cs->exception_index = excp; +} + +static const char *access_type_names[] = { "MMU_DATA_LOAD ", "MMU_DATA_STORE", + "MMU_INST_FETCH" }; + +static const char *mmu_idx_names[] = { "MMU_USER_IDX", "MMU_GUEST_IDX", + "MMU_KERNEL_IDX" }; + +static bool hexagon_tlb_fill(CPUState *cs, vaddr address, int size, + MMUAccessType access_type, int mmu_idx, bool probe, + uintptr_t retaddr) +{ + CPUHexagonState *env = cpu_env(cs); + int slot = 0; + hwaddr phys; + int prot = 0; + uint64_t page_size = 0; + int32_t excp = 0; + bool ret = 0; + + qemu_log_mask( + CPU_LOG_MMU, + "%s: tid = 0x%" PRIx32 ", pc = 0x%08" PRIx32 + ", vaddr = 0x%08" VADDR_PRIx ", size = %d, %s,\tprobe = %d, %s\n", + __func__, env->threadId, env->gpr[HEX_REG_PC], address, size, + access_type_names[access_type], probe, mmu_idx_names[mmu_idx]); + ret = get_physical_address(env, &phys, &prot, &page_size, &excp, address, + access_type, mmu_idx); + if (ret) { + if (!excp) { + find_qemu_subpage(&address, &phys, page_size); + tlb_set_page(cs, address, phys, prot, mmu_idx, TARGET_PAGE_SIZE); + return ret; + } + if (probe) { + return false; + } + raise_perm_exception(cs, address, slot, access_type, excp); + do_raise_exception(env, cs->exception_index, env->gpr[HEX_REG_PC], + retaddr); + } + if (probe) { + return false; + } + raise_tlbmiss_exception(cs, address, slot, access_type); + do_raise_exception(env, cs->exception_index, env->gpr[HEX_REG_PC], retaddr); +} + +#include "hw/core/sysemu-cpu-ops.h" + +static const struct SysemuCPUOps hexagon_sysemu_ops = { + .has_work = hexagon_cpu_has_work, + .get_phys_addr_debug = hexagon_cpu_get_phys_addr_debug, +}; + +static bool hexagon_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + CPUHexagonState *env = cpu_env(cs); + if (interrupt_request & CPU_INTERRUPT_TLB_UNLOCK) { + cs->halted = false; + cpu_reset_interrupt(cs, CPU_INTERRUPT_TLB_UNLOCK); + return true; + } + if (interrupt_request & CPU_INTERRUPT_K0_UNLOCK) { + cs->halted = false; + cpu_reset_interrupt(cs, CPU_INTERRUPT_K0_UNLOCK); + return true; + } + if (interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_SWI)) { + return hex_check_interrupts(env); + } + return false; +} + +static vaddr hexagon_pointer_wrap(CPUState *cs, int mmu_idx, + vaddr result, vaddr base) +{ + return result; +} + +#endif + static const TCGCPUOps hexagon_tcg_ops = { /* MTTCG not yet supported: require strict ordering */ .guest_default_memory_order = TCG_MO_ALL, @@ -344,6 +727,14 @@ .synchronize_from_tb = hexagon_cpu_synchronize_from_tb, .restore_state_to_opc = hexagon_restore_state_to_opc, .mmu_index = hexagon_cpu_mmu_index, +#ifndef CONFIG_USER_ONLY + .cpu_exec_interrupt = hexagon_cpu_exec_interrupt, + .pointer_wrap = hexagon_pointer_wrap, + .cpu_exec_reset = cpu_reset, + .tlb_fill = hexagon_tlb_fill, + .cpu_exec_halt = hexagon_cpu_has_work, + .do_interrupt = hexagon_cpu_do_interrupt, +#endif /* !CONFIG_USER_ONLY */ }; static void hexagon_cpu_class_init(ObjectClass *c, const void *data) @@ -369,9 +760,34 @@ cc->gdb_stop_before_watchpoint = true; cc->gdb_core_xml_file = "hexagon-core.xml"; cc->disas_set_info = hexagon_cpu_disas_set_info; +#ifndef CONFIG_USER_ONLY + cc->sysemu_ops = &hexagon_sysemu_ops; + dc->vmsd = &vmstate_hexagon_cpu; +#endif +#ifdef CONFIG_TCG cc->tcg_ops = &hexagon_tcg_ops; +#endif } +#ifndef CONFIG_USER_ONLY +uint32_t hexagon_greg_read(CPUHexagonState *env, uint32_t reg) +{ + if (reg <= HEX_GREG_G3) { + return env->greg[reg]; + } + switch (reg) { + case HEX_GREG_GPCYCLELO: + return hexagon_get_sys_pcycle_count_low(env); + case HEX_GREG_GPCYCLEHI: + return hexagon_get_sys_pcycle_count_high(env); + default: + qemu_log_mask(LOG_UNIMP, "reading greg %" PRId32 + " not yet supported.\n", reg); + return 0; + } +} +#endif + static void hexagon_cpu_class_base_init(ObjectClass *c, const void *data) { HexagonCPUClass *mcc = HEXAGON_CPU_CLASS(c);
diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index 7c09c97..7694fd9 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h
@@ -20,6 +20,16 @@ #include "fpu/softfloat-types.h" +#ifndef CONFIG_USER_ONLY +#define NUM_GREGS 32 +#define GREG_WRITES_MAX 2 +#define NUM_SREGS 64 +#define SREG_WRITES_MAX 2 +#endif + +typedef struct HexagonTLBState HexagonTLBState; +typedef struct HexagonGlobalRegState HexagonGlobalRegState; + #include "cpu-qom.h" #include "exec/cpu-common.h" #include "exec/target_long.h" @@ -28,9 +38,7 @@ #include "hw/core/registerfields.h" #include "qemu/bitmap.h" -#ifndef CONFIG_USER_ONLY -#error "Hexagon does not support system emulation" -#endif +#include "target/hexagon/reg_fields.h" #define NUM_PREGS 4 #define TOTAL_PER_THREAD_REGS 64 @@ -40,10 +48,47 @@ #define REG_WRITES_MAX 32 #define PRED_WRITES_MAX 5 /* 4 insns + endloop */ #define VSTORES_MAX 2 +#define MAX_TLB_ENTRIES 1024 +#define THREADS_MAX 8 #define CPU_RESOLVING_TYPE TYPE_HEXAGON_CPU +#ifndef CONFIG_USER_ONLY +#define CPU_INTERRUPT_SWI CPU_INTERRUPT_TGT_INT_0 +#define CPU_INTERRUPT_K0_UNLOCK CPU_INTERRUPT_TGT_INT_1 +#define CPU_INTERRUPT_TLB_UNLOCK CPU_INTERRUPT_TGT_INT_2 -#define MMU_USER_IDX 0 +#define HEX_CPU_MODE_USER 1 +#define HEX_CPU_MODE_GUEST 2 +#define HEX_CPU_MODE_MONITOR 3 + +#define HEX_EXE_MODE_OFF 1 +#define HEX_EXE_MODE_RUN 2 +#define HEX_EXE_MODE_WAIT 3 +#define HEX_EXE_MODE_DEBUG 4 +#endif + +#define MMU_USER_IDX 0 +#ifndef CONFIG_USER_ONLY +#define MMU_GUEST_IDX 1 +#define MMU_KERNEL_IDX 2 + +typedef enum { + HEX_LOCK_UNLOCKED = 0, + HEX_LOCK_WAITING = 1, + HEX_LOCK_OWNER = 2, + HEX_LOCK_QUEUED = 3 +} hex_lock_state_t; +#endif + + +#define HEXAGON_CPU_IRQ_0 0 +#define HEXAGON_CPU_IRQ_1 1 +#define HEXAGON_CPU_IRQ_2 2 +#define HEXAGON_CPU_IRQ_3 3 +#define HEXAGON_CPU_IRQ_4 4 +#define HEXAGON_CPU_IRQ_5 5 +#define HEXAGON_CPU_IRQ_6 6 +#define HEXAGON_CPU_IRQ_7 7 typedef struct { target_ulong va; @@ -77,12 +122,30 @@ typedef struct CPUArchState { target_ulong gpr[TOTAL_PER_THREAD_REGS]; target_ulong pred[NUM_PREGS]; + uint32_t cause_code; /* For comparing with LLDB on target - see adjust_stack_ptrs function */ target_ulong last_pc_dumped; target_ulong stack_start; uint8_t slot_cancelled; + +#ifndef CONFIG_USER_ONLY + /* Some system registers are per thread and some are global. */ + uint32_t t_sreg[NUM_SREGS]; + + uint32_t greg[NUM_GREGS]; + uint32_t wait_next_pc; + + /* This alias of CPUState.cpu_index is used by imported sources: */ + uint32_t threadId; + hex_lock_state_t tlb_lock_state; + hex_lock_state_t k0_lock_state; + uint32_t tlb_lock_count; + uint32_t k0_lock_count; + uint64_t t_cycle_count; +#endif + uint32_t next_PC; target_ulong new_value_usr; MemLog mem_log_stores[STORES_MAX]; @@ -130,16 +193,34 @@ bool lldb_compat; target_ulong lldb_stack_adjust; bool short_circuit; +#ifndef CONFIG_USER_ONLY + HexagonTLBState *tlb; + uint32_t boot_addr; + HexagonGlobalRegState *globalregs; + uint32_t htid; +#endif }; #include "cpu_bits.h" FIELD(TB_FLAGS, IS_TIGHT_LOOP, 0, 1) +FIELD(TB_FLAGS, MMU_INDEX, 1, 3) +FIELD(TB_FLAGS, PCYCLE_ENABLED, 4, 1) G_NORETURN void hexagon_raise_exception_err(CPUHexagonState *env, uint32_t exception, uintptr_t pc); +#ifndef CONFIG_USER_ONLY +/* + * @return true if the @a thread_env hardware thread is + * not stopped. + */ +bool hexagon_thread_is_enabled(CPUHexagonState *thread_env); +uint32_t hexagon_greg_read(CPUHexagonState *env, uint32_t reg); +void hexagon_cpu_soft_reset(CPUHexagonState *env); +#endif + typedef HexagonCPU ArchCPU; void hexagon_translate_init(void);
diff --git a/target/hexagon/cpu_bits.h b/target/hexagon/cpu_bits.h index aaac6b9..164e74c 100644 --- a/target/hexagon/cpu_bits.h +++ b/target/hexagon/cpu_bits.h
@@ -25,20 +25,77 @@ #define PCALIGN_MASK (PCALIGN - 1) enum hex_event { - HEX_EVENT_NONE = -1, - HEX_EVENT_TRAP0 = 0x008, + HEX_EVENT_NONE = -1, + HEX_EVENT_RESET = 0x0, + HEX_EVENT_IMPRECISE = 0x1, + HEX_EVENT_PRECISE = 0x2, + HEX_EVENT_TLB_MISS_X = 0x4, + HEX_EVENT_TLB_MISS_RW = 0x6, + HEX_EVENT_TRAP0 = 0x8, + HEX_EVENT_TRAP1 = 0x9, + HEX_EVENT_FPTRAP = 0xb, + HEX_EVENT_DEBUG = 0xc, + HEX_EVENT_INT0 = 0x10, + HEX_EVENT_INT1 = 0x11, + HEX_EVENT_INT2 = 0x12, + HEX_EVENT_INT3 = 0x13, + HEX_EVENT_INT4 = 0x14, + HEX_EVENT_INT5 = 0x15, + HEX_EVENT_INT6 = 0x16, + HEX_EVENT_INT7 = 0x17, + HEX_EVENT_INT8 = 0x18, + HEX_EVENT_INT9 = 0x19, + HEX_EVENT_INTA = 0x1a, + HEX_EVENT_INTB = 0x1b, + HEX_EVENT_INTC = 0x1c, + HEX_EVENT_INTD = 0x1d, + HEX_EVENT_INTE = 0x1e, + HEX_EVENT_INTF = 0x1f, }; enum hex_cause { HEX_CAUSE_NONE = -1, - HEX_CAUSE_TRAP0 = 0x172, - HEX_CAUSE_FETCH_NO_UPAGE = 0x012, - HEX_CAUSE_INVALID_PACKET = 0x015, - HEX_CAUSE_INVALID_OPCODE = 0x015, + HEX_CAUSE_RESET = 0x000, + HEX_CAUSE_BIU_PRECISE = 0x001, + HEX_CAUSE_UNSUPPORTED_HVX_64B = 0x002, /* QEMU-specific */ + HEX_CAUSE_DOUBLE_EXCEPT = 0x003, + HEX_CAUSE_TRAP0 = 0x008, + HEX_CAUSE_TRAP1 = 0x009, + HEX_CAUSE_FETCH_NO_XPAGE = 0x011, + HEX_CAUSE_FETCH_NO_UPAGE = 0x012, + HEX_CAUSE_INVALID_PACKET = 0x015, + HEX_CAUSE_INVALID_OPCODE = 0x015, /* alias: same cause as INVALID_PACKET */ + HEX_CAUSE_NO_COPROC_ENABLE = 0x016, + HEX_CAUSE_NO_COPROC2_ENABLE = 0x018, + HEX_CAUSE_PRIV_USER_NO_GINSN = 0x01a, + HEX_CAUSE_PRIV_USER_NO_SINSN = 0x01b, HEX_CAUSE_REG_WRITE_CONFLICT = 0x01d, - HEX_CAUSE_PC_NOT_ALIGNED = 0x01e, - HEX_CAUSE_PRIV_NO_UREAD = 0x024, - HEX_CAUSE_PRIV_NO_UWRITE = 0x025, + HEX_CAUSE_PC_NOT_ALIGNED = 0x01e, + HEX_CAUSE_MISALIGNED_LOAD = 0x020, + HEX_CAUSE_MISALIGNED_STORE = 0x021, + HEX_CAUSE_PRIV_NO_READ = 0x022, + HEX_CAUSE_PRIV_NO_WRITE = 0x023, + HEX_CAUSE_PRIV_NO_UREAD = 0x024, + HEX_CAUSE_PRIV_NO_UWRITE = 0x025, + HEX_CAUSE_COPROC_LDST = 0x026, + HEX_CAUSE_STACK_LIMIT = 0x027, + HEX_CAUSE_VWCTRL_WINDOW_MISS = 0x029, + HEX_CAUSE_IMPRECISE_NMI = 0x043, + HEX_CAUSE_IMPRECISE_MULTI_TLB_MATCH = 0x044, + HEX_CAUSE_TLBMISSX_CAUSE_NORMAL = 0x060, + HEX_CAUSE_TLBMISSX_CAUSE_NEXTPAGE = 0x061, + HEX_CAUSE_TLBMISSRW_CAUSE_READ = 0x070, + HEX_CAUSE_TLBMISSRW_CAUSE_WRITE = 0x071, + HEX_CAUSE_DEBUG_SINGLESTEP = 0x80, + HEX_CAUSE_FPTRAP_CAUSE_BADFLOAT = 0x0bf, + HEX_CAUSE_INT0 = 0x0c0, + HEX_CAUSE_INT1 = 0x0c1, + HEX_CAUSE_INT2 = 0x0c2, + HEX_CAUSE_INT3 = 0x0c3, + HEX_CAUSE_INT4 = 0x0c4, + HEX_CAUSE_INT5 = 0x0c5, + HEX_CAUSE_INT6 = 0x0c6, + HEX_CAUSE_INT7 = 0x0c7, }; #define PACKET_WORDS_MAX 4
diff --git a/target/hexagon/cpu_helper.c b/target/hexagon/cpu_helper.c new file mode 100644 index 0000000..64c5746 --- /dev/null +++ b/target/hexagon/cpu_helper.c
@@ -0,0 +1,399 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "cpu_helper.h" +#include "system/cpus.h" +#include "hw/core/boards.h" +#include "hw/hexagon/hexagon.h" +#include "hw/hexagon/hexagon_globalreg.h" +#include "hex_interrupts.h" +#include "hex_mmu.h" +#include "system/runstate.h" +#include "exec/cpu-interrupt.h" +#include "exec/target_page.h" +#include "accel/tcg/cpu-ldst.h" +#include "exec/cputlb.h" +#include "qemu/log.h" +#include "tcg/tcg-op.h" +#include "internal.h" +#include "macros.h" +#include "sys_macros.h" +#include "arch.h" + + +uint32_t hexagon_get_pmu_counter(CPUHexagonState *cur_env, int index) +{ + g_assert_not_reached(); +} + +uint64_t hexagon_get_sys_pcycle_count(CPUHexagonState *env) +{ + uint64_t total = 0; + CPUState *cs; + + g_assert(bql_locked()); + CPU_FOREACH(cs) { + CPUHexagonState *thread_env = cpu_env(cs); + total += thread_env->t_cycle_count; + } + return total; +} + +uint32_t hexagon_get_sys_pcycle_count_high(CPUHexagonState *env) +{ + return (uint32_t)(hexagon_get_sys_pcycle_count(env) >> 32); +} + +uint32_t hexagon_get_sys_pcycle_count_low(CPUHexagonState *env) +{ + return (uint32_t)(hexagon_get_sys_pcycle_count(env)); +} + +void hexagon_set_sys_pcycle_count_high(CPUHexagonState *env, uint32_t val) +{ + uint64_t old; + + g_assert(bql_locked()); + old = hexagon_get_sys_pcycle_count(env); + old = deposit64(old, 32, 32, val); + hexagon_set_sys_pcycle_count(env, old); +} + +void hexagon_set_sys_pcycle_count_low(CPUHexagonState *env, uint32_t val) +{ + uint64_t old; + + g_assert(bql_locked()); + old = hexagon_get_sys_pcycle_count(env); + old = deposit64(old, 0, 32, val); + hexagon_set_sys_pcycle_count(env, old); +} + +void hexagon_set_sys_pcycle_count(CPUHexagonState *env, uint64_t val) +{ + CPUState *cs; + uint64_t total; + int num_threads; + int64_t delta, per_thread, remainder; + + g_assert(bql_locked()); + total = hexagon_get_sys_pcycle_count(env); + + /* Count active threads */ + num_threads = 0; + CPU_FOREACH(cs) { + num_threads++; + } + g_assert(num_threads > 0); + + /* + * Distribute the delta evenly across all threads. + * Any remainder goes to the calling thread. + */ + delta = (int64_t)(val - total); + per_thread = delta / num_threads; + remainder = delta - per_thread * num_threads; + + CPU_FOREACH(cs) { + CPUHexagonState *thread_env = cpu_env(cs); + thread_env->t_cycle_count += per_thread; + } + env->t_cycle_count += remainder; +} + +static void hexagon_resume_thread(CPUHexagonState *env) +{ + CPUState *cs = env_cpu(env); + clear_wait_mode(env); + /* + * The wait instruction keeps the PC pointing to itself + * so that it has an opportunity to check for interrupts. + * + * When we come out of wait mode, adjust the PC to the + * next executable instruction. + */ + env->gpr[HEX_REG_PC] = env->wait_next_pc; + cs = env_cpu(env); + ASSERT_DIRECT_TO_GUEST_UNSET(env, cs->exception_index); + cs->halted = false; + cs->exception_index = HEX_EVENT_NONE; + qemu_cpu_kick(cs); +} + +void hexagon_resume_threads(CPUHexagonState *current_env, uint32_t mask) +{ + CPUState *cs; + CPUHexagonState *env; + + g_assert(bql_locked()); + CPU_FOREACH(cs) { + env = cpu_env(cs); + g_assert(env->threadId < THREADS_MAX); + if ((mask & (0x1 << env->threadId))) { + if (get_exe_mode(env) == HEX_EXE_MODE_WAIT) { + hexagon_resume_thread(env); + } + } + } +} + +void hexagon_modify_ssr(CPUHexagonState *env, uint32_t new, uint32_t old) +{ + bool old_EX, old_UM, old_GM, old_IE; + bool new_EX, new_UM, new_GM, new_IE; + uint8_t old_asid, new_asid; + + g_assert(bql_locked()); + + old_EX = GET_SSR_FIELD(SSR_EX, old); + old_UM = GET_SSR_FIELD(SSR_UM, old); + old_GM = GET_SSR_FIELD(SSR_GM, old); + old_IE = GET_SSR_FIELD(SSR_IE, old); + new_EX = GET_SSR_FIELD(SSR_EX, new); + new_UM = GET_SSR_FIELD(SSR_UM, new); + new_GM = GET_SSR_FIELD(SSR_GM, new); + new_IE = GET_SSR_FIELD(SSR_IE, new); + + if ((old_EX != new_EX) || + (old_UM != new_UM) || + (old_GM != new_GM)) { + hex_mmu_mode_change(env); + } + + old_asid = GET_SSR_FIELD(SSR_ASID, old); + new_asid = GET_SSR_FIELD(SSR_ASID, new); + if (new_asid != old_asid) { + CPUState *cs = env_cpu(env); + tlb_flush(cs); + } + + /* See if the interrupts have been enabled or we have exited EX mode */ + if ((new_IE && !old_IE) || + (!new_EX && old_EX)) { + hex_interrupt_update(env); + } +} + +void clear_wait_mode(CPUHexagonState *env) +{ + HexagonCPU *cpu; + uint32_t modectl, thread_wait_mask; + + g_assert(bql_locked()); + + cpu = env_archcpu(env); + if (cpu->globalregs) { + modectl = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL, + env->threadId); + thread_wait_mask = GET_FIELD(MODECTL_W, modectl); + thread_wait_mask &= ~(0x1 << env->threadId); + SET_SYSTEM_FIELD(env, HEX_SREG_MODECTL, MODECTL_W, thread_wait_mask); + } +} + +void hexagon_ssr_set_cause(CPUHexagonState *env, uint32_t cause) +{ + uint32_t old, new; + + g_assert(bql_locked()); + + old = env->t_sreg[HEX_SREG_SSR]; + SET_SYSTEM_FIELD(env, HEX_SREG_SSR, SSR_EX, 1); + SET_SYSTEM_FIELD(env, HEX_SREG_SSR, SSR_CAUSE, cause); + new = env->t_sreg[HEX_SREG_SSR]; + + hexagon_modify_ssr(env, new, old); +} + + +int get_exe_mode(CPUHexagonState *env) +{ + HexagonCPU *cpu; + uint32_t modectl, thread_enabled_mask, thread_wait_mask; + uint32_t isdbst, debugmode; + bool E_bit, W_bit, D_bit; + + g_assert(bql_locked()); + + cpu = env_archcpu(env); + modectl = cpu->globalregs ? + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL, + env->threadId) : 0; + thread_enabled_mask = GET_FIELD(MODECTL_E, modectl); + E_bit = thread_enabled_mask & (0x1 << env->threadId); + thread_wait_mask = GET_FIELD(MODECTL_W, modectl); + W_bit = thread_wait_mask & (0x1 << env->threadId); + isdbst = cpu->globalregs ? + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_ISDBST, + env->threadId) : 0; + debugmode = GET_FIELD(ISDBST_DEBUGMODE, isdbst); + D_bit = debugmode & (0x1 << env->threadId); + + if (!D_bit && !W_bit && !E_bit) { + return HEX_EXE_MODE_OFF; + } + if (!D_bit && !W_bit && E_bit) { + return HEX_EXE_MODE_RUN; + } + if (!D_bit && W_bit && E_bit) { + return HEX_EXE_MODE_WAIT; + } + if (D_bit && !W_bit && E_bit) { + return HEX_EXE_MODE_DEBUG; + } + g_assert_not_reached(); +} + +static uint32_t set_enable_mask(CPUHexagonState *env) +{ + HexagonCPU *cpu; + uint32_t modectl, thread_enabled_mask; + + g_assert(bql_locked()); + + cpu = env_archcpu(env); + if (!cpu->globalregs) { + return 0; + } + modectl = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL, + env->threadId); + thread_enabled_mask = GET_FIELD(MODECTL_E, modectl); + thread_enabled_mask |= 0x1 << env->threadId; + SET_SYSTEM_FIELD(env, HEX_SREG_MODECTL, MODECTL_E, thread_enabled_mask); + return thread_enabled_mask; +} + +static uint32_t clear_enable_mask(CPUHexagonState *env) +{ + HexagonCPU *cpu; + uint32_t modectl, thread_enabled_mask; + + g_assert(bql_locked()); + + cpu = env_archcpu(env); + if (!cpu->globalregs) { + return 0; + } + modectl = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL, + env->threadId); + thread_enabled_mask = GET_FIELD(MODECTL_E, modectl); + thread_enabled_mask &= ~(0x1 << env->threadId); + SET_SYSTEM_FIELD(env, HEX_SREG_MODECTL, MODECTL_E, thread_enabled_mask); + return thread_enabled_mask; +} +static void do_start_thread(CPUState *cs, run_on_cpu_data tbd) +{ + CPUHexagonState *env; + + BQL_LOCK_GUARD(); + + env = cpu_env(cs); + + hexagon_cpu_soft_reset(env); + + set_enable_mask(env); + + cs->halted = 0; + cs->exception_index = HEX_EVENT_NONE; + cpu_resume(cs); +} + +void hexagon_start_threads(CPUHexagonState *current_env, uint32_t mask) +{ + CPUState *cs; + CPU_FOREACH(cs) { + CPUHexagonState *env = cpu_env(cs); + if (!(mask & (0x1 << env->threadId))) { + continue; + } + + if (current_env->threadId != env->threadId) { + async_safe_run_on_cpu(cs, do_start_thread, RUN_ON_CPU_NULL); + } + } +} + +/* + * When we have all threads stopped, the return + * value to the shell is register 2 from thread 0. + */ +static uint32_t get_thread0_r2(void) +{ + CPUState *cs; + CPU_FOREACH(cs) { + CPUHexagonState *thread = cpu_env(cs); + if (thread->threadId == 0) { + return thread->gpr[2]; + } + } + g_assert_not_reached(); +} + +void hexagon_stop_thread(CPUHexagonState *env) +{ + uint32_t thread_enabled_mask; + CPUState *cs; + + BQL_LOCK_GUARD(); + + thread_enabled_mask = clear_enable_mask(env); + cs = env_cpu(env); + cpu_interrupt(cs, CPU_INTERRUPT_HALT); + if (!thread_enabled_mask) { + /* All threads are stopped, request shutdown */ + qemu_system_shutdown_request_with_code( + SHUTDOWN_CAUSE_GUEST_SHUTDOWN, get_thread0_r2()); + } +} + +static int sys_in_monitor_mode_ssr(uint32_t ssr) +{ + if ((GET_SSR_FIELD(SSR_EX, ssr) != 0) || + ((GET_SSR_FIELD(SSR_EX, ssr) == 0) && + (GET_SSR_FIELD(SSR_UM, ssr) == 0))) { + return 1; + } + return 0; +} + +static int sys_in_guest_mode_ssr(uint32_t ssr) +{ + if ((GET_SSR_FIELD(SSR_EX, ssr) == 0) && + (GET_SSR_FIELD(SSR_UM, ssr) != 0) && + (GET_SSR_FIELD(SSR_GM, ssr) != 0)) { + return 1; + } + return 0; +} + +static int sys_in_user_mode_ssr(uint32_t ssr) +{ + if ((GET_SSR_FIELD(SSR_EX, ssr) == 0) && + (GET_SSR_FIELD(SSR_UM, ssr) != 0) && + (GET_SSR_FIELD(SSR_GM, ssr) == 0)) { + return 1; + } + return 0; +} + +int get_cpu_mode(CPUHexagonState *env) +{ + uint32_t ssr = env->t_sreg[HEX_SREG_SSR]; + + if (sys_in_monitor_mode_ssr(ssr)) { + return HEX_CPU_MODE_MONITOR; + } else if (sys_in_guest_mode_ssr(ssr)) { + return HEX_CPU_MODE_GUEST; + } else if (sys_in_user_mode_ssr(ssr)) { + return HEX_CPU_MODE_USER; + } + return HEX_CPU_MODE_MONITOR; +}
diff --git a/target/hexagon/cpu_helper.h b/target/hexagon/cpu_helper.h new file mode 100644 index 0000000..d176750 --- /dev/null +++ b/target/hexagon/cpu_helper.h
@@ -0,0 +1,26 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HEXAGON_CPU_HELPER_H +#define HEXAGON_CPU_HELPER_H + +uint32_t hexagon_get_pmu_counter(CPUHexagonState *cur_env, int index); +void hexagon_modify_ssr(CPUHexagonState *env, uint32_t new, uint32_t old); +int get_cpu_mode(CPUHexagonState *env); +int get_exe_mode(CPUHexagonState *env); +void clear_wait_mode(CPUHexagonState *env); +void hexagon_ssr_set_cause(CPUHexagonState *env, uint32_t cause); +void hexagon_start_threads(CPUHexagonState *env, uint32_t mask); +void hexagon_stop_thread(CPUHexagonState *env); +void hexagon_resume_threads(CPUHexagonState *env, uint32_t mask); +uint64_t hexagon_get_sys_pcycle_count(CPUHexagonState *env); +uint32_t hexagon_get_sys_pcycle_count_high(CPUHexagonState *env); +uint32_t hexagon_get_sys_pcycle_count_low(CPUHexagonState *env); +void hexagon_set_sys_pcycle_count_high(CPUHexagonState *env, uint32_t val); +void hexagon_set_sys_pcycle_count_low(CPUHexagonState *env, uint32_t val); +void hexagon_set_sys_pcycle_count(CPUHexagonState *env, uint64_t val); + +#endif
diff --git a/target/hexagon/decode.c b/target/hexagon/decode.c index 1595451..6eddcca 100644 --- a/target/hexagon/decode.c +++ b/target/hexagon/decode.c
@@ -185,6 +185,8 @@ if ((GET_ATTRIB(opcode, A_JUMP)) || (GET_ATTRIB(opcode, A_CALL)) || (opcode == J2_trap0) || + (opcode == J2_trap1) || + (opcode == J2_rte) || (opcode == J2_pause)) { /* Exception to A_JUMP attribute */ if (opcode == J4_hintjumpr) { @@ -363,6 +365,18 @@ break; } } + /* + * And at the very very very end, move any RTE's, since they update + * user/supervisor mode. + */ +#if !defined(CONFIG_USER_ONLY) + for (i = 0; i < last_insn; i++) { + if (packet->insn[i].opcode == J2_rte) { + decode_send_insn_to(packet, i, last_insn); + break; + } + } +#endif } static void
diff --git a/target/hexagon/gen_analyze_funcs.py b/target/hexagon/gen_analyze_funcs.py index fdefd5b..44bb22e 100755 --- a/target/hexagon/gen_analyze_funcs.py +++ b/target/hexagon/gen_analyze_funcs.py
@@ -22,7 +22,6 @@ import string import hex_common - ## ## Generate the code to analyze the instruction ## For A2_add: Rd32=add(Rs32,Rt32), { RdV=RsV+RtV;} @@ -42,6 +41,13 @@ def gen_analyze_func(f, tag, regs, imms): f.write(f"static void analyze_{tag}(DisasContext *ctx)\n") f.write("{\n") + if hex_common.tag_ignore(tag): + f.write("}\n\n") + return + + if hex_common.is_sysemu_tag(tag): + f.write("#ifndef CONFIG_USER_ONLY\n") + f.write(" Insn *insn G_GNUC_UNUSED = ctx->insn;\n") if (hex_common.is_hvx_insn(tag)): if hex_common.has_hvx_helper(tag): @@ -58,7 +64,8 @@ def gen_analyze_func(f, tag, regs, imms): for regno, register in enumerate(regs): reg_type, reg_id = register reg = hex_common.get_register(tag, reg_type, reg_id) - reg.decl_reg_num(f, regno) + if reg.is_read() or reg.is_written(): + reg.decl_reg_num(f, regno) ## Analyze the register reads for regno, register in enumerate(regs): @@ -78,6 +85,9 @@ def gen_analyze_func(f, tag, regs, imms): f.write(" mark_implicit_writes(ctx);\n") + if hex_common.is_sysemu_tag(tag): + f.write("#endif /* !CONFIG_USER_ONLY */\n") + f.write("}\n\n")
diff --git a/target/hexagon/gen_helper_funcs.py b/target/hexagon/gen_helper_funcs.py index 6206104..1629ebc 100755 --- a/target/hexagon/gen_helper_funcs.py +++ b/target/hexagon/gen_helper_funcs.py
@@ -104,30 +104,24 @@ def main(): tagimms = hex_common.get_tagimms() with open(args.out, "w") as f: - for tag in hex_common.tags: - ## Skip the priv instructions - if "A_PRIV" in hex_common.attribdict[tag]: - continue - ## Skip the guest instructions - if "A_GUEST" in hex_common.attribdict[tag]: - continue - ## Skip the floating point instructions - if "A_FPOP" in hex_common.attribdict[tag]: - continue - ## Skip the diag instructions - if tag == "Y6_diag": - continue - if tag == "Y6_diag0": - continue - if tag == "Y6_diag1": + for tag in hex_common.get_user_tags(): + if hex_common.tag_ignore(tag): continue if hex_common.skip_qemu_helper(tag): continue if hex_common.is_idef_parser_enabled(tag): continue - gen_helper_function(f, tag, tagregs, tagimms) + f.write("#if !defined(CONFIG_USER_ONLY)\n") + for tag in hex_common.get_sys_tags(): + if hex_common.skip_qemu_helper(tag): + continue + if hex_common.is_idef_parser_enabled(tag): + continue + gen_helper_function(f, tag, tagregs, tagimms) + f.write("#endif\n") + if __name__ == "__main__": main()
diff --git a/target/hexagon/gen_helper_protos.py b/target/hexagon/gen_helper_protos.py index 77f8e0a..59c8bdd 100755 --- a/target/hexagon/gen_helper_protos.py +++ b/target/hexagon/gen_helper_protos.py
@@ -59,19 +59,8 @@ def main(): tagimms = hex_common.get_tagimms() with open(args.out, "w") as f: - for tag in hex_common.tags: - ## Skip the priv instructions - if "A_PRIV" in hex_common.attribdict[tag]: - continue - ## Skip the guest instructions - if "A_GUEST" in hex_common.attribdict[tag]: - continue - ## Skip the diag instructions - if tag == "Y6_diag": - continue - if tag == "Y6_diag0": - continue - if tag == "Y6_diag1": + for tag in hex_common.get_user_tags(): + if hex_common.tag_ignore(tag): continue if hex_common.skip_qemu_helper(tag): @@ -81,6 +70,18 @@ def main(): gen_helper_prototype(f, tag, tagregs, tagimms) + f.write("#if !defined(CONFIG_USER_ONLY)\n") + for tag in hex_common.get_sys_tags(): + if hex_common.tag_ignore(tag): + continue + if hex_common.skip_qemu_helper(tag): + continue + if hex_common.is_idef_parser_enabled(tag): + continue + + gen_helper_prototype(f, tag, tagregs, tagimms) + f.write("#endif\n") + if __name__ == "__main__": main()
diff --git a/target/hexagon/gen_idef_parser_funcs.py b/target/hexagon/gen_idef_parser_funcs.py index 2f6e826..32bce9b 100644 --- a/target/hexagon/gen_idef_parser_funcs.py +++ b/target/hexagon/gen_idef_parser_funcs.py
@@ -60,6 +60,8 @@ def main(): f.write('#include "macros.h.inc"\n\n') for tag in hex_common.tags: + if hex_common.tag_ignore(tag): + continue ## Skip the priv instructions if "A_PRIV" in hex_common.attribdict[tag]: continue
diff --git a/target/hexagon/gen_op_attribs.py b/target/hexagon/gen_op_attribs.py index bbbb02d..94dd1f8 100755 --- a/target/hexagon/gen_op_attribs.py +++ b/target/hexagon/gen_op_attribs.py
@@ -38,7 +38,7 @@ def main(): ## Generate all the attributes associated with each instruction ## with open(args.out, "w") as f: - for tag in hex_common.tags: + for tag in hex_common.get_all_tags(): f.write( f"OP_ATTRIB({tag},ATTRIBS(" f'{",".join(sorted(hex_common.attribdict[tag]))}))\n'
diff --git a/target/hexagon/gen_opcodes_def.py b/target/hexagon/gen_opcodes_def.py index 94a19ff..17ba3f9 100755 --- a/target/hexagon/gen_opcodes_def.py +++ b/target/hexagon/gen_opcodes_def.py
@@ -37,7 +37,10 @@ def main(): ## Generate a list of all the opcodes ## with open(args.out, "w") as f: - for tag in hex_common.tags: + for tag in hex_common.get_user_tags(): + f.write(f"OPCODE({tag}),\n") + + for tag in hex_common.get_sys_tags(): f.write(f"OPCODE({tag}),\n")
diff --git a/target/hexagon/gen_tcg.h b/target/hexagon/gen_tcg.h index 0159e5c..a28af0c 100644 --- a/target/hexagon/gen_tcg.h +++ b/target/hexagon/gen_tcg.h
@@ -488,6 +488,7 @@ /* dczeroa clears the 32 byte cache line at the address given */ #define fGEN_TCG_Y2_dczeroa(SHORTCODE) SHORTCODE +#define fGEN_TCG_Y2_dczeroa_nt(SHORTCODE) SHORTCODE /* In linux-user mode, these are not modelled, suppress compiler warning */ #define fGEN_TCG_Y2_dcinva(SHORTCODE) \ @@ -1112,6 +1113,9 @@ RdV, tcg_constant_tl(0)); \ } while (0) +#define fGEN_TCG_Y2_break(SHORTCODE) +#define fGEN_TCG_J2_unpause(SHORTCODE) + #define fGEN_TCG_J2_pause(SHORTCODE) \ do { \ uiV = uiV; \ @@ -1321,6 +1325,11 @@ RsV = RsV; \ uiV = uiV; \ } while (0) +#define fGEN_TCG_Y2_dcfetchbo_nt(SHORTCODE) \ + do { \ + RsV = RsV; \ + uiV = uiV; \ + } while (0) #define fGEN_TCG_L2_loadw_aq(SHORTCODE) SHORTCODE #define fGEN_TCG_L4_loadd_aq(SHORTCODE) SHORTCODE @@ -1340,13 +1349,6 @@ #define fGEN_TCG_S2_storew_rl_st_vi(SHORTCODE) SHORTCODE #define fGEN_TCG_S4_stored_rl_st_vi(SHORTCODE) SHORTCODE -#define fGEN_TCG_J2_trap0(SHORTCODE) \ - do { \ - uiV = uiV; \ - tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], ctx->pkt.pc); \ - TCGv excp = tcg_constant_tl(HEX_EVENT_TRAP0); \ - gen_helper_raise_exception(tcg_env, excp); \ - } while (0) #endif #define fGEN_TCG_A2_nop(SHORTCODE) do { } while (0)
diff --git a/target/hexagon/gen_tcg_funcs.py b/target/hexagon/gen_tcg_funcs.py index e7f90a0..6d5d99c 100755 --- a/target/hexagon/gen_tcg_funcs.py +++ b/target/hexagon/gen_tcg_funcs.py
@@ -21,7 +21,7 @@ import re import string import hex_common - +from textwrap import dedent ## ## Generate the TCG code to call the helper @@ -49,6 +49,18 @@ def gen_tcg_func(f, tag, regs, imms): f.write(" Insn *insn G_GNUC_UNUSED = ctx->insn;\n") + if "A_PRIV" in hex_common.attribdict[tag]: + f.write(dedent("""\ +#ifdef CONFIG_USER_ONLY + hex_gen_exception_end_tb(ctx, HEX_CAUSE_PRIV_USER_NO_SINSN); +#else +""")) + if "A_GUEST" in hex_common.attribdict[tag]: + f.write(dedent("""\ +#ifdef CONFIG_USER_ONLY + hex_gen_exception_end_tb(ctx, HEX_CAUSE_PRIV_USER_NO_GINSN); +#else +""")) if hex_common.need_ea(tag): f.write(" TCGv EA G_GNUC_UNUSED = tcg_temp_new();\n") @@ -100,6 +112,11 @@ def gen_tcg_func(f, tag, regs, imms): if reg.is_written(): reg.gen_write(f, tag) + if ( + "A_PRIV" in hex_common.attribdict[tag] + or "A_GUEST" in hex_common.attribdict[tag] + ): + f.write("#endif /* CONFIG_USER_ONLY */\n") f.write("}\n\n") @@ -124,18 +141,10 @@ def main(): f.write('#include "idef-generated-emitter.h.inc"\n\n') for tag in hex_common.tags: - ## Skip the priv instructions - if "A_PRIV" in hex_common.attribdict[tag]: - continue - ## Skip the guest instructions - if "A_GUEST" in hex_common.attribdict[tag]: - continue - ## Skip the diag instructions - if tag == "Y6_diag": - continue - if tag == "Y6_diag0": - continue - if tag == "Y6_diag1": + if hex_common.tag_ignore(tag): + f.write(f"static void generate_{tag}" + f"(DisasContext *ctx)\n") + f.write("{\n}\n\n") continue gen_def_tcg_func(f, tag, tagregs, tagimms)
diff --git a/target/hexagon/gen_tcg_sys.h b/target/hexagon/gen_tcg_sys.h new file mode 100644 index 0000000..af50322 --- /dev/null +++ b/target/hexagon/gen_tcg_sys.h
@@ -0,0 +1,139 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HEXAGON_GEN_TCG_SYS_H +#define HEXAGON_GEN_TCG_SYS_H + +/* System mode instructions */ +#define fGEN_TCG_Y2_swi(SHORTCODE) \ + gen_helper_swi(tcg_env, RsV) + +#define fGEN_TCG_Y2_cswi(SHORTCODE) \ + gen_helper_cswi(tcg_env, RsV) + +#define fGEN_TCG_Y2_ciad(SHORTCODE) \ + gen_helper_ciad(tcg_env, RsV) + +#define fGEN_TCG_Y4_siad(SHORTCODE) \ + gen_helper_siad(tcg_env, RsV) + +#define fGEN_TCG_Y2_iassignw(SHORTCODE) \ + gen_helper_iassignw(tcg_env, RsV) + +#define fGEN_TCG_Y2_iassignr(SHORTCODE) \ + gen_helper_iassignr(RdV, tcg_env, RsV) + +#define fGEN_TCG_Y2_getimask(SHORTCODE) \ + gen_helper_getimask(RdV, tcg_env, RsV) + +#define fGEN_TCG_Y2_setimask(SHORTCODE) \ + gen_helper_setimask(tcg_env, PtV, RsV) + +#define fGEN_TCG_Y2_setprio(SHORTCODE) \ + gen_helper_setprio(tcg_env, PtV, RsV) + +#define fGEN_TCG_Y2_crswap0(SHORTCODE) \ + do { \ + TCGv_i32 tmp = tcg_temp_new_i32(); \ + tcg_gen_mov_tl(tmp, RxV); \ + tcg_gen_mov_tl(RxV, hex_t_sreg[HEX_SREG_SGP0]); \ + if (ctx->need_commit) { \ + tcg_gen_mov_tl(ctx->t_sreg_new_value[HEX_SREG_SGP0], tmp); \ + } else { \ + tcg_gen_mov_tl(hex_t_sreg[HEX_SREG_SGP0], tmp); \ + } \ + } while (0) + +#define fGEN_TCG_Y4_crswap1(SHORTCODE) \ + do { \ + TCGv_i32 tmp = tcg_temp_new_i32(); \ + tcg_gen_mov_tl(tmp, RxV); \ + tcg_gen_mov_tl(RxV, hex_t_sreg[HEX_SREG_SGP1]); \ + if (ctx->need_commit) { \ + tcg_gen_mov_tl(ctx->t_sreg_new_value[HEX_SREG_SGP1], tmp); \ + } else { \ + tcg_gen_mov_tl(hex_t_sreg[HEX_SREG_SGP1], tmp); \ + } \ + } while (0) + +#define fGEN_TCG_Y4_crswap10(SHORTCODE) \ + do { \ + TCGv_i64 tmp = tcg_temp_new_i64(); \ + tcg_gen_mov_i64(tmp, RxxV); \ + tcg_gen_concat_i32_i64(RxxV, \ + hex_t_sreg[HEX_SREG_SGP0], \ + hex_t_sreg[HEX_SREG_SGP1]); \ + if (ctx->need_commit) { \ + tcg_gen_extrl_i64_i32( \ + ctx->t_sreg_new_value[HEX_SREG_SGP0], tmp); \ + tcg_gen_extrh_i64_i32( \ + ctx->t_sreg_new_value[HEX_SREG_SGP1], tmp); \ + } else { \ + tcg_gen_extrl_i64_i32(hex_t_sreg[HEX_SREG_SGP0], tmp); \ + tcg_gen_extrh_i64_i32(hex_t_sreg[HEX_SREG_SGP1], tmp); \ + } \ + } while (0) + +#define fGEN_TCG_Y2_wait(SHORTCODE) \ + do { \ + RsV = RsV; \ + gen_helper_wait(tcg_env, tcg_constant_tl(ctx->pkt.pc)); \ + } while (0) + +#define fGEN_TCG_Y2_resume(SHORTCODE) \ + gen_helper_resume(tcg_env, RsV) + +#define fGEN_TCG_Y2_start(SHORTCODE) \ + gen_helper_start(tcg_env, RsV) + +#define fGEN_TCG_Y2_stop(SHORTCODE) \ + do { \ + RsV = RsV; \ + gen_helper_stop(tcg_env); \ + } while (0) + +#define fGEN_TCG_Y2_tfrscrr(SHORTCODE) \ + tcg_gen_mov_tl(RdV, SsV) + +#define fGEN_TCG_Y2_tfrsrcr(SHORTCODE) \ + tcg_gen_mov_tl(SdV, RsV) + +#define fGEN_TCG_Y4_tfrscpp(SHORTCODE) \ + tcg_gen_mov_i64(RddV, SssV) + +#define fGEN_TCG_Y4_tfrspcp(SHORTCODE) \ + tcg_gen_mov_i64(SddV, RssV) + +#define fGEN_TCG_G4_tfrgcrr(SHORTCODE) \ + tcg_gen_mov_tl(RdV, GsV) + +#define fGEN_TCG_G4_tfrgrcr(SHORTCODE) \ + tcg_gen_mov_tl(GdV, RsV) + +#define fGEN_TCG_G4_tfrgcpp(SHORTCODE) \ + tcg_gen_mov_i64(RddV, GssV) + +#define fGEN_TCG_G4_tfrgpcp(SHORTCODE) \ + tcg_gen_mov_i64(GddV, RssV) + + +/* + * rte (return from exception) + * Clear the EX bit in SSR + * Jump to ELR + */ +#define fGEN_TCG_J2_rte(SHORTCODE) \ + do { \ + TCGv_i32 new_ssr = tcg_temp_new_i32(); \ + tcg_gen_deposit_tl(new_ssr, hex_t_sreg[HEX_SREG_SSR], \ + tcg_constant_tl(0), \ + reg_field_info[SSR_EX].offset, \ + reg_field_info[SSR_EX].width); \ + gen_log_sreg_write(ctx, HEX_SREG_SSR, new_ssr); \ + gen_jumpr(ctx, hex_t_sreg[HEX_SREG_ELR]); \ + } while (0) + +#endif
diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index 5d5adac..1ebc747 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c
@@ -23,6 +23,7 @@ #include "exec/helper-gen.h" #include "insn.h" #include "opcodes.h" +#include "sys_macros.h" #include "translate.h" #define QEMU_GENERATE /* Used internally by macros.h */ #include "macros.h" @@ -30,6 +31,10 @@ #undef QEMU_GENERATE #include "gen_tcg.h" #include "gen_tcg_hvx.h" +#ifndef CONFIG_USER_ONLY +#include "gen_tcg_sys.h" +#endif + #include "genptr.h" TCGv gen_read_reg(TCGv result, int num) @@ -120,6 +125,156 @@ } } +#ifndef CONFIG_USER_ONLY +G_GNUC_UNUSED +static bool greg_writable(int rnum, bool pair) +{ + if (pair) { + if (rnum < HEX_GREG_G3) { + return true; + } + qemu_log_mask(LOG_UNIMP, + "Warning: ignoring write to guest register pair G%d:%d\n", + rnum + 1, rnum); + } else { + if (rnum <= HEX_GREG_G3) { + return true; + } + qemu_log_mask(LOG_UNIMP, + "Warning: ignoring write to guest register G%d\n", rnum); + } + return false; +} + +G_GNUC_UNUSED +static void check_greg_impl(int rnum, bool pair) +{ + if (pair && (!greg_implemented(rnum) || !greg_implemented(rnum + 1))) { + qemu_log_mask(LOG_UNIMP, + "Warning: guest register pair G%d:%d is unimplemented or " + "reserved. Read will yield 0.\n", + rnum + 1, rnum); + } else if (!pair && !greg_implemented(rnum)) { + qemu_log_mask(LOG_UNIMP, + "Warning: guest register G%d is unimplemented or reserved." + " Read will yield 0.\n", rnum); + } +} + +G_GNUC_UNUSED +static inline void gen_log_greg_write(DisasContext *ctx, int rnum, TCGv_i32 val) +{ + tcg_gen_mov_i32(ctx->greg_new_value[rnum], val); +} + +G_GNUC_UNUSED +static void gen_log_greg_write_pair(DisasContext *ctx, int rnum, TCGv_i64 val) +{ + TCGv_i32 val32 = tcg_temp_new_i32(); + + /* Low word */ + tcg_gen_extrl_i64_i32(val32, val); + gen_log_greg_write(ctx, rnum, val32); + + /* High word */ + tcg_gen_extrh_i64_i32(val32, val); + gen_log_greg_write(ctx, rnum + 1, val32); +} + +static const uint32_t sreg_immut_masks[NUM_SREGS] = { + [HEX_SREG_STID] = 0xff00ff00, + [HEX_SREG_ELR] = 0x00000003, + [HEX_SREG_SSR] = 0x00008000, + [HEX_SREG_CCR] = 0x10e0ff24, + [HEX_SREG_HTID] = IMMUTABLE, + [HEX_SREG_IMASK] = 0xffff0000, + [HEX_SREG_GEVB] = 0x000000ff, +}; + +G_GNUC_UNUSED +static void gen_log_sreg_write(DisasContext *ctx, int rnum, TCGv_i32 val) +{ + const uint32_t reg_mask = sreg_immut_masks[rnum]; + + if (reg_mask != IMMUTABLE) { + if (rnum < HEX_SREG_GLB_START) { + gen_masked_reg_write(val, hex_t_sreg[rnum], reg_mask); + if (ctx->need_commit || rnum == HEX_SREG_SSR) { + tcg_gen_mov_i32(ctx->t_sreg_new_value[rnum], val); + } else { + tcg_gen_mov_i32(hex_t_sreg[rnum], val); + } + } else { + gen_helper_sreg_write_masked(tcg_env, tcg_constant_i32(rnum), val); + } + } +} + +G_GNUC_UNUSED +static void gen_log_sreg_write_pair(DisasContext *ctx, int rnum, TCGv_i64 val) +{ + TCGv_i32 val32 = tcg_temp_new_i32(); + + /* Low word */ + tcg_gen_extrl_i64_i32(val32, val); + gen_log_sreg_write(ctx, rnum, val32); + + /* High word */ + tcg_gen_extrh_i64_i32(val32, val); + gen_log_sreg_write(ctx, rnum + 1, val32); +} + +G_GNUC_UNUSED +static void gen_read_sreg(TCGv_i32 dst, int reg_num) +{ + if (reg_num >= HEX_SREG_GLB_START || reg_num == HEX_SREG_BADVA) { + gen_helper_sreg_read(dst, tcg_env, tcg_constant_i32(reg_num)); + } else { + tcg_gen_mov_i32(dst, hex_t_sreg[reg_num]); + } +} + +G_GNUC_UNUSED +static void gen_read_sreg_pair(TCGv_i64 dst, int reg_num) +{ + if (reg_num < HEX_SREG_GLB_START) { + if (reg_num + 1 == HEX_SREG_BADVA) { + TCGv_i32 badva = tcg_temp_new_i32(); + gen_helper_sreg_read(badva, tcg_env, + tcg_constant_i32(HEX_SREG_BADVA)); + tcg_gen_concat_i32_i64(dst, hex_t_sreg[reg_num], badva); + } else { + tcg_gen_concat_i32_i64(dst, hex_t_sreg[reg_num], + hex_t_sreg[reg_num + 1]); + } + } else { + gen_helper_sreg_read_pair(dst, tcg_env, tcg_constant_i32(reg_num)); + } +} + +G_GNUC_UNUSED +static void gen_read_greg(TCGv_i32 dst, int reg_num) +{ + if (reg_num <= HEX_GREG_G3) { + tcg_gen_mov_i32(dst, hex_greg[reg_num]); + } else { + gen_helper_greg_read(dst, tcg_env, tcg_constant_i32(reg_num)); + } +} + +G_GNUC_UNUSED +static void gen_read_greg_pair(TCGv_i64 dst, int reg_num) +{ + if (reg_num == HEX_GREG_G0 || reg_num == HEX_GREG_G2) { + tcg_gen_concat_i32_i64(dst, hex_greg[reg_num], + hex_greg[reg_num + 1]); + } else { + gen_helper_greg_read_pair(dst, tcg_env, tcg_constant_i32(reg_num)); + } +} +#endif + + void gen_pred_write(DisasContext *ctx, int pnum, TCGv val) { TCGv pred = get_result_pred(ctx, pnum); @@ -738,26 +893,30 @@ tcg_gen_qemu_ld_i64(frame, EA, ctx->mem_idx, MO_LE | MO_UQ); } -#ifndef CONFIG_HEXAGON_IDEF_PARSER /* Stack overflow check */ -static void gen_framecheck(TCGv EA, int framesize) +void gen_framecheck(DisasContext *ctx, TCGv_i32 addr, TCGv_i32 ea) { - /* Not modelled in linux-user mode */ - /* Placeholder for system mode */ #ifndef CONFIG_USER_ONLY - g_assert_not_reached(); + TCGLabel *ok = gen_new_label(); + tcg_gen_brcond_i32(TCG_COND_GEU, addr, hex_gpr[HEX_REG_FRAMELIMIT], ok); + gen_helper_raise_stack_overflow(tcg_env, + tcg_constant_i32(ctx->insn->slot), ea); + gen_set_label(ok); #endif } +#ifndef CONFIG_HEXAGON_IDEF_PARSER static void gen_allocframe(DisasContext *ctx, TCGv r29, int framesize) { TCGv r30 = get_result_gpr(ctx, HEX_REG_FP); + TCGv_i32 new_r29 = tcg_temp_new_i32(); TCGv_i64 frame; tcg_gen_addi_tl(r30, r29, -8); frame = gen_frame_scramble(); gen_store8(tcg_env, r30, frame, ctx->insn->slot); - gen_framecheck(r30, framesize); - tcg_gen_subi_tl(r29, r30, framesize); + tcg_gen_subi_tl(new_r29, r30, framesize); + gen_framecheck(ctx, new_r29, hex_gpr[HEX_REG_PC]); + tcg_gen_mov_tl(r29, new_r29); } static void gen_deallocframe(DisasContext *ctx, TCGv_i64 r31_30, TCGv r30)
diff --git a/target/hexagon/helper.h b/target/hexagon/helper.h index 6445682..033e561 100644 --- a/target/hexagon/helper.h +++ b/target/hexagon/helper.h
@@ -18,7 +18,7 @@ #include "internal.h" #include "helper_protos_generated.h.inc" -DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_RETURN, noreturn, env, i32) +DEF_HELPER_FLAGS_3(raise_exception, TCG_CALL_NO_RETURN, noreturn, env, i32, i32) DEF_HELPER_2(commit_store, void, env, int) DEF_HELPER_3(gather_store, void, env, i32, int) DEF_HELPER_1(commit_hvx_stores, void, env) @@ -107,3 +107,28 @@ DEF_HELPER_2(probe_pkt_scalar_store_s0, void, env, int) DEF_HELPER_2(probe_hvx_stores, void, env, int) DEF_HELPER_2(probe_pkt_scalar_hvx_stores, void, env, int) + +#if !defined(CONFIG_USER_ONLY) +DEF_HELPER_3(raise_stack_overflow, void, env, i32, i32) +DEF_HELPER_2(swi, void, env, i32) +DEF_HELPER_2(cswi, void, env, i32) +DEF_HELPER_2(ciad, void, env, i32) +DEF_HELPER_2(siad, void, env, i32) +DEF_HELPER_2(iassignw, void, env, i32) +DEF_HELPER_2(iassignr, i32, env, i32) +DEF_HELPER_2(getimask, i32, env, i32) +DEF_HELPER_3(setimask, void, env, i32, i32) +DEF_HELPER_2(sreg_read, i32, env, i32) +DEF_HELPER_2(sreg_read_pair, i64, env, i32) +DEF_HELPER_2(greg_read, i32, env, i32) +DEF_HELPER_2(greg_read_pair, i64, env, i32) +DEF_HELPER_3(sreg_write_masked, void, env, i32, i32) +DEF_HELPER_3(setprio, void, env, i32, i32) +DEF_HELPER_2(start, void, env, i32) +DEF_HELPER_1(stop, void, env) +DEF_HELPER_2(wait, void, env, i32) +DEF_HELPER_2(resume, void, env, i32) +DEF_HELPER_1(resched, void, env) +DEF_HELPER_3(modify_ssr, void, env, i32, i32) +DEF_HELPER_1(pending_interrupt, void, env) +#endif
diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py index e37d5a5..d91a653 100755 --- a/target/hexagon/hex_common.py +++ b/target/hexagon/hex_common.py
@@ -33,6 +33,41 @@ overrides = {} # tags with helper overrides idef_parser_enabled = {} # tags enabled for idef-parser + +def is_sysemu_tag(tag): + return bool(attribdict[tag] & {"A_PRIV", "A_GUEST"}) + + +def tag_ignore(tag): + tag_skips = ( + "Y6_diag", + "Y6_diag0", + "Y6_diag1", + ) + attr_skips = { + "A_FAKEINSN", + "A_MAPPING", + "A_CONDMAPPING", + } + return tag in tag_skips or attribdict[tag] & attr_skips + + +def get_sys_tags(): + return sorted( + tag for tag in frozenset(tags) if is_sysemu_tag(tag) + ) + + +def get_user_tags(): + return sorted( + tag for tag in frozenset(tags) if not is_sysemu_tag(tag) + ) + + +def get_all_tags(): + return get_user_tags() + get_sys_tags() + + # We should do this as a hash for performance, # but to keep order let's keep it as a list. def uniquify(seq): @@ -93,8 +128,11 @@ def calculate_attribs(): add_qemu_macro_attrib("fTRAP", "A_IMPLICIT_READS_PC") add_qemu_macro_attrib("fSET_OVERFLOW", "A_IMPLICIT_WRITES_USR") add_qemu_macro_attrib("fSET_LPCFG", "A_IMPLICIT_WRITES_USR") + add_qemu_macro_attrib("fCLEAR_RTE_EX", "A_IMPLICIT_WRITES_SSR") add_qemu_macro_attrib("fLOAD", "A_SCALAR_LOAD") add_qemu_macro_attrib("fSTORE", "A_SCALAR_STORE") + add_qemu_macro_attrib("fSET_K0_LOCK", "A_IMPLICIT_READS_PC") + add_qemu_macro_attrib("fSET_TLB_LOCK", "A_IMPLICIT_READS_PC") add_qemu_macro_attrib('fLSBNEW0', 'A_IMPLICIT_READS_P0') add_qemu_macro_attrib('fLSBNEW0NOT', 'A_IMPLICIT_READS_P0') add_qemu_macro_attrib('fREAD_P0', 'A_IMPLICIT_READS_P0') @@ -215,7 +253,9 @@ def need_env(tag): "A_LOAD" in attribdict[tag] or "A_CVI_GATHER" in attribdict[tag] or "A_CVI_SCATTER" in attribdict[tag] or - "A_IMPLICIT_WRITES_USR" in attribdict[tag]) + "A_IMPLICIT_WRITES_USR" in attribdict[tag] or + "A_PRIV" in attribdict[tag] or + "J2_trap" in tag) def need_slot(tag): @@ -224,6 +264,9 @@ def need_slot(tag): and "A_CVI_GATHER" not in attribdict[tag] and ("A_STORE" in attribdict[tag] or "A_LOAD" in attribdict[tag]) + and tag != "L4_loadw_phys" + and tag != "L6_memcpy" + and tag != "Y6_dmlink" ): return 1 else: @@ -247,7 +290,9 @@ def need_next_PC(tag): def need_pkt_has_multi_cof(tag): - return "A_COF" in attribdict[tag] + if attribdict[tag] & {"A_JUMP", "A_CALL"}: + return tag != "J4_hintjumpr" + return False def need_pkt_need_commit(tag): @@ -367,12 +412,16 @@ def helper_proto_type(self): return "s32" def helper_arg_type(self): return "int32_t" + def is_pair(self): + return False class Pair(Scalar): def helper_proto_type(self): return "s64" def helper_arg_type(self): return "int64_t" + def is_pair(self): + return True class Hvx: def is_scalar_reg(self): @@ -1008,6 +1057,125 @@ def analyze_write(self, f, tag, regno): ctx_log_qreg_write(ctx, {self.reg_num}, insn_has_hvx_helper); """)) +class GuestRegister(Register): + pass + +class GuestDest(GuestRegister, Single, Dest): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv_i32 {self.reg_tcg()} = tcg_temp_new_i32(); + """)) + def gen_write(self, f, tag): + f.write(code_fmt(f"""\ + gen_log_greg_write(ctx, {self.reg_num}, {self.reg_tcg()}); + """)) + def analyze_write(self, f, tag, regno): + f.write(code_fmt(f"""\ + ctx_log_greg_write(ctx, {self.reg_num}); + """)) + +class GuestSource(GuestRegister, Single, OldSource): + def decl_reg_num(self, f, regno): + f.write(code_fmt(f"""\ + const int {self.reg_num} G_GNUC_UNUSED = insn->regno[{regno}]; + """)) + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv_i32 {self.reg_tcg()} = tcg_temp_new_i32(); + gen_read_greg({self.reg_tcg()}, {self.reg_num}); + """)) + def analyze_read(self, f, regno): + pass + +class GuestPairDest(GuestRegister, Pair, Dest): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv_i64 {self.reg_tcg()} = tcg_temp_new_i64(); + """)) + def gen_write(self, f, tag): + f.write(code_fmt(f"""\ + gen_log_greg_write_pair(ctx, {self.reg_num}, {self.reg_tcg()}); + """)) + def analyze_write(self, f, tag, regno): + f.write(code_fmt(f"""\ + ctx_log_greg_write_pair(ctx, {self.reg_num}); + """)) + +class GuestPairSource(GuestRegister, Pair, OldSource): + def decl_reg_num(self, f, regno): + f.write(code_fmt(f"""\ + const int {self.reg_num} G_GNUC_UNUSED = insn->regno[{regno}]; + """)) + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv_i64 {self.reg_tcg()} = tcg_temp_new_i64(); + gen_read_greg_pair({self.reg_tcg()}, {self.reg_num}); + """)) + def analyze_read(self, f, regno): + pass + +class SystemDest(Register, Single, Dest): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv_i32 {self.reg_tcg()} = tcg_temp_new_i32(); + """)) + def gen_write(self, f, tag): + f.write(code_fmt(f"""\ + gen_log_sreg_write(ctx, {self.reg_num}, {self.reg_tcg()}); + """)) + def analyze_write(self, f, tag, regno): + f.write(code_fmt(f"""\ + ctx_log_sreg_write(ctx, {self.reg_num}); + """)) + +class SystemSource(Register, Single, OldSource): + def decl_reg_num(self, f, regno): + f.write(code_fmt(f"""\ + const int {self.reg_num} G_GNUC_UNUSED = insn->regno[{regno}]; + """)) + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv_i32 {self.reg_tcg()} = tcg_temp_new_i32(); + gen_read_sreg({self.reg_tcg()}, {self.reg_num}); + """)) + def analyze_read(self, f, regno): + pass + +class SystemPairDest(Register, Pair, Dest): + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv_i64 {self.reg_tcg()} = tcg_temp_new_i64(); + """)) + def gen_write(self, f, tag): + f.write(code_fmt(f"""\ + gen_log_sreg_write_pair(ctx, {self.reg_num}, {self.reg_tcg()}); + """)) + def analyze_write(self, f, tag, regno): + f.write(code_fmt(f"""\ + ctx_log_sreg_write_pair(ctx, {self.reg_num}); + """)) + +class SystemPairSource(Register, Pair, OldSource): + def decl_reg_num(self, f, regno): + f.write(code_fmt(f"""\ + const int {self.reg_num} G_GNUC_UNUSED = insn->regno[{regno}]; + """)) + def decl_tcg(self, f, tag, regno): + self.decl_reg_num(f, regno) + f.write(code_fmt(f"""\ + TCGv_i64 {self.reg_tcg()} = tcg_temp_new_i64(); + gen_read_sreg_pair({self.reg_tcg()}, {self.reg_num}); + """)) + def analyze_read(self, f, regno): + pass + def init_registers(): regs = { GprDest("R", "d"), @@ -1054,6 +1222,16 @@ def init_registers(): QRegSource("Q", "u"), QRegSource("Q", "v"), QRegReadWrite("Q", "x"), + + # system regs + GuestDest("G", "d"), + GuestSource("G", "s"), + GuestPairDest("G", "dd"), + GuestPairSource("G", "ss"), + SystemDest("S", "d"), + SystemSource("S", "s"), + SystemPairDest("S", "dd"), + SystemPairSource("S", "ss"), } for reg in regs: registers[f"{reg.regtype}{reg.regid}"] = reg @@ -1197,6 +1375,7 @@ def parse_common_args(desc): parser.add_argument("semantics", help="semantics file") parser.add_argument("overrides", help="overrides file") parser.add_argument("overrides_vec", help="vector overrides file") + parser.add_argument("overrides_sys", help="system overrides file") parser.add_argument("out", help="output file") parser.add_argument("--idef-parser", help="file of instructions translated by idef-parser") @@ -1204,6 +1383,7 @@ def parse_common_args(desc): read_semantics_file(args.semantics) read_overrides_file(args.overrides) read_overrides_file(args.overrides_vec) + read_overrides_file(args.overrides_sys) if args.idef_parser: read_idef_parser_enabled_file(args.idef_parser) calculate_attribs()
diff --git a/target/hexagon/hex_interrupts.c b/target/hexagon/hex_interrupts.c new file mode 100644 index 0000000..3534481 --- /dev/null +++ b/target/hexagon/hex_interrupts.c
@@ -0,0 +1,371 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/main-loop.h" +#include "cpu.h" +#include "cpu_helper.h" +#include "exec/cpu-interrupt.h" +#include "hex_interrupts.h" +#include "macros.h" +#include "sys_macros.h" +#include "system/cpus.h" +#include "hw/hexagon/hexagon_globalreg.h" + +static bool hex_is_qualified_for_int(CPUHexagonState *env, int int_num); + +static bool get_syscfg_gie(CPUHexagonState *env) +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t syscfg = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SYSCFG, + env->threadId); + return GET_SYSCFG_FIELD(SYSCFG_GIE, syscfg); +} + +static bool get_ssr_ex(CPUHexagonState *env) +{ + uint32_t ssr = env->t_sreg[HEX_SREG_SSR]; + return GET_SSR_FIELD(SSR_EX, ssr); +} + +static bool get_ssr_ie(CPUHexagonState *env) +{ + uint32_t ssr = env->t_sreg[HEX_SREG_SSR]; + return GET_SSR_FIELD(SSR_IE, ssr); +} + +/* Do these together so we only have to call hexagon_modify_ssr once */ +static void set_ssr_ex_cause(CPUHexagonState *env, int ex, uint32_t cause) +{ + uint32_t old, new; + + old = env->t_sreg[HEX_SREG_SSR]; + SET_SYSTEM_FIELD(env, HEX_SREG_SSR, SSR_EX, ex); + SET_SYSTEM_FIELD(env, HEX_SREG_SSR, SSR_CAUSE, cause); + new = env->t_sreg[HEX_SREG_SSR]; + hexagon_modify_ssr(env, new, old); +} + +static bool get_iad_bit(CPUHexagonState *env, int int_num) +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t ipendad = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_IPENDAD, + env->threadId); + uint32_t iad = GET_FIELD(IPENDAD_IAD, ipendad); + return extract32(iad, int_num, 1); +} + +static void set_iad_bit(CPUHexagonState *env, int int_num, int val) +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t ipendad = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_IPENDAD, + env->threadId); + uint32_t iad = GET_FIELD(IPENDAD_IAD, ipendad); + iad = deposit32(iad, int_num, 1, val); + fSET_FIELD(ipendad, IPENDAD_IAD, iad); + hexagon_globalreg_write(cpu->globalregs, HEX_SREG_IPENDAD, + ipendad, env->threadId); +} + +static uint32_t get_ipend(CPUHexagonState *env) +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t ipendad = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_IPENDAD, + env->threadId); + return GET_FIELD(IPENDAD_IPEND, ipendad); +} + +static inline bool get_ipend_bit(CPUHexagonState *env, int int_num) +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t ipendad = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_IPENDAD, + env->threadId); + uint32_t ipend = GET_FIELD(IPENDAD_IPEND, ipendad); + return extract32(ipend, int_num, 1); +} + +static void clear_ipend(CPUHexagonState *env, uint32_t mask) +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t ipendad = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_IPENDAD, + env->threadId); + uint32_t ipend = GET_FIELD(IPENDAD_IPEND, ipendad); + ipend &= ~mask; + fSET_FIELD(ipendad, IPENDAD_IPEND, ipend); + hexagon_globalreg_write(cpu->globalregs, HEX_SREG_IPENDAD, + ipendad, env->threadId); +} + +static void set_ipend(CPUHexagonState *env, uint32_t mask) +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t ipendad = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_IPENDAD, + env->threadId); + uint32_t ipend = GET_FIELD(IPENDAD_IPEND, ipendad); + ipend |= mask; + fSET_FIELD(ipendad, IPENDAD_IPEND, ipend); + hexagon_globalreg_write(cpu->globalregs, HEX_SREG_IPENDAD, + ipendad, env->threadId); +} + +static void set_ipend_bit(CPUHexagonState *env, int int_num, int val) +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t ipendad = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_IPENDAD, + env->threadId); + uint32_t ipend = GET_FIELD(IPENDAD_IPEND, ipendad); + ipend = deposit32(ipend, int_num, 1, val); + fSET_FIELD(ipendad, IPENDAD_IPEND, ipend); + hexagon_globalreg_write(cpu->globalregs, HEX_SREG_IPENDAD, + ipendad, env->threadId); +} + +static bool get_imask_bit(CPUHexagonState *env, int int_num) +{ + uint32_t imask = env->t_sreg[HEX_SREG_IMASK]; + return extract32(imask, int_num, 1); +} + +static uint32_t get_prio(CPUHexagonState *env) +{ + uint32_t stid = env->t_sreg[HEX_SREG_STID]; + return extract32(stid, reg_field_info[STID_PRIO].offset, + reg_field_info[STID_PRIO].width); +} + +static void set_elr(CPUHexagonState *env, uint32_t val) +{ + env->t_sreg[HEX_SREG_ELR] = val; +} + +static bool get_schedcfgen(CPUHexagonState *env) +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t schedcfg = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SCHEDCFG, + env->threadId); + return extract32(schedcfg, reg_field_info[SCHEDCFG_EN].offset, + reg_field_info[SCHEDCFG_EN].width); +} + +static bool is_lowest_prio(CPUHexagonState *env, int int_num) +{ + uint32_t my_prio = get_prio(env); + CPUState *cs; + + CPU_FOREACH(cs) { + CPUHexagonState *hex_env = cpu_env(cs); + if (!hex_is_qualified_for_int(hex_env, int_num)) { + continue; + } + + /* Note that lower values indicate *higher* priority */ + if (my_prio < get_prio(hex_env)) { + return false; + } + } + return true; +} + +static bool hex_is_qualified_for_int(CPUHexagonState *env, int int_num) +{ + bool syscfg_gie = get_syscfg_gie(env); + bool iad = get_iad_bit(env, int_num); + bool ssr_ie = get_ssr_ie(env); + bool ssr_ex = get_ssr_ex(env); + bool imask = get_imask_bit(env, int_num); + + return syscfg_gie && !iad && ssr_ie && !ssr_ex && !imask; +} + +static void clear_pending_locks(CPUHexagonState *env) +{ + g_assert(bql_locked()); + if (env->k0_lock_state == HEX_LOCK_WAITING) { + env->k0_lock_state = HEX_LOCK_UNLOCKED; + } + if (env->tlb_lock_state == HEX_LOCK_WAITING) { + env->tlb_lock_state = HEX_LOCK_UNLOCKED; + } +} + +static bool should_not_exec(CPUHexagonState *env) +{ + return (get_exe_mode(env) == HEX_EXE_MODE_WAIT); +} + +static void restore_state(CPUHexagonState *env, bool int_accepted) +{ + CPUState *cs = env_cpu(env); + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD | CPU_INTERRUPT_SWI); + if (!int_accepted && should_not_exec(env)) { + cpu_interrupt(cs, CPU_INTERRUPT_HALT); + } +} + +static void hex_accept_int(CPUHexagonState *env, int int_num) +{ + CPUState *cs = env_cpu(env); + HexagonCPU *cpu = env_archcpu(env); + uint32_t evb = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_EVB, + env->threadId); + const int exe_mode = get_exe_mode(env); + const bool in_wait_mode = exe_mode == HEX_EXE_MODE_WAIT; + + set_ipend_bit(env, int_num, 0); + set_iad_bit(env, int_num, 1); + set_ssr_ex_cause(env, 1, HEX_CAUSE_INT0 | int_num); + cs->exception_index = HEX_EVENT_INT0 + int_num; + env->cause_code = HEX_EVENT_INT0 + int_num; + clear_pending_locks(env); + if (in_wait_mode) { + qemu_log_mask(CPU_LOG_INT, + "%s: thread " TARGET_FMT_ld " resuming, exiting WAIT mode\n", + __func__, env->threadId); + set_elr(env, env->wait_next_pc); + clear_wait_mode(env); + cs->halted = false; + } else if (env->k0_lock_state == HEX_LOCK_WAITING) { + g_assert_not_reached(); + } else { + set_elr(env, env->gpr[HEX_REG_PC]); + } + env->gpr[HEX_REG_PC] = evb | (cs->exception_index << 2); + if (get_ipend(env) == 0) { + restore_state(env, true); + } +} + + +bool hex_check_interrupts(CPUHexagonState *env) +{ + CPUState *cs = env_cpu(env); + bool int_handled = false; + bool ssr_ex = get_ssr_ex(env); + int max_ints = 32; + bool schedcfgen; + + /* Early exit if nothing pending */ + if (get_ipend(env) == 0) { + restore_state(env, false); + return false; + } + + BQL_LOCK_GUARD(); + /* Only check priorities when schedcfgen is set */ + schedcfgen = get_schedcfgen(env); + for (int i = 0; i < max_ints; i++) { + if (!get_iad_bit(env, i) && get_ipend_bit(env, i)) { + bool syscfg_gie, iad, ssr_ie, imask; + + qemu_log_mask(CPU_LOG_INT, + "%s: thread[" TARGET_FMT_ld "] " + "pc = 0x" TARGET_FMT_lx + " found int %d\n", + __func__, env->threadId, + env->gpr[HEX_REG_PC], i); + if (hex_is_qualified_for_int(env, i) && + (!schedcfgen || is_lowest_prio(env, i))) { + qemu_log_mask(CPU_LOG_INT, + "%s: thread[" TARGET_FMT_ld "] int %d handled_\n", + __func__, env->threadId, i); + hex_accept_int(env, i); + int_handled = true; + break; + } + syscfg_gie = get_syscfg_gie(env); + iad = get_iad_bit(env, i); + ssr_ie = get_ssr_ie(env); + imask = get_imask_bit(env, i); + + qemu_log_mask(CPU_LOG_INT, + "%s: thread[" TARGET_FMT_ld "] " + "int %d not handled, qualified: %d, " + "schedcfg_en: %d, low prio %d\n", + __func__, env->threadId, i, + hex_is_qualified_for_int(env, i), schedcfgen, + is_lowest_prio(env, i)); + + qemu_log_mask(CPU_LOG_INT, + "%s: thread[" TARGET_FMT_ld "] " + "int %d not handled, GIE %d, iad %d, " + "SSR:IE %d, SSR:EX: %d, imask bit %d\n", + __func__, env->threadId, i, syscfg_gie, iad, ssr_ie, + ssr_ex, imask); + } + } + + /* + * If we didn't handle the interrupt and it wasn't + * because we were in EX state, then we won't be able + * to execute the interrupt on this CPU unless something + * changes in the CPU state. Clear the interrupt_request bits + * while preserving the IPEND bits, and we can re-assert the + * interrupt_request bit(s) when we execute one of those instructions. + */ + if (!int_handled && !ssr_ex) { + restore_state(env, int_handled); + } else if (int_handled) { + assert(!cs->halted); + } + + return int_handled; +} + +void hex_clear_interrupts(CPUHexagonState *env, uint32_t mask, uint32_t type) +{ + if (mask == 0) { + return; + } + + /* + * Notify all CPUs that the interrupt has happened + */ + BQL_LOCK_GUARD(); + clear_ipend(env, mask); + hex_interrupt_update(env); +} + +void hex_raise_interrupts(CPUHexagonState *env, uint32_t mask, uint32_t type) +{ + g_assert(bql_locked()); + if (mask == 0) { + return; + } + + /* + * Notify all CPUs that the interrupt has happened + */ + set_ipend(env, mask); + hex_interrupt_update(env); +} + +void hex_interrupt_update(CPUHexagonState *env) +{ + CPUState *cs; + + g_assert(bql_locked()); + if (get_ipend(env) != 0) { + CPU_FOREACH(cs) { + CPUHexagonState *hex_env = cpu_env(cs); + const int exe_mode = get_exe_mode(hex_env); + if (exe_mode != HEX_EXE_MODE_OFF) { + cpu_interrupt(cs, CPU_INTERRUPT_SWI); + cpu_resume(cs); + } + } + } +}
diff --git a/target/hexagon/hex_interrupts.h b/target/hexagon/hex_interrupts.h new file mode 100644 index 0000000..6b6f540 --- /dev/null +++ b/target/hexagon/hex_interrupts.h
@@ -0,0 +1,15 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HEX_INTERRUPTS_H +#define HEX_INTERRUPTS_H + +bool hex_check_interrupts(CPUHexagonState *env); +void hex_clear_interrupts(CPUHexagonState *env, uint32_t mask, uint32_t type); +void hex_raise_interrupts(CPUHexagonState *env, uint32_t mask, uint32_t type); +void hex_interrupt_update(CPUHexagonState *env); + +#endif
diff --git a/target/hexagon/hex_mmu.c b/target/hexagon/hex_mmu.c new file mode 100644 index 0000000..81d64a6 --- /dev/null +++ b/target/hexagon/hex_mmu.c
@@ -0,0 +1,270 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/main-loop.h" +#include "qemu/qemu-print.h" +#include "cpu.h" +#include "system/cpus.h" +#include "internal.h" +#include "exec/cpu-interrupt.h" +#include "cpu_helper.h" +#include "exec/cputlb.h" +#include "hex_mmu.h" +#include "macros.h" +#include "sys_macros.h" +#include "hw/hexagon/hexagon_tlb.h" +#include "hw/hexagon/hexagon_globalreg.h" + +static inline void hex_log_tlbw(uint32_t index, uint64_t entry) +{ + qemu_log_mask(CPU_LOG_MMU, + "tlbw[%03" PRIu32 "]: 0x%016" PRIx64 "\n", + index, entry); +} + +void hex_tlbw(CPUHexagonState *env, uint32_t index, uint64_t value) +{ + uint32_t myidx = fTLB_NONPOW2WRAP(fTLB_IDXMASK(index)); + HexagonTLBState *tlb = env_archcpu(env)->tlb; + uint64_t old_entry = hexagon_tlb_read(tlb, myidx); + + bool old_entry_valid = extract64(old_entry, 63, 1); + if (old_entry_valid && hexagon_cpu_mmu_enabled(env)) { + CPUState *cs = env_cpu(env); + tlb_flush(cs); + } + hexagon_tlb_write(tlb, myidx, value); + hex_log_tlbw(myidx, value); +} + +void hex_mmu_on(CPUHexagonState *env) +{ + CPUState *cs = env_cpu(env); + qemu_log_mask(CPU_LOG_MMU, "Hexagon MMU turned on!\n"); + tlb_flush(cs); +} + +void hex_mmu_off(CPUHexagonState *env) +{ + CPUState *cs = env_cpu(env); + qemu_log_mask(CPU_LOG_MMU, "Hexagon MMU turned off!\n"); + tlb_flush(cs); +} + +void hex_mmu_mode_change(CPUHexagonState *env) +{ + qemu_log_mask(CPU_LOG_MMU, "Hexagon mode change!\n"); + CPUState *cs = env_cpu(env); + tlb_flush(cs); +} + +bool hex_tlb_find_match(CPUHexagonState *env, uint32_t VA, + MMUAccessType access_type, hwaddr *PA, int *prot, + uint64_t *size, int32_t *excp, int mmu_idx) +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t ssr = env->t_sreg[HEX_SREG_SSR]; + uint8_t asid = GET_SSR_FIELD(SSR_ASID, ssr); + int cause_code = 0; + + bool found = hexagon_tlb_find_match(cpu->tlb, asid, VA, access_type, + PA, prot, size, excp, &cause_code, + mmu_idx); + if (cause_code) { + env->cause_code = cause_code; + } + return found; +} + +/* Called from tlbp instruction */ +uint32_t hex_tlb_lookup(CPUHexagonState *env, uint32_t ssr, uint32_t VA) +{ + HexagonCPU *cpu = env_archcpu(env); + uint8_t asid = GET_SSR_FIELD(SSR_ASID, ssr); + int cause_code = 0; + + uint32_t result = hexagon_tlb_lookup(cpu->tlb, asid, VA, &cause_code); + if (cause_code) { + env->cause_code = cause_code; + } + return result; +} + +/* + * Return codes: + * 0 or positive index of match + * -1 multiple matches + * -2 no match + */ +int hex_tlb_check_overlap(CPUHexagonState *env, uint64_t entry, uint64_t index) +{ + HexagonCPU *cpu = env_archcpu(env); + return hexagon_tlb_check_overlap(cpu->tlb, entry, index); +} + +void dump_mmu(Monitor *mon, CPUHexagonState *env) +{ + HexagonCPU *cpu = env_archcpu(env); + hexagon_tlb_dump(mon, cpu->tlb); +} + +static inline void print_thread(const char *str, CPUState *cs) +{ + g_assert(bql_locked()); + CPUHexagonState *thread = cpu_env(cs); + bool is_stopped = cpu_is_stopped(cs); + int exe_mode = get_exe_mode(thread); + hex_lock_state_t lock_state = thread->tlb_lock_state; + qemu_log_mask(CPU_LOG_MMU, + "%s: threadId = %" PRIu32 ": %s," + " exe_mode = %s, tlb_lock_state = %s\n", + str, + thread->threadId, + is_stopped ? "stopped" : "running", + exe_mode == HEX_EXE_MODE_OFF ? "off" : + exe_mode == HEX_EXE_MODE_RUN ? "run" : + exe_mode == HEX_EXE_MODE_WAIT ? "wait" : + exe_mode == HEX_EXE_MODE_DEBUG ? "debug" : + "unknown", + lock_state == HEX_LOCK_UNLOCKED ? "unlocked" : + lock_state == HEX_LOCK_WAITING ? "waiting" : + lock_state == HEX_LOCK_OWNER ? "owner" : + "unknown"); +} + +static inline void print_thread_states(const char *str) +{ + CPUState *cs; + CPU_FOREACH(cs) { + print_thread(str, cs); + } +} + +void hex_tlb_lock(CPUHexagonState *env) +{ + qemu_log_mask(CPU_LOG_MMU, "hex_tlb_lock: " TARGET_FMT_ld "\n", + env->threadId); + BQL_LOCK_GUARD(); + g_assert((env->tlb_lock_count == 0) || (env->tlb_lock_count == 1)); + + HexagonCPU *cpu = env_archcpu(env); + uint32_t syscfg = cpu->globalregs ? + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SYSCFG, + env->threadId) : 0; + uint8_t tlb_lock = GET_SYSCFG_FIELD(SYSCFG_TLBLOCK, syscfg); + if (tlb_lock) { + if (env->tlb_lock_state == HEX_LOCK_QUEUED) { + env->next_PC += 4; + env->tlb_lock_count++; + env->tlb_lock_state = HEX_LOCK_OWNER; + SET_SYSCFG_FIELD(env, SYSCFG_TLBLOCK, 1); + return; + } + if (env->tlb_lock_state == HEX_LOCK_OWNER) { + qemu_log_mask(CPU_LOG_MMU | LOG_GUEST_ERROR, + "Double tlblock at PC: 0x%" PRIx32 + ", thread may hang\n", + env->next_PC); + env->next_PC += 4; + CPUState *cs = env_cpu(env); + cpu_interrupt(cs, CPU_INTERRUPT_HALT); + return; + } + env->tlb_lock_state = HEX_LOCK_WAITING; + CPUState *cs = env_cpu(env); + cpu_interrupt(cs, CPU_INTERRUPT_HALT); + } else { + env->next_PC += 4; + env->tlb_lock_count++; + env->tlb_lock_state = HEX_LOCK_OWNER; + SET_SYSCFG_FIELD(env, SYSCFG_TLBLOCK, 1); + } + + if (qemu_loglevel_mask(CPU_LOG_MMU)) { + qemu_log_mask(CPU_LOG_MMU, "Threads after hex_tlb_lock:\n"); + print_thread_states("\tThread"); + } +} + +void hex_tlb_unlock(CPUHexagonState *env) +{ + BQL_LOCK_GUARD(); + g_assert((env->tlb_lock_count == 0) || (env->tlb_lock_count == 1)); + + /* Nothing to do if the TLB isn't locked by this thread */ + HexagonCPU *cpu = env_archcpu(env); + uint32_t syscfg = cpu->globalregs ? + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SYSCFG, + env->threadId) : 0; + uint8_t tlb_lock = GET_SYSCFG_FIELD(SYSCFG_TLBLOCK, syscfg); + if ((tlb_lock == 0) || + (env->tlb_lock_state != HEX_LOCK_OWNER)) { + qemu_log_mask(LOG_GUEST_ERROR, + "thread %" PRIu32 " attempted to tlbunlock" + " without having the lock, tlb_lock state = %u\n", + env->threadId, (unsigned)env->tlb_lock_state); + g_assert(env->tlb_lock_state != HEX_LOCK_WAITING); + return; + } + + env->tlb_lock_count--; + env->tlb_lock_state = HEX_LOCK_UNLOCKED; + SET_SYSCFG_FIELD(env, SYSCFG_TLBLOCK, 0); + + /* Look for a thread to unlock */ + unsigned int this_threadId = env->threadId; + CPUHexagonState *unlock_thread = NULL; + CPUState *cs; + CPU_FOREACH(cs) { + CPUHexagonState *thread = cpu_env(cs); + + /* + * The hardware implements round-robin fairness, so we look for threads + * starting at env->threadId + 1 and incrementing modulo the number of + * threads. + * + * To implement this, we check if thread is a earlier in the modulo + * sequence than unlock_thread. + * if unlock thread is higher than this thread + * thread must be between this thread and unlock_thread + * else + * thread higher than this thread is ahead of unlock_thread + * thread must be lower then unlock thread + */ + if (thread->tlb_lock_state == HEX_LOCK_WAITING) { + if (!unlock_thread) { + unlock_thread = thread; + } else if (unlock_thread->threadId > this_threadId) { + if (this_threadId < thread->threadId && + thread->threadId < unlock_thread->threadId) { + unlock_thread = thread; + } + } else { + if (thread->threadId > this_threadId) { + unlock_thread = thread; + } + if (thread->threadId < unlock_thread->threadId) { + unlock_thread = thread; + } + } + } + } + if (unlock_thread) { + cs = env_cpu(unlock_thread); + print_thread("\tWaiting thread found", cs); + unlock_thread->tlb_lock_state = HEX_LOCK_QUEUED; + SET_SYSCFG_FIELD(unlock_thread, SYSCFG_TLBLOCK, 1); + cpu_interrupt(cs, CPU_INTERRUPT_TLB_UNLOCK); + } + + if (qemu_loglevel_mask(CPU_LOG_MMU)) { + qemu_log_mask(CPU_LOG_MMU, "Threads after hex_tlb_unlock:\n"); + print_thread_states("\tThread"); + } + +}
diff --git a/target/hexagon/hex_mmu.h b/target/hexagon/hex_mmu.h new file mode 100644 index 0000000..4f556c7 --- /dev/null +++ b/target/hexagon/hex_mmu.h
@@ -0,0 +1,26 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HEXAGON_MMU_H +#define HEXAGON_MMU_H + +#include "cpu.h" +#include "monitor/monitor.h" + +extern void hex_tlbw(CPUHexagonState *env, uint32_t index, uint64_t value); +extern uint32_t hex_tlb_lookup(CPUHexagonState *env, uint32_t ssr, uint32_t VA); +extern void hex_mmu_on(CPUHexagonState *env); +extern void hex_mmu_off(CPUHexagonState *env); +extern void hex_mmu_mode_change(CPUHexagonState *env); +extern bool hex_tlb_find_match(CPUHexagonState *env, uint32_t VA, + MMUAccessType access_type, hwaddr *PA, int *prot, + uint64_t *size, int32_t *excp, int mmu_idx); +extern int hex_tlb_check_overlap(CPUHexagonState *env, uint64_t entry, + uint64_t index); +extern void hex_tlb_lock(CPUHexagonState *env); +extern void hex_tlb_unlock(CPUHexagonState *env); +void dump_mmu(Monitor *mon, CPUHexagonState *env); +#endif
diff --git a/target/hexagon/hex_regs.h b/target/hexagon/hex_regs.h index bddfc28..214b7b8 100644 --- a/target/hexagon/hex_regs.h +++ b/target/hexagon/hex_regs.h
@@ -81,4 +81,121 @@ HEX_REG_UTIMERHI = 63, }; +#ifndef CONFIG_USER_ONLY + +#define HEX_GREG_VALUES \ + DECL_HEX_GREG(G0, 0) \ + DECL_HEX_GREG(GELR, 0) \ + DECL_HEX_GREG(G1, 1) \ + DECL_HEX_GREG(GSR, 1) \ + DECL_HEX_GREG(G2, 2) \ + DECL_HEX_GREG(GOSP, 2) \ + DECL_HEX_GREG(G3, 3) \ + DECL_HEX_GREG(GBADVA, 3) \ + DECL_HEX_GREG(GCYCLE_1T, 10) \ + DECL_HEX_GREG(GCYCLE_2T, 11) \ + DECL_HEX_GREG(GCYCLE_3T, 12) \ + DECL_HEX_GREG(GCYCLE_4T, 13) \ + DECL_HEX_GREG(GCYCLE_5T, 14) \ + DECL_HEX_GREG(GCYCLE_6T, 15) \ + DECL_HEX_GREG(GPMUCNT4, 16) \ + DECL_HEX_GREG(GPMUCNT5, 17) \ + DECL_HEX_GREG(GPMUCNT6, 18) \ + DECL_HEX_GREG(GPMUCNT7, 19) \ + DECL_HEX_GREG(GPCYCLELO, 24) \ + DECL_HEX_GREG(GPCYCLEHI, 25) \ + DECL_HEX_GREG(GPMUCNT0, 26) \ + DECL_HEX_GREG(GPMUCNT1, 27) \ + DECL_HEX_GREG(GPMUCNT2, 28) \ + DECL_HEX_GREG(GPMUCNT3, 29) \ + DECL_HEX_GREG_DONE + +#define DECL_HEX_GREG_DONE +#define DECL_HEX_GREG(name, val) HEX_GREG_ ##name = val, +enum hex_greg { + HEX_GREG_VALUES +}; +#undef DECL_HEX_GREG +#undef DECL_HEX_GREG_DONE + +#define DECL_HEX_GREG_DONE 0 +#define DECL_HEX_GREG(_, val) (1 << val) | +static inline bool greg_implemented(enum hex_greg greg) +{ +#if NUM_GREGS > 32 +#error "NUM_GREGS too large for greg_implemented(): update `impl_bitmap`" +#endif + static int32_t impl_bitmap = HEX_GREG_VALUES; + return impl_bitmap & (1 << greg); +} +#undef DECL_HEX_GREG +#undef DECL_HEX_GREG_DONE + +#endif /* CONFIG_USER_ONLY */ + +#ifndef CONFIG_USER_ONLY +enum { + HEX_SREG_SGP0 = 0, + HEX_SREG_SGP1 = 1, + HEX_SREG_STID = 2, + HEX_SREG_ELR = 3, + HEX_SREG_BADVA0 = 4, + HEX_SREG_BADVA1 = 5, + HEX_SREG_SSR = 6, + HEX_SREG_CCR = 7, + HEX_SREG_HTID = 8, + HEX_SREG_BADVA = 9, + HEX_SREG_IMASK = 10, + HEX_SREG_GEVB = 11, + HEX_SREG_GLB_START = 16, + HEX_SREG_EVB = 16, + HEX_SREG_MODECTL = 17, + HEX_SREG_SYSCFG = 18, + HEX_SREG_IPENDAD = 20, + HEX_SREG_VID = 21, + HEX_SREG_VID1 = 22, + HEX_SREG_BESTWAIT = 23, + HEX_SREG_IEL = 24, + HEX_SREG_SCHEDCFG = 25, + HEX_SREG_IAHL = 26, + HEX_SREG_CFGBASE = 27, + HEX_SREG_DIAG = 28, + HEX_SREG_REV = 29, + HEX_SREG_PCYCLELO = 30, + HEX_SREG_PCYCLEHI = 31, + HEX_SREG_ISDBST = 32, + HEX_SREG_ISDBCFG0 = 33, + HEX_SREG_ISDBCFG1 = 34, + HEX_SREG_LIVELOCK = 35, + HEX_SREG_BRKPTPC0 = 36, + HEX_SREG_BRKPTCFG0 = 37, + HEX_SREG_BRKPTPC1 = 38, + HEX_SREG_BRKPTCFG1 = 39, + HEX_SREG_ISDBMBXIN = 40, + HEX_SREG_ISDBMBXOUT = 41, + HEX_SREG_ISDBEN = 42, + HEX_SREG_ISDBGPR = 43, + HEX_SREG_PMUCNT4 = 44, + HEX_SREG_PMUCNT5 = 45, + HEX_SREG_PMUCNT6 = 46, + HEX_SREG_PMUCNT7 = 47, + HEX_SREG_PMUCNT0 = 48, + HEX_SREG_PMUCNT1 = 49, + HEX_SREG_PMUCNT2 = 50, + HEX_SREG_PMUCNT3 = 51, + HEX_SREG_PMUEVTCFG = 52, + HEX_SREG_PMUSTID0 = 53, + HEX_SREG_PMUEVTCFG1 = 54, + HEX_SREG_PMUSTID1 = 55, + HEX_SREG_TIMERLO = 56, + HEX_SREG_TIMERHI = 57, + HEX_SREG_PMUCFG = 58, + HEX_SREG_S59 = 59, + HEX_SREG_S60 = 60, + HEX_SREG_S61 = 61, + HEX_SREG_S62 = 62, + HEX_SREG_S63 = 63, +}; +#endif /* !CONFIG_USER_ONLY */ + #endif
diff --git a/target/hexagon/hexswi.c b/target/hexagon/hexswi.c new file mode 100644 index 0000000..43c373e --- /dev/null +++ b/target/hexagon/hexswi.c
@@ -0,0 +1,271 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "cpu_helper.h" +#include "exec/helper-proto.h" +#include "qemu/log.h" +#include "qemu/main-loop.h" +#include "arch.h" +#include "internal.h" +#include "macros.h" +#include "sys_macros.h" +#include "accel/tcg/cpu-loop.h" +#include "tcg/tcg-op.h" +#include "hex_mmu.h" +#include "hexswi.h" +#include "hw/hexagon/hexagon_globalreg.h" + +#ifdef CONFIG_USER_ONLY +#error "This file is only used in system emulation" +#endif + +static void set_addresses(CPUHexagonState *env, uint32_t pc_offset, + uint32_t exception_index) + +{ + HexagonCPU *cpu = env_archcpu(env); + uint32_t evb = cpu->globalregs ? + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_EVB, + env->threadId) : + cpu->boot_addr; + env->t_sreg[HEX_SREG_ELR] = env->gpr[HEX_REG_PC] + pc_offset; + env->gpr[HEX_REG_PC] = evb | (exception_index << 2); +} + +static const char *event_name[] = { + [HEX_EVENT_RESET] = "HEX_EVENT_RESET", + [HEX_EVENT_IMPRECISE] = "HEX_EVENT_IMPRECISE", + [HEX_EVENT_PRECISE] = "HEX_EVENT_PRECISE", + [HEX_EVENT_TLB_MISS_X] = "HEX_EVENT_TLB_MISS_X", + [HEX_EVENT_TLB_MISS_RW] = "HEX_EVENT_TLB_MISS_RW", + [HEX_EVENT_TRAP0] = "HEX_EVENT_TRAP0", + [HEX_EVENT_TRAP1] = "HEX_EVENT_TRAP1", + [HEX_EVENT_FPTRAP] = "HEX_EVENT_FPTRAP", + [HEX_EVENT_DEBUG] = "HEX_EVENT_DEBUG", + [HEX_EVENT_INT0] = "HEX_EVENT_INT0", + [HEX_EVENT_INT1] = "HEX_EVENT_INT1", + [HEX_EVENT_INT2] = "HEX_EVENT_INT2", + [HEX_EVENT_INT3] = "HEX_EVENT_INT3", + [HEX_EVENT_INT4] = "HEX_EVENT_INT4", + [HEX_EVENT_INT5] = "HEX_EVENT_INT5", + [HEX_EVENT_INT6] = "HEX_EVENT_INT6", + [HEX_EVENT_INT7] = "HEX_EVENT_INT7", + [HEX_EVENT_INT8] = "HEX_EVENT_INT8", + [HEX_EVENT_INT9] = "HEX_EVENT_INT9", + [HEX_EVENT_INTA] = "HEX_EVENT_INTA", + [HEX_EVENT_INTB] = "HEX_EVENT_INTB", + [HEX_EVENT_INTC] = "HEX_EVENT_INTC", + [HEX_EVENT_INTD] = "HEX_EVENT_INTD", + [HEX_EVENT_INTE] = "HEX_EVENT_INTE", + [HEX_EVENT_INTF] = "HEX_EVENT_INTF" +}; + +void hexagon_cpu_do_interrupt(CPUState *cs) + +{ + CPUHexagonState *env = cpu_env(cs); + uint32_t ssr; + + BQL_LOCK_GUARD(); + + qemu_log_mask(CPU_LOG_INT, + "\t%s: event 0x%02x:%s, cause 0x%" PRIx32 "(%" PRIu32 ")\n", + __func__, (unsigned)cs->exception_index, + event_name[cs->exception_index], env->cause_code, + env->cause_code); + + env->llsc_addr = ~0; + + ssr = env->t_sreg[HEX_SREG_SSR]; + if (GET_SSR_FIELD(SSR_EX, ssr) == 1) { + HexagonCPU *cpu = env_archcpu(env); + if (cpu->globalregs) { + hexagon_globalreg_write(cpu->globalregs, HEX_SREG_DIAG, + env->cause_code, env->threadId); + } + env->cause_code = HEX_CAUSE_DOUBLE_EXCEPT; + cs->exception_index = HEX_EVENT_PRECISE; + } + + switch (cs->exception_index) { + case HEX_EVENT_TRAP0: + if (env->cause_code == 0) { + qemu_log_mask(LOG_UNIMP, + "trap0 is unhandled, no semihosting available\n"); + } + + hexagon_ssr_set_cause(env, env->cause_code); + set_addresses(env, 4, cs->exception_index); + break; + + case HEX_EVENT_TRAP1: + hexagon_ssr_set_cause(env, env->cause_code); + set_addresses(env, 4, cs->exception_index); + break; + + case HEX_EVENT_TLB_MISS_X: + switch (env->cause_code) { + case HEX_CAUSE_TLBMISSX_CAUSE_NORMAL: + case HEX_CAUSE_TLBMISSX_CAUSE_NEXTPAGE: + qemu_log_mask(CPU_LOG_MMU, + "TLB miss EX exception (0x%02" PRIx32 ") caught: " + "Cause code (0x%" PRIx32 ") " + "TID = 0x%" PRIx32 ", PC = 0x%" PRIx32 + ", BADVA = 0x%" PRIx32 "\n", + (uint32_t)cs->exception_index, + env->cause_code, env->threadId, + env->gpr[HEX_REG_PC], + env->t_sreg[HEX_SREG_BADVA]); + + hexagon_ssr_set_cause(env, env->cause_code); + set_addresses(env, 0, cs->exception_index); + break; + + default: + cpu_abort(cs, + "1:Hexagon exception %" PRId32 "/0x%02" PRIx32 ": " + "Unknown cause code %" PRIu32 "/0x%" PRIx32 "\n", + (uint32_t)cs->exception_index, + (uint32_t)cs->exception_index, + env->cause_code, + env->cause_code); + break; + } + break; + + case HEX_EVENT_TLB_MISS_RW: + switch (env->cause_code) { + case HEX_CAUSE_TLBMISSRW_CAUSE_READ: + case HEX_CAUSE_TLBMISSRW_CAUSE_WRITE: + qemu_log_mask(CPU_LOG_MMU, + "TLB miss RW exception (0x%02" PRIx32 ") caught: " + "Cause code (0x%" PRIx32 ") " + "TID = 0x%" PRIx32 ", PC = 0x%" PRIx32 + ", BADVA = 0x%" PRIx32 "\n", + (uint32_t)cs->exception_index, + env->cause_code, env->threadId, + env->gpr[HEX_REG_PC], + env->t_sreg[HEX_SREG_BADVA]); + + hexagon_ssr_set_cause(env, env->cause_code); + set_addresses(env, 0, cs->exception_index); + /* env->sreg[HEX_SREG_BADVA] is set when the exception is raised */ + break; + + default: + cpu_abort(cs, + "2:Hexagon exception %" PRId32 "/0x%02" PRIx32 ": " + "Unknown cause code %" PRIu32 "/0x%" PRIx32 "\n", + (uint32_t)cs->exception_index, + (uint32_t)cs->exception_index, + env->cause_code, + env->cause_code); + break; + } + break; + + case HEX_EVENT_FPTRAP: + hexagon_ssr_set_cause(env, env->cause_code); + set_addresses(env, 0, cs->exception_index); + break; + + case HEX_EVENT_DEBUG: + hexagon_ssr_set_cause(env, env->cause_code); + set_addresses(env, 0, cs->exception_index); + qemu_log_mask(LOG_UNIMP, "single-step exception is not handled\n"); + break; + + case HEX_EVENT_PRECISE: + switch (env->cause_code) { + case HEX_CAUSE_FETCH_NO_XPAGE: + case HEX_CAUSE_FETCH_NO_UPAGE: + case HEX_CAUSE_PRIV_NO_READ: + case HEX_CAUSE_PRIV_NO_UREAD: + case HEX_CAUSE_PRIV_NO_WRITE: + case HEX_CAUSE_PRIV_NO_UWRITE: + case HEX_CAUSE_MISALIGNED_LOAD: + case HEX_CAUSE_MISALIGNED_STORE: + case HEX_CAUSE_PC_NOT_ALIGNED: + qemu_log_mask(CPU_LOG_MMU, + "MMU permission exception (0x%02" PRIx32 ") caught: " + "Cause code (0x%" PRIx32 ") " + "TID = 0x%" PRIx32 ", PC = 0x%" PRIx32 + ", BADVA = 0x%" PRIx32 "\n", + (uint32_t)cs->exception_index, + env->cause_code, env->threadId, + env->gpr[HEX_REG_PC], + env->t_sreg[HEX_SREG_BADVA]); + + + hexagon_ssr_set_cause(env, env->cause_code); + set_addresses(env, 0, cs->exception_index); + /* env->sreg[HEX_SREG_BADVA] is set when the exception is raised */ + break; + + case HEX_CAUSE_DOUBLE_EXCEPT: + case HEX_CAUSE_PRIV_USER_NO_SINSN: + case HEX_CAUSE_PRIV_USER_NO_GINSN: + case HEX_CAUSE_INVALID_OPCODE: + case HEX_CAUSE_NO_COPROC_ENABLE: + case HEX_CAUSE_NO_COPROC2_ENABLE: + case HEX_CAUSE_UNSUPPORTED_HVX_64B: + case HEX_CAUSE_REG_WRITE_CONFLICT: + case HEX_CAUSE_VWCTRL_WINDOW_MISS: + hexagon_ssr_set_cause(env, env->cause_code); + set_addresses(env, 0, cs->exception_index); + break; + + case HEX_CAUSE_COPROC_LDST: + hexagon_ssr_set_cause(env, env->cause_code); + set_addresses(env, 0, cs->exception_index); + break; + + case HEX_CAUSE_STACK_LIMIT: + hexagon_ssr_set_cause(env, env->cause_code); + set_addresses(env, 0, cs->exception_index); + break; + + default: + cpu_abort(cs, + "3:Hexagon exception %" PRId32 "/0x%02" PRIx32 ": " + "Unknown cause code %" PRIu32 "/0x%" PRIx32 "\n", + (uint32_t)cs->exception_index, + (uint32_t)cs->exception_index, + env->cause_code, + env->cause_code); + break; + } + break; + + case HEX_EVENT_IMPRECISE: + qemu_log_mask(LOG_UNIMP, + "Imprecise exception: this case is not yet handled"); + break; + + default: + qemu_log_mask(LOG_UNIMP, + "Hexagon Unsupported exception 0x%02x/0x%" PRIx32 "\n", + (unsigned)cs->exception_index, env->cause_code); + break; + } + + cs->exception_index = HEX_EVENT_NONE; +} + +void register_trap_exception(CPUHexagonState *env, int traptype, int imm, + uint32_t PC) +{ + CPUState *cs = env_cpu(env); + + cs->exception_index = (traptype == 0) ? HEX_EVENT_TRAP0 : HEX_EVENT_TRAP1; + ASSERT_DIRECT_TO_GUEST_UNSET(env, cs->exception_index); + + env->cause_code = imm; + env->gpr[HEX_REG_PC] = PC; + cpu_loop_exit(cs); +}
diff --git a/target/hexagon/hexswi.h b/target/hexagon/hexswi.h new file mode 100644 index 0000000..48c1ae6 --- /dev/null +++ b/target/hexagon/hexswi.h
@@ -0,0 +1,17 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HEXSWI_H +#define HEXSWI_H + + +#include "cpu.h" + +void hexagon_cpu_do_interrupt(CPUState *cpu); +void register_trap_exception(CPUHexagonState *env, int type, int imm, + uint32_t PC); + +#endif /* HEXSWI_H */
diff --git a/target/hexagon/idef-parser/idef-parser.y b/target/hexagon/idef-parser/idef-parser.y index c6f17c6..3cffafa 100644 --- a/target/hexagon/idef-parser/idef-parser.y +++ b/target/hexagon/idef-parser/idef-parser.y
@@ -404,6 +404,9 @@ ; frame_check : FCHK '(' rvalue ',' rvalue ')' ';' + { + gen_framecheck(c, &@1, &$3, &$5); + } ; cancel_statement : LOAD_CANCEL @@ -869,7 +872,7 @@ context.header_str = g_string_new(NULL); context.ternary = g_array_new(FALSE, TRUE, sizeof(Ternary)); /* Read input file */ - FILE *input_file = fopen(argv[ARG_INDEX_IDEFS], "r"); + FILE *input_file = fopen(argv[ARG_INDEX_IDEFS], "rb"); fseek(input_file, 0L, SEEK_END); long input_size = ftell(input_file); context.input_buffer = (char *) calloc(input_size + 1, sizeof(char));
diff --git a/target/hexagon/idef-parser/parser-helpers.c b/target/hexagon/idef-parser/parser-helpers.c index 70bfa64..b942d9e 100644 --- a/target/hexagon/idef-parser/parser-helpers.c +++ b/target/hexagon/idef-parser/parser-helpers.c
@@ -1731,6 +1731,15 @@ OUT(c, locp, "}\n"); } +void gen_framecheck(Context *c, YYLTYPE *locp, HexValue *addr, HexValue *ea) +{ + HexValue addr_m = rvalue_materialize(c, locp, addr); + HexValue ea_m = rvalue_materialize(c, locp, ea); + addr_m = gen_rvalue_truncate(c, locp, &addr_m); + ea_m = gen_rvalue_truncate(c, locp, &ea_m); + OUT(c, locp, "gen_framecheck(ctx, ", &addr_m, ", ", &ea_m, ");\n"); +} + void gen_load(Context *c, YYLTYPE *locp, HexValue *width, HexSignedness signedness, HexValue *ea, HexValue *dst) {
diff --git a/target/hexagon/idef-parser/parser-helpers.h b/target/hexagon/idef-parser/parser-helpers.h index 2087d53..d3dfcec 100644 --- a/target/hexagon/idef-parser/parser-helpers.h +++ b/target/hexagon/idef-parser/parser-helpers.h
@@ -295,6 +295,8 @@ void gen_load_cancel(Context *c, YYLTYPE *locp); +void gen_framecheck(Context *c, YYLTYPE *locp, HexValue *addr, HexValue *ea); + void gen_load(Context *c, YYLTYPE *locp, HexValue *size, HexSignedness signedness, HexValue *ea, HexValue *dst);
diff --git a/target/hexagon/imported/encode_pp.def b/target/hexagon/imported/encode_pp.def index 0cd30a5..1c64495 100644 --- a/target/hexagon/imported/encode_pp.def +++ b/target/hexagon/imported/encode_pp.def
@@ -382,15 +382,19 @@ DEF_ENC32(L4_return_tnew_pnt, ICLASS_LD" 011 0 000 sssss PP0010vv ---ddddd") DEF_ENC32(L4_return_fnew_pnt, ICLASS_LD" 011 0 000 sssss PP1010vv ---ddddd") -DEF_ENC32(L2_loadw_locked,ICLASS_LD" 001 0 000 sssss PP000--- 000ddddd") +/** Load Acquire Store Release Encoding **/ - +DEF_ENC32(L4_loadw_phys, ICLASS_LD" 001 0 000 sssss PP1ttttt -00ddddd") +DEF_ENC32(L2_loadw_locked, ICLASS_LD" 001 0 000 sssss PP000--- 000ddddd") +DEF_ENC32(L4_loadd_locked, ICLASS_LD" 001 0 000 sssss PP010--- 000ddddd") DEF_ENC32(L2_loadw_aq, ICLASS_LD" 001 0 000 sssss PP001--- 000ddddd") DEF_ENC32(L4_loadd_aq, ICLASS_LD" 001 0 000 sssss PP011--- 000ddddd") -DEF_ENC32(R6_release_at_vi, ICLASS_ST" 000 01 11sssss PP0ttttt --0011dd") -DEF_ENC32(R6_release_st_vi, ICLASS_ST" 000 01 11sssss PP0ttttt --1011dd") + +DEF_ENC32(S2_storew_locked, ICLASS_ST" 000 01 01sssss PP-ttttt ----00dd") +DEF_ENC32(S4_stored_locked, ICLASS_ST" 000 01 11sssss PP0ttttt ----00dd") + DEF_ENC32(S2_storew_rl_at_vi, ICLASS_ST" 000 01 01sssss PP-ttttt --0010dd") DEF_ENC32(S2_storew_rl_st_vi, ICLASS_ST" 000 01 01sssss PP-ttttt --1010dd") @@ -398,17 +402,15 @@ DEF_ENC32(S4_stored_rl_at_vi, ICLASS_ST" 000 01 11sssss PP0ttttt --0010dd") DEF_ENC32(S4_stored_rl_st_vi, ICLASS_ST" 000 01 11sssss PP0ttttt --1010dd") -DEF_ENC32(L4_loadd_locked,ICLASS_LD" 001 0 000 sssss PP010--- 000ddddd") -DEF_EXT_SPACE(EXTRACTW, ICLASS_LD" 001 0 000 iiiii PP0iiiii -01iiiii") +DEF_ENC32(R6_release_at_vi, ICLASS_ST" 000 01 11sssss PP0ttttt --0011dd") +DEF_ENC32(R6_release_st_vi, ICLASS_ST" 000 01 11sssss PP0ttttt --1011dd") + +DEF_EXT_SPACE(EXTRACTW, ICLASS_LD" 001 0 000 iiiii PP0iiiii 001iiiii") DEF_ENC32(Y2_dcfetchbo, ICLASS_LD" 010 0 000 sssss PP0--iii iiiiiiii") - - - - /*******************************/ /* */ /* */ @@ -488,13 +490,17 @@ /* x bus/cache */ /* x store/cache */ DEF_ENC32(S2_allocframe, ICLASS_ST" 000 01 00xxxxx PP000iii iiiiiiii") -DEF_ENC32(S2_storew_locked,ICLASS_ST" 000 01 01sssss PP-ttttt ----00dd") -DEF_ENC32(S4_stored_locked,ICLASS_ST" 000 01 11sssss PP0ttttt ----00dd") +DEF_ENC32(Y5_l2locka, ICLASS_ST" 000 01 11sssss PP1----- ------dd") DEF_ENC32(Y2_dczeroa, ICLASS_ST" 000 01 10sssss PP0----- --------") DEF_ENC32(Y2_barrier, ICLASS_ST" 100 00 00----- PP------ 000-----") DEF_ENC32(Y2_syncht, ICLASS_ST" 100 00 10----- PP------ --------") +DEF_ENC32(Y2_l2kill, ICLASS_ST" 100 00 01----- PP-000-- --------") +DEF_ENC32(Y5_l2gunlock, ICLASS_ST" 100 00 01----- PP-010-- --------") +DEF_ENC32(Y5_l2gclean, ICLASS_ST" 100 00 01----- PP-100-- --------") +DEF_ENC32(Y5_l2gcleaninv, ICLASS_ST" 100 00 01----- PP-110-- --------") +DEF_ENC32(Y2_l2cleaninvidx,ICLASS_ST" 100 00 11sssss PP------ --------") @@ -502,9 +508,28 @@ DEF_ENC32(Y2_dcinva, ICLASS_ST" 000 00 01sssss PP------ --------") DEF_ENC32(Y2_dccleaninva, ICLASS_ST" 000 00 10sssss PP------ --------") -DEF_ENC32(Y4_l2fetch, ICLASS_ST" 011 00 00sssss PP-ttttt 000-----") +/* Super */ +DEF_ENC32(Y2_dckill, ICLASS_ST" 001 00 00----- PP------ --------") +DEF_ENC32(Y2_dccleanidx, ICLASS_ST" 001 00 01sssss PP------ --------") +DEF_ENC32(Y2_dcinvidx, ICLASS_ST" 001 00 10sssss PP------ --------") +DEF_ENC32(Y2_dccleaninvidx,ICLASS_ST" 001 00 11sssss PP------ --------") + +DEF_ENC32(Y2_dctagw ,ICLASS_ST" 010 00 00sssss PP-ttttt --------") +DEF_ENC32(Y2_dctagr ,ICLASS_ST" 010 00 01sssss PP------ ---ddddd") + +DEF_ENC32(Y4_l2tagw ,ICLASS_ST" 010 00 10sssss PP0ttttt --------") +DEF_ENC32(Y4_l2tagr ,ICLASS_ST" 010 00 11sssss PP------ ---ddddd") + +DEF_ENC32(Y4_l2fetch, ICLASS_ST" 011 00 00sssss PP-ttttt 000-----") +DEF_ENC32(Y5_l2cleanidx, ICLASS_ST" 011 00 01sssss PP------ --------") +DEF_ENC32(Y5_l2invidx, ICLASS_ST" 011 00 10sssss PP------ --------") +DEF_ENC32(Y5_l2unlocka, ICLASS_ST" 011 00 11sssss PP------ --------") DEF_ENC32(Y5_l2fetch, ICLASS_ST" 011 01 00sssss PP-ttttt --------") +DEF_ENC32(Y6_l2gcleanpa, ICLASS_ST" 011 01 01----- PP-ttttt --------") +DEF_ENC32(Y6_l2gcleaninvpa,ICLASS_ST" 011 01 10----- PP-ttttt --------") + + /*******************************/ /* */ /* */ @@ -547,13 +572,23 @@ DEF_FIELDROW_DESC32(ICLASS_J" 0100 -------- PP------ --------","[#4] (#u8) ") DEF_ENC32(J2_trap0, ICLASS_J" 0100 00------ PP-iiiii ---iii--") -DEF_ENC32(J2_pause, ICLASS_J" 0100 01------ PP-iiiii ---iii--") +DEF_ENC32(J2_trap1, ICLASS_J" 0100 10-xxxxx PP-iiiii ---iii--") +DEF_ENC32(J2_pause, ICLASS_J" 0100 01----ii PP-iiiii ---iii--") + +DEF_FIELDROW_DESC32(ICLASS_J" 0101 -------- PP------ --------","[#5] Rd=(Rs) ") +DEF_ENC32(Y2_icdatar, ICLASS_J" 0101 101sssss PP------ ---ddddd") +DEF_ENC32(Y2_ictagr, ICLASS_J" 0101 111sssss PP------ ---ddddd") +DEF_ENC32(Y2_ictagw, ICLASS_J" 0101 110sssss PP0ttttt --------") +DEF_ENC32(Y2_icdataw, ICLASS_J" 0101 110sssss PP1ttttt --------") DEF_FIELDROW_DESC32(ICLASS_J" 0110 -------- PP------ --------","[#6] icop(Rs) ") DEF_ENC32(Y2_icinva, ICLASS_J" 0110 110sssss PP000--- --------") +DEF_ENC32(Y2_icinvidx, ICLASS_J" 0110 110sssss PP001--- --------") +DEF_ENC32(Y2_ickill, ICLASS_J" 0110 110----- PP010--- --------") DEF_FIELDROW_DESC32(ICLASS_J" 0111 -------- PP------ --------","[#7] () ") DEF_ENC32(Y2_isync, ICLASS_J" 0111 11000000 PP0---00 00000010") +DEF_ENC32(J2_rte, ICLASS_J" 0111 111----- PP00---- 000-----") /* JUMP */ DEF_FIELDROW_DESC32(ICLASS_J" 100- -------- PP------ --------","[#8,9] PC=(#r22)") @@ -738,12 +773,19 @@ DEF_FIELDROW_DESC32( ICLASS_CR" 0010 -------- PP------ --------","[#2] Cd=Rs ") DEF_ENC32(A2_tfrrcr, ICLASS_CR" 0010 001sssss PP------ ---ddddd") +DEF_ENC32(G4_tfrgrcr, ICLASS_CR" 0010 000sssss PP------ ---ddddd") +DEF_ENC32(Y4_trace, ICLASS_CR" 0010 010sssss PP------ 000-----") +DEF_ENC32(Y6_diag, ICLASS_CR" 0010 010sssss PP------ 001-----") +DEF_ENC32(Y6_diag0, ICLASS_CR" 0010 010sssss PP-ttttt 010-----") +DEF_ENC32(Y6_diag1, ICLASS_CR" 0010 010sssss PP-ttttt 011-----") DEF_FIELDROW_DESC32( ICLASS_CR" 0011 -------- PP------ --------","[#3] Cdd=Rss ") DEF_ENC32(A4_tfrpcp, ICLASS_CR" 0011 001sssss PP------ ---ddddd") +DEF_ENC32(G4_tfrgpcp, ICLASS_CR" 0011 000sssss PP------ ---ddddd") DEF_FIELDROW_DESC32( ICLASS_CR" 1000 -------- PP------ --------","[#8] Rdd=Css ") DEF_ENC32(A4_tfrcpp, ICLASS_CR" 1000 000sssss PP------ ---ddddd") +DEF_ENC32(G4_tfrgcpp, ICLASS_CR" 1000 001sssss PP------ ---ddddd") DEF_FIELDROW_DESC32( ICLASS_CR" 1001 -------- PP------ --------","[#9] (#r8,#U10)") DEF_ENC32(J2_ploop1si, ICLASS_CR" 1001 101IIIII PP-iiiii IIIii-II") @@ -754,6 +796,7 @@ DEF_FIELDROW_DESC32( ICLASS_CR" 1010 -------- PP------ --------","[#10] Rd=Cs ") DEF_ENC32(A2_tfrcrr, ICLASS_CR" 1010 000sssss PP------ ---ddddd") +DEF_ENC32(G4_tfrgcrr, ICLASS_CR" 1010 001sssss PP------ ---ddddd") DEF_ENC32(C4_addipc, ICLASS_CR" 1010 01001001 PP-iiiii i--ddddd") @@ -781,6 +824,64 @@ +/* Supervisor CR ops */ +/* Interrupts */ +DEF_FIELDROW_DESC32( ICLASS_CR" 0100 -------- PP------ --------","[#4] (Rs,Pt)") +DEF_ENC32(Y2_swi, ICLASS_CR" 0100 000sssss PP------ 000-----") +DEF_ENC32(Y2_cswi, ICLASS_CR" 0100 000sssss PP------ 001-----") +DEF_ENC32(Y2_iassignw, ICLASS_CR" 0100 000sssss PP------ 010-----") +DEF_ENC32(Y2_ciad, ICLASS_CR" 0100 000sssss PP------ 011-----") +DEF_ENC32(Y2_setimask, ICLASS_CR" 0100 100sssss PP----tt 000-----") +DEF_ENC32(Y2_setprio, ICLASS_CR" 0100 100sssss PP----tt 001-----") +DEF_ENC32(Y4_siad, ICLASS_CR" 0100 100sssss PP------ 011-----") + +DEF_ENC32(Y2_wait, ICLASS_CR" 0100 010sssss PP------ 000-----") +DEF_ENC32(Y2_resume, ICLASS_CR" 0100 010sssss PP------ 001-----") +DEF_ENC32(Y2_stop, ICLASS_CR" 0100 011sssss PP------ 000-----") +DEF_ENC32(Y2_start, ICLASS_CR" 0100 011sssss PP------ 001-----") +DEF_ENC32(Y4_nmi, ICLASS_CR" 0100 011sssss PP------ 010-----") + +DEF_FIELDROW_DESC32( ICLASS_CR" 0101 -------- PP------ --------","[#5] Rx ") +DEF_ENC32(Y2_crswap0, ICLASS_CR" 0101 000xxxxx PP------ --------") +DEF_ENC32(Y4_crswap1, ICLASS_CR" 0101 001xxxxx PP------ --------") + +DEF_FIELDROW_DESC32( ICLASS_CR" 0110 -------- PP------ --------","[#6] Rd=(Rs)") +DEF_ENC32(Y2_getimask, ICLASS_CR" 0110 000sssss PP------ ---ddddd") +DEF_ENC32(Y2_iassignr, ICLASS_CR" 0110 011sssss PP------ ---ddddd") + +DEF_FIELDROW_DESC32( ICLASS_CR" 0111 -------- PP------ --------","[#7] cr=Rs ") +DEF_ENC32(Y2_tfrsrcr, ICLASS_CR" 0111 00-sssss PP------ -ddddddd") + +DEF_FIELDROW_DESC32( ICLASS_CR" 1100 -------- PP------ --------","[#12] ") +DEF_ENC32(Y2_break, ICLASS_CR" 1100 001----- PP------ 000-----") +DEF_ENC32(Y2_tlblock, ICLASS_CR" 1100 001----- PP------ 001-----") +DEF_ENC32(Y2_tlbunlock,ICLASS_CR" 1100 001----- PP------ 010-----") +DEF_ENC32(Y2_k0lock, ICLASS_CR" 1100 001----- PP------ 011-----") +DEF_ENC32(Y2_k0unlock, ICLASS_CR" 1100 001----- PP------ 100-----") +DEF_ENC32(Y2_tlbp, ICLASS_CR" 1100 100sssss PP------ ---ddddd") +DEF_ENC32(Y5_tlboc, ICLASS_CR" 1100 111sssss PP------ ---ddddd") +DEF_ENC32(Y5_tlbasidi, ICLASS_CR" 1100 101sssss PP------ --------") +DEF_ENC32(Y2_tlbr, ICLASS_CR" 1100 010sssss PP------ ---ddddd") +DEF_ENC32(Y2_tlbw, ICLASS_CR" 1100 000sssss PP0ttttt --------") +DEF_ENC32(Y5_ctlbw, ICLASS_CR" 1100 110sssss PP0ttttt ---ddddd") + +DEF_FIELDROW_DESC32( ICLASS_CR" 1101 -------- PP------ --------","[#13] Rxx ") +DEF_ENC32(Y4_crswap10, ICLASS_CR" 1101 10-xxxxx PP------ ---00000") +DEF_ENC32(Y4_tfrspcp, ICLASS_CR" 1101 00-sssss PP------ -ddddddd") + +DEF_FIELDROW_DESC32( ICLASS_CR" 1110 -------- PP------ --------","[#14] Rd=cr ") +DEF_ENC32(Y2_tfrscrr, ICLASS_CR" 1110 1sssssss PP------ ---ddddd") + +DEF_FIELDROW_DESC32( ICLASS_CR" 1111 -------- PP------ --------","[#15] Rdd=Sss ") +DEF_ENC32(Y4_tfrscpp, ICLASS_CR" 1111 0sssssss PP------ ---ddddd") + + + + + + + + /*******************************/ /* */ /* */
diff --git a/target/hexagon/imported/ldst.idef b/target/hexagon/imported/ldst.idef index 5319817..4e1e5d5 100644 --- a/target/hexagon/imported/ldst.idef +++ b/target/hexagon/imported/ldst.idef
@@ -203,6 +203,9 @@ Q6INSN(L4_loadd_locked,"Rdd32=memd_locked(Rs32)", ATTRIBS(A_REGWRSIZE_8B,A_MEMSIZE_8B,A_LOAD,A_RESTRICT_SLOT0ONLY,A_RESTRICT_PACKET_AXOK,A_NOTE_AXOK), "Load double with lock", { fEA_REG(RsV); fLOAD_LOCKED(1,8,u,EA,RddV) }) +Q6INSN(L4_loadw_phys,"Rd32=memw_phys(Rs32,Rt32)", ATTRIBS(A_REGWRSIZE_4B,A_PRIV,A_RESTRICT_SLOT0ONLY,A_NOTE_PRIV,A_MEMSIZE_4B,A_LOAD,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), "Load word from physical address", +{ fLOAD_PHYS(1,4,u,RsV,RtV,RdV); }) + Q6INSN(S4_stored_locked,"memd_locked(Rs32,Pd4)=Rtt32", ATTRIBS(A_REGWRSIZE_8B,A_MEMSIZE_8B,A_STORE,A_RESTRICT_SLOT0ONLY,A_RESTRICT_PACKET_AXOK,A_NOTE_AXOK,A_RESTRICT_LATEPRED,A_NOTE_LATEPRED), "Store word with lock", { fEA_REG(RsV); fSTORE_LOCKED(1,8,EA,RttV,PdV) })
diff --git a/target/hexagon/imported/macros.def b/target/hexagon/imported/macros.def old mode 100755 new mode 100644 index 4bbcfdd..13eb13c --- a/target/hexagon/imported/macros.def +++ b/target/hexagon/imported/macros.def
@@ -354,6 +354,12 @@ ) DEF_MACRO( + fREAD_SSR, /* read SSR register */ + (READ_RREG(REG_SSR)), /* behavior */ + () +) + +DEF_MACRO( fWRITE_LR, /* write lr */ WRITE_RREG(REG_LR,A), /* behavior */ (A_IMPLICIT_WRITES_LR) @@ -372,12 +378,36 @@ ) DEF_MACRO( + fWRITE_GOSP, /* write gosp */ + WRITE_RREG(REG_GOSP,A), /* behavior */ + (A_IMPLICIT_WRITES_GOSP) +) + +DEF_MACRO( fREAD_SP, /* read stack pointer */ (READ_RREG(REG_SP)), /* behavior */ () ) DEF_MACRO( + fREAD_GOSP, /* read guest other stack pointer */ + (READ_RREG(REG_GOSP)), /* behavior */ + () +) + +DEF_MACRO( + fREAD_GELR, /* read guest other stack pointer */ + (READ_RREG(REG_GELR)), /* behavior */ + () +) + +DEF_MACRO( + fREAD_GEVB, /* read guest other stack pointer */ + (READ_RREG(REG_GEVB)), /* behavior */ + () +) + +DEF_MACRO( fREAD_CSREG, /* read CS register */ (READ_RREG(REG_CSA+N)), /* behavior */ () @@ -570,6 +600,11 @@ WRITE_PREG(3,VAL), /* behavior */ (A_IMPLICIT_WRITES_P3) ) +DEF_MACRO( + fWRITE_P3_LATE, /* write Predicate 0 */ + {WRITE_PREG(3,VAL); fHIDE(MARK_LATE_PRED_WRITE(3))} , /* behavior */ + (A_IMPLICIT_WRITES_P3,A_RESTRICT_LATEPRED) +) DEF_MACRO( fPART1, /* write Predicate 0 */ @@ -660,6 +695,7 @@ ((size8s_t)((size2s_t)(A))), /* optional attributes */ ) + DEF_MACRO( fCAST2_8u, /* macro name */ ((size8u_t)((size2u_t)(A))), @@ -1532,18 +1568,137 @@ /* OS interface and stop/wait */ /********************************************/ +DEF_MACRO(RUNNABLE_THREADS_MAX, + /* */, + () +) + +DEF_MACRO(THREAD_IS_ON, + ((PROC->arch_proc_options->thread_enable_mask>>TNUM) & 0x1), + () +) + +DEF_MACRO(THREAD_EN_MASK, + ((PROC->arch_proc_options->thread_enable_mask)), + () +) + + + +DEF_MACRO(READ_IMASK, + /* */, + () +) +DEF_MACRO(WRITE_IMASK, + /* */, + (A_IMPLICIT_WRITES_IMASK_ANYTHREAD) +) + + +DEF_MACRO(WRITE_PRIO, + /* */, + (A_IMPLICIT_WRITES_STID_PRIO_ANYTHREAD) +) + + +DEF_MACRO(DO_IASSIGNW, + /* */, + (A_IMPLICIT_WRITES_IMASK_ANYTHREAD) +) + + + + +DEF_MACRO(fDO_NMI, + /* */, +) + +DEF_MACRO(fDO_TRACE, + /* */, +) + +DEF_MACRO(DO_IASSIGNR, + /* */, + () +) + +DEF_MACRO(DO_SWI, + /* */, + (A_EXCEPTION_SWI) +) + +DEF_MACRO(DO_CSWI, + LOG_GLOBAL_REG_WRITE(REG_IPEND,GLOBAL_REG_READ(REG_IPEND) & ~((REG) & GLOBAL_REG_READ(REG_IEL)));, + () +) + +DEF_MACRO(DO_CIAD, + sys_ciad(thread,VAL); LOG_GLOBAL_REG_WRITE(REG_IAD,GLOBAL_REG_READ(REG_IAD) & ~(VAL));, + (A_EXCEPTION_SWI) +) + +DEF_MACRO(DO_SIAD, + sys_siad(thread,VAL); LOG_GLOBAL_REG_WRITE(REG_IAD,GLOBAL_REG_READ(REG_IAD) | (VAL));, + (A_EXCEPTION_SWI) +) + +DEF_MACRO(fBREAK, + /* */, + () +) + DEF_MACRO(fPAUSE, {sys_pause(thread, insn->slot, IMM);}, () ) + DEF_MACRO(fTRAP, - warn("Trap NPC=%x ",fREAD_NPC()); - warn("Trap exception, PCYCLE=%lld TYPE=%d NPC=%x IMM=0x%x",thread->processor_ptr->pstats[pcycles],TRAPTYPE,fREAD_NPC(),IMM); - register_trap_exception(thread,fREAD_NPC(),TRAPTYPE,IMM);, + /* */, + (A_EXCEPTION_SWI) +) + +DEF_MACRO(fCLEAR_RTE_EX, + /* */, + () +) + +DEF_MACRO(fTLB_LOCK_AVAILABLE, + (fREAD_GLOBAL_REG_FIELD(SYSCONF,SYSCFG_TLBLOCK) == 0), () ) +DEF_MACRO(fK0_LOCK_AVAILABLE, + (fREAD_GLOBAL_REG_FIELD(SYSCONF,SYSCFG_K0LOCK) == 0), + () +) + +DEF_MACRO(fSET_TLB_LOCK, + { + if (fTLB_LOCK_AVAILABLE()) { + fLOG_GLOBAL_REG_FIELD(SYSCONF,SYSCFG_TLBLOCK,1); + } else { + sys_waiting_for_tlb_lock(thread); + } + }, + () +) + +DEF_MACRO(fSET_K0_LOCK, + /* */, + () +) + +DEF_MACRO(fCLEAR_TLB_LOCK, + /* */, + () +) + +DEF_MACRO(fCLEAR_K0_LOCK, + /* */, + () +) + DEF_MACRO(fALIGN_REG_FIELD_VALUE, ((VAL)<<reg_field_info[FIELD].offset), /* */ @@ -1554,6 +1709,24 @@ /* */ ) +DEF_MACRO(fLOG_REG_FIELD, + LOG_MASKED_REG_WRITE(thread,REG_##REG, + fALIGN_REG_FIELD_VALUE(FIELD,VAL), + fGET_REG_FIELD_MASK(FIELD)), + () +) + +DEF_MACRO(fWRITE_GLOBAL_REG_FIELD, + /* */, +) + +DEF_MACRO(fLOG_GLOBAL_REG_FIELD, + LOG_MASKED_GLOBAL_REG_WRITE(REG_##REG, + fALIGN_REG_FIELD_VALUE(FIELD,VAL), + fGET_REG_FIELD_MASK(FIELD)), + () +) + DEF_MACRO(fREAD_REG_FIELD, fEXTRACTU_BITS(thread->Regs[REG_##REG], reg_field_info[FIELD].width, @@ -1561,6 +1734,11 @@ /* ATTRIBS */ ) +DEF_MACRO(fREAD_GLOBAL_REG_FIELD, + /* */, + /* ATTRIBS */ +) + DEF_MACRO(fGET_FIELD, fEXTRACTU_BITS(VAL, reg_field_info[FIELD].width, @@ -1576,6 +1754,174 @@ /* ATTRIBS */ ) +DEF_MACRO(fSET_RUN_MODE_NOW, + /* */, +) + +DEF_MACRO(fIN_DEBUG_MODE, + (thread->debug_mode || (fREAD_GLOBAL_REG_FIELD(ISDBST,ISDBST_DEBUGMODE) & 1<<TNUM)), + () +) +DEF_MACRO(fIN_DEBUG_MODE_NO_ISDB, + (thread->debug_mode), + () +) + + +DEF_MACRO(fIN_DEBUG_MODE_WARN, + { + if (fREAD_GLOBAL_REG_FIELD(ISDBST,ISDBST_DEBUGMODE) & 1<<TNUM) + warn("In ISDB debug mode, but TB told me to step normally"); + }, + () +) + +DEF_MACRO(fCLEAR_RUN_MODE, + {fLOG_GLOBAL_REG_FIELD(MODECTL,MODECTL_E, + fREAD_GLOBAL_REG_FIELD(MODECTL,MODECTL_E) & ~(1<<(TNUM)))}, + /* NOTHING */ +) + +DEF_MACRO(fCLEAR_RUN_MODE_NOW, + /* */, + /* NOTHING */ +) + +DEF_MACRO(fGET_RUN_MODE, + /* */, +) + +DEF_MACRO(fSET_WAIT_MODE, + {fLOG_GLOBAL_REG_FIELD(MODECTL,MODECTL_W, + fREAD_GLOBAL_REG_FIELD(MODECTL,MODECTL_W) | 1<<(TNUM))}, + /* NOTHING */ +) + +DEF_MACRO(fCLEAR_WAIT_MODE, + /* */, +) + +DEF_MACRO(fGET_WAIT_MODE, + /* */, +) + + +DEF_MACRO(fRESET_THREAD, + register_reset_interrupt(T,NUM), +) + +DEF_MACRO(fREAD_CURRENT_EVB, + (GLOBAL_REG_READ(REG_EVB)), + /* nothing */ +) + +DEF_MACRO(fREAD_ELR, + READ_RREG(REG_ELR), + () +) + +DEF_MACRO(fPOW2_HELP_ROUNDUP, + ((VAL) | ((VAL) >> 1) | ((VAL) >> 2) | ((VAL) >> 4) | ((VAL) >> 8) | ((VAL) >> 16)), + () +) + +DEF_MACRO(fPOW2_ROUNDUP, + fPOW2_HELP_ROUNDUP((VAL)-1)+1, + () +) + +DEF_MACRO(fTLB_IDXMASK, + /* */, + () +) + +DEF_MACRO(fTLB_NONPOW2WRAP, + /* */, + /* ATTRIBS */ +) + +DEF_MACRO(fTLBW, + /* */, + /* ATTRIBS */ +) + +DEF_MACRO(fTLB_ENTRY_OVERLAP, + fHIDE( (sys_check_overlap(thread,VALUE)!=-2) ), + /* ATTRIBS */ +) + +DEF_MACRO(fTLB_ENTRY_OVERLAP_IDX, + fHIDE(sys_check_overlap(thread,VALUE)), + /* ATTRIBS */ +) + + +DEF_MACRO(fTLBR, + TLB_REG_READ(fTLB_NONPOW2WRAP(fTLB_IDXMASK(INDEX))), + /* ATTRIBS */ +) + +DEF_MACRO(fTLBP, + tlb_lookup(thread,((TLBHI)>>12),((TLBHI)<<12),1), + /* attribs */ +) + + + +DEF_MACRO(READ_SGP0, + READ_RREG(REG_SGP), + () +) + +DEF_MACRO(READ_SGP1, + READ_RREG(REG_SGP+1), + () +) + +DEF_MACRO(READ_SGP10, + READ_RREG_PAIR(REG_SGP), + () +) + +DEF_MACRO(READ_UGP, + READ_RREG(REG_UGP), +) + +DEF_MACRO(WRITE_SGP0, + WRITE_RREG(REG_SGP,VAL), + (A_IMPLICIT_WRITES_SGP0) +) + +DEF_MACRO(WRITE_SGP1, + WRITE_RREG(REG_SGP+1,VAL), + (A_IMPLICIT_WRITES_SGP1) +) + +DEF_MACRO(WRITE_SGP10, + WRITE_RREG_PAIR(REG_SGP,VAL), + (A_IMPLICIT_WRITES_SGP0,A_IMPLICIT_WRITES_SGP1) +) + +DEF_MACRO(WRITE_UGP, + WRITE_RREG(REG_UGP,VAL), +) + +DEF_MACRO(fSTART, + /* */, + () +) + +DEF_MACRO(fRESUME, + fLOG_GLOBAL_REG_FIELD(MODECTL,MODECTL_W, + fREAD_GLOBAL_REG_FIELD(MODECTL,MODECTL_W) & (~(REG))), + () +) + +DEF_MACRO(fGET_TNUM, + thread->threadId, + () +) + /********************************************/ /* Cache Management */ /********************************************/ @@ -1602,19 +1948,49 @@ ) +DEF_MACRO(fICFETCH, + , + () +) + DEF_MACRO(fDCFETCH, sys_dcfetch(thread, (REG), insn->slot), (A_MEMLIKE) ) DEF_MACRO(fICINVA, - { - arch_internal_flush(thread->processor_ptr, 0, 0xffffffff); - sys_icinva(thread, (REG),insn->slot); - }, + /* */, (A_ICINVA) ) +DEF_MACRO(fDCTAGR, + ({DST=sys_dctagr(thread, INDEX, insn->slot,DSTREGNO);})/* FIXME */, + () +) + +DEF_MACRO(fDCTAGW, + (sys_dctagw(thread, INDEX, PART2, insn->slot)), + () +) +DEF_MACRO(fICTAGR, + ({DST=sys_ictagr(thread, INDEX, insn->slot,REGNO);}), + () +) + +DEF_MACRO(fICDATAR, + ({DST=sys_icdatar(thread, INDEX, insn->slot);}), + () +) + +DEF_MACRO(fICTAGW, + (sys_ictagw(thread, INDEX, PART2, insn->slot)), + () +) +DEF_MACRO(fICDATAW, + ({ fHIDE(); }), + () +) + DEF_MACRO(fL2FETCH, sys_l2fetch(thread, ADDR,HEIGHT,WIDTH,STRIDE,FLAGS, insn->slot), (A_MEMLIKE,A_L2FETCH) @@ -1635,6 +2011,12 @@ (A_MEMLIKE) ) +DEF_MACRO(fDCINVA, + sys_dcinva(thread, (REG)), + (A_MEMLIKE) +) + + DEF_MACRO(fCHECKFORPRIV, {sys_check_privs(thread); if (EXCEPTION_DETECTED) return; }, () @@ -1645,6 +2027,16 @@ () ) +DEF_MACRO(fTAKEN_INTERRUPT_EDGECLEAR, + { proc->global_regs[REG_IPEND] &= ~(INT_NUMTOMASK(intnum) & proc->global_regs[REG_IEL]); }, + () +) + +DEF_MACRO(fSET_IAD, + /* */, + () +) + DEF_MACRO(fBRANCH_SPECULATE_STALL, { sys_speculate_branch_stall(thread, insn->slot, JUMP_COND(JUMP_PRED_SET), @@ -1664,3 +2056,79 @@ , () ) + +DEF_MACRO(fIN_MONITOR_MODE, + sys_in_monitor_mode(thread), + () +) + +DEF_MACRO(fIN_USER_MODE, + sys_in_user_mode(thread), + () +) + +DEF_MACRO(fIN_GUEST_MODE, + sys_in_guest_mode(thread), + () +) + +DEF_MACRO(fGRE_ENABLED, + fREAD_REG_FIELD(CCR,CCR_GRE), + () +) + +DEF_MACRO(fGTE_ENABLED, + fREAD_REG_FIELD(CCR,CCR_GRE), + () +) + +DEF_MACRO(fTRAP1_VIRTINSN, + ((fIN_GUEST_MODE()) + && (fGRE_ENABLED()) + && ( ((IMM) == 1) + || ((IMM) == 3) + || ((IMM) == 4) + || ((IMM) == 6))), + () +) + +DEF_MACRO(fVIRTINSN_RTE, + do { + thread->trap1_info = TRAP1_VIRTINSN_RTE; + fLOG_REG_FIELD(SSR,SSR_SS,fREAD_REG_FIELD(GSR,GSR_SS)); + fLOG_REG_FIELD(CCR,CCR_GIE,fREAD_REG_FIELD(GSR,GSR_IE)); + fLOG_REG_FIELD(SSR,SSR_GM,!fREAD_REG_FIELD(GSR,GSR_UM)); + fBRANCH((fREAD_GELR() & -4),COF_TYPE_RTE); + fINTERNAL_CLEAR_SAMEPAGE(); + } while (0), + (A_IMPLICIT_WRITES_CCR,A_IMPLICIT_WRITES_SSR) +) + +DEF_MACRO(fVIRTINSN_SETIE, + do { + fLOG_REG_FIELD(CCR,CCR_GIE,(REG) & 1); + REG = fREAD_REG_FIELD(CCR,CCR_GIE); + thread->trap1_info = TRAP1_VIRTINSN_SETIE; + } while (0), + (A_IMPLICIT_WRITES_CCR) +) + +DEF_MACRO(fVIRTINSN_GETIE, + { + thread->trap1_info = TRAP1_VIRTINSN_GETIE; + REG = fREAD_REG_FIELD(CCR,CCR_GIE); + }, + () +) + +DEF_MACRO(fVIRTINSN_SPSWAP, + do { + if (fREAD_REG_FIELD(GSR,GSR_UM)) { + size4u_t TEMP = REG; + REG = fREAD_GOSP(); + fWRITE_GOSP(TEMP); + thread->trap1_info = TRAP1_VIRTINSN_SPSWAP; + } + } while (0), + (A_IMPLICIT_WRITES_GOSP) +)
diff --git a/target/hexagon/imported/system.idef b/target/hexagon/imported/system.idef index 7c6568e..9e85bed 100644 --- a/target/hexagon/imported/system.idef +++ b/target/hexagon/imported/system.idef
@@ -25,31 +25,230 @@ /* User->OS interface */ /********************************************/ -Q6INSN(J2_trap0,"trap0(#u8)",ATTRIBS(A_COF), +Q6INSN(J2_trap0,"trap0(#u8)",ATTRIBS(A_COF,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), "Trap to Operating System", fTRAP(0,uiV); ) -Q6INSN(J2_pause,"pause(#u8)",ATTRIBS(A_COF), +Q6INSN(J2_trap1,"trap1(Rx32,#u8)",ATTRIBS(A_COF,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), +"Trap to Operating System", + /* + * Note: if RxV is not written, we get the same as the input. + * Since trap1 is SOLO, this means the register will effectively not be updated + */ + if (!fTRAP1_VIRTINSN(uiV)) { + fTRAP(1,uiV); + } else if (uiV == 1) { + fVIRTINSN_RTE(uiV,RxV); + } else if (uiV == 3) { + fVIRTINSN_SETIE(uiV,RxV); + } else if (uiV == 4) { + fVIRTINSN_GETIE(uiV,RxV); + } else if (uiV == 6) { + fVIRTINSN_SPSWAP(uiV,RxV); + }) + +Q6INSN(J2_pause,"pause(#u8)",ATTRIBS(A_COF,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), "Enter low-power state for #u8 cycles",{fPAUSE(uiV);}) -Q6INSN(Y2_icinva,"icinva(Rs32)",ATTRIBS(A_ICOP,A_ICFLUSHOP),"Instruction Cache Invalidate Address",{fEA_REG(RsV); fICINVA(EA);}) - -Q6INSN(Y2_isync,"isync",ATTRIBS(),"Memory Synchronization",{fISYNC();}) -Q6INSN(Y2_barrier,"barrier",ATTRIBS(A_RESTRICT_SLOT0ONLY),"Memory Barrier",{fBARRIER();}) -Q6INSN(Y2_syncht,"syncht",ATTRIBS(A_RESTRICT_SLOT0ONLY),"Memory Synchronization",{fSYNCH();}) +Q6INSN(J2_rte, "rte", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NO_TIMING_LOG), +"Return from Exception", +{ +fHIDE(if((thread->timing_on) && (thread->status & EXEC_STATUS_REPLAY)) { return; }) +fHIDE(CALLBACK(thread->processor_ptr->options->rte_callback, + thread->system_ptr,thread->processor_ptr, + thread->threadId,0);) +fCLEAR_RTE_EX(); +fBRANCH(fREAD_ELR(),COF_TYPE_RTE);}) -Q6INSN(Y2_dcfetchbo,"dcfetch(Rs32+#u11:3)",ATTRIBS(A_RESTRICT_PREFERSLOT0,A_DCFETCH),"Data Cache Prefetch",{fEA_RI(RsV,uiV); fDCFETCH(EA);}) +/********************************************/ +/* Interrupt Management */ +/********************************************/ + +Q6INSN(Y2_swi,"swi(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK),"Software Interrupt",{DO_SWI(RsV);}) +Q6INSN(Y2_cswi,"cswi(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK),"Cancel Software Interrupt",{DO_CSWI(RsV);}) +Q6INSN(Y2_ciad,"ciad(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK),"Re-enable interrupt in IAD",{DO_CIAD(RsV);}) +Q6INSN(Y4_siad,"siad(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK),"Disable interrupt in IAD",{DO_SIAD(RsV);}) +Q6INSN(Y2_iassignr,"Rd32=iassignr(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK),"Read interrupt to thread assignments",{DO_IASSIGNR(RsV,RdV);}) +Q6INSN(Y2_iassignw,"iassignw(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK),"Write interrupt to thread assignments",{DO_IASSIGNW(RsV);}) -Q6INSN(Y2_dczeroa,"dczeroa(Rs32)",ATTRIBS(A_STORE,A_RESTRICT_SLOT0ONLY,A_DCZEROA),"Zero an aligned 32-byte cacheline",{fEA_REG(RsV); fDCZEROA(EA);}) -Q6INSN(Y2_dccleana,"dccleana(Rs32)",ATTRIBS(A_RESTRICT_SLOT0ONLY,A_DCFLUSHOP),"Data Cache Clean Address",{fEA_REG(RsV); fDCCLEANA(EA);}) -Q6INSN(Y2_dccleaninva,"dccleaninva(Rs32)",ATTRIBS(A_RESTRICT_SLOT0ONLY,A_DCFLUSHOP),"Data Cache Clean and Invalidate Address",{fEA_REG(RsV); fDCCLEANINVA(EA);}) -Q6INSN(Y2_dcinva,"dcinva(Rs32)",ATTRIBS(A_RESTRICT_SLOT0ONLY,A_DCFLUSHOP),"Data Cache Invalidate Address",{fEA_REG(RsV); fDCCLEANINVA(EA);}) +Q6INSN(Y2_getimask,"Rd32=getimask(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK),"Read imask register of another thread", +{RdV = READ_IMASK(RsV & thread->processor_ptr->thread_system_mask); }) + +Q6INSN(Y2_setimask,"setimask(Pt4,Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK),"Change imask register of another thread", +{fPREDUSE_TIMING();WRITE_IMASK(PtV & thread->processor_ptr->thread_system_mask,RsV); }) -Q6INSN(Y4_l2fetch,"l2fetch(Rs32,Rt32)",ATTRIBS(A_RESTRICT_SLOT0ONLY),"L2 Cache Prefetch", + +/********************************************/ +/* TLB management */ +/********************************************/ + +Q6INSN(Y2_tlbw,"tlbw(Rss32,Rt32)", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), +"Write TLB entry", {fTLBW(RtV,RssV);}) + +Q6INSN(Y5_ctlbw,"Rd32=ctlbw(Rss32,Rt32)", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), +"Conditional Write TLB entry", +{ + if (fTLB_ENTRY_OVERLAP( (1LL<<63) | RssV )) { + RdV=fTLB_ENTRY_OVERLAP_IDX( (1LL<<63) | RssV); + } else { + fTLBW(RtV,RssV); + RdV=0x80000000; + } +}) + +Q6INSN(Y5_tlboc,"Rd32=tlboc(Rss32)", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), +"TLB overlap check", +{ + if (fTLB_ENTRY_OVERLAP( (1LL<<63) | RssV )) { + RdV=fTLB_ENTRY_OVERLAP_IDX( (1LL<<63) | RssV); + } else { + RdV=0x80000000; + } +}) + +Q6INSN(Y2_tlbr,"Rdd32=tlbr(Rs32)", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), "Read TLB entry", +{RddV = fTLBR(RsV);}) + +Q6INSN(Y2_tlbp,"Rd32=tlbp(Rs32)", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), "Probe TLB", {RdV=fTLBP(RsV);}) + +Q6INSN(Y5_tlbasidi,"tlbinvasid(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), "Invalidate ASID", +{ + fHIDE(int i;) + fHIDE(unsigned int NUM_TLB_ENTRIES = NUM_TLB_REGS(thread->processor_ptr);) + for (i = 0; i < NUM_TLB_ENTRIES; i++) { + if ((fGET_FIELD(fTLBR(i),PTE_G) == 0) && + (fGET_FIELD(fTLBR(i),PTE_ASID) == fEXTRACTU_RANGE(RsV,26,20))) { + fTLBW(i,fTLBR(i) & ~(1ULL << 63)); + } + } +}) + +Q6INSN(Y2_tlblock,"tlblock", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_NO_TIMING_LOG), "Lock TLB", +{fSET_TLB_LOCK();}) + +Q6INSN(Y2_tlbunlock,"tlbunlock", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), "Unlock TLB", +{fCLEAR_TLB_LOCK();}) + +Q6INSN(Y2_k0lock,"k0lock", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_NO_TIMING_LOG), "Lock K0", +{fSET_K0_LOCK();}) + +Q6INSN(Y2_k0unlock,"k0unlock", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET), "Unlock K0", +{fCLEAR_K0_LOCK();}) + +/********************************************/ +/* Supervisor Reg Management */ +/********************************************/ + +Q6INSN(Y2_crswap0,"crswap(Rx32,sgp0)",ATTRIBS(A_PRIV,A_NOTE_PRIV), "Swap system general pointer 0 with GPR", +{fHIDE(size4s_t tmp;) tmp = RxV; RxV = READ_SGP0(); WRITE_SGP0(tmp);}) +Q6INSN(Y4_crswap1,"crswap(Rx32,sgp1)",ATTRIBS(A_PRIV,A_NOTE_PRIV), "Swap system general pointer 1 with GPR", +{fHIDE(size4s_t tmp;) tmp = RxV; RxV = READ_SGP1(); WRITE_SGP1(tmp);}) + +Q6INSN(Y4_crswap10,"crswap(Rxx32,sgp1:0)",ATTRIBS(A_PRIV,A_NOTE_PRIV), "Swap system general purpose 0/1 with GPR Pair", +{fHIDE(size8s_t tmp;) tmp = RxxV; RxxV=READ_SGP10(); WRITE_SGP10(tmp);}) + +Q6INSN(Y2_tfrscrr,"Rd32=Ss128",ATTRIBS(A_PRIV,A_NOTE_PRIV),"Transfer Supervisor Reg to GPR", {RdV=SsV;}) +Q6INSN(Y2_tfrsrcr,"Sd128=Rs32",ATTRIBS(A_PRIV,A_NOTE_PRIV),"Transfer GPR to Supervisor Reg", {SdV=RsV;}) +Q6INSN(Y4_tfrscpp,"Rdd32=Sss128",ATTRIBS(A_PRIV,A_NOTE_PRIV),"Transfer Supervisor Reg to GPR", {RddV=SssV;}) +Q6INSN(Y4_tfrspcp,"Sdd128=Rss32",ATTRIBS(A_PRIV,A_NOTE_PRIV),"Transfer GPR to Supervisor Reg", {SddV=RssV;}) + +Q6INSN(G4_tfrgcrr,"Rd32=Gs32",ATTRIBS(A_GUEST,A_NOTE_GUEST),"Transfer Guest Reg to GPR", {RdV=GsV;}) +Q6INSN(G4_tfrgrcr,"Gd32=Rs32",ATTRIBS(A_GUEST,A_NOTE_GUEST),"Transfer GPR to Guest Reg", {GdV=RsV;}) +Q6INSN(G4_tfrgcpp,"Rdd32=Gss32",ATTRIBS(A_GUEST,A_NOTE_GUEST),"Transfer Guest Reg to GPR", {RddV=GssV;}) +Q6INSN(G4_tfrgpcp,"Gdd32=Rss32",ATTRIBS(A_GUEST,A_NOTE_GUEST),"Transfer GPR to Guest Reg", {GddV=RssV;}) + + + +Q6INSN(Y2_setprio,"setprio(Pt4,Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV),"Change TID Prio of another thread", +{fPREDUSE_TIMING();WRITE_PRIO(PtV & thread->processor_ptr->thread_system_mask,RsV); }) + + + + +/********************************************/ +/* Power Management / Thread on/off */ +/********************************************/ +Q6INSN(Y6_diag,"diag(Rs32)",ATTRIBS(),"Send value to Diag trace module",{ +}) +Q6INSN(Y6_diag0,"diag0(Rss32,Rtt32)",ATTRIBS(),"Send values of two register to DIAG Trace. Set X=0",{ +}) +Q6INSN(Y6_diag1,"diag1(Rss32,Rtt32)",ATTRIBS(),"Send values of two register to DIAG Trace. Set X=1",{ +}) + + +Q6INSN(Y4_trace,"trace(Rs32)",ATTRIBS(A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK),"Send value to ETM trace",{ + fDO_TRACE(RsV); +}) + +Q6INSN(Y2_stop,"stop(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET),"Stop thread(s)",{ + fHIDE(RsV=RsV;) + if (!fIN_DEBUG_MODE_NO_ISDB(fGET_TNUM())) fCLEAR_RUN_MODE(fGET_TNUM()); +}) + +Q6INSN(Y4_nmi,"nmi(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_NO_TIMING_LOG),"Raise NMI on thread(s)",{ + fDO_NMI(RsV); +}) + +Q6INSN(Y2_start,"start(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET),"Start thread(s)",fSTART(RsV);) + +Q6INSN(Y2_wait,"wait(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_NO_TIMING_LOG),"Make thread(s) wait",{ + fHIDE(RsV=RsV;) + if (!fIN_DEBUG_MODE(fGET_TNUM())) fSET_WAIT_MODE(fGET_TNUM()); + fIN_DEBUG_MODE_WARN(fGET_TNUM()); +}) + +Q6INSN(Y2_resume,"resume(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET),"Make thread(s) stop waiting",fRESUME(RsV);) + +Q6INSN(Y2_break,"brkpt",ATTRIBS(A_NOTE_NOPACKET,A_RESTRICT_NOPACKET),"Breakpoint",{fBREAK();}) + + +/********************************************/ +/* Cache Management */ +/********************************************/ + +Q6INSN(Y2_ictagr,"Rd32=ictagr(Rs32)",ATTRIBS(A_ICOP,A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_CACHEOP,A_COPBYIDX,A_ICTAGOP),"Instruction Cache Tag Read",{fICTAGR(RsV,RdV,RdN);}) +Q6INSN(Y2_ictagw,"ictagw(Rs32,Rt32)",ATTRIBS(A_ICOP,A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_CACHEOP,A_COPBYIDX,A_ICTAGOP),"Instruction Cache Tag Write",{fICTAGW(RsV,RtV);}) +Q6INSN(Y2_icdataw,"icdataw(Rs32,Rt32)",ATTRIBS(A_ICOP,A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_CACHEOP,A_COPBYIDX,A_ICTAGOP),"Instruction Cache Data Write",{fICDATAW(RsV,RtV);}) +Q6INSN(Y2_icdatar,"Rd32=icdatar(Rs32)",ATTRIBS(A_ICOP,A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_CACHEOP,A_COPBYIDX,A_ICTAGOP),"Instruction Cache Data Read",{fICDATAR(RsV, RdV);}) +Q6INSN(Y2_icinva,"icinva(Rs32)",ATTRIBS(A_ICOP,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_CACHEOP,A_COPBYADDRESS,A_ICFLUSHOP),"Instruction Cache Invalidate Address",{fEA_REG(RsV); fICINVA(EA);}) +Q6INSN(Y2_icinvidx,"icinvidx(Rs32)",ATTRIBS(A_ICOP,A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_CACHEOP,A_COPBYIDX,A_ICFLUSHOP),"Instruction Cache Invalidate Index",{fICINVIDX(RsV);}) +Q6INSN(Y2_ickill,"ickill",ATTRIBS(A_ICOP,A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_CACHEOP,A_ICFLUSHOP),"Instruction Cache Invalidate",{fICKILL();}) + +Q6INSN(Y2_isync,"isync",ATTRIBS(A_NOTE_NOPACKET,A_RESTRICT_NOPACKET),"Memory Synchronization",{fISYNC();}) +Q6INSN(Y2_barrier,"barrier",ATTRIBS(A_NOTE_NOPACKET,A_RESTRICT_SLOT0ONLY,A_RESTRICT_PACKET_AXOK),"Memory Barrier",{fBARRIER();}) +Q6INSN(Y2_syncht,"syncht",ATTRIBS(A_NOTE_NOPACKET,A_RESTRICT_SLOT0ONLY,A_RESTRICT_NOPACKET),"Memory Synchronization",{fSYNCH();}) + + +Q6INSN(Y2_dcfetchbo,"dcfetch(Rs32+#u11:3)",ATTRIBS(A_RESTRICT_PREFERSLOT0,A_DCFETCH,A_RESTRICT_NOSLOT1_STORE),"Data Cache Prefetch",{fEA_RI(RsV,uiV); fDCFETCH(EA);}) +Q6INSN(Y2_dckill,"dckill",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_SLOT0ONLY,A_RESTRICT_NOPACKET,A_CACHEOP,A_DCFLUSHOP),"Data Cache Invalidate",{fDCKILL();}) + + +Q6INSN(Y2_dczeroa,"dczeroa(Rs32)",ATTRIBS(A_STORE,A_RESTRICT_SLOT1_AOK,A_NOTE_SLOT1_AOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYADDRESS,A_DCZEROA),"Zero an aligned 32-byte cacheline",{fEA_REG(RsV); fDCZEROA(EA);}) +Q6INSN(Y2_dccleana,"dccleana(Rs32)",ATTRIBS(A_RESTRICT_SLOT1_AOK,A_NOTE_SLOT1_AOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYADDRESS,A_DCFLUSHOP),"Data Cache Clean Address",{fEA_REG(RsV); fDCCLEANA(EA);}) +Q6INSN(Y2_dccleanidx,"dccleanidx(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_RESTRICT_PACKET_AXOK,A_NOTE_AXOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYIDX,A_DCFLUSHOP),"Data Cache Clean Index",{fDCCLEANIDX(RsV);}) +Q6INSN(Y2_dccleaninva,"dccleaninva(Rs32)",ATTRIBS(A_RESTRICT_SLOT1_AOK,A_NOTE_SLOT1_AOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYADDRESS,A_DCFLUSHOP),"Data Cache Clean and Invalidate Address",{fEA_REG(RsV); fDCCLEANINVA(EA);}) +Q6INSN(Y2_dccleaninvidx,"dccleaninvidx(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_RESTRICT_PACKET_AXOK,A_NOTE_AXOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYIDX,A_DCFLUSHOP),"Data Cache Clean and Invalidate Index",{fDCCLEANINVIDX(RsV);}) +Q6INSN(Y2_dcinva,"dcinva(Rs32)",ATTRIBS(A_RESTRICT_SLOT1_AOK,A_NOTE_SLOT1_AOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYADDRESS,A_DCFLUSHOP),"Data Cache Invalidate Address",{fEA_REG(RsV); fDCCLEANINVA(EA);}) +Q6INSN(Y2_dcinvidx,"dcinvidx(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_RESTRICT_PACKET_AXOK,A_NOTE_AXOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYIDX,A_DCFLUSHOP),"Data Cache Invalidate Index",{fDCINVIDX(RsV);}) +Q6INSN(Y2_dctagr,"Rd32=dctagr(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_RESTRICT_PACKET_AXOK,A_NOTE_AXOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYIDX,A_DCTAGOP),"Data Cache Tag Read",{fDCTAGR(RsV,RdV,RdN);}) +Q6INSN(Y2_dctagw,"dctagw(Rs32,Rt32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_RESTRICT_SLOT0ONLY,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_CACHEOP,A_COPBYIDX,A_DCTAGOP),"Data Cache Tag Write",{fDCTAGW(RsV,RtV);}) + + +Q6INSN(Y2_l2kill,"l2kill",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_SLOT0ONLY,A_RESTRICT_NOPACKET,A_CACHEOP,A_L2FLUSHOP),"L2 Cache Invalidate",{fL2KILL();}) +Q6INSN(Y4_l2tagw,"l2tagw(Rs32,Rt32)",ATTRIBS(A_PRIV,A_NOTE_BADTAG_UNDEF,A_NOTE_PRIV,A_RESTRICT_SLOT0ONLY,A_NOTE_NOPACKET,A_RESTRICT_NOPACKET,A_CACHEOP,A_COPBYIDX,A_L2TAGOP),"L2 Cache Tag Write",{fL2TAGW(RsV,RtV);}) +Q6INSN(Y4_l2tagr,"Rd32=l2tagr(Rs32)",ATTRIBS(A_PRIV,A_NOTE_BADTAG_UNDEF,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYIDX,A_L2TAGOP),"L2 Cache Tag Read",{fL2TAGR(RsV,RdV,RdN);}) + +Q6INSN(Y2_l2cleaninvidx,"l2cleaninvidx(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYIDX,A_L2FLUSHOP),"L2 Cache Clean and Invalidate Index",{fL2CLEANINVIDX(RsV); }) +Q6INSN(Y5_l2cleanidx,"l2cleanidx(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYIDX,A_L2FLUSHOP),"L2 Cache Clean by Index",{fL2CLEANIDX(RsV); }) +Q6INSN(Y5_l2invidx,"l2invidx(Rs32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_AXOK,A_RESTRICT_PACKET_AXOK,A_RESTRICT_SLOT0ONLY,A_CACHEOP,A_COPBYIDX,A_L2FLUSHOP),"L2 Cache Invalidate by Index",{fL2INVIDX(RsV); }) + + + +Q6INSN(Y4_l2fetch,"l2fetch(Rs32,Rt32)",ATTRIBS(A_RESTRICT_SLOT0ONLY,A_RESTRICT_PACKET_AXOK,A_NOTE_AXOK),"L2 Cache Prefetch", { fL2FETCH(RsV, (RtV&0xff), /*height*/ ((RtV>>8)&0xff), /*width*/ @@ -59,10 +258,28 @@ -Q6INSN(Y5_l2fetch,"l2fetch(Rs32,Rtt32)",ATTRIBS(A_RESTRICT_SLOT0ONLY),"L2 Cache Prefetch", +Q6INSN(Y5_l2fetch,"l2fetch(Rs32,Rtt32)",ATTRIBS(A_RESTRICT_SLOT0ONLY,A_RESTRICT_PACKET_AXOK,A_NOTE_AXOK),"L2 Cache Prefetch", { fL2FETCH(RsV, fGETUHALF(0,RttV), /*height*/ fGETUHALF(1,RttV), /*width*/ fGETUHALF(2,RttV), /*stride*/ fGETUHALF(3,RttV)); /*flags*/ }) + +Q6INSN(Y5_l2locka,"Pd4=l2locka(Rs32)", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_CACHEOP,A_COPBYADDRESS,A_RESTRICT_SLOT0ONLY,A_RESTRICT_PACKET_AXOK,A_NOTE_AXOK,A_RESTRICT_LATEPRED,A_NOTE_LATEPRED), +"Lock L2 cache line by address", { fEA_REG(RsV); fL2LOCKA(EA,PdV,PdN); fHIDE(MARK_LATE_PRED_WRITE(PdN)) }) + + +Q6INSN(Y5_l2unlocka,"l2unlocka(Rs32)", ATTRIBS(A_PRIV,A_NOTE_PRIV,A_CACHEOP,A_COPBYADDRESS,A_RESTRICT_SLOT0ONLY,A_RESTRICT_PACKET_AXOK,A_NOTE_AXOK), "UnLock L2 cache line by address", { fEA_REG(RsV); fL2UNLOCKA(EA); }) + + + +Q6INSN(Y5_l2gunlock,"l2gunlock",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_SLOT0ONLY,A_RESTRICT_NOPACKET,A_CACHEOP,A_L2FLUSHOP),"L2 Global Unlock",{fL2UNLOCK();}) + +Q6INSN(Y5_l2gclean,"l2gclean",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_SLOT0ONLY,A_RESTRICT_NOPACKET,A_CACHEOP,A_L2FLUSHOP),"L2 Global Clean",{fL2CLEAN();}) + +Q6INSN(Y5_l2gcleaninv,"l2gcleaninv",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_SLOT0ONLY,A_RESTRICT_NOPACKET,A_CACHEOP,A_L2FLUSHOP),"L2 Global Clean and Invalidate",{fL2CLEANINV();}) + +Q6INSN(Y6_l2gcleanpa,"l2gclean(Rtt32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_SLOT0ONLY,A_RESTRICT_NOPACKET,A_CACHEOP,A_L2FLUSHOP),"L2 Global Clean by PA Range",{fL2CLEANPA(RttV);}) + +Q6INSN(Y6_l2gcleaninvpa,"l2gcleaninv(Rtt32)",ATTRIBS(A_PRIV,A_NOTE_PRIV,A_NOTE_NOPACKET,A_RESTRICT_SLOT0ONLY,A_RESTRICT_NOPACKET,A_CACHEOP,A_L2FLUSHOP),"L2 Global Clean and Invalidate by PA Range",{fL2CLEANINVPA(RttV);})
diff --git a/target/hexagon/internal.h b/target/hexagon/internal.h index 5fc837a..00b37ae 100644 --- a/target/hexagon/internal.h +++ b/target/hexagon/internal.h
@@ -30,5 +30,27 @@ void hexagon_debug(CPUHexagonState *env); extern const char * const hexagon_regnames[TOTAL_PER_THREAD_REGS]; +#ifndef CONFIG_USER_ONLY +extern const char * const hexagon_sregnames[]; +extern const char * const hexagon_gregnames[]; +#endif + +void G_NORETURN do_raise_exception(CPUHexagonState *env, + uint32_t exception, + uint32_t PC, + uintptr_t retaddr); + +#define hexagon_cpu_mmu_enabled(env) ({ \ + HexagonCPU *cpu = env_archcpu(env); \ + cpu->globalregs ? \ + GET_SYSCFG_FIELD(SYSCFG_MMUEN, \ + hexagon_globalreg_read(cpu->globalregs, \ + HEX_SREG_SYSCFG, (env)->threadId)) : \ + 0; \ +}) + +#ifndef CONFIG_USER_ONLY +extern const VMStateDescription vmstate_hexagon_cpu; +#endif #endif
diff --git a/target/hexagon/machine.c b/target/hexagon/machine.c new file mode 100644 index 0000000..2dd9546 --- /dev/null +++ b/target/hexagon/machine.c
@@ -0,0 +1,32 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "migration/vmstate.h" +#include "cpu.h" + +const VMStateDescription vmstate_hexagon_cpu = { + .name = "cpu", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(env.gpr, HexagonCPU, TOTAL_PER_THREAD_REGS), + VMSTATE_UINT32_ARRAY(env.pred, HexagonCPU, NUM_PREGS), + VMSTATE_UINT32_ARRAY(env.t_sreg, HexagonCPU, NUM_SREGS), + VMSTATE_UINT32_ARRAY(env.greg, HexagonCPU, NUM_GREGS), + VMSTATE_UINT32(env.next_PC, HexagonCPU), + VMSTATE_UINT32(env.tlb_lock_state, HexagonCPU), + VMSTATE_UINT32(env.k0_lock_state, HexagonCPU), + VMSTATE_UINT32(env.tlb_lock_count, HexagonCPU), + VMSTATE_UINT32(env.k0_lock_count, HexagonCPU), + VMSTATE_UINT32(env.threadId, HexagonCPU), + VMSTATE_UINT32(env.cause_code, HexagonCPU), + VMSTATE_UINT32(env.wait_next_pc, HexagonCPU), + VMSTATE_UINT64(env.t_cycle_count, HexagonCPU), + + VMSTATE_END_OF_LIST() + }, +};
diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index eebfe1e..21ab8ae 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h
@@ -538,9 +538,6 @@ #ifdef CONFIG_USER_ONLY #define fFRAMECHECK(ADDR, EA) do { } while (0) /* Not modelled in linux-user */ -#else -/* System mode not implemented yet */ -#define fFRAMECHECK(ADDR, EA) g_assert_not_reached(); #endif #ifdef QEMU_GENERATE @@ -631,8 +628,18 @@ #define fCONSTLL(A) A##LL #define fECHO(A) (A) -#define fTRAP(TRAPTYPE, IMM) helper_raise_exception(env, HEX_EXCP_TRAP0) +#ifdef CONFIG_USER_ONLY +#define fTRAP(TRAPTYPE, IMM) \ + do { \ + hexagon_raise_exception_err(env, HEX_EVENT_TRAP0, PC); \ + } while (0) +#endif + +#define fDO_TRACE(SREG) +#define fBREAK() +#define fUNPAUSE() #define fPAUSE(IMM) +#define fDCFETCH(REG) #define fALIGN_REG_FIELD_VALUE(FIELD, VAL) \ ((VAL) << reg_field_info[FIELD].offset) @@ -643,6 +650,16 @@ reg_field_info[FIELD].width, \ reg_field_info[FIELD].offset) +#define fGET_FIELD(VAL, FIELD) \ + fEXTRACTU_BITS(VAL, \ + reg_field_info[FIELD].width, \ + reg_field_info[FIELD].offset) +#define fSET_FIELD(VAL, FIELD, NEWVAL) \ + fINSERT_BITS(VAL, \ + reg_field_info[FIELD].width, \ + reg_field_info[FIELD].offset, \ + (NEWVAL)) + #ifdef QEMU_GENERATE #define fDCZEROA(REG) \ do { \ @@ -654,5 +671,18 @@ #define fBRANCH_SPECULATE_STALL(DOTNEWVAL, JUMP_COND, SPEC_DIR, HINTBITNUM, \ STRBITNUM) /* Nothing */ +#ifdef CONFIG_USER_ONLY +/* + * This macro can only be true in guest mode. + * In user mode, the 4 VIRTINSN's can't be reached + */ +#define fTRAP1_VIRTINSN(IMM) (false) +#define fVIRTINSN_SPSWAP(IMM, REG) g_assert_not_reached() +#define fVIRTINSN_GETIE(IMM, REG) g_assert_not_reached() +#define fVIRTINSN_SETIE(IMM, REG) g_assert_not_reached() +#define fVIRTINSN_RTE(IMM, REG) g_assert_not_reached() +#endif + +#define fPREDUSE_TIMING() #endif
diff --git a/target/hexagon/meson.build b/target/hexagon/meson.build index d169cf7..59cb09c 100644 --- a/target/hexagon/meson.build +++ b/target/hexagon/meson.build
@@ -20,6 +20,7 @@ hex_common_py = 'hex_common.py' gen_tcg_h = meson.current_source_dir() / 'gen_tcg.h' gen_tcg_hvx_h = meson.current_source_dir() / 'gen_tcg_hvx.h' +gen_tcg_sys_h = meson.current_source_dir() / 'gen_tcg_sys.h' idef_parser_dir = meson.current_source_dir() / 'idef-parser' # @@ -234,6 +235,7 @@ command: [python, files('gen_trans_funcs.py'), semantics_generated, '@OUTPUT@'], ) hexagon_ss.add(decodetree_trans_funcs_generated) +hexagon_softmmu_ss = ss.source_set() hexagon_ss.add(files( 'cpu.c', @@ -252,6 +254,14 @@ 'mmvec/system_ext_mmvec.c', )) +hexagon_softmmu_ss.add(files( + 'cpu_helper.c', + 'hex_mmu.c', + 'hex_interrupts.c', + 'hexswi.c', + 'machine.c', +)) + # # Step 4.5 # We use flex/bison based idef-parser to generate TCG code for a lot @@ -261,7 +271,8 @@ # idef-generated-enabled-instructions # idef_parser_enabled = get_option('hexagon_idef_parser') -if idef_parser_enabled and 'hexagon-linux-user' in target_dirs +if idef_parser_enabled and ('hexagon-linux-user' in target_dirs or + 'hexagon-softmmu' in target_dirs) idef_parser_input_generated = custom_target( 'idef_parser_input.h.inc', output: 'idef_parser_input.h.inc', @@ -270,13 +281,13 @@ command: [python, files('gen_idef_parser_funcs.py'), semantics_generated, '@OUTPUT@'], ) - compiler = meson.get_compiler('c').get_id() + compiler = meson.get_compiler('c').cmd_array() preprocessed_idef_parser_input_generated = custom_target( 'idef_parser_input.preprocessed.h.inc', output: 'idef_parser_input.preprocessed.h.inc', input: idef_parser_input_generated, depend_files: [idef_parser_dir / 'macros.h.inc'], - command: [compiler, '-x', 'c', '-E', '-I', idef_parser_dir, '-o', '@OUTPUT@', '@INPUT@'], + command: compiler + ['-x', 'c', '-E', '-I', idef_parser_dir, '-o', '@OUTPUT@', '@INPUT@'], ) flex = generator( @@ -337,12 +348,12 @@ # Setup input and dependencies for the next step, this depends on whether or # not idef-parser is enabled helper_dep = [semantics_generated, idef_generated_tcg_c, idef_generated_tcg] - helper_in = [semantics_generated, gen_tcg_h, gen_tcg_hvx_h, '--idef-parser', idef_generated_list] + helper_in = [semantics_generated, gen_tcg_h, gen_tcg_hvx_h, gen_tcg_sys_h, '--idef-parser', idef_generated_list] else # Setup input and dependencies for the next step, this depends on whether or # not idef-parser is enabled helper_dep = [semantics_generated] - helper_in = [semantics_generated, gen_tcg_h, gen_tcg_hvx_h] + helper_in = [semantics_generated, gen_tcg_h, gen_tcg_hvx_h, gen_tcg_sys_h] endif # @@ -356,7 +367,7 @@ 'helper_protos_generated.h.inc', output: 'helper_protos_generated.h.inc', depends: helper_dep, - depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h], + depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h, gen_tcg_sys_h], command: [python, files('gen_helper_protos.py'), helper_in, '@OUTPUT@'], ) hexagon_ss.add(helper_protos_generated) @@ -365,7 +376,7 @@ 'helper_funcs_generated.c.inc', output: 'helper_funcs_generated.c.inc', depends: helper_dep, - depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h], + depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h, gen_tcg_sys_h], command: [python, files('gen_helper_funcs.py'), helper_in, '@OUTPUT@'], ) hexagon_ss.add(helper_funcs_generated) @@ -374,7 +385,7 @@ 'tcg_funcs_generated.c.inc', output: 'tcg_funcs_generated.c.inc', depends: helper_dep, - depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h], + depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h, gen_tcg_sys_h], command: [python, files('gen_tcg_funcs.py'), helper_in, '@OUTPUT@'], ) hexagon_ss.add(tcg_funcs_generated) @@ -383,9 +394,10 @@ 'analyze_funcs_generated.c.inc', output: 'analyze_funcs_generated.c.inc', depends: helper_dep, - depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h], + depend_files: [hex_common_py, gen_tcg_h, gen_tcg_hvx_h, gen_tcg_sys_h], command: [python, files('gen_analyze_funcs.py'), helper_in, '@OUTPUT@'], ) hexagon_ss.add(analyze_funcs_generated) target_arch += {'hexagon': hexagon_ss} +target_system_arch += {'hexagon': hexagon_softmmu_ss}
diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index 0a94643..125952a 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c
@@ -20,36 +20,59 @@ #include "accel/tcg/cpu-ldst.h" #include "accel/tcg/cpu-loop.h" #include "accel/tcg/probe.h" +#include "qemu/main-loop.h" +#include "cpu.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" -#include "cpu.h" +#include "exec/cpu-interrupt.h" #include "internal.h" #include "macros.h" +#include "sys_macros.h" #include "arch.h" #include "hex_arch_types.h" #include "fma_emu.h" #include "mmvec/mmvec.h" #include "mmvec/macros.h" #include "op_helper.h" +#include "cpu_helper.h" #include "translate.h" +#ifndef CONFIG_USER_ONLY +#include "hw/hexagon/hexagon_globalreg.h" +#include "hex_mmu.h" +#include "hw/hexagon/hexagon_tlb.h" +#include "hex_interrupts.h" +#include "hexswi.h" +#endif #define SF_BIAS 127 #define SF_MANTBITS 23 /* Exceptions processing helpers */ +G_NORETURN +void do_raise_exception(CPUHexagonState *env, uint32_t exception, + uint32_t PC, uintptr_t retaddr) +{ + CPUState *cs = env_cpu(env); + qemu_log_mask(CPU_LOG_INT, "%s: 0x%08" PRIx32 ", @ %08" PRIx32 "\n", + __func__, exception, PC); + ASSERT_DIRECT_TO_GUEST_UNSET(env, exception); + + env->gpr[HEX_REG_PC] = PC; + cs->exception_index = exception; + cpu_loop_exit_restore(cs, retaddr); +} + G_NORETURN void hexagon_raise_exception_err(CPUHexagonState *env, uint32_t exception, uintptr_t pc) { - CPUState *cs = env_cpu(env); - qemu_log_mask(CPU_LOG_INT, "%s: %d\n", __func__, exception); - cs->exception_index = exception; - cpu_loop_exit_restore(cs, pc); + do_raise_exception(env, exception, pc, 0); } -G_NORETURN void HELPER(raise_exception)(CPUHexagonState *env, uint32_t excp) +G_NORETURN void HELPER(raise_exception)(CPUHexagonState *env, uint32_t excp, + uint32_t PC) { - hexagon_raise_exception_err(env, excp, 0); + hexagon_raise_exception_err(env, excp, PC); } void log_store32(CPUHexagonState *env, target_ulong addr, @@ -1205,6 +1228,131 @@ return RxxV; } +#ifndef CONFIG_USER_ONLY +void HELPER(modify_ssr)(CPUHexagonState *env, uint32_t new, uint32_t old) +{ + BQL_LOCK_GUARD(); + hexagon_modify_ssr(env, new, old); +} + +static void hex_k0_lock(CPUHexagonState *env) +{ + HexagonCPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + target_ulong syscfg; + + BQL_LOCK_GUARD(); + g_assert((env->k0_lock_count == 0) || (env->k0_lock_count == 1)); + + syscfg = cpu->globalregs ? + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SYSCFG, + env->threadId) : 0; + if (GET_SYSCFG_FIELD(SYSCFG_K0LOCK, syscfg)) { + if (env->k0_lock_state == HEX_LOCK_QUEUED) { + env->next_PC += 4; + env->k0_lock_count++; + env->k0_lock_state = HEX_LOCK_OWNER; + SET_SYSCFG_FIELD(env, SYSCFG_K0LOCK, 1); + return; + } + if (env->k0_lock_state == HEX_LOCK_OWNER) { + qemu_log_mask(LOG_GUEST_ERROR, + "Double k0lock at PC: 0x%" PRIx32 + ", thread may hang\n", + env->next_PC); + env->next_PC += 4; + cpu_interrupt(cs, CPU_INTERRUPT_HALT); + cpu_loop_exit(cs); + return; + } + env->k0_lock_state = HEX_LOCK_WAITING; + cpu_interrupt(cs, CPU_INTERRUPT_HALT); + cpu_loop_exit(cs); + } else { + env->next_PC += 4; + env->k0_lock_count++; + env->k0_lock_state = HEX_LOCK_OWNER; + SET_SYSCFG_FIELD(env, SYSCFG_K0LOCK, 1); + } +} + +static void hex_k0_unlock(CPUHexagonState *env) +{ + HexagonCPU *cpu = env_archcpu(env); + unsigned int this_threadId = env->threadId; + CPUHexagonState *unlock_thread = NULL; + CPUState *cs; + target_ulong syscfg; + + BQL_LOCK_GUARD(); + g_assert((env->k0_lock_count == 0) || (env->k0_lock_count == 1)); + + /* Nothing to do if the k0 isn't locked by this thread */ + syscfg = cpu->globalregs ? + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SYSCFG, + env->threadId) : 0; + if ((GET_SYSCFG_FIELD(SYSCFG_K0LOCK, syscfg) == 0) || + (env->k0_lock_state != HEX_LOCK_OWNER)) { + qemu_log_mask(LOG_GUEST_ERROR, + "thread %" PRIu32 " attempted to unlock k0 without" + " having the lock, k0_lock state = %u," + " syscfg:k0 = %" PRIu32 "\n", + env->threadId, (unsigned)env->k0_lock_state, + (uint32_t)GET_SYSCFG_FIELD(SYSCFG_K0LOCK, syscfg)); + g_assert(env->k0_lock_state != HEX_LOCK_WAITING); + return; + } + + env->k0_lock_count--; + env->k0_lock_state = HEX_LOCK_UNLOCKED; + SET_SYSCFG_FIELD(env, SYSCFG_K0LOCK, 0); + + /* Look for a thread to unlock */ + CPU_FOREACH(cs) { + CPUHexagonState *thread = cpu_env(cs); + + /* + * The hardware implements round-robin fairness, so we look for threads + * starting at env->threadId + 1 and incrementing modulo the number of + * threads. + * + * To implement this, we check if thread is a earlier in the modulo + * sequence than unlock_thread. + * if unlock thread is higher than this thread + * thread must be between this thread and unlock_thread + * else + * thread higher than this thread is ahead of unlock_thread + * thread must be lower then unlock thread + */ + if (thread->k0_lock_state == HEX_LOCK_WAITING) { + if (!unlock_thread) { + unlock_thread = thread; + } else if (unlock_thread->threadId > this_threadId) { + if (this_threadId < thread->threadId && + thread->threadId < unlock_thread->threadId) { + unlock_thread = thread; + } + } else { + if (thread->threadId > this_threadId) { + unlock_thread = thread; + } + if (thread->threadId < unlock_thread->threadId) { + unlock_thread = thread; + } + } + } + } + if (unlock_thread) { + cs = env_cpu(unlock_thread); + unlock_thread->k0_lock_state = HEX_LOCK_QUEUED; + SET_SYSCFG_FIELD(unlock_thread, SYSCFG_K0LOCK, 1); + cpu_interrupt(cs, CPU_INTERRUPT_K0_UNLOCK); + } + +} +#endif + + /* Histogram instructions */ void HELPER(vhist)(CPUHexagonState *env) @@ -1370,6 +1518,420 @@ } } +#ifndef CONFIG_USER_ONLY +void HELPER(raise_stack_overflow)(CPUHexagonState *env, uint32_t slot, + uint32_t badva) +{ + /* + * Per section 7.3.1 of the V67 Programmer's Reference, + * stack limit exception isn't raised in monitor mode. + */ + uint32_t ssr = env->t_sreg[HEX_SREG_SSR]; + CPUState *cs; + + if (GET_SSR_FIELD(SSR_EX, ssr) || + !GET_SSR_FIELD(SSR_UM, ssr)) { + return; + } + + cs = env_cpu(env); + cs->exception_index = HEX_EVENT_PRECISE; + env->cause_code = HEX_CAUSE_STACK_LIMIT; + ASSERT_DIRECT_TO_GUEST_UNSET(env, cs->exception_index); + + if (slot == 0) { + env->t_sreg[HEX_SREG_BADVA0] = badva; + SET_SSR_FIELD(env, SSR_V0, 1); + SET_SSR_FIELD(env, SSR_V1, 0); + SET_SSR_FIELD(env, SSR_BVS, 0); + } else if (slot == 1) { + env->t_sreg[HEX_SREG_BADVA1] = badva; + SET_SSR_FIELD(env, SSR_V0, 0); + SET_SSR_FIELD(env, SSR_V1, 1); + SET_SSR_FIELD(env, SSR_BVS, 1); + } else { + g_assert_not_reached(); + } + cpu_loop_exit_restore(cs, 0); +} + +void HELPER(ciad)(CPUHexagonState *env, uint32_t mask) +{ + g_assert_not_reached(); +} + +void HELPER(siad)(CPUHexagonState *env, uint32_t mask) +{ + uint32_t ipendad; + uint32_t iad; + HexagonCPU *cpu; + + BQL_LOCK_GUARD(); + cpu = env_archcpu(env); + ipendad = cpu->globalregs ? + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_IPENDAD, + env->threadId) : 0; + iad = fGET_FIELD(ipendad, IPENDAD_IAD); + fSET_FIELD(ipendad, IPENDAD_IAD, iad | mask); + if (cpu->globalregs) { + hexagon_globalreg_write(cpu->globalregs, HEX_SREG_IPENDAD, + ipendad, env->threadId); + } + hex_interrupt_update(env); +} + +void HELPER(swi)(CPUHexagonState *env, uint32_t mask) +{ + BQL_LOCK_GUARD(); + hex_raise_interrupts(env, mask, CPU_INTERRUPT_SWI); +} + +void HELPER(cswi)(CPUHexagonState *env, uint32_t mask) +{ + BQL_LOCK_GUARD(); + hex_clear_interrupts(env, mask, CPU_INTERRUPT_SWI); +} + +void HELPER(iassignw)(CPUHexagonState *env, uint32_t src) +{ + uint32_t modectl; + uint32_t thread_enabled_mask; + CPUState *cpu; + HexagonCPU *hex_cpu; + + BQL_LOCK_GUARD(); + hex_cpu = env_archcpu(env); + modectl = hex_cpu->globalregs ? + hexagon_globalreg_read(hex_cpu->globalregs, HEX_SREG_MODECTL, + env->threadId) : 0; + thread_enabled_mask = GET_FIELD(MODECTL_E, modectl); + + CPU_FOREACH(cpu) { + CPUHexagonState *thread_env = &(HEXAGON_CPU(cpu)->env); + uint32_t thread_id_mask = 0x1 << thread_env->threadId; + if (thread_enabled_mask & thread_id_mask) { + uint32_t imask = thread_env->t_sreg[HEX_SREG_IMASK]; + uint32_t intbitpos = (src >> 16) & 0xF; + uint32_t val = (src >> thread_env->threadId) & 0x1; + imask = deposit32(imask, intbitpos, 1, val); + thread_env->t_sreg[HEX_SREG_IMASK] = imask; + + qemu_log_mask(CPU_LOG_INT, "%s: thread " TARGET_FMT_ld + ", new imask 0x%" PRIx32 "\n", __func__, + thread_env->threadId, imask); + } + } + hex_interrupt_update(env); +} + +uint32_t HELPER(iassignr)(CPUHexagonState *env, uint32_t src) +{ + uint32_t modectl; + uint32_t thread_enabled_mask; + uint32_t intbitpos; + uint32_t dest_reg; + CPUState *cpu; + HexagonCPU *hex_cpu; + + BQL_LOCK_GUARD(); + hex_cpu = env_archcpu(env); + modectl = hex_cpu->globalregs ? + hexagon_globalreg_read(hex_cpu->globalregs, HEX_SREG_MODECTL, + env->threadId) : 0; + thread_enabled_mask = GET_FIELD(MODECTL_E, modectl); + /* src fields are in same position as modectl, but mean different things */ + intbitpos = GET_FIELD(MODECTL_W, src); + dest_reg = 0; + CPU_FOREACH(cpu) { + CPUHexagonState *thread_env = &(HEXAGON_CPU(cpu)->env); + uint32_t thread_id_mask = 0x1 << thread_env->threadId; + if (thread_enabled_mask & thread_id_mask) { + uint32_t imask = thread_env->t_sreg[HEX_SREG_IMASK]; + dest_reg |= ((imask >> intbitpos) & 0x1) << thread_env->threadId; + } + } + + return dest_reg; +} + +void HELPER(start)(CPUHexagonState *env, uint32_t imask) +{ + hexagon_start_threads(env, imask); +} + +void HELPER(stop)(CPUHexagonState *env) +{ + hexagon_stop_thread(env); +} + +static void set_wait_mode(CPUHexagonState *env) +{ + HexagonCPU *cpu; + uint32_t modectl; + uint32_t thread_wait_mask; + + g_assert(bql_locked()); + + cpu = env_archcpu(env); + if (!cpu->globalregs) { + return; + } + modectl = + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL, + env->threadId); + thread_wait_mask = GET_FIELD(MODECTL_W, modectl); + thread_wait_mask |= 0x1 << env->threadId; + SET_SYSTEM_FIELD(env, HEX_SREG_MODECTL, MODECTL_W, thread_wait_mask); +} + +static void hexagon_wait_thread(CPUHexagonState *env, uint32_t PC) +{ + CPUState *cs; + + g_assert(bql_locked()); + + if (qemu_loglevel_mask(LOG_GUEST_ERROR) && + (env->k0_lock_state != HEX_LOCK_UNLOCKED || + env->tlb_lock_state != HEX_LOCK_UNLOCKED)) { + qemu_log("WARNING: executing wait() with acquired lock" + "may lead to deadlock\n"); + } + g_assert(get_exe_mode(env) != HEX_EXE_MODE_WAIT); + + cs = env_cpu(env); + /* + * The addtion of cpu_has_work is borrowed from arm's wfi helper + * and is critical for our stability + */ + if ((cs->exception_index != HEX_EVENT_NONE) || + (cpu_has_work(cs))) { + qemu_log_mask(CPU_LOG_INT, + "%s: thread %" PRIu32 " skipping WAIT mode, have some work\n", + __func__, env->threadId); + return; + } + set_wait_mode(env); + env->wait_next_pc = PC + 4; + + cpu_interrupt(cs, CPU_INTERRUPT_HALT); +} + +static inline QEMU_ALWAYS_INLINE void resched(CPUHexagonState *env) +{ + uint32_t schedcfg; + uint32_t schedcfg_en; + int int_number; + CPUState *cs; + uint32_t lowest_th_prio = 0; /* 0 is highest prio */ + uint32_t bestwait_reg; + uint32_t best_prio; + HexagonCPU *cpu; + + BQL_LOCK_GUARD(); + qemu_log_mask(CPU_LOG_INT, "%s: check resched\n", __func__); + cpu = env_archcpu(env); + schedcfg = cpu->globalregs ? + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SCHEDCFG, + env->threadId) : 0; + schedcfg_en = GET_FIELD(SCHEDCFG_EN, schedcfg); + int_number = GET_FIELD(SCHEDCFG_INTNO, schedcfg); + + if (!schedcfg_en) { + return; + } + + CPU_FOREACH(cs) { + HexagonCPU *thread = HEXAGON_CPU(cs); + CPUHexagonState *thread_env = &(thread->env); + uint32_t th_prio = GET_FIELD( + STID_PRIO, thread_env->t_sreg[HEX_SREG_STID]); + if (!hexagon_thread_is_enabled(thread_env)) { + continue; + } + + lowest_th_prio = (lowest_th_prio > th_prio) + ? lowest_th_prio + : th_prio; + } + + bestwait_reg = cpu->globalregs ? + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_BESTWAIT, + env->threadId) : 0; + best_prio = GET_FIELD(BESTWAIT_PRIO, bestwait_reg); + + /* + * If the lowest priority thread is lower priority than the + * value in the BESTWAIT register, we must raise the reschedule + * interrupt on the lowest priority thread. + */ + if (lowest_th_prio > best_prio) { + qemu_log_mask(CPU_LOG_INT, + "%s: raising resched int %u," + " cur PC 0x%" PRIx32 "\n", + __func__, (unsigned)int_number, env->gpr[HEX_REG_PC]); + SET_SYSTEM_FIELD(env, HEX_SREG_BESTWAIT, BESTWAIT_PRIO, ~0); + hex_raise_interrupts(env, 1 << int_number, CPU_INTERRUPT_SWI); + } +} + +void HELPER(resched)(CPUHexagonState *env) +{ + resched(env); +} + +void HELPER(wait)(CPUHexagonState *env, uint32_t PC) +{ + BQL_LOCK_GUARD(); + + if (!fIN_DEBUG_MODE(env->threadId)) { + hexagon_wait_thread(env, PC); + } +} + +void HELPER(resume)(CPUHexagonState *env, uint32_t mask) +{ + BQL_LOCK_GUARD(); + hexagon_resume_threads(env, mask); +} + +uint32_t HELPER(getimask)(CPUHexagonState *env, uint32_t tid) +{ + CPUState *cs; + BQL_LOCK_GUARD(); + CPU_FOREACH(cs) { + HexagonCPU *found_cpu = HEXAGON_CPU(cs); + CPUHexagonState *found_env = &found_cpu->env; + if (found_env->threadId == tid) { + uint32_t imask = found_env->t_sreg[HEX_SREG_IMASK]; + qemu_log_mask(CPU_LOG_INT, "%s: tid " TARGET_FMT_lx + " imask = 0x%" PRIx32 "\n", __func__, + env->threadId, + (uint32_t)GET_FIELD(IMASK_MASK, imask)); + return GET_FIELD(IMASK_MASK, imask); + } + } + return 0; +} + +void HELPER(setimask)(CPUHexagonState *env, uint32_t tid, uint32_t imask) +{ + CPUState *cs; + + BQL_LOCK_GUARD(); + CPU_FOREACH(cs) { + HexagonCPU *found_cpu = HEXAGON_CPU(cs); + CPUHexagonState *found_env = &found_cpu->env; + + if (tid == found_env->threadId) { + SET_SYSTEM_FIELD(found_env, HEX_SREG_IMASK, IMASK_MASK, imask); + qemu_log_mask(CPU_LOG_INT, "%s: tid " TARGET_FMT_lx + " imask 0x%" PRIx32 "\n", + __func__, found_env->threadId, imask); + hex_interrupt_update(found_env); + return; + } + } + qemu_log_mask(LOG_GUEST_ERROR, + "setimask used with an invalid tid near PC: 0x%" + PRIx32 "\n", env->next_PC); +} + +void HELPER(sreg_write_masked)(CPUHexagonState *env, uint32_t reg, uint32_t val) +{ + BQL_LOCK_GUARD(); + if (reg < HEX_SREG_GLB_START) { + env->t_sreg[reg] = val; + } else { + HexagonCPU *cpu = env_archcpu(env); + if (cpu->globalregs) { + hexagon_globalreg_write_masked(cpu->globalregs, reg, val); + } + } +} + +static inline QEMU_ALWAYS_INLINE uint32_t sreg_read(CPUHexagonState *env, + uint32_t reg) +{ + HexagonCPU *cpu; + + g_assert(bql_locked()); + if (reg < HEX_SREG_GLB_START) { + return env->t_sreg[reg]; + } + cpu = env_archcpu(env); + return cpu->globalregs ? + hexagon_globalreg_read(cpu->globalregs, reg, env->threadId) : 0; +} + +uint32_t HELPER(sreg_read)(CPUHexagonState *env, uint32_t reg) +{ + BQL_LOCK_GUARD(); + return sreg_read(env, reg); +} + +uint64_t HELPER(sreg_read_pair)(CPUHexagonState *env, uint32_t reg) +{ + BQL_LOCK_GUARD(); + + return deposit64((uint64_t) sreg_read(env, reg), 32, 32, + sreg_read(env, reg + 1)); +} + +uint32_t HELPER(greg_read)(CPUHexagonState *env, uint32_t reg) + +{ + return hexagon_greg_read(env, reg); +} + +uint64_t HELPER(greg_read_pair)(CPUHexagonState *env, uint32_t reg) + +{ + if (reg == HEX_GREG_G0 || reg == HEX_GREG_G2) { + return (uint64_t)(env->greg[reg]) | + (((uint64_t)(env->greg[reg + 1])) << 32); + } + switch (reg) { + case HEX_GREG_GPCYCLELO: + return hexagon_get_sys_pcycle_count(env); + default: + return (uint64_t)hexagon_greg_read(env, reg) | + ((uint64_t)(hexagon_greg_read(env, reg + 1)) << 32); + } +} + +/* + * setprio/resched - hardware-assisted scheduler helpers for managing + * the run queue and interrupt steering. + */ +void HELPER(setprio)(CPUHexagonState *env, uint32_t thread, uint32_t prio) +{ + CPUState *cs; + + BQL_LOCK_GUARD(); + CPU_FOREACH(cs) { + HexagonCPU *found_cpu = HEXAGON_CPU(cs); + CPUHexagonState *found_env = &found_cpu->env; + if (thread == found_env->threadId) { + SET_SYSTEM_FIELD(found_env, HEX_SREG_STID, STID_PRIO, prio); + qemu_log_mask(CPU_LOG_INT, + "%s: tid %" PRIu32 " prio = 0x%" PRIx32 "\n", + __func__, found_env->threadId, prio); + resched(env); + return; + } + } + g_assert_not_reached(); +} + + +void HELPER(pending_interrupt)(CPUHexagonState *env) +{ + BQL_LOCK_GUARD(); + hex_interrupt_update(env); +} +#endif + + /* These macros can be referenced in the generated helper functions */ #define warn(...) /* Nothing */ #define fatal(...) g_assert_not_reached();
diff --git a/target/hexagon/reg_fields_def.h.inc b/target/hexagon/reg_fields_def.h.inc index f2a58d4..d2c706d 100644 --- a/target/hexagon/reg_fields_def.h.inc +++ b/target/hexagon/reg_fields_def.h.inc
@@ -39,3 +39,110 @@ DEF_REG_FIELD(USR_FPOVFE, 27, 1) DEF_REG_FIELD(USR_FPUNFE, 28, 1) DEF_REG_FIELD(USR_FPINPE, 29, 1) + +DEF_REG_FIELD(IPENDAD_IAD, 16, 16) +DEF_REG_FIELD(IPENDAD_IPEND, 0, 16) + +DEF_REG_FIELD(SCHEDCFG_EN, 8, 1) +DEF_REG_FIELD(SCHEDCFG_INTNO, 0, 4) +DEF_REG_FIELD(BESTWAIT_PRIO, 0, 9) + + +/* PTE (aka TLB entry) fields */ +DEF_REG_FIELD(PTE_PPD, 0, 24) +DEF_REG_FIELD(PTE_C, 24, 4) +DEF_REG_FIELD(PTE_U, 28, 1) +DEF_REG_FIELD(PTE_R, 29, 1) +DEF_REG_FIELD(PTE_W, 30, 1) +DEF_REG_FIELD(PTE_X, 31, 1) +DEF_REG_FIELD(PTE_VPN, 32, 20) +DEF_REG_FIELD(PTE_ASID, 52, 7) +DEF_REG_FIELD(PTE_ATR0, 59, 1) +DEF_REG_FIELD(PTE_ATR1, 60, 1) +DEF_REG_FIELD(PTE_PA35, 61, 1) +DEF_REG_FIELD(PTE_G, 62, 1) +DEF_REG_FIELD(PTE_V, 63, 1) + +/* SYSCFG fields */ +DEF_REG_FIELD(SYSCFG_MMUEN, 0, 1) +DEF_REG_FIELD(SYSCFG_ICEN, 1, 1) +DEF_REG_FIELD(SYSCFG_DCEN, 2, 1) +DEF_REG_FIELD(SYSCFG_ISDBTRUSTED, 3, 1) +DEF_REG_FIELD(SYSCFG_GIE, 4, 1) +DEF_REG_FIELD(SYSCFG_ISDBREADY, 5, 1) +DEF_REG_FIELD(SYSCFG_PCYCLEEN, 6, 1) +DEF_REG_FIELD(SYSCFG_V2X, 7, 1) +DEF_REG_FIELD(SYSCFG_IGNOREDABORT, 8, 1) +DEF_REG_FIELD(SYSCFG_PM, 9, 1) +DEF_REG_FIELD(SYSCFG_TLBLOCK, 11, 1) +DEF_REG_FIELD(SYSCFG_K0LOCK, 12, 1) +DEF_REG_FIELD(SYSCFG_BQ, 13, 1) +DEF_REG_FIELD(SYSCFG_PRIO, 14, 1) +DEF_REG_FIELD(SYSCFG_DMT, 15, 1) +DEF_REG_FIELD(SYSCFG_L2CFG, 16, 3) +DEF_REG_FIELD(SYSCFG_ITCM, 19, 1) +DEF_REG_FIELD(SYSCFG_L2NWA, 21, 1) +DEF_REG_FIELD(SYSCFG_L2NRA, 22, 1) +DEF_REG_FIELD(SYSCFG_L2WB, 23, 1) +DEF_REG_FIELD(SYSCFG_L2P, 24, 1) +DEF_REG_FIELD(SYSCFG_SLVCTL0, 25, 2) +DEF_REG_FIELD(SYSCFG_SLVCTL1, 27, 2) +DEF_REG_FIELD(SYSCFG_L2PARTSIZE, 29, 2) +DEF_REG_FIELD(SYSCFG_L2GCA, 31, 1) + +/* SSR fields */ +DEF_REG_FIELD(SSR_CAUSE, 0, 8) +DEF_REG_FIELD(SSR_ASID, 8, 7) +DEF_REG_FIELD(SSR_UM, 16, 1) +DEF_REG_FIELD(SSR_EX, 17, 1) +DEF_REG_FIELD(SSR_IE, 18, 1) +DEF_REG_FIELD(SSR_GM, 19, 1) +DEF_REG_FIELD(SSR_V0, 20, 1) +DEF_REG_FIELD(SSR_V1, 21, 1) +DEF_REG_FIELD(SSR_BVS, 22, 1) +DEF_REG_FIELD(SSR_CE, 23, 1) +DEF_REG_FIELD(SSR_PE, 24, 1) +DEF_REG_FIELD(SSR_BP, 25, 1) +DEF_REG_FIELD(SSR_XE2, 26, 1) +DEF_REG_FIELD(SSR_XA, 27, 3) +DEF_REG_FIELD(SSR_SS, 30, 1) +DEF_REG_FIELD(SSR_XE, 31, 1) + +/* misc registers */ +DEF_REG_FIELD(IMASK_MASK, 0, 16) + +DEF_REG_FIELD(STID_PRIO, 16, 8) +DEF_REG_FIELD(STID_STID, 0, 8) + +/* MODECTL fields */ +DEF_REG_FIELD(MODECTL_E, 0, 8) +DEF_REG_FIELD(MODECTL_W, 16, 8) + +DEF_REG_FIELD(CCR_L1ICP, 0, 2) +DEF_REG_FIELD(CCR_L1DCP, 3, 2) +DEF_REG_FIELD(CCR_L2CP, 6, 2) + +DEF_REG_FIELD(CCR_HFI, 16, 1) +DEF_REG_FIELD(CCR_HFD, 17, 1) +DEF_REG_FIELD(CCR_HFIL2, 18, 1) +DEF_REG_FIELD(CCR_HFDL2, 19, 1) +DEF_REG_FIELD(CCR_SFD, 20, 1) + +DEF_REG_FIELD(CCR_GIE, 24, 1) +DEF_REG_FIELD(CCR_GTE, 25, 1) +DEF_REG_FIELD(CCR_GEE, 26, 1) +DEF_REG_FIELD(CCR_GRE, 27, 1) +DEF_REG_FIELD(CCR_VV1, 29, 1) +DEF_REG_FIELD(CCR_VV2, 30, 1) +DEF_REG_FIELD(CCR_VV3, 31, 1) + +/* ISDB ST fields */ +DEF_REG_FIELD(ISDBST_WAITRUN, 24, 8) +DEF_REG_FIELD(ISDBST_ONOFF, 16, 8) +DEF_REG_FIELD(ISDBST_DEBUGMODE, 8, 8) +DEF_REG_FIELD(ISDBST_STUFFSTATUS, 5, 1) +DEF_REG_FIELD(ISDBST_CMDSTATUS, 4, 1) +DEF_REG_FIELD(ISDBST_PROCMODE, 3, 1) +DEF_REG_FIELD(ISDBST_MBXINSTATUS, 2, 1) +DEF_REG_FIELD(ISDBST_MBXOUTSTATUS, 1, 1) +DEF_REG_FIELD(ISDBST_READY, 0, 1)
diff --git a/target/hexagon/sys_macros.h b/target/hexagon/sys_macros.h new file mode 100644 index 0000000..a49c727 --- /dev/null +++ b/target/hexagon/sys_macros.h
@@ -0,0 +1,252 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HEXAGON_SYS_MACROS_H +#define HEXAGON_SYS_MACROS_H + +/* + * Macro definitions for Hexagon system mode + */ + +#ifndef CONFIG_USER_ONLY + +#include "system/physmem.h" + +#ifdef QEMU_GENERATE +#define GET_SSR_FIELD(RES, FIELD) \ + GET_FIELD(RES, FIELD, hex_t_sreg[HEX_SREG_SSR]) +#else + +#define GET_SSR_FIELD(FIELD, REGIN) \ + (uint32_t)GET_FIELD(FIELD, REGIN) +#define GET_SYSCFG_FIELD(FIELD, REGIN) \ + (uint32_t)GET_FIELD(FIELD, REGIN) +#define SET_SYSTEM_FIELD(ENV, REG, FIELD, VAL) \ + do { \ + HexagonCPU *_sf_cpu = env_archcpu(ENV); \ + uint32_t regval; \ + if ((REG) < HEX_SREG_GLB_START) { \ + regval = (ENV)->t_sreg[(REG)]; \ + } else { \ + regval = _sf_cpu->globalregs ? \ + hexagon_globalreg_read(_sf_cpu->globalregs, (REG), \ + (ENV)->threadId) : 0; \ + } \ + fINSERT_BITS(regval, reg_field_info[FIELD].width, \ + reg_field_info[FIELD].offset, (VAL)); \ + if ((REG) < HEX_SREG_GLB_START) { \ + (ENV)->t_sreg[(REG)] = regval; \ + } else if (_sf_cpu->globalregs) { \ + hexagon_globalreg_write(_sf_cpu->globalregs, (REG), regval, \ + (ENV)->threadId); \ + } \ + } while (0) +#define SET_SSR_FIELD(ENV, FIELD, VAL) \ + SET_SYSTEM_FIELD(ENV, HEX_SREG_SSR, FIELD, VAL) +#define SET_SYSCFG_FIELD(ENV, FIELD, VAL) \ + SET_SYSTEM_FIELD(ENV, HEX_SREG_SYSCFG, FIELD, VAL) + +#define CCR_FIELD_SET(ENV, FIELD) \ + (!!GET_FIELD(FIELD, (ENV)->t_sreg[HEX_SREG_CCR])) + +/* + * Direct-to-guest is not implemented yet, continuing would cause unexpected + * behavior, so we abort. + */ +#define ASSERT_DIRECT_TO_GUEST_UNSET(ENV, EXCP) \ + do { \ + switch (EXCP) { \ + case HEX_EVENT_TRAP0: \ + g_assert(!CCR_FIELD_SET(ENV, CCR_GTE)); \ + break; \ + case HEX_EVENT_IMPRECISE: \ + case HEX_EVENT_PRECISE: \ + case HEX_EVENT_FPTRAP: \ + g_assert(!CCR_FIELD_SET(ENV, CCR_GEE)); \ + break; \ + default: \ + if ((EXCP) >= HEX_EVENT_INT0) { \ + g_assert(!CCR_FIELD_SET(ENV, CCR_GIE)); \ + } \ + break; \ + } \ + } while (0) +#endif + +#define fREAD_ELR() (env->t_sreg[HEX_SREG_ELR]) + +#define fLOAD_PHYS(NUM, SIZE, SIGN, SRC1, SRC2, DST) { \ + const uintptr_t rs = ((unsigned long)(unsigned)(SRC1)) & 0x7ff; \ + const uintptr_t rt = ((unsigned long)(unsigned)(SRC2)) << 11; \ + const uintptr_t addr = rs + rt; \ + physical_memory_read(addr, &DST, sizeof(uint32_t)); \ +} + +#define fPOW2_HELP_ROUNDUP(VAL) \ + ((VAL) | \ + ((VAL) >> 1) | \ + ((VAL) >> 2) | \ + ((VAL) >> 4) | \ + ((VAL) >> 8) | \ + ((VAL) >> 16)) +#define fPOW2_ROUNDUP(VAL) (fPOW2_HELP_ROUNDUP((VAL) - 1) + 1) + +#define fTRAP(TRAPTYPE, IMM) \ + register_trap_exception(env, TRAPTYPE, IMM, PC) + +#ifdef QEMU_GENERATE +#define fFRAMECHECK(ADDR, EA) gen_framecheck(ctx, ADDR, EA) +#endif + +#define fVIRTINSN_SPSWAP(IMM, REG) +#define fVIRTINSN_GETIE(IMM, REG) { REG = 0xdeafbeef; } +#define fVIRTINSN_SETIE(IMM, REG) +#define fVIRTINSN_RTE(IMM, REG) +#define fGRE_ENABLED() \ + GET_FIELD(CCR_GRE, env->t_sreg[HEX_SREG_CCR]) +#define fTRAP1_VIRTINSN(IMM) \ + (fGRE_ENABLED() && \ + (((IMM) == 1) || ((IMM) == 3) || ((IMM) == 4) || ((IMM) == 6))) + +/* Not modeled in qemu */ + +#define MARK_LATE_PRED_WRITE(RNUM) +#define fICINVIDX(REG) +#define fICKILL() +#define fDCKILL() +#define fL2KILL() +#define fL2UNLOCK() +#define fL2CLEAN() +#define fL2CLEANINV() +#define fL2CLEANPA(REG) +#define fL2CLEANINVPA(REG) +#define fL2CLEANINVIDX(REG) +#define fL2CLEANIDX(REG) +#define fL2INVIDX(REG) +#define fL2TAGR(INDEX, DST, DSTREG) +#define fL2UNLOCKA(VA) ((void) VA) +#define fL2TAGW(INDEX, PART2) +#define fDCCLEANIDX(REG) +#define fDCCLEANINVIDX(REG) + +/* Always succeed: */ +#define fL2LOCKA(EA, PDV, PDN) ((void) EA, PDV = 0xFF) +#define fCLEAR_RTE_EX() \ + do { \ + uint32_t tmp = env->t_sreg[HEX_SREG_SSR]; \ + fINSERT_BITS(tmp, reg_field_info[SSR_EX].width, \ + reg_field_info[SSR_EX].offset, 0); \ + log_sreg_write(env, HEX_SREG_SSR, tmp, slot); \ + } while (0) + +#define fDCINVIDX(REG) +#define fDCINVA(REG) do { REG = REG; } while (0) /* Nothing to do in qemu */ + +#define fSET_TLB_LOCK() hex_tlb_lock(env); +#define fCLEAR_TLB_LOCK() hex_tlb_unlock(env); + +#define fSET_K0_LOCK() hex_k0_lock(env); +#define fCLEAR_K0_LOCK() hex_k0_unlock(env); + +#define fTLB_IDXMASK(INDEX) \ + ((INDEX) & (fPOW2_ROUNDUP( \ + fCAST4u(hexagon_tlb_get_num_entries(env_archcpu(env)->tlb))) - 1)) + +#define fTLB_NONPOW2WRAP(INDEX) \ + (((INDEX) >= hexagon_tlb_get_num_entries(env_archcpu(env)->tlb)) ? \ + ((INDEX) - hexagon_tlb_get_num_entries(env_archcpu(env)->tlb)) : \ + (INDEX)) + + +#define fTLBW(INDEX, VALUE) \ + hex_tlbw(env, (INDEX), (VALUE)) +#define fTLBW_EXTENDED(INDEX, VALUE) \ + hex_tlbw(env, (INDEX), (VALUE)) +#define fTLB_ENTRY_OVERLAP(VALUE) \ + (hex_tlb_check_overlap(env, VALUE, -1) != -2) +#define fTLB_ENTRY_OVERLAP_IDX(VALUE) \ + hex_tlb_check_overlap(env, VALUE, -1) +#define fTLBR(INDEX) \ + hexagon_tlb_read(env_archcpu(env)->tlb, \ + fTLB_NONPOW2WRAP(fTLB_IDXMASK(INDEX))) +#define fTLBR_EXTENDED(INDEX) \ + hexagon_tlb_read(env_archcpu(env)->tlb, \ + fTLB_NONPOW2WRAP(fTLB_IDXMASK(INDEX))) +#define fTLBP(TLBHI) \ + hex_tlb_lookup(env, ((TLBHI) >> 12), ((TLBHI) << 12)) +#define iic_flush_cache(p) + +#define fIN_DEBUG_MODE(TNUM) ({ \ + HexagonCPU *_cpu = env_archcpu(env); \ + uint32_t _isdbst = _cpu->globalregs ? \ + hexagon_globalreg_read(_cpu->globalregs, \ + HEX_SREG_ISDBST, env->threadId) : 0; \ + (GET_FIELD(ISDBST_DEBUGMODE, _isdbst) \ + & (0x1 << (TNUM))) != 0; }) + +#define fIN_DEBUG_MODE_NO_ISDB(TNUM) false +#define fIN_DEBUG_MODE_WARN(TNUM) false + +#ifdef QEMU_GENERATE + +/* + * Read tags back as zero for now: + * + * tag value in RD[31:10] for 32k, RD[31:9] for 16k + */ +#define fICTAGR(RS, RD, RD2) \ + do { \ + RD = ctx->zero; \ + } while (0) +#define fICTAGW(RS, RD) +#define fICDATAR(RS, RD) \ + do { \ + RD = ctx->zero; \ + } while (0) +#define fICDATAW(RS, RD) + +#define fDCTAGW(RS, RT) +/* tag: RD[23:0], state: RD[30:29] */ +#define fDCTAGR(INDEX, DST, DST_REG_NUM) \ + do { \ + DST = ctx->zero; \ + } while (0) +#else + +/* + * Read tags back as zero for now: + * + * tag value in RD[31:10] for 32k, RD[31:9] for 16k + */ +#define fICTAGR(RS, RD, RD2) \ + do { \ + RD = 0x00; \ + } while (0) +#define fICTAGW(RS, RD) +#define fICDATAR(RS, RD) \ + do { \ + RD = 0x00; \ + } while (0) +#define fICDATAW(RS, RD) + +#define fDCTAGW(RS, RT) +/* tag: RD[23:0], state: RD[30:29] */ +#define fDCTAGR(INDEX, DST, DST_REG_NUM) \ + do { \ + DST = 0; \ + } while (0) +#endif + +#else +#define ASSERT_DIRECT_TO_GUEST_UNSET(ENV, EXCP) do { } while (0) +#endif + +#define NUM_TLB_REGS(x) (hexagon_tlb_get_num_entries(env_archcpu(env)->tlb)) + +/* NMI routing not yet implemented; Y4_nmi is a no-op for now */ +#define fDO_NMI(THREAD_MASK) do { } while (0) + +#endif
diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 0b9337d..7723591 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c
@@ -32,6 +32,7 @@ #include "translate.h" #include "genptr.h" #include "printinsn.h" +#include "exec/target_page.h" #define HELPER_H "helper.h" #include "exec/helper-info.c.inc" @@ -61,10 +62,19 @@ TCGv hex_llsc_addr; TCGv hex_llsc_val; TCGv_i64 hex_llsc_val_i64; +#ifndef CONFIG_USER_ONLY +TCGv_i64 hex_cycle_count; +#endif TCGv hex_vstore_addr[VSTORES_MAX]; TCGv hex_vstore_size[VSTORES_MAX]; TCGv hex_vstore_pending[VSTORES_MAX]; +#ifndef CONFIG_USER_ONLY +TCGv_i32 hex_greg[NUM_GREGS]; +TCGv_i32 hex_t_sreg[NUM_SREGS]; +TCGv_i32 hex_cause_code; +#endif + static const char * const hexagon_prednames[] = { "p0", "p1", "p2", "p3" }; @@ -117,11 +127,28 @@ return offset; } -static void gen_exception_raw(int excp) +static void gen_exception(int excp, uint32_t PC) { - gen_helper_raise_exception(tcg_env, tcg_constant_i32(excp)); + gen_helper_raise_exception(tcg_env, tcg_constant_i32(excp), + tcg_constant_i32(PC)); } +#ifndef CONFIG_USER_ONLY +static inline void gen_precise_exception(int excp, uint32_t PC) +{ + tcg_gen_movi_i32(hex_cause_code, excp); + gen_exception(HEX_EVENT_PRECISE, PC); +} + +static void gen_pcycle_counters(DisasContext *ctx) +{ + if (ctx->pcycle_enabled) { + tcg_gen_addi_i64(hex_cycle_count, hex_cycle_count, ctx->num_cycles); + } +} +#endif + + static void gen_exec_counters(DisasContext *ctx) { tcg_gen_addi_tl(hex_gpr[HEX_REG_QEMU_PKT_CNT], @@ -130,6 +157,9 @@ hex_gpr[HEX_REG_QEMU_INSN_CNT], ctx->num_insns); tcg_gen_addi_tl(hex_gpr[HEX_REG_QEMU_HVX_CNT], hex_gpr[HEX_REG_QEMU_HVX_CNT], ctx->num_hvx_insns); +#ifndef CONFIG_USER_ONLY + gen_pcycle_counters(ctx); +#endif } static bool use_goto_tb(DisasContext *ctx, target_ulong dest) @@ -187,11 +217,14 @@ ctx->base.is_jmp = DISAS_NORETURN; } -static void gen_exception_end_tb(DisasContext *ctx, int excp) +void hex_gen_exception_end_tb(DisasContext *ctx, int excp) { gen_exec_counters(ctx); - tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], ctx->next_PC); - gen_exception_raw(excp); +#ifdef CONFIG_USER_ONLY + gen_exception(excp, ctx->pkt.pc); +#else + gen_precise_exception(excp, ctx->pkt.pc); +#endif ctx->base.is_jmp = DISAS_NORETURN; } @@ -205,7 +238,7 @@ gen_exec_counters(ctx); tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], fail_pc); - gen_exception_raw(excp); + gen_exception(excp, fail_pc); ctx->base.is_jmp = DISAS_NORETURN; ctx->base.pc_next = fail_pc; } @@ -249,6 +282,16 @@ return false; } +static bool check_for_opcode(Packet *pkt, uint16_t opcode) +{ + for (int i = 0; i < pkt->num_insns; i++) { + if (pkt->insn[i].opcode == opcode) { + return true; + } + } + return false; +} + static bool need_slot_cancelled(Packet *pkt) { /* We only need slot_cancelled for conditional store instructions */ @@ -262,6 +305,90 @@ return false; } +#ifndef CONFIG_USER_ONLY +static bool sreg_write_ends_tb(int reg_num) +{ + return reg_num == HEX_SREG_SSR || + reg_num == HEX_SREG_STID || + reg_num == HEX_SREG_IMASK || + reg_num == HEX_SREG_IPENDAD || + reg_num == HEX_SREG_BESTWAIT || + reg_num == HEX_SREG_SCHEDCFG; +} + +static bool has_sreg_write_ends_tb(Packet const *pkt) +{ + for (int i = 0; i < pkt->num_insns; i++) { + Insn const *insn = &pkt->insn[i]; + uint16_t opcode = insn->opcode; + if (opcode == Y2_tfrsrcr) { + /* Write to a single sreg */ + int reg_num = insn->regno[0]; + if (sreg_write_ends_tb(reg_num)) { + return true; + } + } else if (opcode == Y4_tfrspcp) { + /* Write to a sreg pair */ + int reg_num = insn->regno[0]; + if (sreg_write_ends_tb(reg_num)) { + return true; + } + if (sreg_write_ends_tb(reg_num + 1)) { + return true; + } + } + } + return false; +} +#endif + +static bool pkt_ends_tb(Packet *pkt) +{ + if (pkt->pkt_has_cof) { + return true; + } +#ifndef CONFIG_USER_ONLY + /* System mode instructions that end TLB */ + if (check_for_opcode(pkt, Y2_swi) || + check_for_opcode(pkt, Y2_cswi) || + check_for_opcode(pkt, Y2_ciad) || + check_for_opcode(pkt, Y4_siad) || + check_for_opcode(pkt, Y2_wait) || + check_for_opcode(pkt, Y2_resume) || + check_for_opcode(pkt, Y2_iassignw) || + check_for_opcode(pkt, Y2_setimask) || + check_for_opcode(pkt, Y4_nmi) || + check_for_opcode(pkt, Y2_setprio) || + check_for_opcode(pkt, Y2_start) || + check_for_opcode(pkt, Y2_stop) || + check_for_opcode(pkt, Y2_k0lock) || + check_for_opcode(pkt, Y2_k0unlock) || + check_for_opcode(pkt, Y2_tlblock) || + check_for_opcode(pkt, Y2_tlbunlock) || + check_for_opcode(pkt, Y2_break) || + check_for_opcode(pkt, Y2_isync) || + check_for_opcode(pkt, Y2_syncht) || + check_for_opcode(pkt, Y2_tlbp) || + check_for_opcode(pkt, Y2_tlbw) || + check_for_opcode(pkt, Y5_ctlbw) || + check_for_opcode(pkt, Y5_tlbasidi)) { + return true; + } + + /* + * Check for sreg writes that would end the TB + */ + if (check_for_attrib(pkt, A_IMPLICIT_WRITES_SSR)) { + return true; + } + if (has_sreg_write_ends_tb(pkt)) { + return true; + } +#endif + return false; +} + + static bool need_next_PC(DisasContext *ctx) { /* Check for conditional control flow or HW loop end */ @@ -309,6 +436,16 @@ } } +#ifndef CONFIG_USER_ONLY +static void mark_implicit_sreg_write(DisasContext *ctx, int attrib, int snum) +{ + uint16_t opcode = ctx->insn->opcode; + if (GET_ATTRIB(opcode, attrib)) { + ctx_log_sreg_write(ctx, snum); + } +} +#endif + static void mark_implicit_reg_writes(DisasContext *ctx) { mark_implicit_reg_write(ctx, A_IMPLICIT_WRITES_FP, HEX_REG_FP); @@ -321,6 +458,12 @@ mark_implicit_usr_write(ctx, A_IMPLICIT_WRITES_USR); mark_implicit_usr_write(ctx, A_FPOP); + +#ifndef CONFIG_USER_ONLY + mark_implicit_sreg_write(ctx, A_IMPLICIT_WRITES_SGP0, HEX_SREG_SGP0); + mark_implicit_sreg_write(ctx, A_IMPLICIT_WRITES_SGP1, HEX_SREG_SGP1); + mark_implicit_sreg_write(ctx, A_IMPLICIT_WRITES_SSR, HEX_SREG_SSR); +#endif } static void mark_implicit_pred_write(DisasContext *ctx, int attrib, int pnum) @@ -418,7 +561,11 @@ static void gen_start_packet(DisasContext *ctx) { - target_ulong next_PC = ctx->base.pc_next + ctx->pkt.encod_pkt_size_in_bytes; + Packet *pkt = &ctx->pkt; + target_ulong next_PC = (check_for_opcode(pkt, Y2_k0lock) || + check_for_opcode(pkt, Y2_tlblock)) ? + ctx->base.pc_next : + ctx->base.pc_next + pkt->encod_pkt_size_in_bytes; int i; /* Clear out the disassembly context */ @@ -426,6 +573,10 @@ ctx->reg_log_idx = 0; bitmap_zero(ctx->regs_written, TOTAL_PER_THREAD_REGS); bitmap_zero(ctx->predicated_regs, TOTAL_PER_THREAD_REGS); +#ifndef CONFIG_USER_ONLY + ctx->greg_log_idx = 0; + ctx->sreg_log_idx = 0; +#endif ctx->preg_log_idx = 0; bitmap_zero(ctx->pregs_written, NUM_PREGS); ctx->future_vregs_idx = 0; @@ -458,6 +609,25 @@ * gen phase, so clear it again. */ bitmap_zero(ctx->pregs_written, NUM_PREGS); +#ifndef CONFIG_USER_ONLY + for (i = 0; i < HEX_SREG_GLB_START; i++) { + ctx->t_sreg_new_value[i] = NULL; + } + for (i = 0; i < ctx->sreg_log_idx; i++) { + int reg_num = ctx->sreg_log[i]; + if (reg_num < HEX_SREG_GLB_START && + (ctx->need_commit || reg_num == HEX_SREG_SSR)) { + ctx->t_sreg_new_value[reg_num] = tcg_temp_new(); + } + } + for (i = 0; i < NUM_GREGS; i++) { + ctx->greg_new_value[i] = NULL; + } + for (i = 0; i < ctx->greg_log_idx; i++) { + int reg_num = ctx->greg_log[i]; + ctx->greg_new_value[reg_num] = tcg_temp_new(); + } +#endif /* Initialize the runtime state for packet semantics */ if (need_slot_cancelled(&ctx->pkt)) { @@ -580,7 +750,7 @@ ctx->insn->generate(ctx); mark_store_width(ctx); } else { - gen_exception_end_tb(ctx, HEX_CAUSE_INVALID_OPCODE); + hex_gen_exception_end_tb(ctx, HEX_CAUSE_INVALID_OPCODE); } } @@ -615,6 +785,50 @@ } } +#ifndef CONFIG_USER_ONLY +static void gen_greg_writes(DisasContext *ctx) +{ + int i; + + for (i = 0; i < ctx->greg_log_idx; i++) { + int reg_num = ctx->greg_log[i]; + + tcg_gen_mov_tl(hex_greg[reg_num], ctx->greg_new_value[reg_num]); + } +} + + +static void gen_sreg_writes(DisasContext *ctx) +{ + int i; + + TCGv_i32 old_reg = tcg_temp_new_i32(); + for (i = 0; i < ctx->sreg_log_idx; i++) { + int reg_num = ctx->sreg_log[i]; + + if (reg_num == HEX_SREG_SSR) { + tcg_gen_mov_tl(old_reg, hex_t_sreg[reg_num]); + tcg_gen_mov_tl(hex_t_sreg[reg_num], ctx->t_sreg_new_value[reg_num]); + gen_helper_modify_ssr(tcg_env, ctx->t_sreg_new_value[reg_num], + old_reg); + } else if ((reg_num == HEX_SREG_STID) || + (reg_num == HEX_SREG_IMASK) || + (reg_num == HEX_SREG_IPENDAD)) { + if (ctx->need_commit && reg_num < HEX_SREG_GLB_START) { + tcg_gen_mov_tl(hex_t_sreg[reg_num], + ctx->t_sreg_new_value[reg_num]); + } + gen_helper_pending_interrupt(tcg_env); + } else if ((reg_num == HEX_SREG_BESTWAIT) || + (reg_num == HEX_SREG_SCHEDCFG)) { + gen_helper_resched(tcg_env); + } else if (ctx->need_commit && reg_num < HEX_SREG_GLB_START) { + tcg_gen_mov_tl(hex_t_sreg[reg_num], ctx->t_sreg_new_value[reg_num]); + } + } +} +#endif + static void gen_pred_writes(DisasContext *ctx) { /* Early exit if not needed or the log is empty */ @@ -804,6 +1018,8 @@ } } +#define PCYCLES_PER_PACKET 1 + static void update_exec_counters(DisasContext *ctx) { int num_real_insns = 0; @@ -823,6 +1039,7 @@ ctx->num_packets++; ctx->num_insns += num_real_insns; ctx->num_hvx_insns += num_hvx_insns; + ctx->num_cycles += PCYCLES_PER_PACKET; } static void gen_commit_packet(DisasContext *ctx) @@ -908,6 +1125,10 @@ process_store_log(ctx); gen_reg_writes(ctx); +#ifndef CONFIG_USER_ONLY + gen_greg_writes(ctx); + gen_sreg_writes(ctx); +#endif gen_pred_writes(ctx); if (ctx->pkt.pkt_has_hvx) { gen_commit_hvx(ctx); @@ -920,7 +1141,7 @@ ctx->pkt.vhist_insn->generate(ctx); } - if (ctx->pkt.pkt_has_cof) { + if (pkt_ends_tb(&ctx->pkt) || ctx->base.is_jmp == DISAS_NORETURN) { gen_end_tb(ctx); } } @@ -964,7 +1185,7 @@ HexagonCPU *hex_cpu = env_archcpu(cpu_env(cs)); uint32_t hex_flags = dcbase->tb->flags; - ctx->mem_idx = MMU_USER_IDX; + ctx->mem_idx = FIELD_EX32(hex_flags, TB_FLAGS, MMU_INDEX); ctx->num_packets = 0; ctx->num_insns = 0; ctx->num_hvx_insns = 0; @@ -972,6 +1193,10 @@ ctx->is_tight_loop = FIELD_EX32(hex_flags, TB_FLAGS, IS_TIGHT_LOOP); ctx->short_circuit = hex_cpu->short_circuit; ctx->hex_def = HEXAGON_CPU_GET_CLASS(hex_cpu)->hex_def; +#ifndef CONFIG_USER_ONLY + ctx->num_cycles = 0; + ctx->pcycle_enabled = FIELD_EX32(hex_flags, TB_FLAGS, PCYCLE_ENABLED); +#endif } static void hexagon_tr_tb_start(DisasContextBase *db, CPUState *cpu) @@ -1080,6 +1305,20 @@ opcode_init(); +#ifndef CONFIG_USER_ONLY + for (i = 0; i < NUM_GREGS; i++) { + hex_greg[i] = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUHexagonState, greg[i]), + hexagon_gregnames[i]); + } + for (i = 0; i < NUM_SREGS; i++) { + if (i < HEX_SREG_GLB_START) { + hex_t_sreg[i] = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUHexagonState, t_sreg[i]), + hexagon_sregnames[i]); + } + } +#endif for (i = 0; i < TOTAL_PER_THREAD_REGS; i++) { hex_gpr[i] = tcg_global_mem_new(tcg_env, offsetof(CPUHexagonState, gpr[i]), @@ -1101,6 +1340,12 @@ offsetof(CPUHexagonState, llsc_val), "llsc_val"); hex_llsc_val_i64 = tcg_global_mem_new_i64(tcg_env, offsetof(CPUHexagonState, llsc_val_i64), "llsc_val_i64"); +#ifndef CONFIG_USER_ONLY + hex_cause_code = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUHexagonState, cause_code), "cause_code"); + hex_cycle_count = tcg_global_mem_new_i64(tcg_env, + offsetof(CPUHexagonState, t_cycle_count), "t_cycle_count"); +#endif for (i = 0; i < STORES_MAX; i++) { snprintf(store_addr_names[i], NAME_LEN, "store_addr_%d", i); hex_store_addr[i] = tcg_global_mem_new(tcg_env,
diff --git a/target/hexagon/translate.h b/target/hexagon/translate.h index 1fc185e..0a7f37d 100644 --- a/target/hexagon/translate.h +++ b/target/hexagon/translate.h
@@ -41,6 +41,14 @@ DECLARE_BITMAP(regs_written, TOTAL_PER_THREAD_REGS); DECLARE_BITMAP(predicated_regs, TOTAL_PER_THREAD_REGS); bool implicit_usr_write; +#ifndef CONFIG_USER_ONLY + int greg_log[GREG_WRITES_MAX]; + int greg_log_idx; + int sreg_log[SREG_WRITES_MAX]; + int sreg_log_idx; + TCGv_i32 t_sreg_new_value[HEX_SREG_GLB_START]; + TCGv_i32 greg_new_value[NUM_GREGS]; +#endif int preg_log[PRED_WRITES_MAX]; int preg_log_idx; DECLARE_BITMAP(pregs_written, NUM_PREGS); @@ -77,10 +85,40 @@ TCGv new_pred_value[NUM_PREGS]; TCGv branch_taken; TCGv dczero_addr; + bool pcycle_enabled; + uint32_t num_cycles; } DisasContext; bool is_gather_store_insn(DisasContext *ctx); +#ifndef CONFIG_USER_ONLY +static inline void ctx_log_greg_write(DisasContext *ctx, int rnum) +{ + assert(rnum <= HEX_GREG_G3); + ctx->greg_log[ctx->greg_log_idx] = rnum; + ctx->greg_log_idx++; +} + +static inline void ctx_log_greg_write_pair(DisasContext *ctx, int rnum) +{ + assert(!(rnum % 2)); + ctx_log_greg_write(ctx, rnum); + ctx_log_greg_write(ctx, rnum + 1); +} + +static inline void ctx_log_sreg_write(DisasContext *ctx, int rnum) +{ + ctx->sreg_log[ctx->sreg_log_idx] = rnum; + ctx->sreg_log_idx++; +} + +static inline void ctx_log_sreg_write_pair(DisasContext *ctx, int rnum) +{ + ctx_log_sreg_write(ctx, rnum); + ctx_log_sreg_write(ctx, rnum + 1); +} +#endif + static inline void ctx_log_pred_write(DisasContext *ctx, int pnum) { if (!test_bit(pnum, ctx->pregs_written)) { @@ -282,6 +320,13 @@ extern TCGv hex_vstore_addr[VSTORES_MAX]; extern TCGv hex_vstore_size[VSTORES_MAX]; extern TCGv hex_vstore_pending[VSTORES_MAX]; +#ifndef CONFIG_USER_ONLY +extern TCGv_i32 hex_greg[NUM_GREGS]; +extern TCGv_i32 hex_t_sreg[NUM_SREGS]; +#endif + + +void hex_gen_exception_end_tb(DisasContext *ctx, int excp); void process_store(DisasContext *ctx, int slot_num); @@ -295,4 +340,6 @@ FIELD(PROBE_PKT_SCALAR_HVX_STORES, S1_IS_PRED, 4, 1) FIELD(PROBE_PKT_SCALAR_HVX_STORES, MMU_IDX, 5, 2) +void gen_framecheck(DisasContext *ctx, TCGv_i32 addr, TCGv_i32 ea); + #endif
diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 8929a75..5805d33 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c
@@ -26,6 +26,7 @@ #include "tcg/helper-tcg.h" #include "exec/translation-block.h" #include "system/hvf.h" +#include "system/mshv.h" #include "system/whpx.h" #include "whpx/whpx-i386.h" #include "hvf/hvf-i386.h" @@ -8233,6 +8234,13 @@ r = hvf_get_supported_cpuid(wi->cpuid.eax, wi->cpuid.ecx, wi->cpuid.reg); + } else if (mshv_enabled()) { + if (wi->type != CPUID_FEATURE_WORD) { + return 0; + } + r = mshv_get_supported_cpuid(wi->cpuid.eax, + wi->cpuid.ecx, + wi->cpuid.reg); } else if (whpx_enabled()) { switch (wi->type) { case CPUID_FEATURE_WORD:
diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 67e2ecf..e6a1976 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h
@@ -1983,6 +1983,9 @@ CPUCacheInfo *l3_cache; } CPUCaches; +struct kvm_msrs; +struct hv_vp_register_page; + typedef struct CPUArchState { /* standard registers */ target_ulong regs[CPU_NB_EREGS]; @@ -2289,6 +2292,10 @@ QEMUTimer *xen_periodic_timer; QemuMutex xen_timers_lock; #endif +#if defined(CONFIG_MSHV) + /* Shared register page */ + struct hv_vp_register_page *regs_page; +#endif #if defined(CONFIG_HVF) || defined(CONFIG_MSHV) || defined(CONFIG_WHPX) void *emu_mmio_buf; #endif @@ -2315,8 +2322,6 @@ DECLARE_BITMAP(avail_cpu_topo, CPU_TOPOLOGY_LEVEL__MAX); } CPUX86State; -struct kvm_msrs; - /** * X86CPU: * @env: #CPUX86State
diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 9e35288..64cc421 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c
@@ -32,6 +32,7 @@ #include "vmsr_energy.h" #include "system/system.h" #include "system/hw_accel.h" +#include "system/accel-irq.h" #include "system/kvm_int.h" #include "system/runstate.h" #include "system/ramblock.h" @@ -6678,7 +6679,7 @@ kvm_gsi_routing_allowed = true; if (kvm_irqchip_is_split()) { - KVMRouteChange c = kvm_irqchip_begin_route_changes(s); + AccelRouteChange c = accel_irqchip_begin_route_changes(); int i; /* If the ioapic is in QEMU and the lapics are in KVM, reserve @@ -6689,7 +6690,7 @@ exit(1); } } - kvm_irqchip_commit_route_changes(&c); + accel_irqchip_commit_route_changes(&c); } }
diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index dfad469..c9c3b05 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c
@@ -560,19 +560,29 @@ } TdxXFAMDep; /* - * Note, only the CPUID bits whose virtualization type are "XFAM & Native" are - * defiend here. + * Note, usually the CPUID bits whose virtualization type are "XFAM & Native" + * are defined here while "XFAM & Configured & Native" are not. Because the + * latter are reported as configurable bits by KVM when they are supported. + * And they are not supported when not in the configurable bits list from KVM + * even if the corresponding XFAM bit is supported. * - * For those whose virtualization type are "XFAM & Configured & Native", they - * are reported as configurable bits. And they are not supported if not in the - * configureable bits list from KVM even if the corresponding XFAM bit is - * supported. + * Special cases: + * + * - AMX alias bits, their type is "CPUID_Enabled & Native" which means their + * value is determined by the CPUID bit they are aliased to. + * + * - AVX10_VL_MASK, their type is "XFAM & CPUID_Enabled & Native" which means + * their value is determined by both the corresponding XFAM bit and CPUID bit. + * + * For simplicity, relax the dependency to related XFAM bit. + * tdx_check_features() will eventually catch the unsupported configurations. */ TdxXFAMDep tdx_xfam_deps[] = { { XSTATE_YMM_BIT, { FEAT_1_ECX, CPUID_EXT_FMA } }, { XSTATE_YMM_BIT, { FEAT_7_0_EBX, CPUID_7_0_EBX_AVX2 } }, { XSTATE_OPMASK_BIT, { FEAT_7_0_ECX, CPUID_7_0_ECX_AVX512_VBMI } }, { XSTATE_OPMASK_BIT, { FEAT_7_0_EDX, CPUID_7_0_EDX_AVX512_FP16 } }, + { XSTATE_OPMASK_BIT, { FEAT_24_0_EBX, CPUID_24_0_EBX_AVX10_VL_MASK } }, { XSTATE_PT_BIT, { FEAT_7_0_EBX, CPUID_7_0_EBX_INTEL_PT } }, { XSTATE_PKRU_BIT, { FEAT_7_0_ECX, CPUID_7_0_ECX_PKU } }, { XSTATE_CET_U_BIT, { FEAT_7_0_ECX, CPUID_7_0_ECX_CET_SHSTK } }, @@ -580,6 +590,10 @@ { XSTATE_XTILE_CFG_BIT, { FEAT_7_0_EDX, CPUID_7_0_EDX_AMX_BF16 } }, { XSTATE_XTILE_CFG_BIT, { FEAT_7_0_EDX, CPUID_7_0_EDX_AMX_TILE } }, { XSTATE_XTILE_CFG_BIT, { FEAT_7_0_EDX, CPUID_7_0_EDX_AMX_INT8 } }, + { XSTATE_XTILE_CFG_BIT, { FEAT_1E_1_EAX, CPUID_1E_1_EAX_AMX_INT8_ALIAS } }, + { XSTATE_XTILE_CFG_BIT, { FEAT_1E_1_EAX, CPUID_1E_1_EAX_AMX_BF16_ALIAS } }, + { XSTATE_XTILE_CFG_BIT, { FEAT_1E_1_EAX, CPUID_1E_1_EAX_AMX_COMPLEX_ALIAS } }, + { XSTATE_XTILE_CFG_BIT, { FEAT_1E_1_EAX, CPUID_1E_1_EAX_AMX_FP16_ALIAS } }, }; static struct kvm_cpuid_entry2 *find_in_supported_entry(uint32_t function, @@ -1403,7 +1417,7 @@ uint64_t reg_mask = run->system_event.data[R_ECX]; char *message = NULL; uint64_t *tmp; - uint64_t gpa = -1ull; + uint64_t gpa = 0; bool has_gpa = false; if (error_code & 0xffff) {
diff --git a/target/i386/meson.build b/target/i386/meson.build index 80062d1..b4fbf8a 100644 --- a/target/i386/meson.build +++ b/target/i386/meson.build
@@ -12,7 +12,7 @@ i386_ss.add(when: 'CONFIG_HVF', if_true: files('xsave_helper.c', 'host-cpu.c')) i386_ss.add(when: 'CONFIG_WHPX', if_true: files('xsave_helper.c', 'host-cpu.c')) i386_ss.add(when: 'CONFIG_NVMM', if_true: files('host-cpu.c')) -i386_ss.add(when: 'CONFIG_MSHV', if_true: files('host-cpu.c')) +i386_ss.add(when: 'CONFIG_MSHV', if_true: files('xsave_helper.c', 'host-cpu.c')) i386_system_ss = ss.source_set() i386_system_ss.add(files(
diff --git a/target/i386/mshv/meson.build b/target/i386/mshv/meson.build index 49f28d4..6091c21 100644 --- a/target/i386/mshv/meson.build +++ b/target/i386/mshv/meson.build
@@ -2,6 +2,7 @@ i386_mshv_ss.add(files( 'mshv-cpu.c', + 'msr.c', )) i386_system_ss.add_all(when: 'CONFIG_MSHV', if_true: i386_mshv_ss)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index 4ed6e75..126ca40 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c
@@ -24,10 +24,13 @@ #include "hw/i386/apic_internal.h" #include "cpu.h" +#include "host-cpu.h" #include "emulate/x86_decode.h" #include "emulate/x86_emu.h" #include "emulate/x86_flags.h" +#include "accel/accel-cpu-target.h" + #include "trace-accel_mshv.h" #include "trace.h" @@ -107,6 +110,175 @@ HV_X64_REGISTER_XMM_CONTROL_STATUS, }; +static int set_special_regs(const CPUState *cpu); + +static int get_xsave_state(CPUState *cpu) +{ + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + int cpu_fd = mshv_vcpufd(cpu); + int ret; + void *xsavec_buf; + const size_t page = HV_HYP_PAGE_SIZE; + size_t xsavec_buf_len = page; + + /* TODO: should properly determine xsavec size based on CPUID */ + xsavec_buf = qemu_memalign(page, xsavec_buf_len); + memset(xsavec_buf, 0, xsavec_buf_len); + + struct mshv_get_set_vp_state args = { + .type = MSHV_VP_STATE_XSAVE, + .buf_sz = xsavec_buf_len, + .buf_ptr = (uintptr_t)xsavec_buf, + }; + + ret = ioctl(cpu_fd, MSHV_GET_VP_STATE, &args); + if (ret < 0) { + error_report("failed to get xsave state: %s", strerror(errno)); + return -errno; + } + + ret = decompact_xsave_area(xsavec_buf, xsavec_buf_len, env); + g_free(xsavec_buf); + if (ret < 0) { + error_report("failed to decompact xsave area"); + return ret; + } + x86_cpu_xrstor_all_areas(x86cpu, env->xsave_buf, env->xsave_buf_len); + + return 0; +} + +static int set_xsave_state(const CPUState *cpu) +{ + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + int cpu_fd = mshv_vcpufd(cpu); + int ret; + void *xsavec_buf; + size_t page = HV_HYP_PAGE_SIZE, xsavec_buf_len; + + /* allocate and populate compacted buffer */ + xsavec_buf = qemu_memalign(page, page); + xsavec_buf_len = page; + + /* save registers to standard format buffer */ + x86_cpu_xsave_all_areas(x86cpu, env->xsave_buf, env->xsave_buf_len); + + /* store compacted version of xsave area in xsavec_buf */ + compact_xsave_area(env, xsavec_buf, xsavec_buf_len); + + struct mshv_get_set_vp_state args = { + .type = MSHV_VP_STATE_XSAVE, + .buf_sz = xsavec_buf_len, + .buf_ptr = (uintptr_t)xsavec_buf, + }; + + ret = ioctl(cpu_fd, MSHV_SET_VP_STATE, &args); + g_free(xsavec_buf); + if (ret < 0) { + error_report("failed to set xsave state: %s", strerror(errno)); + return -errno; + } + + return 0; +} + +static void populate_fpu(const hv_register_assoc *assocs, X86CPU *x86cpu) +{ + union hv_register_value value; + const union hv_x64_fp_control_status_register *ctrl_status; + const union hv_x64_xmm_control_status_register *xmm_ctrl; + CPUX86State *env = &x86cpu->env; + size_t i, fp_i; + bool valid; + + /* first 16 registers are xmm0-xmm15 */ + for (i = 0; i < 16; i++) { + value = assocs[i].value; + env->xmm_regs[i].ZMM_Q(0) = value.reg128.low_part; + env->xmm_regs[i].ZMM_Q(1) = value.reg128.high_part; + } + + /* next 8 registers are fp_mmx0-fp_mmx7 */ + for (i = 16; i < 24; i++) { + fp_i = i - 16; + value = assocs[i].value; + env->fpregs[fp_i].d.low = value.fp.mantissa; + env->fpregs[fp_i].d.high = (value.fp.sign << 15) + | (value.fp.biased_exponent & 0x7FFF); + } + + /* last two registers are fp_control_status and xmm_control_status */ + ctrl_status = &assocs[24].value.fp_control_status; + env->fpuc = ctrl_status->fp_control; + + env->fpus = ctrl_status->fp_status & ~0x3800; + /* bits 11,12,13 are the top of stack pointer */ + env->fpstt = (ctrl_status->fp_status >> 11) & 0x7; + + for (i = 0; i < 8; i++) { + valid = ctrl_status->fp_tag & (1 << i); + env->fptags[i] = valid ? 0 : 1; + } + + env->fpop = ctrl_status->last_fp_op; + env->fpip = ctrl_status->last_fp_rip; + + xmm_ctrl = &assocs[25].value.xmm_control_status; + env->mxcsr = xmm_ctrl->xmm_status_control; + env->fpdp = xmm_ctrl->last_fp_rdp; +} + +static int get_fpu(CPUState *cpu) +{ + struct hv_register_assoc assocs[ARRAY_SIZE(FPU_REGISTER_NAMES)]; + int ret; + X86CPU *x86cpu = X86_CPU(cpu); + size_t n_regs = ARRAY_SIZE(FPU_REGISTER_NAMES); + + for (size_t i = 0; i < n_regs; i++) { + assocs[i].name = FPU_REGISTER_NAMES[i]; + } + ret = mshv_get_generic_regs(cpu, assocs, n_regs); + if (ret < 0) { + error_report("failed to get special registers"); + return -errno; + } + + populate_fpu(assocs, x86cpu); + + return 0; +} + +static int get_xc_reg(CPUState *cpu) +{ + int ret; + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + struct hv_register_assoc assocs[1]; + + assocs[0].name = HV_X64_REGISTER_XFEM; + + ret = mshv_get_generic_regs(cpu, assocs, 1); + if (ret < 0) { + error_report("failed to get xcr0"); + return -1; + } + env->xcr0 = assocs[0].value.reg64; + + return 0; +} + +static enum hv_register_name NON_VP_PAGE_REGISTER_NAMES[6] = { + HV_X64_REGISTER_TR, + HV_X64_REGISTER_LDTR, + HV_X64_REGISTER_GDTR, + HV_X64_REGISTER_IDTR, + HV_X64_REGISTER_CR2, + HV_X64_REGISTER_APIC_BASE, +}; + static int translate_gva(const CPUState *cpu, uint64_t gva, uint64_t *gpa, uint64_t flags) { @@ -190,8 +362,8 @@ return 0; } -static int get_generic_regs(CPUState *cpu, hv_register_assoc *assocs, - size_t n_regs) +int mshv_get_generic_regs(CPUState *cpu, hv_register_assoc *assocs, + size_t n_regs) { int cpu_fd = mshv_vcpufd(cpu); int vp_index = cpu->cpu_index; @@ -285,14 +457,56 @@ return 0; } -int mshv_store_regs(CPUState *cpu) +static void mshv_set_standard_regs_vp_page(CPUState *cpu) { + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + + env->regs_page->rax = env->regs[R_EAX]; + env->regs_page->rbx = env->regs[R_EBX]; + env->regs_page->rcx = env->regs[R_ECX]; + env->regs_page->rdx = env->regs[R_EDX]; + env->regs_page->rsi = env->regs[R_ESI]; + env->regs_page->rdi = env->regs[R_EDI]; + env->regs_page->rsp = env->regs[R_ESP]; + env->regs_page->rbp = env->regs[R_EBP]; + env->regs_page->r8 = env->regs[R_R8]; + env->regs_page->r9 = env->regs[R_R9]; + env->regs_page->r10 = env->regs[R_R10]; + env->regs_page->r11 = env->regs[R_R11]; + env->regs_page->r12 = env->regs[R_R12]; + env->regs_page->r13 = env->regs[R_R13]; + env->regs_page->r14 = env->regs[R_R14]; + env->regs_page->r15 = env->regs[R_R15]; + env->regs_page->rip = env->eip; + lflags_to_rflags(env); + env->regs_page->rflags = env->eflags; + + env->regs_page->dirty |= (1u << HV_X64_REGISTER_CLASS_GENERAL) + | (1u << HV_X64_REGISTER_CLASS_IP) + | (1u << HV_X64_REGISTER_CLASS_FLAGS); +} + +static int store_regs(CPUState *cpu) +{ + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; int ret; - ret = set_standard_regs(cpu); + /* Use register vp page to optimize registers access */ + if (env->regs_page && env->regs_page->isvalid != 0) { + mshv_set_standard_regs_vp_page(cpu); + } else { + ret = set_standard_regs(cpu); + if (ret < 0) { + return ret; + } + } + + ret = set_special_regs(cpu); if (ret < 0) { - error_report("Failed to store standard registers"); - return -1; + error_report("Failed to store speical registers"); + return ret; } return 0; @@ -323,7 +537,7 @@ rflags_to_lflags(env); } -int mshv_get_standard_regs(CPUState *cpu) +static int get_standard_regs(CPUState *cpu) { struct hv_register_assoc assocs[ARRAY_SIZE(STANDARD_REGISTER_NAMES)]; int ret; @@ -334,7 +548,7 @@ for (size_t i = 0; i < n_regs; i++) { assocs[i].name = STANDARD_REGISTER_NAMES[i]; } - ret = get_generic_regs(cpu, assocs, n_regs); + ret = mshv_get_generic_regs(cpu, assocs, n_regs); if (ret < 0) { error_report("failed to get standard registers"); return -1; @@ -401,8 +615,107 @@ cpu_set_apic_base(x86cpu->apic_state, assocs[16].value.reg64); } +static void mshv_get_standard_regs_vp_page(CPUState *cpu) +{ + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; -int mshv_get_special_regs(CPUState *cpu) + /* General Purpose Registers */ + env->regs[R_EAX] = env->regs_page->rax; + env->regs[R_EBX] = env->regs_page->rbx; + env->regs[R_ECX] = env->regs_page->rcx; + env->regs[R_EDX] = env->regs_page->rdx; + env->regs[R_ESI] = env->regs_page->rsi; + env->regs[R_EDI] = env->regs_page->rdi; + env->regs[R_ESP] = env->regs_page->rsp; + env->regs[R_EBP] = env->regs_page->rbp; + env->regs[R_R8] = env->regs_page->r8; + env->regs[R_R9] = env->regs_page->r9; + env->regs[R_R10] = env->regs_page->r10; + env->regs[R_R11] = env->regs_page->r11; + env->regs[R_R12] = env->regs_page->r12; + env->regs[R_R13] = env->regs_page->r13; + env->regs[R_R14] = env->regs_page->r14; + env->regs[R_R15] = env->regs_page->r15; + + env->eip = env->regs_page->rip; + env->eflags = env->regs_page->rflags; + rflags_to_lflags(env); +} + +static int mshv_get_special_regs_vp_page(CPUState *cpu) +{ + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + struct hv_register_assoc assocs[ARRAY_SIZE(NON_VP_PAGE_REGISTER_NAMES)]; + int ret; + size_t n_regs = ARRAY_SIZE(NON_VP_PAGE_REGISTER_NAMES); + hv_x64_segment_register seg; + + /* Populate special registers that are in the VP register page */ + env->cr[0] = env->regs_page->cr0; + env->cr[3] = env->regs_page->cr3; + env->cr[4] = env->regs_page->cr4; + env->efer = env->regs_page->efer; + cpu_set_apic_tpr(x86cpu->apic_state, env->regs_page->cr8); + + /* Segment Registers - copy from packed struct to avoid unaligned access */ + memcpy(&seg, &env->regs_page->es, sizeof(hv_x64_segment_register)); + populate_segment_reg(&seg, &env->segs[R_ES]); + memcpy(&seg, &env->regs_page->cs, sizeof(hv_x64_segment_register)); + populate_segment_reg(&seg, &env->segs[R_CS]); + memcpy(&seg, &env->regs_page->ss, sizeof(hv_x64_segment_register)); + populate_segment_reg(&seg, &env->segs[R_SS]); + memcpy(&seg, &env->regs_page->ds, sizeof(hv_x64_segment_register)); + populate_segment_reg(&seg, &env->segs[R_DS]); + memcpy(&seg, &env->regs_page->fs, sizeof(hv_x64_segment_register)); + populate_segment_reg(&seg, &env->segs[R_FS]); + memcpy(&seg, &env->regs_page->gs, sizeof(hv_x64_segment_register)); + populate_segment_reg(&seg, &env->segs[R_GS]); + + /* The rest of the special registers that are not in the VP register page */ + for (size_t i = 0; i < n_regs; i++) { + assocs[i].name = NON_VP_PAGE_REGISTER_NAMES[i]; + } + + ret = mshv_get_generic_regs(cpu, assocs, n_regs); + if (ret < 0) { + error_report("failed to get non-vp-page special registers"); + return -1; + } + + /* Non-VP page registers - TR, LDTR, GDTR, IDTR, CR2, APIC_BASE */ + populate_segment_reg(&assocs[0].value.segment, &env->tr); + populate_segment_reg(&assocs[1].value.segment, &env->ldt); + + populate_table_reg(&assocs[2].value.table, &env->gdt); + populate_table_reg(&assocs[3].value.table, &env->idt); + env->cr[2] = assocs[4].value.reg64; + + cpu_set_apic_base(x86cpu->apic_state, assocs[5].value.reg64); + + return ret; +} + +static int mshv_get_registers_vp_page(CPUState *cpu) +{ + int ret; + + /* General Purpose Registers */ + mshv_get_standard_regs_vp_page(cpu); + + /* Special Registers - makes a hypercall */ + ret = mshv_get_special_regs_vp_page(cpu); + if (ret < 0) { + error_report("failed to get special registers for vp page"); + return -1; + } + + return 0; +} + + +static int get_special_regs(CPUState *cpu) { struct hv_register_assoc assocs[ARRAY_SIZE(SPECIAL_REGISTER_NAMES)]; int ret; @@ -412,7 +725,7 @@ for (size_t i = 0; i < n_regs; i++) { assocs[i].name = SPECIAL_REGISTER_NAMES[i]; } - ret = get_generic_regs(cpu, assocs, n_regs); + ret = mshv_get_generic_regs(cpu, assocs, n_regs); if (ret < 0) { error_report("failed to get special registers"); return -errno; @@ -422,26 +735,245 @@ return 0; } -int mshv_load_regs(CPUState *cpu) +static int load_regs(CPUState *cpu) { + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; int ret; - ret = mshv_get_standard_regs(cpu); + /* Use register vp page to optimize registers access */ + if (env->regs_page && env->regs_page->isvalid != 0) { + ret = mshv_get_registers_vp_page(cpu); + return ret; + } + + ret = get_standard_regs(cpu); if (ret < 0) { - error_report("Failed to load standard registers"); + return ret; + } + + ret = get_special_regs(cpu); + if (ret < 0) { + return ret; + } + + return 0; +} + +static int get_vcpu_events(CPUState *cpu) +{ + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + struct hv_register_assoc assocs[] = { + { .name = HV_REGISTER_PENDING_INTERRUPTION }, + { .name = HV_REGISTER_INTERRUPT_STATE }, + { .name = HV_REGISTER_PENDING_EVENT0 }, + }; + union hv_x64_pending_interruption_register pending_int; + union hv_x64_interrupt_state_register int_state; + union hv_x64_pending_exception_event pending_exc; + int ret; + + ret = mshv_get_generic_regs(cpu, assocs, ARRAY_SIZE(assocs)); + if (ret < 0) { + error_report("failed to get vcpu event registers"); return -1; } - ret = mshv_get_special_regs(cpu); + pending_int.as_uint64 = assocs[0].value.reg64; + int_state.as_uint64 = assocs[1].value.reg64; + pending_exc = assocs[2].value.pending_exception_event; + + /* Clear previous state. injected ints/excs are blanked w/ -1 */ + env->interrupt_injected = -1; + env->soft_interrupt = 0; + env->exception_injected = 0; + env->exception_pending = 0; + env->exception_nr = -1; + env->has_error_code = 0; + env->error_code = 0; + env->exception_has_payload = 0; + env->exception_payload = 0; + env->nmi_injected = 0; + + if (pending_int.interruption_pending) { + switch (pending_int.interruption_type) { + case MSHV_HV_INTERRUPTION_TYPE_EXT_INT: + env->interrupt_injected = pending_int.interruption_vector; + break; + case MSHV_HV_INTERRUPTION_TYPE_NMI: + env->nmi_injected = 1; + break; + case MSHV_HV_INTERRUPTION_TYPE_HW_EXC: + env->exception_injected = 1; + env->exception_nr = pending_int.interruption_vector; + env->has_error_code = pending_int.deliver_error_code; + env->error_code = pending_int.error_code; + break; + case MSHV_HV_INTERRUPTION_TYPE_SW_INT: + env->interrupt_injected = pending_int.interruption_vector; + env->soft_interrupt = 1; + break; + case MSHV_HV_INTERRUPTION_TYPE_SW_EXC: + case MSHV_HV_INTERRUPTION_TYPE_PRIV_SW_EXC: + env->exception_injected = 1; + env->exception_nr = pending_int.interruption_vector; + env->has_error_code = pending_int.deliver_error_code; + env->error_code = pending_int.error_code; + break; + default: + error_report("unknown interruption type %u", + pending_int.interruption_type); + return -EINVAL; + } + } + + /* disabled for one instr after STI, MOV/POP SS, see hvf_store_events() */ + if (int_state.interrupt_shadow) { + env->hflags |= HF_INHIBIT_IRQ_MASK; + } else { + env->hflags &= ~HF_INHIBIT_IRQ_MASK; + } + + /* see kvm_get_vcpu_events(), hvf_store_events() */ + if (int_state.nmi_masked) { + env->hflags2 |= HF2_NMI_MASK; + } else { + env->hflags2 &= ~HF2_NMI_MASK; + } + + /* HV_REGISTER_PENDING_EVENT0: pending exception not yet injected */ + if (pending_exc.event_pending) { + env->exception_pending = 1; + env->exception_nr = pending_exc.vector; + env->has_error_code = pending_exc.deliver_error_code; + env->error_code = pending_exc.error_code; + env->exception_has_payload = (pending_exc.exception_parameter != 0); + env->exception_payload = pending_exc.exception_parameter; + } + + /* + * Ignoring HV_REGISTER_PENDING_EVENT1, virtualization fault events, MSHV + * does not support nested virtualization. + */ + + return 0; +} + +static int set_vcpu_events(const CPUState *cpu) +{ + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + union hv_x64_pending_interruption_register pending_int = { 0 }; + union hv_x64_interrupt_state_register int_state = { 0 }; + union hv_x64_pending_exception_event pending_exc = { 0 }; + struct hv_register_assoc assocs[3]; + int ret; + + /* build pending_int from CPUX86State */ + if (env->exception_injected) { + pending_int.interruption_pending = 1; + pending_int.interruption_type = MSHV_HV_INTERRUPTION_TYPE_HW_EXC; + pending_int.interruption_vector = env->exception_nr; + pending_int.deliver_error_code = env->has_error_code; + pending_int.error_code = env->error_code; + } else if (env->nmi_injected) { + pending_int.interruption_pending = 1; + pending_int.interruption_type = MSHV_HV_INTERRUPTION_TYPE_NMI; + pending_int.interruption_vector = EXCP02_NMI; + } else if (env->interrupt_injected >= 0) { + pending_int.interruption_pending = 1; + pending_int.interruption_type = env->soft_interrupt + ? MSHV_HV_INTERRUPTION_TYPE_SW_INT + : MSHV_HV_INTERRUPTION_TYPE_EXT_INT; + pending_int.interruption_vector = env->interrupt_injected; + } + + /* build int_state, normalize to bool */ + int_state.interrupt_shadow = !!(env->hflags & HF_INHIBIT_IRQ_MASK); + int_state.nmi_masked = !!(env->hflags2 & HF2_NMI_MASK); + + /* build pending_exc */ + if (env->exception_pending) { + pending_exc.event_pending = 1; + pending_exc.vector = env->exception_nr; + pending_exc.deliver_error_code = env->has_error_code; + pending_exc.error_code = env->error_code; + pending_exc.exception_parameter = env->exception_payload; + } + + assocs[0].name = HV_REGISTER_PENDING_INTERRUPTION; + assocs[0].value.reg64 = pending_int.as_uint64; + assocs[1].name = HV_REGISTER_INTERRUPT_STATE; + assocs[1].value.reg64 = int_state.as_uint64; + assocs[2].name = HV_REGISTER_PENDING_EVENT0; + assocs[2].value.pending_exception_event = pending_exc; + + ret = mshv_set_generic_regs(cpu, assocs, ARRAY_SIZE(assocs)); if (ret < 0) { - error_report("Failed to load special registers"); + error_report("failed to set vcpu event registers"); return -1; } return 0; } -static void add_cpuid_entry(GList *cpuid_entries, +static int update_hflags(CPUState *cpu) +{ + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + + x86_update_hflags(env); + + return 0; +} + +int mshv_arch_load_vcpu_state(CPUState *cpu) +{ + int ret; + + ret = get_standard_regs(cpu); + if (ret < 0) { + return ret; + } + + ret = get_special_regs(cpu); + if (ret < 0) { + return ret; + } + + /* INVARIANT: hflags are derived from regs+sregs, need to get both first */ + update_hflags(cpu); + + ret = get_xc_reg(cpu); + if (ret < 0) { + return ret; + } + + ret = get_fpu(cpu); + if (ret < 0) { + return ret; + } + + ret = mshv_get_msrs(cpu); + if (ret < 0) { + return ret; + } + + ret = get_xsave_state(cpu); + if (ret < 0) { + return ret; + } + + ret = get_vcpu_events(cpu); + if (ret < 0) { + return ret; + } + + return 0; +} + +static void add_cpuid_entry(GList **cpuid_entries, uint32_t function, uint32_t index, uint32_t eax, uint32_t ebx, uint32_t ecx, uint32_t edx) @@ -456,23 +988,26 @@ entry->ecx = ecx; entry->edx = edx; - cpuid_entries = g_list_append(cpuid_entries, entry); + *cpuid_entries = g_list_append(*cpuid_entries, entry); } -static void collect_cpuid_entries(const CPUState *cpu, GList *cpuid_entries) +static void collect_cpuid_entries(const CPUState *cpu, GList **cpuid_entries) { X86CPU *x86_cpu = X86_CPU(cpu); CPUX86State *env = &x86_cpu->env; uint32_t eax, ebx, ecx, edx; uint32_t leaf, subleaf; - size_t max_leaf = 0x1F; - size_t max_subleaf = 0x20; - - uint32_t leaves_with_subleaves[] = {0x4, 0x7, 0xD, 0xF, 0x10}; + uint32_t max_basic_leaf, max_extended_leaf; + uint32_t max_subleaf = 0x20; + uint32_t leaves_with_subleaves[] = {0x04, 0x07, 0x0d, 0x0f, 0x10}; int n_subleaf_leaves = ARRAY_SIZE(leaves_with_subleaves); - /* Regular leaves without subleaves */ - for (leaf = 0; leaf <= max_leaf; leaf++) { + /* Get maximum basic and and extended CPUID leaves */ + cpu_x86_cpuid(env, 0, 0, &max_basic_leaf, &ebx, &ecx, &edx); + cpu_x86_cpuid(env, 0x80000000, 0, &max_extended_leaf, &ebx, &ecx, &edx); + + /* Collect basic leaves (0x0 to max_basic_leaf) */ + for (leaf = 0; leaf <= max_basic_leaf; leaf++) { bool has_subleaves = false; for (int i = 0; i < n_subleaf_leaves; i++) { if (leaf == leaves_with_subleaves[i]) { @@ -483,32 +1018,46 @@ if (!has_subleaves) { cpu_x86_cpuid(env, leaf, 0, &eax, &ebx, &ecx, &edx); - if (eax == 0 && ebx == 0 && ecx == 0 && edx == 0) { - /* all zeroes indicates no more leaves */ - continue; - } - add_cpuid_entry(cpuid_entries, leaf, 0, eax, ebx, ecx, edx); continue; } + /* + * Valid XSAVE components can exist at a higher index se we need to set + * all subleaves for leaf 0x0d, even if we encounter an empty one. + */ + if (leaf == 0x0d) { + for (subleaf = 0; subleaf <= 63; subleaf++) { + cpu_x86_cpuid(env, leaf, subleaf, &eax, &ebx, &ecx, &edx); + add_cpuid_entry(cpuid_entries, leaf, subleaf, + eax, ebx, ecx, edx); + } + continue; + } + subleaf = 0; while (subleaf < max_subleaf) { cpu_x86_cpuid(env, leaf, subleaf, &eax, &ebx, &ecx, &edx); if (eax == 0 && ebx == 0 && ecx == 0 && edx == 0) { - /* all zeroes indicates no more leaves */ break; } - add_cpuid_entry(cpuid_entries, leaf, 0, eax, ebx, ecx, edx); + add_cpuid_entry(cpuid_entries, leaf, subleaf, eax, ebx, ecx, edx); subleaf++; } } + + /* Collect extended leaves (0x80000000 to max_extended_leaf) */ + for (leaf = 0x80000000; leaf <= max_extended_leaf; leaf++) { + cpu_x86_cpuid(env, leaf, 0, &eax, &ebx, &ecx, &edx); + add_cpuid_entry(cpuid_entries, leaf, 0, eax, ebx, ecx, edx); + } } static int register_intercept_result_cpuid_entry(const CPUState *cpu, uint8_t subleaf_specific, uint8_t always_override, + uint32_t ebx_mask, struct hv_cpuid_entry *entry) { int ret; @@ -522,22 +1071,18 @@ .input.always_override = always_override, .input.padding = 0, /* - * With regard to masks - these are to specify bits to be overwritten - * The current CpuidEntry structure wouldn't allow to carry the masks - * in addition to the actual register values. For this reason, the - * masks are set to the exact values of the corresponding register bits - * to be registered for an overwrite. To view resulting values the - * hypervisor would return, HvCallGetVpCpuidValues hypercall can be - * used. + * Masks specify which bits to override. Set to 0xFFFFFFFF to + * override all bits with the values from the QEMU CPU model. + * A mask of 0 lets the hypervisor supply its own value. */ .result.eax = entry->eax, - .result.eax_mask = entry->eax, + .result.eax_mask = 0xFFFFFFFF, .result.ebx = entry->ebx, - .result.ebx_mask = entry->ebx, + .result.ebx_mask = ebx_mask, .result.ecx = entry->ecx, - .result.ecx_mask = entry->ecx, + .result.ecx_mask = 0xFFFFFFFF, .result.edx = entry->edx, - .result.edx_mask = entry->edx, + .result.edx_mask = 0xFFFFFFFF, }; union hv_register_intercept_result_parameters parameters = { .cpuid = cpuid_params, @@ -568,6 +1113,7 @@ int ret = 0, entry_ret; struct hv_cpuid_entry *entry; uint8_t subleaf_specific, always_override; + uint32_t ebx_mask; for (size_t i = 0; i < cpuid->nent; i++) { entry = &cpuid->entries[i]; @@ -575,29 +1121,62 @@ /* set defaults */ subleaf_specific = 0; always_override = 1; + ebx_mask = 0xFFFFFFFF; - /* Intel */ - /* 0xb - Extended Topology Enumeration Leaf */ - /* 0x1f - V2 Extended Topology Enumeration Leaf */ - /* AMD */ - /* 0x8000_001e - Processor Topology Information */ - /* 0x8000_0026 - Extended CPU Topology */ - if (entry->function == 0xb - || entry->function == 0x1f - || entry->function == 0x8000001e - || entry->function == 0x80000026) { + /* + * Intel + * 0xb - Extended Topology Enumeration Leaf + * 0x1f - V2 Extended Topology Enumeration Leaf + * AMD + * 0x8000_001e - Processor Topology Information + * 0x8000_0026 - Extended CPU Topology + */ + if (entry->function == 0xb || + entry->function == 0x1f || + entry->function == 0x8000001e || + entry->function == 0x80000026) { subleaf_specific = 1; always_override = 1; - } else if (entry->function == 0x00000001 - || entry->function == 0x80000000 - || entry->function == 0x80000001 - || entry->function == 0x80000008) { + /* + * Feature enumeration leaves (subleaf-specific) + * 0x04: Deterministic Cache Parameters + * 0x07: Structured Extended Feature Flags + * 0x0D: Processor Extended State Enumeration + * 0x0F: Platform QoS Monitoring + * 0x10: Platform QoS Enforcement + */ + } else if (entry->function == 0x04 || + entry->function == 0x07 || + entry->function == 0x0d || + entry->function == 0x0f || + entry->function == 0x10) { + subleaf_specific = 1; + always_override = 1; + /* Basic feature leaves (no subleaves) */ + } else if (entry->function == 0x00000001 || + entry->function == 0x80000000 || + entry->function == 0x80000001 || + entry->function == 0x80000008) { subleaf_specific = 0; always_override = 1; } - entry_ret = register_intercept_result_cpuid_entry(cpu, subleaf_specific, + /* + * CPUID[0xD,0].EBX and CPUID[0xD,1].EBX report the XSAVE area + * size based on features currently enabled in XCR0/XSS. These + * values are dynamic and must not be overridden with static + * results from the QEMU CPU model. Setting ebx_mask to 0 lets + * the hypervisor supply EBX based on the guest's actual state. + */ + if (entry->function == 0x0d && + (entry->index == 0 || entry->index == 1)) { + ebx_mask = 0; + } + + entry_ret = register_intercept_result_cpuid_entry(cpu, + subleaf_specific, always_override, + ebx_mask, entry); if ((entry_ret < 0) && (ret == 0)) { ret = entry_ret; @@ -607,7 +1186,7 @@ return ret; } -static int set_cpuid2(const CPUState *cpu) +static int init_cpuid2(const CPUState *cpu) { int ret; size_t n_entries, cpuid_size; @@ -615,7 +1194,7 @@ struct hv_cpuid_entry *entry; GList *entries = NULL; - collect_cpuid_entries(cpu, entries); + collect_cpuid_entries(cpu, &entries); n_entries = g_list_length(entries); cpuid_size = sizeof(struct hv_cpuid) @@ -710,48 +1289,65 @@ return 0; } -static int set_fpu(const CPUState *cpu, const struct MshvFPU *regs) +static int set_fpu(const CPUState *cpu) { struct hv_register_assoc assocs[ARRAY_SIZE(FPU_REGISTER_NAMES)]; union hv_register_value *value; - size_t fp_i; union hv_x64_fp_control_status_register *ctrl_status; union hv_x64_xmm_control_status_register *xmm_ctrl_status; int ret; size_t n_regs = ARRAY_SIZE(FPU_REGISTER_NAMES); + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + size_t i, fp_i; + bool valid; /* first 16 registers are xmm0-xmm15 */ - for (size_t i = 0; i < 16; i++) { + for (i = 0; i < 16; i++) { assocs[i].name = FPU_REGISTER_NAMES[i]; value = &assocs[i].value; - memcpy(&value->reg128, ®s->xmm[i], 16); + value->reg128.low_part = env->xmm_regs[i].ZMM_Q(0); + value->reg128.high_part = env->xmm_regs[i].ZMM_Q(1); } /* next 8 registers are fp_mmx0-fp_mmx7 */ - for (size_t i = 16; i < 24; i++) { - assocs[i].name = FPU_REGISTER_NAMES[i]; + for (i = 16; i < 24; i++) { fp_i = (i - 16); + assocs[i].name = FPU_REGISTER_NAMES[i]; value = &assocs[i].value; - memcpy(&value->reg128, ®s->fpr[fp_i], 16); + value->fp.mantissa = env->fpregs[fp_i].d.low; + value->fp.biased_exponent = env->fpregs[fp_i].d.high & 0x7FFF; + value->fp.sign = (env->fpregs[fp_i].d.high >> 15) & 0x1; + value->fp.reserved = 0; } /* last two registers are fp_control_status and xmm_control_status */ assocs[24].name = FPU_REGISTER_NAMES[24]; value = &assocs[24].value; ctrl_status = &value->fp_control_status; - ctrl_status->fp_control = regs->fcw; - ctrl_status->fp_status = regs->fsw; - ctrl_status->fp_tag = regs->ftwx; + + ctrl_status->fp_control = env->fpuc; + /* bits 11,12,13 are the top of stack pointer */ + ctrl_status->fp_status = (env->fpus & ~0x3800) | ((env->fpstt & 0x7) << 11); + + ctrl_status->fp_tag = 0; + for (i = 0; i < 8; i++) { + valid = (env->fptags[i] == 0); + if (valid) { + ctrl_status->fp_tag |= (1u << i); + } + } + ctrl_status->reserved = 0; - ctrl_status->last_fp_op = regs->last_opcode; - ctrl_status->last_fp_rip = regs->last_ip; + ctrl_status->last_fp_op = env->fpop; + ctrl_status->last_fp_rip = env->fpip; assocs[25].name = FPU_REGISTER_NAMES[25]; value = &assocs[25].value; xmm_ctrl_status = &value->xmm_control_status; - xmm_ctrl_status->xmm_status_control = regs->mxcsr; - xmm_ctrl_status->xmm_status_control_mask = 0; - xmm_ctrl_status->last_fp_rdp = regs->last_dp; + xmm_ctrl_status->xmm_status_control = env->mxcsr; + xmm_ctrl_status->xmm_status_control_mask = 0x0000ffff; + xmm_ctrl_status->last_fp_rdp = env->fpdp; ret = mshv_set_generic_regs(cpu, assocs, n_regs); if (ret < 0) { @@ -762,12 +1358,15 @@ return 0; } -static int set_xc_reg(const CPUState *cpu, uint64_t xcr0) +static int set_xc_reg(const CPUState *cpu) { int ret; + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + struct hv_register_assoc assoc = { .name = HV_X64_REGISTER_XFEM, - .value.reg64 = xcr0, + .value.reg64 = env->xcr0, }; ret = mshv_set_generic_regs(cpu, &assoc, 1); @@ -778,30 +1377,6 @@ return 0; } -static int set_cpu_state(const CPUState *cpu, const MshvFPU *fpu_regs, - uint64_t xcr0) -{ - int ret; - - ret = set_standard_regs(cpu); - if (ret < 0) { - return ret; - } - ret = set_special_regs(cpu); - if (ret < 0) { - return ret; - } - ret = set_fpu(cpu, fpu_regs); - if (ret < 0) { - return ret; - } - ret = set_xc_reg(cpu, xcr0); - if (ret < 0) { - return ret; - } - return 0; -} - static int get_vp_state(int cpu_fd, struct mshv_get_set_vp_state *state) { int ret; @@ -815,7 +1390,7 @@ return 0; } -static int get_lapic(int cpu_fd, +static int get_lapic(const CPUState *cpu, struct hv_local_interrupt_controller_state *state) { int ret; @@ -823,6 +1398,7 @@ /* buffer aligned to 4k, as *state requires that */ void *buffer = qemu_memalign(size, size); struct mshv_get_set_vp_state mshv_state = { 0 }; + int cpu_fd = mshv_vcpufd(cpu); mshv_state.buf_ptr = (uint64_t) buffer; mshv_state.buf_sz = size; @@ -859,7 +1435,7 @@ return 0; } -static int set_lapic(int cpu_fd, +static int set_lapic(const CPUState *cpu, const struct hv_local_interrupt_controller_state *state) { int ret; @@ -867,6 +1443,7 @@ /* buffer aligned to 4k, as *state requires that */ void *buffer = qemu_memalign(size, size); struct mshv_get_set_vp_state mshv_state = { 0 }; + int cpu_fd = mshv_vcpufd(cpu); if (!state) { error_report("lapic state is NULL"); @@ -888,13 +1465,13 @@ return 0; } -static int set_lint(int cpu_fd) +static int init_lint(const CPUState *cpu) { int ret; uint32_t *lvt_lint0, *lvt_lint1; struct hv_local_interrupt_controller_state lapic_state = { 0 }; - ret = get_lapic(cpu_fd, &lapic_state); + ret = get_lapic(cpu, &lapic_state); if (ret < 0) { return ret; } @@ -907,167 +1484,46 @@ /* TODO: should we skip setting lapic if the values are the same? */ - return set_lapic(cpu_fd, &lapic_state); + return set_lapic(cpu, &lapic_state); } -static int setup_msrs(const CPUState *cpu) +int mshv_arch_store_vcpu_state(const CPUState *cpu) { int ret; - uint64_t default_type = MSR_MTRR_ENABLE | MSR_MTRR_MEM_TYPE_WB; - /* boot msr entries */ - MshvMsrEntry msrs[9] = { - { .index = IA32_MSR_SYSENTER_CS, .data = 0x0, }, - { .index = IA32_MSR_SYSENTER_ESP, .data = 0x0, }, - { .index = IA32_MSR_SYSENTER_EIP, .data = 0x0, }, - { .index = IA32_MSR_STAR, .data = 0x0, }, - { .index = IA32_MSR_CSTAR, .data = 0x0, }, - { .index = IA32_MSR_LSTAR, .data = 0x0, }, - { .index = IA32_MSR_KERNEL_GS_BASE, .data = 0x0, }, - { .index = IA32_MSR_SFMASK, .data = 0x0, }, - { .index = IA32_MSR_MTRR_DEF_TYPE, .data = default_type, }, - }; - - ret = mshv_configure_msr(cpu, msrs, 9); + ret = set_standard_regs(cpu); if (ret < 0) { - error_report("failed to setup msrs"); - return -1; - } - - return 0; -} - -/* - * TODO: populate topology info: - * - * X86CPU *x86cpu = X86_CPU(cpu); - * CPUX86State *env = &x86cpu->env; - * X86CPUTopoInfo *topo_info = &env->topo_info; - */ -int mshv_configure_vcpu(const CPUState *cpu, const struct MshvFPU *fpu, - uint64_t xcr0) -{ - int ret; - int cpu_fd = mshv_vcpufd(cpu); - - ret = set_cpuid2(cpu); - if (ret < 0) { - error_report("failed to set cpuid"); - return -1; - } - - ret = setup_msrs(cpu); - if (ret < 0) { - error_report("failed to setup msrs"); - return -1; - } - - ret = set_cpu_state(cpu, fpu, xcr0); - if (ret < 0) { - error_report("failed to set cpu state"); - return -1; - } - - ret = set_lint(cpu_fd); - if (ret < 0) { - error_report("failed to set lpic int"); - return -1; - } - - return 0; -} - -static int put_regs(const CPUState *cpu) -{ - X86CPU *x86cpu = X86_CPU(cpu); - CPUX86State *env = &x86cpu->env; - MshvFPU fpu = {0}; - int ret; - - memset(&fpu, 0, sizeof(fpu)); - - ret = mshv_configure_vcpu(cpu, &fpu, env->xcr0); - if (ret < 0) { - error_report("failed to configure vcpu"); return ret; } - return 0; -} - -struct MsrPair { - uint32_t index; - uint64_t value; -}; - -static int put_msrs(const CPUState *cpu) -{ - int ret = 0; - X86CPU *x86cpu = X86_CPU(cpu); - CPUX86State *env = &x86cpu->env; - MshvMsrEntries *msrs = g_malloc0(sizeof(MshvMsrEntries)); - - struct MsrPair pairs[] = { - { MSR_IA32_SYSENTER_CS, env->sysenter_cs }, - { MSR_IA32_SYSENTER_ESP, env->sysenter_esp }, - { MSR_IA32_SYSENTER_EIP, env->sysenter_eip }, - { MSR_EFER, env->efer }, - { MSR_PAT, env->pat }, - { MSR_STAR, env->star }, - { MSR_CSTAR, env->cstar }, - { MSR_LSTAR, env->lstar }, - { MSR_KERNELGSBASE, env->kernelgsbase }, - { MSR_FMASK, env->fmask }, - { MSR_MTRRdefType, env->mtrr_deftype }, - { MSR_VM_HSAVE_PA, env->vm_hsave }, - { MSR_SMI_COUNT, env->msr_smi_count }, - { MSR_IA32_PKRS, env->pkrs }, - { MSR_IA32_BNDCFGS, env->msr_bndcfgs }, - { MSR_IA32_XSS, env->xss }, - { MSR_IA32_UMWAIT_CONTROL, env->umwait }, - { MSR_IA32_TSX_CTRL, env->tsx_ctrl }, - { MSR_AMD64_TSC_RATIO, env->amd_tsc_scale_msr }, - { MSR_TSC_AUX, env->tsc_aux }, - { MSR_TSC_ADJUST, env->tsc_adjust }, - { MSR_IA32_SMBASE, env->smbase }, - { MSR_IA32_SPEC_CTRL, env->spec_ctrl }, - { MSR_VIRT_SSBD, env->virt_ssbd }, - }; - - if (ARRAY_SIZE(pairs) > MSHV_MSR_ENTRIES_COUNT) { - error_report("MSR entries exceed maximum size"); - g_free(msrs); - return -1; - } - - for (size_t i = 0; i < ARRAY_SIZE(pairs); i++) { - MshvMsrEntry *entry = &msrs->entries[i]; - entry->index = pairs[i].index; - entry->reserved = 0; - entry->data = pairs[i].value; - msrs->nmsrs++; - } - - ret = mshv_configure_msr(cpu, &msrs->entries[0], msrs->nmsrs); - g_free(msrs); - return ret; -} - - -int mshv_arch_put_registers(const CPUState *cpu) -{ - int ret; - - ret = put_regs(cpu); + ret = set_special_regs(cpu); if (ret < 0) { - error_report("Failed to put registers"); - return -1; + return ret; } - ret = put_msrs(cpu); + ret = set_xc_reg(cpu); if (ret < 0) { - error_report("Failed to put msrs"); - return -1; + return ret; + } + + ret = set_fpu(cpu); + if (ret < 0) { + return ret; + } + + ret = mshv_set_msrs(cpu); + if (ret < 0) { + return ret; + } + + ret = set_xsave_state(cpu); + if (ret < 0) { + return ret; + } + + ret = set_vcpu_events(cpu); + if (ret < 0) { + return ret; } return 0; @@ -1079,6 +1535,12 @@ features->access_guest_idle_reg = 1; } +void mshv_arch_disable_partition_proc_features( + union hv_partition_processor_features *disabled_features) +{ + disabled_features->la57_support = 1; +} + static int set_memory_info(const struct hyperv_message *msg, struct hv_x64_memory_intercept_message *info) { @@ -1103,16 +1565,16 @@ int ret; x86_insn_stream stream = { .bytes = insn_bytes, .len = insn_len }; - ret = mshv_load_regs(cpu); + ret = load_regs(cpu); if (ret < 0) { - error_report("failed to load registers"); + error_report("Failed to load registers"); return -1; } decode_instruction_stream(env, &decode, &stream); exec_instruction(env, &decode); - ret = mshv_store_regs(cpu); + ret = store_regs(cpu); if (ret < 0) { error_report("failed to store registers"); return -1; @@ -1291,25 +1753,6 @@ return 0; } -static int fetch_guest_state(CPUState *cpu) -{ - int ret; - - ret = mshv_get_standard_regs(cpu); - if (ret < 0) { - error_report("Failed to get standard registers"); - return -1; - } - - ret = mshv_get_special_regs(cpu); - if (ret < 0) { - error_report("Failed to get special registers"); - return -1; - } - - return 0; -} - static int read_memory(const CPUState *cpu, uint64_t initial_gva, uint64_t initial_gpa, uint64_t gva, uint8_t *data, size_t len) @@ -1429,9 +1872,9 @@ X86CPU *x86_cpu = X86_CPU(cpu); CPUX86State *env = &x86_cpu->env; - ret = fetch_guest_state(cpu); + ret = load_regs(cpu); if (ret < 0) { - error_report("Failed to fetch guest state"); + error_report("Failed to load registers"); return -1; } @@ -1462,7 +1905,7 @@ ret = set_x64_registers(cpu, reg_names, reg_values); if (ret < 0) { - error_report("Failed to set x64 registers"); + error_report("Failed to set RIP and RAX registers"); return -1; } @@ -1605,8 +2048,10 @@ X86CPU *x86_cpu = X86_CPU(cpu); CPUX86State *env = &x86_cpu->env; AccelCPUState *state = cpu->accel; - size_t page = HV_HYP_PAGE_SIZE; + size_t page = HV_HYP_PAGE_SIZE, xsave_len; void *mem = qemu_memalign(page, 2 * page); + int ret; + X86XSaveHeader *header; /* sanity check, to make sure we don't overflow the page */ QEMU_BUILD_BUG_ON((MAX_REGISTER_COUNT @@ -1614,11 +2059,48 @@ + sizeof(hv_input_get_vp_registers) > HV_HYP_PAGE_SIZE)); + /* mmap the registers page */ + void *rp = mmap(NULL, page, PROT_READ | PROT_WRITE, + MAP_SHARED, mshv_vcpufd(cpu), + MSHV_VP_MMAP_OFFSET_REGISTERS * page); + if (rp == MAP_FAILED) { + warn_report("register page mmap failed, falling back to hypercalls: %s", + strerror(errno)); + env->regs_page = NULL; + } else { + env->regs_page = (struct hv_vp_register_page *) rp; + } + state->hvcall_args.base = mem; state->hvcall_args.input_page = mem; state->hvcall_args.output_page = (uint8_t *)mem + page; env->emu_mmio_buf = g_new(char, 4096); + + /* Initialize XSAVE buffer page-aligned */ + /* TODO: pick proper size based on CPUID */ + xsave_len = page; + env->xsave_buf = qemu_memalign(page, xsave_len); + env->xsave_buf_len = xsave_len; + memset(env->xsave_buf, 0, env->xsave_buf_len); + + /* we need to set the compacted format bit in xsave header for mshv */ + header = (X86XSaveHeader *)(env->xsave_buf + sizeof(X86LegacyXSaveArea)); + header->xcomp_bv = header->xstate_bv | (1ULL << 63); + + /* + * TODO: populate topology info: + * X86CPUTopoInfo *topo_info = &env->topo_info; + */ + + ret = init_cpuid2(cpu); + assert(ret == 0); + + ret = mshv_init_msrs(cpu); + assert(ret == 0); + + ret = init_lint(cpu); + assert(ret == 0); } void mshv_arch_destroy_vcpu(CPUState *cpu) @@ -1627,9 +2109,55 @@ CPUX86State *env = &x86_cpu->env; AccelCPUState *state = cpu->accel; + /* Unmap the register page */ + if (env->regs_page) { + munmap(env->regs_page, HV_HYP_PAGE_SIZE); + env->regs_page = NULL; + } g_free(state->hvcall_args.base); state->hvcall_args = (MshvHvCallArgs){0}; g_clear_pointer(&env->emu_mmio_buf, g_free); + + qemu_vfree(env->xsave_buf); + env->xsave_buf = NULL; + env->xsave_buf_len = 0; +} + +uint32_t mshv_get_supported_cpuid(uint32_t func, uint32_t idx, int reg) +{ + uint32_t eax, ebx, ecx, edx; + uint32_t ret = 0; + + host_cpuid(func, idx, &eax, &ebx, &ecx, &edx); + switch (reg) { + case R_EAX: + ret = eax; break; + case R_EBX: + ret = ebx; break; + case R_ECX: + ret = ecx; break; + case R_EDX: + ret = edx; break; + } + + /* Disable nested virtualization features not yet supported by MSHV */ + if (func == 0x80000001 && reg == R_ECX) { + ret &= ~CPUID_EXT3_SVM; + } + if (func == 0x01 && reg == R_ECX) { + ret &= ~CPUID_EXT_VMX; + } + + if (func == 0x07 && idx == 0 && reg == R_ECX) { + /* + * LA57 (5-level paging) causes incorrect GVA=>GPA translations + * in the instruction decoder/emulator. Disable until page table + * walk in x86_mmu.c works w/ 5-level paging. + */ + ret &= ~CPUID_7_0_ECX_LA57; + } + + return ret; } /* @@ -1671,3 +2199,62 @@ return ret; } + +static void mshv_cpu_xsave_init(void) +{ + static bool first = true; + uint32_t eax, ebx, ecx, edx; + int i; + + if (!first) { + return; + } + first = false; + + /* x87 and SSE states are in the legacy region of the XSAVE area. */ + x86_ext_save_areas[XSTATE_FP_BIT].offset = 0; + x86_ext_save_areas[XSTATE_SSE_BIT].offset = 0; + + for (i = XSTATE_SSE_BIT + 1; i < XSAVE_STATE_AREA_COUNT; i++) { + ExtSaveArea *esa = &x86_ext_save_areas[i]; + + if (!esa->size) { + continue; + } + host_cpuid(0xd, i, &eax, &ebx, &ecx, &edx); + if (eax != 0) { + assert(esa->size == eax); + esa->offset = ebx; + esa->ecx = ecx; + } + } +} + +static void mshv_cpu_instance_init(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + + host_cpu_instance_init(cpu); + mshv_cpu_xsave_init(); +} + +static void mshv_cpu_accel_class_init(ObjectClass *oc, const void *data) +{ + AccelCPUClass *acc = ACCEL_CPU_CLASS(oc); + + acc->cpu_instance_init = mshv_cpu_instance_init; +} + +static const TypeInfo mshv_cpu_accel_type_info = { + .name = ACCEL_CPU_NAME("mshv"), + .parent = TYPE_ACCEL_CPU, + .class_init = mshv_cpu_accel_class_init, + .abstract = true, +}; + +static void mshv_cpu_accel_register_types(void) +{ + type_register_static(&mshv_cpu_accel_type_info); +} + +type_init(mshv_cpu_accel_register_types);
diff --git a/target/i386/mshv/msr.c b/target/i386/mshv/msr.c new file mode 100644 index 0000000..8c220a9 --- /dev/null +++ b/target/i386/mshv/msr.c
@@ -0,0 +1,425 @@ +/* + * QEMU MSHV support + * + * Copyright Microsoft, Corp. 2025 + * + * Authors: Magnus Kulke <magnuskulke@microsoft.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "system/mshv.h" +#include "system/mshv_int.h" +#include "hw/hyperv/hvgdk_mini.h" +#include "linux/mshv.h" +#include "qemu/error-report.h" +#include "cpu.h" + +#define MSHV_ENV_FIELD(env, offset) (*(uint64_t *)((char *)(env) + (offset))) + +typedef struct MshvMsrEnvMap { + uint32_t msr_index; + uint32_t hv_name; + ptrdiff_t env_offset; +} MshvMsrEnvMap; + +/* We assert that 64bit access to sysenter_cs is safe because of padding */ +QEMU_BUILD_BUG_ON(offsetof(CPUX86State, sysenter_esp) - + offsetof(CPUX86State, sysenter_cs) + < sizeof(uint64_t)); + +/* Those MSRs have a direct mapping to fields in CPUX86State */ +static const MshvMsrEnvMap msr_env_map[] = { + /* Architectural */ + { IA32_MSR_EFER, HV_X64_REGISTER_EFER, offsetof(CPUX86State, efer) }, + { IA32_MSR_PAT, HV_X64_REGISTER_PAT, offsetof(CPUX86State, pat) }, + + /* Syscall */ + { IA32_MSR_SYSENTER_CS, HV_X64_REGISTER_SYSENTER_CS, + offsetof(CPUX86State, sysenter_cs) }, + { IA32_MSR_SYSENTER_ESP, HV_X64_REGISTER_SYSENTER_ESP, + offsetof(CPUX86State, sysenter_esp) }, + { IA32_MSR_SYSENTER_EIP, HV_X64_REGISTER_SYSENTER_EIP, + offsetof(CPUX86State, sysenter_eip) }, + { IA32_MSR_STAR, HV_X64_REGISTER_STAR, + offsetof(CPUX86State, star) }, + { IA32_MSR_LSTAR, HV_X64_REGISTER_LSTAR, + offsetof(CPUX86State, lstar) }, + { IA32_MSR_CSTAR, HV_X64_REGISTER_CSTAR, + offsetof(CPUX86State, cstar) }, + { IA32_MSR_SFMASK, HV_X64_REGISTER_SFMASK, + offsetof(CPUX86State, fmask) }, + { IA32_MSR_KERNEL_GS_BASE, HV_X64_REGISTER_KERNEL_GS_BASE, + offsetof(CPUX86State, kernelgsbase) }, + + /* TSC-related */ + { IA32_MSR_TSC, HV_X64_REGISTER_TSC, + offsetof(CPUX86State, tsc) }, + { IA32_MSR_TSC_AUX, HV_X64_REGISTER_TSC_AUX, + offsetof(CPUX86State, tsc_aux) }, + { IA32_MSR_TSC_ADJUST, HV_X64_REGISTER_TSC_ADJUST, + offsetof(CPUX86State, tsc_adjust) }, + + /* Hyper-V per-partition MSRs */ + { HV_X64_MSR_HYPERCALL, HV_X64_REGISTER_HYPERCALL, + offsetof(CPUX86State, msr_hv_hypercall) }, + { HV_X64_MSR_GUEST_OS_ID, HV_REGISTER_GUEST_OS_ID, + offsetof(CPUX86State, msr_hv_guest_os_id) }, + { HV_X64_MSR_REFERENCE_TSC, HV_REGISTER_REFERENCE_TSC, + offsetof(CPUX86State, msr_hv_tsc) }, + + /* Hyper-V MSRs (non-SINT) */ + { HV_X64_MSR_SCONTROL, HV_REGISTER_SCONTROL, + offsetof(CPUX86State, msr_hv_synic_control) }, + { HV_X64_MSR_SIEFP, HV_REGISTER_SIEFP, + offsetof(CPUX86State, msr_hv_synic_evt_page) }, + { HV_X64_MSR_SIMP, HV_REGISTER_SIMP, + offsetof(CPUX86State, msr_hv_synic_msg_page) }, + + /* MTRR default type */ + { IA32_MSR_MTRR_DEF_TYPE, HV_X64_REGISTER_MSR_MTRR_DEF_TYPE, + offsetof(CPUX86State, mtrr_deftype) }, + + /* CET / Shadow Stack */ + { MSR_IA32_U_CET, HV_X64_REGISTER_U_CET, + offsetof(CPUX86State, u_cet) }, + { MSR_IA32_S_CET, HV_X64_REGISTER_S_CET, + offsetof(CPUX86State, s_cet) }, + { MSR_IA32_PL0_SSP, HV_X64_REGISTER_PL0_SSP, + offsetof(CPUX86State, pl0_ssp) }, + { MSR_IA32_PL1_SSP, HV_X64_REGISTER_PL1_SSP, + offsetof(CPUX86State, pl1_ssp) }, + { MSR_IA32_PL2_SSP, HV_X64_REGISTER_PL2_SSP, + offsetof(CPUX86State, pl2_ssp) }, + { MSR_IA32_PL3_SSP, HV_X64_REGISTER_PL3_SSP, + offsetof(CPUX86State, pl3_ssp) }, + { MSR_IA32_INT_SSP_TAB, HV_X64_REGISTER_INTERRUPT_SSP_TABLE_ADDR, + offsetof(CPUX86State, int_ssp_table) }, + + /* XSAVE Supervisor State */ + { MSR_IA32_XSS, HV_X64_REGISTER_U_XSS, + offsetof(CPUX86State, xss) }, + + /* Other */ + + /* TODO: find out processor features that correlate to unsupported MSRs. */ + /* { IA32_MSR_MISC_ENABLE, HV_X64_REGISTER_MSR_IA32_MISC_ENABLE, */ + /* offsetof(CPUX86State, msr_ia32_misc_enable) }, */ + /* { IA32_MSR_BNDCFGS, HV_X64_REGISTER_BNDCFGS, */ + /* offsetof(CPUX86State, msr_bndcfgs) }, */ + { IA32_MSR_SPEC_CTRL, HV_X64_REGISTER_SPEC_CTRL, + offsetof(CPUX86State, spec_ctrl) }, +}; + +/* + * The assocs have to be set according to this schema: + * 8 entries for 0-7 mtrr_base + * 8 entries for mtrr_mask 0-7 + * 11 entries for 1 x 64k, 2 x 16k, 8 x 4k fixed MTRR + * 27 total entries + */ + +#define MSHV_MTRR_MSR_COUNT 27 +#define MSHV_MSR_TOTAL_COUNT (ARRAY_SIZE(msr_env_map) + MSHV_MTRR_MSR_COUNT) + +static void store_in_env_mtrr_phys(CPUState *cpu, + const struct hv_register_assoc *assocs, + size_t n_assocs) +{ + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + size_t i, fixed_offset; + hv_register_name hv_name; + uint64_t base, mask; + + assert(n_assocs == MSHV_MTRR_MSR_COUNT); + + for (i = 0; i < MSR_MTRRcap_VCNT; i++) { + hv_name = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE0 + i; + assert(assocs[i].name == hv_name); + hv_name = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK0 + i; + assert(assocs[i + MSR_MTRRcap_VCNT].name == hv_name); + + base = assocs[i].value.reg64; + mask = assocs[i + MSR_MTRRcap_VCNT].value.reg64; + env->mtrr_var[i].base = base; + env->mtrr_var[i].mask = mask; + } + + /* fixed 1x 64, 2x 16, 8x 4 kB */ + fixed_offset = MSR_MTRRcap_VCNT * 2; + for (i = 0; i < 11; i++) { + hv_name = HV_X64_REGISTER_MSR_MTRR_FIX64K00000 + i; + assert(assocs[fixed_offset + i].name == hv_name); + env->mtrr_fixed[i] = assocs[fixed_offset + i].value.reg64; + } +} + +/* + * The assocs have to be set according to this schema: + * 8 entries for 0-7 mtrr_base + * 8 entries for mtrr_mask 0-7 + * 11 entries for 1 x 64k, 2 x 16k, 8 x 4k fixed MTRR + * 27 total entries + */ +static void load_from_env_mtrr_phys(const CPUState *cpu, + struct hv_register_assoc *assocs, + size_t n_assocs) +{ + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + size_t i, fixed_offset; + uint64_t base, mask, fixed_value; + hv_register_name base_name, mask_name, fixed_name; + hv_register_assoc *assoc; + + assert(n_assocs == MSHV_MTRR_MSR_COUNT); + + for (i = 0; i < MSR_MTRRcap_VCNT; i++) { + base = env->mtrr_var[i].base; + mask = env->mtrr_var[i].mask; + + base_name = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE0 + i; + mask_name = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK0 + i; + + assoc = &assocs[i]; + assoc->name = base_name; + assoc->value.reg64 = base; + + assoc = &assocs[i + MSR_MTRRcap_VCNT]; + assoc->name = mask_name; + assoc->value.reg64 = mask; + } + + /* fixed 1x 64, 2x 16, 8x 4 kB */ + fixed_offset = MSR_MTRRcap_VCNT * 2; + for (i = 0; i < 11; i++) { + fixed_name = HV_X64_REGISTER_MSR_MTRR_FIX64K00000 + i; + fixed_value = env->mtrr_fixed[i]; + + assoc = &assocs[fixed_offset + i]; + assoc->name = fixed_name; + assoc->value.reg64 = fixed_value; + } +} + +int mshv_init_msrs(const CPUState *cpu) +{ + int ret; + uint64_t d_t = MSR_MTRR_ENABLE | MSR_MTRR_MEM_TYPE_WB; + + const struct hv_register_assoc assocs[] = { + { .name = HV_X64_REGISTER_SYSENTER_CS, .value.reg64 = 0x0 }, + { .name = HV_X64_REGISTER_SYSENTER_ESP, .value.reg64 = 0x0 }, + { .name = HV_X64_REGISTER_SYSENTER_EIP, .value.reg64 = 0x0 }, + { .name = HV_X64_REGISTER_STAR, .value.reg64 = 0x0 }, + { .name = HV_X64_REGISTER_CSTAR, .value.reg64 = 0x0 }, + { .name = HV_X64_REGISTER_LSTAR, .value.reg64 = 0x0 }, + { .name = HV_X64_REGISTER_KERNEL_GS_BASE, .value.reg64 = 0x0 }, + { .name = HV_X64_REGISTER_SFMASK, .value.reg64 = 0x0 }, + { .name = HV_X64_REGISTER_MSR_MTRR_DEF_TYPE, .value.reg64 = d_t }, + }; + + ret = mshv_set_generic_regs(cpu, assocs, ARRAY_SIZE(assocs)); + if (ret < 0) { + error_report("failed to put msrs"); + return -1; + } + + return 0; +} + + +/* + * INVARIANT: this fn expects assocs in the same order as they appear in + * msr_env_map. + */ +static void store_in_env(CPUState *cpu, const struct hv_register_assoc *assocs, + size_t n_assocs) +{ + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + size_t i, j; + const MshvMsrEnvMap *mapping; + union hv_register_value hv_value; + ptrdiff_t offset; + uint32_t hv_name; + size_t mtrr_index; + + assert(n_assocs <= MSHV_MSR_TOTAL_COUNT); + + for (i = 0, j = 0; i < ARRAY_SIZE(msr_env_map); i++) { + hv_name = assocs[j].name; + mapping = &msr_env_map[i]; + if (hv_name != mapping->hv_name) { + continue; + } + + hv_value = assocs[j].value; + offset = mapping->env_offset; + MSHV_ENV_FIELD(env, offset) = hv_value.reg64; + j++; + } + + mtrr_index = j; + store_in_env_mtrr_phys(cpu, &assocs[mtrr_index], MSHV_MTRR_MSR_COUNT); +} + +static void set_hv_name_in_assocs(struct hv_register_assoc *assocs, + size_t n_assocs) +{ + size_t i; + size_t mtrr_offset, mtrr_fixed_offset; + hv_register_name hv_name; + + assert(n_assocs == MSHV_MSR_TOTAL_COUNT); + + for (i = 0; i < ARRAY_SIZE(msr_env_map); i++) { + assocs[i].name = msr_env_map[i].hv_name; + } + + mtrr_offset = ARRAY_SIZE(msr_env_map); + for (i = 0; i < MSR_MTRRcap_VCNT; i++) { + hv_name = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE0 + i; + assocs[mtrr_offset + i].name = hv_name; + hv_name = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK0 + i; + assocs[mtrr_offset + MSR_MTRRcap_VCNT + i].name = hv_name; + } + + /* fixed 1x 64, 2x 16, 8x 4 kB */ + mtrr_fixed_offset = mtrr_offset + MSR_MTRRcap_VCNT * 2; + for (i = 0; i < 11; i++) { + hv_name = HV_X64_REGISTER_MSR_MTRR_FIX64K00000 + i; + assocs[mtrr_fixed_offset + i].name = hv_name; + } +} + +static bool msr_supported(uint32_t name) +{ + /* + * This check is not done comprehensively, it's meant to avoid hvcall + * failures for certain MSRs on architectures that don't support them. + */ + + switch (name) { + case HV_X64_REGISTER_SPEC_CTRL: + return mshv_state->processor_features.ibrs_support; + case HV_X64_REGISTER_TSC_ADJUST: + return mshv_state->processor_features.tsc_adjust_support; + case HV_X64_REGISTER_U_CET: + case HV_X64_REGISTER_S_CET: + case HV_X64_REGISTER_PL0_SSP: + case HV_X64_REGISTER_PL1_SSP: + case HV_X64_REGISTER_PL2_SSP: + case HV_X64_REGISTER_PL3_SSP: + case HV_X64_REGISTER_INTERRUPT_SSP_TABLE_ADDR: + case HV_X64_REGISTER_U_XSS: + return mshv_state->processor_features.cet_ss_support || + mshv_state->processor_features.cet_ibt_support; + } + + return true; +} + +int mshv_get_msrs(CPUState *cpu) +{ + int ret = 0; + size_t n_assocs = MSHV_MSR_TOTAL_COUNT; + struct hv_register_assoc assocs[MSHV_MSR_TOTAL_COUNT]; + size_t i, j; + uint32_t name; + + set_hv_name_in_assocs(assocs, n_assocs); + + /* Filter out MSRs that cannot be read */ + for (i = 0, j = 0; i < n_assocs; i++) { + name = assocs[i].name; + + if (!msr_supported(name)) { + continue; + } + + if (j != i) { + assocs[j] = assocs[i]; + } + j++; + } + n_assocs = j; + + ret = mshv_get_generic_regs(cpu, assocs, n_assocs); + if (ret < 0) { + error_report("Failed to get MSRs"); + return -errno; + } + + store_in_env(cpu, assocs, n_assocs); + + return 0; +} + +static void load_from_env(const CPUState *cpu, struct hv_register_assoc *assocs, + size_t n_assocs) +{ + size_t i; + const MshvMsrEnvMap *mapping; + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + ptrdiff_t offset; + union hv_register_value *hv_value; + size_t mtrr_offset; + + assert(n_assocs == MSHV_MSR_TOTAL_COUNT); + + for (i = 0; i < ARRAY_SIZE(msr_env_map); i++) { + mapping = &msr_env_map[i]; + offset = mapping->env_offset; + assocs[i].name = mapping->hv_name; + hv_value = &assocs[i].value; + hv_value->reg64 = MSHV_ENV_FIELD(env, offset); + } + + mtrr_offset = ARRAY_SIZE(msr_env_map); + load_from_env_mtrr_phys(cpu, &assocs[mtrr_offset], MSHV_MTRR_MSR_COUNT); +} + +int mshv_set_msrs(const CPUState *cpu) +{ + size_t n_assocs = MSHV_MSR_TOTAL_COUNT; + struct hv_register_assoc assocs[MSHV_MSR_TOTAL_COUNT]; + int ret; + size_t i, j; + + load_from_env(cpu, assocs, n_assocs); + + /* Filter out MSRs that cannot be written */ + for (i = 0, j = 0; i < n_assocs; i++) { + uint32_t name = assocs[i].name; + + /* Partition-wide MSRs: only write on vCPU 0 */ + if (cpu->cpu_index != 0 && + (name == HV_X64_REGISTER_HYPERCALL || + name == HV_REGISTER_GUEST_OS_ID || + name == HV_REGISTER_REFERENCE_TSC)) { + continue; + } + + if (!msr_supported(name)) { + continue; + } + + if (j != i) { + assocs[j] = assocs[i]; + } + j++; + } + n_assocs = j; + + ret = mshv_set_generic_regs(cpu, assocs, n_assocs); + if (ret < 0) { + error_report("Failed to set MSRs"); + return -errno; + } + + return 0; +}
diff --git a/target/i386/sev.c b/target/i386/sev.c index 99cf308..debe634 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c
@@ -160,10 +160,6 @@ * * The SevGuestState object is used for creating and managing a SEV * guest. - * - * # $QEMU \ - * -object sev-guest,id=sev0 \ - * -machine ...,memory-encryption=sev0 */ struct SevGuestState { SevCommonState parent_obj;
diff --git a/tests/docker/dockerfiles/emsdk-wasm64-cross.docker b/tests/docker/dockerfiles/emsdk-wasm64-cross.docker index cbe20c7..c04a741 100644 --- a/tests/docker/dockerfiles/emsdk-wasm64-cross.docker +++ b/tests/docker/dockerfiles/emsdk-wasm64-cross.docker
@@ -19,7 +19,9 @@ ENV LDFLAGS="-sWASM_BIGINT -sASYNCIFY=1 -L$TARGET/lib" RUN apt-get update && apt-get install -y \ autoconf \ + bison \ build-essential \ + flex \ libglib2.0-dev \ libtool \ pkgconf \
diff --git a/tests/functional/pylintrc b/tests/functional/pylintrc index 049c3e7..949bea6 100644 --- a/tests/functional/pylintrc +++ b/tests/functional/pylintrc
@@ -58,6 +58,7 @@ # no Warning level messages displayed, use "--disable=all --enable=classes # --disable=W". disable=bad-inline-option, + c-extension-no-member, consider-using-f-string, file-ignored, fixme,
diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py index c357e6e..86bcdf7 100644 --- a/tests/qemu-iotests/testenv.py +++ b/tests/qemu-iotests/testenv.py
@@ -259,6 +259,7 @@ def __init__(self, source_dir: str, build_dir: str, ('arm', 'virt'), ('aarch64', 'virt'), ('avr', 'mega2560'), + ('hexagon', 'virt'), ('m68k', 'virt'), ('or1k', 'virt'), ('riscv32', 'virt'),
diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c index bcd0a9c..37fee7a 100644 --- a/tests/qtest/boot-serial-test.c +++ b/tests/qtest/boot-serial-test.c
@@ -142,6 +142,13 @@ 0x04, 0x38, 0x01, 0x40 /* 0x40013804 = USART1 TXD */ }; +static const uint8_t bios_hexagon[] = { + 0x00, 0x40, 0x00, 0x01, /* immext(#0x10000000) */ + 0x00, 0xc0, 0x00, 0x78, /* r0 = ##0x10000000 */ + 0x54, 0xc0, 0x00, 0x3c, /* memb(r0+#0) = #0x54 Write 'T' */ + 0xf8, 0xff, 0xff, 0x59 /* jump 0x0 ; Loop back to start */ +}; + typedef struct testdef { const char *arch; /* Target architecture */ const char *machine; /* Name of the machine */ @@ -194,6 +201,7 @@ { "arm", "microbit", "", "T", sizeof(kernel_nrf51), kernel_nrf51 }, { "arm", "stm32vldiscovery", "", "T", sizeof(kernel_stm32vldiscovery), kernel_stm32vldiscovery }, + { "hexagon", "virt", "", "TT", sizeof(bios_hexagon), NULL, bios_hexagon }, { NULL } };
diff --git a/tests/qtest/libqos/virtio-9p-client.c b/tests/qtest/libqos/virtio-9p-client.c index af01d4c..305b0da 100644 --- a/tests/qtest/libqos/virtio-9p-client.c +++ b/tests/qtest/libqos/virtio-9p-client.c
@@ -241,6 +241,9 @@ id == P9_RUNLINKAT ? "RUNLINKAT" : id == P9_RFLUSH ? "RFLUSH" : id == P9_RREADDIR ? "RREADDIR" : + id == P9_RREAD ? "RREAD" : + id == P9_RCLUNK ? "RCLUNK" : + id == P9_RXATTRCREATE ? "RXATTRCREATE" : "<unknown>"; } @@ -1103,3 +1106,124 @@ v9fs_req_recv(req, P9_RUNLINKAT); v9fs_req_free(req); } + +/* size[4] Tread tag[2] fid[4] offset[8] count[4] */ +TReadRes v9fs_tread(TReadOpt opt) +{ + P9Req *req; + uint32_t err; + + g_assert(opt.client); + + uint32_t body_size = 4 + 8 + 4; + + req = v9fs_req_init(opt.client, body_size, P9_TREAD, opt.tag); + v9fs_uint32_write(req, opt.fid); + v9fs_uint64_write(req, opt.offset); + v9fs_uint32_write(req, opt.count); + v9fs_req_send(req); + + if (!opt.requestOnly) { + v9fs_req_wait_for_reply(req, NULL); + if (opt.expectErr) { + v9fs_rlerror(req, &err); + g_assert_cmpint(err, ==, opt.expectErr); + } else { + v9fs_rread(req, opt.rread.count, opt.rread.data); + } + req = NULL; /* request was freed */ + } + + return (TReadRes) { + .req = req, + .count = opt.rread.count ? *opt.rread.count : 0 + }; +} + +/* size[4] Rread tag[2] count[4] data[count] */ +void v9fs_rread(P9Req *req, uint32_t *count, void *data) +{ + v9fs_req_recv(req, P9_RREAD); + v9fs_uint32_read(req, count); + if (data && *count > 0) { + v9fs_memread(req, data, *count); + } + v9fs_req_free(req); +} + +/* size[4] Tclunk tag[2] fid[4] */ +TClunkRes v9fs_tclunk(TClunkOpt opt) +{ + P9Req *req; + uint32_t err; + + g_assert(opt.client); + + req = v9fs_req_init(opt.client, 4, P9_TCLUNK, opt.tag); + v9fs_uint32_write(req, opt.fid); + v9fs_req_send(req); + + if (!opt.requestOnly) { + v9fs_req_wait_for_reply(req, NULL); + if (opt.expectErr) { + v9fs_rlerror(req, &err); + g_assert_cmpint(err, ==, opt.expectErr); + } else { + v9fs_rclunk(req); + } + req = NULL; /* request was freed */ + } + + return (TClunkRes) { .req = req }; +} + +/* size[4] Rclunk tag[2] */ +void v9fs_rclunk(P9Req *req) +{ + v9fs_req_recv(req, P9_RCLUNK); + v9fs_req_free(req); +} + +/* size[4] Txattrcreate tag[2] fid[4] name[s] attr_size[8] flags[4] */ +TXattrCreateRes v9fs_txattrcreate(TXattrCreateOpt opt) +{ + P9Req *req; + uint32_t err; + + g_assert(opt.client); + g_assert(opt.name); + + uint32_t body_size = 4 + 8 + 4; + uint16_t string_size = v9fs_string_size(opt.name); + + g_assert_cmpint(body_size, <=, UINT32_MAX - string_size); + body_size += string_size; + + req = v9fs_req_init(opt.client, body_size, P9_TXATTRCREATE, opt.tag); + v9fs_uint32_write(req, opt.fid); + v9fs_string_write(req, opt.name); + v9fs_uint64_write(req, opt.size); + v9fs_uint32_write(req, opt.flags); + v9fs_req_send(req); + + err = 0; + if (!opt.requestOnly) { + v9fs_req_wait_for_reply(req, NULL); + if (opt.expectErr) { + v9fs_rlerror(req, &err); + g_assert_cmpint(err, ==, opt.expectErr); + } else { + v9fs_rxattrcreate(req); + } + req = NULL; /* request was freed */ + } + + return (TXattrCreateRes) { .req = req, .err = err }; +} + +/* size[4] Rxattrcreate tag[2] */ +void v9fs_rxattrcreate(P9Req *req) +{ + v9fs_req_recv(req, P9_RXATTRCREATE); + v9fs_req_free(req); +}
diff --git a/tests/qtest/libqos/virtio-9p-client.h b/tests/qtest/libqos/virtio-9p-client.h index e3221a3..4b04324 100644 --- a/tests/qtest/libqos/virtio-9p-client.h +++ b/tests/qtest/libqos/virtio-9p-client.h
@@ -21,7 +21,8 @@ #include "qgraph.h" #include "tests/qtest/libqtest-single.h" -#define P9_MAX_SIZE 4096 /* Max size of a T-message or R-message */ +/* Max size of a T-message or R-message */ +#define P9_MAX_SIZE (32 * 1024) typedef struct { QTestState *qts; @@ -473,6 +474,85 @@ P9Req *req; } TunlinkatRes; +/* options for 'Tread' 9p request */ +typedef struct TReadOpt { + /* 9P client being used (mandatory) */ + QVirtio9P *client; + /* user supplied tag number being returned with response (optional) */ + uint16_t tag; + /* file ID of file to read from (required) */ + uint32_t fid; + /* start position of read from beginning of file (optional) */ + uint64_t offset; + /* how many bytes to read (required) */ + uint32_t count; + /* data being received from 9p server as 'Rread' response (optional) */ + struct { + uint32_t *count; + void *data; + } rread; + /* only send Tread request but not wait for a reply? (optional) */ + bool requestOnly; + /* do we expect an Rlerror response, if yes which error code? (optional) */ + uint32_t expectErr; +} TReadOpt; + +/* result of 'Tread' 9p request */ +typedef struct TReadRes { + /* if requestOnly was set: request object for further processing */ + P9Req *req; + /* amount of bytes read */ + uint32_t count; +} TReadRes; + +/* options for 'Tclunk' 9p request */ +typedef struct TClunkOpt { + /* 9P client being used (mandatory) */ + QVirtio9P *client; + /* user supplied tag number being returned with response (optional) */ + uint16_t tag; + /* file ID to clunk (required) */ + uint32_t fid; + /* only send Tclunk request but not wait for a reply? (optional) */ + bool requestOnly; + /* do we expect an Rlerror response, if yes which error code? (optional) */ + uint32_t expectErr; +} TClunkOpt; + +/* result of 'Tclunk' 9p request */ +typedef struct TClunkRes { + /* if requestOnly was set: request object for further processing */ + P9Req *req; +} TClunkRes; + +/* options for 'Txattrcreate' 9p request */ +typedef struct TXattrCreateOpt { + /* 9P client being used (mandatory) */ + QVirtio9P *client; + /* user supplied tag number being returned with response (optional) */ + uint16_t tag; + /* file ID to convert to xattr fid (required) */ + uint32_t fid; + /* name of the xattr (required) */ + const char *name; + /* size of the xattr value (required) */ + uint64_t size; + /* flags: P9_XATTR_CREATE or P9_XATTR_REPLACE (optional) */ + uint32_t flags; + /* only send Txattrcreate request but not wait for a reply? (optional) */ + bool requestOnly; + /* do we expect an Rlerror response, if yes which error code? (optional) */ + uint32_t expectErr; +} TXattrCreateOpt; + +/* result of 'Txattrcreate' 9p request */ +typedef struct TXattrCreateRes { + /* if requestOnly was set: request object for further processing */ + P9Req *req; + /* error code if Rlerror received */ + uint32_t err; +} TXattrCreateRes; + void v9fs_set_allocator(QGuestAllocator *t_alloc); void v9fs_memwrite(P9Req *req, const void *addr, size_t len); void v9fs_memskip(P9Req *req, size_t len); @@ -524,5 +604,11 @@ void v9fs_rlink(P9Req *req); TunlinkatRes v9fs_tunlinkat(TunlinkatOpt); void v9fs_runlinkat(P9Req *req); +TReadRes v9fs_tread(TReadOpt opt); +void v9fs_rread(P9Req *req, uint32_t *count, void *data); +TClunkRes v9fs_tclunk(TClunkOpt opt); +void v9fs_rclunk(P9Req *req); +TXattrCreateRes v9fs_txattrcreate(TXattrCreateOpt opt); +void v9fs_rxattrcreate(P9Req *req); #endif
diff --git a/tests/qtest/libqos/virtio-9p.c b/tests/qtest/libqos/virtio-9p.c index 186fcc1..823756d 100644 --- a/tests/qtest/libqos/virtio-9p.c +++ b/tests/qtest/libqos/virtio-9p.c
@@ -228,6 +228,12 @@ g_string_assign(haystack, s); } +void virtio_9p_add_synth_driver_args(GString *cmd_line, const char *args) +{ + /* append passed args to '-fsdev ...' group */ + regex_replace(cmd_line, "(-fsdev \\w[^ ]*)", "\\1,%s", args); +} + void virtio_9p_assign_local_driver(GString *cmd_line, const char *args) { g_assert_nonnull(local_test_path);
diff --git a/tests/qtest/libqos/virtio-9p.h b/tests/qtest/libqos/virtio-9p.h index 4807271..e7efeef 100644 --- a/tests/qtest/libqos/virtio-9p.h +++ b/tests/qtest/libqos/virtio-9p.h
@@ -45,6 +45,12 @@ }; /** + * Add required test specific args to the QEMU command line for the 9pfs + * 'synth' fs driver. + */ +void virtio_9p_add_synth_driver_args(GString *cmd_line, const char *args); + +/** * Creates the directory for the 9pfs 'local' filesystem driver to access. */ void virtio_9p_create_local_test_dir(void);
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index e154abb..822e0bd 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build
@@ -296,6 +296,8 @@ ['iommu-riscv-test'] : []) + \ (config_all_devices.has_key('CONFIG_K230') ? ['k230-wdt-test'] : []) +qtests_hexagon = ['boot-serial-test'] + qos_test_ss = ss.source_set() qos_test_ss.add( 'ac97-test.c',
diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c index 1c69d41..cfd3c02 100644 --- a/tests/qtest/virtio-9p-test.c +++ b/tests/qtest/virtio-9p-test.c
@@ -31,6 +31,18 @@ #define tsymlink(...) v9fs_tsymlink((TsymlinkOpt) __VA_ARGS__) #define tlink(...) v9fs_tlink((TlinkOpt) __VA_ARGS__) #define tunlinkat(...) v9fs_tunlinkat((TunlinkatOpt) __VA_ARGS__) +#define tread(...) v9fs_tread((TReadOpt) __VA_ARGS__) +#define tclunk(...) v9fs_tclunk((TClunkOpt) __VA_ARGS__) +#define txattrcreate(...) v9fs_txattrcreate((TXattrCreateOpt) __VA_ARGS__) + +/* + * xattr size to be used for xattr tests + * + * 64k is the max. xattr size supported by the Linux kernel, However btrfs + * for instance supports only 16219 bytes. So let's be conservative and + * just use 8k for the xattr tests. + */ +#define TEST_XATTR_SIZE (8 * 1024) static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc) { @@ -104,6 +116,42 @@ return false; } +/* + * Returns the current internal xattr FID count (works with synth driver only). + */ +static size_t get_xattr_count(QVirtio9P *v9p) +{ + uint16_t nwqid; + v9fs_qid *wqid; + const char *xattr_count_path[] = { "stat", "xattr_count" }; + size_t xattr_count; + uint32_t bytes_read; + + /* walk to /stat/xattr_count file */ + uint32_t fid = twalk({ + .client = v9p, .fid = 0, + .nwname = 2, .wnames = (char **)xattr_count_path, + .rwalk = { .nwqid = &nwqid, .wqid = &wqid } + }).newfid; + + /* open for read */ + tlopen({ + .client = v9p, .fid = fid, .flags = O_RDONLY, + .rlopen = { .qid = NULL, .iounit = NULL } + }); + + /* read the internal xattr FID count */ + tread({ + .client = v9p, .fid = fid, .offset = 0, .count = sizeof(xattr_count), + .rread = { .count = &bytes_read, .data = &xattr_count } + }); + + /* cleanup */ + tclunk({ .client = v9p, .fid = fid }); + + return xattr_count; +} + /* basic readdir test where reply fits into a single response message */ static void fs_readdir(void *obj, void *data, QGuestAllocator *t_alloc) { @@ -245,6 +293,121 @@ g_free(wnames[0]); } +/* + * Test 9p server's xattr FID count limit enforcement. + * + * Shared test code for both 'synth' and 'local' driver to verify correct + * behaviour of 9p server enforcing preconfigured xattr FID count limit + * correctly. + * + * @v9p: 9pfs client + * + * @max_xattr: max. allowed xattr FIDs, or -1 for infinite + * + * @check_counter: whether to verify 9p server internal xattr FID counter + * (only works with 'synth' fs driver) + */ +static void do_xattr_limit(QVirtio9P *v9p, int max_xattr, bool check_counter) +{ + size_t count; + int i; + int limit = (max_xattr != -1) ? max_xattr : V9FS_MAX_XATTR_DEFAULT + 100; + g_autofree uint32_t *fids = g_new0(uint32_t, limit); + uint32_t err_fid = 0; + const char *file_path[] = { QTEST_V9FS_SYNTH_WRITE_FILE }; + g_autofree uint8_t *xattr_data = g_malloc(TEST_XATTR_SIZE); + + if (!g_test_slow()) { + g_test_skip("This is a slow test, run with -m slow"); + return; + } + + /* prepare xattr data with 'X' characters */ + memset(xattr_data, 'X', TEST_XATTR_SIZE); + + tattach({ .client = v9p }); + + /* create max. amount of permitted xattrs */ + for (i = 0; i < limit; i++) { + /* walk to create a new fid */ + fids[i] = twalk({ + .client = v9p, .fid = 0, + .nwname = 1, .wnames = (char **) file_path + }).newfid; + + /* create new xattr fid */ + txattrcreate({ + .client = v9p, .fid = fids[i], .name = "user.test", + .size = TEST_XATTR_SIZE, .flags = 0 + }); + + /* transfer the xattr data */ + twrite({ + .client = v9p, .fid = fids[i], .offset = 0, + .count = TEST_XATTR_SIZE, .data = xattr_data + }); + + /* verify server internal xattr counter */ + if (check_counter) { + count = get_xattr_count(v9p); + g_assert_cmpuint(count, ==, (i + 1)); + } + + /* avoid virtio descriptor exhaustion */ + qvirtqueue_reset_pool(v9p->vq); + } + + /* if xattrs are limited, the next xattr should fail */ + if (max_xattr != -1) { + /* walk to create another fid */ + err_fid = twalk({ + .client = v9p, .fid = 0, + .nwname = 1, .wnames = (char **) file_path + }).newfid; + + /* try to create one more xattr fid - should fail */ + txattrcreate({ + .client = v9p, .fid = err_fid, .name = "user.test_exceed", + .size = TEST_XATTR_SIZE, .flags = 0, + .expectErr = ENOSPC + }); + + /* verify internal xattr counter hasn't changed */ + if (check_counter) { + count = get_xattr_count(v9p); + g_assert_cmpuint(count, ==, limit); + } + } + + /* clunk all fids (should decrement xattr counter) */ + for (i = 0; i < limit; i++) { + tclunk({ .client = v9p, .fid = fids[i] }); + qvirtqueue_reset_pool(v9p->vq); + } + if (err_fid) { + tclunk({ .client = v9p, .fid = err_fid }); + } + + /* verify internal xattr counter is zero */ + if (check_counter) { + count = get_xattr_count(v9p); + g_assert_cmpuint(count, ==, 0); + } +} + +static void do_local_xattr_limit(QVirtio9P *v9p, int max_xattr) +{ + g_autofree char *test_file = virtio_9p_test_path("WRITE"); + + /* + * this file must be created for the test to work with the 'local' fs driver + */ + g_file_set_contents(test_file, "", 0, NULL); + + /* the actual test code shared with the 'synth' fs driver tests */ + do_xattr_limit(v9p, max_xattr, false); +} + static void fs_walk_no_slash(void *obj, void *data, QGuestAllocator *t_alloc) { QVirtio9P *v9p = obj; @@ -505,6 +668,27 @@ do_readdir_split(obj, 512); } +static void fs_synth_xattr_limit_default(void *obj, void *data, + QGuestAllocator *t_alloc) +{ + v9fs_set_allocator(t_alloc); + do_xattr_limit(obj, V9FS_MAX_XATTR_DEFAULT, true); +} + +static void fs_synth_xattr_limit_custom(void *obj, void *data, + QGuestAllocator *t_alloc) +{ + v9fs_set_allocator(t_alloc); + do_xattr_limit(obj, 100, true); +} + +static void fs_synth_xattr_limit_unlimited(void *obj, void *data, + QGuestAllocator *t_alloc) +{ + v9fs_set_allocator(t_alloc); + do_xattr_limit(obj, -1, true); +} + /* tests using the 9pfs 'local' fs driver */ @@ -819,26 +1003,81 @@ g_string_free(path, TRUE); } +static void fs_local_xattr_limit_default(void *obj, void *data, + QGuestAllocator *t_alloc) +{ + v9fs_set_allocator(t_alloc); + do_local_xattr_limit(obj, V9FS_MAX_XATTR_DEFAULT); +} + +static void fs_local_xattr_limit_custom(void *obj, void *data, + QGuestAllocator *t_alloc) +{ + v9fs_set_allocator(t_alloc); + do_local_xattr_limit(obj, 100); +} + +static void fs_local_xattr_limit_unlimited(void *obj, void *data, + QGuestAllocator *t_alloc) +{ + v9fs_set_allocator(t_alloc); + do_local_xattr_limit(obj, -1); +} + +static void *synth_max_xattr_custom_opt(GString *cmd_line, void *arg) +{ + virtio_9p_add_synth_driver_args(cmd_line, "max_xattr=100"); + return arg; +} + +static void *synth_max_xattr_unlimited_opt(GString *cmd_line, void *arg) +{ + virtio_9p_add_synth_driver_args(cmd_line, "max_xattr=0"); + return arg; +} + static void cleanup_9p_local_driver(void *data) { /* remove previously created test dir when test is completed */ virtio_9p_remove_local_test_dir(); } -static void *assign_9p_local_driver(GString *cmd_line, void *arg) +static void assign_9p_local_driver_with_args(GString *cmd_line, + const char *extra_opts) { /* make sure test dir for the 'local' tests exists */ virtio_9p_create_local_test_dir(); - virtio_9p_assign_local_driver(cmd_line, "security_model=mapped-xattr"); + g_autofree char *opts = + (extra_opts) ? + g_strdup_printf("security_model=mapped-xattr,%s", extra_opts) : + g_strdup("security_model=mapped-xattr"); + + virtio_9p_assign_local_driver(cmd_line, opts); g_test_queue_destroy(cleanup_9p_local_driver, NULL); +} + +static void *assign_9p_local_driver(GString *cmd_line, void *arg) +{ + assign_9p_local_driver_with_args(cmd_line, NULL); + return arg; +} + +static void *local_max_xattr_custom_opt(GString *cmd_line, void *arg) +{ + assign_9p_local_driver_with_args(cmd_line, "max_xattr=100"); + return arg; +} + +static void *local_max_xattr_unlimited_opt(GString *cmd_line, void *arg) +{ + assign_9p_local_driver_with_args(cmd_line, "max_xattr=0"); return arg; } static void register_virtio_9p_test(void) { - QOSGraphTestOptions opts = { }; @@ -869,7 +1108,14 @@ fs_readdir_split_256, &opts); qos_add_test("synth/readdir/split_128", "virtio-9p", fs_readdir_split_128, &opts); - + qos_add_test("synth/xattr_limit/default", "virtio-9p", + fs_synth_xattr_limit_default, &opts); + opts.before = synth_max_xattr_custom_opt; + qos_add_test("synth/xattr_limit/custom", "virtio-9p", + fs_synth_xattr_limit_custom, &opts); + opts.before = synth_max_xattr_unlimited_opt; + qos_add_test("synth/xattr_limit/unlimited", "virtio-9p", + fs_synth_xattr_limit_unlimited, &opts); /* 9pfs test cases using the 'local' filesystem driver */ opts.before = assign_9p_local_driver; @@ -888,6 +1134,14 @@ &opts); qos_add_test("local/deep_absolute_path", "virtio-9p", fs_deep_absolute_path, &opts); + qos_add_test("local/xattr_limit/default", "virtio-9p", + fs_local_xattr_limit_default, &opts); + opts.before = local_max_xattr_custom_opt; + qos_add_test("local/xattr_limit/custom", "virtio-9p", + fs_local_xattr_limit_custom, &opts); + opts.before = local_max_xattr_unlimited_opt; + qos_add_test("local/xattr_limit/unlimited", "virtio-9p", + fs_local_xattr_limit_unlimited, &opts); } libqos_init(register_virtio_9p_test);