Merge remote-tracking branch 'refs/remotes/upstream/stable-10.1' into cr16-wip-bump-10.1
# Conflicts: # VERSION # hw/9pfs/9p.c # hw/i386/amd_iommu.c # hw/misc/aspeed_hace.c # hw/net/npcm_gmac.c # hw/virtio/virtio.c # qapi/misc-i386.json # target/arm/debug_helper.c # target/arm/kvm.c # target/arm/tcg/translate-sve.c # target/i386/cpu.c # target/i386/hvf/x86_flags.c # target/loongarch/tcg/insn_trans/trans_vec.c.inc # target/riscv/kvm/kvm-cpu.c # target/riscv/pmp.c # ui/gtk-gl-area.c # ui/gtk.c # ui/vnc-enc-tight.c # ui/vnc-enc-zlib.c # ui/vnc-jobs.c # ui/vnc.c # ui/vnc.h
This commit is contained in:
commit
2ff7b31f7a
3030 changed files with 97904 additions and 51341 deletions
|
|
@ -11,4 +11,3 @@
|
|||
prep-perpatch-check-cmd = scripts/checkpatch.pl -q --terse --no-summary --mailback -
|
||||
searchmask = https://lore.kernel.org/qemu-devel/?x=m&t=1&q=%s
|
||||
linkmask = https://lore.kernel.org/qemu-devel/%s
|
||||
linktrailermask = Message-ID: <%s>
|
||||
|
|
|
|||
|
|
@ -69,10 +69,6 @@ variables:
|
|||
- if: '$QEMU_CI != "1" && $QEMU_CI != "2" && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM'
|
||||
when: never
|
||||
|
||||
# Avocado jobs don't run in forks unless $QEMU_CI_AVOCADO_TESTING is set
|
||||
- if: '$QEMU_JOB_AVOCADO && $QEMU_CI_AVOCADO_TESTING != "1" && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM'
|
||||
when: never
|
||||
|
||||
|
||||
#############################################################
|
||||
# Stage 2: fine tune execution of jobs in specific scenarios
|
||||
|
|
@ -101,8 +97,8 @@ variables:
|
|||
when: manual
|
||||
allow_failure: true
|
||||
|
||||
# Avocado jobs can be manually start in forks if $QEMU_CI_AVOCADO_TESTING is unset
|
||||
- if: '$QEMU_JOB_AVOCADO && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM'
|
||||
# Functional jobs can be manually started in forks
|
||||
- if: '$QEMU_JOB_FUNCTIONAL && $QEMU_CI_FUNCTIONAL != "1" && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM'
|
||||
when: manual
|
||||
allow_failure: true
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
- ccache --zero-stats
|
||||
- section_start configure "Running configure"
|
||||
- ../configure --enable-werror --disable-docs --enable-fdt=system
|
||||
--disable-debug-info
|
||||
${TARGETS:+--target-list="$TARGETS"}
|
||||
$CONFIGURE_ARGS ||
|
||||
{ cat config.log meson-logs/meson-log.txt && exit 1; }
|
||||
|
|
@ -76,7 +77,8 @@
|
|||
fi
|
||||
- section_end buildenv
|
||||
- section_start test "Running tests"
|
||||
- $MAKE NINJA=":" $MAKE_CHECK_ARGS
|
||||
# doctests need all the compilation artifacts
|
||||
- $MAKE NINJA=":" MTESTARGS="--no-suite doc" $MAKE_CHECK_ARGS
|
||||
- section_end test
|
||||
|
||||
.native_test_job_template:
|
||||
|
|
@ -95,7 +97,6 @@
|
|||
cache:
|
||||
key: "${CI_JOB_NAME}-cache"
|
||||
paths:
|
||||
- ${CI_PROJECT_DIR}/avocado-cache
|
||||
- ${CI_PROJECT_DIR}/functional-cache
|
||||
policy: pull-push
|
||||
artifacts:
|
||||
|
|
@ -109,20 +110,37 @@
|
|||
reports:
|
||||
junit: build/tests/results/latest/results.xml
|
||||
before_script:
|
||||
- mkdir -p ~/.config/avocado
|
||||
- echo "[datadir.paths]" > ~/.config/avocado/avocado.conf
|
||||
- echo "cache_dirs = ['${CI_PROJECT_DIR}/avocado-cache']"
|
||||
>> ~/.config/avocado/avocado.conf
|
||||
- echo -e '[job.output.testlogs]\nstatuses = ["FAIL", "INTERRUPT"]'
|
||||
>> ~/.config/avocado/avocado.conf
|
||||
- if [ -d ${CI_PROJECT_DIR}/avocado-cache ]; then
|
||||
du -chs ${CI_PROJECT_DIR}/*-cache ;
|
||||
fi
|
||||
- export AVOCADO_ALLOW_UNTRUSTED_CODE=1
|
||||
- export QEMU_TEST_ALLOW_UNTRUSTED_CODE=1
|
||||
- export QEMU_TEST_CACHE_DIR=${CI_PROJECT_DIR}/functional-cache
|
||||
after_script:
|
||||
- cd build
|
||||
- du -chs ${CI_PROJECT_DIR}/*-cache
|
||||
variables:
|
||||
QEMU_JOB_AVOCADO: 1
|
||||
QEMU_JOB_FUNCTIONAL: 1
|
||||
|
||||
.wasm_build_job_template:
|
||||
extends: .base_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
|
||||
- cd build
|
||||
- section_start configure "Running configure"
|
||||
- emconfigure ../configure --disable-docs
|
||||
${TARGETS:+--target-list="$TARGETS"}
|
||||
$CONFIGURE_ARGS ||
|
||||
{ cat config.log meson-logs/meson-log.txt && exit 1; }
|
||||
- if test -n "$LD_JOBS";
|
||||
then
|
||||
pyvenv/bin/meson configure . -Dbackend_max_links="$LD_JOBS" ;
|
||||
fi || exit 1;
|
||||
- section_end configure
|
||||
- section_start build "Building QEMU"
|
||||
- emmake make -j"$JOBS"
|
||||
- section_end build
|
||||
|
|
|
|||
|
|
@ -29,8 +29,7 @@ functional-system-alpine:
|
|||
artifacts: true
|
||||
variables:
|
||||
IMAGE: alpine
|
||||
MAKE_CHECK_ARGS: check-avocado check-functional
|
||||
AVOCADO_TAGS: arch:avr arch:loongarch64 arch:mips64 arch:mipsel
|
||||
MAKE_CHECK_ARGS: check-functional
|
||||
|
||||
build-system-ubuntu:
|
||||
extends:
|
||||
|
|
@ -42,7 +41,7 @@ build-system-ubuntu:
|
|||
IMAGE: ubuntu2204
|
||||
CONFIGURE_ARGS: --enable-docs --enable-rust
|
||||
TARGETS: alpha-softmmu microblazeel-softmmu mips64el-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
MAKE_CHECK_ARGS: check-build check-doc
|
||||
|
||||
check-system-ubuntu:
|
||||
extends: .native_test_job_template
|
||||
|
|
@ -60,8 +59,7 @@ functional-system-ubuntu:
|
|||
artifacts: true
|
||||
variables:
|
||||
IMAGE: ubuntu2204
|
||||
MAKE_CHECK_ARGS: check-avocado check-functional
|
||||
AVOCADO_TAGS: arch:alpha arch:microblazeel arch:mips64el
|
||||
MAKE_CHECK_ARGS: check-functional
|
||||
|
||||
build-system-debian:
|
||||
extends:
|
||||
|
|
@ -92,8 +90,7 @@ functional-system-debian:
|
|||
artifacts: true
|
||||
variables:
|
||||
IMAGE: debian
|
||||
MAKE_CHECK_ARGS: check-avocado check-functional
|
||||
AVOCADO_TAGS: arch:arm arch:i386 arch:riscv64 arch:sh4 arch:sparc arch:xtensa
|
||||
MAKE_CHECK_ARGS: check-functional
|
||||
|
||||
crash-test-debian:
|
||||
extends: .native_test_job_template
|
||||
|
|
@ -118,7 +115,7 @@ build-system-fedora:
|
|||
CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs --enable-crypto-afalg --enable-rust
|
||||
TARGETS: microblaze-softmmu mips-softmmu
|
||||
xtensa-softmmu m68k-softmmu riscv32-softmmu ppc-softmmu sparc64-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
MAKE_CHECK_ARGS: check-build check-doc
|
||||
|
||||
build-system-fedora-rust-nightly:
|
||||
extends:
|
||||
|
|
@ -130,12 +127,7 @@ build-system-fedora-rust-nightly:
|
|||
IMAGE: fedora-rust-nightly
|
||||
CONFIGURE_ARGS: --disable-docs --enable-rust --enable-strict-rust-lints
|
||||
TARGETS: aarch64-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
after_script:
|
||||
- source scripts/ci/gitlab-ci-section
|
||||
- section_start test "Running Rust doctests"
|
||||
- cd build
|
||||
- pyvenv/bin/meson devenv -w ../rust ${CARGO-cargo} test --doc -p qemu_api
|
||||
MAKE_CHECK_ARGS: check-build check-doc
|
||||
|
||||
allow_failure: true
|
||||
|
||||
|
|
@ -155,9 +147,7 @@ functional-system-fedora:
|
|||
artifacts: true
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
MAKE_CHECK_ARGS: check-avocado check-functional
|
||||
AVOCADO_TAGS: arch:microblaze arch:mips arch:xtensa arch:m68k
|
||||
arch:riscv32 arch:ppc arch:sparc64
|
||||
MAKE_CHECK_ARGS: check-functional
|
||||
|
||||
crash-test-fedora:
|
||||
extends: .native_test_job_template
|
||||
|
|
@ -193,12 +183,11 @@ build-previous-qemu:
|
|||
when: on_success
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build-previous
|
||||
exclude:
|
||||
- build-previous/**/*.p
|
||||
- build-previous/**/*.a.p
|
||||
- build-previous/**/*.c.o
|
||||
- build-previous/**/*.c.o.d
|
||||
- build-previous/qemu-bundle
|
||||
- build-previous/qemu-system-aarch64
|
||||
- build-previous/qemu-system-x86_64
|
||||
- build-previous/tests/qtest/migration-test
|
||||
- build-previous/scripts
|
||||
needs:
|
||||
job: amd64-opensuse-leap-container
|
||||
variables:
|
||||
|
|
@ -208,6 +197,11 @@ build-previous-qemu:
|
|||
GIT_FETCH_EXTRA_FLAGS: --prune --quiet
|
||||
before_script:
|
||||
- source scripts/ci/gitlab-ci-section
|
||||
# Skip if this series contains the release bump commit. During the
|
||||
# release process there might be a window of commits when the
|
||||
# version tag is not yet present in the remote and git fetch would
|
||||
# fail.
|
||||
- if grep -q "\.0$" VERSION; then exit 0; fi
|
||||
- export QEMU_PREV_VERSION="$(sed 's/\([0-9.]*\)\.[0-9]*/v\1.0/' VERSION)"
|
||||
- git remote add upstream https://gitlab.com/qemu-project/qemu
|
||||
- git fetch upstream refs/tags/$QEMU_PREV_VERSION:refs/tags/$QEMU_PREV_VERSION
|
||||
|
|
@ -228,18 +222,13 @@ build-previous-qemu:
|
|||
IMAGE: opensuse-leap
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
script:
|
||||
# Skip for round release numbers, this job is only relevant for
|
||||
# testing a development tree.
|
||||
- if grep -q "\.0$" VERSION; then exit 0; fi
|
||||
# Use the migration-tests from the older QEMU tree. This avoids
|
||||
# testing an old QEMU against new features/tests that it is not
|
||||
# compatible with.
|
||||
- cd build-previous
|
||||
# Don't allow python-based tests to run. The
|
||||
# vmstate-checker-script test has a race that causes it to fail
|
||||
# sometimes. It cannot be fixed it because this job runs the test
|
||||
# from the old QEMU version. The test will be removed on master,
|
||||
# but this job will only see the change in the next release.
|
||||
#
|
||||
# TODO: remove this line after 9.2 release
|
||||
- unset PYTHON
|
||||
# old to new
|
||||
- QTEST_QEMU_BINARY_SRC=./qemu-system-${TARGET}
|
||||
QTEST_QEMU_BINARY=../build/qemu-system-${TARGET} ./tests/qtest/migration-test
|
||||
|
|
@ -278,9 +267,7 @@ functional-system-centos:
|
|||
artifacts: true
|
||||
variables:
|
||||
IMAGE: centos9
|
||||
MAKE_CHECK_ARGS: check-avocado check-functional
|
||||
AVOCADO_TAGS: arch:ppc64 arch:or1k arch:s390x arch:x86_64 arch:rx
|
||||
arch:sh4
|
||||
MAKE_CHECK_ARGS: check-functional
|
||||
|
||||
build-system-opensuse:
|
||||
extends:
|
||||
|
|
@ -309,8 +296,7 @@ functional-system-opensuse:
|
|||
artifacts: true
|
||||
variables:
|
||||
IMAGE: opensuse-leap
|
||||
MAKE_CHECK_ARGS: check-avocado check-functional
|
||||
AVOCADO_TAGS: arch:s390x arch:x86_64 arch:aarch64
|
||||
MAKE_CHECK_ARGS: check-functional
|
||||
|
||||
#
|
||||
# Flaky tests. We don't run these by default and they are allow fail
|
||||
|
|
@ -338,10 +324,9 @@ functional-system-flaky:
|
|||
allow_failure: true
|
||||
variables:
|
||||
IMAGE: debian
|
||||
MAKE_CHECK_ARGS: check-avocado check-functional
|
||||
MAKE_CHECK_ARGS: check-functional
|
||||
QEMU_JOB_OPTIONAL: 1
|
||||
QEMU_TEST_FLAKY_TESTS: 1
|
||||
AVOCADO_TAGS: flaky
|
||||
|
||||
# This jobs explicitly disable TCG (--disable-tcg), KVM is detected by
|
||||
# the configure script. The container doesn't contain Xen headers so
|
||||
|
|
@ -482,8 +467,8 @@ clang-user:
|
|||
# Since slirp callbacks are used in QEMU Timers, we cannot use libslirp with
|
||||
# CFI builds, and thus have to disable it here.
|
||||
#
|
||||
# Split in three sets of build/check/avocado to limit the execution time of each
|
||||
# job
|
||||
# Split in three sets of build/check/functional to limit the execution time
|
||||
# of each job
|
||||
build-cfi-aarch64:
|
||||
extends:
|
||||
- .native_build_job_template
|
||||
|
|
@ -520,7 +505,7 @@ functional-cfi-aarch64:
|
|||
artifacts: true
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
MAKE_CHECK_ARGS: check-avocado check-functional
|
||||
MAKE_CHECK_ARGS: check-functional
|
||||
|
||||
build-cfi-ppc64-s390x:
|
||||
extends:
|
||||
|
|
@ -558,7 +543,7 @@ functional-cfi-ppc64-s390x:
|
|||
artifacts: true
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
MAKE_CHECK_ARGS: check-avocado check-functional
|
||||
MAKE_CHECK_ARGS: check-functional
|
||||
|
||||
build-cfi-x86_64:
|
||||
extends:
|
||||
|
|
@ -592,7 +577,7 @@ functional-cfi-x86_64:
|
|||
artifacts: true
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
MAKE_CHECK_ARGS: check-avocado check-functional
|
||||
MAKE_CHECK_ARGS: check-functional
|
||||
|
||||
tsan-build:
|
||||
extends: .native_build_job_template
|
||||
|
|
@ -801,3 +786,12 @@ coverity:
|
|||
when: never
|
||||
# Always manual on forks even if $QEMU_CI == "2"
|
||||
- when: manual
|
||||
|
||||
build-wasm:
|
||||
extends: .wasm_build_job_template
|
||||
timeout: 2h
|
||||
needs:
|
||||
job: wasm-emsdk-cross-container
|
||||
variables:
|
||||
IMAGE: emsdk-wasm32-cross
|
||||
CONFIGURE_ARGS: --static --disable-tools --enable-debug --enable-tcg-interpreter
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
from os import access, R_OK, path
|
||||
from sys import argv, exit
|
||||
from sys import exit
|
||||
import json
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
from collections import Counter
|
||||
|
||||
|
||||
|
|
@ -28,7 +30,7 @@ def extract_build_units(cc_path):
|
|||
return build_units
|
||||
|
||||
|
||||
def analyse_units(build_units):
|
||||
def analyse_units(build_units, top_n):
|
||||
"""
|
||||
Analyse the build units and report stats and the top 10 rebuilds
|
||||
"""
|
||||
|
|
@ -42,7 +44,7 @@ def analyse_units(build_units):
|
|||
reverse=True)
|
||||
|
||||
print("Most rebuilt units:")
|
||||
for unit, count in sorted_build_units[:20]:
|
||||
for unit, count in sorted_build_units[:top_n]:
|
||||
print(f" {unit} built {count} times")
|
||||
|
||||
print("Least rebuilt units:")
|
||||
|
|
@ -51,16 +53,19 @@ def analyse_units(build_units):
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(argv) != 2:
|
||||
script_name = path.basename(argv[0])
|
||||
print(f"Usage: {script_name} <path_to_compile_commands.json>")
|
||||
exit(1)
|
||||
parser = argparse.ArgumentParser(
|
||||
description="analyse number of build units in compile_commands.json")
|
||||
parser.add_argument("cc_path", type=Path, default=None,
|
||||
help="Path to compile_commands.json")
|
||||
parser.add_argument("-n", type=int, default=20,
|
||||
help="Dump the top <n> entries")
|
||||
|
||||
cc_path = argv[1]
|
||||
if path.isfile(cc_path) and access(cc_path, R_OK):
|
||||
units = extract_build_units(cc_path)
|
||||
analyse_units(units)
|
||||
args = parser.parse_args()
|
||||
|
||||
if path.isfile(args.cc_path) and access(args.cc_path, R_OK):
|
||||
units = extract_build_units(args.cc_path)
|
||||
analyse_units(units, args.n)
|
||||
exit(0)
|
||||
else:
|
||||
print(f"{cc_path} doesn't exist or isn't readable")
|
||||
print(f"{args.cc_path} doesn't exist or isn't readable")
|
||||
exit(1)
|
||||
|
|
|
|||
|
|
@ -67,11 +67,8 @@ ppc64el-debian-cross-container:
|
|||
riscv64-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
stage: containers
|
||||
# as we are currently based on 'sid/unstable' we may break so...
|
||||
allow_failure: true
|
||||
variables:
|
||||
NAME: debian-riscv64-cross
|
||||
QEMU_JOB_OPTIONAL: 1
|
||||
|
||||
s390x-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
|
|
@ -94,3 +91,8 @@ win64-fedora-cross-container:
|
|||
extends: .container_job_template
|
||||
variables:
|
||||
NAME: fedora-win64-cross
|
||||
|
||||
wasm-emsdk-cross-container:
|
||||
extends: .container_job_template
|
||||
variables:
|
||||
NAME: emsdk-wasm32-cross
|
||||
|
|
|
|||
|
|
@ -118,12 +118,8 @@ cross-ppc64el-kvm-only:
|
|||
IMAGE: debian-ppc64el-cross
|
||||
EXTRA_CONFIGURE_OPTS: --disable-tcg --without-default-devices
|
||||
|
||||
# The riscv64 cross-builds currently use a 'sid' container to get
|
||||
# compilers and libraries. Until something more stable is found we
|
||||
# allow_failure so as not to block CI.
|
||||
cross-riscv64-system:
|
||||
extends: .cross_system_build_job
|
||||
allow_failure: true
|
||||
needs:
|
||||
job: riscv64-debian-cross-container
|
||||
variables:
|
||||
|
|
@ -131,7 +127,6 @@ cross-riscv64-system:
|
|||
|
||||
cross-riscv64-user:
|
||||
extends: .cross_user_build_job
|
||||
allow_failure: true
|
||||
needs:
|
||||
job: riscv64-debian-cross-container
|
||||
variables:
|
||||
|
|
|
|||
|
|
@ -31,7 +31,9 @@ ubuntu-22.04-s390x-all-system:
|
|||
timeout: 75m
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
allow_failure: true
|
||||
- if: "$S390X_RUNNER_AVAILABLE"
|
||||
allow_failure: true
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
|
|
|
|||
3
.mailmap
3
.mailmap
|
|
@ -67,7 +67,8 @@ Andrey Drobyshev <andrey.drobyshev@virtuozzo.com> Andrey Drobyshev via <qemu-blo
|
|||
BALATON Zoltan <balaton@eik.bme.hu> BALATON Zoltan via <qemu-ppc@nongnu.org>
|
||||
|
||||
# Next, replace old addresses by a more recent one.
|
||||
Akihiko Odaki <akihiko.odaki@daynix.com> <akihiko.odaki@gmail.com>
|
||||
Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> <akihiko.odaki@daynix.com>
|
||||
Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> <akihiko.odaki@gmail.com>
|
||||
Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <aleksandar.markovic@mips.com>
|
||||
Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <aleksandar.markovic@imgtec.com>
|
||||
Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <amarkovic@wavecomp.com>
|
||||
|
|
|
|||
|
|
@ -21,5 +21,3 @@ python:
|
|||
install:
|
||||
- requirements: docs/requirements.txt
|
||||
|
||||
# We want all the document formats
|
||||
formats: all
|
||||
|
|
|
|||
35
.travis.yml
35
.travis.yml
|
|
@ -79,41 +79,6 @@ after_script:
|
|||
jobs:
|
||||
include:
|
||||
|
||||
- name: "[aarch64] GCC check-tcg"
|
||||
arch: arm64
|
||||
addons:
|
||||
apt_packages:
|
||||
- libaio-dev
|
||||
- libattr1-dev
|
||||
- libbrlapi-dev
|
||||
- libcacard-dev
|
||||
- libcap-ng-dev
|
||||
- libfdt-dev
|
||||
- libgcrypt20-dev
|
||||
- libgnutls28-dev
|
||||
- libgtk-3-dev
|
||||
- libiscsi-dev
|
||||
- liblttng-ust-dev
|
||||
- libncurses5-dev
|
||||
- libnfs-dev
|
||||
- libpixman-1-dev
|
||||
- libpng-dev
|
||||
- librados-dev
|
||||
- libsdl2-dev
|
||||
- libseccomp-dev
|
||||
- liburcu-dev
|
||||
- libusb-1.0-0-dev
|
||||
- libvdeplug-dev
|
||||
- libvte-2.91-dev
|
||||
- ninja-build
|
||||
- python3-tomli
|
||||
# Tests dependencies
|
||||
- genisoimage
|
||||
env:
|
||||
- TEST_CMD="make check check-tcg V=1"
|
||||
- CONFIG="--disable-containers --enable-fdt=system
|
||||
--target-list=${MAIN_SYSTEM_TARGETS} --cxx=/bin/false"
|
||||
|
||||
- name: "[ppc64] Clang check-tcg"
|
||||
arch: ppc64le
|
||||
compiler: clang
|
||||
|
|
|
|||
5
COPYING
5
COPYING
|
|
@ -2,7 +2,7 @@
|
|||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
<https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
|
@ -304,8 +304,7 @@ the "copyright" line and a pointer to where the full notice is found.
|
|||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
with this program; if not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
<https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
|
@ -484,8 +484,7 @@ convey the exclusion of warranty; and each file should have at least the
|
|||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
License along with this library; if not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
|
|
|
|||
245
MAINTAINERS
245
MAINTAINERS
|
|
@ -112,6 +112,7 @@ F: hw/intc/s390_flic.c
|
|||
F: hw/intc/s390_flic_kvm.c
|
||||
F: hw/s390x/
|
||||
F: hw/vfio/ap.c
|
||||
F: hw/s390x/ap-stub.c
|
||||
F: hw/vfio/ccw.c
|
||||
F: hw/watchdog/wdt_diag288.c
|
||||
F: include/hw/s390x/
|
||||
|
|
@ -168,7 +169,7 @@ F: include/exec/helper*.h.inc
|
|||
F: include/exec/helper-info.c.inc
|
||||
F: include/exec/page-protection.h
|
||||
F: include/system/tcg.h
|
||||
F: include/accel/tcg/cpu-ops.h
|
||||
F: include/accel/tcg/
|
||||
F: host/include/*/host/cpuinfo.h
|
||||
F: util/cpuinfo-*.c
|
||||
F: include/tcg/
|
||||
|
|
@ -211,7 +212,7 @@ L: qemu-arm@nongnu.org
|
|||
S: Maintained
|
||||
F: hw/arm/smmu*
|
||||
F: include/hw/arm/smmu*
|
||||
F: tests/avocado/smmu.py
|
||||
F: tests/functional/test_aarch64_smmu.py
|
||||
|
||||
AVR TCG CPUs
|
||||
M: Michael Rolnik <mrolnik@gmail.com>
|
||||
|
|
@ -219,7 +220,7 @@ S: Maintained
|
|||
F: docs/system/target-avr.rst
|
||||
F: gdb-xml/avr-cpu.xml
|
||||
F: target/avr/
|
||||
F: tests/functional/test_avr_mega2560.py
|
||||
F: tests/functional/test_avr_*.py
|
||||
|
||||
Hexagon TCG CPUs
|
||||
M: Brian Cain <brian.cain@oss.qualcomm.com>
|
||||
|
|
@ -294,7 +295,7 @@ F: tests/tcg/openrisc/
|
|||
|
||||
PowerPC TCG CPUs
|
||||
M: Nicholas Piggin <npiggin@gmail.com>
|
||||
M: Daniel Henrique Barboza <danielhb413@gmail.com>
|
||||
R: Chinmay Rath <rathc@linux.ibm.com>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: target/ppc/
|
||||
|
|
@ -328,6 +329,7 @@ F: include/hw/char/riscv_htif.h
|
|||
F: include/hw/riscv/
|
||||
F: linux-user/host/riscv32/
|
||||
F: linux-user/host/riscv64/
|
||||
F: common-user/host/riscv*
|
||||
F: tests/functional/test_riscv*
|
||||
F: tests/tcg/riscv64/
|
||||
|
||||
|
|
@ -350,7 +352,7 @@ F: target/riscv/insn_trans/trans_xventanacondops.c.inc
|
|||
F: disas/riscv-xventana*
|
||||
|
||||
RENESAS RX CPUs
|
||||
R: Yoshinori Sato <ysato@users.sourceforge.jp>
|
||||
R: Yoshinori Sato <yoshinori.sato@nifty.com>
|
||||
S: Orphan
|
||||
F: target/rx/
|
||||
|
||||
|
|
@ -366,7 +368,7 @@ F: tests/tcg/s390x/
|
|||
L: qemu-s390x@nongnu.org
|
||||
|
||||
SH4 TCG CPUs
|
||||
R: Yoshinori Sato <ysato@users.sourceforge.jp>
|
||||
R: Yoshinori Sato <yoshinori.sato@nifty.com>
|
||||
S: Orphan
|
||||
F: target/sh4/
|
||||
F: hw/sh4/
|
||||
|
|
@ -450,13 +452,15 @@ F: target/mips/system/
|
|||
|
||||
PPC KVM CPUs
|
||||
M: Nicholas Piggin <npiggin@gmail.com>
|
||||
R: Daniel Henrique Barboza <danielhb413@gmail.com>
|
||||
R: Harsh Prateek Bora <harshpb@linux.ibm.com>
|
||||
S: Odd Fixes
|
||||
F: target/ppc/kvm.c
|
||||
|
||||
S390 KVM CPUs
|
||||
M: Halil Pasic <pasic@linux.ibm.com>
|
||||
M: Christian Borntraeger <borntraeger@linux.ibm.com>
|
||||
R: Eric Farman <farman@linux.ibm.com>
|
||||
R: Matthew Rosato <mjrosato@linux.ibm.com>
|
||||
S: Supported
|
||||
F: target/s390x/kvm/
|
||||
F: target/s390x/machine.c
|
||||
|
|
@ -475,6 +479,7 @@ F: docs/system/i386/sgx.rst
|
|||
F: target/i386/kvm/
|
||||
F: target/i386/sev*
|
||||
F: scripts/kvm/vmxcap
|
||||
F: tests/functional/test_x86_64_hotplug_cpu.py
|
||||
|
||||
Xen emulation on X86 KVM CPUs
|
||||
M: David Woodhouse <dwmw2@infradead.org>
|
||||
|
|
@ -490,25 +495,29 @@ Guest CPU Cores (other accelerators)
|
|||
Overall
|
||||
M: Richard Henderson <richard.henderson@linaro.org>
|
||||
R: Paolo Bonzini <pbonzini@redhat.com>
|
||||
R: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
S: Maintained
|
||||
F: include/exec/cpu*.h
|
||||
F: include/exec/exec-all.h
|
||||
F: include/exec/target_long.h
|
||||
F: include/qemu/accel.h
|
||||
F: include/system/accel-*.h
|
||||
F: include/system/cpus.h
|
||||
F: include/accel/accel-cpu-target.h
|
||||
F: accel/accel-*.c
|
||||
F: include/accel/accel-*.h
|
||||
F: accel/accel-*.?
|
||||
F: accel/dummy-cpus.?
|
||||
F: accel/Makefile.objs
|
||||
F: accel/stubs/Makefile.objs
|
||||
F: cpu-common.c
|
||||
F: cpu-target.c
|
||||
F: qapi/accelerator.json
|
||||
F: system/cpus.c
|
||||
|
||||
Apple Silicon HVF CPUs
|
||||
M: Alexander Graf <agraf@csgraf.de>
|
||||
R: Mads Ynddal <mads@ynddal.dk>
|
||||
S: Maintained
|
||||
F: target/arm/hvf/
|
||||
F: target/arm/hvf-stub.c
|
||||
|
||||
X86 HVF CPUs
|
||||
M: Cameron Esfahani <dirty@apple.com>
|
||||
|
|
@ -522,9 +531,11 @@ HVF
|
|||
M: Cameron Esfahani <dirty@apple.com>
|
||||
M: Roman Bolshakov <rbolshakov@ddn.com>
|
||||
R: Phil Dennis-Jordan <phil@philjordan.eu>
|
||||
R: Mads Ynddal <mads@ynddal.dk>
|
||||
W: https://wiki.qemu.org/Features/HVF
|
||||
S: Maintained
|
||||
F: accel/hvf/
|
||||
F: accel/stubs/hvf-stub.c
|
||||
F: include/system/hvf.h
|
||||
F: include/system/hvf_int.h
|
||||
|
||||
|
|
@ -532,8 +543,17 @@ WHPX CPUs
|
|||
M: Sunil Muthuswamy <sunilmut@microsoft.com>
|
||||
S: Supported
|
||||
F: target/i386/whpx/
|
||||
F: accel/stubs/whpx-stub.c
|
||||
F: include/system/whpx.h
|
||||
|
||||
X86 Instruction Emulator
|
||||
M: Cameron Esfahani <dirty@apple.com>
|
||||
M: Roman Bolshakov <rbolshakov@ddn.com>
|
||||
R: Phil Dennis-Jordan <phil@philjordan.eu>
|
||||
R: Wei Liu <wei.liu@kernel.org>
|
||||
S: Maintained
|
||||
F: target/i386/emulate/
|
||||
|
||||
Guest CPU Cores (Xen)
|
||||
---------------------
|
||||
X86 Xen CPUs
|
||||
|
|
@ -570,6 +590,7 @@ NetBSD Virtual Machine Monitor (NVMM) CPU support
|
|||
M: Reinoud Zandijk <reinoud@netbsd.org>
|
||||
S: Maintained
|
||||
F: include/system/nvmm.h
|
||||
F: accel/stubs/nvmm-stub.c
|
||||
F: target/i386/nvmm/
|
||||
|
||||
Hosts
|
||||
|
|
@ -619,6 +640,15 @@ F: .gitlab-ci.d/cirrus/macos-*
|
|||
F: */*.m
|
||||
F: scripts/entitlement.sh
|
||||
|
||||
WebAssembly
|
||||
M: Kohei Tokunaga <ktokunaga.mail@gmail.com>
|
||||
S: Maintained
|
||||
F: include/system/os-wasm.h
|
||||
F: os-wasm.c
|
||||
F: util/coroutine-wasm.c
|
||||
F: configs/meson/emscripten.txt
|
||||
F: tests/docker/dockerfiles/emsdk-wasm32-cross.docker
|
||||
|
||||
Alpha Machines
|
||||
--------------
|
||||
M: Richard Henderson <richard.henderson@linaro.org>
|
||||
|
|
@ -626,6 +656,7 @@ S: Maintained
|
|||
F: hw/alpha/
|
||||
F: hw/isa/smc37c669-superio.c
|
||||
F: tests/tcg/alpha/system/
|
||||
F: tests/functional/test_alpha_clipper.py
|
||||
|
||||
ARM Machines
|
||||
------------
|
||||
|
|
@ -713,6 +744,16 @@ F: include/hw/timer/armv7m_systick.h
|
|||
F: include/hw/misc/armv7m_ras.h
|
||||
F: tests/qtest/test-arm-mptimer.c
|
||||
|
||||
Bananapi M2U
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: docs/system/arm/bananapi_m2u.rst
|
||||
F: hw/*/allwinner-r40*.c
|
||||
F: hw/arm/bananapi_m2u.c
|
||||
F: include/hw/*/allwinner-r40*.h
|
||||
F: tests/functional/test_arm_bpim2u.py
|
||||
|
||||
B-L475E-IOT01A IoT Node
|
||||
M: Samuel Tardieu <sam@rfc1149.net>
|
||||
L: qemu-arm@nongnu.org
|
||||
|
|
@ -833,6 +874,7 @@ F: include/hw/arm/fsl-imx8mp.h
|
|||
F: include/hw/misc/imx8mp_*.h
|
||||
F: include/hw/pci-host/fsl_imx8m_phy.h
|
||||
F: docs/system/arm/imx8mp-evk.rst
|
||||
F: tests/functional/test_aarch64_imx8mp_evk.py
|
||||
F: tests/qtest/rs5c372-test.c
|
||||
|
||||
MPS2 / MPS3
|
||||
|
|
@ -922,6 +964,7 @@ F: hw/cpu/realview_mpcore.c
|
|||
F: hw/intc/realview_gic.c
|
||||
F: include/hw/intc/realview_gic.h
|
||||
F: docs/system/arm/realview.rst
|
||||
F: tests/functional/test_arm_realview.py
|
||||
|
||||
SABRELITE / i.MX6
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
|
|
@ -950,7 +993,7 @@ F: hw/misc/sbsa_ec.c
|
|||
F: hw/watchdog/sbsa_gwdt.c
|
||||
F: include/hw/watchdog/sbsa_gwdt.h
|
||||
F: docs/system/arm/sbsa.rst
|
||||
F: tests/functional/test_aarch64_sbsaref*.py
|
||||
F: tests/functional/test_aarch64_*sbsaref*.py
|
||||
|
||||
Sharp SL-5500 (Collie) PDA
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
|
|
@ -969,9 +1012,10 @@ L: qemu-arm@nongnu.org
|
|||
S: Odd Fixes
|
||||
F: hw/*/stellaris*
|
||||
F: hw/display/ssd03*
|
||||
F: include/hw/input/gamepad.h
|
||||
F: include/hw/input/stellaris_gamepad.h
|
||||
F: include/hw/timer/stellaris-gptm.h
|
||||
F: docs/system/arm/stellaris.rst
|
||||
F: tests/functional/test_arm_stellaris.py
|
||||
|
||||
STM32L4x5 SoC Family
|
||||
M: Samuel Tardieu <sam@rfc1149.net>
|
||||
|
|
@ -1019,9 +1063,10 @@ S: Maintained
|
|||
F: hw/arm/virt*
|
||||
F: include/hw/arm/virt.h
|
||||
F: docs/system/arm/virt.rst
|
||||
F: tests/functional/test_aarch64_virt*.py
|
||||
F: tests/functional/test_aarch64_*virt*.py
|
||||
F: tests/functional/test_aarch64_tuxrun.py
|
||||
F: tests/functional/test_arm_tuxrun.py
|
||||
F: tests/functional/test_arm_virt.py
|
||||
|
||||
Xilinx Zynq
|
||||
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
|
||||
|
|
@ -1163,6 +1208,7 @@ F: docs/system/arm/fby35.rst
|
|||
F: tests/*/*aspeed*
|
||||
F: tests/*/*ast2700*
|
||||
F: hw/arm/fby35.c
|
||||
F: pc-bios/ast27x0_bootrom.bin
|
||||
|
||||
NRF51
|
||||
M: Joel Stanley <joel@jms.id.au>
|
||||
|
|
@ -1201,6 +1247,7 @@ Arduino
|
|||
M: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
S: Maintained
|
||||
F: hw/avr/arduino.c
|
||||
F: tests/functional/test_avr_uno.py
|
||||
|
||||
HP-PARISC Machines
|
||||
------------------
|
||||
|
|
@ -1262,6 +1309,7 @@ F: hw/m68k/mcf_intc.c
|
|||
F: hw/char/mcf_uart.c
|
||||
F: hw/net/mcf_fec.c
|
||||
F: include/hw/m68k/mcf*.h
|
||||
F: tests/functional/test_m68k_mcf5208evb.py
|
||||
|
||||
NeXTcube
|
||||
M: Thomas Huth <huth@tuxfamily.org>
|
||||
|
|
@ -1355,7 +1403,6 @@ F: hw/acpi/piix4.c
|
|||
F: hw/mips/malta.c
|
||||
F: hw/pci-host/gt64120.c
|
||||
F: include/hw/southbridge/piix.h
|
||||
F: tests/avocado/linux_ssh_mips_malta.py
|
||||
F: tests/functional/test_mips*_malta.py
|
||||
F: tests/functional/test_mips*_tuxrun.py
|
||||
|
||||
|
|
@ -1407,6 +1454,7 @@ S: Maintained
|
|||
F: docs/system/openrisc/or1k-sim.rst
|
||||
F: hw/intc/ompic.c
|
||||
F: hw/openrisc/openrisc_sim.c
|
||||
F: tests/functional/test_or1k_sim.py
|
||||
|
||||
PowerPC Machines
|
||||
----------------
|
||||
|
|
@ -1502,7 +1550,7 @@ F: tests/functional/test_ppc_40p.py
|
|||
|
||||
sPAPR (pseries)
|
||||
M: Nicholas Piggin <npiggin@gmail.com>
|
||||
R: Daniel Henrique Barboza <danielhb413@gmail.com>
|
||||
M: Harsh Prateek Bora <harshpb@linux.ibm.com>
|
||||
R: Harsh Prateek Bora <harshpb@linux.ibm.com>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Odd Fixes
|
||||
|
|
@ -1527,7 +1575,7 @@ F: tests/functional/test_ppc64_tuxrun.py
|
|||
|
||||
PowerNV (Non-Virtualized)
|
||||
M: Nicholas Piggin <npiggin@gmail.com>
|
||||
R: Frédéric Barrat <fbarrat@linux.ibm.com>
|
||||
R: Aditya Gupta <adityag@linux.ibm.com>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: docs/system/ppc/powernv.rst
|
||||
|
|
@ -1569,7 +1617,7 @@ F: hw/pci-host/ppc440_pcix.c
|
|||
F: hw/display/sm501*
|
||||
F: hw/ide/sii3112.c
|
||||
F: hw/rtc/m41t80.c
|
||||
F: pc-bios/canyonlands.dt[sb]
|
||||
F: pc-bios/dtb/canyonlands.dt[sb]
|
||||
F: pc-bios/u-boot-sam460ex-20100605.bin
|
||||
F: roms/u-boot-sam460ex
|
||||
F: docs/system/ppc/amigang.rst
|
||||
|
|
@ -1647,6 +1695,7 @@ S: Supported
|
|||
F: docs/system/riscv/sifive_u.rst
|
||||
F: hw/*/*sifive*.c
|
||||
F: include/hw/*/*sifive*.h
|
||||
F: tests/functional/test_riscv64_sifive_u.py
|
||||
|
||||
AMD Microblaze-V Generic Board
|
||||
M: Sai Pavan Boddu <sai.pavan.boddu@amd.com>
|
||||
|
|
@ -1654,10 +1703,17 @@ S: Maintained
|
|||
F: hw/riscv/microblaze-v-generic.c
|
||||
F: docs/system/riscv/microblaze-v-generic.rst
|
||||
|
||||
Xiangshan Kunminghu
|
||||
M: Ran Wang <wangran@bosc.ac.cn>
|
||||
S: Maintained
|
||||
F: docs/system/riscv/xiangshan-kunminghu.rst
|
||||
F: hw/riscv/xiangshan_kmh.c
|
||||
F: include/hw/riscv/xiangshan_kmh.h
|
||||
|
||||
RX Machines
|
||||
-----------
|
||||
rx-gdbsim
|
||||
R: Yoshinori Sato <ysato@users.sourceforge.jp>
|
||||
R: Yoshinori Sato <yoshinori.sato@nifty.com>
|
||||
S: Orphan
|
||||
F: docs/system/target-rx.rst
|
||||
F: hw/rx/rx-gdbsim.c
|
||||
|
|
@ -1666,7 +1722,7 @@ F: tests/functional/test_rx_gdbsim.py
|
|||
SH4 Machines
|
||||
------------
|
||||
R2D
|
||||
R: Yoshinori Sato <ysato@users.sourceforge.jp>
|
||||
R: Yoshinori Sato <yoshinori.sato@nifty.com>
|
||||
R: Magnus Damm <magnus.damm@gmail.com>
|
||||
S: Odd Fixes
|
||||
F: hw/char/sh_serial.c
|
||||
|
|
@ -1732,6 +1788,7 @@ S390 Virtio-ccw
|
|||
M: Halil Pasic <pasic@linux.ibm.com>
|
||||
M: Christian Borntraeger <borntraeger@linux.ibm.com>
|
||||
M: Eric Farman <farman@linux.ibm.com>
|
||||
R: Matthew Rosato <mjrosato@linux.ibm.com>
|
||||
S: Supported
|
||||
F: hw/s390x/
|
||||
F: include/hw/s390x/
|
||||
|
|
@ -1743,11 +1800,14 @@ L: qemu-s390x@nongnu.org
|
|||
S390-ccw boot
|
||||
M: Christian Borntraeger <borntraeger@linux.ibm.com>
|
||||
M: Thomas Huth <thuth@redhat.com>
|
||||
R: Jared Rossi <jrossi@linux.ibm.com>
|
||||
R: Zhuoying Cai <zycai@linux.ibm.com>
|
||||
S: Supported
|
||||
F: hw/s390x/ipl.*
|
||||
F: pc-bios/s390-ccw/
|
||||
F: pc-bios/s390-ccw.img
|
||||
F: docs/devel/s390-dasd-ipl.rst
|
||||
F: tests/functional/test_s390x_pxelinux.py
|
||||
T: git https://github.com/borntraeger/qemu.git s390-next
|
||||
L: qemu-s390x@nongnu.org
|
||||
|
||||
|
|
@ -1763,6 +1823,7 @@ S390 channel subsystem
|
|||
M: Halil Pasic <pasic@linux.ibm.com>
|
||||
M: Christian Borntraeger <borntraeger@linux.ibm.com>
|
||||
M: Eric Farman <farman@linux.ibm.com>
|
||||
R: Farhan Ali <alifm@linux.ibm.com>
|
||||
S: Supported
|
||||
F: hw/s390x/ccw-device.[ch]
|
||||
F: hw/s390x/css.c
|
||||
|
|
@ -1783,6 +1844,7 @@ L: qemu-s390x@nongnu.org
|
|||
S390 SCLP-backed devices
|
||||
M: Halil Pasic <pasic@linux.ibm.com>
|
||||
M: Christian Borntraeger <borntraeger@linux.ibm.com>
|
||||
R: Jason Herne <jjherne@linux.ibm.com>
|
||||
S: Supported
|
||||
F: include/hw/s390x/event-facility.h
|
||||
F: include/hw/s390x/sclp.h
|
||||
|
|
@ -1828,6 +1890,7 @@ F: include/hw/isa/apm.h
|
|||
F: tests/unit/test-x86-topo.c
|
||||
F: tests/qtest/test-x86-cpuid-compat.c
|
||||
F: tests/functional/test_i386_tuxrun.py
|
||||
F: tests/functional/test_linux_initrd.py
|
||||
F: tests/functional/test_mem_addr_space.py
|
||||
F: tests/functional/test_pc_cpu_hotplug_props.py
|
||||
F: tests/functional/test_x86_64_tuxrun.py
|
||||
|
|
@ -1903,7 +1966,6 @@ F: hw/core/numa.c
|
|||
F: hw/cpu/cluster.c
|
||||
F: qapi/machine.json
|
||||
F: qapi/machine-common.json
|
||||
F: qapi/machine-target.json
|
||||
F: include/hw/boards.h
|
||||
F: include/hw/core/cpu.h
|
||||
F: include/hw/cpu/cluster.h
|
||||
|
|
@ -1913,6 +1975,13 @@ F: tests/functional/test_empty_cpu_model.py
|
|||
F: tests/unit/test-smp-parse.c
|
||||
T: git https://gitlab.com/ehabkost/qemu.git machine-next
|
||||
|
||||
TargetInfo API
|
||||
M: Pierrick Bouvier <pierrick.bouvier@linaro.org>
|
||||
M: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
S: Supported
|
||||
F: include/qemu/target-info*.h
|
||||
F: target-info*.c
|
||||
|
||||
Xtensa Machines
|
||||
---------------
|
||||
sim
|
||||
|
|
@ -2027,6 +2096,7 @@ F: hw/pci-bridge/*
|
|||
F: qapi/pci.json
|
||||
F: docs/pci*
|
||||
F: docs/specs/*pci*
|
||||
F: docs/system/sriov.rst
|
||||
|
||||
PCIE DOE
|
||||
M: Huai-Cheng Kuo <hchkuo@avery-design.com.tw>
|
||||
|
|
@ -2035,6 +2105,12 @@ S: Supported
|
|||
F: include/hw/pci/pcie_doe.h
|
||||
F: hw/pci/pcie_doe.c
|
||||
|
||||
ARM PCI Hotplug
|
||||
M: Gustavo Romero <gustavo.romero@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Supported
|
||||
F: tests/functional/test_aarch64_hotplug_pci.py
|
||||
|
||||
ACPI/SMBIOS
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
M: Igor Mammedov <imammedo@redhat.com>
|
||||
|
|
@ -2074,13 +2150,13 @@ S: Supported
|
|||
F: hw/acpi/viot.c
|
||||
F: hw/acpi/viot.h
|
||||
|
||||
ACPI/AVOCADO/BIOSBITS
|
||||
ACPI/FUNCTIONAL/BIOSBITS
|
||||
M: Ani Sinha <anisinha@redhat.com>
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
S: Supported
|
||||
F: tests/functional/acpi-bits/*
|
||||
F: tests/functional/test_acpi_bits.py
|
||||
F: docs/devel/acpi-bits.rst
|
||||
F: docs/devel/testing/acpi-bits.rst
|
||||
|
||||
ACPI/HEST/GHES
|
||||
R: Dongjiu Geng <gengdongjiu1@gmail.com>
|
||||
|
|
@ -2117,7 +2193,7 @@ F: hw/net/
|
|||
F: include/hw/net/
|
||||
F: tests/qtest/virtio-net-test.c
|
||||
F: tests/functional/test_info_usernet.py
|
||||
F: docs/virtio-net-failover.rst
|
||||
F: docs/system/virtio-net-failover.rst
|
||||
T: git https://github.com/jasowang/qemu.git net
|
||||
|
||||
Parallel NOR Flash devices
|
||||
|
|
@ -2168,7 +2244,6 @@ F: tests/qtest/sdhci-test.c
|
|||
USB
|
||||
S: Orphan
|
||||
F: hw/usb/*
|
||||
F: stubs/usb-dev-stub.c
|
||||
F: tests/qtest/usb-*-test.c
|
||||
F: docs/system/devices/usb.rst
|
||||
F: include/hw/usb.h
|
||||
|
|
@ -2184,6 +2259,7 @@ M: Alex Williamson <alex.williamson@redhat.com>
|
|||
M: Cédric Le Goater <clg@redhat.com>
|
||||
S: Supported
|
||||
F: hw/vfio/*
|
||||
F: util/vfio-helpers.c
|
||||
F: include/hw/vfio/
|
||||
F: docs/devel/migration/vfio.rst
|
||||
F: qapi/vfio.json
|
||||
|
|
@ -2247,6 +2323,7 @@ F: include/*/vhost*
|
|||
F: subprojects/libvhost-user/
|
||||
F: block/export/vhost-user*
|
||||
F: util/vhost-user-server.c
|
||||
F: net/vhost*
|
||||
|
||||
vhost-shadow-virtqueue
|
||||
R: Eugenio Pérez <eperezma@redhat.com>
|
||||
|
|
@ -2420,9 +2497,8 @@ S: Supported
|
|||
F: hw/s390x/virtio-ccw-md.c
|
||||
F: hw/s390x/virtio-ccw-md.h
|
||||
F: hw/s390x/virtio-ccw-md-stubs.c
|
||||
F: hw/virtio/virtio-md-pci.c
|
||||
F: hw/virtio/virtio-md-*.c
|
||||
F: include/hw/virtio/virtio-md-pci.h
|
||||
F: stubs/virtio-md-pci.c
|
||||
|
||||
virtio-mem
|
||||
M: David Hildenbrand <david@redhat.com>
|
||||
|
|
@ -2474,17 +2550,17 @@ F: tests/qtest/fuzz-megasas-test.c
|
|||
|
||||
Network packet abstractions
|
||||
M: Dmitry Fleytman <dmitry.fleytman@gmail.com>
|
||||
R: Akihiko Odaki <akihiko.odaki@daynix.com>
|
||||
R: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
|
||||
S: Maintained
|
||||
F: include/net/eth.h
|
||||
F: net/eth.c
|
||||
F: hw/net/net_rx_pkt*
|
||||
F: hw/net/net_tx_pkt*
|
||||
|
||||
Vmware
|
||||
VMware
|
||||
M: Dmitry Fleytman <dmitry.fleytman@gmail.com>
|
||||
S: Maintained
|
||||
F: docs/specs/vmw_pvscsi-spec.txt
|
||||
F: docs/specs/vmw_pvscsi-spec.rst
|
||||
F: hw/display/vmware_vga.c
|
||||
F: hw/net/vmxnet*
|
||||
F: hw/scsi/vmw_pvscsi*
|
||||
|
|
@ -2492,7 +2568,7 @@ F: pc-bios/efi-vmxnet3.rom
|
|||
F: pc-bios/vgabios-vmware.bin
|
||||
F: roms/config.vga-vmware
|
||||
F: tests/qtest/vmxnet3-test.c
|
||||
F: docs/specs/vwm_pvscsi-spec.rst
|
||||
F: docs/specs/vmw_pvscsi-spec.rst
|
||||
|
||||
Rocker
|
||||
M: Jiri Pirko <jiri@resnulli.us>
|
||||
|
|
@ -2504,13 +2580,13 @@ F: docs/specs/rocker.rst
|
|||
|
||||
e1000x
|
||||
M: Dmitry Fleytman <dmitry.fleytman@gmail.com>
|
||||
R: Akihiko Odaki <akihiko.odaki@daynix.com>
|
||||
R: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
|
||||
S: Maintained
|
||||
F: hw/net/e1000x*
|
||||
|
||||
e1000e
|
||||
M: Dmitry Fleytman <dmitry.fleytman@gmail.com>
|
||||
R: Akihiko Odaki <akihiko.odaki@daynix.com>
|
||||
R: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
|
||||
S: Maintained
|
||||
F: hw/net/e1000e*
|
||||
F: tests/qtest/fuzz-e1000e-test.c
|
||||
|
|
@ -2518,9 +2594,9 @@ F: tests/qtest/e1000e-test.c
|
|||
F: tests/qtest/libqos/e1000e.*
|
||||
|
||||
igb
|
||||
M: Akihiko Odaki <akihiko.odaki@daynix.com>
|
||||
M: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
|
||||
R: Sriram Yagnaraman <sriram.yagnaraman@ericsson.com>
|
||||
S: Maintained
|
||||
S: Odd Fixes
|
||||
F: docs/system/devices/igb.rst
|
||||
F: hw/net/igb*
|
||||
F: tests/functional/test_netdev_ethtool.py
|
||||
|
|
@ -2623,7 +2699,10 @@ F: hw/display/ramfb*.c
|
|||
F: include/hw/display/ramfb.h
|
||||
|
||||
virtio-gpu
|
||||
S: Orphan
|
||||
M: Alex Bennée <alex.bennee@linaro.org>
|
||||
R: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
|
||||
R: Dmitry Osipenko <dmitry.osipenko@collabora.com>
|
||||
S: Odd Fixes
|
||||
F: hw/display/virtio-gpu*
|
||||
F: hw/display/virtio-vga.*
|
||||
F: include/hw/virtio/virtio-gpu.h
|
||||
|
|
@ -2689,7 +2768,7 @@ Firmware configuration (fw_cfg)
|
|||
M: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
R: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Supported
|
||||
F: docs/specs/fw_cfg.txt
|
||||
F: docs/specs/fw_cfg.rst
|
||||
F: hw/nvram/fw_cfg*.c
|
||||
F: stubs/fw_cfg.c
|
||||
F: include/hw/nvram/fw_cfg.h
|
||||
|
|
@ -2699,7 +2778,7 @@ F: tests/qtest/fw_cfg-test.c
|
|||
T: git https://github.com/philmd/qemu.git fw_cfg-next
|
||||
|
||||
XIVE
|
||||
R: Frédéric Barrat <fbarrat@linux.ibm.com>
|
||||
R: Gautam Menghani <gautam@linux.ibm.com>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: hw/*/*xive*
|
||||
|
|
@ -2708,7 +2787,7 @@ F: tests/qtest/*xive*
|
|||
F: docs/*/*xive*
|
||||
|
||||
Renesas peripherals
|
||||
R: Yoshinori Sato <ysato@users.sourceforge.jp>
|
||||
R: Yoshinori Sato <yoshinori.sato@nifty.com>
|
||||
R: Magnus Damm <magnus.damm@gmail.com>
|
||||
S: Odd Fixes
|
||||
F: hw/char/renesas_sci.c
|
||||
|
|
@ -2720,7 +2799,7 @@ F: include/hw/sh4/sh.h
|
|||
F: include/hw/timer/renesas_*.h
|
||||
|
||||
Renesas RX peripherals
|
||||
R: Yoshinori Sato <ysato@users.sourceforge.jp>
|
||||
R: Yoshinori Sato <yoshinori.sato@nifty.com>
|
||||
S: Orphan
|
||||
F: hw/intc/rx_icu.c
|
||||
F: hw/rx/
|
||||
|
|
@ -2761,6 +2840,7 @@ F: include/hw/timer/mips_gictimer.h
|
|||
S390 3270 device
|
||||
M: Halil Pasic <pasic@linux.ibm.com>
|
||||
M: Christian Borntraeger <borntraeger@linux.ibm.com>
|
||||
R: Collin Walling <walling@linux.ibm.com>
|
||||
S: Odd fixes
|
||||
F: include/hw/s390x/3270-ccw.h
|
||||
F: hw/char/terminal3270.c
|
||||
|
|
@ -2770,6 +2850,7 @@ L: qemu-s390x@nongnu.org
|
|||
S390 diag 288 watchdog
|
||||
M: Halil Pasic <pasic@linux.ibm.com>
|
||||
M: Christian Borntraeger <borntraeger@linux.ibm.com>
|
||||
R: Collin Walling <walling@linux.ibm.com>
|
||||
S: Supported
|
||||
F: hw/watchdog/wdt_diag288.c
|
||||
F: include/hw/watchdog/wdt_diag288.h
|
||||
|
|
@ -2778,6 +2859,7 @@ L: qemu-s390x@nongnu.org
|
|||
S390 storage key device
|
||||
M: Halil Pasic <pasic@linux.ibm.com>
|
||||
M: Christian Borntraeger <borntraeger@linux.ibm.com>
|
||||
R: Jason Herne <jjherne@linux.ibm.com>
|
||||
S: Supported
|
||||
F: hw/s390x/storage-keys.h
|
||||
F: hw/s390x/s390-skeys*.c
|
||||
|
|
@ -2786,6 +2868,7 @@ L: qemu-s390x@nongnu.org
|
|||
S390 storage attribute device
|
||||
M: Halil Pasic <pasic@linux.ibm.com>
|
||||
M: Christian Borntraeger <borntraeger@linux.ibm.com>
|
||||
R: Jason Herne <jjherne@linux.ibm.com>
|
||||
S: Supported
|
||||
F: hw/s390x/storage-attributes.h
|
||||
F: hw/s390x/s390-stattrib*.c
|
||||
|
|
@ -2795,6 +2878,7 @@ S390 floating interrupt controller
|
|||
M: Halil Pasic <pasic@linux.ibm.com>
|
||||
M: Christian Borntraeger <borntraeger@linux.ibm.com>
|
||||
M: David Hildenbrand <david@redhat.com>
|
||||
R: Jason Herne <jjherne@linux.ibm.com>
|
||||
S: Supported
|
||||
F: hw/intc/s390_flic*.c
|
||||
F: include/hw/s390x/s390_flic.h
|
||||
|
|
@ -2865,7 +2949,7 @@ Core Audio framework backend
|
|||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
M: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
R: Christian Schoenebeck <qemu_oss@crudebyte.com>
|
||||
R: Akihiko Odaki <akihiko.odaki@daynix.com>
|
||||
R: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
|
||||
S: Odd Fixes
|
||||
F: audio/coreaudio.m
|
||||
|
||||
|
|
@ -2987,6 +3071,16 @@ F: include/qemu/co-shared-resource.h
|
|||
T: git https://gitlab.com/jsnow/qemu.git jobs
|
||||
T: git https://gitlab.com/vsementsov/qemu.git block
|
||||
|
||||
CheckPoint and Restart (CPR)
|
||||
R: Steve Sistare <steven.sistare@oracle.com>
|
||||
S: Supported
|
||||
F: hw/vfio/cpr*
|
||||
F: include/hw/vfio/vfio-cpr.h
|
||||
F: include/migration/cpr.h
|
||||
F: migration/cpr*
|
||||
F: tests/qtest/migration/cpr*
|
||||
F: docs/devel/migration/CPR.rst
|
||||
|
||||
Compute Express Link
|
||||
M: Jonathan Cameron <jonathan.cameron@huawei.com>
|
||||
R: Fan Ni <fan.ni@samsung.com>
|
||||
|
|
@ -3105,18 +3199,19 @@ M: Peter Xu <peterx@redhat.com>
|
|||
M: David Hildenbrand <david@redhat.com>
|
||||
R: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
S: Supported
|
||||
F: include/exec/ioport.h
|
||||
F: include/system/ioport.h
|
||||
F: include/exec/memop.h
|
||||
F: include/exec/memory.h
|
||||
F: include/exec/ram_addr.h
|
||||
F: include/exec/ramblock.h
|
||||
F: include/system/memory.h
|
||||
F: include/system/ram_addr.h
|
||||
F: include/system/ramblock.h
|
||||
F: include/system/memory_mapping.h
|
||||
F: system/dma-helpers.c
|
||||
F: system/ioport.c
|
||||
F: system/memory.c
|
||||
F: system/memory_mapping.c
|
||||
F: system/physmem.c
|
||||
F: include/exec/memory-internal.h
|
||||
F: system/memory-internal.h
|
||||
F: system/ram-block-attributes.c
|
||||
F: scripts/coccinelle/memory-region-housekeeping.cocci
|
||||
|
||||
Memory devices
|
||||
|
|
@ -3124,13 +3219,12 @@ M: David Hildenbrand <david@redhat.com>
|
|||
M: Igor Mammedov <imammedo@redhat.com>
|
||||
R: Xiao Guangrong <xiaoguangrong.eric@gmail.com>
|
||||
S: Supported
|
||||
F: hw/mem/memory-device.c
|
||||
F: hw/mem/memory-device*.c
|
||||
F: hw/mem/nvdimm.c
|
||||
F: hw/mem/pc-dimm.c
|
||||
F: include/hw/mem/memory-device.h
|
||||
F: include/hw/mem/nvdimm.h
|
||||
F: include/hw/mem/pc-dimm.h
|
||||
F: stubs/memory_device.c
|
||||
F: docs/nvdimm.txt
|
||||
|
||||
SPICE
|
||||
|
|
@ -3151,11 +3245,12 @@ F: include/ui/
|
|||
F: qapi/ui.json
|
||||
F: util/drm.c
|
||||
F: docs/devel/ui.rst
|
||||
F: tests/functional/test_vnc.py
|
||||
|
||||
Cocoa graphics
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
M: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
R: Akihiko Odaki <akihiko.odaki@daynix.com>
|
||||
R: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
|
||||
S: Odd Fixes
|
||||
F: ui/cocoa.m
|
||||
|
||||
|
|
@ -3170,9 +3265,9 @@ F: util/qemu-timer*.c
|
|||
F: system/vl.c
|
||||
F: system/main.c
|
||||
F: system/cpus.c
|
||||
F: system/cpu-throttle.c
|
||||
F: system/cpu-timers.c
|
||||
F: system/runstate*
|
||||
F: migration/cpu-throttle.c
|
||||
F: qapi/run-state.json
|
||||
|
||||
Read, Copy, Update (RCU)
|
||||
|
|
@ -3191,7 +3286,7 @@ Human Monitor (HMP)
|
|||
M: Dr. David Alan Gilbert <dave@treblig.org>
|
||||
S: Maintained
|
||||
F: monitor/monitor-internal.h
|
||||
F: monitor/misc.c
|
||||
F: monitor/hmp-target.c
|
||||
F: monitor/monitor.c
|
||||
F: monitor/hmp*
|
||||
F: hmp.h
|
||||
|
|
@ -3311,7 +3406,7 @@ T: git https://repo.or.cz/qemu/armbru.git qapi-next
|
|||
|
||||
QEMU Guest Agent
|
||||
M: Michael Roth <michael.roth@amd.com>
|
||||
M: Konstantin Kostiuk <kkostiuk@redhat.com>
|
||||
M: Kostiantyn Kostiuk <kkostiuk@redhat.com>
|
||||
S: Maintained
|
||||
F: qga/
|
||||
F: contrib/systemd/qemu-guest-agent.service
|
||||
|
|
@ -3322,7 +3417,7 @@ F: tests/*/test-qga*
|
|||
T: git https://github.com/mdroth/qemu.git qga
|
||||
|
||||
QEMU Guest Agent Win32
|
||||
M: Konstantin Kostiuk <kkostiuk@redhat.com>
|
||||
M: Kostiantyn Kostiuk <kkostiuk@redhat.com>
|
||||
S: Maintained
|
||||
F: qga/*win32*
|
||||
F: qga/vss-win32/
|
||||
|
|
@ -3384,8 +3479,8 @@ F: system/qtest.c
|
|||
F: include/system/qtest.h
|
||||
F: accel/qtest/
|
||||
F: tests/qtest/
|
||||
F: docs/devel/qgraph.rst
|
||||
F: docs/devel/qtest.rst
|
||||
F: docs/devel/testing/qgraph.rst
|
||||
F: docs/devel/testing/qtest.rst
|
||||
X: tests/qtest/bios-tables-test*
|
||||
X: tests/qtest/migration-*
|
||||
|
||||
|
|
@ -3403,7 +3498,7 @@ F: tests/qtest/fuzz-*test.c
|
|||
F: tests/docker/test-fuzz
|
||||
F: scripts/oss-fuzz/
|
||||
F: hw/mem/sparse-mem.c
|
||||
F: docs/devel/fuzzing.rst
|
||||
F: docs/devel/testing/fuzzing.rst
|
||||
|
||||
Register API
|
||||
M: Alistair Francis <alistair@alistair23.me>
|
||||
|
|
@ -3418,6 +3513,7 @@ S: Maintained
|
|||
F: rust/qemu-api
|
||||
F: rust/qemu-api-macros
|
||||
F: rust/rustfmt.toml
|
||||
F: scripts/get-wraps-from-cargo-registry.py
|
||||
|
||||
Rust-related patches CC here
|
||||
L: qemu-rust@nongnu.org
|
||||
|
|
@ -3516,6 +3612,7 @@ R: Li Zhijian <lizhijian@fujitsu.com>
|
|||
R: Peter Xu <peterx@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: migration/rdma*
|
||||
F: scripts/rdma-migration-helper.sh
|
||||
|
||||
Migration dirty limit and dirty page rate
|
||||
M: Hyman Huang <yong.huang@smartx.com>
|
||||
|
|
@ -3669,9 +3766,7 @@ F: include/system/replay.h
|
|||
F: docs/devel/replay.rst
|
||||
F: docs/system/replay.rst
|
||||
F: stubs/replay.c
|
||||
F: tests/avocado/replay_kernel.py
|
||||
F: tests/avocado/replay_linux.py
|
||||
F: tests/avocado/reverse_debugging.py
|
||||
F: tests/functional/*reverse_debug*.py
|
||||
F: tests/functional/*replay*.py
|
||||
F: qapi/replay.json
|
||||
|
||||
|
|
@ -3683,7 +3778,7 @@ F: util/iova-tree.c
|
|||
|
||||
elf2dmp
|
||||
M: Viktor Prutyanov <viktor.prutyanov@phystech.edu>
|
||||
R: Akihiko Odaki <akihiko.odaki@daynix.com>
|
||||
R: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
|
||||
S: Maintained
|
||||
F: contrib/elf2dmp/
|
||||
|
||||
|
|
@ -3818,6 +3913,7 @@ F: configs/targets/*linux-user.mak
|
|||
F: scripts/qemu-binfmt-conf.sh
|
||||
F: scripts/update-syscalltbl.sh
|
||||
F: scripts/update-mips-syscall-args.sh
|
||||
F: tests/functional/test_arm_bflt.py
|
||||
|
||||
Tiny Code Generator (TCG)
|
||||
-------------------------
|
||||
|
|
@ -4022,7 +4118,7 @@ M: Stefan Hajnoczi <stefanha@redhat.com>
|
|||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: block/blkverify.c
|
||||
F: docs/devel/blkverify.rst
|
||||
F: docs/devel/testing/blkverify.rst
|
||||
|
||||
bochs
|
||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
|
|
@ -4058,6 +4154,7 @@ M: Stefan Hajnoczi <stefanha@redhat.com>
|
|||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: block/qed.c
|
||||
F: docs/interop/qed_spec.rst
|
||||
|
||||
raw
|
||||
M: Kevin Wolf <kwolf@redhat.com>
|
||||
|
|
@ -4086,7 +4183,7 @@ M: Hanna Reitz <hreitz@redhat.com>
|
|||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: block/qcow2*
|
||||
F: docs/interop/qcow2.txt
|
||||
F: docs/interop/qcow2.rst
|
||||
|
||||
qcow
|
||||
M: Kevin Wolf <kwolf@redhat.com>
|
||||
|
|
@ -4100,7 +4197,7 @@ M: Hanna Reitz <hreitz@redhat.com>
|
|||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: block/blkdebug.c
|
||||
F: docs/devel/blkdebug.rst
|
||||
F: docs/devel/testing/blkdebug.rst
|
||||
|
||||
vpc
|
||||
M: Kevin Wolf <kwolf@redhat.com>
|
||||
|
|
@ -4185,11 +4282,22 @@ F: hw/remote/proxy-memory-listener.c
|
|||
F: include/hw/remote/proxy-memory-listener.h
|
||||
F: hw/remote/iohub.c
|
||||
F: include/hw/remote/iohub.h
|
||||
F: subprojects/libvfio-user
|
||||
F: hw/remote/vfio-user-obj.c
|
||||
F: include/hw/remote/vfio-user-obj.h
|
||||
F: hw/remote/iommu.c
|
||||
F: include/hw/remote/iommu.h
|
||||
F: tests/functional/test_multiprocess.py
|
||||
|
||||
VFIO-USER:
|
||||
M: John Levon <john.levon@nutanix.com>
|
||||
M: Thanos Makatos <thanos.makatos@nutanix.com>
|
||||
M: Cédric Le Goater <clg@redhat.com>
|
||||
S: Supported
|
||||
F: docs/interop/vfio-user.rst
|
||||
F: docs/system/devices/vfio-user.rst
|
||||
F: hw/vfio-user/*
|
||||
F: include/hw/vfio-user/*
|
||||
F: subprojects/libvfio-user
|
||||
|
||||
EBPF:
|
||||
M: Jason Wang <jasowang@redhat.com>
|
||||
|
|
@ -4219,7 +4327,8 @@ F: tests/vm/
|
|||
F: tests/lcitool/
|
||||
F: tests/functional/test_*_tuxrun.py
|
||||
F: scripts/archive-source.sh
|
||||
F: docs/devel/testing.rst
|
||||
F: docs/devel/testing/ci*
|
||||
F: docs/devel/testing/main.rst
|
||||
W: https://gitlab.com/qemu-project/qemu/pipelines
|
||||
W: https://travis-ci.org/qemu/qemu
|
||||
|
||||
|
|
@ -4249,12 +4358,6 @@ R: Philippe Mathieu-Daudé <philmd@linaro.org>
|
|||
S: Maintained
|
||||
F: tests/tcg/Makefile.target
|
||||
|
||||
Integration Testing with the Avocado framework
|
||||
W: https://trello.com/b/6Qi1pxVn/avocado-qemu
|
||||
R: Cleber Rosa <crosa@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: tests/avocado/
|
||||
|
||||
GitLab custom runner (Works On Arm Sponsored)
|
||||
M: Alex Bennée <alex.bennee@linaro.org>
|
||||
M: Philippe Mathieu-Daudé <philmd@linaro.org>
|
||||
|
|
@ -4328,9 +4431,11 @@ M: Peter Maydell <peter.maydell@linaro.org>
|
|||
S: Maintained
|
||||
F: docs/conf.py
|
||||
F: docs/*/conf.py
|
||||
F: docs/requirements.txt
|
||||
F: docs/sphinx/
|
||||
F: docs/_templates/
|
||||
F: docs/devel/docs.rst
|
||||
F: docs/devel/qapi-domain.rst
|
||||
|
||||
Rust build system integration
|
||||
M: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
||||
|
|
|
|||
1
Makefile
1
Makefile
|
|
@ -227,6 +227,7 @@ distclean: clean recurse-distclean
|
|||
rm -Rf .sdk qemu-bundle
|
||||
|
||||
find-src-path = find "$(SRC_PATH)" -path "$(SRC_PATH)/meson" -prune -o \
|
||||
-path "$(SRC_PATH)/.pc" -prune -o \
|
||||
-type l -prune -o \( -name "*.[chsS]" -o -name "*.[ch].inc" \)
|
||||
|
||||
.PHONY: ctags
|
||||
|
|
|
|||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
10.0.3
|
||||
10.1.0
|
||||
|
|
|
|||
144
accel/accel-common.c
Normal file
144
accel/accel-common.c
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* QEMU accel class, components common to system emulation and user mode
|
||||
*
|
||||
* Copyright (c) 2003-2008 Fabrice Bellard
|
||||
* Copyright (c) 2014 Red Hat Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/accel.h"
|
||||
#include "qemu/target-info.h"
|
||||
#include "accel/accel-ops.h"
|
||||
#include "accel/accel-cpu.h"
|
||||
#include "accel/accel-cpu-ops.h"
|
||||
#include "accel-internal.h"
|
||||
|
||||
/* Lookup AccelClass from opt_name. Returns NULL if not found */
|
||||
AccelClass *accel_find(const char *opt_name)
|
||||
{
|
||||
char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name);
|
||||
AccelClass *ac = ACCEL_CLASS(module_object_class_by_name(class_name));
|
||||
g_free(class_name);
|
||||
return ac;
|
||||
}
|
||||
|
||||
/* Return the name of the current accelerator */
|
||||
const char *current_accel_name(void)
|
||||
{
|
||||
AccelClass *ac = ACCEL_GET_CLASS(current_accel());
|
||||
|
||||
return ac->name;
|
||||
}
|
||||
|
||||
static void accel_init_cpu_int_aux(ObjectClass *klass, void *opaque)
|
||||
{
|
||||
CPUClass *cc = CPU_CLASS(klass);
|
||||
AccelCPUClass *accel_cpu = opaque;
|
||||
|
||||
/*
|
||||
* The first callback allows accel-cpu to run initializations
|
||||
* for the CPU, customizing CPU behavior according to the accelerator.
|
||||
*
|
||||
* The second one allows the CPU to customize the accel-cpu
|
||||
* behavior according to the CPU.
|
||||
*
|
||||
* The second is currently only used by TCG, to specialize the
|
||||
* TCGCPUOps depending on the CPU type.
|
||||
*/
|
||||
cc->accel_cpu = accel_cpu;
|
||||
if (accel_cpu->cpu_class_init) {
|
||||
accel_cpu->cpu_class_init(cc);
|
||||
}
|
||||
if (cc->init_accel_cpu) {
|
||||
cc->init_accel_cpu(accel_cpu, cc);
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize the arch-specific accel CpuClass interfaces */
|
||||
static void accel_init_cpu_interfaces(AccelClass *ac)
|
||||
{
|
||||
const char *ac_name; /* AccelClass name */
|
||||
char *acc_name; /* AccelCPUClass name */
|
||||
ObjectClass *acc; /* AccelCPUClass */
|
||||
const char *cpu_resolving_type = target_cpu_type();
|
||||
|
||||
ac_name = object_class_get_name(OBJECT_CLASS(ac));
|
||||
g_assert(ac_name != NULL);
|
||||
|
||||
acc_name = g_strdup_printf("%s-%s", ac_name, cpu_resolving_type);
|
||||
acc = object_class_by_name(acc_name);
|
||||
g_free(acc_name);
|
||||
|
||||
if (acc) {
|
||||
object_class_foreach(accel_init_cpu_int_aux,
|
||||
cpu_resolving_type, false, acc);
|
||||
}
|
||||
}
|
||||
|
||||
void accel_init_interfaces(AccelClass *ac)
|
||||
{
|
||||
accel_init_ops_interfaces(ac);
|
||||
accel_init_cpu_interfaces(ac);
|
||||
}
|
||||
|
||||
void accel_cpu_instance_init(CPUState *cpu)
|
||||
{
|
||||
if (cpu->cc->accel_cpu && cpu->cc->accel_cpu->cpu_instance_init) {
|
||||
cpu->cc->accel_cpu->cpu_instance_init(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
bool accel_cpu_common_realize(CPUState *cpu, Error **errp)
|
||||
{
|
||||
AccelState *accel = current_accel();
|
||||
AccelClass *acc = ACCEL_GET_CLASS(accel);
|
||||
|
||||
/* target specific realization */
|
||||
if (cpu->cc->accel_cpu
|
||||
&& cpu->cc->accel_cpu->cpu_target_realize
|
||||
&& !cpu->cc->accel_cpu->cpu_target_realize(cpu, errp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* generic realization */
|
||||
if (acc->cpu_common_realize && !acc->cpu_common_realize(cpu, errp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void accel_cpu_common_unrealize(CPUState *cpu)
|
||||
{
|
||||
AccelState *accel = current_accel();
|
||||
AccelClass *acc = ACCEL_GET_CLASS(accel);
|
||||
|
||||
/* generic unrealization */
|
||||
if (acc->cpu_common_unrealize) {
|
||||
acc->cpu_common_unrealize(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
int accel_supported_gdbstub_sstep_flags(void)
|
||||
{
|
||||
AccelState *accel = current_accel();
|
||||
AccelClass *acc = ACCEL_GET_CLASS(accel);
|
||||
if (acc->gdbstub_supported_sstep_flags) {
|
||||
return acc->gdbstub_supported_sstep_flags(accel);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const TypeInfo accel_types[] = {
|
||||
{
|
||||
.name = TYPE_ACCEL,
|
||||
.parent = TYPE_OBJECT,
|
||||
.class_size = sizeof(AccelClass),
|
||||
.instance_size = sizeof(AccelState),
|
||||
.abstract = true,
|
||||
},
|
||||
};
|
||||
|
||||
DEFINE_TYPES(accel_types)
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* QEMU System Emulation accel internal functions
|
||||
* QEMU accel internal functions
|
||||
*
|
||||
* Copyright 2021 SUSE LLC
|
||||
*
|
||||
|
|
@ -7,9 +7,11 @@
|
|||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef ACCEL_SYSTEM_H
|
||||
#define ACCEL_SYSTEM_H
|
||||
#ifndef ACCEL_INTERNAL_H
|
||||
#define ACCEL_INTERNAL_H
|
||||
|
||||
void accel_system_init_ops_interfaces(AccelClass *ac);
|
||||
#include "qemu/accel.h"
|
||||
|
||||
void accel_init_ops_interfaces(AccelClass *ac);
|
||||
|
||||
#endif /* ACCEL_SYSTEM_H */
|
||||
35
accel/accel-qmp.c
Normal file
35
accel/accel-qmp.c
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* QMP commands related to accelerators
|
||||
*
|
||||
* Copyright (c) Linaro
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/accel.h"
|
||||
#include "qapi/type-helpers.h"
|
||||
#include "qapi/qapi-commands-accelerator.h"
|
||||
#include "accel/accel-ops.h"
|
||||
#include "accel/accel-cpu-ops.h"
|
||||
#include "hw/core/cpu.h"
|
||||
|
||||
HumanReadableText *qmp_x_accel_stats(Error **errp)
|
||||
{
|
||||
AccelState *accel = current_accel();
|
||||
AccelClass *acc = ACCEL_GET_CLASS(accel);
|
||||
g_autoptr(GString) buf = g_string_new("");
|
||||
|
||||
if (acc->get_stats) {
|
||||
acc->get_stats(accel, buf);
|
||||
}
|
||||
if (acc->ops->get_vcpu_stats) {
|
||||
CPUState *cpu;
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
acc->ops->get_vcpu_stats(cpu, buf);
|
||||
}
|
||||
}
|
||||
|
||||
return human_readable_text_from_str(buf);
|
||||
}
|
||||
|
|
@ -25,11 +25,15 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/accel.h"
|
||||
#include "qapi/qapi-commands-accelerator.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "hw/boards.h"
|
||||
#include "system/accel-ops.h"
|
||||
#include "hw/core/cpu.h"
|
||||
#include "accel/accel-ops.h"
|
||||
#include "accel/accel-cpu-ops.h"
|
||||
#include "system/cpus.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "accel-system.h"
|
||||
#include "accel-internal.h"
|
||||
|
||||
int accel_init_machine(AccelState *accel, MachineState *ms)
|
||||
{
|
||||
|
|
@ -37,7 +41,7 @@ int accel_init_machine(AccelState *accel, MachineState *ms)
|
|||
int ret;
|
||||
ms->accelerator = accel;
|
||||
*(acc->allowed) = true;
|
||||
ret = acc->init_machine(ms);
|
||||
ret = acc->init_machine(accel, ms);
|
||||
if (ret < 0) {
|
||||
ms->accelerator = NULL;
|
||||
*(acc->allowed) = false;
|
||||
|
|
@ -58,12 +62,21 @@ void accel_setup_post(MachineState *ms)
|
|||
AccelState *accel = ms->accelerator;
|
||||
AccelClass *acc = ACCEL_GET_CLASS(accel);
|
||||
if (acc->setup_post) {
|
||||
acc->setup_post(ms, accel);
|
||||
acc->setup_post(accel);
|
||||
}
|
||||
}
|
||||
|
||||
void accel_pre_resume(MachineState *ms, bool step_pending)
|
||||
{
|
||||
AccelState *accel = ms->accelerator;
|
||||
AccelClass *acc = ACCEL_GET_CLASS(accel);
|
||||
if (acc->pre_resume_vm) {
|
||||
acc->pre_resume_vm(accel, step_pending);
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize the arch-independent accel operation interfaces */
|
||||
void accel_system_init_ops_interfaces(AccelClass *ac)
|
||||
void accel_init_ops_interfaces(AccelClass *ac)
|
||||
{
|
||||
const char *ac_name;
|
||||
char *ops_name;
|
||||
|
|
@ -85,17 +98,24 @@ void accel_system_init_ops_interfaces(AccelClass *ac)
|
|||
* non-NULL create_vcpu_thread operation.
|
||||
*/
|
||||
ops = ACCEL_OPS_CLASS(oc);
|
||||
ac->ops = ops;
|
||||
if (ops->ops_init) {
|
||||
ops->ops_init(ops);
|
||||
ops->ops_init(ac);
|
||||
}
|
||||
cpus_register_accel(ops);
|
||||
}
|
||||
|
||||
static void accel_ops_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
monitor_register_hmp_info_hrt("accel", qmp_x_accel_stats);
|
||||
}
|
||||
|
||||
static const TypeInfo accel_ops_type_info = {
|
||||
.name = TYPE_ACCEL_OPS,
|
||||
.parent = TYPE_OBJECT,
|
||||
.abstract = true,
|
||||
.class_size = sizeof(AccelOpsClass),
|
||||
.class_init = accel_ops_class_init,
|
||||
};
|
||||
|
||||
static void accel_system_register_types(void)
|
||||
|
|
|
|||
|
|
@ -24,141 +24,8 @@
|
|||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/accel.h"
|
||||
|
||||
#include "cpu.h"
|
||||
#include "accel/accel-cpu-target.h"
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
#include "accel-system.h"
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
|
||||
static const TypeInfo accel_type = {
|
||||
.name = TYPE_ACCEL,
|
||||
.parent = TYPE_OBJECT,
|
||||
.class_size = sizeof(AccelClass),
|
||||
.instance_size = sizeof(AccelState),
|
||||
.abstract = true,
|
||||
};
|
||||
|
||||
/* Lookup AccelClass from opt_name. Returns NULL if not found */
|
||||
AccelClass *accel_find(const char *opt_name)
|
||||
{
|
||||
char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name);
|
||||
AccelClass *ac = ACCEL_CLASS(module_object_class_by_name(class_name));
|
||||
g_free(class_name);
|
||||
return ac;
|
||||
}
|
||||
|
||||
/* Return the name of the current accelerator */
|
||||
const char *current_accel_name(void)
|
||||
{
|
||||
AccelClass *ac = ACCEL_GET_CLASS(current_accel());
|
||||
|
||||
return ac->name;
|
||||
}
|
||||
|
||||
static void accel_init_cpu_int_aux(ObjectClass *klass, void *opaque)
|
||||
{
|
||||
CPUClass *cc = CPU_CLASS(klass);
|
||||
AccelCPUClass *accel_cpu = opaque;
|
||||
|
||||
/*
|
||||
* The first callback allows accel-cpu to run initializations
|
||||
* for the CPU, customizing CPU behavior according to the accelerator.
|
||||
*
|
||||
* The second one allows the CPU to customize the accel-cpu
|
||||
* behavior according to the CPU.
|
||||
*
|
||||
* The second is currently only used by TCG, to specialize the
|
||||
* TCGCPUOps depending on the CPU type.
|
||||
*/
|
||||
cc->accel_cpu = accel_cpu;
|
||||
if (accel_cpu->cpu_class_init) {
|
||||
accel_cpu->cpu_class_init(cc);
|
||||
}
|
||||
if (cc->init_accel_cpu) {
|
||||
cc->init_accel_cpu(accel_cpu, cc);
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize the arch-specific accel CpuClass interfaces */
|
||||
static void accel_init_cpu_interfaces(AccelClass *ac)
|
||||
{
|
||||
const char *ac_name; /* AccelClass name */
|
||||
char *acc_name; /* AccelCPUClass name */
|
||||
ObjectClass *acc; /* AccelCPUClass */
|
||||
|
||||
ac_name = object_class_get_name(OBJECT_CLASS(ac));
|
||||
g_assert(ac_name != NULL);
|
||||
|
||||
acc_name = g_strdup_printf("%s-%s", ac_name, CPU_RESOLVING_TYPE);
|
||||
acc = object_class_by_name(acc_name);
|
||||
g_free(acc_name);
|
||||
|
||||
if (acc) {
|
||||
object_class_foreach(accel_init_cpu_int_aux,
|
||||
CPU_RESOLVING_TYPE, false, acc);
|
||||
}
|
||||
}
|
||||
|
||||
void accel_init_interfaces(AccelClass *ac)
|
||||
{
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
accel_system_init_ops_interfaces(ac);
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
|
||||
accel_init_cpu_interfaces(ac);
|
||||
}
|
||||
|
||||
void accel_cpu_instance_init(CPUState *cpu)
|
||||
{
|
||||
if (cpu->cc->accel_cpu && cpu->cc->accel_cpu->cpu_instance_init) {
|
||||
cpu->cc->accel_cpu->cpu_instance_init(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
bool accel_cpu_common_realize(CPUState *cpu, Error **errp)
|
||||
{
|
||||
AccelState *accel = current_accel();
|
||||
AccelClass *acc = ACCEL_GET_CLASS(accel);
|
||||
|
||||
/* target specific realization */
|
||||
if (cpu->cc->accel_cpu
|
||||
&& cpu->cc->accel_cpu->cpu_target_realize
|
||||
&& !cpu->cc->accel_cpu->cpu_target_realize(cpu, errp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* generic realization */
|
||||
if (acc->cpu_common_realize && !acc->cpu_common_realize(cpu, errp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void accel_cpu_common_unrealize(CPUState *cpu)
|
||||
{
|
||||
AccelState *accel = current_accel();
|
||||
AccelClass *acc = ACCEL_GET_CLASS(accel);
|
||||
|
||||
/* generic unrealization */
|
||||
if (acc->cpu_common_unrealize) {
|
||||
acc->cpu_common_unrealize(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
int accel_supported_gdbstub_sstep_flags(void)
|
||||
{
|
||||
AccelState *accel = current_accel();
|
||||
AccelClass *acc = ACCEL_GET_CLASS(accel);
|
||||
if (acc->gdbstub_supported_sstep_flags) {
|
||||
return acc->gdbstub_supported_sstep_flags();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const TypeInfo accel_cpu_type = {
|
||||
.name = TYPE_ACCEL_CPU,
|
||||
.parent = TYPE_OBJECT,
|
||||
|
|
@ -168,7 +35,6 @@ static const TypeInfo accel_cpu_type = {
|
|||
|
||||
static void register_accel_types(void)
|
||||
{
|
||||
type_register_static(&accel_type);
|
||||
type_register_static(&accel_cpu_type);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,12 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/accel.h"
|
||||
#include "accel-internal.h"
|
||||
|
||||
void accel_init_ops_interfaces(AccelClass *ac)
|
||||
{
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
AccelState *current_accel(void)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include "qemu/guest-random.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "hw/core/cpu.h"
|
||||
#include "accel/dummy-cpus.h"
|
||||
|
||||
static void *dummy_cpu_thread_fn(void *arg)
|
||||
{
|
||||
|
|
|
|||
14
accel/dummy-cpus.h
Normal file
14
accel/dummy-cpus.h
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Dummy cpu thread code
|
||||
*
|
||||
* Copyright IBM, Corp. 2011
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef ACCEL_DUMMY_CPUS_H
|
||||
#define ACCEL_DUMMY_CPUS_H
|
||||
|
||||
void dummy_start_vcpu_thread(CPUState *cpu);
|
||||
|
||||
#endif
|
||||
|
|
@ -48,18 +48,17 @@
|
|||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/guest-random.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "gdbstub/enums.h"
|
||||
#include "hw/boards.h"
|
||||
#include "system/accel-ops.h"
|
||||
#include "exec/cpu-common.h"
|
||||
#include "hw/core/cpu.h"
|
||||
#include "accel/accel-cpu-ops.h"
|
||||
#include "system/cpus.h"
|
||||
#include "system/hvf.h"
|
||||
#include "system/hvf_int.h"
|
||||
#include "system/runstate.h"
|
||||
#include "qemu/guest-random.h"
|
||||
#include <mach/mach_time.h>
|
||||
|
||||
HVFState *hvf_state;
|
||||
|
||||
|
|
@ -79,138 +78,17 @@ hvf_slot *hvf_find_overlap_slot(uint64_t start, uint64_t size)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
struct mac_slot {
|
||||
int present;
|
||||
uint64_t size;
|
||||
uint64_t gpa_start;
|
||||
uint64_t gva;
|
||||
};
|
||||
|
||||
struct mac_slot mac_slots[32];
|
||||
|
||||
static int do_hvf_set_memory(hvf_slot *slot, hv_memory_flags_t flags)
|
||||
{
|
||||
struct mac_slot *macslot;
|
||||
hv_return_t ret;
|
||||
|
||||
macslot = &mac_slots[slot->slot_id];
|
||||
|
||||
if (macslot->present) {
|
||||
if (macslot->size != slot->size) {
|
||||
macslot->present = 0;
|
||||
ret = hv_vm_unmap(macslot->gpa_start, macslot->size);
|
||||
assert_hvf_ok(ret);
|
||||
}
|
||||
}
|
||||
|
||||
if (!slot->size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
macslot->present = 1;
|
||||
macslot->gpa_start = slot->start;
|
||||
macslot->size = slot->size;
|
||||
ret = hv_vm_map(slot->mem, slot->start, slot->size, flags);
|
||||
assert_hvf_ok(ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hvf_set_phys_mem(MemoryRegionSection *section, bool add)
|
||||
{
|
||||
hvf_slot *mem;
|
||||
MemoryRegion *area = section->mr;
|
||||
bool writable = !area->readonly && !area->rom_device;
|
||||
hv_memory_flags_t flags;
|
||||
uint64_t page_size = qemu_real_host_page_size();
|
||||
|
||||
if (!memory_region_is_ram(area)) {
|
||||
if (writable) {
|
||||
return;
|
||||
} else if (!memory_region_is_romd(area)) {
|
||||
/*
|
||||
* If the memory device is not in romd_mode, then we actually want
|
||||
* to remove the hvf memory slot so all accesses will trap.
|
||||
*/
|
||||
add = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!QEMU_IS_ALIGNED(int128_get64(section->size), page_size) ||
|
||||
!QEMU_IS_ALIGNED(section->offset_within_address_space, page_size)) {
|
||||
/* Not page aligned, so we can not map as RAM */
|
||||
add = false;
|
||||
}
|
||||
|
||||
mem = hvf_find_overlap_slot(
|
||||
section->offset_within_address_space,
|
||||
int128_get64(section->size));
|
||||
|
||||
if (mem && add) {
|
||||
if (mem->size == int128_get64(section->size) &&
|
||||
mem->start == section->offset_within_address_space &&
|
||||
mem->mem == (memory_region_get_ram_ptr(area) +
|
||||
section->offset_within_region)) {
|
||||
return; /* Same region was attempted to register, go away. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Region needs to be reset. set the size to 0 and remap it. */
|
||||
if (mem) {
|
||||
mem->size = 0;
|
||||
if (do_hvf_set_memory(mem, 0)) {
|
||||
error_report("Failed to reset overlapping slot");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
if (!add) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (area->readonly ||
|
||||
(!memory_region_is_ram(area) && memory_region_is_romd(area))) {
|
||||
flags = HV_MEMORY_READ | HV_MEMORY_EXEC;
|
||||
} else {
|
||||
flags = HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC;
|
||||
}
|
||||
|
||||
/* Now make a new slot. */
|
||||
int x;
|
||||
|
||||
for (x = 0; x < hvf_state->num_slots; ++x) {
|
||||
mem = &hvf_state->slots[x];
|
||||
if (!mem->size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (x == hvf_state->num_slots) {
|
||||
error_report("No free slots");
|
||||
abort();
|
||||
}
|
||||
|
||||
mem->size = int128_get64(section->size);
|
||||
mem->mem = memory_region_get_ram_ptr(area) + section->offset_within_region;
|
||||
mem->start = section->offset_within_address_space;
|
||||
mem->region = area;
|
||||
|
||||
if (do_hvf_set_memory(mem, flags)) {
|
||||
error_report("Error registering new memory slot");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void do_hvf_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg)
|
||||
{
|
||||
if (!cpu->accel->dirty) {
|
||||
if (!cpu->vcpu_dirty) {
|
||||
hvf_get_registers(cpu);
|
||||
cpu->accel->dirty = true;
|
||||
cpu->vcpu_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void hvf_cpu_synchronize_state(CPUState *cpu)
|
||||
{
|
||||
if (!cpu->accel->dirty) {
|
||||
if (!cpu->vcpu_dirty) {
|
||||
run_on_cpu(cpu, do_hvf_cpu_synchronize_state, RUN_ON_CPU_NULL);
|
||||
}
|
||||
}
|
||||
|
|
@ -219,7 +97,7 @@ static void do_hvf_cpu_synchronize_set_dirty(CPUState *cpu,
|
|||
run_on_cpu_data arg)
|
||||
{
|
||||
/* QEMU state is the reference, push it to HVF now and on next entry */
|
||||
cpu->accel->dirty = true;
|
||||
cpu->vcpu_dirty = true;
|
||||
}
|
||||
|
||||
static void hvf_cpu_synchronize_post_reset(CPUState *cpu)
|
||||
|
|
@ -237,146 +115,16 @@ static void hvf_cpu_synchronize_pre_loadvm(CPUState *cpu)
|
|||
run_on_cpu(cpu, do_hvf_cpu_synchronize_set_dirty, RUN_ON_CPU_NULL);
|
||||
}
|
||||
|
||||
static void hvf_set_dirty_tracking(MemoryRegionSection *section, bool on)
|
||||
{
|
||||
hvf_slot *slot;
|
||||
|
||||
slot = hvf_find_overlap_slot(
|
||||
section->offset_within_address_space,
|
||||
int128_get64(section->size));
|
||||
|
||||
/* protect region against writes; begin tracking it */
|
||||
if (on) {
|
||||
slot->flags |= HVF_SLOT_LOG;
|
||||
hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size,
|
||||
HV_MEMORY_READ | HV_MEMORY_EXEC);
|
||||
/* stop tracking region*/
|
||||
} else {
|
||||
slot->flags &= ~HVF_SLOT_LOG;
|
||||
hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size,
|
||||
HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC);
|
||||
}
|
||||
}
|
||||
|
||||
static void hvf_log_start(MemoryListener *listener,
|
||||
MemoryRegionSection *section, int old, int new)
|
||||
{
|
||||
if (old != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
hvf_set_dirty_tracking(section, 1);
|
||||
}
|
||||
|
||||
static void hvf_log_stop(MemoryListener *listener,
|
||||
MemoryRegionSection *section, int old, int new)
|
||||
{
|
||||
if (new != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
hvf_set_dirty_tracking(section, 0);
|
||||
}
|
||||
|
||||
static void hvf_log_sync(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
/*
|
||||
* sync of dirty pages is handled elsewhere; just make sure we keep
|
||||
* tracking the region.
|
||||
*/
|
||||
hvf_set_dirty_tracking(section, 1);
|
||||
}
|
||||
|
||||
static void hvf_region_add(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
hvf_set_phys_mem(section, true);
|
||||
}
|
||||
|
||||
static void hvf_region_del(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
hvf_set_phys_mem(section, false);
|
||||
}
|
||||
|
||||
static MemoryListener hvf_memory_listener = {
|
||||
.name = "hvf",
|
||||
.priority = MEMORY_LISTENER_PRIORITY_ACCEL,
|
||||
.region_add = hvf_region_add,
|
||||
.region_del = hvf_region_del,
|
||||
.log_start = hvf_log_start,
|
||||
.log_stop = hvf_log_stop,
|
||||
.log_sync = hvf_log_sync,
|
||||
};
|
||||
|
||||
static void dummy_signal(int sig)
|
||||
{
|
||||
}
|
||||
|
||||
bool hvf_allowed;
|
||||
|
||||
static int hvf_accel_init(MachineState *ms)
|
||||
static void do_hvf_get_vcpu_exec_time(CPUState *cpu, run_on_cpu_data arg)
|
||||
{
|
||||
int x;
|
||||
hv_return_t ret;
|
||||
HVFState *s;
|
||||
int pa_range = 36;
|
||||
MachineClass *mc = MACHINE_GET_CLASS(ms);
|
||||
|
||||
if (mc->hvf_get_physical_address_range) {
|
||||
pa_range = mc->hvf_get_physical_address_range(ms);
|
||||
if (pa_range < 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
ret = hvf_arch_vm_create(ms, (uint32_t)pa_range);
|
||||
assert_hvf_ok(ret);
|
||||
|
||||
s = g_new0(HVFState, 1);
|
||||
|
||||
s->num_slots = ARRAY_SIZE(s->slots);
|
||||
for (x = 0; x < s->num_slots; ++x) {
|
||||
s->slots[x].size = 0;
|
||||
s->slots[x].slot_id = x;
|
||||
}
|
||||
|
||||
QTAILQ_INIT(&s->hvf_sw_breakpoints);
|
||||
|
||||
hvf_state = s;
|
||||
memory_listener_register(&hvf_memory_listener, &address_space_memory);
|
||||
|
||||
return hvf_arch_init();
|
||||
int r = hv_vcpu_get_exec_time(cpu->accel->fd, arg.host_ptr);
|
||||
assert_hvf_ok(r);
|
||||
}
|
||||
|
||||
static inline int hvf_gdbstub_sstep_flags(void)
|
||||
{
|
||||
return SSTEP_ENABLE | SSTEP_NOIRQ;
|
||||
}
|
||||
|
||||
static void hvf_accel_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
AccelClass *ac = ACCEL_CLASS(oc);
|
||||
ac->name = "HVF";
|
||||
ac->init_machine = hvf_accel_init;
|
||||
ac->allowed = &hvf_allowed;
|
||||
ac->gdbstub_supported_sstep_flags = hvf_gdbstub_sstep_flags;
|
||||
}
|
||||
|
||||
static const TypeInfo hvf_accel_type = {
|
||||
.name = TYPE_HVF_ACCEL,
|
||||
.parent = TYPE_ACCEL,
|
||||
.class_init = hvf_accel_class_init,
|
||||
};
|
||||
|
||||
static void hvf_type_init(void)
|
||||
{
|
||||
type_register_static(&hvf_accel_type);
|
||||
}
|
||||
|
||||
type_init(hvf_type_init);
|
||||
|
||||
static void hvf_vcpu_destroy(CPUState *cpu)
|
||||
{
|
||||
hv_return_t ret = hv_vcpu_destroy(cpu->accel->fd);
|
||||
|
|
@ -409,8 +157,8 @@ static int hvf_init_vcpu(CPUState *cpu)
|
|||
#else
|
||||
r = hv_vcpu_create(&cpu->accel->fd, HV_VCPU_DEFAULT);
|
||||
#endif
|
||||
cpu->accel->dirty = true;
|
||||
assert_hvf_ok(r);
|
||||
cpu->vcpu_dirty = true;
|
||||
|
||||
cpu->accel->guest_debug_enabled = false;
|
||||
|
||||
|
|
@ -476,6 +224,34 @@ static void hvf_start_vcpu_thread(CPUState *cpu)
|
|||
cpu, QEMU_THREAD_JOINABLE);
|
||||
}
|
||||
|
||||
struct hvf_sw_breakpoint *hvf_find_sw_breakpoint(CPUState *cpu, vaddr pc)
|
||||
{
|
||||
struct hvf_sw_breakpoint *bp;
|
||||
|
||||
QTAILQ_FOREACH(bp, &hvf_state->hvf_sw_breakpoints, entry) {
|
||||
if (bp->pc == pc) {
|
||||
return bp;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int hvf_sw_breakpoints_active(CPUState *cpu)
|
||||
{
|
||||
return !QTAILQ_EMPTY(&hvf_state->hvf_sw_breakpoints);
|
||||
}
|
||||
|
||||
static void do_hvf_update_guest_debug(CPUState *cpu, run_on_cpu_data arg)
|
||||
{
|
||||
hvf_arch_update_guest_debug(cpu);
|
||||
}
|
||||
|
||||
int hvf_update_guest_debug(CPUState *cpu)
|
||||
{
|
||||
run_on_cpu(cpu, do_hvf_update_guest_debug, RUN_ON_CPU_NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hvf_insert_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len)
|
||||
{
|
||||
struct hvf_sw_breakpoint *bp;
|
||||
|
|
@ -578,12 +354,28 @@ static void hvf_remove_all_breakpoints(CPUState *cpu)
|
|||
}
|
||||
}
|
||||
|
||||
static void hvf_accel_ops_class_init(ObjectClass *oc, void *data)
|
||||
static void hvf_get_vcpu_stats(CPUState *cpu, GString *buf)
|
||||
{
|
||||
uint64_t time_mach; /* units of mach_absolute_time() */
|
||||
|
||||
run_on_cpu(cpu, do_hvf_get_vcpu_exec_time, RUN_ON_CPU_HOST_PTR(&time_mach));
|
||||
|
||||
mach_timebase_info_data_t timebase;
|
||||
mach_timebase_info(&timebase);
|
||||
uint64_t time_ns = time_mach * timebase.numer / timebase.denom;
|
||||
|
||||
g_string_append_printf(buf, "HVF cumulative execution time: %llu.%.3llus\n",
|
||||
time_ns / 1000000000,
|
||||
(time_ns % 1000000000) / 1000000);
|
||||
}
|
||||
|
||||
static void hvf_accel_ops_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
|
||||
|
||||
ops->create_vcpu_thread = hvf_start_vcpu_thread;
|
||||
ops->kick_vcpu_thread = hvf_kick_vcpu_thread;
|
||||
ops->handle_interrupt = generic_handle_interrupt;
|
||||
|
||||
ops->synchronize_post_reset = hvf_cpu_synchronize_post_reset;
|
||||
ops->synchronize_post_init = hvf_cpu_synchronize_post_init;
|
||||
|
|
@ -595,7 +387,10 @@ static void hvf_accel_ops_class_init(ObjectClass *oc, void *data)
|
|||
ops->remove_all_breakpoints = hvf_remove_all_breakpoints;
|
||||
ops->update_guest_debug = hvf_update_guest_debug;
|
||||
ops->supports_guest_debug = hvf_arch_supports_guest_debug;
|
||||
|
||||
ops->get_vcpu_stats = hvf_get_vcpu_stats;
|
||||
};
|
||||
|
||||
static const TypeInfo hvf_accel_ops_type = {
|
||||
.name = ACCEL_OPS_NAME("hvf"),
|
||||
|
||||
|
|
@ -603,8 +398,10 @@ static const TypeInfo hvf_accel_ops_type = {
|
|||
.class_init = hvf_accel_ops_class_init,
|
||||
.abstract = true,
|
||||
};
|
||||
|
||||
static void hvf_accel_ops_register_types(void)
|
||||
{
|
||||
type_register_static(&hvf_accel_ops_type);
|
||||
}
|
||||
|
||||
type_init(hvf_accel_ops_register_types);
|
||||
|
|
|
|||
|
|
@ -10,8 +10,25 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "accel/accel-ops.h"
|
||||
#include "system/address-spaces.h"
|
||||
#include "system/memory.h"
|
||||
#include "system/hvf.h"
|
||||
#include "system/hvf_int.h"
|
||||
#include "hw/core/cpu.h"
|
||||
#include "hw/boards.h"
|
||||
#include "trace.h"
|
||||
|
||||
bool hvf_allowed;
|
||||
|
||||
struct mac_slot {
|
||||
int present;
|
||||
uint64_t size;
|
||||
uint64_t gpa_start;
|
||||
uint64_t gva;
|
||||
};
|
||||
|
||||
struct mac_slot mac_slots[32];
|
||||
|
||||
const char *hvf_return_string(hv_return_t ret)
|
||||
{
|
||||
|
|
@ -41,25 +58,257 @@ void assert_hvf_ok_impl(hv_return_t ret, const char *file, unsigned int line,
|
|||
abort();
|
||||
}
|
||||
|
||||
struct hvf_sw_breakpoint *hvf_find_sw_breakpoint(CPUState *cpu, vaddr pc)
|
||||
static int do_hvf_set_memory(hvf_slot *slot, hv_memory_flags_t flags)
|
||||
{
|
||||
struct hvf_sw_breakpoint *bp;
|
||||
struct mac_slot *macslot;
|
||||
hv_return_t ret;
|
||||
|
||||
QTAILQ_FOREACH(bp, &hvf_state->hvf_sw_breakpoints, entry) {
|
||||
if (bp->pc == pc) {
|
||||
return bp;
|
||||
macslot = &mac_slots[slot->slot_id];
|
||||
|
||||
if (macslot->present) {
|
||||
if (macslot->size != slot->size) {
|
||||
macslot->present = 0;
|
||||
trace_hvf_vm_unmap(macslot->gpa_start, macslot->size);
|
||||
ret = hv_vm_unmap(macslot->gpa_start, macslot->size);
|
||||
assert_hvf_ok(ret);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int hvf_sw_breakpoints_active(CPUState *cpu)
|
||||
{
|
||||
return !QTAILQ_EMPTY(&hvf_state->hvf_sw_breakpoints);
|
||||
}
|
||||
if (!slot->size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hvf_update_guest_debug(CPUState *cpu)
|
||||
{
|
||||
hvf_arch_update_guest_debug(cpu);
|
||||
macslot->present = 1;
|
||||
macslot->gpa_start = slot->start;
|
||||
macslot->size = slot->size;
|
||||
trace_hvf_vm_map(slot->start, slot->size, slot->mem, flags,
|
||||
flags & HV_MEMORY_READ ? 'R' : '-',
|
||||
flags & HV_MEMORY_WRITE ? 'W' : '-',
|
||||
flags & HV_MEMORY_EXEC ? 'X' : '-');
|
||||
ret = hv_vm_map(slot->mem, slot->start, slot->size, flags);
|
||||
assert_hvf_ok(ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hvf_set_phys_mem(MemoryRegionSection *section, bool add)
|
||||
{
|
||||
hvf_slot *mem;
|
||||
MemoryRegion *area = section->mr;
|
||||
bool writable = !area->readonly && !area->rom_device;
|
||||
hv_memory_flags_t flags;
|
||||
uint64_t page_size = qemu_real_host_page_size();
|
||||
|
||||
if (!memory_region_is_ram(area)) {
|
||||
if (writable) {
|
||||
return;
|
||||
} else if (!memory_region_is_romd(area)) {
|
||||
/*
|
||||
* If the memory device is not in romd_mode, then we actually want
|
||||
* to remove the hvf memory slot so all accesses will trap.
|
||||
*/
|
||||
add = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!QEMU_IS_ALIGNED(int128_get64(section->size), page_size) ||
|
||||
!QEMU_IS_ALIGNED(section->offset_within_address_space, page_size)) {
|
||||
/* Not page aligned, so we can not map as RAM */
|
||||
add = false;
|
||||
}
|
||||
|
||||
mem = hvf_find_overlap_slot(
|
||||
section->offset_within_address_space,
|
||||
int128_get64(section->size));
|
||||
|
||||
if (mem && add) {
|
||||
if (mem->size == int128_get64(section->size) &&
|
||||
mem->start == section->offset_within_address_space &&
|
||||
mem->mem == (memory_region_get_ram_ptr(area) +
|
||||
section->offset_within_region)) {
|
||||
return; /* Same region was attempted to register, go away. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Region needs to be reset. set the size to 0 and remap it. */
|
||||
if (mem) {
|
||||
mem->size = 0;
|
||||
if (do_hvf_set_memory(mem, 0)) {
|
||||
error_report("Failed to reset overlapping slot");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
if (!add) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (area->readonly ||
|
||||
(!memory_region_is_ram(area) && memory_region_is_romd(area))) {
|
||||
flags = HV_MEMORY_READ | HV_MEMORY_EXEC;
|
||||
} else {
|
||||
flags = HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC;
|
||||
}
|
||||
|
||||
/* Now make a new slot. */
|
||||
int x;
|
||||
|
||||
for (x = 0; x < hvf_state->num_slots; ++x) {
|
||||
mem = &hvf_state->slots[x];
|
||||
if (!mem->size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (x == hvf_state->num_slots) {
|
||||
error_report("No free slots");
|
||||
abort();
|
||||
}
|
||||
|
||||
mem->size = int128_get64(section->size);
|
||||
mem->mem = memory_region_get_ram_ptr(area) + section->offset_within_region;
|
||||
mem->start = section->offset_within_address_space;
|
||||
mem->region = area;
|
||||
|
||||
if (do_hvf_set_memory(mem, flags)) {
|
||||
error_report("Error registering new memory slot");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void hvf_set_dirty_tracking(MemoryRegionSection *section, bool on)
|
||||
{
|
||||
hvf_slot *slot;
|
||||
|
||||
slot = hvf_find_overlap_slot(
|
||||
section->offset_within_address_space,
|
||||
int128_get64(section->size));
|
||||
|
||||
/* protect region against writes; begin tracking it */
|
||||
if (on) {
|
||||
slot->flags |= HVF_SLOT_LOG;
|
||||
hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size,
|
||||
HV_MEMORY_READ | HV_MEMORY_EXEC);
|
||||
/* stop tracking region*/
|
||||
} else {
|
||||
slot->flags &= ~HVF_SLOT_LOG;
|
||||
hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size,
|
||||
HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC);
|
||||
}
|
||||
}
|
||||
|
||||
static void hvf_log_start(MemoryListener *listener,
|
||||
MemoryRegionSection *section, int old, int new)
|
||||
{
|
||||
if (old != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
hvf_set_dirty_tracking(section, 1);
|
||||
}
|
||||
|
||||
static void hvf_log_stop(MemoryListener *listener,
|
||||
MemoryRegionSection *section, int old, int new)
|
||||
{
|
||||
if (new != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
hvf_set_dirty_tracking(section, 0);
|
||||
}
|
||||
|
||||
static void hvf_log_sync(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
/*
|
||||
* sync of dirty pages is handled elsewhere; just make sure we keep
|
||||
* tracking the region.
|
||||
*/
|
||||
hvf_set_dirty_tracking(section, 1);
|
||||
}
|
||||
|
||||
static void hvf_region_add(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
hvf_set_phys_mem(section, true);
|
||||
}
|
||||
|
||||
static void hvf_region_del(MemoryListener *listener,
|
||||
MemoryRegionSection *section)
|
||||
{
|
||||
hvf_set_phys_mem(section, false);
|
||||
}
|
||||
|
||||
static MemoryListener hvf_memory_listener = {
|
||||
.name = "hvf",
|
||||
.priority = MEMORY_LISTENER_PRIORITY_ACCEL,
|
||||
.region_add = hvf_region_add,
|
||||
.region_del = hvf_region_del,
|
||||
.log_start = hvf_log_start,
|
||||
.log_stop = hvf_log_stop,
|
||||
.log_sync = hvf_log_sync,
|
||||
};
|
||||
|
||||
static int hvf_accel_init(AccelState *as, MachineState *ms)
|
||||
{
|
||||
int x;
|
||||
hv_return_t ret;
|
||||
HVFState *s = HVF_STATE(as);
|
||||
int pa_range = 36;
|
||||
MachineClass *mc = MACHINE_GET_CLASS(ms);
|
||||
|
||||
if (mc->hvf_get_physical_address_range) {
|
||||
pa_range = mc->hvf_get_physical_address_range(ms);
|
||||
if (pa_range < 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
ret = hvf_arch_vm_create(ms, (uint32_t)pa_range);
|
||||
if (ret == HV_DENIED) {
|
||||
error_report("Could not access HVF. Is the executable signed"
|
||||
" with com.apple.security.hypervisor entitlement?");
|
||||
exit(1);
|
||||
}
|
||||
assert_hvf_ok(ret);
|
||||
|
||||
s->num_slots = ARRAY_SIZE(s->slots);
|
||||
for (x = 0; x < s->num_slots; ++x) {
|
||||
s->slots[x].size = 0;
|
||||
s->slots[x].slot_id = x;
|
||||
}
|
||||
|
||||
QTAILQ_INIT(&s->hvf_sw_breakpoints);
|
||||
|
||||
hvf_state = s;
|
||||
memory_listener_register(&hvf_memory_listener, &address_space_memory);
|
||||
|
||||
return hvf_arch_init();
|
||||
}
|
||||
|
||||
static int hvf_gdbstub_sstep_flags(AccelState *as)
|
||||
{
|
||||
return SSTEP_ENABLE | SSTEP_NOIRQ;
|
||||
}
|
||||
|
||||
static void hvf_accel_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
AccelClass *ac = ACCEL_CLASS(oc);
|
||||
ac->name = "HVF";
|
||||
ac->init_machine = hvf_accel_init;
|
||||
ac->allowed = &hvf_allowed;
|
||||
ac->gdbstub_supported_sstep_flags = hvf_gdbstub_sstep_flags;
|
||||
}
|
||||
|
||||
static const TypeInfo hvf_accel_type = {
|
||||
.name = TYPE_HVF_ACCEL,
|
||||
.parent = TYPE_ACCEL,
|
||||
.instance_size = sizeof(HVFState),
|
||||
.class_init = hvf_accel_class_init,
|
||||
};
|
||||
|
||||
static void hvf_type_init(void)
|
||||
{
|
||||
type_register_static(&hvf_accel_type);
|
||||
}
|
||||
|
||||
type_init(hvf_type_init);
|
||||
|
|
|
|||
7
accel/hvf/trace-events
Normal file
7
accel/hvf/trace-events
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
# See docs/devel/tracing.rst for syntax documentation.
|
||||
|
||||
# hvf-accel-ops.c
|
||||
hvf_vm_map(uint64_t paddr, uint64_t size, void *vaddr, uint8_t flags, const char r, const char w, const char e) "paddr:0x%016"PRIx64" size:0x%08"PRIx64" vaddr:%p flags:0x%02x/%c%c%c"
|
||||
hvf_vm_unmap(uint64_t paddr, uint64_t size) "paddr:0x%016"PRIx64" size:0x%08"PRIx64
|
||||
2
accel/hvf/trace.h
Normal file
2
accel/hvf/trace.h
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#include "trace/trace-accel_hvf.h"
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
#include "qemu/osdep.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "system/accel-ops.h"
|
||||
#include "accel/accel-cpu-ops.h"
|
||||
#include "system/kvm.h"
|
||||
#include "system/kvm_int.h"
|
||||
#include "system/runstate.h"
|
||||
|
|
@ -90,7 +90,7 @@ static int kvm_update_guest_debug_ops(CPUState *cpu)
|
|||
}
|
||||
#endif
|
||||
|
||||
static void kvm_accel_ops_class_init(ObjectClass *oc, void *data)
|
||||
static void kvm_accel_ops_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
|
||||
|
||||
|
|
@ -101,6 +101,7 @@ static void kvm_accel_ops_class_init(ObjectClass *oc, void *data)
|
|||
ops->synchronize_post_init = kvm_cpu_synchronize_post_init;
|
||||
ops->synchronize_state = kvm_cpu_synchronize_state;
|
||||
ops->synchronize_pre_loadvm = kvm_cpu_synchronize_pre_loadvm;
|
||||
ops->handle_interrupt = generic_handle_interrupt;
|
||||
|
||||
#ifdef TARGET_KVM_HAVE_GUEST_DEBUG
|
||||
ops->update_guest_debug = kvm_update_guest_debug_ops;
|
||||
|
|
|
|||
|
|
@ -32,9 +32,11 @@
|
|||
#include "system/runstate.h"
|
||||
#include "system/cpus.h"
|
||||
#include "system/accel-blocker.h"
|
||||
#include "accel/accel-ops.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "exec/memory.h"
|
||||
#include "exec/ram_addr.h"
|
||||
#include "exec/tswap.h"
|
||||
#include "system/memory.h"
|
||||
#include "system/ram_addr.h"
|
||||
#include "qemu/event_notifier.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "trace.h"
|
||||
|
|
@ -57,6 +59,11 @@
|
|||
#include <sys/eventfd.h>
|
||||
#endif
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
|
||||
# define KVM_HAVE_MCE_INJECTION 1
|
||||
#endif
|
||||
|
||||
|
||||
/* KVM uses PAGE_SIZE in its definition of KVM_COALESCED_MMIO_MAX. We
|
||||
* need to use the real host PAGE_SIZE, as that's what KVM will use.
|
||||
*/
|
||||
|
|
@ -93,6 +100,7 @@ bool kvm_allowed;
|
|||
bool kvm_readonly_mem_allowed;
|
||||
bool kvm_vm_attributes_allowed;
|
||||
bool kvm_msi_use_devid;
|
||||
bool kvm_pre_fault_memory_supported;
|
||||
static bool kvm_has_guest_debug;
|
||||
static int kvm_sstep_flags;
|
||||
static bool kvm_immediate_exit;
|
||||
|
|
@ -446,7 +454,13 @@ static void kvm_reset_parked_vcpus(KVMState *s)
|
|||
}
|
||||
}
|
||||
|
||||
int kvm_create_vcpu(CPUState *cpu)
|
||||
/**
|
||||
* kvm_create_vcpu - Gets a parked KVM vCPU or creates a KVM vCPU
|
||||
* @cpu: QOM CPUState object for which KVM vCPU has to be fetched/created.
|
||||
*
|
||||
* @returns: 0 when success, errno (<0) when failed.
|
||||
*/
|
||||
static int kvm_create_vcpu(CPUState *cpu)
|
||||
{
|
||||
unsigned long vcpu_id = kvm_arch_vcpu_id(cpu);
|
||||
KVMState *s = kvm_state;
|
||||
|
|
@ -465,7 +479,9 @@ int kvm_create_vcpu(CPUState *cpu)
|
|||
|
||||
cpu->kvm_fd = kvm_fd;
|
||||
cpu->kvm_state = s;
|
||||
cpu->vcpu_dirty = true;
|
||||
if (!s->guest_state_protected) {
|
||||
cpu->vcpu_dirty = true;
|
||||
}
|
||||
cpu->dirty_pages = 0;
|
||||
cpu->throttle_us_per_full = 0;
|
||||
|
||||
|
|
@ -506,16 +522,23 @@ static int do_kvm_destroy_vcpu(CPUState *cpu)
|
|||
goto err;
|
||||
}
|
||||
|
||||
/* If I am the CPU that created coalesced_mmio_ring, then discard it */
|
||||
if (s->coalesced_mmio_ring == (void *)cpu->kvm_run + PAGE_SIZE) {
|
||||
s->coalesced_mmio_ring = NULL;
|
||||
}
|
||||
|
||||
ret = munmap(cpu->kvm_run, mmap_size);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
cpu->kvm_run = NULL;
|
||||
|
||||
if (cpu->kvm_dirty_gfns) {
|
||||
ret = munmap(cpu->kvm_dirty_gfns, s->kvm_dirty_ring_bytes);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
cpu->kvm_dirty_gfns = NULL;
|
||||
}
|
||||
|
||||
kvm_park_vcpu(cpu);
|
||||
|
|
@ -539,6 +562,11 @@ int kvm_init_vcpu(CPUState *cpu, Error **errp)
|
|||
|
||||
trace_kvm_init_vcpu(cpu->cpu_index, kvm_arch_vcpu_id(cpu));
|
||||
|
||||
ret = kvm_arch_pre_create_vcpu(cpu, errp);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = kvm_create_vcpu(cpu);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret,
|
||||
|
|
@ -594,6 +622,31 @@ err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
void kvm_close(void)
|
||||
{
|
||||
CPUState *cpu;
|
||||
|
||||
if (!kvm_state || kvm_state->fd == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
cpu_remove_sync(cpu);
|
||||
close(cpu->kvm_fd);
|
||||
cpu->kvm_fd = -1;
|
||||
close(cpu->kvm_vcpu_stats_fd);
|
||||
cpu->kvm_vcpu_stats_fd = -1;
|
||||
}
|
||||
|
||||
if (kvm_state && kvm_state->fd != -1) {
|
||||
close(kvm_state->vmfd);
|
||||
kvm_state->vmfd = -1;
|
||||
close(kvm_state->fd);
|
||||
kvm_state->fd = -1;
|
||||
}
|
||||
kvm_state = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* dirty pages logging control
|
||||
*/
|
||||
|
|
@ -1313,21 +1366,22 @@ bool kvm_hwpoisoned_mem(void)
|
|||
|
||||
static uint32_t adjust_ioeventfd_endianness(uint32_t val, uint32_t size)
|
||||
{
|
||||
#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN
|
||||
/* The kernel expects ioeventfd values in HOST_BIG_ENDIAN
|
||||
* endianness, but the memory core hands them in target endianness.
|
||||
* For example, PPC is always treated as big-endian even if running
|
||||
* on KVM and on PPC64LE. Correct here.
|
||||
*/
|
||||
switch (size) {
|
||||
case 2:
|
||||
val = bswap16(val);
|
||||
break;
|
||||
case 4:
|
||||
val = bswap32(val);
|
||||
break;
|
||||
if (target_needs_bswap()) {
|
||||
/*
|
||||
* The kernel expects ioeventfd values in HOST_BIG_ENDIAN
|
||||
* endianness, but the memory core hands them in target endianness.
|
||||
* For example, PPC is always treated as big-endian even if running
|
||||
* on KVM and on PPC64LE. Correct here, swapping back.
|
||||
*/
|
||||
switch (size) {
|
||||
case 2:
|
||||
val = bswap16(val);
|
||||
break;
|
||||
case 4:
|
||||
val = bswap32(val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return val;
|
||||
}
|
||||
|
||||
|
|
@ -2419,7 +2473,7 @@ static int kvm_recommended_vcpus(KVMState *s)
|
|||
|
||||
static int kvm_max_vcpus(KVMState *s)
|
||||
{
|
||||
int ret = kvm_check_extension(s, KVM_CAP_MAX_VCPUS);
|
||||
int ret = kvm_vm_check_extension(s, KVM_CAP_MAX_VCPUS);
|
||||
return (ret) ? ret : kvm_recommended_vcpus(s);
|
||||
}
|
||||
|
||||
|
|
@ -2449,13 +2503,10 @@ uint32_t kvm_dirty_ring_size(void)
|
|||
return kvm_state->kvm_dirty_ring_size;
|
||||
}
|
||||
|
||||
static int do_kvm_create_vm(MachineState *ms, int type)
|
||||
static int do_kvm_create_vm(KVMState *s, int type)
|
||||
{
|
||||
KVMState *s;
|
||||
int ret;
|
||||
|
||||
s = KVM_STATE(ms->accelerator);
|
||||
|
||||
do {
|
||||
ret = kvm_ioctl(s, KVM_CREATE_VM, type);
|
||||
} while (ret == -EINTR);
|
||||
|
|
@ -2552,7 +2603,7 @@ static int kvm_setup_dirty_ring(KVMState *s)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int kvm_init(MachineState *ms)
|
||||
static int kvm_init(AccelState *as, MachineState *ms)
|
||||
{
|
||||
MachineClass *mc = MACHINE_GET_CLASS(ms);
|
||||
static const char upgrade_note[] =
|
||||
|
|
@ -2566,15 +2617,13 @@ static int kvm_init(MachineState *ms)
|
|||
{ /* end of list */ }
|
||||
}, *nc = num_cpus;
|
||||
int soft_vcpus_limit, hard_vcpus_limit;
|
||||
KVMState *s;
|
||||
KVMState *s = KVM_STATE(as);
|
||||
const KVMCapabilityInfo *missing_cap;
|
||||
int ret;
|
||||
int type;
|
||||
|
||||
qemu_mutex_init(&kml_slots_lock);
|
||||
|
||||
s = KVM_STATE(ms->accelerator);
|
||||
|
||||
/*
|
||||
* On systems where the kernel can support different base page
|
||||
* sizes, host page size may be different from TARGET_PAGE_SIZE,
|
||||
|
|
@ -2626,7 +2675,7 @@ static int kvm_init(MachineState *ms)
|
|||
goto err;
|
||||
}
|
||||
|
||||
ret = do_kvm_create_vm(ms, type);
|
||||
ret = do_kvm_create_vm(s, type);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
|
@ -2730,6 +2779,7 @@ static int kvm_init(MachineState *ms)
|
|||
kvm_check_extension(s, KVM_CAP_GUEST_MEMFD) &&
|
||||
kvm_check_extension(s, KVM_CAP_USER_MEMORY2) &&
|
||||
(kvm_supported_memory_attributes & KVM_MEMORY_ATTRIBUTE_PRIVATE);
|
||||
kvm_pre_fault_memory_supported = kvm_vm_check_extension(s, KVM_CAP_PRE_FAULT_MEMORY);
|
||||
|
||||
if (s->kernel_irqchip_split == ON_OFF_AUTO_AUTO) {
|
||||
s->kernel_irqchip_split = mc->default_kernel_irqchip_split ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
|
||||
|
|
@ -3074,6 +3124,15 @@ int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private)
|
|||
addr = memory_region_get_ram_ptr(mr) + section.offset_within_region;
|
||||
rb = qemu_ram_block_from_host(addr, false, &offset);
|
||||
|
||||
ret = ram_block_attributes_state_change(RAM_BLOCK_ATTRIBUTES(mr->rdm),
|
||||
offset, size, to_private);
|
||||
if (ret) {
|
||||
error_report("Failed to notify the listener the state change of "
|
||||
"(0x%"HWADDR_PRIx" + 0x%"HWADDR_PRIx") to %s",
|
||||
start, size, to_private ? "private" : "shared");
|
||||
goto out_unref;
|
||||
}
|
||||
|
||||
if (to_private) {
|
||||
if (rb->page_size != qemu_real_host_page_size()) {
|
||||
/*
|
||||
|
|
@ -3759,10 +3818,10 @@ int kvm_get_one_reg(CPUState *cs, uint64_t id, void *target)
|
|||
return r;
|
||||
}
|
||||
|
||||
static bool kvm_accel_has_memory(MachineState *ms, AddressSpace *as,
|
||||
static bool kvm_accel_has_memory(AccelState *accel, AddressSpace *as,
|
||||
hwaddr start_addr, hwaddr size)
|
||||
{
|
||||
KVMState *kvm = KVM_STATE(ms->accelerator);
|
||||
KVMState *kvm = KVM_STATE(accel);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < kvm->nr_as; ++i) {
|
||||
|
|
@ -3953,12 +4012,12 @@ static void kvm_accel_instance_init(Object *obj)
|
|||
* Returns: SSTEP_* flags that KVM supports for guest debug. The
|
||||
* support is probed during kvm_init()
|
||||
*/
|
||||
static int kvm_gdbstub_sstep_flags(void)
|
||||
static int kvm_gdbstub_sstep_flags(AccelState *as)
|
||||
{
|
||||
return kvm_sstep_flags;
|
||||
}
|
||||
|
||||
static void kvm_accel_class_init(ObjectClass *oc, void *data)
|
||||
static void kvm_accel_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
AccelClass *ac = ACCEL_CLASS(oc);
|
||||
ac->name = "KVM";
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
common_ss.add(files('accel-common.c'))
|
||||
specific_ss.add(files('accel-target.c'))
|
||||
system_ss.add(files('accel-system.c', 'accel-blocker.c'))
|
||||
system_ss.add(files('accel-system.c', 'accel-blocker.c', 'accel-qmp.c'))
|
||||
user_ss.add(files('accel-user.c'))
|
||||
|
||||
subdir('tcg')
|
||||
|
|
|
|||
|
|
@ -18,12 +18,14 @@
|
|||
#include "qemu/option.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/accel.h"
|
||||
#include "system/accel-ops.h"
|
||||
#include "accel/accel-ops.h"
|
||||
#include "accel/accel-cpu-ops.h"
|
||||
#include "system/qtest.h"
|
||||
#include "system/cpus.h"
|
||||
#include "qemu/guest-random.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "hw/core/cpu.h"
|
||||
#include "accel/dummy-cpus.h"
|
||||
|
||||
static int64_t qtest_clock_counter;
|
||||
|
||||
|
|
@ -37,12 +39,12 @@ static void qtest_set_virtual_clock(int64_t count)
|
|||
qatomic_set_i64(&qtest_clock_counter, count);
|
||||
}
|
||||
|
||||
static int qtest_init_accel(MachineState *ms)
|
||||
static int qtest_init_accel(AccelState *as, MachineState *ms)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qtest_accel_class_init(ObjectClass *oc, void *data)
|
||||
static void qtest_accel_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
AccelClass *ac = ACCEL_CLASS(oc);
|
||||
ac->name = "QTest";
|
||||
|
|
@ -59,13 +61,14 @@ static const TypeInfo qtest_accel_type = {
|
|||
};
|
||||
module_obj(TYPE_QTEST_ACCEL);
|
||||
|
||||
static void qtest_accel_ops_class_init(ObjectClass *oc, void *data)
|
||||
static void qtest_accel_ops_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
|
||||
|
||||
ops->create_vcpu_thread = dummy_start_vcpu_thread;
|
||||
ops->get_virtual_clock = qtest_get_virtual_clock;
|
||||
ops->set_virtual_clock = qtest_set_virtual_clock;
|
||||
ops->handle_interrupt = generic_handle_interrupt;
|
||||
};
|
||||
|
||||
static const TypeInfo qtest_accel_ops_type = {
|
||||
|
|
|
|||
12
accel/stubs/hvf-stub.c
Normal file
12
accel/stubs/hvf-stub.c
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* HVF stubs for QEMU
|
||||
*
|
||||
* Copyright (c) Linaro
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "system/hvf.h"
|
||||
|
||||
bool hvf_allowed;
|
||||
|
|
@ -29,10 +29,6 @@ void kvm_flush_coalesced_mmio_buffer(void)
|
|||
{
|
||||
}
|
||||
|
||||
void kvm_cpu_synchronize_state(CPUState *cpu)
|
||||
{
|
||||
}
|
||||
|
||||
bool kvm_has_sync_mmu(void)
|
||||
{
|
||||
return false;
|
||||
|
|
@ -105,11 +101,6 @@ unsigned int kvm_get_free_memslots(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void kvm_init_cpu_signals(CPUState *cpu)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
bool kvm_arm_supports_user_irq(void)
|
||||
{
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -2,5 +2,8 @@ system_stubs_ss = ss.source_set()
|
|||
system_stubs_ss.add(when: 'CONFIG_XEN', if_false: files('xen-stub.c'))
|
||||
system_stubs_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c'))
|
||||
system_stubs_ss.add(when: 'CONFIG_TCG', if_false: files('tcg-stub.c'))
|
||||
system_stubs_ss.add(when: 'CONFIG_HVF', if_false: files('hvf-stub.c'))
|
||||
system_stubs_ss.add(when: 'CONFIG_NVMM', if_false: files('nvmm-stub.c'))
|
||||
system_stubs_ss.add(when: 'CONFIG_WHPX', if_false: files('whpx-stub.c'))
|
||||
|
||||
specific_ss.add_all(when: ['CONFIG_SYSTEM_ONLY'], if_true: system_stubs_ss)
|
||||
|
|
|
|||
12
accel/stubs/nvmm-stub.c
Normal file
12
accel/stubs/nvmm-stub.c
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* NVMM stubs for QEMU
|
||||
*
|
||||
* Copyright (c) Linaro
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "system/nvmm.h"
|
||||
|
||||
bool nvmm_allowed;
|
||||
|
|
@ -11,8 +11,7 @@
|
|||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "exec/tb-flush.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/cpu-common.h"
|
||||
|
||||
G_NORETURN void cpu_loop_exit(CPUState *cpu)
|
||||
{
|
||||
|
|
|
|||
12
accel/stubs/whpx-stub.c
Normal file
12
accel/stubs/whpx-stub.c
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* WHPX stubs for QEMU
|
||||
*
|
||||
* Copyright (c) Linaro
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "system/whpx.h"
|
||||
|
||||
bool whpx_allowed;
|
||||
|
|
@ -77,7 +77,7 @@
|
|||
# define END _le
|
||||
#endif
|
||||
|
||||
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, abi_ptr addr,
|
||||
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr,
|
||||
ABI_TYPE cmpv, ABI_TYPE newv,
|
||||
MemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
|
|
@ -101,7 +101,7 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, abi_ptr addr,
|
|||
}
|
||||
|
||||
#if DATA_SIZE < 16
|
||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, abi_ptr addr, ABI_TYPE val,
|
||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
|
||||
MemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
|
||||
|
|
@ -120,7 +120,7 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, abi_ptr addr, ABI_TYPE val,
|
|||
}
|
||||
|
||||
#define GEN_ATOMIC_HELPER(X) \
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, abi_ptr addr, \
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \
|
||||
ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \
|
||||
{ \
|
||||
DATA_TYPE *haddr, ret; \
|
||||
|
|
@ -156,7 +156,7 @@ GEN_ATOMIC_HELPER(xor_fetch)
|
|||
* of CF_PARALLEL's value, we'll trace just a read and a write.
|
||||
*/
|
||||
#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, abi_ptr addr, \
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \
|
||||
ABI_TYPE xval, MemOpIdx oi, uintptr_t retaddr) \
|
||||
{ \
|
||||
XDATA_TYPE *haddr, cmp, old, new, val = xval; \
|
||||
|
|
@ -202,7 +202,7 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
|
|||
# define END _be
|
||||
#endif
|
||||
|
||||
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, abi_ptr addr,
|
||||
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr,
|
||||
ABI_TYPE cmpv, ABI_TYPE newv,
|
||||
MemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
|
|
@ -226,7 +226,7 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, abi_ptr addr,
|
|||
}
|
||||
|
||||
#if DATA_SIZE < 16
|
||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, abi_ptr addr, ABI_TYPE val,
|
||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
|
||||
MemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
|
||||
|
|
@ -245,7 +245,7 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, abi_ptr addr, ABI_TYPE val,
|
|||
}
|
||||
|
||||
#define GEN_ATOMIC_HELPER(X) \
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, abi_ptr addr, \
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \
|
||||
ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \
|
||||
{ \
|
||||
DATA_TYPE *haddr, ret; \
|
||||
|
|
@ -278,7 +278,7 @@ GEN_ATOMIC_HELPER(xor_fetch)
|
|||
* of CF_PARALLEL's value, we'll trace just a read and a write.
|
||||
*/
|
||||
#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, abi_ptr addr, \
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \
|
||||
ABI_TYPE xval, MemOpIdx oi, uintptr_t retaddr) \
|
||||
{ \
|
||||
XDATA_TYPE *haddr, ldo, ldn, old, new, val = xval; \
|
||||
|
|
|
|||
41
accel/tcg/backend-ldst.h
Normal file
41
accel/tcg/backend-ldst.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Internal memory barrier helpers for QEMU (target agnostic)
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#ifndef ACCEL_TCG_BACKEND_LDST_H
|
||||
#define ACCEL_TCG_BACKEND_LDST_H
|
||||
|
||||
#include "tcg-target-mo.h"
|
||||
|
||||
/**
|
||||
* tcg_req_mo:
|
||||
* @guest_mo: Guest default memory order
|
||||
* @type: TCGBar
|
||||
*
|
||||
* Filter @type to the barrier that is required for the guest
|
||||
* memory ordering vs the host memory ordering. A non-zero
|
||||
* result indicates that some barrier is required.
|
||||
*/
|
||||
#define tcg_req_mo(guest_mo, type) \
|
||||
((type) & guest_mo & ~TCG_TARGET_DEFAULT_MO)
|
||||
|
||||
/**
|
||||
* cpu_req_mo:
|
||||
* @cpu: CPUState
|
||||
* @type: TCGBar
|
||||
*
|
||||
* If tcg_req_mo indicates a barrier for @type is required
|
||||
* for the guest memory model, issue a host memory barrier.
|
||||
*/
|
||||
#define cpu_req_mo(cpu, type) \
|
||||
do { \
|
||||
if (tcg_req_mo(cpu->cc->tcg_ops->guest_default_memory_order, type)) { \
|
||||
smp_mb(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
|
@ -23,18 +23,20 @@
|
|||
#include "qapi/type-helpers.h"
|
||||
#include "hw/core/cpu.h"
|
||||
#include "accel/tcg/cpu-ops.h"
|
||||
#include "accel/tcg/helper-retaddr.h"
|
||||
#include "trace.h"
|
||||
#include "disas/disas.h"
|
||||
#include "exec/cpu-common.h"
|
||||
#include "exec/cpu-interrupt.h"
|
||||
#include "exec/page-protection.h"
|
||||
#include "exec/mmap-lock.h"
|
||||
#include "exec/translation-block.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "qemu/atomic.h"
|
||||
#include "qemu/rcu.h"
|
||||
#include "exec/log.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "exec/cpu-all.h"
|
||||
#include "system/cpu-timers.h"
|
||||
#include "exec/icount.h"
|
||||
#include "exec/replay-core.h"
|
||||
#include "system/tcg.h"
|
||||
#include "exec/helper-proto-common.h"
|
||||
|
|
@ -43,7 +45,6 @@
|
|||
#include "tb-context.h"
|
||||
#include "tb-internal.h"
|
||||
#include "internal-common.h"
|
||||
#include "internal-target.h"
|
||||
|
||||
/* -icount align implementation. */
|
||||
|
||||
|
|
@ -148,12 +149,9 @@ static void init_delay_params(SyncClocks *sc, const CPUState *cpu)
|
|||
#endif /* CONFIG USER ONLY */
|
||||
|
||||
struct tb_desc {
|
||||
vaddr pc;
|
||||
uint64_t cs_base;
|
||||
TCGTBCPUState s;
|
||||
CPUArchState *env;
|
||||
tb_page_addr_t page_addr0;
|
||||
uint32_t flags;
|
||||
uint32_t cflags;
|
||||
};
|
||||
|
||||
static bool tb_lookup_cmp(const void *p, const void *d)
|
||||
|
|
@ -161,11 +159,11 @@ static bool tb_lookup_cmp(const void *p, const void *d)
|
|||
const TranslationBlock *tb = p;
|
||||
const struct tb_desc *desc = d;
|
||||
|
||||
if ((tb_cflags(tb) & CF_PCREL || tb->pc == desc->pc) &&
|
||||
if ((tb_cflags(tb) & CF_PCREL || tb->pc == desc->s.pc) &&
|
||||
tb_page_addr0(tb) == desc->page_addr0 &&
|
||||
tb->cs_base == desc->cs_base &&
|
||||
tb->flags == desc->flags &&
|
||||
tb_cflags(tb) == desc->cflags) {
|
||||
tb->cs_base == desc->s.cs_base &&
|
||||
tb->flags == desc->s.flags &&
|
||||
tb_cflags(tb) == desc->s.cflags) {
|
||||
/* check next page if needed */
|
||||
tb_page_addr_t tb_phys_page1 = tb_page_addr1(tb);
|
||||
if (tb_phys_page1 == -1) {
|
||||
|
|
@ -183,7 +181,7 @@ static bool tb_lookup_cmp(const void *p, const void *d)
|
|||
* is different for the new TB. Therefore any exception raised
|
||||
* here by the faulting lookup is not premature.
|
||||
*/
|
||||
virt_page1 = TARGET_PAGE_ALIGN(desc->pc);
|
||||
virt_page1 = TARGET_PAGE_ALIGN(desc->s.pc);
|
||||
phys_page1 = get_page_addr_code(desc->env, virt_page1);
|
||||
if (tb_phys_page1 == phys_page1) {
|
||||
return true;
|
||||
|
|
@ -193,26 +191,21 @@ static bool tb_lookup_cmp(const void *p, const void *d)
|
|||
return false;
|
||||
}
|
||||
|
||||
static TranslationBlock *tb_htable_lookup(CPUState *cpu, vaddr pc,
|
||||
uint64_t cs_base, uint32_t flags,
|
||||
uint32_t cflags)
|
||||
static TranslationBlock *tb_htable_lookup(CPUState *cpu, TCGTBCPUState s)
|
||||
{
|
||||
tb_page_addr_t phys_pc;
|
||||
struct tb_desc desc;
|
||||
uint32_t h;
|
||||
|
||||
desc.s = s;
|
||||
desc.env = cpu_env(cpu);
|
||||
desc.cs_base = cs_base;
|
||||
desc.flags = flags;
|
||||
desc.cflags = cflags;
|
||||
desc.pc = pc;
|
||||
phys_pc = get_page_addr_code(desc.env, pc);
|
||||
phys_pc = get_page_addr_code(desc.env, s.pc);
|
||||
if (phys_pc == -1) {
|
||||
return NULL;
|
||||
}
|
||||
desc.page_addr0 = phys_pc;
|
||||
h = tb_hash_func(phys_pc, (cflags & CF_PCREL ? 0 : pc),
|
||||
flags, cs_base, cflags);
|
||||
h = tb_hash_func(phys_pc, (s.cflags & CF_PCREL ? 0 : s.pc),
|
||||
s.flags, s.cs_base, s.cflags);
|
||||
return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp);
|
||||
}
|
||||
|
||||
|
|
@ -230,35 +223,33 @@ static TranslationBlock *tb_htable_lookup(CPUState *cpu, vaddr pc,
|
|||
*
|
||||
* Returns: an existing translation block or NULL.
|
||||
*/
|
||||
static inline TranslationBlock *tb_lookup(CPUState *cpu, vaddr pc,
|
||||
uint64_t cs_base, uint32_t flags,
|
||||
uint32_t cflags)
|
||||
static inline TranslationBlock *tb_lookup(CPUState *cpu, TCGTBCPUState s)
|
||||
{
|
||||
TranslationBlock *tb;
|
||||
CPUJumpCache *jc;
|
||||
uint32_t hash;
|
||||
|
||||
/* we should never be trying to look up an INVALID tb */
|
||||
tcg_debug_assert(!(cflags & CF_INVALID));
|
||||
tcg_debug_assert(!(s.cflags & CF_INVALID));
|
||||
|
||||
hash = tb_jmp_cache_hash_func(pc);
|
||||
hash = tb_jmp_cache_hash_func(s.pc);
|
||||
jc = cpu->tb_jmp_cache;
|
||||
|
||||
tb = qatomic_read(&jc->array[hash].tb);
|
||||
if (likely(tb &&
|
||||
jc->array[hash].pc == pc &&
|
||||
tb->cs_base == cs_base &&
|
||||
tb->flags == flags &&
|
||||
tb_cflags(tb) == cflags)) {
|
||||
jc->array[hash].pc == s.pc &&
|
||||
tb->cs_base == s.cs_base &&
|
||||
tb->flags == s.flags &&
|
||||
tb_cflags(tb) == s.cflags)) {
|
||||
goto hit;
|
||||
}
|
||||
|
||||
tb = tb_htable_lookup(cpu, pc, cs_base, flags, cflags);
|
||||
tb = tb_htable_lookup(cpu, s);
|
||||
if (tb == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jc->array[hash].pc = pc;
|
||||
jc->array[hash].pc = s.pc;
|
||||
qatomic_set(&jc->array[hash].tb, tb);
|
||||
|
||||
hit:
|
||||
|
|
@ -266,7 +257,7 @@ hit:
|
|||
* As long as tb is not NULL, the contents are consistent. Therefore,
|
||||
* the virtual PC has to match for non-CF_PCREL translations.
|
||||
*/
|
||||
assert((tb_cflags(tb) & CF_PCREL) || tb->pc == pc);
|
||||
assert((tb_cflags(tb) & CF_PCREL) || tb->pc == s.pc);
|
||||
return tb;
|
||||
}
|
||||
|
||||
|
|
@ -283,14 +274,11 @@ static void log_cpu_exec(vaddr pc, CPUState *cpu,
|
|||
if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) {
|
||||
FILE *logfile = qemu_log_trylock();
|
||||
if (logfile) {
|
||||
int flags = 0;
|
||||
int flags = CPU_DUMP_CCOP;
|
||||
|
||||
if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) {
|
||||
flags |= CPU_DUMP_FPU;
|
||||
}
|
||||
#if defined(TARGET_I386)
|
||||
flags |= CPU_DUMP_CCOP;
|
||||
#endif
|
||||
if (qemu_loglevel_mask(CPU_LOG_TB_VPU)) {
|
||||
flags |= CPU_DUMP_VPU;
|
||||
}
|
||||
|
|
@ -386,9 +374,6 @@ const void *HELPER(lookup_tb_ptr)(CPUArchState *env)
|
|||
{
|
||||
CPUState *cpu = env_cpu(env);
|
||||
TranslationBlock *tb;
|
||||
vaddr pc;
|
||||
uint64_t cs_base;
|
||||
uint32_t flags, cflags;
|
||||
|
||||
/*
|
||||
* By definition we've just finished a TB, so I/O is OK.
|
||||
|
|
@ -398,20 +383,21 @@ const void *HELPER(lookup_tb_ptr)(CPUArchState *env)
|
|||
* The next TB, if we chain to it, will clear the flag again.
|
||||
*/
|
||||
cpu->neg.can_do_io = true;
|
||||
cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
|
||||
|
||||
cflags = curr_cflags(cpu);
|
||||
if (check_for_breakpoints(cpu, pc, &cflags)) {
|
||||
TCGTBCPUState s = cpu->cc->tcg_ops->get_tb_cpu_state(cpu);
|
||||
s.cflags = curr_cflags(cpu);
|
||||
|
||||
if (check_for_breakpoints(cpu, s.pc, &s.cflags)) {
|
||||
cpu_loop_exit(cpu);
|
||||
}
|
||||
|
||||
tb = tb_lookup(cpu, pc, cs_base, flags, cflags);
|
||||
tb = tb_lookup(cpu, s);
|
||||
if (tb == NULL) {
|
||||
return tcg_code_gen_epilogue;
|
||||
}
|
||||
|
||||
if (qemu_loglevel_mask(CPU_LOG_TB_CPU | CPU_LOG_EXEC)) {
|
||||
log_cpu_exec(pc, cpu, tb);
|
||||
log_cpu_exec(s.pc, cpu, tb);
|
||||
}
|
||||
|
||||
return tb->tc.ptr;
|
||||
|
|
@ -561,11 +547,7 @@ static void cpu_exec_longjmp_cleanup(CPUState *cpu)
|
|||
|
||||
void cpu_exec_step_atomic(CPUState *cpu)
|
||||
{
|
||||
CPUArchState *env = cpu_env(cpu);
|
||||
TranslationBlock *tb;
|
||||
vaddr pc;
|
||||
uint64_t cs_base;
|
||||
uint32_t flags, cflags;
|
||||
int tb_exit;
|
||||
|
||||
if (sigsetjmp(cpu->jmp_env, 0) == 0) {
|
||||
|
|
@ -574,13 +556,13 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
|||
g_assert(!cpu->running);
|
||||
cpu->running = true;
|
||||
|
||||
cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
|
||||
TCGTBCPUState s = cpu->cc->tcg_ops->get_tb_cpu_state(cpu);
|
||||
s.cflags = curr_cflags(cpu);
|
||||
|
||||
cflags = curr_cflags(cpu);
|
||||
/* Execute in a serial context. */
|
||||
cflags &= ~CF_PARALLEL;
|
||||
s.cflags &= ~CF_PARALLEL;
|
||||
/* After 1 insn, return and release the exclusive lock. */
|
||||
cflags |= CF_NO_GOTO_TB | CF_NO_GOTO_PTR | 1;
|
||||
s.cflags |= CF_NO_GOTO_TB | CF_NO_GOTO_PTR | 1;
|
||||
/*
|
||||
* No need to check_for_breakpoints here.
|
||||
* We only arrive in cpu_exec_step_atomic after beginning execution
|
||||
|
|
@ -588,16 +570,16 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
|||
* Any breakpoint for this insn will have been recognized earlier.
|
||||
*/
|
||||
|
||||
tb = tb_lookup(cpu, pc, cs_base, flags, cflags);
|
||||
tb = tb_lookup(cpu, s);
|
||||
if (tb == NULL) {
|
||||
mmap_lock();
|
||||
tb = tb_gen_code(cpu, pc, cs_base, flags, cflags);
|
||||
tb = tb_gen_code(cpu, s);
|
||||
mmap_unlock();
|
||||
}
|
||||
|
||||
cpu_exec_enter(cpu);
|
||||
/* execute the generated code */
|
||||
trace_exec_tb(tb, pc);
|
||||
trace_exec_tb(tb, s.pc);
|
||||
cpu_tb_exec(cpu, tb, &tb_exit);
|
||||
cpu_exec_exit(cpu);
|
||||
} else {
|
||||
|
|
@ -665,7 +647,6 @@ static inline void tb_add_jump(TranslationBlock *tb, int n,
|
|||
|
||||
out_unlock_next:
|
||||
qemu_spin_unlock(&tb_next->jmp_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
static inline bool cpu_handle_halt(CPUState *cpu)
|
||||
|
|
@ -731,10 +712,10 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
|
|||
* If user mode only, we simulate a fake exception which will be
|
||||
* handled outside the cpu execution loop.
|
||||
*/
|
||||
#if defined(TARGET_I386)
|
||||
const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops;
|
||||
tcg_ops->fake_user_interrupt(cpu);
|
||||
#endif /* TARGET_I386 */
|
||||
if (tcg_ops->fake_user_interrupt) {
|
||||
tcg_ops->fake_user_interrupt(cpu);
|
||||
}
|
||||
*ret = cpu->exception_index;
|
||||
cpu->exception_index = -1;
|
||||
return true;
|
||||
|
|
@ -821,33 +802,22 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
|||
cpu->exception_index = EXCP_HLT;
|
||||
bql_unlock();
|
||||
return true;
|
||||
}
|
||||
#if defined(TARGET_I386)
|
||||
else if (interrupt_request & CPU_INTERRUPT_INIT) {
|
||||
X86CPU *x86_cpu = X86_CPU(cpu);
|
||||
CPUArchState *env = &x86_cpu->env;
|
||||
replay_interrupt();
|
||||
cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0, 0);
|
||||
do_cpu_init(x86_cpu);
|
||||
cpu->exception_index = EXCP_HALTED;
|
||||
bql_unlock();
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
else if (interrupt_request & CPU_INTERRUPT_RESET) {
|
||||
replay_interrupt();
|
||||
cpu_reset(cpu);
|
||||
bql_unlock();
|
||||
return true;
|
||||
}
|
||||
#endif /* !TARGET_I386 */
|
||||
/* The target hook has 3 exit conditions:
|
||||
False when the interrupt isn't processed,
|
||||
True when it is, and we should restart on a new TB,
|
||||
and via longjmp via cpu_loop_exit. */
|
||||
else {
|
||||
} else {
|
||||
const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops;
|
||||
|
||||
if (interrupt_request & CPU_INTERRUPT_RESET) {
|
||||
replay_interrupt();
|
||||
tcg_ops->cpu_exec_reset(cpu);
|
||||
bql_unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* The target hook has 3 exit conditions:
|
||||
* False when the interrupt isn't processed,
|
||||
* True when it is, and we should restart on a new TB,
|
||||
* and via longjmp via cpu_loop_exit.
|
||||
*/
|
||||
if (tcg_ops->cpu_exec_interrupt(cpu, interrupt_request)) {
|
||||
if (!tcg_ops->need_replay_interrupt ||
|
||||
tcg_ops->need_replay_interrupt(interrupt_request)) {
|
||||
|
|
@ -954,11 +924,8 @@ cpu_exec_loop(CPUState *cpu, SyncClocks *sc)
|
|||
|
||||
while (!cpu_handle_interrupt(cpu, &last_tb)) {
|
||||
TranslationBlock *tb;
|
||||
vaddr pc;
|
||||
uint64_t cs_base;
|
||||
uint32_t flags, cflags;
|
||||
|
||||
cpu_get_tb_cpu_state(cpu_env(cpu), &pc, &cs_base, &flags);
|
||||
TCGTBCPUState s = cpu->cc->tcg_ops->get_tb_cpu_state(cpu);
|
||||
s.cflags = cpu->cflags_next_tb;
|
||||
|
||||
/*
|
||||
* When requested, use an exact setting for cflags for the next
|
||||
|
|
@ -967,33 +934,32 @@ cpu_exec_loop(CPUState *cpu, SyncClocks *sc)
|
|||
* have CF_INVALID set, -1 is a convenient invalid value that
|
||||
* does not require tcg headers for cpu_common_reset.
|
||||
*/
|
||||
cflags = cpu->cflags_next_tb;
|
||||
if (cflags == -1) {
|
||||
cflags = curr_cflags(cpu);
|
||||
if (s.cflags == -1) {
|
||||
s.cflags = curr_cflags(cpu);
|
||||
} else {
|
||||
cpu->cflags_next_tb = -1;
|
||||
}
|
||||
|
||||
if (check_for_breakpoints(cpu, pc, &cflags)) {
|
||||
if (check_for_breakpoints(cpu, s.pc, &s.cflags)) {
|
||||
break;
|
||||
}
|
||||
|
||||
tb = tb_lookup(cpu, pc, cs_base, flags, cflags);
|
||||
tb = tb_lookup(cpu, s);
|
||||
if (tb == NULL) {
|
||||
CPUJumpCache *jc;
|
||||
uint32_t h;
|
||||
|
||||
mmap_lock();
|
||||
tb = tb_gen_code(cpu, pc, cs_base, flags, cflags);
|
||||
tb = tb_gen_code(cpu, s);
|
||||
mmap_unlock();
|
||||
|
||||
/*
|
||||
* We add the TB in the virtual pc hash table
|
||||
* for the fast lookup
|
||||
*/
|
||||
h = tb_jmp_cache_hash_func(pc);
|
||||
h = tb_jmp_cache_hash_func(s.pc);
|
||||
jc = cpu->tb_jmp_cache;
|
||||
jc->array[h].pc = pc;
|
||||
jc->array[h].pc = s.pc;
|
||||
qatomic_set(&jc->array[h].tb, tb);
|
||||
}
|
||||
|
||||
|
|
@ -1013,7 +979,7 @@ cpu_exec_loop(CPUState *cpu, SyncClocks *sc)
|
|||
tb_add_jump(last_tb, tb_exit, tb);
|
||||
}
|
||||
|
||||
cpu_loop_exec_tb(cpu, tb, pc, &last_tb, &tb_exit);
|
||||
cpu_loop_exec_tb(cpu, tb, s.pc, &last_tb, &tb_exit);
|
||||
|
||||
/* Try to align the host and virtual clocks
|
||||
if the guest is in advance */
|
||||
|
|
@ -1072,8 +1038,12 @@ bool tcg_exec_realizefn(CPUState *cpu, Error **errp)
|
|||
#ifndef CONFIG_USER_ONLY
|
||||
assert(tcg_ops->cpu_exec_halt);
|
||||
assert(tcg_ops->cpu_exec_interrupt);
|
||||
assert(tcg_ops->cpu_exec_reset);
|
||||
assert(tcg_ops->pointer_wrap);
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
assert(tcg_ops->translate_code);
|
||||
assert(tcg_ops->get_tb_cpu_state);
|
||||
assert(tcg_ops->mmu_index);
|
||||
tcg_ops->initialize();
|
||||
tcg_target_initialized = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,15 +19,17 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/target-info.h"
|
||||
#include "accel/tcg/cpu-ops.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "accel/tcg/iommu.h"
|
||||
#include "accel/tcg/probe.h"
|
||||
#include "exec/page-protection.h"
|
||||
#include "exec/memory.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "system/memory.h"
|
||||
#include "accel/tcg/cpu-ldst-common.h"
|
||||
#include "accel/tcg/cpu-mmu-index.h"
|
||||
#include "exec/cputlb.h"
|
||||
#include "exec/tb-flush.h"
|
||||
#include "exec/memory-internal.h"
|
||||
#include "exec/ram_addr.h"
|
||||
#include "system/ram_addr.h"
|
||||
#include "exec/mmu-access-type.h"
|
||||
#include "exec/tlb-common.h"
|
||||
#include "exec/vaddr.h"
|
||||
|
|
@ -35,18 +37,21 @@
|
|||
#include "qemu/error-report.h"
|
||||
#include "exec/log.h"
|
||||
#include "exec/helper-proto-common.h"
|
||||
#include "exec/tlb-flags.h"
|
||||
#include "qemu/atomic.h"
|
||||
#include "qemu/atomic128.h"
|
||||
#include "tb-internal.h"
|
||||
#include "trace.h"
|
||||
#include "tb-hash.h"
|
||||
#include "tb-internal.h"
|
||||
#include "tlb-bounds.h"
|
||||
#include "internal-common.h"
|
||||
#include "internal-target.h"
|
||||
#ifdef CONFIG_PLUGIN
|
||||
#include "qemu/plugin-memory.h"
|
||||
#endif
|
||||
#include "tcg/tcg-ldst.h"
|
||||
#include "backend-ldst.h"
|
||||
|
||||
|
||||
/* DEBUG defines, enable DEBUG_TLB_LOG to log to the CPU_LOG_MMU target */
|
||||
/* #define DEBUG_TLB */
|
||||
|
|
@ -768,19 +773,19 @@ void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr,
|
|||
|
||||
assert_cpu_is_self(cpu);
|
||||
|
||||
/* If no page bits are significant, this devolves to tlb_flush. */
|
||||
if (bits < TARGET_PAGE_BITS) {
|
||||
tlb_flush_by_mmuidx(cpu, idxmap);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* If all bits are significant, and len is small,
|
||||
* this devolves to tlb_flush_page.
|
||||
*/
|
||||
if (bits >= TARGET_LONG_BITS && len <= TARGET_PAGE_SIZE) {
|
||||
if (len <= TARGET_PAGE_SIZE && bits >= target_long_bits()) {
|
||||
tlb_flush_page_by_mmuidx(cpu, addr, idxmap);
|
||||
return;
|
||||
}
|
||||
/* If no page bits are significant, this devolves to tlb_flush. */
|
||||
if (bits < TARGET_PAGE_BITS) {
|
||||
tlb_flush_by_mmuidx(cpu, idxmap);
|
||||
return;
|
||||
}
|
||||
|
||||
/* This should already be page aligned */
|
||||
d.addr = addr & TARGET_PAGE_MASK;
|
||||
|
|
@ -806,19 +811,19 @@ void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
|
|||
TLBFlushRangeData d, *p;
|
||||
CPUState *dst_cpu;
|
||||
|
||||
/* If no page bits are significant, this devolves to tlb_flush. */
|
||||
if (bits < TARGET_PAGE_BITS) {
|
||||
tlb_flush_by_mmuidx_all_cpus_synced(src_cpu, idxmap);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* If all bits are significant, and len is small,
|
||||
* this devolves to tlb_flush_page.
|
||||
*/
|
||||
if (bits >= TARGET_LONG_BITS && len <= TARGET_PAGE_SIZE) {
|
||||
if (len <= TARGET_PAGE_SIZE && bits >= target_long_bits()) {
|
||||
tlb_flush_page_by_mmuidx_all_cpus_synced(src_cpu, addr, idxmap);
|
||||
return;
|
||||
}
|
||||
/* If no page bits are significant, this devolves to tlb_flush. */
|
||||
if (bits < TARGET_PAGE_BITS) {
|
||||
tlb_flush_by_mmuidx_all_cpus_synced(src_cpu, idxmap);
|
||||
return;
|
||||
}
|
||||
|
||||
/* This should already be page aligned */
|
||||
d.addr = addr & TARGET_PAGE_MASK;
|
||||
|
|
@ -882,18 +887,17 @@ void tlb_unprotect_code(ram_addr_t ram_addr)
|
|||
*
|
||||
* Called with tlb_c.lock held.
|
||||
*/
|
||||
static void tlb_reset_dirty_range_locked(CPUTLBEntry *tlb_entry,
|
||||
static void tlb_reset_dirty_range_locked(CPUTLBEntryFull *full, CPUTLBEntry *ent,
|
||||
uintptr_t start, uintptr_t length)
|
||||
{
|
||||
uintptr_t addr = tlb_entry->addr_write;
|
||||
const uintptr_t addr = ent->addr_write;
|
||||
int flags = addr | full->slow_flags[MMU_DATA_STORE];
|
||||
|
||||
if ((addr & (TLB_INVALID_MASK | TLB_MMIO |
|
||||
TLB_DISCARD_WRITE | TLB_NOTDIRTY)) == 0) {
|
||||
addr &= TARGET_PAGE_MASK;
|
||||
addr += tlb_entry->addend;
|
||||
if ((addr - start) < length) {
|
||||
qatomic_set(&tlb_entry->addr_write,
|
||||
tlb_entry->addr_write | TLB_NOTDIRTY);
|
||||
flags &= TLB_INVALID_MASK | TLB_MMIO | TLB_DISCARD_WRITE | TLB_NOTDIRTY;
|
||||
if (flags == 0) {
|
||||
uintptr_t host = (addr & TARGET_PAGE_MASK) + ent->addend;
|
||||
if ((host - start) < length) {
|
||||
qatomic_set(&ent->addr_write, addr | TLB_NOTDIRTY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -912,23 +916,25 @@ static inline void copy_tlb_helper_locked(CPUTLBEntry *d, const CPUTLBEntry *s)
|
|||
* We must take tlb_c.lock to avoid racing with another vCPU update. The only
|
||||
* thing actually updated is the target TLB entry ->addr_write flags.
|
||||
*/
|
||||
void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length)
|
||||
void tlb_reset_dirty(CPUState *cpu, uintptr_t start, uintptr_t length)
|
||||
{
|
||||
int mmu_idx;
|
||||
|
||||
qemu_spin_lock(&cpu->neg.tlb.c.lock);
|
||||
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
||||
CPUTLBDesc *desc = &cpu->neg.tlb.d[mmu_idx];
|
||||
CPUTLBDescFast *fast = &cpu->neg.tlb.f[mmu_idx];
|
||||
unsigned int n = tlb_n_entries(fast);
|
||||
unsigned int i;
|
||||
unsigned int n = tlb_n_entries(&cpu->neg.tlb.f[mmu_idx]);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
tlb_reset_dirty_range_locked(&cpu->neg.tlb.f[mmu_idx].table[i],
|
||||
start1, length);
|
||||
tlb_reset_dirty_range_locked(&desc->fulltlb[i], &fast->table[i],
|
||||
start, length);
|
||||
}
|
||||
|
||||
for (i = 0; i < CPU_VTLB_SIZE; i++) {
|
||||
tlb_reset_dirty_range_locked(&cpu->neg.tlb.d[mmu_idx].vtable[i],
|
||||
start1, length);
|
||||
tlb_reset_dirty_range_locked(&desc->vfulltlb[i], &desc->vtable[i],
|
||||
start, length);
|
||||
}
|
||||
}
|
||||
qemu_spin_unlock(&cpu->neg.tlb.c.lock);
|
||||
|
|
@ -1336,7 +1342,7 @@ static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size,
|
|||
trace_memory_notdirty_write_access(mem_vaddr, ram_addr, size);
|
||||
|
||||
if (!cpu_physical_memory_get_dirty_flag(ram_addr, DIRTY_MEMORY_CODE)) {
|
||||
tb_invalidate_phys_range_fast(ram_addr, size, retaddr);
|
||||
tb_invalidate_phys_range_fast(cpu, ram_addr, size, retaddr);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1767,6 +1773,9 @@ static bool mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi,
|
|||
l->page[1].size = l->page[0].size - size0;
|
||||
l->page[0].size = size0;
|
||||
|
||||
l->page[1].addr = cpu->cc->tcg_ops->pointer_wrap(cpu, l->mmu_idx,
|
||||
l->page[1].addr, addr);
|
||||
|
||||
/*
|
||||
* Lookup both pages, recognizing exceptions from either. If the
|
||||
* second lookup potentially resized, refresh first CPUTLBEntryFull.
|
||||
|
|
@ -1865,8 +1874,12 @@ static void *atomic_mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi,
|
|||
goto stop_the_world;
|
||||
}
|
||||
|
||||
/* Collect tlb flags for read. */
|
||||
/* Finish collecting tlb flags for both read and write. */
|
||||
full = &cpu->neg.tlb.d[mmu_idx].fulltlb[index];
|
||||
tlb_addr |= tlbe->addr_read;
|
||||
tlb_addr &= TLB_FLAGS_MASK & ~TLB_FORCE_SLOW;
|
||||
tlb_addr |= full->slow_flags[MMU_DATA_STORE];
|
||||
tlb_addr |= full->slow_flags[MMU_DATA_LOAD];
|
||||
|
||||
/* Notice an IO access or a needs-MMU-lookup access */
|
||||
if (unlikely(tlb_addr & (TLB_MMIO | TLB_DISCARD_WRITE))) {
|
||||
|
|
@ -1876,13 +1889,12 @@ static void *atomic_mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi,
|
|||
}
|
||||
|
||||
hostaddr = (void *)((uintptr_t)addr + tlbe->addend);
|
||||
full = &cpu->neg.tlb.d[mmu_idx].fulltlb[index];
|
||||
|
||||
if (unlikely(tlb_addr & TLB_NOTDIRTY)) {
|
||||
notdirty_write(cpu, addr, size, full, retaddr);
|
||||
}
|
||||
|
||||
if (unlikely(tlb_addr & TLB_FORCE_SLOW)) {
|
||||
if (unlikely(tlb_addr & TLB_WATCHPOINT)) {
|
||||
int wp_flags = 0;
|
||||
|
||||
if (full->slow_flags[MMU_DATA_STORE] & TLB_WATCHPOINT) {
|
||||
|
|
@ -1891,10 +1903,8 @@ static void *atomic_mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi,
|
|||
if (full->slow_flags[MMU_DATA_LOAD] & TLB_WATCHPOINT) {
|
||||
wp_flags |= BP_MEM_READ;
|
||||
}
|
||||
if (wp_flags) {
|
||||
cpu_check_watchpoint(cpu, addr, size,
|
||||
full->attrs, wp_flags, retaddr);
|
||||
}
|
||||
cpu_check_watchpoint(cpu, addr, size,
|
||||
full->attrs, wp_flags, retaddr);
|
||||
}
|
||||
|
||||
return hostaddr;
|
||||
|
|
@ -2321,7 +2331,7 @@ static uint8_t do_ld1_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi,
|
|||
MMULookupLocals l;
|
||||
bool crosspage;
|
||||
|
||||
cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
|
||||
cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD);
|
||||
crosspage = mmu_lookup(cpu, addr, oi, ra, access_type, &l);
|
||||
tcg_debug_assert(!crosspage);
|
||||
|
||||
|
|
@ -2336,7 +2346,7 @@ static uint16_t do_ld2_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi,
|
|||
uint16_t ret;
|
||||
uint8_t a, b;
|
||||
|
||||
cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
|
||||
cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD);
|
||||
crosspage = mmu_lookup(cpu, addr, oi, ra, access_type, &l);
|
||||
if (likely(!crosspage)) {
|
||||
return do_ld_2(cpu, &l.page[0], l.mmu_idx, access_type, l.memop, ra);
|
||||
|
|
@ -2360,7 +2370,7 @@ static uint32_t do_ld4_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi,
|
|||
bool crosspage;
|
||||
uint32_t ret;
|
||||
|
||||
cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
|
||||
cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD);
|
||||
crosspage = mmu_lookup(cpu, addr, oi, ra, access_type, &l);
|
||||
if (likely(!crosspage)) {
|
||||
return do_ld_4(cpu, &l.page[0], l.mmu_idx, access_type, l.memop, ra);
|
||||
|
|
@ -2381,7 +2391,7 @@ static uint64_t do_ld8_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi,
|
|||
bool crosspage;
|
||||
uint64_t ret;
|
||||
|
||||
cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
|
||||
cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD);
|
||||
crosspage = mmu_lookup(cpu, addr, oi, ra, access_type, &l);
|
||||
if (likely(!crosspage)) {
|
||||
return do_ld_8(cpu, &l.page[0], l.mmu_idx, access_type, l.memop, ra);
|
||||
|
|
@ -2404,7 +2414,7 @@ static Int128 do_ld16_mmu(CPUState *cpu, vaddr addr,
|
|||
Int128 ret;
|
||||
int first;
|
||||
|
||||
cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
|
||||
cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD);
|
||||
crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_LOAD, &l);
|
||||
if (likely(!crosspage)) {
|
||||
if (unlikely(l.page[0].flags & TLB_MMIO)) {
|
||||
|
|
@ -2732,7 +2742,7 @@ static void do_st1_mmu(CPUState *cpu, vaddr addr, uint8_t val,
|
|||
MMULookupLocals l;
|
||||
bool crosspage;
|
||||
|
||||
cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
|
||||
cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST);
|
||||
crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_STORE, &l);
|
||||
tcg_debug_assert(!crosspage);
|
||||
|
||||
|
|
@ -2746,7 +2756,7 @@ static void do_st2_mmu(CPUState *cpu, vaddr addr, uint16_t val,
|
|||
bool crosspage;
|
||||
uint8_t a, b;
|
||||
|
||||
cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
|
||||
cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST);
|
||||
crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_STORE, &l);
|
||||
if (likely(!crosspage)) {
|
||||
do_st_2(cpu, &l.page[0], val, l.mmu_idx, l.memop, ra);
|
||||
|
|
@ -2768,7 +2778,7 @@ static void do_st4_mmu(CPUState *cpu, vaddr addr, uint32_t val,
|
|||
MMULookupLocals l;
|
||||
bool crosspage;
|
||||
|
||||
cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
|
||||
cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST);
|
||||
crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_STORE, &l);
|
||||
if (likely(!crosspage)) {
|
||||
do_st_4(cpu, &l.page[0], val, l.mmu_idx, l.memop, ra);
|
||||
|
|
@ -2789,7 +2799,7 @@ static void do_st8_mmu(CPUState *cpu, vaddr addr, uint64_t val,
|
|||
MMULookupLocals l;
|
||||
bool crosspage;
|
||||
|
||||
cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
|
||||
cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST);
|
||||
crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_STORE, &l);
|
||||
if (likely(!crosspage)) {
|
||||
do_st_8(cpu, &l.page[0], val, l.mmu_idx, l.memop, ra);
|
||||
|
|
@ -2812,7 +2822,7 @@ static void do_st16_mmu(CPUState *cpu, vaddr addr, Int128 val,
|
|||
uint64_t a, b;
|
||||
int first;
|
||||
|
||||
cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
|
||||
cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST);
|
||||
crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_STORE, &l);
|
||||
if (likely(!crosspage)) {
|
||||
if (unlikely(l.page[0].flags & TLB_MMIO)) {
|
||||
|
|
@ -2897,54 +2907,45 @@ static void do_st16_mmu(CPUState *cpu, vaddr addr, Int128 val,
|
|||
|
||||
/* Code access functions. */
|
||||
|
||||
uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr addr)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
MemOpIdx oi = make_memop_idx(MO_UB, cpu_mmu_index(cs, true));
|
||||
return do_ld1_mmu(cs, addr, oi, 0, MMU_INST_FETCH);
|
||||
}
|
||||
|
||||
uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr addr)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
MemOpIdx oi = make_memop_idx(MO_TEUW, cpu_mmu_index(cs, true));
|
||||
return do_ld2_mmu(cs, addr, oi, 0, MMU_INST_FETCH);
|
||||
}
|
||||
|
||||
uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr addr)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
MemOpIdx oi = make_memop_idx(MO_TEUL, cpu_mmu_index(cs, true));
|
||||
return do_ld4_mmu(cs, addr, oi, 0, MMU_INST_FETCH);
|
||||
}
|
||||
|
||||
uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr addr)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
MemOpIdx oi = make_memop_idx(MO_TEUQ, cpu_mmu_index(cs, true));
|
||||
return do_ld8_mmu(cs, addr, oi, 0, MMU_INST_FETCH);
|
||||
}
|
||||
|
||||
uint8_t cpu_ldb_code_mmu(CPUArchState *env, abi_ptr addr,
|
||||
uint8_t cpu_ldb_code_mmu(CPUArchState *env, vaddr addr,
|
||||
MemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return do_ld1_mmu(env_cpu(env), addr, oi, retaddr, MMU_INST_FETCH);
|
||||
}
|
||||
|
||||
uint16_t cpu_ldw_code_mmu(CPUArchState *env, abi_ptr addr,
|
||||
uint16_t cpu_ldw_code_mmu(CPUArchState *env, vaddr addr,
|
||||
MemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return do_ld2_mmu(env_cpu(env), addr, oi, retaddr, MMU_INST_FETCH);
|
||||
}
|
||||
|
||||
uint32_t cpu_ldl_code_mmu(CPUArchState *env, abi_ptr addr,
|
||||
uint32_t cpu_ldl_code_mmu(CPUArchState *env, vaddr addr,
|
||||
MemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return do_ld4_mmu(env_cpu(env), addr, oi, retaddr, MMU_INST_FETCH);
|
||||
}
|
||||
|
||||
uint64_t cpu_ldq_code_mmu(CPUArchState *env, abi_ptr addr,
|
||||
uint64_t cpu_ldq_code_mmu(CPUArchState *env, vaddr addr,
|
||||
MemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
return do_ld8_mmu(env_cpu(env), addr, oi, retaddr, MMU_INST_FETCH);
|
||||
}
|
||||
|
||||
/*
|
||||
* Common pointer_wrap implementations.
|
||||
*/
|
||||
|
||||
/*
|
||||
* To be used for strict alignment targets.
|
||||
* Because no accesses are unaligned, no accesses wrap either.
|
||||
*/
|
||||
vaddr cpu_pointer_wrap_notreached(CPUState *cs, int idx, vaddr res, vaddr base)
|
||||
{
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
/* To be used for strict 32-bit targets. */
|
||||
vaddr cpu_pointer_wrap_uint32(CPUState *cs, int idx, vaddr res, vaddr base)
|
||||
{
|
||||
return (uint32_t)res;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
#include "system/replay.h"
|
||||
#include "system/runstate.h"
|
||||
#include "hw/core/cpu.h"
|
||||
#include "system/cpu-timers.h"
|
||||
#include "exec/icount.h"
|
||||
#include "system/cpu-timers-internal.h"
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "exec/cpu-common.h"
|
||||
#include "exec/translation-block.h"
|
||||
#include "exec/mmap-lock.h"
|
||||
#include "accel/tcg/tb-cpu-state.h"
|
||||
|
||||
extern int64_t max_delay;
|
||||
extern int64_t max_advance;
|
||||
|
|
@ -45,9 +47,7 @@ static inline bool cpu_plugin_mem_cbs_enabled(const CPUState *cpu)
|
|||
#endif
|
||||
}
|
||||
|
||||
TranslationBlock *tb_gen_code(CPUState *cpu, vaddr pc,
|
||||
uint64_t cs_base, uint32_t flags,
|
||||
int cflags);
|
||||
TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s);
|
||||
void page_init(void);
|
||||
void tb_htable_init(void);
|
||||
void tb_reset_jump(TranslationBlock *tb, int n);
|
||||
|
|
@ -74,4 +74,71 @@ uint32_t curr_cflags(CPUState *cpu);
|
|||
|
||||
void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr);
|
||||
|
||||
/**
|
||||
* get_page_addr_code_hostp()
|
||||
* @env: CPUArchState
|
||||
* @addr: guest virtual address of guest code
|
||||
*
|
||||
* See get_page_addr_code() (full-system version) for documentation on the
|
||||
* return value.
|
||||
*
|
||||
* Sets *@hostp (when @hostp is non-NULL) as follows.
|
||||
* If the return value is -1, sets *@hostp to NULL. Otherwise, sets *@hostp
|
||||
* to the host address where @addr's content is kept.
|
||||
*
|
||||
* Note: this function can trigger an exception.
|
||||
*/
|
||||
tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr,
|
||||
void **hostp);
|
||||
|
||||
/**
|
||||
* get_page_addr_code()
|
||||
* @env: CPUArchState
|
||||
* @addr: guest virtual address of guest code
|
||||
*
|
||||
* If we cannot translate and execute from the entire RAM page, or if
|
||||
* the region is not backed by RAM, returns -1. Otherwise, returns the
|
||||
* ram_addr_t corresponding to the guest code at @addr.
|
||||
*
|
||||
* Note: this function can trigger an exception.
|
||||
*/
|
||||
static inline tb_page_addr_t get_page_addr_code(CPUArchState *env,
|
||||
vaddr addr)
|
||||
{
|
||||
return get_page_addr_code_hostp(env, addr, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Access to the various translations structures need to be serialised
|
||||
* via locks for consistency. In user-mode emulation access to the
|
||||
* memory related structures are protected with mmap_lock.
|
||||
* In !user-mode we use per-page locks.
|
||||
*/
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
#define assert_memory_lock() tcg_debug_assert(have_mmap_lock())
|
||||
#else
|
||||
#define assert_memory_lock()
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SOFTMMU) && defined(CONFIG_DEBUG_TCG)
|
||||
void assert_no_pages_locked(void);
|
||||
#else
|
||||
static inline void assert_no_pages_locked(void) { }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
static inline void page_table_config_init(void) { }
|
||||
#else
|
||||
void page_table_config_init(void);
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr);
|
||||
#endif /* CONFIG_USER_ONLY */
|
||||
|
||||
void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr);
|
||||
void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr);
|
||||
|
||||
void tcg_get_stats(AccelState *accel, GString *buf);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
* Internal execution defines for qemu (target specific)
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#ifndef ACCEL_TCG_INTERNAL_TARGET_H
|
||||
#define ACCEL_TCG_INTERNAL_TARGET_H
|
||||
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/translation-block.h"
|
||||
#include "tb-internal.h"
|
||||
#include "tcg-target-mo.h"
|
||||
|
||||
/*
|
||||
* Access to the various translations structures need to be serialised
|
||||
* via locks for consistency. In user-mode emulation access to the
|
||||
* memory related structures are protected with mmap_lock.
|
||||
* In !user-mode we use per-page locks.
|
||||
*/
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
#define assert_memory_lock() tcg_debug_assert(have_mmap_lock())
|
||||
#else
|
||||
#define assert_memory_lock()
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SOFTMMU) && defined(CONFIG_DEBUG_TCG)
|
||||
void assert_no_pages_locked(void);
|
||||
#else
|
||||
static inline void assert_no_pages_locked(void) { }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
static inline void page_table_config_init(void) { }
|
||||
#else
|
||||
void page_table_config_init(void);
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr);
|
||||
#endif /* CONFIG_USER_ONLY */
|
||||
|
||||
/**
|
||||
* tcg_req_mo:
|
||||
* @type: TCGBar
|
||||
*
|
||||
* Filter @type to the barrier that is required for the guest
|
||||
* memory ordering vs the host memory ordering. A non-zero
|
||||
* result indicates that some barrier is required.
|
||||
*
|
||||
* If TCG_GUEST_DEFAULT_MO is not defined, assume that the
|
||||
* guest requires strict ordering.
|
||||
*
|
||||
* This is a macro so that it's constant even without optimization.
|
||||
*/
|
||||
#ifdef TCG_GUEST_DEFAULT_MO
|
||||
# define tcg_req_mo(type) \
|
||||
((type) & TCG_GUEST_DEFAULT_MO & ~TCG_TARGET_DEFAULT_MO)
|
||||
#else
|
||||
# define tcg_req_mo(type) ((type) & ~TCG_TARGET_DEFAULT_MO)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* cpu_req_mo:
|
||||
* @type: TCGBar
|
||||
*
|
||||
* If tcg_req_mo indicates a barrier for @type is required
|
||||
* for the guest memory model, issue a host memory barrier.
|
||||
*/
|
||||
#define cpu_req_mo(type) \
|
||||
do { \
|
||||
if (tcg_req_mo(type)) { \
|
||||
smp_mb(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif /* ACCEL_TCG_INTERNAL_H */
|
||||
|
|
@ -123,7 +123,7 @@ void helper_st_i128(CPUArchState *env, uint64_t addr, Int128 val, MemOpIdx oi)
|
|||
* Load helpers for cpu_ldst.h
|
||||
*/
|
||||
|
||||
static void plugin_load_cb(CPUArchState *env, abi_ptr addr,
|
||||
static void plugin_load_cb(CPUArchState *env, vaddr addr,
|
||||
uint64_t value_low,
|
||||
uint64_t value_high,
|
||||
MemOpIdx oi)
|
||||
|
|
@ -135,7 +135,7 @@ static void plugin_load_cb(CPUArchState *env, abi_ptr addr,
|
|||
}
|
||||
}
|
||||
|
||||
uint8_t cpu_ldb_mmu(CPUArchState *env, abi_ptr addr, MemOpIdx oi, uintptr_t ra)
|
||||
uint8_t cpu_ldb_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra)
|
||||
{
|
||||
uint8_t ret;
|
||||
|
||||
|
|
@ -145,7 +145,7 @@ uint8_t cpu_ldb_mmu(CPUArchState *env, abi_ptr addr, MemOpIdx oi, uintptr_t ra)
|
|||
return ret;
|
||||
}
|
||||
|
||||
uint16_t cpu_ldw_mmu(CPUArchState *env, abi_ptr addr,
|
||||
uint16_t cpu_ldw_mmu(CPUArchState *env, vaddr addr,
|
||||
MemOpIdx oi, uintptr_t ra)
|
||||
{
|
||||
uint16_t ret;
|
||||
|
|
@ -156,7 +156,7 @@ uint16_t cpu_ldw_mmu(CPUArchState *env, abi_ptr addr,
|
|||
return ret;
|
||||
}
|
||||
|
||||
uint32_t cpu_ldl_mmu(CPUArchState *env, abi_ptr addr,
|
||||
uint32_t cpu_ldl_mmu(CPUArchState *env, vaddr addr,
|
||||
MemOpIdx oi, uintptr_t ra)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
|
@ -167,7 +167,7 @@ uint32_t cpu_ldl_mmu(CPUArchState *env, abi_ptr addr,
|
|||
return ret;
|
||||
}
|
||||
|
||||
uint64_t cpu_ldq_mmu(CPUArchState *env, abi_ptr addr,
|
||||
uint64_t cpu_ldq_mmu(CPUArchState *env, vaddr addr,
|
||||
MemOpIdx oi, uintptr_t ra)
|
||||
{
|
||||
uint64_t ret;
|
||||
|
|
@ -178,7 +178,7 @@ uint64_t cpu_ldq_mmu(CPUArchState *env, abi_ptr addr,
|
|||
return ret;
|
||||
}
|
||||
|
||||
Int128 cpu_ld16_mmu(CPUArchState *env, abi_ptr addr,
|
||||
Int128 cpu_ld16_mmu(CPUArchState *env, vaddr addr,
|
||||
MemOpIdx oi, uintptr_t ra)
|
||||
{
|
||||
Int128 ret;
|
||||
|
|
@ -193,7 +193,7 @@ Int128 cpu_ld16_mmu(CPUArchState *env, abi_ptr addr,
|
|||
* Store helpers for cpu_ldst.h
|
||||
*/
|
||||
|
||||
static void plugin_store_cb(CPUArchState *env, abi_ptr addr,
|
||||
static void plugin_store_cb(CPUArchState *env, vaddr addr,
|
||||
uint64_t value_low,
|
||||
uint64_t value_high,
|
||||
MemOpIdx oi)
|
||||
|
|
@ -205,14 +205,14 @@ static void plugin_store_cb(CPUArchState *env, abi_ptr addr,
|
|||
}
|
||||
}
|
||||
|
||||
void cpu_stb_mmu(CPUArchState *env, abi_ptr addr, uint8_t val,
|
||||
void cpu_stb_mmu(CPUArchState *env, vaddr addr, uint8_t val,
|
||||
MemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
helper_stb_mmu(env, addr, val, oi, retaddr);
|
||||
plugin_store_cb(env, addr, val, 0, oi);
|
||||
}
|
||||
|
||||
void cpu_stw_mmu(CPUArchState *env, abi_ptr addr, uint16_t val,
|
||||
void cpu_stw_mmu(CPUArchState *env, vaddr addr, uint16_t val,
|
||||
MemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_16);
|
||||
|
|
@ -220,7 +220,7 @@ void cpu_stw_mmu(CPUArchState *env, abi_ptr addr, uint16_t val,
|
|||
plugin_store_cb(env, addr, val, 0, oi);
|
||||
}
|
||||
|
||||
void cpu_stl_mmu(CPUArchState *env, abi_ptr addr, uint32_t val,
|
||||
void cpu_stl_mmu(CPUArchState *env, vaddr addr, uint32_t val,
|
||||
MemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_32);
|
||||
|
|
@ -228,7 +228,7 @@ void cpu_stl_mmu(CPUArchState *env, abi_ptr addr, uint32_t val,
|
|||
plugin_store_cb(env, addr, val, 0, oi);
|
||||
}
|
||||
|
||||
void cpu_stq_mmu(CPUArchState *env, abi_ptr addr, uint64_t val,
|
||||
void cpu_stq_mmu(CPUArchState *env, vaddr addr, uint64_t val,
|
||||
MemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_64);
|
||||
|
|
@ -236,325 +236,10 @@ void cpu_stq_mmu(CPUArchState *env, abi_ptr addr, uint64_t val,
|
|||
plugin_store_cb(env, addr, val, 0, oi);
|
||||
}
|
||||
|
||||
void cpu_st16_mmu(CPUArchState *env, abi_ptr addr, Int128 val,
|
||||
void cpu_st16_mmu(CPUArchState *env, vaddr addr, Int128 val,
|
||||
MemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_128);
|
||||
do_st16_mmu(env_cpu(env), addr, val, oi, retaddr);
|
||||
plugin_store_cb(env, addr, int128_getlo(val), int128_gethi(val), oi);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wrappers of the above
|
||||
*/
|
||||
|
||||
uint32_t cpu_ldub_mmuidx_ra(CPUArchState *env, abi_ptr addr,
|
||||
int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx);
|
||||
return cpu_ldb_mmu(env, addr, oi, ra);
|
||||
}
|
||||
|
||||
int cpu_ldsb_mmuidx_ra(CPUArchState *env, abi_ptr addr,
|
||||
int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
return (int8_t)cpu_ldub_mmuidx_ra(env, addr, mmu_idx, ra);
|
||||
}
|
||||
|
||||
uint32_t cpu_lduw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr,
|
||||
int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
MemOpIdx oi = make_memop_idx(MO_BEUW | MO_UNALN, mmu_idx);
|
||||
return cpu_ldw_mmu(env, addr, oi, ra);
|
||||
}
|
||||
|
||||
int cpu_ldsw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr,
|
||||
int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
return (int16_t)cpu_lduw_be_mmuidx_ra(env, addr, mmu_idx, ra);
|
||||
}
|
||||
|
||||
uint32_t cpu_ldl_be_mmuidx_ra(CPUArchState *env, abi_ptr addr,
|
||||
int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
MemOpIdx oi = make_memop_idx(MO_BEUL | MO_UNALN, mmu_idx);
|
||||
return cpu_ldl_mmu(env, addr, oi, ra);
|
||||
}
|
||||
|
||||
uint64_t cpu_ldq_be_mmuidx_ra(CPUArchState *env, abi_ptr addr,
|
||||
int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
MemOpIdx oi = make_memop_idx(MO_BEUQ | MO_UNALN, mmu_idx);
|
||||
return cpu_ldq_mmu(env, addr, oi, ra);
|
||||
}
|
||||
|
||||
uint32_t cpu_lduw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr,
|
||||
int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
MemOpIdx oi = make_memop_idx(MO_LEUW | MO_UNALN, mmu_idx);
|
||||
return cpu_ldw_mmu(env, addr, oi, ra);
|
||||
}
|
||||
|
||||
int cpu_ldsw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr,
|
||||
int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
return (int16_t)cpu_lduw_le_mmuidx_ra(env, addr, mmu_idx, ra);
|
||||
}
|
||||
|
||||
uint32_t cpu_ldl_le_mmuidx_ra(CPUArchState *env, abi_ptr addr,
|
||||
int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
MemOpIdx oi = make_memop_idx(MO_LEUL | MO_UNALN, mmu_idx);
|
||||
return cpu_ldl_mmu(env, addr, oi, ra);
|
||||
}
|
||||
|
||||
uint64_t cpu_ldq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr,
|
||||
int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
MemOpIdx oi = make_memop_idx(MO_LEUQ | MO_UNALN, mmu_idx);
|
||||
return cpu_ldq_mmu(env, addr, oi, ra);
|
||||
}
|
||||
|
||||
void cpu_stb_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val,
|
||||
int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx);
|
||||
cpu_stb_mmu(env, addr, val, oi, ra);
|
||||
}
|
||||
|
||||
void cpu_stw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val,
|
||||
int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
MemOpIdx oi = make_memop_idx(MO_BEUW | MO_UNALN, mmu_idx);
|
||||
cpu_stw_mmu(env, addr, val, oi, ra);
|
||||
}
|
||||
|
||||
void cpu_stl_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val,
|
||||
int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
MemOpIdx oi = make_memop_idx(MO_BEUL | MO_UNALN, mmu_idx);
|
||||
cpu_stl_mmu(env, addr, val, oi, ra);
|
||||
}
|
||||
|
||||
void cpu_stq_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint64_t val,
|
||||
int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
MemOpIdx oi = make_memop_idx(MO_BEUQ | MO_UNALN, mmu_idx);
|
||||
cpu_stq_mmu(env, addr, val, oi, ra);
|
||||
}
|
||||
|
||||
void cpu_stw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val,
|
||||
int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
MemOpIdx oi = make_memop_idx(MO_LEUW | MO_UNALN, mmu_idx);
|
||||
cpu_stw_mmu(env, addr, val, oi, ra);
|
||||
}
|
||||
|
||||
void cpu_stl_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val,
|
||||
int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
MemOpIdx oi = make_memop_idx(MO_LEUL | MO_UNALN, mmu_idx);
|
||||
cpu_stl_mmu(env, addr, val, oi, ra);
|
||||
}
|
||||
|
||||
void cpu_stq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint64_t val,
|
||||
int mmu_idx, uintptr_t ra)
|
||||
{
|
||||
MemOpIdx oi = make_memop_idx(MO_LEUQ | MO_UNALN, mmu_idx);
|
||||
cpu_stq_mmu(env, addr, val, oi, ra);
|
||||
}
|
||||
|
||||
/*--------------------------*/
|
||||
|
||||
uint32_t cpu_ldub_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
|
||||
{
|
||||
int mmu_index = cpu_mmu_index(env_cpu(env), false);
|
||||
return cpu_ldub_mmuidx_ra(env, addr, mmu_index, ra);
|
||||
}
|
||||
|
||||
int cpu_ldsb_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
|
||||
{
|
||||
return (int8_t)cpu_ldub_data_ra(env, addr, ra);
|
||||
}
|
||||
|
||||
uint32_t cpu_lduw_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
|
||||
{
|
||||
int mmu_index = cpu_mmu_index(env_cpu(env), false);
|
||||
return cpu_lduw_be_mmuidx_ra(env, addr, mmu_index, ra);
|
||||
}
|
||||
|
||||
int cpu_ldsw_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
|
||||
{
|
||||
return (int16_t)cpu_lduw_be_data_ra(env, addr, ra);
|
||||
}
|
||||
|
||||
uint32_t cpu_ldl_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
|
||||
{
|
||||
int mmu_index = cpu_mmu_index(env_cpu(env), false);
|
||||
return cpu_ldl_be_mmuidx_ra(env, addr, mmu_index, ra);
|
||||
}
|
||||
|
||||
uint64_t cpu_ldq_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
|
||||
{
|
||||
int mmu_index = cpu_mmu_index(env_cpu(env), false);
|
||||
return cpu_ldq_be_mmuidx_ra(env, addr, mmu_index, ra);
|
||||
}
|
||||
|
||||
uint32_t cpu_lduw_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
|
||||
{
|
||||
int mmu_index = cpu_mmu_index(env_cpu(env), false);
|
||||
return cpu_lduw_le_mmuidx_ra(env, addr, mmu_index, ra);
|
||||
}
|
||||
|
||||
int cpu_ldsw_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
|
||||
{
|
||||
return (int16_t)cpu_lduw_le_data_ra(env, addr, ra);
|
||||
}
|
||||
|
||||
uint32_t cpu_ldl_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
|
||||
{
|
||||
int mmu_index = cpu_mmu_index(env_cpu(env), false);
|
||||
return cpu_ldl_le_mmuidx_ra(env, addr, mmu_index, ra);
|
||||
}
|
||||
|
||||
uint64_t cpu_ldq_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra)
|
||||
{
|
||||
int mmu_index = cpu_mmu_index(env_cpu(env), false);
|
||||
return cpu_ldq_le_mmuidx_ra(env, addr, mmu_index, ra);
|
||||
}
|
||||
|
||||
void cpu_stb_data_ra(CPUArchState *env, abi_ptr addr,
|
||||
uint32_t val, uintptr_t ra)
|
||||
{
|
||||
int mmu_index = cpu_mmu_index(env_cpu(env), false);
|
||||
cpu_stb_mmuidx_ra(env, addr, val, mmu_index, ra);
|
||||
}
|
||||
|
||||
void cpu_stw_be_data_ra(CPUArchState *env, abi_ptr addr,
|
||||
uint32_t val, uintptr_t ra)
|
||||
{
|
||||
int mmu_index = cpu_mmu_index(env_cpu(env), false);
|
||||
cpu_stw_be_mmuidx_ra(env, addr, val, mmu_index, ra);
|
||||
}
|
||||
|
||||
void cpu_stl_be_data_ra(CPUArchState *env, abi_ptr addr,
|
||||
uint32_t val, uintptr_t ra)
|
||||
{
|
||||
int mmu_index = cpu_mmu_index(env_cpu(env), false);
|
||||
cpu_stl_be_mmuidx_ra(env, addr, val, mmu_index, ra);
|
||||
}
|
||||
|
||||
void cpu_stq_be_data_ra(CPUArchState *env, abi_ptr addr,
|
||||
uint64_t val, uintptr_t ra)
|
||||
{
|
||||
int mmu_index = cpu_mmu_index(env_cpu(env), false);
|
||||
cpu_stq_be_mmuidx_ra(env, addr, val, mmu_index, ra);
|
||||
}
|
||||
|
||||
void cpu_stw_le_data_ra(CPUArchState *env, abi_ptr addr,
|
||||
uint32_t val, uintptr_t ra)
|
||||
{
|
||||
int mmu_index = cpu_mmu_index(env_cpu(env), false);
|
||||
cpu_stw_le_mmuidx_ra(env, addr, val, mmu_index, ra);
|
||||
}
|
||||
|
||||
void cpu_stl_le_data_ra(CPUArchState *env, abi_ptr addr,
|
||||
uint32_t val, uintptr_t ra)
|
||||
{
|
||||
int mmu_index = cpu_mmu_index(env_cpu(env), false);
|
||||
cpu_stl_le_mmuidx_ra(env, addr, val, mmu_index, ra);
|
||||
}
|
||||
|
||||
void cpu_stq_le_data_ra(CPUArchState *env, abi_ptr addr,
|
||||
uint64_t val, uintptr_t ra)
|
||||
{
|
||||
int mmu_index = cpu_mmu_index(env_cpu(env), false);
|
||||
cpu_stq_le_mmuidx_ra(env, addr, val, mmu_index, ra);
|
||||
}
|
||||
|
||||
/*--------------------------*/
|
||||
|
||||
uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr addr)
|
||||
{
|
||||
return cpu_ldub_data_ra(env, addr, 0);
|
||||
}
|
||||
|
||||
int cpu_ldsb_data(CPUArchState *env, abi_ptr addr)
|
||||
{
|
||||
return (int8_t)cpu_ldub_data(env, addr);
|
||||
}
|
||||
|
||||
uint32_t cpu_lduw_be_data(CPUArchState *env, abi_ptr addr)
|
||||
{
|
||||
return cpu_lduw_be_data_ra(env, addr, 0);
|
||||
}
|
||||
|
||||
int cpu_ldsw_be_data(CPUArchState *env, abi_ptr addr)
|
||||
{
|
||||
return (int16_t)cpu_lduw_be_data(env, addr);
|
||||
}
|
||||
|
||||
uint32_t cpu_ldl_be_data(CPUArchState *env, abi_ptr addr)
|
||||
{
|
||||
return cpu_ldl_be_data_ra(env, addr, 0);
|
||||
}
|
||||
|
||||
uint64_t cpu_ldq_be_data(CPUArchState *env, abi_ptr addr)
|
||||
{
|
||||
return cpu_ldq_be_data_ra(env, addr, 0);
|
||||
}
|
||||
|
||||
uint32_t cpu_lduw_le_data(CPUArchState *env, abi_ptr addr)
|
||||
{
|
||||
return cpu_lduw_le_data_ra(env, addr, 0);
|
||||
}
|
||||
|
||||
int cpu_ldsw_le_data(CPUArchState *env, abi_ptr addr)
|
||||
{
|
||||
return (int16_t)cpu_lduw_le_data(env, addr);
|
||||
}
|
||||
|
||||
uint32_t cpu_ldl_le_data(CPUArchState *env, abi_ptr addr)
|
||||
{
|
||||
return cpu_ldl_le_data_ra(env, addr, 0);
|
||||
}
|
||||
|
||||
uint64_t cpu_ldq_le_data(CPUArchState *env, abi_ptr addr)
|
||||
{
|
||||
return cpu_ldq_le_data_ra(env, addr, 0);
|
||||
}
|
||||
|
||||
void cpu_stb_data(CPUArchState *env, abi_ptr addr, uint32_t val)
|
||||
{
|
||||
cpu_stb_data_ra(env, addr, val, 0);
|
||||
}
|
||||
|
||||
void cpu_stw_be_data(CPUArchState *env, abi_ptr addr, uint32_t val)
|
||||
{
|
||||
cpu_stw_be_data_ra(env, addr, val, 0);
|
||||
}
|
||||
|
||||
void cpu_stl_be_data(CPUArchState *env, abi_ptr addr, uint32_t val)
|
||||
{
|
||||
cpu_stl_be_data_ra(env, addr, val, 0);
|
||||
}
|
||||
|
||||
void cpu_stq_be_data(CPUArchState *env, abi_ptr addr, uint64_t val)
|
||||
{
|
||||
cpu_stq_be_data_ra(env, addr, val, 0);
|
||||
}
|
||||
|
||||
void cpu_stw_le_data(CPUArchState *env, abi_ptr addr, uint32_t val)
|
||||
{
|
||||
cpu_stw_le_data_ra(env, addr, val, 0);
|
||||
}
|
||||
|
||||
void cpu_stl_le_data(CPUArchState *env, abi_ptr addr, uint32_t val)
|
||||
{
|
||||
cpu_stl_le_data_ra(env, addr, val, 0);
|
||||
}
|
||||
|
||||
void cpu_stq_le_data(CPUArchState *env, abi_ptr addr, uint64_t val)
|
||||
{
|
||||
cpu_stq_le_data_ra(env, addr, val, 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,34 @@
|
|||
common_ss.add(when: 'CONFIG_TCG', if_true: files(
|
||||
if not have_tcg
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
tcg_ss = ss.source_set()
|
||||
|
||||
tcg_ss.add(files(
|
||||
'cpu-exec.c',
|
||||
'cpu-exec-common.c',
|
||||
'tcg-runtime.c',
|
||||
'tcg-runtime-gvec.c',
|
||||
))
|
||||
tcg_specific_ss = ss.source_set()
|
||||
tcg_specific_ss.add(files(
|
||||
'tcg-all.c',
|
||||
'cpu-exec.c',
|
||||
'tb-maint.c',
|
||||
'tcg-all.c',
|
||||
'tcg-stats.c',
|
||||
'translate-all.c',
|
||||
'translator.c',
|
||||
))
|
||||
tcg_specific_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-exec.c'))
|
||||
tcg_specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_false: files('user-exec-stub.c'))
|
||||
if get_option('plugins')
|
||||
tcg_specific_ss.add(files('plugin-gen.c'))
|
||||
tcg_ss.add(files('plugin-gen.c'))
|
||||
endif
|
||||
specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_specific_ss)
|
||||
|
||||
specific_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files(
|
||||
'cputlb.c',
|
||||
user_ss.add_all(tcg_ss)
|
||||
system_ss.add_all(tcg_ss)
|
||||
|
||||
user_ss.add(files(
|
||||
'user-exec.c',
|
||||
'user-exec-stub.c',
|
||||
))
|
||||
|
||||
system_ss.add(when: ['CONFIG_TCG'], if_true: files(
|
||||
system_ss.add(files(
|
||||
'cputlb.c',
|
||||
'icount-common.c',
|
||||
'monitor.c',
|
||||
'tcg-accel-ops.c',
|
||||
|
|
|
|||
|
|
@ -7,196 +7,13 @@
|
|||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/accel.h"
|
||||
#include "qemu/qht.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/type-helpers.h"
|
||||
#include "qapi/qapi-commands-machine.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "system/cpu-timers.h"
|
||||
#include "system/tcg.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "internal-common.h"
|
||||
#include "tb-context.h"
|
||||
|
||||
|
||||
static void dump_drift_info(GString *buf)
|
||||
{
|
||||
if (!icount_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_string_append_printf(buf, "Host - Guest clock %"PRIi64" ms\n",
|
||||
(cpu_get_clock() - icount_get()) / SCALE_MS);
|
||||
if (icount_align_option) {
|
||||
g_string_append_printf(buf, "Max guest delay %"PRIi64" ms\n",
|
||||
-max_delay / SCALE_MS);
|
||||
g_string_append_printf(buf, "Max guest advance %"PRIi64" ms\n",
|
||||
max_advance / SCALE_MS);
|
||||
} else {
|
||||
g_string_append_printf(buf, "Max guest delay NA\n");
|
||||
g_string_append_printf(buf, "Max guest advance NA\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_accel_info(GString *buf)
|
||||
{
|
||||
AccelState *accel = current_accel();
|
||||
bool one_insn_per_tb = object_property_get_bool(OBJECT(accel),
|
||||
"one-insn-per-tb",
|
||||
&error_fatal);
|
||||
|
||||
g_string_append_printf(buf, "Accelerator settings:\n");
|
||||
g_string_append_printf(buf, "one-insn-per-tb: %s\n\n",
|
||||
one_insn_per_tb ? "on" : "off");
|
||||
}
|
||||
|
||||
static void print_qht_statistics(struct qht_stats hst, GString *buf)
|
||||
{
|
||||
uint32_t hgram_opts;
|
||||
size_t hgram_bins;
|
||||
char *hgram;
|
||||
|
||||
if (!hst.head_buckets) {
|
||||
return;
|
||||
}
|
||||
g_string_append_printf(buf, "TB hash buckets %zu/%zu "
|
||||
"(%0.2f%% head buckets used)\n",
|
||||
hst.used_head_buckets, hst.head_buckets,
|
||||
(double)hst.used_head_buckets /
|
||||
hst.head_buckets * 100);
|
||||
|
||||
hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS;
|
||||
hgram_opts |= QDIST_PR_100X | QDIST_PR_PERCENT;
|
||||
if (qdist_xmax(&hst.occupancy) - qdist_xmin(&hst.occupancy) == 1) {
|
||||
hgram_opts |= QDIST_PR_NODECIMAL;
|
||||
}
|
||||
hgram = qdist_pr(&hst.occupancy, 10, hgram_opts);
|
||||
g_string_append_printf(buf, "TB hash occupancy %0.2f%% avg chain occ. "
|
||||
"Histogram: %s\n",
|
||||
qdist_avg(&hst.occupancy) * 100, hgram);
|
||||
g_free(hgram);
|
||||
|
||||
hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS;
|
||||
hgram_bins = qdist_xmax(&hst.chain) - qdist_xmin(&hst.chain);
|
||||
if (hgram_bins > 10) {
|
||||
hgram_bins = 10;
|
||||
} else {
|
||||
hgram_bins = 0;
|
||||
hgram_opts |= QDIST_PR_NODECIMAL | QDIST_PR_NOBINRANGE;
|
||||
}
|
||||
hgram = qdist_pr(&hst.chain, hgram_bins, hgram_opts);
|
||||
g_string_append_printf(buf, "TB hash avg chain %0.3f buckets. "
|
||||
"Histogram: %s\n",
|
||||
qdist_avg(&hst.chain), hgram);
|
||||
g_free(hgram);
|
||||
}
|
||||
|
||||
struct tb_tree_stats {
|
||||
size_t nb_tbs;
|
||||
size_t host_size;
|
||||
size_t target_size;
|
||||
size_t max_target_size;
|
||||
size_t direct_jmp_count;
|
||||
size_t direct_jmp2_count;
|
||||
size_t cross_page;
|
||||
};
|
||||
|
||||
static gboolean tb_tree_stats_iter(gpointer key, gpointer value, gpointer data)
|
||||
{
|
||||
const TranslationBlock *tb = value;
|
||||
struct tb_tree_stats *tst = data;
|
||||
|
||||
tst->nb_tbs++;
|
||||
tst->host_size += tb->tc.size;
|
||||
tst->target_size += tb->size;
|
||||
if (tb->size > tst->max_target_size) {
|
||||
tst->max_target_size = tb->size;
|
||||
}
|
||||
if (tb->page_addr[1] != -1) {
|
||||
tst->cross_page++;
|
||||
}
|
||||
if (tb->jmp_reset_offset[0] != TB_JMP_OFFSET_INVALID) {
|
||||
tst->direct_jmp_count++;
|
||||
if (tb->jmp_reset_offset[1] != TB_JMP_OFFSET_INVALID) {
|
||||
tst->direct_jmp2_count++;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide)
|
||||
{
|
||||
CPUState *cpu;
|
||||
size_t full = 0, part = 0, elide = 0;
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
full += qatomic_read(&cpu->neg.tlb.c.full_flush_count);
|
||||
part += qatomic_read(&cpu->neg.tlb.c.part_flush_count);
|
||||
elide += qatomic_read(&cpu->neg.tlb.c.elide_flush_count);
|
||||
}
|
||||
*pfull = full;
|
||||
*ppart = part;
|
||||
*pelide = elide;
|
||||
}
|
||||
|
||||
static void tcg_dump_info(GString *buf)
|
||||
{
|
||||
g_string_append_printf(buf, "[TCG profiler not compiled]\n");
|
||||
}
|
||||
|
||||
static void dump_exec_info(GString *buf)
|
||||
{
|
||||
struct tb_tree_stats tst = {};
|
||||
struct qht_stats hst;
|
||||
size_t nb_tbs, flush_full, flush_part, flush_elide;
|
||||
|
||||
tcg_tb_foreach(tb_tree_stats_iter, &tst);
|
||||
nb_tbs = tst.nb_tbs;
|
||||
/* XXX: avoid using doubles ? */
|
||||
g_string_append_printf(buf, "Translation buffer state:\n");
|
||||
/*
|
||||
* Report total code size including the padding and TB structs;
|
||||
* otherwise users might think "-accel tcg,tb-size" is not honoured.
|
||||
* For avg host size we use the precise numbers from tb_tree_stats though.
|
||||
*/
|
||||
g_string_append_printf(buf, "gen code size %zu/%zu\n",
|
||||
tcg_code_size(), tcg_code_capacity());
|
||||
g_string_append_printf(buf, "TB count %zu\n", nb_tbs);
|
||||
g_string_append_printf(buf, "TB avg target size %zu max=%zu bytes\n",
|
||||
nb_tbs ? tst.target_size / nb_tbs : 0,
|
||||
tst.max_target_size);
|
||||
g_string_append_printf(buf, "TB avg host size %zu bytes "
|
||||
"(expansion ratio: %0.1f)\n",
|
||||
nb_tbs ? tst.host_size / nb_tbs : 0,
|
||||
tst.target_size ?
|
||||
(double)tst.host_size / tst.target_size : 0);
|
||||
g_string_append_printf(buf, "cross page TB count %zu (%zu%%)\n",
|
||||
tst.cross_page,
|
||||
nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0);
|
||||
g_string_append_printf(buf, "direct jump count %zu (%zu%%) "
|
||||
"(2 jumps=%zu %zu%%)\n",
|
||||
tst.direct_jmp_count,
|
||||
nb_tbs ? (tst.direct_jmp_count * 100) / nb_tbs : 0,
|
||||
tst.direct_jmp2_count,
|
||||
nb_tbs ? (tst.direct_jmp2_count * 100) / nb_tbs : 0);
|
||||
|
||||
qht_statistics_init(&tb_ctx.htable, &hst);
|
||||
print_qht_statistics(hst, buf);
|
||||
qht_statistics_destroy(&hst);
|
||||
|
||||
g_string_append_printf(buf, "\nStatistics:\n");
|
||||
g_string_append_printf(buf, "TB flush count %u\n",
|
||||
qatomic_read(&tb_ctx.tb_flush_count));
|
||||
g_string_append_printf(buf, "TB invalidate count %u\n",
|
||||
qatomic_read(&tb_ctx.tb_phys_invalidate_count));
|
||||
|
||||
tlb_flush_counts(&flush_full, &flush_part, &flush_elide);
|
||||
g_string_append_printf(buf, "TLB full flushes %zu\n", flush_full);
|
||||
g_string_append_printf(buf, "TLB partial flushes %zu\n", flush_part);
|
||||
g_string_append_printf(buf, "TLB elided flushes %zu\n", flush_elide);
|
||||
tcg_dump_info(buf);
|
||||
}
|
||||
|
||||
HumanReadableText *qmp_x_query_jit(Error **errp)
|
||||
{
|
||||
|
|
@ -207,29 +24,7 @@ HumanReadableText *qmp_x_query_jit(Error **errp)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
dump_accel_info(buf);
|
||||
dump_exec_info(buf);
|
||||
dump_drift_info(buf);
|
||||
|
||||
return human_readable_text_from_str(buf);
|
||||
}
|
||||
|
||||
static void tcg_dump_op_count(GString *buf)
|
||||
{
|
||||
g_string_append_printf(buf, "[TCG profiler not compiled]\n");
|
||||
}
|
||||
|
||||
HumanReadableText *qmp_x_query_opcount(Error **errp)
|
||||
{
|
||||
g_autoptr(GString) buf = g_string_new("");
|
||||
|
||||
if (!tcg_enabled()) {
|
||||
error_setg(errp,
|
||||
"Opcode count information is only available with accel=tcg");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tcg_dump_op_count(buf);
|
||||
tcg_dump_stats(buf);
|
||||
|
||||
return human_readable_text_from_str(buf);
|
||||
}
|
||||
|
|
@ -237,7 +32,6 @@ HumanReadableText *qmp_x_query_opcount(Error **errp)
|
|||
static void hmp_tcg_register(void)
|
||||
{
|
||||
monitor_register_hmp_info_hrt("jit", qmp_x_query_jit);
|
||||
monitor_register_hmp_info_hrt("opcount", qmp_x_query_opcount);
|
||||
}
|
||||
|
||||
type_init(hmp_tcg_register);
|
||||
|
|
|
|||
|
|
@ -22,13 +22,12 @@
|
|||
#include "qemu/osdep.h"
|
||||
#include "qemu/plugin.h"
|
||||
#include "qemu/log.h"
|
||||
#include "cpu.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "tcg/tcg-temp-internal.h"
|
||||
#include "tcg/tcg-op.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "tcg/tcg-op-common.h"
|
||||
#include "exec/plugin-gen.h"
|
||||
#include "exec/translator.h"
|
||||
#include "exec/translation-block.h"
|
||||
|
||||
enum plugin_gen_from {
|
||||
PLUGIN_GEN_FROM_TB,
|
||||
|
|
@ -89,15 +88,13 @@ static void gen_enable_mem_helper(struct qemu_plugin_tb *ptb,
|
|||
qemu_plugin_add_dyn_cb_arr(arr);
|
||||
|
||||
tcg_gen_st_ptr(tcg_constant_ptr((intptr_t)arr), tcg_env,
|
||||
offsetof(CPUState, neg.plugin_mem_cbs) -
|
||||
offsetof(ArchCPU, env));
|
||||
offsetof(CPUState, neg.plugin_mem_cbs) - sizeof(CPUState));
|
||||
}
|
||||
|
||||
static void gen_disable_mem_helper(void)
|
||||
{
|
||||
tcg_gen_st_ptr(tcg_constant_ptr(0), tcg_env,
|
||||
offsetof(CPUState, neg.plugin_mem_cbs) -
|
||||
offsetof(ArchCPU, env));
|
||||
offsetof(CPUState, neg.plugin_mem_cbs) - sizeof(CPUState));
|
||||
}
|
||||
|
||||
static TCGv_i32 gen_cpu_index(void)
|
||||
|
|
@ -113,17 +110,27 @@ static TCGv_i32 gen_cpu_index(void)
|
|||
}
|
||||
TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
|
||||
tcg_gen_ld_i32(cpu_index, tcg_env,
|
||||
-offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
|
||||
offsetof(CPUState, cpu_index) - sizeof(CPUState));
|
||||
return cpu_index;
|
||||
}
|
||||
|
||||
static void gen_udata_cb(struct qemu_plugin_regular_cb *cb)
|
||||
{
|
||||
TCGv_i32 cpu_index = gen_cpu_index();
|
||||
enum qemu_plugin_cb_flags cb_flags =
|
||||
tcg_call_to_qemu_plugin_cb_flags(cb->info->flags);
|
||||
TCGv_i32 flags = tcg_constant_i32(cb_flags);
|
||||
TCGv_i32 clear_flags = tcg_constant_i32(QEMU_PLUGIN_CB_NO_REGS);
|
||||
tcg_gen_st_i32(flags, tcg_env,
|
||||
offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState));
|
||||
tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL,
|
||||
tcgv_i32_temp(cpu_index),
|
||||
tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
|
||||
tcg_gen_st_i32(clear_flags, tcg_env,
|
||||
offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState));
|
||||
tcg_temp_free_i32(cpu_index);
|
||||
tcg_temp_free_i32(flags);
|
||||
tcg_temp_free_i32(clear_flags);
|
||||
}
|
||||
|
||||
static TCGv_ptr gen_plugin_u64_ptr(qemu_plugin_u64 entry)
|
||||
|
|
@ -176,10 +183,20 @@ static void gen_udata_cond_cb(struct qemu_plugin_conditional_cb *cb)
|
|||
tcg_gen_ld_i64(val, ptr, 0);
|
||||
tcg_gen_brcondi_i64(cond, val, cb->imm, after_cb);
|
||||
TCGv_i32 cpu_index = gen_cpu_index();
|
||||
enum qemu_plugin_cb_flags cb_flags =
|
||||
tcg_call_to_qemu_plugin_cb_flags(cb->info->flags);
|
||||
TCGv_i32 flags = tcg_constant_i32(cb_flags);
|
||||
TCGv_i32 clear_flags = tcg_constant_i32(QEMU_PLUGIN_CB_NO_REGS);
|
||||
tcg_gen_st_i32(flags, tcg_env,
|
||||
offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState));
|
||||
tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL,
|
||||
tcgv_i32_temp(cpu_index),
|
||||
tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
|
||||
tcg_gen_st_i32(clear_flags, tcg_env,
|
||||
offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState));
|
||||
tcg_temp_free_i32(cpu_index);
|
||||
tcg_temp_free_i32(flags);
|
||||
tcg_temp_free_i32(clear_flags);
|
||||
gen_set_label(after_cb);
|
||||
|
||||
tcg_temp_free_i64(val);
|
||||
|
|
@ -213,12 +230,22 @@ static void gen_mem_cb(struct qemu_plugin_regular_cb *cb,
|
|||
qemu_plugin_meminfo_t meminfo, TCGv_i64 addr)
|
||||
{
|
||||
TCGv_i32 cpu_index = gen_cpu_index();
|
||||
enum qemu_plugin_cb_flags cb_flags =
|
||||
tcg_call_to_qemu_plugin_cb_flags(cb->info->flags);
|
||||
TCGv_i32 flags = tcg_constant_i32(cb_flags);
|
||||
TCGv_i32 clear_flags = tcg_constant_i32(QEMU_PLUGIN_CB_NO_REGS);
|
||||
tcg_gen_st_i32(flags, tcg_env,
|
||||
offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState));
|
||||
tcg_gen_call4(cb->f.vcpu_mem, cb->info, NULL,
|
||||
tcgv_i32_temp(cpu_index),
|
||||
tcgv_i32_temp(tcg_constant_i32(meminfo)),
|
||||
tcgv_i64_temp(addr),
|
||||
tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
|
||||
tcg_gen_st_i32(clear_flags, tcg_env,
|
||||
offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState));
|
||||
tcg_temp_free_i32(cpu_index);
|
||||
tcg_temp_free_i32(flags);
|
||||
tcg_temp_free_i32(clear_flags);
|
||||
}
|
||||
|
||||
static void inject_cb(struct qemu_plugin_dyn_cb *cb)
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@
|
|||
#ifndef EXEC_TB_HASH_H
|
||||
#define EXEC_TB_HASH_H
|
||||
|
||||
#include "exec/cpu-defs.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/vaddr.h"
|
||||
#include "exec/target_page.h"
|
||||
#include "exec/translation-block.h"
|
||||
#include "qemu/xxhash.h"
|
||||
#include "tb-jmp-cache.h"
|
||||
|
|
|
|||
|
|
@ -9,8 +9,6 @@
|
|||
#ifndef ACCEL_TCG_TB_INTERNAL_TARGET_H
|
||||
#define ACCEL_TCG_TB_INTERNAL_TARGET_H
|
||||
|
||||
#include "exec/cpu-all.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/translation-block.h"
|
||||
|
||||
/*
|
||||
|
|
@ -24,66 +22,34 @@
|
|||
*/
|
||||
#define GETPC_ADJ 2
|
||||
|
||||
#ifdef CONFIG_SOFTMMU
|
||||
|
||||
#define CPU_TLB_DYN_MIN_BITS 6
|
||||
#define CPU_TLB_DYN_DEFAULT_BITS 8
|
||||
|
||||
# if HOST_LONG_BITS == 32
|
||||
/* Make sure we do not require a double-word shift for the TLB load */
|
||||
# define CPU_TLB_DYN_MAX_BITS (32 - TARGET_PAGE_BITS)
|
||||
# else /* HOST_LONG_BITS == 64 */
|
||||
/*
|
||||
* Assuming TARGET_PAGE_BITS==12, with 2**22 entries we can cover 2**(22+12) ==
|
||||
* 2**34 == 16G of address space. This is roughly what one would expect a
|
||||
* TLB to cover in a modern (as of 2018) x86_64 CPU. For instance, Intel
|
||||
* Skylake's Level-2 STLB has 16 1G entries.
|
||||
* Also, make sure we do not size the TLB past the guest's address space.
|
||||
*/
|
||||
# ifdef TARGET_PAGE_BITS_VARY
|
||||
# define CPU_TLB_DYN_MAX_BITS \
|
||||
MIN(22, TARGET_VIRT_ADDR_SPACE_BITS - TARGET_PAGE_BITS)
|
||||
# else
|
||||
# define CPU_TLB_DYN_MAX_BITS \
|
||||
MIN_CONST(22, TARGET_VIRT_ADDR_SPACE_BITS - TARGET_PAGE_BITS)
|
||||
# endif
|
||||
# endif
|
||||
|
||||
#endif /* CONFIG_SOFTMMU */
|
||||
void tb_lock_page0(tb_page_addr_t);
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
#include "user/page-protection.h"
|
||||
/*
|
||||
* For user-only, page_protect sets the page read-only.
|
||||
* Since most execution is already on read-only pages, and we'd need to
|
||||
* account for other TBs on the same page, defer undoing any page protection
|
||||
* until we receive the write fault.
|
||||
*/
|
||||
static inline void tb_lock_page0(tb_page_addr_t p0)
|
||||
{
|
||||
page_protect(p0);
|
||||
}
|
||||
|
||||
static inline void tb_lock_page1(tb_page_addr_t p0, tb_page_addr_t p1)
|
||||
{
|
||||
page_protect(p1);
|
||||
tb_lock_page0(p1);
|
||||
}
|
||||
|
||||
static inline void tb_unlock_page1(tb_page_addr_t p0, tb_page_addr_t p1) { }
|
||||
static inline void tb_unlock_pages(TranslationBlock *tb) { }
|
||||
#else
|
||||
void tb_lock_page0(tb_page_addr_t);
|
||||
void tb_lock_page1(tb_page_addr_t, tb_page_addr_t);
|
||||
void tb_unlock_page1(tb_page_addr_t, tb_page_addr_t);
|
||||
void tb_unlock_pages(TranslationBlock *);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SOFTMMU
|
||||
void tb_invalidate_phys_range_fast(ram_addr_t ram_addr,
|
||||
unsigned size,
|
||||
uintptr_t retaddr);
|
||||
void tb_invalidate_phys_range_fast(CPUState *cpu, ram_addr_t ram_addr,
|
||||
unsigned size, uintptr_t retaddr);
|
||||
#endif /* CONFIG_SOFTMMU */
|
||||
|
||||
bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc);
|
||||
bool tb_invalidate_phys_page_unwind(CPUState *cpu, tb_page_addr_t addr,
|
||||
uintptr_t pc);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -22,9 +22,11 @@
|
|||
#include "qemu/qtree.h"
|
||||
#include "exec/cputlb.h"
|
||||
#include "exec/log.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/page-protection.h"
|
||||
#include "exec/mmap-lock.h"
|
||||
#include "exec/tb-flush.h"
|
||||
#include "exec/target_page.h"
|
||||
#include "accel/tcg/cpu-ops.h"
|
||||
#include "tb-internal.h"
|
||||
#include "system/tcg.h"
|
||||
#include "tcg/tcg.h"
|
||||
|
|
@ -32,7 +34,6 @@
|
|||
#include "tb-context.h"
|
||||
#include "tb-internal.h"
|
||||
#include "internal-common.h"
|
||||
#include "internal-target.h"
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
#include "user/page-protection.h"
|
||||
#endif
|
||||
|
|
@ -156,11 +157,7 @@ static PageForEachNext foreach_tb_next(PageForEachNext tb,
|
|||
/*
|
||||
* In system mode we want L1_MAP to be based on ram offsets.
|
||||
*/
|
||||
#if HOST_LONG_BITS < TARGET_PHYS_ADDR_SPACE_BITS
|
||||
# define L1_MAP_ADDR_SPACE_BITS HOST_LONG_BITS
|
||||
#else
|
||||
# define L1_MAP_ADDR_SPACE_BITS TARGET_PHYS_ADDR_SPACE_BITS
|
||||
#endif
|
||||
#define L1_MAP_ADDR_SPACE_BITS HOST_LONG_BITS
|
||||
|
||||
/* Size of the L2 (and L3, etc) page tables. */
|
||||
#define V_L2_BITS 10
|
||||
|
|
@ -1009,7 +1006,8 @@ TranslationBlock *tb_link_page(TranslationBlock *tb)
|
|||
* Called with mmap_lock held for user-mode emulation.
|
||||
* NOTE: this function must not be called while a TB is running.
|
||||
*/
|
||||
void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last)
|
||||
void tb_invalidate_phys_range(CPUState *cpu, tb_page_addr_t start,
|
||||
tb_page_addr_t last)
|
||||
{
|
||||
TranslationBlock *tb;
|
||||
PageForEachNext n;
|
||||
|
|
@ -1032,17 +1030,16 @@ static void tb_invalidate_phys_page(tb_page_addr_t addr)
|
|||
|
||||
start = addr & TARGET_PAGE_MASK;
|
||||
last = addr | ~TARGET_PAGE_MASK;
|
||||
tb_invalidate_phys_range(start, last);
|
||||
tb_invalidate_phys_range(NULL, start, last);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called with mmap_lock held. If pc is not 0 then it indicates the
|
||||
* host PC of the faulting store instruction that caused this invalidate.
|
||||
* Returns true if the caller needs to abort execution of the current
|
||||
* TB (because it was modified by this store and the guest CPU has
|
||||
* precise-SMC semantics).
|
||||
* Returns true if the caller needs to abort execution of the current TB.
|
||||
*/
|
||||
bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc)
|
||||
bool tb_invalidate_phys_page_unwind(CPUState *cpu, tb_page_addr_t addr,
|
||||
uintptr_t pc)
|
||||
{
|
||||
TranslationBlock *current_tb;
|
||||
bool current_tb_modified;
|
||||
|
|
@ -1054,10 +1051,7 @@ bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc)
|
|||
* Without precise smc semantics, or when outside of a TB,
|
||||
* we can skip to invalidate.
|
||||
*/
|
||||
#ifndef TARGET_HAS_PRECISE_SMC
|
||||
pc = 0;
|
||||
#endif
|
||||
if (!pc) {
|
||||
if (!pc || !cpu || !cpu->cc->tcg_ops->precise_smc) {
|
||||
tb_invalidate_phys_page(addr);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1080,15 +1074,14 @@ bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc)
|
|||
* the CPU state.
|
||||
*/
|
||||
current_tb_modified = true;
|
||||
cpu_restore_state_from_tb(current_cpu, current_tb, pc);
|
||||
cpu_restore_state_from_tb(cpu, current_tb, pc);
|
||||
}
|
||||
tb_phys_invalidate__locked(tb);
|
||||
}
|
||||
|
||||
if (current_tb_modified) {
|
||||
/* Force execution of one insn next time. */
|
||||
CPUState *cpu = current_cpu;
|
||||
cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(current_cpu);
|
||||
cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -1097,23 +1090,28 @@ bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc)
|
|||
/*
|
||||
* @p must be non-NULL.
|
||||
* Call with all @pages locked.
|
||||
* (@cpu, @retaddr) may be (NULL, 0) outside of a cpu context,
|
||||
* in which case precise_smc need not be detected.
|
||||
*/
|
||||
static void
|
||||
tb_invalidate_phys_page_range__locked(struct page_collection *pages,
|
||||
tb_invalidate_phys_page_range__locked(CPUState *cpu,
|
||||
struct page_collection *pages,
|
||||
PageDesc *p, tb_page_addr_t start,
|
||||
tb_page_addr_t last,
|
||||
uintptr_t retaddr)
|
||||
{
|
||||
TranslationBlock *tb;
|
||||
PageForEachNext n;
|
||||
#ifdef TARGET_HAS_PRECISE_SMC
|
||||
bool current_tb_modified = false;
|
||||
TranslationBlock *current_tb = retaddr ? tcg_tb_lookup(retaddr) : NULL;
|
||||
#endif /* TARGET_HAS_PRECISE_SMC */
|
||||
TranslationBlock *current_tb = NULL;
|
||||
|
||||
/* Range may not cross a page. */
|
||||
tcg_debug_assert(((start ^ last) & TARGET_PAGE_MASK) == 0);
|
||||
|
||||
if (retaddr && cpu && cpu->cc->tcg_ops->precise_smc) {
|
||||
current_tb = tcg_tb_lookup(retaddr);
|
||||
}
|
||||
|
||||
/*
|
||||
* We remove all the TBs in the range [start, last].
|
||||
* XXX: see if in some cases it could be faster to invalidate all the code
|
||||
|
|
@ -1131,8 +1129,7 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages,
|
|||
tb_last = tb_start + (tb_last & ~TARGET_PAGE_MASK);
|
||||
}
|
||||
if (!(tb_last < start || tb_start > last)) {
|
||||
#ifdef TARGET_HAS_PRECISE_SMC
|
||||
if (current_tb == tb &&
|
||||
if (unlikely(current_tb == tb) &&
|
||||
(tb_cflags(current_tb) & CF_COUNT_MASK) != 1) {
|
||||
/*
|
||||
* If we are modifying the current TB, we must stop
|
||||
|
|
@ -1142,9 +1139,8 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages,
|
|||
* restore the CPU state.
|
||||
*/
|
||||
current_tb_modified = true;
|
||||
cpu_restore_state_from_tb(current_cpu, current_tb, retaddr);
|
||||
cpu_restore_state_from_tb(cpu, current_tb, retaddr);
|
||||
}
|
||||
#endif /* TARGET_HAS_PRECISE_SMC */
|
||||
tb_phys_invalidate__locked(tb);
|
||||
}
|
||||
}
|
||||
|
|
@ -1154,15 +1150,13 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages,
|
|||
tlb_unprotect_code(start);
|
||||
}
|
||||
|
||||
#ifdef TARGET_HAS_PRECISE_SMC
|
||||
if (current_tb_modified) {
|
||||
if (unlikely(current_tb_modified)) {
|
||||
page_collection_unlock(pages);
|
||||
/* Force execution of one insn next time. */
|
||||
current_cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(current_cpu);
|
||||
cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu);
|
||||
mmap_unlock();
|
||||
cpu_loop_exit_noexc(current_cpu);
|
||||
cpu_loop_exit_noexc(cpu);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1172,7 +1166,8 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages,
|
|||
* access: the virtual CPU will exit the current TB if code is modified inside
|
||||
* this TB.
|
||||
*/
|
||||
void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last)
|
||||
void tb_invalidate_phys_range(CPUState *cpu, tb_page_addr_t start,
|
||||
tb_page_addr_t last)
|
||||
{
|
||||
struct page_collection *pages;
|
||||
tb_page_addr_t index, index_last;
|
||||
|
|
@ -1191,44 +1186,30 @@ void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last)
|
|||
page_start = index << TARGET_PAGE_BITS;
|
||||
page_last = page_start | ~TARGET_PAGE_MASK;
|
||||
page_last = MIN(page_last, last);
|
||||
tb_invalidate_phys_page_range__locked(pages, pd,
|
||||
tb_invalidate_phys_page_range__locked(cpu, pages, pd,
|
||||
page_start, page_last, 0);
|
||||
}
|
||||
page_collection_unlock(pages);
|
||||
}
|
||||
|
||||
/*
|
||||
* Call with all @pages in the range [@start, @start + len[ locked.
|
||||
*/
|
||||
static void tb_invalidate_phys_page_fast__locked(struct page_collection *pages,
|
||||
tb_page_addr_t start,
|
||||
unsigned len, uintptr_t ra)
|
||||
{
|
||||
PageDesc *p;
|
||||
|
||||
p = page_find(start >> TARGET_PAGE_BITS);
|
||||
if (!p) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert_page_locked(p);
|
||||
tb_invalidate_phys_page_range__locked(pages, p, start, start + len - 1, ra);
|
||||
}
|
||||
|
||||
/*
|
||||
* len must be <= 8 and start must be a multiple of len.
|
||||
* Called via softmmu_template.h when code areas are written to with
|
||||
* iothread mutex not held.
|
||||
*/
|
||||
void tb_invalidate_phys_range_fast(ram_addr_t ram_addr,
|
||||
unsigned size,
|
||||
uintptr_t retaddr)
|
||||
void tb_invalidate_phys_range_fast(CPUState *cpu, ram_addr_t start,
|
||||
unsigned len, uintptr_t ra)
|
||||
{
|
||||
struct page_collection *pages;
|
||||
PageDesc *p = page_find(start >> TARGET_PAGE_BITS);
|
||||
|
||||
pages = page_collection_lock(ram_addr, ram_addr + size - 1);
|
||||
tb_invalidate_phys_page_fast__locked(pages, ram_addr, size, retaddr);
|
||||
page_collection_unlock(pages);
|
||||
if (p) {
|
||||
ram_addr_t last = start + len - 1;
|
||||
struct page_collection *pages = page_collection_lock(start, last);
|
||||
|
||||
tb_invalidate_phys_page_range__locked(cpu, pages, p,
|
||||
start, last, ra);
|
||||
page_collection_unlock(pages);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CONFIG_USER_ONLY */
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "system/replay.h"
|
||||
#include "system/cpu-timers.h"
|
||||
#include "exec/icount.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/guest-random.h"
|
||||
#include "hw/core/cpu.h"
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
#include "qemu/osdep.h"
|
||||
#include "system/tcg.h"
|
||||
#include "system/replay.h"
|
||||
#include "system/cpu-timers.h"
|
||||
#include "exec/icount.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/notify.h"
|
||||
#include "qemu/guest-random.h"
|
||||
|
|
@ -113,7 +113,6 @@ static void *mttcg_cpu_thread_fn(void *arg)
|
|||
}
|
||||
}
|
||||
|
||||
qatomic_set_mb(&cpu->exit_request, 0);
|
||||
qemu_wait_io_event(cpu);
|
||||
} while (!cpu->unplug || cpu_can_run(cpu));
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
#include "qemu/lockable.h"
|
||||
#include "system/tcg.h"
|
||||
#include "system/replay.h"
|
||||
#include "system/cpu-timers.h"
|
||||
#include "exec/icount.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/notify.h"
|
||||
#include "qemu/guest-random.h"
|
||||
|
|
|
|||
|
|
@ -26,10 +26,11 @@
|
|||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "system/accel-ops.h"
|
||||
#include "accel/accel-ops.h"
|
||||
#include "accel/accel-cpu-ops.h"
|
||||
#include "system/tcg.h"
|
||||
#include "system/replay.h"
|
||||
#include "system/cpu-timers.h"
|
||||
#include "exec/icount.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/guest-random.h"
|
||||
#include "qemu/timer.h"
|
||||
|
|
@ -37,6 +38,7 @@
|
|||
#include "exec/hwaddr.h"
|
||||
#include "exec/tb-flush.h"
|
||||
#include "exec/translation-block.h"
|
||||
#include "exec/watchpoint.h"
|
||||
#include "gdbstub/enums.h"
|
||||
|
||||
#include "hw/core/cpu.h"
|
||||
|
|
@ -79,6 +81,9 @@ int tcg_cpu_exec(CPUState *cpu)
|
|||
cpu_exec_start(cpu);
|
||||
ret = cpu_exec(cpu);
|
||||
cpu_exec_end(cpu);
|
||||
|
||||
qatomic_set_mb(&cpu->exit_request, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -92,8 +97,6 @@ static void tcg_cpu_reset_hold(CPUState *cpu)
|
|||
/* mask must never be zero, except for A20 change call */
|
||||
void tcg_handle_interrupt(CPUState *cpu, int mask)
|
||||
{
|
||||
g_assert(bql_locked());
|
||||
|
||||
cpu->interrupt_request |= mask;
|
||||
|
||||
/*
|
||||
|
|
@ -197,8 +200,10 @@ static inline void tcg_remove_all_breakpoints(CPUState *cpu)
|
|||
cpu_watchpoint_remove_all(cpu, BP_GDB);
|
||||
}
|
||||
|
||||
static void tcg_accel_ops_init(AccelOpsClass *ops)
|
||||
static void tcg_accel_ops_init(AccelClass *ac)
|
||||
{
|
||||
AccelOpsClass *ops = ac->ops;
|
||||
|
||||
if (qemu_tcg_mttcg_enabled()) {
|
||||
ops->create_vcpu_thread = mttcg_start_vcpu_thread;
|
||||
ops->kick_vcpu_thread = mttcg_kick_vcpu_thread;
|
||||
|
|
@ -223,7 +228,7 @@ static void tcg_accel_ops_init(AccelOpsClass *ops)
|
|||
ops->remove_all_breakpoints = tcg_remove_all_breakpoints;
|
||||
}
|
||||
|
||||
static void tcg_accel_ops_class_init(ObjectClass *oc, void *data)
|
||||
static void tcg_accel_ops_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
|
||||
|
||||
|
|
|
|||
|
|
@ -26,27 +26,29 @@
|
|||
#include "qemu/osdep.h"
|
||||
#include "system/tcg.h"
|
||||
#include "exec/replay-core.h"
|
||||
#include "system/cpu-timers.h"
|
||||
#include "exec/icount.h"
|
||||
#include "tcg/startup.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/accel.h"
|
||||
#include "qemu/atomic.h"
|
||||
#include "qapi/qapi-types-common.h"
|
||||
#include "qapi/qapi-builtin-visit.h"
|
||||
#include "qemu/units.h"
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
#include "hw/qdev-core.h"
|
||||
#else
|
||||
#include "qemu/target-info.h"
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
#include "hw/boards.h"
|
||||
#endif
|
||||
#include "accel/accel-ops.h"
|
||||
#include "accel/accel-cpu-ops.h"
|
||||
#include "accel/tcg/cpu-ops.h"
|
||||
#include "internal-common.h"
|
||||
#include "cpu-param.h"
|
||||
|
||||
|
||||
struct TCGState {
|
||||
AccelState parent_obj;
|
||||
|
||||
bool mttcg_enabled;
|
||||
OnOffAuto mttcg_enabled;
|
||||
bool one_insn_per_tb;
|
||||
int splitwx_enabled;
|
||||
unsigned long tb_size;
|
||||
|
|
@ -58,40 +60,18 @@ typedef struct TCGState TCGState;
|
|||
DECLARE_INSTANCE_CHECKER(TCGState, TCG_STATE,
|
||||
TYPE_TCG_ACCEL)
|
||||
|
||||
/*
|
||||
* We default to false if we know other options have been enabled
|
||||
* which are currently incompatible with MTTCG. Otherwise when each
|
||||
* guest (target) has been updated to support:
|
||||
* - atomic instructions
|
||||
* - memory ordering primitives (barriers)
|
||||
* they can set the appropriate CONFIG flags in ${target}-softmmu.mak
|
||||
*
|
||||
* Once a guest architecture has been converted to the new primitives
|
||||
* there is one remaining limitation to check:
|
||||
* - The guest can't be oversized (e.g. 64 bit guest on 32 bit host)
|
||||
*/
|
||||
|
||||
static bool default_mttcg_enabled(void)
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
bool qemu_tcg_mttcg_enabled(void)
|
||||
{
|
||||
if (icount_enabled()) {
|
||||
return false;
|
||||
}
|
||||
#ifdef TARGET_SUPPORTS_MTTCG
|
||||
# ifndef TCG_GUEST_DEFAULT_MO
|
||||
# error "TARGET_SUPPORTS_MTTCG without TCG_GUEST_DEFAULT_MO"
|
||||
# endif
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
TCGState *s = TCG_STATE(current_accel());
|
||||
return s->mttcg_enabled == ON_OFF_AUTO_ON;
|
||||
}
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
|
||||
static void tcg_accel_instance_init(Object *obj)
|
||||
{
|
||||
TCGState *s = TCG_STATE(obj);
|
||||
|
||||
s->mttcg_enabled = default_mttcg_enabled();
|
||||
|
||||
/* If debugging enabled, default "auto on", otherwise off. */
|
||||
#if defined(CONFIG_DEBUG_TCG) && !defined(CONFIG_USER_ONLY)
|
||||
s->splitwx_enabled = -1;
|
||||
|
|
@ -100,24 +80,57 @@ static void tcg_accel_instance_init(Object *obj)
|
|||
#endif
|
||||
}
|
||||
|
||||
bool mttcg_enabled;
|
||||
bool one_insn_per_tb;
|
||||
|
||||
static int tcg_init_machine(MachineState *ms)
|
||||
static int tcg_init_machine(AccelState *as, MachineState *ms)
|
||||
{
|
||||
TCGState *s = TCG_STATE(current_accel());
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
unsigned max_cpus = 1;
|
||||
#else
|
||||
unsigned max_cpus = ms->smp.max_cpus;
|
||||
TCGState *s = TCG_STATE(as);
|
||||
unsigned max_threads = 1;
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
CPUClass *cc = CPU_CLASS(object_class_by_name(target_cpu_type()));
|
||||
bool mttcg_supported = cc->tcg_ops->mttcg_supported;
|
||||
|
||||
switch (s->mttcg_enabled) {
|
||||
case ON_OFF_AUTO_AUTO:
|
||||
/*
|
||||
* We default to false if we know other options have been enabled
|
||||
* which are currently incompatible with MTTCG. Otherwise when each
|
||||
* guest (target) has been updated to support:
|
||||
* - atomic instructions
|
||||
* - memory ordering primitives (barriers)
|
||||
* they can set the appropriate CONFIG flags in ${target}-softmmu.mak
|
||||
*
|
||||
* Once a guest architecture has been converted to the new primitives
|
||||
* there is one remaining limitation to check:
|
||||
* - The guest can't be oversized (e.g. 64 bit guest on 32 bit host)
|
||||
*/
|
||||
if (mttcg_supported && !icount_enabled()) {
|
||||
s->mttcg_enabled = ON_OFF_AUTO_ON;
|
||||
max_threads = ms->smp.max_cpus;
|
||||
} else {
|
||||
s->mttcg_enabled = ON_OFF_AUTO_OFF;
|
||||
}
|
||||
break;
|
||||
case ON_OFF_AUTO_ON:
|
||||
if (!mttcg_supported) {
|
||||
warn_report("Guest not yet converted to MTTCG - "
|
||||
"you may get unexpected results");
|
||||
}
|
||||
max_threads = ms->smp.max_cpus;
|
||||
break;
|
||||
case ON_OFF_AUTO_OFF:
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
#endif
|
||||
|
||||
tcg_allowed = true;
|
||||
mttcg_enabled = s->mttcg_enabled;
|
||||
|
||||
page_init();
|
||||
tb_htable_init();
|
||||
tcg_init(s->tb_size * MiB, s->splitwx_enabled, max_cpus);
|
||||
tcg_init(s->tb_size * MiB, s->splitwx_enabled, max_threads);
|
||||
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
/*
|
||||
|
|
@ -138,7 +151,7 @@ static char *tcg_get_thread(Object *obj, Error **errp)
|
|||
{
|
||||
TCGState *s = TCG_STATE(obj);
|
||||
|
||||
return g_strdup(s->mttcg_enabled ? "multi" : "single");
|
||||
return g_strdup(s->mttcg_enabled == ON_OFF_AUTO_ON ? "multi" : "single");
|
||||
}
|
||||
|
||||
static void tcg_set_thread(Object *obj, const char *value, Error **errp)
|
||||
|
|
@ -149,14 +162,10 @@ static void tcg_set_thread(Object *obj, const char *value, Error **errp)
|
|||
if (icount_enabled()) {
|
||||
error_setg(errp, "No MTTCG when icount is enabled");
|
||||
} else {
|
||||
#ifndef TARGET_SUPPORTS_MTTCG
|
||||
warn_report("Guest not yet converted to MTTCG - "
|
||||
"you may get unexpected results");
|
||||
#endif
|
||||
s->mttcg_enabled = true;
|
||||
s->mttcg_enabled = ON_OFF_AUTO_ON;
|
||||
}
|
||||
} else if (strcmp(value, "single") == 0) {
|
||||
s->mttcg_enabled = false;
|
||||
s->mttcg_enabled = ON_OFF_AUTO_OFF;
|
||||
} else {
|
||||
error_setg(errp, "Invalid 'thread' setting %s", value);
|
||||
}
|
||||
|
|
@ -212,7 +221,7 @@ static void tcg_set_one_insn_per_tb(Object *obj, bool value, Error **errp)
|
|||
qatomic_set(&one_insn_per_tb, value);
|
||||
}
|
||||
|
||||
static int tcg_gdbstub_supported_sstep_flags(void)
|
||||
static int tcg_gdbstub_supported_sstep_flags(AccelState *as)
|
||||
{
|
||||
/*
|
||||
* In replay mode all events will come from the log and can't be
|
||||
|
|
@ -227,13 +236,14 @@ static int tcg_gdbstub_supported_sstep_flags(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void tcg_accel_class_init(ObjectClass *oc, void *data)
|
||||
static void tcg_accel_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
AccelClass *ac = ACCEL_CLASS(oc);
|
||||
ac->name = "tcg";
|
||||
ac->init_machine = tcg_init_machine;
|
||||
ac->cpu_common_realize = tcg_exec_realizefn;
|
||||
ac->cpu_common_unrealize = tcg_exec_unrealizefn;
|
||||
ac->get_stats = tcg_get_stats;
|
||||
ac->allowed = &tcg_allowed;
|
||||
ac->gdbstub_supported_sstep_flags = tcg_gdbstub_supported_sstep_flags;
|
||||
|
||||
|
|
|
|||
219
accel/tcg/tcg-stats.c
Normal file
219
accel/tcg/tcg-stats.c
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*
|
||||
* QEMU TCG statistics
|
||||
*
|
||||
* Copyright (c) 2003-2005 Fabrice Bellard
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/accel.h"
|
||||
#include "qemu/qht.h"
|
||||
#include "qapi/error.h"
|
||||
#include "system/cpu-timers.h"
|
||||
#include "exec/icount.h"
|
||||
#include "hw/core/cpu.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "internal-common.h"
|
||||
#include "tb-context.h"
|
||||
#include <math.h>
|
||||
|
||||
static void dump_drift_info(GString *buf)
|
||||
{
|
||||
if (!icount_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_string_append_printf(buf, "Host - Guest clock %"PRIi64" ms\n",
|
||||
(cpu_get_clock() - icount_get()) / SCALE_MS);
|
||||
if (icount_align_option) {
|
||||
g_string_append_printf(buf, "Max guest delay %"PRIi64" ms\n",
|
||||
-max_delay / SCALE_MS);
|
||||
g_string_append_printf(buf, "Max guest advance %"PRIi64" ms\n",
|
||||
max_advance / SCALE_MS);
|
||||
} else {
|
||||
g_string_append_printf(buf, "Max guest delay NA\n");
|
||||
g_string_append_printf(buf, "Max guest advance NA\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_accel_info(AccelState *accel, GString *buf)
|
||||
{
|
||||
bool one_insn_per_tb = object_property_get_bool(OBJECT(accel),
|
||||
"one-insn-per-tb",
|
||||
&error_fatal);
|
||||
|
||||
g_string_append_printf(buf, "Accelerator settings:\n");
|
||||
g_string_append_printf(buf, "one-insn-per-tb: %s\n\n",
|
||||
one_insn_per_tb ? "on" : "off");
|
||||
}
|
||||
|
||||
static void print_qht_statistics(struct qht_stats hst, GString *buf)
|
||||
{
|
||||
uint32_t hgram_opts;
|
||||
size_t hgram_bins;
|
||||
char *hgram;
|
||||
double avg;
|
||||
|
||||
if (!hst.head_buckets) {
|
||||
return;
|
||||
}
|
||||
g_string_append_printf(buf, "TB hash buckets %zu/%zu "
|
||||
"(%0.2f%% head buckets used)\n",
|
||||
hst.used_head_buckets, hst.head_buckets,
|
||||
(double)hst.used_head_buckets /
|
||||
hst.head_buckets * 100);
|
||||
|
||||
hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS;
|
||||
hgram_opts |= QDIST_PR_100X | QDIST_PR_PERCENT;
|
||||
if (qdist_xmax(&hst.occupancy) - qdist_xmin(&hst.occupancy) == 1) {
|
||||
hgram_opts |= QDIST_PR_NODECIMAL;
|
||||
}
|
||||
hgram = qdist_pr(&hst.occupancy, 10, hgram_opts);
|
||||
avg = qdist_avg(&hst.occupancy);
|
||||
if (!isnan(avg)) {
|
||||
g_string_append_printf(buf, "TB hash occupancy "
|
||||
"%0.2f%% avg chain occ. "
|
||||
"Histogram: %s\n",
|
||||
avg * 100, hgram);
|
||||
}
|
||||
g_free(hgram);
|
||||
|
||||
hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS;
|
||||
hgram_bins = qdist_xmax(&hst.chain) - qdist_xmin(&hst.chain);
|
||||
if (hgram_bins > 10) {
|
||||
hgram_bins = 10;
|
||||
} else {
|
||||
hgram_bins = 0;
|
||||
hgram_opts |= QDIST_PR_NODECIMAL | QDIST_PR_NOBINRANGE;
|
||||
}
|
||||
hgram = qdist_pr(&hst.chain, hgram_bins, hgram_opts);
|
||||
avg = qdist_avg(&hst.chain);
|
||||
if (!isnan(avg)) {
|
||||
g_string_append_printf(buf, "TB hash avg chain %0.3f buckets. "
|
||||
"Histogram: %s\n",
|
||||
avg, hgram);
|
||||
}
|
||||
g_free(hgram);
|
||||
}
|
||||
|
||||
struct tb_tree_stats {
|
||||
size_t nb_tbs;
|
||||
size_t host_size;
|
||||
size_t target_size;
|
||||
size_t max_target_size;
|
||||
size_t direct_jmp_count;
|
||||
size_t direct_jmp2_count;
|
||||
size_t cross_page;
|
||||
};
|
||||
|
||||
static gboolean tb_tree_stats_iter(gpointer key, gpointer value, gpointer data)
|
||||
{
|
||||
const TranslationBlock *tb = value;
|
||||
struct tb_tree_stats *tst = data;
|
||||
|
||||
tst->nb_tbs++;
|
||||
tst->host_size += tb->tc.size;
|
||||
tst->target_size += tb->size;
|
||||
if (tb->size > tst->max_target_size) {
|
||||
tst->max_target_size = tb->size;
|
||||
}
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (tb->page_addr[1] != -1) {
|
||||
tst->cross_page++;
|
||||
}
|
||||
#endif
|
||||
if (tb->jmp_reset_offset[0] != TB_JMP_OFFSET_INVALID) {
|
||||
tst->direct_jmp_count++;
|
||||
if (tb->jmp_reset_offset[1] != TB_JMP_OFFSET_INVALID) {
|
||||
tst->direct_jmp2_count++;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide)
|
||||
{
|
||||
CPUState *cpu;
|
||||
size_t full = 0, part = 0, elide = 0;
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
full += qatomic_read(&cpu->neg.tlb.c.full_flush_count);
|
||||
part += qatomic_read(&cpu->neg.tlb.c.part_flush_count);
|
||||
elide += qatomic_read(&cpu->neg.tlb.c.elide_flush_count);
|
||||
}
|
||||
*pfull = full;
|
||||
*ppart = part;
|
||||
*pelide = elide;
|
||||
}
|
||||
|
||||
static void tcg_dump_flush_info(GString *buf)
|
||||
{
|
||||
size_t flush_full, flush_part, flush_elide;
|
||||
|
||||
g_string_append_printf(buf, "TB flush count %u\n",
|
||||
qatomic_read(&tb_ctx.tb_flush_count));
|
||||
g_string_append_printf(buf, "TB invalidate count %u\n",
|
||||
qatomic_read(&tb_ctx.tb_phys_invalidate_count));
|
||||
|
||||
tlb_flush_counts(&flush_full, &flush_part, &flush_elide);
|
||||
g_string_append_printf(buf, "TLB full flushes %zu\n", flush_full);
|
||||
g_string_append_printf(buf, "TLB partial flushes %zu\n", flush_part);
|
||||
g_string_append_printf(buf, "TLB elided flushes %zu\n", flush_elide);
|
||||
}
|
||||
|
||||
static void dump_exec_info(GString *buf)
|
||||
{
|
||||
struct tb_tree_stats tst = {};
|
||||
struct qht_stats hst;
|
||||
size_t nb_tbs;
|
||||
|
||||
tcg_tb_foreach(tb_tree_stats_iter, &tst);
|
||||
nb_tbs = tst.nb_tbs;
|
||||
/* XXX: avoid using doubles ? */
|
||||
g_string_append_printf(buf, "Translation buffer state:\n");
|
||||
/*
|
||||
* Report total code size including the padding and TB structs;
|
||||
* otherwise users might think "-accel tcg,tb-size" is not honoured.
|
||||
* For avg host size we use the precise numbers from tb_tree_stats though.
|
||||
*/
|
||||
g_string_append_printf(buf, "gen code size %zu/%zu\n",
|
||||
tcg_code_size(), tcg_code_capacity());
|
||||
g_string_append_printf(buf, "TB count %zu\n", nb_tbs);
|
||||
g_string_append_printf(buf, "TB avg target size %zu max=%zu bytes\n",
|
||||
nb_tbs ? tst.target_size / nb_tbs : 0,
|
||||
tst.max_target_size);
|
||||
g_string_append_printf(buf, "TB avg host size %zu bytes "
|
||||
"(expansion ratio: %0.1f)\n",
|
||||
nb_tbs ? tst.host_size / nb_tbs : 0,
|
||||
tst.target_size ?
|
||||
(double)tst.host_size / tst.target_size : 0);
|
||||
g_string_append_printf(buf, "cross page TB count %zu (%zu%%)\n",
|
||||
tst.cross_page,
|
||||
nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0);
|
||||
g_string_append_printf(buf, "direct jump count %zu (%zu%%) "
|
||||
"(2 jumps=%zu %zu%%)\n",
|
||||
tst.direct_jmp_count,
|
||||
nb_tbs ? (tst.direct_jmp_count * 100) / nb_tbs : 0,
|
||||
tst.direct_jmp2_count,
|
||||
nb_tbs ? (tst.direct_jmp2_count * 100) / nb_tbs : 0);
|
||||
|
||||
qht_statistics_init(&tb_ctx.htable, &hst);
|
||||
print_qht_statistics(hst, buf);
|
||||
qht_statistics_destroy(&hst);
|
||||
|
||||
g_string_append_printf(buf, "\nStatistics:\n");
|
||||
tcg_dump_flush_info(buf);
|
||||
}
|
||||
|
||||
void tcg_get_stats(AccelState *accel, GString *buf)
|
||||
{
|
||||
dump_accel_info(accel, buf);
|
||||
dump_exec_info(buf);
|
||||
dump_drift_info(buf);
|
||||
}
|
||||
|
||||
void tcg_dump_stats(GString *buf)
|
||||
{
|
||||
tcg_get_stats(current_accel(), buf);
|
||||
}
|
||||
13
accel/tcg/tlb-bounds.h
Normal file
13
accel/tcg/tlb-bounds.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* softmmu size bounds
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#ifndef ACCEL_TCG_TLB_BOUNDS_H
|
||||
#define ACCEL_TCG_TLB_BOUNDS_H
|
||||
|
||||
#define CPU_TLB_DYN_MIN_BITS 6
|
||||
#define CPU_TLB_DYN_MAX_BITS (32 - TARGET_PAGE_BITS)
|
||||
#define CPU_TLB_DYN_DEFAULT_BITS 8
|
||||
|
||||
#endif /* ACCEL_TCG_TLB_BOUNDS_H */
|
||||
|
|
@ -21,49 +21,20 @@
|
|||
|
||||
#include "trace.h"
|
||||
#include "disas/disas.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "tcg/tcg.h"
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
#include "qemu.h"
|
||||
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
#include <sys/param.h>
|
||||
#if __FreeBSD_version >= 700104
|
||||
#define HAVE_KINFO_GETVMMAP
|
||||
#define sigqueue sigqueue_freebsd /* avoid redefinition */
|
||||
#include <sys/proc.h>
|
||||
#include <machine/profile.h>
|
||||
#define _KERNEL
|
||||
#include <sys/user.h>
|
||||
#undef _KERNEL
|
||||
#undef sigqueue
|
||||
#include <libutil.h>
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
#include "exec/ram_addr.h"
|
||||
#endif
|
||||
|
||||
#include "exec/cputlb.h"
|
||||
#include "exec/page-protection.h"
|
||||
#include "exec/mmap-lock.h"
|
||||
#include "tb-internal.h"
|
||||
#include "exec/translator.h"
|
||||
#include "exec/tb-flush.h"
|
||||
#include "qemu/bitmap.h"
|
||||
#include "qemu/qemu-print.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/cacheinfo.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/target-info.h"
|
||||
#include "exec/log.h"
|
||||
#include "system/cpu-timers.h"
|
||||
#include "system/tcg.h"
|
||||
#include "qapi/error.h"
|
||||
#include "exec/icount.h"
|
||||
#include "accel/tcg/cpu-ops.h"
|
||||
#include "tb-jmp-cache.h"
|
||||
#include "tb-hash.h"
|
||||
#include "tb-context.h"
|
||||
#include "tb-internal.h"
|
||||
#include "internal-common.h"
|
||||
#include "internal-target.h"
|
||||
#include "tcg/perf.h"
|
||||
#include "tcg/insn-start-words.h"
|
||||
|
||||
|
|
@ -117,7 +88,7 @@ static int64_t decode_sleb128(const uint8_t **pp)
|
|||
/* Encode the data collected about the instructions while compiling TB.
|
||||
Place the data at BLOCK, and return the number of bytes consumed.
|
||||
|
||||
The logical table consists of TARGET_INSN_START_WORDS target_ulong's,
|
||||
The logical table consists of INSN_START_WORDS uint64_t's,
|
||||
which come from the target's insn_start data, followed by a uintptr_t
|
||||
which comes from the host pc of the end of the code implementing the insn.
|
||||
|
||||
|
|
@ -137,13 +108,13 @@ static int encode_search(TranslationBlock *tb, uint8_t *block)
|
|||
for (i = 0, n = tb->icount; i < n; ++i) {
|
||||
uint64_t prev, curr;
|
||||
|
||||
for (j = 0; j < TARGET_INSN_START_WORDS; ++j) {
|
||||
for (j = 0; j < INSN_START_WORDS; ++j) {
|
||||
if (i == 0) {
|
||||
prev = (!(tb_cflags(tb) & CF_PCREL) && j == 0 ? tb->pc : 0);
|
||||
} else {
|
||||
prev = insn_data[(i - 1) * TARGET_INSN_START_WORDS + j];
|
||||
prev = insn_data[(i - 1) * INSN_START_WORDS + j];
|
||||
}
|
||||
curr = insn_data[i * TARGET_INSN_START_WORDS + j];
|
||||
curr = insn_data[i * INSN_START_WORDS + j];
|
||||
p = encode_sleb128(p, curr - prev);
|
||||
}
|
||||
prev = (i == 0 ? 0 : insn_end_off[i - 1]);
|
||||
|
|
@ -175,7 +146,7 @@ static int cpu_unwind_data_from_tb(TranslationBlock *tb, uintptr_t host_pc,
|
|||
return -1;
|
||||
}
|
||||
|
||||
memset(data, 0, sizeof(uint64_t) * TARGET_INSN_START_WORDS);
|
||||
memset(data, 0, sizeof(uint64_t) * INSN_START_WORDS);
|
||||
if (!(tb_cflags(tb) & CF_PCREL)) {
|
||||
data[0] = tb->pc;
|
||||
}
|
||||
|
|
@ -185,7 +156,7 @@ static int cpu_unwind_data_from_tb(TranslationBlock *tb, uintptr_t host_pc,
|
|||
* at which the end of the insn exceeds host_pc.
|
||||
*/
|
||||
for (i = 0; i < num_insns; ++i) {
|
||||
for (j = 0; j < TARGET_INSN_START_WORDS; ++j) {
|
||||
for (j = 0; j < INSN_START_WORDS; ++j) {
|
||||
data[j] += decode_sleb128(&p);
|
||||
}
|
||||
iter_pc += decode_sleb128(&p);
|
||||
|
|
@ -203,7 +174,7 @@ static int cpu_unwind_data_from_tb(TranslationBlock *tb, uintptr_t host_pc,
|
|||
void cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb,
|
||||
uintptr_t host_pc)
|
||||
{
|
||||
uint64_t data[TARGET_INSN_START_WORDS];
|
||||
uint64_t data[INSN_START_WORDS];
|
||||
int insns_left = cpu_unwind_data_from_tb(tb, host_pc, data);
|
||||
|
||||
if (insns_left < 0) {
|
||||
|
|
@ -287,9 +258,7 @@ static int setjmp_gen_code(CPUArchState *env, TranslationBlock *tb,
|
|||
}
|
||||
|
||||
/* Called with mmap_lock held for user mode emulation. */
|
||||
TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||
vaddr pc, uint64_t cs_base,
|
||||
uint32_t flags, int cflags)
|
||||
TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s)
|
||||
{
|
||||
CPUArchState *env = cpu_env(cpu);
|
||||
TranslationBlock *tb, *existing_tb;
|
||||
|
|
@ -302,14 +271,14 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
|||
assert_memory_lock();
|
||||
qemu_thread_jit_write();
|
||||
|
||||
phys_pc = get_page_addr_code_hostp(env, pc, &host_pc);
|
||||
phys_pc = get_page_addr_code_hostp(env, s.pc, &host_pc);
|
||||
|
||||
if (phys_pc == -1) {
|
||||
/* Generate a one-shot TB with 1 insn in it */
|
||||
cflags = (cflags & ~CF_COUNT_MASK) | 1;
|
||||
s.cflags = (s.cflags & ~CF_COUNT_MASK) | 1;
|
||||
}
|
||||
|
||||
max_insns = cflags & CF_COUNT_MASK;
|
||||
max_insns = s.cflags & CF_COUNT_MASK;
|
||||
if (max_insns == 0) {
|
||||
max_insns = TCG_MAX_INSNS;
|
||||
}
|
||||
|
|
@ -329,12 +298,12 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
|||
|
||||
gen_code_buf = tcg_ctx->code_gen_ptr;
|
||||
tb->tc.ptr = tcg_splitwx_to_rx(gen_code_buf);
|
||||
if (!(cflags & CF_PCREL)) {
|
||||
tb->pc = pc;
|
||||
if (!(s.cflags & CF_PCREL)) {
|
||||
tb->pc = s.pc;
|
||||
}
|
||||
tb->cs_base = cs_base;
|
||||
tb->flags = flags;
|
||||
tb->cflags = cflags;
|
||||
tb->cs_base = s.cs_base;
|
||||
tb->flags = s.flags;
|
||||
tb->cflags = s.cflags;
|
||||
tb_set_page_addr0(tb, phys_pc);
|
||||
tb_set_page_addr1(tb, -1);
|
||||
if (phys_pc != -1) {
|
||||
|
|
@ -342,23 +311,13 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
|||
}
|
||||
|
||||
tcg_ctx->gen_tb = tb;
|
||||
tcg_ctx->addr_type = TARGET_LONG_BITS == 32 ? TCG_TYPE_I32 : TCG_TYPE_I64;
|
||||
#ifdef CONFIG_SOFTMMU
|
||||
tcg_ctx->page_bits = TARGET_PAGE_BITS;
|
||||
tcg_ctx->page_mask = TARGET_PAGE_MASK;
|
||||
tcg_ctx->tlb_dyn_max_bits = CPU_TLB_DYN_MAX_BITS;
|
||||
#endif
|
||||
tcg_ctx->insn_start_words = TARGET_INSN_START_WORDS;
|
||||
#ifdef TCG_GUEST_DEFAULT_MO
|
||||
tcg_ctx->guest_mo = TCG_GUEST_DEFAULT_MO;
|
||||
#else
|
||||
tcg_ctx->guest_mo = TCG_MO_ALL;
|
||||
#endif
|
||||
tcg_ctx->addr_type = target_long_bits() == 32 ? TCG_TYPE_I32 : TCG_TYPE_I64;
|
||||
tcg_ctx->guest_mo = cpu->cc->tcg_ops->guest_default_memory_order;
|
||||
|
||||
restart_translate:
|
||||
trace_translate_block(tb, pc, tb->tc.ptr);
|
||||
trace_translate_block(tb, s.pc, tb->tc.ptr);
|
||||
|
||||
gen_code_size = setjmp_gen_code(env, tb, pc, host_pc, &max_insns, &ti);
|
||||
gen_code_size = setjmp_gen_code(env, tb, s.pc, host_pc, &max_insns, &ti);
|
||||
if (unlikely(gen_code_size < 0)) {
|
||||
switch (gen_code_size) {
|
||||
case -1:
|
||||
|
|
@ -435,10 +394,10 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
|||
* For CF_PCREL, attribute all executions of the generated code
|
||||
* to its first mapping.
|
||||
*/
|
||||
perf_report_code(pc, tb, tcg_splitwx_to_rx(gen_code_buf));
|
||||
perf_report_code(s.pc, tb, tcg_splitwx_to_rx(gen_code_buf));
|
||||
|
||||
if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM) &&
|
||||
qemu_log_in_addr_range(pc)) {
|
||||
qemu_log_in_addr_range(s.pc)) {
|
||||
FILE *logfile = qemu_log_trylock();
|
||||
if (logfile) {
|
||||
int code_size, data_size;
|
||||
|
|
@ -460,7 +419,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
|||
fprintf(logfile, "OUT: [size=%d]\n", gen_code_size);
|
||||
fprintf(logfile,
|
||||
" -- guest addr 0x%016" PRIx64 " + tb prologue\n",
|
||||
tcg_ctx->gen_insn_data[insn * TARGET_INSN_START_WORDS]);
|
||||
tcg_ctx->gen_insn_data[insn * INSN_START_WORDS]);
|
||||
chunk_start = tcg_ctx->gen_insn_end_off[insn];
|
||||
disas(logfile, tb->tc.ptr, chunk_start);
|
||||
|
||||
|
|
@ -473,7 +432,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
|||
size_t chunk_end = tcg_ctx->gen_insn_end_off[insn];
|
||||
if (chunk_end > chunk_start) {
|
||||
fprintf(logfile, " -- guest addr 0x%016" PRIx64 "\n",
|
||||
tcg_ctx->gen_insn_data[insn * TARGET_INSN_START_WORDS]);
|
||||
tcg_ctx->gen_insn_data[insn * INSN_START_WORDS]);
|
||||
disas(logfile, tb->tc.ptr + chunk_start,
|
||||
chunk_end - chunk_start);
|
||||
chunk_start = chunk_end;
|
||||
|
|
@ -591,15 +550,11 @@ void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr)
|
|||
/* The exception probably happened in a helper. The CPU state should
|
||||
have been saved before calling it. Fetch the PC from there. */
|
||||
CPUArchState *env = cpu_env(cpu);
|
||||
vaddr pc;
|
||||
uint64_t cs_base;
|
||||
tb_page_addr_t addr;
|
||||
uint32_t flags;
|
||||
TCGTBCPUState s = cpu->cc->tcg_ops->get_tb_cpu_state(cpu);
|
||||
tb_page_addr_t addr = get_page_addr_code(env, s.pc);
|
||||
|
||||
cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
|
||||
addr = get_page_addr_code(env, pc);
|
||||
if (addr != -1) {
|
||||
tb_invalidate_phys_range(addr, addr);
|
||||
tb_invalidate_phys_range(cpu, addr, addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,16 +8,16 @@
|
|||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "accel/tcg/cpu-ldst-common.h"
|
||||
#include "accel/tcg/cpu-mmu-index.h"
|
||||
#include "exec/target_page.h"
|
||||
#include "exec/translator.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "exec/plugin-gen.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "exec/tswap.h"
|
||||
#include "tcg/tcg-op-common.h"
|
||||
#include "internal-target.h"
|
||||
#include "internal-common.h"
|
||||
#include "disas/disas.h"
|
||||
#include "tb-internal.h"
|
||||
|
||||
|
|
@ -25,8 +25,7 @@ static void set_can_do_io(DisasContextBase *db, bool val)
|
|||
{
|
||||
QEMU_BUILD_BUG_ON(sizeof_field(CPUState, neg.can_do_io) != 1);
|
||||
tcg_gen_st8_i32(tcg_constant_i32(val), tcg_env,
|
||||
offsetof(ArchCPU, parent_obj.neg.can_do_io) -
|
||||
offsetof(ArchCPU, env));
|
||||
offsetof(CPUState, neg.can_do_io) - sizeof(CPUState));
|
||||
}
|
||||
|
||||
bool translator_io_start(DisasContextBase *db)
|
||||
|
|
@ -49,8 +48,8 @@ static TCGOp *gen_tb_start(DisasContextBase *db, uint32_t cflags)
|
|||
if ((cflags & CF_USE_ICOUNT) || !(cflags & CF_NOIRQ)) {
|
||||
count = tcg_temp_new_i32();
|
||||
tcg_gen_ld_i32(count, tcg_env,
|
||||
offsetof(ArchCPU, parent_obj.neg.icount_decr.u32)
|
||||
- offsetof(ArchCPU, env));
|
||||
offsetof(CPUState, neg.icount_decr.u32) -
|
||||
sizeof(CPUState));
|
||||
}
|
||||
|
||||
if (cflags & CF_USE_ICOUNT) {
|
||||
|
|
@ -79,8 +78,8 @@ static TCGOp *gen_tb_start(DisasContextBase *db, uint32_t cflags)
|
|||
|
||||
if (cflags & CF_USE_ICOUNT) {
|
||||
tcg_gen_st16_i32(count, tcg_env,
|
||||
offsetof(ArchCPU, parent_obj.neg.icount_decr.u16.low)
|
||||
- offsetof(ArchCPU, env));
|
||||
offsetof(CPUState, neg.icount_decr.u16.low) -
|
||||
sizeof(CPUState));
|
||||
}
|
||||
|
||||
return icount_start_insn;
|
||||
|
|
@ -142,6 +141,7 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
|
|||
db->host_addr[1] = NULL;
|
||||
db->record_start = 0;
|
||||
db->record_len = 0;
|
||||
db->code_mmuidx = cpu_mmu_index(cpu, true);
|
||||
|
||||
ops->init_disas_context(db, cpu);
|
||||
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
|
||||
|
|
@ -265,12 +265,14 @@ static bool translator_ld(CPUArchState *env, DisasContextBase *db,
|
|||
|
||||
if (likely(((base ^ last) & TARGET_PAGE_MASK) == 0)) {
|
||||
/* Entire read is from the first page. */
|
||||
memcpy(dest, host + (pc - base), len);
|
||||
return true;
|
||||
goto do_read;
|
||||
}
|
||||
|
||||
if (unlikely(((base ^ pc) & TARGET_PAGE_MASK) == 0)) {
|
||||
/* Read begins on the first page and extends to the second. */
|
||||
/*
|
||||
* Read begins on the first page and extends to the second.
|
||||
* The unaligned read is never atomic.
|
||||
*/
|
||||
size_t len0 = -(pc | TARGET_PAGE_MASK);
|
||||
memcpy(dest, host + (pc - base), len0);
|
||||
pc += len0;
|
||||
|
|
@ -329,7 +331,39 @@ static bool translator_ld(CPUArchState *env, DisasContextBase *db,
|
|||
host = db->host_addr[1];
|
||||
}
|
||||
|
||||
memcpy(dest, host + (pc - base), len);
|
||||
do_read:
|
||||
/*
|
||||
* Assume aligned reads should be atomic, if possible.
|
||||
* We're not in a position to jump out with EXCP_ATOMIC.
|
||||
*/
|
||||
host += pc - base;
|
||||
switch (len) {
|
||||
case 2:
|
||||
if (QEMU_IS_ALIGNED(pc, 2)) {
|
||||
uint16_t t = qatomic_read((uint16_t *)host);
|
||||
stw_he_p(dest, t);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (QEMU_IS_ALIGNED(pc, 4)) {
|
||||
uint32_t t = qatomic_read((uint32_t *)host);
|
||||
stl_he_p(dest, t);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
#ifdef CONFIG_ATOMIC64
|
||||
case 8:
|
||||
if (QEMU_IS_ALIGNED(pc, 8)) {
|
||||
uint64_t t = qatomic_read__nocheck((uint64_t *)host);
|
||||
stq_he_p(dest, t);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
/* Unaligned or partial read from the second page is not atomic. */
|
||||
memcpy(dest, host, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -423,55 +457,62 @@ bool translator_st(const DisasContextBase *db, void *dest,
|
|||
|
||||
uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, vaddr pc)
|
||||
{
|
||||
uint8_t raw;
|
||||
uint8_t val;
|
||||
|
||||
if (!translator_ld(env, db, &raw, pc, sizeof(raw))) {
|
||||
raw = cpu_ldub_code(env, pc);
|
||||
record_save(db, pc, &raw, sizeof(raw));
|
||||
if (!translator_ld(env, db, &val, pc, sizeof(val))) {
|
||||
MemOpIdx oi = make_memop_idx(MO_UB, db->code_mmuidx);
|
||||
val = cpu_ldb_code_mmu(env, pc, oi, 0);
|
||||
record_save(db, pc, &val, sizeof(val));
|
||||
}
|
||||
return raw;
|
||||
return val;
|
||||
}
|
||||
|
||||
uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, vaddr pc)
|
||||
uint16_t translator_lduw_end(CPUArchState *env, DisasContextBase *db,
|
||||
vaddr pc, MemOp endian)
|
||||
{
|
||||
uint16_t raw, tgt;
|
||||
uint16_t val;
|
||||
|
||||
if (translator_ld(env, db, &raw, pc, sizeof(raw))) {
|
||||
tgt = tswap16(raw);
|
||||
} else {
|
||||
tgt = cpu_lduw_code(env, pc);
|
||||
raw = tswap16(tgt);
|
||||
record_save(db, pc, &raw, sizeof(raw));
|
||||
if (!translator_ld(env, db, &val, pc, sizeof(val))) {
|
||||
MemOpIdx oi = make_memop_idx(MO_UW, db->code_mmuidx);
|
||||
val = cpu_ldw_code_mmu(env, pc, oi, 0);
|
||||
record_save(db, pc, &val, sizeof(val));
|
||||
}
|
||||
return tgt;
|
||||
if (endian & MO_BSWAP) {
|
||||
val = bswap16(val);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, vaddr pc)
|
||||
uint32_t translator_ldl_end(CPUArchState *env, DisasContextBase *db,
|
||||
vaddr pc, MemOp endian)
|
||||
{
|
||||
uint32_t raw, tgt;
|
||||
uint32_t val;
|
||||
|
||||
if (translator_ld(env, db, &raw, pc, sizeof(raw))) {
|
||||
tgt = tswap32(raw);
|
||||
} else {
|
||||
tgt = cpu_ldl_code(env, pc);
|
||||
raw = tswap32(tgt);
|
||||
record_save(db, pc, &raw, sizeof(raw));
|
||||
if (!translator_ld(env, db, &val, pc, sizeof(val))) {
|
||||
MemOpIdx oi = make_memop_idx(MO_UL, db->code_mmuidx);
|
||||
val = cpu_ldl_code_mmu(env, pc, oi, 0);
|
||||
record_save(db, pc, &val, sizeof(val));
|
||||
}
|
||||
return tgt;
|
||||
if (endian & MO_BSWAP) {
|
||||
val = bswap32(val);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, vaddr pc)
|
||||
uint64_t translator_ldq_end(CPUArchState *env, DisasContextBase *db,
|
||||
vaddr pc, MemOp endian)
|
||||
{
|
||||
uint64_t raw, tgt;
|
||||
uint64_t val;
|
||||
|
||||
if (translator_ld(env, db, &raw, pc, sizeof(raw))) {
|
||||
tgt = tswap64(raw);
|
||||
} else {
|
||||
tgt = cpu_ldq_code(env, pc);
|
||||
raw = tswap64(tgt);
|
||||
record_save(db, pc, &raw, sizeof(raw));
|
||||
if (!translator_ld(env, db, &val, pc, sizeof(val))) {
|
||||
MemOpIdx oi = make_memop_idx(MO_UQ, db->code_mmuidx);
|
||||
val = cpu_ldq_code_mmu(env, pc, oi, 0);
|
||||
record_save(db, pc, &val, sizeof(val));
|
||||
}
|
||||
return tgt;
|
||||
if (endian & MO_BSWAP) {
|
||||
val = bswap64(val);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
void translator_fake_ld(DisasContextBase *db, const void *data, size_t len)
|
||||
|
|
|
|||
|
|
@ -20,23 +20,26 @@
|
|||
#include "accel/tcg/cpu-ops.h"
|
||||
#include "disas/disas.h"
|
||||
#include "exec/vaddr.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/tlb-flags.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "qemu/rcu.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "accel/tcg/cpu-ldst-common.h"
|
||||
#include "accel/tcg/helper-retaddr.h"
|
||||
#include "accel/tcg/probe.h"
|
||||
#include "user/cpu_loop.h"
|
||||
#include "user/guest-host.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "user/page-protection.h"
|
||||
#include "exec/page-protection.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "exec/helper-proto-common.h"
|
||||
#include "qemu/atomic128.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "qemu/int128.h"
|
||||
#include "trace.h"
|
||||
#include "tcg/tcg-ldst.h"
|
||||
#include "backend-ldst.h"
|
||||
#include "internal-common.h"
|
||||
#include "internal-target.h"
|
||||
#include "tb-internal.h"
|
||||
|
||||
__thread uintptr_t helper_retaddr;
|
||||
|
|
@ -123,9 +126,9 @@ MMUAccessType adjust_signal_pc(uintptr_t *pc, bool is_write)
|
|||
* guest, we'd end up in an infinite loop of retrying the faulting access.
|
||||
*/
|
||||
bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set,
|
||||
uintptr_t host_pc, abi_ptr guest_addr)
|
||||
uintptr_t host_pc, vaddr guest_addr)
|
||||
{
|
||||
switch (page_unprotect(guest_addr, host_pc)) {
|
||||
switch (page_unprotect(cpu, guest_addr, host_pc)) {
|
||||
case 0:
|
||||
/*
|
||||
* Fault not caused by a page marked unwritable to protect
|
||||
|
|
@ -159,7 +162,7 @@ typedef struct PageFlagsNode {
|
|||
|
||||
static IntervalTreeRoot pageflags_root;
|
||||
|
||||
static PageFlagsNode *pageflags_find(target_ulong start, target_ulong last)
|
||||
static PageFlagsNode *pageflags_find(vaddr start, vaddr last)
|
||||
{
|
||||
IntervalTreeNode *n;
|
||||
|
||||
|
|
@ -167,8 +170,7 @@ static PageFlagsNode *pageflags_find(target_ulong start, target_ulong last)
|
|||
return n ? container_of(n, PageFlagsNode, itree) : NULL;
|
||||
}
|
||||
|
||||
static PageFlagsNode *pageflags_next(PageFlagsNode *p, target_ulong start,
|
||||
target_ulong last)
|
||||
static PageFlagsNode *pageflags_next(PageFlagsNode *p, vaddr start, vaddr last)
|
||||
{
|
||||
IntervalTreeNode *n;
|
||||
|
||||
|
|
@ -197,13 +199,22 @@ int walk_memory_regions(void *priv, walk_memory_regions_fn fn)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int dump_region(void *priv, target_ulong start,
|
||||
target_ulong end, unsigned long prot)
|
||||
static int dump_region(void *opaque, vaddr start, vaddr end, int prot)
|
||||
{
|
||||
FILE *f = (FILE *)priv;
|
||||
FILE *f = opaque;
|
||||
uint64_t mask;
|
||||
int width;
|
||||
|
||||
fprintf(f, TARGET_FMT_lx"-"TARGET_FMT_lx" "TARGET_FMT_lx" %c%c%c\n",
|
||||
start, end, end - start,
|
||||
if (guest_addr_max <= UINT32_MAX) {
|
||||
mask = UINT32_MAX, width = 8;
|
||||
} else {
|
||||
mask = UINT64_MAX, width = 16;
|
||||
}
|
||||
|
||||
fprintf(f, "%0*" PRIx64 "-%0*" PRIx64 " %0*" PRIx64 " %c%c%c\n",
|
||||
width, start & mask,
|
||||
width, end & mask,
|
||||
width, (end - start) & mask,
|
||||
((prot & PAGE_READ) ? 'r' : '-'),
|
||||
((prot & PAGE_WRITE) ? 'w' : '-'),
|
||||
((prot & PAGE_EXEC) ? 'x' : '-'));
|
||||
|
|
@ -213,14 +224,14 @@ static int dump_region(void *priv, target_ulong start,
|
|||
/* dump memory mappings */
|
||||
void page_dump(FILE *f)
|
||||
{
|
||||
const int length = sizeof(target_ulong) * 2;
|
||||
int width = guest_addr_max <= UINT32_MAX ? 8 : 16;
|
||||
|
||||
fprintf(f, "%-*s %-*s %-*s %s\n",
|
||||
length, "start", length, "end", length, "size", "prot");
|
||||
width, "start", width, "end", width, "size", "prot");
|
||||
walk_memory_regions(f, dump_region);
|
||||
}
|
||||
|
||||
int page_get_flags(target_ulong address)
|
||||
int page_get_flags(vaddr address)
|
||||
{
|
||||
PageFlagsNode *p = pageflags_find(address, address);
|
||||
|
||||
|
|
@ -243,7 +254,7 @@ int page_get_flags(target_ulong address)
|
|||
}
|
||||
|
||||
/* A subroutine of page_set_flags: insert a new node for [start,last]. */
|
||||
static void pageflags_create(target_ulong start, target_ulong last, int flags)
|
||||
static void pageflags_create(vaddr start, vaddr last, int flags)
|
||||
{
|
||||
PageFlagsNode *p = g_new(PageFlagsNode, 1);
|
||||
|
||||
|
|
@ -254,13 +265,13 @@ static void pageflags_create(target_ulong start, target_ulong last, int flags)
|
|||
}
|
||||
|
||||
/* A subroutine of page_set_flags: remove everything in [start,last]. */
|
||||
static bool pageflags_unset(target_ulong start, target_ulong last)
|
||||
static bool pageflags_unset(vaddr start, vaddr last)
|
||||
{
|
||||
bool inval_tb = false;
|
||||
|
||||
while (true) {
|
||||
PageFlagsNode *p = pageflags_find(start, last);
|
||||
target_ulong p_last;
|
||||
vaddr p_last;
|
||||
|
||||
if (!p) {
|
||||
break;
|
||||
|
|
@ -299,8 +310,7 @@ static bool pageflags_unset(target_ulong start, target_ulong last)
|
|||
* A subroutine of page_set_flags: nothing overlaps [start,last],
|
||||
* but check adjacent mappings and maybe merge into a single range.
|
||||
*/
|
||||
static void pageflags_create_merge(target_ulong start, target_ulong last,
|
||||
int flags)
|
||||
static void pageflags_create_merge(vaddr start, vaddr last, int flags)
|
||||
{
|
||||
PageFlagsNode *next = NULL, *prev = NULL;
|
||||
|
||||
|
|
@ -351,11 +361,11 @@ static void pageflags_create_merge(target_ulong start, target_ulong last,
|
|||
#define PAGE_STICKY (PAGE_ANON | PAGE_PASSTHROUGH | PAGE_TARGET_STICKY)
|
||||
|
||||
/* A subroutine of page_set_flags: add flags to [start,last]. */
|
||||
static bool pageflags_set_clear(target_ulong start, target_ulong last,
|
||||
static bool pageflags_set_clear(vaddr start, vaddr last,
|
||||
int set_flags, int clear_flags)
|
||||
{
|
||||
PageFlagsNode *p;
|
||||
target_ulong p_start, p_last;
|
||||
vaddr p_start, p_last;
|
||||
int p_flags, merge_flags;
|
||||
bool inval_tb = false;
|
||||
|
||||
|
|
@ -490,7 +500,7 @@ static bool pageflags_set_clear(target_ulong start, target_ulong last,
|
|||
return inval_tb;
|
||||
}
|
||||
|
||||
void page_set_flags(target_ulong start, target_ulong last, int flags)
|
||||
void page_set_flags(vaddr start, vaddr last, int flags)
|
||||
{
|
||||
bool reset = false;
|
||||
bool inval_tb = false;
|
||||
|
|
@ -499,7 +509,7 @@ void page_set_flags(target_ulong start, target_ulong last, int flags)
|
|||
guest address space. If this assert fires, it probably indicates
|
||||
a missing call to h2g_valid. */
|
||||
assert(start <= last);
|
||||
assert(last <= GUEST_ADDR_MAX);
|
||||
assert(last <= guest_addr_max);
|
||||
/* Only set PAGE_ANON with new mappings. */
|
||||
assert(!(flags & PAGE_ANON) || (flags & PAGE_RESET));
|
||||
assert_memory_lock();
|
||||
|
|
@ -526,13 +536,13 @@ void page_set_flags(target_ulong start, target_ulong last, int flags)
|
|||
~(reset ? 0 : PAGE_STICKY));
|
||||
}
|
||||
if (inval_tb) {
|
||||
tb_invalidate_phys_range(start, last);
|
||||
tb_invalidate_phys_range(NULL, start, last);
|
||||
}
|
||||
}
|
||||
|
||||
bool page_check_range(target_ulong start, target_ulong len, int flags)
|
||||
bool page_check_range(vaddr start, vaddr len, int flags)
|
||||
{
|
||||
target_ulong last;
|
||||
vaddr last;
|
||||
int locked; /* tri-state: =0: unlocked, +1: global, -1: local */
|
||||
bool ret;
|
||||
|
||||
|
|
@ -581,7 +591,7 @@ bool page_check_range(target_ulong start, target_ulong len, int flags)
|
|||
break;
|
||||
}
|
||||
/* Asking about writable, but has been protected: undo. */
|
||||
if (!page_unprotect(start, 0)) {
|
||||
if (!page_unprotect(NULL, start, 0)) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
|
|
@ -608,20 +618,19 @@ bool page_check_range(target_ulong start, target_ulong len, int flags)
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool page_check_range_empty(target_ulong start, target_ulong last)
|
||||
bool page_check_range_empty(vaddr start, vaddr last)
|
||||
{
|
||||
assert(last >= start);
|
||||
assert_memory_lock();
|
||||
return pageflags_find(start, last) == NULL;
|
||||
}
|
||||
|
||||
target_ulong page_find_range_empty(target_ulong min, target_ulong max,
|
||||
target_ulong len, target_ulong align)
|
||||
vaddr page_find_range_empty(vaddr min, vaddr max, vaddr len, vaddr align)
|
||||
{
|
||||
target_ulong len_m1, align_m1;
|
||||
vaddr len_m1, align_m1;
|
||||
|
||||
assert(min <= max);
|
||||
assert(max <= GUEST_ADDR_MAX);
|
||||
assert(max <= guest_addr_max);
|
||||
assert(len != 0);
|
||||
assert(is_power_of_2(align));
|
||||
assert_memory_lock();
|
||||
|
|
@ -656,10 +665,10 @@ target_ulong page_find_range_empty(target_ulong min, target_ulong max,
|
|||
}
|
||||
}
|
||||
|
||||
void page_protect(tb_page_addr_t address)
|
||||
void tb_lock_page0(tb_page_addr_t address)
|
||||
{
|
||||
PageFlagsNode *p;
|
||||
target_ulong start, last;
|
||||
vaddr start, last;
|
||||
int host_page_size = qemu_real_host_page_size();
|
||||
int prot;
|
||||
|
||||
|
|
@ -701,11 +710,13 @@ void page_protect(tb_page_addr_t address)
|
|||
* immediately exited. (We can only return 2 if the 'pc' argument is
|
||||
* non-zero.)
|
||||
*/
|
||||
int page_unprotect(tb_page_addr_t address, uintptr_t pc)
|
||||
int page_unprotect(CPUState *cpu, tb_page_addr_t address, uintptr_t pc)
|
||||
{
|
||||
PageFlagsNode *p;
|
||||
bool current_tb_invalidated;
|
||||
|
||||
assert((cpu == NULL) == (pc == 0));
|
||||
|
||||
/*
|
||||
* Technically this isn't safe inside a signal handler. However we
|
||||
* know this only ever happens in a synchronous SEGV handler, so in
|
||||
|
|
@ -728,15 +739,15 @@ int page_unprotect(tb_page_addr_t address, uintptr_t pc)
|
|||
* this thread raced with another one which got here first and
|
||||
* set the page to PAGE_WRITE and did the TB invalidate for us.
|
||||
*/
|
||||
#ifdef TARGET_HAS_PRECISE_SMC
|
||||
TranslationBlock *current_tb = tcg_tb_lookup(pc);
|
||||
if (current_tb) {
|
||||
current_tb_invalidated = tb_cflags(current_tb) & CF_INVALID;
|
||||
if (pc && cpu->cc->tcg_ops->precise_smc) {
|
||||
TranslationBlock *current_tb = tcg_tb_lookup(pc);
|
||||
if (current_tb) {
|
||||
current_tb_invalidated = tb_cflags(current_tb) & CF_INVALID;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
int host_page_size = qemu_real_host_page_size();
|
||||
target_ulong start, len, i;
|
||||
vaddr start, len, i;
|
||||
int prot;
|
||||
|
||||
if (host_page_size <= TARGET_PAGE_SIZE) {
|
||||
|
|
@ -744,14 +755,15 @@ int page_unprotect(tb_page_addr_t address, uintptr_t pc)
|
|||
len = TARGET_PAGE_SIZE;
|
||||
prot = p->flags | PAGE_WRITE;
|
||||
pageflags_set_clear(start, start + len - 1, PAGE_WRITE, 0);
|
||||
current_tb_invalidated = tb_invalidate_phys_page_unwind(start, pc);
|
||||
current_tb_invalidated =
|
||||
tb_invalidate_phys_page_unwind(cpu, start, pc);
|
||||
} else {
|
||||
start = address & -host_page_size;
|
||||
len = host_page_size;
|
||||
prot = 0;
|
||||
|
||||
for (i = 0; i < len; i += TARGET_PAGE_SIZE) {
|
||||
target_ulong addr = start + i;
|
||||
vaddr addr = start + i;
|
||||
|
||||
p = pageflags_find(addr, addr);
|
||||
if (p) {
|
||||
|
|
@ -767,7 +779,7 @@ int page_unprotect(tb_page_addr_t address, uintptr_t pc)
|
|||
* the corresponding translated code.
|
||||
*/
|
||||
current_tb_invalidated |=
|
||||
tb_invalidate_phys_page_unwind(addr, pc);
|
||||
tb_invalidate_phys_page_unwind(cpu, addr, pc);
|
||||
}
|
||||
}
|
||||
if (prot & PAGE_EXEC) {
|
||||
|
|
@ -847,6 +859,12 @@ void *probe_access(CPUArchState *env, vaddr addr, int size,
|
|||
return size ? g2h(env_cpu(env), addr) : NULL;
|
||||
}
|
||||
|
||||
void *tlb_vaddr_to_host(CPUArchState *env, vaddr addr,
|
||||
MMUAccessType access_type, int mmu_idx)
|
||||
{
|
||||
return g2h(env_cpu(env), addr);
|
||||
}
|
||||
|
||||
tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr,
|
||||
void **hostp)
|
||||
{
|
||||
|
|
@ -861,7 +879,6 @@ tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr,
|
|||
return addr;
|
||||
}
|
||||
|
||||
#ifdef TARGET_PAGE_DATA_SIZE
|
||||
/*
|
||||
* Allocate chunks of target data together. For the only current user,
|
||||
* if we allocate one hunk per page, we have overhead of 40/128 or 40%.
|
||||
|
|
@ -877,10 +894,16 @@ typedef struct TargetPageDataNode {
|
|||
} TargetPageDataNode;
|
||||
|
||||
static IntervalTreeRoot targetdata_root;
|
||||
static size_t target_page_data_size;
|
||||
|
||||
void page_reset_target_data(target_ulong start, target_ulong last)
|
||||
void page_reset_target_data(vaddr start, vaddr last)
|
||||
{
|
||||
IntervalTreeNode *n, *next;
|
||||
size_t size = target_page_data_size;
|
||||
|
||||
if (likely(size == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert_memory_lock();
|
||||
|
||||
|
|
@ -892,7 +915,7 @@ void page_reset_target_data(target_ulong start, target_ulong last)
|
|||
n != NULL;
|
||||
n = next,
|
||||
next = next ? interval_tree_iter_next(n, start, last) : NULL) {
|
||||
target_ulong n_start, n_last, p_ofs, p_len;
|
||||
vaddr n_start, n_last, p_ofs, p_len;
|
||||
TargetPageDataNode *t = container_of(n, TargetPageDataNode, itree);
|
||||
|
||||
if (n->start >= start && n->last <= last) {
|
||||
|
|
@ -911,16 +934,21 @@ void page_reset_target_data(target_ulong start, target_ulong last)
|
|||
n_last = MIN(last, n->last);
|
||||
p_len = (n_last + 1 - n_start) >> TARGET_PAGE_BITS;
|
||||
|
||||
memset(t->data + p_ofs * TARGET_PAGE_DATA_SIZE, 0,
|
||||
p_len * TARGET_PAGE_DATA_SIZE);
|
||||
memset(t->data + p_ofs * size, 0, p_len * size);
|
||||
}
|
||||
}
|
||||
|
||||
void *page_get_target_data(target_ulong address)
|
||||
void *page_get_target_data(vaddr address, size_t size)
|
||||
{
|
||||
IntervalTreeNode *n;
|
||||
TargetPageDataNode *t;
|
||||
target_ulong page, region, p_ofs;
|
||||
vaddr page, region, p_ofs;
|
||||
|
||||
/* Remember the size from the first call, and it should be constant. */
|
||||
if (unlikely(target_page_data_size != size)) {
|
||||
assert(target_page_data_size == 0);
|
||||
target_page_data_size = size;
|
||||
}
|
||||
|
||||
page = address & TARGET_PAGE_MASK;
|
||||
region = address & TBD_MASK;
|
||||
|
|
@ -936,8 +964,7 @@ void *page_get_target_data(target_ulong address)
|
|||
mmap_lock();
|
||||
n = interval_tree_iter_first(&targetdata_root, page, page);
|
||||
if (!n) {
|
||||
t = g_malloc0(sizeof(TargetPageDataNode)
|
||||
+ TPD_PAGES * TARGET_PAGE_DATA_SIZE);
|
||||
t = g_malloc0(sizeof(TargetPageDataNode) + TPD_PAGES * size);
|
||||
n = &t->itree;
|
||||
n->start = region;
|
||||
n->last = region | ~TBD_MASK;
|
||||
|
|
@ -948,11 +975,8 @@ void *page_get_target_data(target_ulong address)
|
|||
|
||||
t = container_of(n, TargetPageDataNode, itree);
|
||||
p_ofs = (page - region) >> TARGET_PAGE_BITS;
|
||||
return t->data + p_ofs * TARGET_PAGE_DATA_SIZE;
|
||||
return t->data + p_ofs * size;
|
||||
}
|
||||
#else
|
||||
void page_reset_target_data(target_ulong start, target_ulong last) { }
|
||||
#endif /* TARGET_PAGE_DATA_SIZE */
|
||||
|
||||
/* The system-mode versions of these helpers are in cputlb.c. */
|
||||
|
||||
|
|
@ -1014,7 +1038,7 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr,
|
|||
* be under mmap_lock() in order to prevent the creation of
|
||||
* another TranslationBlock in between.
|
||||
*/
|
||||
tb_invalidate_phys_range(addr, addr + l - 1);
|
||||
tb_invalidate_phys_range(NULL, addr, addr + l - 1);
|
||||
written = pwrite(fd, buf, l,
|
||||
(off_t)(uintptr_t)g2h_untagged(addr));
|
||||
if (written != l) {
|
||||
|
|
@ -1059,7 +1083,7 @@ static uint8_t do_ld1_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi,
|
|||
void *haddr;
|
||||
uint8_t ret;
|
||||
|
||||
cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
|
||||
cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD);
|
||||
haddr = cpu_mmu_lookup(cpu, addr, get_memop(oi), ra, access_type);
|
||||
ret = ldub_p(haddr);
|
||||
clear_helper_retaddr();
|
||||
|
|
@ -1073,7 +1097,7 @@ static uint16_t do_ld2_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi,
|
|||
uint16_t ret;
|
||||
MemOp mop = get_memop(oi);
|
||||
|
||||
cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
|
||||
cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD);
|
||||
haddr = cpu_mmu_lookup(cpu, addr, mop, ra, access_type);
|
||||
ret = load_atom_2(cpu, ra, haddr, mop);
|
||||
clear_helper_retaddr();
|
||||
|
|
@ -1091,7 +1115,7 @@ static uint32_t do_ld4_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi,
|
|||
uint32_t ret;
|
||||
MemOp mop = get_memop(oi);
|
||||
|
||||
cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
|
||||
cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD);
|
||||
haddr = cpu_mmu_lookup(cpu, addr, mop, ra, access_type);
|
||||
ret = load_atom_4(cpu, ra, haddr, mop);
|
||||
clear_helper_retaddr();
|
||||
|
|
@ -1109,7 +1133,7 @@ static uint64_t do_ld8_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi,
|
|||
uint64_t ret;
|
||||
MemOp mop = get_memop(oi);
|
||||
|
||||
cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
|
||||
cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD);
|
||||
haddr = cpu_mmu_lookup(cpu, addr, mop, ra, access_type);
|
||||
ret = load_atom_8(cpu, ra, haddr, mop);
|
||||
clear_helper_retaddr();
|
||||
|
|
@ -1120,7 +1144,7 @@ static uint64_t do_ld8_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static Int128 do_ld16_mmu(CPUState *cpu, abi_ptr addr,
|
||||
static Int128 do_ld16_mmu(CPUState *cpu, vaddr addr,
|
||||
MemOpIdx oi, uintptr_t ra)
|
||||
{
|
||||
void *haddr;
|
||||
|
|
@ -1128,7 +1152,7 @@ static Int128 do_ld16_mmu(CPUState *cpu, abi_ptr addr,
|
|||
MemOp mop = get_memop(oi);
|
||||
|
||||
tcg_debug_assert((mop & MO_SIZE) == MO_128);
|
||||
cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
|
||||
cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD);
|
||||
haddr = cpu_mmu_lookup(cpu, addr, mop, ra, MMU_DATA_LOAD);
|
||||
ret = load_atom_16(cpu, ra, haddr, mop);
|
||||
clear_helper_retaddr();
|
||||
|
|
@ -1144,7 +1168,7 @@ static void do_st1_mmu(CPUState *cpu, vaddr addr, uint8_t val,
|
|||
{
|
||||
void *haddr;
|
||||
|
||||
cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
|
||||
cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST);
|
||||
haddr = cpu_mmu_lookup(cpu, addr, get_memop(oi), ra, MMU_DATA_STORE);
|
||||
stb_p(haddr, val);
|
||||
clear_helper_retaddr();
|
||||
|
|
@ -1156,7 +1180,7 @@ static void do_st2_mmu(CPUState *cpu, vaddr addr, uint16_t val,
|
|||
void *haddr;
|
||||
MemOp mop = get_memop(oi);
|
||||
|
||||
cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
|
||||
cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST);
|
||||
haddr = cpu_mmu_lookup(cpu, addr, mop, ra, MMU_DATA_STORE);
|
||||
|
||||
if (mop & MO_BSWAP) {
|
||||
|
|
@ -1172,7 +1196,7 @@ static void do_st4_mmu(CPUState *cpu, vaddr addr, uint32_t val,
|
|||
void *haddr;
|
||||
MemOp mop = get_memop(oi);
|
||||
|
||||
cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
|
||||
cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST);
|
||||
haddr = cpu_mmu_lookup(cpu, addr, mop, ra, MMU_DATA_STORE);
|
||||
|
||||
if (mop & MO_BSWAP) {
|
||||
|
|
@ -1188,7 +1212,7 @@ static void do_st8_mmu(CPUState *cpu, vaddr addr, uint64_t val,
|
|||
void *haddr;
|
||||
MemOp mop = get_memop(oi);
|
||||
|
||||
cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
|
||||
cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST);
|
||||
haddr = cpu_mmu_lookup(cpu, addr, mop, ra, MMU_DATA_STORE);
|
||||
|
||||
if (mop & MO_BSWAP) {
|
||||
|
|
@ -1204,7 +1228,7 @@ static void do_st16_mmu(CPUState *cpu, vaddr addr, Int128 val,
|
|||
void *haddr;
|
||||
MemOpIdx mop = get_memop(oi);
|
||||
|
||||
cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
|
||||
cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST);
|
||||
haddr = cpu_mmu_lookup(cpu, addr, mop, ra, MMU_DATA_STORE);
|
||||
|
||||
if (mop & MO_BSWAP) {
|
||||
|
|
@ -1214,101 +1238,28 @@ static void do_st16_mmu(CPUState *cpu, vaddr addr, Int128 val,
|
|||
clear_helper_retaddr();
|
||||
}
|
||||
|
||||
uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
set_helper_retaddr(1);
|
||||
ret = ldub_p(g2h_untagged(ptr));
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
set_helper_retaddr(1);
|
||||
ret = lduw_p(g2h_untagged(ptr));
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
set_helper_retaddr(1);
|
||||
ret = ldl_p(g2h_untagged(ptr));
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr ptr)
|
||||
{
|
||||
uint64_t ret;
|
||||
|
||||
set_helper_retaddr(1);
|
||||
ret = ldq_p(g2h_untagged(ptr));
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t cpu_ldb_code_mmu(CPUArchState *env, abi_ptr addr,
|
||||
uint8_t cpu_ldb_code_mmu(CPUArchState *env, vaddr addr,
|
||||
MemOpIdx oi, uintptr_t ra)
|
||||
{
|
||||
void *haddr;
|
||||
uint8_t ret;
|
||||
|
||||
haddr = cpu_mmu_lookup(env_cpu(env), addr, oi, ra, MMU_INST_FETCH);
|
||||
ret = ldub_p(haddr);
|
||||
clear_helper_retaddr();
|
||||
return ret;
|
||||
return do_ld1_mmu(env_cpu(env), addr, oi, ra ? ra : 1, MMU_INST_FETCH);
|
||||
}
|
||||
|
||||
uint16_t cpu_ldw_code_mmu(CPUArchState *env, abi_ptr addr,
|
||||
uint16_t cpu_ldw_code_mmu(CPUArchState *env, vaddr addr,
|
||||
MemOpIdx oi, uintptr_t ra)
|
||||
{
|
||||
void *haddr;
|
||||
uint16_t ret;
|
||||
|
||||
haddr = cpu_mmu_lookup(env_cpu(env), addr, oi, ra, MMU_INST_FETCH);
|
||||
ret = lduw_p(haddr);
|
||||
clear_helper_retaddr();
|
||||
if (get_memop(oi) & MO_BSWAP) {
|
||||
ret = bswap16(ret);
|
||||
}
|
||||
return ret;
|
||||
return do_ld2_mmu(env_cpu(env), addr, oi, ra ? ra : 1, MMU_INST_FETCH);
|
||||
}
|
||||
|
||||
uint32_t cpu_ldl_code_mmu(CPUArchState *env, abi_ptr addr,
|
||||
uint32_t cpu_ldl_code_mmu(CPUArchState *env, vaddr addr,
|
||||
MemOpIdx oi, uintptr_t ra)
|
||||
{
|
||||
void *haddr;
|
||||
uint32_t ret;
|
||||
|
||||
haddr = cpu_mmu_lookup(env_cpu(env), addr, oi, ra, MMU_INST_FETCH);
|
||||
ret = ldl_p(haddr);
|
||||
clear_helper_retaddr();
|
||||
if (get_memop(oi) & MO_BSWAP) {
|
||||
ret = bswap32(ret);
|
||||
}
|
||||
return ret;
|
||||
return do_ld4_mmu(env_cpu(env), addr, oi, ra ? ra : 1, MMU_INST_FETCH);
|
||||
}
|
||||
|
||||
uint64_t cpu_ldq_code_mmu(CPUArchState *env, abi_ptr addr,
|
||||
uint64_t cpu_ldq_code_mmu(CPUArchState *env, vaddr addr,
|
||||
MemOpIdx oi, uintptr_t ra)
|
||||
{
|
||||
void *haddr;
|
||||
uint64_t ret;
|
||||
|
||||
haddr = cpu_mmu_lookup(env_cpu(env), addr, oi, ra, MMU_DATA_LOAD);
|
||||
ret = ldq_p(haddr);
|
||||
clear_helper_retaddr();
|
||||
if (get_memop(oi) & MO_BSWAP) {
|
||||
ret = bswap64(ret);
|
||||
}
|
||||
return ret;
|
||||
return do_ld8_mmu(env_cpu(env), addr, oi, ra ? ra : 1, MMU_INST_FETCH);
|
||||
}
|
||||
|
||||
#include "ldst_common.c.inc"
|
||||
|
|
|
|||
|
|
@ -124,17 +124,14 @@ void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len,
|
|||
}
|
||||
cpu->watchpoint_hit = wp;
|
||||
|
||||
mmap_lock();
|
||||
/* This call also restores vCPU state */
|
||||
tb_check_watchpoint(cpu, ra);
|
||||
if (wp->flags & BP_STOP_BEFORE_ACCESS) {
|
||||
cpu->exception_index = EXCP_DEBUG;
|
||||
mmap_unlock();
|
||||
cpu_loop_exit(cpu);
|
||||
} else {
|
||||
/* Force execution of one insn next time. */
|
||||
cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu);
|
||||
mmap_unlock();
|
||||
cpu_loop_exit_noexc(cpu);
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@
|
|||
#include "hw/xen/xen_igd.h"
|
||||
#include "chardev/char.h"
|
||||
#include "qemu/accel.h"
|
||||
#include "system/accel-ops.h"
|
||||
#include "accel/dummy-cpus.h"
|
||||
#include "accel/accel-ops.h"
|
||||
#include "accel/accel-cpu-ops.h"
|
||||
#include "system/cpus.h"
|
||||
#include "system/xen.h"
|
||||
#include "system/runstate.h"
|
||||
|
|
@ -63,7 +65,7 @@ static void xen_set_igd_gfx_passthru(Object *obj, bool value, Error **errp)
|
|||
xen_igd_gfx_pt_set(value, errp);
|
||||
}
|
||||
|
||||
static void xen_setup_post(MachineState *ms, AccelState *accel)
|
||||
static void xen_setup_post(AccelState *as)
|
||||
{
|
||||
int rc;
|
||||
|
||||
|
|
@ -76,7 +78,7 @@ static void xen_setup_post(MachineState *ms, AccelState *accel)
|
|||
}
|
||||
}
|
||||
|
||||
static int xen_init(MachineState *ms)
|
||||
static int xen_init(AccelState *as, MachineState *ms)
|
||||
{
|
||||
MachineClass *mc = MACHINE_GET_CLASS(ms);
|
||||
|
||||
|
|
@ -116,7 +118,7 @@ static int xen_init(MachineState *ms)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void xen_accel_class_init(ObjectClass *oc, void *data)
|
||||
static void xen_accel_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
AccelClass *ac = ACCEL_CLASS(oc);
|
||||
static GlobalProperty compat[] = {
|
||||
|
|
@ -147,11 +149,12 @@ static const TypeInfo xen_accel_type = {
|
|||
.class_init = xen_accel_class_init,
|
||||
};
|
||||
|
||||
static void xen_accel_ops_class_init(ObjectClass *oc, void *data)
|
||||
static void xen_accel_ops_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
|
||||
|
||||
ops->create_vcpu_thread = dummy_start_vcpu_thread;
|
||||
ops->handle_interrupt = generic_handle_interrupt;
|
||||
}
|
||||
|
||||
static const TypeInfo xen_accel_ops_type = {
|
||||
|
|
|
|||
|
|
@ -899,7 +899,7 @@ static void alsa_enable_in(HWVoiceIn *hw, bool enable)
|
|||
static void alsa_init_per_direction(AudiodevAlsaPerDirectionOptions *apdo)
|
||||
{
|
||||
if (!apdo->has_try_poll) {
|
||||
apdo->try_poll = true;
|
||||
apdo->try_poll = false;
|
||||
apdo->has_try_poll = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1892,7 +1892,8 @@ CaptureVoiceOut *AUD_add_capture(
|
|||
cap->buf = g_malloc0_n(hw->mix_buf.size, hw->info.bytes_per_frame);
|
||||
|
||||
if (hw->info.is_float) {
|
||||
hw->clip = mixeng_clip_float[hw->info.nchannels == 2];
|
||||
hw->clip = mixeng_clip_float[hw->info.nchannels == 2]
|
||||
[hw->info.swap_endianness];
|
||||
} else {
|
||||
hw->clip = mixeng_clip
|
||||
[hw->info.nchannels == 2]
|
||||
|
|
@ -2282,17 +2283,19 @@ size_t audio_rate_peek_bytes(RateCtl *rate, struct audio_pcm_info *info)
|
|||
ticks = now - rate->start_ticks;
|
||||
bytes = muldiv64(ticks, info->bytes_per_second, NANOSECONDS_PER_SECOND);
|
||||
frames = (bytes - rate->bytes_sent) / info->bytes_per_frame;
|
||||
if (frames < 0 || frames > 65536) {
|
||||
AUD_log(NULL, "Resetting rate control (%" PRId64 " frames)\n", frames);
|
||||
audio_rate_start(rate);
|
||||
frames = 0;
|
||||
}
|
||||
rate->peeked_frames = frames;
|
||||
|
||||
return frames * info->bytes_per_frame;
|
||||
return frames < 0 ? 0 : frames * info->bytes_per_frame;
|
||||
}
|
||||
|
||||
void audio_rate_add_bytes(RateCtl *rate, size_t bytes_used)
|
||||
{
|
||||
if (rate->peeked_frames < 0 || rate->peeked_frames > 65536) {
|
||||
AUD_log(NULL, "Resetting rate control (%" PRId64 " frames)\n",
|
||||
rate->peeked_frames);
|
||||
audio_rate_start(rate);
|
||||
}
|
||||
|
||||
rate->bytes_sent += bytes_used;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -255,6 +255,7 @@ const char *audio_application_name(void);
|
|||
typedef struct RateCtl {
|
||||
int64_t start_ticks;
|
||||
int64_t bytes_sent;
|
||||
int64_t peeked_frames;
|
||||
} RateCtl;
|
||||
|
||||
void audio_rate_start(RateCtl *rate);
|
||||
|
|
|
|||
|
|
@ -174,9 +174,11 @@ static int glue (audio_pcm_sw_init_, TYPE) (
|
|||
|
||||
if (sw->info.is_float) {
|
||||
#ifdef DAC
|
||||
sw->conv = mixeng_conv_float[sw->info.nchannels == 2];
|
||||
sw->conv = mixeng_conv_float[sw->info.nchannels == 2]
|
||||
[sw->info.swap_endianness];
|
||||
#else
|
||||
sw->clip = mixeng_clip_float[sw->info.nchannels == 2];
|
||||
sw->clip = mixeng_clip_float[sw->info.nchannels == 2]
|
||||
[sw->info.swap_endianness];
|
||||
#endif
|
||||
} else {
|
||||
#ifdef DAC
|
||||
|
|
@ -303,9 +305,11 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
|
|||
|
||||
if (hw->info.is_float) {
|
||||
#ifdef DAC
|
||||
hw->clip = mixeng_clip_float[hw->info.nchannels == 2];
|
||||
hw->clip = mixeng_clip_float[hw->info.nchannels == 2]
|
||||
[hw->info.swap_endianness];
|
||||
#else
|
||||
hw->conv = mixeng_conv_float[hw->info.nchannels == 2];
|
||||
hw->conv = mixeng_conv_float[hw->info.nchannels == 2]
|
||||
[hw->info.swap_endianness];
|
||||
#endif
|
||||
} else {
|
||||
#ifdef DAC
|
||||
|
|
|
|||
|
|
@ -283,10 +283,15 @@ static const float float_scale_reciprocal = 1.f / ((int64_t)INT32_MAX + 1);
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#define F32_TO_F32S(v) \
|
||||
bswap32((union { uint32_t i; float f; }){ .f = (v) }.i)
|
||||
#define F32S_TO_F32(v) \
|
||||
((union { uint32_t i; float f; }){ .i = bswap32(v) }.f)
|
||||
|
||||
static void conv_natural_float_to_mono(struct st_sample *dst, const void *src,
|
||||
int samples)
|
||||
{
|
||||
float *in = (float *)src;
|
||||
const float *in = src;
|
||||
|
||||
while (samples--) {
|
||||
dst->r = dst->l = CONV_NATURAL_FLOAT(*in++);
|
||||
|
|
@ -294,10 +299,21 @@ static void conv_natural_float_to_mono(struct st_sample *dst, const void *src,
|
|||
}
|
||||
}
|
||||
|
||||
static void conv_swap_float_to_mono(struct st_sample *dst, const void *src,
|
||||
int samples)
|
||||
{
|
||||
const uint32_t *in_f32s = src;
|
||||
|
||||
while (samples--) {
|
||||
dst->r = dst->l = CONV_NATURAL_FLOAT(F32S_TO_F32(*in_f32s++));
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
static void conv_natural_float_to_stereo(struct st_sample *dst, const void *src,
|
||||
int samples)
|
||||
{
|
||||
float *in = (float *)src;
|
||||
const float *in = src;
|
||||
|
||||
while (samples--) {
|
||||
dst->l = CONV_NATURAL_FLOAT(*in++);
|
||||
|
|
@ -306,15 +322,33 @@ static void conv_natural_float_to_stereo(struct st_sample *dst, const void *src,
|
|||
}
|
||||
}
|
||||
|
||||
t_sample *mixeng_conv_float[2] = {
|
||||
conv_natural_float_to_mono,
|
||||
conv_natural_float_to_stereo,
|
||||
static void conv_swap_float_to_stereo(struct st_sample *dst, const void *src,
|
||||
int samples)
|
||||
{
|
||||
const uint32_t *in_f32s = src;
|
||||
|
||||
while (samples--) {
|
||||
dst->l = CONV_NATURAL_FLOAT(F32S_TO_F32(*in_f32s++));
|
||||
dst->r = CONV_NATURAL_FLOAT(F32S_TO_F32(*in_f32s++));
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
t_sample *mixeng_conv_float[2][2] = {
|
||||
{
|
||||
conv_natural_float_to_mono,
|
||||
conv_swap_float_to_mono,
|
||||
},
|
||||
{
|
||||
conv_natural_float_to_stereo,
|
||||
conv_swap_float_to_stereo,
|
||||
}
|
||||
};
|
||||
|
||||
static void clip_natural_float_from_mono(void *dst, const struct st_sample *src,
|
||||
int samples)
|
||||
{
|
||||
float *out = (float *)dst;
|
||||
float *out = dst;
|
||||
|
||||
while (samples--) {
|
||||
*out++ = CLIP_NATURAL_FLOAT(src->l + src->r);
|
||||
|
|
@ -322,10 +356,21 @@ static void clip_natural_float_from_mono(void *dst, const struct st_sample *src,
|
|||
}
|
||||
}
|
||||
|
||||
static void clip_swap_float_from_mono(void *dst, const struct st_sample *src,
|
||||
int samples)
|
||||
{
|
||||
uint32_t *out_f32s = dst;
|
||||
|
||||
while (samples--) {
|
||||
*out_f32s++ = F32_TO_F32S(CLIP_NATURAL_FLOAT(src->l + src->r));
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
static void clip_natural_float_from_stereo(
|
||||
void *dst, const struct st_sample *src, int samples)
|
||||
{
|
||||
float *out = (float *)dst;
|
||||
float *out = dst;
|
||||
|
||||
while (samples--) {
|
||||
*out++ = CLIP_NATURAL_FLOAT(src->l);
|
||||
|
|
@ -334,9 +379,27 @@ static void clip_natural_float_from_stereo(
|
|||
}
|
||||
}
|
||||
|
||||
f_sample *mixeng_clip_float[2] = {
|
||||
clip_natural_float_from_mono,
|
||||
clip_natural_float_from_stereo,
|
||||
static void clip_swap_float_from_stereo(
|
||||
void *dst, const struct st_sample *src, int samples)
|
||||
{
|
||||
uint32_t *out_f32s = dst;
|
||||
|
||||
while (samples--) {
|
||||
*out_f32s++ = F32_TO_F32S(CLIP_NATURAL_FLOAT(src->l));
|
||||
*out_f32s++ = F32_TO_F32S(CLIP_NATURAL_FLOAT(src->r));
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
f_sample *mixeng_clip_float[2][2] = {
|
||||
{
|
||||
clip_natural_float_from_mono,
|
||||
clip_swap_float_from_mono,
|
||||
},
|
||||
{
|
||||
clip_natural_float_from_stereo,
|
||||
clip_swap_float_from_stereo,
|
||||
}
|
||||
};
|
||||
|
||||
void audio_sample_to_uint64(const void *samples, int pos,
|
||||
|
|
|
|||
|
|
@ -42,9 +42,9 @@ typedef void (f_sample) (void *dst, const struct st_sample *src, int samples);
|
|||
extern t_sample *mixeng_conv[2][2][2][3];
|
||||
extern f_sample *mixeng_clip[2][2][2][3];
|
||||
|
||||
/* indices: [stereo] */
|
||||
extern t_sample *mixeng_conv_float[2];
|
||||
extern f_sample *mixeng_clip_float[2];
|
||||
/* indices: [stereo][swap endianness] */
|
||||
extern t_sample *mixeng_conv_float[2][2];
|
||||
extern f_sample *mixeng_clip_float[2][2];
|
||||
|
||||
void *st_rate_start (int inrate, int outrate);
|
||||
void st_rate_flow(void *opaque, st_sample *ibuf, st_sample *obuf,
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ qauthz_list_finalize(Object *obj)
|
|||
|
||||
|
||||
static void
|
||||
qauthz_list_class_init(ObjectClass *oc, void *data)
|
||||
qauthz_list_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
QAuthZClass *authz = QAUTHZ_CLASS(oc);
|
||||
|
||||
|
|
@ -253,7 +253,7 @@ static const TypeInfo qauthz_list_info = {
|
|||
.instance_size = sizeof(QAuthZList),
|
||||
.instance_finalize = qauthz_list_finalize,
|
||||
.class_init = qauthz_list_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
.interfaces = (const InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ qauthz_list_file_finalize(Object *obj)
|
|||
|
||||
|
||||
static void
|
||||
qauthz_list_file_class_init(ObjectClass *oc, void *data)
|
||||
qauthz_list_file_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
||||
QAuthZClass *authz = QAUTHZ_CLASS(oc);
|
||||
|
|
@ -272,7 +272,7 @@ static const TypeInfo qauthz_list_file_info = {
|
|||
.instance_size = sizeof(QAuthZListFile),
|
||||
.instance_finalize = qauthz_list_file_finalize,
|
||||
.class_init = qauthz_list_file_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
.interfaces = (const InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ qauthz_pam_finalize(Object *obj)
|
|||
|
||||
|
||||
static void
|
||||
qauthz_pam_class_init(ObjectClass *oc, void *data)
|
||||
qauthz_pam_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
||||
QAuthZClass *authz = QAUTHZ_CLASS(oc);
|
||||
|
|
@ -136,7 +136,7 @@ static const TypeInfo qauthz_pam_info = {
|
|||
.instance_size = sizeof(QAuthZPAM),
|
||||
.instance_finalize = qauthz_pam_finalize,
|
||||
.class_init = qauthz_pam_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
.interfaces = (const InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ qauthz_simple_complete(UserCreatable *uc, Error **errp)
|
|||
|
||||
|
||||
static void
|
||||
qauthz_simple_class_init(ObjectClass *oc, void *data)
|
||||
qauthz_simple_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
QAuthZClass *authz = QAUTHZ_CLASS(oc);
|
||||
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
||||
|
|
@ -111,7 +111,7 @@ static const TypeInfo qauthz_simple_info = {
|
|||
.instance_size = sizeof(QAuthZSimple),
|
||||
.instance_finalize = qauthz_simple_finalize,
|
||||
.class_init = qauthz_simple_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
.interfaces = (const InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,14 +14,58 @@
|
|||
#include "qemu/osdep.h"
|
||||
|
||||
#include "system/confidential-guest-support.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
OBJECT_DEFINE_ABSTRACT_TYPE(ConfidentialGuestSupport,
|
||||
confidential_guest_support,
|
||||
CONFIDENTIAL_GUEST_SUPPORT,
|
||||
OBJECT)
|
||||
|
||||
static void confidential_guest_support_class_init(ObjectClass *oc, void *data)
|
||||
static bool check_support(ConfidentialGuestPlatformType platform,
|
||||
uint16_t platform_version, uint8_t highest_vtl,
|
||||
uint64_t shared_gpa_boundary)
|
||||
{
|
||||
/* Default: no support. */
|
||||
return false;
|
||||
}
|
||||
|
||||
static int set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len,
|
||||
ConfidentialGuestPageType memory_type,
|
||||
uint16_t cpu_index, Error **errp)
|
||||
{
|
||||
error_setg(errp,
|
||||
"Setting confidential guest state is not supported for this platform");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int set_guest_policy(ConfidentialGuestPolicyType policy_type,
|
||||
uint64_t policy,
|
||||
void *policy_data1, uint32_t policy_data1_size,
|
||||
void *policy_data2, uint32_t policy_data2_size,
|
||||
Error **errp)
|
||||
{
|
||||
error_setg(errp,
|
||||
"Setting confidential guest policy is not supported for this platform");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int get_mem_map_entry(int index, ConfidentialGuestMemoryMapEntry *entry,
|
||||
Error **errp)
|
||||
{
|
||||
error_setg(
|
||||
errp,
|
||||
"Obtaining the confidential guest memory map is not supported for this platform");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void confidential_guest_support_class_init(ObjectClass *oc,
|
||||
const void *data)
|
||||
{
|
||||
ConfidentialGuestSupportClass *cgsc = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc);
|
||||
cgsc->check_support = check_support;
|
||||
cgsc->set_guest_state = set_guest_state;
|
||||
cgsc->set_guest_policy = set_guest_policy;
|
||||
cgsc->get_mem_map_entry = get_mem_map_entry;
|
||||
}
|
||||
|
||||
static void confidential_guest_support_init(Object *obj)
|
||||
|
|
|
|||
|
|
@ -608,7 +608,7 @@ static void cryptodev_builtin_cleanup(
|
|||
}
|
||||
|
||||
static void
|
||||
cryptodev_builtin_class_init(ObjectClass *oc, void *data)
|
||||
cryptodev_builtin_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_CLASS(oc);
|
||||
|
||||
|
|
|
|||
|
|
@ -619,7 +619,7 @@ static int cryptodev_lkcf_close_session(CryptoDevBackend *backend,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void cryptodev_lkcf_class_init(ObjectClass *oc, void *data)
|
||||
static void cryptodev_lkcf_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_CLASS(oc);
|
||||
|
||||
|
|
|
|||
|
|
@ -393,7 +393,7 @@ static void cryptodev_vhost_user_finalize(Object *obj)
|
|||
}
|
||||
|
||||
static void
|
||||
cryptodev_vhost_user_class_init(ObjectClass *oc, void *data)
|
||||
cryptodev_vhost_user_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_CLASS(oc);
|
||||
|
||||
|
|
|
|||
|
|
@ -608,7 +608,7 @@ static void cryptodev_backend_schemas_cb(StatsSchemaList **result,
|
|||
}
|
||||
|
||||
static void
|
||||
cryptodev_backend_class_init(ObjectClass *oc, void *data)
|
||||
cryptodev_backend_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
||||
|
||||
|
|
@ -641,7 +641,7 @@ static const TypeInfo cryptodev_backend_info = {
|
|||
.instance_finalize = cryptodev_backend_finalize,
|
||||
.class_size = sizeof(CryptoDevBackendClass),
|
||||
.class_init = cryptodev_backend_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
.interfaces = (const InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -485,7 +485,7 @@ dbus_vmstate_get_id(VMStateIf *vmif)
|
|||
}
|
||||
|
||||
static void
|
||||
dbus_vmstate_class_init(ObjectClass *oc, void *data)
|
||||
dbus_vmstate_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
||||
VMStateIfClass *vc = VMSTATE_IF_CLASS(oc);
|
||||
|
|
@ -505,7 +505,7 @@ static const TypeInfo dbus_vmstate_info = {
|
|||
.instance_size = sizeof(DBusVMState),
|
||||
.instance_finalize = dbus_vmstate_finalize,
|
||||
.class_init = dbus_vmstate_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
.interfaces = (const InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ TYPE_VMSTATE_IF },
|
||||
{ }
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ OBJECT_DEFINE_ABSTRACT_TYPE(HostIOMMUDevice,
|
|||
HOST_IOMMU_DEVICE,
|
||||
OBJECT)
|
||||
|
||||
static void host_iommu_device_class_init(ObjectClass *oc, void *data)
|
||||
static void host_iommu_device_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ static void sgx_epc_backend_instance_init(Object *obj)
|
|||
m->dump = false;
|
||||
}
|
||||
|
||||
static void sgx_epc_backend_class_init(ObjectClass *oc, void *data)
|
||||
static void sgx_epc_backend_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc);
|
||||
|
||||
|
|
|
|||
|
|
@ -270,7 +270,7 @@ static void file_backend_unparent(Object *obj)
|
|||
}
|
||||
|
||||
static void
|
||||
file_backend_class_init(ObjectClass *oc, void *data)
|
||||
file_backend_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc);
|
||||
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ memfd_backend_instance_init(Object *obj)
|
|||
}
|
||||
|
||||
static void
|
||||
memfd_backend_class_init(ObjectClass *oc, void *data)
|
||||
memfd_backend_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc);
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
|||
}
|
||||
|
||||
static void
|
||||
ram_backend_class_init(ObjectClass *oc, void *data)
|
||||
ram_backend_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc);
|
||||
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ shm_backend_instance_init(Object *obj)
|
|||
}
|
||||
|
||||
static void
|
||||
shm_backend_class_init(ObjectClass *oc, void *data)
|
||||
shm_backend_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc);
|
||||
|
||||
|
|
|
|||
|
|
@ -501,7 +501,7 @@ host_memory_backend_set_use_canonical_path(Object *obj, bool value,
|
|||
}
|
||||
|
||||
static void
|
||||
host_memory_backend_class_init(ObjectClass *oc, void *data)
|
||||
host_memory_backend_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
||||
|
||||
|
|
@ -586,7 +586,7 @@ static const TypeInfo host_memory_backend_info = {
|
|||
.instance_size = sizeof(HostMemoryBackend),
|
||||
.instance_init = host_memory_backend_init,
|
||||
.instance_post_init = host_memory_backend_post_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
.interfaces = (const InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ }
|
||||
}
|
||||
|
|
|
|||
51
backends/igvm-cfg.c
Normal file
51
backends/igvm-cfg.c
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* QEMU IGVM interface
|
||||
*
|
||||
* Copyright (C) 2023-2024 SUSE
|
||||
*
|
||||
* Authors:
|
||||
* Roy Hopkins <roy.hopkins@randomman.co.uk>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#include "system/igvm-cfg.h"
|
||||
#include "igvm.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
|
||||
static char *get_igvm(Object *obj, Error **errp)
|
||||
{
|
||||
IgvmCfg *igvm = IGVM_CFG(obj);
|
||||
return g_strdup(igvm->filename);
|
||||
}
|
||||
|
||||
static void set_igvm(Object *obj, const char *value, Error **errp)
|
||||
{
|
||||
IgvmCfg *igvm = IGVM_CFG(obj);
|
||||
g_free(igvm->filename);
|
||||
igvm->filename = g_strdup(value);
|
||||
}
|
||||
|
||||
OBJECT_DEFINE_TYPE_WITH_INTERFACES(IgvmCfg, igvm_cfg, IGVM_CFG, OBJECT,
|
||||
{ TYPE_USER_CREATABLE }, { NULL })
|
||||
|
||||
static void igvm_cfg_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
IgvmCfgClass *igvmc = IGVM_CFG_CLASS(oc);
|
||||
|
||||
object_class_property_add_str(oc, "file", get_igvm, set_igvm);
|
||||
object_class_property_set_description(oc, "file",
|
||||
"Set the IGVM filename to use");
|
||||
|
||||
igvmc->process = qigvm_process_file;
|
||||
}
|
||||
|
||||
static void igvm_cfg_init(Object *obj)
|
||||
{
|
||||
}
|
||||
|
||||
static void igvm_cfg_finalize(Object *obj)
|
||||
{
|
||||
}
|
||||
988
backends/igvm.c
Normal file
988
backends/igvm.c
Normal file
|
|
@ -0,0 +1,988 @@
|
|||
/*
|
||||
* QEMU IGVM configuration backend for guests
|
||||
*
|
||||
* Copyright (C) 2023-2024 SUSE
|
||||
*
|
||||
* Authors:
|
||||
* Roy Hopkins <roy.hopkins@randomman.co.uk>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#include "igvm.h"
|
||||
#include "qapi/error.h"
|
||||
#include "system/memory.h"
|
||||
#include "system/address-spaces.h"
|
||||
#include "hw/core/cpu.h"
|
||||
|
||||
#include <igvm/igvm.h>
|
||||
#include <igvm/igvm_defs.h>
|
||||
|
||||
typedef struct QIgvmParameterData {
|
||||
QTAILQ_ENTRY(QIgvmParameterData) next;
|
||||
uint8_t *data;
|
||||
uint32_t size;
|
||||
uint32_t index;
|
||||
} QIgvmParameterData;
|
||||
|
||||
/*
|
||||
* Some directives are specific to particular confidential computing platforms.
|
||||
* Define required types for each of those platforms here.
|
||||
*/
|
||||
|
||||
/* SEV/SEV-ES/SEV-SNP */
|
||||
|
||||
/*
|
||||
* These structures are defined in "SEV Secure Nested Paging Firmware ABI
|
||||
* Specification" Rev 1.58, section 8.18.
|
||||
*/
|
||||
struct QEMU_PACKED sev_id_block {
|
||||
uint8_t ld[48];
|
||||
uint8_t family_id[16];
|
||||
uint8_t image_id[16];
|
||||
uint32_t version;
|
||||
uint32_t guest_svn;
|
||||
uint64_t policy;
|
||||
};
|
||||
|
||||
struct QEMU_PACKED sev_id_authentication {
|
||||
uint32_t id_key_alg;
|
||||
uint32_t auth_key_algo;
|
||||
uint8_t reserved[56];
|
||||
uint8_t id_block_sig[512];
|
||||
uint8_t id_key[1028];
|
||||
uint8_t reserved2[60];
|
||||
uint8_t id_key_sig[512];
|
||||
uint8_t author_key[1028];
|
||||
uint8_t reserved3[892];
|
||||
};
|
||||
|
||||
#define IGVM_SEV_ID_BLOCK_VERSION 1
|
||||
|
||||
/*
|
||||
* QIgvm contains the information required during processing
|
||||
* of a single IGVM file.
|
||||
*/
|
||||
typedef struct QIgvm {
|
||||
IgvmHandle file;
|
||||
ConfidentialGuestSupport *cgs;
|
||||
ConfidentialGuestSupportClass *cgsc;
|
||||
uint32_t compatibility_mask;
|
||||
unsigned current_header_index;
|
||||
QTAILQ_HEAD(, QIgvmParameterData) parameter_data;
|
||||
IgvmPlatformType platform_type;
|
||||
|
||||
/*
|
||||
* SEV-SNP platforms can contain an ID block and authentication
|
||||
* that should be verified by the guest.
|
||||
*/
|
||||
struct sev_id_block *id_block;
|
||||
struct sev_id_authentication *id_auth;
|
||||
|
||||
/* Define the guest policy for SEV guests */
|
||||
uint64_t sev_policy;
|
||||
|
||||
/* These variables keep track of contiguous page regions */
|
||||
IGVM_VHS_PAGE_DATA region_prev_page_data;
|
||||
uint64_t region_start;
|
||||
unsigned region_start_index;
|
||||
unsigned region_last_index;
|
||||
unsigned region_page_count;
|
||||
} QIgvm;
|
||||
|
||||
static int qigvm_directive_page_data(QIgvm *ctx, const uint8_t *header_data,
|
||||
Error **errp);
|
||||
static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data,
|
||||
Error **errp);
|
||||
static int qigvm_directive_parameter_area(QIgvm *ctx,
|
||||
const uint8_t *header_data,
|
||||
Error **errp);
|
||||
static int qigvm_directive_parameter_insert(QIgvm *ctx,
|
||||
const uint8_t *header_data,
|
||||
Error **errp);
|
||||
static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_data,
|
||||
Error **errp);
|
||||
static int qigvm_directive_vp_count(QIgvm *ctx, const uint8_t *header_data,
|
||||
Error **errp);
|
||||
static int qigvm_directive_environment_info(QIgvm *ctx,
|
||||
const uint8_t *header_data,
|
||||
Error **errp);
|
||||
static int qigvm_directive_required_memory(QIgvm *ctx,
|
||||
const uint8_t *header_data,
|
||||
Error **errp);
|
||||
static int qigvm_directive_snp_id_block(QIgvm *ctx, const uint8_t *header_data,
|
||||
Error **errp);
|
||||
static int qigvm_initialization_guest_policy(QIgvm *ctx,
|
||||
const uint8_t *header_data,
|
||||
Error **errp);
|
||||
|
||||
struct QIGVMHandler {
|
||||
uint32_t type;
|
||||
uint32_t section;
|
||||
int (*handler)(QIgvm *ctx, const uint8_t *header_data, Error **errp);
|
||||
};
|
||||
|
||||
static struct QIGVMHandler handlers[] = {
|
||||
{ IGVM_VHT_PAGE_DATA, IGVM_HEADER_SECTION_DIRECTIVE,
|
||||
qigvm_directive_page_data },
|
||||
{ IGVM_VHT_VP_CONTEXT, IGVM_HEADER_SECTION_DIRECTIVE,
|
||||
qigvm_directive_vp_context },
|
||||
{ IGVM_VHT_PARAMETER_AREA, IGVM_HEADER_SECTION_DIRECTIVE,
|
||||
qigvm_directive_parameter_area },
|
||||
{ IGVM_VHT_PARAMETER_INSERT, IGVM_HEADER_SECTION_DIRECTIVE,
|
||||
qigvm_directive_parameter_insert },
|
||||
{ IGVM_VHT_MEMORY_MAP, IGVM_HEADER_SECTION_DIRECTIVE,
|
||||
qigvm_directive_memory_map },
|
||||
{ IGVM_VHT_VP_COUNT_PARAMETER, IGVM_HEADER_SECTION_DIRECTIVE,
|
||||
qigvm_directive_vp_count },
|
||||
{ IGVM_VHT_ENVIRONMENT_INFO_PARAMETER, IGVM_HEADER_SECTION_DIRECTIVE,
|
||||
qigvm_directive_environment_info },
|
||||
{ IGVM_VHT_REQUIRED_MEMORY, IGVM_HEADER_SECTION_DIRECTIVE,
|
||||
qigvm_directive_required_memory },
|
||||
{ IGVM_VHT_SNP_ID_BLOCK, IGVM_HEADER_SECTION_DIRECTIVE,
|
||||
qigvm_directive_snp_id_block },
|
||||
{ IGVM_VHT_GUEST_POLICY, IGVM_HEADER_SECTION_INITIALIZATION,
|
||||
qigvm_initialization_guest_policy },
|
||||
};
|
||||
|
||||
static int qigvm_handler(QIgvm *ctx, uint32_t type, Error **errp)
|
||||
{
|
||||
size_t handler;
|
||||
IgvmHandle header_handle;
|
||||
const uint8_t *header_data;
|
||||
int result;
|
||||
|
||||
for (handler = 0; handler < G_N_ELEMENTS(handlers); handler++) {
|
||||
if (handlers[handler].type != type) {
|
||||
continue;
|
||||
}
|
||||
header_handle = igvm_get_header(ctx->file, handlers[handler].section,
|
||||
ctx->current_header_index);
|
||||
if (header_handle < 0) {
|
||||
error_setg(
|
||||
errp,
|
||||
"IGVM file is invalid: Failed to read directive header (code: %d)",
|
||||
(int)header_handle);
|
||||
return -1;
|
||||
}
|
||||
header_data = igvm_get_buffer(ctx->file, header_handle) +
|
||||
sizeof(IGVM_VHS_VARIABLE_HEADER);
|
||||
result = handlers[handler].handler(ctx, header_data, errp);
|
||||
igvm_free_buffer(ctx->file, header_handle);
|
||||
return result;
|
||||
}
|
||||
error_setg(errp,
|
||||
"IGVM: Unknown header type encountered when processing file: "
|
||||
"(type 0x%X)",
|
||||
type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void *qigvm_prepare_memory(QIgvm *ctx, uint64_t addr, uint64_t size,
|
||||
int region_identifier, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
MemoryRegion *igvm_pages = NULL;
|
||||
Int128 gpa_region_size;
|
||||
MemoryRegionSection mrs =
|
||||
memory_region_find(get_system_memory(), addr, size);
|
||||
if (mrs.mr) {
|
||||
if (!memory_region_is_ram(mrs.mr)) {
|
||||
memory_region_unref(mrs.mr);
|
||||
error_setg(
|
||||
errp,
|
||||
"Processing of IGVM file failed: Could not prepare memory "
|
||||
"at address 0x%lX due to existing non-RAM region",
|
||||
addr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gpa_region_size = int128_make64(size);
|
||||
if (int128_lt(mrs.size, gpa_region_size)) {
|
||||
memory_region_unref(mrs.mr);
|
||||
error_setg(
|
||||
errp,
|
||||
"Processing of IGVM file failed: Could not prepare memory "
|
||||
"at address 0x%lX: region size exceeded",
|
||||
addr);
|
||||
return NULL;
|
||||
}
|
||||
return qemu_map_ram_ptr(mrs.mr->ram_block, mrs.offset_within_region);
|
||||
} else {
|
||||
/*
|
||||
* The region_identifier is the is the index of the IGVM directive that
|
||||
* contains the page with the lowest GPA in the region. This will
|
||||
* generate a unique region name.
|
||||
*/
|
||||
g_autofree char *region_name =
|
||||
g_strdup_printf("igvm.%X", region_identifier);
|
||||
igvm_pages = g_new0(MemoryRegion, 1);
|
||||
if (ctx->cgs && ctx->cgs->require_guest_memfd) {
|
||||
if (!memory_region_init_ram_guest_memfd(igvm_pages, NULL,
|
||||
region_name, size, errp)) {
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
if (!memory_region_init_ram(igvm_pages, NULL, region_name, size,
|
||||
errp)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
memory_region_add_subregion(get_system_memory(), addr, igvm_pages);
|
||||
return memory_region_get_ram_ptr(igvm_pages);
|
||||
}
|
||||
}
|
||||
|
||||
static int qigvm_type_to_cgs_type(IgvmPageDataType memory_type, bool unmeasured,
|
||||
bool zero)
|
||||
{
|
||||
switch (memory_type) {
|
||||
case IGVM_PAGE_DATA_TYPE_NORMAL: {
|
||||
if (unmeasured) {
|
||||
return CGS_PAGE_TYPE_UNMEASURED;
|
||||
} else {
|
||||
return zero ? CGS_PAGE_TYPE_ZERO : CGS_PAGE_TYPE_NORMAL;
|
||||
}
|
||||
}
|
||||
case IGVM_PAGE_DATA_TYPE_SECRETS:
|
||||
return CGS_PAGE_TYPE_SECRETS;
|
||||
case IGVM_PAGE_DATA_TYPE_CPUID_DATA:
|
||||
return CGS_PAGE_TYPE_CPUID;
|
||||
case IGVM_PAGE_DATA_TYPE_CPUID_XF:
|
||||
return CGS_PAGE_TYPE_CPUID;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static bool qigvm_page_attrs_equal(IgvmHandle igvm, unsigned header_index,
|
||||
const IGVM_VHS_PAGE_DATA *page_1,
|
||||
const IGVM_VHS_PAGE_DATA *page_2)
|
||||
{
|
||||
IgvmHandle data_handle1, data_handle2;
|
||||
|
||||
/*
|
||||
* If one page has data and the other doesn't then this results in different
|
||||
* page types: NORMAL vs ZERO.
|
||||
*/
|
||||
data_handle1 = igvm_get_header_data(igvm, IGVM_HEADER_SECTION_DIRECTIVE,
|
||||
header_index - 1);
|
||||
data_handle2 =
|
||||
igvm_get_header_data(igvm, IGVM_HEADER_SECTION_DIRECTIVE, header_index);
|
||||
if ((data_handle1 == IGVMAPI_NO_DATA ||
|
||||
data_handle2 == IGVMAPI_NO_DATA) &&
|
||||
data_handle1 != data_handle2) {
|
||||
return false;
|
||||
}
|
||||
return ((*(const uint32_t *)&page_1->flags ==
|
||||
*(const uint32_t *)&page_2->flags) &&
|
||||
(page_1->data_type == page_2->data_type) &&
|
||||
(page_1->compatibility_mask == page_2->compatibility_mask));
|
||||
}
|
||||
|
||||
static int qigvm_process_mem_region(QIgvm *ctx, unsigned start_index,
|
||||
uint64_t gpa_start, unsigned page_count,
|
||||
const IgvmPageDataFlags *flags,
|
||||
const IgvmPageDataType page_type,
|
||||
Error **errp)
|
||||
{
|
||||
uint8_t *region;
|
||||
IgvmHandle data_handle;
|
||||
const void *data;
|
||||
uint32_t data_size;
|
||||
unsigned page_index;
|
||||
bool zero = true;
|
||||
const uint64_t page_size = flags->is_2mb_page ? 0x200000 : 0x1000;
|
||||
int result;
|
||||
int cgs_page_type;
|
||||
|
||||
region = qigvm_prepare_memory(ctx, gpa_start, page_count * page_size,
|
||||
start_index, errp);
|
||||
if (!region) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (page_index = 0; page_index < page_count; page_index++) {
|
||||
data_handle = igvm_get_header_data(
|
||||
ctx->file, IGVM_HEADER_SECTION_DIRECTIVE, page_index + start_index);
|
||||
if (data_handle == IGVMAPI_NO_DATA) {
|
||||
/* No data indicates a zero page */
|
||||
memset(®ion[page_index * page_size], 0, page_size);
|
||||
} else if (data_handle < 0) {
|
||||
error_setg(
|
||||
errp,
|
||||
"IGVM file contains invalid page data for directive with "
|
||||
"index %d",
|
||||
page_index + start_index);
|
||||
return -1;
|
||||
} else {
|
||||
zero = false;
|
||||
data_size = igvm_get_buffer_size(ctx->file, data_handle);
|
||||
if (data_size < page_size) {
|
||||
memset(®ion[page_index * page_size], 0, page_size);
|
||||
} else if (data_size > page_size) {
|
||||
error_setg(errp,
|
||||
"IGVM file contains page data with invalid size for "
|
||||
"directive with index %d",
|
||||
page_index + start_index);
|
||||
return -1;
|
||||
}
|
||||
data = igvm_get_buffer(ctx->file, data_handle);
|
||||
memcpy(®ion[page_index * page_size], data, data_size);
|
||||
igvm_free_buffer(ctx->file, data_handle);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If a confidential guest support object is provided then use it to set the
|
||||
* guest state.
|
||||
*/
|
||||
if (ctx->cgs) {
|
||||
cgs_page_type =
|
||||
qigvm_type_to_cgs_type(page_type, flags->unmeasured, zero);
|
||||
if (cgs_page_type < 0) {
|
||||
error_setg(errp,
|
||||
"Invalid page type in IGVM file. Directives: %d to %d, "
|
||||
"page type: %d",
|
||||
start_index, start_index + page_count, page_type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = ctx->cgsc->set_guest_state(
|
||||
gpa_start, region, page_size * page_count, cgs_page_type, 0, errp);
|
||||
if (result < 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qigvm_process_mem_page(QIgvm *ctx,
|
||||
const IGVM_VHS_PAGE_DATA *page_data,
|
||||
Error **errp)
|
||||
{
|
||||
if (page_data) {
|
||||
if (ctx->region_page_count == 0) {
|
||||
ctx->region_start = page_data->gpa;
|
||||
ctx->region_start_index = ctx->current_header_index;
|
||||
} else {
|
||||
if (!qigvm_page_attrs_equal(ctx->file, ctx->current_header_index,
|
||||
page_data,
|
||||
&ctx->region_prev_page_data) ||
|
||||
((ctx->region_prev_page_data.gpa +
|
||||
(ctx->region_prev_page_data.flags.is_2mb_page ? 0x200000 :
|
||||
0x1000)) !=
|
||||
page_data->gpa) ||
|
||||
(ctx->region_last_index != (ctx->current_header_index - 1))) {
|
||||
/* End of current region */
|
||||
if (qigvm_process_mem_region(
|
||||
ctx, ctx->region_start_index, ctx->region_start,
|
||||
ctx->region_page_count,
|
||||
&ctx->region_prev_page_data.flags,
|
||||
ctx->region_prev_page_data.data_type, errp) < 0) {
|
||||
return -1;
|
||||
}
|
||||
ctx->region_page_count = 0;
|
||||
ctx->region_start = page_data->gpa;
|
||||
ctx->region_start_index = ctx->current_header_index;
|
||||
}
|
||||
}
|
||||
memcpy(&ctx->region_prev_page_data, page_data,
|
||||
sizeof(ctx->region_prev_page_data));
|
||||
ctx->region_last_index = ctx->current_header_index;
|
||||
ctx->region_page_count++;
|
||||
} else {
|
||||
if (ctx->region_page_count > 0) {
|
||||
if (qigvm_process_mem_region(
|
||||
ctx, ctx->region_start_index, ctx->region_start,
|
||||
ctx->region_page_count, &ctx->region_prev_page_data.flags,
|
||||
ctx->region_prev_page_data.data_type, errp) < 0) {
|
||||
return -1;
|
||||
}
|
||||
ctx->region_page_count = 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qigvm_directive_page_data(QIgvm *ctx, const uint8_t *header_data,
|
||||
Error **errp)
|
||||
{
|
||||
const IGVM_VHS_PAGE_DATA *page_data =
|
||||
(const IGVM_VHS_PAGE_DATA *)header_data;
|
||||
if (page_data->compatibility_mask & ctx->compatibility_mask) {
|
||||
return qigvm_process_mem_page(ctx, page_data, errp);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data,
|
||||
Error **errp)
|
||||
{
|
||||
const IGVM_VHS_VP_CONTEXT *vp_context =
|
||||
(const IGVM_VHS_VP_CONTEXT *)header_data;
|
||||
IgvmHandle data_handle;
|
||||
uint8_t *data;
|
||||
int result;
|
||||
|
||||
if (!(vp_context->compatibility_mask & ctx->compatibility_mask)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* A confidential guest support object must be provided for setting
|
||||
* a VP context.
|
||||
*/
|
||||
if (!ctx->cgs) {
|
||||
error_setg(
|
||||
errp,
|
||||
"A VP context is present in the IGVM file but is not supported "
|
||||
"by the current system.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
data_handle = igvm_get_header_data(ctx->file, IGVM_HEADER_SECTION_DIRECTIVE,
|
||||
ctx->current_header_index);
|
||||
if (data_handle < 0) {
|
||||
error_setg(errp, "Invalid VP context in IGVM file. Error code: %X",
|
||||
data_handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
data = (uint8_t *)igvm_get_buffer(ctx->file, data_handle);
|
||||
result = ctx->cgsc->set_guest_state(
|
||||
vp_context->gpa, data, igvm_get_buffer_size(ctx->file, data_handle),
|
||||
CGS_PAGE_TYPE_VMSA, vp_context->vp_index, errp);
|
||||
igvm_free_buffer(ctx->file, data_handle);
|
||||
if (result < 0) {
|
||||
return result;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qigvm_directive_parameter_area(QIgvm *ctx,
|
||||
const uint8_t *header_data,
|
||||
Error **errp)
|
||||
{
|
||||
const IGVM_VHS_PARAMETER_AREA *param_area =
|
||||
(const IGVM_VHS_PARAMETER_AREA *)header_data;
|
||||
QIgvmParameterData *param_entry;
|
||||
|
||||
param_entry = g_new0(QIgvmParameterData, 1);
|
||||
param_entry->size = param_area->number_of_bytes;
|
||||
param_entry->index = param_area->parameter_area_index;
|
||||
param_entry->data = g_malloc0(param_entry->size);
|
||||
|
||||
QTAILQ_INSERT_TAIL(&ctx->parameter_data, param_entry, next);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qigvm_directive_parameter_insert(QIgvm *ctx,
|
||||
const uint8_t *header_data,
|
||||
Error **errp)
|
||||
{
|
||||
const IGVM_VHS_PARAMETER_INSERT *param =
|
||||
(const IGVM_VHS_PARAMETER_INSERT *)header_data;
|
||||
QIgvmParameterData *param_entry;
|
||||
int result;
|
||||
void *region;
|
||||
|
||||
if (!(param->compatibility_mask & ctx->compatibility_mask)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next)
|
||||
{
|
||||
if (param_entry->index == param->parameter_area_index) {
|
||||
region = qigvm_prepare_memory(ctx, param->gpa, param_entry->size,
|
||||
ctx->current_header_index, errp);
|
||||
if (!region) {
|
||||
return -1;
|
||||
}
|
||||
memcpy(region, param_entry->data, param_entry->size);
|
||||
g_free(param_entry->data);
|
||||
param_entry->data = NULL;
|
||||
|
||||
/*
|
||||
* If a confidential guest support object is provided then use it to
|
||||
* set the guest state.
|
||||
*/
|
||||
if (ctx->cgs) {
|
||||
result = ctx->cgsc->set_guest_state(param->gpa, region,
|
||||
param_entry->size,
|
||||
CGS_PAGE_TYPE_UNMEASURED, 0,
|
||||
errp);
|
||||
if (result < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qigvm_cmp_mm_entry(const void *a, const void *b)
|
||||
{
|
||||
const IGVM_VHS_MEMORY_MAP_ENTRY *entry_a =
|
||||
(const IGVM_VHS_MEMORY_MAP_ENTRY *)a;
|
||||
const IGVM_VHS_MEMORY_MAP_ENTRY *entry_b =
|
||||
(const IGVM_VHS_MEMORY_MAP_ENTRY *)b;
|
||||
if (entry_a->starting_gpa_page_number < entry_b->starting_gpa_page_number) {
|
||||
return -1;
|
||||
} else if (entry_a->starting_gpa_page_number >
|
||||
entry_b->starting_gpa_page_number) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_data,
|
||||
Error **errp)
|
||||
{
|
||||
const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data;
|
||||
QIgvmParameterData *param_entry;
|
||||
int max_entry_count;
|
||||
int entry = 0;
|
||||
IGVM_VHS_MEMORY_MAP_ENTRY *mm_entry;
|
||||
ConfidentialGuestMemoryMapEntry cgmm_entry;
|
||||
int retval = 0;
|
||||
|
||||
if (!ctx->cgs) {
|
||||
error_setg(errp,
|
||||
"IGVM file contains a memory map but this is not supported "
|
||||
"by the current system.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Find the parameter area that should hold the memory map */
|
||||
QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next)
|
||||
{
|
||||
if (param_entry->index == param->parameter_area_index) {
|
||||
max_entry_count =
|
||||
param_entry->size / sizeof(IGVM_VHS_MEMORY_MAP_ENTRY);
|
||||
mm_entry = (IGVM_VHS_MEMORY_MAP_ENTRY *)param_entry->data;
|
||||
|
||||
retval = ctx->cgsc->get_mem_map_entry(entry, &cgmm_entry, errp);
|
||||
while (retval == 0) {
|
||||
if (entry > max_entry_count) {
|
||||
error_setg(
|
||||
errp,
|
||||
"IGVM: guest memory map size exceeds parameter area defined in IGVM file");
|
||||
return -1;
|
||||
}
|
||||
mm_entry[entry].starting_gpa_page_number = cgmm_entry.gpa >> 12;
|
||||
mm_entry[entry].number_of_pages = cgmm_entry.size >> 12;
|
||||
|
||||
switch (cgmm_entry.type) {
|
||||
case CGS_MEM_RAM:
|
||||
mm_entry[entry].entry_type =
|
||||
IGVM_MEMORY_MAP_ENTRY_TYPE_MEMORY;
|
||||
break;
|
||||
case CGS_MEM_RESERVED:
|
||||
mm_entry[entry].entry_type =
|
||||
IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED;
|
||||
break;
|
||||
case CGS_MEM_ACPI:
|
||||
mm_entry[entry].entry_type =
|
||||
IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED;
|
||||
break;
|
||||
case CGS_MEM_NVS:
|
||||
mm_entry[entry].entry_type =
|
||||
IGVM_MEMORY_MAP_ENTRY_TYPE_PERSISTENT;
|
||||
break;
|
||||
case CGS_MEM_UNUSABLE:
|
||||
mm_entry[entry].entry_type =
|
||||
IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED;
|
||||
break;
|
||||
}
|
||||
retval =
|
||||
ctx->cgsc->get_mem_map_entry(++entry, &cgmm_entry, errp);
|
||||
}
|
||||
if (retval < 0) {
|
||||
return retval;
|
||||
}
|
||||
/* The entries need to be sorted */
|
||||
qsort(mm_entry, entry, sizeof(IGVM_VHS_MEMORY_MAP_ENTRY),
|
||||
qigvm_cmp_mm_entry);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qigvm_directive_vp_count(QIgvm *ctx, const uint8_t *header_data,
|
||||
Error **errp)
|
||||
{
|
||||
const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data;
|
||||
QIgvmParameterData *param_entry;
|
||||
uint32_t *vp_count;
|
||||
CPUState *cpu;
|
||||
|
||||
QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next)
|
||||
{
|
||||
if (param_entry->index == param->parameter_area_index) {
|
||||
vp_count = (uint32_t *)(param_entry->data + param->byte_offset);
|
||||
*vp_count = 0;
|
||||
CPU_FOREACH(cpu)
|
||||
{
|
||||
(*vp_count)++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qigvm_directive_environment_info(QIgvm *ctx,
|
||||
const uint8_t *header_data,
|
||||
Error **errp)
|
||||
{
|
||||
const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data;
|
||||
QIgvmParameterData *param_entry;
|
||||
IgvmEnvironmentInfo *environmental_state;
|
||||
|
||||
QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next)
|
||||
{
|
||||
if (param_entry->index == param->parameter_area_index) {
|
||||
environmental_state =
|
||||
(IgvmEnvironmentInfo *)(param_entry->data + param->byte_offset);
|
||||
environmental_state->memory_is_shared = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qigvm_directive_required_memory(QIgvm *ctx,
|
||||
const uint8_t *header_data,
|
||||
Error **errp)
|
||||
{
|
||||
const IGVM_VHS_REQUIRED_MEMORY *mem =
|
||||
(const IGVM_VHS_REQUIRED_MEMORY *)header_data;
|
||||
uint8_t *region;
|
||||
int result;
|
||||
|
||||
if (!(mem->compatibility_mask & ctx->compatibility_mask)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
region = qigvm_prepare_memory(ctx, mem->gpa, mem->number_of_bytes,
|
||||
ctx->current_header_index, errp);
|
||||
if (!region) {
|
||||
return -1;
|
||||
}
|
||||
if (ctx->cgs) {
|
||||
result =
|
||||
ctx->cgsc->set_guest_state(mem->gpa, region, mem->number_of_bytes,
|
||||
CGS_PAGE_TYPE_REQUIRED_MEMORY, 0, errp);
|
||||
if (result < 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qigvm_directive_snp_id_block(QIgvm *ctx, const uint8_t *header_data,
|
||||
Error **errp)
|
||||
{
|
||||
const IGVM_VHS_SNP_ID_BLOCK *igvm_id =
|
||||
(const IGVM_VHS_SNP_ID_BLOCK *)header_data;
|
||||
|
||||
if (!(igvm_id->compatibility_mask & ctx->compatibility_mask)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ctx->id_block) {
|
||||
error_setg(errp, "IGVM: Multiple ID blocks encountered "
|
||||
"in IGVM file.");
|
||||
return -1;
|
||||
}
|
||||
ctx->id_block = g_new0(struct sev_id_block, 1);
|
||||
ctx->id_auth = g_new0(struct sev_id_authentication, 1);
|
||||
|
||||
memcpy(ctx->id_block->family_id, igvm_id->family_id,
|
||||
sizeof(ctx->id_block->family_id));
|
||||
memcpy(ctx->id_block->image_id, igvm_id->image_id,
|
||||
sizeof(ctx->id_block->image_id));
|
||||
ctx->id_block->guest_svn = igvm_id->guest_svn;
|
||||
ctx->id_block->version = IGVM_SEV_ID_BLOCK_VERSION;
|
||||
memcpy(ctx->id_block->ld, igvm_id->ld, sizeof(ctx->id_block->ld));
|
||||
|
||||
ctx->id_auth->id_key_alg = igvm_id->id_key_algorithm;
|
||||
assert(sizeof(igvm_id->id_key_signature) <=
|
||||
sizeof(ctx->id_auth->id_block_sig));
|
||||
memcpy(ctx->id_auth->id_block_sig, &igvm_id->id_key_signature,
|
||||
sizeof(igvm_id->id_key_signature));
|
||||
|
||||
ctx->id_auth->auth_key_algo = igvm_id->author_key_algorithm;
|
||||
assert(sizeof(igvm_id->author_key_signature) <=
|
||||
sizeof(ctx->id_auth->id_key_sig));
|
||||
memcpy(ctx->id_auth->id_key_sig, &igvm_id->author_key_signature,
|
||||
sizeof(igvm_id->author_key_signature));
|
||||
|
||||
/*
|
||||
* SEV and IGVM public key structure population are slightly different.
|
||||
* See SEV Secure Nested Paging Firmware ABI Specification, Chapter 10.
|
||||
*/
|
||||
*((uint32_t *)ctx->id_auth->id_key) = igvm_id->id_public_key.curve;
|
||||
memcpy(&ctx->id_auth->id_key[4], &igvm_id->id_public_key.qx, 72);
|
||||
memcpy(&ctx->id_auth->id_key[76], &igvm_id->id_public_key.qy, 72);
|
||||
|
||||
*((uint32_t *)ctx->id_auth->author_key) =
|
||||
igvm_id->author_public_key.curve;
|
||||
memcpy(&ctx->id_auth->author_key[4], &igvm_id->author_public_key.qx,
|
||||
72);
|
||||
memcpy(&ctx->id_auth->author_key[76], &igvm_id->author_public_key.qy,
|
||||
72);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qigvm_initialization_guest_policy(QIgvm *ctx,
|
||||
const uint8_t *header_data, Error **errp)
|
||||
{
|
||||
const IGVM_VHS_GUEST_POLICY *guest =
|
||||
(const IGVM_VHS_GUEST_POLICY *)header_data;
|
||||
|
||||
if (guest->compatibility_mask & ctx->compatibility_mask) {
|
||||
ctx->sev_policy = guest->policy;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qigvm_supported_platform_compat_mask(QIgvm *ctx, Error **errp)
|
||||
{
|
||||
int32_t header_count;
|
||||
unsigned header_index;
|
||||
IgvmHandle header_handle;
|
||||
IGVM_VHS_SUPPORTED_PLATFORM *platform;
|
||||
uint32_t compatibility_mask_sev = 0;
|
||||
uint32_t compatibility_mask_sev_es = 0;
|
||||
uint32_t compatibility_mask_sev_snp = 0;
|
||||
uint32_t compatibility_mask = 0;
|
||||
|
||||
header_count = igvm_header_count(ctx->file, IGVM_HEADER_SECTION_PLATFORM);
|
||||
if (header_count < 0) {
|
||||
error_setg(errp,
|
||||
"Invalid platform header count in IGVM file. Error code: %X",
|
||||
header_count);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (header_index = 0; header_index < (unsigned)header_count;
|
||||
header_index++) {
|
||||
IgvmVariableHeaderType typ = igvm_get_header_type(
|
||||
ctx->file, IGVM_HEADER_SECTION_PLATFORM, header_index);
|
||||
if (typ == IGVM_VHT_SUPPORTED_PLATFORM) {
|
||||
header_handle = igvm_get_header(
|
||||
ctx->file, IGVM_HEADER_SECTION_PLATFORM, header_index);
|
||||
if (header_handle < 0) {
|
||||
error_setg(errp,
|
||||
"Invalid platform header in IGVM file. "
|
||||
"Index: %d, Error code: %X",
|
||||
header_index, header_handle);
|
||||
return -1;
|
||||
}
|
||||
platform =
|
||||
(IGVM_VHS_SUPPORTED_PLATFORM *)(igvm_get_buffer(ctx->file,
|
||||
header_handle) +
|
||||
sizeof(
|
||||
IGVM_VHS_VARIABLE_HEADER));
|
||||
if ((platform->platform_type == IGVM_PLATFORM_TYPE_SEV_ES) &&
|
||||
ctx->cgs) {
|
||||
if (ctx->cgsc->check_support(
|
||||
CGS_PLATFORM_SEV_ES, platform->platform_version,
|
||||
platform->highest_vtl, platform->shared_gpa_boundary)) {
|
||||
compatibility_mask_sev_es = platform->compatibility_mask;
|
||||
}
|
||||
} else if ((platform->platform_type == IGVM_PLATFORM_TYPE_SEV) &&
|
||||
ctx->cgs) {
|
||||
if (ctx->cgsc->check_support(
|
||||
CGS_PLATFORM_SEV, platform->platform_version,
|
||||
platform->highest_vtl, platform->shared_gpa_boundary)) {
|
||||
compatibility_mask_sev = platform->compatibility_mask;
|
||||
}
|
||||
} else if ((platform->platform_type ==
|
||||
IGVM_PLATFORM_TYPE_SEV_SNP) &&
|
||||
ctx->cgs) {
|
||||
if (ctx->cgsc->check_support(
|
||||
CGS_PLATFORM_SEV_SNP, platform->platform_version,
|
||||
platform->highest_vtl, platform->shared_gpa_boundary)) {
|
||||
compatibility_mask_sev_snp = platform->compatibility_mask;
|
||||
}
|
||||
} else if (platform->platform_type == IGVM_PLATFORM_TYPE_NATIVE) {
|
||||
compatibility_mask = platform->compatibility_mask;
|
||||
}
|
||||
igvm_free_buffer(ctx->file, header_handle);
|
||||
}
|
||||
}
|
||||
/* Choose the strongest supported isolation technology */
|
||||
if (compatibility_mask_sev_snp != 0) {
|
||||
ctx->compatibility_mask = compatibility_mask_sev_snp;
|
||||
ctx->platform_type = IGVM_PLATFORM_TYPE_SEV_SNP;
|
||||
} else if (compatibility_mask_sev_es != 0) {
|
||||
ctx->compatibility_mask = compatibility_mask_sev_es;
|
||||
ctx->platform_type = IGVM_PLATFORM_TYPE_SEV_ES;
|
||||
} else if (compatibility_mask_sev != 0) {
|
||||
ctx->compatibility_mask = compatibility_mask_sev;
|
||||
ctx->platform_type = IGVM_PLATFORM_TYPE_SEV;
|
||||
} else if (compatibility_mask != 0) {
|
||||
ctx->compatibility_mask = compatibility_mask;
|
||||
ctx->platform_type = IGVM_PLATFORM_TYPE_NATIVE;
|
||||
} else {
|
||||
error_setg(
|
||||
errp,
|
||||
"IGVM file does not describe a compatible supported platform");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qigvm_handle_policy(QIgvm *ctx, Error **errp)
|
||||
{
|
||||
if (ctx->platform_type == IGVM_PLATFORM_TYPE_SEV_SNP) {
|
||||
int id_block_len = 0;
|
||||
int id_auth_len = 0;
|
||||
if (ctx->id_block) {
|
||||
ctx->id_block->policy = ctx->sev_policy;
|
||||
id_block_len = sizeof(struct sev_id_block);
|
||||
id_auth_len = sizeof(struct sev_id_authentication);
|
||||
}
|
||||
return ctx->cgsc->set_guest_policy(GUEST_POLICY_SEV, ctx->sev_policy,
|
||||
ctx->id_block, id_block_len,
|
||||
ctx->id_auth, id_auth_len, errp);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static IgvmHandle qigvm_file_init(char *filename, Error **errp)
|
||||
{
|
||||
IgvmHandle igvm;
|
||||
g_autofree uint8_t *buf = NULL;
|
||||
unsigned long len;
|
||||
g_autoptr(GError) gerr = NULL;
|
||||
|
||||
if (!g_file_get_contents(filename, (gchar **)&buf, &len, &gerr)) {
|
||||
error_setg(errp, "Unable to load %s: %s", filename, gerr->message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
igvm = igvm_new_from_binary(buf, len);
|
||||
if (igvm < 0) {
|
||||
error_setg(errp, "Unable to parse IGVM file %s: %d", filename, igvm);
|
||||
return -1;
|
||||
}
|
||||
return igvm;
|
||||
}
|
||||
|
||||
int qigvm_process_file(IgvmCfg *cfg, ConfidentialGuestSupport *cgs,
|
||||
bool onlyVpContext, Error **errp)
|
||||
{
|
||||
int32_t header_count;
|
||||
QIgvmParameterData *parameter;
|
||||
int retval = -1;
|
||||
QIgvm ctx;
|
||||
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
ctx.file = qigvm_file_init(cfg->filename, errp);
|
||||
if (ctx.file < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The ConfidentialGuestSupport object is optional and allows a confidential
|
||||
* guest platform to perform extra processing, such as page measurement, on
|
||||
* IGVM directives.
|
||||
*/
|
||||
ctx.cgs = cgs;
|
||||
ctx.cgsc = cgs ? CONFIDENTIAL_GUEST_SUPPORT_GET_CLASS(cgs) : NULL;
|
||||
|
||||
/*
|
||||
* Check that the IGVM file provides configuration for the current
|
||||
* platform
|
||||
*/
|
||||
if (qigvm_supported_platform_compat_mask(&ctx, errp) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
header_count = igvm_header_count(ctx.file, IGVM_HEADER_SECTION_DIRECTIVE);
|
||||
if (header_count <= 0) {
|
||||
error_setg(
|
||||
errp, "Invalid directive header count in IGVM file. Error code: %X",
|
||||
header_count);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
QTAILQ_INIT(&ctx.parameter_data);
|
||||
|
||||
for (ctx.current_header_index = 0;
|
||||
ctx.current_header_index < (unsigned)header_count;
|
||||
ctx.current_header_index++) {
|
||||
IgvmVariableHeaderType type = igvm_get_header_type(
|
||||
ctx.file, IGVM_HEADER_SECTION_DIRECTIVE, ctx.current_header_index);
|
||||
if (!onlyVpContext || (type == IGVM_VHT_VP_CONTEXT)) {
|
||||
if (qigvm_handler(&ctx, type, errp) < 0) {
|
||||
goto cleanup_parameters;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If only processing the VP context then we don't need to process
|
||||
* any more of the file.
|
||||
*/
|
||||
if (onlyVpContext) {
|
||||
retval = 0;
|
||||
goto cleanup_parameters;
|
||||
}
|
||||
|
||||
header_count =
|
||||
igvm_header_count(ctx.file, IGVM_HEADER_SECTION_INITIALIZATION);
|
||||
if (header_count < 0) {
|
||||
error_setg(
|
||||
errp,
|
||||
"Invalid initialization header count in IGVM file. Error code: %X",
|
||||
header_count);
|
||||
goto cleanup_parameters;
|
||||
}
|
||||
|
||||
for (ctx.current_header_index = 0;
|
||||
ctx.current_header_index < (unsigned)header_count;
|
||||
ctx.current_header_index++) {
|
||||
IgvmVariableHeaderType type =
|
||||
igvm_get_header_type(ctx.file, IGVM_HEADER_SECTION_INITIALIZATION,
|
||||
ctx.current_header_index);
|
||||
if (qigvm_handler(&ctx, type, errp) < 0) {
|
||||
goto cleanup_parameters;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Contiguous pages of data with compatible flags are grouped together in
|
||||
* order to reduce the number of memory regions we create. Make sure the
|
||||
* last group is processed with this call.
|
||||
*/
|
||||
retval = qigvm_process_mem_page(&ctx, NULL, errp);
|
||||
|
||||
if (retval == 0) {
|
||||
retval = qigvm_handle_policy(&ctx, errp);
|
||||
}
|
||||
|
||||
cleanup_parameters:
|
||||
QTAILQ_FOREACH(parameter, &ctx.parameter_data, next)
|
||||
{
|
||||
g_free(parameter->data);
|
||||
parameter->data = NULL;
|
||||
}
|
||||
g_free(ctx.id_block);
|
||||
g_free(ctx.id_auth);
|
||||
|
||||
cleanup:
|
||||
igvm_free(ctx.file);
|
||||
|
||||
return retval;
|
||||
}
|
||||
22
backends/igvm.h
Normal file
22
backends/igvm.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* QEMU IGVM configuration backend for Confidential Guests
|
||||
*
|
||||
* Copyright (C) 2023-2024 SUSE
|
||||
*
|
||||
* Authors:
|
||||
* Roy Hopkins <roy.hopkins@randomman.co.uk>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_IGVM_H
|
||||
#define BACKENDS_IGVM_H
|
||||
|
||||
#include "system/confidential-guest-support.h"
|
||||
#include "system/igvm-cfg.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
int qigvm_process_file(IgvmCfg *igvm, ConfidentialGuestSupport *cgs,
|
||||
bool onlyVpContext, Error **errp);
|
||||
|
||||
#endif
|
||||
|
|
@ -16,12 +16,18 @@
|
|||
#include "qemu/module.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "migration/cpr.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "trace.h"
|
||||
#include "hw/vfio/vfio-common.h"
|
||||
#include "hw/vfio/vfio-device.h"
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/iommufd.h>
|
||||
|
||||
static const char *iommufd_fd_name(IOMMUFDBackend *be)
|
||||
{
|
||||
return object_get_canonical_path_component(OBJECT(be));
|
||||
}
|
||||
|
||||
static void iommufd_backend_init(Object *obj)
|
||||
{
|
||||
IOMMUFDBackend *be = IOMMUFD_BACKEND(obj);
|
||||
|
|
@ -64,26 +70,73 @@ static bool iommufd_backend_can_be_deleted(UserCreatable *uc)
|
|||
return !be->users;
|
||||
}
|
||||
|
||||
static void iommufd_backend_class_init(ObjectClass *oc, void *data)
|
||||
static void iommufd_backend_complete(UserCreatable *uc, Error **errp)
|
||||
{
|
||||
IOMMUFDBackend *be = IOMMUFD_BACKEND(uc);
|
||||
const char *name = iommufd_fd_name(be);
|
||||
|
||||
if (!be->owned) {
|
||||
/* fd came from the command line. Fetch updated value from cpr state. */
|
||||
if (cpr_is_incoming()) {
|
||||
be->fd = cpr_find_fd(name, 0);
|
||||
} else {
|
||||
cpr_save_fd(name, 0, be->fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void iommufd_backend_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
||||
|
||||
ucc->can_be_deleted = iommufd_backend_can_be_deleted;
|
||||
ucc->complete = iommufd_backend_complete;
|
||||
|
||||
object_class_property_add_str(oc, "fd", NULL, iommufd_backend_set_fd);
|
||||
}
|
||||
|
||||
bool iommufd_change_process_capable(IOMMUFDBackend *be)
|
||||
{
|
||||
struct iommu_ioas_change_process args = {.size = sizeof(args)};
|
||||
|
||||
/*
|
||||
* Call IOMMU_IOAS_CHANGE_PROCESS to verify it is a recognized ioctl.
|
||||
* This is a no-op if the process has not changed since DMA was mapped.
|
||||
*/
|
||||
return !ioctl(be->fd, IOMMU_IOAS_CHANGE_PROCESS, &args);
|
||||
}
|
||||
|
||||
bool iommufd_change_process(IOMMUFDBackend *be, Error **errp)
|
||||
{
|
||||
struct iommu_ioas_change_process args = {.size = sizeof(args)};
|
||||
bool ret = !ioctl(be->fd, IOMMU_IOAS_CHANGE_PROCESS, &args);
|
||||
|
||||
if (!ret) {
|
||||
error_setg_errno(errp, errno, "IOMMU_IOAS_CHANGE_PROCESS fd %d failed",
|
||||
be->fd);
|
||||
}
|
||||
trace_iommufd_change_process(be->fd, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool iommufd_backend_connect(IOMMUFDBackend *be, Error **errp)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (be->owned && !be->users) {
|
||||
fd = qemu_open("/dev/iommu", O_RDWR, errp);
|
||||
fd = cpr_open_fd("/dev/iommu", O_RDWR, iommufd_fd_name(be), 0, errp);
|
||||
if (fd < 0) {
|
||||
return false;
|
||||
}
|
||||
be->fd = fd;
|
||||
}
|
||||
if (!be->users && !vfio_iommufd_cpr_register_iommufd(be, errp)) {
|
||||
if (be->owned) {
|
||||
close(be->fd);
|
||||
be->fd = -1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
be->users++;
|
||||
|
||||
trace_iommufd_backend_connect(be->fd, be->owned, be->users);
|
||||
|
|
@ -96,9 +149,13 @@ void iommufd_backend_disconnect(IOMMUFDBackend *be)
|
|||
goto out;
|
||||
}
|
||||
be->users--;
|
||||
if (!be->users && be->owned) {
|
||||
close(be->fd);
|
||||
be->fd = -1;
|
||||
if (!be->users) {
|
||||
vfio_iommufd_cpr_unregister_iommufd(be);
|
||||
if (be->owned) {
|
||||
cpr_delete_fd(iommufd_fd_name(be), 0);
|
||||
close(be->fd);
|
||||
be->fd = -1;
|
||||
}
|
||||
}
|
||||
out:
|
||||
trace_iommufd_backend_disconnect(be->fd, be->users);
|
||||
|
|
@ -172,6 +229,44 @@ int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int iommufd_backend_map_file_dma(IOMMUFDBackend *be, uint32_t ioas_id,
|
||||
hwaddr iova, ram_addr_t size,
|
||||
int mfd, unsigned long start, bool readonly)
|
||||
{
|
||||
int ret, fd = be->fd;
|
||||
struct iommu_ioas_map_file map = {
|
||||
.size = sizeof(map),
|
||||
.flags = IOMMU_IOAS_MAP_READABLE |
|
||||
IOMMU_IOAS_MAP_FIXED_IOVA,
|
||||
.ioas_id = ioas_id,
|
||||
.fd = mfd,
|
||||
.start = start,
|
||||
.iova = iova,
|
||||
.length = size,
|
||||
};
|
||||
|
||||
if (cpr_is_incoming()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!readonly) {
|
||||
map.flags |= IOMMU_IOAS_MAP_WRITEABLE;
|
||||
}
|
||||
|
||||
ret = ioctl(fd, IOMMU_IOAS_MAP_FILE, &map);
|
||||
trace_iommufd_backend_map_file_dma(fd, ioas_id, iova, size, mfd, start,
|
||||
readonly, ret);
|
||||
if (ret) {
|
||||
ret = -errno;
|
||||
|
||||
/* TODO: Not support mapping hardware PCI BAR region for now. */
|
||||
if (errno == EFAULT) {
|
||||
warn_report("IOMMU_IOAS_MAP_FILE failed: %m, PCI BAR?");
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id,
|
||||
hwaddr iova, ram_addr_t size)
|
||||
{
|
||||
|
|
@ -183,6 +278,10 @@ int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id,
|
|||
.length = size,
|
||||
};
|
||||
|
||||
if (cpr_is_incoming()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = ioctl(fd, IOMMU_IOAS_UNMAP, &unmap);
|
||||
/*
|
||||
* IOMMUFD takes mapping as some kind of object, unmapping
|
||||
|
|
@ -311,6 +410,62 @@ bool iommufd_backend_get_device_info(IOMMUFDBackend *be, uint32_t devid,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool iommufd_backend_invalidate_cache(IOMMUFDBackend *be, uint32_t id,
|
||||
uint32_t data_type, uint32_t entry_len,
|
||||
uint32_t *entry_num, void *data,
|
||||
Error **errp)
|
||||
{
|
||||
int ret, fd = be->fd;
|
||||
uint32_t total_entries = *entry_num;
|
||||
struct iommu_hwpt_invalidate cache = {
|
||||
.size = sizeof(cache),
|
||||
.hwpt_id = id,
|
||||
.data_type = data_type,
|
||||
.entry_len = entry_len,
|
||||
.entry_num = total_entries,
|
||||
.data_uptr = (uintptr_t)data,
|
||||
};
|
||||
|
||||
ret = ioctl(fd, IOMMU_HWPT_INVALIDATE, &cache);
|
||||
trace_iommufd_backend_invalidate_cache(fd, id, data_type, entry_len,
|
||||
total_entries, cache.entry_num,
|
||||
(uintptr_t)data, ret ? errno : 0);
|
||||
*entry_num = cache.entry_num;
|
||||
|
||||
if (ret) {
|
||||
error_setg_errno(errp, errno, "IOMMU_HWPT_INVALIDATE failed:"
|
||||
" total %d entries, processed %d entries",
|
||||
total_entries, cache.entry_num);
|
||||
} else if (total_entries != cache.entry_num) {
|
||||
error_setg(errp, "IOMMU_HWPT_INVALIDATE succeed but with unprocessed"
|
||||
" entries: total %d entries, processed %d entries."
|
||||
" Kernel BUG?!", total_entries, cache.entry_num);
|
||||
return false;
|
||||
}
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
bool host_iommu_device_iommufd_attach_hwpt(HostIOMMUDeviceIOMMUFD *idev,
|
||||
uint32_t hwpt_id, Error **errp)
|
||||
{
|
||||
HostIOMMUDeviceIOMMUFDClass *idevc =
|
||||
HOST_IOMMU_DEVICE_IOMMUFD_GET_CLASS(idev);
|
||||
|
||||
g_assert(idevc->attach_hwpt);
|
||||
return idevc->attach_hwpt(idev, hwpt_id, errp);
|
||||
}
|
||||
|
||||
bool host_iommu_device_iommufd_detach_hwpt(HostIOMMUDeviceIOMMUFD *idev,
|
||||
Error **errp)
|
||||
{
|
||||
HostIOMMUDeviceIOMMUFDClass *idevc =
|
||||
HOST_IOMMU_DEVICE_IOMMUFD_GET_CLASS(idev);
|
||||
|
||||
g_assert(idevc->detach_hwpt);
|
||||
return idevc->detach_hwpt(idev, errp);
|
||||
}
|
||||
|
||||
static int hiod_iommufd_get_cap(HostIOMMUDevice *hiod, int cap, Error **errp)
|
||||
{
|
||||
HostIOMMUDeviceCaps *caps = &hiod->caps;
|
||||
|
|
@ -326,7 +481,7 @@ static int hiod_iommufd_get_cap(HostIOMMUDevice *hiod, int cap, Error **errp)
|
|||
}
|
||||
}
|
||||
|
||||
static void hiod_iommufd_class_init(ObjectClass *oc, void *data)
|
||||
static void hiod_iommufd_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
HostIOMMUDeviceClass *hioc = HOST_IOMMU_DEVICE_CLASS(oc);
|
||||
|
||||
|
|
@ -342,13 +497,15 @@ static const TypeInfo types[] = {
|
|||
.instance_finalize = iommufd_backend_finalize,
|
||||
.class_size = sizeof(IOMMUFDBackendClass),
|
||||
.class_init = iommufd_backend_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
.interfaces = (const InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ }
|
||||
}
|
||||
}, {
|
||||
.name = TYPE_HOST_IOMMU_DEVICE_IOMMUFD,
|
||||
.parent = TYPE_HOST_IOMMU_DEVICE,
|
||||
.instance_size = sizeof(HostIOMMUDeviceIOMMUFD),
|
||||
.class_size = sizeof(HostIOMMUDeviceIOMMUFDClass),
|
||||
.class_init = hiod_iommufd_class_init,
|
||||
.abstract = true,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,10 @@ system_ss.add([files(
|
|||
|
||||
if host_os != 'windows'
|
||||
system_ss.add(files('rng-random.c'))
|
||||
system_ss.add(files('hostmem-file.c'))
|
||||
system_ss.add([files('hostmem-shm.c'), rt])
|
||||
if host_os != 'emscripten'
|
||||
system_ss.add(files('hostmem-file.c'))
|
||||
system_ss.add([files('hostmem-shm.c'), rt])
|
||||
endif
|
||||
endif
|
||||
if host_os == 'linux'
|
||||
system_ss.add(files('hostmem-memfd.c'))
|
||||
|
|
@ -32,6 +34,11 @@ if have_vhost_user_crypto
|
|||
endif
|
||||
system_ss.add(when: gio, if_true: files('dbus-vmstate.c'))
|
||||
system_ss.add(when: 'CONFIG_SGX', if_true: files('hostmem-epc.c'))
|
||||
if igvm.found()
|
||||
system_ss.add(igvm)
|
||||
system_ss.add(files('igvm-cfg.c'), igvm)
|
||||
system_ss.add(files('igvm.c'), igvm)
|
||||
endif
|
||||
|
||||
system_ss.add(when: 'CONFIG_SPDM_SOCKET', if_true: files('spdm-socket.c'))
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ static void rng_builtin_finalize(Object *obj)
|
|||
qemu_bh_delete(s->bh);
|
||||
}
|
||||
|
||||
static void rng_builtin_class_init(ObjectClass *klass, void *data)
|
||||
static void rng_builtin_class_init(ObjectClass *klass, const void *data)
|
||||
{
|
||||
RngBackendClass *rbc = RNG_BACKEND_CLASS(klass);
|
||||
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ static void rng_egd_finalize(Object *obj)
|
|||
g_free(s->chr_name);
|
||||
}
|
||||
|
||||
static void rng_egd_class_init(ObjectClass *klass, void *data)
|
||||
static void rng_egd_class_init(ObjectClass *klass, const void *data)
|
||||
{
|
||||
RngBackendClass *rbc = RNG_BACKEND_CLASS(klass);
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ static void rng_random_finalize(Object *obj)
|
|||
g_free(s->filename);
|
||||
}
|
||||
|
||||
static void rng_random_class_init(ObjectClass *klass, void *data)
|
||||
static void rng_random_class_init(ObjectClass *klass, const void *data)
|
||||
{
|
||||
RngBackendClass *rbc = RNG_BACKEND_CLASS(klass);
|
||||
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ static void rng_backend_finalize(Object *obj)
|
|||
rng_backend_free_requests(s);
|
||||
}
|
||||
|
||||
static void rng_backend_class_init(ObjectClass *oc, void *data)
|
||||
static void rng_backend_class_init(ObjectClass *oc, const void *data)
|
||||
{
|
||||
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
||||
|
||||
|
|
@ -119,7 +119,7 @@ static const TypeInfo rng_backend_info = {
|
|||
.class_size = sizeof(RngBackendClass),
|
||||
.class_init = rng_backend_class_init,
|
||||
.abstract = true,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
.interfaces = (const InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,11 +129,11 @@ static int tpm_emulator_ctrlcmd(TPMEmulator *tpm, unsigned long cmd, void *msg,
|
|||
CharBackend *dev = &tpm->ctrl_chr;
|
||||
uint32_t cmd_no = cpu_to_be32(cmd);
|
||||
ssize_t n = sizeof(uint32_t) + msg_len_in;
|
||||
uint8_t *buf = NULL;
|
||||
ptm_res res;
|
||||
|
||||
WITH_QEMU_LOCK_GUARD(&tpm->mutex) {
|
||||
buf = g_alloca(n);
|
||||
g_autofree uint8_t *buf = g_malloc(n);
|
||||
|
||||
memcpy(buf, &cmd_no, sizeof(cmd_no));
|
||||
memcpy(buf + sizeof(cmd_no), msg, msg_len_in);
|
||||
|
||||
|
|
@ -1056,7 +1056,7 @@ static void tpm_emulator_inst_finalize(Object *obj)
|
|||
vmstate_unregister(NULL, &vmstate_tpm_emulator, obj);
|
||||
}
|
||||
|
||||
static void tpm_emulator_class_init(ObjectClass *klass, void *data)
|
||||
static void tpm_emulator_class_init(ObjectClass *klass, const void *data)
|
||||
{
|
||||
TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
|
||||
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@ static void tpm_passthrough_inst_finalize(Object *obj)
|
|||
qapi_free_TPMPassthroughOptions(tpm_pt->options);
|
||||
}
|
||||
|
||||
static void tpm_passthrough_class_init(ObjectClass *klass, void *data)
|
||||
static void tpm_passthrough_class_init(ObjectClass *klass, const void *data)
|
||||
{
|
||||
TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
#include "qapi/error.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "tpm_int.h"
|
||||
#include "exec/memory.h"
|
||||
#include "system/memory.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "system/tpm_backend.h"
|
||||
#include "system/tpm_util.h"
|
||||
|
|
|
|||
|
|
@ -7,10 +7,12 @@ dbus_vmstate_loading(const char *id) "id: %s"
|
|||
dbus_vmstate_saving(const char *id) "id: %s"
|
||||
|
||||
# iommufd.c
|
||||
iommufd_change_process(int fd, bool ret) "fd=%d (%d)"
|
||||
iommufd_backend_connect(int fd, bool owned, uint32_t users) "fd=%d owned=%d users=%d"
|
||||
iommufd_backend_disconnect(int fd, uint32_t users) "fd=%d users=%d"
|
||||
iommu_backend_set_fd(int fd) "pre-opened /dev/iommu fd=%d"
|
||||
iommufd_backend_map_dma(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, void *vaddr, bool readonly, int ret) " iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" addr=%p readonly=%d (%d)"
|
||||
iommufd_backend_map_file_dma(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, int fd, unsigned long start, bool readonly, int ret) " iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" fd=%d start=%ld readonly=%d (%d)"
|
||||
iommufd_backend_unmap_dma_non_exist(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, int ret) " Unmap nonexistent mapping: iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" (%d)"
|
||||
iommufd_backend_unmap_dma(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, int ret) " iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" (%d)"
|
||||
iommufd_backend_alloc_ioas(int iommufd, uint32_t ioas) " iommufd=%d ioas=%d"
|
||||
|
|
@ -18,3 +20,4 @@ iommufd_backend_alloc_hwpt(int iommufd, uint32_t dev_id, uint32_t pt_id, uint32_
|
|||
iommufd_backend_free_id(int iommufd, uint32_t id, int ret) " iommufd=%d id=%d (%d)"
|
||||
iommufd_backend_set_dirty(int iommufd, uint32_t hwpt_id, bool start, int ret) " iommufd=%d hwpt=%u enable=%d (%d)"
|
||||
iommufd_backend_get_dirty_bitmap(int iommufd, uint32_t hwpt_id, uint64_t iova, uint64_t size, uint64_t page_size, int ret) " iommufd=%d hwpt=%u iova=0x%"PRIx64" size=0x%"PRIx64" page_size=0x%"PRIx64" (%d)"
|
||||
iommufd_backend_invalidate_cache(int iommufd, uint32_t id, uint32_t data_type, uint32_t entry_len, uint32_t entry_num, uint32_t done_num, uint64_t data_ptr, int ret) " iommufd=%d id=%u data_type=%u entry_len=%u entry_num=%u done_num=%u data_ptr=0x%"PRIx64" (%d)"
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue