diff --git a/.cirrus.yml b/.cirrus.yml index f287d23c5b..f53c519447 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -3,40 +3,163 @@ env: freebsd_12_task: freebsd_instance: - image_family: freebsd-12-1 + image_family: freebsd-12-2 cpu: 8 memory: 8G - install_script: ASSUME_ALWAYS_YES=yes pkg bootstrap -f ; pkg install -y - bash curl cyrus-sasl git glib gmake gnutls gsed - nettle perl5 pixman pkgconf png usbredir + install_script: + - ASSUME_ALWAYS_YES=yes pkg bootstrap -f ; + - pkg install -y bash curl cyrus-sasl git glib gmake gnutls gsed + nettle perl5 pixman pkgconf png usbredir ninja script: - mkdir build - cd build - - ../configure || { cat config.log; exit 1; } - - gmake -j8 - - gmake V=1 check + # TODO: Enable gnutls again once FreeBSD's libtasn1 got fixed + # See: https://gitlab.com/gnutls/libtasn1/-/merge_requests/71 + - ../configure --enable-werror --disable-gnutls + || { cat config.log meson-logs/meson-log.txt; exit 1; } + - gmake -j$(sysctl -n hw.ncpu) + - gmake -j$(sysctl -n hw.ncpu) check V=1 macos_task: osx_instance: - image: mojave-base + image: catalina-base install_script: - - brew install pkg-config python gnu-sed glib pixman make sdl2 bash + - brew install pkg-config python gnu-sed glib pixman make sdl2 bash ninja script: - mkdir build - cd build - - ../configure --python=/usr/local/bin/python3 || { cat config.log; exit 1; } + - ../configure --python=/usr/local/bin/python3 --enable-werror + --extra-cflags='-Wno-error=deprecated-declarations' + || { cat config.log meson-logs/meson-log.txt; exit 1; } - gmake -j$(sysctl -n hw.ncpu) - - gmake check + - gmake check-unit V=1 + - gmake check-block V=1 + - gmake check-qapi-schema V=1 + - gmake check-softfloat V=1 + - gmake check-qtest-x86_64 V=1 macos_xcode_task: osx_instance: # this is an alias for the latest Xcode - image: mojave-xcode + image: catalina-xcode install_script: - - brew install pkg-config gnu-sed glib pixman make sdl2 bash + - brew install pkg-config gnu-sed glib pixman make sdl2 bash ninja script: - mkdir build - cd build - - ../configure --cc=clang || { cat config.log; exit 1; } + - ../configure --extra-cflags='-Wno-error=deprecated-declarations' --enable-modules + --enable-werror --cc=clang || { cat config.log meson-logs/meson-log.txt; exit 1; } - gmake -j$(sysctl -n hw.ncpu) - - gmake check + - gmake check-unit V=1 + - gmake check-block V=1 + - gmake check-qapi-schema V=1 + - gmake check-softfloat V=1 + - gmake check-qtest-x86_64 V=1 + +windows_msys2_task: + timeout_in: 90m + windows_container: + image: cirrusci/windowsservercore:2019 + os_version: 2019 + cpu: 8 + memory: 8G + env: + CIRRUS_SHELL: powershell + MSYS: winsymlinks:nativestrict + MSYSTEM: MINGW64 + MSYS2_URL: https://github.com/msys2/msys2-installer/releases/download/2021-01-05/msys2-base-x86_64-20210105.sfx.exe + MSYS2_FINGERPRINT: 0 + MSYS2_PACKAGES: " + diffutils git grep make pkg-config sed + mingw-w64-x86_64-python + mingw-w64-x86_64-python-sphinx + mingw-w64-x86_64-toolchain + mingw-w64-x86_64-SDL2 + mingw-w64-x86_64-SDL2_image + mingw-w64-x86_64-gtk3 + mingw-w64-x86_64-glib2 + mingw-w64-x86_64-ninja + mingw-w64-x86_64-jemalloc + mingw-w64-x86_64-lzo2 + mingw-w64-x86_64-zstd + mingw-w64-x86_64-libjpeg-turbo + mingw-w64-x86_64-pixman + mingw-w64-x86_64-libgcrypt + mingw-w64-x86_64-libpng + mingw-w64-x86_64-libssh + mingw-w64-x86_64-libxml2 + mingw-w64-x86_64-snappy + mingw-w64-x86_64-libusb + mingw-w64-x86_64-usbredir + mingw-w64-x86_64-libtasn1 + mingw-w64-x86_64-nettle + mingw-w64-x86_64-cyrus-sasl + mingw-w64-x86_64-curl + mingw-w64-x86_64-gnutls + mingw-w64-x86_64-libnfs + " + CHERE_INVOKING: 1 + msys2_cache: + folder: C:\tools\archive + reupload_on_changes: false + # These env variables are used to generate fingerprint to trigger the cache procedure + # If wanna to force re-populate msys2, increase MSYS2_FINGERPRINT + fingerprint_script: + - | + echo $env:CIRRUS_TASK_NAME + echo $env:MSYS2_URL + echo $env:MSYS2_FINGERPRINT + echo $env:MSYS2_PACKAGES + populate_script: + - | + md -Force C:\tools\archive\pkg + $start_time = Get-Date + bitsadmin /transfer msys_download /dynamic /download /priority FOREGROUND $env:MSYS2_URL C:\tools\archive\base.exe + Write-Output "Download time taken: $((Get-Date).Subtract($start_time))" + cd C:\tools + C:\tools\archive\base.exe -y + del -Force C:\tools\archive\base.exe + Write-Output "Base install time taken: $((Get-Date).Subtract($start_time))" + $start_time = Get-Date + + ((Get-Content -path C:\tools\msys64\etc\\post-install\\07-pacman-key.post -Raw) -replace '--refresh-keys', '--version') | Set-Content -Path C:\tools\msys64\etc\\post-install\\07-pacman-key.post + C:\tools\msys64\usr\bin\bash.exe -lc "sed -i 's/^CheckSpace/#CheckSpace/g' /etc/pacman.conf" + C:\tools\msys64\usr\bin\bash.exe -lc "export" + C:\tools\msys64\usr\bin\pacman.exe --noconfirm -Sy + echo Y | C:\tools\msys64\usr\bin\pacman.exe --noconfirm -Suu --overwrite=* + taskkill /F /FI "MODULES eq msys-2.0.dll" + tasklist + C:\tools\msys64\usr\bin\bash.exe -lc "mv -f /etc/pacman.conf.pacnew /etc/pacman.conf || true" + C:\tools\msys64\usr\bin\bash.exe -lc "pacman --noconfirm -Suu --overwrite=*" + Write-Output "Core install time taken: $((Get-Date).Subtract($start_time))" + $start_time = Get-Date + + C:\tools\msys64\usr\bin\bash.exe -lc "pacman --noconfirm -S --needed $env:MSYS2_PACKAGES" + Write-Output "Package install time taken: $((Get-Date).Subtract($start_time))" + $start_time = Get-Date + + del -Force -ErrorAction SilentlyContinue C:\tools\msys64\etc\mtab + del -Force -ErrorAction SilentlyContinue C:\tools\msys64\dev\fd + del -Force -ErrorAction SilentlyContinue C:\tools\msys64\dev\stderr + del -Force -ErrorAction SilentlyContinue C:\tools\msys64\dev\stdin + del -Force -ErrorAction SilentlyContinue C:\tools\msys64\dev\stdout + del -Force -Recurse -ErrorAction SilentlyContinue C:\tools\msys64\var\cache\pacman\pkg + tar cf C:\tools\archive\msys64.tar -C C:\tools\ msys64 + + Write-Output "Package archive time taken: $((Get-Date).Subtract($start_time))" + del -Force -Recurse -ErrorAction SilentlyContinue c:\tools\msys64 + install_script: + - | + $start_time = Get-Date + cd C:\tools + ls C:\tools\archive\msys64.tar + tar xf C:\tools\archive\msys64.tar + Write-Output "Extract msys2 time taken: $((Get-Date).Subtract($start_time))" + script: + - C:\tools\msys64\usr\bin\bash.exe -lc "mkdir build" + - C:\tools\msys64\usr\bin\bash.exe -lc "cd build && ../configure --python=python3" + - C:\tools\msys64\usr\bin\bash.exe -lc "cd build && make -j8" + - exit $LastExitCode + test_script: + - C:\tools\msys64\usr\bin\bash.exe -lc "cd build && make V=1 check" + - exit $LastExitCode diff --git a/.editorconfig b/.editorconfig index a001f340bd..7303759ed7 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,6 +4,11 @@ # plugin. # # Check https://editorconfig.org for details. +# +# Emacs: you need https://github.com/10sr/editorconfig-custom-majormode-el +# to automatically enable the appropriate major-mode for your files +# that aren't already caught by your existing config. +# root = true @@ -15,16 +20,17 @@ charset = utf-8 [*.mak] indent_style = tab indent_size = 8 -file_type_emacs = makefile +emacs_mode = makefile [Makefile*] indent_style = tab indent_size = 8 -file_type_emacs = makefile +emacs_mode = makefile -[*.{c,h}] +[*.{c,h,c.inc,h.inc}] indent_style = space indent_size = 4 +emacs_mode = c [*.sh] indent_style = space @@ -33,11 +39,11 @@ indent_size = 4 [*.{s,S}] indent_style = tab indent_size = 8 -file_type_emacs = asm +emacs_mode = asm [*.{vert,frag}] -file_type_emacs = glsl +emacs_mode = glsl [*.json] indent_style = space -file_type_emacs = python +emacs_mode = python diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..07f430e944 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +*.c.inc diff=c +*.h.inc diff=c +*.py diff=python diff --git a/.github/lockdown.yml b/.github/lockdown.yml index 9acc393f1c..07fc2f31ee 100644 --- a/.github/lockdown.yml +++ b/.github/lockdown.yml @@ -10,8 +10,8 @@ issues: comment: | Thank you for your interest in the QEMU project. - This repository is a read-only mirror of the project's master - repostories hosted on https://git.qemu.org/git/qemu.git. + This repository is a read-only mirror of the project's repostories hosted + at https://gitlab.com/qemu-project/qemu.git. The project does not process issues filed on GitHub. The project issues are tracked on Launchpad: @@ -24,8 +24,8 @@ pulls: comment: | Thank you for your interest in the QEMU project. - This repository is a read-only mirror of the project's master - repostories hosted on https://git.qemu.org/git/qemu.git. + This repository is a read-only mirror of the project's repostories hosted + on https://gitlab.com/qemu-project/qemu.git. The project does not process merge requests filed on GitHub. QEMU welcomes contributions of code (either fixing bugs or adding new diff --git a/.gitignore b/.gitignore index ca2304af29..8b7f79d071 100644 --- a/.gitignore +++ b/.gitignore @@ -1,168 +1,18 @@ -/.doctrees -/config-devices.* -/config-all-devices.* -/config-all-disas.* -/config-host.* -/config-target.* -/config.status -/config-temp -/tools/virtiofsd/50-qemu-virtiofsd.json -/elf2dmp -/trace-events-all -/trace/generated-events.h -/trace/generated-events.c -/trace/generated-helpers-wrappers.h -/trace/generated-helpers.h -/trace/generated-helpers.c -/trace/generated-tcg-tracers.h -/ui/shader/texture-blit-frag.h -/ui/shader/texture-blit-vert.h -/ui/shader/texture-blit-flip-vert.h -/ui/input-keymap-*.c -*-timestamp -/*-softmmu -/*-darwin-user -/*-linux-user -/*-bsd-user -/ivshmem-client -/ivshmem-server -/libdis* -/libuser -/linux-headers/asm -/qga/qapi-generated -/qapi-gen-timestamp -/qapi/qapi-builtin-types.[ch] -/qapi/qapi-builtin-visit.[ch] -/qapi/qapi-commands-*.[ch] -**/qapi/qapi-commands.[ch] -**/qapi/qapi-emit-events.[ch] -/qapi/qapi-events-*.[ch] -**/qapi/qapi-events.[ch] -**/qapi/qapi-init-commands.[ch] -**/qapi/qapi-introspect.[ch] -/qapi/qapi-types-*.[ch] -**/qapi/qapi-types.[ch] -/qapi/qapi-visit-*.[ch] -!/qapi/qapi-visit-core.c -**/qapi/qapi-visit.[ch] -**/qapi/qapi-doc.texi -/qemu-edid -/qemu-img -/qemu-nbd -/qemu-options.def -/qemu-options.texi -/qemu-img-cmds.texi -/qemu-img-cmds.h -/qemu-io -/qemu-ga -/qemu-bridge-helper -/qemu-keymap -/qemu-monitor.texi -/qemu-monitor-info.texi -/qemu-storage-daemon -/qemu-version.h -/qemu-version.h.tmp -/module_block.h -/scsi/qemu-pr-helper -/vhost-user-scsi -/vhost-user-blk -/vhost-user-gpu -/vhost-user-input -/fsdev/virtfs-proxy-helper -*.tmp -*.[1-9] -*.a -*.aux -*.cp -*.exe -*.msi -*.dll -*.so -*.mo -*.fn -*.ky -*.log -*.pdf -*.pod -*.cps -*.fns -*.kys -*.pg +/GNUmakefile +/build/ *.pyc -*.toc -*.tp -*.vr -*.d -!/.gitlab-ci.d -!/scripts/qemu-guest-agent/fsfreeze-hook.d -*.o .sdk -*.gcda -*.gcno -*.gcov -/pc-bios/bios-pq/status -/pc-bios/edk2-*.fd -/pc-bios/vgabios-pq/status -/pc-bios/optionrom/linuxboot.asm -/pc-bios/optionrom/linuxboot.bin -/pc-bios/optionrom/linuxboot.raw -/pc-bios/optionrom/linuxboot.img -/pc-bios/optionrom/linuxboot_dma.asm -/pc-bios/optionrom/linuxboot_dma.bin -/pc-bios/optionrom/linuxboot_dma.raw -/pc-bios/optionrom/linuxboot_dma.img -/pc-bios/optionrom/pvh.asm -/pc-bios/optionrom/pvh.bin -/pc-bios/optionrom/pvh.raw -/pc-bios/optionrom/pvh.img -/pc-bios/optionrom/multiboot.asm -/pc-bios/optionrom/multiboot.bin -/pc-bios/optionrom/multiboot.raw -/pc-bios/optionrom/multiboot.img -/pc-bios/optionrom/kvmvapic.asm -/pc-bios/optionrom/kvmvapic.bin -/pc-bios/optionrom/kvmvapic.raw -/pc-bios/optionrom/kvmvapic.img -/pc-bios/s390-ccw/s390-ccw.elf -/pc-bios/s390-ccw/s390-ccw.img -/docs/built -/docs/interop/qemu-ga-qapi.texi -/docs/interop/qemu-ga-ref.html -/docs/interop/qemu-ga-ref.info* -/docs/interop/qemu-ga-ref.txt -/docs/interop/qemu-qmp-qapi.texi -/docs/interop/qemu-qmp-ref.html -/docs/interop/qemu-qmp-ref.info* -/docs/interop/qemu-qmp-ref.txt -/docs/version.texi -/contrib/vhost-user-gpu/50-qemu-gpu.json -*.tps .stgit-* .git-submodule-status cscope.* tags TAGS -docker-src.* +GPATH +GRTAGS +GTAGS *~ *.ast_raw *.depend_raw -trace.h -trace.c -trace-ust.h -trace-ust.h -trace-dtrace.h -trace-dtrace.dtrace -trace-root.h -trace-root.c -trace-ust-root.h -trace-ust-root.h -trace-ust-all.h -trace-ust-all.c -trace-dtrace-root.h -trace-dtrace-root.dtrace -trace-ust-all.h -trace-ust-all.c -/target/arm/decode-sve.inc.c /dist /xemu-version.c diff --git a/.gitlab-ci.d/check-dco.py b/.gitlab-ci.d/check-dco.py new file mode 100755 index 0000000000..632c8bcce8 --- /dev/null +++ b/.gitlab-ci.d/check-dco.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +# +# check-dco.py: validate all commits are signed off +# +# Copyright (C) 2020 Red Hat, Inc. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import os.path +import sys +import subprocess + +namespace = "qemu-project" +if len(sys.argv) >= 2: + namespace = sys.argv[1] + +cwd = os.getcwd() +reponame = os.path.basename(cwd) +repourl = "https://gitlab.com/%s/%s.git" % (namespace, reponame) + +subprocess.check_call(["git", "remote", "add", "check-dco", repourl]) +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"], + universal_newlines=True) + +ancestor = ancestor.strip() + +subprocess.check_call(["git", "remote", "rm", "check-dco"]) + +errors = False + +print("\nChecking for 'Signed-off-by: NAME ' " + + "on all commits since %s...\n" % ancestor) + +log = subprocess.check_output(["git", "log", "--format=%H %s", + ancestor + "..."], + universal_newlines=True) + +if log == "": + commits = [] +else: + commits = [[c[0:40], c[41:]] for c in log.strip().split("\n")] + +for sha, subject in commits: + + msg = subprocess.check_output(["git", "show", "-s", sha], + universal_newlines=True) + lines = msg.strip().split("\n") + + print("🔍 %s %s" % (sha, subject)) + sob = False + for line in lines: + if "Signed-off-by:" in line: + sob = True + if "localhost" in line: + print(" ❌ FAIL: bad email in %s" % line) + errors = True + + if not sob: + print(" ❌ FAIL missing Signed-off-by tag") + errors = True + +if errors: + print(""" + +❌ ERROR: One or more commits are missing a valid Signed-off-By tag. + + +This project requires all contributors to assert that their contributions +are provided in compliance with the terms of the Developer's Certificate +of Origin 1.1 (DCO): + + https://developercertificate.org/ + +To indicate acceptance of the DCO every commit must have a tag + + Signed-off-by: REAL NAME + +This can be achieved by passing the "-s" flag to the "git commit" command. + +To bulk update all commits on current branch "git rebase" can be used: + + git rebase -i master -x 'git commit --amend --no-edit -s' + +""") + + sys.exit(1) + +sys.exit(0) diff --git a/.gitlab-ci.d/check-patch.py b/.gitlab-ci.d/check-patch.py new file mode 100755 index 0000000000..39e2b403c9 --- /dev/null +++ b/.gitlab-ci.d/check-patch.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# +# check-patch.py: run checkpatch.pl across all commits in a branch +# +# Copyright (C) 2020 Red Hat, Inc. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import os.path +import sys +import subprocess + +namespace = "qemu-project" +if len(sys.argv) >= 2: + namespace = sys.argv[1] + +cwd = os.getcwd() +reponame = os.path.basename(cwd) +repourl = "https://gitlab.com/%s/%s.git" % (namespace, reponame) + +# 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"], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) + +ancestor = subprocess.check_output(["git", "merge-base", + "check-patch/master", "HEAD"], + universal_newlines=True) + +ancestor = ancestor.strip() + +log = subprocess.check_output(["git", "log", "--format=%H %s", + ancestor + "..."], + universal_newlines=True) + +subprocess.check_call(["git", "remote", "rm", "check-patch"]) + +if log == "": + print("\nNo commits since %s, skipping checks\n" % ancestor) + sys.exit(0) + +errors = False + +print("\nChecking all commits since %s...\n" % ancestor, flush=True) + +ret = subprocess.run(["scripts/checkpatch.pl", "--terse", ancestor + "..."]) + +if ret.returncode != 0: + print(" ❌ FAIL one or more commits failed scripts/checkpatch.pl") + sys.exit(1) + +sys.exit(0) diff --git a/.gitlab-ci.d/containers.yml b/.gitlab-ci.d/containers.yml index 8c89efeb6d..33e4046e23 100644 --- a/.gitlab-ci.d/containers.yml +++ b/.gitlab-ci.d/containers.yml @@ -8,7 +8,7 @@ - export COMMON_TAG="$CI_REGISTRY/qemu-project/qemu/$NAME:latest" - apk add python3 - docker info - - docker login registry.gitlab.com -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" + - docker login $CI_REGISTRY -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" script: - echo "TAG:$TAG" - echo "COMMON_TAG:$COMMON_TAG" @@ -20,13 +20,11 @@ - docker push "$TAG" after_script: - docker logout - rules: - - changes: - - .gitlab-ci.d/containers.yml - - tests/docker/* - - tests/docker/dockerfiles/* - - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' - - if: '$CI_COMMIT_REF_NAME == "testing/next"' + +amd64-alpine-container: + <<: *container_job_definition + variables: + NAME: alpine amd64-centos7-container: <<: *container_job_definition @@ -48,18 +46,6 @@ amd64-debian11-container: variables: NAME: debian11 -amd64-debian9-container: - <<: *container_job_definition - variables: - NAME: debian9 - -amd64-debian9-mxe-container: - <<: *container_job_definition - stage: containers-layer2 - needs: ['amd64-debian9-container'] - variables: - NAME: debian9-mxe - alpha-debian-cross-container: <<: *container_job_definition stage: containers-layer2 @@ -210,24 +196,10 @@ sparc64-debian-cross-container: tricore-debian-cross-container: <<: *container_job_definition stage: containers-layer2 - needs: ['amd64-debian9-container'] + needs: ['amd64-debian10-container'] variables: NAME: debian-tricore-cross -win32-debian-cross-container: - <<: *container_job_definition - stage: containers-layer3 - needs: ['amd64-debian9-mxe-container'] - variables: - NAME: debian-win32-cross - -win64-debian-cross-container: - <<: *container_job_definition - stage: containers-layer3 - needs: ['amd64-debian9-mxe-container'] - variables: - NAME: debian-win64-cross - xtensa-debian-cross-container: <<: *container_job_definition variables: @@ -248,6 +220,16 @@ i386-fedora-cross-container: variables: NAME: fedora-i386-cross +win32-fedora-cross-container: + <<: *container_job_definition + variables: + NAME: fedora-win32-cross + +win64-fedora-cross-container: + <<: *container_job_definition + variables: + NAME: fedora-win64-cross + amd64-ubuntu1804-container: <<: *container_job_definition variables: @@ -262,3 +244,8 @@ amd64-ubuntu-container: <<: *container_job_definition variables: NAME: ubuntu + +amd64-opensuse-leap-container: + <<: *container_job_definition + variables: + NAME: opensuse-leap diff --git a/.gitlab-ci.d/crossbuilds.yml b/.gitlab-ci.d/crossbuilds.yml new file mode 100644 index 0000000000..2d95784ed5 --- /dev/null +++ b/.gitlab-ci.d/crossbuilds.yml @@ -0,0 +1,209 @@ +.cross_system_build_job: + stage: build + image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest + timeout: 80m + script: + - mkdir build + - cd build + - PKG_CONFIG_PATH=$PKG_CONFIG_PATH + ../configure --enable-werror --disable-docs $QEMU_CONFIGURE_OPTS + --disable-user --target-list-exclude="arm-softmmu cris-softmmu + i386-softmmu microblaze-softmmu mips-softmmu mipsel-softmmu + mips64-softmmu ppc-softmmu sh4-softmmu xtensa-softmmu" + - make -j$(expr $(nproc) + 1) all check-build $MAKE_CHECK_ARGS + +# Job to cross-build specific accelerators. +# +# Set the $ACCEL variable to select the specific accelerator (default to +# KVM), and set extra options (such disabling other accelerators) via the +# $ACCEL_CONFIGURE_OPTS variable. +.cross_accel_build_job: + stage: build + image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest + timeout: 30m + script: + - mkdir build + - cd build + - PKG_CONFIG_PATH=$PKG_CONFIG_PATH + ../configure --enable-werror --disable-docs $QEMU_CONFIGURE_OPTS + --disable-tools --enable-${ACCEL:-kvm} $ACCEL_CONFIGURE_OPTS + - make -j$(expr $(nproc) + 1) all check-build + +.cross_user_build_job: + stage: build + image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest + script: + - mkdir build + - cd build + - PKG_CONFIG_PATH=$PKG_CONFIG_PATH + ../configure --enable-werror --disable-docs $QEMU_CONFIGURE_OPTS + --disable-system + - make -j$(expr $(nproc) + 1) all check-build $MAKE_CHECK_ARGS + +cross-armel-system: + extends: .cross_system_build_job + needs: + job: armel-debian-cross-container + variables: + IMAGE: debian-armel-cross + +cross-armel-user: + extends: .cross_user_build_job + needs: + job: armel-debian-cross-container + variables: + IMAGE: debian-armel-cross + +cross-armhf-system: + extends: .cross_system_build_job + needs: + job: armhf-debian-cross-container + variables: + IMAGE: debian-armhf-cross + +cross-armhf-user: + extends: .cross_user_build_job + needs: + job: armhf-debian-cross-container + variables: + IMAGE: debian-armhf-cross + +cross-arm64-system: + extends: .cross_system_build_job + needs: + job: arm64-debian-cross-container + variables: + IMAGE: debian-arm64-cross + +cross-arm64-user: + extends: .cross_user_build_job + needs: + job: arm64-debian-cross-container + variables: + IMAGE: debian-arm64-cross + +cross-i386-system: + extends: .cross_system_build_job + needs: + job: i386-fedora-cross-container + variables: + IMAGE: fedora-i386-cross + MAKE_CHECK_ARGS: check-qtest + +cross-i386-user: + extends: .cross_user_build_job + needs: + job: i386-fedora-cross-container + variables: + IMAGE: fedora-i386-cross + MAKE_CHECK_ARGS: check + +cross-mips-system: + extends: .cross_system_build_job + needs: + job: mips-debian-cross-container + variables: + IMAGE: debian-mips-cross + +cross-mips-user: + extends: .cross_user_build_job + needs: + job: mips-debian-cross-container + variables: + IMAGE: debian-mips-cross + +cross-mipsel-system: + extends: .cross_system_build_job + needs: + job: mipsel-debian-cross-container + variables: + IMAGE: debian-mipsel-cross + +cross-mipsel-user: + extends: .cross_user_build_job + needs: + job: mipsel-debian-cross-container + variables: + IMAGE: debian-mipsel-cross + +cross-mips64el-system: + extends: .cross_system_build_job + needs: + job: mips64el-debian-cross-container + variables: + IMAGE: debian-mips64el-cross + +cross-mips64el-user: + extends: .cross_user_build_job + needs: + job: mips64el-debian-cross-container + variables: + IMAGE: debian-mips64el-cross + +cross-ppc64el-system: + extends: .cross_system_build_job + needs: + job: ppc64el-debian-cross-container + variables: + IMAGE: debian-ppc64el-cross + +cross-ppc64el-user: + extends: .cross_user_build_job + needs: + job: ppc64el-debian-cross-container + variables: + IMAGE: debian-ppc64el-cross + +cross-s390x-system: + extends: .cross_system_build_job + needs: + job: s390x-debian-cross-container + variables: + IMAGE: debian-s390x-cross + +cross-s390x-user: + extends: .cross_user_build_job + needs: + job: s390x-debian-cross-container + variables: + IMAGE: debian-s390x-cross + +cross-s390x-kvm-only: + extends: .cross_accel_build_job + needs: + job: s390x-debian-cross-container + variables: + IMAGE: debian-s390x-cross + ACCEL_CONFIGURE_OPTS: --disable-tcg + +cross-win32-system: + extends: .cross_system_build_job + needs: + job: win32-fedora-cross-container + variables: + IMAGE: fedora-win32-cross + +cross-win64-system: + extends: .cross_system_build_job + needs: + job: win64-fedora-cross-container + variables: + IMAGE: fedora-win64-cross + +cross-amd64-xen-only: + extends: .cross_accel_build_job + needs: + job: amd64-debian-cross-container + variables: + IMAGE: debian-amd64-cross + ACCEL: xen + ACCEL_CONFIGURE_OPTS: --disable-tcg --disable-kvm + +cross-arm64-xen-only: + extends: .cross_accel_build_job + needs: + job: arm64-debian-cross-container + variables: + IMAGE: debian-arm64-cross + ACCEL: xen + ACCEL_CONFIGURE_OPTS: --disable-tcg --disable-kvm diff --git a/.gitlab-ci.d/edk2.yml b/.gitlab-ci.d/edk2.yml index e1e0452416..ba7280605c 100644 --- a/.gitlab-ci.d/edk2.yml +++ b/.gitlab-ci.d/edk2.yml @@ -25,6 +25,7 @@ docker-edk2: build-edk2: stage: build + needs: ['docker-edk2'] rules: # Only run this job when ... - changes: # ... roms/edk2/ is modified (submodule updated) - roms/edk2/* diff --git a/.gitlab-ci.d/opensbi.yml b/.gitlab-ci.d/opensbi.yml index 62088ec5ec..f66cd1d908 100644 --- a/.gitlab-ci.d/opensbi.yml +++ b/.gitlab-ci.d/opensbi.yml @@ -25,6 +25,7 @@ docker-opensbi: build-opensbi: stage: build + needs: ['docker-opensbi'] rules: # Only run this job when ... - changes: # ... roms/opensbi/ is modified (submodule updated) - roms/opensbi/* @@ -35,18 +36,14 @@ build-opensbi: when: always artifacts: paths: # 'artifacts.zip' will contains the following files: - - pc-bios/opensbi-riscv32-sifive_u-fw_jump.bin - - pc-bios/opensbi-riscv32-virt-fw_jump.bin - - pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin - - pc-bios/opensbi-riscv64-virt-fw_jump.bin - - opensbi32-virt-stdout.log - - opensbi32-virt-stderr.log - - opensbi64-virt-stdout.log - - opensbi64-virt-stderr.log - - opensbi32-sifive_u-stdout.log - - opensbi32-sifive_u-stderr.log - - opensbi64-sifive_u-stdout.log - - opensbi64-sifive_u-stderr.log + - pc-bios/opensbi-riscv32-generic-fw_dynamic.bin + - pc-bios/opensbi-riscv32-generic-fw_dynamic.elf + - pc-bios/opensbi-riscv64-generic-fw_dynamic.bin + - pc-bios/opensbi-riscv64-generic-fw_dynamic.elf + - opensbi32-generic-stdout.log + - opensbi32-generic-stderr.log + - opensbi64-generic-stdout.log + - opensbi64-generic-stderr.log image: $CI_REGISTRY_IMAGE:opensbi-cross-build variables: GIT_DEPTH: 3 @@ -55,10 +52,6 @@ build-opensbi: - export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1)) - echo "=== Using ${JOBS} simultaneous jobs ===" - make -j${JOBS} -C roms/opensbi clean - - make -j${JOBS} -C roms opensbi32-virt 2>&1 1>opensbi32-virt-stdout.log | tee -a opensbi32-virt-stderr.log >&2 + - make -j${JOBS} -C roms opensbi32-generic 2>&1 1>opensbi32-generic-stdout.log | tee -a opensbi32-generic-stderr.log >&2 - make -j${JOBS} -C roms/opensbi clean - - make -j${JOBS} -C roms opensbi64-virt 2>&1 1>opensbi64-virt-stdout.log | tee -a opensbi64-virt-stderr.log >&2 - - make -j${JOBS} -C roms/opensbi clean - - make -j${JOBS} -C roms opensbi32-sifive_u 2>&1 1>opensbi32-sifive_u-stdout.log | tee -a opensbi32-sifive_u-stderr.log >&2 - - make -j${JOBS} -C roms/opensbi clean - - make -j${JOBS} -C roms opensbi64-sifive_u 2>&1 1>opensbi64-sifive_u-stdout.log | tee -a opensbi64-sifive_u-stderr.log >&2 + - make -j${JOBS} -C roms opensbi64-generic 2>&1 1>opensbi64-generic-stdout.log | tee -a opensbi64-generic-stderr.log >&2 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9820066379..52d65d6c04 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,20 +4,14 @@ stages: - containers - containers-layer2 - - containers-layer3 - build - test -# We assume GitLab has it's own caching set up for RPM/APT repositories so we -# just take care of avocado assets here. -cache: - paths: - - $HOME/avocado/data/cache - include: - local: '/.gitlab-ci.d/edk2.yml' - local: '/.gitlab-ci.d/opensbi.yml' - local: '/.gitlab-ci.d/containers.yml' + - local: '/.gitlab-ci.d/crossbuilds.yml' .native_build_job_template: &native_build_job_definition stage: build @@ -29,10 +23,14 @@ include: - cd build - if test -n "$TARGETS"; then - ../configure --enable-werror $CONFIGURE_ARGS --target-list="$TARGETS" ; + ../configure --enable-werror --disable-docs $CONFIGURE_ARGS --target-list="$TARGETS" ; else - ../configure --enable-werror $CONFIGURE_ARGS ; - fi + ../configure --enable-werror --disable-docs $CONFIGURE_ARGS ; + fi || { cat config.log meson-logs/meson-log.txt && exit 1; } + - if test -n "$LD_JOBS"; + then + meson configure . -Dbackend_max_links="$LD_JOBS" ; + fi || exit 1; - make -j"$JOBS" - if test -n "$MAKE_CHECK_ARGS"; then @@ -43,9 +41,12 @@ include: stage: test image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest script: + - scripts/git-submodule.sh update + $(sed -n '/GIT_SUBMODULES=/ s/.*=// p' build/config-host.mak) - cd build - find . -type f -exec touch {} + - - make $MAKE_CHECK_ARGS + # Avoid recompiling by hiding ninja with NINJA=":" + - make NINJA=":" $MAKE_CHECK_ARGS .acceptance_template: &acceptance_definition cache: @@ -53,27 +54,77 @@ include: paths: - ${CI_PROJECT_DIR}/avocado-cache policy: pull-push + artifacts: + name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" + when: always + expire_in: 2 days + paths: + - build/tests/results/latest/results.xml + - build/tests/results/latest/test-results + reports: + junit: build/tests/results/latest/results.xml before_script: - mkdir -p ~/.config/avocado - echo "[datadir.paths]" > ~/.config/avocado/avocado.conf - echo "cache_dirs = ['${CI_PROJECT_DIR}/avocado-cache']" >> ~/.config/avocado/avocado.conf + - echo -e '[job.output.testlogs]\nstatuses = ["FAIL", "INTERRUPT"]' + >> ~/.config/avocado/avocado.conf - if [ -d ${CI_PROJECT_DIR}/avocado-cache ]; then du -chs ${CI_PROJECT_DIR}/avocado-cache ; fi + - export AVOCADO_ALLOW_UNTRUSTED_CODE=1 after_script: - cd build - - python3 -c 'import json; r = json.load(open("tests/results/latest/results.json")); [print(t["logfile"]) for t in r["tests"] if t["status"] not in ("PASS", "SKIP")]' | xargs cat - du -chs ${CI_PROJECT_DIR}/avocado-cache +build-system-alpine: + <<: *native_build_job_definition + needs: + - job: amd64-alpine-container + variables: + IMAGE: alpine + TARGETS: aarch64-softmmu alpha-softmmu cris-softmmu hppa-softmmu + moxie-softmmu microblazeel-softmmu mips64el-softmmu + MAKE_CHECK_ARGS: check-build + CONFIGURE_ARGS: --enable-docs --enable-trace-backends=log,simple,syslog + artifacts: + expire_in: 2 days + paths: + - .git-submodule-status + - build + +check-system-alpine: + <<: *native_test_job_definition + needs: + - job: build-system-alpine + artifacts: true + variables: + IMAGE: alpine + MAKE_CHECK_ARGS: check + +acceptance-system-alpine: + <<: *native_test_job_definition + needs: + - job: build-system-alpine + artifacts: true + variables: + IMAGE: alpine + MAKE_CHECK_ARGS: check-acceptance + <<: *acceptance_definition + build-system-ubuntu: <<: *native_build_job_definition + needs: + job: amd64-ubuntu2004-container variables: IMAGE: ubuntu2004 + CONFIGURE_ARGS: --enable-docs --enable-fdt=system --enable-slirp=system TARGETS: aarch64-softmmu alpha-softmmu cris-softmmu hppa-softmmu moxie-softmmu microblazeel-softmmu mips64el-softmmu MAKE_CHECK_ARGS: check-build artifacts: + expire_in: 2 days paths: - build @@ -98,12 +149,16 @@ acceptance-system-ubuntu: build-system-debian: <<: *native_build_job_definition + needs: + job: amd64-debian-container variables: IMAGE: debian-amd64 + CONFIGURE_ARGS: --enable-fdt=system TARGETS: arm-softmmu avr-softmmu i386-softmmu mipsel-softmmu riscv64-softmmu sh4eb-softmmu sparc-softmmu xtensaeb-softmmu MAKE_CHECK_ARGS: check-build artifacts: + expire_in: 2 days paths: - build @@ -128,12 +183,17 @@ acceptance-system-debian: build-system-fedora: <<: *native_build_job_definition + needs: + job: amd64-fedora-container variables: IMAGE: fedora - TARGETS: tricore-softmmu unicore32-softmmu microblaze-softmmu mips-softmmu + CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs + --enable-fdt=system --enable-slirp=system --enable-capstone=system + TARGETS: tricore-softmmu microblaze-softmmu mips-softmmu xtensa-softmmu m68k-softmmu riscv32-softmmu ppc-softmmu sparc64-softmmu MAKE_CHECK_ARGS: check-build artifacts: + expire_in: 2 days paths: - build @@ -158,12 +218,17 @@ acceptance-system-fedora: build-system-centos: <<: *native_build_job_definition + needs: + job: amd64-centos8-container variables: IMAGE: centos8 - TARGETS: ppc64-softmmu lm32-softmmu or1k-softmmu s390x-softmmu + CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-fdt=system + --enable-modules --enable-trace-backends=dtrace + TARGETS: ppc64-softmmu or1k-softmmu s390x-softmmu x86_64-softmmu rx-softmmu sh4-softmmu nios2-softmmu MAKE_CHECK_ARGS: check-build artifacts: + expire_in: 2 days paths: - build @@ -186,27 +251,138 @@ acceptance-system-centos: MAKE_CHECK_ARGS: check-acceptance <<: *acceptance_definition +build-system-opensuse: + <<: *native_build_job_definition + needs: + job: amd64-opensuse-leap-container + variables: + IMAGE: opensuse-leap + CONFIGURE_ARGS: --enable-fdt=system + TARGETS: s390x-softmmu x86_64-softmmu aarch64-softmmu + MAKE_CHECK_ARGS: check-build + artifacts: + expire_in: 2 days + paths: + - build + +check-system-opensuse: + <<: *native_test_job_definition + needs: + - job: build-system-opensuse + artifacts: true + variables: + IMAGE: opensuse-leap + MAKE_CHECK_ARGS: check + +acceptance-system-opensuse: + <<: *native_test_job_definition + needs: + - job: build-system-opensuse + artifacts: true + variables: + IMAGE: opensuse-leap + MAKE_CHECK_ARGS: check-acceptance + <<: *acceptance_definition + + build-disabled: <<: *native_build_job_definition + needs: + job: amd64-fedora-container variables: IMAGE: fedora - CONFIGURE_ARGS: --disable-rdma --disable-slirp --disable-curl - --disable-capstone --disable-live-block-migration --disable-glusterfs - --disable-replication --disable-coroutine-pool --disable-smartcard - --disable-guest-agent --disable-curses --disable-libxml2 --disable-tpm - --disable-qom-cast-debug --disable-spice --disable-vhost-vsock - --disable-vhost-net --disable-vhost-crypto --disable-vhost-user - TARGETS: i386-softmmu ppc64-softmmu mips64-softmmu i386-linux-user + CONFIGURE_ARGS: + --disable-attr + --disable-auth-pam + --disable-avx2 + --disable-bochs + --disable-brlapi + --disable-bzip2 + --disable-cap-ng + --disable-capstone + --disable-cloop + --disable-coroutine-pool + --disable-curl + --disable-curses + --disable-dmg + --disable-docs + --disable-gcrypt + --disable-glusterfs + --disable-gnutls + --disable-gtk + --disable-guest-agent + --disable-iconv + --disable-keyring + --disable-kvm + --disable-libiscsi + --disable-libpmem + --disable-libssh + --disable-libudev + --disable-libusb + --disable-libxml2 + --disable-linux-aio + --disable-live-block-migration + --disable-lzo + --disable-malloc-trim + --disable-mpath + --disable-nettle + --disable-numa + --disable-opengl + --disable-parallels + --disable-pie + --disable-qcow1 + --disable-qed + --disable-qom-cast-debug + --disable-rbd + --disable-rdma + --disable-replication + --disable-sdl + --disable-seccomp + --disable-sheepdog + --disable-slirp + --disable-smartcard + --disable-snappy + --disable-sparse + --disable-spice + --disable-strip + --disable-tpm + --disable-usb-redir + --disable-vdi + --disable-vhost-crypto + --disable-vhost-net + --disable-vhost-scsi + --disable-vhost-kernel + --disable-vhost-user + --disable-vhost-vdpa + --disable-vhost-vsock + --disable-virglrenderer + --disable-vnc + --disable-vte + --disable-vvfat + --disable-xen + --disable-zstd + TARGETS: arm-softmmu i386-softmmu ppc64-softmmu mips64-softmmu + s390x-softmmu i386-linux-user MAKE_CHECK_ARGS: check-qtest SPEED=slow +# This jobs explicitly disable TCG (--disable-tcg), KVM is detected by +# the configure script. The container doesn't contain Xen headers so +# Xen accelerator is not detected / selected. As result it build the +# i386-softmmu and x86_64-softmmu with KVM being the single accelerator +# available. +# Also use a different coroutine implementation (which is only really of +# interest to KVM users, i.e. with TCG disabled) build-tcg-disabled: <<: *native_build_job_definition + needs: + job: amd64-centos8-container variables: IMAGE: centos8 script: - mkdir build - cd build - - ../configure --disable-tcg --audio-drv-list="" + - ../configure --disable-tcg --audio-drv-list="" --with-coroutine=ucontext + || { cat config.log meson-logs/meson-log.txt && exit 1; } - make -j"$JOBS" - make check-unit - make check-qapi-schema @@ -214,60 +390,448 @@ build-tcg-disabled: - ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048 052 063 077 086 101 104 106 113 148 150 151 152 157 159 160 163 170 171 183 184 192 194 197 208 215 221 222 226 227 236 253 277 - - ./check -qcow2 028 051 056 057 058 065 067 068 082 085 091 095 096 102 122 + - ./check -qcow2 028 051 056 057 058 065 068 082 085 091 095 096 102 122 124 132 139 142 144 145 151 152 155 157 165 194 196 197 200 202 208 209 215 216 218 222 227 234 246 247 248 250 254 255 257 258 260 261 262 263 264 270 272 273 277 279 build-user: <<: *native_build_job_definition + needs: + job: amd64-debian-user-cross-container variables: IMAGE: debian-all-test-cross CONFIGURE_ARGS: --disable-tools --disable-system MAKE_CHECK_ARGS: check-tcg -build-clang: +build-user-static: <<: *native_build_job_definition + needs: + job: amd64-debian-user-cross-container + variables: + IMAGE: debian-all-test-cross + CONFIGURE_ARGS: --disable-tools --disable-system --static + MAKE_CHECK_ARGS: check-tcg + +# Only build the softmmu targets we have check-tcg tests for +build-some-softmmu: + <<: *native_build_job_definition + needs: + job: amd64-debian-user-cross-container + variables: + IMAGE: debian-all-test-cross + CONFIGURE_ARGS: --disable-tools --enable-debug + TARGETS: xtensa-softmmu arm-softmmu aarch64-softmmu alpha-softmmu + MAKE_CHECK_ARGS: check-tcg + +# Run check-tcg against linux-user (with plugins) +# we skip sparc64-linux-user until it has been fixed somewhat +# we skip cris-linux-user as it doesn't use the common run loop +build-user-plugins: + <<: *native_build_job_definition + needs: + job: amd64-debian-user-cross-container + variables: + IMAGE: debian-all-test-cross + CONFIGURE_ARGS: --disable-tools --disable-system --enable-plugins --enable-debug-tcg --target-list-exclude=sparc64-linux-user,cris-linux-user + MAKE_CHECK_ARGS: check-tcg + timeout: 1h 30m + +build-user-centos7: + <<: *native_build_job_definition + needs: + job: amd64-centos7-container + variables: + IMAGE: centos7 + CONFIGURE_ARGS: --disable-system --disable-tools --disable-docs + MAKE_CHECK_ARGS: check-tcg + +build-some-softmmu-plugins: + <<: *native_build_job_definition + needs: + job: amd64-debian-user-cross-container + variables: + IMAGE: debian-all-test-cross + CONFIGURE_ARGS: --disable-tools --disable-user --enable-plugins --enable-debug-tcg + TARGETS: xtensa-softmmu arm-softmmu aarch64-softmmu alpha-softmmu + MAKE_CHECK_ARGS: check-tcg + +clang-system: + <<: *native_build_job_definition + needs: + job: amd64-fedora-container variables: IMAGE: fedora CONFIGURE_ARGS: --cc=clang --cxx=clang++ + --extra-cflags=-fsanitize=undefined --extra-cflags=-fno-sanitize-recover=undefined TARGETS: alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu - ppc-softmmu s390x-softmmu arm-linux-user + ppc-softmmu s390x-softmmu + MAKE_CHECK_ARGS: check-qtest check-tcg + +clang-user: + <<: *native_build_job_definition + needs: + job: amd64-debian-user-cross-container + variables: + IMAGE: debian-all-test-cross + CONFIGURE_ARGS: --cc=clang --cxx=clang++ --disable-system + --target-list-exclude=microblazeel-linux-user,aarch64_be-linux-user,i386-linux-user,m68k-linux-user,mipsn32el-linux-user,xtensaeb-linux-user + --extra-cflags=-fsanitize=undefined --extra-cflags=-fno-sanitize-recover=undefined + MAKE_CHECK_ARGS: check-unit check-tcg + +# Set LD_JOBS=1 because this requires LTO and ld consumes a large amount of memory. +# On gitlab runners, default value sometimes end up calling 2 lds concurrently and +# triggers an Out-Of-Memory error +# +# Since slirp callbacks are used in QEMU Timers, slirp needs to be compiled together +# with QEMU and linked as a static library to avoid false positives in CFI checks. +# This can be accomplished by using -enable-slirp=git, which avoids the use of +# a system-wide version of the library +# +# Split in three sets of build/check/acceptance to limit the execution time of each +# job +build-cfi-aarch64: + <<: *native_build_job_definition + needs: + - job: amd64-fedora-container + variables: + LD_JOBS: 1 + AR: llvm-ar + IMAGE: fedora + CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug + --enable-safe-stack --enable-slirp=git + TARGETS: aarch64-softmmu + MAKE_CHECK_ARGS: check-build + timeout: 70m + artifacts: + expire_in: 2 days + paths: + - build + +check-cfi-aarch64: + <<: *native_test_job_definition + needs: + - job: build-cfi-aarch64 + artifacts: true + variables: + IMAGE: fedora MAKE_CHECK_ARGS: check +acceptance-cfi-aarch64: + <<: *native_test_job_definition + needs: + - job: build-cfi-aarch64 + artifacts: true + variables: + IMAGE: fedora + MAKE_CHECK_ARGS: check-acceptance + <<: *acceptance_definition + +build-cfi-ppc64-s390x: + <<: *native_build_job_definition + needs: + - job: amd64-fedora-container + variables: + LD_JOBS: 1 + AR: llvm-ar + IMAGE: fedora + CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug + --enable-safe-stack --enable-slirp=git + TARGETS: ppc64-softmmu s390x-softmmu + MAKE_CHECK_ARGS: check-build + timeout: 70m + artifacts: + expire_in: 2 days + paths: + - build + +check-cfi-ppc64-s390x: + <<: *native_test_job_definition + needs: + - job: build-cfi-ppc64-s390x + artifacts: true + variables: + IMAGE: fedora + MAKE_CHECK_ARGS: check + +acceptance-cfi-ppc64-s390x: + <<: *native_test_job_definition + needs: + - job: build-cfi-ppc64-s390x + artifacts: true + variables: + IMAGE: fedora + MAKE_CHECK_ARGS: check-acceptance + <<: *acceptance_definition + +build-cfi-x86_64: + <<: *native_build_job_definition + needs: + - job: amd64-fedora-container + variables: + LD_JOBS: 1 + AR: llvm-ar + IMAGE: fedora + CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-cfi --enable-cfi-debug + --enable-safe-stack --enable-slirp=git + TARGETS: x86_64-softmmu + MAKE_CHECK_ARGS: check-build + timeout: 70m + artifacts: + expire_in: 2 days + paths: + - build + +check-cfi-x86_64: + <<: *native_test_job_definition + needs: + - job: build-cfi-x86_64 + artifacts: true + variables: + IMAGE: fedora + MAKE_CHECK_ARGS: check + +acceptance-cfi-x86_64: + <<: *native_test_job_definition + needs: + - job: build-cfi-x86_64 + artifacts: true + variables: + IMAGE: fedora + MAKE_CHECK_ARGS: check-acceptance + <<: *acceptance_definition + +tsan-build: + <<: *native_build_job_definition + needs: + job: amd64-ubuntu2004-container + variables: + IMAGE: ubuntu2004 + CONFIGURE_ARGS: --enable-tsan --cc=clang-10 --cxx=clang++-10 + --enable-trace-backends=ust --enable-fdt=system --enable-slirp=system + TARGETS: x86_64-softmmu ppc64-softmmu riscv64-softmmu x86_64-linux-user + MAKE_CHECK_ARGS: bench V=1 + +# These targets are on the way out +build-deprecated: + <<: *native_build_job_definition + needs: + job: amd64-debian-user-cross-container + variables: + IMAGE: debian-all-test-cross + CONFIGURE_ARGS: --disable-tools + MAKE_CHECK_ARGS: build-tcg + TARGETS: ppc64abi32-linux-user lm32-softmmu unicore32-softmmu + artifacts: + expire_in: 2 days + paths: + - build + +# We split the check-tcg step as test failures are expected but we still +# want to catch the build breaking. +check-deprecated: + <<: *native_test_job_definition + needs: + - job: build-deprecated + artifacts: true + variables: + IMAGE: debian-all-test-cross + MAKE_CHECK_ARGS: check-tcg + allow_failure: true + +# gprof/gcov are GCC features +gprof-gcov: + <<: *native_build_job_definition + needs: + job: amd64-ubuntu2004-container + variables: + IMAGE: ubuntu2004 + CONFIGURE_ARGS: --enable-gprof --enable-gcov + MAKE_CHECK_ARGS: check + TARGETS: aarch64-softmmu ppc64-softmmu s390x-softmmu x86_64-softmmu + timeout: 70m + after_script: + - ${CI_PROJECT_DIR}/scripts/ci/coverage-summary.sh + build-oss-fuzz: <<: *native_build_job_definition + needs: + job: amd64-fedora-container variables: IMAGE: fedora script: - mkdir build-oss-fuzz - CC="clang" CXX="clang++" CFLAGS="-fsanitize=address" ./scripts/oss-fuzz/build.sh + - export ASAN_OPTIONS="fast_unwind_on_malloc=0" - for fuzzer in $(find ./build-oss-fuzz/DEST_DIR/ -executable -type f | grep -v slirp); do grep "LLVMFuzzerTestOneInput" ${fuzzer} > /dev/null 2>&1 || continue ; echo Testing ${fuzzer} ... ; - ASAN_OPTIONS="fast_unwind_on_malloc=0" - "${fuzzer}" -runs=1000 -seed=1 || exit 1 ; + "${fuzzer}" -runs=1 -seed=1 || exit 1 ; done + # Unrelated to fuzzer: run some tests with -fsanitize=address + - cd build-oss-fuzz && make check-qtest-i386 check-unit build-tci: <<: *native_build_job_definition + needs: + job: amd64-debian-user-cross-container variables: - IMAGE: fedora + IMAGE: debian-all-test-cross script: - TARGETS="aarch64 alpha arm hppa m68k microblaze moxie ppc64 s390x x86_64" - mkdir build - cd build - ../configure --enable-tcg-interpreter - --target-list="$(for tg in $TARGETS; do echo -n ${tg}'-softmmu '; done)" + --target-list="$(for tg in $TARGETS; do echo -n ${tg}'-softmmu '; done)" || { cat config.log meson-logs/meson-log.txt && exit 1; } - make -j"$JOBS" - - make run-tcg-tests-x86_64-softmmu - make tests/qtest/boot-serial-test tests/qtest/cdrom-test tests/qtest/pxe-test - for tg in $TARGETS ; do - export QTEST_QEMU_BINARY="${tg}-softmmu/qemu-system-${tg}" ; + export QTEST_QEMU_BINARY="./qemu-system-${tg}" ; ./tests/qtest/boot-serial-test || exit 1 ; ./tests/qtest/cdrom-test || exit 1 ; done - - QTEST_QEMU_BINARY="x86_64-softmmu/qemu-system-x86_64" ./tests/qtest/pxe-test - - QTEST_QEMU_BINARY="s390x-softmmu/qemu-system-s390x" ./tests/qtest/pxe-test -m slow + - QTEST_QEMU_BINARY="./qemu-system-x86_64" ./tests/qtest/pxe-test + - QTEST_QEMU_BINARY="./qemu-system-s390x" ./tests/qtest/pxe-test -m slow + - make check-tcg + +# Alternate coroutines implementations are only really of interest to KVM users +# However we can't test against KVM on Gitlab-CI so we can only run unit tests +build-coroutine-sigaltstack: + <<: *native_build_job_definition + needs: + job: amd64-ubuntu2004-container + variables: + IMAGE: ubuntu2004 + CONFIGURE_ARGS: --with-coroutine=sigaltstack --disable-tcg + --enable-trace-backends=ftrace + MAKE_CHECK_ARGS: check-unit + +# Most jobs test latest gcrypt or nettle builds +# +# These jobs test old gcrypt and nettle from RHEL7 +# which had some API differences. +crypto-old-nettle: + <<: *native_build_job_definition + needs: + job: amd64-centos7-container + variables: + IMAGE: centos7 + TARGETS: x86_64-softmmu x86_64-linux-user + CONFIGURE_ARGS: --disable-gcrypt --enable-nettle + MAKE_CHECK_ARGS: check + +crypto-old-gcrypt: + <<: *native_build_job_definition + needs: + job: amd64-centos7-container + variables: + IMAGE: centos7 + TARGETS: x86_64-softmmu x86_64-linux-user + CONFIGURE_ARGS: --disable-nettle --enable-gcrypt + MAKE_CHECK_ARGS: check + +crypto-only-gnutls: + <<: *native_build_job_definition + needs: + job: amd64-centos7-container + variables: + IMAGE: centos7 + TARGETS: x86_64-softmmu x86_64-linux-user + CONFIGURE_ARGS: --disable-nettle --disable-gcrypt --enable-gnutls + MAKE_CHECK_ARGS: check + + +# Check our reduced build configurations +build-without-default-devices: + <<: *native_build_job_definition + needs: + job: amd64-centos8-container + variables: + IMAGE: centos8 + CONFIGURE_ARGS: --without-default-devices --disable-user + +build-without-default-features: + <<: *native_build_job_definition + needs: + job: amd64-debian-container + variables: + IMAGE: debian-amd64 + CONFIGURE_ARGS: --without-default-features --disable-user + --target-list-exclude=arm-softmmu,i386-softmmu,mipsel-softmmu,mips64-softmmu,ppc-softmmu + MAKE_CHECK_ARGS: check-unit + +check-patch: + stage: build + image: $CI_REGISTRY_IMAGE/qemu/centos8:latest + needs: + job: amd64-centos8-container + script: .gitlab-ci.d/check-patch.py + except: + variables: + - $CI_PROJECT_NAMESPACE == 'qemu-project' && $CI_COMMIT_BRANCH == 'master' + variables: + GIT_DEPTH: 1000 + allow_failure: true + +check-dco: + stage: build + image: $CI_REGISTRY_IMAGE/qemu/centos8:latest + needs: + job: amd64-centos8-container + script: .gitlab-ci.d/check-dco.py + except: + variables: + - $CI_PROJECT_NAMESPACE == 'qemu-project' && $CI_COMMIT_BRANCH == 'master' + variables: + GIT_DEPTH: 1000 + +build-libvhost-user: + stage: build + image: $CI_REGISTRY_IMAGE/qemu/fedora:latest + needs: + job: amd64-fedora-container + before_script: + - dnf install -y meson ninja-build + script: + - mkdir subprojects/libvhost-user/build + - cd subprojects/libvhost-user/build + - meson + - ninja + +# No targets are built here, just tools, docs, and unit tests. This +# also feeds into the eventual documentation deployment steps later +build-tools-and-docs-debian: + <<: *native_build_job_definition + needs: + job: amd64-debian-container + variables: + IMAGE: debian-amd64 + MAKE_CHECK_ARGS: check-unit check-softfloat ctags TAGS cscope + CONFIGURE_ARGS: --disable-system --disable-user --enable-docs --enable-tools + artifacts: + expire_in: 2 days + paths: + - build + +# Prepare for GitLab pages deployment. Anything copied into the +# "public" directory will be deployed to $USER.gitlab.io/$PROJECT +pages: + image: $CI_REGISTRY_IMAGE/qemu/debian-amd64:latest + stage: test + needs: + - job: build-tools-and-docs-debian + script: + - mkdir -p public + # HTML-ised source tree + - make gtags + - htags -anT --tree-view=filetree -m qemu_init + -t "Welcome to the QEMU sourcecode" + - mv HTML public/src + # Project documentation + - make -C build install DESTDIR=$(pwd)/temp-install + - mv temp-install/usr/local/share/doc/qemu/* public/ + artifacts: + paths: + - public diff --git a/.gitmodules b/.gitmodules index b794b8d3b1..0f5c437765 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,63 +1,69 @@ [submodule "roms/seabios"] path = roms/seabios - url = https://git.qemu.org/git/seabios.git/ + url = https://gitlab.com/qemu-project/seabios.git/ [submodule "roms/SLOF"] path = roms/SLOF - url = https://git.qemu.org/git/SLOF.git + url = https://gitlab.com/qemu-project/SLOF.git [submodule "roms/ipxe"] path = roms/ipxe - url = https://git.qemu.org/git/ipxe.git + url = https://gitlab.com/qemu-project/ipxe.git [submodule "roms/openbios"] path = roms/openbios - url = https://git.qemu.org/git/openbios.git + url = https://gitlab.com/qemu-project/openbios.git [submodule "roms/qemu-palcode"] path = roms/qemu-palcode - url = https://git.qemu.org/git/qemu-palcode.git + url = https://gitlab.com/qemu-project/qemu-palcode.git [submodule "roms/sgabios"] path = roms/sgabios - url = https://git.qemu.org/git/sgabios.git + url = https://gitlab.com/qemu-project/sgabios.git [submodule "dtc"] path = dtc - url = https://git.qemu.org/git/dtc.git + url = https://gitlab.com/qemu-project/dtc.git [submodule "roms/u-boot"] path = roms/u-boot - url = https://git.qemu.org/git/u-boot.git + url = https://gitlab.com/qemu-project/u-boot.git [submodule "roms/skiboot"] path = roms/skiboot - url = https://git.qemu.org/git/skiboot.git + url = https://gitlab.com/qemu-project/skiboot.git [submodule "roms/QemuMacDrivers"] path = roms/QemuMacDrivers - url = https://git.qemu.org/git/QemuMacDrivers.git + url = https://gitlab.com/qemu-project/QemuMacDrivers.git [submodule "ui/keycodemapdb"] path = ui/keycodemapdb - url = https://git.qemu.org/git/keycodemapdb.git + url = https://gitlab.com/qemu-project/keycodemapdb.git [submodule "capstone"] path = capstone - url = https://git.qemu.org/git/capstone.git + url = https://gitlab.com/qemu-project/capstone.git [submodule "roms/seabios-hppa"] path = roms/seabios-hppa - url = https://git.qemu.org/git/seabios-hppa.git + url = https://gitlab.com/qemu-project/seabios-hppa.git [submodule "roms/u-boot-sam460ex"] path = roms/u-boot-sam460ex - url = https://git.qemu.org/git/u-boot-sam460ex.git + url = https://gitlab.com/qemu-project/u-boot-sam460ex.git [submodule "tests/fp/berkeley-testfloat-3"] path = tests/fp/berkeley-testfloat-3 - url = https://git.qemu.org/git/berkeley-testfloat-3.git + url = https://gitlab.com/qemu-project/berkeley-testfloat-3.git [submodule "tests/fp/berkeley-softfloat-3"] path = tests/fp/berkeley-softfloat-3 - url = https://git.qemu.org/git/berkeley-softfloat-3.git + url = https://gitlab.com/qemu-project/berkeley-softfloat-3.git [submodule "roms/edk2"] path = roms/edk2 - url = https://git.qemu.org/git/edk2.git + url = https://gitlab.com/qemu-project/edk2.git [submodule "slirp"] path = slirp - url = https://git.qemu.org/git/libslirp.git + url = https://gitlab.com/qemu-project/libslirp.git [submodule "roms/opensbi"] path = roms/opensbi - url = https://git.qemu.org/git/opensbi.git + url = https://gitlab.com/qemu-project/opensbi.git [submodule "roms/qboot"] path = roms/qboot - url = https://github.com/bonzini/qboot + url = https://gitlab.com/qemu-project/qboot.git +[submodule "meson"] + path = meson + url = https://gitlab.com/qemu-project/meson.git +[submodule "roms/vbootrom"] + path = roms/vbootrom + url = https://gitlab.com/qemu-project/vbootrom.git [submodule "ui/imgui"] path = ui/imgui url = https://github.com/ocornut/imgui.git diff --git a/.mailmap b/.mailmap index 81c2ce0937..a1bd659817 100644 --- a/.mailmap +++ b/.mailmap @@ -48,13 +48,16 @@ Alexander Graf Anthony Liguori Anthony Liguori Filip Bozuta Frederic Konrad +Greg Kurz +Huacai Chen +Huacai Chen James Hogan Leif Lindholm Radoslaw Biernacki -Paul Burton -Paul Burton -Paul Burton -Philippe Mathieu-DaudĂ© +Paul Burton +Paul Burton +Paul Burton +Paul Burton Stefan Brankovic Yongbok Kim @@ -84,6 +87,7 @@ Christophe Lyon Collin L. Walling Daniel P. BerrangĂ© Eduardo Otubo +Erik Smit Fabrice Desclaux Fernando Luis VĂĄzquez Cao Fernando Luis VĂĄzquez Cao @@ -141,6 +145,7 @@ Roger Pau MonnĂ© Shin'ichiro Kawasaki Shin'ichiro Kawasaki Sochin Jiang +Stefan Berger Takashi Yoshii Thomas Huth Thomas Knych diff --git a/.readthedocs.yml b/.readthedocs.yml index 8355dbc634..7fb7b8dd61 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -17,4 +17,4 @@ formats: all # we require for other Python in our codebase (our conf.py # enforces this, and some code needs it.) python: - version: 3.5 + version: 3.6 diff --git a/.shippable.yml b/.shippable.yml deleted file mode 100644 index 89d8be4291..0000000000 --- a/.shippable.yml +++ /dev/null @@ -1,39 +0,0 @@ -language: c -git: - submodules: false -env: - global: - - LC_ALL=C - matrix: - - IMAGE=debian-amd64 - TARGET_LIST=x86_64-softmmu,x86_64-linux-user - - IMAGE=debian-win32-cross - TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu - - IMAGE=debian-win64-cross - TARGET_LIST=aarch64-softmmu,sparc64-softmmu,x86_64-softmmu - - IMAGE=debian-armel-cross - TARGET_LIST=arm-softmmu,arm-linux-user,armeb-linux-user - - IMAGE=debian-armhf-cross - TARGET_LIST=arm-softmmu,arm-linux-user,armeb-linux-user - - IMAGE=debian-arm64-cross - TARGET_LIST=aarch64-softmmu,aarch64-linux-user - - IMAGE=debian-s390x-cross - TARGET_LIST=s390x-softmmu,s390x-linux-user - - IMAGE=debian-mips-cross - TARGET_LIST=mips-softmmu,mipsel-linux-user - - IMAGE=debian-mips64el-cross - TARGET_LIST=mips64el-softmmu,mips64el-linux-user - - IMAGE=debian-ppc64el-cross - TARGET_LIST=ppc64-softmmu,ppc64-linux-user,ppc64abi32-linux-user -build: - pre_ci_boot: - image_name: registry.gitlab.com/qemu-project/qemu/qemu/${IMAGE} - image_tag: latest - pull: true - options: "-e HOME=/root" - ci: - - unset CC - - mkdir build - - cd build - - ../configure --disable-docs ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST} - - make -j$(($(getconf _NPROCESSORS_ONLN) + 1)) diff --git a/.travis.yml b/.travis.yml index 6695c0620f..4609240b5a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ # Additional builds with specific requirements for a full VM need to # be added as additional matrix: entries later on os: linux -dist: xenial +dist: focal language: c compiler: - gcc @@ -10,7 +10,7 @@ cache: # There is one cache per branch and compiler version. # characteristics of each job are used to identify the cache: # - OS name (currently only linux) - # - OS distribution (for Linux, xenial, trusty, or precise) + # - OS distribution (for Linux, bionic or focal) # - Names and values of visible environment variables set in .travis.yml or Settings panel timeout: 1200 ccache: true @@ -27,7 +27,7 @@ addons: - libattr1-dev - libbrlapi-dev - libcap-ng-dev - - libgcc-4.8-dev + - libgcc-7-dev - libgnutls28-dev - libgtk-3-dev - libiscsi-dev @@ -49,9 +49,9 @@ addons: - libvdeplug-dev - libvte-2.91-dev - libzstd-dev + - ninja-build - sparse - uuid-dev - - gcovr # Tests dependencies - genisoimage @@ -86,15 +86,17 @@ git: submodules: false # Common first phase for all steps +# We no longer use nproc to calculate jobs: +# https://travis-ci.community/t/nproc-reports-32-cores-on-arm64/5851 before_install: - if command -v ccache ; then ccache --zero-stats ; fi - - export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1)) + - export JOBS=3 - echo "=== Using ${JOBS} simultaneous jobs ===" # Configure step - may be overridden before_script: - mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR} - - ${SRC_DIR}/configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; } + - ${SRC_DIR}/configure ${BASE_CONFIG} ${CONFIG} || { cat config.log meson-logs/meson-log.txt && exit 1; } # Main build & test - rarely overridden - controlled by TEST_CMD script: @@ -118,267 +120,10 @@ after_script: jobs: include: - - name: "GCC static (user)" - env: - - CONFIG="--disable-system --static" - - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default" - - - # we split the system builds as it takes a while to build them all - - name: "GCC (main-softmmu)" - env: - - CONFIG="--disable-user --target-list=${MAIN_SOFTMMU_TARGETS}" - - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default" - - - - name: "GCC (other-softmmu)" - env: - - CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}" - - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default" - - - # Just build tools and run minimal unit and softfloat checks - - name: "GCC check-softfloat (user)" - env: - - BASE_CONFIG="--enable-tools" - - CONFIG="--disable-user --disable-system" - - TEST_CMD="make check-unit check-softfloat -j${JOBS}" - - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default" - - - # --enable-debug implies --enable-debug-tcg, also runs quite a bit slower - - name: "GCC debug (main-softmmu)" - env: - - CONFIG="--enable-debug --target-list=${MAIN_SOFTMMU_TARGETS}" - - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug" - - - # TCG debug can be run just on its own and is mostly agnostic to user/softmmu distinctions - - name: "GCC debug (user)" - env: - - CONFIG="--enable-debug-tcg --disable-system" - - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg" - - - - name: "GCC some libs disabled (main-softmmu)" - env: - - CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-libusb --disable-replication --target-list=${MAIN_SOFTMMU_TARGETS}" - - - # Module builds are mostly of interest to major distros - - name: "GCC modules (main-softmmu)" - env: - - CONFIG="--enable-modules --target-list=${MAIN_SOFTMMU_TARGETS}" - - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default" - - - # Alternate coroutines implementations are only really of interest to KVM users - # However we can't test against KVM on Travis so we can only run unit tests - - name: "check-unit coroutine=ucontext" - env: - - CONFIG="--with-coroutine=ucontext --disable-tcg" - - TEST_CMD="make check-unit -j${JOBS} V=1" - - - - name: "check-unit coroutine=sigaltstack" - env: - - CONFIG="--with-coroutine=sigaltstack --disable-tcg" - - TEST_CMD="make check-unit -j${JOBS} V=1" - - - # Check we can build docs and tools (out of tree) - - name: "tools and docs (bionic)" - dist: bionic - env: - - BUILD_DIR="out-of-tree/build/dir" SRC_DIR="../../.." - - BASE_CONFIG="--enable-tools --enable-docs" - - CONFIG="--target-list=x86_64-softmmu,aarch64-linux-user" - - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default" - addons: - apt: - packages: - - python3-sphinx - - texinfo - - perl - - - # Test with Clang for compile portability (Travis uses clang-5.0) - - name: "Clang (user)" - env: - - CONFIG="--disable-system --host-cc=clang --cxx=clang++" - - CACHE_NAME="${TRAVIS_BRANCH}-linux-clang-default" - compiler: clang - - - - name: "Clang (main-softmmu)" - env: - - CONFIG="--target-list=${MAIN_SOFTMMU_TARGETS} - --host-cc=clang --cxx=clang++" - - CACHE_NAME="${TRAVIS_BRANCH}-linux-clang-sanitize" - compiler: clang - before_script: - - mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR} - - ${SRC_DIR}/configure ${CONFIG} --extra-cflags="-fsanitize=undefined -Werror" || { cat config.log && exit 1; } - - - - name: "Clang (other-softmmu)" - env: - - CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS} - --host-cc=clang --cxx=clang++" - - CACHE_NAME="${TRAVIS_BRANCH}-linux-clang-default" - compiler: clang - - - # gprof/gcov are GCC features - - name: "GCC gprof/gcov" - env: - - CONFIG="--enable-gprof --enable-gcov --disable-pie --target-list=${MAIN_SOFTMMU_TARGETS}" - after_success: - - ${SRC_DIR}/scripts/travis/coverage-summary.sh - - - # We manually include builds which we disable "make check" for - - name: "GCC without-default-devices (softmmu)" - env: - - CONFIG="--without-default-devices --disable-user" - - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default" - - TEST_CMD="" - - - # Check the TCG interpreter (TCI) - - name: "GCC TCI" - env: - - CONFIG="--enable-debug-tcg --enable-tcg-interpreter --disable-kvm --disable-containers - --target-list=alpha-softmmu,arm-softmmu,hppa-softmmu,m68k-softmmu,microblaze-softmmu,moxie-softmmu,ppc-softmmu,s390x-softmmu,x86_64-softmmu" - - TEST_CMD="make check-qtest check-tcg V=1" - - - # We don't need to exercise every backend with every front-end - - name: "GCC trace log,simple,syslog (user)" - env: - - CONFIG="--enable-trace-backends=log,simple,syslog --disable-system" - - TEST_CMD="" - - - - name: "GCC trace ftrace (x86_64-softmmu)" - env: - - CONFIG="--enable-trace-backends=ftrace --target-list=x86_64-softmmu" - - TEST_CMD="" - - - - name: "GCC trace ust (x86_64-softmmu)" - env: - - CONFIG="--enable-trace-backends=ust --target-list=x86_64-softmmu" - - TEST_CMD="" - - - # Python builds - - name: "GCC Python 3.5 (x86_64-softmmu)" - env: - - CONFIG="--target-list=x86_64-softmmu" - - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default" - language: python - python: 3.5 - - - - name: "GCC Python 3.6 (x86_64-softmmu)" - env: - - CONFIG="--target-list=x86_64-softmmu" - - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default" - language: python - python: 3.6 - - - # Using newer GCC with sanitizers - - name: "GCC9 with sanitizers (softmmu)" - addons: - apt: - update: true - sources: - # PPAs for newer toolchains - - ubuntu-toolchain-r-test - packages: - # Extra toolchains - - gcc-9 - - g++-9 - # Build dependencies - - libaio-dev - - libattr1-dev - - libbrlapi-dev - - libcap-ng-dev - - libgnutls-dev - - libgtk-3-dev - - libiscsi-dev - - liblttng-ust-dev - - libnfs-dev - - libncurses5-dev - - libnss3-dev - - libpixman-1-dev - - libpng12-dev - - librados-dev - - libsdl2-dev - - libsdl2-image-dev - - libseccomp-dev - - libspice-protocol-dev - - libspice-server-dev - - libssh-dev - - liburcu-dev - - libusb-1.0-0-dev - - libvte-2.91-dev - - sparse - - uuid-dev - language: generic - compiler: none - env: - - COMPILER_NAME=gcc CXX=g++-9 CC=gcc-9 - - CONFIG="--cc=gcc-9 --cxx=g++-9 --disable-pie --disable-linux-user" - - TEST_CMD="" - before_script: - - mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR} - - ${SRC_DIR}/configure ${CONFIG} --extra-cflags="-g3 -O0 -Wno-error=stringop-truncation -fsanitize=thread" --extra-ldflags="-fuse-ld=gold" || { cat config.log && exit 1; } - - - # Run check-tcg against linux-user - - name: "GCC check-tcg (user)" - env: - - CONFIG="--disable-system --enable-debug-tcg" - - TEST_BUILD_CMD="make build-tcg" - - TEST_CMD="make check-tcg" - - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg" - - - # Run check-tcg against linux-user (with plugins) - # we skip sparc64-linux-user until it has been fixed somewhat - # we skip cris-linux-user as it doesn't use the common run loop - # we skip ppc64abi32-linux-user as it seems to have a broken libc - - name: "GCC plugins check-tcg (user)" - env: - - CONFIG="--disable-system --enable-plugins --enable-debug-tcg --target-list-exclude=sparc64-linux-user,cris-linux-user,ppc64abi32-linux-user" - - TEST_BUILD_CMD="make build-tcg" - - TEST_CMD="make check-tcg" - - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg" - - - # Run check-tcg against softmmu targets - - name: "GCC check-tcg (some-softmmu)" - env: - - CONFIG="--enable-debug-tcg --target-list=xtensa-softmmu,arm-softmmu,aarch64-softmmu,alpha-softmmu" - - TEST_BUILD_CMD="make build-tcg" - - TEST_CMD="make check-tcg" - - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg" - - - # Run check-tcg against softmmu targets (with plugins) - - name: "GCC plugins check-tcg (some-softmmu)" - env: - - CONFIG="--enable-plugins --enable-debug-tcg --target-list=xtensa-softmmu,arm-softmmu,aarch64-softmmu,alpha-softmmu" - - TEST_BUILD_CMD="make build-tcg" - - TEST_CMD="make check-tcg" - - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg" - name: "[aarch64] GCC check-tcg" arch: arm64 - dist: xenial + dist: focal addons: apt_packages: - libaio-dev @@ -402,16 +147,17 @@ jobs: - libusb-1.0-0-dev - libvdeplug-dev - libvte-2.91-dev + - ninja-build # Tests dependencies - genisoimage env: - TEST_CMD="make check check-tcg V=1" - - CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS}" + - CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS} --cxx=/bin/false" - UNRELIABLE=true - name: "[ppc64] GCC check-tcg" arch: ppc64le - dist: xenial + dist: focal addons: apt_packages: - libaio-dev @@ -435,6 +181,7 @@ jobs: - libusb-1.0-0-dev - libvdeplug-dev - libvte-2.91-dev + - ninja-build # Tests dependencies - genisoimage env: @@ -467,6 +214,7 @@ jobs: - libusb-1.0-0-dev - libvdeplug-dev - libvte-2.91-dev + - ninja-build # Tests dependencies - genisoimage env: @@ -506,6 +254,7 @@ jobs: - libzstd-dev - nettle-dev - xfslibs-dev + - ninja-build # Tests dependencies - genisoimage env: @@ -519,6 +268,7 @@ jobs: apt_packages: - libgcrypt20-dev - libgnutls28-dev + - ninja-build env: - CONFIG="--disable-containers --disable-system" @@ -549,6 +299,7 @@ jobs: - libusb-1.0-0-dev - libvdeplug-dev - libvte-2.91-dev + - ninja-build env: - TEST_CMD="make check-unit" - CONFIG="--disable-containers --disable-tcg --enable-kvm @@ -573,7 +324,7 @@ jobs: - ls -l ${SRC_DIR}/qemu-${QEMU_VERSION}.tar.bz2 - tar -xf ${SRC_DIR}/qemu-${QEMU_VERSION}.tar.bz2 && cd qemu-${QEMU_VERSION} - mkdir -p release-build && cd release-build - - ../configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; } + - ../configure ${BASE_CONFIG} ${CONFIG} || { cat config.log meson-logs/meson-log.txt && exit 1; } - make install allow_failures: - env: UNRELIABLE=true diff --git a/Changelog b/Changelog deleted file mode 100644 index 4a90bb9e8b..0000000000 --- a/Changelog +++ /dev/null @@ -1,580 +0,0 @@ -This file documents changes for QEMU releases 0.12 and earlier. -For changelog information for later releases, see -https://wiki.qemu.org/ChangeLog or look at the git history for -more detailed information. - - -version 0.12.0: - - - Update to SeaBIOS 0.5.0 - - e1000: fix device link status in Linux (Anthony Liguori) - - monitor: fix QMP for balloon command (Luiz Capitulino) - - QMP: Return an empty dict by default (Luiz Capitulino) - - QMP: Only handle converted commands (Luiz Capitulino) - - pci: support PCI based option rom loading (Gerd Hoffman/Anthony Liguori) - - Fix backcompat for hotplug of SCSI controllers (Daniel P. Berrange) - - fdc: fix migration from 0.11 (Juan Quintela) - - vmware-vga: fix segv on cursor resize. (Dave Airlie) - - vmware-vga: various fixes (Dave Airlie/Anthony Liguori) - - qdev: improve property error reporting. (Gerd Hoffmann) - - fix vga names in default_list (Gerd Hoffmann) - - usb-host: check mon before using it. (Gerd Hoffmann) - - usb-net: use qdev for -usbdevice (Gerd Hoffmann) - - monitor: Catch printing to non-existent monitor (Luiz Capitulino) - - Avoid permanently disabled QEMU monitor when UNIX migration fails (Daniel P. Berrange) - - Fix loading of ELF multiboot kernels (Kevin Wolf) - - qemu-io: Fix memory leak (Kevin Wolf) - - Fix thinko in linuxboot.S (Paolo Bonzini) - - target-i386: Fix evaluation of DR7 register (Jan Kiszka) - - vnc: hextile: do not generate ForegroundSpecified and SubrectsColoured tiles (Anthony Liguori) - - S390: Bail out without KVM (Alexander Graf) - - S390: Don't tell guest we're updating config space (Alexander Graf) - - target-s390: Fail on unknown instructions (Alexander Graf) - - osdep: Fix runtime failure on older Linux kernels (Andre Przywara) - - Fix a make -j race (Juergen Lock) - - target-alpha: Fix generic ctz64. (Richard Henderson) - - s390: Fix buggy assignment (Stefan Weil) - - target-mips: fix user-mode emulation startup (Nathan Froyd) - - target-i386: Update CPUID feature set for TCG (Andre Przywara) - - s390: fix build on 32 bit host (Michael S. Tsirkin) - -version 0.12.0-rc2: - - - v2: properly save kvm system time msr registers (Glauber Costa) - - convert more monitor commands to qmp (Luiz Capitulino) - - vnc: fix capslock tracking logic. (Gerd Hoffmann) - - QemuOpts: allow larger option values. (Gerd Hoffmann) - - scsi: fix drive hotplug. (Gerd Hoffmann) - - pci: don't hw_error() when no slot is available. (Gerd Hoffmann) - - pci: don't abort() when trying to hotplug with acpi off. (Gerd Hoffmann) - - allow default devices to be implemented in config file (Gerd Hoffman) - - vc: colorize chardev title line with blue background. (Gerd Hoffmann) - - chardev: make chardevs specified in config file work. (Gerd Hoffmann) - - qdev: also match bus name for global properties (Gerd Hoffmann) - - qdev: add command line option to set global defaults for properties. (Gerd Hoffmann) - - kvm: x86: Save/restore exception_index (Jan Kiszka) - - qdev: Replace device names containing whitespace (Markus Armbruster) - - fix rtc-td-hack on host without high-res timers (Gleb Natapov) - - virtio: verify features on load (Michael S. Tsirkin) - - vmware_vga: add rom file so that it boots. (Dave Airlie) - - Do not abort on qemu_malloc(0) in production builds (Anthony Liguori) - - Fix ARM userspace strex implementation. (Paul Brook) - - qemu: delete rule target on error (Michael S. Tsirkin) - - QMP: add human-readable description to error response (Markus Armbruster) - - convert more monitor commands to QError (Markus Armbruster) - - monitor: Fix double-prompt after "change vnc passwd BLA" (Markus Armbruster) - - monitor: do_cont(): Don't ask for passwords (Luiz Capitulino) - - monitor: Introduce 'block_passwd' command (Luiz Capitulino) - - pci: interrupt disable bit support (Michael S. Tsirkin) - - pci: interrupt status bit implementation (Michael S. Tsirkin) - - pci: prepare irq code for interrupt state (Michael S. Tsirkin) - - msix: function mask support (Michael S. Tsirkin) - - msix: macro rename for function mask support (Michael S. Tsirkin) - - cpuid: Fix multicore setup on Intel (Andre Przywara) - - kvm: x86: Fix initial kvm_has_msr_star (Jan Kiszka) - - Update OpenBIOS images to r640 (Aurelien Jarno) - -version 0.10.2: - - - fix savevm/loadvm (Anthony Liguori) - - live migration: fix dirty tracking windows (Glauber Costa) - - live migration: improve error propagation (Glauber Costa) - - qcow2: fix image creation for > ~2TB images (Chris Wright) - - hotplug: fix error handling for if= parameter (Eduardo Habkost) - - qcow2: fix data corruption (Nolan Leake) - - virtio: fix guest oops with 2.6.25 kernels (Rusty Russell) - - SH4: add support for -kernel (Takashi Yoshii, Aurelien Jarno) - - hotplug: fix closing of char devices (Jan Kiszka) - - hotplug: remove incorrect check for device name (Eduardo Habkost) - - enable -k on win32 (Herve Poussineau) - - configure: use LANG=C for grep (Andreas Faerber) - - fix VGA regression (malc) - -version 0.10.1: - - - virtio-net: check right return size on sg list (Alex Williamson) - - Make qemu_announce_self handle holes (live migration after hotplug) - (Marcelo Tosatti) - - Revert r6804-r6808 (qcow2 allocation info). This series of changes added - a high cost to startup for large qcow2 images (Anthony Liguori) - - qemu-img: fix help message (Aurelien Jarno) - - Fix build for non-default installs of SDL (Anthony Liguori) - - Fix race condition in env->interrupt_request. When using TCG and a dynticks - host timer, this condition could cause TCG to get stuck in an infinite - loop (Aurelien Jarno) - - Fix reading encrypted hard disk passwords during early startup (Jan Kiszka) - - Fix encrypted disk reporting in 'info block' (Jan Kiszka) - - Fix console size with tiny displays (MusicPal) (Jan Kiszka) - - Improve error handling in bdrv_open2 (Jan Kiszka) - - Avoid leaking data in mux'ed character devices (Jan Kiszka) - - Fix initial character device reset (no banner in monitor) (Jan Kiszka) - - Fix cpuid KVM crash on i386 host (Lubomir Rintel) - - Fix SLES10sp2 installation by adding ISTAT1 register to LSI SCSI emulation - (Ryan Harper) - -version 0.10.0: - - - TCG support (No longer requires GCC 3.x) - - Kernel Virtual Machine acceleration support - - BSD userspace emulation - - Bluetooth emulation and host passthrough support - - GDB XML register description support - - Intel e1000 emulation - - HPET emulation - - VirtIO paravirtual device support - - Marvell 88w8618 / MusicPal emulation - - Nokia N-series tablet emulation / OMAP2 processor emulation - - PCI hotplug support - - Live migration and new save/restore formats - - Curses display support - - qemu-nbd utility to mount supported block formats - - Altivec support in PPC emulation and new firmware (OpenBIOS) - - Multiple VNC clients are now supported - - TLS encryption is now supported in VNC - - MIPS Magnum R4000 machine (HervĂ© Poussineau) - - Braille support (Samuel Thibault) - - Freecom MusicPal system emulation (Jan Kiszka) - - OMAP242x and Nokia N800, N810 machines (Andrzej Zaborowski) - - EsounD audio driver (Frederick Reeve) - - Gravis Ultrasound GF1 sound card (Tibor "TS" SchĂŒtz) - - Many, many, bug fixes and new features - -version 0.9.1: - - - TFTP booting from host directory (Anthony Liguori, Erwan Velu) - - Tap device emulation for Solaris (Sittichai Palanisong) - - Monitor multiplexing to several I/O channels (Jason Wessel) - - ds1225y nvram support (Herve Poussineau) - - CPU model selection support (J. Mayer, Paul Brook, Herve Poussineau) - - Several Sparc fixes (Aurelien Jarno, Blue Swirl, Robert Reif) - - MIPS 64-bit FPU support (Thiemo Seufer) - - Xscale PDA emulation (Andrzej Zaborowski) - - ColdFire system emulation (Paul Brook) - - Improved SH4 support (Magnus Damm) - - MIPS64 support (Aurelien Jarno, Thiemo Seufer) - - Preliminary Alpha guest support (J. Mayer) - - Read-only support for Parallels disk images (Alex Beregszaszi) - - SVM (x86 virtualization) support (Alexander Graf) - - CRIS emulation (Edgar E. Iglesias) - - SPARC32PLUS execution support (Blue Swirl) - - MIPS mipssim pseudo machine (Thiemo Seufer) - - Strace for Linux userland emulation (Stuart Anderson, Thayne Harbaugh) - - OMAP310 MPU emulation plus Palm T|E machine (Andrzej Zaborowski) - - ARM v6, v7, NEON SIMD and SMP emulation (Paul Brook/CodeSourcery) - - Gumstix boards: connex and verdex emulation (Thorsten Zitterell) - - Intel mainstone II board emulation (Armin Kuster) - - VMware SVGA II graphics card support (Andrzej Zaborowski) - -version 0.9.0: - - - Support for relative paths in backing files for disk images - - Async file I/O API - - New qcow2 disk image format - - Support of multiple VM snapshots - - Linux: specific host CDROM and floppy support - - SMM support - - Moved PCI init, MP table init and ACPI table init to Bochs BIOS - - Support for MIPS32 Release 2 instruction set (Thiemo Seufer) - - MIPS Malta system emulation (Aurelien Jarno, Stefan Weil) - - Darwin userspace emulation (Pierre d'Herbemont) - - m68k user support (Paul Brook) - - several x86 and x86_64 emulation fixes - - Mouse relative offset VNC extension (Anthony Liguori) - - PXE boot support (Anthony Liguori) - - '-daemonize' option (Anthony Liguori) - -version 0.8.2: - - - ACPI support - - PC VGA BIOS fixes - - switch to OpenBios for SPARC targets (Blue Swirl) - - VNC server fixes - - MIPS FPU support (Marius Groeger) - - Solaris/SPARC host support (Juergen Keil) - - PPC breakpoints and single stepping (Jason Wessel) - - USB updates (Paul Brook) - - UDP/TCP/telnet character devices (Jason Wessel) - - Windows sparse file support (Frediano Ziglio) - - RTL8139 NIC TCP segmentation offloading (Igor Kovalenko) - - PCNET NIC support (Antony T Curtis) - - Support for variable frequency host CPUs - - Workaround for win32 SMP hosts - - Support for AMD Flash memories (Jocelyn Mayer) - - Audio capture to WAV files support (malc) - -version 0.8.1: - - - USB tablet support (Brad Campbell, Anthony Liguori) - - win32 host serial support (Kazu) - - PC speaker support (Joachim Henke) - - IDE LBA48 support (Jens Axboe) - - SSE3 support - - Solaris port (Juergen Keil) - - Preliminary SH4 target (Samuel Tardieu) - - VNC server (Anthony Liguori) - - slirp fixes (Ed Swierk et al.) - - USB fixes - - ARM Versatile Platform Baseboard emulation (Paul Brook) - -version 0.8.0: - - - ARM system emulation: Arm Integrator/CP board with an arm1026ej-s - cpu (Paul Brook) - - SMP support - - Mac OS X cocoa improvements (Mike Kronenberg) - - Mac OS X CoreAudio driver (Mike Kronenberg) - - DirectSound driver (malc) - - ALSA audio driver (malc) - - new audio options: '-soundhw' and '-audio-help' (malc) - - ES1370 PCI audio device (malc) - - Initial USB support - - Linux host serial port access - - Linux host low level parallel port access - - New network emulation code supporting VLANs. - - MIPS and MIPSel User Linux emulation - - MIPS fixes to boot Linux (Daniel Jacobowitz) - - NX bit support - - Initial SPARC SMP support (Blue Swirl) - - Major overhaul of the virtual FAT driver for read/write support - (Johannes Schindelin) - -version 0.7.2: - - - x86_64 fixes (Win2000 and Linux 2.6 boot in 32 bit) - - merge self modifying code handling in dirty ram page mecanism. - - MIPS fixes (Ralf Baechle) - - better user net performances - -version 0.7.1: - - - read-only Virtual FAT support (Johannes Schindelin) - - Windows 2000 install disk full hack (original idea from Vladimir - N. Oleynik) - - VMDK disk image creation (Filip Navara) - - SPARC64 progress (Blue Swirl) - - initial MIPS support (Jocelyn mayer) - - MIPS improvements (Ralf Baechle) - - 64 bit fixes in user networking (initial patch by Gwenole Beauchesne) - - IOAPIC support (Filip Navara) - -version 0.7.0: - - - better BIOS translation and HDD geometry auto-detection - - user mode networking bug fix - - undocumented FPU ops support - - Cirrus VGA: support for 1280x1024x[8,15,16] modes - - 'pidfile' option - - .dmg disk image format support (Johannes Schindelin) - - keymaps support (initial patch by Johannes Schindelin) - - big endian ARM support (Lennert Buytenhek) - - added generic 64 bit target support - - x86_64 target support - - initial APIC support - - MMX/SSE/SSE2/PNI support - - PC parallel port support (Mark Jonckheere) - - initial SPARC64 support (Blue Swirl) - - SPARC target boots Linux (Blue Swirl) - - armv5te user mode support (Paul Brook) - - ARM VFP support (Paul Brook) - - ARM "Angel" semihosting syscalls (Paul Brook) - - user mode gdb stub support (Paul Brook) - - Samba 3 support - - initial Cocoa support (Pierre d'Herbemont) - - generic FPU emulation code - - Virtual PC read-only disk image support (Alex Beregszaszi) - -version 0.6.1: - - - Mac OS X port (Pierre d'Herbemont) - - Virtual console support - - Better monitor line edition - - New block device layer - - New 'qcow' growable disk image support with AES encryption and - transparent decompression - - VMware 3 and 4 read-only disk image support (untested) - - Support for up to 4 serial ports - - TFTP server support (Magnus Damm) - - Port redirection support in user mode networking - - Support for not executable data sections - - Compressed loop disk image support (Johannes Schindelin) - - Level triggered IRQ fix (aka NE2000 PCI performance fix) (Steve - Wormley) - - Fixed Fedora Core 2 problems (now you can run qemu without any - LD_ASSUME_KERNEL tricks on FC2) - - DHCP fix for Windows (accept DHCPREQUEST alone) - - SPARC system emulation (Blue Swirl) - - Automatic Samba configuration for host file access from Windows. - - '-loadvm' and '-full-screen' options - - ne2000 savevm support (Johannes Schindelin) - - Ctrl-Alt is now the default grab key. Ctrl-Alt-[0-9] switches to - the virtual consoles. - - BIOS floppy fix for NT4 (Mike Nordell, Derek Fawcus, Volker Ruppert) - - Floppy fixes for NT4 and NT5 (Mike Nordell) - - NT4 IDE fixes (Ben Pfaf, Mike Nordell) - - SDL Audio support and SB16 fixes (malc) - - ENTER instruction bug fix (initial patch by Stefan Kisdaroczi) - - VGA font change fix - - VGA read-only CRTC register fix - -version 0.6.0: - - - minimalist FPU exception support (NetBSD FPU probe fix) - - cr0.ET fix (Win95 boot) - - *BSD port (Markus Niemisto) - - I/O access fix (signaled by Mark Jonckheere) - - IDE drives serial number fix (Mike Nordell) - - int13 CDROM BIOS fix (aka Solaris x86 install CD fix) - - int15, ah=86 BIOS fix (aka Solaris x86 hardware probe hang up fix) - - BSR/BSF "undefined behaviour" fix - - vmdk2raw: convert VMware disk images to raw images - - PCI support - - NE2K PCI support - - dummy VGA PCI support - - VGA font selection fix (Daniel Serpell) - - PIC reset fix (Hidemi KAWAI) - - PIC spurious irq support (aka Solaris install bug) - - added '-localtime' option - - Cirrus CL-GD54xx VGA support (initial patch by Makoto Suzuki (suzu)) - - APM and system shutdown support - - Fixed system reset - - Support for other PC BIOSes - - Initial PowerMac hardware emulation - - PowerMac/PREP OpenFirmware compatible BIOS (Jocelyn Mayer) - - initial IDE BMDMA support (needed for Darwin x86) - - Set the default memory size for PC emulation to 128 MB - -version 0.5.5: - - - SDL full screen support (initial patch by malc) - - VGA support on PowerPC PREP - - VBE fixes (Matthew Mastracci) - - PIT fixes (aka Win98 hardware probe and "VGA slowness" bug) - - IDE master only fixes (aka Win98 CD-ROM probe bug) - - ARM load/store half word fix (Ulrich Hecht) - - FDC fixes for Win98 - -version 0.5.4: - - - qemu-fast fixes - - BIOS area protection fix (aka EMM386.EXE fix) (Mike Nordell) - - keyboard/mouse fix (Mike Nordell) - - IDE fixes (Linux did not recognized slave drivers) - - VM86 EIP masking fix (aka NT5 install fix) (Mike Nordell) - - QEMU can now boot a PowerPC Linux kernel (Jocelyn Mayer) - - User mode network stack - - imul imm8 fix + 0x82 opcode support (Hidemi KAWAI) - - precise self modifying code (aka BeOS install bug) - -version 0.5.3: - - - added Bochs VESA VBE support - - VGA memory map mode 3 access fix (OS/2 install fix) - - IDE fixes (Jens Axboe) - - CPU interrupt fixes - - fixed various TLB invalidation cases (NT install) - - fixed cr0.WP semantics (XP install) - - direct chaining support for SPARC and PowerPC (faster) - - ARM NWFPE support (initial patch by Ulrich Hecht) - - added specific x86 to x86 translator (close to native performance - in qemu-i386 and qemu-fast) - - shm syscalls support (Paul McKerras) - - added accurate CR0.MP/ME/TS emulation - - fixed DMA memory write access (Win95 boot floppy fix) - - graphical x86 linux loader - - command line monitor - - generic removable device support - - support of CD-ROM change - - multiple network interface support - - initial x86-64 host support (Gwenole Beauchesne) - - lret to outer privilege fix (OS/2 install fix) - - task switch fixes (SkyOS boot) - - VM save/restore commands - - new timer API - - more precise RTC emulation (periodic timers + time updates) - - Win32 port (initial patch by Kazu) - -version 0.5.2: - - - improved soft MMU speed (assembly functions and specializing) - - improved multitasking speed by avoiding flushing TBs when - switching tasks - - improved qemu-fast speed - - improved self modifying code handling (big performance gain in - softmmu mode). - - fixed IO checking - - fixed CD-ROM detection (win98 install CD) - - fixed addseg real mode bug (GRUB boot fix) - - added ROM memory support (win98 boot) - - fixed 'call Ev' in case of paging exception - - updated the script 'qemu-binfmt-conf.sh' to use QEMU automagically - when launching executables for the supported target CPUs. - - PowerPC system emulation update (Jocelyn Mayer) - - PC floppy emulation and DMA fixes (Jocelyn Mayer) - - polled mode for PIC (Jocelyn Mayer) - - fixed PTE dirty bit handling - - fixed xadd same reg bug - - fixed cmpxchg exception safeness - - access to virtual memory in gdb stub - - task gate and NT flag fixes - - eflags optimisation fix for string operations - -version 0.5.1: - - - float access fixes when using soft mmu - - PC emulation support on PowerPC - - A20 support - - IDE CD-ROM emulation - - ARM fixes (Ulrich Hecht) - - SB16 emulation (malc) - - IRET and INT fixes in VM86 mode with IOPL=3 - - Port I/Os use TSS io map - - Full task switching/task gate support - - added verr, verw, arpl, fcmovxx - - PowerPC target support (Jocelyn Mayer) - - Major SPARC target fixes (dynamically linked programs begin to work) - -version 0.5.0: - - - full hardware level VGA emulation - - graphical display with SDL - - added PS/2 mouse and keyboard emulation - - popw (%esp) fix - - mov to/from segment data width fix - - added real mode support - - added Bochs BIOS and LGPL'ed VGA BIOS loader in qemu - - m68k host port (Richard Zidlicky) - - partial soft MMU support for memory mapped I/Os - - multi-target build - - fixed: no error code in hardware interrupts - - fixed: pop ss, mov ss, x and sti disable hardware irqs for the next insn - - correct single stepping through string operations - - preliminary SPARC target support (Thomas M. Ogrisegg) - - tun-fd option (Rusty Russell) - - automatic IDE geometry detection - - renamed 'vl' to qemu[-fast] and user qemu to qemu-{cpu}. - - added man page - - added full soft mmu mode to launch unpatched OSes. - -version 0.4.3: - - - x86 exception fix in case of nop instruction. - - gcc 3.2.2 bug workaround (RedHat 9 fix) - - sparc and Alpha host fixes - - many ARM target fixes: 'ls' and 'bash' can be launched. - -version 0.4.2: - - - many exception handling fixes (can compile a Linux kernel inside vl) - - IDE emulation support - - initial GDB stub support - - deferred update support for disk images (Rusty Russell) - - accept User Mode Linux Copy On Write disk images - - SMP kernels can at least be booted - -version 0.4.1: - - - more accurate timer support in vl. - - more reliable NE2000 probe in vl. - - added 2.5.66 kernel in vl-test. - - added VLTMPDIR environment variable in vl. - -version 0.4: - - - initial support for ring 0 x86 processor emulation - - fixed signal handling for correct dosemu DPMI emulation - - fast x86 MMU emulation with mmap() - - fixed popl (%esp) case - - Linux kernel can be executed by QEMU with the 'vl' command. - -version 0.3: - - - initial support for ARM emulation - - added fnsave, frstor, fnstenv, fldenv FPU instructions - - added FPU register save in signal emulation - - initial ARM port - - Sparc and Alpha ports work on the regression test - - generic ioctl number conversion - - fixed ioctl type conversion - -version 0.2: - - - PowerPC disassembly and ELF symbols output (Rusty Russell) - - flock support (Rusty Russell) - - ugetrlimit support (Rusty Russell) - - fstat64 fix (Rusty Russell) - - initial Alpha port (Falk Hueffner) - - initial IA64 port (Matt Wilson) - - initial Sparc and Sparc64 port (David S. Miller) - - added HLT instruction - - LRET instruction fix. - - added GPF generation for I/Os. - - added INT3 and TF flag support. - - SHL instruction C flag fix. - - mmap emulation for host page size > 4KB - - self-modifying code support - - better VM86 support (dosemu works on non trivial programs) - - precise exception support (EIP is computed correctly in most cases) - - more precise LDT/GDT/IDT emulation - - faster segment load in vm86 mode - - direct chaining of basic blocks (faster emulation) - -version 0.1.6: - - - automatic library search system. QEMU can now work with unpatched - ELF dynamic loader and libc (Rusty Russell). - - ISO C warning fixes (Alistair Strachan) - - first self-virtualizable version (works only as long as the - translation cache is not flushed) - - RH9 fixes - -version 0.1.5: - - - ppc64 support + personality() patch (Rusty Russell) - - first Alpha CPU patches (Falk Hueffner) - - removed bfd.h dependency - - fixed shrd, shld, idivl and divl on PowerPC. - - fixed buggy glibc PowerPC rint() function (test-i386 passes now on PowerPC). - -version 0.1.4: - - - more accurate VM86 emulation (can launch small DOS 16 bit - executables in wine). - - fixed push/pop fs/gs - - added iret instruction. - - added times() syscall and SIOCATMARK ioctl. - -version 0.1.3: - - - S390 support (Ulrich Weigand) - - glibc 2.3.x compile fix (Ulrich Weigand) - - socketcall endian fix (Ulrich Weigand) - - struct sockaddr endian fix (Ulrich Weigand) - - sendmsg/recvmsg endian fix (Ulrich Weigand) - - execve endian fix (Ulrich Weigand) - - fdset endian fix (Ulrich Weigand) - - partial setsockopt syscall support (Ulrich Weigand) - - more accurate pushf/popf emulation - - first partial vm86() syscall support (can be used with runcom example). - - added bound, cmpxchg8b, cpuid instructions - - added 16 bit addressing support/override for string operations - - poll() fix - -version 0.1.2: - - - compile fixes - - xlat instruction - - xchg instruction memory lock - - added simple vm86 example (not working with QEMU yet). The 54 byte - DOS executable 'pi_10.com' program was released by Bertram - Felgenhauer (more information at http://www.boo.net/~jasonp/pipage.html). - -version 0.1.1: - - - glibc 2.2 compilation fixes - - added -s and -L options - - binary distribution of x86 glibc and wine - - big endian fixes in ELF loader and getdents. - -version 0.1: - - - initial public release. diff --git a/Kconfig b/Kconfig index bf694c42af..d52ebd839b 100644 --- a/Kconfig +++ b/Kconfig @@ -2,3 +2,4 @@ source Kconfig.host source backends/Kconfig source accel/Kconfig source hw/Kconfig +source semihosting/Kconfig diff --git a/Kconfig.host b/Kconfig.host index a6d871c399..24255ef441 100644 --- a/Kconfig.host +++ b/Kconfig.host @@ -1,6 +1,6 @@ # These are "proxy" symbols used to pass config-host.mak values -# down to Kconfig. See also MINIKCONF_ARGS in the Makefile: -# these two need to be kept in sync. +# down to Kconfig. See also kconfig_external_symbols in +# meson.build: these two need to be kept in sync. config LINUX bool @@ -24,6 +24,10 @@ config VHOST_USER bool select VHOST +config VHOST_VDPA + bool + select VHOST + config VHOST_KERNEL bool select VHOST @@ -33,3 +37,7 @@ config VIRTFS config PVRDMA bool + +config MULTIPROCESS_ALLOWED + bool + imply MULTIPROCESS diff --git a/MAINTAINERS b/MAINTAINERS index 0886eb3d2b..36055f14c5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -87,7 +87,7 @@ S390 general architecture support M: Cornelia Huck M: Thomas Huth S: Supported -F: default-configs/s390x-softmmu.mak +F: default-configs/*/s390x-softmmu.mak F: gdb-xml/s390*.xml F: hw/char/sclp*.[hc] F: hw/char/terminal3270.c @@ -106,20 +106,23 @@ F: docs/system/target-s390x.rst F: docs/system/s390x/ F: tests/migration/s390x/ K: ^Subject:.*(?i)s390x? -T: git https://github.com/cohuck/qemu.git s390-next +T: git https://gitlab.com/cohuck/qemu.git s390-next L: qemu-s390x@nongnu.org Guest CPU cores (TCG) --------------------- Overall TCG CPUs -M: Richard Henderson +M: Richard Henderson R: Paolo Bonzini S: Maintained F: softmmu/cpus.c F: cpus-common.c -F: exec.c +F: page-vary.c +F: page-vary-common.c F: accel/tcg/ F: accel/stubs/tcg-stub.c +F: util/cacheinfo.c +F: util/cacheflush.c F: scripts/decodetree.py F: docs/devel/decodetree.rst F: include/exec/cpu*.h @@ -128,6 +131,7 @@ F: include/exec/helper*.h F: include/exec/tb-hash.h F: include/sysemu/cpus.h F: include/sysemu/tcg.h +F: include/hw/core/tcg-cpu-ops.h FPU emulation M: Aurelien Jarno @@ -139,7 +143,7 @@ F: include/fpu/ F: tests/fp/ Alpha TCG CPUs -M: Richard Henderson +M: Richard Henderson S: Maintained F: target/alpha/ F: tests/tcg/alpha/ @@ -159,6 +163,7 @@ F: disas/arm.c F: disas/arm-a64.cc F: disas/libvixl/ F: docs/system/target-arm.rst +F: docs/system/arm/cpu-features.rst ARM SMMU M: Eric Auger @@ -169,8 +174,8 @@ F: include/hw/arm/smmu* AVR TCG CPUs M: Michael Rolnik -R: Sarah Harris S: Maintained +F: docs/system/target-avr.rst F: gdb-xml/avr-cpu.xml F: target/avr/ F: tests/acceptance/machine_avr6.py @@ -184,8 +189,17 @@ F: include/hw/cris/ F: tests/tcg/cris/ F: disas/cris.c +Hexagon TCG CPUs +M: Taylor Simpson +S: Supported +F: target/hexagon/ +F: linux-user/hexagon/ +F: tests/tcg/hexagon/ +F: disas/hexagon.c +F: default-configs/targets/hexagon-linux-user.mak + HPPA (PA-RISC) TCG CPUs -M: Richard Henderson +M: Richard Henderson S: Maintained F: target/hppa/ F: hw/hppa/ @@ -220,14 +234,14 @@ F: hw/microblaze/ F: disas/microblaze.c MIPS TCG CPUs -M: Aleksandar Markovic +M: Philippe Mathieu-DaudĂ© R: Aurelien Jarno R: Jiaxun Yang R: Aleksandar Rikalo -S: Maintained +S: Odd Fixes F: target/mips/ -F: default-configs/*mips* -F: disas/*mips* +F: default-configs/*/*mips* +F: disas/mips.c F: docs/system/cpu-models-mips.rst.inc F: hw/intc/mips_gic.c F: hw/mips/ @@ -237,18 +251,20 @@ F: include/hw/intc/mips_gic.h F: include/hw/mips/ F: include/hw/misc/mips_* F: include/hw/timer/mips_gictimer.h -F: tests/acceptance/linux_ssh_mips_malta.py -F: tests/acceptance/machine_mips_malta.py F: tests/tcg/mips/ K: ^Subject:.*(?i)mips +MIPS TCG CPUs (nanoMIPS ISA) +S: Orphan +F: disas/nanomips.* + Moxie TCG CPUs M: Anthony Green S: Maintained F: target/moxie/ F: disas/moxie.c F: hw/moxie/ -F: default-configs/moxie-softmmu.mak +F: default-configs/*/moxie-softmmu.mak NiosII TCG CPUs M: Chris Wulff @@ -256,9 +272,8 @@ M: Marek Vasut S: Maintained F: target/nios2/ F: hw/nios2/ -F: hw/intc/nios2_iic.c F: disas/nios2.c -F: default-configs/nios2-softmmu.mak +F: default-configs/*/nios2-softmmu.mak OpenRISC TCG CPUs M: Stafford Horne @@ -269,12 +284,14 @@ F: tests/tcg/openrisc/ PowerPC TCG CPUs M: David Gibson +M: Greg Kurz L: qemu-ppc@nongnu.org S: Maintained F: target/ppc/ F: hw/ppc/ F: include/hw/ppc/ F: disas/ppc.c +F: tests/acceptance/machine_ppc.py RISC-V TCG CPUs M: Palmer Dabbelt @@ -290,12 +307,12 @@ F: linux-user/host/riscv32/ F: linux-user/host/riscv64/ RENESAS RX CPUs -M: Yoshinori Sato -S: Maintained +R: Yoshinori Sato +S: Orphan F: target/rx/ S390 TCG CPUs -M: Richard Henderson +M: Richard Henderson M: David Hildenbrand S: Maintained F: target/s390x/ @@ -305,8 +322,8 @@ F: tests/tcg/s390x/ L: qemu-s390x@nongnu.org SH4 TCG CPUs -M: Yoshinori Sato -S: Odd Fixes +R: Yoshinori Sato +S: Orphan F: target/sh4/ F: hw/sh4/ F: disas/sh4.c @@ -331,7 +348,7 @@ F: include/hw/unicore32/ X86 TCG CPUs M: Paolo Bonzini -M: Richard Henderson +M: Richard Henderson M: Eduardo Habkost S: Maintained F: target/i386/ @@ -340,7 +357,7 @@ F: tests/tcg/x86_64/ F: hw/i386/ F: disas/i386.c F: docs/system/cpu-models-x86.rst.inc -T: git https://github.com/ehabkost/qemu.git x86-next +T: git https://gitlab.com/ehabkost/qemu.git x86-next Xtensa TCG CPUs M: Max Filippov @@ -351,7 +368,7 @@ F: hw/xtensa/ F: tests/tcg/xtensa/ F: disas/xtensa.c F: include/hw/xtensa/xtensa-isa.h -F: default-configs/xtensa*.mak +F: default-configs/*/xtensa*.mak TriCore TCG CPUs M: Bastian Koppelmann @@ -385,13 +402,13 @@ S: Maintained F: target/arm/kvm.c MIPS KVM CPUs -M: Huacai Chen -M: Aleksandar Markovic +M: Huacai Chen S: Odd Fixes F: target/mips/kvm.c PPC KVM CPUs M: David Gibson +M: Greg Kurz S: Maintained F: target/ppc/kvm.c @@ -414,7 +431,7 @@ F: hw/intc/s390_flic.c F: hw/intc/s390_flic_kvm.c F: include/hw/s390x/s390_flic.h F: gdb-xml/s390*.xml -T: git https://github.com/cohuck/qemu.git s390-next +T: git https://gitlab.com/cohuck/qemu.git s390-next T: git https://github.com/borntraeger/qemu.git s390-next L: qemu-s390x@nongnu.org @@ -423,17 +440,19 @@ M: Paolo Bonzini M: Marcelo Tosatti L: kvm@vger.kernel.org S: Supported -F: target/i386/kvm.c +F: target/i386/kvm/ F: scripts/kvm/vmxcap Guest CPU Cores (other accelerators) ------------------------------------ Overall -M: Richard Henderson +M: Richard Henderson R: Paolo Bonzini S: Maintained -F: include/sysemu/accel.h -F: accel/accel.c +F: include/qemu/accel.h +F: include/sysemu/accel-ops.h +F: include/hw/core/accel-cpu.h +F: accel/accel-*.c F: accel/Makefile.objs F: accel/stubs/Makefile.objs @@ -442,16 +461,13 @@ M: Cameron Esfahani M: Roman Bolshakov W: https://wiki.qemu.org/Features/HVF S: Maintained -F: accel/stubs/hvf-stub.c F: target/i386/hvf/ F: include/sysemu/hvf.h WHPX CPUs M: Sunil Muthuswamy S: Supported -F: target/i386/whpx-all.c -F: target/i386/whp-dispatch.h -F: accel/stubs/whpx-stub.c +F: target/i386/whpx/ F: include/sysemu/whpx.h Guest CPU Cores (Xen) @@ -479,6 +495,7 @@ F: include/hw/block/dataplane/xen* F: include/hw/xen/ F: include/sysemu/xen.h F: include/sysemu/xen-mapcache.h +F: stubs/xen-hw-stub.c Guest CPU Cores (HAXM) --------------------- @@ -490,7 +507,7 @@ W: https://github.com/intel/haxm/issues S: Maintained F: accel/stubs/hax-stub.c F: include/sysemu/hax.h -F: target/i386/hax-* +F: target/i386/hax/ Hosts ----- @@ -531,7 +548,7 @@ F: qemu.nsi Alpha Machines -------------- -M: Richard Henderson +M: Richard Henderson S: Maintained F: hw/alpha/ F: hw/isa/smc37c669-superio.c @@ -555,7 +572,7 @@ S: Maintained F: hw/*/allwinner-h3* F: include/hw/*/allwinner-h3* F: hw/arm/orangepi.c -F: docs/system/orangepi.rst +F: docs/system/arm/orangepi.rst ARM PrimeCell and CMSDK devices M: Peter Maydell @@ -578,12 +595,15 @@ F: include/hw/rtc/pl031.h F: include/hw/arm/primecell.h F: hw/timer/cmsdk-apb-timer.c F: include/hw/timer/cmsdk-apb-timer.h +F: tests/qtest/cmsdk-apb-timer-test.c F: hw/timer/cmsdk-apb-dualtimer.c F: include/hw/timer/cmsdk-apb-dualtimer.h +F: tests/qtest/cmsdk-apb-dualtimer-test.c F: hw/char/cmsdk-apb-uart.c F: include/hw/char/cmsdk-apb-uart.h F: hw/watchdog/cmsdk-apb-watchdog.c F: include/hw/watchdog/cmsdk-apb-watchdog.h +F: tests/qtest/cmsdk-apb-watchdog-test.c F: hw/misc/tz-ppc.c F: include/hw/misc/tz-ppc.h F: hw/misc/tz-mpc.c @@ -668,7 +688,6 @@ F: include/hw/misc/imx25_ccm.h F: include/hw/watchdog/wdt_imx2.h i.MX31 (kzm) -M: Peter Chubb M: Peter Maydell L: qemu-arm@nongnu.org S: Odd Fixes @@ -728,10 +747,17 @@ F: hw/misc/iotkit-sysctl.c F: include/hw/misc/iotkit-sysctl.h F: hw/misc/iotkit-sysinfo.c F: include/hw/misc/iotkit-sysinfo.h +F: hw/misc/armsse-cpu-pwrctrl.c +F: include/hw/misc/armsse-cpu-pwrctrl.h F: hw/misc/armsse-cpuid.c F: include/hw/misc/armsse-cpuid.h F: hw/misc/armsse-mhu.c F: include/hw/misc/armsse-mhu.h +F: hw/timer/sse-counter.c +F: include/hw/timer/sse-counter.h +F: hw/timer/sse-timer.c +F: include/hw/timer/sse-timer.h +F: tests/qtest/sse-timer-test.c F: docs/system/arm/mps2.rst Musca @@ -749,6 +775,18 @@ S: Odd Fixes F: hw/arm/musicpal.c F: docs/system/arm/musicpal.rst +Nuvoton NPCM7xx +M: Havard Skinnemoen +M: Tyrone Ting +L: qemu-arm@nongnu.org +S: Supported +F: hw/*/npcm7xx* +F: include/hw/*/npcm7xx* +F: tests/qtest/npcm7xx* +F: pc-bios/npcm7xx_bootrom.bin +F: roms/vbootrom +F: docs/system/arm/nuvoton.rst + nSeries M: Andrzej Zaborowski M: Peter Maydell @@ -787,6 +825,7 @@ F: hw/arm/raspi_platform.h F: hw/*/bcm283* F: include/hw/arm/raspi* F: include/hw/*/bcm283* +F: docs/system/arm/raspi.rst Real View M: Peter Maydell @@ -841,6 +880,7 @@ R: Leif Lindholm L: qemu-arm@nongnu.org S: Maintained F: hw/arm/sbsa-ref.c +F: docs/system/arm/sbsa.rst Sharp SL-5500 (Collie) PDA M: Peter Maydell @@ -894,7 +934,7 @@ F: hw/misc/zynq* F: include/hw/misc/zynq* X: hw/ssi/xilinx_* -Xilinx ZynqMP +Xilinx ZynqMP and Versal M: Alistair Francis M: Edgar E. Iglesias M: Peter Maydell @@ -905,6 +945,7 @@ F: include/hw/*/xlnx*.h F: include/hw/ssi/xilinx_spips.h F: hw/display/dpcd.c F: include/hw/display/dpcd.h +F: docs/system/arm/xlnx-versal-virt.rst ARM ACPI Subsystem M: Shannon Zhao @@ -984,6 +1025,7 @@ F: include/hw/*/*aspeed* F: include/hw/misc/pca9552*.h F: hw/net/ftgmac100.c F: include/hw/net/ftgmac100.h +F: docs/system/arm/aspeed.rst NRF51 M: Joel Stanley @@ -1001,9 +1043,8 @@ AVR Machines AVR MCUs M: Michael Rolnik -R: Sarah Harris S: Maintained -F: default-configs/avr-softmmu.mak +F: default-configs/*/avr-softmmu.mak F: hw/avr/ F: include/hw/char/avr_usart.h F: hw/char/avr_usart.c @@ -1014,7 +1055,6 @@ F: hw/misc/avr_power.c Arduino M: Philippe Mathieu-DaudĂ© -R: Sarah Harris S: Maintained F: hw/avr/arduino.c @@ -1029,10 +1069,10 @@ F: hw/*/etraxfs_*.c HP-PARISC Machines ------------------ HP B160L -M: Richard Henderson +M: Richard Henderson R: Helge Deller S: Odd Fixes -F: default-configs/hppa-softmmu.mak +F: default-configs/*/hppa-softmmu.mak F: hw/hppa/ F: pc-bios/hppa-firmware.img @@ -1081,11 +1121,26 @@ F: hw/nubus/* F: hw/display/macfb.c F: hw/block/swim.c F: hw/m68k/bootinfo.h +F: include/standard-headers/asm-m68k/bootinfo.h +F: include/standard-headers/asm-m68k/bootinfo-mac.h F: include/hw/misc/mac_via.h F: include/hw/nubus/* F: include/hw/display/macfb.h F: include/hw/block/swim.h +virt +M: Laurent Vivier +S: Maintained +F: hw/m68k/virt.c +F: hw/char/goldfish_tty.c +F: hw/intc/goldfish_pic.c +F: hw/intc/m68k_irqc.c +F: hw/misc/virt_ctrl.c +F: include/hw/char/goldfish_tty.h +F: include/hw/intc/goldfish_pic.h +F: include/hw/intc/m68k_irqc.h +F: include/hw/misc/virt_ctrl.h + MicroBlaze Machines ------------------- petalogix_s3adsp1800 @@ -1093,6 +1148,7 @@ M: Edgar E. Iglesias S: Maintained F: hw/microblaze/petalogix_s3adsp1800_mmu.c F: include/hw/char/xilinx_uartlite.h +F: tests/acceptance/machine_microblaze.py petalogix_ml605 M: Edgar E. Iglesias @@ -1110,10 +1166,9 @@ F: hw/display/jazz_led.c F: hw/dma/rc4030.c Malta -M: Aleksandar Markovic M: Philippe Mathieu-DaudĂ© R: Aurelien Jarno -S: Maintained +S: Odd Fixes F: hw/isa/piix4.c F: hw/acpi/piix4.c F: hw/mips/malta.c @@ -1123,40 +1178,36 @@ F: tests/acceptance/linux_ssh_mips_malta.py F: tests/acceptance/machine_mips_malta.py Mipssim -M: Aleksandar Markovic R: Aleksandar Rikalo -S: Odd Fixes +S: Orphan F: hw/mips/mipssim.c F: hw/net/mipsnet.c -R4000 -M: Aleksandar Markovic -R: Aurelien Jarno -R: Aleksandar Rikalo -S: Obsolete -F: hw/mips/r4k.c - Fuloong 2E -M: Huacai Chen +M: Huacai Chen M: Philippe Mathieu-DaudĂ© -M: Aleksandar Markovic R: Jiaxun Yang S: Odd Fixes F: hw/mips/fuloong2e.c F: hw/isa/vt82c686.c F: hw/pci-host/bonito.c +F: hw/usb/vt82c686-uhci-pci.c F: include/hw/isa/vt82c686.h Loongson-3 virtual platforms -M: Huacai Chen +M: Huacai Chen R: Jiaxun Yang S: Maintained F: hw/intc/loongson_liointc.c +F: hw/mips/loongson3_bootp.c +F: hw/mips/loongson3_bootp.h +F: hw/mips/loongson3_virt.c +F: tests/acceptance/machine_mips_loongson3v.py Boston -M: Paul Burton +M: Paul Burton R: Aleksandar Rikalo -S: Maintained +S: Odd Fixes F: hw/core/loader-fit.c F: hw/mips/boston.c F: hw/pci-host/xilinx-pcie.c @@ -1173,18 +1224,21 @@ PowerPC Machines ---------------- 405 M: David Gibson +M: Greg Kurz L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/ppc/ppc405_boards.c Bamboo M: David Gibson +M: Greg Kurz L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/ppc/ppc440_bamboo.c e500 M: David Gibson +M: Greg Kurz L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/ppc/e500* @@ -1198,6 +1252,7 @@ F: pc-bios/u-boot.e500 mpc8544ds M: David Gibson +M: Greg Kurz L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/ppc/mpc8544ds.c @@ -1206,6 +1261,7 @@ F: hw/ppc/mpc8544_guts.c New World (mac99) M: Mark Cave-Ayland R: David Gibson +R: Greg Kurz L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/ppc/mac_newworld.c @@ -1225,6 +1281,7 @@ F: pc-bios/qemu_vga.ndrv Old World (g3beige) M: Mark Cave-Ayland R: David Gibson +R: Greg Kurz L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/ppc/mac_oldworld.c @@ -1238,6 +1295,8 @@ F: pc-bios/qemu_vga.ndrv PReP M: HervĂ© Poussineau +R: David Gibson +R: Greg Kurz L: qemu-ppc@nongnu.org S: Maintained F: hw/ppc/prep.c @@ -1254,6 +1313,7 @@ F: tests/acceptance/ppc_prep_40p.py sPAPR M: David Gibson +M: Greg Kurz L: qemu-ppc@nongnu.org S: Supported F: hw/*/spapr* @@ -1271,6 +1331,7 @@ F: tests/qtest/libqos/rtas* PowerNV (Non-Virtualized) M: CĂ©dric Le Goater M: David Gibson +M: Greg Kurz L: qemu-ppc@nongnu.org S: Maintained F: hw/ppc/pnv* @@ -1290,6 +1351,8 @@ F: hw/ppc/virtex_ml507.c sam460ex M: BALATON Zoltan +R: David Gibson +R: Greg Kurz L: qemu-ppc@nongnu.org S: Maintained F: hw/ppc/sam460ex.c @@ -1314,11 +1377,35 @@ F: include/hw/riscv/opentitan.h F: include/hw/char/ibex_uart.h F: include/hw/intc/ibex_plic.h +Microchip PolarFire SoC Icicle Kit +M: Bin Meng +L: qemu-riscv@nongnu.org +S: Supported +F: hw/riscv/microchip_pfsoc.c +F: hw/char/mchp_pfsoc_mmuart.c +F: hw/misc/mchp_pfsoc_dmc.c +F: hw/misc/mchp_pfsoc_ioscb.c +F: hw/misc/mchp_pfsoc_sysreg.c +F: include/hw/riscv/microchip_pfsoc.h +F: include/hw/char/mchp_pfsoc_mmuart.h +F: include/hw/misc/mchp_pfsoc_dmc.h +F: include/hw/misc/mchp_pfsoc_ioscb.h +F: include/hw/misc/mchp_pfsoc_sysreg.h + +SiFive Machines +M: Alistair Francis +M: Bin Meng +M: Palmer Dabbelt +L: qemu-riscv@nongnu.org +S: Supported +F: hw/*/*sifive*.c +F: include/hw/*/*sifive*.h + RX Machines ----------- rx-gdbsim -M: Yoshinori Sato -S: Maintained +R: Yoshinori Sato +S: Orphan F: docs/system/target-rx.rst F: hw/rx/rx-gdbsim.c F: tests/acceptance/machine_rx_gdbsim.py @@ -1326,19 +1413,25 @@ F: tests/acceptance/machine_rx_gdbsim.py SH4 Machines ------------ R2D -M: Yoshinori Sato +R: Yoshinori Sato R: Magnus Damm -S: Maintained +S: Odd Fixes +F: hw/char/sh_serial.c F: hw/sh4/r2d.c F: hw/intc/sh_intc.c +F: hw/pci-host/sh_pci.c +F: hw/timer/sh_timer.c F: include/hw/sh4/sh_intc.h Shix -M: Yoshinori Sato +R: Yoshinori Sato R: Magnus Damm S: Odd Fixes +F: hw/block/tc58128.c +F: hw/char/sh_serial.c F: hw/sh4/shix.c F: hw/intc/sh_intc.c +F: hw/timer/sh_timer.c F: include/hw/sh4/sh_intc.h SPARC Machines @@ -1400,8 +1493,9 @@ F: hw/s390x/ F: include/hw/s390x/ F: hw/watchdog/wdt_diag288.c F: include/hw/watchdog/wdt_diag288.h -F: default-configs/s390x-softmmu.mak -T: git https://github.com/cohuck/qemu.git s390-next +F: default-configs/*/s390x-softmmu.mak +F: tests/acceptance/machine_s390_ccw_virtio.py +T: git https://gitlab.com/cohuck/qemu.git s390-next T: git https://github.com/borntraeger/qemu.git s390-next L: qemu-s390x@nongnu.org @@ -1418,8 +1512,10 @@ L: qemu-s390x@nongnu.org S390 PCI M: Matthew Rosato +M: Eric Farman S: Supported F: hw/s390x/s390-pci* +F: include/hw/s390x/s390-pci* L: qemu-s390x@nongnu.org UniCore32 Machines @@ -1454,7 +1550,7 @@ F: include/hw/southbridge/piix.h F: hw/misc/sga.c F: hw/isa/apm.c F: include/hw/isa/apm.h -F: tests/test-x86-cpuid.c +F: tests/unit/test-x86-cpuid.c F: tests/qtest/test-x86-cpuid-compat.c PC Chipset @@ -1502,6 +1598,7 @@ Machine core M: Eduardo Habkost M: Marcel Apfelbaum S: Supported +F: cpu.c F: hw/core/cpu.c F: hw/core/machine-qmp-cmds.c F: hw/core/machine.c @@ -1514,7 +1611,7 @@ F: include/hw/boards.h F: include/hw/core/cpu.h F: include/hw/cpu/cluster.h F: include/sysemu/numa.h -T: git https://github.com/ehabkost/qemu.git machine-next +T: git https://gitlab.com/ehabkost/qemu.git machine-next Xtensa Machines --------------- @@ -1536,6 +1633,14 @@ F: hw/net/opencores_eth.c Devices ------- +Xilinx CAN +M: Vikram Garhwal +M: Francisco Iglesias +S: Maintained +F: hw/net/can/xlnx-* +F: include/hw/net/xlnx-* +F: tests/qtest/xlnx-can-test* + EDU M: Jiri Slaby S: Maintained @@ -1555,7 +1660,7 @@ F: tests/qtest/ide-test.c F: tests/qtest/ahci-test.c F: tests/qtest/cdrom-test.c F: tests/qtest/libqos/ahci* -T: git https://github.com/jnsnow/qemu.git ide +T: git https://gitlab.com/jsnow/qemu.git ide IPMI M: Corey Minyard @@ -1573,7 +1678,7 @@ S: Supported F: hw/block/fdc.c F: include/hw/block/fdc.h F: tests/qtest/fdc-test.c -T: git https://github.com/jnsnow/qemu.git ide +T: git https://gitlab.com/jsnow/qemu.git ide OMAP M: Peter Maydell @@ -1581,6 +1686,7 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/*/omap* F: include/hw/arm/omap.h +F: docs/system/arm/sx1.rst IPack M: Alberto Garcia @@ -1596,6 +1702,7 @@ F: include/hw/pci/* F: hw/misc/pci-testdev.c F: hw/pci/* F: hw/pci-bridge/* +F: qapi/pci.json F: docs/pci* F: docs/specs/*pci* F: default-configs/pci.mak @@ -1611,13 +1718,13 @@ F: hw/acpi/* F: hw/smbios/* F: hw/i386/acpi-build.[hc] F: hw/arm/virt-acpi-build.c +F: qapi/acpi.json F: tests/qtest/bios-tables-test* F: tests/qtest/acpi-utils.[hc] F: tests/data/acpi/ ACPI/HEST/GHES -R: Dongjiu Geng -R: Xiang Zheng +R: Dongjiu Geng L: qemu-arm@nongnu.org S: Maintained F: hw/acpi/ghes.c @@ -1632,6 +1739,8 @@ F: hw/ppc/ppc4*.c F: hw/i2c/ppc4xx_i2c.c F: include/hw/ppc/ppc4xx.h F: include/hw/i2c/ppc4xx_i2c.h +F: hw/intc/ppc-uic.c +F: include/hw/intc/ppc-uic.h Character devices M: Marc-AndrĂ© Lureau @@ -1662,6 +1771,8 @@ S: Supported F: include/hw/scsi/* F: hw/scsi/* F: tests/qtest/virtio-scsi-test.c +F: tests/qtest/fuzz-virtio-scsi-test.c +F: tests/qtest/am53c974-test.c T: git https://github.com/bonzini/qemu.git scsi-next SSI @@ -1680,6 +1791,7 @@ F: hw/ssi/xilinx_* SD (Secure Card) M: Philippe Mathieu-DaudĂ© +M: Bin Meng L: qemu-block@nongnu.org S: Odd Fixes F: include/hw/sd/sd* @@ -1710,23 +1822,24 @@ M: Alex Williamson S: Supported F: hw/vfio/* F: include/hw/vfio/ +F: docs/igd-assign.txt vfio-ccw M: Cornelia Huck M: Eric Farman +M: Matthew Rosato S: Supported F: hw/vfio/ccw.c F: hw/s390x/s390-ccw.c F: include/hw/s390x/s390-ccw.h F: include/hw/s390x/vfio-ccw.h -T: git https://github.com/cohuck/qemu.git s390-next +T: git https://gitlab.com/cohuck/qemu.git s390-next L: qemu-s390x@nongnu.org vfio-ap -M: Christian Borntraeger M: Tony Krowiak M: Halil Pasic -M: Pierre Morel +M: Jason Herne S: Supported F: hw/s390x/ap-device.c F: hw/s390x/ap-bridge.c @@ -1771,9 +1884,10 @@ S: Odd Fixes F: hw/9pfs/ X: hw/9pfs/xen-9p* F: fsdev/ -F: docs/interop/virtfs-proxy-helper.rst +F: docs/tools/virtfs-proxy-helper.rst F: tests/qtest/virtio-9p-test.c -T: git https://github.com/gkurz/qemu.git 9p-next +T: git https://gitlab.com/gkurz/qemu.git 9p-next +T: git https://github.com/cschoenebeck/qemu.git 9p.next virtio-blk M: Stefan Hajnoczi @@ -1790,7 +1904,7 @@ M: Halil Pasic S: Supported F: hw/s390x/virtio-ccw*.[hc] F: hw/s390x/vhost-vsock-ccw.c -T: git https://github.com/cohuck/qemu.git s390-next +T: git https://gitlab.com/cohuck/qemu.git s390-next T: git https://github.com/borntraeger/qemu.git s390-next L: qemu-s390x@nongnu.org @@ -1801,7 +1915,8 @@ S: Supported F: tools/virtiofsd/* F: hw/virtio/vhost-user-fs* F: include/hw/virtio/vhost-user-fs.h -F: docs/interop/virtiofsd.rst +F: docs/tools/virtiofsd.rst +L: virtio-fs@redhat.com virtio-input M: Gerd Hoffmann @@ -1854,10 +1969,14 @@ F: include/hw/virtio/virtio-mem.h nvme M: Keith Busch +M: Klaus Jensen L: qemu-block@nongnu.org S: Supported F: hw/block/nvme* +F: include/block/nvme.h F: tests/qtest/nvme-test.c +F: docs/system/nvme.rst +T: git git://git.infradead.org/qemu-nvme.git nvme-next megasas M: Hannes Reinecke @@ -1866,6 +1985,7 @@ S: Supported F: hw/scsi/megasas.c F: hw/scsi/mfi.h F: tests/qtest/megasas-test.c +F: tests/qtest/fuzz-megasas-test.c Network packet abstractions M: Dmitry Fleytman @@ -1886,6 +2006,7 @@ Rocker M: Jiri Pirko S: Maintained F: hw/net/rocker/ +F: qapi/rocker.json F: tests/rocker/ F: docs/specs/rocker.txt @@ -1906,6 +2027,7 @@ e1000e M: Dmitry Fleytman S: Maintained F: hw/net/e1000e* +F: tests/qtest/fuzz-e1000e-test.c eepro100 M: Stefan Weil @@ -1923,7 +2045,14 @@ M: Alistair Francis S: Maintained F: hw/core/generic-loader.c F: include/hw/core/generic-loader.h -F: docs/generic-loader.txt +F: docs/system/generic-loader.rst + +Guest Loader +M: Alex BennĂ©e +S: Maintained +F: hw/core/guest-loader.c +F: docs/system/guest-loader.rst +F: tests/acceptance/boot_xen.py Intel Hexadecimal Object File Loader M: Su Hang @@ -1939,14 +2068,19 @@ F: include/hw/nvram/chrp_nvram.h F: tests/qtest/prom-env-test.c VM Generation ID -M: Ben Warren -S: Maintained +S: Orphan F: hw/acpi/vmgenid.c F: include/hw/acpi/vmgenid.h F: docs/specs/vmgenid.txt F: tests/qtest/vmgenid-test.c F: stubs/vmgenid.c +LED +M: Philippe Mathieu-DaudĂ© +S: Maintained +F: include/hw/misc/led.h +F: hw/misc/led.c + Unimplemented device M: Peter Maydell R: Philippe Mathieu-DaudĂ© @@ -2028,7 +2162,7 @@ R: Laszlo Ersek R: Gerd Hoffmann S: Supported F: docs/specs/fw_cfg.txt -F: hw/nvram/fw_cfg.c +F: hw/nvram/fw_cfg*.c F: stubs/fw_cfg.c F: include/hw/nvram/fw_cfg.h F: include/standard-headers/linux/qemu_fw_cfg.h @@ -2037,8 +2171,9 @@ F: tests/qtest/fw_cfg-test.c T: git https://github.com/philmd/qemu.git fw_cfg-next XIVE -M: David Gibson M: CĂ©dric Le Goater +R: David Gibson +R: Greg Kurz L: qemu-ppc@nongnu.org S: Supported F: hw/*/*xive* @@ -2046,9 +2181,9 @@ F: include/hw/*/*xive* F: docs/*/*xive* Renesas peripherals -M: Yoshinori Sato +R: Yoshinori Sato R: Magnus Damm -S: Maintained +S: Odd Fixes F: hw/char/renesas_sci.c F: hw/char/sh_serial.c F: hw/timer/renesas_*.c @@ -2058,13 +2193,22 @@ F: include/hw/sh4/sh.h F: include/hw/timer/renesas_*.h Renesas RX peripherals -M: Yoshinori Sato -S: Maintained +R: Yoshinori Sato +S: Orphan F: hw/intc/rx_icu.c F: hw/rx/ F: include/hw/intc/rx_icu.h F: include/hw/rx/ +CAN bus subsystem and hardware +M: Pavel Pisa +M: Vikram Garhwal +S: Maintained +W: https://canbus.pages.fel.cvut.cz/ +F: net/can/* +F: hw/net/can/* +F: include/net/can_*.h + Subsystems ---------- Audio @@ -2073,6 +2217,7 @@ S: Maintained F: audio/ F: hw/audio/ F: include/hw/audio/ +F: qapi/audio.json F: tests/qtest/ac97-test.c F: tests/qtest/es1370-test.c F: tests/qtest/intel-hda-test.c @@ -2087,12 +2232,21 @@ F: block/ F: hw/block/ F: include/block/ F: qemu-img* -F: docs/interop/qemu-img.rst +F: docs/tools/qemu-img.rst F: qemu-io* F: tests/qemu-iotests/ F: util/qemu-progress.c F: qobject/block-qdict.c -F: tests/check-block-qdict.c +F: tests/unit/check-block-qdict.c +T: git https://repo.or.cz/qemu/kevin.git block + +Storage daemon +M: Kevin Wolf +L: qemu-block@nongnu.org +S: Supported +F: storage-daemon/ +F: docs/interop/qemu-storage-daemon-qmp-ref.rst +F: docs/tools/qemu-storage-daemon.rst T: git https://repo.or.cz/qemu/kevin.git block Block I/O path @@ -2109,6 +2263,7 @@ F: migration/block* F: include/block/aio.h F: include/block/aio-wait.h F: scripts/qemugdb/aio.py +F: tests/unit/test-fdmon-epoll.c T: git https://github.com/stefanha/qemu.git block Block SCSI subsystem @@ -2121,6 +2276,7 @@ F: scsi/* Block Jobs M: John Snow +M: Vladimir Sementsov-Ogievskiy L: qemu-block@nongnu.org S: Supported F: blockjob.c @@ -2133,7 +2289,16 @@ F: block/commit.c F: block/stream.c F: block/mirror.c F: qapi/job.json -T: git https://github.com/jnsnow/qemu.git jobs +F: block/block-copy.c +F: include/block/block-copy.c +F: block/backup-top.h +F: block/backup-top.c +F: include/block/aio_task.h +F: block/aio_task.c +F: util/qemu-co-shared-resource.c +F: include/qemu/co-shared-resource.h +T: git https://gitlab.com/jsnow/qemu.git jobs +T: git https://src.openvz.org/scm/~vsementsov/qemu.git jobs Block QAPI, monitor, command line M: Markus Armbruster @@ -2158,7 +2323,7 @@ F: block/dirty-bitmap.c F: block/qcow2-bitmap.c F: migration/block-dirty-bitmap.c F: util/hbitmap.c -F: tests/test-hbitmap.c +F: tests/unit/test-hbitmap.c F: docs/interop/bitmaps.rst T: git https://repo.or.cz/qemu/ericb.git bitmaps @@ -2179,8 +2344,8 @@ Command line option argument parsing M: Markus Armbruster S: Supported F: include/qemu/option.h -F: tests/test-keyval.c -F: tests/test-qemu-opts.c +F: tests/unit/test-keyval.c +F: tests/unit/test-qemu-opts.c F: util/keyval.c F: util/qemu-option.c @@ -2198,7 +2363,7 @@ Device Tree M: Alistair Francis R: David Gibson S: Maintained -F: device_tree.c +F: softmmu/device_tree.c F: include/sysemu/device_tree.h Dump @@ -2234,7 +2399,9 @@ M: Alex BennĂ©e R: Philippe Mathieu-DaudĂ© S: Maintained F: gdbstub* +F: include/exec/gdbstub.h F: gdb-xml/ +F: tests/tcg/multiarch/gdbstub/ Memory API M: Paolo Bonzini @@ -2244,10 +2411,11 @@ F: include/exec/memop.h F: include/exec/memory.h F: include/exec/ram_addr.h F: include/exec/ramblock.h +F: softmmu/dma-helpers.c F: softmmu/ioport.c F: softmmu/memory.c +F: softmmu/physmem.c F: include/exec/memory-internal.h -F: exec.c F: scripts/coccinelle/memory-region-housekeeping.cocci SPICE @@ -2279,14 +2447,26 @@ M: Paolo Bonzini S: Maintained F: include/qemu/main-loop.h F: include/sysemu/runstate.h +F: include/sysemu/runstate-action.h F: util/main-loop.c F: util/qemu-timer.c F: softmmu/vl.c F: softmmu/main.c F: softmmu/cpus.c F: softmmu/cpu-throttle.c +F: softmmu/cpu-timers.c +F: softmmu/icount.c +F: softmmu/runstate-action.c F: qapi/run-state.json +Read, Copy, Update (RCU) +M: Paolo Bonzini +S: Maintained +F: include/qemu/rcu*.h +F: tests/unit/rcutorture.c +F: tests/unit/test-rcu-*.c +F: util/rcu.c + Human Monitor (HMP) M: Dr. David Alan Gilbert S: Maintained @@ -2324,7 +2504,7 @@ M: Igor Mammedov S: Maintained F: backends/hostmem*.c F: include/sysemu/hostmem.h -T: git https://github.com/ehabkost/qemu.git machine-next +T: git https://gitlab.com/ehabkost/qemu.git machine-next Cryptodev Backends M: Gonglei @@ -2332,11 +2512,18 @@ S: Maintained F: include/sysemu/cryptodev*.h F: backends/cryptodev*.c +Python library +M: John Snow +M: Cleber Rosa +R: Eduardo Habkost +S: Maintained +F: python/ +T: git https://gitlab.com/jsnow/qemu.git python + Python scripts M: Eduardo Habkost M: Cleber Rosa -S: Odd fixes -F: python/qemu/*py +S: Odd Fixes F: scripts/*.py F: tests/*.py @@ -2347,7 +2534,7 @@ F: scripts/simplebench/ QAPI M: Markus Armbruster -M: Michael Roth +M: Michael Roth S: Supported F: qapi/ X: qapi/*.json @@ -2355,12 +2542,13 @@ F: include/qapi/ X: include/qapi/qmp/ F: include/qapi/qmp/dispatch.h F: tests/qapi-schema/ -F: tests/test-*-visitor.c -F: tests/test-qapi-*.c -F: tests/test-qmp-*.c -F: tests/test-visitor-serialization.c +F: tests/unit/test-*-visitor.c +F: tests/unit/test-qapi-*.c +F: tests/unit/test-qmp-*.c +F: tests/unit/test-visitor-serialization.c F: scripts/qapi-gen.py F: scripts/qapi/* +F: docs/sphinx/qapidoc.py F: docs/devel/qapi* T: git https://repo.or.cz/qemu/armbru.git qapi-next @@ -2378,25 +2566,25 @@ F: qobject/ F: include/qapi/qmp/ X: include/qapi/qmp/dispatch.h F: scripts/coccinelle/qobject.cocci -F: tests/check-qdict.c -F: tests/check-qjson.c -F: tests/check-qlist.c -F: tests/check-qlit.c -F: tests/check-qnull.c -F: tests/check-qnum.c -F: tests/check-qobject.c -F: tests/check-qstring.c +F: tests/unit/check-qdict.c +F: tests/unit/check-qjson.c +F: tests/unit/check-qlist.c +F: tests/unit/check-qlit.c +F: tests/unit/check-qnull.c +F: tests/unit/check-qnum.c +F: tests/unit/check-qobject.c +F: tests/unit/check-qstring.c F: tests/data/qobject/qdict.txt T: git https://repo.or.cz/qemu/armbru.git qapi-next QEMU Guest Agent -M: Michael Roth +M: Michael Roth S: Maintained F: qga/ F: docs/interop/qemu-ga.rst +F: docs/interop/qemu-ga-ref.rst F: scripts/qemu-guest-agent/ -F: tests/test-qga.c -F: docs/interop/qemu-ga-ref.texi +F: tests/unit/test-qga.c T: git https://github.com/mdroth/qemu.git qga QOM @@ -2413,11 +2601,18 @@ F: include/monitor/qdev.h F: include/qom/ F: qapi/qom.json F: qapi/qdev.json -F: qdev-monitor.c +F: scripts/coccinelle/qom-parent-type.cocci +F: softmmu/qdev-monitor.c +F: stubs/qdev.c F: qom/ -F: tests/check-qom-interface.c -F: tests/check-qom-proplist.c -F: tests/test-qdev-global-props.c +F: tests/unit/check-qom-interface.c +F: tests/unit/check-qom-proplist.c +F: tests/unit/test-qdev-global-props.c + +QOM boilerplate conversion script +M: Eduardo Habkost +S: Maintained +F: scripts/codeconverter/ QMP M: Markus Armbruster @@ -2426,7 +2621,9 @@ F: monitor/monitor-internal.h F: monitor/qmp* F: monitor/misc.c F: monitor/monitor.c +F: qapi/control.json F: qapi/error.json +F: qapi/introspect.json F: docs/devel/*qmp-* F: docs/interop/*qmp-* F: scripts/qmp/ @@ -2440,9 +2637,10 @@ M: Laurent Vivier R: Paolo Bonzini S: Maintained F: softmmu/qtest.c -F: accel/qtest.c +F: accel/qtest/ F: tests/qtest/ -X: tests/qtest/bios-tables-test-allowed-diff.h +F: docs/devel/qgraph.rst +X: tests/qtest/bios-tables-test* Device Fuzzing M: Alexander Bulekov @@ -2452,8 +2650,10 @@ R: Stefan Hajnoczi R: Thomas Huth S: Maintained F: tests/qtest/fuzz/ +F: tests/qtest/fuzz-*test.c F: scripts/oss-fuzz/ -F: docs/devel/fuzzing.txt +F: hw/mem/sparse-mem.c +F: docs/devel/fuzzing.rst Register API M: Alistair Francis @@ -2487,10 +2687,11 @@ S: Maintained F: trace/ F: trace-events F: docs/qemu-option-trace.rst.inc +F: qapi/trace.json F: scripts/tracetool.py F: scripts/tracetool/ F: scripts/qemu-trace-stap* -F: docs/interop/qemu-trace-stap.rst +F: docs/tools/qemu-trace-stap.rst F: docs/devel/tracing.txt T: git https://github.com/stefanha/qemu.git tracing @@ -2524,6 +2725,7 @@ F: tests/vmstate-static-checker-data/ F: tests/qtest/migration-test.c F: docs/devel/migration.rst F: qapi/migration.json +F: tests/migration/ D-Bus M: Marc-AndrĂ© Lureau @@ -2538,7 +2740,7 @@ F: docs/interop/dbus-vmstate.rst Seccomp M: Eduardo Otubo S: Supported -F: qemu-seccomp.c +F: softmmu/qemu-seccomp.c F: include/sysemu/seccomp.h Cryptography @@ -2546,10 +2748,11 @@ M: Daniel P. Berrange S: Maintained F: crypto/ F: include/crypto/ -F: tests/test-crypto-* -F: tests/benchmark-crypto-* -F: tests/crypto-tls-* -F: tests/pkix_asn1_tab.c +F: qapi/crypto.json +F: tests/unit/test-crypto-* +F: tests/bench/benchmark-crypto-* +F: tests/unit/crypto-tls-* +F: tests/unit/pkix_asn1_tab.c F: qemu.sasl Coroutines @@ -2558,11 +2761,11 @@ M: Kevin Wolf S: Maintained F: util/*coroutine* F: include/qemu/coroutine* -F: tests/test-coroutine.c +F: tests/unit/test-coroutine.c Buffers M: Daniel P. Berrange -S: Odd fixes +S: Odd Fixes F: util/buffer.c F: include/qemu/buffer.h @@ -2571,7 +2774,7 @@ M: Daniel P. Berrange S: Maintained F: io/ F: include/io/ -F: tests/test-io-* +F: tests/unit/test-io-* User authorization M: Daniel P. Berrange @@ -2579,7 +2782,7 @@ S: Maintained F: authz/ F: qapi/authz.json F: include/authz/ -F: tests/test-authz-* +F: tests/unit/test-authz-* Sockets M: Daniel P. Berrange @@ -2591,10 +2794,10 @@ F: qapi/sockets.json File monitor M: Daniel P. Berrange -S: Odd fixes +S: Odd Fixes F: util/filemonitor*.c F: include/qemu/filemonitor.h -F: tests/test-util-filemonitor.c +F: tests/unit/test-util-filemonitor.c Throttling infrastructure M: Alberto Garcia @@ -2604,7 +2807,7 @@ F: include/block/throttle-groups.h F: include/qemu/throttle*.h F: util/throttle.c F: docs/throttle.txt -F: tests/test-throttle.c +F: tests/unit/test-throttle.c L: qemu-block@nongnu.org UUID @@ -2612,7 +2815,16 @@ M: Fam Zheng S: Supported F: util/uuid.c F: include/qemu/uuid.h -F: tests/test-uuid.c +F: tests/unit/test-uuid.c + +Yank feature +M: Lukas Straub +S: Odd fixes +F: util/yank.c +F: migration/yank_functions* +F: tests/unit/test-yank.c +F: include/qemu/yank.h +F: qapi/yank.json COLO Framework M: zhanghailiang @@ -2643,6 +2855,8 @@ F: include/sysemu/replay.h F: docs/replay.txt F: stubs/replay.c F: tests/acceptance/replay_kernel.py +F: tests/acceptance/reverse_debugging.py +F: qapi/replay.json IOVA Tree M: Peter Xu @@ -2705,6 +2919,17 @@ F: pc-bios/opensbi-* F: .gitlab-ci.d/opensbi.yml F: .gitlab-ci.d/opensbi/ +Clock framework +M: Luc Michel +R: Damien Hedde +S: Maintained +F: include/hw/clock.h +F: include/hw/qdev-clock.h +F: hw/core/clock.c +F: hw/core/clock-vmstate.c +F: hw/core/qdev-clock.c +F: docs/devel/clocks.rst + Usermode Emulation ------------------ Overall usermode emulation @@ -2714,15 +2939,18 @@ F: thunk.c F: accel/tcg/user-exec*.c BSD user -S: Orphan +M: Warner Losh +R: Kyle Evans +S: Maintained F: bsd-user/ -F: default-configs/*-bsd-user.mak +F: default-configs/targets/*-bsd-user.mak +T: git https://github.com/qemu-bsd-user/qemu-bsd-user bsd-user-rebase-3.1 Linux user M: Laurent Vivier S: Maintained F: linux-user/ -F: default-configs/*-linux-user.mak +F: default-configs/targets/*linux-user.mak F: scripts/qemu-binfmt-conf.sh F: scripts/update-syscalltbl.sh F: scripts/update-mips-syscall-args.sh @@ -2731,7 +2959,7 @@ F: scripts/gensyscalls.sh Tiny Code Generator (TCG) ------------------------- Common TCG code -M: Richard Henderson +M: Richard Henderson S: Maintained F: tcg/ F: include/tcg/ @@ -2741,7 +2969,9 @@ M: Alex BennĂ©e S: Maintained F: docs/devel/tcg-plugins.rst F: plugins/ -F: tests/plugin +F: tests/plugin/ +F: tests/acceptance/tcg_plugins.py +F: contrib/plugins/ AArch64 TCG target M: Richard Henderson @@ -2759,22 +2989,22 @@ F: tcg/arm/ F: disas/arm.c i386 TCG target -M: Richard Henderson +M: Richard Henderson S: Maintained F: tcg/i386/ F: disas/i386.c MIPS TCG target -M: Aleksandar Markovic +M: Philippe Mathieu-DaudĂ© R: Aurelien Jarno -R: Huacai Chen +R: Huacai Chen R: Jiaxun Yang R: Aleksandar Rikalo -S: Maintained +S: Odd Fixes F: tcg/mips/ PPC TCG target -M: Richard Henderson +M: Richard Henderson S: Odd Fixes F: tcg/ppc/ F: disas/ppc.c @@ -2788,7 +3018,7 @@ F: tcg/riscv/ F: disas/riscv.c S390 TCG target -M: Richard Henderson +M: Richard Henderson S: Maintained F: tcg/s390/ F: disas/s390.c @@ -2823,7 +3053,6 @@ F: block/rbd.c Sheepdog M: Liu Yuan L: qemu-block@nongnu.org -L: sheepdog@lists.wpkg.org S: Odd Fixes F: block/sheepdog.c @@ -2850,6 +3079,7 @@ F: block/iscsi-opts.c Network Block Device (NBD) M: Eric Blake +M: Vladimir Sementsov-Ogievskiy L: qemu-block@nongnu.org S: Maintained F: block/nbd* @@ -2858,8 +3088,9 @@ F: include/block/nbd* F: qemu-nbd.* F: blockdev-nbd.c F: docs/interop/nbd.txt -F: docs/interop/qemu-nbd.rst +F: docs/tools/qemu-nbd.rst T: git https://repo.or.cz/qemu/ericb.git nbd +T: git https://src.openvz.org/scm/~vsementsov/qemu.git nbd NFS M: Peter Lieven @@ -2891,15 +3122,18 @@ S: Supported F: block/null.c NVMe Block Driver -M: Fam Zheng +M: Stefan Hajnoczi +R: Fam Zheng L: qemu-block@nongnu.org S: Supported F: block/nvme* +F: include/block/nvme.h +T: git https://github.com/stefanha/qemu.git block Bootdevice M: Gonglei S: Maintained -F: bootdevice.c +F: softmmu/bootdevice.c Quorum M: Alberto Garcia @@ -2940,10 +3174,13 @@ F: block/dmg.c parallels M: Stefan Hajnoczi M: Denis V. Lunev +M: Vladimir Sementsov-Ogievskiy L: qemu-block@nongnu.org S: Supported F: block/parallels.c +F: block/parallels-ext.c F: docs/interop/parallels.txt +T: git https://src.openvz.org/scm/~vsementsov/qemu.git parallels qed M: Stefan Hajnoczi @@ -3010,13 +3247,28 @@ L: qemu-block@nongnu.org S: Supported F: tests/image-fuzzer/ +Vhost-user block device backend server +M: Coiby Xu +S: Maintained +F: block/export/vhost-user-blk-server.c +F: block/export/vhost-user-blk-server.h +F: include/qemu/vhost-user-server.h +F: tests/qtest/libqos/vhost-user-blk.c +F: util/vhost-user-server.c + +FUSE block device exports +M: Max Reitz +L: qemu-block@nongnu.org +S: Supported +F: block/export/fuse.c + Replication M: Wen Congyang M: Xie Changlong S: Supported F: replication* F: block/replication.c -F: tests/test-replication.c +F: tests/unit/test-replication.c F: docs/block-replication.txt PVRDMA @@ -3032,26 +3284,53 @@ F: qapi/rdma.json Semihosting M: Alex BennĂ©e S: Maintained -F: hw/semihosting/ -F: include/hw/semihosting/ +F: semihosting/ +F: include/semihosting/ +F: tests/tcg/multiarch/arm-compat-semi/ + +Multi-process QEMU +M: Elena Ufimtseva +M: Jagannathan Raman +M: John G Johnson +S: Maintained +F: docs/devel/multi-process.rst +F: docs/system/multi-process.rst +F: hw/pci-host/remote.c +F: include/hw/pci-host/remote.h +F: hw/remote/machine.c +F: include/hw/remote/machine.h +F: hw/remote/mpqemu-link.c +F: include/hw/remote/mpqemu-link.h +F: hw/remote/message.c +F: hw/remote/remote-obj.c +F: include/hw/remote/memory.h +F: hw/remote/memory.c +F: hw/remote/proxy.c +F: include/hw/remote/proxy.h +F: hw/remote/proxy-memory-listener.c +F: include/hw/remote/proxy-memory-listener.h +F: hw/remote/iohub.c +F: include/hw/remote/iohub.h Build and test automation ------------------------- -Build and test automation +Build and test automation, general continuous integration M: Alex BennĂ©e -M: Fam Zheng -R: Philippe Mathieu-DaudĂ© +M: Philippe Mathieu-DaudĂ© +M: Thomas Huth +R: Wainer dos Santos Moschetta +R: Willian Rampazzo S: Maintained F: .github/lockdown.yml +F: .gitlab-ci.yml +F: .gitlab-ci.d/ F: .travis.yml -F: scripts/travis/ -F: .shippable.yml +F: scripts/ci/ F: tests/docker/ F: tests/vm/ F: scripts/archive-source.sh +W: https://gitlab.com/qemu-project/qemu/pipelines W: https://travis-ci.org/qemu/qemu -W: https://app.shippable.com/github/qemu/qemu -W: http://patchew.org/QEMU/ FreeBSD Hosted Continuous Integration M: Ed Maste @@ -3060,13 +3339,11 @@ S: Maintained F: .cirrus.yml W: https://cirrus-ci.com/github/qemu/qemu -GitLab Continuous Integration -M: Thomas Huth -M: Philippe Mathieu-DaudĂ© -M: Alex BennĂ©e -R: Wainer dos Santos Moschetta +Windows Hosted Continuous Integration +M: Yonggang Luo S: Maintained -F: .gitlab-ci.yml +F: .cirrus.yml +W: https://cirrus-ci.com/github/qemu/qemu Guest Test Compilation Support M: Alex BennĂ©e @@ -3108,7 +3385,7 @@ S: Odd Fixes F: scripts/git-submodule.sh UI translations -M: Aleksandar Markovic +S: Orphan F: po/*.po Sphinx documentation configuration and build machinery @@ -3116,6 +3393,7 @@ M: Peter Maydell S: Maintained F: docs/conf.py F: docs/*/conf.py +F: docs/sphinx/ Miscellaneous ------------- diff --git a/Makefile b/Makefile index 5865f7da6f..bcbbec71a1 100644 --- a/Makefile +++ b/Makefile @@ -10,49 +10,49 @@ BUILD_DIR=$(CURDIR) # Before including a proper config-host.mak, assume we are in the source tree SRC_PATH=. -# Use bash as sh if-statement evaluation is wonky -SHELL := /usr/bin/env bash +# Don't use implicit rules or variables +# we have explicit rules for everything +MAKEFLAGS += -rR + +SHELL = /usr/bin/env bash -o pipefail + +# Usage: $(call quiet-command,command and args,"NAME","args to print") +# This will run "command and args", and either: +# if V=1 just print the whole command and args +# otherwise print the 'quiet' output in the format " NAME args to print" +# NAME should be a short name of the command, 7 letters or fewer. +# If called with only a single argument, will print nothing in quiet mode. +quiet-command-run = $(if $(V),,$(if $2,printf " %-7s %s\n" $2 $3 && ))$1 +quiet-@ = $(if $(V),,@) +quiet-command = $(quiet-@)$(call quiet-command-run,$1,$2,$3) UNCHECKED_GOALS := %clean TAGS cscope ctags dist \ - html info pdf txt \ help check-help print-% \ docker docker-% vm-help vm-test vm-build-% +all: +.PHONY: all clean distclean recurse-all dist msi FORCE + +# Don't try to regenerate Makefile or configure +# We don't generate any of them +Makefile: ; +configure: ; + # All following code might depend on configuration variables ifneq ($(wildcard config-host.mak),) -# Put the all: rule here so that config-host.mak can contain dependencies. -all: include config-host.mak git-submodule-update: +.git-submodule-status: git-submodule-update config-host.mak +Makefile: .git-submodule-status .PHONY: git-submodule-update - -git_module_status := $(shell \ - cd '$(SRC_PATH)' && \ - GIT="$(GIT)" ./scripts/git-submodule.sh status $(GIT_SUBMODULES); \ - echo $$?; \ -) - -ifeq (1,$(git_module_status)) -ifeq (no,$(GIT_UPDATE)) git-submodule-update: $(call quiet-command, \ - echo && \ - echo "GIT submodule checkout is out of date. Please run" && \ - echo " scripts/git-submodule.sh update $(GIT_SUBMODULES)" && \ - echo "from the source directory checkout $(SRC_PATH)" && \ - echo && \ - exit 1) -else -git-submodule-update: - $(call quiet-command, \ - (cd $(SRC_PATH) && GIT="$(GIT)" ./scripts/git-submodule.sh update $(GIT_SUBMODULES)), \ - "GIT","$(GIT_SUBMODULES)") -endif -endif + (GIT="$(GIT)" "$(SRC_PATH)/scripts/git-submodule.sh" $(GIT_SUBMODULES_ACTION) $(GIT_SUBMODULES)), \ + "GIT","$(GIT_SUBMODULES)") -.git-submodule-status: git-submodule-update config-host.mak +# 0. ensure the build tree is okay # Check that we're not trying to do an out-of-tree build from # a tree that's been used for an in-tree build. @@ -64,1275 +64,264 @@ seems to have been used for an in-tree build. You can fix this by running \ endif endif -CONFIG_SOFTMMU := $(if $(filter %-softmmu,$(TARGET_DIRS)),y) -CONFIG_USER_ONLY := $(if $(filter %-user,$(TARGET_DIRS)),y) -CONFIG_XEN := $(CONFIG_XEN_BACKEND) -CONFIG_ALL=y --include config-all-devices.mak --include config-all-disas.mak +# force a rerun of configure if config-host.mak is too old or corrupted +ifeq ($(MESON),) +.PHONY: config-host.mak +x := $(shell rm -rf meson-private meson-info meson-logs) +endif +ifeq ($(NINJA),) +.PHONY: config-host.mak +x := $(shell rm -rf meson-private meson-info meson-logs) +else +export NINJA +endif +ifeq ($(wildcard build.ninja),) +.PHONY: config-host.mak +x := $(shell rm -rf meson-private meson-info meson-logs) +endif +ifeq ($(origin prefix),file) +.PHONY: config-host.mak +x := $(shell rm -rf meson-private meson-info meson-logs) +endif +# 1. ensure config-host.mak is up-to-date config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/pc-bios $(SRC_PATH)/VERSION - @echo $@ is out-of-date, running configure - @./config.status + @echo config-host.mak is out-of-date, running configure + @if test -f meson-private/coredata.dat; then \ + ./config.status --skip-meson; \ + else \ + ./config.status && touch build.ninja.stamp; \ + fi + +# 2. meson.stamp exists if meson has run at least once (so ninja reconfigure +# works), but otherwise never needs to be updated +meson-private/coredata.dat: meson.stamp +meson.stamp: config-host.mak + @touch meson.stamp + +# 3. ensure generated build files are up-to-date + +ifneq ($(NINJA),) +Makefile.ninja: build.ninja + $(quiet-@){ \ + echo 'ninja-targets = \'; \ + $(NINJA) -t targets all | sed 's/:.*//; $$!s/$$/ \\/'; \ + echo 'build-files = \'; \ + $(NINJA) -t query build.ninja | sed -n '1,/^ input:/d; /^ outputs:/q; s/$$/ \\/p'; \ + } > $@.tmp && mv $@.tmp $@ +-include Makefile.ninja + +# A separate rule is needed for Makefile dependencies to avoid -n +build.ninja: build.ninja.stamp +$(build-files): +build.ninja.stamp: meson.stamp $(build-files) + $(NINJA) $(if $V,-v,) build.ninja && touch $@ +endif + +ifneq ($(MESON),) +Makefile.mtest: build.ninja scripts/mtest2make.py + $(MESON) introspect --targets --tests --benchmarks | $(PYTHON) scripts/mtest2make.py > $@ +-include Makefile.mtest +endif + +# 4. Rules to bridge to other makefiles + +ifneq ($(NINJA),) +MAKE.n = $(findstring n,$(firstword $(MAKEFLAGS))) +MAKE.k = $(findstring k,$(firstword $(MAKEFLAGS))) +MAKE.q = $(findstring q,$(firstword $(MAKEFLAGS))) +MAKE.nq = $(if $(word 2, $(MAKE.n) $(MAKE.q)),nq) +NINJAFLAGS = $(if $V,-v) $(if $(MAKE.n), -n) $(if $(MAKE.k), -k0) \ + $(filter-out -j, $(lastword -j1 $(filter -l% -j%, $(MAKEFLAGS)))) \ + +ninja-cmd-goals = $(or $(MAKECMDGOALS), all) +ninja-cmd-goals += $(foreach t, $(.tests), $(.test.deps.$t)) + +makefile-targets := build.ninja ctags TAGS cscope dist clean uninstall +# "ninja -t targets" also lists all prerequisites. If build system +# files are marked as PHONY, however, Make will always try to execute +# "ninja build.ninja". +ninja-targets := $(filter-out $(build-files) $(makefile-targets), $(ninja-targets)) +.PHONY: $(ninja-targets) run-ninja +$(ninja-targets): run-ninja + +# Use "| cat" to give Ninja a more "make-y" output. Use "+" to bypass the +# --output-sync line. +run-ninja: config-host.mak +ifneq ($(filter $(ninja-targets), $(ninja-cmd-goals)),) + +$(quiet-@)$(if $(MAKE.nq),@:, $(NINJA) -d keepdepfile \ + $(NINJAFLAGS) $(sort $(filter $(ninja-targets), $(ninja-cmd-goals))) | cat) +endif +endif # Force configure to re-run if the API symbols are updated ifeq ($(CONFIG_PLUGIN),y) config-host.mak: $(SRC_PATH)/plugins/qemu-plugins.symbols -endif -else +.PHONY: plugins +plugins: + $(call quiet-command,\ + $(MAKE) $(SUBDIR_MAKEFLAGS) -C contrib/plugins V="$(V)", \ + "BUILD", "example plugins") +endif # $(CONFIG_PLUGIN) + +else # config-host.mak does not exist config-host.mak: ifneq ($(filter-out $(UNCHECKED_GOALS),$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail)) @echo "Please call configure before running make!" @exit 1 endif -endif +endif # config-host.mak does not exist -include $(SRC_PATH)/rules.mak - -# lor is defined in rules.mak -CONFIG_BLOCK := $(call lor,$(CONFIG_SOFTMMU),$(CONFIG_TOOLS)) - -# Create QEMU_PKGVERSION and FULL_VERSION strings -# If PKGVERSION is set, use that; otherwise get version and -dirty status from git -QEMU_PKGVERSION := $(if $(PKGVERSION),$(PKGVERSION),$(shell \ - cd $(SRC_PATH); \ - if test -e .git; then \ - git describe --match 'v*' 2>/dev/null | tr -d '\n'; \ - if ! git diff-index --quiet HEAD &>/dev/null; then \ - echo "-dirty"; \ - fi; \ - fi)) - -# Either "version (pkgversion)", or just "version" if pkgversion not set -FULL_VERSION := $(if $(QEMU_PKGVERSION),$(VERSION) ($(QEMU_PKGVERSION)),$(VERSION)) - -generated-files-y = qemu-version.h config-host.h qemu-options.def - -GENERATED_QAPI_FILES = qapi/qapi-builtin-types.h qapi/qapi-builtin-types.c -GENERATED_QAPI_FILES += qapi/qapi-types.h qapi/qapi-types.c -GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-types-%.h) -GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-types-%.c) -GENERATED_QAPI_FILES += qapi/qapi-builtin-visit.h qapi/qapi-builtin-visit.c -GENERATED_QAPI_FILES += qapi/qapi-visit.h qapi/qapi-visit.c -GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.h) -GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.c) -GENERATED_QAPI_FILES += qapi/qapi-init-commands.h qapi/qapi-init-commands.c -GENERATED_QAPI_FILES += qapi/qapi-commands.h qapi/qapi-commands.c -GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.h) -GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.c) -GENERATED_QAPI_FILES += qapi/qapi-emit-events.h qapi/qapi-emit-events.c -GENERATED_QAPI_FILES += qapi/qapi-events.h qapi/qapi-events.c -GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.h) -GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.c) -GENERATED_QAPI_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h -GENERATED_QAPI_FILES += qapi/qapi-doc.texi - -# The following list considers only the storage daemon main module. All other -# modules are currently shared with the main schema, so we don't actually -# generate additional files. - -GENERATED_STORAGE_DAEMON_QAPI_FILES = storage-daemon/qapi/qapi-commands.h -GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-commands.c -GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-emit-events.h -GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-emit-events.c -GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-events.h -GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-events.c -GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-init-commands.h -GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-init-commands.c -GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-introspect.h -GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-introspect.c -GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-types.h -GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-types.c -GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-visit.h -GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-visit.c -GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-doc.texi - -generated-files-y += $(GENERATED_QAPI_FILES) -generated-files-y += $(GENERATED_STORAGE_DAEMON_QAPI_FILES) - -generated-files-y += trace/generated-tcg-tracers.h - -generated-files-y += trace/generated-helpers-wrappers.h -generated-files-y += trace/generated-helpers.h -generated-files-y += trace/generated-helpers.c - -generated-files-$(CONFIG_TRACE_UST) += trace-ust-all.h -generated-files-$(CONFIG_TRACE_UST) += trace-ust-all.c - -generated-files-y += module_block.h - -TRACE_HEADERS = trace-root.h $(trace-events-subdirs:%=%/trace.h) -TRACE_SOURCES = trace-root.c $(trace-events-subdirs:%=%/trace.c) -TRACE_DTRACE = -ifdef CONFIG_TRACE_DTRACE -TRACE_HEADERS += trace-dtrace-root.h $(trace-events-subdirs:%=%/trace-dtrace.h) -TRACE_DTRACE += trace-dtrace-root.dtrace $(trace-events-subdirs:%=%/trace-dtrace.dtrace) -endif -ifdef CONFIG_TRACE_UST -TRACE_HEADERS += trace-ust-root.h $(trace-events-subdirs:%=%/trace-ust.h) -endif - -generated-files-y += $(TRACE_HEADERS) -generated-files-y += $(TRACE_SOURCES) -generated-files-y += $(BUILD_DIR)/trace-events-all -generated-files-y += .git-submodule-status - -trace-group-name = $(shell dirname $1 | sed -e 's/[^a-zA-Z0-9]/_/g') - -tracetool-y = $(SRC_PATH)/scripts/tracetool.py -tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") - -%/trace.h: %/trace.h-timestamp - @cmp $< $@ >/dev/null 2>&1 || cp $< $@ -%/trace.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak - $(call quiet-command,$(TRACETOOL) \ - --group=$(call trace-group-name,$@) \ - --format=h \ - --backends=$(TRACE_BACKENDS) \ - $< > $@,"GEN","$(@:%-timestamp=%)") - -%/trace.c: %/trace.c-timestamp - @cmp $< $@ >/dev/null 2>&1 || cp $< $@ -%/trace.c-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak - $(call quiet-command,$(TRACETOOL) \ - --group=$(call trace-group-name,$@) \ - --format=c \ - --backends=$(TRACE_BACKENDS) \ - $< > $@,"GEN","$(@:%-timestamp=%)") - -%/trace-ust.h: %/trace-ust.h-timestamp - @cmp $< $@ >/dev/null 2>&1 || cp $< $@ -%/trace-ust.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak - $(call quiet-command,$(TRACETOOL) \ - --group=$(call trace-group-name,$@) \ - --format=ust-events-h \ - --backends=$(TRACE_BACKENDS) \ - $< > $@,"GEN","$(@:%-timestamp=%)") - -%/trace-dtrace.dtrace: %/trace-dtrace.dtrace-timestamp - @cmp $< $@ >/dev/null 2>&1 || cp $< $@ -%/trace-dtrace.dtrace-timestamp: $(SRC_PATH)/%/trace-events $(BUILD_DIR)/config-host.mak $(tracetool-y) - $(call quiet-command,$(TRACETOOL) \ - --group=$(call trace-group-name,$@) \ - --format=d \ - --backends=$(TRACE_BACKENDS) \ - $< > $@,"GEN","$(@:%-timestamp=%)") - -%/trace-dtrace.h: %/trace-dtrace.dtrace $(tracetool-y) - $(call quiet-command,dtrace -o $@ -h -s $<, "GEN","$@") - -%/trace-dtrace.o: %/trace-dtrace.dtrace $(tracetool-y) - - -trace-root.h: trace-root.h-timestamp - @cmp $< $@ >/dev/null 2>&1 || cp $< $@ -trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak - $(call quiet-command,$(TRACETOOL) \ - --group=root \ - --format=h \ - --backends=$(TRACE_BACKENDS) \ - $< > $@,"GEN","$(@:%-timestamp=%)") - -trace-root.c: trace-root.c-timestamp - @cmp $< $@ >/dev/null 2>&1 || cp $< $@ -trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak - $(call quiet-command,$(TRACETOOL) \ - --group=root \ - --format=c \ - --backends=$(TRACE_BACKENDS) \ - $< > $@,"GEN","$(@:%-timestamp=%)") - -trace-ust-root.h: trace-ust-root.h-timestamp - @cmp $< $@ >/dev/null 2>&1 || cp $< $@ -trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak - $(call quiet-command,$(TRACETOOL) \ - --group=root \ - --format=ust-events-h \ - --backends=$(TRACE_BACKENDS) \ - $< > $@,"GEN","$(@:%-timestamp=%)") - -trace-ust-all.h: trace-ust-all.h-timestamp - @cmp $< $@ >/dev/null 2>&1 || cp $< $@ -trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y) $(BUILD_DIR)/config-host.mak - $(call quiet-command,$(TRACETOOL) \ - --group=all \ - --format=ust-events-h \ - --backends=$(TRACE_BACKENDS) \ - $(trace-events-files) > $@,"GEN","$(@:%-timestamp=%)") - -trace-ust-all.c: trace-ust-all.c-timestamp - @cmp $< $@ >/dev/null 2>&1 || cp $< $@ -trace-ust-all.c-timestamp: $(trace-events-files) $(tracetool-y) $(BUILD_DIR)/config-host.mak - $(call quiet-command,$(TRACETOOL) \ - --group=all \ - --format=ust-events-c \ - --backends=$(TRACE_BACKENDS) \ - $(trace-events-files) > $@,"GEN","$(@:%-timestamp=%)") - -trace-dtrace-root.dtrace: trace-dtrace-root.dtrace-timestamp - @cmp $< $@ >/dev/null 2>&1 || cp $< $@ -trace-dtrace-root.dtrace-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak $(tracetool-y) - $(call quiet-command,$(TRACETOOL) \ - --group=root \ - --format=d \ - --backends=$(TRACE_BACKENDS) \ - $< > $@,"GEN","$(@:%-timestamp=%)") - -trace-dtrace-root.h: trace-dtrace-root.dtrace - $(call quiet-command,dtrace -o $@ -h -s $<, "GEN","$@") - -trace-dtrace-root.o: trace-dtrace-root.dtrace - -KEYCODEMAP_GEN = $(SRC_PATH)/ui/keycodemapdb/tools/keymap-gen -KEYCODEMAP_CSV = $(SRC_PATH)/ui/keycodemapdb/data/keymaps.csv - -KEYCODEMAP_FILES = \ - ui/input-keymap-atset1-to-qcode.c \ - ui/input-keymap-linux-to-qcode.c \ - ui/input-keymap-qcode-to-atset1.c \ - ui/input-keymap-qcode-to-atset2.c \ - ui/input-keymap-qcode-to-atset3.c \ - ui/input-keymap-qcode-to-linux.c \ - ui/input-keymap-qcode-to-qnum.c \ - ui/input-keymap-qcode-to-sun.c \ - ui/input-keymap-qnum-to-qcode.c \ - ui/input-keymap-usb-to-qcode.c \ - ui/input-keymap-win32-to-qcode.c \ - ui/input-keymap-x11-to-qcode.c \ - ui/input-keymap-xorgevdev-to-qcode.c \ - ui/input-keymap-xorgkbd-to-qcode.c \ - ui/input-keymap-xorgxquartz-to-qcode.c \ - ui/input-keymap-xorgxwin-to-qcode.c \ - ui/input-keymap-osx-to-qcode.c \ - $(NULL) - -generated-files-$(CONFIG_SOFTMMU) += $(KEYCODEMAP_FILES) - -ui/input-keymap-%.c: $(KEYCODEMAP_GEN) $(KEYCODEMAP_CSV) $(SRC_PATH)/ui/Makefile.objs - $(call quiet-command,\ - stem=$* && src=$${stem%-to-*} dst=$${stem#*-to-} && \ - test -e $(KEYCODEMAP_GEN) && \ - $(PYTHON) $(KEYCODEMAP_GEN) \ - --lang glib2 \ - --varname qemu_input_map_$${src}_to_$${dst} \ - code-map $(KEYCODEMAP_CSV) $${src} $${dst} \ - > $@ || rm -f $@, "GEN", "$@") - -$(KEYCODEMAP_GEN): .git-submodule-status -$(KEYCODEMAP_CSV): .git-submodule-status - -edk2-decompressed = $(basename $(wildcard pc-bios/edk2-*.fd.bz2)) -pc-bios/edk2-%.fd: pc-bios/edk2-%.fd.bz2 - $(call quiet-command,bzip2 -d -c $< > $@,"BUNZIP2",$<) - -# Don't try to regenerate Makefile or configure -# We don't generate any of them -Makefile: ; -configure: ; - -.PHONY: all clean cscope distclean html info install install-doc \ - pdf txt recurse-all dist msi FORCE - -$(call set-vpath, $(SRC_PATH)) - -LIBS+=-lz $(LIBS_TOOLS) - -vhost-user-json-y = -HELPERS-y = $(HELPERS) - -HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) += qemu-bridge-helper$(EXESUF) - -ifeq ($(CONFIG_LINUX)$(CONFIG_VIRGL)$(CONFIG_GBM)$(CONFIG_TOOLS),yyyy) -HELPERS-y += vhost-user-gpu$(EXESUF) -vhost-user-json-y += contrib/vhost-user-gpu/50-qemu-gpu.json -endif - -ifeq ($(CONFIG_SOFTMMU)$(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyyy) -HELPERS-y += virtiofsd$(EXESUF) -vhost-user-json-y += tools/virtiofsd/50-qemu-virtiofsd.json -endif - -# Sphinx does not allow building manuals into the same directory as -# the source files, so if we're doing an in-tree QEMU build we must -# build the manuals into a subdirectory (and then install them from -# there for 'make install'). For an out-of-tree build we can just -# use the docs/ subdirectory in the build tree as normal. -ifeq ($(realpath $(SRC_PATH)),$(realpath .)) -MANUAL_BUILDDIR := docs/built -else -MANUAL_BUILDDIR := docs -endif - -ifdef BUILD_DOCS -DOCS+=$(MANUAL_BUILDDIR)/system/qemu.1 -DOCS+=$(MANUAL_BUILDDIR)/tools/qemu-img.1 -DOCS+=$(MANUAL_BUILDDIR)/tools/qemu-nbd.8 -DOCS+=$(MANUAL_BUILDDIR)/interop/qemu-ga.8 -ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy) -DOCS+=$(MANUAL_BUILDDIR)/tools/virtiofsd.1 -endif -DOCS+=$(MANUAL_BUILDDIR)/system/qemu-block-drivers.7 -DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7 -DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7 -DOCS+=$(MANUAL_BUILDDIR)/system/qemu-cpu-models.7 -DOCS+=$(MANUAL_BUILDDIR)/index.html -ifdef CONFIG_VIRTFS -DOCS+=$(MANUAL_BUILDDIR)/tools/virtfs-proxy-helper.1 -endif -ifdef CONFIG_TRACE_SYSTEMTAP -DOCS+=$(MANUAL_BUILDDIR)/tools/qemu-trace-stap.1 -endif -else -DOCS= -endif - -SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory --quiet) BUILD_DIR=$(BUILD_DIR) -SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(filter %-softmmu, $(TARGET_DIRS))) -SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %.d, $(SUBDIR_DEVICES_MAK)) - -ifeq ($(SUBDIR_DEVICES_MAK),) -config-all-devices.mak: config-host.mak - $(call quiet-command,echo '# no devices' > $@,"GEN","$@") -else -config-all-devices.mak: $(SUBDIR_DEVICES_MAK) config-host.mak - $(call quiet-command, sed -n \ - 's|^\([^=]*\)=\(.*\)$$|\1:=$$(findstring y,$$(\1)\2)|p' \ - $(SUBDIR_DEVICES_MAK) | sort -u > $@, \ - "GEN","$@") -endif - --include $(SUBDIR_DEVICES_MAK_DEP) - -# This has to be kept in sync with Kconfig.host. -MINIKCONF_ARGS = \ - $(CONFIG_MINIKCONF_MODE) \ - $@ $*/config-devices.mak.d $< $(SRC_PATH)/Kconfig \ - CONFIG_TCG=$(CONFIG_TCG) \ - CONFIG_KVM=$(CONFIG_KVM) \ - CONFIG_SPICE=$(CONFIG_SPICE) \ - CONFIG_IVSHMEM=$(CONFIG_IVSHMEM) \ - CONFIG_TPM=$(CONFIG_TPM) \ - CONFIG_XEN=$(CONFIG_XEN) \ - CONFIG_OPENGL=$(CONFIG_OPENGL) \ - CONFIG_X11=$(CONFIG_X11) \ - CONFIG_VHOST_USER=$(CONFIG_VHOST_USER) \ - CONFIG_VHOST_KERNEL=$(CONFIG_VHOST_KERNEL) \ - CONFIG_VIRTFS=$(CONFIG_VIRTFS) \ - CONFIG_LINUX=$(CONFIG_LINUX) \ - CONFIG_PVRDMA=$(CONFIG_PVRDMA) - -MINIKCONF = $(PYTHON) $(SRC_PATH)/scripts/minikconf.py - -$(SUBDIR_DEVICES_MAK): %/config-devices.mak: default-configs/%.mak $(SRC_PATH)/Kconfig $(BUILD_DIR)/config-host.mak - $(call quiet-command, $(MINIKCONF) $(MINIKCONF_ARGS) \ - > $@.tmp, "GEN", "$@.tmp") - $(call quiet-command, if test -f $@; then \ - if cmp -s $@.old $@; then \ - mv $@.tmp $@; \ - cp -p $@ $@.old; \ - else \ - if test -f $@.old; then \ - echo "WARNING: $@ (user modified) out of date.";\ - else \ - echo "WARNING: $@ out of date.";\ - fi; \ - echo "Run \"$(MAKE) defconfig\" to regenerate."; \ - rm $@.tmp; \ - fi; \ - else \ - mv $@.tmp $@; \ - cp -p $@ $@.old; \ - fi,"GEN","$@"); - -defconfig: - rm -f config-all-devices.mak $(SUBDIR_DEVICES_MAK) - -ifneq ($(wildcard config-host.mak),) -include $(SRC_PATH)/Makefile.objs -endif - -dummy := $(call unnest-vars,, \ - stub-obj-y \ - authz-obj-y \ - chardev-obj-y \ - util-obj-y \ - qga-obj-y \ - elf2dmp-obj-y \ - ivshmem-client-obj-y \ - ivshmem-server-obj-y \ - virtiofsd-obj-y \ - rdmacm-mux-obj-y \ - libvhost-user-obj-y \ - vhost-user-scsi-obj-y \ - vhost-user-blk-obj-y \ - vhost-user-input-obj-y \ - vhost-user-gpu-obj-y \ - qga-vss-dll-obj-y \ - block-obj-y \ - block-obj-m \ - storage-daemon-obj-y \ - storage-daemon-obj-m \ - crypto-obj-y \ - qom-obj-y \ - io-obj-y \ - common-obj-y \ - common-obj-m \ - trace-obj-y) +SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory --quiet) include $(SRC_PATH)/tests/Makefile.include -all: $(DOCS) $(if $(BUILD_DOCS),sphinxdocs) $(TOOLS) $(HELPERS-y) recurse-all modules $(vhost-user-json-y) - -qemu-version.h: FORCE - $(call quiet-command, \ - (printf '#define QEMU_PKGVERSION "$(QEMU_PKGVERSION)"\n'; \ - printf '#define QEMU_FULL_VERSION "$(FULL_VERSION)"\n'; \ - ) > $@.tmp) - $(call quiet-command, if ! cmp -s $@ $@.tmp; then \ - mv $@.tmp $@; \ - else \ - rm $@.tmp; \ - fi) - -XEMU_DATE := $(shell date -u ) - -XEMU_COMMIT := $(shell \ - cd $(SRC_PATH); \ - if test -e .git; then \ - git rev-parse HEAD 2>/dev/null | tr -d '\n'; \ - elif test -e XEMU_COMMIT; then \ - cat XEMU_COMMIT; \ - fi) - -XEMU_BRANCH := $(shell \ - cd $(SRC_PATH); \ - if test -e .git; then \ - git symbolic-ref --short HEAD; \ - elif test -e XEMU_BRANCH; then \ - cat XEMU_BRANCH; \ - fi) - -XEMU_VERSION := $(shell \ - cd $(SRC_PATH); \ - if test -e .git; then \ - git describe --match 'xemu-v*' | cut -c 7- | tr -d '\n'; \ - elif test -e XEMU_VERSION; then \ - cat XEMU_VERSION; \ - fi) - -xemu-version.c: FORCE - $(call quiet-command, (\ - printf 'const char *xemu_version = "$(XEMU_VERSION)";\n'; \ - printf 'const char *xemu_branch = "$(XEMU_BRANCH)";\n'; \ - printf 'const char *xemu_commit = "$(XEMU_COMMIT)";\n'; \ - printf 'const char *xemu_date = "$(XEMU_DATE)";\n'; \ - ) > $@.tmp) - $(call quiet-command, if ! cmp -s $@ $@.tmp; then \ - mv $@.tmp $@; \ - else \ - rm $@.tmp; \ - fi) - -config-host.h: config-host.h-timestamp -config-host.h-timestamp: config-host.mak -qemu-options.def: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool - $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$@") - -TARGET_DIRS_RULES := $(foreach t, all fuzz clean install, $(addsuffix /$(t), $(TARGET_DIRS))) - -SOFTMMU_ALL_RULES=$(filter %-softmmu/all, $(TARGET_DIRS_RULES)) -$(SOFTMMU_ALL_RULES): $(authz-obj-y) -$(SOFTMMU_ALL_RULES): $(block-obj-y) -$(SOFTMMU_ALL_RULES): $(storage-daemon-obj-y) -$(SOFTMMU_ALL_RULES): $(chardev-obj-y) -$(SOFTMMU_ALL_RULES): $(crypto-obj-y) -$(SOFTMMU_ALL_RULES): $(io-obj-y) -$(SOFTMMU_ALL_RULES): config-all-devices.mak -ifdef DECOMPRESS_EDK2_BLOBS -$(SOFTMMU_ALL_RULES): $(edk2-decompressed) -endif - -SOFTMMU_FUZZ_RULES=$(filter %-softmmu/fuzz, $(TARGET_DIRS_RULES)) -$(SOFTMMU_FUZZ_RULES): $(authz-obj-y) -$(SOFTMMU_FUZZ_RULES): $(block-obj-y) -$(SOFTMMU_FUZZ_RULES): $(chardev-obj-y) -$(SOFTMMU_FUZZ_RULES): $(crypto-obj-y) -$(SOFTMMU_FUZZ_RULES): $(io-obj-y) -$(SOFTMMU_FUZZ_RULES): config-all-devices.mak -$(SOFTMMU_FUZZ_RULES): $(edk2-decompressed) - -.PHONY: $(TARGET_DIRS_RULES) -# The $(TARGET_DIRS_RULES) are of the form SUBDIR/GOAL, so that -# $(dir $@) yields the sub-directory, and $(notdir $@) yields the sub-goal -$(TARGET_DIRS_RULES): - $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $(dir $@) V="$(V)" TARGET_DIR="$(dir $@)" $(notdir $@),) - -# LIBFDT_lib="": avoid breaking existing trees with objects requiring -fPIC -DTC_MAKE_ARGS=-I$(SRC_PATH)/dtc VPATH=$(SRC_PATH)/dtc -C dtc V="$(V)" LIBFDT_lib="" -DTC_CFLAGS=$(CFLAGS) $(QEMU_CFLAGS) -DTC_CPPFLAGS=-I$(SRC_PATH)/dtc/libfdt - -.PHONY: dtc/all -dtc/all: .git-submodule-status dtc/libfdt - $(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(QEMU_LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt,) - -dtc/%: .git-submodule-status - @mkdir -p $@ - -# Overriding CFLAGS causes us to lose defines added in the sub-makefile. -# Not overriding CFLAGS leads to mis-matches between compilation modes. -# Therefore we replicate some of the logic in the sub-makefile. -# Remove all the extra -Warning flags that QEMU uses that Capstone doesn't; -# no need to annoy QEMU developers with such things. -CAP_CFLAGS = $(patsubst -W%,,$(CFLAGS) $(QEMU_CFLAGS)) -CAP_CFLAGS += -DCAPSTONE_USE_SYS_DYN_MEM -CAP_CFLAGS += -DCAPSTONE_HAS_ARM -CAP_CFLAGS += -DCAPSTONE_HAS_ARM64 -CAP_CFLAGS += -DCAPSTONE_HAS_POWERPC -CAP_CFLAGS += -DCAPSTONE_HAS_X86 - -.PHONY: capstone/all -capstone/all: .git-submodule-status - $(call quiet-command,$(MAKE) -C $(SRC_PATH)/capstone CAPSTONE_SHARED=no BUILDDIR="$(BUILD_DIR)/capstone" CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" CFLAGS="$(CAP_CFLAGS)" $(SUBDIR_MAKEFLAGS) $(BUILD_DIR)/capstone/$(LIBCAPSTONE)) - -.PHONY: slirp/all -slirp/all: .git-submodule-status - $(call quiet-command,$(MAKE) -C $(SRC_PATH)/slirp \ - BUILD_DIR="$(BUILD_DIR)/slirp" \ - PKG_CONFIG="$(PKG_CONFIG)" \ - CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" \ - CFLAGS="$(QEMU_CFLAGS) $(CFLAGS)" LDFLAGS="$(QEMU_LDFLAGS)") - -$(filter %/all, $(TARGET_DIRS_RULES)): libqemuutil.a $(common-obj-y) \ - $(qom-obj-y) - -$(filter %/fuzz, $(TARGET_DIRS_RULES)): libqemuutil.a $(common-obj-y) \ - $(qom-obj-y) $(crypto-user-obj-$(CONFIG_USER_ONLY)) +all: recurse-all ROM_DIRS = $(addprefix pc-bios/, $(ROMS)) ROM_DIRS_RULES=$(foreach t, all clean, $(addsuffix /$(t), $(ROM_DIRS))) # Only keep -O and -g cflags .PHONY: $(ROM_DIRS_RULES) $(ROM_DIRS_RULES): - $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $(dir $@) V="$(V)" TARGET_DIR="$(dir $@)" CFLAGS="$(filter -O% -g%,$(CFLAGS))" $(notdir $@),) + $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $(dir $@) V="$(V)" TARGET_DIR="$(dir $@)" $(notdir $@),) -.PHONY: recurse-all recurse-clean recurse-install -recurse-all: $(addsuffix /all, $(TARGET_DIRS) $(ROM_DIRS)) -recurse-clean: $(addsuffix /clean, $(TARGET_DIRS) $(ROM_DIRS)) -recurse-install: $(addsuffix /install, $(TARGET_DIRS)) -$(addsuffix /install, $(TARGET_DIRS)): all - -$(BUILD_DIR)/version.o: $(SRC_PATH)/version.rc config-host.h - $(call quiet-command,$(WINDRES) -I$(BUILD_DIR) -o $@ $<,"RC","version.o") - -Makefile: $(version-obj-y) +.PHONY: recurse-all recurse-clean +recurse-all: $(addsuffix /all, $(ROM_DIRS)) +recurse-clean: $(addsuffix /clean, $(ROM_DIRS)) ###################################################################### -# Build libraries - -libqemuutil.a: $(util-obj-y) $(trace-obj-y) $(stub-obj-y) -libvhost-user.a: $(libvhost-user-obj-y) $(util-obj-y) $(stub-obj-y) - -###################################################################### - -COMMON_LDADDS = libqemuutil.a - -qemu-img.o: qemu-img-cmds.h - -qemu-img$(EXESUF): qemu-img.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) -qemu-nbd$(EXESUF): qemu-nbd.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) -qemu-io$(EXESUF): qemu-io.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) -qemu-storage-daemon$(EXESUF): qemu-storage-daemon.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(chardev-obj-y) $(io-obj-y) $(qom-obj-y) $(storage-daemon-obj-y) $(COMMON_LDADDS) - -qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS) - -qemu-keymap$(EXESUF): qemu-keymap.o ui/input-keymap.o $(COMMON_LDADDS) - -qemu-edid$(EXESUF): qemu-edid.o hw/display/edid-generate.o $(COMMON_LDADDS) - -fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS) - -scsi/qemu-pr-helper$(EXESUF): scsi/qemu-pr-helper.o scsi/utils.o $(authz-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) -ifdef CONFIG_MPATH -scsi/qemu-pr-helper$(EXESUF): LIBS += -ludev -lmultipath -lmpathpersist -endif - -qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool - $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$@") - -qemu-ga$(EXESUF): LIBS = $(LIBS_QGA) -qemu-ga$(EXESUF): QEMU_CFLAGS += -I qga/qapi-generated - -qemu-keymap$(EXESUF): LIBS += $(XKBCOMMON_LIBS) -qemu-keymap$(EXESUF): QEMU_CFLAGS += $(XKBCOMMON_CFLAGS) - -qapi-py = $(SRC_PATH)/scripts/qapi/__init__.py \ -$(SRC_PATH)/scripts/qapi/commands.py \ -$(SRC_PATH)/scripts/qapi/common.py \ -$(SRC_PATH)/scripts/qapi/doc.py \ -$(SRC_PATH)/scripts/qapi/error.py \ -$(SRC_PATH)/scripts/qapi/events.py \ -$(SRC_PATH)/scripts/qapi/expr.py \ -$(SRC_PATH)/scripts/qapi/gen.py \ -$(SRC_PATH)/scripts/qapi/introspect.py \ -$(SRC_PATH)/scripts/qapi/parser.py \ -$(SRC_PATH)/scripts/qapi/schema.py \ -$(SRC_PATH)/scripts/qapi/source.py \ -$(SRC_PATH)/scripts/qapi/types.py \ -$(SRC_PATH)/scripts/qapi/visit.py \ -$(SRC_PATH)/scripts/qapi-gen.py - -qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \ -qga/qapi-generated/qga-qapi-visit.c qga/qapi-generated/qga-qapi-visit.h \ -qga/qapi-generated/qga-qapi-commands.h qga/qapi-generated/qga-qapi-commands.c \ -qga/qapi-generated/qga-qapi-init-commands.h qga/qapi-generated/qga-qapi-init-commands.c \ -qga/qapi-generated/qga-qapi-doc.texi: \ -qga/qapi-generated/qapi-gen-timestamp ; -qga/qapi-generated/qapi-gen-timestamp: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py) - $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \ - -o qga/qapi-generated -p "qga-" $<, \ - "GEN","$(@:%-timestamp=%)") - @>$@ - -qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json \ - $(QAPI_MODULES:%=$(SRC_PATH)/qapi/%.json) - -$(GENERATED_QAPI_FILES): qapi-gen-timestamp ; -qapi-gen-timestamp: $(qapi-modules) $(qapi-py) - $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \ - -o "qapi" -b $<, \ - "GEN","$(@:%-timestamp=%)") - @>$@ - -qapi-modules-storage-daemon = \ - $(SRC_PATH)/storage-daemon/qapi/qapi-schema.json \ - $(QAPI_MODULES_STORAGE_DAEMON:%=$(SRC_PATH)/qapi/%.json) - -$(GENERATED_STORAGE_DAEMON_QAPI_FILES): storage-daemon/qapi/qapi-gen-timestamp ; -storage-daemon/qapi/qapi-gen-timestamp: $(qapi-modules-storage-daemon) $(qapi-py) - $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \ - -o "storage-daemon/qapi" $<, \ - "GEN","$(@:%-timestamp=%)") - @>$@ - -QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qapi-commands.h qga-qapi-init-commands.h) -$(qga-obj-y): $(QGALIB_GEN) - -qemu-ga$(EXESUF): $(qga-obj-y) $(COMMON_LDADDS) - $(call LINK, $^) - -ifdef QEMU_GA_MSI_ENABLED -QEMU_GA_MSI=qemu-ga-$(ARCH).msi - -msi: $(QEMU_GA_MSI) - -$(QEMU_GA_MSI): qemu-ga.exe $(QGA_VSS_PROVIDER) - -$(QEMU_GA_MSI): config-host.mak - -$(QEMU_GA_MSI): $(SRC_PATH)/qga/installer/qemu-ga.wxs - $(call quiet-command,QEMU_GA_VERSION="$(QEMU_GA_VERSION)" QEMU_GA_MANUFACTURER="$(QEMU_GA_MANUFACTURER)" QEMU_GA_DISTRO="$(QEMU_GA_DISTRO)" BUILD_DIR="$(BUILD_DIR)" \ - wixl -o $@ $(QEMU_GA_MSI_ARCH) $(QEMU_GA_MSI_WITH_VSS) $(QEMU_GA_MSI_MINGW_DLL_PATH) $<,"WIXL","$@") -else -msi: - @echo "MSI build not configured or dependency resolution failed (reconfigure with --enable-guest-agent-msi option)" -endif - -ifneq ($(EXESUF),) -.PHONY: qemu-ga -qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI) -endif - -elf2dmp$(EXESUF): $(elf2dmp-obj-y) - $(call LINK, $^) - -ifdef CONFIG_IVSHMEM -ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) $(COMMON_LDADDS) - $(call LINK, $^) -ivshmem-server$(EXESUF): $(ivshmem-server-obj-y) $(COMMON_LDADDS) - $(call LINK, $^) -endif -vhost-user-scsi$(EXESUF): $(vhost-user-scsi-obj-y) libvhost-user.a - $(call LINK, $^) -vhost-user-blk$(EXESUF): $(vhost-user-blk-obj-y) libvhost-user.a - $(call LINK, $^) - -rdmacm-mux$(EXESUF): LIBS += "-libumad" -rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADDS) - $(call LINK, $^) - -# relies on Linux-specific syscalls -ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy) -virtiofsd$(EXESUF): $(virtiofsd-obj-y) libvhost-user.a $(COMMON_LDADDS) - $(call LINK, $^) -endif - -vhost-user-gpu$(EXESUF): $(vhost-user-gpu-obj-y) $(libvhost-user-obj-y) libqemuutil.a - $(call LINK, $^) - -ifdef CONFIG_VHOST_USER_INPUT -ifdef CONFIG_LINUX -vhost-user-input$(EXESUF): $(vhost-user-input-obj-y) libvhost-user.a libqemuutil.a - $(call LINK, $^) - -# build by default, do not install -all: vhost-user-input$(EXESUF) -endif -endif - -module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak - $(call quiet-command,$(PYTHON) $< $@ \ - $(addprefix $(SRC_PATH)/,$(patsubst %.mo,%.c,$(block-obj-m))), \ - "GEN","$@") - -ifdef CONFIG_GCOV -.PHONY: clean-coverage -clean-coverage: - $(call quiet-command, \ - find . \( -name '*.gcda' -o -name '*.gcov' \) -type f -exec rm {} +, \ - "CLEAN", "coverage files") -endif clean: recurse-clean + -$(quiet-@)test -f build.ninja && $(NINJA) $(NINJAFLAGS) -t clean || : + -$(quiet-@)test -f build.ninja && $(NINJA) $(NINJAFLAGS) clean-ctlist || : # avoid old build problems by removing potentially incorrect old files rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h - rm -f qemu-options.def - rm -f *.msi - find . \( -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f \ + find . \( -name '*.so' -o -name '*.dll' -o -name '*.[oda]' \) -type f \ ! -path ./roms/edk2/ArmPkg/Library/GccLto/liblto-aarch64.a \ ! -path ./roms/edk2/ArmPkg/Library/GccLto/liblto-arm.a \ - ! -path ./roms/edk2/BaseTools/Source/Python/UPT/Dll/sqlite3.dll \ -exec rm {} + - rm -f $(edk2-decompressed) - rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) TAGS cscope.* *.pod *~ */*~ + rm -f TAGS cscope.* *.pod *~ */*~ rm -f fsdev/*.pod scsi/*.pod - rm -f qemu-img-cmds.h - rm -f ui/shader/*-vert.h ui/shader/*-frag.h - @# May not be present in generated-files-y - rm -f trace/generated-tracers-dtrace.dtrace* - rm -f trace/generated-tracers-dtrace.h* - rm -f $(foreach f,$(generated-files-y),$(f) $(f)-timestamp) - rm -f qapi-gen-timestamp - rm -f storage-daemon/qapi/qapi-gen-timestamp - rm -rf qga/qapi-generated - rm -f config-all-devices.mak - rm -f $(SUBDIR_DEVICES_MAK) -VERSION ?= $(shell cat VERSION) +VERSION = $(shell cat $(SRC_PATH)/VERSION) dist: qemu-$(VERSION).tar.bz2 qemu-%.tar.bz2: $(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.bz2,%,$@)" -define clean-manual = -rm -rf $(MANUAL_BUILDDIR)/$1/_static -rm -f $(MANUAL_BUILDDIR)/$1/objects.inv $(MANUAL_BUILDDIR)/$1/searchindex.js $(MANUAL_BUILDDIR)/$1/*.html -endef - distclean: clean - rm -f config-host.mak config-host.h* $(DOCS) + -$(quiet-@)test -f build.ninja && $(NINJA) $(NINJAFLAGS) -t clean -g || : + rm -f config-host.mak config-host.h* rm -f tests/tcg/config-*.mak - rm -f config-all-devices.mak config-all-disas.mak config.status - rm -f $(SUBDIR_DEVICES_MAK) - rm -f po/*.mo tests/qemu-iotests/common.env + rm -f config-all-disas.mak config.status rm -f roms/seabios/config.mak roms/vgabios/config.mak rm -f qemu-plugins-ld.symbols qemu-plugins-ld64.symbols + rm -f *-config-target.h *-config-devices.mak *-config-devices.h + rm -rf meson-private meson-logs meson-info compile_commands.json + rm -f Makefile.ninja Makefile.mtest build.ninja.stamp meson.stamp rm -f config.log rm -f linux-headers/asm - rm -f docs/version.texi - rm -f docs/interop/qemu-ga-qapi.texi docs/interop/qemu-qmp-qapi.texi - rm -f docs/interop/qemu-qmp-ref.7 docs/interop/qemu-ga-ref.7 - rm -f docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt - rm -f docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf - rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html - rm -rf .doctrees - $(call clean-manual,devel) - $(call clean-manual,interop) - $(call clean-manual,specs) - $(call clean-manual,system) - $(call clean-manual,tools) - $(call clean-manual,user) - for d in $(TARGET_DIRS); do \ - rm -rf $$d || exit 1 ; \ - done rm -Rf .sdk -KEYMAPS=da en-gb et fr fr-ch is lt no pt-br sv \ -ar de en-us fi fr-be hr it lv nl pl ru th \ -de-ch es fo fr-ca hu ja mk pt sl tr \ -bepo cz - -ifdef INSTALL_BLOBS -BLOBS=bios.bin bios-256k.bin bios-microvm.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \ -vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \ -vgabios-ramfb.bin vgabios-bochs-display.bin vgabios-ati.bin \ -openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \ -pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \ -pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \ -efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \ -efi-pcnet.rom efi-rtl8139.rom efi-virtio.rom \ -efi-e1000e.rom efi-vmxnet3.rom \ -qemu-nsis.bmp \ -bamboo.dtb canyonlands.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \ -multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin pvh.bin \ -s390-ccw.img s390-netboot.img \ -slof.bin skiboot.lid \ -palcode-clipper \ -u-boot.e500 u-boot-sam460-20100605.bin \ -qemu_vga.ndrv \ -edk2-licenses.txt \ -hppa-firmware.img \ -opensbi-riscv32-sifive_u-fw_jump.bin opensbi-riscv32-virt-fw_jump.bin \ -opensbi-riscv64-sifive_u-fw_jump.bin opensbi-riscv64-virt-fw_jump.bin - - -DESCS=50-edk2-i386-secure.json 50-edk2-x86_64-secure.json \ -60-edk2-aarch64.json 60-edk2-arm.json 60-edk2-i386.json 60-edk2-x86_64.json -else -BLOBS= -DESCS= -endif - -# Note that we manually filter-out the non-Sphinx documentation which -# is currently built into the docs/interop directory in the build tree, -# and also any sphinx-built manpages. -define install-manual = -for d in $$(cd $(MANUAL_BUILDDIR) && find $1 -type d); do $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)/$$d"; done -for f in $$(cd $(MANUAL_BUILDDIR) && find $1 -type f -a '!' '(' -name '*.[0-9]' -o -name 'qemu-*-qapi.*' -o -name 'qemu-*-ref.*' ')' ); do $(INSTALL_DATA) "$(MANUAL_BUILDDIR)/$$f" "$(DESTDIR)$(qemu_docdir)/$$f"; done -endef - -# Note that we deliberately do not install the "devel" manual: it is -# for QEMU developers, and not interesting to our users. -.PHONY: install-sphinxdocs -install-sphinxdocs: sphinxdocs - $(call install-manual,interop) - $(call install-manual,specs) - $(call install-manual,system) - $(call install-manual,tools) - $(call install-manual,user) - -install-doc: $(DOCS) install-sphinxdocs - $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)" - $(INSTALL_DATA) $(MANUAL_BUILDDIR)/index.html "$(DESTDIR)$(qemu_docdir)" - $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)/interop" - $(INSTALL_DATA) docs/interop/qemu-qmp-ref.html "$(DESTDIR)$(qemu_docdir)/interop" - $(INSTALL_DATA) docs/interop/qemu-qmp-ref.txt "$(DESTDIR)$(qemu_docdir)/interop" -ifdef CONFIG_POSIX - $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1" - $(INSTALL_DATA) $(MANUAL_BUILDDIR)/system/qemu.1 "$(DESTDIR)$(mandir)/man1" - $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7" - $(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7" - $(INSTALL_DATA) $(MANUAL_BUILDDIR)/system/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7" - $(INSTALL_DATA) $(MANUAL_BUILDDIR)/system/qemu-cpu-models.7 "$(DESTDIR)$(mandir)/man7" -ifeq ($(CONFIG_TOOLS),y) - $(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/qemu-img.1 "$(DESTDIR)$(mandir)/man1" - $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8" - $(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/qemu-nbd.8 "$(DESTDIR)$(mandir)/man8" -endif -ifdef CONFIG_TRACE_SYSTEMTAP - $(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/qemu-trace-stap.1 "$(DESTDIR)$(mandir)/man1" -endif -ifneq (,$(findstring qemu-ga,$(TOOLS))) - $(INSTALL_DATA) $(MANUAL_BUILDDIR)/interop/qemu-ga.8 "$(DESTDIR)$(mandir)/man8" - $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)/interop" - $(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)/interop" - $(INSTALL_DATA) docs/interop/qemu-ga-ref.txt "$(DESTDIR)$(qemu_docdir)/interop" - $(INSTALL_DATA) docs/interop/qemu-ga-ref.7 "$(DESTDIR)$(mandir)/man7" -endif -endif -ifdef CONFIG_VIRTFS - $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1" - $(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/virtfs-proxy-helper.1 "$(DESTDIR)$(mandir)/man1" -endif -ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy) - $(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/virtiofsd.1 "$(DESTDIR)$(mandir)/man1" -endif - -install-datadir: - $(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)" - -install-localstatedir: -ifdef CONFIG_POSIX -ifneq (,$(findstring qemu-ga,$(TOOLS))) - $(INSTALL_DIR) "$(DESTDIR)$(qemu_localstatedir)"/run -endif -endif - -ICON_SIZES=16x16 24x24 32x32 48x48 64x64 128x128 256x256 512x512 - -install-includedir: - $(INSTALL_DIR) "$(DESTDIR)$(includedir)" - -install: all $(if $(BUILD_DOCS),install-doc) \ - install-datadir install-localstatedir install-includedir \ - $(if $(INSTALL_BLOBS),$(edk2-decompressed)) \ - recurse-install -ifneq ($(TOOLS),) - $(call install-prog,$(TOOLS),$(DESTDIR)$(bindir)) -endif -ifneq ($(CONFIG_MODULES),) - $(INSTALL_DIR) "$(DESTDIR)$(qemu_moddir)" - for s in $(modules-m:.mo=$(DSOSUF)); do \ - t="$(DESTDIR)$(qemu_moddir)/$$(echo $$s | tr / -)"; \ - $(INSTALL_LIB) $$s "$$t"; \ - test -z "$(STRIP)" || $(STRIP) "$$t"; \ - done -endif -ifneq ($(HELPERS-y),) - $(call install-prog,$(HELPERS-y),$(DESTDIR)$(libexecdir)) -endif -ifneq ($(vhost-user-json-y),) - $(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/vhost-user/" - for x in $(vhost-user-json-y); do \ - $(INSTALL_DATA) $$x "$(DESTDIR)$(qemu_datadir)/vhost-user/"; \ - done -endif -ifdef CONFIG_TRACE_SYSTEMTAP - $(INSTALL_PROG) "scripts/qemu-trace-stap" $(DESTDIR)$(bindir) -endif -ifneq ($(BLOBS),) - set -e; for x in $(BLOBS); do \ - $(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \ - done -endif -ifdef INSTALL_BLOBS - set -e; for x in $(edk2-decompressed); do \ - $(INSTALL_DATA) $$x "$(DESTDIR)$(qemu_datadir)"; \ - done -endif -ifneq ($(DESCS),) - $(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/firmware" - set -e; tmpf=$$(mktemp); trap 'rm -f -- "$$tmpf"' EXIT; \ - for x in $(DESCS); do \ - sed -e 's,@DATADIR@,$(qemu_datadir),' \ - "$(SRC_PATH)/pc-bios/descriptors/$$x" > "$$tmpf"; \ - $(INSTALL_DATA) "$$tmpf" \ - "$(DESTDIR)$(qemu_datadir)/firmware/$$x"; \ - done -endif - for s in $(ICON_SIZES); do \ - mkdir -p "$(DESTDIR)$(qemu_icondir)/hicolor/$${s}/apps"; \ - $(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu_$${s}.png \ - "$(DESTDIR)$(qemu_icondir)/hicolor/$${s}/apps/qemu.png"; \ - done; \ - mkdir -p "$(DESTDIR)$(qemu_icondir)/hicolor/32x32/apps"; \ - $(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu_32x32.bmp \ - "$(DESTDIR)$(qemu_icondir)/hicolor/32x32/apps/qemu.bmp"; \ - mkdir -p "$(DESTDIR)$(qemu_icondir)/hicolor/scalable/apps"; \ - $(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu.svg \ - "$(DESTDIR)$(qemu_icondir)/hicolor/scalable/apps/qemu.svg" - mkdir -p "$(DESTDIR)$(qemu_desktopdir)" - $(INSTALL_DATA) $(SRC_PATH)/ui/qemu.desktop \ - "$(DESTDIR)$(qemu_desktopdir)/qemu.desktop" -ifdef CONFIG_GTK - $(MAKE) -C po $@ -endif -ifeq ($(CONFIG_PLUGIN),y) - $(INSTALL_DATA) $(SRC_PATH)/include/qemu/qemu-plugin.h "$(DESTDIR)$(includedir)/qemu-plugin.h" -endif - $(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps" - set -e; for x in $(KEYMAPS); do \ - $(INSTALL_DATA) $(SRC_PATH)/pc-bios/keymaps/$$x "$(DESTDIR)$(qemu_datadir)/keymaps"; \ - done - $(INSTALL_DATA) $(BUILD_DIR)/trace-events-all "$(DESTDIR)$(qemu_datadir)/trace-events-all" +find-src-path = find "$(SRC_PATH)/" -path "$(SRC_PATH)/meson" -prune -o \( -name "*.[chsS]" -o -name "*.[ch].inc" \) .PHONY: ctags ctags: - rm -f tags - find "$(SRC_PATH)" -name '*.[hc]' -exec ctags --append {} + + $(call quiet-command, \ + rm -f "$(SRC_PATH)/"tags, \ + "CTAGS", "Remove old tags") + $(call quiet-command, \ + $(find-src-path) -exec ctags \ + -f "$(SRC_PATH)/"tags --append {} +, \ + "CTAGS", "Re-index $(SRC_PATH)") + +.PHONY: gtags +gtags: + $(call quiet-command, \ + rm -f "$(SRC_PATH)/"GTAGS; \ + rm -f "$(SRC_PATH)/"GRTAGS; \ + rm -f "$(SRC_PATH)/"GPATH, \ + "GTAGS", "Remove old $@ files") + $(call quiet-command, \ + (cd $(SRC_PATH) && \ + $(find-src-path) | gtags -f -), \ + "GTAGS", "Re-index $(SRC_PATH)") .PHONY: TAGS TAGS: - rm -f TAGS - find "$(SRC_PATH)" -name '*.[hc]' -exec etags --append {} + + $(call quiet-command, \ + rm -f "$(SRC_PATH)/"TAGS, \ + "TAGS", "Remove old $@") + $(call quiet-command, \ + $(find-src-path) -exec etags \ + -f "$(SRC_PATH)/"TAGS --append {} +, \ + "TAGS", "Re-index $(SRC_PATH)") +.PHONY: cscope cscope: - rm -f "$(SRC_PATH)"/cscope.* - find "$(SRC_PATH)/" -name "*.[chsS]" -print | sed 's,^\./,,' > "$(SRC_PATH)/cscope.files" - cscope -b -i"$(SRC_PATH)/cscope.files" + $(call quiet-command, \ + rm -f "$(SRC_PATH)/"cscope.* , \ + "cscope", "Remove old $@ files") + $(call quiet-command, \ + ($(find-src-path) -print | sed -e 's,^\./,,' \ + > "$(SRC_PATH)/cscope.files"), \ + "cscope", "Create file list") + $(call quiet-command, \ + cscope -b -i"$(SRC_PATH)/cscope.files" \ + -f"$(SRC_PATH)"/cscope.out, \ + "cscope", "Re-index $(SRC_PATH)") -# opengl shader programs -ui/shader/%-vert.h: $(SRC_PATH)/ui/shader/%.vert $(SRC_PATH)/scripts/shaderinclude.pl - @mkdir -p $(dir $@) - $(call quiet-command,\ - perl $(SRC_PATH)/scripts/shaderinclude.pl $< > $@,\ - "VERT","$@") - -ui/shader/%-frag.h: $(SRC_PATH)/ui/shader/%.frag $(SRC_PATH)/scripts/shaderinclude.pl - @mkdir -p $(dir $@) - $(call quiet-command,\ - perl $(SRC_PATH)/scripts/shaderinclude.pl $< > $@,\ - "FRAG","$@") - -ui/shader.o: $(SRC_PATH)/ui/shader.c \ - ui/shader/texture-blit-vert.h \ - ui/shader/texture-blit-flip-vert.h \ - ui/shader/texture-blit-frag.h - -# documentation -MAKEINFO=makeinfo -MAKEINFOINCLUDES= -I docs -I $( $@,"GEN","$@") - -%.html: %.texi docs/version.texi - $(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \ - --html $< -o $@,"GEN","$@") - -%.info: %.texi docs/version.texi - $(call quiet-command,$(MAKEINFO) $(MAKEINFOFLAGS) $< -o $@,"GEN","$@") - -%.txt: %.texi docs/version.texi - $(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \ - --plaintext $< -o $@,"GEN","$@") - -%.pdf: %.texi docs/version.texi - $(call quiet-command,texi2pdf $(TEXI2PDFFLAGS) $< -o $@,"GEN","$@") - -# Sphinx builds all its documentation at once in one invocation -# and handles "don't rebuild things unless necessary" itself. -# The '.doctrees' files are cached information to speed this up. -.PHONY: sphinxdocs -sphinxdocs: $(MANUAL_BUILDDIR)/devel/index.html \ - $(MANUAL_BUILDDIR)/interop/index.html \ - $(MANUAL_BUILDDIR)/specs/index.html \ - $(MANUAL_BUILDDIR)/system/index.html \ - $(MANUAL_BUILDDIR)/tools/index.html \ - $(MANUAL_BUILDDIR)/user/index.html - -# Canned command to build a single manual -# Arguments: $1 = manual name, $2 = Sphinx builder ('html' or 'man') -# Note the use of different doctree for each (manual, builder) tuple; -# this works around Sphinx not handling parallel invocation on -# a single doctree: https://github.com/sphinx-doc/sphinx/issues/2946 -build-manual = $(call quiet-command,CONFDIR="$(qemu_confdir)" $(SPHINX_BUILD) $(if $(V),,-q) $(SPHINX_WERROR) -b $2 -D version=$(VERSION) -D release="$(FULL_VERSION)" -d .doctrees/$1-$2 $(SRC_PATH)/docs/$1 $(MANUAL_BUILDDIR)/$1 ,"SPHINX","$(MANUAL_BUILDDIR)/$1") -# We assume all RST files in the manual's directory are used in it -manual-deps = $(wildcard $(SRC_PATH)/docs/$1/*.rst $(SRC_PATH)/docs/$1/*/*.rst) \ - $(SRC_PATH)/docs/defs.rst.inc \ - $(SRC_PATH)/docs/$1/conf.py $(SRC_PATH)/docs/conf.py \ - $(SRC_PATH)/docs/sphinx/*.py -# Macro to write out the rule and dependencies for building manpages -# Usage: $(call define-manpage-rule,manualname,manpage1 manpage2...[,extradeps]) -# 'extradeps' is optional, and specifies extra files (eg .hx files) that -# the manual page depends on. -define define-manpage-rule -$(call atomic,$(foreach manpage,$2,$(MANUAL_BUILDDIR)/$1/$(manpage)),$(call manual-deps,$1) $3) - $(call build-manual,$1,man) -endef - -$(MANUAL_BUILDDIR)/devel/index.html: $(call manual-deps,devel) - $(call build-manual,devel,html) - -$(MANUAL_BUILDDIR)/interop/index.html: $(call manual-deps,interop) - $(call build-manual,interop,html) - -$(MANUAL_BUILDDIR)/specs/index.html: $(call manual-deps,specs) - $(call build-manual,specs,html) - -$(MANUAL_BUILDDIR)/system/index.html: $(call manual-deps,system) $(SRC_PATH)/hmp-commands.hx $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/qemu-options.hx - $(call build-manual,system,html) - -$(MANUAL_BUILDDIR)/tools/index.html: $(call manual-deps,tools) $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/docs/qemu-option-trace.rst.inc - $(call build-manual,tools,html) - -$(MANUAL_BUILDDIR)/user/index.html: $(call manual-deps,user) - $(call build-manual,user,html) - -$(call define-manpage-rule,interop,qemu-ga.8) - -$(call define-manpage-rule,system,qemu.1 qemu-block-drivers.7 qemu-cpu-models.7) - -$(call define-manpage-rule,tools,\ - qemu-img.1 qemu-nbd.8 qemu-trace-stap.1\ - virtiofsd.1 virtfs-proxy-helper.1,\ - $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/docs/qemu-option-trace.rst.inc) - -$(MANUAL_BUILDDIR)/index.html: $(SRC_PATH)/docs/index.html.in qemu-version.h - @mkdir -p "$(MANUAL_BUILDDIR)" - $(call quiet-command, sed "s|@@VERSION@@|${VERSION}|g" $< >$@, \ - "GEN","$@") - -docs/interop/qemu-qmp-qapi.texi: qapi/qapi-doc.texi - @cp -p $< $@ - -docs/interop/qemu-ga-qapi.texi: qga/qapi-generated/qga-qapi-doc.texi - @cp -p $< $@ - -html: docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html sphinxdocs -info: docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info -pdf: docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf -txt: docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt - -docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \ - docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \ - docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7: \ - docs/interop/qemu-ga-ref.texi docs/interop/qemu-ga-qapi.texi - -docs/interop/qemu-qmp-ref.dvi docs/interop/qemu-qmp-ref.html \ - docs/interop/qemu-qmp-ref.info docs/interop/qemu-qmp-ref.pdf \ - docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \ - docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi - -$(filter %.1 %.7 %.8,$(DOCS)): scripts/texi2pod.pl - -# Reports/Analysis - -%/coverage-report.html: - @mkdir -p $* - $(call quiet-command,\ - gcovr -r $(SRC_PATH) \ - $(foreach t, $(TARGET_DIRS), --object-directory $(BUILD_DIR)/$(t)) \ - --object-directory $(BUILD_DIR) \ - -p --html --html-details -o $@, \ - "GEN", "coverage-report.html") - -.PHONY: coverage-report -coverage-report: $(CURDIR)/reports/coverage/coverage-report.html - -ifdef CONFIG_WIN32 - -INSTALLER = qemu-setup-$(VERSION)$(EXESUF) - -nsisflags = -V2 -NOCD - -ifneq ($(wildcard $(SRC_PATH)/dll),) -ifeq ($(ARCH),x86_64) -# 64 bit executables -DLL_PATH = $(SRC_PATH)/dll/w64 -nsisflags += -DW64 -else -# 32 bit executables -DLL_PATH = $(SRC_PATH)/dll/w32 -endif -endif - -.PHONY: installer -installer: $(INSTALLER) - -INSTDIR=/tmp/qemu-nsis - -$(INSTALLER): install-doc $(SRC_PATH)/qemu.nsi - $(MAKE) install prefix=${INSTDIR} -ifdef SIGNCODE - (cd ${INSTDIR}; \ - for i in *.exe; do \ - $(SIGNCODE) $${i}; \ - done \ - ) -endif # SIGNCODE - (cd ${INSTDIR}; \ - for i in qemu-system-*.exe; do \ - arch=$${i%.exe}; \ - arch=$${arch#qemu-system-}; \ - echo Section \"$$arch\" Section_$$arch; \ - echo SetOutPath \"\$$INSTDIR\"; \ - echo File \"\$${BINDIR}\\$$i\"; \ - echo SectionEnd; \ - done \ - ) >${INSTDIR}/system-emulations.nsh - makensis $(nsisflags) \ - $(if $(BUILD_DOCS),-DCONFIG_DOCUMENTATION="y") \ - $(if $(CONFIG_GTK),-DCONFIG_GTK="y") \ - -DBINDIR="${INSTDIR}" \ - $(if $(DLL_PATH),-DDLLDIR="$(DLL_PATH)") \ - -DSRCDIR="$(SRC_PATH)" \ - -DOUTFILE="$(INSTALLER)" \ - -DDISPLAYVERSION="$(VERSION)" \ - $(SRC_PATH)/qemu.nsi - rm -r ${INSTDIR} -ifdef SIGNCODE - $(SIGNCODE) $(INSTALLER) -endif # SIGNCODE -endif # CONFIG_WIN - -# Add a dependency on the generated files, so that they are always -# rebuilt before other object files -ifneq ($(wildcard config-host.mak),) -ifneq ($(filter-out $(UNCHECKED_GOALS),$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail)) -Makefile: $(generated-files-y) -endif -endif - -.SECONDARY: $(TRACE_HEADERS) $(TRACE_HEADERS:%=%-timestamp) \ - $(TRACE_SOURCES) $(TRACE_SOURCES:%=%-timestamp) \ - $(TRACE_DTRACE) $(TRACE_DTRACE:%=%-timestamp) - -# Include automatically generated dependency files -# Dependencies in Makefile.objs files come from our recursive subdir rules --include $(wildcard *.d tests/*.d) +# Needed by "meson install" +export DESTDIR include $(SRC_PATH)/tests/docker/Makefile.include include $(SRC_PATH)/tests/vm/Makefile.include print-help-run = printf " %-30s - %s\\n" "$1" "$2" -print-help = $(quiet-@)$(call print-help-run,$1,$2) +print-help = @$(call print-help-run,$1,$2) .PHONY: help help: @echo 'Generic targets:' $(call print-help,all,Build all) -ifdef CONFIG_MODULES - $(call print-help,modules,Build all modules) -endif $(call print-help,dir/file.o,Build specified target only) $(call print-help,install,Install QEMU, documentation and tools) - $(call print-help,ctags/TAGS,Generate tags file for editors) + $(call print-help,ctags/gtags/TAGS,Generate tags file for editors) $(call print-help,cscope,Generate cscope index) + $(call print-help,sparse,Run sparse on the QEMU source) @echo '' - @$(if $(TARGET_DIRS), \ - echo 'Architecture specific targets:'; \ - $(foreach t, $(TARGET_DIRS), \ - $(call print-help-run,$(t)/all,Build for $(t)); \ - $(if $(CONFIG_FUZZ), \ - $(if $(findstring softmmu,$(t)), \ - $(call print-help-run,$(t)/fuzz,Build fuzzer for $(t)); \ - ))) \ - echo '') - @$(if $(HELPERS-y), \ - echo 'Helper targets:'; \ - $(foreach t, $(HELPERS-y), \ - $(call print-help-run,$(t),Build $(shell basename $(t)));) \ - echo '') - @$(if $(TOOLS), \ - echo 'Tools targets:'; \ - $(foreach t, $(TOOLS), \ - $(call print-help-run,$(t),Build $(shell basename $(t)) tool);) \ - echo '') +ifeq ($(CONFIG_PLUGIN),y) + @echo 'Plugin targets:' + $(call print-help,plugins,Build the example TCG plugins) + @echo '' +endif @echo 'Cleaning targets:' $(call print-help,clean,Remove most generated files but keep the config) -ifdef CONFIG_GCOV - $(call print-help,clean-coverage,Remove coverage files) -endif $(call print-help,distclean,Remove all generated files) $(call print-help,dist,Build a distributable tarball) @echo '' @echo 'Test targets:' $(call print-help,check,Run all tests (check-help for details)) - $(call print-help,docker,Help about targets running tests inside containers) + $(call print-help,bench,Run all benchmarks) + $(call print-help,docker-help,Help about targets running tests inside containers) $(call print-help,vm-help,Help about targets running tests inside VM) @echo '' @echo 'Documentation targets:' - $(call print-help,html info pdf txt,Build documentation in specified format) -ifdef CONFIG_GCOV - $(call print-help,coverage-report,Create code coverage report) -endif + $(call print-help,html man,Build documentation in specified format) @echo '' ifdef CONFIG_WIN32 @echo 'Windows targets:' $(call print-help,installer,Build NSIS-based installer for QEMU) -ifdef QEMU_GA_MSI_ENABLED $(call print-help,msi,Build MSI-based installer for qemu-ga) -endif @echo '' endif $(call print-help,$(MAKE) [targets],(quiet build, default)) $(call print-help,$(MAKE) V=1 [targets],(verbose build)) + +# will delete the target of a rule if commands exit with a nonzero exit status +.DELETE_ON_ERROR: + +print-%: + @echo '$*=$($*)' diff --git a/Makefile.objs b/Makefile.objs deleted file mode 100644 index 0db62db4da..0000000000 --- a/Makefile.objs +++ /dev/null @@ -1,217 +0,0 @@ -####################################################################### -# Common libraries for tools and emulators -stub-obj-y = stubs/ -util-obj-y = crypto/ util/ qobject/ qapi/ -qom-obj-y = qom/ - -####################################################################### -# code used by both qemu system emulation and qemu-img - -ifeq ($(call lor,$(CONFIG_SOFTMMU),$(CONFIG_TOOLS)),y) - -chardev-obj-y = chardev/ - -authz-obj-y = authz/ - -block-obj-y = block/ nbd/ scsi/ -block-obj-y += block.o blockjob.o job.o -block-obj-y += qemu-io-cmds.o -block-obj-$(CONFIG_REPLICATION) += replication.o - -block-obj-m = block/ - -crypto-obj-y = crypto/ - -io-obj-y = io/ - -endif # CONFIG_SOFTMMU or CONFIG_TOOLS - -####################################################################### -# storage-daemon-obj-y is code used by qemu-storage-daemon (these objects are -# used for system emulation, too, but specified separately there) - -storage-daemon-obj-y = block/ monitor/ qapi/ qom/ storage-daemon/ -storage-daemon-obj-y += blockdev.o blockdev-nbd.o iothread.o job-qmp.o -storage-daemon-obj-$(CONFIG_WIN32) += os-win32.o -storage-daemon-obj-$(CONFIG_POSIX) += os-posix.o - -###################################################################### -# Target independent part of system emulation. The long term path is to -# suppress *all* target specific code in case of system emulation, i.e. a -# single QEMU executable should support all CPUs and machines. - -ifeq ($(CONFIG_SOFTMMU),y) -common-obj-y = blockdev.o blockdev-nbd.o block/ -common-obj-y += bootdevice.o iothread.o -common-obj-y += dump/ -common-obj-y += job-qmp.o -common-obj-y += monitor/ -common-obj-y += net/ -common-obj-y += qdev-monitor.o -common-obj-$(CONFIG_WIN32) += os-win32.o -common-obj-$(CONFIG_POSIX) += os-posix.o - -common-obj-$(CONFIG_LINUX) += fsdev/ - -common-obj-y += accel/ -common-obj-y += migration/ - -common-obj-y += audio/ -common-obj-m += audio/ -common-obj-y += hw/ -common-obj-m += hw/ - -common-obj-y += replay/ - -common-obj-y += ui/ -common-obj-m += ui/ - -common-obj-y += dma-helpers.o -common-obj-$(CONFIG_TPM) += tpm.o - -common-obj-y += backends/ -common-obj-y += chardev/ -common-obj-m += chardev/ - -common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o -qemu-seccomp.o-cflags := $(SECCOMP_CFLAGS) -qemu-seccomp.o-libs := $(SECCOMP_LIBS) - -common-obj-$(CONFIG_FDT) += device_tree.o - -common-obj-y += qapi/ - -common-obj-y += xemu-version.o - -endif # CONFIG_SOFTMMU - -####################################################################### -# Target-independent parts used in system and user emulation -common-obj-y += cpus-common.o -common-obj-y += hw/ -common-obj-y += qom/ -common-obj-y += disas/ - -###################################################################### -# Resource file for Windows executables -version-obj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.o - -###################################################################### -# tracing -util-obj-y += trace/ - -###################################################################### -# guest agent - -# FIXME: a few definitions from qapi/qapi-types.o and -# qapi/qapi-visit.o are needed by libqemuutil.a. These should be -# extracted into a QAPI schema module, or perhaps a separate schema. -qga-obj-y = qga/ -qga-vss-dll-obj-y = qga/ - -###################################################################### -# contrib -elf2dmp-obj-y = contrib/elf2dmp/ -ivshmem-client-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-client/ -ivshmem-server-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-server/ -libvhost-user-obj-y = contrib/libvhost-user/ -vhost-user-scsi.o-cflags := $(LIBISCSI_CFLAGS) -vhost-user-scsi.o-libs := $(LIBISCSI_LIBS) -vhost-user-scsi-obj-y = contrib/vhost-user-scsi/ -vhost-user-blk-obj-y = contrib/vhost-user-blk/ -rdmacm-mux-obj-y = contrib/rdmacm-mux/ -vhost-user-input-obj-y = contrib/vhost-user-input/ -vhost-user-gpu-obj-y = contrib/vhost-user-gpu/ -virtiofsd-obj-y = tools/virtiofsd/ - -###################################################################### -trace-events-subdirs = -trace-events-subdirs += accel/kvm -trace-events-subdirs += accel/tcg -trace-events-subdirs += backends -trace-events-subdirs += backends/tpm -trace-events-subdirs += crypto -trace-events-subdirs += monitor -ifeq ($(CONFIG_USER_ONLY),y) -trace-events-subdirs += linux-user -endif -ifeq ($(CONFIG_BLOCK),y) -trace-events-subdirs += authz -trace-events-subdirs += block -trace-events-subdirs += io -trace-events-subdirs += nbd -trace-events-subdirs += scsi -endif -ifeq ($(CONFIG_SOFTMMU),y) -trace-events-subdirs += audio -trace-events-subdirs += chardev -trace-events-subdirs += hw/9pfs -trace-events-subdirs += hw/acpi -trace-events-subdirs += hw/alpha -trace-events-subdirs += hw/arm -trace-events-subdirs += hw/audio -trace-events-subdirs += hw/block -trace-events-subdirs += hw/block/dataplane -trace-events-subdirs += hw/char -trace-events-subdirs += hw/dma -trace-events-subdirs += hw/hppa -trace-events-subdirs += hw/hyperv -trace-events-subdirs += hw/i2c -trace-events-subdirs += hw/i386 -trace-events-subdirs += hw/i386/xen -trace-events-subdirs += hw/ide -trace-events-subdirs += hw/input -trace-events-subdirs += hw/intc -trace-events-subdirs += hw/isa -trace-events-subdirs += hw/mem -trace-events-subdirs += hw/mips -trace-events-subdirs += hw/misc -trace-events-subdirs += hw/misc/macio -trace-events-subdirs += hw/net -trace-events-subdirs += hw/nvram -trace-events-subdirs += hw/pci -trace-events-subdirs += hw/pci-host -trace-events-subdirs += hw/ppc -trace-events-subdirs += hw/rdma -trace-events-subdirs += hw/rdma/vmw -trace-events-subdirs += hw/rtc -trace-events-subdirs += hw/s390x -trace-events-subdirs += hw/scsi -trace-events-subdirs += hw/sd -trace-events-subdirs += hw/sparc -trace-events-subdirs += hw/sparc64 -trace-events-subdirs += hw/ssi -trace-events-subdirs += hw/timer -trace-events-subdirs += hw/tpm -trace-events-subdirs += hw/usb -trace-events-subdirs += hw/vfio -trace-events-subdirs += hw/virtio -trace-events-subdirs += hw/watchdog -trace-events-subdirs += hw/xen -trace-events-subdirs += hw/gpio -trace-events-subdirs += hw/riscv -trace-events-subdirs += migration -trace-events-subdirs += net -trace-events-subdirs += ui -endif -trace-events-subdirs += hw/core -trace-events-subdirs += hw/display -trace-events-subdirs += qapi -trace-events-subdirs += qom -trace-events-subdirs += target/arm -trace-events-subdirs += target/hppa -trace-events-subdirs += target/i386 -trace-events-subdirs += target/mips -trace-events-subdirs += target/ppc -trace-events-subdirs += target/riscv -trace-events-subdirs += target/s390x -trace-events-subdirs += target/sparc -trace-events-subdirs += util - -trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events) - -trace-obj-y = trace-root.o -trace-obj-y += $(trace-events-subdirs:%=%/trace.o) -trace-obj-$(CONFIG_TRACE_UST) += trace-ust-all.o -trace-obj-$(CONFIG_TRACE_DTRACE) += trace-dtrace-root.o -trace-obj-$(CONFIG_TRACE_DTRACE) += $(trace-events-subdirs:%=%/trace-dtrace.o) diff --git a/Makefile.target b/Makefile.target deleted file mode 100644 index 63af7f760e..0000000000 --- a/Makefile.target +++ /dev/null @@ -1,291 +0,0 @@ -# -*- Mode: makefile -*- - -BUILD_DIR?=$(CURDIR)/.. - -include ../config-host.mak -include config-target.mak -include $(SRC_PATH)/rules.mak - -ifdef CONFIG_SOFTMMU -include config-devices.mak -endif - -$(call set-vpath, $(SRC_PATH):$(BUILD_DIR)) -ifdef CONFIG_LINUX -QEMU_CFLAGS += -isystem ../linux-headers -endif -QEMU_CFLAGS += -iquote .. -iquote $(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H - -QEMU_CFLAGS+=-iquote $(SRC_PATH)/include - -ifdef CONFIG_USER_ONLY -# user emulator name -QEMU_PROG=qemu-$(TARGET_NAME) -QEMU_PROG_BUILD = $(QEMU_PROG) -else -# system emulator name -QEMU_PROG=qemu-system-$(TARGET_NAME)$(EXESUF) -ifneq (,$(findstring -mwindows,$(SDL_LIBS))) -# Terminate program name with a 'w' because the linker builds a windows executable. -QEMU_PROGW=qemu-system-$(TARGET_NAME)w$(EXESUF) -$(QEMU_PROG): $(QEMU_PROGW) - $(call quiet-command,$(OBJCOPY) --subsystem console $(QEMU_PROGW) $(QEMU_PROG),"GEN","$(TARGET_DIR)$(QEMU_PROG)") -QEMU_PROG_BUILD = $(QEMU_PROGW) -else -QEMU_PROG_BUILD = $(QEMU_PROG) -endif -endif - -PROGS=$(QEMU_PROG) $(QEMU_PROGW) -STPFILES= - -config-target.h: config-target.h-timestamp -config-target.h-timestamp: config-target.mak - -config-devices.h: config-devices.h-timestamp -config-devices.h-timestamp: config-devices.mak - -ifdef CONFIG_TRACE_SYSTEMTAP -stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp $(QEMU_PROG)-log.stp - -ifdef CONFIG_USER_ONLY -TARGET_TYPE=user -else -TARGET_TYPE=system -endif - -tracetool-y = $(SRC_PATH)/scripts/tracetool.py -tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") - -$(QEMU_PROG).stp-installed: $(BUILD_DIR)/trace-events-all $(tracetool-y) - $(call quiet-command,$(TRACETOOL) \ - --group=all \ - --format=stap \ - --backends=$(TRACE_BACKENDS) \ - --binary=$(bindir)/$(QEMU_PROG) \ - --target-name=$(TARGET_NAME) \ - --target-type=$(TARGET_TYPE) \ - $< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG).stp-installed") - -$(QEMU_PROG).stp: $(BUILD_DIR)/trace-events-all $(tracetool-y) - $(call quiet-command,$(TRACETOOL) \ - --group=all \ - --format=stap \ - --backends=$(TRACE_BACKENDS) \ - --binary=$(realpath .)/$(QEMU_PROG) \ - --target-name=$(TARGET_NAME) \ - --target-type=$(TARGET_TYPE) \ - $< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG).stp") - -$(QEMU_PROG)-simpletrace.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y) - $(call quiet-command,$(TRACETOOL) \ - --group=all \ - --format=simpletrace-stap \ - --backends=$(TRACE_BACKENDS) \ - --probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \ - $< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-simpletrace.stp") - -$(QEMU_PROG)-log.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y) - $(call quiet-command,$(TRACETOOL) \ - --group=all \ - --format=log-stap \ - --backends=$(TRACE_BACKENDS) \ - --probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \ - $< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-log.stp") - -else -stap: -endif -.PHONY: stap - -all: $(PROGS) stap - -# Dummy command so that make thinks it has done something - @true - -obj-y += trace/ - -######################################################### -# xemu -obj-y += xemu-xbe.o - -######################################################### -# cpu emulator library -obj-y += exec.o exec-vary.o -obj-y += accel/ -obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/tcg-op-vec.o tcg/tcg-op-gvec.o -obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o -obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o -obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o -obj-$(CONFIG_TCG) += fpu/softfloat.o -obj-y += target/$(TARGET_BASE_ARCH)/ -obj-y += disas.o -obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o -LIBS := $(libs_cpu) $(LIBS) - -obj-$(CONFIG_PLUGIN) += plugins/ - -######################################################### -# Linux user emulator target - -ifdef CONFIG_LINUX_USER - -QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) \ - -I$(SRC_PATH)/linux-user/host/$(ARCH) \ - -I$(SRC_PATH)/linux-user \ - -Ilinux-user/$(TARGET_ABI_DIR) - -obj-y += linux-user/ -obj-y += gdbstub.o thunk.o - -endif #CONFIG_LINUX_USER - -######################################################### -# BSD user emulator target - -ifdef CONFIG_BSD_USER - -QEMU_CFLAGS+=-I$(SRC_PATH)/bsd-user -I$(SRC_PATH)/bsd-user/$(TARGET_ABI_DIR) \ - -I$(SRC_PATH)/bsd-user/$(HOST_VARIANT_DIR) - -obj-y += bsd-user/ -obj-y += gdbstub.o - -endif #CONFIG_BSD_USER - -######################################################### -# System emulator target -ifdef CONFIG_SOFTMMU -obj-y += softmmu/ -obj-y += gdbstub.o -obj-y += dump/ -obj-y += hw/ -obj-y += monitor/ -obj-y += qapi/ -obj-y += migration/ram.o -LIBS := $(libs_softmmu) $(LIBS) - -# Hardware support -ifeq ($(TARGET_NAME), sparc64) -obj-y += hw/sparc64/ -else -obj-y += hw/$(TARGET_BASE_ARCH)/ -endif - -generated-files-y += hmp-commands.h hmp-commands-info.h -generated-files-y += config-devices.h - -endif # CONFIG_SOFTMMU - -dummy := $(call unnest-vars,,obj-y) -all-obj-y := $(obj-y) - -# -# common-obj-m has some crap here, probably as side effect from -# unnest-vars recursing into target directories to fill obj-y and not -# properly handling the -m case. -# -# Clear common-obj-m as workaround. Fixes suspious dependency errors -# when building devices as modules. A bit hackish, but should be ok -# as long as we do not have any target-specific modules. -# -# The meson-based build system currently in development doesn't need -# unnest-vars and will obsolete this workaround. -# -common-obj-m := - -include $(SRC_PATH)/Makefile.objs -dummy := $(call unnest-vars,.., \ - authz-obj-y \ - block-obj-y \ - block-obj-m \ - chardev-obj-y \ - crypto-obj-y \ - qom-obj-y \ - io-obj-y \ - common-obj-y \ - common-obj-m) -all-obj-y += $(common-obj-y) -all-obj-y += $(qom-obj-y) -all-obj-$(CONFIG_SOFTMMU) += $(authz-obj-y) -all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y) -all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y) -all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y) - -ifdef CONFIG_SOFTMMU -$(QEMU_PROG_BUILD): config-devices.mak -endif - -COMMON_LDADDS = ../libqemuutil.a - -# build either PROG or PROGW -$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS) $(softmmu-main-y) - $(call LINK, $(filter-out %.mak, $^)) -ifdef CONFIG_DARWIN - $(call quiet-command,Rez -append $(SRC_PATH)/pc-bios/qemu.rsrc -o $@,"REZ","$(TARGET_DIR)$@") - $(call quiet-command,SetFile -a C $@,"SETFILE","$(TARGET_DIR)$@") -endif - -gdbstub-xml.c: $(TARGET_XML_FILES) $(SRC_PATH)/scripts/feature_to_c.sh - $(call quiet-command,rm -f $@ && $(SHELL) $(SRC_PATH)/scripts/feature_to_c.sh $@ $(TARGET_XML_FILES),"GEN","$(TARGET_DIR)$@") - -hmp-commands.h: $(SRC_PATH)/hmp-commands.hx $(SRC_PATH)/scripts/hxtool - $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$(TARGET_DIR)$@") - -hmp-commands-info.h: $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/scripts/hxtool - $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$(TARGET_DIR)$@") - -clean: clean-target - rm -f *.a *~ $(PROGS) - rm -f $(shell find . -name '*.[od]') - rm -f hmp-commands.h gdbstub-xml.c - rm -f trace/generated-helpers.c trace/generated-helpers.c-timestamp -ifdef CONFIG_TRACE_SYSTEMTAP - rm -f *.stp -endif - -ifdef CONFIG_FUZZ -include $(SRC_PATH)/tests/qtest/fuzz/Makefile.include -include $(SRC_PATH)/tests/qtest/Makefile.include - -fuzz: fuzz-vars -fuzz-vars: QEMU_CFLAGS := $(FUZZ_CFLAGS) $(QEMU_CFLAGS) -fuzz-vars: QEMU_LDFLAGS := $(FUZZ_LDFLAGS) $(QEMU_LDFLAGS) -fuzz-vars: $(QEMU_PROG_FUZZ) -dummy := $(call unnest-vars,, fuzz-obj-y) - - -$(QEMU_PROG_FUZZ): config-devices.mak $(all-obj-y) $(COMMON_LDADDS) $(fuzz-obj-y) - $(call LINK, $(filter-out %.mak, $^)) - -endif - -install: all -ifneq ($(PROGS),) - $(call install-prog,$(PROGS),$(DESTDIR)$(bindir)) -endif -ifdef CONFIG_TRACE_SYSTEMTAP - $(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset" - $(INSTALL_DATA) $(QEMU_PROG).stp-installed "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG).stp" - $(INSTALL_DATA) $(QEMU_PROG)-simpletrace.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-simpletrace.stp" - $(INSTALL_DATA) $(QEMU_PROG)-log.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-log.stp" -endif - -generated-files-y += config-target.h -Makefile: $(generated-files-y) - -# Reports/Analysis -# -# The target specific coverage report only cares about target specific -# blobs and not the shared code. -# - -%/coverage-report.html: - @mkdir -p $* - $(call quiet-command,\ - gcovr -r $(SRC_PATH) --object-directory $(CURDIR) \ - -p --html --html-details -o $@, \ - "GEN", "coverage-report.html") - -.PHONY: coverage-report -coverage-report: $(CURDIR)/reports/coverage/coverage-report.html diff --git a/README.rst b/README.rst index 7497709291..a92c7394b7 100644 --- a/README.rst +++ b/README.rst @@ -31,6 +31,17 @@ QEMU as a whole is released under the GNU General Public License, version 2. For full licensing details, consult the LICENSE file. +Documentation +============= + +Documentation can be found hosted online at +``_. The documentation for the +current development version that is available at +``_ is generated from the ``docs/`` +folder in the source tree, and is built by `Sphinx +_`. + + Building ======== @@ -60,13 +71,15 @@ The QEMU source code is maintained under the GIT version control system. .. code-block:: shell - git clone https://git.qemu.org/git/qemu.git + git clone https://gitlab.com/qemu-project/qemu.git When submitting patches, one common approach is to use 'git format-patch' and/or 'git send-email' to format & send the mail to the qemu-devel@nongnu.org mailing list. All patches submitted must contain a 'Signed-off-by' line from the author. Patches should follow the -guidelines set out in the CODING_STYLE.rst file. +guidelines set out in the `style section +` of +the Developers Guide. Additional information on submitting patches can be found online via the QEMU website @@ -78,7 +91,7 @@ The QEMU website is also maintained under source control. .. code-block:: shell - git clone https://git.qemu.org/git/qemu-web.git + git clone https://gitlab.com/qemu-project/qemu-web.git * ``_ @@ -134,6 +147,14 @@ For additional information on bug reporting consult: * ``_ +ChangeLog +========= + +For version history and release notes, please visit +``_ or look at the git history for +more detailed information. + + Contact ======= diff --git a/VERSION b/VERSION index 831446cbd2..161f136ce4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.1.0 +5.2.93 diff --git a/accel/Kconfig b/accel/Kconfig index 2ad94a3839..461104c771 100644 --- a/accel/Kconfig +++ b/accel/Kconfig @@ -1,3 +1,12 @@ +config WHPX + bool + +config HAX + bool + +config HVF + bool + config TCG bool diff --git a/accel/Makefile.objs b/accel/Makefile.objs deleted file mode 100644 index ff72f0d030..0000000000 --- a/accel/Makefile.objs +++ /dev/null @@ -1,6 +0,0 @@ -common-obj-$(CONFIG_SOFTMMU) += accel.o -obj-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_POSIX)) += qtest.o -obj-$(CONFIG_KVM) += kvm/ -obj-$(CONFIG_TCG) += tcg/ -obj-$(CONFIG_XEN) += xen/ -obj-y += stubs/ diff --git a/accel/accel-common.c b/accel/accel-common.c new file mode 100644 index 0000000000..9901b0531c --- /dev/null +++ b/accel/accel-common.c @@ -0,0 +1,105 @@ +/* + * QEMU accel class, components common to system emulation and user mode + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu/accel.h" + +#include "cpu.h" +#include "hw/core/accel-cpu.h" + +#ifndef CONFIG_USER_ONLY +#include "accel-softmmu.h" +#endif /* !CONFIG_USER_ONLY */ + +static const TypeInfo accel_type = { + .name = TYPE_ACCEL, + .parent = TYPE_OBJECT, + .class_size = sizeof(AccelClass), + .instance_size = sizeof(AccelState), +}; + +/* Lookup AccelClass from opt_name. Returns NULL if not found */ +AccelClass *accel_find(const char *opt_name) +{ + char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name); + AccelClass *ac = ACCEL_CLASS(object_class_by_name(class_name)); + g_free(class_name); + return ac; +} + +static void accel_init_cpu_int_aux(ObjectClass *klass, void *opaque) +{ + CPUClass *cc = CPU_CLASS(klass); + AccelCPUClass *accel_cpu = opaque; + + cc->accel_cpu = accel_cpu; + if (accel_cpu->cpu_class_init) { + accel_cpu->cpu_class_init(cc); + } +} + +/* initialize the arch-specific accel CpuClass interfaces */ +static void accel_init_cpu_interfaces(AccelClass *ac) +{ + const char *ac_name; /* AccelClass name */ + char *acc_name; /* AccelCPUClass name */ + ObjectClass *acc; /* AccelCPUClass */ + + ac_name = object_class_get_name(OBJECT_CLASS(ac)); + g_assert(ac_name != NULL); + + acc_name = g_strdup_printf("%s-%s", ac_name, CPU_RESOLVING_TYPE); + acc = object_class_by_name(acc_name); + g_free(acc_name); + + if (acc) { + object_class_foreach(accel_init_cpu_int_aux, + CPU_RESOLVING_TYPE, false, acc); + } +} + +void accel_init_interfaces(AccelClass *ac) +{ +#ifndef CONFIG_USER_ONLY + accel_init_ops_interfaces(ac); +#endif /* !CONFIG_USER_ONLY */ + + accel_init_cpu_interfaces(ac); +} + +static const TypeInfo accel_cpu_type = { + .name = TYPE_ACCEL_CPU, + .parent = TYPE_OBJECT, + .abstract = true, + .class_size = sizeof(AccelCPUClass), +}; + +static void register_accel_types(void) +{ + type_register_static(&accel_type); + type_register_static(&accel_cpu_type); +} + +type_init(register_accel_types); diff --git a/accel/accel.c b/accel/accel-softmmu.c similarity index 64% rename from accel/accel.c rename to accel/accel-softmmu.c index cb555e3b06..50fa5acaa4 100644 --- a/accel/accel.c +++ b/accel/accel-softmmu.c @@ -1,5 +1,5 @@ /* - * QEMU System Emulator, accelerator interfaces + * QEMU accel class, system emulation components * * Copyright (c) 2003-2008 Fabrice Bellard * Copyright (c) 2014 Red Hat Inc. @@ -24,27 +24,11 @@ */ #include "qemu/osdep.h" -#include "sysemu/accel.h" +#include "qemu/accel.h" #include "hw/boards.h" -#include "sysemu/arch_init.h" -#include "sysemu/sysemu.h" -#include "qom/object.h" +#include "sysemu/cpus.h" -static const TypeInfo accel_type = { - .name = TYPE_ACCEL, - .parent = TYPE_OBJECT, - .class_size = sizeof(AccelClass), - .instance_size = sizeof(AccelState), -}; - -/* Lookup AccelClass from opt_name. Returns NULL if not found */ -AccelClass *accel_find(const char *opt_name) -{ - char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name); - AccelClass *ac = ACCEL_CLASS(object_class_by_name(class_name)); - g_free(class_name); - return ac; -} +#include "accel-softmmu.h" int accel_init_machine(AccelState *accel, MachineState *ms) { @@ -77,9 +61,40 @@ void accel_setup_post(MachineState *ms) } } -static void register_accel_types(void) +/* initialize the arch-independent accel operation interfaces */ +void accel_init_ops_interfaces(AccelClass *ac) { - type_register_static(&accel_type); + const char *ac_name; + char *ops_name; + AccelOpsClass *ops; + + ac_name = object_class_get_name(OBJECT_CLASS(ac)); + g_assert(ac_name != NULL); + + ops_name = g_strdup_printf("%s" ACCEL_OPS_SUFFIX, ac_name); + ops = ACCEL_OPS_CLASS(object_class_by_name(ops_name)); + g_free(ops_name); + + /* + * all accelerators need to define ops, providing at least a mandatory + * non-NULL create_vcpu_thread operation. + */ + g_assert(ops != NULL); + if (ops->ops_init) { + ops->ops_init(ops); + } + cpus_register_accel(ops); } -type_init(register_accel_types); +static const TypeInfo accel_ops_type_info = { + .name = TYPE_ACCEL_OPS, + .parent = TYPE_OBJECT, + .abstract = true, + .class_size = sizeof(AccelOpsClass), +}; + +static void accel_softmmu_register_types(void) +{ + type_register_static(&accel_ops_type_info); +} +type_init(accel_softmmu_register_types); diff --git a/accel/accel-softmmu.h b/accel/accel-softmmu.h new file mode 100644 index 0000000000..5e192f1882 --- /dev/null +++ b/accel/accel-softmmu.h @@ -0,0 +1,15 @@ +/* + * QEMU System Emulation accel internal functions + * + * Copyright 2021 SUSE LLC + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef ACCEL_SOFTMMU_H +#define ACCEL_SOFTMMU_H + +void accel_init_ops_interfaces(AccelClass *ac); + +#endif /* ACCEL_SOFTMMU_H */ diff --git a/accel/accel-user.c b/accel/accel-user.c new file mode 100644 index 0000000000..22b6a1a1a8 --- /dev/null +++ b/accel/accel-user.c @@ -0,0 +1,24 @@ +/* + * QEMU accel class, user-mode components + * + * Copyright 2021 SUSE LLC + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/accel.h" + +AccelState *current_accel(void) +{ + static AccelState *accel; + + if (!accel) { + AccelClass *ac = accel_find("tcg"); + + g_assert(ac != NULL); + accel = ACCEL(object_new_with_class(OBJECT_CLASS(ac))); + } + return accel; +} diff --git a/accel/dummy-cpus.c b/accel/dummy-cpus.c new file mode 100644 index 0000000000..10429fdfb2 --- /dev/null +++ b/accel/dummy-cpus.c @@ -0,0 +1,72 @@ +/* + * Dummy cpu thread code + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu/rcu.h" +#include "sysemu/cpus.h" +#include "qemu/guest-random.h" +#include "qemu/main-loop.h" +#include "hw/core/cpu.h" + +static void *dummy_cpu_thread_fn(void *arg) +{ + CPUState *cpu = arg; + sigset_t waitset; + int r; + + rcu_register_thread(); + + qemu_mutex_lock_iothread(); + qemu_thread_get_self(cpu->thread); + cpu->thread_id = qemu_get_thread_id(); + cpu->can_do_io = 1; + current_cpu = cpu; + + sigemptyset(&waitset); + sigaddset(&waitset, SIG_IPI); + + /* signal CPU creation */ + cpu_thread_signal_created(cpu); + qemu_guest_random_seed_thread_part2(cpu->random_seed); + + do { + qemu_mutex_unlock_iothread(); + do { + int sig; + r = sigwait(&waitset, &sig); + } while (r == -1 && (errno == EAGAIN || errno == EINTR)); + if (r == -1) { + perror("sigwait"); + exit(1); + } + qemu_mutex_lock_iothread(); + qemu_wait_io_event(cpu); + } while (!cpu->unplug); + + qemu_mutex_unlock_iothread(); + rcu_unregister_thread(); + return NULL; +} + +void dummy_start_vcpu_thread(CPUState *cpu) +{ + char thread_name[VCPU_THREAD_NAME_SIZE]; + + cpu->thread = g_malloc0(sizeof(QemuThread)); + cpu->halt_cond = g_malloc0(sizeof(QemuCond)); + qemu_cond_init(cpu->halt_cond); + snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/DUMMY", + cpu->cpu_index); + qemu_thread_create(cpu->thread, thread_name, dummy_cpu_thread_fn, cpu, + QEMU_THREAD_JOINABLE); +} diff --git a/accel/hvf/entitlements.plist b/accel/hvf/entitlements.plist new file mode 100644 index 0000000000..154f3308ef --- /dev/null +++ b/accel/hvf/entitlements.plist @@ -0,0 +1,8 @@ + + + + + com.apple.security.hypervisor + + + diff --git a/accel/kvm/Makefile.objs b/accel/kvm/Makefile.objs deleted file mode 100644 index fdfa481578..0000000000 --- a/accel/kvm/Makefile.objs +++ /dev/null @@ -1,2 +0,0 @@ -obj-y += kvm-all.o -obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c new file mode 100644 index 0000000000..7516c67a3f --- /dev/null +++ b/accel/kvm/kvm-accel-ops.c @@ -0,0 +1,100 @@ +/* + * QEMU KVM support + * + * Copyright IBM, Corp. 2008 + * Red Hat, Inc. 2008 + * + * Authors: + * Anthony Liguori + * Glauber Costa + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" +#include "sysemu/kvm_int.h" +#include "sysemu/runstate.h" +#include "sysemu/cpus.h" +#include "qemu/guest-random.h" +#include "qapi/error.h" + +#include "kvm-cpus.h" + +static void *kvm_vcpu_thread_fn(void *arg) +{ + CPUState *cpu = arg; + int r; + + rcu_register_thread(); + + qemu_mutex_lock_iothread(); + qemu_thread_get_self(cpu->thread); + cpu->thread_id = qemu_get_thread_id(); + cpu->can_do_io = 1; + current_cpu = cpu; + + r = kvm_init_vcpu(cpu, &error_fatal); + kvm_init_cpu_signals(cpu); + + /* signal CPU creation */ + cpu_thread_signal_created(cpu); + qemu_guest_random_seed_thread_part2(cpu->random_seed); + + do { + if (cpu_can_run(cpu)) { + r = kvm_cpu_exec(cpu); + if (r == EXCP_DEBUG) { + cpu_handle_guest_debug(cpu); + } + } + qemu_wait_io_event(cpu); + } while (!cpu->unplug || cpu_can_run(cpu)); + + kvm_destroy_vcpu(cpu); + cpu_thread_signal_destroyed(cpu); + qemu_mutex_unlock_iothread(); + rcu_unregister_thread(); + return NULL; +} + +static void kvm_start_vcpu_thread(CPUState *cpu) +{ + char thread_name[VCPU_THREAD_NAME_SIZE]; + + cpu->thread = g_malloc0(sizeof(QemuThread)); + cpu->halt_cond = g_malloc0(sizeof(QemuCond)); + qemu_cond_init(cpu->halt_cond); + snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/KVM", + cpu->cpu_index); + qemu_thread_create(cpu->thread, thread_name, kvm_vcpu_thread_fn, + cpu, QEMU_THREAD_JOINABLE); +} + +static void kvm_accel_ops_class_init(ObjectClass *oc, void *data) +{ + AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); + + ops->create_vcpu_thread = kvm_start_vcpu_thread; + ops->synchronize_post_reset = kvm_cpu_synchronize_post_reset; + ops->synchronize_post_init = kvm_cpu_synchronize_post_init; + ops->synchronize_state = kvm_cpu_synchronize_state; + ops->synchronize_pre_loadvm = kvm_cpu_synchronize_pre_loadvm; +} + +static const TypeInfo kvm_accel_ops_type = { + .name = ACCEL_OPS_NAME("kvm"), + + .parent = TYPE_ACCEL_OPS, + .class_init = kvm_accel_ops_class_init, + .abstract = true, +}; + +static void kvm_accel_ops_register_types(void) +{ + type_register_static(&kvm_accel_ops_type); +} +type_init(kvm_accel_ops_register_types); diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 63ef6af9a1..b6d9f92f15 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -39,11 +39,13 @@ #include "qemu/main-loop.h" #include "trace.h" #include "hw/irq.h" -#include "sysemu/sev.h" #include "qapi/visitor.h" #include "qapi/qapi-types-common.h" #include "qapi/qapi-visit-common.h" #include "sysemu/reset.h" +#include "qemu/guest-random.h" +#include "sysemu/hw_accel.h" +#include "kvm-cpus.h" #include "hw/boards.h" @@ -55,6 +57,9 @@ /* KVM uses PAGE_SIZE in its definition of KVM_COALESCED_MMIO_MAX. We * need to use the real host PAGE_SIZE, as that's what KVM will use. */ +#ifdef PAGE_SIZE +#undef PAGE_SIZE +#endif #define PAGE_SIZE qemu_real_host_page_size //#define DEBUG_KVM @@ -117,10 +122,6 @@ struct KVMState KVMMemoryListener memory_listener; QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus; - /* memory encryption */ - void *memcrypt_handle; - int (*memcrypt_encrypt_data)(void *handle, uint8_t *ptr, uint64_t len); - /* For "info mtree -f" to tell if an MR is registered in KVM */ int nr_as; struct KVMAs { @@ -219,26 +220,6 @@ int kvm_get_max_memslots(void) return s->nr_slots; } -bool kvm_memcrypt_enabled(void) -{ - if (kvm_state && kvm_state->memcrypt_handle) { - return true; - } - - return false; -} - -int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len) -{ - if (kvm_state->memcrypt_handle && - kvm_state->memcrypt_encrypt_data) { - return kvm_state->memcrypt_encrypt_data(kvm_state->memcrypt_handle, - ptr, len); - } - - return 1; -} - /* Called with KVMMemoryListener.slots_lock held */ static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml) { @@ -378,7 +359,7 @@ err: return ret; } -int kvm_destroy_vcpu(CPUState *cpu) +static int do_kvm_destroy_vcpu(CPUState *cpu) { KVMState *s = kvm_state; long mmap_size; @@ -412,6 +393,14 @@ err: return ret; } +void kvm_destroy_vcpu(CPUState *cpu) +{ + if (do_kvm_destroy_vcpu(cpu) < 0) { + error_report("kvm_destroy_vcpu failed"); + exit(EXIT_FAILURE); + } +} + static int kvm_get_vcpu(KVMState *s, unsigned long vcpu_id) { struct KVMParkedVcpu *cpu; @@ -430,17 +419,18 @@ static int kvm_get_vcpu(KVMState *s, unsigned long vcpu_id) return kvm_vm_ioctl(s, KVM_CREATE_VCPU, (void *)vcpu_id); } -int kvm_init_vcpu(CPUState *cpu) +int kvm_init_vcpu(CPUState *cpu, Error **errp) { KVMState *s = kvm_state; long mmap_size; int ret; - DPRINTF("kvm_init_vcpu\n"); + trace_kvm_init_vcpu(cpu->cpu_index, kvm_arch_vcpu_id(cpu)); ret = kvm_get_vcpu(s, kvm_arch_vcpu_id(cpu)); if (ret < 0) { - DPRINTF("kvm_create_vcpu failed\n"); + error_setg_errno(errp, -ret, "kvm_init_vcpu: kvm_get_vcpu failed (%lu)", + kvm_arch_vcpu_id(cpu)); goto err; } @@ -451,7 +441,8 @@ int kvm_init_vcpu(CPUState *cpu) mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0); if (mmap_size < 0) { ret = mmap_size; - DPRINTF("KVM_GET_VCPU_MMAP_SIZE failed\n"); + error_setg_errno(errp, -mmap_size, + "kvm_init_vcpu: KVM_GET_VCPU_MMAP_SIZE failed"); goto err; } @@ -459,7 +450,9 @@ int kvm_init_vcpu(CPUState *cpu) cpu->kvm_fd, 0); if (cpu->kvm_run == MAP_FAILED) { ret = -errno; - DPRINTF("mmap'ing vcpu state failed\n"); + error_setg_errno(errp, ret, + "kvm_init_vcpu: mmap'ing vcpu state failed (%lu)", + kvm_arch_vcpu_id(cpu)); goto err; } @@ -469,6 +462,11 @@ int kvm_init_vcpu(CPUState *cpu) } ret = kvm_arch_init_vcpu(cpu); + if (ret < 0) { + error_setg_errno(errp, -ret, + "kvm_init_vcpu: kvm_arch_init_vcpu failed (%lu)", + kvm_arch_vcpu_id(cpu)); + } err: return ret; } @@ -600,8 +598,12 @@ static void kvm_memslot_init_dirty_bitmap(KVMSlot *mem) * too, in most cases). * So for now, let's align to 64 instead of HOST_LONG_BITS here, in * a hope that sizeof(long) won't become >8 any time soon. + * + * Note: the granule of kvm dirty log is qemu_real_host_page_size. + * And mem->memory_size is aligned to it (otherwise this mem can't + * be registered to KVM). */ - hwaddr bitmap_size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS), + hwaddr bitmap_size = ALIGN(mem->memory_size / qemu_real_host_page_size, /*HOST_LONG_BITS*/ 64) / 8; mem->dirty_bmap = g_malloc0(bitmap_size); } @@ -645,16 +647,19 @@ static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml, d.dirty_bitmap = mem->dirty_bmap; d.slot = mem->slot | (kml->as_id << 16); - if (kvm_vm_ioctl(s, KVM_GET_DIRTY_LOG, &d) == -1) { - DPRINTF("ioctl failed %d\n", errno); - ret = -1; + ret = kvm_vm_ioctl(s, KVM_GET_DIRTY_LOG, &d); + if (ret == -ENOENT) { + /* kernel does not have dirty bitmap in this slot */ + ret = 0; + } else if (ret < 0) { + error_report("ioctl KVM_GET_DIRTY_LOG failed: %d", errno); goto out; + } else { + subsection.offset_within_region += slot_offset; + subsection.size = int128_make64(slot_size); + kvm_get_dirty_pages_log_range(&subsection, d.dirty_bitmap); } - subsection.offset_within_region += slot_offset; - subsection.size = int128_make64(slot_size); - kvm_get_dirty_pages_log_range(&subsection, d.dirty_bitmap); - slot_offset += slot_size; start_addr += slot_size; size -= slot_size; @@ -725,7 +730,7 @@ static int kvm_log_clear_one_slot(KVMSlot *mem, int as_id, uint64_t start, assert(bmap_start % BITS_PER_LONG == 0); /* We should never do log_clear before log_sync */ assert(mem->dirty_bmap); - if (start_delta) { + if (start_delta || bmap_npages - size / psize) { /* Slow path - we need to manipulate a temp bitmap */ bmap_clear = bitmap_new(bmap_npages); bitmap_copy_with_src_offset(bmap_clear, mem->dirty_bmap, @@ -738,7 +743,10 @@ static int kvm_log_clear_one_slot(KVMSlot *mem, int as_id, uint64_t start, bitmap_clear(bmap_clear, 0, start_delta); d.dirty_bitmap = bmap_clear; } else { - /* Fast path - start address aligns well with BITS_PER_LONG */ + /* + * Fast path - both start and size align well with BITS_PER_LONG + * (or the end of memory slot) + */ d.dirty_bitmap = mem->dirty_bmap + BIT_WORD(bmap_start); } @@ -748,8 +756,8 @@ static int kvm_log_clear_one_slot(KVMSlot *mem, int as_id, uint64_t start, d.num_pages = bmap_npages; d.slot = mem->slot | (as_id << 16); - if (kvm_vm_ioctl(s, KVM_CLEAR_DIRTY_LOG, &d) == -1) { - ret = -errno; + ret = kvm_vm_ioctl(s, KVM_CLEAR_DIRTY_LOG, &d); + if (ret < 0 && ret != -ENOENT) { error_report("%s: KVM_CLEAR_DIRTY_LOG failed, slot=%d, " "start=0x%"PRIx64", size=0x%"PRIx32", errno=%d", __func__, d.slot, (uint64_t)d.first_page, @@ -1993,7 +2001,6 @@ static int kvm_init(MachineState *ms) const KVMCapabilityInfo *missing_cap; int ret; int type = 0; - const char *kvm_type; uint64_t dirty_log_manual_caps; s = KVM_STATE(ms->accelerator); @@ -2013,7 +2020,7 @@ static int kvm_init(MachineState *ms) #endif QLIST_INIT(&s->kvm_parked_vcpus); s->vmfd = -1; - s->fd = qemu_open("/dev/kvm", O_RDWR); + s->fd = qemu_open_old("/dev/kvm", O_RDWR); if (s->fd == -1) { fprintf(stderr, "Could not access KVM kernel module: %m\n"); ret = -errno; @@ -2049,13 +2056,13 @@ static int kvm_init(MachineState *ms) } s->as = g_new0(struct KVMAs, s->nr_as); - kvm_type = qemu_opt_get(qemu_get_machine_opts(), "kvm-type"); - if (mc->kvm_type) { + if (object_property_find(OBJECT(current_machine), "kvm-type")) { + g_autofree char *kvm_type = object_property_get_str(OBJECT(current_machine), + "kvm-type", + &error_abort); type = mc->kvm_type(ms, kvm_type); - } else if (kvm_type) { - ret = -EINVAL; - fprintf(stderr, "Invalid argument kvm-type=%s\n", kvm_type); - goto err; + } else if (mc->kvm_type) { + type = mc->kvm_type(ms, NULL); } do { @@ -2181,20 +2188,6 @@ static int kvm_init(MachineState *ms) kvm_state = s; - /* - * if memory encryption object is specified then initialize the memory - * encryption context. - */ - if (ms->memory_encryption) { - kvm_state->memcrypt_handle = sev_guest_init(ms->memory_encryption); - if (!kvm_state->memcrypt_handle) { - ret = -1; - goto err; - } - - kvm_state->memcrypt_encrypt_data = sev_encrypt_data; - } - ret = kvm_arch_init(ms, s); if (ret < 0) { goto err; @@ -2219,8 +2212,10 @@ static int kvm_init(MachineState *ms) kvm_memory_listener_register(s, &s->memory_listener, &address_space_memory, 0); - memory_listener_register(&kvm_io_listener, - &address_space_io); + if (kvm_eventfds_allowed) { + memory_listener_register(&kvm_io_listener, + &address_space_io); + } memory_listener_register(&kvm_coalesced_pio_listener, &address_space_io); @@ -2231,7 +2226,6 @@ static int kvm_init(MachineState *ms) ret = ram_block_discard_disable(true); assert(!ret); } - return 0; err: @@ -2324,6 +2318,11 @@ void kvm_flush_coalesced_mmio_buffer(void) s->coalesced_flush_in_progress = false; } +bool kvm_cpu_check_are_resettable(void) +{ + return kvm_arch_cpu_check_are_resettable(); +} + static void do_kvm_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) { if (!cpu->vcpu_dirty) { @@ -2379,7 +2378,7 @@ static __thread bool have_sigbus_pending; static void kvm_cpu_kick(CPUState *cpu) { - atomic_set(&cpu->kvm_run->immediate_exit, 1); + qatomic_set(&cpu->kvm_run->immediate_exit, 1); } static void kvm_cpu_kick_self(void) @@ -2400,7 +2399,7 @@ static void kvm_eat_signals(CPUState *cpu) int r; if (kvm_immediate_exit) { - atomic_set(&cpu->kvm_run->immediate_exit, 0); + qatomic_set(&cpu->kvm_run->immediate_exit, 0); /* Write kvm_run->immediate_exit before the cpu->exit_request * write in kvm_cpu_exec. */ @@ -2434,7 +2433,7 @@ int kvm_cpu_exec(CPUState *cpu) DPRINTF("kvm_cpu_exec()\n"); if (kvm_arch_process_async_events(cpu)) { - atomic_set(&cpu->exit_request, 0); + qatomic_set(&cpu->exit_request, 0); return EXCP_HLT; } @@ -2450,7 +2449,7 @@ int kvm_cpu_exec(CPUState *cpu) } kvm_arch_pre_run(cpu, run); - if (atomic_read(&cpu->exit_request)) { + if (qatomic_read(&cpu->exit_request)) { DPRINTF("interrupt exit requested\n"); /* * KVM requires us to reenter the kernel after IO exits to complete @@ -2577,7 +2576,7 @@ int kvm_cpu_exec(CPUState *cpu) vm_stop(RUN_STATE_INTERNAL_ERROR); } - atomic_set(&cpu->exit_request, 0); + qatomic_set(&cpu->exit_request, 0); return ret; } @@ -2994,7 +2993,7 @@ int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr) have_sigbus_pending = true; pending_sigbus_addr = addr; pending_sigbus_code = code; - atomic_set(&cpu->exit_request, 1); + qatomic_set(&cpu->exit_request, 1); return 0; #else return 1; diff --git a/accel/kvm/kvm-cpus.h b/accel/kvm/kvm-cpus.h new file mode 100644 index 0000000000..bf0bd1bee4 --- /dev/null +++ b/accel/kvm/kvm-cpus.h @@ -0,0 +1,22 @@ +/* + * Accelerator CPUS Interface + * + * Copyright 2020 SUSE LLC + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef KVM_CPUS_H +#define KVM_CPUS_H + +#include "sysemu/cpus.h" + +int kvm_init_vcpu(CPUState *cpu, Error **errp); +int kvm_cpu_exec(CPUState *cpu); +void kvm_destroy_vcpu(CPUState *cpu); +void kvm_cpu_synchronize_post_reset(CPUState *cpu); +void kvm_cpu_synchronize_post_init(CPUState *cpu); +void kvm_cpu_synchronize_pre_loadvm(CPUState *cpu); + +#endif /* KVM_CPUS_H */ diff --git a/accel/kvm/meson.build b/accel/kvm/meson.build new file mode 100644 index 0000000000..8d219bea50 --- /dev/null +++ b/accel/kvm/meson.build @@ -0,0 +1,8 @@ +kvm_ss = ss.source_set() +kvm_ss.add(files( + 'kvm-all.c', + 'kvm-accel-ops.c', +)) +kvm_ss.add(when: 'CONFIG_SEV', if_false: files('sev-stub.c')) + +specific_ss.add_all(when: 'CONFIG_KVM', if_true: kvm_ss) diff --git a/accel/kvm/sev-stub.c b/accel/kvm/sev-stub.c index 4f97452585..9587d1b2a3 100644 --- a/accel/kvm/sev-stub.c +++ b/accel/kvm/sev-stub.c @@ -15,12 +15,8 @@ #include "qemu-common.h" #include "sysemu/sev.h" -int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len) +int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { - abort(); -} - -void *sev_guest_init(const char *id) -{ - return NULL; + /* If we get here, cgs must be some non-SEV thing */ + return 0; } diff --git a/accel/kvm/trace-events b/accel/kvm/trace-events index a68eb66534..e15ae8980d 100644 --- a/accel/kvm/trace-events +++ b/accel/kvm/trace-events @@ -8,6 +8,7 @@ kvm_run_exit(int cpu_index, uint32_t reason) "cpu_index %d, reason %d" kvm_device_ioctl(int fd, int type, void *arg) "dev fd %d, type 0x%x, arg %p" kvm_failed_reg_get(uint64_t id, const char *msg) "Warning: Unable to retrieve ONEREG %" PRIu64 " from KVM: %s" kvm_failed_reg_set(uint64_t id, const char *msg) "Warning: Unable to set ONEREG %" PRIu64 " to KVM: %s" +kvm_init_vcpu(int cpu_index, unsigned long arch_cpu_id) "index: %d id: %lu" kvm_irqchip_commit_routes(void) "" kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d" kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d" diff --git a/accel/kvm/trace.h b/accel/kvm/trace.h new file mode 100644 index 0000000000..67c935a6f7 --- /dev/null +++ b/accel/kvm/trace.h @@ -0,0 +1 @@ +#include "trace/trace-accel_kvm.h" diff --git a/accel/meson.build b/accel/meson.build new file mode 100644 index 0000000000..b44ba30c86 --- /dev/null +++ b/accel/meson.build @@ -0,0 +1,17 @@ +specific_ss.add(files('accel-common.c')) +softmmu_ss.add(files('accel-softmmu.c')) +user_ss.add(files('accel-user.c')) + +subdir('qtest') +subdir('kvm') +subdir('tcg') +subdir('xen') +subdir('stubs') + +dummy_ss = ss.source_set() +dummy_ss.add(files( + 'dummy-cpus.c', +)) + +specific_ss.add_all(when: ['CONFIG_SOFTMMU', 'CONFIG_POSIX'], if_true: dummy_ss) +specific_ss.add_all(when: ['CONFIG_XEN'], if_true: dummy_ss) diff --git a/accel/qtest/meson.build b/accel/qtest/meson.build new file mode 100644 index 0000000000..a2f3276459 --- /dev/null +++ b/accel/qtest/meson.build @@ -0,0 +1,6 @@ +qtest_ss = ss.source_set() +qtest_ss.add(files( + 'qtest.c', +)) + +specific_ss.add_all(when: ['CONFIG_SOFTMMU', 'CONFIG_POSIX'], if_true: qtest_ss) diff --git a/accel/qtest.c b/accel/qtest/qtest.c similarity index 61% rename from accel/qtest.c rename to accel/qtest/qtest.c index 5b88f55921..edb29f6fa4 100644 --- a/accel/qtest.c +++ b/accel/qtest/qtest.c @@ -12,21 +12,21 @@ */ #include "qemu/osdep.h" +#include "qemu/rcu.h" #include "qapi/error.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/config-file.h" -#include "sysemu/accel.h" +#include "qemu/accel.h" #include "sysemu/qtest.h" #include "sysemu/cpus.h" +#include "sysemu/cpu-timers.h" +#include "qemu/guest-random.h" +#include "qemu/main-loop.h" +#include "hw/core/cpu.h" static int qtest_init_accel(MachineState *ms) { - QemuOpts *opts = qemu_opts_create(qemu_find_opts("icount"), NULL, 0, - &error_abort); - qemu_opt_set(opts, "shift", "0", &error_abort); - configure_icount(opts, &error_abort); - qemu_opts_del(opts); return 0; } @@ -46,9 +46,26 @@ static const TypeInfo qtest_accel_type = { .class_init = qtest_accel_class_init, }; +static void qtest_accel_ops_class_init(ObjectClass *oc, void *data) +{ + AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); + + ops->create_vcpu_thread = dummy_start_vcpu_thread; + ops->get_virtual_clock = qtest_get_virtual_clock; +}; + +static const TypeInfo qtest_accel_ops_type = { + .name = ACCEL_OPS_NAME("qtest"), + + .parent = TYPE_ACCEL_OPS, + .class_init = qtest_accel_ops_class_init, + .abstract = true, +}; + static void qtest_type_init(void) { type_register_static(&qtest_accel_type); + type_register_static(&qtest_accel_ops_type); } type_init(qtest_type_init); diff --git a/accel/stubs/Makefile.objs b/accel/stubs/Makefile.objs deleted file mode 100644 index bbd14e71fb..0000000000 --- a/accel/stubs/Makefile.objs +++ /dev/null @@ -1,6 +0,0 @@ -obj-$(call lnot,$(CONFIG_HAX)) += hax-stub.o -obj-$(call lnot,$(CONFIG_HVF)) += hvf-stub.o -obj-$(call lnot,$(CONFIG_WHPX)) += whpx-stub.o -obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o -obj-$(call lnot,$(CONFIG_TCG)) += tcg-stub.o -obj-$(call lnot,$(CONFIG_XEN)) += xen-stub.o diff --git a/accel/stubs/hax-stub.c b/accel/stubs/hax-stub.c index 7ad190cae2..49077f88e3 100644 --- a/accel/stubs/hax-stub.c +++ b/accel/stubs/hax-stub.c @@ -14,20 +14,9 @@ */ #include "qemu/osdep.h" -#include "cpu.h" #include "sysemu/hax.h" int hax_sync_vcpus(void) { return 0; } - -int hax_init_vcpu(CPUState *cpu) -{ - return -ENOSYS; -} - -int hax_smp_cpu_exec(CPUState *cpu) -{ - return -ENOSYS; -} diff --git a/accel/stubs/hvf-stub.c b/accel/stubs/hvf-stub.c deleted file mode 100644 index e81dfe888c..0000000000 --- a/accel/stubs/hvf-stub.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * QEMU HVF support - * - * Copyright 2017 Red Hat, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2 or later, as published by the Free Software Foundation, - * and may be copied, distributed, and modified under those terms. - * - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "sysemu/hvf.h" - -int hvf_init_vcpu(CPUState *cpu) -{ - return -ENOSYS; -} - -int hvf_vcpu_exec(CPUState *cpu) -{ - return -ENOSYS; -} - -void hvf_vcpu_destroy(CPUState *cpu) -{ -} diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index 82f118d2df..0f17acfac0 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c @@ -32,16 +32,6 @@ bool kvm_readonly_mem_allowed; bool kvm_ioeventfd_any_length_allowed; bool kvm_msi_use_devid; -int kvm_destroy_vcpu(CPUState *cpu) -{ - return -ENOSYS; -} - -int kvm_init_vcpu(CPUState *cpu) -{ - return -ENOSYS; -} - void kvm_flush_coalesced_mmio_buffer(void) { } @@ -50,19 +40,6 @@ void kvm_cpu_synchronize_state(CPUState *cpu) { } -void kvm_cpu_synchronize_post_reset(CPUState *cpu) -{ -} - -void kvm_cpu_synchronize_post_init(CPUState *cpu) -{ -} - -int kvm_cpu_exec(CPUState *cpu) -{ - abort(); -} - bool kvm_has_sync_mmu(void) { return false; @@ -104,16 +81,6 @@ int kvm_on_sigbus(int code, void *addr) return 1; } -bool kvm_memcrypt_enabled(void) -{ - return false; -} - -int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len) -{ - return 1; -} - #ifndef CONFIG_USER_ONLY int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev) { diff --git a/accel/stubs/meson.build b/accel/stubs/meson.build new file mode 100644 index 0000000000..12dd1539af --- /dev/null +++ b/accel/stubs/meson.build @@ -0,0 +1,4 @@ +specific_ss.add(when: 'CONFIG_HAX', if_false: files('hax-stub.c')) +specific_ss.add(when: 'CONFIG_XEN', if_false: files('xen-stub.c')) +specific_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c')) +specific_ss.add(when: 'CONFIG_TCG', if_false: files('tcg-stub.c')) diff --git a/accel/stubs/tcg-stub.c b/accel/stubs/tcg-stub.c index e4bbf997aa..2304606f8e 100644 --- a/accel/stubs/tcg-stub.c +++ b/accel/stubs/tcg-stub.c @@ -12,7 +12,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "tcg/tcg.h" #include "exec/exec-all.h" void tb_flush(CPUState *cpu) @@ -29,3 +28,13 @@ void *probe_access(CPUArchState *env, target_ulong addr, int size, /* Handled by hardware accelerator. */ g_assert_not_reached(); } + +void QEMU_NORETURN cpu_loop_exit(CPUState *cpu) +{ + g_assert_not_reached(); +} + +void QEMU_NORETURN cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc) +{ + g_assert_not_reached(); +} diff --git a/accel/stubs/whpx-stub.c b/accel/stubs/whpx-stub.c deleted file mode 100644 index 1efb89f25e..0000000000 --- a/accel/stubs/whpx-stub.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * QEMU Windows Hypervisor Platform accelerator (WHPX) stub - * - * Copyright Microsoft Corp. 2017 - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "sysemu/whpx.h" - -int whpx_init_vcpu(CPUState *cpu) -{ - return -1; -} - -int whpx_vcpu_exec(CPUState *cpu) -{ - return -1; -} - -void whpx_destroy_vcpu(CPUState *cpu) -{ -} - -void whpx_vcpu_kick(CPUState *cpu) -{ -} - -void whpx_cpu_synchronize_state(CPUState *cpu) -{ -} - -void whpx_cpu_synchronize_post_reset(CPUState *cpu) -{ -} - -void whpx_cpu_synchronize_post_init(CPUState *cpu) -{ -} - -void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu) -{ -} diff --git a/accel/stubs/xen-stub.c b/accel/stubs/xen-stub.c index 8ae658acff..7054965c48 100644 --- a/accel/stubs/xen-stub.c +++ b/accel/stubs/xen-stub.c @@ -6,50 +6,11 @@ */ #include "qemu/osdep.h" -#include "hw/xen/xen.h" -#include "qapi/qapi-commands-misc.h" +#include "sysemu/xen.h" +#include "qapi/qapi-commands-migration.h" bool xen_allowed; -void xenstore_store_pv_console_info(int i, Chardev *chr) -{ -} - -int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num) -{ - return -1; -} - -void xen_piix3_set_irq(void *opaque, int irq_num, int level) -{ -} - -void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len) -{ -} - -void xen_hvm_inject_msi(uint64_t addr, uint32_t data) -{ -} - -int xen_is_pirq_msi(uint32_t msi_data) -{ - return 0; -} - -qemu_irq *xen_interrupt_controller_init(void) -{ - return NULL; -} - -void xen_register_framebuffer(MemoryRegion *mr) -{ -} - -void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) -{ -} - void qmp_xen_set_global_dirty_log(bool enable, Error **errp) { } diff --git a/accel/tcg/Makefile.objs b/accel/tcg/Makefile.objs deleted file mode 100644 index a92f2c454b..0000000000 --- a/accel/tcg/Makefile.objs +++ /dev/null @@ -1,9 +0,0 @@ -obj-$(CONFIG_SOFTMMU) += tcg-all.o -obj-$(CONFIG_SOFTMMU) += cputlb.o -obj-y += tcg-runtime.o tcg-runtime-gvec.o -obj-y += cpu-exec.o cpu-exec-common.o translate-all.o -obj-y += translator.o - -obj-$(CONFIG_USER_ONLY) += user-exec.o -obj-$(call lnot,$(CONFIG_SOFTMMU)) += user-exec-stub.o -obj-$(CONFIG_PLUGIN) += plugin-gen.o diff --git a/accel/tcg/atomic_common.inc.c b/accel/tcg/atomic_common.c.inc similarity index 100% rename from accel/tcg/atomic_common.inc.c rename to accel/tcg/atomic_common.c.inc diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h index 26969487d6..0ff7f913e1 100644 --- a/accel/tcg/atomic_template.h +++ b/accel/tcg/atomic_template.h @@ -83,7 +83,7 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, #if DATA_SIZE == 16 ret = atomic16_cmpxchg(haddr, cmpv, newv); #else - ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv); + ret = qatomic_cmpxchg__nocheck(haddr, cmpv, newv); #endif ATOMIC_MMU_CLEANUP; atomic_trace_rmw_post(env, addr, info); @@ -131,7 +131,7 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ATOMIC_MMU_IDX); atomic_trace_rmw_pre(env, addr, info); - ret = atomic_xchg__nocheck(haddr, val); + ret = qatomic_xchg__nocheck(haddr, val); ATOMIC_MMU_CLEANUP; atomic_trace_rmw_post(env, addr, info); return ret; @@ -147,7 +147,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ uint16_t info = trace_mem_build_info(SHIFT, false, 0, false, \ ATOMIC_MMU_IDX); \ atomic_trace_rmw_pre(env, addr, info); \ - ret = atomic_##X(haddr, val); \ + ret = qatomic_##X(haddr, val); \ ATOMIC_MMU_CLEANUP; \ atomic_trace_rmw_post(env, addr, info); \ return ret; \ @@ -182,10 +182,10 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ ATOMIC_MMU_IDX); \ atomic_trace_rmw_pre(env, addr, info); \ smp_mb(); \ - cmp = atomic_read__nocheck(haddr); \ + cmp = qatomic_read__nocheck(haddr); \ do { \ old = cmp; new = FN(old, val); \ - cmp = atomic_cmpxchg__nocheck(haddr, old, new); \ + cmp = qatomic_cmpxchg__nocheck(haddr, old, new); \ } while (cmp != old); \ ATOMIC_MMU_CLEANUP; \ atomic_trace_rmw_post(env, addr, info); \ @@ -230,7 +230,7 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, #if DATA_SIZE == 16 ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv)); #else - ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); + ret = qatomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); #endif ATOMIC_MMU_CLEANUP; atomic_trace_rmw_post(env, addr, info); @@ -280,7 +280,7 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ATOMIC_MMU_IDX); atomic_trace_rmw_pre(env, addr, info); - ret = atomic_xchg__nocheck(haddr, BSWAP(val)); + ret = qatomic_xchg__nocheck(haddr, BSWAP(val)); ATOMIC_MMU_CLEANUP; atomic_trace_rmw_post(env, addr, info); return BSWAP(ret); @@ -296,7 +296,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, \ false, ATOMIC_MMU_IDX); \ atomic_trace_rmw_pre(env, addr, info); \ - ret = atomic_##X(haddr, BSWAP(val)); \ + ret = qatomic_##X(haddr, BSWAP(val)); \ ATOMIC_MMU_CLEANUP; \ atomic_trace_rmw_post(env, addr, info); \ return BSWAP(ret); \ @@ -329,10 +329,10 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ false, ATOMIC_MMU_IDX); \ atomic_trace_rmw_pre(env, addr, info); \ smp_mb(); \ - ldn = atomic_read__nocheck(haddr); \ + ldn = qatomic_read__nocheck(haddr); \ do { \ ldo = ldn; old = BSWAP(ldo); new = FN(old, val); \ - ldn = atomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new)); \ + ldn = qatomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new)); \ } while (ldo != ldn); \ ATOMIC_MMU_CLEANUP; \ atomic_trace_rmw_post(env, addr, info); \ diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 66d38f9d85..f62f12e717 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -19,13 +19,15 @@ #include "qemu/osdep.h" #include "qemu-common.h" +#include "qemu/qemu-print.h" #include "cpu.h" +#include "hw/core/tcg-cpu-ops.h" #include "trace.h" #include "disas/disas.h" #include "exec/exec-all.h" #include "tcg/tcg.h" #include "qemu/atomic.h" -#include "sysemu/qtest.h" +#include "qemu/compiler.h" #include "qemu/timer.h" #include "qemu/rcu.h" #include "exec/tb-hash.h" @@ -36,7 +38,10 @@ #include "hw/i386/apic.h" #endif #include "sysemu/cpus.h" +#include "exec/cpu-all.h" +#include "sysemu/cpu-timers.h" #include "sysemu/replay.h" +#include "internal.h" /* -icount align implementation. */ @@ -56,6 +61,9 @@ typedef struct SyncClocks { #define MAX_DELAY_PRINT_RATE 2000000000LL #define MAX_NB_PRINTS 100 +static int64_t max_delay; +static int64_t max_advance; + static void align_clocks(SyncClocks *sc, CPUState *cpu) { int64_t cpu_icount; @@ -65,7 +73,7 @@ static void align_clocks(SyncClocks *sc, CPUState *cpu) } cpu_icount = cpu->icount_extra + cpu_neg(cpu)->icount_decr.u16.low; - sc->diff_clk += cpu_icount_to_ns(sc->last_cpu_icount - cpu_icount); + sc->diff_clk += icount_to_ns(sc->last_cpu_icount - cpu_icount); sc->last_cpu_icount = cpu_icount; if (sc->diff_clk > VM_CLOCK_ADVANCE) { @@ -98,9 +106,9 @@ static void print_delay(const SyncClocks *sc) (-sc->diff_clk / (float)1000000000LL < (threshold_delay - THRESHOLD_REDUCE))) { threshold_delay = (-sc->diff_clk / 1000000000LL) + 1; - printf("Warning: The guest is now late by %.1f to %.1f seconds\n", - threshold_delay - 1, - threshold_delay); + qemu_printf("Warning: The guest is now late by %.1f to %.1f seconds\n", + threshold_delay - 1, + threshold_delay); nb_prints++; last_realtime_clock = sc->realtime_clock; } @@ -138,13 +146,22 @@ static void init_delay_params(SyncClocks *sc, const CPUState *cpu) #endif /* CONFIG USER ONLY */ /* Execute a TB, and fix up the CPU state afterwards if necessary */ -static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb) +/* + * Disable CFI checks. + * TCG creates binary blobs at runtime, with the transformed code. + * A TB is a blob of binary code, created at runtime and called with an + * indirect function call. Since such function did not exist at compile time, + * the CFI runtime has no way to verify its signature and would fail. + * TCG is not considered a security-sensitive part of QEMU so this does not + * affect the impact of CFI in environment with high security requirements + */ +static inline TranslationBlock * QEMU_DISABLE_CFI +cpu_tb_exec(CPUState *cpu, TranslationBlock *itb, int *tb_exit) { CPUArchState *env = cpu->env_ptr; uintptr_t ret; TranslationBlock *last_tb; - int tb_exit; - uint8_t *tb_ptr = itb->tc.ptr; + const void *tb_ptr = itb->tc.ptr; qemu_log_mask_and_addr(CPU_LOG_EXEC, itb->pc, "Trace %d: %p [" @@ -169,13 +186,23 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb) } #endif /* DEBUG_DISAS */ + qemu_thread_jit_execute(); ret = tcg_qemu_tb_exec(env, tb_ptr); cpu->can_do_io = 1; - last_tb = (TranslationBlock *)(ret & ~TB_EXIT_MASK); - tb_exit = ret & TB_EXIT_MASK; - trace_exec_tb_exit(last_tb, tb_exit); + /* + * TODO: Delay swapping back to the read-write region of the TB + * until we actually need to modify the TB. The read-only copy, + * coming from the rx region, shares the same host TLB entry as + * the code that executed the exit_tb opcode that arrived here. + * If we insist on touching both the RX and the RW pages, we + * double the host TLB pressure. + */ + last_tb = tcg_splitwx_to_rw((void *)(ret & ~TB_EXIT_MASK)); + *tb_exit = ret & TB_EXIT_MASK; - if (tb_exit > TB_EXIT_IDX1) { + trace_exec_tb_exit(last_tb, *tb_exit); + + if (*tb_exit > TB_EXIT_IDX1) { /* We didn't start executing this TB (eg because the instruction * counter hit zero); we must restore the guest PC to the address * of the start of the TB. @@ -186,76 +213,64 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb) TARGET_FMT_lx "] %s\n", last_tb->tc.ptr, last_tb->pc, lookup_symbol(last_tb->pc)); - if (cc->synchronize_from_tb) { - cc->synchronize_from_tb(cpu, last_tb); + if (cc->tcg_ops->synchronize_from_tb) { + cc->tcg_ops->synchronize_from_tb(cpu, last_tb); } else { assert(cc->set_pc); cc->set_pc(cpu, last_tb->pc); } } - return ret; + return last_tb; } -#ifndef CONFIG_USER_ONLY -/* Execute the code without caching the generated code. An interpreter - could be used if available. */ -static void cpu_exec_nocache(CPUState *cpu, int max_cycles, - TranslationBlock *orig_tb, bool ignore_icount) + +static void cpu_exec_enter(CPUState *cpu) { - TranslationBlock *tb; - uint32_t cflags = curr_cflags() | CF_NOCACHE; + CPUClass *cc = CPU_GET_CLASS(cpu); - if (ignore_icount) { - cflags &= ~CF_USE_ICOUNT; + if (cc->tcg_ops->cpu_exec_enter) { + cc->tcg_ops->cpu_exec_enter(cpu); + } +} + +static void cpu_exec_exit(CPUState *cpu) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + + if (cc->tcg_ops->cpu_exec_exit) { + cc->tcg_ops->cpu_exec_exit(cpu); } - - /* Should never happen. - We only end up here when an existing TB is too long. */ - cflags |= MIN(max_cycles, CF_COUNT_MASK); - - mmap_lock(); - tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base, - orig_tb->flags, cflags); - tb->orig_tb = orig_tb; - mmap_unlock(); - - /* execute the generated code */ - trace_exec_tb_nocache(tb, tb->pc); - cpu_tb_exec(cpu, tb); - - mmap_lock(); - tb_phys_invalidate(tb, -1); - mmap_unlock(); - tcg_tb_remove(tb); } -#endif void cpu_exec_step_atomic(CPUState *cpu) { - CPUClass *cc = CPU_GET_CLASS(cpu); + CPUArchState *env = (CPUArchState *)cpu->env_ptr; TranslationBlock *tb; target_ulong cs_base, pc; uint32_t flags; - uint32_t cflags = 1; - uint32_t cf_mask = cflags & CF_HASH_MASK; + uint32_t cflags = (curr_cflags(cpu) & ~CF_PARALLEL) | 1; + int tb_exit; if (sigsetjmp(cpu->jmp_env, 0) == 0) { start_exclusive(); + g_assert(cpu == current_cpu); + g_assert(!cpu->running); + cpu->running = true; + + cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); + tb = tb_lookup(cpu, pc, cs_base, flags, cflags); - tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); if (tb == NULL) { mmap_lock(); tb = tb_gen_code(cpu, pc, cs_base, flags, cflags); mmap_unlock(); } - /* Since we got here, we know that parallel_cpus must be true. */ - parallel_cpus = false; - cc->cpu_exec_enter(cpu); + cpu_exec_enter(cpu); /* execute the generated code */ trace_exec_tb(tb, pc); - cpu_tb_exec(cpu, tb); - cc->cpu_exec_exit(cpu); + cpu_tb_exec(cpu, tb, &tb_exit); + cpu_exec_exit(cpu); } else { /* * The mmap_lock is dropped by tb_gen_code if it runs out of @@ -278,7 +293,7 @@ void cpu_exec_step_atomic(CPUState *cpu) * the execution. */ g_assert(cpu_in_exclusive_context(cpu)); - parallel_cpus = true; + cpu->running = false; end_exclusive(); } @@ -288,7 +303,7 @@ struct tb_desc { CPUArchState *env; tb_page_addr_t phys_page1; uint32_t flags; - uint32_t cf_mask; + uint32_t cflags; uint32_t trace_vcpu_dstate; }; @@ -302,7 +317,7 @@ static bool tb_lookup_cmp(const void *p, const void *d) tb->cs_base == desc->cs_base && tb->flags == desc->flags && tb->trace_vcpu_dstate == desc->trace_vcpu_dstate && - (tb_cflags(tb) & (CF_HASH_MASK | CF_INVALID)) == desc->cf_mask) { + tb_cflags(tb) == desc->cflags) { /* check next page if needed */ if (tb->page_addr[1] == -1) { return true; @@ -322,7 +337,7 @@ static bool tb_lookup_cmp(const void *p, const void *d) TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc, target_ulong cs_base, uint32_t flags, - uint32_t cf_mask) + uint32_t cflags) { tb_page_addr_t phys_pc; struct tb_desc desc; @@ -331,7 +346,7 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc, desc.env = (CPUArchState *)cpu->env_ptr; desc.cs_base = cs_base; desc.flags = flags; - desc.cf_mask = cf_mask; + desc.cflags = cflags; desc.trace_vcpu_dstate = *cpu->trace_dstate; desc.pc = pc; phys_pc = get_page_addr_code(desc.env, pc); @@ -339,7 +354,7 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc, return NULL; } desc.phys_page1 = phys_pc & TARGET_PAGE_MASK; - h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate); + h = tb_hash_func(phys_pc, pc, flags, cflags, *cpu->trace_dstate); return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp); } @@ -348,7 +363,9 @@ void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr) if (TCG_TARGET_HAS_direct_jump) { uintptr_t offset = tb->jmp_target_arg[n]; uintptr_t tc_ptr = (uintptr_t)tb->tc.ptr; - tb_target_set_jmp_target(tc_ptr, tc_ptr + offset, addr); + uintptr_t jmp_rx = tc_ptr + offset; + uintptr_t jmp_rw = jmp_rx - tcg_splitwx_diff; + tb_target_set_jmp_target(tc_ptr, jmp_rx, jmp_rw, addr); } else { tb->jmp_target_arg[n] = addr; } @@ -359,6 +376,7 @@ static inline void tb_add_jump(TranslationBlock *tb, int n, { uintptr_t old; + qemu_thread_jit_write(); assert(n < ARRAY_SIZE(tb->jmp_list_next)); qemu_spin_lock(&tb_next->jmp_lock); @@ -367,7 +385,8 @@ static inline void tb_add_jump(TranslationBlock *tb, int n, goto out_unlock_next; } /* Atomically claim the jump destination slot only if it was NULL */ - old = atomic_cmpxchg(&tb->jmp_dest[n], (uintptr_t)NULL, (uintptr_t)tb_next); + old = qatomic_cmpxchg(&tb->jmp_dest[n], (uintptr_t)NULL, + (uintptr_t)tb_next); if (old) { goto out_unlock_next; } @@ -395,19 +414,22 @@ static inline void tb_add_jump(TranslationBlock *tb, int n, static inline TranslationBlock *tb_find(CPUState *cpu, TranslationBlock *last_tb, - int tb_exit, uint32_t cf_mask) + int tb_exit, uint32_t cflags) { + CPUArchState *env = (CPUArchState *)cpu->env_ptr; TranslationBlock *tb; target_ulong cs_base, pc; uint32_t flags; - tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); + cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); + + tb = tb_lookup(cpu, pc, cs_base, flags, cflags); if (tb == NULL) { mmap_lock(); - tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask); + tb = tb_gen_code(cpu, pc, cs_base, flags, cflags); mmap_unlock(); /* We add the TB in the virtual pc hash table for the fast lookup */ - atomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb); + qatomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb); } #ifndef CONFIG_USER_ONLY /* We don't take care of direct jumps when address mapping changes in @@ -429,8 +451,7 @@ static inline bool cpu_handle_halt(CPUState *cpu) { if (cpu->halted) { #if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY) - if ((cpu->interrupt_request & CPU_INTERRUPT_POLL) - && replay_interrupt()) { + if (cpu->interrupt_request & CPU_INTERRUPT_POLL) { X86CPU *x86_cpu = X86_CPU(cpu); qemu_mutex_lock_iothread(); apic_poll_irq(x86_cpu->apic_state); @@ -459,7 +480,9 @@ static inline void cpu_handle_debug_exception(CPUState *cpu) } } - cc->debug_excp_handler(cpu); + if (cc->tcg_ops->debug_excp_handler) { + cc->tcg_ops->debug_excp_handler(cpu); + } } static inline bool cpu_handle_exception(CPUState *cpu, int *ret) @@ -468,15 +491,12 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) #ifndef CONFIG_USER_ONLY if (replay_has_exception() && cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra == 0) { - /* try to cause an exception pending in the log */ - cpu_exec_nocache(cpu, 1, tb_find(cpu, NULL, 0, curr_cflags()), true); + /* Execute just one insn to trigger exception pending in the log */ + cpu->cflags_next_tb = (curr_cflags(cpu) & ~CF_USE_ICOUNT) | 1; } #endif - if (cpu->exception_index < 0) { - return false; - } + return false; } - if (cpu->exception_index >= EXCP_INTERRUPT) { /* exit request from the cpu execution loop */ *ret = cpu->exception_index; @@ -492,7 +512,7 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) loop */ #if defined(TARGET_I386) CPUClass *cc = CPU_GET_CLASS(cpu); - cc->do_interrupt(cpu); + cc->tcg_ops->do_interrupt(cpu); #endif *ret = cpu->exception_index; cpu->exception_index = -1; @@ -501,7 +521,7 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) if (replay_exception()) { CPUClass *cc = CPU_GET_CLASS(cpu); qemu_mutex_lock_iothread(); - cc->do_interrupt(cpu); + cc->tcg_ops->do_interrupt(cpu); qemu_mutex_unlock_iothread(); cpu->exception_index = -1; @@ -526,6 +546,20 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) return false; } +/* + * CPU_INTERRUPT_POLL is a virtual event which gets converted into a + * "real" interrupt event later. It does not need to be recorded for + * replay purposes. + */ +static inline bool need_replay_interrupt(int interrupt_request) +{ +#if defined(TARGET_I386) + return !(interrupt_request & CPU_INTERRUPT_POLL); +#else + return true; +#endif +} + static inline bool cpu_handle_interrupt(CPUState *cpu, TranslationBlock **last_tb) { @@ -536,9 +570,9 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, * Ensure zeroing happens before reading cpu->exit_request or * cpu->interrupt_request (see also smp_wmb in cpu_exit()) */ - atomic_mb_set(&cpu_neg(cpu)->icount_decr.u16.high, 0); + qatomic_mb_set(&cpu_neg(cpu)->icount_decr.u16.high, 0); - if (unlikely(atomic_read(&cpu->interrupt_request))) { + if (unlikely(qatomic_read(&cpu->interrupt_request))) { int interrupt_request; qemu_mutex_lock_iothread(); interrupt_request = cpu->interrupt_request; @@ -586,8 +620,11 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, True when it is, and we should restart on a new TB, and via longjmp via cpu_loop_exit. */ else { - if (cc->cpu_exec_interrupt(cpu, interrupt_request)) { - replay_interrupt(); + if (cc->tcg_ops->cpu_exec_interrupt && + cc->tcg_ops->cpu_exec_interrupt(cpu, interrupt_request)) { + if (need_replay_interrupt(interrupt_request)) { + replay_interrupt(); + } /* * After processing the interrupt, ensure an EXCP_DEBUG is * raised when single-stepping so that GDB doesn't miss the @@ -613,10 +650,11 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, } /* Finally, check if we need to exit to the main loop. */ - if (unlikely(atomic_read(&cpu->exit_request)) - || (use_icount + if (unlikely(qatomic_read(&cpu->exit_request)) + || (icount_enabled() + && (cpu->cflags_next_tb == -1 || cpu->cflags_next_tb & CF_USE_ICOUNT) && cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra == 0)) { - atomic_set(&cpu->exit_request, 0); + qatomic_set(&cpu->exit_request, 0); if (cpu->exception_index == -1) { cpu->exception_index = EXCP_INTERRUPT; } @@ -629,20 +667,17 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, TranslationBlock **last_tb, int *tb_exit) { - uintptr_t ret; int32_t insns_left; trace_exec_tb(tb, tb->pc); - ret = cpu_tb_exec(cpu, tb); - tb = (TranslationBlock *)(ret & ~TB_EXIT_MASK); - *tb_exit = ret & TB_EXIT_MASK; + tb = cpu_tb_exec(cpu, tb, tb_exit); if (*tb_exit != TB_EXIT_REQUESTED) { *last_tb = tb; return; } *last_tb = NULL; - insns_left = atomic_read(&cpu_neg(cpu)->icount_decr.u32); + insns_left = qatomic_read(&cpu_neg(cpu)->icount_decr.u32); if (insns_left < 0) { /* Something asked us to stop executing chained TBs; just * continue round the main loop. Whatever requested the exit @@ -655,21 +690,22 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, } /* Instruction counter expired. */ - assert(use_icount); + assert(icount_enabled()); #ifndef CONFIG_USER_ONLY /* Ensure global icount has gone forward */ - cpu_update_icount(cpu); + icount_update(cpu); /* Refill decrementer and continue execution. */ - insns_left = MIN(0xffff, cpu->icount_budget); + insns_left = MIN(CF_COUNT_MASK, cpu->icount_budget); cpu_neg(cpu)->icount_decr.u16.low = insns_left; cpu->icount_extra = cpu->icount_budget - insns_left; - if (!cpu->icount_extra) { - /* Execute any remaining instructions, then let the main loop - * handle the next event. - */ - if (insns_left > 0) { - cpu_exec_nocache(cpu, insns_left, tb, false); - } + + /* + * If the next tb has more instructions than we have left to + * execute we need to ensure we find/generate a TB with exactly + * insns_left instructions in it. + */ + if (!cpu->icount_extra && insns_left > 0 && insns_left < tb->icount) { + cpu->cflags_next_tb = (tb->cflags & ~CF_COUNT_MASK) | insns_left; } #endif } @@ -691,7 +727,7 @@ int cpu_exec(CPUState *cpu) rcu_read_lock(); - cc->cpu_exec_enter(cpu); + cpu_exec_enter(cpu); /* Calculate difference between guest clock and host clock. * This delay includes the delay of the last cycle, so @@ -702,18 +738,31 @@ int cpu_exec(CPUState *cpu) /* prepare setjmp context for exception handling */ if (sigsetjmp(cpu->jmp_env, 0) != 0) { -#if defined(__clang__) || !QEMU_GNUC_PREREQ(4, 6) - /* Some compilers wrongly smash all local variables after - * siglongjmp. There were bug reports for gcc 4.5.0 and clang. +#if defined(__clang__) + /* + * Some compilers wrongly smash all local variables after + * siglongjmp (the spec requires that only non-volatile locals + * which are changed between the sigsetjmp and siglongjmp are + * permitted to be trashed). There were bug reports for gcc + * 4.5.0 and clang. The bug is fixed in all versions of gcc + * that we support, but is still unfixed in clang: + * https://bugs.llvm.org/show_bug.cgi?id=21183 + * * Reload essential local variables here for those compilers. - * Newer versions of gcc would complain about this code (-Wclobbered). */ + * Newer versions of gcc would complain about this code (-Wclobbered), + * so we only perform the workaround for clang. + */ cpu = current_cpu; cc = CPU_GET_CLASS(cpu); -#else /* buggy compiler */ - /* Assert that the compiler does not smash local variables. */ +#else + /* + * Non-buggy compilers preserve these locals; assert that + * they have the correct value. + */ g_assert(cpu == current_cpu); g_assert(cc == CPU_GET_CLASS(cpu)); -#endif /* buggy compiler */ +#endif + #ifndef CONFIG_SOFTMMU tcg_debug_assert(!have_mmap_lock()); #endif @@ -740,7 +789,7 @@ int cpu_exec(CPUState *cpu) have CF_INVALID set, -1 is a convenient invalid value that does not require tcg headers for cpu_common_reset. */ if (cflags == -1) { - cflags = curr_cflags(); + cflags = curr_cflags(cpu); } else { cpu->cflags_next_tb = -1; } @@ -753,8 +802,59 @@ int cpu_exec(CPUState *cpu) } } - cc->cpu_exec_exit(cpu); + cpu_exec_exit(cpu); rcu_read_unlock(); return ret; } + +void tcg_exec_realizefn(CPUState *cpu, Error **errp) +{ + static bool tcg_target_initialized; + CPUClass *cc = CPU_GET_CLASS(cpu); + + if (!tcg_target_initialized) { + cc->tcg_ops->initialize(); + tcg_target_initialized = true; + } + tlb_init(cpu); + qemu_plugin_vcpu_init_hook(cpu); + +#ifndef CONFIG_USER_ONLY + tcg_iommu_init_notifier_list(cpu); +#endif /* !CONFIG_USER_ONLY */ +} + +/* undo the initializations in reverse order */ +void tcg_exec_unrealizefn(CPUState *cpu) +{ +#ifndef CONFIG_USER_ONLY + tcg_iommu_free_notifier_list(cpu); +#endif /* !CONFIG_USER_ONLY */ + + qemu_plugin_vcpu_exit_hook(cpu); + tlb_destroy(cpu); +} + +#ifndef CONFIG_USER_ONLY + +void dump_drift_info(void) +{ + if (!icount_enabled()) { + return; + } + + qemu_printf("Host - Guest clock %"PRIi64" ms\n", + (cpu_get_clock() - icount_get()) / SCALE_MS); + if (icount_align_option) { + qemu_printf("Max guest delay %"PRIi64" ms\n", + -max_delay / SCALE_MS); + qemu_printf("Max guest advance %"PRIi64" ms\n", + max_advance / SCALE_MS); + } else { + qemu_printf("Max guest delay NA\n"); + qemu_printf("Max guest advance NA\n"); + } +} + +#endif /* !CONFIG_USER_ONLY */ diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 92464e94bb..7b078982d0 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -20,11 +20,13 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "cpu.h" +#include "hw/core/tcg-cpu-ops.h" #include "exec/exec-all.h" #include "exec/memory.h" #include "exec/address-spaces.h" #include "exec/cpu_ldst.h" #include "exec/cputlb.h" +#include "exec/tb-hash.h" #include "exec/memory-internal.h" #include "exec/ram_addr.h" #include "tcg/tcg.h" @@ -33,9 +35,10 @@ #include "exec/helper-proto.h" #include "qemu/atomic.h" #include "qemu/atomic128.h" -#include "translate-all.h" -#include "trace-root.h" +#include "exec/translate-all.h" +#include "trace/trace-root.h" #include "trace/mem.h" +#include "internal.h" #ifdef CONFIG_PLUGIN #include "qemu/plugin-memory.h" #endif @@ -97,6 +100,23 @@ static void tlb_window_reset(CPUTLBDesc *desc, int64_t ns, desc->window_max_entries = max_entries; } +static void tb_jmp_cache_clear_page(CPUState *cpu, target_ulong page_addr) +{ + unsigned int i, i0 = tb_jmp_cache_hash_page(page_addr); + + for (i = 0; i < TB_JMP_PAGE_SIZE; i++) { + qatomic_set(&cpu->tb_jmp_cache[i0 + i], NULL); + } +} + +static void tb_flush_jmp_cache(CPUState *cpu, target_ulong addr) +{ + /* Discard jump cache entries for any tb which might potentially + overlap the flushed page. */ + tb_jmp_cache_clear_page(cpu, addr - TARGET_PAGE_SIZE); + tb_jmp_cache_clear_page(cpu, addr); +} + /** * tlb_mmu_resize_locked() - perform TLB resize bookkeeping; resize if necessary * @desc: The CPUTLBDesc portion of the TLB @@ -312,9 +332,9 @@ void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide) CPU_FOREACH(cpu) { CPUArchState *env = cpu->env_ptr; - full += atomic_read(&env_tlb(env)->c.full_flush_count); - part += atomic_read(&env_tlb(env)->c.part_flush_count); - elide += atomic_read(&env_tlb(env)->c.elide_flush_count); + full += qatomic_read(&env_tlb(env)->c.full_flush_count); + part += qatomic_read(&env_tlb(env)->c.part_flush_count); + elide += qatomic_read(&env_tlb(env)->c.elide_flush_count); } *pfull = full; *ppart = part; @@ -349,13 +369,13 @@ static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data) cpu_tb_jmp_cache_clear(cpu); if (to_clean == ALL_MMUIDX_BITS) { - atomic_set(&env_tlb(env)->c.full_flush_count, + qatomic_set(&env_tlb(env)->c.full_flush_count, env_tlb(env)->c.full_flush_count + 1); } else { - atomic_set(&env_tlb(env)->c.part_flush_count, + qatomic_set(&env_tlb(env)->c.part_flush_count, env_tlb(env)->c.part_flush_count + ctpop16(to_clean)); if (to_clean != asked) { - atomic_set(&env_tlb(env)->c.elide_flush_count, + qatomic_set(&env_tlb(env)->c.elide_flush_count, env_tlb(env)->c.elide_flush_count + ctpop16(asked & ~to_clean)); } @@ -409,12 +429,21 @@ void tlb_flush_all_cpus_synced(CPUState *src_cpu) tlb_flush_by_mmuidx_all_cpus_synced(src_cpu, ALL_MMUIDX_BITS); } +static bool tlb_hit_page_mask_anyprot(CPUTLBEntry *tlb_entry, + target_ulong page, target_ulong mask) +{ + page &= mask; + mask &= TARGET_PAGE_MASK | TLB_INVALID_MASK; + + return (page == (tlb_entry->addr_read & mask) || + page == (tlb_addr_write(tlb_entry) & mask) || + page == (tlb_entry->addr_code & mask)); +} + static inline bool tlb_hit_page_anyprot(CPUTLBEntry *tlb_entry, target_ulong page) { - return tlb_hit_page(tlb_entry->addr_read, page) || - tlb_hit_page(tlb_addr_write(tlb_entry), page) || - tlb_hit_page(tlb_entry->addr_code, page); + return tlb_hit_page_mask_anyprot(tlb_entry, page, -1); } /** @@ -427,31 +456,45 @@ static inline bool tlb_entry_is_empty(const CPUTLBEntry *te) } /* Called with tlb_c.lock held */ -static inline bool tlb_flush_entry_locked(CPUTLBEntry *tlb_entry, - target_ulong page) +static bool tlb_flush_entry_mask_locked(CPUTLBEntry *tlb_entry, + target_ulong page, + target_ulong mask) { - if (tlb_hit_page_anyprot(tlb_entry, page)) { + if (tlb_hit_page_mask_anyprot(tlb_entry, page, mask)) { memset(tlb_entry, -1, sizeof(*tlb_entry)); return true; } return false; } +static inline bool tlb_flush_entry_locked(CPUTLBEntry *tlb_entry, + target_ulong page) +{ + return tlb_flush_entry_mask_locked(tlb_entry, page, -1); +} + /* Called with tlb_c.lock held */ -static inline void tlb_flush_vtlb_page_locked(CPUArchState *env, int mmu_idx, - target_ulong page) +static void tlb_flush_vtlb_page_mask_locked(CPUArchState *env, int mmu_idx, + target_ulong page, + target_ulong mask) { CPUTLBDesc *d = &env_tlb(env)->d[mmu_idx]; int k; assert_cpu_is_self(env_cpu(env)); for (k = 0; k < CPU_VTLB_SIZE; k++) { - if (tlb_flush_entry_locked(&d->vtable[k], page)) { + if (tlb_flush_entry_mask_locked(&d->vtable[k], page, mask)) { tlb_n_used_entries_dec(env, mmu_idx); } } } +static inline void tlb_flush_vtlb_page_locked(CPUArchState *env, int mmu_idx, + target_ulong page) +{ + tlb_flush_vtlb_page_mask_locked(env, mmu_idx, page, -1); +} + static void tlb_flush_page_locked(CPUArchState *env, int midx, target_ulong page) { @@ -666,6 +709,240 @@ void tlb_flush_page_all_cpus_synced(CPUState *src, target_ulong addr) tlb_flush_page_by_mmuidx_all_cpus_synced(src, addr, ALL_MMUIDX_BITS); } +static void tlb_flush_page_bits_locked(CPUArchState *env, int midx, + target_ulong page, unsigned bits) +{ + CPUTLBDesc *d = &env_tlb(env)->d[midx]; + CPUTLBDescFast *f = &env_tlb(env)->f[midx]; + target_ulong mask = MAKE_64BIT_MASK(0, bits); + + /* + * If @bits is smaller than the tlb size, there may be multiple entries + * within the TLB; otherwise all addresses that match under @mask hit + * the same TLB entry. + * + * TODO: Perhaps allow bits to be a few bits less than the size. + * For now, just flush the entire TLB. + */ + if (mask < f->mask) { + tlb_debug("forcing full flush midx %d (" + TARGET_FMT_lx "/" TARGET_FMT_lx ")\n", + midx, page, mask); + tlb_flush_one_mmuidx_locked(env, midx, get_clock_realtime()); + return; + } + + /* Check if we need to flush due to large pages. */ + if ((page & d->large_page_mask) == d->large_page_addr) { + tlb_debug("forcing full flush midx %d (" + TARGET_FMT_lx "/" TARGET_FMT_lx ")\n", + midx, d->large_page_addr, d->large_page_mask); + tlb_flush_one_mmuidx_locked(env, midx, get_clock_realtime()); + return; + } + + if (tlb_flush_entry_mask_locked(tlb_entry(env, midx, page), page, mask)) { + tlb_n_used_entries_dec(env, midx); + } + tlb_flush_vtlb_page_mask_locked(env, midx, page, mask); +} + +typedef struct { + target_ulong addr; + uint16_t idxmap; + uint16_t bits; +} TLBFlushPageBitsByMMUIdxData; + +static void +tlb_flush_page_bits_by_mmuidx_async_0(CPUState *cpu, + TLBFlushPageBitsByMMUIdxData d) +{ + CPUArchState *env = cpu->env_ptr; + int mmu_idx; + + assert_cpu_is_self(cpu); + + tlb_debug("page addr:" TARGET_FMT_lx "/%u mmu_map:0x%x\n", + d.addr, d.bits, d.idxmap); + + qemu_spin_lock(&env_tlb(env)->c.lock); + for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { + if ((d.idxmap >> mmu_idx) & 1) { + tlb_flush_page_bits_locked(env, mmu_idx, d.addr, d.bits); + } + } + qemu_spin_unlock(&env_tlb(env)->c.lock); + + tb_flush_jmp_cache(cpu, d.addr); +} + +static bool encode_pbm_to_runon(run_on_cpu_data *out, + TLBFlushPageBitsByMMUIdxData d) +{ + /* We need 6 bits to hold to hold @bits up to 63. */ + if (d.idxmap <= MAKE_64BIT_MASK(0, TARGET_PAGE_BITS - 6)) { + *out = RUN_ON_CPU_TARGET_PTR(d.addr | (d.idxmap << 6) | d.bits); + return true; + } + return false; +} + +static TLBFlushPageBitsByMMUIdxData +decode_runon_to_pbm(run_on_cpu_data data) +{ + target_ulong addr_map_bits = (target_ulong) data.target_ptr; + return (TLBFlushPageBitsByMMUIdxData){ + .addr = addr_map_bits & TARGET_PAGE_MASK, + .idxmap = (addr_map_bits & ~TARGET_PAGE_MASK) >> 6, + .bits = addr_map_bits & 0x3f + }; +} + +static void tlb_flush_page_bits_by_mmuidx_async_1(CPUState *cpu, + run_on_cpu_data runon) +{ + tlb_flush_page_bits_by_mmuidx_async_0(cpu, decode_runon_to_pbm(runon)); +} + +static void tlb_flush_page_bits_by_mmuidx_async_2(CPUState *cpu, + run_on_cpu_data data) +{ + TLBFlushPageBitsByMMUIdxData *d = data.host_ptr; + tlb_flush_page_bits_by_mmuidx_async_0(cpu, *d); + g_free(d); +} + +void tlb_flush_page_bits_by_mmuidx(CPUState *cpu, target_ulong addr, + uint16_t idxmap, unsigned bits) +{ + TLBFlushPageBitsByMMUIdxData d; + run_on_cpu_data runon; + + /* If all bits are significant, this devolves to tlb_flush_page. */ + if (bits >= TARGET_LONG_BITS) { + tlb_flush_page_by_mmuidx(cpu, addr, idxmap); + return; + } + /* If no page bits are significant, this devolves to tlb_flush. */ + if (bits < TARGET_PAGE_BITS) { + tlb_flush_by_mmuidx(cpu, idxmap); + return; + } + + /* This should already be page aligned */ + d.addr = addr & TARGET_PAGE_MASK; + d.idxmap = idxmap; + d.bits = bits; + + if (qemu_cpu_is_self(cpu)) { + tlb_flush_page_bits_by_mmuidx_async_0(cpu, d); + } else if (encode_pbm_to_runon(&runon, d)) { + async_run_on_cpu(cpu, tlb_flush_page_bits_by_mmuidx_async_1, runon); + } else { + TLBFlushPageBitsByMMUIdxData *p + = g_new(TLBFlushPageBitsByMMUIdxData, 1); + + /* Otherwise allocate a structure, freed by the worker. */ + *p = d; + async_run_on_cpu(cpu, tlb_flush_page_bits_by_mmuidx_async_2, + RUN_ON_CPU_HOST_PTR(p)); + } +} + +void tlb_flush_page_bits_by_mmuidx_all_cpus(CPUState *src_cpu, + target_ulong addr, + uint16_t idxmap, + unsigned bits) +{ + TLBFlushPageBitsByMMUIdxData d; + run_on_cpu_data runon; + + /* If all bits are significant, this devolves to tlb_flush_page. */ + if (bits >= TARGET_LONG_BITS) { + tlb_flush_page_by_mmuidx_all_cpus(src_cpu, addr, idxmap); + return; + } + /* If no page bits are significant, this devolves to tlb_flush. */ + if (bits < TARGET_PAGE_BITS) { + tlb_flush_by_mmuidx_all_cpus(src_cpu, idxmap); + return; + } + + /* This should already be page aligned */ + d.addr = addr & TARGET_PAGE_MASK; + d.idxmap = idxmap; + d.bits = bits; + + if (encode_pbm_to_runon(&runon, d)) { + flush_all_helper(src_cpu, tlb_flush_page_bits_by_mmuidx_async_1, runon); + } else { + CPUState *dst_cpu; + TLBFlushPageBitsByMMUIdxData *p; + + /* Allocate a separate data block for each destination cpu. */ + CPU_FOREACH(dst_cpu) { + if (dst_cpu != src_cpu) { + p = g_new(TLBFlushPageBitsByMMUIdxData, 1); + *p = d; + async_run_on_cpu(dst_cpu, + tlb_flush_page_bits_by_mmuidx_async_2, + RUN_ON_CPU_HOST_PTR(p)); + } + } + } + + tlb_flush_page_bits_by_mmuidx_async_0(src_cpu, d); +} + +void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *src_cpu, + target_ulong addr, + uint16_t idxmap, + unsigned bits) +{ + TLBFlushPageBitsByMMUIdxData d; + run_on_cpu_data runon; + + /* If all bits are significant, this devolves to tlb_flush_page. */ + if (bits >= TARGET_LONG_BITS) { + tlb_flush_page_by_mmuidx_all_cpus_synced(src_cpu, addr, idxmap); + return; + } + /* If no page bits are significant, this devolves to tlb_flush. */ + if (bits < TARGET_PAGE_BITS) { + tlb_flush_by_mmuidx_all_cpus_synced(src_cpu, idxmap); + return; + } + + /* This should already be page aligned */ + d.addr = addr & TARGET_PAGE_MASK; + d.idxmap = idxmap; + d.bits = bits; + + if (encode_pbm_to_runon(&runon, d)) { + flush_all_helper(src_cpu, tlb_flush_page_bits_by_mmuidx_async_1, runon); + async_safe_run_on_cpu(src_cpu, tlb_flush_page_bits_by_mmuidx_async_1, + runon); + } else { + CPUState *dst_cpu; + TLBFlushPageBitsByMMUIdxData *p; + + /* Allocate a separate data block for each destination cpu. */ + CPU_FOREACH(dst_cpu) { + if (dst_cpu != src_cpu) { + p = g_new(TLBFlushPageBitsByMMUIdxData, 1); + *p = d; + async_run_on_cpu(dst_cpu, tlb_flush_page_bits_by_mmuidx_async_2, + RUN_ON_CPU_HOST_PTR(p)); + } + } + + p = g_new(TLBFlushPageBitsByMMUIdxData, 1); + *p = d; + async_safe_run_on_cpu(src_cpu, tlb_flush_page_bits_by_mmuidx_async_2, + RUN_ON_CPU_HOST_PTR(p)); + } +} + /* update the TLBs so that writes to code in the virtual page 'addr' can be detected */ void tlb_protect_code(ram_addr_t ram_addr) @@ -693,7 +970,7 @@ void tlb_unprotect_code(ram_addr_t ram_addr) * generated code. * * Other vCPUs might be reading their TLBs during guest execution, so we update - * te->addr_write with atomic_set. We don't need to worry about this for + * te->addr_write with qatomic_set. We don't need to worry about this for * oversized guests as MTTCG is disabled for them. * * Called with tlb_c.lock held. @@ -711,7 +988,7 @@ static void tlb_reset_dirty_range_locked(CPUTLBEntry *tlb_entry, #if TCG_OVERSIZED_GUEST tlb_entry->addr_write |= TLB_NOTDIRTY; #else - atomic_set(&tlb_entry->addr_write, + qatomic_set(&tlb_entry->addr_write, tlb_entry->addr_write | TLB_NOTDIRTY); #endif } @@ -1034,10 +1311,37 @@ static void tlb_fill(CPUState *cpu, target_ulong addr, int size, * This is not a probe, so only valid return is success; failure * should result in exception + longjmp to the cpu loop. */ - ok = cc->tlb_fill(cpu, addr, size, access_type, mmu_idx, false, retaddr); + ok = cc->tcg_ops->tlb_fill(cpu, addr, size, + access_type, mmu_idx, false, retaddr); assert(ok); } +static inline void cpu_unaligned_access(CPUState *cpu, vaddr addr, + MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + + cc->tcg_ops->do_unaligned_access(cpu, addr, access_type, mmu_idx, retaddr); +} + +static inline void cpu_transaction_failed(CPUState *cpu, hwaddr physaddr, + vaddr addr, unsigned size, + MMUAccessType access_type, + int mmu_idx, MemTxAttrs attrs, + MemTxResult response, + uintptr_t retaddr) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + + if (!cpu->ignore_memory_transaction_failures && + cc->tcg_ops->do_transaction_failed) { + cc->tcg_ops->do_transaction_failed(cpu, physaddr, addr, size, + access_type, mmu_idx, attrs, + response, retaddr); + } +} + static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, int mmu_idx, target_ulong addr, uintptr_t retaddr, MMUAccessType access_type, MemOp op) @@ -1058,7 +1362,7 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, cpu_io_recompile(cpu, retaddr); } - if (mr->global_locking && !qemu_mutex_iothread_locked()) { + if (!qemu_mutex_iothread_locked()) { qemu_mutex_lock_iothread(); locked = true; } @@ -1119,7 +1423,7 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry, */ save_iotlb_data(cpu, iotlbentry->addr, section, mr_offset); - if (mr->global_locking && !qemu_mutex_iothread_locked()) { + if (!qemu_mutex_iothread_locked()) { qemu_mutex_lock_iothread(); locked = true; } @@ -1143,8 +1447,8 @@ static inline target_ulong tlb_read_ofs(CPUTLBEntry *entry, size_t ofs) #if TCG_OVERSIZED_GUEST return *(target_ulong *)((uintptr_t)entry + ofs); #else - /* ofs might correspond to .addr_write, so use atomic_read */ - return atomic_read((target_ulong *)((uintptr_t)entry + ofs)); + /* ofs might correspond to .addr_write, so use qatomic_read */ + return qatomic_read((target_ulong *)((uintptr_t)entry + ofs)); #endif } @@ -1160,11 +1464,11 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index, CPUTLBEntry *vtlb = &env_tlb(env)->d[mmu_idx].vtable[vidx]; target_ulong cmp; - /* elt_ofs might correspond to .addr_write, so use atomic_read */ + /* elt_ofs might correspond to .addr_write, so use qatomic_read */ #if TCG_OVERSIZED_GUEST cmp = *(target_ulong *)((uintptr_t)vtlb + elt_ofs); #else - cmp = atomic_read((target_ulong *)((uintptr_t)vtlb + elt_ofs)); + cmp = qatomic_read((target_ulong *)((uintptr_t)vtlb + elt_ofs)); #endif if (cmp == page) { @@ -1305,8 +1609,8 @@ static int probe_access_internal(CPUArchState *env, target_ulong addr, CPUState *cs = env_cpu(env); CPUClass *cc = CPU_GET_CLASS(cs); - if (!cc->tlb_fill(cs, addr, fault_size, access_type, - mmu_idx, nonfault, retaddr)) { + if (!cc->tcg_ops->tlb_fill(cs, addr, fault_size, access_type, + mmu_idx, nonfault, retaddr)) { /* Non-faulting page table read failed. */ *phost = NULL; return TLB_INVALID_MASK; @@ -2023,6 +2327,97 @@ store_memop(void *haddr, uint64_t val, MemOp op) } } +static void __attribute__((noinline)) +store_helper_unaligned(CPUArchState *env, target_ulong addr, uint64_t val, + uintptr_t retaddr, size_t size, uintptr_t mmu_idx, + bool big_endian) +{ + const size_t tlb_off = offsetof(CPUTLBEntry, addr_write); + uintptr_t index, index2; + CPUTLBEntry *entry, *entry2; + target_ulong page2, tlb_addr, tlb_addr2; + TCGMemOpIdx oi; + size_t size2; + int i; + + /* + * Ensure the second page is in the TLB. Note that the first page + * is already guaranteed to be filled, and that the second page + * cannot evict the first. + */ + + // FIXME: Upstream patch for this + if ((addr & ~TARGET_PAGE_MASK) + size - 1 >= TARGET_PAGE_SIZE) { + + page2 = (addr + size) & TARGET_PAGE_MASK; + size2 = (addr + size) & ~TARGET_PAGE_MASK; + index2 = tlb_index(env, mmu_idx, page2); + entry2 = tlb_entry(env, mmu_idx, page2); + + tlb_addr2 = tlb_addr_write(entry2); + if (!tlb_hit_page(tlb_addr2, page2)) { + if (!victim_tlb_hit(env, mmu_idx, index2, tlb_off, page2)) { + tlb_fill(env_cpu(env), page2, size2, MMU_DATA_STORE, + mmu_idx, retaddr); + index2 = tlb_index(env, mmu_idx, page2); + entry2 = tlb_entry(env, mmu_idx, page2); + } + tlb_addr2 = tlb_addr_write(entry2); + } + + } else { + /* The access happens on a single page */ + size2 = 0; + } + + index = tlb_index(env, mmu_idx, addr); + entry = tlb_entry(env, mmu_idx, addr); + tlb_addr = tlb_addr_write(entry); + + /* + * Handle watchpoints. Since this may trap, all checks + * must happen before any store. + */ + if (unlikely(tlb_addr & TLB_WATCHPOINT)) { +#ifdef XBOX + mem_check_access_callback_vaddr(env_cpu(env), addr, size - size2, + BP_MEM_WRITE, &env_tlb(env)->d[mmu_idx].iotlb[index]); +#endif + cpu_check_watchpoint(env_cpu(env), addr, size - size2, + env_tlb(env)->d[mmu_idx].iotlb[index].attrs, + BP_MEM_WRITE, retaddr); + } + if (size2 > 0 && unlikely(tlb_addr2 & TLB_WATCHPOINT)) { +#ifdef XBOX + mem_check_access_callback_vaddr(env_cpu(env), page2, size2, + BP_MEM_WRITE, &env_tlb(env)->d[mmu_idx].iotlb[index2]); +#endif + cpu_check_watchpoint(env_cpu(env), page2, size2, + env_tlb(env)->d[mmu_idx].iotlb[index2].attrs, + BP_MEM_WRITE, retaddr); + } + + /* + * XXX: not efficient, but simple. + * This loop must go in the forward direction to avoid issues + * with self-modifying code in Windows 64-bit. + */ + oi = make_memop_idx(MO_UB, mmu_idx); + if (big_endian) { + for (i = 0; i < size; ++i) { + /* Big-endian extract. */ + uint8_t val8 = val >> (((size - 1) * 8) - (i * 8)); + helper_ret_stb_mmu(env, addr + i, val8, oi, retaddr); + } + } else { + for (i = 0; i < size; ++i) { + /* Little-endian extract. */ + uint8_t val8 = val >> (i * 8); + helper_ret_stb_mmu(env, addr + i, val8, oi, retaddr); + } + } +} + static inline void QEMU_ALWAYS_INLINE store_helper(CPUArchState *env, target_ulong addr, uint64_t val, TCGMemOpIdx oi, uintptr_t retaddr, MemOp op) @@ -2116,79 +2511,9 @@ store_helper(CPUArchState *env, target_ulong addr, uint64_t val, if (size > 1 && unlikely((addr & ~TARGET_PAGE_MASK) + size - 1 >= TARGET_PAGE_SIZE)) { - int i; - uintptr_t index2; - CPUTLBEntry *entry2; - target_ulong page2, tlb_addr2; - size_t size2; - do_unaligned_access: - /* - * Ensure the second page is in the TLB. Note that the first page - * is already guaranteed to be filled, and that the second page - * cannot evict the first. - */ - - // FIXME: Upstream patch for this - if ((addr & ~TARGET_PAGE_MASK) + size - 1 >= TARGET_PAGE_SIZE) { - page2 = (addr + size) & TARGET_PAGE_MASK; - size2 = (addr + size) & ~TARGET_PAGE_MASK; - index2 = tlb_index(env, mmu_idx, page2); - entry2 = tlb_entry(env, mmu_idx, page2); - tlb_addr2 = tlb_addr_write(entry2); - if (!tlb_hit_page(tlb_addr2, page2)) { - if (!victim_tlb_hit(env, mmu_idx, index2, tlb_off, page2)) { - tlb_fill(env_cpu(env), page2, size2, MMU_DATA_STORE, - mmu_idx, retaddr); - index2 = tlb_index(env, mmu_idx, page2); - entry2 = tlb_entry(env, mmu_idx, page2); - } - tlb_addr2 = tlb_addr_write(entry2); - } - } else { - /* The access happens on a single page */ - size2 = 0; - } - - /* - * Handle watchpoints. Since this may trap, all checks - * must happen before any store. - */ - if (unlikely(tlb_addr & TLB_WATCHPOINT)) { -#ifdef XBOX - mem_check_access_callback_vaddr(env_cpu(env), addr, size - size2, - BP_MEM_WRITE, &env_tlb(env)->d[mmu_idx].iotlb[index]); -#endif - cpu_check_watchpoint(env_cpu(env), addr, size - size2, - env_tlb(env)->d[mmu_idx].iotlb[index].attrs, - BP_MEM_WRITE, retaddr); - } - if (size2 > 0 && unlikely(tlb_addr2 & TLB_WATCHPOINT)) { -#ifdef XBOX - mem_check_access_callback_vaddr(env_cpu(env), page2, size2, - BP_MEM_WRITE, &env_tlb(env)->d[mmu_idx].iotlb[index2]); -#endif - cpu_check_watchpoint(env_cpu(env), page2, size2, - env_tlb(env)->d[mmu_idx].iotlb[index2].attrs, - BP_MEM_WRITE, retaddr); - } - - /* - * XXX: not efficient, but simple. - * This loop must go in the forward direction to avoid issues - * with self-modifying code in Windows 64-bit. - */ - for (i = 0; i < size; ++i) { - uint8_t val8; - if (memop_big_endian(op)) { - /* Big-endian extract. */ - val8 = val >> (((size - 1) * 8) - (i * 8)); - } else { - /* Little-endian extract. */ - val8 = val >> (i * 8); - } - helper_ret_stb_mmu(env, addr + i, val8, oi, retaddr); - } + store_helper_unaligned(env, addr, val, retaddr, size, + mmu_idx, memop_big_endian(op)); return; } @@ -2196,8 +2521,9 @@ store_helper(CPUArchState *env, target_ulong addr, uint64_t val, store_memop(haddr, val, op); } -void helper_ret_stb_mmu(CPUArchState *env, target_ulong addr, uint8_t val, - TCGMemOpIdx oi, uintptr_t retaddr) +void __attribute__((noinline)) +helper_ret_stb_mmu(CPUArchState *env, target_ulong addr, uint8_t val, + TCGMemOpIdx oi, uintptr_t retaddr) { store_helper(env, addr, val, oi, retaddr, MO_UB); } @@ -2388,7 +2714,7 @@ void cpu_stq_le_data(CPUArchState *env, target_ulong ptr, uint64_t val) #define ATOMIC_MMU_CLEANUP #define ATOMIC_MMU_IDX get_mmuidx(oi) -#include "atomic_common.inc.c" +#include "atomic_common.c.inc" #define DATA_SIZE 1 #include "atomic_template.h" diff --git a/accel/tcg/internal.h b/accel/tcg/internal.h new file mode 100644 index 0000000000..e9c145e0fb --- /dev/null +++ b/accel/tcg/internal.h @@ -0,0 +1,20 @@ +/* + * Internal execution defines for qemu + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef ACCEL_TCG_INTERNAL_H +#define ACCEL_TCG_INTERNAL_H + +#include "exec/exec-all.h" + +TranslationBlock *tb_gen_code(CPUState *cpu, target_ulong pc, + target_ulong cs_base, uint32_t flags, + int cflags); + +void QEMU_NORETURN cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); + +#endif /* ACCEL_TCG_INTERNAL_H */ diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build new file mode 100644 index 0000000000..1236ac7b91 --- /dev/null +++ b/accel/tcg/meson.build @@ -0,0 +1,22 @@ +tcg_ss = ss.source_set() +tcg_ss.add(files( + 'tcg-all.c', + 'cpu-exec-common.c', + 'cpu-exec.c', + 'tcg-runtime-gvec.c', + 'tcg-runtime.c', + 'translate-all.c', + 'translator.c', +)) +tcg_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-exec.c')) +tcg_ss.add(when: 'CONFIG_SOFTMMU', if_false: files('user-exec-stub.c')) +tcg_ss.add(when: 'CONFIG_PLUGIN', if_true: [files('plugin-gen.c'), libdl]) +specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_ss) + +specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: files( + 'cputlb.c', + 'tcg-accel-ops.c', + 'tcg-accel-ops-mttcg.c', + 'tcg-accel-ops-icount.c', + 'tcg-accel-ops-rr.c' +)) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 51580d51a0..c3dc3effe7 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -284,8 +284,8 @@ static TCGOp *copy_extu_i32_i64(TCGOp **begin_op, TCGOp *op) if (TCG_TARGET_REG_BITS == 32) { /* mov_i32 */ op = copy_op(begin_op, op, INDEX_op_mov_i32); - /* movi_i32 */ - op = copy_op(begin_op, op, INDEX_op_movi_i32); + /* mov_i32 w/ $0 */ + op = copy_op(begin_op, op, INDEX_op_mov_i32); } else { /* extu_i32_i64 */ op = copy_op(begin_op, op, INDEX_op_extu_i32_i64); @@ -306,41 +306,20 @@ static TCGOp *copy_mov_i64(TCGOp **begin_op, TCGOp *op) return op; } -static TCGOp *copy_movi_i64(TCGOp **begin_op, TCGOp *op, uint64_t v) -{ - if (TCG_TARGET_REG_BITS == 32) { - /* 2x movi_i32 */ - op = copy_op(begin_op, op, INDEX_op_movi_i32); - op->args[1] = v; - - op = copy_op(begin_op, op, INDEX_op_movi_i32); - op->args[1] = v >> 32; - } else { - /* movi_i64 */ - op = copy_op(begin_op, op, INDEX_op_movi_i64); - op->args[1] = v; - } - return op; -} - static TCGOp *copy_const_ptr(TCGOp **begin_op, TCGOp *op, void *ptr) { if (UINTPTR_MAX == UINT32_MAX) { - /* movi_i32 */ - op = copy_op(begin_op, op, INDEX_op_movi_i32); - op->args[1] = (uintptr_t)ptr; + /* mov_i32 */ + op = copy_op(begin_op, op, INDEX_op_mov_i32); + op->args[1] = tcgv_i32_arg(tcg_constant_i32((uintptr_t)ptr)); } else { - /* movi_i64 */ - op = copy_movi_i64(begin_op, op, (uint64_t)(uintptr_t)ptr); + /* mov_i64 */ + op = copy_op(begin_op, op, INDEX_op_mov_i64); + op->args[1] = tcgv_i64_arg(tcg_constant_i64((uintptr_t)ptr)); } return op; } -static TCGOp *copy_const_i64(TCGOp **begin_op, TCGOp *op, uint64_t v) -{ - return copy_movi_i64(begin_op, op, v); -} - static TCGOp *copy_extu_tl_i64(TCGOp **begin_op, TCGOp *op) { if (TARGET_LONG_BITS == 32) { @@ -379,14 +358,17 @@ static TCGOp *copy_st_i64(TCGOp **begin_op, TCGOp *op) return op; } -static TCGOp *copy_add_i64(TCGOp **begin_op, TCGOp *op) +static TCGOp *copy_add_i64(TCGOp **begin_op, TCGOp *op, uint64_t v) { if (TCG_TARGET_REG_BITS == 32) { /* all 32-bit backends must implement add2_i32 */ g_assert(TCG_TARGET_HAS_add2_i32); op = copy_op(begin_op, op, INDEX_op_add2_i32); + op->args[4] = tcgv_i32_arg(tcg_constant_i32(v)); + op->args[5] = tcgv_i32_arg(tcg_constant_i32(v >> 32)); } else { op = copy_op(begin_op, op, INDEX_op_add_i64); + op->args[2] = tcgv_i64_arg(tcg_constant_i64(v)); } return op; } @@ -436,6 +418,12 @@ static TCGOp *copy_call(TCGOp **begin_op, TCGOp *op, void *empty_func, return op; } +/* + * When we append/replace ops here we are sensitive to changing patterns of + * TCGOps generated by the tcg_gen_FOO calls when we generated the + * empty callbacks. This will assert very quickly in a debug build as + * we assert the ops we are replacing are the correct ones. + */ static TCGOp *append_udata_cb(const struct qemu_plugin_dyn_cb *cb, TCGOp *begin_op, TCGOp *op, int *cb_idx) { @@ -467,11 +455,8 @@ static TCGOp *append_inline_cb(const struct qemu_plugin_dyn_cb *cb, /* ld_i64 */ op = copy_ld_i64(&begin_op, op); - /* const_i64 */ - op = copy_const_i64(&begin_op, op, cb->inline_insn.imm); - /* add_i64 */ - op = copy_add_i64(&begin_op, op); + op = copy_add_i64(&begin_op, op, cb->inline_insn.imm); /* st_i64 */ op = copy_st_i64(&begin_op, op); @@ -486,8 +471,8 @@ static TCGOp *append_mem_cb(const struct qemu_plugin_dyn_cb *cb, tcg_debug_assert(type == PLUGIN_GEN_CB_MEM); - /* const_i32 == movi_i32 ("info", so it remains as is) */ - op = copy_op(&begin_op, op, INDEX_op_movi_i32); + /* const_i32 == mov_i32 ("info", so it remains as is) */ + op = copy_op(&begin_op, op, INDEX_op_mov_i32); /* const_ptr */ op = copy_const_ptr(&begin_op, op, cb->userp); @@ -857,7 +842,7 @@ static void plugin_gen_inject(const struct qemu_plugin_tb *plugin_tb) pr_ops(); } -bool plugin_gen_tb_start(CPUState *cpu, const TranslationBlock *tb) +bool plugin_gen_tb_start(CPUState *cpu, const TranslationBlock *tb, bool mem_only) { struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb; bool ret = false; @@ -870,6 +855,7 @@ bool plugin_gen_tb_start(CPUState *cpu, const TranslationBlock *tb) ptb->vaddr2 = -1; get_page_addr_code_hostp(cpu->env_ptr, tb->pc, &ptb->haddr1); ptb->haddr2 = NULL; + ptb->mem_only = mem_only; plugin_gen_empty_callback(PLUGIN_GEN_FROM_TB); } diff --git a/accel/tcg/tcg-accel-ops-icount.c b/accel/tcg/tcg-accel-ops-icount.c new file mode 100644 index 0000000000..13b8fbeb69 --- /dev/null +++ b/accel/tcg/tcg-accel-ops-icount.c @@ -0,0 +1,144 @@ +/* + * QEMU TCG Single Threaded vCPUs implementation using instruction counting + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "sysemu/tcg.h" +#include "sysemu/replay.h" +#include "qemu/main-loop.h" +#include "qemu/guest-random.h" +#include "exec/exec-all.h" +#include "hw/boards.h" + +#include "tcg-accel-ops.h" +#include "tcg-accel-ops-icount.h" +#include "tcg-accel-ops-rr.h" + +static int64_t icount_get_limit(void) +{ + int64_t deadline; + + if (replay_mode != REPLAY_MODE_PLAY) { + /* + * Include all the timers, because they may need an attention. + * Too long CPU execution may create unnecessary delay in UI. + */ + deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, + QEMU_TIMER_ATTR_ALL); + /* Check realtime timers, because they help with input processing */ + deadline = qemu_soonest_timeout(deadline, + qemu_clock_deadline_ns_all(QEMU_CLOCK_REALTIME, + QEMU_TIMER_ATTR_ALL)); + + /* + * Maintain prior (possibly buggy) behaviour where if no deadline + * was set (as there is no QEMU_CLOCK_VIRTUAL timer) or it is more than + * INT32_MAX nanoseconds ahead, we still use INT32_MAX + * nanoseconds. + */ + if ((deadline < 0) || (deadline > INT32_MAX)) { + deadline = INT32_MAX; + } + + return icount_round(deadline); + } else { + return replay_get_instructions(); + } +} + +static void icount_notify_aio_contexts(void) +{ + /* Wake up other AioContexts. */ + qemu_clock_notify(QEMU_CLOCK_VIRTUAL); + qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL); +} + +void icount_handle_deadline(void) +{ + assert(qemu_in_vcpu_thread()); + int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, + QEMU_TIMER_ATTR_ALL); + + /* + * Instructions, interrupts, and exceptions are processed in cpu-exec. + * Don't interrupt cpu thread, when these events are waiting + * (i.e., there is no checkpoint) + */ + if (deadline == 0 + && (replay_mode != REPLAY_MODE_PLAY || replay_has_checkpoint())) { + icount_notify_aio_contexts(); + } +} + +void icount_prepare_for_run(CPUState *cpu) +{ + int insns_left; + + /* + * These should always be cleared by icount_process_data after + * each vCPU execution. However u16.high can be raised + * asynchronously by cpu_exit/cpu_interrupt/tcg_handle_interrupt + */ + g_assert(cpu_neg(cpu)->icount_decr.u16.low == 0); + g_assert(cpu->icount_extra == 0); + + cpu->icount_budget = icount_get_limit(); + insns_left = MIN(0xffff, cpu->icount_budget); + cpu_neg(cpu)->icount_decr.u16.low = insns_left; + cpu->icount_extra = cpu->icount_budget - insns_left; + + replay_mutex_lock(); + + if (cpu->icount_budget == 0 && replay_has_checkpoint()) { + icount_notify_aio_contexts(); + } +} + +void icount_process_data(CPUState *cpu) +{ + /* Account for executed instructions */ + icount_update(cpu); + + /* Reset the counters */ + cpu_neg(cpu)->icount_decr.u16.low = 0; + cpu->icount_extra = 0; + cpu->icount_budget = 0; + + replay_account_executed_instructions(); + + replay_mutex_unlock(); +} + +void icount_handle_interrupt(CPUState *cpu, int mask) +{ + int old_mask = cpu->interrupt_request; + + tcg_handle_interrupt(cpu, mask); + if (qemu_cpu_is_self(cpu) && + !cpu->can_do_io + && (mask & ~old_mask) != 0) { + cpu_abort(cpu, "Raised interrupt while not in I/O function"); + } +} diff --git a/accel/tcg/tcg-accel-ops-icount.h b/accel/tcg/tcg-accel-ops-icount.h new file mode 100644 index 0000000000..d884aa2aaa --- /dev/null +++ b/accel/tcg/tcg-accel-ops-icount.h @@ -0,0 +1,19 @@ +/* + * QEMU TCG Single Threaded vCPUs implementation using instruction counting + * + * Copyright 2020 SUSE LLC + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef TCG_CPUS_ICOUNT_H +#define TCG_CPUS_ICOUNT_H + +void icount_handle_deadline(void); +void icount_prepare_for_run(CPUState *cpu); +void icount_process_data(CPUState *cpu); + +void icount_handle_interrupt(CPUState *cpu, int mask); + +#endif /* TCG_CPUS_ICOUNT_H */ diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c new file mode 100644 index 0000000000..847d2079d2 --- /dev/null +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -0,0 +1,133 @@ +/* + * QEMU TCG Multi Threaded vCPUs implementation + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "sysemu/tcg.h" +#include "sysemu/replay.h" +#include "qemu/main-loop.h" +#include "qemu/guest-random.h" +#include "exec/exec-all.h" +#include "hw/boards.h" + +#include "tcg-accel-ops.h" +#include "tcg-accel-ops-mttcg.h" + +/* + * In the multi-threaded case each vCPU has its own thread. The TLS + * variable current_cpu can be used deep in the code to find the + * current CPUState for a given thread. + */ + +static void *mttcg_cpu_thread_fn(void *arg) +{ + CPUState *cpu = arg; + + assert(tcg_enabled()); + g_assert(!icount_enabled()); + + rcu_register_thread(); + tcg_register_thread(); + + qemu_mutex_lock_iothread(); + qemu_thread_get_self(cpu->thread); + + cpu->thread_id = qemu_get_thread_id(); + cpu->can_do_io = 1; + current_cpu = cpu; + cpu_thread_signal_created(cpu); + qemu_guest_random_seed_thread_part2(cpu->random_seed); + + /* process any pending work */ + cpu->exit_request = 1; + + do { + if (cpu_can_run(cpu)) { + int r; + qemu_mutex_unlock_iothread(); + r = tcg_cpus_exec(cpu); + qemu_mutex_lock_iothread(); + switch (r) { + case EXCP_DEBUG: + cpu_handle_guest_debug(cpu); + break; + case EXCP_HALTED: + /* + * during start-up the vCPU is reset and the thread is + * kicked several times. If we don't ensure we go back + * to sleep in the halted state we won't cleanly + * start-up when the vCPU is enabled. + * + * cpu->halted should ensure we sleep in wait_io_event + */ + g_assert(cpu->halted); + break; + case EXCP_ATOMIC: + qemu_mutex_unlock_iothread(); + cpu_exec_step_atomic(cpu); + qemu_mutex_lock_iothread(); + default: + /* Ignore everything else? */ + break; + } + } + + qatomic_mb_set(&cpu->exit_request, 0); + qemu_wait_io_event(cpu); + } while (!cpu->unplug || cpu_can_run(cpu)); + + tcg_cpus_destroy(cpu); + qemu_mutex_unlock_iothread(); + rcu_unregister_thread(); + return NULL; +} + +void mttcg_kick_vcpu_thread(CPUState *cpu) +{ + cpu_exit(cpu); +} + +void mttcg_start_vcpu_thread(CPUState *cpu) +{ + char thread_name[VCPU_THREAD_NAME_SIZE]; + + g_assert(tcg_enabled()); + tcg_cpu_init_cflags(cpu, current_machine->smp.max_cpus > 1); + + cpu->thread = g_malloc0(sizeof(QemuThread)); + cpu->halt_cond = g_malloc0(sizeof(QemuCond)); + qemu_cond_init(cpu->halt_cond); + + /* create a thread per vCPU with TCG (MTTCG) */ + snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/TCG", + cpu->cpu_index); + + qemu_thread_create(cpu->thread, thread_name, mttcg_cpu_thread_fn, + cpu, QEMU_THREAD_JOINABLE); + +#ifdef _WIN32 + cpu->hThread = qemu_thread_get_handle(cpu->thread); +#endif +} diff --git a/accel/tcg/tcg-accel-ops-mttcg.h b/accel/tcg/tcg-accel-ops-mttcg.h new file mode 100644 index 0000000000..9fdc5a2ab5 --- /dev/null +++ b/accel/tcg/tcg-accel-ops-mttcg.h @@ -0,0 +1,19 @@ +/* + * QEMU TCG Multi Threaded vCPUs implementation + * + * Copyright 2021 SUSE LLC + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef TCG_CPUS_MTTCG_H +#define TCG_CPUS_MTTCG_H + +/* kick MTTCG vCPU thread */ +void mttcg_kick_vcpu_thread(CPUState *cpu); + +/* start an mttcg vCPU thread */ +void mttcg_start_vcpu_thread(CPUState *cpu); + +#endif /* TCG_CPUS_MTTCG_H */ diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c new file mode 100644 index 0000000000..018b54c508 --- /dev/null +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -0,0 +1,298 @@ +/* + * QEMU TCG Single Threaded vCPUs implementation + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "sysemu/tcg.h" +#include "sysemu/replay.h" +#include "qemu/main-loop.h" +#include "qemu/guest-random.h" +#include "exec/exec-all.h" +#include "hw/boards.h" + +#include "tcg-accel-ops.h" +#include "tcg-accel-ops-rr.h" +#include "tcg-accel-ops-icount.h" + +/* Kick all RR vCPUs */ +void rr_kick_vcpu_thread(CPUState *unused) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + cpu_exit(cpu); + }; +} + +/* + * TCG vCPU kick timer + * + * The kick timer is responsible for moving single threaded vCPU + * emulation on to the next vCPU. If more than one vCPU is running a + * timer event with force a cpu->exit so the next vCPU can get + * scheduled. + * + * The timer is removed if all vCPUs are idle and restarted again once + * idleness is complete. + */ + +static QEMUTimer *rr_kick_vcpu_timer; +static CPUState *rr_current_cpu; + +#define TCG_KICK_PERIOD (NANOSECONDS_PER_SECOND / 10) + +static inline int64_t rr_next_kick_time(void) +{ + return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + TCG_KICK_PERIOD; +} + +/* Kick the currently round-robin scheduled vCPU to next */ +static void rr_kick_next_cpu(void) +{ + CPUState *cpu; + do { + cpu = qatomic_mb_read(&rr_current_cpu); + if (cpu) { + cpu_exit(cpu); + } + } while (cpu != qatomic_mb_read(&rr_current_cpu)); +} + +static void rr_kick_thread(void *opaque) +{ + timer_mod(rr_kick_vcpu_timer, rr_next_kick_time()); + rr_kick_next_cpu(); +} + +static void rr_start_kick_timer(void) +{ + if (!rr_kick_vcpu_timer && CPU_NEXT(first_cpu)) { + rr_kick_vcpu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + rr_kick_thread, NULL); + } + if (rr_kick_vcpu_timer && !timer_pending(rr_kick_vcpu_timer)) { + timer_mod(rr_kick_vcpu_timer, rr_next_kick_time()); + } +} + +static void rr_stop_kick_timer(void) +{ + if (rr_kick_vcpu_timer && timer_pending(rr_kick_vcpu_timer)) { + timer_del(rr_kick_vcpu_timer); + } +} + +static void rr_wait_io_event(void) +{ + CPUState *cpu; + + while (all_cpu_threads_idle()) { + rr_stop_kick_timer(); + qemu_cond_wait_iothread(first_cpu->halt_cond); + } + + rr_start_kick_timer(); + + CPU_FOREACH(cpu) { + qemu_wait_io_event_common(cpu); + } +} + +/* + * Destroy any remaining vCPUs which have been unplugged and have + * finished running + */ +static void rr_deal_with_unplugged_cpus(void) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + if (cpu->unplug && !cpu_can_run(cpu)) { + tcg_cpus_destroy(cpu); + break; + } + } +} + +/* + * In the single-threaded case each vCPU is simulated in turn. If + * there is more than a single vCPU we create a simple timer to kick + * the vCPU and ensure we don't get stuck in a tight loop in one vCPU. + * This is done explicitly rather than relying on side-effects + * elsewhere. + */ + +static void *rr_cpu_thread_fn(void *arg) +{ + CPUState *cpu = arg; + + assert(tcg_enabled()); + rcu_register_thread(); + tcg_register_thread(); + + qemu_mutex_lock_iothread(); + qemu_thread_get_self(cpu->thread); + + cpu->thread_id = qemu_get_thread_id(); + cpu->can_do_io = 1; + cpu_thread_signal_created(cpu); + qemu_guest_random_seed_thread_part2(cpu->random_seed); + + /* wait for initial kick-off after machine start */ + while (first_cpu->stopped) { + qemu_cond_wait_iothread(first_cpu->halt_cond); + + /* process any pending work */ + CPU_FOREACH(cpu) { + current_cpu = cpu; + qemu_wait_io_event_common(cpu); + } + } + + rr_start_kick_timer(); + + cpu = first_cpu; + + /* process any pending work */ + cpu->exit_request = 1; + + while (1) { + qemu_mutex_unlock_iothread(); + replay_mutex_lock(); + qemu_mutex_lock_iothread(); + + if (icount_enabled()) { + /* Account partial waits to QEMU_CLOCK_VIRTUAL. */ + icount_account_warp_timer(); + /* + * Run the timers here. This is much more efficient than + * waking up the I/O thread and waiting for completion. + */ + icount_handle_deadline(); + } + + replay_mutex_unlock(); + + if (!cpu) { + cpu = first_cpu; + } + + while (cpu && cpu_work_list_empty(cpu) && !cpu->exit_request) { + + qatomic_mb_set(&rr_current_cpu, cpu); + current_cpu = cpu; + + qemu_clock_enable(QEMU_CLOCK_VIRTUAL, + (cpu->singlestep_enabled & SSTEP_NOTIMER) == 0); + + if (cpu_can_run(cpu)) { + int r; + + qemu_mutex_unlock_iothread(); + if (icount_enabled()) { + icount_prepare_for_run(cpu); + } + r = tcg_cpus_exec(cpu); + if (icount_enabled()) { + icount_process_data(cpu); + } + qemu_mutex_lock_iothread(); + + if (r == EXCP_DEBUG) { + cpu_handle_guest_debug(cpu); + break; + } else if (r == EXCP_ATOMIC) { + qemu_mutex_unlock_iothread(); + cpu_exec_step_atomic(cpu); + qemu_mutex_lock_iothread(); + break; + } + } else if (cpu->stop) { + if (cpu->unplug) { + cpu = CPU_NEXT(cpu); + } + break; + } + + cpu = CPU_NEXT(cpu); + } /* while (cpu && !cpu->exit_request).. */ + + /* Does not need qatomic_mb_set because a spurious wakeup is okay. */ + qatomic_set(&rr_current_cpu, NULL); + + if (cpu && cpu->exit_request) { + qatomic_mb_set(&cpu->exit_request, 0); + } + + if (icount_enabled() && all_cpu_threads_idle()) { + /* + * When all cpus are sleeping (e.g in WFI), to avoid a deadlock + * in the main_loop, wake it up in order to start the warp timer. + */ + qemu_notify_event(); + } + + rr_wait_io_event(); + rr_deal_with_unplugged_cpus(); + } + + rcu_unregister_thread(); + return NULL; +} + +void rr_start_vcpu_thread(CPUState *cpu) +{ + char thread_name[VCPU_THREAD_NAME_SIZE]; + static QemuCond *single_tcg_halt_cond; + static QemuThread *single_tcg_cpu_thread; + + g_assert(tcg_enabled()); + tcg_cpu_init_cflags(cpu, false); + + if (!single_tcg_cpu_thread) { + cpu->thread = g_malloc0(sizeof(QemuThread)); + cpu->halt_cond = g_malloc0(sizeof(QemuCond)); + qemu_cond_init(cpu->halt_cond); + + /* share a single thread for all cpus with TCG */ + snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "ALL CPUs/TCG"); + qemu_thread_create(cpu->thread, thread_name, + rr_cpu_thread_fn, + cpu, QEMU_THREAD_JOINABLE); + + single_tcg_halt_cond = cpu->halt_cond; + single_tcg_cpu_thread = cpu->thread; +#ifdef _WIN32 + cpu->hThread = qemu_thread_get_handle(cpu->thread); +#endif + } else { + /* we share the thread */ + cpu->thread = single_tcg_cpu_thread; + cpu->halt_cond = single_tcg_halt_cond; + cpu->thread_id = first_cpu->thread_id; + cpu->can_do_io = 1; + cpu->created = true; + } +} diff --git a/accel/tcg/tcg-accel-ops-rr.h b/accel/tcg/tcg-accel-ops-rr.h new file mode 100644 index 0000000000..54f6ae6e86 --- /dev/null +++ b/accel/tcg/tcg-accel-ops-rr.h @@ -0,0 +1,21 @@ +/* + * QEMU TCG Single Threaded vCPUs implementation + * + * Copyright 2020 SUSE LLC + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef TCG_CPUS_RR_H +#define TCG_CPUS_RR_H + +#define TCG_KICK_PERIOD (NANOSECONDS_PER_SECOND / 10) + +/* Kick all RR vCPUs. */ +void rr_kick_vcpu_thread(CPUState *unused); + +/* start the round robin vcpu thread */ +void rr_start_vcpu_thread(CPUState *cpu); + +#endif /* TCG_CPUS_RR_H */ diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c new file mode 100644 index 0000000000..6cdcaa2855 --- /dev/null +++ b/accel/tcg/tcg-accel-ops.c @@ -0,0 +1,133 @@ +/* + * QEMU TCG vCPU common functionality + * + * Functionality common to all TCG vCPU variants: mttcg, rr and icount. + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "sysemu/tcg.h" +#include "sysemu/replay.h" +#include "qemu/main-loop.h" +#include "qemu/guest-random.h" +#include "exec/exec-all.h" +#include "hw/boards.h" + +#include "tcg-accel-ops.h" +#include "tcg-accel-ops-mttcg.h" +#include "tcg-accel-ops-rr.h" +#include "tcg-accel-ops-icount.h" + +/* common functionality among all TCG variants */ + +void tcg_cpu_init_cflags(CPUState *cpu, bool parallel) +{ + uint32_t cflags = cpu->cluster_index << CF_CLUSTER_SHIFT; + cflags |= parallel ? CF_PARALLEL : 0; + cflags |= icount_enabled() ? CF_USE_ICOUNT : 0; + cpu->tcg_cflags = cflags; +} + +void tcg_cpus_destroy(CPUState *cpu) +{ + cpu_thread_signal_destroyed(cpu); +} + +int tcg_cpus_exec(CPUState *cpu) +{ + int ret; +#ifdef CONFIG_PROFILER + int64_t ti; +#endif + assert(tcg_enabled()); +#ifdef CONFIG_PROFILER + ti = profile_getclock(); +#endif + cpu_exec_start(cpu); + ret = cpu_exec(cpu); + cpu_exec_end(cpu); +#ifdef CONFIG_PROFILER + qatomic_set(&tcg_ctx->prof.cpu_exec_time, + tcg_ctx->prof.cpu_exec_time + profile_getclock() - ti); +#endif + return ret; +} + +/* mask must never be zero, except for A20 change call */ +void tcg_handle_interrupt(CPUState *cpu, int mask) +{ + g_assert(qemu_mutex_iothread_locked()); + + cpu->interrupt_request |= mask; + + /* + * If called from iothread context, wake the target cpu in + * case its halted. + */ + if (!qemu_cpu_is_self(cpu)) { + qemu_cpu_kick(cpu); + } else { + qatomic_set(&cpu_neg(cpu)->icount_decr.u16.high, -1); + } +} + +static void tcg_accel_ops_init(AccelOpsClass *ops) +{ + if (qemu_tcg_mttcg_enabled()) { + ops->create_vcpu_thread = mttcg_start_vcpu_thread; + ops->kick_vcpu_thread = mttcg_kick_vcpu_thread; + ops->handle_interrupt = tcg_handle_interrupt; + } else if (icount_enabled()) { + ops->create_vcpu_thread = rr_start_vcpu_thread; + ops->kick_vcpu_thread = rr_kick_vcpu_thread; + ops->handle_interrupt = icount_handle_interrupt; + ops->get_virtual_clock = icount_get; + ops->get_elapsed_ticks = icount_get; + } else { + ops->create_vcpu_thread = rr_start_vcpu_thread; + ops->kick_vcpu_thread = rr_kick_vcpu_thread; + ops->handle_interrupt = tcg_handle_interrupt; + } +} + +static void tcg_accel_ops_class_init(ObjectClass *oc, void *data) +{ + AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); + + ops->ops_init = tcg_accel_ops_init; +} + +static const TypeInfo tcg_accel_ops_type = { + .name = ACCEL_OPS_NAME("tcg"), + + .parent = TYPE_ACCEL_OPS, + .class_init = tcg_accel_ops_class_init, + .abstract = true, +}; + +static void tcg_accel_ops_register_types(void) +{ + type_register_static(&tcg_accel_ops_type); +} +type_init(tcg_accel_ops_register_types); diff --git a/accel/tcg/tcg-accel-ops.h b/accel/tcg/tcg-accel-ops.h new file mode 100644 index 0000000000..6a5fcef889 --- /dev/null +++ b/accel/tcg/tcg-accel-ops.h @@ -0,0 +1,22 @@ +/* + * QEMU TCG vCPU common functionality + * + * Functionality common to all TCG vcpu variants: mttcg, rr and icount. + * + * Copyright 2020 SUSE LLC + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef TCG_CPUS_H +#define TCG_CPUS_H + +#include "sysemu/cpus.h" + +void tcg_cpus_destroy(CPUState *cpu); +int tcg_cpus_exec(CPUState *cpu); +void tcg_handle_interrupt(CPUState *cpu, int mask); +void tcg_cpu_init_cflags(CPUState *cpu, bool parallel); + +#endif /* TCG_CPUS_H */ diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index eace2c113b..e378c2db73 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -24,54 +24,28 @@ */ #include "qemu/osdep.h" -#include "sysemu/accel.h" +#include "qemu-common.h" #include "sysemu/tcg.h" -#include "qom/object.h" -#include "cpu.h" -#include "sysemu/cpus.h" -#include "qemu/main-loop.h" +#include "sysemu/cpu-timers.h" #include "tcg/tcg.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "hw/boards.h" +#include "qemu/accel.h" #include "qapi/qapi-builtin-visit.h" -typedef struct TCGState { +struct TCGState { AccelState parent_obj; bool mttcg_enabled; + int splitwx_enabled; unsigned long tb_size; -} TCGState; +}; +typedef struct TCGState TCGState; #define TYPE_TCG_ACCEL ACCEL_CLASS_NAME("tcg") -#define TCG_STATE(obj) \ - OBJECT_CHECK(TCGState, (obj), TYPE_TCG_ACCEL) - -/* mask must never be zero, except for A20 change call */ -static void tcg_handle_interrupt(CPUState *cpu, int mask) -{ - int old_mask; - g_assert(qemu_mutex_iothread_locked()); - - old_mask = cpu->interrupt_request; - cpu->interrupt_request |= mask; - - /* - * If called from iothread context, wake the target cpu in - * case its halted. - */ - if (!qemu_cpu_is_self(cpu)) { - qemu_cpu_kick(cpu); - } else { - atomic_set(&cpu_neg(cpu)->icount_decr.u16.high, -1); - if (use_icount && - !cpu->can_do_io - && (mask & ~old_mask) != 0) { - cpu_abort(cpu, "Raised interrupt while not in I/O function"); - } - } -} +DECLARE_INSTANCE_CHECKER(TCGState, TCG_STATE, + TYPE_TCG_ACCEL) /* * We default to false if we know other options have been enabled @@ -104,7 +78,7 @@ static bool check_tcg_memory_orders_compatible(void) static bool default_mttcg_enabled(void) { - if (use_icount || TCG_OVERSIZED_GUEST) { + if (icount_enabled() || TCG_OVERSIZED_GUEST) { return false; } else { #ifdef TARGET_SUPPORTS_MTTCG @@ -120,15 +94,34 @@ static void tcg_accel_instance_init(Object *obj) TCGState *s = TCG_STATE(obj); s->mttcg_enabled = default_mttcg_enabled(); + + /* If debugging enabled, default "auto on", otherwise off. */ +#if defined(CONFIG_DEBUG_TCG) && !defined(CONFIG_USER_ONLY) + s->splitwx_enabled = -1; +#else + s->splitwx_enabled = 0; +#endif } +bool mttcg_enabled; + static int tcg_init(MachineState *ms) { TCGState *s = TCG_STATE(current_accel()); - tcg_exec_init(s->tb_size * 1024 * 1024); - cpu_interrupt_handler = tcg_handle_interrupt; + tcg_exec_init(s->tb_size * 1024 * 1024, s->splitwx_enabled); mttcg_enabled = s->mttcg_enabled; + + /* + * Initialize TCG regions only for softmmu. + * + * This needs to be done later for user mode, because the prologue + * generation needs to be delayed so that GUEST_BASE is already set. + */ +#ifndef CONFIG_USER_ONLY + tcg_region_init(); +#endif /* !CONFIG_USER_ONLY */ + return 0; } @@ -146,7 +139,7 @@ static void tcg_set_thread(Object *obj, const char *value, Error **errp) if (strcmp(value, "multi") == 0) { if (TCG_OVERSIZED_GUEST) { error_setg(errp, "No MTTCG when guest word size > hosts"); - } else if (use_icount) { + } else if (icount_enabled()) { error_setg(errp, "No MTTCG when icount is enabled"); } else { #ifndef TARGET_SUPPORTS_MTTCG @@ -191,6 +184,18 @@ static void tcg_set_tb_size(Object *obj, Visitor *v, s->tb_size = value; } +static bool tcg_get_splitwx(Object *obj, Error **errp) +{ + TCGState *s = TCG_STATE(obj); + return s->splitwx_enabled; +} + +static void tcg_set_splitwx(Object *obj, bool value, Error **errp) +{ + TCGState *s = TCG_STATE(obj); + s->splitwx_enabled = value; +} + static void tcg_accel_class_init(ObjectClass *oc, void *data) { AccelClass *ac = ACCEL_CLASS(oc); @@ -208,6 +213,10 @@ static void tcg_accel_class_init(ObjectClass *oc, void *data) object_class_property_set_description(oc, "tb-size", "TCG translation block cache size"); + object_class_property_add_bool(oc, "split-wx", + tcg_get_splitwx, tcg_set_splitwx); + object_class_property_set_description(oc, "split-wx", + "Map jit pages into separate RW and RX regions"); } static const TypeInfo tcg_accel_type = { diff --git a/accel/tcg/tcg-runtime.c b/accel/tcg/tcg-runtime.c index 446465a09a..49f5de37e8 100644 --- a/accel/tcg/tcg-runtime.c +++ b/accel/tcg/tcg-runtime.c @@ -27,10 +27,10 @@ #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" #include "exec/exec-all.h" -#include "exec/tb-lookup.h" #include "disas/disas.h" #include "exec/log.h" #include "tcg/tcg.h" +#include "exec/tb-lookup.h" /* 32-bit helpers */ @@ -145,16 +145,18 @@ uint64_t HELPER(ctpop_i64)(uint64_t arg) return ctpop64(arg); } -void *HELPER(lookup_tb_ptr)(CPUArchState *env) +const void *HELPER(lookup_tb_ptr)(CPUArchState *env) { CPUState *cpu = env_cpu(env); TranslationBlock *tb; target_ulong cs_base, pc; uint32_t flags; - tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, curr_cflags()); + cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); + + tb = tb_lookup(cpu, pc, cs_base, flags, curr_cflags(cpu)); if (tb == NULL) { - return tcg_ctx->code_gen_epilogue; + return tcg_code_gen_epilogue; } qemu_log_mask_and_addr(CPU_LOG_EXEC, pc, "Chain %d: %p [" diff --git a/accel/tcg/tcg-runtime.h b/accel/tcg/tcg-runtime.h index 4eda24e63a..91a5b7e85f 100644 --- a/accel/tcg/tcg-runtime.h +++ b/accel/tcg/tcg-runtime.h @@ -24,10 +24,21 @@ DEF_HELPER_FLAGS_1(clrsb_i64, TCG_CALL_NO_RWG_SE, i64, i64) DEF_HELPER_FLAGS_1(ctpop_i32, TCG_CALL_NO_RWG_SE, i32, i32) DEF_HELPER_FLAGS_1(ctpop_i64, TCG_CALL_NO_RWG_SE, i64, i64) -DEF_HELPER_FLAGS_1(lookup_tb_ptr, TCG_CALL_NO_WG_SE, ptr, env) +DEF_HELPER_FLAGS_1(lookup_tb_ptr, TCG_CALL_NO_WG_SE, cptr, env) DEF_HELPER_FLAGS_1(exit_atomic, TCG_CALL_NO_WG, noreturn, env) +#ifndef IN_HELPER_PROTO +/* + * Pass calls to memset directly to libc, without a thunk in qemu. + * Do not re-declare memset, especially since we fudge the type here; + * we assume sizeof(void *) == sizeof(size_t), which is true for + * all supported hosts. + */ +#define helper_memset memset +DEF_HELPER_FLAGS_3(memset, TCG_CALL_NO_RWG, ptr, ptr, int, ptr) +#endif /* IN_HELPER_PROTO */ + #ifdef CONFIG_SOFTMMU DEF_HELPER_FLAGS_5(atomic_cmpxchgb, TCG_CALL_NO_WG, diff --git a/accel/tcg/trace-events b/accel/tcg/trace-events index 385b9f749b..6eefb37f5d 100644 --- a/accel/tcg/trace-events +++ b/accel/tcg/trace-events @@ -7,4 +7,4 @@ exec_tb_nocache(void *tb, uintptr_t pc) "tb:%p pc=0x%"PRIxPTR exec_tb_exit(void *last_tb, unsigned int flags) "tb:%p flags=0x%x" # translate-all.c -translate_block(void *tb, uintptr_t pc, uint8_t *tb_code) "tb:%p, pc:0x%"PRIxPTR", tb_code:%p" +translate_block(void *tb, uintptr_t pc, const void *tb_code) "tb:%p, pc:0x%"PRIxPTR", tb_code:%p" diff --git a/accel/tcg/trace.h b/accel/tcg/trace.h new file mode 100644 index 0000000000..db61fad3c6 --- /dev/null +++ b/accel/tcg/trace.h @@ -0,0 +1 @@ +#include "trace/trace-accel_tcg.h" diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 2d83013633..ba6ab09790 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -49,7 +49,7 @@ #include "exec/cputlb.h" #include "exec/tb-hash.h" -#include "translate-all.h" +#include "exec/translate-all.h" #include "qemu/bitmap.h" #include "qemu/error-report.h" #include "qemu/qemu-print.h" @@ -57,7 +57,11 @@ #include "qemu/main-loop.h" #include "exec/log.h" #include "sysemu/cpus.h" +#include "sysemu/cpu-timers.h" #include "sysemu/tcg.h" +#include "qapi/error.h" +#include "hw/core/tcg-cpu-ops.h" +#include "internal.h" /* #define DEBUG_TB_INVALIDATE */ /* #define DEBUG_TB_FLUSH */ @@ -111,6 +115,7 @@ typedef struct PageDesc { unsigned int code_write_count; #else unsigned long flags; + void *target_data; #endif #ifndef CONFIG_USER_ONLY QemuSpin lock; @@ -219,7 +224,6 @@ static void *l1_map[V_L1_MAX_SIZE]; TCGContext tcg_init_ctx; __thread TCGContext *tcg_ctx; TBContext tb_ctx; -bool parallel_cpus; static void page_table_config_init(void) { @@ -241,7 +245,7 @@ static void page_table_config_init(void) assert(v_l2_levels >= 0); } -void cpu_gen_init(void) +static void cpu_gen_init(void) { tcg_context_init(&tcg_init_ctx); } @@ -268,9 +272,9 @@ static uint8_t *encode_sleb128(uint8_t *p, target_long val) /* Decode a signed leb128 sequence at *PP; increment *PP past the decoded value. Return the decoded value. */ -static target_long decode_sleb128(uint8_t **pp) +static target_long decode_sleb128(const uint8_t **pp) { - uint8_t *p = *pp; + const uint8_t *p = *pp; target_long val = 0; int byte, shift = 0; @@ -341,7 +345,7 @@ static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb, target_ulong data[TARGET_INSN_START_WORDS] = { tb->pc }; uintptr_t host_pc = (uintptr_t)tb->tc.ptr; CPUArchState *env = cpu->env_ptr; - uint8_t *p = tb->tc.ptr + tb->tc.size; + const uint8_t *p = tb->tc.ptr + tb->tc.size; int i, j, num_insns = tb->icount; #ifdef CONFIG_PROFILER TCGProfile *prof = &tcg_ctx->prof; @@ -369,7 +373,7 @@ static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb, found: if (reset_icount && (tb_cflags(tb) & CF_USE_ICOUNT)) { - assert(use_icount); + assert(icount_enabled()); /* Reset the cycle counter to the start of the block and shift if to the number of actually executed instructions */ cpu_neg(cpu)->icount_decr.u16.low += num_insns - i; @@ -377,9 +381,9 @@ static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb, restore_state_to_opc(env, tb, data); #ifdef CONFIG_PROFILER - atomic_set(&prof->restore_time, + qatomic_set(&prof->restore_time, prof->restore_time + profile_getclock() - ti); - atomic_set(&prof->restore_count, prof->restore_count + 1); + qatomic_set(&prof->restore_count, prof->restore_count + 1); #endif return 0; } @@ -391,40 +395,24 @@ void tb_destroy(TranslationBlock *tb) bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc, bool will_exit) { - TranslationBlock *tb; - bool r = false; - uintptr_t check_offset; - - /* The host_pc has to be in the region of current code buffer. If - * it is not we will not be able to resolve it here. The two cases - * where host_pc will not be correct are: + /* + * The host_pc has to be in the rx region of the code buffer. + * If it is not we will not be able to resolve it here. + * The two cases where host_pc will not be correct are: * * - fault during translation (instruction fetch) * - fault from helper (not using GETPC() macro) * * Either way we need return early as we can't resolve it here. - * - * We are using unsigned arithmetic so if host_pc < - * tcg_init_ctx.code_gen_buffer check_offset will wrap to way - * above the code_gen_buffer_size */ - check_offset = host_pc - (uintptr_t) tcg_init_ctx.code_gen_buffer; - - if (check_offset < tcg_init_ctx.code_gen_buffer_size) { - tb = tcg_tb_lookup(host_pc); + if (in_code_gen_buffer((const void *)(host_pc - tcg_splitwx_diff))) { + TranslationBlock *tb = tcg_tb_lookup(host_pc); if (tb) { cpu_restore_state_from_tb(cpu, tb, host_pc, will_exit); - if (tb_cflags(tb) & CF_NOCACHE) { - /* one-shot translation, invalidate it immediately */ - tb_phys_invalidate(tb, -1); - tcg_tb_remove(tb); - tb_destroy(tb); - } - r = true; + return true; } } - - return r; + return false; } static void page_init(void) @@ -509,7 +497,7 @@ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc) /* Level 2..N-1. */ for (i = v_l2_levels; i > 0; i--) { - void **p = atomic_rcu_read(lp); + void **p = qatomic_rcu_read(lp); if (p == NULL) { void *existing; @@ -518,7 +506,7 @@ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc) return NULL; } p = g_new0(void *, V_L2_SIZE); - existing = atomic_cmpxchg(lp, NULL, p); + existing = qatomic_cmpxchg(lp, NULL, p); if (unlikely(existing)) { g_free(p); p = existing; @@ -528,7 +516,7 @@ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc) lp = p + ((index >> (i * V_L2_BITS)) & (V_L2_SIZE - 1)); } - pd = atomic_rcu_read(lp); + pd = qatomic_rcu_read(lp); if (pd == NULL) { void *existing; @@ -545,7 +533,7 @@ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc) } } #endif - existing = atomic_cmpxchg(lp, NULL, pd); + existing = qatomic_cmpxchg(lp, NULL, pd); if (unlikely(existing)) { #ifndef CONFIG_USER_ONLY { @@ -972,7 +960,7 @@ static void page_lock_pair(PageDesc **ret_p1, tb_page_addr_t phys1, (DEFAULT_CODE_GEN_BUFFER_SIZE_1 < MAX_CODE_GEN_BUFFER_SIZE \ ? DEFAULT_CODE_GEN_BUFFER_SIZE_1 : MAX_CODE_GEN_BUFFER_SIZE) -static inline size_t size_code_gen_buffer(size_t tb_size) +static size_t size_code_gen_buffer(size_t tb_size) { /* Size the buffer. */ if (tb_size == 0) { @@ -1023,22 +1011,27 @@ static inline void *split_cross_256mb(void *buf1, size_t size1) static uint8_t static_code_gen_buffer[DEFAULT_CODE_GEN_BUFFER_SIZE] __attribute__((aligned(CODE_GEN_ALIGN))); -static inline void *alloc_code_gen_buffer(void) +static bool alloc_code_gen_buffer(size_t tb_size, int splitwx, Error **errp) { - void *buf = static_code_gen_buffer; - void *end = static_code_gen_buffer + sizeof(static_code_gen_buffer); + void *buf, *end; size_t size; + if (splitwx > 0) { + error_setg(errp, "jit split-wx not supported"); + return false; + } + /* page-align the beginning and end of the buffer */ + buf = static_code_gen_buffer; + end = static_code_gen_buffer + sizeof(static_code_gen_buffer); buf = QEMU_ALIGN_PTR_UP(buf, qemu_real_host_page_size); end = QEMU_ALIGN_PTR_DOWN(end, qemu_real_host_page_size); size = end - buf; /* Honor a command-line option limiting the size of the buffer. */ - if (size > tcg_ctx->code_gen_buffer_size) { - size = QEMU_ALIGN_DOWN(tcg_ctx->code_gen_buffer_size, - qemu_real_host_page_size); + if (size > tb_size) { + size = QEMU_ALIGN_DOWN(tb_size, qemu_real_host_page_size); } tcg_ctx->code_gen_buffer_size = size; @@ -1050,31 +1043,49 @@ static inline void *alloc_code_gen_buffer(void) #endif if (qemu_mprotect_rwx(buf, size)) { - abort(); + error_setg_errno(errp, errno, "mprotect of jit buffer"); + return false; } qemu_madvise(buf, size, QEMU_MADV_HUGEPAGE); - return buf; + tcg_ctx->code_gen_buffer = buf; + return true; } #elif defined(_WIN32) -static inline void *alloc_code_gen_buffer(void) +static bool alloc_code_gen_buffer(size_t size, int splitwx, Error **errp) { - size_t size = tcg_ctx->code_gen_buffer_size; - return VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, - PAGE_EXECUTE_READWRITE); + void *buf; + + if (splitwx > 0) { + error_setg(errp, "jit split-wx not supported"); + return false; + } + + buf = VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, + PAGE_EXECUTE_READWRITE); + if (buf == NULL) { + error_setg_win32(errp, GetLastError(), + "allocate %zu bytes for jit buffer", size); + return false; + } + + tcg_ctx->code_gen_buffer = buf; + tcg_ctx->code_gen_buffer_size = size; + return true; } #else -static inline void *alloc_code_gen_buffer(void) +static bool alloc_code_gen_buffer_anon(size_t size, int prot, + int flags, Error **errp) { - int prot = PROT_WRITE | PROT_READ | PROT_EXEC; - int flags = MAP_PRIVATE | MAP_ANONYMOUS; - size_t size = tcg_ctx->code_gen_buffer_size; void *buf; buf = mmap(NULL, size, prot, flags, -1, 0); if (buf == MAP_FAILED) { - return NULL; + error_setg_errno(errp, errno, + "allocate %zu bytes for jit buffer", size); + return false; } + tcg_ctx->code_gen_buffer_size = size; #ifdef __mips__ if (cross_256mb(buf, size)) { @@ -1113,20 +1124,184 @@ static inline void *alloc_code_gen_buffer(void) /* Request large pages for the buffer. */ qemu_madvise(buf, size, QEMU_MADV_HUGEPAGE); - return buf; + tcg_ctx->code_gen_buffer = buf; + return true; +} + +#ifndef CONFIG_TCG_INTERPRETER +#ifdef CONFIG_POSIX +#include "qemu/memfd.h" + +static bool alloc_code_gen_buffer_splitwx_memfd(size_t size, Error **errp) +{ + void *buf_rw = NULL, *buf_rx = MAP_FAILED; + int fd = -1; + +#ifdef __mips__ + /* Find space for the RX mapping, vs the 256MiB regions. */ + if (!alloc_code_gen_buffer_anon(size, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS | + MAP_NORESERVE, errp)) { + return false; + } + /* The size of the mapping may have been adjusted. */ + size = tcg_ctx->code_gen_buffer_size; + buf_rx = tcg_ctx->code_gen_buffer; +#endif + + buf_rw = qemu_memfd_alloc("tcg-jit", size, 0, &fd, errp); + if (buf_rw == NULL) { + goto fail; + } + +#ifdef __mips__ + void *tmp = mmap(buf_rx, size, PROT_READ | PROT_EXEC, + MAP_SHARED | MAP_FIXED, fd, 0); + if (tmp != buf_rx) { + goto fail_rx; + } +#else + buf_rx = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_SHARED, fd, 0); + if (buf_rx == MAP_FAILED) { + goto fail_rx; + } +#endif + + close(fd); + tcg_ctx->code_gen_buffer = buf_rw; + tcg_ctx->code_gen_buffer_size = size; + tcg_splitwx_diff = buf_rx - buf_rw; + + /* Request large pages for the buffer and the splitwx. */ + qemu_madvise(buf_rw, size, QEMU_MADV_HUGEPAGE); + qemu_madvise(buf_rx, size, QEMU_MADV_HUGEPAGE); + return true; + + fail_rx: + error_setg_errno(errp, errno, "failed to map shared memory for execute"); + fail: + if (buf_rx != MAP_FAILED) { + munmap(buf_rx, size); + } + if (buf_rw) { + munmap(buf_rw, size); + } + if (fd >= 0) { + close(fd); + } + return false; +} +#endif /* CONFIG_POSIX */ + +#ifdef CONFIG_DARWIN +#include + +extern kern_return_t mach_vm_remap(vm_map_t target_task, + mach_vm_address_t *target_address, + mach_vm_size_t size, + mach_vm_offset_t mask, + int flags, + vm_map_t src_task, + mach_vm_address_t src_address, + boolean_t copy, + vm_prot_t *cur_protection, + vm_prot_t *max_protection, + vm_inherit_t inheritance); + +static bool alloc_code_gen_buffer_splitwx_vmremap(size_t size, Error **errp) +{ + kern_return_t ret; + mach_vm_address_t buf_rw, buf_rx; + vm_prot_t cur_prot, max_prot; + + /* Map the read-write portion via normal anon memory. */ + if (!alloc_code_gen_buffer_anon(size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, errp)) { + return false; + } + + buf_rw = (mach_vm_address_t)tcg_ctx->code_gen_buffer; + buf_rx = 0; + ret = mach_vm_remap(mach_task_self(), + &buf_rx, + size, + 0, + VM_FLAGS_ANYWHERE, + mach_task_self(), + buf_rw, + false, + &cur_prot, + &max_prot, + VM_INHERIT_NONE); + if (ret != KERN_SUCCESS) { + /* TODO: Convert "ret" to a human readable error message. */ + error_setg(errp, "vm_remap for jit splitwx failed"); + munmap((void *)buf_rw, size); + return false; + } + + if (mprotect((void *)buf_rx, size, PROT_READ | PROT_EXEC) != 0) { + error_setg_errno(errp, errno, "mprotect for jit splitwx"); + munmap((void *)buf_rx, size); + munmap((void *)buf_rw, size); + return false; + } + + tcg_splitwx_diff = buf_rx - buf_rw; + return true; +} +#endif /* CONFIG_DARWIN */ +#endif /* CONFIG_TCG_INTERPRETER */ + +static bool alloc_code_gen_buffer_splitwx(size_t size, Error **errp) +{ +#ifndef CONFIG_TCG_INTERPRETER +# ifdef CONFIG_DARWIN + return alloc_code_gen_buffer_splitwx_vmremap(size, errp); +# endif +# ifdef CONFIG_POSIX + return alloc_code_gen_buffer_splitwx_memfd(size, errp); +# endif +#endif + error_setg(errp, "jit split-wx not supported"); + return false; +} + +static bool alloc_code_gen_buffer(size_t size, int splitwx, Error **errp) +{ + ERRP_GUARD(); + int prot, flags; + + if (splitwx) { + if (alloc_code_gen_buffer_splitwx(size, errp)) { + return true; + } + /* + * If splitwx force-on (1), fail; + * if splitwx default-on (-1), fall through to splitwx off. + */ + if (splitwx > 0) { + return false; + } + error_free_or_abort(errp); + } + + prot = PROT_READ | PROT_WRITE | PROT_EXEC; + flags = MAP_PRIVATE | MAP_ANONYMOUS; +#ifdef CONFIG_TCG_INTERPRETER + /* The tcg interpreter does not need execute permission. */ + prot = PROT_READ | PROT_WRITE; +#elif defined(CONFIG_DARWIN) + /* Applicable to both iOS and macOS (Apple Silicon). */ + if (!splitwx) { + flags |= MAP_JIT; + } +#endif + + return alloc_code_gen_buffer_anon(size, prot, flags, errp); } #endif /* USE_STATIC_CODE_GEN_BUFFER, WIN32, POSIX */ -static inline void code_gen_alloc(size_t tb_size) -{ - tcg_ctx->code_gen_buffer_size = size_code_gen_buffer(tb_size); - tcg_ctx->code_gen_buffer = alloc_code_gen_buffer(); - if (tcg_ctx->code_gen_buffer == NULL) { - fprintf(stderr, "Could not allocate dynamic translator buffer\n"); - exit(1); - } -} - static bool tb_cmp(const void *ap, const void *bp) { const TranslationBlock *a = ap; @@ -1135,7 +1310,7 @@ static bool tb_cmp(const void *ap, const void *bp) return a->pc == b->pc && a->cs_base == b->cs_base && a->flags == b->flags && - (tb_cflags(a) & CF_HASH_MASK) == (tb_cflags(b) & CF_HASH_MASK) && + (tb_cflags(a) & ~CF_INVALID) == (tb_cflags(b) & ~CF_INVALID) && a->trace_vcpu_dstate == b->trace_vcpu_dstate && a->page_addr[0] == b->page_addr[0] && a->page_addr[1] == b->page_addr[1]; @@ -1151,13 +1326,19 @@ static void tb_htable_init(void) /* Must be called before using the QEMU cpus. 'tb_size' is the size (in bytes) allocated to the translation buffer. Zero means default size. */ -void tcg_exec_init(unsigned long tb_size) +void tcg_exec_init(unsigned long tb_size, int splitwx) { + bool ok; + tcg_allowed = true; cpu_gen_init(); page_init(); tb_htable_init(); - code_gen_alloc(tb_size); + + ok = alloc_code_gen_buffer(size_code_gen_buffer(tb_size), + splitwx, &error_fatal); + assert(ok); + #if defined(CONFIG_SOFTMMU) /* There's no guest base to take into account, so go ahead and initialize the prologue now. */ @@ -1253,7 +1434,7 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count) tcg_region_reset_all(); /* XXX: flush processor icache at this point if cache flush is expensive */ - atomic_mb_set(&tb_ctx.tb_flush_count, tb_ctx.tb_flush_count + 1); + qatomic_mb_set(&tb_ctx.tb_flush_count, tb_ctx.tb_flush_count + 1); done: mmap_unlock(); @@ -1265,7 +1446,7 @@ done: void tb_flush(CPUState *cpu) { if (tcg_enabled()) { - unsigned tb_flush_count = atomic_mb_read(&tb_ctx.tb_flush_count); + unsigned tb_flush_count = qatomic_mb_read(&tb_ctx.tb_flush_count); if (cpu_in_exclusive_context(cpu)) { do_tb_flush(cpu, RUN_ON_CPU_HOST_INT(tb_flush_count)); @@ -1358,7 +1539,7 @@ static inline void tb_remove_from_jmp_list(TranslationBlock *orig, int n_orig) int n; /* mark the LSB of jmp_dest[] so that no further jumps can be inserted */ - ptr = atomic_or_fetch(&orig->jmp_dest[n_orig], 1); + ptr = qatomic_or_fetch(&orig->jmp_dest[n_orig], 1); dest = (TranslationBlock *)(ptr & ~1); if (dest == NULL) { return; @@ -1369,7 +1550,7 @@ static inline void tb_remove_from_jmp_list(TranslationBlock *orig, int n_orig) * While acquiring the lock, the jump might have been removed if the * destination TB was invalidated; check again. */ - ptr_locked = atomic_read(&orig->jmp_dest[n_orig]); + ptr_locked = qatomic_read(&orig->jmp_dest[n_orig]); if (ptr_locked != ptr) { qemu_spin_unlock(&dest->jmp_lock); /* @@ -1415,7 +1596,7 @@ static inline void tb_jmp_unlink(TranslationBlock *dest) TB_FOR_EACH_JMP(dest, tb, n) { tb_reset_jump(tb, n); - atomic_and(&tb->jmp_dest[n], (uintptr_t)NULL | 1); + qatomic_and(&tb->jmp_dest[n], (uintptr_t)NULL | 1); /* No need to clear the list entry; setting the dest ptr is enough */ } dest->jmp_list_head = (uintptr_t)NULL; @@ -1434,20 +1615,20 @@ static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list) PageDesc *p; uint32_t h; tb_page_addr_t phys_pc; + uint32_t orig_cflags = tb_cflags(tb); assert_memory_lock(); /* make sure no further incoming jumps will be chained to this TB */ qemu_spin_lock(&tb->jmp_lock); - atomic_set(&tb->cflags, tb->cflags | CF_INVALID); + qatomic_set(&tb->cflags, tb->cflags | CF_INVALID); qemu_spin_unlock(&tb->jmp_lock); /* remove the TB from the hash list */ phys_pc = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK); - h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb_cflags(tb) & CF_HASH_MASK, + h = tb_hash_func(phys_pc, tb->pc, tb->flags, orig_cflags, tb->trace_vcpu_dstate); - if (!(tb->cflags & CF_NOCACHE) && - !qht_remove(&tb_ctx.htable, tb, h)) { + if (!qht_remove(&tb_ctx.htable, tb, h)) { return; } @@ -1466,8 +1647,8 @@ static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list) /* remove the TB from the hash list */ h = tb_jmp_cache_hash_func(tb->pc); CPU_FOREACH(cpu) { - if (atomic_read(&cpu->tb_jmp_cache[h]) == tb) { - atomic_set(&cpu->tb_jmp_cache[h], NULL); + if (qatomic_read(&cpu->tb_jmp_cache[h]) == tb) { + qatomic_set(&cpu->tb_jmp_cache[h], NULL); } } @@ -1478,13 +1659,15 @@ static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list) /* suppress any remaining jumps to this TB */ tb_jmp_unlink(tb); - atomic_set(&tcg_ctx->tb_phys_invalidate_count, + qatomic_set(&tcg_ctx->tb_phys_invalidate_count, tcg_ctx->tb_phys_invalidate_count + 1); } static void tb_phys_invalidate__locked(TranslationBlock *tb) { + qemu_thread_jit_write(); do_tb_phys_invalidate(tb, true); + qemu_thread_jit_execute(); } /* invalidate one TB @@ -1573,7 +1756,7 @@ static inline void tb_page_add(PageDesc *p, TranslationBlock *tb, prot |= p2->flags; p2->flags &= ~PAGE_WRITE; } - mprotect(g2h(page_addr), qemu_host_page_size, + mprotect(g2h_untagged(page_addr), qemu_host_page_size, (prot & PAGE_BITS) & ~PAGE_WRITE); if (DEBUG_TB_INVALIDATE_GATE) { printf("protecting code page: 0x" TB_PAGE_ADDR_FMT "\n", page_addr); @@ -1589,7 +1772,8 @@ static inline void tb_page_add(PageDesc *p, TranslationBlock *tb, #endif } -/* add a new TB and link it to the physical page tables. phys_page2 is +/* + * Add a new TB and link it to the physical page tables. phys_page2 is * (-1) to indicate that only one page contains the TB. * * Called with mmap_lock held for user-mode emulation. @@ -1605,19 +1789,11 @@ tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc, { PageDesc *p; PageDesc *p2 = NULL; + void *existing_tb = NULL; + uint32_t h; assert_memory_lock(); - - if (phys_pc == -1) { - /* - * If the TB is not associated with a physical RAM page then - * it must be a temporary one-insn TB, and we have nothing to do - * except fill in the page_addr[] fields. - */ - assert(tb->cflags & CF_NOCACHE); - tb->page_addr[0] = tb->page_addr[1] = -1; - return tb; - } + tcg_debug_assert(!(tb->cflags & CF_INVALID)); /* * Add the TB to the page list, acquiring first the pages's locks. @@ -1635,25 +1811,20 @@ tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc, tb->page_addr[1] = -1; } - if (!(tb->cflags & CF_NOCACHE)) { - void *existing_tb = NULL; - uint32_t h; + /* add in the hash table */ + h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb->cflags, + tb->trace_vcpu_dstate); + qht_insert(&tb_ctx.htable, tb, h, &existing_tb); - /* add in the hash table */ - h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb->cflags & CF_HASH_MASK, - tb->trace_vcpu_dstate); - qht_insert(&tb_ctx.htable, tb, h, &existing_tb); - - /* remove TB from the page(s) if we couldn't insert it */ - if (unlikely(existing_tb)) { - tb_page_remove(p, tb); - invalidate_page_bitmap(p); - if (p2) { - tb_page_remove(p2, tb); - invalidate_page_bitmap(p2); - } - tb = existing_tb; + /* remove TB from the page(s) if we couldn't insert it */ + if (unlikely(existing_tb)) { + tb_page_remove(p, tb); + invalidate_page_bitmap(p); + if (p2) { + tb_page_remove(p2, tb); + invalidate_page_bitmap(p2); } + tb = existing_tb; } if (p2 && p2 != p) { @@ -1686,18 +1857,15 @@ TranslationBlock *tb_gen_code(CPUState *cpu, #endif assert_memory_lock(); + qemu_thread_jit_write(); phys_pc = get_page_addr_code(env, pc); if (phys_pc == -1) { - /* Generate a temporary TB with 1 insn in it */ - cflags &= ~CF_COUNT_MASK; - cflags |= CF_NOCACHE | 1; + /* Generate a one-shot TB with 1 insn in it */ + cflags = (cflags & ~CF_COUNT_MASK) | 1; } - cflags &= ~CF_CLUSTER_MASK; - cflags |= cpu->cluster_index << CF_CLUSTER_SHIFT; - max_insns = cflags & CF_COUNT_MASK; if (max_insns == 0) { max_insns = CF_COUNT_MASK; @@ -1721,27 +1889,32 @@ TranslationBlock *tb_gen_code(CPUState *cpu, } gen_code_buf = tcg_ctx->code_gen_ptr; - tb->tc.ptr = gen_code_buf; + tb->tc.ptr = tcg_splitwx_to_rx(gen_code_buf); tb->pc = pc; tb->cs_base = cs_base; tb->flags = flags; tb->cflags = cflags; - tb->orig_tb = NULL; tb->trace_vcpu_dstate = *cpu->trace_dstate; tcg_ctx->tb_cflags = cflags; tb_overflow: #ifdef CONFIG_PROFILER /* includes aborted translations because of exceptions */ - atomic_set(&prof->tb_count1, prof->tb_count1 + 1); + qatomic_set(&prof->tb_count1, prof->tb_count1 + 1); ti = profile_getclock(); #endif + gen_code_size = sigsetjmp(tcg_ctx->jmp_trans, 0); + if (unlikely(gen_code_size != 0)) { + goto error_return; + } + tcg_func_start(tcg_ctx); tcg_ctx->cpu = env_cpu(env); gen_intermediate_code(cpu, tb, max_insns); tcg_ctx->cpu = NULL; + max_insns = tb->icount; trace_translate_block(tb, tb->pc, tb->tc.ptr); @@ -1758,13 +1931,15 @@ TranslationBlock *tb_gen_code(CPUState *cpu, } #ifdef CONFIG_PROFILER - atomic_set(&prof->tb_count, prof->tb_count + 1); - atomic_set(&prof->interm_time, prof->interm_time + profile_getclock() - ti); + qatomic_set(&prof->tb_count, prof->tb_count + 1); + qatomic_set(&prof->interm_time, + prof->interm_time + profile_getclock() - ti); ti = profile_getclock(); #endif gen_code_size = tcg_gen_code(tcg_ctx, tb); if (unlikely(gen_code_size < 0)) { + error_return: switch (gen_code_size) { case -1: /* @@ -1776,6 +1951,9 @@ TranslationBlock *tb_gen_code(CPUState *cpu, * flush the TBs, allocate a new TB, re-initialize it per * above, and re-do the actual code generation. */ + qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT, + "Restarting code generation for " + "code_gen_buffer overflow\n"); goto buffer_overflow; case -2: @@ -1788,9 +1966,12 @@ TranslationBlock *tb_gen_code(CPUState *cpu, * Try again with half as many insns as we attempted this time. * If a single insn overflows, there's a bug somewhere... */ - max_insns = tb->icount; assert(max_insns > 1); max_insns /= 2; + qemu_log_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT, + "Restarting code generation with " + "smaller translation block (max %d insns)\n", + max_insns); goto tb_overflow; default: @@ -1804,63 +1985,66 @@ TranslationBlock *tb_gen_code(CPUState *cpu, tb->tc.size = gen_code_size; #ifdef CONFIG_PROFILER - atomic_set(&prof->code_time, prof->code_time + profile_getclock() - ti); - atomic_set(&prof->code_in_len, prof->code_in_len + tb->size); - atomic_set(&prof->code_out_len, prof->code_out_len + gen_code_size); - atomic_set(&prof->search_out_len, prof->search_out_len + search_size); + qatomic_set(&prof->code_time, prof->code_time + profile_getclock() - ti); + qatomic_set(&prof->code_in_len, prof->code_in_len + tb->size); + qatomic_set(&prof->code_out_len, prof->code_out_len + gen_code_size); + qatomic_set(&prof->search_out_len, prof->search_out_len + search_size); #endif #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM) && qemu_log_in_addr_range(tb->pc)) { FILE *logfile = qemu_log_lock(); - int code_size, data_size = 0; - g_autoptr(GString) note = g_string_new("[tb header & initial instruction]"); - size_t chunk_start = 0; + int code_size, data_size; + const tcg_target_ulong *rx_data_gen_ptr; + size_t chunk_start; int insn = 0; - qemu_log("OUT: [size=%d]\n", gen_code_size); + if (tcg_ctx->data_gen_ptr) { - code_size = tcg_ctx->data_gen_ptr - tb->tc.ptr; + rx_data_gen_ptr = tcg_splitwx_to_rx(tcg_ctx->data_gen_ptr); + code_size = (const void *)rx_data_gen_ptr - tb->tc.ptr; data_size = gen_code_size - code_size; } else { + rx_data_gen_ptr = 0; code_size = gen_code_size; + data_size = 0; } /* Dump header and the first instruction */ + qemu_log("OUT: [size=%d]\n", gen_code_size); + qemu_log(" -- guest addr 0x" TARGET_FMT_lx " + tb prologue\n", + tcg_ctx->gen_insn_data[insn][0]); chunk_start = tcg_ctx->gen_insn_end_off[insn]; - log_disas(tb->tc.ptr, chunk_start, note->str); + log_disas(tb->tc.ptr, chunk_start); /* * Dump each instruction chunk, wrapping up empty chunks into * the next instruction. The whole array is offset so the * first entry is the beginning of the 2nd instruction. */ - while (insn <= tb->icount && chunk_start < code_size) { + while (insn < tb->icount) { size_t chunk_end = tcg_ctx->gen_insn_end_off[insn]; if (chunk_end > chunk_start) { - g_string_printf(note, "[guest addr: " TARGET_FMT_lx "]", - tcg_ctx->gen_insn_data[insn][0]); - log_disas(tb->tc.ptr + chunk_start, chunk_end - chunk_start, - note->str); + qemu_log(" -- guest addr 0x" TARGET_FMT_lx "\n", + tcg_ctx->gen_insn_data[insn][0]); + log_disas(tb->tc.ptr + chunk_start, chunk_end - chunk_start); chunk_start = chunk_end; } insn++; } + if (chunk_start < code_size) { + qemu_log(" -- tb slow paths + alignment\n"); + log_disas(tb->tc.ptr + chunk_start, code_size - chunk_start); + } + /* Finally dump any data we may have after the block */ if (data_size) { int i; qemu_log(" data: [size=%d]\n", data_size); - for (i = 0; i < data_size; i += sizeof(tcg_target_ulong)) { - if (sizeof(tcg_target_ulong) == 8) { - qemu_log("0x%08" PRIxPTR ": .quad 0x%016" PRIx64 "\n", - (uintptr_t)tcg_ctx->data_gen_ptr + i, - *(uint64_t *)(tcg_ctx->data_gen_ptr + i)); - } else { - qemu_log("0x%08" PRIxPTR ": .long 0x%08x\n", - (uintptr_t)tcg_ctx->data_gen_ptr + i, - *(uint32_t *)(tcg_ctx->data_gen_ptr + i)); - } + for (i = 0; i < data_size / sizeof(tcg_target_ulong); i++) { + qemu_log("0x%08" PRIxPTR ": .quad 0x%" TCG_PRIlx "\n", + (uintptr_t)&rx_data_gen_ptr[i], rx_data_gen_ptr[i]); } } qemu_log("\n"); @@ -1869,7 +2053,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, } #endif - atomic_set(&tcg_ctx->code_gen_ptr, (void *) + qatomic_set(&tcg_ctx->code_gen_ptr, (void *) ROUND_UP((uintptr_t)gen_code_buf + gen_code_size + search_size, CODE_GEN_ALIGN)); @@ -1889,6 +2073,17 @@ TranslationBlock *tb_gen_code(CPUState *cpu, tb_reset_jump(tb, 1); } + /* + * If the TB is not associated with a physical RAM page then + * it must be a temporary one-insn TB, and we have nothing to do + * except fill in the page_addr[] fields. Return early before + * attempting to link to other TBs or add to the lookup table. + */ + if (phys_pc == -1) { + tb->page_addr[0] = tb->page_addr[1] = -1; + return tb; + } + /* check next page if needed */ virt_page2 = (pc + tb->size - 1) & TARGET_PAGE_MASK; phys_page2 = -1; @@ -1905,7 +2100,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, uintptr_t orig_aligned = (uintptr_t)gen_code_buf; orig_aligned -= ROUND_UP(sizeof(*tb), qemu_icache_linesize); - atomic_set(&tcg_ctx->code_gen_ptr, (void *)orig_aligned); + qatomic_set(&tcg_ctx->code_gen_ptr, (void *)orig_aligned); tb_destroy(tb); return existing_tb; } @@ -1997,7 +2192,7 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages, if (current_tb_modified) { page_collection_unlock(pages); /* Force execution of one insn next time. */ - cpu->cflags_next_tb = 1 | curr_cflags(); + cpu->cflags_next_tb = 1 | curr_cflags(cpu); mmap_unlock(); cpu_loop_exit_noexc(cpu); } @@ -2165,7 +2360,7 @@ static bool tb_invalidate_phys_page(tb_page_addr_t addr, uintptr_t pc) #ifdef TARGET_HAS_PRECISE_SMC if (current_tb_modified) { /* Force execution of one insn next time. */ - cpu->cflags_next_tb = 1 | curr_cflags(); + cpu->cflags_next_tb = 1 | curr_cflags(cpu); return true; } #endif @@ -2203,17 +2398,16 @@ void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr) } #ifndef CONFIG_USER_ONLY -/* in deterministic execution mode, instructions doing device I/Os +/* + * In deterministic execution mode, instructions doing device I/Os * must be at the end of the TB. * * Called by softmmu_template.h, with iothread mutex not held. */ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) { -#if defined(TARGET_MIPS) || defined(TARGET_SH4) - CPUArchState *env = cpu->env_ptr; -#endif TranslationBlock *tb; + CPUClass *cc; uint32_t n; tb = tcg_tb_lookup(retaddr); @@ -2223,66 +2417,32 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) } cpu_restore_state_from_tb(cpu, tb, retaddr, true); - /* On MIPS and SH, delay slot instructions can only be restarted if - they were already the first instruction in the TB. If this is not - the first instruction in a TB then re-execute the preceding - branch. */ - n = 1; -#if defined(TARGET_MIPS) - if ((env->hflags & MIPS_HFLAG_BMASK) != 0 - && env->active_tc.PC != tb->pc) { - env->active_tc.PC -= (env->hflags & MIPS_HFLAG_B16 ? 2 : 4); - cpu_neg(cpu)->icount_decr.u16.low++; - env->hflags &= ~MIPS_HFLAG_BMASK; - n = 2; - } -#elif defined(TARGET_SH4) - if ((env->flags & ((DELAY_SLOT | DELAY_SLOT_CONDITIONAL))) != 0 - && env->pc != tb->pc) { - env->pc -= 2; - cpu_neg(cpu)->icount_decr.u16.low++; - env->flags &= ~(DELAY_SLOT | DELAY_SLOT_CONDITIONAL); - n = 2; - } -#endif - - /* Generate a new TB executing the I/O insn. */ - cpu->cflags_next_tb = curr_cflags() | CF_LAST_IO | n; - - if (tb_cflags(tb) & CF_NOCACHE) { - if (tb->orig_tb) { - /* Invalidate original TB if this TB was generated in - * cpu_exec_nocache() */ - tb_phys_invalidate(tb->orig_tb, -1); - } - tcg_tb_remove(tb); - tb_destroy(tb); - } - - /* TODO: If env->pc != tb->pc (i.e. the faulting instruction was not - * the first in the TB) then we end up generating a whole new TB and - * repeating the fault, which is horribly inefficient. - * Better would be to execute just this insn uncached, or generate a - * second new TB. + /* + * Some guests must re-execute the branch when re-executing a delay + * slot instruction. When this is the case, adjust icount and N + * to account for the re-execution of the branch. */ - cpu_loop_exit_noexc(cpu); -} - -static void tb_jmp_cache_clear_page(CPUState *cpu, target_ulong page_addr) -{ - unsigned int i, i0 = tb_jmp_cache_hash_page(page_addr); - - for (i = 0; i < TB_JMP_PAGE_SIZE; i++) { - atomic_set(&cpu->tb_jmp_cache[i0 + i], NULL); + n = 1; + cc = CPU_GET_CLASS(cpu); + if (cc->tcg_ops->io_recompile_replay_branch && + cc->tcg_ops->io_recompile_replay_branch(cpu, tb)) { + cpu_neg(cpu)->icount_decr.u16.low++; + n = 2; } -} -void tb_flush_jmp_cache(CPUState *cpu, target_ulong addr) -{ - /* Discard jump cache entries for any tb which might potentially - overlap the flushed page. */ - tb_jmp_cache_clear_page(cpu, addr - TARGET_PAGE_SIZE); - tb_jmp_cache_clear_page(cpu, addr); + /* + * Exit the loop and potentially generate a new TB executing the + * just the I/O insns. We also limit instrumentation to memory + * operations only (which execute after completion) so we don't + * double instrument the instruction. + */ + cpu->cflags_next_tb = curr_cflags(cpu) | CF_MEMI_ONLY | CF_LAST_IO | n; + + qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc, + "cpu_io_recompile: rewound execution of TB to " + TARGET_FMT_lx "\n", tb->pc); + + cpu_loop_exit_noexc(cpu); } static void print_qht_statistics(struct qht_stats hst) @@ -2367,7 +2527,7 @@ void dump_exec_info(void) qemu_printf("Translation buffer state:\n"); /* * Report total code size including the padding and TB structs; - * otherwise users might think "-tb-size" is not honoured. + * otherwise users might think "-accel tcg,tb-size" is not honoured. * For avg host size we use the precise numbers from tb_tree_stats though. */ qemu_printf("gen code size %zu/%zu\n", @@ -2393,7 +2553,7 @@ void dump_exec_info(void) qemu_printf("\nStatistics:\n"); qemu_printf("TB flush count %u\n", - atomic_read(&tb_ctx.tb_flush_count)); + qatomic_read(&tb_ctx.tb_flush_count)); qemu_printf("TB invalidate count %zu\n", tcg_tb_phys_invalidate_count()); @@ -2415,7 +2575,7 @@ void cpu_interrupt(CPUState *cpu, int mask) { g_assert(qemu_mutex_iothread_locked()); cpu->interrupt_request |= mask; - atomic_set(&cpu_neg(cpu)->icount_decr.u16.high, -1); + qatomic_set(&cpu_neg(cpu)->icount_decr.u16.high, -1); } /* @@ -2547,12 +2707,15 @@ int page_get_flags(target_ulong address) void page_set_flags(target_ulong start, target_ulong end, int flags) { target_ulong addr, len; + bool reset_target_data; /* This function should never be called with addresses outside the guest address space. If this assert fires, it probably indicates a missing call to h2g_valid. */ assert(end - 1 <= GUEST_ADDR_MAX); assert(start < end); + /* Only set PAGE_ANON with new mappings. */ + assert(!(flags & PAGE_ANON) || (flags & PAGE_RESET)); assert_memory_lock(); start = start & TARGET_PAGE_MASK; @@ -2561,6 +2724,8 @@ void page_set_flags(target_ulong start, target_ulong end, int flags) if (flags & PAGE_WRITE) { flags |= PAGE_WRITE_ORG; } + reset_target_data = !(flags & PAGE_VALID) || (flags & PAGE_RESET); + flags &= ~PAGE_RESET; for (addr = start, len = end - start; len != 0; @@ -2574,10 +2739,37 @@ void page_set_flags(target_ulong start, target_ulong end, int flags) p->first_tb) { tb_invalidate_phys_page(addr, 0); } - p->flags = flags; + if (reset_target_data) { + g_free(p->target_data); + p->target_data = NULL; + p->flags = flags; + } else { + /* Using mprotect on a page does not change MAP_ANON. */ + p->flags = (p->flags & PAGE_ANON) | flags; + } } } +void *page_get_target_data(target_ulong address) +{ + PageDesc *p = page_find(address >> TARGET_PAGE_BITS); + return p ? p->target_data : NULL; +} + +void *page_alloc_target_data(target_ulong address, size_t size) +{ + PageDesc *p = page_find(address >> TARGET_PAGE_BITS); + void *ret = NULL; + + if (p->flags & PAGE_VALID) { + ret = p->target_data; + if (!ret) { + p->target_data = ret = g_malloc0(size); + } + } + return ret; +} + int page_check_range(target_ulong start, target_ulong len, int flags) { PageDesc *p; @@ -2691,7 +2883,7 @@ int page_unprotect(target_ulong address, uintptr_t pc) } #endif } - mprotect((void *)g2h(host_start), qemu_host_page_size, + mprotect((void *)g2h_untagged(host_start), qemu_host_page_size, prot & PAGE_BITS); } mmap_unlock(); diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 603d17ff83..2dfc27102f 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -17,6 +17,7 @@ #include "exec/log.h" #include "exec/translator.h" #include "exec/plugin-gen.h" +#include "sysemu/replay.h" /* Pairs with tcg_clear_temp_count. To be called by #TranslatorOps.{translate_insn,tb_stop} if @@ -57,7 +58,8 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, ops->tb_start(db, cpu); tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ - plugin_enabled = plugin_gen_tb_start(cpu, tb); + plugin_enabled = plugin_gen_tb_start(cpu, tb, + tb_cflags(db->tb) & CF_MEMI_ONLY); while (true) { db->num_insns++; @@ -99,6 +101,8 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, gen_io_start(); ops->translate_insn(db, cpu); } else { + /* we should only see CF_MEMI_ONLY for io_recompile */ + tcg_debug_assert(!(tb_cflags(db->tb) & CF_MEMI_ONLY)); ops->translate_insn(db, cpu); } @@ -132,8 +136,8 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, } /* The disas_log hook may use these values rather than recompute. */ - db->tb->size = db->pc_next - db->pc_first; - db->tb->icount = db->num_insns; + tb->size = db->pc_next - db->pc_first; + tb->icount = db->num_insns; #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) diff --git a/accel/tcg/user-exec-stub.c b/accel/tcg/user-exec-stub.c index f6d8c8fb6f..b876f5c1e4 100644 --- a/accel/tcg/user-exec-stub.c +++ b/accel/tcg/user-exec-stub.c @@ -9,6 +9,10 @@ void cpu_resume(CPUState *cpu) { } +void cpu_remove_sync(CPUState *cpu) +{ +} + void qemu_init_vcpu(CPUState *cpu) { } diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index d8b027f8c1..0d8cc27b21 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -18,15 +18,16 @@ */ #include "qemu/osdep.h" #include "cpu.h" +#include "hw/core/tcg-cpu-ops.h" #include "disas/disas.h" #include "exec/exec-all.h" #include "tcg/tcg.h" #include "qemu/bitops.h" #include "exec/cpu_ldst.h" -#include "translate-all.h" +#include "exec/translate-all.h" #include "exec/helper-proto.h" #include "qemu/atomic128.h" -#include "trace-root.h" +#include "trace/trace-root.h" #include "trace/mem.h" #undef EAX @@ -49,7 +50,8 @@ __thread uintptr_t helper_retaddr; /* exit the current TB from a signal handler. The host registers are restored in a state compatible with the CPU emulator */ -static void cpu_exit_tb_from_sighandler(CPUState *cpu, sigset_t *old_set) +static void QEMU_NORETURN cpu_exit_tb_from_sighandler(CPUState *cpu, + sigset_t *old_set) { /* XXX: use siglongjmp ? */ sigprocmask(SIG_SETMASK, old_set, NULL); @@ -88,7 +90,7 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, * use that value directly. Within cpu_restore_state_from_tb, we * assume PC comes from GETPC(), as used by the helper functions, * so we adjust the address by -GETPC_ADJ to form an address that - * is within the call insn, so that the address does not accidentially + * is within the call insn, so that the address does not accidentally * match the beginning of the next guest insn. However, when the * pc comes from the signal frame it points to the actual faulting * host memory insn and not the return from a call insn. @@ -186,7 +188,8 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info, clear_helper_retaddr(); cc = CPU_GET_CLASS(cpu); - cc->tlb_fill(cpu, address, 0, access_type, MMU_USER_IDX, false, pc); + cc->tcg_ops->tlb_fill(cpu, address, 0, access_type, + MMU_USER_IDX, false, pc); g_assert_not_reached(); } @@ -210,14 +213,15 @@ static int probe_access_internal(CPUArchState *env, target_ulong addr, g_assert_not_reached(); } - if (!guest_addr_valid(addr) || page_check_range(addr, 1, flags) < 0) { + if (!guest_addr_valid_untagged(addr) || + page_check_range(addr, 1, flags) < 0) { if (nonfault) { return TLB_INVALID_MASK; } else { CPUState *cpu = env_cpu(env); CPUClass *cc = CPU_GET_CLASS(cpu); - cc->tlb_fill(cpu, addr, fault_size, access_type, - MMU_USER_IDX, false, ra); + cc->tcg_ops->tlb_fill(cpu, addr, fault_size, access_type, + MMU_USER_IDX, false, ra); g_assert_not_reached(); } } @@ -231,7 +235,7 @@ int probe_access_flags(CPUArchState *env, target_ulong addr, int flags; flags = probe_access_internal(env, addr, 0, access_type, nonfault, ra); - *phost = flags ? NULL : g2h(addr); + *phost = flags ? NULL : g2h(env_cpu(env), addr); return flags; } @@ -244,7 +248,7 @@ void *probe_access(CPUArchState *env, target_ulong addr, int size, flags = probe_access_internal(env, addr, size, access_type, false, ra); g_assert(flags == 0); - return size ? g2h(addr) : NULL; + return size ? g2h(env_cpu(env), addr) : NULL; } #if defined(__i386__) @@ -702,16 +706,51 @@ int cpu_signal_handler(int host_signum, void *pinfo, #elif defined(__mips__) +#if defined(__misp16) || defined(__mips_micromips) +#error "Unsupported encoding" +#endif + int cpu_signal_handler(int host_signum, void *pinfo, void *puc) { siginfo_t *info = pinfo; ucontext_t *uc = puc; - greg_t pc = uc->uc_mcontext.pc; - int is_write; + uintptr_t pc = uc->uc_mcontext.pc; + uint32_t insn = *(uint32_t *)pc; + int is_write = 0; + + /* Detect all store instructions at program counter. */ + switch((insn >> 26) & 077) { + case 050: /* SB */ + case 051: /* SH */ + case 052: /* SWL */ + case 053: /* SW */ + case 054: /* SDL */ + case 055: /* SDR */ + case 056: /* SWR */ + case 070: /* SC */ + case 071: /* SWC1 */ + case 074: /* SCD */ + case 075: /* SDC1 */ + case 077: /* SD */ +#if !defined(__mips_isa_rev) || __mips_isa_rev < 6 + case 072: /* SWC2 */ + case 076: /* SDC2 */ +#endif + is_write = 1; + break; + case 023: /* COP1X */ + /* Required in all versions of MIPS64 since + MIPS64r1 and subsequent versions of MIPS32r2. */ + switch (insn & 077) { + case 010: /* SWXC1 */ + case 011: /* SDXC1 */ + case 015: /* SUXC1 */ + is_write = 1; + } + break; + } - /* XXX: compute is_write */ - is_write = 0; return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); } @@ -804,7 +843,7 @@ uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr ptr) uint16_t meminfo = trace_mem_get_info(MO_UB, MMU_USER_IDX, false); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); - ret = ldub_p(g2h(ptr)); + ret = ldub_p(g2h(env_cpu(env), ptr)); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); return ret; } @@ -815,7 +854,7 @@ int cpu_ldsb_data(CPUArchState *env, abi_ptr ptr) uint16_t meminfo = trace_mem_get_info(MO_SB, MMU_USER_IDX, false); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); - ret = ldsb_p(g2h(ptr)); + ret = ldsb_p(g2h(env_cpu(env), ptr)); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); return ret; } @@ -826,7 +865,7 @@ uint32_t cpu_lduw_be_data(CPUArchState *env, abi_ptr ptr) uint16_t meminfo = trace_mem_get_info(MO_BEUW, MMU_USER_IDX, false); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); - ret = lduw_be_p(g2h(ptr)); + ret = lduw_be_p(g2h(env_cpu(env), ptr)); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); return ret; } @@ -837,7 +876,7 @@ int cpu_ldsw_be_data(CPUArchState *env, abi_ptr ptr) uint16_t meminfo = trace_mem_get_info(MO_BESW, MMU_USER_IDX, false); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); - ret = ldsw_be_p(g2h(ptr)); + ret = ldsw_be_p(g2h(env_cpu(env), ptr)); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); return ret; } @@ -848,7 +887,7 @@ uint32_t cpu_ldl_be_data(CPUArchState *env, abi_ptr ptr) uint16_t meminfo = trace_mem_get_info(MO_BEUL, MMU_USER_IDX, false); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); - ret = ldl_be_p(g2h(ptr)); + ret = ldl_be_p(g2h(env_cpu(env), ptr)); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); return ret; } @@ -859,7 +898,7 @@ uint64_t cpu_ldq_be_data(CPUArchState *env, abi_ptr ptr) uint16_t meminfo = trace_mem_get_info(MO_BEQ, MMU_USER_IDX, false); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); - ret = ldq_be_p(g2h(ptr)); + ret = ldq_be_p(g2h(env_cpu(env), ptr)); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); return ret; } @@ -870,7 +909,7 @@ uint32_t cpu_lduw_le_data(CPUArchState *env, abi_ptr ptr) uint16_t meminfo = trace_mem_get_info(MO_LEUW, MMU_USER_IDX, false); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); - ret = lduw_le_p(g2h(ptr)); + ret = lduw_le_p(g2h(env_cpu(env), ptr)); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); return ret; } @@ -881,7 +920,7 @@ int cpu_ldsw_le_data(CPUArchState *env, abi_ptr ptr) uint16_t meminfo = trace_mem_get_info(MO_LESW, MMU_USER_IDX, false); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); - ret = ldsw_le_p(g2h(ptr)); + ret = ldsw_le_p(g2h(env_cpu(env), ptr)); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); return ret; } @@ -892,7 +931,7 @@ uint32_t cpu_ldl_le_data(CPUArchState *env, abi_ptr ptr) uint16_t meminfo = trace_mem_get_info(MO_LEUL, MMU_USER_IDX, false); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); - ret = ldl_le_p(g2h(ptr)); + ret = ldl_le_p(g2h(env_cpu(env), ptr)); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); return ret; } @@ -903,7 +942,7 @@ uint64_t cpu_ldq_le_data(CPUArchState *env, abi_ptr ptr) uint16_t meminfo = trace_mem_get_info(MO_LEQ, MMU_USER_IDX, false); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); - ret = ldq_le_p(g2h(ptr)); + ret = ldq_le_p(g2h(env_cpu(env), ptr)); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); return ret; } @@ -1013,7 +1052,7 @@ void cpu_stb_data(CPUArchState *env, abi_ptr ptr, uint32_t val) uint16_t meminfo = trace_mem_get_info(MO_UB, MMU_USER_IDX, true); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); - stb_p(g2h(ptr), val); + stb_p(g2h(env_cpu(env), ptr), val); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); } @@ -1022,7 +1061,7 @@ void cpu_stw_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val) uint16_t meminfo = trace_mem_get_info(MO_BEUW, MMU_USER_IDX, true); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); - stw_be_p(g2h(ptr), val); + stw_be_p(g2h(env_cpu(env), ptr), val); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); } @@ -1031,7 +1070,7 @@ void cpu_stl_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val) uint16_t meminfo = trace_mem_get_info(MO_BEUL, MMU_USER_IDX, true); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); - stl_be_p(g2h(ptr), val); + stl_be_p(g2h(env_cpu(env), ptr), val); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); } @@ -1040,7 +1079,7 @@ void cpu_stq_be_data(CPUArchState *env, abi_ptr ptr, uint64_t val) uint16_t meminfo = trace_mem_get_info(MO_BEQ, MMU_USER_IDX, true); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); - stq_be_p(g2h(ptr), val); + stq_be_p(g2h(env_cpu(env), ptr), val); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); } @@ -1049,7 +1088,7 @@ void cpu_stw_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val) uint16_t meminfo = trace_mem_get_info(MO_LEUW, MMU_USER_IDX, true); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); - stw_le_p(g2h(ptr), val); + stw_le_p(g2h(env_cpu(env), ptr), val); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); } @@ -1058,7 +1097,7 @@ void cpu_stl_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val) uint16_t meminfo = trace_mem_get_info(MO_LEUL, MMU_USER_IDX, true); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); - stl_le_p(g2h(ptr), val); + stl_le_p(g2h(env_cpu(env), ptr), val); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); } @@ -1067,7 +1106,7 @@ void cpu_stq_le_data(CPUArchState *env, abi_ptr ptr, uint64_t val) uint16_t meminfo = trace_mem_get_info(MO_LEQ, MMU_USER_IDX, true); trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo); - stq_le_p(g2h(ptr), val); + stq_le_p(g2h(env_cpu(env), ptr), val); qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo); } @@ -1132,7 +1171,7 @@ uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr ptr) uint32_t ret; set_helper_retaddr(1); - ret = ldub_p(g2h(ptr)); + ret = ldub_p(g2h_untagged(ptr)); clear_helper_retaddr(); return ret; } @@ -1142,7 +1181,7 @@ uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr ptr) uint32_t ret; set_helper_retaddr(1); - ret = lduw_p(g2h(ptr)); + ret = lduw_p(g2h_untagged(ptr)); clear_helper_retaddr(); return ret; } @@ -1152,7 +1191,7 @@ uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr ptr) uint32_t ret; set_helper_retaddr(1); - ret = ldl_p(g2h(ptr)); + ret = ldl_p(g2h_untagged(ptr)); clear_helper_retaddr(); return ret; } @@ -1162,7 +1201,7 @@ uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr ptr) uint64_t ret; set_helper_retaddr(1); - ret = ldq_p(g2h(ptr)); + ret = ldq_p(g2h_untagged(ptr)); clear_helper_retaddr(); return ret; } @@ -1175,7 +1214,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, if (unlikely(addr & (size - 1))) { cpu_loop_exit_atomic(env_cpu(env), retaddr); } - void *ret = g2h(addr); + void *ret = g2h(env_cpu(env), addr); set_helper_retaddr(retaddr); return ret; } @@ -1189,7 +1228,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, #define ATOMIC_NAME(X) HELPER(glue(glue(atomic_ ## X, SUFFIX), END)) #define EXTRA_ARGS -#include "atomic_common.inc.c" +#include "atomic_common.c.inc" #define DATA_SIZE 1 #include "atomic_template.h" diff --git a/accel/xen/Makefile.objs b/accel/xen/Makefile.objs deleted file mode 100644 index 7482cfb436..0000000000 --- a/accel/xen/Makefile.objs +++ /dev/null @@ -1 +0,0 @@ -obj-y += xen-all.o diff --git a/accel/xen/meson.build b/accel/xen/meson.build new file mode 100644 index 0000000000..002bdb03c6 --- /dev/null +++ b/accel/xen/meson.build @@ -0,0 +1 @@ +specific_ss.add(when: 'CONFIG_XEN', if_true: files('xen-all.c')) diff --git a/accel/xen/xen-all.c b/accel/xen/xen-all.c index 60b971d0a8..69aa7d018b 100644 --- a/accel/xen/xen-all.c +++ b/accel/xen/xen-all.c @@ -15,7 +15,8 @@ #include "hw/xen/xen-legacy-backend.h" #include "hw/xen/xen_pt.h" #include "chardev/char.h" -#include "sysemu/accel.h" +#include "qemu/accel.h" +#include "sysemu/cpus.h" #include "sysemu/xen.h" #include "sysemu/runstate.h" #include "migration/misc.h" @@ -121,7 +122,7 @@ static void xenstore_record_dm_state(struct xs_handle *xs, const char *state) } -static void xen_change_state_handler(void *opaque, int running, +static void xen_change_state_handler(void *opaque, bool running, RunState state) { if (running) { @@ -214,9 +215,24 @@ static const TypeInfo xen_accel_type = { .class_init = xen_accel_class_init, }; +static void xen_accel_ops_class_init(ObjectClass *oc, void *data) +{ + AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); + + ops->create_vcpu_thread = dummy_start_vcpu_thread; +} + +static const TypeInfo xen_accel_ops_type = { + .name = ACCEL_OPS_NAME("xen"), + + .parent = TYPE_ACCEL_OPS, + .class_init = xen_accel_ops_class_init, + .abstract = true, +}; + static void xen_type_init(void) { type_register_static(&xen_accel_type); + type_register_static(&xen_accel_ops_type); } - type_init(xen_type_init); diff --git a/audio/Makefile.objs b/audio/Makefile.objs deleted file mode 100644 index b4a4c11f31..0000000000 --- a/audio/Makefile.objs +++ /dev/null @@ -1,35 +0,0 @@ -common-obj-y = audio.o audio_legacy.o noaudio.o wavaudio.o mixeng.o -common-obj-$(CONFIG_SPICE) += spiceaudio.o -common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o -common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o -common-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o -common-obj-y += wavcapture.o - -coreaudio.o-libs := $(COREAUDIO_LIBS) -dsoundaudio.o-libs := $(DSOUND_LIBS) - -# alsa module -common-obj-$(CONFIG_AUDIO_ALSA) += alsa.mo -alsa.mo-objs = alsaaudio.o -alsa.mo-libs := $(ALSA_LIBS) - -# oss module -common-obj-$(CONFIG_AUDIO_OSS) += oss.mo -oss.mo-objs = ossaudio.o -oss.mo-libs := $(OSS_LIBS) - -# pulseaudio module -common-obj-$(CONFIG_AUDIO_PA) += pa.mo -pa.mo-objs = paaudio.o -pa.mo-libs := $(PULSE_LIBS) - -# sdl module -common-obj-$(CONFIG_AUDIO_SDL) += sdl.mo -sdl.mo-objs = sdlaudio.o -sdl.mo-cflags := $(SDL_CFLAGS) -sdl.mo-libs := $(SDL_LIBS) - -# jack module -common-obj-$(CONFIG_AUDIO_JACK) += jack.mo -jack.mo-objs = jackaudio.o -jack.mo-libs := $(JACK_LIBS) diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index a8e62542f9..fcc2f62864 100644 --- a/audio/alsaaudio.c +++ b/audio/alsaaudio.c @@ -278,32 +278,28 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness) case AUDIO_FORMAT_S16: if (endianness) { return SND_PCM_FORMAT_S16_BE; - } - else { + } else { return SND_PCM_FORMAT_S16_LE; } case AUDIO_FORMAT_U16: if (endianness) { return SND_PCM_FORMAT_U16_BE; - } - else { + } else { return SND_PCM_FORMAT_U16_LE; } case AUDIO_FORMAT_S32: if (endianness) { return SND_PCM_FORMAT_S32_BE; - } - else { + } else { return SND_PCM_FORMAT_S32_LE; } case AUDIO_FORMAT_U32: if (endianness) { return SND_PCM_FORMAT_U32_BE; - } - else { + } else { return SND_PCM_FORMAT_U32_LE; } @@ -599,7 +595,7 @@ static int alsa_open(bool in, struct alsa_params_req *req, } #ifdef DEBUG - alsa_dump_info(req, obt, obtfmt, pdo); + alsa_dump_info(req, obt, obtfmt, apdo); #endif return 0; @@ -722,8 +718,7 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl) alsa_logerr (err, "Could not stop %s\n", typ); return -1; } - } - else { + } else { err = snd_pcm_prepare (handle); if (err < 0) { alsa_logerr (err, "Could not prepare handle for %s\n", typ); @@ -929,6 +924,7 @@ static struct audio_pcm_ops alsa_pcm_ops = { .init_in = alsa_init_in, .fini_in = alsa_fini_in, .read = alsa_read, + .run_buffer_in = audio_generic_run_buffer_in, .enable_in = alsa_enable_in, }; diff --git a/audio/audio.c b/audio/audio.c index ce8c6dec5f..534278edfe 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -34,6 +34,7 @@ #include "qemu/module.h" #include "sysemu/replay.h" #include "sysemu/runstate.h" +#include "ui/qemu-spice.h" #include "trace.h" #define AUDIO_CAP "audio" @@ -103,9 +104,6 @@ const struct mixeng_volume nominal_volume = { static bool legacy_config = true; -#ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED -#error No its not -#else int audio_bug (const char *funcname, int cond) { if (cond) { @@ -118,25 +116,11 @@ int audio_bug (const char *funcname, int cond) AUD_log (NULL, "I am sorry\n"); } AUD_log (NULL, "Context:\n"); - -#if defined AUDIO_BREAKPOINT_ON_BUG -# if defined HOST_I386 -# if defined __GNUC__ - __asm__ ("int3"); -# elif defined _MSC_VER - _asm _emit 0xcc; -# else - abort (); -# endif -# else - abort (); -# endif -#endif + abort(); } return cond; } -#endif static inline int audio_bits_to_index (int bits) { @@ -360,8 +344,7 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len) if (info->is_signed || info->is_float) { memset(buf, 0x00, len * info->bytes_per_frame); - } - else { + } else { switch (info->bits) { case 8: memset(buf, 0x80, len * info->bytes_per_frame); @@ -600,8 +583,7 @@ static size_t audio_pcm_sw_get_rpos_in(SWVoiceIn *sw) rpos = hw->conv_buf->pos - live; if (rpos >= 0) { return rpos; - } - else { + } else { return hw->conv_buf->size + rpos; } } @@ -804,10 +786,14 @@ static int audio_is_timer_needed(AudioState *s) HWVoiceOut *hwo = NULL; while ((hwo = audio_pcm_hw_find_any_enabled_out(s, hwo))) { - if (!hwo->poll_mode) return 1; + if (!hwo->poll_mode) { + return 1; + } } while ((hwi = audio_pcm_hw_find_any_enabled_in(s, hwi))) { - if (!hwi->poll_mode) return 1; + if (!hwi->poll_mode) { + return 1; + } } return 0; } @@ -924,8 +910,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on) audio_reset_timer (s); } } - } - else { + } else { if (hw->enabled) { int nb_active = 0; @@ -972,8 +957,7 @@ void AUD_set_active_in (SWVoiceIn *sw, int on) } } sw->total_hw_samples_acquired = hw->total_samples_captured; - } - else { + } else { if (hw->enabled) { int nb_active = 0; @@ -1089,14 +1073,18 @@ static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live) size_t clipped = 0; while (live) { - size_t size, decr, proc; + size_t size = live * hw->info.bytes_per_frame; + size_t decr, proc; void *buf = hw->pcm_ops->get_buffer_out(hw, &size); - if (!buf || size == 0) { + + if (size == 0) { break; } decr = MIN(size / hw->info.bytes_per_frame, live); - audio_pcm_hw_clip_out(hw, buf, decr); + if (buf) { + audio_pcm_hw_clip_out(hw, buf, decr); + } proc = hw->pcm_ops->put_buffer_out(hw, buf, decr * hw->info.bytes_per_frame) / hw->info.bytes_per_frame; @@ -1144,7 +1132,7 @@ static void audio_run_out (AudioState *s) while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) { size_t played, live, prev_rpos, free; - int nb_live, cleanup_required; + int nb_live; live = audio_pcm_hw_get_live_out (hw, &nb_live); if (!nb_live) { @@ -1182,6 +1170,9 @@ static void audio_run_out (AudioState *s) } } } + if (hw->pcm_ops->run_buffer_out) { + hw->pcm_ops->run_buffer_out(hw); + } continue; } @@ -1203,7 +1194,6 @@ static void audio_run_out (AudioState *s) audio_capture_mix_and_clear (hw, prev_rpos, played); } - cleanup_required = 0; for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { if (!sw->active && sw->empty) { continue; @@ -1219,7 +1209,6 @@ static void audio_run_out (AudioState *s) if (!sw->total_hw_samples_mixed) { sw->empty = 1; - cleanup_required |= !sw->active && !sw->callback.fn; } if (sw->active) { @@ -1229,19 +1218,6 @@ static void audio_run_out (AudioState *s) } } } - - if (cleanup_required) { - SWVoiceOut *sw1; - - sw = hw->sw_head.lh_first; - while (sw) { - sw1 = sw->entries.le_next; - if (!sw->active && !sw->callback.fn) { - audio_close_out (sw); - } - sw = sw1; - } - } } } @@ -1250,6 +1226,10 @@ static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples) size_t conv = 0; STSampleBuffer *conv_buf = hw->conv_buf; + if (hw->pcm_ops->run_buffer_in) { + hw->pcm_ops->run_buffer_in(hw); + } + while (samples) { size_t proc; size_t size = samples * hw->info.bytes_per_frame; @@ -1257,7 +1237,6 @@ static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples) assert(size % hw->info.bytes_per_frame == 0); if (size == 0) { - hw->pcm_ops->put_buffer_in(hw, buf, size); break; } @@ -1391,14 +1370,11 @@ void audio_run(AudioState *s, const char *msg) #endif } -void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size) +void audio_generic_run_buffer_in(HWVoiceIn *hw) { - ssize_t start; - if (unlikely(!hw->buf_emul)) { - size_t calc_size = hw->conv_buf->size * hw->info.bytes_per_frame; - hw->buf_emul = g_malloc(calc_size); - hw->size_emul = calc_size; + hw->size_emul = hw->samples * hw->info.bytes_per_frame; + hw->buf_emul = g_malloc(hw->size_emul); hw->pos_emul = hw->pending_emul = 0; } @@ -1413,8 +1389,12 @@ void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size) break; } } +} + +void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size) +{ + ssize_t start = (ssize_t)hw->pos_emul - hw->pending_emul; - start = ((ssize_t) hw->pos_emul) - hw->pending_emul; if (start < 0) { start += hw->size_emul; } @@ -1456,10 +1436,8 @@ void audio_generic_run_buffer_out(HWVoiceOut *hw) void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size) { if (unlikely(!hw->buf_emul)) { - size_t calc_size = hw->mix_buf->size * hw->info.bytes_per_frame; - - hw->buf_emul = g_malloc(calc_size); - hw->size_emul = calc_size; + hw->size_emul = hw->samples * hw->info.bytes_per_frame; + hw->buf_emul = g_malloc(hw->size_emul); hw->pos_emul = hw->pending_emul = 0; } @@ -1481,22 +1459,58 @@ size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size) size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size) { - size_t dst_size, copy_size; - void *dst = hw->pcm_ops->get_buffer_out(hw, &dst_size); - copy_size = MIN(size, dst_size); + size_t total = 0; - memcpy(dst, buf, copy_size); - return hw->pcm_ops->put_buffer_out(hw, dst, copy_size); + while (total < size) { + size_t dst_size = size - total; + size_t copy_size, proc; + void *dst = hw->pcm_ops->get_buffer_out(hw, &dst_size); + + if (dst_size == 0) { + break; + } + + copy_size = MIN(size - total, dst_size); + if (dst) { + memcpy(dst, (char *)buf + total, copy_size); + } + proc = hw->pcm_ops->put_buffer_out(hw, dst, copy_size); + total += proc; + + if (proc == 0 || proc < copy_size) { + break; + } + } + + if (hw->pcm_ops->run_buffer_out) { + hw->pcm_ops->run_buffer_out(hw); + } + + return total; } size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size) { - void *src = hw->pcm_ops->get_buffer_in(hw, &size); + size_t total = 0; - memcpy(buf, src, size); - hw->pcm_ops->put_buffer_in(hw, src, size); + if (hw->pcm_ops->run_buffer_in) { + hw->pcm_ops->run_buffer_in(hw); + } - return size; + while (total < size) { + size_t src_size = size - total; + void *src = hw->pcm_ops->get_buffer_in(hw, &src_size); + + if (src_size == 0) { + break; + } + + memcpy((char *)buf + total, src, src_size); + hw->pcm_ops->put_buffer_in(hw, src, src_size); + total += src_size; + } + + return total; } static int audio_driver_init(AudioState *s, struct audio_driver *drv, @@ -1518,8 +1532,7 @@ static int audio_driver_init(AudioState *s, struct audio_driver *drv, audio_init_nb_voices_in(s, drv); s->drv = drv; return 0; - } - else { + } else { if (msg) { dolog("Could not init `%s' audio driver\n", drv->name); } @@ -1527,7 +1540,7 @@ static int audio_driver_init(AudioState *s, struct audio_driver *drv, } } -static void audio_vm_change_state_handler (void *opaque, int running, +static void audio_vm_change_state_handler (void *opaque, bool running, RunState state) { AudioState *s = opaque; @@ -1549,13 +1562,6 @@ static void audio_vm_change_state_handler (void *opaque, int running, audio_reset_timer (s); } -static bool is_cleaning_up; - -bool audio_is_cleaning_up(void) -{ - return is_cleaning_up; -} - static void free_audio_state(AudioState *s) { HWVoiceOut *hwo, *hwon; @@ -1608,7 +1614,6 @@ static void free_audio_state(AudioState *s) void audio_cleanup(void) { - is_cleaning_up = true; while (!QTAILQ_EMPTY(&audio_states)) { AudioState *s = QTAILQ_FIRST(&audio_states); QTAILQ_REMOVE(&audio_states, s, list); @@ -1658,6 +1663,23 @@ static AudioState *audio_init(Audiodev *dev, const char *name) /* silence gcc warning about uninitialized variable */ AudiodevListHead head = QSIMPLEQ_HEAD_INITIALIZER(head); + if (using_spice) { + /* + * When using spice allow the spice audio driver being picked + * as default. + * + * Temporary hack. Using audio devices without explicit + * audiodev= property is already deprecated. Same goes for + * the -soundhw switch. Once this support gets finally + * removed we can also drop the concept of a default audio + * backend and this can go away. + */ + driver = audio_driver_lookup("spice"); + if (driver) { + driver->can_be_default = 1; + } + } + if (dev) { /* -audiodev option */ legacy_config = false; @@ -1674,7 +1696,7 @@ static AudioState *audio_init(Audiodev *dev, const char *name) head = audio_handle_legacy_opts(); /* * In case of legacy initialization, all Audiodevs in the list will have - * the same configuration (except the driver), so it does't matter which + * the same configuration (except the driver), so it doesn't matter which * one we chose. We need an Audiodev to set up AudioState before we can * init a driver. Also note that dev at this point is still in the * list. @@ -1825,8 +1847,7 @@ CaptureVoiceOut *AUD_add_capture( if (cap) { QLIST_INSERT_HEAD (&cap->cb_head, cb, entries); return cap; - } - else { + } else { HWVoiceOut *hw; CaptureVoiceOut *cap; @@ -1972,7 +1993,7 @@ void audio_create_pdos(Audiodev *dev) CASE(JACK, jack, Jack); CASE(OSS, oss, Oss); CASE(PA, pa, Pa); - CASE(SDL, sdl, ); + CASE(SDL, sdl, Sdl); CASE(SPICE, spice, ); CASE(WAV, wav, ); diff --git a/audio/audio.h b/audio/audio.h index b883ebfb1f..c8bde536b5 100644 --- a/audio/audio.h +++ b/audio/audio.h @@ -28,6 +28,7 @@ #include "qemu/queue.h" #include "qapi/qapi-types-audio.h" #include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" typedef void (*audio_callback_fn) (void *opaque, int avail); @@ -160,7 +161,6 @@ static inline void *advance (void *p, int incr) int wav_start_capture(AudioState *state, CaptureState *s, const char *path, int freq, int bits, int nchannels); -bool audio_is_cleaning_up(void); void audio_cleanup(void); void audio_sample_to_uint64(const void *samples, int pos, diff --git a/audio/audio_int.h b/audio/audio_int.h index 4775857bf2..06f0913835 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -172,12 +172,14 @@ struct audio_pcm_ops { int (*init_in) (HWVoiceIn *hw, audsettings *as, void *drv_opaque); void (*fini_in) (HWVoiceIn *hw); size_t (*read) (HWVoiceIn *hw, void *buf, size_t size); + void (*run_buffer_in)(HWVoiceIn *hw); void *(*get_buffer_in)(HWVoiceIn *hw, size_t *size); void (*put_buffer_in)(HWVoiceIn *hw, void *buf, size_t size); void (*enable_in)(HWVoiceIn *hw, bool enable); void (*volume_in)(HWVoiceIn *hw, Volume *vol); }; +void audio_generic_run_buffer_in(HWVoiceIn *hw); void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size); void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size); void audio_generic_run_buffer_out(HWVoiceOut *hw); diff --git a/audio/audio_legacy.c b/audio/audio_legacy.c index ffdbd0bcce..0fe827b057 100644 --- a/audio/audio_legacy.c +++ b/audio/audio_legacy.c @@ -286,7 +286,8 @@ static void handle_sdl(Audiodev *dev) { /* SDL is output only */ get_samples_to_usecs("QEMU_SDL_SAMPLES", &dev->u.sdl.out->buffer_length, - &dev->u.sdl.out->has_buffer_length, dev->u.sdl.out); + &dev->u.sdl.out->has_buffer_length, + qapi_AudiodevSdlPerDirectionOptions_base(dev->u.sdl.out)); } /* wav */ diff --git a/audio/audio_template.h b/audio/audio_template.h index 8dd48ce14e..c6714946aa 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -47,8 +47,7 @@ static void glue(audio_init_nb_voices_, TYPE)(AudioState *s, #ifdef DAC dolog ("Driver `%s' does not support " NAME "\n", drv->name); #endif - } - else { + } else { dolog ("Driver `%s' does not support %d " NAME " voices, max %d\n", drv->name, glue (s->nb_hw_voices_, TYPE), @@ -204,13 +203,13 @@ static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp) if (!hw->sw_head.lh_first) { #ifdef DAC - audio_detach_capture (hw); + audio_detach_capture(hw); #endif - QLIST_REMOVE (hw, entries); - glue (hw->pcm_ops->fini_, TYPE) (hw); - glue (s->nb_hw_voices_, TYPE) += 1; - glue (audio_pcm_hw_free_resources_ ,TYPE) (hw); - g_free (hw); + QLIST_REMOVE(hw, entries); + glue(hw->pcm_ops->fini_, TYPE) (hw); + glue(s->nb_hw_voices_, TYPE) += 1; + glue(audio_pcm_hw_free_resources_ , TYPE) (hw); + g_free(hw); *hwp = NULL; } } @@ -337,7 +336,7 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev) case AUDIODEV_DRIVER_PA: return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.TYPE); case AUDIODEV_DRIVER_SDL: - return dev->u.sdl.TYPE; + return qapi_AudiodevSdlPerDirectionOptions_base(dev->u.sdl.TYPE); case AUDIODEV_DRIVER_SPICE: return dev->u.spice.TYPE; case AUDIODEV_DRIVER_WAV: @@ -387,8 +386,7 @@ static SW *glue(audio_pcm_create_voice_pair_, TYPE)( if (pdo->fixed_settings) { hw_as = audiodev_to_audsettings(pdo); - } - else { + } else { hw_as = *as; } @@ -498,8 +496,7 @@ SW *glue (AUD_open_, TYPE) ( if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, as)) { goto fail; } - } - else { + } else { sw = glue(audio_pcm_create_voice_pair_, TYPE)(s, name, as); if (!sw) { dolog ("Failed to create voice `%s'\n", name); @@ -553,8 +550,7 @@ uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts) if (cur_ts >= old_ts) { delta = cur_ts - old_ts; - } - else { + } else { delta = UINT64_MAX - old_ts + cur_ts; } diff --git a/audio/audio_win_int.c b/audio/audio_win_int.c index b938fd667b..5ea8157dfc 100644 --- a/audio/audio_win_int.c +++ b/audio/audio_win_int.c @@ -5,6 +5,7 @@ #define AUDIO_CAP "win-int" #include +#include #include #include "audio.h" @@ -16,7 +17,6 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx, { memset (wfx, 0, sizeof (*wfx)); - wfx->wFormatTag = WAVE_FORMAT_PCM; wfx->nChannels = as->nchannels; wfx->nSamplesPerSec = as->freq; wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2); @@ -26,11 +26,13 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx, switch (as->fmt) { case AUDIO_FORMAT_S8: case AUDIO_FORMAT_U8: + wfx->wFormatTag = WAVE_FORMAT_PCM; wfx->wBitsPerSample = 8; break; case AUDIO_FORMAT_S16: case AUDIO_FORMAT_U16: + wfx->wFormatTag = WAVE_FORMAT_PCM; wfx->wBitsPerSample = 16; wfx->nAvgBytesPerSec <<= 1; wfx->nBlockAlign <<= 1; @@ -38,13 +40,21 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx, case AUDIO_FORMAT_S32: case AUDIO_FORMAT_U32: + wfx->wFormatTag = WAVE_FORMAT_PCM; + wfx->wBitsPerSample = 32; + wfx->nAvgBytesPerSec <<= 2; + wfx->nBlockAlign <<= 2; + break; + + case AUDIO_FORMAT_F32: + wfx->wFormatTag = WAVE_FORMAT_IEEE_FLOAT; wfx->wBitsPerSample = 32; wfx->nAvgBytesPerSec <<= 2; wfx->nBlockAlign <<= 2; break; default: - dolog ("Internal logic error: Bad audio format %d\n", as->freq); + dolog("Internal logic error: Bad audio format %d\n", as->fmt); return -1; } @@ -54,12 +64,6 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx, int waveformat_to_audio_settings (WAVEFORMATEX *wfx, struct audsettings *as) { - if (wfx->wFormatTag != WAVE_FORMAT_PCM) { - dolog ("Invalid wave format, tag is not PCM, but %d\n", - wfx->wFormatTag); - return -1; - } - if (!wfx->nSamplesPerSec) { dolog ("Invalid wave format, frequency is zero\n"); return -1; @@ -83,23 +87,42 @@ int waveformat_to_audio_settings (WAVEFORMATEX *wfx, return -1; } - switch (wfx->wBitsPerSample) { - case 8: - as->fmt = AUDIO_FORMAT_U8; - break; + if (wfx->wFormatTag == WAVE_FORMAT_PCM) { + switch (wfx->wBitsPerSample) { + case 8: + as->fmt = AUDIO_FORMAT_U8; + break; - case 16: - as->fmt = AUDIO_FORMAT_S16; - break; + case 16: + as->fmt = AUDIO_FORMAT_S16; + break; - case 32: - as->fmt = AUDIO_FORMAT_S32; - break; + case 32: + as->fmt = AUDIO_FORMAT_S32; + break; - default: - dolog ("Invalid wave format, bits per sample is not " - "8, 16 or 32, but %d\n", - wfx->wBitsPerSample); + default: + dolog("Invalid PCM wave format, bits per sample is not " + "8, 16 or 32, but %d\n", + wfx->wBitsPerSample); + return -1; + } + } else if (wfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { + switch (wfx->wBitsPerSample) { + case 32: + as->fmt = AUDIO_FORMAT_F32; + break; + + default: + dolog("Invalid IEEE_FLOAT wave format, bits per sample is not " + "32, but %d\n", + wfx->wBitsPerSample); + return -1; + } + } else { + dolog("Invalid wave format, tag is not PCM and not IEEE_FLOAT, " + "but %d\n", + wfx->wFormatTag); return -1; } diff --git a/audio/coreaudio.c b/audio/coreaudio.c index 4b4365660f..578ec9b8b2 100644 --- a/audio/coreaudio.c +++ b/audio/coreaudio.c @@ -32,33 +32,30 @@ #define AUDIO_CAP "coreaudio" #include "audio_int.h" -#ifndef MAC_OS_X_VERSION_10_6 -#define MAC_OS_X_VERSION_10_6 1060 -#endif - typedef struct coreaudioVoiceOut { HWVoiceOut hw; pthread_mutex_t mutex; AudioDeviceID outputDeviceID; + int frameSizeSetting; + uint32_t bufferCount; UInt32 audioDevicePropertyBufferFrameSize; AudioStreamBasicDescription outputStreamBasicDescription; AudioDeviceIOProcID ioprocid; + bool enabled; } coreaudioVoiceOut; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 -/* The APIs used here only become available from 10.6 */ +static const AudioObjectPropertyAddress voice_addr = { + kAudioHardwarePropertyDefaultOutputDevice, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster +}; static OSStatus coreaudio_get_voice(AudioDeviceID *id) { UInt32 size = sizeof(*id); - AudioObjectPropertyAddress addr = { - kAudioHardwarePropertyDefaultOutputDevice, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster - }; return AudioObjectGetPropertyData(kAudioObjectSystemObject, - &addr, + &voice_addr, 0, NULL, &size, @@ -169,108 +166,12 @@ static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result) &size, result); } -#else -/* Legacy versions of functions using deprecated APIs */ - -static OSStatus coreaudio_get_voice(AudioDeviceID *id) -{ - UInt32 size = sizeof(*id); - - return AudioHardwareGetProperty( - kAudioHardwarePropertyDefaultOutputDevice, - &size, - id); -} - -static OSStatus coreaudio_get_framesizerange(AudioDeviceID id, - AudioValueRange *framerange) -{ - UInt32 size = sizeof(*framerange); - - return AudioDeviceGetProperty( - id, - 0, - 0, - kAudioDevicePropertyBufferFrameSizeRange, - &size, - framerange); -} - -static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize) -{ - UInt32 size = sizeof(*framesize); - - return AudioDeviceGetProperty( - id, - 0, - false, - kAudioDevicePropertyBufferFrameSize, - &size, - framesize); -} - -static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize) -{ - UInt32 size = sizeof(*framesize); - - return AudioDeviceSetProperty( - id, - NULL, - 0, - false, - kAudioDevicePropertyBufferFrameSize, - size, - framesize); -} - -static OSStatus coreaudio_get_streamformat(AudioDeviceID id, - AudioStreamBasicDescription *d) -{ - UInt32 size = sizeof(*d); - - return AudioDeviceGetProperty( - id, - 0, - false, - kAudioDevicePropertyStreamFormat, - &size, - d); -} - -static OSStatus coreaudio_set_streamformat(AudioDeviceID id, - AudioStreamBasicDescription *d) -{ - UInt32 size = sizeof(*d); - - return AudioDeviceSetProperty( - id, - 0, - 0, - 0, - kAudioDevicePropertyStreamFormat, - size, - d); -} - -static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result) -{ - UInt32 size = sizeof(*result); - - return AudioDeviceGetProperty( - id, - 0, - 0, - kAudioDevicePropertyDeviceIsRunning, - &size, - result); -} -#endif static void coreaudio_logstatus (OSStatus status) { const char *str = "BUG"; - switch(status) { + switch (status) { case kAudioHardwareNoError: str = "kAudioHardwareNoError"; break; @@ -356,17 +257,8 @@ static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 ( coreaudio_logstatus (status); } -static inline UInt32 isPlaying (AudioDeviceID outputDeviceID) -{ - OSStatus status; - UInt32 result = 0; - status = coreaudio_get_isrunning(outputDeviceID, &result); - if (status != kAudioHardwareNoError) { - coreaudio_logerr(status, - "Could not determine whether Device is playing\n"); - } - return result; -} +#define coreaudio_playback_logerr(status, ...) \ + coreaudio_logerr2(status, "playback", __VA_ARGS__) static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name) { @@ -421,12 +313,12 @@ COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size), /* callback to feed audiooutput buffer */ static OSStatus audioDeviceIOProc( AudioDeviceID inDevice, - const AudioTimeStamp* inNow, - const AudioBufferList* inInputData, - const AudioTimeStamp* inInputTime, - AudioBufferList* outOutputData, - const AudioTimeStamp* inOutputTime, - void* hwptr) + const AudioTimeStamp *inNow, + const AudioBufferList *inInputData, + const AudioTimeStamp *inInputTime, + AudioBufferList *outOutputData, + const AudioTimeStamp *inOutputTime, + void *hwptr) { UInt32 frameCount, pending_frames; void *out = outOutputData->mBuffers[0].mData; @@ -439,6 +331,11 @@ static OSStatus audioDeviceIOProc( return 0; } + if (inDevice != core->outputDeviceID) { + coreaudio_unlock (core, "audioDeviceIOProc(old device)"); + return 0; + } + frameCount = core->audioDevicePropertyBufferFrameSize; pending_frames = hw->pending_emul / hw->info.bytes_per_frame; @@ -471,156 +368,298 @@ static OSStatus audioDeviceIOProc( return 0; } -static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, - void *drv_opaque) +static OSStatus init_out_device(coreaudioVoiceOut *core) { OSStatus status; - coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; - int err; - const char *typ = "playback"; AudioValueRange frameRange; - Audiodev *dev = drv_opaque; - AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out; - int frames; - struct audsettings fake_as; - - /* create mutex */ - err = pthread_mutex_init(&core->mutex, NULL); - if (err) { - dolog("Could not create mutex\nReason: %s\n", strerror (err)); - return -1; - } - - fake_as = *as; - as = &fake_as; - as->fmt = AUDIO_FORMAT_F32; - audio_pcm_init_info (&hw->info, as); status = coreaudio_get_voice(&core->outputDeviceID); if (status != kAudioHardwareNoError) { - coreaudio_logerr2 (status, typ, - "Could not get default output Device\n"); - return -1; + coreaudio_playback_logerr (status, + "Could not get default output Device\n"); + return status; } if (core->outputDeviceID == kAudioDeviceUnknown) { - dolog ("Could not initialize %s - Unknown Audiodevice\n", typ); - return -1; + dolog ("Could not initialize playback - Unknown Audiodevice\n"); + return status; } /* get minimum and maximum buffer frame sizes */ status = coreaudio_get_framesizerange(core->outputDeviceID, &frameRange); + if (status == kAudioHardwareBadObjectError) { + return 0; + } if (status != kAudioHardwareNoError) { - coreaudio_logerr2 (status, typ, - "Could not get device buffer frame range\n"); - return -1; + coreaudio_playback_logerr (status, + "Could not get device buffer frame range\n"); + return status; } - frames = audio_buffer_frames( - qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610); - if (frameRange.mMinimum > frames) { + if (frameRange.mMinimum > core->frameSizeSetting) { core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum; dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum); - } else if (frameRange.mMaximum < frames) { + } else if (frameRange.mMaximum < core->frameSizeSetting) { core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum; dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum); - } - else { - core->audioDevicePropertyBufferFrameSize = frames; + } else { + core->audioDevicePropertyBufferFrameSize = core->frameSizeSetting; } /* set Buffer Frame Size */ status = coreaudio_set_framesize(core->outputDeviceID, &core->audioDevicePropertyBufferFrameSize); + if (status == kAudioHardwareBadObjectError) { + return 0; + } if (status != kAudioHardwareNoError) { - coreaudio_logerr2 (status, typ, - "Could not set device buffer frame size %" PRIu32 "\n", - (uint32_t)core->audioDevicePropertyBufferFrameSize); - return -1; + coreaudio_playback_logerr (status, + "Could not set device buffer frame size %" PRIu32 "\n", + (uint32_t)core->audioDevicePropertyBufferFrameSize); + return status; } /* get Buffer Frame Size */ status = coreaudio_get_framesize(core->outputDeviceID, &core->audioDevicePropertyBufferFrameSize); - if (status != kAudioHardwareNoError) { - coreaudio_logerr2 (status, typ, - "Could not get device buffer frame size\n"); - return -1; + if (status == kAudioHardwareBadObjectError) { + return 0; } - hw->samples = (cpdo->has_buffer_count ? cpdo->buffer_count : 4) * - core->audioDevicePropertyBufferFrameSize; + if (status != kAudioHardwareNoError) { + coreaudio_playback_logerr (status, + "Could not get device buffer frame size\n"); + return status; + } + core->hw.samples = core->bufferCount * core->audioDevicePropertyBufferFrameSize; /* get StreamFormat */ status = coreaudio_get_streamformat(core->outputDeviceID, &core->outputStreamBasicDescription); + if (status == kAudioHardwareBadObjectError) { + return 0; + } if (status != kAudioHardwareNoError) { - coreaudio_logerr2 (status, typ, - "Could not get Device Stream properties\n"); + coreaudio_playback_logerr (status, + "Could not get Device Stream properties\n"); core->outputDeviceID = kAudioDeviceUnknown; - return -1; + return status; } /* set Samplerate */ - core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq; - status = coreaudio_set_streamformat(core->outputDeviceID, &core->outputStreamBasicDescription); + if (status == kAudioHardwareBadObjectError) { + return 0; + } if (status != kAudioHardwareNoError) { - coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n", - as->freq); + coreaudio_playback_logerr (status, + "Could not set samplerate %lf\n", + core->outputStreamBasicDescription.mSampleRate); core->outputDeviceID = kAudioDeviceUnknown; - return -1; + return status; } /* set Callback */ core->ioprocid = NULL; status = AudioDeviceCreateIOProcID(core->outputDeviceID, audioDeviceIOProc, - hw, + &core->hw, &core->ioprocid); - if (status != kAudioHardwareNoError || core->ioprocid == NULL) { - coreaudio_logerr2 (status, typ, "Could not set IOProc\n"); - core->outputDeviceID = kAudioDeviceUnknown; - return -1; + if (status == kAudioHardwareBadDeviceError) { + return 0; } - - /* start Playback */ - if (!isPlaying(core->outputDeviceID)) { - status = AudioDeviceStart(core->outputDeviceID, core->ioprocid); - if (status != kAudioHardwareNoError) { - coreaudio_logerr2 (status, typ, "Could not start playback\n"); - AudioDeviceDestroyIOProcID(core->outputDeviceID, core->ioprocid); - core->outputDeviceID = kAudioDeviceUnknown; - return -1; - } + if (status != kAudioHardwareNoError || core->ioprocid == NULL) { + coreaudio_playback_logerr (status, "Could not set IOProc\n"); + core->outputDeviceID = kAudioDeviceUnknown; + return status; } return 0; } +static void fini_out_device(coreaudioVoiceOut *core) +{ + OSStatus status; + UInt32 isrunning; + + /* stop playback */ + status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning); + if (status != kAudioHardwareBadObjectError) { + if (status != kAudioHardwareNoError) { + coreaudio_logerr(status, + "Could not determine whether Device is playing\n"); + } + + if (isrunning) { + status = AudioDeviceStop(core->outputDeviceID, core->ioprocid); + if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) { + coreaudio_logerr(status, "Could not stop playback\n"); + } + } + } + + /* remove callback */ + status = AudioDeviceDestroyIOProcID(core->outputDeviceID, + core->ioprocid); + if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) { + coreaudio_logerr(status, "Could not remove IOProc\n"); + } + core->outputDeviceID = kAudioDeviceUnknown; +} + +static void update_device_playback_state(coreaudioVoiceOut *core) +{ + OSStatus status; + UInt32 isrunning; + + status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning); + if (status != kAudioHardwareNoError) { + if (status != kAudioHardwareBadObjectError) { + coreaudio_logerr(status, + "Could not determine whether Device is playing\n"); + } + + return; + } + + if (core->enabled) { + /* start playback */ + if (!isrunning) { + status = AudioDeviceStart(core->outputDeviceID, core->ioprocid); + if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) { + coreaudio_logerr (status, "Could not resume playback\n"); + } + } + } else { + /* stop playback */ + if (isrunning) { + status = AudioDeviceStop(core->outputDeviceID, + core->ioprocid); + if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) { + coreaudio_logerr(status, "Could not pause playback\n"); + } + } + } +} + +static OSStatus handle_voice_change( + AudioObjectID in_object_id, + UInt32 in_number_addresses, + const AudioObjectPropertyAddress *in_addresses, + void *in_client_data) +{ + OSStatus status; + coreaudioVoiceOut *core = in_client_data; + + if (coreaudio_lock(core, __func__)) { + abort(); + } + + if (core->outputDeviceID) { + fini_out_device(core); + } + + status = init_out_device(core); + if (!status) { + update_device_playback_state(core); + } + + coreaudio_unlock (core, __func__); + return status; +} + +static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, + void *drv_opaque) +{ + OSStatus status; + coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; + int err; + Audiodev *dev = drv_opaque; + AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out; + struct audsettings obt_as; + + /* create mutex */ + err = pthread_mutex_init(&core->mutex, NULL); + if (err) { + dolog("Could not create mutex\nReason: %s\n", strerror (err)); + goto mutex_error; + } + + if (coreaudio_lock(core, __func__)) { + goto lock_error; + } + + obt_as = *as; + as = &obt_as; + as->fmt = AUDIO_FORMAT_F32; + audio_pcm_init_info (&hw->info, as); + + core->frameSizeSetting = audio_buffer_frames( + qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610); + + core->bufferCount = cpdo->has_buffer_count ? cpdo->buffer_count : 4; + core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq; + + status = AudioObjectAddPropertyListener(kAudioObjectSystemObject, + &voice_addr, handle_voice_change, + core); + if (status != kAudioHardwareNoError) { + coreaudio_playback_logerr (status, + "Could not listen to voice property change\n"); + goto listener_error; + } + + if (init_out_device(core)) { + goto device_error; + } + + coreaudio_unlock(core, __func__); + return 0; + +device_error: + status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, + &voice_addr, + handle_voice_change, + core); + if (status != kAudioHardwareNoError) { + coreaudio_playback_logerr(status, + "Could not remove voice property change listener\n"); + } + +listener_error: + coreaudio_unlock(core, __func__); + +lock_error: + err = pthread_mutex_destroy(&core->mutex); + if (err) { + dolog("Could not destroy mutex\nReason: %s\n", strerror (err)); + } + +mutex_error: + return -1; +} + static void coreaudio_fini_out (HWVoiceOut *hw) { OSStatus status; int err; coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; - if (!audio_is_cleaning_up()) { - /* stop playback */ - if (isPlaying(core->outputDeviceID)) { - status = AudioDeviceStop(core->outputDeviceID, core->ioprocid); - if (status != kAudioHardwareNoError) { - coreaudio_logerr (status, "Could not stop playback\n"); - } - } - - /* remove callback */ - status = AudioDeviceDestroyIOProcID(core->outputDeviceID, - core->ioprocid); - if (status != kAudioHardwareNoError) { - coreaudio_logerr (status, "Could not remove IOProc\n"); - } + if (coreaudio_lock(core, __func__)) { + abort(); } - core->outputDeviceID = kAudioDeviceUnknown; + + status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, + &voice_addr, + handle_voice_change, + core); + if (status != kAudioHardwareNoError) { + coreaudio_logerr(status, "Could not remove voice property change listener\n"); + } + + fini_out_device(core); + + coreaudio_unlock(core, __func__); /* destroy mutex */ err = pthread_mutex_destroy(&core->mutex); @@ -631,29 +670,16 @@ static void coreaudio_fini_out (HWVoiceOut *hw) static void coreaudio_enable_out(HWVoiceOut *hw, bool enable) { - OSStatus status; coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; - if (enable) { - /* start playback */ - if (!isPlaying(core->outputDeviceID)) { - status = AudioDeviceStart(core->outputDeviceID, core->ioprocid); - if (status != kAudioHardwareNoError) { - coreaudio_logerr (status, "Could not resume playback\n"); - } - } - } else { - /* stop playback */ - if (!audio_is_cleaning_up()) { - if (isPlaying(core->outputDeviceID)) { - status = AudioDeviceStop(core->outputDeviceID, - core->ioprocid); - if (status != kAudioHardwareNoError) { - coreaudio_logerr (status, "Could not pause playback\n"); - } - } - } + if (coreaudio_lock(core, __func__)) { + abort(); } + + core->enabled = enable; + update_device_playback_state(core); + + coreaudio_unlock(core, __func__); } static void *coreaudio_audio_init(Audiodev *dev) diff --git a/audio/dsound_template.h b/audio/dsound_template.h index 9c5ce625ab..0678f2de38 100644 --- a/audio/dsound_template.h +++ b/audio/dsound_template.h @@ -205,7 +205,7 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, NULL ); #else - bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2; + bd.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2; hr = IDirectSound_CreateSoundBuffer ( s->dsound, &bd, diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c index 4cdf19ab67..cfc79c129e 100644 --- a/audio/dsoundaudio.c +++ b/audio/dsoundaudio.c @@ -89,7 +89,9 @@ static void dsound_log_hresult (HRESULT hr) #endif #ifdef DSERR_ALLOCATED case DSERR_ALLOCATED: - str = "The request failed because resources, such as a priority level, were already in use by another caller"; + str = "The request failed because resources, " + "such as a priority level, were already in use " + "by another caller"; break; #endif #ifdef DSERR_ALREADYINITIALIZED @@ -104,7 +106,8 @@ static void dsound_log_hresult (HRESULT hr) #endif #ifdef DSERR_BADSENDBUFFERGUID case DSERR_BADSENDBUFFERGUID: - str = "The GUID specified in an audiopath file does not match a valid mix-in buffer"; + str = "The GUID specified in an audiopath file " + "does not match a valid mix-in buffer"; break; #endif #ifdef DSERR_BUFFERLOST @@ -114,26 +117,35 @@ static void dsound_log_hresult (HRESULT hr) #endif #ifdef DSERR_BUFFERTOOSMALL case DSERR_BUFFERTOOSMALL: - str = "The buffer size is not great enough to enable effects processing"; + str = "The buffer size is not great enough to " + "enable effects processing"; break; #endif #ifdef DSERR_CONTROLUNAVAIL case DSERR_CONTROLUNAVAIL: - str = "The buffer control (volume, pan, and so on) requested by the caller is not available. Controls must be specified when the buffer is created, using the dwFlags member of DSBUFFERDESC"; + str = "The buffer control (volume, pan, and so on) " + "requested by the caller is not available. " + "Controls must be specified when the buffer is created, " + "using the dwFlags member of DSBUFFERDESC"; break; #endif #ifdef DSERR_DS8_REQUIRED case DSERR_DS8_REQUIRED: - str = "A DirectSound object of class CLSID_DirectSound8 or later is required for the requested functionality. For more information, see IDirectSound8 Interface"; + str = "A DirectSound object of class CLSID_DirectSound8 or later " + "is required for the requested functionality. " + "For more information, see IDirectSound8 Interface"; break; #endif #ifdef DSERR_FXUNAVAILABLE case DSERR_FXUNAVAILABLE: - str = "The effects requested could not be found on the system, or they are in the wrong order or in the wrong location; for example, an effect expected in hardware was found in software"; + str = "The effects requested could not be found on the system, " + "or they are in the wrong order or in the wrong location; " + "for example, an effect expected in hardware " + "was found in software"; break; #endif #ifdef DSERR_GENERIC - case DSERR_GENERIC : + case DSERR_GENERIC: str = "An undetermined error occurred inside the DirectSound subsystem"; break; #endif @@ -154,7 +166,8 @@ static void dsound_log_hresult (HRESULT hr) #endif #ifdef DSERR_NODRIVER case DSERR_NODRIVER: - str = "No sound driver is available for use, or the given GUID is not a valid DirectSound device ID"; + str = "No sound driver is available for use, " + "or the given GUID is not a valid DirectSound device ID"; break; #endif #ifdef DSERR_NOINTERFACE @@ -169,12 +182,14 @@ static void dsound_log_hresult (HRESULT hr) #endif #ifdef DSERR_OTHERAPPHASPRIO case DSERR_OTHERAPPHASPRIO: - str = "Another application has a higher priority level, preventing this call from succeeding"; + str = "Another application has a higher priority level, " + "preventing this call from succeeding"; break; #endif #ifdef DSERR_OUTOFMEMORY case DSERR_OUTOFMEMORY: - str = "The DirectSound subsystem could not allocate sufficient memory to complete the caller's request"; + str = "The DirectSound subsystem could not allocate " + "sufficient memory to complete the caller's request"; break; #endif #ifdef DSERR_PRIOLEVELNEEDED @@ -189,7 +204,9 @@ static void dsound_log_hresult (HRESULT hr) #endif #ifdef DSERR_UNINITIALIZED case DSERR_UNINITIALIZED: - str = "The Initialize method has not been called or has not been called successfully before other methods were called"; + str = "The Initialize method has not been called " + "or has not been called successfully " + "before other methods were called"; break; #endif #ifdef DSERR_UNSUPPORTED @@ -198,7 +215,7 @@ static void dsound_log_hresult (HRESULT hr) break; #endif default: - AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT %#lx)\n", hr); + AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT 0x%lx)\n", hr); return; } @@ -342,12 +359,12 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb, dsound_unlock_out (dsb, p1, p2, blen1, blen2); } -static int dsound_open (dsound *s) +static int dsound_set_cooperative_level(dsound *s) { HRESULT hr; HWND hwnd; - hwnd = GetForegroundWindow (); + hwnd = GetDesktopWindow(); hr = IDirectSound_SetCooperativeLevel ( s->dsound, hwnd, @@ -404,8 +421,7 @@ static void dsound_enable_out(HWVoiceOut *hw, bool enable) dsound_logerr (hr, "Could not stop playing buffer\n"); return; } - } - else { + } else { dolog ("warning: Voice is not playing\n"); } } @@ -509,8 +525,7 @@ static void dsound_enable_in(HWVoiceIn *hw, bool enable) dsound_logerr (hr, "Could not stop capturing\n"); return; } - } - else { + } else { dolog ("warning: Voice is not capturing\n"); } } @@ -659,8 +674,7 @@ static void *dsound_audio_init(Audiodev *dev) ); if (FAILED (hr)) { dsound_logerr (hr, "Could not create DirectSoundCapture instance\n"); - } - else { + } else { hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL); if (FAILED (hr)) { dsound_logerr (hr, "Could not initialize DirectSoundCapture\n"); @@ -673,7 +687,7 @@ static void *dsound_audio_init(Audiodev *dev) } } - err = dsound_open (s); + err = dsound_set_cooperative_level(s); if (err) { dsound_audio_fini (s); return NULL; diff --git a/audio/jackaudio.c b/audio/jackaudio.c index 72ed7c4929..3031c4e29b 100644 --- a/audio/jackaudio.c +++ b/audio/jackaudio.c @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include "qemu/module.h" #include "qemu/atomic.h" +#include "qemu/main-loop.h" #include "qemu-common.h" #include "audio.h" @@ -63,6 +64,7 @@ typedef struct QJackClient { QJackState state; jack_client_t *client; jack_nframes_t freq; + QEMUBH *shutdown_bh; struct QJack *j; int nchannels; @@ -87,6 +89,7 @@ QJackIn; static int qjack_client_init(QJackClient *c); static void qjack_client_connect_ports(QJackClient *c); static void qjack_client_fini(QJackClient *c); +static QemuMutex qjack_shutdown_lock; static void qjack_buffer_create(QJackBuffer *buffer, int channels, int frames) { @@ -104,7 +107,7 @@ static void qjack_buffer_create(QJackBuffer *buffer, int channels, int frames) static void qjack_buffer_clear(QJackBuffer *buffer) { assert(buffer->data); - atomic_store_release(&buffer->used, 0); + qatomic_store_release(&buffer->used, 0); buffer->rptr = 0; buffer->wptr = 0; } @@ -129,7 +132,7 @@ static int qjack_buffer_write(QJackBuffer *buffer, float *data, int size) assert(buffer->data); const int samples = size / sizeof(float); int frames = samples / buffer->channels; - const int avail = buffer->frames - atomic_load_acquire(&buffer->used); + const int avail = buffer->frames - qatomic_load_acquire(&buffer->used); if (frames > avail) { frames = avail; @@ -153,7 +156,7 @@ static int qjack_buffer_write(QJackBuffer *buffer, float *data, int size) buffer->wptr = wptr; - atomic_add(&buffer->used, frames); + qatomic_add(&buffer->used, frames); return frames * buffer->channels * sizeof(float); }; @@ -161,7 +164,7 @@ static int qjack_buffer_write(QJackBuffer *buffer, float *data, int size) static int qjack_buffer_write_l(QJackBuffer *buffer, float **dest, int frames) { assert(buffer->data); - const int avail = buffer->frames - atomic_load_acquire(&buffer->used); + const int avail = buffer->frames - qatomic_load_acquire(&buffer->used); int wptr = buffer->wptr; if (frames > avail) { @@ -185,7 +188,7 @@ static int qjack_buffer_write_l(QJackBuffer *buffer, float **dest, int frames) } buffer->wptr = wptr; - atomic_add(&buffer->used, frames); + qatomic_add(&buffer->used, frames); return frames; } @@ -195,7 +198,7 @@ static int qjack_buffer_read(QJackBuffer *buffer, float *dest, int size) assert(buffer->data); const int samples = size / sizeof(float); int frames = samples / buffer->channels; - const int avail = atomic_load_acquire(&buffer->used); + const int avail = qatomic_load_acquire(&buffer->used); if (frames > avail) { frames = avail; @@ -219,7 +222,7 @@ static int qjack_buffer_read(QJackBuffer *buffer, float *dest, int size) buffer->rptr = rptr; - atomic_sub(&buffer->used, frames); + qatomic_sub(&buffer->used, frames); return frames * buffer->channels * sizeof(float); } @@ -228,7 +231,7 @@ static int qjack_buffer_read_l(QJackBuffer *buffer, float **dest, int frames) { assert(buffer->data); int copy = frames; - const int used = atomic_load_acquire(&buffer->used); + const int used = qatomic_load_acquire(&buffer->used); int rptr = buffer->rptr; if (copy > used) { @@ -252,7 +255,7 @@ static int qjack_buffer_read_l(QJackBuffer *buffer, float **dest, int frames) } buffer->rptr = rptr; - atomic_sub(&buffer->used, copy); + qatomic_sub(&buffer->used, copy); return copy; } @@ -274,7 +277,7 @@ static int qjack_process(jack_nframes_t nframes, void *arg) if (likely(c->enabled)) { qjack_buffer_read_l(&c->fifo, buffers, nframes); } else { - for(int i = 0; i < c->nchannels; ++i) { + for (int i = 0; i < c->nchannels; ++i) { memset(buffers[i], 0, nframes * sizeof(float)); } } @@ -306,21 +309,27 @@ static int qjack_xrun(void *arg) return 0; } +static void qjack_shutdown_bh(void *opaque) +{ + QJackClient *c = (QJackClient *)opaque; + qjack_client_fini(c); +} + static void qjack_shutdown(void *arg) { QJackClient *c = (QJackClient *)arg; c->state = QJACK_STATE_SHUTDOWN; + qemu_bh_schedule(c->shutdown_bh); } static void qjack_client_recover(QJackClient *c) { - if (c->state == QJACK_STATE_SHUTDOWN) { - qjack_client_fini(c); + if (c->state != QJACK_STATE_DISCONNECTED) { + return; } /* packets is used simply to throttle this */ - if (c->state == QJACK_STATE_DISCONNECTED && - c->packets % 100 == 0) { + if (c->packets % 100 == 0) { /* if enabled then attempt to recover */ if (c->enabled) { @@ -489,15 +498,16 @@ static int qjack_init_out(HWVoiceOut *hw, struct audsettings *as, QJackOut *jo = (QJackOut *)hw; Audiodev *dev = (Audiodev *)drv_opaque; - qjack_client_fini(&jo->c); - jo->c.out = true; jo->c.enabled = false; jo->c.nchannels = as->nchannels; jo->c.opt = dev->u.jack.out; + jo->c.shutdown_bh = qemu_bh_new(qjack_shutdown_bh, &jo->c); + int ret = qjack_client_init(&jo->c); if (ret != 0) { + qemu_bh_delete(jo->c.shutdown_bh); return ret; } @@ -525,15 +535,16 @@ static int qjack_init_in(HWVoiceIn *hw, struct audsettings *as, QJackIn *ji = (QJackIn *)hw; Audiodev *dev = (Audiodev *)drv_opaque; - qjack_client_fini(&ji->c); - ji->c.out = false; ji->c.enabled = false; ji->c.nchannels = as->nchannels; ji->c.opt = dev->u.jack.in; + ji->c.shutdown_bh = qemu_bh_new(qjack_shutdown_bh, &ji->c); + int ret = qjack_client_init(&ji->c); if (ret != 0) { + qemu_bh_delete(ji->c.shutdown_bh); return ret; } @@ -555,7 +566,7 @@ static int qjack_init_in(HWVoiceIn *hw, struct audsettings *as, return 0; } -static void qjack_client_fini(QJackClient *c) +static void qjack_client_fini_locked(QJackClient *c) { switch (c->state) { case QJACK_STATE_RUNNING: @@ -564,28 +575,40 @@ static void qjack_client_fini(QJackClient *c) case QJACK_STATE_SHUTDOWN: jack_client_close(c->client); + c->client = NULL; + + qjack_buffer_free(&c->fifo); + g_free(c->port); + + c->state = QJACK_STATE_DISCONNECTED; /* fallthrough */ case QJACK_STATE_DISCONNECTED: break; } +} - qjack_buffer_free(&c->fifo); - g_free(c->port); - - c->state = QJACK_STATE_DISCONNECTED; +static void qjack_client_fini(QJackClient *c) +{ + qemu_mutex_lock(&qjack_shutdown_lock); + qjack_client_fini_locked(c); + qemu_mutex_unlock(&qjack_shutdown_lock); } static void qjack_fini_out(HWVoiceOut *hw) { QJackOut *jo = (QJackOut *)hw; qjack_client_fini(&jo->c); + + qemu_bh_delete(jo->c.shutdown_bh); } static void qjack_fini_in(HWVoiceIn *hw) { QJackIn *ji = (QJackIn *)hw; qjack_client_fini(&ji->c); + + qemu_bh_delete(ji->c.shutdown_bh); } static void qjack_enable_out(HWVoiceOut *hw, bool enable) @@ -634,6 +657,7 @@ static struct audio_pcm_ops jack_pcm_ops = { .init_in = qjack_init_in, .fini_in = qjack_fini_in, .read = qjack_read, + .run_buffer_in = audio_generic_run_buffer_in, .enable_in = qjack_enable_in }; @@ -662,6 +686,7 @@ static void qjack_info(const char *msg) static void register_audio_jack(void) { + qemu_mutex_init(&qjack_shutdown_lock); audio_driver_register(&jack_driver); jack_set_thread_creator(qjack_thread_creator); jack_set_error_function(qjack_error); diff --git a/audio/meson.build b/audio/meson.build new file mode 100644 index 0000000000..7d53b0f920 --- /dev/null +++ b/audio/meson.build @@ -0,0 +1,30 @@ +softmmu_ss.add([spice_headers, files('audio.c')]) +softmmu_ss.add(files( + 'audio_legacy.c', + 'mixeng.c', + 'noaudio.c', + 'wavaudio.c', + 'wavcapture.c', +)) + +softmmu_ss.add(when: [coreaudio, 'CONFIG_AUDIO_COREAUDIO'], if_true: files('coreaudio.c')) +softmmu_ss.add(when: [dsound, 'CONFIG_AUDIO_DSOUND'], if_true: files('dsoundaudio.c')) +softmmu_ss.add(when: ['CONFIG_AUDIO_WIN_INT'], if_true: files('audio_win_int.c')) + +audio_modules = {} +foreach m : [ + ['CONFIG_AUDIO_ALSA', 'alsa', alsa, 'alsaaudio.c'], + ['CONFIG_AUDIO_OSS', 'oss', oss, 'ossaudio.c'], + ['CONFIG_AUDIO_PA', 'pa', pulse, 'paaudio.c'], + ['CONFIG_AUDIO_SDL', 'sdl', sdl, 'sdlaudio.c'], + ['CONFIG_AUDIO_JACK', 'jack', jack, 'jackaudio.c'], + ['CONFIG_SPICE', 'spice', spice, 'spiceaudio.c'] +] + if config_host.has_key(m[0]) + module_ss = ss.source_set() + module_ss.add(when: m[2], if_true: files(m[3])) + audio_modules += {m[1] : module_ss} + endif +endforeach + +modules += {'audio': audio_modules} diff --git a/audio/noaudio.c b/audio/noaudio.c index 05798ea210..aac87dbc93 100644 --- a/audio/noaudio.c +++ b/audio/noaudio.c @@ -124,6 +124,7 @@ static struct audio_pcm_ops no_pcm_ops = { .init_in = no_init_in, .fini_in = no_fini_in, .read = no_read, + .run_buffer_in = audio_generic_run_buffer_in, .enable_in = no_enable_in }; diff --git a/audio/ossaudio.c b/audio/ossaudio.c index a7dcaa31ad..60eff66424 100644 --- a/audio/ossaudio.c +++ b/audio/ossaudio.c @@ -142,16 +142,14 @@ static int aud_to_ossfmt (AudioFormat fmt, int endianness) case AUDIO_FORMAT_S16: if (endianness) { return AFMT_S16_BE; - } - else { + } else { return AFMT_S16_LE; } case AUDIO_FORMAT_U16: if (endianness) { return AFMT_U16_BE; - } - else { + } else { return AFMT_U16_LE; } @@ -542,16 +540,14 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, int trig = 0; if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n"); - } - else { + } else { trig = PCM_ENABLE_OUTPUT; if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { oss_logerr ( errno, "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n" ); - } - else { + } else { oss->mmapped = 1; } } @@ -762,6 +758,7 @@ static struct audio_pcm_ops oss_pcm_ops = { .init_in = oss_init_in, .fini_in = oss_fini_in, .read = oss_read, + .run_buffer_in = audio_generic_run_buffer_in, .enable_in = oss_enable_in }; diff --git a/audio/paaudio.c b/audio/paaudio.c index b052084698..c97b22e970 100644 --- a/audio/paaudio.c +++ b/audio/paaudio.c @@ -207,6 +207,46 @@ static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size) PAVoiceOut *p = (PAVoiceOut *) hw; PAConnection *c = p->g->conn; void *ret; + size_t l; + int r; + + pa_threaded_mainloop_lock(c->mainloop); + + CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail, + "pa_threaded_mainloop_lock failed\n"); + if (pa_stream_get_state(p->stream) != PA_STREAM_READY) { + /* wait for stream to become ready */ + l = 0; + ret = NULL; + goto unlock; + } + + l = pa_stream_writable_size(p->stream); + CHECK_SUCCESS_GOTO(c, l != (size_t) -1, unlock_and_fail, + "pa_stream_writable_size failed\n"); + + *size = -1; + r = pa_stream_begin_write(p->stream, &ret, size); + CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail, + "pa_stream_begin_write failed\n"); + +unlock: + pa_threaded_mainloop_unlock(c->mainloop); + if (*size > l) { + *size = l; + } + return ret; + +unlock_and_fail: + pa_threaded_mainloop_unlock(c->mainloop); + *size = 0; + return NULL; +} + +static size_t qpa_put_buffer_out(HWVoiceOut *hw, void *data, size_t length) +{ + PAVoiceOut *p = (PAVoiceOut *)hw; + PAConnection *c = p->g->conn; int r; pa_threaded_mainloop_lock(c->mainloop); @@ -214,18 +254,15 @@ static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size) CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail, "pa_threaded_mainloop_lock failed\n"); - *size = -1; - r = pa_stream_begin_write(p->stream, &ret, size); - CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail, - "pa_stream_begin_write failed\n"); + r = pa_stream_write(p->stream, data, length, NULL, 0LL, PA_SEEK_RELATIVE); + CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail, "pa_stream_write failed\n"); pa_threaded_mainloop_unlock(c->mainloop); - return ret; + return length; unlock_and_fail: pa_threaded_mainloop_unlock(c->mainloop); - *size = 0; - return NULL; + return 0; } static size_t qpa_write(HWVoiceOut *hw, void *data, size_t length) @@ -239,6 +276,11 @@ static size_t qpa_write(HWVoiceOut *hw, void *data, size_t length) CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail, "pa_threaded_mainloop_lock failed\n"); + if (pa_stream_get_state(p->stream) != PA_STREAM_READY) { + /* wait for stream to become ready */ + l = 0; + goto unlock; + } l = pa_stream_writable_size(p->stream); @@ -252,6 +294,7 @@ static size_t qpa_write(HWVoiceOut *hw, void *data, size_t length) r = pa_stream_write(p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE); CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail, "pa_stream_write failed\n"); +unlock: pa_threaded_mainloop_unlock(c->mainloop); return l; @@ -437,7 +480,7 @@ static pa_stream *qpa_simple_new ( } if (r < 0) { - goto fail; + goto fail; } pa_threaded_mainloop_unlock(c->mainloop); @@ -474,7 +517,8 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, ss.rate = as->freq; ba.tlength = pa_usec_to_bytes(ppdo->latency, &ss); - ba.minreq = -1; + ba.minreq = pa_usec_to_bytes(MIN(ppdo->latency >> 2, + (g->dev->timer_period >> 2) * 3), &ss); ba.maxlength = -1; ba.prebuf = -1; @@ -495,9 +539,12 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, } audio_pcm_init_info (&hw->info, &obt_as); + /* + * This is wrong. hw->samples counts in frames. hw->samples will be + * number of channels times larger than expected. + */ hw->samples = audio_buffer_samples( - qapi_AudiodevPaPerDirectionOptions_base(ppdo), - &obt_as, ppdo->buffer_length); + qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440); return 0; @@ -521,8 +568,9 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) ss.channels = as->nchannels; ss.rate = as->freq; - ba.fragsize = pa_usec_to_bytes(ppdo->latency, &ss); - ba.maxlength = pa_usec_to_bytes(ppdo->latency * 2, &ss); + ba.fragsize = pa_usec_to_bytes((g->dev->timer_period >> 1) * 3, &ss); + ba.maxlength = pa_usec_to_bytes( + MAX(ppdo->latency, g->dev->timer_period * 3), &ss); ba.minreq = -1; ba.prebuf = -1; @@ -543,9 +591,12 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) } audio_pcm_init_info (&hw->info, &obt_as); + /* + * This is wrong. hw->samples counts in frames. hw->samples will be + * number of channels times larger than expected. + */ hw->samples = audio_buffer_samples( - qapi_AudiodevPaPerDirectionOptions_base(ppdo), - &obt_as, ppdo->buffer_length); + qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440); return 0; @@ -695,10 +746,6 @@ static void qpa_volume_in(HWVoiceIn *hw, Volume *vol) static int qpa_validate_per_direction_opts(Audiodev *dev, AudiodevPaPerDirectionOptions *pdo) { - if (!pdo->has_buffer_length) { - pdo->has_buffer_length = true; - pdo->buffer_length = 46440; - } if (!pdo->has_latency) { pdo->has_latency = true; pdo->latency = 15000; @@ -861,7 +908,7 @@ static struct audio_pcm_ops qpa_pcm_ops = { .fini_out = qpa_fini_out, .write = qpa_write, .get_buffer_out = qpa_get_buffer_out, - .put_buffer_out = qpa_write, /* pa handles it */ + .put_buffer_out = qpa_put_buffer_out, .volume_out = qpa_volume_out, .init_in = qpa_init_in, diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c index 21b7a0484b..c68c62a3e4 100644 --- a/audio/sdlaudio.c +++ b/audio/sdlaudio.c @@ -41,15 +41,19 @@ typedef struct SDLVoiceOut { HWVoiceOut hw; -} SDLVoiceOut; - -static struct SDLAudioState { int exit; int initialized; - bool driver_created; Audiodev *dev; -} glob_sdl; -typedef struct SDLAudioState SDLAudioState; + SDL_AudioDeviceID devid; +} SDLVoiceOut; + +typedef struct SDLVoiceIn { + HWVoiceIn hw; + int exit; + int initialized; + Audiodev *dev; + SDL_AudioDeviceID devid; +} SDLVoiceIn; static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...) { @@ -155,9 +159,10 @@ static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness) return 0; } -static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) +static SDL_AudioDeviceID sdl_open(SDL_AudioSpec *req, SDL_AudioSpec *obt, + int rec) { - int status; + SDL_AudioDeviceID devid; #ifndef _WIN32 int err; sigset_t new, old; @@ -166,18 +171,19 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) err = sigfillset (&new); if (err) { dolog ("sdl_open: sigfillset failed: %s\n", strerror (errno)); - return -1; + return 0; } err = pthread_sigmask (SIG_BLOCK, &new, &old); if (err) { dolog ("sdl_open: pthread_sigmask failed: %s\n", strerror (err)); - return -1; + return 0; } #endif - status = SDL_OpenAudio (req, obt); - if (status) { - sdl_logerr ("SDL_OpenAudio failed\n"); + devid = SDL_OpenAudioDevice(NULL, rec, req, obt, 0); + if (!devid) { + sdl_logerr("SDL_OpenAudioDevice for %s failed\n", + rec ? "recording" : "playback"); } #ifndef _WIN32 @@ -190,112 +196,175 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) exit (EXIT_FAILURE); } #endif - return status; + return devid; } -static void sdl_close (SDLAudioState *s) +static void sdl_close_out(SDLVoiceOut *sdl) { - if (s->initialized) { - SDL_LockAudio(); - s->exit = 1; - SDL_UnlockAudio(); - SDL_PauseAudio (1); - SDL_CloseAudio (); - s->initialized = 0; + if (sdl->initialized) { + SDL_LockAudioDevice(sdl->devid); + sdl->exit = 1; + SDL_UnlockAudioDevice(sdl->devid); + SDL_PauseAudioDevice(sdl->devid, 1); + sdl->initialized = 0; + } + if (sdl->devid) { + SDL_CloseAudioDevice(sdl->devid); + sdl->devid = 0; } } -static void sdl_callback (void *opaque, Uint8 *buf, int len) +static void sdl_callback_out(void *opaque, Uint8 *buf, int len) { SDLVoiceOut *sdl = opaque; - SDLAudioState *s = &glob_sdl; HWVoiceOut *hw = &sdl->hw; - if (s->exit) { - return; - } + if (!sdl->exit) { - /* dolog ("in callback samples=%zu live=%zu\n", samples, sdl->live); */ + /* dolog("callback_out: len=%d avail=%zu\n", len, hw->pending_emul); */ - while (hw->pending_emul && len) { - size_t write_len; - ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul; - if (start < 0) { - start += hw->size_emul; + while (hw->pending_emul && len) { + size_t write_len; + ssize_t start = (ssize_t)hw->pos_emul - hw->pending_emul; + if (start < 0) { + start += hw->size_emul; + } + assert(start >= 0 && start < hw->size_emul); + + write_len = MIN(MIN(hw->pending_emul, len), + hw->size_emul - start); + + memcpy(buf, hw->buf_emul + start, write_len); + hw->pending_emul -= write_len; + len -= write_len; + buf += write_len; } - assert(start >= 0 && start < hw->size_emul); - - write_len = MIN(MIN(hw->pending_emul, len), - hw->size_emul - start); - - memcpy(buf, hw->buf_emul + start, write_len); - hw->pending_emul -= write_len; - len -= write_len; - buf += write_len; } /* clear remaining buffer that we couldn't fill with data */ if (len) { - memset(buf, 0, len); + audio_pcm_info_clear_buf(&hw->info, buf, + len / hw->info.bytes_per_frame); } } -#define SDL_WRAPPER_FUNC(name, ret_type, args_decl, args, fail, unlock) \ - static ret_type glue(sdl_, name)args_decl \ - { \ - ret_type ret; \ - \ - SDL_LockAudio(); \ - \ - ret = glue(audio_generic_, name)args; \ - \ - SDL_UnlockAudio(); \ - return ret; \ +static void sdl_close_in(SDLVoiceIn *sdl) +{ + if (sdl->initialized) { + SDL_LockAudioDevice(sdl->devid); + sdl->exit = 1; + SDL_UnlockAudioDevice(sdl->devid); + SDL_PauseAudioDevice(sdl->devid, 1); + sdl->initialized = 0; + } + if (sdl->devid) { + SDL_CloseAudioDevice(sdl->devid); + sdl->devid = 0; + } +} + +static void sdl_callback_in(void *opaque, Uint8 *buf, int len) +{ + SDLVoiceIn *sdl = opaque; + HWVoiceIn *hw = &sdl->hw; + + if (sdl->exit) { + return; + } + + /* dolog("callback_in: len=%d pending=%zu\n", len, hw->pending_emul); */ + + while (hw->pending_emul < hw->size_emul && len) { + size_t read_len = MIN(len, MIN(hw->size_emul - hw->pos_emul, + hw->size_emul - hw->pending_emul)); + + memcpy(hw->buf_emul + hw->pos_emul, buf, read_len); + + hw->pending_emul += read_len; + hw->pos_emul = (hw->pos_emul + read_len) % hw->size_emul; + len -= read_len; + buf += read_len; + } +} + +#define SDL_WRAPPER_FUNC(name, ret_type, args_decl, args, dir) \ + static ret_type glue(sdl_, name)args_decl \ + { \ + ret_type ret; \ + glue(SDLVoice, dir) *sdl = (glue(SDLVoice, dir) *)hw; \ + \ + SDL_LockAudioDevice(sdl->devid); \ + ret = glue(audio_generic_, name)args; \ + SDL_UnlockAudioDevice(sdl->devid); \ + \ + return ret; \ + } + +#define SDL_WRAPPER_VOID_FUNC(name, args_decl, args, dir) \ + static void glue(sdl_, name)args_decl \ + { \ + glue(SDLVoice, dir) *sdl = (glue(SDLVoice, dir) *)hw; \ + \ + SDL_LockAudioDevice(sdl->devid); \ + glue(audio_generic_, name)args; \ + SDL_UnlockAudioDevice(sdl->devid); \ } SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size), - (hw, size), *size = 0, sdl_unlock) + (hw, size), Out) SDL_WRAPPER_FUNC(put_buffer_out, size_t, - (HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size), - /*nothing*/, sdl_unlock_and_post) + (HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size), Out) SDL_WRAPPER_FUNC(write, size_t, - (HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size), - /*nothing*/, sdl_unlock_and_post) - + (HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size), Out) +SDL_WRAPPER_FUNC(read, size_t, (HWVoiceIn *hw, void *buf, size_t size), + (hw, buf, size), In) +SDL_WRAPPER_FUNC(get_buffer_in, void *, (HWVoiceIn *hw, size_t *size), + (hw, size), In) +SDL_WRAPPER_VOID_FUNC(put_buffer_in, (HWVoiceIn *hw, void *buf, size_t size), + (hw, buf, size), In) #undef SDL_WRAPPER_FUNC +#undef SDL_WRAPPER_VOID_FUNC -static void sdl_fini_out (HWVoiceOut *hw) +static void sdl_fini_out(HWVoiceOut *hw) { - (void) hw; + SDLVoiceOut *sdl = (SDLVoiceOut *)hw; - sdl_close (&glob_sdl); + sdl_close_out(sdl); } static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) { - SDLVoiceOut *sdl = (SDLVoiceOut *) hw; - SDLAudioState *s = &glob_sdl; + SDLVoiceOut *sdl = (SDLVoiceOut *)hw; SDL_AudioSpec req, obt; int endianness; int err; AudioFormat effective_fmt; + Audiodev *dev = drv_opaque; + AudiodevSdlPerDirectionOptions *spdo = dev->u.sdl.out; struct audsettings obt_as; req.freq = as->freq; req.format = aud_to_sdlfmt (as->fmt); req.channels = as->nchannels; - req.samples = audio_buffer_samples(s->dev->u.sdl.out, as, 11610); - req.callback = sdl_callback; + /* + * This is wrong. SDL samples are QEMU frames. The buffer size will be + * the requested buffer size multiplied by the number of channels. + */ + req.samples = audio_buffer_samples( + qapi_AudiodevSdlPerDirectionOptions_base(spdo), as, 11610); + req.callback = sdl_callback_out; req.userdata = sdl; - if (sdl_open (&req, &obt)) { + sdl->dev = dev; + sdl->devid = sdl_open(&req, &obt, 0); + if (!sdl->devid) { return -1; } err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness); if (err) { - sdl_close (s); + sdl_close_out(sdl); return -1; } @@ -305,44 +374,97 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, obt_as.endianness = endianness; audio_pcm_init_info (&hw->info, &obt_as); - hw->samples = obt.samples; + hw->samples = (spdo->has_buffer_count ? spdo->buffer_count : 4) * + obt.samples; - s->initialized = 1; - s->exit = 0; - SDL_PauseAudio (0); + sdl->initialized = 1; + sdl->exit = 0; return 0; } static void sdl_enable_out(HWVoiceOut *hw, bool enable) { - SDL_PauseAudio(!enable); + SDLVoiceOut *sdl = (SDLVoiceOut *)hw; + + SDL_PauseAudioDevice(sdl->devid, !enable); +} + +static void sdl_fini_in(HWVoiceIn *hw) +{ + SDLVoiceIn *sdl = (SDLVoiceIn *)hw; + + sdl_close_in(sdl); +} + +static int sdl_init_in(HWVoiceIn *hw, audsettings *as, void *drv_opaque) +{ + SDLVoiceIn *sdl = (SDLVoiceIn *)hw; + SDL_AudioSpec req, obt; + int endianness; + int err; + AudioFormat effective_fmt; + Audiodev *dev = drv_opaque; + AudiodevSdlPerDirectionOptions *spdo = dev->u.sdl.in; + struct audsettings obt_as; + + req.freq = as->freq; + req.format = aud_to_sdlfmt(as->fmt); + req.channels = as->nchannels; + /* SDL samples are QEMU frames */ + req.samples = audio_buffer_frames( + qapi_AudiodevSdlPerDirectionOptions_base(spdo), as, 11610); + req.callback = sdl_callback_in; + req.userdata = sdl; + + sdl->dev = dev; + sdl->devid = sdl_open(&req, &obt, 1); + if (!sdl->devid) { + return -1; + } + + err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness); + if (err) { + sdl_close_in(sdl); + return -1; + } + + obt_as.freq = obt.freq; + obt_as.nchannels = obt.channels; + obt_as.fmt = effective_fmt; + obt_as.endianness = endianness; + + audio_pcm_init_info(&hw->info, &obt_as); + hw->samples = (spdo->has_buffer_count ? spdo->buffer_count : 4) * + obt.samples; + hw->size_emul = hw->samples * hw->info.bytes_per_frame; + hw->buf_emul = g_malloc(hw->size_emul); + hw->pos_emul = hw->pending_emul = 0; + + sdl->initialized = 1; + sdl->exit = 0; + return 0; +} + +static void sdl_enable_in(HWVoiceIn *hw, bool enable) +{ + SDLVoiceIn *sdl = (SDLVoiceIn *)hw; + + SDL_PauseAudioDevice(sdl->devid, !enable); } static void *sdl_audio_init(Audiodev *dev) { - SDLAudioState *s = &glob_sdl; - if (s->driver_created) { - sdl_logerr("Can't create multiple sdl backends\n"); - return NULL; - } - if (SDL_InitSubSystem (SDL_INIT_AUDIO)) { sdl_logerr ("SDL failed to initialize audio subsystem\n"); return NULL; } - s->driver_created = true; - s->dev = dev; - return s; + return dev; } static void sdl_audio_fini (void *opaque) { - SDLAudioState *s = opaque; - sdl_close (s); SDL_QuitSubSystem (SDL_INIT_AUDIO); - s->driver_created = false; - s->dev = NULL; } static struct audio_pcm_ops sdl_pcm_ops = { @@ -355,6 +477,15 @@ static struct audio_pcm_ops sdl_pcm_ops = { /* wrapper for audio_generic_put_buffer_out */ .put_buffer_out = sdl_put_buffer_out, .enable_out = sdl_enable_out, + .init_in = sdl_init_in, + .fini_in = sdl_fini_in, + /* wrapper for audio_generic_read */ + .read = sdl_read, + /* wrapper for audio_generic_get_buffer_in */ + .get_buffer_in = sdl_get_buffer_in, + /* wrapper for audio_generic_put_buffer_in */ + .put_buffer_in = sdl_put_buffer_in, + .enable_in = sdl_enable_in, }; static struct audio_driver sdl_audio_driver = { @@ -364,10 +495,10 @@ static struct audio_driver sdl_audio_driver = { .fini = sdl_audio_fini, .pcm_ops = &sdl_pcm_ops, .can_be_default = 1, - .max_voices_out = 1, - .max_voices_in = 0, - .voice_size_out = sizeof (SDLVoiceOut), - .voice_size_in = 0 + .max_voices_out = INT_MAX, + .max_voices_in = INT_MAX, + .voice_size_out = sizeof(SDLVoiceOut), + .voice_size_in = sizeof(SDLVoiceIn), }; static void register_audio_sdl(void) diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c index b6b5da4812..999bfbde47 100644 --- a/audio/spiceaudio.c +++ b/audio/spiceaudio.c @@ -106,7 +106,7 @@ static int line_out_init(HWVoiceOut *hw, struct audsettings *as, out->active = 0; out->sin.base.sif = &playback_sif.base; - qemu_spice_add_interface (&out->sin.base); + qemu_spice.add_interface(&out->sin.base); #if SPICE_INTERFACE_PLAYBACK_MAJOR > 1 || SPICE_INTERFACE_PLAYBACK_MINOR >= 3 spice_server_set_playback_rate(&out->sin, settings.freq); #endif @@ -130,12 +130,11 @@ static void *line_out_get_buffer(HWVoiceOut *hw, size_t *size) } if (out->frame) { - *size = audio_rate_get_bytes( - &hw->info, &out->rate, - (out->fsize - out->fpos) * hw->info.bytes_per_frame); - } else { - audio_rate_start(&out->rate); + *size = MIN((out->fsize - out->fpos) << 2, *size); } + + *size = audio_rate_get_bytes(&hw->info, &out->rate, *size); + return out->frame + out->fpos; } @@ -143,12 +142,14 @@ static size_t line_out_put_buffer(HWVoiceOut *hw, void *buf, size_t size) { SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw); - assert(buf == out->frame + out->fpos && out->fpos <= out->fsize); - out->fpos += size >> 2; + if (buf) { + assert(buf == out->frame + out->fpos && out->fpos <= out->fsize); + out->fpos += size >> 2; - if (out->fpos == out->fsize) { /* buffer full */ - spice_server_playback_put_samples(&out->sin, out->frame); - out->frame = NULL; + if (out->fpos == out->fsize) { /* buffer full */ + spice_server_playback_put_samples(&out->sin, out->frame); + out->frame = NULL; + } } return size; @@ -214,7 +215,7 @@ static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) in->active = 0; in->sin.base.sif = &record_sif.base; - qemu_spice_add_interface (&in->sin.base); + qemu_spice.add_interface(&in->sin.base); #if SPICE_INTERFACE_RECORD_MAJOR > 2 || SPICE_INTERFACE_RECORD_MINOR >= 3 spice_server_set_record_rate(&in->sin, settings.freq); #endif @@ -292,6 +293,7 @@ static struct audio_pcm_ops audio_callbacks = { .init_in = line_in_init, .fini_in = line_in_fini, .read = line_in_read, + .run_buffer_in = audio_generic_run_buffer_in, .enable_in = line_in_enable, #if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2)) .volume_in = line_in_volume, @@ -310,11 +312,6 @@ static struct audio_driver spice_audio_driver = { .voice_size_in = sizeof (SpiceVoiceIn), }; -void qemu_spice_audio_init (void) -{ - spice_audio_driver.can_be_default = 1; -} - static void register_audio_spice(void) { audio_driver_register(&spice_audio_driver); diff --git a/audio/trace-events b/audio/trace-events index a1d1eccb8a..6aec535763 100644 --- a/audio/trace-events +++ b/audio/trace-events @@ -9,12 +9,9 @@ alsa_read_zero(long len) "Failed to read %ld frames (read zero)" alsa_xrun_out(void) "Recovering from playback xrun" alsa_xrun_in(void) "Recovering from capture xrun" alsa_resume_out(void) "Resuming suspended output stream" -alsa_resume_in(void) "Resuming suspended input stream" -alsa_no_frames(int state) "No frames available and ALSA state is %d" # ossaudio.c oss_version(int version) "OSS version = 0x%x" -oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d" # audio.c audio_timer_start(int interval) "interval %d ms" diff --git a/audio/trace.h b/audio/trace.h new file mode 100644 index 0000000000..4072a11b0f --- /dev/null +++ b/audio/trace.h @@ -0,0 +1 @@ +#include "trace/trace-audio.h" diff --git a/audio/wavcapture.c b/audio/wavcapture.c index 17e87ed6f4..c60286e162 100644 --- a/audio/wavcapture.c +++ b/audio/wavcapture.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "monitor/monitor.h" +#include "qemu/qemu-print.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "audio.h" @@ -94,9 +94,9 @@ static void wav_capture_info (void *opaque) WAVState *wav = opaque; char *path = wav->path; - monitor_printf (cur_mon, "Capturing audio(%d,%d,%d) to %s: %d bytes\n", - wav->freq, wav->bits, wav->nchannels, - path ? path : "", wav->bytes); + qemu_printf("Capturing audio(%d,%d,%d) to %s: %d bytes\n", + wav->freq, wav->bits, wav->nchannels, + path ? path : "", wav->bytes); } static struct capture_ops wav_capture_ops = { diff --git a/authz/Makefile.objs b/authz/Makefile.objs deleted file mode 100644 index ed7b273596..0000000000 --- a/authz/Makefile.objs +++ /dev/null @@ -1,7 +0,0 @@ -authz-obj-y += base.o -authz-obj-y += simple.o -authz-obj-y += list.o -authz-obj-y += listfile.o -authz-obj-$(CONFIG_AUTH_PAM) += pamacct.o - -pamacct.o-libs = -lpam diff --git a/authz/base.c b/authz/base.c index c75bce3fd1..f2b7fbe9c1 100644 --- a/authz/base.c +++ b/authz/base.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/authz/list.c b/authz/list.c index 8e904bfc93..0e17eed897 100644 --- a/authz/list.c +++ b/authz/list.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -252,7 +252,6 @@ static const TypeInfo qauthz_list_info = { .name = TYPE_QAUTHZ_LIST, .instance_size = sizeof(QAuthZList), .instance_finalize = qauthz_list_finalize, - .class_size = sizeof(QAuthZListClass), .class_init = qauthz_list_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_USER_CREATABLE }, diff --git a/authz/listfile.c b/authz/listfile.c index 666df872ad..da3a0e69a2 100644 --- a/authz/listfile.c +++ b/authz/listfile.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -73,7 +73,8 @@ qauthz_list_file_load(QAuthZListFile *fauthz, Error **errp) pdict = qobject_to(QDict, obj); if (!pdict) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "obj", "dict"); + error_setg(errp, "File '%s' must contain a JSON object", + fauthz->filename); goto cleanup; } @@ -122,7 +123,15 @@ qauthz_list_file_complete(UserCreatable *uc, Error **errp) QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(uc); gchar *dir = NULL, *file = NULL; + if (!fauthz->filename) { + error_setg(errp, "filename not provided"); + return; + } + fauthz->list = qauthz_list_file_load(fauthz, errp); + if (!fauthz->list) { + return; + } if (!fauthz->refresh) { return; @@ -263,7 +272,6 @@ static const TypeInfo qauthz_list_file_info = { .instance_init = qauthz_list_file_init, .instance_size = sizeof(QAuthZListFile), .instance_finalize = qauthz_list_file_finalize, - .class_size = sizeof(QAuthZListFileClass), .class_init = qauthz_list_file_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_USER_CREATABLE }, diff --git a/authz/meson.build b/authz/meson.build new file mode 100644 index 0000000000..88fa7769cb --- /dev/null +++ b/authz/meson.build @@ -0,0 +1,9 @@ +authz_ss.add(genh) +authz_ss.add(files( + 'base.c', + 'list.c', + 'listfile.c', + 'simple.c', +)) + +authz_ss.add(when: ['CONFIG_AUTH_PAM', pam], if_true: files('pamacct.c')) diff --git a/authz/pamacct.c b/authz/pamacct.c index 3c6be43916..c862d9ff39 100644 --- a/authz/pamacct.c +++ b/authz/pamacct.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -84,6 +84,12 @@ qauthz_pam_prop_get_service(Object *obj, static void qauthz_pam_complete(UserCreatable *uc, Error **errp) { + QAuthZPAM *pauthz = QAUTHZ_PAM(uc); + + if (!pauthz->service) { + error_setg(errp, "The 'service' property must be set"); + return; + } } @@ -129,7 +135,6 @@ static const TypeInfo qauthz_pam_info = { .name = TYPE_QAUTHZ_PAM, .instance_size = sizeof(QAuthZPAM), .instance_finalize = qauthz_pam_finalize, - .class_size = sizeof(QAuthZPAMClass), .class_init = qauthz_pam_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_USER_CREATABLE }, diff --git a/authz/simple.c b/authz/simple.c index 84954b80a5..0597dcd8ea 100644 --- a/authz/simple.c +++ b/authz/simple.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -65,11 +65,25 @@ qauthz_simple_finalize(Object *obj) } +static void +qauthz_simple_complete(UserCreatable *uc, Error **errp) +{ + QAuthZSimple *sauthz = QAUTHZ_SIMPLE(uc); + + if (!sauthz->identity) { + error_setg(errp, "The 'identity' property must be set"); + return; + } +} + + static void qauthz_simple_class_init(ObjectClass *oc, void *data) { QAuthZClass *authz = QAUTHZ_CLASS(oc); + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + ucc->complete = qauthz_simple_complete; authz->is_allowed = qauthz_simple_is_allowed; object_class_property_add_str(oc, "identity", @@ -96,7 +110,6 @@ static const TypeInfo qauthz_simple_info = { .name = TYPE_QAUTHZ_SIMPLE, .instance_size = sizeof(QAuthZSimple), .instance_finalize = qauthz_simple_finalize, - .class_size = sizeof(QAuthZSimpleClass), .class_init = qauthz_simple_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_USER_CREATABLE }, diff --git a/authz/trace.h b/authz/trace.h new file mode 100644 index 0000000000..3176c127f7 --- /dev/null +++ b/authz/trace.h @@ -0,0 +1 @@ +#include "trace/trace-authz.h" diff --git a/backends/Makefile.objs b/backends/Makefile.objs deleted file mode 100644 index 22d204cb48..0000000000 --- a/backends/Makefile.objs +++ /dev/null @@ -1,23 +0,0 @@ -common-obj-y += rng.o rng-egd.o rng-builtin.o -common-obj-$(CONFIG_POSIX) += rng-random.o - -common-obj-$(CONFIG_TPM) += tpm/ - -common-obj-y += hostmem.o hostmem-ram.o -common-obj-$(CONFIG_POSIX) += hostmem-file.o - -common-obj-y += cryptodev.o -common-obj-y += cryptodev-builtin.o - -ifeq ($(CONFIG_VIRTIO_CRYPTO),y) -common-obj-y += cryptodev-vhost.o -common-obj-$(CONFIG_VHOST_CRYPTO) += cryptodev-vhost-user.o -endif - -common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_VIRTIO)) += vhost-user.o - -common-obj-$(CONFIG_LINUX) += hostmem-memfd.o - -common-obj-$(CONFIG_GIO) += dbus-vmstate.o -dbus-vmstate.o-cflags = $(GIO_CFLAGS) -dbus-vmstate.o-libs = $(GIO_LIBS) diff --git a/backends/confidential-guest-support.c b/backends/confidential-guest-support.c new file mode 100644 index 0000000000..052fde8db0 --- /dev/null +++ b/backends/confidential-guest-support.c @@ -0,0 +1,33 @@ +/* + * QEMU Confidential Guest support + * + * Copyright Red Hat. + * + * Authors: + * David Gibson + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" + +#include "exec/confidential-guest-support.h" + +OBJECT_DEFINE_ABSTRACT_TYPE(ConfidentialGuestSupport, + confidential_guest_support, + CONFIDENTIAL_GUEST_SUPPORT, + OBJECT) + +static void confidential_guest_support_class_init(ObjectClass *oc, void *data) +{ +} + +static void confidential_guest_support_init(Object *obj) +{ +} + +static void confidential_guest_support_finalize(Object *obj) +{ +} diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c index 14316333fe..0671bf9f3e 100644 --- a/backends/cryptodev-builtin.c +++ b/backends/cryptodev-builtin.c @@ -9,7 +9,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -26,6 +26,7 @@ #include "qapi/error.h" #include "standard-headers/linux/virtio_crypto.h" #include "crypto/cipher.h" +#include "qom/object.h" /** @@ -34,12 +35,8 @@ */ #define TYPE_CRYPTODEV_BACKEND_BUILTIN "cryptodev-backend-builtin" -#define CRYPTODEV_BACKEND_BUILTIN(obj) \ - OBJECT_CHECK(CryptoDevBackendBuiltin, \ - (obj), TYPE_CRYPTODEV_BACKEND_BUILTIN) +OBJECT_DECLARE_SIMPLE_TYPE(CryptoDevBackendBuiltin, CRYPTODEV_BACKEND_BUILTIN) -typedef struct CryptoDevBackendBuiltin - CryptoDevBackendBuiltin; typedef struct CryptoDevBackendBuiltinSession { QCryptoCipher *cipher; diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c index dbe5a8aae6..bedb452474 100644 --- a/backends/cryptodev-vhost-user.c +++ b/backends/cryptodev-vhost-user.c @@ -9,7 +9,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -30,6 +30,7 @@ #include "sysemu/cryptodev-vhost.h" #include "chardev/char-fe.h" #include "sysemu/cryptodev-vhost-user.h" +#include "qom/object.h" /** @@ -38,12 +39,10 @@ */ #define TYPE_CRYPTODEV_BACKEND_VHOST_USER "cryptodev-vhost-user" -#define CRYPTODEV_BACKEND_VHOST_USER(obj) \ - OBJECT_CHECK(CryptoDevBackendVhostUser, \ - (obj), TYPE_CRYPTODEV_BACKEND_VHOST_USER) +OBJECT_DECLARE_SIMPLE_TYPE(CryptoDevBackendVhostUser, CRYPTODEV_BACKEND_VHOST_USER) -typedef struct CryptoDevBackendVhostUser { +struct CryptoDevBackendVhostUser { CryptoDevBackend parent_obj; VhostUserState vhost_user; @@ -51,7 +50,7 @@ typedef struct CryptoDevBackendVhostUser { char *chr_name; bool opened; CryptoDevBackendVhost *vhost_crypto[MAX_CRYPTO_QUEUE_NUM]; -} CryptoDevBackendVhostUser; +}; static int cryptodev_vhost_user_running( @@ -335,13 +334,6 @@ cryptodev_vhost_user_get_chardev(Object *obj, Error **errp) return NULL; } -static void cryptodev_vhost_user_instance_int(Object *obj) -{ - object_property_add_str(obj, "chardev", - cryptodev_vhost_user_get_chardev, - cryptodev_vhost_user_set_chardev); -} - static void cryptodev_vhost_user_finalize(Object *obj) { CryptoDevBackendVhostUser *s = @@ -362,13 +354,17 @@ cryptodev_vhost_user_class_init(ObjectClass *oc, void *data) bc->create_session = cryptodev_vhost_user_sym_create_session; bc->close_session = cryptodev_vhost_user_sym_close_session; bc->do_sym_op = NULL; + + object_class_property_add_str(oc, "chardev", + cryptodev_vhost_user_get_chardev, + cryptodev_vhost_user_set_chardev); + } static const TypeInfo cryptodev_vhost_user_info = { .name = TYPE_CRYPTODEV_BACKEND_VHOST_USER, .parent = TYPE_CRYPTODEV_BACKEND, .class_init = cryptodev_vhost_user_class_init, - .instance_init = cryptodev_vhost_user_instance_int, .instance_finalize = cryptodev_vhost_user_finalize, .instance_size = sizeof(CryptoDevBackendVhostUser), }; diff --git a/backends/cryptodev-vhost.c b/backends/cryptodev-vhost.c index 8337c9a495..8231e7f1bc 100644 --- a/backends/cryptodev-vhost.c +++ b/backends/cryptodev-vhost.c @@ -10,7 +10,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/backends/cryptodev.c b/backends/cryptodev.c index ada4ebe78b..bf52476166 100644 --- a/backends/cryptodev.c +++ b/backends/cryptodev.c @@ -9,7 +9,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -206,10 +206,6 @@ cryptodev_backend_can_be_deleted(UserCreatable *uc) static void cryptodev_backend_instance_init(Object *obj) { - object_property_add(obj, "queues", "uint32", - cryptodev_backend_get_queues, - cryptodev_backend_set_queues, - NULL, NULL); /* Initialize devices' queues property to 1 */ object_property_set_int(obj, "queues", 1, NULL); } @@ -230,6 +226,10 @@ cryptodev_backend_class_init(ObjectClass *oc, void *data) ucc->can_be_deleted = cryptodev_backend_can_be_deleted; QTAILQ_INIT(&crypto_clients); + object_class_property_add(oc, "queues", "uint32", + cryptodev_backend_get_queues, + cryptodev_backend_set_queues, + NULL, NULL); } static const TypeInfo cryptodev_backend_info = { diff --git a/backends/dbus-vmstate.c b/backends/dbus-vmstate.c index 56361a6272..9cfd758c42 100644 --- a/backends/dbus-vmstate.c +++ b/backends/dbus-vmstate.c @@ -19,21 +19,13 @@ #include "qapi/qmp/qerror.h" #include "migration/vmstate.h" #include "trace.h" +#include "qom/object.h" -typedef struct DBusVMState DBusVMState; -typedef struct DBusVMStateClass DBusVMStateClass; #define TYPE_DBUS_VMSTATE "dbus-vmstate" -#define DBUS_VMSTATE(obj) \ - OBJECT_CHECK(DBusVMState, (obj), TYPE_DBUS_VMSTATE) -#define DBUS_VMSTATE_GET_CLASS(obj) \ - OBJECT_GET_CLASS(DBusVMStateClass, (obj), TYPE_DBUS_VMSTATE) -#define DBUS_VMSTATE_CLASS(klass) \ - OBJECT_CLASS_CHECK(DBusVMStateClass, (klass), TYPE_DBUS_VMSTATE) +OBJECT_DECLARE_SIMPLE_TYPE(DBusVMState, + DBUS_VMSTATE) -struct DBusVMStateClass { - ObjectClass parent_class; -}; struct DBusVMState { Object parent; @@ -212,6 +204,8 @@ static int dbus_vmstate_post_load(void *opaque, int version_id) m = g_memory_input_stream_new_from_data(self->data, self->data_size, NULL); s = g_data_input_stream_new(m); g_data_input_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN); + g_buffered_input_stream_set_buffer_size(G_BUFFERED_INPUT_STREAM(s), + DBUS_VMSTATE_SIZE_LIMIT); nelem = g_data_input_stream_read_uint32(s, NULL, &err); if (err) { @@ -237,7 +231,10 @@ static int dbus_vmstate_post_load(void *opaque, int version_id) &bytes_read, NULL, &err)) { goto error; } - g_return_val_if_fail(bytes_read == len, -1); + if (bytes_read != len) { + error_report("%s: Short read", __func__); + return -1; + } id[len] = 0; trace_dbus_vmstate_loading(id); @@ -249,11 +246,23 @@ static int dbus_vmstate_post_load(void *opaque, int version_id) } len = g_data_input_stream_read_uint32(s, NULL, &err); + if (len > DBUS_VMSTATE_SIZE_LIMIT) { + error_report("%s: Invalid vmstate size: %u", __func__, len); + return -1; + } + + g_buffered_input_stream_fill(G_BUFFERED_INPUT_STREAM(s), len, NULL, + &err); + if (err) { + goto error; + } + avail = g_buffered_input_stream_get_available( G_BUFFERED_INPUT_STREAM(s)); - - if (len > DBUS_VMSTATE_SIZE_LIMIT || len > avail) { - error_report("%s: Invalid vmstate size: %u", __func__, len); + if (len > avail) { + error_report("%s: Not enough data available to load for Id: '%s'. " + "Available data size: %zu, Actual vmstate size: %u", + __func__, id, avail, len); return -1; } @@ -491,7 +500,6 @@ static const TypeInfo dbus_vmstate_info = { .parent = TYPE_OBJECT, .instance_size = sizeof(DBusVMState), .instance_finalize = dbus_vmstate_finalize, - .class_size = sizeof(DBusVMStateClass), .class_init = dbus_vmstate_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_USER_CREATABLE }, diff --git a/backends/hostmem-file.c b/backends/hostmem-file.c index 5b819020b4..b683da9daf 100644 --- a/backends/hostmem-file.c +++ b/backends/hostmem-file.c @@ -17,11 +17,10 @@ #include "sysemu/hostmem.h" #include "sysemu/sysemu.h" #include "qom/object_interfaces.h" +#include "qom/object.h" -#define MEMORY_BACKEND_FILE(obj) \ - OBJECT_CHECK(HostMemoryBackendFile, (obj), TYPE_MEMORY_BACKEND_FILE) +OBJECT_DECLARE_SIMPLE_TYPE(HostMemoryBackendFile, MEMORY_BACKEND_FILE) -typedef struct HostMemoryBackendFile HostMemoryBackendFile; struct HostMemoryBackendFile { HostMemoryBackend parent_obj; @@ -30,6 +29,7 @@ struct HostMemoryBackendFile { uint64_t align; bool discard_data; bool is_pmem; + bool readonly; }; static void @@ -57,7 +57,7 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) backend->size, fb->align, (backend->share ? RAM_SHARED : 0) | (fb->is_pmem ? RAM_PMEM : 0), - fb->mem_path, errp); + fb->mem_path, fb->readonly, errp); g_free(name); #endif } @@ -124,6 +124,7 @@ static void file_memory_backend_set_align(Object *o, Visitor *v, fb->align = val; } +#ifdef CONFIG_LIBPMEM static bool file_memory_backend_get_pmem(Object *o, Error **errp) { return MEMORY_BACKEND_FILE(o)->is_pmem; @@ -140,16 +141,30 @@ static void file_memory_backend_set_pmem(Object *o, bool value, Error **errp) return; } -#ifndef CONFIG_LIBPMEM - if (value) { - error_setg(errp, "Lack of libpmem support while setting the 'pmem=on'" - " of %s. We can't ensure data persistence.", - object_get_typename(o)); + fb->is_pmem = value; +} +#endif /* CONFIG_LIBPMEM */ + +static bool file_memory_backend_get_readonly(Object *obj, Error **errp) +{ + HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(obj); + + return fb->readonly; +} + +static void file_memory_backend_set_readonly(Object *obj, bool value, + Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(obj); + + if (host_memory_backend_mr_inited(backend)) { + error_setg(errp, "cannot change property 'readonly' of %s.", + object_get_typename(obj)); return; } -#endif - fb->is_pmem = value; + fb->readonly = value; } static void file_backend_unparent(Object *obj) @@ -181,8 +196,13 @@ file_backend_class_init(ObjectClass *oc, void *data) file_memory_backend_get_align, file_memory_backend_set_align, NULL, NULL); +#ifdef CONFIG_LIBPMEM object_class_property_add_bool(oc, "pmem", file_memory_backend_get_pmem, file_memory_backend_set_pmem); +#endif + object_class_property_add_bool(oc, "readonly", + file_memory_backend_get_readonly, + file_memory_backend_set_readonly); } static void file_backend_instance_finalize(Object *o) diff --git a/backends/hostmem-memfd.c b/backends/hostmem-memfd.c index 4c040a7541..69b0ae30bb 100644 --- a/backends/hostmem-memfd.c +++ b/backends/hostmem-memfd.c @@ -17,13 +17,12 @@ #include "qemu/memfd.h" #include "qemu/module.h" #include "qapi/error.h" +#include "qom/object.h" #define TYPE_MEMORY_BACKEND_MEMFD "memory-backend-memfd" -#define MEMORY_BACKEND_MEMFD(obj) \ - OBJECT_CHECK(HostMemoryBackendMemfd, (obj), TYPE_MEMORY_BACKEND_MEMFD) +OBJECT_DECLARE_SIMPLE_TYPE(HostMemoryBackendMemfd, MEMORY_BACKEND_MEMFD) -typedef struct HostMemoryBackendMemfd HostMemoryBackendMemfd; struct HostMemoryBackendMemfd { HostMemoryBackend parent_obj; @@ -56,7 +55,7 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) name = host_memory_backend_get_name(backend); memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), name, backend->size, - backend->share, fd, errp); + backend->share, fd, 0, errp); g_free(name); } diff --git a/backends/hostmem.c b/backends/hostmem.c index 4bde00e8e7..c6c1ff5b99 100644 --- a/backends/hostmem.c +++ b/backends/hostmem.c @@ -80,7 +80,7 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name, { HostMemoryBackend *backend = MEMORY_BACKEND(obj); uint16List *host_nodes = NULL; - uint16List **node = &host_nodes; + uint16List **tail = &host_nodes; unsigned long value; value = find_first_bit(backend->host_nodes, MAX_NODES); @@ -88,9 +88,7 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name, goto ret; } - *node = g_malloc0(sizeof(**node)); - (*node)->value = value; - node = &(*node)->next; + QAPI_LIST_APPEND(tail, value); do { value = find_next_bit(backend->host_nodes, MAX_NODES, value + 1); @@ -98,13 +96,12 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name, break; } - *node = g_malloc0(sizeof(**node)); - (*node)->value = value; - node = &(*node)->next; + QAPI_LIST_APPEND(tail, value); } while (true); ret: visit_type_uint16List(v, name, &host_nodes, errp); + qapi_free_uint16List(host_nodes); } static void @@ -497,6 +494,16 @@ host_memory_backend_class_init(ObjectClass *oc, void *data) host_memory_backend_get_share, host_memory_backend_set_share); object_class_property_set_description(oc, "share", "Mark the memory as private to QEMU or shared"); + /* + * Do not delete/rename option. This option must be considered stable + * (as if it didn't have the 'x-' prefix including deprecation period) as + * long as 4.0 and older machine types exists. + * Option will be used by upper layers to override (disable) canonical path + * for ramblock-id set by compat properties on old machine types ( <= 4.0), + * to keep migration working when backend is used for main RAM with + * -machine memory-backend= option (main RAM historically used prefix-less + * ramblock-id). + */ object_class_property_add_bool(oc, "x-use-canonical-path-for-ramblock-id", host_memory_backend_get_use_canonical_path, host_memory_backend_set_use_canonical_path); diff --git a/backends/meson.build b/backends/meson.build new file mode 100644 index 0000000000..d4221831fc --- /dev/null +++ b/backends/meson.build @@ -0,0 +1,20 @@ +softmmu_ss.add([files( + 'cryptodev-builtin.c', + 'cryptodev.c', + 'hostmem-ram.c', + 'hostmem.c', + 'rng-builtin.c', + 'rng-egd.c', + 'rng.c', + 'confidential-guest-support.c', +), numa]) + +softmmu_ss.add(when: 'CONFIG_POSIX', if_true: files('rng-random.c')) +softmmu_ss.add(when: 'CONFIG_POSIX', if_true: files('hostmem-file.c')) +softmmu_ss.add(when: 'CONFIG_LINUX', if_true: files('hostmem-memfd.c')) +softmmu_ss.add(when: ['CONFIG_VHOST_USER', 'CONFIG_VIRTIO'], if_true: files('vhost-user.c')) +softmmu_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('cryptodev-vhost.c')) +softmmu_ss.add(when: ['CONFIG_VIRTIO_CRYPTO', 'CONFIG_VHOST_CRYPTO'], if_true: files('cryptodev-vhost-user.c')) +softmmu_ss.add(when: 'CONFIG_GIO', if_true: [files('dbus-vmstate.c'), gio]) + +subdir('tpm') diff --git a/backends/rng-builtin.c b/backends/rng-builtin.c index ba1b8d66b8..f367eb665c 100644 --- a/backends/rng-builtin.c +++ b/backends/rng-builtin.c @@ -9,13 +9,15 @@ #include "sysemu/rng.h" #include "qemu/main-loop.h" #include "qemu/guest-random.h" +#include "qom/object.h" +#include "sysemu/replay.h" -#define RNG_BUILTIN(obj) OBJECT_CHECK(RngBuiltin, (obj), TYPE_RNG_BUILTIN) +OBJECT_DECLARE_SIMPLE_TYPE(RngBuiltin, RNG_BUILTIN) -typedef struct RngBuiltin { +struct RngBuiltin { RngBackend parent; QEMUBH *bh; -} RngBuiltin; +}; static void rng_builtin_receive_entropy_bh(void *opaque) { @@ -36,7 +38,7 @@ static void rng_builtin_request_entropy(RngBackend *b, RngRequest *req) { RngBuiltin *s = RNG_BUILTIN(b); - qemu_bh_schedule(s->bh); + replay_bh_schedule_event(s->bh); } static void rng_builtin_init(Object *obj) diff --git a/backends/rng-egd.c b/backends/rng-egd.c index 7aaa6ee239..4de142b9dc 100644 --- a/backends/rng-egd.c +++ b/backends/rng-egd.c @@ -16,17 +16,17 @@ #include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qemu/module.h" +#include "qom/object.h" #define TYPE_RNG_EGD "rng-egd" -#define RNG_EGD(obj) OBJECT_CHECK(RngEgd, (obj), TYPE_RNG_EGD) +OBJECT_DECLARE_SIMPLE_TYPE(RngEgd, RNG_EGD) -typedef struct RngEgd -{ +struct RngEgd { RngBackend parent; CharBackend chr; char *chr_name; -} RngEgd; +}; static void rng_egd_request_entropy(RngBackend *b, RngRequest *req) { @@ -135,12 +135,6 @@ static char *rng_egd_get_chardev(Object *obj, Error **errp) return NULL; } -static void rng_egd_init(Object *obj) -{ - object_property_add_str(obj, "chardev", - rng_egd_get_chardev, rng_egd_set_chardev); -} - static void rng_egd_finalize(Object *obj) { RngEgd *s = RNG_EGD(obj); @@ -155,6 +149,8 @@ static void rng_egd_class_init(ObjectClass *klass, void *data) rbc->request_entropy = rng_egd_request_entropy; rbc->opened = rng_egd_opened; + object_class_property_add_str(klass, "chardev", + rng_egd_get_chardev, rng_egd_set_chardev); } static const TypeInfo rng_egd_info = { @@ -162,7 +158,6 @@ static const TypeInfo rng_egd_info = { .parent = TYPE_RNG_BACKEND, .instance_size = sizeof(RngEgd), .class_init = rng_egd_class_init, - .instance_init = rng_egd_init, .instance_finalize = rng_egd_finalize, }; diff --git a/backends/rng-random.c b/backends/rng-random.c index 32998d8ee7..7add272edd 100644 --- a/backends/rng-random.c +++ b/backends/rng-random.c @@ -75,7 +75,7 @@ static void rng_random_opened(RngBackend *b, Error **errp) error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "filename", "a valid filename"); } else { - s->fd = qemu_open(s->filename, O_RDONLY | O_NONBLOCK); + s->fd = qemu_open_old(s->filename, O_RDONLY | O_NONBLOCK); if (s->fd == -1) { error_setg_file_open(errp, errno, s->filename); } @@ -108,10 +108,6 @@ static void rng_random_init(Object *obj) { RngRandom *s = RNG_RANDOM(obj); - object_property_add_str(obj, "filename", - rng_random_get_filename, - rng_random_set_filename); - s->filename = g_strdup("/dev/urandom"); s->fd = -1; } @@ -134,6 +130,10 @@ static void rng_random_class_init(ObjectClass *klass, void *data) rbc->request_entropy = rng_random_request_entropy; rbc->opened = rng_random_opened; + object_class_property_add_str(klass, "filename", + rng_random_get_filename, + rng_random_set_filename); + } static const TypeInfo rng_random_info = { diff --git a/backends/rng.c b/backends/rng.c index 484f04e891..3757b04485 100644 --- a/backends/rng.c +++ b/backends/rng.c @@ -105,10 +105,6 @@ static void rng_backend_init(Object *obj) RngBackend *s = RNG_BACKEND(obj); QSIMPLEQ_INIT(&s->requests); - - object_property_add_bool(obj, "opened", - rng_backend_prop_get_opened, - rng_backend_prop_set_opened); } static void rng_backend_finalize(Object *obj) @@ -123,6 +119,10 @@ static void rng_backend_class_init(ObjectClass *oc, void *data) UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); ucc->complete = rng_backend_complete; + + object_class_property_add_bool(oc, "opened", + rng_backend_prop_get_opened, + rng_backend_prop_set_opened); } static const TypeInfo rng_backend_info = { diff --git a/backends/tpm/Makefile.objs b/backends/tpm/Makefile.objs deleted file mode 100644 index db2731f634..0000000000 --- a/backends/tpm/Makefile.objs +++ /dev/null @@ -1,4 +0,0 @@ -common-obj-y += tpm_backend.o -common-obj-y += tpm_util.o -common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o -common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o diff --git a/backends/tpm/meson.build b/backends/tpm/meson.build new file mode 100644 index 0000000000..857929082e --- /dev/null +++ b/backends/tpm/meson.build @@ -0,0 +1,8 @@ +tpm_ss = ss.source_set() + +tpm_ss.add(files('tpm_backend.c')) +tpm_ss.add(files('tpm_util.c')) +tpm_ss.add(when: 'CONFIG_TPM_PASSTHROUGH', if_true: files('tpm_passthrough.c')) +tpm_ss.add(when: 'CONFIG_TPM_EMULATOR', if_true: files('tpm_emulator.c')) + +softmmu_ss.add_all(when: 'CONFIG_TPM', if_true: tpm_ss) diff --git a/backends/tpm/tpm_emulator.c b/backends/tpm/tpm_emulator.c index a9b0f55e67..a012adc193 100644 --- a/backends/tpm/tpm_emulator.c +++ b/backends/tpm/tpm_emulator.c @@ -14,7 +14,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -42,10 +42,10 @@ #include "qapi/qapi-visit-tpm.h" #include "chardev/char-fe.h" #include "trace.h" +#include "qom/object.h" #define TYPE_TPM_EMULATOR "tpm-emulator" -#define TPM_EMULATOR(obj) \ - OBJECT_CHECK(TPMEmulator, (obj), TYPE_TPM_EMULATOR) +OBJECT_DECLARE_SIMPLE_TYPE(TPMEmulator, TPM_EMULATOR) #define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap)) @@ -63,7 +63,7 @@ typedef struct TPMBlobBuffers { TPMSizedBuffer savestate; } TPMBlobBuffers; -typedef struct TPMEmulator { +struct TPMEmulator { TPMBackend parent; TPMEmulatorOptions *options; @@ -80,7 +80,7 @@ typedef struct TPMEmulator { unsigned int established_flag_cached:1; TPMBlobBuffers state_blobs; -} TPMEmulator; +}; struct tpm_error { uint32_t tpm_result; diff --git a/backends/tpm/tpm_ioctl.h b/backends/tpm/tpm_ioctl.h index f5f5c553a9..bd6c12cb86 100644 --- a/backends/tpm/tpm_ioctl.h +++ b/backends/tpm/tpm_ioctl.h @@ -12,6 +12,10 @@ #include #include +#ifdef HAVE_SYS_IOCCOM_H +#include +#endif + /* * Every response from a command involving a TPM command execution must hold * the ptm_res as the first element. diff --git a/backends/tpm/tpm_passthrough.c b/backends/tpm/tpm_passthrough.c index 7403807ec4..21b7459183 100644 --- a/backends/tpm/tpm_passthrough.c +++ b/backends/tpm/tpm_passthrough.c @@ -11,7 +11,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -33,10 +33,10 @@ #include "qapi/clone-visitor.h" #include "qapi/qapi-visit-tpm.h" #include "trace.h" +#include "qom/object.h" #define TYPE_TPM_PASSTHROUGH "tpm-passthrough" -#define TPM_PASSTHROUGH(obj) \ - OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH) +OBJECT_DECLARE_SIMPLE_TYPE(TPMPassthruState, TPM_PASSTHROUGH) /* data structures */ struct TPMPassthruState { @@ -53,7 +53,6 @@ struct TPMPassthruState { size_t tpm_buffersize; }; -typedef struct TPMPassthruState TPMPassthruState; #define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0" @@ -217,7 +216,7 @@ static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt) char path[PATH_MAX]; if (tpm_pt->options->cancel_path) { - fd = qemu_open(tpm_pt->options->cancel_path, O_WRONLY); + fd = qemu_open_old(tpm_pt->options->cancel_path, O_WRONLY); if (fd < 0) { error_report("tpm_passthrough: Could not open TPM cancel path: %s", strerror(errno)); @@ -235,11 +234,11 @@ static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt) dev++; if (snprintf(path, sizeof(path), "/sys/class/tpm/%s/device/cancel", dev) < sizeof(path)) { - fd = qemu_open(path, O_WRONLY); + fd = qemu_open_old(path, O_WRONLY); if (fd < 0) { if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel", dev) < sizeof(path)) { - fd = qemu_open(path, O_WRONLY); + fd = qemu_open_old(path, O_WRONLY); } } } @@ -271,7 +270,7 @@ tpm_passthrough_handle_device_opts(TPMPassthruState *tpm_pt, QemuOpts *opts) } tpm_pt->tpm_dev = value ? value : TPM_PASSTHROUGH_DEFAULT_DEVICE; - tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR); + tpm_pt->tpm_fd = qemu_open_old(tpm_pt->tpm_dev, O_RDWR); if (tpm_pt->tpm_fd < 0) { error_report("Cannot access TPM device using '%s': %s", tpm_pt->tpm_dev, strerror(errno)); diff --git a/backends/tpm/tpm_util.c b/backends/tpm/tpm_util.c index b58d298c1a..a6e6d3e72f 100644 --- a/backends/tpm/tpm_util.c +++ b/backends/tpm/tpm_util.c @@ -8,7 +8,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -35,8 +35,7 @@ static void get_tpm(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - DeviceState *dev = DEVICE(obj); - TPMBackend **be = qdev_get_prop_ptr(dev, opaque); + TPMBackend **be = object_field_prop_ptr(obj, opaque); char *p; p = g_strdup(*be ? (*be)->id : ""); @@ -47,16 +46,10 @@ static void get_tpm(Object *obj, Visitor *v, const char *name, void *opaque, static void set_tpm(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - DeviceState *dev = DEVICE(obj); Property *prop = opaque; - TPMBackend *s, **be = qdev_get_prop_ptr(dev, prop); + TPMBackend *s, **be = object_field_prop_ptr(obj, prop); char *str; - if (dev->realized) { - qdev_prop_set_after_realize(dev, name, errp); - return; - } - if (!visit_type_str(v, name, &str, errp)) { return; } @@ -64,7 +57,7 @@ static void set_tpm(Object *obj, Visitor *v, const char *name, void *opaque, s = qemu_find_tpm_be(str); if (s == NULL) { error_setg(errp, "Property '%s.%s' can't find value '%s'", - object_get_typename(obj), prop->name, str); + object_get_typename(obj), name, str); } else if (tpm_backend_init(s, TPM_IF(obj), errp) == 0) { *be = s; /* weak reference, avoid cyclic ref */ } @@ -73,9 +66,8 @@ static void set_tpm(Object *obj, Visitor *v, const char *name, void *opaque, static void release_tpm(Object *obj, const char *name, void *opaque) { - DeviceState *dev = DEVICE(obj); Property *prop = opaque; - TPMBackend **be = qdev_get_prop_ptr(dev, prop); + TPMBackend **be = object_field_prop_ptr(obj, prop); if (*be) { tpm_backend_reset(*be); diff --git a/backends/tpm/trace.h b/backends/tpm/trace.h new file mode 100644 index 0000000000..40c4729884 --- /dev/null +++ b/backends/tpm/trace.h @@ -0,0 +1 @@ +#include "trace/trace-backends_tpm.h" diff --git a/backends/trace.h b/backends/trace.h new file mode 100644 index 0000000000..77fe57f36c --- /dev/null +++ b/backends/trace.h @@ -0,0 +1 @@ +#include "trace/trace-backends.h" diff --git a/backends/vhost-user.c b/backends/vhost-user.c index 9e6e198546..b366610e16 100644 --- a/backends/vhost-user.c +++ b/backends/vhost-user.c @@ -175,9 +175,9 @@ static char *get_chardev(Object *obj, Error **errp) return NULL; } -static void vhost_user_backend_init(Object *obj) +static void vhost_user_backend_class_init(ObjectClass *oc, void *data) { - object_property_add_str(obj, "chardev", get_chardev, set_chardev); + object_class_property_add_str(oc, "chardev", get_chardev, set_chardev); } static void vhost_user_backend_finalize(Object *obj) @@ -195,9 +195,8 @@ static const TypeInfo vhost_user_backend_info = { .name = TYPE_VHOST_USER_BACKEND, .parent = TYPE_OBJECT, .instance_size = sizeof(VhostUserBackend), - .instance_init = vhost_user_backend_init, + .class_init = vhost_user_backend_class_init, .instance_finalize = vhost_user_backend_finalize, - .class_size = sizeof(VhostUserBackendClass), }; static void register_types(void) diff --git a/block.c b/block.c index d9ac0e07eb..c5b887cec1 100644 --- a/block.c +++ b/block.c @@ -26,10 +26,11 @@ #include "block/trace.h" #include "block/block_int.h" #include "block/blockjob.h" +#include "block/fuse.h" #include "block/nbd.h" #include "block/qdict.h" #include "qemu/error-report.h" -#include "module_block.h" +#include "block/module_block.h" #include "qemu/main-loop.h" #include "qemu/module.h" #include "qapi/error.h" @@ -48,6 +49,7 @@ #include "qemu/timer.h" #include "qemu/cutils.h" #include "qemu/id.h" +#include "block/coroutines.h" #ifdef CONFIG_BSD #include @@ -215,7 +217,7 @@ void bdrv_parse_filename_strip_prefix(const char *filename, const char *prefix, /* Stripping the explicit protocol prefix may result in a protocol * prefix being (wrongly) detected (if the filename contains a colon) */ if (path_has_protocol(filename)) { - QString *fat_filename; + GString *fat_filename; /* This means there is some colon before the first slash; therefore, * this cannot be an absolute path */ @@ -223,12 +225,13 @@ void bdrv_parse_filename_strip_prefix(const char *filename, const char *prefix, /* And we can thus fix the protocol detection issue by prefixing it * by "./" */ - fat_filename = qstring_from_str("./"); - qstring_append(fat_filename, filename); + fat_filename = g_string_new("./"); + g_string_append(fat_filename, filename); - assert(!path_has_protocol(qstring_get_str(fat_filename))); + assert(!path_has_protocol(fat_filename->str)); - qdict_put(options, "filename", fat_filename); + qdict_put(options, "filename", + qstring_from_gstring(fat_filename)); } else { /* If no protocol prefix was detected, we can use the shortened * filename as-is */ @@ -433,9 +436,11 @@ static int bdrv_format_is_whitelisted(const char *format_name, bool read_only) { static const char *whitelist_rw[] = { CONFIG_BDRV_RW_WHITELIST + NULL }; static const char *whitelist_ro[] = { CONFIG_BDRV_RO_WHITELIST + NULL }; const char **p; @@ -665,14 +670,48 @@ out: int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp) { + QemuOpts *protocol_opts; BlockDriver *drv; + QDict *qdict; + int ret; drv = bdrv_find_protocol(filename, true, errp); if (drv == NULL) { return -ENOENT; } - return bdrv_create(drv, filename, opts, errp); + if (!drv->create_opts) { + error_setg(errp, "Driver '%s' does not support image creation", + drv->format_name); + return -ENOTSUP; + } + + /* + * 'opts' contains a QemuOptsList with a combination of format and protocol + * default values. + * + * The format properly removes its options, but the default values remain + * in 'opts->list'. So if the protocol has options with the same name + * (e.g. rbd has 'cluster_size' as qcow2), it will see the default values + * of the format, since for overlapping options, the format wins. + * + * To avoid this issue, lets convert QemuOpts to QDict, in this way we take + * only the set options, and then convert it back to QemuOpts, using the + * create_opts of the protocol. So the new QemuOpts, will contain only the + * protocol defaults. + */ + qdict = qemu_opts_to_qdict(opts, NULL); + protocol_opts = qemu_opts_from_qdict(drv->create_opts, qdict, errp); + if (protocol_opts == NULL) { + ret = -EINVAL; + goto out; + } + + ret = bdrv_create(drv, filename, protocol_opts, errp); +out: + qemu_opts_del(protocol_opts); + qobject_unref(qdict); + return ret; } int coroutine_fn bdrv_co_delete_file(BlockDriverState *bs, Error **errp) @@ -701,6 +740,28 @@ int coroutine_fn bdrv_co_delete_file(BlockDriverState *bs, Error **errp) return ret; } +void coroutine_fn bdrv_co_delete_file_noerr(BlockDriverState *bs) +{ + Error *local_err = NULL; + int ret; + + if (!bs) { + return; + } + + ret = bdrv_co_delete_file(bs, &local_err); + /* + * ENOTSUP will happen if the block driver doesn't support + * the 'bdrv_co_delete_file' interface. This is a predictable + * scenario and shouldn't be reported back to the user. + */ + if (ret == -ENOTSUP) { + error_free(local_err); + } else if (ret < 0) { + error_report_err(local_err); + } +} + /** * Try to get @bs's logical and physical block size. * On success, store them in @bsz struct and return 0. @@ -710,11 +771,12 @@ int coroutine_fn bdrv_co_delete_file(BlockDriverState *bs, Error **errp) int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) { BlockDriver *drv = bs->drv; + BlockDriverState *filtered = bdrv_filter_bs(bs); if (drv && drv->bdrv_probe_blocksizes) { return drv->bdrv_probe_blocksizes(bs, bsz); - } else if (drv && drv->is_filter && bs->file) { - return bdrv_probe_blocksizes(bs->file->bs, bsz); + } else if (filtered) { + return bdrv_probe_blocksizes(filtered, bsz); } return -ENOTSUP; @@ -729,11 +791,12 @@ int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo) { BlockDriver *drv = bs->drv; + BlockDriverState *filtered = bdrv_filter_bs(bs); if (drv && drv->bdrv_probe_geometry) { return drv->bdrv_probe_geometry(bs, geo); - } else if (drv && drv->is_filter && bs->file) { - return bdrv_probe_geometry(bs->file->bs, geo); + } else if (filtered) { + return bdrv_probe_geometry(filtered, geo); } return -ENOTSUP; @@ -956,6 +1019,11 @@ int refresh_total_sectors(BlockDriverState *bs, int64_t hint) } bs->total_sectors = hint; + + if (bs->total_sectors * BDRV_SECTOR_SIZE > BDRV_MAX_LENGTH) { + return -EFBIG; + } + return 0; } @@ -1151,10 +1219,6 @@ static void bdrv_backing_attach(BdrvChild *c) bdrv_refresh_filename(backing_hd); parent->open_flags &= ~BDRV_O_NO_BACKING; - pstrcpy(parent->backing_file, sizeof(parent->backing_file), - backing_hd->filename); - pstrcpy(parent->backing_format, sizeof(parent->backing_format), - backing_hd->drv ? backing_hd->drv->format_name : ""); bdrv_op_block_all(backing_hd, parent->backing_blocker); /* Otherwise we won't be able to commit or stream */ @@ -1410,7 +1474,7 @@ static void bdrv_assign_node_name(BlockDriverState *bs, * Check for empty string or invalid characters, but not if it is * generated (generated names use characters not available to the user) */ - error_setg(errp, "Invalid node name"); + error_setg(errp, "Invalid node-name: '%s'", node_name); return; } @@ -1423,7 +1487,7 @@ static void bdrv_assign_node_name(BlockDriverState *bs, /* takes care of avoiding duplicates node names */ if (bdrv_find_node(node_name)) { - error_setg(errp, "Duplicate node name"); + error_setg(errp, "Duplicate nodes with node-name='%s'", node_name); goto out; } @@ -1694,7 +1758,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, } /* bdrv_new() and bdrv_close() make it so */ - assert(atomic_read(&bs->copy_on_read) == 0); + assert(qatomic_read(&bs->copy_on_read) == 0); if (bs->open_flags & BDRV_O_COPY_ON_READ) { if (!bs->read_only) { @@ -1893,10 +1957,9 @@ static int bdrv_fill_options(QDict **options, const char *filename, static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q, uint64_t perm, uint64_t shared, - GSList *ignore_children, - bool *tighten_restrictions, Error **errp); + GSList *ignore_children, Error **errp); static void bdrv_child_abort_perm_update(BdrvChild *c); -static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared); +static void bdrv_child_set_perm(BdrvChild *c); typedef struct BlockReopenQueueEntry { bool prepared; @@ -1967,43 +2030,18 @@ static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs, * permissions of all its parents. This involves checking whether all necessary * permission changes to child nodes can be performed. * - * Will set *tighten_restrictions to true if and only if new permissions have to - * be taken or currently shared permissions are to be unshared. Otherwise, - * errors are not fatal as long as the caller accepts that the restrictions - * remain tighter than they need to be. The caller still has to abort the - * transaction. - * @tighten_restrictions cannot be used together with @q: When reopening, we may - * encounter fatal errors even though no restrictions are to be tightened. For - * example, changing a node from RW to RO will fail if the WRITE permission is - * to be kept. - * * A call to this function must always be followed by a call to bdrv_set_perm() * or bdrv_abort_perm_update(). */ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q, uint64_t cumulative_perms, uint64_t cumulative_shared_perms, - GSList *ignore_children, - bool *tighten_restrictions, Error **errp) + GSList *ignore_children, Error **errp) { BlockDriver *drv = bs->drv; BdrvChild *c; int ret; - assert(!q || !tighten_restrictions); - - if (tighten_restrictions) { - uint64_t current_perms, current_shared; - uint64_t added_perms, removed_shared_perms; - - bdrv_get_cumulative_perm(bs, ¤t_perms, ¤t_shared); - - added_perms = cumulative_perms & ~current_perms; - removed_shared_perms = current_shared & ~cumulative_shared_perms; - - *tighten_restrictions = added_perms || removed_shared_perms; - } - /* Write permissions never work with read-only images */ if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) && !bdrv_is_writable_after_reopen(bs, q)) @@ -2047,8 +2085,11 @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q, } if (drv->bdrv_check_perm) { - return drv->bdrv_check_perm(bs, cumulative_perms, - cumulative_shared_perms, errp); + ret = drv->bdrv_check_perm(bs, cumulative_perms, + cumulative_shared_perms, errp); + if (ret < 0) { + return ret; + } } /* Drivers that never have children can omit .bdrv_child_perm() */ @@ -2060,18 +2101,12 @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q, /* Check all children */ QLIST_FOREACH(c, &bs->children, next) { uint64_t cur_perm, cur_shared; - bool child_tighten_restr; bdrv_child_perm(bs, c->bs, c, c->role, q, cumulative_perms, cumulative_shared_perms, &cur_perm, &cur_shared); ret = bdrv_child_check_perm(c, q, cur_perm, cur_shared, ignore_children, - tighten_restrictions ? &child_tighten_restr - : NULL, errp); - if (tighten_restrictions) { - *tighten_restrictions |= child_tighten_restr; - } if (ret < 0) { return ret; } @@ -2105,9 +2140,9 @@ static void bdrv_abort_perm_update(BlockDriverState *bs) } } -static void bdrv_set_perm(BlockDriverState *bs, uint64_t cumulative_perms, - uint64_t cumulative_shared_perms) +static void bdrv_set_perm(BlockDriverState *bs) { + uint64_t cumulative_perms, cumulative_shared_perms; BlockDriver *drv = bs->drv; BdrvChild *c; @@ -2115,6 +2150,8 @@ static void bdrv_set_perm(BlockDriverState *bs, uint64_t cumulative_perms, return; } + bdrv_get_cumulative_perm(bs, &cumulative_perms, &cumulative_shared_perms); + /* Update this node */ if (drv->bdrv_set_perm) { drv->bdrv_set_perm(bs, cumulative_perms, cumulative_shared_perms); @@ -2128,11 +2165,7 @@ static void bdrv_set_perm(BlockDriverState *bs, uint64_t cumulative_perms, /* Update all children */ QLIST_FOREACH(c, &bs->children, next) { - uint64_t cur_perm, cur_shared; - bdrv_child_perm(bs, c->bs, c, c->role, NULL, - cumulative_perms, cumulative_shared_perms, - &cur_perm, &cur_shared); - bdrv_child_set_perm(c, cur_perm, cur_shared); + bdrv_child_set_perm(c); } } @@ -2196,22 +2229,18 @@ char *bdrv_perm_names(uint64_t perm) * set, the BdrvChild objects in this list are ignored in the calculations; * this allows checking permission updates for an existing reference. * - * See bdrv_check_perm() for the semantics of @tighten_restrictions. - * * Needs to be followed by a call to either bdrv_set_perm() or * bdrv_abort_perm_update(). */ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q, uint64_t new_used_perm, uint64_t new_shared_perm, GSList *ignore_children, - bool *tighten_restrictions, Error **errp) { BdrvChild *c; uint64_t cumulative_perms = new_used_perm; uint64_t cumulative_shared_perms = new_shared_perm; - assert(!q || !tighten_restrictions); /* There is no reason why anyone couldn't tolerate write_unchanged */ assert(new_shared_perm & BLK_PERM_WRITE_UNCHANGED); @@ -2225,10 +2254,6 @@ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q, char *user = bdrv_child_user_desc(c); char *perm_names = bdrv_perm_names(new_used_perm & ~c->shared_perm); - if (tighten_restrictions) { - *tighten_restrictions = true; - } - error_setg(errp, "Conflicts with use by %s as '%s', which does not " "allow '%s' on %s", user, c->name, perm_names, bdrv_get_node_name(c->bs)); @@ -2241,10 +2266,6 @@ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q, char *user = bdrv_child_user_desc(c); char *perm_names = bdrv_perm_names(c->perm & ~new_shared_perm); - if (tighten_restrictions) { - *tighten_restrictions = true; - } - error_setg(errp, "Conflicts with use by %s as '%s', which uses " "'%s' on %s", user, c->name, perm_names, bdrv_get_node_name(c->bs)); @@ -2258,21 +2279,19 @@ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q, } return bdrv_check_perm(bs, q, cumulative_perms, cumulative_shared_perms, - ignore_children, tighten_restrictions, errp); + ignore_children, errp); } /* Needs to be followed by a call to either bdrv_child_set_perm() or * bdrv_child_abort_perm_update(). */ static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q, uint64_t perm, uint64_t shared, - GSList *ignore_children, - bool *tighten_restrictions, Error **errp) + GSList *ignore_children, Error **errp) { int ret; ignore_children = g_slist_prepend(g_slist_copy(ignore_children), c); - ret = bdrv_check_update_perm(c->bs, q, perm, shared, ignore_children, - tighten_restrictions, errp); + ret = bdrv_check_update_perm(c->bs, q, perm, shared, ignore_children, errp); g_slist_free(ignore_children); if (ret < 0) { @@ -2295,18 +2314,11 @@ static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q, return 0; } -static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared) +static void bdrv_child_set_perm(BdrvChild *c) { - uint64_t cumulative_perms, cumulative_shared_perms; - c->has_backup_perm = false; - c->perm = perm; - c->shared_perm = shared; - - bdrv_get_cumulative_perm(c->bs, &cumulative_perms, - &cumulative_shared_perms); - bdrv_set_perm(c->bs, cumulative_perms, cumulative_shared_perms); + bdrv_set_perm(c->bs); } static void bdrv_child_abort_perm_update(BdrvChild *c) @@ -2320,18 +2332,33 @@ static void bdrv_child_abort_perm_update(BdrvChild *c) bdrv_abort_perm_update(c->bs); } +static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp) +{ + int ret; + uint64_t perm, shared_perm; + + bdrv_get_cumulative_perm(bs, &perm, &shared_perm); + ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, errp); + if (ret < 0) { + bdrv_abort_perm_update(bs); + return ret; + } + bdrv_set_perm(bs); + + return 0; +} + int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, Error **errp) { Error *local_err = NULL; int ret; - bool tighten_restrictions; - ret = bdrv_child_check_perm(c, NULL, perm, shared, NULL, - &tighten_restrictions, &local_err); + ret = bdrv_child_check_perm(c, NULL, perm, shared, NULL, &local_err); if (ret < 0) { bdrv_child_abort_perm_update(c); - if (tighten_restrictions) { + if ((perm & ~c->perm) || (c->shared_perm & ~shared)) { + /* tighten permissions */ error_propagate(errp, local_err); } else { /* @@ -2346,7 +2373,7 @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, return ret; } - bdrv_child_set_perm(c, perm, shared); + bdrv_child_set_perm(c); return 0; } @@ -2602,7 +2629,7 @@ static void bdrv_replace_child_noperm(BdrvChild *child, /* * Updates @child to change its reference to point to @new_bs, including - * checking and applying the necessary permisson updates both to the old node + * checking and applying the necessary permission updates both to the old node * and to @new_bs. * * NULL is passed as @new_bs for removing the reference before freeing @child. @@ -2610,12 +2637,14 @@ static void bdrv_replace_child_noperm(BdrvChild *child, * If @new_bs is not NULL, bdrv_check_perm() must be called beforehand, as this * function uses bdrv_set_perm() to update the permissions according to the new * reference that @new_bs gets. + * + * Callers must ensure that child->frozen is false. */ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs) { BlockDriverState *old_bs = child->bs; - uint64_t perm, shared_perm; + /* Asserts that child->frozen == false */ bdrv_replace_child_noperm(child, new_bs); /* @@ -2627,27 +2656,16 @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs) * restrictions. */ if (new_bs) { - bdrv_get_cumulative_perm(new_bs, &perm, &shared_perm); - bdrv_set_perm(new_bs, perm, shared_perm); + bdrv_set_perm(new_bs); } if (old_bs) { - /* Update permissions for old node. This is guaranteed to succeed - * because we're just taking a parent away, so we're loosening - * restrictions. */ - bool tighten_restrictions; - int ret; - - bdrv_get_cumulative_perm(old_bs, &perm, &shared_perm); - ret = bdrv_check_perm(old_bs, NULL, perm, shared_perm, NULL, - &tighten_restrictions, NULL); - assert(tighten_restrictions == false); - if (ret < 0) { - /* We only tried to loosen restrictions, so errors are not fatal */ - bdrv_abort_perm_update(old_bs); - } else { - bdrv_set_perm(old_bs, perm, shared_perm); - } + /* + * Update permissions for old node. We're just taking a parent away, so + * we're loosening restrictions. Errors of permission update are not + * fatal in this case, ignore them. + */ + bdrv_refresh_perms(old_bs, NULL); /* When the parent requiring a non-default AioContext is removed, the * node moves back to the main AioContext */ @@ -2677,8 +2695,7 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, Error *local_err = NULL; int ret; - ret = bdrv_check_update_perm(child_bs, NULL, perm, shared_perm, NULL, NULL, - errp); + ret = bdrv_check_update_perm(child_bs, NULL, perm, shared_perm, NULL, errp); if (ret < 0) { bdrv_abort_perm_update(child_bs); bdrv_unref(child_bs); @@ -2776,6 +2793,7 @@ static void bdrv_detach_child(BdrvChild *child) g_free(child); } +/* Callers must ensure that child->frozen is false. */ void bdrv_root_unref_child(BdrvChild *child) { BlockDriverState *child_bs; @@ -2813,6 +2831,7 @@ static void bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child) } } +/* Callers must ensure that child->frozen is false. */ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child) { if (child == NULL) { @@ -2861,17 +2880,18 @@ static BdrvChildRole bdrv_backing_role(BlockDriverState *bs) } /* - * Sets the backing file link of a BDS. A new reference is created; callers + * Sets the bs->backing link of a BDS. A new reference is created; callers * which don't need their own reference any more must call bdrv_unref(). */ -void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, - Error **errp) +int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, + Error **errp) { + int ret = 0; bool update_inherits_from = bdrv_chain_contains(bs, backing_hd) && bdrv_inherits_from_recursive(backing_hd, bs); - if (bdrv_is_backing_chain_frozen(bs, backing_bs(bs), errp)) { - return; + if (bdrv_is_backing_chain_frozen(bs, child_bs(bs->backing), errp)) { + return -EPERM; } if (backing_hd) { @@ -2879,6 +2899,7 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, } if (bs->backing) { + /* Cannot be frozen, we checked that above */ bdrv_unref_child(bs, bs->backing); bs->backing = NULL; } @@ -2889,15 +2910,22 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_of_bds, bdrv_backing_role(bs), errp); + if (!bs->backing) { + ret = -EPERM; + goto out; + } + /* If backing_hd was already part of bs's backing chain, and * inherits_from pointed recursively to bs then let's update it to * point directly to bs (else it will become NULL). */ - if (bs->backing && update_inherits_from) { + if (update_inherits_from) { backing_hd->inherits_from = bs; } out: bdrv_refresh_limits(bs, NULL); + + return ret; } /* @@ -3001,11 +3029,9 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, /* Hook up the backing file link; drop our reference, bs owns the * backing_hd reference now */ - bdrv_set_backing_hd(bs, backing_hd, &local_err); + ret = bdrv_set_backing_hd(bs, backing_hd, errp); bdrv_unref(backing_hd); - if (local_err) { - error_propagate(errp, local_err); - ret = -EINVAL; + if (ret < 0) { goto free_exit; } @@ -3146,7 +3172,6 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, int64_t total_size; QemuOpts *opts = NULL; BlockDriverState *bs_snapshot = NULL; - Error *local_err = NULL; int ret; /* if snapshot, we create a temporary backing file and open it @@ -3193,9 +3218,8 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, * order to be able to return one, we have to increase * bs_snapshot's refcount here */ bdrv_ref(bs_snapshot); - bdrv_append(bs_snapshot, bs, &local_err); - if (local_err) { - error_propagate(errp, local_err); + ret = bdrv_append(bs_snapshot, bs, errp); + if (ret < 0) { bs_snapshot = NULL; goto out; } @@ -3807,7 +3831,7 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) QTAILQ_FOREACH(bs_entry, bs_queue, entry) { BDRVReopenState *state = &bs_entry->state; ret = bdrv_check_perm(state->bs, bs_queue, state->perm, - state->shared_perm, NULL, NULL, errp); + state->shared_perm, NULL, errp); if (ret < 0) { goto cleanup_perm; } @@ -3819,7 +3843,7 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) bs_queue, state->perm, state->shared_perm, &nperm, &nshared); ret = bdrv_check_update_perm(state->new_backing_bs, NULL, - nperm, nshared, NULL, NULL, errp); + nperm, nshared, NULL, errp); if (ret < 0) { goto cleanup_perm; } @@ -3850,7 +3874,13 @@ cleanup_perm: } if (ret == 0) { - bdrv_set_perm(state->bs, state->perm, state->shared_perm); + uint64_t perm, shared; + + bdrv_get_cumulative_perm(state->bs, &perm, &shared); + assert(perm == state->perm); + assert(shared == state->shared_perm); + + bdrv_set_perm(state->bs); } else { bdrv_abort_perm_update(state->bs); if (state->replace_backing_bs && state->new_backing_bs) { @@ -3994,7 +4024,7 @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state, Error **errp) { BlockDriverState *bs = reopen_state->bs; - BlockDriverState *overlay_bs, *new_backing_bs; + BlockDriverState *overlay_bs, *below_bs, *new_backing_bs; QObject *value; const char *str; @@ -4008,7 +4038,7 @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state, new_backing_bs = NULL; break; case QTYPE_QSTRING: - str = qobject_get_try_str(value); + str = qstring_get_str(qobject_to(QString, value)); new_backing_bs = bdrv_lookup_bs(NULL, str, errp); if (new_backing_bs == NULL) { return -EINVAL; @@ -4033,26 +4063,57 @@ static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state, } } + /* + * Ensure that @bs can really handle backing files, because we are + * about to give it one (or swap the existing one) + */ + if (bs->drv->is_filter) { + /* Filters always have a file or a backing child */ + if (!bs->backing) { + error_setg(errp, "'%s' is a %s filter node that does not support a " + "backing child", bs->node_name, bs->drv->format_name); + return -EINVAL; + } + } else if (!bs->drv->supports_backing) { + error_setg(errp, "Driver '%s' of node '%s' does not support backing " + "files", bs->drv->format_name, bs->node_name); + return -EINVAL; + } + /* * Find the "actual" backing file by skipping all links that point * to an implicit node, if any (e.g. a commit filter node). + * We cannot use any of the bdrv_skip_*() functions here because + * those return the first explicit node, while we are looking for + * its overlay here. */ overlay_bs = bs; - while (backing_bs(overlay_bs) && backing_bs(overlay_bs)->implicit) { - overlay_bs = backing_bs(overlay_bs); + for (below_bs = bdrv_filter_or_cow_bs(overlay_bs); + below_bs && below_bs->implicit; + below_bs = bdrv_filter_or_cow_bs(overlay_bs)) + { + overlay_bs = below_bs; } /* If we want to replace the backing file we need some extra checks */ - if (new_backing_bs != backing_bs(overlay_bs)) { + if (new_backing_bs != bdrv_filter_or_cow_bs(overlay_bs)) { /* Check for implicit nodes between bs and its backing file */ if (bs != overlay_bs) { error_setg(errp, "Cannot change backing link if '%s' has " "an implicit backing file", bs->node_name); return -EPERM; } - /* Check if the backing link that we want to replace is frozen */ - if (bdrv_is_backing_chain_frozen(overlay_bs, backing_bs(overlay_bs), - errp)) { + /* + * Check if the backing link that we want to replace is frozen. + * Note that + * bdrv_filter_or_cow_child(overlay_bs) == overlay_bs->backing, + * because we know that overlay_bs == bs, and that @bs + * either is a filter that uses ->backing or a COW format BDS + * with bs->drv->supports_backing == true. + */ + if (bdrv_is_backing_chain_frozen(overlay_bs, + child_bs(overlay_bs->backing), errp)) + { return -EPERM; } reopen_state->replace_backing_bs = true; @@ -4201,7 +4262,7 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, * its metadata. Otherwise the 'backing' option can be omitted. */ if (drv->supports_backing && reopen_state->backing_missing && - (backing_bs(reopen_state->bs) || reopen_state->bs->backing_file[0])) { + (reopen_state->bs->backing || reopen_state->bs->backing_file[0])) { error_setg(errp, "backing is missing for '%s'", reopen_state->bs->node_name); ret = -EINVAL; @@ -4240,8 +4301,8 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, } if (child) { - const char *str = qobject_get_try_str(new); - if (!strcmp(child->bs->node_name, str)) { + if (!strcmp(child->bs->node_name, + qstring_get_str(qobject_to(QString, new)))) { continue; /* Found child with this name, skip option */ } } @@ -4342,7 +4403,7 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state) * from bdrv_set_backing_hd()) has the new values. */ if (reopen_state->replace_backing_bs) { - BlockDriverState *old_backing_bs = backing_bs(bs); + BlockDriverState *old_backing_bs = child_bs(bs->backing); assert(!old_backing_bs || !old_backing_bs->implicit); /* Abort the permission update on the backing bs we're detaching */ if (old_backing_bs) { @@ -4385,6 +4446,7 @@ static void bdrv_close(BlockDriverState *bs) if (bs->drv) { if (bs->drv->bdrv_close) { + /* Must unfreeze all children, so bdrv_unref_child() works */ bs->drv->bdrv_close(bs); } bs->drv = NULL; @@ -4398,7 +4460,7 @@ static void bdrv_close(BlockDriverState *bs) bs->file = NULL; g_free(bs->opaque); bs->opaque = NULL; - atomic_set(&bs->copy_on_read, 0); + qatomic_set(&bs->copy_on_read, 0); bs->backing_file[0] = '\0'; bs->backing_format[0] = '\0'; bs->total_sectors = 0; @@ -4419,12 +4481,20 @@ static void bdrv_close(BlockDriverState *bs) } QLIST_INIT(&bs->aio_notifiers); bdrv_drained_end(bs); + + /* + * If we're still inside some bdrv_drain_all_begin()/end() sections, end + * them now since this BDS won't exist anymore when bdrv_drain_all_end() + * gets called. + */ + if (bs->quiesce_counter) { + bdrv_drain_all_end_quiesce(bs); + } } void bdrv_close_all(void) { assert(job_next(NULL) == NULL); - nbd_export_close_all(); /* Drop references from requests still in flight, such as canceled block * jobs whose AIO context has not been polled yet */ @@ -4515,8 +4585,16 @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to) return ret; } -void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, - Error **errp) +/* + * With auto_skip=true bdrv_replace_node_common skips updating from parents + * if it creates a parent-child relation loop or if parent is block-job. + * + * With auto_skip=false the error is returned if from has a parent which should + * not be updated. + */ +static int bdrv_replace_node_common(BlockDriverState *from, + BlockDriverState *to, + bool auto_skip, Error **errp) { BdrvChild *c, *next; GSList *list = NULL, *p; @@ -4535,9 +4613,16 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { assert(c->bs == from); if (!should_update_child(c, to)) { - continue; + if (auto_skip) { + continue; + } + ret = -EINVAL; + error_setg(errp, "Should not change '%s' link to '%s'", + c->name, from->node_name); + goto out; } if (c->frozen) { + ret = -EPERM; error_setg(errp, "Cannot change '%s' link to '%s'", c->name, from->node_name); goto out; @@ -4549,7 +4634,7 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, /* Check whether the required permissions can be granted on @to, ignoring * all BdrvChild in @list so that they can't block themselves. */ - ret = bdrv_check_update_perm(to, NULL, perm, shared, list, NULL, errp); + ret = bdrv_check_update_perm(to, NULL, perm, shared, list, errp); if (ret < 0) { bdrv_abort_perm_update(to); goto out; @@ -4566,13 +4651,22 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, bdrv_unref(from); } - bdrv_get_cumulative_perm(to, &perm, &shared); - bdrv_set_perm(to, perm, shared); + bdrv_set_perm(to); + + ret = 0; out: g_slist_free(list); bdrv_drained_end(from); bdrv_unref(from); + + return ret; +} + +int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, + Error **errp) +{ + return bdrv_replace_node_common(from, to, true, errp); } /* @@ -4591,28 +4685,30 @@ out: * parents of bs_top after bdrv_append() returns. If the caller needs to keep a * reference of its own, it must call bdrv_ref(). */ -void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, - Error **errp) +int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, + Error **errp) { - Error *local_err = NULL; - - bdrv_set_backing_hd(bs_new, bs_top, &local_err); - if (local_err) { - error_propagate(errp, local_err); + int ret = bdrv_set_backing_hd(bs_new, bs_top, errp); + if (ret < 0) { goto out; } - bdrv_replace_node(bs_top, bs_new, &local_err); - if (local_err) { - error_propagate(errp, local_err); + ret = bdrv_replace_node(bs_top, bs_new, errp); + if (ret < 0) { bdrv_set_backing_hd(bs_new, NULL, &error_abort); goto out; } - /* bs_new is now referenced by its new parents, we don't need the - * additional reference any more. */ + ret = 0; + out: + /* + * bs_new is now referenced by its new parents, we don't need the + * additional reference any more. + */ bdrv_unref(bs_new); + + return ret; } static void bdrv_delete(BlockDriverState *bs) @@ -4631,6 +4727,31 @@ static void bdrv_delete(BlockDriverState *bs) g_free(bs); } +BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options, + int flags, Error **errp) +{ + BlockDriverState *new_node_bs; + Error *local_err = NULL; + + new_node_bs = bdrv_open(NULL, NULL, node_options, flags, errp); + if (new_node_bs == NULL) { + error_prepend(errp, "Could not create node: "); + return NULL; + } + + bdrv_drained_begin(bs); + bdrv_replace_node(bs, new_node_bs, &local_err); + bdrv_drained_end(bs); + + if (local_err) { + bdrv_unref(new_node_bs); + error_propagate(errp, local_err); + return NULL; + } + + return new_node_bs; +} + /* * Run consistency checks on an image * @@ -4638,8 +4759,8 @@ static void bdrv_delete(BlockDriverState *bs) * free of errors) or -errno when an internal error occurred. The results of the * check are stored in res. */ -static int coroutine_fn bdrv_co_check(BlockDriverState *bs, - BdrvCheckResult *res, BdrvCheckMode fix) +int coroutine_fn bdrv_co_check(BlockDriverState *bs, + BdrvCheckResult *res, BdrvCheckMode fix) { if (bs->drv == NULL) { return -ENOMEDIUM; @@ -4652,43 +4773,6 @@ static int coroutine_fn bdrv_co_check(BlockDriverState *bs, return bs->drv->bdrv_co_check(bs, res, fix); } -typedef struct CheckCo { - BlockDriverState *bs; - BdrvCheckResult *res; - BdrvCheckMode fix; - int ret; -} CheckCo; - -static void coroutine_fn bdrv_check_co_entry(void *opaque) -{ - CheckCo *cco = opaque; - cco->ret = bdrv_co_check(cco->bs, cco->res, cco->fix); - aio_wait_kick(); -} - -int bdrv_check(BlockDriverState *bs, - BdrvCheckResult *res, BdrvCheckMode fix) -{ - Coroutine *co; - CheckCo cco = { - .bs = bs, - .res = res, - .ret = -EINPROGRESS, - .fix = fix, - }; - - if (qemu_in_coroutine()) { - /* Fast-path if already in coroutine context */ - bdrv_check_co_entry(&cco); - } else { - co = qemu_coroutine_create(bdrv_check_co_entry, &cco); - bdrv_coroutine_enter(bs, co); - BDRV_POLL_WHILE(bs, cco.ret == -EINPROGRESS); - } - - return cco.ret; -} - /* * Return values: * 0 - success @@ -4734,9 +4818,9 @@ int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, } /* - * Finds the image layer in the chain that has 'bs' as its backing file. - * - * active is the current topmost image. + * Finds the first non-filter node above bs in the chain between + * active and bs. The returned node is either an immediate parent of + * bs, or there are only filter nodes between the two. * * Returns NULL if bs is not found in active's image chain, * or if active == bs. @@ -4746,11 +4830,18 @@ int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, BlockDriverState *bdrv_find_overlay(BlockDriverState *active, BlockDriverState *bs) { - while (active && bs != backing_bs(active)) { - active = backing_bs(active); + bs = bdrv_skip_filters(bs); + active = bdrv_skip_filters(active); + + while (active) { + BlockDriverState *next = bdrv_backing_chain_next(active); + if (bs == next) { + return active; + } + active = next; } - return active; + return NULL; } /* Given a BDS, searches for the base layer. */ @@ -4760,20 +4851,22 @@ BlockDriverState *bdrv_find_base(BlockDriverState *bs) } /* - * Return true if at least one of the backing links between @bs and - * @base is frozen. @errp is set if that's the case. + * Return true if at least one of the COW (backing) and filter links + * between @bs and @base is frozen. @errp is set if that's the case. * @base must be reachable from @bs, or NULL. */ bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base, Error **errp) { BlockDriverState *i; + BdrvChild *child; - for (i = bs; i != base; i = backing_bs(i)) { - if (i->backing && i->backing->frozen) { + for (i = bs; i != base; i = child_bs(child)) { + child = bdrv_filter_or_cow_child(i); + + if (child && child->frozen) { error_setg(errp, "Cannot change '%s' link from '%s' to '%s'", - i->backing->name, i->node_name, - backing_bs(i)->node_name); + child->name, i->node_name, child->bs->node_name); return true; } } @@ -4782,7 +4875,7 @@ bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base, } /* - * Freeze all backing links between @bs and @base. + * Freeze all COW (backing) and filter links between @bs and @base. * If any of the links is already frozen the operation is aborted and * none of the links are modified. * @base must be reachable from @bs, or NULL. @@ -4792,22 +4885,25 @@ int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base, Error **errp) { BlockDriverState *i; + BdrvChild *child; if (bdrv_is_backing_chain_frozen(bs, base, errp)) { return -EPERM; } - for (i = bs; i != base; i = backing_bs(i)) { - if (i->backing && backing_bs(i)->never_freeze) { + for (i = bs; i != base; i = child_bs(child)) { + child = bdrv_filter_or_cow_child(i); + if (child && child->bs->never_freeze) { error_setg(errp, "Cannot freeze '%s' link to '%s'", - i->backing->name, backing_bs(i)->node_name); + child->name, child->bs->node_name); return -EPERM; } } - for (i = bs; i != base; i = backing_bs(i)) { - if (i->backing) { - i->backing->frozen = true; + for (i = bs; i != base; i = child_bs(child)) { + child = bdrv_filter_or_cow_child(i); + if (child) { + child->frozen = true; } } @@ -4815,18 +4911,21 @@ int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base, } /* - * Unfreeze all backing links between @bs and @base. The caller must - * ensure that all links are frozen before using this function. + * Unfreeze all COW (backing) and filter links between @bs and @base. + * The caller must ensure that all links are frozen before using this + * function. * @base must be reachable from @bs, or NULL. */ void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base) { BlockDriverState *i; + BdrvChild *child; - for (i = bs; i != base; i = backing_bs(i)) { - if (i->backing) { - assert(i->backing->frozen); - i->backing->frozen = false; + for (i = bs; i != base; i = child_bs(child)) { + child = bdrv_filter_or_cow_child(i); + if (child) { + assert(child->frozen); + child->frozen = false; } } } @@ -4865,9 +4964,11 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, { BlockDriverState *explicit_top = top; bool update_inherits_from; - BdrvChild *c, *next; + BdrvChild *c; Error *local_err = NULL; int ret = -EIO; + g_autoptr(GSList) updated_children = NULL; + GSList *p; bdrv_ref(top); bdrv_subtree_drained_begin(top); @@ -4881,22 +4982,12 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, goto exit; } - /* This function changes all links that point to top and makes - * them point to base. Check that none of them is frozen. */ - QLIST_FOREACH(c, &top->parents, next_parent) { - if (c->frozen) { - goto exit; - } - } - /* If 'base' recursively inherits from 'top' then we should set * base->inherits_from to top->inherits_from after 'top' and all * other intermediate nodes have been dropped. * If 'top' is an implicit node (e.g. "commit_top") we should skip * it because no one inherits from it. We use explicit_top for that. */ - while (explicit_top && explicit_top->implicit) { - explicit_top = backing_bs(explicit_top); - } + explicit_top = bdrv_skip_implicit_filters(explicit_top); update_inherits_from = bdrv_inherits_from_recursive(base, explicit_top); /* success - we can delete the intermediate states, and link top->base */ @@ -4907,33 +4998,36 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, backing_file_str = base->filename; } - QLIST_FOREACH_SAFE(c, &top->parents, next_parent, next) { - /* Check whether we are allowed to switch c from top to base */ - GSList *ignore_children = g_slist_prepend(NULL, c); - ret = bdrv_check_update_perm(base, NULL, c->perm, c->shared_perm, - ignore_children, NULL, &local_err); - g_slist_free(ignore_children); - if (ret < 0) { - error_report_err(local_err); - goto exit; - } + QLIST_FOREACH(c, &top->parents, next_parent) { + updated_children = g_slist_prepend(updated_children, c); + } + + bdrv_replace_node_common(top, base, false, &local_err); + if (local_err) { + error_report_err(local_err); + goto exit; + } + + for (p = updated_children; p; p = p->next) { + c = p->data; - /* If so, update the backing file path in the image file */ if (c->klass->update_filename) { ret = c->klass->update_filename(c, base, backing_file_str, &local_err); if (ret < 0) { - bdrv_abort_perm_update(base); + /* + * TODO: Actually, we want to rollback all previous iterations + * of this loop, and (which is almost impossible) previous + * bdrv_replace_node()... + * + * Note, that c->klass->update_filename may lead to permission + * update, so it's a bad idea to call it inside permission + * update transaction of bdrv_replace_node. + */ error_report_err(local_err); goto exit; } } - - /* Do the actual switch in the in-memory graph. - * Completes bdrv_check_update_perm() transaction internally. */ - bdrv_ref(base); - bdrv_replace_child(c, base); - bdrv_unref(top); } if (update_inherits_from) { @@ -4947,6 +5041,31 @@ exit: return ret; } +/** + * Implementation of BlockDriver.bdrv_get_allocated_file_size() that + * sums the size of all data-bearing children. (This excludes backing + * children.) + */ +static int64_t bdrv_sum_allocated_file_size(BlockDriverState *bs) +{ + BdrvChild *child; + int64_t child_size, sum = 0; + + QLIST_FOREACH(child, &bs->children, next) { + if (child->role & (BDRV_CHILD_DATA | BDRV_CHILD_METADATA | + BDRV_CHILD_FILTERED)) + { + child_size = bdrv_get_allocated_file_size(child->bs); + if (child_size < 0) { + return child_size; + } + sum += child_size; + } + } + + return sum; +} + /** * Length of a allocated file in bytes. Sparse files are counted by actual * allocated space. Return < 0 if error or unknown. @@ -4960,10 +5079,21 @@ int64_t bdrv_get_allocated_file_size(BlockDriverState *bs) if (drv->bdrv_get_allocated_file_size) { return drv->bdrv_get_allocated_file_size(bs); } - if (bs->file) { - return bdrv_get_allocated_file_size(bs->file->bs); + + if (drv->bdrv_file_open) { + /* + * Protocol drivers default to -ENOTSUP (most of their data is + * not stored in any of their children (if they even have any), + * so there is no generic way to figure it out). + */ + return -ENOTSUP; + } else if (drv->is_filter) { + /* Filter drivers default to the size of their filtered child */ + return bdrv_get_allocated_file_size(bdrv_filter_bs(bs)); + } else { + /* Other drivers default to summing their children's sizes */ + return bdrv_sum_allocated_file_size(bs); } - return -ENOTSUP; } /* @@ -5028,8 +5158,13 @@ int64_t bdrv_getlength(BlockDriverState *bs) { int64_t ret = bdrv_nb_sectors(bs); - ret = ret > INT64_MAX / BDRV_SECTOR_SIZE ? -EFBIG : ret; - return ret < 0 ? ret : ret * BDRV_SECTOR_SIZE; + if (ret < 0) { + return ret; + } + if (ret > INT64_MAX / BDRV_SECTOR_SIZE) { + return -EFBIG; + } + return ret * BDRV_SECTOR_SIZE; } /* return 0 as number of sectors if no device present or error */ @@ -5045,12 +5180,27 @@ bool bdrv_is_sg(BlockDriverState *bs) return bs->sg; } -bool bdrv_is_encrypted(BlockDriverState *bs) +/** + * Return whether the given node supports compressed writes. + */ +bool bdrv_supports_compressed_writes(BlockDriverState *bs) { - if (bs->backing && bs->backing->bs->encrypted) { - return true; + BlockDriverState *filtered; + + if (!bs->drv || !block_driver_can_compress(bs->drv)) { + return false; } - return bs->encrypted; + + filtered = bdrv_filter_bs(bs); + if (filtered) { + /* + * Filters can only forward compressed writes, so we have to + * check the child. + */ + return bdrv_supports_compressed_writes(filtered); + } + + return true; } const char *bdrv_get_format_name(BlockDriverState *bs) @@ -5142,7 +5292,7 @@ BlockDriverState *bdrv_find_node(const char *node_name) BlockDeviceInfoList *bdrv_named_nodes_list(bool flat, Error **errp) { - BlockDeviceInfoList *list, *entry; + BlockDeviceInfoList *list; BlockDriverState *bs; list = NULL; @@ -5152,22 +5302,12 @@ BlockDeviceInfoList *bdrv_named_nodes_list(bool flat, qapi_free_BlockDeviceInfoList(list); return NULL; } - entry = g_malloc0(sizeof(*entry)); - entry->value = info; - entry->next = list; - list = entry; + QAPI_LIST_PREPEND(list, info); } return list; } -#define QAPI_LIST_ADD(list, element) do { \ - typeof(list) _tmp = g_new(typeof(*(list)), 1); \ - _tmp->value = (element); \ - _tmp->next = (list); \ - (list) = _tmp; \ -} while (0) - typedef struct XDbgBlockGraphConstructor { XDbgBlockGraph *graph; GHashTable *graph_nodes; @@ -5222,7 +5362,7 @@ static void xdbg_graph_add_node(XDbgBlockGraphConstructor *gr, void *node, n->type = type; n->name = g_strdup(name); - QAPI_LIST_ADD(gr->graph->nodes, n); + QAPI_LIST_PREPEND(gr->graph->nodes, n); } static void xdbg_graph_add_edge(XDbgBlockGraphConstructor *gr, void *parent, @@ -5241,14 +5381,14 @@ static void xdbg_graph_add_edge(XDbgBlockGraphConstructor *gr, void *parent, uint64_t flag = bdrv_qapi_perm_to_blk_perm(qapi_perm); if (flag & child->perm) { - QAPI_LIST_ADD(edge->perm, qapi_perm); + QAPI_LIST_PREPEND(edge->perm, qapi_perm); } if (flag & child->shared_perm) { - QAPI_LIST_ADD(edge->shared_perm, qapi_perm); + QAPI_LIST_PREPEND(edge->shared_perm, qapi_perm); } } - QAPI_LIST_ADD(gr->graph->edges, edge); + QAPI_LIST_PREPEND(gr->graph->edges, edge); } @@ -5324,7 +5464,7 @@ BlockDriverState *bdrv_lookup_bs(const char *device, } } - error_setg(errp, "Cannot find device=%s nor node_name=%s", + error_setg(errp, "Cannot find device=\'%s\' nor node-name=\'%s\'", device ? device : "", node_name ? node_name : ""); return NULL; @@ -5335,7 +5475,7 @@ BlockDriverState *bdrv_lookup_bs(const char *device, bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base) { while (top && top != base) { - top = backing_bs(top); + top = bdrv_filter_or_cow_bs(top); } return top != NULL; @@ -5407,20 +5547,24 @@ int bdrv_has_zero_init_1(BlockDriverState *bs) int bdrv_has_zero_init(BlockDriverState *bs) { + BlockDriverState *filtered; + if (!bs->drv) { return 0; } /* If BS is a copy on write image, it is initialized to the contents of the base image, which may not be zeroes. */ - if (bs->backing) { + if (bdrv_cow_child(bs)) { return 0; } if (bs->drv->bdrv_has_zero_init) { return bs->drv->bdrv_has_zero_init(bs); } - if (bs->file && bs->drv->is_filter) { - return bdrv_has_zero_init(bs->file->bs); + + filtered = bdrv_filter_bs(bs); + if (filtered) { + return bdrv_has_zero_init(filtered); } /* safe default */ @@ -5444,19 +5588,30 @@ void bdrv_get_backing_filename(BlockDriverState *bs, int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { + int ret; BlockDriver *drv = bs->drv; /* if bs->drv == NULL, bs is closed, so there's nothing to do here */ if (!drv) { return -ENOMEDIUM; } if (!drv->bdrv_get_info) { - if (bs->file && drv->is_filter) { - return bdrv_get_info(bs->file->bs, bdi); + BlockDriverState *filtered = bdrv_filter_bs(bs); + if (filtered) { + return bdrv_get_info(filtered, bdi); } return -ENOTSUP; } memset(bdi, 0, sizeof(*bdi)); - return drv->bdrv_get_info(bs, bdi); + ret = drv->bdrv_get_info(bs, bdi); + if (ret < 0) { + return ret; + } + + if (bdi->cluster_size > BDRV_MAX_ALIGNMENT) { + return -EINVAL; + } + + return 0; } ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs, @@ -5490,17 +5645,7 @@ void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event) static BlockDriverState *bdrv_find_debug_node(BlockDriverState *bs) { while (bs && bs->drv && !bs->drv->bdrv_debug_breakpoint) { - if (bs->file) { - bs = bs->file->bs; - continue; - } - - if (bs->drv->is_filter && bs->backing) { - bs = bs->backing->bs; - continue; - } - - break; + bs = bdrv_primary_bs(bs); } if (bs && bs->drv && bs->drv->bdrv_debug_breakpoint) { @@ -5535,7 +5680,7 @@ int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag) int bdrv_debug_resume(BlockDriverState *bs, const char *tag) { while (bs && (!bs->drv || !bs->drv->bdrv_debug_resume)) { - bs = bs->file ? bs->file->bs : NULL; + bs = bdrv_primary_bs(bs); } if (bs && bs->drv && bs->drv->bdrv_debug_resume) { @@ -5548,7 +5693,7 @@ int bdrv_debug_resume(BlockDriverState *bs, const char *tag) bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag) { while (bs && bs->drv && !bs->drv->bdrv_debug_is_suspended) { - bs = bs->file ? bs->file->bs : NULL; + bs = bdrv_primary_bs(bs); } if (bs && bs->drv && bs->drv->bdrv_debug_is_suspended) { @@ -5569,8 +5714,10 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, char *backing_file_full = NULL; char *filename_tmp = NULL; int is_protocol = 0; + bool filenames_refreshed = false; BlockDriverState *curr_bs = NULL; BlockDriverState *retval = NULL; + BlockDriverState *bs_below; if (!bs || !bs->drv || !backing_file) { return NULL; @@ -5581,15 +5728,47 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, is_protocol = path_has_protocol(backing_file); - for (curr_bs = bs; curr_bs->backing; curr_bs = curr_bs->backing->bs) { + /* + * Being largely a legacy function, skip any filters here + * (because filters do not have normal filenames, so they cannot + * match anyway; and allowing json:{} filenames is a bit out of + * scope). + */ + for (curr_bs = bdrv_skip_filters(bs); + bdrv_cow_child(curr_bs) != NULL; + curr_bs = bs_below) + { + bs_below = bdrv_backing_chain_next(curr_bs); - /* If either of the filename paths is actually a protocol, then - * compare unmodified paths; otherwise make paths relative */ - if (is_protocol || path_has_protocol(curr_bs->backing_file)) { + if (bdrv_backing_overridden(curr_bs)) { + /* + * If the backing file was overridden, we can only compare + * directly against the backing node's filename. + */ + + if (!filenames_refreshed) { + /* + * This will automatically refresh all of the + * filenames in the rest of the backing chain, so we + * only need to do this once. + */ + bdrv_refresh_filename(bs_below); + filenames_refreshed = true; + } + + if (strcmp(backing_file, bs_below->filename) == 0) { + retval = bs_below; + break; + } + } else if (is_protocol || path_has_protocol(curr_bs->backing_file)) { + /* + * If either of the filename paths is actually a protocol, then + * compare unmodified paths; otherwise make paths relative. + */ char *backing_file_full_ret; if (strcmp(backing_file, curr_bs->backing_file) == 0) { - retval = curr_bs->backing->bs; + retval = bs_below; break; } /* Also check against the full backing filename for the image */ @@ -5599,7 +5778,7 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, bool equal = strcmp(backing_file, backing_file_full_ret) == 0; g_free(backing_file_full_ret); if (equal) { - retval = curr_bs->backing->bs; + retval = bs_below; break; } } @@ -5625,7 +5804,7 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, g_free(filename_tmp); if (strcmp(backing_file_full, filename_full) == 0) { - retval = curr_bs->backing->bs; + retval = bs_below; break; } } @@ -5647,24 +5826,22 @@ void bdrv_init_with_whitelist(void) bdrv_init(); } -static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, - Error **errp) +int coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp) { BdrvChild *child, *parent; - uint64_t perm, shared_perm; Error *local_err = NULL; int ret; BdrvDirtyBitmap *bm; if (!bs->drv) { - return; + return -ENOMEDIUM; } QLIST_FOREACH(child, &bs->children, next) { bdrv_co_invalidate_cache(child->bs, &local_err); if (local_err) { error_propagate(errp, local_err); - return; + return -EINVAL; } } @@ -5683,20 +5860,18 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, */ if (bs->open_flags & BDRV_O_INACTIVE) { bs->open_flags &= ~BDRV_O_INACTIVE; - bdrv_get_cumulative_perm(bs, &perm, &shared_perm); - ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, NULL, errp); + ret = bdrv_refresh_perms(bs, errp); if (ret < 0) { bs->open_flags |= BDRV_O_INACTIVE; - return; + return ret; } - bdrv_set_perm(bs, perm, shared_perm); if (bs->drv->bdrv_co_invalidate_cache) { bs->drv->bdrv_co_invalidate_cache(bs, &local_err); if (local_err) { bs->open_flags |= BDRV_O_INACTIVE; error_propagate(errp, local_err); - return; + return -EINVAL; } } @@ -5708,7 +5883,7 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, if (ret < 0) { bs->open_flags |= BDRV_O_INACTIVE; error_setg_errno(errp, -ret, "Could not refresh total sector count"); - return; + return ret; } } @@ -5718,59 +5893,27 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, if (local_err) { bs->open_flags |= BDRV_O_INACTIVE; error_propagate(errp, local_err); - return; + return -EINVAL; } } } -} -typedef struct InvalidateCacheCo { - BlockDriverState *bs; - Error **errp; - bool done; -} InvalidateCacheCo; - -static void coroutine_fn bdrv_invalidate_cache_co_entry(void *opaque) -{ - InvalidateCacheCo *ico = opaque; - bdrv_co_invalidate_cache(ico->bs, ico->errp); - ico->done = true; - aio_wait_kick(); -} - -void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp) -{ - Coroutine *co; - InvalidateCacheCo ico = { - .bs = bs, - .done = false, - .errp = errp - }; - - if (qemu_in_coroutine()) { - /* Fast-path if already in coroutine context */ - bdrv_invalidate_cache_co_entry(&ico); - } else { - co = qemu_coroutine_create(bdrv_invalidate_cache_co_entry, &ico); - bdrv_coroutine_enter(bs, co); - BDRV_POLL_WHILE(bs, !ico.done); - } + return 0; } void bdrv_invalidate_cache_all(Error **errp) { BlockDriverState *bs; - Error *local_err = NULL; BdrvNextIterator it; for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { AioContext *aio_context = bdrv_get_aio_context(bs); + int ret; aio_context_acquire(aio_context); - bdrv_invalidate_cache(bs, &local_err); + ret = bdrv_invalidate_cache(bs, errp); aio_context_release(aio_context); - if (local_err) { - error_propagate(errp, local_err); + if (ret < 0) { bdrv_next_cleanup(&it); return; } @@ -5796,8 +5939,6 @@ static bool bdrv_has_bds_parent(BlockDriverState *bs, bool only_active) static int bdrv_inactivate_recurse(BlockDriverState *bs) { BdrvChild *child, *parent; - bool tighten_restrictions; - uint64_t perm, shared_perm; int ret; if (!bs->drv) { @@ -5831,18 +5972,12 @@ static int bdrv_inactivate_recurse(BlockDriverState *bs) bs->open_flags |= BDRV_O_INACTIVE; - /* Update permissions, they may differ for inactive nodes */ - bdrv_get_cumulative_perm(bs, &perm, &shared_perm); - ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, - &tighten_restrictions, NULL); - assert(tighten_restrictions == false); - if (ret < 0) { - /* We only tried to loosen restrictions, so errors are not fatal */ - bdrv_abort_perm_update(bs); - } else { - bdrv_set_perm(bs, perm, shared_perm); - } - + /* + * Update permissions, they may differ for inactive nodes. + * We only tried to loosen restrictions, so errors are not fatal, ignore + * them. + */ + bdrv_refresh_perms(bs, NULL); /* Recursively inactivate children */ QLIST_FOREACH(child, &bs->children, next) { @@ -6117,6 +6252,10 @@ void bdrv_img_create(const char *filename, const char *fmt, "same filename as the backing file"); goto out; } + if (backing_file[0] == '\0') { + error_setg(errp, "Expected backing file name, got empty string"); + goto out; + } } backing_fmt = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT); @@ -6234,6 +6373,56 @@ AioContext *bdrv_get_aio_context(BlockDriverState *bs) return bs ? bs->aio_context : qemu_get_aio_context(); } +AioContext *coroutine_fn bdrv_co_enter(BlockDriverState *bs) +{ + Coroutine *self = qemu_coroutine_self(); + AioContext *old_ctx = qemu_coroutine_get_aio_context(self); + AioContext *new_ctx; + + /* + * Increase bs->in_flight to ensure that this operation is completed before + * moving the node to a different AioContext. Read new_ctx only afterwards. + */ + bdrv_inc_in_flight(bs); + + new_ctx = bdrv_get_aio_context(bs); + aio_co_reschedule_self(new_ctx); + return old_ctx; +} + +void coroutine_fn bdrv_co_leave(BlockDriverState *bs, AioContext *old_ctx) +{ + aio_co_reschedule_self(old_ctx); + bdrv_dec_in_flight(bs); +} + +void coroutine_fn bdrv_co_lock(BlockDriverState *bs) +{ + AioContext *ctx = bdrv_get_aio_context(bs); + + /* In the main thread, bs->aio_context won't change concurrently */ + assert(qemu_get_current_aio_context() == qemu_get_aio_context()); + + /* + * We're in coroutine context, so we already hold the lock of the main + * loop AioContext. Don't lock it twice to avoid deadlocks. + */ + assert(qemu_in_coroutine()); + if (ctx != qemu_get_aio_context()) { + aio_context_acquire(ctx); + } +} + +void coroutine_fn bdrv_co_unlock(BlockDriverState *bs) +{ + AioContext *ctx = bdrv_get_aio_context(bs); + + assert(qemu_in_coroutine()); + if (ctx != qemu_get_aio_context()) { + aio_context_release(ctx); + } +} + void bdrv_coroutine_enter(BlockDriverState *bs, Coroutine *co) { aio_co_enter(bdrv_get_aio_context(bs), co); @@ -6317,7 +6506,10 @@ void bdrv_set_aio_context_ignore(BlockDriverState *bs, AioContext *new_context, GSList **ignore) { AioContext *old_context = bdrv_get_aio_context(bs); - BdrvChild *child; + GSList *children_to_process = NULL; + GSList *parents_to_process = NULL; + GSList *entry; + BdrvChild *child, *parent; g_assert(qemu_get_current_aio_context() == qemu_get_aio_context()); @@ -6332,17 +6524,34 @@ void bdrv_set_aio_context_ignore(BlockDriverState *bs, continue; } *ignore = g_slist_prepend(*ignore, child); - bdrv_set_aio_context_ignore(child->bs, new_context, ignore); + children_to_process = g_slist_prepend(children_to_process, child); } - QLIST_FOREACH(child, &bs->parents, next_parent) { - if (g_slist_find(*ignore, child)) { + + QLIST_FOREACH(parent, &bs->parents, next_parent) { + if (g_slist_find(*ignore, parent)) { continue; } - assert(child->klass->set_aio_ctx); - *ignore = g_slist_prepend(*ignore, child); - child->klass->set_aio_ctx(child, new_context, ignore); + *ignore = g_slist_prepend(*ignore, parent); + parents_to_process = g_slist_prepend(parents_to_process, parent); } + for (entry = children_to_process; + entry != NULL; + entry = g_slist_next(entry)) { + child = entry->data; + bdrv_set_aio_context_ignore(child->bs, new_context, ignore); + } + g_slist_free(children_to_process); + + for (entry = parents_to_process; + entry != NULL; + entry = g_slist_next(entry)) { + parent = entry->data; + assert(parent->klass->set_aio_ctx); + parent->klass->set_aio_ctx(parent, new_context, ignore); + } + g_slist_free(parents_to_process); + bdrv_detach_aio_context(bs); /* Acquire the new context, if necessary */ @@ -6532,6 +6741,8 @@ int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts, bool bdrv_recurse_can_replace(BlockDriverState *bs, BlockDriverState *to_replace) { + BlockDriverState *filtered; + if (!bs || !bs->drv) { return false; } @@ -6546,9 +6757,9 @@ bool bdrv_recurse_can_replace(BlockDriverState *bs, } /* For filters without an own implementation, we can recurse on our own */ - if (bs->drv->is_filter) { - BdrvChild *child = bs->file ?: bs->backing; - return bdrv_recurse_can_replace(child->bs, to_replace); + filtered = bdrv_filter_bs(bs); + if (filtered) { + return bdrv_recurse_can_replace(filtered, to_replace); } /* Safe default */ @@ -6573,7 +6784,7 @@ BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs, AioContext *aio_context; if (!to_replace_bs) { - error_setg(errp, "Node name '%s' not found", node_name); + error_setg(errp, "Failed to find node with node-name='%s'", node_name); return NULL; } @@ -6699,7 +6910,7 @@ static bool append_strong_runtime_options(QDict *d, BlockDriverState *bs) /* Note: This function may return false positives; it may return true * even if opening the backing file specified by bs's image header * would result in exactly bs->backing. */ -static bool bdrv_backing_overridden(BlockDriverState *bs) +bool bdrv_backing_overridden(BlockDriverState *bs) { if (bs->backing) { return strcmp(bs->auto_backing_file, @@ -6727,6 +6938,7 @@ void bdrv_refresh_filename(BlockDriverState *bs) { BlockDriver *drv = bs->drv; BdrvChild *child; + BlockDriverState *primary_child_bs; QDict *opts; bool backing_overridden; bool generate_json_filename; /* Whether our default implementation should @@ -6796,20 +7008,30 @@ void bdrv_refresh_filename(BlockDriverState *bs) qobject_unref(bs->full_open_options); bs->full_open_options = opts; + primary_child_bs = bdrv_primary_bs(bs); + if (drv->bdrv_refresh_filename) { /* Obsolete information is of no use here, so drop the old file name * information before refreshing it */ bs->exact_filename[0] = '\0'; drv->bdrv_refresh_filename(bs); - } else if (bs->file) { - /* Try to reconstruct valid information from the underlying file */ + } else if (primary_child_bs) { + /* + * Try to reconstruct valid information from the underlying + * file -- this only works for format nodes (filter nodes + * cannot be probed and as such must be selected by the user + * either through an options dict, or through a special + * filename which the filter driver must construct in its + * .bdrv_refresh_filename() implementation). + */ bs->exact_filename[0] = '\0'; /* * We can use the underlying file's filename if: * - it has a filename, + * - the current BDS is not a filter, * - the file is a protocol BDS, and * - opening that file (as this BDS's format) will automatically create * the BDS tree we have right now, that is: @@ -6818,30 +7040,31 @@ void bdrv_refresh_filename(BlockDriverState *bs) * - no non-file child of this BDS has been overridden by the user * Both of these conditions are represented by generate_json_filename. */ - if (bs->file->bs->exact_filename[0] && - bs->file->bs->drv->bdrv_file_open && - !generate_json_filename) + if (primary_child_bs->exact_filename[0] && + primary_child_bs->drv->bdrv_file_open && + !drv->is_filter && !generate_json_filename) { - strcpy(bs->exact_filename, bs->file->bs->exact_filename); + strcpy(bs->exact_filename, primary_child_bs->exact_filename); } } if (bs->exact_filename[0]) { pstrcpy(bs->filename, sizeof(bs->filename), bs->exact_filename); } else { - QString *json = qobject_to_json(QOBJECT(bs->full_open_options)); + GString *json = qobject_to_json(QOBJECT(bs->full_open_options)); if (snprintf(bs->filename, sizeof(bs->filename), "json:%s", - qstring_get_str(json)) >= sizeof(bs->filename)) { + json->str) >= sizeof(bs->filename)) { /* Give user a hint if we truncated things. */ strcpy(bs->filename + sizeof(bs->filename) - 4, "..."); } - qobject_unref(json); + g_string_free(json, true); } } char *bdrv_dirname(BlockDriverState *bs, Error **errp) { BlockDriver *drv = bs->drv; + BlockDriverState *child_bs; if (!drv) { error_setg(errp, "Node '%s' is ejected", bs->node_name); @@ -6852,8 +7075,9 @@ char *bdrv_dirname(BlockDriverState *bs, Error **errp) return drv->bdrv_dirname(bs, errp); } - if (bs->file) { - return bdrv_dirname(bs->file->bs, errp); + child_bs = bdrv_primary_bs(bs); + if (child_bs) { + return bdrv_dirname(child_bs, errp); } bdrv_refresh_filename(bs); @@ -6937,3 +7161,156 @@ int bdrv_make_empty(BdrvChild *c, Error **errp) return 0; } + +/* + * Return the child that @bs acts as an overlay for, and from which data may be + * copied in COW or COR operations. Usually this is the backing file. + */ +BdrvChild *bdrv_cow_child(BlockDriverState *bs) +{ + if (!bs || !bs->drv) { + return NULL; + } + + if (bs->drv->is_filter) { + return NULL; + } + + if (!bs->backing) { + return NULL; + } + + assert(bs->backing->role & BDRV_CHILD_COW); + return bs->backing; +} + +/* + * If @bs acts as a filter for exactly one of its children, return + * that child. + */ +BdrvChild *bdrv_filter_child(BlockDriverState *bs) +{ + BdrvChild *c; + + if (!bs || !bs->drv) { + return NULL; + } + + if (!bs->drv->is_filter) { + return NULL; + } + + /* Only one of @backing or @file may be used */ + assert(!(bs->backing && bs->file)); + + c = bs->backing ?: bs->file; + if (!c) { + return NULL; + } + + assert(c->role & BDRV_CHILD_FILTERED); + return c; +} + +/* + * Return either the result of bdrv_cow_child() or bdrv_filter_child(), + * whichever is non-NULL. + * + * Return NULL if both are NULL. + */ +BdrvChild *bdrv_filter_or_cow_child(BlockDriverState *bs) +{ + BdrvChild *cow_child = bdrv_cow_child(bs); + BdrvChild *filter_child = bdrv_filter_child(bs); + + /* Filter nodes cannot have COW backing files */ + assert(!(cow_child && filter_child)); + + return cow_child ?: filter_child; +} + +/* + * Return the primary child of this node: For filters, that is the + * filtered child. For other nodes, that is usually the child storing + * metadata. + * (A generally more helpful description is that this is (usually) the + * child that has the same filename as @bs.) + * + * Drivers do not necessarily have a primary child; for example quorum + * does not. + */ +BdrvChild *bdrv_primary_child(BlockDriverState *bs) +{ + BdrvChild *c, *found = NULL; + + QLIST_FOREACH(c, &bs->children, next) { + if (c->role & BDRV_CHILD_PRIMARY) { + assert(!found); + found = c; + } + } + + return found; +} + +static BlockDriverState *bdrv_do_skip_filters(BlockDriverState *bs, + bool stop_on_explicit_filter) +{ + BdrvChild *c; + + if (!bs) { + return NULL; + } + + while (!(stop_on_explicit_filter && !bs->implicit)) { + c = bdrv_filter_child(bs); + if (!c) { + /* + * A filter that is embedded in a working block graph must + * have a child. Assert this here so this function does + * not return a filter node that is not expected by the + * caller. + */ + assert(!bs->drv || !bs->drv->is_filter); + break; + } + bs = c->bs; + } + /* + * Note that this treats nodes with bs->drv == NULL as not being + * filters (bs->drv == NULL should be replaced by something else + * anyway). + * The advantage of this behavior is that this function will thus + * always return a non-NULL value (given a non-NULL @bs). + */ + + return bs; +} + +/* + * Return the first BDS that has not been added implicitly or that + * does not have a filtered child down the chain starting from @bs + * (including @bs itself). + */ +BlockDriverState *bdrv_skip_implicit_filters(BlockDriverState *bs) +{ + return bdrv_do_skip_filters(bs, true); +} + +/* + * Return the first BDS that does not have a filtered child down the + * chain starting from @bs (including @bs itself). + */ +BlockDriverState *bdrv_skip_filters(BlockDriverState *bs) +{ + return bdrv_do_skip_filters(bs, false); +} + +/* + * For a backing chain, return the first non-filter backing image of + * the first non-filter image. + */ +BlockDriverState *bdrv_backing_chain_next(BlockDriverState *bs) +{ + return bdrv_skip_filters(bdrv_cow_bs(bdrv_skip_filters(bs))); +} diff --git a/block/Makefile.objs b/block/Makefile.objs deleted file mode 100644 index 19c6f371c9..0000000000 --- a/block/Makefile.objs +++ /dev/null @@ -1,75 +0,0 @@ -block-obj-y += raw-format.o vmdk.o vpc.o -block-obj-$(CONFIG_QCOW1) += qcow.o -block-obj-$(CONFIG_VDI) += vdi.o -block-obj-$(CONFIG_CLOOP) += cloop.o -block-obj-$(CONFIG_BOCHS) += bochs.o -block-obj-$(CONFIG_VVFAT) += vvfat.o -block-obj-$(CONFIG_DMG) += dmg.o - -block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o qcow2-threads.o -block-obj-$(CONFIG_QED) += qed.o qed-l2-cache.o qed-table.o qed-cluster.o -block-obj-$(CONFIG_QED) += qed-check.o -block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o -block-obj-y += quorum.o -block-obj-y += blkdebug.o blkverify.o blkreplay.o -block-obj-$(CONFIG_PARALLELS) += parallels.o -block-obj-y += blklogwrites.o -block-obj-y += block-backend.o snapshot.o qapi.o -block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o -block-obj-$(CONFIG_POSIX) += file-posix.o -block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o -block-obj-$(CONFIG_LINUX_IO_URING) += io_uring.o -block-obj-y += null.o mirror.o commit.o io.o create.o amend.o -block-obj-y += throttle-groups.o -block-obj-$(CONFIG_LINUX) += nvme.o - -block-obj-y += nbd.o -block-obj-$(CONFIG_SHEEPDOG) += sheepdog.o -block-obj-$(CONFIG_LIBISCSI) += iscsi.o -block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o -block-obj-$(CONFIG_LIBNFS) += nfs.o -block-obj-$(CONFIG_CURL) += curl.o -block-obj-$(CONFIG_RBD) += rbd.o -block-obj-$(CONFIG_GLUSTERFS) += gluster.o -block-obj-$(CONFIG_LIBSSH) += ssh.o -block-obj-y += accounting.o dirty-bitmap.o -block-obj-y += write-threshold.o -block-obj-y += backup.o -block-obj-$(CONFIG_REPLICATION) += replication.o -block-obj-y += throttle.o copy-on-read.o -block-obj-y += block-copy.o - -block-obj-y += crypto.o - -block-obj-y += aio_task.o -block-obj-y += backup-top.o -block-obj-y += filter-compress.o -common-obj-y += monitor/ -block-obj-y += monitor/ - -block-obj-y += stream.o - -common-obj-y += qapi-sysemu.o - -nfs.o-libs := $(LIBNFS_LIBS) -iscsi.o-cflags := $(LIBISCSI_CFLAGS) -iscsi.o-libs := $(LIBISCSI_LIBS) -curl.o-cflags := $(CURL_CFLAGS) -curl.o-libs := $(CURL_LIBS) -rbd.o-cflags := $(RBD_CFLAGS) -rbd.o-libs := $(RBD_LIBS) -gluster.o-cflags := $(GLUSTERFS_CFLAGS) -gluster.o-libs := $(GLUSTERFS_LIBS) -ssh.o-cflags := $(LIBSSH_CFLAGS) -ssh.o-libs := $(LIBSSH_LIBS) -block-obj-dmg-bz2-$(CONFIG_BZIP2) += dmg-bz2.o -block-obj-$(if $(CONFIG_DMG),m,n) += $(block-obj-dmg-bz2-y) -dmg-bz2.o-libs := $(BZIP2_LIBS) -block-obj-$(if $(CONFIG_LZFSE),m,n) += dmg-lzfse.o -dmg-lzfse.o-libs := $(LZFSE_LIBS) -qcow.o-libs := -lz -linux-aio.o-libs := -laio -io_uring.o-cflags := $(LINUX_IO_URING_CFLAGS) -io_uring.o-libs := $(LINUX_IO_URING_LIBS) -parallels.o-cflags := $(LIBXML2_CFLAGS) -parallels.o-libs := $(LIBXML2_LIBS) diff --git a/block/accounting.c b/block/accounting.c index 8d41c8a83a..2030851d79 100644 --- a/block/accounting.c +++ b/block/accounting.c @@ -199,29 +199,27 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, return; } - qemu_mutex_lock(&stats->lock); + WITH_QEMU_LOCK_GUARD(&stats->lock) { + if (failed) { + stats->failed_ops[cookie->type]++; + } else { + stats->nr_bytes[cookie->type] += cookie->bytes; + stats->nr_ops[cookie->type]++; + } - if (failed) { - stats->failed_ops[cookie->type]++; - } else { - stats->nr_bytes[cookie->type] += cookie->bytes; - stats->nr_ops[cookie->type]++; - } + block_latency_histogram_account(&stats->latency_histogram[cookie->type], + latency_ns); - block_latency_histogram_account(&stats->latency_histogram[cookie->type], - latency_ns); + if (!failed || stats->account_failed) { + stats->total_time_ns[cookie->type] += latency_ns; + stats->last_access_time_ns = time_ns; - if (!failed || stats->account_failed) { - stats->total_time_ns[cookie->type] += latency_ns; - stats->last_access_time_ns = time_ns; - - QSLIST_FOREACH(s, &stats->intervals, entries) { - timed_average_account(&s->latency[cookie->type], latency_ns); + QSLIST_FOREACH(s, &stats->intervals, entries) { + timed_average_account(&s->latency[cookie->type], latency_ns); + } } } - qemu_mutex_unlock(&stats->lock); - cookie->type = BLOCK_ACCT_NONE; } diff --git a/block/backup-top.c b/block/backup-top.c index af2f20f346..589e8b651d 100644 --- a/block/backup-top.c +++ b/block/backup-top.c @@ -45,6 +45,12 @@ static coroutine_fn int backup_top_co_preadv( BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { + BDRVBackupTopState *s = bs->opaque; + + if (!s->active) { + return -EIO; + } + return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); } @@ -54,6 +60,10 @@ static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset, BDRVBackupTopState *s = bs->opaque; uint64_t off, end; + if (!s->active) { + return -EIO; + } + if (flags & BDRV_REQ_WRITE_UNCHANGED) { return 0; } @@ -61,7 +71,7 @@ static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset, off = QEMU_ALIGN_DOWN(offset, s->cluster_size); end = QEMU_ALIGN_UP(offset + bytes, s->cluster_size); - return block_copy(s->bcs, off, end - off, NULL); + return block_copy(s->bcs, off, end - off, true); } static int coroutine_fn backup_top_co_pdiscard(BlockDriverState *bs, @@ -175,8 +185,6 @@ BlockDriver bdrv_backup_top_filter = { .bdrv_co_pdiscard = backup_top_co_pdiscard, .bdrv_co_flush = backup_top_co_flush, - .bdrv_co_block_status = bdrv_co_block_status_from_backing, - .bdrv_refresh_filename = backup_top_refresh_filename, .bdrv_child_perm = backup_top_child_perm, @@ -188,11 +196,13 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, BlockDriverState *target, const char *filter_node_name, uint64_t cluster_size, + BackupPerf *perf, BdrvRequestFlags write_flags, BlockCopyState **bcs, Error **errp) { - Error *local_err = NULL; + ERRP_GUARD(); + int ret; BDRVBackupTopState *state; BlockDriverState *top; bool appended = false; @@ -225,9 +235,9 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, bdrv_drained_begin(source); bdrv_ref(top); - bdrv_append(top, source, &local_err); - if (local_err) { - error_prepend(&local_err, "Cannot append backup-top filter: "); + ret = bdrv_append(top, source, errp); + if (ret < 0) { + error_prepend(errp, "Cannot append backup-top filter: "); goto fail; } appended = true; @@ -237,18 +247,18 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, * we want. */ state->active = true; - bdrv_child_refresh_perms(top, top->backing, &local_err); - if (local_err) { - error_prepend(&local_err, - "Cannot set permissions for backup-top filter: "); + ret = bdrv_child_refresh_perms(top, top->backing, errp); + if (ret < 0) { + error_prepend(errp, "Cannot set permissions for backup-top filter: "); goto fail; } state->cluster_size = cluster_size; state->bcs = block_copy_state_new(top->backing, state->target, - cluster_size, write_flags, &local_err); - if (local_err) { - error_prepend(&local_err, "Cannot create block-copy-state: "); + cluster_size, perf->use_copy_range, + write_flags, errp); + if (!state->bcs) { + error_prepend(errp, "Cannot create block-copy-state: "); goto fail; } *bcs = state->bcs; @@ -266,7 +276,6 @@ fail: } bdrv_drained_end(source); - error_propagate(errp, local_err); return NULL; } @@ -281,7 +290,7 @@ void bdrv_backup_top_drop(BlockDriverState *bs) s->active = false; bdrv_child_refresh_perms(bs, bs->backing, &error_abort); - bdrv_replace_node(bs, backing_bs(bs), &error_abort); + bdrv_replace_node(bs, bs->backing->bs, &error_abort); bdrv_set_backing_hd(bs, NULL, &error_abort); bdrv_drained_end(bs); diff --git a/block/backup-top.h b/block/backup-top.h index e5cabfa197..b28b0031c4 100644 --- a/block/backup-top.h +++ b/block/backup-top.h @@ -33,6 +33,7 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, BlockDriverState *target, const char *filter_node_name, uint64_t cluster_size, + BackupPerf *perf, BdrvRequestFlags write_flags, BlockCopyState **bcs, Error **errp); diff --git a/block/backup.c b/block/backup.c index 4f13bb20a5..6cf2f974aa 100644 --- a/block/backup.c +++ b/block/backup.c @@ -22,7 +22,6 @@ #include "block/block-copy.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" -#include "qemu/ratelimit.h" #include "qemu/cutils.h" #include "sysemu/block-backend.h" #include "qemu/bitmap.h" @@ -36,6 +35,7 @@ typedef struct BackupBlockJob { BlockJob common; BlockDriverState *backup_top; BlockDriverState *source_bs; + BlockDriverState *target_bs; BdrvDirtyBitmap *sync_bitmap; @@ -44,40 +44,17 @@ typedef struct BackupBlockJob { BlockdevOnError on_source_error; BlockdevOnError on_target_error; uint64_t len; - uint64_t bytes_read; int64_t cluster_size; + BackupPerf perf; BlockCopyState *bcs; + + bool wait; + BlockCopyCallState *bg_bcs_call; } BackupBlockJob; static const BlockJobDriver backup_job_driver; -static void backup_progress_bytes_callback(int64_t bytes, void *opaque) -{ - BackupBlockJob *s = opaque; - - s->bytes_read += bytes; -} - -static int coroutine_fn backup_do_cow(BackupBlockJob *job, - int64_t offset, uint64_t bytes, - bool *error_is_read) -{ - int ret = 0; - int64_t start, end; /* bytes */ - - start = QEMU_ALIGN_DOWN(offset, job->cluster_size); - end = QEMU_ALIGN_UP(bytes + offset, job->cluster_size); - - trace_backup_do_cow_enter(job, start, offset, bytes); - - ret = block_copy(job->bcs, start, end - start, error_is_read); - - trace_backup_do_cow_return(job, offset, bytes, ret); - - return ret; -} - static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) { BdrvDirtyBitmap *bm; @@ -126,6 +103,7 @@ static void backup_abort(Job *job) static void backup_clean(Job *job) { BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); + block_job_remove_all_bdrv(&s->common); bdrv_backup_top_drop(s->backup_top); } @@ -157,53 +135,96 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job, } } -static bool coroutine_fn yield_and_check(BackupBlockJob *job) +static void coroutine_fn backup_block_copy_callback(void *opaque) { - uint64_t delay_ns; + BackupBlockJob *s = opaque; - if (job_is_cancelled(&job->common.job)) { - return true; + if (s->wait) { + s->wait = false; + aio_co_wake(s->common.job.co); + } else { + job_enter(&s->common.job); } - - /* - * We need to yield even for delay_ns = 0 so that bdrv_drain_all() can - * return. Without a yield, the VM would not reboot. - */ - delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read); - job->bytes_read = 0; - job_sleep_ns(&job->common.job, delay_ns); - - if (job_is_cancelled(&job->common.job)) { - return true; - } - - return false; } static int coroutine_fn backup_loop(BackupBlockJob *job) { - bool error_is_read; - int64_t offset; - BdrvDirtyBitmapIter *bdbi; + BlockCopyCallState *s = NULL; int ret = 0; + bool error_is_read; + BlockErrorAction act; - bdbi = bdrv_dirty_iter_new(block_copy_dirty_bitmap(job->bcs)); - while ((offset = bdrv_dirty_iter_next(bdbi)) != -1) { - do { - if (yield_and_check(job)) { - goto out; - } - ret = backup_do_cow(job, offset, job->cluster_size, &error_is_read); - if (ret < 0 && backup_error_action(job, error_is_read, -ret) == - BLOCK_ERROR_ACTION_REPORT) - { - goto out; - } - } while (ret < 0); + while (true) { /* retry loop */ + job->bg_bcs_call = s = block_copy_async(job->bcs, 0, + QEMU_ALIGN_UP(job->len, job->cluster_size), + job->perf.max_workers, job->perf.max_chunk, + backup_block_copy_callback, job); + + while (!block_copy_call_finished(s) && + !job_is_cancelled(&job->common.job)) + { + job_yield(&job->common.job); + } + + if (!block_copy_call_finished(s)) { + assert(job_is_cancelled(&job->common.job)); + /* + * Note that we can't use job_yield() here, as it doesn't work for + * cancelled job. + */ + block_copy_call_cancel(s); + job->wait = true; + qemu_coroutine_yield(); + assert(block_copy_call_finished(s)); + ret = 0; + goto out; + } + + if (job_is_cancelled(&job->common.job) || + block_copy_call_succeeded(s)) + { + ret = 0; + goto out; + } + + if (block_copy_call_cancelled(s)) { + /* + * Job is not cancelled but only block-copy call. This is possible + * after job pause. Now the pause is finished, start new block-copy + * iteration. + */ + block_copy_call_free(s); + continue; + } + + /* The only remaining case is failed block-copy call. */ + assert(block_copy_call_failed(s)); + + ret = block_copy_call_status(s, &error_is_read); + act = backup_error_action(job, error_is_read, -ret); + switch (act) { + case BLOCK_ERROR_ACTION_REPORT: + goto out; + case BLOCK_ERROR_ACTION_STOP: + /* + * Go to pause prior to starting new block-copy call on the next + * iteration. + */ + job_pause_point(&job->common.job); + break; + case BLOCK_ERROR_ACTION_IGNORE: + /* Proceed to new block-copy call to retry. */ + break; + default: + abort(); + } + + block_copy_call_free(s); } - out: - bdrv_dirty_iter_free(bdbi); +out: + block_copy_call_free(s); + job->bg_bcs_call = NULL; return ret; } @@ -235,7 +256,7 @@ static void backup_init_bcs_bitmap(BackupBlockJob *job) static int coroutine_fn backup_run(Job *job, Error **errp) { BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); - int ret = 0; + int ret; backup_init_bcs_bitmap(s); @@ -244,14 +265,19 @@ static int coroutine_fn backup_run(Job *job, Error **errp) int64_t count; for (offset = 0; offset < s->len; ) { - if (yield_and_check(s)) { - ret = -ECANCELED; - goto out; + if (job_is_cancelled(job)) { + return -ECANCELED; + } + + job_pause_point(job); + + if (job_is_cancelled(job)) { + return -ECANCELED; } ret = block_copy_reset_unallocated(s->bcs, offset, &count); if (ret < 0) { - goto out; + return ret; } offset += count; @@ -272,11 +298,44 @@ static int coroutine_fn backup_run(Job *job, Error **errp) job_yield(job); } } else { - ret = backup_loop(s); + return backup_loop(s); } - out: - return ret; + return 0; +} + +static void coroutine_fn backup_pause(Job *job) +{ + BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); + + if (s->bg_bcs_call && !block_copy_call_finished(s->bg_bcs_call)) { + block_copy_call_cancel(s->bg_bcs_call); + s->wait = true; + qemu_coroutine_yield(); + } +} + +static void coroutine_fn backup_set_speed(BlockJob *job, int64_t speed) +{ + BackupBlockJob *s = container_of(job, BackupBlockJob, common); + + /* + * block_job_set_speed() is called first from block_job_create(), when we + * don't yet have s->bcs. + */ + if (s->bcs) { + block_copy_set_speed(s->bcs, speed); + if (s->bg_bcs_call) { + block_copy_kick(s->bg_bcs_call); + } + } +} + +static void backup_cancel(Job *job) +{ + BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); + + bdrv_cancel_in_flight(s->target_bs); } static const BlockJobDriver backup_job_driver = { @@ -289,7 +348,10 @@ static const BlockJobDriver backup_job_driver = { .commit = backup_commit, .abort = backup_abort, .clean = backup_clean, - } + .pause = backup_pause, + .cancel = backup_cancel, + }, + .set_speed = backup_set_speed, }; static int64_t backup_calculate_cluster_size(BlockDriverState *target, @@ -297,6 +359,7 @@ static int64_t backup_calculate_cluster_size(BlockDriverState *target, { int ret; BlockDriverInfo bdi; + bool target_does_cow = bdrv_backing_chain_next(target); /* * If there is no backing file on the target, we cannot rely on COW if our @@ -304,7 +367,7 @@ static int64_t backup_calculate_cluster_size(BlockDriverState *target, * targets with a backing file, try to avoid COW if possible. */ ret = bdrv_get_info(target, &bdi); - if (ret == -ENOTSUP && !target->backing) { + if (ret == -ENOTSUP && !target_does_cow) { /* Cluster size is not defined */ warn_report("The target block device doesn't provide " "information about the block size and it doesn't have a " @@ -313,14 +376,14 @@ static int64_t backup_calculate_cluster_size(BlockDriverState *target, "this default, the backup may be unusable", BACKUP_CLUSTER_SIZE_DEFAULT); return BACKUP_CLUSTER_SIZE_DEFAULT; - } else if (ret < 0 && !target->backing) { + } else if (ret < 0 && !target_does_cow) { error_setg_errno(errp, -ret, "Couldn't determine the cluster size of the target image, " "which has no backing file"); error_append_hint(errp, "Aborting, since this may create an unusable destination image\n"); return ret; - } else if (ret < 0 && target->backing) { + } else if (ret < 0 && target_does_cow) { /* Not fatal; just trudge on ahead. */ return BACKUP_CLUSTER_SIZE_DEFAULT; } @@ -334,6 +397,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, BitmapSyncMode bitmap_mode, bool compress, const char *filter_node_name, + BackupPerf *perf, BlockdevOnError on_source_error, BlockdevOnError on_target_error, int creation_flags, @@ -371,7 +435,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, return NULL; } - if (compress && !block_driver_can_compress(target->drv)) { + if (compress && !bdrv_supports_compressed_writes(target)) { error_setg(errp, "Compression is not supported for this drive %s", bdrv_get_device_name(target)); return NULL; @@ -385,6 +449,29 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, return NULL; } + cluster_size = backup_calculate_cluster_size(target, errp); + if (cluster_size < 0) { + goto error; + } + + if (perf->max_workers < 1) { + error_setg(errp, "max-workers must be greater than zero"); + return NULL; + } + + if (perf->max_chunk < 0) { + error_setg(errp, "max-chunk must be zero (which means no limit) or " + "positive"); + return NULL; + } + + if (perf->max_chunk && perf->max_chunk < cluster_size) { + error_setg(errp, "Required max-chunk (%" PRIi64 ") is less than backup " + "cluster size (%" PRIi64 ")", perf->max_chunk, cluster_size); + return NULL; + } + + if (sync_bitmap) { /* If we need to write to this bitmap, check that we can: */ if (bitmap_mode != BITMAP_SYNC_MODE_NEVER && @@ -417,11 +504,6 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, goto error; } - cluster_size = backup_calculate_cluster_size(target, errp); - if (cluster_size < 0) { - goto error; - } - /* * If source is in backing chain of target assume that target is going to be * used for "image fleecing", i.e. it should represent a kind of snapshot of @@ -440,7 +522,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, (compress ? BDRV_REQ_WRITE_COMPRESSED : 0), backup_top = bdrv_backup_top_append(bs, target, filter_node_name, - cluster_size, write_flags, &bcs, errp); + cluster_size, perf, + write_flags, &bcs, errp); if (!backup_top) { goto error; } @@ -455,6 +538,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, job->backup_top = backup_top; job->source_bs = bs; + job->target_bs = target; job->on_source_error = on_source_error; job->on_target_error = on_target_error; job->sync_mode = sync_mode; @@ -463,9 +547,10 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, job->bcs = bcs; job->cluster_size = cluster_size; job->len = len; + job->perf = *perf; - block_copy_set_progress_callback(bcs, backup_progress_bytes_callback, job); block_copy_set_progress_meter(bcs, &job->common.job.progress); + block_copy_set_speed(bcs, speed); /* Required permissions are already taken by backup-top target */ block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, diff --git a/block/blkdebug.c b/block/blkdebug.c index 9c08d8a005..2c0b9b0ee8 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -173,7 +173,7 @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp) { struct add_rule_data *d = opaque; BDRVBlkdebugState *s = d->s; - const char* event_name; + const char *event_name; int event; struct BlkdebugRule *rule; int64_t sector; @@ -215,6 +215,7 @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp) BLKDEBUG_IO_TYPE__MAX, &local_error); if (local_error) { error_propagate(errp, local_error); + g_free(rule); return -1; } if (iotype != BLKDEBUG_IO_TYPE__MAX) { @@ -278,9 +279,8 @@ static int read_config(BDRVBlkdebugState *s, const char *filename, return -errno; } - ret = qemu_config_parse(f, config_groups, filename); + ret = qemu_config_parse(f, config_groups, filename, errp); if (ret < 0) { - error_setg(errp, "Could not parse blkdebug config file"); goto fail; } } @@ -464,7 +464,6 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, { BDRVBlkdebugState *s = bs->opaque; QemuOpts *opts; - Error *local_err = NULL; int ret; uint64_t align; @@ -494,10 +493,9 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image", bs, &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, - false, &local_err); - if (local_err) { + false, errp); + if (!bs->file) { ret = -EINVAL; - error_propagate(errp, local_err); goto out; } @@ -752,8 +750,11 @@ static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs, return err; } - return bdrv_co_block_status_from_file(bs, want_zero, offset, bytes, - pnum, map, file); + assert(bs->file && bs->file->bs); + *pnum = bytes; + *map = offset; + *file = bs->file->bs; + return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID; } static void blkdebug_close(BlockDriverState *bs) diff --git a/block/blklogwrites.c b/block/blklogwrites.c index 57315f56b4..b7579370a3 100644 --- a/block/blklogwrites.c +++ b/block/blklogwrites.c @@ -157,19 +157,17 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, /* Open the file */ bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, false, - &local_err); - if (local_err) { + errp); + if (!bs->file) { ret = -EINVAL; - error_propagate(errp, local_err); goto fail; } /* Open the log file */ s->log_file = bdrv_open_child(NULL, options, "log", bs, &child_of_bds, - BDRV_CHILD_METADATA, false, &local_err); - if (local_err) { + BDRV_CHILD_METADATA, false, errp); + if (!s->log_file) { ret = -EINVAL; - error_propagate(errp, local_err); goto fail; } @@ -515,7 +513,6 @@ static BlockDriver bdrv_blk_log_writes = { .bdrv_co_pwrite_zeroes = blk_log_writes_co_pwrite_zeroes, .bdrv_co_flush_to_disk = blk_log_writes_co_flush_to_disk, .bdrv_co_pdiscard = blk_log_writes_co_pdiscard, - .bdrv_co_block_status = bdrv_co_block_status_from_file, .is_filter = true, .strong_runtime_opts = blk_log_writes_strong_runtime_opts, diff --git a/block/blkreplay.c b/block/blkreplay.c index 30a0f5d57a..4a247752fd 100644 --- a/block/blkreplay.c +++ b/block/blkreplay.c @@ -23,16 +23,14 @@ typedef struct Request { static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { - Error *local_err = NULL; int ret; /* Open the image file */ bs->file = bdrv_open_child(NULL, options, "image", bs, &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, - false, &local_err); - if (local_err) { + false, errp); + if (!bs->file) { ret = -EINVAL; - error_propagate(errp, local_err); goto fail; } diff --git a/block/blkverify.c b/block/blkverify.c index 4aed53ab59..188d7632fa 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -31,7 +31,7 @@ typedef struct BlkverifyRequest { uint64_t bytes; int flags; - int (*request_fn)(BdrvChild *, int64_t, unsigned int, QEMUIOVector *, + int (*request_fn)(BdrvChild *, int64_t, int64_t, QEMUIOVector *, BdrvRequestFlags); int ret; /* test image result */ @@ -112,7 +112,6 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, { BDRVBlkverifyState *s = bs->opaque; QemuOpts *opts; - Error *local_err = NULL; int ret; opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); @@ -125,20 +124,18 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, bs->file = bdrv_open_child(qemu_opt_get(opts, "x-raw"), options, "raw", bs, &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, - false, &local_err); - if (local_err) { + false, errp); + if (!bs->file) { ret = -EINVAL; - error_propagate(errp, local_err); goto fail; } /* Open the test file */ s->test_file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "test", bs, &child_of_bds, BDRV_CHILD_DATA, - false, &local_err); - if (local_err) { + false, errp); + if (!s->test_file) { ret = -EINVAL; - error_propagate(errp, local_err); goto fail; } diff --git a/block/block-backend.c b/block/block-backend.c index 3a13cb5f0b..413af51f3b 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -163,7 +163,7 @@ static const char *blk_root_get_name(BdrvChild *child) return blk_name(child->opaque); } -static void blk_vm_state_changed(void *opaque, int running, RunState state) +static void blk_vm_state_changed(void *opaque, bool running, RunState state) { Error *local_err = NULL; BlockBackend *blk = opaque; @@ -1353,12 +1353,12 @@ int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags) void blk_inc_in_flight(BlockBackend *blk) { - atomic_inc(&blk->in_flight); + qatomic_inc(&blk->in_flight); } void blk_dec_in_flight(BlockBackend *blk) { - atomic_dec(&blk->in_flight); + qatomic_dec(&blk->in_flight); aio_wait_kick(); } @@ -1720,7 +1720,7 @@ void blk_drain(BlockBackend *blk) /* We may have -ENOMEDIUM completions in flight */ AIO_WAIT_WHILE(blk_get_aio_context(blk), - atomic_mb_read(&blk->in_flight) > 0); + qatomic_mb_read(&blk->in_flight) > 0); if (bs) { bdrv_drained_end(bs); @@ -1739,7 +1739,7 @@ void blk_drain_all(void) aio_context_acquire(ctx); /* We may have -ENOMEDIUM completions in flight */ - AIO_WAIT_WHILE(ctx, atomic_mb_read(&blk->in_flight) > 0); + AIO_WAIT_WHILE(ctx, qatomic_mb_read(&blk->in_flight) > 0); aio_context_release(ctx); } @@ -1826,17 +1826,30 @@ void blk_error_action(BlockBackend *blk, BlockErrorAction action, } } -bool blk_is_read_only(BlockBackend *blk) +/* + * Returns true if the BlockBackend can support taking write permissions + * (because its root node is not read-only). + */ +bool blk_supports_write_perm(BlockBackend *blk) { BlockDriverState *bs = blk_bs(blk); if (bs) { - return bdrv_is_read_only(bs); + return !bdrv_is_read_only(bs); } else { - return blk->root_state.read_only; + return !blk->root_state.read_only; } } +/* + * Returns true if the BlockBackend can be written to in its current + * configuration (i.e. if write permission have been requested) + */ +bool blk_is_writable(BlockBackend *blk) +{ + return blk->perm & BLK_PERM_WRITE; +} + bool blk_is_sg(BlockBackend *blk) { BlockDriverState *bs = blk_bs(blk); @@ -2279,10 +2292,13 @@ int blk_commit_all(void) while ((blk = blk_all_next(blk)) != NULL) { AioContext *aio_context = blk_get_aio_context(blk); + BlockDriverState *unfiltered_bs = bdrv_skip_filters(blk_bs(blk)); aio_context_acquire(aio_context); - if (blk_is_inserted(blk) && blk->root->bs->backing) { - int ret = bdrv_commit(blk->root->bs); + if (blk_is_inserted(blk) && bdrv_cow_child(unfiltered_bs)) { + int ret; + + ret = bdrv_commit(unfiltered_bs); if (ret < 0) { aio_context_release(aio_context); return ret; @@ -2343,6 +2359,7 @@ void blk_io_limits_update_group(BlockBackend *blk, const char *group) static void blk_root_drained_begin(BdrvChild *child) { BlockBackend *blk = child->opaque; + ThrottleGroupMember *tgm = &blk->public.throttle_group_member; if (++blk->quiesce_counter == 1) { if (blk->dev_ops && blk->dev_ops->drained_begin) { @@ -2353,8 +2370,8 @@ static void blk_root_drained_begin(BdrvChild *child) /* Note that blk->root may not be accessible here yet if we are just * attaching to a BlockDriverState that is drained. Use child instead. */ - if (atomic_fetch_inc(&blk->public.throttle_group_member.io_limits_disabled) == 0) { - throttle_group_restart_tgm(&blk->public.throttle_group_member); + if (qatomic_fetch_inc(&tgm->io_limits_disabled) == 0) { + throttle_group_restart_tgm(tgm); } } @@ -2371,7 +2388,7 @@ static void blk_root_drained_end(BdrvChild *child, int *drained_end_counter) assert(blk->quiesce_counter); assert(blk->public.throttle_group_member.io_limits_disabled); - atomic_dec(&blk->public.throttle_group_member.io_limits_disabled); + qatomic_dec(&blk->public.throttle_group_member.io_limits_disabled); if (--blk->quiesce_counter == 0) { if (blk->dev_ops && blk->dev_ops->drained_end) { diff --git a/block/block-copy.c b/block/block-copy.c index f7428a7c08..39ae481c8b 100644 --- a/block/block-copy.c +++ b/block/block-copy.c @@ -26,11 +26,34 @@ #define BLOCK_COPY_MAX_BUFFER (1 * MiB) #define BLOCK_COPY_MAX_MEM (128 * MiB) #define BLOCK_COPY_MAX_WORKERS 64 +#define BLOCK_COPY_SLICE_TIME 100000000ULL /* ns */ static coroutine_fn int block_copy_task_entry(AioTask *task); typedef struct BlockCopyCallState { - bool failed; + /* IN parameters. Initialized in block_copy_async() and never changed. */ + BlockCopyState *s; + int64_t offset; + int64_t bytes; + int max_workers; + int64_t max_chunk; + bool ignore_ratelimit; + BlockCopyAsyncCallbackFunc cb; + void *cb_opaque; + + /* Coroutine where async block-copy is running */ + Coroutine *co; + + /* To reference all call states from BlockCopyState */ + QLIST_ENTRY(BlockCopyCallState) list; + + /* State */ + int ret; + bool finished; + QemuCoSleepState *sleep_state; + bool cancelled; + + /* OUT parameters */ bool error_is_read; } BlockCopyCallState; @@ -65,7 +88,8 @@ typedef struct BlockCopyState { bool use_copy_range; int64_t copy_size; uint64_t len; - QLIST_HEAD(, BlockCopyTask) tasks; + QLIST_HEAD(, BlockCopyTask) tasks; /* All tasks from all block-copy calls */ + QLIST_HEAD(, BlockCopyCallState) calls; BdrvRequestFlags write_flags; @@ -86,11 +110,11 @@ typedef struct BlockCopyState { bool skip_unallocated; ProgressMeter *progress; - /* progress_bytes_callback: called when some copying progress is done. */ - ProgressBytesCallbackFunc progress_bytes_callback; - void *progress_opaque; SharedResource *mem; + + uint64_t speed; + RateLimit rate_limit; } BlockCopyState; static BlockCopyTask *find_conflicting_task(BlockCopyState *s, @@ -134,14 +158,18 @@ static BlockCopyTask *block_copy_task_create(BlockCopyState *s, int64_t offset, int64_t bytes) { BlockCopyTask *task; + int64_t max_chunk = MIN_NON_ZERO(s->copy_size, call_state->max_chunk); if (!bdrv_dirty_bitmap_next_dirty_area(s->copy_bitmap, offset, offset + bytes, - s->copy_size, &offset, &bytes)) + max_chunk, &offset, &bytes)) { return NULL; } + assert(QEMU_IS_ALIGNED(offset, s->cluster_size)); + bytes = QEMU_ALIGN_UP(bytes, s->cluster_size); + /* region is dirty, so no existent tasks possible in it */ assert(!find_conflicting_task(s, offset, bytes)); @@ -215,7 +243,7 @@ static uint32_t block_copy_max_transfer(BdrvChild *source, BdrvChild *target) } BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, - int64_t cluster_size, + int64_t cluster_size, bool use_copy_range, BdrvRequestFlags write_flags, Error **errp) { BlockCopyState *s; @@ -257,24 +285,16 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, * We enable copy-range, but keep small copy_size, until first * successful copy_range (look at block_copy_do_copy). */ - s->use_copy_range = true; + s->use_copy_range = use_copy_range; s->copy_size = MAX(s->cluster_size, BLOCK_COPY_MAX_BUFFER); } QLIST_INIT(&s->tasks); + QLIST_INIT(&s->calls); return s; } -void block_copy_set_progress_callback( - BlockCopyState *s, - ProgressBytesCallbackFunc progress_bytes_callback, - void *progress_opaque) -{ - s->progress_bytes_callback = progress_bytes_callback; - s->progress_opaque = progress_opaque; -} - void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm) { s->progress = pm; @@ -417,12 +437,11 @@ static coroutine_fn int block_copy_task_entry(AioTask *task) ret = block_copy_do_copy(t->s, t->offset, t->bytes, t->zeroes, &error_is_read); - if (ret < 0 && !t->call_state->failed) { - t->call_state->failed = true; + if (ret < 0 && !t->call_state->ret) { + t->call_state->ret = ret; t->call_state->error_is_read = error_is_read; } else { progress_work_done(t->s->progress, t->bytes); - t->s->progress_bytes_callback(t->bytes, t->s->progress_opaque); } co_put_to_shres(t->s->mem, t->bytes); block_copy_task_end(t, ret); @@ -437,8 +456,8 @@ static int block_copy_block_status(BlockCopyState *s, int64_t offset, BlockDriverState *base; int ret; - if (s->skip_unallocated && s->source->bs->backing) { - base = s->source->bs->backing->bs; + if (s->skip_unallocated) { + base = bdrv_backing_chain_next(s->source->bs); } else { base = NULL; } @@ -541,15 +560,17 @@ int64_t block_copy_reset_unallocated(BlockCopyState *s, * Returns 1 if dirty clusters found and successfully copied, 0 if no dirty * clusters found and -errno on failure. */ -static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s, - int64_t offset, int64_t bytes, - bool *error_is_read) +static int coroutine_fn +block_copy_dirty_clusters(BlockCopyCallState *call_state) { + BlockCopyState *s = call_state->s; + int64_t offset = call_state->offset; + int64_t bytes = call_state->bytes; + int ret = 0; bool found_dirty = false; int64_t end = offset + bytes; AioTaskPool *aio = NULL; - BlockCopyCallState call_state = {false, false}; /* * block_copy() user is responsible for keeping source and target in same @@ -561,11 +582,11 @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s, assert(QEMU_IS_ALIGNED(offset, s->cluster_size)); assert(QEMU_IS_ALIGNED(bytes, s->cluster_size)); - while (bytes && aio_task_pool_status(aio) == 0) { + while (bytes && aio_task_pool_status(aio) == 0 && !call_state->cancelled) { BlockCopyTask *task; int64_t status_bytes; - task = block_copy_task_create(s, &call_state, offset, bytes); + task = block_copy_task_create(s, call_state, offset, bytes); if (!task) { /* No more dirty bits in the bitmap */ trace_block_copy_skip_range(s, offset, bytes); @@ -596,6 +617,21 @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s, } task->zeroes = ret & BDRV_BLOCK_ZERO; + if (s->speed) { + if (!call_state->ignore_ratelimit) { + uint64_t ns = ratelimit_calculate_delay(&s->rate_limit, 0); + if (ns > 0) { + block_copy_task_end(task, -EAGAIN); + g_free(task); + qemu_co_sleep_ns_wakeable(QEMU_CLOCK_REALTIME, ns, + &call_state->sleep_state); + continue; + } + } + + ratelimit_calculate_delay(&s->rate_limit, task->bytes); + } + trace_block_copy_process(s, task->offset); co_get_from_shres(s->mem, task->bytes); @@ -604,7 +640,7 @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s, bytes = end - offset; if (!aio && bytes) { - aio = aio_task_pool_new(BLOCK_COPY_MAX_WORKERS); + aio = aio_task_pool_new(call_state->max_workers); } ret = block_copy_task_run(aio, task); @@ -630,15 +666,19 @@ out: aio_task_pool_free(aio); } - if (error_is_read && ret < 0) { - *error_is_read = call_state.error_is_read; - } return ret < 0 ? ret : found_dirty; } +void block_copy_kick(BlockCopyCallState *call_state) +{ + if (call_state->sleep_state) { + qemu_co_sleep_wake(call_state->sleep_state); + } +} + /* - * block_copy + * block_copy_common * * Copy requested region, accordingly to dirty bitmap. * Collaborate with parallel block_copy requests: if they succeed it will help @@ -646,16 +686,18 @@ out: * it means that some I/O operation failed in context of _this_ block_copy call, * not some parallel operation. */ -int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes, - bool *error_is_read) +static int coroutine_fn block_copy_common(BlockCopyCallState *call_state) { int ret; - do { - ret = block_copy_dirty_clusters(s, offset, bytes, error_is_read); + QLIST_INSERT_HEAD(&call_state->s->calls, call_state, list); - if (ret == 0) { - ret = block_copy_wait_one(s, offset, bytes); + do { + ret = block_copy_dirty_clusters(call_state); + + if (ret == 0 && !call_state->cancelled) { + ret = block_copy_wait_one(call_state->s, call_state->offset, + call_state->bytes); } /* @@ -667,11 +709,110 @@ int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes, * 2. We have waited for some intersecting block-copy request * It may have failed and produced new dirty bits. */ - } while (ret > 0); + } while (ret > 0 && !call_state->cancelled); + + call_state->finished = true; + + if (call_state->cb) { + call_state->cb(call_state->cb_opaque); + } + + QLIST_REMOVE(call_state, list); return ret; } +int coroutine_fn block_copy(BlockCopyState *s, int64_t start, int64_t bytes, + bool ignore_ratelimit) +{ + BlockCopyCallState call_state = { + .s = s, + .offset = start, + .bytes = bytes, + .ignore_ratelimit = ignore_ratelimit, + .max_workers = BLOCK_COPY_MAX_WORKERS, + }; + + return block_copy_common(&call_state); +} + +static void coroutine_fn block_copy_async_co_entry(void *opaque) +{ + block_copy_common(opaque); +} + +BlockCopyCallState *block_copy_async(BlockCopyState *s, + int64_t offset, int64_t bytes, + int max_workers, int64_t max_chunk, + BlockCopyAsyncCallbackFunc cb, + void *cb_opaque) +{ + BlockCopyCallState *call_state = g_new(BlockCopyCallState, 1); + + *call_state = (BlockCopyCallState) { + .s = s, + .offset = offset, + .bytes = bytes, + .max_workers = max_workers, + .max_chunk = max_chunk, + .cb = cb, + .cb_opaque = cb_opaque, + + .co = qemu_coroutine_create(block_copy_async_co_entry, call_state), + }; + + qemu_coroutine_enter(call_state->co); + + return call_state; +} + +void block_copy_call_free(BlockCopyCallState *call_state) +{ + if (!call_state) { + return; + } + + assert(call_state->finished); + g_free(call_state); +} + +bool block_copy_call_finished(BlockCopyCallState *call_state) +{ + return call_state->finished; +} + +bool block_copy_call_succeeded(BlockCopyCallState *call_state) +{ + return call_state->finished && !call_state->cancelled && + call_state->ret == 0; +} + +bool block_copy_call_failed(BlockCopyCallState *call_state) +{ + return call_state->finished && !call_state->cancelled && + call_state->ret < 0; +} + +bool block_copy_call_cancelled(BlockCopyCallState *call_state) +{ + return call_state->cancelled; +} + +int block_copy_call_status(BlockCopyCallState *call_state, bool *error_is_read) +{ + assert(call_state->finished); + if (error_is_read) { + *error_is_read = call_state->error_is_read; + } + return call_state->ret; +} + +void block_copy_call_cancel(BlockCopyCallState *call_state) +{ + call_state->cancelled = true; + block_copy_kick(call_state); +} + BdrvDirtyBitmap *block_copy_dirty_bitmap(BlockCopyState *s) { return s->copy_bitmap; @@ -681,3 +822,18 @@ void block_copy_set_skip_unallocated(BlockCopyState *s, bool skip) { s->skip_unallocated = skip; } + +void block_copy_set_speed(BlockCopyState *s, uint64_t speed) +{ + s->speed = speed; + if (speed > 0) { + ratelimit_set_speed(&s->rate_limit, speed, BLOCK_COPY_SLICE_TIME); + } + + /* + * Note: it's good to kick all call states from here, but it should be done + * only from a coroutine, to not crash if s->calls list changed while + * entering one call. So for now, the only user of this function kicks its + * only one call_state by hand. + */ +} diff --git a/block/block-gen.h b/block/block-gen.h new file mode 100644 index 0000000000..f80cf4897d --- /dev/null +++ b/block/block-gen.h @@ -0,0 +1,49 @@ +/* + * Block coroutine wrapping core, used by auto-generated block/block-gen.c + * + * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2020 Virtuozzo International GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef BLOCK_BLOCK_GEN_H +#define BLOCK_BLOCK_GEN_H + +#include "block/block_int.h" + +/* Base structure for argument packing structures */ +typedef struct BdrvPollCo { + BlockDriverState *bs; + bool in_progress; + int ret; + Coroutine *co; /* Keep pointer here for debugging */ +} BdrvPollCo; + +static inline int bdrv_poll_co(BdrvPollCo *s) +{ + assert(!qemu_in_coroutine()); + + bdrv_coroutine_enter(s->bs, s->co); + BDRV_POLL_WHILE(s->bs, s->in_progress); + + return s->ret; +} + +#endif /* BLOCK_BLOCK_GEN_H */ diff --git a/block/commit.c b/block/commit.c index 7732d02dfe..dd9ba87349 100644 --- a/block/commit.c +++ b/block/commit.c @@ -37,6 +37,7 @@ typedef struct CommitBlockJob { BlockBackend *top; BlockBackend *base; BlockDriverState *base_bs; + BlockDriverState *base_overlay; BlockdevOnError on_error; bool base_read_only; bool chain_frozen; @@ -89,7 +90,7 @@ static void commit_abort(Job *job) * XXX Can (or should) we somehow keep 'consistent read' blocked even * after the failed/cancelled commit job is gone? If we already wrote * something to base, the intermediate images aren't valid any more. */ - bdrv_replace_node(s->commit_top_bs, backing_bs(s->commit_top_bs), + bdrv_replace_node(s->commit_top_bs, s->commit_top_bs->backing->bs, &error_abort); bdrv_unref(s->commit_top_bs); @@ -153,9 +154,9 @@ static int coroutine_fn commit_run(Job *job, Error **errp) break; } /* Copy if allocated above the base */ - ret = bdrv_is_allocated_above(blk_bs(s->top), blk_bs(s->base), false, + ret = bdrv_is_allocated_above(blk_bs(s->top), s->base_overlay, true, offset, COMMIT_BUFFER_SIZE, &n); - copy = (ret == 1); + copy = (ret > 0); trace_commit_one_iteration(s, offset, n, ret); if (copy) { assert(n < SIZE_MAX); @@ -237,7 +238,6 @@ static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c, static BlockDriver bdrv_commit_top = { .format_name = "commit_top", .bdrv_co_preadv = bdrv_commit_top_preadv, - .bdrv_co_block_status = bdrv_co_block_status_from_backing, .bdrv_refresh_filename = bdrv_commit_top_refresh_filename, .bdrv_child_perm = bdrv_commit_top_child_perm, @@ -253,15 +253,34 @@ void commit_start(const char *job_id, BlockDriverState *bs, CommitBlockJob *s; BlockDriverState *iter; BlockDriverState *commit_top_bs = NULL; - Error *local_err = NULL; + BlockDriverState *filtered_base; + int64_t base_size, top_size; + uint64_t base_perms, iter_shared_perms; int ret; assert(top != bs); - if (top == base) { + if (bdrv_skip_filters(top) == bdrv_skip_filters(base)) { error_setg(errp, "Invalid files for merge: top and base are the same"); return; } + base_size = bdrv_getlength(base); + if (base_size < 0) { + error_setg_errno(errp, -base_size, "Could not inquire base image size"); + return; + } + + top_size = bdrv_getlength(top); + if (top_size < 0) { + error_setg_errno(errp, -top_size, "Could not inquire top image size"); + return; + } + + base_perms = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE; + if (base_size < top_size) { + base_perms |= BLK_PERM_RESIZE; + } + s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL, speed, creation_flags, NULL, NULL, errp); if (!s) { @@ -292,26 +311,51 @@ void commit_start(const char *job_id, BlockDriverState *bs, commit_top_bs->total_sectors = top->total_sectors; - bdrv_append(commit_top_bs, top, &local_err); - if (local_err) { + ret = bdrv_append(commit_top_bs, top, errp); + if (ret < 0) { commit_top_bs = NULL; - error_propagate(errp, local_err); goto fail; } s->commit_top_bs = commit_top_bs; - /* Block all nodes between top and base, because they will - * disappear from the chain after this operation. */ - assert(bdrv_chain_contains(top, base)); - for (iter = top; iter != base; iter = backing_bs(iter)) { - /* XXX BLK_PERM_WRITE needs to be allowed so we don't block ourselves - * at s->base (if writes are blocked for a node, they are also blocked - * for its backing file). The other options would be a second filter - * driver above s->base. */ + /* + * Block all nodes between top and base, because they will + * disappear from the chain after this operation. + * Note that this assumes that the user is fine with removing all + * nodes (including R/W filters) between top and base. Assuring + * this is the responsibility of the interface (i.e. whoever calls + * commit_start()). + */ + s->base_overlay = bdrv_find_overlay(top, base); + assert(s->base_overlay); + + /* + * The topmost node with + * bdrv_skip_filters(filtered_base) == bdrv_skip_filters(base) + */ + filtered_base = bdrv_cow_bs(s->base_overlay); + assert(bdrv_skip_filters(filtered_base) == bdrv_skip_filters(base)); + + /* + * XXX BLK_PERM_WRITE needs to be allowed so we don't block ourselves + * at s->base (if writes are blocked for a node, they are also blocked + * for its backing file). The other options would be a second filter + * driver above s->base. + */ + iter_shared_perms = BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE; + + for (iter = top; iter != base; iter = bdrv_filter_or_cow_bs(iter)) { + if (iter == filtered_base) { + /* + * From here on, all nodes are filters on the base. This + * allows us to share BLK_PERM_CONSISTENT_READ. + */ + iter_shared_perms |= BLK_PERM_CONSISTENT_READ; + } + ret = block_job_add_bdrv(&s->common, "intermediate node", iter, 0, - BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE, - errp); + iter_shared_perms, errp); if (ret < 0) { goto fail; } @@ -328,9 +372,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, } s->base = blk_new(s->common.job.aio_context, - BLK_PERM_CONSISTENT_READ - | BLK_PERM_WRITE - | BLK_PERM_RESIZE, + base_perms, BLK_PERM_CONSISTENT_READ | BLK_PERM_GRAPH_MOD | BLK_PERM_WRITE_UNCHANGED); @@ -398,19 +440,22 @@ int bdrv_commit(BlockDriverState *bs) if (!drv) return -ENOMEDIUM; - if (!bs->backing) { + backing_file_bs = bdrv_cow_bs(bs); + + if (!backing_file_bs) { return -ENOTSUP; } if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, NULL) || - bdrv_op_is_blocked(bs->backing->bs, BLOCK_OP_TYPE_COMMIT_TARGET, NULL)) { + bdrv_op_is_blocked(backing_file_bs, BLOCK_OP_TYPE_COMMIT_TARGET, NULL)) + { return -EBUSY; } - ro = bs->backing->bs->read_only; + ro = backing_file_bs->read_only; if (ro) { - if (bdrv_reopen_set_read_only(bs->backing->bs, false, NULL)) { + if (bdrv_reopen_set_read_only(backing_file_bs, false, NULL)) { return -EACCES; } } @@ -428,8 +473,6 @@ int bdrv_commit(BlockDriverState *bs) } /* Insert commit_top block node above backing, so we can write to it */ - backing_file_bs = backing_bs(bs); - commit_top_bs = bdrv_new_open_driver(&bdrv_commit_top, NULL, BDRV_O_RDWR, &local_err); if (commit_top_bs == NULL) { @@ -515,7 +558,7 @@ ro_cleanup: qemu_vfree(buf); blk_unref(backing); - if (backing_file_bs) { + if (bdrv_cow_bs(bs) != backing_file_bs) { bdrv_set_backing_hd(bs, backing_file_bs, &error_abort); } bdrv_unref(commit_top_bs); @@ -523,7 +566,7 @@ ro_cleanup: if (ro) { /* ignoring error return here */ - bdrv_reopen_set_read_only(bs->backing->bs, true, NULL); + bdrv_reopen_set_read_only(backing_file_bs, true, NULL); } return ret; diff --git a/block/copy-on-read.c b/block/copy-on-read.c index a6e3c74a68..9cad9e1b8c 100644 --- a/block/copy-on-read.c +++ b/block/copy-on-read.c @@ -23,11 +23,26 @@ #include "qemu/osdep.h" #include "block/block_int.h" #include "qemu/module.h" +#include "qapi/error.h" +#include "qapi/qmp/qdict.h" +#include "block/copy-on-read.h" + + +typedef struct BDRVStateCOR { + bool active; + BlockDriverState *bottom_bs; + bool chain_frozen; +} BDRVStateCOR; static int cor_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { + BlockDriverState *bottom_bs = NULL; + BDRVStateCOR *state = bs->opaque; + /* Find a bottom node name, if any */ + const char *bottom_node = qdict_get_try_str(options, "bottom"); + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, false, errp); @@ -35,6 +50,8 @@ static int cor_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } + bs->supported_read_flags = BDRV_REQ_PREFETCH; + bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); @@ -42,6 +59,44 @@ static int cor_open(BlockDriverState *bs, QDict *options, int flags, ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & bs->file->bs->supported_zero_flags); + if (bottom_node) { + bottom_bs = bdrv_find_node(bottom_node); + if (!bottom_bs) { + error_setg(errp, "Bottom node '%s' not found", bottom_node); + qdict_del(options, "bottom"); + return -EINVAL; + } + qdict_del(options, "bottom"); + + if (!bottom_bs->drv) { + error_setg(errp, "Bottom node '%s' not opened", bottom_node); + return -EINVAL; + } + + if (bottom_bs->drv->is_filter) { + error_setg(errp, "Bottom node '%s' is a filter", bottom_node); + return -EINVAL; + } + + if (bdrv_freeze_backing_chain(bs, bottom_bs, errp) < 0) { + return -EINVAL; + } + state->chain_frozen = true; + + /* + * We do freeze the chain, so it shouldn't be removed. Still, storing a + * pointer worth bdrv_ref(). + */ + bdrv_ref(bottom_bs); + } + state->active = true; + state->bottom_bs = bottom_bs; + + /* + * We don't need to call bdrv_child_refresh_perms() now as the permissions + * will be updated later when the filter node gets its parent. + */ + return 0; } @@ -57,6 +112,17 @@ static void cor_child_perm(BlockDriverState *bs, BdrvChild *c, uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared) { + BDRVStateCOR *s = bs->opaque; + + if (!s->active) { + /* + * While the filter is being removed + */ + *nperm = 0; + *nshared = BLK_PERM_ALL; + return; + } + *nperm = perm & PERM_PASSTHROUGH; *nshared = (shared & PERM_PASSTHROUGH) | PERM_UNCHANGED; @@ -74,21 +140,67 @@ static int64_t cor_getlength(BlockDriverState *bs) } -static int coroutine_fn cor_co_preadv(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) +static int coroutine_fn cor_co_preadv_part(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, + size_t qiov_offset, + int flags) { - return bdrv_co_preadv(bs->file, offset, bytes, qiov, - flags | BDRV_REQ_COPY_ON_READ); + int64_t n; + int local_flags; + int ret; + BDRVStateCOR *state = bs->opaque; + + if (!state->bottom_bs) { + return bdrv_co_preadv_part(bs->file, offset, bytes, qiov, qiov_offset, + flags | BDRV_REQ_COPY_ON_READ); + } + + while (bytes) { + local_flags = flags; + + /* In case of failure, try to copy-on-read anyway */ + ret = bdrv_is_allocated(bs->file->bs, offset, bytes, &n); + if (ret <= 0) { + ret = bdrv_is_allocated_above(bdrv_backing_chain_next(bs->file->bs), + state->bottom_bs, true, offset, + n, &n); + if (ret > 0 || ret < 0) { + local_flags |= BDRV_REQ_COPY_ON_READ; + } + /* Finish earlier if the end of a backing file has been reached */ + if (n == 0) { + break; + } + } + + /* Skip if neither read nor write are needed */ + if ((local_flags & (BDRV_REQ_PREFETCH | BDRV_REQ_COPY_ON_READ)) != + BDRV_REQ_PREFETCH) { + ret = bdrv_co_preadv_part(bs->file, offset, n, qiov, qiov_offset, + local_flags); + if (ret < 0) { + return ret; + } + } + + offset += n; + qiov_offset += n; + bytes -= n; + } + + return 0; } -static int coroutine_fn cor_co_pwritev(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) +static int coroutine_fn cor_co_pwritev_part(BlockDriverState *bs, + uint64_t offset, + uint64_t bytes, + QEMUIOVector *qiov, + size_t qiov_offset, int flags) { - - return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); + return bdrv_co_pwritev_part(bs->file, offset, bytes, qiov, qiov_offset, + flags); } @@ -107,6 +219,16 @@ static int coroutine_fn cor_co_pdiscard(BlockDriverState *bs, } +static int coroutine_fn cor_co_pwritev_compressed(BlockDriverState *bs, + uint64_t offset, + uint64_t bytes, + QEMUIOVector *qiov) +{ + return bdrv_co_pwritev(bs->file, offset, bytes, qiov, + BDRV_REQ_WRITE_COMPRESSED); +} + + static void cor_eject(BlockDriverState *bs, bool eject_flag) { bdrv_eject(bs->file->bs, eject_flag); @@ -119,28 +241,75 @@ static void cor_lock_medium(BlockDriverState *bs, bool locked) } +static void cor_close(BlockDriverState *bs) +{ + BDRVStateCOR *s = bs->opaque; + + if (s->chain_frozen) { + s->chain_frozen = false; + bdrv_unfreeze_backing_chain(bs, s->bottom_bs); + } + + bdrv_unref(s->bottom_bs); +} + + static BlockDriver bdrv_copy_on_read = { .format_name = "copy-on-read", + .instance_size = sizeof(BDRVStateCOR), .bdrv_open = cor_open, + .bdrv_close = cor_close, .bdrv_child_perm = cor_child_perm, .bdrv_getlength = cor_getlength, - .bdrv_co_preadv = cor_co_preadv, - .bdrv_co_pwritev = cor_co_pwritev, + .bdrv_co_preadv_part = cor_co_preadv_part, + .bdrv_co_pwritev_part = cor_co_pwritev_part, .bdrv_co_pwrite_zeroes = cor_co_pwrite_zeroes, .bdrv_co_pdiscard = cor_co_pdiscard, + .bdrv_co_pwritev_compressed = cor_co_pwritev_compressed, .bdrv_eject = cor_eject, .bdrv_lock_medium = cor_lock_medium, - .bdrv_co_block_status = bdrv_co_block_status_from_file, - .has_variable_length = true, .is_filter = true, }; + +void bdrv_cor_filter_drop(BlockDriverState *cor_filter_bs) +{ + BdrvChild *child; + BlockDriverState *bs; + BDRVStateCOR *s = cor_filter_bs->opaque; + + child = bdrv_filter_child(cor_filter_bs); + if (!child) { + return; + } + bs = child->bs; + + /* Retain the BDS until we complete the graph change. */ + bdrv_ref(bs); + /* Hold a guest back from writing while permissions are being reset. */ + bdrv_drained_begin(bs); + /* Drop permissions before the graph change. */ + s->active = false; + /* unfreeze, as otherwise bdrv_replace_node() will fail */ + if (s->chain_frozen) { + s->chain_frozen = false; + bdrv_unfreeze_backing_chain(cor_filter_bs, s->bottom_bs); + } + bdrv_child_refresh_perms(cor_filter_bs, child, &error_abort); + bdrv_replace_node(cor_filter_bs, bs, &error_abort); + + bdrv_drained_end(bs); + bdrv_unref(bs); + bdrv_unref(cor_filter_bs); +} + + static void bdrv_copy_on_read_init(void) { bdrv_register(&bdrv_copy_on_read); diff --git a/block/copy-on-read.h b/block/copy-on-read.h new file mode 100644 index 0000000000..7bf405dccd --- /dev/null +++ b/block/copy-on-read.h @@ -0,0 +1,32 @@ +/* + * Copy-on-read filter block driver + * + * The filter driver performs Copy-On-Read (COR) operations + * + * Copyright (c) 2018-2020 Virtuozzo International GmbH. + * + * Author: + * Andrey Shinkevich + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef BLOCK_COPY_ON_READ +#define BLOCK_COPY_ON_READ + +#include "block/block_int.h" + +void bdrv_cor_filter_drop(BlockDriverState *cor_filter_bs); + +#endif /* BLOCK_COPY_ON_READ */ diff --git a/block/coroutines.h b/block/coroutines.h new file mode 100644 index 0000000000..4cfb4946e6 --- /dev/null +++ b/block/coroutines.h @@ -0,0 +1,69 @@ +/* + * Block layer I/O functions + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef BLOCK_COROUTINES_INT_H +#define BLOCK_COROUTINES_INT_H + +#include "block/block_int.h" + +int coroutine_fn bdrv_co_check(BlockDriverState *bs, + BdrvCheckResult *res, BdrvCheckMode fix); +int coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp); + +int generated_co_wrapper +bdrv_preadv(BdrvChild *child, int64_t offset, unsigned int bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags); +int generated_co_wrapper +bdrv_pwritev(BdrvChild *child, int64_t offset, unsigned int bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags); + +int coroutine_fn +bdrv_co_common_block_status_above(BlockDriverState *bs, + BlockDriverState *base, + bool include_base, + bool want_zero, + int64_t offset, + int64_t bytes, + int64_t *pnum, + int64_t *map, + BlockDriverState **file, + int *depth); +int generated_co_wrapper +bdrv_common_block_status_above(BlockDriverState *bs, + BlockDriverState *base, + bool include_base, + bool want_zero, + int64_t offset, + int64_t bytes, + int64_t *pnum, + int64_t *map, + BlockDriverState **file, + int *depth); + +int coroutine_fn bdrv_co_readv_vmstate(BlockDriverState *bs, + QEMUIOVector *qiov, int64_t pos); +int coroutine_fn bdrv_co_writev_vmstate(BlockDriverState *bs, + QEMUIOVector *qiov, int64_t pos); + +#endif /* BLOCK_COROUTINES_INT_H */ diff --git a/block/crypto.c b/block/crypto.c index 0807557763..1d30fde38e 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -725,17 +725,8 @@ fail: * If an error occurred, delete 'filename'. Even if the file existed * beforehand, it has been truncated and corrupted in the process. */ - if (ret && bs) { - Error *local_delete_err = NULL; - int r_del = bdrv_co_delete_file(bs, &local_delete_err); - /* - * ENOTSUP will happen if the block driver doesn't support - * the 'bdrv_co_delete_file' interface. This is a predictable - * scenario and shouldn't be reported back to the user. - */ - if ((r_del < 0) && (r_del != -ENOTSUP)) { - error_report_err(local_delete_err); - } + if (ret) { + bdrv_co_delete_file_noerr(bs); } bdrv_unref(bs); diff --git a/block/crypto.h b/block/crypto.h index c72c3dec61..72e792c9af 100644 --- a/block/crypto.h +++ b/block/crypto.h @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/block/curl.c b/block/curl.c index 4f907c47be..50e741a0d7 100644 --- a/block/curl.c +++ b/block/curl.c @@ -37,26 +37,6 @@ // #define DEBUG_VERBOSE -#if LIBCURL_VERSION_NUM >= 0x071000 -/* The multi interface timer callback was introduced in 7.16.0 */ -#define NEED_CURL_TIMER_CALLBACK -#define HAVE_SOCKET_ACTION -#endif - -#ifndef HAVE_SOCKET_ACTION -/* If curl_multi_socket_action isn't available, define it statically here in - * terms of curl_multi_socket. Note that ev_bitmask will be ignored, which is - * less efficient but still safe. */ -static CURLMcode __curl_multi_socket_action(CURLM *multi_handle, - curl_socket_t sockfd, - int ev_bitmask, - int *running_handles) -{ - return curl_multi_socket(multi_handle, sockfd, running_handles); -} -#define curl_multi_socket_action __curl_multi_socket_action -#endif - #define PROTOCOLS (CURLPROTO_HTTP | CURLPROTO_HTTPS | \ CURLPROTO_FTP | CURLPROTO_FTPS) @@ -98,8 +78,7 @@ typedef struct CURLAIOCB { typedef struct CURLSocket { int fd; - struct CURLState *state; - QLIST_ENTRY(CURLSocket) next; + struct BDRVCURLState *s; } CURLSocket; typedef struct CURLState @@ -107,7 +86,6 @@ typedef struct CURLState struct BDRVCURLState *s; CURLAIOCB *acb[CURL_NUM_ACB]; CURL *curl; - QLIST_HEAD(, CURLSocket) sockets; char *orig_buf; uint64_t buf_start; size_t buf_off; @@ -122,6 +100,7 @@ typedef struct BDRVCURLState { QEMUTimer timer; uint64_t len; CURLState states[CURL_NUM_STATES]; + GHashTable *sockets; /* GINT_TO_POINTER(fd) -> socket */ char *url; size_t readahead_size; bool sslverify; @@ -140,7 +119,21 @@ typedef struct BDRVCURLState { static void curl_clean_state(CURLState *s); static void curl_multi_do(void *arg); -#ifdef NEED_CURL_TIMER_CALLBACK +static gboolean curl_drop_socket(void *key, void *value, void *opaque) +{ + CURLSocket *socket = value; + BDRVCURLState *s = socket->s; + + aio_set_fd_handler(s->aio_context, socket->fd, false, + NULL, NULL, NULL, NULL); + return true; +} + +static void curl_drop_all_sockets(GHashTable *sockets) +{ + g_hash_table_foreach_remove(sockets, curl_drop_socket, NULL); +} + /* Called from curl_multi_do_locked, with s->mutex held. */ static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque) { @@ -156,7 +149,6 @@ static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque) } return 0; } -#endif /* Called from curl_multi_do_locked, with s->mutex held. */ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, @@ -169,16 +161,12 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&state); s = state->s; - QLIST_FOREACH(socket, &state->sockets, next) { - if (socket->fd == fd) { - break; - } - } + socket = g_hash_table_lookup(s->sockets, GINT_TO_POINTER(fd)); if (!socket) { socket = g_new0(CURLSocket, 1); socket->fd = fd; - socket->state = state; - QLIST_INSERT_HEAD(&state->sockets, socket, next); + socket->s = s; + g_hash_table_insert(s->sockets, GINT_TO_POINTER(fd), socket); } trace_curl_sock_cb(action, (int)fd); @@ -202,8 +190,7 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, } if (action == CURL_POLL_REMOVE) { - QLIST_REMOVE(socket, next); - g_free(socket); + g_hash_table_remove(s->sockets, GINT_TO_POINTER(fd)); } return 0; @@ -407,7 +394,7 @@ static void curl_multi_check_completion(BDRVCURLState *s) /* Called with s->mutex held. */ static void curl_multi_do_locked(CURLSocket *socket) { - BDRVCURLState *s = socket->state->s; + BDRVCURLState *s = socket->s; int running; int r; @@ -423,7 +410,7 @@ static void curl_multi_do_locked(CURLSocket *socket) static void curl_multi_do(void *arg) { CURLSocket *socket = arg; - BDRVCURLState *s = socket->state->s; + BDRVCURLState *s = socket->s; qemu_mutex_lock(&s->mutex); curl_multi_do_locked(socket); @@ -433,7 +420,6 @@ static void curl_multi_do(void *arg) static void curl_multi_timeout_do(void *arg) { -#ifdef NEED_CURL_TIMER_CALLBACK BDRVCURLState *s = (BDRVCURLState *)arg; int running; @@ -446,9 +432,6 @@ static void curl_multi_timeout_do(void *arg) curl_multi_check_completion(s); qemu_mutex_unlock(&s->mutex); -#else - abort(); -#endif } /* Called with s->mutex held. */ @@ -524,7 +507,6 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state) #endif } - QLIST_INIT(&state->sockets); state->s = s; return 0; @@ -541,13 +523,6 @@ static void curl_clean_state(CURLState *s) if (s->s->multi) curl_multi_remove_handle(s->s->multi, s->curl); - while (!QLIST_EMPTY(&s->sockets)) { - CURLSocket *socket = QLIST_FIRST(&s->sockets); - - QLIST_REMOVE(socket, next); - g_free(socket); - } - s->in_use = 0; qemu_co_enter_next(&s->s->free_state_waitq, &s->s->mutex); @@ -564,23 +539,24 @@ static void curl_detach_aio_context(BlockDriverState *bs) BDRVCURLState *s = bs->opaque; int i; - qemu_mutex_lock(&s->mutex); - for (i = 0; i < CURL_NUM_STATES; i++) { - if (s->states[i].in_use) { - curl_clean_state(&s->states[i]); + WITH_QEMU_LOCK_GUARD(&s->mutex) { + curl_drop_all_sockets(s->sockets); + for (i = 0; i < CURL_NUM_STATES; i++) { + if (s->states[i].in_use) { + curl_clean_state(&s->states[i]); + } + if (s->states[i].curl) { + curl_easy_cleanup(s->states[i].curl); + s->states[i].curl = NULL; + } + g_free(s->states[i].orig_buf); + s->states[i].orig_buf = NULL; } - if (s->states[i].curl) { - curl_easy_cleanup(s->states[i].curl); - s->states[i].curl = NULL; + if (s->multi) { + curl_multi_cleanup(s->multi); + s->multi = NULL; } - g_free(s->states[i].orig_buf); - s->states[i].orig_buf = NULL; } - if (s->multi) { - curl_multi_cleanup(s->multi); - s->multi = NULL; - } - qemu_mutex_unlock(&s->mutex); timer_del(&s->timer); } @@ -598,10 +574,8 @@ static void curl_attach_aio_context(BlockDriverState *bs, s->multi = curl_multi_init(); s->aio_context = new_context; curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb); -#ifdef NEED_CURL_TIMER_CALLBACK curl_multi_setopt(s->multi, CURLMOPT_TIMERDATA, s); curl_multi_setopt(s->multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb); -#endif } static QemuOptsList runtime_opts = { @@ -773,6 +747,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, qemu_co_queue_init(&s->free_state_waitq); s->aio_context = bdrv_get_aio_context(bs); s->url = g_strdup(file); + s->sockets = g_hash_table_new_full(NULL, NULL, NULL, g_free); qemu_mutex_lock(&s->mutex); state = curl_find_state(s); qemu_mutex_unlock(&s->mutex); @@ -846,6 +821,8 @@ out_noclean: g_free(s->username); g_free(s->proxyusername); g_free(s->proxypassword); + curl_drop_all_sockets(s->sockets); + g_hash_table_destroy(s->sockets); qemu_opts_del(opts); return -EINVAL; } @@ -944,6 +921,7 @@ static void curl_close(BlockDriverState *bs) curl_detach_aio_context(bs); qemu_mutex_destroy(&s->mutex); + g_hash_table_destroy(s->sockets); g_free(s->cookie); g_free(s->url); g_free(s->username); diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index c01319b188..68d295d6e3 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -166,43 +166,6 @@ bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap) return !bitmap->disabled; } -/** - * bdrv_dirty_bitmap_status: This API is now deprecated. - * Called with BQL taken. - * - * A BdrvDirtyBitmap can be in four possible user-visible states: - * (1) Active: successor is NULL, and disabled is false: full r/w mode - * (2) Disabled: successor is NULL, and disabled is true: qualified r/w mode, - * guest writes are dropped, but monitor writes are possible, - * through commands like merge and clear. - * (3) Frozen: successor is not NULL. - * A frozen bitmap cannot be renamed, deleted, cleared, set, - * enabled, merged to, etc. A frozen bitmap can only abdicate() - * or reclaim(). - * In this state, the anonymous successor bitmap may be either - * Active and recording writes from the guest (e.g. backup jobs), - * or it can be Disabled and not recording writes. - * (4) Locked: Whether Active or Disabled, the user cannot modify this bitmap - * in any way from the monitor. - * (5) Inconsistent: This is a persistent bitmap whose "in use" bit is set, and - * is unusable by QEMU. It can be deleted to remove it from - * the qcow2. - */ -DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap) -{ - if (bdrv_dirty_bitmap_inconsistent(bitmap)) { - return DIRTY_BITMAP_STATUS_INCONSISTENT; - } else if (bdrv_dirty_bitmap_has_successor(bitmap)) { - return DIRTY_BITMAP_STATUS_FROZEN; - } else if (bdrv_dirty_bitmap_busy(bitmap)) { - return DIRTY_BITMAP_STATUS_LOCKED; - } else if (!bdrv_dirty_bitmap_enabled(bitmap)) { - return DIRTY_BITMAP_STATUS_DISABLED; - } else { - return DIRTY_BITMAP_STATUS_ACTIVE; - } -} - /* Called with BQL taken. */ static bool bdrv_dirty_bitmap_recording(BdrvDirtyBitmap *bitmap) { @@ -572,25 +535,22 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) { BdrvDirtyBitmap *bm; BlockDirtyInfoList *list = NULL; - BlockDirtyInfoList **plist = &list; + BlockDirtyInfoList **tail = &list; bdrv_dirty_bitmaps_lock(bs); QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { BlockDirtyInfo *info = g_new0(BlockDirtyInfo, 1); - BlockDirtyInfoList *entry = g_new0(BlockDirtyInfoList, 1); + info->count = bdrv_get_dirty_count(bm); info->granularity = bdrv_dirty_bitmap_granularity(bm); info->has_name = !!bm->name; info->name = g_strdup(bm->name); - info->status = bdrv_dirty_bitmap_status(bm); info->recording = bdrv_dirty_bitmap_recording(bm); info->busy = bdrv_dirty_bitmap_busy(bm); info->persistent = bm->persistent; info->has_inconsistent = bm->inconsistent; info->inconsistent = bm->inconsistent; - entry->value = info; - *plist = entry; - plist = &entry->next; + QAPI_LIST_APPEND(tail, info); } bdrv_dirty_bitmaps_unlock(bs); @@ -728,6 +688,19 @@ uint64_t bdrv_dirty_bitmap_serialization_align(const BdrvDirtyBitmap *bitmap) return hbitmap_serialization_align(bitmap->bitmap); } +/* Return the disk size covered by a chunk of serialized bitmap data. */ +uint64_t bdrv_dirty_bitmap_serialization_coverage(int serialized_chunk_size, + const BdrvDirtyBitmap *bitmap) +{ + uint64_t granularity = bdrv_dirty_bitmap_granularity(bitmap); + uint64_t limit = granularity * (serialized_chunk_size << 3); + + assert(QEMU_IS_ALIGNED(limit, + bdrv_dirty_bitmap_serialization_align(bitmap))); + return limit; +} + + void bdrv_dirty_bitmap_serialize_part(const BdrvDirtyBitmap *bitmap, uint8_t *buf, uint64_t offset, uint64_t bytes) diff --git a/block/dmg-lzfse.c b/block/dmg-lzfse.c index 19d25bc646..6798cf4fbf 100644 --- a/block/dmg-lzfse.c +++ b/block/dmg-lzfse.c @@ -22,7 +22,6 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "dmg.h" #include diff --git a/block/dmg.c b/block/dmg.c index 0d6c317296..ef35a505f2 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -559,7 +559,7 @@ static void dmg_refresh_limits(BlockDriverState *bs, Error **errp) bs->bl.request_alignment = BDRV_SECTOR_SIZE; /* No sub-sector I/O */ } -static inline int is_sector_in_chunk(BDRVDMGState* s, +static inline int is_sector_in_chunk(BDRVDMGState *s, uint32_t chunk_num, uint64_t sector_num) { if (chunk_num >= s->n_chunks || s->sectors[chunk_num] > sector_num || diff --git a/block/export/export.c b/block/export/export.c new file mode 100644 index 0000000000..fec7d9f738 --- /dev/null +++ b/block/export/export.c @@ -0,0 +1,361 @@ +/* + * Common block export infrastructure + * + * Copyright (c) 2012, 2020 Red Hat, Inc. + * + * Authors: + * Paolo Bonzini + * Kevin Wolf + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "block/block.h" +#include "sysemu/block-backend.h" +#include "sysemu/iothread.h" +#include "block/export.h" +#include "block/fuse.h" +#include "block/nbd.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-block-export.h" +#include "qapi/qapi-events-block-export.h" +#include "qemu/id.h" +#ifdef CONFIG_VHOST_USER_BLK_SERVER +#include "vhost-user-blk-server.h" +#endif + +static const BlockExportDriver *blk_exp_drivers[] = { + &blk_exp_nbd, +#ifdef CONFIG_VHOST_USER_BLK_SERVER + &blk_exp_vhost_user_blk, +#endif +#ifdef CONFIG_FUSE + &blk_exp_fuse, +#endif +}; + +/* Only accessed from the main thread */ +static QLIST_HEAD(, BlockExport) block_exports = + QLIST_HEAD_INITIALIZER(block_exports); + +BlockExport *blk_exp_find(const char *id) +{ + BlockExport *exp; + + QLIST_FOREACH(exp, &block_exports, next) { + if (strcmp(id, exp->id) == 0) { + return exp; + } + } + + return NULL; +} + +static const BlockExportDriver *blk_exp_find_driver(BlockExportType type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(blk_exp_drivers); i++) { + if (blk_exp_drivers[i]->type == type) { + return blk_exp_drivers[i]; + } + } + return NULL; +} + +BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp) +{ + bool fixed_iothread = export->has_fixed_iothread && export->fixed_iothread; + const BlockExportDriver *drv; + BlockExport *exp = NULL; + BlockDriverState *bs; + BlockBackend *blk = NULL; + AioContext *ctx; + uint64_t perm; + int ret; + + if (!id_wellformed(export->id)) { + error_setg(errp, "Invalid block export id"); + return NULL; + } + if (blk_exp_find(export->id)) { + error_setg(errp, "Block export id '%s' is already in use", export->id); + return NULL; + } + + drv = blk_exp_find_driver(export->type); + if (!drv) { + error_setg(errp, "No driver found for the requested export type"); + return NULL; + } + + bs = bdrv_lookup_bs(NULL, export->node_name, errp); + if (!bs) { + return NULL; + } + + if (!export->has_writable) { + export->writable = false; + } + if (bdrv_is_read_only(bs) && export->writable) { + error_setg(errp, "Cannot export read-only node as writable"); + return NULL; + } + + ctx = bdrv_get_aio_context(bs); + aio_context_acquire(ctx); + + if (export->has_iothread) { + IOThread *iothread; + AioContext *new_ctx; + + iothread = iothread_by_id(export->iothread); + if (!iothread) { + error_setg(errp, "iothread \"%s\" not found", export->iothread); + goto fail; + } + + new_ctx = iothread_get_aio_context(iothread); + + ret = bdrv_try_set_aio_context(bs, new_ctx, errp); + if (ret == 0) { + aio_context_release(ctx); + aio_context_acquire(new_ctx); + ctx = new_ctx; + } else if (fixed_iothread) { + goto fail; + } + } + + /* + * Block exports are used for non-shared storage migration. Make sure + * that BDRV_O_INACTIVE is cleared and the image is ready for write + * access since the export could be available before migration handover. + * ctx was acquired in the caller. + */ + bdrv_invalidate_cache(bs, NULL); + + perm = BLK_PERM_CONSISTENT_READ; + if (export->writable) { + perm |= BLK_PERM_WRITE; + } + + blk = blk_new(ctx, perm, BLK_PERM_ALL); + + if (!fixed_iothread) { + blk_set_allow_aio_context_change(blk, true); + } + + ret = blk_insert_bs(blk, bs, errp); + if (ret < 0) { + goto fail; + } + + if (!export->has_writethrough) { + export->writethrough = false; + } + blk_set_enable_write_cache(blk, !export->writethrough); + + assert(drv->instance_size >= sizeof(BlockExport)); + exp = g_malloc0(drv->instance_size); + *exp = (BlockExport) { + .drv = drv, + .refcount = 1, + .user_owned = true, + .id = g_strdup(export->id), + .ctx = ctx, + .blk = blk, + }; + + ret = drv->create(exp, export, errp); + if (ret < 0) { + goto fail; + } + + assert(exp->blk != NULL); + + QLIST_INSERT_HEAD(&block_exports, exp, next); + + aio_context_release(ctx); + return exp; + +fail: + blk_unref(blk); + aio_context_release(ctx); + if (exp) { + g_free(exp->id); + g_free(exp); + } + return NULL; +} + +/* Callers must hold exp->ctx lock */ +void blk_exp_ref(BlockExport *exp) +{ + assert(exp->refcount > 0); + exp->refcount++; +} + +/* Runs in the main thread */ +static void blk_exp_delete_bh(void *opaque) +{ + BlockExport *exp = opaque; + AioContext *aio_context = exp->ctx; + + aio_context_acquire(aio_context); + + assert(exp->refcount == 0); + QLIST_REMOVE(exp, next); + exp->drv->delete(exp); + blk_unref(exp->blk); + qapi_event_send_block_export_deleted(exp->id); + g_free(exp->id); + g_free(exp); + + aio_context_release(aio_context); +} + +/* Callers must hold exp->ctx lock */ +void blk_exp_unref(BlockExport *exp) +{ + assert(exp->refcount > 0); + if (--exp->refcount == 0) { + /* Touch the block_exports list only in the main thread */ + aio_bh_schedule_oneshot(qemu_get_aio_context(), blk_exp_delete_bh, + exp); + } +} + +/* + * Drops the user reference to the export and requests that all client + * connections and other internally held references start to shut down. When + * the function returns, there may still be active references while the export + * is in the process of shutting down. + * + * Acquires exp->ctx internally. Callers must *not* hold the lock. + */ +void blk_exp_request_shutdown(BlockExport *exp) +{ + AioContext *aio_context = exp->ctx; + + aio_context_acquire(aio_context); + + /* + * If the user doesn't own the export any more, it is already shutting + * down. We must not call .request_shutdown and decrease the refcount a + * second time. + */ + if (!exp->user_owned) { + goto out; + } + + exp->drv->request_shutdown(exp); + + assert(exp->user_owned); + exp->user_owned = false; + blk_exp_unref(exp); + +out: + aio_context_release(aio_context); +} + +/* + * Returns whether a block export of the given type exists. + * type == BLOCK_EXPORT_TYPE__MAX checks for an export of any type. + */ +static bool blk_exp_has_type(BlockExportType type) +{ + BlockExport *exp; + + if (type == BLOCK_EXPORT_TYPE__MAX) { + return !QLIST_EMPTY(&block_exports); + } + + QLIST_FOREACH(exp, &block_exports, next) { + if (exp->drv->type == type) { + return true; + } + } + + return false; +} + +/* type == BLOCK_EXPORT_TYPE__MAX for all types */ +void blk_exp_close_all_type(BlockExportType type) +{ + BlockExport *exp, *next; + + assert(in_aio_context_home_thread(qemu_get_aio_context())); + + QLIST_FOREACH_SAFE(exp, &block_exports, next, next) { + if (type != BLOCK_EXPORT_TYPE__MAX && exp->drv->type != type) { + continue; + } + blk_exp_request_shutdown(exp); + } + + AIO_WAIT_WHILE(NULL, blk_exp_has_type(type)); +} + +void blk_exp_close_all(void) +{ + blk_exp_close_all_type(BLOCK_EXPORT_TYPE__MAX); +} + +void qmp_block_export_add(BlockExportOptions *export, Error **errp) +{ + blk_exp_add(export, errp); +} + +void qmp_block_export_del(const char *id, + bool has_mode, BlockExportRemoveMode mode, + Error **errp) +{ + ERRP_GUARD(); + BlockExport *exp; + + exp = blk_exp_find(id); + if (exp == NULL) { + error_setg(errp, "Export '%s' is not found", id); + return; + } + if (!exp->user_owned) { + error_setg(errp, "Export '%s' is already shutting down", id); + return; + } + + if (!has_mode) { + mode = BLOCK_EXPORT_REMOVE_MODE_SAFE; + } + if (mode == BLOCK_EXPORT_REMOVE_MODE_SAFE && exp->refcount > 1) { + error_setg(errp, "export '%s' still in use", exp->id); + error_append_hint(errp, "Use mode='hard' to force client " + "disconnect\n"); + return; + } + + blk_exp_request_shutdown(exp); +} + +BlockExportInfoList *qmp_query_block_exports(Error **errp) +{ + BlockExportInfoList *head = NULL, **tail = &head; + BlockExport *exp; + + QLIST_FOREACH(exp, &block_exports, next) { + BlockExportInfo *info = g_new(BlockExportInfo, 1); + *info = (BlockExportInfo) { + .id = g_strdup(exp->id), + .type = exp->drv->type, + .node_name = g_strdup(bdrv_get_node_name(blk_bs(exp->blk))), + .shutting_down = !exp->user_owned, + }; + + QAPI_LIST_APPEND(tail, info); + } + + return head; +} diff --git a/block/export/fuse.c b/block/export/fuse.c new file mode 100644 index 0000000000..38f74c94da --- /dev/null +++ b/block/export/fuse.c @@ -0,0 +1,726 @@ +/* + * Present a block device as a raw image through FUSE + * + * Copyright (c) 2020 Max Reitz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 or later of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#define FUSE_USE_VERSION 31 + +#include "qemu/osdep.h" +#include "block/aio.h" +#include "block/block.h" +#include "block/export.h" +#include "block/fuse.h" +#include "block/qapi.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-block.h" +#include "sysemu/block-backend.h" + +#include +#include + + +/* Prevent overly long bounce buffer allocations */ +#define FUSE_MAX_BOUNCE_BYTES (MIN(BDRV_REQUEST_MAX_BYTES, 64 * 1024 * 1024)) + + +typedef struct FuseExport { + BlockExport common; + + struct fuse_session *fuse_session; + struct fuse_buf fuse_buf; + bool mounted, fd_handler_set_up; + + char *mountpoint; + bool writable; + bool growable; +} FuseExport; + +static GHashTable *exports; +static const struct fuse_lowlevel_ops fuse_ops; + +static void fuse_export_shutdown(BlockExport *exp); +static void fuse_export_delete(BlockExport *exp); + +static void init_exports_table(void); + +static int setup_fuse_export(FuseExport *exp, const char *mountpoint, + Error **errp); +static void read_from_fuse_export(void *opaque); + +static bool is_regular_file(const char *path, Error **errp); + + +static int fuse_export_create(BlockExport *blk_exp, + BlockExportOptions *blk_exp_args, + Error **errp) +{ + FuseExport *exp = container_of(blk_exp, FuseExport, common); + BlockExportOptionsFuse *args = &blk_exp_args->u.fuse; + int ret; + + assert(blk_exp_args->type == BLOCK_EXPORT_TYPE_FUSE); + + /* For growable exports, take the RESIZE permission */ + if (args->growable) { + uint64_t blk_perm, blk_shared_perm; + + blk_get_perm(exp->common.blk, &blk_perm, &blk_shared_perm); + + ret = blk_set_perm(exp->common.blk, blk_perm | BLK_PERM_RESIZE, + blk_shared_perm, errp); + if (ret < 0) { + return ret; + } + } + + init_exports_table(); + + /* + * It is important to do this check before calling is_regular_file() -- + * that function will do a stat(), which we would have to handle if we + * already exported something on @mountpoint. But we cannot, because + * we are currently caught up here. + * (Note that ideally we would want to resolve relative paths here, + * but bdrv_make_absolute_filename() might do the wrong thing for + * paths that contain colons, and realpath() would resolve symlinks, + * which we do not want: The mount point is not going to be the + * symlink's destination, but the link itself.) + * So this will not catch all potential clashes, but hopefully at + * least the most common one of specifying exactly the same path + * string twice. + */ + if (g_hash_table_contains(exports, args->mountpoint)) { + error_setg(errp, "There already is a FUSE export on '%s'", + args->mountpoint); + ret = -EEXIST; + goto fail; + } + + if (!is_regular_file(args->mountpoint, errp)) { + ret = -EINVAL; + goto fail; + } + + exp->mountpoint = g_strdup(args->mountpoint); + exp->writable = blk_exp_args->writable; + exp->growable = args->growable; + + ret = setup_fuse_export(exp, args->mountpoint, errp); + if (ret < 0) { + goto fail; + } + + return 0; + +fail: + fuse_export_delete(blk_exp); + return ret; +} + +/** + * Allocates the global @exports hash table. + */ +static void init_exports_table(void) +{ + if (exports) { + return; + } + + exports = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); +} + +/** + * Create exp->fuse_session and mount it. + */ +static int setup_fuse_export(FuseExport *exp, const char *mountpoint, + Error **errp) +{ + const char *fuse_argv[4]; + char *mount_opts; + struct fuse_args fuse_args; + int ret; + + /* Needs to match what fuse_init() sets. Only max_read must be supplied. */ + mount_opts = g_strdup_printf("max_read=%zu", FUSE_MAX_BOUNCE_BYTES); + + fuse_argv[0] = ""; /* Dummy program name */ + fuse_argv[1] = "-o"; + fuse_argv[2] = mount_opts; + fuse_argv[3] = NULL; + fuse_args = (struct fuse_args)FUSE_ARGS_INIT(3, (char **)fuse_argv); + + exp->fuse_session = fuse_session_new(&fuse_args, &fuse_ops, + sizeof(fuse_ops), exp); + g_free(mount_opts); + if (!exp->fuse_session) { + error_setg(errp, "Failed to set up FUSE session"); + ret = -EIO; + goto fail; + } + + ret = fuse_session_mount(exp->fuse_session, mountpoint); + if (ret < 0) { + error_setg(errp, "Failed to mount FUSE session to export"); + ret = -EIO; + goto fail; + } + exp->mounted = true; + + g_hash_table_insert(exports, g_strdup(mountpoint), NULL); + + aio_set_fd_handler(exp->common.ctx, + fuse_session_fd(exp->fuse_session), true, + read_from_fuse_export, NULL, NULL, exp); + exp->fd_handler_set_up = true; + + return 0; + +fail: + fuse_export_shutdown(&exp->common); + return ret; +} + +/** + * Callback to be invoked when the FUSE session FD can be read from. + * (This is basically the FUSE event loop.) + */ +static void read_from_fuse_export(void *opaque) +{ + FuseExport *exp = opaque; + int ret; + + blk_exp_ref(&exp->common); + + do { + ret = fuse_session_receive_buf(exp->fuse_session, &exp->fuse_buf); + } while (ret == -EINTR); + if (ret < 0) { + goto out; + } + + fuse_session_process_buf(exp->fuse_session, &exp->fuse_buf); + +out: + blk_exp_unref(&exp->common); +} + +static void fuse_export_shutdown(BlockExport *blk_exp) +{ + FuseExport *exp = container_of(blk_exp, FuseExport, common); + + if (exp->fuse_session) { + fuse_session_exit(exp->fuse_session); + + if (exp->fd_handler_set_up) { + aio_set_fd_handler(exp->common.ctx, + fuse_session_fd(exp->fuse_session), true, + NULL, NULL, NULL, NULL); + exp->fd_handler_set_up = false; + } + } + + if (exp->mountpoint) { + /* + * Safe to drop now, because we will not handle any requests + * for this export anymore anyway. + */ + g_hash_table_remove(exports, exp->mountpoint); + } +} + +static void fuse_export_delete(BlockExport *blk_exp) +{ + FuseExport *exp = container_of(blk_exp, FuseExport, common); + + if (exp->fuse_session) { + if (exp->mounted) { + fuse_session_unmount(exp->fuse_session); + } + + fuse_session_destroy(exp->fuse_session); + } + + free(exp->fuse_buf.mem); + g_free(exp->mountpoint); +} + +/** + * Check whether @path points to a regular file. If not, put an + * appropriate message into *errp. + */ +static bool is_regular_file(const char *path, Error **errp) +{ + struct stat statbuf; + int ret; + + ret = stat(path, &statbuf); + if (ret < 0) { + error_setg_errno(errp, errno, "Failed to stat '%s'", path); + return false; + } + + if (!S_ISREG(statbuf.st_mode)) { + error_setg(errp, "'%s' is not a regular file", path); + return false; + } + + return true; +} + +/** + * A chance to set change some parameters supplied to FUSE_INIT. + */ +static void fuse_init(void *userdata, struct fuse_conn_info *conn) +{ + /* + * MIN_NON_ZERO() would not be wrong here, but what we set here + * must equal what has been passed to fuse_session_new(). + * Therefore, as long as max_read must be passed as a mount option + * (which libfuse claims will be changed at some point), we have + * to set max_read to a fixed value here. + */ + conn->max_read = FUSE_MAX_BOUNCE_BYTES; + + conn->max_write = MIN_NON_ZERO(BDRV_REQUEST_MAX_BYTES, conn->max_write); +} + +/** + * Let clients look up files. Always return ENOENT because we only + * care about the mountpoint itself. + */ +static void fuse_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) +{ + fuse_reply_err(req, ENOENT); +} + +/** + * Let clients get file attributes (i.e., stat() the file). + */ +static void fuse_getattr(fuse_req_t req, fuse_ino_t inode, + struct fuse_file_info *fi) +{ + struct stat statbuf; + int64_t length, allocated_blocks; + time_t now = time(NULL); + FuseExport *exp = fuse_req_userdata(req); + mode_t mode; + + length = blk_getlength(exp->common.blk); + if (length < 0) { + fuse_reply_err(req, -length); + return; + } + + allocated_blocks = bdrv_get_allocated_file_size(blk_bs(exp->common.blk)); + if (allocated_blocks <= 0) { + allocated_blocks = DIV_ROUND_UP(length, 512); + } else { + allocated_blocks = DIV_ROUND_UP(allocated_blocks, 512); + } + + mode = S_IFREG | S_IRUSR; + if (exp->writable) { + mode |= S_IWUSR; + } + + statbuf = (struct stat) { + .st_ino = inode, + .st_mode = mode, + .st_nlink = 1, + .st_uid = getuid(), + .st_gid = getgid(), + .st_size = length, + .st_blksize = blk_bs(exp->common.blk)->bl.request_alignment, + .st_blocks = allocated_blocks, + .st_atime = now, + .st_mtime = now, + .st_ctime = now, + }; + + fuse_reply_attr(req, &statbuf, 1.); +} + +static int fuse_do_truncate(const FuseExport *exp, int64_t size, + bool req_zero_write, PreallocMode prealloc) +{ + uint64_t blk_perm, blk_shared_perm; + BdrvRequestFlags truncate_flags = 0; + int ret; + + if (req_zero_write) { + truncate_flags |= BDRV_REQ_ZERO_WRITE; + } + + /* Growable exports have a permanent RESIZE permission */ + if (!exp->growable) { + blk_get_perm(exp->common.blk, &blk_perm, &blk_shared_perm); + + ret = blk_set_perm(exp->common.blk, blk_perm | BLK_PERM_RESIZE, + blk_shared_perm, NULL); + if (ret < 0) { + return ret; + } + } + + ret = blk_truncate(exp->common.blk, size, true, prealloc, + truncate_flags, NULL); + + if (!exp->growable) { + /* Must succeed, because we are only giving up the RESIZE permission */ + blk_set_perm(exp->common.blk, blk_perm, blk_shared_perm, &error_abort); + } + + return ret; +} + +/** + * Let clients set file attributes. Only resizing is supported. + */ +static void fuse_setattr(fuse_req_t req, fuse_ino_t inode, struct stat *statbuf, + int to_set, struct fuse_file_info *fi) +{ + FuseExport *exp = fuse_req_userdata(req); + int ret; + + if (!exp->writable) { + fuse_reply_err(req, EACCES); + return; + } + + if (to_set & ~FUSE_SET_ATTR_SIZE) { + fuse_reply_err(req, ENOTSUP); + return; + } + + ret = fuse_do_truncate(exp, statbuf->st_size, true, PREALLOC_MODE_OFF); + if (ret < 0) { + fuse_reply_err(req, -ret); + return; + } + + fuse_getattr(req, inode, fi); +} + +/** + * Let clients open a file (i.e., the exported image). + */ +static void fuse_open(fuse_req_t req, fuse_ino_t inode, + struct fuse_file_info *fi) +{ + fuse_reply_open(req, fi); +} + +/** + * Handle client reads from the exported image. + */ +static void fuse_read(fuse_req_t req, fuse_ino_t inode, + size_t size, off_t offset, struct fuse_file_info *fi) +{ + FuseExport *exp = fuse_req_userdata(req); + int64_t length; + void *buf; + int ret; + + /* Limited by max_read, should not happen */ + if (size > FUSE_MAX_BOUNCE_BYTES) { + fuse_reply_err(req, EINVAL); + return; + } + + /** + * Clients will expect short reads at EOF, so we have to limit + * offset+size to the image length. + */ + length = blk_getlength(exp->common.blk); + if (length < 0) { + fuse_reply_err(req, -length); + return; + } + + if (offset + size > length) { + size = length - offset; + } + + buf = qemu_try_blockalign(blk_bs(exp->common.blk), size); + if (!buf) { + fuse_reply_err(req, ENOMEM); + return; + } + + ret = blk_pread(exp->common.blk, offset, buf, size); + if (ret >= 0) { + fuse_reply_buf(req, buf, size); + } else { + fuse_reply_err(req, -ret); + } + + qemu_vfree(buf); +} + +/** + * Handle client writes to the exported image. + */ +static void fuse_write(fuse_req_t req, fuse_ino_t inode, const char *buf, + size_t size, off_t offset, struct fuse_file_info *fi) +{ + FuseExport *exp = fuse_req_userdata(req); + int64_t length; + int ret; + + /* Limited by max_write, should not happen */ + if (size > BDRV_REQUEST_MAX_BYTES) { + fuse_reply_err(req, EINVAL); + return; + } + + if (!exp->writable) { + fuse_reply_err(req, EACCES); + return; + } + + /** + * Clients will expect short writes at EOF, so we have to limit + * offset+size to the image length. + */ + length = blk_getlength(exp->common.blk); + if (length < 0) { + fuse_reply_err(req, -length); + return; + } + + if (offset + size > length) { + if (exp->growable) { + ret = fuse_do_truncate(exp, offset + size, true, PREALLOC_MODE_OFF); + if (ret < 0) { + fuse_reply_err(req, -ret); + return; + } + } else { + size = length - offset; + } + } + + ret = blk_pwrite(exp->common.blk, offset, buf, size, 0); + if (ret >= 0) { + fuse_reply_write(req, size); + } else { + fuse_reply_err(req, -ret); + } +} + +/** + * Let clients perform various fallocate() operations. + */ +static void fuse_fallocate(fuse_req_t req, fuse_ino_t inode, int mode, + off_t offset, off_t length, + struct fuse_file_info *fi) +{ + FuseExport *exp = fuse_req_userdata(req); + int64_t blk_len; + int ret; + + if (!exp->writable) { + fuse_reply_err(req, EACCES); + return; + } + + blk_len = blk_getlength(exp->common.blk); + if (blk_len < 0) { + fuse_reply_err(req, -blk_len); + return; + } + + if (mode & FALLOC_FL_KEEP_SIZE) { + length = MIN(length, blk_len - offset); + } + + if (mode & FALLOC_FL_PUNCH_HOLE) { + if (!(mode & FALLOC_FL_KEEP_SIZE)) { + fuse_reply_err(req, EINVAL); + return; + } + + do { + int size = MIN(length, BDRV_REQUEST_MAX_BYTES); + + ret = blk_pdiscard(exp->common.blk, offset, size); + offset += size; + length -= size; + } while (ret == 0 && length > 0); + } else if (mode & FALLOC_FL_ZERO_RANGE) { + if (!(mode & FALLOC_FL_KEEP_SIZE) && offset + length > blk_len) { + /* No need for zeroes, we are going to write them ourselves */ + ret = fuse_do_truncate(exp, offset + length, false, + PREALLOC_MODE_OFF); + if (ret < 0) { + fuse_reply_err(req, -ret); + return; + } + } + + do { + int size = MIN(length, BDRV_REQUEST_MAX_BYTES); + + ret = blk_pwrite_zeroes(exp->common.blk, + offset, size, 0); + offset += size; + length -= size; + } while (ret == 0 && length > 0); + } else if (!mode) { + /* We can only fallocate at the EOF with a truncate */ + if (offset < blk_len) { + fuse_reply_err(req, EOPNOTSUPP); + return; + } + + if (offset > blk_len) { + /* No preallocation needed here */ + ret = fuse_do_truncate(exp, offset, true, PREALLOC_MODE_OFF); + if (ret < 0) { + fuse_reply_err(req, -ret); + return; + } + } + + ret = fuse_do_truncate(exp, offset + length, true, + PREALLOC_MODE_FALLOC); + } else { + ret = -EOPNOTSUPP; + } + + fuse_reply_err(req, ret < 0 ? -ret : 0); +} + +/** + * Let clients fsync the exported image. + */ +static void fuse_fsync(fuse_req_t req, fuse_ino_t inode, int datasync, + struct fuse_file_info *fi) +{ + FuseExport *exp = fuse_req_userdata(req); + int ret; + + ret = blk_flush(exp->common.blk); + fuse_reply_err(req, ret < 0 ? -ret : 0); +} + +/** + * Called before an FD to the exported image is closed. (libfuse + * notes this to be a way to return last-minute errors.) + */ +static void fuse_flush(fuse_req_t req, fuse_ino_t inode, + struct fuse_file_info *fi) +{ + fuse_fsync(req, inode, 1, fi); +} + +#ifdef CONFIG_FUSE_LSEEK +/** + * Let clients inquire allocation status. + */ +static void fuse_lseek(fuse_req_t req, fuse_ino_t inode, off_t offset, + int whence, struct fuse_file_info *fi) +{ + FuseExport *exp = fuse_req_userdata(req); + + if (whence != SEEK_HOLE && whence != SEEK_DATA) { + fuse_reply_err(req, EINVAL); + return; + } + + while (true) { + int64_t pnum; + int ret; + + ret = bdrv_block_status_above(blk_bs(exp->common.blk), NULL, + offset, INT64_MAX, &pnum, NULL, NULL); + if (ret < 0) { + fuse_reply_err(req, -ret); + return; + } + + if (!pnum && (ret & BDRV_BLOCK_EOF)) { + int64_t blk_len; + + /* + * If blk_getlength() rounds (e.g. by sectors), then the + * export length will be rounded, too. However, + * bdrv_block_status_above() may return EOF at unaligned + * offsets. We must not let this become visible and thus + * always simulate a hole between @offset (the real EOF) + * and @blk_len (the client-visible EOF). + */ + + blk_len = blk_getlength(exp->common.blk); + if (blk_len < 0) { + fuse_reply_err(req, -blk_len); + return; + } + + if (offset > blk_len || whence == SEEK_DATA) { + fuse_reply_err(req, ENXIO); + } else { + fuse_reply_lseek(req, offset); + } + return; + } + + if (ret & BDRV_BLOCK_DATA) { + if (whence == SEEK_DATA) { + fuse_reply_lseek(req, offset); + return; + } + } else { + if (whence == SEEK_HOLE) { + fuse_reply_lseek(req, offset); + return; + } + } + + /* Safety check against infinite loops */ + if (!pnum) { + fuse_reply_err(req, ENXIO); + return; + } + + offset += pnum; + } +} +#endif + +static const struct fuse_lowlevel_ops fuse_ops = { + .init = fuse_init, + .lookup = fuse_lookup, + .getattr = fuse_getattr, + .setattr = fuse_setattr, + .open = fuse_open, + .read = fuse_read, + .write = fuse_write, + .fallocate = fuse_fallocate, + .flush = fuse_flush, + .fsync = fuse_fsync, +#ifdef CONFIG_FUSE_LSEEK + .lseek = fuse_lseek, +#endif +}; + +const BlockExportDriver blk_exp_fuse = { + .type = BLOCK_EXPORT_TYPE_FUSE, + .instance_size = sizeof(FuseExport), + .create = fuse_export_create, + .delete = fuse_export_delete, + .request_shutdown = fuse_export_shutdown, +}; diff --git a/block/export/meson.build b/block/export/meson.build new file mode 100644 index 0000000000..0a08e384c7 --- /dev/null +++ b/block/export/meson.build @@ -0,0 +1,7 @@ +blockdev_ss.add(files('export.c')) + +if have_vhost_user_blk_server + blockdev_ss.add(files('vhost-user-blk-server.c')) +endif + +blockdev_ss.add(when: fuse, if_true: files('fuse.c')) diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c new file mode 100644 index 0000000000..fa06996d37 --- /dev/null +++ b/block/export/vhost-user-blk-server.c @@ -0,0 +1,526 @@ +/* + * Sharing QEMU block devices via vhost-user protocal + * + * Parts of the code based on nbd/server.c. + * + * Copyright (c) Coiby Xu . + * Copyright (c) 2020 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "block/block.h" +#include "subprojects/libvhost-user/libvhost-user.h" /* only for the type definitions */ +#include "standard-headers/linux/virtio_blk.h" +#include "qemu/vhost-user-server.h" +#include "vhost-user-blk-server.h" +#include "qapi/error.h" +#include "qom/object_interfaces.h" +#include "sysemu/block-backend.h" +#include "util/block-helpers.h" + +/* + * Sector units are 512 bytes regardless of the + * virtio_blk_config->blk_size value. + */ +#define VIRTIO_BLK_SECTOR_BITS 9 +#define VIRTIO_BLK_SECTOR_SIZE (1ull << VIRTIO_BLK_SECTOR_BITS) + +enum { + VHOST_USER_BLK_NUM_QUEUES_DEFAULT = 1, + VHOST_USER_BLK_MAX_DISCARD_SECTORS = 32768, + VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS = 32768, +}; +struct virtio_blk_inhdr { + unsigned char status; +}; + +typedef struct VuBlkReq { + VuVirtqElement elem; + int64_t sector_num; + size_t size; + struct virtio_blk_inhdr *in; + struct virtio_blk_outhdr out; + VuServer *server; + struct VuVirtq *vq; +} VuBlkReq; + +/* vhost user block device */ +typedef struct { + BlockExport export; + VuServer vu_server; + uint32_t blk_size; + QIOChannelSocket *sioc; + struct virtio_blk_config blkcfg; + bool writable; +} VuBlkExport; + +static void vu_blk_req_complete(VuBlkReq *req) +{ + VuDev *vu_dev = &req->server->vu_dev; + + /* IO size with 1 extra status byte */ + vu_queue_push(vu_dev, req->vq, &req->elem, req->size + 1); + vu_queue_notify(vu_dev, req->vq); + + free(req); +} + +static bool vu_blk_sect_range_ok(VuBlkExport *vexp, uint64_t sector, + size_t size) +{ + uint64_t nb_sectors = size >> BDRV_SECTOR_BITS; + uint64_t total_sectors; + + if (nb_sectors > BDRV_REQUEST_MAX_SECTORS) { + return false; + } + if ((sector << VIRTIO_BLK_SECTOR_BITS) % vexp->blk_size) { + return false; + } + blk_get_geometry(vexp->export.blk, &total_sectors); + if (sector > total_sectors || nb_sectors > total_sectors - sector) { + return false; + } + return true; +} + +static int coroutine_fn +vu_blk_discard_write_zeroes(VuBlkExport *vexp, struct iovec *iov, + uint32_t iovcnt, uint32_t type) +{ + BlockBackend *blk = vexp->export.blk; + struct virtio_blk_discard_write_zeroes desc; + ssize_t size; + uint64_t sector; + uint32_t num_sectors; + uint32_t max_sectors; + uint32_t flags; + int bytes; + + /* Only one desc is currently supported */ + if (unlikely(iov_size(iov, iovcnt) > sizeof(desc))) { + return VIRTIO_BLK_S_UNSUPP; + } + + size = iov_to_buf(iov, iovcnt, 0, &desc, sizeof(desc)); + if (unlikely(size != sizeof(desc))) { + error_report("Invalid size %zd, expected %zu", size, sizeof(desc)); + return VIRTIO_BLK_S_IOERR; + } + + sector = le64_to_cpu(desc.sector); + num_sectors = le32_to_cpu(desc.num_sectors); + flags = le32_to_cpu(desc.flags); + max_sectors = (type == VIRTIO_BLK_T_WRITE_ZEROES) ? + VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS : + VHOST_USER_BLK_MAX_DISCARD_SECTORS; + + /* This check ensures that 'bytes' fits in an int */ + if (unlikely(num_sectors > max_sectors)) { + return VIRTIO_BLK_S_IOERR; + } + + bytes = num_sectors << VIRTIO_BLK_SECTOR_BITS; + + if (unlikely(!vu_blk_sect_range_ok(vexp, sector, bytes))) { + return VIRTIO_BLK_S_IOERR; + } + + /* + * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for discard + * and write zeroes commands if any unknown flag is set. + */ + if (unlikely(flags & ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) { + return VIRTIO_BLK_S_UNSUPP; + } + + if (type == VIRTIO_BLK_T_WRITE_ZEROES) { + int blk_flags = 0; + + if (flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP) { + blk_flags |= BDRV_REQ_MAY_UNMAP; + } + + if (blk_co_pwrite_zeroes(blk, sector << VIRTIO_BLK_SECTOR_BITS, + bytes, blk_flags) == 0) { + return VIRTIO_BLK_S_OK; + } + } else if (type == VIRTIO_BLK_T_DISCARD) { + /* + * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for + * discard commands if the unmap flag is set. + */ + if (unlikely(flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) { + return VIRTIO_BLK_S_UNSUPP; + } + + if (blk_co_pdiscard(blk, sector << VIRTIO_BLK_SECTOR_BITS, + bytes) == 0) { + return VIRTIO_BLK_S_OK; + } + } + + return VIRTIO_BLK_S_IOERR; +} + +static void coroutine_fn vu_blk_virtio_process_req(void *opaque) +{ + VuBlkReq *req = opaque; + VuServer *server = req->server; + VuVirtqElement *elem = &req->elem; + uint32_t type; + + VuBlkExport *vexp = container_of(server, VuBlkExport, vu_server); + BlockBackend *blk = vexp->export.blk; + + struct iovec *in_iov = elem->in_sg; + struct iovec *out_iov = elem->out_sg; + unsigned in_num = elem->in_num; + unsigned out_num = elem->out_num; + + /* refer to hw/block/virtio_blk.c */ + if (elem->out_num < 1 || elem->in_num < 1) { + error_report("virtio-blk request missing headers"); + goto err; + } + + if (unlikely(iov_to_buf(out_iov, out_num, 0, &req->out, + sizeof(req->out)) != sizeof(req->out))) { + error_report("virtio-blk request outhdr too short"); + goto err; + } + + iov_discard_front(&out_iov, &out_num, sizeof(req->out)); + + if (in_iov[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) { + error_report("virtio-blk request inhdr too short"); + goto err; + } + + /* We always touch the last byte, so just see how big in_iov is. */ + req->in = (void *)in_iov[in_num - 1].iov_base + + in_iov[in_num - 1].iov_len + - sizeof(struct virtio_blk_inhdr); + iov_discard_back(in_iov, &in_num, sizeof(struct virtio_blk_inhdr)); + + type = le32_to_cpu(req->out.type); + switch (type & ~VIRTIO_BLK_T_BARRIER) { + case VIRTIO_BLK_T_IN: + case VIRTIO_BLK_T_OUT: { + QEMUIOVector qiov; + int64_t offset; + ssize_t ret = 0; + bool is_write = type & VIRTIO_BLK_T_OUT; + req->sector_num = le64_to_cpu(req->out.sector); + + if (is_write && !vexp->writable) { + req->in->status = VIRTIO_BLK_S_IOERR; + break; + } + + if (is_write) { + qemu_iovec_init_external(&qiov, out_iov, out_num); + } else { + qemu_iovec_init_external(&qiov, in_iov, in_num); + } + + if (unlikely(!vu_blk_sect_range_ok(vexp, + req->sector_num, + qiov.size))) { + req->in->status = VIRTIO_BLK_S_IOERR; + break; + } + + offset = req->sector_num << VIRTIO_BLK_SECTOR_BITS; + + if (is_write) { + ret = blk_co_pwritev(blk, offset, qiov.size, &qiov, 0); + } else { + ret = blk_co_preadv(blk, offset, qiov.size, &qiov, 0); + } + if (ret >= 0) { + req->in->status = VIRTIO_BLK_S_OK; + } else { + req->in->status = VIRTIO_BLK_S_IOERR; + } + break; + } + case VIRTIO_BLK_T_FLUSH: + if (blk_co_flush(blk) == 0) { + req->in->status = VIRTIO_BLK_S_OK; + } else { + req->in->status = VIRTIO_BLK_S_IOERR; + } + break; + case VIRTIO_BLK_T_GET_ID: { + size_t size = MIN(iov_size(&elem->in_sg[0], in_num), + VIRTIO_BLK_ID_BYTES); + snprintf(elem->in_sg[0].iov_base, size, "%s", "vhost_user_blk"); + req->in->status = VIRTIO_BLK_S_OK; + req->size = elem->in_sg[0].iov_len; + break; + } + case VIRTIO_BLK_T_DISCARD: + case VIRTIO_BLK_T_WRITE_ZEROES: { + if (!vexp->writable) { + req->in->status = VIRTIO_BLK_S_IOERR; + break; + } + + req->in->status = vu_blk_discard_write_zeroes(vexp, out_iov, out_num, + type); + break; + } + default: + req->in->status = VIRTIO_BLK_S_UNSUPP; + break; + } + + vu_blk_req_complete(req); + return; + +err: + free(req); +} + +static void vu_blk_process_vq(VuDev *vu_dev, int idx) +{ + VuServer *server = container_of(vu_dev, VuServer, vu_dev); + VuVirtq *vq = vu_get_queue(vu_dev, idx); + + while (1) { + VuBlkReq *req; + + req = vu_queue_pop(vu_dev, vq, sizeof(VuBlkReq)); + if (!req) { + break; + } + + req->server = server; + req->vq = vq; + + Coroutine *co = + qemu_coroutine_create(vu_blk_virtio_process_req, req); + qemu_coroutine_enter(co); + } +} + +static void vu_blk_queue_set_started(VuDev *vu_dev, int idx, bool started) +{ + VuVirtq *vq; + + assert(vu_dev); + + vq = vu_get_queue(vu_dev, idx); + vu_set_queue_handler(vu_dev, vq, started ? vu_blk_process_vq : NULL); +} + +static uint64_t vu_blk_get_features(VuDev *dev) +{ + uint64_t features; + VuServer *server = container_of(dev, VuServer, vu_dev); + VuBlkExport *vexp = container_of(server, VuBlkExport, vu_server); + features = 1ull << VIRTIO_BLK_F_SIZE_MAX | + 1ull << VIRTIO_BLK_F_SEG_MAX | + 1ull << VIRTIO_BLK_F_TOPOLOGY | + 1ull << VIRTIO_BLK_F_BLK_SIZE | + 1ull << VIRTIO_BLK_F_FLUSH | + 1ull << VIRTIO_BLK_F_DISCARD | + 1ull << VIRTIO_BLK_F_WRITE_ZEROES | + 1ull << VIRTIO_BLK_F_CONFIG_WCE | + 1ull << VIRTIO_BLK_F_MQ | + 1ull << VIRTIO_F_VERSION_1 | + 1ull << VIRTIO_RING_F_INDIRECT_DESC | + 1ull << VIRTIO_RING_F_EVENT_IDX | + 1ull << VHOST_USER_F_PROTOCOL_FEATURES; + + if (!vexp->writable) { + features |= 1ull << VIRTIO_BLK_F_RO; + } + + return features; +} + +static uint64_t vu_blk_get_protocol_features(VuDev *dev) +{ + return 1ull << VHOST_USER_PROTOCOL_F_CONFIG; +} + +static int +vu_blk_get_config(VuDev *vu_dev, uint8_t *config, uint32_t len) +{ + VuServer *server = container_of(vu_dev, VuServer, vu_dev); + VuBlkExport *vexp = container_of(server, VuBlkExport, vu_server); + + if (len > sizeof(struct virtio_blk_config)) { + return -1; + } + + memcpy(config, &vexp->blkcfg, len); + return 0; +} + +static int +vu_blk_set_config(VuDev *vu_dev, const uint8_t *data, + uint32_t offset, uint32_t size, uint32_t flags) +{ + VuServer *server = container_of(vu_dev, VuServer, vu_dev); + VuBlkExport *vexp = container_of(server, VuBlkExport, vu_server); + uint8_t wce; + + /* don't support live migration */ + if (flags != VHOST_SET_CONFIG_TYPE_MASTER) { + return -EINVAL; + } + + if (offset != offsetof(struct virtio_blk_config, wce) || + size != 1) { + return -EINVAL; + } + + wce = *data; + vexp->blkcfg.wce = wce; + blk_set_enable_write_cache(vexp->export.blk, wce); + return 0; +} + +/* + * When the client disconnects, it sends a VHOST_USER_NONE request + * and vu_process_message will simple call exit which cause the VM + * to exit abruptly. + * To avoid this issue, process VHOST_USER_NONE request ahead + * of vu_process_message. + * + */ +static int vu_blk_process_msg(VuDev *dev, VhostUserMsg *vmsg, int *do_reply) +{ + if (vmsg->request == VHOST_USER_NONE) { + dev->panic(dev, "disconnect"); + return true; + } + return false; +} + +static const VuDevIface vu_blk_iface = { + .get_features = vu_blk_get_features, + .queue_set_started = vu_blk_queue_set_started, + .get_protocol_features = vu_blk_get_protocol_features, + .get_config = vu_blk_get_config, + .set_config = vu_blk_set_config, + .process_msg = vu_blk_process_msg, +}; + +static void blk_aio_attached(AioContext *ctx, void *opaque) +{ + VuBlkExport *vexp = opaque; + + vexp->export.ctx = ctx; + vhost_user_server_attach_aio_context(&vexp->vu_server, ctx); +} + +static void blk_aio_detach(void *opaque) +{ + VuBlkExport *vexp = opaque; + + vhost_user_server_detach_aio_context(&vexp->vu_server); + vexp->export.ctx = NULL; +} + +static void +vu_blk_initialize_config(BlockDriverState *bs, + struct virtio_blk_config *config, + uint32_t blk_size, + uint16_t num_queues) +{ + config->capacity = + cpu_to_le64(bdrv_getlength(bs) >> VIRTIO_BLK_SECTOR_BITS); + config->blk_size = cpu_to_le32(blk_size); + config->size_max = cpu_to_le32(0); + config->seg_max = cpu_to_le32(128 - 2); + config->min_io_size = cpu_to_le16(1); + config->opt_io_size = cpu_to_le32(1); + config->num_queues = cpu_to_le16(num_queues); + config->max_discard_sectors = + cpu_to_le32(VHOST_USER_BLK_MAX_DISCARD_SECTORS); + config->max_discard_seg = cpu_to_le32(1); + config->discard_sector_alignment = + cpu_to_le32(blk_size >> VIRTIO_BLK_SECTOR_BITS); + config->max_write_zeroes_sectors + = cpu_to_le32(VHOST_USER_BLK_MAX_WRITE_ZEROES_SECTORS); + config->max_write_zeroes_seg = cpu_to_le32(1); +} + +static void vu_blk_exp_request_shutdown(BlockExport *exp) +{ + VuBlkExport *vexp = container_of(exp, VuBlkExport, export); + + vhost_user_server_stop(&vexp->vu_server); +} + +static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts, + Error **errp) +{ + VuBlkExport *vexp = container_of(exp, VuBlkExport, export); + BlockExportOptionsVhostUserBlk *vu_opts = &opts->u.vhost_user_blk; + Error *local_err = NULL; + uint64_t logical_block_size; + uint16_t num_queues = VHOST_USER_BLK_NUM_QUEUES_DEFAULT; + + vexp->writable = opts->writable; + vexp->blkcfg.wce = 0; + + if (vu_opts->has_logical_block_size) { + logical_block_size = vu_opts->logical_block_size; + } else { + logical_block_size = VIRTIO_BLK_SECTOR_SIZE; + } + check_block_size(exp->id, "logical-block-size", logical_block_size, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return -EINVAL; + } + vexp->blk_size = logical_block_size; + blk_set_guest_block_size(exp->blk, logical_block_size); + + if (vu_opts->has_num_queues) { + num_queues = vu_opts->num_queues; + } + if (num_queues == 0) { + error_setg(errp, "num-queues must be greater than 0"); + return -EINVAL; + } + + vu_blk_initialize_config(blk_bs(exp->blk), &vexp->blkcfg, + logical_block_size, num_queues); + + blk_add_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach, + vexp); + + if (!vhost_user_server_start(&vexp->vu_server, vu_opts->addr, exp->ctx, + num_queues, &vu_blk_iface, errp)) { + blk_remove_aio_context_notifier(exp->blk, blk_aio_attached, + blk_aio_detach, vexp); + return -EADDRNOTAVAIL; + } + + return 0; +} + +static void vu_blk_exp_delete(BlockExport *exp) +{ + VuBlkExport *vexp = container_of(exp, VuBlkExport, export); + + blk_remove_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach, + vexp); +} + +const BlockExportDriver blk_exp_vhost_user_blk = { + .type = BLOCK_EXPORT_TYPE_VHOST_USER_BLK, + .instance_size = sizeof(VuBlkExport), + .create = vu_blk_exp_create, + .delete = vu_blk_exp_delete, + .request_shutdown = vu_blk_exp_request_shutdown, +}; diff --git a/block/export/vhost-user-blk-server.h b/block/export/vhost-user-blk-server.h new file mode 100644 index 0000000000..fcf46fc8a5 --- /dev/null +++ b/block/export/vhost-user-blk-server.h @@ -0,0 +1,19 @@ +/* + * Sharing QEMU block devices via vhost-user protocal + * + * Copyright (c) Coiby Xu . + * Copyright (c) 2020 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#ifndef VHOST_USER_BLK_SERVER_H +#define VHOST_USER_BLK_SERVER_H + +#include "block/export.h" + +/* For block/export/export.c */ +extern const BlockExportDriver blk_exp_vhost_user_blk; + +#endif /* VHOST_USER_BLK_SERVER_H */ diff --git a/block/file-posix.c b/block/file-posix.c index 9a00d4190a..20e14f8e96 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -216,6 +216,20 @@ typedef struct RawPosixAIOData { static int cdrom_reopen(BlockDriverState *bs); #endif +/* + * Elide EAGAIN and EACCES details when failing to lock, as this + * indicates that the specified file region is already locked by + * another process, which is considered a common scenario. + */ +#define raw_lock_error_setg_errno(errp, err, fmt, ...) \ + do { \ + if ((err) == EAGAIN || (err) == EACCES) { \ + error_setg((errp), (fmt), ## __VA_ARGS__); \ + } else { \ + error_setg_errno((errp), (err), (fmt), ## __VA_ARGS__); \ + } \ + } while (0) + #if defined(__NetBSD__) static int raw_normalize_devicepath(const char **filename, Error **errp) { @@ -630,11 +644,10 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, raw_parse_flags(bdrv_flags, &s->open_flags, false); s->fd = -1; - fd = qemu_open(filename, s->open_flags, 0644); + fd = qemu_open(filename, s->open_flags, errp); ret = fd < 0 ? -errno : 0; if (ret < 0) { - error_setg_file_open(errp, -ret, filename); if (ret == -EROFS) { ret = -EACCES; } @@ -706,15 +719,9 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, } if (!device) { - if (S_ISBLK(st.st_mode)) { - warn_report("Opening a block device as a file using the '%s' " - "driver is deprecated", bs->drv->format_name); - } else if (S_ISCHR(st.st_mode)) { - warn_report("Opening a character device as a file using the '%s' " - "driver is deprecated", bs->drv->format_name); - } else if (!S_ISREG(st.st_mode)) { - error_setg(errp, "A regular file was expected by the '%s' driver, " - "but something else was given", bs->drv->format_name); + if (!S_ISREG(st.st_mode)) { + error_setg(errp, "'%s' driver requires '%s' to be a regular file", + bs->drv->format_name, bs->filename); ret = -EINVAL; goto fail; } else { @@ -723,8 +730,9 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, } } else { if (!(S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))) { - error_setg(errp, "'%s' driver expects either " - "a character or block device", bs->drv->format_name); + error_setg(errp, "'%s' driver requires '%s' to be either " + "a character or block device", + bs->drv->format_name, bs->filename); ret = -EINVAL; goto fail; } @@ -837,7 +845,8 @@ static int raw_apply_lock_bytes(BDRVRawState *s, int fd, if ((perm_lock_bits & bit) && !(locked_perm & bit)) { ret = qemu_lock_fd(fd, off, 1, false); if (ret) { - error_setg(errp, "Failed to lock byte %d", off); + raw_lock_error_setg_errno(errp, -ret, "Failed to lock byte %d", + off); return ret; } else if (s) { s->locked_perm |= bit; @@ -845,7 +854,7 @@ static int raw_apply_lock_bytes(BDRVRawState *s, int fd, } else if (unlock && (locked_perm & bit) && !(perm_lock_bits & bit)) { ret = qemu_unlock_fd(fd, off, 1); if (ret) { - error_setg(errp, "Failed to unlock byte %d", off); + error_setg_errno(errp, -ret, "Failed to unlock byte %d", off); return ret; } else if (s) { s->locked_perm &= ~bit; @@ -858,7 +867,8 @@ static int raw_apply_lock_bytes(BDRVRawState *s, int fd, if ((shared_perm_lock_bits & bit) && !(locked_shared_perm & bit)) { ret = qemu_lock_fd(fd, off, 1, false); if (ret) { - error_setg(errp, "Failed to lock byte %d", off); + raw_lock_error_setg_errno(errp, -ret, "Failed to lock byte %d", + off); return ret; } else if (s) { s->locked_shared_perm |= bit; @@ -867,7 +877,7 @@ static int raw_apply_lock_bytes(BDRVRawState *s, int fd, !(shared_perm_lock_bits & bit)) { ret = qemu_unlock_fd(fd, off, 1); if (ret) { - error_setg(errp, "Failed to unlock byte %d", off); + error_setg_errno(errp, -ret, "Failed to unlock byte %d", off); return ret; } else if (s) { s->locked_shared_perm &= ~bit; @@ -891,9 +901,10 @@ static int raw_check_lock_bytes(int fd, uint64_t perm, uint64_t shared_perm, ret = qemu_lock_fd_test(fd, off, 1, true); if (ret) { char *perm_name = bdrv_perm_names(p); - error_setg(errp, - "Failed to get \"%s\" lock", - perm_name); + + raw_lock_error_setg_errno(errp, -ret, + "Failed to get \"%s\" lock", + perm_name); g_free(perm_name); return ret; } @@ -906,9 +917,10 @@ static int raw_check_lock_bytes(int fd, uint64_t perm, uint64_t shared_perm, ret = qemu_lock_fd_test(fd, off, 1, true); if (ret) { char *perm_name = bdrv_perm_names(p); - error_setg(errp, - "Failed to get shared \"%s\" lock", - perm_name); + + raw_lock_error_setg_errno(errp, -ret, + "Failed to get shared \"%s\" lock", + perm_name); g_free(perm_name); return ret; } @@ -1037,10 +1049,8 @@ static int raw_reconfigure_getfd(BlockDriverState *bs, int flags, const char *normalized_filename = bs->filename; ret = raw_normalize_devicepath(&normalized_filename, errp); if (ret >= 0) { - assert(!(*open_flags & O_CREAT)); - fd = qemu_open(normalized_filename, *open_flags); + fd = qemu_open(normalized_filename, *open_flags, errp); if (fd == -1) { - error_setg_errno(errp, errno, "Could not reopen file"); return -1; } } @@ -1701,6 +1711,7 @@ static int handle_aiocb_write_zeroes_unmap(void *opaque) switch (ret) { case -ENOTSUP: case -EINVAL: + case -EBUSY: break; default: return ret; @@ -2113,7 +2124,7 @@ static void raw_aio_attach_aio_context(BlockDriverState *bs, #endif #ifdef CONFIG_LINUX_IO_URING if (s->use_linux_io_uring) { - Error *local_err; + Error *local_err = NULL; if (!aio_setup_linux_io_uring(new_context, &local_err)) { error_reportf_err(local_err, "Unable to use linux io_uring, " "falling back to thread pool: "); @@ -2411,10 +2422,9 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) } /* Create file */ - fd = qemu_open(file_opts->filename, O_RDWR | O_CREAT | O_BINARY, 0644); + fd = qemu_create(file_opts->filename, O_RDWR | O_BINARY, 0644, errp); if (fd < 0) { result = -errno; - error_setg_errno(errp, -result, "Could not create file"); goto out; } @@ -2929,7 +2939,6 @@ raw_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, #ifdef CONFIG_FALLOCATE if (offset + bytes > bs->total_sectors * BDRV_SECTOR_SIZE) { BdrvTrackedRequest *req; - uint64_t end; /* * This is a workaround for a bug in the Linux XFS driver, @@ -2953,11 +2962,11 @@ raw_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, assert(req->offset <= offset); assert(req->offset + req->bytes >= offset + bytes); - end = INT64_MAX & -(uint64_t)bs->bl.request_alignment; - req->bytes = end - req->offset; - req->overlap_bytes = req->bytes; + req->bytes = BDRV_MAX_LENGTH - req->offset; - bdrv_mark_request_serialising(req, bs->bl.request_alignment); + bdrv_check_request(req->offset, req->bytes, &error_abort); + + bdrv_make_request_serialising(req, bs->bl.request_alignment); } #endif @@ -3107,7 +3116,7 @@ static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared, } /* Copy locks to the new fd */ - if (s->perm_change_fd) { + if (s->perm_change_fd && s->use_lock) { ret = raw_apply_lock_bytes(NULL, s->perm_change_fd, perm, ~shared, false, errp); if (ret < 0) { @@ -3335,7 +3344,7 @@ static bool setup_cdrom(char *bsd_path, Error **errp) for (index = 0; index < num_of_test_partitions; index++) { snprintf(test_partition, sizeof(test_partition), "%ss%d", bsd_path, index); - fd = qemu_open(test_partition, O_RDONLY | O_BINARY | O_LARGEFILE); + fd = qemu_open(test_partition, O_RDONLY | O_BINARY | O_LARGEFILE, NULL); if (fd >= 0) { partition_found = true; qemu_close(fd); @@ -3653,7 +3662,7 @@ static int cdrom_probe_device(const char *filename) int prio = 0; struct stat st; - fd = qemu_open(filename, O_RDONLY | O_NONBLOCK); + fd = qemu_open(filename, O_RDONLY | O_NONBLOCK, NULL); if (fd < 0) { goto out; } @@ -3787,7 +3796,7 @@ static int cdrom_reopen(BlockDriverState *bs) */ if (s->fd >= 0) qemu_close(s->fd); - fd = qemu_open(bs->filename, s->open_flags, 0644); + fd = qemu_open(bs->filename, s->open_flags, NULL); if (fd < 0) { s->fd = -1; return -EIO; diff --git a/block/file-win32.c b/block/file-win32.c index ab69bd811a..2642088bd6 100644 --- a/block/file-win32.c +++ b/block/file-win32.c @@ -299,6 +299,11 @@ static QemuOptsList raw_runtime_opts = { .type = QEMU_OPT_STRING, .help = "host AIO implementation (threads, native)", }, + { + .name = "locking", + .type = QEMU_OPT_STRING, + .help = "file locking mode (on/off/auto, default: auto)", + }, { /* end of list */ } }, }; @@ -333,6 +338,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, Error *local_err = NULL; const char *filename; bool use_aio; + OnOffAuto locking; int ret; s->type = FTYPE_FILE; @@ -343,10 +349,24 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - if (qdict_get_try_bool(options, "locking", false)) { + locking = qapi_enum_parse(&OnOffAuto_lookup, + qemu_opt_get(opts, "locking"), + ON_OFF_AUTO_AUTO, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; + } + switch (locking) { + case ON_OFF_AUTO_ON: error_setg(errp, "locking=on is not supported on Windows"); ret = -EINVAL; goto fail; + case ON_OFF_AUTO_OFF: + case ON_OFF_AUTO_AUTO: + break; + default: + g_assert_not_reached(); } filename = qemu_opt_get(opts, "filename"); @@ -576,10 +596,9 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp) return -EINVAL; } - fd = qemu_open(file_opts->filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, - 0644); + fd = qemu_create(file_opts->filename, O_WRONLY | O_TRUNC | O_BINARY, + 0644, errp); if (fd < 0) { - error_setg_errno(errp, errno, "Could not create file"); return -EIO; } set_sparse(fd); diff --git a/block/filter-compress.c b/block/filter-compress.c index 8ec1991c1f..5136371bf8 100644 --- a/block/filter-compress.c +++ b/block/filter-compress.c @@ -146,8 +146,6 @@ static BlockDriver bdrv_compress = { .bdrv_eject = compress_eject, .bdrv_lock_medium = compress_lock_medium, - .bdrv_co_block_status = bdrv_co_block_status_from_file, - .has_variable_length = true, .is_filter = true, }; diff --git a/block/gluster.c b/block/gluster.c index 4f1448e2bc..e8ee14c8e9 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -359,8 +359,8 @@ static int qemu_gluster_parse_uri(BlockdevOptionsGluster *gconf, return -EINVAL; } - gconf->server = g_new0(SocketAddressList, 1); - gconf->server->value = gsconf = g_new0(SocketAddress, 1); + gsconf = g_new0(SocketAddress, 1); + QAPI_LIST_PREPEND(gconf->server, gsconf); /* transport */ if (!uri->scheme || !strcmp(uri->scheme, "gluster")) { @@ -514,7 +514,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf, { QemuOpts *opts; SocketAddress *gsconf = NULL; - SocketAddressList *curr = NULL; + SocketAddressList **tail; QDict *backing_options = NULL; Error *local_err = NULL; char *str = NULL; @@ -547,6 +547,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf, } gconf->path = g_strdup(ptr); qemu_opts_del(opts); + tail = &gconf->server; for (i = 0; i < num_servers; i++) { str = g_strdup_printf(GLUSTER_OPT_SERVER_PATTERN"%d.", i); @@ -655,15 +656,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf, qemu_opts_del(opts); } - if (gconf->server == NULL) { - gconf->server = g_new0(SocketAddressList, 1); - gconf->server->value = gsconf; - curr = gconf->server; - } else { - curr->next = g_new0(SocketAddressList, 1); - curr->next->value = gsconf; - curr = curr->next; - } + QAPI_LIST_APPEND(tail, gsconf); gsconf = NULL; qobject_unref(backing_options); diff --git a/block/io.c b/block/io.c index ad3a51ed53..ca2dca3007 100644 --- a/block/io.c +++ b/block/io.c @@ -29,6 +29,7 @@ #include "block/blockjob.h" #include "block/blockjob_int.h" #include "block/block_int.h" +#include "block/coroutines.h" #include "qemu/cutils.h" #include "qapi/error.h" #include "qemu/error-report.h" @@ -40,7 +41,7 @@ static void bdrv_parent_cb_resize(BlockDriverState *bs); static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int bytes, BdrvRequestFlags flags); + int64_t offset, int64_t bytes, BdrvRequestFlags flags); static void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore, bool ignore_bds_parents) @@ -69,7 +70,7 @@ void bdrv_parent_drained_end_single(BdrvChild *c) { int drained_end_counter = 0; bdrv_parent_drained_end_single_no_poll(c, &drained_end_counter); - BDRV_POLL_WHILE(c->bs, atomic_read(&drained_end_counter) > 0); + BDRV_POLL_WHILE(c->bs, qatomic_read(&drained_end_counter) > 0); } static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore, @@ -134,8 +135,10 @@ static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src) void bdrv_refresh_limits(BlockDriverState *bs, Error **errp) { + ERRP_GUARD(); BlockDriver *drv = bs->drv; - Error *local_err = NULL; + BdrvChild *c; + bool have_limits; memset(&bs->bl, 0, sizeof(bs->bl)); @@ -149,14 +152,20 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp) drv->bdrv_co_preadv_part) ? 1 : 512; /* Take some limits from the children as a default */ - if (bs->file) { - bdrv_refresh_limits(bs->file->bs, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; + have_limits = false; + QLIST_FOREACH(c, &bs->children, next) { + if (c->role & (BDRV_CHILD_DATA | BDRV_CHILD_FILTERED | BDRV_CHILD_COW)) + { + bdrv_refresh_limits(c->bs, errp); + if (*errp) { + return; + } + bdrv_merge_limits(&bs->bl, &c->bs->bl); + have_limits = true; } - bdrv_merge_limits(&bs->bl, &bs->file->bs->bl); - } else { + } + + if (!have_limits) { bs->bl.min_mem_alignment = 512; bs->bl.opt_mem_alignment = qemu_real_host_page_size; @@ -164,18 +173,16 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp) bs->bl.max_iov = IOV_MAX; } - if (bs->backing) { - bdrv_refresh_limits(bs->backing->bs, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - bdrv_merge_limits(&bs->bl, &bs->backing->bs->bl); - } - /* Then let the driver override it */ if (drv->bdrv_refresh_limits) { drv->bdrv_refresh_limits(bs, errp); + if (*errp) { + return; + } + } + + if (bs->bl.request_alignment > BDRV_MAX_ALIGNMENT) { + error_setg(errp, "Driver requires too large request alignment"); } } @@ -186,12 +193,12 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp) */ void bdrv_enable_copy_on_read(BlockDriverState *bs) { - atomic_inc(&bs->copy_on_read); + qatomic_inc(&bs->copy_on_read); } void bdrv_disable_copy_on_read(BlockDriverState *bs) { - int old = atomic_fetch_dec(&bs->copy_on_read); + int old = qatomic_fetch_dec(&bs->copy_on_read); assert(old >= 1); } @@ -219,9 +226,9 @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque) } /* Set data->done and decrement drained_end_counter before bdrv_wakeup() */ - atomic_mb_set(&data->done, true); + qatomic_mb_set(&data->done, true); if (!data->begin) { - atomic_dec(data->drained_end_counter); + qatomic_dec(data->drained_end_counter); } bdrv_dec_in_flight(bs); @@ -248,7 +255,7 @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin, }; if (!begin) { - atomic_inc(drained_end_counter); + qatomic_inc(drained_end_counter); } /* Make sure the driver callback completes during the polling phase for @@ -268,7 +275,7 @@ bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, return true; } - if (atomic_read(&bs->in_flight)) { + if (qatomic_read(&bs->in_flight)) { return true; } @@ -305,17 +312,7 @@ static void bdrv_co_drain_bh_cb(void *opaque) if (bs) { AioContext *ctx = bdrv_get_aio_context(bs); - AioContext *co_ctx = qemu_coroutine_get_aio_context(co); - - /* - * When the coroutine yielded, the lock for its home context was - * released, so we need to re-acquire it here. If it explicitly - * acquired a different context, the lock is still held and we don't - * want to lock it a second time (or AIO_WAIT_WHILE() would hang). - */ - if (ctx == co_ctx) { - aio_context_acquire(ctx); - } + aio_context_acquire(ctx); bdrv_dec_in_flight(bs); if (data->begin) { assert(!data->drained_end_counter); @@ -327,9 +324,7 @@ static void bdrv_co_drain_bh_cb(void *opaque) data->ignore_bds_parents, data->drained_end_counter); } - if (ctx == co_ctx) { - aio_context_release(ctx); - } + aio_context_release(ctx); } else { assert(data->begin); bdrv_drain_all_begin(); @@ -347,13 +342,16 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, int *drained_end_counter) { BdrvCoDrainData data; + Coroutine *self = qemu_coroutine_self(); + AioContext *ctx = bdrv_get_aio_context(bs); + AioContext *co_ctx = qemu_coroutine_get_aio_context(self); /* Calling bdrv_drain() from a BH ensures the current coroutine yields and * other coroutines run if they were queued by aio_co_enter(). */ assert(qemu_in_coroutine()); data = (BdrvCoDrainData) { - .co = qemu_coroutine_self(), + .co = self, .bs = bs, .done = false, .begin = begin, @@ -367,13 +365,29 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, if (bs) { bdrv_inc_in_flight(bs); } - replay_bh_schedule_oneshot_event(bdrv_get_aio_context(bs), - bdrv_co_drain_bh_cb, &data); + + /* + * Temporarily drop the lock across yield or we would get deadlocks. + * bdrv_co_drain_bh_cb() reaquires the lock as needed. + * + * When we yield below, the lock for the current context will be + * released, so if this is actually the lock that protects bs, don't drop + * it a second time. + */ + if (ctx != co_ctx) { + aio_context_release(ctx); + } + replay_bh_schedule_oneshot_event(ctx, bdrv_co_drain_bh_cb, &data); qemu_coroutine_yield(); /* If we are resumed from some other event (such as an aio completion or a * timer callback), it is a bug in the caller that should be fixed. */ assert(data.done); + + /* Reaquire the AioContext of bs if we dropped it */ + if (ctx != co_ctx) { + aio_context_acquire(ctx); + } } void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, @@ -382,7 +396,7 @@ void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, assert(!qemu_in_coroutine()); /* Stop things in parent-to-child order */ - if (atomic_fetch_inc(&bs->quiesce_counter) == 0) { + if (qatomic_fetch_inc(&bs->quiesce_counter) == 0) { aio_disable_external(bdrv_get_aio_context(bs)); } @@ -473,7 +487,7 @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, bdrv_parent_drained_end(bs, parent, ignore_bds_parents, drained_end_counter); - old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter); + old_quiesce_counter = qatomic_fetch_dec(&bs->quiesce_counter); if (old_quiesce_counter == 1) { aio_enable_external(bdrv_get_aio_context(bs)); } @@ -492,7 +506,7 @@ void bdrv_drained_end(BlockDriverState *bs) { int drained_end_counter = 0; bdrv_do_drained_end(bs, false, NULL, false, &drained_end_counter); - BDRV_POLL_WHILE(bs, atomic_read(&drained_end_counter) > 0); + BDRV_POLL_WHILE(bs, qatomic_read(&drained_end_counter) > 0); } void bdrv_drained_end_no_poll(BlockDriverState *bs, int *drained_end_counter) @@ -504,7 +518,7 @@ void bdrv_subtree_drained_end(BlockDriverState *bs) { int drained_end_counter = 0; bdrv_do_drained_end(bs, true, NULL, false, &drained_end_counter); - BDRV_POLL_WHILE(bs, atomic_read(&drained_end_counter) > 0); + BDRV_POLL_WHILE(bs, qatomic_read(&drained_end_counter) > 0); } void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent) @@ -526,7 +540,7 @@ void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent) &drained_end_counter); } - BDRV_POLL_WHILE(child->bs, atomic_read(&drained_end_counter) > 0); + BDRV_POLL_WHILE(child->bs, qatomic_read(&drained_end_counter) > 0); } /* @@ -553,7 +567,7 @@ static void bdrv_drain_assert_idle(BlockDriverState *bs) { BdrvChild *child, *next; - assert(atomic_read(&bs->in_flight) == 0); + assert(qatomic_read(&bs->in_flight) == 0); QLIST_FOREACH_SAFE(child, &bs->children, next, next) { bdrv_drain_assert_idle(child->bs); } @@ -632,6 +646,19 @@ void bdrv_drain_all_begin(void) } } +void bdrv_drain_all_end_quiesce(BlockDriverState *bs) +{ + int drained_end_counter = 0; + + g_assert(bs->quiesce_counter > 0); + g_assert(!bs->refcnt); + + while (bs->quiesce_counter) { + bdrv_do_drained_end(bs, false, NULL, true, &drained_end_counter); + } + BDRV_POLL_WHILE(bs, qatomic_read(&drained_end_counter) > 0); +} + void bdrv_drain_all_end(void) { BlockDriverState *bs = NULL; @@ -655,7 +682,7 @@ void bdrv_drain_all_end(void) } assert(qemu_get_current_aio_context() == qemu_get_aio_context()); - AIO_WAIT_WHILE(NULL, atomic_read(&drained_end_counter) > 0); + AIO_WAIT_WHILE(NULL, qatomic_read(&drained_end_counter) > 0); assert(bdrv_drain_all_count > 0); bdrv_drain_all_count--; @@ -675,7 +702,7 @@ void bdrv_drain_all(void) static void tracked_request_end(BdrvTrackedRequest *req) { if (req->serialising) { - atomic_dec(&req->bs->serialising_in_flight); + qatomic_dec(&req->bs->serialising_in_flight); } qemu_co_mutex_lock(&req->bs->reqs_lock); @@ -690,10 +717,10 @@ static void tracked_request_end(BdrvTrackedRequest *req) static void tracked_request_begin(BdrvTrackedRequest *req, BlockDriverState *bs, int64_t offset, - uint64_t bytes, + int64_t bytes, enum BdrvTrackedRequestType type) { - assert(bytes <= INT64_MAX && offset <= INT64_MAX - bytes); + bdrv_check_request(offset, bytes, &error_abort); *req = (BdrvTrackedRequest){ .bs = bs, @@ -714,8 +741,10 @@ static void tracked_request_begin(BdrvTrackedRequest *req, } static bool tracked_request_overlaps(BdrvTrackedRequest *req, - int64_t offset, uint64_t bytes) + int64_t offset, int64_t bytes) { + bdrv_check_request(offset, bytes, &error_abort); + /* aaaa bbbb */ if (offset >= req->overlap_offset + req->overlap_bytes) { return false; @@ -727,65 +756,74 @@ static bool tracked_request_overlaps(BdrvTrackedRequest *req, return true; } -static bool coroutine_fn -bdrv_wait_serialising_requests_locked(BlockDriverState *bs, - BdrvTrackedRequest *self) +/* Called with self->bs->reqs_lock held */ +static BdrvTrackedRequest * +bdrv_find_conflicting_request(BdrvTrackedRequest *self) { BdrvTrackedRequest *req; - bool retry; - bool waited = false; - do { - retry = false; - QLIST_FOREACH(req, &bs->tracked_requests, list) { - if (req == self || (!req->serialising && !self->serialising)) { - continue; - } - if (tracked_request_overlaps(req, self->overlap_offset, - self->overlap_bytes)) - { - /* Hitting this means there was a reentrant request, for - * example, a block driver issuing nested requests. This must - * never happen since it means deadlock. - */ - assert(qemu_coroutine_self() != req->co); + QLIST_FOREACH(req, &self->bs->tracked_requests, list) { + if (req == self || (!req->serialising && !self->serialising)) { + continue; + } + if (tracked_request_overlaps(req, self->overlap_offset, + self->overlap_bytes)) + { + /* + * Hitting this means there was a reentrant request, for + * example, a block driver issuing nested requests. This must + * never happen since it means deadlock. + */ + assert(qemu_coroutine_self() != req->co); - /* If the request is already (indirectly) waiting for us, or - * will wait for us as soon as it wakes up, then just go on - * (instead of producing a deadlock in the former case). */ - if (!req->waiting_for) { - self->waiting_for = req; - qemu_co_queue_wait(&req->wait_queue, &bs->reqs_lock); - self->waiting_for = NULL; - retry = true; - waited = true; - break; - } + /* + * If the request is already (indirectly) waiting for us, or + * will wait for us as soon as it wakes up, then just go on + * (instead of producing a deadlock in the former case). + */ + if (!req->waiting_for) { + return req; } } - } while (retry); + } + + return NULL; +} + +/* Called with self->bs->reqs_lock held */ +static bool coroutine_fn +bdrv_wait_serialising_requests_locked(BdrvTrackedRequest *self) +{ + BdrvTrackedRequest *req; + bool waited = false; + + while ((req = bdrv_find_conflicting_request(self))) { + self->waiting_for = req; + qemu_co_queue_wait(&req->wait_queue, &self->bs->reqs_lock); + self->waiting_for = NULL; + waited = true; + } + return waited; } -bool bdrv_mark_request_serialising(BdrvTrackedRequest *req, uint64_t align) +/* Called with req->bs->reqs_lock held */ +static void tracked_request_set_serialising(BdrvTrackedRequest *req, + uint64_t align) { - BlockDriverState *bs = req->bs; int64_t overlap_offset = req->offset & ~(align - 1); - uint64_t overlap_bytes = ROUND_UP(req->offset + req->bytes, align) - - overlap_offset; - bool waited; + int64_t overlap_bytes = + ROUND_UP(req->offset + req->bytes, align) - overlap_offset; + + bdrv_check_request(req->offset, req->bytes, &error_abort); - qemu_co_mutex_lock(&bs->reqs_lock); if (!req->serialising) { - atomic_inc(&req->bs->serialising_in_flight); + qatomic_inc(&req->bs->serialising_in_flight); req->serialising = true; } req->overlap_offset = MIN(req->overlap_offset, overlap_offset); req->overlap_bytes = MAX(req->overlap_bytes, overlap_bytes); - waited = bdrv_wait_serialising_requests_locked(bs, req); - qemu_co_mutex_unlock(&bs->reqs_lock); - return waited; } /** @@ -841,7 +879,7 @@ static int bdrv_get_cluster_size(BlockDriverState *bs) void bdrv_inc_in_flight(BlockDriverState *bs) { - atomic_inc(&bs->in_flight); + qatomic_inc(&bs->in_flight); } void bdrv_wakeup(BlockDriverState *bs) @@ -851,7 +889,7 @@ void bdrv_wakeup(BlockDriverState *bs) void bdrv_dec_in_flight(BlockDriverState *bs) { - atomic_dec(&bs->in_flight); + qatomic_dec(&bs->in_flight); bdrv_wakeup(bs); } @@ -860,118 +898,116 @@ static bool coroutine_fn bdrv_wait_serialising_requests(BdrvTrackedRequest *self BlockDriverState *bs = self->bs; bool waited = false; - if (!atomic_read(&bs->serialising_in_flight)) { + if (!qatomic_read(&bs->serialising_in_flight)) { return false; } qemu_co_mutex_lock(&bs->reqs_lock); - waited = bdrv_wait_serialising_requests_locked(bs, self); + waited = bdrv_wait_serialising_requests_locked(self); qemu_co_mutex_unlock(&bs->reqs_lock); return waited; } -static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset, - size_t size) +bool coroutine_fn bdrv_make_request_serialising(BdrvTrackedRequest *req, + uint64_t align) { - if (size > BDRV_REQUEST_MAX_BYTES) { + bool waited; + + qemu_co_mutex_lock(&req->bs->reqs_lock); + + tracked_request_set_serialising(req, align); + waited = bdrv_wait_serialising_requests_locked(req); + + qemu_co_mutex_unlock(&req->bs->reqs_lock); + + return waited; +} + +static int bdrv_check_qiov_request(int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, + Error **errp) +{ + /* + * Check generic offset/bytes correctness + */ + + if (offset < 0) { + error_setg(errp, "offset is negative: %" PRIi64, offset); return -EIO; } - if (!bdrv_is_inserted(bs)) { - return -ENOMEDIUM; + if (bytes < 0) { + error_setg(errp, "bytes is negative: %" PRIi64, bytes); + return -EIO; } - if (offset < 0) { + if (bytes > BDRV_MAX_LENGTH) { + error_setg(errp, "bytes(%" PRIi64 ") exceeds maximum(%" PRIi64 ")", + bytes, BDRV_MAX_LENGTH); + return -EIO; + } + + if (offset > BDRV_MAX_LENGTH) { + error_setg(errp, "offset(%" PRIi64 ") exceeds maximum(%" PRIi64 ")", + offset, BDRV_MAX_LENGTH); + return -EIO; + } + + if (offset > BDRV_MAX_LENGTH - bytes) { + error_setg(errp, "sum of offset(%" PRIi64 ") and bytes(%" PRIi64 ") " + "exceeds maximum(%" PRIi64 ")", offset, bytes, + BDRV_MAX_LENGTH); + return -EIO; + } + + if (!qiov) { + return 0; + } + + /* + * Check qiov and qiov_offset + */ + + if (qiov_offset > qiov->size) { + error_setg(errp, "qiov_offset(%zu) overflow io vector size(%zu)", + qiov_offset, qiov->size); + return -EIO; + } + + if (bytes > qiov->size - qiov_offset) { + error_setg(errp, "bytes(%" PRIi64 ") + qiov_offset(%zu) overflow io " + "vector size(%zu)", bytes, qiov_offset, qiov->size); return -EIO; } return 0; } -typedef int coroutine_fn BdrvRequestEntry(void *opaque); -typedef struct BdrvRunCo { - BdrvRequestEntry *entry; - void *opaque; - int ret; - bool done; - Coroutine *co; /* Coroutine, running bdrv_run_co_entry, for debugging */ -} BdrvRunCo; - -static void coroutine_fn bdrv_run_co_entry(void *opaque) +int bdrv_check_request(int64_t offset, int64_t bytes, Error **errp) { - BdrvRunCo *arg = opaque; - - arg->ret = arg->entry(arg->opaque); - arg->done = true; - aio_wait_kick(); + return bdrv_check_qiov_request(offset, bytes, NULL, 0, errp); } -static int bdrv_run_co(BlockDriverState *bs, BdrvRequestEntry *entry, - void *opaque) +static int bdrv_check_request32(int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset) { - if (qemu_in_coroutine()) { - /* Fast-path if already in coroutine context */ - return entry(opaque); - } else { - BdrvRunCo s = { .entry = entry, .opaque = opaque }; - - s.co = qemu_coroutine_create(bdrv_run_co_entry, &s); - bdrv_coroutine_enter(bs, s.co); - - BDRV_POLL_WHILE(bs, !s.done); - - return s.ret; + int ret = bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, NULL); + if (ret < 0) { + return ret; } -} -typedef struct RwCo { - BdrvChild *child; - int64_t offset; - QEMUIOVector *qiov; - bool is_write; - BdrvRequestFlags flags; -} RwCo; - -static int coroutine_fn bdrv_rw_co_entry(void *opaque) -{ - RwCo *rwco = opaque; - - if (!rwco->is_write) { - return bdrv_co_preadv(rwco->child, rwco->offset, - rwco->qiov->size, rwco->qiov, - rwco->flags); - } else { - return bdrv_co_pwritev(rwco->child, rwco->offset, - rwco->qiov->size, rwco->qiov, - rwco->flags); + if (bytes > BDRV_REQUEST_MAX_BYTES) { + return -EIO; } -} -/* - * Process a vectored synchronous request using coroutines - */ -static int bdrv_prwv_co(BdrvChild *child, int64_t offset, - QEMUIOVector *qiov, bool is_write, - BdrvRequestFlags flags) -{ - RwCo rwco = { - .child = child, - .offset = offset, - .qiov = qiov, - .is_write = is_write, - .flags = flags, - }; - - return bdrv_run_co(child->bs, bdrv_rw_co_entry, &rwco); + return 0; } int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, - int bytes, BdrvRequestFlags flags) + int64_t bytes, BdrvRequestFlags flags) { - QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, bytes); - - return bdrv_prwv_co(child, offset, &qiov, true, + return bdrv_pwritev(child, offset, bytes, NULL, BDRV_REQ_ZERO_WRITE | flags); } @@ -1016,41 +1052,19 @@ int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags) } } -/* return < 0 if error. See bdrv_pwrite() for the return codes */ -int bdrv_preadv(BdrvChild *child, int64_t offset, QEMUIOVector *qiov) +/* See bdrv_pwrite() for the return codes */ +int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int64_t bytes) { int ret; - - ret = bdrv_prwv_co(child, offset, qiov, false, 0); - if (ret < 0) { - return ret; - } - - return qiov->size; -} - -/* See bdrv_pwrite() for the return codes */ -int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int bytes) -{ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); if (bytes < 0) { return -EINVAL; } - return bdrv_preadv(child, offset, &qiov); -} + ret = bdrv_preadv(child, offset, bytes, &qiov, 0); -int bdrv_pwritev(BdrvChild *child, int64_t offset, QEMUIOVector *qiov) -{ - int ret; - - ret = bdrv_prwv_co(child, offset, qiov, true, 0); - if (ret < 0) { - return ret; - } - - return qiov->size; + return ret < 0 ? ret : bytes; } /* Return no. of bytes on success or < 0 on error. Important errors are: @@ -1059,15 +1073,19 @@ int bdrv_pwritev(BdrvChild *child, int64_t offset, QEMUIOVector *qiov) -EINVAL Invalid offset or number of bytes -EACCES Trying to write a read-only device */ -int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, int bytes) +int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, + int64_t bytes) { + int ret; QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); if (bytes < 0) { return -EINVAL; } - return bdrv_pwritev(child, offset, &qiov); + ret = bdrv_pwritev(child, offset, bytes, &qiov, 0); + + return ret < 0 ? ret : bytes; } /* @@ -1077,7 +1095,7 @@ int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, int bytes) * Returns 0 on success, -errno in error cases. */ int bdrv_pwrite_sync(BdrvChild *child, int64_t offset, - const void *buf, int count) + const void *buf, int64_t count) { int ret; @@ -1108,7 +1126,7 @@ static void bdrv_co_io_em_complete(void *opaque, int ret) } static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, int flags) { @@ -1118,6 +1136,7 @@ static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs, QEMUIOVector local_qiov; int ret; + bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); assert(!(flags & ~BDRV_REQ_MASK)); assert(!(flags & BDRV_REQ_NO_FALLBACK)); @@ -1177,7 +1196,7 @@ out: } static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs, - uint64_t offset, uint64_t bytes, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, int flags) { @@ -1187,6 +1206,7 @@ static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs, QEMUIOVector local_qiov; int ret; + bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); assert(!(flags & ~BDRV_REQ_MASK)); assert(!(flags & BDRV_REQ_NO_FALLBACK)); @@ -1257,14 +1277,16 @@ emulate_flags: } static int coroutine_fn -bdrv_driver_pwritev_compressed(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, QEMUIOVector *qiov, +bdrv_driver_pwritev_compressed(BlockDriverState *bs, int64_t offset, + int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset) { BlockDriver *drv = bs->drv; QEMUIOVector local_qiov; int ret; + bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); + if (!drv) { return -ENOMEDIUM; } @@ -1290,7 +1312,7 @@ bdrv_driver_pwritev_compressed(BlockDriverState *bs, uint64_t offset, } static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, - int64_t offset, unsigned int bytes, QEMUIOVector *qiov, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, int flags) { BlockDriverState *bs = child->bs; @@ -1305,13 +1327,15 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, BlockDriver *drv = bs->drv; int64_t cluster_offset; int64_t cluster_bytes; - size_t skip_bytes; + int64_t skip_bytes; int ret; int max_transfer = MIN_NON_ZERO(bs->bl.max_transfer, BDRV_REQUEST_MAX_BYTES); - unsigned int progress = 0; + int64_t progress = 0; bool skip_write; + bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); + if (!drv) { return -ENOMEDIUM; } @@ -1452,15 +1476,16 @@ err: * reads; any other features must be implemented by the caller. */ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child, - BdrvTrackedRequest *req, int64_t offset, unsigned int bytes, + BdrvTrackedRequest *req, int64_t offset, int64_t bytes, int64_t align, QEMUIOVector *qiov, size_t qiov_offset, int flags) { BlockDriverState *bs = child->bs; int64_t total_bytes, max_bytes; int ret = 0; - uint64_t bytes_remaining = bytes; + int64_t bytes_remaining = bytes; int max_transfer; + bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); assert(is_power_of_2(align)); assert((offset & (align - 1)) == 0); assert((bytes & (align - 1)) == 0); @@ -1481,7 +1506,7 @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child, * with each other for the same cluster. For example, in copy-on-read * it ensures that the CoR read and write operations are atomic and * guest writes cannot interleave between them. */ - bdrv_mark_request_serialising(req, bdrv_get_cluster_size(bs)); + bdrv_make_request_serialising(req, bdrv_get_cluster_size(bs)); } else { bdrv_wait_serialising_requests(req); } @@ -1489,6 +1514,9 @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child, if (flags & BDRV_REQ_COPY_ON_READ) { int64_t pnum; + /* The flag BDRV_REQ_COPY_ON_READ has reached its addressee */ + flags &= ~BDRV_REQ_COPY_ON_READ; + ret = bdrv_is_allocated(bs, offset, bytes, &pnum); if (ret < 0) { goto out; @@ -1510,14 +1538,16 @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child, goto out; } + assert(!(flags & ~bs->supported_read_flags)); + max_bytes = ROUND_UP(MAX(0, total_bytes - offset), align); if (bytes <= max_bytes && bytes <= max_transfer) { - ret = bdrv_driver_preadv(bs, offset, bytes, qiov, qiov_offset, 0); + ret = bdrv_driver_preadv(bs, offset, bytes, qiov, qiov_offset, flags); goto out; } while (bytes_remaining) { - int num; + int64_t num; if (max_bytes) { num = MIN(bytes_remaining, MIN(max_bytes, max_transfer)); @@ -1525,7 +1555,8 @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child, ret = bdrv_driver_preadv(bs, offset + bytes - bytes_remaining, num, qiov, - qiov_offset + bytes - bytes_remaining, 0); + qiov_offset + bytes - bytes_remaining, + flags); max_bytes -= num; } else { num = bytes_remaining; @@ -1578,8 +1609,12 @@ static bool bdrv_init_padding(BlockDriverState *bs, int64_t offset, int64_t bytes, BdrvRequestPadding *pad) { - uint64_t align = bs->bl.request_alignment; - size_t sum; + int64_t align = bs->bl.request_alignment; + int64_t sum; + + bdrv_check_request(offset, bytes, &error_abort); + assert(align <= INT_MAX); /* documented in block/block_int.h */ + assert(align <= SIZE_MAX / 2); /* so we can allocate the buffer */ memset(pad, 0, sizeof(*pad)); @@ -1619,7 +1654,7 @@ static int bdrv_padding_rmw_read(BdrvChild *child, assert(req->serialising && pad->buf); if (pad->head || pad->merge_reads) { - uint64_t bytes = pad->merge_reads ? pad->buf_len : align; + int64_t bytes = pad->merge_reads ? pad->buf_len : align; qemu_iovec_init_buf(&local_qiov, pad->buf, bytes); @@ -1674,6 +1709,7 @@ static void bdrv_padding_destroy(BdrvRequestPadding *pad) qemu_vfree(pad->buf); qemu_iovec_destroy(&pad->local_qiov); } + memset(pad, 0, sizeof(*pad)); } /* @@ -1683,40 +1719,55 @@ static void bdrv_padding_destroy(BdrvRequestPadding *pad) * read of padding, bdrv_padding_rmw_read() should be called separately if * needed. * - * All parameters except @bs are in-out: they represent original request at - * function call and padded (if padding needed) at function finish. - * - * Function always succeeds. + * Request parameters (@qiov, &qiov_offset, &offset, &bytes) are in-out: + * - on function start they represent original request + * - on failure or when padding is not needed they are unchanged + * - on success when padding is needed they represent padded request */ -static bool bdrv_pad_request(BlockDriverState *bs, - QEMUIOVector **qiov, size_t *qiov_offset, - int64_t *offset, unsigned int *bytes, - BdrvRequestPadding *pad) +static int bdrv_pad_request(BlockDriverState *bs, + QEMUIOVector **qiov, size_t *qiov_offset, + int64_t *offset, int64_t *bytes, + BdrvRequestPadding *pad, bool *padded) { + int ret; + + bdrv_check_qiov_request(*offset, *bytes, *qiov, *qiov_offset, &error_abort); + if (!bdrv_init_padding(bs, *offset, *bytes, pad)) { - return false; + if (padded) { + *padded = false; + } + return 0; } - qemu_iovec_init_extended(&pad->local_qiov, pad->buf, pad->head, - *qiov, *qiov_offset, *bytes, - pad->buf + pad->buf_len - pad->tail, pad->tail); + ret = qemu_iovec_init_extended(&pad->local_qiov, pad->buf, pad->head, + *qiov, *qiov_offset, *bytes, + pad->buf + pad->buf_len - pad->tail, + pad->tail); + if (ret < 0) { + bdrv_padding_destroy(pad); + return ret; + } *bytes += pad->head + pad->tail; *offset -= pad->head; *qiov = &pad->local_qiov; *qiov_offset = 0; + if (padded) { + *padded = true; + } - return true; + return 0; } int coroutine_fn bdrv_co_preadv(BdrvChild *child, - int64_t offset, unsigned int bytes, QEMUIOVector *qiov, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { return bdrv_co_preadv_part(child, offset, bytes, qiov, 0, flags); } int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, - int64_t offset, unsigned int bytes, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags) { @@ -1725,9 +1776,13 @@ int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, BdrvRequestPadding pad; int ret; - trace_bdrv_co_preadv(bs, offset, bytes, flags); + trace_bdrv_co_preadv_part(bs, offset, bytes, flags); - ret = bdrv_check_byte_request(bs, offset, bytes); + if (!bdrv_is_inserted(bs)) { + return -ENOMEDIUM; + } + + ret = bdrv_check_request32(offset, bytes, qiov, qiov_offset); if (ret < 0) { return ret; } @@ -1747,11 +1802,15 @@ int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, bdrv_inc_in_flight(bs); /* Don't do copy-on-read if we read data before write operation */ - if (atomic_read(&bs->copy_on_read)) { + if (qatomic_read(&bs->copy_on_read)) { flags |= BDRV_REQ_COPY_ON_READ; } - bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, &pad); + ret = bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, &pad, + NULL); + if (ret < 0) { + return ret; + } tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_READ); ret = bdrv_aligned_preadv(child, &req, offset, bytes, @@ -1766,7 +1825,7 @@ int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, } static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, - int64_t offset, int bytes, BdrvRequestFlags flags) + int64_t offset, int64_t bytes, BdrvRequestFlags flags) { BlockDriver *drv = bs->drv; QEMUIOVector qiov; @@ -1781,6 +1840,8 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, bs->bl.request_alignment); int max_transfer = MIN_NON_ZERO(bs->bl.max_transfer, MAX_BOUNCE_BUFFER); + bdrv_check_request(offset, bytes, &error_abort); + if (!drv) { return -ENOMEDIUM; } @@ -1796,7 +1857,7 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, assert(max_write_zeroes >= bs->bl.request_alignment); while (bytes > 0 && !ret) { - int num = bytes; + int64_t num = bytes; /* Align request. Block drivers can expect the "bulk" of the request * to be aligned, and that unaligned requests do not cross cluster @@ -1877,12 +1938,12 @@ fail: } static inline int coroutine_fn -bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, uint64_t bytes, +bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, int64_t bytes, BdrvTrackedRequest *req, int flags) { BlockDriverState *bs = child->bs; - bool waited; - int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE); + + bdrv_check_request(offset, bytes, &error_abort); if (bs->read_only) { return -EPERM; @@ -1891,24 +1952,26 @@ bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, uint64_t bytes, assert(!(bs->open_flags & BDRV_O_INACTIVE)); assert((bs->open_flags & BDRV_O_NO_IO) == 0); assert(!(flags & ~BDRV_REQ_MASK)); + assert(!((flags & BDRV_REQ_NO_WAIT) && !(flags & BDRV_REQ_SERIALISING))); if (flags & BDRV_REQ_SERIALISING) { - waited = bdrv_mark_request_serialising(req, bdrv_get_cluster_size(bs)); - /* - * For a misaligned request we should have already waited earlier, - * because we come after bdrv_padding_rmw_read which must be called - * with the request already marked as serialising. - */ - assert(!waited || - (req->offset == req->overlap_offset && - req->bytes == req->overlap_bytes)); + QEMU_LOCK_GUARD(&bs->reqs_lock); + + tracked_request_set_serialising(req, bdrv_get_cluster_size(bs)); + + if ((flags & BDRV_REQ_NO_WAIT) && bdrv_find_conflicting_request(req)) { + return -EBUSY; + } + + bdrv_wait_serialising_requests_locked(req); } else { bdrv_wait_serialising_requests(req); } assert(req->overlap_offset <= offset); assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); - assert(end_sector <= bs->total_sectors || child->perm & BLK_PERM_RESIZE); + assert(offset + bytes <= bs->total_sectors * BDRV_SECTOR_SIZE || + child->perm & BLK_PERM_RESIZE); switch (req->type) { case BDRV_TRACKED_WRITE: @@ -1929,13 +1992,15 @@ bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, uint64_t bytes, } static inline void coroutine_fn -bdrv_co_write_req_finish(BdrvChild *child, int64_t offset, uint64_t bytes, +bdrv_co_write_req_finish(BdrvChild *child, int64_t offset, int64_t bytes, BdrvTrackedRequest *req, int ret) { int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE); BlockDriverState *bs = child->bs; - atomic_inc(&bs->write_gen); + bdrv_check_request(offset, bytes, &error_abort); + + qatomic_inc(&bs->write_gen); /* * Discard cannot extend the image, but in error handling cases, such as @@ -1971,16 +2036,18 @@ bdrv_co_write_req_finish(BdrvChild *child, int64_t offset, uint64_t bytes, * after possibly fragmenting it. */ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, - BdrvTrackedRequest *req, int64_t offset, unsigned int bytes, + BdrvTrackedRequest *req, int64_t offset, int64_t bytes, int64_t align, QEMUIOVector *qiov, size_t qiov_offset, int flags) { BlockDriverState *bs = child->bs; BlockDriver *drv = bs->drv; int ret; - uint64_t bytes_remaining = bytes; + int64_t bytes_remaining = bytes; int max_transfer; + bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); + if (!drv) { return -ENOMEDIUM; } @@ -1992,7 +2059,6 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, assert(is_power_of_2(align)); assert((offset & (align - 1)) == 0); assert((bytes & (align - 1)) == 0); - assert(!qiov || qiov_offset + bytes <= qiov->size); max_transfer = QEMU_ALIGN_DOWN(MIN_NON_ZERO(bs->bl.max_transfer, INT_MAX), align); @@ -2054,7 +2120,7 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child, int64_t offset, - unsigned int bytes, + int64_t bytes, BdrvRequestFlags flags, BdrvTrackedRequest *req) { @@ -2067,7 +2133,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child, padding = bdrv_init_padding(bs, offset, bytes, &pad); if (padding) { - bdrv_mark_request_serialising(req, align); + bdrv_make_request_serialising(req, align); bdrv_padding_rmw_read(child, req, &pad, true); @@ -2091,7 +2157,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child, assert(!bytes || (offset & (align - 1)) == 0); if (bytes >= align) { /* Write the aligned part in the middle. */ - uint64_t aligned_bytes = bytes & ~(align - 1); + int64_t aligned_bytes = bytes & ~(align - 1); ret = bdrv_aligned_pwritev(child, req, offset, aligned_bytes, align, NULL, 0, flags); if (ret < 0) { @@ -2121,14 +2187,14 @@ out: * Handle a write request in coroutine context */ int coroutine_fn bdrv_co_pwritev(BdrvChild *child, - int64_t offset, unsigned int bytes, QEMUIOVector *qiov, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { return bdrv_co_pwritev_part(child, offset, bytes, qiov, 0, flags); } int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, - int64_t offset, unsigned int bytes, QEMUIOVector *qiov, size_t qiov_offset, + int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags) { BlockDriverState *bs = child->bs; @@ -2136,14 +2202,15 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, uint64_t align = bs->bl.request_alignment; BdrvRequestPadding pad; int ret; + bool padded = false; - trace_bdrv_co_pwritev(child->bs, offset, bytes, flags); + trace_bdrv_co_pwritev_part(child->bs, offset, bytes, flags); - if (!bs->drv) { + if (!bdrv_is_inserted(bs)) { return -ENOMEDIUM; } - ret = bdrv_check_byte_request(bs, offset, bytes); + ret = bdrv_check_request32(offset, bytes, qiov, qiov_offset); if (ret < 0) { return ret; } @@ -2167,21 +2234,36 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, return 0; } + if (!(flags & BDRV_REQ_ZERO_WRITE)) { + /* + * Pad request for following read-modify-write cycle. + * bdrv_co_do_zero_pwritev() does aligning by itself, so, we do + * alignment only if there is no ZERO flag. + */ + ret = bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, &pad, + &padded); + if (ret < 0) { + return ret; + } + } + bdrv_inc_in_flight(bs); - /* - * Align write if necessary by performing a read-modify-write cycle. - * Pad qiov with the read parts and be sure to have a tracked request not - * only for bdrv_aligned_pwritev, but also for the reads of the RMW cycle. - */ tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_WRITE); if (flags & BDRV_REQ_ZERO_WRITE) { + assert(!padded); ret = bdrv_co_do_zero_pwritev(child, offset, bytes, flags, &req); goto out; } - if (bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, &pad)) { - bdrv_mark_request_serialising(&req, align); + if (padded) { + /* + * Request was unaligned to request_alignment and therefore + * padded. We are going to do read-modify-write, and must + * serialize the request to prevent interactions of the + * widened region with other transactions. + */ + bdrv_make_request_serialising(&req, align); bdrv_padding_rmw_read(child, &req, &pad, false); } @@ -2198,7 +2280,7 @@ out: } int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, - int bytes, BdrvRequestFlags flags) + int64_t bytes, BdrvRequestFlags flags) { trace_bdrv_co_pwrite_zeroes(child->bs, offset, bytes, flags); @@ -2243,48 +2325,6 @@ int bdrv_flush_all(void) return result; } - -typedef struct BdrvCoBlockStatusData { - BlockDriverState *bs; - BlockDriverState *base; - bool want_zero; - int64_t offset; - int64_t bytes; - int64_t *pnum; - int64_t *map; - BlockDriverState **file; -} BdrvCoBlockStatusData; - -int coroutine_fn bdrv_co_block_status_from_file(BlockDriverState *bs, - bool want_zero, - int64_t offset, - int64_t bytes, - int64_t *pnum, - int64_t *map, - BlockDriverState **file) -{ - assert(bs->file && bs->file->bs); - *pnum = bytes; - *map = offset; - *file = bs->file->bs; - return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID; -} - -int coroutine_fn bdrv_co_block_status_from_backing(BlockDriverState *bs, - bool want_zero, - int64_t offset, - int64_t bytes, - int64_t *pnum, - int64_t *map, - BlockDriverState **file) -{ - assert(bs->backing && bs->backing->bs); - *pnum = bytes; - *map = offset; - *file = bs->backing->bs; - return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID; -} - /* * Returns the allocation status of the specified sectors. * Drivers not implementing the functionality are assumed to not support @@ -2325,6 +2365,7 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, BlockDriverState *local_file = NULL; int64_t aligned_offset, aligned_bytes; uint32_t align; + bool has_filtered_child; assert(pnum); *pnum = 0; @@ -2350,7 +2391,8 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, /* Must be non-NULL or bdrv_getlength() would have failed */ assert(bs->drv); - if (!bs->drv->bdrv_co_block_status) { + has_filtered_child = bdrv_filter_child(bs); + if (!bs->drv->bdrv_co_block_status && !has_filtered_child) { *pnum = bytes; ret = BDRV_BLOCK_DATA | BDRV_BLOCK_ALLOCATED; if (offset + bytes == total_size) { @@ -2371,9 +2413,20 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, aligned_offset = QEMU_ALIGN_DOWN(offset, align); aligned_bytes = ROUND_UP(offset + bytes, align) - aligned_offset; - ret = bs->drv->bdrv_co_block_status(bs, want_zero, aligned_offset, - aligned_bytes, pnum, &local_map, - &local_file); + if (bs->drv->bdrv_co_block_status) { + ret = bs->drv->bdrv_co_block_status(bs, want_zero, aligned_offset, + aligned_bytes, pnum, &local_map, + &local_file); + } else { + /* Default code for filters */ + + local_file = bdrv_filter_bs(bs); + assert(local_file); + + *pnum = aligned_bytes; + local_map = aligned_offset; + ret = BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID; + } if (ret < 0) { *pnum = 0; goto out; @@ -2408,16 +2461,17 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, if (ret & (BDRV_BLOCK_DATA | BDRV_BLOCK_ZERO)) { ret |= BDRV_BLOCK_ALLOCATED; - } else if (want_zero && bs->drv->supports_backing) { - if (bs->backing) { - BlockDriverState *bs2 = bs->backing->bs; - int64_t size2 = bdrv_getlength(bs2); + } else if (bs->drv->supports_backing) { + BlockDriverState *cow_bs = bdrv_cow_bs(bs); + + if (!cow_bs) { + ret |= BDRV_BLOCK_ZERO; + } else if (want_zero) { + int64_t size2 = bdrv_getlength(cow_bs); if (size2 >= 0 && offset >= size2) { ret |= BDRV_BLOCK_ZERO; } - } else { - ret |= BDRV_BLOCK_ZERO; } } @@ -2465,107 +2519,158 @@ early_out: return ret; } -static int coroutine_fn bdrv_co_block_status_above(BlockDriverState *bs, - BlockDriverState *base, - bool want_zero, - int64_t offset, - int64_t bytes, - int64_t *pnum, - int64_t *map, - BlockDriverState **file) +int coroutine_fn +bdrv_co_common_block_status_above(BlockDriverState *bs, + BlockDriverState *base, + bool include_base, + bool want_zero, + int64_t offset, + int64_t bytes, + int64_t *pnum, + int64_t *map, + BlockDriverState **file, + int *depth) { + int ret; BlockDriverState *p; - int ret = 0; - bool first = true; + int64_t eof = 0; + int dummy; - assert(bs != base); - for (p = bs; p != base; p = backing_bs(p)) { + assert(!include_base || base); /* Can't include NULL base */ + + if (!depth) { + depth = &dummy; + } + *depth = 0; + + if (!include_base && bs == base) { + *pnum = bytes; + return 0; + } + + ret = bdrv_co_block_status(bs, want_zero, offset, bytes, pnum, map, file); + ++*depth; + if (ret < 0 || *pnum == 0 || ret & BDRV_BLOCK_ALLOCATED || bs == base) { + return ret; + } + + if (ret & BDRV_BLOCK_EOF) { + eof = offset + *pnum; + } + + assert(*pnum <= bytes); + bytes = *pnum; + + for (p = bdrv_filter_or_cow_bs(bs); include_base || p != base; + p = bdrv_filter_or_cow_bs(p)) + { ret = bdrv_co_block_status(p, want_zero, offset, bytes, pnum, map, file); + ++*depth; if (ret < 0) { - break; + return ret; } - if (ret & BDRV_BLOCK_ZERO && ret & BDRV_BLOCK_EOF && !first) { + if (*pnum == 0) { /* - * Reading beyond the end of the file continues to read - * zeroes, but we can only widen the result to the - * unallocated length we learned from an earlier - * iteration. + * The top layer deferred to this layer, and because this layer is + * short, any zeroes that we synthesize beyond EOF behave as if they + * were allocated at this layer. + * + * We don't include BDRV_BLOCK_EOF into ret, as upper layer may be + * larger. We'll add BDRV_BLOCK_EOF if needed at function end, see + * below. */ + assert(ret & BDRV_BLOCK_EOF); *pnum = bytes; - } - if (ret & (BDRV_BLOCK_ZERO | BDRV_BLOCK_DATA)) { + if (file) { + *file = p; + } + ret = BDRV_BLOCK_ZERO | BDRV_BLOCK_ALLOCATED; break; } - /* [offset, pnum] unallocated on this layer, which could be only - * the first part of [offset, bytes]. */ - bytes = MIN(bytes, *pnum); - first = false; + if (ret & BDRV_BLOCK_ALLOCATED) { + /* + * We've found the node and the status, we must break. + * + * Drop BDRV_BLOCK_EOF, as it's not for upper layer, which may be + * larger. We'll add BDRV_BLOCK_EOF if needed at function end, see + * below. + */ + ret &= ~BDRV_BLOCK_EOF; + break; + } + + if (p == base) { + assert(include_base); + break; + } + + /* + * OK, [offset, offset + *pnum) region is unallocated on this layer, + * let's continue the diving. + */ + assert(*pnum <= bytes); + bytes = *pnum; } + + if (offset + *pnum == eof) { + ret |= BDRV_BLOCK_EOF; + } + return ret; } -/* Coroutine wrapper for bdrv_block_status_above() */ -static int coroutine_fn bdrv_block_status_above_co_entry(void *opaque) -{ - BdrvCoBlockStatusData *data = opaque; - - return bdrv_co_block_status_above(data->bs, data->base, - data->want_zero, - data->offset, data->bytes, - data->pnum, data->map, data->file); -} - -/* - * Synchronous wrapper around bdrv_co_block_status_above(). - * - * See bdrv_co_block_status_above() for details. - */ -static int bdrv_common_block_status_above(BlockDriverState *bs, - BlockDriverState *base, - bool want_zero, int64_t offset, - int64_t bytes, int64_t *pnum, - int64_t *map, - BlockDriverState **file) -{ - BdrvCoBlockStatusData data = { - .bs = bs, - .base = base, - .want_zero = want_zero, - .offset = offset, - .bytes = bytes, - .pnum = pnum, - .map = map, - .file = file, - }; - - return bdrv_run_co(bs, bdrv_block_status_above_co_entry, &data); -} - int bdrv_block_status_above(BlockDriverState *bs, BlockDriverState *base, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) { - return bdrv_common_block_status_above(bs, base, true, offset, bytes, - pnum, map, file); + return bdrv_common_block_status_above(bs, base, false, true, offset, bytes, + pnum, map, file, NULL); } int bdrv_block_status(BlockDriverState *bs, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) { - return bdrv_block_status_above(bs, backing_bs(bs), + return bdrv_block_status_above(bs, bdrv_filter_or_cow_bs(bs), offset, bytes, pnum, map, file); } +/* + * Check @bs (and its backing chain) to see if the range defined + * by @offset and @bytes is known to read as zeroes. + * Return 1 if that is the case, 0 otherwise and -errno on error. + * This test is meant to be fast rather than accurate so returning 0 + * does not guarantee non-zero data. + */ +int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset, + int64_t bytes) +{ + int ret; + int64_t pnum = bytes; + + if (!bytes) { + return 1; + } + + ret = bdrv_common_block_status_above(bs, NULL, false, false, offset, + bytes, &pnum, NULL, NULL, NULL); + + if (ret < 0) { + return ret; + } + + return (pnum == bytes) && (ret & BDRV_BLOCK_ZERO); +} + int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes, int64_t *pnum) { int ret; int64_t dummy; - ret = bdrv_common_block_status_above(bs, backing_bs(bs), false, offset, + ret = bdrv_common_block_status_above(bs, bs, true, false, offset, bytes, pnum ? pnum : &dummy, NULL, - NULL); + NULL, NULL); if (ret < 0) { return ret; } @@ -2575,8 +2680,9 @@ int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t offset, /* * Given an image chain: ... -> [BASE] -> [INTER1] -> [INTER2] -> [TOP] * - * Return 1 if (a prefix of) the given range is allocated in any image - * between BASE and TOP (BASE is only included if include_base is set). + * Return a positive depth if (a prefix of) the given range is allocated + * in any image between BASE and TOP (BASE is only included if include_base + * is set). Depth 1 is TOP, 2 is the first backing layer, and so forth. * BASE can be NULL to check if the given offset is allocated in any * image of the chain. Return 0 otherwise, or negative errno on * failure. @@ -2587,143 +2693,90 @@ int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t offset, * at 'offset + *pnum' may return the same allocation status (in other * words, the result is not necessarily the maximum possible range); * but 'pnum' will only be 0 when end of file is reached. - * */ int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, bool include_base, int64_t offset, int64_t bytes, int64_t *pnum) { - BlockDriverState *intermediate; - int ret; - int64_t n = bytes; - - assert(base || !include_base); - - intermediate = top; - while (include_base || intermediate != base) { - int64_t pnum_inter; - int64_t size_inter; - - assert(intermediate); - ret = bdrv_is_allocated(intermediate, offset, bytes, &pnum_inter); - if (ret < 0) { - return ret; - } - if (ret) { - *pnum = pnum_inter; - return 1; - } - - size_inter = bdrv_getlength(intermediate); - if (size_inter < 0) { - return size_inter; - } - if (n > pnum_inter && - (intermediate == top || offset + pnum_inter < size_inter)) { - n = pnum_inter; - } - - if (intermediate == base) { - break; - } - - intermediate = backing_bs(intermediate); + int depth; + int ret = bdrv_common_block_status_above(top, base, include_base, false, + offset, bytes, pnum, NULL, NULL, + &depth); + if (ret < 0) { + return ret; } - *pnum = n; + if (ret & BDRV_BLOCK_ALLOCATED) { + return depth; + } return 0; } -typedef struct BdrvVmstateCo { - BlockDriverState *bs; - QEMUIOVector *qiov; - int64_t pos; - bool is_read; -} BdrvVmstateCo; - -static int coroutine_fn -bdrv_co_rw_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos, - bool is_read) +int coroutine_fn +bdrv_co_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) { BlockDriver *drv = bs->drv; + BlockDriverState *child_bs = bdrv_primary_bs(bs); int ret = -ENOTSUP; + if (!drv) { + return -ENOMEDIUM; + } + bdrv_inc_in_flight(bs); - if (!drv) { - ret = -ENOMEDIUM; - } else if (drv->bdrv_load_vmstate) { - if (is_read) { - ret = drv->bdrv_load_vmstate(bs, qiov, pos); - } else { - ret = drv->bdrv_save_vmstate(bs, qiov, pos); - } - } else if (bs->file) { - ret = bdrv_co_rw_vmstate(bs->file->bs, qiov, pos, is_read); + if (drv->bdrv_load_vmstate) { + ret = drv->bdrv_load_vmstate(bs, qiov, pos); + } else if (child_bs) { + ret = bdrv_co_readv_vmstate(child_bs, qiov, pos); } bdrv_dec_in_flight(bs); + return ret; } -static int coroutine_fn bdrv_co_rw_vmstate_entry(void *opaque) +int coroutine_fn +bdrv_co_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) { - BdrvVmstateCo *co = opaque; + BlockDriver *drv = bs->drv; + BlockDriverState *child_bs = bdrv_primary_bs(bs); + int ret = -ENOTSUP; - return bdrv_co_rw_vmstate(co->bs, co->qiov, co->pos, co->is_read); -} + if (!drv) { + return -ENOMEDIUM; + } -static inline int -bdrv_rw_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos, - bool is_read) -{ - BdrvVmstateCo data = { - .bs = bs, - .qiov = qiov, - .pos = pos, - .is_read = is_read, - }; + bdrv_inc_in_flight(bs); - return bdrv_run_co(bs, bdrv_co_rw_vmstate_entry, &data); + if (drv->bdrv_save_vmstate) { + ret = drv->bdrv_save_vmstate(bs, qiov, pos); + } else if (child_bs) { + ret = bdrv_co_writev_vmstate(child_bs, qiov, pos); + } + + bdrv_dec_in_flight(bs); + + return ret; } int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf, int64_t pos, int size) { QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, size); - int ret; + int ret = bdrv_writev_vmstate(bs, &qiov, pos); - ret = bdrv_writev_vmstate(bs, &qiov, pos); - if (ret < 0) { - return ret; - } - - return size; -} - -int bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) -{ - return bdrv_rw_vmstate(bs, qiov, pos, false); + return ret < 0 ? ret : size; } int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, int64_t pos, int size) { QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, size); - int ret; + int ret = bdrv_readv_vmstate(bs, &qiov, pos); - ret = bdrv_readv_vmstate(bs, &qiov, pos); - if (ret < 0) { - return ret; - } - - return size; -} - -int bdrv_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) -{ - return bdrv_rw_vmstate(bs, qiov, pos, true); + return ret < 0 ? ret : size; } /**************************************************************/ @@ -2763,13 +2816,10 @@ void bdrv_aio_cancel_async(BlockAIOCB *acb) /**************************************************************/ /* Coroutine block device emulation */ -static int coroutine_fn bdrv_flush_co_entry(void *opaque) -{ - return bdrv_co_flush(opaque); -} - int coroutine_fn bdrv_co_flush(BlockDriverState *bs) { + BdrvChild *primary_child = bdrv_primary_child(bs); + BdrvChild *child; int current_gen; int ret = 0; @@ -2781,7 +2831,7 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs) } qemu_co_mutex_lock(&bs->reqs_lock); - current_gen = atomic_read(&bs->write_gen); + current_gen = qatomic_read(&bs->write_gen); /* Wait until any previous flushes are completed */ while (bs->active_flush_req) { @@ -2799,7 +2849,7 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs) } /* Write back cached data to the OS even with cache=unsafe */ - BLKDBG_EVENT(bs->file, BLKDBG_FLUSH_TO_OS); + BLKDBG_EVENT(primary_child, BLKDBG_FLUSH_TO_OS); if (bs->drv->bdrv_co_flush_to_os) { ret = bs->drv->bdrv_co_flush_to_os(bs); if (ret < 0) { @@ -2809,15 +2859,15 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs) /* But don't actually force it to the disk with cache=unsafe */ if (bs->open_flags & BDRV_O_NO_FLUSH) { - goto flush_parent; + goto flush_children; } /* Check if we really need to flush anything */ if (bs->flushed_gen == current_gen) { - goto flush_parent; + goto flush_children; } - BLKDBG_EVENT(bs->file, BLKDBG_FLUSH_TO_DISK); + BLKDBG_EVENT(primary_child, BLKDBG_FLUSH_TO_DISK); if (!bs->drv) { /* bs->drv->bdrv_co_flush() might have ejected the BDS * (even in case of apparent success) */ @@ -2861,8 +2911,17 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs) /* Now flush the underlying protocol. It will also have BDRV_O_NO_FLUSH * in the case of cache=unsafe, so there are no useless flushes. */ -flush_parent: - ret = bs->file ? bdrv_co_flush(bs->file->bs) : 0; +flush_children: + ret = 0; + QLIST_FOREACH(child, &bs->children, next) { + if (child->perm & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) { + int this_child_ret = bdrv_co_flush(child->bs); + if (!ret) { + ret = this_child_ret; + } + } + } + out: /* Notify any pending flushes that we have completed */ if (ret == 0) { @@ -2880,24 +2939,6 @@ early_exit: return ret; } -int bdrv_flush(BlockDriverState *bs) -{ - return bdrv_run_co(bs, bdrv_flush_co_entry, bs); -} - -typedef struct DiscardCo { - BdrvChild *child; - int64_t offset; - int64_t bytes; -} DiscardCo; - -static int coroutine_fn bdrv_pdiscard_co_entry(void *opaque) -{ - DiscardCo *rwco = opaque; - - return bdrv_co_pdiscard(rwco->child, rwco->offset, rwco->bytes); -} - int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes) { @@ -2914,8 +2955,9 @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, return -EPERM; } - if (offset < 0 || bytes < 0 || bytes > INT64_MAX - offset) { - return -EIO; + ret = bdrv_check_request(offset, bytes, NULL); + if (ret < 0) { + return ret; } /* Do nothing if disabled. */ @@ -3012,17 +3054,6 @@ out: return ret; } -int bdrv_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes) -{ - DiscardCo rwco = { - .child = child, - .offset = offset, - .bytes = bytes, - }; - - return bdrv_run_co(child->bs, bdrv_pdiscard_co_entry, &rwco); -} - int bdrv_co_ioctl(BlockDriverState *bs, int req, void *buf) { BlockDriver *drv = bs->drv; @@ -3120,7 +3151,7 @@ void bdrv_io_plug(BlockDriverState *bs) bdrv_io_plug(child->bs); } - if (atomic_fetch_inc(&bs->io_plugged) == 0) { + if (qatomic_fetch_inc(&bs->io_plugged) == 0) { BlockDriver *drv = bs->drv; if (drv && drv->bdrv_io_plug) { drv->bdrv_io_plug(bs); @@ -3133,7 +3164,7 @@ void bdrv_io_unplug(BlockDriverState *bs) BdrvChild *child; assert(bs->io_plugged); - if (atomic_fetch_dec(&bs->io_plugged) == 1) { + if (qatomic_fetch_dec(&bs->io_plugged) == 1) { BlockDriver *drv = bs->drv; if (drv && drv->bdrv_io_unplug) { drv->bdrv_io_unplug(bs); @@ -3170,8 +3201,8 @@ void bdrv_unregister_buf(BlockDriverState *bs, void *host) } static int coroutine_fn bdrv_co_copy_range_internal( - BdrvChild *src, uint64_t src_offset, BdrvChild *dst, - uint64_t dst_offset, uint64_t bytes, + BdrvChild *src, int64_t src_offset, BdrvChild *dst, + int64_t dst_offset, int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags, bool recurse_src) { @@ -3182,10 +3213,10 @@ static int coroutine_fn bdrv_co_copy_range_internal( assert(!(read_flags & BDRV_REQ_NO_FALLBACK)); assert(!(write_flags & BDRV_REQ_NO_FALLBACK)); - if (!dst || !dst->bs) { + if (!dst || !dst->bs || !bdrv_is_inserted(dst->bs)) { return -ENOMEDIUM; } - ret = bdrv_check_byte_request(dst->bs, dst_offset, bytes); + ret = bdrv_check_request32(dst_offset, bytes, NULL, 0); if (ret) { return ret; } @@ -3193,10 +3224,10 @@ static int coroutine_fn bdrv_co_copy_range_internal( return bdrv_co_pwrite_zeroes(dst, dst_offset, bytes, write_flags); } - if (!src || !src->bs) { + if (!src || !src->bs || !bdrv_is_inserted(src->bs)) { return -ENOMEDIUM; } - ret = bdrv_check_byte_request(src->bs, src_offset, bytes); + ret = bdrv_check_request32(src_offset, bytes, NULL, 0); if (ret) { return ret; } @@ -3249,9 +3280,9 @@ static int coroutine_fn bdrv_co_copy_range_internal( * * See the comment of bdrv_co_copy_range for the parameter and return value * semantics. */ -int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset, - BdrvChild *dst, uint64_t dst_offset, - uint64_t bytes, +int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) { @@ -3265,9 +3296,9 @@ int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset, * * See the comment of bdrv_co_copy_range for the parameter and return value * semantics. */ -int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset, - BdrvChild *dst, uint64_t dst_offset, - uint64_t bytes, +int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) { @@ -3277,9 +3308,9 @@ int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset, bytes, read_flags, write_flags, false); } -int coroutine_fn bdrv_co_copy_range(BdrvChild *src, uint64_t src_offset, - BdrvChild *dst, uint64_t dst_offset, - uint64_t bytes, BdrvRequestFlags read_flags, +int coroutine_fn bdrv_co_copy_range(BdrvChild *src, int64_t src_offset, + BdrvChild *dst, int64_t dst_offset, + int64_t bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) { return bdrv_co_copy_range_from(src, src_offset, @@ -3309,6 +3340,7 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact, Error **errp) { BlockDriverState *bs = child->bs; + BdrvChild *filtered, *backing; BlockDriver *drv = bs->drv; BdrvTrackedRequest req; int64_t old_size, new_bytes; @@ -3325,6 +3357,11 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact, return -EINVAL; } + ret = bdrv_check_request(offset, 0, errp); + if (ret < 0) { + return ret; + } + old_size = bdrv_getlength(bs); if (old_size < 0) { error_setg_errno(errp, -old_size, "Failed to get old image size"); @@ -3345,7 +3382,7 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact, * new area, we need to make sure that no write requests are made to it * concurrently or they might be overwritten by preallocation. */ if (new_bytes) { - bdrv_mark_request_serialising(&req, 1); + bdrv_make_request_serialising(&req, 1); } if (bs->read_only) { error_setg(errp, "Image is read-only"); @@ -3360,6 +3397,9 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact, goto out; } + filtered = bdrv_filter_child(bs); + backing = bdrv_cow_child(bs); + /* * If the image has a backing file that is large enough that it would * provide data for the new area, we cannot leave it unallocated because @@ -3370,10 +3410,10 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact, * backing file, taking care of keeping things consistent with that backing * file is the user's responsibility. */ - if (new_bytes && bs->backing) { + if (new_bytes && backing) { int64_t backing_len; - backing_len = bdrv_getlength(backing_bs(bs)); + backing_len = bdrv_getlength(backing->bs); if (backing_len < 0) { ret = backing_len; error_setg_errno(errp, -ret, "Could not get backing file size"); @@ -3392,8 +3432,8 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact, goto out; } ret = drv->bdrv_co_truncate(bs, offset, exact, prealloc, flags, errp); - } else if (bs->file && drv->is_filter) { - ret = bdrv_co_truncate(bs->file, offset, exact, prealloc, flags, errp); + } else if (filtered) { + ret = bdrv_co_truncate(filtered, offset, exact, prealloc, flags, errp); } else { error_setg(errp, "Image format driver does not support resize"); ret = -ENOTSUP; @@ -3421,34 +3461,13 @@ out: return ret; } -typedef struct TruncateCo { - BdrvChild *child; - int64_t offset; - bool exact; - PreallocMode prealloc; - BdrvRequestFlags flags; - Error **errp; -} TruncateCo; - -static int coroutine_fn bdrv_truncate_co_entry(void *opaque) +void bdrv_cancel_in_flight(BlockDriverState *bs) { - TruncateCo *tco = opaque; + if (!bs || !bs->drv) { + return; + } - return bdrv_co_truncate(tco->child, tco->offset, tco->exact, - tco->prealloc, tco->flags, tco->errp); -} - -int bdrv_truncate(BdrvChild *child, int64_t offset, bool exact, - PreallocMode prealloc, BdrvRequestFlags flags, Error **errp) -{ - TruncateCo tco = { - .child = child, - .offset = offset, - .exact = exact, - .prealloc = prealloc, - .flags = flags, - .errp = errp, - }; - - return bdrv_run_co(child->bs, bdrv_truncate_co_entry, &tco); + if (bs->drv->bdrv_cancel_in_flight) { + bs->drv->bdrv_cancel_in_flight(bs); + } } diff --git a/block/io_uring.c b/block/io_uring.c index 037af09471..00a3ee9fb8 100644 --- a/block/io_uring.c +++ b/block/io_uring.c @@ -425,6 +425,6 @@ LuringState *luring_init(Error **errp) void luring_cleanup(LuringState *s) { io_uring_queue_exit(&s->ring); - g_free(s); trace_luring_cleanup_state(s); + g_free(s); } diff --git a/block/iscsi.c b/block/iscsi.c index bd2122a3a4..4d2a416ce7 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -42,7 +42,7 @@ #include "qemu/uuid.h" #include "sysemu/replay.h" #include "qapi/error.h" -#include "qapi/qapi-commands-misc.h" +#include "qapi/qapi-commands-machine.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" #include "crypto/secret.h" @@ -322,25 +322,23 @@ iscsi_aio_cancel(BlockAIOCB *blockacb) IscsiAIOCB *acb = (IscsiAIOCB *)blockacb; IscsiLun *iscsilun = acb->iscsilun; - qemu_mutex_lock(&iscsilun->mutex); + WITH_QEMU_LOCK_GUARD(&iscsilun->mutex) { - /* If it was cancelled or completed already, our work is done here */ - if (acb->cancelled || acb->status != -EINPROGRESS) { - qemu_mutex_unlock(&iscsilun->mutex); - return; + /* If it was cancelled or completed already, our work is done here */ + if (acb->cancelled || acb->status != -EINPROGRESS) { + return; + } + + acb->cancelled = true; + + qemu_aio_ref(acb); /* released in iscsi_abort_task_cb() */ + + /* send a task mgmt call to the target to cancel the task on the target */ + if (iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task, + iscsi_abort_task_cb, acb) < 0) { + qemu_aio_unref(acb); /* since iscsi_abort_task_cb() won't be called */ + } } - - acb->cancelled = true; - - qemu_aio_ref(acb); /* released in iscsi_abort_task_cb() */ - - /* send a task mgmt call to the target to cancel the task on the target */ - if (iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task, - iscsi_abort_task_cb, acb) < 0) { - qemu_aio_unref(acb); /* since iscsi_abort_task_cb() won't be called */ - } - - qemu_mutex_unlock(&iscsilun->mutex); } static const AIOCBInfo iscsi_aiocb_info = { @@ -375,22 +373,22 @@ static void iscsi_timed_check_events(void *opaque) { IscsiLun *iscsilun = opaque; - qemu_mutex_lock(&iscsilun->mutex); + WITH_QEMU_LOCK_GUARD(&iscsilun->mutex) { + /* check for timed out requests */ + iscsi_service(iscsilun->iscsi, 0); - /* check for timed out requests */ - iscsi_service(iscsilun->iscsi, 0); + if (iscsilun->request_timed_out) { + iscsilun->request_timed_out = false; + iscsi_reconnect(iscsilun->iscsi); + } - if (iscsilun->request_timed_out) { - iscsilun->request_timed_out = false; - iscsi_reconnect(iscsilun->iscsi); + /* + * newer versions of libiscsi may return zero events. Ensure we are + * able to return to service once this situation changes. + */ + iscsi_set_events(iscsilun); } - /* newer versions of libiscsi may return zero events. Ensure we are able - * to return to service once this situation changes. */ - iscsi_set_events(iscsilun); - - qemu_mutex_unlock(&iscsilun->mutex); - timer_mod(iscsilun->event_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + EVENT_INTERVAL); } @@ -1526,12 +1524,10 @@ static void iscsi_detach_aio_context(BlockDriverState *bs) iscsilun->events = 0; if (iscsilun->nop_timer) { - timer_del(iscsilun->nop_timer); timer_free(iscsilun->nop_timer); iscsilun->nop_timer = NULL; } if (iscsilun->event_timer) { - timer_del(iscsilun->event_timer); timer_free(iscsilun->event_timer); iscsilun->event_timer = NULL; } diff --git a/block/meson.build b/block/meson.build new file mode 100644 index 0000000000..d21990ec95 --- /dev/null +++ b/block/meson.build @@ -0,0 +1,128 @@ +block_ss.add(genh) +block_ss.add(files( + 'accounting.c', + 'aio_task.c', + 'amend.c', + 'backup.c', + 'backup-top.c', + 'blkdebug.c', + 'blklogwrites.c', + 'blkverify.c', + 'block-backend.c', + 'block-copy.c', + 'commit.c', + 'copy-on-read.c', + 'preallocate.c', + 'create.c', + 'crypto.c', + 'dirty-bitmap.c', + 'filter-compress.c', + 'io.c', + 'mirror.c', + 'nbd.c', + 'null.c', + 'qapi.c', + 'qcow2-bitmap.c', + 'qcow2-cache.c', + 'qcow2-cluster.c', + 'qcow2-refcount.c', + 'qcow2-snapshot.c', + 'qcow2-threads.c', + 'qcow2.c', + 'quorum.c', + 'raw-format.c', + 'snapshot.c', + 'throttle-groups.c', + 'throttle.c', + 'vhdx-endian.c', + 'vhdx-log.c', + 'vhdx.c', + 'vmdk.c', + 'vpc.c', + 'write-threshold.c', +), zstd, zlib, gnutls) + +softmmu_ss.add(when: 'CONFIG_TCG', if_true: files('blkreplay.c')) + +block_ss.add(when: 'CONFIG_QCOW1', if_true: files('qcow.c')) +block_ss.add(when: 'CONFIG_VDI', if_true: files('vdi.c')) +block_ss.add(when: 'CONFIG_CLOOP', if_true: files('cloop.c')) +block_ss.add(when: 'CONFIG_BOCHS', if_true: files('bochs.c')) +block_ss.add(when: 'CONFIG_VVFAT', if_true: files('vvfat.c')) +block_ss.add(when: 'CONFIG_DMG', if_true: files('dmg.c')) +block_ss.add(when: 'CONFIG_QED', if_true: files( + 'qed-check.c', + 'qed-cluster.c', + 'qed-l2-cache.c', + 'qed-table.c', + 'qed.c', +)) +block_ss.add(when: [libxml2, 'CONFIG_PARALLELS'], + if_true: files('parallels.c', 'parallels-ext.c')) +block_ss.add(when: 'CONFIG_WIN32', if_true: files('file-win32.c', 'win32-aio.c')) +block_ss.add(when: 'CONFIG_POSIX', if_true: [files('file-posix.c'), coref, iokit]) +block_ss.add(when: libiscsi, if_true: files('iscsi-opts.c')) +block_ss.add(when: 'CONFIG_LINUX', if_true: files('nvme.c')) +block_ss.add(when: 'CONFIG_REPLICATION', if_true: files('replication.c')) +block_ss.add(when: 'CONFIG_SHEEPDOG', if_true: files('sheepdog.c')) +block_ss.add(when: ['CONFIG_LINUX_AIO', libaio], if_true: files('linux-aio.c')) +block_ss.add(when: ['CONFIG_LINUX_IO_URING', linux_io_uring], if_true: files('io_uring.c')) + +block_modules = {} + +modsrc = [] +foreach m : [ + [curl, 'curl', [curl, glib], 'curl.c'], + [glusterfs, 'gluster', glusterfs, 'gluster.c'], + [libiscsi, 'iscsi', libiscsi, 'iscsi.c'], + [libnfs, 'nfs', libnfs, 'nfs.c'], + [libssh, 'ssh', libssh, 'ssh.c'], + [rbd, 'rbd', rbd, 'rbd.c'], +] + if m[0].found() + if enable_modules + modsrc += files(m[3]) + endif + module_ss = ss.source_set() + module_ss.add(when: m[2], if_true: files(m[3])) + block_modules += {m[1] : module_ss} + endif +endforeach + +# those are not exactly regular block modules, so treat them apart +if 'CONFIG_DMG' in config_host + foreach m : [ + [liblzfse, 'dmg-lzfse', liblzfse, 'dmg-lzfse.c'], + [libbzip2, 'dmg-bz2', [glib, libbzip2], 'dmg-bz2.c'] + ] + if m[0].found() + module_ss = ss.source_set() + module_ss.add(when: m[2], if_true: files(m[3])) + block_modules += {m[1] : module_ss} + endif + endforeach +endif + +module_block_py = find_program('../scripts/modules/module_block.py') +module_block_h = custom_target('module_block.h', + output: 'module_block.h', + input: modsrc, + command: [module_block_py, '@OUTPUT0@', modsrc]) +block_ss.add(module_block_h) + +wrapper_py = find_program('../scripts/block-coroutine-wrapper.py') +block_gen_c = custom_target('block-gen.c', + output: 'block-gen.c', + input: files('../include/block/block.h', + 'coroutines.h'), + command: [wrapper_py, '@OUTPUT@', '@INPUT@']) +block_ss.add(block_gen_c) + +block_ss.add(files('stream.c')) + +softmmu_ss.add(files('qapi-sysemu.c')) + +subdir('export') +subdir('monitor') + +modules += {'block': block_modules} diff --git a/block/mirror.c b/block/mirror.c index e8e8844afc..5a71bd8bbc 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -42,6 +42,7 @@ typedef struct MirrorBlockJob { BlockBackend *target; BlockDriverState *mirror_top_bs; BlockDriverState *base; + BlockDriverState *base_overlay; /* The name of the graph node to replace */ char *replaces; @@ -88,6 +89,7 @@ typedef struct MirrorBlockJob { typedef struct MirrorBDSOpaque { MirrorBlockJob *job; bool stop; + bool is_commit; } MirrorBDSOpaque; struct MirrorOp { @@ -677,14 +679,24 @@ static int mirror_exit_common(Job *job) &error_abort); if (!abort && s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) { BlockDriverState *backing = s->is_none_mode ? src : s->base; - if (backing_bs(target_bs) != backing) { - bdrv_set_backing_hd(target_bs, backing, &local_err); + BlockDriverState *unfiltered_target = bdrv_skip_filters(target_bs); + + if (bdrv_cow_bs(unfiltered_target) != backing) { + bdrv_set_backing_hd(unfiltered_target, backing, &local_err); if (local_err) { error_report_err(local_err); local_err = NULL; ret = -EPERM; } } + } else if (!abort && s->backing_mode == MIRROR_OPEN_BACKING_CHAIN) { + assert(!bdrv_backing_chain_next(target_bs)); + ret = bdrv_open_backing_file(bdrv_skip_filters(target_bs), NULL, + "backing", &local_err); + if (ret < 0) { + error_report_err(local_err); + local_err = NULL; + } } if (s->to_replace) { @@ -740,7 +752,7 @@ static int mirror_exit_common(Job *job) * valid. */ block_job_remove_all_bdrv(bjob); - bdrv_replace_node(mirror_top_bs, backing_bs(mirror_top_bs), &error_abort); + bdrv_replace_node(mirror_top_bs, mirror_top_bs->backing->bs, &error_abort); /* We just changed the BDS the job BB refers to (with either or both of the * bdrv_replace_node() calls), so switch the BB back so the cleanup does @@ -786,7 +798,6 @@ static void coroutine_fn mirror_throttle(MirrorBlockJob *s) static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) { int64_t offset; - BlockDriverState *base = s->base; BlockDriverState *bs = s->mirror_top_bs->backing->bs; BlockDriverState *target_bs = blk_bs(s->target); int ret; @@ -837,13 +848,14 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) return 0; } - ret = bdrv_is_allocated_above(bs, base, false, offset, bytes, &count); + ret = bdrv_is_allocated_above(bs, s->base_overlay, true, offset, bytes, + &count); if (ret < 0) { return ret; } assert(count); - if (ret == 1) { + if (ret > 0) { bdrv_set_dirty_bitmap(s->dirty_bitmap, offset, count); } offset += count; @@ -936,7 +948,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) } else { s->target_cluster_size = BDRV_SECTOR_SIZE; } - if (backing_filename[0] && !target_bs->backing && + if (backing_filename[0] && !bdrv_backing_chain_next(target_bs) && s->granularity < s->target_cluster_size) { s->buf_size = MAX(s->buf_size, s->target_cluster_size); s->cow_bitmap = bitmap_new(length); @@ -1103,9 +1115,6 @@ immediate_exit: static void mirror_complete(Job *job, Error **errp) { MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); - BlockDriverState *target; - - target = blk_bs(s->target); if (!s->synced) { error_setg(errp, "The active block job '%s' cannot be completed", @@ -1113,16 +1122,6 @@ static void mirror_complete(Job *job, Error **errp) return; } - if (s->backing_mode == MIRROR_OPEN_BACKING_CHAIN) { - int ret; - - assert(!target->backing); - ret = bdrv_open_backing_file(target, NULL, "backing", errp); - if (ret < 0) { - return; - } - } - /* block all operations on to_replace bs */ if (s->replaces) { AioContext *replace_aio_context; @@ -1149,7 +1148,11 @@ static void mirror_complete(Job *job, Error **errp) } s->should_complete = true; - job_enter(job); + + /* If the job is paused, it will be re-entered when it is resumed */ + if (!job->paused) { + job_enter(job); + } } static void coroutine_fn mirror_pause(Job *job) @@ -1175,6 +1178,14 @@ static bool mirror_drained_poll(BlockJob *job) return !!s->in_flight; } +static void mirror_cancel(Job *job) +{ + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); + BlockDriverState *target = blk_bs(s->target); + + bdrv_cancel_in_flight(target); +} + static const BlockJobDriver mirror_job_driver = { .job_driver = { .instance_size = sizeof(MirrorBlockJob), @@ -1186,6 +1197,7 @@ static const BlockJobDriver mirror_job_driver = { .abort = mirror_abort, .pause = mirror_pause, .complete = mirror_complete, + .cancel = mirror_cancel, }, .drained_poll = mirror_drained_poll, }; @@ -1509,13 +1521,27 @@ static void bdrv_mirror_top_child_perm(BlockDriverState *bs, BdrvChild *c, return; } - /* Must be able to forward guest writes to the real image */ - *nperm = 0; - if (perm & BLK_PERM_WRITE) { - *nperm |= BLK_PERM_WRITE; - } + bdrv_default_perms(bs, c, role, reopen_queue, + perm, shared, nperm, nshared); - *nshared = BLK_PERM_ALL; + if (s->is_commit) { + /* + * For commit jobs, we cannot take CONSISTENT_READ, because + * that permission is unshared for everything above the base + * node (except for filters on the base node). + * We also have to force-share the WRITE permission, or + * otherwise we would block ourselves at the base node (if + * writes are blocked for a node, they are also blocked for + * its backing file). + * (We could also share RESIZE, because it may be needed for + * the target if its size is less than the top node's; but + * bdrv_default_perms_for_cow() automatically shares RESIZE + * for backing nodes if WRITE is shared, so there is no need + * to do it here.) + */ + *nperm &= ~BLK_PERM_CONSISTENT_READ; + *nshared |= BLK_PERM_WRITE; + } } /* Dummy node that provides consistent read to its users without requiring it @@ -1527,7 +1553,6 @@ static BlockDriver bdrv_mirror_top = { .bdrv_co_pwrite_zeroes = bdrv_mirror_top_pwrite_zeroes, .bdrv_co_pdiscard = bdrv_mirror_top_pdiscard, .bdrv_co_flush = bdrv_mirror_top_flush, - .bdrv_co_block_status = bdrv_co_block_status_from_backing, .bdrv_refresh_filename = bdrv_mirror_top_refresh_filename, .bdrv_child_perm = bdrv_mirror_top_child_perm, @@ -1555,9 +1580,8 @@ static BlockJob *mirror_start_job( MirrorBlockJob *s; MirrorBDSOpaque *bs_opaque; BlockDriverState *mirror_top_bs; - bool target_graph_mod; bool target_is_backing; - Error *local_err = NULL; + uint64_t target_perms, target_shared_perms; int ret; if (granularity == 0) { @@ -1575,11 +1599,13 @@ static BlockJob *mirror_start_job( buf_size = DEFAULT_MIRROR_BUF_SIZE; } - if (bs == target) { + if (bdrv_skip_filters(bs) == bdrv_skip_filters(target)) { error_setg(errp, "Can't mirror node into itself"); return NULL; } + target_is_backing = bdrv_chain_contains(bs, target); + /* In the case of active commit, add dummy driver to provide consistent * reads on the top, while disabling it in the intermediate nodes, and make * the backing chain writable. */ @@ -1602,16 +1628,17 @@ static BlockJob *mirror_start_job( bs_opaque = g_new0(MirrorBDSOpaque, 1); mirror_top_bs->opaque = bs_opaque; + bs_opaque->is_commit = target_is_backing; + /* bdrv_append takes ownership of the mirror_top_bs reference, need to keep * it alive until block_job_create() succeeds even if bs has no parent. */ bdrv_ref(mirror_top_bs); bdrv_drained_begin(bs); - bdrv_append(mirror_top_bs, bs, &local_err); + ret = bdrv_append(mirror_top_bs, bs, errp); bdrv_drained_end(bs); - if (local_err) { + if (ret < 0) { bdrv_unref(mirror_top_bs); - error_propagate(errp, local_err); return NULL; } @@ -1639,15 +1666,49 @@ static BlockJob *mirror_start_job( * In the case of active commit, things look a bit different, though, * because the target is an already populated backing file in active use. * We can allow anything except resize there.*/ - target_is_backing = bdrv_chain_contains(bs, target); - target_graph_mod = (backing_mode != MIRROR_LEAVE_BACKING_CHAIN); + + target_perms = BLK_PERM_WRITE; + target_shared_perms = BLK_PERM_WRITE_UNCHANGED; + + if (target_is_backing) { + int64_t bs_size, target_size; + bs_size = bdrv_getlength(bs); + if (bs_size < 0) { + error_setg_errno(errp, -bs_size, + "Could not inquire top image size"); + goto fail; + } + + target_size = bdrv_getlength(target); + if (target_size < 0) { + error_setg_errno(errp, -target_size, + "Could not inquire base image size"); + goto fail; + } + + if (target_size < bs_size) { + target_perms |= BLK_PERM_RESIZE; + } + + target_shared_perms |= BLK_PERM_CONSISTENT_READ + | BLK_PERM_WRITE + | BLK_PERM_GRAPH_MOD; + } else if (bdrv_chain_contains(bs, bdrv_skip_filters(target))) { + /* + * We may want to allow this in the future, but it would + * require taking some extra care. + */ + error_setg(errp, "Cannot mirror to a filter on top of a node in the " + "source's backing chain"); + goto fail; + } + + if (backing_mode != MIRROR_LEAVE_BACKING_CHAIN) { + target_perms |= BLK_PERM_GRAPH_MOD; + } + s->target = blk_new(s->common.job.aio_context, - BLK_PERM_WRITE | BLK_PERM_RESIZE | - (target_graph_mod ? BLK_PERM_GRAPH_MOD : 0), - BLK_PERM_WRITE_UNCHANGED | - (target_is_backing ? BLK_PERM_CONSISTENT_READ | - BLK_PERM_WRITE | - BLK_PERM_GRAPH_MOD : 0)); + target_perms, target_shared_perms); ret = blk_insert_bs(s->target, target, errp); if (ret < 0) { goto fail; @@ -1672,6 +1733,7 @@ static BlockJob *mirror_start_job( s->zero_target = zero_target; s->copy_mode = copy_mode; s->base = base; + s->base_overlay = bdrv_find_overlay(bs, base); s->granularity = granularity; s->buf_size = ROUND_UP(buf_size, granularity); s->unmap = unmap; @@ -1702,15 +1764,39 @@ static BlockJob *mirror_start_job( /* In commit_active_start() all intermediate nodes disappear, so * any jobs in them must be blocked */ if (target_is_backing) { - BlockDriverState *iter; - for (iter = backing_bs(bs); iter != target; iter = backing_bs(iter)) { - /* XXX BLK_PERM_WRITE needs to be allowed so we don't block - * ourselves at s->base (if writes are blocked for a node, they are - * also blocked for its backing file). The other options would be a - * second filter driver above s->base (== target). */ + BlockDriverState *iter, *filtered_target; + uint64_t iter_shared_perms; + + /* + * The topmost node with + * bdrv_skip_filters(filtered_target) == bdrv_skip_filters(target) + */ + filtered_target = bdrv_cow_bs(bdrv_find_overlay(bs, target)); + + assert(bdrv_skip_filters(filtered_target) == + bdrv_skip_filters(target)); + + /* + * XXX BLK_PERM_WRITE needs to be allowed so we don't block + * ourselves at s->base (if writes are blocked for a node, they are + * also blocked for its backing file). The other options would be a + * second filter driver above s->base (== target). + */ + iter_shared_perms = BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE; + + for (iter = bdrv_filter_or_cow_bs(bs); iter != target; + iter = bdrv_filter_or_cow_bs(iter)) + { + if (iter == filtered_target) { + /* + * From here on, all nodes are filters on the base. + * This allows us to share BLK_PERM_CONSISTENT_READ. + */ + iter_shared_perms |= BLK_PERM_CONSISTENT_READ; + } + ret = block_job_add_bdrv(&s->common, "intermediate node", iter, 0, - BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE, - errp); + iter_shared_perms, errp); if (ret < 0) { goto fail; } @@ -1746,7 +1832,7 @@ fail: bs_opaque->stop = true; bdrv_child_refresh_perms(mirror_top_bs, mirror_top_bs->backing, &error_abort); - bdrv_replace_node(mirror_top_bs, backing_bs(mirror_top_bs), &error_abort); + bdrv_replace_node(mirror_top_bs, mirror_top_bs->backing->bs, &error_abort); bdrv_unref(mirror_top_bs); @@ -1774,7 +1860,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs, return; } is_none_mode = mode == MIRROR_SYNC_MODE_NONE; - base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL; + base = mode == MIRROR_SYNC_MODE_TOP ? bdrv_backing_chain_next(bs) : NULL; mirror_start_job(job_id, bs, creation_flags, target, replaces, speed, granularity, buf_size, backing_mode, zero_target, on_source_error, on_target_error, unmap, NULL, NULL, @@ -1790,8 +1876,7 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, bool auto_complete, Error **errp) { bool base_read_only; - Error *local_err = NULL; - BlockJob *ret; + BlockJob *job; base_read_only = bdrv_is_read_only(base); @@ -1801,19 +1886,18 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, } } - ret = mirror_start_job( + job = mirror_start_job( job_id, bs, creation_flags, base, NULL, speed, 0, 0, MIRROR_LEAVE_BACKING_CHAIN, false, on_error, on_error, true, cb, opaque, &commit_active_job_driver, false, base, auto_complete, filter_node_name, false, MIRROR_COPY_MODE_BACKGROUND, - &local_err); - if (local_err) { - error_propagate(errp, local_err); + errp); + if (!job) { goto error_restore_flags; } - return ret; + return job; error_restore_flags: /* ignore error and errp for bdrv_reopen, because we want to propagate diff --git a/block/monitor/Makefile.objs b/block/monitor/Makefile.objs deleted file mode 100644 index 39acf85022..0000000000 --- a/block/monitor/Makefile.objs +++ /dev/null @@ -1,2 +0,0 @@ -common-obj-y += block-hmp-cmds.o -block-obj-y += bitmap-qmp-cmds.o diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c index 4c8c375172..ebf1033f31 100644 --- a/block/monitor/block-hmp-cmds.c +++ b/block/monitor/block-hmp-cmds.c @@ -40,6 +40,7 @@ #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "qapi/qapi-commands-block.h" +#include "qapi/qapi-commands-block-export.h" #include "qapi/qmp/qdict.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" @@ -217,7 +218,7 @@ void hmp_commit(Monitor *mon, const QDict *qdict) return; } - bs = blk_bs(blk); + bs = bdrv_skip_implicit_filters(blk_bs(blk)); aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); @@ -397,7 +398,7 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) Error *local_err = NULL; BlockInfoList *block_list, *info; SocketAddress *addr; - BlockExportNbd export; + NbdServerAddOptions export; if (writable && !all) { error_setg(&local_err, "-w only valid together with -a"); @@ -410,7 +411,7 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) goto exit; } - nbd_server_start(addr, NULL, NULL, &local_err); + nbd_server_start(addr, NULL, NULL, 0, &local_err); qapi_free_SocketAddress(addr); if (local_err != NULL) { goto exit; @@ -430,7 +431,7 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) continue; } - export = (BlockExportNbd) { + export = (NbdServerAddOptions) { .device = info->value->device, .has_writable = true, .writable = writable, @@ -457,7 +458,7 @@ void hmp_nbd_server_add(Monitor *mon, const QDict *qdict) bool writable = qdict_get_try_bool(qdict, "writable", false); Error *local_err = NULL; - BlockExportNbd export = { + NbdServerAddOptions export = { .device = (char *) device, .has_name = !!name, .name = (char *) name, @@ -475,8 +476,8 @@ void hmp_nbd_server_remove(Monitor *mon, const QDict *qdict) bool force = qdict_get_try_bool(qdict, "force", false); Error *err = NULL; - /* Rely on NBD_SERVER_REMOVE_MODE_SAFE being the default */ - qmp_nbd_server_remove(name, force, NBD_SERVER_REMOVE_MODE_HARD, &err); + /* Rely on BLOCK_EXPORT_REMOVE_MODE_SAFE being the default */ + qmp_nbd_server_remove(name, force, BLOCK_EXPORT_REMOVE_MODE_HARD, &err); hmp_handle_error(mon, err); } @@ -506,23 +507,14 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict) int64_t speed = qdict_get_try_int(qdict, "speed", 0); qmp_block_stream(true, device, device, base != NULL, base, false, NULL, - false, NULL, qdict_haskey(qdict, "speed"), speed, true, - BLOCKDEV_ON_ERROR_REPORT, false, false, false, false, - &error); + false, NULL, false, NULL, + qdict_haskey(qdict, "speed"), speed, true, + BLOCKDEV_ON_ERROR_REPORT, false, NULL, false, false, false, + false, &error); hmp_handle_error(mon, error); } -void hmp_block_passwd(Monitor *mon, const QDict *qdict) -{ - const char *device = qdict_get_str(qdict, "device"); - const char *password = qdict_get_str(qdict, "password"); - Error *err = NULL; - - qmp_block_passwd(true, device, false, NULL, password, &err); - hmp_handle_error(mon, err); -} - void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict) { Error *err = NULL; @@ -898,10 +890,11 @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict) ImageEntry *image_entry, *next_ie; SnapshotEntry *snapshot_entry; + Error *err = NULL; - bs = bdrv_all_find_vmstate_bs(); + bs = bdrv_all_find_vmstate_bs(NULL, false, NULL, &err); if (!bs) { - monitor_printf(mon, "No available block device supports snapshots\n"); + error_report_err(err); return; } aio_context = bdrv_get_aio_context(bs); @@ -951,7 +944,7 @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict) total = 0; for (i = 0; i < nb_sns; i++) { SnapshotEntry *next_sn; - if (bdrv_all_find_snapshot(sn_tab[i].name, &bs1) == 0) { + if (bdrv_all_has_snapshot(sn_tab[i].name, false, NULL, NULL) == 1) { global_snapshots[total] = i; total++; QTAILQ_FOREACH(image_entry, &image_list, next) { diff --git a/block/monitor/meson.build b/block/monitor/meson.build new file mode 100644 index 0000000000..374aac1140 --- /dev/null +++ b/block/monitor/meson.build @@ -0,0 +1,2 @@ +softmmu_ss.add(files('block-hmp-cmds.c')) +block_ss.add(files('bitmap-qmp-cmds.c')) diff --git a/block/nbd.c b/block/nbd.c index 7bb881fef4..1d4668d42d 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -35,14 +35,18 @@ #include "qemu/option.h" #include "qemu/cutils.h" #include "qemu/main-loop.h" +#include "qemu/atomic.h" #include "qapi/qapi-visit-sockets.h" #include "qapi/qmp/qstring.h" +#include "qapi/clone-visitor.h" #include "block/qdict.h" #include "block/nbd.h" #include "block/block_int.h" +#include "qemu/yank.h" + #define EN_OPTSTR ":exportname=" #define MAX_NBD_REQUESTS 16 @@ -62,6 +66,47 @@ typedef enum NBDClientState { NBD_CLIENT_QUIT } NBDClientState; +typedef enum NBDConnectThreadState { + /* No thread, no pending results */ + CONNECT_THREAD_NONE, + + /* Thread is running, no results for now */ + CONNECT_THREAD_RUNNING, + + /* + * Thread is running, but requestor exited. Thread should close + * the new socket and free the connect state on exit. + */ + CONNECT_THREAD_RUNNING_DETACHED, + + /* Thread finished, results are stored in a state */ + CONNECT_THREAD_FAIL, + CONNECT_THREAD_SUCCESS +} NBDConnectThreadState; + +typedef struct NBDConnectThread { + /* Initialization constants */ + SocketAddress *saddr; /* address to connect to */ + /* + * Bottom half to schedule on completion. Scheduled only if bh_ctx is not + * NULL + */ + QEMUBHFunc *bh_func; + void *bh_opaque; + + /* + * Result of last attempt. Valid in FAIL and SUCCESS states. + * If you want to steal error, don't forget to set pointer to NULL. + */ + QIOChannelSocket *sioc; + Error *err; + + /* state and bh_ctx are protected by mutex */ + QemuMutex mutex; + NBDConnectThreadState state; /* current state of the thread */ + AioContext *bh_ctx; /* where to schedule bh (NULL means don't schedule) */ +} NBDConnectThread; + typedef struct BDRVNBDState { QIOChannelSocket *sioc; /* The master data channel */ QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */ @@ -80,6 +125,8 @@ typedef struct BDRVNBDState { Error *connect_err; bool wait_in_flight; + QEMUTimer *reconnect_delay_timer; + NBDClientRequest requests[MAX_NBD_REQUESTS]; NBDReply reply; BlockDriverState *bs; @@ -91,12 +138,19 @@ typedef struct BDRVNBDState { QCryptoTLSCreds *tlscreds; const char *hostname; char *x_dirty_bitmap; + bool alloc_depth; + + bool wait_connect; + NBDConnectThread *connect_thread; } BDRVNBDState; -static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr, - Error **errp); -static int nbd_client_handshake(BlockDriverState *bs, QIOChannelSocket *sioc, - Error **errp); +static int nbd_establish_connection(BlockDriverState *bs, SocketAddress *saddr, + Error **errp); +static int nbd_co_establish_connection(BlockDriverState *bs, Error **errp); +static void nbd_co_establish_connection_cancel(BlockDriverState *bs, + bool detach); +static int nbd_client_handshake(BlockDriverState *bs, Error **errp); +static void nbd_yank(void *opaque); static void nbd_clear_bdrvstate(BDRVNBDState *s) { @@ -114,12 +168,12 @@ static void nbd_clear_bdrvstate(BDRVNBDState *s) static void nbd_channel_error(BDRVNBDState *s, int ret) { if (ret == -EIO) { - if (s->state == NBD_CLIENT_CONNECTED) { + if (qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTED) { s->state = s->reconnect_delay ? NBD_CLIENT_CONNECTING_WAIT : NBD_CLIENT_CONNECTING_NOWAIT; } } else { - if (s->state == NBD_CLIENT_CONNECTED) { + if (qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTED) { qio_channel_shutdown(s->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); } s->state = NBD_CLIENT_QUIT; @@ -139,11 +193,56 @@ static void nbd_recv_coroutines_wake_all(BDRVNBDState *s) } } +static void reconnect_delay_timer_del(BDRVNBDState *s) +{ + if (s->reconnect_delay_timer) { + timer_free(s->reconnect_delay_timer); + s->reconnect_delay_timer = NULL; + } +} + +static void reconnect_delay_timer_cb(void *opaque) +{ + BDRVNBDState *s = opaque; + + if (qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTING_WAIT) { + s->state = NBD_CLIENT_CONNECTING_NOWAIT; + while (qemu_co_enter_next(&s->free_sema, NULL)) { + /* Resume all queued requests */ + } + } + + reconnect_delay_timer_del(s); +} + +static void reconnect_delay_timer_init(BDRVNBDState *s, uint64_t expire_time_ns) +{ + if (qatomic_load_acquire(&s->state) != NBD_CLIENT_CONNECTING_WAIT) { + return; + } + + assert(!s->reconnect_delay_timer); + s->reconnect_delay_timer = aio_timer_new(bdrv_get_aio_context(s->bs), + QEMU_CLOCK_REALTIME, + SCALE_NS, + reconnect_delay_timer_cb, s); + timer_mod(s->reconnect_delay_timer, expire_time_ns); +} + static void nbd_client_detach_aio_context(BlockDriverState *bs) { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; - qio_channel_detach_aio_context(QIO_CHANNEL(s->ioc)); + /* Timer is deleted in nbd_client_co_drain_begin() */ + assert(!s->reconnect_delay_timer); + /* + * If reconnect is in progress we may have no ->ioc. It will be + * re-instantiated in the proper aio context once the connection is + * reestablished. + */ + if (s->ioc) { + qio_channel_detach_aio_context(QIO_CHANNEL(s->ioc)); + } } static void nbd_client_attach_aio_context_bh(void *opaque) @@ -151,13 +250,15 @@ static void nbd_client_attach_aio_context_bh(void *opaque) BlockDriverState *bs = opaque; BDRVNBDState *s = (BDRVNBDState *)bs->opaque; - /* - * The node is still drained, so we know the coroutine has yielded in - * nbd_read_eof(), the only place where bs->in_flight can reach 0, or it is - * entered for the first time. Both places are safe for entering the - * coroutine. - */ - qemu_aio_coroutine_enter(bs->aio_context, s->connection_co); + if (s->connection_co) { + /* + * The node is still drained, so we know the coroutine has yielded in + * nbd_read_eof(), the only place where bs->in_flight can reach 0, or + * it is entered for the first time. Both places are safe for entering + * the coroutine. + */ + qemu_aio_coroutine_enter(bs->aio_context, s->connection_co); + } bdrv_dec_in_flight(bs); } @@ -170,7 +271,7 @@ static void nbd_client_attach_aio_context(BlockDriverState *bs, * s->connection_co is either yielded from nbd_receive_reply or from * nbd_co_reconnect_loop() */ - if (s->state == NBD_CLIENT_CONNECTED) { + if (qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTED) { qio_channel_attach_aio_context(QIO_CHANNEL(s->ioc), new_context); } @@ -191,6 +292,15 @@ static void coroutine_fn nbd_client_co_drain_begin(BlockDriverState *bs) if (s->connection_co_sleep_ns_state) { qemu_co_sleep_wake(s->connection_co_sleep_ns_state); } + + nbd_co_establish_connection_cancel(bs, false); + + reconnect_delay_timer_del(s); + + if (qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTING_WAIT) { + s->state = NBD_CLIENT_CONNECTING_NOWAIT; + qemu_co_queue_restart_all(&s->free_sema); + } } static void coroutine_fn nbd_client_co_drain_end(BlockDriverState *bs) @@ -223,6 +333,7 @@ static void nbd_teardown_connection(BlockDriverState *bs) if (s->connection_co_sleep_ns_state) { qemu_co_sleep_wake(s->connection_co_sleep_ns_state); } + nbd_co_establish_connection_cancel(bs, true); } if (qemu_in_coroutine()) { s->teardown_co = qemu_coroutine_self(); @@ -237,20 +348,248 @@ static void nbd_teardown_connection(BlockDriverState *bs) static bool nbd_client_connecting(BDRVNBDState *s) { - return s->state == NBD_CLIENT_CONNECTING_WAIT || - s->state == NBD_CLIENT_CONNECTING_NOWAIT; + NBDClientState state = qatomic_load_acquire(&s->state); + return state == NBD_CLIENT_CONNECTING_WAIT || + state == NBD_CLIENT_CONNECTING_NOWAIT; } static bool nbd_client_connecting_wait(BDRVNBDState *s) { - return s->state == NBD_CLIENT_CONNECTING_WAIT; + return qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTING_WAIT; +} + +static void connect_bh(void *opaque) +{ + BDRVNBDState *state = opaque; + + assert(state->wait_connect); + state->wait_connect = false; + aio_co_wake(state->connection_co); +} + +static void nbd_init_connect_thread(BDRVNBDState *s) +{ + s->connect_thread = g_new(NBDConnectThread, 1); + + *s->connect_thread = (NBDConnectThread) { + .saddr = QAPI_CLONE(SocketAddress, s->saddr), + .state = CONNECT_THREAD_NONE, + .bh_func = connect_bh, + .bh_opaque = s, + }; + + qemu_mutex_init(&s->connect_thread->mutex); +} + +static void nbd_free_connect_thread(NBDConnectThread *thr) +{ + if (thr->sioc) { + qio_channel_close(QIO_CHANNEL(thr->sioc), NULL); + } + error_free(thr->err); + qapi_free_SocketAddress(thr->saddr); + g_free(thr); +} + +static void *connect_thread_func(void *opaque) +{ + NBDConnectThread *thr = opaque; + int ret; + bool do_free = false; + + thr->sioc = qio_channel_socket_new(); + + error_free(thr->err); + thr->err = NULL; + ret = qio_channel_socket_connect_sync(thr->sioc, thr->saddr, &thr->err); + if (ret < 0) { + object_unref(OBJECT(thr->sioc)); + thr->sioc = NULL; + } + + qemu_mutex_lock(&thr->mutex); + + switch (thr->state) { + case CONNECT_THREAD_RUNNING: + thr->state = ret < 0 ? CONNECT_THREAD_FAIL : CONNECT_THREAD_SUCCESS; + if (thr->bh_ctx) { + aio_bh_schedule_oneshot(thr->bh_ctx, thr->bh_func, thr->bh_opaque); + + /* play safe, don't reuse bh_ctx on further connection attempts */ + thr->bh_ctx = NULL; + } + break; + case CONNECT_THREAD_RUNNING_DETACHED: + do_free = true; + break; + default: + abort(); + } + + qemu_mutex_unlock(&thr->mutex); + + if (do_free) { + nbd_free_connect_thread(thr); + } + + return NULL; +} + +static int coroutine_fn +nbd_co_establish_connection(BlockDriverState *bs, Error **errp) +{ + int ret; + QemuThread thread; + BDRVNBDState *s = bs->opaque; + NBDConnectThread *thr = s->connect_thread; + + if (!thr) { + /* detached */ + return -1; + } + + qemu_mutex_lock(&thr->mutex); + + switch (thr->state) { + case CONNECT_THREAD_FAIL: + case CONNECT_THREAD_NONE: + error_free(thr->err); + thr->err = NULL; + thr->state = CONNECT_THREAD_RUNNING; + qemu_thread_create(&thread, "nbd-connect", + connect_thread_func, thr, QEMU_THREAD_DETACHED); + break; + case CONNECT_THREAD_SUCCESS: + /* Previous attempt finally succeeded in background */ + thr->state = CONNECT_THREAD_NONE; + s->sioc = thr->sioc; + thr->sioc = NULL; + yank_register_function(BLOCKDEV_YANK_INSTANCE(bs->node_name), + nbd_yank, bs); + qemu_mutex_unlock(&thr->mutex); + return 0; + case CONNECT_THREAD_RUNNING: + /* Already running, will wait */ + break; + default: + abort(); + } + + thr->bh_ctx = qemu_get_current_aio_context(); + + qemu_mutex_unlock(&thr->mutex); + + + /* + * We are going to wait for connect-thread finish, but + * nbd_client_co_drain_begin() can interrupt. + * + * Note that wait_connect variable is not visible for connect-thread. It + * doesn't need mutex protection, it used only inside home aio context of + * bs. + */ + s->wait_connect = true; + qemu_coroutine_yield(); + + if (!s->connect_thread) { + /* detached */ + return -1; + } + assert(thr == s->connect_thread); + + qemu_mutex_lock(&thr->mutex); + + switch (thr->state) { + case CONNECT_THREAD_SUCCESS: + case CONNECT_THREAD_FAIL: + thr->state = CONNECT_THREAD_NONE; + error_propagate(errp, thr->err); + thr->err = NULL; + s->sioc = thr->sioc; + thr->sioc = NULL; + if (s->sioc) { + yank_register_function(BLOCKDEV_YANK_INSTANCE(bs->node_name), + nbd_yank, bs); + } + ret = (s->sioc ? 0 : -1); + break; + case CONNECT_THREAD_RUNNING: + case CONNECT_THREAD_RUNNING_DETACHED: + /* + * Obviously, drained section wants to start. Report the attempt as + * failed. Still connect thread is executing in background, and its + * result may be used for next connection attempt. + */ + ret = -1; + error_setg(errp, "Connection attempt cancelled by other operation"); + break; + + case CONNECT_THREAD_NONE: + /* + * Impossible. We've seen this thread running. So it should be + * running or at least give some results. + */ + abort(); + + default: + abort(); + } + + qemu_mutex_unlock(&thr->mutex); + + return ret; +} + +/* + * nbd_co_establish_connection_cancel + * Cancel nbd_co_establish_connection asynchronously: it will finish soon, to + * allow drained section to begin. + * + * If detach is true, also cleanup the state (or if thread is running, move it + * to CONNECT_THREAD_RUNNING_DETACHED state). s->connect_thread becomes NULL if + * detach is true. + */ +static void nbd_co_establish_connection_cancel(BlockDriverState *bs, + bool detach) +{ + BDRVNBDState *s = bs->opaque; + NBDConnectThread *thr = s->connect_thread; + bool wake = false; + bool do_free = false; + + qemu_mutex_lock(&thr->mutex); + + if (thr->state == CONNECT_THREAD_RUNNING) { + /* We can cancel only in running state, when bh is not yet scheduled */ + thr->bh_ctx = NULL; + if (s->wait_connect) { + s->wait_connect = false; + wake = true; + } + if (detach) { + thr->state = CONNECT_THREAD_RUNNING_DETACHED; + s->connect_thread = NULL; + } + } else if (detach) { + do_free = true; + } + + qemu_mutex_unlock(&thr->mutex); + + if (do_free) { + nbd_free_connect_thread(thr); + s->connect_thread = NULL; + } + + if (wake) { + aio_co_wake(s->connection_co); + } } static coroutine_fn void nbd_reconnect_attempt(BDRVNBDState *s) { int ret; Error *local_err = NULL; - QIOChannelSocket *sioc; if (!nbd_client_connecting(s)) { return; @@ -282,22 +621,23 @@ static coroutine_fn void nbd_reconnect_attempt(BDRVNBDState *s) /* Finalize previous connection if any */ if (s->ioc) { - nbd_client_detach_aio_context(s->bs); + qio_channel_detach_aio_context(QIO_CHANNEL(s->ioc)); + yank_unregister_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name), + nbd_yank, s->bs); object_unref(OBJECT(s->sioc)); s->sioc = NULL; object_unref(OBJECT(s->ioc)); s->ioc = NULL; } - sioc = nbd_establish_connection(s->saddr, &local_err); - if (!sioc) { + if (nbd_co_establish_connection(s->bs, &local_err) < 0) { ret = -ECONNREFUSED; goto out; } bdrv_dec_in_flight(s->bs); - ret = nbd_client_handshake(s->bs, sioc, &local_err); + ret = nbd_client_handshake(s->bs, &local_err); if (s->drained) { s->wait_drained_end = true; @@ -326,21 +666,17 @@ out: static coroutine_fn void nbd_co_reconnect_loop(BDRVNBDState *s) { - uint64_t start_time_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - uint64_t delay_ns = s->reconnect_delay * NANOSECONDS_PER_SECOND; uint64_t timeout = 1 * NANOSECONDS_PER_SECOND; uint64_t max_timeout = 16 * NANOSECONDS_PER_SECOND; + if (qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTING_WAIT) { + reconnect_delay_timer_init(s, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + + s->reconnect_delay * NANOSECONDS_PER_SECOND); + } + nbd_reconnect_attempt(s); while (nbd_client_connecting(s)) { - if (s->state == NBD_CLIENT_CONNECTING_WAIT && - qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - start_time_ns > delay_ns) - { - s->state = NBD_CLIENT_CONNECTING_NOWAIT; - qemu_co_queue_restart_all(&s->free_sema); - } - if (s->drained) { bdrv_dec_in_flight(s->bs); s->wait_drained_end = true; @@ -355,6 +691,9 @@ static coroutine_fn void nbd_co_reconnect_loop(BDRVNBDState *s) } else { qemu_co_sleep_ns_wakeable(QEMU_CLOCK_REALTIME, timeout, &s->connection_co_sleep_ns_state); + if (s->drained) { + continue; + } if (timeout < max_timeout) { timeout *= 2; } @@ -362,6 +701,8 @@ static coroutine_fn void nbd_co_reconnect_loop(BDRVNBDState *s) nbd_reconnect_attempt(s); } + + reconnect_delay_timer_del(s); } static coroutine_fn void nbd_connection_entry(void *opaque) @@ -371,7 +712,7 @@ static coroutine_fn void nbd_connection_entry(void *opaque) int ret = 0; Error *local_err = NULL; - while (s->state != NBD_CLIENT_QUIT) { + while (qatomic_load_acquire(&s->state) != NBD_CLIENT_QUIT) { /* * The NBD client can only really be considered idle when it has * yielded from qio_channel_readv_all_eof(), waiting for data. This is @@ -386,7 +727,7 @@ static coroutine_fn void nbd_connection_entry(void *opaque) nbd_co_reconnect_loop(s); } - if (s->state != NBD_CLIENT_CONNECTED) { + if (qatomic_load_acquire(&s->state) != NBD_CLIENT_CONNECTED) { continue; } @@ -440,7 +781,9 @@ static coroutine_fn void nbd_connection_entry(void *opaque) s->connection_co = NULL; if (s->ioc) { - nbd_client_detach_aio_context(s->bs); + qio_channel_detach_aio_context(QIO_CHANNEL(s->ioc)); + yank_unregister_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name), + nbd_yank, s->bs); object_unref(OBJECT(s->sioc)); s->sioc = NULL; object_unref(OBJECT(s->ioc)); @@ -465,7 +808,7 @@ static int nbd_co_send_request(BlockDriverState *bs, qemu_co_queue_wait(&s->free_sema, &s->send_mutex); } - if (s->state != NBD_CLIENT_CONNECTED) { + if (qatomic_load_acquire(&s->state) != NBD_CLIENT_CONNECTED) { rc = -EIO; goto err; } @@ -492,7 +835,8 @@ static int nbd_co_send_request(BlockDriverState *bs, if (qiov) { qio_channel_set_cork(s->ioc, true); rc = nbd_send_request(s->ioc, request); - if (rc >= 0 && s->state == NBD_CLIENT_CONNECTED) { + if (qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTED && + rc >= 0) { if (qio_channel_writev_all(s->ioc, qiov->iov, qiov->niov, NULL) < 0) { rc = -EIO; @@ -650,6 +994,16 @@ static int nbd_parse_blockstatus_payload(BDRVNBDState *s, trace_nbd_parse_blockstatus_compliance("extent length too large"); } + /* + * HACK: if we are using x-dirty-bitmaps to access + * qemu:allocation-depth, treat all depths > 2 the same as 2, + * since nbd_client_co_block_status is only expecting the low two + * bits to be set. + */ + if (s->alloc_depth && extent->flags > 2) { + extent->flags = 2; + } + return 0; } @@ -807,7 +1161,7 @@ static coroutine_fn int nbd_co_do_receive_one_chunk( s->requests[i].receiving = true; qemu_coroutine_yield(); s->requests[i].receiving = false; - if (s->state != NBD_CLIENT_CONNECTED) { + if (qatomic_load_acquire(&s->state) != NBD_CLIENT_CONNECTED) { error_setg(errp, "Connection closed"); return -EIO; } @@ -966,7 +1320,7 @@ static bool nbd_reply_chunk_iter_receive(BDRVNBDState *s, NBDReply local_reply; NBDStructuredReplyChunk *chunk; Error *local_err = NULL; - if (s->state != NBD_CLIENT_CONNECTED) { + if (qatomic_load_acquire(&s->state) != NBD_CLIENT_CONNECTED) { error_setg(&local_err, "Connection closed"); nbd_iter_channel_error(iter, -EIO, &local_err); goto break_loop; @@ -991,7 +1345,8 @@ static bool nbd_reply_chunk_iter_receive(BDRVNBDState *s, } /* Do not execute the body of NBD_FOREACH_REPLY_CHUNK for simple reply. */ - if (nbd_reply_is_simple(reply) || s->state != NBD_CLIENT_CONNECTED) { + if (nbd_reply_is_simple(reply) || + qatomic_load_acquire(&s->state) != NBD_CLIENT_CONNECTED) { goto break_loop; } @@ -1423,6 +1778,15 @@ static int nbd_client_reopen_prepare(BDRVReopenState *state, return 0; } +static void nbd_yank(void *opaque) +{ + BlockDriverState *bs = opaque; + BDRVNBDState *s = (BDRVNBDState *)bs->opaque; + + qatomic_store_release(&s->state, NBD_CLIENT_QUIT); + qio_channel_shutdown(QIO_CHANNEL(s->sioc), QIO_CHANNEL_SHUTDOWN_BOTH, NULL); +} + static void nbd_client_close(BlockDriverState *bs) { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; @@ -1435,60 +1799,66 @@ static void nbd_client_close(BlockDriverState *bs) nbd_teardown_connection(bs); } -static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr, - Error **errp) +static int nbd_establish_connection(BlockDriverState *bs, + SocketAddress *saddr, + Error **errp) { ERRP_GUARD(); - QIOChannelSocket *sioc; + BDRVNBDState *s = (BDRVNBDState *)bs->opaque; - sioc = qio_channel_socket_new(); - qio_channel_set_name(QIO_CHANNEL(sioc), "nbd-client"); + s->sioc = qio_channel_socket_new(); + qio_channel_set_name(QIO_CHANNEL(s->sioc), "nbd-client"); - qio_channel_socket_connect_sync(sioc, saddr, errp); + qio_channel_socket_connect_sync(s->sioc, saddr, errp); if (*errp) { - object_unref(OBJECT(sioc)); - return NULL; + object_unref(OBJECT(s->sioc)); + s->sioc = NULL; + return -1; } - qio_channel_set_delay(QIO_CHANNEL(sioc), false); + yank_register_function(BLOCKDEV_YANK_INSTANCE(bs->node_name), nbd_yank, bs); + qio_channel_set_delay(QIO_CHANNEL(s->sioc), false); - return sioc; + return 0; } -/* nbd_client_handshake takes ownership on sioc. On failure it is unref'ed. */ -static int nbd_client_handshake(BlockDriverState *bs, QIOChannelSocket *sioc, - Error **errp) +/* nbd_client_handshake takes ownership on s->sioc. On failure it's unref'ed. */ +static int nbd_client_handshake(BlockDriverState *bs, Error **errp) { BDRVNBDState *s = (BDRVNBDState *)bs->opaque; AioContext *aio_context = bdrv_get_aio_context(bs); int ret; trace_nbd_client_handshake(s->export); - - s->sioc = sioc; - - qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL); - qio_channel_attach_aio_context(QIO_CHANNEL(sioc), aio_context); + qio_channel_set_blocking(QIO_CHANNEL(s->sioc), false, NULL); + qio_channel_attach_aio_context(QIO_CHANNEL(s->sioc), aio_context); s->info.request_sizes = true; s->info.structured_reply = true; s->info.base_allocation = true; s->info.x_dirty_bitmap = g_strdup(s->x_dirty_bitmap); s->info.name = g_strdup(s->export ?: ""); - ret = nbd_receive_negotiate(aio_context, QIO_CHANNEL(sioc), s->tlscreds, + ret = nbd_receive_negotiate(aio_context, QIO_CHANNEL(s->sioc), s->tlscreds, s->hostname, &s->ioc, &s->info, errp); g_free(s->info.x_dirty_bitmap); g_free(s->info.name); if (ret < 0) { - object_unref(OBJECT(sioc)); + yank_unregister_function(BLOCKDEV_YANK_INSTANCE(bs->node_name), + nbd_yank, bs); + object_unref(OBJECT(s->sioc)); s->sioc = NULL; return ret; } - if (s->x_dirty_bitmap && !s->info.base_allocation) { - error_setg(errp, "requested x-dirty-bitmap %s not found", - s->x_dirty_bitmap); - ret = -EINVAL; - goto fail; + if (s->x_dirty_bitmap) { + if (!s->info.base_allocation) { + error_setg(errp, "requested x-dirty-bitmap %s not found", + s->x_dirty_bitmap); + ret = -EINVAL; + goto fail; + } + if (strcmp(s->x_dirty_bitmap, "qemu:allocation-depth") == 0) { + s->alloc_depth = true; + } } if (s->info.flags & NBD_FLAG_READ_ONLY) { ret = bdrv_apply_auto_read_only(bs, "NBD export is read-only", errp); @@ -1508,7 +1878,7 @@ static int nbd_client_handshake(BlockDriverState *bs, QIOChannelSocket *sioc, } if (!s->ioc) { - s->ioc = QIO_CHANNEL(sioc); + s->ioc = QIO_CHANNEL(s->sioc); object_ref(OBJECT(s->ioc)); } @@ -1524,9 +1894,11 @@ static int nbd_client_handshake(BlockDriverState *bs, QIOChannelSocket *sioc, { NBDRequest request = { .type = NBD_CMD_DISC }; - nbd_send_request(s->ioc ?: QIO_CHANNEL(sioc), &request); + nbd_send_request(s->ioc ?: QIO_CHANNEL(s->sioc), &request); - object_unref(OBJECT(sioc)); + yank_unregister_function(BLOCKDEV_YANK_INSTANCE(bs->node_name), + nbd_yank, bs); + object_unref(OBJECT(s->sioc)); s->sioc = NULL; return ret; @@ -1918,7 +2290,6 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, { int ret; BDRVNBDState *s = (BDRVNBDState *)bs->opaque; - QIOChannelSocket *sioc; ret = nbd_process_options(bs, options, errp); if (ret < 0) { @@ -1929,23 +2300,30 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, qemu_co_mutex_init(&s->send_mutex); qemu_co_queue_init(&s->free_sema); + if (!yank_register_instance(BLOCKDEV_YANK_INSTANCE(bs->node_name), errp)) { + return -EEXIST; + } + /* * establish TCP connection, return error if it fails * TODO: Configurable retry-until-timeout behaviour. */ - sioc = nbd_establish_connection(s->saddr, errp); - if (!sioc) { + if (nbd_establish_connection(bs, s->saddr, errp) < 0) { + yank_unregister_instance(BLOCKDEV_YANK_INSTANCE(bs->node_name)); return -ECONNREFUSED; } - ret = nbd_client_handshake(bs, sioc, errp); + ret = nbd_client_handshake(bs, errp); if (ret < 0) { + yank_unregister_instance(BLOCKDEV_YANK_INSTANCE(bs->node_name)); nbd_clear_bdrvstate(s); return ret; } /* successfully connected */ s->state = NBD_CLIENT_CONNECTED; + nbd_init_connect_thread(s); + s->connection_co = qemu_coroutine_create(nbd_connection_entry, s); bdrv_inc_in_flight(bs); aio_co_schedule(bdrv_get_aio_context(bs), s->connection_co); @@ -1997,6 +2375,7 @@ static void nbd_close(BlockDriverState *bs) BDRVNBDState *s = bs->opaque; nbd_client_close(bs); + yank_unregister_instance(BLOCKDEV_YANK_INSTANCE(bs->node_name)); nbd_clear_bdrvstate(s); } @@ -2090,6 +2469,18 @@ static const char *const nbd_strong_runtime_opts[] = { NULL }; +static void nbd_cancel_in_flight(BlockDriverState *bs) +{ + BDRVNBDState *s = (BDRVNBDState *)bs->opaque; + + reconnect_delay_timer_del(s); + + if (s->state == NBD_CLIENT_CONNECTING_WAIT) { + s->state = NBD_CLIENT_CONNECTING_NOWAIT; + qemu_co_queue_restart_all(&s->free_sema); + } +} + static BlockDriver bdrv_nbd = { .format_name = "nbd", .protocol_name = "nbd", @@ -2116,6 +2507,7 @@ static BlockDriver bdrv_nbd = { .bdrv_co_block_status = nbd_client_co_block_status, .bdrv_dirname = nbd_dirname, .strong_runtime_opts = nbd_strong_runtime_opts, + .bdrv_cancel_in_flight = nbd_cancel_in_flight, }; static BlockDriver bdrv_nbd_tcp = { @@ -2144,6 +2536,7 @@ static BlockDriver bdrv_nbd_tcp = { .bdrv_co_block_status = nbd_client_co_block_status, .bdrv_dirname = nbd_dirname, .strong_runtime_opts = nbd_strong_runtime_opts, + .bdrv_cancel_in_flight = nbd_cancel_in_flight, }; static BlockDriver bdrv_nbd_unix = { @@ -2172,6 +2565,7 @@ static BlockDriver bdrv_nbd_unix = { .bdrv_co_block_status = nbd_client_co_block_status, .bdrv_dirname = nbd_dirname, .strong_runtime_opts = nbd_strong_runtime_opts, + .bdrv_cancel_in_flight = nbd_cancel_in_flight, }; static void bdrv_nbd_init(void) diff --git a/block/nfs.c b/block/nfs.c index 61a249a9fc..8c1968bb41 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -24,7 +24,9 @@ #include "qemu/osdep.h" +#if !defined(_WIN32) #include +#endif #include "qemu/config-file.h" #include "qemu/error-report.h" #include "qapi/error.h" @@ -58,7 +60,7 @@ typedef struct NFSClient { bool has_zero_init; AioContext *aio_context; QemuMutex mutex; - blkcnt_t st_blocks; + uint64_t st_blocks; bool cache_used; NFSServer *server; char *path; @@ -545,7 +547,9 @@ static int64_t nfs_client_open(NFSClient *client, BlockdevOptionsNfs *opts, } ret = DIV_ROUND_UP(st.st_size, BDRV_SECTOR_SIZE); +#if !defined(_WIN32) client->st_blocks = st.st_blocks; +#endif client->has_zero_init = S_ISREG(st.st_mode); *strp = '/'; goto out; @@ -588,7 +592,7 @@ static int64_t nfs_client_open_qdict(NFSClient *client, QDict *options, int flags, int open_flags, Error **errp) { BlockdevOptionsNfs *opts; - int ret; + int64_t ret; opts = nfs_options_qdict_to_qapi(options, errp); if (opts == NULL) { @@ -706,6 +710,7 @@ static int nfs_has_zero_init(BlockDriverState *bs) return client->has_zero_init; } +#if !defined(_WIN32) /* Called (via nfs_service) with QemuMutex held. */ static void nfs_get_allocated_file_size_cb(int ret, struct nfs_context *nfs, void *data, @@ -721,7 +726,7 @@ nfs_get_allocated_file_size_cb(int ret, struct nfs_context *nfs, void *data, } /* Set task->complete before reading bs->wakeup. */ - atomic_mb_set(&task->complete, 1); + qatomic_mb_set(&task->complete, 1); bdrv_wakeup(task->bs); } @@ -748,6 +753,7 @@ static int64_t nfs_get_allocated_file_size(BlockDriverState *bs) return (task.ret < 0 ? task.ret : st.st_blocks * 512); } +#endif static int coroutine_fn nfs_file_co_truncate(BlockDriverState *bs, int64_t offset, bool exact, @@ -800,7 +806,9 @@ static int nfs_reopen_prepare(BDRVReopenState *state, nfs_get_error(client->context)); return ret; } +#if !defined(_WIN32) client->st_blocks = st.st_blocks; +#endif } return 0; @@ -869,7 +877,10 @@ static BlockDriver bdrv_nfs = { .create_opts = &nfs_create_opts, .bdrv_has_zero_init = nfs_has_zero_init, +/* libnfs does not provide the allocated filesize of a file on win32. */ +#if !defined(_WIN32) .bdrv_get_allocated_file_size = nfs_get_allocated_file_size, +#endif .bdrv_co_truncate = nfs_file_co_truncate, .bdrv_file_open = nfs_file_open, diff --git a/block/null.c b/block/null.c index 15e1d56746..cc9b1d4ea7 100644 --- a/block/null.c +++ b/block/null.c @@ -262,6 +262,11 @@ static void null_refresh_filename(BlockDriverState *bs) bs->drv->format_name); } +static int64_t null_allocated_file_size(BlockDriverState *bs) +{ + return 0; +} + static const char *const null_strong_runtime_opts[] = { BLOCK_OPT_SIZE, NULL_OPT_ZEROES, @@ -277,6 +282,7 @@ static BlockDriver bdrv_null_co = { .bdrv_file_open = null_file_open, .bdrv_parse_filename = null_co_parse_filename, .bdrv_getlength = null_getlength, + .bdrv_get_allocated_file_size = null_allocated_file_size, .bdrv_co_preadv = null_co_preadv, .bdrv_co_pwritev = null_co_pwritev, @@ -297,6 +303,7 @@ static BlockDriver bdrv_null_aio = { .bdrv_file_open = null_file_open, .bdrv_parse_filename = null_aio_parse_filename, .bdrv_getlength = null_getlength, + .bdrv_get_allocated_file_size = null_allocated_file_size, .bdrv_aio_preadv = null_aio_preadv, .bdrv_aio_pwritev = null_aio_pwritev, diff --git a/block/nvme.c b/block/nvme.c index 374e268915..2b5421e7aa 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -31,7 +31,7 @@ #define NVME_SQ_ENTRY_BYTES 64 #define NVME_CQ_ENTRY_BYTES 16 #define NVME_QUEUE_SIZE 128 -#define NVME_BAR_SIZE 8192 +#define NVME_DOORBELL_SIZE 4096 /* * We have to leave one slot empty as that is the full queue case where @@ -41,6 +41,16 @@ typedef struct BDRVNVMeState BDRVNVMeState; +/* Same index is used for queues and IRQs */ +#define INDEX_ADMIN 0 +#define INDEX_IO(n) (1 + n) + +/* This driver shares a single MSIX IRQ for the admin and I/O queues */ +enum { + MSIX_SHARED_IRQ_IDX = 0, + MSIX_IRQ_COUNT = 1 +}; + typedef struct { int32_t head, tail; uint8_t *queue; @@ -81,43 +91,26 @@ typedef struct { QEMUBH *completion_bh; } NVMeQueuePair; -/* Memory mapped registers */ -typedef volatile struct { - uint64_t cap; - uint32_t vs; - uint32_t intms; - uint32_t intmc; - uint32_t cc; - uint32_t reserved0; - uint32_t csts; - uint32_t nssr; - uint32_t aqa; - uint64_t asq; - uint64_t acq; - uint32_t cmbloc; - uint32_t cmbsz; - uint8_t reserved1[0xec0]; - uint8_t cmd_set_specfic[0x100]; - uint32_t doorbells[]; -} NVMeRegs; - -QEMU_BUILD_BUG_ON(offsetof(NVMeRegs, doorbells) != 0x1000); - struct BDRVNVMeState { AioContext *aio_context; QEMUVFIOState *vfio; - NVMeRegs *regs; + void *bar0_wo_map; + /* Memory mapped registers */ + volatile struct { + uint32_t sq_tail; + uint32_t cq_head; + } *doorbells; /* The submission/completion queue pairs. * [0]: admin queue. * [1..]: io queues. */ NVMeQueuePair **queues; - int nr_queues; + unsigned queue_count; size_t page_size; /* How many uint32_t elements does each doorbell entry take. */ size_t doorbell_scale; bool write_cache_supported; - EventNotifier irq_notifier; + EventNotifier irq_notifier[MSIX_IRQ_COUNT]; uint64_t nsze; /* Namespace size reported by identify command */ int nsid; /* The namespace id to read/write data. */ @@ -137,6 +130,12 @@ struct BDRVNVMeState { /* PCI address (required for nvme_refresh_filename()) */ char *device; + + struct { + uint64_t completion_errors; + uint64_t aligned_accesses; + uint64_t unaligned_accesses; + } stats; }; #define NVME_BLOCK_OPT_DEVICE "device" @@ -162,29 +161,32 @@ static QemuOptsList runtime_opts = { }, }; -static void nvme_init_queue(BlockDriverState *bs, NVMeQueue *q, - int nentries, int entry_bytes, Error **errp) +/* Returns true on success, false on failure. */ +static bool nvme_init_queue(BDRVNVMeState *s, NVMeQueue *q, + unsigned nentries, size_t entry_bytes, Error **errp) { - BDRVNVMeState *s = bs->opaque; size_t bytes; int r; - bytes = ROUND_UP(nentries * entry_bytes, s->page_size); + bytes = ROUND_UP(nentries * entry_bytes, qemu_real_host_page_size); q->head = q->tail = 0; - q->queue = qemu_try_blockalign0(bs, bytes); - + q->queue = qemu_try_memalign(qemu_real_host_page_size, bytes); if (!q->queue) { error_setg(errp, "Cannot allocate queue"); - return; + return false; } + memset(q->queue, 0, bytes); r = qemu_vfio_dma_map(s->vfio, q->queue, bytes, false, &q->iova); if (r) { error_setg(errp, "Cannot map queue"); + return false; } + return true; } static void nvme_free_queue_pair(NVMeQueuePair *q) { + trace_nvme_free_queue_pair(q->index, q); if (q->completion_bh) { qemu_bh_delete(q->completion_bh); } @@ -206,25 +208,35 @@ static void nvme_free_req_queue_cb(void *opaque) qemu_mutex_unlock(&q->lock); } -static NVMeQueuePair *nvme_create_queue_pair(BlockDriverState *bs, - int idx, int size, +static NVMeQueuePair *nvme_create_queue_pair(BDRVNVMeState *s, + AioContext *aio_context, + unsigned idx, size_t size, Error **errp) { int i, r; - BDRVNVMeState *s = bs->opaque; - Error *local_err = NULL; - NVMeQueuePair *q = g_new0(NVMeQueuePair, 1); + NVMeQueuePair *q; uint64_t prp_list_iova; + size_t bytes; + q = g_try_new0(NVMeQueuePair, 1); + if (!q) { + return NULL; + } + trace_nvme_create_queue_pair(idx, q, size, aio_context, + event_notifier_get_fd(s->irq_notifier)); + bytes = QEMU_ALIGN_UP(s->page_size * NVME_NUM_REQS, + qemu_real_host_page_size); + q->prp_list_pages = qemu_try_memalign(qemu_real_host_page_size, bytes); + if (!q->prp_list_pages) { + goto fail; + } + memset(q->prp_list_pages, 0, bytes); qemu_mutex_init(&q->lock); q->s = s; q->index = idx; qemu_co_queue_init(&q->free_req_queue); - q->prp_list_pages = qemu_blockalign0(bs, s->page_size * NVME_NUM_REQS); - q->completion_bh = aio_bh_new(bdrv_get_aio_context(bs), - nvme_process_completion_bh, q); - r = qemu_vfio_dma_map(s->vfio, q->prp_list_pages, - s->page_size * NVME_NUM_REQS, + q->completion_bh = aio_bh_new(aio_context, nvme_process_completion_bh, q); + r = qemu_vfio_dma_map(s->vfio, q->prp_list_pages, bytes, false, &prp_list_iova); if (r) { goto fail; @@ -239,19 +251,15 @@ static NVMeQueuePair *nvme_create_queue_pair(BlockDriverState *bs, req->prp_list_iova = prp_list_iova + i * s->page_size; } - nvme_init_queue(bs, &q->sq, size, NVME_SQ_ENTRY_BYTES, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!nvme_init_queue(s, &q->sq, size, NVME_SQ_ENTRY_BYTES, errp)) { goto fail; } - q->sq.doorbell = &s->regs->doorbells[idx * 2 * s->doorbell_scale]; + q->sq.doorbell = &s->doorbells[idx * s->doorbell_scale].sq_tail; - nvme_init_queue(bs, &q->cq, size, NVME_CQ_ENTRY_BYTES, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!nvme_init_queue(s, &q->cq, size, NVME_CQ_ENTRY_BYTES, errp)) { goto fail; } - q->cq.doorbell = &s->regs->doorbells[(idx * 2 + 1) * s->doorbell_scale]; + q->cq.doorbell = &s->doorbells[idx * s->doorbell_scale].cq_head; return q; fail: @@ -288,7 +296,7 @@ static NVMeRequest *nvme_get_free_req(NVMeQueuePair *q) while (q->free_req_head == -1) { if (qemu_in_coroutine()) { - trace_nvme_free_req_queue_wait(q); + trace_nvme_free_req_queue_wait(q->s, q->index); qemu_co_queue_wait(&q->free_req_queue, &q->lock); } else { qemu_mutex_unlock(&q->lock); @@ -386,14 +394,17 @@ static bool nvme_process_completion(NVMeQueuePair *q) break; } ret = nvme_translate_error(c); + if (ret) { + s->stats.completion_errors++; + } q->cq.head = (q->cq.head + 1) % NVME_QUEUE_SIZE; if (!q->cq.head) { q->cq_phase = !q->cq_phase; } cid = le16_to_cpu(c->cid); if (cid == 0 || cid > NVME_QUEUE_SIZE) { - fprintf(stderr, "Unexpected CID in completion queue: %" PRIu32 "\n", - cid); + warn_report("NVMe: Unexpected CID in completion queue: %"PRIu32", " + "queue size: %u", cid, NVME_QUEUE_SIZE); continue; } trace_nvme_complete_command(s, q->index, cid); @@ -441,6 +452,9 @@ static void nvme_trace_command(const NvmeCmd *cmd) { int i; + if (!trace_event_get_state_backends(TRACE_NVME_SUBMIT_COMMAND_RAW)) { + return; + } for (i = 0; i < 8; ++i) { uint8_t *cmdp = (uint8_t *)cmd + i * 8; trace_nvme_submit_command_raw(cmdp[0], cmdp[1], cmdp[2], cmdp[3], @@ -455,7 +469,7 @@ static void nvme_submit_command(NVMeQueuePair *q, NVMeRequest *req, assert(!req->cb); req->cb = cb; req->opaque = opaque; - cmd->cid = cpu_to_le32(req->cid); + cmd->cid = cpu_to_le16(req->cid); trace_nvme_submit_command(q->s, q->index, req->cid); nvme_trace_command(cmd); @@ -469,35 +483,40 @@ static void nvme_submit_command(NVMeQueuePair *q, NVMeRequest *req, qemu_mutex_unlock(&q->lock); } -static void nvme_cmd_sync_cb(void *opaque, int ret) +static void nvme_admin_cmd_sync_cb(void *opaque, int ret) { int *pret = opaque; *pret = ret; aio_wait_kick(); } -static int nvme_cmd_sync(BlockDriverState *bs, NVMeQueuePair *q, - NvmeCmd *cmd) +static int nvme_admin_cmd_sync(BlockDriverState *bs, NvmeCmd *cmd) { + BDRVNVMeState *s = bs->opaque; + NVMeQueuePair *q = s->queues[INDEX_ADMIN]; + AioContext *aio_context = bdrv_get_aio_context(bs); NVMeRequest *req; int ret = -EINPROGRESS; req = nvme_get_free_req(q); if (!req) { return -EBUSY; } - nvme_submit_command(q, req, cmd, nvme_cmd_sync_cb, &ret); + nvme_submit_command(q, req, cmd, nvme_admin_cmd_sync_cb, &ret); - BDRV_POLL_WHILE(bs, ret == -EINPROGRESS); + AIO_WAIT_WHILE(aio_context, ret == -EINPROGRESS); return ret; } -static void nvme_identify(BlockDriverState *bs, int namespace, Error **errp) +/* Returns true on success, false on failure. */ +static bool nvme_identify(BlockDriverState *bs, int namespace, Error **errp) { BDRVNVMeState *s = bs->opaque; - NvmeIdCtrl *idctrl; - NvmeIdNs *idns; + bool ret = false; + union { + NvmeIdCtrl ctrl; + NvmeIdNs ns; + } *id; NvmeLBAF *lbaf; - uint8_t *resp; uint16_t oncs; int r; uint64_t iova; @@ -505,55 +524,54 @@ static void nvme_identify(BlockDriverState *bs, int namespace, Error **errp) .opcode = NVME_ADM_CMD_IDENTIFY, .cdw10 = cpu_to_le32(0x1), }; + size_t id_size = QEMU_ALIGN_UP(sizeof(*id), qemu_real_host_page_size); - resp = qemu_try_blockalign0(bs, sizeof(NvmeIdCtrl)); - if (!resp) { + id = qemu_try_memalign(qemu_real_host_page_size, id_size); + if (!id) { error_setg(errp, "Cannot allocate buffer for identify response"); goto out; } - idctrl = (NvmeIdCtrl *)resp; - idns = (NvmeIdNs *)resp; - r = qemu_vfio_dma_map(s->vfio, resp, sizeof(NvmeIdCtrl), true, &iova); + r = qemu_vfio_dma_map(s->vfio, id, id_size, true, &iova); if (r) { error_setg(errp, "Cannot map buffer for DMA"); goto out; } - cmd.prp1 = cpu_to_le64(iova); - if (nvme_cmd_sync(bs, s->queues[0], &cmd)) { + memset(id, 0, id_size); + cmd.dptr.prp1 = cpu_to_le64(iova); + if (nvme_admin_cmd_sync(bs, &cmd)) { error_setg(errp, "Failed to identify controller"); goto out; } - if (le32_to_cpu(idctrl->nn) < namespace) { + if (le32_to_cpu(id->ctrl.nn) < namespace) { error_setg(errp, "Invalid namespace"); goto out; } - s->write_cache_supported = le32_to_cpu(idctrl->vwc) & 0x1; - s->max_transfer = (idctrl->mdts ? 1 << idctrl->mdts : 0) * s->page_size; + s->write_cache_supported = le32_to_cpu(id->ctrl.vwc) & 0x1; + s->max_transfer = (id->ctrl.mdts ? 1 << id->ctrl.mdts : 0) * s->page_size; /* For now the page list buffer per command is one page, to hold at most * s->page_size / sizeof(uint64_t) entries. */ s->max_transfer = MIN_NON_ZERO(s->max_transfer, s->page_size / sizeof(uint64_t) * s->page_size); - oncs = le16_to_cpu(idctrl->oncs); - s->supports_write_zeroes = !!(oncs & NVME_ONCS_WRITE_ZEROS); + oncs = le16_to_cpu(id->ctrl.oncs); + s->supports_write_zeroes = !!(oncs & NVME_ONCS_WRITE_ZEROES); s->supports_discard = !!(oncs & NVME_ONCS_DSM); - memset(resp, 0, 4096); - + memset(id, 0, id_size); cmd.cdw10 = 0; cmd.nsid = cpu_to_le32(namespace); - if (nvme_cmd_sync(bs, s->queues[0], &cmd)) { + if (nvme_admin_cmd_sync(bs, &cmd)) { error_setg(errp, "Failed to identify namespace"); goto out; } - s->nsze = le64_to_cpu(idns->nsze); - lbaf = &idns->lbaf[NVME_ID_NS_FLBAS_INDEX(idns->flbas)]; + s->nsze = le64_to_cpu(id->ns.nsze); + lbaf = &id->ns.lbaf[NVME_ID_NS_FLBAS_INDEX(id->ns.flbas)]; - if (NVME_ID_NS_DLFEAT_WRITE_ZEROES(idns->dlfeat) && - NVME_ID_NS_DLFEAT_READ_BEHAVIOR(idns->dlfeat) == + if (NVME_ID_NS_DLFEAT_WRITE_ZEROES(id->ns.dlfeat) && + NVME_ID_NS_DLFEAT_READ_BEHAVIOR(id->ns.dlfeat) == NVME_ID_NS_DLFEAT_READ_BEHAVIOR_ZEROES) { bs->supported_write_flags |= BDRV_REQ_MAY_UNMAP; } @@ -571,10 +589,40 @@ static void nvme_identify(BlockDriverState *bs, int namespace, Error **errp) goto out; } + ret = true; s->blkshift = lbaf->ds; out: - qemu_vfio_dma_unmap(s->vfio, resp); - qemu_vfree(resp); + qemu_vfio_dma_unmap(s->vfio, id); + qemu_vfree(id); + + return ret; +} + +static bool nvme_poll_queue(NVMeQueuePair *q) +{ + bool progress = false; + + const size_t cqe_offset = q->cq.head * NVME_CQ_ENTRY_BYTES; + NvmeCqe *cqe = (NvmeCqe *)&q->cq.queue[cqe_offset]; + + trace_nvme_poll_queue(q->s, q->index); + /* + * Do an early check for completions. q->lock isn't needed because + * nvme_process_completion() only runs in the event loop thread and + * cannot race with itself. + */ + if ((le16_to_cpu(cqe->status) & 0x1) == q->cq_phase) { + return false; + } + + qemu_mutex_lock(&q->lock); + while (nvme_process_completion(q)) { + /* Keep polling */ + progress = true; + } + qemu_mutex_unlock(&q->lock); + + return progress; } static bool nvme_poll_queues(BDRVNVMeState *s) @@ -582,33 +630,18 @@ static bool nvme_poll_queues(BDRVNVMeState *s) bool progress = false; int i; - for (i = 0; i < s->nr_queues; i++) { - NVMeQueuePair *q = s->queues[i]; - const size_t cqe_offset = q->cq.head * NVME_CQ_ENTRY_BYTES; - NvmeCqe *cqe = (NvmeCqe *)&q->cq.queue[cqe_offset]; - - /* - * Do an early check for completions. q->lock isn't needed because - * nvme_process_completion() only runs in the event loop thread and - * cannot race with itself. - */ - if ((le16_to_cpu(cqe->status) & 0x1) == q->cq_phase) { - continue; - } - - qemu_mutex_lock(&q->lock); - while (nvme_process_completion(q)) { - /* Keep polling */ + for (i = 0; i < s->queue_count; i++) { + if (nvme_poll_queue(s->queues[i])) { progress = true; } - qemu_mutex_unlock(&q->lock); } return progress; } static void nvme_handle_event(EventNotifier *n) { - BDRVNVMeState *s = container_of(n, BDRVNVMeState, irq_notifier); + BDRVNVMeState *s = container_of(n, BDRVNVMeState, + irq_notifier[MSIX_SHARED_IRQ_IDX]); trace_nvme_handle_event(s); event_notifier_test_and_clear(n); @@ -618,49 +651,52 @@ static void nvme_handle_event(EventNotifier *n) static bool nvme_add_io_queue(BlockDriverState *bs, Error **errp) { BDRVNVMeState *s = bs->opaque; - int n = s->nr_queues; + unsigned n = s->queue_count; NVMeQueuePair *q; NvmeCmd cmd; - int queue_size = NVME_QUEUE_SIZE; + unsigned queue_size = NVME_QUEUE_SIZE; - q = nvme_create_queue_pair(bs, n, queue_size, errp); + assert(n <= UINT16_MAX); + q = nvme_create_queue_pair(s, bdrv_get_aio_context(bs), + n, queue_size, errp); if (!q) { return false; } cmd = (NvmeCmd) { .opcode = NVME_ADM_CMD_CREATE_CQ, - .prp1 = cpu_to_le64(q->cq.iova), - .cdw10 = cpu_to_le32(((queue_size - 1) << 16) | (n & 0xFFFF)), - .cdw11 = cpu_to_le32(0x3), + .dptr.prp1 = cpu_to_le64(q->cq.iova), + .cdw10 = cpu_to_le32(((queue_size - 1) << 16) | n), + .cdw11 = cpu_to_le32(NVME_CQ_IEN | NVME_CQ_PC), }; - if (nvme_cmd_sync(bs, s->queues[0], &cmd)) { - error_setg(errp, "Failed to create io queue [%d]", n); - nvme_free_queue_pair(q); - return false; + if (nvme_admin_cmd_sync(bs, &cmd)) { + error_setg(errp, "Failed to create CQ io queue [%u]", n); + goto out_error; } cmd = (NvmeCmd) { .opcode = NVME_ADM_CMD_CREATE_SQ, - .prp1 = cpu_to_le64(q->sq.iova), - .cdw10 = cpu_to_le32(((queue_size - 1) << 16) | (n & 0xFFFF)), - .cdw11 = cpu_to_le32(0x1 | (n << 16)), + .dptr.prp1 = cpu_to_le64(q->sq.iova), + .cdw10 = cpu_to_le32(((queue_size - 1) << 16) | n), + .cdw11 = cpu_to_le32(NVME_SQ_PC | (n << 16)), }; - if (nvme_cmd_sync(bs, s->queues[0], &cmd)) { - error_setg(errp, "Failed to create io queue [%d]", n); - nvme_free_queue_pair(q); - return false; + if (nvme_admin_cmd_sync(bs, &cmd)) { + error_setg(errp, "Failed to create SQ io queue [%u]", n); + goto out_error; } s->queues = g_renew(NVMeQueuePair *, s->queues, n + 1); s->queues[n] = q; - s->nr_queues++; + s->queue_count++; return true; +out_error: + nvme_free_queue_pair(q); + return false; } static bool nvme_poll_cb(void *opaque) { EventNotifier *e = opaque; - BDRVNVMeState *s = container_of(e, BDRVNVMeState, irq_notifier); + BDRVNVMeState *s = container_of(e, BDRVNVMeState, + irq_notifier[MSIX_SHARED_IRQ_IDX]); - trace_nvme_poll_cb(s); return nvme_poll_queues(s); } @@ -668,18 +704,21 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, Error **errp) { BDRVNVMeState *s = bs->opaque; + NVMeQueuePair *q; + AioContext *aio_context = bdrv_get_aio_context(bs); int ret; uint64_t cap; + uint32_t ver; uint64_t timeout_ms; uint64_t deadline, now; - Error *local_err = NULL; + volatile NvmeBar *regs = NULL; qemu_co_mutex_init(&s->dma_map_lock); qemu_co_queue_init(&s->dma_flush_queue); s->device = g_strdup(device); s->nsid = namespace; s->aio_context = bdrv_get_aio_context(bs); - ret = event_notifier_init(&s->irq_notifier, 0); + ret = event_notifier_init(&s->irq_notifier[MSIX_SHARED_IRQ_IDX], 0); if (ret) { error_setg(errp, "Failed to init event notifier"); return ret; @@ -691,32 +730,51 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, goto out; } - s->regs = qemu_vfio_pci_map_bar(s->vfio, 0, 0, NVME_BAR_SIZE, errp); - if (!s->regs) { + regs = qemu_vfio_pci_map_bar(s->vfio, 0, 0, sizeof(NvmeBar), + PROT_READ | PROT_WRITE, errp); + if (!regs) { ret = -EINVAL; goto out; } - /* Perform initialize sequence as described in NVMe spec "7.6.1 * Initialization". */ - cap = le64_to_cpu(s->regs->cap); - if (!(cap & (1ULL << 37))) { + cap = le64_to_cpu(regs->cap); + trace_nvme_controller_capability_raw(cap); + trace_nvme_controller_capability("Maximum Queue Entries Supported", + 1 + NVME_CAP_MQES(cap)); + trace_nvme_controller_capability("Contiguous Queues Required", + NVME_CAP_CQR(cap)); + trace_nvme_controller_capability("Doorbell Stride", + 1 << (2 + NVME_CAP_DSTRD(cap))); + trace_nvme_controller_capability("Subsystem Reset Supported", + NVME_CAP_NSSRS(cap)); + trace_nvme_controller_capability("Memory Page Size Minimum", + 1 << (12 + NVME_CAP_MPSMIN(cap))); + trace_nvme_controller_capability("Memory Page Size Maximum", + 1 << (12 + NVME_CAP_MPSMAX(cap))); + if (!NVME_CAP_CSS(cap)) { error_setg(errp, "Device doesn't support NVMe command set"); ret = -EINVAL; goto out; } - s->page_size = MAX(4096, 1 << (12 + ((cap >> 48) & 0xF))); - s->doorbell_scale = (4 << (((cap >> 32) & 0xF))) / sizeof(uint32_t); + s->page_size = 1u << (12 + NVME_CAP_MPSMIN(cap)); + s->doorbell_scale = (4 << NVME_CAP_DSTRD(cap)) / sizeof(uint32_t); bs->bl.opt_mem_alignment = s->page_size; - timeout_ms = MIN(500 * ((cap >> 24) & 0xFF), 30000); + bs->bl.request_alignment = s->page_size; + timeout_ms = MIN(500 * NVME_CAP_TO(cap), 30000); + + ver = le32_to_cpu(regs->vs); + trace_nvme_controller_spec_version(extract32(ver, 16, 16), + extract32(ver, 8, 8), + extract32(ver, 0, 8)); /* Reset device to get a clean state. */ - s->regs->cc = cpu_to_le32(le32_to_cpu(s->regs->cc) & 0xFE); + regs->cc = cpu_to_le32(le32_to_cpu(regs->cc) & 0xFE); /* Wait for CSTS.RDY = 0. */ - deadline = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + timeout_ms * 1000000ULL; - while (le32_to_cpu(s->regs->csts) & 0x1) { + deadline = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + timeout_ms * SCALE_MS; + while (NVME_CSTS_RDY(le32_to_cpu(regs->csts))) { if (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) > deadline) { error_setg(errp, "Timeout while waiting for device to reset (%" PRId64 " ms)", @@ -726,27 +784,38 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, } } - /* Set up admin queue. */ - s->queues = g_new(NVMeQueuePair *, 1); - s->queues[0] = nvme_create_queue_pair(bs, 0, NVME_QUEUE_SIZE, errp); - if (!s->queues[0]) { + s->bar0_wo_map = qemu_vfio_pci_map_bar(s->vfio, 0, 0, + sizeof(NvmeBar) + NVME_DOORBELL_SIZE, + PROT_WRITE, errp); + s->doorbells = (void *)((uintptr_t)s->bar0_wo_map + sizeof(NvmeBar)); + if (!s->doorbells) { ret = -EINVAL; goto out; } - s->nr_queues = 1; - QEMU_BUILD_BUG_ON(NVME_QUEUE_SIZE & 0xF000); - s->regs->aqa = cpu_to_le32((NVME_QUEUE_SIZE << 16) | NVME_QUEUE_SIZE); - s->regs->asq = cpu_to_le64(s->queues[0]->sq.iova); - s->regs->acq = cpu_to_le64(s->queues[0]->cq.iova); + + /* Set up admin queue. */ + s->queues = g_new(NVMeQueuePair *, 1); + q = nvme_create_queue_pair(s, aio_context, 0, NVME_QUEUE_SIZE, errp); + if (!q) { + ret = -EINVAL; + goto out; + } + s->queues[INDEX_ADMIN] = q; + s->queue_count = 1; + QEMU_BUILD_BUG_ON((NVME_QUEUE_SIZE - 1) & 0xF000); + regs->aqa = cpu_to_le32(((NVME_QUEUE_SIZE - 1) << AQA_ACQS_SHIFT) | + ((NVME_QUEUE_SIZE - 1) << AQA_ASQS_SHIFT)); + regs->asq = cpu_to_le64(q->sq.iova); + regs->acq = cpu_to_le64(q->cq.iova); /* After setting up all control registers we can enable device now. */ - s->regs->cc = cpu_to_le32((ctz32(NVME_CQ_ENTRY_BYTES) << 20) | - (ctz32(NVME_SQ_ENTRY_BYTES) << 16) | - 0x1); + regs->cc = cpu_to_le32((ctz32(NVME_CQ_ENTRY_BYTES) << CC_IOCQES_SHIFT) | + (ctz32(NVME_SQ_ENTRY_BYTES) << CC_IOSQES_SHIFT) | + CC_EN_MASK); /* Wait for CSTS.RDY = 1. */ now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - deadline = now + timeout_ms * 1000000; - while (!(le32_to_cpu(s->regs->csts) & 0x1)) { + deadline = now + timeout_ms * SCALE_MS; + while (!NVME_CSTS_RDY(le32_to_cpu(regs->csts))) { if (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) > deadline) { error_setg(errp, "Timeout while waiting for device to start (%" PRId64 " ms)", @@ -756,17 +825,16 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, } } - ret = qemu_vfio_pci_init_irq(s->vfio, &s->irq_notifier, + ret = qemu_vfio_pci_init_irq(s->vfio, s->irq_notifier, VFIO_PCI_MSIX_IRQ_INDEX, errp); if (ret) { goto out; } - aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier, + aio_set_event_notifier(bdrv_get_aio_context(bs), + &s->irq_notifier[MSIX_SHARED_IRQ_IDX], false, nvme_handle_event, nvme_poll_cb); - nvme_identify(bs, namespace, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!nvme_identify(bs, namespace, errp)) { ret = -EIO; goto out; } @@ -776,6 +844,10 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, ret = -EIO; } out: + if (regs) { + qemu_vfio_pci_unmap_bar(s->vfio, 0, (void *)regs, 0, sizeof(NvmeBar)); + } + /* Cleaning up is done in nvme_file_open() upon error. */ return ret; } @@ -828,7 +900,7 @@ static int nvme_enable_disable_write_cache(BlockDriverState *bs, bool enable, .cdw11 = cpu_to_le32(enable ? 0x01 : 0x00), }; - ret = nvme_cmd_sync(bs, s->queues[0], &cmd); + ret = nvme_admin_cmd_sync(bs, &cmd); if (ret) { error_setg(errp, "Failed to configure NVMe write cache"); } @@ -837,17 +909,18 @@ static int nvme_enable_disable_write_cache(BlockDriverState *bs, bool enable, static void nvme_close(BlockDriverState *bs) { - int i; BDRVNVMeState *s = bs->opaque; - for (i = 0; i < s->nr_queues; ++i) { + for (unsigned i = 0; i < s->queue_count; ++i) { nvme_free_queue_pair(s->queues[i]); } g_free(s->queues); - aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier, + aio_set_event_notifier(bdrv_get_aio_context(bs), + &s->irq_notifier[MSIX_SHARED_IRQ_IDX], false, NULL, NULL); - event_notifier_cleanup(&s->irq_notifier); - qemu_vfio_pci_unmap_bar(s->vfio, 0, (void *)s->regs, 0, NVME_BAR_SIZE); + event_notifier_cleanup(&s->irq_notifier[MSIX_SHARED_IRQ_IDX]); + qemu_vfio_pci_unmap_bar(s->vfio, 0, s->bar0_wo_map, + 0, sizeof(NvmeBar) + NVME_DOORBELL_SIZE); qemu_vfio_close(s->vfio); g_free(s->device); @@ -951,11 +1024,12 @@ static coroutine_fn int nvme_cmd_map_qiov(BlockDriverState *bs, NvmeCmd *cmd, for (i = 0; i < qiov->niov; ++i) { bool retry = true; uint64_t iova; + size_t len = QEMU_ALIGN_UP(qiov->iov[i].iov_len, + qemu_real_host_page_size); try_map: r = qemu_vfio_dma_map(s->vfio, qiov->iov[i].iov_base, - qiov->iov[i].iov_len, - true, &iova); + len, true, &iova); if (r == -ENOMEM && retry) { retry = false; trace_nvme_dma_flush_queue_wait(s); @@ -988,16 +1062,16 @@ try_map: case 0: abort(); case 1: - cmd->prp1 = pagelist[0]; - cmd->prp2 = 0; + cmd->dptr.prp1 = pagelist[0]; + cmd->dptr.prp2 = 0; break; case 2: - cmd->prp1 = pagelist[0]; - cmd->prp2 = pagelist[1]; + cmd->dptr.prp1 = pagelist[0]; + cmd->dptr.prp2 = pagelist[1]; break; default: - cmd->prp1 = pagelist[0]; - cmd->prp2 = cpu_to_le64(req->prp_list_iova + sizeof(uint64_t)); + cmd->dptr.prp1 = pagelist[0]; + cmd->dptr.prp2 = cpu_to_le64(req->prp_list_iova + sizeof(uint64_t)); break; } trace_nvme_cmd_map_qiov(s, cmd, req, qiov, entries); @@ -1045,7 +1119,7 @@ static coroutine_fn int nvme_co_prw_aligned(BlockDriverState *bs, { int r; BDRVNVMeState *s = bs->opaque; - NVMeQueuePair *ioq = s->queues[1]; + NVMeQueuePair *ioq = s->queues[INDEX_IO(0)]; NVMeRequest *req; uint32_t cdw12 = (((bytes >> s->blkshift) - 1) & 0xFFFF) | @@ -1063,7 +1137,7 @@ static coroutine_fn int nvme_co_prw_aligned(BlockDriverState *bs, }; trace_nvme_prw_aligned(s, is_write, offset, bytes, flags, qiov->niov); - assert(s->nr_queues > 1); + assert(s->queue_count > 1); req = nvme_get_free_req(ioq); assert(req); @@ -1099,8 +1173,9 @@ static inline bool nvme_qiov_aligned(BlockDriverState *bs, BDRVNVMeState *s = bs->opaque; for (i = 0; i < qiov->niov; ++i) { - if (!QEMU_PTR_IS_ALIGNED(qiov->iov[i].iov_base, s->page_size) || - !QEMU_IS_ALIGNED(qiov->iov[i].iov_len, s->page_size)) { + if (!QEMU_PTR_IS_ALIGNED(qiov->iov[i].iov_base, + qemu_real_host_page_size) || + !QEMU_IS_ALIGNED(qiov->iov[i].iov_len, qemu_real_host_page_size)) { trace_nvme_qiov_unaligned(qiov, i, qiov->iov[i].iov_base, qiov->iov[i].iov_len, s->page_size); return false; @@ -1116,15 +1191,17 @@ static int nvme_co_prw(BlockDriverState *bs, uint64_t offset, uint64_t bytes, int r; uint8_t *buf = NULL; QEMUIOVector local_qiov; - + size_t len = QEMU_ALIGN_UP(bytes, qemu_real_host_page_size); assert(QEMU_IS_ALIGNED(offset, s->page_size)); assert(QEMU_IS_ALIGNED(bytes, s->page_size)); assert(bytes <= s->max_transfer); if (nvme_qiov_aligned(bs, qiov)) { + s->stats.aligned_accesses++; return nvme_co_prw_aligned(bs, offset, bytes, qiov, is_write, flags); } + s->stats.unaligned_accesses++; trace_nvme_prw_buffered(s, offset, bytes, qiov->niov, is_write); - buf = qemu_try_blockalign(bs, bytes); + buf = qemu_try_memalign(qemu_real_host_page_size, len); if (!buf) { return -ENOMEM; @@ -1160,7 +1237,7 @@ static coroutine_fn int nvme_co_pwritev(BlockDriverState *bs, static coroutine_fn int nvme_co_flush(BlockDriverState *bs) { BDRVNVMeState *s = bs->opaque; - NVMeQueuePair *ioq = s->queues[1]; + NVMeQueuePair *ioq = s->queues[INDEX_IO(0)]; NVMeRequest *req; NvmeCmd cmd = { .opcode = NVME_CMD_FLUSH, @@ -1171,7 +1248,7 @@ static coroutine_fn int nvme_co_flush(BlockDriverState *bs) .ret = -EINPROGRESS, }; - assert(s->nr_queues > 1); + assert(s->queue_count > 1); req = nvme_get_free_req(ioq); assert(req); nvme_submit_command(ioq, req, &cmd, nvme_rw_cb, &data); @@ -1191,7 +1268,7 @@ static coroutine_fn int nvme_co_pwrite_zeroes(BlockDriverState *bs, BdrvRequestFlags flags) { BDRVNVMeState *s = bs->opaque; - NVMeQueuePair *ioq = s->queues[1]; + NVMeQueuePair *ioq = s->queues[INDEX_IO(0)]; NVMeRequest *req; uint32_t cdw12 = ((bytes >> s->blkshift) - 1) & 0xFFFF; @@ -1201,7 +1278,7 @@ static coroutine_fn int nvme_co_pwrite_zeroes(BlockDriverState *bs, } NvmeCmd cmd = { - .opcode = NVME_CMD_WRITE_ZEROS, + .opcode = NVME_CMD_WRITE_ZEROES, .nsid = cpu_to_le32(s->nsid), .cdw10 = cpu_to_le32((offset >> s->blkshift) & 0xFFFFFFFF), .cdw11 = cpu_to_le32(((offset >> s->blkshift) >> 32) & 0xFFFFFFFF), @@ -1223,7 +1300,7 @@ static coroutine_fn int nvme_co_pwrite_zeroes(BlockDriverState *bs, cmd.cdw12 = cpu_to_le32(cdw12); trace_nvme_write_zeroes(s, offset, bytes, flags); - assert(s->nr_queues > 1); + assert(s->queue_count > 1); req = nvme_get_free_req(ioq); assert(req); @@ -1244,7 +1321,7 @@ static int coroutine_fn nvme_co_pdiscard(BlockDriverState *bs, int bytes) { BDRVNVMeState *s = bs->opaque; - NVMeQueuePair *ioq = s->queues[1]; + NVMeQueuePair *ioq = s->queues[INDEX_IO(0)]; NVMeRequest *req; NvmeDsmRange *buf; QEMUIOVector local_qiov; @@ -1266,13 +1343,13 @@ static int coroutine_fn nvme_co_pdiscard(BlockDriverState *bs, return -ENOTSUP; } - assert(s->nr_queues > 1); + assert(s->queue_count > 1); - buf = qemu_try_blockalign0(bs, s->page_size); + buf = qemu_try_memalign(s->page_size, s->page_size); if (!buf) { return -ENOMEM; } - + memset(buf, 0, s->page_size); buf->nlb = cpu_to_le32(bytes >> s->blkshift); buf->slba = cpu_to_le64(offset >> s->blkshift); buf->cattr = 0; @@ -1318,6 +1395,29 @@ out: } +static int coroutine_fn nvme_co_truncate(BlockDriverState *bs, int64_t offset, + bool exact, PreallocMode prealloc, + BdrvRequestFlags flags, Error **errp) +{ + int64_t cur_length; + + if (prealloc != PREALLOC_MODE_OFF) { + error_setg(errp, "Unsupported preallocation mode '%s'", + PreallocMode_str(prealloc)); + return -ENOTSUP; + } + + cur_length = nvme_getlength(bs); + if (offset != cur_length && exact) { + error_setg(errp, "Cannot resize NVMe devices"); + return -ENOTSUP; + } else if (offset > cur_length) { + error_setg(errp, "Cannot grow NVMe devices"); + return -EINVAL; + } + + return 0; +} static int nvme_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, Error **errp) @@ -1346,14 +1446,15 @@ static void nvme_detach_aio_context(BlockDriverState *bs) { BDRVNVMeState *s = bs->opaque; - for (int i = 0; i < s->nr_queues; i++) { + for (unsigned i = 0; i < s->queue_count; i++) { NVMeQueuePair *q = s->queues[i]; qemu_bh_delete(q->completion_bh); q->completion_bh = NULL; } - aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier, + aio_set_event_notifier(bdrv_get_aio_context(bs), + &s->irq_notifier[MSIX_SHARED_IRQ_IDX], false, NULL, NULL); } @@ -1363,10 +1464,10 @@ static void nvme_attach_aio_context(BlockDriverState *bs, BDRVNVMeState *s = bs->opaque; s->aio_context = new_context; - aio_set_event_notifier(new_context, &s->irq_notifier, + aio_set_event_notifier(new_context, &s->irq_notifier[MSIX_SHARED_IRQ_IDX], false, nvme_handle_event, nvme_poll_cb); - for (int i = 0; i < s->nr_queues; i++) { + for (unsigned i = 0; i < s->queue_count; i++) { NVMeQueuePair *q = s->queues[i]; q->completion_bh = @@ -1383,11 +1484,10 @@ static void nvme_aio_plug(BlockDriverState *bs) static void nvme_aio_unplug(BlockDriverState *bs) { - int i; BDRVNVMeState *s = bs->opaque; assert(s->plugged); s->plugged = false; - for (i = 1; i < s->nr_queues; i++) { + for (unsigned i = INDEX_IO(0); i < s->queue_count; i++) { NVMeQueuePair *q = s->queues[i]; qemu_mutex_lock(&q->lock); nvme_kick(q); @@ -1417,6 +1517,21 @@ static void nvme_unregister_buf(BlockDriverState *bs, void *host) qemu_vfio_dma_unmap(s->vfio, host); } +static BlockStatsSpecific *nvme_get_specific_stats(BlockDriverState *bs) +{ + BlockStatsSpecific *stats = g_new(BlockStatsSpecific, 1); + BDRVNVMeState *s = bs->opaque; + + stats->driver = BLOCKDEV_DRIVER_NVME; + stats->u.nvme = (BlockStatsSpecificNvme) { + .completion_errors = s->stats.completion_errors, + .aligned_accesses = s->stats.aligned_accesses, + .unaligned_accesses = s->stats.unaligned_accesses, + }; + + return stats; +} + static const char *const nvme_strong_runtime_opts[] = { NVME_BLOCK_OPT_DEVICE, NVME_BLOCK_OPT_NAMESPACE, @@ -1437,6 +1552,7 @@ static BlockDriver bdrv_nvme = { .bdrv_close = nvme_close, .bdrv_getlength = nvme_getlength, .bdrv_probe_blocksizes = nvme_probe_blocksizes, + .bdrv_co_truncate = nvme_co_truncate, .bdrv_co_preadv = nvme_co_preadv, .bdrv_co_pwritev = nvme_co_pwritev, @@ -1450,6 +1566,7 @@ static BlockDriver bdrv_nvme = { .bdrv_refresh_filename = nvme_refresh_filename, .bdrv_refresh_limits = nvme_refresh_limits, .strong_runtime_opts = nvme_strong_runtime_opts, + .bdrv_get_specific_stats = nvme_get_specific_stats, .bdrv_detach_aio_context = nvme_detach_aio_context, .bdrv_attach_aio_context = nvme_attach_aio_context, diff --git a/block/parallels-ext.c b/block/parallels-ext.c new file mode 100644 index 0000000000..e0dd0975c6 --- /dev/null +++ b/block/parallels-ext.c @@ -0,0 +1,300 @@ +/* + * Support of Parallels Format Extension. It's a part of Parallels format + * driver. + * + * Copyright (c) 2021 Virtuozzo International GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "block/block_int.h" +#include "parallels.h" +#include "crypto/hash.h" +#include "qemu/uuid.h" + +#define PARALLELS_FORMAT_EXTENSION_MAGIC 0xAB234CEF23DCEA87ULL + +#define PARALLELS_END_OF_FEATURES_MAGIC 0x0ULL +#define PARALLELS_DIRTY_BITMAP_FEATURE_MAGIC 0x20385FAE252CB34AULL + +typedef struct ParallelsFormatExtensionHeader { + uint64_t magic; /* PARALLELS_FORMAT_EXTENSION_MAGIC */ + uint8_t check_sum[16]; +} QEMU_PACKED ParallelsFormatExtensionHeader; + +typedef struct ParallelsFeatureHeader { + uint64_t magic; + uint64_t flags; + uint32_t data_size; + uint32_t _unused; +} QEMU_PACKED ParallelsFeatureHeader; + +typedef struct ParallelsDirtyBitmapFeature { + uint64_t size; + uint8_t id[16]; + uint32_t granularity; + uint32_t l1_size; + /* L1 table follows */ +} QEMU_PACKED ParallelsDirtyBitmapFeature; + +/* Given L1 table read bitmap data from the image and populate @bitmap */ +static int parallels_load_bitmap_data(BlockDriverState *bs, + const uint64_t *l1_table, + uint32_t l1_size, + BdrvDirtyBitmap *bitmap, + Error **errp) +{ + BDRVParallelsState *s = bs->opaque; + int ret = 0; + uint64_t offset, limit; + uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap); + uint8_t *buf = NULL; + uint64_t i, tab_size = + DIV_ROUND_UP(bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size), + s->cluster_size); + + if (tab_size != l1_size) { + error_setg(errp, "Bitmap table size %" PRIu32 " does not correspond " + "to bitmap size and cluster size. Expected %" PRIu64, + l1_size, tab_size); + return -EINVAL; + } + + buf = qemu_blockalign(bs, s->cluster_size); + limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap); + for (i = 0, offset = 0; i < tab_size; ++i, offset += limit) { + uint64_t count = MIN(bm_size - offset, limit); + uint64_t entry = l1_table[i]; + + if (entry == 0) { + /* No need to deserialize zeros because @bitmap is cleared. */ + continue; + } + + if (entry == 1) { + bdrv_dirty_bitmap_deserialize_ones(bitmap, offset, count, false); + } else { + ret = bdrv_pread(bs->file, entry << BDRV_SECTOR_BITS, buf, + s->cluster_size); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Failed to read bitmap data cluster"); + goto finish; + } + bdrv_dirty_bitmap_deserialize_part(bitmap, buf, offset, count, + false); + } + } + ret = 0; + + bdrv_dirty_bitmap_deserialize_finish(bitmap); + +finish: + qemu_vfree(buf); + + return ret; +} + +/* + * @data buffer (of @data_size size) is the Dirty bitmaps feature which + * consists of ParallelsDirtyBitmapFeature followed by L1 table. + */ +static BdrvDirtyBitmap *parallels_load_bitmap(BlockDriverState *bs, + uint8_t *data, + size_t data_size, + Error **errp) +{ + int ret; + ParallelsDirtyBitmapFeature bf; + g_autofree uint64_t *l1_table = NULL; + BdrvDirtyBitmap *bitmap; + QemuUUID uuid; + char uuidstr[UUID_FMT_LEN + 1]; + int i; + + if (data_size < sizeof(bf)) { + error_setg(errp, "Too small Bitmap Feature area in Parallels Format " + "Extension: %zu bytes, expected at least %zu bytes", + data_size, sizeof(bf)); + return NULL; + } + memcpy(&bf, data, sizeof(bf)); + bf.size = le64_to_cpu(bf.size); + bf.granularity = le32_to_cpu(bf.granularity) << BDRV_SECTOR_BITS; + bf.l1_size = le32_to_cpu(bf.l1_size); + data += sizeof(bf); + data_size -= sizeof(bf); + + if (bf.size != bs->total_sectors) { + error_setg(errp, "Bitmap size (in sectors) %" PRId64 " differs from " + "disk size in sectors %" PRId64, bf.size, bs->total_sectors); + return NULL; + } + + if (bf.l1_size * sizeof(uint64_t) > data_size) { + error_setg(errp, "Bitmaps feature corrupted: l1 table exceeds " + "extension data_size"); + return NULL; + } + + memcpy(&uuid, bf.id, sizeof(uuid)); + qemu_uuid_unparse(&uuid, uuidstr); + bitmap = bdrv_create_dirty_bitmap(bs, bf.granularity, uuidstr, errp); + if (!bitmap) { + return NULL; + } + + l1_table = g_new(uint64_t, bf.l1_size); + for (i = 0; i < bf.l1_size; i++, data += sizeof(uint64_t)) { + l1_table[i] = ldq_le_p(data); + } + + ret = parallels_load_bitmap_data(bs, l1_table, bf.l1_size, bitmap, errp); + if (ret < 0) { + bdrv_release_dirty_bitmap(bitmap); + return NULL; + } + + /* We support format extension only for RO parallels images. */ + assert(!(bs->open_flags & BDRV_O_RDWR)); + bdrv_dirty_bitmap_set_readonly(bitmap, true); + + return bitmap; +} + +static int parallels_parse_format_extension(BlockDriverState *bs, + uint8_t *ext_cluster, Error **errp) +{ + BDRVParallelsState *s = bs->opaque; + int ret; + int remaining = s->cluster_size; + uint8_t *pos = ext_cluster; + ParallelsFormatExtensionHeader eh; + g_autofree uint8_t *hash = NULL; + size_t hash_len = 0; + GSList *bitmaps = NULL, *el; + + memcpy(&eh, pos, sizeof(eh)); + eh.magic = le64_to_cpu(eh.magic); + pos += sizeof(eh); + remaining -= sizeof(eh); + + if (eh.magic != PARALLELS_FORMAT_EXTENSION_MAGIC) { + error_setg(errp, "Wrong parallels Format Extension magic: 0x%" PRIx64 + ", expected: 0x%llx", eh.magic, + PARALLELS_FORMAT_EXTENSION_MAGIC); + goto fail; + } + + ret = qcrypto_hash_bytes(QCRYPTO_HASH_ALG_MD5, (char *)pos, remaining, + &hash, &hash_len, errp); + if (ret < 0) { + goto fail; + } + + if (hash_len != sizeof(eh.check_sum) || + memcmp(hash, eh.check_sum, sizeof(eh.check_sum)) != 0) { + error_setg(errp, "Wrong checksum in Format Extension header. Format " + "extension is corrupted."); + goto fail; + } + + while (true) { + ParallelsFeatureHeader fh; + BdrvDirtyBitmap *bitmap; + + if (remaining < sizeof(fh)) { + error_setg(errp, "Can not read feature header, as remaining bytes " + "(%d) in Format Extension is less than Feature header " + "size (%zu)", remaining, sizeof(fh)); + goto fail; + } + + memcpy(&fh, pos, sizeof(fh)); + pos += sizeof(fh); + remaining -= sizeof(fh); + + fh.magic = le64_to_cpu(fh.magic); + fh.flags = le64_to_cpu(fh.flags); + fh.data_size = le32_to_cpu(fh.data_size); + + if (fh.flags) { + error_setg(errp, "Flags for extension feature are unsupported"); + goto fail; + } + + if (fh.data_size > remaining) { + error_setg(errp, "Feature data_size exceedes Format Extension " + "cluster"); + goto fail; + } + + switch (fh.magic) { + case PARALLELS_END_OF_FEATURES_MAGIC: + return 0; + + case PARALLELS_DIRTY_BITMAP_FEATURE_MAGIC: + bitmap = parallels_load_bitmap(bs, pos, fh.data_size, errp); + if (!bitmap) { + goto fail; + } + bitmaps = g_slist_append(bitmaps, bitmap); + break; + + default: + error_setg(errp, "Unknown feature: 0x%" PRIu64, fh.magic); + goto fail; + } + + pos = ext_cluster + QEMU_ALIGN_UP(pos + fh.data_size - ext_cluster, 8); + } + +fail: + for (el = bitmaps; el; el = el->next) { + bdrv_release_dirty_bitmap(el->data); + } + g_slist_free(bitmaps); + + return -EINVAL; +} + +int parallels_read_format_extension(BlockDriverState *bs, + int64_t ext_off, Error **errp) +{ + BDRVParallelsState *s = bs->opaque; + int ret; + uint8_t *ext_cluster = qemu_blockalign(bs, s->cluster_size); + + assert(ext_off > 0); + + ret = bdrv_pread(bs->file, ext_off, ext_cluster, s->cluster_size); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to read Format Extension cluster"); + goto out; + } + + ret = parallels_parse_format_extension(bs, ext_cluster, errp); + +out: + qemu_vfree(ext_cluster); + + return ret; +} diff --git a/block/parallels.c b/block/parallels.c index 3c22dfdc9d..6ebad2a2bb 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -29,6 +29,7 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qapi/error.h" #include "block/block_int.h" #include "block/qdict.h" @@ -421,7 +422,6 @@ static int coroutine_fn parallels_co_check(BlockDriverState *bs, int ret; uint32_t i; bool flush_bat = false; - int cluster_size = s->tracks << BDRV_SECTOR_BITS; size = bdrv_getlength(bs->file->bs); if (size < 0) { @@ -472,7 +472,7 @@ static int coroutine_fn parallels_co_check(BlockDriverState *bs, high_off = off; } - if (prev_off != 0 && (prev_off + cluster_size) != off) { + if (prev_off != 0 && (prev_off + s->cluster_size) != off) { res->bfi.fragmented_clusters++; } prev_off = off; @@ -487,10 +487,10 @@ static int coroutine_fn parallels_co_check(BlockDriverState *bs, } } - res->image_end_offset = high_off + cluster_size; + res->image_end_offset = high_off + s->cluster_size; if (size > res->image_end_offset) { int64_t count; - count = DIV_ROUND_UP(size - res->image_end_offset, cluster_size); + count = DIV_ROUND_UP(size - res->image_end_offset, s->cluster_size); fprintf(stderr, "%s space leaked at the end of the image %" PRId64 "\n", fix & BDRV_FIX_LEAKS ? "Repairing" : "ERROR", size - res->image_end_offset); @@ -771,6 +771,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, ret = -EFBIG; goto fail; } + s->cluster_size = s->tracks << BDRV_SECTOR_BITS; s->bat_size = le32_to_cpu(ph.bat_entries); if (s->bat_size > INT_MAX / sizeof(uint32_t)) { @@ -843,6 +844,23 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, goto fail_options; } + if (ph.ext_off) { + if (flags & BDRV_O_RDWR) { + /* + * It's unsafe to open image RW if there is an extension (as we + * don't support it). But parallels driver in QEMU historically + * ignores the extension, so print warning and don't care. + */ + warn_report("Format Extension ignored in RW mode"); + } else { + ret = parallels_read_format_extension( + bs, le64_to_cpu(ph.ext_off) << BDRV_SECTOR_BITS, errp); + if (ret < 0) { + goto fail; + } + } + } + if ((flags & BDRV_O_RDWR) && !(flags & BDRV_O_INACTIVE)) { s->header->inuse = cpu_to_le32(HEADER_INUSE_MAGIC); ret = parallels_update_header(bs); diff --git a/block/parallels.h b/block/parallels.h index 5aa101cfc8..f22f43f988 100644 --- a/block/parallels.h +++ b/block/parallels.h @@ -48,7 +48,8 @@ typedef struct ParallelsHeader { uint64_t nb_sectors; uint32_t inuse; uint32_t data_off; - char padding[12]; + uint32_t flags; + uint64_t ext_off; } QEMU_PACKED ParallelsHeader; typedef enum ParallelsPreallocMode { @@ -79,9 +80,13 @@ typedef struct BDRVParallelsState { ParallelsPreallocMode prealloc_mode; unsigned int tracks; + unsigned int cluster_size; unsigned int off_multiplier; Error *migration_blocker; } BDRVParallelsState; +int parallels_read_format_extension(BlockDriverState *bs, + int64_t ext_off, Error **errp); + #endif diff --git a/block/preallocate.c b/block/preallocate.c new file mode 100644 index 0000000000..b619206304 --- /dev/null +++ b/block/preallocate.c @@ -0,0 +1,559 @@ +/* + * preallocate filter driver + * + * The driver performs preallocate operation: it is injected above + * some node, and before each write over EOF it does additional preallocating + * write-zeroes request. + * + * Copyright (c) 2020 Virtuozzo International GmbH. + * + * Author: + * Sementsov-Ogievskiy Vladimir + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "qemu/osdep.h" + +#include "qapi/error.h" +#include "qemu/module.h" +#include "qemu/option.h" +#include "qemu/units.h" +#include "block/block_int.h" + + +typedef struct PreallocateOpts { + int64_t prealloc_size; + int64_t prealloc_align; +} PreallocateOpts; + +typedef struct BDRVPreallocateState { + PreallocateOpts opts; + + /* + * Track real data end, to crop preallocation on close. If < 0 the status is + * unknown. + * + * @data_end is a maximum of file size on open (or when we get write/resize + * permissions) and all write request ends after it. So it's safe to + * truncate to data_end if it is valid. + */ + int64_t data_end; + + /* + * Start of trailing preallocated area which reads as zero. May be smaller + * than data_end, if user does over-EOF write zero operation. If < 0 the + * status is unknown. + * + * If both @zero_start and @file_end are valid, the region + * [@zero_start, @file_end) is known to be preallocated zeroes. If @file_end + * is not valid, @zero_start doesn't make much sense. + */ + int64_t zero_start; + + /* + * Real end of file. Actually the cache for bdrv_getlength(bs->file->bs), + * to avoid extra lseek() calls on each write operation. If < 0 the status + * is unknown. + */ + int64_t file_end; + + /* + * All three states @data_end, @zero_start and @file_end are guaranteed to + * be invalid (< 0) when we don't have both exclusive BLK_PERM_RESIZE and + * BLK_PERM_WRITE permissions on file child. + */ +} BDRVPreallocateState; + +#define PREALLOCATE_OPT_PREALLOC_ALIGN "prealloc-align" +#define PREALLOCATE_OPT_PREALLOC_SIZE "prealloc-size" +static QemuOptsList runtime_opts = { + .name = "preallocate", + .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), + .desc = { + { + .name = PREALLOCATE_OPT_PREALLOC_ALIGN, + .type = QEMU_OPT_SIZE, + .help = "on preallocation, align file length to this number, " + "default 1M", + }, + { + .name = PREALLOCATE_OPT_PREALLOC_SIZE, + .type = QEMU_OPT_SIZE, + .help = "how much to preallocate, default 128M", + }, + { /* end of list */ } + }, +}; + +static bool preallocate_absorb_opts(PreallocateOpts *dest, QDict *options, + BlockDriverState *child_bs, Error **errp) +{ + QemuOpts *opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); + + if (!qemu_opts_absorb_qdict(opts, options, errp)) { + return false; + } + + dest->prealloc_align = + qemu_opt_get_size(opts, PREALLOCATE_OPT_PREALLOC_ALIGN, 1 * MiB); + dest->prealloc_size = + qemu_opt_get_size(opts, PREALLOCATE_OPT_PREALLOC_SIZE, 128 * MiB); + + qemu_opts_del(opts); + + if (!QEMU_IS_ALIGNED(dest->prealloc_align, BDRV_SECTOR_SIZE)) { + error_setg(errp, "prealloc-align parameter of preallocate filter " + "is not aligned to %llu", BDRV_SECTOR_SIZE); + return false; + } + + if (!QEMU_IS_ALIGNED(dest->prealloc_align, + child_bs->bl.request_alignment)) { + error_setg(errp, "prealloc-align parameter of preallocate filter " + "is not aligned to underlying node request alignment " + "(%" PRIi32 ")", child_bs->bl.request_alignment); + return false; + } + + return true; +} + +static int preallocate_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVPreallocateState *s = bs->opaque; + + /* + * s->data_end and friends should be initialized on permission update. + * For this to work, mark them invalid. + */ + s->file_end = s->zero_start = s->data_end = -EINVAL; + + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, + BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, + false, errp); + if (!bs->file) { + return -EINVAL; + } + + if (!preallocate_absorb_opts(&s->opts, options, bs->file->bs, errp)) { + return -EINVAL; + } + + bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | + (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); + + bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | + ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & + bs->file->bs->supported_zero_flags); + + return 0; +} + +static void preallocate_close(BlockDriverState *bs) +{ + int ret; + BDRVPreallocateState *s = bs->opaque; + + if (s->data_end < 0) { + return; + } + + if (s->file_end < 0) { + s->file_end = bdrv_getlength(bs->file->bs); + if (s->file_end < 0) { + return; + } + } + + if (s->data_end < s->file_end) { + ret = bdrv_truncate(bs->file, s->data_end, true, PREALLOC_MODE_OFF, 0, + NULL); + s->file_end = ret < 0 ? ret : s->data_end; + } +} + + +/* + * Handle reopen. + * + * We must implement reopen handlers, otherwise reopen just don't work. Handle + * new options and don't care about preallocation state, as it is handled in + * set/check permission handlers. + */ + +static int preallocate_reopen_prepare(BDRVReopenState *reopen_state, + BlockReopenQueue *queue, Error **errp) +{ + PreallocateOpts *opts = g_new0(PreallocateOpts, 1); + + if (!preallocate_absorb_opts(opts, reopen_state->options, + reopen_state->bs->file->bs, errp)) { + g_free(opts); + return -EINVAL; + } + + reopen_state->opaque = opts; + + return 0; +} + +static void preallocate_reopen_commit(BDRVReopenState *state) +{ + BDRVPreallocateState *s = state->bs->opaque; + + s->opts = *(PreallocateOpts *)state->opaque; + + g_free(state->opaque); + state->opaque = NULL; +} + +static void preallocate_reopen_abort(BDRVReopenState *state) +{ + g_free(state->opaque); + state->opaque = NULL; +} + +static coroutine_fn int preallocate_co_preadv_part( + BlockDriverState *bs, uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset, int flags) +{ + return bdrv_co_preadv_part(bs->file, offset, bytes, qiov, qiov_offset, + flags); +} + +static int coroutine_fn preallocate_co_pdiscard(BlockDriverState *bs, + int64_t offset, int bytes) +{ + return bdrv_co_pdiscard(bs->file, offset, bytes); +} + +static bool can_write_resize(uint64_t perm) +{ + return (perm & BLK_PERM_WRITE) && (perm & BLK_PERM_RESIZE); +} + +static bool has_prealloc_perms(BlockDriverState *bs) +{ + BDRVPreallocateState *s = bs->opaque; + + if (can_write_resize(bs->file->perm)) { + assert(!(bs->file->shared_perm & BLK_PERM_WRITE)); + assert(!(bs->file->shared_perm & BLK_PERM_RESIZE)); + return true; + } + + assert(s->data_end < 0); + assert(s->zero_start < 0); + assert(s->file_end < 0); + return false; +} + +/* + * Call on each write. Returns true if @want_merge_zero is true and the region + * [offset, offset + bytes) is zeroed (as a result of this call or earlier + * preallocation). + * + * want_merge_zero is used to merge write-zero request with preallocation in + * one bdrv_co_pwrite_zeroes() call. + */ +static bool coroutine_fn handle_write(BlockDriverState *bs, int64_t offset, + int64_t bytes, bool want_merge_zero) +{ + BDRVPreallocateState *s = bs->opaque; + int64_t end = offset + bytes; + int64_t prealloc_start, prealloc_end; + int ret; + + if (!has_prealloc_perms(bs)) { + /* We don't have state neither should try to recover it */ + return false; + } + + if (s->data_end < 0) { + s->data_end = bdrv_getlength(bs->file->bs); + if (s->data_end < 0) { + return false; + } + + if (s->file_end < 0) { + s->file_end = s->data_end; + } + } + + if (end <= s->data_end) { + return false; + } + + /* We have valid s->data_end, and request writes beyond it. */ + + s->data_end = end; + if (s->zero_start < 0 || !want_merge_zero) { + s->zero_start = end; + } + + if (s->file_end < 0) { + s->file_end = bdrv_getlength(bs->file->bs); + if (s->file_end < 0) { + return false; + } + } + + /* Now s->data_end, s->zero_start and s->file_end are valid. */ + + if (end <= s->file_end) { + /* No preallocation needed. */ + return want_merge_zero && offset >= s->zero_start; + } + + /* Now we want new preallocation, as request writes beyond s->file_end. */ + + prealloc_start = want_merge_zero ? MIN(offset, s->file_end) : s->file_end; + prealloc_end = QEMU_ALIGN_UP(end + s->opts.prealloc_size, + s->opts.prealloc_align); + + ret = bdrv_co_pwrite_zeroes( + bs->file, prealloc_start, prealloc_end - prealloc_start, + BDRV_REQ_NO_FALLBACK | BDRV_REQ_SERIALISING | BDRV_REQ_NO_WAIT); + if (ret < 0) { + s->file_end = ret; + return false; + } + + s->file_end = prealloc_end; + return want_merge_zero; +} + +static int coroutine_fn preallocate_co_pwrite_zeroes(BlockDriverState *bs, + int64_t offset, int bytes, BdrvRequestFlags flags) +{ + bool want_merge_zero = + !(flags & ~(BDRV_REQ_ZERO_WRITE | BDRV_REQ_NO_FALLBACK)); + if (handle_write(bs, offset, bytes, want_merge_zero)) { + return 0; + } + + return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); +} + +static coroutine_fn int preallocate_co_pwritev_part(BlockDriverState *bs, + uint64_t offset, + uint64_t bytes, + QEMUIOVector *qiov, + size_t qiov_offset, + int flags) +{ + handle_write(bs, offset, bytes, false); + + return bdrv_co_pwritev_part(bs->file, offset, bytes, qiov, qiov_offset, + flags); +} + +static int coroutine_fn +preallocate_co_truncate(BlockDriverState *bs, int64_t offset, + bool exact, PreallocMode prealloc, + BdrvRequestFlags flags, Error **errp) +{ + ERRP_GUARD(); + BDRVPreallocateState *s = bs->opaque; + int ret; + + if (s->data_end >= 0 && offset > s->data_end) { + if (s->file_end < 0) { + s->file_end = bdrv_getlength(bs->file->bs); + if (s->file_end < 0) { + error_setg(errp, "failed to get file length"); + return s->file_end; + } + } + + if (prealloc == PREALLOC_MODE_FALLOC) { + /* + * If offset <= s->file_end, the task is already done, just + * update s->data_end, to move part of "filter preallocation" + * to "preallocation requested by user". + * Otherwise just proceed to preallocate missing part. + */ + if (offset <= s->file_end) { + s->data_end = offset; + return 0; + } + } else { + /* + * We have to drop our preallocation, to + * - avoid "Cannot use preallocation for shrinking files" in + * case of offset < file_end + * - give PREALLOC_MODE_OFF a chance to keep small disk + * usage + * - give PREALLOC_MODE_FULL a chance to actually write the + * whole region as user expects + */ + if (s->file_end > s->data_end) { + ret = bdrv_co_truncate(bs->file, s->data_end, true, + PREALLOC_MODE_OFF, 0, errp); + if (ret < 0) { + s->file_end = ret; + error_prepend(errp, "preallocate-filter: failed to drop " + "write-zero preallocation: "); + return ret; + } + s->file_end = s->data_end; + } + } + + s->data_end = offset; + } + + ret = bdrv_co_truncate(bs->file, offset, exact, prealloc, flags, errp); + if (ret < 0) { + s->file_end = s->zero_start = s->data_end = ret; + return ret; + } + + if (has_prealloc_perms(bs)) { + s->file_end = s->zero_start = s->data_end = offset; + } + return 0; +} + +static int coroutine_fn preallocate_co_flush(BlockDriverState *bs) +{ + return bdrv_co_flush(bs->file->bs); +} + +static int64_t preallocate_getlength(BlockDriverState *bs) +{ + int64_t ret; + BDRVPreallocateState *s = bs->opaque; + + if (s->data_end >= 0) { + return s->data_end; + } + + ret = bdrv_getlength(bs->file->bs); + + if (has_prealloc_perms(bs)) { + s->file_end = s->zero_start = s->data_end = ret; + } + + return ret; +} + +static int preallocate_check_perm(BlockDriverState *bs, + uint64_t perm, uint64_t shared, Error **errp) +{ + BDRVPreallocateState *s = bs->opaque; + + if (s->data_end >= 0 && !can_write_resize(perm)) { + /* + * Lose permissions. + * We should truncate in check_perm, as in set_perm bs->file->perm will + * be already changed, and we should not violate it. + */ + if (s->file_end < 0) { + s->file_end = bdrv_getlength(bs->file->bs); + if (s->file_end < 0) { + error_setg(errp, "Failed to get file length"); + return s->file_end; + } + } + + if (s->data_end < s->file_end) { + int ret = bdrv_truncate(bs->file, s->data_end, true, + PREALLOC_MODE_OFF, 0, NULL); + if (ret < 0) { + error_setg(errp, "Failed to drop preallocation"); + s->file_end = ret; + return ret; + } + s->file_end = s->data_end; + } + } + + return 0; +} + +static void preallocate_set_perm(BlockDriverState *bs, + uint64_t perm, uint64_t shared) +{ + BDRVPreallocateState *s = bs->opaque; + + if (can_write_resize(perm)) { + if (s->data_end < 0) { + s->data_end = s->file_end = s->zero_start = + bdrv_getlength(bs->file->bs); + } + } else { + /* + * We drop our permissions, as well as allow shared + * permissions (see preallocate_child_perm), anyone will be able to + * change the child, so mark all states invalid. We'll regain control if + * get good permissions back. + */ + s->data_end = s->file_end = s->zero_start = -EINVAL; + } +} + +static void preallocate_child_perm(BlockDriverState *bs, BdrvChild *c, + BdrvChildRole role, BlockReopenQueue *reopen_queue, + uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared) +{ + bdrv_default_perms(bs, c, role, reopen_queue, perm, shared, nperm, nshared); + + if (can_write_resize(perm)) { + /* This should come by default, but let's enforce: */ + *nperm |= BLK_PERM_WRITE | BLK_PERM_RESIZE; + + /* + * Don't share, to keep our states s->file_end, s->data_end and + * s->zero_start valid. + */ + *nshared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE); + } +} + +BlockDriver bdrv_preallocate_filter = { + .format_name = "preallocate", + .instance_size = sizeof(BDRVPreallocateState), + + .bdrv_getlength = preallocate_getlength, + .bdrv_open = preallocate_open, + .bdrv_close = preallocate_close, + + .bdrv_reopen_prepare = preallocate_reopen_prepare, + .bdrv_reopen_commit = preallocate_reopen_commit, + .bdrv_reopen_abort = preallocate_reopen_abort, + + .bdrv_co_preadv_part = preallocate_co_preadv_part, + .bdrv_co_pwritev_part = preallocate_co_pwritev_part, + .bdrv_co_pwrite_zeroes = preallocate_co_pwrite_zeroes, + .bdrv_co_pdiscard = preallocate_co_pdiscard, + .bdrv_co_flush = preallocate_co_flush, + .bdrv_co_truncate = preallocate_co_truncate, + + .bdrv_check_perm = preallocate_check_perm, + .bdrv_set_perm = preallocate_set_perm, + .bdrv_child_perm = preallocate_child_perm, + + .has_variable_length = true, + .is_filter = true, +}; + +static void bdrv_preallocate_init(void) +{ + bdrv_register(&bdrv_preallocate_filter); +} + +block_init(bdrv_preallocate_init); diff --git a/block/qapi.c b/block/qapi.c index afd9f3b4a7..943e7b15ad 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -47,7 +47,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, Error **errp) { ImageInfo **p_image_info; - BlockDriverState *bs0; + BlockDriverState *bs0, *backing; BlockDeviceInfo *info; if (!bs->drv) { @@ -62,7 +62,6 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, info->ro = bs->read_only; info->drv = g_strdup(bs->drv->format_name); info->encrypted = bs->encrypted; - info->encryption_key_missing = false; info->cache = g_new(BlockdevCacheInfo, 1); *info->cache = (BlockdevCacheInfo) { @@ -76,9 +75,10 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, info->node_name = g_strdup(bs->node_name); } - if (bs->backing_file[0]) { + backing = bdrv_cow_bs(bs); + if (backing) { info->has_backing_file = true; - info->backing_file = g_strdup(bs->backing_file); + info->backing_file = g_strdup(backing->filename); } if (!QLIST_EMPTY(&bs->dirty_bitmaps)) { @@ -163,9 +163,13 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, break; } - if (bs0->drv && bs0->backing) { + if (bs0->drv && bdrv_filter_or_cow_child(bs0)) { + /* + * Put any filtered child here (for backwards compatibility to when + * we put bs0->backing here, which might be any filtered child). + */ info->backing_file_depth++; - bs0 = bs0->backing->bs; + bs0 = bdrv_filter_or_cow_bs(bs0); (*p_image_info)->has_backing_image = true; p_image_info = &((*p_image_info)->backing_image); } else { @@ -174,9 +178,8 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, /* Skip automatically inserted nodes that the user isn't aware of for * query-block (blk != NULL), but not for query-named-block-nodes */ - while (blk && bs0->drv && bs0->implicit) { - bs0 = backing_bs(bs0); - assert(bs0); + if (blk) { + bs0 = bdrv_skip_implicit_filters(bs0); } } @@ -194,7 +197,7 @@ int bdrv_query_snapshot_info_list(BlockDriverState *bs, { int i, sn_count; QEMUSnapshotInfo *sn_tab = NULL; - SnapshotInfoList *info_list, *cur_item = NULL, *head = NULL; + SnapshotInfoList *head = NULL, **tail = &head; SnapshotInfo *info; sn_count = bdrv_snapshot_list(bs, &sn_tab); @@ -226,18 +229,10 @@ int bdrv_query_snapshot_info_list(BlockDriverState *bs, info->date_nsec = sn_tab[i].date_nsec; info->vm_clock_sec = sn_tab[i].vm_clock_nsec / 1000000000; info->vm_clock_nsec = sn_tab[i].vm_clock_nsec % 1000000000; + info->icount = sn_tab[i].icount; + info->has_icount = sn_tab[i].icount != -1ULL; - info_list = g_new0(SnapshotInfoList, 1); - info_list->value = info; - - /* XXX: waiting for the qapi to support qemu-queue.h types */ - if (!cur_item) { - head = cur_item = info_list; - } else { - cur_item->next = info_list; - cur_item = info_list; - } - + QAPI_LIST_APPEND(tail, info); } g_free(sn_tab); @@ -288,7 +283,7 @@ void bdrv_query_image_info(BlockDriverState *bs, info->virtual_size = size; info->actual_size = bdrv_get_allocated_file_size(bs); info->has_actual_size = info->actual_size >= 0; - if (bdrv_is_encrypted(bs)) { + if (bs->encrypted) { info->encrypted = true; info->has_encrypted = true; } @@ -311,6 +306,7 @@ void bdrv_query_image_info(BlockDriverState *bs, backing_filename = bs->backing_file; if (backing_filename[0] != '\0') { char *backing_filename2; + info->backing_filename = g_strdup(backing_filename); info->has_backing_filename = true; backing_filename2 = bdrv_get_full_backing_filename(bs, NULL); @@ -362,9 +358,7 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, char *qdev; /* Skip automatically inserted nodes that the user isn't aware of */ - while (bs && bs->drv && bs->implicit) { - bs = backing_bs(bs); - } + bs = bdrv_skip_implicit_filters(bs); info->device = g_strdup(blk_name(blk)); info->type = g_strdup("unknown"); @@ -389,11 +383,6 @@ static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, info->io_status = blk_iostatus(blk); } - if (bs && !QLIST_EMPTY(&bs->dirty_bitmaps)) { - info->has_dirty_bitmaps = true; - info->dirty_bitmaps = bdrv_query_dirty_bitmaps(bs); - } - if (bs && bs->drv) { info->has_inserted = true; info->inserted = bdrv_block_device_info(blk, bs, false, errp); @@ -413,17 +402,12 @@ static uint64List *uint64_list(uint64_t *list, int size) { int i; uint64List *out_list = NULL; - uint64List **pout_list = &out_list; + uint64List **tail = &out_list; for (i = 0; i < size; i++) { - uint64List *entry = g_new(uint64List, 1); - entry->value = list[i]; - *pout_list = entry; - pout_list = &entry->next; + QAPI_LIST_APPEND(tail, list[i]); } - *pout_list = NULL; - return out_list; } @@ -481,12 +465,7 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk) ds->account_failed = stats->account_failed; while ((ts = block_acct_interval_next(stats, ts))) { - BlockDeviceTimedStatsList *timed_stats = - g_malloc0(sizeof(*timed_stats)); BlockDeviceTimedStats *dev_stats = g_malloc0(sizeof(*dev_stats)); - timed_stats->next = ds->timed_stats; - timed_stats->value = dev_stats; - ds->timed_stats = timed_stats; TimedAverage *rd = &ts->latency[BLOCK_ACCT_READ]; TimedAverage *wr = &ts->latency[BLOCK_ACCT_WRITE]; @@ -510,6 +489,8 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk) block_acct_queue_depth(ts, BLOCK_ACCT_READ); dev_stats->avg_wr_queue_depth = block_acct_queue_depth(ts, BLOCK_ACCT_WRITE); + + QAPI_LIST_PREPEND(ds->timed_stats, dev_stats); } bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_READ], @@ -526,6 +507,8 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk) static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs, bool blk_level) { + BdrvChild *parent_child; + BlockDriverState *filter_or_cow_bs; BlockStats *s = NULL; s = g_malloc0(sizeof(*s)); @@ -538,9 +521,8 @@ static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs, /* Skip automatically inserted nodes that the user isn't aware of in * a BlockBackend-level command. Stay at the exact node for a node-level * command. */ - while (blk_level && bs->drv && bs->implicit) { - bs = backing_bs(bs); - assert(bs); + if (blk_level) { + bs = bdrv_skip_implicit_filters(bs); } if (bdrv_get_node_name(bs)[0]) { @@ -555,14 +537,46 @@ static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs, s->has_driver_specific = true; } - if (bs->file) { + parent_child = bdrv_primary_child(bs); + if (!parent_child || + !(parent_child->role & (BDRV_CHILD_DATA | BDRV_CHILD_FILTERED))) + { + BdrvChild *c; + + /* + * Look for a unique data-storing child. We do not need to look for + * filtered children, as there would be only one and it would have been + * the primary child. + */ + parent_child = NULL; + QLIST_FOREACH(c, &bs->children, next) { + if (c->role & BDRV_CHILD_DATA) { + if (parent_child) { + /* + * There are multiple data-storing children and we cannot + * choose between them. + */ + parent_child = NULL; + break; + } + parent_child = c; + } + } + } + if (parent_child) { s->has_parent = true; - s->parent = bdrv_query_bds_stats(bs->file->bs, blk_level); + s->parent = bdrv_query_bds_stats(parent_child->bs, blk_level); } - if (blk_level && bs->backing) { + filter_or_cow_bs = bdrv_filter_or_cow_bs(bs); + if (blk_level && filter_or_cow_bs) { + /* + * Put any filtered or COW child here (for backwards + * compatibility to when we put bs0->backing here, which might + * be either) + */ s->has_backing = true; - s->backing = bdrv_query_bds_stats(bs->backing->bs, blk_level); + s->backing = bdrv_query_bds_stats(filter_or_cow_bs, blk_level); } return s; @@ -601,26 +615,21 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes, bool query_nodes, Error **errp) { - BlockStatsList *head = NULL, **p_next = &head; + BlockStatsList *head = NULL, **tail = &head; BlockBackend *blk; BlockDriverState *bs; /* Just to be safe if query_nodes is not always initialized */ if (has_query_nodes && query_nodes) { for (bs = bdrv_next_node(NULL); bs; bs = bdrv_next_node(bs)) { - BlockStatsList *info = g_malloc0(sizeof(*info)); AioContext *ctx = bdrv_get_aio_context(bs); aio_context_acquire(ctx); - info->value = bdrv_query_bds_stats(bs, false); + QAPI_LIST_APPEND(tail, bdrv_query_bds_stats(bs, false)); aio_context_release(ctx); - - *p_next = info; - p_next = &info->next; } } else { for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) { - BlockStatsList *info; AioContext *ctx = blk_get_aio_context(blk); BlockStats *s; char *qdev; @@ -645,10 +654,7 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes, bdrv_query_blk_stats(s->stats, blk); aio_context_release(ctx); - info = g_malloc0(sizeof(*info)); - info->value = s; - *p_next = info; - p_next = &info->next; + QAPI_LIST_APPEND(tail, s); } } @@ -658,14 +664,15 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes, void bdrv_snapshot_dump(QEMUSnapshotInfo *sn) { char date_buf[128], clock_buf[128]; + char icount_buf[128] = {0}; struct tm tm; time_t ti; int64_t secs; char *sizing = NULL; if (!sn) { - qemu_printf("%-10s%-20s%11s%20s%15s", - "ID", "TAG", "VM SIZE", "DATE", "VM CLOCK"); + qemu_printf("%-10s%-17s%8s%20s%13s%11s", + "ID", "TAG", "VM SIZE", "DATE", "VM CLOCK", "ICOUNT"); } else { ti = sn->date_sec; localtime_r(&ti, &tm); @@ -679,11 +686,16 @@ void bdrv_snapshot_dump(QEMUSnapshotInfo *sn) (int)(secs % 60), (int)((sn->vm_clock_nsec / 1000000) % 1000)); sizing = size_to_str(sn->vm_state_size); - qemu_printf("%-10s%-20s%11s%20s%15s", + if (sn->icount != -1ULL) { + snprintf(icount_buf, sizeof(icount_buf), + "%"PRId64, sn->icount); + } + qemu_printf("%-9s %-16s %8s%20s%13s%11s", sn->id_str, sn->name, sizing, date_buf, - clock_buf); + clock_buf, + icount_buf); } g_free(sizing); } @@ -845,6 +857,8 @@ void bdrv_image_info_dump(ImageInfo *info) .date_nsec = elem->value->date_nsec, .vm_clock_nsec = elem->value->vm_clock_sec * 1000000000ULL + elem->value->vm_clock_nsec, + .icount = elem->value->has_icount ? + elem->value->icount : -1ULL, }; pstrcpy(sn.id_str, sizeof(sn.id_str), elem->value->id); diff --git a/block/qcow.c b/block/qcow.c index e514a86fe5..f8919a44d1 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -105,15 +105,6 @@ static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; } -static QemuOptsList qcow_runtime_opts = { - .name = "qcow", - .head = QTAILQ_HEAD_INITIALIZER(qcow_runtime_opts.head), - .desc = { - BLOCK_CRYPTO_OPT_DEF_QCOW_KEY_SECRET("encrypt."), - { /* end of list */ } - }, -}; - static int qcow_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 8c34b2aef7..8fb4731551 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -42,6 +42,9 @@ #define BME_MIN_GRANULARITY_BITS 9 #define BME_MAX_NAME_SIZE 1023 +/* Size of bitmap table entries */ +#define BME_TABLE_ENTRY_SIZE (sizeof(uint64_t)) + QEMU_BUILD_BUG_ON(BME_MAX_NAME_SIZE != BDRV_BITMAP_MAX_NAME_SIZE); #if BME_MAX_TABLE_SIZE * 8ULL > INT_MAX @@ -232,7 +235,7 @@ static int bitmap_table_load(BlockDriverState *bs, Qcow2BitmapTable *tb, assert(tb->size <= BME_MAX_TABLE_SIZE); ret = bdrv_pread(bs->file, tb->offset, - table, tb->size * sizeof(uint64_t)); + table, tb->size * BME_TABLE_ENTRY_SIZE); if (ret < 0) { goto fail; } @@ -265,7 +268,7 @@ static int free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb) } clear_bitmap_table(bs, bitmap_table, tb->size); - qcow2_free_clusters(bs, tb->offset, tb->size * sizeof(uint64_t), + qcow2_free_clusters(bs, tb->offset, tb->size * BME_TABLE_ENTRY_SIZE, QCOW2_DISCARD_OTHER); g_free(bitmap_table); @@ -275,18 +278,6 @@ static int free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb) return 0; } -/* Return the disk size covered by a single qcow2 cluster of bitmap data. */ -static uint64_t bytes_covered_by_bitmap_cluster(const BDRVQcow2State *s, - const BdrvDirtyBitmap *bitmap) -{ - uint64_t granularity = bdrv_dirty_bitmap_granularity(bitmap); - uint64_t limit = granularity * (s->cluster_size << 3); - - assert(QEMU_IS_ALIGNED(limit, - bdrv_dirty_bitmap_serialization_align(bitmap))); - return limit; -} - /* load_bitmap_data * @bitmap_table entries must satisfy specification constraints. * @bitmap must be cleared */ @@ -309,7 +300,7 @@ static int load_bitmap_data(BlockDriverState *bs, } buf = g_malloc(s->cluster_size); - limit = bytes_covered_by_bitmap_cluster(s, bitmap); + limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap); for (i = 0, offset = 0; i < tab_size; ++i, offset += limit) { uint64_t count = MIN(bm_size - offset, limit); uint64_t entry = bitmap_table[i]; @@ -690,7 +681,7 @@ int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res, ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, refcount_table_size, bm->table.offset, - bm->table.size * sizeof(uint64_t)); + bm->table.size * BME_TABLE_ENTRY_SIZE); if (ret < 0) { goto out; } @@ -959,25 +950,27 @@ static void set_readonly_helper(gpointer bitmap, gpointer value) bdrv_dirty_bitmap_set_readonly(bitmap, (bool)value); } -/* qcow2_load_dirty_bitmaps() - * Return value is a hint for caller: true means that the Qcow2 header was - * updated. (false doesn't mean that the header should be updated by the - * caller, it just means that updating was not needed or the image cannot be - * written to). - * On failure the function returns false. +/* + * Return true on success, false on failure. + * If header_updated is not NULL then it is set appropriately regardless of + * the return value. */ -bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) +bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, bool *header_updated, + Error **errp) { BDRVQcow2State *s = bs->opaque; Qcow2BitmapList *bm_list; Qcow2Bitmap *bm; GSList *created_dirty_bitmaps = NULL; - bool header_updated = false; bool needs_update = false; + if (header_updated) { + *header_updated = false; + } + if (s->nb_bitmaps == 0) { /* No bitmaps - nothing to do */ - return false; + return true; } bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, @@ -1033,7 +1026,9 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) error_setg_errno(errp, -ret, "Can't update bitmap directory"); goto fail; } - header_updated = true; + if (header_updated) { + *header_updated = true; + } } if (!can_write(bs)) { @@ -1044,7 +1039,7 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) g_slist_free(created_dirty_bitmaps); bitmap_list_free(bm_list); - return header_updated; + return true; fail: g_slist_foreach(created_dirty_bitmaps, release_dirty_bitmap_helper, bs); @@ -1058,7 +1053,7 @@ fail: static Qcow2BitmapInfoFlagsList *get_bitmap_info_flags(uint32_t flags) { Qcow2BitmapInfoFlagsList *list = NULL; - Qcow2BitmapInfoFlagsList **plist = &list; + Qcow2BitmapInfoFlagsList **tail = &list; int i; static const struct { @@ -1073,11 +1068,7 @@ static Qcow2BitmapInfoFlagsList *get_bitmap_info_flags(uint32_t flags) for (i = 0; i < map_size; ++i) { if (flags & map[i].bme) { - Qcow2BitmapInfoFlagsList *entry = - g_new0(Qcow2BitmapInfoFlagsList, 1); - entry->value = map[i].info; - *plist = entry; - plist = &entry->next; + QAPI_LIST_APPEND(tail, map[i].info); flags &= ~map[i].bme; } } @@ -1090,44 +1081,43 @@ static Qcow2BitmapInfoFlagsList *get_bitmap_info_flags(uint32_t flags) /* * qcow2_get_bitmap_info_list() * Returns a list of QCOW2 bitmap details. - * In case of no bitmaps, the function returns NULL and - * the @errp parameter is not set. - * When bitmap information can not be obtained, the function returns - * NULL and the @errp parameter is set. + * On success return true with info_list set (note, that if there are no + * bitmaps, info_list is set to NULL). + * On failure return false with errp set. */ -Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs, - Error **errp) +bool qcow2_get_bitmap_info_list(BlockDriverState *bs, + Qcow2BitmapInfoList **info_list, Error **errp) { BDRVQcow2State *s = bs->opaque; Qcow2BitmapList *bm_list; Qcow2Bitmap *bm; - Qcow2BitmapInfoList *list = NULL; - Qcow2BitmapInfoList **plist = &list; + Qcow2BitmapInfoList **tail; if (s->nb_bitmaps == 0) { - return NULL; + *info_list = NULL; + return true; } bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, s->bitmap_directory_size, errp); - if (bm_list == NULL) { - return NULL; + if (!bm_list) { + return false; } + *info_list = NULL; + tail = info_list; + QSIMPLEQ_FOREACH(bm, bm_list, entry) { Qcow2BitmapInfo *info = g_new0(Qcow2BitmapInfo, 1); - Qcow2BitmapInfoList *obj = g_new0(Qcow2BitmapInfoList, 1); info->granularity = 1U << bm->granularity_bits; info->name = g_strdup(bm->name); info->flags = get_bitmap_info_flags(bm->flags & ~BME_RESERVED_FLAGS); - obj->value = info; - *plist = obj; - plist = &obj->next; + QAPI_LIST_APPEND(tail, info); } bitmap_list_free(bm_list); - return list; + return true; } int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp) @@ -1307,7 +1297,7 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, } buf = g_malloc(s->cluster_size); - limit = bytes_covered_by_bitmap_cluster(s, bitmap); + limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap); assert(DIV_ROUND_UP(bm_size, limit) == tb_size); offset = 0; @@ -1529,9 +1519,10 @@ out: * readonly to begin with, and whether we opened directly or reopened to that * state shouldn't matter for the state we get afterward. */ -void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, +bool qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, bool release_stored, Error **errp) { + ERRP_GUARD(); BdrvDirtyBitmap *bitmap; BDRVQcow2State *s = bs->opaque; uint32_t new_nb_bitmaps = s->nb_bitmaps; @@ -1551,7 +1542,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, s->bitmap_directory_size, errp); if (bm_list == NULL) { - return; + return false; } } @@ -1666,7 +1657,7 @@ success: } bitmap_list_free(bm_list); - return; + return true; fail: QSIMPLEQ_FOREACH(bm, bm_list, entry) { @@ -1684,16 +1675,14 @@ fail: } bitmap_list_free(bm_list); + return false; } int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp) { BdrvDirtyBitmap *bitmap; - Error *local_err = NULL; - qcow2_store_persistent_dirty_bitmaps(bs, false, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); + if (!qcow2_store_persistent_dirty_bitmaps(bs, false, errp)) { return -EINVAL; } @@ -1797,7 +1786,7 @@ uint64_t qcow2_get_persistent_dirty_bitmap_size(BlockDriverState *in_bs, /* Assume the entire bitmap is allocated */ bitmaps_size += bmclusters * cluster_size; /* Also reserve space for the bitmap table entries */ - bitmaps_size += ROUND_UP(bmclusters * sizeof(uint64_t), + bitmaps_size += ROUND_UP(bmclusters * BME_TABLE_ENTRY_SIZE, cluster_size); /* And space for contribution to bitmap directory size */ bitmap_dir_size += calc_dir_entry_size(strlen(name), 0); diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 550850b264..bd0597842f 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -47,8 +47,8 @@ int qcow2_shrink_l1_table(BlockDriverState *bs, uint64_t exact_size) BLKDBG_EVENT(bs->file, BLKDBG_L1_SHRINK_WRITE_TABLE); ret = bdrv_pwrite_zeroes(bs->file, s->l1_table_offset + - new_l1_size * sizeof(uint64_t), - (s->l1_size - new_l1_size) * sizeof(uint64_t), 0); + new_l1_size * L1E_SIZE, + (s->l1_size - new_l1_size) * L1E_SIZE, 0); if (ret < 0) { goto fail; } @@ -76,7 +76,7 @@ fail: * l1_table in memory to avoid possible image corruption. */ memset(s->l1_table + new_l1_size, 0, - (s->l1_size - new_l1_size) * sizeof(uint64_t)); + (s->l1_size - new_l1_size) * L1E_SIZE); return ret; } @@ -96,7 +96,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, /* Do a sanity check on min_size before trying to calculate new_l1_size * (this prevents overflows during the while loop for the calculation of * new_l1_size) */ - if (min_size > INT_MAX / sizeof(uint64_t)) { + if (min_size > INT_MAX / L1E_SIZE) { return -EFBIG; } @@ -114,7 +114,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, } QEMU_BUILD_BUG_ON(QCOW_MAX_L1_SIZE > INT_MAX); - if (new_l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) { + if (new_l1_size > QCOW_MAX_L1_SIZE / L1E_SIZE) { return -EFBIG; } @@ -123,7 +123,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, s->l1_size, new_l1_size); #endif - new_l1_size2 = sizeof(uint64_t) * new_l1_size; + new_l1_size2 = L1E_SIZE * new_l1_size; new_l1_table = qemu_try_blockalign(bs->file->bs, new_l1_size2); if (new_l1_table == NULL) { return -ENOMEM; @@ -131,7 +131,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, memset(new_l1_table, 0, new_l1_size2); if (s->l1_size) { - memcpy(new_l1_table, s->l1_table, s->l1_size * sizeof(uint64_t)); + memcpy(new_l1_table, s->l1_table, s->l1_size * L1E_SIZE); } /* write new table (align to cluster) */ @@ -180,7 +180,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, s->l1_table = new_l1_table; old_l1_size = s->l1_size; s->l1_size = new_l1_size; - qcow2_free_clusters(bs, old_l1_table_offset, old_l1_size * sizeof(uint64_t), + qcow2_free_clusters(bs, old_l1_table_offset, old_l1_size * L1E_SIZE, QCOW2_DISCARD_OTHER); return 0; fail: @@ -208,7 +208,7 @@ static int l2_load(BlockDriverState *bs, uint64_t offset, uint64_t l2_offset, uint64_t **l2_slice) { BDRVQcow2State *s = bs->opaque; - int start_of_slice = sizeof(uint64_t) * + int start_of_slice = l2_entry_size(s) * (offset_to_l2_index(s, offset) - offset_to_l2_slice_index(s, offset)); return qcow2_cache_get(bs, s->l2_table_cache, l2_offset + start_of_slice, @@ -225,9 +225,9 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index) BDRVQcow2State *s = bs->opaque; int l1_start_index; int i, ret; - int bufsize = MAX(sizeof(uint64_t), + int bufsize = MAX(L1E_SIZE, MIN(bs->file->bs->bl.request_alignment, s->cluster_size)); - int nentries = bufsize / sizeof(uint64_t); + int nentries = bufsize / L1E_SIZE; g_autofree uint64_t *buf = g_try_new0(uint64_t, nentries); if (buf == NULL) { @@ -240,14 +240,14 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index) } ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1, - s->l1_table_offset + 8 * l1_start_index, bufsize, false); + s->l1_table_offset + L1E_SIZE * l1_start_index, bufsize, false); if (ret < 0) { return ret; } BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); ret = bdrv_pwrite_sync(bs->file, - s->l1_table_offset + 8 * l1_start_index, + s->l1_table_offset + L1E_SIZE * l1_start_index, buf, bufsize); if (ret < 0) { return ret; @@ -281,7 +281,7 @@ static int l2_allocate(BlockDriverState *bs, int l1_index) /* allocate a new l2 entry */ - l2_offset = qcow2_alloc_clusters(bs, s->l2_size * sizeof(uint64_t)); + l2_offset = qcow2_alloc_clusters(bs, s->l2_size * l2_entry_size(s)); if (l2_offset < 0) { ret = l2_offset; goto fail; @@ -305,7 +305,7 @@ static int l2_allocate(BlockDriverState *bs, int l1_index) /* allocate a new entry in the l2 cache */ - slice_size2 = s->l2_slice_size * sizeof(uint64_t); + slice_size2 = s->l2_slice_size * l2_entry_size(s); n_slices = s->cluster_size / slice_size2; trace_qcow2_l2_allocate_get_empty(bs, l1_index); @@ -369,70 +369,123 @@ fail: } s->l1_table[l1_index] = old_l2_offset; if (l2_offset > 0) { - qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t), + qcow2_free_clusters(bs, l2_offset, s->l2_size * l2_entry_size(s), QCOW2_DISCARD_ALWAYS); } return ret; } /* - * Checks how many clusters in a given L2 slice are contiguous in the image - * file. As soon as one of the flags in the bitmask stop_flags changes compared - * to the first cluster, the search is stopped and the cluster is not counted - * as contiguous. (This allows it, for example, to stop at the first compressed - * cluster which may require a different handling) + * For a given L2 entry, count the number of contiguous subclusters of + * the same type starting from @sc_from. Compressed clusters are + * treated as if they were divided into subclusters of size + * s->subcluster_size. + * + * Return the number of contiguous subclusters and set @type to the + * subcluster type. + * + * If the L2 entry is invalid return -errno and set @type to + * QCOW2_SUBCLUSTER_INVALID. */ -static int count_contiguous_clusters(BlockDriverState *bs, int nb_clusters, - int cluster_size, uint64_t *l2_slice, uint64_t stop_flags) +static int qcow2_get_subcluster_range_type(BlockDriverState *bs, + uint64_t l2_entry, + uint64_t l2_bitmap, + unsigned sc_from, + QCow2SubclusterType *type) { - int i; - QCow2ClusterType first_cluster_type; - uint64_t mask = stop_flags | L2E_OFFSET_MASK | QCOW_OFLAG_COMPRESSED; - uint64_t first_entry = be64_to_cpu(l2_slice[0]); - uint64_t offset = first_entry & mask; + BDRVQcow2State *s = bs->opaque; + uint32_t val; - first_cluster_type = qcow2_get_cluster_type(bs, first_entry); - if (first_cluster_type == QCOW2_CLUSTER_UNALLOCATED) { - return 0; + *type = qcow2_get_subcluster_type(bs, l2_entry, l2_bitmap, sc_from); + + if (*type == QCOW2_SUBCLUSTER_INVALID) { + return -EINVAL; + } else if (!has_subclusters(s) || *type == QCOW2_SUBCLUSTER_COMPRESSED) { + return s->subclusters_per_cluster - sc_from; } - /* must be allocated */ - assert(first_cluster_type == QCOW2_CLUSTER_NORMAL || - first_cluster_type == QCOW2_CLUSTER_ZERO_ALLOC); + switch (*type) { + case QCOW2_SUBCLUSTER_NORMAL: + val = l2_bitmap | QCOW_OFLAG_SUB_ALLOC_RANGE(0, sc_from); + return cto32(val) - sc_from; - for (i = 0; i < nb_clusters; i++) { - uint64_t l2_entry = be64_to_cpu(l2_slice[i]) & mask; - if (offset + (uint64_t) i * cluster_size != l2_entry) { - break; - } + case QCOW2_SUBCLUSTER_ZERO_PLAIN: + case QCOW2_SUBCLUSTER_ZERO_ALLOC: + val = (l2_bitmap | QCOW_OFLAG_SUB_ZERO_RANGE(0, sc_from)) >> 32; + return cto32(val) - sc_from; + + case QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN: + case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: + val = ((l2_bitmap >> 32) | l2_bitmap) + & ~QCOW_OFLAG_SUB_ALLOC_RANGE(0, sc_from); + return ctz32(val) - sc_from; + + default: + g_assert_not_reached(); } - - return i; } /* - * Checks how many consecutive unallocated clusters in a given L2 - * slice have the same cluster type. + * Return the number of contiguous subclusters of the exact same type + * in a given L2 slice, starting from cluster @l2_index, subcluster + * @sc_index. Allocated subclusters are required to be contiguous in + * the image file. + * At most @nb_clusters are checked (note that this means clusters, + * not subclusters). + * Compressed clusters are always processed one by one but for the + * purpose of this count they are treated as if they were divided into + * subclusters of size s->subcluster_size. + * On failure return -errno and update @l2_index to point to the + * invalid entry. */ -static int count_contiguous_clusters_unallocated(BlockDriverState *bs, - int nb_clusters, - uint64_t *l2_slice, - QCow2ClusterType wanted_type) +static int count_contiguous_subclusters(BlockDriverState *bs, int nb_clusters, + unsigned sc_index, uint64_t *l2_slice, + unsigned *l2_index) { - int i; + BDRVQcow2State *s = bs->opaque; + int i, count = 0; + bool check_offset = false; + uint64_t expected_offset = 0; + QCow2SubclusterType expected_type = QCOW2_SUBCLUSTER_NORMAL, type; + + assert(*l2_index + nb_clusters <= s->l2_slice_size); - assert(wanted_type == QCOW2_CLUSTER_ZERO_PLAIN || - wanted_type == QCOW2_CLUSTER_UNALLOCATED); for (i = 0; i < nb_clusters; i++) { - uint64_t entry = be64_to_cpu(l2_slice[i]); - QCow2ClusterType type = qcow2_get_cluster_type(bs, entry); - - if (type != wanted_type) { + unsigned first_sc = (i == 0) ? sc_index : 0; + uint64_t l2_entry = get_l2_entry(s, l2_slice, *l2_index + i); + uint64_t l2_bitmap = get_l2_bitmap(s, l2_slice, *l2_index + i); + int ret = qcow2_get_subcluster_range_type(bs, l2_entry, l2_bitmap, + first_sc, &type); + if (ret < 0) { + *l2_index += i; /* Point to the invalid entry */ + return -EIO; + } + if (i == 0) { + if (type == QCOW2_SUBCLUSTER_COMPRESSED) { + /* Compressed clusters are always processed one by one */ + return ret; + } + expected_type = type; + expected_offset = l2_entry & L2E_OFFSET_MASK; + check_offset = (type == QCOW2_SUBCLUSTER_NORMAL || + type == QCOW2_SUBCLUSTER_ZERO_ALLOC || + type == QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC); + } else if (type != expected_type) { + break; + } else if (check_offset) { + expected_offset += s->cluster_size; + if (expected_offset != (l2_entry & L2E_OFFSET_MASK)) { + break; + } + } + count += ret; + /* Stop if there are type changes before the end of the cluster */ + if (first_sc + ret < s->subclusters_per_cluster) { break; } } - return i; + return count; } static int coroutine_fn do_perform_cow_read(BlockDriverState *bs, @@ -496,31 +549,37 @@ static int coroutine_fn do_perform_cow_write(BlockDriverState *bs, /* - * get_cluster_offset + * get_host_offset * - * For a given offset of the virtual disk, find the cluster type and offset in - * the qcow2 file. The offset is stored in *cluster_offset. + * For a given offset of the virtual disk find the equivalent host + * offset in the qcow2 file and store it in *host_offset. Neither + * offset needs to be aligned to a cluster boundary. + * + * If the cluster is unallocated then *host_offset will be 0. + * If the cluster is compressed then *host_offset will contain the + * complete compressed cluster descriptor. * * On entry, *bytes is the maximum number of contiguous bytes starting at * offset that we are interested in. * * On exit, *bytes is the number of bytes starting at offset that have the same - * cluster type and (if applicable) are stored contiguously in the image file. - * Compressed clusters are always returned one by one. + * subcluster type and (if applicable) are stored contiguously in the image + * file. The subcluster type is stored in *subcluster_type. + * Compressed clusters are always processed one by one. * - * Returns the cluster type (QCOW2_CLUSTER_*) on success, -errno in error - * cases. + * Returns 0 on success, -errno in error cases. */ -int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, - unsigned int *bytes, uint64_t *cluster_offset) +int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset, + unsigned int *bytes, uint64_t *host_offset, + QCow2SubclusterType *subcluster_type) { BDRVQcow2State *s = bs->opaque; - unsigned int l2_index; - uint64_t l1_index, l2_offset, *l2_slice; - int c; + unsigned int l2_index, sc_index; + uint64_t l1_index, l2_offset, *l2_slice, l2_entry, l2_bitmap; + int sc; unsigned int offset_in_cluster; uint64_t bytes_available, bytes_needed, nb_clusters; - QCow2ClusterType type; + QCow2SubclusterType type; int ret; offset_in_cluster = offset_into_cluster(s, offset); @@ -537,19 +596,19 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, bytes_needed = bytes_available; } - *cluster_offset = 0; + *host_offset = 0; /* seek to the l2 offset in the l1 table */ l1_index = offset_to_l1_index(s, offset); if (l1_index >= s->l1_size) { - type = QCOW2_CLUSTER_UNALLOCATED; + type = QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN; goto out; } l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK; if (!l2_offset) { - type = QCOW2_CLUSTER_UNALLOCATED; + type = QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN; goto out; } @@ -570,7 +629,9 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, /* find the cluster offset for the given disk offset */ l2_index = offset_to_l2_slice_index(s, offset); - *cluster_offset = be64_to_cpu(l2_slice[l2_index]); + sc_index = offset_to_sc_index(s, offset); + l2_entry = get_l2_entry(s, l2_slice, l2_index); + l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index); nb_clusters = size_to_clusters(s, bytes_needed); /* bytes_needed <= *bytes + offset_in_cluster, both of which are unsigned @@ -578,9 +639,9 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, * true */ assert(nb_clusters <= INT_MAX); - type = qcow2_get_cluster_type(bs, *cluster_offset); - if (s->qcow_version < 3 && (type == QCOW2_CLUSTER_ZERO_PLAIN || - type == QCOW2_CLUSTER_ZERO_ALLOC)) { + type = qcow2_get_subcluster_type(bs, l2_entry, l2_bitmap, sc_index); + if (s->qcow_version < 3 && (type == QCOW2_SUBCLUSTER_ZERO_PLAIN || + type == QCOW2_SUBCLUSTER_ZERO_ALLOC)) { qcow2_signal_corruption(bs, true, -1, -1, "Zero cluster entry found" " in pre-v3 image (L2 offset: %#" PRIx64 ", L2 index: %#x)", l2_offset, l2_index); @@ -588,7 +649,9 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, goto fail; } switch (type) { - case QCOW2_CLUSTER_COMPRESSED: + case QCOW2_SUBCLUSTER_INVALID: + break; /* This is handled by count_contiguous_subclusters() below */ + case QCOW2_SUBCLUSTER_COMPRESSED: if (has_data_file(bs)) { qcow2_signal_corruption(bs, true, -1, -1, "Compressed cluster " "entry found in image with external data " @@ -597,51 +660,53 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, ret = -EIO; goto fail; } - /* Compressed clusters can only be processed one by one */ - c = 1; - *cluster_offset &= L2E_COMPRESSED_OFFSET_SIZE_MASK; + *host_offset = l2_entry & L2E_COMPRESSED_OFFSET_SIZE_MASK; break; - case QCOW2_CLUSTER_ZERO_PLAIN: - case QCOW2_CLUSTER_UNALLOCATED: - /* how many empty clusters ? */ - c = count_contiguous_clusters_unallocated(bs, nb_clusters, - &l2_slice[l2_index], type); - *cluster_offset = 0; + case QCOW2_SUBCLUSTER_ZERO_PLAIN: + case QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN: break; - case QCOW2_CLUSTER_ZERO_ALLOC: - case QCOW2_CLUSTER_NORMAL: - /* how many allocated clusters ? */ - c = count_contiguous_clusters(bs, nb_clusters, s->cluster_size, - &l2_slice[l2_index], QCOW_OFLAG_ZERO); - *cluster_offset &= L2E_OFFSET_MASK; - if (offset_into_cluster(s, *cluster_offset)) { + case QCOW2_SUBCLUSTER_ZERO_ALLOC: + case QCOW2_SUBCLUSTER_NORMAL: + case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: { + uint64_t host_cluster_offset = l2_entry & L2E_OFFSET_MASK; + *host_offset = host_cluster_offset + offset_in_cluster; + if (offset_into_cluster(s, host_cluster_offset)) { qcow2_signal_corruption(bs, true, -1, -1, "Cluster allocation offset %#" PRIx64 " unaligned (L2 offset: %#" PRIx64 - ", L2 index: %#x)", *cluster_offset, + ", L2 index: %#x)", host_cluster_offset, l2_offset, l2_index); ret = -EIO; goto fail; } - if (has_data_file(bs) && *cluster_offset != offset - offset_in_cluster) - { + if (has_data_file(bs) && *host_offset != offset) { qcow2_signal_corruption(bs, true, -1, -1, "External data file host cluster offset %#" PRIx64 " does not match guest cluster " "offset: %#" PRIx64 - ", L2 index: %#x)", *cluster_offset, + ", L2 index: %#x)", host_cluster_offset, offset - offset_in_cluster, l2_index); ret = -EIO; goto fail; } break; + } default: abort(); } + sc = count_contiguous_subclusters(bs, nb_clusters, sc_index, + l2_slice, &l2_index); + if (sc < 0) { + qcow2_signal_corruption(bs, true, -1, -1, "Invalid cluster entry found " + " (L2 offset: %#" PRIx64 ", L2 index: %#x)", + l2_offset, l2_index); + ret = -EIO; + goto fail; + } qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); - bytes_available = (int64_t)c * s->cluster_size; + bytes_available = ((int64_t)sc + sc_index) << s->subcluster_bits; out: if (bytes_available > bytes_needed) { @@ -654,7 +719,9 @@ out: assert(bytes_available - offset_in_cluster <= UINT_MAX); *bytes = bytes_available - offset_in_cluster; - return type; + *subcluster_type = type; + + return 0; fail: qcow2_cache_put(s->l2_table_cache, (void **)&l2_slice); @@ -709,7 +776,7 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, /* Then decrease the refcount of the old table */ if (l2_offset) { - qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t), + qcow2_free_clusters(bs, l2_offset, s->l2_size * l2_entry_size(s), QCOW2_DISCARD_OTHER); } @@ -765,7 +832,7 @@ int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, /* Compression can't overwrite anything. Fail if the cluster was already * allocated. */ - cluster_offset = be64_to_cpu(l2_slice[l2_index]); + cluster_offset = get_l2_entry(s, l2_slice, l2_index); if (cluster_offset & L2E_OFFSET_MASK) { qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); return -EIO; @@ -794,7 +861,10 @@ int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED); qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); - l2_slice[l2_index] = cpu_to_be64(cluster_offset); + set_l2_entry(s, l2_slice, l2_index, cluster_offset); + if (has_subclusters(s)) { + set_l2_bitmap(s, l2_slice, l2_index, 0); + } qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); *host_offset = cluster_offset & s->cluster_offset_mask; @@ -979,6 +1049,8 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); assert(l2_index + m->nb_clusters <= s->l2_slice_size); + assert(m->cow_end.offset + m->cow_end.nb_bytes <= + m->nb_clusters << s->cluster_bits); for (i = 0; i < m->nb_clusters; i++) { uint64_t offset = cluster_offset + ((uint64_t)i << s->cluster_bits); /* if two concurrent writes happen to the same unallocated cluster @@ -987,14 +1059,31 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) * cluster the second one has to do RMW (which is done above by * perform_cow()), update l2 table with its cluster pointer and free * old cluster. This is what this loop does */ - if (l2_slice[l2_index + i] != 0) { - old_cluster[j++] = l2_slice[l2_index + i]; + if (get_l2_entry(s, l2_slice, l2_index + i) != 0) { + old_cluster[j++] = get_l2_entry(s, l2_slice, l2_index + i); } /* The offset must fit in the offset field of the L2 table entry */ assert((offset & L2E_OFFSET_MASK) == offset); - l2_slice[l2_index + i] = cpu_to_be64(offset | QCOW_OFLAG_COPIED); + set_l2_entry(s, l2_slice, l2_index + i, offset | QCOW_OFLAG_COPIED); + + /* Update bitmap with the subclusters that were just written */ + if (has_subclusters(s) && !m->prealloc) { + uint64_t l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index + i); + unsigned written_from = m->cow_start.offset; + unsigned written_to = m->cow_end.offset + m->cow_end.nb_bytes; + int first_sc, last_sc; + /* Narrow written_from and written_to down to the current cluster */ + written_from = MAX(written_from, i << s->cluster_bits); + written_to = MIN(written_to, (i + 1) << s->cluster_bits); + assert(written_from < written_to); + first_sc = offset_to_sc_index(s, written_from); + last_sc = offset_to_sc_index(s, written_to - 1); + l2_bitmap |= QCOW_OFLAG_SUB_ALLOC_RANGE(first_sc, last_sc + 1); + l2_bitmap &= ~QCOW_OFLAG_SUB_ZERO_RANGE(first_sc, last_sc + 1); + set_l2_bitmap(s, l2_slice, l2_index + i, l2_bitmap); + } } @@ -1008,8 +1097,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) */ if (!m->keep_old_clusters && j != 0) { for (i = 0; i < j; i++) { - qcow2_free_any_clusters(bs, be64_to_cpu(old_cluster[i]), 1, - QCOW2_DISCARD_NEVER); + qcow2_free_any_cluster(bs, old_cluster[i], QCOW2_DISCARD_NEVER); } } @@ -1034,36 +1122,250 @@ void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m) } /* - * Returns the number of contiguous clusters that can be used for an allocating - * write, but require COW to be performed (this includes yet unallocated space, - * which must copy from the backing file) + * For a given write request, create a new QCowL2Meta structure, add + * it to @m and the BDRVQcow2State.cluster_allocs list. If the write + * request does not need copy-on-write or changes to the L2 metadata + * then this function does nothing. + * + * @host_cluster_offset points to the beginning of the first cluster. + * + * @guest_offset and @bytes indicate the offset and length of the + * request. + * + * @l2_slice contains the L2 entries of all clusters involved in this + * write request. + * + * If @keep_old is true it means that the clusters were already + * allocated and will be overwritten. If false then the clusters are + * new and we have to decrease the reference count of the old ones. + * + * Returns 0 on success, -errno on failure. */ -static int count_cow_clusters(BlockDriverState *bs, int nb_clusters, - uint64_t *l2_slice, int l2_index) +static int calculate_l2_meta(BlockDriverState *bs, uint64_t host_cluster_offset, + uint64_t guest_offset, unsigned bytes, + uint64_t *l2_slice, QCowL2Meta **m, bool keep_old) { + BDRVQcow2State *s = bs->opaque; + int sc_index, l2_index = offset_to_l2_slice_index(s, guest_offset); + uint64_t l2_entry, l2_bitmap; + unsigned cow_start_from, cow_end_to; + unsigned cow_start_to = offset_into_cluster(s, guest_offset); + unsigned cow_end_from = cow_start_to + bytes; + unsigned nb_clusters = size_to_clusters(s, cow_end_from); + QCowL2Meta *old_m = *m; + QCow2SubclusterType type; int i; + bool skip_cow = keep_old; + assert(nb_clusters <= s->l2_slice_size - l2_index); + + /* Check the type of all affected subclusters */ for (i = 0; i < nb_clusters; i++) { - uint64_t l2_entry = be64_to_cpu(l2_slice[l2_index + i]); - QCow2ClusterType cluster_type = qcow2_get_cluster_type(bs, l2_entry); - - switch(cluster_type) { - case QCOW2_CLUSTER_NORMAL: - if (l2_entry & QCOW_OFLAG_COPIED) { - goto out; + l2_entry = get_l2_entry(s, l2_slice, l2_index + i); + l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index + i); + if (skip_cow) { + unsigned write_from = MAX(cow_start_to, i << s->cluster_bits); + unsigned write_to = MIN(cow_end_from, (i + 1) << s->cluster_bits); + int first_sc = offset_to_sc_index(s, write_from); + int last_sc = offset_to_sc_index(s, write_to - 1); + int cnt = qcow2_get_subcluster_range_type(bs, l2_entry, l2_bitmap, + first_sc, &type); + /* Is any of the subclusters of type != QCOW2_SUBCLUSTER_NORMAL ? */ + if (type != QCOW2_SUBCLUSTER_NORMAL || first_sc + cnt <= last_sc) { + skip_cow = false; } + } else { + /* If we can't skip the cow we can still look for invalid entries */ + type = qcow2_get_subcluster_type(bs, l2_entry, l2_bitmap, 0); + } + if (type == QCOW2_SUBCLUSTER_INVALID) { + int l1_index = offset_to_l1_index(s, guest_offset); + uint64_t l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK; + qcow2_signal_corruption(bs, true, -1, -1, "Invalid cluster " + "entry found (L2 offset: %#" PRIx64 + ", L2 index: %#x)", + l2_offset, l2_index + i); + return -EIO; + } + } + + if (skip_cow) { + return 0; + } + + /* Get the L2 entry of the first cluster */ + l2_entry = get_l2_entry(s, l2_slice, l2_index); + l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index); + sc_index = offset_to_sc_index(s, guest_offset); + type = qcow2_get_subcluster_type(bs, l2_entry, l2_bitmap, sc_index); + + if (!keep_old) { + switch (type) { + case QCOW2_SUBCLUSTER_COMPRESSED: + cow_start_from = 0; break; - case QCOW2_CLUSTER_UNALLOCATED: - case QCOW2_CLUSTER_COMPRESSED: - case QCOW2_CLUSTER_ZERO_PLAIN: - case QCOW2_CLUSTER_ZERO_ALLOC: + case QCOW2_SUBCLUSTER_NORMAL: + case QCOW2_SUBCLUSTER_ZERO_ALLOC: + case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: + if (has_subclusters(s)) { + /* Skip all leading zero and unallocated subclusters */ + uint32_t alloc_bitmap = l2_bitmap & QCOW_L2_BITMAP_ALL_ALLOC; + cow_start_from = + MIN(sc_index, ctz32(alloc_bitmap)) << s->subcluster_bits; + } else { + cow_start_from = 0; + } break; + case QCOW2_SUBCLUSTER_ZERO_PLAIN: + case QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN: + cow_start_from = sc_index << s->subcluster_bits; + break; + default: + g_assert_not_reached(); + } + } else { + switch (type) { + case QCOW2_SUBCLUSTER_NORMAL: + cow_start_from = cow_start_to; + break; + case QCOW2_SUBCLUSTER_ZERO_ALLOC: + case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: + cow_start_from = sc_index << s->subcluster_bits; + break; + default: + g_assert_not_reached(); + } + } + + /* Get the L2 entry of the last cluster */ + l2_index += nb_clusters - 1; + l2_entry = get_l2_entry(s, l2_slice, l2_index); + l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index); + sc_index = offset_to_sc_index(s, guest_offset + bytes - 1); + type = qcow2_get_subcluster_type(bs, l2_entry, l2_bitmap, sc_index); + + if (!keep_old) { + switch (type) { + case QCOW2_SUBCLUSTER_COMPRESSED: + cow_end_to = ROUND_UP(cow_end_from, s->cluster_size); + break; + case QCOW2_SUBCLUSTER_NORMAL: + case QCOW2_SUBCLUSTER_ZERO_ALLOC: + case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: + cow_end_to = ROUND_UP(cow_end_from, s->cluster_size); + if (has_subclusters(s)) { + /* Skip all trailing zero and unallocated subclusters */ + uint32_t alloc_bitmap = l2_bitmap & QCOW_L2_BITMAP_ALL_ALLOC; + cow_end_to -= + MIN(s->subclusters_per_cluster - sc_index - 1, + clz32(alloc_bitmap)) << s->subcluster_bits; + } + break; + case QCOW2_SUBCLUSTER_ZERO_PLAIN: + case QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN: + cow_end_to = ROUND_UP(cow_end_from, s->subcluster_size); + break; + default: + g_assert_not_reached(); + } + } else { + switch (type) { + case QCOW2_SUBCLUSTER_NORMAL: + cow_end_to = cow_end_from; + break; + case QCOW2_SUBCLUSTER_ZERO_ALLOC: + case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: + cow_end_to = ROUND_UP(cow_end_from, s->subcluster_size); + break; default: - abort(); + g_assert_not_reached(); + } + } + + *m = g_malloc0(sizeof(**m)); + **m = (QCowL2Meta) { + .next = old_m, + + .alloc_offset = host_cluster_offset, + .offset = start_of_cluster(s, guest_offset), + .nb_clusters = nb_clusters, + + .keep_old_clusters = keep_old, + + .cow_start = { + .offset = cow_start_from, + .nb_bytes = cow_start_to - cow_start_from, + }, + .cow_end = { + .offset = cow_end_from, + .nb_bytes = cow_end_to - cow_end_from, + }, + }; + + qemu_co_queue_init(&(*m)->dependent_requests); + QLIST_INSERT_HEAD(&s->cluster_allocs, *m, next_in_flight); + + return 0; +} + +/* + * Returns true if writing to the cluster pointed to by @l2_entry + * requires a new allocation (that is, if the cluster is unallocated + * or has refcount > 1 and therefore cannot be written in-place). + */ +static bool cluster_needs_new_alloc(BlockDriverState *bs, uint64_t l2_entry) +{ + switch (qcow2_get_cluster_type(bs, l2_entry)) { + case QCOW2_CLUSTER_NORMAL: + case QCOW2_CLUSTER_ZERO_ALLOC: + if (l2_entry & QCOW_OFLAG_COPIED) { + return false; + } + /* fallthrough */ + case QCOW2_CLUSTER_UNALLOCATED: + case QCOW2_CLUSTER_COMPRESSED: + case QCOW2_CLUSTER_ZERO_PLAIN: + return true; + default: + abort(); + } +} + +/* + * Returns the number of contiguous clusters that can be written to + * using one single write request, starting from @l2_index. + * At most @nb_clusters are checked. + * + * If @new_alloc is true this counts clusters that are either + * unallocated, or allocated but with refcount > 1 (so they need to be + * newly allocated and COWed). + * + * If @new_alloc is false this counts clusters that are already + * allocated and can be overwritten in-place (this includes clusters + * of type QCOW2_CLUSTER_ZERO_ALLOC). + */ +static int count_single_write_clusters(BlockDriverState *bs, int nb_clusters, + uint64_t *l2_slice, int l2_index, + bool new_alloc) +{ + BDRVQcow2State *s = bs->opaque; + uint64_t l2_entry = get_l2_entry(s, l2_slice, l2_index); + uint64_t expected_offset = l2_entry & L2E_OFFSET_MASK; + int i; + + for (i = 0; i < nb_clusters; i++) { + l2_entry = get_l2_entry(s, l2_slice, l2_index + i); + if (cluster_needs_new_alloc(bs, l2_entry) != new_alloc) { + break; + } + if (!new_alloc) { + if (expected_offset != (l2_entry & L2E_OFFSET_MASK)) { + break; + } + expected_offset += s->cluster_size; } } -out: assert(i <= nb_clusters); return i; } @@ -1093,8 +1395,8 @@ static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset, uint64_t start = guest_offset; uint64_t end = start + bytes; - uint64_t old_start = l2meta_cow_start(old_alloc); - uint64_t old_end = l2meta_cow_end(old_alloc); + uint64_t old_start = start_of_cluster(s, l2meta_cow_start(old_alloc)); + uint64_t old_end = ROUND_UP(l2meta_cow_end(old_alloc), s->cluster_size); if (end <= old_start || start >= old_end) { /* No intersection */ @@ -1132,10 +1434,10 @@ static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset, } /* - * Checks how many already allocated clusters that don't require a copy on - * write there are at the given guest_offset (up to *bytes). If *host_offset is - * not INV_OFFSET, only physically contiguous clusters beginning at this host - * offset are counted. + * Checks how many already allocated clusters that don't require a new + * allocation there are at the given guest_offset (up to *bytes). + * If *host_offset is not INV_OFFSET, only physically contiguous clusters + * beginning at this host offset are counted. * * Note that guest_offset may not be cluster aligned. In this case, the * returned *host_offset points to exact byte referenced by guest_offset and @@ -1144,12 +1446,12 @@ static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset, * Returns: * 0: if no allocated clusters are available at the given offset. * *bytes is normally unchanged. It is set to 0 if the cluster - * is allocated and doesn't need COW, but doesn't have the right - * physical offset. + * is allocated and can be overwritten in-place but doesn't have + * the right physical offset. * - * 1: if allocated clusters that don't require a COW are available at - * the requested offset. *bytes may have decreased and describes - * the length of the area that can be written to. + * 1: if allocated clusters that can be overwritten in place are + * available at the requested offset. *bytes may have decreased + * and describes the length of the area that can be written to. * * -errno: in error cases */ @@ -1158,7 +1460,7 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, { BDRVQcow2State *s = bs->opaque; int l2_index; - uint64_t cluster_offset; + uint64_t l2_entry, cluster_offset; uint64_t *l2_slice; uint64_t nb_clusters; unsigned int keep_clusters; @@ -1179,7 +1481,8 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, l2_index = offset_to_l2_slice_index(s, guest_offset); nb_clusters = MIN(nb_clusters, s->l2_slice_size - l2_index); - assert(nb_clusters <= INT_MAX); + /* Limit total byte count to BDRV_REQUEST_MAX_BYTES */ + nb_clusters = MIN(nb_clusters, BDRV_REQUEST_MAX_BYTES >> s->cluster_bits); /* Find L2 entry for the first involved cluster */ ret = get_cluster_table(bs, guest_offset, &l2_slice, &l2_index); @@ -1187,41 +1490,42 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, return ret; } - cluster_offset = be64_to_cpu(l2_slice[l2_index]); + l2_entry = get_l2_entry(s, l2_slice, l2_index); + cluster_offset = l2_entry & L2E_OFFSET_MASK; - /* Check how many clusters are already allocated and don't need COW */ - if (qcow2_get_cluster_type(bs, cluster_offset) == QCOW2_CLUSTER_NORMAL - && (cluster_offset & QCOW_OFLAG_COPIED)) - { - /* If a specific host_offset is required, check it */ - bool offset_matches = - (cluster_offset & L2E_OFFSET_MASK) == *host_offset; - - if (offset_into_cluster(s, cluster_offset & L2E_OFFSET_MASK)) { - qcow2_signal_corruption(bs, true, -1, -1, "Data cluster offset " - "%#llx unaligned (guest offset: %#" PRIx64 - ")", cluster_offset & L2E_OFFSET_MASK, - guest_offset); + if (!cluster_needs_new_alloc(bs, l2_entry)) { + if (offset_into_cluster(s, cluster_offset)) { + qcow2_signal_corruption(bs, true, -1, -1, "%s cluster offset " + "%#" PRIx64 " unaligned (guest offset: %#" + PRIx64 ")", l2_entry & QCOW_OFLAG_ZERO ? + "Preallocated zero" : "Data", + cluster_offset, guest_offset); ret = -EIO; goto out; } - if (*host_offset != INV_OFFSET && !offset_matches) { + /* If a specific host_offset is required, check it */ + if (*host_offset != INV_OFFSET && cluster_offset != *host_offset) { *bytes = 0; ret = 0; goto out; } /* We keep all QCOW_OFLAG_COPIED clusters */ - keep_clusters = - count_contiguous_clusters(bs, nb_clusters, s->cluster_size, - &l2_slice[l2_index], - QCOW_OFLAG_COPIED | QCOW_OFLAG_ZERO); + keep_clusters = count_single_write_clusters(bs, nb_clusters, l2_slice, + l2_index, false); assert(keep_clusters <= nb_clusters); *bytes = MIN(*bytes, keep_clusters * s->cluster_size - offset_into_cluster(s, guest_offset)); + assert(*bytes != 0); + + ret = calculate_l2_meta(bs, cluster_offset, guest_offset, + *bytes, l2_slice, m, true); + if (ret < 0) { + goto out; + } ret = 1; } else { @@ -1235,8 +1539,7 @@ out: /* Only return a host offset if we actually made progress. Otherwise we * would make requirements for handle_alloc() that it can't fulfill */ if (ret > 0) { - *host_offset = (cluster_offset & L2E_OFFSET_MASK) - + offset_into_cluster(s, guest_offset); + *host_offset = cluster_offset + offset_into_cluster(s, guest_offset); } return ret; @@ -1297,9 +1600,10 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset, } /* - * Allocates new clusters for an area that either is yet unallocated or needs a - * copy on write. If *host_offset is not INV_OFFSET, clusters are only - * allocated if the new allocation can match the specified host offset. + * Allocates new clusters for an area that is either still unallocated or + * cannot be overwritten in-place. If *host_offset is not INV_OFFSET, + * clusters are only allocated if the new allocation can match the specified + * host offset. * * Note that guest_offset may not be cluster aligned. In this case, the * returned *host_offset points to exact byte referenced by guest_offset and @@ -1322,12 +1626,10 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, BDRVQcow2State *s = bs->opaque; int l2_index; uint64_t *l2_slice; - uint64_t entry; uint64_t nb_clusters; int ret; - bool keep_old_clusters = false; - uint64_t alloc_cluster_offset = INV_OFFSET; + uint64_t alloc_cluster_offset; trace_qcow2_handle_alloc(qemu_coroutine_self(), guest_offset, *host_offset, *bytes); @@ -1342,10 +1644,8 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, l2_index = offset_to_l2_slice_index(s, guest_offset); nb_clusters = MIN(nb_clusters, s->l2_slice_size - l2_index); - assert(nb_clusters <= INT_MAX); - - /* Limit total allocation byte count to INT_MAX */ - nb_clusters = MIN(nb_clusters, INT_MAX >> s->cluster_bits); + /* Limit total allocation byte count to BDRV_REQUEST_MAX_BYTES */ + nb_clusters = MIN(nb_clusters, BDRV_REQUEST_MAX_BYTES >> s->cluster_bits); /* Find L2 entry for the first involved cluster */ ret = get_cluster_table(bs, guest_offset, &l2_slice, &l2_index); @@ -1353,67 +1653,32 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, return ret; } - entry = be64_to_cpu(l2_slice[l2_index]); - nb_clusters = count_cow_clusters(bs, nb_clusters, l2_slice, l2_index); + nb_clusters = count_single_write_clusters(bs, nb_clusters, + l2_slice, l2_index, true); /* This function is only called when there were no non-COW clusters, so if * we can't find any unallocated or COW clusters either, something is * wrong with our code. */ assert(nb_clusters > 0); - if (qcow2_get_cluster_type(bs, entry) == QCOW2_CLUSTER_ZERO_ALLOC && - (entry & QCOW_OFLAG_COPIED) && - (*host_offset == INV_OFFSET || - start_of_cluster(s, *host_offset) == (entry & L2E_OFFSET_MASK))) - { - int preallocated_nb_clusters; - - if (offset_into_cluster(s, entry & L2E_OFFSET_MASK)) { - qcow2_signal_corruption(bs, true, -1, -1, "Preallocated zero " - "cluster offset %#llx unaligned (guest " - "offset: %#" PRIx64 ")", - entry & L2E_OFFSET_MASK, guest_offset); - ret = -EIO; - goto fail; - } - - /* Try to reuse preallocated zero clusters; contiguous normal clusters - * would be fine, too, but count_cow_clusters() above has limited - * nb_clusters already to a range of COW clusters */ - preallocated_nb_clusters = - count_contiguous_clusters(bs, nb_clusters, s->cluster_size, - &l2_slice[l2_index], QCOW_OFLAG_COPIED); - assert(preallocated_nb_clusters > 0); - - nb_clusters = preallocated_nb_clusters; - alloc_cluster_offset = entry & L2E_OFFSET_MASK; - - /* We want to reuse these clusters, so qcow2_alloc_cluster_link_l2() - * should not free them. */ - keep_old_clusters = true; + /* Allocate at a given offset in the image file */ + alloc_cluster_offset = *host_offset == INV_OFFSET ? INV_OFFSET : + start_of_cluster(s, *host_offset); + ret = do_alloc_cluster_offset(bs, guest_offset, &alloc_cluster_offset, + &nb_clusters); + if (ret < 0) { + goto out; } - qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); - - if (alloc_cluster_offset == INV_OFFSET) { - /* Allocate, if necessary at a given offset in the image file */ - alloc_cluster_offset = *host_offset == INV_OFFSET ? INV_OFFSET : - start_of_cluster(s, *host_offset); - ret = do_alloc_cluster_offset(bs, guest_offset, &alloc_cluster_offset, - &nb_clusters); - if (ret < 0) { - goto fail; - } - - /* Can't extend contiguous allocation */ - if (nb_clusters == 0) { - *bytes = 0; - return 0; - } - - assert(alloc_cluster_offset != INV_OFFSET); + /* Can't extend contiguous allocation */ + if (nb_clusters == 0) { + *bytes = 0; + ret = 0; + goto out; } + assert(alloc_cluster_offset != INV_OFFSET); + /* * Save info needed for meta data update. * @@ -1431,66 +1696,54 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, uint64_t requested_bytes = *bytes + offset_into_cluster(s, guest_offset); int avail_bytes = nb_clusters << s->cluster_bits; int nb_bytes = MIN(requested_bytes, avail_bytes); - QCowL2Meta *old_m = *m; - - *m = g_malloc0(sizeof(**m)); - - **m = (QCowL2Meta) { - .next = old_m, - - .alloc_offset = alloc_cluster_offset, - .offset = start_of_cluster(s, guest_offset), - .nb_clusters = nb_clusters, - - .keep_old_clusters = keep_old_clusters, - - .cow_start = { - .offset = 0, - .nb_bytes = offset_into_cluster(s, guest_offset), - }, - .cow_end = { - .offset = nb_bytes, - .nb_bytes = avail_bytes - nb_bytes, - }, - }; - qemu_co_queue_init(&(*m)->dependent_requests); - QLIST_INSERT_HEAD(&s->cluster_allocs, *m, next_in_flight); *host_offset = alloc_cluster_offset + offset_into_cluster(s, guest_offset); *bytes = MIN(*bytes, nb_bytes - offset_into_cluster(s, guest_offset)); assert(*bytes != 0); - return 1; - -fail: - if (*m && (*m)->nb_clusters > 0) { - QLIST_REMOVE(*m, next_in_flight); + ret = calculate_l2_meta(bs, alloc_cluster_offset, guest_offset, *bytes, + l2_slice, m, false); + if (ret < 0) { + goto out; } + + ret = 1; + +out: + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); return ret; } /* - * alloc_cluster_offset + * For a given area on the virtual disk defined by @offset and @bytes, + * find the corresponding area on the qcow2 image, allocating new + * clusters (or subclusters) if necessary. The result can span a + * combination of allocated and previously unallocated clusters. * - * For a given offset on the virtual disk, find the cluster offset in qcow2 - * file. If the offset is not found, allocate a new cluster. + * Note that offset may not be cluster aligned. In this case, the returned + * *host_offset points to exact byte referenced by offset and therefore + * isn't cluster aligned as well. * - * If the cluster was already allocated, m->nb_clusters is set to 0 and - * other fields in m are meaningless. + * On return, @host_offset is set to the beginning of the requested + * area. This area is guaranteed to be contiguous on the qcow2 file + * but it can be smaller than initially requested. In this case @bytes + * is updated with the actual size. * - * If the cluster is newly allocated, m->nb_clusters is set to the number of - * contiguous clusters that have been allocated. In this case, the other - * fields of m are valid and contain information about the first allocated - * cluster. + * If any clusters or subclusters were allocated then @m contains a + * list with the information of all the affected regions. Note that + * this can happen regardless of whether this function succeeds or + * not. The caller is responsible for updating the L2 metadata of the + * allocated clusters (on success) or freeing them (on failure), and + * for clearing the contents of @m afterwards in both cases. * * If the request conflicts with another write request in flight, the coroutine * is queued and will be reentered when the dependency has completed. * * Return 0 on success and -errno in error cases */ -int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, - unsigned int *bytes, uint64_t *host_offset, - QCowL2Meta **m) +int qcow2_alloc_host_offset(BlockDriverState *bs, uint64_t offset, + unsigned int *bytes, uint64_t *host_offset, + QCowL2Meta **m) { BDRVQcow2State *s = bs->opaque; uint64_t start, remaining; @@ -1511,7 +1764,7 @@ again: while (true) { if (*host_offset == INV_OFFSET && cluster_offset != INV_OFFSET) { - *host_offset = start_of_cluster(s, cluster_offset); + *host_offset = cluster_offset; } assert(remaining >= cur_bytes); @@ -1594,6 +1847,8 @@ again: *bytes -= remaining; assert(*bytes > 0); assert(*host_offset != INV_OFFSET); + assert(offset_into_cluster(s, *host_offset) == + offset_into_cluster(s, offset)); return 0; } @@ -1623,11 +1878,17 @@ static int discard_in_l2_slice(BlockDriverState *bs, uint64_t offset, assert(nb_clusters <= INT_MAX); for (i = 0; i < nb_clusters; i++) { - uint64_t old_l2_entry; - - old_l2_entry = be64_to_cpu(l2_slice[l2_index + i]); + uint64_t old_l2_entry = get_l2_entry(s, l2_slice, l2_index + i); + uint64_t old_l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index + i); + uint64_t new_l2_entry = old_l2_entry; + uint64_t new_l2_bitmap = old_l2_bitmap; + QCow2ClusterType cluster_type = + qcow2_get_cluster_type(bs, old_l2_entry); /* + * If full_discard is true, the cluster should not read back as zeroes, + * but rather fall through to the backing file. + * * If full_discard is false, make sure that a discarded area reads back * as zeroes for v3 images (we cannot do it for v2 without actually * writing a zero-filled buffer). We can skip the operation if the @@ -1636,42 +1897,30 @@ static int discard_in_l2_slice(BlockDriverState *bs, uint64_t offset, * * TODO We might want to use bdrv_block_status(bs) here, but we're * holding s->lock, so that doesn't work today. - * - * If full_discard is true, the sector should not read back as zeroes, - * but rather fall through to the backing file. */ - switch (qcow2_get_cluster_type(bs, old_l2_entry)) { - case QCOW2_CLUSTER_UNALLOCATED: - if (full_discard || !bs->backing) { - continue; + if (full_discard) { + new_l2_entry = new_l2_bitmap = 0; + } else if (bs->backing || qcow2_cluster_is_allocated(cluster_type)) { + if (has_subclusters(s)) { + new_l2_entry = 0; + new_l2_bitmap = QCOW_L2_BITMAP_ALL_ZEROES; + } else { + new_l2_entry = s->qcow_version >= 3 ? QCOW_OFLAG_ZERO : 0; } - break; + } - case QCOW2_CLUSTER_ZERO_PLAIN: - if (!full_discard) { - continue; - } - break; - - case QCOW2_CLUSTER_ZERO_ALLOC: - case QCOW2_CLUSTER_NORMAL: - case QCOW2_CLUSTER_COMPRESSED: - break; - - default: - abort(); + if (old_l2_entry == new_l2_entry && old_l2_bitmap == new_l2_bitmap) { + continue; } /* First remove L2 entries */ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); - if (!full_discard && s->qcow_version >= 3) { - l2_slice[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); - } else { - l2_slice[l2_index + i] = cpu_to_be64(0); + set_l2_entry(s, l2_slice, l2_index + i, new_l2_entry); + if (has_subclusters(s)) { + set_l2_bitmap(s, l2_slice, l2_index + i, new_l2_bitmap); } - /* Then decrease the refcount */ - qcow2_free_any_clusters(bs, old_l2_entry, 1, type); + qcow2_free_any_cluster(bs, old_l2_entry, type); } qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); @@ -1732,7 +1981,6 @@ static int zero_in_l2_slice(BlockDriverState *bs, uint64_t offset, int l2_index; int ret; int i; - bool unmap = !!(flags & BDRV_REQ_MAY_UNMAP); ret = get_cluster_table(bs, offset, &l2_slice, &l2_index); if (ret < 0) { @@ -1744,27 +1992,34 @@ static int zero_in_l2_slice(BlockDriverState *bs, uint64_t offset, assert(nb_clusters <= INT_MAX); for (i = 0; i < nb_clusters; i++) { - uint64_t old_offset; - QCow2ClusterType cluster_type; + uint64_t old_l2_entry = get_l2_entry(s, l2_slice, l2_index + i); + uint64_t old_l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index + i); + QCow2ClusterType type = qcow2_get_cluster_type(bs, old_l2_entry); + bool unmap = (type == QCOW2_CLUSTER_COMPRESSED) || + ((flags & BDRV_REQ_MAY_UNMAP) && qcow2_cluster_is_allocated(type)); + uint64_t new_l2_entry = unmap ? 0 : old_l2_entry; + uint64_t new_l2_bitmap = old_l2_bitmap; - old_offset = be64_to_cpu(l2_slice[l2_index + i]); + if (has_subclusters(s)) { + new_l2_bitmap = QCOW_L2_BITMAP_ALL_ZEROES; + } else { + new_l2_entry |= QCOW_OFLAG_ZERO; + } - /* - * Minimize L2 changes if the cluster already reads back as - * zeroes with correct allocation. - */ - cluster_type = qcow2_get_cluster_type(bs, old_offset); - if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN || - (cluster_type == QCOW2_CLUSTER_ZERO_ALLOC && !unmap)) { + if (old_l2_entry == new_l2_entry && old_l2_bitmap == new_l2_bitmap) { continue; } + /* First update L2 entries */ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); - if (cluster_type == QCOW2_CLUSTER_COMPRESSED || unmap) { - l2_slice[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); - qcow2_free_any_clusters(bs, old_offset, 1, QCOW2_DISCARD_REQUEST); - } else { - l2_slice[l2_index + i] |= cpu_to_be64(QCOW_OFLAG_ZERO); + set_l2_entry(s, l2_slice, l2_index + i, new_l2_entry); + if (has_subclusters(s)) { + set_l2_bitmap(s, l2_slice, l2_index + i, new_l2_bitmap); + } + + /* Then decrease the refcount */ + if (unmap) { + qcow2_free_any_cluster(bs, old_l2_entry, QCOW2_DISCARD_REQUEST); } } @@ -1773,12 +2028,59 @@ static int zero_in_l2_slice(BlockDriverState *bs, uint64_t offset, return nb_clusters; } -int qcow2_cluster_zeroize(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, int flags) +static int zero_l2_subclusters(BlockDriverState *bs, uint64_t offset, + unsigned nb_subclusters) +{ + BDRVQcow2State *s = bs->opaque; + uint64_t *l2_slice; + uint64_t old_l2_bitmap, l2_bitmap; + int l2_index, ret, sc = offset_to_sc_index(s, offset); + + /* For full clusters use zero_in_l2_slice() instead */ + assert(nb_subclusters > 0 && nb_subclusters < s->subclusters_per_cluster); + assert(sc + nb_subclusters <= s->subclusters_per_cluster); + assert(offset_into_subcluster(s, offset) == 0); + + ret = get_cluster_table(bs, offset, &l2_slice, &l2_index); + if (ret < 0) { + return ret; + } + + switch (qcow2_get_cluster_type(bs, get_l2_entry(s, l2_slice, l2_index))) { + case QCOW2_CLUSTER_COMPRESSED: + ret = -ENOTSUP; /* We cannot partially zeroize compressed clusters */ + goto out; + case QCOW2_CLUSTER_NORMAL: + case QCOW2_CLUSTER_UNALLOCATED: + break; + default: + g_assert_not_reached(); + } + + old_l2_bitmap = l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index); + + l2_bitmap |= QCOW_OFLAG_SUB_ZERO_RANGE(sc, sc + nb_subclusters); + l2_bitmap &= ~QCOW_OFLAG_SUB_ALLOC_RANGE(sc, sc + nb_subclusters); + + if (old_l2_bitmap != l2_bitmap) { + set_l2_bitmap(s, l2_slice, l2_index, l2_bitmap); + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); + } + + ret = 0; +out: + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); + + return ret; +} + +int qcow2_subcluster_zeroize(BlockDriverState *bs, uint64_t offset, + uint64_t bytes, int flags) { BDRVQcow2State *s = bs->opaque; uint64_t end_offset = offset + bytes; uint64_t nb_clusters; + unsigned head, tail; int64_t cleared; int ret; @@ -1793,8 +2095,8 @@ int qcow2_cluster_zeroize(BlockDriverState *bs, uint64_t offset, } /* Caller must pass aligned values, except at image end */ - assert(QEMU_IS_ALIGNED(offset, s->cluster_size)); - assert(QEMU_IS_ALIGNED(end_offset, s->cluster_size) || + assert(offset_into_subcluster(s, offset) == 0); + assert(offset_into_subcluster(s, end_offset) == 0 || end_offset >= bs->total_sectors << BDRV_SECTOR_BITS); /* @@ -1809,11 +2111,26 @@ int qcow2_cluster_zeroize(BlockDriverState *bs, uint64_t offset, return -ENOTSUP; } - /* Each L2 slice is handled by its own loop iteration */ - nb_clusters = size_to_clusters(s, bytes); + head = MIN(end_offset, ROUND_UP(offset, s->cluster_size)) - offset; + offset += head; + + tail = (end_offset >= bs->total_sectors << BDRV_SECTOR_BITS) ? 0 : + end_offset - MAX(offset, start_of_cluster(s, end_offset)); + end_offset -= tail; s->cache_discards = true; + if (head) { + ret = zero_l2_subclusters(bs, offset - head, + size_to_subclusters(s, head)); + if (ret < 0) { + goto fail; + } + } + + /* Each L2 slice is handled by its own loop iteration */ + nb_clusters = size_to_clusters(s, end_offset - offset); + while (nb_clusters > 0) { cleared = zero_in_l2_slice(bs, offset, nb_clusters, flags); if (cleared < 0) { @@ -1825,6 +2142,13 @@ int qcow2_cluster_zeroize(BlockDriverState *bs, uint64_t offset, offset += (cleared * s->cluster_size); } + if (tail) { + ret = zero_l2_subclusters(bs, end_offset, size_to_subclusters(s, tail)); + if (ret < 0) { + goto fail; + } + } + ret = 0; fail: s->cache_discards = false; @@ -1854,7 +2178,10 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, int ret; int i, j; - slice_size2 = s->l2_slice_size * sizeof(uint64_t); + /* qcow2_downgrade() is not allowed in images with subclusters */ + assert(!has_subclusters(s)); + + slice_size2 = s->l2_slice_size * l2_entry_size(s); n_slices = s->cluster_size / slice_size2; if (!is_active_l1) { @@ -1909,7 +2236,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, } for (j = 0; j < s->l2_slice_size; j++) { - uint64_t l2_entry = be64_to_cpu(l2_slice[j]); + uint64_t l2_entry = get_l2_entry(s, l2_slice, j); int64_t offset = l2_entry & L2E_OFFSET_MASK; QCow2ClusterType cluster_type = qcow2_get_cluster_type(bs, l2_entry); @@ -1921,9 +2248,12 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { if (!bs->backing) { - /* not backed; therefore we can simply deallocate the - * cluster */ - l2_slice[j] = 0; + /* + * not backed; therefore we can simply deallocate the + * cluster. No need to call set_l2_bitmap(), this + * function doesn't support images with subclusters. + */ + set_l2_entry(s, l2_slice, j, 0); l2_dirty = true; continue; } @@ -1989,10 +2319,14 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, } if (l2_refcount == 1) { - l2_slice[j] = cpu_to_be64(offset | QCOW_OFLAG_COPIED); + set_l2_entry(s, l2_slice, j, offset | QCOW_OFLAG_COPIED); } else { - l2_slice[j] = cpu_to_be64(offset); + set_l2_entry(s, l2_slice, j, offset); } + /* + * No need to call set_l2_bitmap() after set_l2_entry() because + * this function doesn't support images with subclusters. + */ l2_dirty = true; } @@ -2087,7 +2421,7 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs, Error *local_err = NULL; ret = qcow2_validate_table(bs, s->snapshots[i].l1_table_offset, - s->snapshots[i].l1_size, sizeof(uint64_t), + s->snapshots[i].l1_size, L1E_SIZE, QCOW_MAX_L1_SIZE, "Snapshot L1 table", &local_err); if (ret < 0) { @@ -2095,7 +2429,7 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs, goto fail; } - l1_size2 = s->snapshots[i].l1_size * sizeof(uint64_t); + l1_size2 = s->snapshots[i].l1_size * L1E_SIZE; new_l1_table = g_try_realloc(l1_table, l1_size2); if (!new_l1_table) { diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 0457a6060d..8e649b008e 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -105,8 +105,8 @@ int qcow2_refcount_init(BlockDriverState *bs) s->get_refcount = get_refcount_funcs[s->refcount_order]; s->set_refcount = set_refcount_funcs[s->refcount_order]; - assert(s->refcount_table_size <= INT_MAX / sizeof(uint64_t)); - refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t); + assert(s->refcount_table_size <= INT_MAX / REFTABLE_ENTRY_SIZE); + refcount_table_size2 = s->refcount_table_size * REFTABLE_ENTRY_SIZE; s->refcount_table = g_try_malloc(refcount_table_size2); if (s->refcount_table_size > 0) { @@ -434,8 +434,8 @@ static int alloc_refcount_block(BlockDriverState *bs, if (refcount_table_index < s->refcount_table_size) { uint64_t data64 = cpu_to_be64(new_block); BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_HOOKUP); - ret = bdrv_pwrite_sync(bs->file, - s->refcount_table_offset + refcount_table_index * sizeof(uint64_t), + ret = bdrv_pwrite_sync(bs->file, s->refcount_table_offset + + refcount_table_index * REFTABLE_ENTRY_SIZE, &data64, sizeof(data64)); if (ret < 0) { goto fail; @@ -562,8 +562,8 @@ int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset, DIV_ROUND_UP(total_refblock_count, 2); } /* The qcow2 file can only store the reftable size in number of clusters */ - table_size = ROUND_UP(table_size, s->cluster_size / sizeof(uint64_t)); - table_clusters = (table_size * sizeof(uint64_t)) / s->cluster_size; + table_size = ROUND_UP(table_size, s->cluster_size / REFTABLE_ENTRY_SIZE); + table_clusters = (table_size * REFTABLE_ENTRY_SIZE) / s->cluster_size; if (table_size > QCOW_MAX_REFTABLE_SIZE) { return -EFBIG; @@ -581,13 +581,13 @@ int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset, if (table_size > s->max_refcount_table_index) { /* We're actually growing the reftable */ memcpy(new_table, s->refcount_table, - (s->max_refcount_table_index + 1) * sizeof(uint64_t)); + (s->max_refcount_table_index + 1) * REFTABLE_ENTRY_SIZE); } else { /* Improbable case: We're shrinking the reftable. However, the caller * has assured us that there is only empty space beyond @start_offset, * so we can simply drop all of the refblocks that won't fit into the * new reftable. */ - memcpy(new_table, s->refcount_table, table_size * sizeof(uint64_t)); + memcpy(new_table, s->refcount_table, table_size * REFTABLE_ENTRY_SIZE); } if (new_refblock_offset) { @@ -682,7 +682,7 @@ int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset, BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE); ret = bdrv_pwrite_sync(bs->file, table_offset, new_table, - table_size * sizeof(uint64_t)); + table_size * REFTABLE_ENTRY_SIZE); if (ret < 0) { goto fail; } @@ -717,7 +717,8 @@ int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset, update_max_refcount_table_index(s); /* Free old table. */ - qcow2_free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t), + qcow2_free_clusters(bs, old_table_offset, + old_table_size * REFTABLE_ENTRY_SIZE, QCOW2_DISCARD_OTHER); return end_offset; @@ -1156,8 +1157,8 @@ void qcow2_free_clusters(BlockDriverState *bs, * Free a cluster using its L2 entry (handles clusters of all types, e.g. * normal cluster, compressed cluster, etc.) */ -void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry, - int nb_clusters, enum qcow2_discard_type type) +void qcow2_free_any_cluster(BlockDriverState *bs, uint64_t l2_entry, + enum qcow2_discard_type type) { BDRVQcow2State *s = bs->opaque; QCow2ClusterType ctype = qcow2_get_cluster_type(bs, l2_entry); @@ -1168,7 +1169,7 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry, ctype == QCOW2_CLUSTER_ZERO_ALLOC)) { bdrv_pdiscard(s->data_file, l2_entry & L2E_OFFSET_MASK, - nb_clusters << s->cluster_bits); + s->cluster_size); } return; } @@ -1191,7 +1192,7 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry, l2_entry & L2E_OFFSET_MASK); } else { qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK, - nb_clusters << s->cluster_bits, type); + s->cluster_size, type); } break; case QCOW2_CLUSTER_ZERO_PLAIN: @@ -1253,8 +1254,8 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, l2_slice = NULL; l1_table = NULL; - l1_size2 = l1_size * sizeof(uint64_t); - slice_size2 = s->l2_slice_size * sizeof(uint64_t); + l1_size2 = l1_size * L1E_SIZE; + slice_size2 = s->l2_slice_size * l2_entry_size(s); n_slices = s->cluster_size / slice_size2; s->cache_discards = true; @@ -1310,7 +1311,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, uint64_t cluster_index; uint64_t offset; - entry = be64_to_cpu(l2_slice[j]); + entry = get_l2_entry(s, l2_slice, j); old_entry = entry; entry &= ~QCOW_OFLAG_COPIED; offset = entry & L2E_OFFSET_MASK; @@ -1384,7 +1385,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache); } - l2_slice[j] = cpu_to_be64(entry); + set_l2_entry(s, l2_slice, j, entry); qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); } @@ -1605,7 +1606,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, int i, l2_size, nb_csectors, ret; /* Read L2 table from disk */ - l2_size = s->l2_size * sizeof(uint64_t); + l2_size = s->l2_size * l2_entry_size(s); l2_table = g_malloc(l2_size); ret = bdrv_pread(bs->file, l2_offset, l2_table, l2_size); @@ -1617,7 +1618,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, /* Do the actual checks */ for(i = 0; i < s->l2_size; i++) { - l2_entry = be64_to_cpu(l2_table[i]); + l2_entry = get_l2_entry(s, l2_table, i); switch (qcow2_get_cluster_type(bs, l2_entry)) { case QCOW2_CLUSTER_COMPRESSED: @@ -1669,26 +1670,33 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, /* Correct offsets are cluster aligned */ if (offset_into_cluster(s, offset)) { + bool contains_data; res->corruptions++; - if (qcow2_get_cluster_type(bs, l2_entry) == - QCOW2_CLUSTER_ZERO_ALLOC) - { - fprintf(stderr, "%s offset=%" PRIx64 ": Preallocated zero " + if (has_subclusters(s)) { + uint64_t l2_bitmap = get_l2_bitmap(s, l2_table, i); + contains_data = (l2_bitmap & QCOW_L2_BITMAP_ALL_ALLOC); + } else { + contains_data = !(l2_entry & QCOW_OFLAG_ZERO); + } + + if (!contains_data) { + fprintf(stderr, "%s offset=%" PRIx64 ": Preallocated " "cluster is not properly aligned; L2 entry " "corrupted.\n", fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", offset); if (fix & BDRV_FIX_ERRORS) { + int idx = i * (l2_entry_size(s) / sizeof(uint64_t)); uint64_t l2e_offset = - l2_offset + (uint64_t)i * sizeof(uint64_t); + l2_offset + (uint64_t)i * l2_entry_size(s); int ign = active ? QCOW2_OL_ACTIVE_L2 : QCOW2_OL_INACTIVE_L2; - l2_entry = QCOW_OFLAG_ZERO; - l2_table[i] = cpu_to_be64(l2_entry); + l2_entry = has_subclusters(s) ? 0 : QCOW_OFLAG_ZERO; + set_l2_entry(s, l2_table, i, l2_entry); ret = qcow2_pre_write_overlap_check(bs, ign, - l2e_offset, sizeof(uint64_t), false); + l2e_offset, l2_entry_size(s), false); if (ret < 0) { fprintf(stderr, "ERROR: Overlap check failed\n"); res->check_errors++; @@ -1698,7 +1706,8 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, } ret = bdrv_pwrite_sync(bs->file, l2e_offset, - &l2_table[i], sizeof(uint64_t)); + &l2_table[idx], + l2_entry_size(s)); if (ret < 0) { fprintf(stderr, "ERROR: Failed to overwrite L2 " "table entry: %s\n", strerror(-ret)); @@ -1776,7 +1785,7 @@ static int check_refcounts_l1(BlockDriverState *bs, uint64_t *l1_table = NULL, l2_offset, l1_size2; int i, ret; - l1_size2 = l1_size * sizeof(uint64_t); + l1_size2 = l1_size * L1E_SIZE; /* Mark L1 table as used */ ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, refcount_table_size, @@ -1905,7 +1914,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, } ret = bdrv_pread(bs->file, l2_offset, l2_table, - s->l2_size * sizeof(uint64_t)); + s->l2_size * l2_entry_size(s)); if (ret < 0) { fprintf(stderr, "ERROR: Could not read L2 table: %s\n", strerror(-ret)); @@ -1914,7 +1923,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, } for (j = 0; j < s->l2_size; j++) { - uint64_t l2_entry = be64_to_cpu(l2_table[j]); + uint64_t l2_entry = get_l2_entry(s, l2_table, j); uint64_t data_offset = l2_entry & L2E_OFFSET_MASK; QCow2ClusterType cluster_type = qcow2_get_cluster_type(bs, l2_entry); @@ -1937,9 +1946,10 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, "l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n", repair ? "Repairing" : "ERROR", l2_entry, refcount); if (repair) { - l2_table[j] = cpu_to_be64(refcount == 1 - ? l2_entry | QCOW_OFLAG_COPIED - : l2_entry & ~QCOW_OFLAG_COPIED); + set_l2_entry(s, l2_table, j, + refcount == 1 ? + l2_entry | QCOW_OFLAG_COPIED : + l2_entry & ~QCOW_OFLAG_COPIED); l2_dirty++; } } @@ -2137,7 +2147,7 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, res->corruptions++; continue; } - if (sn->l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) { + if (sn->l1_size > QCOW_MAX_L1_SIZE / L1E_SIZE) { fprintf(stderr, "ERROR snapshot %s (%s) l1_size=%#" PRIx32 ": " "L1 table is too large; snapshot table entry corrupted\n", sn->id_str, sn->name, sn->l1_size); @@ -2160,7 +2170,8 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, /* refcount data */ ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, nb_clusters, s->refcount_table_offset, - s->refcount_table_size * sizeof(uint64_t)); + s->refcount_table_size * + REFTABLE_ENTRY_SIZE); if (ret < 0) { return ret; } @@ -2381,11 +2392,11 @@ write_refblocks: uint32_t old_reftable_size = reftable_size; uint64_t *new_on_disk_reftable; - reftable_size = ROUND_UP((refblock_index + 1) * sizeof(uint64_t), - s->cluster_size) / sizeof(uint64_t); + reftable_size = ROUND_UP((refblock_index + 1) * REFTABLE_ENTRY_SIZE, + s->cluster_size) / REFTABLE_ENTRY_SIZE; new_on_disk_reftable = g_try_realloc(on_disk_reftable, reftable_size * - sizeof(uint64_t)); + REFTABLE_ENTRY_SIZE); if (!new_on_disk_reftable) { res->check_errors++; ret = -ENOMEM; @@ -2394,7 +2405,7 @@ write_refblocks: on_disk_reftable = new_on_disk_reftable; memset(on_disk_reftable + old_reftable_size, 0, - (reftable_size - old_reftable_size) * sizeof(uint64_t)); + (reftable_size - old_reftable_size) * REFTABLE_ENTRY_SIZE); /* The offset we have for the reftable is now no longer valid; * this will leak that range, but we can easily fix that by running @@ -2411,7 +2422,7 @@ write_refblocks: reftable_offset < 0) { uint64_t reftable_clusters = size_to_clusters(s, reftable_size * - sizeof(uint64_t)); + REFTABLE_ENTRY_SIZE); reftable_offset = alloc_clusters_imrt(bs, reftable_clusters, refcount_table, nb_clusters, &first_free_cluster); @@ -2451,8 +2462,8 @@ write_refblocks: uint64_t post_refblock_start, reftable_clusters; post_refblock_start = ROUND_UP(*nb_clusters, s->refcount_block_size); - reftable_clusters = size_to_clusters(s, - reftable_size * sizeof(uint64_t)); + reftable_clusters = + size_to_clusters(s, reftable_size * REFTABLE_ENTRY_SIZE); /* Not pretty but simple */ if (first_free_cluster < post_refblock_start) { first_free_cluster = post_refblock_start; @@ -2476,16 +2487,16 @@ write_refblocks: } ret = qcow2_pre_write_overlap_check(bs, 0, reftable_offset, - reftable_size * sizeof(uint64_t), + reftable_size * REFTABLE_ENTRY_SIZE, false); if (ret < 0) { fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret)); goto fail; } - assert(reftable_size < INT_MAX / sizeof(uint64_t)); + assert(reftable_size < INT_MAX / REFTABLE_ENTRY_SIZE); ret = bdrv_pwrite(bs->file, reftable_offset, on_disk_reftable, - reftable_size * sizeof(uint64_t)); + reftable_size * REFTABLE_ENTRY_SIZE); if (ret < 0) { fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret)); goto fail; @@ -2494,7 +2505,7 @@ write_refblocks: /* Enter new reftable into the image header */ reftable_offset_and_clusters.reftable_offset = cpu_to_be64(reftable_offset); reftable_offset_and_clusters.reftable_clusters = - cpu_to_be32(size_to_clusters(s, reftable_size * sizeof(uint64_t))); + cpu_to_be32(size_to_clusters(s, reftable_size * REFTABLE_ENTRY_SIZE)); ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, refcount_table_offset), &reftable_offset_and_clusters, @@ -2684,14 +2695,14 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, offset = start_of_cluster(s, offset); if ((chk & QCOW2_OL_ACTIVE_L1) && s->l1_size) { - if (overlaps_with(s->l1_table_offset, s->l1_size * sizeof(uint64_t))) { + if (overlaps_with(s->l1_table_offset, s->l1_size * L1E_SIZE)) { return QCOW2_OL_ACTIVE_L1; } } if ((chk & QCOW2_OL_REFCOUNT_TABLE) && s->refcount_table_size) { if (overlaps_with(s->refcount_table_offset, - s->refcount_table_size * sizeof(uint64_t))) { + s->refcount_table_size * REFTABLE_ENTRY_SIZE)) { return QCOW2_OL_REFCOUNT_TABLE; } } @@ -2706,7 +2717,7 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, for (i = 0; i < s->nb_snapshots; i++) { if (s->snapshots[i].l1_size && overlaps_with(s->snapshots[i].l1_table_offset, - s->snapshots[i].l1_size * sizeof(uint64_t))) { + s->snapshots[i].l1_size * L1E_SIZE)) { return QCOW2_OL_INACTIVE_L1; } } @@ -2740,11 +2751,11 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, for (i = 0; i < s->nb_snapshots; i++) { uint64_t l1_ofs = s->snapshots[i].l1_table_offset; uint32_t l1_sz = s->snapshots[i].l1_size; - uint64_t l1_sz2 = l1_sz * sizeof(uint64_t); + uint64_t l1_sz2 = l1_sz * L1E_SIZE; uint64_t *l1; int ret; - ret = qcow2_validate_table(bs, l1_ofs, l1_sz, sizeof(uint64_t), + ret = qcow2_validate_table(bs, l1_ofs, l1_sz, L1E_SIZE, QCOW_MAX_L1_SIZE, "", NULL); if (ret < 0) { return ret; @@ -2868,8 +2879,8 @@ static int alloc_refblock(BlockDriverState *bs, uint64_t **reftable, uint64_t new_reftable_size; new_reftable_size = ROUND_UP(reftable_index + 1, - s->cluster_size / sizeof(uint64_t)); - if (new_reftable_size > QCOW_MAX_REFTABLE_SIZE / sizeof(uint64_t)) { + s->cluster_size / REFTABLE_ENTRY_SIZE); + if (new_reftable_size > QCOW_MAX_REFTABLE_SIZE / REFTABLE_ENTRY_SIZE) { error_setg(errp, "This operation would make the refcount table grow " "beyond the maximum size supported by QEMU, aborting"); @@ -2877,14 +2888,14 @@ static int alloc_refblock(BlockDriverState *bs, uint64_t **reftable, } new_reftable = g_try_realloc(*reftable, new_reftable_size * - sizeof(uint64_t)); + REFTABLE_ENTRY_SIZE); if (!new_reftable) { error_setg(errp, "Failed to increase reftable buffer size"); return -ENOMEM; } memset(new_reftable + *reftable_size, 0, - (new_reftable_size - *reftable_size) * sizeof(uint64_t)); + (new_reftable_size - *reftable_size) * REFTABLE_ENTRY_SIZE); *reftable = new_reftable; *reftable_size = new_reftable_size; @@ -3155,13 +3166,14 @@ int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, if (new_allocation) { if (new_reftable_offset) { - qcow2_free_clusters(bs, new_reftable_offset, - allocated_reftable_size * sizeof(uint64_t), - QCOW2_DISCARD_NEVER); + qcow2_free_clusters( + bs, new_reftable_offset, + allocated_reftable_size * REFTABLE_ENTRY_SIZE, + QCOW2_DISCARD_NEVER); } new_reftable_offset = qcow2_alloc_clusters(bs, new_reftable_size * - sizeof(uint64_t)); + REFTABLE_ENTRY_SIZE); if (new_reftable_offset < 0) { error_setg_errno(errp, -new_reftable_offset, "Failed to allocate the new reftable"); @@ -3187,7 +3199,7 @@ int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, /* Write the new reftable */ ret = qcow2_pre_write_overlap_check(bs, 0, new_reftable_offset, - new_reftable_size * sizeof(uint64_t), + new_reftable_size * REFTABLE_ENTRY_SIZE, false); if (ret < 0) { error_setg_errno(errp, -ret, "Overlap check failed"); @@ -3199,7 +3211,7 @@ int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, } ret = bdrv_pwrite(bs->file, new_reftable_offset, new_reftable, - new_reftable_size * sizeof(uint64_t)); + new_reftable_size * REFTABLE_ENTRY_SIZE); for (i = 0; i < new_reftable_size; i++) { be64_to_cpus(&new_reftable[i]); @@ -3276,7 +3288,7 @@ done: if (new_reftable_offset > 0) { qcow2_free_clusters(bs, new_reftable_offset, - new_reftable_size * sizeof(uint64_t), + new_reftable_size * REFTABLE_ENTRY_SIZE, QCOW2_DISCARD_OTHER); } } @@ -3365,7 +3377,7 @@ int qcow2_shrink_reftable(BlockDriverState *bs) { BDRVQcow2State *s = bs->opaque; uint64_t *reftable_tmp = - g_malloc(s->refcount_table_size * sizeof(uint64_t)); + g_malloc(s->refcount_table_size * REFTABLE_ENTRY_SIZE); int i, ret; for (i = 0; i < s->refcount_table_size; i++) { @@ -3403,7 +3415,7 @@ int qcow2_shrink_reftable(BlockDriverState *bs) } ret = bdrv_pwrite_sync(bs->file, s->refcount_table_offset, reftable_tmp, - s->refcount_table_size * sizeof(uint64_t)); + s->refcount_table_size * REFTABLE_ENTRY_SIZE); /* * If the write in the reftable failed the image may contain a partially * overwritten reftable. In this case it would be better to clear the diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index 2756b37d24..2e98c7f4b6 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -164,6 +164,12 @@ static int qcow2_do_read_snapshots(BlockDriverState *bs, bool repair, sn->disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; } + if (sn->extra_data_size >= endof(QCowSnapshotExtraData, icount)) { + sn->icount = be64_to_cpu(extra.icount); + } else { + sn->icount = -1ULL; + } + if (sn->extra_data_size > sizeof(extra)) { uint64_t extra_data_end; size_t unknown_extra_data_size; @@ -333,6 +339,7 @@ int qcow2_write_snapshots(BlockDriverState *bs) memset(&extra, 0, sizeof(extra)); extra.vm_state_size_large = cpu_to_be64(sn->vm_state_size); extra.disk_size = cpu_to_be64(sn->disk_size); + extra.icount = cpu_to_be64(sn->icount); id_str_size = strlen(sn->id_str); name_size = strlen(sn->name); @@ -656,10 +663,11 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) sn->date_sec = sn_info->date_sec; sn->date_nsec = sn_info->date_nsec; sn->vm_clock_nsec = sn_info->vm_clock_nsec; + sn->icount = sn_info->icount; sn->extra_data_size = sizeof(QCowSnapshotExtraData); /* Allocate the L1 table of the snapshot and copy the current one there. */ - l1_table_offset = qcow2_alloc_clusters(bs, s->l1_size * sizeof(uint64_t)); + l1_table_offset = qcow2_alloc_clusters(bs, s->l1_size * L1E_SIZE); if (l1_table_offset < 0) { ret = l1_table_offset; goto fail; @@ -679,13 +687,13 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) } ret = qcow2_pre_write_overlap_check(bs, 0, sn->l1_table_offset, - s->l1_size * sizeof(uint64_t), false); + s->l1_size * L1E_SIZE, false); if (ret < 0) { goto fail; } ret = bdrv_pwrite(bs->file, sn->l1_table_offset, l1_table, - s->l1_size * sizeof(uint64_t)); + s->l1_size * L1E_SIZE); if (ret < 0) { goto fail; } @@ -768,7 +776,7 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) sn = &s->snapshots[snapshot_index]; ret = qcow2_validate_table(bs, sn->l1_table_offset, sn->l1_size, - sizeof(uint64_t), QCOW_MAX_L1_SIZE, + L1E_SIZE, QCOW_MAX_L1_SIZE, "Snapshot L1 table", &local_err); if (ret < 0) { error_report_err(local_err); @@ -803,8 +811,8 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) goto fail; } - cur_l1_bytes = s->l1_size * sizeof(uint64_t); - sn_l1_bytes = sn->l1_size * sizeof(uint64_t); + cur_l1_bytes = s->l1_size * L1E_SIZE; + sn_l1_bytes = sn->l1_size * L1E_SIZE; /* * Copy the snapshot L1 table to the current L1 table. @@ -917,7 +925,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, sn = s->snapshots[snapshot_index]; ret = qcow2_validate_table(bs, sn.l1_table_offset, sn.l1_size, - sizeof(uint64_t), QCOW_MAX_L1_SIZE, + L1E_SIZE, QCOW_MAX_L1_SIZE, "Snapshot L1 table", errp); if (ret < 0) { return ret; @@ -953,7 +961,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, error_setg_errno(errp, -ret, "Failed to free the cluster and L1 table"); return ret; } - qcow2_free_clusters(bs, sn.l1_table_offset, sn.l1_size * sizeof(uint64_t), + qcow2_free_clusters(bs, sn.l1_table_offset, sn.l1_size * L1E_SIZE, QCOW2_DISCARD_SNAPSHOT); /* must update the copied flag on the current cluster offsets */ @@ -1000,6 +1008,7 @@ int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) sn_info->date_sec = sn->date_sec; sn_info->date_nsec = sn->date_nsec; sn_info->vm_clock_nsec = sn->vm_clock_nsec; + sn_info->icount = sn->icount; } *psn_tab = sn_tab; return s->nb_snapshots; @@ -1030,12 +1039,12 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, /* Allocate and read in the snapshot's L1 table */ ret = qcow2_validate_table(bs, sn->l1_table_offset, sn->l1_size, - sizeof(uint64_t), QCOW_MAX_L1_SIZE, + L1E_SIZE, QCOW_MAX_L1_SIZE, "Snapshot L1 table", errp); if (ret < 0) { return ret; } - new_l1_bytes = sn->l1_size * sizeof(uint64_t); + new_l1_bytes = sn->l1_size * L1E_SIZE; new_l1_table = qemu_try_blockalign(bs->file->bs, new_l1_bytes); if (new_l1_table == NULL) { return -ENOMEM; diff --git a/block/qcow2.c b/block/qcow2.c index 6ad6bdc166..9727ae8fe3 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -74,7 +74,7 @@ typedef struct { static int coroutine_fn qcow2_co_preadv_compressed(BlockDriverState *bs, - uint64_t file_cluster_offset, + uint64_t cluster_descriptor, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, @@ -269,7 +269,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, case QCOW2_EXT_MAGIC_FEATURE_TABLE: if (p_feature_table != NULL) { - void* feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature)); + void *feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature)); ret = bdrv_pread(bs->file, offset , feature_table, ext.len); if (ret < 0) { error_setg_errno(errp, -ret, "ERROR: ext_feature_table: " @@ -840,9 +840,10 @@ static void cache_clean_timer_init(BlockDriverState *bs, AioContext *context) { BDRVQcow2State *s = bs->opaque; if (s->cache_clean_interval > 0) { - s->cache_clean_timer = aio_timer_new(context, QEMU_CLOCK_VIRTUAL, - SCALE_MS, cache_clean_timer_cb, - bs); + s->cache_clean_timer = + aio_timer_new_with_attrs(context, QEMU_CLOCK_VIRTUAL, + SCALE_MS, QEMU_TIMER_ATTR_EXTERNAL, + cache_clean_timer_cb, bs); timer_mod(s->cache_clean_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + (int64_t) s->cache_clean_interval * 1000); } @@ -852,7 +853,6 @@ static void cache_clean_timer_del(BlockDriverState *bs) { BDRVQcow2State *s = bs->opaque; if (s->cache_clean_timer) { - timer_del(s->cache_clean_timer); timer_free(s->cache_clean_timer); s->cache_clean_timer = NULL; } @@ -869,7 +869,7 @@ static void qcow2_attach_aio_context(BlockDriverState *bs, cache_clean_timer_init(bs, new_context); } -static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, +static bool read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, uint64_t *l2_cache_size, uint64_t *l2_cache_entry_size, uint64_t *refcount_cache_size, Error **errp) @@ -883,7 +883,7 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, uint64_t max_l2_entries = DIV_ROUND_UP(virtual_disk_size, s->cluster_size); /* An L2 table is always one cluster in size so the max cache size * should be a multiple of the cluster size. */ - uint64_t max_l2_cache = ROUND_UP(max_l2_entries * sizeof(uint64_t), + uint64_t max_l2_cache = ROUND_UP(max_l2_entries * l2_entry_size(s), s->cluster_size); combined_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_CACHE_SIZE); @@ -907,16 +907,16 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, error_setg(errp, QCOW2_OPT_CACHE_SIZE ", " QCOW2_OPT_L2_CACHE_SIZE " and " QCOW2_OPT_REFCOUNT_CACHE_SIZE " may not be set " "at the same time"); - return; + return false; } else if (l2_cache_size_set && (l2_cache_max_setting > combined_cache_size)) { error_setg(errp, QCOW2_OPT_L2_CACHE_SIZE " may not exceed " QCOW2_OPT_CACHE_SIZE); - return; + return false; } else if (*refcount_cache_size > combined_cache_size) { error_setg(errp, QCOW2_OPT_REFCOUNT_CACHE_SIZE " may not exceed " QCOW2_OPT_CACHE_SIZE); - return; + return false; } if (l2_cache_size_set) { @@ -955,8 +955,10 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, error_setg(errp, "L2 cache entry size must be a power of two " "between %d and the cluster size (%d)", 1 << MIN_CLUSTER_BITS, s->cluster_size); - return; + return false; } + + return true; } typedef struct Qcow2ReopenState { @@ -983,7 +985,6 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, int i; const char *encryptfmt; QDict *encryptopts = NULL; - Error *local_err = NULL; int ret; qdict_extract_subqdict(options, &encryptopts, "encrypt."); @@ -996,10 +997,8 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, } /* get L2 table/refcount block cache size from command line options */ - read_cache_sizes(bs, opts, &l2_cache_size, &l2_cache_entry_size, - &refcount_cache_size, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!read_cache_sizes(bs, opts, &l2_cache_size, &l2_cache_entry_size, + &refcount_cache_size, errp)) { ret = -EINVAL; goto fail; } @@ -1042,7 +1041,7 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, } } - r->l2_slice_size = l2_cache_entry_size / sizeof(uint64_t); + r->l2_slice_size = l2_cache_entry_size / l2_entry_size(s); r->l2_table_cache = qcow2_cache_create(bs, l2_cache_size, l2_cache_entry_size); r->refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size, @@ -1160,6 +1159,10 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, } qdict_put_str(encryptopts, "format", "qcow"); r->crypto_opts = block_crypto_open_opts_init(encryptopts, errp); + if (!r->crypto_opts) { + ret = -EINVAL; + goto fail; + } break; case QCOW_CRYPT_LUKS: @@ -1172,14 +1175,15 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, } qdict_put_str(encryptopts, "format", "luks"); r->crypto_opts = block_crypto_open_opts_init(encryptopts, errp); + if (!r->crypto_opts) { + ret = -EINVAL; + goto fail; + } break; default: error_setg(errp, "Unsupported encryption method %d", s->crypt_method_header); - break; - } - if (s->crypt_method_header != QCOW_CRYPT_NONE && !r->crypto_opts) { ret = -EINVAL; goto fail; } @@ -1293,11 +1297,11 @@ static int validate_compression_type(BDRVQcow2State *s, Error **errp) static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { + ERRP_GUARD(); BDRVQcow2State *s = bs->opaque; unsigned int len, i; int ret = 0; QCowHeader header; - Error *local_err = NULL; uint64_t ext_end; uint64_t l1_vm_state_index; bool update_header = false; @@ -1444,6 +1448,17 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, } } + s->subclusters_per_cluster = + has_subclusters(s) ? QCOW_EXTL2_SUBCLUSTERS_PER_CLUSTER : 1; + s->subcluster_size = s->cluster_size / s->subclusters_per_cluster; + s->subcluster_bits = ctz32(s->subcluster_size); + + if (s->subcluster_size < (1 << MIN_CLUSTER_BITS)) { + error_setg(errp, "Unsupported subcluster size: %d", s->subcluster_size); + ret = -EINVAL; + goto fail; + } + /* Check support for various header values */ if (header.refcount_order > 6) { error_setg(errp, "Reference count entry width too large; may not " @@ -1484,7 +1499,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, bs->encrypted = true; } - s->l2_bits = s->cluster_bits - 3; /* L2 is always one cluster */ + s->l2_bits = s->cluster_bits - ctz32(l2_entry_size(s)); s->l2_size = 1 << s->l2_bits; /* 2^(s->refcount_order - 3) is the refcount width in bytes */ s->refcount_block_bits = s->cluster_bits - (s->refcount_order - 3); @@ -1532,7 +1547,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, /* read the level 1 table */ ret = qcow2_validate_table(bs, header.l1_table_offset, - header.l1_size, sizeof(uint64_t), + header.l1_size, L1E_SIZE, QCOW_MAX_L1_SIZE, "Active L1 table", errp); if (ret < 0) { goto fail; @@ -1557,15 +1572,14 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, } if (s->l1_size > 0) { - s->l1_table = qemu_try_blockalign(bs->file->bs, - s->l1_size * sizeof(uint64_t)); + s->l1_table = qemu_try_blockalign(bs->file->bs, s->l1_size * L1E_SIZE); if (s->l1_table == NULL) { error_setg(errp, "Could not allocate L1 table"); ret = -ENOMEM; goto fail; } ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, - s->l1_size * sizeof(uint64_t)); + s->l1_size * L1E_SIZE); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read L1 table"); goto fail; @@ -1602,9 +1616,8 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, /* Open external data file */ s->data_file = bdrv_open_child(NULL, options, "data-file", bs, &child_of_bds, BDRV_CHILD_DATA, - true, &local_err); - if (local_err) { - error_propagate(errp, local_err); + true, errp); + if (*errp) { ret = -EINVAL; goto fail; } @@ -1776,9 +1789,8 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, if (!(bdrv_get_flags(bs) & BDRV_O_INACTIVE)) { /* It's case 1, 2 or 3.2. Or 3.1 which is BUG in management layer. */ - bool header_updated = qcow2_load_dirty_bitmaps(bs, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); + bool header_updated; + if (!qcow2_load_dirty_bitmaps(bs, &header_updated, errp)) { ret = -EINVAL; goto fail; } @@ -1908,7 +1920,7 @@ static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp) /* Encryption works on a sector granularity */ bs->bl.request_alignment = qcrypto_block_get_sector_size(s->crypto); } - bs->bl.pwrite_zeroes_alignment = s->cluster_size; + bs->bl.pwrite_zeroes_alignment = s->subcluster_size; bs->bl.pdiscard_alignment = s->cluster_size; } @@ -2036,8 +2048,9 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs, BlockDriverState **file) { BDRVQcow2State *s = bs->opaque; - uint64_t cluster_offset; + uint64_t host_offset; unsigned int bytes; + QCow2SubclusterType type; int ret, status = 0; qemu_co_mutex_lock(&s->lock); @@ -2049,7 +2062,7 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs, } bytes = MIN(INT_MAX, count); - ret = qcow2_get_cluster_offset(bs, offset, &bytes, &cluster_offset); + ret = qcow2_get_host_offset(bs, offset, &bytes, &host_offset, &type); qemu_co_mutex_unlock(&s->lock); if (ret < 0) { return ret; @@ -2057,15 +2070,18 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs, *pnum = bytes; - if ((ret == QCOW2_CLUSTER_NORMAL || ret == QCOW2_CLUSTER_ZERO_ALLOC) && - !s->crypto) { - *map = cluster_offset | offset_into_cluster(s, offset); + if ((type == QCOW2_SUBCLUSTER_NORMAL || + type == QCOW2_SUBCLUSTER_ZERO_ALLOC || + type == QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC) && !s->crypto) { + *map = host_offset; *file = s->data_file->bs; status |= BDRV_BLOCK_OFFSET_VALID; } - if (ret == QCOW2_CLUSTER_ZERO_PLAIN || ret == QCOW2_CLUSTER_ZERO_ALLOC) { + if (type == QCOW2_SUBCLUSTER_ZERO_PLAIN || + type == QCOW2_SUBCLUSTER_ZERO_ALLOC) { status |= BDRV_BLOCK_ZERO; - } else if (ret != QCOW2_CLUSTER_UNALLOCATED) { + } else if (type != QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN && + type != QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC) { status |= BDRV_BLOCK_DATA; } if (s->metadata_preallocation && (status & BDRV_BLOCK_DATA) && @@ -2096,9 +2112,7 @@ static coroutine_fn int qcow2_handle_l2meta(BlockDriverState *bs, } /* Take the request off the list of running requests */ - if (l2meta->nb_clusters != 0) { - QLIST_REMOVE(l2meta, next_in_flight); - } + QLIST_REMOVE(l2meta, next_in_flight); qemu_co_queue_restart_all(&l2meta->dependent_requests); @@ -2113,7 +2127,7 @@ out: static coroutine_fn int qcow2_co_preadv_encrypted(BlockDriverState *bs, - uint64_t file_cluster_offset, + uint64_t host_offset, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, @@ -2140,16 +2154,12 @@ qcow2_co_preadv_encrypted(BlockDriverState *bs, } BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); - ret = bdrv_co_pread(s->data_file, - file_cluster_offset + offset_into_cluster(s, offset), - bytes, buf, 0); + ret = bdrv_co_pread(s->data_file, host_offset, bytes, buf, 0); if (ret < 0) { goto fail; } - if (qcow2_co_decrypt(bs, - file_cluster_offset + offset_into_cluster(s, offset), - offset, buf, bytes) < 0) + if (qcow2_co_decrypt(bs, host_offset, offset, buf, bytes) < 0) { ret = -EIO; goto fail; @@ -2166,8 +2176,8 @@ typedef struct Qcow2AioTask { AioTask task; BlockDriverState *bs; - QCow2ClusterType cluster_type; /* only for read */ - uint64_t file_cluster_offset; + QCow2SubclusterType subcluster_type; /* only for read */ + uint64_t host_offset; /* or full descriptor in compressed clusters */ uint64_t offset; uint64_t bytes; QEMUIOVector *qiov; @@ -2179,8 +2189,8 @@ static coroutine_fn int qcow2_co_preadv_task_entry(AioTask *task); static coroutine_fn int qcow2_add_task(BlockDriverState *bs, AioTaskPool *pool, AioTaskFunc func, - QCow2ClusterType cluster_type, - uint64_t file_cluster_offset, + QCow2SubclusterType subcluster_type, + uint64_t host_offset, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, @@ -2193,9 +2203,9 @@ static coroutine_fn int qcow2_add_task(BlockDriverState *bs, *task = (Qcow2AioTask) { .task.func = func, .bs = bs, - .cluster_type = cluster_type, + .subcluster_type = subcluster_type, .qiov = qiov, - .file_cluster_offset = file_cluster_offset, + .host_offset = host_offset, .offset = offset, .bytes = bytes, .qiov_offset = qiov_offset, @@ -2204,7 +2214,7 @@ static coroutine_fn int qcow2_add_task(BlockDriverState *bs, trace_qcow2_add_task(qemu_coroutine_self(), bs, pool, func == qcow2_co_preadv_task_entry ? "read" : "write", - cluster_type, file_cluster_offset, offset, bytes, + subcluster_type, host_offset, offset, bytes, qiov, qiov_offset); if (!pool) { @@ -2217,42 +2227,40 @@ static coroutine_fn int qcow2_add_task(BlockDriverState *bs, } static coroutine_fn int qcow2_co_preadv_task(BlockDriverState *bs, - QCow2ClusterType cluster_type, - uint64_t file_cluster_offset, + QCow2SubclusterType subc_type, + uint64_t host_offset, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, size_t qiov_offset) { BDRVQcow2State *s = bs->opaque; - int offset_in_cluster = offset_into_cluster(s, offset); - switch (cluster_type) { - case QCOW2_CLUSTER_ZERO_PLAIN: - case QCOW2_CLUSTER_ZERO_ALLOC: + switch (subc_type) { + case QCOW2_SUBCLUSTER_ZERO_PLAIN: + case QCOW2_SUBCLUSTER_ZERO_ALLOC: /* Both zero types are handled in qcow2_co_preadv_part */ g_assert_not_reached(); - case QCOW2_CLUSTER_UNALLOCATED: + case QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN: + case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: assert(bs->backing); /* otherwise handled in qcow2_co_preadv_part */ BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); return bdrv_co_preadv_part(bs->backing, offset, bytes, qiov, qiov_offset, 0); - case QCOW2_CLUSTER_COMPRESSED: - return qcow2_co_preadv_compressed(bs, file_cluster_offset, + case QCOW2_SUBCLUSTER_COMPRESSED: + return qcow2_co_preadv_compressed(bs, host_offset, offset, bytes, qiov, qiov_offset); - case QCOW2_CLUSTER_NORMAL: - assert(offset_into_cluster(s, file_cluster_offset) == 0); + case QCOW2_SUBCLUSTER_NORMAL: if (bs->encrypted) { - return qcow2_co_preadv_encrypted(bs, file_cluster_offset, + return qcow2_co_preadv_encrypted(bs, host_offset, offset, bytes, qiov, qiov_offset); } BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); - return bdrv_co_preadv_part(s->data_file, - file_cluster_offset + offset_in_cluster, + return bdrv_co_preadv_part(s->data_file, host_offset, bytes, qiov, qiov_offset, 0); default: @@ -2268,8 +2276,9 @@ static coroutine_fn int qcow2_co_preadv_task_entry(AioTask *task) assert(!t->l2meta); - return qcow2_co_preadv_task(t->bs, t->cluster_type, t->file_cluster_offset, - t->offset, t->bytes, t->qiov, t->qiov_offset); + return qcow2_co_preadv_task(t->bs, t->subcluster_type, + t->host_offset, t->offset, t->bytes, + t->qiov, t->qiov_offset); } static coroutine_fn int qcow2_co_preadv_part(BlockDriverState *bs, @@ -2280,7 +2289,8 @@ static coroutine_fn int qcow2_co_preadv_part(BlockDriverState *bs, BDRVQcow2State *s = bs->opaque; int ret = 0; unsigned int cur_bytes; /* number of bytes in current iteration */ - uint64_t cluster_offset = 0; + uint64_t host_offset = 0; + QCow2SubclusterType type; AioTaskPool *aio = NULL; while (bytes != 0 && aio_task_pool_status(aio) == 0) { @@ -2292,23 +2302,25 @@ static coroutine_fn int qcow2_co_preadv_part(BlockDriverState *bs, } qemu_co_mutex_lock(&s->lock); - ret = qcow2_get_cluster_offset(bs, offset, &cur_bytes, &cluster_offset); + ret = qcow2_get_host_offset(bs, offset, &cur_bytes, + &host_offset, &type); qemu_co_mutex_unlock(&s->lock); if (ret < 0) { goto out; } - if (ret == QCOW2_CLUSTER_ZERO_PLAIN || - ret == QCOW2_CLUSTER_ZERO_ALLOC || - (ret == QCOW2_CLUSTER_UNALLOCATED && !bs->backing)) + if (type == QCOW2_SUBCLUSTER_ZERO_PLAIN || + type == QCOW2_SUBCLUSTER_ZERO_ALLOC || + (type == QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN && !bs->backing) || + (type == QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC && !bs->backing)) { qemu_iovec_memset(qiov, qiov_offset, 0, cur_bytes); } else { if (!aio && cur_bytes != bytes) { aio = aio_task_pool_new(QCOW2_MAX_WORKERS); } - ret = qcow2_add_task(bs, aio, qcow2_co_preadv_task_entry, ret, - cluster_offset, offset, cur_bytes, + ret = qcow2_add_task(bs, aio, qcow2_co_preadv_task_entry, type, + host_offset, offset, cur_bytes, qiov, qiov_offset, NULL); if (ret < 0) { goto out; @@ -2351,15 +2363,26 @@ static bool merge_cow(uint64_t offset, unsigned bytes, continue; } - /* The data (middle) region must be immediately after the - * start region */ + /* + * The write request should start immediately after the first + * COW region. This does not always happen because the area + * touched by the request can be larger than the one defined + * by @m (a single request can span an area consisting of a + * mix of previously unallocated and allocated clusters, that + * is why @l2meta is a list). + */ if (l2meta_cow_start(m) + m->cow_start.nb_bytes != offset) { + /* In this case the request starts before this region */ + assert(offset < l2meta_cow_start(m)); + assert(m->cow_start.nb_bytes == 0); continue; } - /* The end region must be immediately after the data (middle) - * region */ + /* The write request should end immediately before the second + * COW region (see above for why it does not always happen) */ if (m->offset + m->cow_end.offset != offset + bytes) { + assert(offset + bytes > m->offset + m->cow_end.offset); + assert(m->cow_end.nb_bytes == 0); continue; } @@ -2377,26 +2400,26 @@ static bool merge_cow(uint64_t offset, unsigned bytes, return false; } -static bool is_unallocated(BlockDriverState *bs, int64_t offset, int64_t bytes) -{ - int64_t nr; - return !bytes || - (!bdrv_is_allocated_above(bs, NULL, false, offset, bytes, &nr) && - nr == bytes); -} - -static bool is_zero_cow(BlockDriverState *bs, QCowL2Meta *m) +/* + * Return 1 if the COW regions read as zeroes, 0 if not, < 0 on error. + * Note that returning 0 does not guarantee non-zero data. + */ +static int is_zero_cow(BlockDriverState *bs, QCowL2Meta *m) { /* * This check is designed for optimization shortcut so it must be * efficient. - * Instead of is_zero(), use is_unallocated() as it is faster (but not - * as accurate and can result in false negatives). + * Instead of is_zero(), use bdrv_co_is_zero_fast() as it is + * faster (but not as accurate and can result in false negatives). */ - return is_unallocated(bs, m->offset + m->cow_start.offset, - m->cow_start.nb_bytes) && - is_unallocated(bs, m->offset + m->cow_end.offset, - m->cow_end.nb_bytes); + int ret = bdrv_co_is_zero_fast(bs, m->offset + m->cow_start.offset, + m->cow_start.nb_bytes); + if (ret <= 0) { + return ret; + } + + return bdrv_co_is_zero_fast(bs, m->offset + m->cow_end.offset, + m->cow_end.nb_bytes); } static int handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta) @@ -2414,12 +2437,18 @@ static int handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta) for (m = l2meta; m != NULL; m = m->next) { int ret; + uint64_t start_offset = m->alloc_offset + m->cow_start.offset; + unsigned nb_bytes = m->cow_end.offset + m->cow_end.nb_bytes - + m->cow_start.offset; if (!m->cow_start.nb_bytes && !m->cow_end.nb_bytes) { continue; } - if (!is_zero_cow(bs, m)) { + ret = is_zero_cow(bs, m); + if (ret < 0) { + return ret; + } else if (ret == 0) { continue; } @@ -2428,16 +2457,14 @@ static int handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta) * efficiently zero out the whole clusters */ - ret = qcow2_pre_write_overlap_check(bs, 0, m->alloc_offset, - m->nb_clusters * s->cluster_size, + ret = qcow2_pre_write_overlap_check(bs, 0, start_offset, nb_bytes, true); if (ret < 0) { return ret; } BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC_SPACE); - ret = bdrv_co_pwrite_zeroes(s->data_file, m->alloc_offset, - m->nb_clusters * s->cluster_size, + ret = bdrv_co_pwrite_zeroes(s->data_file, start_offset, nb_bytes, BDRV_REQ_NO_FALLBACK); if (ret < 0) { if (ret != -ENOTSUP && ret != -EAGAIN) { @@ -2459,7 +2486,7 @@ static int handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta) * not use it somehow after qcow2_co_pwritev_task() call */ static coroutine_fn int qcow2_co_pwritev_task(BlockDriverState *bs, - uint64_t file_cluster_offset, + uint64_t host_offset, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, uint64_t qiov_offset, @@ -2468,7 +2495,6 @@ static coroutine_fn int qcow2_co_pwritev_task(BlockDriverState *bs, int ret; BDRVQcow2State *s = bs->opaque; void *crypt_buf = NULL; - int offset_in_cluster = offset_into_cluster(s, offset); QEMUIOVector encrypted_qiov; if (bs->encrypted) { @@ -2481,9 +2507,7 @@ static coroutine_fn int qcow2_co_pwritev_task(BlockDriverState *bs, } qemu_iovec_to_buf(qiov, qiov_offset, crypt_buf, bytes); - if (qcow2_co_encrypt(bs, file_cluster_offset + offset_in_cluster, - offset, crypt_buf, bytes) < 0) - { + if (qcow2_co_encrypt(bs, host_offset, offset, crypt_buf, bytes) < 0) { ret = -EIO; goto out_unlocked; } @@ -2507,10 +2531,8 @@ static coroutine_fn int qcow2_co_pwritev_task(BlockDriverState *bs, */ if (!merge_cow(offset, bytes, qiov, qiov_offset, l2meta)) { BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); - trace_qcow2_writev_data(qemu_coroutine_self(), - file_cluster_offset + offset_in_cluster); - ret = bdrv_co_pwritev_part(s->data_file, - file_cluster_offset + offset_in_cluster, + trace_qcow2_writev_data(qemu_coroutine_self(), host_offset); + ret = bdrv_co_pwritev_part(s->data_file, host_offset, bytes, qiov, qiov_offset, 0); if (ret < 0) { goto out_unlocked; @@ -2538,9 +2560,9 @@ static coroutine_fn int qcow2_co_pwritev_task_entry(AioTask *task) { Qcow2AioTask *t = container_of(task, Qcow2AioTask, task); - assert(!t->cluster_type); + assert(!t->subcluster_type); - return qcow2_co_pwritev_task(t->bs, t->file_cluster_offset, + return qcow2_co_pwritev_task(t->bs, t->host_offset, t->offset, t->bytes, t->qiov, t->qiov_offset, t->l2meta); } @@ -2553,7 +2575,7 @@ static coroutine_fn int qcow2_co_pwritev_part( int offset_in_cluster; int ret; unsigned int cur_bytes; /* number of sectors in current iteration */ - uint64_t cluster_offset; + uint64_t host_offset; QCowL2Meta *l2meta = NULL; AioTaskPool *aio = NULL; @@ -2574,16 +2596,13 @@ static coroutine_fn int qcow2_co_pwritev_part( qemu_co_mutex_lock(&s->lock); - ret = qcow2_alloc_cluster_offset(bs, offset, &cur_bytes, - &cluster_offset, &l2meta); + ret = qcow2_alloc_host_offset(bs, offset, &cur_bytes, + &host_offset, &l2meta); if (ret < 0) { goto out_locked; } - assert(offset_into_cluster(s, cluster_offset) == 0); - - ret = qcow2_pre_write_overlap_check(bs, 0, - cluster_offset + offset_in_cluster, + ret = qcow2_pre_write_overlap_check(bs, 0, host_offset, cur_bytes, true); if (ret < 0) { goto out_locked; @@ -2595,8 +2614,8 @@ static coroutine_fn int qcow2_co_pwritev_part( aio = aio_task_pool_new(QCOW2_MAX_WORKERS); } ret = qcow2_add_task(bs, aio, qcow2_co_pwritev_task_entry, 0, - cluster_offset, offset, cur_bytes, - qiov, qiov_offset, l2meta); + host_offset, offset, + cur_bytes, qiov, qiov_offset, l2meta); l2meta = NULL; /* l2meta is consumed by qcow2_co_pwritev_task() */ if (ret < 0) { goto fail_nometa; @@ -2703,11 +2722,11 @@ static void qcow2_close(BlockDriverState *bs) static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs, Error **errp) { + ERRP_GUARD(); BDRVQcow2State *s = bs->opaque; int flags = s->flags; QCryptoBlock *crypto = NULL; QDict *options; - Error *local_err = NULL; int ret; /* @@ -2725,16 +2744,11 @@ static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs, flags &= ~BDRV_O_INACTIVE; qemu_co_mutex_lock(&s->lock); - ret = qcow2_do_open(bs, options, flags, &local_err); + ret = qcow2_do_open(bs, options, flags, errp); qemu_co_mutex_unlock(&s->lock); qobject_unref(options); - if (local_err) { - error_propagate_prepend(errp, local_err, - "Could not reopen qcow2 layer: "); - bs->drv = NULL; - return; - } else if (ret < 0) { - error_setg_errno(errp, -ret, "Could not reopen qcow2 layer"); + if (ret < 0) { + error_prepend(errp, "Could not reopen qcow2 layer: "); bs->drv = NULL; return; } @@ -2930,6 +2944,11 @@ int qcow2_update_header(BlockDriverState *bs) .bit = QCOW2_INCOMPAT_COMPRESSION_BITNR, .name = "compression type", }, + { + .type = QCOW2_FEAT_TYPE_INCOMPATIBLE, + .bit = QCOW2_INCOMPAT_EXTL2_BITNR, + .name = "extended L2 entries", + }, { .type = QCOW2_FEAT_TYPE_COMPATIBLE, .bit = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR, @@ -3111,37 +3130,28 @@ static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset, int64_t file_length; unsigned int cur_bytes; int ret; - QCowL2Meta *meta; + QCowL2Meta *meta = NULL, *m; assert(offset <= new_length); bytes = new_length - offset; while (bytes) { cur_bytes = MIN(bytes, QEMU_ALIGN_DOWN(INT_MAX, s->cluster_size)); - ret = qcow2_alloc_cluster_offset(bs, offset, &cur_bytes, - &host_offset, &meta); + ret = qcow2_alloc_host_offset(bs, offset, &cur_bytes, + &host_offset, &meta); if (ret < 0) { error_setg_errno(errp, -ret, "Allocating clusters failed"); - return ret; + goto out; } - while (meta) { - QCowL2Meta *next = meta->next; + for (m = meta; m != NULL; m = m->next) { + m->prealloc = true; + } - ret = qcow2_alloc_cluster_link_l2(bs, meta); - if (ret < 0) { - error_setg_errno(errp, -ret, "Mapping clusters failed"); - qcow2_free_any_clusters(bs, meta->alloc_offset, - meta->nb_clusters, QCOW2_DISCARD_NEVER); - return ret; - } - - /* There are no dependent requests, but we need to remove our - * request from the list of in-flight requests */ - QLIST_REMOVE(meta, next_in_flight); - - g_free(meta); - meta = next; + ret = qcow2_handle_l2meta(bs, &meta, true); + if (ret < 0) { + error_setg_errno(errp, -ret, "Mapping clusters failed"); + goto out; } /* TODO Preallocate data if requested */ @@ -3158,7 +3168,8 @@ static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset, file_length = bdrv_getlength(s->data_file->bs); if (file_length < 0) { error_setg_errno(errp, -file_length, "Could not get file size"); - return file_length; + ret = file_length; + goto out; } if (host_offset + cur_bytes > file_length) { @@ -3168,11 +3179,15 @@ static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset, ret = bdrv_co_truncate(s->data_file, host_offset + cur_bytes, false, mode, 0, errp); if (ret < 0) { - return ret; + goto out; } } - return 0; + ret = 0; + +out: + qcow2_handle_l2meta(bs, &meta, false); + return ret; } /* qcow2_refcount_metadata_size: @@ -3197,7 +3212,7 @@ int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size, * where no further refcount blocks or table clusters are required to * reference count every cluster. */ - int64_t blocks_per_table_cluster = cluster_size / sizeof(uint64_t); + int64_t blocks_per_table_cluster = cluster_size / REFTABLE_ENTRY_SIZE; int64_t refcounts_per_block = cluster_size * 8 / (1 << refcount_order); int64_t table = 0; /* number of refcount table clusters */ int64_t blocks = 0; /* number of refcount block clusters */ @@ -3229,30 +3244,33 @@ int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size, * @total_size: virtual disk size in bytes * @cluster_size: cluster size in bytes * @refcount_order: refcount bits power-of-2 exponent + * @extended_l2: true if the image has extended L2 entries * * Returns: Total number of bytes required for the fully allocated image * (including metadata). */ static int64_t qcow2_calc_prealloc_size(int64_t total_size, size_t cluster_size, - int refcount_order) + int refcount_order, + bool extended_l2) { int64_t meta_size = 0; uint64_t nl1e, nl2e; int64_t aligned_total_size = ROUND_UP(total_size, cluster_size); + size_t l2e_size = extended_l2 ? L2E_SIZE_EXTENDED : L2E_SIZE_NORMAL; /* header: 1 cluster */ meta_size += cluster_size; /* total size of L2 tables */ nl2e = aligned_total_size / cluster_size; - nl2e = ROUND_UP(nl2e, cluster_size / sizeof(uint64_t)); - meta_size += nl2e * sizeof(uint64_t); + nl2e = ROUND_UP(nl2e, cluster_size / l2e_size); + meta_size += nl2e * l2e_size; /* total size of L1 tables */ - nl1e = nl2e * sizeof(uint64_t) / cluster_size; - nl1e = ROUND_UP(nl1e, cluster_size / sizeof(uint64_t)); - meta_size += nl1e * sizeof(uint64_t); + nl1e = nl2e * l2e_size / cluster_size; + nl1e = ROUND_UP(nl1e, cluster_size / L1E_SIZE); + meta_size += nl1e * L1E_SIZE; /* total size of refcount table and blocks */ meta_size += qcow2_refcount_metadata_size( @@ -3262,7 +3280,8 @@ static int64_t qcow2_calc_prealloc_size(int64_t total_size, return meta_size + aligned_total_size; } -static bool validate_cluster_size(size_t cluster_size, Error **errp) +static bool validate_cluster_size(size_t cluster_size, bool extended_l2, + Error **errp) { int cluster_bits = ctz32(cluster_size); if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS || @@ -3272,16 +3291,28 @@ static bool validate_cluster_size(size_t cluster_size, Error **errp) "%dk", 1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10)); return false; } + + if (extended_l2) { + unsigned min_cluster_size = + (1 << MIN_CLUSTER_BITS) * QCOW_EXTL2_SUBCLUSTERS_PER_CLUSTER; + if (cluster_size < min_cluster_size) { + error_setg(errp, "Extended L2 entries are only supported with " + "cluster sizes of at least %u bytes", min_cluster_size); + return false; + } + } + return true; } -static size_t qcow2_opt_get_cluster_size_del(QemuOpts *opts, Error **errp) +static size_t qcow2_opt_get_cluster_size_del(QemuOpts *opts, bool extended_l2, + Error **errp) { size_t cluster_size; cluster_size = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE, DEFAULT_CLUSTER_SIZE); - if (!validate_cluster_size(cluster_size, errp)) { + if (!validate_cluster_size(cluster_size, extended_l2, errp)) { return 0; } return cluster_size; @@ -3354,7 +3385,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) size_t cluster_size; int version; int refcount_order; - uint64_t* refcount_table; + uint64_t *refcount_table; int ret; uint8_t compression_type = QCOW2_COMPRESSION_TYPE_ZLIB; @@ -3395,7 +3426,20 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) cluster_size = DEFAULT_CLUSTER_SIZE; } - if (!validate_cluster_size(cluster_size, errp)) { + if (!qcow2_opts->has_extended_l2) { + qcow2_opts->extended_l2 = false; + } + if (qcow2_opts->extended_l2) { + if (version < 3) { + error_setg(errp, "Extended L2 entries are only supported with " + "compatibility level 1.1 and above (use version=v3 or " + "greater)"); + ret = -EINVAL; + goto out; + } + } + + if (!validate_cluster_size(cluster_size, qcow2_opts->extended_l2, errp)) { ret = -EINVAL; goto out; } @@ -3404,10 +3448,11 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) qcow2_opts->preallocation = PREALLOC_MODE_OFF; } if (qcow2_opts->has_backing_file && - qcow2_opts->preallocation != PREALLOC_MODE_OFF) + qcow2_opts->preallocation != PREALLOC_MODE_OFF && + !qcow2_opts->extended_l2) { - error_setg(errp, "Backing file and preallocation cannot be used at " - "the same time"); + error_setg(errp, "Backing file and preallocation can only be used at " + "the same time if extended_l2 is on"); ret = -EINVAL; goto out; } @@ -3458,6 +3503,28 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) ret = -EINVAL; goto out; } + if (qcow2_opts->data_file_raw && + qcow2_opts->preallocation == PREALLOC_MODE_OFF) + { + /* + * data-file-raw means that "the external data file can be + * read as a consistent standalone raw image without looking + * at the qcow2 metadata." It does not say that the metadata + * must be ignored, though (and the qcow2 driver in fact does + * not ignore it), so the L1/L2 tables must be present and + * give a 1:1 mapping, so you get the same result regardless + * of whether you look at the metadata or whether you ignore + * it. + */ + qcow2_opts->preallocation = PREALLOC_MODE_METADATA; + + /* + * Cannot use preallocation with backing files, but giving a + * backing file when specifying data_file_raw is an error + * anyway. + */ + assert(!qcow2_opts->has_backing_file); + } if (qcow2_opts->data_file) { if (version < 3) { @@ -3546,6 +3613,11 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) cpu_to_be64(QCOW2_INCOMPAT_COMPRESSION); } + if (qcow2_opts->extended_l2) { + header->incompatible_features |= + cpu_to_be64(QCOW2_INCOMPAT_EXTL2); + } + ret = blk_pwrite(blk, 0, header, cluster_size, 0); g_free(header); if (ret < 0) { @@ -3723,6 +3795,7 @@ static int coroutine_fn qcow2_co_create_opts(BlockDriver *drv, { BLOCK_OPT_BACKING_FMT, "backing-fmt" }, { BLOCK_OPT_CLUSTER_SIZE, "cluster-size" }, { BLOCK_OPT_LAZY_REFCOUNTS, "lazy-refcounts" }, + { BLOCK_OPT_EXTL2, "extended-l2" }, { BLOCK_OPT_REFCOUNT_BITS, "refcount-bits" }, { BLOCK_OPT_ENCRYPT, BLOCK_OPT_ENCRYPT_FORMAT }, { BLOCK_OPT_COMPAT_LEVEL, "version" }, @@ -3793,12 +3866,14 @@ static int coroutine_fn qcow2_co_create_opts(BlockDriver *drv, /* Create the qcow2 image (format layer) */ ret = qcow2_co_create(create_options, errp); +finish: if (ret < 0) { - goto finish; + bdrv_co_delete_file_noerr(bs); + bdrv_co_delete_file_noerr(data_bs); + } else { + ret = 0; } - ret = 0; -finish: qobject_unref(qdict); bdrv_unref(bs); bdrv_unref(data_bs); @@ -3820,8 +3895,20 @@ static bool is_zero(BlockDriverState *bs, int64_t offset, int64_t bytes) if (!bytes) { return true; } - res = bdrv_block_status_above(bs, NULL, offset, bytes, &nr, NULL, NULL); - return res >= 0 && (res & BDRV_BLOCK_ZERO) && nr == bytes; + + /* + * bdrv_block_status_above doesn't merge different types of zeros, for + * example, zeros which come from the region which is unallocated in + * the whole backing chain, and zeros which come because of a short + * backing file. So, we need a loop. + */ + do { + res = bdrv_block_status_above(bs, NULL, offset, bytes, &nr, NULL, NULL); + offset += nr; + bytes -= nr; + } while (res >= 0 && (res & BDRV_BLOCK_ZERO) && nr && bytes); + + return res >= 0 && (res & BDRV_BLOCK_ZERO) && bytes == 0; } static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs, @@ -3830,8 +3917,9 @@ static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs, int ret; BDRVQcow2State *s = bs->opaque; - uint32_t head = offset % s->cluster_size; - uint32_t tail = (offset + bytes) % s->cluster_size; + uint32_t head = offset_into_subcluster(s, offset); + uint32_t tail = ROUND_UP(offset + bytes, s->subcluster_size) - + (offset + bytes); trace_qcow2_pwrite_zeroes_start_req(qemu_coroutine_self(), offset, bytes); if (offset + bytes == bs->total_sectors * BDRV_SECTOR_SIZE) { @@ -3841,27 +3929,29 @@ static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs, if (head || tail) { uint64_t off; unsigned int nr; + QCow2SubclusterType type; - assert(head + bytes <= s->cluster_size); + assert(head + bytes + tail <= s->subcluster_size); /* check whether remainder of cluster already reads as zero */ if (!(is_zero(bs, offset - head, head) && - is_zero(bs, offset + bytes, - tail ? s->cluster_size - tail : 0))) { + is_zero(bs, offset + bytes, tail))) { return -ENOTSUP; } qemu_co_mutex_lock(&s->lock); /* We can have new write after previous check */ - offset = QEMU_ALIGN_DOWN(offset, s->cluster_size); - bytes = s->cluster_size; - nr = s->cluster_size; - ret = qcow2_get_cluster_offset(bs, offset, &nr, &off); - if (ret != QCOW2_CLUSTER_UNALLOCATED && - ret != QCOW2_CLUSTER_ZERO_PLAIN && - ret != QCOW2_CLUSTER_ZERO_ALLOC) { + offset -= head; + bytes = s->subcluster_size; + nr = s->subcluster_size; + ret = qcow2_get_host_offset(bs, offset, &nr, &off, &type); + if (ret < 0 || + (type != QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN && + type != QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC && + type != QCOW2_SUBCLUSTER_ZERO_PLAIN && + type != QCOW2_SUBCLUSTER_ZERO_ALLOC)) { qemu_co_mutex_unlock(&s->lock); - return -ENOTSUP; + return ret < 0 ? ret : -ENOTSUP; } } else { qemu_co_mutex_lock(&s->lock); @@ -3869,8 +3959,8 @@ static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs, trace_qcow2_pwrite_zeroes(qemu_coroutine_self(), offset, bytes); - /* Whatever is left can use real zero clusters */ - ret = qcow2_cluster_zeroize(bs, offset, bytes, flags); + /* Whatever is left can use real zero subclusters */ + ret = qcow2_subcluster_zeroize(bs, offset, bytes, flags); qemu_co_mutex_unlock(&s->lock); return ret; @@ -3923,17 +4013,20 @@ qcow2_co_copy_range_from(BlockDriverState *bs, while (bytes != 0) { uint64_t copy_offset = 0; + QCow2SubclusterType type; /* prepare next request */ cur_bytes = MIN(bytes, INT_MAX); cur_write_flags = write_flags; - ret = qcow2_get_cluster_offset(bs, src_offset, &cur_bytes, ©_offset); + ret = qcow2_get_host_offset(bs, src_offset, &cur_bytes, + ©_offset, &type); if (ret < 0) { goto out; } - switch (ret) { - case QCOW2_CLUSTER_UNALLOCATED: + switch (type) { + case QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN: + case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: if (bs->backing && bs->backing->bs) { int64_t backing_length = bdrv_getlength(bs->backing->bs); if (src_offset >= backing_length) { @@ -3948,18 +4041,17 @@ qcow2_co_copy_range_from(BlockDriverState *bs, } break; - case QCOW2_CLUSTER_ZERO_PLAIN: - case QCOW2_CLUSTER_ZERO_ALLOC: + case QCOW2_SUBCLUSTER_ZERO_PLAIN: + case QCOW2_SUBCLUSTER_ZERO_ALLOC: cur_write_flags |= BDRV_REQ_ZERO_WRITE; break; - case QCOW2_CLUSTER_COMPRESSED: + case QCOW2_SUBCLUSTER_COMPRESSED: ret = -ENOTSUP; goto out; - case QCOW2_CLUSTER_NORMAL: + case QCOW2_SUBCLUSTER_NORMAL: child = s->data_file; - copy_offset += offset_into_cluster(s, src_offset); break; default: @@ -3994,10 +4086,9 @@ qcow2_co_copy_range_to(BlockDriverState *bs, BdrvRequestFlags write_flags) { BDRVQcow2State *s = bs->opaque; - int offset_in_cluster; int ret; unsigned int cur_bytes; /* number of sectors in current iteration */ - uint64_t cluster_offset; + uint64_t host_offset; QCowL2Meta *l2meta = NULL; assert(!bs->encrypted); @@ -4008,31 +4099,26 @@ qcow2_co_copy_range_to(BlockDriverState *bs, l2meta = NULL; - offset_in_cluster = offset_into_cluster(s, dst_offset); cur_bytes = MIN(bytes, INT_MAX); /* TODO: * If src->bs == dst->bs, we could simply copy by incrementing * the refcnt, without copying user data. * Or if src->bs == dst->bs->backing->bs, we could copy by discarding. */ - ret = qcow2_alloc_cluster_offset(bs, dst_offset, &cur_bytes, - &cluster_offset, &l2meta); + ret = qcow2_alloc_host_offset(bs, dst_offset, &cur_bytes, + &host_offset, &l2meta); if (ret < 0) { goto fail; } - assert(offset_into_cluster(s, cluster_offset) == 0); - - ret = qcow2_pre_write_overlap_check(bs, 0, - cluster_offset + offset_in_cluster, cur_bytes, true); + ret = qcow2_pre_write_overlap_check(bs, 0, host_offset, cur_bytes, + true); if (ret < 0) { goto fail; } qemu_co_mutex_unlock(&s->lock); - ret = bdrv_co_copy_range_to(src, src_offset, - s->data_file, - cluster_offset + offset_in_cluster, + ret = bdrv_co_copy_range_to(src, src_offset, s->data_file, host_offset, cur_bytes, read_flags, write_flags); qemu_co_mutex_lock(&s->lock); if (ret < 0) { @@ -4174,6 +4260,18 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, error_setg_errno(errp, -ret, "Failed to grow the L1 table"); goto fail; } + + if (data_file_is_raw(bs) && prealloc == PREALLOC_MODE_OFF) { + /* + * When creating a qcow2 image with data-file-raw, we enforce + * at least prealloc=metadata, so that the L1/L2 tables are + * fully allocated and reading from the data file will return + * the same data as reading from the qcow2 image. When the + * image is grown, we must consequently preallocate the + * metadata structures to cover the added area. + */ + prealloc = PREALLOC_MODE_METADATA; + } } switch (prealloc) { @@ -4206,6 +4304,7 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, int64_t clusters_allocated; int64_t old_file_size, last_cluster, new_file_size; uint64_t nb_new_data_clusters, nb_new_l2_tables; + bool subclusters_need_allocation = false; /* With a data file, preallocation means just allocating the metadata * and forwarding the truncate request to the data file */ @@ -4246,7 +4345,7 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, * preallocation. All that matters is that we will not have to allocate * new refcount structures for them.) */ nb_new_l2_tables = DIV_ROUND_UP(nb_new_data_clusters, - s->cluster_size / sizeof(uint64_t)); + s->cluster_size / l2_entry_size(s)); /* The cluster range may not be aligned to L2 boundaries, so add one L2 * table for a potential head/tail */ nb_new_l2_tables++; @@ -4287,6 +4386,8 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, BDRV_REQ_ZERO_WRITE, NULL); if (ret >= 0) { flags &= ~BDRV_REQ_ZERO_WRITE; + /* Ensure that we read zeroes and not backing file data */ + subclusters_need_allocation = true; } } else { ret = -1; @@ -4325,6 +4426,7 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, .offset = nb_clusters << s->cluster_bits, .nb_bytes = 0, }, + .prealloc = !subclusters_need_allocation, }; qemu_co_queue_init(&allocation.dependent_requests); @@ -4349,15 +4451,16 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, } if ((flags & BDRV_REQ_ZERO_WRITE) && offset > old_length) { - uint64_t zero_start = QEMU_ALIGN_UP(old_length, s->cluster_size); + uint64_t zero_start = QEMU_ALIGN_UP(old_length, s->subcluster_size); /* - * Use zero clusters as much as we can. qcow2_cluster_zeroize() - * requires a cluster-aligned start. The end may be unaligned if it is - * at the end of the image (which it is here). + * Use zero clusters as much as we can. qcow2_subcluster_zeroize() + * requires a subcluster-aligned start. The end may be unaligned if + * it is at the end of the image (which it is here). */ if (offset > zero_start) { - ret = qcow2_cluster_zeroize(bs, zero_start, offset - zero_start, 0); + ret = qcow2_subcluster_zeroize(bs, zero_start, offset - zero_start, + 0); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to zero out new clusters"); goto fail; @@ -4398,7 +4501,7 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, /* write updated header.size */ offset = cpu_to_be64(offset); ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, size), - &offset, sizeof(uint64_t)); + &offset, sizeof(offset)); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to update the image size"); goto fail; @@ -4487,7 +4590,7 @@ static coroutine_fn int qcow2_co_pwritev_compressed_task_entry(AioTask *task) { Qcow2AioTask *t = container_of(task, Qcow2AioTask, task); - assert(!t->cluster_type && !t->l2meta); + assert(!t->subcluster_type && !t->l2meta); return qcow2_co_pwritev_compressed_task(t->bs, t->offset, t->bytes, t->qiov, t->qiov_offset); @@ -4562,7 +4665,7 @@ qcow2_co_pwritev_compressed_part(BlockDriverState *bs, static int coroutine_fn qcow2_co_preadv_compressed(BlockDriverState *bs, - uint64_t file_cluster_offset, + uint64_t cluster_descriptor, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, @@ -4574,8 +4677,8 @@ qcow2_co_preadv_compressed(BlockDriverState *bs, uint8_t *buf, *out_buf; int offset_in_cluster = offset_into_cluster(s, offset); - coffset = file_cluster_offset & s->cluster_offset_mask; - nb_csectors = ((file_cluster_offset >> s->csize_shift) & s->csize_mask) + 1; + coffset = cluster_descriptor & s->cluster_offset_mask; + nb_csectors = ((cluster_descriptor >> s->csize_shift) & s->csize_mask) + 1; csize = nb_csectors * QCOW2_COMPRESSED_SECTOR_SIZE - (coffset & ~QCOW2_COMPRESSED_SECTOR_MASK); @@ -4638,8 +4741,8 @@ static int make_completely_empty(BlockDriverState *bs) BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); - l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / sizeof(uint64_t)); - l1_size2 = (uint64_t)s->l1_size * sizeof(uint64_t); + l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / L1E_SIZE); + l1_size2 = (uint64_t)s->l1_size * L1E_SIZE; /* After this call, neither the in-memory nor the on-disk refcount * information accurately describe the actual references */ @@ -4685,14 +4788,14 @@ static int make_completely_empty(BlockDriverState *bs) s->l1_table_offset = 3 * s->cluster_size; - new_reftable = g_try_new0(uint64_t, s->cluster_size / sizeof(uint64_t)); + new_reftable = g_try_new0(uint64_t, s->cluster_size / REFTABLE_ENTRY_SIZE); if (!new_reftable) { ret = -ENOMEM; goto fail_broken_refcounts; } s->refcount_table_offset = s->cluster_size; - s->refcount_table_size = s->cluster_size / sizeof(uint64_t); + s->refcount_table_size = s->cluster_size / REFTABLE_ENTRY_SIZE; s->max_refcount_table_index = 0; g_free(s->refcount_table); @@ -4764,7 +4867,7 @@ static int qcow2_make_empty(BlockDriverState *bs) int step = QEMU_ALIGN_DOWN(INT_MAX, s->cluster_size); int l1_clusters, ret = 0; - l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / sizeof(uint64_t)); + l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / L1E_SIZE); if (s->qcow_version >= 3 && !s->snapshots && !s->nb_bitmaps && 3 + l1_clusters <= s->refcount_block_size && @@ -4829,9 +4932,14 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, PreallocMode prealloc; bool has_backing_file; bool has_luks; + bool extended_l2; + size_t l2e_size; /* Parse image creation options */ - cluster_size = qcow2_opt_get_cluster_size_del(opts, &local_err); + extended_l2 = qemu_opt_get_bool_del(opts, BLOCK_OPT_EXTL2, false); + + cluster_size = qcow2_opt_get_cluster_size_del(opts, extended_l2, + &local_err); if (local_err) { goto err; } @@ -4887,9 +4995,10 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, virtual_size = ROUND_UP(virtual_size, cluster_size); /* Check that virtual disk size is valid */ + l2e_size = extended_l2 ? L2E_SIZE_EXTENDED : L2E_SIZE_NORMAL; l2_tables = DIV_ROUND_UP(virtual_size / cluster_size, - cluster_size / sizeof(uint64_t)); - if (l2_tables * sizeof(uint64_t) > QCOW_MAX_L1_SIZE) { + cluster_size / l2e_size); + if (l2_tables * L1E_SIZE > QCOW_MAX_L1_SIZE) { error_setg(&local_err, "The image size is too large " "(try using a larger cluster size)"); goto err; @@ -4951,9 +5060,9 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, } info = g_new0(BlockMeasureInfo, 1); - info->fully_allocated = + info->fully_allocated = luks_payload_size + qcow2_calc_prealloc_size(virtual_size, cluster_size, - ctz32(refcount_bits)) + luks_payload_size; + ctz32(refcount_bits), extended_l2); /* * Remove data clusters that are not required. This overestimates the @@ -4989,12 +5098,10 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs, BDRVQcow2State *s = bs->opaque; ImageInfoSpecific *spec_info; QCryptoBlockInfo *encrypt_info = NULL; - Error *local_err = NULL; if (s->crypto != NULL) { - encrypt_info = qcrypto_block_get_info(s->crypto, &local_err); - if (local_err) { - error_propagate(errp, local_err); + encrypt_info = qcrypto_block_get_info(s->crypto, errp); + if (!encrypt_info) { return NULL; } } @@ -5011,9 +5118,7 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs, }; } else if (s->qcow_version == 3) { Qcow2BitmapInfoList *bitmaps; - bitmaps = qcow2_get_bitmap_info_list(bs, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!qcow2_get_bitmap_info_list(bs, &bitmaps, errp)) { qapi_free_ImageInfoSpecific(spec_info); qapi_free_QCryptoBlockInfo(encrypt_info); return NULL; @@ -5026,6 +5131,8 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs, .corrupt = s->incompatible_features & QCOW2_INCOMPAT_CORRUPT, .has_corrupt = true, + .has_extended_l2 = true, + .extended_l2 = has_subclusters(s), .refcount_bits = s->refcount_bits, .has_bitmaps = !!bitmaps, .bitmaps = bitmaps, @@ -5753,6 +5860,12 @@ static QemuOptsList qcow2_create_opts = { .help = "qcow2 cluster size", \ .def_value_str = stringify(DEFAULT_CLUSTER_SIZE) \ }, \ + { \ + .name = BLOCK_OPT_EXTL2, \ + .type = QEMU_OPT_BOOL, \ + .help = "Extended L2 tables", \ + .def_value_str = "off" \ + }, \ { \ .name = BLOCK_OPT_PREALLOC, \ .type = QEMU_OPT_STRING, \ diff --git a/block/qcow2.h b/block/qcow2.h index 7ce2c23bdb..0fe5f74ed3 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -78,6 +78,33 @@ /* The cluster reads as all zeros */ #define QCOW_OFLAG_ZERO (1ULL << 0) +#define QCOW_EXTL2_SUBCLUSTERS_PER_CLUSTER 32 + +/* The subcluster X [0..31] is allocated */ +#define QCOW_OFLAG_SUB_ALLOC(X) (1ULL << (X)) +/* The subcluster X [0..31] reads as zeroes */ +#define QCOW_OFLAG_SUB_ZERO(X) (QCOW_OFLAG_SUB_ALLOC(X) << 32) +/* Subclusters [X, Y) (0 <= X <= Y <= 32) are allocated */ +#define QCOW_OFLAG_SUB_ALLOC_RANGE(X, Y) \ + (QCOW_OFLAG_SUB_ALLOC(Y) - QCOW_OFLAG_SUB_ALLOC(X)) +/* Subclusters [X, Y) (0 <= X <= Y <= 32) read as zeroes */ +#define QCOW_OFLAG_SUB_ZERO_RANGE(X, Y) \ + (QCOW_OFLAG_SUB_ALLOC_RANGE(X, Y) << 32) +/* L2 entry bitmap with all allocation bits set */ +#define QCOW_L2_BITMAP_ALL_ALLOC (QCOW_OFLAG_SUB_ALLOC_RANGE(0, 32)) +/* L2 entry bitmap with all "read as zeroes" bits set */ +#define QCOW_L2_BITMAP_ALL_ZEROES (QCOW_OFLAG_SUB_ZERO_RANGE(0, 32)) + +/* Size of normal and extended L2 entries */ +#define L2E_SIZE_NORMAL (sizeof(uint64_t)) +#define L2E_SIZE_EXTENDED (sizeof(uint64_t) * 2) + +/* Size of L1 table entries */ +#define L1E_SIZE (sizeof(uint64_t)) + +/* Size of reftable entries */ +#define REFTABLE_ENTRY_SIZE (sizeof(uint64_t)) + #define MIN_CLUSTER_BITS 9 #define MAX_CLUSTER_BITS 21 @@ -179,6 +206,7 @@ typedef struct QEMU_PACKED QCowSnapshotHeader { typedef struct QEMU_PACKED QCowSnapshotExtraData { uint64_t vm_state_size_large; uint64_t disk_size; + uint64_t icount; } QCowSnapshotExtraData; @@ -192,6 +220,8 @@ typedef struct QCowSnapshot { uint32_t date_sec; uint32_t date_nsec; uint64_t vm_clock_nsec; + /* icount value for the moment when snapshot was taken */ + uint64_t icount; /* Size of all extra data, including QCowSnapshotExtraData if available */ uint32_t extra_data_size; /* Data beyond QCowSnapshotExtraData, if any */ @@ -225,15 +255,18 @@ enum { QCOW2_INCOMPAT_CORRUPT_BITNR = 1, QCOW2_INCOMPAT_DATA_FILE_BITNR = 2, QCOW2_INCOMPAT_COMPRESSION_BITNR = 3, + QCOW2_INCOMPAT_EXTL2_BITNR = 4, QCOW2_INCOMPAT_DIRTY = 1 << QCOW2_INCOMPAT_DIRTY_BITNR, QCOW2_INCOMPAT_CORRUPT = 1 << QCOW2_INCOMPAT_CORRUPT_BITNR, QCOW2_INCOMPAT_DATA_FILE = 1 << QCOW2_INCOMPAT_DATA_FILE_BITNR, QCOW2_INCOMPAT_COMPRESSION = 1 << QCOW2_INCOMPAT_COMPRESSION_BITNR, + QCOW2_INCOMPAT_EXTL2 = 1 << QCOW2_INCOMPAT_EXTL2_BITNR, QCOW2_INCOMPAT_MASK = QCOW2_INCOMPAT_DIRTY | QCOW2_INCOMPAT_CORRUPT | QCOW2_INCOMPAT_DATA_FILE - | QCOW2_INCOMPAT_COMPRESSION, + | QCOW2_INCOMPAT_COMPRESSION + | QCOW2_INCOMPAT_EXTL2, }; /* Compatible feature bits */ @@ -295,6 +328,9 @@ typedef struct BDRVQcow2State { int cluster_bits; int cluster_size; int l2_slice_size; + int subcluster_bits; + int subcluster_size; + int subclusters_per_cluster; int l2_bits; int l2_size; int l1_size; @@ -307,8 +343,8 @@ typedef struct BDRVQcow2State { uint64_t l1_table_offset; uint64_t *l1_table; - Qcow2Cache* l2_table_cache; - Qcow2Cache* refcount_block_cache; + Qcow2Cache *l2_table_cache; + Qcow2Cache *refcount_block_cache; QEMUTimer *cache_clean_timer; unsigned cache_clean_interval; @@ -358,7 +394,7 @@ typedef struct BDRVQcow2State { uint64_t autoclear_features; size_t unknown_header_fields_size; - void* unknown_header_fields; + void *unknown_header_fields; QLIST_HEAD(, Qcow2UnknownHeaderExtension) unknown_header_ext; QTAILQ_HEAD (, Qcow2DiscardRegion) discards; bool cache_discards; @@ -399,17 +435,18 @@ typedef struct Qcow2COWRegion { /** * Describes an in-flight (part of a) write request that writes to clusters - * that are not referenced in their L2 table yet. + * that need to have their L2 table entries updated (because they are + * newly allocated or need changes in their L2 bitmaps) */ typedef struct QCowL2Meta { - /** Guest offset of the first newly allocated cluster */ + /** Guest offset of the first updated cluster */ uint64_t offset; - /** Host offset of the first newly allocated cluster */ + /** Host offset of the first updated cluster */ uint64_t alloc_offset; - /** Number of newly allocated clusters */ + /** Number of updated clusters */ int nb_clusters; /** Do not free the old clusters */ @@ -422,14 +459,16 @@ typedef struct QCowL2Meta CoQueue dependent_requests; /** - * The COW Region between the start of the first allocated cluster and the - * area the guest actually writes to. + * The COW Region immediately before the area the guest actually + * writes to. This (part of the) write request starts at + * cow_start.offset + cow_start.nb_bytes. */ Qcow2COWRegion cow_start; /** - * The COW Region between the area the guest actually writes to and the - * end of the last allocated cluster. + * The COW Region immediately after the area the guest actually + * writes to. This (part of the) write request ends at cow_end.offset + * (which must always be set even when cow_end.nb_bytes is 0). */ Qcow2COWRegion cow_end; @@ -439,6 +478,14 @@ typedef struct QCowL2Meta */ bool skip_cow; + /** + * Indicates that this is not a normal write request but a preallocation. + * If the image has extended L2 entries this means that no new individual + * subclusters will be marked as allocated in the L2 bitmap (but any + * existing contents of that bitmap will be kept). + */ + bool prealloc; + /** * The I/O vector with the data from the actual guest write request. * If non-NULL, this is meant to be merged together with the data @@ -453,6 +500,33 @@ typedef struct QCowL2Meta QLIST_ENTRY(QCowL2Meta) next_in_flight; } QCowL2Meta; +/* + * In images with standard L2 entries all clusters are treated as if + * they had one subcluster so QCow2ClusterType and QCow2SubclusterType + * can be mapped to each other and have the exact same meaning + * (QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC cannot happen in these images). + * + * In images with extended L2 entries QCow2ClusterType refers to the + * complete cluster and QCow2SubclusterType to each of the individual + * subclusters, so there are several possible combinations: + * + * |--------------+---------------------------| + * | Cluster type | Possible subcluster types | + * |--------------+---------------------------| + * | UNALLOCATED | UNALLOCATED_PLAIN | + * | | ZERO_PLAIN | + * |--------------+---------------------------| + * | NORMAL | UNALLOCATED_ALLOC | + * | | ZERO_ALLOC | + * | | NORMAL | + * |--------------+---------------------------| + * | COMPRESSED | COMPRESSED | + * |--------------+---------------------------| + * + * QCOW2_SUBCLUSTER_INVALID means that the L2 entry is incorrect and + * the image should be marked corrupt. + */ + typedef enum QCow2ClusterType { QCOW2_CLUSTER_UNALLOCATED, QCOW2_CLUSTER_ZERO_PLAIN, @@ -461,6 +535,16 @@ typedef enum QCow2ClusterType { QCOW2_CLUSTER_COMPRESSED, } QCow2ClusterType; +typedef enum QCow2SubclusterType { + QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN, + QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC, + QCOW2_SUBCLUSTER_ZERO_PLAIN, + QCOW2_SUBCLUSTER_ZERO_ALLOC, + QCOW2_SUBCLUSTER_NORMAL, + QCOW2_SUBCLUSTER_COMPRESSED, + QCOW2_SUBCLUSTER_INVALID, +} QCow2SubclusterType; + typedef enum QCow2MetadataOverlap { QCOW2_OL_MAIN_HEADER_BITNR = 0, QCOW2_OL_ACTIVE_L1_BITNR = 1, @@ -510,6 +594,49 @@ typedef enum QCow2MetadataOverlap { #define INV_OFFSET (-1ULL) +static inline bool has_subclusters(BDRVQcow2State *s) +{ + return s->incompatible_features & QCOW2_INCOMPAT_EXTL2; +} + +static inline size_t l2_entry_size(BDRVQcow2State *s) +{ + return has_subclusters(s) ? L2E_SIZE_EXTENDED : L2E_SIZE_NORMAL; +} + +static inline uint64_t get_l2_entry(BDRVQcow2State *s, uint64_t *l2_slice, + int idx) +{ + idx *= l2_entry_size(s) / sizeof(uint64_t); + return be64_to_cpu(l2_slice[idx]); +} + +static inline uint64_t get_l2_bitmap(BDRVQcow2State *s, uint64_t *l2_slice, + int idx) +{ + if (has_subclusters(s)) { + idx *= l2_entry_size(s) / sizeof(uint64_t); + return be64_to_cpu(l2_slice[idx + 1]); + } else { + return 0; /* For convenience only; this value has no meaning. */ + } +} + +static inline void set_l2_entry(BDRVQcow2State *s, uint64_t *l2_slice, + int idx, uint64_t entry) +{ + idx *= l2_entry_size(s) / sizeof(uint64_t); + l2_slice[idx] = cpu_to_be64(entry); +} + +static inline void set_l2_bitmap(BDRVQcow2State *s, uint64_t *l2_slice, + int idx, uint64_t bitmap) +{ + assert(has_subclusters(s)); + idx *= l2_entry_size(s) / sizeof(uint64_t); + l2_slice[idx + 1] = cpu_to_be64(bitmap); +} + static inline bool has_data_file(BlockDriverState *bs) { BDRVQcow2State *s = bs->opaque; @@ -532,11 +659,21 @@ static inline int64_t offset_into_cluster(BDRVQcow2State *s, int64_t offset) return offset & (s->cluster_size - 1); } +static inline int64_t offset_into_subcluster(BDRVQcow2State *s, int64_t offset) +{ + return offset & (s->subcluster_size - 1); +} + static inline uint64_t size_to_clusters(BDRVQcow2State *s, uint64_t size) { return (size + (s->cluster_size - 1)) >> s->cluster_bits; } +static inline uint64_t size_to_subclusters(BDRVQcow2State *s, uint64_t size) +{ + return (size + (s->subcluster_size - 1)) >> s->subcluster_bits; +} + static inline int64_t size_to_l1(BDRVQcow2State *s, int64_t size) { int shift = s->cluster_bits + s->l2_bits; @@ -558,6 +695,11 @@ static inline int offset_to_l2_slice_index(BDRVQcow2State *s, int64_t offset) return (offset >> s->cluster_bits) & (s->l2_slice_size - 1); } +static inline int offset_to_sc_index(BDRVQcow2State *s, int64_t offset) +{ + return (offset >> s->subcluster_bits) & (s->subclusters_per_cluster - 1); +} + static inline int64_t qcow2_vm_state_offset(BDRVQcow2State *s) { return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits); @@ -566,9 +708,11 @@ static inline int64_t qcow2_vm_state_offset(BDRVQcow2State *s) static inline QCow2ClusterType qcow2_get_cluster_type(BlockDriverState *bs, uint64_t l2_entry) { + BDRVQcow2State *s = bs->opaque; + if (l2_entry & QCOW_OFLAG_COMPRESSED) { return QCOW2_CLUSTER_COMPRESSED; - } else if (l2_entry & QCOW_OFLAG_ZERO) { + } else if ((l2_entry & QCOW_OFLAG_ZERO) && !has_subclusters(s)) { if (l2_entry & L2E_OFFSET_MASK) { return QCOW2_CLUSTER_ZERO_ALLOC; } @@ -588,6 +732,72 @@ static inline QCow2ClusterType qcow2_get_cluster_type(BlockDriverState *bs, } } +/* + * In an image without subsclusters @l2_bitmap is ignored and + * @sc_index must be 0. + * Return QCOW2_SUBCLUSTER_INVALID if an invalid l2 entry is detected + * (this checks the whole entry and bitmap, not only the bits related + * to subcluster @sc_index). + */ +static inline +QCow2SubclusterType qcow2_get_subcluster_type(BlockDriverState *bs, + uint64_t l2_entry, + uint64_t l2_bitmap, + unsigned sc_index) +{ + BDRVQcow2State *s = bs->opaque; + QCow2ClusterType type = qcow2_get_cluster_type(bs, l2_entry); + assert(sc_index < s->subclusters_per_cluster); + + if (has_subclusters(s)) { + switch (type) { + case QCOW2_CLUSTER_COMPRESSED: + return QCOW2_SUBCLUSTER_COMPRESSED; + case QCOW2_CLUSTER_NORMAL: + if ((l2_bitmap >> 32) & l2_bitmap) { + return QCOW2_SUBCLUSTER_INVALID; + } else if (l2_bitmap & QCOW_OFLAG_SUB_ZERO(sc_index)) { + return QCOW2_SUBCLUSTER_ZERO_ALLOC; + } else if (l2_bitmap & QCOW_OFLAG_SUB_ALLOC(sc_index)) { + return QCOW2_SUBCLUSTER_NORMAL; + } else { + return QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC; + } + case QCOW2_CLUSTER_UNALLOCATED: + if (l2_bitmap & QCOW_L2_BITMAP_ALL_ALLOC) { + return QCOW2_SUBCLUSTER_INVALID; + } else if (l2_bitmap & QCOW_OFLAG_SUB_ZERO(sc_index)) { + return QCOW2_SUBCLUSTER_ZERO_PLAIN; + } else { + return QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN; + } + default: + g_assert_not_reached(); + } + } else { + switch (type) { + case QCOW2_CLUSTER_COMPRESSED: + return QCOW2_SUBCLUSTER_COMPRESSED; + case QCOW2_CLUSTER_ZERO_PLAIN: + return QCOW2_SUBCLUSTER_ZERO_PLAIN; + case QCOW2_CLUSTER_ZERO_ALLOC: + return QCOW2_SUBCLUSTER_ZERO_ALLOC; + case QCOW2_CLUSTER_NORMAL: + return QCOW2_SUBCLUSTER_NORMAL; + case QCOW2_CLUSTER_UNALLOCATED: + return QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN; + default: + g_assert_not_reached(); + } + } +} + +static inline bool qcow2_cluster_is_allocated(QCow2ClusterType type) +{ + return (type == QCOW2_CLUSTER_COMPRESSED || type == QCOW2_CLUSTER_NORMAL || + type == QCOW2_CLUSTER_ZERO_ALLOC); +} + /* Check whether refcounts are eager or lazy */ static inline bool qcow2_need_accurate_refcounts(BDRVQcow2State *s) { @@ -657,8 +867,8 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size); void qcow2_free_clusters(BlockDriverState *bs, int64_t offset, int64_t size, enum qcow2_discard_type type); -void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry, - int nb_clusters, enum qcow2_discard_type type); +void qcow2_free_any_cluster(BlockDriverState *bs, uint64_t l2_entry, + enum qcow2_discard_type type); int qcow2_update_snapshot_refcount(BlockDriverState *bs, int64_t l1_table_offset, int l1_size, int addend); @@ -694,11 +904,12 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index); int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num, uint8_t *buf, int nb_sectors, bool enc, Error **errp); -int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, - unsigned int *bytes, uint64_t *cluster_offset); -int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, - unsigned int *bytes, uint64_t *host_offset, - QCowL2Meta **m); +int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset, + unsigned int *bytes, uint64_t *host_offset, + QCow2SubclusterType *subcluster_type); +int qcow2_alloc_host_offset(BlockDriverState *bs, uint64_t offset, + unsigned int *bytes, uint64_t *host_offset, + QCowL2Meta **m); int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, uint64_t offset, int compressed_size, @@ -709,8 +920,8 @@ void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m); int qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset, uint64_t bytes, enum qcow2_discard_type type, bool full_discard); -int qcow2_cluster_zeroize(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, int flags); +int qcow2_subcluster_zeroize(BlockDriverState *bs, uint64_t offset, + uint64_t bytes, int flags); int qcow2_expand_zero_clusters(BlockDriverState *bs, BlockDriverAmendStatusCB *status_cb, @@ -767,12 +978,13 @@ void qcow2_cache_discard(Qcow2Cache *c, void *table); int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res, void **refcount_table, int64_t *refcount_table_size); -bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp); -Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs, - Error **errp); +bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, bool *header_updated, + Error **errp); +bool qcow2_get_bitmap_info_list(BlockDriverState *bs, + Qcow2BitmapInfoList **info_list, Error **errp); int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp); int qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp); -void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, +bool qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, bool release_stored, Error **errp); int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp); bool qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs, diff --git a/block/qed.c b/block/qed.c index b27e7546ca..f45c640513 100644 --- a/block/qed.c +++ b/block/qed.c @@ -393,6 +393,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, ret = bdrv_pread(bs->file, 0, &le_header, sizeof(le_header)); if (ret < 0) { + error_setg(errp, "Failed to read QED header"); return ret; } qed_header_le_to_cpu(&le_header, &s->header); @@ -408,25 +409,30 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, return -ENOTSUP; } if (!qed_is_cluster_size_valid(s->header.cluster_size)) { + error_setg(errp, "QED cluster size is invalid"); return -EINVAL; } /* Round down file size to the last cluster */ file_size = bdrv_getlength(bs->file->bs); if (file_size < 0) { + error_setg(errp, "Failed to get file length"); return file_size; } s->file_size = qed_start_of_cluster(s, file_size); if (!qed_is_table_size_valid(s->header.table_size)) { + error_setg(errp, "QED table size is invalid"); return -EINVAL; } if (!qed_is_image_size_valid(s->header.image_size, s->header.cluster_size, s->header.table_size)) { + error_setg(errp, "QED image size is invalid"); return -EINVAL; } if (!qed_check_table_offset(s, s->header.l1_table_offset)) { + error_setg(errp, "QED table offset is invalid"); return -EINVAL; } @@ -438,6 +444,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, /* Header size calculation must not overflow uint32_t */ if (s->header.header_size > UINT32_MAX / s->header.cluster_size) { + error_setg(errp, "QED header size is too large"); return -EINVAL; } @@ -445,6 +452,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, if ((uint64_t)s->header.backing_filename_offset + s->header.backing_filename_size > s->header.cluster_size * s->header.header_size) { + error_setg(errp, "QED backing filename offset is invalid"); return -EINVAL; } @@ -453,6 +461,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, bs->auto_backing_file, sizeof(bs->auto_backing_file)); if (ret < 0) { + error_setg(errp, "Failed to read backing filename"); return ret; } pstrcpy(bs->backing_file, sizeof(bs->backing_file), @@ -475,6 +484,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, ret = qed_write_header_sync(s); if (ret) { + error_setg(errp, "Failed to update header"); return ret; } @@ -487,6 +497,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, ret = qed_read_l1_table_sync(s); if (ret) { + error_setg(errp, "Failed to read L1 table"); goto out; } @@ -503,6 +514,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, ret = qed_check(s, &result, true); if (ret) { + error_setg(errp, "Image corrupted"); goto out; } } @@ -1537,22 +1549,16 @@ static void coroutine_fn bdrv_qed_co_invalidate_cache(BlockDriverState *bs, Error **errp) { BDRVQEDState *s = bs->opaque; - Error *local_err = NULL; int ret; bdrv_qed_close(bs); bdrv_qed_init_state(bs); qemu_co_mutex_lock(&s->table_lock); - ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, &local_err); + ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, errp); qemu_co_mutex_unlock(&s->table_lock); - if (local_err) { - error_propagate_prepend(errp, local_err, - "Could not reopen qed layer: "); - return; - } else if (ret < 0) { - error_setg_errno(errp, -ret, "Could not reopen qed layer"); - return; + if (ret < 0) { + error_prepend(errp, "Could not reopen qed layer: "); } } diff --git a/block/quorum.c b/block/quorum.c index 6df9449fc2..cfc1436abb 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -18,6 +18,7 @@ #include "qemu/module.h" #include "qemu/option.h" #include "block/block_int.h" +#include "block/coroutines.h" #include "block/qdict.h" #include "qapi/error.h" #include "qapi/qapi-events-block.h" @@ -29,6 +30,8 @@ #define HASH_LENGTH 32 +#define INDEXSTR_LEN 32 + #define QUORUM_OPT_VOTE_THRESHOLD "vote-threshold" #define QUORUM_OPT_BLKVERIFY "blkverify" #define QUORUM_OPT_REWRITE "rewrite-corrupted" @@ -689,8 +692,13 @@ static void write_quorum_entry(void *opaque) QuorumChildRequest *sacb = &acb->qcrs[i]; sacb->bs = s->children[i]->bs; - sacb->ret = bdrv_co_pwritev(s->children[i], acb->offset, acb->bytes, - acb->qiov, acb->flags); + if (acb->flags & BDRV_REQ_ZERO_WRITE) { + sacb->ret = bdrv_co_pwrite_zeroes(s->children[i], acb->offset, + acb->bytes, acb->flags); + } else { + sacb->ret = bdrv_co_pwritev(s->children[i], acb->offset, acb->bytes, + acb->qiov, acb->flags); + } if (sacb->ret == 0) { acb->success_count++; } else { @@ -736,6 +744,14 @@ static int quorum_co_pwritev(BlockDriverState *bs, uint64_t offset, return ret; } +static int quorum_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, + int bytes, BdrvRequestFlags flags) + +{ + return quorum_co_pwritev(bs, offset, bytes, NULL, + flags | BDRV_REQ_ZERO_WRITE); +} + static int64_t quorum_getlength(BlockDriverState *bs) { BDRVQuorumState *s = bs->opaque; @@ -854,7 +870,7 @@ static int quorum_valid_threshold(int threshold, int num_children, Error **errp) if (threshold < 1) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "vote-threshold", "value >= 1"); + "vote-threshold", "a value >= 1"); return -ERANGE; } @@ -894,11 +910,25 @@ static QemuOptsList quorum_runtime_opts = { }, }; +static void quorum_refresh_flags(BlockDriverState *bs) +{ + BDRVQuorumState *s = bs->opaque; + int i; + + bs->supported_zero_flags = + BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK; + + for (i = 0; i < s->num_children; i++) { + bs->supported_zero_flags &= s->children[i]->bs->supported_zero_flags; + } + + bs->supported_zero_flags |= BDRV_REQ_WRITE_UNCHANGED; +} + static int quorum_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { BDRVQuorumState *s = bs->opaque; - Error *local_err = NULL; QemuOpts *opts = NULL; const char *pattern_str; bool *opened; @@ -970,15 +1000,14 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, opened = g_new0(bool, s->num_children); for (i = 0; i < s->num_children; i++) { - char indexstr[32]; - ret = snprintf(indexstr, 32, "children.%d", i); - assert(ret < 32); + char indexstr[INDEXSTR_LEN]; + ret = snprintf(indexstr, INDEXSTR_LEN, "children.%d", i); + assert(ret < INDEXSTR_LEN); s->children[i] = bdrv_open_child(NULL, options, indexstr, bs, &child_of_bds, BDRV_CHILD_DATA, false, - &local_err); - if (local_err) { - error_propagate(errp, local_err); + errp); + if (!s->children[i]) { ret = -EINVAL; goto close_exit; } @@ -988,6 +1017,7 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, s->next_child_index = s->num_children; bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; + quorum_refresh_flags(bs); g_free(opened); goto exit; @@ -1024,7 +1054,7 @@ static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs, { BDRVQuorumState *s = bs->opaque; BdrvChild *child; - char indexstr[32]; + char indexstr[INDEXSTR_LEN]; int ret; if (s->is_blkverify) { @@ -1039,8 +1069,8 @@ static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs, return; } - ret = snprintf(indexstr, 32, "children.%u", s->next_child_index); - if (ret < 0 || ret >= 32) { + ret = snprintf(indexstr, INDEXSTR_LEN, "children.%u", s->next_child_index); + if (ret < 0 || ret >= INDEXSTR_LEN) { error_setg(errp, "cannot generate child name"); return; } @@ -1059,6 +1089,7 @@ static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs, } s->children = g_renew(BdrvChild *, s->children, s->num_children + 1); s->children[s->num_children++] = child; + quorum_refresh_flags(bs); out: bdrv_drained_end(bs); @@ -1068,6 +1099,7 @@ static void quorum_del_child(BlockDriverState *bs, BdrvChild *child, Error **errp) { BDRVQuorumState *s = bs->opaque; + char indexstr[INDEXSTR_LEN]; int i; for (i = 0; i < s->num_children; i++) { @@ -1089,6 +1121,11 @@ static void quorum_del_child(BlockDriverState *bs, BdrvChild *child, /* We know now that num_children > threshold, so blkverify must be false */ assert(!s->is_blkverify); + snprintf(indexstr, INDEXSTR_LEN, "children.%u", s->next_child_index - 1); + if (!strncmp(child->name, indexstr, INDEXSTR_LEN)) { + s->next_child_index--; + } + bdrv_drained_begin(bs); /* We can safely remove this child now */ @@ -1097,6 +1134,7 @@ static void quorum_del_child(BlockDriverState *bs, BdrvChild *child, s->children = g_renew(BdrvChild *, s->children, --s->num_children); bdrv_unref_child(bs, child); + quorum_refresh_flags(bs); bdrv_drained_end(bs); } @@ -1155,7 +1193,12 @@ static void quorum_child_perm(BlockDriverState *bs, BdrvChild *c, uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared) { + BDRVQuorumState *s = bs->opaque; + *nperm = perm & DEFAULT_PERM_PASSTHROUGH; + if (s->rewrite_corrupted) { + *nperm |= BLK_PERM_WRITE; + } /* * We cannot share RESIZE or WRITE, as this would make the @@ -1166,6 +1209,56 @@ static void quorum_child_perm(BlockDriverState *bs, BdrvChild *c, | DEFAULT_PERM_UNCHANGED; } +/* + * Each one of the children can report different status flags even + * when they contain the same data, so what this function does is + * return BDRV_BLOCK_ZERO if *all* children agree that a certain + * region contains zeroes, and BDRV_BLOCK_DATA otherwise. + */ +static int coroutine_fn quorum_co_block_status(BlockDriverState *bs, + bool want_zero, + int64_t offset, int64_t count, + int64_t *pnum, int64_t *map, + BlockDriverState **file) +{ + BDRVQuorumState *s = bs->opaque; + int i, ret; + int64_t pnum_zero = count; + int64_t pnum_data = 0; + + for (i = 0; i < s->num_children; i++) { + int64_t bytes; + ret = bdrv_co_common_block_status_above(s->children[i]->bs, NULL, false, + want_zero, offset, count, + &bytes, NULL, NULL, NULL); + if (ret < 0) { + quorum_report_bad(QUORUM_OP_TYPE_READ, offset, count, + s->children[i]->bs->node_name, ret); + pnum_data = count; + break; + } + /* + * Even if all children agree about whether there are zeroes + * or not at @offset they might disagree on the size, so use + * the smallest when reporting BDRV_BLOCK_ZERO and the largest + * when reporting BDRV_BLOCK_DATA. + */ + if (ret & BDRV_BLOCK_ZERO) { + pnum_zero = MIN(pnum_zero, bytes); + } else { + pnum_data = MAX(pnum_data, bytes); + } + } + + if (pnum_data) { + *pnum = pnum_data; + return BDRV_BLOCK_DATA; + } else { + *pnum = pnum_zero; + return BDRV_BLOCK_ZERO; + } +} + static const char *const quorum_strong_runtime_opts[] = { QUORUM_OPT_VOTE_THRESHOLD, QUORUM_OPT_BLKVERIFY, @@ -1184,6 +1277,7 @@ static BlockDriver bdrv_quorum = { .bdrv_close = quorum_close, .bdrv_gather_child_options = quorum_gather_child_options, .bdrv_dirname = quorum_dirname, + .bdrv_co_block_status = quorum_co_block_status, .bdrv_co_flush_to_disk = quorum_co_flush, @@ -1191,6 +1285,7 @@ static BlockDriver bdrv_quorum = { .bdrv_co_preadv = quorum_co_preadv, .bdrv_co_pwritev = quorum_co_pwritev, + .bdrv_co_pwrite_zeroes = quorum_co_pwrite_zeroes, .bdrv_add_child = quorum_add_child, .bdrv_del_child = quorum_del_child, diff --git a/block/raw-format.c b/block/raw-format.c index 42ec50802b..7717578ed6 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -575,6 +575,11 @@ static const char *const raw_strong_runtime_opts[] = { NULL }; +static void raw_cancel_in_flight(BlockDriverState *bs) +{ + bdrv_cancel_in_flight(bs->file->bs); +} + BlockDriver bdrv_raw = { .format_name = "raw", .instance_size = sizeof(BDRVRawState), @@ -608,6 +613,7 @@ BlockDriver bdrv_raw = { .bdrv_has_zero_init = &raw_has_zero_init, .strong_runtime_opts = raw_strong_runtime_opts, .mutable_opts = mutable_opts, + .bdrv_cancel_in_flight = raw_cancel_in_flight, }; static void bdrv_raw_init(void) diff --git a/block/rbd.c b/block/rbd.c index 688074c64b..f098a89c7b 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -232,7 +232,7 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options, if (keypairs) { qdict_put(options, "=keyvalue-pairs", - qobject_to_json(QOBJECT(keypairs))); + qstring_from_gstring(qobject_to_json(QOBJECT(keypairs)))); } done: @@ -341,48 +341,6 @@ static void qemu_rbd_memset(RADOSCB *rcb, int64_t offs) } } -static QemuOptsList runtime_opts = { - .name = "rbd", - .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), - .desc = { - { - .name = "pool", - .type = QEMU_OPT_STRING, - .help = "Rados pool name", - }, - { - .name = "namespace", - .type = QEMU_OPT_STRING, - .help = "Rados namespace name in the pool", - }, - { - .name = "image", - .type = QEMU_OPT_STRING, - .help = "Image name in the pool", - }, - { - .name = "conf", - .type = QEMU_OPT_STRING, - .help = "Rados config file location", - }, - { - .name = "snapshot", - .type = QEMU_OPT_STRING, - .help = "Ceph snapshot name", - }, - { - /* maps to 'id' in rados_create() */ - .name = "user", - .type = QEMU_OPT_STRING, - .help = "Rados id name", - }, - /* - * server.* extracted manually, see qemu_rbd_mon_host() - */ - { /* end of list */ } - }, -}; - /* FIXME Deprecate and remove keypairs or make it available in QMP. */ static int qemu_rbd_do_create(BlockdevCreateOptions *options, const char *keypairs, const char *password_secret, @@ -486,6 +444,7 @@ static int coroutine_fn qemu_rbd_co_create_opts(BlockDriver *drv, loc->user = g_strdup(qdict_get_try_str(options, "user")); loc->has_user = !!loc->user; loc->q_namespace = g_strdup(qdict_get_try_str(options, "namespace")); + loc->has_q_namespace = !!loc->q_namespace; loc->image = g_strdup(qdict_get_try_str(options, "image")); keypairs = qdict_get_try_str(options, "=keyvalue-pairs"); @@ -605,13 +564,13 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, if (local_err) { error_propagate(errp, local_err); r = -EINVAL; - goto failed_opts; + goto out; } r = rados_create(cluster, opts->user); if (r < 0) { error_setg_errno(errp, -r, "error initializing"); - goto failed_opts; + goto out; } /* try default location when conf=NULL, but ignore failure */ @@ -668,11 +627,12 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, */ rados_ioctx_set_namespace(*io_ctx, opts->q_namespace); - return 0; + r = 0; + goto out; failed_shutdown: rados_shutdown(*cluster); -failed_opts: +out: g_free(mon_host); return r; } @@ -1289,6 +1249,7 @@ static QemuOptsList qemu_rbd_create_opts = { static const char *const qemu_rbd_strong_runtime_opts[] = { "pool", + "namespace", "image", "conf", "snapshot", diff --git a/block/replication.c b/block/replication.c index 0c70215784..97be7ef4de 100644 --- a/block/replication.c +++ b/block/replication.c @@ -454,6 +454,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, int64_t active_length, hidden_length, disk_length; AioContext *aio_context; Error *local_err = NULL; + BackupPerf perf = { .use_copy_range = true, .max_workers = 1 }; aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); @@ -558,6 +559,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, s->backup_job = backup_job_create( NULL, s->secondary_disk->bs, s->hidden_disk->bs, 0, MIRROR_SYNC_MODE_NONE, NULL, 0, false, NULL, + &perf, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL, backup_job_completed, bs, NULL, &local_err); diff --git a/block/sheepdog.c b/block/sheepdog.c index cbbebc1aaf..a45c73826d 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -242,6 +242,16 @@ typedef struct SheepdogInode { */ #define FNV1A_64_INIT ((uint64_t)0xcbf29ce484222325ULL) +static void deprecation_warning(void) +{ + static bool warned; + + if (!warned) { + warn_report("the sheepdog block driver is deprecated"); + warned = true; + } +} + /* * 64 bit Fowler/Noll/Vo FNV-1a hash code */ @@ -665,7 +675,7 @@ out: srco->co = NULL; srco->ret = ret; /* Set srco->finished before reading bs->wakeup. */ - atomic_mb_set(&srco->finished, true); + qatomic_mb_set(&srco->finished, true); if (srco->bs) { bdrv_wakeup(srco->bs); } @@ -740,7 +750,7 @@ static coroutine_fn void reconnect_to_sdog(void *opaque) if (s->fd < 0) { trace_sheepdog_reconnect_to_sdog(); error_report_err(local_err); - qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000000ULL); + qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, NANOSECONDS_PER_SECOND); } }; @@ -1548,6 +1558,8 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags, char *buf = NULL; QemuOpts *opts; + deprecation_warning(); + s->bs = bs; s->aio_context = bdrv_get_aio_context(bs); @@ -2007,6 +2019,8 @@ static int sd_co_create(BlockdevCreateOptions *options, Error **errp) assert(options->driver == BLOCKDEV_DRIVER_SHEEPDOG); + deprecation_warning(); + s = g_new0(BDRVSheepdogState, 1); /* Steal SocketAddress from QAPI, set NULL to prevent double free */ diff --git a/block/snapshot.c b/block/snapshot.c index bd9fb01817..e8ae9a28c1 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -147,6 +147,56 @@ bool bdrv_snapshot_find_by_id_and_name(BlockDriverState *bs, return ret; } +/** + * Return a pointer to the child BDS pointer to which we can fall + * back if the given BDS does not support snapshots. + * Return NULL if there is no BDS to (safely) fall back to. + * + * We need to return an indirect pointer because bdrv_snapshot_goto() + * has to modify the BdrvChild pointer. + */ +static BdrvChild **bdrv_snapshot_fallback_ptr(BlockDriverState *bs) +{ + BdrvChild **fallback; + BdrvChild *child; + + /* + * The only BdrvChild pointers that are safe to modify (and which + * we can thus return a reference to) are bs->file and + * bs->backing. + */ + fallback = &bs->file; + if (!*fallback && bs->drv && bs->drv->is_filter) { + fallback = &bs->backing; + } + + if (!*fallback) { + return NULL; + } + + /* + * Check that there are no other children that would need to be + * snapshotted. If there are, it is not safe to fall back to + * *fallback. + */ + QLIST_FOREACH(child, &bs->children, next) { + if (child->role & (BDRV_CHILD_DATA | BDRV_CHILD_METADATA | + BDRV_CHILD_FILTERED) && + child != *fallback) + { + return NULL; + } + } + + return fallback; +} + +static BlockDriverState *bdrv_snapshot_fallback(BlockDriverState *bs) +{ + BdrvChild **child_ptr = bdrv_snapshot_fallback_ptr(bs); + return child_ptr ? (*child_ptr)->bs : NULL; +} + int bdrv_can_snapshot(BlockDriverState *bs) { BlockDriver *drv = bs->drv; @@ -155,8 +205,9 @@ int bdrv_can_snapshot(BlockDriverState *bs) } if (!drv->bdrv_snapshot_create) { - if (bs->file != NULL) { - return bdrv_can_snapshot(bs->file->bs); + BlockDriverState *fallback_bs = bdrv_snapshot_fallback(bs); + if (fallback_bs) { + return bdrv_can_snapshot(fallback_bs); } return 0; } @@ -168,14 +219,15 @@ int bdrv_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) { BlockDriver *drv = bs->drv; + BlockDriverState *fallback_bs = bdrv_snapshot_fallback(bs); if (!drv) { return -ENOMEDIUM; } if (drv->bdrv_snapshot_create) { return drv->bdrv_snapshot_create(bs, sn_info); } - if (bs->file) { - return bdrv_snapshot_create(bs->file->bs, sn_info); + if (fallback_bs) { + return bdrv_snapshot_create(fallback_bs, sn_info); } return -ENOTSUP; } @@ -185,6 +237,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs, Error **errp) { BlockDriver *drv = bs->drv; + BdrvChild **fallback_ptr; int ret, open_ret; if (!drv) { @@ -205,39 +258,46 @@ int bdrv_snapshot_goto(BlockDriverState *bs, return ret; } - if (bs->file) { - BlockDriverState *file; - QDict *options = qdict_clone_shallow(bs->options); + fallback_ptr = bdrv_snapshot_fallback_ptr(bs); + if (fallback_ptr) { + QDict *options; QDict *file_options; Error *local_err = NULL; + BlockDriverState *fallback_bs = (*fallback_ptr)->bs; + char *subqdict_prefix = g_strdup_printf("%s.", (*fallback_ptr)->name); + + options = qdict_clone_shallow(bs->options); - file = bs->file->bs; /* Prevent it from getting deleted when detached from bs */ - bdrv_ref(file); + bdrv_ref(fallback_bs); - qdict_extract_subqdict(options, &file_options, "file."); + qdict_extract_subqdict(options, &file_options, subqdict_prefix); qobject_unref(file_options); - qdict_put_str(options, "file", bdrv_get_node_name(file)); + g_free(subqdict_prefix); + + qdict_put_str(options, (*fallback_ptr)->name, + bdrv_get_node_name(fallback_bs)); if (drv->bdrv_close) { drv->bdrv_close(bs); } - bdrv_unref_child(bs, bs->file); - bs->file = NULL; - ret = bdrv_snapshot_goto(file, snapshot_id, errp); + bdrv_unref_child(bs, *fallback_ptr); + *fallback_ptr = NULL; + + ret = bdrv_snapshot_goto(fallback_bs, snapshot_id, errp); open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err); qobject_unref(options); if (open_ret < 0) { - bdrv_unref(file); + bdrv_unref(fallback_bs); bs->drv = NULL; /* A bdrv_snapshot_goto() error takes precedence */ error_propagate(errp, local_err); return ret < 0 ? ret : open_ret; } - assert(bs->file->bs == file); - bdrv_unref(file); + assert(fallback_bs == (*fallback_ptr)->bs); + bdrv_unref(fallback_bs); return ret; } @@ -273,6 +333,7 @@ int bdrv_snapshot_delete(BlockDriverState *bs, Error **errp) { BlockDriver *drv = bs->drv; + BlockDriverState *fallback_bs = bdrv_snapshot_fallback(bs); int ret; if (!drv) { @@ -289,8 +350,8 @@ int bdrv_snapshot_delete(BlockDriverState *bs, if (drv->bdrv_snapshot_delete) { ret = drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp); - } else if (bs->file) { - ret = bdrv_snapshot_delete(bs->file->bs, snapshot_id, name, errp); + } else if (fallback_bs) { + ret = bdrv_snapshot_delete(fallback_bs, snapshot_id, name, errp); } else { error_setg(errp, "Block format '%s' used by device '%s' " "does not support internal snapshot deletion", @@ -306,14 +367,15 @@ int bdrv_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_info) { BlockDriver *drv = bs->drv; + BlockDriverState *fallback_bs = bdrv_snapshot_fallback(bs); if (!drv) { return -ENOMEDIUM; } if (drv->bdrv_snapshot_list) { return drv->bdrv_snapshot_list(bs, psn_info); } - if (bs->file) { - return bdrv_snapshot_list(bs->file->bs, psn_info); + if (fallback_bs) { + return bdrv_snapshot_list(fallback_bs, psn_info); } return -ENOTSUP; } @@ -385,6 +447,41 @@ int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs, return ret; } + +static int bdrv_all_get_snapshot_devices(bool has_devices, strList *devices, + GList **all_bdrvs, + Error **errp) +{ + g_autoptr(GList) bdrvs = NULL; + + if (has_devices) { + if (!devices) { + error_setg(errp, "At least one device is required for snapshot"); + return -1; + } + + while (devices) { + BlockDriverState *bs = bdrv_find_node(devices->value); + if (!bs) { + error_setg(errp, "No block device node '%s'", devices->value); + return -1; + } + bdrvs = g_list_append(bdrvs, bs); + devices = devices->next; + } + } else { + BlockDriverState *bs; + BdrvNextIterator it; + for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { + bdrvs = g_list_append(bdrvs, bs); + } + } + + *all_bdrvs = g_steal_pointer(&bdrvs); + return 0; +} + + static bool bdrv_all_snapshots_includes_bs(BlockDriverState *bs) { if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) { @@ -400,44 +497,59 @@ static bool bdrv_all_snapshots_includes_bs(BlockDriverState *bs) * These functions will properly handle dataplane (take aio_context_acquire * when appropriate for appropriate block drivers) */ -bool bdrv_all_can_snapshot(BlockDriverState **first_bad_bs) +bool bdrv_all_can_snapshot(bool has_devices, strList *devices, + Error **errp) { - bool ok = true; - BlockDriverState *bs; - BdrvNextIterator it; + g_autoptr(GList) bdrvs = NULL; + GList *iterbdrvs; - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { + if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { + return false; + } + + iterbdrvs = bdrvs; + while (iterbdrvs) { + BlockDriverState *bs = iterbdrvs->data; AioContext *ctx = bdrv_get_aio_context(bs); + bool ok = true; aio_context_acquire(ctx); - if (bdrv_all_snapshots_includes_bs(bs)) { + if (devices || bdrv_all_snapshots_includes_bs(bs)) { ok = bdrv_can_snapshot(bs); } aio_context_release(ctx); if (!ok) { - bdrv_next_cleanup(&it); - goto fail; + error_setg(errp, "Device '%s' is writable but does not support " + "snapshots", bdrv_get_device_or_node_name(bs)); + return false; } + + iterbdrvs = iterbdrvs->next; } -fail: - *first_bad_bs = bs; - return ok; + return true; } -int bdrv_all_delete_snapshot(const char *name, BlockDriverState **first_bad_bs, +int bdrv_all_delete_snapshot(const char *name, + bool has_devices, strList *devices, Error **errp) { - int ret = 0; - BlockDriverState *bs; - BdrvNextIterator it; - QEMUSnapshotInfo sn1, *snapshot = &sn1; + g_autoptr(GList) bdrvs = NULL; + GList *iterbdrvs; - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { + if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { + return -1; + } + + iterbdrvs = bdrvs; + while (iterbdrvs) { + BlockDriverState *bs = iterbdrvs->data; AioContext *ctx = bdrv_get_aio_context(bs); + QEMUSnapshotInfo sn1, *snapshot = &sn1; + int ret = 0; aio_context_acquire(ctx); - if (bdrv_all_snapshots_includes_bs(bs) && + if ((devices || bdrv_all_snapshots_includes_bs(bs)) && bdrv_snapshot_find(bs, snapshot, name) >= 0) { ret = bdrv_snapshot_delete(bs, snapshot->id_str, @@ -445,118 +557,180 @@ int bdrv_all_delete_snapshot(const char *name, BlockDriverState **first_bad_bs, } aio_context_release(ctx); if (ret < 0) { - bdrv_next_cleanup(&it); - goto fail; + error_prepend(errp, "Could not delete snapshot '%s' on '%s': ", + name, bdrv_get_device_or_node_name(bs)); + return -1; } + + iterbdrvs = iterbdrvs->next; } -fail: - *first_bad_bs = bs; - return ret; + return 0; } -int bdrv_all_goto_snapshot(const char *name, BlockDriverState **first_bad_bs, +int bdrv_all_goto_snapshot(const char *name, + bool has_devices, strList *devices, Error **errp) { - int ret = 0; - BlockDriverState *bs; - BdrvNextIterator it; + g_autoptr(GList) bdrvs = NULL; + GList *iterbdrvs; - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { + if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { + return -1; + } + + iterbdrvs = bdrvs; + while (iterbdrvs) { + BlockDriverState *bs = iterbdrvs->data; AioContext *ctx = bdrv_get_aio_context(bs); + int ret = 0; aio_context_acquire(ctx); - if (bdrv_all_snapshots_includes_bs(bs)) { + if (devices || bdrv_all_snapshots_includes_bs(bs)) { ret = bdrv_snapshot_goto(bs, name, errp); } aio_context_release(ctx); if (ret < 0) { - bdrv_next_cleanup(&it); - goto fail; + error_prepend(errp, "Could not load snapshot '%s' on '%s': ", + name, bdrv_get_device_or_node_name(bs)); + return -1; } + + iterbdrvs = iterbdrvs->next; } -fail: - *first_bad_bs = bs; - return ret; + return 0; } -int bdrv_all_find_snapshot(const char *name, BlockDriverState **first_bad_bs) +int bdrv_all_has_snapshot(const char *name, + bool has_devices, strList *devices, + Error **errp) { - QEMUSnapshotInfo sn; - int err = 0; - BlockDriverState *bs; - BdrvNextIterator it; + g_autoptr(GList) bdrvs = NULL; + GList *iterbdrvs; - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { - AioContext *ctx = bdrv_get_aio_context(bs); - - aio_context_acquire(ctx); - if (bdrv_all_snapshots_includes_bs(bs)) { - err = bdrv_snapshot_find(bs, &sn, name); - } - aio_context_release(ctx); - if (err < 0) { - bdrv_next_cleanup(&it); - goto fail; - } + if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { + return -1; } -fail: - *first_bad_bs = bs; - return err; + iterbdrvs = bdrvs; + while (iterbdrvs) { + BlockDriverState *bs = iterbdrvs->data; + AioContext *ctx = bdrv_get_aio_context(bs); + QEMUSnapshotInfo sn; + int ret = 0; + + aio_context_acquire(ctx); + if (devices || bdrv_all_snapshots_includes_bs(bs)) { + ret = bdrv_snapshot_find(bs, &sn, name); + } + aio_context_release(ctx); + if (ret < 0) { + if (ret == -ENOENT) { + return 0; + } else { + error_setg_errno(errp, errno, + "Could not check snapshot '%s' on '%s'", + name, bdrv_get_device_or_node_name(bs)); + return -1; + } + } + + iterbdrvs = iterbdrvs->next; + } + + return 1; } int bdrv_all_create_snapshot(QEMUSnapshotInfo *sn, BlockDriverState *vm_state_bs, uint64_t vm_state_size, - BlockDriverState **first_bad_bs) + bool has_devices, strList *devices, + Error **errp) { - int err = 0; - BlockDriverState *bs; - BdrvNextIterator it; + g_autoptr(GList) bdrvs = NULL; + GList *iterbdrvs; - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { + if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { + return -1; + } + + iterbdrvs = bdrvs; + while (iterbdrvs) { + BlockDriverState *bs = iterbdrvs->data; AioContext *ctx = bdrv_get_aio_context(bs); + int ret = 0; aio_context_acquire(ctx); if (bs == vm_state_bs) { sn->vm_state_size = vm_state_size; - err = bdrv_snapshot_create(bs, sn); - } else if (bdrv_all_snapshots_includes_bs(bs)) { + ret = bdrv_snapshot_create(bs, sn); + } else if (devices || bdrv_all_snapshots_includes_bs(bs)) { sn->vm_state_size = 0; - err = bdrv_snapshot_create(bs, sn); + ret = bdrv_snapshot_create(bs, sn); } aio_context_release(ctx); - if (err < 0) { - bdrv_next_cleanup(&it); - goto fail; + if (ret < 0) { + error_setg(errp, "Could not create snapshot '%s' on '%s'", + sn->name, bdrv_get_device_or_node_name(bs)); + return -1; } + + iterbdrvs = iterbdrvs->next; } -fail: - *first_bad_bs = bs; - return err; + return 0; } -BlockDriverState *bdrv_all_find_vmstate_bs(void) -{ - BlockDriverState *bs; - BdrvNextIterator it; - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { +BlockDriverState *bdrv_all_find_vmstate_bs(const char *vmstate_bs, + bool has_devices, strList *devices, + Error **errp) +{ + g_autoptr(GList) bdrvs = NULL; + GList *iterbdrvs; + + if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { + return NULL; + } + + iterbdrvs = bdrvs; + while (iterbdrvs) { + BlockDriverState *bs = iterbdrvs->data; AioContext *ctx = bdrv_get_aio_context(bs); - bool found; + bool found = false; aio_context_acquire(ctx); - found = bdrv_all_snapshots_includes_bs(bs) && bdrv_can_snapshot(bs); + found = (devices || bdrv_all_snapshots_includes_bs(bs)) && + bdrv_can_snapshot(bs); aio_context_release(ctx); - if (found) { - bdrv_next_cleanup(&it); - break; + if (vmstate_bs) { + if (g_str_equal(vmstate_bs, + bdrv_get_node_name(bs))) { + if (found) { + return bs; + } else { + error_setg(errp, + "vmstate block device '%s' does not support snapshots", + vmstate_bs); + return NULL; + } + } + } else if (found) { + return bs; } + + iterbdrvs = iterbdrvs->next; } - return bs; + + if (vmstate_bs) { + error_setg(errp, + "vmstate block device '%s' does not exist", vmstate_bs); + } else { + error_setg(errp, + "no block device can store vmstate for snapshot"); + } + return NULL; } diff --git a/block/stream.c b/block/stream.c index 310ccbaa4c..97bee482dc 100644 --- a/block/stream.c +++ b/block/stream.c @@ -17,8 +17,10 @@ #include "block/blockjob_int.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" +#include "qapi/qmp/qdict.h" #include "qemu/ratelimit.h" #include "sysemu/block-backend.h" +#include "block/copy-on-read.h" enum { /* @@ -31,11 +33,13 @@ enum { typedef struct StreamBlockJob { BlockJob common; - BlockDriverState *bottom; + BlockDriverState *base_overlay; /* COW overlay (stream from this) */ + BlockDriverState *above_base; /* Node directly above the base */ + BlockDriverState *cor_filter_bs; + BlockDriverState *target_bs; BlockdevOnError on_error; char *backing_file_str; bool bs_read_only; - bool chain_frozen; } StreamBlockJob; static int coroutine_fn stream_populate(BlockBackend *blk, @@ -43,42 +47,32 @@ static int coroutine_fn stream_populate(BlockBackend *blk, { assert(bytes < SIZE_MAX); - return blk_co_preadv(blk, offset, bytes, NULL, - BDRV_REQ_COPY_ON_READ | BDRV_REQ_PREFETCH); -} - -static void stream_abort(Job *job) -{ - StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); - - if (s->chain_frozen) { - BlockJob *bjob = &s->common; - bdrv_unfreeze_backing_chain(blk_bs(bjob->blk), s->bottom); - } + return blk_co_preadv(blk, offset, bytes, NULL, BDRV_REQ_PREFETCH); } static int stream_prepare(Job *job) { StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); - BlockJob *bjob = &s->common; - BlockDriverState *bs = blk_bs(bjob->blk); - BlockDriverState *base = backing_bs(s->bottom); + BlockDriverState *unfiltered_bs = bdrv_skip_filters(s->target_bs); + BlockDriverState *base = bdrv_filter_or_cow_bs(s->above_base); + BlockDriverState *unfiltered_base = bdrv_skip_filters(base); Error *local_err = NULL; int ret = 0; - bdrv_unfreeze_backing_chain(bs, s->bottom); - s->chain_frozen = false; + /* We should drop filter at this point, as filter hold the backing chain */ + bdrv_cor_filter_drop(s->cor_filter_bs); + s->cor_filter_bs = NULL; - if (bs->backing) { + if (bdrv_cow_child(unfiltered_bs)) { const char *base_id = NULL, *base_fmt = NULL; - if (base) { - base_id = s->backing_file_str; - if (base->drv) { - base_fmt = base->drv->format_name; + if (unfiltered_base) { + base_id = s->backing_file_str ?: unfiltered_base->filename; + if (unfiltered_base->drv) { + base_fmt = unfiltered_base->drv->format_name; } } - bdrv_set_backing_hd(bs, base, &local_err); - ret = bdrv_change_backing_file(bs, base_id, base_fmt, false); + bdrv_set_backing_hd(unfiltered_bs, base, &local_err); + ret = bdrv_change_backing_file(unfiltered_bs, base_id, base_fmt, false); if (local_err) { error_report_err(local_err); return -EPERM; @@ -92,13 +86,17 @@ static void stream_clean(Job *job) { StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); BlockJob *bjob = &s->common; - BlockDriverState *bs = blk_bs(bjob->blk); + + if (s->cor_filter_bs) { + bdrv_cor_filter_drop(s->cor_filter_bs); + s->cor_filter_bs = NULL; + } /* Reopen the image back in read-only mode if necessary */ if (s->bs_read_only) { /* Give up write permissions before making it read-only */ blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort); - bdrv_reopen_set_read_only(bs, true, NULL); + bdrv_reopen_set_read_only(s->target_bs, true, NULL); } g_free(s->backing_file_str); @@ -108,34 +106,24 @@ static int coroutine_fn stream_run(Job *job, Error **errp) { StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); BlockBackend *blk = s->common.blk; - BlockDriverState *bs = blk_bs(blk); - bool enable_cor = !backing_bs(s->bottom); + BlockDriverState *unfiltered_bs = bdrv_skip_filters(s->target_bs); int64_t len; int64_t offset = 0; uint64_t delay_ns = 0; int error = 0; int64_t n = 0; /* bytes */ - if (bs == s->bottom) { + if (unfiltered_bs == s->base_overlay) { /* Nothing to stream */ return 0; } - len = bdrv_getlength(bs); + len = bdrv_getlength(s->target_bs); if (len < 0) { return len; } job_progress_set_remaining(&s->common.job, len); - /* Turn on copy-on-read for the whole block device so that guest read - * requests help us make progress. Only do this when copying the entire - * backing chain since the copy-on-read operation does not take base into - * account. - */ - if (enable_cor) { - bdrv_enable_copy_on_read(bs); - } - for ( ; offset < len; offset += n) { bool copy; int ret; @@ -150,20 +138,21 @@ static int coroutine_fn stream_run(Job *job, Error **errp) copy = false; - ret = bdrv_is_allocated(bs, offset, STREAM_CHUNK, &n); + ret = bdrv_is_allocated(unfiltered_bs, offset, STREAM_CHUNK, &n); if (ret == 1) { /* Allocated in the top, no need to copy. */ } else if (ret >= 0) { /* Copy if allocated in the intermediate images. Limit to the * known-unallocated area [offset, offset+n*BDRV_SECTOR_SIZE). */ - ret = bdrv_is_allocated_above(backing_bs(bs), s->bottom, true, + ret = bdrv_is_allocated_above(bdrv_cow_bs(unfiltered_bs), + s->base_overlay, true, offset, n, &n); /* Finish early if end of backing file has been reached */ if (ret == 0 && n == 0) { n = len - offset; } - copy = (ret == 1); + copy = (ret > 0); } trace_stream_one_iteration(s, offset, n, ret); if (copy) { @@ -193,10 +182,6 @@ static int coroutine_fn stream_run(Job *job, Error **errp) } } - if (enable_cor) { - bdrv_disable_copy_on_read(bs); - } - /* Do not remove the backing file if an error was there but ignored. */ return error; } @@ -208,7 +193,6 @@ static const BlockJobDriver stream_job_driver = { .free = block_job_free, .run = stream_run, .prepare = stream_prepare, - .abort = stream_abort, .clean = stream_clean, .user_resume = block_job_user_resume, }, @@ -216,56 +200,140 @@ static const BlockJobDriver stream_job_driver = { void stream_start(const char *job_id, BlockDriverState *bs, BlockDriverState *base, const char *backing_file_str, + BlockDriverState *bottom, int creation_flags, int64_t speed, - BlockdevOnError on_error, Error **errp) + BlockdevOnError on_error, + const char *filter_node_name, + Error **errp) { - StreamBlockJob *s; + StreamBlockJob *s = NULL; BlockDriverState *iter; bool bs_read_only; int basic_flags = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED; - BlockDriverState *bottom = bdrv_find_overlay(bs, base); + BlockDriverState *base_overlay; + BlockDriverState *cor_filter_bs = NULL; + BlockDriverState *above_base; + QDict *opts; + int ret; - if (bdrv_freeze_backing_chain(bs, bottom, errp) < 0) { - return; + assert(!(base && bottom)); + assert(!(backing_file_str && bottom)); + + if (bottom) { + /* + * New simple interface. The code is written in terms of old interface + * with @base parameter (still, it doesn't freeze link to base, so in + * this mean old code is correct for new interface). So, for now, just + * emulate base_overlay and above_base. Still, when old interface + * finally removed, we should refactor code to use only "bottom", but + * not "*base*" things. + */ + assert(!bottom->drv->is_filter); + base_overlay = above_base = bottom; + } else { + base_overlay = bdrv_find_overlay(bs, base); + if (!base_overlay) { + error_setg(errp, "'%s' is not in the backing chain of '%s'", + base->node_name, bs->node_name); + return; + } + + /* + * Find the node directly above @base. @base_overlay is a COW overlay, + * so it must have a bdrv_cow_child(), but it is the immediate overlay + * of @base, so between the two there can only be filters. + */ + above_base = base_overlay; + if (bdrv_cow_bs(above_base) != base) { + above_base = bdrv_cow_bs(above_base); + while (bdrv_filter_bs(above_base) != base) { + above_base = bdrv_filter_bs(above_base); + } + } } /* Make sure that the image is opened in read-write mode */ bs_read_only = bdrv_is_read_only(bs); if (bs_read_only) { - if (bdrv_reopen_set_read_only(bs, false, errp) != 0) { - bs_read_only = false; - goto fail; + int ret; + /* Hold the chain during reopen */ + if (bdrv_freeze_backing_chain(bs, above_base, errp) < 0) { + return; + } + + ret = bdrv_reopen_set_read_only(bs, false, errp); + + /* failure, or cor-filter will hold the chain */ + bdrv_unfreeze_backing_chain(bs, above_base); + + if (ret < 0) { + return; } } - /* Prevent concurrent jobs trying to modify the graph structure here, we - * already have our own plans. Also don't allow resize as the image size is - * queried only at the job start and then cached. */ - s = block_job_create(job_id, &stream_job_driver, NULL, bs, - basic_flags | BLK_PERM_GRAPH_MOD, + opts = qdict_new(); + + qdict_put_str(opts, "driver", "copy-on-read"); + qdict_put_str(opts, "file", bdrv_get_node_name(bs)); + /* Pass the base_overlay node name as 'bottom' to COR driver */ + qdict_put_str(opts, "bottom", base_overlay->node_name); + if (filter_node_name) { + qdict_put_str(opts, "node-name", filter_node_name); + } + + cor_filter_bs = bdrv_insert_node(bs, opts, BDRV_O_RDWR, errp); + if (!cor_filter_bs) { + goto fail; + } + + if (!filter_node_name) { + cor_filter_bs->implicit = true; + } + + s = block_job_create(job_id, &stream_job_driver, NULL, cor_filter_bs, + BLK_PERM_CONSISTENT_READ, basic_flags | BLK_PERM_WRITE, speed, creation_flags, NULL, NULL, errp); if (!s) { goto fail; } + /* + * Prevent concurrent jobs trying to modify the graph structure here, we + * already have our own plans. Also don't allow resize as the image size is + * queried only at the job start and then cached. + */ + if (block_job_add_bdrv(&s->common, "active node", bs, 0, + basic_flags | BLK_PERM_WRITE, errp)) { + goto fail; + } + /* Block all intermediate nodes between bs and base, because they will * disappear from the chain after this operation. The streaming job reads * every block only once, assuming that it doesn't change, so forbid writes * and resizes. Reassign the base node pointer because the backing BS of the * bottom node might change after the call to bdrv_reopen_set_read_only() * due to parallel block jobs running. + * above_base node might change after the call to + * bdrv_reopen_set_read_only() due to parallel block jobs running. */ - base = backing_bs(bottom); - for (iter = backing_bs(bs); iter && iter != base; iter = backing_bs(iter)) { - block_job_add_bdrv(&s->common, "intermediate node", iter, 0, - basic_flags, &error_abort); + base = bdrv_filter_or_cow_bs(above_base); + for (iter = bdrv_filter_or_cow_bs(bs); iter != base; + iter = bdrv_filter_or_cow_bs(iter)) + { + ret = block_job_add_bdrv(&s->common, "intermediate node", iter, 0, + basic_flags, errp); + if (ret < 0) { + goto fail; + } } - s->bottom = bottom; + s->base_overlay = base_overlay; + s->above_base = above_base; s->backing_file_str = g_strdup(backing_file_str); + s->cor_filter_bs = cor_filter_bs; + s->target_bs = bs; s->bs_read_only = bs_read_only; - s->chain_frozen = true; s->on_error = on_error; trace_stream_start(bs, base, s); @@ -273,8 +341,13 @@ void stream_start(const char *job_id, BlockDriverState *bs, return; fail: + if (s) { + job_early_fail(&s->common.job); + } + if (cor_filter_bs) { + bdrv_cor_filter_drop(cor_filter_bs); + } if (bs_read_only) { bdrv_reopen_set_read_only(bs, true, NULL); } - bdrv_unfreeze_backing_chain(bs, bottom); } diff --git a/block/throttle-groups.c b/block/throttle-groups.c index 98fea7fd47..fb203c3ced 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -63,7 +63,7 @@ static void timer_cb(ThrottleGroupMember *tgm, bool is_write); * access some other ThrottleGroupMember's timers only after verifying that * that ThrottleGroupMember has throttled requests in the queue. */ -typedef struct ThrottleGroup { +struct ThrottleGroup { Object parent_obj; /* refuse individual property change if initialization is complete */ @@ -79,7 +79,7 @@ typedef struct ThrottleGroup { /* This field is protected by the global QEMU mutex */ QTAILQ_ENTRY(ThrottleGroup) list; -} ThrottleGroup; +}; /* This is protected by the global QEMU mutex */ static QTAILQ_HEAD(, ThrottleGroup) throttle_groups = @@ -228,7 +228,7 @@ static ThrottleGroupMember *next_throttle_token(ThrottleGroupMember *tgm, * immediately if it has pending requests. Otherwise we could be * forcing it to wait for other member's throttled requests. */ if (tgm_has_pending_reqs(tgm, is_write) && - atomic_read(&tgm->io_limits_disabled)) { + qatomic_read(&tgm->io_limits_disabled)) { return tgm; } @@ -272,7 +272,7 @@ static bool throttle_group_schedule_timer(ThrottleGroupMember *tgm, ThrottleTimers *tt = &tgm->throttle_timers; bool must_wait; - if (atomic_read(&tgm->io_limits_disabled)) { + if (qatomic_read(&tgm->io_limits_disabled)) { return false; } @@ -358,12 +358,15 @@ static void schedule_next_request(ThrottleGroupMember *tgm, bool is_write) * @is_write: the type of operation (read/write) */ void coroutine_fn throttle_group_co_io_limits_intercept(ThrottleGroupMember *tgm, - unsigned int bytes, + int64_t bytes, bool is_write) { bool must_wait; ThrottleGroupMember *token; ThrottleGroup *tg = container_of(tgm->throttle_state, ThrottleGroup, ts); + + assert(bytes >= 0); + qemu_mutex_lock(&tg->lock); /* First we check if this I/O has to be throttled. */ @@ -417,7 +420,7 @@ static void coroutine_fn throttle_group_restart_queue_entry(void *opaque) g_free(data); - atomic_dec(&tgm->restart_pending); + qatomic_dec(&tgm->restart_pending); aio_wait_kick(); } @@ -434,7 +437,7 @@ static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write * be no timer pending on this tgm at this point */ assert(!timer_pending(tgm->throttle_timers.timers[is_write])); - atomic_inc(&tgm->restart_pending); + qatomic_inc(&tgm->restart_pending); co = qemu_coroutine_create(throttle_group_restart_queue_entry, rd); aio_co_enter(tgm->aio_context, co); @@ -544,9 +547,9 @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm, tgm->throttle_state = ts; tgm->aio_context = ctx; - atomic_set(&tgm->restart_pending, 0); + qatomic_set(&tgm->restart_pending, 0); - qemu_mutex_lock(&tg->lock); + QEMU_LOCK_GUARD(&tg->lock); /* If the ThrottleGroup is new set this ThrottleGroupMember as the token */ for (i = 0; i < 2; i++) { if (!tg->tokens[i]) { @@ -565,8 +568,6 @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm, qemu_co_mutex_init(&tgm->throttled_reqs_lock); qemu_co_queue_init(&tgm->throttled_reqs[0]); qemu_co_queue_init(&tgm->throttled_reqs[1]); - - qemu_mutex_unlock(&tg->lock); } /* Unregister a ThrottleGroupMember from its group, removing it from the list, @@ -592,27 +593,27 @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm) } /* Wait for throttle_group_restart_queue_entry() coroutines to finish */ - AIO_WAIT_WHILE(tgm->aio_context, atomic_read(&tgm->restart_pending) > 0); + AIO_WAIT_WHILE(tgm->aio_context, qatomic_read(&tgm->restart_pending) > 0); - qemu_mutex_lock(&tg->lock); - for (i = 0; i < 2; i++) { - assert(tgm->pending_reqs[i] == 0); - assert(qemu_co_queue_empty(&tgm->throttled_reqs[i])); - assert(!timer_pending(tgm->throttle_timers.timers[i])); - if (tg->tokens[i] == tgm) { - token = throttle_group_next_tgm(tgm); - /* Take care of the case where this is the last tgm in the group */ - if (token == tgm) { - token = NULL; + WITH_QEMU_LOCK_GUARD(&tg->lock) { + for (i = 0; i < 2; i++) { + assert(tgm->pending_reqs[i] == 0); + assert(qemu_co_queue_empty(&tgm->throttled_reqs[i])); + assert(!timer_pending(tgm->throttle_timers.timers[i])); + if (tg->tokens[i] == tgm) { + token = throttle_group_next_tgm(tgm); + /* Take care of the case where this is the last tgm in the group */ + if (token == tgm) { + token = NULL; + } + tg->tokens[i] = token; } - tg->tokens[i] = token; } - } - /* remove the current tgm from the list */ - QLIST_REMOVE(tgm, round_robin); - throttle_timers_destroy(&tgm->throttle_timers); - qemu_mutex_unlock(&tg->lock); + /* remove the current tgm from the list */ + QLIST_REMOVE(tgm, round_robin); + throttle_timers_destroy(&tgm->throttle_timers); + } throttle_group_unref(&tg->ts); tgm->throttle_state = NULL; @@ -638,14 +639,14 @@ void throttle_group_detach_aio_context(ThrottleGroupMember *tgm) assert(qemu_co_queue_empty(&tgm->throttled_reqs[1])); /* Kick off next ThrottleGroupMember, if necessary */ - qemu_mutex_lock(&tg->lock); - for (i = 0; i < 2; i++) { - if (timer_pending(tt->timers[i])) { - tg->any_timer_armed[i] = false; - schedule_next_request(tgm, i); + WITH_QEMU_LOCK_GUARD(&tg->lock) { + for (i = 0; i < 2; i++) { + if (timer_pending(tt->timers[i])) { + tg->any_timer_armed[i] = false; + schedule_next_request(tgm, i); + } } } - qemu_mutex_unlock(&tg->lock); throttle_timers_detach_aio_context(tt); tgm->aio_context = NULL; diff --git a/block/throttle.c b/block/throttle.c index 1c1ac57bee..b685166ad4 100644 --- a/block/throttle.c +++ b/block/throttle.c @@ -151,6 +151,15 @@ static int coroutine_fn throttle_co_pdiscard(BlockDriverState *bs, return bdrv_co_pdiscard(bs->file, offset, bytes); } +static int coroutine_fn throttle_co_pwritev_compressed(BlockDriverState *bs, + uint64_t offset, + uint64_t bytes, + QEMUIOVector *qiov) +{ + return throttle_co_pwritev(bs, offset, bytes, qiov, + BDRV_REQ_WRITE_COMPRESSED); +} + static int throttle_co_flush(BlockDriverState *bs) { return bdrv_co_flush(bs->file->bs); @@ -208,7 +217,7 @@ static void throttle_reopen_abort(BDRVReopenState *reopen_state) static void coroutine_fn throttle_co_drain_begin(BlockDriverState *bs) { ThrottleGroupMember *tgm = bs->opaque; - if (atomic_fetch_inc(&tgm->io_limits_disabled) == 0) { + if (qatomic_fetch_inc(&tgm->io_limits_disabled) == 0) { throttle_group_restart_tgm(tgm); } } @@ -217,7 +226,7 @@ static void coroutine_fn throttle_co_drain_end(BlockDriverState *bs) { ThrottleGroupMember *tgm = bs->opaque; assert(tgm->io_limits_disabled); - atomic_dec(&tgm->io_limits_disabled); + qatomic_dec(&tgm->io_limits_disabled); } static const char *const throttle_strong_runtime_opts[] = { @@ -243,6 +252,7 @@ static BlockDriver bdrv_throttle = { .bdrv_co_pwrite_zeroes = throttle_co_pwrite_zeroes, .bdrv_co_pdiscard = throttle_co_pdiscard, + .bdrv_co_pwritev_compressed = throttle_co_pwritev_compressed, .bdrv_attach_aio_context = throttle_attach_aio_context, .bdrv_detach_aio_context = throttle_detach_aio_context, @@ -250,7 +260,6 @@ static BlockDriver bdrv_throttle = { .bdrv_reopen_prepare = throttle_reopen_prepare, .bdrv_reopen_commit = throttle_reopen_commit, .bdrv_reopen_abort = throttle_reopen_abort, - .bdrv_co_block_status = bdrv_co_block_status_from_file, .bdrv_co_drain_begin = throttle_co_drain_begin, .bdrv_co_drain_end = throttle_co_drain_end, diff --git a/block/trace-events b/block/trace-events index 9158335061..1a12d634e2 100644 --- a/block/trace-events +++ b/block/trace-events @@ -11,12 +11,12 @@ blk_root_attach(void *child, void *blk, void *bs) "child %p blk %p bs %p" blk_root_detach(void *child, void *blk, void *bs) "child %p blk %p bs %p" # io.c -bdrv_co_preadv(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs %p offset %"PRId64" nbytes %"PRId64" flags 0x%x" -bdrv_co_pwritev(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs %p offset %"PRId64" nbytes %"PRId64" flags 0x%x" -bdrv_co_pwrite_zeroes(void *bs, int64_t offset, int count, int flags) "bs %p offset %"PRId64" count %d flags 0x%x" -bdrv_co_do_copy_on_readv(void *bs, int64_t offset, unsigned int bytes, int64_t cluster_offset, int64_t cluster_bytes) "bs %p offset %"PRId64" bytes %u cluster_offset %"PRId64" cluster_bytes %"PRId64 -bdrv_co_copy_range_from(void *src, uint64_t src_offset, void *dst, uint64_t dst_offset, uint64_t bytes, int read_flags, int write_flags) "src %p offset %"PRIu64" dst %p offset %"PRIu64" bytes %"PRIu64" rw flags 0x%x 0x%x" -bdrv_co_copy_range_to(void *src, uint64_t src_offset, void *dst, uint64_t dst_offset, uint64_t bytes, int read_flags, int write_flags) "src %p offset %"PRIu64" dst %p offset %"PRIu64" bytes %"PRIu64" rw flags 0x%x 0x%x" +bdrv_co_preadv_part(void *bs, int64_t offset, int64_t bytes, unsigned int flags) "bs %p offset %" PRId64 " bytes %" PRId64 " flags 0x%x" +bdrv_co_pwritev_part(void *bs, int64_t offset, int64_t bytes, unsigned int flags) "bs %p offset %" PRId64 " bytes %" PRId64 " flags 0x%x" +bdrv_co_pwrite_zeroes(void *bs, int64_t offset, int64_t bytes, int flags) "bs %p offset %" PRId64 " bytes %" PRId64 " flags 0x%x" +bdrv_co_do_copy_on_readv(void *bs, int64_t offset, int64_t bytes, int64_t cluster_offset, int64_t cluster_bytes) "bs %p offset %" PRId64 " bytes %" PRId64 " cluster_offset %" PRId64 " cluster_bytes %" PRId64 +bdrv_co_copy_range_from(void *src, int64_t src_offset, void *dst, int64_t dst_offset, int64_t bytes, int read_flags, int write_flags) "src %p offset %" PRId64 " dst %p offset %" PRId64 " bytes %" PRId64 " rw flags 0x%x 0x%x" +bdrv_co_copy_range_to(void *src, int64_t src_offset, void *dst, int64_t dst_offset, int64_t bytes, int read_flags, int write_flags) "src %p offset %" PRId64 " dst %p offset %" PRId64 " bytes %" PRId64 " rw flags 0x%x 0x%x" # stream.c stream_one_iteration(void *s, int64_t offset, uint64_t bytes, int is_allocated) "s %p offset %" PRId64 " bytes %" PRIu64 " is_allocated %d" @@ -42,7 +42,6 @@ backup_do_cow_enter(void *job, int64_t start, int64_t offset, uint64_t bytes) "j backup_do_cow_return(void *job, int64_t offset, uint64_t bytes, int ret) "job %p offset %" PRId64 " bytes %" PRIu64 " ret %d" # block-copy.c -block_copy_skip(void *bcs, int64_t start) "bcs %p start %"PRId64 block_copy_skip_range(void *bcs, int64_t start, uint64_t bytes) "bcs %p start %"PRId64" bytes %"PRId64 block_copy_process(void *bcs, int64_t start) "bcs %p start %"PRId64 block_copy_copy_range_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d" @@ -59,12 +58,10 @@ qmp_block_job_finalize(void *job) "job %p" qmp_block_job_dismiss(void *job) "job %p" qmp_block_stream(void *bs) "bs %p" -# file-posix.c # file-win32.c file_paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "acb %p opaque %p offset %"PRId64" count %d type %d" -file_copy_file_range(void *bs, int src, int64_t src_off, int dst, int64_t dst_off, int64_t bytes, int flags, int64_t ret) "bs %p src_fd %d offset %"PRIu64" dst_fd %d offset %"PRIu64" bytes %"PRIu64" flags %d ret %"PRId64 -#io_uring.c +# io_uring.c luring_init_state(void *s, size_t size) "s %p size %zu" luring_cleanup_state(void *s) "%p freed" luring_io_plug(void *s) "LuringState %p plug" @@ -77,7 +74,7 @@ luring_io_uring_submit(void *s, int ret) "LuringState %p ret %d" luring_resubmit_short_read(void *s, void *luringcb, int nread) "LuringState %p luringcb %p nread %d" # qcow2.c -qcow2_add_task(void *co, void *bs, void *pool, const char *action, int cluster_type, uint64_t file_cluster_offset, uint64_t offset, uint64_t bytes, void *qiov, size_t qiov_offset) "co %p bs %p pool %p: %s: cluster_type %d file_cluster_offset %" PRIu64 " offset %" PRIu64 " bytes %" PRIu64 " qiov %p qiov_offset %zu" +qcow2_add_task(void *co, void *bs, void *pool, const char *action, int cluster_type, uint64_t host_offset, uint64_t offset, uint64_t bytes, void *qiov, size_t qiov_offset) "co %p bs %p pool %p: %s: cluster_type %d file_cluster_offset %" PRIu64 " offset %" PRIu64 " bytes %" PRIu64 " qiov %p qiov_offset %zu" qcow2_writev_start_req(void *co, int64_t offset, int bytes) "co %p offset 0x%" PRIx64 " bytes %d" qcow2_writev_done_req(void *co, int ret) "co %p ret %d" qcow2_writev_start_part(void *co) "co %p" @@ -137,25 +134,30 @@ qed_aio_write_postfill(void *s, void *acb, uint64_t start, size_t len, uint64_t qed_aio_write_main(void *s, void *acb, int ret, uint64_t offset, size_t len) "s %p acb %p ret %d offset %"PRIu64" len %zu" # nvme.c -nvme_kick(void *s, int queue) "s %p queue %d" +nvme_controller_capability_raw(uint64_t value) "0x%08"PRIx64 +nvme_controller_capability(const char *desc, uint64_t value) "%s: %"PRIu64 +nvme_controller_spec_version(uint32_t mjr, uint32_t mnr, uint32_t ter) "Specification supported: %u.%u.%u" +nvme_kick(void *s, unsigned q_index) "s %p q #%u" nvme_dma_flush_queue_wait(void *s) "s %p" nvme_error(int cmd_specific, int sq_head, int sqid, int cid, int status) "cmd_specific %d sq_head %d sqid %d cid %d status 0x%x" -nvme_process_completion(void *s, int index, int inflight) "s %p queue %d inflight %d" -nvme_process_completion_queue_plugged(void *s, int index) "s %p queue %d" -nvme_complete_command(void *s, int index, int cid) "s %p queue %d cid %d" -nvme_submit_command(void *s, int index, int cid) "s %p queue %d cid %d" +nvme_process_completion(void *s, unsigned q_index, int inflight) "s %p q #%u inflight %d" +nvme_process_completion_queue_plugged(void *s, unsigned q_index) "s %p q #%u" +nvme_complete_command(void *s, unsigned q_index, int cid) "s %p q #%u cid %d" +nvme_submit_command(void *s, unsigned q_index, int cid) "s %p q #%u cid %d" nvme_submit_command_raw(int c0, int c1, int c2, int c3, int c4, int c5, int c6, int c7) "%02x %02x %02x %02x %02x %02x %02x %02x" nvme_handle_event(void *s) "s %p" -nvme_poll_cb(void *s) "s %p" -nvme_prw_aligned(void *s, int is_write, uint64_t offset, uint64_t bytes, int flags, int niov) "s %p is_write %d offset %"PRId64" bytes %"PRId64" flags %d niov %d" -nvme_write_zeroes(void *s, uint64_t offset, uint64_t bytes, int flags) "s %p offset %"PRId64" bytes %"PRId64" flags %d" +nvme_poll_queue(void *s, unsigned q_index) "s %p q #%u" +nvme_prw_aligned(void *s, int is_write, uint64_t offset, uint64_t bytes, int flags, int niov) "s %p is_write %d offset 0x%"PRIx64" bytes %"PRId64" flags %d niov %d" +nvme_write_zeroes(void *s, uint64_t offset, uint64_t bytes, int flags) "s %p offset 0x%"PRIx64" bytes %"PRId64" flags %d" nvme_qiov_unaligned(const void *qiov, int n, void *base, size_t size, int align) "qiov %p n %d base %p size 0x%zx align 0x%x" -nvme_prw_buffered(void *s, uint64_t offset, uint64_t bytes, int niov, int is_write) "s %p offset %"PRId64" bytes %"PRId64" niov %d is_write %d" -nvme_rw_done(void *s, int is_write, uint64_t offset, uint64_t bytes, int ret) "s %p is_write %d offset %"PRId64" bytes %"PRId64" ret %d" -nvme_dsm(void *s, uint64_t offset, uint64_t bytes) "s %p offset %"PRId64" bytes %"PRId64"" -nvme_dsm_done(void *s, uint64_t offset, uint64_t bytes, int ret) "s %p offset %"PRId64" bytes %"PRId64" ret %d" +nvme_prw_buffered(void *s, uint64_t offset, uint64_t bytes, int niov, int is_write) "s %p offset 0x%"PRIx64" bytes %"PRId64" niov %d is_write %d" +nvme_rw_done(void *s, int is_write, uint64_t offset, uint64_t bytes, int ret) "s %p is_write %d offset 0x%"PRIx64" bytes %"PRId64" ret %d" +nvme_dsm(void *s, uint64_t offset, uint64_t bytes) "s %p offset 0x%"PRIx64" bytes %"PRId64"" +nvme_dsm_done(void *s, uint64_t offset, uint64_t bytes, int ret) "s %p offset 0x%"PRIx64" bytes %"PRId64" ret %d" nvme_dma_map_flush(void *s) "s %p" -nvme_free_req_queue_wait(void *q) "q %p" +nvme_free_req_queue_wait(void *s, unsigned q_index) "s %p q #%u" +nvme_create_queue_pair(unsigned q_index, void *q, unsigned size, void *aio_context, int fd) "index %u q %p size %u aioctx %p fd %d" +nvme_free_queue_pair(unsigned q_index, void *q) "index %u q %p" nvme_cmd_map_qiov(void *s, void *cmd, void *req, void *qiov, int entries) "s %p cmd %p req %p qiov %p entries %d" nvme_cmd_map_qiov_pages(void *s, int i, uint64_t page) "s %p page[%d] 0x%"PRIx64 nvme_cmd_map_qiov_iov(void *s, int i, void *page, int pages) "s %p iov[%d] %p pages %d" @@ -200,8 +202,7 @@ curl_setup_preadv(uint64_t bytes, uint64_t start, const char *range) "reading %" curl_close(void) "close" # file-posix.c -file_xfs_write_zeroes(const char *error) "cannot write zero range (%s)" -file_xfs_discard(const char *error) "cannot punch hole (%s)" +file_copy_file_range(void *bs, int src, int64_t src_off, int dst, int64_t dst_off, int64_t bytes, int flags, int64_t ret) "bs %p src_fd %d offset %"PRIu64" dst_fd %d offset %"PRIu64" bytes %"PRIu64" flags %d ret %"PRId64 file_FindEjectableOpticalMedia(const char *media) "Matching using %s" file_setup_cdrom(const char *partition) "Using %s as optical disc" file_hdev_is_sg(int type, int version) "SG device found: type=%d, version=%d" diff --git a/block/trace.h b/block/trace.h new file mode 100644 index 0000000000..3a436e6c7f --- /dev/null +++ b/block/trace.h @@ -0,0 +1 @@ +#include "trace/trace-block.h" diff --git a/block/vdi.c b/block/vdi.c index 5627e7d764..548f8a057b 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -690,23 +690,26 @@ nonallocating_write: logout("finished data write\n"); if (ret < 0) { + g_free(block); return ret; } if (block) { /* One or more new blocks were allocated. */ - VdiHeader *header = (VdiHeader *) block; + VdiHeader *header; uint8_t *base; uint64_t offset; uint32_t n_sectors; + g_free(block); + header = g_malloc(sizeof(*header)); + logout("now writing modified header\n"); assert(VDI_IS_ALLOCATED(bmap_first)); *header = s->header; vdi_header_to_le(header); - ret = bdrv_pwrite(bs->file, 0, block, sizeof(VdiHeader)); - g_free(block); - block = NULL; + ret = bdrv_pwrite(bs->file, 0, header, sizeof(*header)); + g_free(header); if (ret < 0) { return ret; diff --git a/block/vhdx.c b/block/vhdx.c index 791eb90263..356ec4c455 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -816,9 +816,9 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) goto exit; } - /* only 2 supported sector sizes */ - if (s->logical_sector_size != 512 && s->logical_sector_size != 4096) { - ret = -EINVAL; + /* Currently we only support 512 */ + if (s->logical_sector_size != 512) { + ret = -ENOTSUP; goto exit; } diff --git a/block/vmdk.c b/block/vmdk.c index bf9df5ce92..4499f136bd 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -595,7 +595,7 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs, int ret; uint32_t magic; VMDK3Header header; - VmdkExtent *extent; + VmdkExtent *extent = NULL; ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header)); if (ret < 0) { @@ -751,7 +751,7 @@ static int vmdk_open_se_sparse(BlockDriverState *bs, int ret; VMDKSESparseConstHeader const_header; VMDKSESparseVolatileHeader volatile_header; - VmdkExtent *extent; + VmdkExtent *extent = NULL; ret = bdrv_apply_auto_read_only(bs, "No write support for seSparse images available", errp); @@ -869,7 +869,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, uint32_t magic; uint32_t l1_size, l1_entry_sectors; VMDK4Header header; - VmdkExtent *extent; + VmdkExtent *extent = NULL; BDRVVmdkState *s = bs->opaque; int64_t l1_backup_offset = 0; bool compressed; @@ -1053,14 +1053,11 @@ static int vmdk_open_sparse(BlockDriverState *bs, BdrvChild *file, int flags, switch (magic) { case VMDK3_MAGIC: return vmdk_open_vmfs_sparse(bs, file, flags, errp); - break; case VMDK4_MAGIC: return vmdk_open_vmdk4(bs, file, flags, options, errp); - break; default: error_setg(errp, "Image not in VMDK format"); return -EINVAL; - break; } } @@ -1091,7 +1088,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, BdrvChild *extent_file; BdrvChildRole extent_role; BDRVVmdkState *s = bs->opaque; - VmdkExtent *extent; + VmdkExtent *extent = NULL; char extent_opt_prefix[32]; Error *local_err = NULL; @@ -2806,21 +2803,6 @@ static void vmdk_close(BlockDriverState *bs) error_free(s->migration_blocker); } -static coroutine_fn int vmdk_co_flush(BlockDriverState *bs) -{ - BDRVVmdkState *s = bs->opaque; - int i, err; - int ret = 0; - - for (i = 0; i < s->num_extents; i++) { - err = bdrv_co_flush(s->extents[i].file->bs); - if (err < 0) { - ret = err; - } - } - return ret; -} - static int64_t vmdk_get_allocated_file_size(BlockDriverState *bs) { int i; @@ -2946,7 +2928,7 @@ static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs, int i; BDRVVmdkState *s = bs->opaque; ImageInfoSpecific *spec_info = g_new0(ImageInfoSpecific, 1); - ImageInfoList **next; + ImageInfoList **tail; *spec_info = (ImageInfoSpecific){ .type = IMAGE_INFO_SPECIFIC_KIND_VMDK, @@ -2961,12 +2943,9 @@ static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs, .parent_cid = s->parent_cid, }; - next = &spec_info->u.vmdk.data->extents; + tail = &spec_info->u.vmdk.data->extents; for (i = 0; i < s->num_extents; i++) { - *next = g_new0(ImageInfoList, 1); - (*next)->value = vmdk_get_extent_info(&s->extents[i]); - (*next)->next = NULL; - next = &(*next)->next; + QAPI_LIST_APPEND(tail, vmdk_get_extent_info(&s->extents[i])); } return spec_info; @@ -3084,7 +3063,6 @@ static BlockDriver bdrv_vmdk = { .bdrv_close = vmdk_close, .bdrv_co_create_opts = vmdk_co_create_opts, .bdrv_co_create = vmdk_co_create, - .bdrv_co_flush_to_disk = vmdk_co_flush, .bdrv_co_block_status = vmdk_co_block_status, .bdrv_get_allocated_file_size = vmdk_get_allocated_file_size, .bdrv_has_zero_init = vmdk_has_zero_init, diff --git a/block/vpc.c b/block/vpc.c index 890554277e..17a705b482 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -39,8 +39,6 @@ /**************************************************************/ -#define HEADER_SIZE 512 - //#define CACHE enum vhd_type { @@ -95,8 +93,11 @@ typedef struct vhd_footer { QemuUUID uuid; uint8_t in_saved_state; + uint8_t reserved[427]; } QEMU_PACKED VHDFooter; +QEMU_BUILD_BUG_ON(sizeof(VHDFooter) != 512); + typedef struct vhd_dyndisk_header { char magic[8]; /* "cxsparse" */ @@ -127,11 +128,14 @@ typedef struct vhd_dyndisk_header { uint32_t reserved; uint64_t data_offset; } parent_locator[8]; + uint8_t reserved2[256]; } QEMU_PACKED VHDDynDiskHeader; +QEMU_BUILD_BUG_ON(sizeof(VHDDynDiskHeader) != 1024); + typedef struct BDRVVPCState { CoMutex lock; - uint8_t footer_buf[HEADER_SIZE]; + VHDFooter footer; uint64_t free_data_block_offset; int max_table_entries; uint32_t *pagetable; @@ -172,8 +176,9 @@ static QemuOptsList vpc_runtime_opts = { static QemuOptsList vpc_create_opts; -static uint32_t vpc_checksum(uint8_t* buf, size_t size) +static uint32_t vpc_checksum(void *p, size_t size) { + uint8_t *buf = p; uint32_t res = 0; int i; @@ -216,11 +221,10 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, BDRVVPCState *s = bs->opaque; int i; VHDFooter *footer; - VHDDynDiskHeader *dyndisk_header; QemuOpts *opts = NULL; Error *local_err = NULL; bool use_chs; - uint8_t buf[HEADER_SIZE]; + VHDDynDiskHeader dyndisk_header; uint32_t checksum; uint64_t computed_size; uint64_t pagetable_size; @@ -247,28 +251,28 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - ret = bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE); + ret = bdrv_pread(bs->file, 0, &s->footer, sizeof(s->footer)); if (ret < 0) { error_setg(errp, "Unable to read VHD header"); goto fail; } - footer = (VHDFooter *) s->footer_buf; + footer = &s->footer; if (strncmp(footer->creator, "conectix", 8)) { int64_t offset = bdrv_getlength(bs->file->bs); if (offset < 0) { ret = offset; error_setg(errp, "Invalid file size"); goto fail; - } else if (offset < HEADER_SIZE) { + } else if (offset < sizeof(*footer)) { ret = -EINVAL; error_setg(errp, "File too small for a VHD header"); goto fail; } /* If a fixed disk, the footer is found only at the end of the file */ - ret = bdrv_pread(bs->file, offset-HEADER_SIZE, s->footer_buf, - HEADER_SIZE); + ret = bdrv_pread(bs->file, offset - sizeof(*footer), + footer, sizeof(*footer)); if (ret < 0) { goto fail; } @@ -282,7 +286,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, checksum = be32_to_cpu(footer->checksum); footer->checksum = 0; - if (vpc_checksum(s->footer_buf, HEADER_SIZE) != checksum) { + if (vpc_checksum(footer, sizeof(*footer)) != checksum) { error_setg(errp, "Incorrect header checksum"); ret = -EINVAL; goto fail; @@ -340,22 +344,20 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, } if (disk_type == VHD_DYNAMIC) { - ret = bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, - HEADER_SIZE); + ret = bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), + &dyndisk_header, sizeof(dyndisk_header)); if (ret < 0) { error_setg(errp, "Error reading dynamic VHD header"); goto fail; } - dyndisk_header = (VHDDynDiskHeader *) buf; - - if (strncmp(dyndisk_header->magic, "cxsparse", 8)) { + if (strncmp(dyndisk_header.magic, "cxsparse", 8)) { error_setg(errp, "Invalid header magic"); ret = -EINVAL; goto fail; } - s->block_size = be32_to_cpu(dyndisk_header->block_size); + s->block_size = be32_to_cpu(dyndisk_header.block_size); if (!is_power_of_2(s->block_size) || s->block_size < BDRV_SECTOR_SIZE) { error_setg(errp, "Invalid block size %" PRIu32, s->block_size); ret = -EINVAL; @@ -363,7 +365,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, } s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511; - s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries); + s->max_table_entries = be32_to_cpu(dyndisk_header.max_table_entries); if ((bs->total_sectors * 512) / s->block_size > 0xffffffffU) { error_setg(errp, "Too many blocks"); @@ -395,7 +397,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - s->bat_offset = be64_to_cpu(dyndisk_header->table_offset); + s->bat_offset = be64_to_cpu(dyndisk_header.table_offset); ret = bdrv_pread(bs->file, s->bat_offset, s->pagetable, pagetable_size); @@ -528,13 +530,13 @@ static inline int64_t get_image_offset(BlockDriverState *bs, uint64_t offset, * * Returns 0 on success and < 0 on error */ -static int rewrite_footer(BlockDriverState* bs) +static int rewrite_footer(BlockDriverState *bs) { int ret; BDRVVPCState *s = bs->opaque; int64_t offset = s->free_data_block_offset; - ret = bdrv_pwrite_sync(bs->file, offset, s->footer_buf, HEADER_SIZE); + ret = bdrv_pwrite_sync(bs->file, offset, &s->footer, sizeof(s->footer)); if (ret < 0) return ret; @@ -548,7 +550,7 @@ static int rewrite_footer(BlockDriverState* bs) * * Returns the sectors' offset in the image file on success and < 0 on error */ -static int64_t alloc_block(BlockDriverState* bs, int64_t offset) +static int64_t alloc_block(BlockDriverState *bs, int64_t offset) { BDRVVPCState *s = bs->opaque; int64_t bat_offset; @@ -597,9 +599,8 @@ fail: static int vpc_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { BDRVVPCState *s = (BDRVVPCState *)bs->opaque; - VHDFooter *footer = (VHDFooter *) s->footer_buf; - if (be32_to_cpu(footer->type) != VHD_FIXED) { + if (be32_to_cpu(s->footer.type) != VHD_FIXED) { bdi->cluster_size = s->block_size; } @@ -615,10 +616,9 @@ vpc_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, int64_t image_offset; int64_t n_bytes; int64_t bytes_done = 0; - VHDFooter *footer = (VHDFooter *) s->footer_buf; QEMUIOVector local_qiov; - if (be32_to_cpu(footer->type) == VHD_FIXED) { + if (be32_to_cpu(s->footer.type) == VHD_FIXED) { return bdrv_co_preadv(bs->file, offset, bytes, qiov, 0); } @@ -666,10 +666,9 @@ vpc_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, int64_t n_bytes; int64_t bytes_done = 0; int ret = 0; - VHDFooter *footer = (VHDFooter *) s->footer_buf; QEMUIOVector local_qiov; - if (be32_to_cpu(footer->type) == VHD_FIXED) { + if (be32_to_cpu(s->footer.type) == VHD_FIXED) { return bdrv_co_pwritev(bs->file, offset, bytes, qiov, 0); } @@ -723,13 +722,12 @@ static int coroutine_fn vpc_co_block_status(BlockDriverState *bs, BlockDriverState **file) { BDRVVPCState *s = bs->opaque; - VHDFooter *footer = (VHDFooter*) s->footer_buf; int64_t image_offset; bool allocated; int ret; int64_t n; - if (be32_to_cpu(footer->type) == VHD_FIXED) { + if (be32_to_cpu(s->footer.type) == VHD_FIXED) { *pnum = bytes; *map = offset; *file = bs->file->bs; @@ -781,8 +779,8 @@ static int coroutine_fn vpc_co_block_status(BlockDriverState *bs, * the hardware EIDE and ATA-2 limit of 16 heads (max disk size of 127 GB) * and instead allow up to 255 heads. */ -static int calculate_geometry(int64_t total_sectors, uint16_t* cyls, - uint8_t* heads, uint8_t* secs_per_cyl) +static int calculate_geometry(int64_t total_sectors, uint16_t *cyls, + uint8_t *heads, uint8_t *secs_per_cyl) { uint32_t cyls_times_heads; @@ -819,11 +817,11 @@ static int calculate_geometry(int64_t total_sectors, uint16_t* cyls, return 0; } -static int create_dynamic_disk(BlockBackend *blk, uint8_t *buf, +static int create_dynamic_disk(BlockBackend *blk, VHDFooter *footer, int64_t total_sectors) { - VHDDynDiskHeader *dyndisk_header = - (VHDDynDiskHeader *) buf; + VHDDynDiskHeader dyndisk_header; + uint8_t bat_sector[512]; size_t block_size, num_bat_entries; int i; int ret; @@ -833,13 +831,13 @@ static int create_dynamic_disk(BlockBackend *blk, uint8_t *buf, block_size = 0x200000; num_bat_entries = DIV_ROUND_UP(total_sectors, block_size / 512); - ret = blk_pwrite(blk, offset, buf, HEADER_SIZE, 0); + ret = blk_pwrite(blk, offset, footer, sizeof(*footer), 0); if (ret < 0) { goto fail; } offset = 1536 + ((num_bat_entries * 4 + 511) & ~511); - ret = blk_pwrite(blk, offset, buf, HEADER_SIZE, 0); + ret = blk_pwrite(blk, offset, footer, sizeof(*footer), 0); if (ret < 0) { goto fail; } @@ -847,9 +845,9 @@ static int create_dynamic_disk(BlockBackend *blk, uint8_t *buf, /* Write the initial BAT */ offset = 3 * 512; - memset(buf, 0xFF, 512); + memset(bat_sector, 0xFF, 512); for (i = 0; i < DIV_ROUND_UP(num_bat_entries * 4, 512); i++) { - ret = blk_pwrite(blk, offset, buf, 512, 0); + ret = blk_pwrite(blk, offset, bat_sector, 512, 0); if (ret < 0) { goto fail; } @@ -857,26 +855,27 @@ static int create_dynamic_disk(BlockBackend *blk, uint8_t *buf, } /* Prepare the Dynamic Disk Header */ - memset(buf, 0, 1024); + memset(&dyndisk_header, 0, sizeof(dyndisk_header)); - memcpy(dyndisk_header->magic, "cxsparse", 8); + memcpy(dyndisk_header.magic, "cxsparse", 8); /* * Note: The spec is actually wrong here for data_offset, it says * 0xFFFFFFFF, but MS tools expect all 64 bits to be set. */ - dyndisk_header->data_offset = cpu_to_be64(0xFFFFFFFFFFFFFFFFULL); - dyndisk_header->table_offset = cpu_to_be64(3 * 512); - dyndisk_header->version = cpu_to_be32(0x00010000); - dyndisk_header->block_size = cpu_to_be32(block_size); - dyndisk_header->max_table_entries = cpu_to_be32(num_bat_entries); + dyndisk_header.data_offset = cpu_to_be64(0xFFFFFFFFFFFFFFFFULL); + dyndisk_header.table_offset = cpu_to_be64(3 * 512); + dyndisk_header.version = cpu_to_be32(0x00010000); + dyndisk_header.block_size = cpu_to_be32(block_size); + dyndisk_header.max_table_entries = cpu_to_be32(num_bat_entries); - dyndisk_header->checksum = cpu_to_be32(vpc_checksum(buf, 1024)); + dyndisk_header.checksum = cpu_to_be32( + vpc_checksum(&dyndisk_header, sizeof(dyndisk_header))); /* Write the header */ offset = 512; - ret = blk_pwrite(blk, offset, buf, 1024, 0); + ret = blk_pwrite(blk, offset, &dyndisk_header, sizeof(dyndisk_header), 0); if (ret < 0) { goto fail; } @@ -886,20 +885,21 @@ static int create_dynamic_disk(BlockBackend *blk, uint8_t *buf, return ret; } -static int create_fixed_disk(BlockBackend *blk, uint8_t *buf, +static int create_fixed_disk(BlockBackend *blk, VHDFooter *footer, int64_t total_size, Error **errp) { int ret; /* Add footer to total size */ - total_size += HEADER_SIZE; + total_size += sizeof(*footer); ret = blk_truncate(blk, total_size, false, PREALLOC_MODE_OFF, 0, errp); if (ret < 0) { return ret; } - ret = blk_pwrite(blk, total_size - HEADER_SIZE, buf, HEADER_SIZE, 0); + ret = blk_pwrite(blk, total_size - sizeof(*footer), + footer, sizeof(*footer), 0); if (ret < 0) { error_setg_errno(errp, -ret, "Unable to write VHD header"); return ret; @@ -971,8 +971,7 @@ static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts, BlockBackend *blk = NULL; BlockDriverState *bs = NULL; - uint8_t buf[1024]; - VHDFooter *footer = (VHDFooter *) buf; + VHDFooter footer; uint16_t cyls = 0; uint8_t heads = 0; uint8_t secs_per_cyl = 0; @@ -1035,48 +1034,48 @@ static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts, } /* Prepare the Hard Disk Footer */ - memset(buf, 0, 1024); + memset(&footer, 0, sizeof(footer)); - memcpy(footer->creator, "conectix", 8); + memcpy(footer.creator, "conectix", 8); if (vpc_opts->force_size) { - memcpy(footer->creator_app, "qem2", 4); + memcpy(footer.creator_app, "qem2", 4); } else { - memcpy(footer->creator_app, "qemu", 4); + memcpy(footer.creator_app, "qemu", 4); } - memcpy(footer->creator_os, "Wi2k", 4); + memcpy(footer.creator_os, "Wi2k", 4); - footer->features = cpu_to_be32(0x02); - footer->version = cpu_to_be32(0x00010000); + footer.features = cpu_to_be32(0x02); + footer.version = cpu_to_be32(0x00010000); if (disk_type == VHD_DYNAMIC) { - footer->data_offset = cpu_to_be64(HEADER_SIZE); + footer.data_offset = cpu_to_be64(sizeof(footer)); } else { - footer->data_offset = cpu_to_be64(0xFFFFFFFFFFFFFFFFULL); + footer.data_offset = cpu_to_be64(0xFFFFFFFFFFFFFFFFULL); } - footer->timestamp = cpu_to_be32(time(NULL) - VHD_TIMESTAMP_BASE); + footer.timestamp = cpu_to_be32(time(NULL) - VHD_TIMESTAMP_BASE); /* Version of Virtual PC 2007 */ - footer->major = cpu_to_be16(0x0005); - footer->minor = cpu_to_be16(0x0003); - footer->orig_size = cpu_to_be64(total_size); - footer->current_size = cpu_to_be64(total_size); - footer->cyls = cpu_to_be16(cyls); - footer->heads = heads; - footer->secs_per_cyl = secs_per_cyl; + footer.major = cpu_to_be16(0x0005); + footer.minor = cpu_to_be16(0x0003); + footer.orig_size = cpu_to_be64(total_size); + footer.current_size = cpu_to_be64(total_size); + footer.cyls = cpu_to_be16(cyls); + footer.heads = heads; + footer.secs_per_cyl = secs_per_cyl; - footer->type = cpu_to_be32(disk_type); + footer.type = cpu_to_be32(disk_type); qemu_uuid_generate(&uuid); - footer->uuid = uuid; + footer.uuid = uuid; - footer->checksum = cpu_to_be32(vpc_checksum(buf, HEADER_SIZE)); + footer.checksum = cpu_to_be32(vpc_checksum(&footer, sizeof(footer))); if (disk_type == VHD_DYNAMIC) { - ret = create_dynamic_disk(blk, buf, total_sectors); + ret = create_dynamic_disk(blk, &footer, total_sectors); if (ret < 0) { error_setg(errp, "Unable to create or write VHD header"); } } else { - ret = create_fixed_disk(blk, buf, total_size, errp); + ret = create_fixed_disk(blk, &footer, total_size, errp); } out: @@ -1170,9 +1169,8 @@ fail: static int vpc_has_zero_init(BlockDriverState *bs) { BDRVVPCState *s = bs->opaque; - VHDFooter *footer = (VHDFooter *) s->footer_buf; - if (be32_to_cpu(footer->type) == VHD_FIXED) { + if (be32_to_cpu(s->footer.type) == VHD_FIXED) { return bdrv_has_zero_init(bs->file->bs); } else { return 1; diff --git a/block/vvfat.c b/block/vvfat.c index 36b53c8757..54807f82ca 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -1352,7 +1352,8 @@ static int open_file(BDRVVVFATState* s,mapping_t* mapping) if(!s->current_mapping || strcmp(s->current_mapping->path,mapping->path)) { /* open file */ - int fd = qemu_open(mapping->path, O_RDONLY | O_BINARY | O_LARGEFILE); + int fd = qemu_open_old(mapping->path, + O_RDONLY | O_BINARY | O_LARGEFILE); if(fd<0) return -1; vvfat_close_current_file(s); @@ -1436,7 +1437,7 @@ static void print_direntry(const direntry_t* direntry) for(i=0;i<11;i++) ADD_CHAR(direntry->name[i]); buffer[j] = 0; - fprintf(stderr,"%s attributes=0x%02x begin=%d size=%d\n", + fprintf(stderr, "%s attributes=0x%02x begin=%u size=%u\n", buffer, direntry->attributes, begin_of_direntry(direntry),le32_to_cpu(direntry->size)); @@ -1445,7 +1446,7 @@ static void print_direntry(const direntry_t* direntry) static void print_mapping(const mapping_t* mapping) { - fprintf(stderr, "mapping (%p): begin, end = %d, %d, dir_index = %d, " + fprintf(stderr, "mapping (%p): begin, end = %u, %u, dir_index = %u, " "first_mapping_index = %d, name = %s, mode = 0x%x, " , mapping, mapping->begin, mapping->end, mapping->dir_index, mapping->first_mapping_index, mapping->path, mapping->mode); @@ -1453,7 +1454,7 @@ static void print_mapping(const mapping_t* mapping) if (mapping->mode & MODE_DIRECTORY) fprintf(stderr, "parent_mapping_index = %d, first_dir_index = %d\n", mapping->info.dir.parent_mapping_index, mapping->info.dir.first_dir_index); else - fprintf(stderr, "offset = %d\n", mapping->info.file.offset); + fprintf(stderr, "offset = %u\n", mapping->info.file.offset); } #endif @@ -1587,7 +1588,7 @@ typedef struct commit_t { static void clear_commits(BDRVVVFATState* s) { int i; -DLOG(fprintf(stderr, "clear_commits (%d commits)\n", s->commits.next)); +DLOG(fprintf(stderr, "clear_commits (%u commits)\n", s->commits.next)); for (i = 0; i < s->commits.next; i++) { commit_t* commit = array_get(&(s->commits), i); assert(commit->path || commit->action == ACTION_WRITEOUT); @@ -2513,7 +2514,7 @@ static int commit_one_file(BDRVVVFATState* s, for (i = s->cluster_size; i < offset; i += s->cluster_size) c = modified_fat_get(s, c); - fd = qemu_open(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666); + fd = qemu_open_old(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666); if (fd < 0) { fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path, strerror(errno), errno); @@ -2647,7 +2648,9 @@ static int handle_renames_and_mkdirs(BDRVVVFATState* s) fprintf(stderr, "handle_renames\n"); for (i = 0; i < s->commits.next; i++) { commit_t* commit = array_get(&(s->commits), i); - fprintf(stderr, "%d, %s (%d, %d)\n", i, commit->path ? commit->path : "(null)", commit->param.rename.cluster, commit->action); + fprintf(stderr, "%d, %s (%u, %d)\n", i, + commit->path ? commit->path : "(null)", + commit->param.rename.cluster, commit->action); } #endif diff --git a/blockdev-nbd.c b/blockdev-nbd.c index 1a95d89f00..b264620b98 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -14,7 +14,9 @@ #include "sysemu/block-backend.h" #include "hw/block/block.h" #include "qapi/error.h" -#include "qapi/qapi-commands-block.h" +#include "qapi/clone-visitor.h" +#include "qapi/qapi-visit-block-export.h" +#include "qapi/qapi-commands-block-export.h" #include "block/nbd.h" #include "io/channel-socket.h" #include "io/net-listener.h" @@ -23,23 +25,52 @@ typedef struct NBDServerData { QIONetListener *listener; QCryptoTLSCreds *tlscreds; char *tlsauthz; + uint32_t max_connections; + uint32_t connections; } NBDServerData; static NBDServerData *nbd_server; +static bool is_qemu_nbd; + +static void nbd_update_server_watch(NBDServerData *s); + +void nbd_server_is_qemu_nbd(bool value) +{ + is_qemu_nbd = value; +} + +bool nbd_server_is_running(void) +{ + return nbd_server || is_qemu_nbd; +} static void nbd_blockdev_client_closed(NBDClient *client, bool ignored) { nbd_client_put(client); + assert(nbd_server->connections > 0); + nbd_server->connections--; + nbd_update_server_watch(nbd_server); } static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc, gpointer opaque) { + nbd_server->connections++; + nbd_update_server_watch(nbd_server); + qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server"); nbd_client_new(cioc, nbd_server->tlscreds, nbd_server->tlsauthz, nbd_blockdev_client_closed); } +static void nbd_update_server_watch(NBDServerData *s) +{ + 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) { @@ -88,7 +119,8 @@ static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) void nbd_server_start(SocketAddress *addr, const char *tls_creds, - const char *tls_authz, Error **errp) + const char *tls_authz, uint32_t max_connections, + Error **errp) { if (nbd_server) { error_setg(errp, "NBD server already running"); @@ -96,12 +128,18 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds, } nbd_server = g_new0(NBDServerData, 1); + nbd_server->max_connections = max_connections; nbd_server->listener = qio_net_listener_new(); qio_net_listener_set_name(nbd_server->listener, "nbd-listener"); - if (qio_net_listener_open_sync(nbd_server->listener, addr, 1, errp) < 0) { + /* + * Because this server is persistent, a backlog of SOMAXCONN is + * better than trying to size it to max_connections. + */ + if (qio_net_listener_open_sync(nbd_server->listener, addr, SOMAXCONN, + errp) < 0) { goto error; } @@ -120,10 +158,7 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds, nbd_server->tlsauthz = g_strdup(tls_authz); - qio_net_listener_set_client_func(nbd_server->listener, - nbd_accept, - NULL, - NULL); + nbd_update_server_watch(nbd_server); return; @@ -134,117 +169,99 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds, void nbd_server_start_options(NbdServerOptions *arg, Error **errp) { - nbd_server_start(arg->addr, arg->tls_creds, arg->tls_authz, errp); + nbd_server_start(arg->addr, arg->tls_creds, arg->tls_authz, + arg->max_connections, errp); } void qmp_nbd_server_start(SocketAddressLegacy *addr, bool has_tls_creds, const char *tls_creds, bool has_tls_authz, const char *tls_authz, + bool has_max_connections, uint32_t max_connections, Error **errp) { SocketAddress *addr_flat = socket_address_flatten(addr); - nbd_server_start(addr_flat, tls_creds, tls_authz, errp); + nbd_server_start(addr_flat, tls_creds, tls_authz, max_connections, errp); qapi_free_SocketAddress(addr_flat); } -void qmp_nbd_server_add(BlockExportNbd *arg, Error **errp) +void qmp_nbd_server_add(NbdServerAddOptions *arg, Error **errp) { - BlockDriverState *bs = NULL; + BlockExport *export; + BlockDriverState *bs; BlockBackend *on_eject_blk; - NBDExport *exp; - int64_t len; - AioContext *aio_context; - - if (!nbd_server) { - error_setg(errp, "NBD server not running"); - return; - } - - if (!arg->has_name) { - arg->name = arg->device; - } - - if (strlen(arg->name) > NBD_MAX_STRING_SIZE) { - error_setg(errp, "export name '%s' too long", arg->name); - return; - } - - if (arg->description && strlen(arg->description) > NBD_MAX_STRING_SIZE) { - error_setg(errp, "description '%s' too long", arg->description); - return; - } - - if (nbd_export_find(arg->name)) { - error_setg(errp, "NBD server already has export named '%s'", arg->name); - return; - } - - on_eject_blk = blk_by_name(arg->device); + BlockExportOptions *export_opts; bs = bdrv_lookup_bs(arg->device, arg->device, errp); if (!bs) { return; } - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); - len = bdrv_getlength(bs); - if (len < 0) { - error_setg_errno(errp, -len, - "Failed to determine the NBD export's length"); - goto out; + /* + * block-export-add would default to the node-name, but we may have to use + * the device name as a default here for compatibility. + */ + if (!arg->has_name) { + arg->has_name = true; + arg->name = g_strdup(arg->device); } - if (!arg->has_writable) { - arg->writable = false; + export_opts = g_new(BlockExportOptions, 1); + *export_opts = (BlockExportOptions) { + .type = BLOCK_EXPORT_TYPE_NBD, + .id = g_strdup(arg->name), + .node_name = g_strdup(bdrv_get_node_name(bs)), + .has_writable = arg->has_writable, + .writable = arg->writable, + }; + QAPI_CLONE_MEMBERS(BlockExportOptionsNbdBase, &export_opts->u.nbd, + qapi_NbdServerAddOptions_base(arg)); + if (arg->has_bitmap) { + export_opts->u.nbd.has_bitmaps = true; + QAPI_LIST_PREPEND(export_opts->u.nbd.bitmaps, g_strdup(arg->bitmap)); } + + /* + * nbd-server-add doesn't complain when a read-only device should be + * exported as writable, but simply downgrades it. This is an error with + * block-export-add. + */ if (bdrv_is_read_only(bs)) { - arg->writable = false; + export_opts->has_writable = true; + export_opts->writable = false; } - exp = nbd_export_new(bs, 0, len, arg->name, arg->description, arg->bitmap, - !arg->writable, !arg->writable, - NULL, false, on_eject_blk, errp); - if (!exp) { - goto out; + export = blk_exp_add(export_opts, errp); + if (!export) { + goto fail; } - /* The list of named exports has a strong reference to this export now and - * our only way of accessing it is through nbd_export_find(), so we can drop - * the strong reference that is @exp. */ - nbd_export_put(exp); + /* + * nbd-server-add removes the export when the named BlockBackend used for + * @device goes away. + */ + on_eject_blk = blk_by_name(arg->device); + if (on_eject_blk) { + nbd_export_set_on_eject_blk(export, on_eject_blk); + } - out: - aio_context_release(aio_context); +fail: + qapi_free_BlockExportOptions(export_opts); } void qmp_nbd_server_remove(const char *name, - bool has_mode, NbdServerRemoveMode mode, + bool has_mode, BlockExportRemoveMode mode, Error **errp) { - NBDExport *exp; - AioContext *aio_context; + BlockExport *exp; - if (!nbd_server) { - error_setg(errp, "NBD server not running"); + exp = blk_exp_find(name); + if (exp && exp->drv->type != BLOCK_EXPORT_TYPE_NBD) { + error_setg(errp, "Block export '%s' is not an NBD export", name); return; } - exp = nbd_export_find(name); - if (exp == NULL) { - error_setg(errp, "Export '%s' is not found", name); - return; - } - - if (!has_mode) { - mode = NBD_SERVER_REMOVE_MODE_SAFE; - } - - aio_context = nbd_export_aio_context(exp); - aio_context_acquire(aio_context); - nbd_export_remove(exp, mode, errp); - aio_context_release(aio_context); + qmp_block_export_del(name, has_mode, mode, errp); } void qmp_nbd_server_stop(Error **errp) @@ -254,7 +271,7 @@ void qmp_nbd_server_stop(Error **errp) return; } - nbd_export_close_all(); + blk_exp_close_all_type(BLOCK_EXPORT_TYPE_NBD); nbd_server_free(nbd_server); nbd_server = NULL; diff --git a/blockdev.c b/blockdev.c index 2b4a25f7c5..43663b0c5d 100644 --- a/blockdev.c +++ b/blockdev.c @@ -57,8 +57,8 @@ #include "block/block_int.h" #include "block/trace.h" #include "sysemu/arch_init.h" -#include "sysemu/qtest.h" #include "sysemu/runstate.h" +#include "sysemu/replay.h" #include "qemu/cutils.h" #include "qemu/help_option.h" #include "qemu/main-loop.h" @@ -239,19 +239,10 @@ DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit) return NULL; } -void drive_mark_claimed_by_board(void) -{ - BlockBackend *blk; - DriveInfo *dinfo; - - for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { - dinfo = blk_legacy_dinfo(blk); - if (dinfo && blk_get_attached_dev(blk)) { - dinfo->claimed_by_board = true; - } - } -} - +/* + * Check board claimed all -drive that are meant to be claimed. + * Fatal error if any remain unclaimed. + */ void drive_check_orphaned(void) { BlockBackend *blk; @@ -261,7 +252,17 @@ void drive_check_orphaned(void) for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { dinfo = blk_legacy_dinfo(blk); - if (dinfo->is_default || dinfo->type == IF_NONE) { + /* + * Ignore default drives, because we create certain default + * drives unconditionally, then leave them unclaimed. Not the + * users fault. + * Ignore IF_VIRTIO, because it gets desugared into -device, + * so we can leave failing to -device. + * Ignore IF_NONE, because leaving unclaimed IF_NONE remains + * available for device_add is a feature. + */ + if (dinfo->is_default || dinfo->type == IF_VIRTIO + || dinfo->type == IF_NONE) { continue; } if (!blk_get_attached_dev(blk)) { @@ -272,14 +273,6 @@ void drive_check_orphaned(void) if_name[dinfo->type], dinfo->bus, dinfo->unit); loc_pop(&loc); orphans = true; - continue; - } - if (!dinfo->claimed_by_board && dinfo->type != IF_VIRTIO) { - loc_push_none(&loc); - qemu_opts_loc_restore(dinfo->opts); - warn_report("bogus if=%s is deprecated, use if=none", - if_name[dinfo->type]); - loc_pop(&loc); } } @@ -977,11 +970,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type, QemuOpts *devopts; devopts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, &error_abort); - if (arch_type == QEMU_ARCH_S390X) { - qemu_opt_set(devopts, "driver", "virtio-blk-ccw", &error_abort); - } else { - qemu_opt_set(devopts, "driver", "virtio-blk-pci", &error_abort); - } + qemu_opt_set(devopts, "driver", "virtio-blk", &error_abort); qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"), &error_abort); } @@ -1199,6 +1188,10 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, info->vm_state_size = sn.vm_state_size; info->vm_clock_nsec = sn.vm_clock_nsec % 1000000000; info->vm_clock_sec = sn.vm_clock_nsec / 1000000000; + if (sn.icount != -1ULL) { + info->icount = sn.icount; + info->has_icount = true; + } return info; @@ -1359,6 +1352,11 @@ static void internal_snapshot_prepare(BlkActionState *common, sn->date_sec = tv.tv_sec; sn->date_nsec = tv.tv_usec * 1000; sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + if (replay_mode != REPLAY_MODE_NONE) { + sn->icount = replay_get_current_icount(); + } else { + sn->icount = -1ULL; + } ret1 = bdrv_snapshot_create(bs, sn); if (ret1 < 0) { @@ -1431,6 +1429,7 @@ typedef struct ExternalSnapshotState { static void external_snapshot_prepare(BlkActionState *common, Error **errp) { + int ret; int flags = 0; QDict *options = NULL; Error *local_err = NULL; @@ -1513,13 +1512,13 @@ static void external_snapshot_prepare(BlkActionState *common, s->has_snapshot_node_name ? s->snapshot_node_name : NULL; if (node_name && !snapshot_node_name) { - error_setg(errp, "New overlay node name missing"); + error_setg(errp, "New overlay node-name missing"); goto out; } if (snapshot_node_name && bdrv_lookup_bs(snapshot_node_name, snapshot_node_name, NULL)) { - error_setg(errp, "New overlay node name already in use"); + error_setg(errp, "New overlay node-name already in use"); goto out; } @@ -1571,7 +1570,12 @@ static void external_snapshot_prepare(BlkActionState *common, goto out; } - if (state->new_bs->backing != NULL) { + if (state->new_bs->drv->is_filter) { + error_setg(errp, "Filters cannot be used as overlays"); + goto out; + } + + if (bdrv_cow_child(state->new_bs)) { error_setg(errp, "The overlay already has a backing image"); goto out; } @@ -1585,9 +1589,8 @@ static void external_snapshot_prepare(BlkActionState *common, * can fail, so we need to do it in .prepare; undoing it for abort is * always possible. */ bdrv_ref(state->new_bs); - bdrv_append(state->new_bs, state->old_bs, &local_err); - if (local_err) { - error_propagate(errp, local_err); + ret = bdrv_append(state->new_bs, state->old_bs, errp); + if (ret < 0) { goto out; } state->overlay_appended = true; @@ -1608,7 +1611,7 @@ static void external_snapshot_commit(BlkActionState *common) /* We don't need (or want) to use the transactional * bdrv_reopen_multiple() across all the entries at once, because we * don't want to abort all of them if one of them fails the reopen */ - if (!atomic_read(&state->old_bs->copy_on_read)) { + if (!qatomic_read(&state->old_bs->copy_on_read)) { bdrv_reopen_set_read_only(state->old_bs, true, NULL); } @@ -1745,7 +1748,13 @@ static void drive_backup_prepare(BlkActionState *common, Error **errp) * on top of. */ if (backup->sync == MIRROR_SYNC_MODE_TOP) { - source = backing_bs(bs); + /* + * Backup will not replace the source by the target, so none + * of the filters skipped here will be removed (in contrast to + * mirror). Therefore, we can skip all of them when looking + * for the first COW relationship. + */ + source = bdrv_cow_bs(bdrv_skip_filters(bs)); if (!source) { backup->sync = MIRROR_SYNC_MODE_FULL; } @@ -1765,9 +1774,14 @@ static void drive_backup_prepare(BlkActionState *common, Error **errp) if (backup->mode != NEW_IMAGE_MODE_EXISTING) { assert(backup->format); if (source) { - bdrv_refresh_filename(source); - bdrv_img_create(backup->target, backup->format, source->filename, - source->drv->format_name, NULL, + /* Implicit filters should not appear in the filename */ + BlockDriverState *explicit_backing = + bdrv_skip_implicit_filters(source); + + bdrv_refresh_filename(explicit_backing); + bdrv_img_create(backup->target, backup->format, + explicit_backing->filename, + explicit_backing->drv->format_name, NULL, size, flags, false, &local_err); } else { bdrv_img_create(backup->target, backup->format, NULL, NULL, NULL, @@ -1808,8 +1822,7 @@ static void drive_backup_prepare(BlkActionState *common, Error **errp) aio_context_acquire(aio_context); if (set_backing_hd) { - bdrv_set_backing_hd(target_bs, source, &local_err); - if (local_err) { + if (bdrv_set_backing_hd(target_bs, source, errp) < 0) { goto unref; } } @@ -2399,14 +2412,6 @@ exit: job_txn_unref(block_job_txn); } -void qmp_block_passwd(bool has_device, const char *device, - bool has_node_name, const char *node_name, - const char *password, Error **errp) -{ - error_setg(errp, - "Setting block passwords directly is no longer supported"); -} - BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node, const char *name, Error **errp) @@ -2432,14 +2437,14 @@ BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node, return ret; } -void qmp_block_resize(bool has_device, const char *device, - bool has_node_name, const char *node_name, - int64_t size, Error **errp) +void coroutine_fn qmp_block_resize(bool has_device, const char *device, + bool has_node_name, const char *node_name, + int64_t size, Error **errp) { Error *local_err = NULL; - BlockBackend *blk = NULL; + BlockBackend *blk; BlockDriverState *bs; - AioContext *aio_context; + AioContext *old_ctx; bs = bdrv_lookup_bs(has_device ? device : NULL, has_node_name ? node_name : NULL, @@ -2449,50 +2454,72 @@ void qmp_block_resize(bool has_device, const char *device, return; } - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); - if (size < 0) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "size", "a >0 size"); - goto out; + return; } if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_RESIZE, NULL)) { error_setg(errp, QERR_DEVICE_IN_USE, device); - goto out; + return; } blk = blk_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL, errp); if (!blk) { - goto out; + return; } + bdrv_co_lock(bs); bdrv_drained_begin(bs); - blk_truncate(blk, size, false, PREALLOC_MODE_OFF, 0, errp); - bdrv_drained_end(bs); + bdrv_co_unlock(bs); -out: + old_ctx = bdrv_co_enter(bs); + blk_truncate(blk, size, false, PREALLOC_MODE_OFF, 0, errp); + bdrv_co_leave(bs, old_ctx); + + bdrv_co_lock(bs); + bdrv_drained_end(bs); blk_unref(blk); - aio_context_release(aio_context); + bdrv_co_unlock(bs); } void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, bool has_base, const char *base, bool has_base_node, const char *base_node, bool has_backing_file, const char *backing_file, + bool has_bottom, const char *bottom, bool has_speed, int64_t speed, bool has_on_error, BlockdevOnError on_error, + bool has_filter_node_name, const char *filter_node_name, bool has_auto_finalize, bool auto_finalize, bool has_auto_dismiss, bool auto_dismiss, Error **errp) { - BlockDriverState *bs, *iter; + BlockDriverState *bs, *iter, *iter_end; BlockDriverState *base_bs = NULL; + BlockDriverState *bottom_bs = NULL; AioContext *aio_context; Error *local_err = NULL; - const char *base_name = NULL; int job_flags = JOB_DEFAULT; + if (has_base && has_base_node) { + error_setg(errp, "'base' and 'base-node' cannot be specified " + "at the same time"); + return; + } + + if (has_base && has_bottom) { + error_setg(errp, "'base' and 'bottom' cannot be specified " + "at the same time"); + return; + } + + if (has_bottom && has_base_node) { + error_setg(errp, "'bottom' and 'base-node' cannot be specified " + "at the same time"); + return; + } + if (!has_on_error) { on_error = BLOCKDEV_ON_ERROR_REPORT; } @@ -2505,20 +2532,13 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); - if (has_base && has_base_node) { - error_setg(errp, "'base' and 'base-node' cannot be specified " - "at the same time"); - goto out; - } - if (has_base) { base_bs = bdrv_find_backing_image(bs, base); if (base_bs == NULL) { - error_setg(errp, QERR_BASE_NOT_FOUND, base); + error_setg(errp, "Can't find '%s' in the backing chain", base); goto out; } assert(bdrv_get_aio_context(base_bs) == aio_context); - base_name = base; } if (has_base_node) { @@ -2533,11 +2553,37 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, } assert(bdrv_get_aio_context(base_bs) == aio_context); bdrv_refresh_filename(base_bs); - base_name = base_bs->filename; } - /* Check for op blockers in the whole chain between bs and base */ - for (iter = bs; iter && iter != base_bs; iter = backing_bs(iter)) { + if (has_bottom) { + bottom_bs = bdrv_lookup_bs(NULL, bottom, errp); + if (!bottom_bs) { + goto out; + } + if (!bottom_bs->drv) { + error_setg(errp, "Node '%s' is not open", bottom); + goto out; + } + if (bottom_bs->drv->is_filter) { + error_setg(errp, "Node '%s' is a filter, use a non-filter node " + "as 'bottom'", bottom); + goto out; + } + if (!bdrv_chain_contains(bs, bottom_bs)) { + error_setg(errp, "Node '%s' is not in a chain starting from '%s'", + bottom, device); + goto out; + } + assert(bdrv_get_aio_context(bottom_bs) == aio_context); + } + + /* + * Check for op blockers in the whole chain between bs and base (or bottom) + */ + iter_end = has_bottom ? bdrv_filter_or_cow_bs(bottom_bs) : base_bs; + for (iter = bs; iter && iter != iter_end; + iter = bdrv_filter_or_cow_bs(iter)) + { if (bdrv_op_is_blocked(iter, BLOCK_OP_TYPE_STREAM, errp)) { goto out; } @@ -2551,9 +2597,6 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, goto out; } - /* backing_file string overrides base bs filename */ - base_name = has_backing_file ? backing_file : base_name; - if (has_auto_finalize && !auto_finalize) { job_flags |= JOB_MANUAL_FINALIZE; } @@ -2561,8 +2604,9 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, job_flags |= JOB_MANUAL_DISMISS; } - stream_start(has_job_id ? job_id : NULL, bs, base_bs, base_name, - job_flags, has_speed ? speed : 0, on_error, &local_err); + stream_start(has_job_id ? job_id : NULL, bs, base_bs, backing_file, + bottom_bs, job_flags, has_speed ? speed : 0, on_error, + filter_node_name, &local_err); if (local_err) { error_propagate(errp, local_err); goto out; @@ -2593,6 +2637,7 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, AioContext *aio_context; Error *local_err = NULL; int job_flags = JOB_DEFAULT; + uint64_t top_perm, top_shared; if (!has_speed) { speed = 0; @@ -2683,18 +2728,23 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, } } else if (has_base && base) { base_bs = bdrv_find_backing_image(top_bs, base); + if (base_bs == NULL) { + error_setg(errp, "Can't find '%s' in the backing chain", base); + goto out; + } } else { base_bs = bdrv_find_base(top_bs); - } - - if (base_bs == NULL) { - error_setg(errp, QERR_BASE_NOT_FOUND, base ? base : "NULL"); - goto out; + if (base_bs == NULL) { + error_setg(errp, "There is no backimg image"); + goto out; + } } assert(bdrv_get_aio_context(base_bs) == aio_context); - for (iter = top_bs; iter != backing_bs(base_bs); iter = backing_bs(iter)) { + for (iter = top_bs; iter != bdrv_filter_or_cow_bs(base_bs); + iter = bdrv_filter_or_cow_bs(iter)) + { if (bdrv_op_is_blocked(iter, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) { goto out; } @@ -2706,14 +2756,38 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, goto out; } - if (top_bs == bs) { + /* + * Active commit is required if and only if someone has taken a + * WRITE permission on the top node. Historically, we have always + * used active commit for top nodes, so continue that practice + * lest we possibly break clients that rely on this behavior, e.g. + * to later attach this node to a writing parent. + * (Active commit is never really wrong.) + */ + bdrv_get_cumulative_perm(top_bs, &top_perm, &top_shared); + if (top_perm & BLK_PERM_WRITE || + bdrv_skip_filters(top_bs) == bdrv_skip_filters(bs)) + { if (has_backing_file) { - error_setg(errp, "'backing-file' specified," - " but 'top' is the active layer"); + if (bdrv_skip_filters(top_bs) == bdrv_skip_filters(bs)) { + error_setg(errp, "'backing-file' specified," + " but 'top' is the active layer"); + } else { + error_setg(errp, "'backing-file' specified, but 'top' has a " + "writer on it"); + } goto out; } - commit_active_start(has_job_id ? job_id : NULL, bs, base_bs, - job_flags, speed, on_error, + if (!has_job_id) { + /* + * Emulate here what block_job_create() does, because it + * is possible that @bs != @top_bs (the block job should + * be named after @bs, even if @top_bs is the actual + * source) + */ + job_id = bdrv_get_device_name(bs); + } + commit_active_start(job_id, top_bs, base_bs, job_flags, speed, on_error, filter_node_name, NULL, NULL, false, &local_err); } else { BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs); @@ -2742,6 +2816,7 @@ static BlockJob *do_backup_common(BackupCommon *backup, { BlockJob *job = NULL; BdrvDirtyBitmap *bmap = NULL; + BackupPerf perf = { .max_workers = 64 }; int job_flags = JOB_DEFAULT; if (!backup->has_speed) { @@ -2766,6 +2841,18 @@ static BlockJob *do_backup_common(BackupCommon *backup, backup->compress = false; } + if (backup->x_perf) { + if (backup->x_perf->has_use_copy_range) { + perf.use_copy_range = backup->x_perf->use_copy_range; + } + if (backup->x_perf->has_max_workers) { + perf.max_workers = backup->x_perf->max_workers; + } + if (backup->x_perf->has_max_chunk) { + perf.max_chunk = backup->x_perf->max_chunk; + } + } + if ((backup->sync == MIRROR_SYNC_MODE_BITMAP) || (backup->sync == MIRROR_SYNC_MODE_INCREMENTAL)) { /* done before desugaring 'incremental' to print the right message */ @@ -2839,6 +2926,7 @@ static BlockJob *do_backup_common(BackupCommon *backup, backup->sync, bmap, backup->bitmap_mode, backup->compress, backup->filter_node_name, + &perf, backup->on_source_error, backup->on_target_error, job_flags, NULL, NULL, txn, errp); @@ -2901,6 +2989,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, bool has_auto_dismiss, bool auto_dismiss, Error **errp) { + BlockDriverState *unfiltered_bs; int job_flags = JOB_DEFAULT; if (!has_speed) { @@ -2941,7 +3030,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, } if (granularity & (granularity - 1)) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity", - "power of 2"); + "a power of 2"); return; } @@ -2952,10 +3041,19 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, return; } - if (!bs->backing && sync == MIRROR_SYNC_MODE_TOP) { + if (!bdrv_backing_chain_next(bs) && sync == MIRROR_SYNC_MODE_TOP) { sync = MIRROR_SYNC_MODE_FULL; } + if (!has_replaces) { + /* We want to mirror from @bs, but keep implicit filters on top */ + unfiltered_bs = bdrv_skip_implicit_filters(bs); + if (unfiltered_bs != bs) { + replaces = unfiltered_bs->node_name; + has_replaces = true; + } + } + if (has_replaces) { BlockDriverState *to_replace_bs; AioContext *replace_aio_context; @@ -3002,7 +3100,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, void qmp_drive_mirror(DriveMirror *arg, Error **errp) { BlockDriverState *bs; - BlockDriverState *source, *target_bs; + BlockDriverState *target_backing_bs, *target_bs; AioContext *aio_context; AioContext *old_context; BlockMirrorBackingMode backing_mode; @@ -3037,12 +3135,12 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) } flags = bs->open_flags | BDRV_O_RDWR; - source = backing_bs(bs); - if (!source && arg->sync == MIRROR_SYNC_MODE_TOP) { + target_backing_bs = bdrv_cow_bs(bdrv_skip_filters(bs)); + if (!target_backing_bs && arg->sync == MIRROR_SYNC_MODE_TOP) { arg->sync = MIRROR_SYNC_MODE_FULL; } if (arg->sync == MIRROR_SYNC_MODE_NONE) { - source = bs; + target_backing_bs = bs; } size = bdrv_getlength(bs); @@ -3068,7 +3166,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) /* Don't open backing image in create() */ flags |= BDRV_O_NO_BACKING; - if ((arg->sync == MIRROR_SYNC_MODE_FULL || !source) + if ((arg->sync == MIRROR_SYNC_MODE_FULL || !target_backing_bs) && arg->mode != NEW_IMAGE_MODE_EXISTING) { /* create new image w/o backing file */ @@ -3076,15 +3174,19 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) bdrv_img_create(arg->target, format, NULL, NULL, NULL, size, flags, false, &local_err); } else { + /* Implicit filters should not appear in the filename */ + BlockDriverState *explicit_backing = + bdrv_skip_implicit_filters(target_backing_bs); + switch (arg->mode) { case NEW_IMAGE_MODE_EXISTING: break; case NEW_IMAGE_MODE_ABSOLUTE_PATHS: /* create new image with backing file */ - bdrv_refresh_filename(source); + bdrv_refresh_filename(explicit_backing); bdrv_img_create(arg->target, format, - source->filename, - source->drv->format_name, + explicit_backing->filename, + explicit_backing->drv->format_name, NULL, size, flags, false, &local_err); break; default: @@ -3483,13 +3585,14 @@ void qmp_x_blockdev_reopen(BlockdevOptions *options, Error **errp) /* Check for the selected node name */ if (!options->has_node_name) { - error_setg(errp, "Node name not specified"); + error_setg(errp, "node-name not specified"); goto fail; } bs = bdrv_find_node(options->node_name); if (!bs) { - error_setg(errp, "Cannot find node named '%s'", options->node_name); + error_setg(errp, "Failed to find node with node-name='%s'", + options->node_name); goto fail; } @@ -3520,7 +3623,7 @@ void qmp_blockdev_del(const char *node_name, Error **errp) bs = bdrv_find_node(node_name); if (!bs) { - error_setg(errp, "Cannot find node %s", node_name); + error_setg(errp, "Failed to find node with node-name='%s'", node_name); return; } if (bdrv_has_blk(bs)) { @@ -3610,28 +3713,25 @@ void qmp_x_blockdev_change(const char *parent, bool has_child, BlockJobInfoList *qmp_query_block_jobs(Error **errp) { - BlockJobInfoList *head = NULL, **p_next = &head; + BlockJobInfoList *head = NULL, **tail = &head; BlockJob *job; for (job = block_job_next(NULL); job; job = block_job_next(job)) { - BlockJobInfoList *elem; + BlockJobInfo *value; AioContext *aio_context; if (block_job_is_internal(job)) { continue; } - elem = g_new0(BlockJobInfoList, 1); aio_context = blk_get_aio_context(job->blk); aio_context_acquire(aio_context); - elem->value = block_job_query(job, errp); + value = block_job_query(job, errp); aio_context_release(aio_context); - if (!elem->value) { - g_free(elem); + if (!value) { qapi_free_BlockJobInfoList(head); return NULL; } - *p_next = elem; - p_next = &elem->next; + QAPI_LIST_APPEND(tail, value); } return head; @@ -3646,7 +3746,7 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread, bs = bdrv_find_node(node_name); if (!bs) { - error_setg(errp, "Cannot find node %s", node_name); + error_setg(errp, "Failed to find node with node-name='%s'", node_name); return; } diff --git a/blockjob.c b/blockjob.c index 470facfd47..207e8c7fd9 100644 --- a/blockjob.c +++ b/blockjob.c @@ -212,15 +212,19 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, Error **errp) { BdrvChild *c; + bool need_context_ops; bdrv_ref(bs); - if (job->job.aio_context != qemu_get_aio_context()) { + + need_context_ops = bdrv_get_aio_context(bs) != job->job.aio_context; + + if (need_context_ops && job->job.aio_context != qemu_get_aio_context()) { aio_context_release(job->job.aio_context); } c = bdrv_root_attach_child(bs, name, &child_job, 0, job->job.aio_context, perm, shared_perm, job, errp); - if (job->job.aio_context != qemu_get_aio_context()) { + if (need_context_ops && job->job.aio_context != qemu_get_aio_context()) { aio_context_acquire(job->job.aio_context); } if (c == NULL) { @@ -254,28 +258,36 @@ static bool job_timer_pending(Job *job) return timer_pending(&job->sleep_timer); } -void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) +bool block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) { + const BlockJobDriver *drv = block_job_driver(job); int64_t old_speed = job->speed; - if (job_apply_verb(&job->job, JOB_VERB_SET_SPEED, errp)) { - return; + if (job_apply_verb(&job->job, JOB_VERB_SET_SPEED, errp) < 0) { + return false; } if (speed < 0) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "speed", "a non-negative value"); - return; + return false; } ratelimit_set_speed(&job->limit, speed, BLOCK_JOB_SLICE_TIME); job->speed = speed; + + if (drv->set_speed) { + drv->set_speed(job, speed); + } + if (speed && speed <= old_speed) { - return; + return true; } /* kick only if a timer is pending */ job_enter_cond(&job->job, job_timer_pending); + + return true; } int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n) @@ -298,7 +310,7 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) info = g_new0(BlockJobInfo, 1); info->type = g_strdup(job_type_str(&job->job)); info->device = g_strdup(job->job.id); - info->busy = atomic_read(&job->job.busy); + info->busy = qatomic_read(&job->job.busy); info->paused = job->job.pause_count > 0; info->offset = job->job.progress.current; info->len = job->job.progress.total; @@ -308,8 +320,12 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) info->status = job->job.status; info->auto_finalize = job->job.auto_finalize; info->auto_dismiss = job->job.auto_dismiss; - info->has_error = job->job.ret != 0; - info->error = job->job.ret ? g_strdup(strerror(-job->job.ret)) : NULL; + if (job->job.ret) { + info->has_error = true; + info->error = job->job.err ? + g_strdup(error_get_pretty(job->job.err)) : + g_strdup(strerror(-job->job.ret)); + } return info; } @@ -346,7 +362,7 @@ static void block_job_event_completed(Notifier *n, void *opaque) } if (job->job.ret < 0) { - msg = strerror(-job->job.ret); + msg = error_get_pretty(job->job.err); } qapi_event_send_block_job_completed(job_type(&job->job), @@ -448,12 +464,8 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, /* Only set speed when necessary to avoid NotSupported error */ if (speed != 0) { - Error *local_err = NULL; - - block_job_set_speed(job, speed, &local_err); - if (local_err) { + if (!block_job_set_speed(job, speed, errp)) { job_early_fail(&job->job); - error_propagate(errp, local_err); return NULL; } } diff --git a/bsd-user/Makefile.objs b/bsd-user/Makefile.objs deleted file mode 100644 index 5e77f57782..0000000000 --- a/bsd-user/Makefile.objs +++ /dev/null @@ -1,2 +0,0 @@ -obj-y = main.o bsdload.o elfload.o mmap.o signal.o strace.o syscall.o \ - uaccess.o diff --git a/bsd-user/arm/target_arch_sysarch.h b/bsd-user/arm/target_arch_sysarch.h new file mode 100644 index 0000000000..632a5cd453 --- /dev/null +++ b/bsd-user/arm/target_arch_sysarch.h @@ -0,0 +1,78 @@ +/* + * arm sysarch() system call emulation + * + * Copyright (c) 2013 Stacey D. Son + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef BSD_USER_ARCH_SYSARCH_H_ +#define BSD_USER_ARCH_SYSARCH_H_ + +#include "target_syscall.h" +#include "target_arch.h" + +static inline abi_long do_freebsd_arch_sysarch(CPUARMState *env, int op, + abi_ulong parms) +{ + int ret = 0; + + switch (op) { + case TARGET_FREEBSD_ARM_SYNC_ICACHE: + case TARGET_FREEBSD_ARM_DRAIN_WRITEBUF: + break; + + case TARGET_FREEBSD_ARM_SET_TP: + target_cpu_set_tls(env, parms); + break; + + case TARGET_FREEBSD_ARM_GET_TP: + ret = target_cpu_get_tls(env); + break; + + default: + ret = -TARGET_EINVAL; + break; + } + return ret; +} + +static inline void do_freebsd_arch_print_sysarch( + const struct syscallname *name, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6) +{ + + switch (arg1) { + case TARGET_FREEBSD_ARM_SYNC_ICACHE: + gemu_log("%s(ARM_SYNC_ICACHE, ...)", name->name); + break; + + case TARGET_FREEBSD_ARM_DRAIN_WRITEBUF: + gemu_log("%s(ARM_DRAIN_WRITEBUF, ...)", name->name); + break; + + case TARGET_FREEBSD_ARM_SET_TP: + gemu_log("%s(ARM_SET_TP, 0x" TARGET_ABI_FMT_lx ")", name->name, arg2); + break; + + case TARGET_FREEBSD_ARM_GET_TP: + gemu_log("%s(ARM_GET_TP, 0x" TARGET_ABI_FMT_lx ")", name->name, arg2); + break; + + default: + gemu_log("UNKNOWN OP: %d, " TARGET_ABI_FMT_lx ")", (int)arg1, arg2); + } +} + +#endif /*!BSD_USER_ARCH_SYSARCH_H_ */ diff --git a/bsd-user/arm/target_syscall.h b/bsd-user/arm/target_syscall.h new file mode 100644 index 0000000000..ef4b37f017 --- /dev/null +++ b/bsd-user/arm/target_syscall.h @@ -0,0 +1,36 @@ +#ifndef BSD_USER_ARCH_SYSCALL_H_ +#define BSD_USER_ARCH_SYSCALL_H_ + +struct target_pt_regs { + abi_long uregs[17]; +}; + +#define ARM_cpsr uregs[16] +#define ARM_pc uregs[15] +#define ARM_lr uregs[14] +#define ARM_sp uregs[13] +#define ARM_ip uregs[12] +#define ARM_fp uregs[11] +#define ARM_r10 uregs[10] +#define ARM_r9 uregs[9] +#define ARM_r8 uregs[8] +#define ARM_r7 uregs[7] +#define ARM_r6 uregs[6] +#define ARM_r5 uregs[5] +#define ARM_r4 uregs[4] +#define ARM_r3 uregs[3] +#define ARM_r2 uregs[2] +#define ARM_r1 uregs[1] +#define ARM_r0 uregs[0] + +#define ARM_SYSCALL_BASE 0 /* XXX: FreeBSD only */ + +#define TARGET_FREEBSD_ARM_SYNC_ICACHE 0 +#define TARGET_FREEBSD_ARM_DRAIN_WRITEBUF 1 +#define TARGET_FREEBSD_ARM_SET_TP 2 +#define TARGET_FREEBSD_ARM_GET_TP 3 + +#define TARGET_HW_MACHINE "arm" +#define TARGET_HW_MACHINE_ARCH "armv6" + +#endif /* !BSD_USER_ARCH_SYSCALL_H_ */ diff --git a/bsd-user/elfload.c b/bsd-user/elfload.c index 32378af7b2..5f4d824d78 100644 --- a/bsd-user/elfload.c +++ b/bsd-user/elfload.c @@ -737,7 +737,7 @@ static void padzero(abi_ulong elf_bss, abi_ulong last_bss) end_addr1 = REAL_HOST_PAGE_ALIGN(elf_bss); end_addr = HOST_PAGE_ALIGN(elf_bss); if (end_addr1 < end_addr) { - mmap((void *)g2h(end_addr1), end_addr - end_addr1, + mmap((void *)g2h_untagged(end_addr1), end_addr - end_addr1, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE|MAP_ANON, -1, 0); } diff --git a/bsd-user/freebsd/os-strace.h b/bsd-user/freebsd/os-strace.h new file mode 100644 index 0000000000..a222f093b5 --- /dev/null +++ b/bsd-user/freebsd/os-strace.h @@ -0,0 +1,29 @@ +/* + * FreeBSD dependent strace print functions + * + * Copyright (c) 2013 Stacey D. Son + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "target_arch_sysarch.h" /* architecture dependent functions */ + + +static inline void do_os_print_sysarch(const struct syscallname *name, + abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, + abi_long arg5, abi_long arg6) +{ + /* This is arch dependent */ + do_freebsd_arch_print_sysarch(name, arg1, arg2, arg3, arg4, arg5, arg6); +} diff --git a/bsd-user/freebsd/strace.list b/bsd-user/freebsd/strace.list index 2800a2d4eb..b01b5f36e8 100644 --- a/bsd-user/freebsd/strace.list +++ b/bsd-user/freebsd/strace.list @@ -28,18 +28,37 @@ { TARGET_FREEBSD_NR___acl_set_fd, "__acl_set_fd", "%s(%d, %d, %#x)", NULL, NULL }, { TARGET_FREEBSD_NR___acl_set_file, "__acl_set_file", "%s(\"%s\", %d, %#x)", NULL, NULL }, { TARGET_FREEBSD_NR___acl_set_link, "__acl_set_link", "%s(\"%s\", %d, %#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR___getcwd, "__getcwd", NULL, NULL, NULL }, { TARGET_FREEBSD_NR___semctl, "__semctl", NULL, NULL, NULL }, { TARGET_FREEBSD_NR___syscall, "__syscall", NULL, NULL, NULL }, { TARGET_FREEBSD_NR___sysctl, "__sysctl", NULL, print_sysctl, NULL }, { TARGET_FREEBSD_NR__umtx_op, "_umtx_op", "%s(%#x, %d, %d, %#x, %#x)", NULL, NULL }, +#if defined(__FreeBSD_version) && __FreeBSD_version < 1000000 +{ TARGET_FREEBSD_NR__umtx_lock, "__umtx_lock", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR__umtx_unlock, "__umtx_unlock", NULL, NULL, NULL }, +#endif { TARGET_FREEBSD_NR_accept, "accept", "%s(%d,%#x,%#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR_accept4, "accept4", "%s(%d,%d,%#x,%#x)", NULL, NULL }, { TARGET_FREEBSD_NR_access, "access", "%s(\"%s\",%#o)", NULL, NULL }, { TARGET_FREEBSD_NR_acct, "acct", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_adjtime, "adjtime", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_bind, "bind", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_bindat, "bindat", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_break, "break", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_cap_enter, "cap_enter", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_cap_fcntls_get, "cap_fcntls_get", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_cap_fcntls_limit, "cap_fcntls_limit", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_cap_getmode, "cap_getmode", NULL, NULL, NULL }, +#if defined(__FreeBSD_version) && __FreeBSD_version < 1000000 +{ TARGET_FREEBSD_NR_cap_getrights, "cap_getrights", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_cap_new, "cap_new", NULL, NULL, NULL }, +#endif +{ TARGET_FREEBSD_NR_cap_ioctls_get, "cap_ioctls_get", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_cap_ioctls_limit, "cap_ioctls_limit", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_cap_rights_limit, "cap_rights_limit", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_chdir, "chdir", "%s(\"%s\")", NULL, NULL }, { TARGET_FREEBSD_NR_chflags, "chflags", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_chflagsat, "chflagsat", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_chmod, "chmod", "%s(\"%s\",%#o)", NULL, NULL }, { TARGET_FREEBSD_NR_chown, "chown", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_chroot, "chroot", NULL, NULL, NULL }, @@ -48,6 +67,9 @@ { TARGET_FREEBSD_NR_clock_settime, "clock_settime", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_close, "close", "%s(%d)", NULL, NULL }, { TARGET_FREEBSD_NR_connect, "connect", "%s(%d,%#x,%d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_connectat, "connectat", "%s(%d,%d,%#x,%d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_cpuset_getdomain, "cpuset_getdomain", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_cpuset_setdomain, "cpuset_setdomain", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_dup, "dup", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_dup2, "dup2", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_eaccess, "eaccess", "%s(\"%s\",%#x)", NULL, NULL }, @@ -61,7 +83,7 @@ { TARGET_FREEBSD_NR_extattr_get_file, "extattr_get_file", "%s(\"%s\", %d, \"%s\", %#x, %d)", NULL, NULL }, { TARGET_FREEBSD_NR_extattr_get_file, "extattr_get_link", "%s(\"%s\", %d, \"%s\", %#x, %d)", NULL, NULL }, { TARGET_FREEBSD_NR_extattr_list_fd, "extattr_list_fd", "%s(%d, %d, %#x, %d)", NULL, NULL }, -{ TARGET_FREEBSD_NR_extattr_list_file, "extattr_list_file", "%s(\"%s\", %d, %#x, %d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_extattr_list_file, "extattr_list_file", "%s(\"%s\", %#x, %d)", NULL, NULL }, { TARGET_FREEBSD_NR_extattr_list_link, "extattr_list_link", "%s(\"%s\", %d, %#x, %d)", NULL, NULL }, { TARGET_FREEBSD_NR_extattr_set_fd, "extattr_set_fd", "%s(%d, %d, \"%s\", %#x, %d)", NULL, NULL }, { TARGET_FREEBSD_NR_extattr_set_file, "extattr_set_file", "%s(\"%s\", %d, \"%s\", %#x, %d)", NULL, NULL }, @@ -71,26 +93,34 @@ { TARGET_FREEBSD_NR_fchmod, "fchmod", "%s(%d,%#o)", NULL, NULL }, { TARGET_FREEBSD_NR_fchown, "fchown", "%s(%d,%d,%d)", NULL, NULL }, { TARGET_FREEBSD_NR_fcntl, "fcntl", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_fdatasync, "fdatasync", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_fexecve, "fexecve", NULL, print_execve, NULL }, { TARGET_FREEBSD_NR_fhopen, "fhopen", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_fhstat, "fhstat", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_fhstatfs, "fhstatfs", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_freebsd11_fhstat, "freebsd11_fhstat", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_freebsd11_fhstatfs, "freebsd11_fhstatfs", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_flock, "flock", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_fork, "fork", "%s()", NULL, NULL }, { TARGET_FREEBSD_NR_fpathconf, "fpathconf", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_fstat, "fstat", "%s(%d,%#x)", NULL, NULL }, { TARGET_FREEBSD_NR_fstatat, "fstatat", "%s(%d,\"%s\", %#x, %d)", NULL, NULL }, { TARGET_FREEBSD_NR_fstatfs, "fstatfs", "%s(%d,%#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR_freebsd11_fstat, "freebsd11_fstat", "%s(%d,%#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR_freebsd11_fstatat, "freebsd11_fstatat", "%s(%d,\"%s\", %#x, %d)", NULL, NULL }, +{ TARGET_FREEBSD_NR_freebsd11_fstatfs, "freebsd11_fstatfs", "%s(%d,%#x)", NULL, NULL }, { TARGET_FREEBSD_NR_fsync, "fsync", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_ftruncate, "ftruncate", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_futimens, "futimens", "%s(%d,%p)", NULL, NULL }, { TARGET_FREEBSD_NR_futimes, "futimes", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_getcontext, "getcontext", "%s(%#x)", NULL, NULL }, { TARGET_FREEBSD_NR_getdirentries, "getdirentries", NULL, NULL, NULL }, -{ TARGET_FREEBSD_NR_freebsd6_mmap, "freebsd6_mmap", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_freebsd11_getdirentries, "freebsd11_getdirentries", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_getegid, "getegid", "%s()", NULL, NULL }, { TARGET_FREEBSD_NR_geteuid, "geteuid", "%s()", NULL, NULL }, { TARGET_FREEBSD_NR_getfh, "getfh", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_getfsstat, "getfsstat", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_freebsd11_getfsstat, "freebsd11_getfsstat", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_getgid, "getgid", "%s()", NULL, NULL }, { TARGET_FREEBSD_NR_getgroups, "getgroups", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_getitimer, "getitimer", NULL, NULL, NULL }, @@ -101,6 +131,7 @@ { TARGET_FREEBSD_NR_getpid, "getpid", "%s()", NULL, NULL }, { TARGET_FREEBSD_NR_getppid, "getppid", "%s()", NULL, NULL }, { TARGET_FREEBSD_NR_getpriority, "getpriority", "%s(%#x,%#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR_getrandom, "getrandom", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_getresgid, "getresgid", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_getresuid, "getresuid", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_getrlimit, "getrlimit", NULL, NULL, NULL }, @@ -112,8 +143,12 @@ { TARGET_FREEBSD_NR_getuid, "getuid", "%s()", NULL, NULL }, { TARGET_FREEBSD_NR_ioctl, "ioctl", NULL, print_ioctl, NULL }, { TARGET_FREEBSD_NR_issetugid, "issetugid", "%s()", NULL, NULL }, +{ TARGET_FREEBSD_NR_freebsd11_kevent, "freebsd11_kevent", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_kevent, "kevent", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_kill, "kill", NULL, NULL, NULL }, +#if defined(__FreeBSD_version) && __FreeBSD_version < 1000000 +{ TARGET_FREEBSD_NR_killpg, "killpg", NULL, NULL, NULL }, +#endif { TARGET_FREEBSD_NR_kqueue, "kqueue", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_ktrace, "ktrace", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_lchown, "lchown", NULL, NULL, NULL }, @@ -121,13 +156,15 @@ { TARGET_FREEBSD_NR_listen, "listen", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_lpathconf, "lpathconf", "%s(\"%s\", %d)", NULL, NULL }, { TARGET_FREEBSD_NR_lseek, "lseek", NULL, NULL, NULL }, -{ TARGET_FREEBSD_NR_lstat, "lstat", "%s(\"%s\",%p)", NULL, NULL }, +{ TARGET_FREEBSD_NR_freebsd11_lstat, "freebsd11_lstat", "%s(\"%s\",%p)", NULL, NULL }, { TARGET_FREEBSD_NR_madvise, "madvise", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_mincore, "mincore", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_minherit, "minherit", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_mkdir, "mkdir", "%s(\"%s\",%#o)", NULL, NULL }, { TARGET_FREEBSD_NR_mkfifo, "mkfifo", NULL, NULL, NULL }, -{ TARGET_FREEBSD_NR_mknod, "mknod", "%s(\"%s\",%#o,%#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR_mknodat, "mknodat", "%s(%d, \"%s\",%#o,%#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR_freebsd11_mknod, "freebsd11_mknod", "%s(\"%s\",%#o,%#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR_freebsd11_mknodat, "freebsd11_mknodat", "%s(%d, \"%s\",%#o,%#x)", NULL, NULL }, { TARGET_FREEBSD_NR_mlock, "mlock", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_mlockall, "mlockall", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_mmap, "mmap", NULL, NULL, print_syscall_ret_addr }, @@ -146,8 +183,10 @@ { TARGET_FREEBSD_NR_open, "open", "%s(\"%s\",%#x,%#o)", NULL, NULL }, { TARGET_FREEBSD_NR_openat, "openat", "%s(%d, \"%s\",%#x,%#o)", NULL, NULL }, { TARGET_FREEBSD_NR_pathconf, "pathconf", "%s(\"%s\", %d)", NULL, NULL }, -{ TARGET_FREEBSD_NR_pipe, "pipe", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_freebsd10_pipe, "freebsd10_pipe", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_pipe2, "pipe2", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_poll, "poll", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_posix_fallocate, "posix_fallocate", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_pread, "pread", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_preadv, "preadv", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_profil, "profil", NULL, NULL, NULL }, @@ -167,6 +206,8 @@ { TARGET_FREEBSD_NR_rmdir, "rmdir", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_rtprio_thread, "rtprio_thread", "%s(%d, %d, %p)", NULL, NULL }, { TARGET_FREEBSD_NR_sbrk, "sbrk", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_sched_get_priority_max, "sched_get_priority_max", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_sched_get_priority_min, "sched_get_priority_min", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_sched_yield, "sched_yield", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_select, "select", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_semget, "semget", NULL, NULL, NULL }, @@ -205,12 +246,17 @@ { TARGET_FREEBSD_NR_socket, "socket", "%s(%d,%d,%d)", NULL, NULL }, { TARGET_FREEBSD_NR_socketpair, "socketpair", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_sstk, "sstk", NULL, NULL, NULL }, -{ TARGET_FREEBSD_NR_stat, "stat", "%s(\"%s\",%p)", NULL, NULL }, -{ TARGET_FREEBSD_NR_statfs, "statfs", "%s(\"%s\",%p)", NULL, NULL }, +{ TARGET_FREEBSD_NR_freebsd11_stat, "freebsd11_stat", "%s(\"%s\",%p)", NULL, NULL }, +{ TARGET_FREEBSD_NR_freebsd11_statfs, "freebsd11_statfs", "%s(\"%s\",%p)", NULL, NULL }, { TARGET_FREEBSD_NR_symlink, "symlink", "%s(\"%s\",\"%s\")", NULL, NULL }, { TARGET_FREEBSD_NR_sync, "sync", NULL, NULL, NULL }, -{ TARGET_FREEBSD_NR_sysarch, "sysarch", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_sysarch, "sysarch", NULL, print_sysarch, NULL }, { TARGET_FREEBSD_NR_syscall, "syscall", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_ktimer_create, "timer_create" , NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_ktimer_delete, "timer_delete" , NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_ktimer_settime, "timer_settime" , NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_ktimer_gettime, "timer_gettime" , NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_ktimer_getoverrun, "timer_getoverrun" , NULL, NULL, NULL }, { TARGET_FREEBSD_NR_thr_create, "thr_create", "%s(%#x, %#x, %d)", NULL, NULL }, { TARGET_FREEBSD_NR_thr_exit, "thr_exit", "%s(%#x)", NULL, NULL }, { TARGET_FREEBSD_NR_thr_kill, "thr_kill", "%s(%d, %#x)", NULL, NULL }, @@ -225,7 +271,10 @@ { TARGET_FREEBSD_NR_unlink, "unlink", "%s(\"%s\")", NULL, NULL }, { TARGET_FREEBSD_NR_unmount, "unmount", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_utimes, "utimes", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_utimensat, "utimensat", "%s(%d,%s,%p,%#x)", NULL, NULL }, { TARGET_FREEBSD_NR_vfork, "vfork", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_wait4, "wait4", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_wait6, "wait6", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_write, "write", "%s(%d,%#x,%d)", NULL, NULL }, { TARGET_FREEBSD_NR_writev, "writev", "%s(%d,%p,%#x)", NULL, NULL }, +{ TARGET_FREEBSD_NR_posix_openpt, "posix_openpt", "%s(%d)", NULL, NULL }, diff --git a/bsd-user/freebsd/syscall_nr.h b/bsd-user/freebsd/syscall_nr.h index d849024792..7f73a6d0f1 100644 --- a/bsd-user/freebsd/syscall_nr.h +++ b/bsd-user/freebsd/syscall_nr.h @@ -1,11 +1,11 @@ /* * System call numbers. * - * created from FreeBSD: releng/9.1/sys/kern/syscalls.master 229723 - * 2012-01-06 19:29:16Z jhb + * DO NOT EDIT-- this file is automatically @generated. + * $FreeBSD$ */ -#define TARGET_FREEBSD_NR_syscall 0 +#define TARGET_FREEBSD_NR_syscall 0 #define TARGET_FREEBSD_NR_exit 1 #define TARGET_FREEBSD_NR_fork 2 #define TARGET_FREEBSD_NR_read 3 @@ -13,438 +13,503 @@ #define TARGET_FREEBSD_NR_open 5 #define TARGET_FREEBSD_NR_close 6 #define TARGET_FREEBSD_NR_wait4 7 - /* 8 is old creat */ + /* 8 is old creat */ #define TARGET_FREEBSD_NR_link 9 -#define TARGET_FREEBSD_NR_unlink 10 - /* 11 is obsolete execv */ +#define TARGET_FREEBSD_NR_unlink 10 + /* 11 is obsolete execv */ #define TARGET_FREEBSD_NR_chdir 12 -#define TARGET_FREEBSD_NR_fchdir 13 -#define TARGET_FREEBSD_NR_mknod 14 +#define TARGET_FREEBSD_NR_fchdir 13 +#define TARGET_FREEBSD_NR_freebsd11_mknod 14 #define TARGET_FREEBSD_NR_chmod 15 #define TARGET_FREEBSD_NR_chown 16 #define TARGET_FREEBSD_NR_break 17 -#define TARGET_FREEBSD_NR_freebsd4_getfsstat 18 - /* 19 is old lseek */ -#define TARGET_FREEBSD_NR_getpid 20 + /* 18 is freebsd4 getfsstat */ + /* 19 is old lseek */ +#define TARGET_FREEBSD_NR_getpid 20 #define TARGET_FREEBSD_NR_mount 21 -#define TARGET_FREEBSD_NR_unmount 22 -#define TARGET_FREEBSD_NR_setuid 23 -#define TARGET_FREEBSD_NR_getuid 24 -#define TARGET_FREEBSD_NR_geteuid 25 -#define TARGET_FREEBSD_NR_ptrace 26 -#define TARGET_FREEBSD_NR_recvmsg 27 -#define TARGET_FREEBSD_NR_sendmsg 28 -#define TARGET_FREEBSD_NR_recvfrom 29 -#define TARGET_FREEBSD_NR_accept 30 +#define TARGET_FREEBSD_NR_unmount 22 +#define TARGET_FREEBSD_NR_setuid 23 +#define TARGET_FREEBSD_NR_getuid 24 +#define TARGET_FREEBSD_NR_geteuid 25 +#define TARGET_FREEBSD_NR_ptrace 26 +#define TARGET_FREEBSD_NR_recvmsg 27 +#define TARGET_FREEBSD_NR_sendmsg 28 +#define TARGET_FREEBSD_NR_recvfrom 29 +#define TARGET_FREEBSD_NR_accept 30 #define TARGET_FREEBSD_NR_getpeername 31 #define TARGET_FREEBSD_NR_getsockname 32 -#define TARGET_FREEBSD_NR_access 33 -#define TARGET_FREEBSD_NR_chflags 34 -#define TARGET_FREEBSD_NR_fchflags 35 +#define TARGET_FREEBSD_NR_access 33 +#define TARGET_FREEBSD_NR_chflags 34 +#define TARGET_FREEBSD_NR_fchflags 35 #define TARGET_FREEBSD_NR_sync 36 #define TARGET_FREEBSD_NR_kill 37 - /* 38 is old stat */ -#define TARGET_FREEBSD_NR_getppid 39 - /* 40 is old lstat */ + /* 38 is old stat */ +#define TARGET_FREEBSD_NR_getppid 39 + /* 40 is old lstat */ #define TARGET_FREEBSD_NR_dup 41 -#define TARGET_FREEBSD_NR_pipe 42 -#define TARGET_FREEBSD_NR_getegid 43 -#define TARGET_FREEBSD_NR_profil 44 -#define TARGET_FREEBSD_NR_ktrace 45 - /* 46 is old sigaction */ -#define TARGET_FREEBSD_NR_getgid 47 - /* 48 is old sigprocmask */ -#define TARGET_FREEBSD_NR_getlogin 49 -#define TARGET_FREEBSD_NR_setlogin 50 +#define TARGET_FREEBSD_NR_freebsd10_pipe 42 +#define TARGET_FREEBSD_NR_getegid 43 +#define TARGET_FREEBSD_NR_profil 44 +#define TARGET_FREEBSD_NR_ktrace 45 + /* 46 is old sigaction */ +#define TARGET_FREEBSD_NR_getgid 47 + /* 48 is old sigprocmask */ +#define TARGET_FREEBSD_NR_getlogin 49 +#define TARGET_FREEBSD_NR_setlogin 50 #define TARGET_FREEBSD_NR_acct 51 - /* 52 is old sigpending */ + /* 52 is old sigpending */ #define TARGET_FREEBSD_NR_sigaltstack 53 #define TARGET_FREEBSD_NR_ioctl 54 -#define TARGET_FREEBSD_NR_reboot 55 -#define TARGET_FREEBSD_NR_revoke 56 -#define TARGET_FREEBSD_NR_symlink 57 -#define TARGET_FREEBSD_NR_readlink 58 -#define TARGET_FREEBSD_NR_execve 59 +#define TARGET_FREEBSD_NR_reboot 55 +#define TARGET_FREEBSD_NR_revoke 56 +#define TARGET_FREEBSD_NR_symlink 57 +#define TARGET_FREEBSD_NR_readlink 58 +#define TARGET_FREEBSD_NR_execve 59 #define TARGET_FREEBSD_NR_umask 60 -#define TARGET_FREEBSD_NR_chroot 61 - /* 62 is old fstat */ - /* 63 is old getkerninfo */ - /* 64 is old getpagesize */ +#define TARGET_FREEBSD_NR_chroot 61 + /* 62 is old fstat */ + /* 63 is old getkerninfo */ + /* 64 is old getpagesize */ #define TARGET_FREEBSD_NR_msync 65 #define TARGET_FREEBSD_NR_vfork 66 - /* 67 is obsolete vread */ - /* 68 is obsolete vwrite */ + /* 67 is obsolete vread */ + /* 68 is obsolete vwrite */ #define TARGET_FREEBSD_NR_sbrk 69 #define TARGET_FREEBSD_NR_sstk 70 - /* 71 is old mmap */ -#define TARGET_FREEBSD_NR_vadvise 72 -#define TARGET_FREEBSD_NR_munmap 73 -#define TARGET_FREEBSD_NR_mprotect 74 -#define TARGET_FREEBSD_NR_madvise 75 - /* 76 is obsolete vhangup */ - /* 77 is obsolete vlimit */ -#define TARGET_FREEBSD_NR_mincore 78 -#define TARGET_FREEBSD_NR_getgroups 79 -#define TARGET_FREEBSD_NR_setgroups 80 -#define TARGET_FREEBSD_NR_getpgrp 81 -#define TARGET_FREEBSD_NR_setpgid 82 -#define TARGET_FREEBSD_NR_setitimer 83 - /* 84 is old wait */ -#define TARGET_FREEBSD_NR_swapon 85 -#define TARGET_FREEBSD_NR_getitimer 86 - /* 87 is old gethostname */ - /* 88 is old sethostname */ + /* 71 is old mmap */ +#define TARGET_FREEBSD_NR_freebsd11_vadvise 72 +#define TARGET_FREEBSD_NR_munmap 73 +#define TARGET_FREEBSD_NR_mprotect 74 +#define TARGET_FREEBSD_NR_madvise 75 + /* 76 is obsolete vhangup */ + /* 77 is obsolete vlimit */ +#define TARGET_FREEBSD_NR_mincore 78 +#define TARGET_FREEBSD_NR_getgroups 79 +#define TARGET_FREEBSD_NR_setgroups 80 +#define TARGET_FREEBSD_NR_getpgrp 81 +#define TARGET_FREEBSD_NR_setpgid 82 +#define TARGET_FREEBSD_NR_setitimer 83 + /* 84 is old wait */ +#define TARGET_FREEBSD_NR_swapon 85 +#define TARGET_FREEBSD_NR_getitimer 86 + /* 87 is old gethostname */ + /* 88 is old sethostname */ #define TARGET_FREEBSD_NR_getdtablesize 89 #define TARGET_FREEBSD_NR_dup2 90 #define TARGET_FREEBSD_NR_fcntl 92 -#define TARGET_FREEBSD_NR_select 93 +#define TARGET_FREEBSD_NR_select 93 #define TARGET_FREEBSD_NR_fsync 95 #define TARGET_FREEBSD_NR_setpriority 96 -#define TARGET_FREEBSD_NR_socket 97 -#define TARGET_FREEBSD_NR_connect 98 - /* 99 is old accept */ +#define TARGET_FREEBSD_NR_socket 97 +#define TARGET_FREEBSD_NR_connect 98 + /* 99 is old accept */ #define TARGET_FREEBSD_NR_getpriority 100 - /* 101 is old send */ - /* 102 is old recv */ - /* 103 is old sigreturn */ + /* 101 is old send */ + /* 102 is old recv */ + /* 103 is old sigreturn */ #define TARGET_FREEBSD_NR_bind 104 #define TARGET_FREEBSD_NR_setsockopt 105 -#define TARGET_FREEBSD_NR_listen 106 - /* 107 is obsolete vtimes */ - /* 108 is old sigvec */ - /* 109 is old sigblock */ - /* 110 is old sigsetmask */ - /* 111 is old sigsuspend */ - /* 112 is old sigstack */ - /* 113 is old recvmsg */ - /* 114 is old sendmsg */ - /* 115 is obsolete vtrace */ +#define TARGET_FREEBSD_NR_listen 106 + /* 107 is obsolete vtimes */ + /* 108 is old sigvec */ + /* 109 is old sigblock */ + /* 110 is old sigsetmask */ + /* 111 is old sigsuspend */ + /* 112 is old sigstack */ + /* 113 is old recvmsg */ + /* 114 is old sendmsg */ + /* 115 is obsolete vtrace */ #define TARGET_FREEBSD_NR_gettimeofday 116 -#define TARGET_FREEBSD_NR_getrusage 117 +#define TARGET_FREEBSD_NR_getrusage 117 #define TARGET_FREEBSD_NR_getsockopt 118 #define TARGET_FREEBSD_NR_readv 120 -#define TARGET_FREEBSD_NR_writev 121 +#define TARGET_FREEBSD_NR_writev 121 #define TARGET_FREEBSD_NR_settimeofday 122 -#define TARGET_FREEBSD_NR_fchown 123 -#define TARGET_FREEBSD_NR_fchmod 124 - /* 125 is old recvfrom */ -#define TARGET_FREEBSD_NR_setreuid 126 -#define TARGET_FREEBSD_NR_setregid 127 -#define TARGET_FREEBSD_NR_rename 128 - /* 129 is old truncate */ - /* 130 is old ftruncate */ +#define TARGET_FREEBSD_NR_fchown 123 +#define TARGET_FREEBSD_NR_fchmod 124 + /* 125 is old recvfrom */ +#define TARGET_FREEBSD_NR_setreuid 126 +#define TARGET_FREEBSD_NR_setregid 127 +#define TARGET_FREEBSD_NR_rename 128 + /* 129 is old truncate */ + /* 130 is old ftruncate */ #define TARGET_FREEBSD_NR_flock 131 -#define TARGET_FREEBSD_NR_mkfifo 132 -#define TARGET_FREEBSD_NR_sendto 133 -#define TARGET_FREEBSD_NR_shutdown 134 +#define TARGET_FREEBSD_NR_mkfifo 132 +#define TARGET_FREEBSD_NR_sendto 133 +#define TARGET_FREEBSD_NR_shutdown 134 #define TARGET_FREEBSD_NR_socketpair 135 #define TARGET_FREEBSD_NR_mkdir 136 #define TARGET_FREEBSD_NR_rmdir 137 -#define TARGET_FREEBSD_NR_utimes 138 - /* 139 is obsolete 4.2 sigreturn */ -#define TARGET_FREEBSD_NR_adjtime 140 - /* 141 is old getpeername */ - /* 142 is old gethostid */ - /* 143 is old sethostid */ - /* 144 is old getrlimit */ - /* 145 is old setrlimit */ - /* 146 is old killpg */ -#define TARGET_FREEBSD_NR_killpg 146 /* COMPAT */ -#define TARGET_FREEBSD_NR_setsid 147 -#define TARGET_FREEBSD_NR_quotactl 148 - /* 149 is old quota */ - /* 150 is old getsockname */ +#define TARGET_FREEBSD_NR_utimes 138 + /* 139 is obsolete 4.2 sigreturn */ +#define TARGET_FREEBSD_NR_adjtime 140 + /* 141 is old getpeername */ + /* 142 is old gethostid */ + /* 143 is old sethostid */ + /* 144 is old getrlimit */ + /* 145 is old setrlimit */ + /* 146 is old killpg */ +#define TARGET_FREEBSD_NR_setsid 147 +#define TARGET_FREEBSD_NR_quotactl 148 + /* 149 is old quota */ + /* 150 is old getsockname */ #define TARGET_FREEBSD_NR_nlm_syscall 154 -#define TARGET_FREEBSD_NR_nfssvc 155 - /* 156 is old getdirentries */ -#define TARGET_FREEBSD_NR_freebsd4_statfs 157 -#define TARGET_FREEBSD_NR_freebsd4_fstatfs 158 -#define TARGET_FREEBSD_NR_lgetfh 160 +#define TARGET_FREEBSD_NR_nfssvc 155 + /* 156 is old getdirentries */ + /* 157 is freebsd4 statfs */ + /* 158 is freebsd4 fstatfs */ +#define TARGET_FREEBSD_NR_lgetfh 160 #define TARGET_FREEBSD_NR_getfh 161 -#define TARGET_FREEBSD_NR_freebsd4_getdomainname 162 -#define TARGET_FREEBSD_NR_freebsd4_setdomainname 163 -#define TARGET_FREEBSD_NR_freebsd4_uname 164 -#define TARGET_FREEBSD_NR_sysarch 165 -#define TARGET_FREEBSD_NR_rtprio 166 -#define TARGET_FREEBSD_NR_semsys 169 -#define TARGET_FREEBSD_NR_msgsys 170 -#define TARGET_FREEBSD_NR_shmsys 171 -#define TARGET_FREEBSD_NR_freebsd6_pread 173 -#define TARGET_FREEBSD_NR_freebsd6_pwrite 174 -#define TARGET_FREEBSD_NR_setfib 175 + /* 162 is freebsd4 getdomainname */ + /* 163 is freebsd4 setdomainname */ + /* 164 is freebsd4 uname */ +#define TARGET_FREEBSD_NR_sysarch 165 +#define TARGET_FREEBSD_NR_rtprio 166 +#define TARGET_FREEBSD_NR_semsys 169 +#define TARGET_FREEBSD_NR_msgsys 170 +#define TARGET_FREEBSD_NR_shmsys 171 + /* 173 is freebsd6 pread */ + /* 174 is freebsd6 pwrite */ +#define TARGET_FREEBSD_NR_setfib 175 #define TARGET_FREEBSD_NR_ntp_adjtime 176 -#define TARGET_FREEBSD_NR_setgid 181 -#define TARGET_FREEBSD_NR_setegid 182 -#define TARGET_FREEBSD_NR_seteuid 183 -#define TARGET_FREEBSD_NR_stat 188 -#define TARGET_FREEBSD_NR_fstat 189 -#define TARGET_FREEBSD_NR_lstat 190 -#define TARGET_FREEBSD_NR_pathconf 191 -#define TARGET_FREEBSD_NR_fpathconf 192 -#define TARGET_FREEBSD_NR_getrlimit 194 -#define TARGET_FREEBSD_NR_setrlimit 195 -#define TARGET_FREEBSD_NR_getdirentries 196 -#define TARGET_FREEBSD_NR_freebsd6_mmap 197 -#define TARGET_FREEBSD_NR___syscall 198 -#define TARGET_FREEBSD_NR_freebsd6_lseek 199 -#define TARGET_FREEBSD_NR_freebsd6_truncate 200 -#define TARGET_FREEBSD_NR_freebsd6_ftruncate 201 -#define TARGET_FREEBSD_NR___sysctl 202 +#define TARGET_FREEBSD_NR_setgid 181 +#define TARGET_FREEBSD_NR_setegid 182 +#define TARGET_FREEBSD_NR_seteuid 183 + /* 184 is obsolete lfs_bmapv */ + /* 185 is obsolete lfs_markv */ + /* 186 is obsolete lfs_segclean */ + /* 187 is obsolete lfs_segwait */ +#define TARGET_FREEBSD_NR_freebsd11_stat 188 +#define TARGET_FREEBSD_NR_freebsd11_fstat 189 +#define TARGET_FREEBSD_NR_freebsd11_lstat 190 +#define TARGET_FREEBSD_NR_pathconf 191 +#define TARGET_FREEBSD_NR_fpathconf 192 +#define TARGET_FREEBSD_NR_getrlimit 194 +#define TARGET_FREEBSD_NR_setrlimit 195 +#define TARGET_FREEBSD_NR_freebsd11_getdirentries 196 + /* 197 is freebsd6 mmap */ +#define TARGET_FREEBSD_NR___syscall 198 + /* 199 is freebsd6 lseek */ + /* 200 is freebsd6 truncate */ + /* 201 is freebsd6 ftruncate */ +#define TARGET_FREEBSD_NR___sysctl 202 #define TARGET_FREEBSD_NR_mlock 203 -#define TARGET_FREEBSD_NR_munlock 204 -#define TARGET_FREEBSD_NR_undelete 205 -#define TARGET_FREEBSD_NR_futimes 206 -#define TARGET_FREEBSD_NR_getpgid 207 +#define TARGET_FREEBSD_NR_munlock 204 +#define TARGET_FREEBSD_NR_undelete 205 +#define TARGET_FREEBSD_NR_futimes 206 +#define TARGET_FREEBSD_NR_getpgid 207 #define TARGET_FREEBSD_NR_poll 209 -#define TARGET_FREEBSD_NR_freebsd7___semctl 220 -#define TARGET_FREEBSD_NR_semget 221 +#define TARGET_FREEBSD_NR_freebsd7___semctl 220 +#define TARGET_FREEBSD_NR_semget 221 #define TARGET_FREEBSD_NR_semop 222 -#define TARGET_FREEBSD_NR_freebsd7_msgctl 224 -#define TARGET_FREEBSD_NR_msgget 225 -#define TARGET_FREEBSD_NR_msgsnd 226 -#define TARGET_FREEBSD_NR_msgrcv 227 + /* 223 is obsolete semconfig */ +#define TARGET_FREEBSD_NR_freebsd7_msgctl 224 +#define TARGET_FREEBSD_NR_msgget 225 +#define TARGET_FREEBSD_NR_msgsnd 226 +#define TARGET_FREEBSD_NR_msgrcv 227 #define TARGET_FREEBSD_NR_shmat 228 -#define TARGET_FREEBSD_NR_freebsd7_shmctl 229 +#define TARGET_FREEBSD_NR_freebsd7_shmctl 229 #define TARGET_FREEBSD_NR_shmdt 230 -#define TARGET_FREEBSD_NR_shmget 231 +#define TARGET_FREEBSD_NR_shmget 231 #define TARGET_FREEBSD_NR_clock_gettime 232 #define TARGET_FREEBSD_NR_clock_settime 233 #define TARGET_FREEBSD_NR_clock_getres 234 #define TARGET_FREEBSD_NR_ktimer_create 235 #define TARGET_FREEBSD_NR_ktimer_delete 236 -#define TARGET_FREEBSD_NR_ktimer_settime 237 -#define TARGET_FREEBSD_NR_ktimer_gettime 238 -#define TARGET_FREEBSD_NR_ktimer_getoverrun 239 -#define TARGET_FREEBSD_NR_nanosleep 240 +#define TARGET_FREEBSD_NR_ktimer_settime 237 +#define TARGET_FREEBSD_NR_ktimer_gettime 238 +#define TARGET_FREEBSD_NR_ktimer_getoverrun 239 +#define TARGET_FREEBSD_NR_nanosleep 240 +#define TARGET_FREEBSD_NR_ffclock_getcounter 241 +#define TARGET_FREEBSD_NR_ffclock_setestimate 242 +#define TARGET_FREEBSD_NR_ffclock_getestimate 243 +#define TARGET_FREEBSD_NR_clock_nanosleep 244 +#define TARGET_FREEBSD_NR_clock_getcpuclockid2 247 #define TARGET_FREEBSD_NR_ntp_gettime 248 -#define TARGET_FREEBSD_NR_minherit 250 +#define TARGET_FREEBSD_NR_minherit 250 #define TARGET_FREEBSD_NR_rfork 251 -#define TARGET_FREEBSD_NR_openbsd_poll 252 -#define TARGET_FREEBSD_NR_issetugid 253 -#define TARGET_FREEBSD_NR_lchown 254 -#define TARGET_FREEBSD_NR_aio_read 255 -#define TARGET_FREEBSD_NR_aio_write 256 + /* 252 is obsolete openbsd_poll */ +#define TARGET_FREEBSD_NR_issetugid 253 +#define TARGET_FREEBSD_NR_lchown 254 +#define TARGET_FREEBSD_NR_aio_read 255 +#define TARGET_FREEBSD_NR_aio_write 256 #define TARGET_FREEBSD_NR_lio_listio 257 -#define TARGET_FREEBSD_NR_getdents 272 -#define TARGET_FREEBSD_NR_lchmod 274 -#define TARGET_FREEBSD_NR_netbsd_lchown 275 -#define TARGET_FREEBSD_NR_lutimes 276 -#define TARGET_FREEBSD_NR_netbsd_msync 277 -#define TARGET_FREEBSD_NR_nstat 278 -#define TARGET_FREEBSD_NR_nfstat 279 -#define TARGET_FREEBSD_NR_nlstat 280 -#define TARGET_FREEBSD_NR_preadv 289 -#define TARGET_FREEBSD_NR_pwritev 290 -#define TARGET_FREEBSD_NR_freebsd4_fhstatfs 297 -#define TARGET_FREEBSD_NR_fhopen 298 -#define TARGET_FREEBSD_NR_fhstat 299 -#define TARGET_FREEBSD_NR_modnext 300 -#define TARGET_FREEBSD_NR_modstat 301 -#define TARGET_FREEBSD_NR_modfnext 302 -#define TARGET_FREEBSD_NR_modfind 303 -#define TARGET_FREEBSD_NR_kldload 304 -#define TARGET_FREEBSD_NR_kldunload 305 -#define TARGET_FREEBSD_NR_kldfind 306 -#define TARGET_FREEBSD_NR_kldnext 307 -#define TARGET_FREEBSD_NR_kldstat 308 +#define TARGET_FREEBSD_NR_freebsd11_getdents 272 +#define TARGET_FREEBSD_NR_lchmod 274 + /* 275 is obsolete netbsd_lchown */ +#define TARGET_FREEBSD_NR_lutimes 276 + /* 277 is obsolete netbsd_msync */ +#define TARGET_FREEBSD_NR_freebsd11_nstat 278 +#define TARGET_FREEBSD_NR_freebsd11_nfstat 279 +#define TARGET_FREEBSD_NR_freebsd11_nlstat 280 +#define TARGET_FREEBSD_NR_preadv 289 +#define TARGET_FREEBSD_NR_pwritev 290 + /* 297 is freebsd4 fhstatfs */ +#define TARGET_FREEBSD_NR_fhopen 298 +#define TARGET_FREEBSD_NR_freebsd11_fhstat 299 +#define TARGET_FREEBSD_NR_modnext 300 +#define TARGET_FREEBSD_NR_modstat 301 +#define TARGET_FREEBSD_NR_modfnext 302 +#define TARGET_FREEBSD_NR_modfind 303 +#define TARGET_FREEBSD_NR_kldload 304 +#define TARGET_FREEBSD_NR_kldunload 305 +#define TARGET_FREEBSD_NR_kldfind 306 +#define TARGET_FREEBSD_NR_kldnext 307 +#define TARGET_FREEBSD_NR_kldstat 308 #define TARGET_FREEBSD_NR_kldfirstmod 309 -#define TARGET_FREEBSD_NR_getsid 310 -#define TARGET_FREEBSD_NR_setresuid 311 -#define TARGET_FREEBSD_NR_setresgid 312 - /* 313 is obsolete signanosleep */ +#define TARGET_FREEBSD_NR_getsid 310 +#define TARGET_FREEBSD_NR_setresuid 311 +#define TARGET_FREEBSD_NR_setresgid 312 + /* 313 is obsolete signanosleep */ #define TARGET_FREEBSD_NR_aio_return 314 #define TARGET_FREEBSD_NR_aio_suspend 315 #define TARGET_FREEBSD_NR_aio_cancel 316 -#define TARGET_FREEBSD_NR_aio_error 317 -#define TARGET_FREEBSD_NR_oaio_read 318 -#define TARGET_FREEBSD_NR_oaio_write 319 -#define TARGET_FREEBSD_NR_olio_listio 320 +#define TARGET_FREEBSD_NR_aio_error 317 + /* 318 is freebsd6 aio_read */ + /* 319 is freebsd6 aio_write */ + /* 320 is freebsd6 lio_listio */ #define TARGET_FREEBSD_NR_yield 321 - /* 322 is obsolete thr_sleep */ - /* 323 is obsolete thr_wakeup */ -#define TARGET_FREEBSD_NR_mlockall 324 + /* 322 is obsolete thr_sleep */ + /* 323 is obsolete thr_wakeup */ +#define TARGET_FREEBSD_NR_mlockall 324 #define TARGET_FREEBSD_NR_munlockall 325 -#define TARGET_FREEBSD_NR___getcwd 326 -#define TARGET_FREEBSD_NR_sched_setparam 327 -#define TARGET_FREEBSD_NR_sched_getparam 328 +#define TARGET_FREEBSD_NR___getcwd 326 +#define TARGET_FREEBSD_NR_sched_setparam 327 +#define TARGET_FREEBSD_NR_sched_getparam 328 #define TARGET_FREEBSD_NR_sched_setscheduler 329 #define TARGET_FREEBSD_NR_sched_getscheduler 330 #define TARGET_FREEBSD_NR_sched_yield 331 -#define TARGET_FREEBSD_NR_sched_get_priority_max 332 -#define TARGET_FREEBSD_NR_sched_get_priority_min 333 +#define TARGET_FREEBSD_NR_sched_get_priority_max 332 +#define TARGET_FREEBSD_NR_sched_get_priority_min 333 #define TARGET_FREEBSD_NR_sched_rr_get_interval 334 -#define TARGET_FREEBSD_NR_utrace 335 -#define TARGET_FREEBSD_NR_freebsd4_sendfile 336 -#define TARGET_FREEBSD_NR_kldsym 337 +#define TARGET_FREEBSD_NR_utrace 335 + /* 336 is freebsd4 sendfile */ +#define TARGET_FREEBSD_NR_kldsym 337 #define TARGET_FREEBSD_NR_jail 338 #define TARGET_FREEBSD_NR_nnpfs_syscall 339 #define TARGET_FREEBSD_NR_sigprocmask 340 #define TARGET_FREEBSD_NR_sigsuspend 341 -#define TARGET_FREEBSD_NR_freebsd4_sigaction 342 + /* 342 is freebsd4 sigaction */ #define TARGET_FREEBSD_NR_sigpending 343 -#define TARGET_FREEBSD_NR_freebsd4_sigreturn 344 + /* 344 is freebsd4 sigreturn */ #define TARGET_FREEBSD_NR_sigtimedwait 345 #define TARGET_FREEBSD_NR_sigwaitinfo 346 -#define TARGET_FREEBSD_NR___acl_get_file 347 -#define TARGET_FREEBSD_NR___acl_set_file 348 +#define TARGET_FREEBSD_NR___acl_get_file 347 +#define TARGET_FREEBSD_NR___acl_set_file 348 #define TARGET_FREEBSD_NR___acl_get_fd 349 #define TARGET_FREEBSD_NR___acl_set_fd 350 -#define TARGET_FREEBSD_NR___acl_delete_file 351 -#define TARGET_FREEBSD_NR___acl_delete_fd 352 +#define TARGET_FREEBSD_NR___acl_delete_file 351 +#define TARGET_FREEBSD_NR___acl_delete_fd 352 #define TARGET_FREEBSD_NR___acl_aclcheck_file 353 -#define TARGET_FREEBSD_NR___acl_aclcheck_fd 354 +#define TARGET_FREEBSD_NR___acl_aclcheck_fd 354 #define TARGET_FREEBSD_NR_extattrctl 355 -#define TARGET_FREEBSD_NR_extattr_set_file 356 -#define TARGET_FREEBSD_NR_extattr_get_file 357 +#define TARGET_FREEBSD_NR_extattr_set_file 356 +#define TARGET_FREEBSD_NR_extattr_get_file 357 #define TARGET_FREEBSD_NR_extattr_delete_file 358 -#define TARGET_FREEBSD_NR_aio_waitcomplete 359 -#define TARGET_FREEBSD_NR_getresuid 360 -#define TARGET_FREEBSD_NR_getresgid 361 -#define TARGET_FREEBSD_NR_kqueue 362 -#define TARGET_FREEBSD_NR_kevent 363 -#define TARGET_FREEBSD_NR_extattr_set_fd 371 -#define TARGET_FREEBSD_NR_extattr_get_fd 372 -#define TARGET_FREEBSD_NR_extattr_delete_fd 373 -#define TARGET_FREEBSD_NR___setugid 374 -#define TARGET_FREEBSD_NR_eaccess 376 +#define TARGET_FREEBSD_NR_aio_waitcomplete 359 +#define TARGET_FREEBSD_NR_getresuid 360 +#define TARGET_FREEBSD_NR_getresgid 361 +#define TARGET_FREEBSD_NR_kqueue 362 +#define TARGET_FREEBSD_NR_freebsd11_kevent 363 + /* 364 is obsolete __cap_get_proc */ + /* 365 is obsolete __cap_set_proc */ + /* 366 is obsolete __cap_get_fd */ + /* 367 is obsolete __cap_get_file */ + /* 368 is obsolete __cap_set_fd */ + /* 369 is obsolete __cap_set_file */ +#define TARGET_FREEBSD_NR_extattr_set_fd 371 +#define TARGET_FREEBSD_NR_extattr_get_fd 372 +#define TARGET_FREEBSD_NR_extattr_delete_fd 373 +#define TARGET_FREEBSD_NR___setugid 374 + /* 375 is obsolete nfsclnt */ +#define TARGET_FREEBSD_NR_eaccess 376 #define TARGET_FREEBSD_NR_afs3_syscall 377 -#define TARGET_FREEBSD_NR_nmount 378 -#define TARGET_FREEBSD_NR___mac_get_proc 384 -#define TARGET_FREEBSD_NR___mac_set_proc 385 +#define TARGET_FREEBSD_NR_nmount 378 + /* 379 is obsolete kse_exit */ + /* 380 is obsolete kse_wakeup */ + /* 381 is obsolete kse_create */ + /* 382 is obsolete kse_thr_interrupt */ + /* 383 is obsolete kse_release */ +#define TARGET_FREEBSD_NR___mac_get_proc 384 +#define TARGET_FREEBSD_NR___mac_set_proc 385 #define TARGET_FREEBSD_NR___mac_get_fd 386 -#define TARGET_FREEBSD_NR___mac_get_file 387 +#define TARGET_FREEBSD_NR___mac_get_file 387 #define TARGET_FREEBSD_NR___mac_set_fd 388 -#define TARGET_FREEBSD_NR___mac_set_file 389 +#define TARGET_FREEBSD_NR___mac_set_file 389 #define TARGET_FREEBSD_NR_kenv 390 -#define TARGET_FREEBSD_NR_lchflags 391 -#define TARGET_FREEBSD_NR_uuidgen 392 -#define TARGET_FREEBSD_NR_sendfile 393 +#define TARGET_FREEBSD_NR_lchflags 391 +#define TARGET_FREEBSD_NR_uuidgen 392 +#define TARGET_FREEBSD_NR_sendfile 393 #define TARGET_FREEBSD_NR_mac_syscall 394 -#define TARGET_FREEBSD_NR_getfsstat 395 -#define TARGET_FREEBSD_NR_statfs 396 -#define TARGET_FREEBSD_NR_fstatfs 397 -#define TARGET_FREEBSD_NR_fhstatfs 398 +#define TARGET_FREEBSD_NR_freebsd11_getfsstat 395 +#define TARGET_FREEBSD_NR_freebsd11_statfs 396 +#define TARGET_FREEBSD_NR_freebsd11_fstatfs 397 +#define TARGET_FREEBSD_NR_freebsd11_fhstatfs 398 #define TARGET_FREEBSD_NR_ksem_close 400 -#define TARGET_FREEBSD_NR_ksem_post 401 -#define TARGET_FREEBSD_NR_ksem_wait 402 +#define TARGET_FREEBSD_NR_ksem_post 401 +#define TARGET_FREEBSD_NR_ksem_wait 402 #define TARGET_FREEBSD_NR_ksem_trywait 403 -#define TARGET_FREEBSD_NR_ksem_init 404 -#define TARGET_FREEBSD_NR_ksem_open 405 +#define TARGET_FREEBSD_NR_ksem_init 404 +#define TARGET_FREEBSD_NR_ksem_open 405 #define TARGET_FREEBSD_NR_ksem_unlink 406 #define TARGET_FREEBSD_NR_ksem_getvalue 407 #define TARGET_FREEBSD_NR_ksem_destroy 408 #define TARGET_FREEBSD_NR___mac_get_pid 409 -#define TARGET_FREEBSD_NR___mac_get_link 410 -#define TARGET_FREEBSD_NR___mac_set_link 411 -#define TARGET_FREEBSD_NR_extattr_set_link 412 -#define TARGET_FREEBSD_NR_extattr_get_link 413 +#define TARGET_FREEBSD_NR___mac_get_link 410 +#define TARGET_FREEBSD_NR___mac_set_link 411 +#define TARGET_FREEBSD_NR_extattr_set_link 412 +#define TARGET_FREEBSD_NR_extattr_get_link 413 #define TARGET_FREEBSD_NR_extattr_delete_link 414 #define TARGET_FREEBSD_NR___mac_execve 415 -#define TARGET_FREEBSD_NR_sigaction 416 -#define TARGET_FREEBSD_NR_sigreturn 417 +#define TARGET_FREEBSD_NR_sigaction 416 +#define TARGET_FREEBSD_NR_sigreturn 417 #define TARGET_FREEBSD_NR_getcontext 421 #define TARGET_FREEBSD_NR_setcontext 422 #define TARGET_FREEBSD_NR_swapcontext 423 -#define TARGET_FREEBSD_NR_swapoff 424 -#define TARGET_FREEBSD_NR___acl_get_link 425 -#define TARGET_FREEBSD_NR___acl_set_link 426 -#define TARGET_FREEBSD_NR___acl_delete_link 427 +#define TARGET_FREEBSD_NR_swapoff 424 +#define TARGET_FREEBSD_NR___acl_get_link 425 +#define TARGET_FREEBSD_NR___acl_set_link 426 +#define TARGET_FREEBSD_NR___acl_delete_link 427 #define TARGET_FREEBSD_NR___acl_aclcheck_link 428 -#define TARGET_FREEBSD_NR_sigwait 429 +#define TARGET_FREEBSD_NR_sigwait 429 #define TARGET_FREEBSD_NR_thr_create 430 -#define TARGET_FREEBSD_NR_thr_exit 431 -#define TARGET_FREEBSD_NR_thr_self 432 -#define TARGET_FREEBSD_NR_thr_kill 433 -#define TARGET_FREEBSD_NR__umtx_lock 434 -#define TARGET_FREEBSD_NR__umtx_unlock 435 +#define TARGET_FREEBSD_NR_thr_exit 431 +#define TARGET_FREEBSD_NR_thr_self 432 +#define TARGET_FREEBSD_NR_thr_kill 433 #define TARGET_FREEBSD_NR_jail_attach 436 -#define TARGET_FREEBSD_NR_extattr_list_fd 437 -#define TARGET_FREEBSD_NR_extattr_list_file 438 -#define TARGET_FREEBSD_NR_extattr_list_link 439 -#define TARGET_FREEBSD_NR_ksem_timedwait 441 +#define TARGET_FREEBSD_NR_extattr_list_fd 437 +#define TARGET_FREEBSD_NR_extattr_list_file 438 +#define TARGET_FREEBSD_NR_extattr_list_link 439 + /* 440 is obsolete kse_switchin */ +#define TARGET_FREEBSD_NR_ksem_timedwait 441 #define TARGET_FREEBSD_NR_thr_suspend 442 -#define TARGET_FREEBSD_NR_thr_wake 443 +#define TARGET_FREEBSD_NR_thr_wake 443 #define TARGET_FREEBSD_NR_kldunloadf 444 #define TARGET_FREEBSD_NR_audit 445 -#define TARGET_FREEBSD_NR_auditon 446 -#define TARGET_FREEBSD_NR_getauid 447 -#define TARGET_FREEBSD_NR_setauid 448 -#define TARGET_FREEBSD_NR_getaudit 449 -#define TARGET_FREEBSD_NR_setaudit 450 +#define TARGET_FREEBSD_NR_auditon 446 +#define TARGET_FREEBSD_NR_getauid 447 +#define TARGET_FREEBSD_NR_setauid 448 +#define TARGET_FREEBSD_NR_getaudit 449 +#define TARGET_FREEBSD_NR_setaudit 450 #define TARGET_FREEBSD_NR_getaudit_addr 451 #define TARGET_FREEBSD_NR_setaudit_addr 452 -#define TARGET_FREEBSD_NR_auditctl 453 -#define TARGET_FREEBSD_NR__umtx_op 454 -#define TARGET_FREEBSD_NR_thr_new 455 -#define TARGET_FREEBSD_NR_sigqueue 456 -#define TARGET_FREEBSD_NR_kmq_open 457 +#define TARGET_FREEBSD_NR_auditctl 453 +#define TARGET_FREEBSD_NR__umtx_op 454 +#define TARGET_FREEBSD_NR_thr_new 455 +#define TARGET_FREEBSD_NR_sigqueue 456 +#define TARGET_FREEBSD_NR_kmq_open 457 #define TARGET_FREEBSD_NR_kmq_setattr 458 -#define TARGET_FREEBSD_NR_kmq_timedreceive 459 +#define TARGET_FREEBSD_NR_kmq_timedreceive 459 #define TARGET_FREEBSD_NR_kmq_timedsend 460 #define TARGET_FREEBSD_NR_kmq_notify 461 #define TARGET_FREEBSD_NR_kmq_unlink 462 -#define TARGET_FREEBSD_NR_abort2 463 +#define TARGET_FREEBSD_NR_abort2 463 #define TARGET_FREEBSD_NR_thr_set_name 464 -#define TARGET_FREEBSD_NR_aio_fsync 465 +#define TARGET_FREEBSD_NR_aio_fsync 465 #define TARGET_FREEBSD_NR_rtprio_thread 466 #define TARGET_FREEBSD_NR_sctp_peeloff 471 #define TARGET_FREEBSD_NR_sctp_generic_sendmsg 472 -#define TARGET_FREEBSD_NR_sctp_generic_sendmsg_iov 473 +#define TARGET_FREEBSD_NR_sctp_generic_sendmsg_iov 473 #define TARGET_FREEBSD_NR_sctp_generic_recvmsg 474 #define TARGET_FREEBSD_NR_pread 475 -#define TARGET_FREEBSD_NR_pwrite 476 +#define TARGET_FREEBSD_NR_pwrite 476 #define TARGET_FREEBSD_NR_mmap 477 #define TARGET_FREEBSD_NR_lseek 478 -#define TARGET_FREEBSD_NR_truncate 479 -#define TARGET_FREEBSD_NR_ftruncate 480 -#define TARGET_FREEBSD_NR_thr_kill2 481 -#define TARGET_FREEBSD_NR_shm_open 482 +#define TARGET_FREEBSD_NR_truncate 479 +#define TARGET_FREEBSD_NR_ftruncate 480 +#define TARGET_FREEBSD_NR_thr_kill2 481 +#define TARGET_FREEBSD_NR_freebsd12_shm_open 482 #define TARGET_FREEBSD_NR_shm_unlink 483 -#define TARGET_FREEBSD_NR_cpuset 484 +#define TARGET_FREEBSD_NR_cpuset 484 #define TARGET_FREEBSD_NR_cpuset_setid 485 #define TARGET_FREEBSD_NR_cpuset_getid 486 #define TARGET_FREEBSD_NR_cpuset_getaffinity 487 #define TARGET_FREEBSD_NR_cpuset_setaffinity 488 -#define TARGET_FREEBSD_NR_faccessat 489 -#define TARGET_FREEBSD_NR_fchmodat 490 -#define TARGET_FREEBSD_NR_fchownat 491 -#define TARGET_FREEBSD_NR_fexecve 492 -#define TARGET_FREEBSD_NR_fstatat 493 -#define TARGET_FREEBSD_NR_futimesat 494 -#define TARGET_FREEBSD_NR_linkat 495 -#define TARGET_FREEBSD_NR_mkdirat 496 -#define TARGET_FREEBSD_NR_mkfifoat 497 -#define TARGET_FREEBSD_NR_mknodat 498 -#define TARGET_FREEBSD_NR_openat 499 +#define TARGET_FREEBSD_NR_faccessat 489 +#define TARGET_FREEBSD_NR_fchmodat 490 +#define TARGET_FREEBSD_NR_fchownat 491 +#define TARGET_FREEBSD_NR_fexecve 492 +#define TARGET_FREEBSD_NR_freebsd11_fstatat 493 +#define TARGET_FREEBSD_NR_futimesat 494 +#define TARGET_FREEBSD_NR_linkat 495 +#define TARGET_FREEBSD_NR_mkdirat 496 +#define TARGET_FREEBSD_NR_mkfifoat 497 +#define TARGET_FREEBSD_NR_freebsd11_mknodat 498 +#define TARGET_FREEBSD_NR_openat 499 #define TARGET_FREEBSD_NR_readlinkat 500 -#define TARGET_FREEBSD_NR_renameat 501 -#define TARGET_FREEBSD_NR_symlinkat 502 -#define TARGET_FREEBSD_NR_unlinkat 503 +#define TARGET_FREEBSD_NR_renameat 501 +#define TARGET_FREEBSD_NR_symlinkat 502 +#define TARGET_FREEBSD_NR_unlinkat 503 #define TARGET_FREEBSD_NR_posix_openpt 504 #define TARGET_FREEBSD_NR_gssd_syscall 505 -#define TARGET_FREEBSD_NR_jail_get 506 -#define TARGET_FREEBSD_NR_jail_set 507 +#define TARGET_FREEBSD_NR_jail_get 506 +#define TARGET_FREEBSD_NR_jail_set 507 #define TARGET_FREEBSD_NR_jail_remove 508 -#define TARGET_FREEBSD_NR_closefrom 509 -#define TARGET_FREEBSD_NR___semctl 510 -#define TARGET_FREEBSD_NR_msgctl 511 -#define TARGET_FREEBSD_NR_shmctl 512 -#define TARGET_FREEBSD_NR_lpathconf 513 -#define TARGET_FREEBSD_NR_cap_new 514 -#define TARGET_FREEBSD_NR_cap_getrights 515 -#define TARGET_FREEBSD_NR_cap_enter 516 +#define TARGET_FREEBSD_NR_freebsd12_closefrom 509 +#define TARGET_FREEBSD_NR___semctl 510 +#define TARGET_FREEBSD_NR_msgctl 511 +#define TARGET_FREEBSD_NR_shmctl 512 +#define TARGET_FREEBSD_NR_lpathconf 513 + /* 514 is obsolete cap_new */ +#define TARGET_FREEBSD_NR___cap_rights_get 515 +#define TARGET_FREEBSD_NR_cap_enter 516 #define TARGET_FREEBSD_NR_cap_getmode 517 -#define TARGET_FREEBSD_NR_pdfork 518 -#define TARGET_FREEBSD_NR_pdkill 519 -#define TARGET_FREEBSD_NR_pdgetpid 520 -#define TARGET_FREEBSD_NR_pselect 522 +#define TARGET_FREEBSD_NR_pdfork 518 +#define TARGET_FREEBSD_NR_pdkill 519 +#define TARGET_FREEBSD_NR_pdgetpid 520 +#define TARGET_FREEBSD_NR_pselect 522 #define TARGET_FREEBSD_NR_getloginclass 523 #define TARGET_FREEBSD_NR_setloginclass 524 -#define TARGET_FREEBSD_NR_rctl_get_racct 525 -#define TARGET_FREEBSD_NR_rctl_get_rules 526 -#define TARGET_FREEBSD_NR_rctl_get_limits 527 +#define TARGET_FREEBSD_NR_rctl_get_racct 525 +#define TARGET_FREEBSD_NR_rctl_get_rules 526 +#define TARGET_FREEBSD_NR_rctl_get_limits 527 #define TARGET_FREEBSD_NR_rctl_add_rule 528 -#define TARGET_FREEBSD_NR_rctl_remove_rule 529 -#define TARGET_FREEBSD_NR_posix_fallocate 530 +#define TARGET_FREEBSD_NR_rctl_remove_rule 529 +#define TARGET_FREEBSD_NR_posix_fallocate 530 #define TARGET_FREEBSD_NR_posix_fadvise 531 -#define TARGET_FREEBSD_NR_MAXSYSCALL 532 +#define TARGET_FREEBSD_NR_wait6 532 +#define TARGET_FREEBSD_NR_cap_rights_limit 533 +#define TARGET_FREEBSD_NR_cap_ioctls_limit 534 +#define TARGET_FREEBSD_NR_cap_ioctls_get 535 +#define TARGET_FREEBSD_NR_cap_fcntls_limit 536 +#define TARGET_FREEBSD_NR_cap_fcntls_get 537 +#define TARGET_FREEBSD_NR_bindat 538 +#define TARGET_FREEBSD_NR_connectat 539 +#define TARGET_FREEBSD_NR_chflagsat 540 +#define TARGET_FREEBSD_NR_accept4 541 +#define TARGET_FREEBSD_NR_pipe2 542 +#define TARGET_FREEBSD_NR_aio_mlock 543 +#define TARGET_FREEBSD_NR_procctl 544 +#define TARGET_FREEBSD_NR_ppoll 545 +#define TARGET_FREEBSD_NR_futimens 546 +#define TARGET_FREEBSD_NR_utimensat 547 + /* 548 is obsolete numa_getaffinity */ + /* 549 is obsolete numa_setaffinity */ +#define TARGET_FREEBSD_NR_fdatasync 550 +#define TARGET_FREEBSD_NR_fstat 551 +#define TARGET_FREEBSD_NR_fstatat 552 +#define TARGET_FREEBSD_NR_fhstat 553 +#define TARGET_FREEBSD_NR_getdirentries 554 +#define TARGET_FREEBSD_NR_statfs 555 +#define TARGET_FREEBSD_NR_fstatfs 556 +#define TARGET_FREEBSD_NR_getfsstat 557 +#define TARGET_FREEBSD_NR_fhstatfs 558 +#define TARGET_FREEBSD_NR_mknodat 559 +#define TARGET_FREEBSD_NR_kevent 560 +#define TARGET_FREEBSD_NR_cpuset_getdomain 561 +#define TARGET_FREEBSD_NR_cpuset_setdomain 562 +#define TARGET_FREEBSD_NR_getrandom 563 +#define TARGET_FREEBSD_NR_getfhat 564 +#define TARGET_FREEBSD_NR_fhlink 565 +#define TARGET_FREEBSD_NR_fhlinkat 566 +#define TARGET_FREEBSD_NR_fhreadlink 567 +#define TARGET_FREEBSD_NR_funlinkat 568 +#define TARGET_FREEBSD_NR_copy_file_range 569 +#define TARGET_FREEBSD_NR___sysctlbyname 570 +#define TARGET_FREEBSD_NR_shm_open2 571 +#define TARGET_FREEBSD_NR_shm_rename 572 +#define TARGET_FREEBSD_NR_sigfastblock 573 +#define TARGET_FREEBSD_NR___realpathat 574 +#define TARGET_FREEBSD_NR_close_range 575 +#define TARGET_FREEBSD_NR_rpctls_syscall 576 +#define TARGET_FREEBSD_NR_MAXSYSCALL 577 diff --git a/bsd-user/i386/target_arch_sysarch.h b/bsd-user/i386/target_arch_sysarch.h new file mode 100644 index 0000000000..e9ab98ec32 --- /dev/null +++ b/bsd-user/i386/target_arch_sysarch.h @@ -0,0 +1,77 @@ +/* + * i386 sysarch system call emulation + * + * Copyright (c) 2013 Stacey D. Son + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef BSD_USER_ARCH_SYSARCH_H_ +#define BSD_USER_ARCH_SYSARCH_H_ + +#include "target_syscall.h" + +static inline abi_long do_freebsd_arch_sysarch(CPUX86State *env, int op, + abi_ulong parms) +{ + abi_long ret = 0; + abi_ulong val; + int idx; + + switch (op) { + case TARGET_FREEBSD_I386_SET_GSBASE: + case TARGET_FREEBSD_I386_SET_FSBASE: + if (op == TARGET_FREEBSD_I386_SET_GSBASE) { + idx = R_GS; + } else { + idx = R_FS; + } + if (get_user(val, parms, abi_ulong)) { + return -TARGET_EFAULT; + } + cpu_x86_load_seg(env, idx, 0); + env->segs[idx].base = val; + break; + + case TARGET_FREEBSD_I386_GET_GSBASE: + case TARGET_FREEBSD_I386_GET_FSBASE: + if (op == TARGET_FREEBSD_I386_GET_GSBASE) { + idx = R_GS; + } else { + idx = R_FS; + } + val = env->segs[idx].base; + if (put_user(val, parms, abi_ulong)) { + return -TARGET_EFAULT; + } + break; + + /* XXX handle the others... */ + default: + ret = -TARGET_EINVAL; + break; + } + return ret; +} + +static inline void do_freebsd_arch_print_sysarch( + const struct syscallname *name, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6) +{ + + gemu_log("%s(%d, " TARGET_ABI_FMT_lx ", " TARGET_ABI_FMT_lx ", " + TARGET_ABI_FMT_lx ")", name->name, (int)arg1, arg2, arg3, arg4); +} + +#endif /* !BSD_USER_ARCH_SYSARCH_H_ */ diff --git a/bsd-user/i386/target_syscall.h b/bsd-user/i386/target_syscall.h index 8f201386af..940c687a52 100644 --- a/bsd-user/i386/target_syscall.h +++ b/bsd-user/i386/target_syscall.h @@ -1,3 +1,20 @@ +/* + * i386 system call definitions + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ #ifndef TARGET_SYSCALL_H #define TARGET_SYSCALL_H @@ -161,5 +178,7 @@ struct target_vm86plus_struct { #define UNAME_MACHINE "i386" +#define TARGET_HW_MACHINE UNAME_MACHINE +#define TARGET_HW_MACHINE_ARCH UNAME_MACHINE #endif /* TARGET_SYSCALL_H */ diff --git a/bsd-user/main.c b/bsd-user/main.c index ac40d79bfa..798aba512c 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/units.h" +#include "qemu/accel.h" #include "sysemu/tcg.h" #include "qemu-version.h" #include @@ -41,7 +42,7 @@ int singlestep; unsigned long mmap_min_addr; -unsigned long guest_base; +uintptr_t guest_base; bool have_guest_base; unsigned long reserved_va; @@ -512,6 +513,7 @@ void cpu_loop(CPUSPARCState *env) case 0x141: if (bsd_type != target_freebsd) goto badtrap; + /* fallthrough */ case 0x100: #endif syscall_nr = env->gregs[1]; @@ -745,7 +747,6 @@ int main(int argc, char **argv) const char *gdbstub = NULL; char **target_environ, **wrk; envlist_t *envlist = NULL; - char *trace_file = NULL; bsd_type = target_openbsd; if (argc <= 1) @@ -851,8 +852,7 @@ int main(int argc, char **argv) } else if (!strcmp(r, "strace")) { do_strace = 1; } else if (!strcmp(r, "trace")) { - g_free(trace_file); - trace_file = trace_opt_parse(optarg); + trace_opt_parse(optarg); } else { usage(); } @@ -880,7 +880,7 @@ int main(int argc, char **argv) if (!trace_init_backends()) { exit(1); } - trace_init_file(trace_file); + trace_init_file(); /* Zero out regs */ memset(regs, 0, sizeof(struct target_pt_regs)); @@ -909,10 +909,14 @@ int main(int argc, char **argv) #endif } - /* init tcg before creating CPUs and to get qemu_host_page_size */ - tcg_exec_init(0); - cpu_type = parse_cpu_option(cpu_model); + /* init tcg before creating CPUs and to get qemu_host_page_size */ + { + AccelClass *ac = ACCEL_GET_CLASS(current_accel()); + + ac->init_machine(NULL); + accel_init_interfaces(ac); + } cpu = cpu_create(cpu_type); env = cpu->env_ptr; #if defined(TARGET_SPARC) || defined(TARGET_PPC) @@ -966,7 +970,7 @@ int main(int argc, char **argv) g_free(target_environ); if (qemu_loglevel_mask(CPU_LOG_PAGE)) { - qemu_log("guest_base 0x%lx\n", guest_base); + qemu_log("guest_base %p\n", (void *)guest_base); log_page_dump("binary load"); qemu_log("start_brk 0x" TARGET_ABI_FMT_lx "\n", info->start_brk); @@ -1051,7 +1055,7 @@ int main(int argc, char **argv) env->idt.base = target_mmap(0, sizeof(uint64_t) * (env->idt.limit + 1), PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); - idt_table = g2h(env->idt.base); + idt_table = g2h_untagged(env->idt.base); set_idt(0, 0); set_idt(1, 0); set_idt(2, 0); @@ -1081,7 +1085,7 @@ int main(int argc, char **argv) PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); env->gdt.limit = sizeof(uint64_t) * TARGET_GDT_ENTRIES - 1; - gdt_table = g2h(env->gdt.base); + gdt_table = g2h_untagged(env->gdt.base); #ifdef TARGET_ABI32 write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff, DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | diff --git a/bsd-user/meson.build b/bsd-user/meson.build new file mode 100644 index 0000000000..0369549340 --- /dev/null +++ b/bsd-user/meson.build @@ -0,0 +1,10 @@ +bsd_user_ss.add(files( + 'bsdload.c', + 'elfload.c', + 'main.c', + 'mmap.c', + 'signal.c', + 'strace.c', + 'syscall.c', + 'uaccess.c', +)) diff --git a/bsd-user/mips/target_arch_sysarch.h b/bsd-user/mips/target_arch_sysarch.h new file mode 100644 index 0000000000..6da803a408 --- /dev/null +++ b/bsd-user/mips/target_arch_sysarch.h @@ -0,0 +1,69 @@ +/* + * mips sysarch() system call emulation + * + * Copyright (c) 2013 Stacey D. Son + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef BSD_USER_ARCH_SYSARCH_H_ +#define BSD_USER_ARCH_SYSARCH_H_ + +#include "target_syscall.h" +#include "target_arch.h" + +static inline abi_long do_freebsd_arch_sysarch(CPUMIPSState *env, int op, + abi_ulong parms) +{ + int ret = 0; + + switch (op) { + case TARGET_MIPS_SET_TLS: + target_cpu_set_tls(env, parms); + break; + + case TARGET_MIPS_GET_TLS: + if (put_user(target_cpu_get_tls(env), parms, abi_ulong)) { + ret = -TARGET_EFAULT; + } + break; + + default: + ret = -TARGET_EINVAL; + break; + } + + return ret; +} + +static inline void do_freebsd_arch_print_sysarch( + const struct syscallname *name, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6) +{ + + switch (arg1) { + case TARGET_MIPS_SET_TLS: + gemu_log("%s(SET_TLS, 0x" TARGET_ABI_FMT_lx ")", name->name, arg2); + break; + + case TARGET_MIPS_GET_TLS: + gemu_log("%s(GET_TLS, 0x" TARGET_ABI_FMT_lx ")", name->name, arg2); + break; + + default: + gemu_log("UNKNOWN OP: %d, " TARGET_ABI_FMT_lx ")", (int)arg1, arg2); + } +} + +#endif /*!BSD_USER_ARCH_SYSARCH_H_ */ diff --git a/bsd-user/mips/target_syscall.h b/bsd-user/mips/target_syscall.h new file mode 100644 index 0000000000..aacc6ddf9f --- /dev/null +++ b/bsd-user/mips/target_syscall.h @@ -0,0 +1,52 @@ +/* + * mips system call definitions + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +#ifndef _MIPS_SYSCALL_H_ +#define _MIPS_SYSCALL_H_ + +/* + * struct target_pt_regs defines the way the registers are stored on the stack + * during a system call. + */ + +struct target_pt_regs { + /* Saved main processor registers. */ + abi_ulong regs[32]; + + /* Saved special registers. */ + abi_ulong cp0_status; + abi_ulong lo; + abi_ulong hi; + abi_ulong cp0_badvaddr; + abi_ulong cp0_cause; + abi_ulong cp0_epc; +}; + +#if defined(TARGET_WORDS_BIGENDIAN) +#define UNAME_MACHINE "mips" +#else +#define UNAME_MACHINE "mipsel" +#endif + +#define TARGET_HW_MACHINE "mips" +#define TARGET_HW_MACHINE_ARCH UNAME_MACHINE + +/* sysarch() commands */ +#define TARGET_MIPS_SET_TLS 1 +#define TARGET_MIPS_GET_TLS 2 + +#endif /* !_MIPS_SYSCALL_H_ */ diff --git a/bsd-user/mips64/target_arch_sysarch.h b/bsd-user/mips64/target_arch_sysarch.h new file mode 100644 index 0000000000..e6f9c00d5f --- /dev/null +++ b/bsd-user/mips64/target_arch_sysarch.h @@ -0,0 +1,69 @@ +/* + * mips64 sysarch() system call emulation + * + * Copyright (c) 2013 Stacey D. Son + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef BSD_USER_ARCH_SYSARCH_H_ +#define BSD_USER_ARCH_SYSARCH_H_ + +#include "target_syscall.h" +#include "target_arch.h" + +static inline abi_long do_freebsd_arch_sysarch(CPUMIPSState *env, int op, + abi_ulong parms) +{ + int ret = 0; + + switch (op) { + case TARGET_MIPS_SET_TLS: + target_cpu_set_tls(env, parms); + break; + + case TARGET_MIPS_GET_TLS: + if (put_user(target_cpu_get_tls(env), parms, abi_ulong)) { + ret = -TARGET_EFAULT; + } + break; + + default: + ret = -TARGET_EINVAL; + break; + } + + return ret; +} + +static inline void do_freebsd_arch_print_sysarch( + const struct syscallname *name, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6) +{ + + switch (arg1) { + case TARGET_MIPS_SET_TLS: + gemu_log("%s(SET_TLS, 0x" TARGET_ABI_FMT_lx ")", name->name, arg2); + break; + + case TARGET_MIPS_GET_TLS: + gemu_log("%s(GET_TLS, 0x" TARGET_ABI_FMT_lx ")", name->name, arg2); + break; + + default: + gemu_log("UNKNOWN OP: %d, " TARGET_ABI_FMT_lx ")", (int)arg1, arg2); + } +} + +#endif /*!BSD_USER_ARCH_SYSARCH_H_ */ diff --git a/bsd-user/mips64/target_syscall.h b/bsd-user/mips64/target_syscall.h new file mode 100644 index 0000000000..bf4c598b13 --- /dev/null +++ b/bsd-user/mips64/target_syscall.h @@ -0,0 +1,53 @@ +/* + * mips64 system call definitions + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +#ifndef _MIPS64_SYSCALL_H_ +#define _MIPS64_SYSCALL_H_ + +/* + * struct target_pt_regs defines the way the registers are stored on the stack + * during a system call. + */ + +struct target_pt_regs { + /* Saved main processor registers. */ + abi_ulong regs[32]; + + /* Saved special registers. */ + abi_ulong cp0_status; + abi_ulong lo; + abi_ulong hi; + abi_ulong cp0_badvaddr; + abi_ulong cp0_cause; + abi_ulong cp0_epc; +}; + + +#if defined(TARGET_WORDS_BIGENDIAN) +#define UNAME_MACHINE "mips64" +#else +#define UNAME_MACHINE "mips64el" +#endif + +#define TARGET_HW_MACHINE "mips" +#define TARGET_HW_MACHINE_ARCH UNAME_MACHINE + +/* sysarch() commands */ +#define TARGET_MIPS_SET_TLS 1 +#define TARGET_MIPS_GET_TLS 2 + +#endif /* !_MIPS64_SYSCALL_H_ */ diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index 17f4cd80aa..01ec808003 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -102,7 +102,8 @@ int target_mprotect(abi_ulong start, abi_ulong len, int prot) } end = host_end; } - ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS); + ret = mprotect(g2h_untagged(host_start), + qemu_host_page_size, prot1 & PAGE_BITS); if (ret != 0) goto error; host_start += qemu_host_page_size; @@ -112,8 +113,8 @@ int target_mprotect(abi_ulong start, abi_ulong len, int prot) for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) { prot1 |= page_get_flags(addr); } - ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size, - prot1 & PAGE_BITS); + ret = mprotect(g2h_untagged(host_end - qemu_host_page_size), + qemu_host_page_size, prot1 & PAGE_BITS); if (ret != 0) goto error; host_end -= qemu_host_page_size; @@ -121,7 +122,7 @@ int target_mprotect(abi_ulong start, abi_ulong len, int prot) /* handle the pages in the middle */ if (host_start < host_end) { - ret = mprotect(g2h(host_start), host_end - host_start, prot); + ret = mprotect(g2h_untagged(host_start), host_end - host_start, prot); if (ret != 0) goto error; } @@ -143,7 +144,7 @@ static int mmap_frag(abi_ulong real_start, int prot1, prot_new; real_end = real_start + qemu_host_page_size; - host_start = g2h(real_start); + host_start = g2h_untagged(real_start); /* get the protection of the target pages outside the mapping */ prot1 = 0; @@ -175,7 +176,7 @@ static int mmap_frag(abi_ulong real_start, mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE); /* read the corresponding file data */ - pread(fd, g2h(start), end - start, offset); + pread(fd, g2h_untagged(start), end - start, offset); /* put final protection */ if (prot_new != (prot1 | PROT_WRITE)) @@ -300,7 +301,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, /* Note: we prefer to control the mapping address. It is especially important if qemu_host_page_size > qemu_real_host_page_size */ - p = mmap(g2h(mmap_start), + p = mmap(g2h_untagged(mmap_start), host_len, prot, flags | MAP_FIXED, fd, host_offset); if (p == MAP_FAILED) goto fail; @@ -344,7 +345,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, -1, 0); if (retaddr == -1) goto fail; - pread(fd, g2h(start), len, offset); + pread(fd, g2h_untagged(start), len, offset); if (!(prot & PROT_WRITE)) { ret = target_mprotect(start, len, prot); if (ret != 0) { @@ -390,7 +391,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, offset1 = 0; else offset1 = offset + real_start - start; - p = mmap(g2h(real_start), real_end - real_start, + p = mmap(g2h_untagged(real_start), real_end - real_start, prot, flags, fd, offset1); if (p == MAP_FAILED) goto fail; @@ -456,7 +457,7 @@ int target_munmap(abi_ulong start, abi_ulong len) ret = 0; /* unmap what we can */ if (real_start < real_end) { - ret = munmap(g2h(real_start), real_end - real_start); + ret = munmap(g2h_untagged(real_start), real_end - real_start); } if (ret == 0) @@ -479,5 +480,5 @@ int target_msync(abi_ulong start, abi_ulong len, int flags) return 0; start &= qemu_host_page_mask; - return msync(g2h(start), end - start, flags); + return msync(g2h_untagged(start), end - start, flags); } diff --git a/bsd-user/netbsd/os-strace.h b/bsd-user/netbsd/os-strace.h new file mode 100644 index 0000000000..70cf51d63a --- /dev/null +++ b/bsd-user/netbsd/os-strace.h @@ -0,0 +1 @@ +/* XXX NetBSD dependent strace print functions */ diff --git a/bsd-user/openbsd/os-strace.h b/bsd-user/openbsd/os-strace.h new file mode 100644 index 0000000000..9161390433 --- /dev/null +++ b/bsd-user/openbsd/os-strace.h @@ -0,0 +1 @@ +/* XXX OpenBSD dependent strace print functions */ diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index f8bb1e5459..d2bcaab741 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -218,13 +218,12 @@ extern unsigned long x86_stack_size; /* user access */ -#define VERIFY_READ 0 -#define VERIFY_WRITE 1 /* implies read access */ +#define VERIFY_READ PAGE_READ +#define VERIFY_WRITE (PAGE_READ | PAGE_WRITE) -static inline int access_ok(int type, abi_ulong addr, abi_ulong size) +static inline bool access_ok(int type, abi_ulong addr, abi_ulong size) { - return page_check_range((target_ulong)addr, size, - (type == VERIFY_READ) ? PAGE_READ : (PAGE_READ | PAGE_WRITE)) == 0; + return page_check_range((target_ulong)addr, size, type) == 0; } /* NOTE __get_user and __put_user use host pointers and don't check access. */ @@ -357,13 +356,13 @@ static inline void *lock_user(int type, abi_ulong guest_addr, long len, int copy void *addr; addr = g_malloc(len); if (copy) - memcpy(addr, g2h(guest_addr), len); + memcpy(addr, g2h_untagged(guest_addr), len); else memset(addr, 0, len); return addr; } #else - return g2h(guest_addr); + return g2h_untagged(guest_addr); #endif } @@ -377,10 +376,10 @@ static inline void unlock_user(void *host_ptr, abi_ulong guest_addr, #ifdef DEBUG_REMAP if (!host_ptr) return; - if (host_ptr == g2h(guest_addr)) + if (host_ptr == g2h_untagged(guest_addr)) return; if (len > 0) - memcpy(g2h(guest_addr), host_ptr, len); + memcpy(g2h_untagged(guest_addr), host_ptr, len); g_free(host_ptr); #endif } diff --git a/bsd-user/sparc/target_arch_sysarch.h b/bsd-user/sparc/target_arch_sysarch.h new file mode 100644 index 0000000000..d0b85ef6bb --- /dev/null +++ b/bsd-user/sparc/target_arch_sysarch.h @@ -0,0 +1,52 @@ +/* + * SPARC sysarch() system call emulation + * + * Copyright (c) 2013 Stacey D. Son + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef BSD_USER_ARCH_SYSARCH_H_ +#define BSD_USER_ARCH_SYSARCH_H_ + +#include "target_syscall.h" + +static inline abi_long do_freebsd_arch_sysarch(void *env, int op, + abi_ulong parms) +{ + int ret = 0; + + switch (op) { + case TARGET_SPARC_SIGTRAMP_INSTALL: + /* XXX not currently handled */ + case TARGET_SPARC_UTRAP_INSTALL: + /* XXX not currently handled */ + default: + ret = -TARGET_EINVAL; + break; + } + + return ret; +} + +static inline void do_freebsd_arch_print_sysarch( + const struct syscallname *name, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6) +{ + + gemu_log("%s(%d, " TARGET_ABI_FMT_lx ", " TARGET_ABI_FMT_lx ", " + TARGET_ABI_FMT_lx ")", name->name, (int)arg1, arg2, arg3, arg4); +} + +#endif /*!BSD_USER_ARCH_SYSARCH_H_ */ diff --git a/bsd-user/sparc/target_syscall.h b/bsd-user/sparc/target_syscall.h index dfdf9f82f5..151284754b 100644 --- a/bsd-user/sparc/target_syscall.h +++ b/bsd-user/sparc/target_syscall.h @@ -1,3 +1,20 @@ +/* + * sparc dependent system call definitions + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ #ifndef TARGET_SYSCALL_H #define TARGET_SYSCALL_H @@ -9,6 +26,11 @@ struct target_pt_regs { abi_ulong u_regs[16]; }; -#define UNAME_MACHINE "sun4" +#define UNAME_MACHINE "sun4" +#define TARGET_HW_MACHINE "sparc" +#define TARGET_HW_MACHINE_ARCH "sparc" + +#define TARGET_SPARC_UTRAP_INSTALL 1 +#define TARGET_SPARC_SIGTRAMP_INSTALL 2 #endif /* TARGET_SYSCALL_H */ diff --git a/bsd-user/sparc64/target_arch_sysarch.h b/bsd-user/sparc64/target_arch_sysarch.h new file mode 100644 index 0000000000..e6f17c1504 --- /dev/null +++ b/bsd-user/sparc64/target_arch_sysarch.h @@ -0,0 +1,52 @@ +/* + * SPARC64 sysarch() system call emulation + * + * Copyright (c) 2013 Stacey D. Son + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef BSD_USER_ARCH_SYSARCH_H_ +#define BSD_USER_ARCH_SYSARCH_H_ + +#include "target_syscall.h" + +static inline abi_long do_freebsd_arch_sysarch(void *env, int op, + abi_ulong parms) +{ + int ret = 0; + + switch (op) { + case TARGET_SPARC_SIGTRAMP_INSTALL: + /* XXX not currently handled */ + case TARGET_SPARC_UTRAP_INSTALL: + /* XXX not currently handled */ + default: + ret = -TARGET_EINVAL; + break; + } + + return ret; +} + +static inline void do_freebsd_arch_print_sysarch( + const struct syscallname *name, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6) +{ + + gemu_log("%s(%d, " TARGET_ABI_FMT_lx ", " TARGET_ABI_FMT_lx ", " + TARGET_ABI_FMT_lx ")", name->name, (int)arg1, arg2, arg3, arg4); +} + +#endif /*!BSD_USER_ARCH_SYSARCH_H_ */ diff --git a/bsd-user/sparc64/target_syscall.h b/bsd-user/sparc64/target_syscall.h index 3a9f4c2ef9..b7d986a76d 100644 --- a/bsd-user/sparc64/target_syscall.h +++ b/bsd-user/sparc64/target_syscall.h @@ -1,3 +1,20 @@ +/* + * sparc64 dependent system call definitions + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ #ifndef TARGET_SYSCALL_H #define TARGET_SYSCALL_H @@ -10,6 +27,11 @@ struct target_pt_regs { abi_ulong fprs; }; -#define UNAME_MACHINE "sun4u" +#define UNAME_MACHINE "sun4u" +#define TARGET_HW_MACHINE "sparc" +#define TARGET_HW_MACHINE_ARCH "sparc64" + +#define TARGET_SPARC_UTRAP_INSTALL 1 +#define TARGET_SPARC_SIGTRAMP_INSTALL 2 #endif /* TARGET_SYSCALL_H */ diff --git a/bsd-user/strace.c b/bsd-user/strace.c index fa66fe1ee2..2c3b59caf0 100644 --- a/bsd-user/strace.c +++ b/bsd-user/strace.c @@ -20,9 +20,12 @@ #include #include #include +#include #include "qemu.h" +#include "os-strace.h" /* OS dependent strace print functions */ + int do_strace; /* @@ -104,6 +107,14 @@ static void print_ioctl(const struct syscallname *name, arg3); } +static void print_sysarch(const struct syscallname *name, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, + abi_long arg6) +{ + /* This is os dependent. */ + do_os_print_sysarch(name, arg1, arg2, arg3, arg4, arg5, arg6); +} + /* * Variants for the return value output function */ diff --git a/bsd-user/syscall.c b/bsd-user/syscall.c index d38ec7a162..adc3d21b54 100644 --- a/bsd-user/syscall.c +++ b/bsd-user/syscall.c @@ -333,7 +333,7 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1, #ifdef CONFIG_GPROF _mcleanup(); #endif - gdb_exit(cpu_env, arg1); + gdb_exit(arg1); qemu_plugin_atexit_cb(); /* XXX: should free thread stack and CPU env */ _exit(arg1); @@ -435,7 +435,7 @@ abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1, #ifdef CONFIG_GPROF _mcleanup(); #endif - gdb_exit(cpu_env, arg1); + gdb_exit(arg1); qemu_plugin_atexit_cb(); /* XXX: should free thread stack and CPU env */ _exit(arg1); @@ -514,7 +514,7 @@ abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1, #ifdef CONFIG_GPROF _mcleanup(); #endif - gdb_exit(cpu_env, arg1); + gdb_exit(arg1); qemu_plugin_atexit_cb(); /* XXX: should free thread stack and CPU env */ _exit(arg1); diff --git a/bsd-user/x86_64/target_arch_sysarch.h b/bsd-user/x86_64/target_arch_sysarch.h new file mode 100644 index 0000000000..5c36fc0752 --- /dev/null +++ b/bsd-user/x86_64/target_arch_sysarch.h @@ -0,0 +1,76 @@ +/* + * x86_64 sysarch() syscall emulation + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef BSD_USER_ARCH_SYSARCH_H_ +#define BSD_USER_ARCH_SYSARCH_H_ + +#include "target_syscall.h" + +static inline abi_long do_freebsd_arch_sysarch(CPUX86State *env, int op, + abi_ulong parms) +{ + abi_long ret = 0; + abi_ulong val; + int idx; + + switch (op) { + case TARGET_FREEBSD_AMD64_SET_GSBASE: + case TARGET_FREEBSD_AMD64_SET_FSBASE: + if (op == TARGET_FREEBSD_AMD64_SET_GSBASE) { + idx = R_GS; + } else { + idx = R_FS; + } + if (get_user(val, parms, abi_ulong)) { + return -TARGET_EFAULT; + } + cpu_x86_load_seg(env, idx, 0); + env->segs[idx].base = val; + break; + + case TARGET_FREEBSD_AMD64_GET_GSBASE: + case TARGET_FREEBSD_AMD64_GET_FSBASE: + if (op == TARGET_FREEBSD_AMD64_GET_GSBASE) { + idx = R_GS; + } else { + idx = R_FS; + } + val = env->segs[idx].base; + if (put_user(val, parms, abi_ulong)) { + return -TARGET_EFAULT; + } + break; + + /* XXX handle the others... */ + default: + ret = -TARGET_EINVAL; + break; + } + return ret; +} + +static inline void do_freebsd_arch_print_sysarch( + const struct syscallname *name, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6) +{ + + gemu_log("%s(%d, " TARGET_ABI_FMT_lx ", " TARGET_ABI_FMT_lx ", " + TARGET_ABI_FMT_lx ")", name->name, (int)arg1, arg2, arg3, arg4); +} + +#endif /*! BSD_USER_ARCH_SYSARCH_H_ */ diff --git a/bsd-user/x86_64/target_syscall.h b/bsd-user/x86_64/target_syscall.h index a5d779884f..a8e6274b76 100644 --- a/bsd-user/x86_64/target_syscall.h +++ b/bsd-user/x86_64/target_syscall.h @@ -1,3 +1,20 @@ +/* + * x86_64 system call definitions + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ #ifndef TARGET_SYSCALL_H #define TARGET_SYSCALL_H @@ -111,7 +128,9 @@ struct target_msqid64_ds { #define TARGET_FREEBSD_AMD64_SET_GSBASE 131 -#define UNAME_MACHINE "x86_64" +#define UNAME_MACHINE "x86_64" +#define TARGET_HW_MACHINE "amd64" +#define TARGET_HW_MACHINE_ARCH "amd64" #define TARGET_ARCH_SET_GS 0x1001 #define TARGET_ARCH_SET_FS 0x1002 diff --git a/capstone b/capstone index 5f173b0562..f8b1b83301 160000 --- a/capstone +++ b/capstone @@ -1 +1 @@ -Subproject commit 5f173b0562ae2bf875cfdd71691776c2d9ed177f +Subproject commit f8b1b833015a4ae47110ed068e0deb7106ced66d diff --git a/chardev/Makefile.objs b/chardev/Makefile.objs deleted file mode 100644 index 3783dadc4c..0000000000 --- a/chardev/Makefile.objs +++ /dev/null @@ -1,29 +0,0 @@ -chardev-obj-y += char.o -chardev-obj-$(CONFIG_SOFTMMU) += chardev-sysemu.o -chardev-obj-$(CONFIG_WIN32) += char-console.o -chardev-obj-$(CONFIG_POSIX) += char-fd.o -chardev-obj-y += char-fe.o -chardev-obj-y += char-file.o -chardev-obj-y += char-io.o -chardev-obj-y += char-mux.o -chardev-obj-y += char-null.o -chardev-obj-$(CONFIG_POSIX) += char-parallel.o -chardev-obj-y += char-pipe.o -chardev-obj-$(CONFIG_POSIX) += char-pty.o -chardev-obj-y += char-ringbuf.o -chardev-obj-y += char-serial.o -chardev-obj-y += char-socket.o -chardev-obj-y += char-stdio.o -chardev-obj-y += char-udp.o -chardev-obj-$(CONFIG_WIN32) += char-win.o -chardev-obj-$(CONFIG_WIN32) += char-win-stdio.o - -common-obj-$(CONFIG_SOFTMMU) += msmouse.o wctablet.o testdev.o - -ifeq ($(CONFIG_BRLAPI),y) -common-obj-m += baum.o -baum.o-cflags := $(SDL_CFLAGS) -baum.o-libs := $(BRLAPI_LIBS) -endif - -common-obj-$(CONFIG_SPICE) += spice.o diff --git a/chardev/baum.c b/chardev/baum.c index 9c95e7bc79..5deca778bc 100644 --- a/chardev/baum.c +++ b/chardev/baum.c @@ -33,6 +33,7 @@ #include #include #include +#include "qom/object.h" #if 0 #define DPRINTF(fmt, ...) \ @@ -86,7 +87,7 @@ #define BUF_SIZE 256 -typedef struct { +struct BaumChardev { Chardev parent; brlapi_handle_t *brlapi; @@ -100,10 +101,12 @@ typedef struct { uint8_t out_buf_used, out_buf_ptr; QEMUTimer *cellCount_timer; -} BaumChardev; +}; +typedef struct BaumChardev BaumChardev; #define TYPE_CHARDEV_BRAILLE "chardev-braille" -#define BAUM_CHARDEV(obj) OBJECT_CHECK(BaumChardev, (obj), TYPE_CHARDEV_BRAILLE) +DECLARE_INSTANCE_CHECKER(BaumChardev, BAUM_CHARDEV, + TYPE_CHARDEV_BRAILLE) /* Let's assume NABCC by default */ enum way { diff --git a/chardev/char-fd.c b/chardev/char-fd.c index c2d8101106..1cd62f2779 100644 --- a/chardev/char-fd.c +++ b/chardev/char-fd.c @@ -119,7 +119,7 @@ int qmp_chardev_open_file_source(char *src, int flags, Error **errp) { int fd = -1; - TFR(fd = qemu_open(src, flags, 0666)); + TFR(fd = qemu_open_old(src, flags, 0666)); if (fd == -1) { error_setg_file_open(errp, errno, src); } diff --git a/chardev/char-mux.c b/chardev/char-mux.c index 6f980bb836..72beef29d2 100644 --- a/chardev/char-mux.c +++ b/chardev/char-mux.c @@ -33,6 +33,13 @@ /* MUX driver for serial I/O splitting */ +/* + * Set to false by suspend_mux_open. Open events are delayed until + * resume_mux_open. Usually suspend_mux_open is called before + * command line processing and resume_mux_open afterwards. + */ +static bool muxes_opened = true; + /* Called with chr_write_lock held. */ static int mux_chr_write(Chardev *chr, const uint8_t *buf, int len) { @@ -237,7 +244,7 @@ void mux_chr_send_all_event(Chardev *chr, QEMUChrEvent event) MuxChardev *d = MUX_CHARDEV(chr); int i; - if (!machine_init_done) { + if (!muxes_opened) { return; } @@ -328,7 +335,7 @@ static void qemu_chr_open_mux(Chardev *chr, /* only default to opened state if we've realized the initial * set of muxes */ - *be_opened = machine_init_done; + *be_opened = muxes_opened; qemu_chr_fe_init(&d->chr, drv, errp); } @@ -360,19 +367,42 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend, * mux will receive CHR_EVENT_OPENED notifications for the BE * immediately. */ -static int open_muxes(Chardev *chr) +static void open_muxes(Chardev *chr) { /* send OPENED to all already-attached FEs */ mux_chr_send_all_event(chr, CHR_EVENT_OPENED); + /* * mark mux as OPENED so any new FEs will immediately receive * OPENED event */ chr->be_open = 1; +} + +void suspend_mux_open(void) +{ + muxes_opened = false; +} + +static int chardev_options_parsed_cb(Object *child, void *opaque) +{ + Chardev *chr = (Chardev *)child; + ChardevClass *class = CHARDEV_GET_CLASS(chr); + + if (!chr->be_open && class->chr_options_parsed) { + class->chr_options_parsed(chr); + } return 0; } +void resume_mux_open(void) +{ + muxes_opened = true; + object_child_foreach(get_chardevs_root(), + chardev_options_parsed_cb, NULL); +} + static void char_mux_class_init(ObjectClass *oc, void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); @@ -383,7 +413,7 @@ static void char_mux_class_init(ObjectClass *oc, void *data) cc->chr_accept_input = mux_chr_accept_input; cc->chr_add_watch = mux_chr_add_watch; cc->chr_be_event = mux_chr_be_event; - cc->chr_machine_done = open_muxes; + cc->chr_options_parsed = open_muxes; cc->chr_update_read_handler = mux_chr_update_read_handlers; } diff --git a/chardev/char-pipe.c b/chardev/char-pipe.c index fd12c9e63b..7eca5d9a56 100644 --- a/chardev/char-pipe.c +++ b/chardev/char-pipe.c @@ -132,8 +132,8 @@ static void qemu_chr_open_pipe(Chardev *chr, filename_in = g_strdup_printf("%s.in", filename); filename_out = g_strdup_printf("%s.out", filename); - TFR(fd_in = qemu_open(filename_in, O_RDWR | O_BINARY)); - TFR(fd_out = qemu_open(filename_out, O_RDWR | O_BINARY)); + TFR(fd_in = qemu_open_old(filename_in, O_RDWR | O_BINARY)); + TFR(fd_out = qemu_open_old(filename_out, O_RDWR | O_BINARY)); g_free(filename_in); g_free(filename_out); if (fd_in < 0 || fd_out < 0) { @@ -143,7 +143,7 @@ static void qemu_chr_open_pipe(Chardev *chr, if (fd_out >= 0) { close(fd_out); } - TFR(fd_in = fd_out = qemu_open(filename, O_RDWR | O_BINARY)); + TFR(fd_in = fd_out = qemu_open_old(filename, O_RDWR | O_BINARY)); if (fd_in < 0) { error_setg_file_open(errp, errno, filename); return; diff --git a/chardev/char-pty.c b/chardev/char-pty.c index 1cc501a481..a2d1e7c985 100644 --- a/chardev/char-pty.c +++ b/chardev/char-pty.c @@ -33,17 +33,20 @@ #include "qemu/qemu-print.h" #include "chardev/char-io.h" +#include "qom/object.h" -typedef struct { +struct PtyChardev { Chardev parent; QIOChannel *ioc; int read_bytes; int connected; GSource *timer_src; -} PtyChardev; +}; +typedef struct PtyChardev PtyChardev; -#define PTY_CHARDEV(obj) OBJECT_CHECK(PtyChardev, (obj), TYPE_CHARDEV_PTY) +DECLARE_INSTANCE_CHECKER(PtyChardev, PTY_CHARDEV, + TYPE_CHARDEV_PTY) static void pty_chr_state(Chardev *chr, int connected); diff --git a/chardev/char-ringbuf.c b/chardev/char-ringbuf.c index 67397a8ce9..d40d21d3cf 100644 --- a/chardev/char-ringbuf.c +++ b/chardev/char-ringbuf.c @@ -29,19 +29,21 @@ #include "qemu/base64.h" #include "qemu/module.h" #include "qemu/option.h" +#include "qom/object.h" /* Ring buffer chardev */ -typedef struct { +struct RingBufChardev { Chardev parent; size_t size; size_t prod; size_t cons; uint8_t *cbuf; -} RingBufChardev; +}; +typedef struct RingBufChardev RingBufChardev; -#define RINGBUF_CHARDEV(obj) \ - OBJECT_CHECK(RingBufChardev, (obj), TYPE_CHARDEV_RINGBUF) +DECLARE_INSTANCE_CHECKER(RingBufChardev, RINGBUF_CHARDEV, + TYPE_CHARDEV_RINGBUF) static size_t ringbuf_count(const Chardev *chr) { diff --git a/chardev/char-socket.c b/chardev/char-socket.c index ef62dbf3d7..daa89fe5d1 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -34,8 +34,10 @@ #include "qapi/error.h" #include "qapi/clone-visitor.h" #include "qapi/qapi-visit-sockets.h" +#include "qemu/yank.h" #include "chardev/char-io.h" +#include "qom/object.h" /***********************************************************/ /* TCP Net console */ @@ -53,7 +55,7 @@ typedef enum { TCP_CHARDEV_STATE_CONNECTED, } TCPChardevState; -typedef struct { +struct SocketChardev { Chardev parent; QIOChannel *ioc; /* Client I/O channel */ QIOChannelSocket *sioc; /* Client master channel */ @@ -69,6 +71,7 @@ typedef struct { size_t read_msgfds_num; int *write_msgfds; size_t write_msgfds_num; + bool registered_yank; SocketAddress *addr; bool is_listen; @@ -84,10 +87,11 @@ typedef struct { bool connect_err_reported; QIOTask *connect_task; -} SocketChardev; +}; +typedef struct SocketChardev SocketChardev; -#define SOCKET_CHARDEV(obj) \ - OBJECT_CHECK(SocketChardev, (obj), TYPE_CHARDEV_SOCKET) +DECLARE_INSTANCE_CHECKER(SocketChardev, SOCKET_CHARDEV, + TYPE_CHARDEV_SOCKET) static gboolean socket_reconnect_timeout(gpointer opaque); static void tcp_chr_telnet_init(Chardev *chr); @@ -383,6 +387,9 @@ static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len) static GSource *tcp_chr_add_watch(Chardev *chr, GIOCondition cond) { SocketChardev *s = SOCKET_CHARDEV(chr); + if (!s->ioc) { + return NULL; + } return qio_channel_create_watch(s->ioc, cond); } @@ -395,6 +402,13 @@ static void remove_hup_source(SocketChardev *s) } } +static void char_socket_yank_iochannel(void *opaque) +{ + QIOChannel *ioc = QIO_CHANNEL(opaque); + + qio_channel_shutdown(ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); +} + static void tcp_chr_free_connection(Chardev *chr) { SocketChardev *s = SOCKET_CHARDEV(chr); @@ -413,6 +427,13 @@ static void tcp_chr_free_connection(Chardev *chr) tcp_set_msgfds(chr, NULL, 0); remove_fd_in_watch(chr); + if (s->registered_yank && + (s->state == TCP_CHARDEV_STATE_CONNECTING + || s->state == TCP_CHARDEV_STATE_CONNECTED)) { + yank_unregister_function(CHARDEV_YANK_INSTANCE(chr->label), + char_socket_yank_iochannel, + QIO_CHANNEL(s->sioc)); + } object_unref(OBJECT(s->sioc)); s->sioc = NULL; object_unref(OBJECT(s->ioc)); @@ -438,16 +459,30 @@ static char *qemu_chr_socket_address(SocketChardev *s, const char *prefix) qemu_chr_socket_protocol(s), s->addr->u.inet.host, s->addr->u.inet.port, - s->is_listen ? ",server" : ""); + s->is_listen ? ",server=on" : ""); break; case SOCKET_ADDRESS_TYPE_UNIX: - return g_strdup_printf("%sunix:%s%s", prefix, - s->addr->u.q_unix.path, - s->is_listen ? ",server" : ""); + { + const char *tight = "", *abstract = ""; + UnixSocketAddress *sa = &s->addr->u.q_unix; + +#ifdef CONFIG_LINUX + if (sa->has_abstract && sa->abstract) { + abstract = ",abstract"; + if (sa->has_tight && sa->tight) { + tight = ",tight"; + } + } +#endif + + return g_strdup_printf("%sunix:%s%s%s%s", prefix, sa->path, + abstract, tight, + s->is_listen ? ",server=on" : ""); break; + } case SOCKET_ADDRESS_TYPE_FD: return g_strdup_printf("%sfd:%s%s", prefix, s->addr->u.fd.str, - s->is_listen ? ",server" : ""); + s->is_listen ? ",server=on" : ""); break; case SOCKET_ADDRESS_TYPE_VSOCK: return g_strdup_printf("%svsock:%s:%s", prefix, @@ -579,7 +614,7 @@ static char *qemu_chr_compute_filename(SocketChardev *s) case AF_UNIX: return g_strdup_printf("unix:%s%s", ((struct sockaddr_un *)(ss))->sun_path, - s->is_listen ? ",server" : ""); + s->is_listen ? ",server=on" : ""); #endif case AF_INET6: left = "["; @@ -593,7 +628,7 @@ static char *qemu_chr_compute_filename(SocketChardev *s) return g_strdup_printf("%s:%s%s%s:%s%s <-> %s%s%s:%s", qemu_chr_socket_protocol(s), left, shost, right, sserv, - s->is_listen ? ",server" : "", + s->is_listen ? ",server=on" : "", left, phost, right, pserv); default: @@ -916,6 +951,11 @@ static int tcp_chr_add_client(Chardev *chr, int fd) } tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); tcp_chr_set_client_ioc_name(chr, sioc); + if (s->registered_yank) { + yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), + char_socket_yank_iochannel, + QIO_CHANNEL(sioc)); + } ret = tcp_chr_new_client(chr, sioc); object_unref(OBJECT(sioc)); return ret; @@ -930,6 +970,11 @@ static void tcp_chr_accept(QIONetListener *listener, tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); tcp_chr_set_client_ioc_name(chr, cioc); + if (s->registered_yank) { + yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), + char_socket_yank_iochannel, + QIO_CHANNEL(cioc)); + } tcp_chr_new_client(chr, cioc); } @@ -945,6 +990,11 @@ static int tcp_chr_connect_client_sync(Chardev *chr, Error **errp) object_unref(OBJECT(sioc)); return -1; } + if (s->registered_yank) { + yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), + char_socket_yank_iochannel, + QIO_CHANNEL(sioc)); + } tcp_chr_new_client(chr, sioc); object_unref(OBJECT(sioc)); return 0; @@ -960,6 +1010,11 @@ static void tcp_chr_accept_server_sync(Chardev *chr) tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); sioc = qio_net_listener_wait_client(s->listener); tcp_chr_set_client_ioc_name(chr, sioc); + if (s->registered_yank) { + yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), + char_socket_yank_iochannel, + QIO_CHANNEL(sioc)); + } tcp_chr_new_client(chr, sioc); object_unref(OBJECT(sioc)); } @@ -1070,6 +1125,15 @@ static void char_socket_finalize(Object *obj) object_unref(OBJECT(s->tls_creds)); } g_free(s->tls_authz); + if (s->registered_yank) { + /* + * In the chardev-change special-case, we shouldn't unregister the yank + * instance, as it still may be needed. + */ + if (!chr->handover_yank_instance) { + yank_unregister_instance(CHARDEV_YANK_INSTANCE(chr->label)); + } + } qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } @@ -1085,6 +1149,11 @@ static void qemu_chr_socket_connected(QIOTask *task, void *opaque) if (qio_task_propagate_error(task, &err)) { tcp_chr_change_state(s, TCP_CHARDEV_STATE_DISCONNECTED); + if (s->registered_yank) { + yank_unregister_function(CHARDEV_YANK_INSTANCE(chr->label), + char_socket_yank_iochannel, + QIO_CHANNEL(sioc)); + } check_report_connect_error(chr, err); goto cleanup; } @@ -1118,6 +1187,11 @@ static void tcp_chr_connect_client_async(Chardev *chr) tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); sioc = qio_channel_socket_new(); tcp_chr_set_client_ioc_name(chr, sioc); + if (s->registered_yank) { + yank_register_function(CHARDEV_YANK_INSTANCE(chr->label), + char_socket_yank_iochannel, + QIO_CHANNEL(sioc)); + } /* * Normally code would use the qio_channel_socket_connect_async * method which uses a QIOTask + qio_task_set_error internally @@ -1278,14 +1352,10 @@ static bool qmp_chardev_validate_socket(ChardevSocket *sock, return false; } if (sock->has_wait) { - warn_report("'wait' option is deprecated with " - "socket in client connect mode"); - if (sock->wait) { - error_setg(errp, "%s", - "'wait' option is incompatible with " - "socket in client connect mode"); - return false; - } + error_setg(errp, "%s", + "'wait' option is incompatible with " + "socket in client connect mode"); + return false; } } @@ -1360,6 +1430,17 @@ static void qmp_chardev_open_socket(Chardev *chr, qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_FD_PASS); } + /* + * In the chardev-change special-case, we shouldn't register a new yank + * instance, as there already may be one. + */ + if (!chr->handover_yank_instance) { + if (!yank_register_instance(CHARDEV_YANK_INSTANCE(chr->label), errp)) { + return; + } + } + s->registered_yank = true; + /* be isn't opened until we get a connection */ *be_opened = false; @@ -1384,8 +1465,10 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, const char *host = qemu_opt_get(opts, "host"); const char *port = qemu_opt_get(opts, "port"); const char *fd = qemu_opt_get(opts, "fd"); +#ifdef CONFIG_LINUX bool tight = qemu_opt_get_bool(opts, "tight", true); bool abstract = qemu_opt_get_bool(opts, "abstract", false); +#endif SocketAddressLegacy *addr; ChardevSocket *sock; @@ -1404,8 +1487,17 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, sock = backend->u.socket.data = g_new0(ChardevSocket, 1); qemu_chr_parse_common(opts, qapi_ChardevSocket_base(sock)); - sock->has_nodelay = qemu_opt_get(opts, "delay"); - sock->nodelay = !qemu_opt_get_bool(opts, "delay", true); + if (qemu_opt_get(opts, "delay") && qemu_opt_get(opts, "nodelay")) { + error_setg(errp, "'delay' and 'nodelay' are mutually exclusive"); + return; + } + sock->has_nodelay = + qemu_opt_get(opts, "delay") || + qemu_opt_get(opts, "nodelay"); + sock->nodelay = + !qemu_opt_get_bool(opts, "delay", true) || + qemu_opt_get_bool(opts, "nodelay", false); + /* * We have different default to QMP for 'server', hence * we can't just check for existence of 'server' @@ -1437,8 +1529,12 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, addr->type = SOCKET_ADDRESS_LEGACY_KIND_UNIX; q_unix = addr->u.q_unix.data = g_new0(UnixSocketAddress, 1); q_unix->path = g_strdup(path); +#ifdef CONFIG_LINUX + q_unix->has_tight = true; q_unix->tight = tight; + q_unix->has_abstract = true; q_unix->abstract = abstract; +#endif } else if (host) { addr->type = SOCKET_ADDRESS_LEGACY_KIND_INET; addr->u.inet.data = g_new(InetSocketAddress, 1); @@ -1483,6 +1579,8 @@ static void char_socket_class_init(ObjectClass *oc, void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); + cc->supports_yank = true; + cc->parse = qemu_chr_parse_socket; cc->open = qmp_chardev_open_socket; cc->chr_wait_connected = tcp_chr_wait_connected; diff --git a/chardev/char-stdio.c b/chardev/char-stdio.c index 82eaebc1db..403da308c9 100644 --- a/chardev/char-stdio.c +++ b/chardev/char-stdio.c @@ -112,9 +112,7 @@ static void qemu_chr_open_stdio(Chardev *chr, qemu_chr_open_fd(chr, 0, 1); - if (opts->has_signal) { - stdio_allow_signal = opts->signal; - } + stdio_allow_signal = !opts->has_signal || opts->signal; qemu_chr_set_echo_stdio(chr, false); } #endif diff --git a/chardev/char-udp.c b/chardev/char-udp.c index bba4145f96..16b5dbce58 100644 --- a/chardev/char-udp.c +++ b/chardev/char-udp.c @@ -30,20 +30,23 @@ #include "qemu/option.h" #include "chardev/char-io.h" +#include "qom/object.h" /***********************************************************/ /* UDP Net console */ -typedef struct { +struct UdpChardev { Chardev parent; QIOChannel *ioc; uint8_t buf[CHR_READ_BUF_LEN]; int bufcnt; int bufptr; int max_size; -} UdpChardev; +}; +typedef struct UdpChardev UdpChardev; -#define UDP_CHARDEV(obj) OBJECT_CHECK(UdpChardev, (obj), TYPE_CHARDEV_UDP) +DECLARE_INSTANCE_CHECKER(UdpChardev, UDP_CHARDEV, + TYPE_CHARDEV_UDP) /* Called with chr_write_lock held. */ static int udp_chr_write(Chardev *chr, const uint8_t *buf, int len) diff --git a/chardev/char-win-stdio.c b/chardev/char-win-stdio.c index 99afda353c..a4771ab82e 100644 --- a/chardev/char-win-stdio.c +++ b/chardev/char-win-stdio.c @@ -28,18 +28,20 @@ #include "qemu/module.h" #include "chardev/char-win.h" #include "chardev/char-win-stdio.h" +#include "qom/object.h" -typedef struct { +struct WinStdioChardev { Chardev parent; HANDLE hStdIn; HANDLE hInputReadyEvent; HANDLE hInputDoneEvent; HANDLE hInputThread; uint8_t win_stdio_buf; -} WinStdioChardev; +}; +typedef struct WinStdioChardev WinStdioChardev; -#define WIN_STDIO_CHARDEV(obj) \ - OBJECT_CHECK(WinStdioChardev, (obj), TYPE_CHARDEV_WIN_STDIO) +DECLARE_INSTANCE_CHECKER(WinStdioChardev, WIN_STDIO_CHARDEV, + TYPE_CHARDEV_WIN_STDIO) static void win_stdio_wait_func(void *opaque) { diff --git a/chardev/char.c b/chardev/char.c index 7472b64b6b..f6e571fd70 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -39,6 +39,7 @@ #include "qemu/option.h" #include "qemu/id.h" #include "qemu/coroutine.h" +#include "qemu/yank.h" #include "chardev-internal.h" @@ -142,7 +143,20 @@ static int qemu_chr_write_buffer(Chardev *s, } } if (*offset > 0) { + /* + * If some data was written by backend, we should + * only log what was actually written. This method + * may be invoked again to write the remaining + * method, thus we'll log the remainder at that time. + */ qemu_chr_write_log(s, buf, *offset); + } else if (res < 0) { + /* + * If a fatal error was reported by the backend, + * assume this method won't be invoked again with + * this buffer, so log it all right away. + */ + qemu_chr_write_log(s, buf, len); } qemu_mutex_unlock(&s->chr_write_lock); @@ -249,7 +263,7 @@ static void qemu_char_open(Chardev *chr, ChardevBackend *backend, } else { flags |= O_TRUNC; } - chr->logfd = qemu_open(common->logfile, flags, 0666); + chr->logfd = qemu_open_old(common->logfile, flags, 0666); if (chr->logfd < 0) { error_setg_errno(errp, errno, "Unable to open logfile %s", @@ -267,6 +281,7 @@ static void char_init(Object *obj) { Chardev *chr = CHARDEV(obj); + chr->handover_yank_instance = false; chr->logfd = -1; #ifdef XBOX @@ -528,7 +543,7 @@ static const ChardevClass *char_get_class(const char *driver, Error **errp) if (object_class_is_abstract(oc)) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver", - "abstract device type"); + "an abstract device type"); return NULL; } @@ -541,9 +556,10 @@ static const ChardevClass *char_get_class(const char *driver, Error **errp) return cc; } -static const struct ChardevAlias { +static struct ChardevAlias { const char *typename; const char *alias; + bool deprecation_warning_printed; } chardev_alias_table[] = { #ifdef HAVE_CHARDEV_PARPORT { "parallel", "parport" }, @@ -572,16 +588,12 @@ chardev_class_foreach(ObjectClass *klass, void *opaque) } static void -chardev_name_foreach(void (*fn)(const char *name, void *opaque), void *opaque) +chardev_name_foreach(void (*fn)(const char *name, void *opaque), + void *opaque) { ChadevClassFE fe = { .fn = fn, .opaque = opaque }; - int i; object_class_foreach(chardev_class_foreach, TYPE_CHARDEV, false, &fe); - - for (i = 0; i < (int)ARRAY_SIZE(chardev_alias_table); i++) { - fn(chardev_alias_table[i].alias, opaque); - } } static void @@ -597,6 +609,11 @@ static const char *chardev_alias_translate(const char *name) int i; for (i = 0; i < (int)ARRAY_SIZE(chardev_alias_table); i++) { if (g_strcmp0(chardev_alias_table[i].alias, name) == 0) { + if (!chardev_alias_table[i].deprecation_warning_printed) { + warn_report("The alias '%s' is deprecated, use '%s' instead", + name, chardev_alias_table[i].typename); + chardev_alias_table[i].deprecation_warning_printed = true; + } return chardev_alias_table[i].typename; } } @@ -783,15 +800,13 @@ static int qmp_query_chardev_foreach(Object *obj, void *data) { Chardev *chr = CHARDEV(obj); ChardevInfoList **list = data; - ChardevInfoList *info = g_malloc0(sizeof(*info)); + ChardevInfo *value = g_malloc0(sizeof(*value)); - info->value = g_malloc0(sizeof(*info->value)); - info->value->label = g_strdup(chr->label); - info->value->filename = g_strdup(chr->filename); - info->value->frontend_open = chr->be && chr->be->fe_open; + value->label = g_strdup(chr->label); + value->filename = g_strdup(chr->filename); + value->frontend_open = chr->be && chr->be->fe_open; - info->next = *list; - *list = info; + QAPI_LIST_PREPEND(*list, value); return 0; } @@ -810,12 +825,11 @@ static void qmp_prepend_backend(const char *name, void *opaque) { ChardevBackendInfoList **list = opaque; - ChardevBackendInfoList *info = g_malloc0(sizeof(*info)); + ChardevBackendInfo *value; - info->value = g_malloc0(sizeof(*info->value)); - info->value->name = g_strdup(name); - info->next = *list; - *list = info; + value = g_new0(ChardevBackendInfo, 1); + value->name = g_strdup(name); + QAPI_LIST_PREPEND(*list, value); } ChardevBackendInfoList *qmp_query_chardev_backends(Error **errp) @@ -878,6 +892,9 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "delay", .type = QEMU_OPT_BOOL, + },{ + .name = "nodelay", + .type = QEMU_OPT_BOOL, },{ .name = "reconnect", .type = QEMU_OPT_NUMBER, @@ -935,6 +952,7 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "logappend", .type = QEMU_OPT_BOOL, +#ifdef CONFIG_LINUX },{ .name = "tight", .type = QEMU_OPT_BOOL, @@ -942,6 +960,7 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "abstract", .type = QEMU_OPT_BOOL, +#endif }, { /* end of list */ } }, @@ -962,6 +981,7 @@ void qemu_chr_set_feature(Chardev *chr, static Chardev *chardev_new(const char *id, const char *typename, ChardevBackend *backend, GMainContext *gcontext, + bool handover_yank_instance, Error **errp) { Object *obj; @@ -970,9 +990,11 @@ static Chardev *chardev_new(const char *id, const char *typename, bool be_opened = true; assert(g_str_has_prefix(typename, "chardev-")); + assert(id); obj = object_new(typename); chr = CHARDEV(obj); + chr->handover_yank_instance = handover_yank_instance; chr->label = g_strdup(id); #ifdef XBOX @@ -987,7 +1009,9 @@ static Chardev *chardev_new(const char *id, const char *typename, qemu_char_open(chr, backend, &be_opened, &local_err); if (local_err) { - goto end; + error_propagate(errp, local_err); + object_unref(obj); + return NULL; } if (!chr->filename) { @@ -997,22 +1021,6 @@ static Chardev *chardev_new(const char *id, const char *typename, qemu_chr_be_event(chr, CHR_EVENT_OPENED); } - if (id) { - object_property_try_add_child(get_chardevs_root(), id, obj, - &local_err); - if (local_err) { - goto end; - } - object_unref(obj); - } - -end: - if (local_err) { - error_propagate(errp, local_err); - object_unref(obj); - return NULL; - } - return chr; } @@ -1021,6 +1029,7 @@ Chardev *qemu_chardev_new(const char *id, const char *typename, GMainContext *gcontext, Error **errp) { + Chardev *chr; g_autofree char *genid = NULL; if (!id) { @@ -1028,7 +1037,19 @@ Chardev *qemu_chardev_new(const char *id, const char *typename, id = genid; } - return chardev_new(id, typename, backend, gcontext, errp); + chr = chardev_new(id, typename, backend, gcontext, false, errp); + if (!chr) { + return NULL; + } + + if (!object_property_try_add_child(get_chardevs_root(), id, OBJECT(chr), + errp)) { + object_unref(OBJECT(chr)); + return NULL; + } + object_unref(OBJECT(chr)); + + return chr; } ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, @@ -1044,11 +1065,18 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, } chr = chardev_new(id, object_class_get_name(OBJECT_CLASS(cc)), - backend, NULL, errp); + backend, NULL, false, errp); if (!chr) { return NULL; } + if (!object_property_try_add_child(get_chardevs_root(), id, OBJECT(chr), + errp)) { + object_unref(OBJECT(chr)); + return NULL; + } + object_unref(OBJECT(chr)); + ret = g_new0(ChardevReturn, 1); if (CHARDEV_IS_PTY(chr)) { ret->pty = g_strdup(chr->filename + 4); @@ -1062,9 +1090,10 @@ ChardevReturn *qmp_chardev_change(const char *id, ChardevBackend *backend, Error **errp) { CharBackend *be; - const ChardevClass *cc; + const ChardevClass *cc, *cc_new; Chardev *chr, *chr_new; bool closed_sent = false; + bool handover_yank_instance; ChardevReturn *ret; chr = qemu_chr_find(id); @@ -1096,17 +1125,23 @@ ChardevReturn *qmp_chardev_change(const char *id, ChardevBackend *backend, return NULL; } - cc = char_get_class(ChardevBackendKind_str(backend->type), errp); - if (!cc) { + cc = CHARDEV_GET_CLASS(chr); + cc_new = char_get_class(ChardevBackendKind_str(backend->type), errp); + if (!cc_new) { return NULL; } - chr_new = chardev_new(NULL, object_class_get_name(OBJECT_CLASS(cc)), - backend, chr->gcontext, errp); + /* + * The new chardev should not register a yank instance if the current + * chardev has registered one already. + */ + handover_yank_instance = cc->supports_yank && cc_new->supports_yank; + + chr_new = chardev_new(id, object_class_get_name(OBJECT_CLASS(cc_new)), + backend, chr->gcontext, handover_yank_instance, errp); if (!chr_new) { return NULL; } - chr_new->label = g_strdup(id); if (chr->be_open && !chr_new->be_open) { qemu_chr_be_event(chr, CHR_EVENT_CLOSED); @@ -1127,6 +1162,15 @@ ChardevReturn *qmp_chardev_change(const char *id, ChardevBackend *backend, return NULL; } + /* change successfull, clean up */ + chr_new->handover_yank_instance = false; + + /* + * When the old chardev is freed, it should not unregister the yank + * instance if the new chardev needs it. + */ + chr->handover_yank_instance = handover_yank_instance; + object_unparent(OBJECT(chr)); object_property_add_child(get_chardevs_root(), chr_new->label, OBJECT(chr_new)); diff --git a/chardev/chardev-internal.h b/chardev/chardev-internal.h index f4d0429763..aba0240759 100644 --- a/chardev/chardev-internal.h +++ b/chardev/chardev-internal.h @@ -32,7 +32,7 @@ #define MUX_BUFFER_SIZE 32 /* Must be a power of 2. */ #define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1) -typedef struct MuxChardev { +struct MuxChardev { Chardev parent; CharBackend *backends[MAX_MUX]; CharBackend chr; @@ -51,9 +51,11 @@ typedef struct MuxChardev { /* Protected by the Chardev chr_write_lock. */ int linestart; int64_t timestamps_start; -} MuxChardev; +}; +typedef struct MuxChardev MuxChardev; -#define MUX_CHARDEV(obj) OBJECT_CHECK(MuxChardev, (obj), TYPE_CHARDEV_MUX) +DECLARE_INSTANCE_CHECKER(MuxChardev, MUX_CHARDEV, + TYPE_CHARDEV_MUX) #define CHARDEV_IS_MUX(chr) \ object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_MUX) diff --git a/chardev/meson.build b/chardev/meson.build new file mode 100644 index 0000000000..32377af383 --- /dev/null +++ b/chardev/meson.build @@ -0,0 +1,44 @@ +chardev_ss.add(files( + 'char-fe.c', + 'char-file.c', + 'char-io.c', + 'char-mux.c', + 'char-null.c', + 'char-pipe.c', + 'char-ringbuf.c', + 'char-serial.c', + 'char-socket.c', + 'char-stdio.c', + 'char-udp.c', + 'char.c', +)) +chardev_ss.add(when: 'CONFIG_POSIX', if_true: files( + 'char-fd.c', + 'char-parallel.c', + 'char-pty.c', +)) +chardev_ss.add(when: 'CONFIG_WIN32', if_true: files( + 'char-console.c', + 'char-win-stdio.c', + 'char-win.c', +)) + +chardev_ss = chardev_ss.apply(config_host, strict: false) + +softmmu_ss.add(files('msmouse.c', 'wctablet.c', 'testdev.c')) + +chardev_modules = {} + +if brlapi.found() + module_ss = ss.source_set() + module_ss.add(when: [brlapi], if_true: [files('baum.c'), pixman]) + chardev_modules += { 'baum': module_ss } +endif + +if config_host.has_key('CONFIG_SPICE') + module_ss = ss.source_set() + module_ss.add(when: [spice], if_true: files('spice.c')) + chardev_modules += { 'spice': module_ss } +endif + +modules += { 'chardev': chardev_modules } diff --git a/chardev/msmouse.c b/chardev/msmouse.c index 6d8f06fed4..eb9231dcdb 100644 --- a/chardev/msmouse.c +++ b/chardev/msmouse.c @@ -27,11 +27,12 @@ #include "chardev/char.h" #include "ui/console.h" #include "ui/input.h" +#include "qom/object.h" #define MSMOUSE_LO6(n) ((n) & 0x3f) #define MSMOUSE_HI2(n) (((n) & 0xc0) >> 6) -typedef struct { +struct MouseChardev { Chardev parent; QemuInputHandlerState *hs; @@ -40,11 +41,12 @@ typedef struct { bool btnc[INPUT_BUTTON__MAX]; uint8_t outbuf[32]; int outlen; -} MouseChardev; +}; +typedef struct MouseChardev MouseChardev; #define TYPE_CHARDEV_MSMOUSE "chardev-msmouse" -#define MOUSE_CHARDEV(obj) \ - OBJECT_CHECK(MouseChardev, (obj), TYPE_CHARDEV_MSMOUSE) +DECLARE_INSTANCE_CHECKER(MouseChardev, MOUSE_CHARDEV, + TYPE_CHARDEV_MSMOUSE) static void msmouse_chr_accept_input(Chardev *chr) { diff --git a/chardev/spice.c b/chardev/spice.c index bf7ea1e294..1104426e3a 100644 --- a/chardev/spice.c +++ b/chardev/spice.c @@ -14,9 +14,6 @@ typedef struct SpiceCharSource { SpiceChardev *scd; } SpiceCharSource; -static QLIST_HEAD(, SpiceChardev) spice_chars = - QLIST_HEAD_INITIALIZER(spice_chars); - static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len) { SpiceChardev *scd = container_of(sin, SpiceChardev, sin); @@ -113,7 +110,7 @@ static void vmc_register_interface(SpiceChardev *scd) return; } scd->sin.base.sif = &vmc_interface.base; - qemu_spice_add_interface(&scd->sin.base); + qemu_spice.add_interface(&scd->sin.base); scd->active = true; trace_spice_vmc_register_interface(scd); } @@ -216,8 +213,6 @@ static void char_spice_finalize(Object *obj) vmc_unregister_interface(s); - QLIST_SAFE_REMOVE(s, next); - g_free((char *)s->sin.subtype); g_free((char *)s->sin.portname); } @@ -256,8 +251,6 @@ static void chr_open(Chardev *chr, const char *subtype) s->active = false; s->sin.subtype = g_strdup(subtype); - - QLIST_INSERT_HEAD(&spice_chars, s, next); } static void qemu_chr_open_spice_vmc(Chardev *chr, @@ -296,10 +289,10 @@ static void qemu_chr_open_spice_vmc(Chardev *chr, chr_open(chr, type); } -void qemu_chr_open_spice_port(Chardev *chr, - ChardevBackend *backend, - bool *be_opened, - Error **errp) +static void qemu_chr_open_spice_port(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { ChardevSpicePort *spiceport = backend->u.spiceport.data; const char *name = spiceport->fqdn; @@ -310,28 +303,18 @@ void qemu_chr_open_spice_port(Chardev *chr, return; } + if (!using_spice) { + error_setg(errp, "spice not enabled"); + return; + } + chr_open(chr, "port"); *be_opened = false; s = SPICE_CHARDEV(chr); s->sin.portname = g_strdup(name); - if (using_spice) { - /* spice server already created */ - vmc_register_interface(s); - } -} - -void qemu_spice_register_ports(void) -{ - SpiceChardev *s; - - QLIST_FOREACH(s, &spice_chars, next) { - if (s->sin.portname == NULL) { - continue; - } - vmc_register_interface(s); - } + vmc_register_interface(s); } static void qemu_chr_parse_spice_vmc(QemuOpts *opts, ChardevBackend *backend, diff --git a/chardev/testdev.c b/chardev/testdev.c index 368a8c041e..a92caca3c3 100644 --- a/chardev/testdev.c +++ b/chardev/testdev.c @@ -27,19 +27,21 @@ #include "qemu/osdep.h" #include "qemu/module.h" #include "chardev/char.h" +#include "qom/object.h" #define BUF_SIZE 32 -typedef struct { +struct TestdevChardev { Chardev parent; uint8_t in_buf[32]; int in_buf_used; -} TestdevChardev; +}; +typedef struct TestdevChardev TestdevChardev; #define TYPE_CHARDEV_TESTDEV "chardev-testdev" -#define TESTDEV_CHARDEV(obj) \ - OBJECT_CHECK(TestdevChardev, (obj), TYPE_CHARDEV_TESTDEV) +DECLARE_INSTANCE_CHECKER(TestdevChardev, TESTDEV_CHARDEV, + TYPE_CHARDEV_TESTDEV) /* Try to interpret a whole incoming packet */ static int testdev_eat_packet(TestdevChardev *testdev) diff --git a/chardev/trace.h b/chardev/trace.h new file mode 100644 index 0000000000..eb4f9027a1 --- /dev/null +++ b/chardev/trace.h @@ -0,0 +1 @@ +#include "trace/trace-chardev.h" diff --git a/chardev/wctablet.c b/chardev/wctablet.c index e9cb7ca710..95e005f5a5 100644 --- a/chardev/wctablet.c +++ b/chardev/wctablet.c @@ -32,6 +32,7 @@ #include "ui/console.h" #include "ui/input.h" #include "trace.h" +#include "qom/object.h" #define WC_OUTPUT_BUF_MAX_LEN 512 @@ -64,7 +65,7 @@ uint8_t WC_FULL_CONFIG_STRING[WC_FULL_CONFIG_STRING_LENGTH + 1] = { }; /* This structure is used to save private info for Wacom Tablet. */ -typedef struct { +struct TabletChardev { Chardev parent; QemuInputHandlerState *hs; @@ -81,11 +82,12 @@ typedef struct { int axis[INPUT_AXIS__MAX]; bool btns[INPUT_BUTTON__MAX]; -} TabletChardev; +}; +typedef struct TabletChardev TabletChardev; #define TYPE_CHARDEV_WCTABLET "chardev-wctablet" -#define WCTABLET_CHARDEV(obj) \ - OBJECT_CHECK(TabletChardev, (obj), TYPE_CHARDEV_WCTABLET) +DECLARE_INSTANCE_CHECKER(TabletChardev, WCTABLET_CHARDEV, + TYPE_CHARDEV_WCTABLET) static void wctablet_chr_accept_input(Chardev *chr); diff --git a/configure b/configure index 1e566c904a..10b5bba1b8 100755 --- a/configure +++ b/configure @@ -11,6 +11,55 @@ unset CLICOLOR_FORCE GREP_OPTIONS # Don't allow CCACHE, if present, to use cached results of compile tests! export CCACHE_RECACHE=yes +# make source path absolute +source_path=$(cd "$(dirname -- "$0")"; pwd) + +if test "$PWD" = "$source_path" +then + echo "Using './build' as the directory for build output" + + MARKER=build/auto-created-by-configure + + if test -e build + then + if test -f $MARKER + then + rm -rf build + else + echo "ERROR: ./build dir already exists and was not previously created by configure" + exit 1 + fi + fi + + mkdir build + touch $MARKER + + cat > GNUmakefile <<'EOF' +# This file is auto-generated by configure to support in-source tree +# 'make' command invocation + +ifeq ($(MAKECMDGOALS),) +recurse: all +endif + +.NOTPARALLEL: % +%: force + @echo 'changing dir to build for $(MAKE) "$(MAKECMDGOALS)"...' + @$(MAKE) -C build -f Makefile $(MAKECMDGOALS) + @if test "$(MAKECMDGOALS)" = "distclean" && \ + test -e build/auto-created-by-configure ; \ + then \ + rm -rf build GNUmakefile ; \ + fi +force: ; +.PHONY: force +GNUmakefile: ; + +EOF + cd build + exec $source_path/configure "$@" +fi + # Temporary directory used for files created while # configure runs. Since it is in the build directory # we can safely blow away any previous version of it @@ -29,7 +78,6 @@ TMPC="${TMPDIR1}/${TMPB}.c" TMPO="${TMPDIR1}/${TMPB}.o" TMPCXX="${TMPDIR1}/${TMPB}.cxx" TMPE="${TMPDIR1}/${TMPB}.exe" -TMPMO="${TMPDIR1}/${TMPB}.mo" TMPTXT="${TMPDIR1}/${TMPB}.txt" rm -f config.log @@ -41,6 +89,10 @@ printf " '%s'" "$0" "$@" >> config.log echo >> config.log echo "#" >> config.log +quote_sh() { + printf "%s" "$1" | sed "s,','\\\\'',g; s,.*,'&'," +} + print_error() { (echo echo "ERROR: $1" @@ -59,7 +111,7 @@ error_exit() { do_compiler() { # Run the compiler, capturing its output to the log. First argument # is compiler binary to execute. - local compiler="$1" + compiler="$1" shift if test -n "$BASH_VERSION"; then eval ' echo >>config.log " @@ -107,16 +159,12 @@ update_cxxflags() { # options which some versions of GCC's C++ compiler complain about # because they only make sense for C programs. QEMU_CXXFLAGS="$QEMU_CXXFLAGS -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS" - + CONFIGURE_CXXFLAGS=$(echo "$CONFIGURE_CFLAGS" | sed s/-std=gnu99/-std=gnu++11/) for arg in $QEMU_CFLAGS; do case $arg in -Wstrict-prototypes|-Wmissing-prototypes|-Wnested-externs|\ -Wold-style-declaration|-Wold-style-definition|-Wredundant-decls) ;; - -std=gnu99) - # QEMU_CXXFLAGS=${QEMU_CXXFLAGS:+$QEMU_CXXFLAGS }"-std=gnu++98" - QEMU_CXXFLAGS=${QEMU_CXXFLAGS:+$QEMU_CXXFLAGS }"-std=c++11" - ;; *) QEMU_CXXFLAGS=${QEMU_CXXFLAGS:+$QEMU_CXXFLAGS }$arg ;; @@ -126,13 +174,14 @@ update_cxxflags() { compile_object() { local_cflags="$1" - do_cc $QEMU_CFLAGS $local_cflags -c -o $TMPO $TMPC + do_cc $CFLAGS $CONFIGURE_CFLAGS $QEMU_CFLAGS $local_cflags -c -o $TMPO $TMPC } compile_prog() { local_cflags="$1" local_ldflags="$2" - do_cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $QEMU_LDFLAGS $local_ldflags + do_cc $CFLAGS $CONFIGURE_CFLAGS $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC \ + $LDFLAGS $CONFIGURE_LDFLAGS $QEMU_LDFLAGS $local_ldflags } # symbolically link $1 to $2. Portable version of "ln -sf". @@ -148,34 +197,23 @@ has() { type "$1" >/dev/null 2>&1 } -# search for an executable in PATH -path_of() { - local_command="$1" - local_ifs="$IFS" - local_dir="" - - # pathname has a dir component? - if [ "${local_command#*/}" != "$local_command" ]; then - if [ -x "$local_command" ] && [ ! -d "$local_command" ]; then - echo "$local_command" - return 0 - fi - fi - if [ -z "$local_command" ]; then - return 1 - fi - - IFS=: - for local_dir in $PATH; do - if [ -x "$local_dir/$local_command" ] && [ ! -d "$local_dir/$local_command" ]; then - echo "$local_dir/$local_command" - IFS="${local_ifs:-$(printf ' \t\n')}" - return 0 - fi +version_ge () { + local_ver1=$(expr "$1" : '\([0-9.]*\)' | tr . ' ') + local_ver2=$(echo "$2" | tr . ' ') + while true; do + set x $local_ver1 + local_first=${2-0} + # 'shift 2' if $2 is set, or 'shift' if $2 is not set + shift ${2:+2} + local_ver1=$* + set x $local_ver2 + # the second argument finished, the first must be greater or equal + test $# = 1 && return 0 + test $local_first -lt $2 && return 1 + test $local_first -gt $2 && return 0 + shift ${2:+2} + local_ver2=$* done - # not found - IFS="${local_ifs:-$(printf ' \t\n')}" - return 1 } have_backend () { @@ -186,106 +224,10 @@ glob() { eval test -z '"${1#'"$2"'}"' } -supported_hax_target() { - test "$hax" = "yes" || return 1 - glob "$1" "*-softmmu" || return 1 - case "${1%-softmmu}" in - i386|x86_64) - return 0 - ;; - esac - return 1 -} - -supported_kvm_target() { - test "$kvm" = "yes" || return 1 - glob "$1" "*-softmmu" || return 1 - case "${1%-softmmu}:$cpu" in - arm:arm | aarch64:aarch64 | \ - i386:i386 | i386:x86_64 | i386:x32 | \ - x86_64:i386 | x86_64:x86_64 | x86_64:x32 | \ - mips:mips | mipsel:mips | mips64:mips | mips64el:mips | \ - ppc:ppc | ppc64:ppc | ppc:ppc64 | ppc64:ppc64 | ppc64:ppc64le | \ - s390x:s390x) - return 0 - ;; - esac - return 1 -} - -supported_xen_target() { - test "$xen" = "yes" || return 1 - glob "$1" "*-softmmu" || return 1 - # Only i386 and x86_64 provide the xenpv machine. - case "${1%-softmmu}" in - i386|x86_64) - return 0 - ;; - esac - return 1 -} - -supported_hvf_target() { - test "$hvf" = "yes" || return 1 - glob "$1" "*-softmmu" || return 1 - case "${1%-softmmu}" in - i386|x86_64) - return 0 - ;; - esac - return 1 -} - -supported_whpx_target() { - test "$whpx" = "yes" || return 1 - glob "$1" "*-softmmu" || return 1 - case "${1%-softmmu}" in - i386|x86_64) - return 0 - ;; - esac - return 1 -} - -supported_target() { - case "$1" in - *-softmmu) - ;; - *-linux-user) - if test "$linux" != "yes"; then - print_error "Target '$target' is only available on a Linux host" - return 1 - fi - ;; - *-bsd-user) - if test "$bsd" != "yes"; then - print_error "Target '$target' is only available on a BSD host" - return 1 - fi - ;; - *) - print_error "Invalid target name '$target'" - return 1 - ;; - esac - test "$tcg" = "yes" && return 0 - supported_kvm_target "$1" && return 0 - supported_xen_target "$1" && return 0 - supported_hax_target "$1" && return 0 - supported_hvf_target "$1" && return 0 - supported_whpx_target "$1" && return 0 - print_error "TCG disabled, but hardware accelerator not available for '$target'" - return 1 -} - - ld_has() { $ld --help 2>/dev/null | grep ".$1" >/dev/null 2>&1 } -# make source path absolute -source_path=$(cd "$(dirname -- "$0")"; pwd) - if printf %s\\n "$source_path" "$PWD" | grep -q "[[:space:]:]"; then error_exit "main directory cannot contain spaces nor colons" @@ -296,17 +238,16 @@ cpu="" iasl="iasl" interp_prefix="/usr/gnemul/qemu-%M" static="no" +cross_compile="no" cross_prefix="" audio_drv_list="" block_drv_rw_whitelist="" block_drv_ro_whitelist="" host_cc="cc" -libs_cpu="" -libs_softmmu="" -libs_tools="" audio_win_int="" libs_qga="" debug_info="yes" +lto="false" stack_protector="" safe_stack="" use_containers="yes" @@ -314,7 +255,7 @@ gdb_bin=$(command -v "gdb-multiarch" || command -v "gdb") if test -e "$source_path/.git" then - git_update=yes + git_submodules_action="update" git_submodules="ui/keycodemapdb" git_submodules="$git_submodules tests/fp/berkeley-testfloat-3" git_submodules="$git_submodules tests/fp/berkeley-softfloat-3" @@ -322,7 +263,7 @@ then # xemu extras git_submodules="$git_submodules ui/imgui ui/implot hw/xbox/nv2a/xxHash" else - git_update=no + git_submodules_action="ignore" git_submodules="" if ! test -f "$source_path/ui/keycodemapdb/README" @@ -361,179 +302,180 @@ unset target_list_exclude # Distributions want to ensure that several features are compiled in, and it # is impossible without a --enable-foo that exits if a feature is not found. -brlapi="" -curl="" -curses="" -docs="" -fdt="" +default_feature="" +# parse CC options second +for opt do + optarg=$(expr "x$opt" : 'x[^=]*=\(.*\)') + case "$opt" in + --without-default-features) + default_feature="no" + ;; + esac +done + +brlapi="auto" +curl="auto" +iconv="auto" +curses="auto" +docs="auto" +fdt="auto" netmap="no" -sdl="" -sdl_image="" -virtfs="" -mpath="" -vnc="yes" -sparse="no" -vde="" -vnc_sasl="" -vnc_jpeg="" -vnc_png="" -xkbcommon="" -xen="" -xen_ctrl_version="" -xen_pci_passthrough="" -linux_aio="" -linux_io_uring="" -cap_ng="" -attr="" -libattr="" -xfs="" -tcg="yes" -membarrier="" -vhost_net="" -vhost_crypto="" -vhost_scsi="" -vhost_vsock="" -vhost_user="" -vhost_user_fs="" -kvm="no" -hax="no" -hvf="no" -whpx="no" -rdma="" -pvrdma="" +sdl="auto" +sdl_image="auto" +coreaudio="auto" +virtiofsd="auto" +virtfs="auto" +libudev="auto" +mpath="auto" +vnc="enabled" +sparse="auto" +vde="$default_feature" +vnc_sasl="auto" +vnc_jpeg="auto" +vnc_png="auto" +xkbcommon="auto" +xen="$default_feature" +xen_ctrl_version="$default_feature" +xen_pci_passthrough="auto" +linux_aio="$default_feature" +linux_io_uring="$default_feature" +cap_ng="auto" +attr="auto" +xfs="$default_feature" +tcg="enabled" +membarrier="$default_feature" +vhost_net="$default_feature" +vhost_crypto="$default_feature" +vhost_scsi="$default_feature" +vhost_vsock="$default_feature" +vhost_user="no" +vhost_user_blk_server="auto" +vhost_user_fs="$default_feature" +kvm="auto" +hax="auto" +hvf="auto" +whpx="auto" +rdma="$default_feature" +pvrdma="$default_feature" gprof="no" debug_tcg="no" debug="no" sanitizers="no" tsan="no" -fortify_source="" +fortify_source="$default_feature" strip_opt="yes" -tcg_interpreter="no" +tcg_interpreter="false" bigendian="no" mingw32="no" gcov="no" -gcov_tool="gcov" EXESUF="" -DSOSUF=".so" -LDFLAGS_SHARED="-shared" +HOST_DSOSUF=".so" modules="no" module_upgrades="no" prefix="/usr/local" -mandir="\${prefix}/share/man" -datadir="\${prefix}/share" -firmwarepath="\${prefix}/share/qemu-firmware" -efi_aarch64="" -qemu_docdir="\${prefix}/share/doc/qemu" -bindir="\${prefix}/bin" -libdir="\${prefix}/lib" -libexecdir="\${prefix}/libexec" -includedir="\${prefix}/include" -sysconfdir="\${prefix}/etc" -local_statedir="\${prefix}/var" -confsuffix="/qemu" -slirp="" +qemu_suffix="qemu" +slirp="auto" oss_lib="" bsd="no" linux="no" solaris="no" profiler="no" -cocoa="no" +cocoa="auto" softmmu="yes" linux_user="no" bsd_user="no" -blobs="yes" -edk2_blobs="no" +blobs="true" pkgversion="" pie="" qom_cast_debug="yes" trace_backends="log" trace_file="trace" -spice="" -rbd="" -smartcard="" -libusb="" -usb_redir="" -opengl="" -opengl_dmabuf="no" +spice="$default_feature" +rbd="auto" +smartcard="$default_feature" +u2f="auto" +libusb="$default_feature" +usb_redir="$default_feature" +opengl="$default_feature" cpuid_h="no" -avx2_opt="" -zlib="yes" -capstone="" -lzo="" -snappy="" -bzip2="" -lzfse="" -zstd="" -guest_agent="" +avx2_opt="$default_feature" +capstone="auto" +lzo="auto" +snappy="auto" +bzip2="auto" +lzfse="auto" +zstd="auto" +guest_agent="$default_feature" guest_agent_with_vss="no" guest_agent_ntddscsi="no" -guest_agent_msi="" -vss_win32_sdk="" +guest_agent_msi="auto" +vss_win32_sdk="$default_feature" win_sdk="no" -want_tools="" -libiscsi="" -libnfs="" +want_tools="$default_feature" +libiscsi="auto" +libnfs="auto" coroutine="" -coroutine_pool="" +coroutine_pool="$default_feature" debug_stack_usage="no" crypto_afalg="no" -seccomp="" -glusterfs="" -glusterfs_xlator_opt="no" -glusterfs_discard="no" -glusterfs_fallocate="no" -glusterfs_zerofill="no" -glusterfs_ftruncate_has_stat="no" -glusterfs_iocb_has_stat="no" -gtk="" -gtk_gl="no" +cfi="false" +cfi_debug="false" +seccomp="auto" +glusterfs="auto" +gtk="auto" tls_priority="NORMAL" -gnutls="" -nettle="" +gnutls="$default_feature" +nettle="$default_feature" nettle_xts="no" -gcrypt="" +gcrypt="$default_feature" gcrypt_hmac="no" gcrypt_xts="no" qemu_private_xts="yes" -auth_pam="" -vte="" -virglrenderer="" -tpm="" -libssh="" -live_block_migration="yes" -numa="" +auth_pam="$default_feature" +vte="$default_feature" +virglrenderer="$default_feature" +tpm="$default_feature" +libssh="$default_feature" +live_block_migration=${default_feature:-yes} +numa="$default_feature" tcmalloc="no" jemalloc="no" -replication="yes" -bochs="yes" -cloop="yes" -dmg="yes" -qcow1="yes" -vdi="yes" -vvfat="yes" -qed="yes" -parallels="yes" -sheepdog="yes" -libxml2="" +replication=${default_feature:-yes} +bochs=${default_feature:-yes} +cloop=${default_feature:-yes} +dmg=${default_feature:-yes} +qcow1=${default_feature:-yes} +vdi=${default_feature:-yes} +vvfat=${default_feature:-yes} +qed=${default_feature:-yes} +parallels=${default_feature:-yes} +sheepdog="no" +libxml2="$default_feature" debug_mutex="no" -libpmem="" -default_devices="yes" +libpmem="$default_feature" +default_devices="true" plugins="no" fuzzing="no" rng_none="no" -secret_keyring="" -libdaxctl="" +secret_keyring="$default_feature" +libdaxctl="$default_feature" +meson="" +ninja="" +skip_meson=no +gettext="auto" +fuse="auto" +fuse_lseek="auto" +multiprocess="auto" -supported_cpu="no" -supported_os="no" -bogus_os="no" -malloc_trim="" +malloc_trim="auto" +gio="$default_feature" -# parse CC options first +# parse CC options second for opt do optarg=$(expr "x$opt" : 'x[^=]*=\(.*\)') case "$opt" in --cross-prefix=*) cross_prefix="$optarg" + cross_compile="yes" ;; --cc=*) CC="$optarg" ;; @@ -611,23 +553,15 @@ ARFLAGS="${ARFLAGS-rv}" # left shift of signed integers is well defined and has the expected # 2s-complement style results. (Both clang and gcc agree that it # provides these semantics.) -QEMU_CFLAGS="-fno-strict-aliasing -fno-common -fwrapv -std=gnu99 $QEMU_CFLAGS" -QEMU_CFLAGS="-Wall -Wundef -Wwrite-strings -Wmissing-prototypes $QEMU_CFLAGS" +QEMU_CFLAGS="-fno-strict-aliasing -fno-common -fwrapv $QEMU_CFLAGS" +QEMU_CFLAGS="-Wundef -Wwrite-strings -Wmissing-prototypes $QEMU_CFLAGS" QEMU_CFLAGS="-Wstrict-prototypes -Wredundant-decls $QEMU_CFLAGS" QEMU_CFLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE $QEMU_CFLAGS" -QEMU_INCLUDES="-iquote . -iquote \$(SRC_PATH) -iquote \$(SRC_PATH)/accel/tcg -iquote \$(SRC_PATH)/include" -QEMU_INCLUDES="$QEMU_INCLUDES -iquote \$(SRC_PATH)/disas/libvixl" -if test "$debug_info" = "yes"; then - CFLAGS="-g $CFLAGS" -fi -# running configure in the source tree? -# we know that's the case if configure is there. -if test -f "./configure"; then - pwd_is_source_path="y" -else - pwd_is_source_path="n" -fi +# Flags that are needed during configure but later taken care of by Meson +CONFIGURE_CFLAGS="-std=gnu99 -Wall" +CONFIGURE_LDFLAGS= + check_define() { cat > $TMPC < /dev/null); then - python_yaml="yes" -fi - : ${smbd=${SMBD-/usr/sbin/smbd}} # Default objcc to clang if available, otherwise use CC @@ -982,22 +849,12 @@ fi if test "$mingw32" = "yes" ; then EXESUF=".exe" - DSOSUF=".dll" + HOST_DSOSUF=".dll" # MinGW needs -mthreads for TLS and macro _MT. - QEMU_CFLAGS="-mthreads $QEMU_CFLAGS" - LIBS="-lwinmm -lws2_32 -lopengl32 -lgdi32 $LIBS" + CONFIGURE_CFLAGS="-mthreads $CONFIGURE_CFLAGS" write_c_skeleton; - if compile_prog "" "-liberty" ; then - LIBS="-liberty $LIBS" - fi - prefix="c:/Program Files/QEMU" - mandir="\${prefix}" - datadir="\${prefix}" - qemu_docdir="\${prefix}" - bindir="\${prefix}" - sysconfdir="\${prefix}" - local_statedir= - confsuffix="" + prefix="/qemu" + qemu_suffix="" libs_qga="-lws2_32 -lwinmm -lpowrprof -lwtsapi32 -lwininet -liphlpapi -lnetapi32 $libs_qga" fi @@ -1028,13 +885,17 @@ for opt do ;; --make=*) make="$optarg" ;; - --install=*) install="$optarg" + --install=*) ;; - --python=*) python="$optarg" + --python=*) python="$optarg" ; explicit_python=yes ;; --sphinx-build=*) sphinx_build="$optarg" ;; - --gcov=*) gcov_tool="$optarg" + --skip-meson) skip_meson=yes + ;; + --meson=*) meson="$optarg" + ;; + --ninja=*) ninja="$optarg" ;; --smbd=*) smbd="$optarg" ;; @@ -1079,9 +940,11 @@ for opt do ;; --with-trace-file=*) trace_file="$optarg" ;; - --with-default-devices) default_devices="yes" + --with-default-devices) default_devices="true" ;; - --without-default-devices) default_devices="no" + --without-default-devices) default_devices="false" + ;; + --without-default-features) # processed above ;; --enable-gprof) gprof="yes" ;; @@ -1103,9 +966,11 @@ for opt do ;; --datadir=*) datadir="$optarg" ;; - --with-confsuffix=*) confsuffix="$optarg" + --with-suffix=*) qemu_suffix="$optarg" ;; - --docdir=*) qemu_docdir="$optarg" + --docdir=*) docdir="$optarg" + ;; + --localedir=*) localedir="$optarg" ;; --sysconfdir=*) sysconfdir="$optarg" ;; @@ -1113,41 +978,51 @@ for opt do ;; --firmwarepath=*) firmwarepath="$optarg" ;; - --efi-aarch64=*) efi_aarch64="$optarg" - ;; --host=*|--build=*|\ --disable-dependency-tracking|\ --sbindir=*|--sharedstatedir=*|\ - --oldincludedir=*|--datarootdir=*|--infodir=*|--localedir=*|\ + --oldincludedir=*|--datarootdir=*|--infodir=*|\ --htmldir=*|--dvidir=*|--pdfdir=*|--psdir=*) # These switches are silently ignored, for compatibility with # autoconf-generated configure scripts. This allows QEMU's # configure to be used by RPM and similar macros that set # lots of directory switches by default. ;; - --disable-sdl) sdl="no" + --disable-sdl) sdl="disabled" ;; - --enable-sdl) sdl="yes" + --enable-sdl) sdl="enabled" ;; - --disable-sdl-image) sdl_image="no" + --disable-sdl-image) sdl_image="disabled" ;; - --enable-sdl-image) sdl_image="yes" + --enable-sdl-image) sdl_image="enabled" ;; --disable-qom-cast-debug) qom_cast_debug="no" ;; --enable-qom-cast-debug) qom_cast_debug="yes" ;; - --disable-virtfs) virtfs="no" + --disable-virtfs) virtfs="disabled" ;; - --enable-virtfs) virtfs="yes" + --enable-virtfs) virtfs="enabled" ;; - --disable-mpath) mpath="no" + --disable-libudev) libudev="disabled" ;; - --enable-mpath) mpath="yes" + --enable-libudev) libudev="enabled" ;; - --disable-vnc) vnc="no" + --disable-virtiofsd) virtiofsd="disabled" ;; - --enable-vnc) vnc="yes" + --enable-virtiofsd) virtiofsd="enabled" + ;; + --disable-mpath) mpath="disabled" + ;; + --enable-mpath) mpath="enabled" + ;; + --disable-vnc) vnc="disabled" + ;; + --enable-vnc) vnc="enabled" + ;; + --disable-gettext) gettext="disabled" + ;; + --enable-gettext) gettext="enabled" ;; --oss-lib=*) oss_lib="$optarg" ;; @@ -1177,9 +1052,9 @@ for opt do ;; --disable-tsan) tsan="no" ;; - --enable-sparse) sparse="yes" + --enable-sparse) sparse="enabled" ;; - --disable-sparse) sparse="no" + --disable-sparse) sparse="disabled" ;; --disable-fortify-source) fortify_source="no" ;; @@ -1187,21 +1062,23 @@ for opt do ;; --disable-strip) strip_opt="no" ;; - --disable-vnc-sasl) vnc_sasl="no" + --disable-vnc-sasl) vnc_sasl="disabled" ;; - --enable-vnc-sasl) vnc_sasl="yes" + --enable-vnc-sasl) vnc_sasl="enabled" ;; - --disable-vnc-jpeg) vnc_jpeg="no" + --disable-vnc-jpeg) vnc_jpeg="disabled" ;; - --enable-vnc-jpeg) vnc_jpeg="yes" + --enable-vnc-jpeg) vnc_jpeg="enabled" ;; - --disable-vnc-png) vnc_png="no" + --disable-vnc-png) vnc_png="disabled" ;; - --enable-vnc-png) vnc_png="yes" + --enable-vnc-png) vnc_png="enabled" ;; - --disable-slirp) slirp="no" + --disable-slirp) slirp="disabled" ;; - --enable-slirp=git) slirp="git" + --enable-slirp) slirp="enabled" + ;; + --enable-slirp=git) slirp="internal" ;; --enable-slirp=system) slirp="system" ;; @@ -1213,69 +1090,67 @@ for opt do ;; --enable-netmap) netmap="yes" ;; - --disable-xen) xen="no" + --disable-xen) xen="disabled" ;; - --enable-xen) xen="yes" + --enable-xen) xen="enabled" ;; - --disable-xen-pci-passthrough) xen_pci_passthrough="no" + --disable-xen-pci-passthrough) xen_pci_passthrough="disabled" ;; - --enable-xen-pci-passthrough) xen_pci_passthrough="yes" + --enable-xen-pci-passthrough) xen_pci_passthrough="enabled" ;; - --disable-brlapi) brlapi="no" + --disable-brlapi) brlapi="disabled" ;; - --enable-brlapi) brlapi="yes" + --enable-brlapi) brlapi="enabled" ;; - --disable-kvm) kvm="no" + --disable-kvm) kvm="disabled" ;; - --enable-kvm) kvm="yes" + --enable-kvm) kvm="enabled" ;; - --disable-hax) hax="no" + --disable-hax) hax="disabled" ;; - --enable-hax) hax="yes" + --enable-hax) hax="enabled" ;; - --disable-hvf) hvf="no" + --disable-hvf) hvf="disabled" ;; - --enable-hvf) hvf="yes" + --enable-hvf) hvf="enabled" ;; - --disable-whpx) whpx="no" + --disable-whpx) whpx="disabled" ;; - --enable-whpx) whpx="yes" + --enable-whpx) whpx="enabled" ;; - --disable-tcg-interpreter) tcg_interpreter="no" + --disable-tcg-interpreter) tcg_interpreter="false" ;; - --enable-tcg-interpreter) tcg_interpreter="yes" + --enable-tcg-interpreter) tcg_interpreter="true" ;; - --disable-cap-ng) cap_ng="no" + --disable-cap-ng) cap_ng="disabled" ;; - --enable-cap-ng) cap_ng="yes" + --enable-cap-ng) cap_ng="enabled" ;; - --disable-tcg) tcg="no" + --disable-tcg) tcg="disabled" ;; - --enable-tcg) tcg="yes" + --enable-tcg) tcg="enabled" ;; - --disable-malloc-trim) malloc_trim="no" + --disable-malloc-trim) malloc_trim="disabled" ;; - --enable-malloc-trim) malloc_trim="yes" + --enable-malloc-trim) malloc_trim="enabled" ;; --disable-spice) spice="no" ;; --enable-spice) spice="yes" ;; - --disable-libiscsi) libiscsi="no" + --disable-libiscsi) libiscsi="disabled" ;; - --enable-libiscsi) libiscsi="yes" + --enable-libiscsi) libiscsi="enabled" ;; - --disable-libnfs) libnfs="no" + --disable-libnfs) libnfs="disabled" ;; - --enable-libnfs) libnfs="yes" + --enable-libnfs) libnfs="enabled" ;; --enable-profiler) profiler="yes" ;; - --disable-cocoa) cocoa="no" + --disable-cocoa) cocoa="disabled" ;; - --enable-cocoa) - cocoa="yes" ; - audio_drv_list="coreaudio $(echo $audio_drv_list | sed s,coreaudio,,g)" + --enable-cocoa) cocoa="enabled" ;; --disable-system) softmmu="no" ;; @@ -1302,6 +1177,10 @@ for opt do ;; --disable-werror) werror="no" ;; + --enable-lto) lto="true" + ;; + --disable-lto) lto="false" + ;; --enable-stack-protector) stack_protector="yes" ;; --disable-stack-protector) stack_protector="no" @@ -1310,21 +1189,35 @@ for opt do ;; --disable-safe-stack) safe_stack="no" ;; - --disable-curses) curses="no" + --enable-cfi) + cfi="true"; + lto="true"; ;; - --enable-curses) curses="yes" + --disable-cfi) cfi="false" ;; - --disable-iconv) iconv="no" + --enable-cfi-debug) cfi_debug="true" ;; - --enable-iconv) iconv="yes" + --disable-cfi-debug) cfi_debug="false" ;; - --disable-curl) curl="no" + --disable-curses) curses="disabled" ;; - --enable-curl) curl="yes" + --enable-curses) curses="enabled" ;; - --disable-fdt) fdt="no" + --disable-iconv) iconv="disabled" ;; - --enable-fdt) fdt="yes" + --enable-iconv) iconv="enabled" + ;; + --disable-curl) curl="disabled" + ;; + --enable-curl) curl="enabled" + ;; + --disable-fdt) fdt="disabled" + ;; + --enable-fdt) fdt="enabled" + ;; + --enable-fdt=git) fdt="internal" + ;; + --enable-fdt=system) fdt="system" ;; --disable-linux-aio) linux_aio="no" ;; @@ -1334,15 +1227,15 @@ for opt do ;; --enable-linux-io-uring) linux_io_uring="yes" ;; - --disable-attr) attr="no" + --disable-attr) attr="disabled" ;; - --enable-attr) attr="yes" + --enable-attr) attr="enabled" ;; --disable-membarrier) membarrier="no" ;; --enable-membarrier) membarrier="yes" ;; - --disable-blobs) blobs="no" + --disable-blobs) blobs="false" ;; --with-pkgversion=*) pkgversion="$optarg" ;; @@ -1358,9 +1251,9 @@ for opt do ;; --disable-crypto-afalg) crypto_afalg="no" ;; - --disable-docs) docs="no" + --disable-docs) docs="disabled" ;; - --enable-docs) docs="yes" + --enable-docs) docs="enabled" ;; --disable-vhost-net) vhost_net="no" ;; @@ -1378,6 +1271,10 @@ for opt do ;; --enable-vhost-vsock) vhost_vsock="yes" ;; + --disable-vhost-user-blk-server) vhost_user_blk_server="disabled" + ;; + --enable-vhost-user-blk-server) vhost_user_blk_server="enabled" + ;; --disable-vhost-user-fs) vhost_user_fs="no" ;; --enable-vhost-user-fs) vhost_user_fs="yes" @@ -1386,9 +1283,9 @@ for opt do ;; --enable-opengl) opengl="yes" ;; - --disable-rbd) rbd="no" + --disable-rbd) rbd="disabled" ;; - --enable-rbd) rbd="yes" + --enable-rbd) rbd="enabled" ;; --disable-xfsctl) xfs="no" ;; @@ -1398,6 +1295,10 @@ for opt do ;; --enable-smartcard) smartcard="yes" ;; + --disable-u2f) u2f="disabled" + ;; + --enable-u2f) u2f="enabled" + ;; --disable-libusb) libusb="no" ;; --enable-libusb) libusb="yes" @@ -1406,35 +1307,35 @@ for opt do ;; --enable-usb-redir) usb_redir="yes" ;; - --disable-zlib-test) zlib="no" + --disable-zlib-test) ;; - --disable-lzo) lzo="no" + --disable-lzo) lzo="disabled" ;; - --enable-lzo) lzo="yes" + --enable-lzo) lzo="enabled" ;; - --disable-snappy) snappy="no" + --disable-snappy) snappy="disabled" ;; - --enable-snappy) snappy="yes" + --enable-snappy) snappy="enabled" ;; - --disable-bzip2) bzip2="no" + --disable-bzip2) bzip2="disabled" ;; - --enable-bzip2) bzip2="yes" + --enable-bzip2) bzip2="enabled" ;; - --enable-lzfse) lzfse="yes" + --enable-lzfse) lzfse="enabled" ;; - --disable-lzfse) lzfse="no" + --disable-lzfse) lzfse="disabled" ;; - --disable-zstd) zstd="no" + --disable-zstd) zstd="disabled" ;; - --enable-zstd) zstd="yes" + --enable-zstd) zstd="enabled" ;; --enable-guest-agent) guest_agent="yes" ;; --disable-guest-agent) guest_agent="no" ;; - --enable-guest-agent-msi) guest_agent_msi="yes" + --enable-guest-agent-msi) guest_agent_msi="enabled" ;; - --disable-guest-agent-msi) guest_agent_msi="no" + --disable-guest-agent-msi) guest_agent_msi="disabled" ;; --with-vss-sdk) vss_win32_sdk="" ;; @@ -1452,11 +1353,11 @@ for opt do ;; --disable-tools) want_tools="no" ;; - --enable-seccomp) seccomp="yes" + --enable-seccomp) seccomp="enabled" ;; - --disable-seccomp) seccomp="no" + --disable-seccomp) seccomp="disabled" ;; - --disable-glusterfs) glusterfs="no" + --disable-glusterfs) glusterfs="disabled" ;; --disable-avx2) avx2_opt="no" ;; @@ -1467,7 +1368,7 @@ for opt do --enable-avx512f) avx512f_opt="yes" ;; - --enable-glusterfs) glusterfs="yes" + --enable-glusterfs) glusterfs="enabled" ;; --disable-virtio-blk-data-plane|--enable-virtio-blk-data-plane) echo "$0: $opt is obsolete, virtio-blk data-plane is always on" >&2 @@ -1478,9 +1379,9 @@ for opt do --enable-uuid|--disable-uuid) echo "$0: $opt is obsolete, UUID support is always built" >&2 ;; - --disable-gtk) gtk="no" + --disable-gtk) gtk="disabled" ;; - --enable-gtk) gtk="yes" + --enable-gtk) gtk="enabled" ;; --tls-priority=*) tls_priority="$optarg" ;; @@ -1596,19 +1497,26 @@ for opt do ;; --enable-vhost-kernel) vhost_kernel="yes" ;; - --disable-capstone) capstone="no" + --disable-capstone) capstone="disabled" ;; - --enable-capstone) capstone="yes" + --enable-capstone) capstone="enabled" ;; - --enable-capstone=git) capstone="git" + --enable-capstone=git) capstone="internal" ;; --enable-capstone=system) capstone="system" ;; --with-git=*) git="$optarg" ;; - --enable-git-update) git_update=yes + --enable-git-update) + git_submodules_action="update" + echo "--enable-git-update deprecated, use --with-git-submodules=update" ;; - --disable-git-update) git_update=no + --disable-git-update) + git_submodules_action="validate" + echo "--disable-git-update deprecated, use --with-git-submodules=validate" + ;; + --with-git-submodules=*) + git_submodules_action="$optarg" ;; --enable-debug-mutex) debug_mutex=yes ;; @@ -1618,9 +1526,9 @@ for opt do ;; --disable-libpmem) libpmem=no ;; - --enable-xkbcommon) xkbcommon=yes + --enable-xkbcommon) xkbcommon="enabled" ;; - --disable-xkbcommon) xkbcommon=no + --disable-xkbcommon) xkbcommon="disabled" ;; --enable-plugins) plugins="yes" ;; @@ -1648,6 +1556,22 @@ for opt do ;; --disable-libdaxctl) libdaxctl=no ;; + --enable-fuse) fuse="enabled" + ;; + --disable-fuse) fuse="disabled" + ;; + --enable-fuse-lseek) fuse_lseek="enabled" + ;; + --disable-fuse-lseek) fuse_lseek="disabled" + ;; + --enable-multiprocess) multiprocess="enabled" + ;; + --disable-multiprocess) multiprocess="disabled" + ;; + --enable-gio) gio=yes + ;; + --disable-gio) gio=no + ;; *) echo "ERROR: unknown option $opt" echo "Try '$0 --help' for more information" @@ -1656,6 +1580,38 @@ for opt do esac done +case $git_submodules_action in + update|validate) + if test ! -e "$source_path/.git"; then + echo "ERROR: cannot $git_submodules_action git submodules without .git" + exit 1 + fi + ;; + ignore) + ;; + *) + echo "ERROR: invalid --with-git-submodules= value '$git_submodules_action'" + exit 1 + ;; +esac + +libdir="${libdir:-$prefix/lib}" +libexecdir="${libexecdir:-$prefix/libexec}" +includedir="${includedir:-$prefix/include}" + +if test "$mingw32" = "yes" ; then + bindir="${bindir:-$prefix}" +else + bindir="${bindir:-$prefix/bin}" +fi +mandir="${mandir:-$prefix/share/man}" +datadir="${datadir:-$prefix/share}" +docdir="${docdir:-$prefix/share/doc}" +sysconfdir="${sysconfdir:-$prefix/etc}" +local_statedir="${local_statedir:-$prefix/var}" +firmwarepath="${firmwarepath:-$datadir/qemu-firmware}" +localedir="${localedir:-$datadir/locale}" + case "$cpu" in ppc) CPU_CFLAGS="-m32" @@ -1710,45 +1666,38 @@ if [ "$ARCH" = "unknown" ]; then linux_user="no" fi -if [ "$bsd_user" = "no" -a "$linux_user" = "no" -a "$softmmu" = "no" ] ; then - tcg="no" -fi - default_target_list="" - +deprecated_targets_list=ppc64abi32-linux-user,lm32-softmmu,unicore32-softmmu +deprecated_features="" mak_wilds="" if [ "$softmmu" = "yes" ]; then - mak_wilds="${mak_wilds} $source_path/default-configs/*-softmmu.mak" + mak_wilds="${mak_wilds} $source_path/default-configs/targets/*-softmmu.mak" fi if [ "$linux_user" = "yes" ]; then - mak_wilds="${mak_wilds} $source_path/default-configs/*-linux-user.mak" + mak_wilds="${mak_wilds} $source_path/default-configs/targets/*-linux-user.mak" fi if [ "$bsd_user" = "yes" ]; then - mak_wilds="${mak_wilds} $source_path/default-configs/*-bsd-user.mak" + mak_wilds="${mak_wilds} $source_path/default-configs/targets/*-bsd-user.mak" fi -if test -z "$target_list_exclude"; then - for config in $mak_wilds; do - default_target_list="${default_target_list} $(basename "$config" .mak)" - done -else - exclude_list=$(echo "$target_list_exclude" | sed -e 's/,/ /g') - for config in $mak_wilds; do - target="$(basename "$config" .mak)" - exclude="no" - for excl in $exclude_list; do - if test "$excl" = "$target"; then - exclude="yes" - break; - fi - done - if test "$exclude" = "no"; then - default_target_list="${default_target_list} $target" - fi - done +# If the user doesn't explicitly specify a deprecated target we will +# skip it. +if test -z "$target_list"; then + if test -z "$target_list_exclude"; then + target_list_exclude="$deprecated_targets_list" + else + target_list_exclude="$target_list_exclude,$deprecated_targets_list" + fi fi +for config in $mak_wilds; do + target="$(basename "$config" .mak)" + if echo "$target_list_exclude" | grep -vq "$target"; then + default_target_list="${default_target_list} $target" + fi +done + # Enumerate public trace backends for --help output trace_backend_list=$(echo $(grep -le '^PUBLIC = True$' "$source_path"/scripts/tracetool/backend/*.py | sed -e 's/^.*\/\(.*\)\.py$/\1/')) @@ -1763,13 +1712,15 @@ Standard options: --prefix=PREFIX install in PREFIX [$prefix] --interp-prefix=PREFIX where to find shared libraries, etc. use %M for cpu name [$interp_prefix] - --target-list=LIST set target list (default: build everything) + --target-list=LIST set target list (default: build all non-deprecated) $(echo Available targets: $default_target_list | \ fold -s -w 53 | sed -e 's/^/ /') +$(echo Deprecated targets: $deprecated_targets_list | \ + fold -s -w 53 | sed -e 's/^/ /') --target-list-exclude=LIST exclude a set of targets from the default target-list Advanced options (experts only): - --cross-prefix=PREFIX use PREFIX for compile tools [$cross_prefix] + --cross-prefix=PREFIX use PREFIX for compile tools, PREFIX can be blank [$cross_prefix] --cc=CC use C compiler CC [$cc] --iasl=IASL use ACPI compiler IASL [$iasl] --host-cc=CC use C compiler CC [$host_cc] for code run at @@ -1782,24 +1733,33 @@ Advanced options (experts only): --cross-cc-ARCH=CC use compiler when building ARCH guest test cases --cross-cc-flags-ARCH= use compiler flags when building ARCH guest tests --make=MAKE use specified make [$make] - --install=INSTALL use specified install [$install] --python=PYTHON use specified python [$python] --sphinx-build=SPHINX use specified sphinx-build [$sphinx_build] + --meson=MESON use specified meson [$meson] + --ninja=NINJA use specified ninja [$ninja] --smbd=SMBD use specified smbd [$smbd] --with-git=GIT use specified git [$git] + --with-git-submodules=update update git submodules (default if .git dir exists) + --with-git-submodules=validate fail if git submodules are not up to date + --with-git-submodules=ignore do not update or check git submodules (default if no .git dir) --static enable static build [$static] --mandir=PATH install man pages in PATH - --datadir=PATH install firmware in PATH$confsuffix - --docdir=PATH install documentation in PATH$confsuffix + --datadir=PATH install firmware in PATH/$qemu_suffix + --localedir=PATH install translation in PATH/$qemu_suffix + --docdir=PATH install documentation in PATH/$qemu_suffix --bindir=PATH install binaries in PATH --libdir=PATH install libraries in PATH --libexecdir=PATH install helper binaries in PATH - --sysconfdir=PATH install config in PATH$confsuffix + --sysconfdir=PATH install config in PATH/$qemu_suffix --localstatedir=PATH install local state in PATH (set at runtime on win32) --firmwarepath=PATH search PATH for firmware files --efi-aarch64=PATH PATH of efi file to use for aarch64 VMs. - --with-confsuffix=SUFFIX suffix for QEMU data inside datadir/libdir/sysconfdir [$confsuffix] + --with-suffix=SUFFIX suffix for QEMU data inside datadir/libdir/sysconfdir/docdir [$qemu_suffix] --with-pkgversion=VERS use specified string as sub-version of the package + --without-default-features default all --enable-* options to "disabled" + --without-default-devices do not include any device that is not needed to + start the emulator (only use if you are including + desired devices in default-configs/devices/) --enable-debug enable common debug build options --enable-sanitizers enable default sanitizers --enable-tsan enable thread sanitizer @@ -1820,14 +1780,13 @@ Advanced options (experts only): --with-trace-file=NAME Full PATH,NAME of file to store traces Default:trace- --disable-slirp disable SLIRP userspace network connectivity - --enable-tcg-interpreter enable TCG with bytecode interpreter (TCI) + --enable-tcg-interpreter enable TCI (TCG with bytecode interpreter, experimental and slow) --enable-malloc-trim enable libc malloc_trim() for memory optimization --oss-lib path to OSS library --cpu=CPU Build for host CPU [$cpu] --with-coroutine=BACKEND coroutine backend. Supported options: ucontext, sigaltstack, windows --enable-gcov enable test coverage analysis with gcov - --gcov=GCOV use specified gcov [$gcov_tool] --disable-blobs disable installing provided firmware blobs --with-vss-sdk=SDK-path enable Windows VSS support in QEMU Guest Agent --with-win-sdk=SDK-path path to Windows Platform SDK (to build VSS .tlb) @@ -1842,7 +1801,8 @@ Advanced options (experts only): --gdb=GDB-path gdb to use for gdbstub tests [$gdb_bin] Optional features, enabled with --enable-FEATURE and -disabled with --disable-FEATURE, default is enabled if available: +disabled with --disable-FEATURE, default is enabled if available +(unless built with --without-default-features): system all system emulation targets user supported user emulation targets @@ -1856,10 +1816,17 @@ disabled with --disable-FEATURE, default is enabled if available: module-upgrades try to load modules from alternate paths for upgrades debug-tcg TCG debugging (default is disabled) debug-info debugging information + lto Enable Link-Time Optimization. sparse sparse checker safe-stack SafeStack Stack Smash Protection. Depends on clang/llvm >= 3.7 and requires coroutine backend ucontext. - + cfi Enable Control-Flow Integrity for indirect function calls. + In case of a cfi violation, QEMU is terminated with SIGILL + Depends on lto and is incompatible with modules + Automatically enables Link-Time Optimization (lto) + cfi-debug In case of a cfi violation, a message containing the line that + triggered the error is written to stderr. After the error, + QEMU is still terminated with SIGILL gnutls GNUTLS cryptography support nettle nettle cryptography support gcrypt libgcrypt cryptography support @@ -1876,6 +1843,8 @@ disabled with --disable-FEATURE, default is enabled if available: vnc-png PNG compression for VNC server cocoa Cocoa UI (Mac OS X only) virtfs VirtFS + virtiofsd build virtiofs daemon (virtiofsd) + libudev Use libudev to enumerate host devices mpath Multipath persistent reservation passthrough xen xen backend driver support xen-pci-passthrough PCI passthrough support for Xen @@ -1901,12 +1870,14 @@ disabled with --disable-FEATURE, default is enabled if available: vhost-crypto vhost-user-crypto backend support vhost-kernel vhost kernel backend support vhost-user vhost-user backend support + vhost-user-blk-server vhost-user-blk server support vhost-vdpa vhost-vdpa kernel backend support spice spice rbd rados block device (rbd) libiscsi iscsi support libnfs nfs support smartcard smartcard support (libcacard) + u2f U2F support (u2f-emu) libusb libusb (for usb passthrough) live-block-migration Block migration in the main migration stream usb-redir usb network redirection support @@ -1943,7 +1914,7 @@ disabled with --disable-FEATURE, default is enabled if available: vvfat vvfat image format support qed qed image format support parallels parallels image format support - sheepdog sheepdog block driver support + sheepdog sheepdog block driver support (deprecated) crypto-afalg Linux AF_ALG crypto backend driver capstone capstone disassembler support debug-mutex mutex debugging support @@ -1951,6 +1922,10 @@ disabled with --disable-FEATURE, default is enabled if available: xkbcommon xkbcommon support rng-none dummy RNG, avoid using /dev/(u)random and getrandom() libdaxctl libdaxctl support + fuse FUSE block device export + fuse-lseek SEEK_HOLE/SEEK_DATA support for FUSE exports + multiprocess Out of process device emulation support + gio libgio support NOTE: The object files are built at the place where configure is launched EOF @@ -1960,29 +1935,19 @@ fi # Remove old dependency files to make sure that they get properly regenerated rm -f */config-devices.mak.d -# Remove syscall_nr.h to be sure they will be regenerated in the build -# directory, not in the source directory -for arch in alpha hppa m68k xtensa sh4 microblaze arm ppc s390x sparc sparc64 \ - i386 x86_64 mips mips64 ; do - # remove the file if it has been generated in the source directory - rm -f "${source_path}/linux-user/${arch}/syscall_nr.h" - # remove the dependency files - for target in ${arch}*-linux-user ; do - test -d "${target}" && find "${target}" -type f -name "*.d" \ - -exec grep -q "${source_path}/linux-user/${arch}/syscall_nr.h" {} \; \ - -print | while read file ; do rm "${file}" "${file%.d}.o" ; done - done -done - if test -z "$python" then error_exit "Python not found. Use --python=/path/to/python" fi +if ! has "$make" +then + error_exit "GNU make ($make) not found" +fi # Note that if the Python conditional here evaluates True we will exit # with status 1 which is a shell 'false' value. -if ! $python -c 'import sys; sys.exit(sys.version_info < (3,5))'; then - error_exit "Cannot use '$python', Python >= 3.5 is required." \ +if ! $python -c 'import sys; sys.exit(sys.version_info < (3,6))'; then + error_exit "Cannot use '$python', Python >= 3.6 is required." \ "Use --python=/path/to/python to specify a supported Python." fi @@ -1992,6 +1957,60 @@ python_version=$($python -c 'import sys; print("%d.%d.%d" % (sys.version_info[0] # Suppress writing compiled files python="$python -B" +if test -z "$meson"; then + if test "$explicit_python" = no && has meson && version_ge "$(meson --version)" 0.55.3; then + meson=meson + elif test $git_submodules_action != 'ignore' ; then + meson=git + elif test -e "${source_path}/meson/meson.py" ; then + meson=internal + else + if test "$explicit_python" = yes; then + error_exit "--python requires using QEMU's embedded Meson distribution, but it was not found." + else + error_exit "Meson not found. Use --meson=/path/to/meson" + fi + fi +else + # Meson uses its own Python interpreter to invoke other Python scripts, + # but the user wants to use the one they specified with --python. + # + # We do not want to override the distro Python interpreter (and sometimes + # cannot: for example in Homebrew /usr/bin/meson is a bash script), so + # just require --meson=git|internal together with --python. + if test "$explicit_python" = yes; then + case "$meson" in + git | internal) ;; + *) error_exit "--python requires using QEMU's embedded Meson distribution." ;; + esac + fi +fi + +if test "$meson" = git; then + git_submodules="${git_submodules} meson" +fi + +case "$meson" in + git | internal) + meson="$python ${source_path}/meson/meson.py" + ;; + *) meson=$(command -v "$meson") ;; +esac + +# Probe for ninja + +if test -z "$ninja"; then + for c in ninja ninja-build samu; do + if has $c; then + ninja=$(command -v "$c") + break + fi + done + if test -z "$ninja"; then + error_exit "Cannot find Ninja" + fi +fi + # Check that the C compiler works. Doing this here before testing # the host CPU ensures that we had a valid CC to autodetect the # $cpu var (and we should bail right here if that's not the case). @@ -2006,20 +2025,10 @@ if ! compile_prog ; then error_exit "\"$cc\" cannot build an executable (is your linker broken?)" fi -# Now we have handled --enable-tcg-interpreter and know we're not just -# printing the help message, bail out if the host CPU isn't supported. -if test "$ARCH" = "unknown"; then - if test "$tcg_interpreter" = "yes" ; then - echo "Unsupported CPU = $cpu, will use TCG with TCI (experimental)" - else - error_exit "Unsupported CPU = $cpu, try --enable-tcg-interpreter" - fi -fi - # Consult white-list to determine whether to enable werror # by default. Only enable by default for git builds if test -z "$werror" ; then - if test -e "$source_path/.git" && \ + if test "$git_submodules_action" != "ignore" && \ { test "$linux" = "yes" || test "$mingw32" = "yes"; }; then werror="yes" else @@ -2027,7 +2036,7 @@ if test -z "$werror" ; then fi fi -if test "$bogus_os" = "yes"; then +if test "$targetos" = "bogus"; then # Now that we know that we're not printing the help and that # the compiler works (so the results of the check_defines we used # to identify the OS are reliable), if we didn't recognize the @@ -2078,6 +2087,7 @@ add_to warn_flags -Wempty-body add_to warn_flags -Wnested-externs add_to warn_flags -Wendif-labels add_to warn_flags -Wexpansion-to-defined +add_to warn_flags -Wimplicit-fallthrough=2 nowarn_flags= add_to nowarn_flags -Wno-initializer-overrides @@ -2122,7 +2132,7 @@ EOF for flag in $gcc_flags; do # We need to check both a compile and a link, since some compiler # setups fail only on a .c->.o compile and some only at link time - if do_cc $QEMU_CFLAGS -Werror $flag -c -o $TMPO $TMPC && + if compile_object "-Werror $flag" && compile_prog "-Werror $flag" ""; then QEMU_CFLAGS="$QEMU_CFLAGS $flag" QEMU_LDFLAGS="$QEMU_LDFLAGS $flag" @@ -2196,7 +2206,7 @@ fi if test "$static" = "yes"; then if test "$pie" != "no" && compile_prog "-Werror -fPIE -DPIE" "-static-pie"; then - QEMU_CFLAGS="-fPIE -DPIE $QEMU_CFLAGS" + CONFIGURE_CFLAGS="-fPIE -DPIE $CONFIGURE_CFLAGS" QEMU_LDFLAGS="-static-pie $QEMU_LDFLAGS" pie="yes" elif test "$pie" = "yes"; then @@ -2206,10 +2216,10 @@ if test "$static" = "yes"; then pie="no" fi elif test "$pie" = "no"; then - QEMU_CFLAGS="$CFLAGS_NOPIE $QEMU_CFLAGS" + CONFIGURE_CFLAGS="$CFLAGS_NOPIE $CONFIGURE_CFLAGS" elif compile_prog "-Werror -fPIE -DPIE" "-pie"; then - QEMU_CFLAGS="-fPIE -DPIE $QEMU_CFLAGS" - QEMU_LDFLAGS="-pie $QEMU_LDFLAGS" + CONFIGURE_CFLAGS="-fPIE -DPIE $CONFIGURE_CFLAGS" + CONFIGURE_LDFLAGS="-pie $CONFIGURE_LDFLAGS" pie="yes" elif test "$pie" = "yes"; then error_exit "PIE not available due to missing toolchain support" @@ -2253,18 +2263,6 @@ fi # Solaris specific configure tool chain decisions if test "$solaris" = "yes" ; then - if has $install; then - : - else - error_exit "Solaris install program not found. Use --install=/usr/ucb/install or" \ - "install fileutils from www.blastwave.org using pkg-get -i fileutils" \ - "to get ginstall which is used by default (which lives in /opt/csw/bin)" - fi - if test "$(path_of $install)" = "/usr/sbin/install" ; then - error_exit "Solaris /usr/sbin/install is not an appropriate install program." \ - "try ginstall from the GNU fileutils available from www.blastwave.org" \ - "using pkg-get -i fileutils, or use --install=/usr/ucb/install" - fi if has ar; then : else @@ -2277,12 +2275,13 @@ if test "$solaris" = "yes" ; then fi if test -z "${target_list+xxx}" ; then + default_targets=yes for target in $default_target_list; do - supported_target $target 2>/dev/null && \ - target_list="$target_list $target" + target_list="$target_list $target" done target_list="${target_list# }" else + default_targets=no target_list=$(echo "$target_list" | sed -e 's/,/ /g') for target in $target_list; do # Check that we recognised the target name; this allows a more @@ -2294,10 +2293,16 @@ else error_exit "Unknown target name '$target'" ;; esac - supported_target $target || exit 1 done fi +for target in $target_list; do + # if a deprecated target is enabled we note it here + if echo "$deprecated_targets_list" | grep -q "$target"; then + add_to deprecated_features $target + fi +done + # see if system emulation was really requested case " $target_list " in *"-softmmu "*) softmmu=yes @@ -2306,18 +2311,6 @@ case " $target_list " in ;; esac -for target in $target_list; do - case "$target" in - arm-softmmu | aarch64-softmmu | i386-softmmu | x86_64-softmmu) - edk2_blobs="yes" - ;; - esac -done -# The EDK2 binaries are compressed with bzip2 -if test "$edk2_blobs" = "yes" && ! has bzip2; then - error_exit "The bzip2 program is required for building QEMU" -fi - feature_not_found() { feature=$1 remedy=$2 @@ -2361,20 +2354,12 @@ if test -z "$want_tools"; then fi ########################################## -# cocoa implies not SDL or GTK -# (the cocoa UI code currently assumes it is always the active UI -# and doesn't interact well with other UI frontend code) -if test "$cocoa" = "yes"; then - if test "$sdl" = "yes"; then - error_exit "Cocoa and SDL UIs cannot both be enabled at once" - fi - if test "$gtk" = "yes"; then - error_exit "Cocoa and GTK UIs cannot both be enabled at once" - fi - gtk=no - sdl=no +# Disable features only meaningful for system-mode emulation +if test "$softmmu" = "no"; then + audio_drv_list="" fi +########################################## # Some versions of Mac OS X incorrectly define SIZE_MAX cat > $TMPC << EOF #include @@ -2402,12 +2387,6 @@ else l2tpv3=no fi -if check_include "pty.h" ; then - pty_h=yes -else - pty_h=no -fi - cat > $TMPC < int main(int argc, char *argv[]) { @@ -2424,9 +2403,8 @@ fi # vhost interdependencies and host support # vhost backends -test "$vhost_user" = "" && vhost_user=yes -if test "$vhost_user" = "yes" && test "$mingw32" = "yes"; then - error_exit "vhost-user isn't available on win32" +if test "$vhost_user" = "yes" && test "$linux" != "yes"; then + error_exit "vhost-user is only available on Linux" fi test "$vhost_vdpa" = "" && vhost_vdpa=$linux if test "$vhost_vdpa" = "yes" && test "$linux" != "yes"; then @@ -2466,43 +2444,13 @@ if test "$vhost_net_vdpa" = "yes" && test "$vhost_vdpa" = "no"; then error_exit "--enable-vhost-net-vdpa requires --enable-vhost-vdpa" fi -# OR the vhost-kernel and vhost-user values for simplicity +# OR the vhost-kernel, vhost-vdpa and vhost-user values for simplicity if test "$vhost_net" = ""; then test "$vhost_net_user" = "yes" && vhost_net=yes + test "$vhost_net_vdpa" = "yes" && vhost_net=yes test "$vhost_kernel" = "yes" && vhost_net=yes fi -########################################## -# MinGW / Mingw-w64 localtime_r/gmtime_r check - -if test "$mingw32" = "yes"; then - # Some versions of MinGW / Mingw-w64 lack localtime_r - # and gmtime_r entirely. - # - # Some versions of Mingw-w64 define a macro for - # localtime_r/gmtime_r. - # - # Some versions of Mingw-w64 will define functions - # for localtime_r/gmtime_r, but only if you have - # _POSIX_THREAD_SAFE_FUNCTIONS defined. For fun - # though, unistd.h and pthread.h both define - # that for you. - # - # So this #undef localtime_r and #include - # are not in fact redundant. -cat > $TMPC << EOF -#include -#include -#undef localtime_r -int main(void) { localtime_r(NULL, NULL); return 0; } -EOF - if compile_prog "" "" ; then - localtime_r="yes" - else - localtime_r="no" - fi -fi - ########################################## # pkg-config probe @@ -2529,120 +2477,10 @@ EOF fi fi -########################################## -# lzo check - -if test "$lzo" != "no" ; then - cat > $TMPC << EOF -#include -int main(void) { lzo_version(); return 0; } -EOF - if compile_prog "" "-llzo2" ; then - libs_softmmu="$libs_softmmu -llzo2" - lzo="yes" - else - if test "$lzo" = "yes"; then - feature_not_found "liblzo2" "Install liblzo2 devel" - fi - lzo="no" - fi -fi - -########################################## -# snappy check - -if test "$snappy" != "no" ; then - cat > $TMPC << EOF -#include -int main(void) { snappy_max_compressed_length(4096); return 0; } -EOF - if compile_prog "" "-lsnappy" ; then - libs_softmmu="$libs_softmmu -lsnappy" - snappy="yes" - else - if test "$snappy" = "yes"; then - feature_not_found "libsnappy" "Install libsnappy devel" - fi - snappy="no" - fi -fi - -########################################## -# bzip2 check - -if test "$bzip2" != "no" ; then - cat > $TMPC << EOF -#include -int main(void) { BZ2_bzlibVersion(); return 0; } -EOF - if compile_prog "" "-lbz2" ; then - bzip2="yes" - else - if test "$bzip2" = "yes"; then - feature_not_found "libbzip2" "Install libbzip2 devel" - fi - bzip2="no" - fi -fi - -########################################## -# lzfse check - -if test "$lzfse" != "no" ; then - cat > $TMPC << EOF -#include -int main(void) { lzfse_decode_scratch_size(); return 0; } -EOF - if compile_prog "" "-llzfse" ; then - lzfse="yes" - else - if test "$lzfse" = "yes"; then - feature_not_found "lzfse" "Install lzfse devel" - fi - lzfse="no" - fi -fi - -########################################## -# zstd check - -if test "$zstd" != "no" ; then - libzstd_minver="1.4.0" - if $pkg_config --atleast-version=$libzstd_minver libzstd ; then - zstd_cflags="$($pkg_config --cflags libzstd)" - zstd_libs="$($pkg_config --libs libzstd)" - LIBS="$zstd_libs $LIBS" - QEMU_CFLAGS="$QEMU_CFLAGS $zstd_cflags" - zstd="yes" - else - if test "$zstd" = "yes" ; then - feature_not_found "libzstd" "Install libzstd devel" - fi - zstd="no" - fi -fi - -########################################## -# libseccomp check - -if test "$seccomp" != "no" ; then - libseccomp_minver="2.3.0" - if $pkg_config --atleast-version=$libseccomp_minver libseccomp ; then - seccomp_cflags="$($pkg_config --cflags libseccomp)" - seccomp_libs="$($pkg_config --libs libseccomp)" - seccomp="yes" - else - if test "$seccomp" = "yes" ; then - feature_not_found "libseccomp" \ - "Install libseccomp devel >= $libseccomp_minver" - fi - seccomp="no" - fi -fi ########################################## # xen probe -if test "$xen" != "no" ; then +if test "$xen" != "disabled" ; then # Check whether Xen library path is specified via --extra-ldflags to avoid # overriding this setting with pkg-config output. If not, try pkg-config # to obtain all needed flags. @@ -2651,17 +2489,17 @@ if test "$xen" != "no" ; then $pkg_config --exists xencontrol ; then xen_ctrl_version="$(printf '%d%02d%02d' \ $($pkg_config --modversion xencontrol | sed 's/\./ /g') )" - xen=yes - xen_pc="xencontrol xenstore xenguest xenforeignmemory xengnttab" + xen=enabled + xen_pc="xencontrol xenstore xenforeignmemory xengnttab" xen_pc="$xen_pc xenevtchn xendevicemodel" if $pkg_config --exists xentoolcore; then xen_pc="$xen_pc xentoolcore" fi - QEMU_CFLAGS="$QEMU_CFLAGS $($pkg_config --cflags $xen_pc)" - libs_softmmu="$($pkg_config --libs $xen_pc) $libs_softmmu" + xen_cflags="$($pkg_config --cflags $xen_pc)" + xen_libs="$($pkg_config --libs $xen_pc)" else - xen_libs="-lxenstore -lxenctrl -lxenguest" + xen_libs="-lxenstore -lxenctrl" xen_stable_libs="-lxenforeignmemory -lxengnttab -lxenevtchn" # First we test whether Xen headers and libraries are available. @@ -2677,10 +2515,10 @@ int main(void) { EOF if ! compile_prog "" "$xen_libs" ; then # Xen not found - if test "$xen" = "yes" ; then + if test "$xen" = "enabled" ; then feature_not_found "xen" "Install xen devel" fi - xen=no + xen=disabled # Xen unstable elif @@ -2706,7 +2544,7 @@ EOF then xen_stable_libs="-lxendevicemodel $xen_stable_libs -lxentoolcore" xen_ctrl_version=41100 - xen=yes + xen=enabled elif cat > $TMPC < $TMPC < $TMPC < $TMPC < $TMPC <= $gtkversion"; then - gtk_cflags=$($pkg_config --cflags $gtkpackage) - gtk_libs=$($pkg_config --libs $gtkpackage) - gtk_version=$($pkg_config --modversion $gtkpackage) - if $pkg_config --exists "$gtkx11package >= $gtkversion"; then - need_x11=yes - gtk_cflags="$gtk_cflags $x11_cflags" - gtk_libs="$gtk_libs $x11_libs" - fi - gtk="yes" - elif test "$gtk" = "yes"; then - feature_not_found "gtk" "Install gtk3-devel" - else - gtk="no" - fi -fi - - ########################################## # GNUTLS probe @@ -3029,8 +2795,6 @@ if test "$gnutls" != "no"; then # At least ubuntu 18.04 ships only shared libraries. write_c_skeleton if compile_prog "" "$gnutls_libs" ; then - LIBS="$gnutls_libs $LIBS" - QEMU_CFLAGS="$QEMU_CFLAGS $gnutls_cflags" pass="yes" fi fi @@ -3100,8 +2864,6 @@ if test "$nettle" != "no"; then # Link test to make sure the given libraries work (e.g for static). write_c_skeleton if compile_prog "" "$nettle_libs" ; then - LIBS="$nettle_libs $LIBS" - QEMU_CFLAGS="$QEMU_CFLAGS $nettle_cflags" if test -z "$gcrypt"; then gcrypt="no" fi @@ -3144,8 +2906,6 @@ if test "$gcrypt" != "no"; then # Link test to make sure the given libraries work (e.g for static). write_c_skeleton if compile_prog "" "$gcrypt_libs" ; then - LIBS="$gcrypt_libs $LIBS" - QEMU_CFLAGS="$QEMU_CFLAGS $gcrypt_cflags" pass="yes" fi fi @@ -3229,28 +2989,6 @@ EOF fi fi -########################################## -# getifaddrs (for tests/test-io-channel-socket ) - -have_ifaddrs_h=yes -if ! check_include "ifaddrs.h" ; then - have_ifaddrs_h=no -fi - -######################################### -# libdrm check -have_drm_h=no -if check_include "libdrm/drm.h" ; then - have_drm_h=yes -fi - -######################################### -# sys/signal.h check -have_sys_signal_h=no -if check_include "sys/signal.h" ; then - have_sys_signal_h=yes -fi - ########################################## # VTE probe @@ -3273,125 +3011,6 @@ if test "$vte" != "no"; then fi fi -########################################## -# SDL probe - -# Look for sdl configuration program (pkg-config or sdl2-config). Try -# sdl2-config even without cross prefix, and favour pkg-config over sdl2-config. - -sdl_probe () -{ - if $pkg_config sdl2 --exists; then - sdlconfig="$pkg_config sdl2" - sdlversion=$($sdlconfig --modversion 2>/dev/null) - elif has "$sdl2_config"; then - sdlconfig="$sdl2_config" - sdlversion=$($sdlconfig --version) - else - if test "$sdl" = "yes" ; then - feature_not_found "sdl" "Install SDL2-devel" - fi - sdl=no - # no need to do the rest - return - fi - if test -n "$cross_prefix" && test "$(basename "$sdlconfig")" = sdl2-config; then - echo warning: using "\"$sdlconfig\"" to detect cross-compiled sdl >&2 - fi - - cat > $TMPC << EOF -#include -#undef main /* We don't want SDL to override our main() */ -int main( void ) { return SDL_Init (SDL_INIT_VIDEO); } -EOF - sdl_cflags=$($sdlconfig --cflags 2>/dev/null) - sdl_cflags="$sdl_cflags -Wno-undef" # workaround 2.0.8 bug - if test "$static" = "yes" ; then - if $pkg_config sdl2 --exists; then - sdl_libs=$($pkg_config sdl2 --static --libs 2>/dev/null) - else - sdl_libs=$($sdlconfig --static-libs 2>/dev/null) - fi - else - sdl_libs=$($sdlconfig --libs 2>/dev/null) - fi - if compile_prog "$sdl_cflags" "$sdl_libs" ; then - sdl=yes - - # static link with sdl ? (note: sdl.pc's --static --libs is broken) - if test "$sdl" = "yes" && test "$static" = "yes" ; then - if test $? = 0 && echo $sdl_libs | grep -- -laa > /dev/null; then - sdl_libs="$sdl_libs $(aalib-config --static-libs 2>/dev/null)" - sdl_cflags="$sdl_cflags $(aalib-config --cflags 2>/dev/null)" - fi - if compile_prog "$sdl_cflags" "$sdl_libs" ; then - : - else - sdl=no - fi - fi # static link - else # sdl not found - if test "$sdl" = "yes" ; then - feature_not_found "sdl" "Install SDL2 devel" - fi - sdl=no - fi # sdl compile test -} - -sdl_image_probe () -{ - if test "$sdl_image" != "no" ; then - if $pkg_config SDL2_image --exists; then - if test "$static" = "yes"; then - sdl_image_libs=$($pkg_config SDL2_image --libs --static 2>/dev/null) - else - sdl_image_libs=$($pkg_config SDL2_image --libs 2>/dev/null) - fi - sdl_image_cflags=$($pkg_config SDL2_image --cflags 2>/dev/null) - sdl_image=yes - - sdl_cflags="$sdl_cflags $sdl_image_cflags" - sdl_libs="$sdl_libs $sdl_image_libs" - else - if test "$sdl_image" = "yes" ; then - feature_not_found "sdl_image" "Install SDL Image devel" - else - sdl_image=no - fi - fi - fi -} - -if test "$sdl" != "no" ; then - sdl_probe -fi - -if test "$sdl" = "yes" ; then - sdl_image_probe -else - if test "$sdl_image" = "yes"; then - echo "warning: SDL Image requested, but SDL is not available, disabling" - fi - sdl_image=no -fi - -if test "$sdl" = "yes" ; then - cat > $TMPC < -#if defined(SDL_VIDEO_DRIVER_X11) -#include -#else -#error No x11 support -#endif -int main(void) { return 0; } -EOF - if compile_prog "$sdl_cflags $x11_cflags" "$sdl_libs $x11_libs" ; then - need_x11=yes - sdl_cflags="$sdl_cflags $x11_cflags" - sdl_libs="$sdl_libs $x11_libs" - fi -fi - ########################################## # RDMA needs OpenFabrics libraries if test "$rdma" != "no" ; then @@ -3402,7 +3021,6 @@ EOF rdma_libs="-lrdmacm -libverbs -libumad" if compile_prog "" "$rdma_libs" ; then rdma="yes" - libs_softmmu="$libs_softmmu $rdma_libs" else if test "$rdma" = "yes" ; then error_exit \ @@ -3487,101 +3105,6 @@ EOF fi fi -########################################## -# VNC SASL detection -if test "$vnc" = "yes" && test "$vnc_sasl" != "no" ; then - cat > $TMPC < -#include -int main(void) { sasl_server_init(NULL, "qemu"); return 0; } -EOF - # Assuming Cyrus-SASL installed in /usr prefix - # QEMU defines struct iovec in "qemu/osdep.h", - # we don't want libsasl to redefine it in . - vnc_sasl_cflags="-DSTRUCT_IOVEC_DEFINED" - vnc_sasl_libs="-lsasl2" - if compile_prog "$vnc_sasl_cflags" "$vnc_sasl_libs" ; then - vnc_sasl=yes - libs_softmmu="$vnc_sasl_libs $libs_softmmu" - QEMU_CFLAGS="$QEMU_CFLAGS $vnc_sasl_cflags" - else - if test "$vnc_sasl" = "yes" ; then - feature_not_found "vnc-sasl" "Install Cyrus SASL devel" - fi - vnc_sasl=no - fi -fi - -########################################## -# VNC JPEG detection -if test "$vnc" = "yes" && test "$vnc_jpeg" != "no" ; then -cat > $TMPC < -#include -int main(void) { struct jpeg_compress_struct s; jpeg_create_compress(&s); return 0; } -EOF - vnc_jpeg_cflags="" - vnc_jpeg_libs="-ljpeg" - if compile_prog "$vnc_jpeg_cflags" "$vnc_jpeg_libs" ; then - vnc_jpeg=yes - libs_softmmu="$vnc_jpeg_libs $libs_softmmu" - QEMU_CFLAGS="$QEMU_CFLAGS $vnc_jpeg_cflags" - else - if test "$vnc_jpeg" = "yes" ; then - feature_not_found "vnc-jpeg" "Install libjpeg-turbo devel" - fi - vnc_jpeg=no - fi -fi - -########################################## -# VNC PNG detection -if test "$vnc" = "yes" && test "$vnc_png" != "no" ; then -cat > $TMPC < -#include -#include -int main(void) { - png_structp png_ptr; - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - return png_ptr != 0; -} -EOF - if $pkg_config libpng --exists; then - vnc_png_cflags=$($pkg_config libpng --cflags) - vnc_png_libs=$($pkg_config libpng --libs) - else - vnc_png_cflags="" - vnc_png_libs="-lpng" - fi - if compile_prog "$vnc_png_cflags" "$vnc_png_libs" ; then - vnc_png=yes - libs_softmmu="$vnc_png_libs $libs_softmmu" - QEMU_CFLAGS="$QEMU_CFLAGS $vnc_png_cflags" - else - if test "$vnc_png" = "yes" ; then - feature_not_found "vnc-png" "Install libpng devel" - fi - vnc_png=no - fi -fi - -########################################## -# xkbcommon probe -if test "$xkbcommon" != "no" ; then - if $pkg_config xkbcommon --exists; then - xkbcommon_cflags=$($pkg_config xkbcommon --cflags) - xkbcommon_libs=$($pkg_config xkbcommon --libs) - xkbcommon=yes - else - if test "$xkbcommon" = "yes" ; then - feature_not_found "xkbcommon" "Install libxkbcommon-devel" - fi - xkbcommon=no - fi -fi - - ########################################## # xfsctl() probe, used for file-posix.c if test "$xfs" != "no" ; then @@ -3598,7 +3121,7 @@ EOF xfs="yes" else if test "$xfs" = "yes" ; then - feature_not_found "xfs" "Instal xfsprogs/xfslibs devel" + feature_not_found "xfs" "Install xfsprogs/xfslibs devel" fi xfs=no fi @@ -3656,40 +3179,21 @@ EOF fi fi -############################################ -# efi-aarch64 probe -# Check for efi files needed by aarch64 VMs. -# By default we will use the efi included with QEMU. -# Allow user to override the path for efi also. -if ! test -f "$efi_aarch64"; then - if test -f $source_path/pc-bios/edk2-aarch64-code.fd.bz2; then - # valid after build - efi_aarch64=$PWD/pc-bios/edk2-aarch64-code.fd - else - efi_aarch64="" - fi -fi - ########################################## -# libcap-ng library probe -if test "$cap_ng" != "no" ; then - cap_libs="-lcap-ng" +# detect CoreAudio +if test "$coreaudio" != "no" ; then + coreaudio_libs="-framework CoreAudio" cat > $TMPC << EOF -#include +#include int main(void) { - capng_capability_to_name(CAPNG_EFFECTIVE); - return 0; + return (int)AudioGetCurrentHostTime(); } EOF - if compile_prog "" "$cap_libs" ; then - cap_ng=yes - libs_tools="$cap_libs $libs_tools" + if compile_prog "" "$coreaudio_libs" ; then + coreaudio=yes else - if test "$cap_ng" = "yes" ; then - feature_not_found "cap_ng" "Install libcap-ng devel" - fi - cap_ng=no + coreaudio=no fi fi @@ -3702,6 +3206,8 @@ for drv in $audio_drv_list; do alsa | try-alsa) if $pkg_config alsa --exists; then alsa_libs=$($pkg_config alsa --libs) + alsa_cflags=$($pkg_config alsa --cflags) + alsa=yes if test "$drv" = "try-alsa"; then audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-alsa/alsa/') fi @@ -3717,7 +3223,9 @@ for drv in $audio_drv_list; do pa | try-pa) if $pkg_config libpulse --exists; then + libpulse=yes pulse_libs=$($pkg_config libpulse --libs) + pulse_cflags=$($pkg_config libpulse --cflags) if test "$drv" = "try-pa"; then audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-pa/pa/') fi @@ -3745,8 +3253,20 @@ for drv in $audio_drv_list; do fi ;; - coreaudio) + coreaudio | try-coreaudio) + if test "$coreaudio" = "no"; then + if test "$drv" = "try-coreaudio"; then + audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-coreaudio//') + else + error_exit "$drv check failed" \ + "Make sure to have the $drv is available." + fi + else coreaudio_libs="-framework CoreAudio" + if test "$drv" = "try-coreaudio"; then + audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-coreaudio/coreaudio/') + fi + fi ;; dsound) @@ -3760,6 +3280,7 @@ for drv in $audio_drv_list; do jack | try-jack) if $pkg_config jack --exists; then + libjack=yes jack_libs=$($pkg_config jack --libs) if test "$drv" = "try-jack"; then audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-jack/jack/') @@ -3783,149 +3304,6 @@ for drv in $audio_drv_list; do esac done -########################################## -# BrlAPI probe - -if test "$brlapi" != "no" ; then - brlapi_libs="-lbrlapi" - cat > $TMPC << EOF -#include -#include -int main( void ) { return brlapi__openConnection (NULL, NULL, NULL); } -EOF - if compile_prog "" "$brlapi_libs" ; then - brlapi=yes - else - if test "$brlapi" = "yes" ; then - feature_not_found "brlapi" "Install brlapi devel" - fi - brlapi=no - fi -fi - -########################################## -# iconv probe -if test "$iconv" != "no" ; then - cat > $TMPC << EOF -#include -int main(void) { - iconv_t conv = iconv_open("WCHAR_T", "UCS-2"); - return conv != (iconv_t) -1; -} -EOF - iconv_prefix_list="/usr/local:/usr" - iconv_lib_list=":-liconv" - IFS=: - for iconv_prefix in $iconv_prefix_list; do - IFS=: - iconv_cflags="-I$iconv_prefix/include" - iconv_ldflags="-L$iconv_prefix/lib" - for iconv_link in $iconv_lib_list; do - unset IFS - iconv_lib="$iconv_ldflags $iconv_link" - echo "looking at iconv in '$iconv_cflags' '$iconv_lib'" >> config.log - if compile_prog "$iconv_cflags" "$iconv_lib" ; then - iconv_found=yes - break - fi - done - if test "$iconv_found" = yes ; then - break - fi - done - if test "$iconv_found" = "yes" ; then - iconv=yes - else - if test "$iconv" = "yes" ; then - feature_not_found "iconv" "Install iconv devel" - fi - iconv=no - fi -fi - -########################################## -# curses probe -if test "$iconv" = "no" ; then - # curses will need iconv - curses=no -fi -if test "$curses" != "no" ; then - if test "$mingw32" = "yes" ; then - curses_inc_list="$($pkg_config --cflags ncurses 2>/dev/null):" - curses_lib_list="$($pkg_config --libs ncurses 2>/dev/null):-lpdcurses" - else - curses_inc_list="$($pkg_config --cflags ncursesw 2>/dev/null):-I/usr/include/ncursesw:" - curses_lib_list="$($pkg_config --libs ncursesw 2>/dev/null):-lncursesw:-lcursesw" - fi - curses_found=no - cat > $TMPC << EOF -#include -#include -#include -#include -int main(void) { - const char *codeset; - wchar_t wch = L'w'; - setlocale(LC_ALL, ""); - resize_term(0, 0); - addwstr(L"wide chars\n"); - addnwstr(&wch, 1); - add_wch(WACS_DEGREE); - codeset = nl_langinfo(CODESET); - return codeset != 0; -} -EOF - IFS=: - for curses_inc in $curses_inc_list; do - # Make sure we get the wide character prototypes - curses_inc="-DNCURSES_WIDECHAR $curses_inc" - IFS=: - for curses_lib in $curses_lib_list; do - unset IFS - if compile_prog "$curses_inc" "$curses_lib" ; then - curses_found=yes - break - fi - done - if test "$curses_found" = yes ; then - break - fi - done - unset IFS - if test "$curses_found" = "yes" ; then - curses=yes - else - if test "$curses" = "yes" ; then - feature_not_found "curses" "Install ncurses devel" - fi - curses=no - fi -fi - -########################################## -# curl probe -if test "$curl" != "no" ; then - if $pkg_config libcurl --exists; then - curlconfig="$pkg_config libcurl" - else - curlconfig=curl-config - fi - cat > $TMPC << EOF -#include -int main(void) { curl_easy_init(); curl_multi_setopt(0, 0, 0); return 0; } -EOF - curl_cflags=$($curlconfig --cflags 2>/dev/null) - curl_libs=$($curlconfig --libs 2>/dev/null) - if compile_prog "$curl_cflags" "$curl_libs" ; then - curl=yes - else - if test "$curl" = "yes" ; then - feature_not_found "curl" "Install libcurl devel" - fi - curl=no - fi -fi # test "$curl" - ########################################## # glib support probe @@ -3938,40 +3316,64 @@ if test "$plugins" = yes; then glib_modules="$glib_modules gmodule-2.0" fi -# This workaround is required due to a bug in pkg-config file for glib as it -# doesn't define GLIB_STATIC_COMPILATION for pkg-config --static - -if test "$static" = yes && test "$mingw32" = yes; then - QEMU_CFLAGS="-DGLIB_STATIC_COMPILATION $QEMU_CFLAGS" -fi - for i in $glib_modules; do if $pkg_config --atleast-version=$glib_req_ver $i; then glib_cflags=$($pkg_config --cflags $i) glib_libs=$($pkg_config --libs $i) - QEMU_CFLAGS="$glib_cflags $QEMU_CFLAGS" - LIBS="$glib_libs $LIBS" - libs_qga="$glib_libs $libs_qga" else error_exit "glib-$glib_req_ver $i is required to compile QEMU" fi done -if $pkg_config --atleast-version=$glib_req_ver gio-2.0; then - gio=yes - gio_cflags=$($pkg_config --cflags gio-2.0) - gio_libs=$($pkg_config --libs gio-2.0) - gdbus_codegen=$($pkg_config --variable=gdbus_codegen gio-2.0) - if [ ! -x "$gdbus_codegen" ]; then - gdbus_codegen= - fi -else - gio=no +# This workaround is required due to a bug in pkg-config file for glib as it +# doesn't define GLIB_STATIC_COMPILATION for pkg-config --static + +if test "$static" = yes && test "$mingw32" = yes; then + glib_cflags="-DGLIB_STATIC_COMPILATION $glib_cflags" fi -if $pkg_config --atleast-version=$glib_req_ver gio-unix-2.0; then - gio_cflags="$gio_cflags $($pkg_config --cflags gio-unix-2.0)" - gio_libs="$gio_libs $($pkg_config --libs gio-unix-2.0)" +if ! test "$gio" = "no"; then + pass=no + if $pkg_config --atleast-version=$glib_req_ver gio-2.0; then + gio_cflags=$($pkg_config --cflags gio-2.0) + gio_libs=$($pkg_config --libs gio-2.0) + gdbus_codegen=$($pkg_config --variable=gdbus_codegen gio-2.0) + if [ ! -x "$gdbus_codegen" ]; then + gdbus_codegen= + fi + # Check that the libraries actually work -- Ubuntu 18.04 ships + # with pkg-config --static --libs data for gio-2.0 that is missing + # -lblkid and will give a link error. + cat > $TMPC < +int main(void) +{ + g_dbus_proxy_new_sync(0, 0, 0, 0, 0, 0, 0, 0); + return 0; +} +EOF + if compile_prog "$gio_cflags" "$gio_libs" ; then + pass=yes + else + pass=no + fi + + if test "$pass" = "yes" && + $pkg_config --atleast-version=$glib_req_ver gio-unix-2.0; then + gio_cflags="$gio_cflags $($pkg_config --cflags gio-unix-2.0)" + gio_libs="$gio_libs $($pkg_config --libs gio-unix-2.0)" + fi + fi + + if test "$pass" = "no"; then + if test "$gio" = "yes"; then + feature_not_found "gio" "Install libgio >= 2.0" + else + gio=no + fi + else + gio=yes + fi fi # Sanity check that the current size_t matches the @@ -3991,7 +3393,7 @@ int main(void) { } EOF -if ! compile_prog "$CFLAGS" "$LIBS" ; then +if ! compile_prog "$glib_cflags" "$glib_libs" ; then error_exit "sizeof(size_t) doesn't match GLIB_SIZEOF_SIZE_T."\ "You probably need to set PKG_CONFIG_LIBDIR"\ "to point to the right pkg-config files for your"\ @@ -4006,7 +3408,7 @@ EOF if ! compile_prog "$glib_cflags -Werror" "$glib_libs" ; then if cc_has_warning_flag "-Wno-unknown-attributes"; then glib_cflags="-Wno-unknown-attributes $glib_cflags" - CFLAGS="-Wno-unknown-attributes $CFLAGS" + CONFIGURE_CFLAGS="-Wno-unknown-attributes $CONFIGURE_CFLAGS" fi fi @@ -4026,30 +3428,7 @@ EOF if ! compile_prog "$glib_cflags -Werror" "$glib_libs" ; then if cc_has_warning_flag "-Wno-unused-function"; then glib_cflags="$glib_cflags -Wno-unused-function" - CFLAGS="$CFLAGS -Wno-unused-function" - fi -fi - -######################################### -# zlib check - -if test "$zlib" != "no" ; then - if $pkg_config --exists zlib; then - zlib_cflags=$($pkg_config --cflags zlib) - zlib_libs=$($pkg_config --libs zlib) - QEMU_CFLAGS="$zlib_cflags $QEMU_CFLAGS" - LIBS="$zlib_libs $LIBS" - else - cat > $TMPC << EOF -#include -int main(void) { zlibVersion(); return 0; } -EOF - if compile_prog "" "-lz" ; then - LIBS="$LIBS -lz" - else - error_exit "zlib check failed" \ - "Make sure to have the zlib libs and headers installed." - fi + CONFIGURE_CFLAGS="$CONFIGURE_CFLAGS -Wno-unused-function" fi fi @@ -4068,71 +3447,6 @@ if test "$modules" = yes; then fi fi -########################################## -# pixman support probe - -if test "$softmmu" = "no"; then - pixman_cflags= - pixman_libs= -elif $pkg_config --atleast-version=0.21.8 pixman-1 > /dev/null 2>&1; then - pixman_cflags=$($pkg_config --cflags pixman-1) - pixman_libs=$($pkg_config --libs pixman-1) -else - error_exit "pixman >= 0.21.8 not present." \ - "Please install the pixman devel package." -fi - -########################################## -# libmpathpersist probe - -if test "$mpath" != "no" ; then - # probe for the new API - cat > $TMPC < -#include -unsigned mpath_mx_alloc_len = 1024; -int logsink; -static struct config *multipath_conf; -extern struct udev *udev; -extern struct config *get_multipath_config(void); -extern void put_multipath_config(struct config *conf); -struct udev *udev; -struct config *get_multipath_config(void) { return multipath_conf; } -void put_multipath_config(struct config *conf) { } - -int main(void) { - udev = udev_new(); - multipath_conf = mpath_lib_init(); - return 0; -} -EOF - if compile_prog "" "-ludev -lmultipath -lmpathpersist" ; then - mpathpersist=yes - mpathpersist_new_api=yes - else - # probe for the old API - cat > $TMPC < -#include -unsigned mpath_mx_alloc_len = 1024; -int logsink; -int main(void) { - struct udev *udev = udev_new(); - mpath_lib_init(udev); - return 0; -} -EOF - if compile_prog "" "-ludev -lmultipath -lmpathpersist" ; then - mpathpersist=yes - mpathpersist_new_api=no - else - mpathpersist=no - fi - fi -else - mpathpersist=no -fi - ########################################## # pthread probe PTHREADLIBS_LIST="-pthread -lpthread -lpthreadGC2" @@ -4153,18 +3467,6 @@ else for pthread_lib in $PTHREADLIBS_LIST; do if compile_prog "" "$pthread_lib" ; then pthread=yes - found=no - for lib_entry in $LIBS; do - if test "$lib_entry" = "$pthread_lib"; then - found=yes - break - fi - done - if test "$found" = "no"; then - LIBS="$pthread_lib $LIBS" - libs_qga="$pthread_lib $libs_qga" - fi - PTHREAD_LIB="$pthread_lib" break fi done @@ -4210,29 +3512,6 @@ if compile_prog "" "$pthread_lib" ; then pthread_setname_np_wo_tid=yes fi -########################################## -# rbd probe -if test "$rbd" != "no" ; then - cat > $TMPC < -#include -int main(void) { - rados_t cluster; - rados_create(&cluster, NULL); - return 0; -} -EOF - rbd_libs="-lrbd -lrados" - if compile_prog "" "$rbd_libs" ; then - rbd=yes - else - if test "$rbd" = "yes" ; then - feature_not_found "rados block device" "Install librbd/ceph devel" - fi - rbd=no - fi -fi - ########################################## # libssh probe if test "$libssh" != "no" ; then @@ -4292,11 +3571,6 @@ if test "$linux_io_uring" != "no" ; then linux_io_uring_cflags=$($pkg_config --cflags liburing) linux_io_uring_libs=$($pkg_config --libs liburing) linux_io_uring=yes - - # io_uring is used in libqemuutil.a where per-file -libs variables are not - # seen by programs linking the archive. It's not ideal, but just add the - # library dependency globally. - LIBS="$linux_io_uring_libs $LIBS" else if test "$linux_io_uring" = "yes" ; then feature_not_found "linux io_uring" "Install liburing devel" @@ -4320,35 +3594,6 @@ elif test "$tpm" = "yes"; then fi fi -########################################## -# attr probe - -if test "$attr" != "no" ; then - cat > $TMPC < -#include -#ifdef CONFIG_LIBATTR -#include -#else -#include -#endif -int main(void) { getxattr(NULL, NULL, NULL, 0); setxattr(NULL, NULL, NULL, 0, 0); return 0; } -EOF - if compile_prog "" "" ; then - attr=yes - # Older distros have , and need -lattr: - elif compile_prog "-DCONFIG_LIBATTR" "-lattr" ; then - attr=yes - LIBS="-lattr $LIBS" - libattr=yes - else - if test "$attr" = "yes" ; then - feature_not_found "ATTR" "Install libc6 or libattr devel" - fi - attr=no - fi -fi - ########################################## # iovec probe cat > $TMPC < $TMPC < -#include -#include -int main(void) { return preadv(0, 0, 0, 0); } -EOF -preadv=no -if compile_prog "" "" ; then - preadv=yes -fi - ########################################## # fdt probe -# fdt support is mandatory for at least some target architectures, -# so insist on it if we're building those system emulators. -fdt_required=no -for target in $target_list; do - case $target in - aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu|riscv*-softmmu|rx-softmmu) - fdt_required=yes + +case "$fdt" in + auto | enabled | internal) + # Simpler to always update submodule, even if not needed. + if test "$git_submodules_action" != "ignore"; then + git_submodules="${git_submodules} dtc" + fi ;; - esac -done - -if test "$fdt_required" = "yes"; then - if test "$fdt" = "no"; then - error_exit "fdt disabled but some requested targets require it." \ - "You can turn off fdt only if you also disable all the system emulation" \ - "targets which need it (by specifying a cut down --target-list)." - fi - fdt=yes -elif test "$fdt" != "yes" ; then - fdt=no -fi - -# fdt is only required when building softmmu targets -if test -z "$fdt" -a "$softmmu" != "yes" ; then - fdt="no" -fi - -if test "$fdt" != "no" ; then - fdt_libs="-lfdt" - # explicitly check for libfdt_env.h as it is missing in some stable installs - # and test for required functions to make sure we are on a version >= 1.4.2 - cat > $TMPC << EOF -#include -#include -int main(void) { fdt_check_full(NULL, 0); return 0; } -EOF - if compile_prog "" "$fdt_libs" ; then - # system DTC is good - use it - fdt=system - else - # have GIT checkout, so activate dtc submodule - if test -e "${source_path}/.git" ; then - git_submodules="${git_submodules} dtc" - fi - if test -d "${source_path}/dtc/libfdt" || test -e "${source_path}/.git" ; then - fdt=git - mkdir -p dtc - if [ "$pwd_is_source_path" != "y" ] ; then - symlink "$source_path/dtc/Makefile" "dtc/Makefile" - fi - fdt_cflags="-I\$(SRC_PATH)/dtc/libfdt" - fdt_ldflags="-L\$(BUILD_DIR)/dtc/libfdt" - fdt_libs="$fdt_libs" - elif test "$fdt" = "yes" ; then - # Not a git build & no libfdt found, prompt for system install - error_exit "DTC (libfdt) version >= 1.4.2 not present." \ - "Please install the DTC (libfdt) devel package" - else - # don't have and don't want - fdt_libs= - fdt=no - fi - fi -fi - -libs_softmmu="$libs_softmmu $fdt_libs" +esac ########################################## # opengl probe (for sdl2, gtk, milkymist-tmu2) @@ -4458,18 +3633,24 @@ fi if false ; then if test "$opengl" != "no" ; then - opengl_pkgs="epoxy gbm" - if $pkg_config $opengl_pkgs; then - opengl_cflags="$($pkg_config --cflags $opengl_pkgs)" - opengl_libs="$($pkg_config --libs $opengl_pkgs)" - opengl=yes - if test "$gtk" = "yes" && $pkg_config --exists "$gtkpackage >= 3.16"; then - gtk_gl="yes" + epoxy=no + if $pkg_config epoxy; then + cat > $TMPC << EOF +#include +int main(void) { return 0; } +EOF + if compile_prog "" "" ; then + epoxy=yes fi - QEMU_CFLAGS="$QEMU_CFLAGS $opengl_cflags" + fi + + if test "$epoxy" = "yes" ; then + opengl_cflags="$($pkg_config --cflags epoxy)" + opengl_libs="$($pkg_config --libs epoxy)" + opengl=yes else if test "$opengl" = "yes" ; then - feature_not_found "opengl" "Please install opengl (mesa) devel pkgs: $opengl_pkgs" + feature_not_found "opengl" "Please install epoxy with EGL" fi opengl_cflags="" opengl_libs="" @@ -4477,29 +3658,6 @@ if test "$opengl" != "no" ; then fi fi -if test "$opengl" = "yes"; then - cat > $TMPC << EOF -#include -#ifndef EGL_MESA_image_dma_buf_export -# error mesa/epoxy lacks support for dmabufs (mesa 10.6+) -#endif -int main(void) { return 0; } -EOF - if compile_prog "" "" ; then - opengl_dmabuf=yes - fi -fi - -if test "$opengl" = "yes" && test "$have_x11" = "yes"; then - for target in $target_list; do - case $target in - lm32-softmmu) # milkymist-tmu2 requires X11 and OpenGL - need_x11=yes - ;; - esac - done -fi - fi # XBOX # XBOX @@ -4559,8 +3717,8 @@ fi ########################################## # openssl probe if $pkg_config --exists openssl; then - QEMU_CFLAGS="$QEMU_CFLAGS $($pkg_config --cflags openssl)" - LIBS="$LIBS $($pkg_config --libs openssl)" + openssl_cflags=$($pkg_config --cflags openssl) + openssl_libs=$($pkg_config --libs openssl) else feature_not_found "openssl" "Install openssl-dev" fi @@ -4568,8 +3726,8 @@ fi ########################################## # libsamplerate probe if $pkg_config --exists samplerate; then - QEMU_CFLAGS="$QEMU_CFLAGS $($pkg_config --cflags samplerate)" - LIBS="$LIBS $($pkg_config --libs samplerate)" + libsamplerate_cflags=$($pkg_config --cflags samplerate) + libsamplerate_libs=$($pkg_config --libs samplerate) else feature_not_found "samplerate" "Install libsamplerate0-dev" fi @@ -4589,64 +3747,6 @@ if test "$libxml2" != "no" ; then fi fi -########################################## -# glusterfs probe -if test "$glusterfs" != "no" ; then - if $pkg_config --atleast-version=3 glusterfs-api; then - glusterfs="yes" - glusterfs_cflags=$($pkg_config --cflags glusterfs-api) - glusterfs_libs=$($pkg_config --libs glusterfs-api) - if $pkg_config --atleast-version=4 glusterfs-api; then - glusterfs_xlator_opt="yes" - fi - if $pkg_config --atleast-version=5 glusterfs-api; then - glusterfs_discard="yes" - fi - if $pkg_config --atleast-version=6 glusterfs-api; then - glusterfs_fallocate="yes" - glusterfs_zerofill="yes" - fi - cat > $TMPC << EOF -#include - -int -main(void) -{ - /* new glfs_ftruncate() passes two additional args */ - return glfs_ftruncate(NULL, 0, NULL, NULL); -} -EOF - if compile_prog "$glusterfs_cflags" "$glusterfs_libs" ; then - glusterfs_ftruncate_has_stat="yes" - fi - cat > $TMPC << EOF -#include - -/* new glfs_io_cbk() passes two additional glfs_stat structs */ -static void -glusterfs_iocb(glfs_fd_t *fd, ssize_t ret, struct glfs_stat *prestat, struct glfs_stat *poststat, void *data) -{} - -int -main(void) -{ - glfs_io_cbk iocb = &glusterfs_iocb; - iocb(NULL, 0 , NULL, NULL, NULL); - return 0; -} -EOF - if compile_prog "$glusterfs_cflags" "$glusterfs_libs" ; then - glusterfs_iocb_has_stat="yes" - fi - else - if test "$glusterfs" = "yes" ; then - feature_not_found "GlusterFS backend support" \ - "Install glusterfs-api devel >= 3" - fi - glusterfs="no" - fi -fi - # Check for inotify functions when we are building linux-user # emulator. This is done because older glibc versions don't # have syscall stubs for these implemented. In that case we @@ -4744,7 +3844,7 @@ EOF if compile_prog "" "-lnuma" ; then numa=yes - libs_softmmu="-lnuma $libs_softmmu" + numa_libs="-lnuma" else if test "$numa" = "yes" ; then feature_not_found "numa" "install numactl devel" @@ -4753,77 +3853,14 @@ EOF fi fi +malloc=system if test "$tcmalloc" = "yes" && test "$jemalloc" = "yes" ; then echo "ERROR: tcmalloc && jemalloc can't be used at the same time" exit 1 -fi - -# Even if malloc_trim() is available, these non-libc memory allocators -# do not support it. -if test "$tcmalloc" = "yes" || test "$jemalloc" = "yes" ; then - if test "$malloc_trim" = "yes" ; then - echo "Disabling malloc_trim with non-libc memory allocator" - fi - malloc_trim="no" -fi - -####################################### -# malloc_trim - -if test "$malloc_trim" != "no" ; then - cat > $TMPC << EOF -#include -int main(void) { malloc_trim(0); return 0; } -EOF - if compile_prog "" "" ; then - malloc_trim="yes" - else - malloc_trim="no" - fi -fi - -########################################## -# tcmalloc probe - -if test "$tcmalloc" = "yes" ; then - cat > $TMPC << EOF -#include -int main(void) { - void *tmp = malloc(1); - if (tmp != NULL) { - return 0; - } - return 1; -} -EOF - - if compile_prog "" "-ltcmalloc" ; then - LIBS="-ltcmalloc $LIBS" - else - feature_not_found "tcmalloc" "install gperftools devel" - fi -fi - -########################################## -# jemalloc probe - -if test "$jemalloc" = "yes" ; then - cat > $TMPC << EOF -#include -int main(void) { - void *tmp = malloc(1); - if (tmp != NULL) { - return 0; - } - return 1; -} -EOF - - if compile_prog "" "-ljemalloc" ; then - LIBS="-ljemalloc $LIBS" - else - feature_not_found "jemalloc" "install jemalloc devel" - fi +elif test "$tcmalloc" = "yes" ; then + malloc=tcmalloc +elif test "$jemalloc" = "yes" ; then + malloc=jemalloc fi ########################################## @@ -5130,6 +4167,7 @@ fi clock_adjtime=no cat > $TMPC < +#include int main(void) { @@ -5156,51 +4194,6 @@ if compile_prog "" "" ; then syncfs=yes fi -# check for kcov support (kernel must be 4.4+, compiled with certain options) -kcov=no -if check_include sys/kcov.h ; then - kcov=yes -fi - -# If we're making warnings fatal, apply this to Sphinx runs as well -sphinx_werror="" -if test "$werror" = "yes"; then - sphinx_werror="-W" -fi - -# Check we have a new enough version of sphinx-build -has_sphinx_build() { - # This is a bit awkward but works: create a trivial document and - # try to run it with our configuration file (which enforces a - # version requirement). This will fail if either - # sphinx-build doesn't exist at all or if it is too old. - mkdir -p "$TMPDIR1/sphinx" - touch "$TMPDIR1/sphinx/index.rst" - "$sphinx_build" $sphinx_werror -c "$source_path/docs" \ - -b html "$TMPDIR1/sphinx" \ - "$TMPDIR1/sphinx/out" >> config.log 2>&1 -} - -# Check if tools are available to build documentation. -if test "$docs" != "no" ; then - if has_sphinx_build; then - sphinx_ok=yes - else - sphinx_ok=no - fi - if has makeinfo && has pod2man && test "$sphinx_ok" = "yes"; then - docs=yes - else - if test "$docs" = "yes" ; then - if has $sphinx_build && test "$sphinx_ok" != "yes"; then - echo "Warning: $sphinx_build exists but it is either too old or uses too old a Python version" >&2 - fi - feature_not_found "docs" "Install texinfo, Perl/perl-podlators and a Python 3 version of python-sphinx" - fi - docs=no - fi -fi - # Search for bswap_32 function byteswap_h=no cat > $TMPC << EOF @@ -5223,60 +4216,7 @@ if compile_prog "" "" ; then bswap_h=yes fi -########################################## -# Do we have libiscsi >= 1.9.0 -if test "$libiscsi" != "no" ; then - if $pkg_config --atleast-version=1.9.0 libiscsi; then - libiscsi="yes" - libiscsi_cflags=$($pkg_config --cflags libiscsi) - libiscsi_libs=$($pkg_config --libs libiscsi) - else - if test "$libiscsi" = "yes" ; then - feature_not_found "libiscsi" "Install libiscsi >= 1.9.0" - fi - libiscsi="no" - fi -fi - -########################################## -# Do we need libm -cat > $TMPC << EOF -#include -int main(int argc, char **argv) { return isnan(sin((double)argc)); } -EOF -if compile_prog "" "" ; then - : -elif compile_prog "" "-lm" ; then - LIBS="-lm $LIBS" - libs_qga="-lm $libs_qga" -else - error_exit "libm check failed" -fi - -########################################## -# Do we need librt -# uClibc provides 2 versions of clock_gettime(), one with realtime -# support and one without. This means that the clock_gettime() don't -# need -lrt. We still need it for timer_create() so we check for this -# function in addition. -cat > $TMPC < -#include -int main(void) { - timer_create(CLOCK_REALTIME, NULL, NULL); - return clock_gettime(CLOCK_REALTIME, NULL); -} -EOF - -if compile_prog "" "" ; then - : -# we need pthread for static linking. use previous pthread test result -elif compile_prog "" "$pthread_lib -lrt" ; then - LIBS="$LIBS -lrt" - libs_qga="$libs_qga -lrt" -fi - -# Check whether we need to link libutil for openpty() +# Check whether we have openpty() in either libc or libutil cat > $TMPC << EOF extern int openpty(int *am, int *as, char *name, void *termp, void *winp); int main(void) { return openpty(0, 0, 0, 0, 0); } @@ -5287,8 +4227,6 @@ if compile_prog "" "" ; then have_openpty="yes" else if compile_prog "" "-lutil" ; then - libs_softmmu="-lutil $libs_softmmu" - libs_tools="-lutil $libs_tools" have_openpty="yes" fi fi @@ -5306,10 +4244,6 @@ EOF $pkg_config --atleast-version=0.12.3 spice-protocol && \ compile_prog "$spice_cflags" "$spice_libs" ; then spice="yes" - libs_softmmu="$libs_softmmu $spice_libs" - QEMU_CFLAGS="$QEMU_CFLAGS $spice_cflags" - spice_protocol_version=$($pkg_config --modversion spice-protocol) - spice_server_version=$($pkg_config --modversion spice-server) else if test "$spice" = "yes" ; then feature_not_found "spice" \ @@ -5465,51 +4399,11 @@ fi # capstone case "$capstone" in - "" | yes) - if $pkg_config capstone; then - capstone=system - elif test -e "${source_path}/.git" && test $git_update = 'yes' ; then - capstone=git - elif test -e "${source_path}/capstone/Makefile" ; then - capstone=internal - elif test -z "$capstone" ; then - capstone=no - else - feature_not_found "capstone" "Install capstone devel or git submodule" - fi - ;; - - system) - if ! $pkg_config capstone; then - feature_not_found "capstone" "Install capstone devel" - fi - ;; -esac - -case "$capstone" in - git | internal) - if test "$capstone" = git; then + auto | enabled | internal) + # Simpler to always update submodule, even if not needed. + if test "$git_submodules_action" != "ignore"; then git_submodules="${git_submodules} capstone" fi - mkdir -p capstone - QEMU_CFLAGS="$QEMU_CFLAGS -I\$(SRC_PATH)/capstone/include" - if test "$mingw32" = "yes"; then - LIBCAPSTONE=capstone.lib - else - LIBCAPSTONE=libcapstone.a - fi - libs_cpu="-L\$(BUILD_DIR)/capstone -lcapstone $libs_cpu" - ;; - - system) - QEMU_CFLAGS="$QEMU_CFLAGS $($pkg_config --cflags capstone)" - libs_cpu="$($pkg_config --libs capstone) $libs_cpu" - ;; - - no) - ;; - *) - error_exit "Unknown state for capstone: $capstone" ;; esac @@ -5652,9 +4546,6 @@ EOF else urcu_bp_libs="-lurcu-bp" fi - - LIBS="$lttng_ust_libs $urcu_bp_libs $LIBS" - libs_qga="$lttng_ust_libs $urcu_bp_libs $libs_qga" else error_exit "Trace backend 'ust' missing lttng-ust header files" fi @@ -5669,6 +4560,13 @@ if have_backend "dtrace"; then trace_backend_stap="no" if has 'stap' ; then trace_backend_stap="yes" + + # Workaround to avoid dtrace(1) producing a file with 'hidden' symbol + # visibility. Define STAP_SDT_V2 to produce 'default' symbol visibility + # instead. QEMU --enable-modules depends on this because the SystemTap + # semaphores are linked into the main binary and not the module's shared + # object. + QEMU_CFLAGS="$QEMU_CFLAGS -DSTAP_SDT_V2" fi fi @@ -5965,9 +4863,9 @@ if test "$int128" = "yes"; then int main(void) { unsigned __int128 x = 0, y = 0; - y = __atomic_load_16(&x, 0); - __atomic_store_16(&x, y, 0); - __atomic_compare_exchange_16(&x, &y, x, 0, 0, 0); + y = __atomic_load(&x, 0); + __atomic_store(&x, y, 0); + __atomic_compare_exchange(&x, &y, x, 0, 0, 0); return 0; } EOF @@ -6002,11 +4900,11 @@ int main(void) { uint64_t x = 0, y = 0; #ifdef __ATOMIC_RELAXED - y = __atomic_load_8(&x, 0); - __atomic_store_8(&x, y, 0); - __atomic_compare_exchange_8(&x, &y, x, 0, 0, 0); - __atomic_exchange_8(&x, y, 0); - __atomic_fetch_add_8(&x, y, 0); + y = __atomic_load_n(&x, __ATOMIC_RELAXED); + __atomic_store_n(&x, y, __ATOMIC_RELAXED); + __atomic_compare_exchange_n(&x, &y, x, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED); + __atomic_exchange_n(&x, y, __ATOMIC_RELAXED); + __atomic_fetch_add(&x, y, __ATOMIC_RELAXED); #else typedef char is_host64[sizeof(void *) >= sizeof(uint64_t) ? 1 : -1]; __sync_lock_test_and_set(&x, y); @@ -6075,21 +4973,6 @@ if test "$plugins" = "yes" && "for this purpose. You can't build with --static." fi -######################################## -# See if __attribute__((alias)) is supported. -# This false for Xcode 9, but has been remedied for Xcode 10. -# Unfortunately, travis uses Xcode 9 by default. - -attralias=no -cat > $TMPC << EOF -int x = 1; -extern const int y __attribute__((alias("x"))); -int main(void) { return 0; } -EOF -if compile_prog "" "" ; then - attralias=yes -fi - ######################################## # check if getauxval is available. @@ -6273,46 +5156,6 @@ fi ################################################# -# Check to see if we have the Hypervisor framework -if [ "$darwin" = "yes" ] ; then - cat > $TMPC << EOF -#include -int main() { return 0;} -EOF - if ! compile_object ""; then - hvf='no' - else - hvf='yes' - QEMU_LDFLAGS="-framework Hypervisor $QEMU_LDFLAGS" - fi -fi - -################################################# -# Sparc implicitly links with --relax, which is -# incompatible with -r, so --no-relax should be -# given. It does no harm to give it on other -# platforms too. - -# Note: the prototype is needed since QEMU_CFLAGS -# contains -Wmissing-prototypes -cat > $TMPC << EOF -extern int foo(void); -int foo(void) { return 0; } -EOF -if ! compile_object ""; then - error_exit "Failed to compile object file for LD_REL_FLAGS test" -fi -for i in '-Wl,-r -Wl,--no-relax' -Wl,-r -r; do - if do_cc -nostdlib $i -o $TMPMO $TMPO; then - LD_REL_FLAGS=$i - break - fi -done -if test "$modules" = "yes" && test "$LD_REL_FLAGS" = ""; then - feature_not_found "modules" "Cannot find how to build relocatable objects" -fi - -########################################## # check for sysmacros.h have_sysmacros=no @@ -6418,10 +5261,13 @@ fi ########################################## # checks for fuzzer -if test "$fuzzing" = "yes" ; then +if test "$fuzzing" = "yes" && test -z "${LIB_FUZZING_ENGINE+xxx}"; then write_c_fuzzer_skeleton if compile_prog "$CPU_CFLAGS -Werror -fsanitize=fuzzer" ""; then - have_fuzzer=yes + have_fuzzer=yes + else + error_exit "Your compiler doesn't support -fsanitize=fuzzer" + exit 1 fi fi @@ -6457,8 +5303,6 @@ if test "$libpmem" != "no"; then libpmem="yes" libpmem_libs=$($pkg_config --libs libpmem) libpmem_cflags=$($pkg_config --cflags libpmem) - libs_softmmu="$libs_softmmu $libpmem_libs" - QEMU_CFLAGS="$QEMU_CFLAGS $libpmem_cflags" else if test "$libpmem" = "yes" ; then feature_not_found "libpmem" "Install nvml or pmdk" @@ -6475,8 +5319,6 @@ if test "$libdaxctl" != "no"; then libdaxctl="yes" libdaxctl_libs=$($pkg_config --libs libdaxctl) libdaxctl_cflags=$($pkg_config --cflags libdaxctl) - libs_softmmu="$libs_softmmu $libdaxctl_libs" - QEMU_CFLAGS="$QEMU_CFLAGS $libdaxctl_cflags" else if test "$libdaxctl" = "yes" ; then feature_not_found "libdaxctl" "Install libdaxctl" @@ -6488,56 +5330,12 @@ fi ########################################## # check for slirp -# slirp is only required when building softmmu targets -if test -z "$slirp" -a "$softmmu" != "yes" ; then - slirp="no" -fi - case "$slirp" in - "" | yes) - if $pkg_config slirp; then - slirp=system - elif test -e "${source_path}/.git" && test $git_update = 'yes' ; then - slirp=git - elif test -e "${source_path}/slirp/Makefile" ; then - slirp=internal - elif test -z "$slirp" ; then - slirp=no - else - feature_not_found "slirp" "Install slirp devel or git submodule" - fi - ;; - - system) - if ! $pkg_config slirp; then - feature_not_found "slirp" "Install slirp devel" - fi - ;; -esac - -case "$slirp" in - git | internal) - if test "$slirp" = git; then + auto | enabled | internal) + # Simpler to always update submodule, even if not needed. + if test "$git_submodules_action" != "ignore"; then git_submodules="${git_submodules} slirp" fi - mkdir -p slirp - slirp_cflags="-I\$(SRC_PATH)/slirp/src -I\$(BUILD_DIR)/slirp/src" - slirp_libs="-L\$(BUILD_DIR)/slirp -lslirp" - if test "$mingw32" = "yes" ; then - slirp_libs="$slirp_libs -lws2_32 -liphlpapi" - fi - ;; - - system) - slirp_version=$($pkg_config --modversion slirp 2>/dev/null) - slirp_cflags=$($pkg_config --cflags slirp 2>/dev/null) - slirp_libs=$($pkg_config --libs slirp 2>/dev/null) - ;; - - no) - ;; - *) - error_exit "Unknown state for slirp: $slirp" ;; esac @@ -6576,28 +5374,6 @@ but not implemented on your system" fi fi -########################################## -# check for usable keyutils.h - -if test "$linux" = "yes" ; then - - have_keyutils=no - cat > $TMPC << EOF -#include -#include -#include -#include -#include -int main(void) { - return request_key("user", NULL, NULL, 0); -} -EOF - if compile_prog "" "-lkeyutils"; then - have_keyutils=yes - fi -fi - - ########################################## # End of CC checks # After here, no more $cc or $ld runs @@ -6605,15 +5381,22 @@ fi write_c_skeleton if test "$gcov" = "yes" ; then - QEMU_CFLAGS="-fprofile-arcs -ftest-coverage -g $QEMU_CFLAGS" - QEMU_LDFLAGS="-fprofile-arcs -ftest-coverage $QEMU_LDFLAGS" + : elif test "$fortify_source" = "yes" ; then - CFLAGS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 $CFLAGS" + QEMU_CFLAGS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 $QEMU_CFLAGS" + debug=no +fi -# Note: xemu build.sh script will pass in appropriate optimization level -# CFLAGS="-O2 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 $CFLAGS" -# elif test "$debug" = "no"; then -# CFLAGS="-O2 $CFLAGS" +case "$ARCH" in +alpha) + # Ensure there's only a single GP + QEMU_CFLAGS="-msmall-data $QEMU_CFLAGS" +;; +esac + +if test "$gprof" = "yes" ; then + QEMU_CFLAGS="-p $QEMU_CFLAGS" + QEMU_LDFLAGS="-p $QEMU_LDFLAGS" fi if test "$have_asan" = "yes"; then @@ -6643,34 +5426,6 @@ if test "$have_ubsan" = "yes"; then fi ########################################## -# Do we have libnfs -if test "$libnfs" != "no" ; then - if $pkg_config --atleast-version=1.9.3 libnfs; then - libnfs="yes" - libnfs_libs=$($pkg_config --libs libnfs) - else - if test "$libnfs" = "yes" ; then - feature_not_found "libnfs" "Install libnfs devel >= 1.9.3" - fi - libnfs="no" - fi -fi - -########################################## -# Do we have libudev -if test "$libudev" != "no" ; then - if $pkg_config libudev && test "$static" != "yes"; then - libudev="yes" - libudev_libs=$($pkg_config --libs libudev) - else - libudev="no" - fi -fi - -# Now we've finished running tests it's OK to add -Werror to the compiler flags -if test "$werror" = "yes"; then - QEMU_CFLAGS="-Werror $QEMU_CFLAGS" -fi # Exclude --warn-common with TSan to suppress warnings from the TSan libraries. if test "$solaris" = "no" && test "$tsan" = "no"; then @@ -6679,100 +5434,33 @@ if test "$solaris" = "no" && test "$tsan" = "no"; then fi fi -# test if pod2man has --utf8 option -if pod2man --help | grep -q utf8; then - POD2MAN="pod2man --utf8" -else - POD2MAN="pod2man" -fi - # Use ASLR, no-SEH and DEP if available if test "$mingw32" = "yes" ; then - for flag in --dynamicbase --no-seh --nxcompat; do + flags="--no-seh --nxcompat" + + # Disable ASLR for debug builds to allow debugging with gdb + if test "$debug" = "no" ; then + flags="--dynamicbase $flags" + fi + + for flag in $flags; do if ld_has $flag ; then QEMU_LDFLAGS="-Wl,$flag $QEMU_LDFLAGS" fi done fi -# Disable OpenBSD W^X if available -if test "$tcg" = "yes" && test "$targetos" = "OpenBSD"; then - cat > $TMPC <" -fi -echo "spice support $spice $(echo_version $spice $spice_protocol_version/$spice_server_version)" -echo "rbd support $rbd" -echo "xfsctl support $xfs" -echo "smartcard support $smartcard" -echo "libusb $libusb" -echo "usb net redir $usb_redir" -echo "OpenGL support $opengl" -echo "OpenGL dmabufs $opengl_dmabuf" -echo "libiscsi support $libiscsi" -echo "libnfs support $libnfs" -echo "build guest agent $guest_agent" -echo "QGA VSS support $guest_agent_with_vss" -echo "QGA w32 disk info $guest_agent_ntddscsi" -echo "QGA MSI support $guest_agent_msi" -echo "seccomp support $seccomp" -echo "coroutine backend $coroutine" -echo "coroutine pool $coroutine_pool" -echo "debug stack usage $debug_stack_usage" -echo "mutex debugging $debug_mutex" -echo "crypto afalg $crypto_afalg" -echo "GlusterFS support $glusterfs" -echo "gcov $gcov_tool" -echo "gcov enabled $gcov" -echo "TPM support $tpm" -echo "libssh support $libssh" -echo "QOM debugging $qom_cast_debug" -echo "Live block migration $live_block_migration" -echo "lzo support $lzo" -echo "snappy support $snappy" -echo "bzip2 support $bzip2" -echo "lzfse support $lzfse" -echo "zstd support $zstd" -echo "NUMA host support $numa" -echo "libxml2 $libxml2" -echo "tcmalloc support $tcmalloc" -echo "jemalloc support $jemalloc" -echo "avx2 optimization $avx2_opt" -echo "avx512f optimization $avx512f_opt" -echo "replication support $replication" -echo "bochs support $bochs" -echo "cloop support $cloop" -echo "dmg support $dmg" -echo "qcow v1 support $qcow1" -echo "vdi support $vdi" -echo "vvfat support $vvfat" -echo "qed support $qed" -echo "parallels support $parallels" -echo "sheepdog support $sheepdog" -echo "capstone $capstone" -echo "libpmem support $libpmem" -echo "libdaxctl support $libdaxctl" -echo "libudev $libudev" -echo "default devices $default_devices" -echo "plugin support $plugins" -echo "fuzzing support $fuzzing" -echo "gdb $gdb_bin" -echo "rng-none $rng_none" -echo "Linux keyring $secret_keyring" - -if test "$supported_cpu" = "no"; then - echo - echo "WARNING: SUPPORT FOR THIS HOST CPU WILL GO AWAY IN FUTURE RELEASES!" - echo - echo "CPU host architecture $cpu support is not currently maintained." - echo "The QEMU project intends to remove support for this host CPU in" - echo "a future release if nobody volunteers to maintain it and to" - echo "provide a build host for our continuous integration setup." - echo "configure has succeeded and you can continue to build, but" - echo "if you care about QEMU on this platform you should contact" - echo "us upstream at qemu-devel@nongnu.org." -fi - -if test "$supported_os" = "no"; then - echo - echo "WARNING: SUPPORT FOR THIS HOST OS WILL GO AWAY IN FUTURE RELEASES!" - echo - echo "Host OS $targetos support is not currently maintained." - echo "The QEMU project intends to remove support for this host OS in" - echo "a future release if nobody volunteers to maintain it and to" - echo "provide a build host for our continuous integration setup." - echo "configure has succeeded and you can continue to build, but" - echo "if you care about QEMU on this platform you should contact" - echo "us upstream at qemu-devel@nongnu.org." +if !(GIT="$git" "$source_path/scripts/git-submodule.sh" "$git_submodules_action" "$git_submodules"); then + exit 1 fi config_host_mak="config-host.mak" -echo "# Automatically generated by configure - do not modify" >config-all-disas.mak - echo "# Automatically generated by configure - do not modify" > $config_host_mak echo >> $config_host_mak echo all: >> $config_host_mak -echo "prefix=$prefix" >> $config_host_mak -echo "bindir=$bindir" >> $config_host_mak -echo "libdir=$libdir" >> $config_host_mak -echo "libexecdir=$libexecdir" >> $config_host_mak -echo "includedir=$includedir" >> $config_host_mak -echo "mandir=$mandir" >> $config_host_mak -echo "sysconfdir=$sysconfdir" >> $config_host_mak -echo "qemu_confdir=$qemu_confdir" >> $config_host_mak -echo "qemu_datadir=$qemu_datadir" >> $config_host_mak -echo "qemu_firmwarepath=$firmwarepath" >> $config_host_mak -echo "qemu_docdir=$qemu_docdir" >> $config_host_mak -echo "qemu_moddir=$qemu_moddir" >> $config_host_mak -if test "$mingw32" = "no" ; then - echo "qemu_localstatedir=$local_statedir" >> $config_host_mak -fi -echo "qemu_helperdir=$libexecdir" >> $config_host_mak -echo "qemu_localedir=$qemu_localedir" >> $config_host_mak -echo "qemu_icondir=$qemu_icondir" >> $config_host_mak -echo "qemu_desktopdir=$qemu_desktopdir" >> $config_host_mak -echo "libs_cpu=$libs_cpu" >> $config_host_mak -echo "libs_softmmu=$libs_softmmu" >> $config_host_mak echo "GIT=$git" >> $config_host_mak echo "GIT_SUBMODULES=$git_submodules" >> $config_host_mak -echo "GIT_UPDATE=$git_update" >> $config_host_mak +echo "GIT_SUBMODULES_ACTION=$git_submodules_action" >> $config_host_mak echo "ARCH=$ARCH" >> $config_host_mak -echo "GLIB_CFLAGS=$glib_cflags" >> $config_host_mak -echo "GLIB_LDFLAGS=$glib_ldflags" >> $config_host_mak - -if test "$default_devices" = "yes" ; then - echo "CONFIG_MINIKCONF_MODE=--defconfig" >> $config_host_mak -else - echo "CONFIG_MINIKCONF_MODE=--allnoconfig" >> $config_host_mak -fi if test "$debug_tcg" = "yes" ; then echo "CONFIG_DEBUG_TCG=y" >> $config_host_mak fi @@ -7186,15 +5572,6 @@ if test "$bigendian" = "yes" ; then fi if test "$mingw32" = "yes" ; then echo "CONFIG_WIN32=y" >> $config_host_mak - rc_version=$(cat $source_path/VERSION) - version_major=${rc_version%%.*} - rc_version=${rc_version#*.} - version_minor=${rc_version%%.*} - rc_version=${rc_version#*.} - version_subminor=${rc_version%%.*} - version_micro=0 - echo "CONFIG_FILEVERSION=$version_major,$version_minor,$version_subminor,$version_micro" >> $config_host_mak - echo "CONFIG_PRODUCTVERSION=$version_major,$version_minor,$version_subminor,$version_micro" >> $config_host_mak if test "$guest_agent_with_vss" = "yes" ; then echo "CONFIG_QGA_VSS=y" >> $config_host_mak echo "QGA_VSS_PROVIDER=$qga_vss_provider" >> $config_host_mak @@ -7203,15 +5580,10 @@ if test "$mingw32" = "yes" ; then if test "$guest_agent_ntddscsi" = "yes" ; then echo "CONFIG_QGA_NTDDSCSI=y" >> $config_host_mak fi - if test "$guest_agent_msi" = "yes"; then - echo "QEMU_GA_MSI_ENABLED=yes" >> $config_host_mak - echo "QEMU_GA_MSI_MINGW_DLL_PATH=${QEMU_GA_MSI_MINGW_DLL_PATH}" >> $config_host_mak - echo "QEMU_GA_MSI_WITH_VSS=${QEMU_GA_MSI_WITH_VSS}" >> $config_host_mak - echo "QEMU_GA_MSI_ARCH=${QEMU_GA_MSI_ARCH}" >> $config_host_mak - echo "QEMU_GA_MANUFACTURER=${QEMU_GA_MANUFACTURER}" >> $config_host_mak - echo "QEMU_GA_DISTRO=${QEMU_GA_DISTRO}" >> $config_host_mak - echo "QEMU_GA_VERSION=${QEMU_GA_VERSION}" >> $config_host_mak - fi + echo "QEMU_GA_MSI_MINGW_DLL_PATH=${QEMU_GA_MSI_MINGW_DLL_PATH}" >> $config_host_mak + echo "QEMU_GA_MANUFACTURER=${QEMU_GA_MANUFACTURER}" >> $config_host_mak + echo "QEMU_GA_DISTRO=${QEMU_GA_DISTRO}" >> $config_host_mak + echo "QEMU_GA_VERSION=${QEMU_GA_VERSION}" >> $config_host_mak else echo "CONFIG_POSIX=y" >> $config_host_mak fi @@ -7239,15 +5611,10 @@ fi if test "$want_tools" = "yes" ; then echo "CONFIG_TOOLS=y" >> $config_host_mak fi -if test "$slirp" != "no"; then - echo "CONFIG_SLIRP=y" >> $config_host_mak - echo "CONFIG_SMBD_COMMAND=\"$smbd\"" >> $config_host_mak - echo "SLIRP_CFLAGS=$slirp_cflags" >> $config_host_mak - echo "SLIRP_LIBS=$slirp_libs" >> $config_host_mak -fi -if [ "$slirp" = "git" -o "$slirp" = "internal" ]; then - echo "config-host.h: slirp/all" >> $config_host_mak +if test "$guest_agent" = "yes" ; then + echo "CONFIG_GUEST_AGENT=y" >> $config_host_mak fi +echo "CONFIG_SMBD_COMMAND=\"$smbd\"" >> $config_host_mak if test "$vde" = "yes" ; then echo "CONFIG_VDE=y" >> $config_host_mak echo "VDE_LIBS=$vde_libs" >> $config_host_mak @@ -7261,57 +5628,40 @@ fi if test "$gprof" = "yes" ; then echo "CONFIG_GPROF=y" >> $config_host_mak fi -if test "$cap_ng" = "yes" ; then - echo "CONFIG_LIBCAP_NG=y" >> $config_host_mak -fi echo "CONFIG_AUDIO_DRIVERS=$audio_drv_list" >> $config_host_mak for drv in $audio_drv_list; do def=CONFIG_AUDIO_$(echo $drv | LC_ALL=C tr '[a-z]' '[A-Z]') - case "$drv" in - alsa | oss | pa | sdl) - echo "$def=m" >> $config_host_mak ;; - *) - echo "$def=y" >> $config_host_mak ;; - esac + echo "$def=y" >> $config_host_mak done +if test "$alsa" = "yes" ; then + echo "CONFIG_ALSA=y" >> $config_host_mak +fi echo "ALSA_LIBS=$alsa_libs" >> $config_host_mak +echo "ALSA_CFLAGS=$alsa_cflags" >> $config_host_mak +if test "$libpulse" = "yes" ; then + echo "CONFIG_LIBPULSE=y" >> $config_host_mak +fi echo "PULSE_LIBS=$pulse_libs" >> $config_host_mak +echo "PULSE_CFLAGS=$pulse_cflags" >> $config_host_mak echo "COREAUDIO_LIBS=$coreaudio_libs" >> $config_host_mak echo "DSOUND_LIBS=$dsound_libs" >> $config_host_mak echo "OSS_LIBS=$oss_libs" >> $config_host_mak +if test "$libjack" = "yes" ; then + echo "CONFIG_LIBJACK=y" >> $config_host_mak +fi echo "JACK_LIBS=$jack_libs" >> $config_host_mak if test "$audio_win_int" = "yes" ; then echo "CONFIG_AUDIO_WIN_INT=y" >> $config_host_mak fi echo "CONFIG_BDRV_RW_WHITELIST=$block_drv_rw_whitelist" >> $config_host_mak echo "CONFIG_BDRV_RO_WHITELIST=$block_drv_ro_whitelist" >> $config_host_mak -if test "$vnc" = "yes" ; then - echo "CONFIG_VNC=y" >> $config_host_mak -fi -if test "$vnc_sasl" = "yes" ; then - echo "CONFIG_VNC_SASL=y" >> $config_host_mak -fi -if test "$vnc_jpeg" = "yes" ; then - echo "CONFIG_VNC_JPEG=y" >> $config_host_mak -fi -if test "$vnc_png" = "yes" ; then - echo "CONFIG_VNC_PNG=y" >> $config_host_mak -fi -if test "$xkbcommon" = "yes" ; then - echo "XKBCOMMON_CFLAGS=$xkbcommon_cflags" >> $config_host_mak - echo "XKBCOMMON_LIBS=$xkbcommon_libs" >> $config_host_mak -fi if test "$xfs" = "yes" ; then echo "CONFIG_XFS=y" >> $config_host_mak fi qemu_version=$(head $source_path/VERSION) -echo "VERSION=$qemu_version" >>$config_host_mak echo "PKGVERSION=$pkgversion" >>$config_host_mak echo "SRC_PATH=$source_path" >> $config_host_mak echo "TARGET_DIRS=$target_list" >> $config_host_mak -if [ "$docs" = "yes" ] ; then - echo "BUILD_DOCS=yes" >> $config_host_mak -fi if test "$modules" = "yes"; then # $shacmd can generate a hash started with digit, which the compiler doesn't # like as an symbol. So prefix it with an underscore @@ -7321,32 +5671,6 @@ fi if test "$module_upgrades" = "yes"; then echo "CONFIG_MODULE_UPGRADES=y" >> $config_host_mak fi -if test "$have_x11" = "yes" && test "$need_x11" = "yes"; then - echo "CONFIG_X11=y" >> $config_host_mak - echo "X11_CFLAGS=$x11_cflags" >> $config_host_mak - echo "X11_LIBS=$x11_libs" >> $config_host_mak -fi -if test "$sdl" = "yes" ; then - echo "CONFIG_SDL=m" >> $config_host_mak - echo "SDL_CFLAGS=$sdl_cflags" >> $config_host_mak - echo "SDL_LIBS=$sdl_libs" >> $config_host_mak - if test "$sdl_image" = "yes" ; then - echo "CONFIG_SDL_IMAGE=y" >> $config_host_mak - fi -fi -if test "$cocoa" = "yes" ; then - echo "CONFIG_COCOA=y" >> $config_host_mak -fi -if test "$iconv" = "yes" ; then - echo "CONFIG_ICONV=y" >> $config_host_mak - echo "ICONV_CFLAGS=$iconv_cflags" >> $config_host_mak - echo "ICONV_LIBS=$iconv_lib" >> $config_host_mak -fi -if test "$curses" = "yes" ; then - echo "CONFIG_CURSES=m" >> $config_host_mak - echo "CURSES_CFLAGS=$curses_inc" >> $config_host_mak - echo "CURSES_LIBS=$curses_lib" >> $config_host_mak -fi if test "$pipe2" = "yes" ; then echo "CONFIG_PIPE2=y" >> $config_host_mak fi @@ -7413,9 +5737,6 @@ fi if test "$syncfs" = "yes" ; then echo "CONFIG_SYNCFS=y" >> $config_host_mak fi -if test "$kcov" = "yes" ; then - echo "CONFIG_KCOV=y" >> $config_host_mak -fi if test "$inotify" = "yes" ; then echo "CONFIG_INOTIFY=y" >> $config_host_mak fi @@ -7437,23 +5758,6 @@ fi if test "$bswap_h" = "yes" ; then echo "CONFIG_MACHINE_BSWAP_H=y" >> $config_host_mak fi -if test "$curl" = "yes" ; then - echo "CONFIG_CURL=m" >> $config_host_mak - echo "CURL_CFLAGS=$curl_cflags" >> $config_host_mak - echo "CURL_LIBS=$curl_libs" >> $config_host_mak -fi -if test "$brlapi" = "yes" ; then - echo "CONFIG_BRLAPI=y" >> $config_host_mak - echo "BRLAPI_LIBS=$brlapi_libs" >> $config_host_mak -fi -if test "$gtk" = "yes" ; then - echo "CONFIG_GTK=m" >> $config_host_mak - echo "GTK_CFLAGS=$gtk_cflags" >> $config_host_mak - echo "GTK_LIBS=$gtk_libs" >> $config_host_mak - if test "$gtk_gl" = "yes" ; then - echo "CONFIG_GTK_GL=y" >> $config_host_mak - fi -fi if test "$gio" = "yes" ; then echo "CONFIG_GIO=y" >> $config_host_mak echo "GIO_CFLAGS=$gio_cflags" >> $config_host_mak @@ -7463,16 +5767,22 @@ fi echo "CONFIG_TLS_PRIORITY=\"$tls_priority\"" >> $config_host_mak if test "$gnutls" = "yes" ; then echo "CONFIG_GNUTLS=y" >> $config_host_mak + echo "GNUTLS_CFLAGS=$gnutls_cflags" >> $config_host_mak + echo "GNUTLS_LIBS=$gnutls_libs" >> $config_host_mak fi if test "$gcrypt" = "yes" ; then echo "CONFIG_GCRYPT=y" >> $config_host_mak if test "$gcrypt_hmac" = "yes" ; then echo "CONFIG_GCRYPT_HMAC=y" >> $config_host_mak fi + echo "GCRYPT_CFLAGS=$gcrypt_cflags" >> $config_host_mak + echo "GCRYPT_LIBS=$gcrypt_libs" >> $config_host_mak fi if test "$nettle" = "yes" ; then echo "CONFIG_NETTLE=y" >> $config_host_mak echo "CONFIG_NETTLE_VERSION_MAJOR=${nettle_version%%.*}" >> $config_host_mak + echo "NETTLE_CFLAGS=$nettle_cflags" >> $config_host_mak + echo "NETTLE_LIBS=$nettle_libs" >> $config_host_mak fi if test "$qemu_private_xts" = "yes" ; then echo "CONFIG_QEMU_PRIVATE_XTS=y" >> $config_host_mak @@ -7483,21 +5793,12 @@ fi if test "$auth_pam" = "yes" ; then echo "CONFIG_AUTH_PAM=y" >> $config_host_mak fi -if test "$have_ifaddrs_h" = "yes" ; then - echo "HAVE_IFADDRS_H=y" >> $config_host_mak -fi -if test "$have_drm_h" = "yes" ; then - echo "HAVE_DRM_H=y" >> $config_host_mak -fi if test "$have_broken_size_max" = "yes" ; then echo "HAVE_BROKEN_SIZE_MAX=y" >> $config_host_mak fi if test "$have_openpty" = "yes" ; then echo "HAVE_OPENPTY=y" >> $config_host_mak fi -if test "$have_sys_signal_h" = "yes" ; then - echo "HAVE_SYS_SIGNAL_H=y" >> $config_host_mak -fi # Work around a system header bug with some kernel/XFS header # versions where they both try to define 'struct fsxattr': @@ -7519,9 +5820,11 @@ if test "$virglrenderer" = "yes" ; then echo "VIRGL_CFLAGS=$virgl_cflags" >> $config_host_mak echo "VIRGL_LIBS=$virgl_libs" >> $config_host_mak fi -if test "$xen" = "yes" ; then +if test "$xen" = "enabled" ; then echo "CONFIG_XEN_BACKEND=y" >> $config_host_mak echo "CONFIG_XEN_CTRL_INTERFACE_VERSION=$xen_ctrl_version" >> $config_host_mak + echo "XEN_CFLAGS=$xen_cflags" >> $config_host_mak + echo "XEN_LIBS=$xen_libs" >> $config_host_mak fi if test "$linux_aio" = "yes" ; then echo "CONFIG_LINUX_AIO=y" >> $config_host_mak @@ -7531,21 +5834,6 @@ if test "$linux_io_uring" = "yes" ; then echo "LINUX_IO_URING_CFLAGS=$linux_io_uring_cflags" >> $config_host_mak echo "LINUX_IO_URING_LIBS=$linux_io_uring_libs" >> $config_host_mak fi -if test "$attr" = "yes" ; then - echo "CONFIG_ATTR=y" >> $config_host_mak -fi -if test "$libattr" = "yes" ; then - echo "CONFIG_LIBATTR=y" >> $config_host_mak -fi -if test "$virtfs" = "yes" ; then - echo "CONFIG_VIRTFS=y" >> $config_host_mak -fi -if test "$mpath" = "yes" ; then - echo "CONFIG_MPATH=y" >> $config_host_mak - if test "$mpathpersist_new_api" = "yes"; then - echo "CONFIG_MPATH_NEW_API=y" >> $config_host_mak - fi -fi if test "$vhost_scsi" = "yes" ; then echo "CONFIG_VHOST_SCSI=y" >> $config_host_mak fi @@ -7579,18 +5867,9 @@ fi if test "$vhost_user_fs" = "yes" ; then echo "CONFIG_VHOST_USER_FS=y" >> $config_host_mak fi -if test "$blobs" = "yes" ; then - echo "INSTALL_BLOBS=yes" >> $config_host_mak -fi if test "$iovec" = "yes" ; then echo "CONFIG_IOVEC=y" >> $config_host_mak fi -if test "$preadv" = "yes" ; then - echo "CONFIG_PREADV=y" >> $config_host_mak -fi -if test "$fdt" != "no" ; then - echo "CONFIG_FDT=y" >> $config_host_mak -fi if test "$membarrier" = "yes" ; then echo "CONFIG_MEMBARRIER=y" >> $config_host_mak fi @@ -7600,12 +5879,6 @@ fi if test "$optreset" = "yes" ; then echo "HAVE_OPTRESET=y" >> $config_host_mak fi -if test "$tcg" = "yes"; then - echo "CONFIG_TCG=y" >> $config_host_mak - if test "$tcg_interpreter" = "yes" ; then - echo "CONFIG_TCG_INTERPRETER=y" >> $config_host_mak - fi -fi if test "$fdatasync" = "yes" ; then echo "CONFIG_FDATASYNC=y" >> $config_host_mak fi @@ -7618,9 +5891,10 @@ fi if test "$posix_memalign" = "yes" ; then echo "CONFIG_POSIX_MEMALIGN=y" >> $config_host_mak fi - if test "$spice" = "yes" ; then echo "CONFIG_SPICE=y" >> $config_host_mak + echo "SPICE_CFLAGS=$spice_cflags" >> $config_host_mak + echo "SPICE_LIBS=$spice_libs" >> $config_host_mak fi if test "$smartcard" = "yes" ; then @@ -7643,10 +5917,8 @@ fi if test "$opengl" = "yes" ; then echo "CONFIG_OPENGL=y" >> $config_host_mak + echo "OPENGL_CFLAGS=$opengl_cflags" >> $config_host_mak echo "OPENGL_LIBS=$opengl_libs" >> $config_host_mak - if test "$opengl_dmabuf" = "yes" ; then - echo "CONFIG_OPENGL_DMABUF=y" >> $config_host_mak - fi fi if test "$gbm" = "yes" ; then @@ -7656,10 +5928,6 @@ if test "$gbm" = "yes" ; then fi -if test "$malloc_trim" = "yes" ; then - echo "CONFIG_MALLOC_TRIM=y" >> $config_host_mak -fi - if test "$avx2_opt" = "yes" ; then echo "CONFIG_AVX2_OPT=y" >> $config_host_mak fi @@ -7668,61 +5936,14 @@ if test "$avx512f_opt" = "yes" ; then echo "CONFIG_AVX512F_OPT=y" >> $config_host_mak fi -if test "$lzo" = "yes" ; then - echo "CONFIG_LZO=y" >> $config_host_mak -fi - -if test "$snappy" = "yes" ; then - echo "CONFIG_SNAPPY=y" >> $config_host_mak -fi - -if test "$bzip2" = "yes" ; then - echo "CONFIG_BZIP2=y" >> $config_host_mak - echo "BZIP2_LIBS=-lbz2" >> $config_host_mak -fi - -if test "$lzfse" = "yes" ; then - echo "CONFIG_LZFSE=y" >> $config_host_mak - echo "LZFSE_LIBS=-llzfse" >> $config_host_mak -fi - -if test "$zstd" = "yes" ; then - echo "CONFIG_ZSTD=y" >> $config_host_mak -fi - -if test "$libiscsi" = "yes" ; then - echo "CONFIG_LIBISCSI=m" >> $config_host_mak - echo "LIBISCSI_CFLAGS=$libiscsi_cflags" >> $config_host_mak - echo "LIBISCSI_LIBS=$libiscsi_libs" >> $config_host_mak -fi - -if test "$libnfs" = "yes" ; then - echo "CONFIG_LIBNFS=m" >> $config_host_mak - echo "LIBNFS_LIBS=$libnfs_libs" >> $config_host_mak -fi - -if test "$seccomp" = "yes"; then - echo "CONFIG_SECCOMP=y" >> $config_host_mak - echo "SECCOMP_CFLAGS=$seccomp_cflags" >> $config_host_mak - echo "SECCOMP_LIBS=$seccomp_libs" >> $config_host_mak -fi - # XXX: suppress that if [ "$bsd" = "yes" ] ; then echo "CONFIG_BSD=y" >> $config_host_mak fi -if test "$localtime_r" = "yes" ; then - echo "CONFIG_LOCALTIME_R=y" >> $config_host_mak -fi if test "$qom_cast_debug" = "yes" ; then echo "CONFIG_QOM_CAST_DEBUG=y" >> $config_host_mak fi -if test "$rbd" = "yes" ; then - echo "CONFIG_RBD=m" >> $config_host_mak - echo "RBD_CFLAGS=$rbd_cflags" >> $config_host_mak - echo "RBD_LIBS=$rbd_libs" >> $config_host_mak -fi echo "CONFIG_COROUTINE_BACKEND=$coroutine" >> $config_host_mak if test "$coroutine_pool" = "yes" ; then @@ -7783,46 +6004,12 @@ if test "$atomic64" = "yes" ; then echo "CONFIG_ATOMIC64=y" >> $config_host_mak fi -if test "$attralias" = "yes" ; then - echo "CONFIG_ATTRIBUTE_ALIAS=y" >> $config_host_mak -fi - if test "$getauxval" = "yes" ; then echo "CONFIG_GETAUXVAL=y" >> $config_host_mak fi -if test "$glusterfs" = "yes" ; then - echo "CONFIG_GLUSTERFS=m" >> $config_host_mak - echo "GLUSTERFS_CFLAGS=$glusterfs_cflags" >> $config_host_mak - echo "GLUSTERFS_LIBS=$glusterfs_libs" >> $config_host_mak -fi - -if test "$glusterfs_xlator_opt" = "yes" ; then - echo "CONFIG_GLUSTERFS_XLATOR_OPT=y" >> $config_host_mak -fi - -if test "$glusterfs_discard" = "yes" ; then - echo "CONFIG_GLUSTERFS_DISCARD=y" >> $config_host_mak -fi - -if test "$glusterfs_fallocate" = "yes" ; then - echo "CONFIG_GLUSTERFS_FALLOCATE=y" >> $config_host_mak -fi - -if test "$glusterfs_zerofill" = "yes" ; then - echo "CONFIG_GLUSTERFS_ZEROFILL=y" >> $config_host_mak -fi - -if test "$glusterfs_ftruncate_has_stat" = "yes" ; then - echo "CONFIG_GLUSTERFS_FTRUNCATE_HAS_STAT=y" >> $config_host_mak -fi - -if test "$glusterfs_iocb_has_stat" = "yes" ; then - echo "CONFIG_GLUSTERFS_IOCB_HAS_STAT=y" >> $config_host_mak -fi - if test "$libssh" = "yes" ; then - echo "CONFIG_LIBSSH=m" >> $config_host_mak + echo "CONFIG_LIBSSH=y" >> $config_host_mak echo "LIBSSH_CFLAGS=$libssh_cflags" >> $config_host_mak echo "LIBSSH_LIBS=$libssh_libs" >> $config_host_mak fi @@ -7849,6 +6036,8 @@ if have_backend "log"; then fi if have_backend "ust"; then echo "CONFIG_TRACE_UST=y" >> $config_host_mak + echo "LTTNG_UST_LIBS=$lttng_ust_libs" >> $config_host_mak + echo "URCU_BP_LIBS=$urcu_bp_libs" >> $config_host_mak fi if have_backend "dtrace"; then echo "CONFIG_TRACE_DTRACE=y" >> $config_host_mak @@ -7916,9 +6105,6 @@ fi if test "$ivshmem" = "yes" ; then echo "CONFIG_IVSHMEM=y" >> $config_host_mak fi -if test "$capstone" != "no" ; then - echo "CONFIG_CAPSTONE=y" >> $config_host_mak -fi if test "$debug_mutex" = "yes" ; then echo "CONFIG_DEBUG_MUTEX=y" >> $config_host_mak fi @@ -7938,10 +6124,13 @@ fi if test "$libpmem" = "yes" ; then echo "CONFIG_LIBPMEM=y" >> $config_host_mak + echo "LIBPMEM_LIBS=$libpmem_libs" >> $config_host_mak + echo "LIBPMEM_CFLAGS=$libpmem_cflags" >> $config_host_mak fi if test "$libdaxctl" = "yes" ; then echo "CONFIG_LIBDAXCTL=y" >> $config_host_mak + echo "LIBDAXCTL_LIBS=$libdaxctl_libs" >> $config_host_mak fi if test "$bochs" = "yes" ; then @@ -7969,28 +6158,35 @@ if test "$parallels" = "yes" ; then echo "CONFIG_PARALLELS=y" >> $config_host_mak fi if test "$sheepdog" = "yes" ; then + add_to deprecated_features "sheepdog" echo "CONFIG_SHEEPDOG=y" >> $config_host_mak fi -if test "$pty_h" = "yes" ; then - echo "HAVE_PTY_H=y" >> $config_host_mak -fi if test "$have_mlockall" = "yes" ; then echo "HAVE_MLOCKALL=y" >> $config_host_mak fi if test "$fuzzing" = "yes" ; then - if test "$have_fuzzer" = "yes"; then - FUZZ_LDFLAGS=" -fsanitize=fuzzer" - FUZZ_CFLAGS=" -fsanitize=fuzzer" - CFLAGS="$CFLAGS -fsanitize=fuzzer-no-link" + # If LIB_FUZZING_ENGINE is set, assume we are running on OSS-Fuzz, and the + # needed CFLAGS have already been provided + if test -z "${LIB_FUZZING_ENGINE+xxx}" ; then + # Add CFLAGS to tell clang to add fuzzer-related instrumentation to all the + # compiled code. + QEMU_CFLAGS="$QEMU_CFLAGS -fsanitize=fuzzer-no-link" + # To build non-fuzzer binaries with --enable-fuzzing, link everything with + # fsanitize=fuzzer-no-link. Otherwise, the linker will be unable to bind + # the fuzzer-related callbacks added by instrumentation. + QEMU_LDFLAGS="$QEMU_LDFLAGS -fsanitize=fuzzer-no-link" + # For the actual fuzzer binaries, we need to link against the libfuzzer + # library. Provide the flags for doing this in FUZZ_EXE_LDFLAGS. The meson + # rule for the fuzzer adds these to the link_args. They need to be + # configurable, to support OSS-Fuzz + FUZZ_EXE_LDFLAGS="-fsanitize=fuzzer" else - error_exit "Your compiler doesn't support -fsanitize=fuzzer" - exit 1 + FUZZ_EXE_LDFLAGS="$LIB_FUZZING_ENGINE" fi fi if test "$plugins" = "yes" ; then echo "CONFIG_PLUGIN=y" >> $config_host_mak - LIBS="-ldl $LIBS" # Copy the export object list to the build dir if test "$ld_dynamic_list" = "yes" ; then echo "CONFIG_HAS_LD_DYNAMIC_LIST=yes" >> $config_host_mak @@ -8009,53 +6205,27 @@ if test "$plugins" = "yes" ; then fi fi -if test -n "$gdb_bin" ; then - echo "HAVE_GDB_BIN=$gdb_bin" >> $config_host_mak +if test -n "$gdb_bin"; then + gdb_version=$($gdb_bin --version | head -n 1) + if version_ge ${gdb_version##* } 9.1; then + echo "HAVE_GDB_BIN=$gdb_bin" >> $config_host_mak + fi fi if test "$secret_keyring" = "yes" ; then echo "CONFIG_SECRET_KEYRING=y" >> $config_host_mak - if test "$have_keyutils" = "yes" ; then - echo "CONFIG_TEST_SECRET_KEYRING=y" >> $config_host_mak - fi fi -if test "$tcg_interpreter" = "yes"; then - QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/tci $QEMU_INCLUDES" -elif test "$ARCH" = "sparc64" ; then - QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/sparc $QEMU_INCLUDES" -elif test "$ARCH" = "s390x" ; then - QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/s390 $QEMU_INCLUDES" -elif test "$ARCH" = "x86_64" || test "$ARCH" = "x32" ; then - QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/i386 $QEMU_INCLUDES" -elif test "$ARCH" = "ppc64" ; then - QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/ppc $QEMU_INCLUDES" -elif test "$ARCH" = "riscv32" || test "$ARCH" = "riscv64" ; then - QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/riscv $QEMU_INCLUDES" -else - QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/\$(ARCH) $QEMU_INCLUDES" -fi - -echo "HELPERS=$helpers" >> $config_host_mak -echo "TOOLS=$tools" >> $config_host_mak echo "ROMS=$roms" >> $config_host_mak echo "MAKE=$make" >> $config_host_mak -echo "INSTALL=$install" >> $config_host_mak -echo "INSTALL_DIR=$install -d -m 0755" >> $config_host_mak -echo "INSTALL_DATA=$install -c -m 0644" >> $config_host_mak -echo "INSTALL_PROG=$install -c -m 0755" >> $config_host_mak -echo "INSTALL_LIB=$install -c -m 0644" >> $config_host_mak echo "PYTHON=$python" >> $config_host_mak -echo "SPHINX_BUILD=$sphinx_build" >> $config_host_mak -echo "SPHINX_WERROR=$sphinx_werror" >> $config_host_mak echo "GENISOIMAGE=$genisoimage" >> $config_host_mak -echo "EFI_AARCH64=$efi_aarch64" >> $config_host_mak -echo "PYTHON_YAML=$python_yaml" >> $config_host_mak +echo "MESON=$meson" >> $config_host_mak +echo "NINJA=$ninja" >> $config_host_mak echo "CC=$cc" >> $config_host_mak if $iasl -h > /dev/null 2>&1; then - echo "IASL=$iasl" >> $config_host_mak + echo "CONFIG_IASL=$iasl" >> $config_host_mak fi -echo "HOST_CC=$host_cc" >> $config_host_mak echo "CXX=$cxx" >> $config_host_mak echo "OBJCC=$objcc" >> $config_host_mak echo "AR=$ar" >> $config_host_mak @@ -8069,49 +6239,26 @@ echo "RANLIB=$ranlib" >> $config_host_mak echo "NM=$nm" >> $config_host_mak echo "PKG_CONFIG=$pkg_config_exe" >> $config_host_mak echo "WINDRES=$windres" >> $config_host_mak -echo "CFLAGS=$CFLAGS" >> $config_host_mak echo "CFLAGS_NOPIE=$CFLAGS_NOPIE" >> $config_host_mak echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak echo "QEMU_CXXFLAGS=$QEMU_CXXFLAGS" >> $config_host_mak -echo "QEMU_INCLUDES=$QEMU_INCLUDES" >> $config_host_mak -if test "$sparse" = "yes" ; then - echo "CC := REAL_CC=\"\$(CC)\" cgcc" >> $config_host_mak - echo "CPP := REAL_CC=\"\$(CPP)\" cgcc" >> $config_host_mak - echo "CXX := REAL_CC=\"\$(CXX)\" cgcc" >> $config_host_mak - echo "HOST_CC := REAL_CC=\"\$(HOST_CC)\" cgcc" >> $config_host_mak - echo "QEMU_CFLAGS += -Wbitwise -Wno-transparent-union -Wno-old-initializer -Wno-non-pointer-null" >> $config_host_mak -fi +echo "GLIB_CFLAGS=$glib_cflags" >> $config_host_mak +echo "GLIB_LIBS=$glib_libs" >> $config_host_mak echo "QEMU_LDFLAGS=$QEMU_LDFLAGS" >> $config_host_mak -echo "LD_REL_FLAGS=$LD_REL_FLAGS" >> $config_host_mak echo "LD_I386_EMULATION=$ld_i386_emulation" >> $config_host_mak -echo "LIBS+=$LIBS" >> $config_host_mak -echo "LIBS_TOOLS+=$libs_tools" >> $config_host_mak -echo "PTHREAD_LIB=$PTHREAD_LIB" >> $config_host_mak echo "EXESUF=$EXESUF" >> $config_host_mak -echo "DSOSUF=$DSOSUF" >> $config_host_mak -echo "LDFLAGS_SHARED=$LDFLAGS_SHARED" >> $config_host_mak -echo "LIBS_QGA+=$libs_qga" >> $config_host_mak +echo "HOST_DSOSUF=$HOST_DSOSUF" >> $config_host_mak +echo "LIBS_QGA=$libs_qga" >> $config_host_mak echo "TASN1_LIBS=$tasn1_libs" >> $config_host_mak echo "TASN1_CFLAGS=$tasn1_cflags" >> $config_host_mak -echo "POD2MAN=$POD2MAN" >> $config_host_mak if test "$gcov" = "yes" ; then echo "CONFIG_GCOV=y" >> $config_host_mak - echo "GCOV=$gcov_tool" >> $config_host_mak fi -if test "$libudev" != "no"; then - echo "CONFIG_LIBUDEV=y" >> $config_host_mak - echo "LIBUDEV_LIBS=$libudev_libs" >> $config_host_mak -fi if test "$fuzzing" != "no"; then echo "CONFIG_FUZZ=y" >> $config_host_mak - echo "FUZZ_CFLAGS=$FUZZ_CFLAGS" >> $config_host_mak - echo "FUZZ_LDFLAGS=$FUZZ_LDFLAGS" >> $config_host_mak -fi - -if test "$edk2_blobs" = "yes" ; then - echo "DECOMPRESS_EDK2_BLOBS=y" >> $config_host_mak fi +echo "FUZZ_EXE_LDFLAGS=$FUZZ_EXE_LDFLAGS" >> $config_host_mak if test "$rng_none" = "yes"; then echo "CONFIG_RNG_NONE=y" >> $config_host_mak @@ -8148,449 +6295,23 @@ if test "$linux" = "yes" ; then fi for target in $target_list; do -target_dir="$target" -config_target_mak=$target_dir/config-target.mak -target_name=$(echo $target | cut -d '-' -f 1) -target_aligned_only="no" -case "$target_name" in - alpha|hppa|mips64el|mips64|mipsel|mips|mipsn32|mipsn32el|sh4|sh4eb|sparc|sparc64|sparc32plus|xtensa|xtensaeb) - target_aligned_only="yes" - ;; -esac -target_bigendian="no" -case "$target_name" in - armeb|aarch64_be|hppa|lm32|m68k|microblaze|mips|mipsn32|mips64|moxie|or1k|ppc|ppc64|ppc64abi32|s390x|sh4eb|sparc|sparc64|sparc32plus|xtensaeb) - target_bigendian="yes" - ;; -esac -target_softmmu="no" -target_user_only="no" -target_linux_user="no" -target_bsd_user="no" -case "$target" in - ${target_name}-softmmu) - target_softmmu="yes" - ;; - ${target_name}-linux-user) - target_user_only="yes" - target_linux_user="yes" - ;; - ${target_name}-bsd-user) - target_user_only="yes" - target_bsd_user="yes" - ;; - *) - error_exit "Target '$target' not recognised" - exit 1 - ;; -esac - -mkdir -p $target_dir -echo "# Automatically generated by configure - do not modify" > $config_target_mak - -bflt="no" -mttcg="no" -interp_prefix1=$(echo "$interp_prefix" | sed "s/%M/$target_name/g") -gdb_xml_files="" - -TARGET_ARCH="$target_name" -TARGET_BASE_ARCH="" -TARGET_ABI_DIR="" - -case "$target_name" in - i386) - mttcg="yes" - gdb_xml_files="i386-32bit.xml" - TARGET_SYSTBL_ABI=i386 - ;; - x86_64) - TARGET_BASE_ARCH=i386 - TARGET_SYSTBL_ABI=common,64 - mttcg="yes" - gdb_xml_files="i386-64bit.xml" - ;; - alpha) - mttcg="yes" - TARGET_SYSTBL_ABI=common - ;; - arm|armeb) - TARGET_ARCH=arm - TARGET_SYSTBL_ABI=common,oabi - bflt="yes" - mttcg="yes" - gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml arm-m-profile.xml" - ;; - aarch64|aarch64_be) - TARGET_ARCH=aarch64 - TARGET_BASE_ARCH=arm - bflt="yes" - mttcg="yes" - gdb_xml_files="aarch64-core.xml aarch64-fpu.xml arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml arm-m-profile.xml" - ;; - avr) - gdb_xml_files="avr-cpu.xml" - target_compiler=$cross_cc_avr - ;; - cris) - ;; - hppa) - mttcg="yes" - TARGET_SYSTBL_ABI=common,32 - ;; - lm32) - ;; - m68k) - bflt="yes" - gdb_xml_files="cf-core.xml cf-fp.xml m68k-core.xml m68k-fp.xml" - TARGET_SYSTBL_ABI=common - ;; - microblaze|microblazeel) - TARGET_ARCH=microblaze - TARGET_SYSTBL_ABI=common - bflt="yes" - echo "TARGET_ABI32=y" >> $config_target_mak - ;; - mips|mipsel) - mttcg="yes" - TARGET_ARCH=mips - echo "TARGET_ABI_MIPSO32=y" >> $config_target_mak - TARGET_SYSTBL_ABI=o32 - ;; - mipsn32|mipsn32el) - mttcg="yes" - TARGET_ARCH=mips64 - TARGET_BASE_ARCH=mips - echo "TARGET_ABI_MIPSN32=y" >> $config_target_mak - echo "TARGET_ABI32=y" >> $config_target_mak - TARGET_SYSTBL_ABI=n32 - ;; - mips64|mips64el) - mttcg="no" - TARGET_ARCH=mips64 - TARGET_BASE_ARCH=mips - echo "TARGET_ABI_MIPSN64=y" >> $config_target_mak - TARGET_SYSTBL_ABI=n64 - ;; - moxie) - ;; - nios2) - ;; - or1k) - TARGET_ARCH=openrisc - TARGET_BASE_ARCH=openrisc - ;; - ppc) - gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml" - TARGET_SYSTBL_ABI=common,nospu,32 - ;; - ppc64) - TARGET_BASE_ARCH=ppc - TARGET_ABI_DIR=ppc - TARGET_SYSTBL_ABI=common,nospu,64 - mttcg=yes - gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml" - ;; - ppc64le) - TARGET_ARCH=ppc64 - TARGET_BASE_ARCH=ppc - TARGET_ABI_DIR=ppc - TARGET_SYSTBL_ABI=common,nospu,64 - mttcg=yes - gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml" - ;; - ppc64abi32) - TARGET_ARCH=ppc64 - TARGET_BASE_ARCH=ppc - TARGET_ABI_DIR=ppc - TARGET_SYSTBL_ABI=common,nospu,32 - echo "TARGET_ABI32=y" >> $config_target_mak - gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml" - ;; - riscv32) - TARGET_BASE_ARCH=riscv - TARGET_ABI_DIR=riscv - mttcg=yes - gdb_xml_files="riscv-32bit-cpu.xml riscv-32bit-fpu.xml riscv-64bit-fpu.xml riscv-32bit-csr.xml riscv-32bit-virtual.xml" - ;; - riscv64) - TARGET_BASE_ARCH=riscv - TARGET_ABI_DIR=riscv - mttcg=yes - gdb_xml_files="riscv-64bit-cpu.xml riscv-32bit-fpu.xml riscv-64bit-fpu.xml riscv-64bit-csr.xml riscv-64bit-virtual.xml" - ;; - rx) - TARGET_ARCH=rx - bflt="yes" - target_compiler=$cross_cc_rx - gdb_xml_files="rx-core.xml" - ;; - sh4|sh4eb) - TARGET_ARCH=sh4 - TARGET_SYSTBL_ABI=common - bflt="yes" - ;; - sparc) - TARGET_SYSTBL_ABI=common,32 - ;; - sparc64) - TARGET_BASE_ARCH=sparc - TARGET_SYSTBL_ABI=common,64 - ;; - sparc32plus) - TARGET_ARCH=sparc64 - TARGET_BASE_ARCH=sparc - TARGET_ABI_DIR=sparc - TARGET_SYSTBL_ABI=common,32 - echo "TARGET_ABI32=y" >> $config_target_mak - ;; - s390x) - TARGET_SYSTBL_ABI=common,64 - mttcg=yes - gdb_xml_files="s390x-core64.xml s390-acr.xml s390-fpr.xml s390-vx.xml s390-cr.xml s390-virt.xml s390-gs.xml" - ;; - tilegx) - ;; - tricore) - ;; - unicore32) - ;; - xtensa|xtensaeb) - TARGET_ARCH=xtensa - TARGET_SYSTBL_ABI=common - bflt="yes" - mttcg="yes" - ;; - *) - error_exit "Unsupported target CPU" - ;; -esac -# TARGET_BASE_ARCH needs to be defined after TARGET_ARCH -if [ "$TARGET_BASE_ARCH" = "" ]; then - TARGET_BASE_ARCH=$TARGET_ARCH -fi - -symlink "$source_path/Makefile.target" "$target_dir/Makefile" - -upper() { - echo "$@"| LC_ALL=C tr '[a-z]' '[A-Z]' -} - -target_arch_name="$(upper $TARGET_ARCH)" -echo "TARGET_$target_arch_name=y" >> $config_target_mak -echo "TARGET_NAME=$target_name" >> $config_target_mak -echo "TARGET_BASE_ARCH=$TARGET_BASE_ARCH" >> $config_target_mak -if [ "$TARGET_ABI_DIR" = "" ]; then - TARGET_ABI_DIR=$TARGET_ARCH -fi -echo "TARGET_ABI_DIR=$TARGET_ABI_DIR" >> $config_target_mak -if [ "$HOST_VARIANT_DIR" != "" ]; then - echo "HOST_VARIANT_DIR=$HOST_VARIANT_DIR" >> $config_target_mak -fi -if [ "$TARGET_SYSTBL_ABI" != "" ]; then - echo "TARGET_SYSTBL_ABI=$TARGET_SYSTBL_ABI" >> $config_target_mak -fi - -if supported_xen_target $target; then - echo "CONFIG_XEN=y" >> $config_target_mak - echo "$target/config-devices.mak: CONFIG_XEN=y" >> $config_host_mak - if test "$xen_pci_passthrough" = yes; then - echo "CONFIG_XEN_PCI_PASSTHROUGH=y" >> "$config_target_mak" - fi -else - echo "$target/config-devices.mak: CONFIG_XEN=n" >> $config_host_mak -fi -if supported_kvm_target $target; then - echo "CONFIG_KVM=y" >> $config_target_mak - echo "$target/config-devices.mak: CONFIG_KVM=y" >> $config_host_mak -else - echo "$target/config-devices.mak: CONFIG_KVM=n" >> $config_host_mak -fi -if supported_hax_target $target; then - echo "CONFIG_HAX=y" >> $config_target_mak -fi -if supported_hvf_target $target; then - echo "CONFIG_HVF=y" >> $config_target_mak -fi -if supported_whpx_target $target; then - echo "CONFIG_WHPX=y" >> $config_target_mak -fi -if test "$target_aligned_only" = "yes" ; then - echo "TARGET_ALIGNED_ONLY=y" >> $config_target_mak -fi -if test "$target_bigendian" = "yes" ; then - echo "TARGET_WORDS_BIGENDIAN=y" >> $config_target_mak -fi -if test "$target_softmmu" = "yes" ; then - echo "CONFIG_SOFTMMU=y" >> $config_target_mak - if test "$mttcg" = "yes" ; then - echo "TARGET_SUPPORTS_MTTCG=y" >> $config_target_mak - fi -fi -if test "$target_user_only" = "yes" ; then - echo "CONFIG_USER_ONLY=y" >> $config_target_mak - echo "CONFIG_QEMU_INTERP_PREFIX=\"$interp_prefix1\"" >> $config_target_mak -fi -if test "$target_linux_user" = "yes" ; then - echo "CONFIG_LINUX_USER=y" >> $config_target_mak -fi -list="" -if test ! -z "$gdb_xml_files" ; then - for x in $gdb_xml_files; do - list="$list $source_path/gdb-xml/$x" - done - echo "TARGET_XML_FILES=$list" >> $config_target_mak -fi - -if test "$target_user_only" = "yes" && test "$bflt" = "yes"; then - echo "TARGET_HAS_BFLT=y" >> $config_target_mak -fi -if test "$target_bsd_user" = "yes" ; then - echo "CONFIG_BSD_USER=y" >> $config_target_mak -fi - - -# generate QEMU_CFLAGS/QEMU_LDFLAGS for targets - -cflags="" -ldflags="" - -disas_config() { - echo "CONFIG_${1}_DIS=y" >> $config_target_mak - echo "CONFIG_${1}_DIS=y" >> config-all-disas.mak -} - -for i in $ARCH $TARGET_BASE_ARCH ; do - case "$i" in - alpha) - disas_config "ALPHA" - ;; - aarch64) - if test -n "${cxx}"; then - disas_config "ARM_A64" - fi - ;; - arm) - disas_config "ARM" - if test -n "${cxx}"; then - disas_config "ARM_A64" - fi - ;; - avr) - disas_config "AVR" - ;; - cris) - disas_config "CRIS" - ;; - hppa) - disas_config "HPPA" - ;; - i386|x86_64|x32) - disas_config "I386" - ;; - lm32) - disas_config "LM32" - ;; - m68k) - disas_config "M68K" - ;; - microblaze*) - disas_config "MICROBLAZE" - ;; - mips*) - disas_config "MIPS" - if test -n "${cxx}"; then - disas_config "NANOMIPS" - fi - ;; - moxie*) - disas_config "MOXIE" - ;; - nios2) - disas_config "NIOS2" - ;; - or1k) - disas_config "OPENRISC" - ;; - ppc*) - disas_config "PPC" - ;; - riscv*) - disas_config "RISCV" - ;; - rx) - disas_config "RX" - ;; - s390*) - disas_config "S390" - ;; - sh4) - disas_config "SH4" - ;; - sparc*) - disas_config "SPARC" - ;; - xtensa*) - disas_config "XTENSA" - ;; - esac + target_dir="$target" + target_name=$(echo $target | cut -d '-' -f 1) + mkdir -p $target_dir + case $target in + *-user) symlink "../qemu-$target_name" "$target_dir/qemu-$target_name" ;; + *) symlink "../qemu-system-$target_name" "$target_dir/qemu-system-$target_name" ;; + esac done -if test "$tcg_interpreter" = "yes" ; then - disas_config "TCI" -fi -case "$ARCH" in -alpha) - # Ensure there's only a single GP - cflags="-msmall-data $cflags" -;; -esac - -if test "$gprof" = "yes" ; then - if test "$target_linux_user" = "yes" ; then - cflags="-p $cflags" - ldflags="-p $ldflags" - fi - if test "$target_softmmu" = "yes" ; then - ldflags="-p $ldflags" - echo "GPROF_CFLAGS=-p" >> $config_target_mak - fi -fi - -# Newer kernels on s390 check for an S390_PGSTE program header and -# enable the pgste page table extensions in that case. This makes -# the vm.allocate_pgste sysctl unnecessary. We enable this program -# header if -# - we build on s390x -# - we build the system emulation for s390x (qemu-system-s390x) -# - KVM is enabled -# - the linker supports --s390-pgste -if test "$TARGET_ARCH" = "s390x" && test "$target_softmmu" = "yes" && \ - test "$ARCH" = "s390x" && test "$kvm" = "yes"; then - if ld_has --s390-pgste ; then - ldflags="-Wl,--s390-pgste $ldflags" - fi -fi - -echo "QEMU_LDFLAGS+=$ldflags" >> $config_target_mak -echo "QEMU_CFLAGS+=$cflags" >> $config_target_mak - -done # for target in $targets - -echo "PIXMAN_CFLAGS=$pixman_cflags" >> $config_host_mak -echo "PIXMAN_LIBS=$pixman_libs" >> $config_host_mak - -if [ "$fdt" = "git" ]; then - echo "config-host.h: dtc/all" >> $config_host_mak -fi -if [ "$capstone" = "git" -o "$capstone" = "internal" ]; then - echo "config-host.h: capstone/all" >> $config_host_mak -fi -if test -n "$LIBCAPSTONE"; then - echo "LIBCAPSTONE=$LIBCAPSTONE" >> $config_host_mak +echo "CONFIG_QEMU_INTERP_PREFIX=$interp_prefix" | sed 's/%M/@0@/' >> $config_host_mak +if test "$default_targets" = "yes"; then + echo "CONFIG_DEFAULT_TARGETS=y" >> $config_host_mak fi if test "$numa" = "yes"; then echo "CONFIG_NUMA=y" >> $config_host_mak + echo "NUMA_LIBS=$numa_libs" >> $config_host_mak fi if test "$ccache_cpp2" = "yes"; then @@ -8611,16 +6332,19 @@ fi # so the build tree will be missing the link back to the new file, and # tests might fail. Prefer to keep the relevant files in their own # directory and symlink the directory instead. +# UNLINK is used to remove symlinks from older development versions +# that might get into the way when doing "git update" without doing +# a "make distclean" in between. DIRS="tests tests/tcg tests/tcg/lm32 tests/qapi-schema tests/qtest/libqos" DIRS="$DIRS tests/qtest tests/qemu-iotests tests/vm tests/fp tests/qgraph" DIRS="$DIRS docs docs/interop fsdev scsi" DIRS="$DIRS pc-bios/optionrom pc-bios/s390-ccw" DIRS="$DIRS roms/seabios" +DIRS="$DIRS contrib/plugins/" LINKS="Makefile" -LINKS="$LINKS tests/tcg/lm32/Makefile po/Makefile" -LINKS="$LINKS tests/tcg/Makefile.target tests/fp/Makefile" -LINKS="$LINKS tests/plugin/Makefile" -LINKS="$LINKS pc-bios/optionrom/Makefile pc-bios/keymaps" +LINKS="$LINKS tests/tcg/lm32/Makefile" +LINKS="$LINKS tests/tcg/Makefile.target" +LINKS="$LINKS pc-bios/optionrom/Makefile" LINKS="$LINKS pc-bios/s390-ccw/Makefile" LINKS="$LINKS roms/seabios/Makefile" LINKS="$LINKS pc-bios/qemu-icon.bmp" @@ -8628,8 +6352,11 @@ LINKS="$LINKS .gdbinit scripts" # scripts needed by relative path in .gdbinit LINKS="$LINKS tests/acceptance tests/data" LINKS="$LINKS tests/qemu-iotests/check" LINKS="$LINKS python" +LINKS="$LINKS contrib/plugins/Makefile " +UNLINK="pc-bios/keymaps" for bios_file in \ $source_path/pc-bios/*.bin \ + $source_path/pc-bios/*.elf \ $source_path/pc-bios/*.lid \ $source_path/pc-bios/*.rom \ $source_path/pc-bios/*.dtb \ @@ -8643,15 +6370,20 @@ do done mkdir -p $DIRS for f in $LINKS ; do - if [ -e "$source_path/$f" ] && [ "$pwd_is_source_path" != "y" ]; then + if [ -e "$source_path/$f" ]; then symlink "$source_path/$f" "$f" fi done +for f in $UNLINK ; do + if [ -L "$f" ]; then + rm -f "$f" + fi +done (for i in $cross_cc_vars; do export $i done -export target_list source_path use_containers +export target_list source_path use_containers ARCH $source_path/tests/tcg/configure.sh) # temporary config to build submodules @@ -8670,12 +6402,127 @@ for rom in seabios; do echo "RANLIB=$ranlib" >> $config_mak done -# set up qemu-iotests in this build directory -iotests_common_env="tests/qemu-iotests/common.env" +if test "$skip_meson" = no; then +cross="config-meson.cross.new" +meson_quote() { + echo "'$(echo $* | sed "s/ /','/g")'" +} -echo "# Automatically generated by configure - do not modify" > "$iotests_common_env" -echo >> "$iotests_common_env" -echo "export PYTHON='$python'" >> "$iotests_common_env" +echo "# Automatically generated by configure - do not modify" > $cross +echo "[properties]" >> $cross +test -z "$cxx" && echo "link_language = 'c'" >> $cross +echo "[built-in options]" >> $cross +echo "c_args = [${CFLAGS:+$(meson_quote $CFLAGS)}]" >> $cross +echo "cpp_args = [${CXXFLAGS:+$(meson_quote $CXXFLAGS)}]" >> $cross +echo "c_link_args = [${LDFLAGS:+$(meson_quote $LDFLAGS)}]" >> $cross +echo "cpp_link_args = [${LDFLAGS:+$(meson_quote $LDFLAGS)}]" >> $cross +echo "[binaries]" >> $cross +echo "c = [$(meson_quote $cc)]" >> $cross +test -n "$cxx" && echo "cpp = [$(meson_quote $cxx)]" >> $cross +test -n "$objcc" && echo "objc = [$(meson_quote $objcc)]" >> $cross +echo "ar = [$(meson_quote $ar)]" >> $cross +echo "nm = [$(meson_quote $nm)]" >> $cross +echo "pkgconfig = [$(meson_quote $pkg_config_exe)]" >> $cross +echo "ranlib = [$(meson_quote $ranlib)]" >> $cross +if has $sdl2_config; then + echo "sdl2-config = [$(meson_quote $sdl2_config)]" >> $cross +fi +echo "strip = [$(meson_quote $strip)]" >> $cross +echo "windres = [$(meson_quote $windres)]" >> $cross +if test "$cross_compile" = "yes"; then + cross_arg="--cross-file config-meson.cross" + echo "[host_machine]" >> $cross + if test "$mingw32" = "yes" ; then + echo "system = 'windows'" >> $cross + fi + if test "$linux" = "yes" ; then + echo "system = 'linux'" >> $cross + fi + if test "$darwin" = "yes" ; then + echo "system = 'darwin'" >> $cross + fi + case "$ARCH" in + i386) + echo "cpu_family = 'x86'" >> $cross + ;; + x86_64) + echo "cpu_family = 'x86_64'" >> $cross + ;; + ppc64le) + echo "cpu_family = 'ppc64'" >> $cross + ;; + *) + echo "cpu_family = '$ARCH'" >> $cross + ;; + esac + echo "cpu = '$cpu'" >> $cross + if test "$bigendian" = "yes" ; then + echo "endian = 'big'" >> $cross + else + echo "endian = 'little'" >> $cross + fi +else + cross_arg="--native-file config-meson.cross" +fi +mv $cross config-meson.cross + +rm -rf meson-private meson-info meson-logs +unset staticpic +if ! version_ge "$($meson --version)" 0.56.0; then + staticpic=$(if test "$pie" = yes; then echo true; else echo false; fi) +fi +NINJA=$ninja $meson setup \ + --prefix "$prefix" \ + --libdir "$libdir" \ + --libexecdir "$libexecdir" \ + --bindir "$bindir" \ + --includedir "$includedir" \ + --datadir "$datadir" \ + --mandir "$mandir" \ + --sysconfdir "$sysconfdir" \ + --localedir "$localedir" \ + --localstatedir "$local_statedir" \ + -Ddocdir="$docdir" \ + -Dqemu_firmwarepath="$firmwarepath" \ + -Dqemu_suffix="$qemu_suffix" \ + -Doptimization=$(if test "$debug" = yes; then echo 0; else echo 2; fi) \ + -Ddebug=$(if test "$debug_info" = yes; then echo true; else echo false; fi) \ + -Dwerror=$(if test "$werror" = yes; then echo true; else echo false; fi) \ + -Dstrip=$(if test "$strip_opt" = yes; then echo true; else echo false; fi) \ + -Db_pie=$(if test "$pie" = yes; then echo true; else echo false; fi) \ + ${staticpic:+-Db_staticpic=$staticpic} \ + -Db_coverage=$(if test "$gcov" = yes; then echo true; else echo false; fi) \ + -Db_lto=$lto -Dcfi=$cfi -Dcfi_debug=$cfi_debug \ + -Dmalloc=$malloc -Dmalloc_trim=$malloc_trim -Dsparse=$sparse \ + -Dkvm=$kvm -Dhax=$hax -Dwhpx=$whpx -Dhvf=$hvf \ + -Dxen=$xen -Dxen_pci_passthrough=$xen_pci_passthrough -Dtcg=$tcg \ + -Dcocoa=$cocoa -Dgtk=$gtk -Dmpath=$mpath -Dsdl=$sdl -Dsdl_image=$sdl_image \ + -Dvnc=$vnc -Dvnc_sasl=$vnc_sasl -Dvnc_jpeg=$vnc_jpeg -Dvnc_png=$vnc_png \ + -Dgettext=$gettext -Dxkbcommon=$xkbcommon -Du2f=$u2f -Dvirtiofsd=$virtiofsd \ + -Dcapstone=$capstone -Dslirp=$slirp -Dfdt=$fdt -Dbrlapi=$brlapi \ + -Dcurl=$curl -Dglusterfs=$glusterfs -Dbzip2=$bzip2 -Dlibiscsi=$libiscsi \ + -Dlibnfs=$libnfs -Diconv=$iconv -Dcurses=$curses -Dlibudev=$libudev\ + -Drbd=$rbd -Dlzo=$lzo -Dsnappy=$snappy -Dlzfse=$lzfse \ + -Dzstd=$zstd -Dseccomp=$seccomp -Dvirtfs=$virtfs -Dcap_ng=$cap_ng \ + -Dattr=$attr -Ddefault_devices=$default_devices \ + -Ddocs=$docs -Dsphinx_build=$sphinx_build -Dinstall_blobs=$blobs \ + -Dvhost_user_blk_server=$vhost_user_blk_server -Dmultiprocess=$multiprocess \ + -Dfuse=$fuse -Dfuse_lseek=$fuse_lseek -Dguest_agent_msi=$guest_agent_msi \ + $(if test "$default_features" = no; then echo "-Dauto_features=disabled"; fi) \ + -Dtcg_interpreter=$tcg_interpreter \ + $cross_arg \ + "$PWD" "$source_path" + +if test "$?" -ne 0 ; then + error_exit "meson setup failed" +fi +fi + +if test -n "${deprecated_features}"; then + echo "Warning, deprecated features enabled." + echo "Please see docs/system/deprecated.rst" + echo " features: ${deprecated_features}" +fi # Save the configure command line for later reuse. cat <config.status @@ -8725,7 +6572,9 @@ preserve_env STRIP preserve_env WINDRES printf "exec" >>config.status -printf " '%s'" "$0" "$@" >>config.status +for i in "$0" "$@"; do + test "$i" = --skip-meson || printf " %s" "$(quote_sh "$i")" >>config.status +done echo ' "$@"' >>config.status chmod +x config.status diff --git a/contrib/elf2dmp/Makefile.objs b/contrib/elf2dmp/Makefile.objs deleted file mode 100644 index 1505716916..0000000000 --- a/contrib/elf2dmp/Makefile.objs +++ /dev/null @@ -1,4 +0,0 @@ -elf2dmp-obj-y = main.o addrspace.o download.o pdb.o qemu_elf.o - -download.o-cflags := $(CURL_CFLAGS) -download.o-libs := $(CURL_LIBS) diff --git a/contrib/elf2dmp/addrspace.c b/contrib/elf2dmp/addrspace.c index 8a76069cb5..53ded17061 100644 --- a/contrib/elf2dmp/addrspace.c +++ b/contrib/elf2dmp/addrspace.c @@ -207,8 +207,8 @@ 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 & PFN_MASK; - size_t s = (page + PAGE_SIZE) - addr; + uint64_t page = addr & ELF2DMP_PFN_MASK; + size_t s = (page + ELF2DMP_PAGE_SIZE) - addr; void *ptr; s = (s > size) ? size : s; diff --git a/contrib/elf2dmp/addrspace.h b/contrib/elf2dmp/addrspace.h index d87f6a18c6..00b44c1218 100644 --- a/contrib/elf2dmp/addrspace.h +++ b/contrib/elf2dmp/addrspace.h @@ -10,9 +10,9 @@ #include "qemu_elf.h" -#define PAGE_BITS 12 -#define PAGE_SIZE (1ULL << PAGE_BITS) -#define PFN_MASK (~(PAGE_SIZE - 1)) +#define ELF2DMP_PAGE_BITS 12 +#define ELF2DMP_PAGE_SIZE (1ULL << ELF2DMP_PAGE_BITS) +#define ELF2DMP_PFN_MASK (~(ELF2DMP_PAGE_SIZE - 1)) #define INVALID_PA UINT64_MAX diff --git a/contrib/elf2dmp/main.c b/contrib/elf2dmp/main.c index 9a2dbc2902..20b477d582 100644 --- a/contrib/elf2dmp/main.c +++ b/contrib/elf2dmp/main.c @@ -244,8 +244,8 @@ static int fill_header(WinDumpHeader64 *hdr, struct pa_space *ps, WinDumpHeader64 h; size_t i; - QEMU_BUILD_BUG_ON(KUSD_OFFSET_SUITE_MASK >= PAGE_SIZE); - QEMU_BUILD_BUG_ON(KUSD_OFFSET_PRODUCT_TYPE >= PAGE_SIZE); + QEMU_BUILD_BUG_ON(KUSD_OFFSET_SUITE_MASK >= ELF2DMP_PAGE_SIZE); + QEMU_BUILD_BUG_ON(KUSD_OFFSET_PRODUCT_TYPE >= ELF2DMP_PAGE_SIZE); if (!suite_mask || !product_type) { return 1; @@ -281,14 +281,14 @@ static int fill_header(WinDumpHeader64 *hdr, struct pa_space *ps, }; for (i = 0; i < ps->block_nr; i++) { - h.PhysicalMemoryBlock.NumberOfPages += ps->block[i].size / PAGE_SIZE; + h.PhysicalMemoryBlock.NumberOfPages += ps->block[i].size / ELF2DMP_PAGE_SIZE; h.PhysicalMemoryBlock.Run[i] = (WinDumpPhyMemRun64) { - .BasePage = ps->block[i].paddr / PAGE_SIZE, - .PageCount = ps->block[i].size / PAGE_SIZE, + .BasePage = ps->block[i].paddr / ELF2DMP_PAGE_SIZE, + .PageCount = ps->block[i].size / ELF2DMP_PAGE_SIZE, }; } - h.RequiredDumpSpace += h.PhysicalMemoryBlock.NumberOfPages << PAGE_BITS; + h.RequiredDumpSpace += h.PhysicalMemoryBlock.NumberOfPages << ELF2DMP_PAGE_BITS; *hdr = h; @@ -379,7 +379,7 @@ static int pe_get_pdb_symstore_hash(uint64_t base, void *start_addr, size_t pdb_name_sz; size_t i; - QEMU_BUILD_BUG_ON(sizeof(*dos_hdr) >= PAGE_SIZE); + QEMU_BUILD_BUG_ON(sizeof(*dos_hdr) >= ELF2DMP_PAGE_SIZE); if (memcmp(&dos_hdr->e_magic, e_magic, sizeof(e_magic))) { return 1; @@ -509,10 +509,10 @@ int main(int argc, char *argv[]) } printf("CPU #0 IDT[0] -> 0x%016"PRIx64"\n", idt_desc_addr(first_idt_desc)); - KernBase = idt_desc_addr(first_idt_desc) & ~(PAGE_SIZE - 1); + KernBase = idt_desc_addr(first_idt_desc) & ~(ELF2DMP_PAGE_SIZE - 1); printf("Searching kernel downwards from 0x%016"PRIx64"...\n", KernBase); - for (; KernBase >= 0xfffff78000000000; KernBase -= PAGE_SIZE) { + for (; KernBase >= 0xfffff78000000000; KernBase -= ELF2DMP_PAGE_SIZE) { nt_start_addr = va_space_resolve(&vs, KernBase); if (!nt_start_addr) { continue; @@ -568,12 +568,12 @@ int main(int argc, char *argv[]) if (fill_header(&header, &ps, &vs, KdDebuggerDataBlock, kdbg, KdVersionBlock, qemu_elf.state_nr)) { err = 1; - goto out_pdb; + goto out_kdbg; } if (fill_context(kdbg, &vs, &qemu_elf)) { err = 1; - goto out_pdb; + goto out_kdbg; } if (write_dump(&ps, &header, argv[2])) { diff --git a/contrib/elf2dmp/meson.build b/contrib/elf2dmp/meson.build new file mode 100644 index 0000000000..4d86cb390a --- /dev/null +++ b/contrib/elf2dmp/meson.build @@ -0,0 +1,5 @@ +if curl.found() + executable('elf2dmp', files('main.c', 'addrspace.c', 'download.c', 'pdb.c', 'qemu_elf.c'), + dependencies: [glib, curl], + install: true) +endif diff --git a/contrib/elf2dmp/pdb.c b/contrib/elf2dmp/pdb.c index a5bd40c99d..b3a6547068 100644 --- a/contrib/elf2dmp/pdb.c +++ b/contrib/elf2dmp/pdb.c @@ -285,6 +285,7 @@ int pdb_init_from_file(const char *name, struct pdb_reader *reader) 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 1; } diff --git a/contrib/elf2dmp/qemu_elf.c b/contrib/elf2dmp/qemu_elf.c index 0db7816586..b601b6d7ba 100644 --- a/contrib/elf2dmp/qemu_elf.c +++ b/contrib/elf2dmp/qemu_elf.c @@ -126,6 +126,7 @@ int QEMU_Elf_init(QEMU_Elf *qe, const char *filename) qe->gmf = g_mapped_file_new(filename, TRUE, &gerr); if (gerr) { eprintf("Failed to map ELF dump file \'%s\'\n", filename); + g_error_free(gerr); return 1; } diff --git a/contrib/gitdm/domain-map b/contrib/gitdm/domain-map index dd79147c76..0074da618f 100644 --- a/contrib/gitdm/domain-map +++ b/contrib/gitdm/domain-map @@ -5,10 +5,13 @@ # amd.com AMD +baidu.com Baidu +bytedance.com ByteDance cmss.chinamobile.com China Mobile citrix.com Citrix -greensocs.com GreenSocs fujitsu.com Fujitsu +google.com Google +greensocs.com GreenSocs huawei.com Huawei ibm.com IBM igalia.com Igalia @@ -16,13 +19,18 @@ intel.com Intel linaro.org Linaro microsoft.com Microsoft nokia.com Nokia +nuviainc.com NUVIA oracle.com Oracle proxmox.com Proxmox +quicinc.com Qualcomm Innovation Center redhat.com Red Hat rt-rk.com RT-RK siemens.com Siemens sifive.com SiFive +suse.com SUSE suse.de SUSE virtuozzo.com Virtuozzo wdc.com Western Digital xilinx.com Xilinx +yadro.com YADRO +yandex-team.ru Yandex diff --git a/contrib/gitdm/filetypes.txt b/contrib/gitdm/filetypes.txt index 2d5002fea0..d2d6f6db8d 100644 --- a/contrib/gitdm/filetypes.txt +++ b/contrib/gitdm/filetypes.txt @@ -22,7 +22,7 @@ # in the gitdm sample-config directory. # # This file contains associations parameters regarding filetypes -# (documentation, develompent, multimedia, images...) +# (documentation, development, multimedia, images...) # # format: # filetype [] @@ -42,7 +42,7 @@ order build,interface,tests,code,documentation,devel-doc,blobs # (most common languages first # filetype code \.c$ # C -filetype code \.inc.c$ # C +filetype code \.c.inc$ # C filetype code \.C$ # C++ filetype code \.cpp$ # C++ filetype code \.c\+\+$ # C++ @@ -59,8 +59,8 @@ filetype code \.s$ # Assembly filetype code \.S$ # Assembly filetype code \.asm$ # Assembly filetype code \.awk$ # awk -filetype code ^common$ # script fragements -filetype code ^common.*$ # script fragements +filetype code ^common$ # script fragments +filetype code ^common.*$ # script fragments filetype code (qom|qmp)-\w+$ # python script fragments # diff --git a/contrib/gitdm/group-map-academics b/contrib/gitdm/group-map-academics index 08f9d81d13..bf3c894821 100644 --- a/contrib/gitdm/group-map-academics +++ b/contrib/gitdm/group-map-academics @@ -12,3 +12,7 @@ ispras.ru # Columbia University cs.columbia.edu cota@braap.org + +uni-paderborn.de +edu +edu.cn diff --git a/contrib/gitdm/group-map-individuals b/contrib/gitdm/group-map-individuals index cf8a2ce367..36bbb77c39 100644 --- a/contrib/gitdm/group-map-individuals +++ b/contrib/gitdm/group-map-individuals @@ -16,3 +16,16 @@ aurelien@aurel32.net balaton@eik.bme.hu e.emanuelegiuseppe@gmail.com andrew.smirnov@gmail.com +sw@weilnetz.de +deller@gmx.de +fthain@telegraphics.com.au +vr_qemu@t-online.de +nieklinnenbank@gmail.com +devnexen@gmail.com +pauldzim@gmail.com +ani@anisinha.ca +sundeep.lkml@gmail.com +mrolnik@gmail.com +huth@tuxfamily.org +jhogan@kernel.org +atar4qemu@gmail.com diff --git a/contrib/gitdm/group-map-redhat b/contrib/gitdm/group-map-redhat index d15db2d35e..02507b7b53 100644 --- a/contrib/gitdm/group-map-redhat +++ b/contrib/gitdm/group-map-redhat @@ -6,3 +6,4 @@ david@gibson.dropbear.id.au laurent@vivier.eu pjp@fedoraproject.org armbru@pond.sub.org +nirsof@gmail.com diff --git a/contrib/ivshmem-client/Makefile.objs b/contrib/ivshmem-client/Makefile.objs deleted file mode 100644 index bfab2d20dd..0000000000 --- a/contrib/ivshmem-client/Makefile.objs +++ /dev/null @@ -1 +0,0 @@ -ivshmem-client-obj-y = ivshmem-client.o main.o diff --git a/contrib/ivshmem-client/ivshmem-client.c b/contrib/ivshmem-client/ivshmem-client.c index b1274b236a..182c79d27c 100644 --- a/contrib/ivshmem-client/ivshmem-client.c +++ b/contrib/ivshmem-client/ivshmem-client.c @@ -178,7 +178,7 @@ ivshmem_client_init(IvshmemClient *client, const char *unix_sock_path, int ivshmem_client_connect(IvshmemClient *client) { - struct sockaddr_un sun; + struct sockaddr_un s_un; int fd, ret; int64_t tmp; @@ -192,16 +192,16 @@ ivshmem_client_connect(IvshmemClient *client) return -1; } - sun.sun_family = AF_UNIX; - ret = snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", + s_un.sun_family = AF_UNIX; + ret = snprintf(s_un.sun_path, sizeof(s_un.sun_path), "%s", client->unix_sock_path); - if (ret < 0 || ret >= sizeof(sun.sun_path)) { + if (ret < 0 || ret >= sizeof(s_un.sun_path)) { IVSHMEM_CLIENT_DEBUG(client, "could not copy unix socket path\n"); goto err_close; } - if (connect(client->sock_fd, (struct sockaddr *)&sun, sizeof(sun)) < 0) { - IVSHMEM_CLIENT_DEBUG(client, "cannot connect to %s: %s\n", sun.sun_path, + if (connect(client->sock_fd, (struct sockaddr *)&s_un, sizeof(s_un)) < 0) { + IVSHMEM_CLIENT_DEBUG(client, "cannot connect to %s: %s\n", s_un.sun_path, strerror(errno)); goto err_close; } diff --git a/contrib/ivshmem-client/ivshmem-client.h b/contrib/ivshmem-client/ivshmem-client.h index fe3cc4a03d..fc45a38060 100644 --- a/contrib/ivshmem-client/ivshmem-client.h +++ b/contrib/ivshmem-client/ivshmem-client.h @@ -174,7 +174,7 @@ int ivshmem_client_notify_all_vects(const IvshmemClient *client, const IvshmemClientPeer *peer); /** - * Broadcat a notification to all vectors of all peers + * Broadcast a notification to all vectors of all peers * * @client: The ivshmem client * diff --git a/contrib/ivshmem-client/meson.build b/contrib/ivshmem-client/meson.build new file mode 100644 index 0000000000..1b171efb4f --- /dev/null +++ b/contrib/ivshmem-client/meson.build @@ -0,0 +1,4 @@ +executable('ivshmem-client', files('ivshmem-client.c', 'main.c'), + dependencies: glib, + build_by_default: targetos == 'linux', + install: false) diff --git a/contrib/ivshmem-server/Makefile.objs b/contrib/ivshmem-server/Makefile.objs deleted file mode 100644 index c060dd3698..0000000000 --- a/contrib/ivshmem-server/Makefile.objs +++ /dev/null @@ -1 +0,0 @@ -ivshmem-server-obj-y = ivshmem-server.o main.o diff --git a/contrib/ivshmem-server/ivshmem-server.c b/contrib/ivshmem-server/ivshmem-server.c index 88daee812d..39a6ffdb5d 100644 --- a/contrib/ivshmem-server/ivshmem-server.c +++ b/contrib/ivshmem-server/ivshmem-server.c @@ -288,7 +288,7 @@ ivshmem_server_init(IvshmemServer *server, const char *unix_sock_path, int ivshmem_server_start(IvshmemServer *server) { - struct sockaddr_un sun; + struct sockaddr_un s_un; int shm_fd, sock_fd, ret; /* open shm file */ @@ -327,15 +327,15 @@ ivshmem_server_start(IvshmemServer *server) goto err_close_shm; } - sun.sun_family = AF_UNIX; - ret = snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", + s_un.sun_family = AF_UNIX; + ret = snprintf(s_un.sun_path, sizeof(s_un.sun_path), "%s", server->unix_sock_path); - if (ret < 0 || ret >= sizeof(sun.sun_path)) { + if (ret < 0 || ret >= sizeof(s_un.sun_path)) { IVSHMEM_SERVER_DEBUG(server, "could not copy unix socket path\n"); goto err_close_sock; } - if (bind(sock_fd, (struct sockaddr *)&sun, sizeof(sun)) < 0) { - IVSHMEM_SERVER_DEBUG(server, "cannot connect to %s: %s\n", sun.sun_path, + if (bind(sock_fd, (struct sockaddr *)&s_un, sizeof(s_un)) < 0) { + IVSHMEM_SERVER_DEBUG(server, "cannot connect to %s: %s\n", s_un.sun_path, strerror(errno)); goto err_close_sock; } diff --git a/contrib/ivshmem-server/main.c b/contrib/ivshmem-server/main.c index ee08c4ced0..224dbeb547 100644 --- a/contrib/ivshmem-server/main.c +++ b/contrib/ivshmem-server/main.c @@ -17,7 +17,7 @@ #define IVSHMEM_SERVER_DEFAULT_PID_FILE "/var/run/ivshmem-server.pid" #define IVSHMEM_SERVER_DEFAULT_UNIX_SOCK_PATH "/tmp/ivshmem_socket" #define IVSHMEM_SERVER_DEFAULT_SHM_PATH "ivshmem" -#define IVSHMEM_SERVER_DEFAULT_SHM_SIZE (4*1024*1024) +#define IVSHMEM_SERVER_DEFAULT_SHM_SIZE (4 * 1024 * 1024) #define IVSHMEM_SERVER_DEFAULT_N_VECTORS 1 /* used to quit on signal SIGTERM */ diff --git a/contrib/ivshmem-server/meson.build b/contrib/ivshmem-server/meson.build new file mode 100644 index 0000000000..3a53942201 --- /dev/null +++ b/contrib/ivshmem-server/meson.build @@ -0,0 +1,4 @@ +executable('ivshmem-server', files('ivshmem-server.c', 'main.c'), + dependencies: [qemuutil, rt], + build_by_default: targetos == 'linux', + install: false) diff --git a/contrib/libvhost-user/Makefile.objs b/contrib/libvhost-user/Makefile.objs deleted file mode 100644 index ef3778edd4..0000000000 --- a/contrib/libvhost-user/Makefile.objs +++ /dev/null @@ -1 +0,0 @@ -libvhost-user-obj-y += libvhost-user.o libvhost-user-glib.o diff --git a/tests/plugin/Makefile b/contrib/plugins/Makefile similarity index 91% rename from tests/plugin/Makefile rename to contrib/plugins/Makefile index e9348fde4a..b9d7935e5e 100644 --- a/tests/plugin/Makefile +++ b/contrib/plugins/Makefile @@ -10,17 +10,14 @@ BUILD_DIR := $(CURDIR)/../.. include $(BUILD_DIR)/config-host.mak -VPATH += $(SRC_PATH)/tests/plugin +VPATH += $(SRC_PATH)/contrib/plugins NAMES := -NAMES += bb -NAMES += empty -NAMES += insn -NAMES += mem NAMES += hotblocks -NAMES += howvec NAMES += hotpages +NAMES += howvec NAMES += lockstep +NAMES += hwprofile SONAMES := $(addsuffix .so,$(addprefix lib,$(NAMES))) diff --git a/tests/plugin/hotblocks.c b/contrib/plugins/hotblocks.c similarity index 96% rename from tests/plugin/hotblocks.c rename to contrib/plugins/hotblocks.c index 3942a2ca54..4b08340143 100644 --- a/tests/plugin/hotblocks.c +++ b/contrib/plugins/hotblocks.c @@ -63,7 +63,7 @@ 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, "%#016"PRIx64", %d, %ld, %"PRId64"\n", + g_string_append_printf(report, "0x%016"PRIx64", %d, %ld, %"PRId64"\n", rec->start_addr, rec->trans_count, rec->insns, rec->exec_count); } @@ -102,7 +102,7 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) { ExecCount *cnt; uint64_t pc = qemu_plugin_tb_vaddr(tb); - unsigned long insns = qemu_plugin_tb_n_insns(tb); + size_t insns = qemu_plugin_tb_n_insns(tb); uint64_t hash = pc ^ insns; g_mutex_lock(&lock); diff --git a/tests/plugin/hotpages.c b/contrib/plugins/hotpages.c similarity index 97% rename from tests/plugin/hotpages.c rename to contrib/plugins/hotpages.c index ecd6c18732..bf53267532 100644 --- a/tests/plugin/hotpages.c +++ b/contrib/plugins/hotpages.c @@ -88,7 +88,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) for (i = 0; i < limit && it->next; i++, it = it->next) { PageCounters *rec = (PageCounters *) it->data; g_string_append_printf(report, - "%#016"PRIx64", 0x%04x, %"PRId64 + "0x%016"PRIx64", 0x%04x, %"PRId64 ", 0x%04x, %"PRId64"\n", rec->page_address, rec->cpu_read, rec->reads, @@ -122,7 +122,7 @@ static void vcpu_haddr(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, } } else { if (hwaddr && !qemu_plugin_hwaddr_is_io(hwaddr)) { - page = (uint64_t) qemu_plugin_hwaddr_device_offset(hwaddr); + page = (uint64_t) qemu_plugin_hwaddr_phys_addr(hwaddr); } else { page = vaddr; } diff --git a/tests/plugin/howvec.c b/contrib/plugins/howvec.c similarity index 95% rename from tests/plugin/howvec.c rename to contrib/plugins/howvec.c index 3b9a6939f2..600f7facc1 100644 --- a/tests/plugin/howvec.c +++ b/contrib/plugins/howvec.c @@ -68,7 +68,7 @@ static InsnClassExecCount aarch64_insn_classes[] = { { "Reserved", "res", 0x1e000000, 0x00000000, COUNT_CLASS}, /* Data Processing Immediate */ { " PCrel addr", "pcrel", 0x1f000000, 0x10000000, COUNT_CLASS}, - { " Add/Sub (imm,tags)","asit", 0x1f800000, 0x11800000, COUNT_CLASS}, + { " Add/Sub (imm,tags)", "asit", 0x1f800000, 0x11800000, COUNT_CLASS}, { " Add/Sub (imm)", "asi", 0x1f000000, 0x11000000, COUNT_CLASS}, { " Logical (imm)", "logi", 0x1f800000, 0x12000000, COUNT_CLASS}, { " Move Wide (imm)", "movwi", 0x1f800000, 0x12800000, COUNT_CLASS}, @@ -91,17 +91,17 @@ static InsnClassExecCount aarch64_insn_classes[] = { { "Branches", "branch", 0x1c000000, 0x14000000, COUNT_CLASS}, /* Loads and Stores */ { " AdvSimd ldstmult", "advlsm", 0xbfbf0000, 0x0c000000, COUNT_CLASS}, - { " AdvSimd ldstmult++","advlsmp",0xbfb00000, 0x0c800000, COUNT_CLASS}, + { " AdvSimd ldstmult++", "advlsmp", 0xbfb00000, 0x0c800000, COUNT_CLASS}, { " AdvSimd ldst", "advlss", 0xbf9f0000, 0x0d000000, COUNT_CLASS}, - { " AdvSimd ldst++", "advlssp",0xbf800000, 0x0d800000, COUNT_CLASS}, + { " AdvSimd ldst++", "advlssp", 0xbf800000, 0x0d800000, COUNT_CLASS}, { " ldst excl", "ldstx", 0x3f000000, 0x08000000, COUNT_CLASS}, { " Prefetch", "prfm", 0xff000000, 0xd8000000, COUNT_CLASS}, { " Load Reg (lit)", "ldlit", 0x1b000000, 0x18000000, COUNT_CLASS}, - { " ldst noalloc pair", "ldstnap",0x3b800000, 0x28000000, COUNT_CLASS}, + { " ldst noalloc pair", "ldstnap", 0x3b800000, 0x28000000, COUNT_CLASS}, { " ldst pair", "ldstp", 0x38000000, 0x28000000, COUNT_CLASS}, { " ldst reg", "ldstr", 0x3b200000, 0x38000000, COUNT_CLASS}, { " Atomic ldst", "atomic", 0x3b200c00, 0x38200000, COUNT_CLASS}, - { " ldst reg (reg off)","ldstro", 0x3b200b00, 0x38200800, COUNT_CLASS}, + { " ldst reg (reg off)", "ldstro", 0x3b200b00, 0x38200800, COUNT_CLASS}, { " ldst reg (pac)", "ldstpa", 0x3b200200, 0x38200800, COUNT_CLASS}, { " ldst reg (imm)", "ldsti", 0x3b000000, 0x39000000, COUNT_CLASS}, { "Loads & Stores", "ldst", 0x0a000000, 0x08000000, COUNT_CLASS}, @@ -145,8 +145,7 @@ typedef struct { int table_sz; } ClassSelector; -static ClassSelector class_tables[] = -{ +static ClassSelector class_tables[] = { { "aarch64", aarch64_insn_classes, ARRAY_SIZE(aarch64_insn_classes) }, { "sparc", sparc32_insn_classes, ARRAY_SIZE(sparc32_insn_classes) }, { "sparc64", sparc64_insn_classes, ARRAY_SIZE(sparc64_insn_classes) }, @@ -202,14 +201,14 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) counts = g_hash_table_get_values(insns); if (counts && g_list_next(counts)) { - g_string_append_printf(report,"Individual Instructions:\n"); + g_string_append_printf(report, "Individual Instructions:\n"); counts = g_list_sort(counts, cmp_exec_count); for (i = 0; i < limit && g_list_next(counts); i++, counts = g_list_next(counts)) { InsnExecCount *rec = (InsnExecCount *) counts->data; g_string_append_printf(report, - "Instr: %-24s\t(%ld hits)\t(op=%#08x/%s)\n", + "Instr: %-24s\t(%ld hits)\t(op=0x%08x/%s)\n", rec->insn, rec->count, rec->opcode, @@ -235,7 +234,7 @@ static void vcpu_insn_exec_before(unsigned int cpu_index, void *udata) (*count)++; } -static uint64_t * find_counter(struct qemu_plugin_insn *insn) +static uint64_t *find_counter(struct qemu_plugin_insn *insn) { int i; uint64_t *cnt = NULL; diff --git a/contrib/plugins/hwprofile.c b/contrib/plugins/hwprofile.c new file mode 100644 index 0000000000..faf216ac00 --- /dev/null +++ b/contrib/plugins/hwprofile.c @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2020, Alex BennĂ©e + * + * HW Profile - breakdown access patterns for IO to devices + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +typedef struct { + uint64_t cpu_read; + uint64_t cpu_write; + uint64_t reads; + uint64_t writes; +} IOCounts; + +typedef struct { + uint64_t off_or_pc; + IOCounts counts; +} IOLocationCounts; + +typedef struct { + const char *name; + uint64_t base; + IOCounts totals; + GHashTable *detail; +} DeviceCounts; + +static GMutex lock; +static GHashTable *devices; + +/* track the access pattern to a piece of HW */ +static bool pattern; +/* track the source address of access to HW */ +static bool source; +/* track only matched regions of HW */ +static bool check_match; +static gchar **matches; + +static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW; + +static inline bool track_reads(void) +{ + return rw == QEMU_PLUGIN_MEM_RW || rw == QEMU_PLUGIN_MEM_R; +} + +static inline bool track_writes(void) +{ + return rw == QEMU_PLUGIN_MEM_RW || rw == QEMU_PLUGIN_MEM_W; +} + +static void plugin_init(void) +{ + devices = g_hash_table_new(NULL, NULL); +} + +static gint sort_cmp(gconstpointer a, gconstpointer b) +{ + DeviceCounts *ea = (DeviceCounts *) a; + DeviceCounts *eb = (DeviceCounts *) b; + return ea->totals.reads + ea->totals.writes > + eb->totals.reads + eb->totals.writes ? -1 : 1; +} + +static gint sort_loc(gconstpointer a, gconstpointer b) +{ + IOLocationCounts *ea = (IOLocationCounts *) a; + IOLocationCounts *eb = (IOLocationCounts *) b; + return ea->off_or_pc > eb->off_or_pc; +} + +static void fmt_iocount_record(GString *s, IOCounts *rec) +{ + if (track_reads()) { + g_string_append_printf(s, ", %"PRIx64", %"PRId64, + rec->cpu_read, rec->reads); + } + if (track_writes()) { + g_string_append_printf(s, ", %"PRIx64", %"PRId64, + rec->cpu_write, rec->writes); + } +} + +static void fmt_dev_record(GString *s, DeviceCounts *rec) +{ + g_string_append_printf(s, "%s, 0x%"PRIx64, + rec->name, rec->base); + fmt_iocount_record(s, &rec->totals); + g_string_append_c(s, '\n'); +} + +static void plugin_exit(qemu_plugin_id_t id, void *p) +{ + g_autoptr(GString) report = g_string_new(""); + GList *counts; + + if (!(pattern || source)) { + g_string_printf(report, "Device, Address"); + if (track_reads()) { + g_string_append_printf(report, ", RCPUs, Reads"); + } + if (track_writes()) { + g_string_append_printf(report, ", WCPUs, Writes"); + } + g_string_append_c(report, '\n'); + } + + counts = g_hash_table_get_values(devices); + if (counts && g_list_next(counts)) { + GList *it; + + it = g_list_sort(counts, sort_cmp); + + while (it) { + DeviceCounts *rec = (DeviceCounts *) it->data; + if (rec->detail) { + GList *accesses = g_hash_table_get_values(rec->detail); + GList *io_it = g_list_sort(accesses, sort_loc); + const char *prefix = pattern ? "off" : "pc"; + g_string_append_printf(report, "%s @ 0x%"PRIx64"\n", + rec->name, rec->base); + while (io_it) { + IOLocationCounts *loc = (IOLocationCounts *) io_it->data; + g_string_append_printf(report, " %s:%08"PRIx64, + prefix, loc->off_or_pc); + fmt_iocount_record(report, &loc->counts); + g_string_append_c(report, '\n'); + io_it = io_it->next; + } + } else { + fmt_dev_record(report, rec); + } + it = it->next; + }; + g_list_free(it); + } + + qemu_plugin_outs(report->str); +} + +static DeviceCounts *new_count(const char *name, uint64_t base) +{ + DeviceCounts *count = g_new0(DeviceCounts, 1); + count->name = name; + count->base = base; + if (pattern || source) { + count->detail = g_hash_table_new(NULL, NULL); + } + g_hash_table_insert(devices, (gpointer) name, count); + return count; +} + +static IOLocationCounts *new_location(GHashTable *table, uint64_t off_or_pc) +{ + IOLocationCounts *loc = g_new0(IOLocationCounts, 1); + loc->off_or_pc = off_or_pc; + g_hash_table_insert(table, (gpointer) off_or_pc, loc); + return loc; +} + +static void hwprofile_match_hit(DeviceCounts *rec, uint64_t off) +{ + g_autoptr(GString) report = g_string_new("hwprofile: match @ offset"); + g_string_append_printf(report, "%"PRIx64", previous hits\n", off); + fmt_dev_record(report, rec); + qemu_plugin_outs(report->str); +} + +static void inc_count(IOCounts *count, bool is_write, unsigned int cpu_index) +{ + if (is_write) { + count->writes++; + count->cpu_write |= (1 << cpu_index); + } else { + count->reads++; + count->cpu_read |= (1 << cpu_index); + } +} + +static void vcpu_haddr(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, + uint64_t vaddr, void *udata) +{ + struct qemu_plugin_hwaddr *hwaddr = qemu_plugin_get_hwaddr(meminfo, vaddr); + + if (!hwaddr || !qemu_plugin_hwaddr_is_io(hwaddr)) { + return; + } else { + const char *name = qemu_plugin_hwaddr_device_name(hwaddr); + uint64_t off = qemu_plugin_hwaddr_phys_addr(hwaddr); + bool is_write = qemu_plugin_mem_is_store(meminfo); + DeviceCounts *counts; + + g_mutex_lock(&lock); + counts = (DeviceCounts *) g_hash_table_lookup(devices, name); + + if (!counts) { + uint64_t base = vaddr - off; + counts = new_count(name, base); + } + + if (check_match) { + if (g_strv_contains((const char * const *)matches, counts->name)) { + hwprofile_match_hit(counts, off); + inc_count(&counts->totals, is_write, cpu_index); + } + } else { + inc_count(&counts->totals, is_write, cpu_index); + } + + /* either track offsets or source of access */ + if (source) { + off = (uint64_t) udata; + } + + if (pattern || source) { + IOLocationCounts *io_count = g_hash_table_lookup(counts->detail, + (gpointer) off); + if (!io_count) { + io_count = new_location(counts->detail, off); + } + inc_count(&io_count->counts, is_write, cpu_index); + } + + g_mutex_unlock(&lock); + } +} + +static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + size_t n = qemu_plugin_tb_n_insns(tb); + size_t i; + + for (i = 0; i < n; i++) { + struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); + gpointer udata = (gpointer) (source ? qemu_plugin_insn_vaddr(insn) : 0); + qemu_plugin_register_vcpu_mem_cb(insn, vcpu_haddr, + QEMU_PLUGIN_CB_NO_REGS, + rw, udata); + } +} + +QEMU_PLUGIN_EXPORT +int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, + int argc, char **argv) +{ + int i; + + for (i = 0; i < argc; i++) { + char *opt = argv[i]; + if (g_strcmp0(opt, "read") == 0) { + rw = QEMU_PLUGIN_MEM_R; + } else if (g_strcmp0(opt, "write") == 0) { + rw = QEMU_PLUGIN_MEM_W; + } else if (g_strcmp0(opt, "pattern") == 0) { + pattern = true; + } else if (g_strcmp0(opt, "source") == 0) { + source = true; + } else if (g_str_has_prefix(opt, "match")) { + gchar **parts = g_strsplit(opt, "=", 2); + check_match = true; + matches = g_strsplit(parts[1], ",", -1); + g_strfreev(parts); + } else { + fprintf(stderr, "option parsing failed: %s\n", opt); + return -1; + } + } + + if (source && pattern) { + fprintf(stderr, "can only currently track either source or pattern.\n"); + return -1; + } + + if (!info->system_emulation) { + fprintf(stderr, "hwprofile: plugin only useful for system emulation\n"); + return -1; + } + + /* Just warn about overflow */ + if (info->system.smp_vcpus > 64 || + info->system.max_vcpus > 64) { + fprintf(stderr, "hwprofile: can only track up to 64 CPUs\n"); + } + + plugin_init(); + + qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); + qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); + return 0; +} diff --git a/tests/plugin/lockstep.c b/contrib/plugins/lockstep.c similarity index 96% rename from tests/plugin/lockstep.c rename to contrib/plugins/lockstep.c index a696673dff..7fd35eb669 100644 --- a/tests/plugin/lockstep.c +++ b/contrib/plugins/lockstep.c @@ -134,7 +134,7 @@ static void report_divergance(ExecState *us, ExecState *them) /* Output short log entry of going out of sync... */ if (verbose || divrec.distance == 1 || diverged) { - g_string_printf(out, "@ %#016lx vs %#016lx (%d/%d since last)\n", + g_string_printf(out, "@ 0x%016lx vs 0x%016lx (%d/%d since last)\n", us->pc, them->pc, g_slist_length(divergence_log), divrec.distance); qemu_plugin_outs(out->str); @@ -144,7 +144,7 @@ static void report_divergance(ExecState *us, ExecState *them) int i; GSList *entry; - g_string_printf(out, "Δ insn_count @ %#016lx (%ld) vs %#016lx (%ld)\n", + g_string_printf(out, "Δ insn_count @ 0x%016lx (%ld) vs 0x%016lx (%ld)\n", us->pc, us->insn_count, them->pc, them->insn_count); for (entry = log, i = 0; @@ -152,7 +152,7 @@ static void report_divergance(ExecState *us, ExecState *them) entry = g_slist_next(entry), i++) { ExecInfo *prev = (ExecInfo *) entry->data; g_string_append_printf(out, - " previously @ %#016lx/%ld (%ld insns)\n", + " previously @ 0x%016lx/%ld (%ld insns)\n", prev->block->pc, prev->block->insns, prev->insn_count); } @@ -268,11 +268,13 @@ static bool setup_socket(const char *path) socket_fd = accept(fd, NULL, NULL); if (socket_fd < 0 && errno != EINTR) { perror("accept socket"); + close(fd); return false; } qemu_plugin_outs("setup_socket::ready\n"); + close(fd); return true; } @@ -292,6 +294,7 @@ static bool connect_socket(const char *path) if (connect(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) { perror("failed to connect"); + close(fd); return false; } diff --git a/contrib/rdmacm-mux/Makefile.objs b/contrib/rdmacm-mux/Makefile.objs deleted file mode 100644 index 3df744af89..0000000000 --- a/contrib/rdmacm-mux/Makefile.objs +++ /dev/null @@ -1,3 +0,0 @@ -ifdef CONFIG_PVRDMA -rdmacm-mux-obj-y = main.o -endif diff --git a/contrib/rdmacm-mux/main.c b/contrib/rdmacm-mux/main.c index bd82abbad3..771ca01e03 100644 --- a/contrib/rdmacm-mux/main.c +++ b/contrib/rdmacm-mux/main.c @@ -186,7 +186,7 @@ static int hash_tbl_search_fd_by_ifid(int *fd, __be64 *gid_ifid) *fd = _hash_tbl_search_fd_by_ifid(gid_ifid); pthread_rwlock_unlock(&server.lock); - if (!fd) { + if (!*fd) { syslog(LOG_WARNING, "Can't find matching for ifid 0x%llx\n", *gid_ifid); return -ENOENT; } diff --git a/contrib/rdmacm-mux/meson.build b/contrib/rdmacm-mux/meson.build new file mode 100644 index 0000000000..6cc5016747 --- /dev/null +++ b/contrib/rdmacm-mux/meson.build @@ -0,0 +1,9 @@ +if 'CONFIG_PVRDMA' in config_host + # if not found, CONFIG_PVRDMA should not be set + # FIXME: broken on big endian architectures + libumad = cc.find_library('ibumad', required: true) + executable('rdmacm-mux', files('main.c'), + dependencies: [glib, libumad], + build_by_default: false, + install: false) +endif diff --git a/contrib/vhost-user-blk/Makefile.objs b/contrib/vhost-user-blk/Makefile.objs deleted file mode 100644 index 72e2cdc3ad..0000000000 --- a/contrib/vhost-user-blk/Makefile.objs +++ /dev/null @@ -1 +0,0 @@ -vhost-user-blk-obj-y = vhost-user-blk.o diff --git a/contrib/vhost-user-blk/meson.build b/contrib/vhost-user-blk/meson.build new file mode 100644 index 0000000000..601ea15ef5 --- /dev/null +++ b/contrib/vhost-user-blk/meson.build @@ -0,0 +1,5 @@ +# FIXME: broken on 32-bit architectures +executable('vhost-user-blk', files('vhost-user-blk.c'), + dependencies: [qemuutil, vhost_user], + build_by_default: false, + install: false) diff --git a/contrib/vhost-user-blk/vhost-user-blk.c b/contrib/vhost-user-blk/vhost-user-blk.c index 25eccd02b5..d14b2896bf 100644 --- a/contrib/vhost-user-blk/vhost-user-blk.c +++ b/contrib/vhost-user-blk/vhost-user-blk.c @@ -17,8 +17,7 @@ #include "qemu/osdep.h" #include "standard-headers/linux/virtio_blk.h" -#include "contrib/libvhost-user/libvhost-user-glib.h" -#include "contrib/libvhost-user/libvhost-user.h" +#include "libvhost-user-glib.h" #if defined(__linux__) #include @@ -404,6 +403,10 @@ vub_get_config(VuDev *vu_dev, uint8_t *config, uint32_t len) VugDev *gdev; VubDev *vdev_blk; + if (len > sizeof(struct virtio_blk_config)) { + return -1; + } + gdev = container_of(vu_dev, VugDev, parent); vdev_blk = container_of(gdev, VubDev, parent); memcpy(config, &vdev_blk->blkcfg, len); @@ -474,7 +477,7 @@ static int unix_sock_new(char *unix_fn) assert(unix_fn); sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock <= 0) { + if (sock < 0) { perror("socket"); return -1; } diff --git a/contrib/vhost-user-gpu/Makefile.objs b/contrib/vhost-user-gpu/Makefile.objs deleted file mode 100644 index 09296091be..0000000000 --- a/contrib/vhost-user-gpu/Makefile.objs +++ /dev/null @@ -1,10 +0,0 @@ -vhost-user-gpu-obj-y = vhost-user-gpu.o virgl.o vugbm.o - -vhost-user-gpu.o-cflags := $(PIXMAN_CFLAGS) $(GBM_CFLAGS) -vhost-user-gpu.o-libs := $(PIXMAN_LIBS) - -virgl.o-cflags := $(VIRGL_CFLAGS) $(GBM_CFLAGS) -virgl.o-libs := $(VIRGL_LIBS) - -vugbm.o-cflags := $(GBM_CFLAGS) -vugbm.o-libs := $(GBM_LIBS) diff --git a/contrib/vhost-user-gpu/meson.build b/contrib/vhost-user-gpu/meson.build new file mode 100644 index 0000000000..0ce1515a10 --- /dev/null +++ b/contrib/vhost-user-gpu/meson.build @@ -0,0 +1,13 @@ +if 'CONFIG_TOOLS' in config_host and 'CONFIG_VIRGL' in config_host \ + and 'CONFIG_GBM' in config_host and 'CONFIG_LINUX' in config_host \ + and pixman.found() + executable('vhost-user-gpu', files('vhost-user-gpu.c', 'virgl.c', 'vugbm.c'), + dependencies: [qemuutil, pixman, gbm, virgl, vhost_user, opengl], + install: true, + install_dir: get_option('libexecdir')) + + configure_file(input: '50-qemu-gpu.json.in', + output: '50-qemu-gpu.json', + configuration: { 'libexecdir' : get_option('prefix') / get_option('libexecdir') }, + install_dir: qemu_datadir / 'vhost-user') +endif diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c index a019d0a9ac..f73f292c9f 100644 --- a/contrib/vhost-user-gpu/vhost-user-gpu.c +++ b/contrib/vhost-user-gpu/vhost-user-gpu.c @@ -124,7 +124,7 @@ source_wait_cb(gint fd, GIOCondition condition, gpointer user_data) } /* resume */ - g->wait_ok = 0; + g->wait_in = 0; vg_handle_ctrl(&g->dev.parent, 0); return G_SOURCE_REMOVE; @@ -133,8 +133,8 @@ source_wait_cb(gint fd, GIOCondition condition, gpointer user_data) void vg_wait_ok(VuGpu *g) { - assert(g->wait_ok == 0); - g->wait_ok = g_unix_fd_add(g->sock_fd, G_IO_IN | G_IO_HUP, + assert(g->wait_in == 0); + g->wait_in = g_unix_fd_add(g->sock_fd, G_IO_IN | G_IO_HUP, source_wait_cb, g); } @@ -246,7 +246,7 @@ vg_ctrl_response(VuGpu *g, } vu_queue_push(&g->dev.parent, cmd->vq, &cmd->elem, s); vu_queue_notify(&g->dev.parent, cmd->vq); - cmd->finished = true; + cmd->state = VG_CMD_STATE_FINISHED; } void @@ -261,23 +261,44 @@ vg_ctrl_response_nodata(VuGpu *g, vg_ctrl_response(g, cmd, &resp, sizeof(resp)); } + +static gboolean +get_display_info_cb(gint fd, GIOCondition condition, gpointer user_data) +{ + struct virtio_gpu_resp_display_info dpy_info = { {} }; + VuGpu *vg = user_data; + struct virtio_gpu_ctrl_command *cmd = QTAILQ_LAST(&vg->fenceq); + + g_debug("disp info cb"); + assert(cmd->cmd_hdr.type == VIRTIO_GPU_CMD_GET_DISPLAY_INFO); + if (!vg_recv_msg(vg, VHOST_USER_GPU_GET_DISPLAY_INFO, + sizeof(dpy_info), &dpy_info)) { + return G_SOURCE_CONTINUE; + } + + QTAILQ_REMOVE(&vg->fenceq, cmd, next); + vg_ctrl_response(vg, cmd, &dpy_info.hdr, sizeof(dpy_info)); + + vg->wait_in = 0; + vg_handle_ctrl(&vg->dev.parent, 0); + + return G_SOURCE_REMOVE; +} + void vg_get_display_info(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd) { - struct virtio_gpu_resp_display_info dpy_info = { {} }; VhostUserGpuMsg msg = { .request = VHOST_USER_GPU_GET_DISPLAY_INFO, .size = 0, }; - assert(vg->wait_ok == 0); + assert(vg->wait_in == 0); vg_send_msg(vg, &msg, -1); - if (!vg_recv_msg(vg, msg.request, sizeof(dpy_info), &dpy_info)) { - return; - } - - vg_ctrl_response(vg, cmd, &dpy_info.hdr, sizeof(dpy_info)); + vg->wait_in = g_unix_fd_add(vg->sock_fd, G_IO_IN | G_IO_HUP, + get_display_info_cb, vg); + cmd->state = VG_CMD_STATE_PENDING; } static void @@ -800,7 +821,7 @@ vg_process_cmd(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd) cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; break; } - if (!cmd->finished) { + if (cmd->state == VG_CMD_STATE_NEW) { vg_ctrl_response_nodata(vg, cmd, cmd->error ? cmd->error : VIRTIO_GPU_RESP_OK_NODATA); } @@ -815,7 +836,7 @@ vg_handle_ctrl(VuDev *dev, int qidx) size_t len; for (;;) { - if (vg->wait_ok != 0) { + if (vg->wait_in != 0) { return; } @@ -825,7 +846,7 @@ vg_handle_ctrl(VuDev *dev, int qidx) } cmd->vq = vq; cmd->error = 0; - cmd->finished = false; + cmd->state = VG_CMD_STATE_NEW; len = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 0, &cmd->cmd_hdr, sizeof(cmd->cmd_hdr)); @@ -844,7 +865,7 @@ vg_handle_ctrl(VuDev *dev, int qidx) vg_process_cmd(vg, cmd); } - if (!cmd->finished) { + if (cmd->state != VG_CMD_STATE_FINISHED) { QTAILQ_INSERT_TAIL(&vg->fenceq, cmd, next); vg->inflight++; } else { @@ -871,11 +892,8 @@ update_cursor_data_simple(VuGpu *g, uint32_t resource_id, gpointer data) static void vg_process_cursor_cmd(VuGpu *g, struct virtio_gpu_update_cursor *cursor) { - bool move = cursor->hdr.type != VIRTIO_GPU_CMD_MOVE_CURSOR; - - g_debug("%s move:%d\n", G_STRFUNC, move); - - if (move) { + switch (cursor->hdr.type) { + case VIRTIO_GPU_CMD_MOVE_CURSOR: { VhostUserGpuMsg msg = { .request = cursor->resource_id ? VHOST_USER_GPU_CURSOR_POS : VHOST_USER_GPU_CURSOR_POS_HIDE, @@ -886,8 +904,11 @@ vg_process_cursor_cmd(VuGpu *g, struct virtio_gpu_update_cursor *cursor) .y = cursor->pos.y, } }; + g_debug("%s: move", G_STRFUNC); vg_send_msg(g, &msg, -1); - } else { + break; + } + case VIRTIO_GPU_CMD_UPDATE_CURSOR: { VhostUserGpuMsg msg = { .request = VHOST_USER_GPU_CURSOR_UPDATE, .size = sizeof(VhostUserGpuCursorUpdate), @@ -901,6 +922,7 @@ vg_process_cursor_cmd(VuGpu *g, struct virtio_gpu_update_cursor *cursor) .hot_y = cursor->hot_y, } }; + g_debug("%s: update", G_STRFUNC); if (g->virgl) { vg_virgl_update_cursor_data(g, cursor->resource_id, msg.payload.cursor_update.data); @@ -909,6 +931,11 @@ vg_process_cursor_cmd(VuGpu *g, struct virtio_gpu_update_cursor *cursor) msg.payload.cursor_update.data); } vg_send_msg(g, &msg, -1); + break; + } + default: + g_debug("%s: unknown cmd %d", G_STRFUNC, cursor->hdr.type); + break; } } @@ -969,18 +996,17 @@ vg_queue_set_started(VuDev *dev, int qidx, bool started) } } -static void -set_gpu_protocol_features(VuGpu *g) +static gboolean +protocol_features_cb(gint fd, GIOCondition condition, gpointer user_data) { + VuGpu *g = user_data; uint64_t u64; VhostUserGpuMsg msg = { .request = VHOST_USER_GPU_GET_PROTOCOL_FEATURES }; - assert(g->wait_ok == 0); - vg_send_msg(g, &msg, -1); if (!vg_recv_msg(g, msg.request, sizeof(u64), &u64)) { - return; + return G_SOURCE_CONTINUE; } msg = (VhostUserGpuMsg) { @@ -989,6 +1015,24 @@ set_gpu_protocol_features(VuGpu *g) .payload.u64 = 0 }; vg_send_msg(g, &msg, -1); + + g->wait_in = 0; + vg_handle_ctrl(&g->dev.parent, 0); + + return G_SOURCE_REMOVE; +} + +static void +set_gpu_protocol_features(VuGpu *g) +{ + VhostUserGpuMsg msg = { + .request = VHOST_USER_GPU_GET_PROTOCOL_FEATURES + }; + + vg_send_msg(g, &msg, -1); + assert(g->wait_in == 0); + g->wait_in = g_unix_fd_add(g->sock_fd, G_IO_IN | G_IO_HUP, + protocol_features_cb, g); } static int @@ -1044,7 +1088,9 @@ vg_get_config(VuDev *dev, uint8_t *config, uint32_t len) { VuGpu *g = container_of(dev, VuGpu, dev.parent); - g_return_val_if_fail(len <= sizeof(struct virtio_gpu_config), -1); + if (len > sizeof(struct virtio_gpu_config)) { + return -1; + } if (opt_virgl) { g->virtio_config.num_capsets = vg_virgl_get_num_capsets(); @@ -1146,11 +1192,7 @@ main(int argc, char *argv[]) exit(EXIT_FAILURE); } - if (g.drm_rnode_fd >= 0) { - if (!vugbm_device_init(&g.gdev, g.drm_rnode_fd)) { - g_warning("Failed to init DRM device, using fallback path"); - } - } + vugbm_device_init(&g.gdev, g.drm_rnode_fd); if ((!!opt_socket_path + (opt_fdnum != -1)) != 1) { g_printerr("Please specify either --fd or --socket-path\n"); diff --git a/contrib/vhost-user-gpu/virgl.c b/contrib/vhost-user-gpu/virgl.c index b0bc22c3c1..9e6660c7ab 100644 --- a/contrib/vhost-user-gpu/virgl.c +++ b/contrib/vhost-user-gpu/virgl.c @@ -12,9 +12,12 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include #include "virgl.h" +#include + void vg_virgl_update_cursor_data(VuGpu *g, uint32_t resource_id, gpointer data) @@ -371,6 +374,7 @@ virgl_cmd_resource_flush(VuGpu *g, VUGPU_FILL_CMD(rf); + glFlush(); if (!rf.resource_id) { g_debug("bad resource id for flush..?"); return; @@ -481,7 +485,7 @@ void vg_virgl_process_cmd(VuGpu *g, struct virtio_gpu_ctrl_command *cmd) break; } - if (cmd->finished) { + if (cmd->state != VG_CMD_STATE_NEW) { return; } diff --git a/contrib/vhost-user-gpu/vugbm.c b/contrib/vhost-user-gpu/vugbm.c index 9c357b6399..fb15d0372c 100644 --- a/contrib/vhost-user-gpu/vugbm.c +++ b/contrib/vhost-user-gpu/vugbm.c @@ -7,6 +7,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "vugbm.h" static bool @@ -198,55 +199,51 @@ vugbm_device_destroy(struct vugbm_device *dev) dev->device_destroy(dev); } -bool +void vugbm_device_init(struct vugbm_device *dev, int fd) { - dev->fd = fd; + assert(!dev->inited); #ifdef CONFIG_GBM - dev->dev = gbm_create_device(fd); -#endif - - if (0) { - /* nothing */ + if (fd >= 0) { + dev->dev = gbm_create_device(fd); } -#ifdef CONFIG_GBM - else if (dev->dev != NULL) { + if (dev->dev != NULL) { + dev->fd = fd; dev->alloc_bo = alloc_bo; dev->free_bo = free_bo; dev->get_fd = get_fd; dev->map_bo = map_bo; dev->unmap_bo = unmap_bo; dev->device_destroy = device_destroy; + dev->inited = true; } #endif #ifdef CONFIG_MEMFD - else if (g_file_test("/dev/udmabuf", G_FILE_TEST_EXISTS)) { + if (!dev->inited && g_file_test("/dev/udmabuf", G_FILE_TEST_EXISTS)) { dev->fd = open("/dev/udmabuf", O_RDWR); - if (dev->fd < 0) { - return false; + if (dev->fd >= 0) { + g_debug("Using experimental udmabuf backend"); + dev->alloc_bo = udmabuf_alloc_bo; + dev->free_bo = udmabuf_free_bo; + dev->get_fd = udmabuf_get_fd; + dev->map_bo = udmabuf_map_bo; + dev->unmap_bo = udmabuf_unmap_bo; + dev->device_destroy = udmabuf_device_destroy; + dev->inited = true; } - g_debug("Using experimental udmabuf backend"); - dev->alloc_bo = udmabuf_alloc_bo; - dev->free_bo = udmabuf_free_bo; - dev->get_fd = udmabuf_get_fd; - dev->map_bo = udmabuf_map_bo; - dev->unmap_bo = udmabuf_unmap_bo; - dev->device_destroy = udmabuf_device_destroy; } #endif - else { + if (!dev->inited) { g_debug("Using mem fallback"); dev->alloc_bo = mem_alloc_bo; dev->free_bo = mem_free_bo; dev->map_bo = mem_map_bo; dev->unmap_bo = mem_unmap_bo; dev->device_destroy = mem_device_destroy; - return false; + dev->inited = true; } - - dev->inited = true; - return true; + assert(dev->inited); } static bool diff --git a/contrib/vhost-user-gpu/vugbm.h b/contrib/vhost-user-gpu/vugbm.h index 07e698fcd7..82bc4934e1 100644 --- a/contrib/vhost-user-gpu/vugbm.h +++ b/contrib/vhost-user-gpu/vugbm.h @@ -10,10 +10,8 @@ #ifndef VHOST_USER_GPU_VUGBM_H #define VHOST_USER_GPU_VUGBM_H -#include "qemu/osdep.h" #ifdef CONFIG_MEMFD -#include #include #endif @@ -56,7 +54,7 @@ struct vugbm_buffer { uint32_t format; }; -bool vugbm_device_init(struct vugbm_device *dev, int fd); +void vugbm_device_init(struct vugbm_device *dev, int fd); void vugbm_device_destroy(struct vugbm_device *dev); bool vugbm_buffer_create(struct vugbm_buffer *buffer, struct vugbm_device *dev, diff --git a/contrib/vhost-user-gpu/vugpu.h b/contrib/vhost-user-gpu/vugpu.h index 3153c9a6de..04d5615812 100644 --- a/contrib/vhost-user-gpu/vugpu.h +++ b/contrib/vhost-user-gpu/vugpu.h @@ -15,9 +15,8 @@ #ifndef VUGPU_H #define VUGPU_H -#include "qemu/osdep.h" -#include "contrib/libvhost-user/libvhost-user-glib.h" +#include "libvhost-user-glib.h" #include "standard-headers/linux/virtio_gpu.h" #include "qemu/queue.h" @@ -119,7 +118,7 @@ typedef struct VuGpu { int sock_fd; int drm_rnode_fd; GSource *renderer_source; - guint wait_ok; + guint wait_in; bool virgl; bool virgl_inited; @@ -130,12 +129,18 @@ typedef struct VuGpu { QTAILQ_HEAD(, virtio_gpu_ctrl_command) fenceq; } VuGpu; +enum { + VG_CMD_STATE_NEW, + VG_CMD_STATE_PENDING, + VG_CMD_STATE_FINISHED, +}; + struct virtio_gpu_ctrl_command { VuVirtqElement elem; VuVirtq *vq; struct virtio_gpu_ctrl_hdr cmd_hdr; uint32_t error; - bool finished; + int state; QTAILQ_ENTRY(virtio_gpu_ctrl_command) next; }; diff --git a/contrib/vhost-user-input/Makefile.objs b/contrib/vhost-user-input/Makefile.objs deleted file mode 100644 index b1fad90d51..0000000000 --- a/contrib/vhost-user-input/Makefile.objs +++ /dev/null @@ -1 +0,0 @@ -vhost-user-input-obj-y = main.o diff --git a/contrib/vhost-user-input/main.c b/contrib/vhost-user-input/main.c index 6020c6f33a..c15d18c33f 100644 --- a/contrib/vhost-user-input/main.c +++ b/contrib/vhost-user-input/main.c @@ -6,14 +6,12 @@ #include "qemu/osdep.h" -#include #include #include "qemu/iov.h" #include "qemu/bswap.h" #include "qemu/sockets.h" -#include "contrib/libvhost-user/libvhost-user.h" -#include "contrib/libvhost-user/libvhost-user-glib.h" +#include "libvhost-user-glib.h" #include "standard-headers/linux/virtio_input.h" #include "qapi/error.h" @@ -212,7 +210,9 @@ static int vi_get_config(VuDev *dev, uint8_t *config, uint32_t len) { VuInput *vi = container_of(dev, VuInput, dev.parent); - g_return_val_if_fail(len <= sizeof(*vi->sel_config), -1); + if (len > sizeof(*vi->sel_config)) { + return -1; + } if (vi->sel_config) { memcpy(config, vi->sel_config, len); diff --git a/contrib/vhost-user-input/meson.build b/contrib/vhost-user-input/meson.build new file mode 100644 index 0000000000..21a9ed4f15 --- /dev/null +++ b/contrib/vhost-user-input/meson.build @@ -0,0 +1,4 @@ +executable('vhost-user-input', files('main.c'), + dependencies: [qemuutil, vhost_user], + build_by_default: targetos == 'linux', + install: false) diff --git a/contrib/vhost-user-scsi/Makefile.objs b/contrib/vhost-user-scsi/Makefile.objs deleted file mode 100644 index e83a38a85b..0000000000 --- a/contrib/vhost-user-scsi/Makefile.objs +++ /dev/null @@ -1 +0,0 @@ -vhost-user-scsi-obj-y = vhost-user-scsi.o diff --git a/contrib/vhost-user-scsi/meson.build b/contrib/vhost-user-scsi/meson.build new file mode 100644 index 0000000000..cc893f6f20 --- /dev/null +++ b/contrib/vhost-user-scsi/meson.build @@ -0,0 +1,6 @@ +if libiscsi.found() + executable('vhost-user-scsi', files('vhost-user-scsi.c'), + dependencies: [qemuutil, libiscsi, vhost_user], + build_by_default: targetos == 'linux', + install: false) +endif diff --git a/contrib/vhost-user-scsi/vhost-user-scsi.c b/contrib/vhost-user-scsi/vhost-user-scsi.c index 7a1db164c8..4f6e3e2a24 100644 --- a/contrib/vhost-user-scsi/vhost-user-scsi.c +++ b/contrib/vhost-user-scsi/vhost-user-scsi.c @@ -12,8 +12,10 @@ #include "qemu/osdep.h" #include +#define inline __attribute__((gnu_inline)) /* required for libiscsi v1.9.0 */ #include -#include "contrib/libvhost-user/libvhost-user-glib.h" +#undef inline +#include "libvhost-user-glib.h" #include "standard-headers/linux/virtio_scsi.h" @@ -230,6 +232,7 @@ static void vus_proc_req(VuDev *vu_dev, int idx) VugDev *gdev; VusDev *vdev_scsi; VuVirtq *vq; + VuVirtqElement *elem = NULL; assert(vu_dev); @@ -246,7 +249,6 @@ static void vus_proc_req(VuDev *vu_dev, int idx) g_debug("Got kicked on vq[%d]@%p", idx, vq); while (1) { - VuVirtqElement *elem; VirtIOSCSICmdReq *req; VirtIOSCSICmdResp *rsp; @@ -286,6 +288,7 @@ static void vus_proc_req(VuDev *vu_dev, int idx) free(elem); } + free(elem); } static void vus_queue_set_started(VuDev *vu_dev, int idx, bool started) @@ -318,7 +321,7 @@ static int unix_sock_new(char *unix_fn) assert(unix_fn); sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock <= 0) { + if (sock < 0) { perror("socket"); return -1; } diff --git a/cpu.c b/cpu.c new file mode 100644 index 0000000000..bfbe5a66f9 --- /dev/null +++ b/cpu.c @@ -0,0 +1,437 @@ +/* + * Target-specific parts of the CPU object + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qapi/error.h" + +#include "exec/target_page.h" +#include "hw/qdev-core.h" +#include "hw/qdev-properties.h" +#include "qemu/error-report.h" +#include "migration/vmstate.h" +#ifdef CONFIG_USER_ONLY +#include "qemu.h" +#else +#include "exec/address-spaces.h" +#endif +#include "sysemu/tcg.h" +#include "sysemu/kvm.h" +#include "sysemu/replay.h" +#include "exec/translate-all.h" +#include "exec/log.h" + +uintptr_t qemu_host_page_size; +intptr_t qemu_host_page_mask; + +#ifndef CONFIG_USER_ONLY +static int cpu_common_post_load(void *opaque, int version_id) +{ + CPUState *cpu = opaque; + + /* 0x01 was CPU_INTERRUPT_EXIT. This line can be removed when the + version_id is increased. */ + cpu->interrupt_request &= ~0x01; + tlb_flush(cpu); + + /* loadvm has just updated the content of RAM, bypassing the + * usual mechanisms that ensure we flush TBs for writes to + * memory we've translated code from. So we must flush all TBs, + * which will now be stale. + */ + tb_flush(cpu); + + return 0; +} + +static int cpu_common_pre_load(void *opaque) +{ + CPUState *cpu = opaque; + + cpu->exception_index = -1; + + return 0; +} + +static bool cpu_common_exception_index_needed(void *opaque) +{ + CPUState *cpu = opaque; + + return tcg_enabled() && cpu->exception_index != -1; +} + +static const VMStateDescription vmstate_cpu_common_exception_index = { + .name = "cpu_common/exception_index", + .version_id = 1, + .minimum_version_id = 1, + .needed = cpu_common_exception_index_needed, + .fields = (VMStateField[]) { + VMSTATE_INT32(exception_index, CPUState), + VMSTATE_END_OF_LIST() + } +}; + +static bool cpu_common_crash_occurred_needed(void *opaque) +{ + CPUState *cpu = opaque; + + return cpu->crash_occurred; +} + +static const VMStateDescription vmstate_cpu_common_crash_occurred = { + .name = "cpu_common/crash_occurred", + .version_id = 1, + .minimum_version_id = 1, + .needed = cpu_common_crash_occurred_needed, + .fields = (VMStateField[]) { + VMSTATE_BOOL(crash_occurred, CPUState), + VMSTATE_END_OF_LIST() + } +}; + +const VMStateDescription vmstate_cpu_common = { + .name = "cpu_common", + .version_id = 1, + .minimum_version_id = 1, + .pre_load = cpu_common_pre_load, + .post_load = cpu_common_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(halted, CPUState), + VMSTATE_UINT32(interrupt_request, CPUState), + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription*[]) { + &vmstate_cpu_common_exception_index, + &vmstate_cpu_common_crash_occurred, + NULL + } +}; +#endif + +void cpu_exec_realizefn(CPUState *cpu, Error **errp) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + + cpu_list_add(cpu); + +#ifdef CONFIG_TCG + /* NB: errp parameter is unused currently */ + if (tcg_enabled()) { + tcg_exec_realizefn(cpu, errp); + } +#endif /* CONFIG_TCG */ + +#ifdef CONFIG_USER_ONLY + assert(cc->vmsd == NULL); +#else + if (qdev_get_vmsd(DEVICE(cpu)) == NULL) { + vmstate_register(NULL, cpu->cpu_index, &vmstate_cpu_common, cpu); + } + if (cc->vmsd != NULL) { + vmstate_register(NULL, cpu->cpu_index, cc->vmsd, cpu); + } +#endif /* CONFIG_USER_ONLY */ +} + +void cpu_exec_unrealizefn(CPUState *cpu) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + +#ifdef CONFIG_USER_ONLY + assert(cc->vmsd == NULL); +#else + if (cc->vmsd != NULL) { + vmstate_unregister(NULL, cc->vmsd, cpu); + } + if (qdev_get_vmsd(DEVICE(cpu)) == NULL) { + vmstate_unregister(NULL, &vmstate_cpu_common, cpu); + } +#endif +#ifdef CONFIG_TCG + /* NB: errp parameter is unused currently */ + if (tcg_enabled()) { + tcg_exec_unrealizefn(cpu); + } +#endif /* CONFIG_TCG */ + + cpu_list_remove(cpu); +} + +void cpu_exec_initfn(CPUState *cpu) +{ + cpu->as = NULL; + cpu->num_ases = 0; + +#ifndef CONFIG_USER_ONLY + cpu->thread_id = qemu_get_thread_id(); + cpu->memory = get_system_memory(); + object_ref(OBJECT(cpu->memory)); +#endif +} + +const char *parse_cpu_option(const char *cpu_option) +{ + ObjectClass *oc; + CPUClass *cc; + gchar **model_pieces; + const char *cpu_type; + + model_pieces = g_strsplit(cpu_option, ",", 2); + if (!model_pieces[0]) { + error_report("-cpu option cannot be empty"); + exit(1); + } + + oc = cpu_class_by_name(CPU_RESOLVING_TYPE, model_pieces[0]); + if (oc == NULL) { + error_report("unable to find CPU model '%s'", model_pieces[0]); + g_strfreev(model_pieces); + exit(EXIT_FAILURE); + } + + cpu_type = object_class_get_name(oc); + cc = CPU_CLASS(oc); + cc->parse_features(cpu_type, model_pieces[1], &error_fatal); + g_strfreev(model_pieces); + return cpu_type; +} + +#if defined(CONFIG_USER_ONLY) +void tb_invalidate_phys_addr(target_ulong addr) +{ + mmap_lock(); + tb_invalidate_phys_page_range(addr, addr + 1); + mmap_unlock(); +} + +static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) +{ + tb_invalidate_phys_addr(pc); +} +#else +void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs) +{ + ram_addr_t ram_addr; + MemoryRegion *mr; + hwaddr l = 1; + + if (!tcg_enabled()) { + return; + } + + RCU_READ_LOCK_GUARD(); + mr = address_space_translate(as, addr, &addr, &l, false, attrs); + if (!(memory_region_is_ram(mr) + || memory_region_is_romd(mr))) { + return; + } + ram_addr = memory_region_get_ram_addr(mr) + addr; + tb_invalidate_phys_page_range(ram_addr, ram_addr + 1); +} + +static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) +{ + /* + * There may not be a virtual to physical translation for the pc + * right now, but there may exist cached TB for this pc. + * Flush the whole TB cache to force re-translation of such TBs. + * This is heavyweight, but we're debugging anyway. + */ + tb_flush(cpu); +} +#endif + +/* Add a breakpoint. */ +int cpu_breakpoint_insert(CPUState *cpu, vaddr pc, int flags, + CPUBreakpoint **breakpoint) +{ + CPUBreakpoint *bp; + + bp = g_malloc(sizeof(*bp)); + + bp->pc = pc; + bp->flags = flags; + + /* keep all GDB-injected breakpoints in front */ + if (flags & BP_GDB) { + QTAILQ_INSERT_HEAD(&cpu->breakpoints, bp, entry); + } else { + QTAILQ_INSERT_TAIL(&cpu->breakpoints, bp, entry); + } + + breakpoint_invalidate(cpu, pc); + + if (breakpoint) { + *breakpoint = bp; + } + return 0; +} + +/* Remove a specific breakpoint. */ +int cpu_breakpoint_remove(CPUState *cpu, vaddr pc, int flags) +{ + CPUBreakpoint *bp; + + QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { + if (bp->pc == pc && bp->flags == flags) { + cpu_breakpoint_remove_by_ref(cpu, bp); + return 0; + } + } + return -ENOENT; +} + +/* Remove a specific breakpoint by reference. */ +void cpu_breakpoint_remove_by_ref(CPUState *cpu, CPUBreakpoint *breakpoint) +{ + QTAILQ_REMOVE(&cpu->breakpoints, breakpoint, entry); + + breakpoint_invalidate(cpu, breakpoint->pc); + + g_free(breakpoint); +} + +/* Remove all matching breakpoints. */ +void cpu_breakpoint_remove_all(CPUState *cpu, int mask) +{ + CPUBreakpoint *bp, *next; + + QTAILQ_FOREACH_SAFE(bp, &cpu->breakpoints, entry, next) { + if (bp->flags & mask) { + cpu_breakpoint_remove_by_ref(cpu, bp); + } + } +} + +/* enable or disable single step mode. EXCP_DEBUG is returned by the + CPU loop after each instruction */ +void cpu_single_step(CPUState *cpu, int enabled) +{ + if (cpu->singlestep_enabled != enabled) { + cpu->singlestep_enabled = enabled; + if (kvm_enabled()) { + kvm_update_guest_debug(cpu, 0); + } else { + /* must flush all the translated code to avoid inconsistencies */ + /* XXX: only flush what is necessary */ + tb_flush(cpu); + } + } +} + +void cpu_abort(CPUState *cpu, const char *fmt, ...) +{ + va_list ap; + va_list ap2; + + va_start(ap, fmt); + va_copy(ap2, ap); + fprintf(stderr, "qemu: fatal: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + cpu_dump_state(cpu, stderr, CPU_DUMP_FPU | CPU_DUMP_CCOP); + if (qemu_log_separate()) { + FILE *logfile = qemu_log_lock(); + qemu_log("qemu: fatal: "); + qemu_log_vprintf(fmt, ap2); + qemu_log("\n"); + log_cpu_state(cpu, CPU_DUMP_FPU | CPU_DUMP_CCOP); + qemu_log_flush(); + qemu_log_unlock(logfile); + qemu_log_close(); + } + va_end(ap2); + va_end(ap); + replay_finish(); +#if defined(CONFIG_USER_ONLY) + { + struct sigaction act; + sigfillset(&act.sa_mask); + act.sa_handler = SIG_DFL; + act.sa_flags = 0; + sigaction(SIGABRT, &act, NULL); + } +#endif + abort(); +} + +/* physical memory access (slow version, mainly for debug) */ +#if defined(CONFIG_USER_ONLY) +int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, + void *ptr, target_ulong len, bool is_write) +{ + int flags; + target_ulong l, page; + void * p; + uint8_t *buf = ptr; + + while (len > 0) { + page = addr & TARGET_PAGE_MASK; + l = (page + TARGET_PAGE_SIZE) - addr; + if (l > len) + l = len; + flags = page_get_flags(page); + if (!(flags & PAGE_VALID)) + return -1; + if (is_write) { + if (!(flags & PAGE_WRITE)) + return -1; + /* XXX: this code should not depend on lock_user */ + if (!(p = lock_user(VERIFY_WRITE, addr, l, 0))) + return -1; + memcpy(p, buf, l); + unlock_user(p, addr, l); + } else { + if (!(flags & PAGE_READ)) + return -1; + /* XXX: this code should not depend on lock_user */ + if (!(p = lock_user(VERIFY_READ, addr, l, 1))) + return -1; + memcpy(buf, p, l); + unlock_user(p, addr, 0); + } + len -= l; + buf += l; + addr += l; + } + return 0; +} +#endif + +bool target_words_bigendian(void) +{ +#if defined(TARGET_WORDS_BIGENDIAN) + return true; +#else + return false; +#endif +} + +void page_size_init(void) +{ + /* NOTE: we can always suppose that qemu_host_page_size >= + TARGET_PAGE_SIZE */ + if (qemu_host_page_size == 0) { + qemu_host_page_size = qemu_real_host_page_size; + } + if (qemu_host_page_size < TARGET_PAGE_SIZE) { + qemu_host_page_size = TARGET_PAGE_SIZE; + } + qemu_host_page_mask = -(intptr_t)qemu_host_page_size; +} diff --git a/cpus-common.c b/cpus-common.c index 34044f4e4c..6e73d3e58d 100644 --- a/cpus-common.c +++ b/cpus-common.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -148,7 +148,7 @@ void do_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data, wi.exclusive = false; queue_work_on_cpu(cpu, &wi); - while (!atomic_mb_read(&wi.done)) { + while (!qatomic_mb_read(&wi.done)) { CPUState *self_cpu = current_cpu; qemu_cond_wait(&qemu_work_cond, mutex); @@ -188,20 +188,20 @@ void start_exclusive(void) exclusive_idle(); /* Make all other cpus stop executing. */ - atomic_set(&pending_cpus, 1); + qatomic_set(&pending_cpus, 1); /* Write pending_cpus before reading other_cpu->running. */ smp_mb(); running_cpus = 0; CPU_FOREACH(other_cpu) { - if (atomic_read(&other_cpu->running)) { + if (qatomic_read(&other_cpu->running)) { other_cpu->has_waiter = true; running_cpus++; qemu_cpu_kick(other_cpu); } } - atomic_set(&pending_cpus, running_cpus + 1); + qatomic_set(&pending_cpus, running_cpus + 1); while (pending_cpus > 1) { qemu_cond_wait(&exclusive_cond, &qemu_cpu_list_lock); } @@ -220,7 +220,7 @@ void end_exclusive(void) current_cpu->in_exclusive_context = false; qemu_mutex_lock(&qemu_cpu_list_lock); - atomic_set(&pending_cpus, 0); + qatomic_set(&pending_cpus, 0); qemu_cond_broadcast(&exclusive_resume); qemu_mutex_unlock(&qemu_cpu_list_lock); } @@ -228,7 +228,7 @@ void end_exclusive(void) /* Wait for exclusive ops to finish, and begin cpu execution. */ void cpu_exec_start(CPUState *cpu) { - atomic_set(&cpu->running, true); + qatomic_set(&cpu->running, true); /* Write cpu->running before reading pending_cpus. */ smp_mb(); @@ -246,17 +246,17 @@ void cpu_exec_start(CPUState *cpu) * 3. pending_cpus == 0. Then start_exclusive is definitely going to * see cpu->running == true, and it will kick the CPU. */ - if (unlikely(atomic_read(&pending_cpus))) { + if (unlikely(qatomic_read(&pending_cpus))) { QEMU_LOCK_GUARD(&qemu_cpu_list_lock); if (!cpu->has_waiter) { /* Not counted in pending_cpus, let the exclusive item * run. Since we have the lock, just set cpu->running to true * while holding it; no need to check pending_cpus again. */ - atomic_set(&cpu->running, false); + qatomic_set(&cpu->running, false); exclusive_idle(); /* Now pending_cpus is zero. */ - atomic_set(&cpu->running, true); + qatomic_set(&cpu->running, true); } else { /* Counted in pending_cpus, go ahead and release the * waiter at cpu_exec_end. @@ -268,7 +268,7 @@ void cpu_exec_start(CPUState *cpu) /* Mark cpu as not executing, and release pending exclusive ops. */ void cpu_exec_end(CPUState *cpu) { - atomic_set(&cpu->running, false); + qatomic_set(&cpu->running, false); /* Write cpu->running before reading pending_cpus. */ smp_mb(); @@ -288,11 +288,11 @@ void cpu_exec_end(CPUState *cpu) * see cpu->running == false, and it can ignore this CPU until the * next cpu_exec_start. */ - if (unlikely(atomic_read(&pending_cpus))) { + if (unlikely(qatomic_read(&pending_cpus))) { QEMU_LOCK_GUARD(&qemu_cpu_list_lock); if (cpu->has_waiter) { cpu->has_waiter = false; - atomic_set(&pending_cpus, pending_cpus - 1); + qatomic_set(&pending_cpus, pending_cpus - 1); if (pending_cpus == 1) { qemu_cond_signal(&exclusive_cond); } @@ -346,7 +346,7 @@ void process_queued_cpu_work(CPUState *cpu) if (wi->free) { g_free(wi); } else { - atomic_mb_set(&wi->done, true); + qatomic_mb_set(&wi->done, true); } } qemu_mutex_unlock(&cpu->work_mutex); diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs deleted file mode 100644 index f1965b1a68..0000000000 --- a/crypto/Makefile.objs +++ /dev/null @@ -1,43 +0,0 @@ -crypto-obj-y = init.o -crypto-obj-y += hash.o -crypto-obj-$(CONFIG_NETTLE) += hash-nettle.o -crypto-obj-$(if $(CONFIG_NETTLE),n,$(CONFIG_GCRYPT)) += hash-gcrypt.o -crypto-obj-$(if $(CONFIG_NETTLE),n,$(if $(CONFIG_GCRYPT),n,y)) += hash-glib.o -crypto-obj-y += hmac.o -crypto-obj-$(CONFIG_NETTLE) += hmac-nettle.o -crypto-obj-$(CONFIG_GCRYPT_HMAC) += hmac-gcrypt.o -crypto-obj-$(if $(CONFIG_NETTLE),n,$(if $(CONFIG_GCRYPT_HMAC),n,y)) += hmac-glib.o -crypto-obj-y += aes.o -crypto-obj-y += desrfb.o -crypto-obj-y += cipher.o -crypto-obj-$(CONFIG_AF_ALG) += afalg.o -crypto-obj-$(CONFIG_AF_ALG) += cipher-afalg.o -crypto-obj-$(CONFIG_AF_ALG) += hash-afalg.o -crypto-obj-$(CONFIG_GNUTLS) += tls-cipher-suites.o -crypto-obj-y += tlscreds.o -crypto-obj-y += tlscredsanon.o -crypto-obj-y += tlscredspsk.o -crypto-obj-y += tlscredsx509.o -crypto-obj-y += tlssession.o -crypto-obj-y += secret_common.o -crypto-obj-y += secret.o -crypto-obj-$(CONFIG_SECRET_KEYRING) += secret_keyring.o -crypto-obj-y += pbkdf.o -crypto-obj-$(CONFIG_NETTLE) += pbkdf-nettle.o -crypto-obj-$(if $(CONFIG_NETTLE),n,$(CONFIG_GCRYPT)) += pbkdf-gcrypt.o -crypto-obj-$(if $(CONFIG_NETTLE),n,$(if $(CONFIG_GCRYPT),n,y)) += pbkdf-stub.o -crypto-obj-y += ivgen.o -crypto-obj-y += ivgen-essiv.o -crypto-obj-y += ivgen-plain.o -crypto-obj-y += ivgen-plain64.o -crypto-obj-y += afsplit.o -crypto-obj-$(CONFIG_QEMU_PRIVATE_XTS) += xts.o -crypto-obj-y += block.o -crypto-obj-y += block-qcow.o -crypto-obj-y += block-luks.o - -util-obj-$(CONFIG_GCRYPT) += random-gcrypt.o -util-obj-$(if $(CONFIG_GCRYPT),n,$(CONFIG_GNUTLS)) += random-gnutls.o -util-obj-$(if $(CONFIG_GCRYPT),n,$(if $(CONFIG_GNUTLS),n,$(CONFIG_RNG_NONE))) += random-none.o -util-obj-$(if $(CONFIG_GCRYPT),n,$(if $(CONFIG_GNUTLS),n,$(if $(CONFIG_RNG_NONE),n,y))) += random-platform.o -util-obj-y += aes.o init.o diff --git a/crypto/aes.c b/crypto/aes.c index 0f6a195af8..af72ff7779 100644 --- a/crypto/aes.c +++ b/crypto/aes.c @@ -1080,9 +1080,9 @@ int AES_set_encrypt_key(const unsigned char *userKey, const int bits, rk = key->rd_key; - if (bits==128) + if (bits == 128) key->rounds = 10; - else if (bits==192) + else if (bits == 192) key->rounds = 12; else key->rounds = 14; @@ -1182,7 +1182,7 @@ int AES_set_decrypt_key(const unsigned char *userKey, const int bits, rk = key->rd_key; /* invert the order of the round keys: */ - for (i = 0, j = 4*(key->rounds); i < j; i += 4, j -= 4) { + for (i = 0, j = 4 * (key->rounds); i < j; i += 4, j -= 4) { temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; @@ -1599,54 +1599,3 @@ void AES_decrypt(const unsigned char *in, unsigned char *out, } #endif /* AES_ASM */ - -void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, - const unsigned long length, const AES_KEY *key, - unsigned char *ivec, const int enc) -{ - - unsigned long n; - unsigned long len = length; - unsigned char tmp[AES_BLOCK_SIZE]; - - assert(in && out && key && ivec); - - if (enc) { - while (len >= AES_BLOCK_SIZE) { - for(n=0; n < AES_BLOCK_SIZE; ++n) - tmp[n] = in[n] ^ ivec[n]; - AES_encrypt(tmp, out, key); - memcpy(ivec, out, AES_BLOCK_SIZE); - len -= AES_BLOCK_SIZE; - in += AES_BLOCK_SIZE; - out += AES_BLOCK_SIZE; - } - if (len) { - for(n=0; n < len; ++n) - tmp[n] = in[n] ^ ivec[n]; - for(n=len; n < AES_BLOCK_SIZE; ++n) - tmp[n] = ivec[n]; - AES_encrypt(tmp, tmp, key); - memcpy(out, tmp, AES_BLOCK_SIZE); - memcpy(ivec, tmp, AES_BLOCK_SIZE); - } - } else { - while (len >= AES_BLOCK_SIZE) { - memcpy(tmp, in, AES_BLOCK_SIZE); - AES_decrypt(in, out, key); - for(n=0; n < AES_BLOCK_SIZE; ++n) - out[n] ^= ivec[n]; - memcpy(ivec, tmp, AES_BLOCK_SIZE); - len -= AES_BLOCK_SIZE; - in += AES_BLOCK_SIZE; - out += AES_BLOCK_SIZE; - } - if (len) { - memcpy(tmp, in, AES_BLOCK_SIZE); - AES_decrypt(tmp, tmp, key); - for(n=0; n < len; ++n) - out[n] = tmp[n] ^ ivec[n]; - memcpy(ivec, tmp, AES_BLOCK_SIZE); - } - } -} diff --git a/crypto/afalgpriv.h b/crypto/afalgpriv.h index f6550b5c51..5a2393f1b7 100644 --- a/crypto/afalgpriv.h +++ b/crypto/afalgpriv.h @@ -15,6 +15,7 @@ #define QCRYPTO_AFALGPRIV_H #include +#include "crypto/cipher.h" #define SALG_TYPE_LEN_MAX 14 #define SALG_NAME_LEN_MAX 64 @@ -32,6 +33,8 @@ typedef struct QCryptoAFAlg QCryptoAFAlg; struct QCryptoAFAlg { + QCryptoCipher base; + int tfmfd; int opfd; struct msghdr *msg; diff --git a/crypto/block-luks.c b/crypto/block-luks.c index 564caa1094..fe8f04ffb2 100644 --- a/crypto/block-luks.c +++ b/crypto/block-luks.c @@ -1885,7 +1885,7 @@ static int qcrypto_block_luks_get_info(QCryptoBlock *block, { QCryptoBlockLUKS *luks = block->opaque; QCryptoBlockInfoLUKSSlot *slot; - QCryptoBlockInfoLUKSSlotList *slots = NULL, **prev = &info->u.luks.slots; + QCryptoBlockInfoLUKSSlotList **tail = &info->u.luks.slots; size_t i; info->u.luks.cipher_alg = luks->cipher_alg; @@ -1902,10 +1902,7 @@ static int qcrypto_block_luks_get_info(QCryptoBlock *block, sizeof(luks->header.uuid)); for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) { - slots = g_new0(QCryptoBlockInfoLUKSSlotList, 1); - *prev = slots; - - slots->value = slot = g_new0(QCryptoBlockInfoLUKSSlot, 1); + slot = g_new0(QCryptoBlockInfoLUKSSlot, 1); slot->active = luks->header.key_slots[i].active == QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED; slot->key_offset = luks->header.key_slots[i].key_offset_sector @@ -1917,7 +1914,7 @@ static int qcrypto_block_luks_get_info(QCryptoBlock *block, slot->stripes = luks->header.key_slots[i].stripes; } - prev = &slots->next; + QAPI_LIST_APPEND(tail, slot); } return 0; diff --git a/crypto/cipher-afalg.c b/crypto/cipher-afalg.c index cd72284690..052355a8a9 100644 --- a/crypto/cipher-afalg.c +++ b/crypto/cipher-afalg.c @@ -58,7 +58,9 @@ qcrypto_afalg_cipher_format_name(QCryptoCipherAlgorithm alg, return name; } -QCryptoAFAlg * +static const struct QCryptoCipherDriver qcrypto_cipher_afalg_driver; + +QCryptoCipher * qcrypto_afalg_cipher_ctx_new(QCryptoCipherAlgorithm alg, QCryptoCipherMode mode, const uint8_t *key, @@ -109,7 +111,8 @@ qcrypto_afalg_cipher_ctx_new(QCryptoCipherAlgorithm alg, } afalg->cmsg = CMSG_FIRSTHDR(afalg->msg); - return afalg; + afalg->base.driver = &qcrypto_cipher_afalg_driver; + return &afalg->base; } static int @@ -117,9 +120,9 @@ qcrypto_afalg_cipher_setiv(QCryptoCipher *cipher, const uint8_t *iv, size_t niv, Error **errp) { + QCryptoAFAlg *afalg = container_of(cipher, QCryptoAFAlg, base); struct af_alg_iv *alg_iv; size_t expect_niv; - QCryptoAFAlg *afalg = cipher->opaque; expect_niv = qcrypto_cipher_get_iv_len(cipher->alg, cipher->mode); if (niv != expect_niv) { @@ -200,8 +203,9 @@ qcrypto_afalg_cipher_encrypt(QCryptoCipher *cipher, const void *in, void *out, size_t len, Error **errp) { - return qcrypto_afalg_cipher_op(cipher->opaque, in, out, - len, true, errp); + QCryptoAFAlg *afalg = container_of(cipher, QCryptoAFAlg, base); + + return qcrypto_afalg_cipher_op(afalg, in, out, len, true, errp); } static int @@ -209,16 +213,19 @@ qcrypto_afalg_cipher_decrypt(QCryptoCipher *cipher, const void *in, void *out, size_t len, Error **errp) { - return qcrypto_afalg_cipher_op(cipher->opaque, in, out, - len, false, errp); + QCryptoAFAlg *afalg = container_of(cipher, QCryptoAFAlg, base); + + return qcrypto_afalg_cipher_op(afalg, in, out, len, false, errp); } static void qcrypto_afalg_comm_ctx_free(QCryptoCipher *cipher) { - qcrypto_afalg_comm_free(cipher->opaque); + QCryptoAFAlg *afalg = container_of(cipher, QCryptoAFAlg, base); + + qcrypto_afalg_comm_free(afalg); } -struct QCryptoCipherDriver qcrypto_cipher_afalg_driver = { +static const struct QCryptoCipherDriver qcrypto_cipher_afalg_driver = { .cipher_encrypt = qcrypto_afalg_cipher_encrypt, .cipher_decrypt = qcrypto_afalg_cipher_decrypt, .cipher_setiv = qcrypto_afalg_cipher_setiv, diff --git a/crypto/cipher-builtin.c b/crypto/cipher-builtin.c deleted file mode 100644 index 35cf7820d9..0000000000 --- a/crypto/cipher-builtin.c +++ /dev/null @@ -1,532 +0,0 @@ -/* - * QEMU Crypto cipher built-in algorithms - * - * Copyright (c) 2015 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" -#include "crypto/aes.h" -#include "crypto/desrfb.h" -#include "crypto/xts.h" -#include "cipherpriv.h" - -typedef struct QCryptoCipherBuiltinAESContext QCryptoCipherBuiltinAESContext; -struct QCryptoCipherBuiltinAESContext { - AES_KEY enc; - AES_KEY dec; -}; -typedef struct QCryptoCipherBuiltinAES QCryptoCipherBuiltinAES; -struct QCryptoCipherBuiltinAES { - QCryptoCipherBuiltinAESContext key; - QCryptoCipherBuiltinAESContext key_tweak; - uint8_t iv[AES_BLOCK_SIZE]; -}; -typedef struct QCryptoCipherBuiltinDESRFB QCryptoCipherBuiltinDESRFB; -struct QCryptoCipherBuiltinDESRFB { - uint8_t *key; - size_t nkey; -}; - -typedef struct QCryptoCipherBuiltin QCryptoCipherBuiltin; -struct QCryptoCipherBuiltin { - union { - QCryptoCipherBuiltinAES aes; - QCryptoCipherBuiltinDESRFB desrfb; - } state; - size_t blocksize; - void (*free)(QCryptoCipher *cipher); - int (*setiv)(QCryptoCipher *cipher, - const uint8_t *iv, size_t niv, - Error **errp); - int (*encrypt)(QCryptoCipher *cipher, - const void *in, - void *out, - size_t len, - Error **errp); - int (*decrypt)(QCryptoCipher *cipher, - const void *in, - void *out, - size_t len, - Error **errp); -}; - - -static void qcrypto_cipher_free_aes(QCryptoCipher *cipher) -{ - QCryptoCipherBuiltin *ctxt = cipher->opaque; - - g_free(ctxt); - cipher->opaque = NULL; -} - - -static void qcrypto_cipher_aes_ecb_encrypt(const AES_KEY *key, - const void *in, - void *out, - size_t len) -{ - const uint8_t *inptr = in; - uint8_t *outptr = out; - while (len) { - if (len > AES_BLOCK_SIZE) { - AES_encrypt(inptr, outptr, key); - inptr += AES_BLOCK_SIZE; - outptr += AES_BLOCK_SIZE; - len -= AES_BLOCK_SIZE; - } else { - uint8_t tmp1[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE]; - memcpy(tmp1, inptr, len); - /* Fill with 0 to avoid valgrind uninitialized reads */ - memset(tmp1 + len, 0, sizeof(tmp1) - len); - AES_encrypt(tmp1, tmp2, key); - memcpy(outptr, tmp2, len); - len = 0; - } - } -} - - -static void qcrypto_cipher_aes_ecb_decrypt(const AES_KEY *key, - const void *in, - void *out, - size_t len) -{ - const uint8_t *inptr = in; - uint8_t *outptr = out; - while (len) { - if (len > AES_BLOCK_SIZE) { - AES_decrypt(inptr, outptr, key); - inptr += AES_BLOCK_SIZE; - outptr += AES_BLOCK_SIZE; - len -= AES_BLOCK_SIZE; - } else { - uint8_t tmp1[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE]; - memcpy(tmp1, inptr, len); - /* Fill with 0 to avoid valgrind uninitialized reads */ - memset(tmp1 + len, 0, sizeof(tmp1) - len); - AES_decrypt(tmp1, tmp2, key); - memcpy(outptr, tmp2, len); - len = 0; - } - } -} - - -static void qcrypto_cipher_aes_xts_encrypt(const void *ctx, - size_t length, - uint8_t *dst, - const uint8_t *src) -{ - const QCryptoCipherBuiltinAESContext *aesctx = ctx; - - qcrypto_cipher_aes_ecb_encrypt(&aesctx->enc, src, dst, length); -} - - -static void qcrypto_cipher_aes_xts_decrypt(const void *ctx, - size_t length, - uint8_t *dst, - const uint8_t *src) -{ - const QCryptoCipherBuiltinAESContext *aesctx = ctx; - - qcrypto_cipher_aes_ecb_decrypt(&aesctx->dec, src, dst, length); -} - - -static int qcrypto_cipher_encrypt_aes(QCryptoCipher *cipher, - const void *in, - void *out, - size_t len, - Error **errp) -{ - QCryptoCipherBuiltin *ctxt = cipher->opaque; - - switch (cipher->mode) { - case QCRYPTO_CIPHER_MODE_ECB: - qcrypto_cipher_aes_ecb_encrypt(&ctxt->state.aes.key.enc, - in, out, len); - break; - case QCRYPTO_CIPHER_MODE_CBC: - AES_cbc_encrypt(in, out, len, - &ctxt->state.aes.key.enc, - ctxt->state.aes.iv, 1); - break; - case QCRYPTO_CIPHER_MODE_XTS: - xts_encrypt(&ctxt->state.aes.key, - &ctxt->state.aes.key_tweak, - qcrypto_cipher_aes_xts_encrypt, - qcrypto_cipher_aes_xts_decrypt, - ctxt->state.aes.iv, - len, out, in); - break; - default: - g_assert_not_reached(); - } - - return 0; -} - - -static int qcrypto_cipher_decrypt_aes(QCryptoCipher *cipher, - const void *in, - void *out, - size_t len, - Error **errp) -{ - QCryptoCipherBuiltin *ctxt = cipher->opaque; - - switch (cipher->mode) { - case QCRYPTO_CIPHER_MODE_ECB: - qcrypto_cipher_aes_ecb_decrypt(&ctxt->state.aes.key.dec, - in, out, len); - break; - case QCRYPTO_CIPHER_MODE_CBC: - AES_cbc_encrypt(in, out, len, - &ctxt->state.aes.key.dec, - ctxt->state.aes.iv, 0); - break; - case QCRYPTO_CIPHER_MODE_XTS: - xts_decrypt(&ctxt->state.aes.key, - &ctxt->state.aes.key_tweak, - qcrypto_cipher_aes_xts_encrypt, - qcrypto_cipher_aes_xts_decrypt, - ctxt->state.aes.iv, - len, out, in); - break; - default: - g_assert_not_reached(); - } - - return 0; -} - -static int qcrypto_cipher_setiv_aes(QCryptoCipher *cipher, - const uint8_t *iv, size_t niv, - Error **errp) -{ - QCryptoCipherBuiltin *ctxt = cipher->opaque; - if (niv != AES_BLOCK_SIZE) { - error_setg(errp, "IV must be %d bytes not %zu", - AES_BLOCK_SIZE, niv); - return -1; - } - - memcpy(ctxt->state.aes.iv, iv, AES_BLOCK_SIZE); - - return 0; -} - - - - -static QCryptoCipherBuiltin * -qcrypto_cipher_init_aes(QCryptoCipherMode mode, - const uint8_t *key, size_t nkey, - Error **errp) -{ - QCryptoCipherBuiltin *ctxt; - - if (mode != QCRYPTO_CIPHER_MODE_CBC && - mode != QCRYPTO_CIPHER_MODE_ECB && - mode != QCRYPTO_CIPHER_MODE_XTS) { - error_setg(errp, "Unsupported cipher mode %s", - QCryptoCipherMode_str(mode)); - return NULL; - } - - ctxt = g_new0(QCryptoCipherBuiltin, 1); - - if (mode == QCRYPTO_CIPHER_MODE_XTS) { - if (AES_set_encrypt_key(key, nkey * 4, &ctxt->state.aes.key.enc) != 0) { - error_setg(errp, "Failed to set encryption key"); - goto error; - } - - if (AES_set_decrypt_key(key, nkey * 4, &ctxt->state.aes.key.dec) != 0) { - error_setg(errp, "Failed to set decryption key"); - goto error; - } - - if (AES_set_encrypt_key(key + (nkey / 2), nkey * 4, - &ctxt->state.aes.key_tweak.enc) != 0) { - error_setg(errp, "Failed to set encryption key"); - goto error; - } - - if (AES_set_decrypt_key(key + (nkey / 2), nkey * 4, - &ctxt->state.aes.key_tweak.dec) != 0) { - error_setg(errp, "Failed to set decryption key"); - goto error; - } - } else { - if (AES_set_encrypt_key(key, nkey * 8, &ctxt->state.aes.key.enc) != 0) { - error_setg(errp, "Failed to set encryption key"); - goto error; - } - - if (AES_set_decrypt_key(key, nkey * 8, &ctxt->state.aes.key.dec) != 0) { - error_setg(errp, "Failed to set decryption key"); - goto error; - } - } - - ctxt->blocksize = AES_BLOCK_SIZE; - ctxt->free = qcrypto_cipher_free_aes; - ctxt->setiv = qcrypto_cipher_setiv_aes; - ctxt->encrypt = qcrypto_cipher_encrypt_aes; - ctxt->decrypt = qcrypto_cipher_decrypt_aes; - - return ctxt; - - error: - g_free(ctxt); - return NULL; -} - - -static void qcrypto_cipher_free_des_rfb(QCryptoCipher *cipher) -{ - QCryptoCipherBuiltin *ctxt = cipher->opaque; - - g_free(ctxt->state.desrfb.key); - g_free(ctxt); - cipher->opaque = NULL; -} - - -static int qcrypto_cipher_encrypt_des_rfb(QCryptoCipher *cipher, - const void *in, - void *out, - size_t len, - Error **errp) -{ - QCryptoCipherBuiltin *ctxt = cipher->opaque; - size_t i; - - if (len % 8) { - error_setg(errp, "Buffer size must be multiple of 8 not %zu", - len); - return -1; - } - - deskey(ctxt->state.desrfb.key, EN0); - - for (i = 0; i < len; i += 8) { - des((void *)in + i, out + i); - } - - return 0; -} - - -static int qcrypto_cipher_decrypt_des_rfb(QCryptoCipher *cipher, - const void *in, - void *out, - size_t len, - Error **errp) -{ - QCryptoCipherBuiltin *ctxt = cipher->opaque; - size_t i; - - if (len % 8) { - error_setg(errp, "Buffer size must be multiple of 8 not %zu", - len); - return -1; - } - - deskey(ctxt->state.desrfb.key, DE1); - - for (i = 0; i < len; i += 8) { - des((void *)in + i, out + i); - } - - return 0; -} - - -static int qcrypto_cipher_setiv_des_rfb(QCryptoCipher *cipher, - const uint8_t *iv, size_t niv, - Error **errp) -{ - error_setg(errp, "Setting IV is not supported"); - return -1; -} - - -static QCryptoCipherBuiltin * -qcrypto_cipher_init_des_rfb(QCryptoCipherMode mode, - const uint8_t *key, size_t nkey, - Error **errp) -{ - QCryptoCipherBuiltin *ctxt; - - if (mode != QCRYPTO_CIPHER_MODE_ECB) { - error_setg(errp, "Unsupported cipher mode %s", - QCryptoCipherMode_str(mode)); - return NULL; - } - - ctxt = g_new0(QCryptoCipherBuiltin, 1); - - ctxt->state.desrfb.key = g_new0(uint8_t, nkey); - memcpy(ctxt->state.desrfb.key, key, nkey); - ctxt->state.desrfb.nkey = nkey; - - ctxt->blocksize = 8; - ctxt->free = qcrypto_cipher_free_des_rfb; - ctxt->setiv = qcrypto_cipher_setiv_des_rfb; - ctxt->encrypt = qcrypto_cipher_encrypt_des_rfb; - ctxt->decrypt = qcrypto_cipher_decrypt_des_rfb; - - return ctxt; -} - - -bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, - QCryptoCipherMode mode) -{ - switch (alg) { - case QCRYPTO_CIPHER_ALG_DES_RFB: - case QCRYPTO_CIPHER_ALG_AES_128: - case QCRYPTO_CIPHER_ALG_AES_192: - case QCRYPTO_CIPHER_ALG_AES_256: - break; - default: - return false; - } - - switch (mode) { - case QCRYPTO_CIPHER_MODE_ECB: - case QCRYPTO_CIPHER_MODE_CBC: - case QCRYPTO_CIPHER_MODE_XTS: - return true; - case QCRYPTO_CIPHER_MODE_CTR: - return false; - default: - return false; - } -} - - -static QCryptoCipherBuiltin *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, - QCryptoCipherMode mode, - const uint8_t *key, - size_t nkey, - Error **errp) -{ - QCryptoCipherBuiltin *ctxt; - - switch (mode) { - case QCRYPTO_CIPHER_MODE_ECB: - case QCRYPTO_CIPHER_MODE_CBC: - case QCRYPTO_CIPHER_MODE_XTS: - break; - default: - error_setg(errp, "Unsupported cipher mode %s", - QCryptoCipherMode_str(mode)); - return NULL; - } - - if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) { - return NULL; - } - - switch (alg) { - case QCRYPTO_CIPHER_ALG_DES_RFB: - ctxt = qcrypto_cipher_init_des_rfb(mode, key, nkey, errp); - break; - case QCRYPTO_CIPHER_ALG_AES_128: - case QCRYPTO_CIPHER_ALG_AES_192: - case QCRYPTO_CIPHER_ALG_AES_256: - ctxt = qcrypto_cipher_init_aes(mode, key, nkey, errp); - break; - default: - error_setg(errp, - "Unsupported cipher algorithm %s", - QCryptoCipherAlgorithm_str(alg)); - return NULL; - } - - return ctxt; -} - -static void -qcrypto_builtin_cipher_ctx_free(QCryptoCipher *cipher) -{ - QCryptoCipherBuiltin *ctxt; - - ctxt = cipher->opaque; - ctxt->free(cipher); -} - - -static int -qcrypto_builtin_cipher_encrypt(QCryptoCipher *cipher, - const void *in, - void *out, - size_t len, - Error **errp) -{ - QCryptoCipherBuiltin *ctxt = cipher->opaque; - - if (len % ctxt->blocksize) { - error_setg(errp, "Length %zu must be a multiple of block size %zu", - len, ctxt->blocksize); - return -1; - } - - return ctxt->encrypt(cipher, in, out, len, errp); -} - - -static int -qcrypto_builtin_cipher_decrypt(QCryptoCipher *cipher, - const void *in, - void *out, - size_t len, - Error **errp) -{ - QCryptoCipherBuiltin *ctxt = cipher->opaque; - - if (len % ctxt->blocksize) { - error_setg(errp, "Length %zu must be a multiple of block size %zu", - len, ctxt->blocksize); - return -1; - } - - return ctxt->decrypt(cipher, in, out, len, errp); -} - - -static int -qcrypto_builtin_cipher_setiv(QCryptoCipher *cipher, - const uint8_t *iv, size_t niv, - Error **errp) -{ - QCryptoCipherBuiltin *ctxt = cipher->opaque; - - return ctxt->setiv(cipher, iv, niv, errp); -} - - -static struct QCryptoCipherDriver qcrypto_cipher_lib_driver = { - .cipher_encrypt = qcrypto_builtin_cipher_encrypt, - .cipher_decrypt = qcrypto_builtin_cipher_decrypt, - .cipher_setiv = qcrypto_builtin_cipher_setiv, - .cipher_free = qcrypto_builtin_cipher_ctx_free, -}; diff --git a/crypto/cipher-builtin.c.inc b/crypto/cipher-builtin.c.inc new file mode 100644 index 0000000000..7597cf4a10 --- /dev/null +++ b/crypto/cipher-builtin.c.inc @@ -0,0 +1,435 @@ +/* + * QEMU Crypto cipher built-in algorithms + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "crypto/aes.h" +#include "crypto/desrfb.h" +#include "crypto/xts.h" + +typedef struct QCryptoCipherBuiltinAESContext QCryptoCipherBuiltinAESContext; +struct QCryptoCipherBuiltinAESContext { + AES_KEY enc; + AES_KEY dec; +}; + +typedef struct QCryptoCipherBuiltinAES QCryptoCipherBuiltinAES; +struct QCryptoCipherBuiltinAES { + QCryptoCipher base; + QCryptoCipherBuiltinAESContext key; + QCryptoCipherBuiltinAESContext key_tweak; + uint8_t iv[AES_BLOCK_SIZE]; +}; + + +static inline bool qcrypto_length_check(size_t len, size_t blocksize, + Error **errp) +{ + if (unlikely(len & (blocksize - 1))) { + error_setg(errp, "Length %zu must be a multiple of block size %zu", + len, blocksize); + return false; + } + return true; +} + +static void qcrypto_cipher_ctx_free(QCryptoCipher *cipher) +{ + g_free(cipher); +} + +static int qcrypto_cipher_no_setiv(QCryptoCipher *cipher, + const uint8_t *iv, size_t niv, + Error **errp) +{ + error_setg(errp, "Setting IV is not supported"); + return -1; +} + +static void do_aes_encrypt_ecb(const void *vctx, + size_t len, + uint8_t *out, + const uint8_t *in) +{ + const QCryptoCipherBuiltinAESContext *ctx = vctx; + + /* We have already verified that len % AES_BLOCK_SIZE == 0. */ + while (len) { + AES_encrypt(in, out, &ctx->enc); + in += AES_BLOCK_SIZE; + out += AES_BLOCK_SIZE; + len -= AES_BLOCK_SIZE; + } +} + +static void do_aes_decrypt_ecb(const void *vctx, + size_t len, + uint8_t *out, + const uint8_t *in) +{ + const QCryptoCipherBuiltinAESContext *ctx = vctx; + + /* We have already verified that len % AES_BLOCK_SIZE == 0. */ + while (len) { + AES_decrypt(in, out, &ctx->dec); + in += AES_BLOCK_SIZE; + out += AES_BLOCK_SIZE; + len -= AES_BLOCK_SIZE; + } +} + +static void do_aes_encrypt_cbc(const AES_KEY *key, + size_t len, + uint8_t *out, + const uint8_t *in, + uint8_t *ivec) +{ + uint8_t tmp[AES_BLOCK_SIZE]; + size_t n; + + /* We have already verified that len % AES_BLOCK_SIZE == 0. */ + while (len) { + for (n = 0; n < AES_BLOCK_SIZE; ++n) { + tmp[n] = in[n] ^ ivec[n]; + } + AES_encrypt(tmp, out, key); + memcpy(ivec, out, AES_BLOCK_SIZE); + len -= AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + out += AES_BLOCK_SIZE; + } +} + +static void do_aes_decrypt_cbc(const AES_KEY *key, + size_t len, + uint8_t *out, + const uint8_t *in, + uint8_t *ivec) +{ + uint8_t tmp[AES_BLOCK_SIZE]; + size_t n; + + /* We have already verified that len % AES_BLOCK_SIZE == 0. */ + while (len) { + memcpy(tmp, in, AES_BLOCK_SIZE); + AES_decrypt(in, out, key); + for (n = 0; n < AES_BLOCK_SIZE; ++n) { + out[n] ^= ivec[n]; + } + memcpy(ivec, tmp, AES_BLOCK_SIZE); + len -= AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + out += AES_BLOCK_SIZE; + } +} + +static int qcrypto_cipher_aes_encrypt_ecb(QCryptoCipher *cipher, + const void *in, void *out, + size_t len, Error **errp) +{ + QCryptoCipherBuiltinAES *ctx + = container_of(cipher, QCryptoCipherBuiltinAES, base); + + if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) { + return -1; + } + do_aes_encrypt_ecb(&ctx->key, len, out, in); + return 0; +} + +static int qcrypto_cipher_aes_decrypt_ecb(QCryptoCipher *cipher, + const void *in, void *out, + size_t len, Error **errp) +{ + QCryptoCipherBuiltinAES *ctx + = container_of(cipher, QCryptoCipherBuiltinAES, base); + + if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) { + return -1; + } + do_aes_decrypt_ecb(&ctx->key, len, out, in); + return 0; +} + +static int qcrypto_cipher_aes_encrypt_cbc(QCryptoCipher *cipher, + const void *in, void *out, + size_t len, Error **errp) +{ + QCryptoCipherBuiltinAES *ctx + = container_of(cipher, QCryptoCipherBuiltinAES, base); + + if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) { + return -1; + } + do_aes_encrypt_cbc(&ctx->key.enc, len, out, in, ctx->iv); + return 0; +} + +static int qcrypto_cipher_aes_decrypt_cbc(QCryptoCipher *cipher, + const void *in, void *out, + size_t len, Error **errp) +{ + QCryptoCipherBuiltinAES *ctx + = container_of(cipher, QCryptoCipherBuiltinAES, base); + + if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) { + return -1; + } + do_aes_decrypt_cbc(&ctx->key.dec, len, out, in, ctx->iv); + return 0; +} + +static int qcrypto_cipher_aes_encrypt_xts(QCryptoCipher *cipher, + const void *in, void *out, + size_t len, Error **errp) +{ + QCryptoCipherBuiltinAES *ctx + = container_of(cipher, QCryptoCipherBuiltinAES, base); + + if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) { + return -1; + } + xts_encrypt(&ctx->key, &ctx->key_tweak, + do_aes_encrypt_ecb, do_aes_decrypt_ecb, + ctx->iv, len, out, in); + return 0; +} + +static int qcrypto_cipher_aes_decrypt_xts(QCryptoCipher *cipher, + const void *in, void *out, + size_t len, Error **errp) +{ + QCryptoCipherBuiltinAES *ctx + = container_of(cipher, QCryptoCipherBuiltinAES, base); + + if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) { + return -1; + } + xts_decrypt(&ctx->key, &ctx->key_tweak, + do_aes_encrypt_ecb, do_aes_decrypt_ecb, + ctx->iv, len, out, in); + return 0; +} + + +static int qcrypto_cipher_aes_setiv(QCryptoCipher *cipher, const uint8_t *iv, + size_t niv, Error **errp) +{ + QCryptoCipherBuiltinAES *ctx + = container_of(cipher, QCryptoCipherBuiltinAES, base); + + if (niv != AES_BLOCK_SIZE) { + error_setg(errp, "IV must be %d bytes not %zu", + AES_BLOCK_SIZE, niv); + return -1; + } + + memcpy(ctx->iv, iv, AES_BLOCK_SIZE); + return 0; +} + +static const struct QCryptoCipherDriver qcrypto_cipher_aes_driver_ecb = { + .cipher_encrypt = qcrypto_cipher_aes_encrypt_ecb, + .cipher_decrypt = qcrypto_cipher_aes_decrypt_ecb, + .cipher_setiv = qcrypto_cipher_no_setiv, + .cipher_free = qcrypto_cipher_ctx_free, +}; + +static const struct QCryptoCipherDriver qcrypto_cipher_aes_driver_cbc = { + .cipher_encrypt = qcrypto_cipher_aes_encrypt_cbc, + .cipher_decrypt = qcrypto_cipher_aes_decrypt_cbc, + .cipher_setiv = qcrypto_cipher_aes_setiv, + .cipher_free = qcrypto_cipher_ctx_free, +}; + +static const struct QCryptoCipherDriver qcrypto_cipher_aes_driver_xts = { + .cipher_encrypt = qcrypto_cipher_aes_encrypt_xts, + .cipher_decrypt = qcrypto_cipher_aes_decrypt_xts, + .cipher_setiv = qcrypto_cipher_aes_setiv, + .cipher_free = qcrypto_cipher_ctx_free, +}; + + +typedef struct QCryptoCipherBuiltinDESRFB QCryptoCipherBuiltinDESRFB; +struct QCryptoCipherBuiltinDESRFB { + QCryptoCipher base; + + /* C.f. alg_key_len[QCRYPTO_CIPHER_ALG_DES_RFB] */ + uint8_t key[8]; +}; + +static int qcrypto_cipher_encrypt_des_rfb(QCryptoCipher *cipher, + const void *in, void *out, + size_t len, Error **errp) +{ + QCryptoCipherBuiltinDESRFB *ctx + = container_of(cipher, QCryptoCipherBuiltinDESRFB, base); + size_t i; + + if (!qcrypto_length_check(len, 8, errp)) { + return -1; + } + + deskey(ctx->key, EN0); + + for (i = 0; i < len; i += 8) { + des((void *)in + i, out + i); + } + + return 0; +} + +static int qcrypto_cipher_decrypt_des_rfb(QCryptoCipher *cipher, + const void *in, void *out, + size_t len, Error **errp) +{ + QCryptoCipherBuiltinDESRFB *ctx + = container_of(cipher, QCryptoCipherBuiltinDESRFB, base); + size_t i; + + if (!qcrypto_length_check(len, 8, errp)) { + return -1; + } + + deskey(ctx->key, DE1); + + for (i = 0; i < len; i += 8) { + des((void *)in + i, out + i); + } + + return 0; +} + +static const struct QCryptoCipherDriver qcrypto_cipher_des_rfb_driver = { + .cipher_encrypt = qcrypto_cipher_encrypt_des_rfb, + .cipher_decrypt = qcrypto_cipher_decrypt_des_rfb, + .cipher_setiv = qcrypto_cipher_no_setiv, + .cipher_free = qcrypto_cipher_ctx_free, +}; + +bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, + QCryptoCipherMode mode) +{ + switch (alg) { + case QCRYPTO_CIPHER_ALG_DES_RFB: + return mode == QCRYPTO_CIPHER_MODE_ECB; + case QCRYPTO_CIPHER_ALG_AES_128: + case QCRYPTO_CIPHER_ALG_AES_192: + case QCRYPTO_CIPHER_ALG_AES_256: + switch (mode) { + case QCRYPTO_CIPHER_MODE_ECB: + case QCRYPTO_CIPHER_MODE_CBC: + case QCRYPTO_CIPHER_MODE_XTS: + return true; + default: + return false; + } + break; + default: + return false; + } +} + +static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, + QCryptoCipherMode mode, + const uint8_t *key, + size_t nkey, + Error **errp) +{ + if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) { + return NULL; + } + + switch (alg) { + case QCRYPTO_CIPHER_ALG_DES_RFB: + if (mode == QCRYPTO_CIPHER_MODE_ECB) { + QCryptoCipherBuiltinDESRFB *ctx; + + ctx = g_new0(QCryptoCipherBuiltinDESRFB, 1); + ctx->base.driver = &qcrypto_cipher_des_rfb_driver; + memcpy(ctx->key, key, sizeof(ctx->key)); + + return &ctx->base; + } + goto bad_mode; + + case QCRYPTO_CIPHER_ALG_AES_128: + case QCRYPTO_CIPHER_ALG_AES_192: + case QCRYPTO_CIPHER_ALG_AES_256: + { + QCryptoCipherBuiltinAES *ctx; + const QCryptoCipherDriver *drv; + + switch (mode) { + case QCRYPTO_CIPHER_MODE_ECB: + drv = &qcrypto_cipher_aes_driver_ecb; + break; + case QCRYPTO_CIPHER_MODE_CBC: + drv = &qcrypto_cipher_aes_driver_cbc; + break; + case QCRYPTO_CIPHER_MODE_XTS: + drv = &qcrypto_cipher_aes_driver_xts; + break; + default: + goto bad_mode; + } + + ctx = g_new0(QCryptoCipherBuiltinAES, 1); + ctx->base.driver = drv; + + if (mode == QCRYPTO_CIPHER_MODE_XTS) { + nkey /= 2; + if (AES_set_encrypt_key(key + nkey, nkey * 8, + &ctx->key_tweak.enc)) { + error_setg(errp, "Failed to set encryption key"); + goto error; + } + if (AES_set_decrypt_key(key + nkey, nkey * 8, + &ctx->key_tweak.dec)) { + error_setg(errp, "Failed to set decryption key"); + goto error; + } + } + if (AES_set_encrypt_key(key, nkey * 8, &ctx->key.enc)) { + error_setg(errp, "Failed to set encryption key"); + goto error; + } + if (AES_set_decrypt_key(key, nkey * 8, &ctx->key.dec)) { + error_setg(errp, "Failed to set decryption key"); + goto error; + } + + return &ctx->base; + + error: + g_free(ctx); + return NULL; + } + + default: + error_setg(errp, + "Unsupported cipher algorithm %s", + QCryptoCipherAlgorithm_str(alg)); + return NULL; + } + + bad_mode: + error_setg(errp, "Unsupported cipher mode %s", + QCryptoCipherMode_str(mode)); + return NULL; +} diff --git a/crypto/cipher-gcrypt.c b/crypto/cipher-gcrypt.c.inc similarity index 52% rename from crypto/cipher-gcrypt.c rename to crypto/cipher-gcrypt.c.inc index 2864099527..42d4137534 100644 --- a/crypto/cipher-gcrypt.c +++ b/crypto/cipher-gcrypt.c.inc @@ -18,15 +18,12 @@ * */ -#include "qemu/osdep.h" #ifdef CONFIG_QEMU_PRIVATE_XTS #include "crypto/xts.h" #endif -#include "cipherpriv.h" #include - bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, QCryptoCipherMode mode) { @@ -58,69 +55,224 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, } } -typedef struct QCryptoCipherGcrypt QCryptoCipherGcrypt; -struct QCryptoCipherGcrypt { +typedef struct QCryptoCipherGcrypt { + QCryptoCipher base; gcry_cipher_hd_t handle; size_t blocksize; #ifdef CONFIG_QEMU_PRIVATE_XTS gcry_cipher_hd_t tweakhandle; - /* Initialization vector or Counter */ - uint8_t *iv; + uint8_t iv[XTS_BLOCK_SIZE]; #endif -}; +} QCryptoCipherGcrypt; -static void -qcrypto_gcrypt_cipher_free_ctx(QCryptoCipherGcrypt *ctx, - QCryptoCipherMode mode) + +static void qcrypto_gcrypt_ctx_free(QCryptoCipher *cipher) { - if (!ctx) { - return; - } + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base); gcry_cipher_close(ctx->handle); -#ifdef CONFIG_QEMU_PRIVATE_XTS - if (mode == QCRYPTO_CIPHER_MODE_XTS) { - gcry_cipher_close(ctx->tweakhandle); - } - g_free(ctx->iv); -#endif g_free(ctx); } +static int qcrypto_gcrypt_encrypt(QCryptoCipher *cipher, const void *in, + void *out, size_t len, Error **errp) +{ + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base); + gcry_error_t err; -static QCryptoCipherGcrypt *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, - QCryptoCipherMode mode, - const uint8_t *key, - size_t nkey, - Error **errp) + if (len & (ctx->blocksize - 1)) { + error_setg(errp, "Length %zu must be a multiple of block size %zu", + len, ctx->blocksize); + return -1; + } + + err = gcry_cipher_encrypt(ctx->handle, out, len, in, len); + if (err != 0) { + error_setg(errp, "Cannot encrypt data: %s", gcry_strerror(err)); + return -1; + } + + return 0; +} + + +static int qcrypto_gcrypt_decrypt(QCryptoCipher *cipher, const void *in, + void *out, size_t len, Error **errp) +{ + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base); + gcry_error_t err; + + if (len & (ctx->blocksize - 1)) { + error_setg(errp, "Length %zu must be a multiple of block size %zu", + len, ctx->blocksize); + return -1; + } + + err = gcry_cipher_decrypt(ctx->handle, out, len, in, len); + if (err != 0) { + error_setg(errp, "Cannot decrypt data: %s", + gcry_strerror(err)); + return -1; + } + + return 0; +} + +static int qcrypto_gcrypt_setiv(QCryptoCipher *cipher, + const uint8_t *iv, size_t niv, + Error **errp) +{ + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base); + gcry_error_t err; + + if (niv != ctx->blocksize) { + error_setg(errp, "Expected IV size %zu not %zu", + ctx->blocksize, niv); + return -1; + } + + gcry_cipher_reset(ctx->handle); + err = gcry_cipher_setiv(ctx->handle, iv, niv); + if (err != 0) { + error_setg(errp, "Cannot set IV: %s", gcry_strerror(err)); + return -1; + } + + return 0; +} + +static int qcrypto_gcrypt_ctr_setiv(QCryptoCipher *cipher, + const uint8_t *iv, size_t niv, + Error **errp) +{ + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base); + gcry_error_t err; + + if (niv != ctx->blocksize) { + error_setg(errp, "Expected IV size %zu not %zu", + ctx->blocksize, niv); + return -1; + } + + err = gcry_cipher_setctr(ctx->handle, iv, niv); + if (err != 0) { + error_setg(errp, "Cannot set Counter: %s", gcry_strerror(err)); + return -1; + } + + return 0; +} + + +static const struct QCryptoCipherDriver qcrypto_gcrypt_driver = { + .cipher_encrypt = qcrypto_gcrypt_encrypt, + .cipher_decrypt = qcrypto_gcrypt_decrypt, + .cipher_setiv = qcrypto_gcrypt_setiv, + .cipher_free = qcrypto_gcrypt_ctx_free, +}; + +static const struct QCryptoCipherDriver qcrypto_gcrypt_ctr_driver = { + .cipher_encrypt = qcrypto_gcrypt_encrypt, + .cipher_decrypt = qcrypto_gcrypt_decrypt, + .cipher_setiv = qcrypto_gcrypt_ctr_setiv, + .cipher_free = qcrypto_gcrypt_ctx_free, +}; + +#ifdef CONFIG_QEMU_PRIVATE_XTS +static void qcrypto_gcrypt_xts_ctx_free(QCryptoCipher *cipher) +{ + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base); + + gcry_cipher_close(ctx->tweakhandle); + qcrypto_gcrypt_ctx_free(cipher); +} + +static void qcrypto_gcrypt_xts_wrape(const void *ctx, size_t length, + uint8_t *dst, const uint8_t *src) +{ + gcry_error_t err; + err = gcry_cipher_encrypt((gcry_cipher_hd_t)ctx, dst, length, src, length); + g_assert(err == 0); +} + +static void qcrypto_gcrypt_xts_wrapd(const void *ctx, size_t length, + uint8_t *dst, const uint8_t *src) +{ + gcry_error_t err; + err = gcry_cipher_decrypt((gcry_cipher_hd_t)ctx, dst, length, src, length); + g_assert(err == 0); +} + +static int qcrypto_gcrypt_xts_encrypt(QCryptoCipher *cipher, const void *in, + void *out, size_t len, Error **errp) +{ + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base); + + if (len & (ctx->blocksize - 1)) { + error_setg(errp, "Length %zu must be a multiple of block size %zu", + len, ctx->blocksize); + return -1; + } + + xts_encrypt(ctx->handle, ctx->tweakhandle, + qcrypto_gcrypt_xts_wrape, qcrypto_gcrypt_xts_wrapd, + ctx->iv, len, out, in); + return 0; +} + +static int qcrypto_gcrypt_xts_decrypt(QCryptoCipher *cipher, const void *in, + void *out, size_t len, Error **errp) +{ + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base); + + if (len & (ctx->blocksize - 1)) { + error_setg(errp, "Length %zu must be a multiple of block size %zu", + len, ctx->blocksize); + return -1; + } + + xts_decrypt(ctx->handle, ctx->tweakhandle, + qcrypto_gcrypt_xts_wrape, qcrypto_gcrypt_xts_wrapd, + ctx->iv, len, out, in); + return 0; +} + +static int qcrypto_gcrypt_xts_setiv(QCryptoCipher *cipher, + const uint8_t *iv, size_t niv, + Error **errp) +{ + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base); + + if (niv != ctx->blocksize) { + error_setg(errp, "Expected IV size %zu not %zu", + ctx->blocksize, niv); + return -1; + } + + memcpy(ctx->iv, iv, niv); + return 0; +} + +static const struct QCryptoCipherDriver qcrypto_gcrypt_xts_driver = { + .cipher_encrypt = qcrypto_gcrypt_xts_encrypt, + .cipher_decrypt = qcrypto_gcrypt_xts_decrypt, + .cipher_setiv = qcrypto_gcrypt_xts_setiv, + .cipher_free = qcrypto_gcrypt_xts_ctx_free, +}; +#endif /* CONFIG_QEMU_PRIVATE_XTS */ + + +static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, + QCryptoCipherMode mode, + const uint8_t *key, + size_t nkey, + Error **errp) { QCryptoCipherGcrypt *ctx; + const QCryptoCipherDriver *drv; gcry_error_t err; int gcryalg, gcrymode; - switch (mode) { - case QCRYPTO_CIPHER_MODE_ECB: - gcrymode = GCRY_CIPHER_MODE_ECB; - break; - case QCRYPTO_CIPHER_MODE_XTS: -#ifdef CONFIG_QEMU_PRIVATE_XTS - gcrymode = GCRY_CIPHER_MODE_ECB; -#else - gcrymode = GCRY_CIPHER_MODE_XTS; -#endif - break; - case QCRYPTO_CIPHER_MODE_CBC: - gcrymode = GCRY_CIPHER_MODE_CBC; - break; - case QCRYPTO_CIPHER_MODE_CTR: - gcrymode = GCRY_CIPHER_MODE_CTR; - break; - default: - error_setg(errp, "Unsupported cipher mode %s", - QCryptoCipherMode_str(mode)); - return NULL; - } - if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) { return NULL; } @@ -129,54 +281,70 @@ static QCryptoCipherGcrypt *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, case QCRYPTO_CIPHER_ALG_DES_RFB: 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; - default: error_setg(errp, "Unsupported cipher algorithm %s", QCryptoCipherAlgorithm_str(alg)); return NULL; } + drv = &qcrypto_gcrypt_driver; + switch (mode) { + case QCRYPTO_CIPHER_MODE_ECB: + gcrymode = GCRY_CIPHER_MODE_ECB; + break; + case QCRYPTO_CIPHER_MODE_XTS: +#ifdef CONFIG_QEMU_PRIVATE_XTS + drv = &qcrypto_gcrypt_xts_driver; + gcrymode = GCRY_CIPHER_MODE_ECB; +#else + gcrymode = GCRY_CIPHER_MODE_XTS; +#endif + break; + case QCRYPTO_CIPHER_MODE_CBC: + gcrymode = GCRY_CIPHER_MODE_CBC; + 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; + } + ctx = g_new0(QCryptoCipherGcrypt, 1); + ctx->base.driver = drv; err = gcry_cipher_open(&ctx->handle, gcryalg, gcrymode, 0); if (err != 0) { @@ -184,8 +352,16 @@ static QCryptoCipherGcrypt *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, gcry_strerror(err)); goto error; } + ctx->blocksize = gcry_cipher_get_algo_blklen(gcryalg); + #ifdef CONFIG_QEMU_PRIVATE_XTS if (mode == QCRYPTO_CIPHER_MODE_XTS) { + if (ctx->blocksize != XTS_BLOCK_SIZE) { + error_setg(errp, + "Cipher block size %zu must equal XTS block size %d", + ctx->blocksize, XTS_BLOCK_SIZE); + goto error; + } err = gcry_cipher_open(&ctx->tweakhandle, gcryalg, gcrymode, 0); if (err != 0) { error_setg(errp, "Cannot initialize cipher: %s", @@ -203,220 +379,31 @@ static QCryptoCipherGcrypt *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, uint8_t *rfbkey = qcrypto_cipher_munge_des_rfb_key(key, nkey); err = gcry_cipher_setkey(ctx->handle, rfbkey, nkey); g_free(rfbkey); - ctx->blocksize = 8; } else { #ifdef CONFIG_QEMU_PRIVATE_XTS if (mode == QCRYPTO_CIPHER_MODE_XTS) { nkey /= 2; - err = gcry_cipher_setkey(ctx->handle, key, nkey); + err = gcry_cipher_setkey(ctx->tweakhandle, key + nkey, nkey); if (err != 0) { - error_setg(errp, "Cannot set key: %s", - gcry_strerror(err)); + error_setg(errp, "Cannot set key: %s", gcry_strerror(err)); goto error; } - err = gcry_cipher_setkey(ctx->tweakhandle, key + nkey, nkey); - } else { -#endif - err = gcry_cipher_setkey(ctx->handle, key, nkey); -#ifdef CONFIG_QEMU_PRIVATE_XTS } #endif - if (err != 0) { - error_setg(errp, "Cannot set key: %s", - gcry_strerror(err)); - goto error; - } - switch (alg) { - case QCRYPTO_CIPHER_ALG_AES_128: - case QCRYPTO_CIPHER_ALG_AES_192: - case QCRYPTO_CIPHER_ALG_AES_256: - case QCRYPTO_CIPHER_ALG_SERPENT_128: - case QCRYPTO_CIPHER_ALG_SERPENT_192: - case QCRYPTO_CIPHER_ALG_SERPENT_256: - case QCRYPTO_CIPHER_ALG_TWOFISH_128: - case QCRYPTO_CIPHER_ALG_TWOFISH_256: - ctx->blocksize = 16; - break; - case QCRYPTO_CIPHER_ALG_3DES: - case QCRYPTO_CIPHER_ALG_CAST5_128: - ctx->blocksize = 8; - break; - default: - g_assert_not_reached(); - } + err = gcry_cipher_setkey(ctx->handle, key, nkey); + } + if (err != 0) { + error_setg(errp, "Cannot set key: %s", gcry_strerror(err)); + goto error; } -#ifdef CONFIG_QEMU_PRIVATE_XTS - if (mode == QCRYPTO_CIPHER_MODE_XTS) { - if (ctx->blocksize != XTS_BLOCK_SIZE) { - error_setg(errp, - "Cipher block size %zu must equal XTS block size %d", - ctx->blocksize, XTS_BLOCK_SIZE); - goto error; - } - ctx->iv = g_new0(uint8_t, ctx->blocksize); - } -#endif - - return ctx; + return &ctx->base; error: - qcrypto_gcrypt_cipher_free_ctx(ctx, mode); +#ifdef CONFIG_QEMU_PRIVATE_XTS + gcry_cipher_close(ctx->tweakhandle); +#endif + gcry_cipher_close(ctx->handle); + g_free(ctx); return NULL; } - - -static void -qcrypto_gcrypt_cipher_ctx_free(QCryptoCipher *cipher) -{ - qcrypto_gcrypt_cipher_free_ctx(cipher->opaque, cipher->mode); -} - - -#ifdef CONFIG_QEMU_PRIVATE_XTS -static void qcrypto_gcrypt_xts_encrypt(const void *ctx, - size_t length, - uint8_t *dst, - const uint8_t *src) -{ - gcry_error_t err; - err = gcry_cipher_encrypt((gcry_cipher_hd_t)ctx, dst, length, src, length); - g_assert(err == 0); -} - -static void qcrypto_gcrypt_xts_decrypt(const void *ctx, - size_t length, - uint8_t *dst, - const uint8_t *src) -{ - gcry_error_t err; - err = gcry_cipher_decrypt((gcry_cipher_hd_t)ctx, dst, length, src, length); - g_assert(err == 0); -} -#endif - -static int -qcrypto_gcrypt_cipher_encrypt(QCryptoCipher *cipher, - const void *in, - void *out, - size_t len, - Error **errp) -{ - QCryptoCipherGcrypt *ctx = cipher->opaque; - gcry_error_t err; - - if (len % ctx->blocksize) { - error_setg(errp, "Length %zu must be a multiple of block size %zu", - len, ctx->blocksize); - return -1; - } - -#ifdef CONFIG_QEMU_PRIVATE_XTS - if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) { - xts_encrypt(ctx->handle, ctx->tweakhandle, - qcrypto_gcrypt_xts_encrypt, - qcrypto_gcrypt_xts_decrypt, - ctx->iv, len, out, in); - return 0; - } -#endif - - err = gcry_cipher_encrypt(ctx->handle, - out, len, - in, len); - if (err != 0) { - error_setg(errp, "Cannot encrypt data: %s", - gcry_strerror(err)); - return -1; - } - - return 0; -} - - -static int -qcrypto_gcrypt_cipher_decrypt(QCryptoCipher *cipher, - const void *in, - void *out, - size_t len, - Error **errp) -{ - QCryptoCipherGcrypt *ctx = cipher->opaque; - gcry_error_t err; - - if (len % ctx->blocksize) { - error_setg(errp, "Length %zu must be a multiple of block size %zu", - len, ctx->blocksize); - return -1; - } - -#ifdef CONFIG_QEMU_PRIVATE_XTS - if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) { - xts_decrypt(ctx->handle, ctx->tweakhandle, - qcrypto_gcrypt_xts_encrypt, - qcrypto_gcrypt_xts_decrypt, - ctx->iv, len, out, in); - return 0; - } -#endif - - err = gcry_cipher_decrypt(ctx->handle, - out, len, - in, len); - if (err != 0) { - error_setg(errp, "Cannot decrypt data: %s", - gcry_strerror(err)); - return -1; - } - - return 0; -} - -static int -qcrypto_gcrypt_cipher_setiv(QCryptoCipher *cipher, - const uint8_t *iv, size_t niv, - Error **errp) -{ - QCryptoCipherGcrypt *ctx = cipher->opaque; - gcry_error_t err; - - if (niv != ctx->blocksize) { - error_setg(errp, "Expected IV size %zu not %zu", - ctx->blocksize, niv); - return -1; - } - -#ifdef CONFIG_QEMU_PRIVATE_XTS - if (ctx->iv) { - memcpy(ctx->iv, iv, niv); - return 0; - } -#endif - - if (cipher->mode == QCRYPTO_CIPHER_MODE_CTR) { - err = gcry_cipher_setctr(ctx->handle, iv, niv); - if (err != 0) { - error_setg(errp, "Cannot set Counter: %s", - gcry_strerror(err)); - return -1; - } - } else { - gcry_cipher_reset(ctx->handle); - err = gcry_cipher_setiv(ctx->handle, iv, niv); - if (err != 0) { - error_setg(errp, "Cannot set IV: %s", - gcry_strerror(err)); - return -1; - } - } - - return 0; -} - - -static struct QCryptoCipherDriver qcrypto_cipher_lib_driver = { - .cipher_encrypt = qcrypto_gcrypt_cipher_encrypt, - .cipher_decrypt = qcrypto_gcrypt_cipher_decrypt, - .cipher_setiv = qcrypto_gcrypt_cipher_setiv, - .cipher_free = qcrypto_gcrypt_cipher_ctx_free, -}; diff --git a/crypto/cipher-nettle.c b/crypto/cipher-nettle.c deleted file mode 100644 index 7e9a4cc199..0000000000 --- a/crypto/cipher-nettle.c +++ /dev/null @@ -1,733 +0,0 @@ -/* - * QEMU Crypto cipher nettle algorithms - * - * Copyright (c) 2015 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "qemu/osdep.h" -#ifdef CONFIG_QEMU_PRIVATE_XTS -#include "crypto/xts.h" -#endif -#include "cipherpriv.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#ifndef CONFIG_QEMU_PRIVATE_XTS -#include -#endif - -typedef void (*QCryptoCipherNettleFuncWrapper)(const void *ctx, - size_t length, - uint8_t *dst, - const uint8_t *src); - -#if CONFIG_NETTLE_VERSION_MAJOR < 3 -typedef nettle_crypt_func * QCryptoCipherNettleFuncNative; -typedef void * cipher_ctx_t; -typedef unsigned cipher_length_t; - -#define cast5_set_key cast128_set_key - -#define aes128_ctx aes_ctx -#define aes192_ctx aes_ctx -#define aes256_ctx aes_ctx -#define aes128_set_encrypt_key(c, k) \ - aes_set_encrypt_key(c, 16, k) -#define aes192_set_encrypt_key(c, k) \ - aes_set_encrypt_key(c, 24, k) -#define aes256_set_encrypt_key(c, k) \ - aes_set_encrypt_key(c, 32, k) -#define aes128_set_decrypt_key(c, k) \ - aes_set_decrypt_key(c, 16, k) -#define aes192_set_decrypt_key(c, k) \ - aes_set_decrypt_key(c, 24, k) -#define aes256_set_decrypt_key(c, k) \ - aes_set_decrypt_key(c, 32, k) -#define aes128_encrypt aes_encrypt -#define aes192_encrypt aes_encrypt -#define aes256_encrypt aes_encrypt -#define aes128_decrypt aes_decrypt -#define aes192_decrypt aes_decrypt -#define aes256_decrypt aes_decrypt -#else -typedef nettle_cipher_func * QCryptoCipherNettleFuncNative; -typedef const void * cipher_ctx_t; -typedef size_t cipher_length_t; -#endif - -typedef struct QCryptoNettleAES128 { - struct aes128_ctx enc; - struct aes128_ctx dec; -} QCryptoNettleAES128; - -typedef struct QCryptoNettleAES192 { - struct aes192_ctx enc; - struct aes192_ctx dec; -} QCryptoNettleAES192; - -typedef struct QCryptoNettleAES256 { - struct aes256_ctx enc; - struct aes256_ctx dec; -} QCryptoNettleAES256; - -static void aes128_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, - uint8_t *dst, const uint8_t *src) -{ - const QCryptoNettleAES128 *aesctx = ctx; - aes128_encrypt(&aesctx->enc, length, dst, src); -} - -static void aes128_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, - uint8_t *dst, const uint8_t *src) -{ - const QCryptoNettleAES128 *aesctx = ctx; - aes128_decrypt(&aesctx->dec, length, dst, src); -} - -static void aes192_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, - uint8_t *dst, const uint8_t *src) -{ - const QCryptoNettleAES192 *aesctx = ctx; - aes192_encrypt(&aesctx->enc, length, dst, src); -} - -static void aes192_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, - uint8_t *dst, const uint8_t *src) -{ - const QCryptoNettleAES192 *aesctx = ctx; - aes192_decrypt(&aesctx->dec, length, dst, src); -} - -static void aes256_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, - uint8_t *dst, const uint8_t *src) -{ - const QCryptoNettleAES256 *aesctx = ctx; - aes256_encrypt(&aesctx->enc, length, dst, src); -} - -static void aes256_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, - uint8_t *dst, const uint8_t *src) -{ - const QCryptoNettleAES256 *aesctx = ctx; - aes256_decrypt(&aesctx->dec, length, dst, src); -} - -static void des_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, - uint8_t *dst, const uint8_t *src) -{ - des_encrypt(ctx, length, dst, src); -} - -static void des_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, - uint8_t *dst, const uint8_t *src) -{ - des_decrypt(ctx, length, dst, src); -} - -static void des3_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, - uint8_t *dst, const uint8_t *src) -{ - des3_encrypt(ctx, length, dst, src); -} - -static void des3_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, - uint8_t *dst, const uint8_t *src) -{ - des3_decrypt(ctx, length, dst, src); -} - -static void cast128_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, - uint8_t *dst, const uint8_t *src) -{ - cast128_encrypt(ctx, length, dst, src); -} - -static void cast128_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, - uint8_t *dst, const uint8_t *src) -{ - cast128_decrypt(ctx, length, dst, src); -} - -static void serpent_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, - uint8_t *dst, const uint8_t *src) -{ - serpent_encrypt(ctx, length, dst, src); -} - -static void serpent_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, - uint8_t *dst, const uint8_t *src) -{ - serpent_decrypt(ctx, length, dst, src); -} - -static void twofish_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, - uint8_t *dst, const uint8_t *src) -{ - twofish_encrypt(ctx, length, dst, src); -} - -static void twofish_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, - uint8_t *dst, const uint8_t *src) -{ - twofish_decrypt(ctx, length, dst, src); -} - -static void aes128_encrypt_wrapper(const void *ctx, size_t length, - uint8_t *dst, const uint8_t *src) -{ - const QCryptoNettleAES128 *aesctx = ctx; - aes128_encrypt(&aesctx->enc, length, dst, src); -} - -static void aes128_decrypt_wrapper(const void *ctx, size_t length, - uint8_t *dst, const uint8_t *src) -{ - const QCryptoNettleAES128 *aesctx = ctx; - aes128_decrypt(&aesctx->dec, length, dst, src); -} - -static void aes192_encrypt_wrapper(const void *ctx, size_t length, - uint8_t *dst, const uint8_t *src) -{ - const QCryptoNettleAES192 *aesctx = ctx; - aes192_encrypt(&aesctx->enc, length, dst, src); -} - -static void aes192_decrypt_wrapper(const void *ctx, size_t length, - uint8_t *dst, const uint8_t *src) -{ - const QCryptoNettleAES192 *aesctx = ctx; - aes192_decrypt(&aesctx->dec, length, dst, src); -} - -static void aes256_encrypt_wrapper(const void *ctx, size_t length, - uint8_t *dst, const uint8_t *src) -{ - const QCryptoNettleAES256 *aesctx = ctx; - aes256_encrypt(&aesctx->enc, length, dst, src); -} - -static void aes256_decrypt_wrapper(const void *ctx, size_t length, - uint8_t *dst, const uint8_t *src) -{ - const QCryptoNettleAES256 *aesctx = ctx; - aes256_decrypt(&aesctx->dec, length, dst, src); -} - -static void des_encrypt_wrapper(const void *ctx, size_t length, - uint8_t *dst, const uint8_t *src) -{ - des_encrypt(ctx, length, dst, src); -} - -static void des_decrypt_wrapper(const void *ctx, size_t length, - uint8_t *dst, const uint8_t *src) -{ - des_decrypt(ctx, length, dst, src); -} - -static void des3_encrypt_wrapper(const void *ctx, size_t length, - uint8_t *dst, const uint8_t *src) -{ - des3_encrypt(ctx, length, dst, src); -} - -static void des3_decrypt_wrapper(const void *ctx, size_t length, - uint8_t *dst, const uint8_t *src) -{ - des3_decrypt(ctx, length, dst, src); -} - -static void cast128_encrypt_wrapper(const void *ctx, size_t length, - uint8_t *dst, const uint8_t *src) -{ - cast128_encrypt(ctx, length, dst, src); -} - -static void cast128_decrypt_wrapper(const void *ctx, size_t length, - uint8_t *dst, const uint8_t *src) -{ - cast128_decrypt(ctx, length, dst, src); -} - -static void serpent_encrypt_wrapper(const void *ctx, size_t length, - uint8_t *dst, const uint8_t *src) -{ - serpent_encrypt(ctx, length, dst, src); -} - -static void serpent_decrypt_wrapper(const void *ctx, size_t length, - uint8_t *dst, const uint8_t *src) -{ - serpent_decrypt(ctx, length, dst, src); -} - -static void twofish_encrypt_wrapper(const void *ctx, size_t length, - uint8_t *dst, const uint8_t *src) -{ - twofish_encrypt(ctx, length, dst, src); -} - -static void twofish_decrypt_wrapper(const void *ctx, size_t length, - uint8_t *dst, const uint8_t *src) -{ - twofish_decrypt(ctx, length, dst, src); -} - -typedef struct QCryptoCipherNettle QCryptoCipherNettle; -struct QCryptoCipherNettle { - /* Primary cipher context for all modes */ - void *ctx; - /* Second cipher context for XTS mode only */ - void *ctx_tweak; - /* Cipher callbacks for both contexts */ - QCryptoCipherNettleFuncNative alg_encrypt_native; - QCryptoCipherNettleFuncNative alg_decrypt_native; - QCryptoCipherNettleFuncWrapper alg_encrypt_wrapper; - QCryptoCipherNettleFuncWrapper alg_decrypt_wrapper; - /* Initialization vector or Counter */ - uint8_t *iv; - size_t blocksize; -}; - -bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, - QCryptoCipherMode mode) -{ - switch (alg) { - case QCRYPTO_CIPHER_ALG_DES_RFB: - case QCRYPTO_CIPHER_ALG_3DES: - case QCRYPTO_CIPHER_ALG_AES_128: - case QCRYPTO_CIPHER_ALG_AES_192: - case QCRYPTO_CIPHER_ALG_AES_256: - case QCRYPTO_CIPHER_ALG_CAST5_128: - case QCRYPTO_CIPHER_ALG_SERPENT_128: - case QCRYPTO_CIPHER_ALG_SERPENT_192: - case QCRYPTO_CIPHER_ALG_SERPENT_256: - case QCRYPTO_CIPHER_ALG_TWOFISH_128: - case QCRYPTO_CIPHER_ALG_TWOFISH_192: - case QCRYPTO_CIPHER_ALG_TWOFISH_256: - break; - default: - return false; - } - - switch (mode) { - case QCRYPTO_CIPHER_MODE_ECB: - case QCRYPTO_CIPHER_MODE_CBC: - case QCRYPTO_CIPHER_MODE_XTS: - case QCRYPTO_CIPHER_MODE_CTR: - return true; - default: - return false; - } -} - - -static void -qcrypto_nettle_cipher_free_ctx(QCryptoCipherNettle *ctx) -{ - if (!ctx) { - return; - } - - g_free(ctx->iv); - g_free(ctx->ctx); - g_free(ctx->ctx_tweak); - g_free(ctx); -} - - -static QCryptoCipherNettle *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, - QCryptoCipherMode mode, - const uint8_t *key, - size_t nkey, - Error **errp) -{ - QCryptoCipherNettle *ctx; - uint8_t *rfbkey; - - switch (mode) { - case QCRYPTO_CIPHER_MODE_ECB: - case QCRYPTO_CIPHER_MODE_CBC: - case QCRYPTO_CIPHER_MODE_XTS: - case QCRYPTO_CIPHER_MODE_CTR: - break; - default: - error_setg(errp, "Unsupported cipher mode %s", - QCryptoCipherMode_str(mode)); - return NULL; - } - - if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) { - return NULL; - } - - ctx = g_new0(QCryptoCipherNettle, 1); - - switch (alg) { - case QCRYPTO_CIPHER_ALG_DES_RFB: - ctx->ctx = g_new0(struct des_ctx, 1); - rfbkey = qcrypto_cipher_munge_des_rfb_key(key, nkey); - des_set_key(ctx->ctx, rfbkey); - g_free(rfbkey); - - ctx->alg_encrypt_native = des_encrypt_native; - ctx->alg_decrypt_native = des_decrypt_native; - ctx->alg_encrypt_wrapper = des_encrypt_wrapper; - ctx->alg_decrypt_wrapper = des_decrypt_wrapper; - - ctx->blocksize = DES_BLOCK_SIZE; - break; - - case QCRYPTO_CIPHER_ALG_3DES: - ctx->ctx = g_new0(struct des3_ctx, 1); - des3_set_key(ctx->ctx, key); - - ctx->alg_encrypt_native = des3_encrypt_native; - ctx->alg_decrypt_native = des3_decrypt_native; - ctx->alg_encrypt_wrapper = des3_encrypt_wrapper; - ctx->alg_decrypt_wrapper = des3_decrypt_wrapper; - - ctx->blocksize = DES3_BLOCK_SIZE; - break; - - case QCRYPTO_CIPHER_ALG_AES_128: - ctx->ctx = g_new0(QCryptoNettleAES128, 1); - - if (mode == QCRYPTO_CIPHER_MODE_XTS) { - ctx->ctx_tweak = g_new0(QCryptoNettleAES128, 1); - - nkey /= 2; - aes128_set_encrypt_key(&((QCryptoNettleAES128 *)ctx->ctx)->enc, - key); - aes128_set_decrypt_key(&((QCryptoNettleAES128 *)ctx->ctx)->dec, - key); - - aes128_set_encrypt_key(&((QCryptoNettleAES128 *)ctx->ctx_tweak)-> - enc, key + nkey); - aes128_set_decrypt_key(&((QCryptoNettleAES128 *)ctx->ctx_tweak)-> - dec, key + nkey); - } else { - aes128_set_encrypt_key(&((QCryptoNettleAES128 *)ctx->ctx)->enc, - key); - aes128_set_decrypt_key(&((QCryptoNettleAES128 *)ctx->ctx)->dec, - key); - } - - ctx->alg_encrypt_native = aes128_encrypt_native; - ctx->alg_decrypt_native = aes128_decrypt_native; - ctx->alg_encrypt_wrapper = aes128_encrypt_wrapper; - ctx->alg_decrypt_wrapper = aes128_decrypt_wrapper; - - ctx->blocksize = AES_BLOCK_SIZE; - break; - - case QCRYPTO_CIPHER_ALG_AES_192: - ctx->ctx = g_new0(QCryptoNettleAES192, 1); - - if (mode == QCRYPTO_CIPHER_MODE_XTS) { - ctx->ctx_tweak = g_new0(QCryptoNettleAES192, 1); - - nkey /= 2; - aes192_set_encrypt_key(&((QCryptoNettleAES192 *)ctx->ctx)->enc, - key); - aes192_set_decrypt_key(&((QCryptoNettleAES192 *)ctx->ctx)->dec, - key); - - aes192_set_encrypt_key(&((QCryptoNettleAES192 *)ctx->ctx_tweak)-> - enc, key + nkey); - aes192_set_decrypt_key(&((QCryptoNettleAES192 *)ctx->ctx_tweak)-> - dec, key + nkey); - } else { - aes192_set_encrypt_key(&((QCryptoNettleAES192 *)ctx->ctx)->enc, - key); - aes192_set_decrypt_key(&((QCryptoNettleAES192 *)ctx->ctx)->dec, - key); - } - - ctx->alg_encrypt_native = aes192_encrypt_native; - ctx->alg_decrypt_native = aes192_decrypt_native; - ctx->alg_encrypt_wrapper = aes192_encrypt_wrapper; - ctx->alg_decrypt_wrapper = aes192_decrypt_wrapper; - - ctx->blocksize = AES_BLOCK_SIZE; - break; - - case QCRYPTO_CIPHER_ALG_AES_256: - ctx->ctx = g_new0(QCryptoNettleAES256, 1); - - if (mode == QCRYPTO_CIPHER_MODE_XTS) { - ctx->ctx_tweak = g_new0(QCryptoNettleAES256, 1); - - nkey /= 2; - aes256_set_encrypt_key(&((QCryptoNettleAES256 *)ctx->ctx)->enc, - key); - aes256_set_decrypt_key(&((QCryptoNettleAES256 *)ctx->ctx)->dec, - key); - - aes256_set_encrypt_key(&((QCryptoNettleAES256 *)ctx->ctx_tweak)-> - enc, key + nkey); - aes256_set_decrypt_key(&((QCryptoNettleAES256 *)ctx->ctx_tweak)-> - dec, key + nkey); - } else { - aes256_set_encrypt_key(&((QCryptoNettleAES256 *)ctx->ctx)->enc, - key); - aes256_set_decrypt_key(&((QCryptoNettleAES256 *)ctx->ctx)->dec, - key); - } - - ctx->alg_encrypt_native = aes256_encrypt_native; - ctx->alg_decrypt_native = aes256_decrypt_native; - ctx->alg_encrypt_wrapper = aes256_encrypt_wrapper; - ctx->alg_decrypt_wrapper = aes256_decrypt_wrapper; - - ctx->blocksize = AES_BLOCK_SIZE; - break; - - case QCRYPTO_CIPHER_ALG_CAST5_128: - ctx->ctx = g_new0(struct cast128_ctx, 1); - - if (mode == QCRYPTO_CIPHER_MODE_XTS) { - ctx->ctx_tweak = g_new0(struct cast128_ctx, 1); - - nkey /= 2; - cast5_set_key(ctx->ctx, nkey, key); - cast5_set_key(ctx->ctx_tweak, nkey, key + nkey); - } else { - cast5_set_key(ctx->ctx, nkey, key); - } - - ctx->alg_encrypt_native = cast128_encrypt_native; - ctx->alg_decrypt_native = cast128_decrypt_native; - ctx->alg_encrypt_wrapper = cast128_encrypt_wrapper; - ctx->alg_decrypt_wrapper = cast128_decrypt_wrapper; - - ctx->blocksize = CAST128_BLOCK_SIZE; - break; - - case QCRYPTO_CIPHER_ALG_SERPENT_128: - case QCRYPTO_CIPHER_ALG_SERPENT_192: - case QCRYPTO_CIPHER_ALG_SERPENT_256: - ctx->ctx = g_new0(struct serpent_ctx, 1); - - if (mode == QCRYPTO_CIPHER_MODE_XTS) { - ctx->ctx_tweak = g_new0(struct serpent_ctx, 1); - - nkey /= 2; - serpent_set_key(ctx->ctx, nkey, key); - serpent_set_key(ctx->ctx_tweak, nkey, key + nkey); - } else { - serpent_set_key(ctx->ctx, nkey, key); - } - - ctx->alg_encrypt_native = serpent_encrypt_native; - ctx->alg_decrypt_native = serpent_decrypt_native; - ctx->alg_encrypt_wrapper = serpent_encrypt_wrapper; - ctx->alg_decrypt_wrapper = serpent_decrypt_wrapper; - - ctx->blocksize = SERPENT_BLOCK_SIZE; - break; - - case QCRYPTO_CIPHER_ALG_TWOFISH_128: - case QCRYPTO_CIPHER_ALG_TWOFISH_192: - case QCRYPTO_CIPHER_ALG_TWOFISH_256: - ctx->ctx = g_new0(struct twofish_ctx, 1); - - if (mode == QCRYPTO_CIPHER_MODE_XTS) { - ctx->ctx_tweak = g_new0(struct twofish_ctx, 1); - - nkey /= 2; - twofish_set_key(ctx->ctx, nkey, key); - twofish_set_key(ctx->ctx_tweak, nkey, key + nkey); - } else { - twofish_set_key(ctx->ctx, nkey, key); - } - - ctx->alg_encrypt_native = twofish_encrypt_native; - ctx->alg_decrypt_native = twofish_decrypt_native; - ctx->alg_encrypt_wrapper = twofish_encrypt_wrapper; - ctx->alg_decrypt_wrapper = twofish_decrypt_wrapper; - - ctx->blocksize = TWOFISH_BLOCK_SIZE; - break; - - default: - error_setg(errp, "Unsupported cipher algorithm %s", - QCryptoCipherAlgorithm_str(alg)); - goto error; - } - - if (mode == QCRYPTO_CIPHER_MODE_XTS && - ctx->blocksize != XTS_BLOCK_SIZE) { - error_setg(errp, "Cipher block size %zu must equal XTS block size %d", - ctx->blocksize, XTS_BLOCK_SIZE); - goto error; - } - - ctx->iv = g_new0(uint8_t, ctx->blocksize); - - return ctx; - - error: - qcrypto_nettle_cipher_free_ctx(ctx); - return NULL; -} - - -static void -qcrypto_nettle_cipher_ctx_free(QCryptoCipher *cipher) -{ - QCryptoCipherNettle *ctx; - - ctx = cipher->opaque; - qcrypto_nettle_cipher_free_ctx(ctx); -} - - -static int -qcrypto_nettle_cipher_encrypt(QCryptoCipher *cipher, - const void *in, - void *out, - size_t len, - Error **errp) -{ - QCryptoCipherNettle *ctx = cipher->opaque; - - if (len % ctx->blocksize) { - error_setg(errp, "Length %zu must be a multiple of block size %zu", - len, ctx->blocksize); - return -1; - } - - switch (cipher->mode) { - case QCRYPTO_CIPHER_MODE_ECB: - ctx->alg_encrypt_wrapper(ctx->ctx, len, out, in); - break; - - case QCRYPTO_CIPHER_MODE_CBC: - cbc_encrypt(ctx->ctx, ctx->alg_encrypt_native, - ctx->blocksize, ctx->iv, - len, out, in); - break; - - case QCRYPTO_CIPHER_MODE_XTS: -#ifdef CONFIG_QEMU_PRIVATE_XTS - xts_encrypt(ctx->ctx, ctx->ctx_tweak, - ctx->alg_encrypt_wrapper, ctx->alg_encrypt_wrapper, - ctx->iv, len, out, in); -#else - xts_encrypt_message(ctx->ctx, ctx->ctx_tweak, - ctx->alg_encrypt_native, - ctx->iv, len, out, in); -#endif - break; - - case QCRYPTO_CIPHER_MODE_CTR: - ctr_crypt(ctx->ctx, ctx->alg_encrypt_native, - ctx->blocksize, ctx->iv, - len, out, in); - break; - - default: - error_setg(errp, "Unsupported cipher mode %s", - QCryptoCipherMode_str(cipher->mode)); - return -1; - } - return 0; -} - - -static int -qcrypto_nettle_cipher_decrypt(QCryptoCipher *cipher, - const void *in, - void *out, - size_t len, - Error **errp) -{ - QCryptoCipherNettle *ctx = cipher->opaque; - - if (len % ctx->blocksize) { - error_setg(errp, "Length %zu must be a multiple of block size %zu", - len, ctx->blocksize); - return -1; - } - - switch (cipher->mode) { - case QCRYPTO_CIPHER_MODE_ECB: - ctx->alg_decrypt_wrapper(ctx->ctx, len, out, in); - break; - - case QCRYPTO_CIPHER_MODE_CBC: - cbc_decrypt(ctx->ctx, ctx->alg_decrypt_native, - ctx->blocksize, ctx->iv, - len, out, in); - break; - - case QCRYPTO_CIPHER_MODE_XTS: -#ifdef CONFIG_QEMU_PRIVATE_XTS - xts_decrypt(ctx->ctx, ctx->ctx_tweak, - ctx->alg_encrypt_wrapper, ctx->alg_decrypt_wrapper, - ctx->iv, len, out, in); -#else - xts_decrypt_message(ctx->ctx, ctx->ctx_tweak, - ctx->alg_decrypt_native, - ctx->alg_encrypt_native, - ctx->iv, len, out, in); -#endif - break; - case QCRYPTO_CIPHER_MODE_CTR: - ctr_crypt(ctx->ctx, ctx->alg_encrypt_native, - ctx->blocksize, ctx->iv, - len, out, in); - break; - - default: - error_setg(errp, "Unsupported cipher mode %s", - QCryptoCipherMode_str(cipher->mode)); - return -1; - } - return 0; -} - -static int -qcrypto_nettle_cipher_setiv(QCryptoCipher *cipher, - const uint8_t *iv, size_t niv, - Error **errp) -{ - QCryptoCipherNettle *ctx = cipher->opaque; - if (niv != ctx->blocksize) { - error_setg(errp, "Expected IV size %zu not %zu", - ctx->blocksize, niv); - return -1; - } - memcpy(ctx->iv, iv, niv); - return 0; -} - - -static struct QCryptoCipherDriver qcrypto_cipher_lib_driver = { - .cipher_encrypt = qcrypto_nettle_cipher_encrypt, - .cipher_decrypt = qcrypto_nettle_cipher_decrypt, - .cipher_setiv = qcrypto_nettle_cipher_setiv, - .cipher_free = qcrypto_nettle_cipher_ctx_free, -}; diff --git a/crypto/cipher-nettle.c.inc b/crypto/cipher-nettle.c.inc new file mode 100644 index 0000000000..cac771e4ff --- /dev/null +++ b/crypto/cipher-nettle.c.inc @@ -0,0 +1,760 @@ +/* + * QEMU Crypto cipher nettle algorithms + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#ifdef CONFIG_QEMU_PRIVATE_XTS +#include "crypto/xts.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef CONFIG_QEMU_PRIVATE_XTS +#include +#endif + +typedef void (*QCryptoCipherNettleFuncWrapper)(const void *ctx, + size_t length, + uint8_t *dst, + const uint8_t *src); + +#if CONFIG_NETTLE_VERSION_MAJOR < 3 +typedef nettle_crypt_func * QCryptoCipherNettleFuncNative; +typedef void * cipher_ctx_t; +typedef unsigned cipher_length_t; +#define CONST_CTX + +#define cast5_set_key cast128_set_key + +#define aes128_ctx aes_ctx +#define aes192_ctx aes_ctx +#define aes256_ctx aes_ctx +#define aes128_set_encrypt_key(c, k) \ + aes_set_encrypt_key(c, 16, k) +#define aes192_set_encrypt_key(c, k) \ + aes_set_encrypt_key(c, 24, k) +#define aes256_set_encrypt_key(c, k) \ + aes_set_encrypt_key(c, 32, k) +#define aes128_set_decrypt_key(c, k) \ + aes_set_decrypt_key(c, 16, k) +#define aes192_set_decrypt_key(c, k) \ + aes_set_decrypt_key(c, 24, k) +#define aes256_set_decrypt_key(c, k) \ + aes_set_decrypt_key(c, 32, k) +#define aes128_encrypt aes_encrypt +#define aes192_encrypt aes_encrypt +#define aes256_encrypt aes_encrypt +#define aes128_decrypt aes_decrypt +#define aes192_decrypt aes_decrypt +#define aes256_decrypt aes_decrypt +#else +typedef nettle_cipher_func * QCryptoCipherNettleFuncNative; +typedef const void * cipher_ctx_t; +typedef size_t cipher_length_t; +#define CONST_CTX const +#endif + +static inline bool qcrypto_length_check(size_t len, size_t blocksize, + Error **errp) +{ + if (unlikely(len & (blocksize - 1))) { + error_setg(errp, "Length %zu must be a multiple of block size %zu", + len, blocksize); + return false; + } + return true; +} + + +static void qcrypto_cipher_ctx_free(QCryptoCipher *ctx) +{ + g_free(ctx); +} + +static int qcrypto_cipher_no_setiv(QCryptoCipher *cipher, + const uint8_t *iv, size_t niv, + Error **errp) +{ + error_setg(errp, "Setting IV is not supported"); + return -1; +} + + +#define DEFINE_SETIV(NAME, TYPE, BLEN) \ +static int NAME##_setiv(QCryptoCipher *cipher, const uint8_t *iv, \ + size_t niv, Error **errp) \ +{ \ + TYPE *ctx = container_of(cipher, TYPE, base); \ + if (niv != BLEN) { \ + error_setg(errp, "Expected IV size %d not %zu", BLEN, niv); \ + return -1; \ + } \ + memcpy(ctx->iv, iv, niv); \ + return 0; \ +} + + +#define DEFINE_ECB(NAME, TYPE, BLEN, ENCRYPT, DECRYPT) \ +static int NAME##_encrypt_ecb(QCryptoCipher *cipher, const void *in, \ + void *out, size_t len, Error **errp) \ +{ \ + TYPE *ctx = container_of(cipher, TYPE, base); \ + if (!qcrypto_length_check(len, BLEN, errp)) { \ + return -1; \ + } \ + ENCRYPT(&ctx->key, len, out, in); \ + return 0; \ +} \ +static int NAME##_decrypt_ecb(QCryptoCipher *cipher, const void *in, \ + void *out, size_t len, Error **errp) \ +{ \ + TYPE *ctx = container_of(cipher, TYPE, base); \ + if (!qcrypto_length_check(len, BLEN, errp)) { \ + return -1; \ + } \ + DECRYPT(&ctx->key, len, out, in); \ + return 0; \ +} \ +static const struct QCryptoCipherDriver NAME##_driver_ecb = { \ + .cipher_encrypt = NAME##_encrypt_ecb, \ + .cipher_decrypt = NAME##_decrypt_ecb, \ + .cipher_setiv = qcrypto_cipher_no_setiv, \ + .cipher_free = qcrypto_cipher_ctx_free, \ +}; + + +#define DEFINE_CBC(NAME, TYPE, BLEN, ENCRYPT, DECRYPT) \ +static int NAME##_encrypt_cbc(QCryptoCipher *cipher, const void *in, \ + void *out, size_t len, Error **errp) \ +{ \ + TYPE *ctx = container_of(cipher, TYPE, base); \ + if (!qcrypto_length_check(len, BLEN, errp)) { \ + return -1; \ + } \ + cbc_encrypt(&ctx->key, ENCRYPT, BLEN, ctx->iv, len, out, in); \ + return 0; \ +} \ +static int NAME##_decrypt_cbc(QCryptoCipher *cipher, const void *in, \ + void *out, size_t len, Error **errp) \ +{ \ + TYPE *ctx = container_of(cipher, TYPE, base); \ + if (!qcrypto_length_check(len, BLEN, errp)) { \ + return -1; \ + } \ + cbc_decrypt(&ctx->key, DECRYPT, BLEN, ctx->iv, len, out, in); \ + return 0; \ +} \ +static const struct QCryptoCipherDriver NAME##_driver_cbc = { \ + .cipher_encrypt = NAME##_encrypt_cbc, \ + .cipher_decrypt = NAME##_decrypt_cbc, \ + .cipher_setiv = NAME##_setiv, \ + .cipher_free = qcrypto_cipher_ctx_free, \ +}; + + +#define DEFINE_CTR(NAME, TYPE, BLEN, ENCRYPT) \ +static int NAME##_encrypt_ctr(QCryptoCipher *cipher, const void *in, \ + void *out, size_t len, Error **errp) \ +{ \ + TYPE *ctx = container_of(cipher, TYPE, base); \ + if (!qcrypto_length_check(len, BLEN, errp)) { \ + return -1; \ + } \ + ctr_crypt(&ctx->key, ENCRYPT, BLEN, ctx->iv, len, out, in); \ + return 0; \ +} \ +static const struct QCryptoCipherDriver NAME##_driver_ctr = { \ + .cipher_encrypt = NAME##_encrypt_ctr, \ + .cipher_decrypt = NAME##_encrypt_ctr, \ + .cipher_setiv = NAME##_setiv, \ + .cipher_free = qcrypto_cipher_ctx_free, \ +}; + + +#ifdef CONFIG_QEMU_PRIVATE_XTS +#define DEFINE__XTS(NAME, TYPE, BLEN, ENCRYPT, DECRYPT) \ +static void NAME##_xts_wrape(const void *ctx, size_t length, \ + uint8_t *dst, const uint8_t *src) \ +{ \ + ENCRYPT((cipher_ctx_t)ctx, length, dst, src); \ +} \ +static void NAME##_xts_wrapd(const void *ctx, size_t length, \ + uint8_t *dst, const uint8_t *src) \ +{ \ + DECRYPT((cipher_ctx_t)ctx, length, dst, src); \ +} \ +static int NAME##_encrypt_xts(QCryptoCipher *cipher, const void *in, \ + void *out, size_t len, Error **errp) \ +{ \ + TYPE *ctx = container_of(cipher, TYPE, base); \ + if (!qcrypto_length_check(len, BLEN, errp)) { \ + return -1; \ + } \ + xts_encrypt(&ctx->key, &ctx->key_xts, \ + NAME##_xts_wrape, NAME##_xts_wrapd, \ + ctx->iv, len, out, in); \ + return 0; \ +} \ +static int NAME##_decrypt_xts(QCryptoCipher *cipher, const void *in, \ + void *out, size_t len, Error **errp) \ +{ \ + TYPE *ctx = container_of(cipher, TYPE, base); \ + if (!qcrypto_length_check(len, BLEN, errp)) { \ + return -1; \ + } \ + xts_decrypt(&ctx->key, &ctx->key_xts, \ + NAME##_xts_wrape, NAME##_xts_wrapd, \ + ctx->iv, len, out, in); \ + return 0; \ +} +#else +#define DEFINE__XTS(NAME, TYPE, BLEN, ENCRYPT, DECRYPT) \ +static int NAME##_encrypt_xts(QCryptoCipher *cipher, const void *in, \ + void *out, size_t len, Error **errp) \ +{ \ + TYPE *ctx = container_of(cipher, TYPE, base); \ + if (!qcrypto_length_check(len, BLEN, errp)) { \ + return -1; \ + } \ + xts_encrypt_message(&ctx->key, &ctx->key_xts, ENCRYPT, \ + ctx->iv, len, out, in); \ + return 0; \ +} \ +static int NAME##_decrypt_xts(QCryptoCipher *cipher, const void *in, \ + void *out, size_t len, Error **errp) \ +{ \ + TYPE *ctx = container_of(cipher, TYPE, base); \ + if (!qcrypto_length_check(len, BLEN, errp)) { \ + return -1; \ + } \ + xts_decrypt_message(&ctx->key, &ctx->key_xts, DECRYPT, ENCRYPT, \ + ctx->iv, len, out, in); \ + return 0; \ +} +#endif + +#define DEFINE_XTS(NAME, TYPE, BLEN, ENCRYPT, DECRYPT) \ + QEMU_BUILD_BUG_ON(BLEN != XTS_BLOCK_SIZE); \ + DEFINE__XTS(NAME, TYPE, BLEN, ENCRYPT, DECRYPT) \ +static const struct QCryptoCipherDriver NAME##_driver_xts = { \ + .cipher_encrypt = NAME##_encrypt_xts, \ + .cipher_decrypt = NAME##_decrypt_xts, \ + .cipher_setiv = NAME##_setiv, \ + .cipher_free = qcrypto_cipher_ctx_free, \ +}; + + +#define DEFINE_ECB_CBC_CTR(NAME, TYPE, BLEN, ENCRYPT, DECRYPT) \ + DEFINE_SETIV(NAME, TYPE, BLEN) \ + DEFINE_ECB(NAME, TYPE, BLEN, ENCRYPT, DECRYPT) \ + DEFINE_CBC(NAME, TYPE, BLEN, ENCRYPT, DECRYPT) \ + DEFINE_CTR(NAME, TYPE, BLEN, ENCRYPT) + +#define DEFINE_ECB_CBC_CTR_XTS(NAME, TYPE, BLEN, ENCRYPT, DECRYPT) \ + DEFINE_ECB_CBC_CTR(NAME, TYPE, BLEN, ENCRYPT, DECRYPT) \ + DEFINE_XTS(NAME, TYPE, BLEN, ENCRYPT, DECRYPT) + + +typedef struct QCryptoNettleDESRFB { + QCryptoCipher base; + struct des_ctx key; + uint8_t iv[DES_BLOCK_SIZE]; +} QCryptoNettleDESRFB; + +static void des_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + des_encrypt(ctx, length, dst, src); +} + +static void des_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + des_decrypt(ctx, length, dst, src); +} + +DEFINE_ECB_CBC_CTR(qcrypto_nettle_des_rfb, QCryptoNettleDESRFB, + DES_BLOCK_SIZE, des_encrypt_native, des_decrypt_native) + + +typedef struct QCryptoNettleDES3 { + QCryptoCipher base; + struct des3_ctx key; + uint8_t iv[DES3_BLOCK_SIZE]; +} QCryptoNettleDES3; + +static void des3_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + des3_encrypt(ctx, length, dst, src); +} + +static void des3_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + des3_decrypt(ctx, length, dst, src); +} + +DEFINE_ECB_CBC_CTR(qcrypto_nettle_des3, QCryptoNettleDES3, DES3_BLOCK_SIZE, + des3_encrypt_native, des3_decrypt_native) + + +typedef struct QCryptoNettleAES128 { + QCryptoCipher base; + uint8_t iv[AES_BLOCK_SIZE]; + /* First key from pair is encode, second key is decode. */ + struct aes128_ctx key[2], key_xts[2]; +} QCryptoNettleAES128; + +static void aes128_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + CONST_CTX struct aes128_ctx *keys = ctx; + aes128_encrypt(&keys[0], length, dst, src); +} + +static void aes128_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + CONST_CTX struct aes128_ctx *keys = ctx; + aes128_decrypt(&keys[1], length, dst, src); +} + +DEFINE_ECB_CBC_CTR_XTS(qcrypto_nettle_aes128, + QCryptoNettleAES128, AES_BLOCK_SIZE, + aes128_encrypt_native, aes128_decrypt_native) + + +typedef struct QCryptoNettleAES192 { + QCryptoCipher base; + uint8_t iv[AES_BLOCK_SIZE]; + /* First key from pair is encode, second key is decode. */ + struct aes192_ctx key[2], key_xts[2]; +} QCryptoNettleAES192; + +static void aes192_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + CONST_CTX struct aes192_ctx *keys = ctx; + aes192_encrypt(&keys[0], length, dst, src); +} + +static void aes192_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + CONST_CTX struct aes192_ctx *keys = ctx; + aes192_decrypt(&keys[1], length, dst, src); +} + +DEFINE_ECB_CBC_CTR_XTS(qcrypto_nettle_aes192, + QCryptoNettleAES192, AES_BLOCK_SIZE, + aes192_encrypt_native, aes192_decrypt_native) + + +typedef struct QCryptoNettleAES256 { + QCryptoCipher base; + uint8_t iv[AES_BLOCK_SIZE]; + /* First key from pair is encode, second key is decode. */ + struct aes256_ctx key[2], key_xts[2]; +} QCryptoNettleAES256; + +static void aes256_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + CONST_CTX struct aes256_ctx *keys = ctx; + aes256_encrypt(&keys[0], length, dst, src); +} + +static void aes256_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + CONST_CTX struct aes256_ctx *keys = ctx; + aes256_decrypt(&keys[1], length, dst, src); +} + +DEFINE_ECB_CBC_CTR_XTS(qcrypto_nettle_aes256, + QCryptoNettleAES256, AES_BLOCK_SIZE, + aes256_encrypt_native, aes256_decrypt_native) + + +typedef struct QCryptoNettleCAST128 { + QCryptoCipher base; + uint8_t iv[CAST128_BLOCK_SIZE]; + struct cast128_ctx key, key_xts; +} QCryptoNettleCAST128; + +static void cast128_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + cast128_encrypt(ctx, length, dst, src); +} + +static void cast128_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + cast128_decrypt(ctx, length, dst, src); +} + +DEFINE_ECB_CBC_CTR(qcrypto_nettle_cast128, + QCryptoNettleCAST128, CAST128_BLOCK_SIZE, + cast128_encrypt_native, cast128_decrypt_native) + + +typedef struct QCryptoNettleSerpent { + QCryptoCipher base; + uint8_t iv[SERPENT_BLOCK_SIZE]; + struct serpent_ctx key, key_xts; +} QCryptoNettleSerpent; + + +static void serpent_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + serpent_encrypt(ctx, length, dst, src); +} + +static void serpent_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + serpent_decrypt(ctx, length, dst, src); +} + +DEFINE_ECB_CBC_CTR_XTS(qcrypto_nettle_serpent, + QCryptoNettleSerpent, SERPENT_BLOCK_SIZE, + serpent_encrypt_native, serpent_decrypt_native) + + +typedef struct QCryptoNettleTwofish { + QCryptoCipher base; + uint8_t iv[TWOFISH_BLOCK_SIZE]; + struct twofish_ctx key, key_xts; +} QCryptoNettleTwofish; + +static void twofish_encrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + twofish_encrypt(ctx, length, dst, src); +} + +static void twofish_decrypt_native(cipher_ctx_t ctx, cipher_length_t length, + uint8_t *dst, const uint8_t *src) +{ + twofish_decrypt(ctx, length, dst, src); +} + +DEFINE_ECB_CBC_CTR_XTS(qcrypto_nettle_twofish, + QCryptoNettleTwofish, TWOFISH_BLOCK_SIZE, + twofish_encrypt_native, twofish_decrypt_native) + + +bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, + QCryptoCipherMode mode) +{ + switch (alg) { + case QCRYPTO_CIPHER_ALG_DES_RFB: + case QCRYPTO_CIPHER_ALG_3DES: + case QCRYPTO_CIPHER_ALG_AES_128: + case QCRYPTO_CIPHER_ALG_AES_192: + case QCRYPTO_CIPHER_ALG_AES_256: + case QCRYPTO_CIPHER_ALG_CAST5_128: + case QCRYPTO_CIPHER_ALG_SERPENT_128: + case QCRYPTO_CIPHER_ALG_SERPENT_192: + case QCRYPTO_CIPHER_ALG_SERPENT_256: + case QCRYPTO_CIPHER_ALG_TWOFISH_128: + case QCRYPTO_CIPHER_ALG_TWOFISH_192: + case QCRYPTO_CIPHER_ALG_TWOFISH_256: + break; + default: + return false; + } + + switch (mode) { + case QCRYPTO_CIPHER_MODE_ECB: + case QCRYPTO_CIPHER_MODE_CBC: + case QCRYPTO_CIPHER_MODE_XTS: + case QCRYPTO_CIPHER_MODE_CTR: + return true; + default: + return false; + } +} + +static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, + QCryptoCipherMode mode, + const uint8_t *key, + size_t nkey, + Error **errp) +{ + switch (mode) { + case QCRYPTO_CIPHER_MODE_ECB: + case QCRYPTO_CIPHER_MODE_CBC: + case QCRYPTO_CIPHER_MODE_XTS: + case QCRYPTO_CIPHER_MODE_CTR: + break; + default: + goto bad_cipher_mode; + } + + if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) { + return NULL; + } + + switch (alg) { + case QCRYPTO_CIPHER_ALG_DES_RFB: + { + QCryptoNettleDESRFB *ctx; + const QCryptoCipherDriver *drv; + uint8_t *rfbkey; + + switch (mode) { + case QCRYPTO_CIPHER_MODE_ECB: + drv = &qcrypto_nettle_des_rfb_driver_ecb; + break; + case QCRYPTO_CIPHER_MODE_CBC: + drv = &qcrypto_nettle_des_rfb_driver_cbc; + break; + case QCRYPTO_CIPHER_MODE_CTR: + drv = &qcrypto_nettle_des_rfb_driver_ctr; + break; + default: + goto bad_cipher_mode; + } + + ctx = g_new0(QCryptoNettleDESRFB, 1); + ctx->base.driver = drv; + + rfbkey = qcrypto_cipher_munge_des_rfb_key(key, nkey); + des_set_key(&ctx->key, rfbkey); + g_free(rfbkey); + + return &ctx->base; + } + + case QCRYPTO_CIPHER_ALG_3DES: + { + QCryptoNettleDES3 *ctx; + const QCryptoCipherDriver *drv; + + switch (mode) { + case QCRYPTO_CIPHER_MODE_ECB: + drv = &qcrypto_nettle_des3_driver_ecb; + break; + case QCRYPTO_CIPHER_MODE_CBC: + drv = &qcrypto_nettle_des3_driver_cbc; + break; + case QCRYPTO_CIPHER_MODE_CTR: + drv = &qcrypto_nettle_des3_driver_ctr; + break; + default: + goto bad_cipher_mode; + } + + ctx = g_new0(QCryptoNettleDES3, 1); + ctx->base.driver = drv; + des3_set_key(&ctx->key, key); + return &ctx->base; + } + + case QCRYPTO_CIPHER_ALG_AES_128: + { + QCryptoNettleAES128 *ctx = g_new0(QCryptoNettleAES128, 1); + + switch (mode) { + case QCRYPTO_CIPHER_MODE_ECB: + ctx->base.driver = &qcrypto_nettle_aes128_driver_ecb; + break; + case QCRYPTO_CIPHER_MODE_CBC: + ctx->base.driver = &qcrypto_nettle_aes128_driver_cbc; + break; + case QCRYPTO_CIPHER_MODE_CTR: + ctx->base.driver = &qcrypto_nettle_aes128_driver_ctr; + break; + case QCRYPTO_CIPHER_MODE_XTS: + ctx->base.driver = &qcrypto_nettle_aes128_driver_xts; + nkey /= 2; + aes128_set_encrypt_key(&ctx->key_xts[0], key + nkey); + aes128_set_decrypt_key(&ctx->key_xts[1], key + nkey); + break; + default: + g_assert_not_reached(); + } + aes128_set_encrypt_key(&ctx->key[0], key); + aes128_set_decrypt_key(&ctx->key[1], key); + + return &ctx->base; + } + + case QCRYPTO_CIPHER_ALG_AES_192: + { + QCryptoNettleAES192 *ctx = g_new0(QCryptoNettleAES192, 1); + + switch (mode) { + case QCRYPTO_CIPHER_MODE_ECB: + ctx->base.driver = &qcrypto_nettle_aes192_driver_ecb; + break; + case QCRYPTO_CIPHER_MODE_CBC: + ctx->base.driver = &qcrypto_nettle_aes192_driver_cbc; + break; + case QCRYPTO_CIPHER_MODE_CTR: + ctx->base.driver = &qcrypto_nettle_aes192_driver_ctr; + break; + case QCRYPTO_CIPHER_MODE_XTS: + ctx->base.driver = &qcrypto_nettle_aes192_driver_xts; + nkey /= 2; + aes192_set_encrypt_key(&ctx->key_xts[0], key + nkey); + aes192_set_decrypt_key(&ctx->key_xts[1], key + nkey); + break; + default: + g_assert_not_reached(); + } + aes192_set_encrypt_key(&ctx->key[0], key); + aes192_set_decrypt_key(&ctx->key[1], key); + + return &ctx->base; + } + + case QCRYPTO_CIPHER_ALG_AES_256: + { + QCryptoNettleAES256 *ctx = g_new0(QCryptoNettleAES256, 1); + + switch (mode) { + case QCRYPTO_CIPHER_MODE_ECB: + ctx->base.driver = &qcrypto_nettle_aes256_driver_ecb; + break; + case QCRYPTO_CIPHER_MODE_CBC: + ctx->base.driver = &qcrypto_nettle_aes256_driver_cbc; + break; + case QCRYPTO_CIPHER_MODE_CTR: + ctx->base.driver = &qcrypto_nettle_aes256_driver_ctr; + break; + case QCRYPTO_CIPHER_MODE_XTS: + ctx->base.driver = &qcrypto_nettle_aes256_driver_xts; + nkey /= 2; + aes256_set_encrypt_key(&ctx->key_xts[0], key + nkey); + aes256_set_decrypt_key(&ctx->key_xts[1], key + nkey); + break; + default: + g_assert_not_reached(); + } + aes256_set_encrypt_key(&ctx->key[0], key); + aes256_set_decrypt_key(&ctx->key[1], key); + + return &ctx->base; + } + + case QCRYPTO_CIPHER_ALG_CAST5_128: + { + QCryptoNettleCAST128 *ctx; + const QCryptoCipherDriver *drv; + + switch (mode) { + case QCRYPTO_CIPHER_MODE_ECB: + drv = &qcrypto_nettle_cast128_driver_ecb; + break; + case QCRYPTO_CIPHER_MODE_CBC: + drv = &qcrypto_nettle_cast128_driver_cbc; + break; + case QCRYPTO_CIPHER_MODE_CTR: + drv = &qcrypto_nettle_cast128_driver_ctr; + break; + default: + goto bad_cipher_mode; + } + + ctx = g_new0(QCryptoNettleCAST128, 1); + ctx->base.driver = drv; + cast5_set_key(&ctx->key, nkey, key); + + return &ctx->base; + } + + case QCRYPTO_CIPHER_ALG_SERPENT_128: + case QCRYPTO_CIPHER_ALG_SERPENT_192: + case QCRYPTO_CIPHER_ALG_SERPENT_256: + { + QCryptoNettleSerpent *ctx = g_new0(QCryptoNettleSerpent, 1); + + switch (mode) { + case QCRYPTO_CIPHER_MODE_ECB: + ctx->base.driver = &qcrypto_nettle_serpent_driver_ecb; + break; + case QCRYPTO_CIPHER_MODE_CBC: + ctx->base.driver = &qcrypto_nettle_serpent_driver_cbc; + break; + case QCRYPTO_CIPHER_MODE_CTR: + ctx->base.driver = &qcrypto_nettle_serpent_driver_ctr; + break; + case QCRYPTO_CIPHER_MODE_XTS: + ctx->base.driver = &qcrypto_nettle_serpent_driver_xts; + nkey /= 2; + serpent_set_key(&ctx->key_xts, nkey, key + nkey); + break; + default: + g_assert_not_reached(); + } + serpent_set_key(&ctx->key, nkey, key); + + return &ctx->base; + } + + case QCRYPTO_CIPHER_ALG_TWOFISH_128: + case QCRYPTO_CIPHER_ALG_TWOFISH_192: + case QCRYPTO_CIPHER_ALG_TWOFISH_256: + { + QCryptoNettleTwofish *ctx = g_new0(QCryptoNettleTwofish, 1); + + switch (mode) { + case QCRYPTO_CIPHER_MODE_ECB: + ctx->base.driver = &qcrypto_nettle_twofish_driver_ecb; + break; + case QCRYPTO_CIPHER_MODE_CBC: + ctx->base.driver = &qcrypto_nettle_twofish_driver_cbc; + break; + case QCRYPTO_CIPHER_MODE_CTR: + ctx->base.driver = &qcrypto_nettle_twofish_driver_ctr; + break; + case QCRYPTO_CIPHER_MODE_XTS: + ctx->base.driver = &qcrypto_nettle_twofish_driver_xts; + nkey /= 2; + twofish_set_key(&ctx->key_xts, nkey, key + nkey); + break; + default: + g_assert_not_reached(); + } + twofish_set_key(&ctx->key, nkey, key); + + return &ctx->base; + } + + default: + error_setg(errp, "Unsupported cipher algorithm %s", + QCryptoCipherAlgorithm_str(alg)); + return NULL; + } + + bad_cipher_mode: + error_setg(errp, "Unsupported cipher mode %s", + QCryptoCipherMode_str(mode)); + return NULL; +} diff --git a/crypto/cipher.c b/crypto/cipher.c index e5adb56271..068b2fb867 100644 --- a/crypto/cipher.c +++ b/crypto/cipher.c @@ -19,12 +19,13 @@ */ #include "qemu/osdep.h" +#include "qemu/host-utils.h" #include "qapi/error.h" #include "crypto/cipher.h" #include "cipherpriv.h" -static size_t alg_key_len[QCRYPTO_CIPHER_ALG__MAX] = { +static const size_t alg_key_len[QCRYPTO_CIPHER_ALG__MAX] = { [QCRYPTO_CIPHER_ALG_AES_128] = 16, [QCRYPTO_CIPHER_ALG_AES_192] = 24, [QCRYPTO_CIPHER_ALG_AES_256] = 32, @@ -39,7 +40,7 @@ static size_t alg_key_len[QCRYPTO_CIPHER_ALG__MAX] = { [QCRYPTO_CIPHER_ALG_TWOFISH_256] = 32, }; -static size_t alg_block_len[QCRYPTO_CIPHER_ALG__MAX] = { +static const size_t alg_block_len[QCRYPTO_CIPHER_ALG__MAX] = { [QCRYPTO_CIPHER_ALG_AES_128] = 16, [QCRYPTO_CIPHER_ALG_AES_192] = 16, [QCRYPTO_CIPHER_ALG_AES_256] = 16, @@ -54,7 +55,7 @@ static size_t alg_block_len[QCRYPTO_CIPHER_ALG__MAX] = { [QCRYPTO_CIPHER_ALG_TWOFISH_256] = 16, }; -static bool mode_need_iv[QCRYPTO_CIPHER_MODE__MAX] = { +static const bool mode_need_iv[QCRYPTO_CIPHER_MODE__MAX] = { [QCRYPTO_CIPHER_MODE_ECB] = false, [QCRYPTO_CIPHER_MODE_CBC] = true, [QCRYPTO_CIPHER_MODE_XTS] = true, @@ -150,11 +151,11 @@ qcrypto_cipher_munge_des_rfb_key(const uint8_t *key, #endif /* CONFIG_GCRYPT || CONFIG_NETTLE */ #ifdef CONFIG_GCRYPT -#include "cipher-gcrypt.c" +#include "cipher-gcrypt.c.inc" #elif defined CONFIG_NETTLE -#include "cipher-nettle.c" +#include "cipher-nettle.c.inc" #else -#include "cipher-builtin.c" +#include "cipher-builtin.c.inc" #endif QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, @@ -162,31 +163,21 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, const uint8_t *key, size_t nkey, Error **errp) { - QCryptoCipher *cipher; - void *ctx = NULL; - QCryptoCipherDriver *drv = NULL; + QCryptoCipher *cipher = NULL; #ifdef CONFIG_AF_ALG - ctx = qcrypto_afalg_cipher_ctx_new(alg, mode, key, nkey, NULL); - if (ctx) { - drv = &qcrypto_cipher_afalg_driver; - } + cipher = qcrypto_afalg_cipher_ctx_new(alg, mode, key, nkey, NULL); #endif - if (!ctx) { - ctx = qcrypto_cipher_ctx_new(alg, mode, key, nkey, errp); - if (!ctx) { + if (!cipher) { + cipher = qcrypto_cipher_ctx_new(alg, mode, key, nkey, errp); + if (!cipher) { return NULL; } - - drv = &qcrypto_cipher_lib_driver; } - cipher = g_new0(QCryptoCipher, 1); cipher->alg = alg; cipher->mode = mode; - cipher->opaque = ctx; - cipher->driver = (void *)drv; return cipher; } @@ -198,7 +189,7 @@ int qcrypto_cipher_encrypt(QCryptoCipher *cipher, size_t len, Error **errp) { - QCryptoCipherDriver *drv = cipher->driver; + const QCryptoCipherDriver *drv = cipher->driver; return drv->cipher_encrypt(cipher, in, out, len, errp); } @@ -209,7 +200,7 @@ int qcrypto_cipher_decrypt(QCryptoCipher *cipher, size_t len, Error **errp) { - QCryptoCipherDriver *drv = cipher->driver; + const QCryptoCipherDriver *drv = cipher->driver; return drv->cipher_decrypt(cipher, in, out, len, errp); } @@ -218,17 +209,14 @@ int qcrypto_cipher_setiv(QCryptoCipher *cipher, const uint8_t *iv, size_t niv, Error **errp) { - QCryptoCipherDriver *drv = cipher->driver; + const QCryptoCipherDriver *drv = cipher->driver; return drv->cipher_setiv(cipher, iv, niv, errp); } void qcrypto_cipher_free(QCryptoCipher *cipher) { - QCryptoCipherDriver *drv; if (cipher) { - drv = cipher->driver; - drv->cipher_free(cipher); - g_free(cipher); + cipher->driver->cipher_free(cipher); } } diff --git a/crypto/cipherpriv.h b/crypto/cipherpriv.h index 0823239f41..396527857d 100644 --- a/crypto/cipherpriv.h +++ b/crypto/cipherpriv.h @@ -17,8 +17,6 @@ #include "qapi/qapi-types-crypto.h" -typedef struct QCryptoCipherDriver QCryptoCipherDriver; - struct QCryptoCipherDriver { int (*cipher_encrypt)(QCryptoCipher *cipher, const void *in, @@ -43,14 +41,12 @@ struct QCryptoCipherDriver { #include "afalgpriv.h" -extern QCryptoAFAlg * +extern QCryptoCipher * qcrypto_afalg_cipher_ctx_new(QCryptoCipherAlgorithm alg, QCryptoCipherMode mode, const uint8_t *key, size_t nkey, Error **errp); -extern struct QCryptoCipherDriver qcrypto_cipher_afalg_driver; - #endif #endif diff --git a/crypto/desrfb.c b/crypto/desrfb.c index 3274c36510..b2a105ebbc 100644 --- a/crypto/desrfb.c +++ b/crypto/desrfb.c @@ -56,7 +56,7 @@ static const unsigned char pc1[56] = { 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 }; static const unsigned char totrot[16] = { - 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 }; + 1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28 }; static const unsigned char pc2[48] = { 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, @@ -93,7 +93,7 @@ void deskey(unsigned char *key, int edf) } for( j = 0; j < 24; j++ ) { if( pcr[pc2[j]] ) kn[m] |= bigbyte[j]; - if( pcr[pc2[j+24]] ) kn[n] |= bigbyte[j]; + if( pcr[pc2[j + 24]] ) kn[n] |= bigbyte[j]; } } cookey(kn); diff --git a/crypto/meson.build b/crypto/meson.build new file mode 100644 index 0000000000..7f37b5d335 --- /dev/null +++ b/crypto/meson.build @@ -0,0 +1,73 @@ +crypto_ss.add(genh) +crypto_ss.add(files( + 'afsplit.c', + 'block-luks.c', + 'block-qcow.c', + 'block.c', + 'cipher.c', + 'desrfb.c', + 'hash.c', + 'hmac.c', + 'ivgen-essiv.c', + 'ivgen-plain.c', + 'ivgen-plain64.c', + 'ivgen.c', + 'pbkdf.c', + 'secret_common.c', + 'secret.c', + 'tlscreds.c', + 'tlscredsanon.c', + 'tlscredspsk.c', + 'tlscredsx509.c', + 'tlssession.c', +)) + +if 'CONFIG_NETTLE' in config_host + crypto_ss.add(files('hash-nettle.c', 'hmac-nettle.c', 'pbkdf-nettle.c')) +elif 'CONFIG_GCRYPT' in config_host + crypto_ss.add(files('hash-gcrypt.c', 'pbkdf-gcrypt.c')) + if 'CONFIG_GCRYPT_HMAC' in config_host + crypto_ss.add(files('hmac-gcrypt.c')) + else + crypto_ss.add(files('hmac-glib.c')) + endif +else + crypto_ss.add(files('hash-glib.c', 'hmac-glib.c', 'pbkdf-stub.c')) +endif + +crypto_ss.add(when: 'CONFIG_SECRET_KEYRING', if_true: files('secret_keyring.c')) +crypto_ss.add(when: 'CONFIG_QEMU_PRIVATE_XTS', if_true: files('xts.c')) +crypto_ss.add(when: 'CONFIG_AF_ALG', if_true: files('afalg.c', 'cipher-afalg.c', 'hash-afalg.c')) +crypto_ss.add(when: 'CONFIG_GNUTLS', if_true: files('tls-cipher-suites.c')) + +if 'CONFIG_NETTLE' in config_host + crypto_ss.add(nettle) +elif 'CONFIG_GCRYPT' in config_host + crypto_ss.add(gcrypt) +endif + +if 'CONFIG_GNUTLS' in config_host + crypto_ss.add(gnutls) +endif + + +util_ss.add(files('aes.c')) +util_ss.add(files('init.c')) + +if 'CONFIG_GCRYPT' in config_host + util_ss.add(files('random-gcrypt.c')) +elif 'CONFIG_GNUTLS' in config_host + util_ss.add(files('random-gnutls.c')) +elif 'CONFIG_RNG_NONE' in config_host + util_ss.add(files('random-none.c')) +else + util_ss.add(files('random-platform.c')) +endif + +if 'CONFIG_GCRYPT' in config_host + util_ss.add(gcrypt) +endif + +if 'CONFIG_GNUTLS' in config_host + util_ss.add(gnutls) +endif diff --git a/crypto/secret.c b/crypto/secret.c index 281cb81f0f..44eaff16f6 100644 --- a/crypto/secret.c +++ b/crypto/secret.c @@ -107,13 +107,6 @@ qcrypto_secret_prop_get_file(Object *obj, } -static void -qcrypto_secret_complete(UserCreatable *uc, Error **errp) -{ - object_property_set_bool(OBJECT(uc), "loaded", true, errp); -} - - static void qcrypto_secret_finalize(Object *obj) { @@ -129,9 +122,6 @@ qcrypto_secret_class_init(ObjectClass *oc, void *data) QCryptoSecretCommonClass *sic = QCRYPTO_SECRET_COMMON_CLASS(oc); sic->load_data = qcrypto_secret_load_data; - UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); - ucc->complete = qcrypto_secret_complete; - object_class_property_add_str(oc, "data", qcrypto_secret_prop_get_data, qcrypto_secret_prop_set_data); @@ -148,10 +138,6 @@ static const TypeInfo qcrypto_secret_info = { .instance_finalize = qcrypto_secret_finalize, .class_size = sizeof(QCryptoSecretClass), .class_init = qcrypto_secret_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_USER_CREATABLE }, - { } - } }; diff --git a/crypto/secret_common.c b/crypto/secret_common.c index b03d530867..714a15d5e5 100644 --- a/crypto/secret_common.c +++ b/crypto/secret_common.c @@ -191,9 +191,9 @@ qcrypto_secret_prop_set_loaded(Object *obj, secret->rawdata = input; secret->rawlen = inputlen; - } else { - g_free(secret->rawdata); - secret->rawlen = 0; + } else if (secret->rawdata) { + error_setg(errp, "Cannot unload secret"); + return; } } @@ -268,6 +268,13 @@ qcrypto_secret_prop_get_keyid(Object *obj, } +static void +qcrypto_secret_complete(UserCreatable *uc, Error **errp) +{ + object_property_set_bool(OBJECT(uc), "loaded", true, errp); +} + + static void qcrypto_secret_finalize(Object *obj) { @@ -281,6 +288,10 @@ qcrypto_secret_finalize(Object *obj) static void qcrypto_secret_class_init(ObjectClass *oc, void *data) { + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + + ucc->complete = qcrypto_secret_complete; + object_class_property_add_bool(oc, "loaded", qcrypto_secret_prop_get_loaded, qcrypto_secret_prop_set_loaded); @@ -390,6 +401,10 @@ static const TypeInfo qcrypto_secret_info = { .class_size = sizeof(QCryptoSecretCommonClass), .class_init = qcrypto_secret_class_init, .abstract = true, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } }; diff --git a/crypto/secret_keyring.c b/crypto/secret_keyring.c index 8bfc58ebf4..1b7edec84a 100644 --- a/crypto/secret_keyring.c +++ b/crypto/secret_keyring.c @@ -102,22 +102,12 @@ qcrypto_secret_prop_get_key(Object *obj, Visitor *v, } -static void -qcrypto_secret_keyring_complete(UserCreatable *uc, Error **errp) -{ - object_property_set_bool(OBJECT(uc), "loaded", true, errp); -} - - static void qcrypto_secret_keyring_class_init(ObjectClass *oc, void *data) { QCryptoSecretCommonClass *sic = QCRYPTO_SECRET_COMMON_CLASS(oc); sic->load_data = qcrypto_secret_keyring_load_data; - UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); - ucc->complete = qcrypto_secret_keyring_complete; - object_class_property_add(oc, "serial", "int32_t", qcrypto_secret_prop_get_key, qcrypto_secret_prop_set_key, @@ -129,12 +119,7 @@ static const TypeInfo qcrypto_secret_info = { .parent = TYPE_QCRYPTO_SECRET_COMMON, .name = TYPE_QCRYPTO_SECRET_KEYRING, .instance_size = sizeof(QCryptoSecretKeyring), - .class_size = sizeof(QCryptoSecretKeyringClass), .class_init = qcrypto_secret_keyring_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_USER_CREATABLE }, - { } - } }; diff --git a/crypto/tls-cipher-suites.c b/crypto/tls-cipher-suites.c index 0d305b684b..55fb5f7c19 100644 --- a/crypto/tls-cipher-suites.c +++ b/crypto/tls-cipher-suites.c @@ -108,7 +108,7 @@ static void qcrypto_tls_cipher_suites_class_init(ObjectClass *oc, void *data) static const TypeInfo qcrypto_tls_cipher_suites_info = { .parent = TYPE_QCRYPTO_TLS_CREDS, .name = TYPE_QCRYPTO_TLS_CIPHER_SUITES, - .instance_size = sizeof(QCryptoTLSCreds), + .instance_size = sizeof(QCryptoTLSCipherSuites), .class_size = sizeof(QCryptoTLSCredsClass), .class_init = qcrypto_tls_cipher_suites_class_init, .interfaces = (InterfaceInfo[]) { diff --git a/crypto/tlscredsanon.c b/crypto/tlscredsanon.c index 30275b6847..bea5f76c55 100644 --- a/crypto/tlscredsanon.c +++ b/crypto/tlscredsanon.c @@ -123,10 +123,9 @@ qcrypto_tls_creds_anon_prop_set_loaded(Object *obj, { QCryptoTLSCredsAnon *creds = QCRYPTO_TLS_CREDS_ANON(obj); + qcrypto_tls_creds_anon_unload(creds); if (value) { qcrypto_tls_creds_anon_load(creds, errp); - } else { - qcrypto_tls_creds_anon_unload(creds); } } diff --git a/crypto/tlscredspsk.c b/crypto/tlscredspsk.c index e26807b899..f5a31108d1 100644 --- a/crypto/tlscredspsk.c +++ b/crypto/tlscredspsk.c @@ -192,10 +192,9 @@ qcrypto_tls_creds_psk_prop_set_loaded(Object *obj, { QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); + qcrypto_tls_creds_psk_unload(creds); if (value) { qcrypto_tls_creds_psk_load(creds, errp); - } else { - qcrypto_tls_creds_psk_unload(creds); } } diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c index dd7267ccdb..bc503bab55 100644 --- a/crypto/tlscredsx509.c +++ b/crypto/tlscredsx509.c @@ -143,7 +143,7 @@ qcrypto_tls_creds_check_cert_key_usage(QCryptoTLSCredsX509 *creds, if (status < 0) { if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { usage = isCA ? GNUTLS_KEY_KEY_CERT_SIGN : - GNUTLS_KEY_DIGITAL_SIGNATURE|GNUTLS_KEY_KEY_ENCIPHERMENT; + GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT; } else { error_setg(errp, "Unable to query certificate %s key usage: %s", @@ -694,10 +694,9 @@ qcrypto_tls_creds_x509_prop_set_loaded(Object *obj, { QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); + qcrypto_tls_creds_x509_unload(creds); if (value) { qcrypto_tls_creds_x509_load(creds, errp); - } else { - qcrypto_tls_creds_x509_unload(creds); } } @@ -771,6 +770,51 @@ qcrypto_tls_creds_x509_prop_get_sanity(Object *obj, } +#ifdef CONFIG_GNUTLS + + +static bool +qcrypto_tls_creds_x509_reload(QCryptoTLSCreds *creds, Error **errp) +{ + QCryptoTLSCredsX509 *x509_creds = QCRYPTO_TLS_CREDS_X509(creds); + Error *local_err = NULL; + gnutls_certificate_credentials_t creds_data = x509_creds->data; + gnutls_dh_params_t creds_dh_params = x509_creds->parent_obj.dh_params; + + x509_creds->data = NULL; + x509_creds->parent_obj.dh_params = NULL; + qcrypto_tls_creds_x509_load(x509_creds, &local_err); + if (local_err) { + qcrypto_tls_creds_x509_unload(x509_creds); + x509_creds->data = creds_data; + x509_creds->parent_obj.dh_params = creds_dh_params; + error_propagate(errp, local_err); + return false; + } + + if (creds_data) { + gnutls_certificate_free_credentials(creds_data); + } + if (creds_dh_params) { + gnutls_dh_params_deinit(creds_dh_params); + } + return true; +} + + +#else /* ! CONFIG_GNUTLS */ + + +static bool +qcrypto_tls_creds_x509_reload(QCryptoTLSCreds *creds, Error **errp) +{ + return false; +} + + +#endif /* ! CONFIG_GNUTLS */ + + static void qcrypto_tls_creds_x509_complete(UserCreatable *uc, Error **errp) { @@ -801,6 +845,9 @@ static void qcrypto_tls_creds_x509_class_init(ObjectClass *oc, void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + QCryptoTLSCredsClass *ctcc = QCRYPTO_TLS_CREDS_CLASS(oc); + + ctcc->reload = qcrypto_tls_creds_x509_reload; ucc->complete = qcrypto_tls_creds_x509_complete; diff --git a/crypto/trace.h b/crypto/trace.h new file mode 100644 index 0000000000..a9af0f315c --- /dev/null +++ b/crypto/trace.h @@ -0,0 +1 @@ +#include "trace/trace-crypto.h" diff --git a/default-configs/aarch64-linux-user.mak b/default-configs/aarch64-linux-user.mak deleted file mode 100644 index 0a5b08a007..0000000000 --- a/default-configs/aarch64-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for aarch64-linux-user diff --git a/default-configs/aarch64_be-linux-user.mak b/default-configs/aarch64_be-linux-user.mak deleted file mode 100644 index a69d9d2e41..0000000000 --- a/default-configs/aarch64_be-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for aarch64_be-linux-user diff --git a/default-configs/alpha-linux-user.mak b/default-configs/alpha-linux-user.mak deleted file mode 100644 index 7956e29898..0000000000 --- a/default-configs/alpha-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for alpha-linux-user diff --git a/default-configs/arm-linux-user.mak b/default-configs/arm-linux-user.mak deleted file mode 100644 index 413361a022..0000000000 --- a/default-configs/arm-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for arm-linux-user diff --git a/default-configs/armeb-linux-user.mak b/default-configs/armeb-linux-user.mak deleted file mode 100644 index bf2ffe7038..0000000000 --- a/default-configs/armeb-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for armeb-linux-user diff --git a/default-configs/cris-linux-user.mak b/default-configs/cris-linux-user.mak deleted file mode 100644 index e3aec7b864..0000000000 --- a/default-configs/cris-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for cris-linux-user diff --git a/default-configs/aarch64-softmmu.mak b/default-configs/devices/aarch64-softmmu.mak similarity index 100% rename from default-configs/aarch64-softmmu.mak rename to default-configs/devices/aarch64-softmmu.mak diff --git a/default-configs/alpha-softmmu.mak b/default-configs/devices/alpha-softmmu.mak similarity index 100% rename from default-configs/alpha-softmmu.mak rename to default-configs/devices/alpha-softmmu.mak diff --git a/default-configs/arm-softmmu.mak b/default-configs/devices/arm-softmmu.mak similarity index 93% rename from default-configs/arm-softmmu.mak rename to default-configs/devices/arm-softmmu.mak index 8fc09a4a51..0500156a0c 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/devices/arm-softmmu.mak @@ -27,6 +27,7 @@ CONFIG_GUMSTIX=y CONFIG_SPITZ=y CONFIG_TOSA=y CONFIG_Z2=y +CONFIG_NPCM7XX=y CONFIG_COLLIE=y CONFIG_ASPEED_SOC=y CONFIG_NETDUINO2=y @@ -41,5 +42,5 @@ CONFIG_FSL_IMX25=y CONFIG_FSL_IMX7=y CONFIG_FSL_IMX6UL=y CONFIG_SEMIHOSTING=y +CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y CONFIG_ALLWINNER_H3=y -CONFIG_ACPI_APEI=y diff --git a/default-configs/avr-softmmu.mak b/default-configs/devices/avr-softmmu.mak similarity index 100% rename from default-configs/avr-softmmu.mak rename to default-configs/devices/avr-softmmu.mak diff --git a/default-configs/cris-softmmu.mak b/default-configs/devices/cris-softmmu.mak similarity index 100% rename from default-configs/cris-softmmu.mak rename to default-configs/devices/cris-softmmu.mak diff --git a/default-configs/hppa-softmmu.mak b/default-configs/devices/hppa-softmmu.mak similarity index 100% rename from default-configs/hppa-softmmu.mak rename to default-configs/devices/hppa-softmmu.mak diff --git a/default-configs/i386-softmmu.mak b/default-configs/devices/i386-softmmu.mak similarity index 100% rename from default-configs/i386-softmmu.mak rename to default-configs/devices/i386-softmmu.mak diff --git a/default-configs/lm32-softmmu.mak b/default-configs/devices/lm32-softmmu.mak similarity index 93% rename from default-configs/lm32-softmmu.mak rename to default-configs/devices/lm32-softmmu.mak index 115b3e34c9..1bce3f6e8b 100644 --- a/default-configs/lm32-softmmu.mak +++ b/default-configs/devices/lm32-softmmu.mak @@ -8,5 +8,5 @@ CONFIG_SEMIHOSTING=y # Boards: # -CONFIG_LM32=y +CONFIG_LM32_EVR=y CONFIG_MILKYMIST=y diff --git a/default-configs/m68k-softmmu.mak b/default-configs/devices/m68k-softmmu.mak similarity index 88% rename from default-configs/m68k-softmmu.mak rename to default-configs/devices/m68k-softmmu.mak index 6629fd2aa3..7f8619e427 100644 --- a/default-configs/m68k-softmmu.mak +++ b/default-configs/devices/m68k-softmmu.mak @@ -8,3 +8,4 @@ CONFIG_AN5206=y CONFIG_MCF5208=y CONFIG_NEXTCUBE=y CONFIG_Q800=y +CONFIG_M68K_VIRT=y diff --git a/default-configs/microblaze-softmmu.mak b/default-configs/devices/microblaze-softmmu.mak similarity index 100% rename from default-configs/microblaze-softmmu.mak rename to default-configs/devices/microblaze-softmmu.mak diff --git a/default-configs/microblazeel-softmmu.mak b/default-configs/devices/microblazeel-softmmu.mak similarity index 100% rename from default-configs/microblazeel-softmmu.mak rename to default-configs/devices/microblazeel-softmmu.mak diff --git a/default-configs/mips-softmmu-common.mak b/default-configs/devices/mips-softmmu-common.mak similarity index 98% rename from default-configs/mips-softmmu-common.mak rename to default-configs/devices/mips-softmmu-common.mak index da29c6c0b2..ea78fe7275 100644 --- a/default-configs/mips-softmmu-common.mak +++ b/default-configs/devices/mips-softmmu-common.mak @@ -33,7 +33,6 @@ CONFIG_MC146818RTC=y CONFIG_EMPTY_SLOT=y CONFIG_MIPS_CPS=y CONFIG_MIPS_ITU=y -CONFIG_R4K=y CONFIG_MALTA=y CONFIG_PCNET_PCI=y CONFIG_MIPSSIM=y diff --git a/default-configs/mips-softmmu.mak b/default-configs/devices/mips-softmmu.mak similarity index 100% rename from default-configs/mips-softmmu.mak rename to default-configs/devices/mips-softmmu.mak diff --git a/default-configs/mips64-softmmu.mak b/default-configs/devices/mips64-softmmu.mak similarity index 100% rename from default-configs/mips64-softmmu.mak rename to default-configs/devices/mips64-softmmu.mak diff --git a/default-configs/mips64el-softmmu.mak b/default-configs/devices/mips64el-softmmu.mak similarity index 93% rename from default-configs/mips64el-softmmu.mak rename to default-configs/devices/mips64el-softmmu.mak index 9f8a3ef156..26c660a05c 100644 --- a/default-configs/mips64el-softmmu.mak +++ b/default-configs/devices/mips64el-softmmu.mak @@ -3,6 +3,7 @@ include mips-softmmu-common.mak CONFIG_IDE_VIA=y CONFIG_FULOONG=y +CONFIG_LOONGSON3V=y CONFIG_ATI_VGA=y CONFIG_RTL8139_PCI=y CONFIG_JAZZ=y diff --git a/default-configs/mipsel-softmmu.mak b/default-configs/devices/mipsel-softmmu.mak similarity index 100% rename from default-configs/mipsel-softmmu.mak rename to default-configs/devices/mipsel-softmmu.mak diff --git a/default-configs/moxie-softmmu.mak b/default-configs/devices/moxie-softmmu.mak similarity index 100% rename from default-configs/moxie-softmmu.mak rename to default-configs/devices/moxie-softmmu.mak diff --git a/default-configs/nios2-softmmu.mak b/default-configs/devices/nios2-softmmu.mak similarity index 100% rename from default-configs/nios2-softmmu.mak rename to default-configs/devices/nios2-softmmu.mak diff --git a/default-configs/or1k-softmmu.mak b/default-configs/devices/or1k-softmmu.mak similarity index 100% rename from default-configs/or1k-softmmu.mak rename to default-configs/devices/or1k-softmmu.mak diff --git a/default-configs/ppc-softmmu.mak b/default-configs/devices/ppc-softmmu.mak similarity index 100% rename from default-configs/ppc-softmmu.mak rename to default-configs/devices/ppc-softmmu.mak diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/devices/ppc64-softmmu.mak similarity index 100% rename from default-configs/ppc64-softmmu.mak rename to default-configs/devices/ppc64-softmmu.mak diff --git a/default-configs/riscv32-softmmu.mak b/default-configs/devices/riscv32-softmmu.mak similarity index 80% rename from default-configs/riscv32-softmmu.mak rename to default-configs/devices/riscv32-softmmu.mak index 94a236c9c2..d847bd5692 100644 --- a/default-configs/riscv32-softmmu.mak +++ b/default-configs/devices/riscv32-softmmu.mak @@ -3,6 +3,8 @@ # Uncomment the following lines to disable these optional devices: # #CONFIG_PCI_DEVICES=n +CONFIG_SEMIHOSTING=y +CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y # Boards: # diff --git a/default-configs/riscv64-softmmu.mak b/default-configs/devices/riscv64-softmmu.mak similarity index 72% rename from default-configs/riscv64-softmmu.mak rename to default-configs/devices/riscv64-softmmu.mak index aaf6d735bb..d5eec75f05 100644 --- a/default-configs/riscv64-softmmu.mak +++ b/default-configs/devices/riscv64-softmmu.mak @@ -3,6 +3,8 @@ # Uncomment the following lines to disable these optional devices: # #CONFIG_PCI_DEVICES=n +CONFIG_SEMIHOSTING=y +CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y # Boards: # @@ -10,3 +12,4 @@ CONFIG_SPIKE=y CONFIG_SIFIVE_E=y CONFIG_SIFIVE_U=y CONFIG_RISCV_VIRT=y +CONFIG_MICROCHIP_PFSOC=y diff --git a/default-configs/rx-softmmu.mak b/default-configs/devices/rx-softmmu.mak similarity index 100% rename from default-configs/rx-softmmu.mak rename to default-configs/devices/rx-softmmu.mak diff --git a/default-configs/s390x-softmmu.mak b/default-configs/devices/s390x-softmmu.mak similarity index 100% rename from default-configs/s390x-softmmu.mak rename to default-configs/devices/s390x-softmmu.mak diff --git a/default-configs/sh4-softmmu.mak b/default-configs/devices/sh4-softmmu.mak similarity index 100% rename from default-configs/sh4-softmmu.mak rename to default-configs/devices/sh4-softmmu.mak diff --git a/default-configs/sh4eb-softmmu.mak b/default-configs/devices/sh4eb-softmmu.mak similarity index 100% rename from default-configs/sh4eb-softmmu.mak rename to default-configs/devices/sh4eb-softmmu.mak diff --git a/default-configs/sparc-softmmu.mak b/default-configs/devices/sparc-softmmu.mak similarity index 100% rename from default-configs/sparc-softmmu.mak rename to default-configs/devices/sparc-softmmu.mak diff --git a/default-configs/sparc64-softmmu.mak b/default-configs/devices/sparc64-softmmu.mak similarity index 100% rename from default-configs/sparc64-softmmu.mak rename to default-configs/devices/sparc64-softmmu.mak diff --git a/default-configs/devices/tricore-softmmu.mak b/default-configs/devices/tricore-softmmu.mak new file mode 100644 index 0000000000..5cc91cebce --- /dev/null +++ b/default-configs/devices/tricore-softmmu.mak @@ -0,0 +1 @@ +CONFIG_TRIBOARD=y diff --git a/default-configs/unicore32-softmmu.mak b/default-configs/devices/unicore32-softmmu.mak similarity index 100% rename from default-configs/unicore32-softmmu.mak rename to default-configs/devices/unicore32-softmmu.mak diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/devices/x86_64-softmmu.mak similarity index 100% rename from default-configs/x86_64-softmmu.mak rename to default-configs/devices/x86_64-softmmu.mak diff --git a/default-configs/xtensa-softmmu.mak b/default-configs/devices/xtensa-softmmu.mak similarity index 100% rename from default-configs/xtensa-softmmu.mak rename to default-configs/devices/xtensa-softmmu.mak diff --git a/default-configs/xtensaeb-softmmu.mak b/default-configs/devices/xtensaeb-softmmu.mak similarity index 100% rename from default-configs/xtensaeb-softmmu.mak rename to default-configs/devices/xtensaeb-softmmu.mak diff --git a/default-configs/hppa-linux-user.mak b/default-configs/hppa-linux-user.mak deleted file mode 100644 index 796393940b..0000000000 --- a/default-configs/hppa-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for hppa-linux-user diff --git a/default-configs/i386-bsd-user.mak b/default-configs/i386-bsd-user.mak deleted file mode 100644 index af1b31a59a..0000000000 --- a/default-configs/i386-bsd-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for i386-bsd-user diff --git a/default-configs/i386-linux-user.mak b/default-configs/i386-linux-user.mak deleted file mode 100644 index 8657e68627..0000000000 --- a/default-configs/i386-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for i386-linux-user diff --git a/default-configs/m68k-linux-user.mak b/default-configs/m68k-linux-user.mak deleted file mode 100644 index 06cd5ed7ed..0000000000 --- a/default-configs/m68k-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for m68k-linux-user diff --git a/default-configs/microblaze-linux-user.mak b/default-configs/microblaze-linux-user.mak deleted file mode 100644 index 566fdc01c4..0000000000 --- a/default-configs/microblaze-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for microblaze-linux-user diff --git a/default-configs/microblazeel-linux-user.mak b/default-configs/microblazeel-linux-user.mak deleted file mode 100644 index 378c6ddcb0..0000000000 --- a/default-configs/microblazeel-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for microblazeel-linux-user diff --git a/default-configs/mips-linux-user.mak b/default-configs/mips-linux-user.mak deleted file mode 100644 index 31df57021e..0000000000 --- a/default-configs/mips-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for mips-linux-user diff --git a/default-configs/mips64-linux-user.mak b/default-configs/mips64-linux-user.mak deleted file mode 100644 index 1598bfcf7d..0000000000 --- a/default-configs/mips64-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for mips64-linux-user diff --git a/default-configs/mips64el-linux-user.mak b/default-configs/mips64el-linux-user.mak deleted file mode 100644 index 629f084086..0000000000 --- a/default-configs/mips64el-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for mips64el-linux-user diff --git a/default-configs/mipsel-linux-user.mak b/default-configs/mipsel-linux-user.mak deleted file mode 100644 index 4d0e4afb69..0000000000 --- a/default-configs/mipsel-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for mipsel-linux-user diff --git a/default-configs/mipsn32-linux-user.mak b/default-configs/mipsn32-linux-user.mak deleted file mode 100644 index 5b97919794..0000000000 --- a/default-configs/mipsn32-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for mipsn32-linux-user diff --git a/default-configs/mipsn32el-linux-user.mak b/default-configs/mipsn32el-linux-user.mak deleted file mode 100644 index d6367ff987..0000000000 --- a/default-configs/mipsn32el-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for mipsn32el-linux-user diff --git a/default-configs/nios2-linux-user.mak b/default-configs/nios2-linux-user.mak deleted file mode 100644 index 5be3eb795d..0000000000 --- a/default-configs/nios2-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for nios2-linux-user diff --git a/default-configs/or1k-linux-user.mak b/default-configs/or1k-linux-user.mak deleted file mode 100644 index 20e03c1317..0000000000 --- a/default-configs/or1k-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for or1k-linux-user diff --git a/default-configs/ppc-linux-user.mak b/default-configs/ppc-linux-user.mak deleted file mode 100644 index 6273df2930..0000000000 --- a/default-configs/ppc-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for ppc-linux-user diff --git a/default-configs/ppc64-linux-user.mak b/default-configs/ppc64-linux-user.mak deleted file mode 100644 index 422d3fbaeb..0000000000 --- a/default-configs/ppc64-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for ppc64-linux-user diff --git a/default-configs/ppc64abi32-linux-user.mak b/default-configs/ppc64abi32-linux-user.mak deleted file mode 100644 index 1c657ec9bb..0000000000 --- a/default-configs/ppc64abi32-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for ppc64abi32-linux-user diff --git a/default-configs/ppc64le-linux-user.mak b/default-configs/ppc64le-linux-user.mak deleted file mode 100644 index 63f4269023..0000000000 --- a/default-configs/ppc64le-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for ppc64le-linux-user diff --git a/default-configs/riscv32-linux-user.mak b/default-configs/riscv32-linux-user.mak deleted file mode 100644 index 865b362f5a..0000000000 --- a/default-configs/riscv32-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for riscv-linux-user diff --git a/default-configs/riscv64-linux-user.mak b/default-configs/riscv64-linux-user.mak deleted file mode 100644 index 865b362f5a..0000000000 --- a/default-configs/riscv64-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for riscv-linux-user diff --git a/default-configs/s390x-linux-user.mak b/default-configs/s390x-linux-user.mak deleted file mode 100644 index a243c99874..0000000000 --- a/default-configs/s390x-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for s390x-linux-user diff --git a/default-configs/sh4-linux-user.mak b/default-configs/sh4-linux-user.mak deleted file mode 100644 index a469e19ac6..0000000000 --- a/default-configs/sh4-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for sh4-linux-user diff --git a/default-configs/sh4eb-linux-user.mak b/default-configs/sh4eb-linux-user.mak deleted file mode 100644 index be08ca1002..0000000000 --- a/default-configs/sh4eb-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for sh4eb-linux-user diff --git a/default-configs/sparc-bsd-user.mak b/default-configs/sparc-bsd-user.mak deleted file mode 100644 index 21e09508d8..0000000000 --- a/default-configs/sparc-bsd-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for sparc-bsd-user diff --git a/default-configs/sparc-linux-user.mak b/default-configs/sparc-linux-user.mak deleted file mode 100644 index 9c716d1f92..0000000000 --- a/default-configs/sparc-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for sparc-linux-user diff --git a/default-configs/sparc32plus-linux-user.mak b/default-configs/sparc32plus-linux-user.mak deleted file mode 100644 index 432e88011e..0000000000 --- a/default-configs/sparc32plus-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for sparc32plus-linux-user diff --git a/default-configs/sparc64-bsd-user.mak b/default-configs/sparc64-bsd-user.mak deleted file mode 100644 index b8b9eea7f6..0000000000 --- a/default-configs/sparc64-bsd-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for sparc64-bsd-user diff --git a/default-configs/sparc64-linux-user.mak b/default-configs/sparc64-linux-user.mak deleted file mode 100644 index bf1bdd6aa4..0000000000 --- a/default-configs/sparc64-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for sparc64-linux-user diff --git a/default-configs/targets/aarch64-linux-user.mak b/default-configs/targets/aarch64-linux-user.mak new file mode 100644 index 0000000000..4713253709 --- /dev/null +++ b/default-configs/targets/aarch64-linux-user.mak @@ -0,0 +1,5 @@ +TARGET_ARCH=aarch64 +TARGET_BASE_ARCH=arm +TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml +TARGET_HAS_BFLT=y +CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/default-configs/targets/aarch64-softmmu.mak b/default-configs/targets/aarch64-softmmu.mak new file mode 100644 index 0000000000..7703127674 --- /dev/null +++ b/default-configs/targets/aarch64-softmmu.mak @@ -0,0 +1,5 @@ +TARGET_ARCH=aarch64 +TARGET_BASE_ARCH=arm +TARGET_SUPPORTS_MTTCG=y +TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml +TARGET_NEED_FDT=y diff --git a/default-configs/targets/aarch64_be-linux-user.mak b/default-configs/targets/aarch64_be-linux-user.mak new file mode 100644 index 0000000000..fae831558d --- /dev/null +++ b/default-configs/targets/aarch64_be-linux-user.mak @@ -0,0 +1,6 @@ +TARGET_ARCH=aarch64 +TARGET_BASE_ARCH=arm +TARGET_WORDS_BIGENDIAN=y +TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml +TARGET_HAS_BFLT=y +CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/default-configs/targets/alpha-linux-user.mak b/default-configs/targets/alpha-linux-user.mak new file mode 100644 index 0000000000..7e62fd796a --- /dev/null +++ b/default-configs/targets/alpha-linux-user.mak @@ -0,0 +1,4 @@ +TARGET_ARCH=alpha +TARGET_SYSTBL_ABI=common +TARGET_SYSTBL=syscall.tbl +TARGET_ALIGNED_ONLY=y diff --git a/default-configs/targets/alpha-softmmu.mak b/default-configs/targets/alpha-softmmu.mak new file mode 100644 index 0000000000..e4b874a19e --- /dev/null +++ b/default-configs/targets/alpha-softmmu.mak @@ -0,0 +1,3 @@ +TARGET_ARCH=alpha +TARGET_ALIGNED_ONLY=y +TARGET_SUPPORTS_MTTCG=y diff --git a/default-configs/targets/arm-linux-user.mak b/default-configs/targets/arm-linux-user.mak new file mode 100644 index 0000000000..e741ffd4d3 --- /dev/null +++ b/default-configs/targets/arm-linux-user.mak @@ -0,0 +1,6 @@ +TARGET_ARCH=arm +TARGET_SYSTBL_ABI=common,oabi +TARGET_SYSTBL=syscall.tbl +TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml +TARGET_HAS_BFLT=y +CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/default-configs/targets/arm-softmmu.mak b/default-configs/targets/arm-softmmu.mak new file mode 100644 index 0000000000..84a98f4818 --- /dev/null +++ b/default-configs/targets/arm-softmmu.mak @@ -0,0 +1,4 @@ +TARGET_ARCH=arm +TARGET_SUPPORTS_MTTCG=y +TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml +TARGET_NEED_FDT=y diff --git a/default-configs/targets/armeb-linux-user.mak b/default-configs/targets/armeb-linux-user.mak new file mode 100644 index 0000000000..255e44e8b0 --- /dev/null +++ b/default-configs/targets/armeb-linux-user.mak @@ -0,0 +1,7 @@ +TARGET_ARCH=arm +TARGET_SYSTBL_ABI=common,oabi +TARGET_SYSTBL=syscall.tbl +TARGET_WORDS_BIGENDIAN=y +TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml +TARGET_HAS_BFLT=y +CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/default-configs/targets/avr-softmmu.mak b/default-configs/targets/avr-softmmu.mak new file mode 100644 index 0000000000..e3f921c019 --- /dev/null +++ b/default-configs/targets/avr-softmmu.mak @@ -0,0 +1,2 @@ +TARGET_ARCH=avr +TARGET_XML_FILES= gdb-xml/avr-cpu.xml diff --git a/default-configs/targets/cris-linux-user.mak b/default-configs/targets/cris-linux-user.mak new file mode 100644 index 0000000000..e483c42066 --- /dev/null +++ b/default-configs/targets/cris-linux-user.mak @@ -0,0 +1 @@ +TARGET_ARCH=cris diff --git a/default-configs/targets/cris-softmmu.mak b/default-configs/targets/cris-softmmu.mak new file mode 100644 index 0000000000..e483c42066 --- /dev/null +++ b/default-configs/targets/cris-softmmu.mak @@ -0,0 +1 @@ +TARGET_ARCH=cris diff --git a/default-configs/targets/hexagon-linux-user.mak b/default-configs/targets/hexagon-linux-user.mak new file mode 100644 index 0000000000..003ed0a408 --- /dev/null +++ b/default-configs/targets/hexagon-linux-user.mak @@ -0,0 +1 @@ +TARGET_ARCH=hexagon diff --git a/default-configs/targets/hppa-linux-user.mak b/default-configs/targets/hppa-linux-user.mak new file mode 100644 index 0000000000..f01e0a7b9e --- /dev/null +++ b/default-configs/targets/hppa-linux-user.mak @@ -0,0 +1,5 @@ +TARGET_ARCH=hppa +TARGET_SYSTBL_ABI=common,32 +TARGET_SYSTBL=syscall.tbl +TARGET_ALIGNED_ONLY=y +TARGET_WORDS_BIGENDIAN=y diff --git a/default-configs/targets/hppa-softmmu.mak b/default-configs/targets/hppa-softmmu.mak new file mode 100644 index 0000000000..e3e71eb21b --- /dev/null +++ b/default-configs/targets/hppa-softmmu.mak @@ -0,0 +1,4 @@ +TARGET_ARCH=hppa +TARGET_ALIGNED_ONLY=y +TARGET_WORDS_BIGENDIAN=y +TARGET_SUPPORTS_MTTCG=y diff --git a/default-configs/targets/i386-bsd-user.mak b/default-configs/targets/i386-bsd-user.mak new file mode 100644 index 0000000000..0283bb62a0 --- /dev/null +++ b/default-configs/targets/i386-bsd-user.mak @@ -0,0 +1,2 @@ +TARGET_ARCH=i386 +TARGET_XML_FILES= gdb-xml/i386-32bit.xml diff --git a/default-configs/targets/i386-linux-user.mak b/default-configs/targets/i386-linux-user.mak new file mode 100644 index 0000000000..5b2546a430 --- /dev/null +++ b/default-configs/targets/i386-linux-user.mak @@ -0,0 +1,4 @@ +TARGET_ARCH=i386 +TARGET_SYSTBL_ABI=i386 +TARGET_SYSTBL=syscall_32.tbl +TARGET_XML_FILES= gdb-xml/i386-32bit.xml diff --git a/default-configs/targets/i386-softmmu.mak b/default-configs/targets/i386-softmmu.mak new file mode 100644 index 0000000000..5babf71895 --- /dev/null +++ b/default-configs/targets/i386-softmmu.mak @@ -0,0 +1,3 @@ +TARGET_ARCH=i386 +TARGET_SUPPORTS_MTTCG=y +TARGET_XML_FILES= gdb-xml/i386-32bit.xml diff --git a/default-configs/targets/lm32-softmmu.mak b/default-configs/targets/lm32-softmmu.mak new file mode 100644 index 0000000000..55e7184a3d --- /dev/null +++ b/default-configs/targets/lm32-softmmu.mak @@ -0,0 +1,2 @@ +TARGET_ARCH=lm32 +TARGET_WORDS_BIGENDIAN=y diff --git a/default-configs/targets/m68k-linux-user.mak b/default-configs/targets/m68k-linux-user.mak new file mode 100644 index 0000000000..805d16c6ab --- /dev/null +++ b/default-configs/targets/m68k-linux-user.mak @@ -0,0 +1,6 @@ +TARGET_ARCH=m68k +TARGET_SYSTBL_ABI=common +TARGET_SYSTBL=syscall.tbl +TARGET_WORDS_BIGENDIAN=y +TARGET_XML_FILES= gdb-xml/cf-core.xml gdb-xml/cf-fp.xml gdb-xml/m68k-core.xml gdb-xml/m68k-fp.xml +TARGET_HAS_BFLT=y diff --git a/default-configs/targets/m68k-softmmu.mak b/default-configs/targets/m68k-softmmu.mak new file mode 100644 index 0000000000..5df1a2b7d7 --- /dev/null +++ b/default-configs/targets/m68k-softmmu.mak @@ -0,0 +1,3 @@ +TARGET_ARCH=m68k +TARGET_WORDS_BIGENDIAN=y +TARGET_XML_FILES= gdb-xml/cf-core.xml gdb-xml/cf-fp.xml gdb-xml/m68k-core.xml gdb-xml/m68k-fp.xml diff --git a/default-configs/targets/microblaze-linux-user.mak b/default-configs/targets/microblaze-linux-user.mak new file mode 100644 index 0000000000..2a25bf2fa3 --- /dev/null +++ b/default-configs/targets/microblaze-linux-user.mak @@ -0,0 +1,5 @@ +TARGET_ARCH=microblaze +TARGET_SYSTBL_ABI=common +TARGET_SYSTBL=syscall.tbl +TARGET_WORDS_BIGENDIAN=y +TARGET_HAS_BFLT=y diff --git a/default-configs/targets/microblaze-softmmu.mak b/default-configs/targets/microblaze-softmmu.mak new file mode 100644 index 0000000000..33f2a00402 --- /dev/null +++ b/default-configs/targets/microblaze-softmmu.mak @@ -0,0 +1,4 @@ +TARGET_ARCH=microblaze +TARGET_WORDS_BIGENDIAN=y +TARGET_SUPPORTS_MTTCG=y +TARGET_NEED_FDT=y diff --git a/default-configs/targets/microblazeel-linux-user.mak b/default-configs/targets/microblazeel-linux-user.mak new file mode 100644 index 0000000000..d0e775d840 --- /dev/null +++ b/default-configs/targets/microblazeel-linux-user.mak @@ -0,0 +1,4 @@ +TARGET_ARCH=microblaze +TARGET_SYSTBL_ABI=common +TARGET_SYSTBL=syscall.tbl +TARGET_HAS_BFLT=y diff --git a/default-configs/targets/microblazeel-softmmu.mak b/default-configs/targets/microblazeel-softmmu.mak new file mode 100644 index 0000000000..af40391f2f --- /dev/null +++ b/default-configs/targets/microblazeel-softmmu.mak @@ -0,0 +1,3 @@ +TARGET_ARCH=microblaze +TARGET_SUPPORTS_MTTCG=y +TARGET_NEED_FDT=y diff --git a/default-configs/targets/mips-linux-user.mak b/default-configs/targets/mips-linux-user.mak new file mode 100644 index 0000000000..19f5779831 --- /dev/null +++ b/default-configs/targets/mips-linux-user.mak @@ -0,0 +1,6 @@ +TARGET_ARCH=mips +TARGET_ABI_MIPSO32=y +TARGET_SYSTBL_ABI=o32 +TARGET_SYSTBL=syscall_o32.tbl +TARGET_ALIGNED_ONLY=y +TARGET_WORDS_BIGENDIAN=y diff --git a/default-configs/targets/mips-softmmu.mak b/default-configs/targets/mips-softmmu.mak new file mode 100644 index 0000000000..8a49999a47 --- /dev/null +++ b/default-configs/targets/mips-softmmu.mak @@ -0,0 +1,4 @@ +TARGET_ARCH=mips +TARGET_ALIGNED_ONLY=y +TARGET_WORDS_BIGENDIAN=y +TARGET_SUPPORTS_MTTCG=y diff --git a/default-configs/targets/mips64-linux-user.mak b/default-configs/targets/mips64-linux-user.mak new file mode 100644 index 0000000000..32fd1acdf2 --- /dev/null +++ b/default-configs/targets/mips64-linux-user.mak @@ -0,0 +1,7 @@ +TARGET_ARCH=mips64 +TARGET_ABI_MIPSN64=y +TARGET_BASE_ARCH=mips +TARGET_SYSTBL_ABI=n64 +TARGET_SYSTBL=syscall_n64.tbl +TARGET_ALIGNED_ONLY=y +TARGET_WORDS_BIGENDIAN=y diff --git a/default-configs/targets/mips64-softmmu.mak b/default-configs/targets/mips64-softmmu.mak new file mode 100644 index 0000000000..ece25b9624 --- /dev/null +++ b/default-configs/targets/mips64-softmmu.mak @@ -0,0 +1,4 @@ +TARGET_ARCH=mips64 +TARGET_BASE_ARCH=mips +TARGET_ALIGNED_ONLY=y +TARGET_WORDS_BIGENDIAN=y diff --git a/default-configs/targets/mips64el-linux-user.mak b/default-configs/targets/mips64el-linux-user.mak new file mode 100644 index 0000000000..f348f35997 --- /dev/null +++ b/default-configs/targets/mips64el-linux-user.mak @@ -0,0 +1,6 @@ +TARGET_ARCH=mips64 +TARGET_ABI_MIPSN64=y +TARGET_BASE_ARCH=mips +TARGET_SYSTBL_ABI=n64 +TARGET_SYSTBL=syscall_n64.tbl +TARGET_ALIGNED_ONLY=y diff --git a/default-configs/targets/mips64el-softmmu.mak b/default-configs/targets/mips64el-softmmu.mak new file mode 100644 index 0000000000..5a52aa4b64 --- /dev/null +++ b/default-configs/targets/mips64el-softmmu.mak @@ -0,0 +1,4 @@ +TARGET_ARCH=mips64 +TARGET_BASE_ARCH=mips +TARGET_ALIGNED_ONLY=y +TARGET_NEED_FDT=y diff --git a/default-configs/targets/mipsel-linux-user.mak b/default-configs/targets/mipsel-linux-user.mak new file mode 100644 index 0000000000..e23793070c --- /dev/null +++ b/default-configs/targets/mipsel-linux-user.mak @@ -0,0 +1,5 @@ +TARGET_ARCH=mips +TARGET_ABI_MIPSO32=y +TARGET_SYSTBL_ABI=o32 +TARGET_SYSTBL=syscall_o32.tbl +TARGET_ALIGNED_ONLY=y diff --git a/default-configs/targets/mipsel-softmmu.mak b/default-configs/targets/mipsel-softmmu.mak new file mode 100644 index 0000000000..c7c41f4fb7 --- /dev/null +++ b/default-configs/targets/mipsel-softmmu.mak @@ -0,0 +1,3 @@ +TARGET_ARCH=mips +TARGET_ALIGNED_ONLY=y +TARGET_SUPPORTS_MTTCG=y diff --git a/default-configs/targets/mipsn32-linux-user.mak b/default-configs/targets/mipsn32-linux-user.mak new file mode 100644 index 0000000000..b8c2441ad0 --- /dev/null +++ b/default-configs/targets/mipsn32-linux-user.mak @@ -0,0 +1,8 @@ +TARGET_ARCH=mips64 +TARGET_ABI_MIPSN32=y +TARGET_ABI32=y +TARGET_BASE_ARCH=mips +TARGET_SYSTBL_ABI=n32 +TARGET_SYSTBL=syscall_n32.tbl +TARGET_ALIGNED_ONLY=y +TARGET_WORDS_BIGENDIAN=y diff --git a/default-configs/targets/mipsn32el-linux-user.mak b/default-configs/targets/mipsn32el-linux-user.mak new file mode 100644 index 0000000000..f31a9c394b --- /dev/null +++ b/default-configs/targets/mipsn32el-linux-user.mak @@ -0,0 +1,7 @@ +TARGET_ARCH=mips64 +TARGET_ABI_MIPSN32=y +TARGET_ABI32=y +TARGET_BASE_ARCH=mips +TARGET_SYSTBL_ABI=n32 +TARGET_SYSTBL=syscall_n32.tbl +TARGET_ALIGNED_ONLY=y diff --git a/default-configs/targets/moxie-softmmu.mak b/default-configs/targets/moxie-softmmu.mak new file mode 100644 index 0000000000..183e6b0ebd --- /dev/null +++ b/default-configs/targets/moxie-softmmu.mak @@ -0,0 +1,2 @@ +TARGET_ARCH=moxie +TARGET_WORDS_BIGENDIAN=y diff --git a/default-configs/targets/nios2-linux-user.mak b/default-configs/targets/nios2-linux-user.mak new file mode 100644 index 0000000000..9a372f0717 --- /dev/null +++ b/default-configs/targets/nios2-linux-user.mak @@ -0,0 +1 @@ +TARGET_ARCH=nios2 diff --git a/default-configs/targets/nios2-softmmu.mak b/default-configs/targets/nios2-softmmu.mak new file mode 100644 index 0000000000..9a372f0717 --- /dev/null +++ b/default-configs/targets/nios2-softmmu.mak @@ -0,0 +1 @@ +TARGET_ARCH=nios2 diff --git a/default-configs/targets/or1k-linux-user.mak b/default-configs/targets/or1k-linux-user.mak new file mode 100644 index 0000000000..1dfb93e46d --- /dev/null +++ b/default-configs/targets/or1k-linux-user.mak @@ -0,0 +1,2 @@ +TARGET_ARCH=openrisc +TARGET_WORDS_BIGENDIAN=y diff --git a/default-configs/targets/or1k-softmmu.mak b/default-configs/targets/or1k-softmmu.mak new file mode 100644 index 0000000000..1dfb93e46d --- /dev/null +++ b/default-configs/targets/or1k-softmmu.mak @@ -0,0 +1,2 @@ +TARGET_ARCH=openrisc +TARGET_WORDS_BIGENDIAN=y diff --git a/default-configs/targets/ppc-linux-user.mak b/default-configs/targets/ppc-linux-user.mak new file mode 100644 index 0000000000..ca4187e4aa --- /dev/null +++ b/default-configs/targets/ppc-linux-user.mak @@ -0,0 +1,5 @@ +TARGET_ARCH=ppc +TARGET_SYSTBL_ABI=common,nospu,32 +TARGET_SYSTBL=syscall.tbl +TARGET_WORDS_BIGENDIAN=y +TARGET_XML_FILES= gdb-xml/power-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml diff --git a/default-configs/targets/ppc-softmmu.mak b/default-configs/targets/ppc-softmmu.mak new file mode 100644 index 0000000000..f4eef1819a --- /dev/null +++ b/default-configs/targets/ppc-softmmu.mak @@ -0,0 +1,4 @@ +TARGET_ARCH=ppc +TARGET_WORDS_BIGENDIAN=y +TARGET_XML_FILES= gdb-xml/power-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml +TARGET_NEED_FDT=y diff --git a/default-configs/targets/ppc64-linux-user.mak b/default-configs/targets/ppc64-linux-user.mak new file mode 100644 index 0000000000..3133346676 --- /dev/null +++ b/default-configs/targets/ppc64-linux-user.mak @@ -0,0 +1,7 @@ +TARGET_ARCH=ppc64 +TARGET_BASE_ARCH=ppc +TARGET_ABI_DIR=ppc +TARGET_SYSTBL_ABI=common,nospu,64 +TARGET_SYSTBL=syscall.tbl +TARGET_WORDS_BIGENDIAN=y +TARGET_XML_FILES= gdb-xml/power64-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml gdb-xml/power-vsx.xml diff --git a/default-configs/targets/ppc64-softmmu.mak b/default-configs/targets/ppc64-softmmu.mak new file mode 100644 index 0000000000..84fbf46be9 --- /dev/null +++ b/default-configs/targets/ppc64-softmmu.mak @@ -0,0 +1,6 @@ +TARGET_ARCH=ppc64 +TARGET_BASE_ARCH=ppc +TARGET_WORDS_BIGENDIAN=y +TARGET_SUPPORTS_MTTCG=y +TARGET_XML_FILES= gdb-xml/power64-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml gdb-xml/power-vsx.xml +TARGET_NEED_FDT=y diff --git a/default-configs/targets/ppc64abi32-linux-user.mak b/default-configs/targets/ppc64abi32-linux-user.mak new file mode 100644 index 0000000000..0945451081 --- /dev/null +++ b/default-configs/targets/ppc64abi32-linux-user.mak @@ -0,0 +1,8 @@ +TARGET_ARCH=ppc64 +TARGET_ABI32=y +TARGET_BASE_ARCH=ppc +TARGET_ABI_DIR=ppc +TARGET_SYSTBL_ABI=common,nospu,32 +TARGET_SYSTBL=syscall.tbl +TARGET_WORDS_BIGENDIAN=y +TARGET_XML_FILES= gdb-xml/power64-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml gdb-xml/power-vsx.xml diff --git a/default-configs/targets/ppc64le-linux-user.mak b/default-configs/targets/ppc64le-linux-user.mak new file mode 100644 index 0000000000..426d5a28d6 --- /dev/null +++ b/default-configs/targets/ppc64le-linux-user.mak @@ -0,0 +1,6 @@ +TARGET_ARCH=ppc64 +TARGET_BASE_ARCH=ppc +TARGET_ABI_DIR=ppc +TARGET_SYSTBL_ABI=common,nospu,64 +TARGET_SYSTBL=syscall.tbl +TARGET_XML_FILES= gdb-xml/power64-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml gdb-xml/power-vsx.xml diff --git a/default-configs/targets/riscv32-linux-user.mak b/default-configs/targets/riscv32-linux-user.mak new file mode 100644 index 0000000000..bd2f1fd497 --- /dev/null +++ b/default-configs/targets/riscv32-linux-user.mak @@ -0,0 +1,5 @@ +TARGET_ARCH=riscv32 +TARGET_BASE_ARCH=riscv +TARGET_ABI_DIR=riscv +TARGET_XML_FILES= gdb-xml/riscv-32bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-32bit-virtual.xml +CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/default-configs/targets/riscv32-softmmu.mak b/default-configs/targets/riscv32-softmmu.mak new file mode 100644 index 0000000000..d8b71cddcd --- /dev/null +++ b/default-configs/targets/riscv32-softmmu.mak @@ -0,0 +1,5 @@ +TARGET_ARCH=riscv32 +TARGET_BASE_ARCH=riscv +TARGET_SUPPORTS_MTTCG=y +TARGET_XML_FILES= gdb-xml/riscv-32bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-32bit-virtual.xml +TARGET_NEED_FDT=y diff --git a/default-configs/targets/riscv64-linux-user.mak b/default-configs/targets/riscv64-linux-user.mak new file mode 100644 index 0000000000..4aca7662ce --- /dev/null +++ b/default-configs/targets/riscv64-linux-user.mak @@ -0,0 +1,5 @@ +TARGET_ARCH=riscv64 +TARGET_BASE_ARCH=riscv +TARGET_ABI_DIR=riscv +TARGET_XML_FILES= gdb-xml/riscv-64bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-64bit-virtual.xml +CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y diff --git a/default-configs/targets/riscv64-softmmu.mak b/default-configs/targets/riscv64-softmmu.mak new file mode 100644 index 0000000000..7c0e7eeb42 --- /dev/null +++ b/default-configs/targets/riscv64-softmmu.mak @@ -0,0 +1,5 @@ +TARGET_ARCH=riscv64 +TARGET_BASE_ARCH=riscv +TARGET_SUPPORTS_MTTCG=y +TARGET_XML_FILES= gdb-xml/riscv-64bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-64bit-virtual.xml +TARGET_NEED_FDT=y diff --git a/default-configs/targets/rx-softmmu.mak b/default-configs/targets/rx-softmmu.mak new file mode 100644 index 0000000000..0c458b2d07 --- /dev/null +++ b/default-configs/targets/rx-softmmu.mak @@ -0,0 +1,3 @@ +TARGET_ARCH=rx +TARGET_XML_FILES= gdb-xml/rx-core.xml +TARGET_NEED_FDT=y diff --git a/default-configs/targets/s390x-linux-user.mak b/default-configs/targets/s390x-linux-user.mak new file mode 100644 index 0000000000..9e31ce6457 --- /dev/null +++ b/default-configs/targets/s390x-linux-user.mak @@ -0,0 +1,5 @@ +TARGET_ARCH=s390x +TARGET_SYSTBL_ABI=common,64 +TARGET_SYSTBL=syscall.tbl +TARGET_WORDS_BIGENDIAN=y +TARGET_XML_FILES= gdb-xml/s390x-core64.xml gdb-xml/s390-acr.xml gdb-xml/s390-fpr.xml gdb-xml/s390-vx.xml gdb-xml/s390-cr.xml gdb-xml/s390-virt.xml gdb-xml/s390-gs.xml diff --git a/default-configs/targets/s390x-softmmu.mak b/default-configs/targets/s390x-softmmu.mak new file mode 100644 index 0000000000..fd9fbd870d --- /dev/null +++ b/default-configs/targets/s390x-softmmu.mak @@ -0,0 +1,4 @@ +TARGET_ARCH=s390x +TARGET_WORDS_BIGENDIAN=y +TARGET_SUPPORTS_MTTCG=y +TARGET_XML_FILES= gdb-xml/s390x-core64.xml gdb-xml/s390-acr.xml gdb-xml/s390-fpr.xml gdb-xml/s390-vx.xml gdb-xml/s390-cr.xml gdb-xml/s390-virt.xml gdb-xml/s390-gs.xml diff --git a/default-configs/targets/sh4-linux-user.mak b/default-configs/targets/sh4-linux-user.mak new file mode 100644 index 0000000000..0152d6621e --- /dev/null +++ b/default-configs/targets/sh4-linux-user.mak @@ -0,0 +1,5 @@ +TARGET_ARCH=sh4 +TARGET_SYSTBL_ABI=common +TARGET_SYSTBL=syscall.tbl +TARGET_ALIGNED_ONLY=y +TARGET_HAS_BFLT=y diff --git a/default-configs/targets/sh4-softmmu.mak b/default-configs/targets/sh4-softmmu.mak new file mode 100644 index 0000000000..95896376c4 --- /dev/null +++ b/default-configs/targets/sh4-softmmu.mak @@ -0,0 +1,2 @@ +TARGET_ARCH=sh4 +TARGET_ALIGNED_ONLY=y diff --git a/default-configs/targets/sh4eb-linux-user.mak b/default-configs/targets/sh4eb-linux-user.mak new file mode 100644 index 0000000000..9b6fb4c1bb --- /dev/null +++ b/default-configs/targets/sh4eb-linux-user.mak @@ -0,0 +1,6 @@ +TARGET_ARCH=sh4 +TARGET_SYSTBL_ABI=common +TARGET_SYSTBL=syscall.tbl +TARGET_ALIGNED_ONLY=y +TARGET_WORDS_BIGENDIAN=y +TARGET_HAS_BFLT=y diff --git a/default-configs/targets/sh4eb-softmmu.mak b/default-configs/targets/sh4eb-softmmu.mak new file mode 100644 index 0000000000..382e9a80f8 --- /dev/null +++ b/default-configs/targets/sh4eb-softmmu.mak @@ -0,0 +1,3 @@ +TARGET_ARCH=sh4 +TARGET_ALIGNED_ONLY=y +TARGET_WORDS_BIGENDIAN=y diff --git a/default-configs/targets/sparc-bsd-user.mak b/default-configs/targets/sparc-bsd-user.mak new file mode 100644 index 0000000000..9ba3d7b07f --- /dev/null +++ b/default-configs/targets/sparc-bsd-user.mak @@ -0,0 +1,3 @@ +TARGET_ARCH=sparc +TARGET_ALIGNED_ONLY=y +TARGET_WORDS_BIGENDIAN=y diff --git a/default-configs/targets/sparc-linux-user.mak b/default-configs/targets/sparc-linux-user.mak new file mode 100644 index 0000000000..53dc7aaed5 --- /dev/null +++ b/default-configs/targets/sparc-linux-user.mak @@ -0,0 +1,5 @@ +TARGET_ARCH=sparc +TARGET_SYSTBL_ABI=common,32 +TARGET_SYSTBL=syscall.tbl +TARGET_ALIGNED_ONLY=y +TARGET_WORDS_BIGENDIAN=y diff --git a/default-configs/targets/sparc-softmmu.mak b/default-configs/targets/sparc-softmmu.mak new file mode 100644 index 0000000000..9ba3d7b07f --- /dev/null +++ b/default-configs/targets/sparc-softmmu.mak @@ -0,0 +1,3 @@ +TARGET_ARCH=sparc +TARGET_ALIGNED_ONLY=y +TARGET_WORDS_BIGENDIAN=y diff --git a/default-configs/targets/sparc32plus-linux-user.mak b/default-configs/targets/sparc32plus-linux-user.mak new file mode 100644 index 0000000000..e4c51df3dc --- /dev/null +++ b/default-configs/targets/sparc32plus-linux-user.mak @@ -0,0 +1,8 @@ +TARGET_ARCH=sparc64 +TARGET_ABI32=y +TARGET_BASE_ARCH=sparc +TARGET_ABI_DIR=sparc +TARGET_SYSTBL_ABI=common,32 +TARGET_SYSTBL=syscall.tbl +TARGET_ALIGNED_ONLY=y +TARGET_WORDS_BIGENDIAN=y diff --git a/default-configs/targets/sparc64-bsd-user.mak b/default-configs/targets/sparc64-bsd-user.mak new file mode 100644 index 0000000000..8dd3217800 --- /dev/null +++ b/default-configs/targets/sparc64-bsd-user.mak @@ -0,0 +1,4 @@ +TARGET_ARCH=sparc64 +TARGET_BASE_ARCH=sparc +TARGET_ALIGNED_ONLY=y +TARGET_WORDS_BIGENDIAN=y diff --git a/default-configs/targets/sparc64-linux-user.mak b/default-configs/targets/sparc64-linux-user.mak new file mode 100644 index 0000000000..846924201a --- /dev/null +++ b/default-configs/targets/sparc64-linux-user.mak @@ -0,0 +1,6 @@ +TARGET_ARCH=sparc64 +TARGET_BASE_ARCH=sparc +TARGET_SYSTBL_ABI=common,64 +TARGET_SYSTBL=syscall.tbl +TARGET_ALIGNED_ONLY=y +TARGET_WORDS_BIGENDIAN=y diff --git a/default-configs/targets/sparc64-softmmu.mak b/default-configs/targets/sparc64-softmmu.mak new file mode 100644 index 0000000000..8dd3217800 --- /dev/null +++ b/default-configs/targets/sparc64-softmmu.mak @@ -0,0 +1,4 @@ +TARGET_ARCH=sparc64 +TARGET_BASE_ARCH=sparc +TARGET_ALIGNED_ONLY=y +TARGET_WORDS_BIGENDIAN=y diff --git a/default-configs/targets/tricore-softmmu.mak b/default-configs/targets/tricore-softmmu.mak new file mode 100644 index 0000000000..96b10af853 --- /dev/null +++ b/default-configs/targets/tricore-softmmu.mak @@ -0,0 +1 @@ +TARGET_ARCH=tricore diff --git a/default-configs/targets/unicore32-softmmu.mak b/default-configs/targets/unicore32-softmmu.mak new file mode 100644 index 0000000000..57331e94fe --- /dev/null +++ b/default-configs/targets/unicore32-softmmu.mak @@ -0,0 +1 @@ +TARGET_ARCH=unicore32 diff --git a/default-configs/targets/x86_64-bsd-user.mak b/default-configs/targets/x86_64-bsd-user.mak new file mode 100644 index 0000000000..799cd4acd4 --- /dev/null +++ b/default-configs/targets/x86_64-bsd-user.mak @@ -0,0 +1,3 @@ +TARGET_ARCH=x86_64 +TARGET_BASE_ARCH=i386 +TARGET_XML_FILES= gdb-xml/i386-64bit.xml diff --git a/default-configs/targets/x86_64-linux-user.mak b/default-configs/targets/x86_64-linux-user.mak new file mode 100644 index 0000000000..9ceefbb615 --- /dev/null +++ b/default-configs/targets/x86_64-linux-user.mak @@ -0,0 +1,5 @@ +TARGET_ARCH=x86_64 +TARGET_BASE_ARCH=i386 +TARGET_SYSTBL_ABI=common,64 +TARGET_SYSTBL=syscall_64.tbl +TARGET_XML_FILES= gdb-xml/i386-64bit.xml diff --git a/default-configs/targets/x86_64-softmmu.mak b/default-configs/targets/x86_64-softmmu.mak new file mode 100644 index 0000000000..75e42bc840 --- /dev/null +++ b/default-configs/targets/x86_64-softmmu.mak @@ -0,0 +1,4 @@ +TARGET_ARCH=x86_64 +TARGET_BASE_ARCH=i386 +TARGET_SUPPORTS_MTTCG=y +TARGET_XML_FILES= gdb-xml/i386-64bit.xml diff --git a/default-configs/targets/xtensa-linux-user.mak b/default-configs/targets/xtensa-linux-user.mak new file mode 100644 index 0000000000..fc95cc60f5 --- /dev/null +++ b/default-configs/targets/xtensa-linux-user.mak @@ -0,0 +1,5 @@ +TARGET_ARCH=xtensa +TARGET_SYSTBL_ABI=common +TARGET_SYSTBL=syscall.tbl +TARGET_ALIGNED_ONLY=y +TARGET_HAS_BFLT=y diff --git a/default-configs/targets/xtensa-softmmu.mak b/default-configs/targets/xtensa-softmmu.mak new file mode 100644 index 0000000000..26c0285655 --- /dev/null +++ b/default-configs/targets/xtensa-softmmu.mak @@ -0,0 +1,3 @@ +TARGET_ARCH=xtensa +TARGET_ALIGNED_ONLY=y +TARGET_SUPPORTS_MTTCG=y diff --git a/default-configs/targets/xtensaeb-linux-user.mak b/default-configs/targets/xtensaeb-linux-user.mak new file mode 100644 index 0000000000..cfc3518118 --- /dev/null +++ b/default-configs/targets/xtensaeb-linux-user.mak @@ -0,0 +1,6 @@ +TARGET_ARCH=xtensa +TARGET_SYSTBL_ABI=common +TARGET_SYSTBL=syscall.tbl +TARGET_ALIGNED_ONLY=y +TARGET_WORDS_BIGENDIAN=y +TARGET_HAS_BFLT=y diff --git a/default-configs/targets/xtensaeb-softmmu.mak b/default-configs/targets/xtensaeb-softmmu.mak new file mode 100644 index 0000000000..14cb9289a6 --- /dev/null +++ b/default-configs/targets/xtensaeb-softmmu.mak @@ -0,0 +1,4 @@ +TARGET_ARCH=xtensa +TARGET_ALIGNED_ONLY=y +TARGET_WORDS_BIGENDIAN=y +TARGET_SUPPORTS_MTTCG=y diff --git a/default-configs/tilegx-linux-user.mak b/default-configs/tilegx-linux-user.mak deleted file mode 100644 index 3e47493af0..0000000000 --- a/default-configs/tilegx-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for tilegx-linux-user diff --git a/default-configs/tricore-softmmu.mak b/default-configs/tricore-softmmu.mak deleted file mode 100644 index c397cff38a..0000000000 --- a/default-configs/tricore-softmmu.mak +++ /dev/null @@ -1 +0,0 @@ -CONFIG_TRICORE=y diff --git a/default-configs/x86_64-bsd-user.mak b/default-configs/x86_64-bsd-user.mak deleted file mode 100644 index 73e5d34ec5..0000000000 --- a/default-configs/x86_64-bsd-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for x86_64-bsd-user diff --git a/default-configs/x86_64-linux-user.mak b/default-configs/x86_64-linux-user.mak deleted file mode 100644 index bec1d9e7c6..0000000000 --- a/default-configs/x86_64-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for x86_64-linux-user diff --git a/default-configs/xtensa-linux-user.mak b/default-configs/xtensa-linux-user.mak deleted file mode 100644 index fd1d350ee9..0000000000 --- a/default-configs/xtensa-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for xtensa-linux-user diff --git a/default-configs/xtensaeb-linux-user.mak b/default-configs/xtensaeb-linux-user.mak deleted file mode 100644 index fd1d350ee9..0000000000 --- a/default-configs/xtensaeb-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -# Default configuration for xtensa-linux-user diff --git a/disas.c b/disas.c index c1397d3933..a61f95b580 100644 --- a/disas.c +++ b/disas.c @@ -16,133 +16,68 @@ typedef struct CPUDebug { /* Filled in by elfload.c. Simplistic, but will do for now. */ struct syminfo *syminfos = NULL; -/* Get LENGTH bytes from info's buffer, at target address memaddr. - Transfer them to myaddr. */ -int -buffer_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, - struct disassemble_info *info) +/* + * Get LENGTH bytes from info's buffer, at host address memaddr. + * Transfer them to myaddr. + */ +static int host_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, + struct disassemble_info *info) { if (memaddr < info->buffer_vma - || memaddr + length > info->buffer_vma + info->buffer_length) + || memaddr + length > info->buffer_vma + info->buffer_length) { /* Out of bounds. Use EIO because GDB uses it. */ return EIO; + } memcpy (myaddr, info->buffer + (memaddr - info->buffer_vma), length); return 0; } -/* Get LENGTH bytes from info's buffer, at target address memaddr. - Transfer them to myaddr. */ -static int -target_read_memory (bfd_vma memaddr, - bfd_byte *myaddr, - int length, - struct disassemble_info *info) +/* + * Get LENGTH bytes from info's buffer, at target address memaddr. + * Transfer them to myaddr. + */ +static int target_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, + struct disassemble_info *info) { CPUDebug *s = container_of(info, CPUDebug, info); - int r; - - r = cpu_memory_rw_debug(s->cpu, memaddr, myaddr, length, 0); - + int r = cpu_memory_rw_debug(s->cpu, memaddr, myaddr, length, 0); return r ? EIO : 0; } -/* Print an error message. We can assume that this is in response to - an error return from buffer_read_memory. */ -void -perror_memory (int status, bfd_vma memaddr, struct disassemble_info *info) +/* + * Print an error message. We can assume that this is in response to + * an error return from {host,target}_read_memory. + */ +static void perror_memory(int status, bfd_vma memaddr, + struct disassemble_info *info) { - if (status != EIO) - /* Can't happen. */ - (*info->fprintf_func) (info->stream, "Unknown error %d\n", status); - else - /* Actually, address between memaddr and memaddr + len was - out of bounds. */ - (*info->fprintf_func) (info->stream, - "Address 0x%" PRIx64 " is out of bounds.\n", memaddr); + if (status != EIO) { + /* Can't happen. */ + info->fprintf_func(info->stream, "Unknown error %d\n", status); + } else { + /* Address between memaddr and memaddr + len was out of bounds. */ + info->fprintf_func(info->stream, + "Address 0x%" PRIx64 " is out of bounds.\n", + memaddr); + } } -/* This could be in a separate file, to save minuscule amounts of space - in statically linked executables. */ - -/* Just print the address is hex. This is included for completeness even - though both GDB and objdump provide their own (to print symbolic - addresses). */ - -void -generic_print_address (bfd_vma addr, struct disassemble_info *info) +/* Print address in hex. */ +static void print_address(bfd_vma addr, struct disassemble_info *info) { - (*info->fprintf_func) (info->stream, "0x%" PRIx64, addr); + info->fprintf_func(info->stream, "0x%" PRIx64, addr); } /* Print address in hex, truncated to the width of a host virtual address. */ -static void -generic_print_host_address(bfd_vma addr, struct disassemble_info *info) +static void host_print_address(bfd_vma addr, struct disassemble_info *info) { - uint64_t mask = ~0ULL >> (64 - (sizeof(void *) * 8)); - generic_print_address(addr & mask, info); + print_address((uintptr_t)addr, info); } -/* Just return the given address. */ - -int -generic_symbol_at_address (bfd_vma addr, struct disassemble_info *info) +/* Stub prevents some fruitless earching in optabs disassemblers. */ +static int symbol_at_address(bfd_vma addr, struct disassemble_info *info) { - return 1; -} - -bfd_vma bfd_getl64 (const bfd_byte *addr) -{ - unsigned long long v; - - v = (unsigned long long) addr[0]; - v |= (unsigned long long) addr[1] << 8; - v |= (unsigned long long) addr[2] << 16; - v |= (unsigned long long) addr[3] << 24; - v |= (unsigned long long) addr[4] << 32; - v |= (unsigned long long) addr[5] << 40; - v |= (unsigned long long) addr[6] << 48; - v |= (unsigned long long) addr[7] << 56; - return (bfd_vma) v; -} - -bfd_vma bfd_getl32 (const bfd_byte *addr) -{ - unsigned long v; - - v = (unsigned long) addr[0]; - v |= (unsigned long) addr[1] << 8; - v |= (unsigned long) addr[2] << 16; - v |= (unsigned long) addr[3] << 24; - return (bfd_vma) v; -} - -bfd_vma bfd_getb32 (const bfd_byte *addr) -{ - unsigned long v; - - v = (unsigned long) addr[0] << 24; - v |= (unsigned long) addr[1] << 16; - v |= (unsigned long) addr[2] << 8; - v |= (unsigned long) addr[3]; - return (bfd_vma) v; -} - -bfd_vma bfd_getl16 (const bfd_byte *addr) -{ - unsigned long v; - - v = (unsigned long) addr[0]; - v |= (unsigned long) addr[1] << 8; - return (bfd_vma) v; -} - -bfd_vma bfd_getb16 (const bfd_byte *addr) -{ - unsigned long v; - - v = (unsigned long) addr[0] << 24; - v |= (unsigned long) addr[1] << 16; - return (bfd_vma) v; + return 1; } static int print_insn_objdump(bfd_vma pc, disassemble_info *info, @@ -174,292 +109,121 @@ static int print_insn_od_target(bfd_vma pc, disassemble_info *info) return print_insn_objdump(pc, info, "OBJD-T"); } -#ifdef CONFIG_CAPSTONE -/* Temporary storage for the capstone library. This will be alloced via - malloc with a size private to the library; thus there's no reason not - to share this across calls and across host vs target disassembly. */ -static __thread cs_insn *cap_insn; - -/* Initialize the Capstone library. */ -/* ??? It would be nice to cache this. We would need one handle for the - host and one for the target. For most targets we can reset specific - parameters via cs_option(CS_OPT_MODE, new_mode), but we cannot change - CS_ARCH_* in this way. Thus we would need to be able to close and - re-open the target handle with a different arch for the target in order - to handle AArch64 vs AArch32 mode switching. */ -static cs_err cap_disas_start(disassemble_info *info, csh *handle) +static void initialize_debug(CPUDebug *s) { - cs_mode cap_mode = info->cap_mode; - cs_err err; - - cap_mode += (info->endian == BFD_ENDIAN_BIG ? CS_MODE_BIG_ENDIAN - : CS_MODE_LITTLE_ENDIAN); - - err = cs_open(info->cap_arch, cap_mode, handle); - if (err != CS_ERR_OK) { - return err; - } - - /* ??? There probably ought to be a better place to put this. */ - if (info->cap_arch == CS_ARCH_X86) { - /* We don't care about errors (if for some reason the library - is compiled without AT&T syntax); the user will just have - to deal with the Intel syntax. */ - cs_option(*handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT); - } - - /* "Disassemble" unknown insns as ".byte W,X,Y,Z". */ - cs_option(*handle, CS_OPT_SKIPDATA, CS_OPT_ON); - - /* Allocate temp space for cs_disasm_iter. */ - if (cap_insn == NULL) { - cap_insn = cs_malloc(*handle); - if (cap_insn == NULL) { - cs_close(handle); - return CS_ERR_MEM; - } - } - return CS_ERR_OK; + memset(s, 0, sizeof(*s)); + s->info.arch = bfd_arch_unknown; + s->info.cap_arch = -1; + s->info.cap_insn_unit = 4; + s->info.cap_insn_split = 4; + s->info.memory_error_func = perror_memory; + s->info.symbol_at_address_func = symbol_at_address; } -static void cap_dump_insn_units(disassemble_info *info, cs_insn *insn, - int i, int n) +static void initialize_debug_target(CPUDebug *s, CPUState *cpu) { - fprintf_function print = info->fprintf_func; - FILE *stream = info->stream; + initialize_debug(s); - switch (info->cap_insn_unit) { - case 4: - if (info->endian == BFD_ENDIAN_BIG) { - for (; i < n; i += 4) { - print(stream, " %08x", ldl_be_p(insn->bytes + i)); - - } - } else { - for (; i < n; i += 4) { - print(stream, " %08x", ldl_le_p(insn->bytes + i)); - } - } - break; - - case 2: - if (info->endian == BFD_ENDIAN_BIG) { - for (; i < n; i += 2) { - print(stream, " %04x", lduw_be_p(insn->bytes + i)); - } - } else { - for (; i < n; i += 2) { - print(stream, " %04x", lduw_le_p(insn->bytes + i)); - } - } - break; - - default: - for (; i < n; i++) { - print(stream, " %02x", insn->bytes[i]); - } - break; - } -} - -static void cap_dump_insn(disassemble_info *info, cs_insn *insn, - const char *note) -{ - fprintf_function print = info->fprintf_func; - int i, n, split; - - print(info->stream, "0x%08" PRIx64 ": ", insn->address); - - n = insn->size; - split = info->cap_insn_split; - - /* Dump the first SPLIT bytes of the instruction. */ - cap_dump_insn_units(info, insn, 0, MIN(n, split)); - - /* Add padding up to SPLIT so that mnemonics line up. */ - if (n < split) { - int width = (split - n) / info->cap_insn_unit; - width *= (2 * info->cap_insn_unit + 1); - print(info->stream, "%*s", width, ""); - } - - /* Print the actual instruction. */ - print(info->stream, " %-8s %s", insn->mnemonic, insn->op_str); - if (note) { - print(info->stream, "\t\t%s", note); - } - print(info->stream, "\n"); - - /* Dump any remaining part of the insn on subsequent lines. */ - for (i = split; i < n; i += split) { - print(info->stream, "0x%08" PRIx64 ": ", insn->address + i); - cap_dump_insn_units(info, insn, i, MIN(n, i + split)); - print(info->stream, "\n"); - } -} - -/* Disassemble SIZE bytes at PC for the target. */ -static bool cap_disas_target(disassemble_info *info, uint64_t pc, size_t size) -{ - uint8_t cap_buf[1024]; - csh handle; - cs_insn *insn; - size_t csize = 0; - - if (cap_disas_start(info, &handle) != CS_ERR_OK) { - return false; - } - insn = cap_insn; - - while (1) { - size_t tsize = MIN(sizeof(cap_buf) - csize, size); - const uint8_t *cbuf = cap_buf; - - target_read_memory(pc + csize, cap_buf + csize, tsize, info); - csize += tsize; - size -= tsize; - - while (cs_disasm_iter(handle, &cbuf, &csize, &pc, insn)) { - cap_dump_insn(info, insn, NULL); - } - - /* If the target memory is not consumed, go back for more... */ - if (size != 0) { - /* ... taking care to move any remaining fractional insn - to the beginning of the buffer. */ - if (csize != 0) { - memmove(cap_buf, cbuf, csize); - } - continue; - } - - /* Since the target memory is consumed, we should not have - a remaining fractional insn. */ - if (csize != 0) { - (*info->fprintf_func)(info->stream, - "Disassembler disagrees with translator " - "over instruction decoding\n" - "Please report this to qemu-devel@nongnu.org\n"); - } - break; - } - - cs_close(&handle); - return true; -} - -/* Disassemble SIZE bytes at CODE for the host. */ -static bool cap_disas_host(disassemble_info *info, void *code, size_t size, - const char *note) -{ - csh handle; - const uint8_t *cbuf; - cs_insn *insn; - uint64_t pc; - - if (cap_disas_start(info, &handle) != CS_ERR_OK) { - return false; - } - insn = cap_insn; - - cbuf = code; - pc = (uintptr_t)code; - - while (cs_disasm_iter(handle, &cbuf, &size, &pc, insn)) { - cap_dump_insn(info, insn, note); - note = NULL; - } - if (size != 0) { - (*info->fprintf_func)(info->stream, - "Disassembler disagrees with TCG over instruction encoding\n" - "Please report this to qemu-devel@nongnu.org\n"); - } - - cs_close(&handle); - return true; -} - -#if !defined(CONFIG_USER_ONLY) -/* Disassemble COUNT insns at PC for the target. */ -static bool cap_disas_monitor(disassemble_info *info, uint64_t pc, int count) -{ - uint8_t cap_buf[32]; - csh handle; - cs_insn *insn; - size_t csize = 0; - - if (cap_disas_start(info, &handle) != CS_ERR_OK) { - return false; - } - insn = cap_insn; - - while (1) { - /* We want to read memory for one insn, but generically we do not - know how much memory that is. We have a small buffer which is - known to be sufficient for all supported targets. Try to not - read beyond the page, Just In Case. For even more simplicity, - ignore the actual target page size and use a 1k boundary. If - that turns out to be insufficient, we'll come back around the - loop and read more. */ - uint64_t epc = QEMU_ALIGN_UP(pc + csize + 1, 1024); - size_t tsize = MIN(sizeof(cap_buf) - csize, epc - pc); - const uint8_t *cbuf = cap_buf; - - /* Make certain that we can make progress. */ - assert(tsize != 0); - info->read_memory_func(pc, cap_buf + csize, tsize, info); - csize += tsize; - - if (cs_disasm_iter(handle, &cbuf, &csize, &pc, insn)) { - cap_dump_insn(info, insn, NULL); - if (--count <= 0) { - break; - } - } - memmove(cap_buf, cbuf, csize); - } - - cs_close(&handle); - return true; -} -#endif /* !CONFIG_USER_ONLY */ + s->cpu = cpu; + s->info.read_memory_func = target_read_memory; + s->info.print_address_func = print_address; +#ifdef TARGET_WORDS_BIGENDIAN + s->info.endian = BFD_ENDIAN_BIG; #else -# define cap_disas_target(i, p, s) false -# define cap_disas_host(i, p, s, n) false -# define cap_disas_monitor(i, p, c) false -# define cap_disas_plugin(i, p, c) false -#endif /* CONFIG_CAPSTONE */ + s->info.endian = BFD_ENDIAN_LITTLE; +#endif + + CPUClass *cc = CPU_GET_CLASS(cpu); + if (cc->disas_set_info) { + cc->disas_set_info(cpu, &s->info); + } +} + +static void initialize_debug_host(CPUDebug *s) +{ + initialize_debug(s); + + s->info.read_memory_func = host_read_memory; + s->info.print_address_func = host_print_address; +#ifdef HOST_WORDS_BIGENDIAN + s->info.endian = BFD_ENDIAN_BIG; +#else + s->info.endian = BFD_ENDIAN_LITTLE; +#endif +#if defined(CONFIG_TCG_INTERPRETER) + s->info.print_insn = print_insn_tci; +#elif defined(__i386__) + s->info.mach = bfd_mach_i386_i386; + s->info.print_insn = print_insn_i386; + s->info.cap_arch = CS_ARCH_X86; + s->info.cap_mode = CS_MODE_32; + s->info.cap_insn_unit = 1; + s->info.cap_insn_split = 8; +#elif defined(__x86_64__) + s->info.mach = bfd_mach_x86_64; + s->info.print_insn = print_insn_i386; + s->info.cap_arch = CS_ARCH_X86; + s->info.cap_mode = CS_MODE_64; + s->info.cap_insn_unit = 1; + s->info.cap_insn_split = 8; +#elif defined(_ARCH_PPC) + s->info.disassembler_options = (char *)"any"; + s->info.print_insn = print_insn_ppc; + s->info.cap_arch = CS_ARCH_PPC; +# ifdef _ARCH_PPC64 + s->info.cap_mode = CS_MODE_64; +# endif +#elif defined(__riscv) && defined(CONFIG_RISCV_DIS) +#if defined(_ILP32) || (__riscv_xlen == 32) + s->info.print_insn = print_insn_riscv32; +#elif defined(_LP64) + s->info.print_insn = print_insn_riscv64; +#else +#error unsupported RISC-V ABI +#endif +#elif defined(__aarch64__) + s->info.cap_arch = CS_ARCH_ARM64; +# ifdef CONFIG_ARM_A64_DIS + s->info.print_insn = print_insn_arm_a64; +# endif +#elif defined(__alpha__) + s->info.print_insn = print_insn_alpha; +#elif defined(__sparc__) + s->info.print_insn = print_insn_sparc; + s->info.mach = bfd_mach_sparc_v9b; +#elif defined(__arm__) + /* TCG only generates code for arm mode. */ + s->info.print_insn = print_insn_arm; + s->info.cap_arch = CS_ARCH_ARM; +#elif defined(__MIPSEB__) + s->info.print_insn = print_insn_big_mips; +#elif defined(__MIPSEL__) + s->info.print_insn = print_insn_little_mips; +#elif defined(__m68k__) + s->info.print_insn = print_insn_m68k; +#elif defined(__s390__) + s->info.print_insn = print_insn_s390; + s->info.cap_arch = CS_ARCH_SYSZ; + s->info.cap_insn_unit = 2; + s->info.cap_insn_split = 6; +#elif defined(__hppa__) + s->info.print_insn = print_insn_hppa; +#endif +} /* Disassemble this for me please... (debugging). */ void target_disas(FILE *out, CPUState *cpu, target_ulong code, target_ulong size) { - CPUClass *cc = CPU_GET_CLASS(cpu); target_ulong pc; int count; CPUDebug s; - INIT_DISASSEMBLE_INFO(s.info, out, fprintf); - - s.cpu = cpu; - s.info.read_memory_func = target_read_memory; + initialize_debug_target(&s, cpu); + s.info.fprintf_func = fprintf; + s.info.stream = out; s.info.buffer_vma = code; s.info.buffer_length = size; - s.info.print_address_func = generic_print_address; - s.info.cap_arch = -1; - s.info.cap_mode = 0; - s.info.cap_insn_unit = 4; - s.info.cap_insn_split = 4; - -#ifdef TARGET_WORDS_BIGENDIAN - s.info.endian = BFD_ENDIAN_BIG; -#else - s.info.endian = BFD_ENDIAN_LITTLE; -#endif - - if (cc->disas_set_info) { - cc->disas_set_info(cpu, &s.info); - } if (s.info.cap_arch >= 0 && cap_disas_target(&s.info, code, size)) { return; @@ -485,13 +249,12 @@ void target_disas(FILE *out, CPUState *cpu, target_ulong code, } } -static __thread GString plugin_disas_output; - static int plugin_printf(FILE *stream, const char *fmt, ...) { - va_list va; - GString *s = &plugin_disas_output; + /* We abuse the FILE parameter to pass a GString. */ + GString *s = (GString *)stream; int initial_len = s->len; + va_list va; va_start(va, fmt); g_string_append_vprintf(s, fmt, va); @@ -506,40 +269,6 @@ static void plugin_print_address(bfd_vma addr, struct disassemble_info *info) } -#ifdef CONFIG_CAPSTONE -/* Disassemble a single instruction directly into plugin output */ -static -bool cap_disas_plugin(disassemble_info *info, uint64_t pc, size_t size) -{ - uint8_t cap_buf[1024]; - csh handle; - cs_insn *insn; - size_t csize = 0; - int count; - GString *s = &plugin_disas_output; - - if (cap_disas_start(info, &handle) != CS_ERR_OK) { - return false; - } - insn = cap_insn; - - size_t tsize = MIN(sizeof(cap_buf) - csize, size); - const uint8_t *cbuf = cap_buf; - target_read_memory(pc, cap_buf, tsize, info); - - count = cs_disasm(handle, cbuf, size, 0, 1, &insn); - - if (count) { - g_string_printf(s, "%s %s", insn->mnemonic, insn->op_str); - } else { - g_string_printf(s, "cs_disasm failed"); - } - - cs_close(&handle); - return true; -} -#endif - /* * We should only be dissembling one instruction at a time here. If * there is left over it usually indicates the front end has read more @@ -547,146 +276,52 @@ bool cap_disas_plugin(disassemble_info *info, uint64_t pc, size_t size) */ char *plugin_disas(CPUState *cpu, uint64_t addr, size_t size) { - CPUClass *cc = CPU_GET_CLASS(cpu); - int count; CPUDebug s; - GString *ds = g_string_set_size(&plugin_disas_output, 0); + GString *ds = g_string_new(NULL); - g_assert(ds == &plugin_disas_output); - - INIT_DISASSEMBLE_INFO(s.info, NULL, plugin_printf); - - s.cpu = cpu; - s.info.read_memory_func = target_read_memory; + initialize_debug_target(&s, cpu); + s.info.fprintf_func = plugin_printf; + s.info.stream = (FILE *)ds; /* abuse this slot */ s.info.buffer_vma = addr; s.info.buffer_length = size; s.info.print_address_func = plugin_print_address; - s.info.cap_arch = -1; - s.info.cap_mode = 0; - s.info.cap_insn_unit = 4; - s.info.cap_insn_split = 4; - -#ifdef TARGET_WORDS_BIGENDIAN - s.info.endian = BFD_ENDIAN_BIG; -#else - s.info.endian = BFD_ENDIAN_LITTLE; -#endif - - if (cc->disas_set_info) { - cc->disas_set_info(cpu, &s.info); - } if (s.info.cap_arch >= 0 && cap_disas_plugin(&s.info, addr, size)) { - return g_strdup(ds->str); + ; /* done */ + } else if (s.info.print_insn) { + s.info.print_insn(addr, &s.info); + } else { + ; /* cannot disassemble -- return empty string */ } - if (s.info.print_insn == NULL) { - s.info.print_insn = print_insn_od_target; - } - - count = s.info.print_insn(addr, &s.info); - - /* The decoder probably read more than it needed it's not critical */ - if (count < size) { - warn_report("%s: %zu bytes left over", __func__, size - count); - } - - return g_strdup(ds->str); + /* Return the buffer, freeing the GString container. */ + return g_string_free(ds, false); } /* Disassemble this for me please... (debugging). */ -void disas(FILE *out, void *code, unsigned long size, const char *note) +void disas(FILE *out, const void *code, unsigned long size) { uintptr_t pc; int count; CPUDebug s; - int (*print_insn)(bfd_vma pc, disassemble_info *info) = NULL; - - INIT_DISASSEMBLE_INFO(s.info, out, fprintf); - s.info.print_address_func = generic_print_host_address; + initialize_debug_host(&s); + s.info.fprintf_func = fprintf; + s.info.stream = out; s.info.buffer = code; s.info.buffer_vma = (uintptr_t)code; s.info.buffer_length = size; - s.info.cap_arch = -1; - s.info.cap_mode = 0; - s.info.cap_insn_unit = 4; - s.info.cap_insn_split = 4; -#ifdef HOST_WORDS_BIGENDIAN - s.info.endian = BFD_ENDIAN_BIG; -#else - s.info.endian = BFD_ENDIAN_LITTLE; -#endif -#if defined(CONFIG_TCG_INTERPRETER) - print_insn = print_insn_tci; -#elif defined(__i386__) - s.info.mach = bfd_mach_i386_i386; - print_insn = print_insn_i386; - s.info.cap_arch = CS_ARCH_X86; - s.info.cap_mode = CS_MODE_32; - s.info.cap_insn_unit = 1; - s.info.cap_insn_split = 8; -#elif defined(__x86_64__) - s.info.mach = bfd_mach_x86_64; - print_insn = print_insn_i386; - s.info.cap_arch = CS_ARCH_X86; - s.info.cap_mode = CS_MODE_64; - s.info.cap_insn_unit = 1; - s.info.cap_insn_split = 8; -#elif defined(_ARCH_PPC) - s.info.disassembler_options = (char *)"any"; - print_insn = print_insn_ppc; - s.info.cap_arch = CS_ARCH_PPC; -# ifdef _ARCH_PPC64 - s.info.cap_mode = CS_MODE_64; -# endif -#elif defined(__riscv) && defined(CONFIG_RISCV_DIS) -#if defined(_ILP32) || (__riscv_xlen == 32) - print_insn = print_insn_riscv32; -#elif defined(_LP64) - print_insn = print_insn_riscv64; -#else -#error unsupported RISC-V ABI -#endif -#elif defined(__aarch64__) && defined(CONFIG_ARM_A64_DIS) - print_insn = print_insn_arm_a64; - s.info.cap_arch = CS_ARCH_ARM64; -#elif defined(__alpha__) - print_insn = print_insn_alpha; -#elif defined(__sparc__) - print_insn = print_insn_sparc; - s.info.mach = bfd_mach_sparc_v9b; -#elif defined(__arm__) - print_insn = print_insn_arm; - s.info.cap_arch = CS_ARCH_ARM; - /* TCG only generates code for arm mode. */ -#elif defined(__MIPSEB__) - print_insn = print_insn_big_mips; -#elif defined(__MIPSEL__) - print_insn = print_insn_little_mips; -#elif defined(__m68k__) - print_insn = print_insn_m68k; -#elif defined(__s390__) - print_insn = print_insn_s390; -#elif defined(__hppa__) - print_insn = print_insn_hppa; -#endif - - if (s.info.cap_arch >= 0 && cap_disas_host(&s.info, code, size, note)) { + if (s.info.cap_arch >= 0 && cap_disas_host(&s.info, code, size)) { return; } - if (print_insn == NULL) { - print_insn = print_insn_od_host; + if (s.info.print_insn == NULL) { + s.info.print_insn = print_insn_od_host; } for (pc = (uintptr_t)code; size > 0; pc += count, size -= count) { fprintf(out, "0x%08" PRIxPTR ": ", pc); - count = print_insn(pc, &s.info); - if (note) { - fprintf(out, "\t\t%s", note); - note = NULL; - } + count = s.info.print_insn(pc, &s.info); fprintf(out, "\n"); if (count < 0) { break; @@ -731,31 +366,15 @@ physical_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, void monitor_disas(Monitor *mon, CPUState *cpu, target_ulong pc, int nb_insn, int is_physical) { - CPUClass *cc = CPU_GET_CLASS(cpu); int count, i; CPUDebug s; - INIT_DISASSEMBLE_INFO(s.info, NULL, qemu_fprintf); - - s.cpu = cpu; - s.info.read_memory_func - = (is_physical ? physical_read_memory : target_read_memory); - s.info.print_address_func = generic_print_address; - s.info.buffer_vma = pc; - s.info.cap_arch = -1; - s.info.cap_mode = 0; - s.info.cap_insn_unit = 4; - s.info.cap_insn_split = 4; - -#ifdef TARGET_WORDS_BIGENDIAN - s.info.endian = BFD_ENDIAN_BIG; -#else - s.info.endian = BFD_ENDIAN_LITTLE; -#endif - - if (cc->disas_set_info) { - cc->disas_set_info(cpu, &s.info); + initialize_debug_target(&s, cpu); + s.info.fprintf_func = qemu_fprintf; + if (is_physical) { + s.info.read_memory_func = physical_read_memory; } + s.info.buffer_vma = pc; if (s.info.cap_arch >= 0 && cap_disas_monitor(&s.info, pc, nb_insn)) { return; diff --git a/disas/Makefile.objs b/disas/Makefile.objs deleted file mode 100644 index 3c1cdce026..0000000000 --- a/disas/Makefile.objs +++ /dev/null @@ -1,30 +0,0 @@ - -common-obj-$(CONFIG_ALPHA_DIS) += alpha.o -common-obj-$(CONFIG_ARM_DIS) += arm.o -common-obj-$(CONFIG_ARM_A64_DIS) += arm-a64.o -common-obj-$(CONFIG_ARM_A64_DIS) += libvixl/ -libvixldir = $(SRC_PATH)/disas/libvixl -# The -Wno-sign-compare is needed only for gcc 4.6, which complains about -# some signed-unsigned equality comparisons in libvixl which later gcc -# versions do not. -arm-a64.o-cflags := -I$(libvixldir) -Wno-sign-compare -common-obj-$(CONFIG_CRIS_DIS) += cris.o -common-obj-$(CONFIG_HPPA_DIS) += hppa.o -common-obj-$(CONFIG_I386_DIS) += i386.o -common-obj-$(CONFIG_M68K_DIS) += m68k.o -common-obj-$(CONFIG_MICROBLAZE_DIS) += microblaze.o -common-obj-$(CONFIG_MIPS_DIS) += mips.o -common-obj-$(CONFIG_NANOMIPS_DIS) += nanomips.o -common-obj-$(CONFIG_NIOS2_DIS) += nios2.o -common-obj-$(CONFIG_MOXIE_DIS) += moxie.o -common-obj-$(CONFIG_PPC_DIS) += ppc.o -common-obj-$(CONFIG_RISCV_DIS) += riscv.o -common-obj-$(CONFIG_S390_DIS) += s390.o -common-obj-$(CONFIG_SH4_DIS) += sh4.o -common-obj-$(CONFIG_SPARC_DIS) += sparc.o -common-obj-$(CONFIG_LM32_DIS) += lm32.o -common-obj-$(CONFIG_XTENSA_DIS) += xtensa.o - -# TODO: As long as the TCG interpreter and its generated code depend -# on the QEMU target, we cannot compile the disassembler here. -#common-obj-$(CONFIG_TCI_DIS) += tci.o diff --git a/disas/capstone.c b/disas/capstone.c new file mode 100644 index 0000000000..20bc8f9669 --- /dev/null +++ b/disas/capstone.c @@ -0,0 +1,326 @@ +/* + * Interface to the capstone disassembler. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/bswap.h" +#include "disas/dis-asm.h" +#include "disas/capstone.h" + + +/* + * Temporary storage for the capstone library. This will be alloced via + * malloc with a size private to the library; thus there's no reason not + * to share this across calls and across host vs target disassembly. + */ +static __thread cs_insn *cap_insn; + +/* + * The capstone library always skips 2 bytes for S390X. + * This is less than ideal, since we can tell from the first two bits + * the size of the insn and thus stay in sync with the insn stream. + */ +static size_t CAPSTONE_API +cap_skipdata_s390x_cb(const uint8_t *code, size_t code_size, + size_t offset, void *user_data) +{ + size_t ilen; + + /* See get_ilen() in target/s390x/internal.h. */ + switch (code[offset] >> 6) { + case 0: + ilen = 2; + break; + case 1: + case 2: + ilen = 4; + break; + default: + ilen = 6; + break; + } + + return ilen; +} + +static const cs_opt_skipdata cap_skipdata_s390x = { + .mnemonic = ".byte", + .callback = cap_skipdata_s390x_cb +}; + +/* + * Initialize the Capstone library. + * + * ??? It would be nice to cache this. We would need one handle for the + * host and one for the target. For most targets we can reset specific + * parameters via cs_option(CS_OPT_MODE, new_mode), but we cannot change + * CS_ARCH_* in this way. Thus we would need to be able to close and + * re-open the target handle with a different arch for the target in order + * to handle AArch64 vs AArch32 mode switching. + */ +static cs_err cap_disas_start(disassemble_info *info, csh *handle) +{ + cs_mode cap_mode = info->cap_mode; + cs_err err; + + cap_mode += (info->endian == BFD_ENDIAN_BIG ? CS_MODE_BIG_ENDIAN + : CS_MODE_LITTLE_ENDIAN); + + err = cs_open(info->cap_arch, cap_mode, handle); + if (err != CS_ERR_OK) { + return err; + } + + /* "Disassemble" unknown insns as ".byte W,X,Y,Z". */ + cs_option(*handle, CS_OPT_SKIPDATA, CS_OPT_ON); + + switch (info->cap_arch) { + case CS_ARCH_SYSZ: + cs_option(*handle, CS_OPT_SKIPDATA_SETUP, + (uintptr_t)&cap_skipdata_s390x); + break; + + case CS_ARCH_X86: + /* + * We don't care about errors (if for some reason the library + * is compiled without AT&T syntax); the user will just have + * to deal with the Intel syntax. + */ + cs_option(*handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT); + break; + } + + /* Allocate temp space for cs_disasm_iter. */ + if (cap_insn == NULL) { + cap_insn = cs_malloc(*handle); + if (cap_insn == NULL) { + cs_close(handle); + return CS_ERR_MEM; + } + } + return CS_ERR_OK; +} + +static void cap_dump_insn_units(disassemble_info *info, cs_insn *insn, + int i, int n) +{ + fprintf_function print = info->fprintf_func; + FILE *stream = info->stream; + + switch (info->cap_insn_unit) { + case 4: + if (info->endian == BFD_ENDIAN_BIG) { + for (; i < n; i += 4) { + print(stream, " %08x", ldl_be_p(insn->bytes + i)); + + } + } else { + for (; i < n; i += 4) { + print(stream, " %08x", ldl_le_p(insn->bytes + i)); + } + } + break; + + case 2: + if (info->endian == BFD_ENDIAN_BIG) { + for (; i < n; i += 2) { + print(stream, " %04x", lduw_be_p(insn->bytes + i)); + } + } else { + for (; i < n; i += 2) { + print(stream, " %04x", lduw_le_p(insn->bytes + i)); + } + } + break; + + default: + for (; i < n; i++) { + print(stream, " %02x", insn->bytes[i]); + } + break; + } +} + +static void cap_dump_insn(disassemble_info *info, cs_insn *insn) +{ + fprintf_function print = info->fprintf_func; + FILE *stream = info->stream; + int i, n, split; + + print(stream, "0x%08" PRIx64 ": ", insn->address); + + n = insn->size; + split = info->cap_insn_split; + + /* Dump the first SPLIT bytes of the instruction. */ + cap_dump_insn_units(info, insn, 0, MIN(n, split)); + + /* Add padding up to SPLIT so that mnemonics line up. */ + if (n < split) { + int width = (split - n) / info->cap_insn_unit; + width *= (2 * info->cap_insn_unit + 1); + print(stream, "%*s", width, ""); + } + + /* Print the actual instruction. */ + print(stream, " %-8s %s\n", insn->mnemonic, insn->op_str); + + /* Dump any remaining part of the insn on subsequent lines. */ + for (i = split; i < n; i += split) { + print(stream, "0x%08" PRIx64 ": ", insn->address + i); + cap_dump_insn_units(info, insn, i, MIN(n, i + split)); + print(stream, "\n"); + } +} + +/* Disassemble SIZE bytes at PC for the target. */ +bool cap_disas_target(disassemble_info *info, uint64_t pc, size_t size) +{ + uint8_t cap_buf[1024]; + csh handle; + cs_insn *insn; + size_t csize = 0; + + if (cap_disas_start(info, &handle) != CS_ERR_OK) { + return false; + } + insn = cap_insn; + + while (1) { + size_t tsize = MIN(sizeof(cap_buf) - csize, size); + const uint8_t *cbuf = cap_buf; + + info->read_memory_func(pc + csize, cap_buf + csize, tsize, info); + csize += tsize; + size -= tsize; + + while (cs_disasm_iter(handle, &cbuf, &csize, &pc, insn)) { + cap_dump_insn(info, insn); + } + + /* If the target memory is not consumed, go back for more... */ + if (size != 0) { + /* + * ... taking care to move any remaining fractional insn + * to the beginning of the buffer. + */ + if (csize != 0) { + memmove(cap_buf, cbuf, csize); + } + continue; + } + + /* + * Since the target memory is consumed, we should not have + * a remaining fractional insn. + */ + if (csize != 0) { + info->fprintf_func(info->stream, + "Disassembler disagrees with translator " + "over instruction decoding\n" + "Please report this to qemu-devel@nongnu.org\n"); + } + break; + } + + cs_close(&handle); + return true; +} + +/* Disassemble SIZE bytes at CODE for the host. */ +bool cap_disas_host(disassemble_info *info, const void *code, size_t size) +{ + csh handle; + const uint8_t *cbuf; + cs_insn *insn; + uint64_t pc; + + if (cap_disas_start(info, &handle) != CS_ERR_OK) { + return false; + } + insn = cap_insn; + + cbuf = code; + pc = (uintptr_t)code; + + while (cs_disasm_iter(handle, &cbuf, &size, &pc, insn)) { + cap_dump_insn(info, insn); + } + if (size != 0) { + info->fprintf_func(info->stream, + "Disassembler disagrees with TCG over instruction encoding\n" + "Please report this to qemu-devel@nongnu.org\n"); + } + + cs_close(&handle); + return true; +} + +/* Disassemble COUNT insns at PC for the target. */ +bool cap_disas_monitor(disassemble_info *info, uint64_t pc, int count) +{ + uint8_t cap_buf[32]; + csh handle; + cs_insn *insn; + size_t csize = 0; + + if (cap_disas_start(info, &handle) != CS_ERR_OK) { + return false; + } + insn = cap_insn; + + while (1) { + /* + * We want to read memory for one insn, but generically we do not + * know how much memory that is. We have a small buffer which is + * known to be sufficient for all supported targets. Try to not + * read beyond the page, Just In Case. For even more simplicity, + * ignore the actual target page size and use a 1k boundary. If + * that turns out to be insufficient, we'll come back around the + * loop and read more. + */ + uint64_t epc = QEMU_ALIGN_UP(pc + csize + 1, 1024); + size_t tsize = MIN(sizeof(cap_buf) - csize, epc - pc); + const uint8_t *cbuf = cap_buf; + + /* Make certain that we can make progress. */ + assert(tsize != 0); + info->read_memory_func(pc + csize, cap_buf + csize, tsize, info); + csize += tsize; + + if (cs_disasm_iter(handle, &cbuf, &csize, &pc, insn)) { + cap_dump_insn(info, insn); + if (--count <= 0) { + break; + } + } + memmove(cap_buf, cbuf, csize); + } + + cs_close(&handle); + return true; +} + +/* Disassemble a single instruction directly into plugin output */ +bool cap_disas_plugin(disassemble_info *info, uint64_t pc, size_t size) +{ + uint8_t cap_buf[32]; + const uint8_t *cbuf = cap_buf; + csh handle; + + if (cap_disas_start(info, &handle) != CS_ERR_OK) { + return false; + } + + assert(size < sizeof(cap_buf)); + info->read_memory_func(pc, cap_buf, size, info); + + if (cs_disasm_iter(handle, &cbuf, &size, &pc, cap_insn)) { + info->fprintf_func(info->stream, "%s %s", + cap_insn->mnemonic, cap_insn->op_str); + } + + cs_close(&handle); + return true; +} diff --git a/disas/hexagon.c b/disas/hexagon.c new file mode 100644 index 0000000000..3c24e2a94a --- /dev/null +++ b/disas/hexagon.c @@ -0,0 +1,65 @@ +/* + * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* + * QEMU Hexagon Disassembler + */ + +#include "qemu/osdep.h" +#include "disas/dis-asm.h" +#include "target/hexagon/cpu_bits.h" + +/* + * We will disassemble a packet with up to 4 instructions, so we need + * a hefty size buffer. + */ +#define PACKET_BUFFER_LEN 1028 + +int print_insn_hexagon(bfd_vma memaddr, struct disassemble_info *info) +{ + uint32_t words[PACKET_WORDS_MAX]; + bool found_end = false; + GString *buf = g_string_sized_new(PACKET_BUFFER_LEN); + int i, len; + + for (i = 0; i < PACKET_WORDS_MAX && !found_end; i++) { + int status = (*info->read_memory_func)(memaddr + i * sizeof(uint32_t), + (bfd_byte *)&words[i], + sizeof(uint32_t), info); + if (status) { + if (i > 0) { + break; + } + (*info->memory_error_func)(status, memaddr, info); + return status; + } + if (is_packet_end(words[i])) { + found_end = true; + } + } + + if (!found_end) { + (*info->fprintf_func)(info->stream, ""); + return PACKET_WORDS_MAX * sizeof(uint32_t); + } + + len = disassemble_hexagon(words, i, memaddr, buf); + (*info->fprintf_func)(info->stream, "%s", buf->str); + g_string_free(buf, true); + + return len; +} diff --git a/disas/hppa.c b/disas/hppa.c index 2dbd1fc445..dcf9a47f34 100644 --- a/disas/hppa.c +++ b/disas/hppa.c @@ -2021,7 +2021,7 @@ print_insn_hppa (bfd_vma memaddr, disassemble_info *info) fput_fp_reg (GET_FIELD (insn, 6, 10), info); break; - /* 'fA' will not generate a space before the regsiter + /* 'fA' will not generate a space before the register name. Normally that is fine. Except that it causes problems with xmpyu which has no FP format completer. */ diff --git a/disas/libvixl/Makefile.objs b/disas/libvixl/Makefile.objs deleted file mode 100644 index 99a637f6a0..0000000000 --- a/disas/libvixl/Makefile.objs +++ /dev/null @@ -1,5 +0,0 @@ -common-obj-$(CONFIG_ARM_A64_DIS) = vixl/utils.o \ - vixl/compiler-intrinsics.o \ - vixl/a64/instructions-a64.o \ - vixl/a64/decoder-a64.o \ - vixl/a64/disasm-a64.o diff --git a/disas/libvixl/meson.build b/disas/libvixl/meson.build new file mode 100644 index 0000000000..5e2eb33e8e --- /dev/null +++ b/disas/libvixl/meson.build @@ -0,0 +1,7 @@ +libvixl_ss.add(files( + 'vixl/a64/decoder-a64.cc', + 'vixl/a64/disasm-a64.cc', + 'vixl/a64/instructions-a64.cc', + 'vixl/compiler-intrinsics.cc', + 'vixl/utils.cc', +)) diff --git a/disas/libvixl/vixl/a64/disasm-a64.cc b/disas/libvixl/vixl/a64/disasm-a64.cc index 7a58a5c087..f34d1d68da 100644 --- a/disas/libvixl/vixl/a64/disasm-a64.cc +++ b/disas/libvixl/vixl/a64/disasm-a64.cc @@ -2985,6 +2985,10 @@ int Disassembler::SubstituteImmediateField(const Instruction* instr, } return 3; } + default: { + VIXL_UNIMPLEMENTED(); + return 0; + } } } case 'C': { // ICondB - Immediate Conditional Branch. diff --git a/disas/libvixl/vixl/globals.h b/disas/libvixl/vixl/globals.h index 61dc9f7f7e..7099aa599f 100644 --- a/disas/libvixl/vixl/globals.h +++ b/disas/libvixl/vixl/globals.h @@ -108,10 +108,12 @@ inline void USE(T1, T2, T3, T4) {} #define __has_warning(x) 0 #endif -// Note: This option is only available for Clang. And will only be enabled for -// C++11(201103L). +// Fallthrough annotation for Clang and C++11(201103L). #if __has_warning("-Wimplicit-fallthrough") && __cplusplus >= 201103L #define VIXL_FALLTHROUGH() [[clang::fallthrough]] //NOLINT +// Fallthrough annotation for GCC >= 7. +#elif __GNUC__ >= 7 + #define VIXL_FALLTHROUGH() __attribute__((fallthrough)) #else #define VIXL_FALLTHROUGH() do {} while (0) #endif diff --git a/disas/lm32.c b/disas/lm32.c index c0ef8160fe..4fbb124534 100644 --- a/disas/lm32.c +++ b/disas/lm32.c @@ -6,7 +6,7 @@ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/disas/m68k.c b/disas/m68k.c index 863409c67c..aefaecfbd6 100644 --- a/disas/m68k.c +++ b/disas/m68k.c @@ -70,7 +70,7 @@ struct floatformat unsigned int exp_start; unsigned int exp_len; /* Bias added to a "true" exponent to form the biased exponent. It - is intentionally signed as, otherwize, -exp_bias can turn into a + is intentionally signed as, otherwise, -exp_bias can turn into a very large number (e.g., given the exp_bias of 0x3fff and a 64 bit long, the equation (long)(1 - exp_bias) evaluates to 4294950914) instead of -16382). */ @@ -479,7 +479,7 @@ struct m68k_opcode_alias and remaining 3 bits of register shifted 9 bits in first word. Indicate upper/lower in 1 bit shifted 7 bits in second word. Use with `R' or `u' format. - n `m' withouth upper/lower indication. (For M[S]ACx; 4 bits split + n `m' without upper/lower indication. (For M[S]ACx; 4 bits split with MSB shifted 6 bits in first word and remaining 3 bits of register shifted 9 bits in first word. No upper/lower indication is done.) Use with `R' or `u' format. @@ -854,7 +854,7 @@ fetch_arg (unsigned char *buffer, /* Check if an EA is valid for a particular code. This is required for the EMAC instructions since the type of source address determines - if it is a EMAC-load instruciton if the EA is mode 2-5, otherwise it + if it is a EMAC-load instruction if the EA is mode 2-5, otherwise it is a non-load EMAC instruction and the bits mean register Ry. A similar case exists for the movem instructions where the register mask is interpreted differently for different EAs. */ @@ -1080,7 +1080,7 @@ print_indexed (int basereg, /* Returns number of bytes "eaten" by the operand, or return -1 if an invalid operand was found, or -2 if - an opcode tabe error was found. + an opcode table error was found. ADDR is the pc for this arg to be relative to. */ static int diff --git a/disas/meson.build b/disas/meson.build new file mode 100644 index 0000000000..4c8da01877 --- /dev/null +++ b/disas/meson.build @@ -0,0 +1,25 @@ +libvixl_ss = ss.source_set() +subdir('libvixl') + +common_ss.add(when: 'CONFIG_ALPHA_DIS', if_true: files('alpha.c')) +common_ss.add(when: 'CONFIG_ARM_A64_DIS', if_true: files('arm-a64.cc')) +common_ss.add_all(when: 'CONFIG_ARM_A64_DIS', if_true: libvixl_ss) +common_ss.add(when: 'CONFIG_ARM_DIS', if_true: files('arm.c')) +common_ss.add(when: 'CONFIG_CRIS_DIS', if_true: files('cris.c')) +common_ss.add(when: 'CONFIG_HEXAGON_DIS', if_true: files('hexagon.c')) +common_ss.add(when: 'CONFIG_HPPA_DIS', if_true: files('hppa.c')) +common_ss.add(when: 'CONFIG_I386_DIS', if_true: files('i386.c')) +common_ss.add(when: 'CONFIG_LM32_DIS', if_true: files('lm32.c')) +common_ss.add(when: 'CONFIG_M68K_DIS', if_true: files('m68k.c')) +common_ss.add(when: 'CONFIG_MICROBLAZE_DIS', if_true: files('microblaze.c')) +common_ss.add(when: 'CONFIG_MIPS_DIS', if_true: files('mips.c')) +common_ss.add(when: 'CONFIG_MOXIE_DIS', if_true: files('moxie.c')) +common_ss.add(when: 'CONFIG_NANOMIPS_DIS', if_true: files('nanomips.cpp')) +common_ss.add(when: 'CONFIG_NIOS2_DIS', if_true: files('nios2.c')) +common_ss.add(when: 'CONFIG_PPC_DIS', if_true: files('ppc.c')) +common_ss.add(when: 'CONFIG_RISCV_DIS', if_true: files('riscv.c')) +common_ss.add(when: 'CONFIG_S390_DIS', if_true: files('s390.c')) +common_ss.add(when: 'CONFIG_SH4_DIS', if_true: files('sh4.c')) +common_ss.add(when: 'CONFIG_SPARC_DIS', if_true: files('sparc.c')) +common_ss.add(when: 'CONFIG_XTENSA_DIS', if_true: files('xtensa.c')) +common_ss.add(when: capstone, if_true: files('capstone.c')) diff --git a/disas/nanomips.cpp b/disas/nanomips.cpp index 90e63b8367..2b09655271 100644 --- a/disas/nanomips.cpp +++ b/disas/nanomips.cpp @@ -837,7 +837,7 @@ int NMD::Disassemble(const uint16 * data, std::string & dis, * an ASE attribute and the requested version * not having that attribute */ - dis = "ASE attribute missmatch"; + dis = "ASE attribute mismatch"; return -5; } disassembly_function dis_fn = table[i].disassembly; diff --git a/disas/ppc.c b/disas/ppc.c index 63e97cfe1d..02be878198 100644 --- a/disas/ppc.c +++ b/disas/ppc.c @@ -5226,7 +5226,7 @@ operand_value_powerpc (const struct powerpc_operand *operand, if ((operand->flags & PPC_OPERAND_SIGNED) != 0) { /* BITM is always some number of zeros followed by some - number of ones, followed by some numer of zeros. */ + number of ones, followed by some number of zeros. */ unsigned long top = operand->bitm; /* top & -top gives the rightmost 1 bit, so this fills in any trailing zeros. */ diff --git a/disas/tci.c b/disas/tci.c deleted file mode 100644 index f1d6c6b469..0000000000 --- a/disas/tci.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Tiny Code Interpreter for QEMU - disassembler - * - * Copyright (c) 2011 Stefan Weil - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "disas/dis-asm.h" -#include "tcg/tcg.h" - -/* Disassemble TCI bytecode. */ -int print_insn_tci(bfd_vma addr, disassemble_info *info) -{ - int length; - uint8_t byte; - int status; - TCGOpcode op; - - status = info->read_memory_func(addr, &byte, 1, info); - if (status != 0) { - info->memory_error_func(status, addr, info); - return -1; - } - op = byte; - - addr++; - status = info->read_memory_func(addr, &byte, 1, info); - if (status != 0) { - info->memory_error_func(status, addr, info); - return -1; - } - length = byte; - - if (op >= tcg_op_defs_max) { - info->fprintf_func(info->stream, "illegal opcode %d", op); - } else { - const TCGOpDef *def = &tcg_op_defs[op]; - int nb_oargs = def->nb_oargs; - int nb_iargs = def->nb_iargs; - int nb_cargs = def->nb_cargs; - /* TODO: Improve disassembler output. */ - info->fprintf_func(info->stream, "%s\to=%d i=%d c=%d", - def->name, nb_oargs, nb_iargs, nb_cargs); - } - - return length; -} diff --git a/docs/COLO-FT.txt b/docs/COLO-FT.txt index c8e1740935..8d6d53a5a2 100644 --- a/docs/COLO-FT.txt +++ b/docs/COLO-FT.txt @@ -91,7 +91,7 @@ the heartbeat stops responding, the secondary node will trigger a failover as soon as it determines the absence. COLO disk Manager: -When primary VM writes data into image, the colo disk manger captures this data +When primary VM writes data into image, the colo disk manager captures this data and sends it to secondary VM's which makes sure the context of secondary VM's image is consistent with the context of primary VM 's image. For more details, please refer to docs/block-replication.txt. @@ -146,27 +146,27 @@ in test procedure. == Test procedure == Note: Here we are running both instances on the same host for testing, -change the IP Addresses if you want to run it on two hosts. Initally +change the IP Addresses if you want to run it on two hosts. Initially 127.0.0.1 is the Primary Host and 127.0.0.2 is the Secondary Host. == Startup qemu == 1. Primary: -Note: Initally, $imagefolder/primary.qcow2 needs to be copied to all hosts. +Note: Initially, $imagefolder/primary.qcow2 needs to be copied to all hosts. You don't need to change any IP's here, because 0.0.0.0 listens on any interface. The chardev's with 127.0.0.1 IP's loopback to the local qemu instance. # imagefolder="/mnt/vms/colo-test-primary" -# qemu-system-x86_64 -enable-kvm -cpu qemu64,+kvmclock -m 512 -smp 1 -qmp stdio \ +# qemu-system-x86_64 -enable-kvm -cpu qemu64,kvmclock=on -m 512 -smp 1 -qmp stdio \ -device piix3-usb-uhci -device usb-tablet -name primary \ -netdev tap,id=hn0,vhost=off,helper=/usr/lib/qemu/qemu-bridge-helper \ -device rtl8139,id=e0,netdev=hn0 \ - -chardev socket,id=mirror0,host=0.0.0.0,port=9003,server,nowait \ - -chardev socket,id=compare1,host=0.0.0.0,port=9004,server,wait \ - -chardev socket,id=compare0,host=127.0.0.1,port=9001,server,nowait \ + -chardev socket,id=mirror0,host=0.0.0.0,port=9003,server=on,wait=off \ + -chardev socket,id=compare1,host=0.0.0.0,port=9004,server=on,wait=on \ + -chardev socket,id=compare0,host=127.0.0.1,port=9001,server=on,wait=off \ -chardev socket,id=compare0-0,host=127.0.0.1,port=9001 \ - -chardev socket,id=compare_out,host=127.0.0.1,port=9005,server,nowait \ + -chardev socket,id=compare_out,host=127.0.0.1,port=9005,server=on,wait=off \ -chardev socket,id=compare_out0,host=127.0.0.1,port=9005 \ -object filter-mirror,id=m0,netdev=hn0,queue=tx,outdev=mirror0 \ -object filter-redirector,netdev=hn0,id=redire0,queue=rx,indev=compare_out \ @@ -189,7 +189,7 @@ any IP's here, except for the $primary_ip variable. # qemu-img create -f qcow2 $imagefolder/secondary-hidden.qcow2 10G -# qemu-system-x86_64 -enable-kvm -cpu qemu64,+kvmclock -m 512 -smp 1 -qmp stdio \ +# qemu-system-x86_64 -enable-kvm -cpu qemu64,kvmclock=on -m 512 -smp 1 -qmp stdio \ -device piix3-usb-uhci -device usb-tablet -name secondary \ -netdev tap,id=hn0,vhost=off,helper=/usr/lib/qemu/qemu-bridge-helper \ -device rtl8139,id=e0,netdev=hn0 \ diff --git a/docs/_templates/editpage.html b/docs/_templates/editpage.html new file mode 100644 index 0000000000..4319b0f5ac --- /dev/null +++ b/docs/_templates/editpage.html @@ -0,0 +1,5 @@ +
+ +
diff --git a/docs/amd-memory-encryption.txt b/docs/amd-memory-encryption.txt index 43bf3ee6a5..145896aec7 100644 --- a/docs/amd-memory-encryption.txt +++ b/docs/amd-memory-encryption.txt @@ -73,7 +73,7 @@ complete flow chart. To launch a SEV guest # ${QEMU} \ - -machine ...,memory-encryption=sev0 \ + -machine ...,confidential-guest-support=sev0 \ -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1 Debugging @@ -95,10 +95,10 @@ References ----------------- AMD Memory Encryption whitepaper: -http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2013/12/AMD_Memory_Encryption_Whitepaper_v7-Public.pdf +https://developer.amd.com/wordpress/media/2013/12/AMD_Memory_Encryption_Whitepaper_v7-Public.pdf Secure Encrypted Virtualization Key Management: -[1] http://support.amd.com/TechDocs/55766_SEV-KM API_Specification.pdf +[1] http://developer.amd.com/wordpress/media/2017/11/55766_SEV-KM-API_Specification.pdf KVM Forum slides: http://www.linux-kvm.org/images/7/74/02x08A-Thomas_Lendacky-AMDs_Virtualizatoin_Memory_Encryption_Technology.pdf diff --git a/docs/can.txt b/docs/can.txt index 11ed8f2d68..0d310237df 100644 --- a/docs/can.txt +++ b/docs/can.txt @@ -8,13 +8,22 @@ can be connected to host system CAN API (at this time only Linux SocketCAN is supported). The concept of busses is generic and different CAN controllers -can be implemented for it but at this time only SJA1000 chip -controller is implemented. +can be implemented. + +The initial submission implemented SJA1000 controller which +is common and well supported by by drivers for the most operating +systems. The PCI addon card hardware has been selected as the first CAN interface to implement because such device can be easily connected to systems with different CPU architectures (x86, PowerPC, Arm, etc.). +In 2020, CTU CAN FD controller model has been added as part +of the bachelor thesis of Jan Charvat. This controller is complete +open-source/design/hardware solution. The core designer +of the project is Ondrej Ille, the financial support has been +provided by CTU, and more companies including Volkswagen subsidiaries. + The project has been initially started in frame of RTEMS GSoC 2013 slot by Jin Yang under our mentoring The initial idea was to provide generic CAN subsystem for RTEMS. But lack of common environment for code and RTEMS @@ -22,8 +31,8 @@ testing lead to goal change to provide environment which provides complete emulated environment for testing and RTEMS GSoC slot has been donated to work on CAN hardware emulation on QEMU. -Examples how to use CAN emulation -================================= +Examples how to use CAN emulation for SJA1000 based boards +========================================================== When QEMU with CAN PCI support is compiled then one of the next CAN boards can be selected @@ -90,18 +99,100 @@ traffic with "candump" command which is included in "can-utils". candump can0 +CTU CAN FD support examples +=========================== + +This open-source core provides CAN FD support. CAN FD drames are +delivered even to the host systems when SocketCAN interface is found +CAN FD capable. + +The PCIe board emulation is provided for now (the device identifier is +ctucan_pci). The default build defines two CTU CAN FD cores +on the board. + +Example how to connect the canbus0-bus (virtual wire) to the host +Linux system (SocketCAN used) and to both CTU CAN FD cores emulated +on the corresponding PCI card expects that host system CAN bus +is setup according to the previous SJA1000 section. + + qemu-system-x86_64 -enable-kvm -kernel /boot/vmlinuz-4.19.52+ \ + -initrd ramdisk.cpio \ + -virtfs local,path=shareddir,security_model=none,mount_tag=shareddir \ + -vga cirrus \ + -append "console=ttyS0" \ + -object can-bus,id=canbus0-bus \ + -object can-host-socketcan,if=can0,canbus=canbus0-bus,id=canbus0-socketcan \ + -device ctucan_pci,canbus0=canbus0-bus,canbus1=canbus0-bus \ + -nographic + +Setup of CTU CAN FD controller in a guest Linux system + + insmod ctucanfd.ko || modprobe ctucanfd + insmod ctucanfd_pci.ko || modprobe ctucanfd_pci + + for ifc in /sys/class/net/can* ; do + if [ -e $ifc/device/vendor ] ; then + if ! grep -q 0x1760 $ifc/device/vendor ; then + continue; + fi + else + continue; + fi + if [ -e $ifc/device/device ] ; then + if ! grep -q 0xff00 $ifc/device/device ; then + continue; + fi + else + continue; + fi + ifc=$(basename $ifc) + /bin/ip link set $ifc type can bitrate 1000000 dbitrate 10000000 fd on + /bin/ip link set $ifc up + done + +The test can run for example + + candump can1 + +in the guest system and next commands in the host system for basic CAN + + cangen can0 + +for CAN FD without bitrate switch + + cangen can0 -f + +and with bitrate switch + + cangen can0 -b + +The test can be run viceversa, generate messages in the guest system and capture them +in the host one and much more combinations. + Links to other resources ======================== - (1) Repository with development branch can-pci at Czech Technical University - https://gitlab.fel.cvut.cz/canbus/qemu-canbus - (2) GitHub repository with can-pci and our other changes included + (1) CAN related projects at Czech Technical University, Faculty of Electrical Engineering + http://canbus.pages.fel.cvut.cz/ + (2) Repository with development can-pci branch at Czech Technical University https://gitlab.fel.cvut.cz/canbus/qemu-canbus (3) RTEMS page describing project https://devel.rtems.org/wiki/Developer/Simulators/QEMU/CANEmulation (4) RTLWS 2015 article about the project and its use with CANopen emulation - http://rtime.felk.cvut.cz/publications/public/rtlws2015-qemu-can.pdf - Slides - http://rtime.felk.cvut.cz/publications/public/rtlws2015-qemu-can-slides.pdf - (5) Linux SocketCAN utilities + http://cmp.felk.cvut.cz/~pisa/can/doc/rtlws-17-pisa-qemu-can.pdf + (5) GNU/Linux, CAN and CANopen in Real-time Control Applications + Slides from LinuxDays 2017 (include updated RTLWS 2015 content) + https://www.linuxdays.cz/2017/video/Pavel_Pisa-CAN_canopen.pdf + (6) Linux SocketCAN utilities https://github.com/linux-can/can-utils/ + (7) CTU CAN FD project including core VHDL design, Linux driver, + test utilities etc. + https://gitlab.fel.cvut.cz/canbus/ctucanfd_ip_core + (8) CTU CAN FD Core Datasheet Documentation + http://canbus.pages.fel.cvut.cz/ctucanfd_ip_core/Progdokum.pdf + (9) CTU CAN FD Core System Architecture Documentation + http://canbus.pages.fel.cvut.cz/ctucanfd_ip_core/ctu_can_fd_architecture.pdf + (10) CTU CAN FD Driver Documentation + http://canbus.pages.fel.cvut.cz/ctucanfd_ip_core/driver_doc/ctucanfd-driver.html + (11) Integration with PCIe interfacing for Intel/Altera Cyclone IV based board + https://gitlab.fel.cvut.cz/canbus/pcie-ctu_can_fd diff --git a/docs/ccid.txt b/docs/ccid.txt index c7fda6d07d..c97fbd2de0 100644 --- a/docs/ccid.txt +++ b/docs/ccid.txt @@ -109,7 +109,8 @@ NSS. Registration can be done from Firefox or the command line: on the host specify the ccid-card-passthru device with a suitable chardev: - qemu -chardev socket,server,host=0.0.0.0,port=2001,id=ccid,nowait -usb -device usb-ccid -device ccid-card-passthru,chardev=ccid + qemu -chardev socket,server=on,host=0.0.0.0,port=2001,id=ccid,wait=off \ + -usb -device usb-ccid -device ccid-card-passthru,chardev=ccid on the client run vscclient, built when you built QEMU: @@ -125,7 +126,8 @@ Follow instructions as per #4, except run QEMU and vscclient as follows: Run qemu as per #5, and run vscclient from the "fake-smartcard" directory as follows: - qemu -chardev socket,server,host=0.0.0.0,port=2001,id=ccid,nowait -usb -device usb-ccid -device ccid-card-passthru,chardev=ccid + qemu -chardev socket,server=on,host=0.0.0.0,port=2001,id=ccid,wait=off \ + -usb -device usb-ccid -device ccid-card-passthru,chardev=ccid vscclient -e "db=\"sql:$PWD\" use_hw=no soft=(,Test,CAC,,id-cert,signing-cert,encryption-cert)" 2001 diff --git a/docs/colo-proxy.txt b/docs/colo-proxy.txt index fa1cef0278..1fc38aed1b 100644 --- a/docs/colo-proxy.txt +++ b/docs/colo-proxy.txt @@ -164,11 +164,11 @@ clearly describe the usage. Primary(ip:3.3.3.3): -netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown -device e1000,id=e0,netdev=hn0,mac=52:a4:00:12:78:66 --chardev socket,id=mirror0,host=3.3.3.3,port=9003,server,nowait --chardev socket,id=compare1,host=3.3.3.3,port=9004,server,nowait --chardev socket,id=compare0,host=3.3.3.3,port=9001,server,nowait +-chardev socket,id=mirror0,host=3.3.3.3,port=9003,server=on,wait=off +-chardev socket,id=compare1,host=3.3.3.3,port=9004,server=on,wait=off +-chardev socket,id=compare0,host=3.3.3.3,port=9001,server=on,wait=off -chardev socket,id=compare0-0,host=3.3.3.3,port=9001 --chardev socket,id=compare_out,host=3.3.3.3,port=9005,server,nowait +-chardev socket,id=compare_out,host=3.3.3.3,port=9005,server=on,wait=off -chardev socket,id=compare_out0,host=3.3.3.3,port=9005 -object iothread,id=iothread1 -object filter-mirror,id=m0,netdev=hn0,queue=tx,outdev=mirror0 @@ -190,11 +190,11 @@ If you want to use virtio-net-pci or other driver with vnet_header: Primary(ip:3.3.3.3): -netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown -device e1000,id=e0,netdev=hn0,mac=52:a4:00:12:78:66 --chardev socket,id=mirror0,host=3.3.3.3,port=9003,server,nowait --chardev socket,id=compare1,host=3.3.3.3,port=9004,server,nowait --chardev socket,id=compare0,host=3.3.3.3,port=9001,server,nowait +-chardev socket,id=mirror0,host=3.3.3.3,port=9003,server=on,wait=off +-chardev socket,id=compare1,host=3.3.3.3,port=9004,server=on,wait=off +-chardev socket,id=compare0,host=3.3.3.3,port=9001,server=on,wait=off -chardev socket,id=compare0-0,host=3.3.3.3,port=9001 --chardev socket,id=compare_out,host=3.3.3.3,port=9005,server,nowait +-chardev socket,id=compare_out,host=3.3.3.3,port=9005,server=on,wait=off -chardev socket,id=compare_out0,host=3.3.3.3,port=9005 -object filter-mirror,id=m0,netdev=hn0,queue=tx,outdev=mirror0,vnet_hdr_support -object filter-redirector,netdev=hn0,id=redire0,queue=rx,indev=compare_out,vnet_hdr_support diff --git a/docs/conf.py b/docs/conf.py index d6e173ef77..2ee6111872 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -36,9 +36,9 @@ from sphinx.errors import ConfigError # In newer versions of Sphinx this will display nicely; in older versions # Sphinx will also produce a Python backtrace but at least the information # gets printed... -if sys.version_info < (3,5): +if sys.version_info < (3,6): raise ConfigError( - "QEMU requires a Sphinx that uses Python 3.5 or better\n") + "QEMU requires a Sphinx that uses Python 3.6 or better\n") # The per-manual conf.py will set qemu_docdir for a single-manual build; # otherwise set it here if this is an entire-manual-set build. @@ -52,7 +52,10 @@ except NameError: # add these directories to sys.path here. If the directory is relative to the # documentation root, use an absolute path starting from qemu_docdir. # +# Our extensions are in docs/sphinx; the qapidoc extension requires +# the QAPI modules from scripts/. sys.path.insert(0, os.path.join(qemu_docdir, "sphinx")) +sys.path.insert(0, os.path.join(qemu_docdir, "../scripts")) # -- General configuration ------------------------------------------------ @@ -67,7 +70,7 @@ needs_sphinx = '1.6' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ['kerneldoc', 'qmp_lexer', 'hxtool'] +extensions = ['kerneldoc', 'qmp_lexer', 'hxtool', 'depfile', 'qapidoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -174,6 +177,7 @@ html_theme_options = { html_sidebars = { '**': [ 'about.html', + 'editpage.html', 'navigation.html', 'searchbox.html', ] @@ -220,7 +224,51 @@ latex_documents = [ # -- Options for manual page output --------------------------------------- # Individual manual/conf.py can override this to create man pages -man_pages = [] +man_pages = [ + ('interop/qemu-ga', 'qemu-ga', + 'QEMU Guest Agent', + ['Michael Roth '], 8), + ('interop/qemu-ga-ref', 'qemu-ga-ref', + 'QEMU Guest Agent Protocol Reference', + [], 7), + ('interop/qemu-qmp-ref', 'qemu-qmp-ref', + 'QEMU QMP Reference Manual', + [], 7), + ('interop/qemu-storage-daemon-qmp-ref', 'qemu-storage-daemon-qmp-ref', + 'QEMU Storage Daemon QMP Reference Manual', + [], 7), + ('system/qemu-manpage', 'qemu', + 'QEMU User Documentation', + ['Fabrice Bellard'], 1), + ('system/qemu-block-drivers', 'qemu-block-drivers', + 'QEMU block drivers reference', + ['Fabrice Bellard and the QEMU Project developers'], 7), + ('system/qemu-cpu-models', 'qemu-cpu-models', + 'QEMU CPU Models', + ['The QEMU Project developers'], 7), + ('tools/qemu-img', 'qemu-img', + 'QEMU disk image utility', + ['Fabrice Bellard'], 1), + ('tools/qemu-nbd', 'qemu-nbd', + 'QEMU Disk Network Block Device Server', + ['Anthony Liguori '], 8), + ('tools/qemu-pr-helper', 'qemu-pr-helper', + 'QEMU persistent reservation helper', + [], 8), + ('tools/qemu-storage-daemon', 'qemu-storage-daemon', + 'QEMU storage daemon', + [], 1), + ('tools/qemu-trace-stap', 'qemu-trace-stap', + 'QEMU SystemTap trace tool', + [], 1), + ('tools/virtfs-proxy-helper', 'virtfs-proxy-helper', + 'QEMU 9p virtfs proxy filesystem helper', + ['M. Mohan Kumar'], 1), + ('tools/virtiofsd', 'virtiofsd', + 'QEMU virtio-fs shared file system daemon', + ['Stefan Hajnoczi ', + 'Masayoshi Mizuma '], 1), +] # -- Options for Texinfo output ------------------------------------------- @@ -238,6 +286,7 @@ texinfo_documents = [ # We use paths starting from qemu_docdir here so that you can run # sphinx-build from anywhere and the kerneldoc extension can still # find everything. -kerneldoc_bin = os.path.join(qemu_docdir, '../scripts/kernel-doc') +kerneldoc_bin = ['perl', os.path.join(qemu_docdir, '../scripts/kernel-doc')] kerneldoc_srctree = os.path.join(qemu_docdir, '..') hxtool_srctree = os.path.join(qemu_docdir, '..') +qapidoc_srctree = os.path.join(qemu_docdir, '..') diff --git a/docs/confidential-guest-support.txt b/docs/confidential-guest-support.txt new file mode 100644 index 0000000000..71d07ba57a --- /dev/null +++ b/docs/confidential-guest-support.txt @@ -0,0 +1,49 @@ +Confidential Guest Support +========================== + +Traditionally, hypervisors such as QEMU have complete access to a +guest's memory and other state, meaning that a compromised hypervisor +can compromise any of its guests. A number of platforms have added +mechanisms in hardware and/or firmware which give guests at least some +protection from a compromised hypervisor. This is obviously +especially desirable for public cloud environments. + +These mechanisms have different names and different modes of +operation, but are often referred to as Secure Guests or Confidential +Guests. We use the term "Confidential Guest Support" to distinguish +this from other aspects of guest security (such as security against +attacks from other guests, or from network sources). + +Running a Confidential Guest +---------------------------- + +To run a confidential guest you need to add two command line parameters: + +1. Use "-object" to create a "confidential guest support" object. The + type and parameters will vary with the specific mechanism to be + used +2. Set the "confidential-guest-support" machine parameter to the ID of + the object from (1). + +Example (for AMD SEV):: + + qemu-system-x86_64 \ + \ + -machine ...,confidential-guest-support=sev0 \ + -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1 + +Supported mechanisms +-------------------- + +Currently supported confidential guest mechanisms are: + +AMD Secure Encrypted Virtualization (SEV) + docs/amd-memory-encryption.txt + +POWER Protected Execution Facility (PEF) + docs/papr-pef.txt + +s390x Protected Virtualization (PV) + docs/system/s390x/protvirt.rst + +Other mechanisms may be supported in future. diff --git a/docs/defs.rst.inc b/docs/defs.rst.inc index 48d05aaf33..52d6454b93 100644 --- a/docs/defs.rst.inc +++ b/docs/defs.rst.inc @@ -10,6 +10,6 @@ incorrectly in boldface. .. |qemu_system| replace:: qemu-system-x86_64 -.. |qemu_system_x86| replace:: qemu_system-x86_64 +.. |qemu_system_x86| replace:: qemu-system-x86_64 .. |I2C| replace:: I\ :sup:`2`\ C .. |I2S| replace:: I\ :sup:`2`\ S diff --git a/docs/devel/_templates/editpage.html b/docs/devel/_templates/editpage.html new file mode 100644 index 0000000000..a86d22bca8 --- /dev/null +++ b/docs/devel/_templates/editpage.html @@ -0,0 +1,5 @@ +
+ +
diff --git a/docs/devel/atomics.rst b/docs/devel/atomics.rst index 445c3b3503..52baa0736d 100644 --- a/docs/devel/atomics.rst +++ b/docs/devel/atomics.rst @@ -23,9 +23,9 @@ provides macros that fall in three camps: - compiler barriers: ``barrier()``; -- weak atomic access and manual memory barriers: ``atomic_read()``, - ``atomic_set()``, ``smp_rmb()``, ``smp_wmb()``, ``smp_mb()``, ``smp_mb_acquire()``, - ``smp_mb_release()``, ``smp_read_barrier_depends()``; +- weak atomic access and manual memory barriers: ``qatomic_read()``, + ``qatomic_set()``, ``smp_rmb()``, ``smp_wmb()``, ``smp_mb()``, + ``smp_mb_acquire()``, ``smp_mb_release()``, ``smp_read_barrier_depends()``; - sequentially consistent atomic access: everything else. @@ -67,23 +67,23 @@ in the order specified by its program". ``qemu/atomic.h`` provides the following set of atomic read-modify-write operations:: - void atomic_inc(ptr) - void atomic_dec(ptr) - void atomic_add(ptr, val) - void atomic_sub(ptr, val) - void atomic_and(ptr, val) - void atomic_or(ptr, val) + void qatomic_inc(ptr) + void qatomic_dec(ptr) + void qatomic_add(ptr, val) + void qatomic_sub(ptr, val) + void qatomic_and(ptr, val) + void qatomic_or(ptr, val) - typeof(*ptr) atomic_fetch_inc(ptr) - typeof(*ptr) atomic_fetch_dec(ptr) - typeof(*ptr) atomic_fetch_add(ptr, val) - typeof(*ptr) atomic_fetch_sub(ptr, val) - typeof(*ptr) atomic_fetch_and(ptr, val) - typeof(*ptr) atomic_fetch_or(ptr, val) - typeof(*ptr) atomic_fetch_xor(ptr, val) - typeof(*ptr) atomic_fetch_inc_nonzero(ptr) - typeof(*ptr) atomic_xchg(ptr, val) - typeof(*ptr) atomic_cmpxchg(ptr, old, new) + typeof(*ptr) qatomic_fetch_inc(ptr) + typeof(*ptr) qatomic_fetch_dec(ptr) + typeof(*ptr) qatomic_fetch_add(ptr, val) + typeof(*ptr) qatomic_fetch_sub(ptr, val) + typeof(*ptr) qatomic_fetch_and(ptr, val) + typeof(*ptr) qatomic_fetch_or(ptr, val) + typeof(*ptr) qatomic_fetch_xor(ptr, val) + typeof(*ptr) qatomic_fetch_inc_nonzero(ptr) + typeof(*ptr) qatomic_xchg(ptr, val) + typeof(*ptr) qatomic_cmpxchg(ptr, old, new) all of which return the old value of ``*ptr``. These operations are polymorphic; they operate on any type that is as wide as a pointer or @@ -91,19 +91,19 @@ smaller. Similar operations return the new value of ``*ptr``:: - typeof(*ptr) atomic_inc_fetch(ptr) - typeof(*ptr) atomic_dec_fetch(ptr) - typeof(*ptr) atomic_add_fetch(ptr, val) - typeof(*ptr) atomic_sub_fetch(ptr, val) - typeof(*ptr) atomic_and_fetch(ptr, val) - typeof(*ptr) atomic_or_fetch(ptr, val) - typeof(*ptr) atomic_xor_fetch(ptr, val) + typeof(*ptr) qatomic_inc_fetch(ptr) + typeof(*ptr) qatomic_dec_fetch(ptr) + typeof(*ptr) qatomic_add_fetch(ptr, val) + typeof(*ptr) qatomic_sub_fetch(ptr, val) + typeof(*ptr) qatomic_and_fetch(ptr, val) + typeof(*ptr) qatomic_or_fetch(ptr, val) + typeof(*ptr) qatomic_xor_fetch(ptr, val) ``qemu/atomic.h`` also provides loads and stores that cannot be reordered with each other:: - typeof(*ptr) atomic_mb_read(ptr) - void atomic_mb_set(ptr, val) + typeof(*ptr) qatomic_mb_read(ptr) + void qatomic_mb_set(ptr, val) However these do not provide sequential consistency and, in particular, they do not participate in the total ordering enforced by @@ -115,11 +115,11 @@ easiest to hardest): - lightweight synchronization primitives such as ``QemuEvent`` -- RCU operations (``atomic_rcu_read``, ``atomic_rcu_set``) when publishing +- RCU operations (``qatomic_rcu_read``, ``qatomic_rcu_set``) when publishing or accessing a new version of a data structure -- other atomic accesses: ``atomic_read`` and ``atomic_load_acquire`` for - loads, ``atomic_set`` and ``atomic_store_release`` for stores, ``smp_mb`` +- other atomic accesses: ``qatomic_read`` and ``qatomic_load_acquire`` for + loads, ``qatomic_set`` and ``qatomic_store_release`` for stores, ``smp_mb`` to forbid reordering subsequent loads before a store. @@ -149,22 +149,22 @@ The only guarantees that you can rely upon in this case are: When using this model, variables are accessed with: -- ``atomic_read()`` and ``atomic_set()``; these prevent the compiler from +- ``qatomic_read()`` and ``qatomic_set()``; these prevent the compiler from optimizing accesses out of existence and creating unsolicited accesses, but do not otherwise impose any ordering on loads and stores: both the compiler and the processor are free to reorder them. -- ``atomic_load_acquire()``, which guarantees the LOAD to appear to +- ``qatomic_load_acquire()``, which guarantees the LOAD to appear to happen, with respect to the other components of the system, before all the LOAD or STORE operations specified afterwards. - Operations coming before ``atomic_load_acquire()`` can still be + Operations coming before ``qatomic_load_acquire()`` can still be reordered after it. -- ``atomic_store_release()``, which guarantees the STORE to appear to +- ``qatomic_store_release()``, which guarantees the STORE to appear to happen, with respect to the other components of the system, after all the LOAD or STORE operations specified before. - Operations coming after ``atomic_store_release()`` can still be + Operations coming after ``qatomic_store_release()`` can still be reordered before it. Restrictions to the ordering of accesses can also be specified @@ -229,7 +229,7 @@ They come in six kinds: dependency and a full read barrier or better is required. -Memory barriers and ``atomic_load_acquire``/``atomic_store_release`` are +Memory barriers and ``qatomic_load_acquire``/``qatomic_store_release`` are mostly used when a data structure has one thread that is always a writer and one thread that is always a reader: @@ -238,8 +238,8 @@ and one thread that is always a reader: +==================================+==================================+ | :: | :: | | | | - | atomic_store_release(&a, x); | y = atomic_load_acquire(&b); | - | atomic_store_release(&b, y); | x = atomic_load_acquire(&a); | + | qatomic_store_release(&a, x); | y = qatomic_load_acquire(&b); | + | qatomic_store_release(&b, y); | x = qatomic_load_acquire(&a); | +----------------------------------+----------------------------------+ In this case, correctness is easy to check for using the "pairing" @@ -258,14 +258,14 @@ outside a loop. For example: | | | | n = 0; | n = 0; | | for (i = 0; i < 10; i++) | for (i = 0; i < 10; i++) | - | n += atomic_load_acquire(&a[i]); | n += atomic_read(&a[i]); | + | n += qatomic_load_acquire(&a[i]); | n += qatomic_read(&a[i]); | | | smp_mb_acquire(); | +------------------------------------------+----------------------------------+ | :: | :: | | | | | | smp_mb_release(); | | for (i = 0; i < 10; i++) | for (i = 0; i < 10; i++) | - | atomic_store_release(&a[i], false); | atomic_set(&a[i], false); | + | qatomic_store_release(&a[i], false); | qatomic_set(&a[i], false); | +------------------------------------------+----------------------------------+ Splitting a loop can also be useful to reduce the number of barriers: @@ -277,11 +277,11 @@ Splitting a loop can also be useful to reduce the number of barriers: | | | | n = 0; | smp_mb_release(); | | for (i = 0; i < 10; i++) { | for (i = 0; i < 10; i++) | - | atomic_store_release(&a[i], false); | atomic_set(&a[i], false); | + | qatomic_store_release(&a[i], false); | qatomic_set(&a[i], false); | | smp_mb(); | smb_mb(); | - | n += atomic_read(&b[i]); | n = 0; | + | n += qatomic_read(&b[i]); | n = 0; | | } | for (i = 0; i < 10; i++) | - | | n += atomic_read(&b[i]); | + | | n += qatomic_read(&b[i]); | +------------------------------------------+----------------------------------+ In this case, a ``smp_mb_release()`` is also replaced with a (possibly cheaper, and clearer @@ -294,10 +294,10 @@ as well) ``smp_wmb()``: | | | | | smp_mb_release(); | | for (i = 0; i < 10; i++) { | for (i = 0; i < 10; i++) | - | atomic_store_release(&a[i], false); | atomic_set(&a[i], false); | - | atomic_store_release(&b[i], false); | smb_wmb(); | + | qatomic_store_release(&a[i], false); | qatomic_set(&a[i], false); | + | qatomic_store_release(&b[i], false); | smb_wmb(); | | } | for (i = 0; i < 10; i++) | - | | atomic_set(&b[i], false); | + | | qatomic_set(&b[i], false); | +------------------------------------------+----------------------------------+ @@ -306,7 +306,7 @@ as well) ``smp_wmb()``: Acquire/release pairing and the *synchronizes-with* relation ------------------------------------------------------------ -Atomic operations other than ``atomic_set()`` and ``atomic_read()`` have +Atomic operations other than ``qatomic_set()`` and ``qatomic_read()`` have either *acquire* or *release* semantics [#rmw]_. This has two effects: .. [#rmw] Read-modify-write operations can have both---acquire applies to the @@ -357,16 +357,16 @@ thread 2 is relying on the *synchronizes-with* relation between ``pthread_exit`` Synchronization between threads basically descends from this pairing of a release operation and an acquire operation. Therefore, atomic operations -other than ``atomic_set()`` and ``atomic_read()`` will almost always be +other than ``qatomic_set()`` and ``qatomic_read()`` will almost always be paired with another operation of the opposite kind: an acquire operation will pair with a release operation and vice versa. This rule of thumb is extremely useful; in the case of QEMU, however, note that the other operation may actually be in a driver that runs in the guest! ``smp_read_barrier_depends()``, ``smp_rmb()``, ``smp_mb_acquire()``, -``atomic_load_acquire()`` and ``atomic_rcu_read()`` all count +``qatomic_load_acquire()`` and ``qatomic_rcu_read()`` all count as acquire operations. ``smp_wmb()``, ``smp_mb_release()``, -``atomic_store_release()`` and ``atomic_rcu_set()`` all count as release +``qatomic_store_release()`` and ``qatomic_rcu_set()`` all count as release operations. ``smp_mb()`` counts as both acquire and release, therefore it can pair with any other atomic operation. Here is an example: @@ -375,11 +375,11 @@ it can pair with any other atomic operation. Here is an example: +======================+==============================+ | :: | :: | | | | - | atomic_set(&a, 1); | | + | qatomic_set(&a, 1);| | | smp_wmb(); | | - | atomic_set(&b, 2); | x = atomic_read(&b); | + | qatomic_set(&b, 2);| x = qatomic_read(&b); | | | smp_rmb(); | - | | y = atomic_read(&a); | + | | y = qatomic_read(&a); | +----------------------+------------------------------+ Note that a load-store pair only counts if the two operations access the @@ -393,9 +393,9 @@ correct synchronization: +================================+================================+ | :: | :: | | | | - | atomic_set(&a, 1); | | - | atomic_store_release(&b, 2); | x = atomic_load_acquire(&b); | - | | y = atomic_read(&a); | + | qatomic_set(&a, 1); | | + | qatomic_store_release(&b, 2);| x = qatomic_load_acquire(&b);| + | | y = qatomic_read(&a); | +--------------------------------+--------------------------------+ Acquire and release semantics of higher-level primitives can also be @@ -421,7 +421,7 @@ cannot be a data race: | smp_wmb(); | | | x->i = 2; | | | smp_wmb(); | | - | atomic_set(&a, x); | x = atomic_read(&a); | + | qatomic_set(&a, x);| x = qatomic_read(&a); | | | smp_read_barrier_depends(); | | | y = x->i; | | | smp_read_barrier_depends(); | @@ -442,7 +442,7 @@ and memory barriers, and the equivalents in QEMU: at all. Linux 4.1 updated them to implement volatile semantics via ``ACCESS_ONCE`` (or the more recent ``READ``/``WRITE_ONCE``). - QEMU's ``atomic_read`` and ``atomic_set`` implement C11 atomic relaxed + QEMU's ``qatomic_read`` and ``qatomic_set`` implement C11 atomic relaxed semantics if the compiler supports it, and volatile semantics otherwise. Both semantics prevent the compiler from doing certain transformations; the difference is that atomic accesses are guaranteed to be atomic, @@ -451,8 +451,8 @@ and memory barriers, and the equivalents in QEMU: since we assume the variables passed are machine-word sized and properly aligned. - No barriers are implied by ``atomic_read`` and ``atomic_set`` in either Linux - or QEMU. + No barriers are implied by ``qatomic_read`` and ``qatomic_set`` in either + Linux or QEMU. - atomic read-modify-write operations in Linux are of three kinds: @@ -469,7 +469,7 @@ and memory barriers, and the equivalents in QEMU: a different set of memory barriers; in QEMU, all of them enforce sequential consistency. -- in QEMU, ``atomic_read()`` and ``atomic_set()`` do not participate in +- in QEMU, ``qatomic_read()`` and ``qatomic_set()`` do not participate in the total ordering enforced by sequentially-consistent operations. This is because QEMU uses the C11 memory model. The following example is correct in Linux but not in QEMU: @@ -479,8 +479,8 @@ and memory barriers, and the equivalents in QEMU: +==================================+================================+ | :: | :: | | | | - | a = atomic_fetch_add(&x, 2); | a = atomic_fetch_add(&x, 2); | - | b = READ_ONCE(&y); | b = atomic_read(&y); | + | a = atomic_fetch_add(&x, 2); | a = qatomic_fetch_add(&x, 2);| + | b = READ_ONCE(&y); | b = qatomic_read(&y); | +----------------------------------+--------------------------------+ because the read of ``y`` can be moved (by either the processor or the @@ -495,10 +495,10 @@ and memory barriers, and the equivalents in QEMU: +================================+ | :: | | | - | a = atomic_read(&x); | - | atomic_set(&x, a + 2); | + | a = qatomic_read(&x); | + | qatomic_set(&x, a + 2); | | smp_mb(); | - | b = atomic_read(&y); | + | b = qatomic_read(&y); | +--------------------------------+ Sources diff --git a/docs/devel/blkdebug.txt b/docs/devel/blkdebug.txt index 43d8e8f9c6..0b0c128d35 100644 --- a/docs/devel/blkdebug.txt +++ b/docs/devel/blkdebug.txt @@ -62,7 +62,7 @@ Rules support the following attributes: errno - the numeric errno value to return when a request matches this rule. The errno values depend on the host since the numeric values are not - standarized in the POSIX specification. + standardized in the POSIX specification. sector - (optional) a sector number that the request must overlap in order to match this rule diff --git a/docs/devel/blkverify.txt b/docs/devel/blkverify.txt index d556dc4e6d..aca826c51c 100644 --- a/docs/devel/blkverify.txt +++ b/docs/devel/blkverify.txt @@ -62,8 +62,8 @@ A more realistic scenario is verifying the installation of a guest OS: $ ./qemu-img create raw.img 16G $ ./qemu-img create -f qcow2 test.qcow2 16G - $ x86_64-softmmu/qemu-system-x86_64 -cdrom debian.iso \ - -drive file=blkverify:raw.img:test.qcow2 + $ ./qemu-system-x86_64 -cdrom debian.iso \ + -drive file=blkverify:raw.img:test.qcow2 If the installation is aborted when blkverify detects corruption, use qemu-io to explore the contents of the disk image at the sector in question. diff --git a/docs/devel/block-coroutine-wrapper.rst b/docs/devel/block-coroutine-wrapper.rst new file mode 100644 index 0000000000..412851986b --- /dev/null +++ b/docs/devel/block-coroutine-wrapper.rst @@ -0,0 +1,54 @@ +======================= +block-coroutine-wrapper +======================= + +A lot of functions in QEMU block layer (see ``block/*``) can only be +called in coroutine context. Such functions are normally marked by the +coroutine_fn specifier. Still, sometimes we need to call them from +non-coroutine context; for this we need to start a coroutine, run the +needed function from it and wait for the coroutine to finish in a +BDRV_POLL_WHILE() loop. To run a coroutine we need a function with one +void* argument. So for each coroutine_fn function which needs a +non-coroutine interface, we should define a structure to pack the +parameters, define a separate function to unpack the parameters and +call the original function and finally define a new interface function +with same list of arguments as original one, which will pack the +parameters into a struct, create a coroutine, run it and wait in +BDRV_POLL_WHILE() loop. It's boring to create such wrappers by hand, +so we have a script to generate them. + +Usage +===== + +Assume we have defined the ``coroutine_fn`` function +``bdrv_co_foo()`` and need a non-coroutine interface for it, +called ``bdrv_foo()``. In this case the script can help. To +trigger the generation: + +1. You need ``bdrv_foo`` declaration somewhere (for example, in + ``block/coroutines.h``) with the ``generated_co_wrapper`` mark, + like this: + +.. code-block:: c + + int generated_co_wrapper bdrv_foo(); + +2. You need to feed this declaration to block-coroutine-wrapper script. + For this, add the .h (or .c) file with the declaration to the + ``input: files(...)`` list of ``block_gen_c`` target declaration in + ``block/meson.build`` + +You are done. During the build, coroutine wrappers will be generated in +``/block/block-gen.c``. + +Links +===== + +1. The script location is ``scripts/block-coroutine-wrapper.py``. + +2. Generic place for private ``generated_co_wrapper`` declarations is + ``block/coroutines.h``, for public declarations: + ``include/block/block.h`` + +3. The core API of generated coroutine wrappers is placed in + (not generated) ``block/block-gen.h`` diff --git a/docs/devel/build-system.rst b/docs/devel/build-system.rst new file mode 100644 index 0000000000..7ef36f42d0 --- /dev/null +++ b/docs/devel/build-system.rst @@ -0,0 +1,469 @@ +================================== +The QEMU build system architecture +================================== + +This document aims to help developers understand the architecture of the +QEMU build system. As with projects using GNU autotools, the QEMU build +system has two stages, first the developer runs the "configure" script +to determine the local build environment characteristics, then they run +"make" to build the project. There is about where the similarities with +GNU autotools end, so try to forget what you know about them. + + +Stage 1: configure +================== + +The QEMU configure script is written directly in shell, and should be +compatible with any POSIX shell, hence it uses #!/bin/sh. An important +implication of this is that it is important to avoid using bash-isms on +development platforms where bash is the primary host. + +In contrast to autoconf scripts, QEMU's configure is expected to be +silent while it is checking for features. It will only display output +when an error occurs, or to show the final feature enablement summary +on completion. + +Because QEMU uses the Meson build system under the hood, only VPATH +builds are supported. There are two general ways to invoke configure & +perform a build: + + - VPATH, build artifacts outside of QEMU source tree entirely:: + + cd ../ + mkdir build + cd build + ../qemu/configure + make + + - VPATH, build artifacts in a subdir of QEMU source tree:: + + mkdir build + cd build + ../configure + make + +For now, checks on the compilation environment are found in configure +rather than meson.build, though this is expected to change. The command +line is parsed in the configure script and, whenever needed, converted +into the appropriate options to Meson. + +New checks should be added to Meson, which usually comprises the +following tasks: + + - Add a Meson build option to meson_options.txt. + + - Add support to the command line arg parser to handle any new + `--enable-XXX`/`--disable-XXX` flags required by the feature. + + - Add information to the help output message to report on the new + feature flag. + + - Add code to perform the actual feature check. + + - Add code to include the feature status in `config-host.h` + + - Add code to print out the feature status in the configure summary + upon completion. + + +Taking the probe for SDL2_Image as an example, we have the following pieces +in configure:: + + # Initial variable state + sdl_image=auto + + ..snip.. + + # Configure flag processing + --disable-sdl-image) sdl_image=disabled + ;; + --enable-sdl-image) sdl_image=enabled + ;; + + ..snip.. + + # Help output feature message + sdl-image SDL Image support for icons + + ..snip.. + + # Meson invocation + -Dsdl_image=$sdl_image + +In meson_options.txt:: + + option('sdl', type : 'feature', value : 'auto', + description: 'SDL Image support for icons') + +In meson.build:: + + # Detect dependency + sdl_image = dependency('SDL2_image', required: get_option('sdl_image'), + method: 'pkg-config', + kwargs: static_kwargs) + + # Create config-host.h (if applicable) + config_host_data.set('CONFIG_SDL_IMAGE', sdl_image.found()) + + # Summary + summary_info += {'SDL image support': sdl_image.found()} + + + +Helper functions +---------------- + +The configure script provides a variety of helper functions to assist +developers in checking for system features: + +`do_cc $ARGS...` + Attempt to run the system C compiler passing it $ARGS... + +`do_cxx $ARGS...` + Attempt to run the system C++ compiler passing it $ARGS... + +`compile_object $CFLAGS` + Attempt to compile a test program with the system C compiler using + $CFLAGS. The test program must have been previously written to a file + called $TMPC. The replacement in Meson is the compiler object `cc`, + which has methods such as `cc.compiles()`, + `cc.check_header()`, `cc.has_function()`. + +`compile_prog $CFLAGS $LDFLAGS` + Attempt to compile a test program with the system C compiler using + $CFLAGS and link it with the system linker using $LDFLAGS. The test + program must have been previously written to a file called $TMPC. + The replacement in Meson is `cc.find_library()` and `cc.links()`. + +`has $COMMAND` + Determine if $COMMAND exists in the current environment, either as a + shell builtin, or executable binary, returning 0 on success. The + replacement in Meson is `find_program()`. + +`check_define $NAME` + Determine if the macro $NAME is defined by the system C compiler + +`check_include $NAME` + Determine if the include $NAME file is available to the system C + compiler. The replacement in Meson is `cc.has_header()`. + +`write_c_skeleton` + Write a minimal C program main() function to the temporary file + indicated by $TMPC + +`feature_not_found $NAME $REMEDY` + Print a message to stderr that the feature $NAME was not available + on the system, suggesting the user try $REMEDY to address the + problem. + +`error_exit $MESSAGE $MORE...` + Print $MESSAGE to stderr, followed by $MORE... and then exit from the + configure script with non-zero status + +`query_pkg_config $ARGS...` + Run pkg-config passing it $ARGS. If QEMU is doing a static build, + then --static will be automatically added to $ARGS + + +Stage 2: Meson +============== + +The Meson build system is currently used to describe the build +process for: + +1) executables, which include: + + - Tools - qemu-img, qemu-nbd, qga (guest agent), etc + + - System emulators - qemu-system-$ARCH + + - Userspace emulators - qemu-$ARCH + + - Unit tests + +2) documentation + +3) ROMs, which can be either installed as binary blobs or compiled + +4) other data files, such as icons or desktop files + +All executables are built by default, except for some `contrib/` +binaries that are known to fail to build on some platforms (for example +32-bit or big-endian platforms). Tests are also built by default, +though that might change in the future. + +The source code is highly modularized, split across many files to +facilitate building of all of these components with as little duplicated +compilation as possible. Using the Meson "sourceset" functionality, +`meson.build` files group the source files in rules that are +enabled according to the available system libraries and to various +configuration symbols. Sourcesets belong to one of four groups: + +Subsystem sourcesets: + Various subsystems that are common to both tools and emulators have + their own sourceset, for example `block_ss` for the block device subsystem, + `chardev_ss` for the character device subsystem, etc. These sourcesets + are then turned into static libraries as follows:: + + libchardev = static_library('chardev', chardev_ss.sources(), + name_suffix: 'fa', + build_by_default: false) + + chardev = declare_dependency(link_whole: libchardev) + + As of Meson 0.55.1, the special `.fa` suffix should be used for everything + that is used with `link_whole`, to ensure that the link flags are placed + correctly in the command line. + +Target-independent emulator sourcesets: + Various general purpose helper code is compiled only once and + the .o files are linked into all output binaries that need it. + This includes error handling infrastructure, standard data structures, + platform portability wrapper functions, etc. + + Target-independent code lives in the `common_ss`, `softmmu_ss` and + `user_ss` sourcesets. `common_ss` is linked into all emulators, + `softmmu_ss` only in system emulators, `user_ss` only in user-mode + emulators. + + Target-independent sourcesets must exercise particular care when using + `if_false` rules. The `if_false` rule will be used correctly when linking + emulator binaries; however, when *compiling* target-independent files + into .o files, Meson may need to pick *both* the `if_true` and + `if_false` sides to cater for targets that want either side. To + achieve that, you can add a special rule using the ``CONFIG_ALL`` + symbol:: + + # Some targets have CONFIG_ACPI, some don't, so this is not enough + softmmu_ss.add(when: 'CONFIG_ACPI`, if_true: files('acpi.c'), + if_false: files('acpi-stub.c')) + + # This is required as well: + softmmu_ss.add(when: 'CONFIG_ALL`, if_true: files('acpi-stub.c')) + +Target-dependent emulator sourcesets: + In the target-dependent set lives CPU emulation, some device emulation and + much glue code. This sometimes also has to be compiled multiple times, + once for each target being built. Target-dependent files are included + in the `specific_ss` sourceset. + + Each emulator also includes sources for files in the `hw/` and `target/` + subdirectories. The subdirectory used for each emulator comes + from the target's definition of ``TARGET_BASE_ARCH`` or (if missing) + ``TARGET_ARCH``, as found in `default-configs/targets/*.mak`. + + Each subdirectory in `hw/` adds one sourceset to the `hw_arch` dictionary, + for example:: + + arm_ss = ss.source_set() + arm_ss.add(files('boot.c'), fdt) + ... + hw_arch += {'arm': arm_ss} + + The sourceset is only used for system emulators. + + Each subdirectory in `target/` instead should add one sourceset to each + of the `target_arch` and `target_softmmu_arch`, which are used respectively + for all emulators and for system emulators only. For example:: + + arm_ss = ss.source_set() + arm_softmmu_ss = ss.source_set() + ... + target_arch += {'arm': arm_ss} + target_softmmu_arch += {'arm': arm_softmmu_ss} + +Utility sourcesets: + All binaries link with a static library `libqemuutil.a`. This library + is built from several sourcesets; most of them however host generated + code, and the only two of general interest are `util_ss` and `stub_ss`. + + The separation between these two is purely for documentation purposes. + `util_ss` contains generic utility files. Even though this code is only + linked in some binaries, sometimes it requires hooks only in some of + these and depend on other functions that are not fully implemented by + all QEMU binaries. `stub_ss` links dummy stubs that will only be linked + into the binary if the real implementation is not present. In a way, + the stubs can be thought of as a portable implementation of the weak + symbols concept. + + +The following files concur in the definition of which files are linked +into each emulator: + +`default-configs/devices/*.mak` + The files under `default-configs/devices/` control the boards and devices + that are built into each QEMU system emulation targets. They merely contain + a list of config variable definitions such as:: + + include arm-softmmu.mak + CONFIG_XLNX_ZYNQMP_ARM=y + CONFIG_XLNX_VERSAL=y + +`*/Kconfig` + These files are processed together with `default-configs/devices/*.mak` and + describe the dependencies between various features, subsystems and + device models. They are described in :ref:`kconfig` + +`default-configs/targets/*.mak` + These files mostly define symbols that appear in the `*-config-target.h` + file for each emulator [#cfgtarget]_. However, the ``TARGET_ARCH`` + and ``TARGET_BASE_ARCH`` will also be used to select the `hw/` and + `target/` subdirectories that are compiled into each target. + +.. [#cfgtarget] This header is included by `qemu/osdep.h` when + compiling files from the target-specific sourcesets. + +These files rarely need changing unless you are adding a completely +new target, or enabling new devices or hardware for a particular +system/userspace emulation target + + +Support scripts +--------------- + +Meson has a special convention for invoking Python scripts: if their +first line is `#! /usr/bin/env python3` and the file is *not* executable, +find_program() arranges to invoke the script under the same Python +interpreter that was used to invoke Meson. This is the most common +and preferred way to invoke support scripts from Meson build files, +because it automatically uses the value of configure's --python= option. + +In case the script is not written in Python, use a `#! /usr/bin/env ...` +line and make the script executable. + +Scripts written in Python, where it is desirable to make the script +executable (for example for test scripts that developers may want to +invoke from the command line, such as tests/qapi-schema/test-qapi.py), +should be invoked through the `python` variable in meson.build. For +example:: + + test('QAPI schema regression tests', python, + args: files('test-qapi.py'), + env: test_env, suite: ['qapi-schema', 'qapi-frontend']) + +This is needed to obey the --python= option passed to the configure +script, which may point to something other than the first python3 +binary on the path. + + +Stage 3: makefiles +================== + +The use of GNU make is required with the QEMU build system. + +The output of Meson is a build.ninja file, which is used with the Ninja +build system. QEMU uses a different approach, where Makefile rules are +synthesized from the build.ninja file. The main Makefile includes these +rules and wraps them so that e.g. submodules are built before QEMU. +The resulting build system is largely non-recursive in nature, in +contrast to common practices seen with automake. + +Tests are also ran by the Makefile with the traditional `make check` +phony target, while benchmarks are run with `make bench`. Meson test +suites such as `unit` can be ran with `make check-unit` too. It is also +possible to run tests defined in meson.build with `meson test`. + +Important files for the build system +==================================== + +Statically defined files +------------------------ + +The following key files are statically defined in the source tree, with +the rules needed to build QEMU. Their behaviour is influenced by a +number of dynamically created files listed later. + +`Makefile` + The main entry point used when invoking make to build all the components + of QEMU. The default 'all' target will naturally result in the build of + every component. Makefile takes care of recursively building submodules + directly via a non-recursive set of rules. + +`*/meson.build` + The meson.build file in the root directory is the main entry point for the + Meson build system, and it coordinates the configuration and build of all + executables. Build rules for various subdirectories are included in + other meson.build files spread throughout the QEMU source tree. + +`tests/Makefile.include` + Rules for external test harnesses. These include the TCG tests, + `qemu-iotests` and the Avocado-based acceptance tests. + +`tests/docker/Makefile.include` + Rules for Docker tests. Like tests/Makefile, this file is included + directly by the top level Makefile, anything defined in this file will + influence the entire build system. + +`tests/vm/Makefile.include` + Rules for VM-based tests. Like tests/Makefile, this file is included + directly by the top level Makefile, anything defined in this file will + influence the entire build system. + +Dynamically created files +------------------------- + +The following files are generated dynamically by configure in order to +control the behaviour of the statically defined makefiles. This avoids +the need for QEMU makefiles to go through any pre-processing as seen +with autotools, where Makefile.am generates Makefile.in which generates +Makefile. + +Built by configure: + +`config-host.mak` + When configure has determined the characteristics of the build host it + will write a long list of variables to config-host.mak file. This + provides the various install directories, compiler / linker flags and a + variety of `CONFIG_*` variables related to optionally enabled features. + This is imported by the top level Makefile and meson.build in order to + tailor the build output. + + config-host.mak is also used as a dependency checking mechanism. If make + sees that the modification timestamp on configure is newer than that on + config-host.mak, then configure will be re-run. + + The variables defined here are those which are applicable to all QEMU + build outputs. Variables which are potentially different for each + emulator target are defined by the next file... + + +Built by Meson: + +`${TARGET-NAME}-config-devices.mak` + TARGET-NAME is again the name of a system or userspace emulator. The + config-devices.mak file is automatically generated by make using the + scripts/make_device_config.sh program, feeding it the + default-configs/$TARGET-NAME file as input. + +`config-host.h`, `$TARGET-NAME/config-target.h`, `$TARGET-NAME/config-devices.h` + These files are used by source code to determine what features + are enabled. They are generated from the contents of the corresponding + `*.h` files using the scripts/create_config program. This extracts + relevant variables and formats them as C preprocessor macros. + +`build.ninja` + The build rules. + + +Built by Makefile: + +`Makefile.ninja` + A Makefile include that bridges to ninja for the actual build. The + Makefile is mostly a list of targets that Meson included in build.ninja. + +`Makefile.mtest` + The Makefile definitions that let "make check" run tests defined in + meson.build. The rules are produced from Meson's JSON description of + tests (obtained with "meson introspect --tests") through the script + scripts/mtest2make.py. + + +Useful make targets +------------------- + +`help` + Print a help message for the most common build targets. + +`print-VAR` + Print the value of the variable VAR. Useful for debugging the build + system. diff --git a/docs/devel/build-system.txt b/docs/devel/build-system.txt deleted file mode 100644 index 41bd08ea3a..0000000000 --- a/docs/devel/build-system.txt +++ /dev/null @@ -1,519 +0,0 @@ - The QEMU build system architecture - ================================== - -This document aims to help developers understand the architecture of the -QEMU build system. As with projects using GNU autotools, the QEMU build -system has two stages, first the developer runs the "configure" script -to determine the local build environment characteristics, then they run -"make" to build the project. There is about where the similarities with -GNU autotools end, so try to forget what you know about them. - - -Stage 1: configure -================== - -The QEMU configure script is written directly in shell, and should be -compatible with any POSIX shell, hence it uses #!/bin/sh. An important -implication of this is that it is important to avoid using bash-isms on -development platforms where bash is the primary host. - -In contrast to autoconf scripts, QEMU's configure is expected to be -silent while it is checking for features. It will only display output -when an error occurs, or to show the final feature enablement summary -on completion. - -Adding new checks to the configure script usually comprises the -following tasks: - - - Initialize one or more variables with the default feature state. - - Ideally features should auto-detect whether they are present, - so try to avoid hardcoding the initial state to either enabled - or disabled, as that forces the user to pass a --enable-XXX - / --disable-XXX flag on every invocation of configure. - - - Add support to the command line arg parser to handle any new - --enable-XXX / --disable-XXX flags required by the feature XXX. - - - Add information to the help output message to report on the new - feature flag. - - - Add code to perform the actual feature check. As noted above, try to - be fully dynamic in checking enablement/disablement. - - - Add code to print out the feature status in the configure summary - upon completion. - - - Add any new makefile variables to $config_host_mak on completion. - - -Taking (a simplified version of) the probe for gnutls from configure, -we have the following pieces: - - # Initial variable state - gnutls="" - - ..snip.. - - # Configure flag processing - --disable-gnutls) gnutls="no" - ;; - --enable-gnutls) gnutls="yes" - ;; - - ..snip.. - - # Help output feature message - gnutls GNUTLS cryptography support - - ..snip.. - - # Test for gnutls - if test "$gnutls" != "no"; then - if ! $pkg_config --exists "gnutls"; then - gnutls_cflags=`$pkg_config --cflags gnutls` - gnutls_libs=`$pkg_config --libs gnutls` - libs_softmmu="$gnutls_libs $libs_softmmu" - libs_tools="$gnutls_libs $libs_tools" - QEMU_CFLAGS="$QEMU_CFLAGS $gnutls_cflags" - gnutls="yes" - elif test "$gnutls" = "yes"; then - feature_not_found "gnutls" "Install gnutls devel" - else - gnutls="no" - fi - fi - - ..snip.. - - # Completion feature summary - echo "GNUTLS support $gnutls" - - ..snip.. - - # Define make variables - if test "$gnutls" = "yes" ; then - echo "CONFIG_GNUTLS=y" >> $config_host_mak - fi - - -Helper functions ----------------- - -The configure script provides a variety of helper functions to assist -developers in checking for system features: - - - do_cc $ARGS... - - Attempt to run the system C compiler passing it $ARGS... - - - do_cxx $ARGS... - - Attempt to run the system C++ compiler passing it $ARGS... - - - compile_object $CFLAGS - - Attempt to compile a test program with the system C compiler using - $CFLAGS. The test program must have been previously written to a file - called $TMPC. - - - compile_prog $CFLAGS $LDFLAGS - - Attempt to compile a test program with the system C compiler using - $CFLAGS and link it with the system linker using $LDFLAGS. The test - program must have been previously written to a file called $TMPC. - - - has $COMMAND - - Determine if $COMMAND exists in the current environment, either as a - shell builtin, or executable binary, returning 0 on success. - - - path_of $COMMAND - - Return the fully qualified path of $COMMAND, printing it to stdout, - and returning 0 on success. - - - check_define $NAME - - Determine if the macro $NAME is defined by the system C compiler - - - check_include $NAME - - Determine if the include $NAME file is available to the system C - compiler - - - write_c_skeleton - - Write a minimal C program main() function to the temporary file - indicated by $TMPC - - - feature_not_found $NAME $REMEDY - - Print a message to stderr that the feature $NAME was not available - on the system, suggesting the user try $REMEDY to address the - problem. - - - error_exit $MESSAGE $MORE... - - Print $MESSAGE to stderr, followed by $MORE... and then exit from the - configure script with non-zero status - - - query_pkg_config $ARGS... - - Run pkg-config passing it $ARGS. If QEMU is doing a static build, - then --static will be automatically added to $ARGS - - -Stage 2: makefiles -================== - -The use of GNU make is required with the QEMU build system. - -Although the source code is spread across multiple subdirectories, the -build system should be considered largely non-recursive in nature, in -contrast to common practices seen with automake. There is some recursive -invocation of make, but this is related to the things being built, -rather than the source directory structure. - -QEMU currently supports both VPATH and non-VPATH builds, so there are -three general ways to invoke configure & perform a build. - - - VPATH, build artifacts outside of QEMU source tree entirely - - cd ../ - mkdir build - cd build - ../qemu/configure - make - - - VPATH, build artifacts in a subdir of QEMU source tree - - mkdir build - cd build - ../configure - make - - - non-VPATH, build artifacts everywhere - - ./configure - make - -The QEMU maintainers generally recommend that a VPATH build is used by -developers. Patches to QEMU are expected to ensure VPATH build still -works. - - -Module structure ----------------- - -There are a number of key outputs of the QEMU build system: - - - Tools - qemu-img, qemu-nbd, qga (guest agent), etc - - System emulators - qemu-system-$ARCH - - Userspace emulators - qemu-$ARCH - - Unit tests - -The source code is highly modularized, split across many files to -facilitate building of all of these components with as little duplicated -compilation as possible. There can be considered to be two distinct -groups of files, those which are independent of the QEMU emulation -target and those which are dependent on the QEMU emulation target. - -In the target-independent set lives various general purpose helper code, -such as error handling infrastructure, standard data structures, -platform portability wrapper functions, etc. This code can be compiled -once only and the .o files linked into all output binaries. - -In the target-dependent set lives CPU emulation, device emulation and -much glue code. This sometimes also has to be compiled multiple times, -once for each target being built. - -The utility code that is used by all binaries is built into a -static archive called libqemuutil.a, which is then linked to all the -binaries. In order to provide hooks that are only needed by some of the -binaries, code in libqemuutil.a may depend on other functions that are -not fully implemented by all QEMU binaries. Dummy stubs for all these -functions are also provided by this library, and will only be linked -into the binary if the real implementation is not present. In a way, -the stubs can be thought of as a portable implementation of the weak -symbols concept. - -All binaries should link to libqemuutil.a, e.g.: - - qemu-img$(EXESUF): qemu-img.o ..snip.. libqemuutil.a - - -Windows platform portability ----------------------------- - -On Windows, all binaries have the suffix '.exe', so all Makefile rules -which create binaries must include the $(EXESUF) variable on the binary -name. e.g. - - qemu-img$(EXESUF): qemu-img.o ..snip.. - -This expands to '.exe' on Windows, or '' on other platforms. - -A further complication for the system emulator binaries is that -two separate binaries need to be generated. - -The main binary (e.g. qemu-system-x86_64.exe) is linked against the -Windows console runtime subsystem. These are expected to be run from a -command prompt window, and so will print stderr to the console that -launched them. - -The second binary generated has a 'w' on the end of its name (e.g. -qemu-system-x86_64w.exe) and is linked against the Windows graphical -runtime subsystem. These are expected to be run directly from the -desktop and will open up a dedicated console window for stderr output. - -The Makefile.target will generate the binary for the graphical subsystem -first, and then use objcopy to relink it against the console subsystem -to generate the second binary. - - -Object variable naming ----------------------- - -The QEMU convention is to define variables to list different groups of -object files. These are named with the convention $PREFIX-obj-y. For -example the libqemuutil.a file will be linked with all objects listed -in a variable 'util-obj-y'. So, for example, util/Makefile.obj will -contain a set of definitions looking like - - util-obj-y += bitmap.o bitops.o hbitmap.o - util-obj-y += fifo8.o - util-obj-y += acl.o - util-obj-y += error.o qemu-error.o - -When there is an object file which needs to be conditionally built based -on some characteristic of the host system, the configure script will -define a variable for the conditional. For example, on Windows it will -define $(CONFIG_POSIX) with a value of 'n' and $(CONFIG_WIN32) with a -value of 'y'. It is now possible to use the config variables when -listing object files. For example, - - util-obj-$(CONFIG_WIN32) += oslib-win32.o qemu-thread-win32.o - util-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o - -On Windows this expands to - - util-obj-y += oslib-win32.o qemu-thread-win32.o - util-obj-n += oslib-posix.o qemu-thread-posix.o - -Since libqemutil.a links in $(util-obj-y), the POSIX specific files -listed against $(util-obj-n) are ignored on the Windows platform builds. - - -CFLAGS / LDFLAGS / LIBS handling --------------------------------- - -There are many different binaries being built with differing purposes, -and some of them might even be 3rd party libraries pulled in via git -submodules. As such the use of the global CFLAGS variable is generally -avoided in QEMU, since it would apply to too many build targets. - -Flags that are needed by any QEMU code (i.e. everything *except* GIT -submodule projects) are put in $(QEMU_CFLAGS) variable. For linker -flags the $(LIBS) variable is sometimes used, but a couple of more -targeted variables are preferred. $(libs_softmmu) is used for -libraries that must be linked to system emulator targets, $(LIBS_TOOLS) -is used for tools like qemu-img, qemu-nbd, etc and $(LIBS_QGA) is used -for the QEMU guest agent. There is currently no specific variable for -the userspace emulator targets as the global $(LIBS), or more targeted -variables shown below, are sufficient. - -In addition to these variables, it is possible to provide cflags and -libs against individual source code files, by defining variables of the -form $FILENAME-cflags and $FILENAME-libs. For example, the curl block -driver needs to link to the libcurl library, so block/Makefile defines -some variables: - - curl.o-cflags := $(CURL_CFLAGS) - curl.o-libs := $(CURL_LIBS) - -The scope is a little different between the two variables. The libs get -used when linking any target binary that includes the curl.o object -file, while the cflags get used when compiling the curl.c file only. - - -Statically defined files ------------------------- - -The following key files are statically defined in the source tree, with -the rules needed to build QEMU. Their behaviour is influenced by a -number of dynamically created files listed later. - -- Makefile - -The main entry point used when invoking make to build all the components -of QEMU. The default 'all' target will naturally result in the build of -every component. The various tools and helper binaries are built -directly via a non-recursive set of rules. - -Each system/userspace emulation target needs to have a slightly -different set of make rules / variables. Thus, make will be recursively -invoked for each of the emulation targets. - -The recursive invocation will end up processing the toplevel -Makefile.target file (more on that later). - - -- */Makefile.objs - -Since the source code is spread across multiple directories, the rules -for each file are similarly modularized. Thus each subdirectory -containing .c files will usually also contain a Makefile.objs file. -These files are not directly invoked by a recursive make, but instead -they are imported by the top level Makefile and/or Makefile.target - -Each Makefile.objs usually just declares a set of variables listing the -.o files that need building from the source files in the directory. They -will also define any custom linker or compiler flags. For example in -block/Makefile.objs - - block-obj-$(CONFIG_LIBISCSI) += iscsi.o - block-obj-$(CONFIG_CURL) += curl.o - - ..snip... - - iscsi.o-cflags := $(LIBISCSI_CFLAGS) - iscsi.o-libs := $(LIBISCSI_LIBS) - curl.o-cflags := $(CURL_CFLAGS) - curl.o-libs := $(CURL_LIBS) - -If there are any rules defined in the Makefile.objs file, they should -all use $(obj) as a prefix to the target, e.g. - - $(obj)/generated-tcg-tracers.h: $(obj)/generated-tcg-tracers.h-timestamp - - -- Makefile.target - -This file provides the entry point used to build each individual system -or userspace emulator target. Each enabled target has its own -subdirectory. For example if configure is run with the argument -'--target-list=x86_64-softmmu', then a sub-directory 'x86_64-softmmu' -will be created, containing a 'Makefile' which symlinks back to -Makefile.target - -So when the recursive '$(MAKE) -C x86_64-softmmu' is invoked, it ends up -using Makefile.target for the build rules. - - -- rules.mak - -This file provides the generic helper rules for invoking build tools, in -particular the compiler and linker. This also contains the magic (hairy) -'unnest-vars' function which is used to merge the variable definitions -from all Makefile.objs in the source tree down into the main Makefile -context. - - -- default-configs/*.mak - -The files under default-configs/ control what emulated hardware is built -into each QEMU system and userspace emulator targets. They merely contain -a list of config variable definitions like the machines that should be -included. For example, default-configs/aarch64-softmmu.mak has: - - include arm-softmmu.mak - CONFIG_XLNX_ZYNQMP_ARM=y - CONFIG_XLNX_VERSAL=y - -These files rarely need changing unless new devices / hardware need to -be enabled for a particular system/userspace emulation target - - -- tests/Makefile - -Rules for building the unit tests. This file is included directly by the -top level Makefile, so anything defined in this file will influence the -entire build system. Care needs to be taken when writing rules for tests -to ensure they only apply to the unit test execution / build. - -- tests/docker/Makefile.include - -Rules for Docker tests. Like tests/Makefile, this file is included -directly by the top level Makefile, anything defined in this file will -influence the entire build system. - -- po/Makefile - -Rules for building and installing the binary message catalogs from the -text .po file sources. This almost never needs changing for any reason. - - -Dynamically created files -------------------------- - -The following files are generated dynamically by configure in order to -control the behaviour of the statically defined makefiles. This avoids -the need for QEMU makefiles to go through any pre-processing as seen -with autotools, where Makefile.am generates Makefile.in which generates -Makefile. - - -- config-host.mak - -When configure has determined the characteristics of the build host it -will write a long list of variables to config-host.mak file. This -provides the various install directories, compiler / linker flags and a -variety of CONFIG_* variables related to optionally enabled features. -This is imported by the top level Makefile in order to tailor the build -output. - -The variables defined here are those which are applicable to all QEMU -build outputs. Variables which are potentially different for each -emulator target are defined by the next file... - -It is also used as a dependency checking mechanism. If make sees that -the modification timestamp on configure is newer than that on -config-host.mak, then configure will be re-run. - - -- config-host.h - -The config-host.h file is used by source code to determine what features -are enabled. It is generated from the contents of config-host.mak using -the scripts/create_config program. This extracts all the CONFIG_* variables, -most of the HOST_* variables and a few other misc variables from -config-host.mak, formatting them as C preprocessor macros. - - -- $TARGET-NAME/config-target.mak - -TARGET-NAME is the name of a system or userspace emulator, for example, -x86_64-softmmu denotes the system emulator for the x86_64 architecture. -This file contains the variables which need to vary on a per-target -basis. For example, it will indicate whether KVM or Xen are enabled for -the target and any other potential custom libraries needed for linking -the target. - - -- $TARGET-NAME/config-devices.mak - -TARGET-NAME is again the name of a system or userspace emulator. The -config-devices.mak file is automatically generated by make using the -scripts/make_device_config.sh program, feeding it the -default-configs/$TARGET-NAME file as input. - - -- $TARGET-NAME/Makefile - -This is the entrypoint used when make recurses to build a single system -or userspace emulator target. It is merely a symlink back to the -Makefile.target in the top level. - - -Useful make targets -=================== - -- help - - Print a help message for the most common build targets. - -- print-VAR - - Print the value of the variable VAR. Useful for debugging the build - system. diff --git a/docs/devel/clocks.rst b/docs/devel/clocks.rst index e5da28e211..956bd147ea 100644 --- a/docs/devel/clocks.rst +++ b/docs/devel/clocks.rst @@ -80,11 +80,12 @@ Adding clocks to a device must be done during the init method of the Device instance. To add an input clock to a device, the function ``qdev_init_clock_in()`` -must be used. It takes the name, a callback and an opaque parameter -for the callback (this will be explained in a following section). +must be used. It takes the name, a callback, an opaque parameter +for the callback and a mask of events when the callback should be +called (this will be explained in a following section). Output is simpler; only the name is required. Typically:: - qdev_init_clock_in(DEVICE(dev), "clk_in", clk_in_callback, dev); + qdev_init_clock_in(DEVICE(dev), "clk_in", clk_in_callback, dev, ClockUpdate); qdev_init_clock_out(DEVICE(dev), "clk_out"); Both functions return the created Clock pointer, which should be saved in the @@ -113,7 +114,7 @@ output. * callback for the input clock (see "Callback on input clock * change" section below for more information). */ - static void clk_in_callback(void *opaque); + static void clk_in_callback(void *opaque, ClockEvent event); /* * static array describing clocks: @@ -124,7 +125,7 @@ output. * the clk_out field of a MyDeviceState structure. */ static const ClockPortInitArray mydev_clocks = { - QDEV_CLOCK_IN(MyDeviceState, clk_in, clk_in_callback), + QDEV_CLOCK_IN(MyDeviceState, clk_in, clk_in_callback, ClockUpdate), QDEV_CLOCK_OUT(MyDeviceState, clk_out), QDEV_CLOCK_END }; @@ -153,6 +154,47 @@ nothing else to do. This value will be propagated to other clocks when connecting the clocks together and devices will fetch the right value during the first reset. +Clock callbacks +--------------- + +You can give a clock a callback function in several ways: + + * by passing it as an argument to ``qdev_init_clock_in()`` + * as an argument to the ``QDEV_CLOCK_IN()`` macro initializing an + array to be passed to ``qdev_init_clocks()`` + * by directly calling the ``clock_set_callback()`` function + +The callback function must be of this type: + +.. code-block:: c + + typedef void ClockCallback(void *opaque, ClockEvent event); + +The ``opaque`` argument is the pointer passed to ``qdev_init_clock_in()`` +or ``clock_set_callback()``; for ``qdev_init_clocks()`` it is the +``dev`` device pointer. + +The ``event`` argument specifies why the callback has been called. +When you register the callback you specify a mask of ClockEvent values +that you are interested in. The callback will only be called for those +events. + +The events currently supported are: + + * ``ClockPreUpdate`` : called when the input clock's period is about to + update. This is useful if the device needs to do some action for + which it needs to know the old value of the clock period. During + this callback, Clock API functions like ``clock_get()`` or + ``clock_ticks_to_ns()`` will use the old period. + * ``ClockUpdate`` : called after the input clock's period has changed. + During this callback, Clock API functions like ``clock_ticks_to_ns()`` + will use the new period. + +Note that a clock only has one callback: it is not possible to register +different functions for different events. You must register a single +callback which listens for all of the events you are interested in, +and use the ``event`` argument to identify which event has happened. + Retrieving clocks from a device ------------------------------- @@ -231,22 +273,48 @@ object during device instance init. For example: .. code-block:: c clk = qdev_init_clock_in(DEVICE(dev), "clk-in", clk_in_callback, - dev); + dev, ClockUpdate); /* set initial value to 10ns / 100MHz */ clock_set_ns(clk, 10); +To enforce that the clock is wired up by the board code, you can +call ``clock_has_source()`` in your device's realize method: + +.. code-block:: c + + if (!clock_has_source(s->clk)) { + error_setg(errp, "MyDevice: clk input must be connected"); + return; + } + +Note that this only checks that the clock has been wired up; it is +still possible that the output clock connected to it is disabled +or has not yet been configured, in which case the period will be +zero. You should use the clock callback to find out when the clock +period changes. + Fetching clock frequency/period ------------------------------- -To get the current state of a clock, use the functions ``clock_get()``, -``clock_get_ns()`` or ``clock_get_hz()``. +To get the current state of a clock, use the functions ``clock_get()`` +or ``clock_get_hz()``. + +``clock_get()`` returns the period of the clock in its fully precise +internal representation, as an unsigned 64-bit integer in units of +2^-32 nanoseconds. (For many purposes ``clock_ticks_to_ns()`` will +be more convenient; see the section below on expiry deadlines.) + +``clock_get_hz()`` returns the frequency of the clock, rounded to the +next lowest integer. This implies some inaccuracy due to the rounding, +so be cautious about using it in calculations. It is also possible to register a callback on clock frequency changes. -Here is an example: +Here is an example, which assumes that ``clock_callback`` has been +specified as the callback for the ``ClockUpdate`` event: .. code-block:: c - void clock_callback(void *opaque) { + void clock_callback(void *opaque, ClockEvent event) { MyDeviceState *s = (MyDeviceState *) opaque; /* * 'opaque' is the argument passed to qdev_init_clock_in(); @@ -254,10 +322,56 @@ Here is an example: */ /* do something with the new period */ - fprintf(stdout, "device new period is %" PRIu64 "ns\n", - clock_get_ns(dev->my_clk_input)); + fprintf(stdout, "device new period is %" PRIu64 "* 2^-32 ns\n", + clock_get(dev->my_clk_input)); } +If you are only interested in the frequency for displaying it to +humans (for instance in debugging), use ``clock_display_freq()``, +which returns a prettified string-representation, e.g. "33.3 MHz". +The caller must free the string with g_free() after use. + +Calculating expiry deadlines +---------------------------- + +A commonly required operation for a clock is to calculate how long +it will take for the clock to tick N times; this can then be used +to set a timer expiry deadline. Use the function ``clock_ticks_to_ns()``, +which takes an unsigned 64-bit count of ticks and returns the length +of time in nanoseconds required for the clock to tick that many times. + +It is important not to try to calculate expiry deadlines using a +shortcut like multiplying a "period of clock in nanoseconds" value +by the tick count, because clocks can have periods which are not a +whole number of nanoseconds, and the accumulated error in the +multiplication can be significant. + +For a clock with a very long period and a large number of ticks, +the result of this function could in theory be too large to fit in +a 64-bit value. To avoid overflow in this case, ``clock_ticks_to_ns()`` +saturates the result to INT64_MAX (because this is the largest valid +input to the QEMUTimer APIs). Since INT64_MAX nanoseconds is almost +300 years, anything with an expiry later than that is in the "will +never happen" category. Callers of ``clock_ticks_to_ns()`` should +therefore generally not special-case the possibility of a saturated +result but just allow the timer to be set to that far-future value. +(If you are performing further calculations on the returned value +rather than simply passing it to a QEMUTimer function like +``timer_mod_ns()`` then you should be careful to avoid overflow +in those calculations, of course.) + +Obtaining tick counts +--------------------- + +For calculations where you need to know the number of ticks in +a given duration, use ``clock_ns_to_ticks()``. This function handles +possible non-whole-number-of-nanoseconds periods and avoids +potential rounding errors. It will return '0' if the clock is stopped +(i.e. it has period zero). If the inputs imply a tick count that +overflows a 64-bit value (a very long duration for a clock with a +very short period) the output value is truncated, so effectively +the 64-bit output wraps around. + Changing a clock period ----------------------- diff --git a/docs/devel/code-of-conduct.rst b/docs/devel/code-of-conduct.rst new file mode 100644 index 0000000000..277b5250d1 --- /dev/null +++ b/docs/devel/code-of-conduct.rst @@ -0,0 +1,60 @@ +Code of Conduct +=============== + +The QEMU community is made up of a mixture of professionals and +volunteers from all over the world. Diversity is one of our strengths, +but it can also lead to communication issues and unhappiness. +To that end, we have a few ground rules that we ask people to adhere to. + +* Be welcoming. We are committed to making participation in this project + a harassment-free experience for everyone, regardless of level of + experience, gender, gender identity and expression, sexual orientation, + disability, personal appearance, body size, race, ethnicity, age, religion, + or nationality. + +* Be respectful. Not all of us will agree all the time. Disagreements, both + social and technical, happen all the time and the QEMU community is no + exception. When we disagree, we try to understand why. It is important that + we resolve disagreements and differing views constructively. Members of the + QEMU community should be respectful when dealing with other contributors as + well as with people outside the QEMU community and with users of QEMU. + +Harassment and other exclusionary behavior are not acceptable. A community +where people feel uncomfortable or threatened is neither welcoming nor +respectful. Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery + +* Personal attacks + +* Trolling or insulting/derogatory comments + +* Public or private harassment + +* Publishing other's private information, such as physical or electronic + addresses, without explicit permission + +This isn't an exhaustive list of things that you can't do. Rather, take +it in the spirit in which it's intended: a guide to make it easier to +be excellent to each other. + +This code of conduct applies to all spaces managed by the QEMU project. +This includes IRC, the mailing lists, the issue tracker, community +events, and any other forums created by the project team which the +community uses for communication. This code of conduct also applies +outside these spaces, when an individual acts as a representative or a +member of the project or its community. + +By adopting this code of conduct, project maintainers commit themselves +to fairly and consistently applying these principles to every aspect of +managing this project. If you believe someone is violating the code of +conduct, please read the :ref:`conflict-resolution` document for +information about how to proceed. + +Sources +------- + +This document is based on the `Fedora Code of Conduct +`__ and the +`Contributor Covenant version 1.3.0 +`__. diff --git a/docs/devel/conf.py b/docs/devel/conf.py deleted file mode 100644 index 7441f87e7f..0000000000 --- a/docs/devel/conf.py +++ /dev/null @@ -1,15 +0,0 @@ -# -*- coding: utf-8 -*- -# -# QEMU documentation build configuration file for the 'devel' manual. -# -# This includes the top level conf file and then makes any necessary tweaks. -import sys -import os - -qemu_docdir = os.path.abspath("..") -parent_config = os.path.join(qemu_docdir, "conf.py") -exec(compile(open(parent_config, "rb").read(), parent_config, 'exec')) - -# This slightly misuses the 'description', but is the best way to get -# the manual title to appear in the sidebar. -html_theme_options['description'] = u'Developer''s Guide' diff --git a/docs/devel/conflict-resolution.rst b/docs/devel/conflict-resolution.rst new file mode 100644 index 0000000000..bb25f61865 --- /dev/null +++ b/docs/devel/conflict-resolution.rst @@ -0,0 +1,80 @@ +.. _conflict-resolution: + +Conflict Resolution Policy +========================== + +Conflicts in the community can take many forms, from someone having a +bad day and using harsh and hurtful language on the mailing list to more +serious code of conduct violations (including sexist/racist statements +or threats of violence), and everything in between. + +For the vast majority of issues, we aim to empower individuals to first +resolve conflicts themselves, asking for help when needed, and only +after that fails to escalate further. This approach gives people more +control over the outcome of their dispute. + +How we resolve conflicts +------------------------ + +If you are experiencing conflict, please consider first addressing the +perceived conflict directly with other involved parties, preferably through +a real-time medium such as IRC. You could also try to get a third-party (e.g. +a mutual friend, and/or someone with background on the issue, but not +involved in the conflict) to intercede or mediate. + +If this fails or if you do not feel comfortable proceeding this way, or +if the problem requires immediate escalation, report the issue to the QEMU +leadership committee by sending an email to qemu@sfconservancy.org, providing +references to the misconduct. +For very urgent topics, you can also inform one or more members through IRC. +The up-to-date list of members is `available on the QEMU wiki +`__. + +Your report will be treated confidentially by the leadership committee and +not be published without your agreement. The QEMU leadership committee will +then do its best to review the incident in a timely manner, and will either +seek further information, or will make a determination on next steps. + +Remedies +-------- + +Escalating an issue to the QEMU leadership committee may result in actions +impacting one or more involved parties. In the event the leadership +committee has to intervene, here are some of the ways they might respond: + +1. Take no action. For example, if the leadership committee determines + the complaint has not been substantiated or is being made in bad faith, + or if it is deemed to be outside its purview. + +2. A private reprimand, explaining the consequences of continued behavior, + to one or more involved individuals. + +3. A private reprimand and request for a private or public apology + +4. A public reprimand and request for a public apology + +5. A public reprimand plus a mandatory cooling off period. The cooling + off period may require, for example, one or more of the following: + abstaining from maintainer duties; not interacting with people involved, + including unsolicited interaction with those enforcing the guidelines + and interaction on social media; being denied participation to in-person + events. The cooling off period is voluntary but may escalate to a + temporary ban in order to enforce it. + +6. A temporary or permanent ban from some or all current and future QEMU + spaces (mailing lists, IRC, wiki, etc.), possibly including in-person + events. + +In the event of severe harassment, the leadership committee may advise that +the matter be escalated to the relevant local law enforcement agency. It +is however not the role of the leadership committee to initiate contact +with law enforcement on behalf of any of the community members involved +in an incident. + +Sources +------- + +This document was developed based on the `Drupal Conflict Resolution +Policy and Process `__ +and the `Mozilla Consequence Ladder +`__ diff --git a/docs/devel/control-flow-integrity.rst b/docs/devel/control-flow-integrity.rst new file mode 100644 index 0000000000..d89d70733d --- /dev/null +++ b/docs/devel/control-flow-integrity.rst @@ -0,0 +1,137 @@ +============================ +Control-Flow Integrity (CFI) +============================ + +This document describes the current control-flow integrity (CFI) mechanism in +QEMU. How it can be enabled, its benefits and deficiencies, and how it affects +new and existing code in QEMU + +Basics +------ + +CFI is a hardening technique that focusing on guaranteeing that indirect +function calls have not been altered by an attacker. +The type used in QEMU is a forward-edge control-flow integrity that ensures +function calls performed through function pointers, always call a "compatible" +function. A compatible function is a function with the same signature of the +function pointer declared in the source code. + +This type of CFI is entirely compiler-based and relies on the compiler knowing +the signature of every function and every function pointer used in the code. +As of now, the only compiler that provides support for CFI is Clang. + +CFI is best used on production binaries, to protect against unknown attack +vectors. + +In case of a CFI violation (i.e. call to a non-compatible function) QEMU will +terminate abruptly, to stop the possible attack. + +Building with CFI +----------------- + +NOTE: CFI requires the use of link-time optimization. Therefore, when CFI is +selected, LTO will be automatically enabled. + +To build with CFI, the minimum requirement is Clang 6+. If you +are planning to also enable fuzzing, then Clang 11+ is needed (more on this +later). + +Given the use of LTO, a version of AR that supports LLVM IR is required. +The easies way of doing this is by selecting the AR provided by LLVM:: + + AR=llvm-ar-9 CC=clang-9 CXX=lang++-9 /path/to/configure --enable-cfi + +CFI is enabled on every binary produced. + +If desired, an additional flag to increase the verbosity of the output in case +of a CFI violation is offered (``--enable-debug-cfi``). + +Using QEMU built with CFI +------------------------- + +A binary with CFI will work exactly like a standard binary. In case of a CFI +violation, the binary will terminate with an illegal instruction signal. + +Incompatible code with CFI +-------------------------- + +As mentioned above, CFI is entirely compiler-based and therefore relies on +compile-time knowledge of the code. This means that, while generally supported +for most code, some specific use pattern can break CFI compatibility, and +create false-positives. The two main patterns that can cause issues are: + +* Just-in-time compiled code: since such code is created at runtime, the jump + to the buffer containing JIT code will fail. + +* Libraries loaded dynamically, e.g. with dlopen/dlsym, since the library was + not known at compile time. + +Current areas of QEMU that are not entirely compatible with CFI are: + +1. TCG, since the idea of TCG is to pre-compile groups of instructions at + runtime to speed-up interpretation, quite similarly to a JIT compiler + +2. TCI, where the interpreter has to interpret the generic *call* operation + +3. Plugins, since a plugin is implemented as an external library + +4. Modules, since they are implemented as an external library + +5. Directly calling signal handlers from the QEMU source code, since the + signal handler may have been provided by an external library or even plugged + at runtime. + +Disabling CFI for a specific function +------------------------------------- + +If you are working on function that is performing a call using an +incompatible way, as described before, you can selectively disable CFI checks +for such function by using the decorator ``QEMU_DISABLE_CFI`` at function +definition, and add an explanation on why the function is not compatible +with CFI. An example of the use of ``QEMU_DISABLE_CFI`` is provided here:: + + /* + * Disable CFI checks. + * TCG creates binary blobs at runtime, with the transformed code. + * A TB is a blob of binary code, created at runtime and called with an + * indirect function call. Since such function did not exist at compile time, + * the CFI runtime has no way to verify its signature and would fail. + * TCG is not considered a security-sensitive part of QEMU so this does not + * affect the impact of CFI in environment with high security requirements + */ + QEMU_DISABLE_CFI + static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb) + +NOTE: CFI needs to be disabled at the **caller** function, (i.e. a compatible +cfi function that calls a non-compatible one), since the check is performed +when the function call is performed. + +CFI and fuzzing +--------------- + +There is generally no advantage of using CFI and fuzzing together, because +they target different environments (production for CFI, debug for fuzzing). + +CFI could be used in conjunction with fuzzing to identify a broader set of +bugs that may not end immediately in a segmentation fault or triggering +an assertion. However, other sanitizers such as address and ub sanitizers +can identify such bugs in a more precise way than CFI. + +There is, however, an interesting use case in using CFI in conjunction with +fuzzing, that is to make sure that CFI is not triggering any false positive +in remote-but-possible parts of the code. + +CFI can be enabled with fuzzing, but with some caveats: +1. Fuzzing relies on the linker performing function wrapping at link-time. +The standard BFD linker does not support function wrapping when LTO is +also enabled. The workaround is to use LLVM's lld linker. +2. Fuzzing also relies on a custom linker script, which is only supported by +lld with version 11+. + +In other words, to compile with fuzzing and CFI, clang 11+ is required, and +lld needs to be used as a linker:: + + AR=llvm-ar-11 CC=clang-11 CXX=lang++-11 /path/to/configure --enable-cfi \ + -enable-fuzzing --extra-ldflags="-fuse-ld=lld" + +and then, compile the fuzzers as usual. diff --git a/docs/devel/fuzzing.rst b/docs/devel/fuzzing.rst new file mode 100644 index 0000000000..2749bb9bed --- /dev/null +++ b/docs/devel/fuzzing.rst @@ -0,0 +1,321 @@ +======== +Fuzzing +======== + +This document describes the virtual-device fuzzing infrastructure in QEMU and +how to use it to implement additional fuzzers. + +Basics +------ + +Fuzzing operates by passing inputs to an entry point/target function. The +fuzzer tracks the code coverage triggered by the input. Based on these +findings, the fuzzer mutates the input and repeats the fuzzing. + +To fuzz QEMU, we rely on libfuzzer. Unlike other fuzzers such as AFL, libfuzzer +is an *in-process* fuzzer. For the developer, this means that it is their +responsibility to ensure that state is reset between fuzzing-runs. + +Building the fuzzers +-------------------- + +*NOTE*: If possible, build a 32-bit binary. When forking, the 32-bit fuzzer is +much faster, since the page-map has a smaller size. This is due to the fact that +AddressSanitizer maps ~20TB of memory, as part of its detection. This results +in a large page-map, and a much slower ``fork()``. + +To build the fuzzers, install a recent version of clang: +Configure with (substitute the clang binaries with the version you installed). +Here, enable-sanitizers, is optional but it allows us to reliably detect bugs +such as out-of-bounds accesses, use-after-frees, double-frees etc.:: + + CC=clang-8 CXX=clang++-8 /path/to/configure --enable-fuzzing \ + --enable-sanitizers + +Fuzz targets are built similarly to system targets:: + + make qemu-fuzz-i386 + +This builds ``./qemu-fuzz-i386`` + +The first option to this command is: ``--fuzz-target=FUZZ_NAME`` +To list all of the available fuzzers run ``qemu-fuzz-i386`` with no arguments. + +For example:: + + ./qemu-fuzz-i386 --fuzz-target=virtio-scsi-fuzz + +Internally, libfuzzer parses all arguments that do not begin with ``"--"``. +Information about these is available by passing ``-help=1`` + +Now the only thing left to do is wait for the fuzzer to trigger potential +crashes. + +Useful libFuzzer flags +---------------------- + +As mentioned above, libFuzzer accepts some arguments. Passing ``-help=1`` will +list the available arguments. In particular, these arguments might be helpful: + +* ``CORPUS_DIR/`` : Specify a directory as the last argument to libFuzzer. + libFuzzer stores each "interesting" input in this corpus directory. The next + time you run libFuzzer, it will read all of the inputs from the corpus, and + continue fuzzing from there. You can also specify multiple directories. + libFuzzer loads existing inputs from all specified directories, but will only + write new ones to the first one specified. + +* ``-max_len=4096`` : specify the maximum byte-length of the inputs libFuzzer + will generate. + +* ``-close_fd_mask={1,2,3}`` : close, stderr, or both. Useful for targets that + trigger many debug/error messages, or create output on the serial console. + +* ``-jobs=4 -workers=4`` : These arguments configure libFuzzer to run 4 fuzzers in + parallel (4 fuzzing jobs in 4 worker processes). Alternatively, with only + ``-jobs=N``, libFuzzer automatically spawns a number of workers less than or equal + to half the available CPU cores. Replace 4 with a number appropriate for your + machine. Make sure to specify a ``CORPUS_DIR``, which will allow the parallel + fuzzers to share information about the interesting inputs they find. + +* ``-use_value_profile=1`` : For each comparison operation, libFuzzer computes + ``(caller_pc&4095) | (popcnt(Arg1 ^ Arg2) << 12)`` and places this in the + coverage table. Useful for targets with "magic" constants. If Arg1 came from + the fuzzer's input and Arg2 is a magic constant, then each time the Hamming + distance between Arg1 and Arg2 decreases, libFuzzer adds the input to the + corpus. + +* ``-shrink=1`` : Tries to make elements of the corpus "smaller". Might lead to + better coverage performance, depending on the target. + +Note that libFuzzer's exact behavior will depend on the version of +clang and libFuzzer used to build the device fuzzers. + +Generating Coverage Reports +--------------------------- + +Code coverage is a crucial metric for evaluating a fuzzer's performance. +libFuzzer's output provides a "cov: " column that provides a total number of +unique blocks/edges covered. To examine coverage on a line-by-line basis we +can use Clang coverage: + + 1. Configure libFuzzer to store a corpus of all interesting inputs (see + CORPUS_DIR above) + 2. ``./configure`` the QEMU build with :: + + --enable-fuzzing \ + --extra-cflags="-fprofile-instr-generate -fcoverage-mapping" + + 3. Re-run the fuzzer. Specify $CORPUS_DIR/* as an argument, telling libfuzzer + to execute all of the inputs in $CORPUS_DIR and exit. Once the process + exits, you should find a file, "default.profraw" in the working directory. + 4. Execute these commands to generate a detailed HTML coverage-report:: + + llvm-profdata merge -output=default.profdata default.profraw + llvm-cov show ./path/to/qemu-fuzz-i386 -instr-profile=default.profdata \ + --format html -output-dir=/path/to/output/report + +Adding a new fuzzer +------------------- + +Coverage over virtual devices can be improved by adding additional fuzzers. +Fuzzers are kept in ``tests/qtest/fuzz/`` and should be added to +``tests/qtest/fuzz/meson.build`` + +Fuzzers can rely on both qtest and libqos to communicate with virtual devices. + +1. Create a new source file. For example ``tests/qtest/fuzz/foo-device-fuzz.c``. + +2. Write the fuzzing code using the libqtest/libqos API. See existing fuzzers + for reference. + +3. Add the fuzzer to ``tests/qtest/fuzz/meson.build``. + +Fuzzers can be more-or-less thought of as special qtest programs which can +modify the qtest commands and/or qtest command arguments based on inputs +provided by libfuzzer. Libfuzzer passes a byte array and length. Commonly the +fuzzer loops over the byte-array interpreting it as a list of qtest commands, +addresses, or values. + +The Generic Fuzzer +------------------ + +Writing a fuzz target can be a lot of effort (especially if a device driver has +not be built-out within libqos). Many devices can be fuzzed to some degree, +without any device-specific code, using the generic-fuzz target. + +The generic-fuzz target is capable of fuzzing devices over their PIO, MMIO, +and DMA input-spaces. To apply the generic-fuzz to a device, we need to define +two env-variables, at minimum: + +* ``QEMU_FUZZ_ARGS=`` is the set of QEMU arguments used to configure a machine, with + the device attached. For example, if we want to fuzz the virtio-net device + attached to a pc-i440fx machine, we can specify:: + + QEMU_FUZZ_ARGS="-M pc -nodefaults -netdev user,id=user0 \ + -device virtio-net,netdev=user0" + +* ``QEMU_FUZZ_OBJECTS=`` is a set of space-delimited strings used to identify + the MemoryRegions that will be fuzzed. These strings are compared against + MemoryRegion names and MemoryRegion owner names, to decide whether each + MemoryRegion should be fuzzed. These strings support globbing. For the + virtio-net example, we could use one of :: + + QEMU_FUZZ_OBJECTS='virtio-net' + QEMU_FUZZ_OBJECTS='virtio*' + QEMU_FUZZ_OBJECTS='virtio* pcspk' # Fuzz the virtio devices and the speaker + QEMU_FUZZ_OBJECTS='*' # Fuzz the whole machine`` + +The ``"info mtree"`` and ``"info qom-tree"`` monitor commands can be especially +useful for identifying the ``MemoryRegion`` and ``Object`` names used for +matching. + +As a generic rule-of-thumb, the more ``MemoryRegions``/Devices we match, the +greater the input-space, and the smaller the probability of finding crashing +inputs for individual devices. As such, it is usually a good idea to limit the +fuzzer to only a few ``MemoryRegions``. + +To ensure that these env variables have been configured correctly, we can use:: + + ./qemu-fuzz-i386 --fuzz-target=generic-fuzz -runs=0 + +The output should contain a complete list of matched MemoryRegions. + +OSS-Fuzz +-------- +QEMU is continuously fuzzed on `OSS-Fuzz` __(https://github.com/google/oss-fuzz). +By default, the OSS-Fuzz build will try to fuzz every fuzz-target. Since the +generic-fuzz target requires additional information provided in environment +variables, we pre-define some generic-fuzz configs in +``tests/qtest/fuzz/generic_fuzz_configs.h``. Each config must specify: + +- ``.name``: To identify the fuzzer config + +- ``.args`` OR ``.argfunc``: A string or pointer to a function returning a + string. These strings are used to specify the ``QEMU_FUZZ_ARGS`` + environment variable. ``argfunc`` is useful when the config relies on e.g. + a dynamically created temp directory, or a free tcp/udp port. + +- ``.objects``: A string that specifies the ``QEMU_FUZZ_OBJECTS`` environment + variable. + +To fuzz additional devices/device configuration on OSS-Fuzz, send patches for +either a new device-specific fuzzer or a new generic-fuzz config. + +Build details: + +- The Dockerfile that sets up the environment for building QEMU's + fuzzers on OSS-Fuzz can be fund in the OSS-Fuzz repository + __(https://github.com/google/oss-fuzz/blob/master/projects/qemu/Dockerfile) + +- The script responsible for building the fuzzers can be found in the + QEMU source tree at ``scripts/oss-fuzz/build.sh`` + +Building Crash Reproducers +----------------------------------------- +When we find a crash, we should try to create an independent reproducer, that +can be used on a non-fuzzer build of QEMU. This filters out any potential +false-positives, and improves the debugging experience for developers. +Here are the steps for building a reproducer for a crash found by the +generic-fuzz target. + +- Ensure the crash reproduces:: + + qemu-fuzz-i386 --fuzz-target... ./crash-... + +- Gather the QTest output for the crash:: + + QEMU_FUZZ_TIMEOUT=0 QTEST_LOG=1 FUZZ_SERIALIZE_QTEST=1 \ + qemu-fuzz-i386 --fuzz-target... ./crash-... &> /tmp/trace + +- Reorder and clean-up the resulting trace:: + + scripts/oss-fuzz/reorder_fuzzer_qtest_trace.py /tmp/trace > /tmp/reproducer + +- Get the arguments needed to start qemu, and provide a path to qemu:: + + less /tmp/trace # The args should be logged at the top of this file + export QEMU_ARGS="-machine ..." + export QEMU_PATH="path/to/qemu-system" + +- Ensure the crash reproduces in qemu-system:: + + $QEMU_PATH $QEMU_ARGS -qtest stdio < /tmp/reproducer + +- From the crash output, obtain some string that identifies the crash. This + can be a line in the stack-trace, for example:: + + export CRASH_TOKEN="hw/usb/hcd-xhci.c:1865" + +- Minimize the reproducer:: + + scripts/oss-fuzz/minimize_qtest_trace.py -M1 -M2 \ + /tmp/reproducer /tmp/reproducer-minimized + +- Confirm that the minimized reproducer still crashes:: + + $QEMU_PATH $QEMU_ARGS -qtest stdio < /tmp/reproducer-minimized + +- Create a one-liner reproducer that can be sent over email:: + + ./scripts/oss-fuzz/output_reproducer.py -bash /tmp/reproducer-minimized + +- Output the C source code for a test case that will reproduce the bug:: + + ./scripts/oss-fuzz/output_reproducer.py -owner "John Smith "\ + -name "test_function_name" /tmp/reproducer-minimized + +- Report the bug and send a patch with the C reproducer upstream + +Implementation Details / Fuzzer Lifecycle +----------------------------------------- + +The fuzzer has two entrypoints that libfuzzer calls. libfuzzer provides it's +own ``main()``, which performs some setup, and calls the entrypoints: + +``LLVMFuzzerInitialize``: called prior to fuzzing. Used to initialize all of the +necessary state + +``LLVMFuzzerTestOneInput``: called for each fuzzing run. Processes the input and +resets the state at the end of each run. + +In more detail: + +``LLVMFuzzerInitialize`` parses the arguments to the fuzzer (must start with two +dashes, so they are ignored by libfuzzer ``main()``). Currently, the arguments +select the fuzz target. Then, the qtest client is initialized. If the target +requires qos, qgraph is set up and the QOM/LIBQOS modules are initialized. +Then the QGraph is walked and the QEMU cmd_line is determined and saved. + +After this, the ``vl.c:qemu_main`` is called to set up the guest. There are +target-specific hooks that can be called before and after qemu_main, for +additional setup(e.g. PCI setup, or VM snapshotting). + +``LLVMFuzzerTestOneInput``: Uses qtest/qos functions to act based on the fuzz +input. It is also responsible for manually calling ``main_loop_wait`` to ensure +that bottom halves are executed and any cleanup required before the next input. + +Since the same process is reused for many fuzzing runs, QEMU state needs to +be reset at the end of each run. There are currently two implemented +options for resetting state: + +- Reboot the guest between runs. + - *Pros*: Straightforward and fast for simple fuzz targets. + + - *Cons*: Depending on the device, does not reset all device state. If the + device requires some initialization prior to being ready for fuzzing (common + for QOS-based targets), this initialization needs to be done after each + reboot. + + - *Example target*: ``i440fx-qtest-reboot-fuzz`` + +- Run each test case in a separate forked process and copy the coverage + information back to the parent. This is fairly similar to AFL's "deferred" + fork-server mode [3] + + - *Pros*: Relatively fast. Devices only need to be initialized once. No need to + do slow reboots or vmloads. + + - *Cons*: Not officially supported by libfuzzer. Does not work well for + devices that rely on dedicated threads. + + - *Example target*: ``virtio-net-fork-fuzz`` diff --git a/docs/devel/fuzzing.txt b/docs/devel/fuzzing.txt deleted file mode 100644 index 96d71c94d7..0000000000 --- a/docs/devel/fuzzing.txt +++ /dev/null @@ -1,175 +0,0 @@ -= Fuzzing = - -== Introduction == - -This document describes the virtual-device fuzzing infrastructure in QEMU and -how to use it to implement additional fuzzers. - -== Basics == - -Fuzzing operates by passing inputs to an entry point/target function. The -fuzzer tracks the code coverage triggered by the input. Based on these -findings, the fuzzer mutates the input and repeats the fuzzing. - -To fuzz QEMU, we rely on libfuzzer. Unlike other fuzzers such as AFL, libfuzzer -is an _in-process_ fuzzer. For the developer, this means that it is their -responsibility to ensure that state is reset between fuzzing-runs. - -== Building the fuzzers == - -NOTE: If possible, build a 32-bit binary. When forking, the 32-bit fuzzer is -much faster, since the page-map has a smaller size. This is due to the fact that -AddressSanitizer mmaps ~20TB of memory, as part of its detection. This results -in a large page-map, and a much slower fork(). - -To build the fuzzers, install a recent version of clang: -Configure with (substitute the clang binaries with the version you installed). -Here, enable-sanitizers, is optional but it allows us to reliably detect bugs -such as out-of-bounds accesses, use-after-frees, double-frees etc. - - CC=clang-8 CXX=clang++-8 /path/to/configure --enable-fuzzing \ - --enable-sanitizers - -Fuzz targets are built similarly to system/softmmu: - - make i386-softmmu/fuzz - -This builds ./i386-softmmu/qemu-fuzz-i386 - -The first option to this command is: --fuzz-target=FUZZ_NAME -To list all of the available fuzzers run qemu-fuzz-i386 with no arguments. - -For example: - ./i386-softmmu/qemu-fuzz-i386 --fuzz-target=virtio-scsi-fuzz - -Internally, libfuzzer parses all arguments that do not begin with "--". -Information about these is available by passing -help=1 - -Now the only thing left to do is wait for the fuzzer to trigger potential -crashes. - -== Useful libFuzzer flags == - -As mentioned above, libFuzzer accepts some arguments. Passing -help=1 will list -the available arguments. In particular, these arguments might be helpful: - -$CORPUS_DIR/ : Specify a directory as the last argument to libFuzzer. libFuzzer -stores each "interesting" input in this corpus directory. The next time you run -libFuzzer, it will read all of the inputs from the corpus, and continue fuzzing -from there. You can also specify multiple directories. libFuzzer loads existing -inputs from all specified directories, but will only write new ones to the -first one specified. - --max_len=4096 : specify the maximum byte-length of the inputs libFuzzer will -generate. - --close_fd_mask={1,2,3} : close, stderr, or both. Useful for targets that -trigger many debug/error messages, or create output on the serial console. - --jobs=4 -workers=4 : These arguments configure libFuzzer to run 4 fuzzers in -parallel (4 fuzzing jobs in 4 worker processes). Alternatively, with only --jobs=N, libFuzzer automatically spawns a number of workers less than or equal -to half the available CPU cores. Replace 4 with a number appropriate for your -machine. Make sure to specify a $CORPUS_DIR, which will allow the parallel -fuzzers to share information about the interesting inputs they find. - --use_value_profile=1 : For each comparison operation, libFuzzer computes -(caller_pc&4095) | (popcnt(Arg1 ^ Arg2) << 12) and places this in the coverage -table. Useful for targets with "magic" constants. If Arg1 came from the fuzzer's -input and Arg2 is a magic constant, then each time the Hamming distance -between Arg1 and Arg2 decreases, libFuzzer adds the input to the corpus. - --shrink=1 : Tries to make elements of the corpus "smaller". Might lead to -better coverage performance, depending on the target. - -Note that libFuzzer's exact behavior will depend on the version of -clang and libFuzzer used to build the device fuzzers. - -== Generating Coverage Reports == -Code coverage is a crucial metric for evaluating a fuzzer's performance. -libFuzzer's output provides a "cov: " column that provides a total number of -unique blocks/edges covered. To examine coverage on a line-by-line basis we -can use Clang coverage: - - 1. Configure libFuzzer to store a corpus of all interesting inputs (see - CORPUS_DIR above) - 2. ./configure the QEMU build with: - --enable-fuzzing \ - --extra-cflags="-fprofile-instr-generate -fcoverage-mapping" - 3. Re-run the fuzzer. Specify $CORPUS_DIR/* as an argument, telling libfuzzer - to execute all of the inputs in $CORPUS_DIR and exit. Once the process - exits, you should find a file, "default.profraw" in the working directory. - 4. Execute these commands to generate a detailed HTML coverage-report: - llvm-profdata merge -output=default.profdata default.profraw - llvm-cov show ./path/to/qemu-fuzz-i386 -instr-profile=default.profdata \ - --format html -output-dir=/path/to/output/report - -== Adding a new fuzzer == -Coverage over virtual devices can be improved by adding additional fuzzers. -Fuzzers are kept in tests/qtest/fuzz/ and should be added to -tests/qtest/fuzz/Makefile.include - -Fuzzers can rely on both qtest and libqos to communicate with virtual devices. - -1. Create a new source file. For example ``tests/qtest/fuzz/foo-device-fuzz.c``. - -2. Write the fuzzing code using the libqtest/libqos API. See existing fuzzers -for reference. - -3. Register the fuzzer in ``tests/fuzz/Makefile.include`` by appending the -corresponding object to fuzz-obj-y - -Fuzzers can be more-or-less thought of as special qtest programs which can -modify the qtest commands and/or qtest command arguments based on inputs -provided by libfuzzer. Libfuzzer passes a byte array and length. Commonly the -fuzzer loops over the byte-array interpreting it as a list of qtest commands, -addresses, or values. - -= Implementation Details = - -== The Fuzzer's Lifecycle == - -The fuzzer has two entrypoints that libfuzzer calls. libfuzzer provides it's -own main(), which performs some setup, and calls the entrypoints: - -LLVMFuzzerInitialize: called prior to fuzzing. Used to initialize all of the -necessary state - -LLVMFuzzerTestOneInput: called for each fuzzing run. Processes the input and -resets the state at the end of each run. - -In more detail: - -LLVMFuzzerInitialize parses the arguments to the fuzzer (must start with two -dashes, so they are ignored by libfuzzer main()). Currently, the arguments -select the fuzz target. Then, the qtest client is initialized. If the target -requires qos, qgraph is set up and the QOM/LIBQOS modules are initialized. -Then the QGraph is walked and the QEMU cmd_line is determined and saved. - -After this, the vl.c:qemu__main is called to set up the guest. There are -target-specific hooks that can be called before and after qemu_main, for -additional setup(e.g. PCI setup, or VM snapshotting). - -LLVMFuzzerTestOneInput: Uses qtest/qos functions to act based on the fuzz -input. It is also responsible for manually calling the main loop/main_loop_wait -to ensure that bottom halves are executed and any cleanup required before the -next input. - -Since the same process is reused for many fuzzing runs, QEMU state needs to -be reset at the end of each run. There are currently two implemented -options for resetting state: -1. Reboot the guest between runs. - Pros: Straightforward and fast for simple fuzz targets. - Cons: Depending on the device, does not reset all device state. If the - device requires some initialization prior to being ready for fuzzing - (common for QOS-based targets), this initialization needs to be done after - each reboot. - Example target: i440fx-qtest-reboot-fuzz -2. Run each test case in a separate forked process and copy the coverage - information back to the parent. This is fairly similar to AFL's "deferred" - fork-server mode [3] - Pros: Relatively fast. Devices only need to be initialized once. No need - to do slow reboots or vmloads. - Cons: Not officially supported by libfuzzer. Does not work well for devices - that rely on dedicated threads. - Example target: virtio-net-fork-fuzz diff --git a/docs/devel/index.rst b/docs/devel/index.rst index ae6eac7c9c..6cf7e2d233 100644 --- a/docs/devel/index.rst +++ b/docs/devel/index.rst @@ -1,8 +1,8 @@ .. This is the top level page for the 'devel' manual. -QEMU Developer's Guide -====================== +Developer Information +===================== This manual documents various parts of the internals of QEMU. You only need to read it if you are interested in reading or @@ -12,21 +12,33 @@ Contents: .. toctree:: :maxdepth: 2 + :includehidden: + code-of-conduct + conflict-resolution + build-system + style kconfig + testing + fuzzing + control-flow-integrity loads-stores memory migration atomics stable-process - testing + qtest decodetree secure-coding-practices tcg tcg-icount + tracing multi-thread-tcg tcg-plugins bitops reset s390-dasd-ipl clocks + qom + block-coroutine-wrapper + multi-process diff --git a/docs/devel/kconfig.rst b/docs/devel/kconfig.rst index e5df72b342..cb2d7ffac0 100644 --- a/docs/devel/kconfig.rst +++ b/docs/devel/kconfig.rst @@ -1,3 +1,5 @@ +.. _kconfig: + ================ QEMU and Kconfig ================ @@ -286,21 +288,20 @@ they will include all these symbols and some help text on what they do. ---------------- In some special cases, a configurable element depends on host features -that are detected by QEMU's configure script; for example some devices -depend on the availability of KVM or on the presence of a library on -the host. +that are detected by QEMU's configure or ``meson.build`` scripts; for +example some devices depend on the availability of KVM or on the presence +of a library on the host. These symbols should be listed in ``Kconfig.host`` like this:: - config KVM + config TPM bool -and also listed as follows in the top-level Makefile's ``MINIKCONF_ARGS`` +and also listed as follows in the top-level meson.build's host_kconfig variable:: - MINIKCONF_ARGS = \ - $@ $*/config-devices.mak.d $< $(MINIKCONF_INPUTS) \ - CONFIG_KVM=$(CONFIG_KVM) \ - CONFIG_SPICE=$(CONFIG_SPICE) \ - CONFIG_TPM=$(CONFIG_TPM) \ + host_kconfig = \ + ('CONFIG_TPM' in config_host ? ['CONFIG_TPM=y'] : []) + \ + ('CONFIG_SPICE' in config_host ? ['CONFIG_SPICE=y'] : []) + \ + ('CONFIG_IVSHMEM' in config_host ? ['CONFIG_IVSHMEM=y'] : []) + \ ... diff --git a/docs/devel/loads-stores.rst b/docs/devel/loads-stores.rst index 9a944ef1af..568274baec 100644 --- a/docs/devel/loads-stores.rst +++ b/docs/devel/loads-stores.rst @@ -24,16 +24,12 @@ potentially unaligned pointer values. Function names follow the pattern: -load: ``ld{type}{sign}{size}_{endian}_p(ptr)`` +load: ``ld{sign}{size}_{endian}_p(ptr)`` -store: ``st{type}{size}_{endian}_p(ptr, val)`` - -``type`` - - (empty) : integer access - - ``f`` : float access +store: ``st{size}_{endian}_p(ptr, val)`` ``sign`` - - (empty) : for 32 or 64 bit sizes (including floats and doubles) + - (empty) : for 32 or 64 bit sizes - ``u`` : unsigned - ``s`` : signed @@ -67,8 +63,8 @@ of size ``sz`` bytes. Regexes for git grep - - ``\`` - - ``\`` + - ``\`` + - ``\`` - ``\`` - ``\`` @@ -93,7 +89,13 @@ guest CPU state in case of a guest CPU exception. This is passed to ``cpu_restore_state()``. Therefore the value should either be 0, to indicate that the guest CPU state is already synchronized, or the result of ``GETPC()`` from the top level ``HELPER(foo)`` -function, which is a return address into the generated code. +function, which is a return address into the generated code [#gpc]_. + +.. [#gpc] Note that ``GETPC()`` should be used with great care: calling + it in other functions that are *not* the top level + ``HELPER(foo)`` will cause unexpected behavior. Instead, the + value of ``GETPC()`` should be read from the helper and passed + if needed to the functions that the helper calls. Function names follow the pattern: @@ -477,6 +479,8 @@ make sure our existing code is doing things correctly. Regexes for git grep - ``\`` + - ``\`` + - ``\`` ``pci_dma_*`` and ``{ld,st}*_pci_dma`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/devel/lockcnt.txt b/docs/devel/lockcnt.txt index 7c099bc6c8..2d85462fe3 100644 --- a/docs/devel/lockcnt.txt +++ b/docs/devel/lockcnt.txt @@ -95,7 +95,7 @@ not just frees, though there could be cases where this is not necessary. Reads, instead, can be done without taking the mutex, as long as the readers and writers use the same macros that are used for RCU, for -example atomic_rcu_read, atomic_rcu_set, QLIST_FOREACH_RCU, etc. This is +example qatomic_rcu_read, qatomic_rcu_set, QLIST_FOREACH_RCU, etc. This is because the reads are done outside a lock and a set or QLIST_INSERT_HEAD can happen concurrently with the read. The RCU API ensures that the processor and the compiler see all required memory barriers. @@ -189,7 +189,7 @@ qemu_lockcnt_lock and qemu_lockcnt_unlock: if (!xyz) { new_xyz = g_new(XYZ, 1); ... - atomic_rcu_set(&xyz, new_xyz); + qatomic_rcu_set(&xyz, new_xyz); } qemu_lockcnt_unlock(&xyz_lockcnt); @@ -198,7 +198,7 @@ qemu_lockcnt_dec: qemu_lockcnt_inc(&xyz_lockcnt); if (xyz) { - XYZ *p = atomic_rcu_read(&xyz); + XYZ *p = qatomic_rcu_read(&xyz); ... /* Accesses can now be done through "p". */ } @@ -222,7 +222,7 @@ the decrement, the locking and the check on count as follows: qemu_lockcnt_inc(&xyz_lockcnt); if (xyz) { - XYZ *p = atomic_rcu_read(&xyz); + XYZ *p = qatomic_rcu_read(&xyz); ... /* Accesses can now be done through "p". */ } diff --git a/docs/devel/migration.rst b/docs/devel/migration.rst index 2eb08624fc..19c3d4f3ea 100644 --- a/docs/devel/migration.rst +++ b/docs/devel/migration.rst @@ -53,22 +53,23 @@ savevm/loadvm functionality. Debugging ========= -The migration stream can be analyzed thanks to `scripts/analyze_migration.py`. +The migration stream can be analyzed thanks to `scripts/analyze-migration.py`. Example usage: .. code-block:: shell - $ qemu-system-x86_64 - (qemu) migrate "exec:cat > mig" - $ ./scripts/analyze_migration.py -f mig + $ qemu-system-x86_64 -display none -monitor stdio + (qemu) migrate "exec:cat > mig" + (qemu) q + $ ./scripts/analyze-migration.py -f mig { "ram (3)": { "section sizes": { "pc.ram": "0x0000000008000000", ... -See also ``analyze_migration.py -h`` help for more options. +See also ``analyze-migration.py -h`` help for more options. Common infrastructure ===================== @@ -625,7 +626,7 @@ It can be issued immediately after migration is started or any time later on. Issuing it after the end of a migration is harmless. Blocktime is a postcopy live migration metric, intended to show how -long the vCPU was in state of interruptable sleep due to pagefault. +long the vCPU was in state of interruptible sleep due to pagefault. That metric is calculated both for all vCPUs as overlapped value, and separately for each vCPU. These values are calculated on destination side. To enable postcopy blocktime calculation, enter following @@ -640,7 +641,7 @@ time per vCPU. .. note:: During the postcopy phase, the bandwidth limits set using - ``migrate_set_speed`` is ignored (to avoid delaying requested pages that + ``migrate_set_parameter`` is ignored (to avoid delaying requested pages that the destination is waiting for). Postcopy device transfer diff --git a/docs/devel/multi-process.rst b/docs/devel/multi-process.rst new file mode 100644 index 0000000000..69699329d6 --- /dev/null +++ b/docs/devel/multi-process.rst @@ -0,0 +1,966 @@ +This is the design document for multi-process QEMU. It does not +necessarily reflect the status of the current implementation, which +may lack features or be considerably different from what is described +in this document. This document is still useful as a description of +the goals and general direction of this feature. + +Please refer to the following wiki for latest details: +https://wiki.qemu.org/Features/MultiProcessQEMU + +Multi-process QEMU +=================== + +QEMU is often used as the hypervisor for virtual machines running in the +Oracle cloud. Since one of the advantages of cloud computing is the +ability to run many VMs from different tenants in the same cloud +infrastructure, a guest that compromised its hypervisor could +potentially use the hypervisor's access privileges to access data it is +not authorized for. + +QEMU can be susceptible to security attacks because it is a large, +monolithic program that provides many features to the VMs it services. +Many of these features can be configured out of QEMU, but even a reduced +configuration QEMU has a large amount of code a guest can potentially +attack. Separating QEMU reduces the attack surface by aiding to +limit each component in the system to only access the resources that +it needs to perform its job. + +QEMU services +------------- + +QEMU can be broadly described as providing three main services. One is a +VM control point, where VMs can be created, migrated, re-configured, and +destroyed. A second is to emulate the CPU instructions within the VM, +often accelerated by HW virtualization features such as Intel's VT +extensions. Finally, it provides IO services to the VM by emulating HW +IO devices, such as disk and network devices. + +A multi-process QEMU +~~~~~~~~~~~~~~~~~~~~ + +A multi-process QEMU involves separating QEMU services into separate +host processes. Each of these processes can be given only the privileges +it needs to provide its service, e.g., a disk service could be given +access only to the disk images it provides, and not be allowed to +access other files, or any network devices. An attacker who compromised +this service would not be able to use this exploit to access files or +devices beyond what the disk service was given access to. + +A QEMU control process would remain, but in multi-process mode, will +have no direct interfaces to the VM. During VM execution, it would still +provide the user interface to hot-plug devices or live migrate the VM. + +A first step in creating a multi-process QEMU is to separate IO services +from the main QEMU program, which would continue to provide CPU +emulation. i.e., the control process would also be the CPU emulation +process. In a later phase, CPU emulation could be separated from the +control process. + +Separating IO services +---------------------- + +Separating IO services into individual host processes is a good place to +begin for a couple of reasons. One is the sheer number of IO devices QEMU +can emulate provides a large surface of interfaces which could potentially +be exploited, and, indeed, have been a source of exploits in the past. +Another is the modular nature of QEMU device emulation code provides +interface points where the QEMU functions that perform device emulation +can be separated from the QEMU functions that manage the emulation of +guest CPU instructions. The devices emulated in the separate process are +referred to as remote devices. + +QEMU device emulation +~~~~~~~~~~~~~~~~~~~~~ + +QEMU uses an object oriented SW architecture for device emulation code. +Configured objects are all compiled into the QEMU binary, then objects +are instantiated by name when used by the guest VM. For example, the +code to emulate a device named "foo" is always present in QEMU, but its +instantiation code is only run when the device is included in the target +VM. (e.g., via the QEMU command line as *-device foo*) + +The object model is hierarchical, so device emulation code names its +parent object (such as "pci-device" for a PCI device) and QEMU will +instantiate a parent object before calling the device's instantiation +code. + +Current separation models +~~~~~~~~~~~~~~~~~~~~~~~~~ + +In order to separate the device emulation code from the CPU emulation +code, the device object code must run in a different process. There are +a couple of existing QEMU features that can run emulation code +separately from the main QEMU process. These are examined below. + +vhost user model +^^^^^^^^^^^^^^^^ + +Virtio guest device drivers can be connected to vhost user applications +in order to perform their IO operations. This model uses special virtio +device drivers in the guest and vhost user device objects in QEMU, but +once the QEMU vhost user code has configured the vhost user application, +mission-mode IO is performed by the application. The vhost user +application is a daemon process that can be contacted via a known UNIX +domain socket. + +vhost socket +'''''''''''' + +As mentioned above, one of the tasks of the vhost device object within +QEMU is to contact the vhost application and send it configuration +information about this device instance. As part of the configuration +process, the application can also be sent other file descriptors over +the socket, which then can be used by the vhost user application in +various ways, some of which are described below. + +vhost MMIO store acceleration +''''''''''''''''''''''''''''' + +VMs are often run using HW virtualization features via the KVM kernel +driver. This driver allows QEMU to accelerate the emulation of guest CPU +instructions by running the guest in a virtual HW mode. When the guest +executes instructions that cannot be executed by virtual HW mode, +execution returns to the KVM driver so it can inform QEMU to emulate the +instructions in SW. + +One of the events that can cause a return to QEMU is when a guest device +driver accesses an IO location. QEMU then dispatches the memory +operation to the corresponding QEMU device object. In the case of a +vhost user device, the memory operation would need to be sent over a +socket to the vhost application. This path is accelerated by the QEMU +virtio code by setting up an eventfd file descriptor that the vhost +application can directly receive MMIO store notifications from the KVM +driver, instead of needing them to be sent to the QEMU process first. + +vhost interrupt acceleration +'''''''''''''''''''''''''''' + +Another optimization used by the vhost application is the ability to +directly inject interrupts into the VM via the KVM driver, again, +bypassing the need to send the interrupt back to the QEMU process first. +The QEMU virtio setup code configures the KVM driver with an eventfd +that triggers the device interrupt in the guest when the eventfd is +written. This irqfd file descriptor is then passed to the vhost user +application program. + +vhost access to guest memory +'''''''''''''''''''''''''''' + +The vhost application is also allowed to directly access guest memory, +instead of needing to send the data as messages to QEMU. This is also +done with file descriptors sent to the vhost user application by QEMU. +These descriptors can be passed to ``mmap()`` by the vhost application +to map the guest address space into the vhost application. + +IOMMUs introduce another level of complexity, since the address given to +the guest virtio device to DMA to or from is not a guest physical +address. This case is handled by having vhost code within QEMU register +as a listener for IOMMU mapping changes. The vhost application maintains +a cache of IOMMMU translations: sending translation requests back to +QEMU on cache misses, and in turn receiving flush requests from QEMU +when mappings are purged. + +applicability to device separation +'''''''''''''''''''''''''''''''''' + +Much of the vhost model can be re-used by separated device emulation. In +particular, the ideas of using a socket between QEMU and the device +emulation application, using a file descriptor to inject interrupts into +the VM via KVM, and allowing the application to ``mmap()`` the guest +should be re used. + +There are, however, some notable differences between how a vhost +application works and the needs of separated device emulation. The most +basic is that vhost uses custom virtio device drivers which always +trigger IO with MMIO stores. A separated device emulation model must +work with existing IO device models and guest device drivers. MMIO loads +break vhost store acceleration since they are synchronous - guest +progress cannot continue until the load has been emulated. By contrast, +stores are asynchronous, the guest can continue after the store event +has been sent to the vhost application. + +Another difference is that in the vhost user model, a single daemon can +support multiple QEMU instances. This is contrary to the security regime +desired, in which the emulation application should only be allowed to +access the files or devices the VM it's running on behalf of can access. +#### qemu-io model + +Qemu-io is a test harness used to test changes to the QEMU block backend +object code. (e.g., the code that implements disk images for disk driver +emulation) Qemu-io is not a device emulation application per se, but it +does compile the QEMU block objects into a separate binary from the main +QEMU one. This could be useful for disk device emulation, since its +emulation applications will need to include the QEMU block objects. + +New separation model based on proxy objects +------------------------------------------- + +A different model based on proxy objects in the QEMU program +communicating with remote emulation programs could provide separation +while minimizing the changes needed to the device emulation code. The +rest of this section is a discussion of how a proxy object model would +work. + +Remote emulation processes +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The remote emulation process will run the QEMU object hierarchy without +modification. The device emulation objects will be also be based on the +QEMU code, because for anything but the simplest device, it would not be +a tractable to re-implement both the object model and the many device +backends that QEMU has. + +The processes will communicate with the QEMU process over UNIX domain +sockets. The processes can be executed either as standalone processes, +or be executed by QEMU. In both cases, the host backends the emulation +processes will provide are specified on its command line, as they would +be for QEMU. For example: + +:: + + disk-proc -blockdev driver=file,node-name=file0,filename=disk-file0 \ + -blockdev driver=qcow2,node-name=drive0,file=file0 + +would indicate process *disk-proc* uses a qcow2 emulated disk named +*file0* as its backend. + +Emulation processes may emulate more than one guest controller. A common +configuration might be to put all controllers of the same device class +(e.g., disk, network, etc.) in a single process, so that all backends of +the same type can be managed by a single QMP monitor. + +communication with QEMU +^^^^^^^^^^^^^^^^^^^^^^^ + +The first argument to the remote emulation process will be a Unix domain +socket that connects with the Proxy object. This is a required argument. + +:: + + disk-proc + +remote process QMP monitor +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Remote emulation processes can be monitored via QMP, similar to QEMU +itself. The QMP monitor socket is specified the same as for a QEMU +process: + +:: + + disk-proc -qmp unix:/tmp/disk-mon,server + +can be monitored over the UNIX socket path */tmp/disk-mon*. + +QEMU command line +~~~~~~~~~~~~~~~~~ + +Each remote device emulated in a remote process on the host is +represented as a *-device* of type *pci-proxy-dev*. A socket +sub-option to this option specifies the Unix socket that connects +to the remote process. An *id* sub-option is required, and it should +be the same id as used in the remote process. + +:: + + qemu-system-x86_64 ... -device pci-proxy-dev,id=lsi0,socket=3 + +can be used to add a device emulated in a remote process + + +QEMU management of remote processes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +QEMU is not aware of the type of type of the remote PCI device. It is +a pass through device as far as QEMU is concerned. + +communication with emulation process +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +primary channel +''''''''''''''' + +The primary channel (referred to as com in the code) is used to bootstrap +the remote process. It is also used to pass on device-agnostic commands +like reset. + +per-device channels +''''''''''''''''''' + +Each remote device communicates with QEMU using a dedicated communication +channel. The proxy object sets up this channel using the primary +channel during its initialization. + +QEMU device proxy objects +~~~~~~~~~~~~~~~~~~~~~~~~~ + +QEMU has an object model based on sub-classes inherited from the +"object" super-class. The sub-classes that are of interest here are the +"device" and "bus" sub-classes whose child sub-classes make up the +device tree of a QEMU emulated system. + +The proxy object model will use device proxy objects to replace the +device emulation code within the QEMU process. These objects will live +in the same place in the object and bus hierarchies as the objects they +replace. i.e., the proxy object for an LSI SCSI controller will be a +sub-class of the "pci-device" class, and will have the same PCI bus +parent and the same SCSI bus child objects as the LSI controller object +it replaces. + +It is worth noting that the same proxy object is used to mediate with +all types of remote PCI devices. + +object initialization +^^^^^^^^^^^^^^^^^^^^^ + +The Proxy device objects are initialized in the exact same manner in +which any other QEMU device would be initialized. + +In addition, the Proxy objects perform the following two tasks: +- Parses the "socket" sub option and connects to the remote process +using this channel +- Uses the "id" sub-option to connect to the emulated device on the +separate process + +class\_init +''''''''''' + +The ``class_init()`` method of a proxy object will, in general behave +similarly to the object it replaces, including setting any static +properties and methods needed by the proxy. + +instance\_init / realize +'''''''''''''''''''''''' + +The ``instance_init()`` and ``realize()`` functions would only need to +perform tasks related to being a proxy, such are registering its own +MMIO handlers, or creating a child bus that other proxy devices can be +attached to later. + +Other tasks will be device-specific. For example, PCI device objects +will initialize the PCI config space in order to make a valid PCI device +tree within the QEMU process. + +address space registration +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Most devices are driven by guest device driver accesses to IO addresses +or ports. The QEMU device emulation code uses QEMU's memory region +function calls (such as ``memory_region_init_io()``) to add callback +functions that QEMU will invoke when the guest accesses the device's +areas of the IO address space. When a guest driver does access the +device, the VM will exit HW virtualization mode and return to QEMU, +which will then lookup and execute the corresponding callback function. + +A proxy object would need to mirror the memory region calls the actual +device emulator would perform in its initialization code, but with its +own callbacks. When invoked by QEMU as a result of a guest IO operation, +they will forward the operation to the device emulation process. + +PCI config space +^^^^^^^^^^^^^^^^ + +PCI devices also have a configuration space that can be accessed by the +guest driver. Guest accesses to this space is not handled by the device +emulation object, but by its PCI parent object. Much of this space is +read-only, but certain registers (especially BAR and MSI-related ones) +need to be propagated to the emulation process. + +PCI parent proxy +'''''''''''''''' + +One way to propagate guest PCI config accesses is to create a +"pci-device-proxy" class that can serve as the parent of a PCI device +proxy object. This class's parent would be "pci-device" and it would +override the PCI parent's ``config_read()`` and ``config_write()`` +methods with ones that forward these operations to the emulation +program. + +interrupt receipt +^^^^^^^^^^^^^^^^^ + +A proxy for a device that generates interrupts will need to create a +socket to receive interrupt indications from the emulation process. An +incoming interrupt indication would then be sent up to its bus parent to +be injected into the guest. For example, a PCI device object may use +``pci_set_irq()``. + +live migration +^^^^^^^^^^^^^^ + +The proxy will register to save and restore any *vmstate* it needs over +a live migration event. The device proxy does not need to manage the +remote device's *vmstate*; that will be handled by the remote process +proxy (see below). + +QEMU remote device operation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Generic device operations, such as DMA, will be performed by the remote +process proxy by sending messages to the remote process. + +DMA operations +^^^^^^^^^^^^^^ + +DMA operations would be handled much like vhost applications do. One of +the initial messages sent to the emulation process is a guest memory +table. Each entry in this table consists of a file descriptor and size +that the emulation process can ``mmap()`` to directly access guest +memory, similar to ``vhost_user_set_mem_table()``. Note guest memory +must be backed by file descriptors, such as when QEMU is given the +*-mem-path* command line option. + +IOMMU operations +^^^^^^^^^^^^^^^^ + +When the emulated system includes an IOMMU, the remote process proxy in +QEMU will need to create a socket for IOMMU requests from the emulation +process. It will handle those requests with an +``address_space_get_iotlb_entry()`` call. In order to handle IOMMU +unmaps, the remote process proxy will also register as a listener on the +device's DMA address space. When an IOMMU memory region is created +within the DMA address space, an IOMMU notifier for unmaps will be added +to the memory region that will forward unmaps to the emulation process +over the IOMMU socket. + +device hot-plug via QMP +^^^^^^^^^^^^^^^^^^^^^^^ + +An QMP "device\_add" command can add a device emulated by a remote +process. It will also have "rid" option to the command, just as the +*-device* command line option does. The remote process may either be one +started at QEMU startup, or be one added by the "add-process" QMP +command described above. In either case, the remote process proxy will +forward the new device's JSON description to the corresponding emulation +process. + +live migration +^^^^^^^^^^^^^^ + +The remote process proxy will also register for live migration +notifications with ``vmstate_register()``. When called to save state, +the proxy will send the remote process a secondary socket file +descriptor to save the remote process's device *vmstate* over. The +incoming byte stream length and data will be saved as the proxy's +*vmstate*. When the proxy is resumed on its new host, this *vmstate* +will be extracted, and a secondary socket file descriptor will be sent +to the new remote process through which it receives the *vmstate* in +order to restore the devices there. + +device emulation in remote process +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The parts of QEMU that the emulation program will need include the +object model; the memory emulation objects; the device emulation objects +of the targeted device, and any dependent devices; and, the device's +backends. It will also need code to setup the machine environment, +handle requests from the QEMU process, and route machine-level requests +(such as interrupts or IOMMU mappings) back to the QEMU process. + +initialization +^^^^^^^^^^^^^^ + +The process initialization sequence will follow the same sequence +followed by QEMU. It will first initialize the backend objects, then +device emulation objects. The JSON descriptions sent by the QEMU process +will drive which objects need to be created. + +- address spaces + +Before the device objects are created, the initial address spaces and +memory regions must be configured with ``memory_map_init()``. This +creates a RAM memory region object (*system\_memory*) and an IO memory +region object (*system\_io*). + +- RAM + +RAM memory region creation will follow how ``pc_memory_init()`` creates +them, but must use ``memory_region_init_ram_from_fd()`` instead of +``memory_region_allocate_system_memory()``. The file descriptors needed +will be supplied by the guest memory table from above. Those RAM regions +would then be added to the *system\_memory* memory region with +``memory_region_add_subregion()``. + +- PCI + +IO initialization will be driven by the JSON descriptions sent from the +QEMU process. For a PCI device, a PCI bus will need to be created with +``pci_root_bus_new()``, and a PCI memory region will need to be created +and added to the *system\_memory* memory region with +``memory_region_add_subregion_overlap()``. The overlap version is +required for architectures where PCI memory overlaps with RAM memory. + +MMIO handling +^^^^^^^^^^^^^ + +The device emulation objects will use ``memory_region_init_io()`` to +install their MMIO handlers, and ``pci_register_bar()`` to associate +those handlers with a PCI BAR, as they do within QEMU currently. + +In order to use ``address_space_rw()`` in the emulation process to +handle MMIO requests from QEMU, the PCI physical addresses must be the +same in the QEMU process and the device emulation process. In order to +accomplish that, guest BAR programming must also be forwarded from QEMU +to the emulation process. + +interrupt injection +^^^^^^^^^^^^^^^^^^^ + +When device emulation wants to inject an interrupt into the VM, the +request climbs the device's bus object hierarchy until the point where a +bus object knows how to signal the interrupt to the guest. The details +depend on the type of interrupt being raised. + +- PCI pin interrupts + +On x86 systems, there is an emulated IOAPIC object attached to the root +PCI bus object, and the root PCI object forwards interrupt requests to +it. The IOAPIC object, in turn, calls the KVM driver to inject the +corresponding interrupt into the VM. The simplest way to handle this in +an emulation process would be to setup the root PCI bus driver (via +``pci_bus_irqs()``) to send a interrupt request back to the QEMU +process, and have the device proxy object reflect it up the PCI tree +there. + +- PCI MSI/X interrupts + +PCI MSI/X interrupts are implemented in HW as DMA writes to a +CPU-specific PCI address. In QEMU on x86, a KVM APIC object receives +these DMA writes, then calls into the KVM driver to inject the interrupt +into the VM. A simple emulation process implementation would be to send +the MSI DMA address from QEMU as a message at initialization, then +install an address space handler at that address which forwards the MSI +message back to QEMU. + +DMA operations +^^^^^^^^^^^^^^ + +When a emulation object wants to DMA into or out of guest memory, it +first must use dma\_memory\_map() to convert the DMA address to a local +virtual address. The emulation process memory region objects setup above +will be used to translate the DMA address to a local virtual address the +device emulation code can access. + +IOMMU +^^^^^ + +When an IOMMU is in use in QEMU, DMA translation uses IOMMU memory +regions to translate the DMA address to a guest physical address before +that physical address can be translated to a local virtual address. The +emulation process will need similar functionality. + +- IOTLB cache + +The emulation process will maintain a cache of recent IOMMU translations +(the IOTLB). When the translate() callback of an IOMMU memory region is +invoked, the IOTLB cache will be searched for an entry that will map the +DMA address to a guest PA. On a cache miss, a message will be sent back +to QEMU requesting the corresponding translation entry, which be both be +used to return a guest address and be added to the cache. + +- IOTLB purge + +The IOMMU emulation will also need to act on unmap requests from QEMU. +These happen when the guest IOMMU driver purges an entry from the +guest's translation table. + +live migration +^^^^^^^^^^^^^^ + +When a remote process receives a live migration indication from QEMU, it +will set up a channel using the received file descriptor with +``qio_channel_socket_new_fd()``. This channel will be used to create a +*QEMUfile* that can be passed to ``qemu_save_device_state()`` to send +the process's device state back to QEMU. This method will be reversed on +restore - the channel will be passed to ``qemu_loadvm_state()`` to +restore the device state. + +Accelerating device emulation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The messages that are required to be sent between QEMU and the emulation +process can add considerable latency to IO operations. The optimizations +described below attempt to ameliorate this effect by allowing the +emulation process to communicate directly with the kernel KVM driver. +The KVM file descriptors created would be passed to the emulation process +via initialization messages, much like the guest memory table is done. +#### MMIO acceleration + +Vhost user applications can receive guest virtio driver stores directly +from KVM. The issue with the eventfd mechanism used by vhost user is +that it does not pass any data with the event indication, so it cannot +handle guest loads or guest stores that carry store data. This concept +could, however, be expanded to cover more cases. + +The expanded idea would require a new type of KVM device: +*KVM\_DEV\_TYPE\_USER*. This device has two file descriptors: a master +descriptor that QEMU can use for configuration, and a slave descriptor +that the emulation process can use to receive MMIO notifications. QEMU +would create both descriptors using the KVM driver, and pass the slave +descriptor to the emulation process via an initialization message. + +data structures +^^^^^^^^^^^^^^^ + +- guest physical range + +The guest physical range structure describes the address range that a +device will respond to. It includes the base and length of the range, as +well as which bus the range resides on (e.g., on an x86machine, it can +specify whether the range refers to memory or IO addresses). + +A device can have multiple physical address ranges it responds to (e.g., +a PCI device can have multiple BARs), so the structure will also include +an enumerated identifier to specify which of the device's ranges is +being referred to. + ++--------+----------------------------+ +| Name | Description | ++========+============================+ +| addr | range base address | ++--------+----------------------------+ +| len | range length | ++--------+----------------------------+ +| bus | addr type (memory or IO) | ++--------+----------------------------+ +| id | range ID (e.g., PCI BAR) | ++--------+----------------------------+ + +- MMIO request structure + +This structure describes an MMIO operation. It includes which guest +physical range the MMIO was within, the offset within that range, the +MMIO type (e.g., load or store), and its length and data. It also +includes a sequence number that can be used to reply to the MMIO, and +the CPU that issued the MMIO. + ++----------+------------------------+ +| Name | Description | ++==========+========================+ +| rid | range MMIO is within | ++----------+------------------------+ +| offset | offset withing *rid* | ++----------+------------------------+ +| type | e.g., load or store | ++----------+------------------------+ +| len | MMIO length | ++----------+------------------------+ +| data | store data | ++----------+------------------------+ +| seq | sequence ID | ++----------+------------------------+ + +- MMIO request queues + +MMIO request queues are FIFO arrays of MMIO request structures. There +are two queues: pending queue is for MMIOs that haven't been read by the +emulation program, and the sent queue is for MMIOs that haven't been +acknowledged. The main use of the second queue is to validate MMIO +replies from the emulation program. + +- scoreboard + +Each CPU in the VM is emulated in QEMU by a separate thread, so multiple +MMIOs may be waiting to be consumed by an emulation program and multiple +threads may be waiting for MMIO replies. The scoreboard would contain a +wait queue and sequence number for the per-CPU threads, allowing them to +be individually woken when the MMIO reply is received from the emulation +program. It also tracks the number of posted MMIO stores to the device +that haven't been replied to, in order to satisfy the PCI constraint +that a load to a device will not complete until all previous stores to +that device have been completed. + +- device shadow memory + +Some MMIO loads do not have device side-effects. These MMIOs can be +completed without sending a MMIO request to the emulation program if the +emulation program shares a shadow image of the device's memory image +with the KVM driver. + +The emulation program will ask the KVM driver to allocate memory for the +shadow image, and will then use ``mmap()`` to directly access it. The +emulation program can control KVM access to the shadow image by sending +KVM an access map telling it which areas of the image have no +side-effects (and can be completed immediately), and which require a +MMIO request to the emulation program. The access map can also inform +the KVM drive which size accesses are allowed to the image. + +master descriptor +^^^^^^^^^^^^^^^^^ + +The master descriptor is used by QEMU to configure the new KVM device. +The descriptor would be returned by the KVM driver when QEMU issues a +*KVM\_CREATE\_DEVICE* ``ioctl()`` with a *KVM\_DEV\_TYPE\_USER* type. + +KVM\_DEV\_TYPE\_USER device ops + + +The *KVM\_DEV\_TYPE\_USER* operations vector will be registered by a +``kvm_register_device_ops()`` call when the KVM system in initialized by +``kvm_init()``. These device ops are called by the KVM driver when QEMU +executes certain ``ioctl()`` operations on its KVM file descriptor. They +include: + +- create + +This routine is called when QEMU issues a *KVM\_CREATE\_DEVICE* +``ioctl()`` on its per-VM file descriptor. It will allocate and +initialize a KVM user device specific data structure, and assign the +*kvm\_device* private field to it. + +- ioctl + +This routine is invoked when QEMU issues an ``ioctl()`` on the master +descriptor. The ``ioctl()`` commands supported are defined by the KVM +device type. *KVM\_DEV\_TYPE\_USER* ones will need several commands: + +*KVM\_DEV\_USER\_SLAVE\_FD* creates the slave file descriptor that will +be passed to the device emulation program. Only one slave can be created +by each master descriptor. The file operations performed by this +descriptor are described below. + +The *KVM\_DEV\_USER\_PA\_RANGE* command configures a guest physical +address range that the slave descriptor will receive MMIO notifications +for. The range is specified by a guest physical range structure +argument. For buses that assign addresses to devices dynamically, this +command can be executed while the guest is running, such as the case +when a guest changes a device's PCI BAR registers. + +*KVM\_DEV\_USER\_PA\_RANGE* will use ``kvm_io_bus_register_dev()`` to +register *kvm\_io\_device\_ops* callbacks to be invoked when the guest +performs a MMIO operation within the range. When a range is changed, +``kvm_io_bus_unregister_dev()`` is used to remove the previous +instantiation. + +*KVM\_DEV\_USER\_TIMEOUT* will configure a timeout value that specifies +how long KVM will wait for the emulation process to respond to a MMIO +indication. + +- destroy + +This routine is called when the VM instance is destroyed. It will need +to destroy the slave descriptor; and free any memory allocated by the +driver, as well as the *kvm\_device* structure itself. + +slave descriptor +^^^^^^^^^^^^^^^^ + +The slave descriptor will have its own file operations vector, which +responds to system calls on the descriptor performed by the device +emulation program. + +- read + +A read returns any pending MMIO requests from the KVM driver as MMIO +request structures. Multiple structures can be returned if there are +multiple MMIO operations pending. The MMIO requests are moved from the +pending queue to the sent queue, and if there are threads waiting for +space in the pending to add new MMIO operations, they will be woken +here. + +- write + +A write also consists of a set of MMIO requests. They are compared to +the MMIO requests in the sent queue. Matches are removed from the sent +queue, and any threads waiting for the reply are woken. If a store is +removed, then the number of posted stores in the per-CPU scoreboard is +decremented. When the number is zero, and a non side-effect load was +waiting for posted stores to complete, the load is continued. + +- ioctl + +There are several ioctl()s that can be performed on the slave +descriptor. + +A *KVM\_DEV\_USER\_SHADOW\_SIZE* ``ioctl()`` causes the KVM driver to +allocate memory for the shadow image. This memory can later be +``mmap()``\ ed by the emulation process to share the emulation's view of +device memory with the KVM driver. + +A *KVM\_DEV\_USER\_SHADOW\_CTRL* ``ioctl()`` controls access to the +shadow image. It will send the KVM driver a shadow control map, which +specifies which areas of the image can complete guest loads without +sending the load request to the emulation program. It will also specify +the size of load operations that are allowed. + +- poll + +An emulation program will use the ``poll()`` call with a *POLLIN* flag +to determine if there are MMIO requests waiting to be read. It will +return if the pending MMIO request queue is not empty. + +- mmap + +This call allows the emulation program to directly access the shadow +image allocated by the KVM driver. As device emulation updates device +memory, changes with no side-effects will be reflected in the shadow, +and the KVM driver can satisfy guest loads from the shadow image without +needing to wait for the emulation program. + +kvm\_io\_device ops +^^^^^^^^^^^^^^^^^^^ + +Each KVM per-CPU thread can handle MMIO operation on behalf of the guest +VM. KVM will use the MMIO's guest physical address to search for a +matching *kvm\_io\_device* to see if the MMIO can be handled by the KVM +driver instead of exiting back to QEMU. If a match is found, the +corresponding callback will be invoked. + +- read + +This callback is invoked when the guest performs a load to the device. +Loads with side-effects must be handled synchronously, with the KVM +driver putting the QEMU thread to sleep waiting for the emulation +process reply before re-starting the guest. Loads that do not have +side-effects may be optimized by satisfying them from the shadow image, +if there are no outstanding stores to the device by this CPU. PCI memory +ordering demands that a load cannot complete before all older stores to +the same device have been completed. + +- write + +Stores can be handled asynchronously unless the pending MMIO request +queue is full. In this case, the QEMU thread must sleep waiting for +space in the queue. Stores will increment the number of posted stores in +the per-CPU scoreboard, in order to implement the PCI ordering +constraint above. + +interrupt acceleration +^^^^^^^^^^^^^^^^^^^^^^ + +This performance optimization would work much like a vhost user +application does, where the QEMU process sets up *eventfds* that cause +the device's corresponding interrupt to be triggered by the KVM driver. +These irq file descriptors are sent to the emulation process at +initialization, and are used when the emulation code raises a device +interrupt. + +intx acceleration +''''''''''''''''' + +Traditional PCI pin interrupts are level based, so, in addition to an +irq file descriptor, a re-sampling file descriptor needs to be sent to +the emulation program. This second file descriptor allows multiple +devices sharing an irq to be notified when the interrupt has been +acknowledged by the guest, so they can re-trigger the interrupt if their +device has not de-asserted its interrupt. + +intx irq descriptor + + +The irq descriptors are created by the proxy object +``using event_notifier_init()`` to create the irq and re-sampling +*eventds*, and ``kvm_vm_ioctl(KVM_IRQFD)`` to bind them to an interrupt. +The interrupt route can be found with +``pci_device_route_intx_to_irq()``. + +intx routing changes + + +Intx routing can be changed when the guest programs the APIC the device +pin is connected to. The proxy object in QEMU will use +``pci_device_set_intx_routing_notifier()`` to be informed of any guest +changes to the route. This handler will broadly follow the VFIO +interrupt logic to change the route: de-assigning the existing irq +descriptor from its route, then assigning it the new route. (see +``vfio_intx_update()``) + +MSI/X acceleration +'''''''''''''''''' + +MSI/X interrupts are sent as DMA transactions to the host. The interrupt +data contains a vector that is programmed by the guest, A device may have +multiple MSI interrupts associated with it, so multiple irq descriptors +may need to be sent to the emulation program. + +MSI/X irq descriptor + + +This case will also follow the VFIO example. For each MSI/X interrupt, +an *eventfd* is created, a virtual interrupt is allocated by +``kvm_irqchip_add_msi_route()``, and the virtual interrupt is bound to +the eventfd with ``kvm_irqchip_add_irqfd_notifier()``. + +MSI/X config space changes + + +The guest may dynamically update several MSI-related tables in the +device's PCI config space. These include per-MSI interrupt enables and +vector data. Additionally, MSIX tables exist in device memory space, not +config space. Much like the BAR case above, the proxy object must look +at guest config space programming to keep the MSI interrupt state +consistent between QEMU and the emulation program. + +-------------- + +Disaggregated CPU emulation +--------------------------- + +After IO services have been disaggregated, a second phase would be to +separate a process to handle CPU instruction emulation from the main +QEMU control function. There are no object separation points for this +code, so the first task would be to create one. + +Host access controls +-------------------- + +Separating QEMU relies on the host OS's access restriction mechanisms to +enforce that the differing processes can only access the objects they +are entitled to. There are a couple types of mechanisms usually provided +by general purpose OSs. + +Discretionary access control +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Discretionary access control allows each user to control who can access +their files. In Linux, this type of control is usually too coarse for +QEMU separation, since it only provides three separate access controls: +one for the same user ID, the second for users IDs with the same group +ID, and the third for all other user IDs. Each device instance would +need a separate user ID to provide access control, which is likely to be +unwieldy for dynamically created VMs. + +Mandatory access control +~~~~~~~~~~~~~~~~~~~~~~~~ + +Mandatory access control allows the OS to add an additional set of +controls on top of discretionary access for the OS to control. It also +adds other attributes to processes and files such as types, roles, and +categories, and can establish rules for how processes and files can +interact. + +Type enforcement +^^^^^^^^^^^^^^^^ + +Type enforcement assigns a *type* attribute to processes and files, and +allows rules to be written on what operations a process with a given +type can perform on a file with a given type. QEMU separation could take +advantage of type enforcement by running the emulation processes with +different types, both from the main QEMU process, and from the emulation +processes of different classes of devices. + +For example, guest disk images and disk emulation processes could have +types separate from the main QEMU process and non-disk emulation +processes, and the type rules could prevent processes other than disk +emulation ones from accessing guest disk images. Similarly, network +emulation processes can have a type separate from the main QEMU process +and non-network emulation process, and only that type can access the +host tun/tap device used to provide guest networking. + +Category enforcement +^^^^^^^^^^^^^^^^^^^^ + +Category enforcement assigns a set of numbers within a given range to +the process or file. The process is granted access to the file if the +process's set is a superset of the file's set. This enforcement can be +used to separate multiple instances of devices in the same class. + +For example, if there are multiple disk devices provides to a guest, +each device emulation process could be provisioned with a separate +category. The different device emulation processes would not be able to +access each other's backing disk images. + +Alternatively, categories could be used in lieu of the type enforcement +scheme described above. In this scenario, different categories would be +used to prevent device emulation processes in different classes from +accessing resources assigned to other classes. diff --git a/docs/devel/multi-thread-tcg.rst b/docs/devel/multi-thread-tcg.rst index 21483870db..92a9eba13c 100644 --- a/docs/devel/multi-thread-tcg.rst +++ b/docs/devel/multi-thread-tcg.rst @@ -267,7 +267,7 @@ of view of external observers (e.g. another processor core). They can apply to any memory operations as well as just loads or stores. The Linux kernel has an excellent `write-up -` +`_ on the various forms of memory barrier and the guarantees they can provide. diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt index 69eede6c28..c1cb6f987d 100644 --- a/docs/devel/qapi-code-gen.txt +++ b/docs/devel/qapi-code-gen.txt @@ -147,9 +147,11 @@ prevent incomplete include files. === Pragma directives === Syntax: - PRAGMA = { 'pragma': { '*doc-required': BOOL, - '*returns-whitelist': [ STRING, ... ], - '*name-case-whitelist': [ STRING, ... ] } } + PRAGMA = { 'pragma': { + '*doc-required': BOOL, + '*command-name-exceptions': [ STRING, ... ], + '*command-returns-exceptions': [ STRING, ... ], + '*member-name-exceptions': [ STRING, ... ] } } The pragma directive lets you control optional generator behavior. @@ -159,11 +161,15 @@ pragma to different values in parts of the schema doesn't work. Pragma 'doc-required' takes a boolean value. If true, documentation is required. Default is false. -Pragma 'returns-whitelist' takes a list of command names that may +Pragma 'command-name-exceptions' takes a list of commands whose names +may contain '_' instead of '-'. Default is none. + +Pragma 'command-returns-exceptions' takes a list of commands that may violate the rules on permitted return types. Default is none. -Pragma 'name-case-whitelist' takes a list of names that may violate -rules on use of upper- vs. lower-case letters. Default is none. +Pragma 'member-name-exceptions' takes a list of types whose member +names may contain uppercase letters, and '_' instead of '-'. Default +is none. === Enumeration types === @@ -393,7 +399,7 @@ is identical on the wire to: { 'enum': 'Enum', 'data': ['one', 'two'] } { 'struct': 'Branch1', 'data': { 'data': 'str' } } { 'struct': 'Branch2', 'data': { 'data': 'int' } } - { 'union': 'Flat': 'base': { 'type': 'Enum' }, 'discriminator': 'type', + { 'union': 'Flat', 'base': { 'type': 'Enum' }, 'discriminator': 'type', 'data': { 'one': 'Branch1', 'two': 'Branch2' } } The optional 'if' member specifies a conditional. See "Configuring @@ -472,6 +478,7 @@ Syntax: '*gen': false, '*allow-oob': true, '*allow-preconfig': true, + '*coroutine': true, '*if': COND, '*features': FEATURES } @@ -489,9 +496,9 @@ are the arguments. A union type requires 'boxed': true. Member 'returns' defines the command's return type. It defaults to an empty struct type. It must normally be a complex type or an array of a complex type. To return anything else, the command must be listed -in pragma 'returns-whitelist'. If you do this, extending the command -to return additional information will be harder. Use of -'returns-whitelist' for new commands is strongly discouraged. +in pragma 'commands-returns-exceptions'. If you do this, extending +the command to return additional information will be harder. Use of +the pragma for new commands is strongly discouraged. A command's error responses are not specified in the QAPI schema. Error conditions should be documented in comments. @@ -589,6 +596,8 @@ When in doubt, do not implement OOB execution support. Member 'allow-preconfig' declares whether the command is available before the machine is built. It defaults to false. For example: + { 'enum': 'QMPCapability', + 'data': [ 'oob' ] } { 'command': 'qmp_capabilities', 'data': { '*enable': [ 'QMPCapability' ] }, 'allow-preconfig': true } @@ -596,6 +605,34 @@ before the machine is built. It defaults to false. For example: QMP is available before the machine is built only when QEMU was started with --preconfig. +Member 'coroutine' tells the QMP dispatcher whether the command handler +is safe to be run in a coroutine. It defaults to false. If it is true, +the command handler is called from coroutine context and may yield while +waiting for an external event (such as I/O completion) in order to avoid +blocking the guest and other background operations. + +Coroutine safety can be hard to prove, similar to thread safety. Common +pitfalls are: + +- The global mutex isn't held across qemu_coroutine_yield(), so + operations that used to assume that they execute atomically may have + to be more careful to protect against changes in the global state. + +- Nested event loops (AIO_WAIT_WHILE() etc.) are problematic in + coroutine context and can easily lead to deadlocks. They should be + replaced by yielding and reentering the coroutine when the condition + becomes false. + +Since the command handler may assume coroutine context, any callers +other than the QMP dispatcher must also call it in coroutine context. +In particular, HMP commands calling such a QMP command handler must be +marked .coroutine = true in hmp-commands.hx. + +It is an error to specify both 'coroutine': true and 'allow-oob': true +for a command. We don't currently have a use case for both together and +without a use case, it's not entirely clear what the semantics should +be. + The optional 'if' member specifies a conditional. See "Configuring the schema" below for more on this. @@ -724,8 +761,8 @@ Any name (command, event, type, member, or enum value) beginning with "x-" is marked experimental, and may be withdrawn or changed incompatibly in a future release. -Pragma 'name-case-whitelist' lets you violate the rules on use of -upper and lower case. Use for new code is strongly discouraged. +Pragmas 'command-name-exceptions' and 'member-name-exceptions' let you +violate naming rules. Use for new code is strongly discouraged. === Downstream extensions === @@ -795,7 +832,7 @@ Example: a struct with conditional feature 'allow-negative-numbers' { 'struct': 'TestType', 'data': { 'number': 'int' }, 'features': [ { 'name': 'allow-negative-numbers', - 'if' 'defined(IFCOND)' } ] } + 'if': 'defined(IFCOND)' } ] } Please note that you are responsible to ensure that the C code will compile with an arbitrary combination of conditions, since the @@ -824,21 +861,37 @@ See below for more on definition documentation. Free-form documentation may be used to provide additional text and structuring content. +==== Headings and subheadings ==== + +A free-form documentation comment containing a line which starts with +some '=' symbols and then a space defines a section heading: + + ## + # = This is a top level heading + # + # This is a free-form comment which will go under the + # top level heading. + ## + + ## + # == This is a second level heading + ## + +A heading line must be the first line of the documentation +comment block. + +Section headings must always be correctly nested, so you can only +define a third-level heading inside a second-level heading, and so on. ==== Documentation markup ==== -Comment text starting with '=' is a section title: +Documentation comments can use most rST markup. In particular, +a '::' literal block can be used for examples: - # = Section title - -Double the '=' for a subsection title: - - # == Subsection title - -'|' denotes examples: - - # | Text of the example, may span - # | multiple lines + # :: + # + # Text of the example, may span + # multiple lines '*' starts an itemized list: @@ -854,34 +907,33 @@ A decimal number followed by '.' starts a numbered list: # multiple lines # 2. Second item -The actual number doesn't matter. You could even use '*' instead of -'2.' for the second item. +The actual number doesn't matter. -Lists can't be nested. Blank lines are currently not supported within -lists. +Lists of either kind must be preceded and followed by a blank line. +If a list item's text spans multiple lines, then the second and +subsequent lines must be correctly indented to line up with the +first character of the first line. -Additional whitespace between the initial '#' and the comment text is -permitted. - -*foo* and _foo_ are for strong and emphasis styles respectively (they -do not work over multiple lines). @foo is used to reference a name in -the schema. +The usual '**strong**', '*emphasised*' and '``literal``' markup should +be used. If you need a single literal '*' you will need to +backslash-escape it. As an extension beyond the usual rST syntax, you +can also use '@foo' to reference a name in the schema; this is +rendered the same way as '``foo``'. Example: ## -# = Section -# == Subsection -# -# Some text foo with *strong* and _emphasis_ +# Some text foo with **bold** and *emphasis* # 1. with a list # 2. like that # # And some code: -# | $ echo foo -# | -> do this -# | <- get that # +# :: +# +# $ echo foo +# -> do this +# <- get that ## @@ -899,6 +951,22 @@ commands and events), member (for structs and unions), branch (for alternates), or value (for enums), and finally optional tagged sections. +Descriptions of arguments can span multiple lines. The description +text can start on the line following the '@argname:', in which case it +must not be indented at all. It can also start on the same line as +the '@argname:'. In this case if it spans multiple lines then second +and subsequent lines must be indented to line up with the first +character of the first line of the description: + +# @argone: +# This is a two line description +# in the first style. +# +# @argtwo: This is a two line description +# in the second style. + +The number of spaces between the ':' and the text is not significant. + FIXME: the parser accepts these things in almost any order. FIXME: union branches should be described, too. @@ -909,9 +977,26 @@ A tagged section starts with one of the following words: "Note:"/"Notes:", "Since:", "Example"/"Examples", "Returns:", "TODO:". The section ends with the start of a new section. +The text of a section can start on a new line, in +which case it must not be indented at all. It can also start +on the same line as the 'Note:', 'Returns:', etc tag. In this +case if it spans multiple lines then second and subsequent +lines must be indented to match the first, in the same way as +multiline argument descriptions. + A 'Since: x.y.z' tagged section lists the release that introduced the definition. +The text of a section can start on a new line, in +which case it must not be indented at all. It can also start +on the same line as the 'Note:', 'Returns:', etc tag. In this +case if it spans multiple lines then second and subsequent +lines must be indented to match the first. + +An 'Example' or 'Examples' section is automatically rendered +entirely as literal fixed-width text. In other sections, +the text is formatted, and rST markup can be used. + For example: ## @@ -1321,6 +1406,7 @@ Example: }; void qapi_free_UserDefOne(UserDefOne *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(UserDefOne, qapi_free_UserDefOne) struct UserDefOneList { UserDefOneList *next; @@ -1328,6 +1414,7 @@ Example: }; void qapi_free_UserDefOneList(UserDefOneList *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(UserDefOneList, qapi_free_UserDefOneList) struct q_obj_my_command_arg { UserDefOneList *arg1; diff --git a/docs/devel/qgraph.rst b/docs/devel/qgraph.rst new file mode 100644 index 0000000000..a9aff167ad --- /dev/null +++ b/docs/devel/qgraph.rst @@ -0,0 +1,568 @@ +.. _qgraph: + +======================================== +Qtest Driver Framework +======================================== + +In order to test a specific driver, plain libqos tests need to +take care of booting QEMU with the right machine and devices. +This makes each test "hardcoded" for a specific configuration, reducing +the possible coverage that it can reach. + +For example, the sdhci device is supported on both x86_64 and ARM boards, +therefore a generic sdhci test should test all machines and drivers that +support that device. +Using only libqos APIs, the test has to manually take care of +covering all the setups, and build the correct command line. + +This also introduces backward compability issues: if a device/driver command +line name is changed, all tests that use that will not work +properly anymore and need to be adjusted. + +The aim of qgraph is to create a graph of drivers, machines and tests such that +a test aimed to a certain driver does not have to care of +booting the right QEMU machine, pick the right device, build the command line +and so on. Instead, it only defines what type of device it is testing +(interface in qgraph terms) and the framework takes care of +covering all supported types of devices and machine architectures. + +Following the above example, an interface would be ``sdhci``, +so the sdhci-test should only care of linking its qgraph node with +that interface. In this way, if the command line of a sdhci driver +is changed, only the respective qgraph driver node has to be adjusted. + +The graph is composed by nodes that represent machines, drivers, tests +and edges that define the relationships between them (``CONSUMES``, ``PRODUCES``, and +``CONTAINS``). + + +Nodes +^^^^^^ + +A node can be of four types: + +- **QNODE_MACHINE**: for example ``arm/raspi2`` +- **QNODE_DRIVER**: for example ``generic-sdhci`` +- **QNODE_INTERFACE**: for example ``sdhci`` (interface for all ``-sdhci`` + drivers). + An interface is not explicitly created, it will be automatically + instantiated when a node consumes or produces it. + An interface is simply a struct that abstracts the various drivers + for the same type of device, and offers an API to the nodes that + use it ("consume" relation in qgraph terms) that is implemented/backed up by the drivers that implement it ("produce" relation in qgraph terms). +- **QNODE_TEST**: for example ``sdhci-test``. A test consumes an interface + and tests the functions provided by it. + +Notes for the nodes: + +- QNODE_MACHINE: each machine struct must have a ``QGuestAllocator`` and + implement ``get_driver()`` to return the allocator mapped to the interface + "memory". The function can also return ``NULL`` if the allocator + is not set. +- QNODE_DRIVER: driver names must be unique, and machines and nodes + planned to be "consumed" by other nodes must match QEMU + drivers name, otherwise they won't be discovered + +Edges +^^^^^^ + +An edge relation between two nodes (drivers or machines) `X` and `Y` can be: + +- ``X CONSUMES Y``: `Y` can be plugged into `X` +- ``X PRODUCES Y``: `X` provides the interface `Y` +- ``X CONTAINS Y``: `Y` is part of `X` component + +Execution steps +^^^^^^^^^^^^^^^ + +The basic framework steps are the following: + +- All nodes and edges are created in their respective + machine/driver/test files +- The framework starts QEMU and asks for a list of available devices + and machines (note that only machines and "consumed" nodes are mapped + 1:1 with QEMU devices) +- The framework walks the graph starting from the available machines and + performs a Depth First Search for tests +- Once a test is found, the path is walked again and all drivers are + allocated accordingly and the final interface is passed to the test +- The test is executed +- Unused objects are cleaned and the path discovery is continued + +Depending on the QEMU binary used, only some drivers/machines will be +available and only test that are reached by them will be executed. + +Creating a new driver and its interface +""""""""""""""""""""""""""""""""""""""""" + +Here we continue the ``sdhci`` use case, with the following scenario: + +- ``sdhci-test`` aims to test the ``read[q,w], writeq`` functions + offered by the ``sdhci`` drivers. +- The current ``sdhci`` device is supported by both ``x86_64/pc`` and ``ARM`` + (in this example we focus on the ``arm-raspi2``) machines. +- QEMU offers 2 types of drivers: ``QSDHCI_MemoryMapped`` for ``ARM`` and + ``QSDHCI_PCI`` for ``x86_64/pc``. Both implement the + ``read[q,w], writeq`` functions. + +In order to implement such scenario in qgraph, the test developer needs to: + +- Create the ``x86_64/pc`` machine node. This machine uses the + ``pci-bus`` architecture so it ``contains`` a PCI driver, + ``pci-bus-pc``. The actual path is + + ``x86_64/pc --contains--> 1440FX-pcihost --contains--> + pci-bus-pc --produces--> pci-bus``. + + For the sake of this example, + we do not focus on the PCI interface implementation. +- Create the ``sdhci-pci`` driver node, representing ``QSDHCI_PCI``. + The driver uses the PCI bus (and its API), + so it must ``consume`` the ``pci-bus`` generic interface (which abstracts + all the pci drivers available) + + ``sdhci-pci --consumes--> pci-bus`` +- Create an ``arm/raspi2`` machine node. This machine ``contains`` + a ``generic-sdhci`` memory mapped ``sdhci`` driver node, representing + ``QSDHCI_MemoryMapped``. + + ``arm/raspi2 --contains--> generic-sdhci`` +- Create the ``sdhci`` interface node. This interface offers the + functions that are shared by all ``sdhci`` devices. + The interface is produced by ``sdhci-pci`` and ``generic-sdhci``, + the available architecture-specific drivers. + + ``sdhci-pci --produces--> sdhci`` + + ``generic-sdhci --produces--> sdhci`` +- Create the ``sdhci-test`` test node. The test ``consumes`` the + ``sdhci`` interface, using its API. It doesn't need to look at + the supported machines or drivers. + + ``sdhci-test --consumes--> sdhci`` + +``arm-raspi2`` machine, simplified from +``tests/qtest/libqos/arm-raspi2-machine.c``:: + + #include "qgraph.h" + + struct QRaspi2Machine { + QOSGraphObject obj; + QGuestAllocator alloc; + QSDHCI_MemoryMapped sdhci; + }; + + static void *raspi2_get_driver(void *object, const char *interface) + { + QRaspi2Machine *machine = object; + if (!g_strcmp0(interface, "memory")) { + return &machine->alloc; + } + + fprintf(stderr, "%s not present in arm/raspi2\n", interface); + g_assert_not_reached(); + } + + static QOSGraphObject *raspi2_get_device(void *obj, + const char *device) + { + QRaspi2Machine *machine = obj; + if (!g_strcmp0(device, "generic-sdhci")) { + return &machine->sdhci.obj; + } + + fprintf(stderr, "%s not present in arm/raspi2\n", device); + g_assert_not_reached(); + } + + static void *qos_create_machine_arm_raspi2(QTestState *qts) + { + QRaspi2Machine *machine = g_new0(QRaspi2Machine, 1); + + alloc_init(&machine->alloc, ...); + + /* Get node(s) contained inside (CONTAINS) */ + machine->obj.get_device = raspi2_get_device; + + /* Get node(s) produced (PRODUCES) */ + machine->obj.get_driver = raspi2_get_driver; + + /* free the object */ + machine->obj.destructor = raspi2_destructor; + qos_init_sdhci_mm(&machine->sdhci, ...); + return &machine->obj; + } + + static void raspi2_register_nodes(void) + { + /* arm/raspi2 --contains--> generic-sdhci */ + qos_node_create_machine("arm/raspi2", + qos_create_machine_arm_raspi2); + qos_node_contains("arm/raspi2", "generic-sdhci", NULL); + } + + libqos_init(raspi2_register_nodes); + +``x86_64/pc`` machine, simplified from +``tests/qtest/libqos/x86_64_pc-machine.c``:: + + #include "qgraph.h" + + struct i440FX_pcihost { + QOSGraphObject obj; + QPCIBusPC pci; + }; + + struct QX86PCMachine { + QOSGraphObject obj; + QGuestAllocator alloc; + i440FX_pcihost bridge; + }; + + /* i440FX_pcihost */ + + static QOSGraphObject *i440FX_host_get_device(void *obj, + const char *device) + { + i440FX_pcihost *host = obj; + if (!g_strcmp0(device, "pci-bus-pc")) { + return &host->pci.obj; + } + fprintf(stderr, "%s not present in i440FX-pcihost\n", device); + g_assert_not_reached(); + } + + /* x86_64/pc machine */ + + static void *pc_get_driver(void *object, const char *interface) + { + QX86PCMachine *machine = object; + if (!g_strcmp0(interface, "memory")) { + return &machine->alloc; + } + + fprintf(stderr, "%s not present in x86_64/pc\n", interface); + g_assert_not_reached(); + } + + static QOSGraphObject *pc_get_device(void *obj, const char *device) + { + QX86PCMachine *machine = obj; + if (!g_strcmp0(device, "i440FX-pcihost")) { + return &machine->bridge.obj; + } + + fprintf(stderr, "%s not present in x86_64/pc\n", device); + g_assert_not_reached(); + } + + static void *qos_create_machine_pc(QTestState *qts) + { + QX86PCMachine *machine = g_new0(QX86PCMachine, 1); + + /* Get node(s) contained inside (CONTAINS) */ + machine->obj.get_device = pc_get_device; + + /* Get node(s) produced (PRODUCES) */ + machine->obj.get_driver = pc_get_driver; + + /* free the object */ + machine->obj.destructor = pc_destructor; + pc_alloc_init(&machine->alloc, qts, ALLOC_NO_FLAGS); + + /* Get node(s) contained inside (CONTAINS) */ + machine->bridge.obj.get_device = i440FX_host_get_device; + + return &machine->obj; + } + + static void pc_machine_register_nodes(void) + { + /* x86_64/pc --contains--> 1440FX-pcihost --contains--> + * pci-bus-pc [--produces--> pci-bus (in pci.h)] */ + qos_node_create_machine("x86_64/pc", qos_create_machine_pc); + qos_node_contains("x86_64/pc", "i440FX-pcihost", NULL); + + /* contained drivers don't need a constructor, + * they will be init by the parent */ + qos_node_create_driver("i440FX-pcihost", NULL); + qos_node_contains("i440FX-pcihost", "pci-bus-pc", NULL); + } + + libqos_init(pc_machine_register_nodes); + +``sdhci`` taken from ``tests/qtest/libqos/sdhci.c``:: + + /* Interface node, offers the sdhci API */ + struct QSDHCI { + uint16_t (*readw)(QSDHCI *s, uint32_t reg); + uint64_t (*readq)(QSDHCI *s, uint32_t reg); + void (*writeq)(QSDHCI *s, uint32_t reg, uint64_t val); + /* other fields */ + }; + + /* Memory Mapped implementation of QSDHCI */ + struct QSDHCI_MemoryMapped { + QOSGraphObject obj; + QSDHCI sdhci; + /* other driver-specific fields */ + }; + + /* PCI implementation of QSDHCI */ + struct QSDHCI_PCI { + QOSGraphObject obj; + QSDHCI sdhci; + /* other driver-specific fields */ + }; + + /* Memory mapped implementation of QSDHCI */ + + static void *sdhci_mm_get_driver(void *obj, const char *interface) + { + QSDHCI_MemoryMapped *smm = obj; + if (!g_strcmp0(interface, "sdhci")) { + return &smm->sdhci; + } + fprintf(stderr, "%s not present in generic-sdhci\n", interface); + g_assert_not_reached(); + } + + void qos_init_sdhci_mm(QSDHCI_MemoryMapped *sdhci, QTestState *qts, + uint32_t addr, QSDHCIProperties *common) + { + /* Get node contained inside (CONTAINS) */ + sdhci->obj.get_driver = sdhci_mm_get_driver; + + /* SDHCI interface API */ + sdhci->sdhci.readw = sdhci_mm_readw; + sdhci->sdhci.readq = sdhci_mm_readq; + sdhci->sdhci.writeq = sdhci_mm_writeq; + sdhci->qts = qts; + } + + /* PCI implementation of QSDHCI */ + + static void *sdhci_pci_get_driver(void *object, + const char *interface) + { + QSDHCI_PCI *spci = object; + if (!g_strcmp0(interface, "sdhci")) { + return &spci->sdhci; + } + + fprintf(stderr, "%s not present in sdhci-pci\n", interface); + g_assert_not_reached(); + } + + static void *sdhci_pci_create(void *pci_bus, + QGuestAllocator *alloc, + void *addr) + { + QSDHCI_PCI *spci = g_new0(QSDHCI_PCI, 1); + QPCIBus *bus = pci_bus; + uint64_t barsize; + + qpci_device_init(&spci->dev, bus, addr); + + /* SDHCI interface API */ + spci->sdhci.readw = sdhci_pci_readw; + spci->sdhci.readq = sdhci_pci_readq; + spci->sdhci.writeq = sdhci_pci_writeq; + + /* Get node(s) produced (PRODUCES) */ + spci->obj.get_driver = sdhci_pci_get_driver; + + spci->obj.start_hw = sdhci_pci_start_hw; + spci->obj.destructor = sdhci_destructor; + return &spci->obj; + } + + static void qsdhci_register_nodes(void) + { + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + + /* generic-sdhci */ + /* generic-sdhci --produces--> sdhci */ + qos_node_create_driver("generic-sdhci", NULL); + qos_node_produces("generic-sdhci", "sdhci"); + + /* sdhci-pci */ + /* sdhci-pci --produces--> sdhci + * sdhci-pci --consumes--> pci-bus */ + qos_node_create_driver("sdhci-pci", sdhci_pci_create); + qos_node_produces("sdhci-pci", "sdhci"); + qos_node_consumes("sdhci-pci", "pci-bus", &opts); + } + + libqos_init(qsdhci_register_nodes); + +In the above example, all possible types of relations are created:: + + x86_64/pc --contains--> 1440FX-pcihost --contains--> pci-bus-pc + | + sdhci-pci --consumes--> pci-bus <--produces--+ + | + +--produces--+ + | + v + sdhci + ^ + | + +--produces-- + + | + arm/raspi2 --contains--> generic-sdhci + +or inverting the consumes edge in consumed_by:: + + x86_64/pc --contains--> 1440FX-pcihost --contains--> pci-bus-pc + | + sdhci-pci <--consumed by-- pci-bus <--produces--+ + | + +--produces--+ + | + v + sdhci + ^ + | + +--produces-- + + | + arm/raspi2 --contains--> generic-sdhci + +Adding a new test +""""""""""""""""" + +Given the above setup, adding a new test is very simple. +``sdhci-test``, taken from ``tests/qtest/sdhci-test.c``:: + + static void check_capab_sdma(QSDHCI *s, bool supported) + { + uint64_t capab, capab_sdma; + + capab = s->readq(s, SDHC_CAPAB); + capab_sdma = FIELD_EX64(capab, SDHC_CAPAB, SDMA); + g_assert_cmpuint(capab_sdma, ==, supported); + } + + static void test_registers(void *obj, void *data, + QGuestAllocator *alloc) + { + QSDHCI *s = obj; + + /* example test */ + check_capab_sdma(s, s->props.capab.sdma); + } + + static void register_sdhci_test(void) + { + /* sdhci-test --consumes--> sdhci */ + qos_add_test("registers", "sdhci", test_registers, NULL); + } + + libqos_init(register_sdhci_test); + +Here a new test is created, consuming ``sdhci`` interface node +and creating a valid path from both machines to a test. +Final graph will be like this:: + + x86_64/pc --contains--> 1440FX-pcihost --contains--> pci-bus-pc + | + sdhci-pci --consumes--> pci-bus <--produces--+ + | + +--produces--+ + | + v + sdhci <--consumes-- sdhci-test + ^ + | + +--produces-- + + | + arm/raspi2 --contains--> generic-sdhci + +or inverting the consumes edge in consumed_by:: + + x86_64/pc --contains--> 1440FX-pcihost --contains--> pci-bus-pc + | + sdhci-pci <--consumed by-- pci-bus <--produces--+ + | + +--produces--+ + | + v + sdhci --consumed by--> sdhci-test + ^ + | + +--produces-- + + | + arm/raspi2 --contains--> generic-sdhci + +Assuming there the binary is +``QTEST_QEMU_BINARY=./qemu-system-x86_64`` +a valid test path will be: +``/x86_64/pc/1440FX-pcihost/pci-bus-pc/pci-bus/sdhci-pc/sdhci/sdhci-test`` + +and for the binary ``QTEST_QEMU_BINARY=./qemu-system-arm``: + +``/arm/raspi2/generic-sdhci/sdhci/sdhci-test`` + +Additional examples are also in ``test-qgraph.c`` + +Command line: +"""""""""""""" + +Command line is built by using node names and optional arguments +passed by the user when building the edges. + +There are three types of command line arguments: + +- ``in node`` : created from the node name. For example, machines will + have ``-M `` to its command line, while devices + ``-device ``. It is automatically done by the framework. +- ``after node`` : added as additional argument to the node name. + This argument is added optionally when creating edges, + by setting the parameter ``after_cmd_line`` and + ``extra_edge_opts`` in ``QOSGraphEdgeOptions``. + The framework automatically adds + a comma before ``extra_edge_opts``, + because it is going to add attributes + after the destination node pointed by + the edge containing these options, and automatically + adds a space before ``after_cmd_line``, because it + adds an additional device, not an attribute. +- ``before node`` : added as additional argument to the node name. + This argument is added optionally when creating edges, + by setting the parameter ``before_cmd_line`` in + ``QOSGraphEdgeOptions``. This attribute + is going to add attributes before the destination node + pointed by the edge containing these options. It is + helpful to commands that are not node-representable, + such as ``-fdsev`` or ``-netdev``. + +While adding command line in edges is always used, not all nodes names are +used in every path walk: this is because the contained or produced ones +are already added by QEMU, so only nodes that "consumes" will be used to +build the command line. Also, nodes that will have ``{ "abstract" : true }`` +as QMP attribute will loose their command line, since they are not proper +devices to be added in QEMU. + +Example:: + + QOSGraphEdgeOptions opts = { + .before_cmd_line = "-drive id=drv0,if=none,file=null-co://," + "file.read-zeroes=on,format=raw", + .after_cmd_line = "-device scsi-hd,bus=vs0.0,drive=drv0", + + opts.extra_device_opts = "id=vs0"; + }; + + qos_node_create_driver("virtio-scsi-device", + virtio_scsi_device_create); + qos_node_consumes("virtio-scsi-device", "virtio-bus", &opts); + +Will produce the following command line: +``-drive id=drv0,if=none,file=null-co://, -device virtio-scsi-device,id=vs0 -device scsi-hd,bus=vs0.0,drive=drv0`` + +Qgraph API reference +^^^^^^^^^^^^^^^^^^^^ + +.. kernel-doc:: tests/qtest/libqos/qgraph.h diff --git a/docs/devel/qom.rst b/docs/devel/qom.rst new file mode 100644 index 0000000000..42d0dc4f4d --- /dev/null +++ b/docs/devel/qom.rst @@ -0,0 +1,381 @@ +=========================== +The QEMU Object Model (QOM) +=========================== + +.. highlight:: c + +The QEMU Object Model provides a framework for registering user creatable +types and instantiating objects from those types. QOM provides the following +features: + +- System for dynamically registering types +- Support for single-inheritance of types +- Multiple inheritance of stateless interfaces + +.. code-block:: c + :caption: Creating a minimal type + + #include "qdev.h" + + #define TYPE_MY_DEVICE "my-device" + + // No new virtual functions: we can reuse the typedef for the + // superclass. + typedef DeviceClass MyDeviceClass; + typedef struct MyDevice + { + DeviceState parent; + + int reg0, reg1, reg2; + } MyDevice; + + static const TypeInfo my_device_info = { + .name = TYPE_MY_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(MyDevice), + }; + + static void my_device_register_types(void) + { + type_register_static(&my_device_info); + } + + type_init(my_device_register_types) + +In the above example, we create a simple type that is described by #TypeInfo. +#TypeInfo describes information about the type including what it inherits +from, the instance and class size, and constructor/destructor hooks. + +Alternatively several static types could be registered using helper macro +DEFINE_TYPES() + +.. code-block:: c + + static const TypeInfo device_types_info[] = { + { + .name = TYPE_MY_DEVICE_A, + .parent = TYPE_DEVICE, + .instance_size = sizeof(MyDeviceA), + }, + { + .name = TYPE_MY_DEVICE_B, + .parent = TYPE_DEVICE, + .instance_size = sizeof(MyDeviceB), + }, + }; + + DEFINE_TYPES(device_types_info) + +Every type has an #ObjectClass associated with it. #ObjectClass derivatives +are instantiated dynamically but there is only ever one instance for any +given type. The #ObjectClass typically holds a table of function pointers +for the virtual methods implemented by this type. + +Using object_new(), a new #Object derivative will be instantiated. You can +cast an #Object to a subclass (or base-class) type using +object_dynamic_cast(). You typically want to define macro wrappers around +OBJECT_CHECK() and OBJECT_CLASS_CHECK() to make it easier to convert to a +specific type: + +.. code-block:: c + :caption: Typecasting macros + + #define MY_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(MyDeviceClass, obj, TYPE_MY_DEVICE) + #define MY_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(MyDeviceClass, klass, TYPE_MY_DEVICE) + #define MY_DEVICE(obj) \ + OBJECT_CHECK(MyDevice, obj, TYPE_MY_DEVICE) + +Class Initialization +==================== + +Before an object is initialized, the class for the object must be +initialized. There is only one class object for all instance objects +that is created lazily. + +Classes are initialized by first initializing any parent classes (if +necessary). After the parent class object has initialized, it will be +copied into the current class object and any additional storage in the +class object is zero filled. + +The effect of this is that classes automatically inherit any virtual +function pointers that the parent class has already initialized. All +other fields will be zero filled. + +Once all of the parent classes have been initialized, #TypeInfo::class_init +is called to let the class being instantiated provide default initialize for +its virtual functions. Here is how the above example might be modified +to introduce an overridden virtual function: + +.. code-block:: c + :caption: Overriding a virtual function + + #include "qdev.h" + + void my_device_class_init(ObjectClass *klass, void *class_data) + { + DeviceClass *dc = DEVICE_CLASS(klass); + dc->reset = my_device_reset; + } + + static const TypeInfo my_device_info = { + .name = TYPE_MY_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(MyDevice), + .class_init = my_device_class_init, + }; + +Introducing new virtual methods requires a class to define its own +struct and to add a .class_size member to the #TypeInfo. Each method +will also have a wrapper function to call it easily: + +.. code-block:: c + :caption: Defining an abstract class + + #include "qdev.h" + + typedef struct MyDeviceClass + { + DeviceClass parent; + + void (*frobnicate) (MyDevice *obj); + } MyDeviceClass; + + static const TypeInfo my_device_info = { + .name = TYPE_MY_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(MyDevice), + .abstract = true, // or set a default in my_device_class_init + .class_size = sizeof(MyDeviceClass), + }; + + void my_device_frobnicate(MyDevice *obj) + { + MyDeviceClass *klass = MY_DEVICE_GET_CLASS(obj); + + klass->frobnicate(obj); + } + +Interfaces +========== + +Interfaces allow a limited form of multiple inheritance. Instances are +similar to normal types except for the fact that are only defined by +their classes and never carry any state. As a consequence, a pointer to +an interface instance should always be of incomplete type in order to be +sure it cannot be dereferenced. That is, you should define the +'typedef struct SomethingIf SomethingIf' so that you can pass around +``SomethingIf *si`` arguments, but not define a ``struct SomethingIf { ... }``. +The only things you can validly do with a ``SomethingIf *`` are to pass it as +an argument to a method on its corresponding SomethingIfClass, or to +dynamically cast it to an object that implements the interface. + +Methods +======= + +A *method* is a function within the namespace scope of +a class. It usually operates on the object instance by passing it as a +strongly-typed first argument. +If it does not operate on an object instance, it is dubbed +*class method*. + +Methods cannot be overloaded. That is, the #ObjectClass and method name +uniquely identity the function to be called; the signature does not vary +except for trailing varargs. + +Methods are always *virtual*. Overriding a method in +#TypeInfo.class_init of a subclass leads to any user of the class obtained +via OBJECT_GET_CLASS() accessing the overridden function. +The original function is not automatically invoked. It is the responsibility +of the overriding class to determine whether and when to invoke the method +being overridden. + +To invoke the method being overridden, the preferred solution is to store +the original value in the overriding class before overriding the method. +This corresponds to ``{super,base}.method(...)`` in Java and C# +respectively; this frees the overriding class from hardcoding its parent +class, which someone might choose to change at some point. + +.. code-block:: c + :caption: Overriding a virtual method + + typedef struct MyState MyState; + + typedef void (*MyDoSomething)(MyState *obj); + + typedef struct MyClass { + ObjectClass parent_class; + + MyDoSomething do_something; + } MyClass; + + static void my_do_something(MyState *obj) + { + // do something + } + + static void my_class_init(ObjectClass *oc, void *data) + { + MyClass *mc = MY_CLASS(oc); + + mc->do_something = my_do_something; + } + + static const TypeInfo my_type_info = { + .name = TYPE_MY, + .parent = TYPE_OBJECT, + .instance_size = sizeof(MyState), + .class_size = sizeof(MyClass), + .class_init = my_class_init, + }; + + typedef struct DerivedClass { + MyClass parent_class; + + MyDoSomething parent_do_something; + } DerivedClass; + + static void derived_do_something(MyState *obj) + { + DerivedClass *dc = DERIVED_GET_CLASS(obj); + + // do something here + dc->parent_do_something(obj); + // do something else here + } + + static void derived_class_init(ObjectClass *oc, void *data) + { + MyClass *mc = MY_CLASS(oc); + DerivedClass *dc = DERIVED_CLASS(oc); + + dc->parent_do_something = mc->do_something; + mc->do_something = derived_do_something; + } + + static const TypeInfo derived_type_info = { + .name = TYPE_DERIVED, + .parent = TYPE_MY, + .class_size = sizeof(DerivedClass), + .class_init = derived_class_init, + }; + +Alternatively, object_class_by_name() can be used to obtain the class and +its non-overridden methods for a specific type. This would correspond to +``MyClass::method(...)`` in C++. + +The first example of such a QOM method was #CPUClass.reset, +another example is #DeviceClass.realize. + +Standard type declaration and definition macros +=============================================== + +A lot of the code outlined above follows a standard pattern and naming +convention. To reduce the amount of boilerplate code that needs to be +written for a new type there are two sets of macros to generate the +common parts in a standard format. + +A type is declared using the OBJECT_DECLARE macro family. In types +which do not require any virtual functions in the class, the +OBJECT_DECLARE_SIMPLE_TYPE macro is suitable, and is commonly placed +in the header file: + +.. code-block:: c + :caption: Declaring a simple type + + OBJECT_DECLARE_SIMPLE_TYPE(MyDevice, my_device, + MY_DEVICE, DEVICE) + +This is equivalent to the following: + +.. code-block:: c + :caption: Expansion from declaring a simple type + + typedef struct MyDevice MyDevice; + typedef struct MyDeviceClass MyDeviceClass; + + G_DEFINE_AUTOPTR_CLEANUP_FUNC(MyDeviceClass, object_unref) + + #define MY_DEVICE_GET_CLASS(void *obj) \ + OBJECT_GET_CLASS(MyDeviceClass, obj, TYPE_MY_DEVICE) + #define MY_DEVICE_CLASS(void *klass) \ + OBJECT_CLASS_CHECK(MyDeviceClass, klass, TYPE_MY_DEVICE) + #define MY_DEVICE(void *obj) + OBJECT_CHECK(MyDevice, obj, TYPE_MY_DEVICE) + + struct MyDeviceClass { + DeviceClass parent_class; + }; + +The 'struct MyDevice' needs to be declared separately. +If the type requires virtual functions to be declared in the class +struct, then the alternative OBJECT_DECLARE_TYPE() macro can be +used. This does the same as OBJECT_DECLARE_SIMPLE_TYPE(), but without +the 'struct MyDeviceClass' definition. + +To implement the type, the OBJECT_DEFINE macro family is available. +In the simple case the OBJECT_DEFINE_TYPE macro is suitable: + +.. code-block:: c + :caption: Defining a simple type + + OBJECT_DEFINE_TYPE(MyDevice, my_device, MY_DEVICE, DEVICE) + +This is equivalent to the following: + +.. code-block:: c + :caption: Expansion from defining a simple type + + static void my_device_finalize(Object *obj); + static void my_device_class_init(ObjectClass *oc, void *data); + static void my_device_init(Object *obj); + + static const TypeInfo my_device_info = { + .parent = TYPE_DEVICE, + .name = TYPE_MY_DEVICE, + .instance_size = sizeof(MyDevice), + .instance_init = my_device_init, + .instance_finalize = my_device_finalize, + .class_size = sizeof(MyDeviceClass), + .class_init = my_device_class_init, + }; + + static void + my_device_register_types(void) + { + type_register_static(&my_device_info); + } + type_init(my_device_register_types); + +This is sufficient to get the type registered with the type +system, and the three standard methods now need to be implemented +along with any other logic required for the type. + +If the type needs to implement one or more interfaces, then the +OBJECT_DEFINE_TYPE_WITH_INTERFACES() macro can be used instead. +This accepts an array of interface type names. + +.. code-block:: c + :caption: Defining a simple type implementing interfaces + + OBJECT_DEFINE_TYPE_WITH_INTERFACES(MyDevice, my_device, + MY_DEVICE, DEVICE, + { TYPE_USER_CREATABLE }, + { NULL }) + +If the type is not intended to be instantiated, then then +the OBJECT_DEFINE_ABSTRACT_TYPE() macro can be used instead: + +.. code-block:: c + :caption: Defining a simple abstract type + + OBJECT_DEFINE_ABSTRACT_TYPE(MyDevice, my_device, + MY_DEVICE, DEVICE) + + + +API Reference +------------- + +.. kernel-doc:: include/qom/object.h diff --git a/docs/devel/qtest.rst b/docs/devel/qtest.rst new file mode 100644 index 0000000000..c3dceb6c8a --- /dev/null +++ b/docs/devel/qtest.rst @@ -0,0 +1,92 @@ +======================================== +QTest Device Emulation Testing Framework +======================================== + +.. toctree:: + :hidden: + + qgraph + +QTest is a device emulation testing framework. It can be very useful to test +device models; it could also control certain aspects of QEMU (such as virtual +clock stepping), with a special purpose "qtest" protocol. Refer to +:ref:`qtest-protocol` for more details of the protocol. + +QTest cases can be executed with + +.. code:: + + make check-qtest + +The QTest library is implemented by ``tests/qtest/libqtest.c`` and the API is +defined in ``tests/qtest/libqtest.h``. + +Consider adding a new QTest case when you are introducing a new virtual +hardware, or extending one if you are adding functionalities to an existing +virtual device. + +On top of libqtest, a higher level library, ``libqos``, was created to +encapsulate common tasks of device drivers, such as memory management and +communicating with system buses or devices. Many virtual device tests use +libqos instead of directly calling into libqtest. +Libqos also offers the Qgraph API to increase each test coverage and +automate QEMU command line arguments and devices setup. +Refer to :ref:`qgraph` for Qgraph explanation and API. + +Steps to add a new QTest case are: + +1. Create a new source file for the test. (More than one file can be added as + necessary.) For example, ``tests/qtest/foo-test.c``. + +2. Write the test code with the glib and libqtest/libqos API. See also existing + tests and the library headers for reference. + +3. Register the new test in ``tests/qtest/meson.build``. Add the test + executable name to an appropriate ``qtests_*`` variable. There is + one variable per architecture, plus ``qtests_generic`` for tests + that can be run for all architectures. For example:: + + qtests_generic = [ + ... + 'foo-test', + ... + ] + +4. If the test has more than one source file or needs to be linked with any + dependency other than ``qemuutil`` and ``qos``, list them in the ``qtests`` + dictionary. For example a test that needs to use the ``QIO`` library + will have an entry like:: + + { + ... + 'foo-test': [io], + ... + } + +Debugging a QTest failure is slightly harder than the unit test because the +tests look up QEMU program names in the environment variables, such as +``QTEST_QEMU_BINARY`` and ``QTEST_QEMU_IMG``, and also because it is not easy +to attach gdb to the QEMU process spawned from the test. But manual invoking +and using gdb on the test is still simple to do: find out the actual command +from the output of + +.. code:: + + make check-qtest V=1 + +which you can run manually. + + +.. _qtest-protocol: + +QTest Protocol +-------------- + +.. kernel-doc:: softmmu/qtest.c + :doc: QTest Protocol + + +libqtest API reference +---------------------- + +.. kernel-doc:: tests/qtest/libqos/libqtest.h diff --git a/docs/devel/rcu.txt b/docs/devel/rcu.txt index 0ce15ba198..2e6cc607a1 100644 --- a/docs/devel/rcu.txt +++ b/docs/devel/rcu.txt @@ -130,13 +130,13 @@ The core RCU API is small: g_free_rcu(&foo, rcu); - typeof(*p) atomic_rcu_read(p); + typeof(*p) qatomic_rcu_read(p); - atomic_rcu_read() is similar to atomic_load_acquire(), but it makes + qatomic_rcu_read() is similar to qatomic_load_acquire(), but it makes some assumptions on the code that calls it. This allows a more optimized implementation. - atomic_rcu_read assumes that whenever a single RCU critical + qatomic_rcu_read assumes that whenever a single RCU critical section reads multiple shared data, these reads are either data-dependent or need no ordering. This is almost always the case when using RCU, because read-side critical sections typically @@ -144,7 +144,7 @@ The core RCU API is small: every update) until reaching a data structure of interest, and then read from there. - RCU read-side critical sections must use atomic_rcu_read() to + RCU read-side critical sections must use qatomic_rcu_read() to read data, unless concurrent writes are prevented by another synchronization mechanism. @@ -152,18 +152,18 @@ The core RCU API is small: data structure in a single direction, opposite to the direction in which the updater initializes it. - void atomic_rcu_set(p, typeof(*p) v); + void qatomic_rcu_set(p, typeof(*p) v); - atomic_rcu_set() is similar to atomic_store_release(), though it also + qatomic_rcu_set() is similar to qatomic_store_release(), though it also makes assumptions on the code that calls it in order to allow a more optimized implementation. - In particular, atomic_rcu_set() suffices for synchronization + In particular, qatomic_rcu_set() suffices for synchronization with readers, if the updater never mutates a field within a data item that is already accessible to readers. This is the case when initializing a new copy of the RCU-protected data structure; just ensure that initialization of *p is carried out - before atomic_rcu_set() makes the data item visible to readers. + before qatomic_rcu_set() makes the data item visible to readers. If this rule is observed, writes will happen in the opposite order as reads in the RCU read-side critical sections (or if there is just one update), and there will be no need for other @@ -212,7 +212,7 @@ DIFFERENCES WITH LINUX programming; not allowing this would prevent upgrading an RCU read-side critical section to become an updater. -- atomic_rcu_read and atomic_rcu_set replace rcu_dereference and +- qatomic_rcu_read and qatomic_rcu_set replace rcu_dereference and rcu_assign_pointer. They take a _pointer_ to the variable being accessed. - call_rcu is a macro that has an extra argument (the name of the first @@ -257,7 +257,7 @@ may be used as a restricted reference-counting mechanism. For example, consider the following code fragment: rcu_read_lock(); - p = atomic_rcu_read(&foo); + p = qatomic_rcu_read(&foo); /* do something with p. */ rcu_read_unlock(); @@ -268,7 +268,7 @@ The write side looks simply like this (with appropriate locking): qemu_mutex_lock(&foo_mutex); old = foo; - atomic_rcu_set(&foo, new); + qatomic_rcu_set(&foo, new); qemu_mutex_unlock(&foo_mutex); synchronize_rcu(); free(old); @@ -277,7 +277,7 @@ If the processing cannot be done purely within the critical section, it is possible to combine this idiom with a "real" reference count: rcu_read_lock(); - p = atomic_rcu_read(&foo); + p = qatomic_rcu_read(&foo); foo_ref(p); rcu_read_unlock(); /* do something with p. */ @@ -287,7 +287,7 @@ The write side can be like this: qemu_mutex_lock(&foo_mutex); old = foo; - atomic_rcu_set(&foo, new); + qatomic_rcu_set(&foo, new); qemu_mutex_unlock(&foo_mutex); synchronize_rcu(); foo_unref(old); @@ -296,7 +296,7 @@ or with call_rcu: qemu_mutex_lock(&foo_mutex); old = foo; - atomic_rcu_set(&foo, new); + qatomic_rcu_set(&foo, new); qemu_mutex_unlock(&foo_mutex); call_rcu(foo_unref, old, rcu); @@ -307,7 +307,7 @@ last reference may be dropped on the read side. Hence you can use call_rcu() instead: foo_unref(struct foo *p) { - if (atomic_fetch_dec(&p->refcount) == 1) { + if (qatomic_fetch_dec(&p->refcount) == 1) { call_rcu(foo_destroy, p, rcu); } } @@ -375,7 +375,7 @@ Instead, we store the size of the array with the array itself: read side: rcu_read_lock(); - struct arr *array = atomic_rcu_read(&global_array); + struct arr *array = qatomic_rcu_read(&global_array); x = i < array->size ? array->data[i] : -1; rcu_read_unlock(); return x; @@ -392,7 +392,7 @@ Instead, we store the size of the array with the array itself: /* Removal phase. */ old_array = global_array; - atomic_rcu_set(&new_array->data, new_array); + qatomic_rcu_set(&global_array, new_array); synchronize_rcu(); /* Reclamation phase. */ diff --git a/CODING_STYLE.rst b/docs/devel/style.rst similarity index 85% rename from CODING_STYLE.rst rename to docs/devel/style.rst index 427699e0e4..260e3263fa 100644 --- a/CODING_STYLE.rst +++ b/docs/devel/style.rst @@ -85,8 +85,13 @@ Line width Lines should be 80 characters; try not to make them longer. Sometimes it is hard to do, especially when dealing with QEMU subsystems -that use long function or symbol names. Even in that case, do not make -lines much longer than 80 characters. +that use long function or symbol names. If wrapping the line at 80 columns +is obviously less readable and more awkward, prefer not to wrap it; better +to have an 85 character line than one which is awkwardly wrapped. + +Even in that case, try not to make lines much longer than 80 characters. +(The checkpatch script will warn at 100 characters, but this is intended +as a guard against obviously-overlength lines, not a target.) Rationale: @@ -109,8 +114,41 @@ names are lower_case_with_underscores_ending_with_a_t, like the POSIX uint64_t and family. Note that this last convention contradicts POSIX and is therefore likely to be changed. -When wrapping standard library functions, use the prefix ``qemu_`` to alert -readers that they are seeing a wrapped version; otherwise avoid this prefix. +Variable Naming Conventions +--------------------------- + +A number of short naming conventions exist for variables that use +common QEMU types. For example, the architecture independent CPUState +is often held as a ``cs`` pointer variable, whereas the concrete +CPUArchState is usually held in a pointer called ``env``. + +Likewise, in device emulation code the common DeviceState is usually +called ``dev``. + +Function Naming Conventions +--------------------------- + +Wrapped version of standard library or GLib functions use a ``qemu_`` +prefix to alert readers that they are seeing a wrapped version, for +example ``qemu_strtol`` or ``qemu_mutex_lock``. Other utility functions +that are widely called from across the codebase should not have any +prefix, for example ``pstrcpy`` or bit manipulation functions such as +``find_first_bit``. + +The ``qemu_`` prefix is also used for functions that modify global +emulator state, for example ``qemu_add_vm_change_state_handler``. +However, if there is an obvious subsystem-specific prefix it should be +used instead. + +Public functions from a file or subsystem (declared in headers) tend +to have a consistent prefix to show where they came from. For example, +``tlb_`` for functions from ``cputlb.c`` or ``cpu_`` for functions +from cpus.c. + +If there are two versions of a function to be called with or without a +lock held, the function that expects the lock to be already held +usually uses the suffix ``_locked``. + Block structure =============== @@ -347,17 +385,37 @@ avoided. Low level memory management =========================== -Use of the malloc/free/realloc/calloc/valloc/memalign/posix_memalign +Use of the ``malloc/free/realloc/calloc/valloc/memalign/posix_memalign`` APIs is not allowed in the QEMU codebase. Instead of these routines, -use the GLib memory allocation routines g_malloc/g_malloc0/g_new/ -g_new0/g_realloc/g_free or QEMU's qemu_memalign/qemu_blockalign/qemu_vfree -APIs. +use the GLib memory allocation routines +``g_malloc/g_malloc0/g_new/g_new0/g_realloc/g_free`` +or QEMU's ``qemu_memalign/qemu_blockalign/qemu_vfree`` APIs. -Please note that g_malloc will exit on allocation failure, so there -is no need to test for failure (as you would have to with malloc). -Calling g_malloc with a zero size is valid and will return NULL. +Please note that ``g_malloc`` will exit on allocation failure, so +there is no need to test for failure (as you would have to with +``malloc``). Generally using ``g_malloc`` on start-up is fine as the +result of a failure to allocate memory is going to be a fatal exit +anyway. There may be some start-up cases where failing is unreasonable +(for example speculatively loading a large debug symbol table). -Prefer g_new(T, n) instead of g_malloc(sizeof(T) ``*`` n) for the following +Care should be taken to avoid introducing places where the guest could +trigger an exit by causing a large allocation. For small allocations, +of the order of 4k, a failure to allocate is likely indicative of an +overloaded host and allowing ``g_malloc`` to ``exit`` is a reasonable +approach. However for larger allocations where we could realistically +fall-back to a smaller one if need be we should use functions like +``g_try_new`` and check the result. For example this is valid approach +for a time/space trade-off like ``tlb_mmu_resize_locked`` in the +SoftMMU TLB code. + +If the lifetime of the allocation is within the function and there are +multiple exist paths you can also improve the readability of the code +by using ``g_autofree`` and related annotations. See :ref:`autofree-ref` +for more details. + +Calling ``g_malloc`` with a zero size is valid and will return NULL. + +Prefer ``g_new(T, n)`` instead of ``g_malloc(sizeof(T) * n)`` for the following reasons: * It catches multiplication overflowing size_t; @@ -371,8 +429,8 @@ Declarations like are acceptable, though. -Memory allocated by qemu_memalign or qemu_blockalign must be freed with -qemu_vfree, since breaking this will cause problems on Win32. +Memory allocated by ``qemu_memalign`` or ``qemu_blockalign`` must be freed with +``qemu_vfree``, since breaking this will cause problems on Win32. String manipulation =================== @@ -447,6 +505,8 @@ In addition, QEMU assumes that the compiler does not use the latitude given in C99 and C11 to treat aspects of signed '<<' as undefined, as documented in the GNU Compiler Collection manual starting at version 4.0. +.. _autofree-ref: + Automatic memory deallocation ============================= @@ -603,7 +663,7 @@ trace-events style In trace-events files, use a '0x' prefix to specify hex numbers, as in: -.. code-block:: +.. code-block:: c some_trace(unsigned x, uint64_t y) "x 0x%x y 0x" PRIx64 @@ -611,14 +671,14 @@ An exception is made for groups of numbers that are hexadecimal by convention and separated by the symbols '.', '/', ':', or ' ' (such as PCI bus id): -.. code-block:: +.. code-block:: c another_trace(int cssid, int ssid, int dev_num) "bus id: %x.%x.%04x" However, you can use '0x' for such groups if you want. Anyway, be sure that it is obvious that numbers are in hex, ex.: -.. code-block:: +.. code-block:: c data_dump(uint8_t c1, uint8_t c2, uint8_t c3) "bytes (in hex): %02x %02x %02x" diff --git a/docs/devel/tcg-plugins.rst b/docs/devel/tcg-plugins.rst index a05990906c..18c6581d85 100644 --- a/docs/devel/tcg-plugins.rst +++ b/docs/devel/tcg-plugins.rst @@ -63,6 +63,11 @@ valid during the lifetime of the callback so it is important that any information that is needed is extracted during the callback and saved by the plugin. +API +=== + +.. kernel-doc:: include/qemu/qemu-plugin.h + Usage ===== @@ -134,3 +139,183 @@ longer want to instrument the code. This operation is asynchronous which means callbacks may still occur after the uninstall operation is requested. The plugin isn't completely uninstalled until the safe work has executed while all vCPUs are quiescent. + +Example Plugins +=============== + +There are a number of plugins included with QEMU and you are +encouraged to contribute your own plugins plugins upstream. There is a +`contrib/plugins` directory where they can go. + +- tests/plugins + +These are some basic plugins that are used to test and exercise the +API during the `make check-tcg` target. + +- contrib/plugins/hotblocks.c + +The hotblocks plugin allows you to examine the where hot paths of +execution are in your program. Once the program has finished you will +get a sorted list of blocks reporting the starting PC, translation +count, number of instructions and execution count. This will work best +with linux-user execution as system emulation tends to generate +re-translations as blocks from different programs get swapped in and +out of system memory. + +If your program is single-threaded you can use the `inline` option for +slightly faster (but not thread safe) counters. + +Example:: + + ./aarch64-linux-user/qemu-aarch64 \ + -plugin contrib/plugins/libhotblocks.so -d plugin \ + ./tests/tcg/aarch64-linux-user/sha1 + SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6 + collected 903 entries in the hash table + pc, tcount, icount, ecount + 0x0000000041ed10, 1, 5, 66087 + 0x000000004002b0, 1, 4, 66087 + ... + +- contrib/plugins/hotpages.c + +Similar to hotblocks but this time tracks memory accesses:: + + ./aarch64-linux-user/qemu-aarch64 \ + -plugin contrib/plugins/libhotpages.so -d plugin \ + ./tests/tcg/aarch64-linux-user/sha1 + SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6 + Addr, RCPUs, Reads, WCPUs, Writes + 0x000055007fe000, 0x0001, 31747952, 0x0001, 8835161 + 0x000055007ff000, 0x0001, 29001054, 0x0001, 8780625 + 0x00005500800000, 0x0001, 687465, 0x0001, 335857 + 0x0000000048b000, 0x0001, 130594, 0x0001, 355 + 0x0000000048a000, 0x0001, 1826, 0x0001, 11 + +- contrib/plugins/howvec.c + +This is an instruction classifier so can be used to count different +types of instructions. It has a number of options to refine which get +counted. You can give an argument for a class of instructions to break +it down fully, so for example to see all the system registers +accesses:: + + ./aarch64-softmmu/qemu-system-aarch64 $(QEMU_ARGS) \ + -append "root=/dev/sda2 systemd.unit=benchmark.service" \ + -smp 4 -plugin ./contrib/plugins/libhowvec.so,arg=sreg -d plugin + +which will lead to a sorted list after the class breakdown:: + + Instruction Classes: + Class: UDEF not counted + Class: SVE (68 hits) + Class: PCrel addr (47789483 hits) + Class: Add/Sub (imm) (192817388 hits) + Class: Logical (imm) (93852565 hits) + Class: Move Wide (imm) (76398116 hits) + Class: Bitfield (44706084 hits) + Class: Extract (5499257 hits) + Class: Cond Branch (imm) (147202932 hits) + Class: Exception Gen (193581 hits) + Class: NOP not counted + Class: Hints (6652291 hits) + Class: Barriers (8001661 hits) + Class: PSTATE (1801695 hits) + Class: System Insn (6385349 hits) + Class: System Reg counted individually + Class: Branch (reg) (69497127 hits) + Class: Branch (imm) (84393665 hits) + Class: Cmp & Branch (110929659 hits) + Class: Tst & Branch (44681442 hits) + Class: AdvSimd ldstmult (736 hits) + Class: ldst excl (9098783 hits) + Class: Load Reg (lit) (87189424 hits) + Class: ldst noalloc pair (3264433 hits) + Class: ldst pair (412526434 hits) + Class: ldst reg (imm) (314734576 hits) + Class: Loads & Stores (2117774 hits) + Class: Data Proc Reg (223519077 hits) + Class: Scalar FP (31657954 hits) + Individual Instructions: + Instr: mrs x0, sp_el0 (2682661 hits) (op=0xd5384100/ System Reg) + Instr: mrs x1, tpidr_el2 (1789339 hits) (op=0xd53cd041/ System Reg) + Instr: mrs x2, tpidr_el2 (1513494 hits) (op=0xd53cd042/ System Reg) + Instr: mrs x0, tpidr_el2 (1490823 hits) (op=0xd53cd040/ System Reg) + Instr: mrs x1, sp_el0 (933793 hits) (op=0xd5384101/ System Reg) + Instr: mrs x2, sp_el0 (699516 hits) (op=0xd5384102/ System Reg) + Instr: mrs x4, tpidr_el2 (528437 hits) (op=0xd53cd044/ System Reg) + Instr: mrs x30, ttbr1_el1 (480776 hits) (op=0xd538203e/ System Reg) + Instr: msr ttbr1_el1, x30 (480713 hits) (op=0xd518203e/ System Reg) + Instr: msr vbar_el1, x30 (480671 hits) (op=0xd518c01e/ System Reg) + ... + +To find the argument shorthand for the class you need to examine the +source code of the plugin at the moment, specifically the `*opt` +argument in the InsnClassExecCount tables. + +- contrib/plugins/lockstep.c + +This is a debugging tool for developers who want to find out when and +where execution diverges after a subtle change to TCG code generation. +It is not an exact science and results are likely to be mixed once +asynchronous events are introduced. While the use of -icount can +introduce determinism to the execution flow it doesn't always follow +the translation sequence will be exactly the same. Typically this is +caused by a timer firing to service the GUI causing a block to end +early. However in some cases it has proved to be useful in pointing +people at roughly where execution diverges. The only argument you need +for the plugin is a path for the socket the two instances will +communicate over:: + + + ./sparc-softmmu/qemu-system-sparc -monitor none -parallel none \ + -net none -M SS-20 -m 256 -kernel day11/zImage.elf \ + -plugin ./contrib/plugins/liblockstep.so,arg=lockstep-sparc.sock \ + -d plugin,nochain + +which will eventually report:: + + qemu-system-sparc: warning: nic lance.0 has no peer + @ 0x000000ffd06678 vs 0x000000ffd001e0 (2/1 since last) + @ 0x000000ffd07d9c vs 0x000000ffd06678 (3/1 since last) + Δ insn_count @ 0x000000ffd07d9c (809900609) vs 0x000000ffd06678 (809900612) + previously @ 0x000000ffd06678/10 (809900609 insns) + previously @ 0x000000ffd001e0/4 (809900599 insns) + previously @ 0x000000ffd080ac/2 (809900595 insns) + previously @ 0x000000ffd08098/5 (809900593 insns) + previously @ 0x000000ffd080c0/1 (809900588 insns) + +- contrib/plugins/hwprofile + +The hwprofile tool can only be used with system emulation and allows +the user to see what hardware is accessed how often. It has a number of options: + + * arg=read or arg=write + + By default the plugin tracks both reads and writes. You can use one + of these options to limit the tracking to just one class of accesses. + + * arg=source + + Will include a detailed break down of what the guest PC that made the + access was. Not compatible with arg=pattern. Example output:: + + cirrus-low-memory @ 0xfffffd00000a0000 + pc:fffffc0000005cdc, 1, 256 + pc:fffffc0000005ce8, 1, 256 + pc:fffffc0000005cec, 1, 256 + + * arg=pattern + + Instead break down the accesses based on the offset into the HW + region. This can be useful for seeing the most used registers of a + device. Example output:: + + pci0-conf @ 0xfffffd01fe000000 + off:00000004, 1, 1 + off:00000010, 1, 3 + off:00000014, 1, 3 + off:00000018, 1, 2 + off:0000001c, 1, 2 + off:00000020, 1, 2 + ... diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index c1ff24370b..1da4c4e4c4 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -34,22 +34,23 @@ If you are writing new code in QEMU, consider adding a unit test, especially for utility modules that are relatively stateless or have few dependencies. To add a new unit test: -1. Create a new source file. For example, ``tests/foo-test.c``. +1. Create a new source file. For example, ``tests/unit/foo-test.c``. 2. Write the test. Normally you would include the header file which exports the module API, then verify the interface behaves as expected from your test. The test code should be organized with the glib testing framework. Copying and modifying an existing test is usually a good idea. -3. Add the test to ``tests/Makefile.include``. First, name the unit test - program and add it to ``$(check-unit-y)``; then add a rule to build the - executable. For example: +3. Add the test to ``tests/unit/meson.build``. The unit tests are listed in a + dictionary called ``tests``. The values are any additional sources and + dependencies to be linked with the test. For a simple test whose source + is in ``tests/unit/foo-test.c``, it is enough to add an entry like:: -.. code:: - - check-unit-y += tests/foo-test$(EXESUF) - tests/foo-test$(EXESUF): tests/foo-test.o $(test-util-obj-y) - ... + { + ... + 'foo-test': [], + ... + } Since unit tests don't require environment variables, the simplest way to debug a unit test failure is often directly invoking it or even running it under @@ -70,8 +71,8 @@ QTest QTest is a device emulation testing framework. It can be very useful to test device models; it could also control certain aspects of QEMU (such as virtual -clock stepping), with a special purpose "qtest" protocol. Refer to the -documentation in ``qtest.c`` for more details of the protocol. +clock stepping), with a special purpose "qtest" protocol. Refer to +:doc:`qtest` for more details. QTest cases can be executed with @@ -79,49 +80,6 @@ QTest cases can be executed with make check-qtest -The QTest library is implemented by ``tests/qtest/libqtest.c`` and the API is -defined in ``tests/qtest/libqtest.h``. - -Consider adding a new QTest case when you are introducing a new virtual -hardware, or extending one if you are adding functionalities to an existing -virtual device. - -On top of libqtest, a higher level library, ``libqos``, was created to -encapsulate common tasks of device drivers, such as memory management and -communicating with system buses or devices. Many virtual device tests use -libqos instead of directly calling into libqtest. - -Steps to add a new QTest case are: - -1. Create a new source file for the test. (More than one file can be added as - necessary.) For example, ``tests/qtest/foo-test.c``. - -2. Write the test code with the glib and libqtest/libqos API. See also existing - tests and the library headers for reference. - -3. Register the new test in ``tests/qtest/Makefile.include``. Add the test - executable name to an appropriate ``check-qtest-*-y`` variable. For example: - - ``check-qtest-generic-y = tests/qtest/foo-test$(EXESUF)`` - -4. Add object dependencies of the executable in the Makefile, including the - test source file(s) and other interesting objects. For example: - - ``tests/qtest/foo-test$(EXESUF): tests/qtest/foo-test.o $(libqos-obj-y)`` - -Debugging a QTest failure is slightly harder than the unit test because the -tests look up QEMU program names in the environment variables, such as -``QTEST_QEMU_BINARY`` and ``QTEST_QEMU_IMG``, and also because it is not easy -to attach gdb to the QEMU process spawned from the test. But manual invoking -and using gdb on the test is still simple to do: find out the actual command -from the output of - -.. code:: - - make check-qtest V=1 - -which you can run manually. - QAPI schema tests ----------------- @@ -153,7 +111,7 @@ check-block ----------- ``make check-block`` runs a subset of the block layer iotests (the tests that -are in the "auto" group in ``tests/qemu-iotests/group``). +are in the "auto" group). See the "QEMU iotests" section below for more information. GCC gcov support @@ -164,13 +122,12 @@ instrumenting the tested code. To use it, configure QEMU with ``--enable-gcov`` option and build. Then run ``make check`` as usual. If you want to gather coverage information on a single test the ``make -clean-coverage`` target can be used to delete any existing coverage +clean-gcda`` target can be used to delete any existing coverage information before running a single test. You can generate a HTML coverage report by executing ``make -coverage-report`` which will create -./reports/coverage/coverage-report.html. If you want to create it -elsewhere simply execute ``make /foo/bar/baz/coverage-report.html``. +coverage-html`` which will create +``meson-logs/coveragereport/index.html``. Further analysis can be conducted by running the ``gcov`` command directly on the various .gcda output files. Please read the ``gcov`` @@ -267,21 +224,76 @@ another application on the host may have locked the file, possibly leading to a test failure. If using such devices are explicitly desired, consider adding ``locking=off`` option to disable image locking. -.. _docker-ref: +Test case groups +---------------- -Docker based tests -================== +"Tests may belong to one or more test groups, which are defined in the form +of a comment in the test source file. By convention, test groups are listed +in the second line of the test file, after the "#!/..." line, like this: + +.. code:: + + #!/usr/bin/env python3 + # group: auto quick + # + ... + +Another way of defining groups is creating the tests/qemu-iotests/group.local +file. This should be used only for downstream (this file should never appear +in upstream). This file may be used for defining some downstream test groups +or for temporarily disabling tests, like this: + +.. code:: + + # groups for some company downstream process + # + # ci - tests to run on build + # down - our downstream tests, not for upstream + # + # Format of each line is: + # TEST_NAME TEST_GROUP [TEST_GROUP ]... + + 013 ci + 210 disabled + 215 disabled + our-ugly-workaround-test down ci + +Note that the following group names have a special meaning: + +- quick: Tests in this group should finish within a few seconds. + +- auto: Tests in this group are used during "make check" and should be + runnable in any case. That means they should run with every QEMU binary + (also non-x86), with every QEMU configuration (i.e. must not fail if + an optional feature is not compiled in - but reporting a "skip" is ok), + work at least with the qcow2 file format, work with all kind of host + filesystems and users (e.g. "nobody" or "root") and must not take too + much memory and disk space (since CI pipelines tend to fail otherwise). + +- disabled: Tests in this group are disabled and ignored by check. + +.. _container-ref: + +Container based tests +===================== Introduction ------------ -The Docker testing framework in QEMU utilizes public Docker images to build and -test QEMU in predefined and widely accessible Linux environments. This makes -it possible to expand the test coverage across distros, toolchain flavors and -library versions. +The container testing framework in QEMU utilizes public images to +build and test QEMU in predefined and widely accessible Linux +environments. This makes it possible to expand the test coverage +across distros, toolchain flavors and library versions. The support +was originally written for Docker although we also support Podman as +an alternative container runtime. Although the many of the target +names and scripts are prefixed with "docker" the system will +automatically run on whichever is configured. -Prerequisites -------------- +The container images are also used to augment the generation of tests +for testing TCG. See :ref:`checktcg-ref` for more details. + +Docker Prerequisites +-------------------- Install "docker" with the system package manager and start the Docker service on your development machine, then make sure you have the privilege to run @@ -311,26 +323,53 @@ Note that any one of above configurations makes it possible for the user to exploit the whole host with Docker bind mounting or other privileged operations. So only do it on development machines. -Quickstart ----------- +Podman Prerequisites +-------------------- -From source tree, type ``make docker`` to see the help. Testing can be started -without configuring or building QEMU (``configure`` and ``make`` are done in -the container, with parameters defined by the make target): +Install "podman" with the system package manager. .. code:: - make docker-test-build@min-glib + $ sudo dnf install podman + $ podman ps -This will create a container instance using the ``min-glib`` image (the image +The last command should print an empty table, to verify the system is ready. + +Quickstart +---------- + +From source tree, type ``make docker-help`` to see the help. Testing +can be started without configuring or building QEMU (``configure`` and +``make`` are done in the container, with parameters defined by the +make target): + +.. code:: + + make docker-test-build@centos8 + +This will create a container instance using the ``centos8`` image (the image is downloaded and initialized automatically), in which the ``test-build`` job is executed. +Registry +-------- + +The QEMU project has a container registry hosted by GitLab at +``registry.gitlab.com/qemu-project/qemu`` which will automatically be +used to pull in pre-built layers. This avoids unnecessary strain on +the distro archives created by multiple developers running the same +container build steps over and over again. This can be overridden +locally by using the ``NOCACHE`` build option: + +.. code:: + + make docker-image-debian10 NOCACHE=1 + Images ------ -Along with many other images, the ``min-glib`` image is defined in a Dockerfile -in ``tests/docker/dockerfiles/``, called ``min-glib.docker``. ``make docker`` +Along with many other images, the ``centos8`` image is defined in a Dockerfile +in ``tests/docker/dockerfiles/``, called ``centos8.docker``. ``make docker-help`` command will list all the available images. To add a new image, simply create a new ``.docker`` file under the @@ -350,21 +389,7 @@ QEMU. Docker tests are the executables under ``tests/docker`` named library, ``tests/docker/common.rc``, which provides helpers to find the QEMU source and build it. -The full list of tests is printed in the ``make docker`` help. - -Tools ------ - -There are executables that are created to run in a specific Docker environment. -This makes it easy to write scripts that have heavy or special dependencies, -but are still very easy to use. - -Currently the only tool is ``travis``, which mimics the Travis-CI tests in a -container. It runs in the ``travis`` image: - -.. code:: - - make docker-travis@travis +The full list of tests is printed in the ``make docker-help`` help. Debugging a Docker test failure ------------------------------- @@ -472,7 +497,7 @@ the warning. A few important files for suppressing warnings are: tests/tsan/suppressions.tsan - Has TSan warnings we wish to suppress at runtime. -The comment on each supression will typically indicate why we are +The comment on each suppression will typically indicate why we are suppressing it. More information on the file format can be found here: https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions @@ -697,7 +722,7 @@ To manually install Avocado and its dependencies, run: Alternatively, follow the instructions on this link: - http://avocado-framework.readthedocs.io/en/latest/GetStartedGuide.html#installing-avocado + https://avocado-framework.readthedocs.io/en/latest/guides/user/chapters/installing.html Overview -------- @@ -760,9 +785,6 @@ and hypothetical example follows: class MultipleMachines(Test): - """ - :avocado: enable - """ def test_multiple_machines(self): first_machine = self.get_vm() second_machine = self.get_vm() @@ -820,7 +842,7 @@ the following approaches: 1) Set ``qemu_bin``, and use the given binary 2) Do not set ``qemu_bin``, and use a QEMU binary named like - "${arch}-softmmu/qemu-system-${arch}", either in the current + "qemu-system-${arch}", either in the current working directory, or in the current source tree. The resulting ``qemu_bin`` value will be preserved in the @@ -880,14 +902,14 @@ Parameter reference To understand how Avocado parameters are accessed by tests, and how they can be passed to tests, please refer to:: - http://avocado-framework.readthedocs.io/en/latest/WritingTests.html#accessing-test-parameters + https://avocado-framework.readthedocs.io/en/latest/guides/writer/chapters/writing.html#accessing-test-parameters Parameter values can be easily seen in the log files, and will look like the following: .. code:: - PARAMS (key=qemu_bin, path=*, default=x86_64-softmmu/qemu-system-x86_64) => 'x86_64-softmmu/qemu-system-x86_64 + PARAMS (key=qemu_bin, path=*, default=./qemu-system-x86_64) => './qemu-system-x86_64 arch ~~~~ @@ -914,6 +936,68 @@ qemu_bin The exact QEMU binary to be used on QEMUMachine. +Skipping tests +-------------- +The Avocado framework provides Python decorators which allow for easily skip +tests running under certain conditions. For example, on the lack of a binary +on the test system or when the running environment is a CI system. For further +information about those decorators, please refer to:: + + https://avocado-framework.readthedocs.io/en/latest/guides/writer/chapters/writing.html#skipping-tests + +While the conditions for skipping tests are often specifics of each one, there +are recurring scenarios identified by the QEMU developers and the use of +environment variables became a kind of standard way to enable/disable tests. + +Here is a list of the most used variables: + +AVOCADO_ALLOW_LARGE_STORAGE +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Tests which are going to fetch or produce assets considered *large* are not +going to run unless that `AVOCADO_ALLOW_LARGE_STORAGE=1` is exported on +the environment. + +The definition of *large* is a bit arbitrary here, but it usually means an +asset which occupies at least 1GB of size on disk when uncompressed. + +AVOCADO_ALLOW_UNTRUSTED_CODE +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +There are tests which will boot a kernel image or firmware that can be +considered not safe to run on the developer's workstation, thus they are +skipped by default. The definition of *not safe* is also arbitrary but +usually it means a blob which either its source or build process aren't +public available. + +You should export `AVOCADO_ALLOW_UNTRUSTED_CODE=1` on the environment in +order to allow tests which make use of those kind of assets. + +AVOCADO_TIMEOUT_EXPECTED +~~~~~~~~~~~~~~~~~~~~~~~~ +The Avocado framework has a timeout mechanism which interrupts tests to avoid the +test suite of getting stuck. The timeout value can be set via test parameter or +property defined in the test class, for further details:: + + https://avocado-framework.readthedocs.io/en/latest/guides/writer/chapters/writing.html#setting-a-test-timeout + +Even though the timeout can be set by the test developer, there are some tests +that may not have a well-defined limit of time to finish under certain +conditions. For example, tests that take longer to execute when QEMU is +compiled with debug flags. Therefore, the `AVOCADO_TIMEOUT_EXPECTED` variable +has been used to determine whether those tests should run or not. + +GITLAB_CI +~~~~~~~~~ +A number of tests are flagged to not run on the GitLab CI. Usually because +they proved to the flaky or there are constraints on the CI environment which +would make them fail. If you encounter a similar situation then use that +variable as shown on the code snippet below to skip the test: + +.. code:: + + @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') + def test(self): + do_something() + Uninstalling Avocado -------------------- @@ -930,6 +1014,8 @@ And remove any package you want with:: If you've used ``make check-acceptance``, the Python virtual environment where Avocado is installed will be cleaned up as part of ``make check-clean``. +.. _checktcg-ref: + Testing with "make check-tcg" ============================= @@ -951,10 +1037,17 @@ for the architecture in question, for example:: There is also a ``--cross-cc-flags-ARCH`` flag in case additional compiler flags are needed to build for a given target. -If you have the ability to run containers as the user you can also -take advantage of the build systems "Docker" support. It will then use -containers to build any test case for an enabled guest where there is -no system compiler available. See :ref: `_docker-ref` for details. +If you have the ability to run containers as the user the build system +will automatically use them where no system compiler is available. For +architectures where we also support building QEMU we will generally +use the same container to build tests. However there are a number of +additional containers defined that have a minimal cross-build +environment that is only suitable for building test cases. Sometimes +we may use a bleeding edge distribution for compiler features needed +for test cases that aren't yet in the LTS distros we support for QEMU +itself. + +See :ref:`container-ref` for more details. Running subset of tests ----------------------- diff --git a/docs/devel/tracing.txt b/docs/devel/tracing.rst similarity index 71% rename from docs/devel/tracing.txt rename to docs/devel/tracing.rst index cb5f685de9..ba83954899 100644 --- a/docs/devel/tracing.txt +++ b/docs/devel/tracing.rst @@ -1,71 +1,94 @@ -= Tracing = +======= +Tracing +======= -== Introduction == +Introduction +============ This document describes the tracing infrastructure in QEMU and how to use it for debugging, profiling, and observing execution. -== Quickstart == +Quickstart +========== -1. Build with the 'simple' trace backend: +Enable tracing of ``memory_region_ops_read`` and ``memory_region_ops_write`` +events:: - ./configure --enable-trace-backends=simple - make + $ qemu --trace "memory_region_ops_*" ... + ... + 719585@1608130130.441188:memory_region_ops_read cpu 0 mr 0x562fdfbb3820 addr 0x3cc value 0x67 size 1 + 719585@1608130130.441190:memory_region_ops_write cpu 0 mr 0x562fdfbd2f00 addr 0x3d4 value 0x70e size 2 -2. Create a file with the events you want to trace: +This output comes from the "log" trace backend that is enabled by default when +``./configure --enable-trace-backends=BACKENDS`` was not explicitly specified. - echo memory_region_ops_read >/tmp/events +Multiple patterns can be specified by repeating the ``--trace`` option:: -3. Run the virtual machine to produce a trace file: + $ qemu --trace "kvm_*" --trace "virtio_*" ... - qemu --trace events=/tmp/events ... # your normal QEMU invocation +When patterns are used frequently it is more convenient to store them in a +file to avoid long command-line options:: -4. Pretty-print the binary trace file: + $ echo "memory_region_ops_*" >/tmp/events + $ echo "kvm_*" >>/tmp/events + $ qemu --trace events=/tmp/events ... - ./scripts/simpletrace.py trace-events-all trace-* # Override * with QEMU +Trace events +============ -== Trace events == +Sub-directory setup +------------------- -=== Sub-directory setup === - -Each directory in the source tree can declare a set of static trace events -in a local "trace-events" file. All directories which contain "trace-events" -files must be listed in the "trace-events-subdirs" make variable in the top -level Makefile.objs. During build, the "trace-events" file in each listed -subdirectory will be processed by the "tracetool" script to generate code for -the trace events. +Each directory in the source tree can declare a set of trace events in a local +"trace-events" file. All directories which contain "trace-events" files must be +listed in the "trace_events_subdirs" variable in the top level meson.build +file. During build, the "trace-events" file in each listed subdirectory will be +processed by the "tracetool" script to generate code for the trace events. The individual "trace-events" files are merged into a "trace-events-all" file, which is also installed into "/usr/share/qemu" with the name "trace-events". This merged file is to be used by the "simpletrace.py" script to later analyse traces in the simpletrace data format. -In the sub-directory the following files will be automatically generated +The following files are automatically generated in /trace/ during the +build: - - trace.c - the trace event state declarations - - trace.h - the trace event enums and probe functions - - trace-dtrace.h - DTrace event probe specification - - trace-dtrace.dtrace - DTrace event probe helper declaration - - trace-dtrace.o - binary DTrace provider (generated by dtrace) - - trace-ust.h - UST event probe helper declarations + - trace-.c - the trace event state declarations + - trace-.h - the trace event enums and probe functions + - trace-dtrace-.h - DTrace event probe specification + - trace-dtrace-.dtrace - DTrace event probe helper declaration + - trace-dtrace-.o - binary DTrace provider (generated by dtrace) + - trace-ust-.h - UST event probe helper declarations -Source files in the sub-directory should #include the local 'trace.h' file, -without any sub-directory path prefix. eg io/channel-buffer.c would do +Here is the sub-directory path with '/' replaced by '_'. For example, +"accel/kvm" becomes "accel_kvm" and the final filename for "trace-.c" +becomes "trace-accel_kvm.c". + +Source files in the source tree do not directly include generated files in +"/trace/". Instead they #include the local "trace.h" file, without +any sub-directory path prefix. eg io/channel-buffer.c would do:: #include "trace.h" -To access the 'io/trace.h' file. While it is possible to include a trace.h -file from outside a source files' own sub-directory, this is discouraged in -general. It is strongly preferred that all events be declared directly in -the sub-directory that uses them. The only exception is where there are some -shared trace events defined in the top level directory trace-events file. -The top level directory generates trace files with a filename prefix of -"trace-root" instead of just "trace". This is to avoid ambiguity between -a trace.h in the current directory, vs the top level directory. +The "io/trace.h" file must be created manually with an #include of the +corresponding "trace/trace-.h" file that will be generated in the +builddir:: -=== Using trace events === + $ echo '#include "trace/trace-io.h"' >io/trace.h -Trace events are invoked directly from source code like this: +While it is possible to include a trace.h file from outside a source file's own +sub-directory, this is discouraged in general. It is strongly preferred that +all events be declared directly in the sub-directory that uses them. The only +exception is where there are some shared trace events defined in the top level +directory trace-events file. The top level directory generates trace files +with a filename prefix of "trace/trace-root" instead of just "trace". This is +to avoid ambiguity between a trace.h in the current directory, vs the top level +directory. + +Using trace events +------------------ + +Trace events are invoked directly from source code like this:: #include "trace.h" /* needed for trace event prototype */ @@ -82,7 +105,8 @@ Trace events are invoked directly from source code like this: return ptr; } -=== Declaring trace events === +Declaring trace events +---------------------- The "tracetool" script produces the trace.h header file which is included by every source file that uses trace events. Since many source files include @@ -116,13 +140,14 @@ Format strings must not end with a newline character. It is the responsibility of backends to adapt line ending for proper logging. Each event declaration will start with the event name, then its arguments, -finally a format string for pretty-printing. For example: +finally a format string for pretty-printing. For example:: qemu_vmalloc(size_t size, void *ptr) "size %zu ptr %p" qemu_vfree(void *ptr) "ptr %p" -=== Hints for adding new trace events === +Hints for adding new trace events +--------------------------------- 1. Trace state changes in the code. Interesting points in the code usually involve a state change like starting, stopping, allocating, freeing. State @@ -141,7 +166,8 @@ finally a format string for pretty-printing. For example: 4. Name trace events after their function. If there are multiple trace events in one function, append a unique distinguisher at the end of the name. -== Generic interface and monitor commands == +Generic interface and monitor commands +====================================== You can programmatically query and control the state of trace events through a backend-agnostic interface provided by the header "trace/control.h". @@ -152,11 +178,11 @@ header "trace/control.h" to see which routines are backend-dependent). The state of events can also be queried and modified through monitor commands: -* info trace-events +* ``info trace-events`` View available trace events and their state. State 1 means enabled, state 0 means disabled. -* trace-event NAME on|off +* ``trace-event NAME on|off`` Enable/disable a given trace event or a group of events (using wildcards). The "--trace events=" command line argument can be used to enable the @@ -170,11 +196,12 @@ to enable an entire family of events but one noisy event needs to be disabled. Wildcard matching is supported in both the monitor command "trace-event" and the events list file. That means you can enable/disable the events having a common prefix in a batch. For example, virtio-blk trace events could be enabled using -the following monitor command: +the following monitor command:: trace-event virtio_blk_* on -== Trace backends == +Trace backends +============== The "tracetool" script automates tedious trace event code generation and also keeps the trace event declarations independent of the trace backend. The trace @@ -182,9 +209,9 @@ events are not tightly coupled to a specific trace backend, such as LTTng or SystemTap. Support for trace backends can be added by extending the "tracetool" script. -The trace backends are chosen at configure time: +The trace backends are chosen at configure time:: - ./configure --enable-trace-backends=simple + ./configure --enable-trace-backends=simple,dtrace For a list of supported trace backends, try ./configure --help or see below. If multiple backends are enabled, the trace is sent to them all. @@ -194,7 +221,8 @@ If no backends are explicitly selected, configure will default to the The following subsections describe the supported trace backends. -=== Nop === +Nop +--- The "nop" backend generates empty trace event functions so that the compiler can optimize out trace events completely. This imposes no performance @@ -203,7 +231,8 @@ penalty. Note that regardless of the selected trace backend, events with the "disable" property will be generated with the "nop" backend. -=== Log === +Log +--- The "log" backend sends trace events directly to standard error. This effectively turns trace events into debug printfs. @@ -211,30 +240,56 @@ effectively turns trace events into debug printfs. This is the simplest backend and can be used together with existing code that uses DPRINTF(). -=== Simpletrace === +The -msg timestamp=on|off command-line option controls whether or not to print +the tid/timestamp prefix for each trace event. -The "simple" backend supports common use cases and comes as part of the QEMU -source tree. It may not be as powerful as platform-specific or third-party -trace backends but it is portable. This is the recommended trace backend -unless you have specific needs for more advanced backends. +Simpletrace +----------- -=== Ftrace === +The "simple" backend writes binary trace logs to a file from a thread, making +it lower overhead than the "log" backend. A Python API is available for writing +offline trace file analysis scripts. It may not be as powerful as +platform-specific or third-party trace backends but it is portable and has no +special library dependencies. + +Monitor commands +~~~~~~~~~~~~~~~~ + +* ``trace-file on|off|flush|set `` + Enable/disable/flush the trace file or set the trace file name. + +Analyzing trace files +~~~~~~~~~~~~~~~~~~~~~ + +The "simple" backend produces binary trace files that can be formatted with the +simpletrace.py script. The script takes the "trace-events-all" file and the +binary trace:: + + ./scripts/simpletrace.py trace-events-all trace-12345 + +You must ensure that the same "trace-events-all" file was used to build QEMU, +otherwise trace event declarations may have changed and output will not be +consistent. + +Ftrace +------ The "ftrace" backend writes trace data to ftrace marker. This effectively sends trace events to ftrace ring buffer, and you can compare qemu trace data and kernel(especially kvm.ko when using KVM) trace data. -if you use KVM, enable kvm events in ftrace: +if you use KVM, enable kvm events in ftrace:: # echo 1 > /sys/kernel/debug/tracing/events/kvm/enable -After running qemu by root user, you can get the trace: +After running qemu by root user, you can get the trace:: # cat /sys/kernel/debug/tracing/trace Restriction: "ftrace" backend is restricted to Linux only. -=== Syslog === +Syslog +------ The "syslog" backend sends trace events using the POSIX syslog API. The log is opened specifying the LOG_DAEMON facility and LOG_PID option (so events @@ -246,24 +301,8 @@ NOTE: syslog may squash duplicate consecutive trace events and apply rate Restriction: "syslog" backend is restricted to POSIX compliant OS. -==== Monitor commands ==== - -* trace-file on|off|flush|set - Enable/disable/flush the trace file or set the trace file name. - -==== Analyzing trace files ==== - -The "simple" backend produces binary trace files that can be formatted with the -simpletrace.py script. The script takes the "trace-events-all" file and the -binary trace: - - ./scripts/simpletrace.py trace-events-all trace-12345 - -You must ensure that the same "trace-events-all" file was used to build QEMU, -otherwise trace event declarations may have changed and output will not be -consistent. - -=== LTTng Userspace Tracer === +LTTng Userspace Tracer +---------------------- The "ust" backend uses the LTTng Userspace Tracer library. There are no monitor commands built into QEMU, instead UST utilities should be used to list, @@ -275,61 +314,65 @@ lttng-sessiond daemon for the current user prior to running any instance of QEMU. While running an instrumented QEMU, LTTng should be able to list all available -events: +events:: lttng list -u -Create tracing session: +Create tracing session:: lttng create mysession -Enable events: +Enable events:: lttng enable-event qemu:g_malloc -u Where the events can either be a comma-separated list of events, or "-a" to -enable all tracepoint events. Start and stop tracing as needed: +enable all tracepoint events. Start and stop tracing as needed:: lttng start lttng stop -View the trace: +View the trace:: lttng view -Destroy tracing session: +Destroy tracing session:: lttng destroy -Babeltrace can be used at any later time to view the trace: +Babeltrace can be used at any later time to view the trace:: babeltrace $HOME/lttng-traces/mysession--