Fix conflicts
This commit is contained in:
commit
f3b17f95ac
55
.cirrus.yml
55
.cirrus.yml
@ -1,61 +1,6 @@
|
||||
env:
|
||||
CIRRUS_CLONE_DEPTH: 1
|
||||
|
||||
freebsd_12_task:
|
||||
freebsd_instance:
|
||||
image_family: freebsd-12-2
|
||||
cpu: 8
|
||||
memory: 8G
|
||||
install_script:
|
||||
- ASSUME_ALWAYS_YES=yes pkg bootstrap -f ;
|
||||
- pkg install -y bash curl cyrus-sasl git glib gmake gnutls gsed
|
||||
nettle perl5 pixman pkgconf png usbredir ninja
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
# TODO: Enable gnutls again once FreeBSD's libtasn1 got fixed
|
||||
# See: https://gitlab.com/gnutls/libtasn1/-/merge_requests/71
|
||||
- ../configure --enable-werror --disable-gnutls
|
||||
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
|
||||
- gmake -j$(sysctl -n hw.ncpu)
|
||||
- gmake -j$(sysctl -n hw.ncpu) check V=1
|
||||
|
||||
macos_task:
|
||||
osx_instance:
|
||||
image: catalina-base
|
||||
install_script:
|
||||
- brew install pkg-config python gnu-sed glib pixman make sdl2 bash ninja
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --python=/usr/local/bin/python3 --enable-werror
|
||||
--extra-cflags='-Wno-error=deprecated-declarations'
|
||||
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
|
||||
- gmake -j$(sysctl -n hw.ncpu)
|
||||
- gmake check-unit V=1
|
||||
- gmake check-block V=1
|
||||
- gmake check-qapi-schema V=1
|
||||
- gmake check-softfloat V=1
|
||||
- gmake check-qtest-x86_64 V=1
|
||||
|
||||
macos_xcode_task:
|
||||
osx_instance:
|
||||
# this is an alias for the latest Xcode
|
||||
image: catalina-xcode
|
||||
install_script:
|
||||
- brew install pkg-config gnu-sed glib pixman make sdl2 bash ninja
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --extra-cflags='-Wno-error=deprecated-declarations' --enable-modules
|
||||
--enable-werror --cc=clang || { cat config.log meson-logs/meson-log.txt; exit 1; }
|
||||
- gmake -j$(sysctl -n hw.ncpu)
|
||||
- gmake check-unit V=1
|
||||
- gmake check-block V=1
|
||||
- gmake check-qapi-schema V=1
|
||||
- gmake check-softfloat V=1
|
||||
- gmake check-qtest-x86_64 V=1
|
||||
|
||||
windows_msys2_task:
|
||||
timeout_in: 90m
|
||||
windows_container:
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -15,3 +15,5 @@ GTAGS
|
||||
*~
|
||||
*.ast_raw
|
||||
*.depend_raw
|
||||
*.swp
|
||||
*.patch
|
||||
|
@ -73,9 +73,9 @@
|
||||
# in its namespace setting or via git-push option, see documentation
|
||||
# in /.gitlab-ci.yml of this repository).
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project"'
|
||||
when: always
|
||||
when: on_success
|
||||
- if: '$QEMU_CI_AVOCADO_TESTING'
|
||||
when: always
|
||||
when: on_success
|
||||
# Otherwise, set to manual (the jobs are created but not run).
|
||||
- when: manual
|
||||
allow_failure: true
|
||||
|
@ -305,10 +305,10 @@ build-tcg-disabled:
|
||||
- cd tests/qemu-iotests/
|
||||
- ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048
|
||||
052 063 077 086 101 104 106 113 148 150 151 152 157 159 160 163
|
||||
170 171 183 184 192 194 197 208 215 221 222 226 227 236 253 277
|
||||
170 171 183 184 192 194 208 221 222 226 227 236 253 277
|
||||
- ./check -qcow2 028 051 056 057 058 065 068 082 085 091 095 096 102 122
|
||||
124 132 139 142 144 145 151 152 155 157 165 194 196 197 200 202
|
||||
208 209 215 216 218 222 227 234 246 247 248 250 254 255 257 258
|
||||
124 132 139 142 144 145 151 152 155 157 165 194 196 200 202
|
||||
208 209 216 218 222 227 234 246 247 248 250 254 255 257 258
|
||||
260 261 262 263 264 270 272 273 277 279
|
||||
|
||||
build-user:
|
||||
@ -354,27 +354,15 @@ build-some-softmmu:
|
||||
TARGETS: xtensa-softmmu arm-softmmu aarch64-softmmu alpha-softmmu
|
||||
MAKE_CHECK_ARGS: check-tcg
|
||||
|
||||
# Run check-tcg against linux-user (with plugins)
|
||||
# we skip sparc64-linux-user until it has been fixed somewhat
|
||||
# we skip cris-linux-user as it doesn't use the common run loop
|
||||
build-user-plugins:
|
||||
# We build tricore in a very minimal tricore only container
|
||||
build-tricore-softmmu:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-debian-user-cross-container
|
||||
job: tricore-debian-cross-container
|
||||
variables:
|
||||
IMAGE: debian-all-test-cross
|
||||
CONFIGURE_ARGS: --disable-tools --disable-system --enable-plugins --enable-debug-tcg --target-list-exclude=sparc64-linux-user,cris-linux-user
|
||||
MAKE_CHECK_ARGS: check-tcg
|
||||
timeout: 1h 30m
|
||||
|
||||
build-some-softmmu-plugins:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-debian-user-cross-container
|
||||
variables:
|
||||
IMAGE: debian-all-test-cross
|
||||
CONFIGURE_ARGS: --disable-tools --disable-user --enable-plugins --enable-debug-tcg
|
||||
TARGETS: xtensa-softmmu arm-softmmu aarch64-softmmu alpha-softmmu
|
||||
IMAGE: debian-tricore-cross
|
||||
CONFIGURE_ARGS: --disable-tools --disable-fdt --enable-debug
|
||||
TARGETS: tricore-softmmu
|
||||
MAKE_CHECK_ARGS: check-tcg
|
||||
|
||||
clang-system:
|
||||
@ -428,6 +416,12 @@ build-cfi-aarch64:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
rules:
|
||||
# FIXME: This job is often failing, likely due to out-of-memory problems in
|
||||
# the constrained containers of the shared runners. Thus this is marked as
|
||||
# manual until the situation has been solved.
|
||||
- when: manual
|
||||
allow_failure: true
|
||||
|
||||
check-cfi-aarch64:
|
||||
extends: .native_test_job_template
|
||||
@ -464,6 +458,12 @@ build-cfi-ppc64-s390x:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
rules:
|
||||
# FIXME: This job is often failing, likely due to out-of-memory problems in
|
||||
# the constrained containers of the shared runners. Thus this is marked as
|
||||
# manual until the situation has been solved.
|
||||
- when: manual
|
||||
allow_failure: true
|
||||
|
||||
check-cfi-ppc64-s390x:
|
||||
extends: .native_test_job_template
|
||||
@ -686,6 +686,17 @@ build-tools-and-docs-debian:
|
||||
|
||||
# Prepare for GitLab pages deployment. Anything copied into the
|
||||
# "public" directory will be deployed to $USER.gitlab.io/$PROJECT
|
||||
#
|
||||
# GitLab publishes from any branch that triggers a CI pipeline
|
||||
#
|
||||
# For the main repo we don't want to publish from 'staging'
|
||||
# since that content may not be pushed, nor do we wish to
|
||||
# publish from 'stable-NNN' branches as that content is outdated.
|
||||
# Thus we restrict to just the default branch
|
||||
#
|
||||
# For contributor forks we want to publish from any repo so
|
||||
# that users can see the results of their commits, regardless
|
||||
# of what topic branch they're currently using
|
||||
pages:
|
||||
image: $CI_REGISTRY_IMAGE/qemu/debian-amd64:latest
|
||||
stage: test
|
||||
@ -704,3 +715,10 @@ pages:
|
||||
artifacts:
|
||||
paths:
|
||||
- public
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
||||
when: on_success
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project"'
|
||||
when: never
|
||||
- if: '$CI_PROJECT_NAMESPACE != "qemu-project"'
|
||||
when: on_success
|
||||
|
87
.gitlab-ci.d/cirrus.yml
Normal file
87
.gitlab-ci.d/cirrus.yml
Normal file
@ -0,0 +1,87 @@
|
||||
# Jobs that we delegate to Cirrus CI because they require an operating
|
||||
# system other than Linux. These jobs will only run if the required
|
||||
# setup has been performed on the GitLab account.
|
||||
#
|
||||
# The Cirrus CI configuration is generated by replacing target-specific
|
||||
# variables in a generic template: some of these variables are provided
|
||||
# when the GitLab CI job is defined, others are taken from a shell
|
||||
# snippet generated using lcitool.
|
||||
#
|
||||
# Note that the $PATH environment variable has to be treated with
|
||||
# special care, because we can't just override it at the GitLab CI job
|
||||
# definition level or we risk breaking it completely.
|
||||
.cirrus_build_job:
|
||||
stage: build
|
||||
image: registry.gitlab.com/libvirt/libvirt-ci/cirrus-run:master
|
||||
needs: []
|
||||
allow_failure: true
|
||||
script:
|
||||
- source .gitlab-ci.d/cirrus/$NAME.vars
|
||||
- sed -e "s|[@]CI_REPOSITORY_URL@|$CI_REPOSITORY_URL|g"
|
||||
-e "s|[@]CI_COMMIT_REF_NAME@|$CI_COMMIT_REF_NAME|g"
|
||||
-e "s|[@]CI_COMMIT_SHA@|$CI_COMMIT_SHA|g"
|
||||
-e "s|[@]CIRRUS_VM_INSTANCE_TYPE@|$CIRRUS_VM_INSTANCE_TYPE|g"
|
||||
-e "s|[@]CIRRUS_VM_IMAGE_SELECTOR@|$CIRRUS_VM_IMAGE_SELECTOR|g"
|
||||
-e "s|[@]CIRRUS_VM_IMAGE_NAME@|$CIRRUS_VM_IMAGE_NAME|g"
|
||||
-e "s|[@]CIRRUS_VM_CPUS@|$CIRRUS_VM_CPUS|g"
|
||||
-e "s|[@]CIRRUS_VM_RAM@|$CIRRUS_VM_RAM|g"
|
||||
-e "s|[@]UPDATE_COMMAND@|$UPDATE_COMMAND|g"
|
||||
-e "s|[@]INSTALL_COMMAND@|$INSTALL_COMMAND|g"
|
||||
-e "s|[@]PATH@|$PATH_EXTRA${PATH_EXTRA:+:}\$PATH|g"
|
||||
-e "s|[@]PKG_CONFIG_PATH@|$PKG_CONFIG_PATH|g"
|
||||
-e "s|[@]PKGS@|$PKGS|g"
|
||||
-e "s|[@]MAKE@|$MAKE|g"
|
||||
-e "s|[@]PYTHON@|$PYTHON|g"
|
||||
-e "s|[@]PIP3@|$PIP3|g"
|
||||
-e "s|[@]PYPI_PKGS@|$PYPI_PKGS|g"
|
||||
-e "s|[@]CONFIGURE_ARGS@|$CONFIGURE_ARGS|g"
|
||||
-e "s|[@]TEST_TARGETSS@|$TEST_TARGETSS|g"
|
||||
<.gitlab-ci.d/cirrus/build.yml >.gitlab-ci.d/cirrus/$NAME.yml
|
||||
- cat .gitlab-ci.d/cirrus/$NAME.yml
|
||||
- cirrus-run -v --show-build-log always .gitlab-ci.d/cirrus/$NAME.yml
|
||||
rules:
|
||||
- if: "$CIRRUS_GITHUB_REPO && $CIRRUS_API_TOKEN"
|
||||
|
||||
x64-freebsd-12-build:
|
||||
extends: .cirrus_build_job
|
||||
variables:
|
||||
NAME: freebsd-12
|
||||
CIRRUS_VM_INSTANCE_TYPE: freebsd_instance
|
||||
CIRRUS_VM_IMAGE_SELECTOR: image_family
|
||||
CIRRUS_VM_IMAGE_NAME: freebsd-12-2
|
||||
CIRRUS_VM_CPUS: 8
|
||||
CIRRUS_VM_RAM: 8G
|
||||
UPDATE_COMMAND: pkg update
|
||||
INSTALL_COMMAND: pkg install -y
|
||||
# TODO: Enable gnutls again once FreeBSD's libtasn1 got fixed
|
||||
# See: https://gitlab.com/gnutls/libtasn1/-/merge_requests/71
|
||||
CONFIGURE_ARGS: --disable-gnutls
|
||||
TEST_TARGETS: check
|
||||
|
||||
x64-freebsd-13-build:
|
||||
extends: .cirrus_build_job
|
||||
variables:
|
||||
NAME: freebsd-13
|
||||
CIRRUS_VM_INSTANCE_TYPE: freebsd_instance
|
||||
CIRRUS_VM_IMAGE_SELECTOR: image_family
|
||||
CIRRUS_VM_IMAGE_NAME: freebsd-13-0
|
||||
CIRRUS_VM_CPUS: 8
|
||||
CIRRUS_VM_RAM: 8G
|
||||
UPDATE_COMMAND: pkg update
|
||||
INSTALL_COMMAND: pkg install -y
|
||||
TEST_TARGETS: check
|
||||
|
||||
x64-macos-11-base-build:
|
||||
extends: .cirrus_build_job
|
||||
variables:
|
||||
NAME: macos-11
|
||||
CIRRUS_VM_INSTANCE_TYPE: osx_instance
|
||||
CIRRUS_VM_IMAGE_SELECTOR: image
|
||||
CIRRUS_VM_IMAGE_NAME: big-sur-base
|
||||
CIRRUS_VM_CPUS: 12
|
||||
CIRRUS_VM_RAM: 24G
|
||||
UPDATE_COMMAND: brew update
|
||||
INSTALL_COMMAND: brew install
|
||||
PATH_EXTRA: /usr/local/opt/ccache/libexec:/usr/local/opt/gettext/bin
|
||||
PKG_CONFIG_PATH: /usr/local/opt/curl/lib/pkgconfig:/usr/local/opt/ncurses/lib/pkgconfig:/usr/local/opt/readline/lib/pkgconfig
|
||||
TEST_TARGETS: check-unit check-block check-qapi-schema check-softfloat check-qtest-x86_64
|
54
.gitlab-ci.d/cirrus/README.rst
Normal file
54
.gitlab-ci.d/cirrus/README.rst
Normal file
@ -0,0 +1,54 @@
|
||||
Cirrus CI integration
|
||||
=====================
|
||||
|
||||
GitLab CI shared runners only provide a docker environment running on Linux.
|
||||
While it is possible to provide private runners for non-Linux platforms this
|
||||
is not something most contributors/maintainers will wish to do.
|
||||
|
||||
To work around this limitation, we take advantage of `Cirrus CI`_'s free
|
||||
offering: more specifically, we use the `cirrus-run`_ script to trigger Cirrus
|
||||
CI jobs from GitLab CI jobs so that Cirrus CI job output is integrated into
|
||||
the main GitLab CI pipeline dashboard.
|
||||
|
||||
There is, however, some one-time setup required. If you want FreeBSD and macOS
|
||||
builds to happen when you push to your GitLab repository, you need to
|
||||
|
||||
* set up a GitHub repository for the project, eg. ``yourusername/qemu``.
|
||||
This repository needs to exist for cirrus-run to work, but it doesn't need to
|
||||
be kept up to date, so you can create it and then forget about it;
|
||||
|
||||
* enable the `Cirrus CI GitHub app`_ for your GitHub account;
|
||||
|
||||
* sign up for Cirrus CI. It's enough to log into the website using your GitHub
|
||||
account;
|
||||
|
||||
* grab an API token from the `Cirrus CI settings`_ page;
|
||||
|
||||
* it may be necessary to push an empty ``.cirrus.yml`` file to your github fork
|
||||
for Cirrus CI to properly recognize the project. You can check whether
|
||||
Cirrus CI knows about your project by navigating to:
|
||||
|
||||
``https://cirrus-ci.com/yourusername/qemu``
|
||||
|
||||
* in the *CI/CD / Variables* section of the settings page for your GitLab
|
||||
repository, create two new variables:
|
||||
|
||||
* ``CIRRUS_GITHUB_REPO``, containing the name of the GitHub repository
|
||||
created earlier, eg. ``yourusername/qemu``;
|
||||
|
||||
* ``CIRRUS_API_TOKEN``, containing the Cirrus CI API token generated earlier.
|
||||
This variable **must** be marked as *Masked*, because anyone with knowledge
|
||||
of it can impersonate you as far as Cirrus CI is concerned.
|
||||
|
||||
Neither of these variables should be marked as *Protected*, because in
|
||||
general you'll want to be able to trigger Cirrus CI builds from non-protected
|
||||
branches.
|
||||
|
||||
Once this one-time setup is complete, you can just keep pushing to your GitLab
|
||||
repository as usual and you'll automatically get the additional CI coverage.
|
||||
|
||||
|
||||
.. _Cirrus CI GitHub app: https://github.com/marketplace/cirrus-ci
|
||||
.. _Cirrus CI settings: https://cirrus-ci.com/settings/profile/
|
||||
.. _Cirrus CI: https://cirrus-ci.com/
|
||||
.. _cirrus-run: https://github.com/sio/cirrus-run/
|
35
.gitlab-ci.d/cirrus/build.yml
Normal file
35
.gitlab-ci.d/cirrus/build.yml
Normal file
@ -0,0 +1,35 @@
|
||||
@CIRRUS_VM_INSTANCE_TYPE@:
|
||||
@CIRRUS_VM_IMAGE_SELECTOR@: @CIRRUS_VM_IMAGE_NAME@
|
||||
cpu: @CIRRUS_VM_CPUS@
|
||||
memory: @CIRRUS_VM_RAM@
|
||||
|
||||
env:
|
||||
CIRRUS_CLONE_DEPTH: 1
|
||||
CI_REPOSITORY_URL: "@CI_REPOSITORY_URL@"
|
||||
CI_COMMIT_REF_NAME: "@CI_COMMIT_REF_NAME@"
|
||||
CI_COMMIT_SHA: "@CI_COMMIT_SHA@"
|
||||
PATH: "@PATH@"
|
||||
PKG_CONFIG_PATH: "@PKG_CONFIG_PATH@"
|
||||
PYTHON: "@PYTHON@"
|
||||
MAKE: "@MAKE@"
|
||||
CONFIGURE_ARGS: "@CONFIGURE_ARGS@"
|
||||
|
||||
build_task:
|
||||
install_script:
|
||||
- @UPDATE_COMMAND@
|
||||
- @INSTALL_COMMAND@ @PKGS@
|
||||
- if test -n "@PYPI_PKGS@" ; then @PIP3@ install @PYPI_PKGS@ ; fi
|
||||
clone_script:
|
||||
- git clone --depth 100 "$CI_REPOSITORY_URL" .
|
||||
- git fetch origin "$CI_COMMIT_REF_NAME"
|
||||
- git reset --hard "$CI_COMMIT_SHA"
|
||||
build_script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --enable-werror $CONFIGURE_ARGS
|
||||
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
|
||||
- $MAKE -j$(sysctl -n hw.ncpu)
|
||||
- for TARGET in $TEST_TARGETS ;
|
||||
do
|
||||
$MAKE -j$(sysctl -n hw.ncpu) $TARGET V=1 ;
|
||||
done
|
13
.gitlab-ci.d/cirrus/freebsd-12.vars
Normal file
13
.gitlab-ci.d/cirrus/freebsd-12.vars
Normal file
@ -0,0 +1,13 @@
|
||||
# THIS FILE WAS AUTO-GENERATED
|
||||
#
|
||||
# $ lcitool variables freebsd-12 qemu
|
||||
#
|
||||
# https://gitlab.com/libvirt/libvirt-ci/-/commit/c7e275ab27ac0dcd09da290817b9adeea1fd1eb1
|
||||
|
||||
PACKAGING_COMMAND='pkg'
|
||||
CCACHE='/usr/local/bin/ccache'
|
||||
MAKE='/usr/local/bin/gmake'
|
||||
NINJA='/usr/local/bin/ninja'
|
||||
PYTHON='/usr/local/bin/python3'
|
||||
PIP3='/usr/local/bin/pip-3.8'
|
||||
PKGS='alsa-lib bash bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage ctags curl cyrus-sasl dbus diffutils gettext git glib gmake gnutls gsed gtk3 libepoxy libffi libgcrypt libjpeg-turbo libnfs libspice-server libssh libtasn1 libxml2 llvm lttng-ust lzo2 meson ncurses nettle ninja opencv p5-Test-Harness perl5 pixman pkgconf png py38-numpy py38-pillow py38-pip py38-sphinx py38-sphinx_rtd_theme py38-virtualenv py38-yaml python3 rpm2cpio sdl2 sdl2_image snappy spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd'
|
13
.gitlab-ci.d/cirrus/freebsd-13.vars
Normal file
13
.gitlab-ci.d/cirrus/freebsd-13.vars
Normal file
@ -0,0 +1,13 @@
|
||||
# THIS FILE WAS AUTO-GENERATED
|
||||
#
|
||||
# $ lcitool variables freebsd-13 qemu
|
||||
#
|
||||
# https://gitlab.com/libvirt/libvirt-ci/-/commit/c7e275ab27ac0dcd09da290817b9adeea1fd1eb1
|
||||
|
||||
PACKAGING_COMMAND='pkg'
|
||||
CCACHE='/usr/local/bin/ccache'
|
||||
MAKE='/usr/local/bin/gmake'
|
||||
NINJA='/usr/local/bin/ninja'
|
||||
PYTHON='/usr/local/bin/python3'
|
||||
PIP3='/usr/local/bin/pip-3.8'
|
||||
PKGS='alsa-lib bash bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage ctags curl cyrus-sasl dbus diffutils gettext git glib gmake gnutls gsed gtk3 libepoxy libffi libgcrypt libjpeg-turbo libnfs libspice-server libssh libtasn1 libxml2 llvm lttng-ust lzo2 meson ncurses nettle ninja opencv p5-Test-Harness perl5 pixman pkgconf png py38-numpy py38-pillow py38-pip py38-sphinx py38-sphinx_rtd_theme py38-virtualenv py38-yaml python3 rpm2cpio sdl2 sdl2_image snappy spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd'
|
15
.gitlab-ci.d/cirrus/macos-11.vars
Normal file
15
.gitlab-ci.d/cirrus/macos-11.vars
Normal file
@ -0,0 +1,15 @@
|
||||
# THIS FILE WAS AUTO-GENERATED
|
||||
#
|
||||
# $ lcitool variables macos-11 qemu
|
||||
#
|
||||
# https://gitlab.com/libvirt/libvirt-ci/-/commit/c7e275ab27ac0dcd09da290817b9adeea1fd1eb1
|
||||
|
||||
PACKAGING_COMMAND='brew'
|
||||
CCACHE='/usr/local/bin/ccache'
|
||||
MAKE='/usr/local/bin/gmake'
|
||||
NINJA='/usr/local/bin/ninja'
|
||||
PYTHON='/usr/local/bin/python3'
|
||||
PIP3='/usr/local/bin/pip3'
|
||||
PKGS='bash bc bzip2 capstone ccache cpanminus ctags curl dbus diffutils gcovr gettext git glib gnu-sed gnutls gtk+3 jemalloc jpeg-turbo libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb libxml2 llvm lzo make meson ncurses nettle ninja perl pixman pkg-config python3 rpm2cpio sdl2 sdl2_image snappy sparse spice-protocol tesseract texinfo usbredir vde vte3 zlib zstd'
|
||||
PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme virtualenv'
|
||||
CPAN_PKGS='Test::Harness'
|
@ -9,8 +9,14 @@
|
||||
../configure --enable-werror --disable-docs $QEMU_CONFIGURE_OPTS
|
||||
--disable-user --target-list-exclude="arm-softmmu cris-softmmu
|
||||
i386-softmmu microblaze-softmmu mips-softmmu mipsel-softmmu
|
||||
mips64-softmmu ppc-softmmu sh4-softmmu xtensa-softmmu"
|
||||
mips64-softmmu ppc-softmmu riscv32-softmmu sh4-softmmu
|
||||
sparc-softmmu xtensa-softmmu $CROSS_SKIP_TARGETS"
|
||||
- make -j$(expr $(nproc) + 1) all check-build $MAKE_CHECK_ARGS
|
||||
- if grep -q "EXESUF=.exe" config-host.mak;
|
||||
then make installer;
|
||||
version="$(git describe --match v[0-9]*)";
|
||||
mv -v qemu-setup*.exe qemu-setup-${version}.exe;
|
||||
fi
|
||||
|
||||
# Job to cross-build specific accelerators.
|
||||
#
|
||||
|
@ -160,6 +160,11 @@ cross-win32-system:
|
||||
job: win32-fedora-cross-container
|
||||
variables:
|
||||
IMAGE: fedora-win32-cross
|
||||
CROSS_SKIP_TARGETS: alpha-softmmu avr-softmmu hppa-softmmu m68k-softmmu
|
||||
microblazeel-softmmu mips64el-softmmu nios2-softmmu
|
||||
artifacts:
|
||||
paths:
|
||||
- build/qemu-setup*.exe
|
||||
|
||||
cross-win64-system:
|
||||
extends: .cross_system_build_job
|
||||
@ -167,6 +172,11 @@ cross-win64-system:
|
||||
job: win64-fedora-cross-container
|
||||
variables:
|
||||
IMAGE: fedora-win64-cross
|
||||
CROSS_SKIP_TARGETS: or1k-softmmu rx-softmmu sh4eb-softmmu sparc64-softmmu
|
||||
tricore-softmmu xtensaeb-softmmu
|
||||
artifacts:
|
||||
paths:
|
||||
- build/qemu-setup*.exe
|
||||
|
||||
cross-amd64-xen-only:
|
||||
extends: .cross_accel_build_job
|
||||
|
238
.gitlab-ci.d/custom-runners.yml
Normal file
238
.gitlab-ci.d/custom-runners.yml
Normal file
@ -0,0 +1,238 @@
|
||||
# The CI jobs defined here require GitLab runners installed and
|
||||
# registered on machines that match their operating system names,
|
||||
# versions and architectures. This is in contrast to the other CI
|
||||
# jobs that are intended to run on GitLab's "shared" runners.
|
||||
|
||||
# Different than the default approach on "shared" runners, based on
|
||||
# containers, the custom runners have no such *requirement*, as those
|
||||
# jobs should be capable of running on operating systems with no
|
||||
# compatible container implementation, or no support from
|
||||
# gitlab-runner. To avoid problems that gitlab-runner can cause while
|
||||
# reusing the GIT repository, let's enable the clone strategy, which
|
||||
# guarantees a fresh repository on each job run.
|
||||
variables:
|
||||
GIT_STRATEGY: clone
|
||||
|
||||
# All ubuntu-18.04 jobs should run successfully in an environment
|
||||
# setup by the scripts/ci/setup/build-environment.yml task
|
||||
# "Install basic packages to build QEMU on Ubuntu 18.04/20.04"
|
||||
ubuntu-18.04-s390x-all-linux-static:
|
||||
allow_failure: true
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
- ubuntu_18.04
|
||||
- s390x
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
- if: "$S390X_RUNNER_AVAILABLE"
|
||||
script:
|
||||
# --disable-libssh is needed because of https://bugs.launchpad.net/qemu/+bug/1838763
|
||||
# --disable-glusterfs is needed because there's no static version of those libs in distro supplied packages
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --enable-debug --static --disable-system --disable-glusterfs --disable-libssh
|
||||
- make --output-sync -j`nproc`
|
||||
- make --output-sync -j`nproc` check V=1
|
||||
- make --output-sync -j`nproc` check-tcg V=1
|
||||
|
||||
ubuntu-18.04-s390x-all:
|
||||
allow_failure: true
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
- ubuntu_18.04
|
||||
- s390x
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
- if: "$S390X_RUNNER_AVAILABLE"
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --disable-libssh
|
||||
- make --output-sync -j`nproc`
|
||||
- make --output-sync -j`nproc` check V=1
|
||||
|
||||
ubuntu-18.04-s390x-alldbg:
|
||||
allow_failure: true
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
- ubuntu_18.04
|
||||
- s390x
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
- if: "$S390X_RUNNER_AVAILABLE"
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --enable-debug --disable-libssh
|
||||
- make clean
|
||||
- make --output-sync -j`nproc`
|
||||
- make --output-sync -j`nproc` check V=1
|
||||
|
||||
ubuntu-18.04-s390x-clang:
|
||||
allow_failure: true
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
- ubuntu_18.04
|
||||
- s390x
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
when: manual
|
||||
- if: "$S390X_RUNNER_AVAILABLE"
|
||||
when: manual
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --disable-libssh --cc=clang --cxx=clang++ --enable-sanitizers
|
||||
- make --output-sync -j`nproc`
|
||||
- make --output-sync -j`nproc` check V=1
|
||||
|
||||
ubuntu-18.04-s390x-tci:
|
||||
allow_failure: true
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
- ubuntu_18.04
|
||||
- s390x
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
- if: "$S390X_RUNNER_AVAILABLE"
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --disable-libssh --enable-tcg-interpreter
|
||||
- make --output-sync -j`nproc`
|
||||
|
||||
ubuntu-18.04-s390x-notcg:
|
||||
allow_failure: true
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
- ubuntu_18.04
|
||||
- s390x
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
when: manual
|
||||
- if: "$S390X_RUNNER_AVAILABLE"
|
||||
when: manual
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --disable-libssh --disable-tcg
|
||||
- make --output-sync -j`nproc`
|
||||
- make --output-sync -j`nproc` check V=1
|
||||
|
||||
# All ubuntu-20.04 jobs should run successfully in an environment
|
||||
# setup by the scripts/ci/setup/qemu/build-environment.yml task
|
||||
# "Install basic packages to build QEMU on Ubuntu 18.04/20.04"
|
||||
ubuntu-20.04-aarch64-all-linux-static:
|
||||
allow_failure: true
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
- ubuntu_20.04
|
||||
- aarch64
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
- if: "$S390X_RUNNER_AVAILABLE"
|
||||
script:
|
||||
# --disable-libssh is needed because of https://bugs.launchpad.net/qemu/+bug/1838763
|
||||
# --disable-glusterfs is needed because there's no static version of those libs in distro supplied packages
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --enable-debug --static --disable-system --disable-glusterfs --disable-libssh
|
||||
- make --output-sync -j`nproc`
|
||||
- make --output-sync -j`nproc` check V=1
|
||||
- make --output-sync -j`nproc` check-tcg V=1
|
||||
|
||||
ubuntu-20.04-aarch64-all:
|
||||
allow_failure: true
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
- ubuntu_20.04
|
||||
- aarch64
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
- if: "$S390X_RUNNER_AVAILABLE"
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --disable-libssh
|
||||
- make --output-sync -j`nproc`
|
||||
- make --output-sync -j`nproc` check V=1
|
||||
|
||||
ubuntu-20.04-aarch64-alldbg:
|
||||
allow_failure: true
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
- ubuntu_20.04
|
||||
- aarch64
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
- if: "$S390X_RUNNER_AVAILABLE"
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --enable-debug --disable-libssh
|
||||
- make clean
|
||||
- make --output-sync -j`nproc`
|
||||
- make --output-sync -j`nproc` check V=1
|
||||
|
||||
ubuntu-20.04-aarch64-clang:
|
||||
allow_failure: true
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
- ubuntu_20.04
|
||||
- aarch64
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
when: manual
|
||||
- if: "$S390X_RUNNER_AVAILABLE"
|
||||
when: manual
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --disable-libssh --cc=clang-10 --cxx=clang++-10 --enable-sanitizers
|
||||
- make --output-sync -j`nproc`
|
||||
- make --output-sync -j`nproc` check V=1
|
||||
|
||||
ubuntu-20.04-aarch64-tci:
|
||||
allow_failure: true
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
- ubuntu_20.04
|
||||
- aarch64
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
- if: "$S390X_RUNNER_AVAILABLE"
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --disable-libssh --enable-tcg-interpreter
|
||||
- make --output-sync -j`nproc`
|
||||
|
||||
ubuntu-20.04-aarch64-notcg:
|
||||
allow_failure: true
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
- ubuntu_20.04
|
||||
- aarch64
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
when: manual
|
||||
- if: "$S390X_RUNNER_AVAILABLE"
|
||||
when: manual
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --disable-libssh --disable-tcg
|
||||
- make --output-sync -j`nproc`
|
||||
- make --output-sync -j`nproc` check V=1
|
@ -1,10 +1,22 @@
|
||||
docker-edk2:
|
||||
stage: containers
|
||||
rules: # Only run this job when the Dockerfile is modified
|
||||
# All jobs needing docker-edk2 must use the same rules it uses.
|
||||
.edk2_job_rules:
|
||||
rules: # Only run this job when ...
|
||||
- changes:
|
||||
# this file is modified
|
||||
- .gitlab-ci.d/edk2.yml
|
||||
# or the Dockerfile is modified
|
||||
- .gitlab-ci.d/edk2/Dockerfile
|
||||
when: always
|
||||
# or roms/edk2/ is modified (submodule updated)
|
||||
- roms/edk2/*
|
||||
when: on_success
|
||||
- if: '$CI_COMMIT_REF_NAME =~ /^edk2/' # or the branch/tag starts with 'edk2'
|
||||
when: on_success
|
||||
- if: '$CI_COMMIT_MESSAGE =~ /edk2/i' # or last commit description contains 'EDK2'
|
||||
when: on_success
|
||||
|
||||
docker-edk2:
|
||||
extends: .edk2_job_rules
|
||||
stage: containers
|
||||
image: docker:19.03.1
|
||||
services:
|
||||
- docker:19.03.1-dind
|
||||
@ -24,16 +36,9 @@ docker-edk2:
|
||||
- docker push $IMAGE_TAG
|
||||
|
||||
build-edk2:
|
||||
extends: .edk2_job_rules
|
||||
stage: build
|
||||
needs: ['docker-edk2']
|
||||
rules: # Only run this job when ...
|
||||
- changes: # ... roms/edk2/ is modified (submodule updated)
|
||||
- roms/edk2/*
|
||||
when: always
|
||||
- if: '$CI_COMMIT_REF_NAME =~ /^edk2/' # or the branch/tag starts with 'edk2'
|
||||
when: always
|
||||
- if: '$CI_COMMIT_MESSAGE =~ /edk2/i' # or last commit description contains 'EDK2'
|
||||
when: always
|
||||
artifacts:
|
||||
paths: # 'artifacts.zip' will contains the following files:
|
||||
- pc-bios/edk2*bz2
|
||||
|
@ -1,10 +1,23 @@
|
||||
docker-opensbi:
|
||||
stage: containers
|
||||
rules: # Only run this job when the Dockerfile is modified
|
||||
# All jobs needing docker-opensbi must use the same rules it uses.
|
||||
.opensbi_job_rules:
|
||||
rules: # Only run this job when ...
|
||||
- changes:
|
||||
# this file is modified
|
||||
- .gitlab-ci.d/opensbi.yml
|
||||
# or the Dockerfile is modified
|
||||
- .gitlab-ci.d/opensbi/Dockerfile
|
||||
when: always
|
||||
when: on_success
|
||||
- changes: # or roms/opensbi/ is modified (submodule updated)
|
||||
- roms/opensbi/*
|
||||
when: on_success
|
||||
- if: '$CI_COMMIT_REF_NAME =~ /^opensbi/' # or the branch/tag starts with 'opensbi'
|
||||
when: on_success
|
||||
- if: '$CI_COMMIT_MESSAGE =~ /opensbi/i' # or last commit description contains 'OpenSBI'
|
||||
when: on_success
|
||||
|
||||
docker-opensbi:
|
||||
extends: .opensbi_job_rules
|
||||
stage: containers
|
||||
image: docker:19.03.1
|
||||
services:
|
||||
- docker:19.03.1-dind
|
||||
@ -24,16 +37,9 @@ docker-opensbi:
|
||||
- docker push $IMAGE_TAG
|
||||
|
||||
build-opensbi:
|
||||
extends: .opensbi_job_rules
|
||||
stage: build
|
||||
needs: ['docker-opensbi']
|
||||
rules: # Only run this job when ...
|
||||
- changes: # ... roms/opensbi/ is modified (submodule updated)
|
||||
- roms/opensbi/*
|
||||
when: always
|
||||
- if: '$CI_COMMIT_REF_NAME =~ /^opensbi/' # or the branch/tag starts with 'opensbi'
|
||||
when: always
|
||||
- if: '$CI_COMMIT_MESSAGE =~ /opensbi/i' # or last commit description contains 'OpenSBI'
|
||||
when: always
|
||||
artifacts:
|
||||
paths: # 'artifacts.zip' will contains the following files:
|
||||
- pc-bios/opensbi-riscv32-generic-fw_dynamic.bin
|
||||
|
@ -9,3 +9,5 @@ include:
|
||||
- local: '/.gitlab-ci.d/crossbuilds.yml'
|
||||
- local: '/.gitlab-ci.d/buildtest.yml'
|
||||
- local: '/.gitlab-ci.d/static_checks.yml'
|
||||
- local: '/.gitlab-ci.d/custom-runners.yml'
|
||||
- local: '/.gitlab-ci.d/cirrus.yml'
|
||||
|
@ -43,6 +43,7 @@ check-python-tox:
|
||||
- make -C python check-tox
|
||||
variables:
|
||||
GIT_DEPTH: 1
|
||||
QEMU_TOX_EXTRA_ARGS: --skip-missing-interpreters=false
|
||||
needs:
|
||||
job: python-container
|
||||
allow_failure: true
|
||||
|
@ -16,24 +16,9 @@
|
||||
# QEMU CI jobs are based on templates. Some templates provide
|
||||
# user-configurable options, modifiable via configuration variables.
|
||||
#
|
||||
# These variables can be set globally in the user's CI namespace
|
||||
# setting:
|
||||
# https://docs.gitlab.com/ee/ci/variables/#create-a-custom-variable-in-the-ui
|
||||
# or set manually each time a branch/tag is pushed, as a git-push
|
||||
# command line argument:
|
||||
# https://docs.gitlab.com/ee/user/project/push_options.html#push-options-for-gitlab-cicd
|
||||
# See https://qemu-project.gitlab.io/qemu/devel/ci.html#custom-ci-cd-variables
|
||||
# for more information.
|
||||
#
|
||||
# Example setting the QEMU_CI_EXAMPLE_VAR variable:
|
||||
#
|
||||
# git push -o ci.variable="QEMU_CI_EXAMPLE_VAR=value" myrepo mybranch
|
||||
#
|
||||
# ----------------------------------------------------------------------
|
||||
#
|
||||
# List of environment variables that can be use to modify the set
|
||||
# of jobs selected:
|
||||
#
|
||||
# - QEMU_CI_AVOCADO_TESTING
|
||||
# If set, tests using the Avocado framework will be run
|
||||
|
||||
include:
|
||||
- local: '/.gitlab-ci.d/qemu-project.yml'
|
||||
|
5
.mailmap
5
.mailmap
@ -27,6 +27,10 @@ Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-714662
|
||||
Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
|
||||
# Corrupted Author fields
|
||||
Marek Dolata <mkdolata@us.ibm.com> mkdolata@us.ibm.com <mkdolata@us.ibm.com>
|
||||
Nick Hudson <hnick@vmware.com> hnick@vmware.com <hnick@vmware.com>
|
||||
|
||||
# There is also a:
|
||||
# (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
# for the cvs2svn initialization commit e63c3dc74bf.
|
||||
@ -96,6 +100,7 @@ Gautham R. Shenoy <ego@linux.vnet.ibm.com>
|
||||
Gonglei (Arei) <arei.gonglei@huawei.com>
|
||||
Guang Wang <wang.guang55@zte.com.cn>
|
||||
Hailiang Zhang <zhang.zhanghailiang@huawei.com>
|
||||
Hanna Reitz <hreitz@redhat.com> <mreitz@redhat.com>
|
||||
Hervé Poussineau <hpoussin@reactos.org>
|
||||
Jakub Jermář <jakub@jermar.eu>
|
||||
Jakub Jermář <jakub.jermar@kernkonzept.com>
|
||||
|
12
.travis.yml
12
.travis.yml
@ -27,6 +27,7 @@ addons:
|
||||
- libattr1-dev
|
||||
- libbrlapi-dev
|
||||
- libcap-ng-dev
|
||||
- libcacard-dev
|
||||
- libgcc-7-dev
|
||||
- libgnutls28-dev
|
||||
- libgtk-3-dev
|
||||
@ -34,7 +35,6 @@ addons:
|
||||
- liblttng-ust-dev
|
||||
- libncurses5-dev
|
||||
- libnfs-dev
|
||||
- libnss3-dev
|
||||
- libpixman-1-dev
|
||||
- libpng-dev
|
||||
- librados-dev
|
||||
@ -129,6 +129,7 @@ jobs:
|
||||
- libaio-dev
|
||||
- libattr1-dev
|
||||
- libbrlapi-dev
|
||||
- libcacard-dev
|
||||
- libcap-ng-dev
|
||||
- libgcrypt20-dev
|
||||
- libgnutls28-dev
|
||||
@ -137,7 +138,6 @@ jobs:
|
||||
- liblttng-ust-dev
|
||||
- libncurses5-dev
|
||||
- libnfs-dev
|
||||
- libnss3-dev
|
||||
- libpixman-1-dev
|
||||
- libpng-dev
|
||||
- librados-dev
|
||||
@ -163,6 +163,7 @@ jobs:
|
||||
- libaio-dev
|
||||
- libattr1-dev
|
||||
- libbrlapi-dev
|
||||
- libcacard-dev
|
||||
- libcap-ng-dev
|
||||
- libgcrypt20-dev
|
||||
- libgnutls28-dev
|
||||
@ -171,7 +172,6 @@ jobs:
|
||||
- liblttng-ust-dev
|
||||
- libncurses5-dev
|
||||
- libnfs-dev
|
||||
- libnss3-dev
|
||||
- libpixman-1-dev
|
||||
- libpng-dev
|
||||
- librados-dev
|
||||
@ -196,6 +196,7 @@ jobs:
|
||||
- libaio-dev
|
||||
- libattr1-dev
|
||||
- libbrlapi-dev
|
||||
- libcacard-dev
|
||||
- libcap-ng-dev
|
||||
- libgcrypt20-dev
|
||||
- libgnutls28-dev
|
||||
@ -204,7 +205,6 @@ jobs:
|
||||
- liblttng-ust-dev
|
||||
- libncurses5-dev
|
||||
- libnfs-dev
|
||||
- libnss3-dev
|
||||
- libpixman-1-dev
|
||||
- libpng-dev
|
||||
- librados-dev
|
||||
@ -238,6 +238,7 @@ jobs:
|
||||
apt_packages:
|
||||
- libaio-dev
|
||||
- libattr1-dev
|
||||
- libcacard-dev
|
||||
- libcap-ng-dev
|
||||
- libgnutls28-dev
|
||||
- libiscsi-dev
|
||||
@ -245,7 +246,6 @@ jobs:
|
||||
- liblzo2-dev
|
||||
- libncurses-dev
|
||||
- libnfs-dev
|
||||
- libnss3-dev
|
||||
- libpixman-1-dev
|
||||
- libsdl2-dev
|
||||
- libsdl2-image-dev
|
||||
@ -281,6 +281,7 @@ jobs:
|
||||
- libaio-dev
|
||||
- libattr1-dev
|
||||
- libbrlapi-dev
|
||||
- libcacard-dev
|
||||
- libcap-ng-dev
|
||||
- libgcrypt20-dev
|
||||
- libgnutls28-dev
|
||||
@ -289,7 +290,6 @@ jobs:
|
||||
- liblttng-ust-dev
|
||||
- libncurses5-dev
|
||||
- libnfs-dev
|
||||
- libnss3-dev
|
||||
- libpixman-1-dev
|
||||
- libpng-dev
|
||||
- librados-dev
|
||||
|
56
MAINTAINERS
56
MAINTAINERS
@ -171,6 +171,7 @@ L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/smmu*
|
||||
F: include/hw/arm/smmu*
|
||||
F: tests/acceptance/smmu.py
|
||||
|
||||
AVR TCG CPUs
|
||||
M: Michael Rolnik <mrolnik@gmail.com>
|
||||
@ -559,6 +560,7 @@ S: Odd Fixes
|
||||
F: hw/*/allwinner*
|
||||
F: include/hw/*/allwinner*
|
||||
F: hw/arm/cubieboard.c
|
||||
F: docs/system/arm/cubieboard.rst
|
||||
|
||||
Allwinner-h3
|
||||
M: Niek Linnenbank <nieklinnenbank@gmail.com>
|
||||
@ -641,6 +643,7 @@ L: qemu-arm@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: hw/arm/highbank.c
|
||||
F: hw/net/xgmac.c
|
||||
F: docs/system/arm/highbank.rst
|
||||
|
||||
Canon DIGIC
|
||||
M: Antony Pavlov <antonynpavlov@gmail.com>
|
||||
@ -681,6 +684,7 @@ F: hw/watchdog/wdt_imx2.c
|
||||
F: include/hw/arm/fsl-imx25.h
|
||||
F: include/hw/misc/imx25_ccm.h
|
||||
F: include/hw/watchdog/wdt_imx2.h
|
||||
F: docs/system/arm/imx25-pdk.rst
|
||||
|
||||
i.MX31 (kzm)
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
@ -691,6 +695,7 @@ F: hw/*/imx_*
|
||||
F: hw/*/*imx31*
|
||||
F: include/hw/*/imx_*
|
||||
F: include/hw/*/*imx31*
|
||||
F: docs/system/arm/kzm.rst
|
||||
|
||||
Integrator CP
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
@ -783,7 +788,6 @@ F: roms/vbootrom
|
||||
F: docs/system/arm/nuvoton.rst
|
||||
|
||||
nSeries
|
||||
M: Andrzej Zaborowski <balrogg@gmail.com>
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Odd Fixes
|
||||
@ -801,7 +805,6 @@ F: tests/acceptance/machine_arm_n8x0.py
|
||||
F: docs/system/arm/nseries.rst
|
||||
|
||||
Palm
|
||||
M: Andrzej Zaborowski <balrogg@gmail.com>
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Odd Fixes
|
||||
@ -834,7 +837,6 @@ F: include/hw/intc/realview_gic.h
|
||||
F: docs/system/arm/realview.rst
|
||||
|
||||
PXA2XX
|
||||
M: Andrzej Zaborowski <balrogg@gmail.com>
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Odd Fixes
|
||||
@ -853,6 +855,7 @@ F: include/hw/arm/pxa.h
|
||||
F: include/hw/arm/sharpsl.h
|
||||
F: include/hw/display/tc6393xb.h
|
||||
F: docs/system/arm/xscale.rst
|
||||
F: docs/system/arm/mainstone.rst
|
||||
|
||||
SABRELITE / i.MX6
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
@ -1022,6 +1025,7 @@ M: Peter Maydell <peter.maydell@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/msf2-som.c
|
||||
F: docs/system/arm/emcraft-sf2.rst
|
||||
|
||||
ASPEED BMCs
|
||||
M: Cédric Le Goater <clg@kaod.org>
|
||||
@ -1828,17 +1832,16 @@ F: tests/qtest/sdhci-test.c
|
||||
|
||||
USB
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Maintained
|
||||
S: Odd Fixes
|
||||
F: hw/usb/*
|
||||
F: stubs/usb-dev-stub.c
|
||||
F: tests/qtest/usb-*-test.c
|
||||
F: docs/usb2.txt
|
||||
F: docs/usb-storage.txt
|
||||
F: docs/system/devices/usb.rst
|
||||
F: include/hw/usb.h
|
||||
F: include/hw/usb/
|
||||
|
||||
USB (serial adapter)
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
R: Gerd Hoffmann <kraxel@redhat.com>
|
||||
M: Samuel Thibault <samuel.thibault@ens-lyon.org>
|
||||
S: Maintained
|
||||
F: hw/usb/dev-serial.c
|
||||
@ -1948,7 +1951,7 @@ L: virtio-fs@redhat.com
|
||||
|
||||
virtio-input
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Maintained
|
||||
S: Odd Fixes
|
||||
F: hw/input/vhost-user-input.c
|
||||
F: hw/input/virtio-input*.c
|
||||
F: include/hw/virtio/virtio-input.h
|
||||
@ -2154,7 +2157,7 @@ F: include/hw/display/ramfb.h
|
||||
|
||||
virtio-gpu
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Maintained
|
||||
S: Odd Fixes
|
||||
F: hw/display/virtio-gpu*
|
||||
F: hw/display/virtio-vga.*
|
||||
F: include/hw/virtio/virtio-gpu.h
|
||||
@ -2173,7 +2176,7 @@ F: include/hw/virtio/vhost-user-scsi.h
|
||||
|
||||
vhost-user-gpu
|
||||
M: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
R: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Maintained
|
||||
F: docs/interop/vhost-user-gpu.rst
|
||||
F: contrib/vhost-user-gpu
|
||||
@ -2201,7 +2204,6 @@ F: include/hw/southbridge/piix.h
|
||||
|
||||
Firmware configuration (fw_cfg)
|
||||
M: Philippe Mathieu-Daudé <philmd@redhat.com>
|
||||
R: Laszlo Ersek <lersek@redhat.com>
|
||||
R: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Supported
|
||||
F: docs/specs/fw_cfg.txt
|
||||
@ -2256,7 +2258,7 @@ Subsystems
|
||||
----------
|
||||
Audio
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Maintained
|
||||
S: Odd Fixes
|
||||
F: audio/
|
||||
F: hw/audio/
|
||||
F: include/hw/audio/
|
||||
@ -2268,7 +2270,7 @@ F: tests/qtest/fuzz-sb16-test.c
|
||||
|
||||
Block layer core
|
||||
M: Kevin Wolf <kwolf@redhat.com>
|
||||
M: Max Reitz <mreitz@redhat.com>
|
||||
M: Hanna Reitz <hreitz@redhat.com>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: block*
|
||||
@ -2449,22 +2451,26 @@ F: tests/tcg/multiarch/gdbstub/
|
||||
|
||||
Memory API
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
M: Peter Xu <peterx@redhat.com>
|
||||
M: David Hildenbrand <david@redhat.com>
|
||||
S: Supported
|
||||
F: include/exec/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/sysemu/memory_mapping.h
|
||||
F: softmmu/dma-helpers.c
|
||||
F: softmmu/ioport.c
|
||||
F: softmmu/memory.c
|
||||
F: softmmu/memory_mapping.c
|
||||
F: softmmu/physmem.c
|
||||
F: include/exec/memory-internal.h
|
||||
F: scripts/coccinelle/memory-region-housekeeping.cocci
|
||||
|
||||
SPICE
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Supported
|
||||
S: Odd Fixes
|
||||
F: include/ui/qemu-spice.h
|
||||
F: include/ui/spice-display.h
|
||||
F: ui/spice-*.c
|
||||
@ -2544,7 +2550,7 @@ S: Maintained
|
||||
F: net/netmap.c
|
||||
|
||||
Host Memory Backends
|
||||
M: Eduardo Habkost <ehabkost@redhat.com>
|
||||
M: David Hildenbrand <david@redhat.com>
|
||||
M: Igor Mammedov <imammedo@redhat.com>
|
||||
S: Maintained
|
||||
F: backends/hostmem*.c
|
||||
@ -2837,7 +2843,6 @@ F: tests/unit/test-authz-*
|
||||
|
||||
Sockets
|
||||
M: Daniel P. Berrange <berrange@redhat.com>
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Maintained
|
||||
F: include/qemu/sockets.h
|
||||
F: util/qemu-sockets.c
|
||||
@ -2933,7 +2938,6 @@ F: include/hw/i2c/smbus_slave.h
|
||||
F: include/hw/i2c/smbus_eeprom.h
|
||||
|
||||
Firmware schema specifications
|
||||
M: Laszlo Ersek <lersek@redhat.com>
|
||||
M: Philippe Mathieu-Daudé <philmd@redhat.com>
|
||||
R: Daniel P. Berrange <berrange@redhat.com>
|
||||
R: Kashyap Chamarthy <kchamart@redhat.com>
|
||||
@ -2941,9 +2945,10 @@ S: Maintained
|
||||
F: docs/interop/firmware.json
|
||||
|
||||
EDK2 Firmware
|
||||
M: Laszlo Ersek <lersek@redhat.com>
|
||||
M: Philippe Mathieu-Daudé <philmd@redhat.com>
|
||||
R: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Supported
|
||||
F: hw/i386/*ovmf*
|
||||
F: pc-bios/descriptors/??-edk2-*.json
|
||||
F: pc-bios/edk2-*
|
||||
F: roms/Makefile.edk2
|
||||
@ -3017,6 +3022,8 @@ F: include/tcg/
|
||||
|
||||
TCG Plugins
|
||||
M: Alex Bennée <alex.bennee@linaro.org>
|
||||
R: Alexandre Iooss <erdnaxe@crans.org>
|
||||
R: Mahmoud Mandour <ma.mandourr@gmail.com>
|
||||
S: Maintained
|
||||
F: docs/devel/tcg-plugins.rst
|
||||
F: plugins/
|
||||
@ -3033,7 +3040,7 @@ F: disas/arm-a64.cc
|
||||
F: disas/libvixl/
|
||||
|
||||
ARM TCG target
|
||||
M: Andrzej Zaborowski <balrogg@gmail.com>
|
||||
M: Richard Henderson <richard.henderson@linaro.org>
|
||||
S: Maintained
|
||||
L: qemu-arm@nongnu.org
|
||||
F: tcg/arm/
|
||||
@ -3170,6 +3177,7 @@ F: block/null.c
|
||||
NVMe Block Driver
|
||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
R: Fam Zheng <fam@euphon.net>
|
||||
R: Philippe Mathieu-Daudé <philmd@redhat.com>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: block/nvme*
|
||||
@ -3249,6 +3257,7 @@ Linux io_uring
|
||||
M: Aarushi Mehta <mehta.aaru20@gmail.com>
|
||||
M: Julia Suvorova <jusual@redhat.com>
|
||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
R: Stefano Garzarella <sgarzare@redhat.com>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Maintained
|
||||
F: block/io_uring.c
|
||||
@ -3256,7 +3265,7 @@ F: stubs/io_uring.c
|
||||
|
||||
qcow2
|
||||
M: Kevin Wolf <kwolf@redhat.com>
|
||||
M: Max Reitz <mreitz@redhat.com>
|
||||
M: Hanna Reitz <hreitz@redhat.com>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: block/qcow2*
|
||||
@ -3270,7 +3279,7 @@ F: block/qcow.c
|
||||
|
||||
blkdebug
|
||||
M: Kevin Wolf <kwolf@redhat.com>
|
||||
M: Max Reitz <mreitz@redhat.com>
|
||||
M: Hanna Reitz <hreitz@redhat.com>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: block/blkdebug.c
|
||||
@ -3305,7 +3314,7 @@ F: tests/qtest/vhost-user-blk-test.c
|
||||
F: util/vhost-user-server.c
|
||||
|
||||
FUSE block device exports
|
||||
M: Max Reitz <mreitz@redhat.com>
|
||||
M: Hanna Reitz <hreitz@redhat.com>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: block/export/fuse.c
|
||||
@ -3431,7 +3440,7 @@ F: contrib/gitdm/*
|
||||
|
||||
Incompatible changes
|
||||
R: libvir-list@redhat.com
|
||||
F: docs/system/deprecated.rst
|
||||
F: docs/about/deprecated.rst
|
||||
|
||||
Build System
|
||||
------------
|
||||
@ -3450,6 +3459,7 @@ S: Maintained
|
||||
F: docs/conf.py
|
||||
F: docs/*/conf.py
|
||||
F: docs/sphinx/
|
||||
F: docs/_templates/
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
|
8
Makefile
8
Makefile
@ -129,9 +129,11 @@ endif
|
||||
# 4. Rules to bridge to other makefiles
|
||||
|
||||
ifneq ($(NINJA),)
|
||||
MAKE.n = $(findstring n,$(firstword $(MAKEFLAGS)))
|
||||
MAKE.k = $(findstring k,$(firstword $(MAKEFLAGS)))
|
||||
MAKE.q = $(findstring q,$(firstword $(MAKEFLAGS)))
|
||||
# Filter out long options to avoid flags like --no-print-directory which
|
||||
# may result in false positive match for MAKE.n
|
||||
MAKE.n = $(findstring n,$(firstword $(filter-out --%,$(MAKEFLAGS))))
|
||||
MAKE.k = $(findstring k,$(firstword $(filter-out --%,$(MAKEFLAGS))))
|
||||
MAKE.q = $(findstring q,$(firstword $(filter-out --%,$(MAKEFLAGS))))
|
||||
MAKE.nq = $(if $(word 2, $(MAKE.n) $(MAKE.q)),nq)
|
||||
NINJAFLAGS = $(if $V,-v) $(if $(MAKE.n), -n) $(if $(MAKE.k), -k0) \
|
||||
$(filter-out -j, $(lastword -j1 $(filter -l% -j%, $(MAKEFLAGS)))) \
|
||||
|
@ -2397,6 +2397,12 @@ static int kvm_init(MachineState *ms)
|
||||
"- for kernels supporting the vm.allocate_pgste sysctl, "
|
||||
"whether it is enabled\n");
|
||||
}
|
||||
#elif defined(TARGET_PPC)
|
||||
if (ret == -EINVAL) {
|
||||
fprintf(stderr,
|
||||
"PPC KVM module is not loaded. Try modprobe kvm_%s.\n",
|
||||
(type == 2) ? "pr" : "hv");
|
||||
}
|
||||
#endif
|
||||
goto err;
|
||||
}
|
||||
|
@ -13,42 +13,125 @@
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
static inline
|
||||
void atomic_trace_rmw_pre(CPUArchState *env, target_ulong addr, uint16_t info)
|
||||
static uint16_t atomic_trace_rmw_pre(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi)
|
||||
{
|
||||
CPUState *cpu = env_cpu(env);
|
||||
uint16_t info = trace_mem_get_info(get_memop(oi), get_mmuidx(oi), false);
|
||||
|
||||
trace_guest_mem_before_exec(cpu, addr, info);
|
||||
trace_guest_mem_before_exec(cpu, addr, info | TRACE_MEM_ST);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static inline void
|
||||
atomic_trace_rmw_post(CPUArchState *env, target_ulong addr, uint16_t info)
|
||||
static void atomic_trace_rmw_post(CPUArchState *env, target_ulong addr,
|
||||
uint16_t info)
|
||||
{
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info);
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info | TRACE_MEM_ST);
|
||||
}
|
||||
|
||||
static inline
|
||||
void atomic_trace_ld_pre(CPUArchState *env, target_ulong addr, uint16_t info)
|
||||
#if HAVE_ATOMIC128
|
||||
static uint16_t atomic_trace_ld_pre(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi)
|
||||
{
|
||||
uint16_t info = trace_mem_get_info(get_memop(oi), get_mmuidx(oi), false);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), addr, info);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static inline
|
||||
void atomic_trace_ld_post(CPUArchState *env, target_ulong addr, uint16_t info)
|
||||
static void atomic_trace_ld_post(CPUArchState *env, target_ulong addr,
|
||||
uint16_t info)
|
||||
{
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info);
|
||||
}
|
||||
|
||||
static inline
|
||||
void atomic_trace_st_pre(CPUArchState *env, target_ulong addr, uint16_t info)
|
||||
static uint16_t atomic_trace_st_pre(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi)
|
||||
{
|
||||
uint16_t info = trace_mem_get_info(get_memop(oi), get_mmuidx(oi), true);
|
||||
|
||||
trace_guest_mem_before_exec(env_cpu(env), addr, info);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static inline
|
||||
void atomic_trace_st_post(CPUArchState *env, target_ulong addr, uint16_t info)
|
||||
static void atomic_trace_st_post(CPUArchState *env, target_ulong addr,
|
||||
uint16_t info)
|
||||
{
|
||||
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Atomic helpers callable from TCG.
|
||||
* These have a common interface and all defer to cpu_atomic_*
|
||||
* using the host return address from GETPC().
|
||||
*/
|
||||
|
||||
#define CMPXCHG_HELPER(OP, TYPE) \
|
||||
TYPE HELPER(atomic_##OP)(CPUArchState *env, target_ulong addr, \
|
||||
TYPE oldv, TYPE newv, uint32_t oi) \
|
||||
{ return cpu_atomic_##OP##_mmu(env, addr, oldv, newv, oi, GETPC()); }
|
||||
|
||||
CMPXCHG_HELPER(cmpxchgb, uint32_t)
|
||||
CMPXCHG_HELPER(cmpxchgw_be, uint32_t)
|
||||
CMPXCHG_HELPER(cmpxchgw_le, uint32_t)
|
||||
CMPXCHG_HELPER(cmpxchgl_be, uint32_t)
|
||||
CMPXCHG_HELPER(cmpxchgl_le, uint32_t)
|
||||
|
||||
#ifdef CONFIG_ATOMIC64
|
||||
CMPXCHG_HELPER(cmpxchgq_be, uint64_t)
|
||||
CMPXCHG_HELPER(cmpxchgq_le, uint64_t)
|
||||
#endif
|
||||
|
||||
#undef CMPXCHG_HELPER
|
||||
|
||||
#define ATOMIC_HELPER(OP, TYPE) \
|
||||
TYPE HELPER(glue(atomic_,OP))(CPUArchState *env, target_ulong addr, \
|
||||
TYPE val, uint32_t oi) \
|
||||
{ return glue(glue(cpu_atomic_,OP),_mmu)(env, addr, val, oi, GETPC()); }
|
||||
|
||||
#ifdef CONFIG_ATOMIC64
|
||||
#define GEN_ATOMIC_HELPERS(OP) \
|
||||
ATOMIC_HELPER(glue(OP,b), uint32_t) \
|
||||
ATOMIC_HELPER(glue(OP,w_be), uint32_t) \
|
||||
ATOMIC_HELPER(glue(OP,w_le), uint32_t) \
|
||||
ATOMIC_HELPER(glue(OP,l_be), uint32_t) \
|
||||
ATOMIC_HELPER(glue(OP,l_le), uint32_t) \
|
||||
ATOMIC_HELPER(glue(OP,q_be), uint64_t) \
|
||||
ATOMIC_HELPER(glue(OP,q_le), uint64_t)
|
||||
#else
|
||||
#define GEN_ATOMIC_HELPERS(OP) \
|
||||
ATOMIC_HELPER(glue(OP,b), uint32_t) \
|
||||
ATOMIC_HELPER(glue(OP,w_be), uint32_t) \
|
||||
ATOMIC_HELPER(glue(OP,w_le), uint32_t) \
|
||||
ATOMIC_HELPER(glue(OP,l_be), uint32_t) \
|
||||
ATOMIC_HELPER(glue(OP,l_le), uint32_t)
|
||||
#endif
|
||||
|
||||
GEN_ATOMIC_HELPERS(fetch_add)
|
||||
GEN_ATOMIC_HELPERS(fetch_and)
|
||||
GEN_ATOMIC_HELPERS(fetch_or)
|
||||
GEN_ATOMIC_HELPERS(fetch_xor)
|
||||
GEN_ATOMIC_HELPERS(fetch_smin)
|
||||
GEN_ATOMIC_HELPERS(fetch_umin)
|
||||
GEN_ATOMIC_HELPERS(fetch_smax)
|
||||
GEN_ATOMIC_HELPERS(fetch_umax)
|
||||
|
||||
GEN_ATOMIC_HELPERS(add_fetch)
|
||||
GEN_ATOMIC_HELPERS(and_fetch)
|
||||
GEN_ATOMIC_HELPERS(or_fetch)
|
||||
GEN_ATOMIC_HELPERS(xor_fetch)
|
||||
GEN_ATOMIC_HELPERS(smin_fetch)
|
||||
GEN_ATOMIC_HELPERS(umin_fetch)
|
||||
GEN_ATOMIC_HELPERS(smax_fetch)
|
||||
GEN_ATOMIC_HELPERS(umax_fetch)
|
||||
|
||||
GEN_ATOMIC_HELPERS(xchg)
|
||||
|
||||
#undef ATOMIC_HELPER
|
||||
#undef GEN_ATOMIC_HELPERS
|
||||
|
@ -28,8 +28,8 @@
|
||||
# define SHIFT 4
|
||||
#elif DATA_SIZE == 8
|
||||
# define SUFFIX q
|
||||
# define DATA_TYPE uint64_t
|
||||
# define SDATA_TYPE int64_t
|
||||
# define DATA_TYPE aligned_uint64_t
|
||||
# define SDATA_TYPE aligned_int64_t
|
||||
# define BSWAP bswap64
|
||||
# define SHIFT 3
|
||||
#elif DATA_SIZE == 4
|
||||
@ -71,15 +71,14 @@
|
||||
#endif
|
||||
|
||||
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE cmpv, ABI_TYPE newv EXTRA_ARGS)
|
||||
ABI_TYPE cmpv, ABI_TYPE newv,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP_RW;
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
|
||||
PAGE_READ | PAGE_WRITE, retaddr);
|
||||
DATA_TYPE ret;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
uint16_t info = atomic_trace_rmw_pre(env, addr, oi);
|
||||
|
||||
atomic_trace_rmw_pre(env, addr, info);
|
||||
#if DATA_SIZE == 16
|
||||
ret = atomic16_cmpxchg(haddr, cmpv, newv);
|
||||
#else
|
||||
@ -92,45 +91,41 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
|
||||
#if DATA_SIZE >= 16
|
||||
#if HAVE_ATOMIC128
|
||||
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
||||
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP_R;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
|
||||
PAGE_READ, retaddr);
|
||||
DATA_TYPE val;
|
||||
uint16_t info = atomic_trace_ld_pre(env, addr, oi);
|
||||
|
||||
atomic_trace_ld_pre(env, addr, info);
|
||||
val = atomic16_read(haddr);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
atomic_trace_ld_post(env, addr, info);
|
||||
return val;
|
||||
}
|
||||
|
||||
void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, ABI_TYPE val,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP_W;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, true,
|
||||
ATOMIC_MMU_IDX);
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
|
||||
PAGE_WRITE, retaddr);
|
||||
uint16_t info = atomic_trace_st_pre(env, addr, oi);
|
||||
|
||||
atomic_trace_st_pre(env, addr, info);
|
||||
atomic16_set(haddr, val);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
atomic_trace_st_post(env, addr, info);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP_RW;
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
|
||||
PAGE_READ | PAGE_WRITE, retaddr);
|
||||
DATA_TYPE ret;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
uint16_t info = atomic_trace_rmw_pre(env, addr, oi);
|
||||
|
||||
atomic_trace_rmw_pre(env, addr, info);
|
||||
ret = qatomic_xchg__nocheck(haddr, val);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
atomic_trace_rmw_post(env, addr, info);
|
||||
@ -139,14 +134,12 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
||||
|
||||
#define GEN_ATOMIC_HELPER(X) \
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
ABI_TYPE val EXTRA_ARGS) \
|
||||
ABI_TYPE val, TCGMemOpIdx oi, uintptr_t retaddr) \
|
||||
{ \
|
||||
ATOMIC_MMU_DECLS; \
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP_RW; \
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \
|
||||
PAGE_READ | PAGE_WRITE, retaddr); \
|
||||
DATA_TYPE ret; \
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false, \
|
||||
ATOMIC_MMU_IDX); \
|
||||
atomic_trace_rmw_pre(env, addr, info); \
|
||||
uint16_t info = atomic_trace_rmw_pre(env, addr, oi); \
|
||||
ret = qatomic_##X(haddr, val); \
|
||||
ATOMIC_MMU_CLEANUP; \
|
||||
atomic_trace_rmw_post(env, addr, info); \
|
||||
@ -164,7 +157,8 @@ GEN_ATOMIC_HELPER(xor_fetch)
|
||||
|
||||
#undef GEN_ATOMIC_HELPER
|
||||
|
||||
/* These helpers are, as a whole, full barriers. Within the helper,
|
||||
/*
|
||||
* These helpers are, as a whole, full barriers. Within the helper,
|
||||
* the leading barrier is explicit and the trailing barrier is within
|
||||
* cmpxchg primitive.
|
||||
*
|
||||
@ -173,14 +167,12 @@ GEN_ATOMIC_HELPER(xor_fetch)
|
||||
*/
|
||||
#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
ABI_TYPE xval EXTRA_ARGS) \
|
||||
ABI_TYPE xval, TCGMemOpIdx oi, uintptr_t retaddr) \
|
||||
{ \
|
||||
ATOMIC_MMU_DECLS; \
|
||||
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP_RW; \
|
||||
XDATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \
|
||||
PAGE_READ | PAGE_WRITE, retaddr); \
|
||||
XDATA_TYPE cmp, old, new, val = xval; \
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false, \
|
||||
ATOMIC_MMU_IDX); \
|
||||
atomic_trace_rmw_pre(env, addr, info); \
|
||||
uint16_t info = atomic_trace_rmw_pre(env, addr, oi); \
|
||||
smp_mb(); \
|
||||
cmp = qatomic_read__nocheck(haddr); \
|
||||
do { \
|
||||
@ -218,15 +210,14 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
|
||||
#endif
|
||||
|
||||
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE cmpv, ABI_TYPE newv EXTRA_ARGS)
|
||||
ABI_TYPE cmpv, ABI_TYPE newv,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP_RW;
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
|
||||
PAGE_READ | PAGE_WRITE, retaddr);
|
||||
DATA_TYPE ret;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
uint16_t info = atomic_trace_rmw_pre(env, addr, oi);
|
||||
|
||||
atomic_trace_rmw_pre(env, addr, info);
|
||||
#if DATA_SIZE == 16
|
||||
ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv));
|
||||
#else
|
||||
@ -239,30 +230,27 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
|
||||
#if DATA_SIZE >= 16
|
||||
#if HAVE_ATOMIC128
|
||||
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
||||
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP_R;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
|
||||
PAGE_READ, retaddr);
|
||||
DATA_TYPE val;
|
||||
uint16_t info = atomic_trace_ld_pre(env, addr, oi);
|
||||
|
||||
atomic_trace_ld_pre(env, addr, info);
|
||||
val = atomic16_read(haddr);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
atomic_trace_ld_post(env, addr, info);
|
||||
return BSWAP(val);
|
||||
}
|
||||
|
||||
void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, ABI_TYPE val,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP_W;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, true,
|
||||
ATOMIC_MMU_IDX);
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
|
||||
PAGE_WRITE, retaddr);
|
||||
uint16_t info = atomic_trace_st_pre(env, addr, oi);
|
||||
|
||||
val = BSWAP(val);
|
||||
atomic_trace_st_pre(env, addr, info);
|
||||
val = BSWAP(val);
|
||||
atomic16_set(haddr, val);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
@ -270,16 +258,14 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP_RW;
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE,
|
||||
PAGE_READ | PAGE_WRITE, retaddr);
|
||||
ABI_TYPE ret;
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, false,
|
||||
ATOMIC_MMU_IDX);
|
||||
uint16_t info = atomic_trace_rmw_pre(env, addr, oi);
|
||||
|
||||
atomic_trace_rmw_pre(env, addr, info);
|
||||
ret = qatomic_xchg__nocheck(haddr, BSWAP(val));
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
atomic_trace_rmw_post(env, addr, info);
|
||||
@ -288,14 +274,12 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
||||
|
||||
#define GEN_ATOMIC_HELPER(X) \
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
ABI_TYPE val EXTRA_ARGS) \
|
||||
ABI_TYPE val, TCGMemOpIdx oi, uintptr_t retaddr) \
|
||||
{ \
|
||||
ATOMIC_MMU_DECLS; \
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP_RW; \
|
||||
DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \
|
||||
PAGE_READ | PAGE_WRITE, retaddr); \
|
||||
DATA_TYPE ret; \
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, \
|
||||
false, ATOMIC_MMU_IDX); \
|
||||
atomic_trace_rmw_pre(env, addr, info); \
|
||||
uint16_t info = atomic_trace_rmw_pre(env, addr, oi); \
|
||||
ret = qatomic_##X(haddr, BSWAP(val)); \
|
||||
ATOMIC_MMU_CLEANUP; \
|
||||
atomic_trace_rmw_post(env, addr, info); \
|
||||
@ -320,14 +304,12 @@ GEN_ATOMIC_HELPER(xor_fetch)
|
||||
*/
|
||||
#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \
|
||||
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
||||
ABI_TYPE xval EXTRA_ARGS) \
|
||||
ABI_TYPE xval, TCGMemOpIdx oi, uintptr_t retaddr) \
|
||||
{ \
|
||||
ATOMIC_MMU_DECLS; \
|
||||
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP_RW; \
|
||||
XDATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \
|
||||
PAGE_READ | PAGE_WRITE, retaddr); \
|
||||
XDATA_TYPE ldo, ldn, old, new, val = xval; \
|
||||
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, \
|
||||
false, ATOMIC_MMU_IDX); \
|
||||
atomic_trace_rmw_pre(env, addr, info); \
|
||||
uint16_t info = atomic_trace_rmw_pre(env, addr, oi); \
|
||||
smp_mb(); \
|
||||
ldn = qatomic_read__nocheck(haddr); \
|
||||
do { \
|
||||
|
@ -145,6 +145,28 @@ static void init_delay_params(SyncClocks *sc, const CPUState *cpu)
|
||||
}
|
||||
#endif /* CONFIG USER ONLY */
|
||||
|
||||
uint32_t curr_cflags(CPUState *cpu)
|
||||
{
|
||||
uint32_t cflags = cpu->tcg_cflags;
|
||||
|
||||
/*
|
||||
* Record gdb single-step. We should be exiting the TB by raising
|
||||
* EXCP_DEBUG, but to simplify other tests, disable chaining too.
|
||||
*
|
||||
* For singlestep and -d nochain, suppress goto_tb so that
|
||||
* we can log -d cpu,exec after every TB.
|
||||
*/
|
||||
if (unlikely(cpu->singlestep_enabled)) {
|
||||
cflags |= CF_NO_GOTO_TB | CF_NO_GOTO_PTR | CF_SINGLE_STEP | 1;
|
||||
} else if (singlestep) {
|
||||
cflags |= CF_NO_GOTO_TB | 1;
|
||||
} else if (qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) {
|
||||
cflags |= CF_NO_GOTO_TB;
|
||||
}
|
||||
|
||||
return cflags;
|
||||
}
|
||||
|
||||
/* Might cause an exception, so have a longjmp destination ready */
|
||||
static inline TranslationBlock *tb_lookup(CPUState *cpu, target_ulong pc,
|
||||
target_ulong cs_base,
|
||||
@ -205,6 +227,76 @@ static inline void log_cpu_exec(target_ulong pc, CPUState *cpu,
|
||||
}
|
||||
}
|
||||
|
||||
static bool check_for_breakpoints(CPUState *cpu, target_ulong pc,
|
||||
uint32_t *cflags)
|
||||
{
|
||||
CPUBreakpoint *bp;
|
||||
bool match_page = false;
|
||||
|
||||
if (likely(QTAILQ_EMPTY(&cpu->breakpoints))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Singlestep overrides breakpoints.
|
||||
* This requirement is visible in the record-replay tests, where
|
||||
* we would fail to make forward progress in reverse-continue.
|
||||
*
|
||||
* TODO: gdb singlestep should only override gdb breakpoints,
|
||||
* so that one could (gdb) singlestep into the guest kernel's
|
||||
* architectural breakpoint handler.
|
||||
*/
|
||||
if (cpu->singlestep_enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
|
||||
/*
|
||||
* If we have an exact pc match, trigger the breakpoint.
|
||||
* Otherwise, note matches within the page.
|
||||
*/
|
||||
if (pc == bp->pc) {
|
||||
bool match_bp = false;
|
||||
|
||||
if (bp->flags & BP_GDB) {
|
||||
match_bp = true;
|
||||
} else if (bp->flags & BP_CPU) {
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
g_assert_not_reached();
|
||||
#else
|
||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||
assert(cc->tcg_ops->debug_check_breakpoint);
|
||||
match_bp = cc->tcg_ops->debug_check_breakpoint(cpu);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (match_bp) {
|
||||
cpu->exception_index = EXCP_DEBUG;
|
||||
return true;
|
||||
}
|
||||
} else if (((pc ^ bp->pc) & TARGET_PAGE_MASK) == 0) {
|
||||
match_page = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Within the same page as a breakpoint, single-step,
|
||||
* returning to helper_lookup_tb_ptr after each insn looking
|
||||
* for the actual breakpoint.
|
||||
*
|
||||
* TODO: Perhaps better to record all of the TBs associated
|
||||
* with a given virtual page that contains a breakpoint, and
|
||||
* then invalidate them when a new overlapping breakpoint is
|
||||
* set on the page. Non-overlapping TBs would not be
|
||||
* invalidated, nor would any TB need to be invalidated as
|
||||
* breakpoints are removed.
|
||||
*/
|
||||
if (match_page) {
|
||||
*cflags = (*cflags & ~CF_COUNT_MASK) | CF_NO_GOTO_TB | 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* helper_lookup_tb_ptr: quick check for next tb
|
||||
* @env: current cpu state
|
||||
@ -218,11 +310,16 @@ const void *HELPER(lookup_tb_ptr)(CPUArchState *env)
|
||||
CPUState *cpu = env_cpu(env);
|
||||
TranslationBlock *tb;
|
||||
target_ulong cs_base, pc;
|
||||
uint32_t flags;
|
||||
uint32_t flags, cflags;
|
||||
|
||||
cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
|
||||
|
||||
tb = tb_lookup(cpu, pc, cs_base, flags, curr_cflags(cpu));
|
||||
cflags = curr_cflags(cpu);
|
||||
if (check_for_breakpoints(cpu, pc, &cflags)) {
|
||||
cpu_loop_exit(cpu);
|
||||
}
|
||||
|
||||
tb = tb_lookup(cpu, pc, cs_base, flags, cflags);
|
||||
if (tb == NULL) {
|
||||
return tcg_code_gen_epilogue;
|
||||
}
|
||||
@ -313,8 +410,7 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
||||
CPUArchState *env = (CPUArchState *)cpu->env_ptr;
|
||||
TranslationBlock *tb;
|
||||
target_ulong cs_base, pc;
|
||||
uint32_t flags;
|
||||
uint32_t cflags = (curr_cflags(cpu) & ~CF_PARALLEL) | 1;
|
||||
uint32_t flags, cflags;
|
||||
int tb_exit;
|
||||
|
||||
if (sigsetjmp(cpu->jmp_env, 0) == 0) {
|
||||
@ -324,8 +420,20 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
||||
cpu->running = true;
|
||||
|
||||
cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
|
||||
tb = tb_lookup(cpu, pc, cs_base, flags, cflags);
|
||||
|
||||
cflags = curr_cflags(cpu);
|
||||
/* Execute in a serial context. */
|
||||
cflags &= ~CF_PARALLEL;
|
||||
/* After 1 insn, return and release the exclusive lock. */
|
||||
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
|
||||
* of an insn that includes an atomic operation we can't handle.
|
||||
* Any breakpoint for this insn will have been recognized earlier.
|
||||
*/
|
||||
|
||||
tb = tb_lookup(cpu, pc, cs_base, flags, cflags);
|
||||
if (tb == NULL) {
|
||||
mmap_lock();
|
||||
tb = tb_gen_code(cpu, pc, cs_base, flags, cflags);
|
||||
@ -478,70 +586,6 @@ static inline void tb_add_jump(TranslationBlock *tb, int n,
|
||||
return;
|
||||
}
|
||||
|
||||
//// --- Begin LibAFL code ---
|
||||
|
||||
TranslationBlock *libafl_gen_edge(CPUState *cpu, target_ulong src_block,
|
||||
target_ulong dst_block, target_ulong cs_base,
|
||||
uint32_t flags, int cflags);
|
||||
|
||||
//// --- End LibAFL code ---
|
||||
|
||||
static inline TranslationBlock *tb_find(CPUState *cpu,
|
||||
TranslationBlock *last_tb,
|
||||
int tb_exit, uint32_t cflags)
|
||||
{
|
||||
CPUArchState *env = (CPUArchState *)cpu->env_ptr;
|
||||
TranslationBlock *tb;
|
||||
target_ulong cs_base, pc;
|
||||
uint32_t flags;
|
||||
|
||||
cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
|
||||
|
||||
tb = tb_lookup(cpu, pc, cs_base, flags, cflags);
|
||||
if (tb == NULL) {
|
||||
mmap_lock();
|
||||
tb = tb_gen_code(cpu, pc, cs_base, flags, cflags);
|
||||
mmap_unlock();
|
||||
/* We add the TB in the virtual pc hash table for the fast lookup */
|
||||
qatomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb);
|
||||
}
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
/* We don't take care of direct jumps when address mapping changes in
|
||||
* system emulation. So it's not safe to make a direct jump to a TB
|
||||
* spanning two pages because the mapping for the second page can change.
|
||||
*/
|
||||
if (tb->page_addr[1] != -1) {
|
||||
last_tb = NULL;
|
||||
}
|
||||
#endif
|
||||
/* See if we can patch the calling TB. */
|
||||
if (last_tb) {
|
||||
// tb_add_jump(last_tb, tb_exit, tb);
|
||||
|
||||
//// --- Begin LibAFL code ---
|
||||
|
||||
if (last_tb->jmp_reset_offset[1] != TB_JMP_RESET_OFFSET_INVALID) {
|
||||
mmap_lock();
|
||||
TranslationBlock *edge = libafl_gen_edge(cpu, last_tb->pc, tb->pc,
|
||||
cs_base, flags, cflags);
|
||||
mmap_unlock();
|
||||
|
||||
if (edge) {
|
||||
tb_add_jump(last_tb, tb_exit, edge);
|
||||
tb_add_jump(edge, 0, tb);
|
||||
} else {
|
||||
tb_add_jump(last_tb, tb_exit, tb);
|
||||
}
|
||||
} else {
|
||||
tb_add_jump(last_tb, tb_exit, tb);
|
||||
}
|
||||
|
||||
//// --- End LibAFL code ---
|
||||
|
||||
}
|
||||
return tb;
|
||||
}
|
||||
|
||||
static inline bool cpu_handle_halt(CPUState *cpu)
|
||||
{
|
||||
if (cpu->halted) {
|
||||
@ -802,7 +846,7 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
|
||||
/* Ensure global icount has gone forward */
|
||||
icount_update(cpu);
|
||||
/* Refill decrementer and continue execution. */
|
||||
insns_left = MIN(CF_COUNT_MASK, cpu->icount_budget);
|
||||
insns_left = MIN(0xffff, cpu->icount_budget);
|
||||
cpu_neg(cpu)->icount_decr.u16.low = insns_left;
|
||||
cpu->icount_extra = cpu->icount_budget - insns_left;
|
||||
|
||||
@ -811,17 +855,26 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
|
||||
* execute we need to ensure we find/generate a TB with exactly
|
||||
* insns_left instructions in it.
|
||||
*/
|
||||
if (!cpu->icount_extra && insns_left > 0 && insns_left < tb->icount) {
|
||||
if (insns_left > 0 && insns_left < tb->icount) {
|
||||
assert(insns_left <= CF_COUNT_MASK);
|
||||
assert(cpu->icount_extra == 0);
|
||||
cpu->cflags_next_tb = (tb->cflags & ~CF_COUNT_MASK) | insns_left;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//// --- Begin LibAFL code ---
|
||||
|
||||
TranslationBlock *libafl_gen_edge(CPUState *cpu, target_ulong src_block,
|
||||
target_ulong dst_block, target_ulong cs_base,
|
||||
uint32_t flags, int cflags);
|
||||
|
||||
//// --- End LibAFL code ---
|
||||
|
||||
/* main execution loop */
|
||||
|
||||
int cpu_exec(CPUState *cpu)
|
||||
{
|
||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||
int ret;
|
||||
SyncClocks sc = { 0 };
|
||||
|
||||
@ -855,19 +908,14 @@ int cpu_exec(CPUState *cpu)
|
||||
* that we support, but is still unfixed in clang:
|
||||
* https://bugs.llvm.org/show_bug.cgi?id=21183
|
||||
*
|
||||
* Reload essential local variables here for those compilers.
|
||||
* Reload an essential local variable here for those compilers.
|
||||
* Newer versions of gcc would complain about this code (-Wclobbered),
|
||||
* so we only perform the workaround for clang.
|
||||
*/
|
||||
cpu = current_cpu;
|
||||
cc = CPU_GET_CLASS(cpu);
|
||||
#else
|
||||
/*
|
||||
* Non-buggy compilers preserve these locals; assert that
|
||||
* they have the correct value.
|
||||
*/
|
||||
/* Non-buggy compilers preserve this; assert the correct value. */
|
||||
g_assert(cpu == current_cpu);
|
||||
g_assert(cc == CPU_GET_CLASS(cpu));
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_SOFTMMU
|
||||
@ -887,22 +935,80 @@ int cpu_exec(CPUState *cpu)
|
||||
int tb_exit = 0;
|
||||
|
||||
while (!cpu_handle_interrupt(cpu, &last_tb)) {
|
||||
uint32_t cflags = cpu->cflags_next_tb;
|
||||
TranslationBlock *tb;
|
||||
target_ulong cs_base, pc;
|
||||
uint32_t flags, cflags;
|
||||
|
||||
/* When requested, use an exact setting for cflags for the next
|
||||
execution. This is used for icount, precise smc, and stop-
|
||||
after-access watchpoints. Since this request should never
|
||||
have CF_INVALID set, -1 is a convenient invalid value that
|
||||
does not require tcg headers for cpu_common_reset. */
|
||||
cpu_get_tb_cpu_state(cpu->env_ptr, &pc, &cs_base, &flags);
|
||||
|
||||
/*
|
||||
* When requested, use an exact setting for cflags for the next
|
||||
* execution. This is used for icount, precise smc, and stop-
|
||||
* after-access watchpoints. Since this request should never
|
||||
* 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);
|
||||
} else {
|
||||
cpu->cflags_next_tb = -1;
|
||||
}
|
||||
|
||||
tb = tb_find(cpu, last_tb, tb_exit, cflags);
|
||||
if (check_for_breakpoints(cpu, pc, &cflags)) {
|
||||
break;
|
||||
}
|
||||
|
||||
tb = tb_lookup(cpu, pc, cs_base, flags, cflags);
|
||||
if (tb == NULL) {
|
||||
mmap_lock();
|
||||
tb = tb_gen_code(cpu, pc, cs_base, flags, cflags);
|
||||
mmap_unlock();
|
||||
/*
|
||||
* We add the TB in the virtual pc hash table
|
||||
* for the fast lookup
|
||||
*/
|
||||
qatomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
/*
|
||||
* We don't take care of direct jumps when address mapping
|
||||
* changes in system emulation. So it's not safe to make a
|
||||
* direct jump to a TB spanning two pages because the mapping
|
||||
* for the second page can change.
|
||||
*/
|
||||
if (tb->page_addr[1] != -1) {
|
||||
last_tb = NULL;
|
||||
}
|
||||
#endif
|
||||
/* See if we can patch the calling TB. */
|
||||
if (last_tb) {
|
||||
// tb_add_jump(last_tb, tb_exit, tb);
|
||||
|
||||
//// --- Begin LibAFL code ---
|
||||
|
||||
if (last_tb->jmp_reset_offset[1] != TB_JMP_RESET_OFFSET_INVALID) {
|
||||
mmap_lock();
|
||||
TranslationBlock *edge = libafl_gen_edge(cpu, last_tb->pc, tb->pc,
|
||||
cs_base, flags, cflags);
|
||||
mmap_unlock();
|
||||
|
||||
if (edge) {
|
||||
tb_add_jump(last_tb, tb_exit, edge);
|
||||
tb_add_jump(edge, 0, tb);
|
||||
} else {
|
||||
tb_add_jump(last_tb, tb_exit, tb);
|
||||
}
|
||||
} else {
|
||||
tb_add_jump(last_tb, tb_exit, tb);
|
||||
}
|
||||
|
||||
//// --- End LibAFL code ---
|
||||
}
|
||||
|
||||
cpu_loop_exec_tb(cpu, tb, &last_tb, &tb_exit);
|
||||
|
||||
/* Try to align the host and virtual clocks
|
||||
if the guest is in advance */
|
||||
align_clocks(&sc, cpu);
|
||||
|
@ -1728,7 +1728,7 @@ bool tlb_plugin_lookup(CPUState *cpu, target_ulong addr, int mmu_idx,
|
||||
data->v.io.offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr;
|
||||
} else {
|
||||
data->is_io = false;
|
||||
data->v.ram.hostaddr = addr + tlbe->addend;
|
||||
data->v.ram.hostaddr = (void *)((uintptr_t)addr + tlbe->addend);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
@ -2686,19 +2686,14 @@ void cpu_stq_le_data(CPUArchState *env, target_ulong ptr, uint64_t val)
|
||||
cpu_stq_le_data_ra(env, ptr, val, 0);
|
||||
}
|
||||
|
||||
/* First set of helpers allows passing in of OI and RETADDR. This makes
|
||||
them callable from other helpers. */
|
||||
/*
|
||||
* First set of functions passes in OI and RETADDR.
|
||||
* This makes them callable from other helpers.
|
||||
*/
|
||||
|
||||
#define EXTRA_ARGS , TCGMemOpIdx oi, uintptr_t retaddr
|
||||
#define ATOMIC_NAME(X) \
|
||||
HELPER(glue(glue(glue(atomic_ ## X, SUFFIX), END), _mmu))
|
||||
#define ATOMIC_MMU_DECLS
|
||||
#define ATOMIC_MMU_LOOKUP_RW \
|
||||
atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_READ | PAGE_WRITE, retaddr)
|
||||
#define ATOMIC_MMU_LOOKUP_R \
|
||||
atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_READ, retaddr)
|
||||
#define ATOMIC_MMU_LOOKUP_W \
|
||||
atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_WRITE, retaddr)
|
||||
glue(glue(glue(cpu_atomic_ ## X, SUFFIX), END), _mmu)
|
||||
|
||||
#define ATOMIC_MMU_CLEANUP
|
||||
#define ATOMIC_MMU_IDX get_mmuidx(oi)
|
||||
|
||||
@ -2723,38 +2718,6 @@ void cpu_stq_le_data(CPUArchState *env, target_ulong ptr, uint64_t val)
|
||||
#include "atomic_template.h"
|
||||
#endif
|
||||
|
||||
/* Second set of helpers are directly callable from TCG as helpers. */
|
||||
|
||||
#undef EXTRA_ARGS
|
||||
#undef ATOMIC_NAME
|
||||
#undef ATOMIC_MMU_LOOKUP_RW
|
||||
#undef ATOMIC_MMU_LOOKUP_R
|
||||
#undef ATOMIC_MMU_LOOKUP_W
|
||||
|
||||
#define EXTRA_ARGS , TCGMemOpIdx oi
|
||||
#define ATOMIC_NAME(X) HELPER(glue(glue(atomic_ ## X, SUFFIX), END))
|
||||
#define ATOMIC_MMU_LOOKUP_RW \
|
||||
atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_READ | PAGE_WRITE, GETPC())
|
||||
#define ATOMIC_MMU_LOOKUP_R \
|
||||
atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_READ, GETPC())
|
||||
#define ATOMIC_MMU_LOOKUP_W \
|
||||
atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_WRITE, GETPC())
|
||||
|
||||
#define DATA_SIZE 1
|
||||
#include "atomic_template.h"
|
||||
|
||||
#define DATA_SIZE 2
|
||||
#include "atomic_template.h"
|
||||
|
||||
#define DATA_SIZE 4
|
||||
#include "atomic_template.h"
|
||||
|
||||
#ifdef CONFIG_ATOMIC64
|
||||
#define DATA_SIZE 8
|
||||
#include "atomic_template.h"
|
||||
#endif
|
||||
#undef ATOMIC_MMU_IDX
|
||||
|
||||
/* Code access functions. */
|
||||
|
||||
static uint64_t full_ldub_code(CPUArchState *env, target_ulong addr,
|
||||
|
@ -39,8 +39,6 @@ DEF_HELPER_FLAGS_1(exit_atomic, TCG_CALL_NO_WG, noreturn, env)
|
||||
DEF_HELPER_FLAGS_3(memset, TCG_CALL_NO_RWG, ptr, ptr, int, ptr)
|
||||
#endif /* IN_HELPER_PROTO */
|
||||
|
||||
#ifdef CONFIG_SOFTMMU
|
||||
|
||||
DEF_HELPER_FLAGS_5(atomic_cmpxchgb, TCG_CALL_NO_WG,
|
||||
i32, env, tl, i32, i32, i32)
|
||||
DEF_HELPER_FLAGS_5(atomic_cmpxchgw_be, TCG_CALL_NO_WG,
|
||||
@ -88,50 +86,6 @@ DEF_HELPER_FLAGS_5(atomic_cmpxchgq_le, TCG_CALL_NO_WG,
|
||||
TCG_CALL_NO_WG, i32, env, tl, i32, i32)
|
||||
#endif /* CONFIG_ATOMIC64 */
|
||||
|
||||
#else
|
||||
|
||||
DEF_HELPER_FLAGS_4(atomic_cmpxchgb, TCG_CALL_NO_WG, i32, env, tl, i32, i32)
|
||||
DEF_HELPER_FLAGS_4(atomic_cmpxchgw_be, TCG_CALL_NO_WG, i32, env, tl, i32, i32)
|
||||
DEF_HELPER_FLAGS_4(atomic_cmpxchgw_le, TCG_CALL_NO_WG, i32, env, tl, i32, i32)
|
||||
DEF_HELPER_FLAGS_4(atomic_cmpxchgl_be, TCG_CALL_NO_WG, i32, env, tl, i32, i32)
|
||||
DEF_HELPER_FLAGS_4(atomic_cmpxchgl_le, TCG_CALL_NO_WG, i32, env, tl, i32, i32)
|
||||
#ifdef CONFIG_ATOMIC64
|
||||
DEF_HELPER_FLAGS_4(atomic_cmpxchgq_be, TCG_CALL_NO_WG, i64, env, tl, i64, i64)
|
||||
DEF_HELPER_FLAGS_4(atomic_cmpxchgq_le, TCG_CALL_NO_WG, i64, env, tl, i64, i64)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ATOMIC64
|
||||
#define GEN_ATOMIC_HELPERS(NAME) \
|
||||
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), b), \
|
||||
TCG_CALL_NO_WG, i32, env, tl, i32) \
|
||||
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), w_le), \
|
||||
TCG_CALL_NO_WG, i32, env, tl, i32) \
|
||||
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), w_be), \
|
||||
TCG_CALL_NO_WG, i32, env, tl, i32) \
|
||||
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), l_le), \
|
||||
TCG_CALL_NO_WG, i32, env, tl, i32) \
|
||||
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), l_be), \
|
||||
TCG_CALL_NO_WG, i32, env, tl, i32) \
|
||||
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), q_le), \
|
||||
TCG_CALL_NO_WG, i64, env, tl, i64) \
|
||||
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), q_be), \
|
||||
TCG_CALL_NO_WG, i64, env, tl, i64)
|
||||
#else
|
||||
#define GEN_ATOMIC_HELPERS(NAME) \
|
||||
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), b), \
|
||||
TCG_CALL_NO_WG, i32, env, tl, i32) \
|
||||
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), w_le), \
|
||||
TCG_CALL_NO_WG, i32, env, tl, i32) \
|
||||
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), w_be), \
|
||||
TCG_CALL_NO_WG, i32, env, tl, i32) \
|
||||
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), l_le), \
|
||||
TCG_CALL_NO_WG, i32, env, tl, i32) \
|
||||
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), l_be), \
|
||||
TCG_CALL_NO_WG, i32, env, tl, i32)
|
||||
#endif /* CONFIG_ATOMIC64 */
|
||||
|
||||
#endif /* CONFIG_SOFTMMU */
|
||||
|
||||
GEN_ATOMIC_HELPERS(fetch_add)
|
||||
GEN_ATOMIC_HELPERS(fetch_and)
|
||||
GEN_ATOMIC_HELPERS(fetch_or)
|
||||
|
@ -1841,14 +1841,9 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||
|
||||
max_insns = cflags & CF_COUNT_MASK;
|
||||
if (max_insns == 0) {
|
||||
max_insns = CF_COUNT_MASK;
|
||||
}
|
||||
if (max_insns > TCG_MAX_INSNS) {
|
||||
max_insns = TCG_MAX_INSNS;
|
||||
}
|
||||
if (cpu->singlestep_enabled || singlestep) {
|
||||
max_insns = 1;
|
||||
}
|
||||
QEMU_BUILD_BUG_ON(CF_COUNT_MASK + 1 != TCG_MAX_INSNS);
|
||||
|
||||
buffer_overflow:
|
||||
tb = tcg_tb_alloc(tcg_ctx);
|
||||
|
@ -44,8 +44,8 @@ void translator_loop_temp_check(DisasContextBase *db)
|
||||
|
||||
bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest)
|
||||
{
|
||||
/* Suppress goto_tb in the case of single-steping. */
|
||||
if (db->singlestep_enabled || singlestep) {
|
||||
/* Suppress goto_tb if requested. */
|
||||
if (tb_cflags(db->tb) & CF_NO_GOTO_TB) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@ bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest)
|
||||
void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
CPUState *cpu, TranslationBlock *tb, int max_insns)
|
||||
{
|
||||
int bp_insn = 0;
|
||||
uint32_t cflags = tb_cflags(tb);
|
||||
bool plugin_enabled;
|
||||
|
||||
/* Initialize DisasContext */
|
||||
@ -66,7 +66,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
db->is_jmp = DISAS_NEXT;
|
||||
db->num_insns = 0;
|
||||
db->max_insns = max_insns;
|
||||
db->singlestep_enabled = cpu->singlestep_enabled;
|
||||
db->singlestep_enabled = cflags & CF_SINGLE_STEP;
|
||||
|
||||
ops->init_disas_context(db, cpu);
|
||||
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
|
||||
@ -79,8 +79,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
ops->tb_start(db, cpu);
|
||||
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
|
||||
|
||||
plugin_enabled = plugin_gen_tb_start(cpu, tb,
|
||||
tb_cflags(db->tb) & CF_MEMI_ONLY);
|
||||
plugin_enabled = plugin_gen_tb_start(cpu, tb, cflags & CF_MEMI_ONLY);
|
||||
|
||||
while (true) {
|
||||
db->num_insns++;
|
||||
@ -91,27 +90,6 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
plugin_gen_insn_start(cpu, db);
|
||||
}
|
||||
|
||||
/* Pass breakpoint hits to target for further processing */
|
||||
if (!db->singlestep_enabled
|
||||
&& unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
|
||||
CPUBreakpoint *bp;
|
||||
QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
|
||||
if (bp->pc == db->pc_next) {
|
||||
if (ops->breakpoint_check(db, cpu, bp)) {
|
||||
bp_insn = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* The breakpoint_check hook may use DISAS_TOO_MANY to indicate
|
||||
that only one more instruction is to be executed. Otherwise
|
||||
it should use DISAS_NORETURN when generating an exception,
|
||||
but may use a DISAS_TARGET_* value for Something Else. */
|
||||
if (db->is_jmp > DISAS_TOO_MANY) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//// --- Begin LibAFL code ---
|
||||
|
||||
struct libafl_breakpoint* bp = libafl_qemu_breakpoints;
|
||||
@ -128,14 +106,13 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
update db->pc_next and db->is_jmp to indicate what should be
|
||||
done next -- either exiting this loop or locate the start of
|
||||
the next instruction. */
|
||||
if (db->num_insns == db->max_insns
|
||||
&& (tb_cflags(db->tb) & CF_LAST_IO)) {
|
||||
if (db->num_insns == db->max_insns && (cflags & CF_LAST_IO)) {
|
||||
/* Accept I/O on the last instruction. */
|
||||
gen_io_start();
|
||||
ops->translate_insn(db, cpu);
|
||||
} else {
|
||||
/* we should only see CF_MEMI_ONLY for io_recompile */
|
||||
tcg_debug_assert(!(tb_cflags(db->tb) & CF_MEMI_ONLY));
|
||||
tcg_debug_assert(!(cflags & CF_MEMI_ONLY));
|
||||
ops->translate_insn(db, cpu);
|
||||
}
|
||||
|
||||
@ -162,7 +139,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
|
||||
/* Emit code to exit the TB, as indicated by db->is_jmp. */
|
||||
ops->tb_stop(db, cpu);
|
||||
gen_tb_end(db->tb, db->num_insns - bp_insn);
|
||||
gen_tb_end(db->tb, db->num_insns);
|
||||
|
||||
if (plugin_enabled) {
|
||||
plugin_gen_tb_end(cpu);
|
||||
|
@ -1221,9 +1221,14 @@ uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr ptr)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Do not allow unaligned operations to proceed. Return the host address. */
|
||||
/*
|
||||
* Do not allow unaligned operations to proceed. Return the host address.
|
||||
*
|
||||
* @prot may be PAGE_READ, PAGE_WRITE, or PAGE_READ|PAGE_WRITE.
|
||||
*/
|
||||
static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
int size, uintptr_t retaddr)
|
||||
TCGMemOpIdx oi, int size, int prot,
|
||||
uintptr_t retaddr)
|
||||
{
|
||||
/* Enforce qemu required alignment. */
|
||||
if (unlikely(addr & (size - 1))) {
|
||||
@ -1234,19 +1239,18 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Macro to call the above, with local variables from the use context. */
|
||||
#define ATOMIC_MMU_DECLS do {} while (0)
|
||||
#define ATOMIC_MMU_LOOKUP_RW atomic_mmu_lookup(env, addr, DATA_SIZE, GETPC())
|
||||
#define ATOMIC_MMU_LOOKUP_R ATOMIC_MMU_LOOKUP_RW
|
||||
#define ATOMIC_MMU_LOOKUP_W ATOMIC_MMU_LOOKUP_RW
|
||||
#include "atomic_common.c.inc"
|
||||
|
||||
/*
|
||||
* First set of functions passes in OI and RETADDR.
|
||||
* This makes them callable from other helpers.
|
||||
*/
|
||||
|
||||
#define ATOMIC_NAME(X) \
|
||||
glue(glue(glue(cpu_atomic_ ## X, SUFFIX), END), _mmu)
|
||||
#define ATOMIC_MMU_CLEANUP do { clear_helper_retaddr(); } while (0)
|
||||
#define ATOMIC_MMU_IDX MMU_USER_IDX
|
||||
|
||||
#define ATOMIC_NAME(X) HELPER(glue(glue(atomic_ ## X, SUFFIX), END))
|
||||
#define EXTRA_ARGS
|
||||
|
||||
#include "atomic_common.c.inc"
|
||||
|
||||
#define DATA_SIZE 1
|
||||
#include "atomic_template.h"
|
||||
|
||||
@ -1261,20 +1265,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
#include "atomic_template.h"
|
||||
#endif
|
||||
|
||||
/* The following is only callable from other helpers, and matches up
|
||||
with the softmmu version. */
|
||||
|
||||
#if HAVE_ATOMIC128 || HAVE_CMPXCHG128
|
||||
|
||||
#undef EXTRA_ARGS
|
||||
#undef ATOMIC_NAME
|
||||
#undef ATOMIC_MMU_LOOKUP_RW
|
||||
|
||||
#define EXTRA_ARGS , TCGMemOpIdx oi, uintptr_t retaddr
|
||||
#define ATOMIC_NAME(X) \
|
||||
HELPER(glue(glue(glue(atomic_ ## X, SUFFIX), END), _mmu))
|
||||
#define ATOMIC_MMU_LOOKUP_RW atomic_mmu_lookup(env, addr, DATA_SIZE, retaddr)
|
||||
|
||||
#define DATA_SIZE 16
|
||||
#include "atomic_template.h"
|
||||
#endif
|
||||
|
@ -1622,10 +1622,20 @@ void audio_cleanup(void)
|
||||
}
|
||||
}
|
||||
|
||||
static bool vmstate_audio_needed(void *opaque)
|
||||
{
|
||||
/*
|
||||
* Never needed, this vmstate only exists in case
|
||||
* an old qemu sends it to us.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_audio = {
|
||||
.name = "audio",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = vmstate_audio_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
|
3
block.c
3
block.c
@ -6162,6 +6162,9 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
|
||||
|
||||
void bdrv_init(void)
|
||||
{
|
||||
#ifdef CONFIG_BDRV_WHITELIST_TOOLS
|
||||
use_bdrv_whitelist = 1;
|
||||
#endif
|
||||
module_call_init(MODULE_INIT_BLOCK);
|
||||
}
|
||||
|
||||
|
132
block/blkdebug.c
132
block/blkdebug.c
@ -38,25 +38,27 @@
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "sysemu/qtest.h"
|
||||
|
||||
/* All APIs are thread-safe */
|
||||
|
||||
typedef struct BDRVBlkdebugState {
|
||||
int state;
|
||||
int new_state;
|
||||
/* IN: initialized in blkdebug_open() and never changed */
|
||||
uint64_t align;
|
||||
uint64_t max_transfer;
|
||||
uint64_t opt_write_zero;
|
||||
uint64_t max_write_zero;
|
||||
uint64_t opt_discard;
|
||||
uint64_t max_discard;
|
||||
|
||||
char *config_file; /* For blkdebug_refresh_filename() */
|
||||
/* initialized in blkdebug_parse_perms() */
|
||||
uint64_t take_child_perms;
|
||||
uint64_t unshare_child_perms;
|
||||
|
||||
/* For blkdebug_refresh_filename() */
|
||||
char *config_file;
|
||||
|
||||
/* State. Protected by lock */
|
||||
int state;
|
||||
QLIST_HEAD(, BlkdebugRule) rules[BLKDBG__MAX];
|
||||
QSIMPLEQ_HEAD(, BlkdebugRule) active_rules;
|
||||
QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs;
|
||||
QemuMutex lock;
|
||||
} BDRVBlkdebugState;
|
||||
|
||||
typedef struct BlkdebugAIOCB {
|
||||
@ -65,8 +67,11 @@ typedef struct BlkdebugAIOCB {
|
||||
} BlkdebugAIOCB;
|
||||
|
||||
typedef struct BlkdebugSuspendedReq {
|
||||
/* IN: initialized in suspend_request() */
|
||||
Coroutine *co;
|
||||
char *tag;
|
||||
|
||||
/* List entry protected BDRVBlkdebugState's lock */
|
||||
QLIST_ENTRY(BlkdebugSuspendedReq) next;
|
||||
} BlkdebugSuspendedReq;
|
||||
|
||||
@ -74,9 +79,11 @@ enum {
|
||||
ACTION_INJECT_ERROR,
|
||||
ACTION_SET_STATE,
|
||||
ACTION_SUSPEND,
|
||||
ACTION__MAX,
|
||||
};
|
||||
|
||||
typedef struct BlkdebugRule {
|
||||
/* IN: initialized in add_rule() or blkdebug_debug_breakpoint() */
|
||||
BlkdebugEvent event;
|
||||
int action;
|
||||
int state;
|
||||
@ -95,6 +102,8 @@ typedef struct BlkdebugRule {
|
||||
char *tag;
|
||||
} suspend;
|
||||
} options;
|
||||
|
||||
/* List entries protected BDRVBlkdebugState's lock */
|
||||
QLIST_ENTRY(BlkdebugRule) next;
|
||||
QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
|
||||
} BlkdebugRule;
|
||||
@ -244,11 +253,14 @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
|
||||
};
|
||||
|
||||
/* Add the rule */
|
||||
qemu_mutex_lock(&s->lock);
|
||||
QLIST_INSERT_HEAD(&s->rules[event], rule, next);
|
||||
qemu_mutex_unlock(&s->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called with lock held or from .bdrv_close */
|
||||
static void remove_rule(BlkdebugRule *rule)
|
||||
{
|
||||
switch (rule->action) {
|
||||
@ -467,6 +479,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
int ret;
|
||||
uint64_t align;
|
||||
|
||||
qemu_mutex_init(&s->lock);
|
||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
if (!qemu_opts_absorb_qdict(opts, options, errp)) {
|
||||
ret = -EINVAL;
|
||||
@ -567,6 +580,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
ret = 0;
|
||||
out:
|
||||
if (ret < 0) {
|
||||
qemu_mutex_destroy(&s->lock);
|
||||
g_free(s->config_file);
|
||||
}
|
||||
qemu_opts_del(opts);
|
||||
@ -581,6 +595,7 @@ static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
int error;
|
||||
bool immediately;
|
||||
|
||||
qemu_mutex_lock(&s->lock);
|
||||
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
|
||||
uint64_t inject_offset = rule->options.inject.offset;
|
||||
|
||||
@ -594,6 +609,7 @@ static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
}
|
||||
|
||||
if (!rule || !rule->options.inject.error) {
|
||||
qemu_mutex_unlock(&s->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -605,6 +621,7 @@ static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
remove_rule(rule);
|
||||
}
|
||||
|
||||
qemu_mutex_unlock(&s->lock);
|
||||
if (!immediately) {
|
||||
aio_co_schedule(qemu_get_current_aio_context(), qemu_coroutine_self());
|
||||
qemu_coroutine_yield();
|
||||
@ -770,78 +787,80 @@ static void blkdebug_close(BlockDriverState *bs)
|
||||
}
|
||||
|
||||
g_free(s->config_file);
|
||||
qemu_mutex_destroy(&s->lock);
|
||||
}
|
||||
|
||||
/* Called with lock held. */
|
||||
static void suspend_request(BlockDriverState *bs, BlkdebugRule *rule)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugSuspendedReq r;
|
||||
BlkdebugSuspendedReq *r;
|
||||
|
||||
r = (BlkdebugSuspendedReq) {
|
||||
.co = qemu_coroutine_self(),
|
||||
.tag = g_strdup(rule->options.suspend.tag),
|
||||
};
|
||||
r = g_new(BlkdebugSuspendedReq, 1);
|
||||
|
||||
r->co = qemu_coroutine_self();
|
||||
r->tag = g_strdup(rule->options.suspend.tag);
|
||||
|
||||
remove_rule(rule);
|
||||
QLIST_INSERT_HEAD(&s->suspended_reqs, &r, next);
|
||||
QLIST_INSERT_HEAD(&s->suspended_reqs, r, next);
|
||||
|
||||
if (!qtest_enabled()) {
|
||||
printf("blkdebug: Suspended request '%s'\n", r.tag);
|
||||
printf("blkdebug: Suspended request '%s'\n", r->tag);
|
||||
}
|
||||
qemu_coroutine_yield();
|
||||
if (!qtest_enabled()) {
|
||||
printf("blkdebug: Resuming request '%s'\n", r.tag);
|
||||
}
|
||||
|
||||
QLIST_REMOVE(&r, next);
|
||||
g_free(r.tag);
|
||||
}
|
||||
|
||||
static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
|
||||
bool injected)
|
||||
/* Called with lock held. */
|
||||
static void process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
|
||||
int *action_count, int *new_state)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
|
||||
/* Only process rules for the current state */
|
||||
if (rule->state && rule->state != s->state) {
|
||||
return injected;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Take the action */
|
||||
action_count[rule->action]++;
|
||||
switch (rule->action) {
|
||||
case ACTION_INJECT_ERROR:
|
||||
if (!injected) {
|
||||
if (action_count[ACTION_INJECT_ERROR] == 1) {
|
||||
QSIMPLEQ_INIT(&s->active_rules);
|
||||
injected = true;
|
||||
}
|
||||
QSIMPLEQ_INSERT_HEAD(&s->active_rules, rule, active_next);
|
||||
break;
|
||||
|
||||
case ACTION_SET_STATE:
|
||||
s->new_state = rule->options.set_state.new_state;
|
||||
*new_state = rule->options.set_state.new_state;
|
||||
break;
|
||||
|
||||
case ACTION_SUSPEND:
|
||||
suspend_request(bs, rule);
|
||||
break;
|
||||
}
|
||||
return injected;
|
||||
}
|
||||
|
||||
static void blkdebug_debug_event(BlockDriverState *bs, BlkdebugEvent event)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
struct BlkdebugRule *rule, *next;
|
||||
bool injected;
|
||||
int new_state;
|
||||
int actions_count[ACTION__MAX] = { 0 };
|
||||
|
||||
assert((int)event >= 0 && event < BLKDBG__MAX);
|
||||
|
||||
injected = false;
|
||||
s->new_state = s->state;
|
||||
WITH_QEMU_LOCK_GUARD(&s->lock) {
|
||||
new_state = s->state;
|
||||
QLIST_FOREACH_SAFE(rule, &s->rules[event], next, next) {
|
||||
injected = process_rule(bs, rule, injected);
|
||||
process_rule(bs, rule, actions_count, &new_state);
|
||||
}
|
||||
s->state = new_state;
|
||||
}
|
||||
|
||||
while (actions_count[ACTION_SUSPEND] > 0) {
|
||||
qemu_coroutine_yield();
|
||||
actions_count[ACTION_SUSPEND]--;
|
||||
}
|
||||
s->state = s->new_state;
|
||||
}
|
||||
|
||||
static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event,
|
||||
@ -864,33 +883,64 @@ static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event,
|
||||
.options.suspend.tag = g_strdup(tag),
|
||||
};
|
||||
|
||||
qemu_mutex_lock(&s->lock);
|
||||
QLIST_INSERT_HEAD(&s->rules[blkdebug_event], rule, next);
|
||||
qemu_mutex_unlock(&s->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag)
|
||||
/* Called with lock held. May temporarily release lock. */
|
||||
static int resume_req_by_tag(BDRVBlkdebugState *s, const char *tag, bool all)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugSuspendedReq *r, *next;
|
||||
BlkdebugSuspendedReq *r;
|
||||
|
||||
QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, next) {
|
||||
retry:
|
||||
/*
|
||||
* No need for _SAFE, since a different coroutine can remove another node
|
||||
* (not the current one) in this list, and when the current one is removed
|
||||
* the iteration starts back from beginning anyways.
|
||||
*/
|
||||
QLIST_FOREACH(r, &s->suspended_reqs, next) {
|
||||
if (!strcmp(r->tag, tag)) {
|
||||
qemu_coroutine_enter(r->co);
|
||||
Coroutine *co = r->co;
|
||||
|
||||
if (!qtest_enabled()) {
|
||||
printf("blkdebug: Resuming request '%s'\n", r->tag);
|
||||
}
|
||||
|
||||
QLIST_REMOVE(r, next);
|
||||
g_free(r->tag);
|
||||
g_free(r);
|
||||
|
||||
qemu_mutex_unlock(&s->lock);
|
||||
qemu_coroutine_enter(co);
|
||||
qemu_mutex_lock(&s->lock);
|
||||
|
||||
if (all) {
|
||||
goto retry;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
QEMU_LOCK_GUARD(&s->lock);
|
||||
return resume_req_by_tag(s, tag, false);
|
||||
}
|
||||
|
||||
static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs,
|
||||
const char *tag)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugSuspendedReq *r, *r_next;
|
||||
BlkdebugRule *rule, *next;
|
||||
int i, ret = -ENOENT;
|
||||
|
||||
QEMU_LOCK_GUARD(&s->lock);
|
||||
for (i = 0; i < BLKDBG__MAX; i++) {
|
||||
QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
|
||||
if (rule->action == ACTION_SUSPEND &&
|
||||
@ -900,12 +950,9 @@ static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
}
|
||||
QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, r_next) {
|
||||
if (!strcmp(r->tag, tag)) {
|
||||
qemu_coroutine_enter(r->co);
|
||||
if (resume_req_by_tag(s, tag, true) == 0) {
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -914,6 +961,7 @@ static bool blkdebug_debug_is_suspended(BlockDriverState *bs, const char *tag)
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugSuspendedReq *r;
|
||||
|
||||
QEMU_LOCK_GUARD(&s->lock);
|
||||
QLIST_FOREACH(r, &s->suspended_reqs, next) {
|
||||
if (!strcmp(r->tag, tag)) {
|
||||
return true;
|
||||
|
@ -193,7 +193,7 @@ int bdrv_dirty_bitmap_check(const BdrvDirtyBitmap *bitmap, uint32_t flags,
|
||||
error_setg(errp, "Bitmap '%s' is inconsistent and cannot be used",
|
||||
bitmap->name);
|
||||
error_append_hint(errp, "Try block-dirty-bitmap-remove to delete"
|
||||
" this bitmap from disk");
|
||||
" this bitmap from disk\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -111,6 +111,7 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
|
||||
if (export->has_iothread) {
|
||||
IOThread *iothread;
|
||||
AioContext *new_ctx;
|
||||
Error **set_context_errp;
|
||||
|
||||
iothread = iothread_by_id(export->iothread);
|
||||
if (!iothread) {
|
||||
@ -120,7 +121,9 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
|
||||
|
||||
new_ctx = iothread_get_aio_context(iothread);
|
||||
|
||||
ret = bdrv_try_set_aio_context(bs, new_ctx, errp);
|
||||
/* Ignore errors with fixed-iothread=false */
|
||||
set_context_errp = fixed_iothread ? errp : NULL;
|
||||
ret = bdrv_try_set_aio_context(bs, new_ctx, set_context_errp);
|
||||
if (ret == 0) {
|
||||
aio_context_release(ctx);
|
||||
aio_context_acquire(new_ctx);
|
||||
|
@ -635,7 +635,9 @@ static void fuse_fallocate(fuse_req_t req, fuse_ino_t inode, int mode,
|
||||
offset += size;
|
||||
length -= size;
|
||||
} while (ret == 0 && length > 0);
|
||||
} else if (mode & FALLOC_FL_ZERO_RANGE) {
|
||||
}
|
||||
#ifdef CONFIG_FALLOCATE_ZERO_RANGE
|
||||
else if (mode & FALLOC_FL_ZERO_RANGE) {
|
||||
if (!(mode & FALLOC_FL_KEEP_SIZE) && offset + length > blk_len) {
|
||||
/* No need for zeroes, we are going to write them ourselves */
|
||||
ret = fuse_do_truncate(exp, offset + length, false,
|
||||
@ -654,7 +656,9 @@ static void fuse_fallocate(fuse_req_t req, fuse_ino_t inode, int mode,
|
||||
offset += size;
|
||||
length -= size;
|
||||
} while (ret == 0 && length > 0);
|
||||
} else if (!mode) {
|
||||
}
|
||||
#endif /* CONFIG_FALLOCATE_ZERO_RANGE */
|
||||
else if (!mode) {
|
||||
/* We can only fallocate at the EOF with a truncate */
|
||||
if (offset < blk_len) {
|
||||
fuse_reply_err(req, EOPNOTSUPP);
|
||||
|
@ -1841,7 +1841,7 @@ int coroutine_fn bdrv_co_preadv_part(BdrvChild *child,
|
||||
ret = bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, &pad,
|
||||
NULL);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_READ);
|
||||
@ -1849,10 +1849,11 @@ int coroutine_fn bdrv_co_preadv_part(BdrvChild *child,
|
||||
bs->bl.request_alignment,
|
||||
qiov, qiov_offset, flags);
|
||||
tracked_request_end(&req);
|
||||
bdrv_dec_in_flight(bs);
|
||||
|
||||
bdrv_padding_destroy(&pad);
|
||||
|
||||
fail:
|
||||
bdrv_dec_in_flight(bs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -165,7 +165,21 @@ static void luring_process_completions(LuringState *s)
|
||||
total_bytes = ret + luringcb->total_read;
|
||||
|
||||
if (ret < 0) {
|
||||
if (ret == -EINTR) {
|
||||
/*
|
||||
* Only writev/readv/fsync requests on regular files or host block
|
||||
* devices are submitted. Therefore -EAGAIN is not expected but it's
|
||||
* known to happen sometimes with Linux SCSI. Submit again and hope
|
||||
* the request completes successfully.
|
||||
*
|
||||
* For more information, see:
|
||||
* https://lore.kernel.org/io-uring/20210727165811.284510-3-axboe@kernel.dk/T/#u
|
||||
*
|
||||
* If the code is changed to submit other types of requests in the
|
||||
* future, then this workaround may need to be extended to deal with
|
||||
* genuine -EAGAIN results that should not be resubmitted
|
||||
* immediately.
|
||||
*/
|
||||
if (ret == -EINTR || ret == -EAGAIN) {
|
||||
luring_resubmit(s, luringcb);
|
||||
continue;
|
||||
}
|
||||
|
@ -28,6 +28,9 @@
|
||||
*/
|
||||
#define MAX_EVENTS 1024
|
||||
|
||||
/* Maximum number of requests in a batch. (default value) */
|
||||
#define DEFAULT_MAX_BATCH 32
|
||||
|
||||
struct qemu_laiocb {
|
||||
Coroutine *co;
|
||||
LinuxAioState *ctx;
|
||||
@ -351,6 +354,10 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset,
|
||||
LinuxAioState *s = laiocb->ctx;
|
||||
struct iocb *iocbs = &laiocb->iocb;
|
||||
QEMUIOVector *qiov = laiocb->qiov;
|
||||
int64_t max_batch = s->aio_context->aio_max_batch ?: DEFAULT_MAX_BATCH;
|
||||
|
||||
/* limit the batch with the number of available events */
|
||||
max_batch = MIN_NON_ZERO(MAX_EVENTS - s->io_q.in_flight, max_batch);
|
||||
|
||||
switch (type) {
|
||||
case QEMU_AIO_WRITE:
|
||||
@ -371,7 +378,7 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset,
|
||||
s->io_q.in_queue++;
|
||||
if (!s->io_q.blocked &&
|
||||
(!s->io_q.plugged ||
|
||||
s->io_q.in_flight + s->io_q.in_queue >= MAX_EVENTS)) {
|
||||
s->io_q.in_queue >= max_batch)) {
|
||||
ioq_submit(s);
|
||||
}
|
||||
|
||||
|
@ -107,6 +107,7 @@ struct MirrorOp {
|
||||
bool is_in_flight;
|
||||
CoQueue waiting_requests;
|
||||
Coroutine *co;
|
||||
MirrorOp *waiting_for_op;
|
||||
|
||||
QTAILQ_ENTRY(MirrorOp) next;
|
||||
};
|
||||
@ -159,7 +160,18 @@ static void coroutine_fn mirror_wait_on_conflicts(MirrorOp *self,
|
||||
if (ranges_overlap(self_start_chunk, self_nb_chunks,
|
||||
op_start_chunk, op_nb_chunks))
|
||||
{
|
||||
/*
|
||||
* If the operation is already (indirectly) waiting for us, or
|
||||
* will wait for us as soon as it wakes up, then just go on
|
||||
* (instead of producing a deadlock in the former case).
|
||||
*/
|
||||
if (op->waiting_for_op) {
|
||||
continue;
|
||||
}
|
||||
|
||||
self->waiting_for_op = op;
|
||||
qemu_co_queue_wait(&op->waiting_requests, NULL);
|
||||
self->waiting_for_op = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1343,6 +1355,7 @@ static MirrorOp *coroutine_fn active_write_prepare(MirrorBlockJob *s,
|
||||
.bytes = bytes,
|
||||
.is_active_write = true,
|
||||
.is_in_flight = true,
|
||||
.co = qemu_coroutine_self(),
|
||||
};
|
||||
qemu_co_queue_init(&op->waiting_requests);
|
||||
QTAILQ_INSERT_TAIL(&s->ops_in_flight, op, next);
|
||||
|
@ -371,6 +371,9 @@ int coroutine_fn nbd_co_do_establish_connection(BlockDriverState *bs,
|
||||
return -ECONNREFUSED;
|
||||
}
|
||||
|
||||
yank_register_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name), nbd_yank,
|
||||
bs);
|
||||
|
||||
ret = nbd_handle_updated_info(s->bs, NULL);
|
||||
if (ret < 0) {
|
||||
/*
|
||||
@ -381,6 +384,8 @@ int coroutine_fn nbd_co_do_establish_connection(BlockDriverState *bs,
|
||||
|
||||
nbd_send_request(s->ioc, &request);
|
||||
|
||||
yank_unregister_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name),
|
||||
nbd_yank, bs);
|
||||
object_unref(OBJECT(s->ioc));
|
||||
s->ioc = NULL;
|
||||
|
||||
@ -390,9 +395,6 @@ int coroutine_fn nbd_co_do_establish_connection(BlockDriverState *bs,
|
||||
qio_channel_set_blocking(s->ioc, false, NULL);
|
||||
qio_channel_attach_aio_context(s->ioc, bdrv_get_aio_context(bs));
|
||||
|
||||
yank_register_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name), nbd_yank,
|
||||
bs);
|
||||
|
||||
/* successfully connected */
|
||||
s->state = NBD_CLIENT_CONNECTED;
|
||||
qemu_co_queue_restart_all(&s->free_sema);
|
||||
|
22
block/nvme.c
22
block/nvme.c
@ -1030,7 +1030,29 @@ try_map:
|
||||
r = qemu_vfio_dma_map(s->vfio,
|
||||
qiov->iov[i].iov_base,
|
||||
len, true, &iova);
|
||||
if (r == -ENOSPC) {
|
||||
/*
|
||||
* In addition to the -ENOMEM error, the VFIO_IOMMU_MAP_DMA
|
||||
* ioctl returns -ENOSPC to signal the user exhausted the DMA
|
||||
* mappings available for a container since Linux kernel commit
|
||||
* 492855939bdb ("vfio/type1: Limit DMA mappings per container",
|
||||
* April 2019, see CVE-2019-3882).
|
||||
*
|
||||
* This block driver already handles this error path by checking
|
||||
* for the -ENOMEM error, so we directly replace -ENOSPC by
|
||||
* -ENOMEM. Beside, -ENOSPC has a specific meaning for blockdev
|
||||
* coroutines: it triggers BLOCKDEV_ON_ERROR_ENOSPC and
|
||||
* BLOCK_ERROR_ACTION_STOP which stops the VM, asking the operator
|
||||
* to add more storage to the blockdev. Not something we can do
|
||||
* easily with an IOMMU :)
|
||||
*/
|
||||
r = -ENOMEM;
|
||||
}
|
||||
if (r == -ENOMEM && retry) {
|
||||
/*
|
||||
* We exhausted the DMA mappings available for our container:
|
||||
* recycle the volatile IOVA mappings.
|
||||
*/
|
||||
retry = false;
|
||||
trace_nvme_dma_flush_queue_wait(s);
|
||||
if (s->dma_map_count) {
|
||||
|
@ -35,7 +35,6 @@ typedef enum {
|
||||
typedef struct BDRVReplicationState {
|
||||
ReplicationMode mode;
|
||||
ReplicationStage stage;
|
||||
BdrvChild *active_disk;
|
||||
BlockJob *commit_job;
|
||||
BdrvChild *hidden_disk;
|
||||
BdrvChild *secondary_disk;
|
||||
@ -166,7 +165,12 @@ static void replication_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||
uint64_t perm, uint64_t shared,
|
||||
uint64_t *nperm, uint64_t *nshared)
|
||||
{
|
||||
if (role & BDRV_CHILD_PRIMARY) {
|
||||
*nperm = BLK_PERM_CONSISTENT_READ;
|
||||
} else {
|
||||
*nperm = 0;
|
||||
}
|
||||
|
||||
if ((bs->open_flags & (BDRV_O_INACTIVE | BDRV_O_RDWR)) == BDRV_O_RDWR) {
|
||||
*nperm |= BLK_PERM_WRITE;
|
||||
}
|
||||
@ -307,8 +311,10 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void secondary_do_checkpoint(BDRVReplicationState *s, Error **errp)
|
||||
static void secondary_do_checkpoint(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
BDRVReplicationState *s = bs->opaque;
|
||||
BdrvChild *active_disk = bs->file;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
@ -323,13 +329,13 @@ static void secondary_do_checkpoint(BDRVReplicationState *s, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!s->active_disk->bs->drv) {
|
||||
if (!active_disk->bs->drv) {
|
||||
error_setg(errp, "Active disk %s is ejected",
|
||||
s->active_disk->bs->node_name);
|
||||
active_disk->bs->node_name);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = bdrv_make_empty(s->active_disk, errp);
|
||||
ret = bdrv_make_empty(active_disk, errp);
|
||||
if (ret < 0) {
|
||||
return;
|
||||
}
|
||||
@ -340,17 +346,7 @@ static void secondary_do_checkpoint(BDRVReplicationState *s, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
BlockBackend *blk = blk_new(qemu_get_current_aio_context(),
|
||||
BLK_PERM_WRITE, BLK_PERM_ALL);
|
||||
blk_insert_bs(blk, s->hidden_disk->bs, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
blk_unref(blk);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = blk_make_empty(blk, errp);
|
||||
blk_unref(blk);
|
||||
ret = bdrv_make_empty(s->hidden_disk, errp);
|
||||
if (ret < 0) {
|
||||
return;
|
||||
}
|
||||
@ -365,27 +361,35 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVReplicationState *s = bs->opaque;
|
||||
BdrvChild *hidden_disk, *secondary_disk;
|
||||
BlockReopenQueue *reopen_queue = NULL;
|
||||
|
||||
/*
|
||||
* s->hidden_disk and s->secondary_disk may not be set yet, as they will
|
||||
* only be set after the children are writable.
|
||||
*/
|
||||
hidden_disk = bs->file->bs->backing;
|
||||
secondary_disk = hidden_disk->bs->backing;
|
||||
|
||||
if (writable) {
|
||||
s->orig_hidden_read_only = bdrv_is_read_only(s->hidden_disk->bs);
|
||||
s->orig_secondary_read_only = bdrv_is_read_only(s->secondary_disk->bs);
|
||||
s->orig_hidden_read_only = bdrv_is_read_only(hidden_disk->bs);
|
||||
s->orig_secondary_read_only = bdrv_is_read_only(secondary_disk->bs);
|
||||
}
|
||||
|
||||
bdrv_subtree_drained_begin(s->hidden_disk->bs);
|
||||
bdrv_subtree_drained_begin(s->secondary_disk->bs);
|
||||
bdrv_subtree_drained_begin(hidden_disk->bs);
|
||||
bdrv_subtree_drained_begin(secondary_disk->bs);
|
||||
|
||||
if (s->orig_hidden_read_only) {
|
||||
QDict *opts = qdict_new();
|
||||
qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable);
|
||||
reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs,
|
||||
reopen_queue = bdrv_reopen_queue(reopen_queue, hidden_disk->bs,
|
||||
opts, true);
|
||||
}
|
||||
|
||||
if (s->orig_secondary_read_only) {
|
||||
QDict *opts = qdict_new();
|
||||
qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable);
|
||||
reopen_queue = bdrv_reopen_queue(reopen_queue, s->secondary_disk->bs,
|
||||
reopen_queue = bdrv_reopen_queue(reopen_queue, secondary_disk->bs,
|
||||
opts, true);
|
||||
}
|
||||
|
||||
@ -400,8 +404,8 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
|
||||
}
|
||||
}
|
||||
|
||||
bdrv_subtree_drained_end(s->hidden_disk->bs);
|
||||
bdrv_subtree_drained_end(s->secondary_disk->bs);
|
||||
bdrv_subtree_drained_end(hidden_disk->bs);
|
||||
bdrv_subtree_drained_end(secondary_disk->bs);
|
||||
}
|
||||
|
||||
static void backup_job_cleanup(BlockDriverState *bs)
|
||||
@ -458,6 +462,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
||||
BlockDriverState *bs = rs->opaque;
|
||||
BDRVReplicationState *s;
|
||||
BlockDriverState *top_bs;
|
||||
BdrvChild *active_disk, *hidden_disk, *secondary_disk;
|
||||
int64_t active_length, hidden_length, disk_length;
|
||||
AioContext *aio_context;
|
||||
Error *local_err = NULL;
|
||||
@ -495,32 +500,31 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
||||
case REPLICATION_MODE_PRIMARY:
|
||||
break;
|
||||
case REPLICATION_MODE_SECONDARY:
|
||||
s->active_disk = bs->file;
|
||||
if (!s->active_disk || !s->active_disk->bs ||
|
||||
!s->active_disk->bs->backing) {
|
||||
active_disk = bs->file;
|
||||
if (!active_disk || !active_disk->bs || !active_disk->bs->backing) {
|
||||
error_setg(errp, "Active disk doesn't have backing file");
|
||||
aio_context_release(aio_context);
|
||||
return;
|
||||
}
|
||||
|
||||
s->hidden_disk = s->active_disk->bs->backing;
|
||||
if (!s->hidden_disk->bs || !s->hidden_disk->bs->backing) {
|
||||
hidden_disk = active_disk->bs->backing;
|
||||
if (!hidden_disk->bs || !hidden_disk->bs->backing) {
|
||||
error_setg(errp, "Hidden disk doesn't have backing file");
|
||||
aio_context_release(aio_context);
|
||||
return;
|
||||
}
|
||||
|
||||
s->secondary_disk = s->hidden_disk->bs->backing;
|
||||
if (!s->secondary_disk->bs || !bdrv_has_blk(s->secondary_disk->bs)) {
|
||||
secondary_disk = hidden_disk->bs->backing;
|
||||
if (!secondary_disk->bs || !bdrv_has_blk(secondary_disk->bs)) {
|
||||
error_setg(errp, "The secondary disk doesn't have block backend");
|
||||
aio_context_release(aio_context);
|
||||
return;
|
||||
}
|
||||
|
||||
/* verify the length */
|
||||
active_length = bdrv_getlength(s->active_disk->bs);
|
||||
hidden_length = bdrv_getlength(s->hidden_disk->bs);
|
||||
disk_length = bdrv_getlength(s->secondary_disk->bs);
|
||||
active_length = bdrv_getlength(active_disk->bs);
|
||||
hidden_length = bdrv_getlength(hidden_disk->bs);
|
||||
disk_length = bdrv_getlength(secondary_disk->bs);
|
||||
if (active_length < 0 || hidden_length < 0 || disk_length < 0 ||
|
||||
active_length != hidden_length || hidden_length != disk_length) {
|
||||
error_setg(errp, "Active disk, hidden disk, secondary disk's length"
|
||||
@ -530,10 +534,10 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
||||
}
|
||||
|
||||
/* Must be true, or the bdrv_getlength() calls would have failed */
|
||||
assert(s->active_disk->bs->drv && s->hidden_disk->bs->drv);
|
||||
assert(active_disk->bs->drv && hidden_disk->bs->drv);
|
||||
|
||||
if (!s->active_disk->bs->drv->bdrv_make_empty ||
|
||||
!s->hidden_disk->bs->drv->bdrv_make_empty) {
|
||||
if (!active_disk->bs->drv->bdrv_make_empty ||
|
||||
!hidden_disk->bs->drv->bdrv_make_empty) {
|
||||
error_setg(errp,
|
||||
"Active disk or hidden disk doesn't support make_empty");
|
||||
aio_context_release(aio_context);
|
||||
@ -548,6 +552,26 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
||||
return;
|
||||
}
|
||||
|
||||
bdrv_ref(hidden_disk->bs);
|
||||
s->hidden_disk = bdrv_attach_child(bs, hidden_disk->bs, "hidden disk",
|
||||
&child_of_bds, BDRV_CHILD_DATA,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
aio_context_release(aio_context);
|
||||
return;
|
||||
}
|
||||
|
||||
bdrv_ref(secondary_disk->bs);
|
||||
s->secondary_disk = bdrv_attach_child(bs, secondary_disk->bs,
|
||||
"secondary disk", &child_of_bds,
|
||||
BDRV_CHILD_DATA, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
aio_context_release(aio_context);
|
||||
return;
|
||||
}
|
||||
|
||||
/* start backup job now */
|
||||
error_setg(&s->blocker,
|
||||
"Block device is in use by internal backup job");
|
||||
@ -586,7 +610,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
||||
s->stage = BLOCK_REPLICATION_RUNNING;
|
||||
|
||||
if (s->mode == REPLICATION_MODE_SECONDARY) {
|
||||
secondary_do_checkpoint(s, errp);
|
||||
secondary_do_checkpoint(bs, errp);
|
||||
}
|
||||
|
||||
s->error = 0;
|
||||
@ -615,7 +639,7 @@ static void replication_do_checkpoint(ReplicationState *rs, Error **errp)
|
||||
}
|
||||
|
||||
if (s->mode == REPLICATION_MODE_SECONDARY) {
|
||||
secondary_do_checkpoint(s, errp);
|
||||
secondary_do_checkpoint(bs, errp);
|
||||
}
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
@ -652,8 +676,9 @@ static void replication_done(void *opaque, int ret)
|
||||
if (ret == 0) {
|
||||
s->stage = BLOCK_REPLICATION_DONE;
|
||||
|
||||
s->active_disk = NULL;
|
||||
bdrv_unref_child(bs, s->secondary_disk);
|
||||
s->secondary_disk = NULL;
|
||||
bdrv_unref_child(bs, s->hidden_disk);
|
||||
s->hidden_disk = NULL;
|
||||
s->error = 0;
|
||||
} else {
|
||||
@ -705,7 +730,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
|
||||
}
|
||||
|
||||
if (!failover) {
|
||||
secondary_do_checkpoint(s, errp);
|
||||
secondary_do_checkpoint(bs, errp);
|
||||
s->stage = BLOCK_REPLICATION_DONE;
|
||||
aio_context_release(aio_context);
|
||||
return;
|
||||
@ -713,7 +738,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
|
||||
|
||||
s->stage = BLOCK_REPLICATION_FAILOVER;
|
||||
s->commit_job = commit_active_start(
|
||||
NULL, s->active_disk->bs, s->secondary_disk->bs,
|
||||
NULL, bs->file->bs, s->secondary_disk->bs,
|
||||
JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT,
|
||||
NULL, replication_done, bs, true, errp);
|
||||
break;
|
||||
|
@ -3098,26 +3098,6 @@ static int coroutine_fn vvfat_co_block_status(BlockDriverState *bs,
|
||||
return BDRV_BLOCK_DATA;
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
write_target_commit(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = try_commit(s);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BlockDriver vvfat_write_target = {
|
||||
.format_name = "vvfat_write_target",
|
||||
.instance_size = sizeof(void*),
|
||||
.bdrv_co_pwritev = write_target_commit,
|
||||
};
|
||||
|
||||
static void vvfat_qcow_options(BdrvChildRole role, bool parent_is_format,
|
||||
int *child_flags, QDict *child_options,
|
||||
int parent_flags, QDict *parent_options)
|
||||
@ -3133,7 +3113,6 @@ static int enable_write_target(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
BDRVVVFATState *s = bs->opaque;
|
||||
BlockDriver *bdrv_qcow = NULL;
|
||||
BlockDriverState *backing;
|
||||
QemuOpts *opts = NULL;
|
||||
int ret;
|
||||
int size = sector2cluster(s, s->sector_count);
|
||||
@ -3184,13 +3163,6 @@ static int enable_write_target(BlockDriverState *bs, Error **errp)
|
||||
unlink(s->qcow_filename);
|
||||
#endif
|
||||
|
||||
backing = bdrv_new_open_driver(&vvfat_write_target, NULL, BDRV_O_ALLOW_RDWR,
|
||||
&error_abort);
|
||||
*(void**) backing->opaque = s;
|
||||
|
||||
bdrv_set_backing_hd(s->bs, backing, &error_abort);
|
||||
bdrv_unref(backing);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
@ -3205,17 +3177,10 @@ static void vvfat_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||
uint64_t perm, uint64_t shared,
|
||||
uint64_t *nperm, uint64_t *nshared)
|
||||
{
|
||||
if (role & BDRV_CHILD_DATA) {
|
||||
assert(role & BDRV_CHILD_DATA);
|
||||
/* This is a private node, nobody should try to attach to it */
|
||||
*nperm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE;
|
||||
*nshared = BLK_PERM_WRITE_UNCHANGED;
|
||||
} else {
|
||||
assert(role & BDRV_CHILD_COW);
|
||||
/* The backing file is there so 'commit' can use it. vvfat doesn't
|
||||
* access it in any way. */
|
||||
*nperm = 0;
|
||||
*nshared = BLK_PERM_ALL;
|
||||
}
|
||||
}
|
||||
|
||||
static void vvfat_close(BlockDriverState *bs)
|
||||
|
@ -335,7 +335,7 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
|
||||
_mcleanup();
|
||||
#endif
|
||||
gdb_exit(arg1);
|
||||
qemu_plugin_atexit_cb();
|
||||
qemu_plugin_user_exit();
|
||||
/* XXX: should free thread stack and CPU env */
|
||||
_exit(arg1);
|
||||
ret = 0; /* avoid warning */
|
||||
@ -437,7 +437,7 @@ abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1,
|
||||
_mcleanup();
|
||||
#endif
|
||||
gdb_exit(arg1);
|
||||
qemu_plugin_atexit_cb();
|
||||
qemu_plugin_user_exit();
|
||||
/* XXX: should free thread stack and CPU env */
|
||||
_exit(arg1);
|
||||
ret = 0; /* avoid warning */
|
||||
@ -516,7 +516,7 @@ abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1,
|
||||
_mcleanup();
|
||||
#endif
|
||||
gdb_exit(arg1);
|
||||
qemu_plugin_atexit_cb();
|
||||
qemu_plugin_user_exit();
|
||||
/* XXX: should free thread stack and CPU env */
|
||||
_exit(arg1);
|
||||
ret = 0; /* avoid warning */
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "qemu/sockets.h"
|
||||
#include "qapi/error.h"
|
||||
#include "chardev/char.h"
|
||||
#include "chardev/char-fe.h"
|
||||
#include "io/channel-file.h"
|
||||
|
||||
#include "chardev/char-fd.h"
|
||||
@ -38,6 +39,10 @@ static int fd_chr_write(Chardev *chr, const uint8_t *buf, int len)
|
||||
{
|
||||
FDChardev *s = FD_CHARDEV(chr);
|
||||
|
||||
if (!s->ioc_out) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return io_channel_send(s->ioc_out, buf, len);
|
||||
}
|
||||
|
||||
@ -80,10 +85,85 @@ static int fd_chr_read_poll(void *opaque)
|
||||
return s->max_size;
|
||||
}
|
||||
|
||||
typedef struct FDSource {
|
||||
GSource parent;
|
||||
|
||||
GIOCondition cond;
|
||||
} FDSource;
|
||||
|
||||
static gboolean
|
||||
fd_source_prepare(GSource *source,
|
||||
gint *timeout_)
|
||||
{
|
||||
FDSource *src = (FDSource *)source;
|
||||
|
||||
return src->cond != 0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fd_source_check(GSource *source)
|
||||
{
|
||||
FDSource *src = (FDSource *)source;
|
||||
|
||||
return src->cond != 0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fd_source_dispatch(GSource *source, GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
FDSource *src = (FDSource *)source;
|
||||
FEWatchFunc func = (FEWatchFunc)callback;
|
||||
gboolean ret = G_SOURCE_CONTINUE;
|
||||
|
||||
if (src->cond) {
|
||||
ret = func(NULL, src->cond, user_data);
|
||||
src->cond = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GSourceFuncs fd_source_funcs = {
|
||||
fd_source_prepare,
|
||||
fd_source_check,
|
||||
fd_source_dispatch,
|
||||
NULL, NULL, NULL
|
||||
};
|
||||
|
||||
static GSource *fd_source_new(FDChardev *chr)
|
||||
{
|
||||
return g_source_new(&fd_source_funcs, sizeof(FDSource));
|
||||
}
|
||||
|
||||
static gboolean child_func(GIOChannel *source,
|
||||
GIOCondition condition,
|
||||
gpointer data)
|
||||
{
|
||||
FDSource *parent = data;
|
||||
|
||||
parent->cond |= condition;
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static GSource *fd_chr_add_watch(Chardev *chr, GIOCondition cond)
|
||||
{
|
||||
FDChardev *s = FD_CHARDEV(chr);
|
||||
return qio_channel_create_watch(s->ioc_out, cond);
|
||||
g_autoptr(GSource) source = fd_source_new(s);
|
||||
|
||||
if (s->ioc_out) {
|
||||
g_autoptr(GSource) child = qio_channel_create_watch(s->ioc_out, cond & ~G_IO_IN);
|
||||
g_source_set_callback(child, (GSourceFunc)child_func, source, NULL);
|
||||
g_source_add_child_source(source, child);
|
||||
}
|
||||
if (s->ioc_in) {
|
||||
g_autoptr(GSource) child = qio_channel_create_watch(s->ioc_in, cond & ~G_IO_OUT);
|
||||
g_source_set_callback(child, (GSourceFunc)child_func, source, NULL);
|
||||
g_source_add_child_source(source, child);
|
||||
}
|
||||
|
||||
return g_steal_pointer(&source);
|
||||
}
|
||||
|
||||
static void fd_chr_update_read_handler(Chardev *chr)
|
||||
@ -131,17 +211,32 @@ void qemu_chr_open_fd(Chardev *chr,
|
||||
int fd_in, int fd_out)
|
||||
{
|
||||
FDChardev *s = FD_CHARDEV(chr);
|
||||
char *name;
|
||||
g_autofree char *name = NULL;
|
||||
|
||||
if (fd_out >= 0) {
|
||||
qemu_set_nonblock(fd_out);
|
||||
}
|
||||
|
||||
if (fd_out == fd_in && fd_in >= 0) {
|
||||
s->ioc_in = QIO_CHANNEL(qio_channel_file_new_fd(fd_in));
|
||||
name = g_strdup_printf("chardev-file-%s", chr->label);
|
||||
qio_channel_set_name(QIO_CHANNEL(s->ioc_in), name);
|
||||
s->ioc_out = QIO_CHANNEL(object_ref(s->ioc_in));
|
||||
return;
|
||||
}
|
||||
|
||||
if (fd_in >= 0) {
|
||||
s->ioc_in = QIO_CHANNEL(qio_channel_file_new_fd(fd_in));
|
||||
name = g_strdup_printf("chardev-file-in-%s", chr->label);
|
||||
qio_channel_set_name(QIO_CHANNEL(s->ioc_in), name);
|
||||
g_free(name);
|
||||
}
|
||||
|
||||
if (fd_out >= 0) {
|
||||
s->ioc_out = QIO_CHANNEL(qio_channel_file_new_fd(fd_out));
|
||||
g_free(name);
|
||||
name = g_strdup_printf("chardev-file-out-%s", chr->label);
|
||||
qio_channel_set_name(QIO_CHANNEL(s->ioc_out), name);
|
||||
g_free(name);
|
||||
qemu_set_nonblock(fd_out);
|
||||
}
|
||||
}
|
||||
|
||||
static void char_fd_class_init(ObjectClass *oc, void *data)
|
||||
|
@ -354,7 +354,7 @@ void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
|
||||
}
|
||||
|
||||
guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond,
|
||||
GIOFunc func, void *user_data)
|
||||
FEWatchFunc func, void *user_data)
|
||||
{
|
||||
Chardev *s = be->chr;
|
||||
GSource *src;
|
||||
|
@ -468,9 +468,9 @@ static char *qemu_chr_socket_address(SocketChardev *s, const char *prefix)
|
||||
|
||||
#ifdef CONFIG_LINUX
|
||||
if (sa->has_abstract && sa->abstract) {
|
||||
abstract = ",abstract";
|
||||
abstract = ",abstract=on";
|
||||
if (sa->has_tight && sa->tight) {
|
||||
tight = ",tight";
|
||||
tight = ",tight=on";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -1031,27 +1031,31 @@ Chardev *qemu_chardev_new(const char *id, const char *typename,
|
||||
ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
|
||||
Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
const ChardevClass *cc;
|
||||
ChardevReturn *ret;
|
||||
Chardev *chr;
|
||||
g_autoptr(Chardev) chr = NULL;
|
||||
|
||||
if (qemu_chr_find(id)) {
|
||||
error_setg(errp, "Chardev with id '%s' already exists", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cc = char_get_class(ChardevBackendKind_str(backend->type), errp);
|
||||
if (!cc) {
|
||||
return NULL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
chr = chardev_new(id, object_class_get_name(OBJECT_CLASS(cc)),
|
||||
backend, NULL, false, errp);
|
||||
if (!chr) {
|
||||
return NULL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!object_property_try_add_child(get_chardevs_root(), id, OBJECT(chr),
|
||||
errp)) {
|
||||
object_unref(OBJECT(chr));
|
||||
return NULL;
|
||||
goto err;
|
||||
}
|
||||
object_unref(OBJECT(chr));
|
||||
|
||||
ret = g_new0(ChardevReturn, 1);
|
||||
if (CHARDEV_IS_PTY(chr)) {
|
||||
@ -1060,6 +1064,10 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
err:
|
||||
error_prepend(errp, "Failed to add chardev '%s': ", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ChardevReturn *qmp_chardev_change(const char *id, ChardevBackend *backend,
|
||||
|
@ -382,6 +382,7 @@ static const TypeInfo char_spicevmc_type_info = {
|
||||
.parent = TYPE_CHARDEV_SPICE,
|
||||
.class_init = char_spicevmc_class_init,
|
||||
};
|
||||
module_obj(TYPE_CHARDEV_SPICEVMC);
|
||||
|
||||
static void char_spiceport_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
|
@ -8,8 +8,4 @@ CONFIG_ATI_VGA=y
|
||||
CONFIG_RTL8139_PCI=y
|
||||
CONFIG_JAZZ=y
|
||||
CONFIG_VT82C686=y
|
||||
CONFIG_AHCI=y
|
||||
CONFIG_MIPS_BOSTON=y
|
||||
CONFIG_FITLOADER=y
|
||||
CONFIG_PCI_EXPRESS=y
|
||||
CONFIG_PCI_EXPRESS_XILINX=y
|
||||
|
@ -1,7 +1,6 @@
|
||||
# Default configuration for ppc-softmmu
|
||||
|
||||
# For embedded PPCs:
|
||||
CONFIG_DS1338=y
|
||||
CONFIG_E500=y
|
||||
CONFIG_PPC405=y
|
||||
CONFIG_PPC440=y
|
||||
|
@ -1 +1,2 @@
|
||||
CONFIG_TRICORE_TESTBOARD=y
|
||||
CONFIG_TRIBOARD=y
|
||||
|
223
configure
vendored
223
configure
vendored
@ -243,6 +243,7 @@ cross_prefix=""
|
||||
audio_drv_list=""
|
||||
block_drv_rw_whitelist=""
|
||||
block_drv_ro_whitelist=""
|
||||
block_drv_whitelist_tools="no"
|
||||
host_cc="cc"
|
||||
audio_win_int=""
|
||||
libs_qga=""
|
||||
@ -304,14 +305,14 @@ virtiofsd="auto"
|
||||
virtfs="auto"
|
||||
libudev="auto"
|
||||
mpath="auto"
|
||||
vnc="enabled"
|
||||
vnc="auto"
|
||||
sparse="auto"
|
||||
vde="$default_feature"
|
||||
vnc_sasl="auto"
|
||||
vnc_jpeg="auto"
|
||||
vnc_png="auto"
|
||||
xkbcommon="auto"
|
||||
xen="$default_feature"
|
||||
xen=${default_feature:+disabled}
|
||||
xen_ctrl_version="$default_feature"
|
||||
xen_pci_passthrough="auto"
|
||||
linux_aio="$default_feature"
|
||||
@ -321,6 +322,7 @@ attr="auto"
|
||||
xfs="$default_feature"
|
||||
tcg="enabled"
|
||||
membarrier="$default_feature"
|
||||
vhost_kernel="$default_feature"
|
||||
vhost_net="$default_feature"
|
||||
vhost_crypto="$default_feature"
|
||||
vhost_scsi="$default_feature"
|
||||
@ -328,6 +330,7 @@ vhost_vsock="$default_feature"
|
||||
vhost_user="no"
|
||||
vhost_user_blk_server="auto"
|
||||
vhost_user_fs="$default_feature"
|
||||
vhost_vdpa="$default_feature"
|
||||
bpf="auto"
|
||||
kvm="auto"
|
||||
hax="auto"
|
||||
@ -429,7 +432,7 @@ libxml2="auto"
|
||||
debug_mutex="no"
|
||||
libpmem="auto"
|
||||
default_devices="true"
|
||||
plugins="no"
|
||||
plugins="$default_feature"
|
||||
fuzzing="no"
|
||||
rng_none="no"
|
||||
secret_keyring="$default_feature"
|
||||
@ -710,6 +713,7 @@ MINGW32*)
|
||||
audio_drv_list=""
|
||||
fi
|
||||
supported_os="yes"
|
||||
plugins="no"
|
||||
pie="no"
|
||||
;;
|
||||
GNU/kFreeBSD)
|
||||
@ -768,7 +772,8 @@ SunOS)
|
||||
;;
|
||||
Haiku)
|
||||
haiku="yes"
|
||||
QEMU_CFLAGS="-DB_USE_POSITIVE_POSIX_ERRORS -D_BSD_SOURCE $QEMU_CFLAGS"
|
||||
pie="no"
|
||||
QEMU_CFLAGS="-DB_USE_POSITIVE_POSIX_ERRORS -D_BSD_SOURCE -fPIC $QEMU_CFLAGS"
|
||||
;;
|
||||
Linux)
|
||||
audio_drv_list="try-pa oss"
|
||||
@ -1017,6 +1022,10 @@ for opt do
|
||||
;;
|
||||
--block-drv-ro-whitelist=*) block_drv_ro_whitelist=$(echo "$optarg" | sed -e 's/,/ /g')
|
||||
;;
|
||||
--enable-block-drv-whitelist-in-tools) block_drv_whitelist_tools="yes"
|
||||
;;
|
||||
--disable-block-drv-whitelist-in-tools) block_drv_whitelist_tools="no"
|
||||
;;
|
||||
--enable-debug-tcg) debug_tcg="yes"
|
||||
;;
|
||||
--disable-debug-tcg) debug_tcg="no"
|
||||
@ -1112,6 +1121,7 @@ for opt do
|
||||
--enable-cap-ng) cap_ng="enabled"
|
||||
;;
|
||||
--disable-tcg) tcg="disabled"
|
||||
plugins="no"
|
||||
;;
|
||||
--enable-tcg) tcg="enabled"
|
||||
;;
|
||||
@ -1523,7 +1533,11 @@ for opt do
|
||||
;;
|
||||
--disable-xkbcommon) xkbcommon="disabled"
|
||||
;;
|
||||
--enable-plugins) plugins="yes"
|
||||
--enable-plugins) if test "$mingw32" = "yes"; then
|
||||
error_exit "TCG plugins not currently supported on Windows platforms"
|
||||
else
|
||||
plugins="yes"
|
||||
fi
|
||||
;;
|
||||
--disable-plugins) plugins="no"
|
||||
;;
|
||||
@ -1584,6 +1598,11 @@ for opt do
|
||||
esac
|
||||
done
|
||||
|
||||
# test for any invalid configuration combinations
|
||||
if test "$plugins" = "yes" -a "$tcg" = "disabled"; then
|
||||
error_exit "Can't enable plugins on non-TCG builds"
|
||||
fi
|
||||
|
||||
case $git_submodules_action in
|
||||
update|validate)
|
||||
if test ! -e "$source_path/.git"; then
|
||||
@ -1798,10 +1817,12 @@ Advanced options (experts only):
|
||||
--block-drv-whitelist=L Same as --block-drv-rw-whitelist=L
|
||||
--block-drv-rw-whitelist=L
|
||||
set block driver read-write whitelist
|
||||
(affects only QEMU, not qemu-img)
|
||||
(by default affects only QEMU, not tools like qemu-img)
|
||||
--block-drv-ro-whitelist=L
|
||||
set block driver read-only whitelist
|
||||
(affects only QEMU, not qemu-img)
|
||||
(by default affects only QEMU, not tools like qemu-img)
|
||||
--enable-block-drv-whitelist-in-tools
|
||||
use block whitelist also in tools instead of only QEMU
|
||||
--enable-trace-backends=B Set trace backend
|
||||
Available backends: $trace_backend_list
|
||||
--with-trace-file=NAME Full PATH,NAME of file to store traces
|
||||
@ -2200,11 +2221,16 @@ if test "$modules" = "no" && test "$module_upgrades" = "yes" ; then
|
||||
error_exit "Can't enable module-upgrades as Modules are not enabled"
|
||||
fi
|
||||
|
||||
# Static linking is not possible with modules or PIE
|
||||
# Static linking is not possible with plugins, modules or PIE
|
||||
if test "$static" = "yes" ; then
|
||||
if test "$modules" = "yes" ; then
|
||||
error_exit "static and modules are mutually incompatible"
|
||||
fi
|
||||
if test "$plugins" = "yes"; then
|
||||
error_exit "static and plugins are mutually incompatible"
|
||||
else
|
||||
plugins="no"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Unconditional check for compiler __thread support
|
||||
@ -2358,24 +2384,27 @@ feature_not_found() {
|
||||
# ---
|
||||
# big/little endian test
|
||||
cat > $TMPC << EOF
|
||||
#include <stdio.h>
|
||||
short big_endian[] = { 0x4269, 0x4765, 0x4e64, 0x4961, 0x4e00, 0, };
|
||||
short little_endian[] = { 0x694c, 0x7454, 0x654c, 0x6e45, 0x6944, 0x6e41, 0, };
|
||||
extern int foo(short *, short *);
|
||||
int main(int argc, char *argv[]) {
|
||||
return foo(big_endian, little_endian);
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return printf("%s %s\n", (char *)big_endian, (char *)little_endian);
|
||||
}
|
||||
EOF
|
||||
|
||||
if compile_object ; then
|
||||
if strings -a $TMPO | grep -q BiGeNdIaN ; then
|
||||
if compile_prog ; then
|
||||
if strings -a $TMPE | grep -q BiGeNdIaN ; then
|
||||
bigendian="yes"
|
||||
elif strings -a $TMPO | grep -q LiTtLeEnDiAn ; then
|
||||
elif strings -a $TMPE | grep -q LiTtLeEnDiAn ; then
|
||||
bigendian="no"
|
||||
else
|
||||
echo big/little test failed
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo big/little test failed
|
||||
exit 1
|
||||
fi
|
||||
|
||||
##########################################
|
||||
@ -3097,6 +3126,69 @@ for drv in $audio_drv_list; do
|
||||
esac
|
||||
done
|
||||
|
||||
##########################################
|
||||
# plugin linker support probe
|
||||
|
||||
if test "$plugins" != "no"; then
|
||||
|
||||
#########################################
|
||||
# See if --dynamic-list is supported by the linker
|
||||
|
||||
ld_dynamic_list="no"
|
||||
cat > $TMPTXT <<EOF
|
||||
{
|
||||
foo;
|
||||
};
|
||||
EOF
|
||||
|
||||
cat > $TMPC <<EOF
|
||||
#include <stdio.h>
|
||||
void foo(void);
|
||||
|
||||
void foo(void)
|
||||
{
|
||||
printf("foo\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
foo();
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
|
||||
if compile_prog "" "-Wl,--dynamic-list=$TMPTXT" ; then
|
||||
ld_dynamic_list="yes"
|
||||
fi
|
||||
|
||||
#########################################
|
||||
# See if -exported_symbols_list is supported by the linker
|
||||
|
||||
ld_exported_symbols_list="no"
|
||||
cat > $TMPTXT <<EOF
|
||||
_foo
|
||||
EOF
|
||||
|
||||
if compile_prog "" "-Wl,-exported_symbols_list,$TMPTXT" ; then
|
||||
ld_exported_symbols_list="yes"
|
||||
fi
|
||||
|
||||
if test "$ld_dynamic_list" = "no" &&
|
||||
test "$ld_exported_symbols_list" = "no" ; then
|
||||
if test "$plugins" = "yes"; then
|
||||
error_exit \
|
||||
"Plugin support requires dynamic linking and specifying a set of symbols " \
|
||||
"that are exported to plugins. Unfortunately your linker doesn't " \
|
||||
"support the flag (--dynamic-list or -exported_symbols_list) used " \
|
||||
"for this purpose."
|
||||
else
|
||||
plugins="no"
|
||||
fi
|
||||
else
|
||||
plugins="yes"
|
||||
fi
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# glib support probe
|
||||
|
||||
@ -3105,7 +3197,7 @@ glib_modules=gthread-2.0
|
||||
if test "$modules" = yes; then
|
||||
glib_modules="$glib_modules gmodule-export-2.0"
|
||||
fi
|
||||
if test "$plugins" = yes; then
|
||||
if test "$plugins" = "yes"; then
|
||||
glib_modules="$glib_modules gmodule-2.0"
|
||||
fi
|
||||
|
||||
@ -3193,18 +3285,6 @@ if ! compile_prog "$glib_cflags" "$glib_libs" ; then
|
||||
"build target"
|
||||
fi
|
||||
|
||||
# Silence clang 3.5.0 warnings about glib attribute __alloc_size__ usage
|
||||
cat > $TMPC << EOF
|
||||
#include <glib.h>
|
||||
int main(void) { return 0; }
|
||||
EOF
|
||||
if ! compile_prog "$glib_cflags -Werror" "$glib_libs" ; then
|
||||
if cc_has_warning_flag "-Wno-unknown-attributes"; then
|
||||
glib_cflags="-Wno-unknown-attributes $glib_cflags"
|
||||
CONFIGURE_CFLAGS="-Wno-unknown-attributes $CONFIGURE_CFLAGS"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Silence clang warnings triggered by glib < 2.57.2
|
||||
cat > $TMPC << EOF
|
||||
#include <glib.h>
|
||||
@ -3615,21 +3695,8 @@ fi
|
||||
##########################################
|
||||
# For 'ust' backend, test if ust headers are present
|
||||
if have_backend "ust"; then
|
||||
cat > $TMPC << EOF
|
||||
#include <lttng/tracepoint.h>
|
||||
int main(void) { return 0; }
|
||||
EOF
|
||||
if compile_prog "" "-Wl,--no-as-needed -ldl" ; then
|
||||
if $pkg_config lttng-ust --exists; then
|
||||
lttng_ust_libs=$($pkg_config --libs lttng-ust)
|
||||
else
|
||||
lttng_ust_libs="-llttng-ust -ldl"
|
||||
fi
|
||||
if $pkg_config liburcu-bp --exists; then
|
||||
urcu_bp_libs=$($pkg_config --libs liburcu-bp)
|
||||
else
|
||||
urcu_bp_libs="-lurcu-bp"
|
||||
fi
|
||||
else
|
||||
error_exit "Trace backend 'ust' missing lttng-ust header files"
|
||||
fi
|
||||
@ -3824,7 +3891,7 @@ static int bar(void *a) {
|
||||
}
|
||||
int main(int argc, char *argv[]) { return bar(argv[0]); }
|
||||
EOF
|
||||
if compile_object "" ; then
|
||||
if compile_object "-Werror" ; then
|
||||
avx2_opt="yes"
|
||||
else
|
||||
avx2_opt="no"
|
||||
@ -3854,7 +3921,7 @@ int main(int argc, char *argv[])
|
||||
return bar(argv[0]);
|
||||
}
|
||||
EOF
|
||||
if ! compile_object "" ; then
|
||||
if ! compile_object "-Werror" ; then
|
||||
avx512f_opt="no"
|
||||
fi
|
||||
else
|
||||
@ -3924,18 +3991,11 @@ cat > $TMPC << EOF
|
||||
int main(void)
|
||||
{
|
||||
uint64_t x = 0, y = 0;
|
||||
#ifdef __ATOMIC_RELAXED
|
||||
y = __atomic_load_n(&x, __ATOMIC_RELAXED);
|
||||
__atomic_store_n(&x, y, __ATOMIC_RELAXED);
|
||||
__atomic_compare_exchange_n(&x, &y, x, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED);
|
||||
__atomic_exchange_n(&x, y, __ATOMIC_RELAXED);
|
||||
__atomic_fetch_add(&x, y, __ATOMIC_RELAXED);
|
||||
#else
|
||||
typedef char is_host64[sizeof(void *) >= sizeof(uint64_t) ? 1 : -1];
|
||||
__sync_lock_test_and_set(&x, y);
|
||||
__sync_val_compare_and_swap(&x, y, 0);
|
||||
__sync_fetch_and_add(&x, y);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
@ -3943,61 +4003,6 @@ if compile_prog "" "" ; then
|
||||
atomic64=yes
|
||||
fi
|
||||
|
||||
#########################################
|
||||
# See if --dynamic-list is supported by the linker
|
||||
ld_dynamic_list="no"
|
||||
if test "$static" = "no" ; then
|
||||
cat > $TMPTXT <<EOF
|
||||
{
|
||||
foo;
|
||||
};
|
||||
EOF
|
||||
|
||||
cat > $TMPC <<EOF
|
||||
#include <stdio.h>
|
||||
void foo(void);
|
||||
|
||||
void foo(void)
|
||||
{
|
||||
printf("foo\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
foo();
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
|
||||
if compile_prog "" "-Wl,--dynamic-list=$TMPTXT" ; then
|
||||
ld_dynamic_list="yes"
|
||||
fi
|
||||
fi
|
||||
|
||||
#########################################
|
||||
# See if -exported_symbols_list is supported by the linker
|
||||
|
||||
ld_exported_symbols_list="no"
|
||||
if test "$static" = "no" ; then
|
||||
cat > $TMPTXT <<EOF
|
||||
_foo
|
||||
EOF
|
||||
|
||||
if compile_prog "" "-Wl,-exported_symbols_list,$TMPTXT" ; then
|
||||
ld_exported_symbols_list="yes"
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$plugins" = "yes" &&
|
||||
test "$ld_dynamic_list" = "no" &&
|
||||
test "$ld_exported_symbols_list" = "no" ; then
|
||||
error_exit \
|
||||
"Plugin support requires dynamic linking and specifying a set of symbols " \
|
||||
"that are exported to plugins. Unfortunately your linker doesn't " \
|
||||
"support the flag (--dynamic-list or -exported_symbols_list) used " \
|
||||
"for this purpose. You can't build with --static."
|
||||
fi
|
||||
|
||||
########################################
|
||||
# check if getauxval is available.
|
||||
|
||||
@ -4578,6 +4583,9 @@ if test "$audio_win_int" = "yes" ; then
|
||||
fi
|
||||
echo "CONFIG_BDRV_RW_WHITELIST=$block_drv_rw_whitelist" >> $config_host_mak
|
||||
echo "CONFIG_BDRV_RO_WHITELIST=$block_drv_ro_whitelist" >> $config_host_mak
|
||||
if test "$block_drv_whitelist_tools" = "yes" ; then
|
||||
echo "CONFIG_BDRV_WHITELIST_TOOLS=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$xfs" = "yes" ; then
|
||||
echo "CONFIG_XFS=y" >> $config_host_mak
|
||||
fi
|
||||
@ -4782,7 +4790,6 @@ fi
|
||||
if have_backend "ust"; then
|
||||
echo "CONFIG_TRACE_UST=y" >> $config_host_mak
|
||||
echo "LTTNG_UST_LIBS=$lttng_ust_libs" >> $config_host_mak
|
||||
echo "URCU_BP_LIBS=$urcu_bp_libs" >> $config_host_mak
|
||||
fi
|
||||
if have_backend "dtrace"; then
|
||||
echo "CONFIG_TRACE_DTRACE=y" >> $config_host_mak
|
||||
@ -5106,12 +5113,10 @@ if test "$skip_meson" = no; then
|
||||
echo "[properties]" >> $cross
|
||||
|
||||
# unroll any custom device configs
|
||||
if test -n "$device_archs"; then
|
||||
for a in $device_archs; do
|
||||
eval "c=\$devices_${a}"
|
||||
echo "${a}-softmmu = '$c'" >> $cross
|
||||
done
|
||||
fi
|
||||
|
||||
test -z "$cxx" && echo "link_language = 'c'" >> $cross
|
||||
echo "[built-in options]" >> $cross
|
||||
@ -5214,7 +5219,7 @@ if test "$skip_meson" = no; then
|
||||
-Ddocs=$docs -Dsphinx_build=$sphinx_build -Dinstall_blobs=$blobs \
|
||||
-Dvhost_user_blk_server=$vhost_user_blk_server -Dmultiprocess=$multiprocess \
|
||||
-Dfuse=$fuse -Dfuse_lseek=$fuse_lseek -Dguest_agent_msi=$guest_agent_msi -Dbpf=$bpf\
|
||||
$(if test "$default_features" = no; then echo "-Dauto_features=disabled"; fi) \
|
||||
$(if test "$default_feature" = no; then echo "-Dauto_features=disabled"; fi) \
|
||||
-Dtcg_interpreter=$tcg_interpreter \
|
||||
$cross_arg \
|
||||
"$PWD" "$source_path"
|
||||
@ -5235,7 +5240,7 @@ fi
|
||||
|
||||
if test -n "${deprecated_features}"; then
|
||||
echo "Warning, deprecated features enabled."
|
||||
echo "Please see docs/system/deprecated.rst"
|
||||
echo "Please see docs/about/deprecated.rst"
|
||||
echo " features: ${deprecated_features}"
|
||||
fi
|
||||
|
||||
|
@ -31,6 +31,12 @@ pbrook@c046a42c-6fe2-441c-8c8c-71466251a162 paul@codesourcery.com
|
||||
ths@c046a42c-6fe2-441c-8c8c-71466251a162 ths@networkno.de
|
||||
malc@c046a42c-6fe2-441c-8c8c-71466251a162 av1474@comtv.ru
|
||||
|
||||
# canonical emails
|
||||
liq3ea@163.com liq3ea@gmail.com
|
||||
|
||||
# some broken tags
|
||||
yuval.shaia.ml.gmail.com yuval.shaia.ml@gmail.com
|
||||
|
||||
# There is also a:
|
||||
# (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
# for the cvs2svn initialization commit e63c3dc74bf.
|
||||
|
@ -9,6 +9,8 @@ baidu.com Baidu
|
||||
bytedance.com ByteDance
|
||||
cmss.chinamobile.com China Mobile
|
||||
citrix.com Citrix
|
||||
crudebyte.com Crudebyte
|
||||
eldorado.org.br Instituto de Pesquisas Eldorado
|
||||
fujitsu.com Fujitsu
|
||||
google.com Google
|
||||
greensocs.com GreenSocs
|
||||
@ -17,20 +19,25 @@ ibm.com IBM
|
||||
igalia.com Igalia
|
||||
intel.com Intel
|
||||
linaro.org Linaro
|
||||
lwn.net LWN
|
||||
microsoft.com Microsoft
|
||||
mvista.com MontaVista
|
||||
nokia.com Nokia
|
||||
nuviainc.com NUVIA
|
||||
nvidia.com NVIDIA
|
||||
oracle.com Oracle
|
||||
proxmox.com Proxmox
|
||||
quicinc.com Qualcomm Innovation Center
|
||||
redhat.com Red Hat
|
||||
rt-rk.com RT-RK
|
||||
samsung.com Samsung
|
||||
siemens.com Siemens
|
||||
sifive.com SiFive
|
||||
suse.com SUSE
|
||||
suse.de SUSE
|
||||
virtuozzo.com Virtuozzo
|
||||
wdc.com Western Digital
|
||||
windriver.com Wind River
|
||||
xilinx.com Xilinx
|
||||
yadro.com YADRO
|
||||
yandex-team.ru Yandex
|
||||
|
@ -16,3 +16,6 @@ cota@braap.org
|
||||
uni-paderborn.de
|
||||
edu
|
||||
edu.cn
|
||||
|
||||
# Boston University
|
||||
bu.edu
|
||||
|
@ -29,3 +29,8 @@ mrolnik@gmail.com
|
||||
huth@tuxfamily.org
|
||||
jhogan@kernel.org
|
||||
atar4qemu@gmail.com
|
||||
minwoo.im.dev@gmail.com
|
||||
bmeng.cn@gmail.com
|
||||
liq3ea@gmail.com
|
||||
chetan4windows@gmail.com
|
||||
akihiko.odaki@gmail.com
|
||||
|
13
contrib/gitdm/group-map-interns
Normal file
13
contrib/gitdm/group-map-interns
Normal file
@ -0,0 +1,13 @@
|
||||
#
|
||||
# Group together everyone working as an intern via one of the various
|
||||
# outreach programs.
|
||||
#
|
||||
|
||||
# GSoC 2020 Virtual FIDO/U2F security key
|
||||
cesar.belley@lse.epita.fr
|
||||
|
||||
# GSoC 2020 TCG performance
|
||||
ahmedkhaledkaraman@gmail.com
|
||||
|
||||
# GSoC 2021 TCG plugins
|
||||
ma.mandourr@gmail.com
|
5
contrib/gitdm/group-map-netflix
Normal file
5
contrib/gitdm/group-map-netflix
Normal file
@ -0,0 +1,5 @@
|
||||
#
|
||||
# Netflix contributors using their personal emails
|
||||
#
|
||||
|
||||
imp@bsdimp.com
|
7
contrib/gitdm/group-map-robots
Normal file
7
contrib/gitdm/group-map-robots
Normal file
@ -0,0 +1,7 @@
|
||||
#
|
||||
# There are various automatic robots that occasionally scan and report
|
||||
# bugs. Let's group them together here.
|
||||
#
|
||||
|
||||
# Euler Robot
|
||||
euler.robot@huawei.com
|
@ -13,18 +13,20 @@ include $(BUILD_DIR)/config-host.mak
|
||||
VPATH += $(SRC_PATH)/contrib/plugins
|
||||
|
||||
NAMES :=
|
||||
NAMES += execlog
|
||||
NAMES += hotblocks
|
||||
NAMES += hotpages
|
||||
NAMES += howvec
|
||||
NAMES += lockstep
|
||||
NAMES += hwprofile
|
||||
NAMES += cache
|
||||
|
||||
SONAMES := $(addsuffix .so,$(addprefix lib,$(NAMES)))
|
||||
|
||||
# The main QEMU uses Glib extensively so it's perfectly fine to use it
|
||||
# in plugins (which many example do).
|
||||
CFLAGS = $(GLIB_CFLAGS)
|
||||
CFLAGS += -fPIC
|
||||
CFLAGS += -fPIC -Wall $(filter -W%, $(QEMU_CFLAGS))
|
||||
CFLAGS += $(if $(findstring no-psabi,$(QEMU_CFLAGS)),-Wpsabi)
|
||||
CFLAGS += -I$(SRC_PATH)/include/qemu
|
||||
|
||||
|
640
contrib/plugins/cache.c
Normal file
640
contrib/plugins/cache.c
Normal file
@ -0,0 +1,640 @@
|
||||
/*
|
||||
* Copyright (C) 2021, Mahmoud Mandour <ma.mandourr@gmail.com>
|
||||
*
|
||||
* License: GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include <qemu-plugin.h>
|
||||
|
||||
QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
|
||||
|
||||
static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW;
|
||||
|
||||
static GHashTable *miss_ht;
|
||||
|
||||
static GMutex mtx;
|
||||
static GRand *rng;
|
||||
|
||||
static int limit;
|
||||
static bool sys;
|
||||
|
||||
static uint64_t dmem_accesses;
|
||||
static uint64_t dmisses;
|
||||
|
||||
static uint64_t imem_accesses;
|
||||
static uint64_t imisses;
|
||||
|
||||
enum EvictionPolicy {
|
||||
LRU,
|
||||
FIFO,
|
||||
RAND,
|
||||
};
|
||||
|
||||
enum EvictionPolicy policy;
|
||||
|
||||
/*
|
||||
* A CacheSet is a set of cache blocks. A memory block that maps to a set can be
|
||||
* put in any of the blocks inside the set. The number of block per set is
|
||||
* called the associativity (assoc).
|
||||
*
|
||||
* Each block contains the the stored tag and a valid bit. Since this is not
|
||||
* a functional simulator, the data itself is not stored. We only identify
|
||||
* whether a block is in the cache or not by searching for its tag.
|
||||
*
|
||||
* In order to search for memory data in the cache, the set identifier and tag
|
||||
* are extracted from the address and the set is probed to see whether a tag
|
||||
* match occur.
|
||||
*
|
||||
* An address is logically divided into three portions: The block offset,
|
||||
* the set number, and the tag.
|
||||
*
|
||||
* The set number is used to identify the set in which the block may exist.
|
||||
* The tag is compared against all the tags of a set to search for a match. If a
|
||||
* match is found, then the access is a hit.
|
||||
*
|
||||
* The CacheSet also contains bookkeaping information about eviction details.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
uint64_t tag;
|
||||
bool valid;
|
||||
} CacheBlock;
|
||||
|
||||
typedef struct {
|
||||
CacheBlock *blocks;
|
||||
uint64_t *lru_priorities;
|
||||
uint64_t lru_gen_counter;
|
||||
GQueue *fifo_queue;
|
||||
} CacheSet;
|
||||
|
||||
typedef struct {
|
||||
CacheSet *sets;
|
||||
int num_sets;
|
||||
int cachesize;
|
||||
int assoc;
|
||||
int blksize_shift;
|
||||
uint64_t set_mask;
|
||||
uint64_t tag_mask;
|
||||
} Cache;
|
||||
|
||||
typedef struct {
|
||||
char *disas_str;
|
||||
const char *symbol;
|
||||
uint64_t addr;
|
||||
uint64_t dmisses;
|
||||
uint64_t imisses;
|
||||
} InsnData;
|
||||
|
||||
void (*update_hit)(Cache *cache, int set, int blk);
|
||||
void (*update_miss)(Cache *cache, int set, int blk);
|
||||
|
||||
void (*metadata_init)(Cache *cache);
|
||||
void (*metadata_destroy)(Cache *cache);
|
||||
|
||||
Cache *dcache, *icache;
|
||||
|
||||
static int pow_of_two(int num)
|
||||
{
|
||||
g_assert((num & (num - 1)) == 0);
|
||||
int ret = 0;
|
||||
while (num /= 2) {
|
||||
ret++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* LRU evection policy: For each set, a generation counter is maintained
|
||||
* alongside a priority array.
|
||||
*
|
||||
* On each set access, the generation counter is incremented.
|
||||
*
|
||||
* On a cache hit: The hit-block is assigned the current generation counter,
|
||||
* indicating that it is the most recently used block.
|
||||
*
|
||||
* On a cache miss: The block with the least priority is searched and replaced
|
||||
* with the newly-cached block, of which the priority is set to the current
|
||||
* generation number.
|
||||
*/
|
||||
|
||||
static void lru_priorities_init(Cache *cache)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cache->num_sets; i++) {
|
||||
cache->sets[i].lru_priorities = g_new0(uint64_t, cache->assoc);
|
||||
cache->sets[i].lru_gen_counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void lru_update_blk(Cache *cache, int set_idx, int blk_idx)
|
||||
{
|
||||
CacheSet *set = &cache->sets[set_idx];
|
||||
set->lru_priorities[blk_idx] = cache->sets[set_idx].lru_gen_counter;
|
||||
set->lru_gen_counter++;
|
||||
}
|
||||
|
||||
static int lru_get_lru_block(Cache *cache, int set_idx)
|
||||
{
|
||||
int i, min_idx, min_priority;
|
||||
|
||||
min_priority = cache->sets[set_idx].lru_priorities[0];
|
||||
min_idx = 0;
|
||||
|
||||
for (i = 1; i < cache->assoc; i++) {
|
||||
if (cache->sets[set_idx].lru_priorities[i] < min_priority) {
|
||||
min_priority = cache->sets[set_idx].lru_priorities[i];
|
||||
min_idx = i;
|
||||
}
|
||||
}
|
||||
return min_idx;
|
||||
}
|
||||
|
||||
static void lru_priorities_destroy(Cache *cache)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cache->num_sets; i++) {
|
||||
g_free(cache->sets[i].lru_priorities);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* FIFO eviction policy: a FIFO queue is maintained for each CacheSet that
|
||||
* stores accesses to the cache.
|
||||
*
|
||||
* On a compulsory miss: The block index is enqueued to the fifo_queue to
|
||||
* indicate that it's the latest cached block.
|
||||
*
|
||||
* On a conflict miss: The first-in block is removed from the cache and the new
|
||||
* block is put in its place and enqueued to the FIFO queue.
|
||||
*/
|
||||
|
||||
static void fifo_init(Cache *cache)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cache->num_sets; i++) {
|
||||
cache->sets[i].fifo_queue = g_queue_new();
|
||||
}
|
||||
}
|
||||
|
||||
static int fifo_get_first_block(Cache *cache, int set)
|
||||
{
|
||||
GQueue *q = cache->sets[set].fifo_queue;
|
||||
return GPOINTER_TO_INT(g_queue_pop_tail(q));
|
||||
}
|
||||
|
||||
static void fifo_update_on_miss(Cache *cache, int set, int blk_idx)
|
||||
{
|
||||
GQueue *q = cache->sets[set].fifo_queue;
|
||||
g_queue_push_head(q, GINT_TO_POINTER(blk_idx));
|
||||
}
|
||||
|
||||
static void fifo_destroy(Cache *cache)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cache->num_sets; i++) {
|
||||
g_queue_free(cache->sets[i].fifo_queue);
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint64_t extract_tag(Cache *cache, uint64_t addr)
|
||||
{
|
||||
return addr & cache->tag_mask;
|
||||
}
|
||||
|
||||
static inline uint64_t extract_set(Cache *cache, uint64_t addr)
|
||||
{
|
||||
return (addr & cache->set_mask) >> cache->blksize_shift;
|
||||
}
|
||||
|
||||
static const char *cache_config_error(int blksize, int assoc, int cachesize)
|
||||
{
|
||||
if (cachesize % blksize != 0) {
|
||||
return "cache size must be divisible by block size";
|
||||
} else if (cachesize % (blksize * assoc) != 0) {
|
||||
return "cache size must be divisible by set size (assoc * block size)";
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static bool bad_cache_params(int blksize, int assoc, int cachesize)
|
||||
{
|
||||
return (cachesize % blksize) != 0 || (cachesize % (blksize * assoc) != 0);
|
||||
}
|
||||
|
||||
static Cache *cache_init(int blksize, int assoc, int cachesize)
|
||||
{
|
||||
if (bad_cache_params(blksize, assoc, cachesize)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Cache *cache;
|
||||
int i;
|
||||
uint64_t blk_mask;
|
||||
|
||||
cache = g_new(Cache, 1);
|
||||
cache->assoc = assoc;
|
||||
cache->cachesize = cachesize;
|
||||
cache->num_sets = cachesize / (blksize * assoc);
|
||||
cache->sets = g_new(CacheSet, cache->num_sets);
|
||||
cache->blksize_shift = pow_of_two(blksize);
|
||||
|
||||
for (i = 0; i < cache->num_sets; i++) {
|
||||
cache->sets[i].blocks = g_new0(CacheBlock, assoc);
|
||||
}
|
||||
|
||||
blk_mask = blksize - 1;
|
||||
cache->set_mask = ((cache->num_sets - 1) << cache->blksize_shift);
|
||||
cache->tag_mask = ~(cache->set_mask | blk_mask);
|
||||
|
||||
if (metadata_init) {
|
||||
metadata_init(cache);
|
||||
}
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
static int get_invalid_block(Cache *cache, uint64_t set)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cache->assoc; i++) {
|
||||
if (!cache->sets[set].blocks[i].valid) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int get_replaced_block(Cache *cache, int set)
|
||||
{
|
||||
switch (policy) {
|
||||
case RAND:
|
||||
return g_rand_int_range(rng, 0, cache->assoc);
|
||||
case LRU:
|
||||
return lru_get_lru_block(cache, set);
|
||||
case FIFO:
|
||||
return fifo_get_first_block(cache, set);
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
static int in_cache(Cache *cache, uint64_t addr)
|
||||
{
|
||||
int i;
|
||||
uint64_t tag, set;
|
||||
|
||||
tag = extract_tag(cache, addr);
|
||||
set = extract_set(cache, addr);
|
||||
|
||||
for (i = 0; i < cache->assoc; i++) {
|
||||
if (cache->sets[set].blocks[i].tag == tag &&
|
||||
cache->sets[set].blocks[i].valid) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* access_cache(): Simulate a cache access
|
||||
* @cache: The cache under simulation
|
||||
* @addr: The address of the requested memory location
|
||||
*
|
||||
* Returns true if the requsted data is hit in the cache and false when missed.
|
||||
* The cache is updated on miss for the next access.
|
||||
*/
|
||||
static bool access_cache(Cache *cache, uint64_t addr)
|
||||
{
|
||||
int hit_blk, replaced_blk;
|
||||
uint64_t tag, set;
|
||||
|
||||
tag = extract_tag(cache, addr);
|
||||
set = extract_set(cache, addr);
|
||||
|
||||
hit_blk = in_cache(cache, addr);
|
||||
if (hit_blk != -1) {
|
||||
if (update_hit) {
|
||||
update_hit(cache, set, hit_blk);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
replaced_blk = get_invalid_block(cache, set);
|
||||
|
||||
if (replaced_blk == -1) {
|
||||
replaced_blk = get_replaced_block(cache, set);
|
||||
}
|
||||
|
||||
if (update_miss) {
|
||||
update_miss(cache, set, replaced_blk);
|
||||
}
|
||||
|
||||
cache->sets[set].blocks[replaced_blk].tag = tag;
|
||||
cache->sets[set].blocks[replaced_blk].valid = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void vcpu_mem_access(unsigned int vcpu_index, qemu_plugin_meminfo_t info,
|
||||
uint64_t vaddr, void *userdata)
|
||||
{
|
||||
uint64_t effective_addr;
|
||||
struct qemu_plugin_hwaddr *hwaddr;
|
||||
InsnData *insn;
|
||||
|
||||
hwaddr = qemu_plugin_get_hwaddr(info, vaddr);
|
||||
if (hwaddr && qemu_plugin_hwaddr_is_io(hwaddr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
effective_addr = hwaddr ? qemu_plugin_hwaddr_phys_addr(hwaddr) : vaddr;
|
||||
|
||||
g_mutex_lock(&mtx);
|
||||
if (!access_cache(dcache, effective_addr)) {
|
||||
insn = (InsnData *) userdata;
|
||||
insn->dmisses++;
|
||||
dmisses++;
|
||||
}
|
||||
dmem_accesses++;
|
||||
g_mutex_unlock(&mtx);
|
||||
}
|
||||
|
||||
static void vcpu_insn_exec(unsigned int vcpu_index, void *userdata)
|
||||
{
|
||||
uint64_t insn_addr;
|
||||
InsnData *insn;
|
||||
|
||||
g_mutex_lock(&mtx);
|
||||
insn_addr = ((InsnData *) userdata)->addr;
|
||||
|
||||
if (!access_cache(icache, insn_addr)) {
|
||||
insn = (InsnData *) userdata;
|
||||
insn->imisses++;
|
||||
imisses++;
|
||||
}
|
||||
imem_accesses++;
|
||||
g_mutex_unlock(&mtx);
|
||||
}
|
||||
|
||||
static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
|
||||
{
|
||||
size_t n_insns;
|
||||
size_t i;
|
||||
InsnData *data;
|
||||
|
||||
n_insns = qemu_plugin_tb_n_insns(tb);
|
||||
for (i = 0; i < n_insns; i++) {
|
||||
struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i);
|
||||
uint64_t effective_addr;
|
||||
|
||||
if (sys) {
|
||||
effective_addr = (uint64_t) qemu_plugin_insn_haddr(insn);
|
||||
} else {
|
||||
effective_addr = (uint64_t) qemu_plugin_insn_vaddr(insn);
|
||||
}
|
||||
|
||||
/*
|
||||
* Instructions might get translated multiple times, we do not create
|
||||
* new entries for those instructions. Instead, we fetch the same
|
||||
* entry from the hash table and register it for the callback again.
|
||||
*/
|
||||
g_mutex_lock(&mtx);
|
||||
data = g_hash_table_lookup(miss_ht, GUINT_TO_POINTER(effective_addr));
|
||||
if (data == NULL) {
|
||||
data = g_new0(InsnData, 1);
|
||||
data->disas_str = qemu_plugin_insn_disas(insn);
|
||||
data->symbol = qemu_plugin_insn_symbol(insn);
|
||||
data->addr = effective_addr;
|
||||
g_hash_table_insert(miss_ht, GUINT_TO_POINTER(effective_addr),
|
||||
(gpointer) data);
|
||||
}
|
||||
g_mutex_unlock(&mtx);
|
||||
|
||||
qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem_access,
|
||||
QEMU_PLUGIN_CB_NO_REGS,
|
||||
rw, data);
|
||||
|
||||
qemu_plugin_register_vcpu_insn_exec_cb(insn, vcpu_insn_exec,
|
||||
QEMU_PLUGIN_CB_NO_REGS, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void insn_free(gpointer data)
|
||||
{
|
||||
InsnData *insn = (InsnData *) data;
|
||||
g_free(insn->disas_str);
|
||||
g_free(insn);
|
||||
}
|
||||
|
||||
static void cache_free(Cache *cache)
|
||||
{
|
||||
for (int i = 0; i < cache->num_sets; i++) {
|
||||
g_free(cache->sets[i].blocks);
|
||||
}
|
||||
|
||||
if (metadata_destroy) {
|
||||
metadata_destroy(cache);
|
||||
}
|
||||
|
||||
g_free(cache->sets);
|
||||
g_free(cache);
|
||||
}
|
||||
|
||||
static int dcmp(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
InsnData *insn_a = (InsnData *) a;
|
||||
InsnData *insn_b = (InsnData *) b;
|
||||
|
||||
return insn_a->dmisses < insn_b->dmisses ? 1 : -1;
|
||||
}
|
||||
|
||||
static int icmp(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
InsnData *insn_a = (InsnData *) a;
|
||||
InsnData *insn_b = (InsnData *) b;
|
||||
|
||||
return insn_a->imisses < insn_b->imisses ? 1 : -1;
|
||||
}
|
||||
|
||||
static void log_stats(void)
|
||||
{
|
||||
g_autoptr(GString) rep = g_string_new("");
|
||||
g_string_append_printf(rep,
|
||||
"Data accesses: %lu, Misses: %lu\nMiss rate: %lf%%\n\n",
|
||||
dmem_accesses,
|
||||
dmisses,
|
||||
((double) dmisses / (double) dmem_accesses) * 100.0);
|
||||
|
||||
g_string_append_printf(rep,
|
||||
"Instruction accesses: %lu, Misses: %lu\nMiss rate: %lf%%\n\n",
|
||||
imem_accesses,
|
||||
imisses,
|
||||
((double) imisses / (double) imem_accesses) * 100.0);
|
||||
|
||||
qemu_plugin_outs(rep->str);
|
||||
}
|
||||
|
||||
static void log_top_insns(void)
|
||||
{
|
||||
int i;
|
||||
GList *curr, *miss_insns;
|
||||
InsnData *insn;
|
||||
|
||||
miss_insns = g_hash_table_get_values(miss_ht);
|
||||
miss_insns = g_list_sort(miss_insns, dcmp);
|
||||
g_autoptr(GString) rep = g_string_new("");
|
||||
g_string_append_printf(rep, "%s", "address, data misses, instruction\n");
|
||||
|
||||
for (curr = miss_insns, i = 0; curr && i < limit; i++, curr = curr->next) {
|
||||
insn = (InsnData *) curr->data;
|
||||
g_string_append_printf(rep, "0x%" PRIx64, insn->addr);
|
||||
if (insn->symbol) {
|
||||
g_string_append_printf(rep, " (%s)", insn->symbol);
|
||||
}
|
||||
g_string_append_printf(rep, ", %ld, %s\n", insn->dmisses,
|
||||
insn->disas_str);
|
||||
}
|
||||
|
||||
miss_insns = g_list_sort(miss_insns, icmp);
|
||||
g_string_append_printf(rep, "%s", "\naddress, fetch misses, instruction\n");
|
||||
|
||||
for (curr = miss_insns, i = 0; curr && i < limit; i++, curr = curr->next) {
|
||||
insn = (InsnData *) curr->data;
|
||||
g_string_append_printf(rep, "0x%" PRIx64, insn->addr);
|
||||
if (insn->symbol) {
|
||||
g_string_append_printf(rep, " (%s)", insn->symbol);
|
||||
}
|
||||
g_string_append_printf(rep, ", %ld, %s\n", insn->imisses,
|
||||
insn->disas_str);
|
||||
}
|
||||
|
||||
qemu_plugin_outs(rep->str);
|
||||
g_list_free(miss_insns);
|
||||
}
|
||||
|
||||
static void plugin_exit(qemu_plugin_id_t id, void *p)
|
||||
{
|
||||
log_stats();
|
||||
log_top_insns();
|
||||
|
||||
cache_free(dcache);
|
||||
cache_free(icache);
|
||||
|
||||
g_hash_table_destroy(miss_ht);
|
||||
}
|
||||
|
||||
static void policy_init(void)
|
||||
{
|
||||
switch (policy) {
|
||||
case LRU:
|
||||
update_hit = lru_update_blk;
|
||||
update_miss = lru_update_blk;
|
||||
metadata_init = lru_priorities_init;
|
||||
metadata_destroy = lru_priorities_destroy;
|
||||
break;
|
||||
case FIFO:
|
||||
update_miss = fifo_update_on_miss;
|
||||
metadata_init = fifo_init;
|
||||
metadata_destroy = fifo_destroy;
|
||||
break;
|
||||
case RAND:
|
||||
rng = g_rand_new();
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
QEMU_PLUGIN_EXPORT
|
||||
int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info,
|
||||
int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
int iassoc, iblksize, icachesize;
|
||||
int dassoc, dblksize, dcachesize;
|
||||
|
||||
limit = 32;
|
||||
sys = info->system_emulation;
|
||||
|
||||
dassoc = 8;
|
||||
dblksize = 64;
|
||||
dcachesize = dblksize * dassoc * 32;
|
||||
|
||||
iassoc = 8;
|
||||
iblksize = 64;
|
||||
icachesize = iblksize * iassoc * 32;
|
||||
|
||||
policy = LRU;
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
char *opt = argv[i];
|
||||
if (g_str_has_prefix(opt, "iblksize=")) {
|
||||
iblksize = g_ascii_strtoll(opt + 9, NULL, 10);
|
||||
} else if (g_str_has_prefix(opt, "iassoc=")) {
|
||||
iassoc = g_ascii_strtoll(opt + 7, NULL, 10);
|
||||
} else if (g_str_has_prefix(opt, "icachesize=")) {
|
||||
icachesize = g_ascii_strtoll(opt + 11, NULL, 10);
|
||||
} else if (g_str_has_prefix(opt, "dblksize=")) {
|
||||
dblksize = g_ascii_strtoll(opt + 9, NULL, 10);
|
||||
} else if (g_str_has_prefix(opt, "dassoc=")) {
|
||||
dassoc = g_ascii_strtoll(opt + 7, NULL, 10);
|
||||
} else if (g_str_has_prefix(opt, "dcachesize=")) {
|
||||
dcachesize = g_ascii_strtoll(opt + 11, NULL, 10);
|
||||
} else if (g_str_has_prefix(opt, "limit=")) {
|
||||
limit = g_ascii_strtoll(opt + 6, NULL, 10);
|
||||
} else if (g_str_has_prefix(opt, "evict=")) {
|
||||
gchar *p = opt + 6;
|
||||
if (g_strcmp0(p, "rand") == 0) {
|
||||
policy = RAND;
|
||||
} else if (g_strcmp0(p, "lru") == 0) {
|
||||
policy = LRU;
|
||||
} else if (g_strcmp0(p, "fifo") == 0) {
|
||||
policy = FIFO;
|
||||
} else {
|
||||
fprintf(stderr, "invalid eviction policy: %s\n", opt);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "option parsing failed: %s\n", opt);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
policy_init();
|
||||
|
||||
dcache = cache_init(dblksize, dassoc, dcachesize);
|
||||
if (!dcache) {
|
||||
const char *err = cache_config_error(dblksize, dassoc, dcachesize);
|
||||
fprintf(stderr, "dcache cannot be constructed from given parameters\n");
|
||||
fprintf(stderr, "%s\n", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
icache = cache_init(iblksize, iassoc, icachesize);
|
||||
if (!icache) {
|
||||
const char *err = cache_config_error(iblksize, iassoc, icachesize);
|
||||
fprintf(stderr, "icache cannot be constructed from given parameters\n");
|
||||
fprintf(stderr, "%s\n", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
|
||||
qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
|
||||
|
||||
miss_ht = g_hash_table_new_full(NULL, g_direct_equal, NULL, insn_free);
|
||||
|
||||
return 0;
|
||||
}
|
153
contrib/plugins/execlog.c
Normal file
153
contrib/plugins/execlog.c
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (C) 2021, Alexandre Iooss <erdnaxe@crans.org>
|
||||
*
|
||||
* Log instruction execution with memory access.
|
||||
*
|
||||
* License: GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
#include <glib.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <qemu-plugin.h>
|
||||
|
||||
QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
|
||||
|
||||
/* Store last executed instruction on each vCPU as a GString */
|
||||
GArray *last_exec;
|
||||
|
||||
/**
|
||||
* Add memory read or write information to current instruction log
|
||||
*/
|
||||
static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t info,
|
||||
uint64_t vaddr, void *udata)
|
||||
{
|
||||
GString *s;
|
||||
|
||||
/* Find vCPU in array */
|
||||
g_assert(cpu_index < last_exec->len);
|
||||
s = g_array_index(last_exec, GString *, cpu_index);
|
||||
|
||||
/* Indicate type of memory access */
|
||||
if (qemu_plugin_mem_is_store(info)) {
|
||||
g_string_append(s, ", store");
|
||||
} else {
|
||||
g_string_append(s, ", load");
|
||||
}
|
||||
|
||||
/* If full system emulation log physical address and device name */
|
||||
struct qemu_plugin_hwaddr *hwaddr = qemu_plugin_get_hwaddr(info, vaddr);
|
||||
if (hwaddr) {
|
||||
uint64_t addr = qemu_plugin_hwaddr_phys_addr(hwaddr);
|
||||
const char *name = qemu_plugin_hwaddr_device_name(hwaddr);
|
||||
g_string_append_printf(s, ", 0x%08"PRIx64", %s", addr, name);
|
||||
} else {
|
||||
g_string_append_printf(s, ", 0x%08"PRIx64, vaddr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log instruction execution
|
||||
*/
|
||||
static void vcpu_insn_exec(unsigned int cpu_index, void *udata)
|
||||
{
|
||||
GString *s;
|
||||
|
||||
/* Find or create vCPU in array */
|
||||
while (cpu_index >= last_exec->len) {
|
||||
s = g_string_new(NULL);
|
||||
g_array_append_val(last_exec, s);
|
||||
}
|
||||
s = g_array_index(last_exec, GString *, cpu_index);
|
||||
|
||||
/* Print previous instruction in cache */
|
||||
if (s->len) {
|
||||
qemu_plugin_outs(s->str);
|
||||
qemu_plugin_outs("s\n");
|
||||
}
|
||||
|
||||
/* Store new instruction in cache */
|
||||
/* vcpu_mem will add memory access information to last_exec */
|
||||
g_string_printf(s, "%u, ", cpu_index);
|
||||
g_string_append(s, (char *)udata);
|
||||
}
|
||||
|
||||
/**
|
||||
* On translation block new translation
|
||||
*
|
||||
* QEMU convert code by translation block (TB). By hooking here we can then hook
|
||||
* a callback on each instruction and memory access.
|
||||
*/
|
||||
static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
|
||||
{
|
||||
struct qemu_plugin_insn *insn;
|
||||
uint64_t insn_vaddr;
|
||||
uint32_t insn_opcode;
|
||||
char *insn_disas;
|
||||
|
||||
size_t n = qemu_plugin_tb_n_insns(tb);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
/*
|
||||
* `insn` is shared between translations in QEMU, copy needed data here.
|
||||
* `output` is never freed as it might be used multiple times during
|
||||
* the emulation lifetime.
|
||||
* We only consider the first 32 bits of the instruction, this may be
|
||||
* a limitation for CISC architectures.
|
||||
*/
|
||||
insn = qemu_plugin_tb_get_insn(tb, i);
|
||||
insn_vaddr = qemu_plugin_insn_vaddr(insn);
|
||||
insn_opcode = *((uint32_t *)qemu_plugin_insn_data(insn));
|
||||
insn_disas = qemu_plugin_insn_disas(insn);
|
||||
char *output = g_strdup_printf("0x%"PRIx64", 0x%"PRIx32", \"%s\"",
|
||||
insn_vaddr, insn_opcode, insn_disas);
|
||||
|
||||
/* Register callback on memory read or write */
|
||||
qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem,
|
||||
QEMU_PLUGIN_CB_NO_REGS,
|
||||
QEMU_PLUGIN_MEM_RW, NULL);
|
||||
|
||||
/* Register callback on instruction */
|
||||
qemu_plugin_register_vcpu_insn_exec_cb(insn, vcpu_insn_exec,
|
||||
QEMU_PLUGIN_CB_NO_REGS, output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* On plugin exit, print last instruction in cache
|
||||
*/
|
||||
static void plugin_exit(qemu_plugin_id_t id, void *p)
|
||||
{
|
||||
guint i;
|
||||
GString *s;
|
||||
for (i = 0; i < last_exec->len; i++) {
|
||||
s = g_array_index(last_exec, GString *, i);
|
||||
if (s->str) {
|
||||
qemu_plugin_outs(s->str);
|
||||
qemu_plugin_outs("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install the plugin
|
||||
*/
|
||||
QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
|
||||
const qemu_info_t *info, int argc,
|
||||
char **argv)
|
||||
{
|
||||
/*
|
||||
* Initialize dynamic array to cache vCPU instruction. In user mode
|
||||
* we don't know the size before emulation.
|
||||
*/
|
||||
last_exec = g_array_new(FALSE, FALSE, sizeof(GString *));
|
||||
|
||||
/* Register translation block and exit callbacks */
|
||||
qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
|
||||
qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
34
cpu.c
34
cpu.c
@ -330,11 +330,6 @@ void tb_invalidate_phys_addr(target_ulong addr)
|
||||
tb_invalidate_phys_page_range(addr, addr + 1);
|
||||
mmap_unlock();
|
||||
}
|
||||
|
||||
static void breakpoint_invalidate(CPUState *cpu, target_ulong pc)
|
||||
{
|
||||
tb_invalidate_phys_addr(pc);
|
||||
}
|
||||
#else
|
||||
void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs)
|
||||
{
|
||||
@ -355,25 +350,19 @@ void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs)
|
||||
ram_addr = memory_region_get_ram_addr(mr) + addr;
|
||||
tb_invalidate_phys_page_range(ram_addr, ram_addr + 1);
|
||||
}
|
||||
|
||||
static void breakpoint_invalidate(CPUState *cpu, target_ulong pc)
|
||||
{
|
||||
/*
|
||||
* There may not be a virtual to physical translation for the pc
|
||||
* right now, but there may exist cached TB for this pc.
|
||||
* Flush the whole TB cache to force re-translation of such TBs.
|
||||
* This is heavyweight, but we're debugging anyway.
|
||||
*/
|
||||
tb_flush(cpu);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Add a breakpoint. */
|
||||
int cpu_breakpoint_insert(CPUState *cpu, vaddr pc, int flags,
|
||||
CPUBreakpoint **breakpoint)
|
||||
{
|
||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||
CPUBreakpoint *bp;
|
||||
|
||||
if (cc->gdb_adjust_breakpoint) {
|
||||
pc = cc->gdb_adjust_breakpoint(cpu, pc);
|
||||
}
|
||||
|
||||
bp = g_malloc(sizeof(*bp));
|
||||
|
||||
bp->pc = pc;
|
||||
@ -386,8 +375,6 @@ int cpu_breakpoint_insert(CPUState *cpu, vaddr pc, int flags,
|
||||
QTAILQ_INSERT_TAIL(&cpu->breakpoints, bp, entry);
|
||||
}
|
||||
|
||||
breakpoint_invalidate(cpu, pc);
|
||||
|
||||
if (breakpoint) {
|
||||
*breakpoint = bp;
|
||||
}
|
||||
@ -399,8 +386,13 @@ int cpu_breakpoint_insert(CPUState *cpu, vaddr pc, int flags,
|
||||
/* Remove a specific breakpoint. */
|
||||
int cpu_breakpoint_remove(CPUState *cpu, vaddr pc, int flags)
|
||||
{
|
||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||
CPUBreakpoint *bp;
|
||||
|
||||
if (cc->gdb_adjust_breakpoint) {
|
||||
pc = cc->gdb_adjust_breakpoint(cpu, pc);
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
|
||||
if (bp->pc == pc && bp->flags == flags) {
|
||||
cpu_breakpoint_remove_by_ref(cpu, bp);
|
||||
@ -415,8 +407,6 @@ void cpu_breakpoint_remove_by_ref(CPUState *cpu, CPUBreakpoint *bp)
|
||||
{
|
||||
QTAILQ_REMOVE(&cpu->breakpoints, bp, entry);
|
||||
|
||||
breakpoint_invalidate(cpu, bp->pc);
|
||||
|
||||
trace_breakpoint_remove(cpu->cpu_index, bp->pc, bp->flags);
|
||||
g_free(bp);
|
||||
}
|
||||
@ -441,10 +431,6 @@ void cpu_single_step(CPUState *cpu, int enabled)
|
||||
cpu->singlestep_enabled = enabled;
|
||||
if (kvm_enabled()) {
|
||||
kvm_update_guest_debug(cpu, 0);
|
||||
} else {
|
||||
/* must flush all the translated code to avoid inconsistencies */
|
||||
/* XXX: only flush what is necessary */
|
||||
tb_flush(cpu);
|
||||
}
|
||||
trace_breakpoint_singlestep(cpu->cpu_index, enabled);
|
||||
}
|
||||
|
@ -19,8 +19,6 @@
|
||||
*/
|
||||
|
||||
#include "crypto/aes.h"
|
||||
#include "crypto/desrfb.h"
|
||||
#include "crypto/xts.h"
|
||||
|
||||
typedef struct QCryptoCipherBuiltinAESContext QCryptoCipherBuiltinAESContext;
|
||||
struct QCryptoCipherBuiltinAESContext {
|
||||
@ -32,7 +30,6 @@ typedef struct QCryptoCipherBuiltinAES QCryptoCipherBuiltinAES;
|
||||
struct QCryptoCipherBuiltinAES {
|
||||
QCryptoCipher base;
|
||||
QCryptoCipherBuiltinAESContext key;
|
||||
QCryptoCipherBuiltinAESContext key_tweak;
|
||||
uint8_t iv[AES_BLOCK_SIZE];
|
||||
};
|
||||
|
||||
@ -194,39 +191,6 @@ static int qcrypto_cipher_aes_decrypt_cbc(QCryptoCipher *cipher,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcrypto_cipher_aes_encrypt_xts(QCryptoCipher *cipher,
|
||||
const void *in, void *out,
|
||||
size_t len, Error **errp)
|
||||
{
|
||||
QCryptoCipherBuiltinAES *ctx
|
||||
= container_of(cipher, QCryptoCipherBuiltinAES, base);
|
||||
|
||||
if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) {
|
||||
return -1;
|
||||
}
|
||||
xts_encrypt(&ctx->key, &ctx->key_tweak,
|
||||
do_aes_encrypt_ecb, do_aes_decrypt_ecb,
|
||||
ctx->iv, len, out, in);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcrypto_cipher_aes_decrypt_xts(QCryptoCipher *cipher,
|
||||
const void *in, void *out,
|
||||
size_t len, Error **errp)
|
||||
{
|
||||
QCryptoCipherBuiltinAES *ctx
|
||||
= container_of(cipher, QCryptoCipherBuiltinAES, base);
|
||||
|
||||
if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) {
|
||||
return -1;
|
||||
}
|
||||
xts_decrypt(&ctx->key, &ctx->key_tweak,
|
||||
do_aes_encrypt_ecb, do_aes_decrypt_ecb,
|
||||
ctx->iv, len, out, in);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int qcrypto_cipher_aes_setiv(QCryptoCipher *cipher, const uint8_t *iv,
|
||||
size_t niv, Error **errp)
|
||||
{
|
||||
@ -257,84 +221,16 @@ static const struct QCryptoCipherDriver qcrypto_cipher_aes_driver_cbc = {
|
||||
.cipher_free = qcrypto_cipher_ctx_free,
|
||||
};
|
||||
|
||||
static const struct QCryptoCipherDriver qcrypto_cipher_aes_driver_xts = {
|
||||
.cipher_encrypt = qcrypto_cipher_aes_encrypt_xts,
|
||||
.cipher_decrypt = qcrypto_cipher_aes_decrypt_xts,
|
||||
.cipher_setiv = qcrypto_cipher_aes_setiv,
|
||||
.cipher_free = qcrypto_cipher_ctx_free,
|
||||
};
|
||||
|
||||
|
||||
typedef struct QCryptoCipherBuiltinDESRFB QCryptoCipherBuiltinDESRFB;
|
||||
struct QCryptoCipherBuiltinDESRFB {
|
||||
QCryptoCipher base;
|
||||
|
||||
/* C.f. alg_key_len[QCRYPTO_CIPHER_ALG_DES_RFB] */
|
||||
uint8_t key[8];
|
||||
};
|
||||
|
||||
static int qcrypto_cipher_encrypt_des_rfb(QCryptoCipher *cipher,
|
||||
const void *in, void *out,
|
||||
size_t len, Error **errp)
|
||||
{
|
||||
QCryptoCipherBuiltinDESRFB *ctx
|
||||
= container_of(cipher, QCryptoCipherBuiltinDESRFB, base);
|
||||
size_t i;
|
||||
|
||||
if (!qcrypto_length_check(len, 8, errp)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
deskey(ctx->key, EN0);
|
||||
|
||||
for (i = 0; i < len; i += 8) {
|
||||
des((void *)in + i, out + i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcrypto_cipher_decrypt_des_rfb(QCryptoCipher *cipher,
|
||||
const void *in, void *out,
|
||||
size_t len, Error **errp)
|
||||
{
|
||||
QCryptoCipherBuiltinDESRFB *ctx
|
||||
= container_of(cipher, QCryptoCipherBuiltinDESRFB, base);
|
||||
size_t i;
|
||||
|
||||
if (!qcrypto_length_check(len, 8, errp)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
deskey(ctx->key, DE1);
|
||||
|
||||
for (i = 0; i < len; i += 8) {
|
||||
des((void *)in + i, out + i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct QCryptoCipherDriver qcrypto_cipher_des_rfb_driver = {
|
||||
.cipher_encrypt = qcrypto_cipher_encrypt_des_rfb,
|
||||
.cipher_decrypt = qcrypto_cipher_decrypt_des_rfb,
|
||||
.cipher_setiv = qcrypto_cipher_no_setiv,
|
||||
.cipher_free = qcrypto_cipher_ctx_free,
|
||||
};
|
||||
|
||||
bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg,
|
||||
QCryptoCipherMode mode)
|
||||
{
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_DES_RFB:
|
||||
return mode == QCRYPTO_CIPHER_MODE_ECB;
|
||||
case QCRYPTO_CIPHER_ALG_AES_128:
|
||||
case QCRYPTO_CIPHER_ALG_AES_192:
|
||||
case QCRYPTO_CIPHER_ALG_AES_256:
|
||||
switch (mode) {
|
||||
case QCRYPTO_CIPHER_MODE_ECB:
|
||||
case QCRYPTO_CIPHER_MODE_CBC:
|
||||
case QCRYPTO_CIPHER_MODE_XTS:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@ -356,18 +252,6 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
|
||||
}
|
||||
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_DES_RFB:
|
||||
if (mode == QCRYPTO_CIPHER_MODE_ECB) {
|
||||
QCryptoCipherBuiltinDESRFB *ctx;
|
||||
|
||||
ctx = g_new0(QCryptoCipherBuiltinDESRFB, 1);
|
||||
ctx->base.driver = &qcrypto_cipher_des_rfb_driver;
|
||||
memcpy(ctx->key, key, sizeof(ctx->key));
|
||||
|
||||
return &ctx->base;
|
||||
}
|
||||
goto bad_mode;
|
||||
|
||||
case QCRYPTO_CIPHER_ALG_AES_128:
|
||||
case QCRYPTO_CIPHER_ALG_AES_192:
|
||||
case QCRYPTO_CIPHER_ALG_AES_256:
|
||||
@ -382,9 +266,6 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
|
||||
case QCRYPTO_CIPHER_MODE_CBC:
|
||||
drv = &qcrypto_cipher_aes_driver_cbc;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_MODE_XTS:
|
||||
drv = &qcrypto_cipher_aes_driver_xts;
|
||||
break;
|
||||
default:
|
||||
goto bad_mode;
|
||||
}
|
||||
@ -392,19 +273,6 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
|
||||
ctx = g_new0(QCryptoCipherBuiltinAES, 1);
|
||||
ctx->base.driver = drv;
|
||||
|
||||
if (mode == QCRYPTO_CIPHER_MODE_XTS) {
|
||||
nkey /= 2;
|
||||
if (AES_set_encrypt_key(key + nkey, nkey * 8,
|
||||
&ctx->key_tweak.enc)) {
|
||||
error_setg(errp, "Failed to set encryption key");
|
||||
goto error;
|
||||
}
|
||||
if (AES_set_decrypt_key(key + nkey, nkey * 8,
|
||||
&ctx->key_tweak.dec)) {
|
||||
error_setg(errp, "Failed to set decryption key");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if (AES_set_encrypt_key(key, nkey * 8, &ctx->key.enc)) {
|
||||
error_setg(errp, "Failed to set encryption key");
|
||||
goto error;
|
||||
|
@ -18,17 +18,13 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_QEMU_PRIVATE_XTS
|
||||
#include "crypto/xts.h"
|
||||
#endif
|
||||
|
||||
#include <gcrypt.h>
|
||||
|
||||
bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg,
|
||||
QCryptoCipherMode mode)
|
||||
{
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_DES_RFB:
|
||||
case QCRYPTO_CIPHER_ALG_DES:
|
||||
case QCRYPTO_CIPHER_ALG_3DES:
|
||||
case QCRYPTO_CIPHER_ALG_AES_128:
|
||||
case QCRYPTO_CIPHER_ALG_AES_192:
|
||||
@ -59,10 +55,6 @@ typedef struct QCryptoCipherGcrypt {
|
||||
QCryptoCipher base;
|
||||
gcry_cipher_hd_t handle;
|
||||
size_t blocksize;
|
||||
#ifdef CONFIG_QEMU_PRIVATE_XTS
|
||||
gcry_cipher_hd_t tweakhandle;
|
||||
uint8_t iv[XTS_BLOCK_SIZE];
|
||||
#endif
|
||||
} QCryptoCipherGcrypt;
|
||||
|
||||
|
||||
@ -178,90 +170,6 @@ static const struct QCryptoCipherDriver qcrypto_gcrypt_ctr_driver = {
|
||||
.cipher_free = qcrypto_gcrypt_ctx_free,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_QEMU_PRIVATE_XTS
|
||||
static void qcrypto_gcrypt_xts_ctx_free(QCryptoCipher *cipher)
|
||||
{
|
||||
QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
|
||||
|
||||
gcry_cipher_close(ctx->tweakhandle);
|
||||
qcrypto_gcrypt_ctx_free(cipher);
|
||||
}
|
||||
|
||||
static void qcrypto_gcrypt_xts_wrape(const void *ctx, size_t length,
|
||||
uint8_t *dst, const uint8_t *src)
|
||||
{
|
||||
gcry_error_t err;
|
||||
err = gcry_cipher_encrypt((gcry_cipher_hd_t)ctx, dst, length, src, length);
|
||||
g_assert(err == 0);
|
||||
}
|
||||
|
||||
static void qcrypto_gcrypt_xts_wrapd(const void *ctx, size_t length,
|
||||
uint8_t *dst, const uint8_t *src)
|
||||
{
|
||||
gcry_error_t err;
|
||||
err = gcry_cipher_decrypt((gcry_cipher_hd_t)ctx, dst, length, src, length);
|
||||
g_assert(err == 0);
|
||||
}
|
||||
|
||||
static int qcrypto_gcrypt_xts_encrypt(QCryptoCipher *cipher, const void *in,
|
||||
void *out, size_t len, Error **errp)
|
||||
{
|
||||
QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
|
||||
|
||||
if (len & (ctx->blocksize - 1)) {
|
||||
error_setg(errp, "Length %zu must be a multiple of block size %zu",
|
||||
len, ctx->blocksize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
xts_encrypt(ctx->handle, ctx->tweakhandle,
|
||||
qcrypto_gcrypt_xts_wrape, qcrypto_gcrypt_xts_wrapd,
|
||||
ctx->iv, len, out, in);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcrypto_gcrypt_xts_decrypt(QCryptoCipher *cipher, const void *in,
|
||||
void *out, size_t len, Error **errp)
|
||||
{
|
||||
QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
|
||||
|
||||
if (len & (ctx->blocksize - 1)) {
|
||||
error_setg(errp, "Length %zu must be a multiple of block size %zu",
|
||||
len, ctx->blocksize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
xts_decrypt(ctx->handle, ctx->tweakhandle,
|
||||
qcrypto_gcrypt_xts_wrape, qcrypto_gcrypt_xts_wrapd,
|
||||
ctx->iv, len, out, in);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcrypto_gcrypt_xts_setiv(QCryptoCipher *cipher,
|
||||
const uint8_t *iv, size_t niv,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
|
||||
|
||||
if (niv != ctx->blocksize) {
|
||||
error_setg(errp, "Expected IV size %zu not %zu",
|
||||
ctx->blocksize, niv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(ctx->iv, iv, niv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct QCryptoCipherDriver qcrypto_gcrypt_xts_driver = {
|
||||
.cipher_encrypt = qcrypto_gcrypt_xts_encrypt,
|
||||
.cipher_decrypt = qcrypto_gcrypt_xts_decrypt,
|
||||
.cipher_setiv = qcrypto_gcrypt_xts_setiv,
|
||||
.cipher_free = qcrypto_gcrypt_xts_ctx_free,
|
||||
};
|
||||
#endif /* CONFIG_QEMU_PRIVATE_XTS */
|
||||
|
||||
|
||||
static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
|
||||
QCryptoCipherMode mode,
|
||||
const uint8_t *key,
|
||||
@ -278,7 +186,7 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
|
||||
}
|
||||
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_DES_RFB:
|
||||
case QCRYPTO_CIPHER_ALG_DES:
|
||||
gcryalg = GCRY_CIPHER_DES;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_ALG_3DES:
|
||||
@ -323,12 +231,7 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
|
||||
gcrymode = GCRY_CIPHER_MODE_ECB;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_MODE_XTS:
|
||||
#ifdef CONFIG_QEMU_PRIVATE_XTS
|
||||
drv = &qcrypto_gcrypt_xts_driver;
|
||||
gcrymode = GCRY_CIPHER_MODE_ECB;
|
||||
#else
|
||||
gcrymode = GCRY_CIPHER_MODE_XTS;
|
||||
#endif
|
||||
break;
|
||||
case QCRYPTO_CIPHER_MODE_CBC:
|
||||
gcrymode = GCRY_CIPHER_MODE_CBC;
|
||||
@ -354,44 +257,7 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
|
||||
}
|
||||
ctx->blocksize = gcry_cipher_get_algo_blklen(gcryalg);
|
||||
|
||||
#ifdef CONFIG_QEMU_PRIVATE_XTS
|
||||
if (mode == QCRYPTO_CIPHER_MODE_XTS) {
|
||||
if (ctx->blocksize != XTS_BLOCK_SIZE) {
|
||||
error_setg(errp,
|
||||
"Cipher block size %zu must equal XTS block size %d",
|
||||
ctx->blocksize, XTS_BLOCK_SIZE);
|
||||
goto error;
|
||||
}
|
||||
err = gcry_cipher_open(&ctx->tweakhandle, gcryalg, gcrymode, 0);
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot initialize cipher: %s",
|
||||
gcry_strerror(err));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (alg == QCRYPTO_CIPHER_ALG_DES_RFB) {
|
||||
/* We're using standard DES cipher from gcrypt, so we need
|
||||
* to munge the key so that the results are the same as the
|
||||
* bizarre RFB variant of DES :-)
|
||||
*/
|
||||
uint8_t *rfbkey = qcrypto_cipher_munge_des_rfb_key(key, nkey);
|
||||
err = gcry_cipher_setkey(ctx->handle, rfbkey, nkey);
|
||||
g_free(rfbkey);
|
||||
} else {
|
||||
#ifdef CONFIG_QEMU_PRIVATE_XTS
|
||||
if (mode == QCRYPTO_CIPHER_MODE_XTS) {
|
||||
nkey /= 2;
|
||||
err = gcry_cipher_setkey(ctx->tweakhandle, key + nkey, nkey);
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot set key: %s", gcry_strerror(err));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
err = gcry_cipher_setkey(ctx->handle, key, nkey);
|
||||
}
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot set key: %s", gcry_strerror(err));
|
||||
goto error;
|
||||
@ -400,9 +266,6 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
|
||||
return &ctx->base;
|
||||
|
||||
error:
|
||||
#ifdef CONFIG_QEMU_PRIVATE_XTS
|
||||
gcry_cipher_close(ctx->tweakhandle);
|
||||
#endif
|
||||
gcry_cipher_close(ctx->handle);
|
||||
g_free(ctx);
|
||||
return NULL;
|
||||
|
335
crypto/cipher-gnutls.c.inc
Normal file
335
crypto/cipher-gnutls.c.inc
Normal file
@ -0,0 +1,335 @@
|
||||
/*
|
||||
* QEMU Crypto cipher gnutls algorithms
|
||||
*
|
||||
* Copyright (c) 2021 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "cipherpriv.h"
|
||||
|
||||
#include <gnutls/crypto.h>
|
||||
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030608
|
||||
#define QEMU_GNUTLS_XTS
|
||||
#endif
|
||||
|
||||
bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg,
|
||||
QCryptoCipherMode mode)
|
||||
{
|
||||
|
||||
switch (mode) {
|
||||
case QCRYPTO_CIPHER_MODE_ECB:
|
||||
case QCRYPTO_CIPHER_MODE_CBC:
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_AES_128:
|
||||
case QCRYPTO_CIPHER_ALG_AES_192:
|
||||
case QCRYPTO_CIPHER_ALG_AES_256:
|
||||
case QCRYPTO_CIPHER_ALG_DES:
|
||||
case QCRYPTO_CIPHER_ALG_3DES:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
#ifdef QEMU_GNUTLS_XTS
|
||||
case QCRYPTO_CIPHER_MODE_XTS:
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_AES_128:
|
||||
case QCRYPTO_CIPHER_ALG_AES_256:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct QCryptoCipherGnutls QCryptoCipherGnutls;
|
||||
struct QCryptoCipherGnutls {
|
||||
QCryptoCipher base;
|
||||
gnutls_cipher_hd_t handle; /* XTS & CBC mode */
|
||||
gnutls_cipher_algorithm_t galg; /* ECB mode */
|
||||
guint8 *key; /* ECB mode */
|
||||
size_t nkey; /* ECB mode */
|
||||
size_t blocksize;
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_gnutls_cipher_free(QCryptoCipher *cipher)
|
||||
{
|
||||
QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
|
||||
|
||||
g_free(ctx->key);
|
||||
if (ctx->handle) {
|
||||
gnutls_cipher_deinit(ctx->handle);
|
||||
}
|
||||
g_free(ctx);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qcrypto_gnutls_cipher_encrypt(QCryptoCipher *cipher,
|
||||
const void *in,
|
||||
void *out,
|
||||
size_t len,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
|
||||
int err;
|
||||
|
||||
if (len % ctx->blocksize) {
|
||||
error_setg(errp, "Length %zu must be a multiple of block size %zu",
|
||||
len, ctx->blocksize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ctx->handle) { /* CBC / XTS mode */
|
||||
err = gnutls_cipher_encrypt2(ctx->handle,
|
||||
in, len,
|
||||
out, len);
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot encrypt data: %s",
|
||||
gnutls_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
} else { /* ECB mode very inefficiently faked with CBC */
|
||||
g_autofree unsigned char *iv = g_new0(unsigned char, ctx->blocksize);
|
||||
while (len) {
|
||||
gnutls_cipher_hd_t handle;
|
||||
gnutls_datum_t gkey = { (unsigned char *)ctx->key, ctx->nkey };
|
||||
int err = gnutls_cipher_init(&handle, ctx->galg, &gkey, NULL);
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot initialize cipher: %s",
|
||||
gnutls_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
gnutls_cipher_set_iv(handle, iv, ctx->blocksize);
|
||||
|
||||
err = gnutls_cipher_encrypt2(handle,
|
||||
in, ctx->blocksize,
|
||||
out, ctx->blocksize);
|
||||
if (err != 0) {
|
||||
gnutls_cipher_deinit(handle);
|
||||
error_setg(errp, "Cannot encrypt data: %s",
|
||||
gnutls_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
gnutls_cipher_deinit(handle);
|
||||
|
||||
len -= ctx->blocksize;
|
||||
in += ctx->blocksize;
|
||||
out += ctx->blocksize;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qcrypto_gnutls_cipher_decrypt(QCryptoCipher *cipher,
|
||||
const void *in,
|
||||
void *out,
|
||||
size_t len,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
|
||||
int err;
|
||||
|
||||
if (len % ctx->blocksize) {
|
||||
error_setg(errp, "Length %zu must be a multiple of block size %zu",
|
||||
len, ctx->blocksize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ctx->handle) { /* CBC / XTS mode */
|
||||
err = gnutls_cipher_decrypt2(ctx->handle,
|
||||
in, len,
|
||||
out, len);
|
||||
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot decrypt data: %s",
|
||||
gnutls_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
} else { /* ECB mode very inefficiently faked with CBC */
|
||||
g_autofree unsigned char *iv = g_new0(unsigned char, ctx->blocksize);
|
||||
while (len) {
|
||||
gnutls_cipher_hd_t handle;
|
||||
gnutls_datum_t gkey = { (unsigned char *)ctx->key, ctx->nkey };
|
||||
int err = gnutls_cipher_init(&handle, ctx->galg, &gkey, NULL);
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot initialize cipher: %s",
|
||||
gnutls_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
gnutls_cipher_set_iv(handle, iv, ctx->blocksize);
|
||||
|
||||
err = gnutls_cipher_decrypt2(handle,
|
||||
in, ctx->blocksize,
|
||||
out, ctx->blocksize);
|
||||
if (err != 0) {
|
||||
gnutls_cipher_deinit(handle);
|
||||
error_setg(errp, "Cannot encrypt data: %s",
|
||||
gnutls_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
gnutls_cipher_deinit(handle);
|
||||
|
||||
len -= ctx->blocksize;
|
||||
in += ctx->blocksize;
|
||||
out += ctx->blocksize;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
qcrypto_gnutls_cipher_setiv(QCryptoCipher *cipher,
|
||||
const uint8_t *iv, size_t niv,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
|
||||
|
||||
if (niv != ctx->blocksize) {
|
||||
error_setg(errp, "Expected IV size %zu not %zu",
|
||||
ctx->blocksize, niv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
gnutls_cipher_set_iv(ctx->handle, (unsigned char *)iv, niv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct QCryptoCipherDriver gnutls_driver = {
|
||||
.cipher_encrypt = qcrypto_gnutls_cipher_encrypt,
|
||||
.cipher_decrypt = qcrypto_gnutls_cipher_decrypt,
|
||||
.cipher_setiv = qcrypto_gnutls_cipher_setiv,
|
||||
.cipher_free = qcrypto_gnutls_cipher_free,
|
||||
};
|
||||
|
||||
static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
|
||||
QCryptoCipherMode mode,
|
||||
const uint8_t *key,
|
||||
size_t nkey,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoCipherGnutls *ctx;
|
||||
gnutls_datum_t gkey = { (unsigned char *)key, nkey };
|
||||
gnutls_cipher_algorithm_t galg = GNUTLS_CIPHER_UNKNOWN;
|
||||
int err;
|
||||
|
||||
switch (mode) {
|
||||
#ifdef QEMU_GNUTLS_XTS
|
||||
case QCRYPTO_CIPHER_MODE_XTS:
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_AES_128:
|
||||
galg = GNUTLS_CIPHER_AES_128_XTS;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_ALG_AES_256:
|
||||
galg = GNUTLS_CIPHER_AES_256_XTS;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case QCRYPTO_CIPHER_MODE_ECB:
|
||||
case QCRYPTO_CIPHER_MODE_CBC:
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_AES_128:
|
||||
galg = GNUTLS_CIPHER_AES_128_CBC;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_ALG_AES_192:
|
||||
galg = GNUTLS_CIPHER_AES_192_CBC;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_ALG_AES_256:
|
||||
galg = GNUTLS_CIPHER_AES_256_CBC;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_ALG_DES:
|
||||
galg = GNUTLS_CIPHER_DES_CBC;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_ALG_3DES:
|
||||
galg = GNUTLS_CIPHER_3DES_CBC;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (galg == GNUTLS_CIPHER_UNKNOWN) {
|
||||
error_setg(errp, "Unsupported cipher algorithm %s with %s mode",
|
||||
QCryptoCipherAlgorithm_str(alg),
|
||||
QCryptoCipherMode_str(mode));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx = g_new0(QCryptoCipherGnutls, 1);
|
||||
ctx->base.driver = &gnutls_driver;
|
||||
|
||||
if (mode == QCRYPTO_CIPHER_MODE_ECB) {
|
||||
ctx->key = g_new0(guint8, nkey);
|
||||
memcpy(ctx->key, key, nkey);
|
||||
ctx->nkey = nkey;
|
||||
ctx->galg = galg;
|
||||
} else {
|
||||
err = gnutls_cipher_init(&ctx->handle, galg, &gkey, NULL);
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot initialize cipher: %s",
|
||||
gnutls_strerror(err));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (alg == QCRYPTO_CIPHER_ALG_DES ||
|
||||
alg == QCRYPTO_CIPHER_ALG_3DES)
|
||||
ctx->blocksize = 8;
|
||||
else
|
||||
ctx->blocksize = 16;
|
||||
|
||||
/*
|
||||
* Our API contract for requires iv to be optional
|
||||
* but nettle gets unhappy when called by gnutls
|
||||
* in this case, so we just force set a default
|
||||
* all-zeros IV, to match behaviour of other backends.
|
||||
*/
|
||||
if (mode != QCRYPTO_CIPHER_MODE_ECB) {
|
||||
g_autofree unsigned char *iv = g_new0(unsigned char, ctx->blocksize);
|
||||
gnutls_cipher_set_iv(ctx->handle, iv, ctx->blocksize);
|
||||
}
|
||||
|
||||
return &ctx->base;
|
||||
|
||||
error:
|
||||
qcrypto_gnutls_cipher_free(&ctx->base);
|
||||
return NULL;
|
||||
}
|
@ -235,11 +235,11 @@ static const struct QCryptoCipherDriver NAME##_driver_xts = { \
|
||||
DEFINE_XTS(NAME, TYPE, BLEN, ENCRYPT, DECRYPT)
|
||||
|
||||
|
||||
typedef struct QCryptoNettleDESRFB {
|
||||
typedef struct QCryptoNettleDES {
|
||||
QCryptoCipher base;
|
||||
struct des_ctx key;
|
||||
uint8_t iv[DES_BLOCK_SIZE];
|
||||
} QCryptoNettleDESRFB;
|
||||
} QCryptoNettleDES;
|
||||
|
||||
static void des_encrypt_native(const void *ctx, size_t length,
|
||||
uint8_t *dst, const uint8_t *src)
|
||||
@ -253,7 +253,7 @@ static void des_decrypt_native(const void *ctx, size_t length,
|
||||
des_decrypt(ctx, length, dst, src);
|
||||
}
|
||||
|
||||
DEFINE_ECB_CBC_CTR(qcrypto_nettle_des_rfb, QCryptoNettleDESRFB,
|
||||
DEFINE_ECB_CBC_CTR(qcrypto_nettle_des, QCryptoNettleDES,
|
||||
DES_BLOCK_SIZE, des_encrypt_native, des_decrypt_native)
|
||||
|
||||
|
||||
@ -431,7 +431,7 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg,
|
||||
QCryptoCipherMode mode)
|
||||
{
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_DES_RFB:
|
||||
case QCRYPTO_CIPHER_ALG_DES:
|
||||
case QCRYPTO_CIPHER_ALG_3DES:
|
||||
case QCRYPTO_CIPHER_ALG_AES_128:
|
||||
case QCRYPTO_CIPHER_ALG_AES_192:
|
||||
@ -480,32 +480,28 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
|
||||
}
|
||||
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_DES_RFB:
|
||||
case QCRYPTO_CIPHER_ALG_DES:
|
||||
{
|
||||
QCryptoNettleDESRFB *ctx;
|
||||
QCryptoNettleDES *ctx;
|
||||
const QCryptoCipherDriver *drv;
|
||||
uint8_t *rfbkey;
|
||||
|
||||
switch (mode) {
|
||||
case QCRYPTO_CIPHER_MODE_ECB:
|
||||
drv = &qcrypto_nettle_des_rfb_driver_ecb;
|
||||
drv = &qcrypto_nettle_des_driver_ecb;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_MODE_CBC:
|
||||
drv = &qcrypto_nettle_des_rfb_driver_cbc;
|
||||
drv = &qcrypto_nettle_des_driver_cbc;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_MODE_CTR:
|
||||
drv = &qcrypto_nettle_des_rfb_driver_ctr;
|
||||
drv = &qcrypto_nettle_des_driver_ctr;
|
||||
break;
|
||||
default:
|
||||
goto bad_cipher_mode;
|
||||
}
|
||||
|
||||
ctx = g_new0(QCryptoNettleDESRFB, 1);
|
||||
ctx = g_new0(QCryptoNettleDES, 1);
|
||||
ctx->base.driver = drv;
|
||||
|
||||
rfbkey = qcrypto_cipher_munge_des_rfb_key(key, nkey);
|
||||
des_set_key(&ctx->key, rfbkey);
|
||||
g_free(rfbkey);
|
||||
des_set_key(&ctx->key, key);
|
||||
|
||||
return &ctx->base;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ static const size_t alg_key_len[QCRYPTO_CIPHER_ALG__MAX] = {
|
||||
[QCRYPTO_CIPHER_ALG_AES_128] = 16,
|
||||
[QCRYPTO_CIPHER_ALG_AES_192] = 24,
|
||||
[QCRYPTO_CIPHER_ALG_AES_256] = 32,
|
||||
[QCRYPTO_CIPHER_ALG_DES_RFB] = 8,
|
||||
[QCRYPTO_CIPHER_ALG_DES] = 8,
|
||||
[QCRYPTO_CIPHER_ALG_3DES] = 24,
|
||||
[QCRYPTO_CIPHER_ALG_CAST5_128] = 16,
|
||||
[QCRYPTO_CIPHER_ALG_SERPENT_128] = 16,
|
||||
@ -44,7 +44,7 @@ static const size_t alg_block_len[QCRYPTO_CIPHER_ALG__MAX] = {
|
||||
[QCRYPTO_CIPHER_ALG_AES_128] = 16,
|
||||
[QCRYPTO_CIPHER_ALG_AES_192] = 16,
|
||||
[QCRYPTO_CIPHER_ALG_AES_256] = 16,
|
||||
[QCRYPTO_CIPHER_ALG_DES_RFB] = 8,
|
||||
[QCRYPTO_CIPHER_ALG_DES] = 8,
|
||||
[QCRYPTO_CIPHER_ALG_3DES] = 8,
|
||||
[QCRYPTO_CIPHER_ALG_CAST5_128] = 8,
|
||||
[QCRYPTO_CIPHER_ALG_SERPENT_128] = 16,
|
||||
@ -107,9 +107,9 @@ qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg,
|
||||
}
|
||||
|
||||
if (mode == QCRYPTO_CIPHER_MODE_XTS) {
|
||||
if (alg == QCRYPTO_CIPHER_ALG_DES_RFB
|
||||
|| alg == QCRYPTO_CIPHER_ALG_3DES) {
|
||||
error_setg(errp, "XTS mode not compatible with DES-RFB/3DES");
|
||||
if (alg == QCRYPTO_CIPHER_ALG_DES ||
|
||||
alg == QCRYPTO_CIPHER_ALG_3DES) {
|
||||
error_setg(errp, "XTS mode not compatible with DES/3DES");
|
||||
return false;
|
||||
}
|
||||
if (nkey % 2) {
|
||||
@ -132,28 +132,12 @@ qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg,
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_GCRYPT) || defined(CONFIG_NETTLE)
|
||||
static uint8_t *
|
||||
qcrypto_cipher_munge_des_rfb_key(const uint8_t *key,
|
||||
size_t nkey)
|
||||
{
|
||||
uint8_t *ret = g_new0(uint8_t, nkey);
|
||||
size_t i;
|
||||
for (i = 0; i < nkey; i++) {
|
||||
uint8_t r = key[i];
|
||||
r = (r & 0xf0) >> 4 | (r & 0x0f) << 4;
|
||||
r = (r & 0xcc) >> 2 | (r & 0x33) << 2;
|
||||
r = (r & 0xaa) >> 1 | (r & 0x55) << 1;
|
||||
ret[i] = r;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_GCRYPT || CONFIG_NETTLE */
|
||||
|
||||
#ifdef CONFIG_GCRYPT
|
||||
#include "cipher-gcrypt.c.inc"
|
||||
#elif defined CONFIG_NETTLE
|
||||
#include "cipher-nettle.c.inc"
|
||||
#elif defined CONFIG_GNUTLS_CRYPTO
|
||||
#include "cipher-gnutls.c.inc"
|
||||
#else
|
||||
#include "cipher-builtin.c.inc"
|
||||
#endif
|
||||
|
416
crypto/desrfb.c
416
crypto/desrfb.c
@ -1,416 +0,0 @@
|
||||
/*
|
||||
* This is D3DES (V5.09) by Richard Outerbridge with the double and
|
||||
* triple-length support removed for use in VNC. Also the bytebit[] array
|
||||
* has been reversed so that the most significant bit in each byte of the
|
||||
* key is ignored, not the least significant.
|
||||
*
|
||||
* These changes are:
|
||||
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
/* D3DES (V5.09) -
|
||||
*
|
||||
* A portable, public domain, version of the Data Encryption Standard.
|
||||
*
|
||||
* Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge.
|
||||
* Thanks to: Dan Hoey for his excellent Initial and Inverse permutation
|
||||
* code; Jim Gillogly & Phil Karn for the DES key schedule code; Dennis
|
||||
* Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau,
|
||||
* for humouring me on.
|
||||
*
|
||||
* Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
|
||||
* (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "crypto/desrfb.h"
|
||||
|
||||
static void scrunch(unsigned char *, unsigned long *);
|
||||
static void unscrun(unsigned long *, unsigned char *);
|
||||
static void desfunc(unsigned long *, unsigned long *);
|
||||
static void cookey(unsigned long *);
|
||||
|
||||
static unsigned long KnL[32] = { 0L };
|
||||
|
||||
static const unsigned short bytebit[8] = {
|
||||
01, 02, 04, 010, 020, 040, 0100, 0200 };
|
||||
|
||||
static const unsigned long bigbyte[24] = {
|
||||
0x800000L, 0x400000L, 0x200000L, 0x100000L,
|
||||
0x80000L, 0x40000L, 0x20000L, 0x10000L,
|
||||
0x8000L, 0x4000L, 0x2000L, 0x1000L,
|
||||
0x800L, 0x400L, 0x200L, 0x100L,
|
||||
0x80L, 0x40L, 0x20L, 0x10L,
|
||||
0x8L, 0x4L, 0x2L, 0x1L };
|
||||
|
||||
/* Use the key schedule specified in the Standard (ANSI X3.92-1981). */
|
||||
|
||||
static const unsigned char pc1[56] = {
|
||||
56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17,
|
||||
9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35,
|
||||
62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21,
|
||||
13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 };
|
||||
|
||||
static const unsigned char totrot[16] = {
|
||||
1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28 };
|
||||
|
||||
static const unsigned char pc2[48] = {
|
||||
13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9,
|
||||
22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1,
|
||||
40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
|
||||
43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 };
|
||||
|
||||
/* Thanks to James Gillogly & Phil Karn! */
|
||||
void deskey(unsigned char *key, int edf)
|
||||
{
|
||||
register int i, j, l, m, n;
|
||||
unsigned char pc1m[56], pcr[56];
|
||||
unsigned long kn[32];
|
||||
|
||||
for ( j = 0; j < 56; j++ ) {
|
||||
l = pc1[j];
|
||||
m = l & 07;
|
||||
pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0;
|
||||
}
|
||||
for( i = 0; i < 16; i++ ) {
|
||||
if( edf == DE1 ) m = (15 - i) << 1;
|
||||
else m = i << 1;
|
||||
n = m + 1;
|
||||
kn[m] = kn[n] = 0L;
|
||||
for( j = 0; j < 28; j++ ) {
|
||||
l = j + totrot[i];
|
||||
if( l < 28 ) pcr[j] = pc1m[l];
|
||||
else pcr[j] = pc1m[l - 28];
|
||||
}
|
||||
for( j = 28; j < 56; j++ ) {
|
||||
l = j + totrot[i];
|
||||
if( l < 56 ) pcr[j] = pc1m[l];
|
||||
else pcr[j] = pc1m[l - 28];
|
||||
}
|
||||
for( j = 0; j < 24; j++ ) {
|
||||
if( pcr[pc2[j]] ) kn[m] |= bigbyte[j];
|
||||
if( pcr[pc2[j + 24]] ) kn[n] |= bigbyte[j];
|
||||
}
|
||||
}
|
||||
cookey(kn);
|
||||
return;
|
||||
}
|
||||
|
||||
static void cookey(register unsigned long *raw1)
|
||||
{
|
||||
register unsigned long *cook, *raw0;
|
||||
unsigned long dough[32];
|
||||
register int i;
|
||||
|
||||
cook = dough;
|
||||
for( i = 0; i < 16; i++, raw1++ ) {
|
||||
raw0 = raw1++;
|
||||
*cook = (*raw0 & 0x00fc0000L) << 6;
|
||||
*cook |= (*raw0 & 0x00000fc0L) << 10;
|
||||
*cook |= (*raw1 & 0x00fc0000L) >> 10;
|
||||
*cook++ |= (*raw1 & 0x00000fc0L) >> 6;
|
||||
*cook = (*raw0 & 0x0003f000L) << 12;
|
||||
*cook |= (*raw0 & 0x0000003fL) << 16;
|
||||
*cook |= (*raw1 & 0x0003f000L) >> 4;
|
||||
*cook++ |= (*raw1 & 0x0000003fL);
|
||||
}
|
||||
usekey(dough);
|
||||
return;
|
||||
}
|
||||
|
||||
void usekey(register unsigned long *from)
|
||||
{
|
||||
register unsigned long *to, *endp;
|
||||
|
||||
to = KnL, endp = &KnL[32];
|
||||
while( to < endp ) *to++ = *from++;
|
||||
return;
|
||||
}
|
||||
|
||||
void des(unsigned char *inblock, unsigned char *outblock)
|
||||
{
|
||||
unsigned long work[2];
|
||||
|
||||
scrunch(inblock, work);
|
||||
desfunc(work, KnL);
|
||||
unscrun(work, outblock);
|
||||
return;
|
||||
}
|
||||
|
||||
static void scrunch(register unsigned char *outof, register unsigned long *into)
|
||||
{
|
||||
*into = (*outof++ & 0xffL) << 24;
|
||||
*into |= (*outof++ & 0xffL) << 16;
|
||||
*into |= (*outof++ & 0xffL) << 8;
|
||||
*into++ |= (*outof++ & 0xffL);
|
||||
*into = (*outof++ & 0xffL) << 24;
|
||||
*into |= (*outof++ & 0xffL) << 16;
|
||||
*into |= (*outof++ & 0xffL) << 8;
|
||||
*into |= (*outof & 0xffL);
|
||||
return;
|
||||
}
|
||||
|
||||
static void unscrun(register unsigned long *outof, register unsigned char *into)
|
||||
{
|
||||
*into++ = (unsigned char)((*outof >> 24) & 0xffL);
|
||||
*into++ = (unsigned char)((*outof >> 16) & 0xffL);
|
||||
*into++ = (unsigned char)((*outof >> 8) & 0xffL);
|
||||
*into++ = (unsigned char)(*outof++ & 0xffL);
|
||||
*into++ = (unsigned char)((*outof >> 24) & 0xffL);
|
||||
*into++ = (unsigned char)((*outof >> 16) & 0xffL);
|
||||
*into++ = (unsigned char)((*outof >> 8) & 0xffL);
|
||||
*into = (unsigned char)(*outof & 0xffL);
|
||||
return;
|
||||
}
|
||||
|
||||
static const unsigned long SP1[64] = {
|
||||
0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L,
|
||||
0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L,
|
||||
0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L,
|
||||
0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L,
|
||||
0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L,
|
||||
0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L,
|
||||
0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L,
|
||||
0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L,
|
||||
0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L,
|
||||
0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L,
|
||||
0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L,
|
||||
0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L,
|
||||
0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L,
|
||||
0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L,
|
||||
0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L,
|
||||
0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L };
|
||||
|
||||
static const unsigned long SP2[64] = {
|
||||
0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L,
|
||||
0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L,
|
||||
0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L,
|
||||
0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L,
|
||||
0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L,
|
||||
0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L,
|
||||
0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L,
|
||||
0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L,
|
||||
0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L,
|
||||
0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L,
|
||||
0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L,
|
||||
0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L,
|
||||
0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L,
|
||||
0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L,
|
||||
0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L,
|
||||
0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L };
|
||||
|
||||
static const unsigned long SP3[64] = {
|
||||
0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L,
|
||||
0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L,
|
||||
0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L,
|
||||
0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L,
|
||||
0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L,
|
||||
0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L,
|
||||
0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L,
|
||||
0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L,
|
||||
0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L,
|
||||
0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L,
|
||||
0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L,
|
||||
0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L,
|
||||
0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L,
|
||||
0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L,
|
||||
0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L,
|
||||
0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L };
|
||||
|
||||
static const unsigned long SP4[64] = {
|
||||
0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
|
||||
0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L,
|
||||
0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L,
|
||||
0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L,
|
||||
0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L,
|
||||
0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L,
|
||||
0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L,
|
||||
0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L,
|
||||
0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L,
|
||||
0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L,
|
||||
0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L,
|
||||
0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
|
||||
0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L,
|
||||
0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L,
|
||||
0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L,
|
||||
0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L };
|
||||
|
||||
static const unsigned long SP5[64] = {
|
||||
0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L,
|
||||
0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L,
|
||||
0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L,
|
||||
0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L,
|
||||
0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L,
|
||||
0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L,
|
||||
0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L,
|
||||
0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L,
|
||||
0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L,
|
||||
0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L,
|
||||
0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L,
|
||||
0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L,
|
||||
0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L,
|
||||
0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L,
|
||||
0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L,
|
||||
0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L };
|
||||
|
||||
static const unsigned long SP6[64] = {
|
||||
0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L,
|
||||
0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L,
|
||||
0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L,
|
||||
0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L,
|
||||
0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L,
|
||||
0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L,
|
||||
0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L,
|
||||
0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L,
|
||||
0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L,
|
||||
0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L,
|
||||
0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L,
|
||||
0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L,
|
||||
0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L,
|
||||
0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L,
|
||||
0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L,
|
||||
0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L };
|
||||
|
||||
static const unsigned long SP7[64] = {
|
||||
0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L,
|
||||
0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L,
|
||||
0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L,
|
||||
0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L,
|
||||
0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L,
|
||||
0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L,
|
||||
0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L,
|
||||
0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L,
|
||||
0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L,
|
||||
0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L,
|
||||
0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L,
|
||||
0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L,
|
||||
0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L,
|
||||
0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L,
|
||||
0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L,
|
||||
0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L };
|
||||
|
||||
static const unsigned long SP8[64] = {
|
||||
0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L,
|
||||
0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L,
|
||||
0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L,
|
||||
0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L,
|
||||
0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L,
|
||||
0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L,
|
||||
0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L,
|
||||
0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L,
|
||||
0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L,
|
||||
0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L,
|
||||
0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L,
|
||||
0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L,
|
||||
0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L,
|
||||
0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L,
|
||||
0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L,
|
||||
0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L };
|
||||
|
||||
static void desfunc(register unsigned long *block, register unsigned long *keys)
|
||||
{
|
||||
register unsigned long fval, work, right, leftt;
|
||||
register int round;
|
||||
|
||||
leftt = block[0];
|
||||
right = block[1];
|
||||
work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL;
|
||||
right ^= work;
|
||||
leftt ^= (work << 4);
|
||||
work = ((leftt >> 16) ^ right) & 0x0000ffffL;
|
||||
right ^= work;
|
||||
leftt ^= (work << 16);
|
||||
work = ((right >> 2) ^ leftt) & 0x33333333L;
|
||||
leftt ^= work;
|
||||
right ^= (work << 2);
|
||||
work = ((right >> 8) ^ leftt) & 0x00ff00ffL;
|
||||
leftt ^= work;
|
||||
right ^= (work << 8);
|
||||
right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL;
|
||||
work = (leftt ^ right) & 0xaaaaaaaaL;
|
||||
leftt ^= work;
|
||||
right ^= work;
|
||||
leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL;
|
||||
|
||||
for( round = 0; round < 8; round++ ) {
|
||||
work = (right << 28) | (right >> 4);
|
||||
work ^= *keys++;
|
||||
fval = SP7[ work & 0x3fL];
|
||||
fval |= SP5[(work >> 8) & 0x3fL];
|
||||
fval |= SP3[(work >> 16) & 0x3fL];
|
||||
fval |= SP1[(work >> 24) & 0x3fL];
|
||||
work = right ^ *keys++;
|
||||
fval |= SP8[ work & 0x3fL];
|
||||
fval |= SP6[(work >> 8) & 0x3fL];
|
||||
fval |= SP4[(work >> 16) & 0x3fL];
|
||||
fval |= SP2[(work >> 24) & 0x3fL];
|
||||
leftt ^= fval;
|
||||
work = (leftt << 28) | (leftt >> 4);
|
||||
work ^= *keys++;
|
||||
fval = SP7[ work & 0x3fL];
|
||||
fval |= SP5[(work >> 8) & 0x3fL];
|
||||
fval |= SP3[(work >> 16) & 0x3fL];
|
||||
fval |= SP1[(work >> 24) & 0x3fL];
|
||||
work = leftt ^ *keys++;
|
||||
fval |= SP8[ work & 0x3fL];
|
||||
fval |= SP6[(work >> 8) & 0x3fL];
|
||||
fval |= SP4[(work >> 16) & 0x3fL];
|
||||
fval |= SP2[(work >> 24) & 0x3fL];
|
||||
right ^= fval;
|
||||
}
|
||||
|
||||
right = (right << 31) | (right >> 1);
|
||||
work = (leftt ^ right) & 0xaaaaaaaaL;
|
||||
leftt ^= work;
|
||||
right ^= work;
|
||||
leftt = (leftt << 31) | (leftt >> 1);
|
||||
work = ((leftt >> 8) ^ right) & 0x00ff00ffL;
|
||||
right ^= work;
|
||||
leftt ^= (work << 8);
|
||||
work = ((leftt >> 2) ^ right) & 0x33333333L;
|
||||
right ^= work;
|
||||
leftt ^= (work << 2);
|
||||
work = ((right >> 16) ^ leftt) & 0x0000ffffL;
|
||||
leftt ^= work;
|
||||
right ^= (work << 16);
|
||||
work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL;
|
||||
leftt ^= work;
|
||||
right ^= (work << 4);
|
||||
*block++ = right;
|
||||
*block = leftt;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Validation sets:
|
||||
*
|
||||
* Single-length key, single-length plaintext -
|
||||
* Key : 0123 4567 89ab cdef
|
||||
* Plain : 0123 4567 89ab cde7
|
||||
* Cipher : c957 4425 6a5e d31d
|
||||
*
|
||||
* Double-length key, single-length plaintext -
|
||||
* Key : 0123 4567 89ab cdef fedc ba98 7654 3210
|
||||
* Plain : 0123 4567 89ab cde7
|
||||
* Cipher : 7f1d 0a77 826b 8aff
|
||||
*
|
||||
* Double-length key, double-length plaintext -
|
||||
* Key : 0123 4567 89ab cdef fedc ba98 7654 3210
|
||||
* Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff
|
||||
* Cipher : 27a0 8440 406a df60 278f 47cf 42d6 15d7
|
||||
*
|
||||
* Triple-length key, single-length plaintext -
|
||||
* Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
|
||||
* Plain : 0123 4567 89ab cde7
|
||||
* Cipher : de0b 7c06 ae5e 0ed5
|
||||
*
|
||||
* Triple-length key, double-length plaintext -
|
||||
* Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
|
||||
* Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff
|
||||
* Cipher : ad0d 1b30 ac17 cf07 0ed1 1c63 81e4 4de5
|
||||
*
|
||||
* d3des V5.0a rwo 9208.07 18:44 Graven Imagery
|
||||
**********************************************************************/
|
104
crypto/hash-gnutls.c
Normal file
104
crypto/hash-gnutls.c
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* QEMU Crypto hash algorithms
|
||||
*
|
||||
* Copyright (c) 2021 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include <gnutls/crypto.h>
|
||||
#include "qapi/error.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "hashpriv.h"
|
||||
|
||||
|
||||
static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG__MAX] = {
|
||||
[QCRYPTO_HASH_ALG_MD5] = GNUTLS_DIG_MD5,
|
||||
[QCRYPTO_HASH_ALG_SHA1] = GNUTLS_DIG_SHA1,
|
||||
[QCRYPTO_HASH_ALG_SHA224] = GNUTLS_DIG_SHA224,
|
||||
[QCRYPTO_HASH_ALG_SHA256] = GNUTLS_DIG_SHA256,
|
||||
[QCRYPTO_HASH_ALG_SHA384] = GNUTLS_DIG_SHA384,
|
||||
[QCRYPTO_HASH_ALG_SHA512] = GNUTLS_DIG_SHA512,
|
||||
[QCRYPTO_HASH_ALG_RIPEMD160] = GNUTLS_DIG_RMD160,
|
||||
};
|
||||
|
||||
gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg)
|
||||
{
|
||||
size_t i;
|
||||
const gnutls_digest_algorithm_t *algs;
|
||||
if (alg >= G_N_ELEMENTS(qcrypto_hash_alg_map) ||
|
||||
qcrypto_hash_alg_map[alg] == GNUTLS_DIG_UNKNOWN) {
|
||||
return false;
|
||||
}
|
||||
algs = gnutls_digest_list();
|
||||
for (i = 0; algs[i] != GNUTLS_DIG_UNKNOWN; i++) {
|
||||
if (algs[i] == qcrypto_hash_alg_map[alg]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qcrypto_gnutls_hash_bytesv(QCryptoHashAlgorithm alg,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
uint8_t **result,
|
||||
size_t *resultlen,
|
||||
Error **errp)
|
||||
{
|
||||
int i, ret;
|
||||
gnutls_hash_hd_t hash;
|
||||
|
||||
if (!qcrypto_hash_supports(alg)) {
|
||||
error_setg(errp,
|
||||
"Unknown hash algorithm %d",
|
||||
alg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = gnutls_hash_get_len(qcrypto_hash_alg_map[alg]);
|
||||
if (*resultlen == 0) {
|
||||
*resultlen = ret;
|
||||
*result = g_new0(uint8_t, *resultlen);
|
||||
} else if (*resultlen != ret) {
|
||||
error_setg(errp,
|
||||
"Result buffer size %zu is smaller than hash %d",
|
||||
*resultlen, ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = gnutls_hash_init(&hash, qcrypto_hash_alg_map[alg]);
|
||||
if (ret < 0) {
|
||||
error_setg(errp,
|
||||
"Unable to initialize hash algorithm: %s",
|
||||
gnutls_strerror(ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < niov; i++) {
|
||||
gnutls_hash(hash, iov[i].iov_base, iov[i].iov_len);
|
||||
}
|
||||
|
||||
gnutls_hash_deinit(hash, *result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
QCryptoHashDriver qcrypto_hash_lib_driver = {
|
||||
.hash_bytesv = qcrypto_gnutls_hash_bytesv,
|
||||
};
|
139
crypto/hmac-gnutls.c
Normal file
139
crypto/hmac-gnutls.c
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* QEMU Crypto hmac algorithms
|
||||
*
|
||||
* Copyright (c) 2021 Red Hat, Inc.
|
||||
*
|
||||
* Derived from hmac-gcrypt.c:
|
||||
*
|
||||
* Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
* (at your option) any later version. See the COPYING file in the
|
||||
* top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include <gnutls/crypto.h>
|
||||
|
||||
#include "qapi/error.h"
|
||||
#include "crypto/hmac.h"
|
||||
#include "hmacpriv.h"
|
||||
|
||||
static int qcrypto_hmac_alg_map[QCRYPTO_HASH_ALG__MAX] = {
|
||||
[QCRYPTO_HASH_ALG_MD5] = GNUTLS_MAC_MD5,
|
||||
[QCRYPTO_HASH_ALG_SHA1] = GNUTLS_MAC_SHA1,
|
||||
[QCRYPTO_HASH_ALG_SHA224] = GNUTLS_MAC_SHA224,
|
||||
[QCRYPTO_HASH_ALG_SHA256] = GNUTLS_MAC_SHA256,
|
||||
[QCRYPTO_HASH_ALG_SHA384] = GNUTLS_MAC_SHA384,
|
||||
[QCRYPTO_HASH_ALG_SHA512] = GNUTLS_MAC_SHA512,
|
||||
[QCRYPTO_HASH_ALG_RIPEMD160] = GNUTLS_MAC_RMD160,
|
||||
};
|
||||
|
||||
typedef struct QCryptoHmacGnutls QCryptoHmacGnutls;
|
||||
struct QCryptoHmacGnutls {
|
||||
gnutls_hmac_hd_t handle;
|
||||
};
|
||||
|
||||
bool qcrypto_hmac_supports(QCryptoHashAlgorithm alg)
|
||||
{
|
||||
size_t i;
|
||||
const gnutls_digest_algorithm_t *algs;
|
||||
if (alg >= G_N_ELEMENTS(qcrypto_hmac_alg_map) ||
|
||||
qcrypto_hmac_alg_map[alg] == GNUTLS_DIG_UNKNOWN) {
|
||||
return false;
|
||||
}
|
||||
algs = gnutls_digest_list();
|
||||
for (i = 0; algs[i] != GNUTLS_DIG_UNKNOWN; i++) {
|
||||
if (algs[i] == qcrypto_hmac_alg_map[alg]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void *qcrypto_hmac_ctx_new(QCryptoHashAlgorithm alg,
|
||||
const uint8_t *key, size_t nkey,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoHmacGnutls *ctx;
|
||||
int err;
|
||||
|
||||
if (!qcrypto_hmac_supports(alg)) {
|
||||
error_setg(errp, "Unsupported hmac algorithm %s",
|
||||
QCryptoHashAlgorithm_str(alg));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx = g_new0(QCryptoHmacGnutls, 1);
|
||||
|
||||
err = gnutls_hmac_init(&ctx->handle,
|
||||
qcrypto_hmac_alg_map[alg],
|
||||
(const void *)key, nkey);
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot initialize hmac: %s",
|
||||
gnutls_strerror(err));
|
||||
goto error;
|
||||
}
|
||||
|
||||
return ctx;
|
||||
|
||||
error:
|
||||
g_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
qcrypto_gnutls_hmac_ctx_free(QCryptoHmac *hmac)
|
||||
{
|
||||
QCryptoHmacGnutls *ctx;
|
||||
|
||||
ctx = hmac->opaque;
|
||||
gnutls_hmac_deinit(ctx->handle, NULL);
|
||||
|
||||
g_free(ctx);
|
||||
}
|
||||
|
||||
static int
|
||||
qcrypto_gnutls_hmac_bytesv(QCryptoHmac *hmac,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
uint8_t **result,
|
||||
size_t *resultlen,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoHmacGnutls *ctx;
|
||||
uint32_t ret;
|
||||
int i;
|
||||
|
||||
ctx = hmac->opaque;
|
||||
|
||||
for (i = 0; i < niov; i++) {
|
||||
gnutls_hmac(ctx->handle, iov[i].iov_base, iov[i].iov_len);
|
||||
}
|
||||
|
||||
ret = gnutls_hmac_get_len(qcrypto_hmac_alg_map[hmac->alg]);
|
||||
if (ret <= 0) {
|
||||
error_setg(errp, "Unable to get hmac length: %s",
|
||||
gnutls_strerror(ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*resultlen == 0) {
|
||||
*resultlen = ret;
|
||||
*result = g_new0(uint8_t, *resultlen);
|
||||
} else if (*resultlen != ret) {
|
||||
error_setg(errp, "Result buffer size %zu is smaller than hmac %d",
|
||||
*resultlen, ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
gnutls_hmac_output(ctx->handle, *result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
QCryptoHmacDriver qcrypto_hmac_lib_driver = {
|
||||
.hmac_bytesv = qcrypto_gnutls_hmac_bytesv,
|
||||
.hmac_free = qcrypto_gnutls_hmac_ctx_free,
|
||||
};
|
@ -35,21 +35,6 @@
|
||||
#include "crypto/random.h"
|
||||
|
||||
/* #define DEBUG_GNUTLS */
|
||||
|
||||
/*
|
||||
* We need to init gcrypt threading if
|
||||
*
|
||||
* - gcrypt < 1.6.0
|
||||
*
|
||||
*/
|
||||
|
||||
#if (defined(CONFIG_GCRYPT) && \
|
||||
(GCRYPT_VERSION_NUMBER < 0x010600))
|
||||
#define QCRYPTO_INIT_GCRYPT_THREADS
|
||||
#else
|
||||
#undef QCRYPTO_INIT_GCRYPT_THREADS
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_GNUTLS
|
||||
static void qcrypto_gnutls_log(int level, const char *str)
|
||||
{
|
||||
@ -57,55 +42,8 @@ static void qcrypto_gnutls_log(int level, const char *str)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef QCRYPTO_INIT_GCRYPT_THREADS
|
||||
static int qcrypto_gcrypt_mutex_init(void **priv)
|
||||
{ \
|
||||
QemuMutex *lock = NULL;
|
||||
lock = g_new0(QemuMutex, 1);
|
||||
qemu_mutex_init(lock);
|
||||
*priv = lock;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcrypto_gcrypt_mutex_destroy(void **priv)
|
||||
{
|
||||
QemuMutex *lock = *priv;
|
||||
qemu_mutex_destroy(lock);
|
||||
g_free(lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcrypto_gcrypt_mutex_lock(void **priv)
|
||||
{
|
||||
QemuMutex *lock = *priv;
|
||||
qemu_mutex_lock(lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcrypto_gcrypt_mutex_unlock(void **priv)
|
||||
{
|
||||
QemuMutex *lock = *priv;
|
||||
qemu_mutex_unlock(lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct gcry_thread_cbs qcrypto_gcrypt_thread_impl = {
|
||||
(GCRY_THREAD_OPTION_PTHREAD | (GCRY_THREAD_OPTION_VERSION << 8)),
|
||||
NULL,
|
||||
qcrypto_gcrypt_mutex_init,
|
||||
qcrypto_gcrypt_mutex_destroy,
|
||||
qcrypto_gcrypt_mutex_lock,
|
||||
qcrypto_gcrypt_mutex_unlock,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
||||
};
|
||||
#endif /* QCRYPTO_INIT_GCRYPT */
|
||||
|
||||
int qcrypto_init(Error **errp)
|
||||
{
|
||||
#ifdef QCRYPTO_INIT_GCRYPT_THREADS
|
||||
gcry_control(GCRYCTL_SET_THREAD_CBS, &qcrypto_gcrypt_thread_impl);
|
||||
#endif /* QCRYPTO_INIT_GCRYPT_THREADS */
|
||||
|
||||
#ifdef CONFIG_GNUTLS
|
||||
int ret;
|
||||
ret = gnutls_global_init();
|
||||
|
@ -5,7 +5,6 @@ crypto_ss.add(files(
|
||||
'block-qcow.c',
|
||||
'block.c',
|
||||
'cipher.c',
|
||||
'desrfb.c',
|
||||
'hash.c',
|
||||
'hmac.c',
|
||||
'ivgen-essiv.c',
|
||||
@ -24,14 +23,16 @@ crypto_ss.add(files(
|
||||
|
||||
if nettle.found()
|
||||
crypto_ss.add(nettle, files('hash-nettle.c', 'hmac-nettle.c', 'pbkdf-nettle.c'))
|
||||
if xts == 'private'
|
||||
crypto_ss.add(files('xts.c'))
|
||||
endif
|
||||
elif gcrypt.found()
|
||||
crypto_ss.add(gcrypt, files('hash-gcrypt.c', 'hmac-gcrypt.c', 'pbkdf-gcrypt.c'))
|
||||
elif gnutls_crypto.found()
|
||||
crypto_ss.add(gnutls, files('hash-gnutls.c', 'hmac-gnutls.c', 'pbkdf-gnutls.c'))
|
||||
else
|
||||
crypto_ss.add(files('hash-glib.c', 'hmac-glib.c', 'pbkdf-stub.c'))
|
||||
endif
|
||||
if xts == 'private'
|
||||
crypto_ss.add(files('xts.c'))
|
||||
endif
|
||||
|
||||
crypto_ss.add(when: 'CONFIG_SECRET_KEYRING', if_true: files('secret_keyring.c'))
|
||||
crypto_ss.add(when: 'CONFIG_AF_ALG', if_true: files('afalg.c', 'cipher-afalg.c', 'hash-afalg.c'))
|
||||
@ -39,6 +40,9 @@ crypto_ss.add(when: gnutls, if_true: files('tls-cipher-suites.c'))
|
||||
|
||||
util_ss.add(files('aes.c'))
|
||||
util_ss.add(files('init.c'))
|
||||
if gnutls.found()
|
||||
util_ss.add(gnutls)
|
||||
endif
|
||||
|
||||
if gcrypt.found()
|
||||
util_ss.add(gcrypt, files('random-gcrypt.c'))
|
||||
|
90
crypto/pbkdf-gnutls.c
Normal file
90
crypto/pbkdf-gnutls.c
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* QEMU Crypto PBKDF support (Password-Based Key Derivation Function)
|
||||
*
|
||||
* Copyright (c) 2021 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include <gnutls/crypto.h>
|
||||
#include "qapi/error.h"
|
||||
#include "crypto/pbkdf.h"
|
||||
|
||||
bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash)
|
||||
{
|
||||
switch (hash) {
|
||||
case QCRYPTO_HASH_ALG_MD5:
|
||||
case QCRYPTO_HASH_ALG_SHA1:
|
||||
case QCRYPTO_HASH_ALG_SHA224:
|
||||
case QCRYPTO_HASH_ALG_SHA256:
|
||||
case QCRYPTO_HASH_ALG_SHA384:
|
||||
case QCRYPTO_HASH_ALG_SHA512:
|
||||
case QCRYPTO_HASH_ALG_RIPEMD160:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int qcrypto_pbkdf2(QCryptoHashAlgorithm hash,
|
||||
const uint8_t *key, size_t nkey,
|
||||
const uint8_t *salt, size_t nsalt,
|
||||
uint64_t iterations,
|
||||
uint8_t *out, size_t nout,
|
||||
Error **errp)
|
||||
{
|
||||
static const int hash_map[QCRYPTO_HASH_ALG__MAX] = {
|
||||
[QCRYPTO_HASH_ALG_MD5] = GNUTLS_DIG_MD5,
|
||||
[QCRYPTO_HASH_ALG_SHA1] = GNUTLS_DIG_SHA1,
|
||||
[QCRYPTO_HASH_ALG_SHA224] = GNUTLS_DIG_SHA224,
|
||||
[QCRYPTO_HASH_ALG_SHA256] = GNUTLS_DIG_SHA256,
|
||||
[QCRYPTO_HASH_ALG_SHA384] = GNUTLS_DIG_SHA384,
|
||||
[QCRYPTO_HASH_ALG_SHA512] = GNUTLS_DIG_SHA512,
|
||||
[QCRYPTO_HASH_ALG_RIPEMD160] = GNUTLS_DIG_RMD160,
|
||||
};
|
||||
int ret;
|
||||
const gnutls_datum_t gkey = { (unsigned char *)key, nkey };
|
||||
const gnutls_datum_t gsalt = { (unsigned char *)salt, nsalt };
|
||||
|
||||
if (iterations > ULONG_MAX) {
|
||||
error_setg_errno(errp, ERANGE,
|
||||
"PBKDF iterations %llu must be less than %lu",
|
||||
(long long unsigned)iterations, ULONG_MAX);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hash >= G_N_ELEMENTS(hash_map) ||
|
||||
hash_map[hash] == GNUTLS_DIG_UNKNOWN) {
|
||||
error_setg_errno(errp, ENOSYS,
|
||||
"PBKDF does not support hash algorithm %s",
|
||||
QCryptoHashAlgorithm_str(hash));
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = gnutls_pbkdf2(hash_map[hash],
|
||||
&gkey,
|
||||
&gsalt,
|
||||
iterations,
|
||||
out,
|
||||
nout);
|
||||
if (ret != 0) {
|
||||
error_setg(errp, "Cannot derive password: %s",
|
||||
gnutls_strerror(ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -33,7 +33,7 @@ int print_insn_hexagon(bfd_vma memaddr, struct disassemble_info *info)
|
||||
{
|
||||
uint32_t words[PACKET_WORDS_MAX];
|
||||
bool found_end = false;
|
||||
GString *buf = g_string_sized_new(PACKET_BUFFER_LEN);
|
||||
GString *buf;
|
||||
int i, len;
|
||||
|
||||
for (i = 0; i < PACKET_WORDS_MAX && !found_end; i++) {
|
||||
@ -57,6 +57,7 @@ int print_insn_hexagon(bfd_vma memaddr, struct disassemble_info *info)
|
||||
return PACKET_WORDS_MAX * sizeof(uint32_t);
|
||||
}
|
||||
|
||||
buf = g_string_sized_new(PACKET_BUFFER_LEN);
|
||||
len = disassemble_hexagon(words, i, memaddr, buf);
|
||||
(*info->fprintf_func)(info->stream, "%s", buf->str);
|
||||
g_string_free(buf, true);
|
||||
|
14
docs/_templates/footer.html
vendored
Normal file
14
docs/_templates/footer.html
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
{% extends "!footer.html" %}
|
||||
{% block extrafooter %}
|
||||
|
||||
<!-- Empty para to force a blank line after "Built with Sphinx ..." -->
|
||||
<p></p>
|
||||
|
||||
<p>This documentation is for QEMU version {{ version }}.</p>
|
||||
|
||||
{% trans path=pathto('about/license') %}
|
||||
<p><a href="{{ path }}">QEMU and this manual are released under the
|
||||
GNU General Public License, version 2.</a></p>
|
||||
{% endtrans %}
|
||||
{{ super() }}
|
||||
{% endblock %}
|
27
docs/about/index.rst
Normal file
27
docs/about/index.rst
Normal file
@ -0,0 +1,27 @@
|
||||
About QEMU
|
||||
==========
|
||||
|
||||
QEMU is a generic and open source machine emulator and virtualizer.
|
||||
|
||||
QEMU can be used in several different ways. The most common is for
|
||||
"system emulation", where it provides a virtual model of an
|
||||
entire machine (CPU, memory and emulated devices) to run a guest OS.
|
||||
In this mode the CPU may be fully emulated, or it may work with
|
||||
a hypervisor such as KVM, Xen, Hax or Hypervisor.Framework to
|
||||
allow the guest to run directly on the host CPU.
|
||||
|
||||
The second supported way to use QEMU is "user mode emulation",
|
||||
where QEMU can launch processes compiled for one CPU on another CPU.
|
||||
In this mode the CPU is always emulated.
|
||||
|
||||
QEMU also provides a number of standalone commandline utilities,
|
||||
such as the ``qemu-img`` disk image utility that allows you to create,
|
||||
convert and modify disk images.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
build-platforms
|
||||
deprecated
|
||||
removed-features
|
||||
license
|
@ -9,8 +9,145 @@ trouble after a recent upgrade.
|
||||
System emulator command line arguments
|
||||
--------------------------------------
|
||||
|
||||
``-net ...,name=``\ *name* (removed in 5.1)
|
||||
'''''''''''''''''''''''''''''''''''''''''''
|
||||
``-hdachs`` (removed in 2.12)
|
||||
'''''''''''''''''''''''''''''
|
||||
|
||||
The geometry defined by ``-hdachs c,h,s,t`` should now be specified via
|
||||
``-device ide-hd,drive=dr,cyls=c,heads=h,secs=s,bios-chs-trans=t``
|
||||
(together with ``-drive if=none,id=dr,...``).
|
||||
|
||||
``-net channel`` (removed in 2.12)
|
||||
''''''''''''''''''''''''''''''''''
|
||||
|
||||
This option has been replaced by ``-net user,guestfwd=...``.
|
||||
|
||||
``-net dump`` (removed in 2.12)
|
||||
'''''''''''''''''''''''''''''''
|
||||
|
||||
``-net dump[,vlan=n][,file=filename][,len=maxlen]`` has been replaced by
|
||||
``-object filter-dump,id=id,netdev=dev[,file=filename][,maxlen=maxlen]``.
|
||||
Note that the new syntax works with netdev IDs instead of the old "vlan" hubs.
|
||||
|
||||
``-no-kvm-pit`` (removed in 2.12)
|
||||
'''''''''''''''''''''''''''''''''
|
||||
|
||||
This was just a dummy option that has been ignored, since the in-kernel PIT
|
||||
cannot be disabled separately from the irqchip anymore. A similar effect
|
||||
(which also disables the KVM IOAPIC) can be obtained with
|
||||
``-M kernel_irqchip=split``.
|
||||
|
||||
``-tdf`` (removed in 2.12)
|
||||
''''''''''''''''''''''''''
|
||||
|
||||
There is no replacement, the ``-tdf`` option has just been ignored since the
|
||||
behaviour that could be changed by this option in qemu-kvm is now the default
|
||||
when using the KVM PIT. It still can be requested explicitly using
|
||||
``-global kvm-pit.lost_tick_policy=delay``.
|
||||
|
||||
``-drive secs=s``, ``-drive heads=h`` & ``-drive cyls=c`` (removed in 3.0)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The drive geometry should now be specified via
|
||||
``-device ...,drive=dr,cyls=c,heads=h,secs=s`` (together with
|
||||
``-drive if=none,id=dr,...``).
|
||||
|
||||
``-drive serial=``, ``-drive trans=`` & ``-drive addr=`` (removed in 3.0)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Use ``-device ...,drive=dr,serial=r,bios-chs-trans=t,addr=a`` instead
|
||||
(together with ``-drive if=none,id=dr,...``).
|
||||
|
||||
``-net ...,vlan=x`` (removed in 3.0)
|
||||
''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The term "vlan" was very confusing for most users in this context (it's about
|
||||
specifying a hub ID, not about IEEE 802.1Q or something similar), so this
|
||||
has been removed. To connect one NIC frontend with a network backend, either
|
||||
use ``-nic ...`` (e.g. for on-board NICs) or use ``-netdev ...,id=n`` together
|
||||
with ``-device ...,netdev=n`` (for full control over pluggable NICs). To
|
||||
connect multiple NICs or network backends via a hub device (which is what
|
||||
vlan did), use ``-nic hubport,hubid=x,...`` or
|
||||
``-netdev hubport,id=n,hubid=x,...`` (with ``-device ...,netdev=n``) instead.
|
||||
|
||||
``-no-kvm-irqchip`` (removed in 3.0)
|
||||
''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Use ``-machine kernel_irqchip=off`` instead.
|
||||
|
||||
``-no-kvm-pit-reinjection`` (removed in 3.0)
|
||||
''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Use ``-global kvm-pit.lost_tick_policy=discard`` instead.
|
||||
|
||||
``-balloon`` (removed in 3.1)
|
||||
'''''''''''''''''''''''''''''
|
||||
|
||||
The ``-balloon virtio`` option has been replaced by ``-device virtio-balloon``.
|
||||
The ``-balloon none`` option was a no-op and has no replacement.
|
||||
|
||||
``-bootp`` (removed in 3.1)
|
||||
'''''''''''''''''''''''''''
|
||||
|
||||
The ``-bootp /some/file`` argument is replaced by either
|
||||
``-netdev user,id=x,bootp=/some/file`` (for pluggable NICs, accompanied with
|
||||
``-device ...,netdev=x``), or ``-nic user,bootp=/some/file`` (for on-board NICs).
|
||||
The new syntax allows different settings to be provided per NIC.
|
||||
|
||||
``-redir`` (removed in 3.1)
|
||||
'''''''''''''''''''''''''''
|
||||
|
||||
The ``-redir [tcp|udp]:hostport:[guestaddr]:guestport`` option is replaced
|
||||
by either ``-netdev
|
||||
user,id=x,hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport``
|
||||
(for pluggable NICs, accompanied with ``-device ...,netdev=x``) or by the option
|
||||
``-nic user,hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport``
|
||||
(for on-board NICs). The new syntax allows different settings to be provided
|
||||
per NIC.
|
||||
|
||||
``-smb`` (removed in 3.1)
|
||||
'''''''''''''''''''''''''
|
||||
|
||||
The ``-smb /some/dir`` argument is replaced by either
|
||||
``-netdev user,id=x,smb=/some/dir`` (for pluggable NICs, accompanied with
|
||||
``-device ...,netdev=x``), or ``-nic user,smb=/some/dir`` (for on-board NICs).
|
||||
The new syntax allows different settings to be provided per NIC.
|
||||
|
||||
``-tftp`` (removed in 3.1)
|
||||
''''''''''''''''''''''''''
|
||||
|
||||
The ``-tftp /some/dir`` argument is replaced by either
|
||||
``-netdev user,id=x,tftp=/some/dir`` (for pluggable NICs, accompanied with
|
||||
``-device ...,netdev=x``), or ``-nic user,tftp=/some/dir`` (for embedded NICs).
|
||||
The new syntax allows different settings to be provided per NIC.
|
||||
|
||||
``-localtime`` (removed in 3.1)
|
||||
'''''''''''''''''''''''''''''''
|
||||
|
||||
Replaced by ``-rtc base=localtime``.
|
||||
|
||||
``-nodefconfig`` (removed in 3.1)
|
||||
'''''''''''''''''''''''''''''''''
|
||||
|
||||
Use ``-no-user-config`` instead.
|
||||
|
||||
``-rtc-td-hack`` (removed in 3.1)
|
||||
'''''''''''''''''''''''''''''''''
|
||||
|
||||
Use ``-rtc driftfix=slew`` instead.
|
||||
|
||||
``-startdate`` (removed in 3.1)
|
||||
'''''''''''''''''''''''''''''''
|
||||
|
||||
Replaced by ``-rtc base=date``.
|
||||
|
||||
``-vnc ...,tls=...``, ``-vnc ...,x509=...`` & ``-vnc ...,x509verify=...``
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The "tls-creds" option should be used instead to point to a "tls-creds-x509"
|
||||
object created using "-object".
|
||||
|
||||
``-net ...,name=...`` (removed in 5.1)
|
||||
''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The ``name`` parameter of the ``-net`` option was a synonym
|
||||
for the ``id`` parameter, which should now be used instead.
|
||||
@ -124,7 +261,7 @@ devices. Drives the board doesn't pick up can no longer be used with
|
||||
'''''''''''''''''''''''''''''''''''''
|
||||
|
||||
This option was undocumented and not used in the field.
|
||||
Use `-device usb-ccid`` instead.
|
||||
Use ``-device usb-ccid`` instead.
|
||||
|
||||
RISC-V firmware not booted by default (removed in 5.1)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
@ -219,6 +356,17 @@ Specify the properties for the object as top-level arguments instead.
|
||||
Human Monitor Protocol (HMP) commands
|
||||
-------------------------------------
|
||||
|
||||
``usb_add`` and ``usb_remove`` (removed in 2.12)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Replaced by ``device_add`` and ``device_del`` (use ``device_add help`` for a
|
||||
list of available devices).
|
||||
|
||||
``host_net_add`` and ``host_net_remove`` (removed in 2.12)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Replaced by ``netdev_add`` and ``netdev_del``.
|
||||
|
||||
The ``hub_id`` parameter of ``hostfwd_add`` / ``hostfwd_remove`` (removed in 5.0)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
@ -325,6 +473,22 @@ Removed without replacement.
|
||||
System emulator machines
|
||||
------------------------
|
||||
|
||||
``s390-virtio`` (removed in 2.6)
|
||||
''''''''''''''''''''''''''''''''
|
||||
|
||||
Use the ``s390-ccw-virtio`` machine instead.
|
||||
|
||||
The m68k ``dummy`` machine (removed in 2.9)
|
||||
'''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Use the ``none`` machine with the ``loader`` device instead.
|
||||
|
||||
``xlnx-ep108`` (removed in 3.0)
|
||||
'''''''''''''''''''''''''''''''
|
||||
|
||||
The EP108 was an early access development board that is no longer used.
|
||||
Use the ``xlnx-zcu102`` machine instead.
|
||||
|
||||
``spike_v1.9.1`` and ``spike_v1.10`` (removed in 5.1)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
@ -343,8 +507,8 @@ mips ``fulong2e`` machine alias (removed in 6.0)
|
||||
|
||||
This machine has been renamed ``fuloong2e``.
|
||||
|
||||
``pc-1.0``, ``pc-1.1``, ``pc-1.2`` and ``pc-1.3`` (removed in 6.0)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
``pc-0.10`` up to ``pc-1.3`` (removed in 4.0 up to 6.0)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
These machine types were very old and likely could not be used for live
|
||||
migration from old QEMU versions anymore. Use a newer machine type instead.
|
||||
@ -365,6 +529,17 @@ running the old binaries, you can use older versions of QEMU.
|
||||
System emulator devices
|
||||
-----------------------
|
||||
|
||||
``spapr-pci-vfio-host-bridge`` (removed in 2.12)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The ``spapr-pci-vfio-host-bridge`` device type has been replaced by the
|
||||
``spapr-pci-host-bridge`` device type.
|
||||
|
||||
``ivshmem`` (removed in 4.0)
|
||||
''''''''''''''''''''''''''''
|
||||
|
||||
Replaced by either the ``ivshmem-plain`` or ``ivshmem-doorbell``.
|
||||
|
||||
``ide-drive`` (removed in 6.0)
|
||||
''''''''''''''''''''''''''''''
|
||||
|
370
docs/barrier.txt
370
docs/barrier.txt
@ -1,370 +0,0 @@
|
||||
QEMU Barrier Client
|
||||
|
||||
|
||||
* About
|
||||
|
||||
Barrier is a KVM (Keyboard-Video-Mouse) software forked from Symless's
|
||||
synergy 1.9 codebase.
|
||||
|
||||
See https://github.com/debauchee/barrier
|
||||
|
||||
* QEMU usage
|
||||
|
||||
Generally, mouse and keyboard are grabbed through the QEMU video
|
||||
interface emulation.
|
||||
|
||||
But when we want to use a video graphic adapter via a PCI passthrough
|
||||
there is no way to provide the keyboard and mouse inputs to the VM
|
||||
except by plugging a second set of mouse and keyboard to the host
|
||||
or by installing a KVM software in the guest OS.
|
||||
|
||||
The QEMU Barrier client avoids this by implementing directly the Barrier
|
||||
protocol into QEMU.
|
||||
|
||||
This protocol is enabled by adding an input-barrier object to QEMU.
|
||||
|
||||
Syntax: input-barrier,id=<object-id>,name=<guest display name>
|
||||
[,server=<barrier server address>][,port=<barrier server port>]
|
||||
[,x-origin=<x-origin>][,y-origin=<y-origin>]
|
||||
[,width=<width>][,height=<height>]
|
||||
|
||||
The object can be added on the QEMU command line, for instance with:
|
||||
|
||||
... -object input-barrier,id=barrier0,name=VM-1 ...
|
||||
|
||||
where VM-1 is the name the display configured int the Barrier server
|
||||
on the host providing the mouse and the keyboard events.
|
||||
|
||||
by default <barrier server address> is "localhost", port is 24800,
|
||||
<x-origin> and <y-origin> are set to 0, <width> and <height> to
|
||||
1920 and 1080.
|
||||
|
||||
If Barrier server is stopped QEMU needs to be reconnected manually,
|
||||
by removing and re-adding the input-barrier object, for instance
|
||||
with the help of the HMP monitor:
|
||||
|
||||
(qemu) object_del barrier0
|
||||
(qemu) object_add input-barrier,id=barrier0,name=VM-1
|
||||
|
||||
* Message format
|
||||
|
||||
Message format between the server and client is in two parts:
|
||||
|
||||
1- the payload length is a 32bit integer in network endianness,
|
||||
2- the payload
|
||||
|
||||
The payload starts with a 4byte string (without NUL) which is the
|
||||
command. The first command between the server and the client
|
||||
is the only command not encoded on 4 bytes ("Barrier").
|
||||
The remaining part of the payload is decoded according to the command.
|
||||
|
||||
* Protocol Description (from barrier/src/lib/barrier/protocol_types.h)
|
||||
|
||||
- barrierCmdHello "Barrier"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: { int16_t minor, int16_t major }
|
||||
Description:
|
||||
|
||||
Say hello to client
|
||||
minor = protocol major version number supported by server
|
||||
major = protocol minor version number supported by server
|
||||
|
||||
- barrierCmdHelloBack "Barrier"
|
||||
|
||||
Direction: client ->server
|
||||
Parameters: { int16_t minor, int16_t major, char *name}
|
||||
Description:
|
||||
|
||||
Respond to hello from server
|
||||
minor = protocol major version number supported by client
|
||||
major = protocol minor version number supported by client
|
||||
name = client name
|
||||
|
||||
- barrierCmdDInfo "DINF"
|
||||
|
||||
Direction: client ->server
|
||||
Parameters: { int16_t x_origin, int16_t y_origin, int16_t width, int16_t height, int16_t x, int16_t y}
|
||||
Description:
|
||||
|
||||
The client screen must send this message in response to the
|
||||
barrierCmdQInfo message. It must also send this message when the
|
||||
screen's resolution changes. In this case, the client screen should
|
||||
ignore any barrierCmdDMouseMove messages until it receives a
|
||||
barrierCmdCInfoAck in order to prevent attempts to move the mouse off
|
||||
the new screen area.
|
||||
|
||||
- barrierCmdCNoop "CNOP"
|
||||
|
||||
Direction: client -> server
|
||||
Parameters: None
|
||||
Description:
|
||||
|
||||
No operation
|
||||
|
||||
- barrierCmdCClose "CBYE"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: None
|
||||
Description:
|
||||
|
||||
Close connection
|
||||
|
||||
- barrierCmdCEnter "CINN"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: { int16_t x, int16_t y, int32_t seq, int16_t modifier }
|
||||
Description:
|
||||
|
||||
Enter screen.
|
||||
x,y = entering screen absolute coordinates
|
||||
seq = sequence number, which is used to order messages between
|
||||
screens. the secondary screen must return this number
|
||||
with some messages
|
||||
modifier = modifier key mask. this will have bits set for each
|
||||
toggle modifier key that is activated on entry to the
|
||||
screen. the secondary screen should adjust its toggle
|
||||
modifiers to reflect that state.
|
||||
|
||||
- barrierCmdCLeave "COUT"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: None
|
||||
Description:
|
||||
|
||||
Leaving screen. the secondary screen should send clipboard data in
|
||||
response to this message for those clipboards that it has grabbed
|
||||
(i.e. has sent a barrierCmdCClipboard for and has not received a
|
||||
barrierCmdCClipboard for with a greater sequence number) and that
|
||||
were grabbed or have changed since the last leave.
|
||||
|
||||
- barrierCmdCClipboard "CCLP"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: { int8_t id, int32_t seq }
|
||||
Description:
|
||||
|
||||
Grab clipboard. Sent by screen when some other app on that screen
|
||||
grabs a clipboard.
|
||||
id = the clipboard identifier
|
||||
seq = sequence number. Client must use the sequence number passed in
|
||||
the most recent barrierCmdCEnter. the server always sends 0.
|
||||
|
||||
- barrierCmdCScreenSaver "CSEC"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: { int8_t started }
|
||||
Description:
|
||||
|
||||
Screensaver change.
|
||||
started = Screensaver on primary has started (1) or closed (0)
|
||||
|
||||
- barrierCmdCResetOptions "CROP"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: None
|
||||
Description:
|
||||
|
||||
Reset options. Client should reset all of its options to their
|
||||
defaults.
|
||||
|
||||
- barrierCmdCInfoAck "CIAK"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: None
|
||||
Description:
|
||||
|
||||
Resolution change acknowledgment. Sent by server in response to a
|
||||
client screen's barrierCmdDInfo. This is sent for every
|
||||
barrierCmdDInfo, whether or not the server had sent a barrierCmdQInfo.
|
||||
|
||||
- barrierCmdCKeepAlive "CALV"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: None
|
||||
Description:
|
||||
|
||||
Keep connection alive. Sent by the server periodically to verify
|
||||
that connections are still up and running. clients must reply in
|
||||
kind on receipt. if the server gets an error sending the message or
|
||||
does not receive a reply within a reasonable time then the server
|
||||
disconnects the client. if the client doesn't receive these (or any
|
||||
message) periodically then it should disconnect from the server. the
|
||||
appropriate interval is defined by an option.
|
||||
|
||||
- barrierCmdDKeyDown "DKDN"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: { int16_t keyid, int16_t modifier [,int16_t button] }
|
||||
Description:
|
||||
|
||||
Key pressed.
|
||||
keyid = X11 key id
|
||||
modified = modified mask
|
||||
button = X11 Xkb keycode (optional)
|
||||
|
||||
- barrierCmdDKeyRepeat "DKRP"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: { int16_t keyid, int16_t modifier, int16_t repeat [,int16_t button] }
|
||||
Description:
|
||||
|
||||
Key auto-repeat.
|
||||
keyid = X11 key id
|
||||
modified = modified mask
|
||||
repeat = number of repeats
|
||||
button = X11 Xkb keycode (optional)
|
||||
|
||||
- barrierCmdDKeyUp "DKUP"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: { int16_t keyid, int16_t modifier [,int16_t button] }
|
||||
Description:
|
||||
|
||||
Key released.
|
||||
keyid = X11 key id
|
||||
modified = modified mask
|
||||
button = X11 Xkb keycode (optional)
|
||||
|
||||
- barrierCmdDMouseDown "DMDN"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: { int8_t button }
|
||||
Description:
|
||||
|
||||
Mouse button pressed.
|
||||
button = button id
|
||||
|
||||
- barrierCmdDMouseUp "DMUP"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: { int8_t button }
|
||||
Description:
|
||||
|
||||
Mouse button release.
|
||||
button = button id
|
||||
|
||||
- barrierCmdDMouseMove "DMMV"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: { int16_t x, int16_t y }
|
||||
Description:
|
||||
|
||||
Absolute mouse moved.
|
||||
x,y = absolute screen coordinates
|
||||
|
||||
- barrierCmdDMouseRelMove "DMRM"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: { int16_t x, int16_t y }
|
||||
Description:
|
||||
|
||||
Relative mouse moved.
|
||||
x,y = r relative screen coordinates
|
||||
|
||||
- barrierCmdDMouseWheel "DMWM"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: { int16_t x , int16_t y } or { int16_t y }
|
||||
Description:
|
||||
|
||||
Mouse scroll. The delta should be +120 for one tick forward (away
|
||||
from the user) or right and -120 for one tick backward (toward the
|
||||
user) or left.
|
||||
x = x delta
|
||||
y = y delta
|
||||
|
||||
- barrierCmdDClipboard "DCLP"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: { int8_t id, int32_t seq, int8_t mark, char *data }
|
||||
Description:
|
||||
|
||||
Clipboard data.
|
||||
id = clipboard id
|
||||
seq = sequence number. The sequence number is 0 when sent by the
|
||||
server. Client screens should use the/ sequence number from
|
||||
the most recent barrierCmdCEnter.
|
||||
|
||||
- barrierCmdDSetOptions "DSOP"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: { int32 t nb, { int32_t id, int32_t val }[] }
|
||||
Description:
|
||||
|
||||
Set options. Client should set the given option/value pairs.
|
||||
nb = numbers of { id, val } entries
|
||||
id = option id
|
||||
val = option new value
|
||||
|
||||
- barrierCmdDFileTransfer "DFTR"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: { int8_t mark, char *content }
|
||||
Description:
|
||||
|
||||
Transfer file data.
|
||||
mark = 0 means the content followed is the file size
|
||||
1 means the content followed is the chunk data
|
||||
2 means the file transfer is finished
|
||||
|
||||
- barrierCmdDDragInfo "DDRG" int16_t char *
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: { int16_t nb, char *content }
|
||||
Description:
|
||||
|
||||
Drag information.
|
||||
nb = number of dragging objects
|
||||
content = object's directory
|
||||
|
||||
- barrierCmdQInfo "QINF"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: None
|
||||
Description:
|
||||
|
||||
Query screen info
|
||||
Client should reply with a barrierCmdDInfo
|
||||
|
||||
- barrierCmdEIncompatible "EICV"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: { int16_t nb, major *minor }
|
||||
Description:
|
||||
|
||||
Incompatible version.
|
||||
major = major version
|
||||
minor = minor version
|
||||
|
||||
- barrierCmdEBusy "EBSY"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: None
|
||||
Description:
|
||||
|
||||
Name provided when connecting is already in use.
|
||||
|
||||
- barrierCmdEUnknown "EUNK"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: None
|
||||
Description:
|
||||
|
||||
Unknown client. Name provided when connecting is not in primary's
|
||||
screen configuration map.
|
||||
|
||||
- barrierCmdEBad "EBAD"
|
||||
|
||||
Direction: server -> client
|
||||
Parameters: None
|
||||
Description:
|
||||
|
||||
Protocol violation. Server should disconnect after sending this
|
||||
message.
|
||||
|
||||
* TO DO
|
||||
|
||||
- Enable SSL
|
||||
- Manage SetOptions/ResetOptions commands
|
||||
|
@ -1,52 +0,0 @@
|
||||
= Bootindex property =
|
||||
|
||||
Block and net devices have bootindex property. This property is used to
|
||||
determine the order in which firmware will consider devices for booting
|
||||
the guest OS. If the bootindex property is not set for a device, it gets
|
||||
lowest boot priority. There is no particular order in which devices with
|
||||
unset bootindex property will be considered for booting, but they will
|
||||
still be bootable.
|
||||
|
||||
== Example ==
|
||||
|
||||
Let's assume we have a QEMU machine with two NICs (virtio, e1000) and two
|
||||
disks (IDE, virtio):
|
||||
|
||||
qemu -drive file=disk1.img,if=none,id=disk1
|
||||
-device ide-hd,drive=disk1,bootindex=4
|
||||
-drive file=disk2.img,if=none,id=disk2
|
||||
-device virtio-blk-pci,drive=disk2,bootindex=3
|
||||
-netdev type=user,id=net0 -device virtio-net-pci,netdev=net0,bootindex=2
|
||||
-netdev type=user,id=net1 -device e1000,netdev=net1,bootindex=1
|
||||
|
||||
Given the command above, firmware should try to boot from the e1000 NIC
|
||||
first. If this fails, it should try the virtio NIC next; if this fails
|
||||
too, it should try the virtio disk, and then the IDE disk.
|
||||
|
||||
== Limitations ==
|
||||
|
||||
1. Some firmware has limitations on which devices can be considered for
|
||||
booting. For instance, the PC BIOS boot specification allows only one
|
||||
disk to be bootable. If boot from disk fails for some reason, the BIOS
|
||||
won't retry booting from other disk. It can still try to boot from
|
||||
floppy or net, though.
|
||||
|
||||
2. Sometimes, firmware cannot map the device path QEMU wants firmware to
|
||||
boot from to a boot method. It doesn't happen for devices the firmware
|
||||
can natively boot from, but if firmware relies on an option ROM for
|
||||
booting, and the same option ROM is used for booting from more then one
|
||||
device, the firmware may not be able to ask the option ROM to boot from
|
||||
a particular device reliably. For instance with the PC BIOS, if a SCSI HBA
|
||||
has three bootable devices target1, target3, target5 connected to it,
|
||||
the option ROM will have a boot method for each of them, but it is not
|
||||
possible to map from boot method back to a specific target. This is a
|
||||
shortcoming of the PC BIOS boot specification.
|
||||
|
||||
== Mixing bootindex and boot order parameters ==
|
||||
|
||||
Note that it does not make sense to use the bootindex property together
|
||||
with the "-boot order=..." (or "-boot once=...") parameter. The guest
|
||||
firmware implementations normally either support the one or the other,
|
||||
but not both parameters at the same time. Mixing them will result in
|
||||
undefined behavior, and thus the guest firmware will likely not boot
|
||||
from the expected devices.
|
89
docs/bypass-iommu.txt
Normal file
89
docs/bypass-iommu.txt
Normal file
@ -0,0 +1,89 @@
|
||||
BYPASS IOMMU PROPERTY
|
||||
=====================
|
||||
|
||||
Description
|
||||
===========
|
||||
Traditionally, there is a global switch to enable/disable vIOMMU. All
|
||||
devices in the system can only support go through vIOMMU or not, which
|
||||
is not flexible. We introduce this bypass iommu property to support
|
||||
coexist of devices go through vIOMMU and devices not. This is useful to
|
||||
passthrough devices with no-iommu mode and devices go through vIOMMU in
|
||||
the same virtual machine.
|
||||
|
||||
PCI host bridges have a bypass_iommu property. This property is used to
|
||||
determine whether the devices attached on the PCI host bridge will bypass
|
||||
virtual iommu. The bypass_iommu property is valid only when there is a
|
||||
virtual iommu in the system, it is implemented to allow some devices to
|
||||
bypass vIOMMU. When bypass_iommu property is not set for a host bridge,
|
||||
the attached devices will go through vIOMMU by default.
|
||||
|
||||
Usage
|
||||
=====
|
||||
The bypass iommu feature support PXB host bridge and default main host
|
||||
bridge, we add a bypass_iommu property for PXB and default_bus_bypass_iommu
|
||||
for machine. Note that default_bus_bypass_iommu is available only when
|
||||
the 'q35' machine type on x86 architecture and the 'virt' machine type
|
||||
on AArch64. Other machine types do not support bypass iommu for default
|
||||
root bus.
|
||||
|
||||
1. The following is the bypass iommu options:
|
||||
(1) PCI expander bridge
|
||||
qemu -device pxb-pcie,bus_nr=0x10,addr=0x1,bypass_iommu=true
|
||||
(2) Arm default host bridge
|
||||
qemu -machine virt,iommu=smmuv3,default_bus_bypass_iommu=true
|
||||
(3) X86 default root bus bypass iommu:
|
||||
qemu -machine q35,default_bus_bypass_iommu=true
|
||||
|
||||
2. Here is the detailed qemu command line for 'virt' machine with PXB on
|
||||
AArch64:
|
||||
|
||||
qemu-system-aarch64 \
|
||||
-machine virt,kernel_irqchip=on,iommu=smmuv3,default_bus_bypass_iommu=true \
|
||||
-device pxb-pcie,bus_nr=0x10,id=pci.10,bus=pcie.0,addr=0x3.0x1 \
|
||||
-device pxb-pcie,bus_nr=0x20,id=pci.20,bus=pcie.0,addr=0x3.0x2,bypass_iommu=true \
|
||||
|
||||
And we got:
|
||||
- a default host bridge which bypass SMMUv3
|
||||
- a pxb host bridge which go through SMMUv3
|
||||
- a pxb host bridge which bypass SMMUv3
|
||||
|
||||
3. Here is the detailed qemu command line for 'q35' machine with PXB on
|
||||
x86 architecture:
|
||||
|
||||
qemu-system-x86_64 \
|
||||
-machine q35,accel=kvm,default_bus_bypass_iommu=true \
|
||||
-device pxb-pcie,bus_nr=0x10,id=pci.10,bus=pcie.0,addr=0x3 \
|
||||
-device pxb-pcie,bus_nr=0x20,id=pci.20,bus=pcie.0,addr=0x4,bypass_iommu=true \
|
||||
-device intel-iommu \
|
||||
|
||||
And we got:
|
||||
- a default host bridge which bypass iommu
|
||||
- a pxb host bridge which go through iommu
|
||||
- a pxb host bridge which bypass iommu
|
||||
|
||||
Limitations
|
||||
===========
|
||||
There might be potential security risk when devices bypass iommu, because
|
||||
devices might send malicious dma request to virtual machine if there is no
|
||||
iommu isolation. So it would be necessary to only bypass iommu for trusted
|
||||
device.
|
||||
|
||||
Implementation
|
||||
==============
|
||||
The bypass iommu feature includes:
|
||||
- Address space
|
||||
Add bypass iommu property check of PCI Host and do not get iommu address
|
||||
space for devices bypass iommu.
|
||||
- Arm SMMUv3 support
|
||||
We traverse all PCI root bus and get bus number ranges, then build explicit
|
||||
RID mapping for devices which do not bypass iommu.
|
||||
- X86 IOMMU support
|
||||
To support Intel iommu, we traverse all PCI host bridge and get information
|
||||
of devices which do not bypass iommu, then fill the DMAR drhd struct with
|
||||
explicit device scope info. To support AMD iommu, add check of bypass iommu
|
||||
when traverse the PCI hsot bridge.
|
||||
- Machine and PXB options
|
||||
We add bypass iommu options in machine option for default root bus, and add
|
||||
option for PXB also. Note that the default value of bypass iommu is false,
|
||||
so that the devices will by default go through iommu if there exist one.
|
||||
|
@ -34,15 +34,14 @@ reader and smart card (i.e. not backed by a physical device) using this device.
|
||||
|
||||
2. Building
|
||||
|
||||
The cryptographic functions and access to the physical card is done via NSS.
|
||||
|
||||
Installing NSS:
|
||||
The cryptographic functions and access to the physical card is done via the
|
||||
libcacard library, whose development package must be installed prior to
|
||||
building QEMU:
|
||||
|
||||
In redhat/fedora:
|
||||
yum install nss-devel
|
||||
In ubuntu/debian:
|
||||
apt-get install libnss3-dev
|
||||
(not tested on ubuntu)
|
||||
yum install libcacard-devel
|
||||
In ubuntu:
|
||||
apt-get install libcacard-dev
|
||||
|
||||
Configuring and building:
|
||||
./configure --enable-smartcard && make
|
||||
@ -51,7 +50,7 @@ Configuring and building:
|
||||
3. Using ccid-card-emulated with hardware
|
||||
|
||||
Assuming you have a working smartcard on the host with the current
|
||||
user, using NSS, qemu acts as another NSS client using ccid-card-emulated:
|
||||
user, using libcacard, QEMU acts as another client using ccid-card-emulated:
|
||||
|
||||
qemu -usb -device usb-ccid -device ccid-card-emulated
|
||||
|
||||
|
@ -87,7 +87,7 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'QEMU'
|
||||
copyright = u'2020, The QEMU Project Developers'
|
||||
copyright = u'2021, The QEMU Project Developers'
|
||||
author = u'The QEMU Project Developers'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
|
@ -53,14 +53,14 @@ following tasks:
|
||||
- Add a Meson build option to meson_options.txt.
|
||||
|
||||
- Add support to the command line arg parser to handle any new
|
||||
`--enable-XXX`/`--disable-XXX` flags required by the feature.
|
||||
``--enable-XXX``/``--disable-XXX`` flags required by the feature.
|
||||
|
||||
- Add information to the help output message to report on the new
|
||||
feature flag.
|
||||
|
||||
- Add code to perform the actual feature check.
|
||||
|
||||
- Add code to include the feature status in `config-host.h`
|
||||
- Add code to include the feature status in ``config-host.h``
|
||||
|
||||
- Add code to print out the feature status in the configure summary
|
||||
upon completion.
|
||||
@ -116,51 +116,51 @@ Helper functions
|
||||
The configure script provides a variety of helper functions to assist
|
||||
developers in checking for system features:
|
||||
|
||||
`do_cc $ARGS...`
|
||||
``do_cc $ARGS...``
|
||||
Attempt to run the system C compiler passing it $ARGS...
|
||||
|
||||
`do_cxx $ARGS...`
|
||||
``do_cxx $ARGS...``
|
||||
Attempt to run the system C++ compiler passing it $ARGS...
|
||||
|
||||
`compile_object $CFLAGS`
|
||||
``compile_object $CFLAGS``
|
||||
Attempt to compile a test program with the system C compiler using
|
||||
$CFLAGS. The test program must have been previously written to a file
|
||||
called $TMPC. The replacement in Meson is the compiler object `cc`,
|
||||
which has methods such as `cc.compiles()`,
|
||||
`cc.check_header()`, `cc.has_function()`.
|
||||
called $TMPC. The replacement in Meson is the compiler object ``cc``,
|
||||
which has methods such as ``cc.compiles()``,
|
||||
``cc.check_header()``, ``cc.has_function()``.
|
||||
|
||||
`compile_prog $CFLAGS $LDFLAGS`
|
||||
``compile_prog $CFLAGS $LDFLAGS``
|
||||
Attempt to compile a test program with the system C compiler using
|
||||
$CFLAGS and link it with the system linker using $LDFLAGS. The test
|
||||
program must have been previously written to a file called $TMPC.
|
||||
The replacement in Meson is `cc.find_library()` and `cc.links()`.
|
||||
The replacement in Meson is ``cc.find_library()`` and ``cc.links()``.
|
||||
|
||||
`has $COMMAND`
|
||||
``has $COMMAND``
|
||||
Determine if $COMMAND exists in the current environment, either as a
|
||||
shell builtin, or executable binary, returning 0 on success. The
|
||||
replacement in Meson is `find_program()`.
|
||||
replacement in Meson is ``find_program()``.
|
||||
|
||||
`check_define $NAME`
|
||||
``check_define $NAME``
|
||||
Determine if the macro $NAME is defined by the system C compiler
|
||||
|
||||
`check_include $NAME`
|
||||
``check_include $NAME``
|
||||
Determine if the include $NAME file is available to the system C
|
||||
compiler. The replacement in Meson is `cc.has_header()`.
|
||||
compiler. The replacement in Meson is ``cc.has_header()``.
|
||||
|
||||
`write_c_skeleton`
|
||||
``write_c_skeleton``
|
||||
Write a minimal C program main() function to the temporary file
|
||||
indicated by $TMPC
|
||||
|
||||
`feature_not_found $NAME $REMEDY`
|
||||
``feature_not_found $NAME $REMEDY``
|
||||
Print a message to stderr that the feature $NAME was not available
|
||||
on the system, suggesting the user try $REMEDY to address the
|
||||
problem.
|
||||
|
||||
`error_exit $MESSAGE $MORE...`
|
||||
``error_exit $MESSAGE $MORE...``
|
||||
Print $MESSAGE to stderr, followed by $MORE... and then exit from the
|
||||
configure script with non-zero status
|
||||
|
||||
`query_pkg_config $ARGS...`
|
||||
``query_pkg_config $ARGS...``
|
||||
Run pkg-config passing it $ARGS. If QEMU is doing a static build,
|
||||
then --static will be automatically added to $ARGS
|
||||
|
||||
@ -187,7 +187,7 @@ process for:
|
||||
|
||||
4) other data files, such as icons or desktop files
|
||||
|
||||
All executables are built by default, except for some `contrib/`
|
||||
All executables are built by default, except for some ``contrib/``
|
||||
binaries that are known to fail to build on some platforms (for example
|
||||
32-bit or big-endian platforms). Tests are also built by default,
|
||||
though that might change in the future.
|
||||
@ -195,14 +195,14 @@ though that might change in the future.
|
||||
The source code is highly modularized, split across many files to
|
||||
facilitate building of all of these components with as little duplicated
|
||||
compilation as possible. Using the Meson "sourceset" functionality,
|
||||
`meson.build` files group the source files in rules that are
|
||||
``meson.build`` files group the source files in rules that are
|
||||
enabled according to the available system libraries and to various
|
||||
configuration symbols. Sourcesets belong to one of four groups:
|
||||
|
||||
Subsystem sourcesets:
|
||||
Various subsystems that are common to both tools and emulators have
|
||||
their own sourceset, for example `block_ss` for the block device subsystem,
|
||||
`chardev_ss` for the character device subsystem, etc. These sourcesets
|
||||
their own sourceset, for example ``block_ss`` for the block device subsystem,
|
||||
``chardev_ss`` for the character device subsystem, etc. These sourcesets
|
||||
are then turned into static libraries as follows::
|
||||
|
||||
libchardev = static_library('chardev', chardev_ss.sources(),
|
||||
@ -211,8 +211,8 @@ Subsystem sourcesets:
|
||||
|
||||
chardev = declare_dependency(link_whole: libchardev)
|
||||
|
||||
As of Meson 0.55.1, the special `.fa` suffix should be used for everything
|
||||
that is used with `link_whole`, to ensure that the link flags are placed
|
||||
As of Meson 0.55.1, the special ``.fa`` suffix should be used for everything
|
||||
that is used with ``link_whole``, to ensure that the link flags are placed
|
||||
correctly in the command line.
|
||||
|
||||
Target-independent emulator sourcesets:
|
||||
@ -221,38 +221,38 @@ Target-independent emulator sourcesets:
|
||||
This includes error handling infrastructure, standard data structures,
|
||||
platform portability wrapper functions, etc.
|
||||
|
||||
Target-independent code lives in the `common_ss`, `softmmu_ss` and
|
||||
`user_ss` sourcesets. `common_ss` is linked into all emulators,
|
||||
`softmmu_ss` only in system emulators, `user_ss` only in user-mode
|
||||
Target-independent code lives in the ``common_ss``, ``softmmu_ss`` and
|
||||
``user_ss`` sourcesets. ``common_ss`` is linked into all emulators,
|
||||
``softmmu_ss`` only in system emulators, ``user_ss`` only in user-mode
|
||||
emulators.
|
||||
|
||||
Target-independent sourcesets must exercise particular care when using
|
||||
`if_false` rules. The `if_false` rule will be used correctly when linking
|
||||
``if_false`` rules. The ``if_false`` rule will be used correctly when linking
|
||||
emulator binaries; however, when *compiling* target-independent files
|
||||
into .o files, Meson may need to pick *both* the `if_true` and
|
||||
`if_false` sides to cater for targets that want either side. To
|
||||
into .o files, Meson may need to pick *both* the ``if_true`` and
|
||||
``if_false`` sides to cater for targets that want either side. To
|
||||
achieve that, you can add a special rule using the ``CONFIG_ALL``
|
||||
symbol::
|
||||
|
||||
# Some targets have CONFIG_ACPI, some don't, so this is not enough
|
||||
softmmu_ss.add(when: 'CONFIG_ACPI`, if_true: files('acpi.c'),
|
||||
softmmu_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi.c'),
|
||||
if_false: files('acpi-stub.c'))
|
||||
|
||||
# This is required as well:
|
||||
softmmu_ss.add(when: 'CONFIG_ALL`, if_true: files('acpi-stub.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('acpi-stub.c'))
|
||||
|
||||
Target-dependent emulator sourcesets:
|
||||
In the target-dependent set lives CPU emulation, some device emulation and
|
||||
much glue code. This sometimes also has to be compiled multiple times,
|
||||
once for each target being built. Target-dependent files are included
|
||||
in the `specific_ss` sourceset.
|
||||
in the ``specific_ss`` sourceset.
|
||||
|
||||
Each emulator also includes sources for files in the `hw/` and `target/`
|
||||
Each emulator also includes sources for files in the ``hw/`` and ``target/``
|
||||
subdirectories. The subdirectory used for each emulator comes
|
||||
from the target's definition of ``TARGET_BASE_ARCH`` or (if missing)
|
||||
``TARGET_ARCH``, as found in `default-configs/targets/*.mak`.
|
||||
``TARGET_ARCH``, as found in ``default-configs/targets/*.mak``.
|
||||
|
||||
Each subdirectory in `hw/` adds one sourceset to the `hw_arch` dictionary,
|
||||
Each subdirectory in ``hw/`` adds one sourceset to the ``hw_arch`` dictionary,
|
||||
for example::
|
||||
|
||||
arm_ss = ss.source_set()
|
||||
@ -262,8 +262,8 @@ Target-dependent emulator sourcesets:
|
||||
|
||||
The sourceset is only used for system emulators.
|
||||
|
||||
Each subdirectory in `target/` instead should add one sourceset to each
|
||||
of the `target_arch` and `target_softmmu_arch`, which are used respectively
|
||||
Each subdirectory in ``target/`` instead should add one sourceset to each
|
||||
of the ``target_arch`` and ``target_softmmu_arch``, which are used respectively
|
||||
for all emulators and for system emulators only. For example::
|
||||
|
||||
arm_ss = ss.source_set()
|
||||
@ -273,11 +273,11 @@ Target-dependent emulator sourcesets:
|
||||
target_softmmu_arch += {'arm': arm_softmmu_ss}
|
||||
|
||||
Module sourcesets:
|
||||
There are two dictionaries for modules: `modules` is used for
|
||||
target-independent modules and `target_modules` is used for
|
||||
target-dependent modules. When modules are disabled the `module`
|
||||
source sets are added to `softmmu_ss` and the `target_modules`
|
||||
source sets are added to `specific_ss`.
|
||||
There are two dictionaries for modules: ``modules`` is used for
|
||||
target-independent modules and ``target_modules`` is used for
|
||||
target-dependent modules. When modules are disabled the ``module``
|
||||
source sets are added to ``softmmu_ss`` and the ``target_modules``
|
||||
source sets are added to ``specific_ss``.
|
||||
|
||||
Both dictionaries are nested. One dictionary is created per
|
||||
subdirectory, and these per-subdirectory dictionaries are added to
|
||||
@ -290,15 +290,15 @@ Module sourcesets:
|
||||
modules += { 'hw-display': hw_display_modules }
|
||||
|
||||
Utility sourcesets:
|
||||
All binaries link with a static library `libqemuutil.a`. This library
|
||||
All binaries link with a static library ``libqemuutil.a``. This library
|
||||
is built from several sourcesets; most of them however host generated
|
||||
code, and the only two of general interest are `util_ss` and `stub_ss`.
|
||||
code, and the only two of general interest are ``util_ss`` and ``stub_ss``.
|
||||
|
||||
The separation between these two is purely for documentation purposes.
|
||||
`util_ss` contains generic utility files. Even though this code is only
|
||||
``util_ss`` contains generic utility files. Even though this code is only
|
||||
linked in some binaries, sometimes it requires hooks only in some of
|
||||
these and depend on other functions that are not fully implemented by
|
||||
all QEMU binaries. `stub_ss` links dummy stubs that will only be linked
|
||||
all QEMU binaries. ``stub_ss`` links dummy stubs that will only be linked
|
||||
into the binary if the real implementation is not present. In a way,
|
||||
the stubs can be thought of as a portable implementation of the weak
|
||||
symbols concept.
|
||||
@ -307,8 +307,8 @@ Utility sourcesets:
|
||||
The following files concur in the definition of which files are linked
|
||||
into each emulator:
|
||||
|
||||
`default-configs/devices/*.mak`
|
||||
The files under `default-configs/devices/` control the boards and devices
|
||||
``default-configs/devices/*.mak``
|
||||
The files under ``default-configs/devices/`` control the boards and devices
|
||||
that are built into each QEMU system emulation targets. They merely contain
|
||||
a list of config variable definitions such as::
|
||||
|
||||
@ -316,18 +316,18 @@ into each emulator:
|
||||
CONFIG_XLNX_ZYNQMP_ARM=y
|
||||
CONFIG_XLNX_VERSAL=y
|
||||
|
||||
`*/Kconfig`
|
||||
These files are processed together with `default-configs/devices/*.mak` and
|
||||
``*/Kconfig``
|
||||
These files are processed together with ``default-configs/devices/*.mak`` and
|
||||
describe the dependencies between various features, subsystems and
|
||||
device models. They are described in :ref:`kconfig`
|
||||
|
||||
`default-configs/targets/*.mak`
|
||||
These files mostly define symbols that appear in the `*-config-target.h`
|
||||
``default-configs/targets/*.mak``
|
||||
These files mostly define symbols that appear in the ``*-config-target.h``
|
||||
file for each emulator [#cfgtarget]_. However, the ``TARGET_ARCH``
|
||||
and ``TARGET_BASE_ARCH`` will also be used to select the `hw/` and
|
||||
`target/` subdirectories that are compiled into each target.
|
||||
and ``TARGET_BASE_ARCH`` will also be used to select the ``hw/`` and
|
||||
``target/`` subdirectories that are compiled into each target.
|
||||
|
||||
.. [#cfgtarget] This header is included by `qemu/osdep.h` when
|
||||
.. [#cfgtarget] This header is included by ``qemu/osdep.h`` when
|
||||
compiling files from the target-specific sourcesets.
|
||||
|
||||
These files rarely need changing unless you are adding a completely
|
||||
@ -339,19 +339,19 @@ Support scripts
|
||||
---------------
|
||||
|
||||
Meson has a special convention for invoking Python scripts: if their
|
||||
first line is `#! /usr/bin/env python3` and the file is *not* executable,
|
||||
first line is ``#! /usr/bin/env python3`` and the file is *not* executable,
|
||||
find_program() arranges to invoke the script under the same Python
|
||||
interpreter that was used to invoke Meson. This is the most common
|
||||
and preferred way to invoke support scripts from Meson build files,
|
||||
because it automatically uses the value of configure's --python= option.
|
||||
|
||||
In case the script is not written in Python, use a `#! /usr/bin/env ...`
|
||||
In case the script is not written in Python, use a ``#! /usr/bin/env ...``
|
||||
line and make the script executable.
|
||||
|
||||
Scripts written in Python, where it is desirable to make the script
|
||||
executable (for example for test scripts that developers may want to
|
||||
invoke from the command line, such as tests/qapi-schema/test-qapi.py),
|
||||
should be invoked through the `python` variable in meson.build. For
|
||||
should be invoked through the ``python`` variable in meson.build. For
|
||||
example::
|
||||
|
||||
test('QAPI schema regression tests', python,
|
||||
@ -375,10 +375,10 @@ rules and wraps them so that e.g. submodules are built before QEMU.
|
||||
The resulting build system is largely non-recursive in nature, in
|
||||
contrast to common practices seen with automake.
|
||||
|
||||
Tests are also ran by the Makefile with the traditional `make check`
|
||||
phony target, while benchmarks are run with `make bench`. Meson test
|
||||
suites such as `unit` can be ran with `make check-unit` too. It is also
|
||||
possible to run tests defined in meson.build with `meson test`.
|
||||
Tests are also ran by the Makefile with the traditional ``make check``
|
||||
phony target, while benchmarks are run with ``make bench``. Meson test
|
||||
suites such as ``unit`` can be ran with ``make check-unit`` too. It is also
|
||||
possible to run tests defined in meson.build with ``meson test``.
|
||||
|
||||
Important files for the build system
|
||||
====================================
|
||||
@ -390,28 +390,28 @@ The following key files are statically defined in the source tree, with
|
||||
the rules needed to build QEMU. Their behaviour is influenced by a
|
||||
number of dynamically created files listed later.
|
||||
|
||||
`Makefile`
|
||||
``Makefile``
|
||||
The main entry point used when invoking make to build all the components
|
||||
of QEMU. The default 'all' target will naturally result in the build of
|
||||
every component. Makefile takes care of recursively building submodules
|
||||
directly via a non-recursive set of rules.
|
||||
|
||||
`*/meson.build`
|
||||
``*/meson.build``
|
||||
The meson.build file in the root directory is the main entry point for the
|
||||
Meson build system, and it coordinates the configuration and build of all
|
||||
executables. Build rules for various subdirectories are included in
|
||||
other meson.build files spread throughout the QEMU source tree.
|
||||
|
||||
`tests/Makefile.include`
|
||||
``tests/Makefile.include``
|
||||
Rules for external test harnesses. These include the TCG tests,
|
||||
`qemu-iotests` and the Avocado-based acceptance tests.
|
||||
``qemu-iotests`` and the Avocado-based acceptance tests.
|
||||
|
||||
`tests/docker/Makefile.include`
|
||||
``tests/docker/Makefile.include``
|
||||
Rules for Docker tests. Like tests/Makefile, this file is included
|
||||
directly by the top level Makefile, anything defined in this file will
|
||||
influence the entire build system.
|
||||
|
||||
`tests/vm/Makefile.include`
|
||||
``tests/vm/Makefile.include``
|
||||
Rules for VM-based tests. Like tests/Makefile, this file is included
|
||||
directly by the top level Makefile, anything defined in this file will
|
||||
influence the entire build system.
|
||||
@ -427,11 +427,11 @@ Makefile.
|
||||
|
||||
Built by configure:
|
||||
|
||||
`config-host.mak`
|
||||
``config-host.mak``
|
||||
When configure has determined the characteristics of the build host it
|
||||
will write a long list of variables to config-host.mak file. This
|
||||
provides the various install directories, compiler / linker flags and a
|
||||
variety of `CONFIG_*` variables related to optionally enabled features.
|
||||
variety of ``CONFIG_*`` variables related to optionally enabled features.
|
||||
This is imported by the top level Makefile and meson.build in order to
|
||||
tailor the build output.
|
||||
|
||||
@ -446,29 +446,29 @@ Built by configure:
|
||||
|
||||
Built by Meson:
|
||||
|
||||
`${TARGET-NAME}-config-devices.mak`
|
||||
``${TARGET-NAME}-config-devices.mak``
|
||||
TARGET-NAME is again the name of a system or userspace emulator. The
|
||||
config-devices.mak file is automatically generated by make using the
|
||||
scripts/make_device_config.sh program, feeding it the
|
||||
default-configs/$TARGET-NAME file as input.
|
||||
|
||||
`config-host.h`, `$TARGET-NAME/config-target.h`, `$TARGET-NAME/config-devices.h`
|
||||
``config-host.h``, ``$TARGET-NAME/config-target.h``, ``$TARGET-NAME/config-devices.h``
|
||||
These files are used by source code to determine what features
|
||||
are enabled. They are generated from the contents of the corresponding
|
||||
`*.h` files using the scripts/create_config program. This extracts
|
||||
``*.h`` files using the scripts/create_config program. This extracts
|
||||
relevant variables and formats them as C preprocessor macros.
|
||||
|
||||
`build.ninja`
|
||||
``build.ninja``
|
||||
The build rules.
|
||||
|
||||
|
||||
Built by Makefile:
|
||||
|
||||
`Makefile.ninja`
|
||||
``Makefile.ninja``
|
||||
A Makefile include that bridges to ninja for the actual build. The
|
||||
Makefile is mostly a list of targets that Meson included in build.ninja.
|
||||
|
||||
`Makefile.mtest`
|
||||
``Makefile.mtest``
|
||||
The Makefile definitions that let "make check" run tests defined in
|
||||
meson.build. The rules are produced from Meson's JSON description of
|
||||
tests (obtained with "meson introspect --tests") through the script
|
||||
@ -478,9 +478,9 @@ Built by Makefile:
|
||||
Useful make targets
|
||||
-------------------
|
||||
|
||||
`help`
|
||||
``help``
|
||||
Print a help message for the most common build targets.
|
||||
|
||||
`print-VAR`
|
||||
``print-VAR``
|
||||
Print the value of the variable VAR. Useful for debugging the build
|
||||
system.
|
||||
|
167
docs/devel/ci.rst
Normal file
167
docs/devel/ci.rst
Normal file
@ -0,0 +1,167 @@
|
||||
==
|
||||
CI
|
||||
==
|
||||
|
||||
QEMU has configurations enabled for a number of different CI services.
|
||||
The most up to date information about them and their status can be
|
||||
found at::
|
||||
|
||||
https://wiki.qemu.org/Testing/CI
|
||||
|
||||
Custom CI/CD variables
|
||||
======================
|
||||
|
||||
QEMU CI pipelines can be tuned by setting some CI environment variables.
|
||||
|
||||
Set variable globally in the user's CI namespace
|
||||
------------------------------------------------
|
||||
|
||||
Variables can be set globally in the user's CI namespace setting.
|
||||
|
||||
For further information about how to set these variables, please refer to::
|
||||
|
||||
https://docs.gitlab.com/ee/ci/variables/#add-a-cicd-variable-to-a-project
|
||||
|
||||
Set variable manually when pushing a branch or tag to the user's repository
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
Variables can be set manually when pushing a branch or tag, using
|
||||
git-push command line arguments.
|
||||
|
||||
Example setting the QEMU_CI_EXAMPLE_VAR variable:
|
||||
|
||||
.. code::
|
||||
|
||||
git push -o ci.variable="QEMU_CI_EXAMPLE_VAR=value" myrepo mybranch
|
||||
|
||||
For further information about how to set these variables, please refer to::
|
||||
|
||||
https://docs.gitlab.com/ee/user/project/push_options.html#push-options-for-gitlab-cicd
|
||||
|
||||
Here is a list of the most used variables:
|
||||
|
||||
QEMU_CI_AVOCADO_TESTING
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
By default, tests using the Avocado framework are not run automatically in
|
||||
the pipelines (because multiple artifacts have to be downloaded, and if
|
||||
these artifacts are not already cached, downloading them make the jobs
|
||||
reach the timeout limit). Set this variable to have the tests using the
|
||||
Avocado framework run automatically.
|
||||
|
||||
Jobs on Custom Runners
|
||||
======================
|
||||
|
||||
Besides the jobs run under the various CI systems listed before, there
|
||||
are a number additional jobs that will run before an actual merge.
|
||||
These use the same GitLab CI's service/framework already used for all
|
||||
other GitLab based CI jobs, but rely on additional systems, not the
|
||||
ones provided by GitLab as "shared runners".
|
||||
|
||||
The architecture of GitLab's CI service allows different machines to
|
||||
be set up with GitLab's "agent", called gitlab-runner, which will take
|
||||
care of running jobs created by events such as a push to a branch.
|
||||
Here, the combination of a machine, properly configured with GitLab's
|
||||
gitlab-runner, is called a "custom runner".
|
||||
|
||||
The GitLab CI jobs definition for the custom runners are located under::
|
||||
|
||||
.gitlab-ci.d/custom-runners.yml
|
||||
|
||||
Custom runners entail custom machines. To see a list of the machines
|
||||
currently deployed in the QEMU GitLab CI and their maintainers, please
|
||||
refer to the QEMU `wiki <https://wiki.qemu.org/AdminContacts>`__.
|
||||
|
||||
Machine Setup Howto
|
||||
-------------------
|
||||
|
||||
For all Linux based systems, the setup can be mostly automated by the
|
||||
execution of two Ansible playbooks. Create an ``inventory`` file
|
||||
under ``scripts/ci/setup``, such as this::
|
||||
|
||||
fully.qualified.domain
|
||||
other.machine.hostname
|
||||
|
||||
You may need to set some variables in the inventory file itself. One
|
||||
very common need is to tell Ansible to use a Python 3 interpreter on
|
||||
those hosts. This would look like::
|
||||
|
||||
fully.qualified.domain ansible_python_interpreter=/usr/bin/python3
|
||||
other.machine.hostname ansible_python_interpreter=/usr/bin/python3
|
||||
|
||||
Build environment
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
The ``scripts/ci/setup/build-environment.yml`` Ansible playbook will
|
||||
set up machines with the environment needed to perform builds and run
|
||||
QEMU tests. This playbook consists on the installation of various
|
||||
required packages (and a general package update while at it). It
|
||||
currently covers a number of different Linux distributions, but it can
|
||||
be expanded to cover other systems.
|
||||
|
||||
The minimum required version of Ansible successfully tested in this
|
||||
playbook is 2.8.0 (a version check is embedded within the playbook
|
||||
itself). To run the playbook, execute::
|
||||
|
||||
cd scripts/ci/setup
|
||||
ansible-playbook -i inventory build-environment.yml
|
||||
|
||||
Please note that most of the tasks in the playbook require superuser
|
||||
privileges, such as those from the ``root`` account or those obtained
|
||||
by ``sudo``. If necessary, please refer to ``ansible-playbook``
|
||||
options such as ``--become``, ``--become-method``, ``--become-user``
|
||||
and ``--ask-become-pass``.
|
||||
|
||||
gitlab-runner setup and registration
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The gitlab-runner agent needs to be installed on each machine that
|
||||
will run jobs. The association between a machine and a GitLab project
|
||||
happens with a registration token. To find the registration token for
|
||||
your repository/project, navigate on GitLab's web UI to:
|
||||
|
||||
* Settings (the gears-like icon at the bottom of the left hand side
|
||||
vertical toolbar), then
|
||||
* CI/CD, then
|
||||
* Runners, and click on the "Expand" button, then
|
||||
* Under "Set up a specific Runner manually", look for the value under
|
||||
"And this registration token:"
|
||||
|
||||
Copy the ``scripts/ci/setup/vars.yml.template`` file to
|
||||
``scripts/ci/setup/vars.yml``. Then, set the
|
||||
``gitlab_runner_registration_token`` variable to the value obtained
|
||||
earlier.
|
||||
|
||||
To run the playbook, execute::
|
||||
|
||||
cd scripts/ci/setup
|
||||
ansible-playbook -i inventory gitlab-runner.yml
|
||||
|
||||
Following the registration, it's necessary to configure the runner tags,
|
||||
and optionally other configurations on the GitLab UI. Navigate to:
|
||||
|
||||
* Settings (the gears like icon), then
|
||||
* CI/CD, then
|
||||
* Runners, and click on the "Expand" button, then
|
||||
* "Runners activated for this project", then
|
||||
* Click on the "Edit" icon (next to the "Lock" Icon)
|
||||
|
||||
Tags are very important as they are used to route specific jobs to
|
||||
specific types of runners, so it's a good idea to double check that
|
||||
the automatically created tags are consistent with the OS and
|
||||
architecture. For instance, an Ubuntu 20.04 aarch64 system should
|
||||
have tags set as::
|
||||
|
||||
ubuntu_20.04,aarch64
|
||||
|
||||
Because the job definition at ``.gitlab-ci.d/custom-runners.yml``
|
||||
would contain::
|
||||
|
||||
ubuntu-20.04-aarch64-all:
|
||||
tags:
|
||||
- ubuntu_20.04
|
||||
- aarch64
|
||||
|
||||
It's also recommended to:
|
||||
|
||||
* increase the "Maximum job timeout" to something like ``2h``
|
||||
* give it a better Description
|
@ -72,7 +72,7 @@ eBPF RSS implementation
|
||||
|
||||
eBPF RSS loading functionality located in ebpf/ebpf_rss.c and ebpf/ebpf_rss.h.
|
||||
|
||||
The `struct EBPFRSSContext` structure that holds 4 file descriptors:
|
||||
The ``struct EBPFRSSContext`` structure that holds 4 file descriptors:
|
||||
|
||||
- ctx - pointer of the libbpf context.
|
||||
- program_fd - file descriptor of the eBPF RSS program.
|
||||
@ -80,20 +80,20 @@ The `struct EBPFRSSContext` structure that holds 4 file descriptors:
|
||||
- map_toeplitz_key - file descriptor of the 'Toeplitz key' map. One element of the 40byte key prepared for the hashing algorithm.
|
||||
- map_indirections_table - 128 elements of queue indexes.
|
||||
|
||||
`struct EBPFRSSConfig` fields:
|
||||
``struct EBPFRSSConfig`` fields:
|
||||
|
||||
- redirect - "boolean" value, should the hash be calculated, on false - `default_queue` would be used as the final decision.
|
||||
- redirect - "boolean" value, should the hash be calculated, on false - ``default_queue`` would be used as the final decision.
|
||||
- populate_hash - for now, not used. eBPF RSS doesn't support hash reporting.
|
||||
- hash_types - binary mask of different hash types. See `VIRTIO_NET_RSS_HASH_TYPE_*` defines. If for packet hash should not be calculated - `default_queue` would be used.
|
||||
- hash_types - binary mask of different hash types. See ``VIRTIO_NET_RSS_HASH_TYPE_*`` defines. If for packet hash should not be calculated - ``default_queue`` would be used.
|
||||
- indirections_len - length of the indirections table, maximum 128.
|
||||
- default_queue - the queue index that used for packet that shouldn't be hashed. For some packets, the hash can't be calculated(g.e ARP).
|
||||
|
||||
Functions:
|
||||
|
||||
- `ebpf_rss_init()` - sets ctx to NULL, which indicates that EBPFRSSContext is not loaded.
|
||||
- `ebpf_rss_load()` - creates 3 maps and loads eBPF program from the rss.bpf.skeleton.h. Returns 'true' on success. After that, program_fd can be used to set steering for TAP.
|
||||
- `ebpf_rss_set_all()` - sets values for eBPF maps. `indirections_table` length is in EBPFRSSConfig. `toeplitz_key` is VIRTIO_NET_RSS_MAX_KEY_SIZE aka 40 bytes array.
|
||||
- `ebpf_rss_unload()` - close all file descriptors and set ctx to NULL.
|
||||
- ``ebpf_rss_init()`` - sets ctx to NULL, which indicates that EBPFRSSContext is not loaded.
|
||||
- ``ebpf_rss_load()`` - creates 3 maps and loads eBPF program from the rss.bpf.skeleton.h. Returns 'true' on success. After that, program_fd can be used to set steering for TAP.
|
||||
- ``ebpf_rss_set_all()`` - sets values for eBPF maps. ``indirections_table`` length is in EBPFRSSConfig. ``toeplitz_key`` is VIRTIO_NET_RSS_MAX_KEY_SIZE aka 40 bytes array.
|
||||
- ``ebpf_rss_unload()`` - close all file descriptors and set ctx to NULL.
|
||||
|
||||
Simplified eBPF RSS workflow:
|
||||
|
||||
@ -122,4 +122,4 @@ Simplified eBPF RSS workflow:
|
||||
NetClientState SetSteeringEBPF()
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
For now, `set_steering_ebpf()` method supported by Linux TAP NetClientState. The method requires an eBPF program file descriptor as an argument.
|
||||
For now, ``set_steering_ebpf()`` method supported by Linux TAP NetClientState. The method requires an eBPF program file descriptor as an argument.
|
||||
|
@ -1,15 +1,10 @@
|
||||
.. This is the top level page for the 'devel' manual.
|
||||
|
||||
|
||||
Developer Information
|
||||
=====================
|
||||
|
||||
This manual documents various parts of the internals of QEMU.
|
||||
This section of the manual documents various parts of the internals of QEMU.
|
||||
You only need to read it if you are interested in reading or
|
||||
modifying QEMU's source code.
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:includehidden:
|
||||
@ -27,6 +22,7 @@ Contents:
|
||||
migration
|
||||
atomics
|
||||
stable-process
|
||||
ci
|
||||
qtest
|
||||
decodetree
|
||||
secure-coding-practices
|
||||
@ -46,3 +42,5 @@ Contents:
|
||||
multi-process
|
||||
ebpf_rss
|
||||
vfio-migration
|
||||
qapi-code-gen
|
||||
writing-qmp-commands
|
||||
|
@ -53,7 +53,7 @@ savevm/loadvm functionality.
|
||||
Debugging
|
||||
=========
|
||||
|
||||
The migration stream can be analyzed thanks to `scripts/analyze-migration.py`.
|
||||
The migration stream can be analyzed thanks to ``scripts/analyze-migration.py``.
|
||||
|
||||
Example usage:
|
||||
|
||||
@ -75,8 +75,8 @@ Common infrastructure
|
||||
=====================
|
||||
|
||||
The files, sockets or fd's that carry the migration stream are abstracted by
|
||||
the ``QEMUFile`` type (see `migration/qemu-file.h`). In most cases this
|
||||
is connected to a subtype of ``QIOChannel`` (see `io/`).
|
||||
the ``QEMUFile`` type (see ``migration/qemu-file.h``). In most cases this
|
||||
is connected to a subtype of ``QIOChannel`` (see ``io/``).
|
||||
|
||||
|
||||
Saving the state of one device
|
||||
@ -166,14 +166,14 @@ An example (from hw/input/pckbd.c)
|
||||
};
|
||||
|
||||
We are declaring the state with name "pckbd".
|
||||
The `version_id` is 3, and the fields are 4 uint8_t in a KBDState structure.
|
||||
The ``version_id`` is 3, and the fields are 4 uint8_t in a KBDState structure.
|
||||
We registered this with:
|
||||
|
||||
.. code:: c
|
||||
|
||||
vmstate_register(NULL, 0, &vmstate_kbd, s);
|
||||
|
||||
For devices that are `qdev` based, we can register the device in the class
|
||||
For devices that are ``qdev`` based, we can register the device in the class
|
||||
init function:
|
||||
|
||||
.. code:: c
|
||||
@ -210,9 +210,9 @@ another to load the state back.
|
||||
SaveVMHandlers *ops,
|
||||
void *opaque);
|
||||
|
||||
Two functions in the ``ops`` structure are the `save_state`
|
||||
and `load_state` functions. Notice that `load_state` receives a version_id
|
||||
parameter to know what state format is receiving. `save_state` doesn't
|
||||
Two functions in the ``ops`` structure are the ``save_state``
|
||||
and ``load_state`` functions. Notice that ``load_state`` receives a version_id
|
||||
parameter to know what state format is receiving. ``save_state`` doesn't
|
||||
have a version_id parameter because it always uses the latest version.
|
||||
|
||||
Note that because the VMState macros still save the data in a raw
|
||||
@ -385,18 +385,18 @@ migration of a device, and using them breaks backward-migration
|
||||
compatibility; in general most changes can be made by adding Subsections
|
||||
(see above) or _TEST macros (see above) which won't break compatibility.
|
||||
|
||||
Each version is associated with a series of fields saved. The `save_state` always saves
|
||||
the state as the newer version. But `load_state` sometimes is able to
|
||||
Each version is associated with a series of fields saved. The ``save_state`` always saves
|
||||
the state as the newer version. But ``load_state`` sometimes is able to
|
||||
load state from an older version.
|
||||
|
||||
You can see that there are several version fields:
|
||||
|
||||
- `version_id`: the maximum version_id supported by VMState for that device.
|
||||
- `minimum_version_id`: the minimum version_id that VMState is able to understand
|
||||
- ``version_id``: the maximum version_id supported by VMState for that device.
|
||||
- ``minimum_version_id``: the minimum version_id that VMState is able to understand
|
||||
for that device.
|
||||
- `minimum_version_id_old`: For devices that were not able to port to vmstate, we can
|
||||
- ``minimum_version_id_old``: For devices that were not able to port to vmstate, we can
|
||||
assign a function that knows how to read this old state. This field is
|
||||
ignored if there is no `load_state_old` handler.
|
||||
ignored if there is no ``load_state_old`` handler.
|
||||
|
||||
VMState is able to read versions from minimum_version_id to
|
||||
version_id. And the function ``load_state_old()`` (if present) is able to
|
||||
@ -454,7 +454,7 @@ data and then transferred to the main structure.
|
||||
|
||||
If you use memory API functions that update memory layout outside
|
||||
initialization (i.e., in response to a guest action), this is a strong
|
||||
indication that you need to call these functions in a `post_load` callback.
|
||||
indication that you need to call these functions in a ``post_load`` callback.
|
||||
Examples of such memory API functions are:
|
||||
|
||||
- memory_region_add_subregion()
|
||||
@ -823,12 +823,12 @@ Postcopy migration with shared memory needs explicit support from the other
|
||||
processes that share memory and from QEMU. There are restrictions on the type of
|
||||
memory that userfault can support shared.
|
||||
|
||||
The Linux kernel userfault support works on `/dev/shm` memory and on `hugetlbfs`
|
||||
(although the kernel doesn't provide an equivalent to `madvise(MADV_DONTNEED)`
|
||||
The Linux kernel userfault support works on ``/dev/shm`` memory and on ``hugetlbfs``
|
||||
(although the kernel doesn't provide an equivalent to ``madvise(MADV_DONTNEED)``
|
||||
for hugetlbfs which may be a problem in some configurations).
|
||||
|
||||
The vhost-user code in QEMU supports clients that have Postcopy support,
|
||||
and the `vhost-user-bridge` (in `tests/`) and the DPDK package have changes
|
||||
and the ``vhost-user-bridge`` (in ``tests/``) and the DPDK package have changes
|
||||
to support postcopy.
|
||||
|
||||
The client needs to open a userfaultfd and register the areas
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -66,11 +66,11 @@ Notes for the nodes:
|
||||
Edges
|
||||
^^^^^^
|
||||
|
||||
An edge relation between two nodes (drivers or machines) `X` and `Y` can be:
|
||||
An edge relation between two nodes (drivers or machines) ``X`` and ``Y`` can be:
|
||||
|
||||
- ``X CONSUMES Y``: `Y` can be plugged into `X`
|
||||
- ``X PRODUCES Y``: `X` provides the interface `Y`
|
||||
- ``X CONTAINS Y``: `Y` is part of `X` component
|
||||
- ``X CONSUMES Y``: ``Y`` can be plugged into ``X``
|
||||
- ``X PRODUCES Y``: ``X`` provides the interface ``Y``
|
||||
- ``X CONTAINS Y``: ``Y`` is part of ``X`` component
|
||||
|
||||
Execution steps
|
||||
^^^^^^^^^^^^^^^
|
||||
|
@ -34,11 +34,11 @@ version they were built against. This can be done simply by::
|
||||
QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
|
||||
|
||||
The core code will refuse to load a plugin that doesn't export a
|
||||
`qemu_plugin_version` symbol or if plugin version is outside of QEMU's
|
||||
``qemu_plugin_version`` symbol or if plugin version is outside of QEMU's
|
||||
supported range of API versions.
|
||||
|
||||
Additionally the `qemu_info_t` structure which is passed to the
|
||||
`qemu_plugin_install` method of a plugin will detail the minimum and
|
||||
Additionally the ``qemu_info_t`` structure which is passed to the
|
||||
``qemu_plugin_install`` method of a plugin will detail the minimum and
|
||||
current API versions supported by QEMU. The API version will be
|
||||
incremented if new APIs are added. The minimum API version will be
|
||||
incremented if existing APIs are changed or removed.
|
||||
@ -71,7 +71,8 @@ API
|
||||
Usage
|
||||
=====
|
||||
|
||||
The QEMU binary needs to be compiled for plugin support::
|
||||
Any QEMU binary with TCG support has plugins enabled by default.
|
||||
Earlier releases needed to be explicitly enabled with::
|
||||
|
||||
configure --enable-plugins
|
||||
|
||||
@ -145,12 +146,12 @@ Example Plugins
|
||||
|
||||
There are a number of plugins included with QEMU and you are
|
||||
encouraged to contribute your own plugins plugins upstream. There is a
|
||||
`contrib/plugins` directory where they can go.
|
||||
``contrib/plugins`` directory where they can go.
|
||||
|
||||
- tests/plugins
|
||||
|
||||
These are some basic plugins that are used to test and exercise the
|
||||
API during the `make check-tcg` target.
|
||||
API during the ``make check-tcg`` target.
|
||||
|
||||
- contrib/plugins/hotblocks.c
|
||||
|
||||
@ -162,7 +163,7 @@ with linux-user execution as system emulation tends to generate
|
||||
re-translations as blocks from different programs get swapped in and
|
||||
out of system memory.
|
||||
|
||||
If your program is single-threaded you can use the `inline` option for
|
||||
If your program is single-threaded you can use the ``inline`` option for
|
||||
slightly faster (but not thread safe) counters.
|
||||
|
||||
Example::
|
||||
@ -250,7 +251,7 @@ which will lead to a sorted list after the class breakdown::
|
||||
...
|
||||
|
||||
To find the argument shorthand for the class you need to examine the
|
||||
source code of the plugin at the moment, specifically the `*opt`
|
||||
source code of the plugin at the moment, specifically the ``*opt``
|
||||
argument in the InsnClassExecCount tables.
|
||||
|
||||
- contrib/plugins/lockstep.c
|
||||
@ -319,3 +320,86 @@ the user to see what hardware is accessed how often. It has a number of options:
|
||||
off:0000001c, 1, 2
|
||||
off:00000020, 1, 2
|
||||
...
|
||||
|
||||
- contrib/plugins/execlog.c
|
||||
|
||||
The execlog tool traces executed instructions with memory access. It can be used
|
||||
for debugging and security analysis purposes.
|
||||
Please be aware that this will generate a lot of output.
|
||||
|
||||
The plugin takes no argument::
|
||||
|
||||
qemu-system-arm $(QEMU_ARGS) \
|
||||
-plugin ./contrib/plugins/libexeclog.so -d plugin
|
||||
|
||||
which will output an execution trace following this structure::
|
||||
|
||||
# vCPU, vAddr, opcode, disassembly[, load/store, memory addr, device]...
|
||||
0, 0xa12, 0xf8012400, "movs r4, #0"
|
||||
0, 0xa14, 0xf87f42b4, "cmp r4, r6"
|
||||
0, 0xa16, 0xd206, "bhs #0xa26"
|
||||
0, 0xa18, 0xfff94803, "ldr r0, [pc, #0xc]", load, 0x00010a28, RAM
|
||||
0, 0xa1a, 0xf989f000, "bl #0xd30"
|
||||
0, 0xd30, 0xfff9b510, "push {r4, lr}", store, 0x20003ee0, RAM, store, 0x20003ee4, RAM
|
||||
0, 0xd32, 0xf9893014, "adds r0, #0x14"
|
||||
0, 0xd34, 0xf9c8f000, "bl #0x10c8"
|
||||
0, 0x10c8, 0xfff96c43, "ldr r3, [r0, #0x44]", load, 0x200000e4, RAM
|
||||
|
||||
- contrib/plugins/cache
|
||||
|
||||
Cache modelling plugin that measures the performance of a given cache
|
||||
configuration when a given working set is run::
|
||||
|
||||
qemu-x86_64 -plugin ./contrib/plugins/libcache.so \
|
||||
-d plugin -D cache.log ./tests/tcg/x86_64-linux-user/float_convs
|
||||
|
||||
will report the following::
|
||||
|
||||
Data accesses: 996479, Misses: 507
|
||||
Miss rate: 0.050879%
|
||||
|
||||
Instruction accesses: 2641737, Misses: 18617
|
||||
Miss rate: 0.704726%
|
||||
|
||||
address, data misses, instruction
|
||||
0x424f1e (_int_malloc), 109, movq %rax, 8(%rcx)
|
||||
0x41f395 (_IO_default_xsputn), 49, movb %dl, (%rdi, %rax)
|
||||
0x42584d (ptmalloc_init.part.0), 33, movaps %xmm0, (%rax)
|
||||
0x454d48 (__tunables_init), 20, cmpb $0, (%r8)
|
||||
...
|
||||
|
||||
address, fetch misses, instruction
|
||||
0x4160a0 (__vfprintf_internal), 744, movl $1, %ebx
|
||||
0x41f0a0 (_IO_setb), 744, endbr64
|
||||
0x415882 (__vfprintf_internal), 744, movq %r12, %rdi
|
||||
0x4268a0 (__malloc), 696, andq $0xfffffffffffffff0, %rax
|
||||
...
|
||||
|
||||
The plugin has a number of arguments, all of them are optional:
|
||||
|
||||
* arg="limit=N"
|
||||
|
||||
Print top N icache and dcache thrashing instructions along with their
|
||||
address, number of misses, and its disassembly. (default: 32)
|
||||
|
||||
* arg="icachesize=N"
|
||||
* arg="iblksize=B"
|
||||
* arg="iassoc=A"
|
||||
|
||||
Instruction cache configuration arguments. They specify the cache size, block
|
||||
size, and associativity of the instruction cache, respectively.
|
||||
(default: N = 16384, B = 64, A = 8)
|
||||
|
||||
* arg="dcachesize=N"
|
||||
* arg="dblksize=B"
|
||||
* arg="dassoc=A"
|
||||
|
||||
Data cache configuration arguments. They specify the cache size, block size,
|
||||
and associativity of the data cache, respectively.
|
||||
(default: N = 16384, B = 64, A = 8)
|
||||
|
||||
* arg="evict=POLICY"
|
||||
|
||||
Sets the eviction policy to POLICY. Available policies are: :code:`lru`,
|
||||
:code:`fifo`, and :code:`rand`. The plugin will use the specified policy for
|
||||
both instruction and data caches. (default: POLICY = :code:`lru`)
|
||||
|
@ -775,7 +775,7 @@ The base test class has also support for tests with more than one
|
||||
QEMUMachine. The way to get machines is through the ``self.get_vm()``
|
||||
method which will return a QEMUMachine instance. The ``self.get_vm()``
|
||||
method accepts arguments that will be passed to the QEMUMachine creation
|
||||
and also an optional `name` attribute so you can identify a specific
|
||||
and also an optional ``name`` attribute so you can identify a specific
|
||||
machine and get it more than once through the tests methods. A simple
|
||||
and hypothetical example follows:
|
||||
|
||||
@ -904,6 +904,17 @@ name. If one is not given explicitly, it will either be set to
|
||||
``None``, or, if the test is tagged with one (and only one)
|
||||
``:avocado: tags=arch:VALUE`` tag, it will be set to ``VALUE``.
|
||||
|
||||
cpu
|
||||
~~~
|
||||
|
||||
The cpu model that will be set to all QEMUMachine instances created
|
||||
by the test.
|
||||
|
||||
The ``cpu`` attribute will be set to the test parameter of the same
|
||||
name. If one is not given explicitly, it will either be set to
|
||||
``None ``, or, if the test is tagged with one (and only one)
|
||||
``:avocado: tags=cpu:VALUE`` tag, it will be set to ``VALUE``.
|
||||
|
||||
machine
|
||||
~~~~~~~
|
||||
|
||||
@ -922,6 +933,39 @@ The preserved value of the ``qemu_bin`` parameter or the result of the
|
||||
dynamic probe for a QEMU binary in the current working directory or
|
||||
source tree.
|
||||
|
||||
LinuxTest
|
||||
~~~~~~~~~
|
||||
|
||||
Besides the attributes present on the ``avocado_qemu.Test`` base
|
||||
class, the ``avocado_qemu.LinuxTest`` adds the following attributes:
|
||||
|
||||
distro
|
||||
......
|
||||
|
||||
The name of the Linux distribution used as the guest image for the
|
||||
test. The name should match the **Provider** column on the list
|
||||
of images supported by the avocado.utils.vmimage library:
|
||||
|
||||
https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images
|
||||
|
||||
distro_version
|
||||
..............
|
||||
|
||||
The version of the Linux distribution as the guest image for the
|
||||
test. The name should match the **Version** column on the list
|
||||
of images supported by the avocado.utils.vmimage library:
|
||||
|
||||
https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images
|
||||
|
||||
distro_checksum
|
||||
...............
|
||||
|
||||
The sha256 hash of the guest image file used for the test.
|
||||
|
||||
If this value is not set in the code or by a test parameter (with the
|
||||
same name), no validation on the integrity of the image will be
|
||||
performed.
|
||||
|
||||
Parameter reference
|
||||
-------------------
|
||||
|
||||
@ -950,6 +994,12 @@ architecture of a kernel or disk image to boot a VM with.
|
||||
This parameter has a direct relation with the ``arch`` attribute. If
|
||||
not given, it will default to None.
|
||||
|
||||
cpu
|
||||
~~~
|
||||
|
||||
The cpu model that will be set to all QEMUMachine instances created
|
||||
by the test.
|
||||
|
||||
machine
|
||||
~~~~~~~
|
||||
|
||||
@ -962,6 +1012,38 @@ qemu_bin
|
||||
|
||||
The exact QEMU binary to be used on QEMUMachine.
|
||||
|
||||
LinuxTest
|
||||
~~~~~~~~~
|
||||
|
||||
Besides the parameters present on the ``avocado_qemu.Test`` base
|
||||
class, the ``avocado_qemu.LinuxTest`` adds the following parameters:
|
||||
|
||||
distro
|
||||
......
|
||||
|
||||
The name of the Linux distribution used as the guest image for the
|
||||
test. The name should match the **Provider** column on the list
|
||||
of images supported by the avocado.utils.vmimage library:
|
||||
|
||||
https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images
|
||||
|
||||
distro_version
|
||||
..............
|
||||
|
||||
The version of the Linux distribution as the guest image for the
|
||||
test. The name should match the **Version** column on the list
|
||||
of images supported by the avocado.utils.vmimage library:
|
||||
|
||||
https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images
|
||||
|
||||
distro_checksum
|
||||
...............
|
||||
|
||||
The sha256 hash of the guest image file used for the test.
|
||||
|
||||
If this value is not set in the code or by this parameter no
|
||||
validation on the integrity of the image will be performed.
|
||||
|
||||
Skipping tests
|
||||
--------------
|
||||
The Avocado framework provides Python decorators which allow for easily skip
|
||||
@ -980,7 +1062,7 @@ Here is a list of the most used variables:
|
||||
AVOCADO_ALLOW_LARGE_STORAGE
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Tests which are going to fetch or produce assets considered *large* are not
|
||||
going to run unless that `AVOCADO_ALLOW_LARGE_STORAGE=1` is exported on
|
||||
going to run unless that ``AVOCADO_ALLOW_LARGE_STORAGE=1`` is exported on
|
||||
the environment.
|
||||
|
||||
The definition of *large* is a bit arbitrary here, but it usually means an
|
||||
@ -994,7 +1076,7 @@ skipped by default. The definition of *not safe* is also arbitrary but
|
||||
usually it means a blob which either its source or build process aren't
|
||||
public available.
|
||||
|
||||
You should export `AVOCADO_ALLOW_UNTRUSTED_CODE=1` on the environment in
|
||||
You should export ``AVOCADO_ALLOW_UNTRUSTED_CODE=1`` on the environment in
|
||||
order to allow tests which make use of those kind of assets.
|
||||
|
||||
AVOCADO_TIMEOUT_EXPECTED
|
||||
@ -1008,7 +1090,7 @@ property defined in the test class, for further details::
|
||||
Even though the timeout can be set by the test developer, there are some tests
|
||||
that may not have a well-defined limit of time to finish under certain
|
||||
conditions. For example, tests that take longer to execute when QEMU is
|
||||
compiled with debug flags. Therefore, the `AVOCADO_TIMEOUT_EXPECTED` variable
|
||||
compiled with debug flags. Therefore, the ``AVOCADO_TIMEOUT_EXPECTED`` variable
|
||||
has been used to determine whether those tests should run or not.
|
||||
|
||||
GITLAB_CI
|
||||
|
@ -1,4 +1,5 @@
|
||||
= How to write QMP commands using the QAPI framework =
|
||||
How to write QMP commands using the QAPI framework
|
||||
==================================================
|
||||
|
||||
This document is a step-by-step guide on how to write new QMP commands using
|
||||
the QAPI framework. It also shows how to implement new style HMP commands.
|
||||
@ -10,7 +11,9 @@ For an in-depth introduction to the QAPI framework, please refer to
|
||||
docs/devel/qapi-code-gen.txt. For documentation about the QMP protocol,
|
||||
start with docs/interop/qmp-intro.txt.
|
||||
|
||||
== Overview ==
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
Generally speaking, the following steps should be taken in order to write a
|
||||
new QMP command.
|
||||
@ -31,24 +34,26 @@ new QMP command.
|
||||
The following sections will demonstrate each of the steps above. We will start
|
||||
very simple and get more complex as we progress.
|
||||
|
||||
=== Testing ===
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
For all the examples in the next sections, the test setup is the same and is
|
||||
shown here.
|
||||
|
||||
First, QEMU should be started like this:
|
||||
First, QEMU should be started like this::
|
||||
|
||||
# qemu-system-TARGET [...] \
|
||||
# qemu-system-TARGET [...] \
|
||||
-chardev socket,id=qmp,port=4444,host=localhost,server=on \
|
||||
-mon chardev=qmp,mode=control,pretty=on
|
||||
|
||||
Then, in a different terminal:
|
||||
Then, in a different terminal::
|
||||
|
||||
$ telnet localhost 4444
|
||||
Trying 127.0.0.1...
|
||||
Connected to localhost.
|
||||
Escape character is '^]'.
|
||||
{
|
||||
$ telnet localhost 4444
|
||||
Trying 127.0.0.1...
|
||||
Connected to localhost.
|
||||
Escape character is '^]'.
|
||||
{
|
||||
"QMP": {
|
||||
"version": {
|
||||
"qemu": {
|
||||
@ -61,25 +66,27 @@ Escape character is '^]'.
|
||||
"capabilities": [
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
The above output is the QMP server saying you're connected. The server is
|
||||
actually in capabilities negotiation mode. To enter in command mode type:
|
||||
actually in capabilities negotiation mode. To enter in command mode type::
|
||||
|
||||
{ "execute": "qmp_capabilities" }
|
||||
{ "execute": "qmp_capabilities" }
|
||||
|
||||
Then the server should respond:
|
||||
Then the server should respond::
|
||||
|
||||
{
|
||||
{
|
||||
"return": {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Which is QMP's way of saying "the latest command executed OK and didn't return
|
||||
any data". Now you're ready to enter the QMP example commands as explained in
|
||||
the following sections.
|
||||
|
||||
== Writing a command that doesn't return data ==
|
||||
|
||||
Writing a command that doesn't return data
|
||||
------------------------------------------
|
||||
|
||||
That's the most simple QMP command that can be written. Usually, this kind of
|
||||
command carries some meaningful action in QEMU but here it will just print
|
||||
@ -90,9 +97,9 @@ return any data.
|
||||
|
||||
The first step is defining the command in the appropriate QAPI schema
|
||||
module. We pick module qapi/misc.json, and add the following line at
|
||||
the bottom:
|
||||
the bottom::
|
||||
|
||||
{ 'command': 'hello-world' }
|
||||
{ 'command': 'hello-world' }
|
||||
|
||||
The "command" keyword defines a new QMP command. It's an JSON object. All
|
||||
schema entries are JSON objects. The line above will instruct the QAPI to
|
||||
@ -102,19 +109,19 @@ protocol data.
|
||||
The next step is to write the "hello-world" implementation. As explained
|
||||
earlier, it's preferable for commands to live in QEMU subsystems. But
|
||||
"hello-world" doesn't pertain to any, so we put its implementation in
|
||||
monitor/qmp-cmds.c:
|
||||
monitor/qmp-cmds.c::
|
||||
|
||||
void qmp_hello_world(Error **errp)
|
||||
{
|
||||
void qmp_hello_world(Error **errp)
|
||||
{
|
||||
printf("Hello, world!\n");
|
||||
}
|
||||
}
|
||||
|
||||
There are a few things to be noticed:
|
||||
|
||||
1. QMP command implementation functions must be prefixed with "qmp_"
|
||||
1. QMP command implementation functions must be prefixed with "qmp\_"
|
||||
2. qmp_hello_world() returns void, this is in accordance with the fact that the
|
||||
command doesn't return any data
|
||||
3. It takes an "Error **" argument. This is required. Later we will see how to
|
||||
3. It takes an "Error \*\*" argument. This is required. Later we will see how to
|
||||
return errors and take additional arguments. The Error argument should not
|
||||
be touched if the command doesn't return errors
|
||||
4. We won't add the function's prototype. That's automatically done by the QAPI
|
||||
@ -122,23 +129,25 @@ There are a few things to be noticed:
|
||||
because it's the easiest way to demonstrate a QMP command
|
||||
|
||||
You're done. Now build qemu, run it as suggested in the "Testing" section,
|
||||
and then type the following QMP command:
|
||||
and then type the following QMP command::
|
||||
|
||||
{ "execute": "hello-world" }
|
||||
{ "execute": "hello-world" }
|
||||
|
||||
Then check the terminal running qemu and look for the "Hello, world" string. If
|
||||
you don't see it then something went wrong.
|
||||
|
||||
=== Arguments ===
|
||||
|
||||
Arguments
|
||||
~~~~~~~~~
|
||||
|
||||
Let's add an argument called "message" to our "hello-world" command. The new
|
||||
argument will contain the string to be printed to stdout. It's an optional
|
||||
argument, if it's not present we print our default "Hello, World" string.
|
||||
|
||||
The first change we have to do is to modify the command specification in the
|
||||
schema file to the following:
|
||||
schema file to the following::
|
||||
|
||||
{ 'command': 'hello-world', 'data': { '*message': 'str' } }
|
||||
{ 'command': 'hello-world', 'data': { '*message': 'str' } }
|
||||
|
||||
Notice the new 'data' member in the schema. It's an JSON object whose each
|
||||
element is an argument to the command in question. Also notice the asterisk,
|
||||
@ -147,52 +156,54 @@ for mandatory arguments). Finally, 'str' is the argument's type, which
|
||||
stands for "string". The QAPI also supports integers, booleans, enumerations
|
||||
and user defined types.
|
||||
|
||||
Now, let's update our C implementation in monitor/qmp-cmds.c:
|
||||
Now, let's update our C implementation in monitor/qmp-cmds.c::
|
||||
|
||||
void qmp_hello_world(bool has_message, const char *message, Error **errp)
|
||||
{
|
||||
void qmp_hello_world(bool has_message, const char *message, Error **errp)
|
||||
{
|
||||
if (has_message) {
|
||||
printf("%s\n", message);
|
||||
} else {
|
||||
printf("Hello, world\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
There are two important details to be noticed:
|
||||
|
||||
1. All optional arguments are accompanied by a 'has_' boolean, which is set
|
||||
1. All optional arguments are accompanied by a 'has\_' boolean, which is set
|
||||
if the optional argument is present or false otherwise
|
||||
2. The C implementation signature must follow the schema's argument ordering,
|
||||
which is defined by the "data" member
|
||||
|
||||
Time to test our new version of the "hello-world" command. Build qemu, run it as
|
||||
described in the "Testing" section and then send two commands:
|
||||
described in the "Testing" section and then send two commands::
|
||||
|
||||
{ "execute": "hello-world" }
|
||||
{
|
||||
{ "execute": "hello-world" }
|
||||
{
|
||||
"return": {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{ "execute": "hello-world", "arguments": { "message": "We love qemu" } }
|
||||
{
|
||||
{ "execute": "hello-world", "arguments": { "message": "We love qemu" } }
|
||||
{
|
||||
"return": {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
You should see "Hello, world" and "We love qemu" in the terminal running qemu,
|
||||
if you don't see these strings, then something went wrong.
|
||||
|
||||
=== Errors ===
|
||||
|
||||
Errors
|
||||
~~~~~~
|
||||
|
||||
QMP commands should use the error interface exported by the error.h header
|
||||
file. Basically, most errors are set by calling the error_setg() function.
|
||||
|
||||
Let's say we don't accept the string "message" to contain the word "love". If
|
||||
it does contain it, we want the "hello-world" command to return an error:
|
||||
it does contain it, we want the "hello-world" command to return an error::
|
||||
|
||||
void qmp_hello_world(bool has_message, const char *message, Error **errp)
|
||||
{
|
||||
void qmp_hello_world(bool has_message, const char *message, Error **errp)
|
||||
{
|
||||
if (has_message) {
|
||||
if (strstr(message, "love")) {
|
||||
error_setg(errp, "the word 'love' is not allowed");
|
||||
@ -202,25 +213,25 @@ void qmp_hello_world(bool has_message, const char *message, Error **errp)
|
||||
} else {
|
||||
printf("Hello, world\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
The first argument to the error_setg() function is the Error pointer
|
||||
to pointer, which is passed to all QMP functions. The next argument is a human
|
||||
description of the error, this is a free-form printf-like string.
|
||||
|
||||
Let's test the example above. Build qemu, run it as defined in the "Testing"
|
||||
section, and then issue the following command:
|
||||
section, and then issue the following command::
|
||||
|
||||
{ "execute": "hello-world", "arguments": { "message": "all you need is love" } }
|
||||
{ "execute": "hello-world", "arguments": { "message": "all you need is love" } }
|
||||
|
||||
The QMP server's response should be:
|
||||
The QMP server's response should be::
|
||||
|
||||
{
|
||||
{
|
||||
"error": {
|
||||
"class": "GenericError",
|
||||
"desc": "the word 'love' is not allowed"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Note that error_setg() produces a "GenericError" class. In general,
|
||||
all QMP errors should have that error class. There are two exceptions
|
||||
@ -234,34 +245,38 @@ to this rule:
|
||||
If the failure you want to report falls into one of the two cases above,
|
||||
use error_set() with a second argument of an ErrorClass value.
|
||||
|
||||
=== Command Documentation ===
|
||||
|
||||
Command Documentation
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
There's only one step missing to make "hello-world"'s implementation complete,
|
||||
and that's its documentation in the schema file.
|
||||
|
||||
There are many examples of such documentation in the schema file already, but
|
||||
here goes "hello-world"'s new entry for qapi/misc.json:
|
||||
here goes "hello-world"'s new entry for qapi/misc.json::
|
||||
|
||||
##
|
||||
# @hello-world:
|
||||
#
|
||||
# Print a client provided string to the standard output stream.
|
||||
#
|
||||
# @message: string to be printed
|
||||
#
|
||||
# Returns: Nothing on success.
|
||||
#
|
||||
# Notes: if @message is not provided, the "Hello, world" string will
|
||||
# be printed instead
|
||||
#
|
||||
# Since: <next qemu stable release, eg. 1.0>
|
||||
##
|
||||
{ 'command': 'hello-world', 'data': { '*message': 'str' } }
|
||||
##
|
||||
# @hello-world:
|
||||
#
|
||||
# Print a client provided string to the standard output stream.
|
||||
#
|
||||
# @message: string to be printed
|
||||
#
|
||||
# Returns: Nothing on success.
|
||||
#
|
||||
# Notes: if @message is not provided, the "Hello, world" string will
|
||||
# be printed instead
|
||||
#
|
||||
# Since: <next qemu stable release, eg. 1.0>
|
||||
##
|
||||
{ 'command': 'hello-world', 'data': { '*message': 'str' } }
|
||||
|
||||
Please, note that the "Returns" clause is optional if a command doesn't return
|
||||
any data nor any errors.
|
||||
|
||||
=== Implementing the HMP command ===
|
||||
|
||||
Implementing the HMP command
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Now that the QMP command is in place, we can also make it available in the human
|
||||
monitor (HMP).
|
||||
@ -270,10 +285,10 @@ With the introduction of the QAPI, HMP commands make QMP calls. Most of the
|
||||
time HMP commands are simple wrappers. All HMP commands implementation exist in
|
||||
the monitor/hmp-cmds.c file.
|
||||
|
||||
Here's the implementation of the "hello-world" HMP command:
|
||||
Here's the implementation of the "hello-world" HMP command::
|
||||
|
||||
void hmp_hello_world(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
void hmp_hello_world(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
const char *message = qdict_get_try_str(qdict, "message");
|
||||
Error *err = NULL;
|
||||
|
||||
@ -283,7 +298,7 @@ void hmp_hello_world(Monitor *mon, const QDict *qdict)
|
||||
error_free(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Also, you have to add the function's prototype to the hmp.h file.
|
||||
|
||||
@ -299,7 +314,7 @@ There are three important points to be noticed:
|
||||
QMP call
|
||||
|
||||
There's one last step to actually make the command available to monitor users,
|
||||
we should add it to the hmp-commands.hx file:
|
||||
we should add it to the hmp-commands.hx file::
|
||||
|
||||
{
|
||||
.name = "hello-world",
|
||||
@ -309,11 +324,13 @@ we should add it to the hmp-commands.hx file:
|
||||
.cmd = hmp_hello_world,
|
||||
},
|
||||
|
||||
STEXI
|
||||
@item hello_world @var{message}
|
||||
@findex hello_world
|
||||
Print message to the standard output
|
||||
ETEXI
|
||||
::
|
||||
|
||||
STEXI
|
||||
@item hello_world @var{message}
|
||||
@findex hello_world
|
||||
Print message to the standard output
|
||||
ETEXI
|
||||
|
||||
To test this you have to open a user monitor and issue the "hello-world"
|
||||
command. It might be instructive to check the command's documentation with
|
||||
@ -322,7 +339,9 @@ HMP's "help" command.
|
||||
Please, check the "-monitor" command-line option to know how to open a user
|
||||
monitor.
|
||||
|
||||
== Writing a command that returns data ==
|
||||
|
||||
Writing a command that returns data
|
||||
-----------------------------------
|
||||
|
||||
A QMP command is capable of returning any data the QAPI supports like integers,
|
||||
strings, booleans, enumerations and user defined types.
|
||||
@ -330,7 +349,9 @@ strings, booleans, enumerations and user defined types.
|
||||
In this section we will focus on user defined types. Please, check the QAPI
|
||||
documentation for information about the other types.
|
||||
|
||||
=== User Defined Types ===
|
||||
|
||||
User Defined Types
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
FIXME This example needs to be redone after commit 6d32717
|
||||
|
||||
@ -344,49 +365,49 @@ returned as a string, the latter is an integer in nanoseconds (which is not
|
||||
very useful in practice, as the timer has probably already fired when the
|
||||
information reaches the client).
|
||||
|
||||
The best way to return that data is to create a new QAPI type, as shown below:
|
||||
The best way to return that data is to create a new QAPI type, as shown below::
|
||||
|
||||
##
|
||||
# @QemuAlarmClock
|
||||
#
|
||||
# QEMU alarm clock information.
|
||||
#
|
||||
# @clock-name: The alarm clock method's name.
|
||||
#
|
||||
# @next-deadline: The time (in nanoseconds) the next alarm will fire.
|
||||
#
|
||||
# Since: 1.0
|
||||
##
|
||||
{ 'type': 'QemuAlarmClock',
|
||||
##
|
||||
# @QemuAlarmClock
|
||||
#
|
||||
# QEMU alarm clock information.
|
||||
#
|
||||
# @clock-name: The alarm clock method's name.
|
||||
#
|
||||
# @next-deadline: The time (in nanoseconds) the next alarm will fire.
|
||||
#
|
||||
# Since: 1.0
|
||||
##
|
||||
{ 'type': 'QemuAlarmClock',
|
||||
'data': { 'clock-name': 'str', '*next-deadline': 'int' } }
|
||||
|
||||
The "type" keyword defines a new QAPI type. Its "data" member contains the
|
||||
type's members. In this example our members are the "clock-name" and the
|
||||
"next-deadline" one, which is optional.
|
||||
|
||||
Now let's define the query-alarm-clock command:
|
||||
Now let's define the query-alarm-clock command::
|
||||
|
||||
##
|
||||
# @query-alarm-clock
|
||||
#
|
||||
# Return information about QEMU's alarm clock.
|
||||
#
|
||||
# Returns a @QemuAlarmClock instance describing the alarm clock method
|
||||
# being currently used by QEMU (this is usually set by the '-clock'
|
||||
# command-line option).
|
||||
#
|
||||
# Since: 1.0
|
||||
##
|
||||
{ 'command': 'query-alarm-clock', 'returns': 'QemuAlarmClock' }
|
||||
##
|
||||
# @query-alarm-clock
|
||||
#
|
||||
# Return information about QEMU's alarm clock.
|
||||
#
|
||||
# Returns a @QemuAlarmClock instance describing the alarm clock method
|
||||
# being currently used by QEMU (this is usually set by the '-clock'
|
||||
# command-line option).
|
||||
#
|
||||
# Since: 1.0
|
||||
##
|
||||
{ 'command': 'query-alarm-clock', 'returns': 'QemuAlarmClock' }
|
||||
|
||||
Notice the "returns" keyword. As its name suggests, it's used to define the
|
||||
data returned by a command.
|
||||
|
||||
It's time to implement the qmp_query_alarm_clock() function, you can put it
|
||||
in the qemu-timer.c file:
|
||||
in the qemu-timer.c file::
|
||||
|
||||
QemuAlarmClock *qmp_query_alarm_clock(Error **errp)
|
||||
{
|
||||
QemuAlarmClock *qmp_query_alarm_clock(Error **errp)
|
||||
{
|
||||
QemuAlarmClock *clock;
|
||||
int64_t deadline;
|
||||
|
||||
@ -400,7 +421,7 @@ QemuAlarmClock *qmp_query_alarm_clock(Error **errp)
|
||||
clock->clock_name = g_strdup(alarm_timer->name);
|
||||
|
||||
return clock;
|
||||
}
|
||||
}
|
||||
|
||||
There are a number of things to be noticed:
|
||||
|
||||
@ -423,22 +444,24 @@ There are a number of things to be noticed:
|
||||
6. You have to include "qapi/qapi-commands-misc.h" in qemu-timer.c
|
||||
|
||||
Time to test the new command. Build qemu, run it as described in the "Testing"
|
||||
section and try this:
|
||||
section and try this::
|
||||
|
||||
{ "execute": "query-alarm-clock" }
|
||||
{
|
||||
{ "execute": "query-alarm-clock" }
|
||||
{
|
||||
"return": {
|
||||
"next-deadline": 2368219,
|
||||
"clock-name": "dynticks"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
==== The HMP command ====
|
||||
|
||||
Here's the HMP counterpart of the query-alarm-clock command:
|
||||
The HMP command
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
void hmp_info_alarm_clock(Monitor *mon)
|
||||
{
|
||||
Here's the HMP counterpart of the query-alarm-clock command::
|
||||
|
||||
void hmp_info_alarm_clock(Monitor *mon)
|
||||
{
|
||||
QemuAlarmClock *clock;
|
||||
Error *err = NULL;
|
||||
|
||||
@ -456,7 +479,7 @@ void hmp_info_alarm_clock(Monitor *mon)
|
||||
}
|
||||
|
||||
qapi_free_QemuAlarmClock(clock);
|
||||
}
|
||||
}
|
||||
|
||||
It's important to notice that hmp_info_alarm_clock() calls
|
||||
qapi_free_QemuAlarmClock() to free the data returned by qmp_query_alarm_clock().
|
||||
@ -471,7 +494,7 @@ it's good practice to always check for errors.
|
||||
|
||||
Another important detail is that HMP's "info" commands don't go into the
|
||||
hmp-commands.hx. Instead, they go into the info_cmds[] table, which is defined
|
||||
in the monitor/misc.c file. The entry for the "info alarmclock" follows:
|
||||
in the monitor/misc.c file. The entry for the "info alarmclock" follows::
|
||||
|
||||
{
|
||||
.name = "alarmclock",
|
||||
@ -483,49 +506,51 @@ in the monitor/misc.c file. The entry for the "info alarmclock" follows:
|
||||
|
||||
To test this, run qemu and type "info alarmclock" in the user monitor.
|
||||
|
||||
=== Returning Lists ===
|
||||
|
||||
Returning Lists
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
For this example, we're going to return all available methods for the timer
|
||||
alarm, which is pretty much what the command-line option "-clock ?" does,
|
||||
except that we're also going to inform which method is in use.
|
||||
|
||||
This first step is to define a new type:
|
||||
This first step is to define a new type::
|
||||
|
||||
##
|
||||
# @TimerAlarmMethod
|
||||
#
|
||||
# Timer alarm method information.
|
||||
#
|
||||
# @method-name: The method's name.
|
||||
#
|
||||
# @current: true if this alarm method is currently in use, false otherwise
|
||||
#
|
||||
# Since: 1.0
|
||||
##
|
||||
{ 'type': 'TimerAlarmMethod',
|
||||
##
|
||||
# @TimerAlarmMethod
|
||||
#
|
||||
# Timer alarm method information.
|
||||
#
|
||||
# @method-name: The method's name.
|
||||
#
|
||||
# @current: true if this alarm method is currently in use, false otherwise
|
||||
#
|
||||
# Since: 1.0
|
||||
##
|
||||
{ 'type': 'TimerAlarmMethod',
|
||||
'data': { 'method-name': 'str', 'current': 'bool' } }
|
||||
|
||||
The command will be called "query-alarm-methods", here is its schema
|
||||
specification:
|
||||
specification::
|
||||
|
||||
##
|
||||
# @query-alarm-methods
|
||||
#
|
||||
# Returns information about available alarm methods.
|
||||
#
|
||||
# Returns: a list of @TimerAlarmMethod for each method
|
||||
#
|
||||
# Since: 1.0
|
||||
##
|
||||
{ 'command': 'query-alarm-methods', 'returns': ['TimerAlarmMethod'] }
|
||||
##
|
||||
# @query-alarm-methods
|
||||
#
|
||||
# Returns information about available alarm methods.
|
||||
#
|
||||
# Returns: a list of @TimerAlarmMethod for each method
|
||||
#
|
||||
# Since: 1.0
|
||||
##
|
||||
{ 'command': 'query-alarm-methods', 'returns': ['TimerAlarmMethod'] }
|
||||
|
||||
Notice the syntax for returning lists "'returns': ['TimerAlarmMethod']", this
|
||||
should be read as "returns a list of TimerAlarmMethod instances".
|
||||
|
||||
The C implementation follows:
|
||||
The C implementation follows::
|
||||
|
||||
TimerAlarmMethodList *qmp_query_alarm_methods(Error **errp)
|
||||
{
|
||||
TimerAlarmMethodList *qmp_query_alarm_methods(Error **errp)
|
||||
{
|
||||
TimerAlarmMethodList *method_list = NULL;
|
||||
const struct qemu_alarm_timer *p;
|
||||
bool current = true;
|
||||
@ -539,7 +564,7 @@ TimerAlarmMethodList *qmp_query_alarm_methods(Error **errp)
|
||||
}
|
||||
|
||||
return method_list;
|
||||
}
|
||||
}
|
||||
|
||||
The most important difference from the previous examples is the
|
||||
TimerAlarmMethodList type, which is automatically generated by the QAPI from
|
||||
@ -557,10 +582,10 @@ first element of the alarm_timers array. Also notice that QAPI lists are handled
|
||||
by hand and we return the head of the list.
|
||||
|
||||
Now Build qemu, run it as explained in the "Testing" section and try our new
|
||||
command:
|
||||
command::
|
||||
|
||||
{ "execute": "query-alarm-methods" }
|
||||
{
|
||||
{ "execute": "query-alarm-methods" }
|
||||
{
|
||||
"return": [
|
||||
{
|
||||
"current": false,
|
||||
@ -571,13 +596,13 @@ command:
|
||||
"method-name": "dynticks"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
The HMP counterpart is a bit more complex than previous examples because it
|
||||
has to traverse the list, it's shown below for reference:
|
||||
has to traverse the list, it's shown below for reference::
|
||||
|
||||
void hmp_info_alarm_methods(Monitor *mon)
|
||||
{
|
||||
void hmp_info_alarm_methods(Monitor *mon)
|
||||
{
|
||||
TimerAlarmMethodList *method_list, *method;
|
||||
Error *err = NULL;
|
||||
|
||||
@ -594,4 +619,4 @@ void hmp_info_alarm_methods(Monitor *mon)
|
||||
}
|
||||
|
||||
qapi_free_TimerAlarmMethodList(method_list);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user