Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9173d5d084 | ||
|
|
d8f4a7a80e | ||
|
|
c80a048f38 | ||
|
|
af87044127 | ||
|
|
395d5d09c7 | ||
|
|
ac056cbb54 | ||
|
|
f0060d4691 | ||
|
|
0170f3ea3d | ||
|
|
9ae56d2e46 | ||
|
|
6ed0b8431c | ||
|
|
50a715c77c | ||
|
|
efb1c1d294 |
@@ -24,10 +24,6 @@ variables:
|
||||
# Each script line from will be in a collapsible section in the job output
|
||||
# and show the duration of each line.
|
||||
FF_SCRIPT_SECTIONS: 1
|
||||
# The project has a fairly fat GIT repo so we try and avoid bringing in things
|
||||
# we don't need. The --filter options avoid blobs and tree references we aren't going to use
|
||||
# and we also avoid fetching tags.
|
||||
GIT_FETCH_EXTRA_FLAGS: --filter=blob:none --filter=tree:0 --no-tags --prune --quiet
|
||||
|
||||
interruptible: true
|
||||
|
||||
@@ -128,7 +124,7 @@ variables:
|
||||
when: manual
|
||||
|
||||
# Jobs can run if any jobs they depend on were successful
|
||||
- if: '$CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH =~ /staging-[[:digit:]]+\.[[:digit:]]/'
|
||||
- if: '$QEMU_JOB_SKIPPED && $CI_PROJECT_NAMESPACE == $QEMU_CI_UPSTREAM && $CI_COMMIT_BRANCH =~ /staging-[[:digit:]]+\.[[:digit:]]/'
|
||||
when: on_success
|
||||
variables:
|
||||
QEMU_CI_CONTAINER_TAG: $CI_COMMIT_REF_SLUG
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
- export CCACHE_DIR="$CCACHE_BASEDIR/ccache"
|
||||
- export CCACHE_MAXSIZE="500M"
|
||||
- export PATH="$CCACHE_WRAPPERSDIR:$PATH"
|
||||
- du -sh .git
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ccache --zero-stats
|
||||
@@ -26,10 +25,10 @@
|
||||
then
|
||||
pyvenv/bin/meson configure . -Dbackend_max_links="$LD_JOBS" ;
|
||||
fi || exit 1;
|
||||
- $MAKE -j"$JOBS"
|
||||
- make -j"$JOBS"
|
||||
- if test -n "$MAKE_CHECK_ARGS";
|
||||
then
|
||||
$MAKE -j"$JOBS" $MAKE_CHECK_ARGS ;
|
||||
make -j"$JOBS" $MAKE_CHECK_ARGS ;
|
||||
fi
|
||||
- ccache --show-stats
|
||||
|
||||
@@ -60,7 +59,7 @@
|
||||
- cd build
|
||||
- find . -type f -exec touch {} +
|
||||
# Avoid recompiling by hiding ninja with NINJA=":"
|
||||
- $MAKE NINJA=":" $MAKE_CHECK_ARGS
|
||||
- make NINJA=":" $MAKE_CHECK_ARGS
|
||||
|
||||
.native_test_job_template:
|
||||
extends: .common_test_job_template
|
||||
|
||||
@@ -158,9 +158,9 @@ build-system-centos:
|
||||
- .native_build_job_template
|
||||
- .native_build_artifact_template
|
||||
needs:
|
||||
job: amd64-centos9-container
|
||||
job: amd64-centos8-container
|
||||
variables:
|
||||
IMAGE: centos9
|
||||
IMAGE: centos8
|
||||
CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-vfio-user-server
|
||||
--enable-modules --enable-trace-backends=dtrace --enable-docs
|
||||
TARGETS: ppc64-softmmu or1k-softmmu s390x-softmmu
|
||||
@@ -187,8 +187,6 @@ build-previous-qemu:
|
||||
variables:
|
||||
IMAGE: opensuse-leap
|
||||
TARGETS: x86_64-softmmu aarch64-softmmu
|
||||
# Override the default flags as we need more to grab the old version
|
||||
GIT_FETCH_EXTRA_FLAGS: --prune --quiet
|
||||
before_script:
|
||||
- export QEMU_PREV_VERSION="$(sed 's/\([0-9.]*\)\.[0-9]*/v\1.0/' VERSION)"
|
||||
- git remote add upstream https://gitlab.com/qemu-project/qemu
|
||||
@@ -242,7 +240,7 @@ check-system-centos:
|
||||
- job: build-system-centos
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: centos9
|
||||
IMAGE: centos8
|
||||
MAKE_CHECK_ARGS: check
|
||||
|
||||
avocado-system-centos:
|
||||
@@ -251,7 +249,7 @@ avocado-system-centos:
|
||||
- job: build-system-centos
|
||||
artifacts: true
|
||||
variables:
|
||||
IMAGE: centos9
|
||||
IMAGE: centos8
|
||||
MAKE_CHECK_ARGS: check-avocado
|
||||
AVOCADO_TAGS: arch:ppc64 arch:or1k arch:s390x arch:x86_64 arch:rx
|
||||
arch:sh4 arch:nios2
|
||||
@@ -327,9 +325,9 @@ avocado-system-flaky:
|
||||
build-tcg-disabled:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-centos9-container
|
||||
job: amd64-centos8-container
|
||||
variables:
|
||||
IMAGE: centos9
|
||||
IMAGE: centos8
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
@@ -575,9 +573,6 @@ tsan-build:
|
||||
CONFIGURE_ARGS: --enable-tsan --cc=clang --cxx=clang++
|
||||
--enable-trace-backends=ust --disable-slirp
|
||||
TARGETS: x86_64-softmmu ppc64-softmmu riscv64-softmmu x86_64-linux-user
|
||||
# Remove when we switch to a distro with clang >= 18
|
||||
# https://github.com/google/sanitizers/issues/1716
|
||||
MAKE: setarch -R make
|
||||
|
||||
# gcov is a GCC features
|
||||
gcov:
|
||||
@@ -654,9 +649,9 @@ build-tci:
|
||||
build-without-defaults:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-centos9-container
|
||||
job: amd64-centos8-container
|
||||
variables:
|
||||
IMAGE: centos9
|
||||
IMAGE: centos8
|
||||
CONFIGURE_ARGS:
|
||||
--without-default-devices
|
||||
--without-default-features
|
||||
|
||||
@@ -19,9 +19,10 @@ cwd = os.getcwd()
|
||||
reponame = os.path.basename(cwd)
|
||||
repourl = "https://gitlab.com/%s/%s.git" % (namespace, reponame)
|
||||
|
||||
print(f"adding upstream git repo @ {repourl}")
|
||||
subprocess.check_call(["git", "remote", "add", "check-dco", repourl])
|
||||
subprocess.check_call(["git", "fetch", "check-dco", "master"])
|
||||
subprocess.check_call(["git", "fetch", "check-dco", "master"],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL)
|
||||
|
||||
ancestor = subprocess.check_output(["git", "merge-base",
|
||||
"check-dco/master", "HEAD"],
|
||||
|
||||
@@ -19,12 +19,13 @@ cwd = os.getcwd()
|
||||
reponame = os.path.basename(cwd)
|
||||
repourl = "https://gitlab.com/%s/%s.git" % (namespace, reponame)
|
||||
|
||||
print(f"adding upstream git repo @ {repourl}")
|
||||
# GitLab CI environment does not give us any direct info about the
|
||||
# base for the user's branch. We thus need to figure out a common
|
||||
# ancestor between the user's branch and current git master.
|
||||
subprocess.check_call(["git", "remote", "add", "check-patch", repourl])
|
||||
subprocess.check_call(["git", "fetch", "check-patch", "master"])
|
||||
subprocess.check_call(["git", "fetch", "check-patch", "master"],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL)
|
||||
|
||||
ancestor = subprocess.check_output(["git", "merge-base",
|
||||
"check-patch/master", "HEAD"],
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
.cirrus_build_job:
|
||||
extends: .base_job_template
|
||||
stage: build
|
||||
image: registry.gitlab.com/libvirt/libvirt-ci/cirrus-run:latest
|
||||
image: registry.gitlab.com/libvirt/libvirt-ci/cirrus-run:master
|
||||
needs: []
|
||||
# 20 mins larger than "timeout_in" in cirrus/build.yml
|
||||
# as there's often a 5-10 minute delay before Cirrus CI
|
||||
@@ -57,7 +57,6 @@ x64-freebsd-13-build:
|
||||
CIRRUS_VM_RAM: 8G
|
||||
UPDATE_COMMAND: pkg update; pkg upgrade -y
|
||||
INSTALL_COMMAND: pkg install -y
|
||||
CONFIGURE_ARGS: --target-list-exclude=arm-softmmu,i386-softmmu,microblaze-softmmu,mips64el-softmmu,mipsel-softmmu,mips-softmmu,ppc-softmmu,sh4eb-softmmu,xtensa-softmmu
|
||||
TEST_TARGETS: check
|
||||
|
||||
aarch64-macos-13-base-build:
|
||||
@@ -73,7 +72,6 @@ aarch64-macos-13-base-build:
|
||||
INSTALL_COMMAND: brew install
|
||||
PATH_EXTRA: /opt/homebrew/ccache/libexec:/opt/homebrew/gettext/bin
|
||||
PKG_CONFIG_PATH: /opt/homebrew/curl/lib/pkgconfig:/opt/homebrew/ncurses/lib/pkgconfig:/opt/homebrew/readline/lib/pkgconfig
|
||||
CONFIGURE_ARGS: --target-list-exclude=arm-softmmu,i386-softmmu,microblazeel-softmmu,mips64-softmmu,mipsel-softmmu,mips-softmmu,ppc-softmmu,sh4-softmmu,xtensaeb-softmmu
|
||||
TEST_TARGETS: check-unit check-block check-qapi-schema check-softfloat check-qtest-x86_64
|
||||
|
||||
aarch64-macos-14-base-build:
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
include:
|
||||
- local: '/.gitlab-ci.d/container-template.yml'
|
||||
|
||||
amd64-centos9-container:
|
||||
amd64-centos8-container:
|
||||
extends: .container_job_template
|
||||
variables:
|
||||
NAME: centos9
|
||||
NAME: centos8
|
||||
|
||||
amd64-fedora-container:
|
||||
extends: .container_job_template
|
||||
|
||||
@@ -22,6 +22,12 @@ arm64-debian-cross-container:
|
||||
variables:
|
||||
NAME: debian-arm64-cross
|
||||
|
||||
armel-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
stage: containers
|
||||
variables:
|
||||
NAME: debian-armel-cross
|
||||
|
||||
armhf-debian-cross-container:
|
||||
extends: .container_job_template
|
||||
stage: containers
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
include:
|
||||
- local: '/.gitlab-ci.d/crossbuild-template.yml'
|
||||
|
||||
cross-armel-user:
|
||||
extends: .cross_user_build_job
|
||||
needs:
|
||||
job: armel-debian-cross-container
|
||||
variables:
|
||||
IMAGE: debian-armel-cross
|
||||
|
||||
cross-armhf-user:
|
||||
extends: .cross_user_build_job
|
||||
needs:
|
||||
|
||||
@@ -10,14 +10,13 @@
|
||||
# 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 custom runners can extend this template to upload the testlog
|
||||
# data as an artifact and also feed the junit report
|
||||
.custom_runner_template:
|
||||
extends: .base_job_template
|
||||
variables:
|
||||
GIT_STRATEGY: clone
|
||||
GIT_FETCH_EXTRA_FLAGS: --no-tags --prune --quiet
|
||||
artifacts:
|
||||
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
|
||||
expire_in: 7 days
|
||||
@@ -29,7 +28,7 @@
|
||||
junit: build/meson-logs/testlog.junit.xml
|
||||
|
||||
include:
|
||||
- local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml'
|
||||
- local: '/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml'
|
||||
- local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml'
|
||||
- local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml'
|
||||
- local: '/.gitlab-ci.d/custom-runners/centos-stream-8-x86_64.yml'
|
||||
|
||||
+14
-14
@@ -1,13 +1,13 @@
|
||||
# All ubuntu-22.04 jobs should run successfully in an environment
|
||||
# All ubuntu-20.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 22.04"
|
||||
# "Install basic packages to build QEMU on Ubuntu 20.04/20.04"
|
||||
|
||||
ubuntu-22.04-s390x-all-linux-static:
|
||||
ubuntu-20.04-s390x-all-linux-static:
|
||||
extends: .custom_runner_template
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
- ubuntu_22.04
|
||||
- ubuntu_20.04
|
||||
- s390x
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
@@ -23,12 +23,12 @@ ubuntu-22.04-s390x-all-linux-static:
|
||||
- make --output-sync check-tcg
|
||||
- make --output-sync -j`nproc` check
|
||||
|
||||
ubuntu-22.04-s390x-all:
|
||||
ubuntu-20.04-s390x-all:
|
||||
extends: .custom_runner_template
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
- ubuntu_22.04
|
||||
- ubuntu_20.04
|
||||
- s390x
|
||||
timeout: 75m
|
||||
rules:
|
||||
@@ -42,12 +42,12 @@ ubuntu-22.04-s390x-all:
|
||||
- make --output-sync -j`nproc`
|
||||
- make --output-sync -j`nproc` check
|
||||
|
||||
ubuntu-22.04-s390x-alldbg:
|
||||
ubuntu-20.04-s390x-alldbg:
|
||||
extends: .custom_runner_template
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
- ubuntu_22.04
|
||||
- ubuntu_20.04
|
||||
- s390x
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
@@ -65,12 +65,12 @@ ubuntu-22.04-s390x-alldbg:
|
||||
- make --output-sync -j`nproc`
|
||||
- make --output-sync -j`nproc` check
|
||||
|
||||
ubuntu-22.04-s390x-clang:
|
||||
ubuntu-20.04-s390x-clang:
|
||||
extends: .custom_runner_template
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
- ubuntu_22.04
|
||||
- ubuntu_20.04
|
||||
- s390x
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
@@ -87,11 +87,11 @@ ubuntu-22.04-s390x-clang:
|
||||
- make --output-sync -j`nproc`
|
||||
- make --output-sync -j`nproc` check
|
||||
|
||||
ubuntu-22.04-s390x-tci:
|
||||
ubuntu-20.04-s390x-tci:
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
- ubuntu_22.04
|
||||
- ubuntu_20.04
|
||||
- s390x
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
@@ -107,12 +107,12 @@ ubuntu-22.04-s390x-tci:
|
||||
|| { cat config.log meson-logs/meson-log.txt; exit 1; }
|
||||
- make --output-sync -j`nproc`
|
||||
|
||||
ubuntu-22.04-s390x-notcg:
|
||||
ubuntu-20.04-s390x-notcg:
|
||||
extends: .custom_runner_template
|
||||
needs: []
|
||||
stage: build
|
||||
tags:
|
||||
- ubuntu_22.04
|
||||
- ubuntu_20.04
|
||||
- s390x
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
@@ -1,7 +1,9 @@
|
||||
msys2-64bit:
|
||||
extends: .base_job_template
|
||||
tags:
|
||||
- saas-windows-medium-amd64
|
||||
- shared-windows
|
||||
- windows
|
||||
- windows-1809
|
||||
cache:
|
||||
key: "$CI_JOB_NAME"
|
||||
paths:
|
||||
@@ -26,8 +28,6 @@ msys2-64bit:
|
||||
# qTests don't run successfully with "--without-default-devices",
|
||||
# so let's exclude the qtests from CI for now.
|
||||
TEST_ARGS: --no-suite qtest
|
||||
# The Windows git is a bit older so override the default
|
||||
GIT_FETCH_EXTRA_FLAGS: --no-tags --prune --quiet
|
||||
artifacts:
|
||||
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
|
||||
expire_in: 7 days
|
||||
|
||||
+7
-7
@@ -35,7 +35,7 @@ env:
|
||||
- TEST_BUILD_CMD=""
|
||||
- TEST_CMD="make check V=1"
|
||||
# This is broadly a list of "mainline" system targets which have support across the major distros
|
||||
- MAIN_SYSTEM_TARGETS="aarch64-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
|
||||
- MAIN_SOFTMMU_TARGETS="aarch64-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
|
||||
- CCACHE_SLOPPINESS="include_file_ctime,include_file_mtime"
|
||||
- CCACHE_MAXSIZE=1G
|
||||
- G_MESSAGES_DEBUG=error
|
||||
@@ -114,7 +114,7 @@ jobs:
|
||||
env:
|
||||
- TEST_CMD="make check check-tcg V=1"
|
||||
- CONFIG="--disable-containers --enable-fdt=system
|
||||
--target-list=${MAIN_SYSTEM_TARGETS} --cxx=/bin/false"
|
||||
--target-list=${MAIN_SOFTMMU_TARGETS} --cxx=/bin/false"
|
||||
- UNRELIABLE=true
|
||||
|
||||
- name: "[ppc64] GCC check-tcg"
|
||||
@@ -184,8 +184,8 @@ jobs:
|
||||
- genisoimage
|
||||
env:
|
||||
- TEST_CMD="make check check-tcg V=1"
|
||||
- CONFIG="--disable-containers
|
||||
--target-list=hppa-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
|
||||
- CONFIG="--disable-containers --enable-fdt=system
|
||||
--target-list=${MAIN_SOFTMMU_TARGETS},s390x-linux-user"
|
||||
- UNRELIABLE=true
|
||||
script:
|
||||
- BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$?
|
||||
@@ -220,12 +220,13 @@ jobs:
|
||||
- libsnappy-dev
|
||||
- libzstd-dev
|
||||
- nettle-dev
|
||||
- xfslibs-dev
|
||||
- ninja-build
|
||||
# Tests dependencies
|
||||
- genisoimage
|
||||
env:
|
||||
- CONFIG="--disable-containers --audio-drv-list=sdl --disable-user
|
||||
--target-list=arm-softmmu,avr-softmmu,microblaze-softmmu,sh4eb-softmmu,sparc64-softmmu,xtensaeb-softmmu"
|
||||
- CONFIG="--disable-containers --enable-fdt=system --audio-drv-list=sdl
|
||||
--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
|
||||
|
||||
- name: "[s390x] GCC (user)"
|
||||
arch: s390x
|
||||
@@ -239,7 +240,6 @@ jobs:
|
||||
- flex
|
||||
- bison
|
||||
env:
|
||||
- TEST_CMD="make check check-tcg V=1"
|
||||
- CONFIG="--disable-containers --disable-system"
|
||||
|
||||
- name: "[s390x] Clang (disable-tcg)"
|
||||
|
||||
+10
-22
@@ -316,6 +316,7 @@ F: tests/tcg/openrisc/
|
||||
PowerPC TCG CPUs
|
||||
M: Nicholas Piggin <npiggin@gmail.com>
|
||||
M: Daniel Henrique Barboza <danielhb413@gmail.com>
|
||||
R: Cédric Le Goater <clg@kaod.org>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: target/ppc/
|
||||
@@ -467,6 +468,7 @@ F: target/mips/sysemu/
|
||||
PPC KVM CPUs
|
||||
M: Nicholas Piggin <npiggin@gmail.com>
|
||||
R: Daniel Henrique Barboza <danielhb413@gmail.com>
|
||||
R: Cédric Le Goater <clg@kaod.org>
|
||||
S: Odd Fixes
|
||||
F: target/ppc/kvm.c
|
||||
|
||||
@@ -1128,11 +1130,7 @@ M: Inès Varhol <ines.varhol@telecom-paris.fr>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/stm32l4x5_soc.c
|
||||
F: hw/misc/stm32l4x5_exti.c
|
||||
F: hw/misc/stm32l4x5_syscfg.c
|
||||
F: hw/misc/stm32l4x5_rcc.c
|
||||
F: hw/gpio/stm32l4x5_gpio.c
|
||||
F: include/hw/*/stm32l4x5_*.h
|
||||
F: include/hw/arm/stm32l4x5_soc.h
|
||||
|
||||
B-L475E-IOT01A IoT Node
|
||||
M: Arnaud Minier <arnaud.minier@telecom-paris.fr>
|
||||
@@ -1506,6 +1504,7 @@ F: tests/avocado/ppc_prep_40p.py
|
||||
sPAPR (pseries)
|
||||
M: Nicholas Piggin <npiggin@gmail.com>
|
||||
R: Daniel Henrique Barboza <danielhb413@gmail.com>
|
||||
R: Cédric Le Goater <clg@kaod.org>
|
||||
R: David Gibson <david@gibson.dropbear.id.au>
|
||||
R: Harsh Prateek Bora <harshpb@linux.ibm.com>
|
||||
L: qemu-ppc@nongnu.org
|
||||
@@ -1545,12 +1544,12 @@ F: pc-bios/skiboot.lid
|
||||
F: tests/qtest/pnv*
|
||||
|
||||
pca955x
|
||||
M: Glenn Miles <milesg@linux.ibm.com>
|
||||
M: Glenn Miles <milesg@linux.vnet.ibm.com>
|
||||
L: qemu-ppc@nongnu.org
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: hw/gpio/pca955*.c
|
||||
F: include/hw/gpio/pca955*.h
|
||||
F: hw/misc/pca955*.c
|
||||
F: include/hw/misc/pca955*.h
|
||||
|
||||
virtex_ml507
|
||||
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
|
||||
@@ -1572,7 +1571,6 @@ F: hw/rtc/m41t80.c
|
||||
F: pc-bios/canyonlands.dt[sb]
|
||||
F: pc-bios/u-boot-sam460ex-20100605.bin
|
||||
F: roms/u-boot-sam460ex
|
||||
F: docs/system/ppc/amigang.rst
|
||||
|
||||
pegasos2
|
||||
M: BALATON Zoltan <balaton@eik.bme.hu>
|
||||
@@ -2170,7 +2168,7 @@ S: Supported
|
||||
F: hw/vfio/*
|
||||
F: include/hw/vfio/
|
||||
F: docs/igd-assign.txt
|
||||
F: docs/devel/migration/vfio.rst
|
||||
F: docs/devel/vfio-migration.rst
|
||||
|
||||
vfio-ccw
|
||||
M: Eric Farman <farman@linux.ibm.com>
|
||||
@@ -2231,7 +2229,6 @@ F: qapi/virtio.json
|
||||
F: net/vhost-user.c
|
||||
F: include/hw/virtio/
|
||||
F: docs/devel/virtio*
|
||||
F: docs/devel/migration/virtio.rst
|
||||
|
||||
virtio-balloon
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
@@ -2406,7 +2403,6 @@ F: docs/system/devices/virtio-snd.rst
|
||||
nvme
|
||||
M: Keith Busch <kbusch@kernel.org>
|
||||
M: Klaus Jensen <its@irrelevant.dk>
|
||||
R: Jesper Devantier <foss@defmacro.it>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: hw/nvme/*
|
||||
@@ -2503,12 +2499,6 @@ S: Maintained
|
||||
F: hw/i2c/i2c_mux_pca954x.c
|
||||
F: include/hw/i2c/i2c_mux_pca954x.h
|
||||
|
||||
pcf8574
|
||||
M: Dmitrii Sharikhin <d.sharikhin@yadro.com>
|
||||
S: Maintained
|
||||
F: hw/gpio/pcf8574.c
|
||||
F: include/gpio/pcf8574.h
|
||||
|
||||
Generic Loader
|
||||
M: Alistair Francis <alistair@alistair23.me>
|
||||
S: Maintained
|
||||
@@ -3009,7 +2999,7 @@ F: include/qapi/error.h
|
||||
F: include/qemu/error-report.h
|
||||
F: qapi/error.json
|
||||
F: util/error.c
|
||||
F: util/error-report.c
|
||||
F: util/qemu-error.c
|
||||
F: scripts/coccinelle/err-bad-newline.cocci
|
||||
F: scripts/coccinelle/error-use-after-free.cocci
|
||||
F: scripts/coccinelle/error_propagate_null.cocci
|
||||
@@ -3418,7 +3408,7 @@ F: migration/
|
||||
F: scripts/vmstate-static-checker.py
|
||||
F: tests/vmstate-static-checker-data/
|
||||
F: tests/qtest/migration-test.c
|
||||
F: docs/devel/migration/
|
||||
F: docs/devel/migration.rst
|
||||
F: qapi/migration.json
|
||||
F: tests/migration/
|
||||
F: util/userfaultfd.c
|
||||
@@ -3438,7 +3428,6 @@ F: include/sysemu/dirtylimit.h
|
||||
F: migration/dirtyrate.c
|
||||
F: migration/dirtyrate.h
|
||||
F: include/sysemu/dirtyrate.h
|
||||
F: docs/devel/migration/dirty-limit.rst
|
||||
|
||||
Detached LUKS header
|
||||
M: Hyman Huang <yong.huang@smartx.com>
|
||||
@@ -3594,7 +3583,6 @@ F: util/iova-tree.c
|
||||
|
||||
elf2dmp
|
||||
M: Viktor Prutyanov <viktor.prutyanov@phystech.edu>
|
||||
R: Akihiko Odaki <akihiko.odaki@daynix.com>
|
||||
S: Maintained
|
||||
F: contrib/elf2dmp/
|
||||
|
||||
|
||||
@@ -141,13 +141,8 @@ 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, \
|
||||
$(or $(filter -l% -j%, $(MAKEFLAGS)), \
|
||||
$(if $(filter --jobserver-auth=%, $(MAKEFLAGS)),, -j1))) \
|
||||
NINJAFLAGS = $(if $V,-v) $(if $(MAKE.n), -n) $(if $(MAKE.k), -k0) \
|
||||
$(filter-out -j, $(lastword -j1 $(filter -l% -j%, $(MAKEFLAGS)))) \
|
||||
-d keepdepfile
|
||||
ninja-cmd-goals = $(or $(MAKECMDGOALS), all)
|
||||
ninja-cmd-goals += $(foreach g, $(MAKECMDGOALS), $(.ninja-goals.$g))
|
||||
|
||||
+20
-82
@@ -69,9 +69,6 @@
|
||||
#define KVM_GUESTDBG_BLOCKIRQ 0
|
||||
#endif
|
||||
|
||||
/* Default num of memslots to be allocated when VM starts */
|
||||
#define KVM_MEMSLOTS_NR_ALLOC_DEFAULT 16
|
||||
|
||||
struct KVMParkedVcpu {
|
||||
unsigned long vcpu_id;
|
||||
int kvm_fd;
|
||||
@@ -166,57 +163,6 @@ void kvm_resample_fd_notify(int gsi)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_slots_grow(): Grow the slots[] array in the KVMMemoryListener
|
||||
*
|
||||
* @kml: The KVMMemoryListener* to grow the slots[] array
|
||||
* @nr_slots_new: The new size of slots[] array
|
||||
*
|
||||
* Returns: True if the array grows larger, false otherwise.
|
||||
*/
|
||||
static bool kvm_slots_grow(KVMMemoryListener *kml, unsigned int nr_slots_new)
|
||||
{
|
||||
unsigned int i, cur = kml->nr_slots_allocated;
|
||||
KVMSlot *slots;
|
||||
|
||||
if (nr_slots_new > kvm_state->nr_slots) {
|
||||
nr_slots_new = kvm_state->nr_slots;
|
||||
}
|
||||
|
||||
if (cur >= nr_slots_new) {
|
||||
/* Big enough, no need to grow, or we reached max */
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cur == 0) {
|
||||
slots = g_new0(KVMSlot, nr_slots_new);
|
||||
} else {
|
||||
assert(kml->slots);
|
||||
slots = g_renew(KVMSlot, kml->slots, nr_slots_new);
|
||||
/*
|
||||
* g_renew() doesn't initialize extended buffers, however kvm
|
||||
* memslots require fields to be zero-initialized. E.g. pointers,
|
||||
* memory_size field, etc.
|
||||
*/
|
||||
memset(&slots[cur], 0x0, sizeof(slots[0]) * (nr_slots_new - cur));
|
||||
}
|
||||
|
||||
for (i = cur; i < nr_slots_new; i++) {
|
||||
slots[i].slot = i;
|
||||
}
|
||||
|
||||
kml->slots = slots;
|
||||
kml->nr_slots_allocated = nr_slots_new;
|
||||
trace_kvm_slots_grow(cur, nr_slots_new);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool kvm_slots_double(KVMMemoryListener *kml)
|
||||
{
|
||||
return kvm_slots_grow(kml, kml->nr_slots_allocated * 2);
|
||||
}
|
||||
|
||||
unsigned int kvm_get_max_memslots(void)
|
||||
{
|
||||
KVMState *s = KVM_STATE(current_accel());
|
||||
@@ -245,26 +191,15 @@ unsigned int kvm_get_free_memslots(void)
|
||||
/* Called with KVMMemoryListener.slots_lock held */
|
||||
static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml)
|
||||
{
|
||||
unsigned int n;
|
||||
KVMState *s = kvm_state;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < kml->nr_slots_allocated; i++) {
|
||||
for (i = 0; i < s->nr_slots; i++) {
|
||||
if (kml->slots[i].memory_size == 0) {
|
||||
return &kml->slots[i];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If no free slots, try to grow first by doubling. Cache the old size
|
||||
* here to avoid another round of search: if the grow succeeded, it
|
||||
* means slots[] now must have the existing "n" slots occupied,
|
||||
* followed by one or more free slots starting from slots[n].
|
||||
*/
|
||||
n = kml->nr_slots_allocated;
|
||||
if (kvm_slots_double(kml)) {
|
||||
return &kml->slots[n];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -285,9 +220,10 @@ static KVMSlot *kvm_lookup_matching_slot(KVMMemoryListener *kml,
|
||||
hwaddr start_addr,
|
||||
hwaddr size)
|
||||
{
|
||||
KVMState *s = kvm_state;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < kml->nr_slots_allocated; i++) {
|
||||
for (i = 0; i < s->nr_slots; i++) {
|
||||
KVMSlot *mem = &kml->slots[i];
|
||||
|
||||
if (start_addr == mem->start_addr && size == mem->memory_size) {
|
||||
@@ -329,7 +265,7 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram,
|
||||
int i, ret = 0;
|
||||
|
||||
kvm_slots_lock();
|
||||
for (i = 0; i < kml->nr_slots_allocated; i++) {
|
||||
for (i = 0; i < s->nr_slots; i++) {
|
||||
KVMSlot *mem = &kml->slots[i];
|
||||
|
||||
if (ram >= mem->ram && ram < mem->ram + mem->memory_size) {
|
||||
@@ -1061,7 +997,7 @@ static int kvm_physical_log_clear(KVMMemoryListener *kml,
|
||||
|
||||
kvm_slots_lock();
|
||||
|
||||
for (i = 0; i < kml->nr_slots_allocated; i++) {
|
||||
for (i = 0; i < s->nr_slots; i++) {
|
||||
mem = &kml->slots[i];
|
||||
/* Discard slots that are empty or do not overlap the section */
|
||||
if (!mem->memory_size ||
|
||||
@@ -1666,8 +1602,12 @@ static void kvm_log_sync_global(MemoryListener *l, bool last_stage)
|
||||
/* Flush all kernel dirty addresses into KVMSlot dirty bitmap */
|
||||
kvm_dirty_ring_flush();
|
||||
|
||||
/*
|
||||
* TODO: make this faster when nr_slots is big while there are
|
||||
* only a few used slots (small VMs).
|
||||
*/
|
||||
kvm_slots_lock();
|
||||
for (i = 0; i < kml->nr_slots_allocated; i++) {
|
||||
for (i = 0; i < s->nr_slots; i++) {
|
||||
mem = &kml->slots[i];
|
||||
if (mem->memory_size && mem->flags & KVM_MEM_LOG_DIRTY_PAGES) {
|
||||
kvm_slot_sync_dirty_pages(mem);
|
||||
@@ -1782,9 +1722,12 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml,
|
||||
{
|
||||
int i;
|
||||
|
||||
kml->slots = g_new0(KVMSlot, s->nr_slots);
|
||||
kml->as_id = as_id;
|
||||
|
||||
kvm_slots_grow(kml, KVM_MEMSLOTS_NR_ALLOC_DEFAULT);
|
||||
for (i = 0; i < s->nr_slots; i++) {
|
||||
kml->slots[i].slot = i;
|
||||
}
|
||||
|
||||
QSIMPLEQ_INIT(&kml->transaction_add);
|
||||
QSIMPLEQ_INIT(&kml->transaction_del);
|
||||
@@ -2056,17 +1999,12 @@ int kvm_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (s->irq_routes->nr < s->gsi_count) {
|
||||
trace_kvm_irqchip_add_msi_route(dev ? dev->name : (char *)"N/A",
|
||||
vector, virq);
|
||||
trace_kvm_irqchip_add_msi_route(dev ? dev->name : (char *)"N/A",
|
||||
vector, virq);
|
||||
|
||||
kvm_add_routing_entry(s, &kroute);
|
||||
kvm_arch_add_msi_route_post(&kroute, vector, dev);
|
||||
c->changes++;
|
||||
} else {
|
||||
kvm_irqchip_release_virq(s, virq);
|
||||
return -ENOSPC;
|
||||
}
|
||||
kvm_add_routing_entry(s, &kroute);
|
||||
kvm_arch_add_msi_route_post(&kroute, vector, dev);
|
||||
c->changes++;
|
||||
|
||||
return virq;
|
||||
}
|
||||
|
||||
@@ -31,4 +31,3 @@ kvm_cpu_exec(void) ""
|
||||
kvm_interrupt_exit_request(void) ""
|
||||
kvm_io_window_exit(void) ""
|
||||
kvm_run_exit_system_event(int cpu_index, uint32_t event_type) "cpu_index %d, system_even_type %"PRIu32
|
||||
kvm_slots_grow(unsigned int old, unsigned int new) "%u -> %u"
|
||||
|
||||
@@ -436,6 +436,7 @@ const void *HELPER(lookup_tb_ptr)(CPUArchState *env)
|
||||
static inline TranslationBlock * QEMU_DISABLE_CFI
|
||||
cpu_tb_exec(CPUState *cpu, TranslationBlock *itb, int *tb_exit)
|
||||
{
|
||||
CPUArchState *env = cpu_env(cpu);
|
||||
uintptr_t ret;
|
||||
TranslationBlock *last_tb;
|
||||
const void *tb_ptr = itb->tc.ptr;
|
||||
@@ -445,7 +446,7 @@ cpu_tb_exec(CPUState *cpu, TranslationBlock *itb, int *tb_exit)
|
||||
}
|
||||
|
||||
qemu_thread_jit_execute();
|
||||
ret = tcg_qemu_tb_exec(cpu_env(cpu), tb_ptr);
|
||||
ret = tcg_qemu_tb_exec(env, tb_ptr);
|
||||
cpu->neg.can_do_io = true;
|
||||
qemu_plugin_disable_mem_helpers(cpu);
|
||||
/*
|
||||
|
||||
+7
-28
@@ -1145,11 +1145,14 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx,
|
||||
" prot=%x idx=%d\n",
|
||||
addr, full->phys_addr, prot, mmu_idx);
|
||||
|
||||
read_flags = full->tlb_fill_flags;
|
||||
read_flags = 0;
|
||||
if (full->lg_page_size < TARGET_PAGE_BITS) {
|
||||
/* Repeat the MMU check and TLB fill on every access. */
|
||||
read_flags |= TLB_INVALID_MASK;
|
||||
}
|
||||
if (full->attrs.byte_swap) {
|
||||
read_flags |= TLB_BSWAP;
|
||||
}
|
||||
|
||||
is_ram = memory_region_is_ram(section->mr);
|
||||
is_romd = memory_region_is_romd(section->mr);
|
||||
@@ -1453,8 +1456,9 @@ static int probe_access_internal(CPUState *cpu, vaddr addr,
|
||||
flags |= full->slow_flags[access_type];
|
||||
|
||||
/* Fold all "mmio-like" bits into TLB_MMIO. This is not RAM. */
|
||||
if (unlikely(flags & ~(TLB_WATCHPOINT | TLB_NOTDIRTY | TLB_CHECK_ALIGNED))
|
||||
|| (access_type != MMU_INST_FETCH && force_mmio)) {
|
||||
if (unlikely(flags & ~(TLB_WATCHPOINT | TLB_NOTDIRTY))
|
||||
||
|
||||
(access_type != MMU_INST_FETCH && force_mmio)) {
|
||||
*phost = NULL;
|
||||
return TLB_MMIO;
|
||||
}
|
||||
@@ -1835,31 +1839,6 @@ static bool mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi,
|
||||
tcg_debug_assert((flags & TLB_BSWAP) == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This alignment check differs from the one above, in that this is
|
||||
* based on the atomicity of the operation. The intended use case is
|
||||
* the ARM memory type field of each PTE, where access to pages with
|
||||
* Device memory type require alignment.
|
||||
*/
|
||||
if (unlikely(flags & TLB_CHECK_ALIGNED)) {
|
||||
MemOp size = l->memop & MO_SIZE;
|
||||
|
||||
switch (l->memop & MO_ATOM_MASK) {
|
||||
case MO_ATOM_NONE:
|
||||
size = MO_8;
|
||||
break;
|
||||
case MO_ATOM_IFALIGN_PAIR:
|
||||
case MO_ATOM_WITHIN16_PAIR:
|
||||
size = size ? size - 1 : 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (addr & ((1 << size) - 1)) {
|
||||
cpu_unaligned_access(cpu, addr, type, l->mmu_idx, ra);
|
||||
}
|
||||
}
|
||||
|
||||
return crosspage;
|
||||
}
|
||||
|
||||
|
||||
+18
-57
@@ -57,6 +57,12 @@
|
||||
#include "exec/helper-info.c.inc"
|
||||
#undef HELPER_H
|
||||
|
||||
#ifdef CONFIG_SOFTMMU
|
||||
# define CONFIG_SOFTMMU_GATE 1
|
||||
#else
|
||||
# define CONFIG_SOFTMMU_GATE 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* plugin_cb_start TCG op args[]:
|
||||
* 0: enum plugin_gen_from
|
||||
@@ -127,28 +133,16 @@ static void gen_empty_udata_cb_no_rwg(void)
|
||||
*/
|
||||
static void gen_empty_inline_cb(void)
|
||||
{
|
||||
TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
|
||||
TCGv_ptr cpu_index_as_ptr = tcg_temp_ebb_new_ptr();
|
||||
TCGv_i64 val = tcg_temp_ebb_new_i64();
|
||||
TCGv_ptr ptr = tcg_temp_ebb_new_ptr();
|
||||
|
||||
tcg_gen_ld_i32(cpu_index, tcg_env,
|
||||
-offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
|
||||
/* second operand will be replaced by immediate value */
|
||||
tcg_gen_mul_i32(cpu_index, cpu_index, cpu_index);
|
||||
tcg_gen_ext_i32_ptr(cpu_index_as_ptr, cpu_index);
|
||||
|
||||
tcg_gen_movi_ptr(ptr, 0);
|
||||
tcg_gen_add_ptr(ptr, ptr, cpu_index_as_ptr);
|
||||
tcg_gen_ld_i64(val, ptr, 0);
|
||||
/* second operand will be replaced by immediate value */
|
||||
tcg_gen_add_i64(val, val, val);
|
||||
|
||||
/* pass an immediate != 0 so that it doesn't get optimized away */
|
||||
tcg_gen_addi_i64(val, val, 0xdeadface);
|
||||
tcg_gen_st_i64(val, ptr, 0);
|
||||
tcg_temp_free_ptr(ptr);
|
||||
tcg_temp_free_i64(val);
|
||||
tcg_temp_free_ptr(cpu_index_as_ptr);
|
||||
tcg_temp_free_i32(cpu_index);
|
||||
}
|
||||
|
||||
static void gen_empty_mem_cb(TCGv_i64 addr, uint32_t info)
|
||||
@@ -296,37 +290,12 @@ static TCGOp *copy_const_ptr(TCGOp **begin_op, TCGOp *op, void *ptr)
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *copy_ld_i32(TCGOp **begin_op, TCGOp *op)
|
||||
{
|
||||
return copy_op(begin_op, op, INDEX_op_ld_i32);
|
||||
}
|
||||
|
||||
static TCGOp *copy_ext_i32_ptr(TCGOp **begin_op, TCGOp *op)
|
||||
{
|
||||
if (UINTPTR_MAX == UINT32_MAX) {
|
||||
op = copy_op(begin_op, op, INDEX_op_mov_i32);
|
||||
} else {
|
||||
op = copy_op(begin_op, op, INDEX_op_ext_i32_i64);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *copy_add_ptr(TCGOp **begin_op, TCGOp *op)
|
||||
{
|
||||
if (UINTPTR_MAX == UINT32_MAX) {
|
||||
op = copy_op(begin_op, op, INDEX_op_add_i32);
|
||||
} else {
|
||||
op = copy_op(begin_op, op, INDEX_op_add_i64);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *copy_ld_i64(TCGOp **begin_op, TCGOp *op)
|
||||
{
|
||||
if (TCG_TARGET_REG_BITS == 32) {
|
||||
/* 2x ld_i32 */
|
||||
op = copy_ld_i32(begin_op, op);
|
||||
op = copy_ld_i32(begin_op, op);
|
||||
op = copy_op(begin_op, op, INDEX_op_ld_i32);
|
||||
op = copy_op(begin_op, op, INDEX_op_ld_i32);
|
||||
} else {
|
||||
/* ld_i64 */
|
||||
op = copy_op(begin_op, op, INDEX_op_ld_i64);
|
||||
@@ -362,13 +331,6 @@ static TCGOp *copy_add_i64(TCGOp **begin_op, TCGOp *op, uint64_t v)
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *copy_mul_i32(TCGOp **begin_op, TCGOp *op, uint32_t v)
|
||||
{
|
||||
op = copy_op(begin_op, op, INDEX_op_mul_i32);
|
||||
op->args[2] = tcgv_i32_arg(tcg_constant_i32(v));
|
||||
return op;
|
||||
}
|
||||
|
||||
static TCGOp *copy_st_ptr(TCGOp **begin_op, TCGOp *op)
|
||||
{
|
||||
if (UINTPTR_MAX == UINT32_MAX) {
|
||||
@@ -434,19 +396,18 @@ static TCGOp *append_inline_cb(const struct qemu_plugin_dyn_cb *cb,
|
||||
TCGOp *begin_op, TCGOp *op,
|
||||
int *unused)
|
||||
{
|
||||
char *ptr = cb->inline_insn.entry.score->data->data;
|
||||
size_t elem_size = g_array_get_element_size(
|
||||
cb->inline_insn.entry.score->data);
|
||||
size_t offset = cb->inline_insn.entry.offset;
|
||||
/* const_ptr */
|
||||
op = copy_const_ptr(&begin_op, op, cb->userp);
|
||||
|
||||
op = copy_ld_i32(&begin_op, op);
|
||||
op = copy_mul_i32(&begin_op, op, elem_size);
|
||||
op = copy_ext_i32_ptr(&begin_op, op);
|
||||
op = copy_const_ptr(&begin_op, op, ptr + offset);
|
||||
op = copy_add_ptr(&begin_op, op);
|
||||
/* ld_i64 */
|
||||
op = copy_ld_i64(&begin_op, op);
|
||||
|
||||
/* add_i64 */
|
||||
op = copy_add_i64(&begin_op, op, cb->inline_insn.imm);
|
||||
|
||||
/* st_i64 */
|
||||
op = copy_st_i64(&begin_op, op);
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
|
||||
@@ -712,7 +712,7 @@ static void tb_record(TranslationBlock *tb)
|
||||
tb_page_addr_t paddr0 = tb_page_addr0(tb);
|
||||
tb_page_addr_t paddr1 = tb_page_addr1(tb);
|
||||
tb_page_addr_t pindex0 = paddr0 >> TARGET_PAGE_BITS;
|
||||
tb_page_addr_t pindex1 = paddr1 >> TARGET_PAGE_BITS;
|
||||
tb_page_addr_t pindex1 = paddr0 >> TARGET_PAGE_BITS;
|
||||
|
||||
assert(paddr0 != -1);
|
||||
if (unlikely(paddr1 != -1) && pindex0 != pindex1) {
|
||||
@@ -744,7 +744,7 @@ static void tb_remove(TranslationBlock *tb)
|
||||
tb_page_addr_t paddr0 = tb_page_addr0(tb);
|
||||
tb_page_addr_t paddr1 = tb_page_addr1(tb);
|
||||
tb_page_addr_t pindex0 = paddr0 >> TARGET_PAGE_BITS;
|
||||
tb_page_addr_t pindex1 = paddr1 >> TARGET_PAGE_BITS;
|
||||
tb_page_addr_t pindex1 = paddr0 >> TARGET_PAGE_BITS;
|
||||
|
||||
assert(paddr0 != -1);
|
||||
if (unlikely(paddr1 != -1) && pindex0 != pindex1) {
|
||||
|
||||
@@ -109,7 +109,7 @@ static void rr_wait_io_event(void)
|
||||
{
|
||||
CPUState *cpu;
|
||||
|
||||
while (all_cpu_threads_idle()) {
|
||||
while (all_cpu_threads_idle() && replay_can_wait()) {
|
||||
rr_stop_kick_timer();
|
||||
qemu_cond_wait_bql(first_cpu->halt_cond);
|
||||
}
|
||||
|
||||
@@ -634,7 +634,7 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
|
||||
cpu->cflags_next_tb = curr_cflags(cpu) | CF_MEMI_ONLY | n;
|
||||
|
||||
if (qemu_loglevel_mask(CPU_LOG_EXEC)) {
|
||||
vaddr pc = cpu->cc->get_pc(cpu);
|
||||
vaddr pc = log_pc(cpu, tb);
|
||||
if (qemu_log_in_addr_range(pc)) {
|
||||
qemu_log("cpu_io_recompile: rewound execution of TB to %016"
|
||||
VADDR_PRIx "\n", pc);
|
||||
|
||||
+22
-25
@@ -18,14 +18,20 @@
|
||||
|
||||
static void set_can_do_io(DisasContextBase *db, bool val)
|
||||
{
|
||||
QEMU_BUILD_BUG_ON(sizeof_field(CPUState, neg.can_do_io) != 1);
|
||||
tcg_gen_st8_i32(tcg_constant_i32(val), tcg_env,
|
||||
offsetof(ArchCPU, parent_obj.neg.can_do_io) -
|
||||
offsetof(ArchCPU, env));
|
||||
if (db->saved_can_do_io != val) {
|
||||
db->saved_can_do_io = val;
|
||||
|
||||
QEMU_BUILD_BUG_ON(sizeof_field(CPUState, neg.can_do_io) != 1);
|
||||
tcg_gen_st8_i32(tcg_constant_i32(val), tcg_env,
|
||||
offsetof(ArchCPU, parent_obj.neg.can_do_io) -
|
||||
offsetof(ArchCPU, env));
|
||||
}
|
||||
}
|
||||
|
||||
bool translator_io_start(DisasContextBase *db)
|
||||
{
|
||||
set_can_do_io(db, true);
|
||||
|
||||
/*
|
||||
* Ensure that this instruction will be the last in the TB.
|
||||
* The target may override this to something more forceful.
|
||||
@@ -78,6 +84,13 @@ static TCGOp *gen_tb_start(DisasContextBase *db, uint32_t cflags)
|
||||
- offsetof(ArchCPU, env));
|
||||
}
|
||||
|
||||
/*
|
||||
* cpu->neg.can_do_io is set automatically here at the beginning of
|
||||
* each translation block. The cost is minimal, plus it would be
|
||||
* very easy to forget doing it in the translator.
|
||||
*/
|
||||
set_can_do_io(db, db->max_insns == 1);
|
||||
|
||||
return icount_start_insn;
|
||||
}
|
||||
|
||||
@@ -116,7 +129,6 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
|
||||
{
|
||||
uint32_t cflags = tb_cflags(tb);
|
||||
TCGOp *icount_start_insn;
|
||||
TCGOp *first_insn_start = NULL;
|
||||
bool plugin_enabled;
|
||||
|
||||
/* Initialize DisasContext */
|
||||
@@ -127,7 +139,7 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
|
||||
db->num_insns = 0;
|
||||
db->max_insns = *max_insns;
|
||||
db->singlestep_enabled = cflags & CF_SINGLE_STEP;
|
||||
db->insn_start = NULL;
|
||||
db->saved_can_do_io = -1;
|
||||
db->host_addr[0] = host_pc;
|
||||
db->host_addr[1] = NULL;
|
||||
|
||||
@@ -145,10 +157,6 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
|
||||
while (true) {
|
||||
*max_insns = ++db->num_insns;
|
||||
ops->insn_start(db, cpu);
|
||||
db->insn_start = tcg_last_op();
|
||||
if (first_insn_start == NULL) {
|
||||
first_insn_start = db->insn_start;
|
||||
}
|
||||
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
|
||||
|
||||
if (plugin_enabled) {
|
||||
@@ -161,6 +169,10 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
|
||||
* done next -- either exiting this loop or locate the start of
|
||||
* the next instruction.
|
||||
*/
|
||||
if (db->num_insns == db->max_insns) {
|
||||
/* Accept I/O on the last instruction. */
|
||||
set_can_do_io(db, true);
|
||||
}
|
||||
ops->translate_insn(db, cpu);
|
||||
|
||||
/*
|
||||
@@ -193,21 +205,6 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
|
||||
ops->tb_stop(db, cpu);
|
||||
gen_tb_end(tb, cflags, icount_start_insn, db->num_insns);
|
||||
|
||||
/*
|
||||
* Manage can_do_io for the translation block: set to false before
|
||||
* the first insn and set to true before the last insn.
|
||||
*/
|
||||
if (db->num_insns == 1) {
|
||||
tcg_debug_assert(first_insn_start == db->insn_start);
|
||||
} else {
|
||||
tcg_debug_assert(first_insn_start != db->insn_start);
|
||||
tcg_ctx->emit_before_op = first_insn_start;
|
||||
set_can_do_io(db, false);
|
||||
}
|
||||
tcg_ctx->emit_before_op = db->insn_start;
|
||||
set_can_do_io(db, true);
|
||||
tcg_ctx->emit_before_op = NULL;
|
||||
|
||||
if (plugin_enabled) {
|
||||
plugin_gen_tb_end(cpu, db->num_insns);
|
||||
}
|
||||
|
||||
@@ -796,7 +796,7 @@ static int probe_access_internal(CPUArchState *env, vaddr addr,
|
||||
if (guest_addr_valid_untagged(addr)) {
|
||||
int page_flags = page_get_flags(addr);
|
||||
if (page_flags & acc_flag) {
|
||||
if (access_type != MMU_INST_FETCH
|
||||
if ((acc_flag == PAGE_READ || acc_flag == PAGE_WRITE)
|
||||
&& cpu_plugin_mem_cbs_enabled(env_cpu(env))) {
|
||||
return TLB_MMIO;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "hw/xen/xen_native.h"
|
||||
#include "hw/xen/xen-legacy-backend.h"
|
||||
#include "hw/xen/xen_pt.h"
|
||||
#include "hw/xen/xen_igd.h"
|
||||
#include "chardev/char.h"
|
||||
#include "qemu/accel.h"
|
||||
#include "sysemu/cpus.h"
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/cryptodev.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "standard-headers/linux/virtio_crypto.h"
|
||||
#include "crypto/cipher.h"
|
||||
@@ -397,8 +396,8 @@ static int cryptodev_builtin_create_session(
|
||||
case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
|
||||
case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
|
||||
default:
|
||||
error_report("Unsupported opcode :%" PRIu32 "",
|
||||
sess_info->op_code);
|
||||
error_setg(&local_error, "Unsupported opcode :%" PRIu32 "",
|
||||
sess_info->op_code);
|
||||
return -VIRTIO_CRYPTO_NOTSUPP;
|
||||
}
|
||||
|
||||
@@ -428,9 +427,7 @@ static int cryptodev_builtin_close_session(
|
||||
CRYPTODEV_BACKEND_BUILTIN(backend);
|
||||
CryptoDevBackendBuiltinSession *session;
|
||||
|
||||
if (session_id >= MAX_NUM_SESSIONS || !builtin->sessions[session_id]) {
|
||||
return -VIRTIO_CRYPTO_INVSESS;
|
||||
}
|
||||
assert(session_id < MAX_NUM_SESSIONS && builtin->sessions[session_id]);
|
||||
|
||||
session = builtin->sessions[session_id];
|
||||
if (session->cipher) {
|
||||
@@ -555,8 +552,8 @@ static int cryptodev_builtin_operation(
|
||||
|
||||
if (op_info->session_id >= MAX_NUM_SESSIONS ||
|
||||
builtin->sessions[op_info->session_id] == NULL) {
|
||||
error_report("Cannot find a valid session id: %" PRIu64 "",
|
||||
op_info->session_id);
|
||||
error_setg(&local_error, "Cannot find a valid session id: %" PRIu64 "",
|
||||
op_info->session_id);
|
||||
return -VIRTIO_CRYPTO_INVSESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@ static void iommufd_backend_finalize(Object *obj)
|
||||
|
||||
static void iommufd_backend_set_fd(Object *obj, const char *str, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
IOMMUFDBackend *be = IOMMUFD_BACKEND(obj);
|
||||
int fd = -1;
|
||||
|
||||
|
||||
@@ -86,7 +86,6 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
|
||||
BlockDriverState *parent,
|
||||
const BdrvChildClass *child_class,
|
||||
BdrvChildRole child_role,
|
||||
bool parse_filename,
|
||||
Error **errp);
|
||||
|
||||
static bool bdrv_recurse_has_child(BlockDriverState *bs,
|
||||
@@ -535,9 +534,9 @@ typedef struct CreateCo {
|
||||
int coroutine_fn bdrv_co_create(BlockDriver *drv, const char *filename,
|
||||
QemuOpts *opts, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
int ret;
|
||||
GLOBAL_STATE_CODE();
|
||||
ERRP_GUARD();
|
||||
|
||||
if (!drv->bdrv_co_create_opts) {
|
||||
error_setg(errp, "Driver '%s' does not support image creation",
|
||||
@@ -634,7 +633,6 @@ int coroutine_fn bdrv_co_create_opts_simple(BlockDriver *drv,
|
||||
QemuOpts *opts,
|
||||
Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
BlockBackend *blk;
|
||||
QDict *options;
|
||||
int64_t size = 0;
|
||||
@@ -2000,7 +1998,6 @@ fail_opts:
|
||||
|
||||
static QDict *parse_json_filename(const char *filename, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
QObject *options_obj;
|
||||
QDict *options;
|
||||
int ret;
|
||||
@@ -2059,8 +2056,7 @@ static void parse_json_protocol(QDict *options, const char **pfilename,
|
||||
* block driver has been specified explicitly.
|
||||
*/
|
||||
static int bdrv_fill_options(QDict **options, const char *filename,
|
||||
int *flags, bool allow_parse_filename,
|
||||
Error **errp)
|
||||
int *flags, Error **errp)
|
||||
{
|
||||
const char *drvname;
|
||||
bool protocol = *flags & BDRV_O_PROTOCOL;
|
||||
@@ -2102,7 +2098,7 @@ static int bdrv_fill_options(QDict **options, const char *filename,
|
||||
if (protocol && filename) {
|
||||
if (!qdict_haskey(*options, "filename")) {
|
||||
qdict_put_str(*options, "filename", filename);
|
||||
parse_filename = allow_parse_filename;
|
||||
parse_filename = true;
|
||||
} else {
|
||||
error_setg(errp, "Can't specify 'file' and 'filename' options at "
|
||||
"the same time");
|
||||
@@ -3589,7 +3585,6 @@ int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
|
||||
int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
|
||||
const char *bdref_key, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
char *backing_filename = NULL;
|
||||
char *bdref_key_dot;
|
||||
const char *reference = NULL;
|
||||
@@ -3665,8 +3660,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
|
||||
}
|
||||
|
||||
backing_hd = bdrv_open_inherit(backing_filename, reference, options, 0, bs,
|
||||
&child_of_bds, bdrv_backing_role(bs), true,
|
||||
errp);
|
||||
&child_of_bds, bdrv_backing_role(bs), errp);
|
||||
if (!backing_hd) {
|
||||
bs->open_flags |= BDRV_O_NO_BACKING;
|
||||
error_prepend(errp, "Could not open backing file: ");
|
||||
@@ -3700,8 +3694,7 @@ free_exit:
|
||||
static BlockDriverState *
|
||||
bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key,
|
||||
BlockDriverState *parent, const BdrvChildClass *child_class,
|
||||
BdrvChildRole child_role, bool allow_none,
|
||||
bool parse_filename, Error **errp)
|
||||
BdrvChildRole child_role, bool allow_none, Error **errp)
|
||||
{
|
||||
BlockDriverState *bs = NULL;
|
||||
QDict *image_options;
|
||||
@@ -3732,8 +3725,7 @@ bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key,
|
||||
}
|
||||
|
||||
bs = bdrv_open_inherit(filename, reference, image_options, 0,
|
||||
parent, child_class, child_role, parse_filename,
|
||||
errp);
|
||||
parent, child_class, child_role, errp);
|
||||
if (!bs) {
|
||||
goto done;
|
||||
}
|
||||
@@ -3743,33 +3735,6 @@ done:
|
||||
return bs;
|
||||
}
|
||||
|
||||
static BdrvChild *bdrv_open_child_common(const char *filename,
|
||||
QDict *options, const char *bdref_key,
|
||||
BlockDriverState *parent,
|
||||
const BdrvChildClass *child_class,
|
||||
BdrvChildRole child_role,
|
||||
bool allow_none, bool parse_filename,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
BdrvChild *child;
|
||||
|
||||
GLOBAL_STATE_CODE();
|
||||
|
||||
bs = bdrv_open_child_bs(filename, options, bdref_key, parent, child_class,
|
||||
child_role, allow_none, parse_filename, errp);
|
||||
if (bs == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bdrv_graph_wrlock();
|
||||
child = bdrv_attach_child(parent, bs, bdref_key, child_class, child_role,
|
||||
errp);
|
||||
bdrv_graph_wrunlock();
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
/*
|
||||
* Opens a disk image whose options are given as BlockdevRef in another block
|
||||
* device's options.
|
||||
@@ -3793,15 +3758,27 @@ BdrvChild *bdrv_open_child(const char *filename,
|
||||
BdrvChildRole child_role,
|
||||
bool allow_none, Error **errp)
|
||||
{
|
||||
return bdrv_open_child_common(filename, options, bdref_key, parent,
|
||||
child_class, child_role, allow_none, false,
|
||||
errp);
|
||||
BlockDriverState *bs;
|
||||
BdrvChild *child;
|
||||
|
||||
GLOBAL_STATE_CODE();
|
||||
|
||||
bs = bdrv_open_child_bs(filename, options, bdref_key, parent, child_class,
|
||||
child_role, allow_none, errp);
|
||||
if (bs == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bdrv_graph_wrlock();
|
||||
child = bdrv_attach_child(parent, bs, bdref_key, child_class, child_role,
|
||||
errp);
|
||||
bdrv_graph_wrunlock();
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
/*
|
||||
* This does mostly the same as bdrv_open_child(), but for opening the primary
|
||||
* child of a node. A notable difference from bdrv_open_child() is that it
|
||||
* enables filename parsing for protocol names (including json:).
|
||||
* Wrapper on bdrv_open_child() for most popular case: open primary child of bs.
|
||||
*
|
||||
* @parent can move to a different AioContext in this function.
|
||||
*/
|
||||
@@ -3816,8 +3793,8 @@ int bdrv_open_file_child(const char *filename,
|
||||
role = parent->drv->is_filter ?
|
||||
(BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY) : BDRV_CHILD_IMAGE;
|
||||
|
||||
if (!bdrv_open_child_common(filename, options, bdref_key, parent,
|
||||
&child_of_bds, role, false, true, errp))
|
||||
if (!bdrv_open_child(filename, options, bdref_key, parent,
|
||||
&child_of_bds, role, false, errp))
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -3862,8 +3839,7 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp)
|
||||
|
||||
}
|
||||
|
||||
bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, 0, false,
|
||||
errp);
|
||||
bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, 0, errp);
|
||||
obj = NULL;
|
||||
qobject_unref(obj);
|
||||
visit_free(v);
|
||||
@@ -3875,7 +3851,6 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs,
|
||||
QDict *snapshot_options,
|
||||
Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
g_autofree char *tmp_filename = NULL;
|
||||
int64_t total_size;
|
||||
QemuOpts *opts = NULL;
|
||||
@@ -3953,7 +3928,7 @@ static BlockDriverState * no_coroutine_fn
|
||||
bdrv_open_inherit(const char *filename, const char *reference, QDict *options,
|
||||
int flags, BlockDriverState *parent,
|
||||
const BdrvChildClass *child_class, BdrvChildRole child_role,
|
||||
bool parse_filename, Error **errp)
|
||||
Error **errp)
|
||||
{
|
||||
int ret;
|
||||
BlockBackend *file = NULL;
|
||||
@@ -4001,11 +3976,9 @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options,
|
||||
}
|
||||
|
||||
/* json: syntax counts as explicit options, as if in the QDict */
|
||||
if (parse_filename) {
|
||||
parse_json_protocol(options, &filename, &local_err);
|
||||
if (local_err) {
|
||||
goto fail;
|
||||
}
|
||||
parse_json_protocol(options, &filename, &local_err);
|
||||
if (local_err) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bs->explicit_options = qdict_clone_shallow(options);
|
||||
@@ -4030,8 +4003,7 @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options,
|
||||
parent->open_flags, parent->options);
|
||||
}
|
||||
|
||||
ret = bdrv_fill_options(&options, filename, &flags, parse_filename,
|
||||
&local_err);
|
||||
ret = bdrv_fill_options(&options, filename, &flags, &local_err);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -4100,7 +4072,7 @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options,
|
||||
|
||||
file_bs = bdrv_open_child_bs(filename, options, "file", bs,
|
||||
&child_of_bds, BDRV_CHILD_IMAGE,
|
||||
true, true, &local_err);
|
||||
true, &local_err);
|
||||
if (local_err) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -4249,7 +4221,7 @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
|
||||
GLOBAL_STATE_CODE();
|
||||
|
||||
return bdrv_open_inherit(filename, reference, options, flags, NULL,
|
||||
NULL, 0, true, errp);
|
||||
NULL, 0, errp);
|
||||
}
|
||||
|
||||
/* Return true if the NULL-terminated @list contains @str */
|
||||
|
||||
+2
-4
@@ -899,10 +899,8 @@ static int blkio_file_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
bs->supported_write_flags = BDRV_REQ_FUA | BDRV_REQ_REGISTERED_BUF;
|
||||
bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK;
|
||||
#ifdef CONFIG_BLKIO_WRITE_ZEROS_FUA
|
||||
bs->supported_zero_flags |= BDRV_REQ_FUA;
|
||||
#endif
|
||||
bs->supported_zero_flags = BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP |
|
||||
BDRV_REQ_NO_FALLBACK;
|
||||
|
||||
qemu_mutex_init(&s->blkio_lock);
|
||||
qemu_co_mutex_init(&s->bounce_lock);
|
||||
|
||||
+11
-7
@@ -599,14 +599,14 @@ BlockDriverState *bdrv_next(BdrvNextIterator *it)
|
||||
/* Must be called from the main loop */
|
||||
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
|
||||
|
||||
old_bs = it->bs;
|
||||
|
||||
/* First, return all root nodes of BlockBackends. In order to avoid
|
||||
* returning a BDS twice when multiple BBs refer to it, we only return it
|
||||
* if the BB is the first one in the parent list of the BDS. */
|
||||
if (it->phase == BDRV_NEXT_BACKEND_ROOTS) {
|
||||
BlockBackend *old_blk = it->blk;
|
||||
|
||||
old_bs = old_blk ? blk_bs(old_blk) : NULL;
|
||||
|
||||
do {
|
||||
it->blk = blk_all_next(it->blk);
|
||||
bs = it->blk ? blk_bs(it->blk) : NULL;
|
||||
@@ -620,10 +620,11 @@ BlockDriverState *bdrv_next(BdrvNextIterator *it)
|
||||
if (bs) {
|
||||
bdrv_ref(bs);
|
||||
bdrv_unref(old_bs);
|
||||
it->bs = bs;
|
||||
return bs;
|
||||
}
|
||||
it->phase = BDRV_NEXT_MONITOR_OWNED;
|
||||
} else {
|
||||
old_bs = it->bs;
|
||||
}
|
||||
|
||||
/* Then return the monitor-owned BDSes without a BB attached. Ignore all
|
||||
@@ -663,10 +664,13 @@ void bdrv_next_cleanup(BdrvNextIterator *it)
|
||||
/* Must be called from the main loop */
|
||||
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
|
||||
|
||||
bdrv_unref(it->bs);
|
||||
|
||||
if (it->phase == BDRV_NEXT_BACKEND_ROOTS && it->blk) {
|
||||
blk_unref(it->blk);
|
||||
if (it->phase == BDRV_NEXT_BACKEND_ROOTS) {
|
||||
if (it->blk) {
|
||||
bdrv_unref(blk_bs(it->blk));
|
||||
blk_unref(it->blk);
|
||||
}
|
||||
} else {
|
||||
bdrv_unref(it->bs);
|
||||
}
|
||||
|
||||
bdrv_next_reset(it);
|
||||
|
||||
@@ -65,8 +65,7 @@ typedef struct BDRVCopyBeforeWriteState {
|
||||
|
||||
/*
|
||||
* @frozen_read_reqs: current read requests for fleecing user in bs->file
|
||||
* node. These areas must not be rewritten by guest. There can be multiple
|
||||
* overlapping read requests.
|
||||
* node. These areas must not be rewritten by guest.
|
||||
*/
|
||||
BlockReqList frozen_read_reqs;
|
||||
|
||||
@@ -408,7 +407,6 @@ out:
|
||||
static int cbw_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
BDRVCopyBeforeWriteState *s = bs->opaque;
|
||||
BdrvDirtyBitmap *bitmap = NULL;
|
||||
int64_t cluster_size;
|
||||
|
||||
+12
-19
@@ -1726,29 +1726,22 @@ static int bdrv_pad_request(BlockDriverState *bs,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* For prefetching in stream_populate(), no qiov is passed along, because
|
||||
* only copy-on-read matters.
|
||||
*/
|
||||
if (*qiov) {
|
||||
sliced_iov = qemu_iovec_slice(*qiov, *qiov_offset, *bytes,
|
||||
&sliced_head, &sliced_tail,
|
||||
&sliced_niov);
|
||||
sliced_iov = qemu_iovec_slice(*qiov, *qiov_offset, *bytes,
|
||||
&sliced_head, &sliced_tail,
|
||||
&sliced_niov);
|
||||
|
||||
/* Guaranteed by bdrv_check_request32() */
|
||||
assert(*bytes <= SIZE_MAX);
|
||||
ret = bdrv_create_padded_qiov(bs, pad, sliced_iov, sliced_niov,
|
||||
sliced_head, *bytes);
|
||||
if (ret < 0) {
|
||||
bdrv_padding_finalize(pad);
|
||||
return ret;
|
||||
}
|
||||
*qiov = &pad->local_qiov;
|
||||
*qiov_offset = 0;
|
||||
/* Guaranteed by bdrv_check_request32() */
|
||||
assert(*bytes <= SIZE_MAX);
|
||||
ret = bdrv_create_padded_qiov(bs, pad, sliced_iov, sliced_niov,
|
||||
sliced_head, *bytes);
|
||||
if (ret < 0) {
|
||||
bdrv_padding_finalize(pad);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*bytes += pad->head + pad->tail;
|
||||
*offset -= pad->head;
|
||||
*qiov = &pad->local_qiov;
|
||||
*qiov_offset = 0;
|
||||
if (padded) {
|
||||
*padded = true;
|
||||
}
|
||||
|
||||
+4
-6
@@ -479,9 +479,9 @@ static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset,
|
||||
return bytes_handled;
|
||||
}
|
||||
|
||||
static void coroutine_fn GRAPH_UNLOCKED mirror_iteration(MirrorBlockJob *s)
|
||||
static void coroutine_fn GRAPH_RDLOCK mirror_iteration(MirrorBlockJob *s)
|
||||
{
|
||||
BlockDriverState *source;
|
||||
BlockDriverState *source = s->mirror_top_bs->backing->bs;
|
||||
MirrorOp *pseudo_op;
|
||||
int64_t offset;
|
||||
/* At least the first dirty chunk is mirrored in one iteration. */
|
||||
@@ -489,10 +489,6 @@ static void coroutine_fn GRAPH_UNLOCKED mirror_iteration(MirrorBlockJob *s)
|
||||
bool write_zeroes_ok = bdrv_can_write_zeroes_with_unmap(blk_bs(s->target));
|
||||
int max_io_bytes = MAX(s->buf_size / MAX_IN_FLIGHT, MAX_IO_BYTES);
|
||||
|
||||
bdrv_graph_co_rdlock();
|
||||
source = s->mirror_top_bs->backing->bs;
|
||||
bdrv_graph_co_rdunlock();
|
||||
|
||||
bdrv_dirty_bitmap_lock(s->dirty_bitmap);
|
||||
offset = bdrv_dirty_iter_next(s->dbi);
|
||||
if (offset < 0) {
|
||||
@@ -1070,7 +1066,9 @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
|
||||
mirror_wait_for_free_in_flight_slot(s);
|
||||
continue;
|
||||
} else if (cnt != 0) {
|
||||
bdrv_graph_co_rdlock();
|
||||
mirror_iteration(s);
|
||||
bdrv_graph_co_rdunlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -402,8 +402,7 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
nbd_server_start(addr, NULL, NULL, NBD_DEFAULT_MAX_CONNECTIONS,
|
||||
&local_err);
|
||||
nbd_server_start(addr, NULL, NULL, 0, &local_err);
|
||||
qapi_free_SocketAddress(addr);
|
||||
if (local_err != NULL) {
|
||||
goto exit;
|
||||
|
||||
@@ -852,7 +852,6 @@ static coroutine_fn int nbd_co_do_receive_one_chunk(
|
||||
BDRVNBDState *s, uint64_t cookie, bool only_structured,
|
||||
int *request_ret, QEMUIOVector *qiov, void **payload, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
int ret;
|
||||
int i = COOKIE_TO_INDEX(cookie);
|
||||
void *local_payload = NULL;
|
||||
|
||||
@@ -168,7 +168,6 @@ static QemuOptsList runtime_opts = {
|
||||
static bool nvme_init_queue(BDRVNVMeState *s, NVMeQueue *q,
|
||||
unsigned nentries, size_t entry_bytes, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
size_t bytes;
|
||||
int r;
|
||||
|
||||
@@ -222,7 +221,6 @@ static NVMeQueuePair *nvme_create_queue_pair(BDRVNVMeState *s,
|
||||
unsigned idx, size_t size,
|
||||
Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
int i, r;
|
||||
NVMeQueuePair *q;
|
||||
uint64_t prp_list_iova;
|
||||
@@ -537,7 +535,6 @@ static int nvme_admin_cmd_sync(BlockDriverState *bs, NvmeCmd *cmd)
|
||||
/* Returns true on success, false on failure. */
|
||||
static bool nvme_identify(BlockDriverState *bs, int namespace, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
BDRVNVMeState *s = bs->opaque;
|
||||
bool ret = false;
|
||||
QEMU_AUTO_VFREE union {
|
||||
|
||||
+7
-9
@@ -46,11 +46,11 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
|
||||
bool flat,
|
||||
Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
ImageInfo **p_image_info;
|
||||
ImageInfo *backing_info;
|
||||
BlockDriverState *backing;
|
||||
BlockDeviceInfo *info;
|
||||
ERRP_GUARD();
|
||||
|
||||
if (!bs->drv) {
|
||||
error_setg(errp, "Block device %s is ejected", bs->node_name);
|
||||
@@ -330,8 +330,8 @@ void bdrv_query_image_info(BlockDriverState *bs,
|
||||
bool skip_implicit_filters,
|
||||
Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
ImageInfo *info;
|
||||
ERRP_GUARD();
|
||||
|
||||
info = g_new0(ImageInfo, 1);
|
||||
bdrv_do_query_node_info(bs, qapi_ImageInfo_base(info), errp);
|
||||
@@ -382,10 +382,10 @@ void bdrv_query_block_graph_info(BlockDriverState *bs,
|
||||
BlockGraphInfo **p_info,
|
||||
Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
BlockGraphInfo *info;
|
||||
BlockChildInfoList **children_list_tail;
|
||||
BdrvChild *c;
|
||||
ERRP_GUARD();
|
||||
|
||||
info = g_new0(BlockGraphInfo, 1);
|
||||
bdrv_do_query_node_info(bs, qapi_BlockGraphInfo_base(info), errp);
|
||||
@@ -742,15 +742,15 @@ void bdrv_snapshot_dump(QEMUSnapshotInfo *sn)
|
||||
char *sizing = NULL;
|
||||
|
||||
if (!sn) {
|
||||
qemu_printf("%-7s %-16s %8s %19s %15s %10s",
|
||||
"ID", "TAG", "VM_SIZE", "DATE", "VM_CLOCK", "ICOUNT");
|
||||
qemu_printf("%-10s%-17s%8s%20s%13s%11s",
|
||||
"ID", "TAG", "VM SIZE", "DATE", "VM CLOCK", "ICOUNT");
|
||||
} else {
|
||||
g_autoptr(GDateTime) date = g_date_time_new_from_unix_local(sn->date_sec);
|
||||
g_autofree char *date_buf = g_date_time_format(date, "%Y-%m-%d %H:%M:%S");
|
||||
|
||||
secs = sn->vm_clock_nsec / 1000000000;
|
||||
snprintf(clock_buf, sizeof(clock_buf),
|
||||
"%04d:%02d:%02d.%03d",
|
||||
"%02d:%02d:%02d.%03d",
|
||||
(int)(secs / 3600),
|
||||
(int)((secs / 60) % 60),
|
||||
(int)(secs % 60),
|
||||
@@ -759,10 +759,8 @@ void bdrv_snapshot_dump(QEMUSnapshotInfo *sn)
|
||||
if (sn->icount != -1ULL) {
|
||||
snprintf(icount_buf, sizeof(icount_buf),
|
||||
"%"PRId64, sn->icount);
|
||||
} else {
|
||||
snprintf(icount_buf, sizeof(icount_buf), "--");
|
||||
}
|
||||
qemu_printf("%-7s %-16s %8s %19s %15s %10s",
|
||||
qemu_printf("%-9s %-16s %8s%20s%13s%11s",
|
||||
sn->id_str, sn->name,
|
||||
sizing,
|
||||
date_buf,
|
||||
|
||||
@@ -1710,7 +1710,6 @@ bool coroutine_fn qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs,
|
||||
uint32_t granularity,
|
||||
Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
uint64_t bitmap_directory_size = 0;
|
||||
|
||||
+1
-18
@@ -1636,22 +1636,7 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (open_data_file && (flags & BDRV_O_NO_IO)) {
|
||||
/*
|
||||
* Don't open the data file for 'qemu-img info' so that it can be used
|
||||
* to verify that an untrusted qcow2 image doesn't refer to external
|
||||
* files.
|
||||
*
|
||||
* Note: This still makes has_data_file() return true.
|
||||
*/
|
||||
if (s->incompatible_features & QCOW2_INCOMPAT_DATA_FILE) {
|
||||
s->data_file = NULL;
|
||||
} else {
|
||||
s->data_file = bs->file;
|
||||
}
|
||||
qdict_extract_subqdict(options, NULL, "data-file.");
|
||||
qdict_del(options, "data-file");
|
||||
} else if (open_data_file) {
|
||||
if (open_data_file) {
|
||||
/* Open external data file */
|
||||
bdrv_graph_co_rdunlock();
|
||||
s->data_file = bdrv_co_open_child(NULL, options, "data-file", bs,
|
||||
@@ -3498,7 +3483,6 @@ static uint64_t qcow2_opt_get_refcount_bits_del(QemuOpts *opts, int version,
|
||||
static int coroutine_fn GRAPH_UNLOCKED
|
||||
qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
BlockdevCreateOptionsQcow2 *qcow2_opts;
|
||||
QDict *options;
|
||||
|
||||
@@ -4299,7 +4283,6 @@ static int coroutine_fn GRAPH_RDLOCK
|
||||
qcow2_co_truncate(BlockDriverState *bs, int64_t offset, bool exact,
|
||||
PreallocMode prealloc, BdrvRequestFlags flags, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint64_t old_length;
|
||||
int64_t new_l1_size;
|
||||
|
||||
@@ -1579,7 +1579,6 @@ bdrv_qed_co_change_backing_file(BlockDriverState *bs, const char *backing_file,
|
||||
static void coroutine_fn GRAPH_RDLOCK
|
||||
bdrv_qed_co_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
|
||||
+2
-2
@@ -111,7 +111,7 @@ raw_apply_options(BlockDriverState *bs, BDRVRawState *s, uint64_t offset,
|
||||
if (offset > real_size) {
|
||||
error_setg(errp, "Offset (%" PRIu64 ") cannot be greater than "
|
||||
"size of the containing file (%" PRId64 ")",
|
||||
offset, real_size);
|
||||
s->offset, real_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ raw_apply_options(BlockDriverState *bs, BDRVRawState *s, uint64_t offset,
|
||||
error_setg(errp, "The sum of offset (%" PRIu64 ") and size "
|
||||
"(%" PRIu64 ") has to be smaller or equal to the "
|
||||
" actual size of the containing file (%" PRId64 ")",
|
||||
offset, size, real_size);
|
||||
s->offset, s->size, real_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
void reqlist_init_req(BlockReqList *reqs, BlockReq *req, int64_t offset,
|
||||
int64_t bytes)
|
||||
{
|
||||
assert(!reqlist_find_conflict(reqs, offset, bytes));
|
||||
|
||||
*req = (BlockReq) {
|
||||
.offset = offset,
|
||||
.bytes = bytes,
|
||||
|
||||
@@ -566,7 +566,6 @@ int bdrv_all_delete_snapshot(const char *name,
|
||||
bool has_devices, strList *devices,
|
||||
Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
g_autoptr(GList) bdrvs = NULL;
|
||||
GList *iterbdrvs;
|
||||
|
||||
@@ -606,7 +605,6 @@ int bdrv_all_goto_snapshot(const char *name,
|
||||
bool has_devices, strList *devices,
|
||||
Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
g_autoptr(GList) bdrvs = NULL;
|
||||
GList *iterbdrvs;
|
||||
int ret;
|
||||
|
||||
@@ -738,7 +738,6 @@ static int coroutine_fn GRAPH_UNLOCKED
|
||||
vdi_co_do_create(BlockdevCreateOptions *create_options, size_t block_size,
|
||||
Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
BlockdevCreateOptionsVdi *vdi_opts;
|
||||
int ret = 0;
|
||||
uint64_t bytes = 0;
|
||||
|
||||
@@ -1147,7 +1147,6 @@ static int GRAPH_RDLOCK
|
||||
vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options,
|
||||
Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
int ret;
|
||||
int matches;
|
||||
char access[11];
|
||||
|
||||
+14
-13
@@ -1369,9 +1369,8 @@ static int open_file(BDRVVVFATState* s,mapping_t* mapping)
|
||||
return -1;
|
||||
vvfat_close_current_file(s);
|
||||
s->current_fd = fd;
|
||||
s->current_mapping = mapping;
|
||||
}
|
||||
|
||||
s->current_mapping = mapping;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1409,9 +1408,7 @@ read_cluster_directory:
|
||||
|
||||
assert(s->current_fd);
|
||||
|
||||
offset = s->cluster_size *
|
||||
((cluster_num - s->current_mapping->begin)
|
||||
+ s->current_mapping->info.file.offset);
|
||||
offset=s->cluster_size*(cluster_num-s->current_mapping->begin)+s->current_mapping->info.file.offset;
|
||||
if(lseek(s->current_fd, offset, SEEK_SET)!=offset)
|
||||
return -3;
|
||||
s->cluster=s->cluster_buffer;
|
||||
@@ -1881,6 +1878,7 @@ get_cluster_count_for_direntry(BDRVVVFATState* s, direntry_t* direntry, const ch
|
||||
|
||||
uint32_t cluster_num = begin_of_direntry(direntry);
|
||||
uint32_t offset = 0;
|
||||
int first_mapping_index = -1;
|
||||
mapping_t* mapping = NULL;
|
||||
const char* basename2 = NULL;
|
||||
|
||||
@@ -1931,9 +1929,8 @@ get_cluster_count_for_direntry(BDRVVVFATState* s, direntry_t* direntry, const ch
|
||||
(mapping->mode & MODE_DIRECTORY) == 0) {
|
||||
|
||||
/* was modified in qcow */
|
||||
if (offset != s->cluster_size
|
||||
* ((cluster_num - mapping->begin)
|
||||
+ mapping->info.file.offset)) {
|
||||
if (offset != mapping->info.file.offset + s->cluster_size
|
||||
* (cluster_num - mapping->begin)) {
|
||||
/* offset of this cluster in file chain has changed */
|
||||
abort();
|
||||
copy_it = 1;
|
||||
@@ -1942,9 +1939,14 @@ get_cluster_count_for_direntry(BDRVVVFATState* s, direntry_t* direntry, const ch
|
||||
|
||||
if (strcmp(basename, basename2))
|
||||
copy_it = 1;
|
||||
first_mapping_index = array_index(&(s->mapping), mapping);
|
||||
}
|
||||
|
||||
if (mapping->first_mapping_index != first_mapping_index
|
||||
&& mapping->info.file.offset > 0) {
|
||||
abort();
|
||||
copy_it = 1;
|
||||
}
|
||||
assert(mapping->first_mapping_index == -1
|
||||
|| mapping->info.file.offset > 0);
|
||||
|
||||
/* need to write out? */
|
||||
if (!was_modified && is_file(direntry)) {
|
||||
@@ -2402,7 +2404,7 @@ static int commit_mappings(BDRVVVFATState* s,
|
||||
(mapping->end - mapping->begin);
|
||||
} else
|
||||
next_mapping->info.file.offset = mapping->info.file.offset +
|
||||
(mapping->end - mapping->begin);
|
||||
mapping->end - mapping->begin;
|
||||
|
||||
mapping = next_mapping;
|
||||
}
|
||||
@@ -2523,9 +2525,8 @@ commit_one_file(BDRVVVFATState* s, int dir_index, uint32_t offset)
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < offset; i += s->cluster_size) {
|
||||
for (i = s->cluster_size; i < offset; i += s->cluster_size)
|
||||
c = modified_fat_get(s, c);
|
||||
}
|
||||
|
||||
fd = qemu_open_old(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666);
|
||||
if (fd < 0) {
|
||||
|
||||
+6
-53
@@ -21,18 +21,12 @@
|
||||
#include "io/channel-socket.h"
|
||||
#include "io/net-listener.h"
|
||||
|
||||
typedef struct NBDConn {
|
||||
QIOChannelSocket *cioc;
|
||||
QLIST_ENTRY(NBDConn) next;
|
||||
} NBDConn;
|
||||
|
||||
typedef struct NBDServerData {
|
||||
QIONetListener *listener;
|
||||
QCryptoTLSCreds *tlscreds;
|
||||
char *tlsauthz;
|
||||
uint32_t max_connections;
|
||||
uint32_t connections;
|
||||
QLIST_HEAD(, NBDConn) conns;
|
||||
} NBDServerData;
|
||||
|
||||
static NBDServerData *nbd_server;
|
||||
@@ -57,14 +51,6 @@ int nbd_server_max_connections(void)
|
||||
|
||||
static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
|
||||
{
|
||||
NBDConn *conn = nbd_client_owner(client);
|
||||
|
||||
assert(qemu_in_main_thread() && nbd_server);
|
||||
|
||||
object_unref(OBJECT(conn->cioc));
|
||||
QLIST_REMOVE(conn, next);
|
||||
g_free(conn);
|
||||
|
||||
nbd_client_put(client);
|
||||
assert(nbd_server->connections > 0);
|
||||
nbd_server->connections--;
|
||||
@@ -74,56 +60,31 @@ static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
|
||||
static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
|
||||
gpointer opaque)
|
||||
{
|
||||
NBDConn *conn = g_new0(NBDConn, 1);
|
||||
|
||||
assert(qemu_in_main_thread() && nbd_server);
|
||||
nbd_server->connections++;
|
||||
object_ref(OBJECT(cioc));
|
||||
conn->cioc = cioc;
|
||||
QLIST_INSERT_HEAD(&nbd_server->conns, conn, next);
|
||||
nbd_update_server_watch(nbd_server);
|
||||
|
||||
qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
|
||||
/* TODO - expose handshake timeout as QMP option */
|
||||
nbd_client_new(cioc, NBD_DEFAULT_HANDSHAKE_MAX_SECS,
|
||||
nbd_server->tlscreds, nbd_server->tlsauthz,
|
||||
nbd_blockdev_client_closed, conn);
|
||||
nbd_client_new(cioc, nbd_server->tlscreds, nbd_server->tlsauthz,
|
||||
nbd_blockdev_client_closed);
|
||||
}
|
||||
|
||||
static void nbd_update_server_watch(NBDServerData *s)
|
||||
{
|
||||
if (s->listener) {
|
||||
if (!s->max_connections || s->connections < s->max_connections) {
|
||||
qio_net_listener_set_client_func(s->listener, nbd_accept, NULL,
|
||||
NULL);
|
||||
} else {
|
||||
qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
|
||||
}
|
||||
if (!s->max_connections || s->connections < s->max_connections) {
|
||||
qio_net_listener_set_client_func(s->listener, nbd_accept, NULL, NULL);
|
||||
} else {
|
||||
qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void nbd_server_free(NBDServerData *server)
|
||||
{
|
||||
NBDConn *conn, *tmp;
|
||||
|
||||
if (!server) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Forcefully close the listener socket, and any clients that have
|
||||
* not yet disconnected on their own.
|
||||
*/
|
||||
qio_net_listener_disconnect(server->listener);
|
||||
object_unref(OBJECT(server->listener));
|
||||
server->listener = NULL;
|
||||
QLIST_FOREACH_SAFE(conn, &server->conns, next, tmp) {
|
||||
qio_channel_shutdown(QIO_CHANNEL(conn->cioc), QIO_CHANNEL_SHUTDOWN_BOTH,
|
||||
NULL);
|
||||
}
|
||||
|
||||
AIO_WAIT_WHILE_UNLOCKED(NULL, server->connections > 0);
|
||||
|
||||
if (server->tlscreds) {
|
||||
object_unref(OBJECT(server->tlscreds));
|
||||
}
|
||||
@@ -207,10 +168,6 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds,
|
||||
|
||||
void nbd_server_start_options(NbdServerOptions *arg, Error **errp)
|
||||
{
|
||||
if (!arg->has_max_connections) {
|
||||
arg->max_connections = NBD_DEFAULT_MAX_CONNECTIONS;
|
||||
}
|
||||
|
||||
nbd_server_start(arg->addr, arg->tls_creds, arg->tls_authz,
|
||||
arg->max_connections, errp);
|
||||
}
|
||||
@@ -223,10 +180,6 @@ void qmp_nbd_server_start(SocketAddressLegacy *addr,
|
||||
{
|
||||
SocketAddress *addr_flat = socket_address_flatten(addr);
|
||||
|
||||
if (!has_max_connections) {
|
||||
max_connections = NBD_DEFAULT_MAX_CONNECTIONS;
|
||||
}
|
||||
|
||||
nbd_server_start(addr_flat, tls_creds, tls_authz, max_connections, errp);
|
||||
qapi_free_SocketAddress(addr_flat);
|
||||
}
|
||||
|
||||
+3
-3
@@ -1395,8 +1395,7 @@ static void external_snapshot_action(TransactionAction *action,
|
||||
bdrv_drained_begin(state->old_bs);
|
||||
|
||||
if (!bdrv_is_inserted(state->old_bs)) {
|
||||
error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM,
|
||||
bdrv_get_device_or_node_name(state->old_bs));
|
||||
error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2253,7 +2252,8 @@ void coroutine_fn qmp_block_resize(const char *device, const char *node_name,
|
||||
}
|
||||
|
||||
bdrv_graph_co_rdlock();
|
||||
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_RESIZE, errp)) {
|
||||
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_RESIZE, NULL)) {
|
||||
error_setg(errp, QERR_DEVICE_IN_USE, device);
|
||||
bdrv_graph_co_rdunlock();
|
||||
return;
|
||||
}
|
||||
|
||||
+1
-1
@@ -641,7 +641,7 @@ static abi_long do_bsd_readlink(CPUArchState *env, abi_long arg1,
|
||||
}
|
||||
if (strcmp(p1, "/proc/curproc/file") == 0) {
|
||||
CPUState *cpu = env_cpu(env);
|
||||
TaskState *ts = get_task_state(cpu);
|
||||
TaskState *ts = (TaskState *)cpu->opaque;
|
||||
strncpy(p2, ts->bprm->fullpath, arg3);
|
||||
ret = MIN((abi_long)strlen(ts->bprm->fullpath), arg3);
|
||||
} else {
|
||||
|
||||
@@ -208,7 +208,7 @@ static inline abi_long do_freebsd_fork(void *cpu_env)
|
||||
*/
|
||||
set_second_rval(cpu_env, child_flag);
|
||||
|
||||
fork_end(ret);
|
||||
fork_end(child_flag);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -252,7 +252,7 @@ static inline abi_long do_freebsd_rfork(void *cpu_env, abi_long flags)
|
||||
* value: 0 for parent process, 1 for child process.
|
||||
*/
|
||||
set_second_rval(cpu_env, child_flag);
|
||||
fork_end(ret);
|
||||
fork_end(child_flag);
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -285,7 +285,7 @@ static inline abi_long do_freebsd_pdfork(void *cpu_env, abi_ulong target_fdp,
|
||||
* value: 0 for parent process, 1 for child process.
|
||||
*/
|
||||
set_second_rval(cpu_env, child_flag);
|
||||
fork_end(ret);
|
||||
fork_end(child_flag);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
+3
-8
@@ -113,13 +113,10 @@ void fork_start(void)
|
||||
start_exclusive();
|
||||
cpu_list_lock();
|
||||
mmap_fork_start();
|
||||
gdbserver_fork_start();
|
||||
}
|
||||
|
||||
void fork_end(pid_t pid)
|
||||
void fork_end(int child)
|
||||
{
|
||||
bool child = pid == 0;
|
||||
|
||||
if (child) {
|
||||
CPUState *cpu, *next_cpu;
|
||||
/*
|
||||
@@ -137,12 +134,10 @@ void fork_end(pid_t pid)
|
||||
* state, so we don't need to end_exclusive() here.
|
||||
*/
|
||||
qemu_init_cpu_list();
|
||||
get_task_state(thread_cpu)->ts_tid = qemu_get_thread_id();
|
||||
gdbserver_fork_end(thread_cpu, pid);
|
||||
gdbserver_fork(thread_cpu);
|
||||
} else {
|
||||
mmap_fork_end(child);
|
||||
cpu_list_unlock();
|
||||
gdbserver_fork_end(thread_cpu, pid);
|
||||
end_exclusive();
|
||||
}
|
||||
}
|
||||
@@ -606,7 +601,7 @@ int main(int argc, char **argv)
|
||||
|
||||
if (gdbstub) {
|
||||
gdbserver_start(gdbstub);
|
||||
gdb_handlesig(cpu, 0, NULL, NULL, 0);
|
||||
gdb_handlesig(cpu, 0);
|
||||
}
|
||||
cpu_loop(env);
|
||||
/* never exits */
|
||||
|
||||
+1
-6
@@ -117,11 +117,6 @@ typedef struct TaskState {
|
||||
struct target_sigaltstack sigaltstack_used;
|
||||
} __attribute__((aligned(16))) TaskState;
|
||||
|
||||
static inline TaskState *get_task_state(CPUState *cs)
|
||||
{
|
||||
return cs->opaque;
|
||||
}
|
||||
|
||||
void stop_all_tasks(void);
|
||||
extern const char *interp_prefix;
|
||||
extern const char *qemu_uname_release;
|
||||
@@ -192,7 +187,7 @@ void cpu_loop(CPUArchState *env);
|
||||
char *target_strerror(int err);
|
||||
int get_osversion(void);
|
||||
void fork_start(void);
|
||||
void fork_end(pid_t pid);
|
||||
void fork_end(int child);
|
||||
|
||||
#include "qemu/log.h"
|
||||
|
||||
|
||||
+13
-15
@@ -27,9 +27,6 @@
|
||||
#include "hw/core/tcg-cpu-ops.h"
|
||||
#include "host-signal.h"
|
||||
|
||||
/* target_siginfo_t must fit in gdbstub's siginfo save area. */
|
||||
QEMU_BUILD_BUG_ON(sizeof(target_siginfo_t) > MAX_SIGINFO_LENGTH);
|
||||
|
||||
static struct target_sigaction sigact_table[TARGET_NSIG];
|
||||
static void host_signal_handler(int host_sig, siginfo_t *info, void *puc);
|
||||
static void target_to_host_sigset_internal(sigset_t *d,
|
||||
@@ -322,7 +319,7 @@ void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info)
|
||||
|
||||
int block_signals(void)
|
||||
{
|
||||
TaskState *ts = get_task_state(thread_cpu);
|
||||
TaskState *ts = (TaskState *)thread_cpu->opaque;
|
||||
sigset_t set;
|
||||
|
||||
/*
|
||||
@@ -362,7 +359,7 @@ void dump_core_and_abort(int target_sig)
|
||||
{
|
||||
CPUState *cpu = thread_cpu;
|
||||
CPUArchState *env = cpu_env(cpu);
|
||||
TaskState *ts = get_task_state(cpu);
|
||||
TaskState *ts = cpu->opaque;
|
||||
int core_dumped = 0;
|
||||
int host_sig;
|
||||
struct sigaction act;
|
||||
@@ -424,7 +421,7 @@ void queue_signal(CPUArchState *env, int sig, int si_type,
|
||||
target_siginfo_t *info)
|
||||
{
|
||||
CPUState *cpu = env_cpu(env);
|
||||
TaskState *ts = get_task_state(cpu);
|
||||
TaskState *ts = cpu->opaque;
|
||||
|
||||
trace_user_queue_signal(env, sig);
|
||||
|
||||
@@ -466,19 +463,20 @@ static int fatal_signal(int sig)
|
||||
void force_sig_fault(int sig, int code, abi_ulong addr)
|
||||
{
|
||||
CPUState *cpu = thread_cpu;
|
||||
CPUArchState *env = cpu_env(cpu);
|
||||
target_siginfo_t info = {};
|
||||
|
||||
info.si_signo = sig;
|
||||
info.si_errno = 0;
|
||||
info.si_code = code;
|
||||
info.si_addr = addr;
|
||||
queue_signal(cpu_env(cpu), sig, QEMU_SI_FAULT, &info);
|
||||
queue_signal(env, sig, QEMU_SI_FAULT, &info);
|
||||
}
|
||||
|
||||
static void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
|
||||
{
|
||||
CPUState *cpu = thread_cpu;
|
||||
TaskState *ts = get_task_state(cpu);
|
||||
TaskState *ts = cpu->opaque;
|
||||
target_siginfo_t tinfo;
|
||||
ucontext_t *uc = puc;
|
||||
struct emulated_sigtable *k;
|
||||
@@ -587,7 +585,7 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
|
||||
/* compare to kern/kern_sig.c sys_sigaltstack() and kern_sigaltstack() */
|
||||
abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp)
|
||||
{
|
||||
TaskState *ts = get_task_state(thread_cpu);
|
||||
TaskState *ts = (TaskState *)thread_cpu->opaque;
|
||||
int ret;
|
||||
target_stack_t oss;
|
||||
|
||||
@@ -716,7 +714,7 @@ int do_sigaction(int sig, const struct target_sigaction *act,
|
||||
static inline abi_ulong get_sigframe(struct target_sigaction *ka,
|
||||
CPUArchState *env, size_t frame_size)
|
||||
{
|
||||
TaskState *ts = get_task_state(thread_cpu);
|
||||
TaskState *ts = (TaskState *)thread_cpu->opaque;
|
||||
abi_ulong sp;
|
||||
|
||||
/* Use default user stack */
|
||||
@@ -791,7 +789,7 @@ static int reset_signal_mask(target_ucontext_t *ucontext)
|
||||
int i;
|
||||
sigset_t blocked;
|
||||
target_sigset_t target_set;
|
||||
TaskState *ts = get_task_state(thread_cpu);
|
||||
TaskState *ts = (TaskState *)thread_cpu->opaque;
|
||||
|
||||
for (i = 0; i < TARGET_NSIG_WORDS; i++) {
|
||||
__get_user(target_set.__bits[i], &ucontext->uc_sigmask.__bits[i]);
|
||||
@@ -841,7 +839,7 @@ badframe:
|
||||
|
||||
void signal_init(void)
|
||||
{
|
||||
TaskState *ts = get_task_state(thread_cpu);
|
||||
TaskState *ts = (TaskState *)thread_cpu->opaque;
|
||||
struct sigaction act;
|
||||
struct sigaction oact;
|
||||
int i;
|
||||
@@ -880,7 +878,7 @@ static void handle_pending_signal(CPUArchState *env, int sig,
|
||||
struct emulated_sigtable *k)
|
||||
{
|
||||
CPUState *cpu = env_cpu(env);
|
||||
TaskState *ts = get_task_state(cpu);
|
||||
TaskState *ts = cpu->opaque;
|
||||
struct target_sigaction *sa;
|
||||
int code;
|
||||
sigset_t set;
|
||||
@@ -892,7 +890,7 @@ static void handle_pending_signal(CPUArchState *env, int sig,
|
||||
|
||||
k->pending = 0;
|
||||
|
||||
sig = gdb_handlesig(cpu, sig, NULL, &k->info, sizeof(k->info));
|
||||
sig = gdb_handlesig(cpu, sig);
|
||||
if (!sig) {
|
||||
sa = NULL;
|
||||
handler = TARGET_SIG_IGN;
|
||||
@@ -969,7 +967,7 @@ void process_pending_signals(CPUArchState *env)
|
||||
int sig;
|
||||
sigset_t *blocked_set, set;
|
||||
struct emulated_sigtable *k;
|
||||
TaskState *ts = get_task_state(cpu);
|
||||
TaskState *ts = cpu->opaque;
|
||||
|
||||
while (qatomic_read(&ts->signal_pending)) {
|
||||
sigfillset(&set);
|
||||
|
||||
+6
-7
@@ -199,18 +199,13 @@ bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp)
|
||||
MuxChardev *d = MUX_CHARDEV(s);
|
||||
|
||||
if (d->mux_cnt >= MAX_MUX) {
|
||||
error_setg(errp,
|
||||
"too many uses of multiplexed chardev '%s'"
|
||||
" (maximum is " stringify(MAX_MUX) ")",
|
||||
s->label);
|
||||
return false;
|
||||
goto unavailable;
|
||||
}
|
||||
|
||||
d->backends[d->mux_cnt] = b;
|
||||
tag = d->mux_cnt++;
|
||||
} else if (s->be) {
|
||||
error_setg(errp, "chardev '%s' is already in use", s->label);
|
||||
return false;
|
||||
goto unavailable;
|
||||
} else {
|
||||
s->be = b;
|
||||
}
|
||||
@@ -220,6 +215,10 @@ bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp)
|
||||
b->tag = tag;
|
||||
b->chr = s;
|
||||
return true;
|
||||
|
||||
unavailable:
|
||||
error_setg(errp, QERR_DEVICE_IN_USE, s->label);
|
||||
return false;
|
||||
}
|
||||
|
||||
void qemu_chr_fe_deinit(CharBackend *b, bool del)
|
||||
|
||||
+5
-42
@@ -33,7 +33,6 @@ typedef struct IOWatchPoll {
|
||||
IOCanReadHandler *fd_can_read;
|
||||
GSourceFunc fd_read;
|
||||
void *opaque;
|
||||
GMainContext *context;
|
||||
} IOWatchPoll;
|
||||
|
||||
static IOWatchPoll *io_watch_poll_from_source(GSource *source)
|
||||
@@ -51,55 +50,28 @@ static gboolean io_watch_poll_prepare(GSource *source,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* We do not register the QIOChannel watch as a child GSource.
|
||||
* The 'prepare' function on the parent GSource will be
|
||||
* skipped if a child GSource's 'prepare' function indicates
|
||||
* readiness. We need this prepare function be guaranteed
|
||||
* to run on *every* iteration of the main loop, because
|
||||
* it is critical to ensure we remove the QIOChannel watch
|
||||
* if 'fd_can_read' indicates the frontend cannot receive
|
||||
* more data.
|
||||
*/
|
||||
if (now_active) {
|
||||
iwp->src = qio_channel_create_watch(
|
||||
iwp->ioc, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL);
|
||||
g_source_set_callback(iwp->src, iwp->fd_read, iwp->opaque, NULL);
|
||||
g_source_attach(iwp->src, iwp->context);
|
||||
} else {
|
||||
g_source_destroy(iwp->src);
|
||||
g_source_add_child_source(source, iwp->src);
|
||||
g_source_unref(iwp->src);
|
||||
} else {
|
||||
g_source_remove_child_source(source, iwp->src);
|
||||
iwp->src = NULL;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean io_watch_poll_check(GSource *source)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean io_watch_poll_dispatch(GSource *source, GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
static void io_watch_poll_finalize(GSource *source)
|
||||
{
|
||||
IOWatchPoll *iwp = io_watch_poll_from_source(source);
|
||||
if (iwp->src) {
|
||||
g_source_destroy(iwp->src);
|
||||
g_source_unref(iwp->src);
|
||||
iwp->src = NULL;
|
||||
}
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static GSourceFuncs io_watch_poll_funcs = {
|
||||
.prepare = io_watch_poll_prepare,
|
||||
.check = io_watch_poll_check,
|
||||
.dispatch = io_watch_poll_dispatch,
|
||||
.finalize = io_watch_poll_finalize,
|
||||
};
|
||||
|
||||
GSource *io_add_watch_poll(Chardev *chr,
|
||||
@@ -119,7 +91,6 @@ GSource *io_add_watch_poll(Chardev *chr,
|
||||
iwp->ioc = ioc;
|
||||
iwp->fd_read = (GSourceFunc) fd_read;
|
||||
iwp->src = NULL;
|
||||
iwp->context = context;
|
||||
|
||||
name = g_strdup_printf("chardev-iowatch-%s", chr->label);
|
||||
g_source_set_name((GSource *)iwp, name);
|
||||
@@ -130,18 +101,10 @@ GSource *io_add_watch_poll(Chardev *chr,
|
||||
return (GSource *)iwp;
|
||||
}
|
||||
|
||||
static void io_remove_watch_poll(GSource *source)
|
||||
{
|
||||
IOWatchPoll *iwp;
|
||||
|
||||
iwp = io_watch_poll_from_source(source);
|
||||
g_source_destroy(&iwp->parent);
|
||||
}
|
||||
|
||||
void remove_fd_in_watch(Chardev *chr)
|
||||
{
|
||||
if (chr->gsource) {
|
||||
io_remove_watch_poll(chr->gsource);
|
||||
g_source_destroy(chr->gsource);
|
||||
chr->gsource = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-19
@@ -496,9 +496,9 @@ static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
|
||||
s->max_size <= 0) {
|
||||
return TRUE;
|
||||
}
|
||||
len = sizeof(buf);
|
||||
if (len > s->max_size) {
|
||||
len = s->max_size;
|
||||
len = tcp_chr_read_poll(opaque);
|
||||
if (len > sizeof(buf)) {
|
||||
len = sizeof(buf);
|
||||
}
|
||||
size = tcp_chr_recv(chr, (void *)buf, len);
|
||||
if (size == 0 || (size == -1 && errno != EAGAIN)) {
|
||||
@@ -601,22 +601,6 @@ static void update_ioc_handlers(SocketChardev *s)
|
||||
|
||||
remove_hup_source(s);
|
||||
s->hup_source = qio_channel_create_watch(s->ioc, G_IO_HUP);
|
||||
/*
|
||||
* poll() is liable to return POLLHUP even when there is
|
||||
* still incoming data available to read on the FD. If
|
||||
* we have the hup_source at the same priority as the
|
||||
* main io_add_watch_poll GSource, then we might end up
|
||||
* processing the POLLHUP event first, closing the FD,
|
||||
* and as a result silently discard data we should have
|
||||
* read.
|
||||
*
|
||||
* By setting the hup_source to G_PRIORITY_DEFAULT + 1,
|
||||
* we ensure that io_add_watch_poll GSource will always
|
||||
* be dispatched first, thus guaranteeing we will be
|
||||
* able to process all incoming data before closing the
|
||||
* FD
|
||||
*/
|
||||
g_source_set_priority(s->hup_source, G_PRIORITY_DEFAULT + 1);
|
||||
g_source_set_callback(s->hup_source, (GSourceFunc)tcp_chr_hup,
|
||||
chr, NULL);
|
||||
g_source_attach(s->hup_source, chr->gcontext);
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
/* init terminal so that we can grab keys */
|
||||
static struct termios oldtty;
|
||||
static int old_fd0_flags;
|
||||
static int old_fd1_flags;
|
||||
static bool stdio_in_use;
|
||||
static bool stdio_allow_signal;
|
||||
static bool stdio_echo_state;
|
||||
@@ -51,8 +50,6 @@ static void term_exit(void)
|
||||
if (stdio_in_use) {
|
||||
tcsetattr(0, TCSANOW, &oldtty);
|
||||
fcntl(0, F_SETFL, old_fd0_flags);
|
||||
fcntl(1, F_SETFL, old_fd1_flags);
|
||||
stdio_in_use = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +102,6 @@ static void qemu_chr_open_stdio(Chardev *chr,
|
||||
|
||||
stdio_in_use = true;
|
||||
old_fd0_flags = fcntl(0, F_GETFL);
|
||||
old_fd1_flags = fcntl(1, F_GETFL);
|
||||
tcgetattr(0, &oldtty);
|
||||
if (!g_unix_set_fd_nonblocking(0, true, NULL)) {
|
||||
error_setg_errno(errp, errno, "Failed to set FD nonblocking");
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
struct WinStdioChardev {
|
||||
Chardev parent;
|
||||
HANDLE hStdIn;
|
||||
DWORD dwOldMode;
|
||||
HANDLE hInputReadyEvent;
|
||||
HANDLE hInputDoneEvent;
|
||||
HANDLE hInputThread;
|
||||
@@ -160,7 +159,6 @@ static void qemu_chr_open_stdio(Chardev *chr,
|
||||
}
|
||||
|
||||
is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0;
|
||||
stdio->dwOldMode = dwMode;
|
||||
|
||||
if (is_console) {
|
||||
if (qemu_add_wait_object(stdio->hStdIn,
|
||||
@@ -223,9 +221,6 @@ static void char_win_stdio_finalize(Object *obj)
|
||||
{
|
||||
WinStdioChardev *stdio = WIN_STDIO_CHARDEV(obj);
|
||||
|
||||
if (stdio->hStdIn != INVALID_HANDLE_VALUE) {
|
||||
SetConsoleMode(stdio->hStdIn, stdio->dwOldMode);
|
||||
}
|
||||
if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(stdio->hInputReadyEvent);
|
||||
}
|
||||
|
||||
@@ -411,9 +411,7 @@ else
|
||||
# Using uname is really broken, but it is just a fallback for architectures
|
||||
# that are going to use TCI anyway
|
||||
cpu=$(uname -m)
|
||||
if test "$host_os" != "bogus"; then
|
||||
echo "WARNING: unrecognized host CPU, proceeding with 'uname -m' output '$cpu'"
|
||||
fi
|
||||
echo "WARNING: unrecognized host CPU, proceeding with 'uname -m' output '$cpu'"
|
||||
fi
|
||||
|
||||
# Normalise host CPU name to the values used by Meson cross files and in source
|
||||
@@ -764,7 +762,7 @@ for opt do
|
||||
--*) meson_option_parse "$opt" "$optarg"
|
||||
;;
|
||||
# Pass through -Dxxxx options to meson
|
||||
-D*) meson_option_add "$opt"
|
||||
-D*) meson_options="$meson_options $opt"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
@@ -896,13 +894,6 @@ EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Now that we are sure that the user did not only want to print the --help
|
||||
# information, we should double-check that the C compiler really works:
|
||||
write_c_skeleton
|
||||
if ! compile_object ; then
|
||||
error_exit "C compiler \"$cc\" either does not exist or does not work."
|
||||
fi
|
||||
|
||||
# Remove old dependency files to make sure that they get properly regenerated
|
||||
rm -f ./*/config-devices.mak.d
|
||||
|
||||
|
||||
+26
-37
@@ -22,7 +22,7 @@ static struct pa_block *pa_space_find_block(struct pa_space *ps, uint64_t pa)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *pa_space_resolve(struct pa_space *ps, uint64_t pa)
|
||||
static uint8_t *pa_space_resolve(struct pa_space *ps, uint64_t pa)
|
||||
{
|
||||
struct pa_block *block = pa_space_find_block(ps, pa);
|
||||
|
||||
@@ -33,19 +33,6 @@ static void *pa_space_resolve(struct pa_space *ps, uint64_t pa)
|
||||
return block->addr + (pa - block->paddr);
|
||||
}
|
||||
|
||||
static bool pa_space_read64(struct pa_space *ps, uint64_t pa, uint64_t *value)
|
||||
{
|
||||
uint64_t *resolved = pa_space_resolve(ps, pa);
|
||||
|
||||
if (!resolved) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*value = *resolved;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void pa_block_align(struct pa_block *b)
|
||||
{
|
||||
uint64_t low_align = ((b->paddr - 1) | ELF2DMP_PAGE_MASK) + 1 - b->paddr;
|
||||
@@ -70,7 +57,7 @@ static void pa_block_align(struct pa_block *b)
|
||||
b->paddr += low_align;
|
||||
}
|
||||
|
||||
void pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf)
|
||||
int pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf)
|
||||
{
|
||||
Elf64_Half phdr_nr = elf_getphdrnum(qemu_elf->map);
|
||||
Elf64_Phdr *phdr = elf64_getphdr(qemu_elf->map);
|
||||
@@ -88,12 +75,11 @@ void pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf)
|
||||
ps->block = g_new(struct pa_block, ps->block_nr);
|
||||
|
||||
for (i = 0; i < phdr_nr; i++) {
|
||||
if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset < qemu_elf->size) {
|
||||
if (phdr[i].p_type == PT_LOAD) {
|
||||
ps->block[block_i] = (struct pa_block) {
|
||||
.addr = (uint8_t *)qemu_elf->map + phdr[i].p_offset,
|
||||
.paddr = phdr[i].p_paddr,
|
||||
.size = MIN(phdr[i].p_filesz,
|
||||
qemu_elf->size - phdr[i].p_offset),
|
||||
.size = phdr[i].p_filesz,
|
||||
};
|
||||
pa_block_align(&ps->block[block_i]);
|
||||
block_i = ps->block[block_i].size ? (block_i + 1) : block_i;
|
||||
@@ -101,6 +87,8 @@ void pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf)
|
||||
}
|
||||
|
||||
ps->block_nr = block_i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pa_space_destroy(struct pa_space *ps)
|
||||
@@ -120,20 +108,19 @@ void va_space_create(struct va_space *vs, struct pa_space *ps, uint64_t dtb)
|
||||
va_space_set_dtb(vs, dtb);
|
||||
}
|
||||
|
||||
static bool get_pml4e(struct va_space *vs, uint64_t va, uint64_t *value)
|
||||
static uint64_t get_pml4e(struct va_space *vs, uint64_t va)
|
||||
{
|
||||
uint64_t pa = (vs->dtb & 0xffffffffff000) | ((va & 0xff8000000000) >> 36);
|
||||
|
||||
return pa_space_read64(vs->ps, pa, value);
|
||||
return *(uint64_t *)pa_space_resolve(vs->ps, pa);
|
||||
}
|
||||
|
||||
static bool get_pdpi(struct va_space *vs, uint64_t va, uint64_t pml4e,
|
||||
uint64_t *value)
|
||||
static uint64_t get_pdpi(struct va_space *vs, uint64_t va, uint64_t pml4e)
|
||||
{
|
||||
uint64_t pdpte_paddr = (pml4e & 0xffffffffff000) |
|
||||
((va & 0x7FC0000000) >> 27);
|
||||
|
||||
return pa_space_read64(vs->ps, pdpte_paddr, value);
|
||||
return *(uint64_t *)pa_space_resolve(vs->ps, pdpte_paddr);
|
||||
}
|
||||
|
||||
static uint64_t pde_index(uint64_t va)
|
||||
@@ -146,12 +133,11 @@ static uint64_t pdba_base(uint64_t pdpe)
|
||||
return pdpe & 0xFFFFFFFFFF000;
|
||||
}
|
||||
|
||||
static bool get_pgd(struct va_space *vs, uint64_t va, uint64_t pdpe,
|
||||
uint64_t *value)
|
||||
static uint64_t get_pgd(struct va_space *vs, uint64_t va, uint64_t pdpe)
|
||||
{
|
||||
uint64_t pgd_entry = pdba_base(pdpe) + pde_index(va) * 8;
|
||||
|
||||
return pa_space_read64(vs->ps, pgd_entry, value);
|
||||
return *(uint64_t *)pa_space_resolve(vs->ps, pgd_entry);
|
||||
}
|
||||
|
||||
static uint64_t pte_index(uint64_t va)
|
||||
@@ -164,12 +150,11 @@ static uint64_t ptba_base(uint64_t pde)
|
||||
return pde & 0xFFFFFFFFFF000;
|
||||
}
|
||||
|
||||
static bool get_pte(struct va_space *vs, uint64_t va, uint64_t pgd,
|
||||
uint64_t *value)
|
||||
static uint64_t get_pte(struct va_space *vs, uint64_t va, uint64_t pgd)
|
||||
{
|
||||
uint64_t pgd_val = ptba_base(pgd) + pte_index(va) * 8;
|
||||
|
||||
return pa_space_read64(vs->ps, pgd_val, value);
|
||||
return *(uint64_t *)pa_space_resolve(vs->ps, pgd_val);
|
||||
}
|
||||
|
||||
static uint64_t get_paddr(uint64_t va, uint64_t pte)
|
||||
@@ -201,11 +186,13 @@ static uint64_t va_space_va2pa(struct va_space *vs, uint64_t va)
|
||||
{
|
||||
uint64_t pml4e, pdpe, pgd, pte;
|
||||
|
||||
if (!get_pml4e(vs, va, &pml4e) || !is_present(pml4e)) {
|
||||
pml4e = get_pml4e(vs, va);
|
||||
if (!is_present(pml4e)) {
|
||||
return INVALID_PA;
|
||||
}
|
||||
|
||||
if (!get_pdpi(vs, va, pml4e, &pdpe) || !is_present(pdpe)) {
|
||||
pdpe = get_pdpi(vs, va, pml4e);
|
||||
if (!is_present(pdpe)) {
|
||||
return INVALID_PA;
|
||||
}
|
||||
|
||||
@@ -213,7 +200,8 @@ static uint64_t va_space_va2pa(struct va_space *vs, uint64_t va)
|
||||
return get_1GB_paddr(va, pdpe);
|
||||
}
|
||||
|
||||
if (!get_pgd(vs, va, pdpe, &pgd) || !is_present(pgd)) {
|
||||
pgd = get_pgd(vs, va, pdpe);
|
||||
if (!is_present(pgd)) {
|
||||
return INVALID_PA;
|
||||
}
|
||||
|
||||
@@ -221,7 +209,8 @@ static uint64_t va_space_va2pa(struct va_space *vs, uint64_t va)
|
||||
return get_2MB_paddr(va, pgd);
|
||||
}
|
||||
|
||||
if (!get_pte(vs, va, pgd, &pte) || !is_present(pte)) {
|
||||
pte = get_pte(vs, va, pgd);
|
||||
if (!is_present(pte)) {
|
||||
return INVALID_PA;
|
||||
}
|
||||
|
||||
@@ -239,8 +228,8 @@ void *va_space_resolve(struct va_space *vs, uint64_t va)
|
||||
return pa_space_resolve(vs->ps, pa);
|
||||
}
|
||||
|
||||
bool va_space_rw(struct va_space *vs, uint64_t addr,
|
||||
void *buf, size_t size, int is_write)
|
||||
int va_space_rw(struct va_space *vs, uint64_t addr,
|
||||
void *buf, size_t size, int is_write)
|
||||
{
|
||||
while (size) {
|
||||
uint64_t page = addr & ELF2DMP_PFN_MASK;
|
||||
@@ -251,7 +240,7 @@ bool va_space_rw(struct va_space *vs, uint64_t addr,
|
||||
|
||||
ptr = va_space_resolve(vs, addr);
|
||||
if (!ptr) {
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (is_write) {
|
||||
@@ -265,5 +254,5 @@ bool va_space_rw(struct va_space *vs, uint64_t addr,
|
||||
addr += s;
|
||||
}
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -33,13 +33,13 @@ struct va_space {
|
||||
struct pa_space *ps;
|
||||
};
|
||||
|
||||
void pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf);
|
||||
int pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf);
|
||||
void pa_space_destroy(struct pa_space *ps);
|
||||
|
||||
void va_space_create(struct va_space *vs, struct pa_space *ps, uint64_t dtb);
|
||||
void va_space_set_dtb(struct va_space *vs, uint64_t dtb);
|
||||
void *va_space_resolve(struct va_space *vs, uint64_t va);
|
||||
bool va_space_rw(struct va_space *vs, uint64_t addr,
|
||||
void *buf, size_t size, int is_write);
|
||||
int va_space_rw(struct va_space *vs, uint64_t addr,
|
||||
void *buf, size_t size, int is_write);
|
||||
|
||||
#endif /* ADDRSPACE_H */
|
||||
|
||||
@@ -9,18 +9,19 @@
|
||||
#include <curl/curl.h>
|
||||
#include "download.h"
|
||||
|
||||
bool download_url(const char *name, const char *url)
|
||||
int download_url(const char *name, const char *url)
|
||||
{
|
||||
bool success = false;
|
||||
int err = 0;
|
||||
FILE *file;
|
||||
CURL *curl = curl_easy_init();
|
||||
|
||||
if (!curl) {
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
file = fopen(name, "wb");
|
||||
if (!file) {
|
||||
err = 1;
|
||||
goto out_curl;
|
||||
}
|
||||
|
||||
@@ -32,12 +33,13 @@ bool download_url(const char *name, const char *url)
|
||||
|| curl_easy_perform(curl) != CURLE_OK) {
|
||||
unlink(name);
|
||||
fclose(file);
|
||||
err = 1;
|
||||
} else {
|
||||
success = !fclose(file);
|
||||
err = fclose(file);
|
||||
}
|
||||
|
||||
out_curl:
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
return success;
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
#ifndef DOWNLOAD_H
|
||||
#define DOWNLOAD_H
|
||||
|
||||
bool download_url(const char *name, const char *url);
|
||||
int download_url(const char *name, const char *url);
|
||||
|
||||
#endif /* DOWNLOAD_H */
|
||||
|
||||
+93
-75
@@ -6,7 +6,6 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/bitops.h"
|
||||
|
||||
#include "err.h"
|
||||
#include "addrspace.h"
|
||||
@@ -48,6 +47,11 @@ static const uint64_t SharedUserData = 0xfffff78000000000;
|
||||
s ? printf(#s" = 0x%016"PRIx64"\n", s) :\
|
||||
eprintf("Failed to resolve "#s"\n"), s)
|
||||
|
||||
static uint64_t rol(uint64_t x, uint64_t y)
|
||||
{
|
||||
return (x << y) | (x >> (64 - y));
|
||||
}
|
||||
|
||||
/*
|
||||
* Decoding algorithm can be found in Volatility project
|
||||
*/
|
||||
@@ -60,7 +64,7 @@ static void kdbg_decode(uint64_t *dst, uint64_t *src, size_t size,
|
||||
uint64_t block;
|
||||
|
||||
block = src[i];
|
||||
block = rol64(block ^ kwn, kwn);
|
||||
block = rol(block ^ kwn, (uint8_t)kwn);
|
||||
block = __builtin_bswap64(block ^ kdbe) ^ kwa;
|
||||
dst[i] = block;
|
||||
}
|
||||
@@ -75,9 +79,9 @@ static KDDEBUGGER_DATA64 *get_kdbg(uint64_t KernBase, struct pdb_reader *pdb,
|
||||
bool decode = false;
|
||||
uint64_t kwn, kwa, KdpDataBlockEncoded;
|
||||
|
||||
if (!va_space_rw(vs,
|
||||
KdDebuggerDataBlock + offsetof(KDDEBUGGER_DATA64, Header),
|
||||
&kdbg_hdr, sizeof(kdbg_hdr), 0)) {
|
||||
if (va_space_rw(vs,
|
||||
KdDebuggerDataBlock + offsetof(KDDEBUGGER_DATA64, Header),
|
||||
&kdbg_hdr, sizeof(kdbg_hdr), 0)) {
|
||||
eprintf("Failed to extract KDBG header\n");
|
||||
return NULL;
|
||||
}
|
||||
@@ -93,8 +97,8 @@ static KDDEBUGGER_DATA64 *get_kdbg(uint64_t KernBase, struct pdb_reader *pdb,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!va_space_rw(vs, KiWaitNever, &kwn, sizeof(kwn), 0) ||
|
||||
!va_space_rw(vs, KiWaitAlways, &kwa, sizeof(kwa), 0)) {
|
||||
if (va_space_rw(vs, KiWaitNever, &kwn, sizeof(kwn), 0) ||
|
||||
va_space_rw(vs, KiWaitAlways, &kwa, sizeof(kwa), 0)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -118,7 +122,7 @@ static KDDEBUGGER_DATA64 *get_kdbg(uint64_t KernBase, struct pdb_reader *pdb,
|
||||
|
||||
kdbg = g_malloc(kdbg_hdr.Size);
|
||||
|
||||
if (!va_space_rw(vs, KdDebuggerDataBlock, kdbg, kdbg_hdr.Size, 0)) {
|
||||
if (va_space_rw(vs, KdDebuggerDataBlock, kdbg, kdbg_hdr.Size, 0)) {
|
||||
eprintf("Failed to extract entire KDBG\n");
|
||||
g_free(kdbg);
|
||||
return NULL;
|
||||
@@ -182,13 +186,13 @@ static void win_context_init_from_qemu_cpu_state(WinContext64 *ctx,
|
||||
* Finds paging-structure hierarchy base,
|
||||
* if previously set doesn't give access to kernel structures
|
||||
*/
|
||||
static bool fix_dtb(struct va_space *vs, QEMU_Elf *qe)
|
||||
static int fix_dtb(struct va_space *vs, QEMU_Elf *qe)
|
||||
{
|
||||
/*
|
||||
* Firstly, test previously set DTB.
|
||||
*/
|
||||
if (va_space_resolve(vs, SharedUserData)) {
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -202,7 +206,7 @@ static bool fix_dtb(struct va_space *vs, QEMU_Elf *qe)
|
||||
va_space_set_dtb(vs, s->cr[3]);
|
||||
printf("DTB 0x%016"PRIx64" has been found from CPU #%zu"
|
||||
" as system task CR3\n", vs->dtb, i);
|
||||
return va_space_resolve(vs, SharedUserData);
|
||||
return !(va_space_resolve(vs, SharedUserData));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,16 +220,16 @@ static bool fix_dtb(struct va_space *vs, QEMU_Elf *qe)
|
||||
uint64_t *cr3 = va_space_resolve(vs, Prcb + 0x7000);
|
||||
|
||||
if (!cr3) {
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
va_space_set_dtb(vs, *cr3);
|
||||
printf("DirectoryTableBase = 0x%016"PRIx64" has been found from CPU #0"
|
||||
" as interrupt handling CR3\n", vs->dtb);
|
||||
return va_space_resolve(vs, SharedUserData);
|
||||
return !(va_space_resolve(vs, SharedUserData));
|
||||
}
|
||||
|
||||
return true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void try_merge_runs(struct pa_space *ps,
|
||||
@@ -264,10 +268,9 @@ static void try_merge_runs(struct pa_space *ps,
|
||||
}
|
||||
}
|
||||
|
||||
static bool fill_header(WinDumpHeader64 *hdr, struct pa_space *ps,
|
||||
struct va_space *vs, uint64_t KdDebuggerDataBlock,
|
||||
KDDEBUGGER_DATA64 *kdbg, uint64_t KdVersionBlock,
|
||||
int nr_cpus)
|
||||
static int fill_header(WinDumpHeader64 *hdr, struct pa_space *ps,
|
||||
struct va_space *vs, uint64_t KdDebuggerDataBlock,
|
||||
KDDEBUGGER_DATA64 *kdbg, uint64_t KdVersionBlock, int nr_cpus)
|
||||
{
|
||||
uint32_t *suite_mask = va_space_resolve(vs, SharedUserData +
|
||||
KUSD_OFFSET_SUITE_MASK);
|
||||
@@ -280,12 +283,12 @@ static bool fill_header(WinDumpHeader64 *hdr, struct pa_space *ps,
|
||||
QEMU_BUILD_BUG_ON(KUSD_OFFSET_PRODUCT_TYPE >= ELF2DMP_PAGE_SIZE);
|
||||
|
||||
if (!suite_mask || !product_type) {
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!va_space_rw(vs, KdVersionBlock, &kvb, sizeof(kvb), 0)) {
|
||||
if (va_space_rw(vs, KdVersionBlock, &kvb, sizeof(kvb), 0)) {
|
||||
eprintf("Failed to extract KdVersionBlock\n");
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
h = (WinDumpHeader64) {
|
||||
@@ -330,16 +333,11 @@ static bool fill_header(WinDumpHeader64 *hdr, struct pa_space *ps,
|
||||
|
||||
*hdr = h;
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* fill_context() continues even if it fails to fill contexts of some CPUs.
|
||||
* A dump may still contain valuable information even if it lacks contexts of
|
||||
* some CPUs due to dump corruption or a failure before starting CPUs.
|
||||
*/
|
||||
static void fill_context(KDDEBUGGER_DATA64 *kdbg,
|
||||
struct va_space *vs, QEMU_Elf *qe)
|
||||
static int fill_context(KDDEBUGGER_DATA64 *kdbg,
|
||||
struct va_space *vs, QEMU_Elf *qe)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -349,10 +347,10 @@ static void fill_context(KDDEBUGGER_DATA64 *kdbg,
|
||||
WinContext64 ctx;
|
||||
QEMUCPUState *s = qe->state[i];
|
||||
|
||||
if (!va_space_rw(vs, kdbg->KiProcessorBlock + sizeof(Prcb) * i,
|
||||
&Prcb, sizeof(Prcb), 0)) {
|
||||
if (va_space_rw(vs, kdbg->KiProcessorBlock + sizeof(Prcb) * i,
|
||||
&Prcb, sizeof(Prcb), 0)) {
|
||||
eprintf("Failed to read CPU #%d PRCB location\n", i);
|
||||
continue;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!Prcb) {
|
||||
@@ -360,24 +358,26 @@ static void fill_context(KDDEBUGGER_DATA64 *kdbg,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!va_space_rw(vs, Prcb + kdbg->OffsetPrcbContext,
|
||||
&Context, sizeof(Context), 0)) {
|
||||
if (va_space_rw(vs, Prcb + kdbg->OffsetPrcbContext,
|
||||
&Context, sizeof(Context), 0)) {
|
||||
eprintf("Failed to read CPU #%d ContextFrame location\n", i);
|
||||
continue;
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Filling context for CPU #%d...\n", i);
|
||||
win_context_init_from_qemu_cpu_state(&ctx, s);
|
||||
|
||||
if (!va_space_rw(vs, Context, &ctx, sizeof(ctx), 1)) {
|
||||
if (va_space_rw(vs, Context, &ctx, sizeof(ctx), 1)) {
|
||||
eprintf("Failed to fill CPU #%d context\n", i);
|
||||
continue;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool pe_get_data_dir_entry(uint64_t base, void *start_addr, int idx,
|
||||
void *entry, size_t size, struct va_space *vs)
|
||||
static int pe_get_data_dir_entry(uint64_t base, void *start_addr, int idx,
|
||||
void *entry, size_t size, struct va_space *vs)
|
||||
{
|
||||
const char e_magic[2] = "MZ";
|
||||
const char Signature[4] = "PE\0\0";
|
||||
@@ -390,38 +390,40 @@ static bool pe_get_data_dir_entry(uint64_t base, void *start_addr, int idx,
|
||||
QEMU_BUILD_BUG_ON(sizeof(*dos_hdr) >= ELF2DMP_PAGE_SIZE);
|
||||
|
||||
if (memcmp(&dos_hdr->e_magic, e_magic, sizeof(e_magic))) {
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!va_space_rw(vs, base + dos_hdr->e_lfanew,
|
||||
&nt_hdrs, sizeof(nt_hdrs), 0)) {
|
||||
return false;
|
||||
if (va_space_rw(vs, base + dos_hdr->e_lfanew,
|
||||
&nt_hdrs, sizeof(nt_hdrs), 0)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (memcmp(&nt_hdrs.Signature, Signature, sizeof(Signature)) ||
|
||||
file_hdr->Machine != 0x8664 || opt_hdr->Magic != 0x020b) {
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!va_space_rw(vs, base + data_dir[idx].VirtualAddress, entry, size, 0)) {
|
||||
return false;
|
||||
if (va_space_rw(vs,
|
||||
base + data_dir[idx].VirtualAddress,
|
||||
entry, size, 0)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Data directory entry #%d: RVA = 0x%08"PRIx32"\n", idx,
|
||||
(uint32_t)data_dir[idx].VirtualAddress);
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool write_dump(struct pa_space *ps,
|
||||
WinDumpHeader64 *hdr, const char *name)
|
||||
static int write_dump(struct pa_space *ps,
|
||||
WinDumpHeader64 *hdr, const char *name)
|
||||
{
|
||||
FILE *dmp_file = fopen(name, "wb");
|
||||
size_t i;
|
||||
|
||||
if (!dmp_file) {
|
||||
eprintf("Failed to open output file \'%s\'\n", name);
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Writing header to file...\n");
|
||||
@@ -429,7 +431,7 @@ static bool write_dump(struct pa_space *ps,
|
||||
if (fwrite(hdr, sizeof(*hdr), 1, dmp_file) != 1) {
|
||||
eprintf("Failed to write dump header\n");
|
||||
fclose(dmp_file);
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < ps->block_nr; i++) {
|
||||
@@ -440,11 +442,11 @@ static bool write_dump(struct pa_space *ps,
|
||||
if (fwrite(b->addr, b->size, 1, dmp_file) != 1) {
|
||||
eprintf("Failed to write block\n");
|
||||
fclose(dmp_file);
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return !fclose(dmp_file);
|
||||
return fclose(dmp_file);
|
||||
}
|
||||
|
||||
static bool pe_check_pdb_name(uint64_t base, void *start_addr,
|
||||
@@ -454,8 +456,8 @@ static bool pe_check_pdb_name(uint64_t base, void *start_addr,
|
||||
IMAGE_DEBUG_DIRECTORY debug_dir;
|
||||
char pdb_name[sizeof(PDB_NAME)];
|
||||
|
||||
if (!pe_get_data_dir_entry(base, start_addr, IMAGE_FILE_DEBUG_DIRECTORY,
|
||||
&debug_dir, sizeof(debug_dir), vs)) {
|
||||
if (pe_get_data_dir_entry(base, start_addr, IMAGE_FILE_DEBUG_DIRECTORY,
|
||||
&debug_dir, sizeof(debug_dir), vs)) {
|
||||
eprintf("Failed to get Debug Directory\n");
|
||||
return false;
|
||||
}
|
||||
@@ -465,8 +467,9 @@ static bool pe_check_pdb_name(uint64_t base, void *start_addr,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!va_space_rw(vs, base + debug_dir.AddressOfRawData,
|
||||
rsds, sizeof(*rsds), 0)) {
|
||||
if (va_space_rw(vs,
|
||||
base + debug_dir.AddressOfRawData,
|
||||
rsds, sizeof(*rsds), 0)) {
|
||||
eprintf("Failed to resolve OMFSignatureRSDS\n");
|
||||
return false;
|
||||
}
|
||||
@@ -482,9 +485,9 @@ static bool pe_check_pdb_name(uint64_t base, void *start_addr,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!va_space_rw(vs, base + debug_dir.AddressOfRawData +
|
||||
offsetof(OMFSignatureRSDS, name),
|
||||
pdb_name, sizeof(PDB_NAME), 0)) {
|
||||
if (va_space_rw(vs, base + debug_dir.AddressOfRawData +
|
||||
offsetof(OMFSignatureRSDS, name), pdb_name, sizeof(PDB_NAME),
|
||||
0)) {
|
||||
eprintf("Failed to resolve PDB name\n");
|
||||
return false;
|
||||
}
|
||||
@@ -508,7 +511,7 @@ static void pe_get_pdb_symstore_hash(OMFSignatureRSDS *rsds, char *hash)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int err = 1;
|
||||
int err = 0;
|
||||
QEMU_Elf qemu_elf;
|
||||
struct pa_space ps;
|
||||
struct va_space vs;
|
||||
@@ -532,27 +535,33 @@ int main(int argc, char *argv[])
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!QEMU_Elf_init(&qemu_elf, argv[1])) {
|
||||
if (QEMU_Elf_init(&qemu_elf, argv[1])) {
|
||||
eprintf("Failed to initialize QEMU ELF dump\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
pa_space_create(&ps, &qemu_elf);
|
||||
if (pa_space_create(&ps, &qemu_elf)) {
|
||||
eprintf("Failed to initialize physical address space\n");
|
||||
err = 1;
|
||||
goto out_elf;
|
||||
}
|
||||
|
||||
state = qemu_elf.state[0];
|
||||
printf("CPU #0 CR3 is 0x%016"PRIx64"\n", state->cr[3]);
|
||||
|
||||
va_space_create(&vs, &ps, state->cr[3]);
|
||||
if (!fix_dtb(&vs, &qemu_elf)) {
|
||||
if (fix_dtb(&vs, &qemu_elf)) {
|
||||
eprintf("Failed to find paging base\n");
|
||||
goto out_ps;
|
||||
err = 1;
|
||||
goto out_elf;
|
||||
}
|
||||
|
||||
printf("CPU #0 IDT is at 0x%016"PRIx64"\n", state->idt.base);
|
||||
|
||||
if (!va_space_rw(&vs, state->idt.base,
|
||||
&first_idt_desc, sizeof(first_idt_desc), 0)) {
|
||||
if (va_space_rw(&vs, state->idt.base,
|
||||
&first_idt_desc, sizeof(first_idt_desc), 0)) {
|
||||
eprintf("Failed to get CPU #0 IDT[0]\n");
|
||||
err = 1;
|
||||
goto out_ps;
|
||||
}
|
||||
printf("CPU #0 IDT[0] -> 0x%016"PRIx64"\n", idt_desc_addr(first_idt_desc));
|
||||
@@ -577,6 +586,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (!kernel_found) {
|
||||
eprintf("Failed to find NT kernel image\n");
|
||||
err = 1;
|
||||
goto out_ps;
|
||||
}
|
||||
|
||||
@@ -588,40 +598,47 @@ int main(int argc, char *argv[])
|
||||
sprintf(pdb_url, "%s%s/%s/%s", SYM_URL_BASE, PDB_NAME, pdb_hash, PDB_NAME);
|
||||
printf("PDB URL is %s\n", pdb_url);
|
||||
|
||||
if (!download_url(PDB_NAME, pdb_url)) {
|
||||
if (download_url(PDB_NAME, pdb_url)) {
|
||||
eprintf("Failed to download PDB file\n");
|
||||
err = 1;
|
||||
goto out_ps;
|
||||
}
|
||||
|
||||
if (!pdb_init_from_file(PDB_NAME, &pdb)) {
|
||||
if (pdb_init_from_file(PDB_NAME, &pdb)) {
|
||||
eprintf("Failed to initialize PDB reader\n");
|
||||
err = 1;
|
||||
goto out_pdb_file;
|
||||
}
|
||||
|
||||
if (!SYM_RESOLVE(KernBase, &pdb, KdDebuggerDataBlock) ||
|
||||
!SYM_RESOLVE(KernBase, &pdb, KdVersionBlock)) {
|
||||
err = 1;
|
||||
goto out_pdb;
|
||||
}
|
||||
|
||||
kdbg = get_kdbg(KernBase, &pdb, &vs, KdDebuggerDataBlock);
|
||||
if (!kdbg) {
|
||||
err = 1;
|
||||
goto out_pdb;
|
||||
}
|
||||
|
||||
if (!fill_header(&header, &ps, &vs, KdDebuggerDataBlock, kdbg,
|
||||
KdVersionBlock, qemu_elf.state_nr)) {
|
||||
if (fill_header(&header, &ps, &vs, KdDebuggerDataBlock, kdbg,
|
||||
KdVersionBlock, qemu_elf.state_nr)) {
|
||||
err = 1;
|
||||
goto out_kdbg;
|
||||
}
|
||||
|
||||
fill_context(kdbg, &vs, &qemu_elf);
|
||||
if (fill_context(kdbg, &vs, &qemu_elf)) {
|
||||
err = 1;
|
||||
goto out_kdbg;
|
||||
}
|
||||
|
||||
if (!write_dump(&ps, &header, argv[2])) {
|
||||
if (write_dump(&ps, &header, argv[2])) {
|
||||
eprintf("Failed to save dump\n");
|
||||
err = 1;
|
||||
goto out_kdbg;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
|
||||
out_kdbg:
|
||||
g_free(kdbg);
|
||||
out_pdb:
|
||||
@@ -630,6 +647,7 @@ out_pdb_file:
|
||||
unlink(PDB_NAME);
|
||||
out_ps:
|
||||
pa_space_destroy(&ps);
|
||||
out_elf:
|
||||
QEMU_Elf_exit(&qemu_elf);
|
||||
|
||||
return err;
|
||||
|
||||
+34
-27
@@ -19,7 +19,6 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/bswap.h"
|
||||
|
||||
#include "pdb.h"
|
||||
#include "err.h"
|
||||
@@ -159,35 +158,36 @@ static void *pdb_ds_read_file(struct pdb_reader* r, uint32_t file_number)
|
||||
return pdb_ds_read(r->ds.header, block_list, file_size[file_number]);
|
||||
}
|
||||
|
||||
static bool pdb_init_segments(struct pdb_reader *r)
|
||||
static int pdb_init_segments(struct pdb_reader *r)
|
||||
{
|
||||
unsigned stream_idx = r->segments;
|
||||
|
||||
r->segs = pdb_ds_read_file(r, stream_idx);
|
||||
if (!r->segs) {
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
r->segs_size = pdb_get_file_size(r, stream_idx);
|
||||
if (!r->segs_size) {
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool pdb_init_symbols(struct pdb_reader *r)
|
||||
static int pdb_init_symbols(struct pdb_reader *r)
|
||||
{
|
||||
int err = 0;
|
||||
PDB_SYMBOLS *symbols;
|
||||
|
||||
symbols = pdb_ds_read_file(r, 3);
|
||||
if (!symbols) {
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
r->symbols = symbols;
|
||||
|
||||
r->segments = lduw_le_p((const char *)symbols + sizeof(PDB_SYMBOLS) +
|
||||
r->segments = *(uint16_t *)((const char *)symbols + sizeof(PDB_SYMBOLS) +
|
||||
symbols->module_size + symbols->offset_size +
|
||||
symbols->hash_size + symbols->srcmodule_size +
|
||||
symbols->pdbimport_size + symbols->unknown2_size +
|
||||
@@ -196,21 +196,22 @@ static bool pdb_init_symbols(struct pdb_reader *r)
|
||||
/* Read global symbol table */
|
||||
r->modimage = pdb_ds_read_file(r, symbols->gsym_file);
|
||||
if (!r->modimage) {
|
||||
err = 1;
|
||||
goto out_symbols;
|
||||
}
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
|
||||
out_symbols:
|
||||
g_free(symbols);
|
||||
|
||||
return false;
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr)
|
||||
static int pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr)
|
||||
{
|
||||
if (hdr->block_size == 0) {
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset(r->file_used, 0, sizeof(r->file_used));
|
||||
@@ -219,38 +220,42 @@ static bool pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr)
|
||||
hdr->toc_page * hdr->block_size), hdr->toc_size);
|
||||
|
||||
if (!r->ds.toc) {
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool pdb_reader_init(struct pdb_reader *r, void *data)
|
||||
static int pdb_reader_init(struct pdb_reader *r, void *data)
|
||||
{
|
||||
int err = 0;
|
||||
const char pdb7[] = "Microsoft C/C++ MSF 7.00";
|
||||
|
||||
if (memcmp(data, pdb7, sizeof(pdb7) - 1)) {
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!pdb_reader_ds_init(r, data)) {
|
||||
return false;
|
||||
if (pdb_reader_ds_init(r, data)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
r->ds.root = pdb_ds_read_file(r, 1);
|
||||
if (!r->ds.root) {
|
||||
err = 1;
|
||||
goto out_ds;
|
||||
}
|
||||
|
||||
if (!pdb_init_symbols(r)) {
|
||||
if (pdb_init_symbols(r)) {
|
||||
err = 1;
|
||||
goto out_root;
|
||||
}
|
||||
|
||||
if (!pdb_init_segments(r)) {
|
||||
if (pdb_init_segments(r)) {
|
||||
err = 1;
|
||||
goto out_sym;
|
||||
}
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
|
||||
out_sym:
|
||||
pdb_exit_symbols(r);
|
||||
@@ -259,7 +264,7 @@ out_root:
|
||||
out_ds:
|
||||
pdb_reader_ds_exit(r);
|
||||
|
||||
return false;
|
||||
return err;
|
||||
}
|
||||
|
||||
static void pdb_reader_exit(struct pdb_reader *r)
|
||||
@@ -270,30 +275,32 @@ static void pdb_reader_exit(struct pdb_reader *r)
|
||||
pdb_reader_ds_exit(r);
|
||||
}
|
||||
|
||||
bool pdb_init_from_file(const char *name, struct pdb_reader *reader)
|
||||
int pdb_init_from_file(const char *name, struct pdb_reader *reader)
|
||||
{
|
||||
GError *gerr = NULL;
|
||||
int err = 0;
|
||||
void *map;
|
||||
|
||||
reader->gmf = g_mapped_file_new(name, TRUE, &gerr);
|
||||
if (gerr) {
|
||||
eprintf("Failed to map PDB file \'%s\'\n", name);
|
||||
g_error_free(gerr);
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
reader->file_size = g_mapped_file_get_length(reader->gmf);
|
||||
map = g_mapped_file_get_contents(reader->gmf);
|
||||
if (!pdb_reader_init(reader, map)) {
|
||||
if (pdb_reader_init(reader, map)) {
|
||||
err = 1;
|
||||
goto out_unmap;
|
||||
}
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
|
||||
out_unmap:
|
||||
g_mapped_file_unref(reader->gmf);
|
||||
|
||||
return false;
|
||||
return err;
|
||||
}
|
||||
|
||||
void pdb_exit(struct pdb_reader *reader)
|
||||
|
||||
@@ -233,7 +233,7 @@ struct pdb_reader {
|
||||
size_t segs_size;
|
||||
};
|
||||
|
||||
bool pdb_init_from_file(const char *name, struct pdb_reader *reader);
|
||||
int pdb_init_from_file(const char *name, struct pdb_reader *reader);
|
||||
void pdb_exit(struct pdb_reader *reader);
|
||||
uint64_t pdb_resolve(uint64_t img_base, struct pdb_reader *r, const char *name);
|
||||
uint64_t pdb_find_public_v3_symbol(struct pdb_reader *reader, const char *name);
|
||||
|
||||
+64
-86
@@ -6,7 +6,6 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "err.h"
|
||||
#include "qemu_elf.h"
|
||||
|
||||
@@ -16,11 +15,36 @@
|
||||
#define ROUND_UP(n, d) (((n) + (d) - 1) & -(0 ? (n) : (d)))
|
||||
#endif
|
||||
|
||||
#ifndef DIV_ROUND_UP
|
||||
#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
|
||||
#endif
|
||||
|
||||
#define ELF_NOTE_SIZE(hdr_size, name_size, desc_size) \
|
||||
((DIV_ROUND_UP((hdr_size), 4) + \
|
||||
DIV_ROUND_UP((name_size), 4) + \
|
||||
DIV_ROUND_UP((desc_size), 4)) * 4)
|
||||
|
||||
int is_system(QEMUCPUState *s)
|
||||
{
|
||||
return s->gs.base >> 63;
|
||||
}
|
||||
|
||||
static char *nhdr_get_name(Elf64_Nhdr *nhdr)
|
||||
{
|
||||
return (char *)nhdr + ROUND_UP(sizeof(*nhdr), 4);
|
||||
}
|
||||
|
||||
static void *nhdr_get_desc(Elf64_Nhdr *nhdr)
|
||||
{
|
||||
return nhdr_get_name(nhdr) + ROUND_UP(nhdr->n_namesz, 4);
|
||||
}
|
||||
|
||||
static Elf64_Nhdr *nhdr_get_next(Elf64_Nhdr *nhdr)
|
||||
{
|
||||
return (void *)((uint8_t *)nhdr + ELF_NOTE_SIZE(sizeof(*nhdr),
|
||||
nhdr->n_namesz, nhdr->n_descsz));
|
||||
}
|
||||
|
||||
Elf64_Phdr *elf64_getphdr(void *map)
|
||||
{
|
||||
Elf64_Ehdr *ehdr = map;
|
||||
@@ -36,92 +60,54 @@ Elf64_Half elf_getphdrnum(void *map)
|
||||
return ehdr->e_phnum;
|
||||
}
|
||||
|
||||
static bool advance_note_offset(uint64_t *offsetp, uint64_t size, uint64_t end)
|
||||
{
|
||||
uint64_t offset = *offsetp;
|
||||
|
||||
if (uadd64_overflow(offset, size, &offset) || offset > UINT64_MAX - 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
offset = ROUND_UP(offset, 4);
|
||||
|
||||
if (offset > end) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*offsetp = offset;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool init_states(QEMU_Elf *qe)
|
||||
static int init_states(QEMU_Elf *qe)
|
||||
{
|
||||
Elf64_Phdr *phdr = elf64_getphdr(qe->map);
|
||||
Elf64_Nhdr *start = (void *)((uint8_t *)qe->map + phdr[0].p_offset);
|
||||
Elf64_Nhdr *end = (void *)((uint8_t *)start + phdr[0].p_memsz);
|
||||
Elf64_Nhdr *nhdr;
|
||||
GPtrArray *states;
|
||||
QEMUCPUState *state;
|
||||
uint32_t state_size;
|
||||
uint64_t offset;
|
||||
uint64_t end_offset;
|
||||
char *name;
|
||||
size_t cpu_nr = 0;
|
||||
|
||||
if (phdr[0].p_type != PT_NOTE) {
|
||||
eprintf("Failed to find PT_NOTE\n");
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
qe->has_kernel_gs_base = 1;
|
||||
offset = phdr[0].p_offset;
|
||||
states = g_ptr_array_new();
|
||||
|
||||
if (uadd64_overflow(offset, phdr[0].p_memsz, &end_offset) ||
|
||||
end_offset > qe->size) {
|
||||
end_offset = qe->size;
|
||||
}
|
||||
for (nhdr = start; nhdr < end; nhdr = nhdr_get_next(nhdr)) {
|
||||
if (!strcmp(nhdr_get_name(nhdr), QEMU_NOTE_NAME)) {
|
||||
QEMUCPUState *state = nhdr_get_desc(nhdr);
|
||||
|
||||
while (offset < end_offset) {
|
||||
nhdr = (void *)((uint8_t *)qe->map + offset);
|
||||
|
||||
if (!advance_note_offset(&offset, sizeof(*nhdr), end_offset)) {
|
||||
break;
|
||||
}
|
||||
|
||||
name = (char *)qe->map + offset;
|
||||
|
||||
if (!advance_note_offset(&offset, nhdr->n_namesz, end_offset)) {
|
||||
break;
|
||||
}
|
||||
|
||||
state = (void *)((uint8_t *)qe->map + offset);
|
||||
|
||||
if (!advance_note_offset(&offset, nhdr->n_descsz, end_offset)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!strcmp(name, QEMU_NOTE_NAME) &&
|
||||
nhdr->n_descsz >= offsetof(QEMUCPUState, kernel_gs_base)) {
|
||||
state_size = MIN(state->size, nhdr->n_descsz);
|
||||
|
||||
if (state_size < sizeof(*state)) {
|
||||
eprintf("CPU #%u: QEMU CPU state size %u doesn't match\n",
|
||||
states->len, state_size);
|
||||
if (state->size < sizeof(*state)) {
|
||||
eprintf("CPU #%zu: QEMU CPU state size %u doesn't match\n",
|
||||
cpu_nr, state->size);
|
||||
/*
|
||||
* We assume either every QEMU CPU state has KERNEL_GS_BASE or
|
||||
* no one has.
|
||||
*/
|
||||
qe->has_kernel_gs_base = 0;
|
||||
}
|
||||
g_ptr_array_add(states, state);
|
||||
cpu_nr++;
|
||||
}
|
||||
}
|
||||
|
||||
printf("%u CPU states has been found\n", states->len);
|
||||
printf("%zu CPU states has been found\n", cpu_nr);
|
||||
|
||||
qe->state_nr = states->len;
|
||||
qe->state = (void *)g_ptr_array_free(states, FALSE);
|
||||
qe->state = g_new(QEMUCPUState*, cpu_nr);
|
||||
|
||||
return true;
|
||||
cpu_nr = 0;
|
||||
|
||||
for (nhdr = start; nhdr < end; nhdr = nhdr_get_next(nhdr)) {
|
||||
if (!strcmp(nhdr_get_name(nhdr), QEMU_NOTE_NAME)) {
|
||||
qe->state[cpu_nr] = nhdr_get_desc(nhdr);
|
||||
cpu_nr++;
|
||||
}
|
||||
}
|
||||
|
||||
qe->state_nr = cpu_nr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exit_states(QEMU_Elf *qe)
|
||||
@@ -132,7 +118,6 @@ static void exit_states(QEMU_Elf *qe)
|
||||
static bool check_ehdr(QEMU_Elf *qe)
|
||||
{
|
||||
Elf64_Ehdr *ehdr = qe->map;
|
||||
uint64_t phendoff;
|
||||
|
||||
if (sizeof(Elf64_Ehdr) > qe->size) {
|
||||
eprintf("Invalid input dump file size\n");
|
||||
@@ -174,17 +159,10 @@ static bool check_ehdr(QEMU_Elf *qe)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (umul64_overflow(ehdr->e_phnum, sizeof(Elf64_Phdr), &phendoff) ||
|
||||
uadd64_overflow(phendoff, ehdr->e_phoff, &phendoff) ||
|
||||
phendoff > qe->size) {
|
||||
eprintf("phdrs do not fit in file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool QEMU_Elf_map(QEMU_Elf *qe, const char *filename)
|
||||
static int QEMU_Elf_map(QEMU_Elf *qe, const char *filename)
|
||||
{
|
||||
#ifdef CONFIG_LINUX
|
||||
struct stat st;
|
||||
@@ -195,13 +173,13 @@ static bool QEMU_Elf_map(QEMU_Elf *qe, const char *filename)
|
||||
fd = open(filename, O_RDONLY, 0);
|
||||
if (fd == -1) {
|
||||
eprintf("Failed to open ELF dump file \'%s\'\n", filename);
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (fstat(fd, &st)) {
|
||||
eprintf("Failed to get size of ELF dump file\n");
|
||||
close(fd);
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
qe->size = st.st_size;
|
||||
|
||||
@@ -210,7 +188,7 @@ static bool QEMU_Elf_map(QEMU_Elf *qe, const char *filename)
|
||||
if (qe->map == MAP_FAILED) {
|
||||
eprintf("Failed to map ELF file\n");
|
||||
close(fd);
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
@@ -223,14 +201,14 @@ static bool QEMU_Elf_map(QEMU_Elf *qe, const char *filename)
|
||||
if (gerr) {
|
||||
eprintf("Failed to map ELF dump file \'%s\'\n", filename);
|
||||
g_error_free(gerr);
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
qe->map = g_mapped_file_get_contents(qe->gmf);
|
||||
qe->size = g_mapped_file_get_length(qe->gmf);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void QEMU_Elf_unmap(QEMU_Elf *qe)
|
||||
@@ -242,25 +220,25 @@ static void QEMU_Elf_unmap(QEMU_Elf *qe)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool QEMU_Elf_init(QEMU_Elf *qe, const char *filename)
|
||||
int QEMU_Elf_init(QEMU_Elf *qe, const char *filename)
|
||||
{
|
||||
if (!QEMU_Elf_map(qe, filename)) {
|
||||
return false;
|
||||
if (QEMU_Elf_map(qe, filename)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!check_ehdr(qe)) {
|
||||
eprintf("Input file has the wrong format\n");
|
||||
QEMU_Elf_unmap(qe);
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!init_states(qe)) {
|
||||
if (init_states(qe)) {
|
||||
eprintf("Failed to extract QEMU CPU states\n");
|
||||
QEMU_Elf_unmap(qe);
|
||||
return false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void QEMU_Elf_exit(QEMU_Elf *qe)
|
||||
|
||||
@@ -42,7 +42,7 @@ typedef struct QEMU_Elf {
|
||||
int has_kernel_gs_base;
|
||||
} QEMU_Elf;
|
||||
|
||||
bool QEMU_Elf_init(QEMU_Elf *qe, const char *filename);
|
||||
int QEMU_Elf_init(QEMU_Elf *qe, const char *filename);
|
||||
void QEMU_Elf_exit(QEMU_Elf *qe);
|
||||
|
||||
Elf64_Phdr *elf64_getphdr(void *map);
|
||||
|
||||
@@ -311,24 +311,6 @@ static Register *init_vcpu_register(qemu_plugin_reg_descriptor *desc)
|
||||
return reg;
|
||||
}
|
||||
|
||||
/*
|
||||
* g_pattern_match_string has been deprecated in Glib since 2.70 and
|
||||
* will complain about it if you try to use it. Fortunately the
|
||||
* signature of both functions is the same making it easy to work
|
||||
* around.
|
||||
*/
|
||||
static inline
|
||||
gboolean g_pattern_spec_match_string_qemu(GPatternSpec *pspec,
|
||||
const gchar *string)
|
||||
{
|
||||
#if GLIB_CHECK_VERSION(2, 70, 0)
|
||||
return g_pattern_spec_match_string(pspec, string);
|
||||
#else
|
||||
return g_pattern_match_string(pspec, string);
|
||||
#endif
|
||||
};
|
||||
#define g_pattern_spec_match_string(p, s) g_pattern_spec_match_string_qemu(p, s)
|
||||
|
||||
static GPtrArray *registers_init(int vcpu_index)
|
||||
{
|
||||
g_autoptr(GPtrArray) registers = g_ptr_array_new();
|
||||
@@ -345,8 +327,8 @@ static GPtrArray *registers_init(int vcpu_index)
|
||||
for (int p = 0; p < rmatches->len; p++) {
|
||||
g_autoptr(GPatternSpec) pat = g_pattern_spec_new(rmatches->pdata[p]);
|
||||
g_autofree gchar *rd_lower = g_utf8_strdown(rd->name, -1);
|
||||
if (g_pattern_spec_match_string(pat, rd->name) ||
|
||||
g_pattern_spec_match_string(pat, rd_lower)) {
|
||||
if (g_pattern_match_string(pat, rd->name) ||
|
||||
g_pattern_match_string(pat, rd_lower)) {
|
||||
Register *reg = init_vcpu_register(rd);
|
||||
g_ptr_array_add(registers, reg);
|
||||
|
||||
@@ -354,7 +336,7 @@ static GPtrArray *registers_init(int vcpu_index)
|
||||
if (disas_assist) {
|
||||
g_mutex_lock(&add_reg_name_lock);
|
||||
if (!g_ptr_array_find(all_reg_names, reg->name, NULL)) {
|
||||
g_ptr_array_add(all_reg_names, (gpointer)reg->name);
|
||||
g_ptr_array_add(all_reg_names, reg->name);
|
||||
}
|
||||
g_mutex_unlock(&add_reg_name_lock);
|
||||
}
|
||||
|
||||
+20
-30
@@ -34,8 +34,8 @@ static guint64 limit = 20;
|
||||
*/
|
||||
typedef struct {
|
||||
uint64_t start_addr;
|
||||
struct qemu_plugin_scoreboard *exec_count;
|
||||
int trans_count;
|
||||
uint64_t exec_count;
|
||||
int trans_count;
|
||||
unsigned long insns;
|
||||
} ExecCount;
|
||||
|
||||
@@ -43,17 +43,7 @@ static gint cmp_exec_count(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
ExecCount *ea = (ExecCount *) a;
|
||||
ExecCount *eb = (ExecCount *) b;
|
||||
uint64_t count_a =
|
||||
qemu_plugin_u64_sum(qemu_plugin_scoreboard_u64(ea->exec_count));
|
||||
uint64_t count_b =
|
||||
qemu_plugin_u64_sum(qemu_plugin_scoreboard_u64(eb->exec_count));
|
||||
return count_a > count_b ? -1 : 1;
|
||||
}
|
||||
|
||||
static void exec_count_free(gpointer key, gpointer value, gpointer user_data)
|
||||
{
|
||||
ExecCount *cnt = value;
|
||||
qemu_plugin_scoreboard_free(cnt->exec_count);
|
||||
return ea->exec_count > eb->exec_count ? -1 : 1;
|
||||
}
|
||||
|
||||
static void plugin_exit(qemu_plugin_id_t id, void *p)
|
||||
@@ -62,6 +52,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p)
|
||||
GList *counts, *it;
|
||||
int i;
|
||||
|
||||
g_mutex_lock(&lock);
|
||||
g_string_append_printf(report, "%d entries in the hash table\n",
|
||||
g_hash_table_size(hotblocks));
|
||||
counts = g_hash_table_get_values(hotblocks);
|
||||
@@ -72,21 +63,16 @@ static void plugin_exit(qemu_plugin_id_t id, void *p)
|
||||
|
||||
for (i = 0; i < limit && it->next; i++, it = it->next) {
|
||||
ExecCount *rec = (ExecCount *) it->data;
|
||||
g_string_append_printf(
|
||||
report, "0x%016"PRIx64", %d, %ld, %"PRId64"\n",
|
||||
rec->start_addr, rec->trans_count,
|
||||
rec->insns,
|
||||
qemu_plugin_u64_sum(
|
||||
qemu_plugin_scoreboard_u64(rec->exec_count)));
|
||||
g_string_append_printf(report, "0x%016"PRIx64", %d, %ld, %"PRId64"\n",
|
||||
rec->start_addr, rec->trans_count,
|
||||
rec->insns, rec->exec_count);
|
||||
}
|
||||
|
||||
g_list_free(it);
|
||||
}
|
||||
g_mutex_unlock(&lock);
|
||||
|
||||
qemu_plugin_outs(report->str);
|
||||
|
||||
g_hash_table_foreach(hotblocks, exec_count_free, NULL);
|
||||
g_hash_table_destroy(hotblocks);
|
||||
}
|
||||
|
||||
static void plugin_init(void)
|
||||
@@ -96,9 +82,15 @@ static void plugin_init(void)
|
||||
|
||||
static void vcpu_tb_exec(unsigned int cpu_index, void *udata)
|
||||
{
|
||||
ExecCount *cnt = (ExecCount *)udata;
|
||||
qemu_plugin_u64_add(qemu_plugin_scoreboard_u64(cnt->exec_count),
|
||||
cpu_index, 1);
|
||||
ExecCount *cnt;
|
||||
uint64_t hash = (uint64_t) udata;
|
||||
|
||||
g_mutex_lock(&lock);
|
||||
cnt = (ExecCount *) g_hash_table_lookup(hotblocks, (gconstpointer) hash);
|
||||
/* should always succeed */
|
||||
g_assert(cnt);
|
||||
cnt->exec_count++;
|
||||
g_mutex_unlock(&lock);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -122,20 +114,18 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
|
||||
cnt->start_addr = pc;
|
||||
cnt->trans_count = 1;
|
||||
cnt->insns = insns;
|
||||
cnt->exec_count = qemu_plugin_scoreboard_new(sizeof(uint64_t));
|
||||
g_hash_table_insert(hotblocks, (gpointer) hash, (gpointer) cnt);
|
||||
}
|
||||
|
||||
g_mutex_unlock(&lock);
|
||||
|
||||
if (do_inline) {
|
||||
qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
|
||||
tb, QEMU_PLUGIN_INLINE_ADD_U64,
|
||||
qemu_plugin_scoreboard_u64(cnt->exec_count), 1);
|
||||
qemu_plugin_register_vcpu_tb_exec_inline(tb, QEMU_PLUGIN_INLINE_ADD_U64,
|
||||
&cnt->exec_count, 1);
|
||||
} else {
|
||||
qemu_plugin_register_vcpu_tb_exec_cb(tb, vcpu_tb_exec,
|
||||
QEMU_PLUGIN_CB_NO_REGS,
|
||||
(void *)cnt);
|
||||
(void *)hash);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
-38
@@ -43,13 +43,13 @@ typedef struct {
|
||||
uint32_t mask;
|
||||
uint32_t pattern;
|
||||
CountType what;
|
||||
qemu_plugin_u64 count;
|
||||
uint64_t count;
|
||||
} InsnClassExecCount;
|
||||
|
||||
typedef struct {
|
||||
char *insn;
|
||||
uint32_t opcode;
|
||||
qemu_plugin_u64 count;
|
||||
uint64_t count;
|
||||
InsnClassExecCount *class;
|
||||
} InsnExecCount;
|
||||
|
||||
@@ -159,15 +159,12 @@ static gint cmp_exec_count(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
InsnExecCount *ea = (InsnExecCount *) a;
|
||||
InsnExecCount *eb = (InsnExecCount *) b;
|
||||
uint64_t count_a = qemu_plugin_u64_sum(ea->count);
|
||||
uint64_t count_b = qemu_plugin_u64_sum(eb->count);
|
||||
return count_a > count_b ? -1 : 1;
|
||||
return ea->count > eb->count ? -1 : 1;
|
||||
}
|
||||
|
||||
static void free_record(gpointer data)
|
||||
{
|
||||
InsnExecCount *rec = (InsnExecCount *) data;
|
||||
qemu_plugin_scoreboard_free(rec->count.score);
|
||||
g_free(rec->insn);
|
||||
g_free(rec);
|
||||
}
|
||||
@@ -176,7 +173,6 @@ static void plugin_exit(qemu_plugin_id_t id, void *p)
|
||||
{
|
||||
g_autoptr(GString) report = g_string_new("Instruction Classes:\n");
|
||||
int i;
|
||||
uint64_t total_count;
|
||||
GList *counts;
|
||||
InsnClassExecCount *class = NULL;
|
||||
|
||||
@@ -184,12 +180,11 @@ static void plugin_exit(qemu_plugin_id_t id, void *p)
|
||||
class = &class_table[i];
|
||||
switch (class->what) {
|
||||
case COUNT_CLASS:
|
||||
total_count = qemu_plugin_u64_sum(class->count);
|
||||
if (total_count || verbose) {
|
||||
if (class->count || verbose) {
|
||||
g_string_append_printf(report,
|
||||
"Class: %-24s\t(%" PRId64 " hits)\n",
|
||||
class->class,
|
||||
total_count);
|
||||
class->count);
|
||||
}
|
||||
break;
|
||||
case COUNT_INDIVIDUAL:
|
||||
@@ -217,7 +212,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p)
|
||||
"Instr: %-24s\t(%" PRId64 " hits)"
|
||||
"\t(op=0x%08x/%s)\n",
|
||||
rec->insn,
|
||||
qemu_plugin_u64_sum(rec->count),
|
||||
rec->count,
|
||||
rec->opcode,
|
||||
rec->class ?
|
||||
rec->class->class : "un-categorised");
|
||||
@@ -226,12 +221,6 @@ static void plugin_exit(qemu_plugin_id_t id, void *p)
|
||||
}
|
||||
|
||||
g_hash_table_destroy(insns);
|
||||
for (i = 0; i < ARRAY_SIZE(class_tables); i++) {
|
||||
for (int j = 0; j < class_tables[i].table_sz; ++j) {
|
||||
qemu_plugin_scoreboard_free(class_tables[i].table[j].count.score);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
qemu_plugin_outs(report->str);
|
||||
}
|
||||
@@ -243,12 +232,11 @@ static void plugin_init(void)
|
||||
|
||||
static void vcpu_insn_exec_before(unsigned int cpu_index, void *udata)
|
||||
{
|
||||
struct qemu_plugin_scoreboard *score = udata;
|
||||
qemu_plugin_u64_add(qemu_plugin_scoreboard_u64(score), cpu_index, 1);
|
||||
uint64_t *count = (uint64_t *) udata;
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
static struct qemu_plugin_scoreboard *find_counter(
|
||||
struct qemu_plugin_insn *insn)
|
||||
static uint64_t *find_counter(struct qemu_plugin_insn *insn)
|
||||
{
|
||||
int i;
|
||||
uint64_t *cnt = NULL;
|
||||
@@ -277,7 +265,7 @@ static struct qemu_plugin_scoreboard *find_counter(
|
||||
case COUNT_NONE:
|
||||
return NULL;
|
||||
case COUNT_CLASS:
|
||||
return class->count.score;
|
||||
return &class->count;
|
||||
case COUNT_INDIVIDUAL:
|
||||
{
|
||||
InsnExecCount *icount;
|
||||
@@ -291,16 +279,13 @@ static struct qemu_plugin_scoreboard *find_counter(
|
||||
icount->opcode = opcode;
|
||||
icount->insn = qemu_plugin_insn_disas(insn);
|
||||
icount->class = class;
|
||||
struct qemu_plugin_scoreboard *score =
|
||||
qemu_plugin_scoreboard_new(sizeof(uint64_t));
|
||||
icount->count = qemu_plugin_scoreboard_u64(score);
|
||||
|
||||
g_hash_table_insert(insns, GUINT_TO_POINTER(opcode),
|
||||
(gpointer) icount);
|
||||
}
|
||||
g_mutex_unlock(&lock);
|
||||
|
||||
return icount->count.score;
|
||||
return &icount->count;
|
||||
}
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
@@ -315,14 +300,14 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
uint64_t *cnt;
|
||||
struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i);
|
||||
struct qemu_plugin_scoreboard *cnt = find_counter(insn);
|
||||
cnt = find_counter(insn);
|
||||
|
||||
if (cnt) {
|
||||
if (do_inline) {
|
||||
qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
|
||||
insn, QEMU_PLUGIN_INLINE_ADD_U64,
|
||||
qemu_plugin_scoreboard_u64(cnt), 1);
|
||||
qemu_plugin_register_vcpu_insn_exec_inline(
|
||||
insn, QEMU_PLUGIN_INLINE_ADD_U64, cnt, 1);
|
||||
} else {
|
||||
qemu_plugin_register_vcpu_insn_exec_cb(
|
||||
insn, vcpu_insn_exec_before, QEMU_PLUGIN_CB_NO_REGS, cnt);
|
||||
@@ -337,14 +322,6 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(class_tables); i++) {
|
||||
for (int j = 0; j < class_tables[i].table_sz; ++j) {
|
||||
struct qemu_plugin_scoreboard *score =
|
||||
qemu_plugin_scoreboard_new(sizeof(uint64_t));
|
||||
class_tables[i].table[j].count = qemu_plugin_scoreboard_u64(score);
|
||||
}
|
||||
}
|
||||
|
||||
/* Select a class table appropriate to the guest architecture */
|
||||
for (i = 0; i < ARRAY_SIZE(class_tables); i++) {
|
||||
ClassSelector *entry = &class_tables[i];
|
||||
|
||||
@@ -100,31 +100,6 @@ static void plugin_exit(qemu_plugin_id_t id, void *p)
|
||||
plugin_cleanup(id);
|
||||
}
|
||||
|
||||
/*
|
||||
* g_memdup has been deprecated in Glib since 2.68 and
|
||||
* will complain about it if you try to use it. However until
|
||||
* glib_req_ver for QEMU is bumped we make a copy of the glib-compat
|
||||
* handler.
|
||||
*/
|
||||
static inline gpointer g_memdup2_qemu(gconstpointer mem, gsize byte_size)
|
||||
{
|
||||
#if GLIB_CHECK_VERSION(2, 68, 0)
|
||||
return g_memdup2(mem, byte_size);
|
||||
#else
|
||||
gpointer new_mem;
|
||||
|
||||
if (mem && byte_size != 0) {
|
||||
new_mem = g_malloc(byte_size);
|
||||
memcpy(new_mem, mem, byte_size);
|
||||
} else {
|
||||
new_mem = NULL;
|
||||
}
|
||||
|
||||
return new_mem;
|
||||
#endif
|
||||
}
|
||||
#define g_memdup2(m, s) g_memdup2_qemu(m, s)
|
||||
|
||||
static void report_divergance(ExecState *us, ExecState *them)
|
||||
{
|
||||
DivergeState divrec = { log, 0 };
|
||||
|
||||
+56
-65
@@ -20,56 +20,6 @@
|
||||
|
||||
#include <gcrypt.h>
|
||||
|
||||
static int qcrypto_cipher_alg_to_gcry_alg(QCryptoCipherAlgorithm alg)
|
||||
{
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_DES:
|
||||
return GCRY_CIPHER_DES;
|
||||
case QCRYPTO_CIPHER_ALG_3DES:
|
||||
return GCRY_CIPHER_3DES;
|
||||
case QCRYPTO_CIPHER_ALG_AES_128:
|
||||
return GCRY_CIPHER_AES128;
|
||||
case QCRYPTO_CIPHER_ALG_AES_192:
|
||||
return GCRY_CIPHER_AES192;
|
||||
case QCRYPTO_CIPHER_ALG_AES_256:
|
||||
return GCRY_CIPHER_AES256;
|
||||
case QCRYPTO_CIPHER_ALG_CAST5_128:
|
||||
return GCRY_CIPHER_CAST5;
|
||||
case QCRYPTO_CIPHER_ALG_SERPENT_128:
|
||||
return GCRY_CIPHER_SERPENT128;
|
||||
case QCRYPTO_CIPHER_ALG_SERPENT_192:
|
||||
return GCRY_CIPHER_SERPENT192;
|
||||
case QCRYPTO_CIPHER_ALG_SERPENT_256:
|
||||
return GCRY_CIPHER_SERPENT256;
|
||||
case QCRYPTO_CIPHER_ALG_TWOFISH_128:
|
||||
return GCRY_CIPHER_TWOFISH128;
|
||||
case QCRYPTO_CIPHER_ALG_TWOFISH_256:
|
||||
return GCRY_CIPHER_TWOFISH;
|
||||
#ifdef CONFIG_CRYPTO_SM4
|
||||
case QCRYPTO_CIPHER_ALG_SM4:
|
||||
return GCRY_CIPHER_SM4;
|
||||
#endif
|
||||
default:
|
||||
return GCRY_CIPHER_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static int qcrypto_cipher_mode_to_gcry_mode(QCryptoCipherMode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case QCRYPTO_CIPHER_MODE_ECB:
|
||||
return GCRY_CIPHER_MODE_ECB;
|
||||
case QCRYPTO_CIPHER_MODE_XTS:
|
||||
return GCRY_CIPHER_MODE_XTS;
|
||||
case QCRYPTO_CIPHER_MODE_CBC:
|
||||
return GCRY_CIPHER_MODE_CBC;
|
||||
case QCRYPTO_CIPHER_MODE_CTR:
|
||||
return GCRY_CIPHER_MODE_CTR;
|
||||
default:
|
||||
return GCRY_CIPHER_MODE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg,
|
||||
QCryptoCipherMode mode)
|
||||
{
|
||||
@@ -93,11 +43,6 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gcry_cipher_algo_info(qcrypto_cipher_alg_to_gcry_alg(alg),
|
||||
GCRYCTL_TEST_ALGO, NULL, NULL) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case QCRYPTO_CIPHER_MODE_ECB:
|
||||
case QCRYPTO_CIPHER_MODE_CBC:
|
||||
@@ -243,26 +188,72 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gcryalg = qcrypto_cipher_alg_to_gcry_alg(alg);
|
||||
if (gcryalg == GCRY_CIPHER_NONE) {
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_DES:
|
||||
gcryalg = GCRY_CIPHER_DES;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_ALG_3DES:
|
||||
gcryalg = GCRY_CIPHER_3DES;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_ALG_AES_128:
|
||||
gcryalg = GCRY_CIPHER_AES128;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_ALG_AES_192:
|
||||
gcryalg = GCRY_CIPHER_AES192;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_ALG_AES_256:
|
||||
gcryalg = GCRY_CIPHER_AES256;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_ALG_CAST5_128:
|
||||
gcryalg = GCRY_CIPHER_CAST5;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_ALG_SERPENT_128:
|
||||
gcryalg = GCRY_CIPHER_SERPENT128;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_ALG_SERPENT_192:
|
||||
gcryalg = GCRY_CIPHER_SERPENT192;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_ALG_SERPENT_256:
|
||||
gcryalg = GCRY_CIPHER_SERPENT256;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_ALG_TWOFISH_128:
|
||||
gcryalg = GCRY_CIPHER_TWOFISH128;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_ALG_TWOFISH_256:
|
||||
gcryalg = GCRY_CIPHER_TWOFISH;
|
||||
break;
|
||||
#ifdef CONFIG_CRYPTO_SM4
|
||||
case QCRYPTO_CIPHER_ALG_SM4:
|
||||
gcryalg = GCRY_CIPHER_SM4;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
error_setg(errp, "Unsupported cipher algorithm %s",
|
||||
QCryptoCipherAlgorithm_str(alg));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gcrymode = qcrypto_cipher_mode_to_gcry_mode(mode);
|
||||
if (gcrymode == GCRY_CIPHER_MODE_NONE) {
|
||||
drv = &qcrypto_gcrypt_driver;
|
||||
switch (mode) {
|
||||
case QCRYPTO_CIPHER_MODE_ECB:
|
||||
gcrymode = GCRY_CIPHER_MODE_ECB;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_MODE_XTS:
|
||||
gcrymode = GCRY_CIPHER_MODE_XTS;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_MODE_CBC:
|
||||
gcrymode = GCRY_CIPHER_MODE_CBC;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_MODE_CTR:
|
||||
drv = &qcrypto_gcrypt_ctr_driver;
|
||||
gcrymode = GCRY_CIPHER_MODE_CTR;
|
||||
break;
|
||||
default:
|
||||
error_setg(errp, "Unsupported cipher mode %s",
|
||||
QCryptoCipherMode_str(mode));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (mode == QCRYPTO_CIPHER_MODE_CTR) {
|
||||
drv = &qcrypto_gcrypt_ctr_driver;
|
||||
} else {
|
||||
drv = &qcrypto_gcrypt_driver;
|
||||
}
|
||||
|
||||
ctx = g_new0(QCryptoCipherGcrypt, 1);
|
||||
ctx->base.driver = drv;
|
||||
|
||||
|
||||
@@ -734,19 +734,16 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
|
||||
#ifdef CONFIG_CRYPTO_SM4
|
||||
case QCRYPTO_CIPHER_ALG_SM4:
|
||||
{
|
||||
QCryptoNettleSm4 *ctx;
|
||||
const QCryptoCipherDriver *drv;
|
||||
QCryptoNettleSm4 *ctx = g_new0(QCryptoNettleSm4, 1);
|
||||
|
||||
switch (mode) {
|
||||
case QCRYPTO_CIPHER_MODE_ECB:
|
||||
drv = &qcrypto_nettle_sm4_driver_ecb;
|
||||
ctx->base.driver = &qcrypto_nettle_sm4_driver_ecb;
|
||||
break;
|
||||
default:
|
||||
goto bad_cipher_mode;
|
||||
}
|
||||
|
||||
ctx = g_new0(QCryptoNettleSm4, 1);
|
||||
ctx->base.driver = drv;
|
||||
sm4_set_encrypt_key(&ctx->key[0], key);
|
||||
sm4_set_decrypt_key(&ctx->key[1], key);
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash)
|
||||
case QCRYPTO_HASH_ALG_SHA384:
|
||||
case QCRYPTO_HASH_ALG_SHA512:
|
||||
case QCRYPTO_HASH_ALG_RIPEMD160:
|
||||
return qcrypto_hash_supports(hash);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash)
|
||||
case QCRYPTO_HASH_ALG_SHA384:
|
||||
case QCRYPTO_HASH_ALG_SHA512:
|
||||
case QCRYPTO_HASH_ALG_RIPEMD160:
|
||||
return qcrypto_hash_supports(hash);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
+7
-46
@@ -19,7 +19,6 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/thread.h"
|
||||
#include "qapi/error.h"
|
||||
#include "crypto/pbkdf.h"
|
||||
#ifndef _WIN32
|
||||
@@ -86,28 +85,12 @@ static int qcrypto_pbkdf2_get_thread_cpu(unsigned long long *val_ms,
|
||||
#endif
|
||||
}
|
||||
|
||||
typedef struct CountItersData {
|
||||
QCryptoHashAlgorithm hash;
|
||||
const uint8_t *key;
|
||||
size_t nkey;
|
||||
const uint8_t *salt;
|
||||
size_t nsalt;
|
||||
size_t nout;
|
||||
uint64_t iterations;
|
||||
Error **errp;
|
||||
} CountItersData;
|
||||
|
||||
static void *threaded_qcrypto_pbkdf2_count_iters(void *data)
|
||||
uint64_t qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash,
|
||||
const uint8_t *key, size_t nkey,
|
||||
const uint8_t *salt, size_t nsalt,
|
||||
size_t nout,
|
||||
Error **errp)
|
||||
{
|
||||
CountItersData *iters_data = (CountItersData *) data;
|
||||
QCryptoHashAlgorithm hash = iters_data->hash;
|
||||
const uint8_t *key = iters_data->key;
|
||||
size_t nkey = iters_data->nkey;
|
||||
const uint8_t *salt = iters_data->salt;
|
||||
size_t nsalt = iters_data->nsalt;
|
||||
size_t nout = iters_data->nout;
|
||||
Error **errp = iters_data->errp;
|
||||
|
||||
uint64_t ret = -1;
|
||||
g_autofree uint8_t *out = g_new(uint8_t, nout);
|
||||
uint64_t iterations = (1 << 15);
|
||||
@@ -131,10 +114,7 @@ static void *threaded_qcrypto_pbkdf2_count_iters(void *data)
|
||||
|
||||
delta_ms = end_ms - start_ms;
|
||||
|
||||
if (delta_ms == 0) { /* sanity check */
|
||||
error_setg(errp, "Unable to get accurate CPU usage");
|
||||
goto cleanup;
|
||||
} else if (delta_ms > 500) {
|
||||
if (delta_ms > 500) {
|
||||
break;
|
||||
} else if (delta_ms < 100) {
|
||||
iterations = iterations * 10;
|
||||
@@ -149,24 +129,5 @@ static void *threaded_qcrypto_pbkdf2_count_iters(void *data)
|
||||
|
||||
cleanup:
|
||||
memset(out, 0, nout);
|
||||
iters_data->iterations = ret;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint64_t qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash,
|
||||
const uint8_t *key, size_t nkey,
|
||||
const uint8_t *salt, size_t nsalt,
|
||||
size_t nout,
|
||||
Error **errp)
|
||||
{
|
||||
CountItersData data = {
|
||||
hash, key, nkey, salt, nsalt, nout, 0, errp
|
||||
};
|
||||
QemuThread thread;
|
||||
|
||||
qemu_thread_create(&thread, "pbkdf2", threaded_qcrypto_pbkdf2_count_iters,
|
||||
&data, QEMU_THREAD_JOINABLE);
|
||||
qemu_thread_join(&thread);
|
||||
|
||||
return data.iterations;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -243,7 +243,6 @@ qcrypto_tls_creds_psk_finalize(Object *obj)
|
||||
QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj);
|
||||
|
||||
qcrypto_tls_creds_psk_unload(creds);
|
||||
g_free(creds->username);
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -34,7 +34,6 @@ void monitor_disas(Monitor *mon, CPUState *cpu, uint64_t pc,
|
||||
disas_initialize_debug_target(&s, cpu);
|
||||
s.info.fprintf_func = disas_gstring_printf;
|
||||
s.info.stream = (FILE *)ds; /* abuse this slot */
|
||||
s.info.show_opcodes = true;
|
||||
|
||||
if (is_physical) {
|
||||
s.info.read_memory_func = physical_read_memory;
|
||||
|
||||
@@ -211,7 +211,6 @@ void target_disas(FILE *out, CPUState *cpu, uint64_t code, size_t size)
|
||||
s.info.stream = out;
|
||||
s.info.buffer_vma = code;
|
||||
s.info.buffer_length = size;
|
||||
s.info.show_opcodes = true;
|
||||
|
||||
if (s.info.cap_arch >= 0 && cap_disas_target(&s.info, code, size)) {
|
||||
return;
|
||||
@@ -300,7 +299,6 @@ void disas(FILE *out, const void *code, size_t size)
|
||||
s.info.buffer = code;
|
||||
s.info.buffer_vma = (uintptr_t)code;
|
||||
s.info.buffer_length = size;
|
||||
s.info.show_opcodes = true;
|
||||
|
||||
if (s.info.cap_arch >= 0 && cap_disas_host(&s.info, code, size)) {
|
||||
return;
|
||||
|
||||
+3
-5
@@ -1972,11 +1972,9 @@ print_insn_hppa (bfd_vma memaddr, disassemble_info *info)
|
||||
|
||||
insn = bfd_getb32 (buffer);
|
||||
|
||||
if (info->show_opcodes) {
|
||||
info->fprintf_func(info->stream, " %02x %02x %02x %02x ",
|
||||
(insn >> 24) & 0xff, (insn >> 16) & 0xff,
|
||||
(insn >> 8) & 0xff, insn & 0xff);
|
||||
}
|
||||
info->fprintf_func(info->stream, " %02x %02x %02x %02x ",
|
||||
(insn >> 24) & 0xff, (insn >> 16) & 0xff,
|
||||
(insn >> 8) & 0xff, insn & 0xff);
|
||||
|
||||
for (i = 0; i < NUMOPCODES; ++i)
|
||||
{
|
||||
|
||||
+97
-97
@@ -36,6 +36,35 @@ typedef uint32_t uint32;
|
||||
typedef uint16_t uint16;
|
||||
typedef uint64_t img_address;
|
||||
|
||||
typedef enum {
|
||||
instruction,
|
||||
call_instruction,
|
||||
branch_instruction,
|
||||
return_instruction,
|
||||
reserved_block,
|
||||
pool,
|
||||
} TABLE_ENTRY_TYPE;
|
||||
|
||||
typedef enum {
|
||||
MIPS64_ = 0x00000001,
|
||||
XNP_ = 0x00000002,
|
||||
XMMS_ = 0x00000004,
|
||||
EVA_ = 0x00000008,
|
||||
DSP_ = 0x00000010,
|
||||
MT_ = 0x00000020,
|
||||
EJTAG_ = 0x00000040,
|
||||
TLBINV_ = 0x00000080,
|
||||
CP0_ = 0x00000100,
|
||||
CP1_ = 0x00000200,
|
||||
CP2_ = 0x00000400,
|
||||
UDI_ = 0x00000800,
|
||||
MCU_ = 0x00001000,
|
||||
VZ_ = 0x00002000,
|
||||
TLB_ = 0x00004000,
|
||||
MVH_ = 0x00008000,
|
||||
ALL_ATTRIBUTES = 0xffffffffull,
|
||||
} TABLE_ATTRIBUTE_TYPE;
|
||||
|
||||
typedef struct Dis_info {
|
||||
img_address m_pc;
|
||||
fprintf_function fprintf_func;
|
||||
@@ -43,6 +72,22 @@ typedef struct Dis_info {
|
||||
sigjmp_buf buf;
|
||||
} Dis_info;
|
||||
|
||||
typedef bool (*conditional_function)(uint64 instruction);
|
||||
typedef char * (*disassembly_function)(uint64 instruction,
|
||||
Dis_info *info);
|
||||
|
||||
typedef struct Pool {
|
||||
TABLE_ENTRY_TYPE type;
|
||||
const struct Pool *next_table;
|
||||
int next_table_size;
|
||||
int instructions_size;
|
||||
uint64 mask;
|
||||
uint64 value;
|
||||
disassembly_function disassembly;
|
||||
conditional_function condition;
|
||||
uint64 attributes;
|
||||
} Pool;
|
||||
|
||||
#define IMGASSERTONCE(test)
|
||||
|
||||
|
||||
@@ -499,6 +544,58 @@ static uint64 extract_op_code_value(const uint16 *data, int size)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Recurse through tables until the instruction is found then return
|
||||
* the string and size
|
||||
*
|
||||
* inputs:
|
||||
* pointer to a word stream,
|
||||
* disassember table and size
|
||||
* returns:
|
||||
* instruction size - negative is error
|
||||
* disassembly string - on error will constain error string
|
||||
*/
|
||||
static int Disassemble(const uint16 *data, char **dis,
|
||||
TABLE_ENTRY_TYPE *type, const Pool *table,
|
||||
int table_size, Dis_info *info)
|
||||
{
|
||||
for (int i = 0; i < table_size; i++) {
|
||||
uint64 op_code = extract_op_code_value(data,
|
||||
table[i].instructions_size);
|
||||
if ((op_code & table[i].mask) == table[i].value) {
|
||||
/* possible match */
|
||||
conditional_function cond = table[i].condition;
|
||||
if ((cond == NULL) || cond(op_code)) {
|
||||
if (table[i].type == pool) {
|
||||
return Disassemble(data, dis, type,
|
||||
table[i].next_table,
|
||||
table[i].next_table_size,
|
||||
info);
|
||||
} else if ((table[i].type == instruction) ||
|
||||
(table[i].type == call_instruction) ||
|
||||
(table[i].type == branch_instruction) ||
|
||||
(table[i].type == return_instruction)) {
|
||||
disassembly_function dis_fn = table[i].disassembly;
|
||||
if (dis_fn == 0) {
|
||||
*dis = g_strdup(
|
||||
"disassembler failure - bad table entry");
|
||||
return -6;
|
||||
}
|
||||
*type = table[i].type;
|
||||
*dis = dis_fn(op_code, info);
|
||||
return table[i].instructions_size;
|
||||
} else {
|
||||
*dis = g_strdup("reserved instruction");
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*dis = g_strdup("failed to disassemble");
|
||||
return -1; /* failed to disassemble */
|
||||
}
|
||||
|
||||
|
||||
static uint64 extract_code_18_to_0(uint64 instruction)
|
||||
{
|
||||
uint64 value = 0;
|
||||
@@ -16116,51 +16213,6 @@ static char *YIELD(uint64 instruction, Dis_info *info)
|
||||
*
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
instruction,
|
||||
call_instruction,
|
||||
branch_instruction,
|
||||
return_instruction,
|
||||
reserved_block,
|
||||
pool,
|
||||
} TABLE_ENTRY_TYPE;
|
||||
|
||||
typedef enum {
|
||||
MIPS64_ = 0x00000001,
|
||||
XNP_ = 0x00000002,
|
||||
XMMS_ = 0x00000004,
|
||||
EVA_ = 0x00000008,
|
||||
DSP_ = 0x00000010,
|
||||
MT_ = 0x00000020,
|
||||
EJTAG_ = 0x00000040,
|
||||
TLBINV_ = 0x00000080,
|
||||
CP0_ = 0x00000100,
|
||||
CP1_ = 0x00000200,
|
||||
CP2_ = 0x00000400,
|
||||
UDI_ = 0x00000800,
|
||||
MCU_ = 0x00001000,
|
||||
VZ_ = 0x00002000,
|
||||
TLB_ = 0x00004000,
|
||||
MVH_ = 0x00008000,
|
||||
ALL_ATTRIBUTES = 0xffffffffull,
|
||||
} TABLE_ATTRIBUTE_TYPE;
|
||||
|
||||
typedef bool (*conditional_function)(uint64 instruction);
|
||||
typedef char * (*disassembly_function)(uint64 instruction,
|
||||
Dis_info *info);
|
||||
|
||||
typedef struct Pool {
|
||||
TABLE_ENTRY_TYPE type;
|
||||
const struct Pool *next_table;
|
||||
int next_table_size;
|
||||
int instructions_size;
|
||||
uint64 mask;
|
||||
uint64 value;
|
||||
disassembly_function disassembly;
|
||||
conditional_function condition;
|
||||
uint64 attributes;
|
||||
} Pool;
|
||||
|
||||
static const Pool P_SYSCALL[2] = {
|
||||
{ instruction , 0 , 0 , 32,
|
||||
0xfffc0000, 0x00080000, &SYSCALL_32_ , 0,
|
||||
@@ -21855,58 +21907,6 @@ static const Pool MAJOR[2] = {
|
||||
0x0 }, /* P16 */
|
||||
};
|
||||
|
||||
/*
|
||||
* Recurse through tables until the instruction is found then return
|
||||
* the string and size
|
||||
*
|
||||
* inputs:
|
||||
* pointer to a word stream,
|
||||
* disassember table and size
|
||||
* returns:
|
||||
* instruction size - negative is error
|
||||
* disassembly string - on error will constain error string
|
||||
*/
|
||||
static int Disassemble(const uint16 *data, char **dis,
|
||||
TABLE_ENTRY_TYPE *type, const Pool *table,
|
||||
int table_size, Dis_info *info)
|
||||
{
|
||||
for (int i = 0; i < table_size; i++) {
|
||||
uint64 op_code = extract_op_code_value(data,
|
||||
table[i].instructions_size);
|
||||
if ((op_code & table[i].mask) == table[i].value) {
|
||||
/* possible match */
|
||||
conditional_function cond = table[i].condition;
|
||||
if ((cond == NULL) || cond(op_code)) {
|
||||
if (table[i].type == pool) {
|
||||
return Disassemble(data, dis, type,
|
||||
table[i].next_table,
|
||||
table[i].next_table_size,
|
||||
info);
|
||||
} else if ((table[i].type == instruction) ||
|
||||
(table[i].type == call_instruction) ||
|
||||
(table[i].type == branch_instruction) ||
|
||||
(table[i].type == return_instruction)) {
|
||||
disassembly_function dis_fn = table[i].disassembly;
|
||||
if (dis_fn == 0) {
|
||||
*dis = g_strdup(
|
||||
"disassembler failure - bad table entry");
|
||||
return -6;
|
||||
}
|
||||
*type = table[i].type;
|
||||
*dis = dis_fn(op_code, info);
|
||||
return table[i].instructions_size;
|
||||
} else {
|
||||
*dis = g_strdup("reserved instruction");
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*dis = g_strdup("failed to disassemble");
|
||||
return -1; /* failed to disassemble */
|
||||
}
|
||||
|
||||
|
||||
static bool nanomips_dis(const uint16_t *data, char **buf, Dis_info *info)
|
||||
{
|
||||
TABLE_ENTRY_TYPE type;
|
||||
|
||||
+14
-79
@@ -2190,22 +2190,7 @@ static const char *csr_name(int csrno)
|
||||
case 0x0383: return "mibound";
|
||||
case 0x0384: return "mdbase";
|
||||
case 0x0385: return "mdbound";
|
||||
case 0x03a0: return "pmpcfg0";
|
||||
case 0x03a1: return "pmpcfg1";
|
||||
case 0x03a2: return "pmpcfg2";
|
||||
case 0x03a3: return "pmpcfg3";
|
||||
case 0x03a4: return "pmpcfg4";
|
||||
case 0x03a5: return "pmpcfg5";
|
||||
case 0x03a6: return "pmpcfg6";
|
||||
case 0x03a7: return "pmpcfg7";
|
||||
case 0x03a8: return "pmpcfg8";
|
||||
case 0x03a9: return "pmpcfg9";
|
||||
case 0x03aa: return "pmpcfg10";
|
||||
case 0x03ab: return "pmpcfg11";
|
||||
case 0x03ac: return "pmpcfg12";
|
||||
case 0x03ad: return "pmpcfg13";
|
||||
case 0x03ae: return "pmpcfg14";
|
||||
case 0x03af: return "pmpcfg15";
|
||||
case 0x03a0: return "pmpcfg3";
|
||||
case 0x03b0: return "pmpaddr0";
|
||||
case 0x03b1: return "pmpaddr1";
|
||||
case 0x03b2: return "pmpaddr2";
|
||||
@@ -2222,54 +2207,6 @@ static const char *csr_name(int csrno)
|
||||
case 0x03bd: return "pmpaddr13";
|
||||
case 0x03be: return "pmpaddr14";
|
||||
case 0x03bf: return "pmpaddr15";
|
||||
case 0x03c0: return "pmpaddr16";
|
||||
case 0x03c1: return "pmpaddr17";
|
||||
case 0x03c2: return "pmpaddr18";
|
||||
case 0x03c3: return "pmpaddr19";
|
||||
case 0x03c4: return "pmpaddr20";
|
||||
case 0x03c5: return "pmpaddr21";
|
||||
case 0x03c6: return "pmpaddr22";
|
||||
case 0x03c7: return "pmpaddr23";
|
||||
case 0x03c8: return "pmpaddr24";
|
||||
case 0x03c9: return "pmpaddr25";
|
||||
case 0x03ca: return "pmpaddr26";
|
||||
case 0x03cb: return "pmpaddr27";
|
||||
case 0x03cc: return "pmpaddr28";
|
||||
case 0x03cd: return "pmpaddr29";
|
||||
case 0x03ce: return "pmpaddr30";
|
||||
case 0x03cf: return "pmpaddr31";
|
||||
case 0x03d0: return "pmpaddr32";
|
||||
case 0x03d1: return "pmpaddr33";
|
||||
case 0x03d2: return "pmpaddr34";
|
||||
case 0x03d3: return "pmpaddr35";
|
||||
case 0x03d4: return "pmpaddr36";
|
||||
case 0x03d5: return "pmpaddr37";
|
||||
case 0x03d6: return "pmpaddr38";
|
||||
case 0x03d7: return "pmpaddr39";
|
||||
case 0x03d8: return "pmpaddr40";
|
||||
case 0x03d9: return "pmpaddr41";
|
||||
case 0x03da: return "pmpaddr42";
|
||||
case 0x03db: return "pmpaddr43";
|
||||
case 0x03dc: return "pmpaddr44";
|
||||
case 0x03dd: return "pmpaddr45";
|
||||
case 0x03de: return "pmpaddr46";
|
||||
case 0x03df: return "pmpaddr47";
|
||||
case 0x03e0: return "pmpaddr48";
|
||||
case 0x03e1: return "pmpaddr49";
|
||||
case 0x03e2: return "pmpaddr50";
|
||||
case 0x03e3: return "pmpaddr51";
|
||||
case 0x03e4: return "pmpaddr52";
|
||||
case 0x03e5: return "pmpaddr53";
|
||||
case 0x03e6: return "pmpaddr54";
|
||||
case 0x03e7: return "pmpaddr55";
|
||||
case 0x03e8: return "pmpaddr56";
|
||||
case 0x03e9: return "pmpaddr57";
|
||||
case 0x03ea: return "pmpaddr58";
|
||||
case 0x03eb: return "pmpaddr59";
|
||||
case 0x03ec: return "pmpaddr60";
|
||||
case 0x03ed: return "pmpaddr61";
|
||||
case 0x03ee: return "pmpaddr62";
|
||||
case 0x03ef: return "pmpaddr63";
|
||||
case 0x0780: return "mtohost";
|
||||
case 0x0781: return "mfromhost";
|
||||
case 0x0782: return "mreset";
|
||||
@@ -5255,21 +5192,19 @@ print_insn_riscv(bfd_vma memaddr, struct disassemble_info *info, rv_isa isa)
|
||||
}
|
||||
}
|
||||
|
||||
if (info->show_opcodes) {
|
||||
switch (len) {
|
||||
case 2:
|
||||
(*info->fprintf_func)(info->stream, INST_FMT_2, inst);
|
||||
break;
|
||||
case 4:
|
||||
(*info->fprintf_func)(info->stream, INST_FMT_4, inst);
|
||||
break;
|
||||
case 6:
|
||||
(*info->fprintf_func)(info->stream, INST_FMT_6, inst);
|
||||
break;
|
||||
default:
|
||||
(*info->fprintf_func)(info->stream, INST_FMT_8, inst);
|
||||
break;
|
||||
}
|
||||
switch (len) {
|
||||
case 2:
|
||||
(*info->fprintf_func)(info->stream, INST_FMT_2, inst);
|
||||
break;
|
||||
case 4:
|
||||
(*info->fprintf_func)(info->stream, INST_FMT_4, inst);
|
||||
break;
|
||||
case 6:
|
||||
(*info->fprintf_func)(info->stream, INST_FMT_6, inst);
|
||||
break;
|
||||
default:
|
||||
(*info->fprintf_func)(info->stream, INST_FMT_8, inst);
|
||||
break;
|
||||
}
|
||||
|
||||
disasm_inst(buf, sizeof(buf), isa, memaddr, inst,
|
||||
|
||||
+19
-32
@@ -36,6 +36,22 @@ and will cause a warning.
|
||||
The replacement for the ``nodelay`` short-form boolean option is ``nodelay=on``
|
||||
rather than ``delay=off``.
|
||||
|
||||
``-smp`` ("parameter=0" SMP configurations) (since 6.2)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Specified CPU topology parameters must be greater than zero.
|
||||
|
||||
In the SMP configuration, users should either provide a CPU topology
|
||||
parameter with a reasonable value (greater than zero) or just omit it
|
||||
and QEMU will compute the missing value.
|
||||
|
||||
However, historically it was implicitly allowed for users to provide
|
||||
a parameter with zero value, which is meaningless and could also possibly
|
||||
cause unexpected results in the -smp parsing. So support for this kind of
|
||||
configurations (e.g. -smp 8,sockets=0) is deprecated since 6.2 and will
|
||||
be removed in the near future, users have to ensure that all the topology
|
||||
members described with -smp are greater than zero.
|
||||
|
||||
Plugin argument passing through ``arg=<string>`` (since 6.1)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
@@ -47,20 +63,6 @@ as short-form boolean values, and passed to plugins as ``arg_name=on``.
|
||||
However, short-form booleans are deprecated and full explicit ``arg_name=on``
|
||||
form is preferred.
|
||||
|
||||
``-smp`` (Unsupported "parameter=1" SMP configurations) (since 9.0)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Specified CPU topology parameters must be supported by the machine.
|
||||
|
||||
In the SMP configuration, users should provide the CPU topology parameters that
|
||||
are supported by the target machine.
|
||||
|
||||
However, historically it was allowed for users to specify the unsupported
|
||||
topology parameter as "1", which is meaningless. So support for this kind of
|
||||
configurations (e.g. -smp drawers=1,books=1,clusters=1 for x86 PC machine) is
|
||||
marked deprecated since 9.0, users have to ensure that all the topology members
|
||||
described with -smp are supported by the target machine.
|
||||
|
||||
User-mode emulator command line arguments
|
||||
-----------------------------------------
|
||||
|
||||
@@ -237,28 +239,13 @@ The Nios II architecture is orphan.
|
||||
The machine is no longer in existence and has been long unmaintained
|
||||
in QEMU. This also holds for the TC51828 16MiB flash that it uses.
|
||||
|
||||
``pseries-2.1`` up to ``pseries-2.12`` (since 9.0)
|
||||
``pseries-2.1`` up to ``pseries-2.11`` (since 9.0)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Older pseries machines before version 3.0 have undergone many changes
|
||||
Older pseries machines before version 2.12 have undergone many changes
|
||||
to correct issues, mostly regarding migration compatibility. These are
|
||||
no longer maintained and removing them will make the code easier to
|
||||
read and maintain. Use versions 3.0 and above as a replacement.
|
||||
|
||||
Arm machines ``akita``, ``borzoi``, ``cheetah``, ``connex``, ``mainstone``, ``n800``, ``n810``, ``spitz``, ``terrier``, ``tosa``, ``verdex``, ``z2`` (since 9.0)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
QEMU includes models of some machine types where the QEMU code that
|
||||
emulates their SoCs is very old and unmaintained. This code is now
|
||||
blocking our ability to move forward with various changes across
|
||||
the codebase, and over many years nobody has been interested in
|
||||
trying to modernise it. We don't expect any of these machines to have
|
||||
a large number of users, because they're all modelling hardware that
|
||||
has now passed away into history. We are therefore dropping support
|
||||
for all machine types using the PXA2xx and OMAP2 SoCs. We are also
|
||||
dropping the ``cheetah`` OMAP1 board, because we don't have any
|
||||
test images for it and don't know of anybody who does; the ``sx1``
|
||||
and ``sx1-v1`` OMAP1 machines remain supported for now.
|
||||
read and maintain. Use versions 2.12 and above as a replacement.
|
||||
|
||||
Backend options
|
||||
---------------
|
||||
|
||||
@@ -489,21 +489,6 @@ The ``-singlestep`` option has been turned into an accelerator property,
|
||||
and given a name that better reflects what it actually does.
|
||||
Use ``-accel tcg,one-insn-per-tb=on`` instead.
|
||||
|
||||
``-smp`` ("parameter=0" SMP configurations) (removed in 9.0)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Specified CPU topology parameters must be greater than zero.
|
||||
|
||||
In the SMP configuration, users should either provide a CPU topology
|
||||
parameter with a reasonable value (greater than zero) or just omit it
|
||||
and QEMU will compute the missing value.
|
||||
|
||||
However, historically it was implicitly allowed for users to provide
|
||||
a parameter with zero value, which is meaningless and could also possibly
|
||||
cause unexpected results in the -smp parsing. So support for this kind of
|
||||
configurations (e.g. -smp 8,sockets=0) is removed since 9.0, users have
|
||||
to ensure that all the topology members described with -smp are greater
|
||||
than zero.
|
||||
|
||||
User-mode emulator command line arguments
|
||||
-----------------------------------------
|
||||
|
||||
+1
-1
@@ -88,7 +88,7 @@ default_role = 'any'
|
||||
|
||||
# General information about the project.
|
||||
project = u'QEMU'
|
||||
copyright = u'2024, The QEMU Project Developers'
|
||||
copyright = u'2023, The QEMU Project Developers'
|
||||
author = u'The QEMU Project Developers'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
|
||||
+16
-39
@@ -1,48 +1,26 @@
|
||||
=============================================================================
|
||||
ACPI/SMBIOS avocado tests using biosbits
|
||||
=============================================================================
|
||||
************
|
||||
Introduction
|
||||
************
|
||||
|
||||
Biosbits is a software written by Josh Triplett that can be downloaded
|
||||
from https://biosbits.org/. The github codebase can be found
|
||||
`here <https://github.com/biosbits/bits/tree/master>`__. It is a software that
|
||||
executes the bios components such as acpi and smbios tables directly through
|
||||
acpica bios interpreter (a freely available C based library written by Intel,
|
||||
`here <https://github.com/biosbits/bits/tree/master>`__. It is a software that executes
|
||||
the bios components such as acpi and smbios tables directly through acpica
|
||||
bios interpreter (a freely available C based library written by Intel,
|
||||
downloadable from https://acpica.org/ and is included with biosbits) without an
|
||||
operating system getting involved in between. Bios-bits has python integration
|
||||
with grub so actual routines that executes bios components can be written in
|
||||
python instead of bash-ish (grub's native scripting language).
|
||||
operating system getting involved in between.
|
||||
There are several advantages to directly testing the bios in a real physical
|
||||
machine or in a VM as opposed to indirectly discovering bios issues through the
|
||||
operating system (the OS). Operating systems tend to bypass bios problems and
|
||||
hide them from the end user. We have more control of what we wanted to test and
|
||||
how by being as close to the bios on a running system as possible without a
|
||||
complicated software component such as an operating system coming in between.
|
||||
Another issue is that we cannot exercise bios components such as ACPI and
|
||||
SMBIOS without being in the highest hardware privilege level, ring 0 for
|
||||
example in case of x86. Since the OS executes from ring 0 whereas normal user
|
||||
land software resides in unprivileged ring 3, operating system must be modified
|
||||
in order to write our test routines that exercise and test the bios. This is
|
||||
not possible in all cases. Lastly, test frameworks and routines are preferably
|
||||
written using a high level scripting language such as python. OSes and
|
||||
OS modules are generally written using low level languages such as C and
|
||||
low level assembly machine language. Writing test routines in a low level
|
||||
language makes things more cumbersome. These and other reasons makes using
|
||||
bios-bits very attractive for testing bioses. More details on the inspiration
|
||||
for developing biosbits and its real life uses can be found in [#a]_ and [#b]_.
|
||||
|
||||
machine or VM as opposed to indirectly discovering bios issues through the
|
||||
operating system. For one thing, the OSes tend to hide bios problems from the
|
||||
end user. The other is that we have more control of what we wanted to test
|
||||
and how by directly using acpica interpreter on top of the bios on a running
|
||||
system. More details on the inspiration for developing biosbits and its real
|
||||
life uses can be found in [#a]_ and [#b]_.
|
||||
For QEMU, we maintain a fork of bios bits in gitlab along with all the
|
||||
dependent submodules `here <https://gitlab.com/qemu-project/biosbits-bits>`__.
|
||||
dependent submodules here: https://gitlab.com/qemu-project/biosbits-bits
|
||||
This fork contains numerous fixes, a newer acpica and changes specific to
|
||||
running this avocado QEMU tests using bits. The author of this document
|
||||
is the sole maintainer of the QEMU fork of bios bits repository. For more
|
||||
information, please see author's `FOSDEM talk on this bios-bits based test
|
||||
framework <https://fosdem.org/2024/schedule/event/fosdem-2024-2262-exercising-qemu-generated-acpi-smbios-tables-using-biosbits-from-within-a-guest-vm-/>`__.
|
||||
|
||||
*********************************
|
||||
Description of the test framework
|
||||
*********************************
|
||||
is the sole maintainer of the QEMU fork of bios bits repo.
|
||||
|
||||
Under the directory ``tests/avocado/``, ``acpi-bits.py`` is a QEMU avocado
|
||||
test that drives all this.
|
||||
@@ -142,9 +120,8 @@ Under ``tests/avocado/`` as the root we have:
|
||||
(b) Add a SPDX license header.
|
||||
(c) Perform modifications to the test.
|
||||
|
||||
Commits (a), (b) and (c) preferably should go under separate commits so that
|
||||
the original test script and the changes we have made are separated and
|
||||
clear. (a) and (b) can sometimes be combined into a single step.
|
||||
Commits (a), (b) and (c) should go under separate commits so that the original
|
||||
test script and the changes we have made are separated and clear.
|
||||
|
||||
The test framework will then use your modified test script to run the test.
|
||||
No further changes would be needed. Please check the logs to make sure that
|
||||
@@ -164,4 +141,4 @@ References:
|
||||
-----------
|
||||
.. [#a] https://blog.linuxplumbersconf.org/2011/ocw/system/presentations/867/original/bits.pdf
|
||||
.. [#b] https://www.youtube.com/watch?v=36QIepyUuhg
|
||||
.. [#c] https://fosdem.org/2024/schedule/event/fosdem-2024-2262-exercising-qemu-generated-acpi-smbios-tables-using-biosbits-from-within-a-guest-vm-/
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ The only guarantees that you can rely upon in this case are:
|
||||
ordinary accesses instead cause data races if they are concurrent with
|
||||
other accesses of which at least one is a write. In order to ensure this,
|
||||
the compiler will not optimize accesses out of existence, create unsolicited
|
||||
accesses, or perform other similar optimizations.
|
||||
accesses, or perform other similar optimzations.
|
||||
|
||||
- acquire operations will appear to happen, with respect to the other
|
||||
components of the system, before all the LOAD or STORE operations
|
||||
|
||||
@@ -115,7 +115,7 @@ CI pipeline.
|
||||
QEMU_JOB_SKIPPED
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
The job is not reliably successful in general, so is not
|
||||
The job is not reliably successsful in general, so is not
|
||||
currently suitable to be run by default. Ideally this should
|
||||
be a temporary marker until the problems can be addressed, or
|
||||
the job permanently removed.
|
||||
|
||||
@@ -279,10 +279,6 @@ You can change the multiplier and divider of a clock at runtime,
|
||||
so you can use this to model clock controller devices which
|
||||
have guest-programmable frequency multipliers or dividers.
|
||||
|
||||
Similarly to ``clock_set()``, ``clock_set_mul_div()`` returns ``true`` if
|
||||
the clock state was modified; that is, if the multiplier or the diviser
|
||||
or both were changed by the call.
|
||||
|
||||
Note that ``clock_set_mul_div()`` does not automatically call
|
||||
``clock_propagate()``. If you make a runtime change to the
|
||||
multiplier or divider you must call clock_propagate() yourself.
|
||||
|
||||
@@ -1,147 +0,0 @@
|
||||
CheckPoint and Restart (CPR)
|
||||
============================
|
||||
|
||||
CPR is the umbrella name for a set of migration modes in which the
|
||||
VM is migrated to a new QEMU instance on the same host. It is
|
||||
intended for use when the goal is to update host software components
|
||||
that run the VM, such as QEMU or even the host kernel. At this time,
|
||||
cpr-reboot is the only available mode.
|
||||
|
||||
Because QEMU is restarted on the same host, with access to the same
|
||||
local devices, CPR is allowed in certain cases where normal migration
|
||||
would be blocked. However, the user must not modify the contents of
|
||||
guest block devices between quitting old QEMU and starting new QEMU.
|
||||
|
||||
CPR unconditionally stops VM execution before memory is saved, and
|
||||
thus does not depend on any form of dirty page tracking.
|
||||
|
||||
cpr-reboot mode
|
||||
---------------
|
||||
|
||||
In this mode, QEMU stops the VM, and writes VM state to the migration
|
||||
URI, which will typically be a file. After quitting QEMU, the user
|
||||
resumes by running QEMU with the ``-incoming`` option. Because the
|
||||
old and new QEMU instances are not active concurrently, the URI cannot
|
||||
be a type that streams data from one instance to the other.
|
||||
|
||||
Guest RAM can be saved in place if backed by shared memory, or can be
|
||||
copied to a file. The former is more efficient and is therefore
|
||||
preferred.
|
||||
|
||||
After state and memory are saved, the user may update userland host
|
||||
software before restarting QEMU and resuming the VM. Further, if
|
||||
the RAM is backed by persistent shared memory, such as a DAX device,
|
||||
then the user may reboot to a new host kernel before restarting QEMU.
|
||||
|
||||
This mode supports VFIO devices provided the user first puts the
|
||||
guest in the suspended runstate, such as by issuing the
|
||||
``guest-suspend-ram`` command to the QEMU guest agent. The agent
|
||||
must be pre-installed in the guest, and the guest must support
|
||||
suspend to RAM. Beware that suspension can take a few seconds, so
|
||||
the user should poll to see the suspended state before proceeding
|
||||
with the CPR operation.
|
||||
|
||||
Usage
|
||||
^^^^^
|
||||
|
||||
It is recommended that guest RAM be backed with some type of shared
|
||||
memory, such as ``memory-backend-file,share=on``, and that the
|
||||
``x-ignore-shared`` capability be set. This combination allows memory
|
||||
to be saved in place. Otherwise, after QEMU stops the VM, all guest
|
||||
RAM is copied to the migration URI.
|
||||
|
||||
Outgoing:
|
||||
* Set the migration mode parameter to ``cpr-reboot``.
|
||||
* Set the ``x-ignore-shared`` capability if desired.
|
||||
* Issue the ``migrate`` command. It is recommended the the URI be a
|
||||
``file`` type, but one can use other types such as ``exec``,
|
||||
provided the command captures all the data from the outgoing side,
|
||||
and provides all the data to the incoming side.
|
||||
* Quit when QEMU reaches the postmigrate state.
|
||||
|
||||
Incoming:
|
||||
* Start QEMU with the ``-incoming defer`` option.
|
||||
* Set the migration mode parameter to ``cpr-reboot``.
|
||||
* Set the ``x-ignore-shared`` capability if desired.
|
||||
* Issue the ``migrate-incoming`` command.
|
||||
* If the VM was running when the outgoing ``migrate`` command was
|
||||
issued, then QEMU automatically resumes VM execution.
|
||||
|
||||
Example 1
|
||||
^^^^^^^^^
|
||||
::
|
||||
|
||||
# qemu-kvm -monitor stdio
|
||||
-object memory-backend-file,id=ram0,size=4G,mem-path=/dev/dax0.0,align=2M,share=on -m 4G
|
||||
...
|
||||
|
||||
(qemu) info status
|
||||
VM status: running
|
||||
(qemu) migrate_set_parameter mode cpr-reboot
|
||||
(qemu) migrate_set_capability x-ignore-shared on
|
||||
(qemu) migrate -d file:vm.state
|
||||
(qemu) info status
|
||||
VM status: paused (postmigrate)
|
||||
(qemu) quit
|
||||
|
||||
### optionally update kernel and reboot
|
||||
# systemctl kexec
|
||||
kexec_core: Starting new kernel
|
||||
...
|
||||
|
||||
# qemu-kvm ... -incoming defer
|
||||
(qemu) info status
|
||||
VM status: paused (inmigrate)
|
||||
(qemu) migrate_set_parameter mode cpr-reboot
|
||||
(qemu) migrate_set_capability x-ignore-shared on
|
||||
(qemu) migrate_incoming file:vm.state
|
||||
(qemu) info status
|
||||
VM status: running
|
||||
|
||||
Example 2: VFIO
|
||||
^^^^^^^^^^^^^^^
|
||||
::
|
||||
|
||||
# qemu-kvm -monitor stdio
|
||||
-object memory-backend-file,id=ram0,size=4G,mem-path=/dev/dax0.0,align=2M,share=on -m 4G
|
||||
-device vfio-pci, ...
|
||||
-chardev socket,id=qga0,path=qga.sock,server=on,wait=off
|
||||
-device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0
|
||||
...
|
||||
|
||||
(qemu) info status
|
||||
VM status: running
|
||||
|
||||
# echo '{"execute":"guest-suspend-ram"}' | ncat --send-only -U qga.sock
|
||||
|
||||
(qemu) info status
|
||||
VM status: paused (suspended)
|
||||
(qemu) migrate_set_parameter mode cpr-reboot
|
||||
(qemu) migrate_set_capability x-ignore-shared on
|
||||
(qemu) migrate -d file:vm.state
|
||||
(qemu) info status
|
||||
VM status: paused (postmigrate)
|
||||
(qemu) quit
|
||||
|
||||
### optionally update kernel and reboot
|
||||
# systemctl kexec
|
||||
kexec_core: Starting new kernel
|
||||
...
|
||||
|
||||
# qemu-kvm ... -incoming defer
|
||||
(qemu) info status
|
||||
VM status: paused (inmigrate)
|
||||
(qemu) migrate_set_parameter mode cpr-reboot
|
||||
(qemu) migrate_set_capability x-ignore-shared on
|
||||
(qemu) migrate_incoming file:vm.state
|
||||
(qemu) info status
|
||||
VM status: paused (suspended)
|
||||
(qemu) system_wakeup
|
||||
(qemu) info status
|
||||
VM status: running
|
||||
|
||||
Caveats
|
||||
^^^^^^^
|
||||
|
||||
cpr-reboot mode may not be used with postcopy, background-snapshot,
|
||||
or COLO.
|
||||
@@ -10,5 +10,3 @@ Migration has plenty of features to support different use cases.
|
||||
dirty-limit
|
||||
vfio
|
||||
virtio
|
||||
mapped-ram
|
||||
CPR
|
||||
|
||||
@@ -44,8 +44,7 @@ over any transport.
|
||||
- file migration: do the migration using a file that is passed to QEMU
|
||||
by path. A file offset option is supported to allow a management
|
||||
application to add its own metadata to the start of the file without
|
||||
QEMU interference. Note that QEMU does not flush cached file
|
||||
data/metadata at the end of migration.
|
||||
QEMU interference.
|
||||
|
||||
In addition, support is included for migration using RDMA, which
|
||||
transports the page data using ``RDMA``, where the hardware takes care of
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
Mapped-ram
|
||||
==========
|
||||
|
||||
Mapped-ram is a new stream format for the RAM section designed to
|
||||
supplement the existing ``file:`` migration and make it compatible
|
||||
with ``multifd``. This enables parallel migration of a guest's RAM to
|
||||
a file.
|
||||
|
||||
The core of the feature is to ensure that RAM pages are mapped
|
||||
directly to offsets in the resulting migration file. This enables the
|
||||
``multifd`` threads to write exclusively to those offsets even if the
|
||||
guest is constantly dirtying pages (i.e. live migration). Another
|
||||
benefit is that the resulting file will have a bounded size, since
|
||||
pages which are dirtied multiple times will always go to a fixed
|
||||
location in the file, rather than constantly being added to a
|
||||
sequential stream. Having the pages at fixed offsets also allows the
|
||||
usage of O_DIRECT for save/restore of the migration stream as the
|
||||
pages are ensured to be written respecting O_DIRECT alignment
|
||||
restrictions (direct-io support not yet implemented).
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
On both source and destination, enable the ``multifd`` and
|
||||
``mapped-ram`` capabilities:
|
||||
|
||||
``migrate_set_capability multifd on``
|
||||
|
||||
``migrate_set_capability mapped-ram on``
|
||||
|
||||
Use a ``file:`` URL for migration:
|
||||
|
||||
``migrate file:/path/to/migration/file``
|
||||
|
||||
Mapped-ram migration is best done non-live, i.e. by stopping the VM on
|
||||
the source side before migrating.
|
||||
|
||||
Use-cases
|
||||
---------
|
||||
|
||||
The mapped-ram feature was designed for use cases where the migration
|
||||
stream will be directed to a file in the filesystem and not
|
||||
immediately restored on the destination VM [#]_. These could be
|
||||
thought of as snapshots. We can further categorize them into live and
|
||||
non-live.
|
||||
|
||||
- Non-live snapshot
|
||||
|
||||
If the use case requires a VM to be stopped before taking a snapshot,
|
||||
that's the ideal scenario for mapped-ram migration. Not having to
|
||||
track dirty pages, the migration will write the RAM pages to the disk
|
||||
as fast as it can.
|
||||
|
||||
Note: if a snapshot is taken of a running VM, but the VM will be
|
||||
stopped after the snapshot by the admin, then consider stopping it
|
||||
right before the snapshot to take benefit of the performance gains
|
||||
mentioned above.
|
||||
|
||||
- Live snapshot
|
||||
|
||||
If the use case requires that the VM keeps running during and after
|
||||
the snapshot operation, then mapped-ram migration can still be used,
|
||||
but will be less performant. Other strategies such as
|
||||
background-snapshot should be evaluated as well. One benefit of
|
||||
mapped-ram in this scenario is portability since background-snapshot
|
||||
depends on async dirty tracking (KVM_GET_DIRTY_LOG) which is not
|
||||
supported outside of Linux.
|
||||
|
||||
.. [#] While this same effect could be obtained with the usage of
|
||||
snapshots or the ``file:`` migration alone, mapped-ram provides
|
||||
a performance increase for VMs with larger RAM sizes (10s to
|
||||
100s of GiBs), specially if the VM has been stopped beforehand.
|
||||
|
||||
RAM section format
|
||||
------------------
|
||||
|
||||
Instead of having a sequential stream of pages that follow the
|
||||
RAMBlock headers, the dirty pages for a RAMBlock follow its header
|
||||
instead. This ensures that each RAM page has a fixed offset in the
|
||||
resulting migration file.
|
||||
|
||||
A bitmap is introduced to track which pages have been written in the
|
||||
migration file. Pages are written at a fixed location for every
|
||||
ramblock. Zero pages are ignored as they'd be zero in the destination
|
||||
migration as well.
|
||||
|
||||
::
|
||||
|
||||
Without mapped-ram: With mapped-ram:
|
||||
|
||||
--------------------- --------------------------------
|
||||
| ramblock 1 header | | ramblock 1 header |
|
||||
--------------------- --------------------------------
|
||||
| ramblock 2 header | | ramblock 1 mapped-ram header |
|
||||
--------------------- --------------------------------
|
||||
| ... | | padding to next 1MB boundary |
|
||||
--------------------- | ... |
|
||||
| ramblock n header | --------------------------------
|
||||
--------------------- | ramblock 1 pages |
|
||||
| RAM_SAVE_FLAG_EOS | | ... |
|
||||
--------------------- --------------------------------
|
||||
| stream of pages | | ramblock 2 header |
|
||||
| (iter 1) | --------------------------------
|
||||
| ... | | ramblock 2 mapped-ram header |
|
||||
--------------------- --------------------------------
|
||||
| RAM_SAVE_FLAG_EOS | | padding to next 1MB boundary |
|
||||
--------------------- | ... |
|
||||
| stream of pages | --------------------------------
|
||||
| (iter 2) | | ramblock 2 pages |
|
||||
| ... | | ... |
|
||||
--------------------- --------------------------------
|
||||
| ... | | ... |
|
||||
--------------------- --------------------------------
|
||||
| RAM_SAVE_FLAG_EOS |
|
||||
--------------------------------
|
||||
| ... |
|
||||
--------------------------------
|
||||
|
||||
where:
|
||||
- ramblock header: the generic information for a ramblock, such as
|
||||
idstr, used_len, etc.
|
||||
|
||||
- ramblock mapped-ram header: the information added by this feature:
|
||||
bitmap of pages written, bitmap size and offset of pages in the
|
||||
migration file.
|
||||
|
||||
Restrictions
|
||||
------------
|
||||
|
||||
Since pages are written to their relative offsets and out of order
|
||||
(due to the memory dirtying patterns), streaming channels such as
|
||||
sockets are not supported. A seekable channel such as a file is
|
||||
required. This can be verified in the QIOChannel by the presence of
|
||||
the QIO_CHANNEL_FEATURE_SEEKABLE.
|
||||
|
||||
The improvements brought by this feature apply only to guest physical
|
||||
RAM. Other types of memory such as VRAM are migrated as part of device
|
||||
states.
|
||||
@@ -1,119 +0,0 @@
|
||||
Nested PAPR API (aka KVM on PowerVM)
|
||||
====================================
|
||||
|
||||
This API aims at providing support to enable nested virtualization with
|
||||
KVM on PowerVM. While the existing support for nested KVM on PowerNV was
|
||||
introduced with cap-nested-hv option, however, with a slight design change,
|
||||
to enable this on papr/pseries, a new cap-nested-papr option is added. eg:
|
||||
|
||||
qemu-system-ppc64 -cpu POWER10 -machine pseries,cap-nested-papr=true ...
|
||||
|
||||
Work by:
|
||||
Michael Neuling <mikey@neuling.org>
|
||||
Vaibhav Jain <vaibhav@linux.ibm.com>
|
||||
Jordan Niethe <jniethe5@gmail.com>
|
||||
Harsh Prateek Bora <harshpb@linux.ibm.com>
|
||||
Shivaprasad G Bhat <sbhat@linux.ibm.com>
|
||||
Kautuk Consul <kconsul@linux.vnet.ibm.com>
|
||||
|
||||
Below taken from the kernel documentation:
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
This document explains how a guest operating system can act as a
|
||||
hypervisor and run nested guests through the use of hypercalls, if the
|
||||
hypervisor has implemented them. The terms L0, L1, and L2 are used to
|
||||
refer to different software entities. L0 is the hypervisor mode entity
|
||||
that would normally be called the "host" or "hypervisor". L1 is a
|
||||
guest virtual machine that is directly run under L0 and is initiated
|
||||
and controlled by L0. L2 is a guest virtual machine that is initiated
|
||||
and controlled by L1 acting as a hypervisor. A significant design change
|
||||
wrt existing API is that now the entire L2 state is maintained within L0.
|
||||
|
||||
Existing Nested-HV API
|
||||
======================
|
||||
|
||||
Linux/KVM has had support for Nesting as an L0 or L1 since 2018
|
||||
|
||||
The L0 code was added::
|
||||
|
||||
commit 8e3f5fc1045dc49fd175b978c5457f5f51e7a2ce
|
||||
Author: Paul Mackerras <paulus@ozlabs.org>
|
||||
Date: Mon Oct 8 16:31:03 2018 +1100
|
||||
KVM: PPC: Book3S HV: Framework and hcall stubs for nested virtualization
|
||||
|
||||
The L1 code was added::
|
||||
|
||||
commit 360cae313702cdd0b90f82c261a8302fecef030a
|
||||
Author: Paul Mackerras <paulus@ozlabs.org>
|
||||
Date: Mon Oct 8 16:31:04 2018 +1100
|
||||
KVM: PPC: Book3S HV: Nested guest entry via hypercall
|
||||
|
||||
This API works primarily using a signal hcall h_enter_nested(). This
|
||||
call made by the L1 to tell the L0 to start an L2 vCPU with the given
|
||||
state. The L0 then starts this L2 and runs until an L2 exit condition
|
||||
is reached. Once the L2 exits, the state of the L2 is given back to
|
||||
the L1 by the L0. The full L2 vCPU state is always transferred from
|
||||
and to L1 when the L2 is run. The L0 doesn't keep any state on the L2
|
||||
vCPU (except in the short sequence in the L0 on L1 -> L2 entry and L2
|
||||
-> L1 exit).
|
||||
|
||||
The only state kept by the L0 is the partition table. The L1 registers
|
||||
it's partition table using the h_set_partition_table() hcall. All
|
||||
other state held by the L0 about the L2s is cached state (such as
|
||||
shadow page tables).
|
||||
|
||||
The L1 may run any L2 or vCPU without first informing the L0. It
|
||||
simply starts the vCPU using h_enter_nested(). The creation of L2s and
|
||||
vCPUs is done implicitly whenever h_enter_nested() is called.
|
||||
|
||||
In this document, we call this existing API the v1 API.
|
||||
|
||||
New PAPR API
|
||||
===============
|
||||
|
||||
The new PAPR API changes from the v1 API such that the creating L2 and
|
||||
associated vCPUs is explicit. In this document, we call this the v2
|
||||
API.
|
||||
|
||||
h_enter_nested() is replaced with H_GUEST_VCPU_RUN(). Before this can
|
||||
be called the L1 must explicitly create the L2 using h_guest_create()
|
||||
and any associated vCPUs() created with h_guest_create_vCPU(). Getting
|
||||
and setting vCPU state can also be performed using h_guest_{g|s}et
|
||||
hcall.
|
||||
|
||||
The basic execution flow is for an L1 to create an L2, run it, and
|
||||
delete it is:
|
||||
|
||||
- L1 and L0 negotiate capabilities with H_GUEST_{G,S}ET_CAPABILITIES()
|
||||
(normally at L1 boot time).
|
||||
|
||||
- L1 requests the L0 to create an L2 with H_GUEST_CREATE() and receives a token
|
||||
|
||||
- L1 requests the L0 to create an L2 vCPU with H_GUEST_CREATE_VCPU()
|
||||
|
||||
- L1 and L0 communicate the vCPU state using the H_GUEST_{G,S}ET() hcall
|
||||
|
||||
- L1 requests the L0 to run the vCPU using H_GUEST_RUN_VCPU() hcall
|
||||
|
||||
- L1 deletes L2 with H_GUEST_DELETE()
|
||||
|
||||
For more details, please refer:
|
||||
|
||||
[1] Linux Kernel documentation (upstream documentation commit):
|
||||
|
||||
commit 476652297f94a2e5e5ef29e734b0da37ade94110
|
||||
Author: Michael Neuling <mikey@neuling.org>
|
||||
Date: Thu Sep 14 13:06:00 2023 +1000
|
||||
|
||||
docs: powerpc: Document nested KVM on POWER
|
||||
|
||||
Document support for nested KVM on POWER using the existing API as well
|
||||
as the new PAPR API. This includes the new HCALL interface and how it
|
||||
used by KVM.
|
||||
|
||||
Signed-off-by: Michael Neuling <mikey@neuling.org>
|
||||
Signed-off-by: Jordan Niethe <jniethe5@gmail.com>
|
||||
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
|
||||
Link: https://msgid.link/20230914030600.16993-12-jniethe5@gmail.com
|
||||
@@ -996,8 +996,7 @@ line "Features:", like this::
|
||||
|
||||
A tagged section begins with a paragraph that starts with one of the
|
||||
following words: "Note:"/"Notes:", "Since:", "Example:"/"Examples:",
|
||||
"Returns:", "Errors:", "TODO:". It ends with the start of a new
|
||||
section.
|
||||
"Returns:", "TODO:". It ends with the start of a new section.
|
||||
|
||||
The second and subsequent lines of tagged sections must be indented
|
||||
like this::
|
||||
@@ -1008,9 +1007,6 @@ like this::
|
||||
# Duis aute irure dolor in reprehenderit in voluptate velit esse
|
||||
# cillum dolore eu fugiat nulla pariatur.
|
||||
|
||||
"Returns" and "Errors" sections are only valid for commands. They
|
||||
document the success and the error response, respectively.
|
||||
|
||||
A "Since: x.y.z" tagged section lists the release that introduced the
|
||||
definition.
|
||||
|
||||
|
||||
@@ -357,7 +357,8 @@ probes::
|
||||
|
||||
scripts/tracetool.py --backends=dtrace --format=stap \
|
||||
--binary path/to/qemu-binary \
|
||||
--probe-prefix qemu.system.x86_64 \
|
||||
--target-type system \
|
||||
--target-name x86_64 \
|
||||
--group=all \
|
||||
trace-events-all \
|
||||
qemu.stp
|
||||
|
||||
@@ -66,13 +66,12 @@ Then, in a different terminal::
|
||||
"version": {
|
||||
"qemu": {
|
||||
"micro": 50,
|
||||
"minor": 2,
|
||||
"major": 8
|
||||
"minor": 15,
|
||||
"major": 0
|
||||
},
|
||||
"package": ...
|
||||
"package": ""
|
||||
},
|
||||
"capabilities": [
|
||||
"oob"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -108,14 +107,10 @@ 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::
|
||||
|
||||
##
|
||||
# @hello-world:
|
||||
#
|
||||
# Since: 9.0
|
||||
##
|
||||
{ 'command': 'hello-world' }
|
||||
|
||||
The "command" keyword defines a new QMP command. It instructs QAPI to
|
||||
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
|
||||
generate any prototypes and the necessary code to marshal and unmarshal
|
||||
protocol data.
|
||||
|
||||
@@ -137,70 +132,57 @@ There are a few things to be noticed:
|
||||
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 QAPI
|
||||
4. We won't add the function's prototype. That's automatically done by the QAPI
|
||||
5. Printing to the terminal is discouraged for QMP commands, we do it here
|
||||
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,
|
||||
You're done. Now build qemu, run it as suggested in the "Testing" section,
|
||||
and then type the following QMP command::
|
||||
|
||||
{ "execute": "hello-world" }
|
||||
|
||||
Then check the terminal running QEMU and look for the "Hello, world" string. If
|
||||
Then check the terminal running qemu and look for the "Hello, world" string. If
|
||||
you don't see it then something went wrong.
|
||||
|
||||
|
||||
Arguments
|
||||
~~~~~~~~~
|
||||
|
||||
Let's add arguments to our "hello-world" command.
|
||||
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::
|
||||
|
||||
##
|
||||
# @hello-world:
|
||||
#
|
||||
# @message: message to be printed (default: "Hello, world!")
|
||||
#
|
||||
# @times: how many times to print the message (default: 1)
|
||||
#
|
||||
# Since: 9.0
|
||||
##
|
||||
{ 'command': 'hello-world',
|
||||
'data': { '*message': 'str', '*times': 'int' } }
|
||||
{ 'command': 'hello-world', 'data': { '*message': 'str' } }
|
||||
|
||||
Notice the new 'data' member in the schema. It specifies an argument
|
||||
'message' of QAPI type 'str', and an argument 'times' of QAPI type
|
||||
'int'. Also notice the asterisk, it's used to mark the argument
|
||||
optional.
|
||||
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,
|
||||
it's used to mark the argument optional (that means that you shouldn't use it
|
||||
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::
|
||||
|
||||
void qmp_hello_world(const char *message, bool has_times, int64_t times,
|
||||
Error **errp)
|
||||
void qmp_hello_world(const char *message, Error **errp)
|
||||
{
|
||||
if (!message) {
|
||||
message = "Hello, world";
|
||||
}
|
||||
if (!has_times) {
|
||||
times = 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < times; i++) {
|
||||
if (message) {
|
||||
printf("%s\n", message);
|
||||
} else {
|
||||
printf("Hello, world\n");
|
||||
}
|
||||
}
|
||||
|
||||
There are two important details to be noticed:
|
||||
|
||||
1. Optional arguments other than pointers are accompanied by a 'has\_'
|
||||
boolean, which is set if the optional argument is present or false
|
||||
otherwise
|
||||
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
|
||||
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::
|
||||
|
||||
{ "execute": "hello-world" }
|
||||
@@ -209,13 +191,13 @@ described in the "Testing" section and then send two commands::
|
||||
}
|
||||
}
|
||||
|
||||
{ "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,
|
||||
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.
|
||||
|
||||
|
||||
@@ -245,7 +227,7 @@ 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"
|
||||
Let's test the example above. Build qemu, run it as defined in the "Testing"
|
||||
section, and then issue the following command::
|
||||
|
||||
{ "execute": "hello-world", "arguments": { "message": "all you need is love" } }
|
||||
@@ -272,14 +254,44 @@ 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
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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::
|
||||
|
||||
##
|
||||
# @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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Now that the QMP command is in place, we can also make it available in the human
|
||||
monitor (HMP).
|
||||
|
||||
With the introduction of QAPI, HMP commands make QMP calls. Most of the
|
||||
time HMP commands are simple wrappers.
|
||||
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::
|
||||
|
||||
@@ -294,20 +306,18 @@ Here's the implementation of the "hello-world" HMP command::
|
||||
}
|
||||
}
|
||||
|
||||
Add it to monitor/hmp-cmds.c. Also, add its prototype to
|
||||
include/monitor/hmp.h.
|
||||
Also, you have to add the function's prototype to the hmp.h file.
|
||||
|
||||
There are four important points to be noticed:
|
||||
There are three important points to be noticed:
|
||||
|
||||
1. The "mon" and "qdict" arguments are mandatory for all HMP functions. The
|
||||
former is the monitor object. The latter is how the monitor passes
|
||||
arguments entered by the user to the command implementation
|
||||
2. We chose not to support the "times" argument in HMP
|
||||
3. hmp_hello_world() performs error checking. In this example we just call
|
||||
2. hmp_hello_world() performs error checking. In this example we just call
|
||||
hmp_handle_error() which prints a message to the user, but we could do
|
||||
more, like taking different actions depending on the error
|
||||
qmp_hello_world() returns
|
||||
4. The "err" variable must be initialized to NULL before performing the
|
||||
3. The "err" variable must be initialized to NULL before performing the
|
||||
QMP call
|
||||
|
||||
There's one last step to actually make the command available to monitor users,
|
||||
@@ -330,17 +340,17 @@ 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
|
||||
HMP's "help" command.
|
||||
|
||||
Please check the "-monitor" command-line option to know how to open a user
|
||||
Please, check the "-monitor" command-line option to know how to open a user
|
||||
monitor.
|
||||
|
||||
|
||||
Writing more complex commands
|
||||
-----------------------------
|
||||
|
||||
A QMP command is capable of returning any data QAPI supports like integers,
|
||||
A QMP command is capable of returning any data the QAPI supports like integers,
|
||||
strings, booleans, enumerations and user defined types.
|
||||
|
||||
In this section we will focus on user defined types. Please check the QAPI
|
||||
In this section we will focus on user defined types. Please, check the QAPI
|
||||
documentation for information about the other types.
|
||||
|
||||
|
||||
@@ -362,7 +372,7 @@ data, it is not expected that machines will need to parse the result.
|
||||
The overhead of defining a fine grained QAPI type for the data may not
|
||||
be justified by the potential benefit. In such cases, it is permitted
|
||||
to have a command return a simple string that contains formatted data,
|
||||
however, it is mandatory for the command to be marked unstable.
|
||||
however, it is mandatory for the command to use the 'x-' name prefix.
|
||||
This indicates that the command is not guaranteed to be long term
|
||||
stable / liable to change in future and is not following QAPI design
|
||||
best practices. An example where this approach is taken is the QMP
|
||||
@@ -376,207 +386,302 @@ an illustration.
|
||||
User Defined Types
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
For this example we will write the query-option-roms command, which
|
||||
returns information about ROMs loaded into the option ROM space. For
|
||||
more information about it, please check the "-option-rom" command-line
|
||||
option.
|
||||
FIXME This example needs to be redone after commit 6d32717
|
||||
|
||||
For each option ROM, we want to return two pieces of information: the
|
||||
ROM image's file name, and its bootindex, if any. We need to create a
|
||||
new QAPI type for that, as shown below::
|
||||
For this example we will write the query-alarm-clock command, which returns
|
||||
information about QEMU's timer alarm. For more information about it, please
|
||||
check the "-clock" command-line option.
|
||||
|
||||
We want to return two pieces of information. The first one is the alarm clock's
|
||||
name. The second one is when the next alarm will fire. The former information is
|
||||
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::
|
||||
|
||||
##
|
||||
# @OptionRomInfo:
|
||||
# @QemuAlarmClock
|
||||
#
|
||||
# @filename: option ROM image file name
|
||||
# QEMU alarm clock information.
|
||||
#
|
||||
# @bootindex: option ROM's bootindex
|
||||
# @clock-name: The alarm clock method's name.
|
||||
#
|
||||
# Since: 9.0
|
||||
# @next-deadline: The time (in nanoseconds) the next alarm will fire.
|
||||
#
|
||||
# Since: 1.0
|
||||
##
|
||||
{ 'struct': 'OptionRomInfo',
|
||||
'data': { 'filename': 'str', '*bootindex': 'int' } }
|
||||
{ 'type': 'QemuAlarmClock',
|
||||
'data': { 'clock-name': 'str', '*next-deadline': 'int' } }
|
||||
|
||||
The "struct" keyword defines a new QAPI type. Its "data" member
|
||||
contains the type's members. In this example our members are
|
||||
"filename" and "bootindex". The latter is optional.
|
||||
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-option-roms command::
|
||||
Now let's define the query-alarm-clock command::
|
||||
|
||||
##
|
||||
# @query-option-roms:
|
||||
# @query-alarm-clock
|
||||
#
|
||||
# Query information on ROMs loaded into the option ROM space.
|
||||
# Return information about QEMU's alarm clock.
|
||||
#
|
||||
# Returns: OptionRomInfo
|
||||
# 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: 9.0
|
||||
# Since: 1.0
|
||||
##
|
||||
{ 'command': 'query-option-roms',
|
||||
'returns': ['OptionRomInfo'] }
|
||||
{ '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.
|
||||
|
||||
Notice the syntax ['OptionRomInfo']". This should be read as "returns
|
||||
a list of OptionRomInfo".
|
||||
It's time to implement the qmp_query_alarm_clock() function, you can put it
|
||||
in the qemu-timer.c file::
|
||||
|
||||
It's time to implement the qmp_query_option_roms() function. Add to
|
||||
monitor/qmp-cmds.c::
|
||||
|
||||
OptionRomInfoList *qmp_query_option_roms(Error **errp)
|
||||
QemuAlarmClock *qmp_query_alarm_clock(Error **errp)
|
||||
{
|
||||
OptionRomInfoList *info_list = NULL;
|
||||
OptionRomInfoList **tailp = &info_list;
|
||||
OptionRomInfo *info;
|
||||
QemuAlarmClock *clock;
|
||||
int64_t deadline;
|
||||
|
||||
for (int i = 0; i < nb_option_roms; i++) {
|
||||
info = g_malloc0(sizeof(*info));
|
||||
info->filename = g_strdup(option_rom[i].name);
|
||||
info->has_bootindex = option_rom[i].bootindex >= 0;
|
||||
if (info->has_bootindex) {
|
||||
info->bootindex = option_rom[i].bootindex;
|
||||
}
|
||||
QAPI_LIST_APPEND(tailp, info);
|
||||
clock = g_malloc0(sizeof(*clock));
|
||||
|
||||
deadline = qemu_next_alarm_deadline();
|
||||
if (deadline > 0) {
|
||||
clock->has_next_deadline = true;
|
||||
clock->next_deadline = deadline;
|
||||
}
|
||||
clock->clock_name = g_strdup(alarm_timer->name);
|
||||
|
||||
return info_list;
|
||||
return clock;
|
||||
}
|
||||
|
||||
There are a number of things to be noticed:
|
||||
|
||||
1. Type OptionRomInfo is automatically generated by the QAPI framework,
|
||||
its members correspond to the type's specification in the schema
|
||||
file
|
||||
2. Type OptionRomInfoList is also generated. It's a singly linked
|
||||
list.
|
||||
3. As specified in the schema file, the function returns a
|
||||
OptionRomInfoList, and takes no arguments (besides the "errp" one,
|
||||
which is mandatory for all QMP functions)
|
||||
4. The returned object is dynamically allocated
|
||||
5. All strings are dynamically allocated. This is so because QAPI also
|
||||
generates a function to free its types and it cannot distinguish
|
||||
between dynamically or statically allocated strings
|
||||
6. Remember that "bootindex" is optional? As a non-pointer optional
|
||||
member, it comes with a 'has_bootindex' member that needs to be set
|
||||
1. The QemuAlarmClock type is automatically generated by the QAPI framework,
|
||||
its members correspond to the type's specification in the schema file
|
||||
2. As specified in the schema file, the function returns a QemuAlarmClock
|
||||
instance and takes no arguments (besides the "errp" one, which is mandatory
|
||||
for all QMP functions)
|
||||
3. The "clock" variable (which will point to our QAPI type instance) is
|
||||
allocated by the regular g_malloc0() function. Note that we chose to
|
||||
initialize the memory to zero. This is recommended for all QAPI types, as
|
||||
it helps avoiding bad surprises (specially with booleans)
|
||||
4. Remember that "next_deadline" is optional? Non-pointer optional
|
||||
members have a 'has_TYPE_NAME' member that should be properly set
|
||||
by the implementation, as shown above
|
||||
5. Even static strings, such as "alarm_timer->name", should be dynamically
|
||||
allocated by the implementation. This is so because the QAPI also generates
|
||||
a function to free its types and it cannot distinguish between dynamically
|
||||
or statically allocated strings
|
||||
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"
|
||||
Time to test the new command. Build qemu, run it as described in the "Testing"
|
||||
section and try this::
|
||||
|
||||
{ "execute": "query-option-rom" }
|
||||
{ "execute": "query-alarm-clock" }
|
||||
{
|
||||
"return": [
|
||||
{
|
||||
"filename": "kvmvapic.bin"
|
||||
}
|
||||
]
|
||||
"return": {
|
||||
"next-deadline": 2368219,
|
||||
"clock-name": "dynticks"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
The HMP command
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Here's the HMP counterpart of the query-option-roms command::
|
||||
Here's the HMP counterpart of the query-alarm-clock command::
|
||||
|
||||
void hmp_info_option_roms(Monitor *mon, const QDict *qdict)
|
||||
void hmp_info_alarm_clock(Monitor *mon)
|
||||
{
|
||||
QemuAlarmClock *clock;
|
||||
Error *err = NULL;
|
||||
OptionRomInfoList *info_list, *tail;
|
||||
OptionRomInfo *info;
|
||||
|
||||
info_list = qmp_query_option_roms(&err);
|
||||
clock = qmp_query_alarm_clock(&err);
|
||||
if (hmp_handle_error(mon, err)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (tail = info_list; tail; tail = tail->next) {
|
||||
info = tail->value;
|
||||
monitor_printf(mon, "%s", info->filename);
|
||||
if (info->has_bootindex) {
|
||||
monitor_printf(mon, " %" PRId64, info->bootindex);
|
||||
}
|
||||
monitor_printf(mon, "\n");
|
||||
monitor_printf(mon, "Alarm clock method in use: '%s'\n", clock->clock_name);
|
||||
if (clock->has_next_deadline) {
|
||||
monitor_printf(mon, "Next alarm will fire in %" PRId64 " nanoseconds\n",
|
||||
clock->next_deadline);
|
||||
}
|
||||
|
||||
qapi_free_OptionRomInfoList(info_list);
|
||||
qapi_free_QemuAlarmClock(clock);
|
||||
}
|
||||
|
||||
It's important to notice that hmp_info_option_roms() calls
|
||||
qapi_free_OptionRomInfoList() to free the data returned by
|
||||
qmp_query_option_roms(). For user defined types, QAPI will generate a
|
||||
qapi_free_QAPI_TYPE_NAME() function, and that's what you have to use to
|
||||
free the types you define and qapi_free_QAPI_TYPE_NAMEList() for list
|
||||
types (explained in the next section). If the QMP function returns a
|
||||
string, then you should g_free() to free it.
|
||||
It's important to notice that hmp_info_alarm_clock() calls
|
||||
qapi_free_QemuAlarmClock() to free the data returned by qmp_query_alarm_clock().
|
||||
For user defined types, the QAPI will generate a qapi_free_QAPI_TYPE_NAME()
|
||||
function and that's what you have to use to free the types you define and
|
||||
qapi_free_QAPI_TYPE_NAMEList() for list types (explained in the next section).
|
||||
If the QMP call returns a string, then you should g_free() to free it.
|
||||
|
||||
Also note that hmp_info_option_roms() performs error handling. That's
|
||||
not strictly required when you're sure the QMP function doesn't return
|
||||
errors; you could instead pass it &error_abort then.
|
||||
Also note that hmp_info_alarm_clock() performs error handling. That's not
|
||||
strictly required if you're sure the QMP function doesn't return errors, but
|
||||
it's good practice to always check for errors.
|
||||
|
||||
Another important detail is that HMP's "info" commands go into
|
||||
hmp-commands-info.hx, not hmp-commands.hx. The entry for the "info
|
||||
option-roms" follows::
|
||||
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::
|
||||
|
||||
{
|
||||
.name = "option-roms",
|
||||
.args_type = "",
|
||||
.params = "",
|
||||
.help = "show roms",
|
||||
.cmd = hmp_info_option_roms,
|
||||
},
|
||||
SRST
|
||||
``info option-roms``
|
||||
Show the option ROMs.
|
||||
ERST
|
||||
{
|
||||
.name = "alarmclock",
|
||||
.args_type = "",
|
||||
.params = "",
|
||||
.help = "show information about the alarm clock",
|
||||
.cmd = hmp_info_alarm_clock,
|
||||
},
|
||||
|
||||
To test this, run QEMU and type "info option-roms" in the user monitor.
|
||||
To test this, run qemu and type "info alarmclock" in the user monitor.
|
||||
|
||||
|
||||
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::
|
||||
|
||||
##
|
||||
# @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::
|
||||
|
||||
##
|
||||
# @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::
|
||||
|
||||
TimerAlarmMethodList *qmp_query_alarm_methods(Error **errp)
|
||||
{
|
||||
TimerAlarmMethodList *method_list = NULL;
|
||||
const struct qemu_alarm_timer *p;
|
||||
bool current = true;
|
||||
|
||||
for (p = alarm_timers; p->name; p++) {
|
||||
TimerAlarmMethod *value = g_malloc0(*value);
|
||||
value->method_name = g_strdup(p->name);
|
||||
value->current = current;
|
||||
QAPI_LIST_PREPEND(method_list, value);
|
||||
current = false;
|
||||
}
|
||||
|
||||
return method_list;
|
||||
}
|
||||
|
||||
The most important difference from the previous examples is the
|
||||
TimerAlarmMethodList type, which is automatically generated by the QAPI from
|
||||
the TimerAlarmMethod type.
|
||||
|
||||
Each list node is represented by a TimerAlarmMethodList instance. We have to
|
||||
allocate it, and that's done inside the for loop: the "info" pointer points to
|
||||
an allocated node. We also have to allocate the node's contents, which is
|
||||
stored in its "value" member. In our example, the "value" member is a pointer
|
||||
to an TimerAlarmMethod instance.
|
||||
|
||||
Notice that the "current" variable is used as "true" only in the first
|
||||
iteration of the loop. That's because the alarm timer method in use is the
|
||||
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::
|
||||
|
||||
{ "execute": "query-alarm-methods" }
|
||||
{
|
||||
"return": [
|
||||
{
|
||||
"current": false,
|
||||
"method-name": "unix"
|
||||
},
|
||||
{
|
||||
"current": true,
|
||||
"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::
|
||||
|
||||
void hmp_info_alarm_methods(Monitor *mon)
|
||||
{
|
||||
TimerAlarmMethodList *method_list, *method;
|
||||
Error *err = NULL;
|
||||
|
||||
method_list = qmp_query_alarm_methods(&err);
|
||||
if (hmp_handle_error(mon, err)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (method = method_list; method; method = method->next) {
|
||||
monitor_printf(mon, "%c %s\n", method->value->current ? '*' : ' ',
|
||||
method->value->method_name);
|
||||
}
|
||||
|
||||
qapi_free_TimerAlarmMethodList(method_list);
|
||||
}
|
||||
|
||||
Writing a debugging aid returning unstructured text
|
||||
---------------------------------------------------
|
||||
|
||||
As discussed in section `Modelling data in QAPI`_, it is required that
|
||||
commands expecting machine usage be using fine-grained QAPI data types.
|
||||
The exception to this rule applies when the command is solely intended
|
||||
as a debugging aid and allows for returning unstructured text, such as
|
||||
a query command that report aspects of QEMU's internal state that are
|
||||
useful only to human operators.
|
||||
as a debugging aid and allows for returning unstructured text. This is
|
||||
commonly needed for query commands that report aspects of QEMU's
|
||||
internal state that are useful to human operators.
|
||||
|
||||
In this example we will consider the existing QMP command
|
||||
``x-query-roms`` in qapi/machine.json. It has no parameters and
|
||||
returns a ``HumanReadableText``::
|
||||
In this example we will consider a simplified variant of the HMP
|
||||
command ``info roms``. Following the earlier rules, this command will
|
||||
need to live under the ``x-`` name prefix, so its QMP implementation
|
||||
will be called ``x-query-roms``. It will have no parameters and will
|
||||
return a single text string::
|
||||
|
||||
{ 'struct': 'HumanReadableText',
|
||||
'data': { 'human-readable-text': 'str' } }
|
||||
|
||||
##
|
||||
# @x-query-roms:
|
||||
#
|
||||
# Query information on the registered ROMS
|
||||
#
|
||||
# Features:
|
||||
#
|
||||
# @unstable: This command is meant for debugging.
|
||||
#
|
||||
# Returns: registered ROMs
|
||||
#
|
||||
# Since: 6.2
|
||||
##
|
||||
{ 'command': 'x-query-roms',
|
||||
'returns': 'HumanReadableText',
|
||||
'features': [ 'unstable' ] }
|
||||
'returns': 'HumanReadableText' }
|
||||
|
||||
The ``HumanReadableText`` struct is defined in qapi/common.json as a
|
||||
struct with a string member. It is intended to be used for all
|
||||
commands that are returning unstructured text targeted at
|
||||
humans. These should all have feature 'unstable'. Note that the
|
||||
feature's documentation states why the command is unstable. We
|
||||
commonly use a ``x-`` command name prefix to make lack of stability
|
||||
obvious to human users.
|
||||
The ``HumanReadableText`` struct is intended to be used for all
|
||||
commands, under the ``x-`` name prefix that are returning unstructured
|
||||
text targeted at humans. It should never be used for commands outside
|
||||
the ``x-`` name prefix, as those should be using structured QAPI types.
|
||||
|
||||
Implementing the QMP command
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The QMP implementation will typically involve creating a ``GString``
|
||||
object and printing formatted data into it, like this::
|
||||
object and printing formatted data into it::
|
||||
|
||||
HumanReadableText *qmp_x_query_roms(Error **errp)
|
||||
{
|
||||
@@ -593,9 +698,6 @@ object and printing formatted data into it, like this::
|
||||
return human_readable_text_from_str(buf);
|
||||
}
|
||||
|
||||
The actual implementation emits more information. You can find it in
|
||||
hw/core/loader.c.
|
||||
|
||||
|
||||
Implementing the HMP command
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -604,7 +706,7 @@ Now that the QMP command is in place, we can also make it available in
|
||||
the human monitor (HMP) as shown in previous examples. The HMP
|
||||
implementations will all look fairly similar, as all they need do is
|
||||
invoke the QMP command and then print the resulting text or error
|
||||
message. Here's an implementation of the "info roms" HMP command::
|
||||
message. Here's the implementation of the "info roms" HMP command::
|
||||
|
||||
void hmp_info_roms(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
@@ -644,5 +746,3 @@ field NULL::
|
||||
.help = "show roms",
|
||||
.cmd_info_hrt = qmp_x_query_roms,
|
||||
},
|
||||
|
||||
This is how the actual HMP command is done.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user