osx_instance:
image: mojave-base
install_script:
- - brew install pkg-config python gnu-sed glib pixman make sdl2
+ - brew install pkg-config python gnu-sed glib pixman make sdl2 bash
script:
- mkdir build
- cd build
# this is an alias for the latest Xcode
image: mojave-xcode
install_script:
- - brew install pkg-config gnu-sed glib pixman make sdl2
+ - brew install pkg-config gnu-sed glib pixman make sdl2 bash
script:
- mkdir build
- cd build
*.tp
*.vr
*.d
+!/.gitlab-ci.d
!/scripts/qemu-guest-agent/fsfreeze-hook.d
*.o
.sdk
--- /dev/null
+.container_job_template: &container_job_definition
+ image: docker:stable
+ stage: containers
+ services:
+ - docker:dind
+ before_script:
+ - export TAG="$CI_REGISTRY_IMAGE/qemu/$NAME:latest"
+ - 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"
+ script:
+ - echo "TAG:$TAG"
+ - echo "COMMON_TAG:$COMMON_TAG"
+ - docker pull "$TAG" || docker pull "$COMMON_TAG" || true
+ - ./tests/docker/docker.py --engine docker build
+ -t "qemu/$NAME" -f "tests/docker/dockerfiles/$NAME.docker"
+ -r $CI_REGISTRY_IMAGE
+ - docker tag "qemu/$NAME" "$TAG"
+ - 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-centos7-container:
+ <<: *container_job_definition
+ variables:
+ NAME: centos7
+
+amd64-centos8-container:
+ <<: *container_job_definition
+ variables:
+ NAME: centos8
+
+amd64-debian10-container:
+ <<: *container_job_definition
+ variables:
+ NAME: debian10
+
+amd64-debian11-container:
+ <<: *container_job_definition
+ 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
+ needs: ['amd64-debian10-container']
+ variables:
+ NAME: debian-alpha-cross
+
+amd64-debian-cross-container:
+ <<: *container_job_definition
+ stage: containers-layer2
+ needs: ['amd64-debian10-container']
+ variables:
+ NAME: debian-amd64-cross
+
+amd64-debian-user-cross-container:
+ <<: *container_job_definition
+ stage: containers-layer2
+ needs: ['amd64-debian10-container']
+ variables:
+ NAME: debian-all-test-cross
+
+amd64-debian-container:
+ <<: *container_job_definition
+ stage: containers-layer2
+ needs: ['amd64-debian10-container']
+ variables:
+ NAME: debian-amd64
+
+arm64-debian-cross-container:
+ <<: *container_job_definition
+ stage: containers-layer2
+ needs: ['amd64-debian10-container']
+ variables:
+ NAME: debian-arm64-cross
+
+arm64-test-debian-cross-container:
+ <<: *container_job_definition
+ stage: containers-layer2
+ needs: ['amd64-debian11-container']
+ variables:
+ NAME: debian-arm64-test-cross
+
+armel-debian-cross-container:
+ <<: *container_job_definition
+ stage: containers-layer2
+ needs: ['amd64-debian10-container']
+ variables:
+ NAME: debian-armel-cross
+
+armhf-debian-cross-container:
+ <<: *container_job_definition
+ stage: containers-layer2
+ needs: ['amd64-debian10-container']
+ variables:
+ NAME: debian-armhf-cross
+
+hppa-debian-cross-container:
+ <<: *container_job_definition
+ stage: containers-layer2
+ needs: ['amd64-debian10-container']
+ variables:
+ NAME: debian-hppa-cross
+
+m68k-debian-cross-container:
+ <<: *container_job_definition
+ stage: containers-layer2
+ needs: ['amd64-debian10-container']
+ variables:
+ NAME: debian-m68k-cross
+
+mips64-debian-cross-container:
+ <<: *container_job_definition
+ stage: containers-layer2
+ needs: ['amd64-debian10-container']
+ variables:
+ NAME: debian-mips64-cross
+
+mips64el-debian-cross-container:
+ <<: *container_job_definition
+ stage: containers-layer2
+ needs: ['amd64-debian10-container']
+ variables:
+ NAME: debian-mips64el-cross
+
+mips-debian-cross-container:
+ <<: *container_job_definition
+ stage: containers-layer2
+ needs: ['amd64-debian10-container']
+ variables:
+ NAME: debian-mips-cross
+
+mipsel-debian-cross-container:
+ <<: *container_job_definition
+ stage: containers-layer2
+ needs: ['amd64-debian10-container']
+ variables:
+ NAME: debian-mipsel-cross
+
+powerpc-debian-cross-container:
+ <<: *container_job_definition
+ stage: containers-layer2
+ needs: ['amd64-debian10-container']
+ variables:
+ NAME: debian-powerpc-cross
+
+ppc64-debian-cross-container:
+ <<: *container_job_definition
+ stage: containers-layer2
+ needs: ['amd64-debian10-container']
+ variables:
+ NAME: debian-ppc64-cross
+
+ppc64el-debian-cross-container:
+ <<: *container_job_definition
+ stage: containers-layer2
+ needs: ['amd64-debian10-container']
+ variables:
+ NAME: debian-ppc64el-cross
+
+riscv64-debian-cross-container:
+ <<: *container_job_definition
+ stage: containers-layer2
+ needs: ['amd64-debian10-container']
+ variables:
+ NAME: debian-riscv64-cross
+
+s390x-debian-cross-container:
+ <<: *container_job_definition
+ stage: containers-layer2
+ needs: ['amd64-debian10-container']
+ variables:
+ NAME: debian-s390x-cross
+
+sh4-debian-cross-container:
+ <<: *container_job_definition
+ stage: containers-layer2
+ needs: ['amd64-debian10-container']
+ variables:
+ NAME: debian-sh4-cross
+
+sparc64-debian-cross-container:
+ <<: *container_job_definition
+ stage: containers-layer2
+ needs: ['amd64-debian10-container']
+ variables:
+ NAME: debian-sparc64-cross
+
+tricore-debian-cross-container:
+ <<: *container_job_definition
+ stage: containers-layer2
+ needs: ['amd64-debian9-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:
+ NAME: debian-xtensa-cross
+
+cris-fedora-cross-container:
+ <<: *container_job_definition
+ variables:
+ NAME: fedora-cris-cross
+
+amd64-fedora-container:
+ <<: *container_job_definition
+ variables:
+ NAME: fedora
+
+i386-fedora-cross-container:
+ <<: *container_job_definition
+ variables:
+ NAME: fedora-i386-cross
+
+amd64-ubuntu1804-container:
+ <<: *container_job_definition
+ variables:
+ NAME: ubuntu1804
+
+amd64-ubuntu2004-container:
+ <<: *container_job_definition
+ variables:
+ NAME: ubuntu2004
+
+amd64-ubuntu-container:
+ <<: *container_job_definition
+ variables:
+ NAME: ubuntu
docker-edk2:
- stage: build
+ stage: containers
rules: # Only run this job when the Dockerfile is modified
- changes:
- - .gitlab-ci-edk2.yml
+ - .gitlab-ci.d/edk2.yml
- .gitlab-ci.d/edk2/Dockerfile
when: always
image: docker:19.03.1
- docker push $IMAGE_TAG
build-edk2:
+ stage: build
rules: # Only run this job when ...
- changes: # ... roms/edk2/ is modified (submodule updated)
- roms/edk2/*
docker-opensbi:
- stage: build
+ stage: containers
rules: # Only run this job when the Dockerfile is modified
- changes:
- - .gitlab-ci-opensbi.yml
+ - .gitlab-ci.d/opensbi.yml
- .gitlab-ci.d/opensbi/Dockerfile
when: always
image: docker:19.03.1
- docker push $IMAGE_TAG
build-opensbi:
+ stage: build
rules: # Only run this job when ...
- changes: # ... roms/opensbi/ is modified (submodule updated)
- roms/opensbi/*
+# Currently we have two build stages after our containers are built:
+# - build (for traditional build and test or first stage build)
+# - test (for test stages, using build artefacts from a build stage)
+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'
+
+.native_build_job_template: &native_build_job_definition
+ stage: build
+ image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
+ before_script:
+ - JOBS=$(expr $(nproc) + 1)
+ script:
+ - mkdir build
+ - cd build
+ - if test -n "$TARGETS";
+ then
+ ../configure --enable-werror $CONFIGURE_ARGS --target-list="$TARGETS" ;
+ else
+ ../configure --enable-werror $CONFIGURE_ARGS ;
+ fi
+ - make -j"$JOBS"
+ - if test -n "$MAKE_CHECK_ARGS";
+ then
+ make -j"$JOBS" $MAKE_CHECK_ARGS ;
+ fi
+
+.native_test_job_template: &native_test_job_definition
+ stage: test
+ image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest
+ script:
+ - cd build
+ - find . -type f -exec touch {} +
+ - make $MAKE_CHECK_ARGS
+
+.post_acceptance_template: &post_acceptance
+ 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 $HOME/avocado/data/cache
+
+build-system-ubuntu-main:
+ <<: *native_build_job_definition
+ variables:
+ IMAGE: ubuntu2004
+ TARGETS: aarch64-softmmu alpha-softmmu cris-softmmu hppa-softmmu lm32-softmmu
+ moxie-softmmu microblazeel-softmmu mips64el-softmmu m68k-softmmu ppc-softmmu
+ riscv64-softmmu sparc-softmmu
+ MAKE_CHECK_ARGS: check-build
+ artifacts:
+ paths:
+ - build
-.update_apt_template: &before_script_apt
- before_script:
- - apt-get update -qq
- - apt-get install -y -qq git gcc libglib2.0-dev libpixman-1-dev make
- genisoimage
- - JOBS=$(expr $(nproc) + 1)
-
-.update_dnf_template: &before_script_dnf
- before_script:
- - dnf update -y
- - dnf install -y bzip2 diffutils gcc git genisoimage findutils glib2-devel
- make python3 perl-podlators perl-Test-Harness pixman-devel zlib-devel
- - JOBS=$(expr $(nproc) + 1)
-
-build-system1:
- image: ubuntu:19.10
- <<: *before_script_apt
- script:
- - apt-get install -y -qq libgtk-3-dev libvte-dev nettle-dev libcacard-dev
- libusb-dev libvde-dev libspice-protocol-dev libgl1-mesa-dev libvdeplug-dev
- - mkdir build
- - cd build
- - ../configure --enable-werror --target-list="aarch64-softmmu alpha-softmmu
- cris-softmmu hppa-softmmu lm32-softmmu moxie-softmmu microblazeel-softmmu
- mips64el-softmmu m68k-softmmu ppc-softmmu riscv64-softmmu sparc-softmmu"
- - make -j"$JOBS"
- - make -j"$JOBS" check
-
-build-system2:
- image: fedora:latest
- <<: *before_script_dnf
- script:
- - yum install -y SDL2-devel libgcrypt-devel brlapi-devel libaio-devel
- libfdt-devel lzo-devel librdmacm-devel libibverbs-devel libibumad-devel
- libzstd-devel
- - mkdir build
- - cd build
- - ../configure --enable-werror --target-list="tricore-softmmu unicore32-softmmu
- microblaze-softmmu mips-softmmu riscv32-softmmu s390x-softmmu sh4-softmmu
- sparc64-softmmu x86_64-softmmu xtensa-softmmu nios2-softmmu or1k-softmmu"
- - make -j"$JOBS"
- - make -j"$JOBS" check
+check-system-ubuntu-main:
+ <<: *native_test_job_definition
+ needs:
+ - job: build-system-ubuntu-main
+ artifacts: true
+ variables:
+ IMAGE: ubuntu2004
+ MAKE_CHECK_ARGS: check
+
+acceptance-system-ubuntu-main:
+ <<: *native_test_job_definition
+ needs:
+ - job: build-system-ubuntu-main
+ artifacts: true
+ variables:
+ IMAGE: ubuntu2004
+ MAKE_CHECK_ARGS: check-acceptance
+ <<: *post_acceptance
+
+build-system-fedora-alt:
+ <<: *native_build_job_definition
+ variables:
+ IMAGE: fedora
+ TARGETS: tricore-softmmu unicore32-softmmu microblaze-softmmu mips-softmmu
+ riscv32-softmmu s390x-softmmu sh4-softmmu sparc64-softmmu x86_64-softmmu
+ xtensa-softmmu nios2-softmmu or1k-softmmu
+ MAKE_CHECK_ARGS: check-build
+ artifacts:
+ paths:
+ - build
+
+check-system-fedora-alt:
+ <<: *native_test_job_definition
+ needs:
+ - job: build-system-fedora-alt
+ artifacts: true
+ variables:
+ IMAGE: fedora
+ MAKE_CHECK_ARGS: check
+
+acceptance-system-fedora-alt:
+ <<: *native_test_job_definition
+ needs:
+ - job: build-system-fedora-alt
+ artifacts: true
+ variables:
+ IMAGE: fedora
+ MAKE_CHECK_ARGS: check-acceptance
+ <<: *post_acceptance
build-disabled:
- image: fedora:latest
- <<: *before_script_dnf
- script:
- - mkdir build
- - cd build
- - ../configure --enable-werror --disable-rdma --disable-slirp --disable-curl
+ <<: *native_build_job_definition
+ 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
- --target-list="i386-softmmu ppc64-softmmu mips64-softmmu i386-linux-user"
- - make -j"$JOBS"
- - make -j"$JOBS" check-qtest SPEED=slow
+ TARGETS: i386-softmmu ppc64-softmmu mips64-softmmu i386-linux-user
+ MAKE_CHECK_ARGS: check-qtest SPEED=slow
build-tcg-disabled:
- image: centos:8
- <<: *before_script_dnf
- script:
- - dnf install -y clang gtk3-devel libusbx-devel libgcrypt-devel
- - mkdir build
- - cd build
- - ../configure --cc=clang --enable-werror --disable-tcg --audio-drv-list=""
- - make -j"$JOBS"
- - make check-unit
- - make check-qapi-schema
- - cd tests/qemu-iotests/
- - ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048
+ <<: *native_build_job_definition
+ variables:
+ IMAGE: centos8
+ script:
+ - mkdir build
+ - cd build
+ - ../configure --disable-tcg --audio-drv-list=""
+ - make -j"$JOBS"
+ - make check-unit
+ - make check-qapi-schema
+ - cd tests/qemu-iotests/
+ - ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048
052 063 077 086 101 104 106 113 148 150 151 152 157 159 160 163
170 171 183 184 192 194 197 208 215 221 222 226 227 236 253 277
- - ./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 067 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:
- <<: *before_script_apt
- script:
- - mkdir build
- - cd build
- - ../configure --enable-werror --disable-system --disable-guest-agent
- --disable-capstone --disable-slirp --disable-fdt
- - make -j"$JOBS"
- - make run-tcg-tests-i386-linux-user run-tcg-tests-x86_64-linux-user
+ <<: *native_build_job_definition
+ variables:
+ IMAGE: debian-all-test-cross
+ CONFIGURE_ARGS: --disable-tools --disable-system
+ MAKE_CHECK_ARGS: check-tcg
build-clang:
- image: fedora:latest
- <<: *before_script_dnf
- script:
- - yum install -y clang SDL2-devel libattr-devel libcap-ng-devel xfsprogs-devel
- libiscsi-devel libnfs-devel libseccomp-devel gnutls-devel librbd-devel
- - mkdir build
- - cd build
- - ../configure --cc=clang --cxx=clang++ --enable-werror
- --target-list="alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu
- ppc-softmmu s390x-softmmu x86_64-softmmu arm-linux-user"
- - make -j"$JOBS"
- - make -j"$JOBS" check
+ <<: *native_build_job_definition
+ variables:
+ IMAGE: fedora
+ CONFIGURE_ARGS: --cc=clang --cxx=clang++
+ TARGETS: alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu
+ ppc-softmmu s390x-softmmu arm-linux-user
+ MAKE_CHECK_ARGS: check
+
+build-fuzzer:
+ <<: *native_build_job_definition
+ variables:
+ IMAGE: fedora
+ script:
+ - mkdir build
+ - cd build
+ - ../configure --cc=clang --cxx=clang++ --enable-fuzzing
+ --enable-sanitizers --target-list=x86_64-softmmu
+ - make -j"$JOBS" all check-build x86_64-softmmu/fuzz
+ - make check
+ - for fuzzer in i440fx-qos-fork-fuzz i440fx-qos-noreset-fuzz
+ i440fx-qtest-reboot-fuzz virtio-scsi-flags-fuzz virtio-scsi-fuzz ; do
+ echo Testing ${fuzzer} ... ;
+ x86_64-softmmu/qemu-fuzz-x86_64 --fuzz-target=${fuzzer} -runs=1000
+ || exit 1 ;
+ done
build-tci:
- image: centos:8
- <<: *before_script_dnf
- 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)"
- - 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}" ;
- ./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
+ <<: *native_build_job_definition
+ variables:
+ IMAGE: fedora
+ 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)"
+ - 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}" ;
+ ./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
- IMAGE=debian-ppc64el-cross
TARGET_LIST=ppc64-softmmu,ppc64-linux-user,ppc64abi32-linux-user
build:
- pre_ci:
- - make docker-image-${IMAGE} V=1
pre_ci_boot:
- image_name: qemu
- image_tag: ${IMAGE}
- pull: false
+ image_name: registry.gitlab.com/qemu-project/qemu/${IMAGE}
+ image_tag: latest
+ pull: true
options: "-e HOME=/root"
ci:
- unset CC
python: 3.6
- # Acceptance (Functional) tests
- - name: "GCC check-acceptance"
- dist: bionic
- env:
- - CONFIG="--enable-tools --target-list=aarch64-softmmu,alpha-softmmu,arm-softmmu,m68k-softmmu,microblaze-softmmu,mips-softmmu,mips64el-softmmu,nios2-softmmu,or1k-softmmu,ppc-softmmu,ppc64-softmmu,s390x-softmmu,sh4-softmmu,sparc-softmmu,x86_64-softmmu,xtensa-softmmu"
- - TEST_CMD="make check-acceptance"
- - CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-acceptance"
- after_script:
- - 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 $HOME/avocado/data/cache
- addons:
- apt:
- packages:
- - python3-pil
- - python3-pip
- - python3-numpy
- - python3-opencv
- - python3-venv
- - rpm2cpio
- - tesseract-ocr
- - tesseract-ocr-eng
-
-
# Using newer GCC with sanitizers
- name: "GCC9 with sanitizers (softmmu)"
addons:
# 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"
+ - 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"
$(exit $BUILD_RC);
fi
+ - name: "[s390x] GCC (other-softmmu)"
+ arch: s390x
+ dist: bionic
+ addons:
+ apt_packages:
+ - libaio-dev
+ - libattr1-dev
+ - libcap-ng-dev
+ - libgnutls28-dev
+ - libiscsi-dev
+ - liblttng-ust-dev
+ - liblzo2-dev
+ - libncurses-dev
+ - libnfs-dev
+ - libnss3-dev
+ - libpixman-1-dev
+ - libsdl2-dev
+ - libsdl2-image-dev
+ - libseccomp-dev
+ - libsnappy-dev
+ - libzstd-dev
+ - nettle-dev
+ - xfslibs-dev
+ # Tests dependencies
+ - genisoimage
+ env:
+ - CONFIG="--disable-containers --audio-drv-list=sdl --disable-user
+ --target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
+
+ - name: "[s390x] GCC (user)"
+ arch: s390x
+ dist: bionic
+ addons:
+ apt_packages:
+ - libgcrypt20-dev
+ - libgnutls28-dev
+ env:
+ - CONFIG="--disable-containers --disable-system"
+
- name: "[s390x] Clang (disable-tcg)"
arch: s390x
dist: bionic
--- /dev/null
+source Kconfig.host
+source backends/Kconfig
+source accel/Kconfig
+source hw/Kconfig
# down to Kconfig. See also MINIKCONF_ARGS in the Makefile:
# these two need to be kept in sync.
-config KVM
- bool
-
config LINUX
bool
bool
select VHOST
-config XEN
- bool
- select FSDEV_9P if VIRTFS
-
config VIRTFS
bool
M: Richard Henderson <rth@twiddle.net>
R: Paolo Bonzini <pbonzini@redhat.com>
S: Maintained
-F: cpus.c
+F: softmmu/cpus.c
F: cpus-common.c
F: exec.c
F: accel/tcg/
F: hw/arm/smmu*
F: include/hw/arm/smmu*
+AVR TCG CPUs
+M: Michael Rolnik <mrolnik@gmail.com>
+R: Sarah Harris <S.E.Harris@kent.ac.uk>
+S: Maintained
+F: gdb-xml/avr-cpu.xml
+F: target/avr/
+F: tests/acceptance/machine_avr6.py
+
CRIS TCG CPUs
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
S: Maintained
MIPS TCG CPUs
M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
R: Aurelien Jarno <aurelien@aurel32.net>
+R: Jiaxun Yang <jiaxun.yang@flygoat.com>
R: Aleksandar Rikalo <aleksandar.rikalo@syrmia.com>
S: Maintained
F: target/mips/
M: Paolo Bonzini <pbonzini@redhat.com>
L: kvm@vger.kernel.org
S: Supported
-F: */kvm.*
+F: */*/kvm*
F: accel/kvm/
F: accel/stubs/kvm-stub.c
F: include/hw/kvm/
F: target/arm/kvm.c
MIPS KVM CPUs
+M: Huacai Chen <chenhc@lemote.com>
M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
S: Odd Fixes
F: target/mips/kvm.c
F: target/i386/kvm.c
F: scripts/kvm/vmxcap
+Guest CPU Cores (other accelerators)
+------------------------------------
+Overall
+M: Richard Henderson <rth@twiddle.net>
+R: Paolo Bonzini <pbonzini@redhat.com>
+S: Maintained
+F: include/sysemu/accel.h
+F: accel/accel.c
+F: accel/Makefile.objs
+F: accel/stubs/Makefile.objs
+
X86 HVF CPUs
+M: Cameron Esfahani <dirty@apple.com>
M: Roman Bolshakov <r.bolshakov@yadro.com>
+W: https://wiki.qemu.org/Features/HVF
S: Maintained
F: accel/stubs/hvf-stub.c
F: target/i386/hvf/
L: haxm-team@intel.com
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: include/hw/*/microbit*.h
F: tests/qtest/microbit-test.c
+AVR Machines
+-------------
+
+AVR MCUs
+M: Michael Rolnik <mrolnik@gmail.com>
+R: Sarah Harris <S.E.Harris@kent.ac.uk>
+S: Maintained
+F: default-configs/avr-softmmu.mak
+F: hw/avr/
+F: include/hw/char/avr_usart.h
+F: hw/char/avr_usart.c
+F: include/hw/timer/avr_timer16.h
+F: hw/timer/avr_timer16.c
+F: include/hw/misc/avr_power.h
+F: hw/misc/avr_power.c
+
+Arduino
+M: Philippe Mathieu-Daudé <f4bug@amsat.org>
+R: Sarah Harris <S.E.Harris@kent.ac.uk>
+S: Maintained
+F: hw/avr/arduino.c
+
CRIS Machines
-------------
Axis Dev88
SD (Secure Card)
M: Philippe Mathieu-Daudé <f4bug@amsat.org>
+L: qemu-block@nongnu.org
S: Odd Fixes
F: include/hw/sd/sd*
F: hw/sd/core.c
S: Maintained
F: hw/virtio/virtio-balloon*.c
F: include/hw/virtio/virtio-balloon.h
-F: balloon.c
+F: softmmu/balloon.c
F: include/sysemu/balloon.h
virtio-9p
M: Paolo Bonzini <pbonzini@redhat.com>
S: Supported
F: include/exec/ioport.h
-F: ioport.c
F: include/exec/memop.h
F: include/exec/memory.h
F: include/exec/ram_addr.h
F: include/exec/ramblock.h
-F: memory.c
+F: softmmu/ioport.c
+F: softmmu/memory.c
F: include/exec/memory-internal.h
F: exec.c
F: scripts/coccinelle/memory-region-housekeeping.cocci
Main loop
M: Paolo Bonzini <pbonzini@redhat.com>
S: Maintained
-F: cpus.c
F: include/qemu/main-loop.h
F: include/sysemu/runstate.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: qapi/run-state.json
Human Monitor (HMP)
M: Laurent Vivier <lvivier@redhat.com>
R: Paolo Bonzini <pbonzini@redhat.com>
S: Maintained
-F: qtest.c
+F: softmmu/qtest.c
F: accel/qtest.c
F: tests/qtest/
X: tests/qtest/bios-tables-test-allowed-diff.h
F: hw/i386/intel_iommu_internal.h
F: include/hw/i386/intel_iommu.h
+OpenSBI Firmware
+M: Bin Meng <bmeng.cn@gmail.com>
+S: Supported
+F: pc-bios/opensbi-*
+F: .gitlab-ci.d/opensbi.yml
+F: .gitlab-ci.d/opensbi/
+
Usermode Emulation
------------------
Overall usermode emulation
MIPS TCG target
M: Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
R: Aurelien Jarno <aurelien@aurel32.net>
+R: Huacai Chen <chenhc@lemote.com>
+R: Jiaxun Yang <jiaxun.yang@flygoat.com>
R: Aleksandar Rikalo <aleksandar.rikalo@syrmia.com>
S: Maintained
F: tcg/mips/
vvfat
M: Kevin Wolf <kwolf@redhat.com>
L: qemu-block@nongnu.org
-S: Supported
+S: Odd Fixes
F: block/vvfat.c
Image format fuzzer
# This has to be kept in sync with Kconfig.host.
MINIKCONF_ARGS = \
$(CONFIG_MINIKCONF_MODE) \
- $@ $*/config-devices.mak.d $< $(MINIKCONF_INPUTS) \
+ $@ $*/config-devices.mak.d $< $(SRC_PATH)/Kconfig \
+ CONFIG_TCG=$(CONFIG_TCG) \
CONFIG_KVM=$(CONFIG_KVM) \
CONFIG_SPICE=$(CONFIG_SPICE) \
CONFIG_IVSHMEM=$(CONFIG_IVSHMEM) \
CONFIG_LINUX=$(CONFIG_LINUX) \
CONFIG_PVRDMA=$(CONFIG_PVRDMA)
-MINIKCONF_INPUTS = $(SRC_PATH)/Kconfig.host $(SRC_PATH)/backends/Kconfig $(SRC_PATH)/hw/Kconfig
-MINIKCONF_DEPS = $(MINIKCONF_INPUTS) $(wildcard $(SRC_PATH)/hw/*/Kconfig)
-MINIKCONF = $(PYTHON) $(SRC_PATH)/scripts/minikconf.py \
+MINIKCONF = $(PYTHON) $(SRC_PATH)/scripts/minikconf.py
-$(SUBDIR_DEVICES_MAK): %/config-devices.mak: default-configs/%.mak $(MINIKCONF_DEPS) $(BUILD_DIR)/config-host.mak
- $(call quiet-command, $(MINIKCONF) $(MINIKCONF_ARGS) > $@.tmp, "GEN", "$@.tmp")
+$(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 $@; \
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)
#########################################################
# System emulator target
ifdef CONFIG_SOFTMMU
-obj-y += arch_init.o cpus.o gdbstub.o balloon.o ioport.o
-obj-y += qtest.o
+obj-y += softmmu/
+obj-y += gdbstub.o
obj-y += dump/
obj-y += hw/
obj-y += monitor/
obj-y += qapi/
-obj-y += memory.o
-obj-y += memory_mapping.o
obj-y += migration/ram.o
-obj-y += softmmu/
LIBS := $(libs_softmmu) $(LIBS)
# Hardware support
--- /dev/null
+config TCG
+ bool
+
+config KVM
+ bool
+
+config XEN
+ bool
+ select FSDEV_9P if VIRTFS
void tlb_set_dirty(CPUState *cpu, target_ulong vaddr)
{
}
+
+void *probe_access(CPUArchState *env, target_ulong addr, int size,
+ MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
+{
+ /* Handled by hardware accelerator. */
+ g_assert_not_reached();
+}
cc->do_interrupt(cpu);
qemu_mutex_unlock_iothread();
cpu->exception_index = -1;
+
+ if (unlikely(cpu->singlestep_enabled)) {
+ /*
+ * After processing the exception, ensure an EXCP_DEBUG is
+ * raised when single-stepping so that GDB doesn't miss the
+ * next instruction.
+ */
+ *ret = EXCP_DEBUG;
+ cpu_handle_debug_exception(cpu);
+ return true;
+ }
} else if (!replay_has_interrupt()) {
/* give a chance to iothread in replay mode */
*ret = EXCP_INTERRUPT;
else {
if (cc->cpu_exec_interrupt(cpu, interrupt_request)) {
replay_interrupt();
- cpu->exception_index = -1;
+ /*
+ * After processing the interrupt, ensure an EXCP_DEBUG is
+ * raised when single-stepping so that GDB doesn't miss the
+ * next instruction.
+ */
+ cpu->exception_index =
+ (cpu->singlestep_enabled ? EXCP_DEBUG : -1);
*last_tb = NULL;
}
/* The target hook may have updated the 'cpu->interrupt_request';
return val;
}
+/*
+ * Save a potentially trashed IOTLB entry for later lookup by plugin.
+ *
+ * We also need to track the thread storage address because the RCU
+ * cleanup that runs when we leave the critical region (the current
+ * execution) is actually in a different thread.
+ */
+static void save_iotlb_data(CPUState *cs, hwaddr addr,
+ MemoryRegionSection *section, hwaddr mr_offset)
+{
+#ifdef CONFIG_PLUGIN
+ SavedIOTLB *saved = &cs->saved_iotlb;
+ saved->addr = addr;
+ saved->section = section;
+ saved->mr_offset = mr_offset;
+#endif
+}
+
static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
int mmu_idx, uint64_t val, target_ulong addr,
uintptr_t retaddr, MemOp op)
}
cpu->mem_io_pc = retaddr;
+ /*
+ * The memory_region_dispatch may trigger a flush/resize
+ * so for plugins we save the iotlb_data just in case.
+ */
+ save_iotlb_data(cpu, iotlbentry->addr, section, mr_offset);
+
if (mr->global_locking && !qemu_mutex_iothread_locked()) {
qemu_mutex_lock_iothread();
locked = true;
* in the softmmu lookup code (or helper). We don't handle re-fills or
* checking the victim table. This is purely informational.
*
- * This should never fail as the memory access being instrumented
- * should have just filled the TLB.
+ * This almost never fails as the memory access being instrumented
+ * should have just filled the TLB. The one corner case is io_writex
+ * which can cause TLB flushes and potential resizing of the TLBs
+ * loosing the information we need. In those cases we need to recover
+ * data from a copy of the io_tlb entry.
*/
bool tlb_plugin_lookup(CPUState *cpu, target_ulong addr, int mmu_idx,
data->v.ram.hostaddr = addr + tlbe->addend;
}
return true;
+ } else {
+ SavedIOTLB *saved = &cpu->saved_iotlb;
+ data->is_io = true;
+ data->v.io.section = saved->section;
+ data->v.io.offset = saved->mr_offset;
+ return true;
}
- return false;
}
#endif
+++ /dev/null
-/*
- * QEMU System Emulator
- *
- * Copyright (c) 2003-2008 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.
- */
-#include "qemu/osdep.h"
-#include "cpu.h"
-#include "sysemu/sysemu.h"
-#include "sysemu/arch_init.h"
-#include "hw/pci/pci.h"
-#include "hw/audio/soundhw.h"
-#include "qapi/error.h"
-#include "qemu/config-file.h"
-#include "qemu/error-report.h"
-#include "hw/acpi/acpi.h"
-#include "qemu/help_option.h"
-
-#ifdef TARGET_SPARC
-int graphic_width = 1024;
-int graphic_height = 768;
-int graphic_depth = 8;
-#elif defined(TARGET_M68K)
-int graphic_width = 800;
-int graphic_height = 600;
-int graphic_depth = 8;
-#else
-int graphic_width = 800;
-int graphic_height = 600;
-int graphic_depth = 32;
-#endif
-
-
-#if defined(TARGET_ALPHA)
-#define QEMU_ARCH QEMU_ARCH_ALPHA
-#elif defined(TARGET_ARM)
-#define QEMU_ARCH QEMU_ARCH_ARM
-#elif defined(TARGET_CRIS)
-#define QEMU_ARCH QEMU_ARCH_CRIS
-#elif defined(TARGET_HPPA)
-#define QEMU_ARCH QEMU_ARCH_HPPA
-#elif defined(TARGET_I386)
-#define QEMU_ARCH QEMU_ARCH_I386
-#elif defined(TARGET_LM32)
-#define QEMU_ARCH QEMU_ARCH_LM32
-#elif defined(TARGET_M68K)
-#define QEMU_ARCH QEMU_ARCH_M68K
-#elif defined(TARGET_MICROBLAZE)
-#define QEMU_ARCH QEMU_ARCH_MICROBLAZE
-#elif defined(TARGET_MIPS)
-#define QEMU_ARCH QEMU_ARCH_MIPS
-#elif defined(TARGET_MOXIE)
-#define QEMU_ARCH QEMU_ARCH_MOXIE
-#elif defined(TARGET_NIOS2)
-#define QEMU_ARCH QEMU_ARCH_NIOS2
-#elif defined(TARGET_OPENRISC)
-#define QEMU_ARCH QEMU_ARCH_OPENRISC
-#elif defined(TARGET_PPC)
-#define QEMU_ARCH QEMU_ARCH_PPC
-#elif defined(TARGET_RISCV)
-#define QEMU_ARCH QEMU_ARCH_RISCV
-#elif defined(TARGET_RX)
-#define QEMU_ARCH QEMU_ARCH_RX
-#elif defined(TARGET_S390X)
-#define QEMU_ARCH QEMU_ARCH_S390X
-#elif defined(TARGET_SH4)
-#define QEMU_ARCH QEMU_ARCH_SH4
-#elif defined(TARGET_SPARC)
-#define QEMU_ARCH QEMU_ARCH_SPARC
-#elif defined(TARGET_TRICORE)
-#define QEMU_ARCH QEMU_ARCH_TRICORE
-#elif defined(TARGET_UNICORE32)
-#define QEMU_ARCH QEMU_ARCH_UNICORE32
-#elif defined(TARGET_XTENSA)
-#define QEMU_ARCH QEMU_ARCH_XTENSA
-#endif
-
-const uint32_t arch_type = QEMU_ARCH;
-
-int kvm_available(void)
-{
-#ifdef CONFIG_KVM
- return 1;
-#else
- return 0;
-#endif
-}
-
-int xen_available(void)
-{
-#ifdef CONFIG_XEN
- return 1;
-#else
- return 0;
-#endif
-}
len, dst);
break;
}
+ break;
}
pos += nread;
+++ /dev/null
-/*
- * Generic Balloon handlers and management
- *
- * Copyright (c) 2003-2008 Fabrice Bellard
- * Copyright (C) 2011 Red Hat, Inc.
- * Copyright (C) 2011 Amit Shah <amit.shah@redhat.com>
- *
- * 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/atomic.h"
-#include "sysemu/kvm.h"
-#include "sysemu/balloon.h"
-#include "trace-root.h"
-#include "qapi/error.h"
-#include "qapi/qapi-commands-misc.h"
-#include "qapi/qmp/qerror.h"
-
-static QEMUBalloonEvent *balloon_event_fn;
-static QEMUBalloonStatus *balloon_stat_fn;
-static void *balloon_opaque;
-
-static bool have_balloon(Error **errp)
-{
- if (kvm_enabled() && !kvm_has_sync_mmu()) {
- error_set(errp, ERROR_CLASS_KVM_MISSING_CAP,
- "Using KVM without synchronous MMU, balloon unavailable");
- return false;
- }
- if (!balloon_event_fn) {
- error_set(errp, ERROR_CLASS_DEVICE_NOT_ACTIVE,
- "No balloon device has been activated");
- return false;
- }
- return true;
-}
-
-int qemu_add_balloon_handler(QEMUBalloonEvent *event_func,
- QEMUBalloonStatus *stat_func, void *opaque)
-{
- if (balloon_event_fn || balloon_stat_fn || balloon_opaque) {
- /* We're already registered one balloon handler. How many can
- * a guest really have?
- */
- return -1;
- }
- balloon_event_fn = event_func;
- balloon_stat_fn = stat_func;
- balloon_opaque = opaque;
- return 0;
-}
-
-void qemu_remove_balloon_handler(void *opaque)
-{
- if (balloon_opaque != opaque) {
- return;
- }
- balloon_event_fn = NULL;
- balloon_stat_fn = NULL;
- balloon_opaque = NULL;
-}
-
-BalloonInfo *qmp_query_balloon(Error **errp)
-{
- BalloonInfo *info;
-
- if (!have_balloon(errp)) {
- return NULL;
- }
-
- info = g_malloc0(sizeof(*info));
- balloon_stat_fn(balloon_opaque, info);
- return info;
-}
-
-void qmp_balloon(int64_t target, Error **errp)
-{
- if (!have_balloon(errp)) {
- return;
- }
-
- if (target <= 0) {
- error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "target", "a size");
- return;
- }
-
- trace_balloon_event(balloon_opaque, target);
- balloon_event_fn(balloon_opaque, target);
-}
}
ret = bdrv_change_backing_file(parent, filename,
- base->drv ? base->drv->format_name : "");
+ base->drv ? base->drv->format_name : "",
+ false);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not update backing file link");
}
return -EPERM;
}
+ /*
+ * Unaligned requests will automatically be aligned to bl.request_alignment
+ * and without RESIZE we can't extend requests to write to space beyond the
+ * end of the image, so it's required that the image size is aligned.
+ */
+ if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) &&
+ !(cumulative_perms & BLK_PERM_RESIZE))
+ {
+ if ((bs->total_sectors * BDRV_SECTOR_SIZE) % bs->bl.request_alignment) {
+ error_setg(errp, "Cannot get 'write' permission without 'resize': "
+ "Image size is not a multiple of request "
+ "alignment");
+ return -EPERM;
+ }
+ }
+
/* Check this node */
if (!drv) {
return 0;
* image file header
* -ENOTSUP - format driver doesn't support changing the backing file
*/
-int bdrv_change_backing_file(BlockDriverState *bs,
- const char *backing_file, const char *backing_fmt)
+int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file,
+ const char *backing_fmt, bool warn)
{
BlockDriver *drv = bs->drv;
int ret;
return -EINVAL;
}
+ if (warn && backing_file && !backing_fmt) {
+ warn_report("Deprecated use of backing file without explicit "
+ "backing format, use of this image requires "
+ "potentially unsafe format probing");
+ }
+
if (drv->bdrv_change_backing_file != NULL) {
ret = drv->bdrv_change_backing_file(bs, backing_file, backing_fmt);
} else {
bs = bdrv_open(full_backing, NULL, backing_options, back_flags,
&local_err);
g_free(full_backing);
- if (!bs && size != -1) {
- /* Couldn't open BS, but we have a size, so it's nonfatal */
- warn_reportf_err(local_err,
- "Could not verify backing image. "
- "This may become an error in future versions.\n");
- local_err = NULL;
- } else if (!bs) {
- /* Couldn't open bs, do not have size */
- error_append_hint(&local_err,
- "Could not open backing image to determine size.\n");
+ if (!bs) {
+ error_append_hint(&local_err, "Could not open backing image.\n");
goto out;
} else {
+ if (!backing_fmt) {
+ warn_report("Deprecated use of backing file without explicit "
+ "backing format (detected format of %s)",
+ bs->drv->format_name);
+ if (bs->drv != &bdrv_raw) {
+ /*
+ * A probe of raw deserves the most attention:
+ * leaving the backing format out of the image
+ * will ensure bs->probed is set (ensuring we
+ * don't accidentally commit into the backing
+ * file), and allow more spots to warn the users
+ * to fix their toolchain when opening this image
+ * later. For other images, we can safely record
+ * the format that we probed.
+ */
+ backing_fmt = bs->drv->format_name;
+ qemu_opt_set(opts, BLOCK_OPT_BACKING_FMT, backing_fmt,
+ NULL);
+ }
+ }
if (size == -1) {
/* Opened BS, have no size */
size = bdrv_getlength(bs);
}
bdrv_unref(bs);
}
- } /* (backing_file && !(flags & BDRV_O_NO_BACKING)) */
+ /* (backing_file && !(flags & BDRV_O_NO_BACKING)) */
+ } else if (backing_file && !backing_fmt) {
+ warn_report("Deprecated use of unopened backing file without "
+ "explicit backing format, use of this image requires "
+ "potentially unsafe format probing");
+ }
if (size == -1) {
error_setg(errp, "Image creation needs a size parameter");
printf("Formatting '%s', fmt=%s ", filename, fmt);
qemu_opts_print(opts, " ");
puts("");
+ fflush(stdout);
}
ret = bdrv_create(drv, filename, opts, &local_err);
block-obj-$(CONFIG_CURL) += curl.o
block-obj-$(CONFIG_RBD) += rbd.o
block-obj-$(CONFIG_GLUSTERFS) += gluster.o
-block-obj-$(CONFIG_VXHS) += vxhs.o
block-obj-$(CONFIG_LIBSSH) += ssh.o
block-obj-y += accounting.o dirty-bitmap.o
block-obj-y += write-threshold.o
rbd.o-libs := $(RBD_LIBS)
gluster.o-cflags := $(GLUSTERFS_CFLAGS)
gluster.o-libs := $(GLUSTERFS_LIBS)
-vxhs.o-libs := $(VXHS_LIBS)
ssh.o-cflags := $(LIBSSH_CFLAGS)
ssh.o-libs := $(LIBSSH_LIBS)
block-obj-dmg-bz2-$(CONFIG_BZIP2) += dmg-bz2.o
{
ThrottleGroupMember *tgm = &blk->public.throttle_group_member;
BlockDriverState *bs;
+ BdrvChild *root;
notifier_list_notify(&blk->remove_bs_notifiers, blk);
if (tgm->throttle_state) {
* to avoid that and a potential QEMU crash.
*/
blk_drain(blk);
- bdrv_root_unref_child(blk->root);
+ root = blk->root;
blk->root = NULL;
+ bdrv_root_unref_child(root);
}
/*
#include "block/block_int.h"
#include "qemu/module.h"
#include "qemu/option.h"
+#include "qemu/units.h"
#include "trace.h"
#include "block/thread-pool.h"
#include "qemu/iov.h"
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/syscall.h>
+#include <sys/vfs.h>
#include <linux/cdrom.h>
#include <linux/fd.h>
#include <linux/fs.h>
#include <linux/hdreg.h>
+#include <linux/magic.h>
#include <scsi/sg.h>
#ifdef __s390__
#include <asm/dasd.h>
#endif
}
+/*
+ * Returns true if no alignment restrictions are necessary even for files
+ * opened with O_DIRECT.
+ *
+ * raw_probe_alignment() probes the required alignment and assume that 1 means
+ * the probing failed, so it falls back to a safe default of 4k. This can be
+ * avoided if we know that byte alignment is okay for the file.
+ */
+static bool dio_byte_aligned(int fd)
+{
+#ifdef __linux__
+ struct statfs buf;
+ int ret;
+
+ ret = fstatfs(fd, &buf);
+ if (ret == 0 && buf.f_type == NFS_SUPER_MAGIC) {
+ return true;
+ }
+#endif
+ return false;
+}
+
/* Check if read is allowed with given memory buffer and length.
*
* This function is used to check O_DIRECT memory buffer and request alignment.
}
}
+static int check_hdev_writable(int fd)
+{
+#if defined(BLKROGET)
+ /* Linux block devices can be configured "read-only" using blockdev(8).
+ * This is independent of device node permissions and therefore open(2)
+ * with O_RDWR succeeds. Actual writes fail with EPERM.
+ *
+ * bdrv_open() is supposed to fail if the disk is read-only. Explicitly
+ * check for read-only block devices so that Linux block devices behave
+ * properly.
+ */
+ struct stat st;
+ int readonly = 0;
+
+ if (fstat(fd, &st)) {
+ return -errno;
+ }
+
+ if (!S_ISBLK(st.st_mode)) {
+ return 0;
+ }
+
+ if (ioctl(fd, BLKROGET, &readonly) < 0) {
+ return -errno;
+ }
+
+ if (readonly) {
+ return -EACCES;
+ }
+#endif /* defined(BLKROGET) */
+ return 0;
+}
+
static void raw_parse_flags(int bdrv_flags, int *open_flags, bool has_writers)
{
bool read_write = false;
}
s->fd = fd;
+ /* Check s->open_flags rather than bdrv_flags due to auto-read-only */
+ if (s->open_flags & O_RDWR) {
+ ret = check_hdev_writable(s->fd);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "The device is not writable");
+ goto fail;
+ }
+ }
+
s->perm = 0;
s->shared_perm = BLK_PERM_ALL;
s->has_discard = true;
s->has_write_zeroes = true;
- if ((bs->open_flags & BDRV_O_NOCACHE) != 0) {
+ if ((bs->open_flags & BDRV_O_NOCACHE) != 0 && !dio_byte_aligned(s->fd)) {
s->needs_alignment = true;
}
}
ret = 0;
fail:
+ if (ret < 0 && s->fd != -1) {
+ qemu_close(s->fd);
+ }
if (filename && (bdrv_flags & BDRV_O_TEMPORARY)) {
unlink(filename);
}
}
}
+ if (fd != -1 && (*open_flags & O_RDWR)) {
+ ret = check_hdev_writable(fd);
+ if (ret < 0) {
+ qemu_close(fd);
+ error_setg_errno(errp, -ret, "The device is not writable");
+ return -1;
+ }
+ }
+
return fd;
}
if (!file_opts->has_preallocation) {
file_opts->preallocation = PREALLOC_MODE_OFF;
}
+ if (!file_opts->has_extent_size_hint) {
+ file_opts->extent_size_hint = 1 * MiB;
+ }
+ if (file_opts->extent_size_hint > UINT32_MAX) {
+ result = -EINVAL;
+ error_setg(errp, "Extent size hint is too large");
+ goto out;
+ }
/* Create file */
fd = qemu_open(file_opts->filename, O_RDWR | O_CREAT | O_BINARY, 0644);
}
#endif
}
+#ifdef FS_IOC_FSSETXATTR
+ /*
+ * Try to set the extent size hint. Failure is not fatal, and a warning is
+ * only printed if the option was explicitly specified.
+ */
+ {
+ struct fsxattr attr;
+ result = ioctl(fd, FS_IOC_FSGETXATTR, &attr);
+ if (result == 0) {
+ attr.fsx_xflags |= FS_XFLAG_EXTSIZE;
+ attr.fsx_extsize = file_opts->extent_size_hint;
+ result = ioctl(fd, FS_IOC_FSSETXATTR, &attr);
+ }
+ if (result < 0 && file_opts->has_extent_size_hint &&
+ file_opts->extent_size_hint)
+ {
+ warn_report("Failed to set extent size hint: %s",
+ strerror(errno));
+ }
+ }
+#endif
/* Resize and potentially preallocate the file to the desired
* final size */
{
BlockdevCreateOptions options;
int64_t total_size = 0;
+ int64_t extent_size_hint = 0;
+ bool has_extent_size_hint = false;
bool nocow = false;
PreallocMode prealloc;
char *buf = NULL;
/* Read out options */
total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
BDRV_SECTOR_SIZE);
+ if (qemu_opt_get(opts, BLOCK_OPT_EXTENT_SIZE_HINT)) {
+ has_extent_size_hint = true;
+ extent_size_hint =
+ qemu_opt_get_size_del(opts, BLOCK_OPT_EXTENT_SIZE_HINT, -1);
+ }
nocow = qemu_opt_get_bool(opts, BLOCK_OPT_NOCOW, false);
buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
prealloc = qapi_enum_parse(&PreallocMode_lookup, buf,
.preallocation = prealloc,
.has_nocow = true,
.nocow = nocow,
+ .has_extent_size_hint = has_extent_size_hint,
+ .extent_size_hint = extent_size_hint,
},
};
return raw_co_create(&options, errp);
#endif
", full)"
},
+ {
+ .name = BLOCK_OPT_EXTENT_SIZE_HINT,
+ .type = QEMU_OPT_SIZE,
+ .help = "Extent size hint for the image file, 0 to disable"
+ },
{ /* end of list */ }
}
};
return 0;
}
-static int check_hdev_writable(BDRVRawState *s)
-{
-#if defined(BLKROGET)
- /* Linux block devices can be configured "read-only" using blockdev(8).
- * This is independent of device node permissions and therefore open(2)
- * with O_RDWR succeeds. Actual writes fail with EPERM.
- *
- * bdrv_open() is supposed to fail if the disk is read-only. Explicitly
- * check for read-only block devices so that Linux block devices behave
- * properly.
- */
- struct stat st;
- int readonly = 0;
-
- if (fstat(s->fd, &st)) {
- return -errno;
- }
-
- if (!S_ISBLK(st.st_mode)) {
- return 0;
- }
-
- if (ioctl(s->fd, BLKROGET, &readonly) < 0) {
- return -errno;
- }
-
- if (readonly) {
- return -EACCES;
- }
-#endif /* defined(BLKROGET) */
- return 0;
-}
-
static void hdev_parse_filename(const char *filename, QDict *options,
Error **errp)
{
/* Since this does ioctl the device must be already opened */
bs->sg = hdev_is_sg(bs);
- if (flags & BDRV_O_RDWR) {
- ret = check_hdev_writable(s);
- if (ret < 0) {
- raw_close(bs);
- error_setg_errno(errp, -ret, "The device is not writable");
- return ret;
- }
- }
-
return ret;
}
iTask->status = status;
iTask->do_retry = 0;
+ iTask->err_code = 0;
iTask->task = task;
if (status != SCSI_STATUS_GOOD) {
+ iTask->err_code = -EIO;
if (iTask->retries++ < ISCSI_CMD_RETRIES) {
if (status == SCSI_STATUS_BUSY ||
status == SCSI_STATUS_TIMEOUT ||
timer_mod(&iTask->retry_timer,
qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + retry_time);
iTask->do_retry = 1;
- }
- } else if (status == SCSI_STATUS_CHECK_CONDITION) {
- int error = iscsi_translate_sense(&task->sense);
- if (error == EAGAIN) {
- error_report("iSCSI CheckCondition: %s",
- iscsi_get_error(iscsi));
- iTask->do_retry = 1;
- } else {
- iTask->err_code = -error;
- iTask->err_str = g_strdup(iscsi_get_error(iscsi));
+ } else if (status == SCSI_STATUS_CHECK_CONDITION) {
+ int error = iscsi_translate_sense(&task->sense);
+ if (error == EAGAIN) {
+ error_report("iSCSI CheckCondition: %s",
+ iscsi_get_error(iscsi));
+ iTask->do_retry = 1;
+ } else {
+ iTask->err_code = -error;
+ iTask->err_str = g_strdup(iscsi_get_error(iscsi));
+ }
}
}
}
len = snprintf(bs->exact_filename, sizeof(bs->exact_filename),
"nbd://%s:%s", host, port);
}
- if (len > sizeof(bs->exact_filename)) {
+ if (len >= sizeof(bs->exact_filename)) {
/* Name is too long to represent exactly, so leave it empty. */
bs->exact_filename[0] = '\0';
}
{
BlockdevCreateOptions *create_options = NULL;
BlockDriverState *bs = NULL;
- QDict *qdict;
+ QDict *qdict = NULL;
Visitor *v;
const char *val;
int ret;
+ char *backing_fmt;
static const QDictRenames opt_renames[] = {
{ BLOCK_OPT_BACKING_FILE, "backing-file" },
{ NULL, NULL },
};
+ /*
+ * We can't actually store a backing format, but can check that
+ * the user's request made sense.
+ */
+ backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT);
+ if (backing_fmt && !bdrv_find_format(backing_fmt)) {
+ error_setg(errp, "unrecognized backing format '%s'", backing_fmt);
+ ret = -EINVAL;
+ goto fail;
+ }
+
/* Parse options and convert legacy syntax */
qdict = qemu_opts_to_qdict_filtered(opts, NULL, &qcow_create_opts, true);
ret = 0;
fail:
+ g_free(backing_fmt);
qobject_unref(qdict);
bdrv_unref(bs);
qapi_free_BlockdevCreateOptions(create_options);
.type = QEMU_OPT_STRING,
.help = "File name of a base image"
},
+ {
+ .name = BLOCK_OPT_BACKING_FMT,
+ .type = QEMU_OPT_STRING,
+ .help = "Format of the backing image",
+ },
{
.name = BLOCK_OPT_ENCRYPT,
.type = QEMU_OPT_BOOL,
}
ret = bdrv_change_backing_file(blk_bs(blk), qcow2_opts->backing_file,
- backing_format);
+ backing_format, false);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not assign backing file '%s' "
"with format '%s'", qcow2_opts->backing_file,
}
if (backing_file || backing_format) {
+ if (g_strcmp0(backing_file, s->image_backing_file) ||
+ g_strcmp0(backing_format, s->image_backing_format)) {
+ warn_report("Deprecated use of amend to alter the backing file; "
+ "use qemu-img rebase instead");
+ }
ret = qcow2_change_backing_file(bs,
backing_file ?: s->image_backing_file,
backing_format ?: s->image_backing_format);
Error **errp)
{
BlockdevCreateOptions *create_options = NULL;
- QDict *qdict, *location_qdict;
+ QDict *qdict = NULL, *location_qdict;
Visitor *v;
- char *redundancy;
+ char *redundancy = NULL;
Error *local_err = NULL;
int ret;
+ char *backing_fmt = NULL;
redundancy = qemu_opt_get_del(opts, BLOCK_OPT_REDUNDANCY);
+ backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT);
+
+ if (backing_fmt && strcmp(backing_fmt, "sheepdog") != 0) {
+ error_setg(errp, "backing_file must be a sheepdog image");
+ ret = -EINVAL;
+ goto fail;
+ }
qdict = qemu_opts_to_qdict(opts, NULL);
qdict_put_str(qdict, "driver", "sheepdog");
qapi_free_BlockdevCreateOptions(create_options);
qobject_unref(qdict);
g_free(redundancy);
+ g_free(backing_fmt);
return ret;
}
.type = QEMU_OPT_STRING,
.help = "File name of a base image"
},
+ {
+ .name = BLOCK_OPT_BACKING_FMT,
+ .type = QEMU_OPT_STRING,
+ .help = "Must be 'sheepdog' if present",
+ },
{
.name = BLOCK_OPT_PREALLOC,
.type = QEMU_OPT_STRING,
}
}
bdrv_set_backing_hd(bs, base, &local_err);
- ret = bdrv_change_backing_file(bs, base_id, base_fmt);
+ ret = bdrv_change_backing_file(bs, base_id, base_fmt, false);
if (local_err) {
error_report_err(local_err);
return -EPERM;
qed_aio_write_postfill(void *s, void *acb, uint64_t start, size_t len, uint64_t offset) "s %p acb %p start %"PRIu64" len %zu offset %"PRIu64
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"
-# vxhs.c
-vxhs_iio_callback(int error) "ctx is NULL: error %d"
-vxhs_iio_callback_chnfail(int err, int error) "QNIO channel failed, no i/o %d, %d"
-vxhs_iio_callback_unknwn(int opcode, int err) "unexpected opcode %d, errno %d"
-vxhs_aio_rw_invalid(int req) "Invalid I/O request iodir %d"
-vxhs_aio_rw_ioerr(char *guid, int iodir, uint64_t size, uint64_t off, void *acb, int ret, int err) "IO ERROR (vDisk %s) FOR : Read/Write = %d size = %"PRIu64" offset = %"PRIu64" ACB = %p. Error = %d, errno = %d"
-vxhs_get_vdisk_stat_err(char *guid, int ret, int err) "vDisk (%s) stat ioctl failed, ret = %d, errno = %d"
-vxhs_get_vdisk_stat(char *vdisk_guid, uint64_t vdisk_size) "vDisk %s stat ioctl returned size %"PRIu64
-vxhs_complete_aio(void *acb, uint64_t ret) "aio failed acb %p ret %"PRIu64
-vxhs_parse_uri_filename(const char *filename) "URI passed via bdrv_parse_filename %s"
-vxhs_open_vdiskid(const char *vdisk_id) "Opening vdisk-id %s"
-vxhs_open_hostinfo(char *of_vsa_addr, int port) "Adding host %s:%d to BDRVVXHSState"
-vxhs_open_iio_open(const char *host) "Failed to connect to storage agent on host %s"
-vxhs_parse_uri_hostinfo(char *host, int port) "Host: IP %s, Port %d"
-vxhs_close(char *vdisk_guid) "Closing vdisk %s"
-vxhs_get_creds(const char *cacert, const char *client_key, const char *client_cert) "cacert %s, client_key %s, client_cert %s"
-
# nvme.c
nvme_kick(void *s, int queue) "s %p queue %d"
nvme_dma_flush_queue_wait(void *s) "s %p"
bool zeroed_grain;
bool compat6;
VMDKCreateOptsData data;
+ char *backing_fmt = NULL;
+
+ backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT);
+ if (backing_fmt && strcmp(backing_fmt, "vmdk") != 0) {
+ error_setg(errp, "backing_file must be a vmdk image");
+ ret = -EINVAL;
+ goto exit;
+ }
if (filename_decompose(filename, path, prefix, postfix, PATH_MAX, errp)) {
ret = -EINVAL;
vmdk_co_create_opts_cb, &data, errp);
exit:
+ g_free(backing_fmt);
g_free(adapter_type);
g_free(backing_file);
g_free(hw_version);
.type = QEMU_OPT_STRING,
.help = "File name of a base image"
},
+ {
+ .name = BLOCK_OPT_BACKING_FMT,
+ .type = QEMU_OPT_STRING,
+ .help = "Must be 'vmdk' if present",
+ },
{
.name = BLOCK_OPT_COMPAT6,
.type = QEMU_OPT_BOOL,
+++ /dev/null
-/*
- * QEMU Block driver for Veritas HyperScale (VxHS)
- *
- * Copyright (c) 2017 Veritas Technologies 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 <qnio/qnio_api.h>
-#include <sys/param.h>
-#include "block/block_int.h"
-#include "block/qdict.h"
-#include "qapi/qmp/qerror.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qstring.h"
-#include "trace.h"
-#include "qemu/module.h"
-#include "qemu/uri.h"
-#include "qapi/error.h"
-#include "qemu/uuid.h"
-#include "crypto/tlscredsx509.h"
-#include "sysemu/replay.h"
-
-#define VXHS_OPT_FILENAME "filename"
-#define VXHS_OPT_VDISK_ID "vdisk-id"
-#define VXHS_OPT_SERVER "server"
-#define VXHS_OPT_HOST "host"
-#define VXHS_OPT_PORT "port"
-
-/* Only accessed under QEMU global mutex */
-static uint32_t vxhs_ref;
-
-typedef enum {
- VDISK_AIO_READ,
- VDISK_AIO_WRITE,
-} VDISKAIOCmd;
-
-/*
- * HyperScale AIO callbacks structure
- */
-typedef struct VXHSAIOCB {
- BlockAIOCB common;
- int err;
-} VXHSAIOCB;
-
-typedef struct VXHSvDiskHostsInfo {
- void *dev_handle; /* Device handle */
- char *host; /* Host name or IP */
- int port; /* Host's port number */
-} VXHSvDiskHostsInfo;
-
-/*
- * Structure per vDisk maintained for state
- */
-typedef struct BDRVVXHSState {
- VXHSvDiskHostsInfo vdisk_hostinfo; /* Per host info */
- char *vdisk_guid;
- char *tlscredsid; /* tlscredsid */
-} BDRVVXHSState;
-
-static void vxhs_complete_aio_bh(void *opaque)
-{
- VXHSAIOCB *acb = opaque;
- BlockCompletionFunc *cb = acb->common.cb;
- void *cb_opaque = acb->common.opaque;
- int ret = 0;
-
- if (acb->err != 0) {
- trace_vxhs_complete_aio(acb, acb->err);
- ret = (-EIO);
- }
-
- qemu_aio_unref(acb);
- cb(cb_opaque, ret);
-}
-
-/*
- * Called from a libqnio thread
- */
-static void vxhs_iio_callback(void *ctx, uint32_t opcode, uint32_t error)
-{
- VXHSAIOCB *acb = NULL;
-
- switch (opcode) {
- case IRP_READ_REQUEST:
- case IRP_WRITE_REQUEST:
-
- /*
- * ctx is VXHSAIOCB*
- * ctx is NULL if error is QNIOERROR_CHANNEL_HUP
- */
- if (ctx) {
- acb = ctx;
- } else {
- trace_vxhs_iio_callback(error);
- goto out;
- }
-
- if (error) {
- if (!acb->err) {
- acb->err = error;
- }
- trace_vxhs_iio_callback(error);
- }
-
- replay_bh_schedule_oneshot_event(bdrv_get_aio_context(acb->common.bs),
- vxhs_complete_aio_bh, acb);
- break;
-
- default:
- if (error == QNIOERROR_HUP) {
- /*
- * Channel failed, spontaneous notification,
- * not in response to I/O
- */
- trace_vxhs_iio_callback_chnfail(error, errno);
- } else {
- trace_vxhs_iio_callback_unknwn(opcode, error);
- }
- break;
- }
-out:
- return;
-}
-
-static QemuOptsList runtime_opts = {
- .name = "vxhs",
- .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
- .desc = {
- {
- .name = VXHS_OPT_FILENAME,
- .type = QEMU_OPT_STRING,
- .help = "URI to the Veritas HyperScale image",
- },
- {
- .name = VXHS_OPT_VDISK_ID,
- .type = QEMU_OPT_STRING,
- .help = "UUID of the VxHS vdisk",
- },
- {
- .name = "tls-creds",
- .type = QEMU_OPT_STRING,
- .help = "ID of the TLS/SSL credentials to use",
- },
- { /* end of list */ }
- },
-};
-
-static QemuOptsList runtime_tcp_opts = {
- .name = "vxhs_tcp",
- .head = QTAILQ_HEAD_INITIALIZER(runtime_tcp_opts.head),
- .desc = {
- {
- .name = VXHS_OPT_HOST,
- .type = QEMU_OPT_STRING,
- .help = "host address (ipv4 addresses)",
- },
- {
- .name = VXHS_OPT_PORT,
- .type = QEMU_OPT_NUMBER,
- .help = "port number on which VxHSD is listening (default 9999)",
- .def_value_str = "9999"
- },
- { /* end of list */ }
- },
-};
-
-/*
- * Parse incoming URI and populate *options with the host
- * and device information
- */
-static int vxhs_parse_uri(const char *filename, QDict *options)
-{
- URI *uri = NULL;
- char *port;
- int ret = 0;
-
- trace_vxhs_parse_uri_filename(filename);
- uri = uri_parse(filename);
- if (!uri || !uri->server || !uri->path) {
- uri_free(uri);
- return -EINVAL;
- }
-
- qdict_put_str(options, VXHS_OPT_SERVER ".host", uri->server);
-
- if (uri->port) {
- port = g_strdup_printf("%d", uri->port);
- qdict_put_str(options, VXHS_OPT_SERVER ".port", port);
- g_free(port);
- }
-
- qdict_put_str(options, "vdisk-id", uri->path);
-
- trace_vxhs_parse_uri_hostinfo(uri->server, uri->port);
- uri_free(uri);
-
- return ret;
-}
-
-static void vxhs_parse_filename(const char *filename, QDict *options,
- Error **errp)
-{
- if (qdict_haskey(options, "vdisk-id") || qdict_haskey(options, "server")) {
- error_setg(errp, "vdisk-id/server and a file name may not be specified "
- "at the same time");
- return;
- }
-
- if (strstr(filename, "://")) {
- int ret = vxhs_parse_uri(filename, options);
- if (ret < 0) {
- error_setg(errp, "Invalid URI. URI should be of the form "
- " vxhs://<host_ip>:<port>/<vdisk-id>");
- }
- }
-}
-
-static void vxhs_refresh_limits(BlockDriverState *bs, Error **errp)
-{
- /* XXX Does VXHS support AIO on less than 512-byte alignment? */
- bs->bl.request_alignment = 512;
-}
-
-static int vxhs_init_and_ref(void)
-{
- if (vxhs_ref++ == 0) {
- if (iio_init(QNIO_VERSION, vxhs_iio_callback)) {
- return -ENODEV;
- }
- }
- return 0;
-}
-
-static void vxhs_unref(void)
-{
- if (--vxhs_ref == 0) {
- iio_fini();
- }
-}
-
-static void vxhs_get_tls_creds(const char *id, char **cacert,
- char **key, char **cert, Error **errp)
-{
- Object *obj;
- QCryptoTLSCreds *creds;
- QCryptoTLSCredsX509 *creds_x509;
-
- obj = object_resolve_path_component(
- object_get_objects_root(), id);
-
- if (!obj) {
- error_setg(errp, "No TLS credentials with id '%s'",
- id);
- return;
- }
-
- creds_x509 = (QCryptoTLSCredsX509 *)
- object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS_X509);
-
- if (!creds_x509) {
- error_setg(errp, "Object with id '%s' is not TLS credentials",
- id);
- return;
- }
-
- creds = &creds_x509->parent_obj;
-
- if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
- error_setg(errp,
- "Expecting TLS credentials with a client endpoint");
- return;
- }
-
- /*
- * Get the cacert, client_cert and client_key file names.
- */
- if (!creds->dir) {
- error_setg(errp, "TLS object missing 'dir' property value");
- return;
- }
-
- *cacert = g_strdup_printf("%s/%s", creds->dir,
- QCRYPTO_TLS_CREDS_X509_CA_CERT);
- *cert = g_strdup_printf("%s/%s", creds->dir,
- QCRYPTO_TLS_CREDS_X509_CLIENT_CERT);
- *key = g_strdup_printf("%s/%s", creds->dir,
- QCRYPTO_TLS_CREDS_X509_CLIENT_KEY);
-}
-
-static int vxhs_open(BlockDriverState *bs, QDict *options,
- int bdrv_flags, Error **errp)
-{
- BDRVVXHSState *s = bs->opaque;
- void *dev_handlep;
- QDict *backing_options = NULL;
- QemuOpts *opts = NULL;
- QemuOpts *tcp_opts = NULL;
- char *of_vsa_addr = NULL;
- Error *local_err = NULL;
- const char *vdisk_id_opt;
- const char *server_host_opt;
- int ret = 0;
- char *cacert = NULL;
- char *client_key = NULL;
- char *client_cert = NULL;
-
- ret = vxhs_init_and_ref();
- if (ret < 0) {
- ret = -EINVAL;
- goto out;
- }
-
- /* Create opts info from runtime_opts and runtime_tcp_opts list */
- opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
- tcp_opts = qemu_opts_create(&runtime_tcp_opts, NULL, 0, &error_abort);
-
- if (!qemu_opts_absorb_qdict(opts, options, errp)) {
- ret = -EINVAL;
- goto out;
- }
-
- /* vdisk-id is the disk UUID */
- vdisk_id_opt = qemu_opt_get(opts, VXHS_OPT_VDISK_ID);
- if (!vdisk_id_opt) {
- error_setg(errp, QERR_MISSING_PARAMETER, VXHS_OPT_VDISK_ID);
- ret = -EINVAL;
- goto out;
- }
-
- /* vdisk-id may contain a leading '/' */
- if (strlen(vdisk_id_opt) > UUID_FMT_LEN + 1) {
- error_setg(errp, "vdisk-id cannot be more than %d characters",
- UUID_FMT_LEN);
- ret = -EINVAL;
- goto out;
- }
-
- s->vdisk_guid = g_strdup(vdisk_id_opt);
- trace_vxhs_open_vdiskid(vdisk_id_opt);
-
- /* get the 'server.' arguments */
- qdict_extract_subqdict(options, &backing_options, VXHS_OPT_SERVER".");
-
- if (!qemu_opts_absorb_qdict(tcp_opts, backing_options, errp)) {
- ret = -EINVAL;
- goto out;
- }
-
- server_host_opt = qemu_opt_get(tcp_opts, VXHS_OPT_HOST);
- if (!server_host_opt) {
- error_setg(errp, QERR_MISSING_PARAMETER,
- VXHS_OPT_SERVER"."VXHS_OPT_HOST);
- ret = -EINVAL;
- goto out;
- }
-
- if (strlen(server_host_opt) > MAXHOSTNAMELEN) {
- error_setg(errp, "server.host cannot be more than %d characters",
- MAXHOSTNAMELEN);
- ret = -EINVAL;
- goto out;
- }
-
- /* check if we got tls-creds via the --object argument */
- s->tlscredsid = g_strdup(qemu_opt_get(opts, "tls-creds"));
- if (s->tlscredsid) {
- vxhs_get_tls_creds(s->tlscredsid, &cacert, &client_key,
- &client_cert, &local_err);
- if (local_err != NULL) {
- ret = -EINVAL;
- goto out;
- }
- trace_vxhs_get_creds(cacert, client_key, client_cert);
- }
-
- s->vdisk_hostinfo.host = g_strdup(server_host_opt);
- s->vdisk_hostinfo.port = g_ascii_strtoll(qemu_opt_get(tcp_opts,
- VXHS_OPT_PORT),
- NULL, 0);
-
- trace_vxhs_open_hostinfo(s->vdisk_hostinfo.host,
- s->vdisk_hostinfo.port);
-
- of_vsa_addr = g_strdup_printf("of://%s:%d",
- s->vdisk_hostinfo.host,
- s->vdisk_hostinfo.port);
-
- /*
- * Open qnio channel to storage agent if not opened before
- */
- dev_handlep = iio_open(of_vsa_addr, s->vdisk_guid, 0,
- cacert, client_key, client_cert);
- if (dev_handlep == NULL) {
- trace_vxhs_open_iio_open(of_vsa_addr);
- ret = -ENODEV;
- goto out;
- }
- s->vdisk_hostinfo.dev_handle = dev_handlep;
-
-out:
- g_free(of_vsa_addr);
- qobject_unref(backing_options);
- qemu_opts_del(tcp_opts);
- qemu_opts_del(opts);
- g_free(cacert);
- g_free(client_key);
- g_free(client_cert);
-
- if (ret < 0) {
- vxhs_unref();
- g_free(s->vdisk_hostinfo.host);
- g_free(s->vdisk_guid);
- g_free(s->tlscredsid);
- s->vdisk_guid = NULL;
- }
-
- return ret;
-}
-
-static const AIOCBInfo vxhs_aiocb_info = {
- .aiocb_size = sizeof(VXHSAIOCB)
-};
-
-/*
- * This allocates QEMU-VXHS callback for each IO
- * and is passed to QNIO. When QNIO completes the work,
- * it will be passed back through the callback.
- */
-static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, uint64_t offset,
- QEMUIOVector *qiov, uint64_t size,
- BlockCompletionFunc *cb, void *opaque,
- VDISKAIOCmd iodir)
-{
- VXHSAIOCB *acb = NULL;
- BDRVVXHSState *s = bs->opaque;
- int iio_flags = 0;
- int ret = 0;
- void *dev_handle = s->vdisk_hostinfo.dev_handle;
-
- acb = qemu_aio_get(&vxhs_aiocb_info, bs, cb, opaque);
-
- /*
- * Initialize VXHSAIOCB.
- */
- acb->err = 0;
-
- iio_flags = IIO_FLAG_ASYNC;
-
- switch (iodir) {
- case VDISK_AIO_WRITE:
- ret = iio_writev(dev_handle, acb, qiov->iov, qiov->niov,
- offset, size, iio_flags);
- break;
- case VDISK_AIO_READ:
- ret = iio_readv(dev_handle, acb, qiov->iov, qiov->niov,
- offset, size, iio_flags);
- break;
- default:
- trace_vxhs_aio_rw_invalid(iodir);
- goto errout;
- }
-
- if (ret != 0) {
- trace_vxhs_aio_rw_ioerr(s->vdisk_guid, iodir, size, offset,
- acb, ret, errno);
- goto errout;
- }
- return &acb->common;
-
-errout:
- qemu_aio_unref(acb);
- return NULL;
-}
-
-static BlockAIOCB *vxhs_aio_preadv(BlockDriverState *bs,
- uint64_t offset, uint64_t bytes,
- QEMUIOVector *qiov, int flags,
- BlockCompletionFunc *cb, void *opaque)
-{
- return vxhs_aio_rw(bs, offset, qiov, bytes, cb, opaque, VDISK_AIO_READ);
-}
-
-static BlockAIOCB *vxhs_aio_pwritev(BlockDriverState *bs,
- uint64_t offset, uint64_t bytes,
- QEMUIOVector *qiov, int flags,
- BlockCompletionFunc *cb, void *opaque)
-{
- return vxhs_aio_rw(bs, offset, qiov, bytes, cb, opaque, VDISK_AIO_WRITE);
-}
-
-static void vxhs_close(BlockDriverState *bs)
-{
- BDRVVXHSState *s = bs->opaque;
-
- trace_vxhs_close(s->vdisk_guid);
-
- g_free(s->vdisk_guid);
- s->vdisk_guid = NULL;
-
- /*
- * Close vDisk device
- */
- if (s->vdisk_hostinfo.dev_handle) {
- iio_close(s->vdisk_hostinfo.dev_handle);
- s->vdisk_hostinfo.dev_handle = NULL;
- }
-
- vxhs_unref();
-
- /*
- * Free the dynamically allocated host string etc
- */
- g_free(s->vdisk_hostinfo.host);
- g_free(s->tlscredsid);
- s->tlscredsid = NULL;
- s->vdisk_hostinfo.host = NULL;
- s->vdisk_hostinfo.port = 0;
-}
-
-static int64_t vxhs_get_vdisk_stat(BDRVVXHSState *s)
-{
- int64_t vdisk_size = -1;
- int ret = 0;
- void *dev_handle = s->vdisk_hostinfo.dev_handle;
-
- ret = iio_ioctl(dev_handle, IOR_VDISK_STAT, &vdisk_size, 0);
- if (ret < 0) {
- trace_vxhs_get_vdisk_stat_err(s->vdisk_guid, ret, errno);
- return -EIO;
- }
-
- trace_vxhs_get_vdisk_stat(s->vdisk_guid, vdisk_size);
- return vdisk_size;
-}
-
-/*
- * Returns the size of vDisk in bytes. This is required
- * by QEMU block upper block layer so that it is visible
- * to guest.
- */
-static int64_t vxhs_getlength(BlockDriverState *bs)
-{
- BDRVVXHSState *s = bs->opaque;
- int64_t vdisk_size;
-
- vdisk_size = vxhs_get_vdisk_stat(s);
- if (vdisk_size < 0) {
- return -EIO;
- }
-
- return vdisk_size;
-}
-
-static const char *const vxhs_strong_runtime_opts[] = {
- VXHS_OPT_VDISK_ID,
- "tls-creds",
- VXHS_OPT_HOST,
- VXHS_OPT_PORT,
- VXHS_OPT_SERVER".",
-
- NULL
-};
-
-static BlockDriver bdrv_vxhs = {
- .format_name = "vxhs",
- .protocol_name = "vxhs",
- .instance_size = sizeof(BDRVVXHSState),
- .bdrv_file_open = vxhs_open,
- .bdrv_parse_filename = vxhs_parse_filename,
- .bdrv_refresh_limits = vxhs_refresh_limits,
- .bdrv_close = vxhs_close,
- .bdrv_getlength = vxhs_getlength,
- .bdrv_aio_preadv = vxhs_aio_preadv,
- .bdrv_aio_pwritev = vxhs_aio_pwritev,
- .strong_runtime_opts = vxhs_strong_runtime_opts,
-};
-
-static void bdrv_vxhs_init(void)
-{
- bdrv_register(&bdrv_vxhs);
-}
-
-block_init(bdrv_vxhs_init);
}
ret = bdrv_change_backing_file(image_bs, backing_file,
- image_bs->drv ? image_bs->drv->format_name : "");
+ image_bs->drv ? image_bs->drv->format_name : "",
+ false);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not change backing file to '%s'",
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-$(CONFIG_WIN32) += char-win.o
chardev-obj-$(CONFIG_WIN32) += char-win-stdio.o
-common-obj-y += msmouse.o wctablet.o testdev.o
+common-obj-$(CONFIG_SOFTMMU) += msmouse.o wctablet.o testdev.o
ifeq ($(CONFIG_BRLAPI),y)
common-obj-m += baum.o
#include "chardev/char-fe.h"
#include "chardev/char-io.h"
-#include "chardev/char-mux.h"
+#include "chardev-internal.h"
int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len)
{
#include "chardev/char.h"
#include "sysemu/block-backend.h"
#include "sysemu/sysemu.h"
-#include "chardev/char-mux.h"
+#include "chardev-internal.h"
/* MUX driver for serial I/O splitting */
if (emit_close) {
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
- if (s->reconnect_time) {
+ if (s->reconnect_time && !s->reconnect_timer) {
qemu_chr_socket_restart_timer(chr);
}
}
*/
s->connect_task = qio_task_new(OBJECT(sioc),
qemu_chr_socket_connected,
- chr, NULL);
+ object_ref(OBJECT(chr)),
+ (GDestroyNotify)object_unref);
qio_task_run_in_thread(s->connect_task,
tcp_chr_connect_client_task,
s->addr,
#include "qemu/id.h"
#include "qemu/coroutine.h"
-#include "chardev/char-mux.h"
+#include "chardev-internal.h"
/***********************************************************/
/* character device */
-static Object *get_chardevs_root(void)
+Object *get_chardevs_root(void)
{
return container_get(object_get_root(), "/chardevs");
}
.class_init = char_class_init,
};
-static int chardev_machine_done_notify_one(Object *child, void *opaque)
-{
- Chardev *chr = (Chardev *)child;
- ChardevClass *class = CHARDEV_GET_CLASS(chr);
-
- if (class->chr_machine_done) {
- return class->chr_machine_done(chr);
- }
-
- return 0;
-}
-
-static void chardev_machine_done_hook(Notifier *notifier, void *unused)
-{
- int ret = object_child_foreach(get_chardevs_root(),
- chardev_machine_done_notify_one, NULL);
-
- if (ret) {
- error_report("Failed to call chardev machine_done hooks");
- exit(1);
- }
-}
-
-static Notifier chardev_machine_done_notify = {
- .notify = chardev_machine_done_hook,
-};
-
static bool qemu_chr_is_busy(Chardev *s)
{
if (CHARDEV_IS_MUX(s)) {
}
if (id) {
- object_property_add_child(get_chardevs_root(), id, obj);
+ object_property_try_add_child(get_chardevs_root(), id, obj,
+ &local_err);
+ if (local_err) {
+ goto end;
+ }
object_unref(obj);
}
static void register_types(void)
{
type_register_static(&char_type_info);
-
- /* this must be done after machine init, since we register FEs with muxes
- * as part of realize functions like serial_isa_realizefn when -nographic
- * is specified
- */
- qemu_add_machine_init_done_notifier(&chardev_machine_done_notify);
}
type_init(register_types);
--- /dev/null
+/*
+ * QEMU Character device internals
+ *
+ * Copyright (c) 2003-2008 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 CHARDEV_INTERNAL_H
+#define CHARDEV_INTERNAL_H
+
+#include "chardev/char.h"
+#include "chardev/char-fe.h"
+#include "qom/object.h"
+
+#define MAX_MUX 4
+#define MUX_BUFFER_SIZE 32 /* Must be a power of 2. */
+#define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1)
+
+typedef struct MuxChardev {
+ Chardev parent;
+ CharBackend *backends[MAX_MUX];
+ CharBackend chr;
+ int focus;
+ int mux_cnt;
+ int term_got_escape;
+ int max_size;
+ /* Intermediate input buffer catches escape sequences even if the
+ currently active device is not accepting any input - but only until it
+ is full as well. */
+ unsigned char buffer[MAX_MUX][MUX_BUFFER_SIZE];
+ int prod[MAX_MUX];
+ int cons[MAX_MUX];
+ int timestamps;
+
+ /* Protected by the Chardev chr_write_lock. */
+ int linestart;
+ int64_t timestamps_start;
+} MuxChardev;
+
+#define MUX_CHARDEV(obj) OBJECT_CHECK(MuxChardev, (obj), TYPE_CHARDEV_MUX)
+#define CHARDEV_IS_MUX(chr) \
+ object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_MUX)
+
+void mux_set_focus(Chardev *chr, int focus);
+void mux_chr_send_all_event(Chardev *chr, QEMUChrEvent event);
+
+Object *get_chardevs_root(void);
+
+#endif /* CHAR_MUX_H */
--- /dev/null
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 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.
+ */
+
+#include "qemu/osdep.h"
+#include "sysemu/sysemu.h"
+#include "chardev/char.h"
+#include "qemu/error-report.h"
+#include "chardev-internal.h"
+
+static int chardev_machine_done_notify_one(Object *child, void *opaque)
+{
+ Chardev *chr = (Chardev *)child;
+ ChardevClass *class = CHARDEV_GET_CLASS(chr);
+
+ if (class->chr_machine_done) {
+ return class->chr_machine_done(chr);
+ }
+
+ return 0;
+}
+
+static void chardev_machine_done_hook(Notifier *notifier, void *unused)
+{
+ int ret = object_child_foreach(get_chardevs_root(),
+ chardev_machine_done_notify_one, NULL);
+
+ if (ret) {
+ error_report("Failed to call chardev machine_done hooks");
+ exit(1);
+ }
+}
+
+
+static Notifier chardev_machine_done_notify = {
+ .notify = chardev_machine_done_hook,
+};
+
+static void register_types(void)
+{
+ /*
+ * This must be done after machine init, since we register FEs with muxes
+ * as part of realize functions like serial_isa_realizefn when -nographic
+ * is specified.
+ */
+ qemu_add_machine_init_done_notifier(&chardev_machine_done_notify);
+}
+
+type_init(register_types);
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"
tcmalloc="no"
jemalloc="no"
replication="yes"
-vxhs=""
bochs="yes"
cloop="yes"
dmg="yes"
;;
Haiku)
haiku="yes"
- QEMU_CFLAGS="-DB_USE_POSITIVE_POSIX_ERRORS $QEMU_CFLAGS"
- LIBS="-lposix_error_mapper -lnetwork $LIBS"
+ QEMU_CFLAGS="-DB_USE_POSITIVE_POSIX_ERRORS -DBSD_SOURCE $QEMU_CFLAGS"
+ LIBS="-lposix_error_mapper -lnetwork -lbsd $LIBS"
;;
Linux)
audio_drv_list="try-pa oss"
fi
done
+# Check for existence of python3 yaml, needed to
+# import yaml config files into vm-build.
+python_yaml="no"
+if $(python3 -c "import yaml" 2> /dev/null); then
+ python_yaml="yes"
+fi
+
: ${smbd=${SMBD-/usr/sbin/smbd}}
# Default objcc to clang if available, otherwise use CC
;;
--firmwarepath=*) firmwarepath="$optarg"
;;
+ --efi-aarch64=*) efi_aarch64="$optarg"
+ ;;
--host=*|--build=*|\
--disable-dependency-tracking|\
--sbindir=*|--sharedstatedir=*|\
;;
--enable-replication) replication="yes"
;;
- --disable-vxhs) vxhs="no"
- ;;
- --enable-vxhs) vxhs="yes"
- ;;
--disable-bochs) bochs="no"
;;
--enable-bochs) bochs="yes"
--sysconfdir=PATH install config in PATH$confsuffix
--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-pkgversion=VERS use specified string as sub-version of the package
--enable-debug enable common debug build options
xfsctl xfsctl support
qom-cast-debug cast debugging support
tools build qemu-io, qemu-nbd and qemu-img tools
- vxhs Veritas HyperScale vDisk backend support
bochs bochs image format support
cloop cloop image format support
dmg dmg image format support
l2tpv3=no
fi
+if check_include "pty.h" ; then
+ pty_h=yes
+else
+ pty_h=no
+fi
+
+cat > $TMPC <<EOF
+#include <sys/mman.h>
+int main(int argc, char *argv[]) {
+ return mlockall(MCL_FUTURE);
+}
+EOF
+if compile_prog "" "" ; then
+ have_mlockall=yes
+else
+ have_mlockall=no
+fi
+
#########################################
# vhost interdependencies and host support
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
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
cat > $TMPC << EOF
#include <pthread.h>
-static void *f(void *p) { pthread_setname_np("QEMU"); }
+static void *f(void *p) { pthread_setname_np("QEMU"); return NULL; }
int main(void)
{
pthread_t thread;
linux_magic_h=yes
fi
-########################################
-# check whether we can disable warning option with a pragma (this is needed
-# to silence warnings in the headers of some versions of external libraries).
-# This test has to be compiled with -Werror as otherwise an unknown pragma is
-# only a warning.
-#
-# If we can't selectively disable warning in the code, disable -Werror so that
-# the build doesn't fail anyway.
-
-pragma_disable_unused_but_set=no
-cat > $TMPC << EOF
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wstrict-prototypes"
-#pragma GCC diagnostic pop
-
-int main(void) {
- return 0;
-}
-EOF
-if compile_prog "-Werror" "" ; then
- pragma_diagnostic_available=yes
-else
- werror=no
-fi
-
########################################
# check if we have valgrind/valgrind.h
have_sysmacros=yes
fi
-##########################################
-# Veritas HyperScale block driver VxHS
-# Check if libvxhs is installed
-
-if test "$vxhs" != "no" ; then
- cat > $TMPC <<EOF
-#include <stdint.h>
-#include <qnio/qnio_api.h>
-
-void *vxhs_callback;
-
-int main(void) {
- iio_init(QNIO_VERSION, vxhs_callback);
- return 0;
-}
-EOF
- vxhs_libs="-lvxhs -lssl"
- if compile_prog "" "$vxhs_libs" ; then
- vxhs=yes
- else
- if test "$vxhs" = "yes" ; then
- feature_not_found "vxhs block device" "Install libvxhs See github"
- fi
- vxhs=no
- fi
-fi
-
##########################################
# check for _Static_assert()
if (tmp != NULL) {
return *(int *)(tmp + 2);
}
+ return 1;
}
EOF
if compile_prog "$CPU_CFLAGS -Werror -fsanitize=undefined" ""; then
fi
if test "$secret_keyring" != "no"
then
- if test "$have_keyring" == "yes"
+ if test "$have_keyring" = "yes"
then
secret_keyring=yes
else
echo "sphinx-build $sphinx_build"
fi
echo "genisoimage $genisoimage"
+echo "efi_aarch64 $efi_aarch64"
+echo "python_yaml $python_yaml"
echo "slirp support $slirp $(echo_version $slirp $slirp_version)"
if test "$slirp" != "no" ; then
echo "smbd $smbd"
echo "avx2 optimization $avx2_opt"
echo "avx512f optimization $avx512f_opt"
echo "replication support $replication"
-echo "VxHS block device $vxhs"
echo "bochs support $bochs"
echo "cloop support $cloop"
echo "dmg support $dmg"
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
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':
echo "CONFIG_LINUX_MAGIC_H=y" >> $config_host_mak
fi
-if test "$pragma_diagnostic_available" = "yes" ; then
- echo "CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE=y" >> $config_host_mak
-fi
-
if test "$valgrind_h" = "yes" ; then
echo "CONFIG_VALGRIND_H=y" >> $config_host_mak
fi
echo "CONFIG_PTHREAD_SETNAME_NP_WO_TID=y" >> $config_host_mak
fi
-if test "$vxhs" = "yes" ; then
- echo "CONFIG_VXHS=y" >> $config_host_mak
- echo "VXHS_LIBS=$vxhs_libs" >> $config_host_mak
-fi
-
if test "$libpmem" = "yes" ; then
echo "CONFIG_LIBPMEM=y" >> $config_host_mak
fi
if test "$sheepdog" = "yes" ; then
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=address,fuzzer"
FUZZ_CFLAGS=" -fsanitize=address,fuzzer"
- CFLAGS=" -fsanitize=address,fuzzer-no-link"
+ CFLAGS="$CFLAGS -fsanitize=address,fuzzer-no-link"
else
error_exit "Your compiler doesn't support -fsanitize=address,fuzzer"
exit 1
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 "CC=$cc" >> $config_host_mak
if $iasl -h > /dev/null 2>&1; then
echo "IASL=$iasl" >> $config_host_mak
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)
disas_config "ARM_A64"
fi
;;
+ avr)
+ disas_config "AVR"
+ ;;
cris)
disas_config "CRIS"
;;
return max_cpu_index;
}
+CPUTailQ cpus = QTAILQ_HEAD_INITIALIZER(cpus);
+
void cpu_list_add(CPUState *cpu)
{
QEMU_LOCK_GUARD(&qemu_cpu_list_lock);
cpu->cpu_index = UNASSIGNED_CPU_INDEX;
}
+CPUState *qemu_get_cpu(int index)
+{
+ CPUState *cpu;
+
+ CPU_FOREACH(cpu) {
+ if (cpu->cpu_index == index) {
+ return cpu;
+ }
+ }
+
+ return NULL;
+}
+
+/* current CPU in the current thread. It is only valid inside cpu_exec() */
+__thread CPUState *current_cpu;
+
struct qemu_work_item {
QSIMPLEQ_ENTRY(qemu_work_item) node;
run_on_cpu_func func;
+++ /dev/null
-/*
- * QEMU System Emulator
- *
- * Copyright (c) 2003-2008 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.
- */
-
-#include "qemu/osdep.h"
-#include "qemu-common.h"
-#include "qemu/config-file.h"
-#include "qemu/cutils.h"
-#include "migration/vmstate.h"
-#include "monitor/monitor.h"
-#include "qapi/error.h"
-#include "qapi/qapi-commands-misc.h"
-#include "qapi/qapi-events-run-state.h"
-#include "qapi/qmp/qerror.h"
-#include "qemu/error-report.h"
-#include "qemu/qemu-print.h"
-#include "sysemu/tcg.h"
-#include "sysemu/block-backend.h"
-#include "exec/gdbstub.h"
-#include "sysemu/dma.h"
-#include "sysemu/hw_accel.h"
-#include "sysemu/kvm.h"
-#include "sysemu/hax.h"
-#include "sysemu/hvf.h"
-#include "sysemu/whpx.h"
-#include "exec/exec-all.h"
-
-#include "qemu/thread.h"
-#include "qemu/plugin.h"
-#include "sysemu/cpus.h"
-#include "sysemu/qtest.h"
-#include "qemu/main-loop.h"
-#include "qemu/option.h"
-#include "qemu/bitmap.h"
-#include "qemu/seqlock.h"
-#include "qemu/guest-random.h"
-#include "tcg/tcg.h"
-#include "hw/nmi.h"
-#include "sysemu/replay.h"
-#include "sysemu/runstate.h"
-#include "hw/boards.h"
-#include "hw/hw.h"
-
-#ifdef CONFIG_LINUX
-
-#include <sys/prctl.h>
-
-#ifndef PR_MCE_KILL
-#define PR_MCE_KILL 33
-#endif
-
-#ifndef PR_MCE_KILL_SET
-#define PR_MCE_KILL_SET 1
-#endif
-
-#ifndef PR_MCE_KILL_EARLY
-#define PR_MCE_KILL_EARLY 1
-#endif
-
-#endif /* CONFIG_LINUX */
-
-static QemuMutex qemu_global_mutex;
-
-int64_t max_delay;
-int64_t max_advance;
-
-/* vcpu throttling controls */
-static QEMUTimer *throttle_timer;
-static unsigned int throttle_percentage;
-
-#define CPU_THROTTLE_PCT_MIN 1
-#define CPU_THROTTLE_PCT_MAX 99
-#define CPU_THROTTLE_TIMESLICE_NS 10000000
-
-bool cpu_is_stopped(CPUState *cpu)
-{
- return cpu->stopped || !runstate_is_running();
-}
-
-static inline bool cpu_work_list_empty(CPUState *cpu)
-{
- bool ret;
-
- qemu_mutex_lock(&cpu->work_mutex);
- ret = QSIMPLEQ_EMPTY(&cpu->work_list);
- qemu_mutex_unlock(&cpu->work_mutex);
- return ret;
-}
-
-static bool cpu_thread_is_idle(CPUState *cpu)
-{
- if (cpu->stop || !cpu_work_list_empty(cpu)) {
- return false;
- }
- if (cpu_is_stopped(cpu)) {
- return true;
- }
- if (!cpu->halted || cpu_has_work(cpu) ||
- kvm_halt_in_kernel()) {
- return false;
- }
- return true;
-}
-
-static bool all_cpu_threads_idle(void)
-{
- CPUState *cpu;
-
- CPU_FOREACH(cpu) {
- if (!cpu_thread_is_idle(cpu)) {
- return false;
- }
- }
- return true;
-}
-
-/***********************************************************/
-/* guest cycle counter */
-
-/* Protected by TimersState seqlock */
-
-static bool icount_sleep = true;
-/* Arbitrarily pick 1MIPS as the minimum allowable speed. */
-#define MAX_ICOUNT_SHIFT 10
-
-typedef struct TimersState {
- /* Protected by BQL. */
- int64_t cpu_ticks_prev;
- int64_t cpu_ticks_offset;
-
- /* Protect fields that can be respectively read outside the
- * BQL, and written from multiple threads.
- */
- QemuSeqLock vm_clock_seqlock;
- QemuSpin vm_clock_lock;
-
- int16_t cpu_ticks_enabled;
-
- /* Conversion factor from emulated instructions to virtual clock ticks. */
- int16_t icount_time_shift;
-
- /* Compensate for varying guest execution speed. */
- int64_t qemu_icount_bias;
-
- int64_t vm_clock_warp_start;
- int64_t cpu_clock_offset;
-
- /* Only written by TCG thread */
- int64_t qemu_icount;
-
- /* for adjusting icount */
- QEMUTimer *icount_rt_timer;
- QEMUTimer *icount_vm_timer;
- QEMUTimer *icount_warp_timer;
-} TimersState;
-
-static TimersState timers_state;
-bool mttcg_enabled;
-
-
-/* The current number of executed instructions is based on what we
- * originally budgeted minus the current state of the decrementing
- * icount counters in extra/u16.low.
- */
-static int64_t cpu_get_icount_executed(CPUState *cpu)
-{
- return (cpu->icount_budget -
- (cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra));
-}
-
-/*
- * Update the global shared timer_state.qemu_icount to take into
- * account executed instructions. This is done by the TCG vCPU
- * thread so the main-loop can see time has moved forward.
- */
-static void cpu_update_icount_locked(CPUState *cpu)
-{
- int64_t executed = cpu_get_icount_executed(cpu);
- cpu->icount_budget -= executed;
-
- atomic_set_i64(&timers_state.qemu_icount,
- timers_state.qemu_icount + executed);
-}
-
-/*
- * Update the global shared timer_state.qemu_icount to take into
- * account executed instructions. This is done by the TCG vCPU
- * thread so the main-loop can see time has moved forward.
- */
-void cpu_update_icount(CPUState *cpu)
-{
- seqlock_write_lock(&timers_state.vm_clock_seqlock,
- &timers_state.vm_clock_lock);
- cpu_update_icount_locked(cpu);
- seqlock_write_unlock(&timers_state.vm_clock_seqlock,
- &timers_state.vm_clock_lock);
-}
-
-static int64_t cpu_get_icount_raw_locked(void)
-{
- CPUState *cpu = current_cpu;
-
- if (cpu && cpu->running) {
- if (!cpu->can_do_io) {
- error_report("Bad icount read");
- exit(1);
- }
- /* Take into account what has run */
- cpu_update_icount_locked(cpu);
- }
- /* The read is protected by the seqlock, but needs atomic64 to avoid UB */
- return atomic_read_i64(&timers_state.qemu_icount);
-}
-
-static int64_t cpu_get_icount_locked(void)
-{
- int64_t icount = cpu_get_icount_raw_locked();
- return atomic_read_i64(&timers_state.qemu_icount_bias) +
- cpu_icount_to_ns(icount);
-}
-
-int64_t cpu_get_icount_raw(void)
-{
- int64_t icount;
- unsigned start;
-
- do {
- start = seqlock_read_begin(&timers_state.vm_clock_seqlock);
- icount = cpu_get_icount_raw_locked();
- } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start));
-
- return icount;
-}
-
-/* Return the virtual CPU time, based on the instruction counter. */
-int64_t cpu_get_icount(void)
-{
- int64_t icount;
- unsigned start;
-
- do {
- start = seqlock_read_begin(&timers_state.vm_clock_seqlock);
- icount = cpu_get_icount_locked();
- } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start));
-
- return icount;
-}
-
-int64_t cpu_icount_to_ns(int64_t icount)
-{
- return icount << atomic_read(&timers_state.icount_time_shift);
-}
-
-static int64_t cpu_get_ticks_locked(void)
-{
- int64_t ticks = timers_state.cpu_ticks_offset;
- if (timers_state.cpu_ticks_enabled) {
- ticks += cpu_get_host_ticks();
- }
-
- if (timers_state.cpu_ticks_prev > ticks) {
- /* Non increasing ticks may happen if the host uses software suspend. */
- timers_state.cpu_ticks_offset += timers_state.cpu_ticks_prev - ticks;
- ticks = timers_state.cpu_ticks_prev;
- }
-
- timers_state.cpu_ticks_prev = ticks;
- return ticks;
-}
-
-/* return the time elapsed in VM between vm_start and vm_stop. Unless
- * icount is active, cpu_get_ticks() uses units of the host CPU cycle
- * counter.
- */
-int64_t cpu_get_ticks(void)
-{
- int64_t ticks;
-
- if (use_icount) {
- return cpu_get_icount();
- }
-
- qemu_spin_lock(&timers_state.vm_clock_lock);
- ticks = cpu_get_ticks_locked();
- qemu_spin_unlock(&timers_state.vm_clock_lock);
- return ticks;
-}
-
-static int64_t cpu_get_clock_locked(void)
-{
- int64_t time;
-
- time = timers_state.cpu_clock_offset;
- if (timers_state.cpu_ticks_enabled) {
- time += get_clock();
- }
-
- return time;
-}
-
-/* Return the monotonic time elapsed in VM, i.e.,
- * the time between vm_start and vm_stop
- */
-int64_t cpu_get_clock(void)
-{
- int64_t ti;
- unsigned start;
-
- do {
- start = seqlock_read_begin(&timers_state.vm_clock_seqlock);
- ti = cpu_get_clock_locked();
- } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start));
-
- return ti;
-}
-
-/* enable cpu_get_ticks()
- * Caller must hold BQL which serves as mutex for vm_clock_seqlock.
- */
-void cpu_enable_ticks(void)
-{
- seqlock_write_lock(&timers_state.vm_clock_seqlock,
- &timers_state.vm_clock_lock);
- if (!timers_state.cpu_ticks_enabled) {
- timers_state.cpu_ticks_offset -= cpu_get_host_ticks();
- timers_state.cpu_clock_offset -= get_clock();
- timers_state.cpu_ticks_enabled = 1;
- }
- seqlock_write_unlock(&timers_state.vm_clock_seqlock,
- &timers_state.vm_clock_lock);
-}
-
-/* disable cpu_get_ticks() : the clock is stopped. You must not call
- * cpu_get_ticks() after that.
- * Caller must hold BQL which serves as mutex for vm_clock_seqlock.
- */
-void cpu_disable_ticks(void)
-{
- seqlock_write_lock(&timers_state.vm_clock_seqlock,
- &timers_state.vm_clock_lock);
- if (timers_state.cpu_ticks_enabled) {
- timers_state.cpu_ticks_offset += cpu_get_host_ticks();
- timers_state.cpu_clock_offset = cpu_get_clock_locked();
- timers_state.cpu_ticks_enabled = 0;
- }
- seqlock_write_unlock(&timers_state.vm_clock_seqlock,
- &timers_state.vm_clock_lock);
-}
-
-/* Correlation between real and virtual time is always going to be
- fairly approximate, so ignore small variation.
- When the guest is idle real and virtual time will be aligned in
- the IO wait loop. */
-#define ICOUNT_WOBBLE (NANOSECONDS_PER_SECOND / 10)
-
-static void icount_adjust(void)
-{
- int64_t cur_time;
- int64_t cur_icount;
- int64_t delta;
-
- /* Protected by TimersState mutex. */
- static int64_t last_delta;
-
- /* If the VM is not running, then do nothing. */
- if (!runstate_is_running()) {
- return;
- }
-
- seqlock_write_lock(&timers_state.vm_clock_seqlock,
- &timers_state.vm_clock_lock);
- cur_time = REPLAY_CLOCK_LOCKED(REPLAY_CLOCK_VIRTUAL_RT,
- cpu_get_clock_locked());
- cur_icount = cpu_get_icount_locked();
-
- delta = cur_icount - cur_time;
- /* FIXME: This is a very crude algorithm, somewhat prone to oscillation. */
- if (delta > 0
- && last_delta + ICOUNT_WOBBLE < delta * 2
- && timers_state.icount_time_shift > 0) {
- /* The guest is getting too far ahead. Slow time down. */
- atomic_set(&timers_state.icount_time_shift,
- timers_state.icount_time_shift - 1);
- }
- if (delta < 0
- && last_delta - ICOUNT_WOBBLE > delta * 2
- && timers_state.icount_time_shift < MAX_ICOUNT_SHIFT) {
- /* The guest is getting too far behind. Speed time up. */
- atomic_set(&timers_state.icount_time_shift,
- timers_state.icount_time_shift + 1);
- }
- last_delta = delta;
- atomic_set_i64(&timers_state.qemu_icount_bias,
- cur_icount - (timers_state.qemu_icount
- << timers_state.icount_time_shift));
- seqlock_write_unlock(&timers_state.vm_clock_seqlock,
- &timers_state.vm_clock_lock);
-}
-
-static void icount_adjust_rt(void *opaque)
-{
- timer_mod(timers_state.icount_rt_timer,
- qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT) + 1000);
- icount_adjust();
-}
-
-static void icount_adjust_vm(void *opaque)
-{
- timer_mod(timers_state.icount_vm_timer,
- qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- NANOSECONDS_PER_SECOND / 10);
- icount_adjust();
-}
-
-static int64_t qemu_icount_round(int64_t count)
-{
- int shift = atomic_read(&timers_state.icount_time_shift);
- return (count + (1 << shift) - 1) >> shift;
-}
-
-static void icount_warp_rt(void)
-{
- unsigned seq;
- int64_t warp_start;
-
- /* The icount_warp_timer is rescheduled soon after vm_clock_warp_start
- * changes from -1 to another value, so the race here is okay.
- */
- do {
- seq = seqlock_read_begin(&timers_state.vm_clock_seqlock);
- warp_start = timers_state.vm_clock_warp_start;
- } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, seq));
-
- if (warp_start == -1) {
- return;
- }
-
- seqlock_write_lock(&timers_state.vm_clock_seqlock,
- &timers_state.vm_clock_lock);
- if (runstate_is_running()) {
- int64_t clock = REPLAY_CLOCK_LOCKED(REPLAY_CLOCK_VIRTUAL_RT,
- cpu_get_clock_locked());
- int64_t warp_delta;
-
- warp_delta = clock - timers_state.vm_clock_warp_start;
- if (use_icount == 2) {
- /*
- * In adaptive mode, do not let QEMU_CLOCK_VIRTUAL run too
- * far ahead of real time.
- */
- int64_t cur_icount = cpu_get_icount_locked();
- int64_t delta = clock - cur_icount;
- warp_delta = MIN(warp_delta, delta);
- }
- atomic_set_i64(&timers_state.qemu_icount_bias,
- timers_state.qemu_icount_bias + warp_delta);
- }
- timers_state.vm_clock_warp_start = -1;
- seqlock_write_unlock(&timers_state.vm_clock_seqlock,
- &timers_state.vm_clock_lock);
-
- if (qemu_clock_expired(QEMU_CLOCK_VIRTUAL)) {
- qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
- }
-}
-
-static void icount_timer_cb(void *opaque)
-{
- /* No need for a checkpoint because the timer already synchronizes
- * with CHECKPOINT_CLOCK_VIRTUAL_RT.
- */
- icount_warp_rt();
-}
-
-void qtest_clock_warp(int64_t dest)
-{
- int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- AioContext *aio_context;
- assert(qtest_enabled());
- aio_context = qemu_get_aio_context();
- while (clock < dest) {
- int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
- QEMU_TIMER_ATTR_ALL);
- int64_t warp = qemu_soonest_timeout(dest - clock, deadline);
-
- seqlock_write_lock(&timers_state.vm_clock_seqlock,
- &timers_state.vm_clock_lock);
- atomic_set_i64(&timers_state.qemu_icount_bias,
- timers_state.qemu_icount_bias + warp);
- seqlock_write_unlock(&timers_state.vm_clock_seqlock,
- &timers_state.vm_clock_lock);
-
- qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
- timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]);
- clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- }
- qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
-}
-
-void qemu_start_warp_timer(void)
-{
- int64_t clock;
- int64_t deadline;
-
- if (!use_icount) {
- return;
- }
-
- /* Nothing to do if the VM is stopped: QEMU_CLOCK_VIRTUAL timers
- * do not fire, so computing the deadline does not make sense.
- */
- if (!runstate_is_running()) {
- return;
- }
-
- if (replay_mode != REPLAY_MODE_PLAY) {
- if (!all_cpu_threads_idle()) {
- return;
- }
-
- if (qtest_enabled()) {
- /* When testing, qtest commands advance icount. */
- return;
- }
-
- replay_checkpoint(CHECKPOINT_CLOCK_WARP_START);
- } else {
- /* warp clock deterministically in record/replay mode */
- if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_START)) {
- /* vCPU is sleeping and warp can't be started.
- It is probably a race condition: notification sent
- to vCPU was processed in advance and vCPU went to sleep.
- Therefore we have to wake it up for doing someting. */
- if (replay_has_checkpoint()) {
- qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
- }
- return;
- }
- }
-
- /* We want to use the earliest deadline from ALL vm_clocks */
- clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT);
- deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
- ~QEMU_TIMER_ATTR_EXTERNAL);
- if (deadline < 0) {
- static bool notified;
- if (!icount_sleep && !notified) {
- warn_report("icount sleep disabled and no active timers");
- notified = true;
- }
- return;
- }
-
- if (deadline > 0) {
- /*
- * Ensure QEMU_CLOCK_VIRTUAL proceeds even when the virtual CPU goes to
- * sleep. Otherwise, the CPU might be waiting for a future timer
- * interrupt to wake it up, but the interrupt never comes because
- * the vCPU isn't running any insns and thus doesn't advance the
- * QEMU_CLOCK_VIRTUAL.
- */
- if (!icount_sleep) {
- /*
- * We never let VCPUs sleep in no sleep icount mode.
- * If there is a pending QEMU_CLOCK_VIRTUAL timer we just advance
- * to the next QEMU_CLOCK_VIRTUAL event and notify it.
- * It is useful when we want a deterministic execution time,
- * isolated from host latencies.
- */
- seqlock_write_lock(&timers_state.vm_clock_seqlock,
- &timers_state.vm_clock_lock);
- atomic_set_i64(&timers_state.qemu_icount_bias,
- timers_state.qemu_icount_bias + deadline);
- seqlock_write_unlock(&timers_state.vm_clock_seqlock,
- &timers_state.vm_clock_lock);
- qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
- } else {
- /*
- * We do stop VCPUs and only advance QEMU_CLOCK_VIRTUAL after some
- * "real" time, (related to the time left until the next event) has
- * passed. The QEMU_CLOCK_VIRTUAL_RT clock will do this.
- * This avoids that the warps are visible externally; for example,
- * you will not be sending network packets continuously instead of
- * every 100ms.
- */
- seqlock_write_lock(&timers_state.vm_clock_seqlock,
- &timers_state.vm_clock_lock);
- if (timers_state.vm_clock_warp_start == -1
- || timers_state.vm_clock_warp_start > clock) {
- timers_state.vm_clock_warp_start = clock;
- }
- seqlock_write_unlock(&timers_state.vm_clock_seqlock,
- &timers_state.vm_clock_lock);
- timer_mod_anticipate(timers_state.icount_warp_timer,
- clock + deadline);
- }
- } else if (deadline == 0) {
- qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
- }
-}
-
-static void qemu_account_warp_timer(void)
-{
- if (!use_icount || !icount_sleep) {
- return;
- }
-
- /* Nothing to do if the VM is stopped: QEMU_CLOCK_VIRTUAL timers
- * do not fire, so computing the deadline does not make sense.
- */
- if (!runstate_is_running()) {
- return;
- }
-
- /* warp clock deterministically in record/replay mode */
- if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_ACCOUNT)) {
- return;
- }
-
- timer_del(timers_state.icount_warp_timer);
- icount_warp_rt();
-}
-
-static bool icount_state_needed(void *opaque)
-{
- return use_icount;
-}
-
-static bool warp_timer_state_needed(void *opaque)
-{
- TimersState *s = opaque;
- return s->icount_warp_timer != NULL;
-}
-
-static bool adjust_timers_state_needed(void *opaque)
-{
- TimersState *s = opaque;
- return s->icount_rt_timer != NULL;
-}
-
-static bool shift_state_needed(void *opaque)
-{
- return use_icount == 2;
-}
-
-/*
- * Subsection for warp timer migration is optional, because may not be created
- */
-static const VMStateDescription icount_vmstate_warp_timer = {
- .name = "timer/icount/warp_timer",
- .version_id = 1,
- .minimum_version_id = 1,
- .needed = warp_timer_state_needed,
- .fields = (VMStateField[]) {
- VMSTATE_INT64(vm_clock_warp_start, TimersState),
- VMSTATE_TIMER_PTR(icount_warp_timer, TimersState),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static const VMStateDescription icount_vmstate_adjust_timers = {
- .name = "timer/icount/timers",
- .version_id = 1,
- .minimum_version_id = 1,
- .needed = adjust_timers_state_needed,
- .fields = (VMStateField[]) {
- VMSTATE_TIMER_PTR(icount_rt_timer, TimersState),
- VMSTATE_TIMER_PTR(icount_vm_timer, TimersState),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static const VMStateDescription icount_vmstate_shift = {
- .name = "timer/icount/shift",
- .version_id = 1,
- .minimum_version_id = 1,
- .needed = shift_state_needed,
- .fields = (VMStateField[]) {
- VMSTATE_INT16(icount_time_shift, TimersState),
- VMSTATE_END_OF_LIST()
- }
-};
-
-/*
- * This is a subsection for icount migration.
- */
-static const VMStateDescription icount_vmstate_timers = {
- .name = "timer/icount",
- .version_id = 1,
- .minimum_version_id = 1,
- .needed = icount_state_needed,
- .fields = (VMStateField[]) {
- VMSTATE_INT64(qemu_icount_bias, TimersState),
- VMSTATE_INT64(qemu_icount, TimersState),
- VMSTATE_END_OF_LIST()
- },
- .subsections = (const VMStateDescription*[]) {
- &icount_vmstate_warp_timer,
- &icount_vmstate_adjust_timers,
- &icount_vmstate_shift,
- NULL
- }
-};
-
-static const VMStateDescription vmstate_timers = {
- .name = "timer",
- .version_id = 2,
- .minimum_version_id = 1,
- .fields = (VMStateField[]) {
- VMSTATE_INT64(cpu_ticks_offset, TimersState),
- VMSTATE_UNUSED(8),
- VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2),
- VMSTATE_END_OF_LIST()
- },
- .subsections = (const VMStateDescription*[]) {
- &icount_vmstate_timers,
- NULL
- }
-};
-
-static void cpu_throttle_thread(CPUState *cpu, run_on_cpu_data opaque)
-{
- double pct;
- double throttle_ratio;
- int64_t sleeptime_ns, endtime_ns;
-
- if (!cpu_throttle_get_percentage()) {
- return;
- }
-
- pct = (double)cpu_throttle_get_percentage()/100;
- throttle_ratio = pct / (1 - pct);
- /* Add 1ns to fix double's rounding error (like 0.9999999...) */
- sleeptime_ns = (int64_t)(throttle_ratio * CPU_THROTTLE_TIMESLICE_NS + 1);
- endtime_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + sleeptime_ns;
- while (sleeptime_ns > 0 && !cpu->stop) {
- if (sleeptime_ns > SCALE_MS) {
- qemu_cond_timedwait(cpu->halt_cond, &qemu_global_mutex,
- sleeptime_ns / SCALE_MS);
- } else {
- qemu_mutex_unlock_iothread();
- g_usleep(sleeptime_ns / SCALE_US);
- qemu_mutex_lock_iothread();
- }
- sleeptime_ns = endtime_ns - qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
- }
- atomic_set(&cpu->throttle_thread_scheduled, 0);
-}
-
-static void cpu_throttle_timer_tick(void *opaque)
-{
- CPUState *cpu;
- double pct;
-
- /* Stop the timer if needed */
- if (!cpu_throttle_get_percentage()) {
- return;
- }
- CPU_FOREACH(cpu) {
- if (!atomic_xchg(&cpu->throttle_thread_scheduled, 1)) {
- async_run_on_cpu(cpu, cpu_throttle_thread,
- RUN_ON_CPU_NULL);
- }
- }
-
- pct = (double)cpu_throttle_get_percentage()/100;
- timer_mod(throttle_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) +
- CPU_THROTTLE_TIMESLICE_NS / (1-pct));
-}
-
-void cpu_throttle_set(int new_throttle_pct)
-{
- /* Ensure throttle percentage is within valid range */
- new_throttle_pct = MIN(new_throttle_pct, CPU_THROTTLE_PCT_MAX);
- new_throttle_pct = MAX(new_throttle_pct, CPU_THROTTLE_PCT_MIN);
-
- atomic_set(&throttle_percentage, new_throttle_pct);
-
- timer_mod(throttle_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) +
- CPU_THROTTLE_TIMESLICE_NS);
-}
-
-void cpu_throttle_stop(void)
-{
- atomic_set(&throttle_percentage, 0);
-}
-
-bool cpu_throttle_active(void)
-{
- return (cpu_throttle_get_percentage() != 0);
-}
-
-int cpu_throttle_get_percentage(void)
-{
- return atomic_read(&throttle_percentage);
-}
-
-void cpu_ticks_init(void)
-{
- seqlock_init(&timers_state.vm_clock_seqlock);
- qemu_spin_init(&timers_state.vm_clock_lock);
- vmstate_register(NULL, 0, &vmstate_timers, &timers_state);
- throttle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT,
- cpu_throttle_timer_tick, NULL);
-}
-
-void configure_icount(QemuOpts *opts, Error **errp)
-{
- const char *option = qemu_opt_get(opts, "shift");
- bool sleep = qemu_opt_get_bool(opts, "sleep", true);
- bool align = qemu_opt_get_bool(opts, "align", false);
- long time_shift = -1;
-
- if (!option) {
- if (qemu_opt_get(opts, "align") != NULL) {
- error_setg(errp, "Please specify shift option when using align");
- }
- return;
- }
-
- if (align && !sleep) {
- error_setg(errp, "align=on and sleep=off are incompatible");
- return;
- }
-
- if (strcmp(option, "auto") != 0) {
- if (qemu_strtol(option, NULL, 0, &time_shift) < 0
- || time_shift < 0 || time_shift > MAX_ICOUNT_SHIFT) {
- error_setg(errp, "icount: Invalid shift value");
- return;
- }
- } else if (icount_align_option) {
- error_setg(errp, "shift=auto and align=on are incompatible");
- return;
- } else if (!icount_sleep) {
- error_setg(errp, "shift=auto and sleep=off are incompatible");
- return;
- }
-
- icount_sleep = sleep;
- if (icount_sleep) {
- timers_state.icount_warp_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT,
- icount_timer_cb, NULL);
- }
-
- icount_align_option = align;
-
- if (time_shift >= 0) {
- timers_state.icount_time_shift = time_shift;
- use_icount = 1;
- return;
- }
-
- use_icount = 2;
-
- /* 125MIPS seems a reasonable initial guess at the guest speed.
- It will be corrected fairly quickly anyway. */
- timers_state.icount_time_shift = 3;
-
- /* Have both realtime and virtual time triggers for speed adjustment.
- The realtime trigger catches emulated time passing too slowly,
- the virtual time trigger catches emulated time passing too fast.
- Realtime triggers occur even when idle, so use them less frequently
- than VM triggers. */
- timers_state.vm_clock_warp_start = -1;
- timers_state.icount_rt_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL_RT,
- icount_adjust_rt, NULL);
- timer_mod(timers_state.icount_rt_timer,
- qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT) + 1000);
- timers_state.icount_vm_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
- icount_adjust_vm, NULL);
- timer_mod(timers_state.icount_vm_timer,
- qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- NANOSECONDS_PER_SECOND / 10);
-}
-
-/***********************************************************/
-/* 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 *tcg_kick_vcpu_timer;
-static CPUState *tcg_current_rr_cpu;
-
-#define TCG_KICK_PERIOD (NANOSECONDS_PER_SECOND / 10)
-
-static inline int64_t qemu_tcg_next_kick(void)
-{
- return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + TCG_KICK_PERIOD;
-}
-
-/* Kick the currently round-robin scheduled vCPU to next */
-static void qemu_cpu_kick_rr_next_cpu(void)
-{
- CPUState *cpu;
- do {
- cpu = atomic_mb_read(&tcg_current_rr_cpu);
- if (cpu) {
- cpu_exit(cpu);
- }
- } while (cpu != atomic_mb_read(&tcg_current_rr_cpu));
-}
-
-/* Kick all RR vCPUs */
-static void qemu_cpu_kick_rr_cpus(void)
-{
- CPUState *cpu;
-
- CPU_FOREACH(cpu) {
- cpu_exit(cpu);
- };
-}
-
-static void do_nothing(CPUState *cpu, run_on_cpu_data unused)
-{
-}
-
-void qemu_timer_notify_cb(void *opaque, QEMUClockType type)
-{
- if (!use_icount || type != QEMU_CLOCK_VIRTUAL) {
- qemu_notify_event();
- return;
- }
-
- if (qemu_in_vcpu_thread()) {
- /* A CPU is currently running; kick it back out to the
- * tcg_cpu_exec() loop so it will recalculate its
- * icount deadline immediately.
- */
- qemu_cpu_kick(current_cpu);
- } else if (first_cpu) {
- /* qemu_cpu_kick is not enough to kick a halted CPU out of
- * qemu_tcg_wait_io_event. async_run_on_cpu, instead,
- * causes cpu_thread_is_idle to return false. This way,
- * handle_icount_deadline can run.
- * If we have no CPUs at all for some reason, we don't
- * need to do anything.
- */
- async_run_on_cpu(first_cpu, do_nothing, RUN_ON_CPU_NULL);
- }
-}
-
-static void kick_tcg_thread(void *opaque)
-{
- timer_mod(tcg_kick_vcpu_timer, qemu_tcg_next_kick());
- qemu_cpu_kick_rr_next_cpu();
-}
-
-static void start_tcg_kick_timer(void)
-{
- assert(!mttcg_enabled);
- if (!tcg_kick_vcpu_timer && CPU_NEXT(first_cpu)) {
- tcg_kick_vcpu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
- kick_tcg_thread, NULL);
- }
- if (tcg_kick_vcpu_timer && !timer_pending(tcg_kick_vcpu_timer)) {
- timer_mod(tcg_kick_vcpu_timer, qemu_tcg_next_kick());
- }
-}
-
-static void stop_tcg_kick_timer(void)
-{
- assert(!mttcg_enabled);
- if (tcg_kick_vcpu_timer && timer_pending(tcg_kick_vcpu_timer)) {
- timer_del(tcg_kick_vcpu_timer);
- }
-}
-
-/***********************************************************/
-void hw_error(const char *fmt, ...)
-{
- va_list ap;
- CPUState *cpu;
-
- va_start(ap, fmt);
- fprintf(stderr, "qemu: hardware error: ");
- vfprintf(stderr, fmt, ap);
- fprintf(stderr, "\n");
- CPU_FOREACH(cpu) {
- fprintf(stderr, "CPU #%d:\n", cpu->cpu_index);
- cpu_dump_state(cpu, stderr, CPU_DUMP_FPU);
- }
- va_end(ap);
- abort();
-}
-
-void cpu_synchronize_all_states(void)
-{
- CPUState *cpu;
-
- CPU_FOREACH(cpu) {
- cpu_synchronize_state(cpu);
- /* TODO: move to cpu_synchronize_state() */
- if (hvf_enabled()) {
- hvf_cpu_synchronize_state(cpu);
- }
- }
-}
-
-void cpu_synchronize_all_post_reset(void)
-{
- CPUState *cpu;
-
- CPU_FOREACH(cpu) {
- cpu_synchronize_post_reset(cpu);
- /* TODO: move to cpu_synchronize_post_reset() */
- if (hvf_enabled()) {
- hvf_cpu_synchronize_post_reset(cpu);
- }
- }
-}
-
-void cpu_synchronize_all_post_init(void)
-{
- CPUState *cpu;
-
- CPU_FOREACH(cpu) {
- cpu_synchronize_post_init(cpu);
- /* TODO: move to cpu_synchronize_post_init() */
- if (hvf_enabled()) {
- hvf_cpu_synchronize_post_init(cpu);
- }
- }
-}
-
-void cpu_synchronize_all_pre_loadvm(void)
-{
- CPUState *cpu;
-
- CPU_FOREACH(cpu) {
- cpu_synchronize_pre_loadvm(cpu);
- }
-}
-
-static int do_vm_stop(RunState state, bool send_stop)
-{
- int ret = 0;
-
- if (runstate_is_running()) {
- runstate_set(state);
- cpu_disable_ticks();
- pause_all_vcpus();
- vm_state_notify(0, state);
- if (send_stop) {
- qapi_event_send_stop();
- }
- }
-
- bdrv_drain_all();
- ret = bdrv_flush_all();
-
- return ret;
-}
-
-/* Special vm_stop() variant for terminating the process. Historically clients
- * did not expect a QMP STOP event and so we need to retain compatibility.
- */
-int vm_shutdown(void)
-{
- return do_vm_stop(RUN_STATE_SHUTDOWN, false);
-}
-
-static bool cpu_can_run(CPUState *cpu)
-{
- if (cpu->stop) {
- return false;
- }
- if (cpu_is_stopped(cpu)) {
- return false;
- }
- return true;
-}
-
-static void cpu_handle_guest_debug(CPUState *cpu)
-{
- gdb_set_stop_cpu(cpu);
- qemu_system_debug_request();
- cpu->stopped = true;
-}
-
-#ifdef CONFIG_LINUX
-static void sigbus_reraise(void)
-{
- sigset_t set;
- struct sigaction action;
-
- memset(&action, 0, sizeof(action));
- action.sa_handler = SIG_DFL;
- if (!sigaction(SIGBUS, &action, NULL)) {
- raise(SIGBUS);
- sigemptyset(&set);
- sigaddset(&set, SIGBUS);
- pthread_sigmask(SIG_UNBLOCK, &set, NULL);
- }
- perror("Failed to re-raise SIGBUS!\n");
- abort();
-}
-
-static void sigbus_handler(int n, siginfo_t *siginfo, void *ctx)
-{
- if (siginfo->si_code != BUS_MCEERR_AO && siginfo->si_code != BUS_MCEERR_AR) {
- sigbus_reraise();
- }
-
- if (current_cpu) {
- /* Called asynchronously in VCPU thread. */
- if (kvm_on_sigbus_vcpu(current_cpu, siginfo->si_code, siginfo->si_addr)) {
- sigbus_reraise();
- }
- } else {
- /* Called synchronously (via signalfd) in main thread. */
- if (kvm_on_sigbus(siginfo->si_code, siginfo->si_addr)) {
- sigbus_reraise();
- }
- }
-}
-
-static void qemu_init_sigbus(void)
-{
- struct sigaction action;
-
- memset(&action, 0, sizeof(action));
- action.sa_flags = SA_SIGINFO;
- action.sa_sigaction = sigbus_handler;
- sigaction(SIGBUS, &action, NULL);
-
- prctl(PR_MCE_KILL, PR_MCE_KILL_SET, PR_MCE_KILL_EARLY, 0, 0);
-}
-#else /* !CONFIG_LINUX */
-static void qemu_init_sigbus(void)
-{
-}
-#endif /* !CONFIG_LINUX */
-
-static QemuThread io_thread;
-
-/* cpu creation */
-static QemuCond qemu_cpu_cond;
-/* system init */
-static QemuCond qemu_pause_cond;
-
-void qemu_init_cpu_loop(void)
-{
- qemu_init_sigbus();
- qemu_cond_init(&qemu_cpu_cond);
- qemu_cond_init(&qemu_pause_cond);
- qemu_mutex_init(&qemu_global_mutex);
-
- qemu_thread_get_self(&io_thread);
-}
-
-void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data)
-{
- do_run_on_cpu(cpu, func, data, &qemu_global_mutex);
-}
-
-static void qemu_kvm_destroy_vcpu(CPUState *cpu)
-{
- if (kvm_destroy_vcpu(cpu) < 0) {
- error_report("kvm_destroy_vcpu failed");
- exit(EXIT_FAILURE);
- }
-}
-
-static void qemu_tcg_destroy_vcpu(CPUState *cpu)
-{
-}
-
-static void qemu_cpu_stop(CPUState *cpu, bool exit)
-{
- g_assert(qemu_cpu_is_self(cpu));
- cpu->stop = false;
- cpu->stopped = true;
- if (exit) {
- cpu_exit(cpu);
- }
- qemu_cond_broadcast(&qemu_pause_cond);
-}
-
-static void qemu_wait_io_event_common(CPUState *cpu)
-{
- atomic_mb_set(&cpu->thread_kicked, false);
- if (cpu->stop) {
- qemu_cpu_stop(cpu, false);
- }
- process_queued_cpu_work(cpu);
-}
-
-static void qemu_tcg_rr_wait_io_event(void)
-{
- CPUState *cpu;
-
- while (all_cpu_threads_idle()) {
- stop_tcg_kick_timer();
- qemu_cond_wait(first_cpu->halt_cond, &qemu_global_mutex);
- }
-
- start_tcg_kick_timer();
-
- CPU_FOREACH(cpu) {
- qemu_wait_io_event_common(cpu);
- }
-}
-
-static void qemu_wait_io_event(CPUState *cpu)
-{
- bool slept = false;
-
- while (cpu_thread_is_idle(cpu)) {
- if (!slept) {
- slept = true;
- qemu_plugin_vcpu_idle_cb(cpu);
- }
- qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex);
- }
- if (slept) {
- qemu_plugin_vcpu_resume_cb(cpu);
- }
-
-#ifdef _WIN32
- /* Eat dummy APC queued by qemu_cpu_kick_thread. */
- if (!tcg_enabled()) {
- SleepEx(0, TRUE);
- }
-#endif
- qemu_wait_io_event_common(cpu);
-}
-
-static void *qemu_kvm_cpu_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);
- if (r < 0) {
- error_report("kvm_init_vcpu failed: %s", strerror(-r));
- exit(1);
- }
-
- kvm_init_cpu_signals(cpu);
-
- /* signal CPU creation */
- cpu->created = true;
- qemu_cond_signal(&qemu_cpu_cond);
- 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));
-
- qemu_kvm_destroy_vcpu(cpu);
- cpu->created = false;
- qemu_cond_signal(&qemu_cpu_cond);
- qemu_mutex_unlock_iothread();
- rcu_unregister_thread();
- return NULL;
-}
-
-static void *qemu_dummy_cpu_thread_fn(void *arg)
-{
-#ifdef _WIN32
- error_report("qtest is not supported under Windows");
- exit(1);
-#else
- 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->created = true;
- qemu_cond_signal(&qemu_cpu_cond);
- 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;
-#endif
-}
-
-static int64_t tcg_get_icount_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 qemu_icount_round(deadline);
- } else {
- return replay_get_instructions();
- }
-}
-
-static void notify_aio_contexts(void)
-{
- /* Wake up other AioContexts. */
- qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
- qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
-}
-
-static void handle_icount_deadline(void)
-{
- assert(qemu_in_vcpu_thread());
- if (use_icount) {
- int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
- QEMU_TIMER_ATTR_ALL);
-
- if (deadline == 0) {
- notify_aio_contexts();
- }
- }
-}
-
-static void prepare_icount_for_run(CPUState *cpu)
-{
- if (use_icount) {
- int insns_left;
-
- /* These should always be cleared by process_icount_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 = tcg_get_icount_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()) {
- notify_aio_contexts();
- }
- }
-}
-
-static void process_icount_data(CPUState *cpu)
-{
- if (use_icount) {
- /* Account for executed instructions */
- cpu_update_icount(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();
- }
-}
-
-
-static int tcg_cpu_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
- atomic_set(&tcg_ctx->prof.cpu_exec_time,
- tcg_ctx->prof.cpu_exec_time + profile_getclock() - ti);
-#endif
- return ret;
-}
-
-/* Destroy any remaining vCPUs which have been unplugged and have
- * finished running
- */
-static void deal_with_unplugged_cpus(void)
-{
- CPUState *cpu;
-
- CPU_FOREACH(cpu) {
- if (cpu->unplug && !cpu_can_run(cpu)) {
- qemu_tcg_destroy_vcpu(cpu);
- cpu->created = false;
- qemu_cond_signal(&qemu_cpu_cond);
- break;
- }
- }
-}
-
-/* Single-threaded TCG
- *
- * 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 *qemu_tcg_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->created = true;
- cpu->can_do_io = 1;
- qemu_cond_signal(&qemu_cpu_cond);
- qemu_guest_random_seed_thread_part2(cpu->random_seed);
-
- /* wait for initial kick-off after machine start */
- while (first_cpu->stopped) {
- qemu_cond_wait(first_cpu->halt_cond, &qemu_global_mutex);
-
- /* process any pending work */
- CPU_FOREACH(cpu) {
- current_cpu = cpu;
- qemu_wait_io_event_common(cpu);
- }
- }
-
- start_tcg_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();
- /* Account partial waits to QEMU_CLOCK_VIRTUAL. */
- qemu_account_warp_timer();
-
- /* Run the timers here. This is much more efficient than
- * waking up the I/O thread and waiting for completion.
- */
- handle_icount_deadline();
-
- replay_mutex_unlock();
-
- if (!cpu) {
- cpu = first_cpu;
- }
-
- while (cpu && cpu_work_list_empty(cpu) && !cpu->exit_request) {
-
- atomic_mb_set(&tcg_current_rr_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();
- prepare_icount_for_run(cpu);
-
- r = tcg_cpu_exec(cpu);
-
- process_icount_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 atomic_mb_set because a spurious wakeup is okay. */
- atomic_set(&tcg_current_rr_cpu, NULL);
-
- if (cpu && cpu->exit_request) {
- atomic_mb_set(&cpu->exit_request, 0);
- }
-
- if (use_icount && 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();
- }
-
- qemu_tcg_rr_wait_io_event();
- deal_with_unplugged_cpus();
- }
-
- rcu_unregister_thread();
- return NULL;
-}
-
-static void *qemu_hax_cpu_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->created = true;
- current_cpu = cpu;
-
- hax_init_vcpu(cpu);
- qemu_cond_signal(&qemu_cpu_cond);
- qemu_guest_random_seed_thread_part2(cpu->random_seed);
-
- do {
- if (cpu_can_run(cpu)) {
- r = hax_smp_cpu_exec(cpu);
- if (r == EXCP_DEBUG) {
- cpu_handle_guest_debug(cpu);
- }
- }
-
- qemu_wait_io_event(cpu);
- } while (!cpu->unplug || cpu_can_run(cpu));
- rcu_unregister_thread();
- return NULL;
-}
-
-/* The HVF-specific vCPU thread function. This one should only run when the host
- * CPU supports the VMX "unrestricted guest" feature. */
-static void *qemu_hvf_cpu_thread_fn(void *arg)
-{
- CPUState *cpu = arg;
-
- int r;
-
- assert(hvf_enabled());
-
- 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;
-
- hvf_init_vcpu(cpu);
-
- /* signal CPU creation */
- cpu->created = true;
- qemu_cond_signal(&qemu_cpu_cond);
- qemu_guest_random_seed_thread_part2(cpu->random_seed);
-
- do {
- if (cpu_can_run(cpu)) {
- r = hvf_vcpu_exec(cpu);
- if (r == EXCP_DEBUG) {
- cpu_handle_guest_debug(cpu);
- }
- }
- qemu_wait_io_event(cpu);
- } while (!cpu->unplug || cpu_can_run(cpu));
-
- hvf_vcpu_destroy(cpu);
- cpu->created = false;
- qemu_cond_signal(&qemu_cpu_cond);
- qemu_mutex_unlock_iothread();
- rcu_unregister_thread();
- return NULL;
-}
-
-static void *qemu_whpx_cpu_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();
- current_cpu = cpu;
-
- r = whpx_init_vcpu(cpu);
- if (r < 0) {
- fprintf(stderr, "whpx_init_vcpu failed: %s\n", strerror(-r));
- exit(1);
- }
-
- /* signal CPU creation */
- cpu->created = true;
- qemu_cond_signal(&qemu_cpu_cond);
- qemu_guest_random_seed_thread_part2(cpu->random_seed);
-
- do {
- if (cpu_can_run(cpu)) {
- r = whpx_vcpu_exec(cpu);
- if (r == EXCP_DEBUG) {
- cpu_handle_guest_debug(cpu);
- }
- }
- while (cpu_thread_is_idle(cpu)) {
- qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex);
- }
- qemu_wait_io_event_common(cpu);
- } while (!cpu->unplug || cpu_can_run(cpu));
-
- whpx_destroy_vcpu(cpu);
- cpu->created = false;
- qemu_cond_signal(&qemu_cpu_cond);
- qemu_mutex_unlock_iothread();
- rcu_unregister_thread();
- return NULL;
-}
-
-#ifdef _WIN32
-static void CALLBACK dummy_apc_func(ULONG_PTR unused)
-{
-}
-#endif
-
-/* Multi-threaded TCG
- *
- * 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 *qemu_tcg_cpu_thread_fn(void *arg)
-{
- CPUState *cpu = arg;
-
- assert(tcg_enabled());
- g_assert(!use_icount);
-
- rcu_register_thread();
- tcg_register_thread();
-
- qemu_mutex_lock_iothread();
- qemu_thread_get_self(cpu->thread);
-
- cpu->thread_id = qemu_get_thread_id();
- cpu->created = true;
- cpu->can_do_io = 1;
- current_cpu = cpu;
- qemu_cond_signal(&qemu_cpu_cond);
- 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_cpu_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;
- }
- }
-
- atomic_mb_set(&cpu->exit_request, 0);
- qemu_wait_io_event(cpu);
- } while (!cpu->unplug || cpu_can_run(cpu));
-
- qemu_tcg_destroy_vcpu(cpu);
- cpu->created = false;
- qemu_cond_signal(&qemu_cpu_cond);
- qemu_mutex_unlock_iothread();
- rcu_unregister_thread();
- return NULL;
-}
-
-static void qemu_cpu_kick_thread(CPUState *cpu)
-{
-#ifndef _WIN32
- int err;
-
- if (cpu->thread_kicked) {
- return;
- }
- cpu->thread_kicked = true;
- err = pthread_kill(cpu->thread->thread, SIG_IPI);
- if (err && err != ESRCH) {
- fprintf(stderr, "qemu:%s: %s", __func__, strerror(err));
- exit(1);
- }
-#else /* _WIN32 */
- if (!qemu_cpu_is_self(cpu)) {
- if (whpx_enabled()) {
- whpx_vcpu_kick(cpu);
- } else if (!QueueUserAPC(dummy_apc_func, cpu->hThread, 0)) {
- fprintf(stderr, "%s: QueueUserAPC failed with error %lu\n",
- __func__, GetLastError());
- exit(1);
- }
- }
-#endif
-}
-
-void qemu_cpu_kick(CPUState *cpu)
-{
- qemu_cond_broadcast(cpu->halt_cond);
- if (tcg_enabled()) {
- if (qemu_tcg_mttcg_enabled()) {
- cpu_exit(cpu);
- } else {
- qemu_cpu_kick_rr_cpus();
- }
- } else {
- if (hax_enabled()) {
- /*
- * FIXME: race condition with the exit_request check in
- * hax_vcpu_hax_exec
- */
- cpu->exit_request = 1;
- }
- qemu_cpu_kick_thread(cpu);
- }
-}
-
-void qemu_cpu_kick_self(void)
-{
- assert(current_cpu);
- qemu_cpu_kick_thread(current_cpu);
-}
-
-bool qemu_cpu_is_self(CPUState *cpu)
-{
- return qemu_thread_is_self(cpu->thread);
-}
-
-bool qemu_in_vcpu_thread(void)
-{
- return current_cpu && qemu_cpu_is_self(current_cpu);
-}
-
-static __thread bool iothread_locked = false;
-
-bool qemu_mutex_iothread_locked(void)
-{
- return iothread_locked;
-}
-
-/*
- * The BQL is taken from so many places that it is worth profiling the
- * callers directly, instead of funneling them all through a single function.
- */
-void qemu_mutex_lock_iothread_impl(const char *file, int line)
-{
- QemuMutexLockFunc bql_lock = atomic_read(&qemu_bql_mutex_lock_func);
-
- g_assert(!qemu_mutex_iothread_locked());
- bql_lock(&qemu_global_mutex, file, line);
- iothread_locked = true;
-}
-
-void qemu_mutex_unlock_iothread(void)
-{
- g_assert(qemu_mutex_iothread_locked());
- iothread_locked = false;
- qemu_mutex_unlock(&qemu_global_mutex);
-}
-
-void qemu_cond_wait_iothread(QemuCond *cond)
-{
- qemu_cond_wait(cond, &qemu_global_mutex);
-}
-
-static bool all_vcpus_paused(void)
-{
- CPUState *cpu;
-
- CPU_FOREACH(cpu) {
- if (!cpu->stopped) {
- return false;
- }
- }
-
- return true;
-}
-
-void pause_all_vcpus(void)
-{
- CPUState *cpu;
-
- qemu_clock_enable(QEMU_CLOCK_VIRTUAL, false);
- CPU_FOREACH(cpu) {
- if (qemu_cpu_is_self(cpu)) {
- qemu_cpu_stop(cpu, true);
- } else {
- cpu->stop = true;
- qemu_cpu_kick(cpu);
- }
- }
-
- /* We need to drop the replay_lock so any vCPU threads woken up
- * can finish their replay tasks
- */
- replay_mutex_unlock();
-
- while (!all_vcpus_paused()) {
- qemu_cond_wait(&qemu_pause_cond, &qemu_global_mutex);
- CPU_FOREACH(cpu) {
- qemu_cpu_kick(cpu);
- }
- }
-
- qemu_mutex_unlock_iothread();
- replay_mutex_lock();
- qemu_mutex_lock_iothread();
-}
-
-void cpu_resume(CPUState *cpu)
-{
- cpu->stop = false;
- cpu->stopped = false;
- qemu_cpu_kick(cpu);
-}
-
-void resume_all_vcpus(void)
-{
- CPUState *cpu;
-
- if (!runstate_is_running()) {
- return;
- }
-
- qemu_clock_enable(QEMU_CLOCK_VIRTUAL, true);
- CPU_FOREACH(cpu) {
- cpu_resume(cpu);
- }
-}
-
-void cpu_remove_sync(CPUState *cpu)
-{
- cpu->stop = true;
- cpu->unplug = true;
- qemu_cpu_kick(cpu);
- qemu_mutex_unlock_iothread();
- qemu_thread_join(cpu->thread);
- qemu_mutex_lock_iothread();
-}
-
-/* For temporary buffers for forming a name */
-#define VCPU_THREAD_NAME_SIZE 16
-
-static void qemu_tcg_init_vcpu(CPUState *cpu)
-{
- char thread_name[VCPU_THREAD_NAME_SIZE];
- static QemuCond *single_tcg_halt_cond;
- static QemuThread *single_tcg_cpu_thread;
- static int tcg_region_inited;
-
- assert(tcg_enabled());
- /*
- * Initialize TCG regions--once. Now is a good time, because:
- * (1) TCG's init context, prologue and target globals have been set up.
- * (2) qemu_tcg_mttcg_enabled() works now (TCG init code runs before the
- * -accel flag is processed, so the check doesn't work then).
- */
- if (!tcg_region_inited) {
- tcg_region_inited = 1;
- tcg_region_init();
- }
-
- if (qemu_tcg_mttcg_enabled() || !single_tcg_cpu_thread) {
- cpu->thread = g_malloc0(sizeof(QemuThread));
- cpu->halt_cond = g_malloc0(sizeof(QemuCond));
- qemu_cond_init(cpu->halt_cond);
-
- if (qemu_tcg_mttcg_enabled()) {
- /* create a thread per vCPU with TCG (MTTCG) */
- parallel_cpus = true;
- snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/TCG",
- cpu->cpu_index);
-
- qemu_thread_create(cpu->thread, thread_name, qemu_tcg_cpu_thread_fn,
- cpu, QEMU_THREAD_JOINABLE);
-
- } else {
- /* 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,
- qemu_tcg_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 {
- /* For non-MTTCG cases 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;
- }
-}
-
-static void qemu_hax_start_vcpu(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/HAX",
- cpu->cpu_index);
- qemu_thread_create(cpu->thread, thread_name, qemu_hax_cpu_thread_fn,
- cpu, QEMU_THREAD_JOINABLE);
-#ifdef _WIN32
- cpu->hThread = qemu_thread_get_handle(cpu->thread);
-#endif
-}
-
-static void qemu_kvm_start_vcpu(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, qemu_kvm_cpu_thread_fn,
- cpu, QEMU_THREAD_JOINABLE);
-}
-
-static void qemu_hvf_start_vcpu(CPUState *cpu)
-{
- char thread_name[VCPU_THREAD_NAME_SIZE];
-
- /* HVF currently does not support TCG, and only runs in
- * unrestricted-guest mode. */
- assert(hvf_enabled());
-
- 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/HVF",
- cpu->cpu_index);
- qemu_thread_create(cpu->thread, thread_name, qemu_hvf_cpu_thread_fn,
- cpu, QEMU_THREAD_JOINABLE);
-}
-
-static void qemu_whpx_start_vcpu(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/WHPX",
- cpu->cpu_index);
- qemu_thread_create(cpu->thread, thread_name, qemu_whpx_cpu_thread_fn,
- cpu, QEMU_THREAD_JOINABLE);
-#ifdef _WIN32
- cpu->hThread = qemu_thread_get_handle(cpu->thread);
-#endif
-}
-
-static void qemu_dummy_start_vcpu(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, qemu_dummy_cpu_thread_fn, cpu,
- QEMU_THREAD_JOINABLE);
-}
-
-void qemu_init_vcpu(CPUState *cpu)
-{
- MachineState *ms = MACHINE(qdev_get_machine());
-
- cpu->nr_cores = ms->smp.cores;
- cpu->nr_threads = ms->smp.threads;
- cpu->stopped = true;
- cpu->random_seed = qemu_guest_random_seed_thread_part1();
-
- if (!cpu->as) {
- /* If the target cpu hasn't set up any address spaces itself,
- * give it the default one.
- */
- cpu->num_ases = 1;
- cpu_address_space_init(cpu, 0, "cpu-memory", cpu->memory);
- }
-
- if (kvm_enabled()) {
- qemu_kvm_start_vcpu(cpu);
- } else if (hax_enabled()) {
- qemu_hax_start_vcpu(cpu);
- } else if (hvf_enabled()) {
- qemu_hvf_start_vcpu(cpu);
- } else if (tcg_enabled()) {
- qemu_tcg_init_vcpu(cpu);
- } else if (whpx_enabled()) {
- qemu_whpx_start_vcpu(cpu);
- } else {
- qemu_dummy_start_vcpu(cpu);
- }
-
- while (!cpu->created) {
- qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
- }
-}
-
-void cpu_stop_current(void)
-{
- if (current_cpu) {
- current_cpu->stop = true;
- cpu_exit(current_cpu);
- }
-}
-
-int vm_stop(RunState state)
-{
- if (qemu_in_vcpu_thread()) {
- qemu_system_vmstop_request_prepare();
- qemu_system_vmstop_request(state);
- /*
- * FIXME: should not return to device code in case
- * vm_stop() has been requested.
- */
- cpu_stop_current();
- return 0;
- }
-
- return do_vm_stop(state, true);
-}
-
-/**
- * Prepare for (re)starting the VM.
- * Returns -1 if the vCPUs are not to be restarted (e.g. if they are already
- * running or in case of an error condition), 0 otherwise.
- */
-int vm_prepare_start(void)
-{
- RunState requested;
-
- qemu_vmstop_requested(&requested);
- if (runstate_is_running() && requested == RUN_STATE__MAX) {
- return -1;
- }
-
- /* Ensure that a STOP/RESUME pair of events is emitted if a
- * vmstop request was pending. The BLOCK_IO_ERROR event, for
- * example, according to documentation is always followed by
- * the STOP event.
- */
- if (runstate_is_running()) {
- qapi_event_send_stop();
- qapi_event_send_resume();
- return -1;
- }
-
- /* We are sending this now, but the CPUs will be resumed shortly later */
- qapi_event_send_resume();
-
- cpu_enable_ticks();
- runstate_set(RUN_STATE_RUNNING);
- vm_state_notify(1, RUN_STATE_RUNNING);
- return 0;
-}
-
-void vm_start(void)
-{
- if (!vm_prepare_start()) {
- resume_all_vcpus();
- }
-}
-
-/* does a state transition even if the VM is already stopped,
- current state is forgotten forever */
-int vm_stop_force_state(RunState state)
-{
- if (runstate_is_running()) {
- return vm_stop(state);
- } else {
- runstate_set(state);
-
- bdrv_drain_all();
- /* Make sure to return an error if the flush in a previous vm_stop()
- * failed. */
- return bdrv_flush_all();
- }
-}
-
-void list_cpus(const char *optarg)
-{
- /* XXX: implement xxx_cpu_list for targets that still miss it */
-#if defined(cpu_list)
- cpu_list();
-#endif
-}
-
-void qmp_memsave(int64_t addr, int64_t size, const char *filename,
- bool has_cpu, int64_t cpu_index, Error **errp)
-{
- FILE *f;
- uint32_t l;
- CPUState *cpu;
- uint8_t buf[1024];
- int64_t orig_addr = addr, orig_size = size;
-
- if (!has_cpu) {
- cpu_index = 0;
- }
-
- cpu = qemu_get_cpu(cpu_index);
- if (cpu == NULL) {
- error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index",
- "a CPU number");
- return;
- }
-
- f = fopen(filename, "wb");
- if (!f) {
- error_setg_file_open(errp, errno, filename);
- return;
- }
-
- while (size != 0) {
- l = sizeof(buf);
- if (l > size)
- l = size;
- if (cpu_memory_rw_debug(cpu, addr, buf, l, 0) != 0) {
- error_setg(errp, "Invalid addr 0x%016" PRIx64 "/size %" PRId64
- " specified", orig_addr, orig_size);
- goto exit;
- }
- if (fwrite(buf, 1, l, f) != l) {
- error_setg(errp, QERR_IO_ERROR);
- goto exit;
- }
- addr += l;
- size -= l;
- }
-
-exit:
- fclose(f);
-}
-
-void qmp_pmemsave(int64_t addr, int64_t size, const char *filename,
- Error **errp)
-{
- FILE *f;
- uint32_t l;
- uint8_t buf[1024];
-
- f = fopen(filename, "wb");
- if (!f) {
- error_setg_file_open(errp, errno, filename);
- return;
- }
-
- while (size != 0) {
- l = sizeof(buf);
- if (l > size)
- l = size;
- cpu_physical_memory_read(addr, buf, l);
- if (fwrite(buf, 1, l, f) != l) {
- error_setg(errp, QERR_IO_ERROR);
- goto exit;
- }
- addr += l;
- size -= l;
- }
-
-exit:
- fclose(f);
-}
-
-void qmp_inject_nmi(Error **errp)
-{
- nmi_monitor_handle(monitor_get_cpu_index(), errp);
-}
-
-void dump_drift_info(void)
-{
- if (!use_icount) {
- return;
- }
-
- qemu_printf("Host - Guest clock %"PRIi64" ms\n",
- (cpu_get_clock() - cpu_get_icount())/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");
- }
-}
--- /dev/null
+# Default configuration for avr-softmmu
+
+# Boards:
+#
+CONFIG_ARDUINO=y
fprintf_fn (stream, "xd%d", rn & ~1);
break;
}
+ /* fallthrough */
case D_REG_N:
fprintf_fn (stream, "dr%d", rn);
break;
fprintf_fn (stream, "xd%d", rm & ~1);
break;
}
+ /* fallthrough */
case D_REG_M:
fprintf_fn (stream, "dr%d", rm);
break;
This builds ./i386-softmmu/qemu-fuzz-i386
-The first option to this command is: --fuzz_taget=FUZZ_NAME
+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.
-eg:
- ./i386-softmmu/qemu-fuzz-i386 --fuzz-target=virtio-net-fork-fuzz
+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
decodetree
secure-coding-practices
tcg
+ tcg-icount
+ multi-thread-tcg
tcg-plugins
bitops
reset
--- /dev/null
+..
+ Copyright (c) 2015-2020 Linaro Ltd.
+
+ This work is licensed under the terms of the GNU GPL, version 2 or
+ later. See the COPYING file in the top-level directory.
+
+Introduction
+============
+
+This document outlines the design for multi-threaded TCG (a.k.a MTTCG)
+system-mode emulation. user-mode emulation has always mirrored the
+thread structure of the translated executable although some of the
+changes done for MTTCG system emulation have improved the stability of
+linux-user emulation.
+
+The original system-mode TCG implementation was single threaded and
+dealt with multiple CPUs with simple round-robin scheduling. This
+simplified a lot of things but became increasingly limited as systems
+being emulated gained additional cores and per-core performance gains
+for host systems started to level off.
+
+vCPU Scheduling
+===============
+
+We introduce a new running mode where each vCPU will run on its own
+user-space thread. This is enabled by default for all FE/BE
+combinations where the host memory model is able to accommodate the
+guest (TCG_GUEST_DEFAULT_MO & ~TCG_TARGET_DEFAULT_MO is zero) and the
+guest has had the required work done to support this safely
+(TARGET_SUPPORTS_MTTCG).
+
+System emulation will fall back to the original round robin approach
+if:
+
+* forced by --accel tcg,thread=single
+* enabling --icount mode
+* 64 bit guests on 32 bit hosts (TCG_OVERSIZED_GUEST)
+
+In the general case of running translated code there should be no
+inter-vCPU dependencies and all vCPUs should be able to run at full
+speed. Synchronisation will only be required while accessing internal
+shared data structures or when the emulated architecture requires a
+coherent representation of the emulated machine state.
+
+Shared Data Structures
+======================
+
+Main Run Loop
+-------------
+
+Even when there is no code being generated there are a number of
+structures associated with the hot-path through the main run-loop.
+These are associated with looking up the next translation block to
+execute. These include:
+
+ tb_jmp_cache (per-vCPU, cache of recent jumps)
+ tb_ctx.htable (global hash table, phys address->tb lookup)
+
+As TB linking only occurs when blocks are in the same page this code
+is critical to performance as looking up the next TB to execute is the
+most common reason to exit the generated code.
+
+DESIGN REQUIREMENT: Make access to lookup structures safe with
+multiple reader/writer threads. Minimise any lock contention to do it.
+
+The hot-path avoids using locks where possible. The tb_jmp_cache is
+updated with atomic accesses to ensure consistent results. The fall
+back QHT based hash table is also designed for lockless lookups. Locks
+are only taken when code generation is required or TranslationBlocks
+have their block-to-block jumps patched.
+
+Global TCG State
+----------------
+
+User-mode emulation
+~~~~~~~~~~~~~~~~~~~
+
+We need to protect the entire code generation cycle including any post
+generation patching of the translated code. This also implies a shared
+translation buffer which contains code running on all cores. Any
+execution path that comes to the main run loop will need to hold a
+mutex for code generation. This also includes times when we need flush
+code or entries from any shared lookups/caches. Structures held on a
+per-vCPU basis won't need locking unless other vCPUs will need to
+modify them.
+
+DESIGN REQUIREMENT: Add locking around all code generation and TB
+patching.
+
+(Current solution)
+
+Code generation is serialised with mmap_lock().
+
+!User-mode emulation
+~~~~~~~~~~~~~~~~~~~~
+
+Each vCPU has its own TCG context and associated TCG region, thereby
+requiring no locking during translation.
+
+Translation Blocks
+------------------
+
+Currently the whole system shares a single code generation buffer
+which when full will force a flush of all translations and start from
+scratch again. Some operations also force a full flush of translations
+including:
+
+ - debugging operations (breakpoint insertion/removal)
+ - some CPU helper functions
+ - linux-user spawning its first thread
+
+This is done with the async_safe_run_on_cpu() mechanism to ensure all
+vCPUs are quiescent when changes are being made to shared global
+structures.
+
+More granular translation invalidation events are typically due
+to a change of the state of a physical page:
+
+ - code modification (self modify code, patching code)
+ - page changes (new page mapping in linux-user mode)
+
+While setting the invalid flag in a TranslationBlock will stop it
+being used when looked up in the hot-path there are a number of other
+book-keeping structures that need to be safely cleared.
+
+Any TranslationBlocks which have been patched to jump directly to the
+now invalid blocks need the jump patches reversing so they will return
+to the C code.
+
+There are a number of look-up caches that need to be properly updated
+including the:
+
+ - jump lookup cache
+ - the physical-to-tb lookup hash table
+ - the global page table
+
+The global page table (l1_map) which provides a multi-level look-up
+for PageDesc structures which contain pointers to the start of a
+linked list of all Translation Blocks in that page (see page_next).
+
+Both the jump patching and the page cache involve linked lists that
+the invalidated TranslationBlock needs to be removed from.
+
+DESIGN REQUIREMENT: Safely handle invalidation of TBs
+ - safely patch/revert direct jumps
+ - remove central PageDesc lookup entries
+ - ensure lookup caches/hashes are safely updated
+
+(Current solution)
+
+The direct jump themselves are updated atomically by the TCG
+tb_set_jmp_target() code. Modification to the linked lists that allow
+searching for linked pages are done under the protection of tb->jmp_lock,
+where tb is the destination block of a jump. Each origin block keeps a
+pointer to its destinations so that the appropriate lock can be acquired before
+iterating over a jump list.
+
+The global page table is a lockless radix tree; cmpxchg is used
+to atomically insert new elements.
+
+The lookup caches are updated atomically and the lookup hash uses QHT
+which is designed for concurrent safe lookup.
+
+Parallel code generation is supported. QHT is used at insertion time
+as the synchronization point across threads, thereby ensuring that we only
+keep track of a single TranslationBlock for each guest code block.
+
+Memory maps and TLBs
+--------------------
+
+The memory handling code is fairly critical to the speed of memory
+access in the emulated system. The SoftMMU code is designed so the
+hot-path can be handled entirely within translated code. This is
+handled with a per-vCPU TLB structure which once populated will allow
+a series of accesses to the page to occur without exiting the
+translated code. It is possible to set flags in the TLB address which
+will ensure the slow-path is taken for each access. This can be done
+to support:
+
+ - Memory regions (dividing up access to PIO, MMIO and RAM)
+ - Dirty page tracking (for code gen, SMC detection, migration and display)
+ - Virtual TLB (for translating guest address->real address)
+
+When the TLB tables are updated by a vCPU thread other than their own
+we need to ensure it is done in a safe way so no inconsistent state is
+seen by the vCPU thread.
+
+Some operations require updating a number of vCPUs TLBs at the same
+time in a synchronised manner.
+
+DESIGN REQUIREMENTS:
+
+ - TLB Flush All/Page
+ - can be across-vCPUs
+ - cross vCPU TLB flush may need other vCPU brought to halt
+ - change may need to be visible to the calling vCPU immediately
+ - TLB Flag Update
+ - usually cross-vCPU
+ - want change to be visible as soon as possible
+ - TLB Update (update a CPUTLBEntry, via tlb_set_page_with_attrs)
+ - This is a per-vCPU table - by definition can't race
+ - updated by its own thread when the slow-path is forced
+
+(Current solution)
+
+We have updated cputlb.c to defer operations when a cross-vCPU
+operation with async_run_on_cpu() which ensures each vCPU sees a
+coherent state when it next runs its work (in a few instructions
+time).
+
+A new set up operations (tlb_flush_*_all_cpus) take an additional flag
+which when set will force synchronisation by setting the source vCPUs
+work as "safe work" and exiting the cpu run loop. This ensure by the
+time execution restarts all flush operations have completed.
+
+TLB flag updates are all done atomically and are also protected by the
+corresponding page lock.
+
+(Known limitation)
+
+Not really a limitation but the wait mechanism is overly strict for
+some architectures which only need flushes completed by a barrier
+instruction. This could be a future optimisation.
+
+Emulated hardware state
+-----------------------
+
+Currently thanks to KVM work any access to IO memory is automatically
+protected by the global iothread mutex, also known as the BQL (Big
+Qemu Lock). Any IO region that doesn't use global mutex is expected to
+do its own locking.
+
+However IO memory isn't the only way emulated hardware state can be
+modified. Some architectures have model specific registers that
+trigger hardware emulation features. Generally any translation helper
+that needs to update more than a single vCPUs of state should take the
+BQL.
+
+As the BQL, or global iothread mutex is shared across the system we
+push the use of the lock as far down into the TCG code as possible to
+minimise contention.
+
+(Current solution)
+
+MMIO access automatically serialises hardware emulation by way of the
+BQL. Currently Arm targets serialise all ARM_CP_IO register accesses
+and also defer the reset/startup of vCPUs to the vCPU context by way
+of async_run_on_cpu().
+
+Updates to interrupt state are also protected by the BQL as they can
+often be cross vCPU.
+
+Memory Consistency
+==================
+
+Between emulated guests and host systems there are a range of memory
+consistency models. Even emulating weakly ordered systems on strongly
+ordered hosts needs to ensure things like store-after-load re-ordering
+can be prevented when the guest wants to.
+
+Memory Barriers
+---------------
+
+Barriers (sometimes known as fences) provide a mechanism for software
+to enforce a particular ordering of memory operations from the point
+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
+<https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/plain/Documentation/memory-barriers.txt>`
+on the various forms of memory barrier and the guarantees they can
+provide.
+
+Barriers are often wrapped around synchronisation primitives to
+provide explicit memory ordering semantics. However they can be used
+by themselves to provide safe lockless access by ensuring for example
+a change to a signal flag will only be visible once the changes to
+payload are.
+
+DESIGN REQUIREMENT: Add a new tcg_memory_barrier op
+
+This would enforce a strong load/store ordering so all loads/stores
+complete at the memory barrier. On single-core non-SMP strongly
+ordered backends this could become a NOP.
+
+Aside from explicit standalone memory barrier instructions there are
+also implicit memory ordering semantics which comes with each guest
+memory access instruction. For example all x86 load/stores come with
+fairly strong guarantees of sequential consistency whereas Arm has
+special variants of load/store instructions that imply acquire/release
+semantics.
+
+In the case of a strongly ordered guest architecture being emulated on
+a weakly ordered host the scope for a heavy performance impact is
+quite high.
+
+DESIGN REQUIREMENTS: Be efficient with use of memory barriers
+ - host systems with stronger implied guarantees can skip some barriers
+ - merge consecutive barriers to the strongest one
+
+(Current solution)
+
+The system currently has a tcg_gen_mb() which will add memory barrier
+operations if code generation is being done in a parallel context. The
+tcg_optimize() function attempts to merge barriers up to their
+strongest form before any load/store operations. The solution was
+originally developed and tested for linux-user based systems. All
+backends have been converted to emit fences when required. So far the
+following front-ends have been updated to emit fences when required:
+
+ - target-i386
+ - target-arm
+ - target-aarch64
+ - target-alpha
+ - target-mips
+
+Memory Control and Maintenance
+------------------------------
+
+This includes a class of instructions for controlling system cache
+behaviour. While QEMU doesn't model cache behaviour these instructions
+are often seen when code modification has taken place to ensure the
+changes take effect.
+
+Synchronisation Primitives
+--------------------------
+
+There are two broad types of synchronisation primitives found in
+modern ISAs: atomic instructions and exclusive regions.
+
+The first type offer a simple atomic instruction which will guarantee
+some sort of test and conditional store will be truly atomic w.r.t.
+other cores sharing access to the memory. The classic example is the
+x86 cmpxchg instruction.
+
+The second type offer a pair of load/store instructions which offer a
+guarantee that a region of memory has not been touched between the
+load and store instructions. An example of this is Arm's ldrex/strex
+pair where the strex instruction will return a flag indicating a
+successful store only if no other CPU has accessed the memory region
+since the ldrex.
+
+Traditionally TCG has generated a series of operations that work
+because they are within the context of a single translation block so
+will have completed before another CPU is scheduled. However with
+the ability to have multiple threads running to emulate multiple CPUs
+we will need to explicitly expose these semantics.
+
+DESIGN REQUIREMENTS:
+ - Support classic atomic instructions
+ - Support load/store exclusive (or load link/store conditional) pairs
+ - Generic enough infrastructure to support all guest architectures
+CURRENT OPEN QUESTIONS:
+ - How problematic is the ABA problem in general?
+
+(Current solution)
+
+The TCG provides a number of atomic helpers (tcg_gen_atomic_*) which
+can be used directly or combined to emulate other instructions like
+Arm's ldrex/strex instructions. While they are susceptible to the ABA
+problem so far common guests have not implemented patterns where
+this may be a problem - typically presenting a locking ABI which
+assumes cmpxchg like semantics.
+
+The code also includes a fall-back for cases where multi-threaded TCG
+ops can't work (e.g. guest atomic width > host atomic width). In this
+case an EXCP_ATOMIC exit occurs and the instruction is emulated with
+an exclusive lock which ensures all emulation is serialised.
+
+While the atomic helpers look good enough for now there may be a need
+to look at solutions that can more closely model the guest
+architectures semantics.
+++ /dev/null
-Copyright (c) 2015-2016 Linaro Ltd.
-
-This work is licensed under the terms of the GNU GPL, version 2 or
-later. See the COPYING file in the top-level directory.
-
-Introduction
-============
-
-This document outlines the design for multi-threaded TCG system-mode
-emulation. The current user-mode emulation mirrors the thread
-structure of the translated executable. Some of the work will be
-applicable to both system and linux-user emulation.
-
-The original system-mode TCG implementation was single threaded and
-dealt with multiple CPUs with simple round-robin scheduling. This
-simplified a lot of things but became increasingly limited as systems
-being emulated gained additional cores and per-core performance gains
-for host systems started to level off.
-
-vCPU Scheduling
-===============
-
-We introduce a new running mode where each vCPU will run on its own
-user-space thread. This will be enabled by default for all FE/BE
-combinations that have had the required work done to support this
-safely.
-
-In the general case of running translated code there should be no
-inter-vCPU dependencies and all vCPUs should be able to run at full
-speed. Synchronisation will only be required while accessing internal
-shared data structures or when the emulated architecture requires a
-coherent representation of the emulated machine state.
-
-Shared Data Structures
-======================
-
-Main Run Loop
--------------
-
-Even when there is no code being generated there are a number of
-structures associated with the hot-path through the main run-loop.
-These are associated with looking up the next translation block to
-execute. These include:
-
- tb_jmp_cache (per-vCPU, cache of recent jumps)
- tb_ctx.htable (global hash table, phys address->tb lookup)
-
-As TB linking only occurs when blocks are in the same page this code
-is critical to performance as looking up the next TB to execute is the
-most common reason to exit the generated code.
-
-DESIGN REQUIREMENT: Make access to lookup structures safe with
-multiple reader/writer threads. Minimise any lock contention to do it.
-
-The hot-path avoids using locks where possible. The tb_jmp_cache is
-updated with atomic accesses to ensure consistent results. The fall
-back QHT based hash table is also designed for lockless lookups. Locks
-are only taken when code generation is required or TranslationBlocks
-have their block-to-block jumps patched.
-
-Global TCG State
-----------------
-
-### User-mode emulation
-We need to protect the entire code generation cycle including any post
-generation patching of the translated code. This also implies a shared
-translation buffer which contains code running on all cores. Any
-execution path that comes to the main run loop will need to hold a
-mutex for code generation. This also includes times when we need flush
-code or entries from any shared lookups/caches. Structures held on a
-per-vCPU basis won't need locking unless other vCPUs will need to
-modify them.
-
-DESIGN REQUIREMENT: Add locking around all code generation and TB
-patching.
-
-(Current solution)
-
-Code generation is serialised with mmap_lock().
-
-### !User-mode emulation
-Each vCPU has its own TCG context and associated TCG region, thereby
-requiring no locking.
-
-Translation Blocks
-------------------
-
-Currently the whole system shares a single code generation buffer
-which when full will force a flush of all translations and start from
-scratch again. Some operations also force a full flush of translations
-including:
-
- - debugging operations (breakpoint insertion/removal)
- - some CPU helper functions
-
-This is done with the async_safe_run_on_cpu() mechanism to ensure all
-vCPUs are quiescent when changes are being made to shared global
-structures.
-
-More granular translation invalidation events are typically due
-to a change of the state of a physical page:
-
- - code modification (self modify code, patching code)
- - page changes (new page mapping in linux-user mode)
-
-While setting the invalid flag in a TranslationBlock will stop it
-being used when looked up in the hot-path there are a number of other
-book-keeping structures that need to be safely cleared.
-
-Any TranslationBlocks which have been patched to jump directly to the
-now invalid blocks need the jump patches reversing so they will return
-to the C code.
-
-There are a number of look-up caches that need to be properly updated
-including the:
-
- - jump lookup cache
- - the physical-to-tb lookup hash table
- - the global page table
-
-The global page table (l1_map) which provides a multi-level look-up
-for PageDesc structures which contain pointers to the start of a
-linked list of all Translation Blocks in that page (see page_next).
-
-Both the jump patching and the page cache involve linked lists that
-the invalidated TranslationBlock needs to be removed from.
-
-DESIGN REQUIREMENT: Safely handle invalidation of TBs
- - safely patch/revert direct jumps
- - remove central PageDesc lookup entries
- - ensure lookup caches/hashes are safely updated
-
-(Current solution)
-
-The direct jump themselves are updated atomically by the TCG
-tb_set_jmp_target() code. Modification to the linked lists that allow
-searching for linked pages are done under the protection of tb->jmp_lock,
-where tb is the destination block of a jump. Each origin block keeps a
-pointer to its destinations so that the appropriate lock can be acquired before
-iterating over a jump list.
-
-The global page table is a lockless radix tree; cmpxchg is used
-to atomically insert new elements.
-
-The lookup caches are updated atomically and the lookup hash uses QHT
-which is designed for concurrent safe lookup.
-
-Parallel code generation is supported. QHT is used at insertion time
-as the synchronization point across threads, thereby ensuring that we only
-keep track of a single TranslationBlock for each guest code block.
-
-Memory maps and TLBs
---------------------
-
-The memory handling code is fairly critical to the speed of memory
-access in the emulated system. The SoftMMU code is designed so the
-hot-path can be handled entirely within translated code. This is
-handled with a per-vCPU TLB structure which once populated will allow
-a series of accesses to the page to occur without exiting the
-translated code. It is possible to set flags in the TLB address which
-will ensure the slow-path is taken for each access. This can be done
-to support:
-
- - Memory regions (dividing up access to PIO, MMIO and RAM)
- - Dirty page tracking (for code gen, SMC detection, migration and display)
- - Virtual TLB (for translating guest address->real address)
-
-When the TLB tables are updated by a vCPU thread other than their own
-we need to ensure it is done in a safe way so no inconsistent state is
-seen by the vCPU thread.
-
-Some operations require updating a number of vCPUs TLBs at the same
-time in a synchronised manner.
-
-DESIGN REQUIREMENTS:
-
- - TLB Flush All/Page
- - can be across-vCPUs
- - cross vCPU TLB flush may need other vCPU brought to halt
- - change may need to be visible to the calling vCPU immediately
- - TLB Flag Update
- - usually cross-vCPU
- - want change to be visible as soon as possible
- - TLB Update (update a CPUTLBEntry, via tlb_set_page_with_attrs)
- - This is a per-vCPU table - by definition can't race
- - updated by its own thread when the slow-path is forced
-
-(Current solution)
-
-We have updated cputlb.c to defer operations when a cross-vCPU
-operation with async_run_on_cpu() which ensures each vCPU sees a
-coherent state when it next runs its work (in a few instructions
-time).
-
-A new set up operations (tlb_flush_*_all_cpus) take an additional flag
-which when set will force synchronisation by setting the source vCPUs
-work as "safe work" and exiting the cpu run loop. This ensure by the
-time execution restarts all flush operations have completed.
-
-TLB flag updates are all done atomically and are also protected by the
-corresponding page lock.
-
-(Known limitation)
-
-Not really a limitation but the wait mechanism is overly strict for
-some architectures which only need flushes completed by a barrier
-instruction. This could be a future optimisation.
-
-Emulated hardware state
------------------------
-
-Currently thanks to KVM work any access to IO memory is automatically
-protected by the global iothread mutex, also known as the BQL (Big
-Qemu Lock). Any IO region that doesn't use global mutex is expected to
-do its own locking.
-
-However IO memory isn't the only way emulated hardware state can be
-modified. Some architectures have model specific registers that
-trigger hardware emulation features. Generally any translation helper
-that needs to update more than a single vCPUs of state should take the
-BQL.
-
-As the BQL, or global iothread mutex is shared across the system we
-push the use of the lock as far down into the TCG code as possible to
-minimise contention.
-
-(Current solution)
-
-MMIO access automatically serialises hardware emulation by way of the
-BQL. Currently Arm targets serialise all ARM_CP_IO register accesses
-and also defer the reset/startup of vCPUs to the vCPU context by way
-of async_run_on_cpu().
-
-Updates to interrupt state are also protected by the BQL as they can
-often be cross vCPU.
-
-Memory Consistency
-==================
-
-Between emulated guests and host systems there are a range of memory
-consistency models. Even emulating weakly ordered systems on strongly
-ordered hosts needs to ensure things like store-after-load re-ordering
-can be prevented when the guest wants to.
-
-Memory Barriers
----------------
-
-Barriers (sometimes known as fences) provide a mechanism for software
-to enforce a particular ordering of memory operations from the point
-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 [1].
-
-Barriers are often wrapped around synchronisation primitives to
-provide explicit memory ordering semantics. However they can be used
-by themselves to provide safe lockless access by ensuring for example
-a change to a signal flag will only be visible once the changes to
-payload are.
-
-DESIGN REQUIREMENT: Add a new tcg_memory_barrier op
-
-This would enforce a strong load/store ordering so all loads/stores
-complete at the memory barrier. On single-core non-SMP strongly
-ordered backends this could become a NOP.
-
-Aside from explicit standalone memory barrier instructions there are
-also implicit memory ordering semantics which comes with each guest
-memory access instruction. For example all x86 load/stores come with
-fairly strong guarantees of sequential consistency whereas Arm has
-special variants of load/store instructions that imply acquire/release
-semantics.
-
-In the case of a strongly ordered guest architecture being emulated on
-a weakly ordered host the scope for a heavy performance impact is
-quite high.
-
-DESIGN REQUIREMENTS: Be efficient with use of memory barriers
- - host systems with stronger implied guarantees can skip some barriers
- - merge consecutive barriers to the strongest one
-
-(Current solution)
-
-The system currently has a tcg_gen_mb() which will add memory barrier
-operations if code generation is being done in a parallel context. The
-tcg_optimize() function attempts to merge barriers up to their
-strongest form before any load/store operations. The solution was
-originally developed and tested for linux-user based systems. All
-backends have been converted to emit fences when required. So far the
-following front-ends have been updated to emit fences when required:
-
- - target-i386
- - target-arm
- - target-aarch64
- - target-alpha
- - target-mips
-
-Memory Control and Maintenance
-------------------------------
-
-This includes a class of instructions for controlling system cache
-behaviour. While QEMU doesn't model cache behaviour these instructions
-are often seen when code modification has taken place to ensure the
-changes take effect.
-
-Synchronisation Primitives
---------------------------
-
-There are two broad types of synchronisation primitives found in
-modern ISAs: atomic instructions and exclusive regions.
-
-The first type offer a simple atomic instruction which will guarantee
-some sort of test and conditional store will be truly atomic w.r.t.
-other cores sharing access to the memory. The classic example is the
-x86 cmpxchg instruction.
-
-The second type offer a pair of load/store instructions which offer a
-guarantee that a region of memory has not been touched between the
-load and store instructions. An example of this is Arm's ldrex/strex
-pair where the strex instruction will return a flag indicating a
-successful store only if no other CPU has accessed the memory region
-since the ldrex.
-
-Traditionally TCG has generated a series of operations that work
-because they are within the context of a single translation block so
-will have completed before another CPU is scheduled. However with
-the ability to have multiple threads running to emulate multiple CPUs
-we will need to explicitly expose these semantics.
-
-DESIGN REQUIREMENTS:
- - Support classic atomic instructions
- - Support load/store exclusive (or load link/store conditional) pairs
- - Generic enough infrastructure to support all guest architectures
-CURRENT OPEN QUESTIONS:
- - How problematic is the ABA problem in general?
-
-(Current solution)
-
-The TCG provides a number of atomic helpers (tcg_gen_atomic_*) which
-can be used directly or combined to emulate other instructions like
-Arm's ldrex/strex instructions. While they are susceptible to the ABA
-problem so far common guests have not implemented patterns where
-this may be a problem - typically presenting a locking ABI which
-assumes cmpxchg like semantics.
-
-The code also includes a fall-back for cases where multi-threaded TCG
-ops can't work (e.g. guest atomic width > host atomic width). In this
-case an EXCP_ATOMIC exit occurs and the instruction is emulated with
-an exclusive lock which ensures all emulation is serialised.
-
-While the atomic helpers look good enough for now there may be a need
-to look at solutions that can more closely model the guest
-architectures semantics.
-
-==========
-
-[1] https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/plain/Documentation/memory-barriers.txt
--- /dev/null
+..
+ Copyright (c) 2020, Linaro Limited
+ Written by Alex Bennée
+
+
+========================
+TCG Instruction Counting
+========================
+
+TCG has long supported a feature known as icount which allows for
+instruction counting during execution. This should not be confused
+with cycle accurate emulation - QEMU does not attempt to emulate how
+long an instruction would take on real hardware. That is a job for
+other more detailed (and slower) tools that simulate the rest of a
+micro-architecture.
+
+This feature is only available for system emulation and is
+incompatible with multi-threaded TCG. It can be used to better align
+execution time with wall-clock time so a "slow" device doesn't run too
+fast on modern hardware. It can also provides for a degree of
+deterministic execution and is an essential part of the record/replay
+support in QEMU.
+
+Core Concepts
+=============
+
+At its heart icount is simply a count of executed instructions which
+is stored in the TimersState of QEMU's timer sub-system. The number of
+executed instructions can then be used to calculate QEMU_CLOCK_VIRTUAL
+which represents the amount of elapsed time in the system since
+execution started. Depending on the icount mode this may either be a
+fixed number of ns per instruction or adjusted as execution continues
+to keep wall clock time and virtual time in sync.
+
+To be able to calculate the number of executed instructions the
+translator starts by allocating a budget of instructions to be
+executed. The budget of instructions is limited by how long it will be
+until the next timer will expire. We store this budget as part of a
+vCPU icount_decr field which shared with the machinery for handling
+cpu_exit(). The whole field is checked at the start of every
+translated block and will cause a return to the outer loop to deal
+with whatever caused the exit.
+
+In the case of icount, before the flag is checked we subtract the
+number of instructions the translation block would execute. If this
+would cause the instruction budget to go negative we exit the main
+loop and regenerate a new translation block with exactly the right
+number of instructions to take the budget to 0 meaning whatever timer
+was due to expire will expire exactly when we exit the main run loop.
+
+Dealing with MMIO
+-----------------
+
+While we can adjust the instruction budget for known events like timer
+expiry we cannot do the same for MMIO. Every load/store we execute
+might potentially trigger an I/O event, at which point we will need an
+up to date and accurate reading of the icount number.
+
+To deal with this case, when an I/O access is made we:
+
+ - restore un-executed instructions to the icount budget
+ - re-compile a single [1]_ instruction block for the current PC
+ - exit the cpu loop and execute the re-compiled block
+
+The new block is created with the CF_LAST_IO compile flag which
+ensures the final instruction translation starts with a call to
+gen_io_start() so we don't enter a perpetual loop constantly
+recompiling a single instruction block. For translators using the
+common translator_loop this is done automatically.
+
+.. [1] sometimes two instructions if dealing with delay slots
+
+Other I/O operations
+--------------------
+
+MMIO isn't the only type of operation for which we might need a
+correct and accurate clock. IO port instructions and accesses to
+system registers are the common examples here. These instructions have
+to be handled by the individual translators which have the knowledge
+of which operations are I/O operations.
+
+When the translator is handling an instruction of this kind:
+
+* it must call gen_io_start() if icount is enabled, at some
+ point before the generation of the code which actually does
+ the I/O, using a code fragment similar to:
+
+.. code:: c
+
+ if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
+ gen_io_start();
+ }
+
+* it must end the TB immediately after this instruction
+
+Note that some older front-ends call a "gen_io_end()" function:
+this is obsolete and should not be used.
* if=pflash, if=mtd, if=sd, if=xen are not yet available with -device
-For USB devices, the old way is actually different:
-
- -usbdevice disk:format=FMT:FILENAME
-
-Provides much less control than -drive's OPTS... The new way fixes
-that:
+For USB storage devices, you can use something like:
-device usb-storage,drive=DRIVE-ID,removable=RMB
This lets you control I/O ports and IRQs.
-* -usbdevice serial::chardev becomes -device usb-serial,chardev=dev.
-
* -usbdevice braille doesn't support LEGACY-CHARDEV syntax. It always
uses "braille". With -device, this useful default is gone, so you
have to use something like
-net nic,netdev=NET-ID,macaddr=MACADDR,model=MODEL,name=ID,addr=STR,vectors=V
-Except for USB it looks like this:
-
- -usbdevice net:netdev=NET-ID,macaddr=MACADDR,name=ID
-
The new way is -device:
-device DEVNAME,netdev=NET-ID,mac=MACADDR,DEV-OPTS...
* mouse -device usb-mouse
* tablet -device usb-tablet
* wacom-tablet -device usb-wacom-tablet
-* host:... See "Host Device Assignment"
-* disk:... See "Block Devices"
-* serial:... See "Character Devices"
* braille See "Character Devices"
-* net:... See "Network Devices"
-* bt:... not yet available with -device
=== Watchdog Devices ===
-device vfio-pci,host=ADDR,id=ID
-The old way to assign a host USB device is
-
- -usbdevice host:auto:BUS.ADDR:VID:PRID
-
-where any of BUS, ADDR, VID, PRID can be the wildcard *.
-
-The new way is
+To assign a host USB device use:
-device usb-host,hostbus=BUS,hostaddr=ADDR,vendorid=VID,productid=PRID
-Omitted options match anything, just like the old way's wildcard.
+Omitted options match anything.
=== Default Devices ===
Alternatively, you can also choose to build you own image with buildroot
using the orangepi_pc_defconfig. Also see https://buildroot.org for more information.
+When using an image as an SD card, it must be resized to a power of two. This can be
+done with the qemu-img command. It is recommended to only increase the image size
+instead of shrinking it to a power of two, to avoid loss of data. For example,
+to prepare a downloaded Armbian image, first extract it and then increase
+its size to one gigabyte as follows:
+
+.. code-block:: bash
+
+ $ qemu-img resize Armbian_19.11.3_Orangepipc_bionic_current_5.3.9.img 1G
+
You can choose to attach the selected image either as an SD card or as USB mass storage.
For example, to boot using the Orange Pi PC Debian image on SD card, simply add the -sd
argument and provide the proper root= kernel parameter:
$ dd if=/path/to/u-boot-sunxi-with-spl.bin of=armv7.img bs=1024 seek=8 conv=notrunc
Finally, before starting the machine the SD image must be extended such
-that the NetBSD kernel will not conclude the NetBSD partition is larger than
-the emulated SD card:
+that the size of the SD image is a power of two and that the NetBSD kernel
+will not conclude the NetBSD partition is larger than the emulated SD card:
.. code-block:: bash
- $ dd if=/dev/zero bs=1M count=64 >> armv7.img
+ $ qemu-img resize armv7.img 2G
Start the machine using the following command:
Related binaries
----------------
-``qemu-img convert -n -o`` (since 4.2.0)
-''''''''''''''''''''''''''''''''''''''''
+qemu-img amend to adjust backing file (since 5.1)
+'''''''''''''''''''''''''''''''''''''''''''''''''
-All options specified in ``-o`` are image creation options, so
-they have no effect when used with ``-n`` to skip image creation.
-Silently ignored options can be confusing, so this combination of
-options will be made an error in future versions.
+The use of ``qemu-img amend`` to modify the name or format of a qcow2
+backing image is deprecated; this functionality was never fully
+documented or tested, and interferes with other amend operations that
+need access to the original backing image (such as deciding whether a
+v3 zero cluster may be left unallocated when converting to a v2
+image). Rather, any changes to the backing chain should be performed
+with ``qemu-img rebase -u`` either before or after the remaining
+changes being performed by amend, as appropriate.
+
+qemu-img backing file without format (since 5.1)
+''''''''''''''''''''''''''''''''''''''''''''''''
+
+The use of ``qemu-img create``, ``qemu-img rebase``, or ``qemu-img
+convert`` to create or modify an image that depends on a backing file
+now recommends that an explicit backing format be provided. This is
+for safety: if QEMU probes a different format than what you thought,
+the data presented to the guest will be corrupt; similarly, presenting
+a raw image to a guest allows a potential security exploit if a future
+probe sees a non-raw image based on guest writes.
+
+To avoid the warning message, or even future refusal to create an
+unsafe image, you must pass ``-o backing_fmt=`` (or the shorthand
+``-F`` during create) to specify the intended backing format. You may
+use ``qemu-img rebase -u`` to retroactively add a backing format to an
+existing image. However, be aware that there are already potential
+security risks to blindly using ``qemu-img info`` to probe the format
+of an untrusted backing image, when deciding what format to add into
+an existing image.
Backwards compatibility
-----------------------
Related binaries
----------------
-``qemu-nbd --partition`` (removed in 5.0.0)
-'''''''''''''''''''''''''''''''''''''''''''
+``qemu-nbd --partition`` (removed in 5.0)
+'''''''''''''''''''''''''''''''''''''''''
The ``qemu-nbd --partition $digit`` code (also spelled ``-P``)
could only handle MBR partitions, and never correctly handled logical
qemu-nbd -t --image-opts driver=raw,offset=1M,size=100M,file.driver=qcow2,file.file.driver=file,file.file.filename=file.qcow2
+``qemu-img convert -n -o`` (removed in 5.1)
+'''''''''''''''''''''''''''''''''''''''''''
+
+All options specified in ``-o`` are image creation options, so
+they are now rejected when used with ``-n`` to skip image creation.
+
+
+``qemu-img create -b bad file $size`` (removed in 5.1)
+''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+When creating an image with a backing file that could not be opened,
+``qemu-img create`` used to issue a warning about the failure but
+proceed with the image creation if an explicit size was provided.
+However, as the ``-u`` option exists for this purpose, it is safer to
+enforce that any failure to open the backing image (including if the
+backing file is missing or an incorrect format was specified) is an
+error when ``-u`` is not used.
+
Command line options
--------------------
New machine versions (since 5.1) will not accept the option but it will still
work with old machine types. User can check the QAPI schema to see if the legacy
option is supported by looking at MachineInfo::numa-mem-supported property.
+
+Block devices
+-------------
+
+VXHS backend (removed in 5.1)
+'''''''''''''''''''''''''''''
+
+The VXHS code does not compile since v2.12.0. It was removed in 5.1.
(gdb) maintenance packet Qqemu.sstep=0x5
sending: "qemu.sstep=0x5"
received: "OK"
+
+
+Another feature that QEMU gdbstub provides is to toggle the memory GDB
+works with, by default GDB will show the current process memory respecting
+the virtual address translation.
+
+If you want to examine/change the physical memory you can set the gdbstub
+to work with the physical memory rather with the virtual one.
+
+The memory mode can be checked by sending the following command:
+
+``maintenance packet qqemu.PhyMemMode``
+ This will return either 0 or 1, 1 indicates you are currently in the
+ physical memory mode.
+
+``maintenance packet Qqemu.PhyMemMode:1``
+ This will change the memory mode to physical memory.
+
+``maintenance packet Qqemu.PhyMemMode:0``
+ This will change it back to normal memory mode.
3270 devices
============
-QEMU supports connecting an external 3270 terminal emulator (such as
-``x3270``) to make a single 3270 device available to a guest. Note that this
-supports basic features only.
+The 3270 is the classic 'green-screen' console of the mainframes (see the
+`IBM 3270 Wikipedia article <https://en.wikipedia.org/wiki/IBM_3270>`__).
+
+The 3270 data stream is not implemented within QEMU; the device only provides
+TN3270 (a telnet extension; see `RFC 854 <https://tools.ietf.org/html/rfc854>`__
+and `RFC 1576 <https://tools.ietf.org/html/rfc1576>`__) and leaves the heavy
+lifting to an external 3270 terminal emulator (such as ``x3270``) to make a
+single 3270 device available to a guest. Note that this supports basic
+features only.
To provide a 3270 device to a guest, create a ``x-terminal3270`` linked to
a ``tn3270`` chardev. The guest will see a 3270 channel device. In order
Example configuration
---------------------
+* Make sure that 3270 support is enabled in the guest's Linux kernel. You need
+ ``CONFIG_TN3270`` and at least one of ``CONFIG_TN3270_TTY`` (for additional
+ ttys) or ``CONFIG_TN3270_CONSOLE`` (for a 3270 console).
+
* Add a ``tn3270`` chardev and a ``x-terminal3270`` to the QEMU command line::
- -chardev socket,id=char_0,host=0.0.0.0,port=2300,nowait,server,tn3270
- -device x-terminal3270,chardev=char_0,devno=fe.0.000a,id=terminal_0
+ -chardev socket,id=ch0,host=0.0.0.0,port=2300,nowait,server,tn3270
+ -device x-terminal3270,chardev=ch0,devno=fe.0.000a,id=terminal0
* Start the guest. In the guest, use ``chccwdev -e 0.0.000a`` to enable
the device.
systemctl start serial-getty@3270-tty1.service
-This should get you an addtional tty for logging into the guest.
+ This should get you an additional tty for logging into the guest.
+
+* If you want to use the 3270 device as the Linux kernel console instead of
+ an additional tty, you can also append ``conmode=3270 condev=000a`` to
+ the guest's kernel command line. The kernel then should use the 3270 as
+ console after the next boot.
+
+Restrictions
+------------
+
+3270 support is very basic. In particular:
+
+* Only one 3270 device is supported.
+
+* It has only been tested with Linux guests and the x3270 emulator.
+
+* TLS/SSL is not supported.
+
+* Resizing on reattach is not supported.
+
+* Multiple commands in one inbound buffer (for example, when the reset key
+ is pressed while the network is slow) are not supported.
[root@host ~]# driverctl -b css set-override 0.0.0313 vfio_ccw
[root@host ~]# mdevctl define -u 7e270a25-e163-4922-af60-757fc8ed48c6 \
- -p 0.0.0313 -t vfio-ccw_io -a
+ -p 0.0.0313 -t vfio_ccw-io -a
If using ``mdevctl`` is not possible or wanted, follow the manual procedure
below.
--- /dev/null
+.. _AVR-System-emulator:
+
+AVR System emulator
+-------------------
+
+Use the executable ``qemu-system-avr`` to emulate a AVR 8 bit based machine.
+These can have one of the following cores: avr1, avr2, avr25, avr3, avr31,
+avr35, avr4, avr5, avr51, avr6, avrtiny, xmega2, xmega3, xmega4, xmega5,
+xmega6 and xmega7.
+
+As for now it supports few Arduino boards for educational and testing purposes.
+These boards use a ATmega controller, which model is limited to USART & 16-bit
+timer devices, enought to run FreeRTOS based applications (like
+https://github.com/seharris/qemu-avr-tests/blob/master/free-rtos/Demo/AVR_ATMega2560_GCC/demo.elf
+).
+
+Following are examples of possible usages, assuming demo.elf is compiled for
+AVR cpu
+
+ - Continuous non interrupted execution:
+ ``qemu-system-avr -machine mega2560 -bios demo.elf``
+
+ - Continuous non interrupted execution with serial output into telnet window:
+ ``qemu-system-avr -machine mega2560 -bios demo.elf -serial
+ tcp::5678,server,nowait -nographic``
+ and then in another shell
+ ``telnet localhost 5678``
+
+ - Debugging wit GDB debugger:
+ ``qemu-system-avr -machine mega2560 -bios demo.elf -s -S``
+ and then in another shell
+ ``avr-gdb demo.elf``
+ and then within GDB shell
+ ``target remote :1234``
+
+ - Print out executed instructions:
+ ``qemu-system-avr -machine mega2560 -bios demo.elf -d in_asm``
target-xtensa
target-s390x
target-rx
+ target-avr
Amends the image format specific *OPTIONS* for the image file
*FILENAME*. Not all file formats support this operation.
+ The set of options that can be amended are dependent on the image
+ format, but note that amending the backing chain relationship should
+ instead be performed with ``qemu-img rebase``.
+
--force allows some unsafe operations. Currently for -f luks, it allows to
erase the last encryption key, and to overwrite an active encryption key.
static MemoryRegion io_mem_unassigned;
#endif
-CPUTailQ cpus = QTAILQ_HEAD_INITIALIZER(cpus);
-
-/* current CPU in the current thread. It is only valid inside
- cpu_exec() */
-__thread CPUState *current_cpu;
-
uintptr_t qemu_host_page_size;
intptr_t qemu_host_page_mask;
}
};
-#endif
-
-CPUState *qemu_get_cpu(int index)
-{
- CPUState *cpu;
-
- CPU_FOREACH(cpu) {
- if (cpu->cpu_index == index) {
- return cpu;
- }
- }
-
- return NULL;
-}
-
-#if !defined(CONFIG_USER_ONLY)
void cpu_address_space_init(CPUState *cpu, int asidx,
const char *prefix, MemoryRegion *mr)
{
if (snan_bit_is_one(status)) {
return (((a >> 9) & 0x3F) == 0x3E) && (a & 0x1FF);
} else {
- return ((a & ~0x8000) >= 0x7C80);
+ return ((a >> 9) & 0x3F) == 0x3F;
}
#endif
}
#else
uint16_t a = float16_val(a_);
if (snan_bit_is_one(status)) {
- return ((a & ~0x8000) >= 0x7C80);
+ return ((a >> 9) & 0x3F) == 0x3F;
} else {
return (((a >> 9) & 0x3F) == 0x3E) && (a & 0x1FF);
}
"throttling.iops-read-max-length",
"throttling.iops-write-max-length",
"throttling.iops-size",
+ NULL
},
},
{
.ops = &synth_ops,
.opts = (const char * []) {
COMMON_FS_DRIVER_OPTIONS,
+ NULL
},
},
{
"socket",
"sock_fd",
"writeout",
+ NULL
},
},
};
--- /dev/null
+<?xml version="1.0"?>
+<!-- Copyright (C) 2018-2019 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!-- Register numbers are hard-coded in order to maintain backward
+ compatibility with older versions of tools that didn't use xml
+ register descriptions. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.riscv.cpu">
+ <reg name="r0" bitsize="8" type="int" regnum="0"/>
+ <reg name="r1" bitsize="8" type="int"/>
+ <reg name="r2" bitsize="8" type="int"/>
+ <reg name="r3" bitsize="8" type="int"/>
+ <reg name="r4" bitsize="8" type="int"/>
+ <reg name="r5" bitsize="8" type="int"/>
+ <reg name="r6" bitsize="8" type="int"/>
+ <reg name="r7" bitsize="8" type="int"/>
+ <reg name="r8" bitsize="8" type="int"/>
+ <reg name="r9" bitsize="8" type="int"/>
+ <reg name="r10" bitsize="8" type="int"/>
+ <reg name="r11" bitsize="8" type="int"/>
+ <reg name="r12" bitsize="8" type="int"/>
+ <reg name="r13" bitsize="8" type="int"/>
+ <reg name="r14" bitsize="8" type="int"/>
+ <reg name="r15" bitsize="8" type="int"/>
+ <reg name="r16" bitsize="8" type="int"/>
+ <reg name="r17" bitsize="8" type="int"/>
+ <reg name="r18" bitsize="8" type="int"/>
+ <reg name="r19" bitsize="8" type="int"/>
+ <reg name="r20" bitsize="8" type="int"/>
+ <reg name="r21" bitsize="8" type="int"/>
+ <reg name="r22" bitsize="8" type="int"/>
+ <reg name="r23" bitsize="8" type="int"/>
+ <reg name="r24" bitsize="8" type="int"/>
+ <reg name="r25" bitsize="8" type="int"/>
+ <reg name="r26" bitsize="8" type="int"/>
+ <reg name="r27" bitsize="8" type="int"/>
+ <reg name="r28" bitsize="8" type="int"/>
+ <reg name="r29" bitsize="8" type="int"/>
+ <reg name="r30" bitsize="8" type="int"/>
+ <reg name="r31" bitsize="8" type="int"/>
+ <reg name="sreg" bitsize="8" type="int"/>
+ <reg name="sp" bitsize="8" type="int"/>
+ <reg name="pc" bitsize="8" type="int"/>
+</feature>
# arch Kconfig
source arm/Kconfig
source alpha/Kconfig
+source avr/Kconfig
source cris/Kconfig
source hppa/Kconfig
source i386/Kconfig
{
DeviceState *card;
- card = qdev_new(TYPE_SD_CARD);
- if (dinfo) {
- qdev_prop_set_drive_err(card, "drive", blk_by_legacy_dinfo(dinfo),
- &error_fatal);
+ if (!dinfo) {
+ return;
}
+ card = qdev_new(TYPE_SD_CARD);
+ qdev_prop_set_drive_err(card, "drive", blk_by_legacy_dinfo(dinfo),
+ &error_fatal);
qdev_realize_and_unref(card,
qdev_get_child_bus(DEVICE(sdhci), "sd-bus"),
&error_fatal);
epit_table[i].irq));
}
+ object_property_set_uint(OBJECT(&s->fec), "phy-num", s->phy_num, &err);
qdev_set_nic_properties(DEVICE(&s->fec), &nd_table[0]);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->fec), errp)) {
&s->iram_alias);
}
+static Property fsl_imx25_properties[] = {
+ DEFINE_PROP_UINT32("fec-phy-num", FslIMX25State, phy_num, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void fsl_imx25_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
+ device_class_set_props(dc, fsl_imx25_properties);
dc->realize = fsl_imx25_realize;
dc->desc = "i.MX25 SOC";
/*
spi_table[i].irq));
}
+ object_property_set_uint(OBJECT(&s->eth), "phy-num", s->phy_num, &err);
qdev_set_nic_properties(DEVICE(&s->eth), &nd_table[0]);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->eth), errp)) {
return;
&s->ocram_alias);
}
+static Property fsl_imx6_properties[] = {
+ DEFINE_PROP_UINT32("fec-phy-num", FslIMX6State, phy_num, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void fsl_imx6_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
+ device_class_set_props(dc, fsl_imx6_properties);
dc->realize = fsl_imx6_realize;
dc->desc = "i.MX6 SOC";
/* Reason: Uses serial_hd() in the realize() function */
FSL_IMX7_ENET2_ADDR,
};
+ object_property_set_uint(OBJECT(&s->eth[i]), "phy-num",
+ s->phy_num[i], &error_abort);
object_property_set_uint(OBJECT(&s->eth[i]), "tx-ring-num",
FSL_IMX7_ETH_NUM_TX_RINGS, &error_abort);
qdev_set_nic_properties(DEVICE(&s->eth[i]), &nd_table[i]);
FSL_IMX7_PCIE_PHY_SIZE);
}
+static Property fsl_imx7_properties[] = {
+ DEFINE_PROP_UINT32("fec1-phy-num", FslIMX7State, phy_num[0], 0),
+ DEFINE_PROP_UINT32("fec2-phy-num", FslIMX7State, phy_num[1], 1),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void fsl_imx7_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
+ device_class_set_props(dc, fsl_imx7_properties);
dc->realize = fsl_imx7_realize;
/* Reason: Uses serial_hds and nd_table in realize() directly */
/* Palm Tunsgten|E support */
/* Shared GPIOs */
-#define PALMTE_USBDETECT_GPIO 0
-#define PALMTE_USB_OR_DC_GPIO 1
-#define PALMTE_TSC_GPIO 4
-#define PALMTE_PINTDAV_GPIO 6
-#define PALMTE_MMC_WP_GPIO 8
-#define PALMTE_MMC_POWER_GPIO 9
-#define PALMTE_HDQ_GPIO 11
-#define PALMTE_HEADPHONES_GPIO 14
-#define PALMTE_SPEAKER_GPIO 15
+#define PALMTE_USBDETECT_GPIO 0
+#define PALMTE_USB_OR_DC_GPIO 1
+#define PALMTE_TSC_GPIO 4
+#define PALMTE_PINTDAV_GPIO 6
+#define PALMTE_MMC_WP_GPIO 8
+#define PALMTE_MMC_POWER_GPIO 9
+#define PALMTE_HDQ_GPIO 11
+#define PALMTE_HEADPHONES_GPIO 14
+#define PALMTE_SPEAKER_GPIO 15
/* MPU private GPIOs */
-#define PALMTE_DC_GPIO 2
-#define PALMTE_MMC_SWITCH_GPIO 4
-#define PALMTE_MMC1_GPIO 6
-#define PALMTE_MMC2_GPIO 7
-#define PALMTE_MMC3_GPIO 11
+#define PALMTE_DC_GPIO 2
+#define PALMTE_MMC_SWITCH_GPIO 4
+#define PALMTE_MMC1_GPIO 6
+#define PALMTE_MMC2_GPIO 7
+#define PALMTE_MMC3_GPIO 11
static MouseTransformInfo palmte_pointercal = {
.x = 320,
int column;
} palmte_keymap[0x80] = {
[0 ... 0x7f] = { -1, -1 },
- [0x3b] = { 0, 0 }, /* F1 -> Calendar */
- [0x3c] = { 1, 0 }, /* F2 -> Contacts */
- [0x3d] = { 2, 0 }, /* F3 -> Tasks List */
- [0x3e] = { 3, 0 }, /* F4 -> Note Pad */
- [0x01] = { 4, 0 }, /* Esc -> Power */
- [0x4b] = { 0, 1 }, /* Left */
- [0x50] = { 1, 1 }, /* Down */
- [0x48] = { 2, 1 }, /* Up */
- [0x4d] = { 3, 1 }, /* Right */
- [0x4c] = { 4, 1 }, /* Centre */
- [0x39] = { 4, 1 }, /* Spc -> Centre */
+ [0x3b] = { 0, 0 }, /* F1 -> Calendar */
+ [0x3c] = { 1, 0 }, /* F2 -> Contacts */
+ [0x3d] = { 2, 0 }, /* F3 -> Tasks List */
+ [0x3e] = { 3, 0 }, /* F4 -> Note Pad */
+ [0x01] = { 4, 0 }, /* Esc -> Power */
+ [0x4b] = { 0, 1 }, /* Left */
+ [0x50] = { 1, 1 }, /* Down */
+ [0x48] = { 2, 1 }, /* Up */
+ [0x4d] = { 3, 1 }, /* Right */
+ [0x4c] = { 4, 1 }, /* Centre */
+ [0x39] = { 4, 1 }, /* Spc -> Centre */
};
static void palmte_button_event(void *opaque, int keycode)
!(keycode & 0x80));
}
+/*
+ * Encapsulation of some GPIO line behaviour for the Palm board
+ *
+ * QEMU interface:
+ * + unnamed GPIO inputs 0..6: for the various miscellaneous input lines
+ */
+
+#define TYPE_PALM_MISC_GPIO "palm-misc-gpio"
+#define PALM_MISC_GPIO(obj) \
+ OBJECT_CHECK(PalmMiscGPIOState, (obj), TYPE_PALM_MISC_GPIO)
+
+typedef struct PalmMiscGPIOState {
+ SysBusDevice parent_obj;
+} PalmMiscGPIOState;
+
static void palmte_onoff_gpios(void *opaque, int line, int level)
{
switch (line) {
}
}
+static void palm_misc_gpio_init(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+
+ qdev_init_gpio_in(dev, palmte_onoff_gpios, 7);
+}
+
+static const TypeInfo palm_misc_gpio_info = {
+ .name = TYPE_PALM_MISC_GPIO,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(PalmMiscGPIOState),
+ .instance_init = palm_misc_gpio_init,
+ /*
+ * No class init required: device has no internal state so does not
+ * need to set up reset or vmstate, and has no realize method.
+ */
+};
+
static void palmte_gpio_setup(struct omap_mpu_state_s *cpu)
{
- qemu_irq *misc_gpio;
+ DeviceState *misc_gpio;
+
+ misc_gpio = sysbus_create_simple(TYPE_PALM_MISC_GPIO, -1, NULL);
omap_mmc_handlers(cpu->mmc,
qdev_get_gpio_in(cpu->gpio, PALMTE_MMC_WP_GPIO),
qemu_irq_invert(omap_mpuio_in_get(cpu->mpuio)
[PALMTE_MMC_SWITCH_GPIO]));
- misc_gpio = qemu_allocate_irqs(palmte_onoff_gpios, cpu, 7);
- qdev_connect_gpio_out(cpu->gpio, PALMTE_MMC_POWER_GPIO, misc_gpio[0]);
- qdev_connect_gpio_out(cpu->gpio, PALMTE_SPEAKER_GPIO, misc_gpio[1]);
- qdev_connect_gpio_out(cpu->gpio, 11, misc_gpio[2]);
- qdev_connect_gpio_out(cpu->gpio, 12, misc_gpio[3]);
- qdev_connect_gpio_out(cpu->gpio, 13, misc_gpio[4]);
- omap_mpuio_out_set(cpu->mpuio, 1, misc_gpio[5]);
- omap_mpuio_out_set(cpu->mpuio, 3, misc_gpio[6]);
+ qdev_connect_gpio_out(cpu->gpio, PALMTE_MMC_POWER_GPIO,
+ qdev_get_gpio_in(misc_gpio, 0));
+ qdev_connect_gpio_out(cpu->gpio, PALMTE_SPEAKER_GPIO,
+ qdev_get_gpio_in(misc_gpio, 1));
+ qdev_connect_gpio_out(cpu->gpio, 11, qdev_get_gpio_in(misc_gpio, 2));
+ qdev_connect_gpio_out(cpu->gpio, 12, qdev_get_gpio_in(misc_gpio, 3));
+ qdev_connect_gpio_out(cpu->gpio, 13, qdev_get_gpio_in(misc_gpio, 4));
+ omap_mpuio_out_set(cpu->mpuio, 1, qdev_get_gpio_in(misc_gpio, 5));
+ omap_mpuio_out_set(cpu->mpuio, 3, qdev_get_gpio_in(misc_gpio, 6));
/* Reset some inputs to initial state. */
qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, PALMTE_USBDETECT_GPIO));
}
DEFINE_MACHINE("cheetah", palmte_machine_init)
+
+static void palm_register_types(void)
+{
+ type_register_static(&palm_misc_gpio_info);
+}
+
+type_init(palm_register_types)
#include "hw/sysbus.h"
#include "exec/address-spaces.h"
-#define TOSA_RAM 0x04000000
-#define TOSA_ROM 0x00800000
-
-#define TOSA_GPIO_USB_IN (5)
-#define TOSA_GPIO_nSD_DETECT (9)
-#define TOSA_GPIO_ON_RESET (19)
-#define TOSA_GPIO_CF_IRQ (21) /* CF slot0 Ready */
-#define TOSA_GPIO_CF_CD (13)
-#define TOSA_GPIO_TC6393XB_INT (15)
-#define TOSA_GPIO_JC_CF_IRQ (36) /* CF slot1 Ready */
-
-#define TOSA_SCOOP_GPIO_BASE 1
-#define TOSA_GPIO_IR_POWERDWN (TOSA_SCOOP_GPIO_BASE + 2)
-#define TOSA_GPIO_SD_WP (TOSA_SCOOP_GPIO_BASE + 3)
-#define TOSA_GPIO_PWR_ON (TOSA_SCOOP_GPIO_BASE + 4)
-
-#define TOSA_SCOOP_JC_GPIO_BASE 1
-#define TOSA_GPIO_BT_LED (TOSA_SCOOP_JC_GPIO_BASE + 0)
-#define TOSA_GPIO_NOTE_LED (TOSA_SCOOP_JC_GPIO_BASE + 1)
-#define TOSA_GPIO_CHRG_ERR_LED (TOSA_SCOOP_JC_GPIO_BASE + 2)
-#define TOSA_GPIO_TC6393XB_L3V_ON (TOSA_SCOOP_JC_GPIO_BASE + 5)
-#define TOSA_GPIO_WLAN_LED (TOSA_SCOOP_JC_GPIO_BASE + 7)
-
-#define DAC_BASE 0x4e
-#define DAC_CH1 0
-#define DAC_CH2 1
+#define TOSA_RAM 0x04000000
+#define TOSA_ROM 0x00800000
+
+#define TOSA_GPIO_USB_IN (5)
+#define TOSA_GPIO_nSD_DETECT (9)
+#define TOSA_GPIO_ON_RESET (19)
+#define TOSA_GPIO_CF_IRQ (21) /* CF slot0 Ready */
+#define TOSA_GPIO_CF_CD (13)
+#define TOSA_GPIO_TC6393XB_INT (15)
+#define TOSA_GPIO_JC_CF_IRQ (36) /* CF slot1 Ready */
+
+#define TOSA_SCOOP_GPIO_BASE 1
+#define TOSA_GPIO_IR_POWERDWN (TOSA_SCOOP_GPIO_BASE + 2)
+#define TOSA_GPIO_SD_WP (TOSA_SCOOP_GPIO_BASE + 3)
+#define TOSA_GPIO_PWR_ON (TOSA_SCOOP_GPIO_BASE + 4)
+
+#define TOSA_SCOOP_JC_GPIO_BASE 1
+#define TOSA_GPIO_BT_LED (TOSA_SCOOP_JC_GPIO_BASE + 0)
+#define TOSA_GPIO_NOTE_LED (TOSA_SCOOP_JC_GPIO_BASE + 1)
+#define TOSA_GPIO_CHRG_ERR_LED (TOSA_SCOOP_JC_GPIO_BASE + 2)
+#define TOSA_GPIO_TC6393XB_L3V_ON (TOSA_SCOOP_JC_GPIO_BASE + 5)
+#define TOSA_GPIO_WLAN_LED (TOSA_SCOOP_JC_GPIO_BASE + 7)
+
+#define DAC_BASE 0x4e
+#define DAC_CH1 0
+#define DAC_CH2 1
static void tosa_microdrive_attach(PXA2xxState *cpu)
{
pxa2xx_pcmcia_attach(cpu->pcmcia[0], md);
}
-static void tosa_out_switch(void *opaque, int line, int level)
+/*
+ * Encapsulation of some GPIO line behaviour for the Tosa board
+ *
+ * QEMU interface:
+ * + named GPIO inputs "leds[0..3]": assert to light LEDs
+ * + named GPIO input "reset": when asserted, resets the system
+ */
+
+#define TYPE_TOSA_MISC_GPIO "tosa-misc-gpio"
+#define TOSA_MISC_GPIO(obj) \
+ OBJECT_CHECK(TosaMiscGPIOState, (obj), TYPE_TOSA_MISC_GPIO)
+
+typedef struct TosaMiscGPIOState {
+ SysBusDevice parent_obj;
+} TosaMiscGPIOState;
+
+static void tosa_gpio_leds(void *opaque, int line, int level)
{
switch (line) {
- case 0:
- fprintf(stderr, "blue LED %s.\n", level ? "on" : "off");
- break;
- case 1:
- fprintf(stderr, "green LED %s.\n", level ? "on" : "off");
- break;
- case 2:
- fprintf(stderr, "amber LED %s.\n", level ? "on" : "off");
- break;
- case 3:
- fprintf(stderr, "wlan LED %s.\n", level ? "on" : "off");
- break;
- default:
- fprintf(stderr, "Uhandled out event: %d = %d\n", line, level);
- break;
+ case 0:
+ fprintf(stderr, "blue LED %s.\n", level ? "on" : "off");
+ break;
+ case 1:
+ fprintf(stderr, "green LED %s.\n", level ? "on" : "off");
+ break;
+ case 2:
+ fprintf(stderr, "amber LED %s.\n", level ? "on" : "off");
+ break;
+ case 3:
+ fprintf(stderr, "wlan LED %s.\n", level ? "on" : "off");
+ break;
+ default:
+ g_assert_not_reached();
}
}
}
}
+static void tosa_misc_gpio_init(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+
+ qdev_init_gpio_in_named(dev, tosa_gpio_leds, "leds", 4);
+ qdev_init_gpio_in_named(dev, tosa_reset, "reset", 1);
+}
+
static void tosa_gpio_setup(PXA2xxState *cpu,
DeviceState *scp0,
DeviceState *scp1,
TC6393xbState *tmio)
{
- qemu_irq *outsignals = qemu_allocate_irqs(tosa_out_switch, cpu, 4);
- qemu_irq reset;
+ DeviceState *misc_gpio;
+
+ misc_gpio = sysbus_create_simple(TYPE_TOSA_MISC_GPIO, -1, NULL);
/* MMC/SD host */
pxa2xx_mmci_handlers(cpu->mmc,
qemu_irq_invert(qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_nSD_DETECT)));
/* Handle reset */
- reset = qemu_allocate_irq(tosa_reset, cpu, 0);
- qdev_connect_gpio_out(cpu->gpio, TOSA_GPIO_ON_RESET, reset);
+ qdev_connect_gpio_out(cpu->gpio, TOSA_GPIO_ON_RESET,
+ qdev_get_gpio_in_named(misc_gpio, "reset", 0));
/* PCMCIA signals: card's IRQ and Card-Detect */
pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[0],
qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_JC_CF_IRQ),
NULL);
- qdev_connect_gpio_out(scp1, TOSA_GPIO_BT_LED, outsignals[0]);
- qdev_connect_gpio_out(scp1, TOSA_GPIO_NOTE_LED, outsignals[1]);
- qdev_connect_gpio_out(scp1, TOSA_GPIO_CHRG_ERR_LED, outsignals[2]);
- qdev_connect_gpio_out(scp1, TOSA_GPIO_WLAN_LED, outsignals[3]);
+ qdev_connect_gpio_out(scp1, TOSA_GPIO_BT_LED,
+ qdev_get_gpio_in_named(misc_gpio, "leds", 0));
+ qdev_connect_gpio_out(scp1, TOSA_GPIO_NOTE_LED,
+ qdev_get_gpio_in_named(misc_gpio, "leds", 1));
+ qdev_connect_gpio_out(scp1, TOSA_GPIO_CHRG_ERR_LED,
+ qdev_get_gpio_in_named(misc_gpio, "leds", 2));
+ qdev_connect_gpio_out(scp1, TOSA_GPIO_WLAN_LED,
+ qdev_get_gpio_in_named(misc_gpio, "leds", 3));
qdev_connect_gpio_out(scp1, TOSA_GPIO_TC6393XB_L3V_ON, tc6393xb_l3v_get(tmio));
.class_init = tosa_ssp_class_init,
};
+static const TypeInfo tosa_misc_gpio_info = {
+ .name = "tosa-misc-gpio",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(TosaMiscGPIOState),
+ .instance_init = tosa_misc_gpio_init,
+ /*
+ * No class init required: device has no internal state so does not
+ * need to set up reset or vmstate, and has no realize method.
+ */
+};
+
static void tosa_register_types(void)
{
type_register_static(&tosa_dac_info);
type_register_static(&tosa_ssp_info);
+ type_register_static(&tosa_misc_gpio_info);
}
type_init(tosa_register_types)
--- /dev/null
+config AVR_ATMEGA_MCU
+ bool
+ select AVR_TIMER16
+ select AVR_USART
+ select AVR_POWER
+
+config ARDUINO
+ select AVR_ATMEGA_MCU
+ select UNIMP
--- /dev/null
+obj-y += boot.o
+obj-$(CONFIG_AVR_ATMEGA_MCU) += atmega.o
+obj-$(CONFIG_ARDUINO) += arduino.o
--- /dev/null
+/*
+ * QEMU Arduino boards
+ *
+ * Copyright (c) 2019-2020 Philippe Mathieu-Daudé
+ *
+ * This work is licensed under the terms of the GNU GPLv2 or later.
+ * See the COPYING file in the top-level directory.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* TODO: Implement the use of EXTRAM */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/boards.h"
+#include "atmega.h"
+#include "boot.h"
+
+typedef struct ArduinoMachineState {
+ /*< private >*/
+ MachineState parent_obj;
+ /*< public >*/
+ AtmegaMcuState mcu;
+} ArduinoMachineState;
+
+typedef struct ArduinoMachineClass {
+ /*< private >*/
+ MachineClass parent_class;
+ /*< public >*/
+ const char *mcu_type;
+ uint64_t xtal_hz;
+} ArduinoMachineClass;
+
+#define TYPE_ARDUINO_MACHINE \
+ MACHINE_TYPE_NAME("arduino")
+#define ARDUINO_MACHINE(obj) \
+ OBJECT_CHECK(ArduinoMachineState, (obj), TYPE_ARDUINO_MACHINE)
+#define ARDUINO_MACHINE_CLASS(klass) \
+ OBJECT_CLASS_CHECK(ArduinoMachineClass, (klass), TYPE_ARDUINO_MACHINE)
+#define ARDUINO_MACHINE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(ArduinoMachineClass, (obj), TYPE_ARDUINO_MACHINE)
+
+static void arduino_machine_init(MachineState *machine)
+{
+ ArduinoMachineClass *amc = ARDUINO_MACHINE_GET_CLASS(machine);
+ ArduinoMachineState *ams = ARDUINO_MACHINE(machine);
+
+ object_initialize_child(OBJECT(machine), "mcu", &ams->mcu, amc->mcu_type);
+ object_property_set_uint(OBJECT(&ams->mcu), "xtal-frequency-hz",
+ amc->xtal_hz, &error_abort);
+ sysbus_realize(SYS_BUS_DEVICE(&ams->mcu), &error_abort);
+
+ if (machine->firmware) {
+ if (!avr_load_firmware(&ams->mcu.cpu, machine,
+ &ams->mcu.flash, machine->firmware)) {
+ exit(1);
+ }
+ }
+}
+
+static void arduino_machine_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->init = arduino_machine_init;
+ mc->default_cpus = 1;
+ mc->min_cpus = mc->default_cpus;
+ mc->max_cpus = mc->default_cpus;
+ mc->no_floppy = 1;
+ mc->no_cdrom = 1;
+ mc->no_parallel = 1;
+}
+
+static void arduino_duemilanove_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ ArduinoMachineClass *amc = ARDUINO_MACHINE_CLASS(oc);
+
+ /* https://www.arduino.cc/en/Main/ArduinoBoardDuemilanove */
+ mc->desc = "Arduino Duemilanove (ATmega168)",
+ mc->alias = "2009";
+ amc->mcu_type = TYPE_ATMEGA168_MCU;
+ amc->xtal_hz = 16 * 1000 * 1000;
+};
+
+static void arduino_uno_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ ArduinoMachineClass *amc = ARDUINO_MACHINE_CLASS(oc);
+
+ /* https://store.arduino.cc/arduino-uno-rev3 */
+ mc->desc = "Arduino UNO (ATmega328P)";
+ mc->alias = "uno";
+ amc->mcu_type = TYPE_ATMEGA328_MCU;
+ amc->xtal_hz = 16 * 1000 * 1000;
+};
+
+static void arduino_mega_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ ArduinoMachineClass *amc = ARDUINO_MACHINE_CLASS(oc);
+
+ /* https://www.arduino.cc/en/Main/ArduinoBoardMega */
+ mc->desc = "Arduino Mega (ATmega1280)";
+ mc->alias = "mega";
+ amc->mcu_type = TYPE_ATMEGA1280_MCU;
+ amc->xtal_hz = 16 * 1000 * 1000;
+};
+
+static void arduino_mega2560_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ ArduinoMachineClass *amc = ARDUINO_MACHINE_CLASS(oc);
+
+ /* https://store.arduino.cc/arduino-mega-2560-rev3 */
+ mc->desc = "Arduino Mega 2560 (ATmega2560)";
+ mc->alias = "mega2560";
+ amc->mcu_type = TYPE_ATMEGA2560_MCU;
+ amc->xtal_hz = 16 * 1000 * 1000; /* CSTCE16M0V53-R0 */
+};
+
+static const TypeInfo arduino_machine_types[] = {
+ {
+ .name = MACHINE_TYPE_NAME("arduino-duemilanove"),
+ .parent = TYPE_ARDUINO_MACHINE,
+ .class_init = arduino_duemilanove_class_init,
+ }, {
+ .name = MACHINE_TYPE_NAME("arduino-uno"),
+ .parent = TYPE_ARDUINO_MACHINE,
+ .class_init = arduino_uno_class_init,
+ }, {
+ .name = MACHINE_TYPE_NAME("arduino-mega"),
+ .parent = TYPE_ARDUINO_MACHINE,
+ .class_init = arduino_mega_class_init,
+ }, {
+ .name = MACHINE_TYPE_NAME("arduino-mega-2560-v3"),
+ .parent = TYPE_ARDUINO_MACHINE,
+ .class_init = arduino_mega2560_class_init,
+ }, {
+ .name = TYPE_ARDUINO_MACHINE,
+ .parent = TYPE_MACHINE,
+ .instance_size = sizeof(ArduinoMachineState),
+ .class_size = sizeof(ArduinoMachineClass),
+ .class_init = arduino_machine_class_init,
+ .abstract = true,
+ }
+};
+
+DEFINE_TYPES(arduino_machine_types)
--- /dev/null
+/*
+ * QEMU ATmega MCU
+ *
+ * Copyright (c) 2019-2020 Philippe Mathieu-Daudé
+ *
+ * This work is licensed under the terms of the GNU GPLv2 or later.
+ * See the COPYING file in the top-level directory.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/module.h"
+#include "qemu/units.h"
+#include "qapi/error.h"
+#include "exec/memory.h"
+#include "exec/address-spaces.h"
+#include "sysemu/sysemu.h"
+#include "hw/qdev-properties.h"
+#include "hw/sysbus.h"
+#include "hw/boards.h" /* FIXME memory_region_allocate_system_memory for sram */
+#include "hw/misc/unimp.h"
+#include "atmega.h"
+
+enum AtmegaPeripheral {
+ POWER0, POWER1,
+ GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, GPIOF,
+ GPIOG, GPIOH, GPIOI, GPIOJ, GPIOK, GPIOL,
+ USART0, USART1, USART2, USART3,
+ TIMER0, TIMER1, TIMER2, TIMER3, TIMER4, TIMER5,
+ PERIFMAX
+};
+
+#define GPIO(n) (n + GPIOA)
+#define USART(n) (n + USART0)
+#define TIMER(n) (n + TIMER0)
+#define POWER(n) (n + POWER0)
+
+typedef struct {
+ uint16_t addr;
+ enum AtmegaPeripheral power_index;
+ uint8_t power_bit;
+ /* timer specific */
+ uint16_t intmask_addr;
+ uint16_t intflag_addr;
+ bool is_timer16;
+} peripheral_cfg;
+
+typedef struct AtmegaMcuClass {
+ /*< private >*/
+ SysBusDeviceClass parent_class;
+ /*< public >*/
+ const char *uc_name;
+ const char *cpu_type;
+ size_t flash_size;
+ size_t eeprom_size;
+ size_t sram_size;
+ size_t io_size;
+ size_t gpio_count;
+ size_t adc_count;
+ const uint8_t *irq;
+ const peripheral_cfg *dev;
+} AtmegaMcuClass;
+
+#define ATMEGA_MCU_CLASS(klass) \
+ OBJECT_CLASS_CHECK(AtmegaMcuClass, (klass), TYPE_ATMEGA_MCU)
+#define ATMEGA_MCU_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(AtmegaMcuClass, (obj), TYPE_ATMEGA_MCU)
+
+static const peripheral_cfg dev168_328[PERIFMAX] = {
+ [USART0] = { 0xc0, POWER0, 1 },
+ [TIMER2] = { 0xb0, POWER0, 6, 0x70, 0x37, false },
+ [TIMER1] = { 0x80, POWER0, 3, 0x6f, 0x36, true },
+ [POWER0] = { 0x64 },
+ [TIMER0] = { 0x44, POWER0, 5, 0x6e, 0x35, false },
+ [GPIOD] = { 0x29 },
+ [GPIOC] = { 0x26 },
+ [GPIOB] = { 0x23 },
+}, dev1280_2560[PERIFMAX] = {
+ [USART3] = { 0x130, POWER1, 2 },
+ [TIMER5] = { 0x120, POWER1, 5, 0x73, 0x3a, true },
+ [GPIOL] = { 0x109 },
+ [GPIOK] = { 0x106 },
+ [GPIOJ] = { 0x103 },
+ [GPIOH] = { 0x100 },
+ [USART2] = { 0xd0, POWER1, 1 },
+ [USART1] = { 0xc8, POWER1, 0 },
+ [USART0] = { 0xc0, POWER0, 1 },
+ [TIMER2] = { 0xb0, POWER0, 6, 0x70, 0x37, false }, /* TODO async */
+ [TIMER4] = { 0xa0, POWER1, 4, 0x72, 0x39, true },
+ [TIMER3] = { 0x90, POWER1, 3, 0x71, 0x38, true },
+ [TIMER1] = { 0x80, POWER0, 3, 0x6f, 0x36, true },
+ [POWER1] = { 0x65 },
+ [POWER0] = { 0x64 },
+ [TIMER0] = { 0x44, POWER0, 5, 0x6e, 0x35, false },
+ [GPIOG] = { 0x32 },
+ [GPIOF] = { 0x2f },
+ [GPIOE] = { 0x2c },
+ [GPIOD] = { 0x29 },
+ [GPIOC] = { 0x26 },
+ [GPIOB] = { 0x23 },
+ [GPIOA] = { 0x20 },
+};
+
+enum AtmegaIrq {
+ USART0_RXC_IRQ, USART0_DRE_IRQ, USART0_TXC_IRQ,
+ USART1_RXC_IRQ, USART1_DRE_IRQ, USART1_TXC_IRQ,
+ USART2_RXC_IRQ, USART2_DRE_IRQ, USART2_TXC_IRQ,
+ USART3_RXC_IRQ, USART3_DRE_IRQ, USART3_TXC_IRQ,
+ TIMER0_CAPT_IRQ, TIMER0_COMPA_IRQ, TIMER0_COMPB_IRQ,
+ TIMER0_COMPC_IRQ, TIMER0_OVF_IRQ,
+ TIMER1_CAPT_IRQ, TIMER1_COMPA_IRQ, TIMER1_COMPB_IRQ,
+ TIMER1_COMPC_IRQ, TIMER1_OVF_IRQ,
+ TIMER2_CAPT_IRQ, TIMER2_COMPA_IRQ, TIMER2_COMPB_IRQ,
+ TIMER2_COMPC_IRQ, TIMER2_OVF_IRQ,
+ TIMER3_CAPT_IRQ, TIMER3_COMPA_IRQ, TIMER3_COMPB_IRQ,
+ TIMER3_COMPC_IRQ, TIMER3_OVF_IRQ,
+ TIMER4_CAPT_IRQ, TIMER4_COMPA_IRQ, TIMER4_COMPB_IRQ,
+ TIMER4_COMPC_IRQ, TIMER4_OVF_IRQ,
+ TIMER5_CAPT_IRQ, TIMER5_COMPA_IRQ, TIMER5_COMPB_IRQ,
+ TIMER5_COMPC_IRQ, TIMER5_OVF_IRQ,
+ IRQ_COUNT
+};
+
+#define USART_IRQ_COUNT 3
+#define USART_RXC_IRQ(n) (n * USART_IRQ_COUNT + USART0_RXC_IRQ)
+#define USART_DRE_IRQ(n) (n * USART_IRQ_COUNT + USART0_DRE_IRQ)
+#define USART_TXC_IRQ(n) (n * USART_IRQ_COUNT + USART0_TXC_IRQ)
+#define TIMER_IRQ_COUNT 5
+#define TIMER_CAPT_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_CAPT_IRQ)
+#define TIMER_COMPA_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_COMPA_IRQ)
+#define TIMER_COMPB_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_COMPB_IRQ)
+#define TIMER_COMPC_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_COMPC_IRQ)
+#define TIMER_OVF_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_OVF_IRQ)
+
+static const uint8_t irq168_328[IRQ_COUNT] = {
+ [TIMER2_COMPA_IRQ] = 8,
+ [TIMER2_COMPB_IRQ] = 9,
+ [TIMER2_OVF_IRQ] = 10,
+ [TIMER1_CAPT_IRQ] = 11,
+ [TIMER1_COMPA_IRQ] = 12,
+ [TIMER1_COMPB_IRQ] = 13,
+ [TIMER1_OVF_IRQ] = 14,
+ [TIMER0_COMPA_IRQ] = 15,
+ [TIMER0_COMPB_IRQ] = 16,
+ [TIMER0_OVF_IRQ] = 17,
+ [USART0_RXC_IRQ] = 19,
+ [USART0_DRE_IRQ] = 20,
+ [USART0_TXC_IRQ] = 21,
+}, irq1280_2560[IRQ_COUNT] = {
+ [TIMER2_COMPA_IRQ] = 14,
+ [TIMER2_COMPB_IRQ] = 15,
+ [TIMER2_OVF_IRQ] = 16,
+ [TIMER1_CAPT_IRQ] = 17,
+ [TIMER1_COMPA_IRQ] = 18,
+ [TIMER1_COMPB_IRQ] = 19,
+ [TIMER1_COMPC_IRQ] = 20,
+ [TIMER1_OVF_IRQ] = 21,
+ [TIMER0_COMPA_IRQ] = 22,
+ [TIMER0_COMPB_IRQ] = 23,
+ [TIMER0_OVF_IRQ] = 24,
+ [USART0_RXC_IRQ] = 26,
+ [USART0_DRE_IRQ] = 27,
+ [USART0_TXC_IRQ] = 28,
+ [TIMER3_CAPT_IRQ] = 32,
+ [TIMER3_COMPA_IRQ] = 33,
+ [TIMER3_COMPB_IRQ] = 34,
+ [TIMER3_COMPC_IRQ] = 35,
+ [TIMER3_OVF_IRQ] = 36,
+ [USART1_RXC_IRQ] = 37,
+ [USART1_DRE_IRQ] = 38,
+ [USART1_TXC_IRQ] = 39,
+ [TIMER4_CAPT_IRQ] = 42,
+ [TIMER4_COMPA_IRQ] = 43,
+ [TIMER4_COMPB_IRQ] = 44,
+ [TIMER4_COMPC_IRQ] = 45,
+ [TIMER4_OVF_IRQ] = 46,
+ [TIMER5_CAPT_IRQ] = 47,
+ [TIMER5_COMPA_IRQ] = 48,
+ [TIMER5_COMPB_IRQ] = 49,
+ [TIMER5_COMPC_IRQ] = 50,
+ [TIMER5_OVF_IRQ] = 51,
+ [USART2_RXC_IRQ] = 52,
+ [USART2_DRE_IRQ] = 53,
+ [USART2_TXC_IRQ] = 54,
+ [USART3_RXC_IRQ] = 55,
+ [USART3_DRE_IRQ] = 56,
+ [USART3_TXC_IRQ] = 57,
+};
+
+static void connect_peripheral_irq(const AtmegaMcuClass *k,
+ SysBusDevice *dev, int dev_irqn,
+ DeviceState *cpu,
+ unsigned peripheral_index)
+{
+ int cpu_irq = k->irq[peripheral_index];
+
+ if (!cpu_irq) {
+ return;
+ }
+ /* FIXME move that to avr_cpu_set_int() once 'sample' board is removed */
+ assert(cpu_irq >= 2);
+ cpu_irq -= 2;
+
+ sysbus_connect_irq(dev, dev_irqn, qdev_get_gpio_in(cpu, cpu_irq));
+}
+
+static void connect_power_reduction_gpio(AtmegaMcuState *s,
+ const AtmegaMcuClass *k,
+ DeviceState *cpu,
+ unsigned peripheral_index)
+{
+ unsigned power_index = k->dev[peripheral_index].power_index;
+ assert(k->dev[power_index].addr);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->pwr[power_index - POWER0]),
+ k->dev[peripheral_index].power_bit,
+ qdev_get_gpio_in(cpu, 0));
+}
+
+static void atmega_realize(DeviceState *dev, Error **errp)
+{
+ AtmegaMcuState *s = ATMEGA_MCU(dev);
+ const AtmegaMcuClass *mc = ATMEGA_MCU_GET_CLASS(dev);
+ DeviceState *cpudev;
+ SysBusDevice *sbd;
+ char *devname;
+ size_t i;
+
+ assert(mc->io_size <= 0x200);
+
+ if (!s->xtal_freq_hz) {
+ error_setg(errp, "\"xtal-frequency-hz\" property must be provided.");
+ return;
+ }
+
+ /* CPU */
+ object_initialize_child(OBJECT(dev), "cpu", &s->cpu, mc->cpu_type);
+ object_property_set_bool(OBJECT(&s->cpu), "realized", true, &error_abort);
+ cpudev = DEVICE(&s->cpu);
+
+ /* SRAM */
+ memory_region_init_ram(&s->sram, OBJECT(dev), "sram", mc->sram_size,
+ &error_abort);
+ memory_region_add_subregion(get_system_memory(),
+ OFFSET_DATA + mc->io_size, &s->sram);
+
+ /* Flash */
+ memory_region_init_rom(&s->flash, OBJECT(dev),
+ "flash", mc->flash_size, &error_fatal);
+ memory_region_add_subregion(get_system_memory(), OFFSET_CODE, &s->flash);
+
+ /*
+ * I/O
+ *
+ * 0x00 - 0x1f: Registers
+ * 0x20 - 0x5f: I/O memory
+ * 0x60 - 0xff: Extended I/O
+ */
+ s->io = qdev_new(TYPE_UNIMPLEMENTED_DEVICE);
+ qdev_prop_set_string(s->io, "name", "I/O");
+ qdev_prop_set_uint64(s->io, "size", mc->io_size);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(s->io), &error_fatal);
+ sysbus_mmio_map_overlap(SYS_BUS_DEVICE(s->io), 0, OFFSET_DATA, -1234);
+
+ /* Power Reduction */
+ for (i = 0; i < POWER_MAX; i++) {
+ int idx = POWER(i);
+ if (!mc->dev[idx].addr) {
+ continue;
+ }
+ devname = g_strdup_printf("power%zu", i);
+ object_initialize_child(OBJECT(dev), devname, &s->pwr[i],
+ TYPE_AVR_MASK);
+ sysbus_realize(SYS_BUS_DEVICE(&s->pwr[i]), &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->pwr[i]), 0,
+ OFFSET_DATA + mc->dev[idx].addr);
+ g_free(devname);
+ }
+
+ /* GPIO */
+ for (i = 0; i < GPIO_MAX; i++) {
+ int idx = GPIO(i);
+ if (!mc->dev[idx].addr) {
+ continue;
+ }
+ devname = g_strdup_printf("atmega-gpio-%c", 'a' + (char)i);
+ create_unimplemented_device(devname,
+ OFFSET_DATA + mc->dev[idx].addr, 3);
+ g_free(devname);
+ }
+
+ /* USART */
+ for (i = 0; i < USART_MAX; i++) {
+ int idx = USART(i);
+ if (!mc->dev[idx].addr) {
+ continue;
+ }
+ devname = g_strdup_printf("usart%zu", i);
+ object_initialize_child(OBJECT(dev), devname, &s->usart[i],
+ TYPE_AVR_USART);
+ qdev_prop_set_chr(DEVICE(&s->usart[i]), "chardev", serial_hd(i));
+ sbd = SYS_BUS_DEVICE(&s->usart[i]);
+ sysbus_realize(sbd, &error_abort);
+ sysbus_mmio_map(sbd, 0, OFFSET_DATA + mc->dev[USART(i)].addr);
+ connect_peripheral_irq(mc, sbd, 0, cpudev, USART_RXC_IRQ(i));
+ connect_peripheral_irq(mc, sbd, 1, cpudev, USART_DRE_IRQ(i));
+ connect_peripheral_irq(mc, sbd, 2, cpudev, USART_TXC_IRQ(i));
+ connect_power_reduction_gpio(s, mc, DEVICE(&s->usart[i]), idx);
+ g_free(devname);
+ }
+
+ /* Timer */
+ for (i = 0; i < TIMER_MAX; i++) {
+ int idx = TIMER(i);
+ if (!mc->dev[idx].addr) {
+ continue;
+ }
+ if (!mc->dev[idx].is_timer16) {
+ create_unimplemented_device("avr-timer8",
+ OFFSET_DATA + mc->dev[idx].addr, 5);
+ create_unimplemented_device("avr-timer8-intmask",
+ OFFSET_DATA
+ + mc->dev[idx].intmask_addr, 1);
+ create_unimplemented_device("avr-timer8-intflag",
+ OFFSET_DATA
+ + mc->dev[idx].intflag_addr, 1);
+ continue;
+ }
+ devname = g_strdup_printf("timer%zu", i);
+ object_initialize_child(OBJECT(dev), devname, &s->timer[i],
+ TYPE_AVR_TIMER16);
+ object_property_set_uint(OBJECT(&s->timer[i]), "cpu-frequency-hz",
+ s->xtal_freq_hz, &error_abort);
+ sbd = SYS_BUS_DEVICE(&s->timer[i]);
+ sysbus_realize(sbd, &error_abort);
+ sysbus_mmio_map(sbd, 0, OFFSET_DATA + mc->dev[idx].addr);
+ sysbus_mmio_map(sbd, 1, OFFSET_DATA + mc->dev[idx].intmask_addr);
+ sysbus_mmio_map(sbd, 2, OFFSET_DATA + mc->dev[idx].intflag_addr);
+ connect_peripheral_irq(mc, sbd, 0, cpudev, TIMER_CAPT_IRQ(i));
+ connect_peripheral_irq(mc, sbd, 1, cpudev, TIMER_COMPA_IRQ(i));
+ connect_peripheral_irq(mc, sbd, 2, cpudev, TIMER_COMPB_IRQ(i));
+ connect_peripheral_irq(mc, sbd, 3, cpudev, TIMER_COMPC_IRQ(i));
+ connect_peripheral_irq(mc, sbd, 4, cpudev, TIMER_OVF_IRQ(i));
+ connect_power_reduction_gpio(s, mc, DEVICE(&s->timer[i]), idx);
+ g_free(devname);
+ }
+
+ create_unimplemented_device("avr-twi", OFFSET_DATA + 0x0b8, 6);
+ create_unimplemented_device("avr-adc", OFFSET_DATA + 0x078, 8);
+ create_unimplemented_device("avr-ext-mem-ctrl", OFFSET_DATA + 0x074, 2);
+ create_unimplemented_device("avr-watchdog", OFFSET_DATA + 0x060, 1);
+ create_unimplemented_device("avr-spi", OFFSET_DATA + 0x04c, 3);
+ create_unimplemented_device("avr-eeprom", OFFSET_DATA + 0x03f, 3);
+}
+
+static Property atmega_props[] = {
+ DEFINE_PROP_UINT64("xtal-frequency-hz", AtmegaMcuState,
+ xtal_freq_hz, 0),
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static void atmega_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = atmega_realize;
+ device_class_set_props(dc, atmega_props);
+ /* Reason: Mapped at fixed location on the system bus */
+ dc->user_creatable = false;
+}
+
+static void atmega168_class_init(ObjectClass *oc, void *data)
+{
+ AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc);
+
+ amc->cpu_type = AVR_CPU_TYPE_NAME("avr5");
+ amc->flash_size = 16 * KiB;
+ amc->eeprom_size = 512;
+ amc->sram_size = 1 * KiB;
+ amc->io_size = 256;
+ amc->gpio_count = 23;
+ amc->adc_count = 6;
+ amc->irq = irq168_328;
+ amc->dev = dev168_328;
+};
+
+static void atmega328_class_init(ObjectClass *oc, void *data)
+{
+ AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc);
+
+ amc->cpu_type = AVR_CPU_TYPE_NAME("avr5");
+ amc->flash_size = 32 * KiB;
+ amc->eeprom_size = 1 * KiB;
+ amc->sram_size = 2 * KiB;
+ amc->io_size = 256;
+ amc->gpio_count = 23;
+ amc->adc_count = 6;
+ amc->irq = irq168_328;
+ amc->dev = dev168_328;
+};
+
+static void atmega1280_class_init(ObjectClass *oc, void *data)
+{
+ AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc);
+
+ amc->cpu_type = AVR_CPU_TYPE_NAME("avr6");
+ amc->flash_size = 128 * KiB;
+ amc->eeprom_size = 4 * KiB;
+ amc->sram_size = 8 * KiB;
+ amc->io_size = 512;
+ amc->gpio_count = 86;
+ amc->adc_count = 16;
+ amc->irq = irq1280_2560;
+ amc->dev = dev1280_2560;
+};
+
+static void atmega2560_class_init(ObjectClass *oc, void *data)
+{
+ AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc);
+
+ amc->cpu_type = AVR_CPU_TYPE_NAME("avr6");
+ amc->flash_size = 256 * KiB;
+ amc->eeprom_size = 4 * KiB;
+ amc->sram_size = 8 * KiB;
+ amc->io_size = 512;
+ amc->gpio_count = 54;
+ amc->adc_count = 16;
+ amc->irq = irq1280_2560;
+ amc->dev = dev1280_2560;
+};
+
+static const TypeInfo atmega_mcu_types[] = {
+ {
+ .name = TYPE_ATMEGA168_MCU,
+ .parent = TYPE_ATMEGA_MCU,
+ .class_init = atmega168_class_init,
+ }, {
+ .name = TYPE_ATMEGA328_MCU,
+ .parent = TYPE_ATMEGA_MCU,
+ .class_init = atmega328_class_init,
+ }, {
+ .name = TYPE_ATMEGA1280_MCU,
+ .parent = TYPE_ATMEGA_MCU,
+ .class_init = atmega1280_class_init,
+ }, {
+ .name = TYPE_ATMEGA2560_MCU,
+ .parent = TYPE_ATMEGA_MCU,
+ .class_init = atmega2560_class_init,
+ }, {
+ .name = TYPE_ATMEGA_MCU,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AtmegaMcuState),
+ .class_size = sizeof(AtmegaMcuClass),
+ .class_init = atmega_class_init,
+ .abstract = true,
+ }
+};
+
+DEFINE_TYPES(atmega_mcu_types)
--- /dev/null
+/*
+ * QEMU ATmega MCU
+ *
+ * Copyright (c) 2019-2020 Philippe Mathieu-Daudé
+ *
+ * This work is licensed under the terms of the GNU GPLv2 or later.
+ * See the COPYING file in the top-level directory.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_AVR_ATMEGA_H
+#define HW_AVR_ATMEGA_H
+
+#include "hw/char/avr_usart.h"
+#include "hw/timer/avr_timer16.h"
+#include "hw/misc/avr_power.h"
+#include "target/avr/cpu.h"
+
+#define TYPE_ATMEGA_MCU "ATmega"
+#define TYPE_ATMEGA168_MCU "ATmega168"
+#define TYPE_ATMEGA328_MCU "ATmega328"
+#define TYPE_ATMEGA1280_MCU "ATmega1280"
+#define TYPE_ATMEGA2560_MCU "ATmega2560"
+
+#define ATMEGA_MCU(obj) OBJECT_CHECK(AtmegaMcuState, (obj), TYPE_ATMEGA_MCU)
+
+#define POWER_MAX 2
+#define USART_MAX 4
+#define TIMER_MAX 6
+#define GPIO_MAX 12
+
+typedef struct AtmegaMcuState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+
+ AVRCPU cpu;
+ MemoryRegion flash;
+ MemoryRegion eeprom;
+ MemoryRegion sram;
+ DeviceState *io;
+ AVRMaskState pwr[POWER_MAX];
+ AVRUsartState usart[USART_MAX];
+ AVRTimer16State timer[TIMER_MAX];
+ uint64_t xtal_freq_hz;
+} AtmegaMcuState;
+
+#endif /* HW_AVR_ATMEGA_H */
--- /dev/null
+/*
+ * AVR loader helpers
+ *
+ * Copyright (c) 2019-2020 Philippe Mathieu-Daudé
+ *
+ * This work is licensed under the terms of the GNU GPLv2 or later.
+ * See the COPYING file in the top-level directory.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "hw/loader.h"
+#include "elf.h"
+#include "boot.h"
+#include "qemu/error-report.h"
+
+static const char *avr_elf_e_flags_to_cpu_type(uint32_t flags)
+{
+ switch (flags & EF_AVR_MACH) {
+ case bfd_mach_avr1:
+ return AVR_CPU_TYPE_NAME("avr1");
+ case bfd_mach_avr2:
+ return AVR_CPU_TYPE_NAME("avr2");
+ case bfd_mach_avr25:
+ return AVR_CPU_TYPE_NAME("avr25");
+ case bfd_mach_avr3:
+ return AVR_CPU_TYPE_NAME("avr3");
+ case bfd_mach_avr31:
+ return AVR_CPU_TYPE_NAME("avr31");
+ case bfd_mach_avr35:
+ return AVR_CPU_TYPE_NAME("avr35");
+ case bfd_mach_avr4:
+ return AVR_CPU_TYPE_NAME("avr4");
+ case bfd_mach_avr5:
+ return AVR_CPU_TYPE_NAME("avr5");
+ case bfd_mach_avr51:
+ return AVR_CPU_TYPE_NAME("avr51");
+ case bfd_mach_avr6:
+ return AVR_CPU_TYPE_NAME("avr6");
+ case bfd_mach_avrtiny:
+ return AVR_CPU_TYPE_NAME("avrtiny");
+ case bfd_mach_avrxmega2:
+ return AVR_CPU_TYPE_NAME("xmega2");
+ case bfd_mach_avrxmega3:
+ return AVR_CPU_TYPE_NAME("xmega3");
+ case bfd_mach_avrxmega4:
+ return AVR_CPU_TYPE_NAME("xmega4");
+ case bfd_mach_avrxmega5:
+ return AVR_CPU_TYPE_NAME("xmega5");
+ case bfd_mach_avrxmega6:
+ return AVR_CPU_TYPE_NAME("xmega6");
+ case bfd_mach_avrxmega7:
+ return AVR_CPU_TYPE_NAME("xmega7");
+ default:
+ return NULL;
+ }
+}
+
+bool avr_load_firmware(AVRCPU *cpu, MachineState *ms,
+ MemoryRegion *program_mr, const char *firmware)
+{
+ const char *filename;
+ int bytes_loaded;
+ uint64_t entry;
+ uint32_t e_flags;
+
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, firmware);
+ if (filename == NULL) {
+ error_report("Unable to find %s", firmware);
+ return false;
+ }
+
+ bytes_loaded = load_elf_ram_sym(filename,
+ NULL, NULL, NULL,
+ &entry, NULL, NULL,
+ &e_flags, 0, EM_AVR, 0, 0,
+ NULL, true, NULL);
+ if (bytes_loaded >= 0) {
+ /* If ELF file is provided, determine CPU type reading ELF e_flags. */
+ const char *elf_cpu = avr_elf_e_flags_to_cpu_type(e_flags);
+ const char *mcu_cpu_type = object_get_typename(OBJECT(cpu));
+ int cpu_len = strlen(mcu_cpu_type) - strlen(AVR_CPU_TYPE_SUFFIX);
+
+ if (entry) {
+ error_report("BIOS entry_point must be 0x0000 "
+ "(ELF image '%s' has entry_point 0x%04" PRIx64 ")",
+ firmware, entry);
+ return false;
+ }
+ if (!elf_cpu) {
+ warn_report("Could not determine CPU type for ELF image '%s', "
+ "assuming '%.*s' CPU",
+ firmware, cpu_len, mcu_cpu_type);
+ return true;
+ }
+ if (strcmp(elf_cpu, mcu_cpu_type)) {
+ error_report("Current machine: %s with '%.*s' CPU",
+ MACHINE_GET_CLASS(ms)->desc, cpu_len, mcu_cpu_type);
+ error_report("ELF image '%s' is for '%.*s' CPU",
+ firmware,
+ (int)(strlen(elf_cpu) - strlen(AVR_CPU_TYPE_SUFFIX)),
+ elf_cpu);
+ return false;
+ }
+ } else {
+ bytes_loaded = load_image_mr(filename, program_mr);
+ }
+ if (bytes_loaded < 0) {
+ error_report("Unable to load firmware image %s as ELF or raw binary",
+ firmware);
+ return false;
+ }
+ return true;
+}
--- /dev/null
+/*
+ * AVR loader helpers
+ *
+ * Copyright (c) 2019-2020 Philippe Mathieu-Daudé
+ *
+ * This work is licensed under the terms of the GNU GPLv2 or later.
+ * See the COPYING file in the top-level directory.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_AVR_BOOT_H
+#define HW_AVR_BOOT_H
+
+#include "hw/boards.h"
+#include "cpu.h"
+
+/**
+ * avr_load_firmware: load an image into a memory region
+ *
+ * @cpu: Handle a AVR CPU object
+ * @ms: A MachineState
+ * @mr: Memory Region to load into
+ * @firmware: Path to the firmware file (raw binary or ELF format)
+ *
+ * Load a firmware supplied by the machine or by the user with the
+ * '-bios' command line option, and put it in target memory.
+ *
+ * Returns: true on success, false on error.
+ */
+bool avr_load_firmware(AVRCPU *cpu, MachineState *ms,
+ MemoryRegion *mr, const char *firmware);
+
+#endif
config RENESAS_SCI
bool
+
+config AVR_USART
+ bool
common-obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o
common-obj-$(CONFIG_RASPI) += bcm2835_aux.o
common-obj-$(CONFIG_RENESAS_SCI) += renesas_sci.o
+common-obj-$(CONFIG_AVR_USART) += avr_usart.o
common-obj-$(CONFIG_CMSDK_APB_UART) += cmsdk-apb-uart.o
common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o
--- /dev/null
+/*
+ * AVR USART
+ *
+ * Copyright (c) 2018 University of Kent
+ * Author: Sarah Harris
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "qemu/osdep.h"
+#include "hw/char/avr_usart.h"
+#include "qemu/log.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+
+static int avr_usart_can_receive(void *opaque)
+{
+ AVRUsartState *usart = opaque;
+
+ if (usart->data_valid || !(usart->csrb & USART_CSRB_RXEN)) {
+ return 0;
+ }
+ return 1;
+}
+
+static void avr_usart_receive(void *opaque, const uint8_t *buffer, int size)
+{
+ AVRUsartState *usart = opaque;
+ assert(size == 1);
+ assert(!usart->data_valid);
+ usart->data = buffer[0];
+ usart->data_valid = true;
+ usart->csra |= USART_CSRA_RXC;
+ if (usart->csrb & USART_CSRB_RXCIE) {
+ qemu_set_irq(usart->rxc_irq, 1);
+ }
+}
+
+static void update_char_mask(AVRUsartState *usart)
+{
+ uint8_t mode = ((usart->csrc & USART_CSRC_CSZ0) ? 1 : 0) |
+ ((usart->csrc & USART_CSRC_CSZ1) ? 2 : 0) |
+ ((usart->csrb & USART_CSRB_CSZ2) ? 4 : 0);
+ switch (mode) {
+ case 0:
+ usart->char_mask = 0b11111;
+ break;
+ case 1:
+ usart->char_mask = 0b111111;
+ break;
+ case 2:
+ usart->char_mask = 0b1111111;
+ break;
+ case 3:
+ usart->char_mask = 0b11111111;
+ break;
+ case 4:
+ /* Fallthrough. */
+ case 5:
+ /* Fallthrough. */
+ case 6:
+ qemu_log_mask(
+ LOG_GUEST_ERROR,
+ "%s: Reserved character size 0x%x\n",
+ __func__,
+ mode);
+ break;
+ case 7:
+ qemu_log_mask(
+ LOG_GUEST_ERROR,
+ "%s: Nine bit character size not supported (forcing eight)\n",
+ __func__);
+ usart->char_mask = 0b11111111;
+ break;
+ default:
+ assert(0);
+ }
+}
+
+static void avr_usart_reset(DeviceState *dev)
+{
+ AVRUsartState *usart = AVR_USART(dev);
+ usart->data_valid = false;
+ usart->csra = 0b00100000;
+ usart->csrb = 0b00000000;
+ usart->csrc = 0b00000110;
+ usart->brrl = 0;
+ usart->brrh = 0;
+ update_char_mask(usart);
+ qemu_set_irq(usart->rxc_irq, 0);
+ qemu_set_irq(usart->txc_irq, 0);
+ qemu_set_irq(usart->dre_irq, 0);
+}
+
+static uint64_t avr_usart_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ AVRUsartState *usart = opaque;
+ uint8_t data;
+ assert(size == 1);
+
+ if (!usart->enabled) {
+ return 0;
+ }
+
+ switch (addr) {
+ case USART_DR:
+ if (!(usart->csrb & USART_CSRB_RXEN)) {
+ /* Receiver disabled, ignore. */
+ return 0;
+ }
+ if (usart->data_valid) {
+ data = usart->data & usart->char_mask;
+ usart->data_valid = false;
+ } else {
+ data = 0;
+ }
+ usart->csra &= 0xff ^ USART_CSRA_RXC;
+ qemu_set_irq(usart->rxc_irq, 0);
+ qemu_chr_fe_accept_input(&usart->chr);
+ return data;
+ case USART_CSRA:
+ return usart->csra;
+ case USART_CSRB:
+ return usart->csrb;
+ case USART_CSRC:
+ return usart->csrc;
+ case USART_BRRL:
+ return usart->brrl;
+ case USART_BRRH:
+ return usart->brrh;
+ default:
+ qemu_log_mask(
+ LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%"HWADDR_PRIx"\n",
+ __func__,
+ addr);
+ }
+ return 0;
+}
+
+static void avr_usart_write(void *opaque, hwaddr addr, uint64_t value,
+ unsigned int size)
+{
+ AVRUsartState *usart = opaque;
+ uint8_t mask;
+ uint8_t data;
+ assert((value & 0xff) == value);
+ assert(size == 1);
+
+ if (!usart->enabled) {
+ return;
+ }
+
+ switch (addr) {
+ case USART_DR:
+ if (!(usart->csrb & USART_CSRB_TXEN)) {
+ /* Transmitter disabled, ignore. */
+ return;
+ }
+ usart->csra |= USART_CSRA_TXC;
+ usart->csra |= USART_CSRA_DRE;
+ if (usart->csrb & USART_CSRB_TXCIE) {
+ qemu_set_irq(usart->txc_irq, 1);
+ usart->csra &= 0xff ^ USART_CSRA_TXC;
+ }
+ if (usart->csrb & USART_CSRB_DREIE) {
+ qemu_set_irq(usart->dre_irq, 1);
+ }
+ data = value;
+ qemu_chr_fe_write_all(&usart->chr, &data, 1);
+ break;
+ case USART_CSRA:
+ mask = 0b01000011;
+ /* Mask read-only bits. */
+ value = (value & mask) | (usart->csra & (0xff ^ mask));
+ usart->csra = value;
+ if (value & USART_CSRA_TXC) {
+ usart->csra ^= USART_CSRA_TXC;
+ qemu_set_irq(usart->txc_irq, 0);
+ }
+ if (value & USART_CSRA_MPCM) {
+ qemu_log_mask(
+ LOG_GUEST_ERROR,
+ "%s: MPCM not supported by USART\n",
+ __func__);
+ }
+ break;
+ case USART_CSRB:
+ mask = 0b11111101;
+ /* Mask read-only bits. */
+ value = (value & mask) | (usart->csrb & (0xff ^ mask));
+ usart->csrb = value;
+ if (!(value & USART_CSRB_RXEN)) {
+ /* Receiver disabled, flush input buffer. */
+ usart->data_valid = false;
+ }
+ qemu_set_irq(usart->rxc_irq,
+ ((value & USART_CSRB_RXCIE) &&
+ (usart->csra & USART_CSRA_RXC)) ? 1 : 0);
+ qemu_set_irq(usart->txc_irq,
+ ((value & USART_CSRB_TXCIE) &&
+ (usart->csra & USART_CSRA_TXC)) ? 1 : 0);
+ qemu_set_irq(usart->dre_irq,
+ ((value & USART_CSRB_DREIE) &&
+ (usart->csra & USART_CSRA_DRE)) ? 1 : 0);
+ update_char_mask(usart);
+ break;
+ case USART_CSRC:
+ usart->csrc = value;
+ if ((value & USART_CSRC_MSEL1) && (value & USART_CSRC_MSEL0)) {
+ qemu_log_mask(
+ LOG_GUEST_ERROR,
+ "%s: SPI mode not supported by USART\n",
+ __func__);
+ }
+ if ((value & USART_CSRC_MSEL1) && !(value & USART_CSRC_MSEL0)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad USART mode\n", __func__);
+ }
+ if (!(value & USART_CSRC_PM1) && (value & USART_CSRC_PM0)) {
+ qemu_log_mask(
+ LOG_GUEST_ERROR,
+ "%s: Bad USART parity mode\n",
+ __func__);
+ }
+ update_char_mask(usart);
+ break;
+ case USART_BRRL:
+ usart->brrl = value;
+ break;
+ case USART_BRRH:
+ usart->brrh = value & 0b00001111;
+ break;
+ default:
+ qemu_log_mask(
+ LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%"HWADDR_PRIx"\n",
+ __func__,
+ addr);
+ }
+}
+
+static const MemoryRegionOps avr_usart_ops = {
+ .read = avr_usart_read,
+ .write = avr_usart_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {.min_access_size = 1, .max_access_size = 1}
+};
+
+static Property avr_usart_properties[] = {
+ DEFINE_PROP_CHR("chardev", AVRUsartState, chr),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void avr_usart_pr(void *opaque, int irq, int level)
+{
+ AVRUsartState *s = AVR_USART(opaque);
+
+ s->enabled = !level;
+
+ if (!s->enabled) {
+ avr_usart_reset(DEVICE(s));
+ }
+}
+
+static void avr_usart_init(Object *obj)
+{
+ AVRUsartState *s = AVR_USART(obj);
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->rxc_irq);
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->dre_irq);
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->txc_irq);
+ memory_region_init_io(&s->mmio, obj, &avr_usart_ops, s, TYPE_AVR_USART, 7);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+ qdev_init_gpio_in(DEVICE(s), avr_usart_pr, 1);
+ s->enabled = true;
+}
+
+static void avr_usart_realize(DeviceState *dev, Error **errp)
+{
+ AVRUsartState *s = AVR_USART(dev);
+ qemu_chr_fe_set_handlers(&s->chr, avr_usart_can_receive,
+ avr_usart_receive, NULL, NULL,
+ s, NULL, true);
+ avr_usart_reset(dev);
+}
+
+static void avr_usart_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = avr_usart_reset;
+ device_class_set_props(dc, avr_usart_properties);
+ dc->realize = avr_usart_realize;
+}
+
+static const TypeInfo avr_usart_info = {
+ .name = TYPE_AVR_USART,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AVRUsartState),
+ .instance_init = avr_usart_init,
+ .class_init = avr_usart_class_init,
+};
+
+static void avr_usart_register_types(void)
+{
+ type_register_static(&avr_usart_info);
+}
+
+type_init(avr_usart_register_types)
#include "qemu/osdep.h"
#include "hw/char/ibex_uart.h"
#include "hw/irq.h"
+#include "hw/qdev-clock.h"
#include "hw/qdev-properties.h"
#include "migration/vmstate.h"
#include "qemu/log.h"
static void ibex_uart_update_irqs(IbexUartState *s)
{
- if (s->uart_intr_state & s->uart_intr_enable & INTR_STATE_TX_WATERMARK) {
+ if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_TX_WATERMARK_MASK) {
qemu_set_irq(s->tx_watermark, 1);
} else {
qemu_set_irq(s->tx_watermark, 0);
}
- if (s->uart_intr_state & s->uart_intr_enable & INTR_STATE_RX_WATERMARK) {
+ if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_RX_WATERMARK_MASK) {
qemu_set_irq(s->rx_watermark, 1);
} else {
qemu_set_irq(s->rx_watermark, 0);
}
- if (s->uart_intr_state & s->uart_intr_enable & INTR_STATE_TX_EMPTY) {
+ if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_TX_EMPTY_MASK) {
qemu_set_irq(s->tx_empty, 1);
} else {
qemu_set_irq(s->tx_empty, 0);
}
- if (s->uart_intr_state & s->uart_intr_enable & INTR_STATE_RX_OVERFLOW) {
+ if (s->uart_intr_state & s->uart_intr_enable & R_INTR_STATE_RX_OVERFLOW_MASK) {
qemu_set_irq(s->rx_overflow, 1);
} else {
qemu_set_irq(s->rx_overflow, 0);
{
IbexUartState *s = opaque;
- if (s->uart_ctrl & UART_CTRL_RX_ENABLE) {
+ if (s->uart_ctrl & R_CTRL_RX_ENABLE_MASK) {
return 1;
}
static void ibex_uart_receive(void *opaque, const uint8_t *buf, int size)
{
IbexUartState *s = opaque;
- uint8_t rx_fifo_level = (s->uart_fifo_ctrl & FIFO_CTRL_RXILVL)
- >> FIFO_CTRL_RXILVL_SHIFT;
+ uint8_t rx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_RXILVL_MASK)
+ >> R_FIFO_CTRL_RXILVL_SHIFT;
s->uart_rdata = *buf;
- s->uart_status &= ~UART_STATUS_RXIDLE;
- s->uart_status &= ~UART_STATUS_RXEMPTY;
+ s->uart_status &= ~R_STATUS_RXIDLE_MASK;
+ s->uart_status &= ~R_STATUS_RXEMPTY_MASK;
if (size > rx_fifo_level) {
- s->uart_intr_state |= INTR_STATE_RX_WATERMARK;
+ s->uart_intr_state |= R_INTR_STATE_RX_WATERMARK_MASK;
}
ibex_uart_update_irqs(s);
void *opaque)
{
IbexUartState *s = opaque;
- uint8_t tx_fifo_level = (s->uart_fifo_ctrl & FIFO_CTRL_TXILVL)
- >> FIFO_CTRL_TXILVL_SHIFT;
+ uint8_t tx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_TXILVL_MASK)
+ >> R_FIFO_CTRL_TXILVL_SHIFT;
int ret;
/* instant drain the fifo when there's no back-end */
}
if (!s->tx_level) {
- s->uart_status &= ~UART_STATUS_TXFULL;
- s->uart_status |= UART_STATUS_TXEMPTY;
- s->uart_intr_state |= INTR_STATE_TX_EMPTY;
- s->uart_intr_state &= ~INTR_STATE_TX_WATERMARK;
+ s->uart_status &= ~R_STATUS_TXFULL_MASK;
+ s->uart_status |= R_STATUS_TXEMPTY_MASK;
+ s->uart_intr_state |= R_INTR_STATE_TX_EMPTY_MASK;
+ s->uart_intr_state &= ~R_INTR_STATE_TX_WATERMARK_MASK;
ibex_uart_update_irqs(s);
return FALSE;
}
/* Clear the TX Full bit */
if (s->tx_level != IBEX_UART_TX_FIFO_SIZE) {
- s->uart_status &= ~UART_STATUS_TXFULL;
+ s->uart_status &= ~R_STATUS_TXFULL_MASK;
}
/* Disable the TX_WATERMARK IRQ */
if (s->tx_level < tx_fifo_level) {
- s->uart_intr_state &= ~INTR_STATE_TX_WATERMARK;
+ s->uart_intr_state &= ~R_INTR_STATE_TX_WATERMARK_MASK;
}
/* Set TX empty */
if (s->tx_level == 0) {
- s->uart_status |= UART_STATUS_TXEMPTY;
- s->uart_intr_state |= INTR_STATE_TX_EMPTY;
+ s->uart_status |= R_STATUS_TXEMPTY_MASK;
+ s->uart_intr_state |= R_INTR_STATE_TX_EMPTY_MASK;
}
ibex_uart_update_irqs(s);
int size)
{
uint64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- uint8_t tx_fifo_level = (s->uart_fifo_ctrl & FIFO_CTRL_TXILVL)
- >> FIFO_CTRL_TXILVL_SHIFT;
+ uint8_t tx_fifo_level = (s->uart_fifo_ctrl & R_FIFO_CTRL_TXILVL_MASK)
+ >> R_FIFO_CTRL_TXILVL_SHIFT;
if (size > IBEX_UART_TX_FIFO_SIZE - s->tx_level) {
size = IBEX_UART_TX_FIFO_SIZE - s->tx_level;
s->tx_level += size;
if (s->tx_level > 0) {
- s->uart_status &= ~UART_STATUS_TXEMPTY;
+ s->uart_status &= ~R_STATUS_TXEMPTY_MASK;
}
if (s->tx_level >= tx_fifo_level) {
- s->uart_intr_state |= INTR_STATE_TX_WATERMARK;
+ s->uart_intr_state |= R_INTR_STATE_TX_WATERMARK_MASK;
ibex_uart_update_irqs(s);
}
if (s->tx_level == IBEX_UART_TX_FIFO_SIZE) {
- s->uart_status |= UART_STATUS_TXFULL;
+ s->uart_status |= R_STATUS_TXFULL_MASK;
}
timer_mod(s->fifo_trigger_handle, current_time +
ibex_uart_update_irqs(s);
}
+static uint64_t ibex_uart_get_baud(IbexUartState *s)
+{
+ uint64_t baud;
+
+ baud = ((s->uart_ctrl & R_CTRL_NCO_MASK) >> 16);
+ baud *= clock_get_hz(s->f_clk);
+ baud >>= 20;
+
+ return baud;
+}
+
static uint64_t ibex_uart_read(void *opaque, hwaddr addr,
unsigned int size)
{
IbexUartState *s = opaque;
uint64_t retvalue = 0;
- switch (addr) {
- case IBEX_UART_INTR_STATE:
+ switch (addr >> 2) {
+ case R_INTR_STATE:
retvalue = s->uart_intr_state;
break;
- case IBEX_UART_INTR_ENABLE:
+ case R_INTR_ENABLE:
retvalue = s->uart_intr_enable;
break;
- case IBEX_UART_INTR_TEST:
+ case R_INTR_TEST:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: wdata is write only\n", __func__);
break;
- case IBEX_UART_CTRL:
+ case R_CTRL:
retvalue = s->uart_ctrl;
break;
- case IBEX_UART_STATUS:
+ case R_STATUS:
retvalue = s->uart_status;
break;
- case IBEX_UART_RDATA:
+ case R_RDATA:
retvalue = s->uart_rdata;
- if (s->uart_ctrl & UART_CTRL_RX_ENABLE) {
+ if (s->uart_ctrl & R_CTRL_RX_ENABLE_MASK) {
qemu_chr_fe_accept_input(&s->chr);
- s->uart_status |= UART_STATUS_RXIDLE;
- s->uart_status |= UART_STATUS_RXEMPTY;
+ s->uart_status |= R_STATUS_RXIDLE_MASK;
+ s->uart_status |= R_STATUS_RXEMPTY_MASK;
}
break;
- case IBEX_UART_WDATA:
+ case R_WDATA:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: wdata is write only\n", __func__);
break;
- case IBEX_UART_FIFO_CTRL:
+ case R_FIFO_CTRL:
retvalue = s->uart_fifo_ctrl;
break;
- case IBEX_UART_FIFO_STATUS:
+ case R_FIFO_STATUS:
retvalue = s->uart_fifo_status;
retvalue |= s->tx_level & 0x1F;
"%s: RX fifos are not supported\n", __func__);
break;
- case IBEX_UART_OVRD:
+ case R_OVRD:
retvalue = s->uart_ovrd;
qemu_log_mask(LOG_UNIMP,
"%s: ovrd is not supported\n", __func__);
break;
- case IBEX_UART_VAL:
+ case R_VAL:
retvalue = s->uart_val;
qemu_log_mask(LOG_UNIMP,
"%s: val is not supported\n", __func__);
break;
- case IBEX_UART_TIMEOUT_CTRL:
+ case R_TIMEOUT_CTRL:
retvalue = s->uart_timeout_ctrl;
qemu_log_mask(LOG_UNIMP,
"%s: timeout_ctrl is not supported\n", __func__);
IbexUartState *s = opaque;
uint32_t value = val64;
- switch (addr) {
- case IBEX_UART_INTR_STATE:
+ switch (addr >> 2) {
+ case R_INTR_STATE:
/* Write 1 clear */
s->uart_intr_state &= ~value;
ibex_uart_update_irqs(s);
break;
- case IBEX_UART_INTR_ENABLE:
+ case R_INTR_ENABLE:
s->uart_intr_enable = value;
ibex_uart_update_irqs(s);
break;
- case IBEX_UART_INTR_TEST:
+ case R_INTR_TEST:
s->uart_intr_state |= value;
ibex_uart_update_irqs(s);
break;
- case IBEX_UART_CTRL:
+ case R_CTRL:
s->uart_ctrl = value;
- if (value & UART_CTRL_NF) {
+ if (value & R_CTRL_NF_MASK) {
qemu_log_mask(LOG_UNIMP,
"%s: UART_CTRL_NF is not supported\n", __func__);
}
- if (value & UART_CTRL_SLPBK) {
+ if (value & R_CTRL_SLPBK_MASK) {
qemu_log_mask(LOG_UNIMP,
"%s: UART_CTRL_SLPBK is not supported\n", __func__);
}
- if (value & UART_CTRL_LLPBK) {
+ if (value & R_CTRL_LLPBK_MASK) {
qemu_log_mask(LOG_UNIMP,
"%s: UART_CTRL_LLPBK is not supported\n", __func__);
}
- if (value & UART_CTRL_PARITY_EN) {
+ if (value & R_CTRL_PARITY_EN_MASK) {
qemu_log_mask(LOG_UNIMP,
"%s: UART_CTRL_PARITY_EN is not supported\n",
__func__);
}
- if (value & UART_CTRL_PARITY_ODD) {
+ if (value & R_CTRL_PARITY_ODD_MASK) {
qemu_log_mask(LOG_UNIMP,
"%s: UART_CTRL_PARITY_ODD is not supported\n",
__func__);
}
- if (value & UART_CTRL_RXBLVL) {
+ if (value & R_CTRL_RXBLVL_MASK) {
qemu_log_mask(LOG_UNIMP,
"%s: UART_CTRL_RXBLVL is not supported\n", __func__);
}
- if (value & UART_CTRL_NCO) {
- uint64_t baud = ((value & UART_CTRL_NCO) >> 16);
- baud *= 1000;
- baud >>= 20;
+ if (value & R_CTRL_NCO_MASK) {
+ uint64_t baud = ibex_uart_get_baud(s);
s->char_tx_time = (NANOSECONDS_PER_SECOND / baud) * 10;
}
break;
- case IBEX_UART_STATUS:
+ case R_STATUS:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: status is read only\n", __func__);
break;
- case IBEX_UART_RDATA:
+ case R_RDATA:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: rdata is read only\n", __func__);
break;
- case IBEX_UART_WDATA:
+ case R_WDATA:
uart_write_tx_fifo(s, (uint8_t *) &value, 1);
break;
- case IBEX_UART_FIFO_CTRL:
+ case R_FIFO_CTRL:
s->uart_fifo_ctrl = value;
- if (value & FIFO_CTRL_RXRST) {
+ if (value & R_FIFO_CTRL_RXRST_MASK) {
qemu_log_mask(LOG_UNIMP,
"%s: RX fifos are not supported\n", __func__);
}
- if (value & FIFO_CTRL_TXRST) {
+ if (value & R_FIFO_CTRL_TXRST_MASK) {
s->tx_level = 0;
}
break;
- case IBEX_UART_FIFO_STATUS:
+ case R_FIFO_STATUS:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: fifo_status is read only\n", __func__);
break;
- case IBEX_UART_OVRD:
+ case R_OVRD:
s->uart_ovrd = value;
qemu_log_mask(LOG_UNIMP,
"%s: ovrd is not supported\n", __func__);
break;
- case IBEX_UART_VAL:
+ case R_VAL:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: val is read only\n", __func__);
break;
- case IBEX_UART_TIMEOUT_CTRL:
+ case R_TIMEOUT_CTRL:
s->uart_timeout_ctrl = value;
qemu_log_mask(LOG_UNIMP,
"%s: timeout_ctrl is not supported\n", __func__);
}
}
+static void ibex_uart_clk_update(void *opaque)
+{
+ IbexUartState *s = opaque;
+
+ /* recompute uart's speed on clock change */
+ uint64_t baud = ibex_uart_get_baud(s);
+
+ s->char_tx_time = (NANOSECONDS_PER_SECOND / baud) * 10;
+}
+
static void fifo_trigger_update(void *opaque)
{
IbexUartState *s = opaque;
- if (s->uart_ctrl & UART_CTRL_TX_ENABLE) {
+ if (s->uart_ctrl & R_CTRL_TX_ENABLE_MASK) {
ibex_uart_xmit(NULL, G_IO_OUT, s);
}
}
{
IbexUartState *s = IBEX_UART(obj);
+ s->f_clk = qdev_init_clock_in(DEVICE(obj), "f_clock",
+ ibex_uart_clk_update, s);
+ clock_set_hz(s->f_clk, IBEX_UART_CLOCK);
+
sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->tx_watermark);
sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->rx_watermark);
sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->tx_empty);
mc->max_cpus = 1;
mc->default_ram_size = 0;
mc->default_ram_id = "ram";
+ mc->no_serial = 1;
+ mc->no_parallel = 1;
+ mc->no_floppy = 1;
+ mc->no_cdrom = 1;
+ mc->no_sdcard = 1;
}
DEFINE_MACHINE("none", machine_none_machine_init)
qxl.mo-objs = qxl.o qxl-logger.o qxl-render.o
endif
-ifeq ($(CONFIG_VIRTIO_GPU),y)
-common-obj-m += virtio-gpu.mo
-virtio-gpu-obj-$(CONFIG_VIRTIO_GPU) += virtio-gpu-base.o virtio-gpu.o virtio-gpu-3d.o
-virtio-gpu-obj-$(CONFIG_VHOST_USER_GPU) += vhost-user-gpu.o
-virtio-gpu-obj-$(call land,$(CONFIG_VIRTIO_GPU),$(CONFIG_VIRTIO_PCI)) += virtio-gpu-pci.o
-virtio-gpu-obj-$(call land,$(CONFIG_VHOST_USER_GPU),$(CONFIG_VIRTIO_PCI)) += vhost-user-gpu-pci.o
-virtio-gpu-obj-$(CONFIG_VIRTIO_VGA) += virtio-vga.o
-virtio-gpu-obj-$(CONFIG_VHOST_USER_VGA) += vhost-user-vga.o
-virtio-gpu.mo-objs := $(virtio-gpu-obj-y)
-virtio-gpu.mo-cflags := $(VIRGL_CFLAGS)
-virtio-gpu.mo-libs := $(VIRGL_LIBS)
-endif
-
+common-obj-$(CONFIG_VIRTIO_GPU) += virtio-gpu-base.o virtio-gpu.o virtio-gpu-3d.o
+common-obj-$(CONFIG_VHOST_USER_GPU) += vhost-user-gpu.o
+common-obj-$(call land,$(CONFIG_VIRTIO_GPU),$(CONFIG_VIRTIO_PCI)) += virtio-gpu-pci.o
+common-obj-$(call land,$(CONFIG_VHOST_USER_GPU),$(CONFIG_VIRTIO_PCI)) += vhost-user-gpu-pci.o
+common-obj-$(CONFIG_VIRTIO_VGA) += virtio-vga.o
+common-obj-$(CONFIG_VHOST_USER_VGA) += vhost-user-vga.o
+virtio-gpu.o-cflags := $(VIRGL_CFLAGS)
+virtio-gpu.o-libs += $(VIRGL_LIBS)
+virtio-gpu-3d.o-cflags := $(VIRGL_CFLAGS)
+virtio-gpu-3d.o-libs += $(VIRGL_LIBS)
common-obj-$(CONFIG_DPCD) += dpcd.o
common-obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx_dp.o
if (!xen_enabled()) {
pc_memory_init(pcms, system_memory,
rom_memory, &ram_memory);
- } else if (machine->kernel_filename != NULL) {
- /* For xen HVM direct kernel boot, load linux here */
- xen_load_linux(pcms);
+ } else {
+ pc_system_flash_cleanup_unused(pcms);
+ if (machine->kernel_filename != NULL) {
+ /* For xen HVM direct kernel boot, load linux here */
+ xen_load_linux(pcms);
+ }
}
gsi_state = pc_gsi_create(&x86ms->gsi, pcmc->pci_enabled);
object_property_add_child(OBJECT(pcms), name, OBJECT(dev));
object_property_add_alias(OBJECT(pcms), alias_prop_name,
OBJECT(dev), "drive");
+ /*
+ * The returned reference is tied to the child property and
+ * will be removed with object_unparent.
+ */
+ object_unref(OBJECT(dev));
return PFLASH_CFI01(dev);
}
}
}
-static void pc_system_flash_cleanup_unused(PCMachineState *pcms)
+void pc_system_flash_cleanup_unused(PCMachineState *pcms)
{
char *prop_name;
int i;
return 0;
}
-static uint32_t apic_get_current_count(APICCommonState *s)
-{
- int64_t d;
- uint32_t val;
- d = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->initial_count_load_time) >>
- s->count_shift;
- if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) {
- /* periodic */
- val = s->initial_count - (d % ((uint64_t)s->initial_count + 1));
- } else {
- if (d >= s->initial_count)
- val = 0;
- else
- val = s->initial_count - d;
- }
- return val;
-}
-
static void apic_timer_update(APICCommonState *s, int64_t current_time)
{
if (apic_next_timer(s, current_time)) {
return true;
}
+uint32_t apic_get_current_count(APICCommonState *s)
+{
+ int64_t d;
+ uint32_t val;
+ d = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->initial_count_load_time) >>
+ s->count_shift;
+ if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) {
+ /* periodic */
+ val = s->initial_count - (d % ((uint64_t)s->initial_count + 1));
+ } else {
+ if (d >= s->initial_count) {
+ val = 0;
+ } else {
+ val = s->initial_count - d;
+ }
+ }
+ return val;
+}
+
void apic_init_reset(DeviceState *dev)
{
APICCommonState *s;
#define IPMI_CMD_GET_SENSOR_READING 0x2d
#define IPMI_CMD_SET_SENSOR_TYPE 0x2e
#define IPMI_CMD_GET_SENSOR_TYPE 0x2f
+#define IPMI_CMD_SET_SENSOR_READING 0x30
/* #define IPMI_NETFN_APP 0x06 In ipmi.h */
rsp_buffer_push(rsp, sens->evt_reading_type_code);
}
+/*
+ * bytes parameter
+ * 1 sensor number
+ * 2 operation (see below for bits meaning)
+ * 3 sensor reading
+ * 4:5 assertion states (optional)
+ * 6:7 deassertion states (optional)
+ * 8:10 event data 1,2,3 (optional)
+ */
+static void set_sensor_reading(IPMIBmcSim *ibs,
+ uint8_t *cmd, unsigned int cmd_len,
+ RspBuffer *rsp)
+{
+ IPMISensor *sens;
+ uint8_t evd1 = 0;
+ uint8_t evd2 = 0;
+ uint8_t evd3 = 0;
+ uint8_t new_reading = 0;
+ uint16_t new_assert_states = 0;
+ uint16_t new_deassert_states = 0;
+ bool change_reading = false;
+ bool change_assert = false;
+ bool change_deassert = false;
+ enum {
+ SENSOR_GEN_EVENT_NONE,
+ SENSOR_GEN_EVENT_DATA,
+ SENSOR_GEN_EVENT_BMC,
+ } do_gen_event = SENSOR_GEN_EVENT_NONE;
+
+ if ((cmd[2] >= MAX_SENSORS) ||
+ !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) {
+ rsp_buffer_set_error(rsp, IPMI_CC_REQ_ENTRY_NOT_PRESENT);
+ return;
+ }
+
+ sens = ibs->sensors + cmd[2];
+
+ /* [1:0] Sensor Reading operation */
+ switch ((cmd[3]) & 0x3) {
+ case 0: /* Do not change */
+ break;
+ case 1: /* write given value to sensor reading byte */
+ new_reading = cmd[4];
+ if (sens->reading != new_reading) {
+ change_reading = true;
+ }
+ break;
+ case 2:
+ case 3:
+ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
+ return;
+ }
+
+ /* [3:2] Deassertion bits operation */
+ switch ((cmd[3] >> 2) & 0x3) {
+ case 0: /* Do not change */
+ break;
+ case 1: /* write given value */
+ if (cmd_len > 7) {
+ new_deassert_states = cmd[7];
+ change_deassert = true;
+ }
+ if (cmd_len > 8) {
+ new_deassert_states |= (cmd[8] << 8);
+ }
+ break;
+
+ case 2: /* mask on */
+ if (cmd_len > 7) {
+ new_deassert_states = (sens->deassert_states | cmd[7]);
+ change_deassert = true;
+ }
+ if (cmd_len > 8) {
+ new_deassert_states |= (sens->deassert_states | (cmd[8] << 8));
+ }
+ break;
+
+ case 3: /* mask off */
+ if (cmd_len > 7) {
+ new_deassert_states = (sens->deassert_states & cmd[7]);
+ change_deassert = true;
+ }
+ if (cmd_len > 8) {
+ new_deassert_states |= (sens->deassert_states & (cmd[8] << 8));
+ }
+ break;
+ }
+
+ if (change_deassert && (new_deassert_states == sens->deassert_states)) {
+ change_deassert = false;
+ }
+
+ /* [5:4] Assertion bits operation */
+ switch ((cmd[3] >> 4) & 0x3) {
+ case 0: /* Do not change */
+ break;
+ case 1: /* write given value */
+ if (cmd_len > 5) {
+ new_assert_states = cmd[5];
+ change_assert = true;
+ }
+ if (cmd_len > 6) {
+ new_assert_states |= (cmd[6] << 8);
+ }
+ break;
+
+ case 2: /* mask on */
+ if (cmd_len > 5) {
+ new_assert_states = (sens->assert_states | cmd[5]);
+ change_assert = true;
+ }
+ if (cmd_len > 6) {
+ new_assert_states |= (sens->assert_states | (cmd[6] << 8));
+ }
+ break;
+
+ case 3: /* mask off */
+ if (cmd_len > 5) {
+ new_assert_states = (sens->assert_states & cmd[5]);
+ change_assert = true;
+ }
+ if (cmd_len > 6) {
+ new_assert_states |= (sens->assert_states & (cmd[6] << 8));
+ }
+ break;
+ }
+
+ if (change_assert && (new_assert_states == sens->assert_states)) {
+ change_assert = false;
+ }
+
+ if (cmd_len > 9) {
+ evd1 = cmd[9];
+ }
+ if (cmd_len > 10) {
+ evd2 = cmd[10];
+ }
+ if (cmd_len > 11) {
+ evd3 = cmd[11];
+ }
+
+ /* [7:6] Event Data Bytes operation */
+ switch ((cmd[3] >> 6) & 0x3) {
+ case 0: /*
+ * Don’t use Event Data bytes from this command. BMC will
+ * generate it's own Event Data bytes based on its sensor
+ * implementation.
+ */
+ evd1 = evd2 = evd3 = 0x0;
+ do_gen_event = SENSOR_GEN_EVENT_BMC;
+ break;
+ case 1: /*
+ * Write given values to event data bytes including bits
+ * [3:0] Event Data 1.
+ */
+ do_gen_event = SENSOR_GEN_EVENT_DATA;
+ break;
+ case 2: /*
+ * Write given values to event data bytes excluding bits
+ * [3:0] Event Data 1.
+ */
+ evd1 &= 0xf0;
+ do_gen_event = SENSOR_GEN_EVENT_DATA;
+ break;
+ case 3:
+ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
+ return;
+ }
+
+ /*
+ * Event Data Bytes operation and parameter are inconsistent. The
+ * Specs are not clear on that topic but generating an error seems
+ * correct.
+ */
+ if (do_gen_event == SENSOR_GEN_EVENT_DATA && cmd_len < 10) {
+ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD);
+ return;
+ }
+
+ /* commit values */
+ if (change_reading) {
+ sens->reading = new_reading;
+ }
+
+ if (change_assert) {
+ sens->assert_states = new_assert_states;
+ }
+
+ if (change_deassert) {
+ sens->deassert_states = new_deassert_states;
+ }
+
+ /* TODO: handle threshold sensor */
+ if (!IPMI_SENSOR_IS_DISCRETE(sens)) {
+ return;
+ }
+
+ switch (do_gen_event) {
+ case SENSOR_GEN_EVENT_DATA: {
+ unsigned int bit = evd1 & 0xf;
+ uint16_t mask = (1 << bit);
+
+ if (sens->assert_states & mask & sens->assert_enable) {
+ gen_event(ibs, cmd[2], 0, evd1, evd2, evd3);
+ }
+
+ if (sens->deassert_states & mask & sens->deassert_enable) {
+ gen_event(ibs, cmd[2], 1, evd1, evd2, evd3);
+ }
+ break;
+ }
+ case SENSOR_GEN_EVENT_BMC:
+ /*
+ * TODO: generate event and event data bytes depending on the
+ * sensor
+ */
+ break;
+ case SENSOR_GEN_EVENT_NONE:
+ break;
+ }
+}
static const IPMICmdHandler chassis_cmds[] = {
[IPMI_CMD_GET_CHASSIS_CAPABILITIES] = { chassis_capabilities },
[IPMI_CMD_GET_SENSOR_READING] = { get_sensor_reading, 3 },
[IPMI_CMD_SET_SENSOR_TYPE] = { set_sensor_type, 5 },
[IPMI_CMD_GET_SENSOR_TYPE] = { get_sensor_type, 3 },
+ [IPMI_CMD_SET_SENSOR_READING] = { set_sensor_reading, 5 },
};
static const IPMINetfn sensor_event_netfn = {
.cmd_nums = ARRAY_SIZE(sensor_event_cmds),
select MOS6522
select ADB
+config AVR_POWER
+ bool
+
source macio/Kconfig
obj-$(CONFIG_MAC_VIA) += mac_via.o
common-obj-$(CONFIG_GRLIB) += grlib_ahb_apb_pnp.o
+
+obj-$(CONFIG_AVR_POWER) += avr_power.o
--- /dev/null
+/*
+ * AVR Power Reduction Management
+ *
+ * Copyright (c) 2019-2020 Michael Rolnik
+ *
+ * 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 "hw/misc/avr_power.h"
+#include "qemu/log.h"
+#include "hw/qdev-properties.h"
+#include "hw/irq.h"
+#include "trace.h"
+
+static void avr_mask_reset(DeviceState *dev)
+{
+ AVRMaskState *s = AVR_MASK(dev);
+
+ s->val = 0x00;
+
+ for (int i = 0; i < 8; i++) {
+ qemu_set_irq(s->irq[i], 0);
+ }
+}
+
+static uint64_t avr_mask_read(void *opaque, hwaddr offset, unsigned size)
+{
+ assert(size == 1);
+ assert(offset == 0);
+ AVRMaskState *s = opaque;
+
+ trace_avr_power_read(s->val);
+
+ return (uint64_t)s->val;
+}
+
+static void avr_mask_write(void *opaque, hwaddr offset,
+ uint64_t val64, unsigned size)
+{
+ assert(size == 1);
+ assert(offset == 0);
+ AVRMaskState *s = opaque;
+ uint8_t val8 = val64;
+
+ trace_avr_power_write(val8);
+ s->val = val8;
+ for (int i = 0; i < 8; i++) {
+ qemu_set_irq(s->irq[i], (val8 & (1 << i)) != 0);
+ }
+}
+
+static const MemoryRegionOps avr_mask_ops = {
+ .read = avr_mask_read,
+ .write = avr_mask_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {
+ .max_access_size = 1,
+ },
+};
+
+static void avr_mask_init(Object *dev)
+{
+ AVRMaskState *s = AVR_MASK(dev);
+ SysBusDevice *busdev = SYS_BUS_DEVICE(dev);
+
+ memory_region_init_io(&s->iomem, dev, &avr_mask_ops, s, TYPE_AVR_MASK,
+ 0x01);
+ sysbus_init_mmio(busdev, &s->iomem);
+
+ for (int i = 0; i < 8; i++) {
+ sysbus_init_irq(busdev, &s->irq[i]);
+ }
+ s->val = 0x00;
+}
+
+static void avr_mask_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = avr_mask_reset;
+}
+
+static const TypeInfo avr_mask_info = {
+ .name = TYPE_AVR_MASK,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AVRMaskState),
+ .class_init = avr_mask_class_init,
+ .instance_init = avr_mask_init,
+};
+
+static void avr_mask_register_types(void)
+{
+ type_register_static(&avr_mask_info);
+}
+
+type_init(avr_mask_register_types)
allwinner_sid_read(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32
allwinner_sid_write(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32
+# avr_power.c
+avr_power_read(uint8_t value) "power_reduc read value:%u"
+avr_power_write(uint8_t value) "power_reduc write value:%u"
+
# eccmemctl.c
ecc_mem_writel_mer(uint32_t val) "Write memory enable 0x%08x"
ecc_mem_writel_mdr(uint32_t val) "Write memory delay 0x%08x"
s->phydata = value & 0xffff;
break;
case FTGMAC100_DBLAC: /* DMA Burst Length and Arbitration Control */
- if (FTGMAC100_DBLAC_TXDES_SIZE(s->dblac) < sizeof(FTGMAC100Desc)) {
+ if (FTGMAC100_DBLAC_TXDES_SIZE(value) < sizeof(FTGMAC100Desc)) {
qemu_log_mask(LOG_GUEST_ERROR,
- "%s: transmit descriptor too small : %d bytes\n",
- __func__, FTGMAC100_DBLAC_TXDES_SIZE(s->dblac));
+ "%s: transmit descriptor too small: %" PRIx64
+ " bytes\n", __func__,
+ FTGMAC100_DBLAC_TXDES_SIZE(value));
break;
}
- if (FTGMAC100_DBLAC_RXDES_SIZE(s->dblac) < sizeof(FTGMAC100Desc)) {
+ if (FTGMAC100_DBLAC_RXDES_SIZE(value) < sizeof(FTGMAC100Desc)) {
qemu_log_mask(LOG_GUEST_ERROR,
- "%s: receive descriptor too small : %d bytes\n",
- __func__, FTGMAC100_DBLAC_RXDES_SIZE(s->dblac));
+ "%s: receive descriptor too small : %" PRIx64
+ " bytes\n", __func__,
+ FTGMAC100_DBLAC_RXDES_SIZE(value));
break;
}
s->dblac = value;
/* num of iovec without vhdr */
uint32_t iov_len = pkt->payload_frags + NET_TX_PKT_PL_START_FRAG - 1;
uint16_t csl;
- struct ip_header *iphdr;
size_t csum_offset = pkt->virt_hdr.csum_start + pkt->virt_hdr.csum_offset;
+ uint16_t l3_proto = eth_get_l3_proto(iov, 1, iov->iov_len);
/* Put zero to checksum field */
iov_from_buf(iov, iov_len, csum_offset, &csum, sizeof csum);
/* Calculate L4 TCP/UDP checksum */
csl = pkt->payload_len;
+ csum_cntr = 0;
+ cso = 0;
/* add pseudo header to csum */
- iphdr = pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base;
- csum_cntr = eth_calc_ip4_pseudo_hdr_csum(iphdr, csl, &cso);
+ if (l3_proto == ETH_P_IP) {
+ csum_cntr = eth_calc_ip4_pseudo_hdr_csum(
+ pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base,
+ csl, &cso);
+ } else if (l3_proto == ETH_P_IPV6) {
+ csum_cntr = eth_calc_ip6_pseudo_hdr_csum(
+ pkt->vec[NET_TX_PKT_L3HDR_FRAG].iov_base,
+ csl, pkt->l4proto, &cso);
+ }
/* data checksum */
csum_cntr +=
g_free(n->vlans);
if (n->failover) {
+ device_listener_unregister(&n->primary_listener);
g_free(n->primary_device_id);
g_free(n->standby_id);
qobject_unref(n->primary_device_dict);
void nios2_check_interrupts(CPUNios2State *env)
{
- if (env->irq_pending) {
+ if (env->irq_pending &&
+ (env->regs[CR_STATUS] & CR_STATUS_PIE)) {
env->irq_pending = 0;
cpu_interrupt(env_cpu(env), CPU_INTERRUPT_HARD);
}
#include "hw/boards.h"
#include "hw/loader.h"
#include "hw/riscv/boot.h"
+#include "hw/riscv/boot_opensbi.h"
#include "elf.h"
+#include "sysemu/device_tree.h"
#include "sysemu/qtest.h"
+#include <libfdt.h>
+
#if defined(TARGET_RISCV32)
# define KERNEL_BOOT_ADDRESS 0x80400000
+#define fw_dynamic_info_data(__val) cpu_to_le32(__val)
#else
# define KERNEL_BOOT_ADDRESS 0x80200000
+#define fw_dynamic_info_data(__val) cpu_to_le64(__val)
#endif
void riscv_find_and_load_firmware(MachineState *machine,
return *start + size;
}
+
+uint32_t riscv_load_fdt(hwaddr dram_base, uint64_t mem_size, void *fdt)
+{
+ uint32_t temp, fdt_addr;
+ hwaddr dram_end = dram_base + mem_size;
+ int fdtsize = fdt_totalsize(fdt);
+
+ if (fdtsize <= 0) {
+ error_report("invalid device-tree");
+ exit(1);
+ }
+
+ /*
+ * We should put fdt as far as possible to avoid kernel/initrd overwriting
+ * its content. But it should be addressable by 32 bit system as well.
+ * Thus, put it at an aligned address that less than fdt size from end of
+ * dram or 4GB whichever is lesser.
+ */
+ temp = MIN(dram_end, 4096 * MiB);
+ fdt_addr = QEMU_ALIGN_DOWN(temp - fdtsize, 2 * MiB);
+
+ fdt_pack(fdt);
+ /* copy in the device tree */
+ qemu_fdt_dumpdtb(fdt, fdtsize);
+
+ rom_add_blob_fixed_as("fdt", fdt, fdtsize, fdt_addr,
+ &address_space_memory);
+
+ return fdt_addr;
+}
+
+void riscv_rom_copy_firmware_info(hwaddr rom_base, hwaddr rom_size,
+ uint32_t reset_vec_size, uint64_t kernel_entry)
+{
+ struct fw_dynamic_info dinfo;
+ size_t dinfo_len;
+
+ dinfo.magic = fw_dynamic_info_data(FW_DYNAMIC_INFO_MAGIC_VALUE);
+ dinfo.version = fw_dynamic_info_data(FW_DYNAMIC_INFO_VERSION);
+ dinfo.next_mode = fw_dynamic_info_data(FW_DYNAMIC_INFO_NEXT_MODE_S);
+ dinfo.next_addr = fw_dynamic_info_data(kernel_entry);
+ dinfo.options = 0;
+ dinfo.boot_hart = 0;
+ dinfo_len = sizeof(dinfo);
+
+ /**
+ * copy the dynamic firmware info. This information is specific to
+ * OpenSBI but doesn't break any other firmware as long as they don't
+ * expect any certain value in "a2" register.
+ */
+ if (dinfo_len > (rom_size - reset_vec_size)) {
+ error_report("not enough space to store dynamic firmware info");
+ exit(1);
+ }
+
+ rom_add_blob_fixed_as("mrom.finfo", &dinfo, dinfo_len,
+ rom_base + reset_vec_size,
+ &address_space_memory);
+}
+
+void riscv_setup_rom_reset_vec(hwaddr start_addr, hwaddr rom_base,
+ hwaddr rom_size, uint64_t kernel_entry,
+ uint32_t fdt_load_addr, void *fdt)
+{
+ int i;
+ uint32_t start_addr_hi32 = 0x00000000;
+
+ #if defined(TARGET_RISCV64)
+ start_addr_hi32 = start_addr >> 32;
+ #endif
+ /* reset vector */
+ uint32_t reset_vec[10] = {
+ 0x00000297, /* 1: auipc t0, %pcrel_hi(fw_dyn) */
+ 0x02828613, /* addi a2, t0, %pcrel_lo(1b) */
+ 0xf1402573, /* csrr a0, mhartid */
+#if defined(TARGET_RISCV32)
+ 0x0202a583, /* lw a1, 32(t0) */
+ 0x0182a283, /* lw t0, 24(t0) */
+#elif defined(TARGET_RISCV64)
+ 0x0202b583, /* ld a1, 32(t0) */
+ 0x0182b283, /* ld t0, 24(t0) */
+#endif
+ 0x00028067, /* jr t0 */
+ start_addr, /* start: .dword */
+ start_addr_hi32,
+ fdt_load_addr, /* fdt_laddr: .dword */
+ 0x00000000,
+ /* fw_dyn: */
+ };
+
+ /* copy in the reset vector in little_endian byte order */
+ for (i = 0; i < ARRAY_SIZE(reset_vec); i++) {
+ reset_vec[i] = cpu_to_le32(reset_vec[i]);
+ }
+ rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec),
+ rom_base, &address_space_memory);
+ riscv_rom_copy_firmware_info(rom_base, rom_size, sizeof(reset_vec),
+ kernel_entry);
+
+ return;
+}
#include "sysemu/device_tree.h"
#include "sysemu/runstate.h"
#include "sysemu/sysemu.h"
-#include "exec/address-spaces.h"
#include <libfdt.h>
hwaddr size;
} sifive_u_memmap[] = {
[SIFIVE_U_DEBUG] = { 0x0, 0x100 },
- [SIFIVE_U_MROM] = { 0x1000, 0x11000 },
+ [SIFIVE_U_MROM] = { 0x1000, 0xf000 },
[SIFIVE_U_CLINT] = { 0x2000000, 0x10000 },
[SIFIVE_U_L2LIM] = { 0x8000000, 0x2000000 },
[SIFIVE_U_PLIC] = { 0xc000000, 0x4000000 },
MemoryRegion *main_mem = g_new(MemoryRegion, 1);
MemoryRegion *flash0 = g_new(MemoryRegion, 1);
target_ulong start_addr = memmap[SIFIVE_U_DRAM].base;
+ uint32_t start_addr_hi32 = 0x00000000;
int i;
+ uint32_t fdt_load_addr;
+ uint64_t kernel_entry;
/* Initialize SoC */
object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_RISCV_U_SOC);
riscv_find_and_load_firmware(machine, BIOS_FILENAME, start_addr, NULL);
if (machine->kernel_filename) {
- uint64_t kernel_entry = riscv_load_kernel(machine->kernel_filename,
- NULL);
+ kernel_entry = riscv_load_kernel(machine->kernel_filename, NULL);
if (machine->initrd_filename) {
hwaddr start;
qemu_fdt_setprop_cell(s->fdt, "/chosen", "linux,initrd-end",
end);
}
+ } else {
+ /*
+ * If dynamic firmware is used, it doesn't know where is the next mode
+ * if kernel argument is not set.
+ */
+ kernel_entry = 0;
}
+ /* Compute the fdt load address in dram */
+ fdt_load_addr = riscv_load_fdt(memmap[SIFIVE_U_DRAM].base,
+ machine->ram_size, s->fdt);
+ #if defined(TARGET_RISCV64)
+ start_addr_hi32 = start_addr >> 32;
+ #endif
+
/* reset vector */
- uint32_t reset_vec[8] = {
+ uint32_t reset_vec[11] = {
s->msel, /* MSEL pin state */
- 0x00000297, /* 1: auipc t0, %pcrel_hi(dtb) */
- 0x01c28593, /* addi a1, t0, %pcrel_lo(1b) */
+ 0x00000297, /* 1: auipc t0, %pcrel_hi(fw_dyn) */
+ 0x02828613, /* addi a2, t0, %pcrel_lo(1b) */
0xf1402573, /* csrr a0, mhartid */
#if defined(TARGET_RISCV32)
+ 0x0202a583, /* lw a1, 32(t0) */
0x0182a283, /* lw t0, 24(t0) */
#elif defined(TARGET_RISCV64)
- 0x0182e283, /* lwu t0, 24(t0) */
+ 0x0202b583, /* ld a1, 32(t0) */
+ 0x0182b283, /* ld t0, 24(t0) */
#endif
0x00028067, /* jr t0 */
- 0x00000000,
start_addr, /* start: .dword */
- /* dtb: */
+ start_addr_hi32,
+ fdt_load_addr, /* fdt_laddr: .dword */
+ 0x00000000,
+ /* fw_dyn: */
};
/* copy in the reset vector in little_endian byte order */
- for (i = 0; i < sizeof(reset_vec) >> 2; i++) {
+ for (i = 0; i < ARRAY_SIZE(reset_vec); i++) {
reset_vec[i] = cpu_to_le32(reset_vec[i]);
}
rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec),
memmap[SIFIVE_U_MROM].base, &address_space_memory);
- /* copy in the device tree */
- if (fdt_pack(s->fdt) || fdt_totalsize(s->fdt) >
- memmap[SIFIVE_U_MROM].size - sizeof(reset_vec)) {
- error_report("not enough space to store device-tree");
- exit(1);
- }
- qemu_fdt_dumpdtb(s->fdt, fdt_totalsize(s->fdt));
- rom_add_blob_fixed_as("mrom.fdt", s->fdt, fdt_totalsize(s->fdt),
- memmap[SIFIVE_U_MROM].base + sizeof(reset_vec),
- &address_space_memory);
+ riscv_rom_copy_firmware_info(memmap[SIFIVE_U_MROM].base,
+ memmap[SIFIVE_U_MROM].size,
+ sizeof(reset_vec), kernel_entry);
}
static bool sifive_u_machine_get_start_in_flash(Object *obj, Error **errp)
#include "sysemu/device_tree.h"
#include "sysemu/qtest.h"
#include "sysemu/sysemu.h"
-#include "exec/address-spaces.h"
-
-#include <libfdt.h>
#if defined(TARGET_RISCV32)
# define BIOS_FILENAME "opensbi-riscv32-spike-fw_jump.elf"
hwaddr base;
hwaddr size;
} spike_memmap[] = {
- [SPIKE_MROM] = { 0x1000, 0x11000 },
+ [SPIKE_MROM] = { 0x1000, 0xf000 },
[SPIKE_CLINT] = { 0x2000000, 0x10000 },
[SPIKE_DRAM] = { 0x80000000, 0x0 },
};
MemoryRegion *system_memory = get_system_memory();
MemoryRegion *main_mem = g_new(MemoryRegion, 1);
MemoryRegion *mask_rom = g_new(MemoryRegion, 1);
- int i;
unsigned int smp_cpus = machine->smp.cpus;
+ uint32_t fdt_load_addr;
+ uint64_t kernel_entry;
/* Initialize SOC */
object_initialize_child(OBJECT(machine), "soc", &s->soc,
htif_symbol_callback);
if (machine->kernel_filename) {
- uint64_t kernel_entry = riscv_load_kernel(machine->kernel_filename,
- htif_symbol_callback);
+ kernel_entry = riscv_load_kernel(machine->kernel_filename,
+ htif_symbol_callback);
if (machine->initrd_filename) {
hwaddr start;
qemu_fdt_setprop_cell(s->fdt, "/chosen", "linux,initrd-end",
end);
}
+ } else {
+ /*
+ * If dynamic firmware is used, it doesn't know where is the next mode
+ * if kernel argument is not set.
+ */
+ kernel_entry = 0;
}
- /* reset vector */
- uint32_t reset_vec[8] = {
- 0x00000297, /* 1: auipc t0, %pcrel_hi(dtb) */
- 0x02028593, /* addi a1, t0, %pcrel_lo(1b) */
- 0xf1402573, /* csrr a0, mhartid */
-#if defined(TARGET_RISCV32)
- 0x0182a283, /* lw t0, 24(t0) */
-#elif defined(TARGET_RISCV64)
- 0x0182b283, /* ld t0, 24(t0) */
-#endif
- 0x00028067, /* jr t0 */
- 0x00000000,
- memmap[SPIKE_DRAM].base, /* start: .dword DRAM_BASE */
- 0x00000000,
- /* dtb: */
- };
-
- /* copy in the reset vector in little_endian byte order */
- for (i = 0; i < sizeof(reset_vec) >> 2; i++) {
- reset_vec[i] = cpu_to_le32(reset_vec[i]);
- }
- rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec),
- memmap[SPIKE_MROM].base, &address_space_memory);
-
- /* copy in the device tree */
- if (fdt_pack(s->fdt) || fdt_totalsize(s->fdt) >
- memmap[SPIKE_MROM].size - sizeof(reset_vec)) {
- error_report("not enough space to store device-tree");
- exit(1);
- }
- qemu_fdt_dumpdtb(s->fdt, fdt_totalsize(s->fdt));
- rom_add_blob_fixed_as("mrom.fdt", s->fdt, fdt_totalsize(s->fdt),
- memmap[SPIKE_MROM].base + sizeof(reset_vec),
- &address_space_memory);
+ /* Compute the fdt load address in dram */
+ fdt_load_addr = riscv_load_fdt(memmap[SPIKE_DRAM].base,
+ machine->ram_size, s->fdt);
+ /* load the reset vector */
+ riscv_setup_rom_reset_vec(memmap[SPIKE_DRAM].base, memmap[SPIKE_MROM].base,
+ memmap[SPIKE_MROM].size, kernel_entry,
+ fdt_load_addr, s->fdt);
/* initialize HTIF using symbols found in load_kernel */
htif_mm_init(system_memory, mask_rom, &s->soc.harts[0].env, serial_hd(0));
#include "sysemu/arch_init.h"
#include "sysemu/device_tree.h"
#include "sysemu/sysemu.h"
-#include "exec/address-spaces.h"
#include "hw/pci/pci.h"
#include "hw/pci-host/gpex.h"
-#include <libfdt.h>
-
#if defined(TARGET_RISCV32)
# define BIOS_FILENAME "opensbi-riscv32-virt-fw_jump.bin"
#else
hwaddr size;
} virt_memmap[] = {
[VIRT_DEBUG] = { 0x0, 0x100 },
- [VIRT_MROM] = { 0x1000, 0x11000 },
+ [VIRT_MROM] = { 0x1000, 0xf000 },
[VIRT_TEST] = { 0x100000, 0x1000 },
[VIRT_RTC] = { 0x101000, 0x1000 },
[VIRT_CLINT] = { 0x2000000, 0x10000 },
+ [VIRT_PCIE_PIO] = { 0x3000000, 0x10000 },
[VIRT_PLIC] = { 0xc000000, 0x4000000 },
[VIRT_UART0] = { 0x10000000, 0x100 },
[VIRT_VIRTIO] = { 0x10001000, 0x1000 },
[VIRT_FLASH] = { 0x20000000, 0x4000000 },
- [VIRT_DRAM] = { 0x80000000, 0x0 },
- [VIRT_PCIE_MMIO] = { 0x40000000, 0x40000000 },
- [VIRT_PCIE_PIO] = { 0x03000000, 0x00010000 },
[VIRT_PCIE_ECAM] = { 0x30000000, 0x10000000 },
+ [VIRT_PCIE_MMIO] = { 0x40000000, 0x40000000 },
+ [VIRT_DRAM] = { 0x80000000, 0x0 },
};
#define VIRT_FLASH_SECTOR_SIZE (256 * KiB)
char *plic_hart_config;
size_t plic_hart_config_len;
target_ulong start_addr = memmap[VIRT_DRAM].base;
+ uint32_t fdt_load_addr;
+ uint64_t kernel_entry;
int i;
unsigned int smp_cpus = machine->smp.cpus;
memmap[VIRT_DRAM].base, NULL);
if (machine->kernel_filename) {
- uint64_t kernel_entry = riscv_load_kernel(machine->kernel_filename,
- NULL);
+ kernel_entry = riscv_load_kernel(machine->kernel_filename, NULL);
if (machine->initrd_filename) {
hwaddr start;
qemu_fdt_setprop_cell(s->fdt, "/chosen", "linux,initrd-end",
end);
}
+ } else {
+ /*
+ * If dynamic firmware is used, it doesn't know where is the next mode
+ * if kernel argument is not set.
+ */
+ kernel_entry = 0;
}
if (drive_get(IF_PFLASH, 0, 0)) {
start_addr = virt_memmap[VIRT_FLASH].base;
}
- /* reset vector */
- uint32_t reset_vec[8] = {
- 0x00000297, /* 1: auipc t0, %pcrel_hi(dtb) */
- 0x02028593, /* addi a1, t0, %pcrel_lo(1b) */
- 0xf1402573, /* csrr a0, mhartid */
-#if defined(TARGET_RISCV32)
- 0x0182a283, /* lw t0, 24(t0) */
-#elif defined(TARGET_RISCV64)
- 0x0182b283, /* ld t0, 24(t0) */
-#endif
- 0x00028067, /* jr t0 */
- 0x00000000,
- start_addr, /* start: .dword */
- 0x00000000,
- /* dtb: */
- };
-
- /* copy in the reset vector in little_endian byte order */
- for (i = 0; i < sizeof(reset_vec) >> 2; i++) {
- reset_vec[i] = cpu_to_le32(reset_vec[i]);
- }
- rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec),
- memmap[VIRT_MROM].base, &address_space_memory);
-
- /* copy in the device tree */
- if (fdt_pack(s->fdt) || fdt_totalsize(s->fdt) >
- memmap[VIRT_MROM].size - sizeof(reset_vec)) {
- error_report("not enough space to store device-tree");
- exit(1);
- }
- qemu_fdt_dumpdtb(s->fdt, fdt_totalsize(s->fdt));
- rom_add_blob_fixed_as("mrom.fdt", s->fdt, fdt_totalsize(s->fdt),
- memmap[VIRT_MROM].base + sizeof(reset_vec),
- &address_space_memory);
+ /* Compute the fdt load address in dram */
+ fdt_load_addr = riscv_load_fdt(memmap[VIRT_DRAM].base,
+ machine->ram_size, s->fdt);
+ /* load the reset vector */
+ riscv_setup_rom_reset_vec(start_addr, virt_memmap[VIRT_MROM].base,
+ virt_memmap[VIRT_MROM].size, kernel_entry,
+ fdt_load_addr, s->fdt);
/* create PLIC hart topology configuration string */
plic_hart_config_len = (strlen(VIRT_PLIC_HART_CONFIG) + 1) * smp_cpus;
#include "qemu/osdep.h"
#include "qemu/units.h"
+#include "qemu/cutils.h"
#include "hw/irq.h"
#include "hw/registerfields.h"
#include "sysemu/block-backend.h"
sd->multi_blk_cnt = 0;
}
+ if (sd_cmd_class[req.cmd] == 6 && FIELD_EX32(sd->ocr, OCR, CARD_CAPACITY)) {
+ /* Only Standard Capacity cards support class 6 commands */
+ return sd_illegal;
+ }
+
switch (req.cmd) {
/* Basic commands (Class 0 and Class 1) */
case 0: /* CMD0: GO_IDLE_STATE */
case 17: /* CMD17: READ_SINGLE_BLOCK */
switch (sd->state) {
case sd_transfer_state:
+
+ if (addr + sd->blk_len > sd->size) {
+ sd->card_status |= ADDRESS_ERROR;
+ return sd_r1;
+ }
+
sd->state = sd_sendingdata_state;
sd->data_start = addr;
sd->data_offset = 0;
-
- if (sd->data_start + sd->blk_len > sd->size)
- sd->card_status |= ADDRESS_ERROR;
return sd_r1;
default:
case 18: /* CMD18: READ_MULTIPLE_BLOCK */
switch (sd->state) {
case sd_transfer_state:
+
+ if (addr + sd->blk_len > sd->size) {
+ sd->card_status |= ADDRESS_ERROR;
+ return sd_r1;
+ }
+
sd->state = sd_sendingdata_state;
sd->data_start = addr;
sd->data_offset = 0;
-
- if (sd->data_start + sd->blk_len > sd->size)
- sd->card_status |= ADDRESS_ERROR;
return sd_r1;
default:
/* Writing in SPI mode not implemented. */
if (sd->spi)
break;
+
+ if (addr + sd->blk_len > sd->size) {
+ sd->card_status |= ADDRESS_ERROR;
+ return sd_r1;
+ }
+
sd->state = sd_receivingdata_state;
sd->data_start = addr;
sd->data_offset = 0;
sd->blk_written = 0;
- if (sd->data_start + sd->blk_len > sd->size)
- sd->card_status |= ADDRESS_ERROR;
- if (sd_wp_addr(sd, sd->data_start))
+ if (sd_wp_addr(sd, sd->data_start)) {
sd->card_status |= WP_VIOLATION;
- if (sd->csd[14] & 0x30)
+ }
+ if (sd->csd[14] & 0x30) {
sd->card_status |= WP_VIOLATION;
+ }
return sd_r1;
default:
/* Writing in SPI mode not implemented. */
if (sd->spi)
break;
+
+ if (addr + sd->blk_len > sd->size) {
+ sd->card_status |= ADDRESS_ERROR;
+ return sd_r1;
+ }
+
sd->state = sd_receivingdata_state;
sd->data_start = addr;
sd->data_offset = 0;
sd->blk_written = 0;
- if (sd->data_start + sd->blk_len > sd->size)
- sd->card_status |= ADDRESS_ERROR;
- if (sd_wp_addr(sd, sd->data_start))
+ if (sd_wp_addr(sd, sd->data_start)) {
sd->card_status |= WP_VIOLATION;
- if (sd->csd[14] & 0x30)
+ }
+ if (sd->csd[14] & 0x30) {
sd->card_status |= WP_VIOLATION;
+ }
return sd_r1;
default:
return;
}
- if (sd->blk && blk_is_read_only(sd->blk)) {
- error_setg(errp, "Cannot use read-only drive as SD card");
- return;
- }
-
if (sd->blk) {
+ int64_t blk_size;
+
+ if (blk_is_read_only(sd->blk)) {
+ error_setg(errp, "Cannot use read-only drive as SD card");
+ return;
+ }
+
+ blk_size = blk_getlength(sd->blk);
+ if (blk_size > 0 && !is_power_of_2(blk_size)) {
+ int64_t blk_size_aligned = pow2ceil(blk_size);
+ char *blk_size_str;
+
+ blk_size_str = size_to_str(blk_size);
+ error_setg(errp, "Invalid SD card size: %s", blk_size_str);
+ g_free(blk_size_str);
+
+ blk_size_str = size_to_str(blk_size_aligned);
+ error_append_hint(errp,
+ "SD card size has to be a power of 2, e.g. %s.\n"
+ "You can resize disk images with"
+ " 'qemu-img resize <imagefile> <new-size>'\n"
+ "(note that this will lose data if you make the"
+ " image smaller than it currently is).\n",
+ blk_size_str);
+ g_free(blk_size_str);
+
+ return;
+ }
+
ret = blk_set_perm(sd->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE,
BLK_PERM_ALL, errp);
if (ret < 0) {
config RENESAS_CMT
bool
+
+config AVR_TIMER16
+ bool
common-obj-$(CONFIG_CMSDK_APB_DUALTIMER) += cmsdk-apb-dualtimer.o
common-obj-$(CONFIG_MSF2) += mss-timer.o
common-obj-$(CONFIG_RASPI) += bcm2835_systmr.o
+
+obj-$(CONFIG_AVR_TIMER16) += avr_timer16.o
--- /dev/null
+/*
+ * AVR 16-bit timer
+ *
+ * Copyright (c) 2018 University of Kent
+ * Author: Ed Robbins
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+/*
+ * Driver for 16 bit timers on 8 bit AVR devices.
+ * Note:
+ * ATmega640/V-1280/V-1281/V-2560/V-2561/V timers 1, 3, 4 and 5 are 16 bit
+ */
+
+/*
+ * XXX TODO: Power Reduction Register support
+ * prescaler pause support
+ * PWM modes, GPIO, output capture pins, input compare pin
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "hw/timer/avr_timer16.h"
+#include "trace.h"
+
+/* Register offsets */
+#define T16_CRA 0x0
+#define T16_CRB 0x1
+#define T16_CRC 0x2
+#define T16_CNTL 0x4
+#define T16_CNTH 0x5
+#define T16_ICRL 0x6
+#define T16_ICRH 0x7
+#define T16_OCRAL 0x8
+#define T16_OCRAH 0x9
+#define T16_OCRBL 0xa
+#define T16_OCRBH 0xb
+#define T16_OCRCL 0xc
+#define T16_OCRCH 0xd
+
+/* Field masks */
+#define T16_CRA_WGM01 0x3
+#define T16_CRA_COMC 0xc
+#define T16_CRA_COMB 0x30
+#define T16_CRA_COMA 0xc0
+#define T16_CRA_OC_CONF \
+ (T16_CRA_COMA | T16_CRA_COMB | T16_CRA_COMC)
+
+#define T16_CRB_CS 0x7
+#define T16_CRB_WGM23 0x18
+#define T16_CRB_ICES 0x40
+#define T16_CRB_ICNC 0x80
+
+#define T16_CRC_FOCC 0x20
+#define T16_CRC_FOCB 0x40
+#define T16_CRC_FOCA 0x80
+
+/* Fields masks both TIMSK and TIFR (interrupt mask/flag registers) */
+#define T16_INT_TOV 0x1 /* Timer overflow */
+#define T16_INT_OCA 0x2 /* Output compare A */
+#define T16_INT_OCB 0x4 /* Output compare B */
+#define T16_INT_OCC 0x8 /* Output compare C */
+#define T16_INT_IC 0x20 /* Input capture */
+
+/* Clock source values */
+#define T16_CLKSRC_STOPPED 0
+#define T16_CLKSRC_DIV1 1
+#define T16_CLKSRC_DIV8 2
+#define T16_CLKSRC_DIV64 3
+#define T16_CLKSRC_DIV256 4
+#define T16_CLKSRC_DIV1024 5
+#define T16_CLKSRC_EXT_FALLING 6
+#define T16_CLKSRC_EXT_RISING 7
+
+/* Timer mode values (not including PWM modes) */
+#define T16_MODE_NORMAL 0
+#define T16_MODE_CTC_OCRA 4
+#define T16_MODE_CTC_ICR 12
+
+/* Accessors */
+#define CLKSRC(t16) (t16->crb & T16_CRB_CS)
+#define MODE(t16) (((t16->crb & T16_CRB_WGM23) >> 1) | \
+ (t16->cra & T16_CRA_WGM01))
+#define CNT(t16) VAL16(t16->cntl, t16->cnth)
+#define OCRA(t16) VAL16(t16->ocral, t16->ocrah)
+#define OCRB(t16) VAL16(t16->ocrbl, t16->ocrbh)
+#define OCRC(t16) VAL16(t16->ocrcl, t16->ocrch)
+#define ICR(t16) VAL16(t16->icrl, t16->icrh)
+
+/* Helper macros */
+#define VAL16(l, h) ((h << 8) | l)
+#define DB_PRINT(fmt, args...) /* Nothing */
+
+static inline int64_t avr_timer16_ns_to_ticks(AVRTimer16State *t16, int64_t t)
+{
+ if (t16->period_ns == 0) {
+ return 0;
+ }
+ return t / t16->period_ns;
+}
+
+static void avr_timer16_update_cnt(AVRTimer16State *t16)
+{
+ uint16_t cnt;
+ cnt = avr_timer16_ns_to_ticks(t16, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) -
+ t16->reset_time_ns);
+ t16->cntl = (uint8_t)(cnt & 0xff);
+ t16->cnth = (uint8_t)((cnt & 0xff00) >> 8);
+}
+
+static inline void avr_timer16_recalc_reset_time(AVRTimer16State *t16)
+{
+ t16->reset_time_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) -
+ CNT(t16) * t16->period_ns;
+}
+
+static void avr_timer16_clock_reset(AVRTimer16State *t16)
+{
+ t16->cntl = 0;
+ t16->cnth = 0;
+ t16->reset_time_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+}
+
+static void avr_timer16_clksrc_update(AVRTimer16State *t16)
+{
+ uint16_t divider = 0;
+ switch (CLKSRC(t16)) {
+ case T16_CLKSRC_EXT_FALLING:
+ case T16_CLKSRC_EXT_RISING:
+ qemu_log_mask(LOG_UNIMP, "%s: external clock source unsupported\n",
+ __func__);
+ break;
+ case T16_CLKSRC_STOPPED:
+ break;
+ case T16_CLKSRC_DIV1:
+ divider = 1;
+ break;
+ case T16_CLKSRC_DIV8:
+ divider = 8;
+ break;
+ case T16_CLKSRC_DIV64:
+ divider = 64;
+ break;
+ case T16_CLKSRC_DIV256:
+ divider = 256;
+ break;
+ case T16_CLKSRC_DIV1024:
+ divider = 1024;
+ break;
+ default:
+ break;
+ }
+ if (divider) {
+ t16->freq_hz = t16->cpu_freq_hz / divider;
+ t16->period_ns = NANOSECONDS_PER_SECOND / t16->freq_hz;
+ trace_avr_timer16_clksrc_update(t16->freq_hz, t16->period_ns,
+ (uint64_t)(1e6 / t16->freq_hz));
+ }
+}
+
+static void avr_timer16_set_alarm(AVRTimer16State *t16)
+{
+ if (CLKSRC(t16) == T16_CLKSRC_EXT_FALLING ||
+ CLKSRC(t16) == T16_CLKSRC_EXT_RISING ||
+ CLKSRC(t16) == T16_CLKSRC_STOPPED) {
+ /* Timer is disabled or set to external clock source (unsupported) */
+ return;
+ }
+
+ uint64_t alarm_offset = 0xffff;
+ enum NextInterrupt next_interrupt = OVERFLOW;
+
+ switch (MODE(t16)) {
+ case T16_MODE_NORMAL:
+ /* Normal mode */
+ if (OCRA(t16) < alarm_offset && OCRA(t16) > CNT(t16) &&
+ (t16->imsk & T16_INT_OCA)) {
+ alarm_offset = OCRA(t16);
+ next_interrupt = COMPA;
+ }
+ break;
+ case T16_MODE_CTC_OCRA:
+ /* CTC mode, top = ocra */
+ if (OCRA(t16) < alarm_offset && OCRA(t16) > CNT(t16)) {
+ alarm_offset = OCRA(t16);
+ next_interrupt = COMPA;
+ }
+ break;
+ case T16_MODE_CTC_ICR:
+ /* CTC mode, top = icr */
+ if (ICR(t16) < alarm_offset && ICR(t16) > CNT(t16)) {
+ alarm_offset = ICR(t16);
+ next_interrupt = CAPT;
+ }
+ if (OCRA(t16) < alarm_offset && OCRA(t16) > CNT(t16) &&
+ (t16->imsk & T16_INT_OCA)) {
+ alarm_offset = OCRA(t16);
+ next_interrupt = COMPA;
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: pwm modes are unsupported\n",
+ __func__);
+ return;
+ }
+ if (OCRB(t16) < alarm_offset && OCRB(t16) > CNT(t16) &&
+ (t16->imsk & T16_INT_OCB)) {
+ alarm_offset = OCRB(t16);
+ next_interrupt = COMPB;
+ }
+ if (OCRC(t16) < alarm_offset && OCRB(t16) > CNT(t16) &&
+ (t16->imsk & T16_INT_OCC)) {
+ alarm_offset = OCRB(t16);
+ next_interrupt = COMPC;
+ }
+ alarm_offset -= CNT(t16);
+
+ t16->next_interrupt = next_interrupt;
+ uint64_t alarm_ns =
+ t16->reset_time_ns + ((CNT(t16) + alarm_offset) * t16->period_ns);
+ timer_mod(t16->timer, alarm_ns);
+
+ trace_avr_timer16_next_alarm(alarm_offset * t16->period_ns);
+}
+
+static void avr_timer16_interrupt(void *opaque)
+{
+ AVRTimer16State *t16 = opaque;
+ uint8_t mode = MODE(t16);
+
+ avr_timer16_update_cnt(t16);
+
+ if (CLKSRC(t16) == T16_CLKSRC_EXT_FALLING ||
+ CLKSRC(t16) == T16_CLKSRC_EXT_RISING ||
+ CLKSRC(t16) == T16_CLKSRC_STOPPED) {
+ /* Timer is disabled or set to external clock source (unsupported) */
+ return;
+ }
+
+ trace_avr_timer16_interrupt_count(CNT(t16));
+
+ /* Counter overflow */
+ if (t16->next_interrupt == OVERFLOW) {
+ trace_avr_timer16_interrupt_overflow("counter 0xffff");
+ avr_timer16_clock_reset(t16);
+ if (t16->imsk & T16_INT_TOV) {
+ t16->ifr |= T16_INT_TOV;
+ qemu_set_irq(t16->ovf_irq, 1);
+ }
+ }
+ /* Check for ocra overflow in CTC mode */
+ if (mode == T16_MODE_CTC_OCRA && t16->next_interrupt == COMPA) {
+ trace_avr_timer16_interrupt_overflow("CTC OCRA");
+ avr_timer16_clock_reset(t16);
+ }
+ /* Check for icr overflow in CTC mode */
+ if (mode == T16_MODE_CTC_ICR && t16->next_interrupt == CAPT) {
+ trace_avr_timer16_interrupt_overflow("CTC ICR");
+ avr_timer16_clock_reset(t16);
+ if (t16->imsk & T16_INT_IC) {
+ t16->ifr |= T16_INT_IC;
+ qemu_set_irq(t16->capt_irq, 1);
+ }
+ }
+ /* Check for output compare interrupts */
+ if (t16->imsk & T16_INT_OCA && t16->next_interrupt == COMPA) {
+ t16->ifr |= T16_INT_OCA;
+ qemu_set_irq(t16->compa_irq, 1);
+ }
+ if (t16->imsk & T16_INT_OCB && t16->next_interrupt == COMPB) {
+ t16->ifr |= T16_INT_OCB;
+ qemu_set_irq(t16->compb_irq, 1);
+ }
+ if (t16->imsk & T16_INT_OCC && t16->next_interrupt == COMPC) {
+ t16->ifr |= T16_INT_OCC;
+ qemu_set_irq(t16->compc_irq, 1);
+ }
+ avr_timer16_set_alarm(t16);
+}
+
+static void avr_timer16_reset(DeviceState *dev)
+{
+ AVRTimer16State *t16 = AVR_TIMER16(dev);
+
+ avr_timer16_clock_reset(t16);
+ avr_timer16_clksrc_update(t16);
+ avr_timer16_set_alarm(t16);
+
+ qemu_set_irq(t16->capt_irq, 0);
+ qemu_set_irq(t16->compa_irq, 0);
+ qemu_set_irq(t16->compb_irq, 0);
+ qemu_set_irq(t16->compc_irq, 0);
+ qemu_set_irq(t16->ovf_irq, 0);
+}
+
+static uint64_t avr_timer16_read(void *opaque, hwaddr offset, unsigned size)
+{
+ assert(size == 1);
+ AVRTimer16State *t16 = opaque;
+ uint8_t retval = 0;
+
+ switch (offset) {
+ case T16_CRA:
+ retval = t16->cra;
+ break;
+ case T16_CRB:
+ retval = t16->crb;
+ break;
+ case T16_CRC:
+ retval = t16->crc;
+ break;
+ case T16_CNTL:
+ avr_timer16_update_cnt(t16);
+ t16->rtmp = t16->cnth;
+ retval = t16->cntl;
+ break;
+ case T16_CNTH:
+ retval = t16->rtmp;
+ break;
+ case T16_ICRL:
+ /*
+ * The timer copies cnt to icr when the input capture pin changes
+ * state or when the analog comparator has a match. We don't
+ * emulate this behaviour. We do support it's use for defining a
+ * TOP value in T16_MODE_CTC_ICR
+ */
+ t16->rtmp = t16->icrh;
+ retval = t16->icrl;
+ break;
+ case T16_ICRH:
+ retval = t16->rtmp;
+ break;
+ case T16_OCRAL:
+ retval = t16->ocral;
+ break;
+ case T16_OCRAH:
+ retval = t16->ocrah;
+ break;
+ case T16_OCRBL:
+ retval = t16->ocrbl;
+ break;
+ case T16_OCRBH:
+ retval = t16->ocrbh;
+ break;
+ case T16_OCRCL:
+ retval = t16->ocrcl;
+ break;
+ case T16_OCRCH:
+ retval = t16->ocrch;
+ break;
+ default:
+ break;
+ }
+ trace_avr_timer16_read(offset, retval);
+
+ return (uint64_t)retval;
+}
+
+static void avr_timer16_write(void *opaque, hwaddr offset,
+ uint64_t val64, unsigned size)
+{
+ assert(size == 1);
+ AVRTimer16State *t16 = opaque;
+ uint8_t val8 = (uint8_t)val64;
+ uint8_t prev_clk_src = CLKSRC(t16);
+
+ trace_avr_timer16_write(offset, val8);
+
+ switch (offset) {
+ case T16_CRA:
+ t16->cra = val8;
+ if (t16->cra & T16_CRA_OC_CONF) {
+ qemu_log_mask(LOG_UNIMP, "%s: output compare pins unsupported\n",
+ __func__);
+ }
+ break;
+ case T16_CRB:
+ t16->crb = val8;
+ if (t16->crb & T16_CRB_ICNC) {
+ qemu_log_mask(LOG_UNIMP,
+ "%s: input capture noise canceller unsupported\n",
+ __func__);
+ }
+ if (t16->crb & T16_CRB_ICES) {
+ qemu_log_mask(LOG_UNIMP, "%s: input capture unsupported\n",
+ __func__);
+ }
+ if (CLKSRC(t16) != prev_clk_src) {
+ avr_timer16_clksrc_update(t16);
+ if (prev_clk_src == T16_CLKSRC_STOPPED) {
+ t16->reset_time_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ }
+ }
+ break;
+ case T16_CRC:
+ t16->crc = val8;
+ qemu_log_mask(LOG_UNIMP, "%s: output compare pins unsupported\n",
+ __func__);
+ break;
+ case T16_CNTL:
+ /*
+ * CNT is the 16-bit counter value, it must be read/written via
+ * a temporary register (rtmp) to make the read/write atomic.
+ */
+ /* ICR also has this behaviour, and shares rtmp */
+ /*
+ * Writing CNT blocks compare matches for one clock cycle.
+ * Writing CNT to TOP or to an OCR value (if in use) will
+ * skip the relevant interrupt
+ */
+ t16->cntl = val8;
+ t16->cnth = t16->rtmp;
+ avr_timer16_recalc_reset_time(t16);
+ break;
+ case T16_CNTH:
+ t16->rtmp = val8;
+ break;
+ case T16_ICRL:
+ /* ICR can only be written in mode T16_MODE_CTC_ICR */
+ if (MODE(t16) == T16_MODE_CTC_ICR) {
+ t16->icrl = val8;
+ t16->icrh = t16->rtmp;
+ }
+ break;
+ case T16_ICRH:
+ if (MODE(t16) == T16_MODE_CTC_ICR) {
+ t16->rtmp = val8;
+ }
+ break;
+ case T16_OCRAL:
+ /*
+ * OCRn cause the relevant output compare flag to be raised, and
+ * trigger an interrupt, when CNT is equal to the value here
+ */
+ t16->ocral = val8;
+ break;
+ case T16_OCRAH:
+ t16->ocrah = val8;
+ break;
+ case T16_OCRBL:
+ t16->ocrbl = val8;
+ break;
+ case T16_OCRBH:
+ t16->ocrbh = val8;
+ break;
+ case T16_OCRCL:
+ t16->ocrcl = val8;
+ break;
+ case T16_OCRCH:
+ t16->ocrch = val8;
+ break;
+ default:
+ break;
+ }
+ avr_timer16_set_alarm(t16);
+}
+
+static uint64_t avr_timer16_imsk_read(void *opaque,
+ hwaddr offset,
+ unsigned size)
+{
+ assert(size == 1);
+ AVRTimer16State *t16 = opaque;
+ trace_avr_timer16_read_imsk(offset ? 0 : t16->imsk);
+ if (offset != 0) {
+ return 0;
+ }
+ return t16->imsk;
+}
+
+static void avr_timer16_imsk_write(void *opaque, hwaddr offset,
+ uint64_t val64, unsigned size)
+{
+ assert(size == 1);
+ AVRTimer16State *t16 = opaque;
+ trace_avr_timer16_write_imsk(val64);
+ if (offset != 0) {
+ return;
+ }
+ t16->imsk = (uint8_t)val64;
+}
+
+static uint64_t avr_timer16_ifr_read(void *opaque,
+ hwaddr offset,
+ unsigned size)
+{
+ assert(size == 1);
+ AVRTimer16State *t16 = opaque;
+ trace_avr_timer16_read_ifr(offset ? 0 : t16->ifr);
+ if (offset != 0) {
+ return 0;
+ }
+ return t16->ifr;
+}
+
+static void avr_timer16_ifr_write(void *opaque, hwaddr offset,
+ uint64_t val64, unsigned size)
+{
+ assert(size == 1);
+ AVRTimer16State *t16 = opaque;
+ trace_avr_timer16_write_imsk(val64);
+ if (offset != 0) {
+ return;
+ }
+ t16->ifr = (uint8_t)val64;
+}
+
+static const MemoryRegionOps avr_timer16_ops = {
+ .read = avr_timer16_read,
+ .write = avr_timer16_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {.max_access_size = 1}
+};
+
+static const MemoryRegionOps avr_timer16_imsk_ops = {
+ .read = avr_timer16_imsk_read,
+ .write = avr_timer16_imsk_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {.max_access_size = 1}
+};
+
+static const MemoryRegionOps avr_timer16_ifr_ops = {
+ .read = avr_timer16_ifr_read,
+ .write = avr_timer16_ifr_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {.max_access_size = 1}
+};
+
+static Property avr_timer16_properties[] = {
+ DEFINE_PROP_UINT8("id", struct AVRTimer16State, id, 0),
+ DEFINE_PROP_UINT64("cpu-frequency-hz", struct AVRTimer16State,
+ cpu_freq_hz, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void avr_timer16_pr(void *opaque, int irq, int level)
+{
+ AVRTimer16State *s = AVR_TIMER16(opaque);
+
+ s->enabled = !level;
+
+ if (!s->enabled) {
+ avr_timer16_reset(DEVICE(s));
+ }
+}
+
+static void avr_timer16_init(Object *obj)
+{
+ AVRTimer16State *s = AVR_TIMER16(obj);
+
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->capt_irq);
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->compa_irq);
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->compb_irq);
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->compc_irq);
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->ovf_irq);
+
+ memory_region_init_io(&s->iomem, obj, &avr_timer16_ops,
+ s, "avr-timer16", 0xe);
+ memory_region_init_io(&s->imsk_iomem, obj, &avr_timer16_imsk_ops,
+ s, "avr-timer16-intmask", 0x1);
+ memory_region_init_io(&s->ifr_iomem, obj, &avr_timer16_ifr_ops,
+ s, "avr-timer16-intflag", 0x1);
+
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->imsk_iomem);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->ifr_iomem);
+ qdev_init_gpio_in(DEVICE(s), avr_timer16_pr, 1);
+}
+
+static void avr_timer16_realize(DeviceState *dev, Error **errp)
+{
+ AVRTimer16State *s = AVR_TIMER16(dev);
+
+ if (s->cpu_freq_hz == 0) {
+ error_setg(errp, "AVR timer16: cpu-frequency-hz property must be set");
+ return;
+ }
+
+ s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, avr_timer16_interrupt, s);
+ s->enabled = true;
+}
+
+static void avr_timer16_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = avr_timer16_reset;
+ dc->realize = avr_timer16_realize;
+ device_class_set_props(dc, avr_timer16_properties);
+}
+
+static const TypeInfo avr_timer16_info = {
+ .name = TYPE_AVR_TIMER16,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AVRTimer16State),
+ .instance_init = avr_timer16_init,
+ .class_init = avr_timer16_class_init,
+};
+
+static void avr_timer16_register_types(void)
+{
+ type_register_static(&avr_timer16_info);
+}
+
+type_init(avr_timer16_register_types)
bcm2835_systmr_irq(bool enable) "timer irq state %u"
bcm2835_systmr_read(uint64_t offset, uint64_t data) "timer read: offset 0x%" PRIx64 " data 0x%" PRIx64
bcm2835_systmr_write(uint64_t offset, uint64_t data) "timer write: offset 0x%" PRIx64 " data 0x%" PRIx64
+
+# avr_timer16.c
+avr_timer16_read(uint8_t addr, uint8_t value) "timer16 read addr:%u value:%u"
+avr_timer16_read_ifr(uint8_t value) "timer16 read addr:ifr value:%u"
+avr_timer16_read_imsk(uint8_t value) "timer16 read addr:imsk value:%u"
+avr_timer16_write(uint8_t addr, uint8_t value) "timer16 write addr:%u value:%u"
+avr_timer16_write_ifr(uint8_t value) "timer16 write addr:ifr value:%u"
+avr_timer16_write_imsk(uint8_t value) "timer16 write addr:imsk value:%u"
+avr_timer16_interrupt_count(uint8_t cnt) "count: %u"
+avr_timer16_interrupt_overflow(const char *reason) "overflow: %s"
+avr_timer16_next_alarm(uint64_t delay_ns) "next alarm: %" PRIu64 " ns from now"
+avr_timer16_clksrc_update(uint64_t freq_hz, uint64_t period_ns, uint64_t delay_s) "timer frequency: %" PRIu64 " Hz, period: %" PRIu64 " ns (%" PRId64 " us)"
TPM_SPAPR_BUFFER_MAX);
tpm_backend_reset(s->be_driver);
- tpm_spapr_do_startup_tpm(s, s->be_buffer_size);
+
+ if (tpm_spapr_do_startup_tpm(s, s->be_buffer_size) < 0) {
+ exit(1);
+ }
}
static enum TPMVersion tpm_spapr_get_version(TPMIf *ti)
}
}
}
- if (p->actual_length < p->iov.size && (p->short_not_ok ||
- s->scsi_len >= p->ep->max_packet_size)) {
+ if (p->actual_length < p->iov.size && s->mode == USB_MSDM_DATAIN) {
DPRINTF("Deferring packet %p [wait data-in]\n", p);
s->packet = p;
p->status = USB_RET_ASYNC;
goto fail;
}
} else {
-#if LIBUSB_API_VERSION >= 0x01000107
+#if LIBUSB_API_VERSION >= 0x01000107 && !defined(CONFIG_WIN32)
trace_usb_host_open_hostfd(hostfd);
rc = libusb_wrap_sys_device(ctx, hostfd, &s->dh);
QTAILQ_INIT(&s->isorings);
s->hostfd = -1;
-#if LIBUSB_API_VERSION >= 0x01000107
+#if LIBUSB_API_VERSION >= 0x01000107 && !defined(CONFIG_WIN32)
if (s->hostdevice) {
int fd;
s->needs_autoscan = false;
if (!plane.drm_format || !plane.size) {
if (dpy->ramfb) {
ramfb_display_update(dpy->con, dpy->ramfb);
+ dpy->region.surface = NULL;
}
return;
}
#include "hw/virtio/virtio-net.h"
#include "hw/virtio/vhost-vdpa.h"
#include "qemu/main-loop.h"
-#include <linux/kvm.h>
-#include "sysemu/kvm.h"
+#include "cpu.h"
static bool vhost_vdpa_listener_skipped_section(MemoryRegionSection *section)
{
ptail = (struct virtio_iommu_req_tail *)
(buf + s->config.probe_size);
ptail->status = virtio_iommu_handle_probe(s, iov, iov_cnt, buf);
+ break;
}
default:
tail.status = VIRTIO_IOMMU_S_UNSUPP;
*/
#include "qemu/osdep.h"
-#include <sys/signal.h>
#include "hw/sysbus.h"
#include "hw/boards.h"
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
/* xen-backend devices can be plugged/unplugged dynamically */
dc->user_creatable = true;
+ dc->bus_type = TYPE_XENSYSBUS;
}
static const TypeInfo xendev_type_info = {
.name = TYPE_XENBACKEND,
- .parent = TYPE_XENSYSDEV,
+ .parent = TYPE_DEVICE,
.class_init = xendev_class_init,
.instance_size = sizeof(struct XenLegacyDevice),
};
DeviceClass *dc = DEVICE_CLASS(klass);
device_class_set_props(dc, xen_sysdev_properties);
- dc->bus_type = TYPE_XENSYSBUS;
}
static const TypeInfo xensysdev_info = {
void bdrv_refresh_limits(BlockDriverState *bs, Error **errp);
int bdrv_commit(BlockDriverState *bs);
int bdrv_make_empty(BdrvChild *c, Error **errp);
-int bdrv_change_backing_file(BlockDriverState *bs,
- const char *backing_file, const char *backing_fmt);
+int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file,
+ const char *backing_fmt, bool warn);
void bdrv_register(BlockDriver *bdrv);
int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
const char *backing_file_str);
#define BLOCK_OPT_ADAPTER_TYPE "adapter_type"
#define BLOCK_OPT_REDUNDANCY "redundancy"
#define BLOCK_OPT_NOCOW "nocow"
+#define BLOCK_OPT_EXTENT_SIZE_HINT "extent_size_hint"
#define BLOCK_OPT_OBJECT_SIZE "object_size"
#define BLOCK_OPT_REFCOUNT_BITS "refcount_bits"
#define BLOCK_OPT_DATA_FILE "data_file"
+++ /dev/null
-/*
- * QEMU System Emulator
- *
- * Copyright (c) 2003-2008 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 CHAR_MUX_H
-#define CHAR_MUX_H
-
-#include "chardev/char.h"
-#include "chardev/char-fe.h"
-
-#define MAX_MUX 4
-#define MUX_BUFFER_SIZE 32 /* Must be a power of 2. */
-#define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1)
-typedef struct MuxChardev {
- Chardev parent;
- CharBackend *backends[MAX_MUX];
- CharBackend chr;
- int focus;
- int mux_cnt;
- int term_got_escape;
- int max_size;
- /* Intermediate input buffer catches escape sequences even if the
- currently active device is not accepting any input - but only until it
- is full as well. */
- unsigned char buffer[MAX_MUX][MUX_BUFFER_SIZE];
- int prod[MAX_MUX];
- int cons[MAX_MUX];
- int timestamps;
-
- /* Protected by the Chardev chr_write_lock. */
- int linestart;
- int64_t timestamps_start;
-} MuxChardev;
-
-#define MUX_CHARDEV(obj) OBJECT_CHECK(MuxChardev, (obj), TYPE_CHARDEV_MUX)
-#define CHARDEV_IS_MUX(chr) \
- object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_MUX)
-
-void mux_set_focus(Chardev *chr, int focus);
-void mux_chr_send_all_event(Chardev *chr, QEMUChrEvent event);
-
-#endif /* CHAR_MUX_H */
#define bfd_mach_m32r 0 /* backwards compatibility */
bfd_arch_mn10200, /* Matsushita MN10200 */
bfd_arch_mn10300, /* Matsushita MN10300 */
+ bfd_arch_avr, /* AVR microcontrollers */
+#define bfd_mach_avr1 1
+#define bfd_mach_avr2 2
+#define bfd_mach_avr25 25
+#define bfd_mach_avr3 3
+#define bfd_mach_avr31 31
+#define bfd_mach_avr35 35
+#define bfd_mach_avr4 4
+#define bfd_mach_avr5 5
+#define bfd_mach_avr51 51
+#define bfd_mach_avr6 6
+#define bfd_mach_avrtiny 100
+#define bfd_mach_avrxmega1 101
+#define bfd_mach_avrxmega2 102
+#define bfd_mach_avrxmega3 103
+#define bfd_mach_avrxmega4 104
+#define bfd_mach_avrxmega5 105
+#define bfd_mach_avrxmega6 106
+#define bfd_mach_avrxmega7 107
bfd_arch_cris, /* Axis CRIS */
#define bfd_mach_cris_v0_v10 255
#define bfd_mach_cris_v32 32
#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */
+#define EM_AVR 83 /* AVR 8-bit microcontroller */
+
#define EM_V850 87 /* NEC v850 */
#define EM_H8_300H 47 /* Hitachi H8/300H */
#define EM_MOXIE 223 /* Moxie processor family */
#define EM_MOXIE_OLD 0xFEED
+#define EF_AVR_MACH 0x7F /* Mask for AVR e_flags to get core type */
+
/* This is the info that is needed to parse the dynamic section of the file */
#define DT_NULL 0
#define DT_NEEDED 1
DeviceState parent_obj;
/*< public >*/
- char *cpu_type;
uint32_t enabled_cpus;
struct {
MemoryRegion rom[2];
MemoryRegion iram;
MemoryRegion iram_alias;
+ uint32_t phy_num;
} FslIMX25State;
/**
MemoryRegion caam;
MemoryRegion ocram;
MemoryRegion ocram_alias;
+ uint32_t phy_num;
} FslIMX6State;
IMX7GPRState gpr;
ChipideaState usb[FSL_IMX7_NUM_USBS];
DesignwarePCIEHost pcie;
+ uint32_t phy_num[FSL_IMX7_NUM_ETHS];
} FslIMX7State;
enum FslIMX7MemoryMap {
--- /dev/null
+/*
+ * AVR USART
+ *
+ * Copyright (c) 2018 University of Kent
+ * Author: Sarah Harris
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#ifndef HW_CHAR_AVR_USART_H
+#define HW_CHAR_AVR_USART_H
+
+#include "hw/sysbus.h"
+#include "chardev/char-fe.h"
+#include "hw/hw.h"
+
+/* Offsets of registers. */
+#define USART_DR 0x06
+#define USART_CSRA 0x00
+#define USART_CSRB 0x01
+#define USART_CSRC 0x02
+#define USART_BRRH 0x05
+#define USART_BRRL 0x04
+
+/* Relevant bits in regiters. */
+#define USART_CSRA_RXC (1 << 7)
+#define USART_CSRA_TXC (1 << 6)
+#define USART_CSRA_DRE (1 << 5)
+#define USART_CSRA_MPCM (1 << 0)
+
+#define USART_CSRB_RXCIE (1 << 7)
+#define USART_CSRB_TXCIE (1 << 6)
+#define USART_CSRB_DREIE (1 << 5)
+#define USART_CSRB_RXEN (1 << 4)
+#define USART_CSRB_TXEN (1 << 3)
+#define USART_CSRB_CSZ2 (1 << 2)
+#define USART_CSRB_RXB8 (1 << 1)
+#define USART_CSRB_TXB8 (1 << 0)
+
+#define USART_CSRC_MSEL1 (1 << 7)
+#define USART_CSRC_MSEL0 (1 << 6)
+#define USART_CSRC_PM1 (1 << 5)
+#define USART_CSRC_PM0 (1 << 4)
+#define USART_CSRC_CSZ1 (1 << 2)
+#define USART_CSRC_CSZ0 (1 << 1)
+
+#define TYPE_AVR_USART "avr-usart"
+#define AVR_USART(obj) \
+ OBJECT_CHECK(AVRUsartState, (obj), TYPE_AVR_USART)
+
+typedef struct {
+ /* <private> */
+ SysBusDevice parent_obj;
+
+ /* <public> */
+ MemoryRegion mmio;
+
+ CharBackend chr;
+
+ bool enabled;
+
+ uint8_t data;
+ bool data_valid;
+ uint8_t char_mask;
+ /* Control and Status Registers */
+ uint8_t csra;
+ uint8_t csrb;
+ uint8_t csrc;
+ /* Baud Rate Registers (low/high byte) */
+ uint8_t brrh;
+ uint8_t brrl;
+
+ /* Receive Complete */
+ qemu_irq rxc_irq;
+ /* Transmit Complete */
+ qemu_irq txc_irq;
+ /* Data Register Empty */
+ qemu_irq dre_irq;
+} AVRUsartState;
+
+#endif /* HW_CHAR_AVR_USART_H */
#define HW_IBEX_UART_H
#include "hw/sysbus.h"
+#include "hw/registerfields.h"
#include "chardev/char-fe.h"
#include "qemu/timer.h"
-#define IBEX_UART_INTR_STATE 0x00
- #define INTR_STATE_TX_WATERMARK (1 << 0)
- #define INTR_STATE_RX_WATERMARK (1 << 1)
- #define INTR_STATE_TX_EMPTY (1 << 2)
- #define INTR_STATE_RX_OVERFLOW (1 << 3)
-#define IBEX_UART_INTR_ENABLE 0x04
-#define IBEX_UART_INTR_TEST 0x08
-
-#define IBEX_UART_CTRL 0x0c
- #define UART_CTRL_TX_ENABLE (1 << 0)
- #define UART_CTRL_RX_ENABLE (1 << 1)
- #define UART_CTRL_NF (1 << 2)
- #define UART_CTRL_SLPBK (1 << 4)
- #define UART_CTRL_LLPBK (1 << 5)
- #define UART_CTRL_PARITY_EN (1 << 6)
- #define UART_CTRL_PARITY_ODD (1 << 7)
- #define UART_CTRL_RXBLVL (3 << 8)
- #define UART_CTRL_NCO (0xFFFF << 16)
-
-#define IBEX_UART_STATUS 0x10
- #define UART_STATUS_TXFULL (1 << 0)
- #define UART_STATUS_RXFULL (1 << 1)
- #define UART_STATUS_TXEMPTY (1 << 2)
- #define UART_STATUS_RXIDLE (1 << 4)
- #define UART_STATUS_RXEMPTY (1 << 5)
-
-#define IBEX_UART_RDATA 0x14
-#define IBEX_UART_WDATA 0x18
-
-#define IBEX_UART_FIFO_CTRL 0x1c
- #define FIFO_CTRL_RXRST (1 << 0)
- #define FIFO_CTRL_TXRST (1 << 1)
- #define FIFO_CTRL_RXILVL (7 << 2)
- #define FIFO_CTRL_RXILVL_SHIFT (2)
- #define FIFO_CTRL_TXILVL (3 << 5)
- #define FIFO_CTRL_TXILVL_SHIFT (5)
-
-#define IBEX_UART_FIFO_STATUS 0x20
-#define IBEX_UART_OVRD 0x24
-#define IBEX_UART_VAL 0x28
-#define IBEX_UART_TIMEOUT_CTRL 0x2c
+REG32(INTR_STATE, 0x00)
+ FIELD(INTR_STATE, TX_WATERMARK, 0, 1)
+ FIELD(INTR_STATE, RX_WATERMARK, 1, 1)
+ FIELD(INTR_STATE, TX_EMPTY, 2, 1)
+ FIELD(INTR_STATE, RX_OVERFLOW, 3, 1)
+REG32(INTR_ENABLE, 0x04)
+REG32(INTR_TEST, 0x08)
+REG32(CTRL, 0x0C)
+ FIELD(CTRL, TX_ENABLE, 0, 1)
+ FIELD(CTRL, RX_ENABLE, 1, 1)
+ FIELD(CTRL, NF, 2, 1)
+ FIELD(CTRL, SLPBK, 4, 1)
+ FIELD(CTRL, LLPBK, 5, 1)
+ FIELD(CTRL, PARITY_EN, 6, 1)
+ FIELD(CTRL, PARITY_ODD, 7, 1)
+ FIELD(CTRL, RXBLVL, 8, 2)
+ FIELD(CTRL, NCO, 16, 16)
+REG32(STATUS, 0x10)
+ FIELD(STATUS, TXFULL, 0, 1)
+ FIELD(STATUS, RXFULL, 1, 1)
+ FIELD(STATUS, TXEMPTY, 2, 1)
+ FIELD(STATUS, RXIDLE, 4, 1)
+ FIELD(STATUS, RXEMPTY, 5, 1)
+REG32(RDATA, 0x14)
+REG32(WDATA, 0x18)
+REG32(FIFO_CTRL, 0x1c)
+ FIELD(FIFO_CTRL, RXRST, 0, 1)
+ FIELD(FIFO_CTRL, TXRST, 1, 1)
+ FIELD(FIFO_CTRL, RXILVL, 2, 3)
+ FIELD(FIFO_CTRL, TXILVL, 5, 2)
+REG32(FIFO_STATUS, 0x20)
+REG32(OVRD, 0x24)
+REG32(VAL, 0x28)
+REG32(TIMEOUT_CTRL, 0x2c)
#define IBEX_UART_TX_FIFO_SIZE 16
+#define IBEX_UART_CLOCK 50000000 /* 50MHz clock */
#define TYPE_IBEX_UART "ibex-uart"
#define IBEX_UART(obj) \
uint32_t uart_val;
uint32_t uart_timeout_ctrl;
+ Clock *f_clk;
+
CharBackend chr;
qemu_irq tx_watermark;
qemu_irq rx_watermark;
QTAILQ_ENTRY(CPUWatchpoint) entry;
};
+#ifdef CONFIG_PLUGIN
+/*
+ * For plugins we sometime need to save the resolved iotlb data before
+ * the memory regions get moved around by io_writex.
+ */
+typedef struct SavedIOTLB {
+ hwaddr addr;
+ MemoryRegionSection *section;
+ hwaddr mr_offset;
+} SavedIOTLB;
+#endif
+
struct KVMState;
struct kvm_run;
DECLARE_BITMAP(plugin_mask, QEMU_PLUGIN_EV_MAX);
+#ifdef CONFIG_PLUGIN
GArray *plugin_mem_cbs;
+ /* saved iotlb data from io_writex */
+ SavedIOTLB saved_iotlb;
+#endif
/* TODO Move common fields from CPUArchState here. */
int cpu_index;
*/
CPUState *cpu_by_arch_id(int64_t id);
-/**
- * cpu_throttle_set:
- * @new_throttle_pct: Percent of sleep time. Valid range is 1 to 99.
- *
- * Throttles all vcpus by forcing them to sleep for the given percentage of
- * time. A throttle_percentage of 25 corresponds to a 75% duty cycle roughly.
- * (example: 10ms sleep for every 30ms awake).
- *
- * cpu_throttle_set can be called as needed to adjust new_throttle_pct.
- * Once the throttling starts, it will remain in effect until cpu_throttle_stop
- * is called.
- */
-void cpu_throttle_set(int new_throttle_pct);
-
-/**
- * cpu_throttle_stop:
- *
- * Stops the vcpu throttling started by cpu_throttle_set.
- */
-void cpu_throttle_stop(void);
-
-/**
- * cpu_throttle_active:
- *
- * Returns: %true if the vcpus are currently being throttled, %false otherwise.
- */
-bool cpu_throttle_active(void);
-
-/**
- * cpu_throttle_get_percentage:
- *
- * Returns the vcpu throttle percentage. See cpu_throttle_set for details.
- *
- * Returns: The throttle percentage in range 1 to 99.
- */
-int cpu_throttle_get_percentage(void);
-
#ifndef CONFIG_USER_ONLY
typedef void (*CPUInterruptHandler)(CPUState *, int);
TPRAccess access);
int apic_get_ppr(APICCommonState *s);
+uint32_t apic_get_current_count(APICCommonState *s);
static inline void apic_set_bit(uint32_t *tab, int index)
{
/* pc_sysfw.c */
void pc_system_flash_create(PCMachineState *pcms);
+void pc_system_flash_cleanup_unused(PCMachineState *pcms);
void pc_system_firmware_init(PCMachineState *pcms, MemoryRegion *rom_memory);
/* acpi-build.c */
--- /dev/null
+/*
+ * AVR Power Reduction Management
+ *
+ * Copyright (c) 2019-2020 Michael Rolnik
+ *
+ * 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 HW_MISC_AVR_POWER_H
+#define HW_MISC_AVR_POWER_H
+
+#include "hw/sysbus.h"
+#include "hw/hw.h"
+
+
+#define TYPE_AVR_MASK "avr-power"
+#define AVR_MASK(obj) OBJECT_CHECK(AVRMaskState, (obj), TYPE_AVR_MASK)
+
+typedef struct {
+ /* <private> */
+ SysBusDevice parent_obj;
+
+ /* <public> */
+ MemoryRegion iomem;
+
+ uint8_t val;
+ qemu_irq irq[8];
+} AVRMaskState;
+
+#endif /* HW_MISC_AVR_POWER_H */
symbol_fn_t sym_cb);
hwaddr riscv_load_initrd(const char *filename, uint64_t mem_size,
uint64_t kernel_entry, hwaddr *start);
+uint32_t riscv_load_fdt(hwaddr dram_start, uint64_t dram_size, void *fdt);
+void riscv_setup_rom_reset_vec(hwaddr saddr, hwaddr rom_base,
+ hwaddr rom_size, uint64_t kernel_entry,
+ uint32_t fdt_load_addr, void *fdt);
+void riscv_rom_copy_firmware_info(hwaddr rom_base, hwaddr rom_size,
+ uint32_t reset_vec_size,
+ uint64_t kernel_entry);
#endif /* RISCV_BOOT_H */
--- /dev/null
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (c) 2019 Western Digital Corporation or its affiliates.
+ *
+ * Based on include/sbi/{fw_dynamic.h,sbi_scratch.h} from the OpenSBI project.
+ */
+#ifndef OPENSBI_H
+#define OPENSBI_H
+
+/** Expected value of info magic ('OSBI' ascii string in hex) */
+#define FW_DYNAMIC_INFO_MAGIC_VALUE 0x4942534f
+
+/** Maximum supported info version */
+#define FW_DYNAMIC_INFO_VERSION 0x2
+
+/** Possible next mode values */
+#define FW_DYNAMIC_INFO_NEXT_MODE_U 0x0
+#define FW_DYNAMIC_INFO_NEXT_MODE_S 0x1
+#define FW_DYNAMIC_INFO_NEXT_MODE_M 0x3
+
+enum sbi_scratch_options {
+ /** Disable prints during boot */
+ SBI_SCRATCH_NO_BOOT_PRINTS = (1 << 0),
+ /** Enable runtime debug prints */
+ SBI_SCRATCH_DEBUG_PRINTS = (1 << 1),
+};
+
+/** Representation dynamic info passed by previous booting stage */
+struct fw_dynamic_info {
+ /** Info magic */
+ target_long magic;
+ /** Info version */
+ target_long version;
+ /** Next booting stage address */
+ target_long next_addr;
+ /** Next booting stage mode */
+ target_long next_mode;
+ /** Options for OpenSBI library */
+ target_long options;
+ /**
+ * Preferred boot HART id
+ *
+ * It is possible that the previous booting stage uses same link
+ * address as the FW_DYNAMIC firmware. In this case, the relocation
+ * lottery mechanism can potentially overwrite the previous booting
+ * stage while other HARTs are still running in the previous booting
+ * stage leading to boot-time crash. To avoid this boot-time crash,
+ * the previous booting stage can specify last HART that will jump
+ * to the FW_DYNAMIC firmware as the preferred boot HART.
+ *
+ * To avoid specifying a preferred boot HART, the previous booting
+ * stage can set it to -1UL which will force the FW_DYNAMIC firmware
+ * to use the relocation lottery mechanism.
+ */
+ target_long boot_hart;
+};
+
+#endif
--- /dev/null
+/*
+ * AVR 16-bit timer
+ *
+ * Copyright (c) 2018 University of Kent
+ * Author: Ed Robbins
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+/*
+ * Driver for 16 bit timers on 8 bit AVR devices.
+ * Note:
+ * On ATmega640/V-1280/V-1281/V-2560/V-2561/V timers 1, 3, 4 and 5 are 16 bit
+ */
+
+#ifndef HW_TIMER_AVR_TIMER16_H
+#define HW_TIMER_AVR_TIMER16_H
+
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+#include "hw/hw.h"
+
+enum NextInterrupt {
+ OVERFLOW,
+ COMPA,
+ COMPB,
+ COMPC,
+ CAPT
+};
+
+#define TYPE_AVR_TIMER16 "avr-timer16"
+#define AVR_TIMER16(obj) \
+ OBJECT_CHECK(AVRTimer16State, (obj), TYPE_AVR_TIMER16)
+
+typedef struct AVRTimer16State {
+ /* <private> */
+ SysBusDevice parent_obj;
+
+ /* <public> */
+ MemoryRegion iomem;
+ MemoryRegion imsk_iomem;
+ MemoryRegion ifr_iomem;
+ QEMUTimer *timer;
+ qemu_irq capt_irq;
+ qemu_irq compa_irq;
+ qemu_irq compb_irq;
+ qemu_irq compc_irq;
+ qemu_irq ovf_irq;
+
+ bool enabled;
+
+ /* registers */
+ uint8_t cra;
+ uint8_t crb;
+ uint8_t crc;
+ uint8_t cntl;
+ uint8_t cnth;
+ uint8_t icrl;
+ uint8_t icrh;
+ uint8_t ocral;
+ uint8_t ocrah;
+ uint8_t ocrbl;
+ uint8_t ocrbh;
+ uint8_t ocrcl;
+ uint8_t ocrch;
+ /*
+ * Reads and writes to CNT and ICR utilise a bizarre temporary
+ * register, which we emulate
+ */
+ uint8_t rtmp;
+ uint8_t imsk;
+ uint8_t ifr;
+
+ uint8_t id;
+ uint64_t cpu_freq_hz;
+ uint64_t freq_hz;
+ uint64_t period_ns;
+ uint64_t reset_time_ns;
+ enum NextInterrupt next_interrupt;
+} AVRTimer16State;
+
+#endif /* HW_TIMER_AVR_TIMER16_H */
# include <machine/bswap.h>
#elif defined(__FreeBSD__)
# include <sys/endian.h>
+#elif defined(__HAIKU__)
+# include <endian.h>
#elif defined(CONFIG_BYTESWAP_H)
# include <byteswap.h>
const char *error_get_progname(void);
extern bool error_with_timestamp;
+extern bool error_with_guestname;
+extern const char *error_guest_name;
#endif
*/
void qemu_cond_wait_iothread(QemuCond *cond);
+/*
+ * qemu_cond_timedwait_iothread: like the previous, but with timeout
+ */
+void qemu_cond_timedwait_iothread(QemuCond *cond, int ms);
+
/* internal interfaces */
void qemu_fd_register(int fd);
#include <setjmp.h>
#include <signal.h>
-#ifdef __OpenBSD__
+#ifdef HAVE_SYS_SIGNAL_H
#include <sys/signal.h>
#endif
* Note that neither form is usable as an #if condition; if you truly
* need to write conditional code that depends on a minimum or maximum
* determined by the pre-processor instead of the compiler, you'll
- * have to open-code it.
+ * have to open-code it. Sadly, Coverity is severely confused by the
+ * constant variants, so we have to dumb things down there.
*/
#undef MIN
#define MIN(a, b) \
typeof(1 ? (a) : (b)) _a = (a), _b = (b); \
_a < _b ? _a : _b; \
})
-#define MIN_CONST(a, b) \
- __builtin_choose_expr( \
- __builtin_constant_p(a) && __builtin_constant_p(b), \
- (a) < (b) ? (a) : (b), \
- ((void)0))
#undef MAX
#define MAX(a, b) \
({ \
typeof(1 ? (a) : (b)) _a = (a), _b = (b); \
_a > _b ? _a : _b; \
})
-#define MAX_CONST(a, b) \
+
+#ifdef __COVERITY__
+# define MIN_CONST(a, b) ((a) < (b) ? (a) : (b))
+# define MAX_CONST(a, b) ((a) > (b) ? (a) : (b))
+#else
+# define MIN_CONST(a, b) \
+ __builtin_choose_expr( \
+ __builtin_constant_p(a) && __builtin_constant_p(b), \
+ (a) < (b) ? (a) : (b), \
+ ((void)0))
+# define MAX_CONST(a, b) \
__builtin_choose_expr( \
__builtin_constant_p(a) && __builtin_constant_p(b), \
(a) > (b) ? (a) : (b), \
((void)0))
+#endif
/*
* Minimum function that returns zero only if both values are zero.
#define HAVE_CHARDEV_PARPORT 1
#endif
+#if defined(__HAIKU__)
+#define SIGIO SIGPOLL
+#endif
+
#if defined(CONFIG_LINUX)
#ifndef BUS_MCEERR_AR
#define BUS_MCEERR_AR 4
#endif
}
+/**
+ * qemu_get_host_name:
+ * @errp: Error object
+ *
+ * Operating system agnostic way of querying host name.
+ *
+ * Returns allocated hostname (caller should free), NULL on failure.
+ */
+char *qemu_get_host_name(Error **errp);
+
#endif
int socket_set_cork(int fd, int v);
int socket_set_nodelay(int fd);
void qemu_set_block(int fd);
+int qemu_try_set_nonblock(int fd);
void qemu_set_nonblock(int fd);
int socket_set_fast_reuse(int fd);
typedef struct QString QString;
typedef struct RAMBlock RAMBlock;
typedef struct Range Range;
+typedef struct SavedIOTLB SavedIOTLB;
typedef struct SHPCDevice SHPCDevice;
typedef struct SSIBus SSIBus;
typedef struct VirtIODevice VirtIODevice;
void object_unref(Object *obj);
/**
- * object_property_add:
+ * object_property_try_add:
* @obj: the object to add a property to
* @name: the name of the property. This can contain any character except for
* a forward slash. In general, you should use hyphens '-' instead of
* meant to allow a property to free its opaque upon object
* destruction. This may be NULL.
* @opaque: an opaque pointer to pass to the callbacks for the property
+ * @errp: pointer to error object
*
* Returns: The #ObjectProperty; this can be used to set the @resolve
* callback for child and link properties.
*/
+ObjectProperty *object_property_try_add(Object *obj, const char *name,
+ const char *type,
+ ObjectPropertyAccessor *get,
+ ObjectPropertyAccessor *set,
+ ObjectPropertyRelease *release,
+ void *opaque, Error **errp);
+
+/**
+ * object_property_add:
+ * Same as object_property_try_add() with @errp hardcoded to
+ * &error_abort.
+ */
ObjectProperty *object_property_add(Object *obj, const char *name,
const char *type,
ObjectPropertyAccessor *get,
Object *object_resolve_path_component(Object *parent, const char *part);
/**
- * object_property_add_child:
+ * object_property_try_add_child:
* @obj: the object to add a property to
* @name: the name of the property
* @child: the child object
+ * @errp: pointer to error object
*
* Child properties form the composition tree. All objects need to be a child
* of another object. Objects can only be a child of one object.
*
* Returns: The newly added property on success, or %NULL on failure.
*/
+ObjectProperty *object_property_try_add_child(Object *obj, const char *name,
+ Object *child, Error **errp);
+
+/**
+ * object_property_add_child:
+ * Same as object_property_try_add_child() with @errp hardcoded to
+ * &error_abort
+ */
ObjectProperty *object_property_add_child(Object *obj, const char *name,
Object *child);
QEMU_ARCH_HPPA = (1 << 18),
QEMU_ARCH_RISCV = (1 << 19),
QEMU_ARCH_RX = (1 << 20),
+ QEMU_ARCH_AVR = (1 << 21),
QEMU_ARCH_NONE = (1 << 31),
};
--- /dev/null
+/*
+ * Copyright (c) 2012 SUSE LINUX Products GmbH
+ *
+ * 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
+ * <http://www.gnu.org/licenses/gpl-2.0.html>
+ */
+
+#ifndef SYSEMU_CPU_THROTTLE_H
+#define SYSEMU_CPU_THROTTLE_H
+
+#include "qemu/timer.h"
+
+/**
+ * cpu_throttle_init:
+ *
+ * Initialize the CPU throttling API.
+ */
+void cpu_throttle_init(void);
+
+/**
+ * cpu_throttle_set:
+ * @new_throttle_pct: Percent of sleep time. Valid range is 1 to 99.
+ *
+ * Throttles all vcpus by forcing them to sleep for the given percentage of
+ * time. A throttle_percentage of 25 corresponds to a 75% duty cycle roughly.
+ * (example: 10ms sleep for every 30ms awake).
+ *
+ * cpu_throttle_set can be called as needed to adjust new_throttle_pct.
+ * Once the throttling starts, it will remain in effect until cpu_throttle_stop
+ * is called.
+ */
+void cpu_throttle_set(int new_throttle_pct);
+
+/**
+ * cpu_throttle_stop:
+ *
+ * Stops the vcpu throttling started by cpu_throttle_set.
+ */
+void cpu_throttle_stop(void);
+
+/**
+ * cpu_throttle_active:
+ *
+ * Returns: %true if the vcpus are currently being throttled, %false otherwise.
+ */
+bool cpu_throttle_active(void);
+
+/**
+ * cpu_throttle_get_percentage:
+ *
+ * Returns the vcpu throttle percentage. See cpu_throttle_set for details.
+ *
+ * Returns: The throttle percentage in range 1 to 99.
+ */
+int cpu_throttle_get_percentage(void);
+
+#endif /* SYSEMU_CPU_THROTTLE_H */
void hvf_cpu_synchronize_state(CPUState *);
void hvf_cpu_synchronize_post_reset(CPUState *);
void hvf_cpu_synchronize_post_init(CPUState *);
+void hvf_cpu_synchronize_pre_loadvm(CPUState *);
void hvf_vcpu_destroy(CPUState *);
-void hvf_reset_vcpu(CPUState *);
#define TYPE_HVF_ACCEL ACCEL_CLASS_NAME("hvf")
#include "hw/core/cpu.h"
#include "sysemu/hax.h"
#include "sysemu/kvm.h"
+#include "sysemu/hvf.h"
#include "sysemu/whpx.h"
static inline void cpu_synchronize_state(CPUState *cpu)
if (hax_enabled()) {
hax_cpu_synchronize_state(cpu);
}
+ if (hvf_enabled()) {
+ hvf_cpu_synchronize_state(cpu);
+ }
if (whpx_enabled()) {
whpx_cpu_synchronize_state(cpu);
}
if (hax_enabled()) {
hax_cpu_synchronize_post_reset(cpu);
}
+ if (hvf_enabled()) {
+ hvf_cpu_synchronize_post_reset(cpu);
+ }
if (whpx_enabled()) {
whpx_cpu_synchronize_post_reset(cpu);
}
if (hax_enabled()) {
hax_cpu_synchronize_post_init(cpu);
}
+ if (hvf_enabled()) {
+ hvf_cpu_synchronize_post_init(cpu);
+ }
if (whpx_enabled()) {
whpx_cpu_synchronize_post_init(cpu);
}
if (hax_enabled()) {
hax_cpu_synchronize_pre_loadvm(cpu);
}
+ if (hvf_enabled()) {
+ hvf_cpu_synchronize_pre_loadvm(cpu);
+ }
if (whpx_enabled()) {
whpx_cpu_synchronize_pre_loadvm(cpu);
}
#ifndef UI_GTK_H
#define UI_GTK_H
-#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
/* Work around an -Wstrict-prototypes warning in GTK headers */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
-#endif
#include <gtk/gtk.h>
-#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
#pragma GCC diagnostic pop
-#endif
#include <gdk/gdkkeysyms.h>
#define QEMU_PIXMAN_H
/* pixman-0.16.0 headers have a redundant declaration */
-#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wredundant-decls"
-#endif
#include <pixman.h>
-#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
#pragma GCC diagnostic pop
-#endif
/*
* pixman image formats are defined to be native endian,
+++ /dev/null
-/*
- * QEMU System Emulator
- *
- * Copyright (c) 2003-2008 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.
- */
-/*
- * splitted out ioport related stuffs from vl.c.
- */
-
-#include "qemu/osdep.h"
-#include "cpu.h"
-#include "exec/ioport.h"
-#include "trace-root.h"
-#include "exec/memory.h"
-#include "exec/address-spaces.h"
-
-typedef struct MemoryRegionPortioList {
- MemoryRegion mr;
- void *portio_opaque;
- MemoryRegionPortio ports[];
-} MemoryRegionPortioList;
-
-static uint64_t unassigned_io_read(void *opaque, hwaddr addr, unsigned size)
-{
- return -1ULL;
-}
-
-static void unassigned_io_write(void *opaque, hwaddr addr, uint64_t val,
- unsigned size)
-{
-}
-
-const MemoryRegionOps unassigned_io_ops = {
- .read = unassigned_io_read,
- .write = unassigned_io_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-void cpu_outb(uint32_t addr, uint8_t val)
-{
- trace_cpu_out(addr, 'b', val);
- address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED,
- &val, 1);
-}
-
-void cpu_outw(uint32_t addr, uint16_t val)
-{
- uint8_t buf[2];
-
- trace_cpu_out(addr, 'w', val);
- stw_p(buf, val);
- address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED,
- buf, 2);
-}
-
-void cpu_outl(uint32_t addr, uint32_t val)
-{
- uint8_t buf[4];
-
- trace_cpu_out(addr, 'l', val);
- stl_p(buf, val);
- address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED,
- buf, 4);
-}
-
-uint8_t cpu_inb(uint32_t addr)
-{
- uint8_t val;
-
- address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED,
- &val, 1);
- trace_cpu_in(addr, 'b', val);
- return val;
-}
-
-uint16_t cpu_inw(uint32_t addr)
-{
- uint8_t buf[2];
- uint16_t val;
-
- address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, buf, 2);
- val = lduw_p(buf);
- trace_cpu_in(addr, 'w', val);
- return val;
-}
-
-uint32_t cpu_inl(uint32_t addr)
-{
- uint8_t buf[4];
- uint32_t val;
-
- address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, buf, 4);
- val = ldl_p(buf);
- trace_cpu_in(addr, 'l', val);
- return val;
-}
-
-void portio_list_init(PortioList *piolist,
- Object *owner,
- const MemoryRegionPortio *callbacks,
- void *opaque, const char *name)
-{
- unsigned n = 0;
-
- while (callbacks[n].size) {
- ++n;
- }
-
- piolist->ports = callbacks;
- piolist->nr = 0;
- piolist->regions = g_new0(MemoryRegion *, n);
- piolist->address_space = NULL;
- piolist->opaque = opaque;
- piolist->owner = owner;
- piolist->name = name;
- piolist->flush_coalesced_mmio = false;
-}
-
-void portio_list_set_flush_coalesced(PortioList *piolist)
-{
- piolist->flush_coalesced_mmio = true;
-}
-
-void portio_list_destroy(PortioList *piolist)
-{
- MemoryRegionPortioList *mrpio;
- unsigned i;
-
- for (i = 0; i < piolist->nr; ++i) {
- mrpio = container_of(piolist->regions[i], MemoryRegionPortioList, mr);
- object_unparent(OBJECT(&mrpio->mr));
- g_free(mrpio);
- }
- g_free(piolist->regions);
-}
-
-static const MemoryRegionPortio *find_portio(MemoryRegionPortioList *mrpio,
- uint64_t offset, unsigned size,
- bool write)
-{
- const MemoryRegionPortio *mrp;
-
- for (mrp = mrpio->ports; mrp->size; ++mrp) {
- if (offset >= mrp->offset && offset < mrp->offset + mrp->len &&
- size == mrp->size &&
- (write ? (bool)mrp->write : (bool)mrp->read)) {
- return mrp;
- }
- }
- return NULL;
-}
-
-static uint64_t portio_read(void *opaque, hwaddr addr, unsigned size)
-{
- MemoryRegionPortioList *mrpio = opaque;
- const MemoryRegionPortio *mrp = find_portio(mrpio, addr, size, false);
- uint64_t data;
-
- data = ((uint64_t)1 << (size * 8)) - 1;
- if (mrp) {
- data = mrp->read(mrpio->portio_opaque, mrp->base + addr);
- } else if (size == 2) {
- mrp = find_portio(mrpio, addr, 1, false);
- if (mrp) {
- data = mrp->read(mrpio->portio_opaque, mrp->base + addr);
- if (addr + 1 < mrp->offset + mrp->len) {
- data |= mrp->read(mrpio->portio_opaque, mrp->base + addr + 1) << 8;
- } else {
- data |= 0xff00;
- }
- }
- }
- return data;
-}
-
-static void portio_write(void *opaque, hwaddr addr, uint64_t data,
- unsigned size)
-{
- MemoryRegionPortioList *mrpio = opaque;
- const MemoryRegionPortio *mrp = find_portio(mrpio, addr, size, true);
-
- if (mrp) {
- mrp->write(mrpio->portio_opaque, mrp->base + addr, data);
- } else if (size == 2) {
- mrp = find_portio(mrpio, addr, 1, true);
- if (mrp) {
- mrp->write(mrpio->portio_opaque, mrp->base + addr, data & 0xff);
- if (addr + 1 < mrp->offset + mrp->len) {
- mrp->write(mrpio->portio_opaque, mrp->base + addr + 1, data >> 8);
- }
- }
- }
-}
-
-static const MemoryRegionOps portio_ops = {
- .read = portio_read,
- .write = portio_write,
- .endianness = DEVICE_LITTLE_ENDIAN,
- .valid.unaligned = true,
- .impl.unaligned = true,
-};
-
-static void portio_list_add_1(PortioList *piolist,
- const MemoryRegionPortio *pio_init,
- unsigned count, unsigned start,
- unsigned off_low, unsigned off_high)
-{
- MemoryRegionPortioList *mrpio;
- unsigned i;
-
- /* Copy the sub-list and null-terminate it. */
- mrpio = g_malloc0(sizeof(MemoryRegionPortioList) +
- sizeof(MemoryRegionPortio) * (count + 1));
- mrpio->portio_opaque = piolist->opaque;
- memcpy(mrpio->ports, pio_init, sizeof(MemoryRegionPortio) * count);
- memset(mrpio->ports + count, 0, sizeof(MemoryRegionPortio));
-
- /* Adjust the offsets to all be zero-based for the region. */
- for (i = 0; i < count; ++i) {
- mrpio->ports[i].offset -= off_low;
- mrpio->ports[i].base = start + off_low;
- }
-
- memory_region_init_io(&mrpio->mr, piolist->owner, &portio_ops, mrpio,
- piolist->name, off_high - off_low);
- if (piolist->flush_coalesced_mmio) {
- memory_region_set_flush_coalesced(&mrpio->mr);
- }
- memory_region_add_subregion(piolist->address_space,
- start + off_low, &mrpio->mr);
- piolist->regions[piolist->nr] = &mrpio->mr;
- ++piolist->nr;
-}
-
-void portio_list_add(PortioList *piolist,
- MemoryRegion *address_space,
- uint32_t start)
-{
- const MemoryRegionPortio *pio, *pio_start = piolist->ports;
- unsigned int off_low, off_high, off_last, count;
-
- piolist->address_space = address_space;
-
- /* Handle the first entry specially. */
- off_last = off_low = pio_start->offset;
- off_high = off_low + pio_start->len + pio_start->size - 1;
- count = 1;
-
- for (pio = pio_start + 1; pio->size != 0; pio++, count++) {
- /* All entries must be sorted by offset. */
- assert(pio->offset >= off_last);
- off_last = pio->offset;
-
- /* If we see a hole, break the region. */
- if (off_last > off_high) {
- portio_list_add_1(piolist, pio_start, count, start, off_low,
- off_high);
- /* ... and start collecting anew. */
- pio_start = pio;
- off_low = off_last;
- off_high = off_low + pio->len + pio_start->size - 1;
- count = 0;
- } else if (off_last + pio->len > off_high) {
- off_high = off_last + pio->len + pio_start->size - 1;
- }
- }
-
- /* There will always be an open sub-list. */
- portio_list_add_1(piolist, pio_start, count, start, off_low, off_high);
-}
-
-void portio_list_del(PortioList *piolist)
-{
- MemoryRegionPortioList *mrpio;
- unsigned i;
-
- for (i = 0; i < piolist->nr; ++i) {
- mrpio = container_of(piolist->regions[i], MemoryRegionPortioList, mr);
- memory_region_del_subregion(piolist->address_space, &mrpio->mr);
- }
-}
#define __NR_clone3 (__NR_SYSCALL_BASE + 435)
#define __NR_openat2 (__NR_SYSCALL_BASE + 437)
#define __NR_pidfd_getfd (__NR_SYSCALL_BASE + 438)
+#define __NR_faccessat2 (__NR_SYSCALL_BASE + 439)
#endif /* _ASM_ARM_UNISTD_COMMON_H */
};
struct kvm_vmx_nested_state_hdr {
- __u32 flags;
__u64 vmxon_pa;
__u64 vmcs12_pa;
- __u64 preemption_timer_deadline;
struct {
__u16 flags;
} smm;
+
+ __u32 flags;
+ __u64 preemption_timer_deadline;
};
struct kvm_svm_nested_state_data {
static void pgb_reserved_va(const char *image_name, abi_ulong guest_loaddr,
abi_ulong guest_hiaddr, long align)
{
- const int flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE;
+ int flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE;
void *addr, *test;
if (guest_hiaddr > reserved_va) {
/* Widen the "image" to the entire reserved address space. */
pgb_static(image_name, 0, reserved_va, align);
+#ifdef MAP_FIXED_NOREPLACE
+ flags |= MAP_FIXED_NOREPLACE;
+#endif
+
/* Reserve the memory on the host. */
assert(guest_base != 0);
test = g2h(0);
addr = mmap(test, reserved_va, PROT_NONE, flags, -1, 0);
if (addr == MAP_FAILED) {
error_report("Unable to reserve 0x%lx bytes of virtual address "
- "space for use as guest address space (check your "
+ "space (%s) for use as guest address space (check your "
"virtual memory ulimit setting or reserve less "
- "using -R option)", reserved_va);
+ "using -R option)", reserved_va, strerror(errno));
exit(EXIT_FAILURE);
}
assert(addr == test);
QEMU_IFLA_NEW_IFINDEX,
QEMU_IFLA_MIN_MTU,
QEMU_IFLA_MAX_MTU,
+ QEMU_IFLA_PROP_LIST,
+ QEMU_IFLA_ALT_IFNAME,
+ QEMU_IFLA_PERM_ADDRESS,
QEMU___IFLA_MAX
};
/* binary stream */
case QEMU_IFLA_ADDRESS:
case QEMU_IFLA_BROADCAST:
+ case QEMU_IFLA_PERM_ADDRESS:
/* string */
case QEMU_IFLA_IFNAME:
case QEMU_IFLA_QDISC:
break;
case RTM_NEWLINK:
case RTM_DELLINK:
+ case RTM_SETLINK:
if (nlh->nlmsg_len >= NLMSG_LENGTH(sizeof(*ifi))) {
ifi = NLMSG_DATA(nlh);
ifi->ifi_type = tswap16(ifi->ifi_type);
* Variants for the return value output function
*/
-static void
+static bool
print_syscall_err(abi_long ret)
{
- const char *errstr = NULL;
+ const char *errstr;
qemu_log(" = ");
if (ret < 0) {
- qemu_log("-1 errno=%d", errno);
errstr = target_strerror(-ret);
if (errstr) {
- qemu_log(" (%s)", errstr);
+ qemu_log("-1 errno=%d (%s)", (int)-ret, errstr);
+ return true;
}
}
+ return false;
}
static void
abi_long arg0, abi_long arg1, abi_long arg2,
abi_long arg3, abi_long arg4, abi_long arg5)
{
- print_syscall_err(ret);
-
- if (ret >= 0) {
- qemu_log("0x" TARGET_ABI_FMT_lx "\n", ret);
+ if (!print_syscall_err(ret)) {
+ qemu_log("0x" TARGET_ABI_FMT_lx, ret);
}
+ qemu_log("\n");
}
#if 0 /* currently unused */
abi_long arg0, abi_long arg1, abi_long arg2,
abi_long arg3, abi_long arg4, abi_long arg5)
{
- print_syscall_err(ret);
-
- if (ret >= 0) {
+ if (!print_syscall_err(ret)) {
qemu_log(" = 0x" TARGET_ABI_FMT_lx " (", ret);
print_fdset(arg0, arg1);
qemu_log(",");
abi_long arg0, abi_long arg1, abi_long arg2,
abi_long arg3, abi_long arg4, abi_long arg5)
{
- print_syscall_err(ret);
-
- if (ret >= 0) {
+ if (!print_syscall_err(ret)) {
qemu_log(TARGET_ABI_FMT_ld, ret);
switch (ret) {
case TARGET_TIME_OK:
abi_long arg0, abi_long arg1, abi_long arg2,
abi_long arg3, abi_long arg4, abi_long arg5)
{
- print_syscall_err(ret);
-
- if (ret >= 0) {
+ if (!print_syscall_err(ret)) {
qemu_log(TARGET_ABI_FMT_ld, ret);
qemu_log(" (list = ");
if (arg1 != 0) {
abi_long arg0, abi_long arg1, abi_long arg2,
abi_long arg3, abi_long arg4, abi_long arg5)
{
- print_syscall_err(ret);
-
- if (ret >= 0) {
+ if (!print_syscall_err(ret)) {
qemu_log(TARGET_ABI_FMT_ld, ret);
const IOCTLEntry *ie;
arg_type++;
target_size = thunk_type_size(arg_type, 0);
argptr = lock_user(VERIFY_READ, arg2, target_size, 1);
- thunk_print(argptr, arg_type);
- unlock_user(argptr, arg2, target_size);
+ if (argptr) {
+ thunk_print(argptr, arg_type);
+ unlock_user(argptr, arg2, target_size);
+ } else {
+ print_pointer(arg2, 1);
+ }
qemu_log(")");
}
}
arg_type++;
target_size = thunk_type_size(arg_type, 0);
argptr = lock_user(VERIFY_READ, arg2, target_size, 1);
- thunk_print(argptr, arg_type);
- unlock_user(argptr, arg2, target_size);
+ if (argptr) {
+ thunk_print(argptr, arg_type);
+ unlock_user(argptr, arg2, target_size);
+ } else {
+ print_pointer(arg2, 1);
+ }
break;
}
break;
arg1, arg2, arg3,
arg4, arg5, arg6);
} else {
- print_syscall_err(ret);
-
- if (ret >= 0) {
+ if (!print_syscall_err(ret)) {
qemu_log(TARGET_ABI_FMT_ld, ret);
}
qemu_log("\n");
const struct timespec *, req, struct timespec *, rem)
#endif
#ifdef __NR_ipc
+#ifdef __s390x__
+safe_syscall5(int, ipc, int, call, long, first, long, second, long, third,
+ void *, ptr)
+#else
safe_syscall6(int, ipc, int, call, long, first, long, second, long, third,
void *, ptr, long, fifth)
#endif
+#endif
#ifdef __NR_msgsnd
safe_syscall4(int, msgsnd, int, msgid, const void *, msgp, size_t, sz,
int, flags)
defined(TARGET_NR_pselect6) || defined(TARGET_NR_pselect6) || \
defined(TARGET_NR_nanosleep) || defined(TARGET_NR_clock_settime) || \
defined(TARGET_NR_utimensat) || defined(TARGET_NR_mq_timedsend) || \
- defined(TARGET_NR_mq_timedreceive)
+ defined(TARGET_NR_mq_timedreceive) || defined(TARGET_NR_ipc) || \
+ defined(TARGET_NR_semop) || defined(TARGET_NR_semtimedop)
static inline abi_long target_to_host_timespec(struct timespec *host_ts,
abi_ulong target_addr)
{
#endif
protocol == NETLINK_KOBJECT_UEVENT ||
protocol == NETLINK_AUDIT)) {
- return -TARGET_EPFNOSUPPORT;
+ return -TARGET_EPROTONOSUPPORT;
}
if (domain == AF_PACKET ||
return 0;
}
-static inline abi_long do_semop(int semid, abi_long ptr, unsigned nsops)
+#if defined(TARGET_NR_ipc) || defined(TARGET_NR_semop) || \
+ defined(TARGET_NR_semtimedop)
+
+/*
+ * This macro is required to handle the s390 variants, which passes the
+ * arguments in a different order than default.
+ */
+#ifdef __s390x__
+#define SEMTIMEDOP_IPC_ARGS(__nsops, __sops, __timeout) \
+ (__nsops), (__timeout), (__sops)
+#else
+#define SEMTIMEDOP_IPC_ARGS(__nsops, __sops, __timeout) \
+ (__nsops), 0, (__sops), (__timeout)
+#endif
+
+static inline abi_long do_semtimedop(int semid,
+ abi_long ptr,
+ unsigned nsops,
+ abi_long timeout)
{
struct sembuf sops[nsops];
+ struct timespec ts, *pts = NULL;
abi_long ret;
+ if (timeout) {
+ pts = &ts;
+ if (target_to_host_timespec(pts, timeout)) {
+ return -TARGET_EFAULT;
+ }
+ }
+
if (target_to_host_sembuf(sops, ptr, nsops))
return -TARGET_EFAULT;
ret = -TARGET_ENOSYS;
#ifdef __NR_semtimedop
- ret = get_errno(safe_semtimedop(semid, sops, nsops, NULL));
+ ret = get_errno(safe_semtimedop(semid, sops, nsops, pts));
#endif
#ifdef __NR_ipc
if (ret == -TARGET_ENOSYS) {
- ret = get_errno(safe_ipc(IPCOP_semtimedop, semid, nsops, 0, sops, 0));
+ ret = get_errno(safe_ipc(IPCOP_semtimedop, semid,
+ SEMTIMEDOP_IPC_ARGS(nsops, sops, (long)pts)));
}
#endif
return ret;
}
+#endif
struct target_msqid_ds
{
#endif
#ifdef __NR_ipc
if (ret == -TARGET_ENOSYS) {
+#ifdef __s390x__
+ ret = get_errno(safe_ipc(IPCOP_msgsnd, msqid, msgsz, msgflg,
+ host_mb));
+#else
ret = get_errno(safe_ipc(IPCOP_msgsnd, msqid, msgsz, msgflg,
host_mb, 0));
+#endif
}
#endif
g_free(host_mb);
return ret;
}
+#ifdef __NR_ipc
+#if defined(__sparc__)
+/* SPARC for msgrcv it does not use the kludge on final 2 arguments. */
+#define MSGRCV_ARGS(__msgp, __msgtyp) __msgp, __msgtyp
+#elif defined(__s390x__)
+/* The s390 sys_ipc variant has only five parameters. */
+#define MSGRCV_ARGS(__msgp, __msgtyp) \
+ ((long int[]){(long int)__msgp, __msgtyp})
+#else
+#define MSGRCV_ARGS(__msgp, __msgtyp) \
+ ((long int[]){(long int)__msgp, __msgtyp}), 0
+#endif
+#endif
+
static inline abi_long do_msgrcv(int msqid, abi_long msgp,
ssize_t msgsz, abi_long msgtyp,
int msgflg)
#ifdef __NR_ipc
if (ret == -TARGET_ENOSYS) {
ret = get_errno(safe_ipc(IPCOP_CALL(1, IPCOP_msgrcv), msqid, msgsz,
- msgflg, host_mb, msgtyp));
+ msgflg, MSGRCV_ARGS(host_mb, msgtyp)));
}
#endif
switch (call) {
case IPCOP_semop:
- ret = do_semop(first, ptr, second);
+ ret = do_semtimedop(first, ptr, second, 0);
+ break;
+ case IPCOP_semtimedop:
+ /*
+ * The s390 sys_ipc variant has only five parameters instead of six
+ * (as for default variant) and the only difference is the handling of
+ * SEMTIMEDOP where on s390 the third parameter is used as a pointer
+ * to a struct timespec where the generic variant uses fifth parameter.
+ */
+#if defined(TARGET_S390X)
+ ret = do_semtimedop(first, ptr, second, third);
+#else
+ ret = do_semtimedop(first, ptr, second, fifth);
+#endif
break;
case IPCOP_semget:
#endif
#ifdef TARGET_NR_semop
case TARGET_NR_semop:
- return do_semop(arg1, arg2, arg3);
+ return do_semtimedop(arg1, arg2, arg3, 0);
+#endif
+#ifdef TARGET_NR_semtimedop
+ case TARGET_NR_semtimedop:
+ return do_semtimedop(arg1, arg2, arg3, arg4);
#endif
#ifdef TARGET_NR_semctl
case TARGET_NR_semctl:
+++ /dev/null
-/*
- * Physical memory management
- *
- * Copyright 2011 Red Hat, Inc. and/or its affiliates
- *
- * Authors:
- * Avi Kivity <avi@redhat.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- */
-
-#include "qemu/osdep.h"
-#include "qapi/error.h"
-#include "cpu.h"
-#include "exec/memory.h"
-#include "exec/address-spaces.h"
-#include "qapi/visitor.h"
-#include "qemu/bitops.h"
-#include "qemu/error-report.h"
-#include "qemu/main-loop.h"
-#include "qemu/qemu-print.h"
-#include "qom/object.h"
-#include "trace-root.h"
-
-#include "exec/memory-internal.h"
-#include "exec/ram_addr.h"
-#include "sysemu/kvm.h"
-#include "sysemu/runstate.h"
-#include "sysemu/tcg.h"
-#include "sysemu/accel.h"
-#include "hw/boards.h"
-#include "migration/vmstate.h"
-
-//#define DEBUG_UNASSIGNED
-
-static unsigned memory_region_transaction_depth;
-static bool memory_region_update_pending;
-static bool ioeventfd_update_pending;
-bool global_dirty_log;
-
-static QTAILQ_HEAD(, MemoryListener) memory_listeners
- = QTAILQ_HEAD_INITIALIZER(memory_listeners);
-
-static QTAILQ_HEAD(, AddressSpace) address_spaces
- = QTAILQ_HEAD_INITIALIZER(address_spaces);
-
-static GHashTable *flat_views;
-
-typedef struct AddrRange AddrRange;
-
-/*
- * Note that signed integers are needed for negative offsetting in aliases
- * (large MemoryRegion::alias_offset).
- */
-struct AddrRange {
- Int128 start;
- Int128 size;
-};
-
-static AddrRange addrrange_make(Int128 start, Int128 size)
-{
- return (AddrRange) { start, size };
-}
-
-static bool addrrange_equal(AddrRange r1, AddrRange r2)
-{
- return int128_eq(r1.start, r2.start) && int128_eq(r1.size, r2.size);
-}
-
-static Int128 addrrange_end(AddrRange r)
-{
- return int128_add(r.start, r.size);
-}
-
-static AddrRange addrrange_shift(AddrRange range, Int128 delta)
-{
- int128_addto(&range.start, delta);
- return range;
-}
-
-static bool addrrange_contains(AddrRange range, Int128 addr)
-{
- return int128_ge(addr, range.start)
- && int128_lt(addr, addrrange_end(range));
-}
-
-static bool addrrange_intersects(AddrRange r1, AddrRange r2)
-{
- return addrrange_contains(r1, r2.start)
- || addrrange_contains(r2, r1.start);
-}
-
-static AddrRange addrrange_intersection(AddrRange r1, AddrRange r2)
-{
- Int128 start = int128_max(r1.start, r2.start);
- Int128 end = int128_min(addrrange_end(r1), addrrange_end(r2));
- return addrrange_make(start, int128_sub(end, start));
-}
-
-enum ListenerDirection { Forward, Reverse };
-
-#define MEMORY_LISTENER_CALL_GLOBAL(_callback, _direction, _args...) \
- do { \
- MemoryListener *_listener; \
- \
- switch (_direction) { \
- case Forward: \
- QTAILQ_FOREACH(_listener, &memory_listeners, link) { \
- if (_listener->_callback) { \
- _listener->_callback(_listener, ##_args); \
- } \
- } \
- break; \
- case Reverse: \
- QTAILQ_FOREACH_REVERSE(_listener, &memory_listeners, link) { \
- if (_listener->_callback) { \
- _listener->_callback(_listener, ##_args); \
- } \
- } \
- break; \
- default: \
- abort(); \
- } \
- } while (0)
-
-#define MEMORY_LISTENER_CALL(_as, _callback, _direction, _section, _args...) \
- do { \
- MemoryListener *_listener; \
- \
- switch (_direction) { \
- case Forward: \
- QTAILQ_FOREACH(_listener, &(_as)->listeners, link_as) { \
- if (_listener->_callback) { \
- _listener->_callback(_listener, _section, ##_args); \
- } \
- } \
- break; \
- case Reverse: \
- QTAILQ_FOREACH_REVERSE(_listener, &(_as)->listeners, link_as) { \
- if (_listener->_callback) { \
- _listener->_callback(_listener, _section, ##_args); \
- } \
- } \
- break; \
- default: \
- abort(); \
- } \
- } while (0)
-
-/* No need to ref/unref .mr, the FlatRange keeps it alive. */
-#define MEMORY_LISTENER_UPDATE_REGION(fr, as, dir, callback, _args...) \
- do { \
- MemoryRegionSection mrs = section_from_flat_range(fr, \
- address_space_to_flatview(as)); \
- MEMORY_LISTENER_CALL(as, callback, dir, &mrs, ##_args); \
- } while(0)
-
-struct CoalescedMemoryRange {
- AddrRange addr;
- QTAILQ_ENTRY(CoalescedMemoryRange) link;
-};
-
-struct MemoryRegionIoeventfd {
- AddrRange addr;
- bool match_data;
- uint64_t data;
- EventNotifier *e;
-};
-
-static bool memory_region_ioeventfd_before(MemoryRegionIoeventfd *a,
- MemoryRegionIoeventfd *b)
-{
- if (int128_lt(a->addr.start, b->addr.start)) {
- return true;
- } else if (int128_gt(a->addr.start, b->addr.start)) {
- return false;
- } else if (int128_lt(a->addr.size, b->addr.size)) {
- return true;
- } else if (int128_gt(a->addr.size, b->addr.size)) {
- return false;
- } else if (a->match_data < b->match_data) {
- return true;
- } else if (a->match_data > b->match_data) {
- return false;
- } else if (a->match_data) {
- if (a->data < b->data) {
- return true;
- } else if (a->data > b->data) {
- return false;
- }
- }
- if (a->e < b->e) {
- return true;
- } else if (a->e > b->e) {
- return false;
- }
- return false;
-}
-
-static bool memory_region_ioeventfd_equal(MemoryRegionIoeventfd *a,
- MemoryRegionIoeventfd *b)
-{
- return !memory_region_ioeventfd_before(a, b)
- && !memory_region_ioeventfd_before(b, a);
-}
-
-/* Range of memory in the global map. Addresses are absolute. */
-struct FlatRange {
- MemoryRegion *mr;
- hwaddr offset_in_region;
- AddrRange addr;
- uint8_t dirty_log_mask;
- bool romd_mode;
- bool readonly;
- bool nonvolatile;
-};
-
-#define FOR_EACH_FLAT_RANGE(var, view) \
- for (var = (view)->ranges; var < (view)->ranges + (view)->nr; ++var)
-
-static inline MemoryRegionSection
-section_from_flat_range(FlatRange *fr, FlatView *fv)
-{
- return (MemoryRegionSection) {
- .mr = fr->mr,
- .fv = fv,
- .offset_within_region = fr->offset_in_region,
- .size = fr->addr.size,
- .offset_within_address_space = int128_get64(fr->addr.start),
- .readonly = fr->readonly,
- .nonvolatile = fr->nonvolatile,
- };
-}
-
-static bool flatrange_equal(FlatRange *a, FlatRange *b)
-{
- return a->mr == b->mr
- && addrrange_equal(a->addr, b->addr)
- && a->offset_in_region == b->offset_in_region
- && a->romd_mode == b->romd_mode
- && a->readonly == b->readonly
- && a->nonvolatile == b->nonvolatile;
-}
-
-static FlatView *flatview_new(MemoryRegion *mr_root)
-{
- FlatView *view;
-
- view = g_new0(FlatView, 1);
- view->ref = 1;
- view->root = mr_root;
- memory_region_ref(mr_root);
- trace_flatview_new(view, mr_root);
-
- return view;
-}
-
-/* Insert a range into a given position. Caller is responsible for maintaining
- * sorting order.
- */
-static void flatview_insert(FlatView *view, unsigned pos, FlatRange *range)
-{
- if (view->nr == view->nr_allocated) {
- view->nr_allocated = MAX(2 * view->nr, 10);
- view->ranges = g_realloc(view->ranges,
- view->nr_allocated * sizeof(*view->ranges));
- }
- memmove(view->ranges + pos + 1, view->ranges + pos,
- (view->nr - pos) * sizeof(FlatRange));
- view->ranges[pos] = *range;
- memory_region_ref(range->mr);
- ++view->nr;
-}
-
-static void flatview_destroy(FlatView *view)
-{
- int i;
-
- trace_flatview_destroy(view, view->root);
- if (view->dispatch) {
- address_space_dispatch_free(view->dispatch);
- }
- for (i = 0; i < view->nr; i++) {
- memory_region_unref(view->ranges[i].mr);
- }
- g_free(view->ranges);
- memory_region_unref(view->root);
- g_free(view);
-}
-
-static bool flatview_ref(FlatView *view)
-{
- return atomic_fetch_inc_nonzero(&view->ref) > 0;
-}
-
-void flatview_unref(FlatView *view)
-{
- if (atomic_fetch_dec(&view->ref) == 1) {
- trace_flatview_destroy_rcu(view, view->root);
- assert(view->root);
- call_rcu(view, flatview_destroy, rcu);
- }
-}
-
-static bool can_merge(FlatRange *r1, FlatRange *r2)
-{
- return int128_eq(addrrange_end(r1->addr), r2->addr.start)
- && r1->mr == r2->mr
- && int128_eq(int128_add(int128_make64(r1->offset_in_region),
- r1->addr.size),
- int128_make64(r2->offset_in_region))
- && r1->dirty_log_mask == r2->dirty_log_mask
- && r1->romd_mode == r2->romd_mode
- && r1->readonly == r2->readonly
- && r1->nonvolatile == r2->nonvolatile;
-}
-
-/* Attempt to simplify a view by merging adjacent ranges */
-static void flatview_simplify(FlatView *view)
-{
- unsigned i, j, k;
-
- i = 0;
- while (i < view->nr) {
- j = i + 1;
- while (j < view->nr
- && can_merge(&view->ranges[j-1], &view->ranges[j])) {
- int128_addto(&view->ranges[i].addr.size, view->ranges[j].addr.size);
- ++j;
- }
- ++i;
- for (k = i; k < j; k++) {
- memory_region_unref(view->ranges[k].mr);
- }
- memmove(&view->ranges[i], &view->ranges[j],
- (view->nr - j) * sizeof(view->ranges[j]));
- view->nr -= j - i;
- }
-}
-
-static bool memory_region_big_endian(MemoryRegion *mr)
-{
-#ifdef TARGET_WORDS_BIGENDIAN
- return mr->ops->endianness != DEVICE_LITTLE_ENDIAN;
-#else
- return mr->ops->endianness == DEVICE_BIG_ENDIAN;
-#endif
-}
-
-static void adjust_endianness(MemoryRegion *mr, uint64_t *data, MemOp op)
-{
- if ((op & MO_BSWAP) != devend_memop(mr->ops->endianness)) {
- switch (op & MO_SIZE) {
- case MO_8:
- break;
- case MO_16:
- *data = bswap16(*data);
- break;
- case MO_32:
- *data = bswap32(*data);
- break;
- case MO_64:
- *data = bswap64(*data);
- break;
- default:
- g_assert_not_reached();
- }
- }
-}
-
-static inline void memory_region_shift_read_access(uint64_t *value,
- signed shift,
- uint64_t mask,
- uint64_t tmp)
-{
- if (shift >= 0) {
- *value |= (tmp & mask) << shift;
- } else {
- *value |= (tmp & mask) >> -shift;
- }
-}
-
-static inline uint64_t memory_region_shift_write_access(uint64_t *value,
- signed shift,
- uint64_t mask)
-{
- uint64_t tmp;
-
- if (shift >= 0) {
- tmp = (*value >> shift) & mask;
- } else {
- tmp = (*value << -shift) & mask;
- }
-
- return tmp;
-}
-
-static hwaddr memory_region_to_absolute_addr(MemoryRegion *mr, hwaddr offset)
-{
- MemoryRegion *root;
- hwaddr abs_addr = offset;
-
- abs_addr += mr->addr;
- for (root = mr; root->container; ) {
- root = root->container;
- abs_addr += root->addr;
- }
-
- return abs_addr;
-}
-
-static int get_cpu_index(void)
-{
- if (current_cpu) {
- return current_cpu->cpu_index;
- }
- return -1;
-}
-
-static MemTxResult memory_region_read_accessor(MemoryRegion *mr,
- hwaddr addr,
- uint64_t *value,
- unsigned size,
- signed shift,
- uint64_t mask,
- MemTxAttrs attrs)
-{
- uint64_t tmp;
-
- tmp = mr->ops->read(mr->opaque, addr, size);
- if (mr->subpage) {
- trace_memory_region_subpage_read(get_cpu_index(), mr, addr, tmp, size);
- } else if (trace_event_get_state_backends(TRACE_MEMORY_REGION_OPS_READ)) {
- hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr);
- trace_memory_region_ops_read(get_cpu_index(), mr, abs_addr, tmp, size);
- }
- memory_region_shift_read_access(value, shift, mask, tmp);
- return MEMTX_OK;
-}
-
-static MemTxResult memory_region_read_with_attrs_accessor(MemoryRegion *mr,
- hwaddr addr,
- uint64_t *value,
- unsigned size,
- signed shift,
- uint64_t mask,
- MemTxAttrs attrs)
-{
- uint64_t tmp = 0;
- MemTxResult r;
-
- r = mr->ops->read_with_attrs(mr->opaque, addr, &tmp, size, attrs);
- if (mr->subpage) {
- trace_memory_region_subpage_read(get_cpu_index(), mr, addr, tmp, size);
- } else if (trace_event_get_state_backends(TRACE_MEMORY_REGION_OPS_READ)) {
- hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr);
- trace_memory_region_ops_read(get_cpu_index(), mr, abs_addr, tmp, size);
- }
- memory_region_shift_read_access(value, shift, mask, tmp);
- return r;
-}
-
-static MemTxResult memory_region_write_accessor(MemoryRegion *mr,
- hwaddr addr,
- uint64_t *value,
- unsigned size,
- signed shift,
- uint64_t mask,
- MemTxAttrs attrs)
-{
- uint64_t tmp = memory_region_shift_write_access(value, shift, mask);
-
- if (mr->subpage) {
- trace_memory_region_subpage_write(get_cpu_index(), mr, addr, tmp, size);
- } else if (trace_event_get_state_backends(TRACE_MEMORY_REGION_OPS_WRITE)) {
- hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr);
- trace_memory_region_ops_write(get_cpu_index(), mr, abs_addr, tmp, size);
- }
- mr->ops->write(mr->opaque, addr, tmp, size);
- return MEMTX_OK;
-}
-
-static MemTxResult memory_region_write_with_attrs_accessor(MemoryRegion *mr,
- hwaddr addr,
- uint64_t *value,
- unsigned size,
- signed shift,
- uint64_t mask,
- MemTxAttrs attrs)
-{
- uint64_t tmp = memory_region_shift_write_access(value, shift, mask);
-
- if (mr->subpage) {
- trace_memory_region_subpage_write(get_cpu_index(), mr, addr, tmp, size);
- } else if (trace_event_get_state_backends(TRACE_MEMORY_REGION_OPS_WRITE)) {
- hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr);
- trace_memory_region_ops_write(get_cpu_index(), mr, abs_addr, tmp, size);
- }
- return mr->ops->write_with_attrs(mr->opaque, addr, tmp, size, attrs);
-}
-
-static MemTxResult access_with_adjusted_size(hwaddr addr,
- uint64_t *value,
- unsigned size,
- unsigned access_size_min,
- unsigned access_size_max,
- MemTxResult (*access_fn)
- (MemoryRegion *mr,
- hwaddr addr,
- uint64_t *value,
- unsigned size,
- signed shift,
- uint64_t mask,
- MemTxAttrs attrs),
- MemoryRegion *mr,
- MemTxAttrs attrs)
-{
- uint64_t access_mask;
- unsigned access_size;
- unsigned i;
- MemTxResult r = MEMTX_OK;
-
- if (!access_size_min) {
- access_size_min = 1;
- }
- if (!access_size_max) {
- access_size_max = 4;
- }
-
- /* FIXME: support unaligned access? */
- access_size = MAX(MIN(size, access_size_max), access_size_min);
- access_mask = MAKE_64BIT_MASK(0, access_size * 8);
- if (memory_region_big_endian(mr)) {
- for (i = 0; i < size; i += access_size) {
- r |= access_fn(mr, addr + i, value, access_size,
- (size - access_size - i) * 8, access_mask, attrs);
- }
- } else {
- for (i = 0; i < size; i += access_size) {
- r |= access_fn(mr, addr + i, value, access_size, i * 8,
- access_mask, attrs);
- }
- }
- return r;
-}
-
-static AddressSpace *memory_region_to_address_space(MemoryRegion *mr)
-{
- AddressSpace *as;
-
- while (mr->container) {
- mr = mr->container;
- }
- QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
- if (mr == as->root) {
- return as;
- }
- }
- return NULL;
-}
-
-/* Render a memory region into the global view. Ranges in @view obscure
- * ranges in @mr.
- */
-static void render_memory_region(FlatView *view,
- MemoryRegion *mr,
- Int128 base,
- AddrRange clip,
- bool readonly,
- bool nonvolatile)
-{
- MemoryRegion *subregion;
- unsigned i;
- hwaddr offset_in_region;
- Int128 remain;
- Int128 now;
- FlatRange fr;
- AddrRange tmp;
-
- if (!mr->enabled) {
- return;
- }
-
- int128_addto(&base, int128_make64(mr->addr));
- readonly |= mr->readonly;
- nonvolatile |= mr->nonvolatile;
-
- tmp = addrrange_make(base, mr->size);
-
- if (!addrrange_intersects(tmp, clip)) {
- return;
- }
-
- clip = addrrange_intersection(tmp, clip);
-
- if (mr->alias) {
- int128_subfrom(&base, int128_make64(mr->alias->addr));
- int128_subfrom(&base, int128_make64(mr->alias_offset));
- render_memory_region(view, mr->alias, base, clip,
- readonly, nonvolatile);
- return;
- }
-
- /* Render subregions in priority order. */
- QTAILQ_FOREACH(subregion, &mr->subregions, subregions_link) {
- render_memory_region(view, subregion, base, clip,
- readonly, nonvolatile);
- }
-
- if (!mr->terminates) {
- return;
- }
-
- offset_in_region = int128_get64(int128_sub(clip.start, base));
- base = clip.start;
- remain = clip.size;
-
- fr.mr = mr;
- fr.dirty_log_mask = memory_region_get_dirty_log_mask(mr);
- fr.romd_mode = mr->romd_mode;
- fr.readonly = readonly;
- fr.nonvolatile = nonvolatile;
-
- /* Render the region itself into any gaps left by the current view. */
- for (i = 0; i < view->nr && int128_nz(remain); ++i) {
- if (int128_ge(base, addrrange_end(view->ranges[i].addr))) {
- continue;
- }
- if (int128_lt(base, view->ranges[i].addr.start)) {
- now = int128_min(remain,
- int128_sub(view->ranges[i].addr.start, base));
- fr.offset_in_region = offset_in_region;
- fr.addr = addrrange_make(base, now);
- flatview_insert(view, i, &fr);
- ++i;
- int128_addto(&base, now);
- offset_in_region += int128_get64(now);
- int128_subfrom(&remain, now);
- }
- now = int128_sub(int128_min(int128_add(base, remain),
- addrrange_end(view->ranges[i].addr)),
- base);
- int128_addto(&base, now);
- offset_in_region += int128_get64(now);
- int128_subfrom(&remain, now);
- }
- if (int128_nz(remain)) {
- fr.offset_in_region = offset_in_region;
- fr.addr = addrrange_make(base, remain);
- flatview_insert(view, i, &fr);
- }
-}
-
-static MemoryRegion *memory_region_get_flatview_root(MemoryRegion *mr)
-{
- while (mr->enabled) {
- if (mr->alias) {
- if (!mr->alias_offset && int128_ge(mr->size, mr->alias->size)) {
- /* The alias is included in its entirety. Use it as
- * the "real" root, so that we can share more FlatViews.
- */
- mr = mr->alias;
- continue;
- }
- } else if (!mr->terminates) {
- unsigned int found = 0;
- MemoryRegion *child, *next = NULL;
- QTAILQ_FOREACH(child, &mr->subregions, subregions_link) {
- if (child->enabled) {
- if (++found > 1) {
- next = NULL;
- break;
- }
- if (!child->addr && int128_ge(mr->size, child->size)) {
- /* A child is included in its entirety. If it's the only
- * enabled one, use it in the hope of finding an alias down the
- * way. This will also let us share FlatViews.
- */
- next = child;
- }
- }
- }
- if (found == 0) {
- return NULL;
- }
- if (next) {
- mr = next;
- continue;
- }
- }
-
- return mr;
- }
-
- return NULL;
-}
-
-/* Render a memory topology into a list of disjoint absolute ranges. */
-static FlatView *generate_memory_topology(MemoryRegion *mr)
-{
- int i;
- FlatView *view;
-
- view = flatview_new(mr);
-
- if (mr) {
- render_memory_region(view, mr, int128_zero(),
- addrrange_make(int128_zero(), int128_2_64()),
- false, false);
- }
- flatview_simplify(view);
-
- view->dispatch = address_space_dispatch_new(view);
- for (i = 0; i < view->nr; i++) {
- MemoryRegionSection mrs =
- section_from_flat_range(&view->ranges[i], view);
- flatview_add_to_dispatch(view, &mrs);
- }
- address_space_dispatch_compact(view->dispatch);
- g_hash_table_replace(flat_views, mr, view);
-
- return view;
-}
-
-static void address_space_add_del_ioeventfds(AddressSpace *as,
- MemoryRegionIoeventfd *fds_new,
- unsigned fds_new_nb,
- MemoryRegionIoeventfd *fds_old,
- unsigned fds_old_nb)
-{
- unsigned iold, inew;
- MemoryRegionIoeventfd *fd;
- MemoryRegionSection section;
-
- /* Generate a symmetric difference of the old and new fd sets, adding
- * and deleting as necessary.
- */
-
- iold = inew = 0;
- while (iold < fds_old_nb || inew < fds_new_nb) {
- if (iold < fds_old_nb
- && (inew == fds_new_nb
- || memory_region_ioeventfd_before(&fds_old[iold],
- &fds_new[inew]))) {
- fd = &fds_old[iold];
- section = (MemoryRegionSection) {
- .fv = address_space_to_flatview(as),
- .offset_within_address_space = int128_get64(fd->addr.start),
- .size = fd->addr.size,
- };
- MEMORY_LISTENER_CALL(as, eventfd_del, Forward, §ion,
- fd->match_data, fd->data, fd->e);
- ++iold;
- } else if (inew < fds_new_nb
- && (iold == fds_old_nb
- || memory_region_ioeventfd_before(&fds_new[inew],
- &fds_old[iold]))) {
- fd = &fds_new[inew];
- section = (MemoryRegionSection) {
- .fv = address_space_to_flatview(as),
- .offset_within_address_space = int128_get64(fd->addr.start),
- .size = fd->addr.size,
- };
- MEMORY_LISTENER_CALL(as, eventfd_add, Reverse, §ion,
- fd->match_data, fd->data, fd->e);
- ++inew;
- } else {
- ++iold;
- ++inew;
- }
- }
-}
-
-FlatView *address_space_get_flatview(AddressSpace *as)
-{
- FlatView *view;
-
- RCU_READ_LOCK_GUARD();
- do {
- view = address_space_to_flatview(as);
- /* If somebody has replaced as->current_map concurrently,
- * flatview_ref returns false.
- */
- } while (!flatview_ref(view));
- return view;
-}
-
-static void address_space_update_ioeventfds(AddressSpace *as)
-{
- FlatView *view;
- FlatRange *fr;
- unsigned ioeventfd_nb = 0;
- unsigned ioeventfd_max;
- MemoryRegionIoeventfd *ioeventfds;
- AddrRange tmp;
- unsigned i;
-
- /*
- * It is likely that the number of ioeventfds hasn't changed much, so use
- * the previous size as the starting value, with some headroom to avoid
- * gratuitous reallocations.
- */
- ioeventfd_max = QEMU_ALIGN_UP(as->ioeventfd_nb, 4);
- ioeventfds = g_new(MemoryRegionIoeventfd, ioeventfd_max);
-
- view = address_space_get_flatview(as);
- FOR_EACH_FLAT_RANGE(fr, view) {
- for (i = 0; i < fr->mr->ioeventfd_nb; ++i) {
- tmp = addrrange_shift(fr->mr->ioeventfds[i].addr,
- int128_sub(fr->addr.start,
- int128_make64(fr->offset_in_region)));
- if (addrrange_intersects(fr->addr, tmp)) {
- ++ioeventfd_nb;
- if (ioeventfd_nb > ioeventfd_max) {
- ioeventfd_max = MAX(ioeventfd_max * 2, 4);
- ioeventfds = g_realloc(ioeventfds,
- ioeventfd_max * sizeof(*ioeventfds));
- }
- ioeventfds[ioeventfd_nb-1] = fr->mr->ioeventfds[i];
- ioeventfds[ioeventfd_nb-1].addr = tmp;
- }
- }
- }
-
- address_space_add_del_ioeventfds(as, ioeventfds, ioeventfd_nb,
- as->ioeventfds, as->ioeventfd_nb);
-
- g_free(as->ioeventfds);
- as->ioeventfds = ioeventfds;
- as->ioeventfd_nb = ioeventfd_nb;
- flatview_unref(view);
-}
-
-/*
- * Notify the memory listeners about the coalesced IO change events of
- * range `cmr'. Only the part that has intersection of the specified
- * FlatRange will be sent.
- */
-static void flat_range_coalesced_io_notify(FlatRange *fr, AddressSpace *as,
- CoalescedMemoryRange *cmr, bool add)
-{
- AddrRange tmp;
-
- tmp = addrrange_shift(cmr->addr,
- int128_sub(fr->addr.start,
- int128_make64(fr->offset_in_region)));
- if (!addrrange_intersects(tmp, fr->addr)) {
- return;
- }
- tmp = addrrange_intersection(tmp, fr->addr);
-
- if (add) {
- MEMORY_LISTENER_UPDATE_REGION(fr, as, Forward, coalesced_io_add,
- int128_get64(tmp.start),
- int128_get64(tmp.size));
- } else {
- MEMORY_LISTENER_UPDATE_REGION(fr, as, Reverse, coalesced_io_del,
- int128_get64(tmp.start),
- int128_get64(tmp.size));
- }
-}
-
-static void flat_range_coalesced_io_del(FlatRange *fr, AddressSpace *as)
-{
- CoalescedMemoryRange *cmr;
-
- QTAILQ_FOREACH(cmr, &fr->mr->coalesced, link) {
- flat_range_coalesced_io_notify(fr, as, cmr, false);
- }
-}
-
-static void flat_range_coalesced_io_add(FlatRange *fr, AddressSpace *as)
-{
- MemoryRegion *mr = fr->mr;
- CoalescedMemoryRange *cmr;
-
- if (QTAILQ_EMPTY(&mr->coalesced)) {
- return;
- }
-
- QTAILQ_FOREACH(cmr, &mr->coalesced, link) {
- flat_range_coalesced_io_notify(fr, as, cmr, true);
- }
-}
-
-static void address_space_update_topology_pass(AddressSpace *as,
- const FlatView *old_view,
- const FlatView *new_view,
- bool adding)
-{
- unsigned iold, inew;
- FlatRange *frold, *frnew;
-
- /* Generate a symmetric difference of the old and new memory maps.
- * Kill ranges in the old map, and instantiate ranges in the new map.
- */
- iold = inew = 0;
- while (iold < old_view->nr || inew < new_view->nr) {
- if (iold < old_view->nr) {
- frold = &old_view->ranges[iold];
- } else {
- frold = NULL;
- }
- if (inew < new_view->nr) {
- frnew = &new_view->ranges[inew];
- } else {
- frnew = NULL;
- }
-
- if (frold
- && (!frnew
- || int128_lt(frold->addr.start, frnew->addr.start)
- || (int128_eq(frold->addr.start, frnew->addr.start)
- && !flatrange_equal(frold, frnew)))) {
- /* In old but not in new, or in both but attributes changed. */
-
- if (!adding) {
- flat_range_coalesced_io_del(frold, as);
- MEMORY_LISTENER_UPDATE_REGION(frold, as, Reverse, region_del);
- }
-
- ++iold;
- } else if (frold && frnew && flatrange_equal(frold, frnew)) {
- /* In both and unchanged (except logging may have changed) */
-
- if (adding) {
- MEMORY_LISTENER_UPDATE_REGION(frnew, as, Forward, region_nop);
- if (frnew->dirty_log_mask & ~frold->dirty_log_mask) {
- MEMORY_LISTENER_UPDATE_REGION(frnew, as, Forward, log_start,
- frold->dirty_log_mask,
- frnew->dirty_log_mask);
- }
- if (frold->dirty_log_mask & ~frnew->dirty_log_mask) {
- MEMORY_LISTENER_UPDATE_REGION(frnew, as, Reverse, log_stop,
- frold->dirty_log_mask,
- frnew->dirty_log_mask);
- }
- }
-
- ++iold;
- ++inew;
- } else {
- /* In new */
-
- if (adding) {
- MEMORY_LISTENER_UPDATE_REGION(frnew, as, Forward, region_add);
- flat_range_coalesced_io_add(frnew, as);
- }
-
- ++inew;
- }
- }
-}
-
-static void flatviews_init(void)
-{
- static FlatView *empty_view;
-
- if (flat_views) {
- return;
- }
-
- flat_views = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
- (GDestroyNotify) flatview_unref);
- if (!empty_view) {
- empty_view = generate_memory_topology(NULL);
- /* We keep it alive forever in the global variable. */
- flatview_ref(empty_view);
- } else {
- g_hash_table_replace(flat_views, NULL, empty_view);
- flatview_ref(empty_view);
- }
-}
-
-static void flatviews_reset(void)
-{
- AddressSpace *as;
-
- if (flat_views) {
- g_hash_table_unref(flat_views);
- flat_views = NULL;
- }
- flatviews_init();
-
- /* Render unique FVs */
- QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
- MemoryRegion *physmr = memory_region_get_flatview_root(as->root);
-
- if (g_hash_table_lookup(flat_views, physmr)) {
- continue;
- }
-
- generate_memory_topology(physmr);
- }
-}
-
-static void address_space_set_flatview(AddressSpace *as)
-{
- FlatView *old_view = address_space_to_flatview(as);
- MemoryRegion *physmr = memory_region_get_flatview_root(as->root);
- FlatView *new_view = g_hash_table_lookup(flat_views, physmr);
-
- assert(new_view);
-
- if (old_view == new_view) {
- return;
- }
-
- if (old_view) {
- flatview_ref(old_view);
- }
-
- flatview_ref(new_view);
-
- if (!QTAILQ_EMPTY(&as->listeners)) {
- FlatView tmpview = { .nr = 0 }, *old_view2 = old_view;
-
- if (!old_view2) {
- old_view2 = &tmpview;
- }
- address_space_update_topology_pass(as, old_view2, new_view, false);
- address_space_update_topology_pass(as, old_view2, new_view, true);
- }
-
- /* Writes are protected by the BQL. */
- atomic_rcu_set(&as->current_map, new_view);
- if (old_view) {
- flatview_unref(old_view);
- }
-
- /* Note that all the old MemoryRegions are still alive up to this
- * point. This relieves most MemoryListeners from the need to
- * ref/unref the MemoryRegions they get---unless they use them
- * outside the iothread mutex, in which case precise reference
- * counting is necessary.
- */
- if (old_view) {
- flatview_unref(old_view);
- }
-}
-
-static void address_space_update_topology(AddressSpace *as)
-{
- MemoryRegion *physmr = memory_region_get_flatview_root(as->root);
-
- flatviews_init();
- if (!g_hash_table_lookup(flat_views, physmr)) {
- generate_memory_topology(physmr);
- }
- address_space_set_flatview(as);
-}
-
-void memory_region_transaction_begin(void)
-{
- qemu_flush_coalesced_mmio_buffer();
- ++memory_region_transaction_depth;
-}
-
-void memory_region_transaction_commit(void)
-{
- AddressSpace *as;
-
- assert(memory_region_transaction_depth);
- assert(qemu_mutex_iothread_locked());
-
- --memory_region_transaction_depth;
- if (!memory_region_transaction_depth) {
- if (memory_region_update_pending) {
- flatviews_reset();
-
- MEMORY_LISTENER_CALL_GLOBAL(begin, Forward);
-
- QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
- address_space_set_flatview(as);
- address_space_update_ioeventfds(as);
- }
- memory_region_update_pending = false;
- ioeventfd_update_pending = false;
- MEMORY_LISTENER_CALL_GLOBAL(commit, Forward);
- } else if (ioeventfd_update_pending) {
- QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
- address_space_update_ioeventfds(as);
- }
- ioeventfd_update_pending = false;
- }
- }
-}
-
-static void memory_region_destructor_none(MemoryRegion *mr)
-{
-}
-
-static void memory_region_destructor_ram(MemoryRegion *mr)
-{
- qemu_ram_free(mr->ram_block);
-}
-
-static bool memory_region_need_escape(char c)
-{
- return c == '/' || c == '[' || c == '\\' || c == ']';
-}
-
-static char *memory_region_escape_name(const char *name)
-{
- const char *p;
- char *escaped, *q;
- uint8_t c;
- size_t bytes = 0;
-
- for (p = name; *p; p++) {
- bytes += memory_region_need_escape(*p) ? 4 : 1;
- }
- if (bytes == p - name) {
- return g_memdup(name, bytes + 1);
- }
-
- escaped = g_malloc(bytes + 1);
- for (p = name, q = escaped; *p; p++) {
- c = *p;
- if (unlikely(memory_region_need_escape(c))) {
- *q++ = '\\';
- *q++ = 'x';
- *q++ = "0123456789abcdef"[c >> 4];
- c = "0123456789abcdef"[c & 15];
- }
- *q++ = c;
- }
- *q = 0;
- return escaped;
-}
-
-static void memory_region_do_init(MemoryRegion *mr,
- Object *owner,
- const char *name,
- uint64_t size)
-{
- mr->size = int128_make64(size);
- if (size == UINT64_MAX) {
- mr->size = int128_2_64();
- }
- mr->name = g_strdup(name);
- mr->owner = owner;
- mr->ram_block = NULL;
-
- if (name) {
- char *escaped_name = memory_region_escape_name(name);
- char *name_array = g_strdup_printf("%s[*]", escaped_name);
-
- if (!owner) {
- owner = container_get(qdev_get_machine(), "/unattached");
- }
-
- object_property_add_child(owner, name_array, OBJECT(mr));
- object_unref(OBJECT(mr));
- g_free(name_array);
- g_free(escaped_name);
- }
-}
-
-void memory_region_init(MemoryRegion *mr,
- Object *owner,
- const char *name,
- uint64_t size)
-{
- object_initialize(mr, sizeof(*mr), TYPE_MEMORY_REGION);
- memory_region_do_init(mr, owner, name, size);
-}
-
-static void memory_region_get_container(Object *obj, Visitor *v,
- const char *name, void *opaque,
- Error **errp)
-{
- MemoryRegion *mr = MEMORY_REGION(obj);
- char *path = (char *)"";
-
- if (mr->container) {
- path = object_get_canonical_path(OBJECT(mr->container));
- }
- visit_type_str(v, name, &path, errp);
- if (mr->container) {
- g_free(path);
- }
-}
-
-static Object *memory_region_resolve_container(Object *obj, void *opaque,
- const char *part)
-{
- MemoryRegion *mr = MEMORY_REGION(obj);
-
- return OBJECT(mr->container);
-}
-
-static void memory_region_get_priority(Object *obj, Visitor *v,
- const char *name, void *opaque,
- Error **errp)
-{
- MemoryRegion *mr = MEMORY_REGION(obj);
- int32_t value = mr->priority;
-
- visit_type_int32(v, name, &value, errp);
-}
-
-static void memory_region_get_size(Object *obj, Visitor *v, const char *name,
- void *opaque, Error **errp)
-{
- MemoryRegion *mr = MEMORY_REGION(obj);
- uint64_t value = memory_region_size(mr);
-
- visit_type_uint64(v, name, &value, errp);
-}
-
-static void memory_region_initfn(Object *obj)
-{
- MemoryRegion *mr = MEMORY_REGION(obj);
- ObjectProperty *op;
-
- mr->ops = &unassigned_mem_ops;
- mr->enabled = true;
- mr->romd_mode = true;
- mr->global_locking = true;
- mr->destructor = memory_region_destructor_none;
- QTAILQ_INIT(&mr->subregions);
- QTAILQ_INIT(&mr->coalesced);
-
- op = object_property_add(OBJECT(mr), "container",
- "link<" TYPE_MEMORY_REGION ">",
- memory_region_get_container,
- NULL, /* memory_region_set_container */
- NULL, NULL);
- op->resolve = memory_region_resolve_container;
-
- object_property_add_uint64_ptr(OBJECT(mr), "addr",
- &mr->addr, OBJ_PROP_FLAG_READ);
- object_property_add(OBJECT(mr), "priority", "uint32",
- memory_region_get_priority,
- NULL, /* memory_region_set_priority */
- NULL, NULL);
- object_property_add(OBJECT(mr), "size", "uint64",
- memory_region_get_size,
- NULL, /* memory_region_set_size, */
- NULL, NULL);
-}
-
-static void iommu_memory_region_initfn(Object *obj)
-{
- MemoryRegion *mr = MEMORY_REGION(obj);
-
- mr->is_iommu = true;
-}
-
-static uint64_t unassigned_mem_read(void *opaque, hwaddr addr,
- unsigned size)
-{
-#ifdef DEBUG_UNASSIGNED
- printf("Unassigned mem read " TARGET_FMT_plx "\n", addr);
-#endif
- return 0;
-}
-
-static void unassigned_mem_write(void *opaque, hwaddr addr,
- uint64_t val, unsigned size)
-{
-#ifdef DEBUG_UNASSIGNED
- printf("Unassigned mem write " TARGET_FMT_plx " = 0x%"PRIx64"\n", addr, val);
-#endif
-}
-
-static bool unassigned_mem_accepts(void *opaque, hwaddr addr,
- unsigned size, bool is_write,
- MemTxAttrs attrs)
-{
- return false;
-}
-
-const MemoryRegionOps unassigned_mem_ops = {
- .valid.accepts = unassigned_mem_accepts,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static uint64_t memory_region_ram_device_read(void *opaque,
- hwaddr addr, unsigned size)
-{
- MemoryRegion *mr = opaque;
- uint64_t data = (uint64_t)~0;
-
- switch (size) {
- case 1:
- data = *(uint8_t *)(mr->ram_block->host + addr);
- break;
- case 2:
- data = *(uint16_t *)(mr->ram_block->host + addr);
- break;
- case 4:
- data = *(uint32_t *)(mr->ram_block->host + addr);
- break;
- case 8:
- data = *(uint64_t *)(mr->ram_block->host + addr);
- break;
- }
-
- trace_memory_region_ram_device_read(get_cpu_index(), mr, addr, data, size);
-
- return data;
-}
-
-static void memory_region_ram_device_write(void *opaque, hwaddr addr,
- uint64_t data, unsigned size)
-{
- MemoryRegion *mr = opaque;
-
- trace_memory_region_ram_device_write(get_cpu_index(), mr, addr, data, size);
-
- switch (size) {
- case 1:
- *(uint8_t *)(mr->ram_block->host + addr) = (uint8_t)data;
- break;
- case 2:
- *(uint16_t *)(mr->ram_block->host + addr) = (uint16_t)data;
- break;
- case 4:
- *(uint32_t *)(mr->ram_block->host + addr) = (uint32_t)data;
- break;
- case 8:
- *(uint64_t *)(mr->ram_block->host + addr) = data;
- break;
- }
-}
-
-static const MemoryRegionOps ram_device_mem_ops = {
- .read = memory_region_ram_device_read,
- .write = memory_region_ram_device_write,
- .endianness = DEVICE_HOST_ENDIAN,
- .valid = {
- .min_access_size = 1,
- .max_access_size = 8,
- .unaligned = true,
- },
- .impl = {
- .min_access_size = 1,
- .max_access_size = 8,
- .unaligned = true,
- },
-};
-
-bool memory_region_access_valid(MemoryRegion *mr,
- hwaddr addr,
- unsigned size,
- bool is_write,
- MemTxAttrs attrs)
-{
- if (mr->ops->valid.accepts
- && !mr->ops->valid.accepts(mr->opaque, addr, size, is_write, attrs)) {
- return false;
- }
-
- if (!mr->ops->valid.unaligned && (addr & (size - 1))) {
- return false;
- }
-
- /* Treat zero as compatibility all valid */
- if (!mr->ops->valid.max_access_size) {
- return true;
- }
-
- if (size > mr->ops->valid.max_access_size
- || size < mr->ops->valid.min_access_size) {
- return false;
- }
- return true;
-}
-
-static MemTxResult memory_region_dispatch_read1(MemoryRegion *mr,
- hwaddr addr,
- uint64_t *pval,
- unsigned size,
- MemTxAttrs attrs)
-{
- *pval = 0;
-
- if (mr->ops->read) {
- return access_with_adjusted_size(addr, pval, size,
- mr->ops->impl.min_access_size,
- mr->ops->impl.max_access_size,
- memory_region_read_accessor,
- mr, attrs);
- } else {
- return access_with_adjusted_size(addr, pval, size,
- mr->ops->impl.min_access_size,
- mr->ops->impl.max_access_size,
- memory_region_read_with_attrs_accessor,
- mr, attrs);
- }
-}
-
-MemTxResult memory_region_dispatch_read(MemoryRegion *mr,
- hwaddr addr,
- uint64_t *pval,
- MemOp op,
- MemTxAttrs attrs)
-{
- unsigned size = memop_size(op);
- MemTxResult r;
-
- if (!memory_region_access_valid(mr, addr, size, false, attrs)) {
- *pval = unassigned_mem_read(mr, addr, size);
- return MEMTX_DECODE_ERROR;
- }
-
- r = memory_region_dispatch_read1(mr, addr, pval, size, attrs);
- adjust_endianness(mr, pval, op);
- return r;
-}
-
-/* Return true if an eventfd was signalled */
-static bool memory_region_dispatch_write_eventfds(MemoryRegion *mr,
- hwaddr addr,
- uint64_t data,
- unsigned size,
- MemTxAttrs attrs)
-{
- MemoryRegionIoeventfd ioeventfd = {
- .addr = addrrange_make(int128_make64(addr), int128_make64(size)),
- .data = data,
- };
- unsigned i;
-
- for (i = 0; i < mr->ioeventfd_nb; i++) {
- ioeventfd.match_data = mr->ioeventfds[i].match_data;
- ioeventfd.e = mr->ioeventfds[i].e;
-
- if (memory_region_ioeventfd_equal(&ioeventfd, &mr->ioeventfds[i])) {
- event_notifier_set(ioeventfd.e);
- return true;
- }
- }
-
- return false;
-}
-
-MemTxResult memory_region_dispatch_write(MemoryRegion *mr,
- hwaddr addr,
- uint64_t data,
- MemOp op,
- MemTxAttrs attrs)
-{
- unsigned size = memop_size(op);
-
- if (!memory_region_access_valid(mr, addr, size, true, attrs)) {
- unassigned_mem_write(mr, addr, data, size);
- return MEMTX_DECODE_ERROR;
- }
-
- adjust_endianness(mr, &data, op);
-
- if ((!kvm_eventfds_enabled()) &&
- memory_region_dispatch_write_eventfds(mr, addr, data, size, attrs)) {
- return MEMTX_OK;
- }
-
- if (mr->ops->write) {
- return access_with_adjusted_size(addr, &data, size,
- mr->ops->impl.min_access_size,
- mr->ops->impl.max_access_size,
- memory_region_write_accessor, mr,
- attrs);
- } else {
- return
- access_with_adjusted_size(addr, &data, size,
- mr->ops->impl.min_access_size,
- mr->ops->impl.max_access_size,
- memory_region_write_with_attrs_accessor,
- mr, attrs);
- }
-}
-
-void memory_region_init_io(MemoryRegion *mr,
- Object *owner,
- const MemoryRegionOps *ops,
- void *opaque,
- const char *name,
- uint64_t size)
-{
- memory_region_init(mr, owner, name, size);
- mr->ops = ops ? ops : &unassigned_mem_ops;
- mr->opaque = opaque;
- mr->terminates = true;
-}
-
-void memory_region_init_ram_nomigrate(MemoryRegion *mr,
- Object *owner,
- const char *name,
- uint64_t size,
- Error **errp)
-{
- memory_region_init_ram_shared_nomigrate(mr, owner, name, size, false, errp);
-}
-
-void memory_region_init_ram_shared_nomigrate(MemoryRegion *mr,
- Object *owner,
- const char *name,
- uint64_t size,
- bool share,
- Error **errp)
-{
- Error *err = NULL;
- memory_region_init(mr, owner, name, size);
- mr->ram = true;
- mr->terminates = true;
- mr->destructor = memory_region_destructor_ram;
- mr->ram_block = qemu_ram_alloc(size, share, mr, &err);
- mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
- if (err) {
- mr->size = int128_zero();
- object_unparent(OBJECT(mr));
- error_propagate(errp, err);
- }
-}
-
-void memory_region_init_resizeable_ram(MemoryRegion *mr,
- Object *owner,
- const char *name,
- uint64_t size,
- uint64_t max_size,
- void (*resized)(const char*,
- uint64_t length,
- void *host),
- Error **errp)
-{
- Error *err = NULL;
- memory_region_init(mr, owner, name, size);
- mr->ram = true;
- mr->terminates = true;
- mr->destructor = memory_region_destructor_ram;
- mr->ram_block = qemu_ram_alloc_resizeable(size, max_size, resized,
- mr, &err);
- mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
- if (err) {
- mr->size = int128_zero();
- object_unparent(OBJECT(mr));
- error_propagate(errp, err);
- }
-}
-
-#ifdef CONFIG_POSIX
-void memory_region_init_ram_from_file(MemoryRegion *mr,
- struct Object *owner,
- const char *name,
- uint64_t size,
- uint64_t align,
- uint32_t ram_flags,
- const char *path,
- Error **errp)
-{
- Error *err = NULL;
- memory_region_init(mr, owner, name, size);
- mr->ram = true;
- mr->terminates = true;
- mr->destructor = memory_region_destructor_ram;
- mr->align = align;
- mr->ram_block = qemu_ram_alloc_from_file(size, mr, ram_flags, path, &err);
- mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
- if (err) {
- mr->size = int128_zero();
- object_unparent(OBJECT(mr));
- error_propagate(errp, err);
- }
-}
-
-void memory_region_init_ram_from_fd(MemoryRegion *mr,
- struct Object *owner,
- const char *name,
- uint64_t size,
- bool share,
- int fd,
- Error **errp)
-{
- Error *err = NULL;
- memory_region_init(mr, owner, name, size);
- mr->ram = true;
- mr->terminates = true;
- mr->destructor = memory_region_destructor_ram;
- mr->ram_block = qemu_ram_alloc_from_fd(size, mr,
- share ? RAM_SHARED : 0,
- fd, &err);
- mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
- if (err) {
- mr->size = int128_zero();
- object_unparent(OBJECT(mr));
- error_propagate(errp, err);
- }
-}
-#endif
-
-void memory_region_init_ram_ptr(MemoryRegion *mr,
- Object *owner,
- const char *name,
- uint64_t size,
- void *ptr)
-{
- memory_region_init(mr, owner, name, size);
- mr->ram = true;
- mr->terminates = true;
- mr->destructor = memory_region_destructor_ram;
- mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
-
- /* qemu_ram_alloc_from_ptr cannot fail with ptr != NULL. */
- assert(ptr != NULL);
- mr->ram_block = qemu_ram_alloc_from_ptr(size, ptr, mr, &error_fatal);
-}
-
-void memory_region_init_ram_device_ptr(MemoryRegion *mr,
- Object *owner,
- const char *name,
- uint64_t size,
- void *ptr)
-{
- memory_region_init(mr, owner, name, size);
- mr->ram = true;
- mr->terminates = true;
- mr->ram_device = true;
- mr->ops = &ram_device_mem_ops;
- mr->opaque = mr;
- mr->destructor = memory_region_destructor_ram;
- mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
- /* qemu_ram_alloc_from_ptr cannot fail with ptr != NULL. */
- assert(ptr != NULL);
- mr->ram_block = qemu_ram_alloc_from_ptr(size, ptr, mr, &error_fatal);
-}
-
-void memory_region_init_alias(MemoryRegion *mr,
- Object *owner,
- const char *name,
- MemoryRegion *orig,
- hwaddr offset,
- uint64_t size)
-{
- memory_region_init(mr, owner, name, size);
- mr->alias = orig;
- mr->alias_offset = offset;
-}
-
-void memory_region_init_rom_nomigrate(MemoryRegion *mr,
- struct Object *owner,
- const char *name,
- uint64_t size,
- Error **errp)
-{
- memory_region_init_ram_shared_nomigrate(mr, owner, name, size, false, errp);
- mr->readonly = true;
-}
-
-void memory_region_init_rom_device_nomigrate(MemoryRegion *mr,
- Object *owner,
- const MemoryRegionOps *ops,
- void *opaque,
- const char *name,
- uint64_t size,
- Error **errp)
-{
- Error *err = NULL;
- assert(ops);
- memory_region_init(mr, owner, name, size);
- mr->ops = ops;
- mr->opaque = opaque;
- mr->terminates = true;
- mr->rom_device = true;
- mr->destructor = memory_region_destructor_ram;
- mr->ram_block = qemu_ram_alloc(size, false, mr, &err);
- if (err) {
- mr->size = int128_zero();
- object_unparent(OBJECT(mr));
- error_propagate(errp, err);
- }
-}
-
-void memory_region_init_iommu(void *_iommu_mr,
- size_t instance_size,
- const char *mrtypename,
- Object *owner,
- const char *name,
- uint64_t size)
-{
- struct IOMMUMemoryRegion *iommu_mr;
- struct MemoryRegion *mr;
-
- object_initialize(_iommu_mr, instance_size, mrtypename);
- mr = MEMORY_REGION(_iommu_mr);
- memory_region_do_init(mr, owner, name, size);
- iommu_mr = IOMMU_MEMORY_REGION(mr);
- mr->terminates = true; /* then re-forwards */
- QLIST_INIT(&iommu_mr->iommu_notify);
- iommu_mr->iommu_notify_flags = IOMMU_NOTIFIER_NONE;
-}
-
-static void memory_region_finalize(Object *obj)
-{
- MemoryRegion *mr = MEMORY_REGION(obj);
-
- assert(!mr->container);
-
- /* We know the region is not visible in any address space (it
- * does not have a container and cannot be a root either because
- * it has no references, so we can blindly clear mr->enabled.
- * memory_region_set_enabled instead could trigger a transaction
- * and cause an infinite loop.
- */
- mr->enabled = false;
- memory_region_transaction_begin();
- while (!QTAILQ_EMPTY(&mr->subregions)) {
- MemoryRegion *subregion = QTAILQ_FIRST(&mr->subregions);
- memory_region_del_subregion(mr, subregion);
- }
- memory_region_transaction_commit();
-
- mr->destructor(mr);
- memory_region_clear_coalescing(mr);
- g_free((char *)mr->name);
- g_free(mr->ioeventfds);
-}
-
-Object *memory_region_owner(MemoryRegion *mr)
-{
- Object *obj = OBJECT(mr);
- return obj->parent;
-}
-
-void memory_region_ref(MemoryRegion *mr)
-{
- /* MMIO callbacks most likely will access data that belongs
- * to the owner, hence the need to ref/unref the owner whenever
- * the memory region is in use.
- *
- * The memory region is a child of its owner. As long as the
- * owner doesn't call unparent itself on the memory region,
- * ref-ing the owner will also keep the memory region alive.
- * Memory regions without an owner are supposed to never go away;
- * we do not ref/unref them because it slows down DMA sensibly.
- */
- if (mr && mr->owner) {
- object_ref(mr->owner);
- }
-}
-
-void memory_region_unref(MemoryRegion *mr)
-{
- if (mr && mr->owner) {
- object_unref(mr->owner);
- }
-}
-
-uint64_t memory_region_size(MemoryRegion *mr)
-{
- if (int128_eq(mr->size, int128_2_64())) {
- return UINT64_MAX;
- }
- return int128_get64(mr->size);
-}
-
-const char *memory_region_name(const MemoryRegion *mr)
-{
- if (!mr->name) {
- ((MemoryRegion *)mr)->name =
- object_get_canonical_path_component(OBJECT(mr));
- }
- return mr->name;
-}
-
-bool memory_region_is_ram_device(MemoryRegion *mr)
-{
- return mr->ram_device;
-}
-
-uint8_t memory_region_get_dirty_log_mask(MemoryRegion *mr)
-{
- uint8_t mask = mr->dirty_log_mask;
- if (global_dirty_log && mr->ram_block) {
- mask |= (1 << DIRTY_MEMORY_MIGRATION);
- }
- return mask;
-}
-
-bool memory_region_is_logging(MemoryRegion *mr, uint8_t client)
-{
- return memory_region_get_dirty_log_mask(mr) & (1 << client);
-}
-
-static int memory_region_update_iommu_notify_flags(IOMMUMemoryRegion *iommu_mr,
- Error **errp)
-{
- IOMMUNotifierFlag flags = IOMMU_NOTIFIER_NONE;
- IOMMUNotifier *iommu_notifier;
- IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr);
- int ret = 0;
-
- IOMMU_NOTIFIER_FOREACH(iommu_notifier, iommu_mr) {
- flags |= iommu_notifier->notifier_flags;
- }
-
- if (flags != iommu_mr->iommu_notify_flags && imrc->notify_flag_changed) {
- ret = imrc->notify_flag_changed(iommu_mr,
- iommu_mr->iommu_notify_flags,
- flags, errp);
- }
-
- if (!ret) {
- iommu_mr->iommu_notify_flags = flags;
- }
- return ret;
-}
-
-int memory_region_register_iommu_notifier(MemoryRegion *mr,
- IOMMUNotifier *n, Error **errp)
-{
- IOMMUMemoryRegion *iommu_mr;
- int ret;
-
- if (mr->alias) {
- return memory_region_register_iommu_notifier(mr->alias, n, errp);
- }
-
- /* We need to register for at least one bitfield */
- iommu_mr = IOMMU_MEMORY_REGION(mr);
- assert(n->notifier_flags != IOMMU_NOTIFIER_NONE);
- assert(n->start <= n->end);
- assert(n->iommu_idx >= 0 &&
- n->iommu_idx < memory_region_iommu_num_indexes(iommu_mr));
-
- QLIST_INSERT_HEAD(&iommu_mr->iommu_notify, n, node);
- ret = memory_region_update_iommu_notify_flags(iommu_mr, errp);
- if (ret) {
- QLIST_REMOVE(n, node);
- }
- return ret;
-}
-
-uint64_t memory_region_iommu_get_min_page_size(IOMMUMemoryRegion *iommu_mr)
-{
- IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr);
-
- if (imrc->get_min_page_size) {
- return imrc->get_min_page_size(iommu_mr);
- }
- return TARGET_PAGE_SIZE;
-}
-
-void memory_region_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n)
-{
- MemoryRegion *mr = MEMORY_REGION(iommu_mr);
- IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr);
- hwaddr addr, granularity;
- IOMMUTLBEntry iotlb;
-
- /* If the IOMMU has its own replay callback, override */
- if (imrc->replay) {
- imrc->replay(iommu_mr, n);
- return;
- }
-
- granularity = memory_region_iommu_get_min_page_size(iommu_mr);
-
- for (addr = 0; addr < memory_region_size(mr); addr += granularity) {
- iotlb = imrc->translate(iommu_mr, addr, IOMMU_NONE, n->iommu_idx);
- if (iotlb.perm != IOMMU_NONE) {
- n->notify(n, &iotlb);
- }
-
- /* if (2^64 - MR size) < granularity, it's possible to get an
- * infinite loop here. This should catch such a wraparound */
- if ((addr + granularity) < addr) {
- break;
- }
- }
-}
-
-void memory_region_unregister_iommu_notifier(MemoryRegion *mr,
- IOMMUNotifier *n)
-{
- IOMMUMemoryRegion *iommu_mr;
-
- if (mr->alias) {
- memory_region_unregister_iommu_notifier(mr->alias, n);
- return;
- }
- QLIST_REMOVE(n, node);
- iommu_mr = IOMMU_MEMORY_REGION(mr);
- memory_region_update_iommu_notify_flags(iommu_mr, NULL);
-}
-
-void memory_region_notify_one(IOMMUNotifier *notifier,
- IOMMUTLBEntry *entry)
-{
- IOMMUNotifierFlag request_flags;
- hwaddr entry_end = entry->iova + entry->addr_mask;
-
- /*
- * Skip the notification if the notification does not overlap
- * with registered range.
- */
- if (notifier->start > entry_end || notifier->end < entry->iova) {
- return;
- }
-
- assert(entry->iova >= notifier->start && entry_end <= notifier->end);
-
- if (entry->perm & IOMMU_RW) {
- request_flags = IOMMU_NOTIFIER_MAP;
- } else {
- request_flags = IOMMU_NOTIFIER_UNMAP;
- }
-
- if (notifier->notifier_flags & request_flags) {
- notifier->notify(notifier, entry);
- }
-}
-
-void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr,
- int iommu_idx,
- IOMMUTLBEntry entry)
-{
- IOMMUNotifier *iommu_notifier;
-
- assert(memory_region_is_iommu(MEMORY_REGION(iommu_mr)));
-
- IOMMU_NOTIFIER_FOREACH(iommu_notifier, iommu_mr) {
- if (iommu_notifier->iommu_idx == iommu_idx) {
- memory_region_notify_one(iommu_notifier, &entry);
- }
- }
-}
-
-int memory_region_iommu_get_attr(IOMMUMemoryRegion *iommu_mr,
- enum IOMMUMemoryRegionAttr attr,
- void *data)
-{
- IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr);
-
- if (!imrc->get_attr) {
- return -EINVAL;
- }
-
- return imrc->get_attr(iommu_mr, attr, data);
-}
-
-int memory_region_iommu_attrs_to_index(IOMMUMemoryRegion *iommu_mr,
- MemTxAttrs attrs)
-{
- IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr);
-
- if (!imrc->attrs_to_index) {
- return 0;
- }
-
- return imrc->attrs_to_index(iommu_mr, attrs);
-}
-
-int memory_region_iommu_num_indexes(IOMMUMemoryRegion *iommu_mr)
-{
- IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr);
-
- if (!imrc->num_indexes) {
- return 1;
- }
-
- return imrc->num_indexes(iommu_mr);
-}
-
-void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client)
-{
- uint8_t mask = 1 << client;
- uint8_t old_logging;
-
- assert(client == DIRTY_MEMORY_VGA);
- old_logging = mr->vga_logging_count;
- mr->vga_logging_count += log ? 1 : -1;
- if (!!old_logging == !!mr->vga_logging_count) {
- return;
- }
-
- memory_region_transaction_begin();
- mr->dirty_log_mask = (mr->dirty_log_mask & ~mask) | (log * mask);
- memory_region_update_pending |= mr->enabled;
- memory_region_transaction_commit();
-}
-
-void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr,
- hwaddr size)
-{
- assert(mr->ram_block);
- cpu_physical_memory_set_dirty_range(memory_region_get_ram_addr(mr) + addr,
- size,
- memory_region_get_dirty_log_mask(mr));
-}
-
-static void memory_region_sync_dirty_bitmap(MemoryRegion *mr)
-{
- MemoryListener *listener;
- AddressSpace *as;
- FlatView *view;
- FlatRange *fr;
-
- /* If the same address space has multiple log_sync listeners, we
- * visit that address space's FlatView multiple times. But because
- * log_sync listeners are rare, it's still cheaper than walking each
- * address space once.
- */
- QTAILQ_FOREACH(listener, &memory_listeners, link) {
- if (!listener->log_sync) {
- continue;
- }
- as = listener->address_space;
- view = address_space_get_flatview(as);
- FOR_EACH_FLAT_RANGE(fr, view) {
- if (fr->dirty_log_mask && (!mr || fr->mr == mr)) {
- MemoryRegionSection mrs = section_from_flat_range(fr, view);
- listener->log_sync(listener, &mrs);
- }
- }
- flatview_unref(view);
- }
-}
-
-void memory_region_clear_dirty_bitmap(MemoryRegion *mr, hwaddr start,
- hwaddr len)
-{
- MemoryRegionSection mrs;
- MemoryListener *listener;
- AddressSpace *as;
- FlatView *view;
- FlatRange *fr;
- hwaddr sec_start, sec_end, sec_size;
-
- QTAILQ_FOREACH(listener, &memory_listeners, link) {
- if (!listener->log_clear) {
- continue;
- }
- as = listener->address_space;
- view = address_space_get_flatview(as);
- FOR_EACH_FLAT_RANGE(fr, view) {
- if (!fr->dirty_log_mask || fr->mr != mr) {
- /*
- * Clear dirty bitmap operation only applies to those
- * regions whose dirty logging is at least enabled
- */
- continue;
- }
-
- mrs = section_from_flat_range(fr, view);
-
- sec_start = MAX(mrs.offset_within_region, start);
- sec_end = mrs.offset_within_region + int128_get64(mrs.size);
- sec_end = MIN(sec_end, start + len);
-
- if (sec_start >= sec_end) {
- /*
- * If this memory region section has no intersection
- * with the requested range, skip.
- */
- continue;
- }
-
- /* Valid case; shrink the section if needed */
- mrs.offset_within_address_space +=
- sec_start - mrs.offset_within_region;
- mrs.offset_within_region = sec_start;
- sec_size = sec_end - sec_start;
- mrs.size = int128_make64(sec_size);
- listener->log_clear(listener, &mrs);
- }
- flatview_unref(view);
- }
-}
-
-DirtyBitmapSnapshot *memory_region_snapshot_and_clear_dirty(MemoryRegion *mr,
- hwaddr addr,
- hwaddr size,
- unsigned client)
-{
- DirtyBitmapSnapshot *snapshot;
- assert(mr->ram_block);
- memory_region_sync_dirty_bitmap(mr);
- snapshot = cpu_physical_memory_snapshot_and_clear_dirty(mr, addr, size, client);
- memory_global_after_dirty_log_sync();
- return snapshot;
-}
-
-bool memory_region_snapshot_get_dirty(MemoryRegion *mr, DirtyBitmapSnapshot *snap,
- hwaddr addr, hwaddr size)
-{
- assert(mr->ram_block);
- return cpu_physical_memory_snapshot_get_dirty(snap,
- memory_region_get_ram_addr(mr) + addr, size);
-}
-
-void memory_region_set_readonly(MemoryRegion *mr, bool readonly)
-{
- if (mr->readonly != readonly) {
- memory_region_transaction_begin();
- mr->readonly = readonly;
- memory_region_update_pending |= mr->enabled;
- memory_region_transaction_commit();
- }
-}
-
-void memory_region_set_nonvolatile(MemoryRegion *mr, bool nonvolatile)
-{
- if (mr->nonvolatile != nonvolatile) {
- memory_region_transaction_begin();
- mr->nonvolatile = nonvolatile;
- memory_region_update_pending |= mr->enabled;
- memory_region_transaction_commit();
- }
-}
-
-void memory_region_rom_device_set_romd(MemoryRegion *mr, bool romd_mode)
-{
- if (mr->romd_mode != romd_mode) {
- memory_region_transaction_begin();
- mr->romd_mode = romd_mode;
- memory_region_update_pending |= mr->enabled;
- memory_region_transaction_commit();
- }
-}
-
-void memory_region_reset_dirty(MemoryRegion *mr, hwaddr addr,
- hwaddr size, unsigned client)
-{
- assert(mr->ram_block);
- cpu_physical_memory_test_and_clear_dirty(
- memory_region_get_ram_addr(mr) + addr, size, client);
-}
-
-int memory_region_get_fd(MemoryRegion *mr)
-{
- int fd;
-
- RCU_READ_LOCK_GUARD();
- while (mr->alias) {
- mr = mr->alias;
- }
- fd = mr->ram_block->fd;
-
- return fd;
-}
-
-void *memory_region_get_ram_ptr(MemoryRegion *mr)
-{
- void *ptr;
- uint64_t offset = 0;
-
- RCU_READ_LOCK_GUARD();
- while (mr->alias) {
- offset += mr->alias_offset;
- mr = mr->alias;
- }
- assert(mr->ram_block);
- ptr = qemu_map_ram_ptr(mr->ram_block, offset);
-
- return ptr;
-}
-
-MemoryRegion *memory_region_from_host(void *ptr, ram_addr_t *offset)
-{
- RAMBlock *block;
-
- block = qemu_ram_block_from_host(ptr, false, offset);
- if (!block) {
- return NULL;
- }
-
- return block->mr;
-}
-
-ram_addr_t memory_region_get_ram_addr(MemoryRegion *mr)
-{
- return mr->ram_block ? mr->ram_block->offset : RAM_ADDR_INVALID;
-}
-
-void memory_region_ram_resize(MemoryRegion *mr, ram_addr_t newsize, Error **errp)
-{
- assert(mr->ram_block);
-
- qemu_ram_resize(mr->ram_block, newsize, errp);
-}
-
-void memory_region_msync(MemoryRegion *mr, hwaddr addr, hwaddr size)
-{
- if (mr->ram_block) {
- qemu_ram_msync(mr->ram_block, addr, size);
- }
-}
-
-void memory_region_writeback(MemoryRegion *mr, hwaddr addr, hwaddr size)
-{
- /*
- * Might be extended case needed to cover
- * different types of memory regions
- */
- if (mr->dirty_log_mask) {
- memory_region_msync(mr, addr, size);
- }
-}
-
-/*
- * Call proper memory listeners about the change on the newly
- * added/removed CoalescedMemoryRange.
- */
-static void memory_region_update_coalesced_range(MemoryRegion *mr,
- CoalescedMemoryRange *cmr,
- bool add)
-{
- AddressSpace *as;
- FlatView *view;
- FlatRange *fr;
-
- QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
- view = address_space_get_flatview(as);
- FOR_EACH_FLAT_RANGE(fr, view) {
- if (fr->mr == mr) {
- flat_range_coalesced_io_notify(fr, as, cmr, add);
- }
- }
- flatview_unref(view);
- }
-}
-
-void memory_region_set_coalescing(MemoryRegion *mr)
-{
- memory_region_clear_coalescing(mr);
- memory_region_add_coalescing(mr, 0, int128_get64(mr->size));
-}
-
-void memory_region_add_coalescing(MemoryRegion *mr,
- hwaddr offset,
- uint64_t size)
-{
- CoalescedMemoryRange *cmr = g_malloc(sizeof(*cmr));
-
- cmr->addr = addrrange_make(int128_make64(offset), int128_make64(size));
- QTAILQ_INSERT_TAIL(&mr->coalesced, cmr, link);
- memory_region_update_coalesced_range(mr, cmr, true);
- memory_region_set_flush_coalesced(mr);
-}
-
-void memory_region_clear_coalescing(MemoryRegion *mr)
-{
- CoalescedMemoryRange *cmr;
-
- if (QTAILQ_EMPTY(&mr->coalesced)) {
- return;
- }
-
- qemu_flush_coalesced_mmio_buffer();
- mr->flush_coalesced_mmio = false;
-
- while (!QTAILQ_EMPTY(&mr->coalesced)) {
- cmr = QTAILQ_FIRST(&mr->coalesced);
- QTAILQ_REMOVE(&mr->coalesced, cmr, link);
- memory_region_update_coalesced_range(mr, cmr, false);
- g_free(cmr);
- }
-}
-
-void memory_region_set_flush_coalesced(MemoryRegion *mr)
-{
- mr->flush_coalesced_mmio = true;
-}
-
-void memory_region_clear_flush_coalesced(MemoryRegion *mr)
-{
- qemu_flush_coalesced_mmio_buffer();
- if (QTAILQ_EMPTY(&mr->coalesced)) {
- mr->flush_coalesced_mmio = false;
- }
-}
-
-void memory_region_clear_global_locking(MemoryRegion *mr)
-{
- mr->global_locking = false;
-}
-
-static bool userspace_eventfd_warning;
-
-void memory_region_add_eventfd(MemoryRegion *mr,
- hwaddr addr,
- unsigned size,
- bool match_data,
- uint64_t data,
- EventNotifier *e)
-{
- MemoryRegionIoeventfd mrfd = {
- .addr.start = int128_make64(addr),
- .addr.size = int128_make64(size),
- .match_data = match_data,
- .data = data,
- .e = e,
- };
- unsigned i;
-
- if (kvm_enabled() && (!(kvm_eventfds_enabled() ||
- userspace_eventfd_warning))) {
- userspace_eventfd_warning = true;
- error_report("Using eventfd without MMIO binding in KVM. "
- "Suboptimal performance expected");
- }
-
- if (size) {
- adjust_endianness(mr, &mrfd.data, size_memop(size) | MO_TE);
- }
- memory_region_transaction_begin();
- for (i = 0; i < mr->ioeventfd_nb; ++i) {
- if (memory_region_ioeventfd_before(&mrfd, &mr->ioeventfds[i])) {
- break;
- }
- }
- ++mr->ioeventfd_nb;
- mr->ioeventfds = g_realloc(mr->ioeventfds,
- sizeof(*mr->ioeventfds) * mr->ioeventfd_nb);
- memmove(&mr->ioeventfds[i+1], &mr->ioeventfds[i],
- sizeof(*mr->ioeventfds) * (mr->ioeventfd_nb-1 - i));
- mr->ioeventfds[i] = mrfd;
- ioeventfd_update_pending |= mr->enabled;
- memory_region_transaction_commit();
-}
-
-void memory_region_del_eventfd(MemoryRegion *mr,
- hwaddr addr,
- unsigned size,
- bool match_data,
- uint64_t data,
- EventNotifier *e)
-{
- MemoryRegionIoeventfd mrfd = {
- .addr.start = int128_make64(addr),
- .addr.size = int128_make64(size),
- .match_data = match_data,
- .data = data,
- .e = e,
- };
- unsigned i;
-
- if (size) {
- adjust_endianness(mr, &mrfd.data, size_memop(size) | MO_TE);
- }
- memory_region_transaction_begin();
- for (i = 0; i < mr->ioeventfd_nb; ++i) {
- if (memory_region_ioeventfd_equal(&mrfd, &mr->ioeventfds[i])) {
- break;
- }
- }
- assert(i != mr->ioeventfd_nb);
- memmove(&mr->ioeventfds[i], &mr->ioeventfds[i+1],
- sizeof(*mr->ioeventfds) * (mr->ioeventfd_nb - (i+1)));
- --mr->ioeventfd_nb;
- mr->ioeventfds = g_realloc(mr->ioeventfds,
- sizeof(*mr->ioeventfds)*mr->ioeventfd_nb + 1);
- ioeventfd_update_pending |= mr->enabled;
- memory_region_transaction_commit();
-}
-
-static void memory_region_update_container_subregions(MemoryRegion *subregion)
-{
- MemoryRegion *mr = subregion->container;
- MemoryRegion *other;
-
- memory_region_transaction_begin();
-
- memory_region_ref(subregion);
- QTAILQ_FOREACH(other, &mr->subregions, subregions_link) {
- if (subregion->priority >= other->priority) {
- QTAILQ_INSERT_BEFORE(other, subregion, subregions_link);
- goto done;
- }
- }
- QTAILQ_INSERT_TAIL(&mr->subregions, subregion, subregions_link);
-done:
- memory_region_update_pending |= mr->enabled && subregion->enabled;
- memory_region_transaction_commit();
-}
-
-static void memory_region_add_subregion_common(MemoryRegion *mr,
- hwaddr offset,
- MemoryRegion *subregion)
-{
- assert(!subregion->container);
- subregion->container = mr;
- subregion->addr = offset;
- memory_region_update_container_subregions(subregion);
-}
-
-void memory_region_add_subregion(MemoryRegion *mr,
- hwaddr offset,
- MemoryRegion *subregion)
-{
- subregion->priority = 0;
- memory_region_add_subregion_common(mr, offset, subregion);
-}
-
-void memory_region_add_subregion_overlap(MemoryRegion *mr,
- hwaddr offset,
- MemoryRegion *subregion,
- int priority)
-{
- subregion->priority = priority;
- memory_region_add_subregion_common(mr, offset, subregion);
-}
-
-void memory_region_del_subregion(MemoryRegion *mr,
- MemoryRegion *subregion)
-{
- memory_region_transaction_begin();
- assert(subregion->container == mr);
- subregion->container = NULL;
- QTAILQ_REMOVE(&mr->subregions, subregion, subregions_link);
- memory_region_unref(subregion);
- memory_region_update_pending |= mr->enabled && subregion->enabled;
- memory_region_transaction_commit();
-}
-
-void memory_region_set_enabled(MemoryRegion *mr, bool enabled)
-{
- if (enabled == mr->enabled) {
- return;
- }
- memory_region_transaction_begin();
- mr->enabled = enabled;
- memory_region_update_pending = true;
- memory_region_transaction_commit();
-}
-
-void memory_region_set_size(MemoryRegion *mr, uint64_t size)
-{
- Int128 s = int128_make64(size);
-
- if (size == UINT64_MAX) {
- s = int128_2_64();
- }
- if (int128_eq(s, mr->size)) {
- return;
- }
- memory_region_transaction_begin();
- mr->size = s;
- memory_region_update_pending = true;
- memory_region_transaction_commit();
-}
-
-static void memory_region_readd_subregion(MemoryRegion *mr)
-{
- MemoryRegion *container = mr->container;
-
- if (container) {
- memory_region_transaction_begin();
- memory_region_ref(mr);
- memory_region_del_subregion(container, mr);
- mr->container = container;
- memory_region_update_container_subregions(mr);
- memory_region_unref(mr);
- memory_region_transaction_commit();
- }
-}
-
-void memory_region_set_address(MemoryRegion *mr, hwaddr addr)
-{
- if (addr != mr->addr) {
- mr->addr = addr;
- memory_region_readd_subregion(mr);
- }
-}
-
-void memory_region_set_alias_offset(MemoryRegion *mr, hwaddr offset)
-{
- assert(mr->alias);
-
- if (offset == mr->alias_offset) {
- return;
- }
-
- memory_region_transaction_begin();
- mr->alias_offset = offset;
- memory_region_update_pending |= mr->enabled;
- memory_region_transaction_commit();
-}
-
-uint64_t memory_region_get_alignment(const MemoryRegion *mr)
-{
- return mr->align;
-}
-
-static int cmp_flatrange_addr(const void *addr_, const void *fr_)
-{
- const AddrRange *addr = addr_;
- const FlatRange *fr = fr_;
-
- if (int128_le(addrrange_end(*addr), fr->addr.start)) {
- return -1;
- } else if (int128_ge(addr->start, addrrange_end(fr->addr))) {
- return 1;
- }
- return 0;
-}
-
-static FlatRange *flatview_lookup(FlatView *view, AddrRange addr)
-{
- return bsearch(&addr, view->ranges, view->nr,
- sizeof(FlatRange), cmp_flatrange_addr);
-}
-
-bool memory_region_is_mapped(MemoryRegion *mr)
-{
- return mr->container ? true : false;
-}
-
-/* Same as memory_region_find, but it does not add a reference to the
- * returned region. It must be called from an RCU critical section.
- */
-static MemoryRegionSection memory_region_find_rcu(MemoryRegion *mr,
- hwaddr addr, uint64_t size)
-{
- MemoryRegionSection ret = { .mr = NULL };
- MemoryRegion *root;
- AddressSpace *as;
- AddrRange range;
- FlatView *view;
- FlatRange *fr;
-
- addr += mr->addr;
- for (root = mr; root->container; ) {
- root = root->container;
- addr += root->addr;
- }
-
- as = memory_region_to_address_space(root);
- if (!as) {
- return ret;
- }
- range = addrrange_make(int128_make64(addr), int128_make64(size));
-
- view = address_space_to_flatview(as);
- fr = flatview_lookup(view, range);
- if (!fr) {
- return ret;
- }
-
- while (fr > view->ranges && addrrange_intersects(fr[-1].addr, range)) {
- --fr;
- }
-
- ret.mr = fr->mr;
- ret.fv = view;
- range = addrrange_intersection(range, fr->addr);
- ret.offset_within_region = fr->offset_in_region;
- ret.offset_within_region += int128_get64(int128_sub(range.start,
- fr->addr.start));
- ret.size = range.size;
- ret.offset_within_address_space = int128_get64(range.start);
- ret.readonly = fr->readonly;
- ret.nonvolatile = fr->nonvolatile;
- return ret;
-}
-
-MemoryRegionSection memory_region_find(MemoryRegion *mr,
- hwaddr addr, uint64_t size)
-{
- MemoryRegionSection ret;
- RCU_READ_LOCK_GUARD();
- ret = memory_region_find_rcu(mr, addr, size);
- if (ret.mr) {
- memory_region_ref(ret.mr);
- }
- return ret;
-}
-
-bool memory_region_present(MemoryRegion *container, hwaddr addr)
-{
- MemoryRegion *mr;
-
- RCU_READ_LOCK_GUARD();
- mr = memory_region_find_rcu(container, addr, 1).mr;
- return mr && mr != container;
-}
-
-void memory_global_dirty_log_sync(void)
-{
- memory_region_sync_dirty_bitmap(NULL);
-}
-
-void memory_global_after_dirty_log_sync(void)
-{
- MEMORY_LISTENER_CALL_GLOBAL(log_global_after_sync, Forward);
-}
-
-static VMChangeStateEntry *vmstate_change;
-
-void memory_global_dirty_log_start(void)
-{
- if (vmstate_change) {
- qemu_del_vm_change_state_handler(vmstate_change);
- vmstate_change = NULL;
- }
-
- global_dirty_log = true;
-
- MEMORY_LISTENER_CALL_GLOBAL(log_global_start, Forward);
-
- /* Refresh DIRTY_MEMORY_MIGRATION bit. */
- memory_region_transaction_begin();
- memory_region_update_pending = true;
- memory_region_transaction_commit();
-}
-
-static void memory_global_dirty_log_do_stop(void)
-{
- global_dirty_log = false;
-
- /* Refresh DIRTY_MEMORY_MIGRATION bit. */
- memory_region_transaction_begin();
- memory_region_update_pending = true;
- memory_region_transaction_commit();
-
- MEMORY_LISTENER_CALL_GLOBAL(log_global_stop, Reverse);
-}
-
-static void memory_vm_change_state_handler(void *opaque, int running,
- RunState state)
-{
- if (running) {
- memory_global_dirty_log_do_stop();
-
- if (vmstate_change) {
- qemu_del_vm_change_state_handler(vmstate_change);
- vmstate_change = NULL;
- }
- }
-}
-
-void memory_global_dirty_log_stop(void)
-{
- if (!runstate_is_running()) {
- if (vmstate_change) {
- return;
- }
- vmstate_change = qemu_add_vm_change_state_handler(
- memory_vm_change_state_handler, NULL);
- return;
- }
-
- memory_global_dirty_log_do_stop();
-}
-
-static void listener_add_address_space(MemoryListener *listener,
- AddressSpace *as)
-{
- FlatView *view;
- FlatRange *fr;
-
- if (listener->begin) {
- listener->begin(listener);
- }
- if (global_dirty_log) {
- if (listener->log_global_start) {
- listener->log_global_start(listener);
- }
- }
-
- view = address_space_get_flatview(as);
- FOR_EACH_FLAT_RANGE(fr, view) {
- MemoryRegionSection section = section_from_flat_range(fr, view);
-
- if (listener->region_add) {
- listener->region_add(listener, §ion);
- }
- if (fr->dirty_log_mask && listener->log_start) {
- listener->log_start(listener, §ion, 0, fr->dirty_log_mask);
- }
- }
- if (listener->commit) {
- listener->commit(listener);
- }
- flatview_unref(view);
-}
-
-static void listener_del_address_space(MemoryListener *listener,
- AddressSpace *as)
-{
- FlatView *view;
- FlatRange *fr;
-
- if (listener->begin) {
- listener->begin(listener);
- }
- view = address_space_get_flatview(as);
- FOR_EACH_FLAT_RANGE(fr, view) {
- MemoryRegionSection section = section_from_flat_range(fr, view);
-
- if (fr->dirty_log_mask && listener->log_stop) {
- listener->log_stop(listener, §ion, fr->dirty_log_mask, 0);
- }
- if (listener->region_del) {
- listener->region_del(listener, §ion);
- }
- }
- if (listener->commit) {
- listener->commit(listener);
- }
- flatview_unref(view);
-}
-
-void memory_listener_register(MemoryListener *listener, AddressSpace *as)
-{
- MemoryListener *other = NULL;
-
- listener->address_space = as;
- if (QTAILQ_EMPTY(&memory_listeners)
- || listener->priority >= QTAILQ_LAST(&memory_listeners)->priority) {
- QTAILQ_INSERT_TAIL(&memory_listeners, listener, link);
- } else {
- QTAILQ_FOREACH(other, &memory_listeners, link) {
- if (listener->priority < other->priority) {
- break;
- }
- }
- QTAILQ_INSERT_BEFORE(other, listener, link);
- }
-
- if (QTAILQ_EMPTY(&as->listeners)
- || listener->priority >= QTAILQ_LAST(&as->listeners)->priority) {
- QTAILQ_INSERT_TAIL(&as->listeners, listener, link_as);
- } else {
- QTAILQ_FOREACH(other, &as->listeners, link_as) {
- if (listener->priority < other->priority) {
- break;
- }
- }
- QTAILQ_INSERT_BEFORE(other, listener, link_as);
- }
-
- listener_add_address_space(listener, as);
-}
-
-void memory_listener_unregister(MemoryListener *listener)
-{
- if (!listener->address_space) {
- return;
- }
-
- listener_del_address_space(listener, listener->address_space);
- QTAILQ_REMOVE(&memory_listeners, listener, link);
- QTAILQ_REMOVE(&listener->address_space->listeners, listener, link_as);
- listener->address_space = NULL;
-}
-
-void address_space_remove_listeners(AddressSpace *as)
-{
- while (!QTAILQ_EMPTY(&as->listeners)) {
- memory_listener_unregister(QTAILQ_FIRST(&as->listeners));
- }
-}
-
-void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name)
-{
- memory_region_ref(root);
- as->root = root;
- as->current_map = NULL;
- as->ioeventfd_nb = 0;
- as->ioeventfds = NULL;
- QTAILQ_INIT(&as->listeners);
- QTAILQ_INSERT_TAIL(&address_spaces, as, address_spaces_link);
- as->name = g_strdup(name ? name : "anonymous");
- address_space_update_topology(as);
- address_space_update_ioeventfds(as);
-}
-
-static void do_address_space_destroy(AddressSpace *as)
-{
- assert(QTAILQ_EMPTY(&as->listeners));
-
- flatview_unref(as->current_map);
- g_free(as->name);
- g_free(as->ioeventfds);
- memory_region_unref(as->root);
-}
-
-void address_space_destroy(AddressSpace *as)
-{
- MemoryRegion *root = as->root;
-
- /* Flush out anything from MemoryListeners listening in on this */
- memory_region_transaction_begin();
- as->root = NULL;
- memory_region_transaction_commit();
- QTAILQ_REMOVE(&address_spaces, as, address_spaces_link);
-
- /* At this point, as->dispatch and as->current_map are dummy
- * entries that the guest should never use. Wait for the old
- * values to expire before freeing the data.
- */
- as->root = root;
- call_rcu(as, do_address_space_destroy, rcu);
-}
-
-static const char *memory_region_type(MemoryRegion *mr)
-{
- if (mr->alias) {
- return memory_region_type(mr->alias);
- }
- if (memory_region_is_ram_device(mr)) {
- return "ramd";
- } else if (memory_region_is_romd(mr)) {
- return "romd";
- } else if (memory_region_is_rom(mr)) {
- return "rom";
- } else if (memory_region_is_ram(mr)) {
- return "ram";
- } else {
- return "i/o";
- }
-}
-
-typedef struct MemoryRegionList MemoryRegionList;
-
-struct MemoryRegionList {
- const MemoryRegion *mr;
- QTAILQ_ENTRY(MemoryRegionList) mrqueue;
-};
-
-typedef QTAILQ_HEAD(, MemoryRegionList) MemoryRegionListHead;
-
-#define MR_SIZE(size) (int128_nz(size) ? (hwaddr)int128_get64( \
- int128_sub((size), int128_one())) : 0)
-#define MTREE_INDENT " "
-
-static void mtree_expand_owner(const char *label, Object *obj)
-{
- DeviceState *dev = (DeviceState *) object_dynamic_cast(obj, TYPE_DEVICE);
-
- qemu_printf(" %s:{%s", label, dev ? "dev" : "obj");
- if (dev && dev->id) {
- qemu_printf(" id=%s", dev->id);
- } else {
- char *canonical_path = object_get_canonical_path(obj);
- if (canonical_path) {
- qemu_printf(" path=%s", canonical_path);
- g_free(canonical_path);
- } else {
- qemu_printf(" type=%s", object_get_typename(obj));
- }
- }
- qemu_printf("}");
-}
-
-static void mtree_print_mr_owner(const MemoryRegion *mr)
-{
- Object *owner = mr->owner;
- Object *parent = memory_region_owner((MemoryRegion *)mr);
-
- if (!owner && !parent) {
- qemu_printf(" orphan");
- return;
- }
- if (owner) {
- mtree_expand_owner("owner", owner);
- }
- if (parent && parent != owner) {
- mtree_expand_owner("parent", parent);
- }
-}
-
-static void mtree_print_mr(const MemoryRegion *mr, unsigned int level,
- hwaddr base,
- MemoryRegionListHead *alias_print_queue,
- bool owner, bool display_disabled)
-{
- MemoryRegionList *new_ml, *ml, *next_ml;
- MemoryRegionListHead submr_print_queue;
- const MemoryRegion *submr;
- unsigned int i;
- hwaddr cur_start, cur_end;
-
- if (!mr) {
- return;
- }
-
- cur_start = base + mr->addr;
- cur_end = cur_start + MR_SIZE(mr->size);
-
- /*
- * Try to detect overflow of memory region. This should never
- * happen normally. When it happens, we dump something to warn the
- * user who is observing this.
- */
- if (cur_start < base || cur_end < cur_start) {
- qemu_printf("[DETECTED OVERFLOW!] ");
- }
-
- if (mr->alias) {
- MemoryRegionList *ml;
- bool found = false;
-
- /* check if the alias is already in the queue */
- QTAILQ_FOREACH(ml, alias_print_queue, mrqueue) {
- if (ml->mr == mr->alias) {
- found = true;
- }
- }
-
- if (!found) {
- ml = g_new(MemoryRegionList, 1);
- ml->mr = mr->alias;
- QTAILQ_INSERT_TAIL(alias_print_queue, ml, mrqueue);
- }
- if (mr->enabled || display_disabled) {
- for (i = 0; i < level; i++) {
- qemu_printf(MTREE_INDENT);
- }
- qemu_printf(TARGET_FMT_plx "-" TARGET_FMT_plx
- " (prio %d, %s%s): alias %s @%s " TARGET_FMT_plx
- "-" TARGET_FMT_plx "%s",
- cur_start, cur_end,
- mr->priority,
- mr->nonvolatile ? "nv-" : "",
- memory_region_type((MemoryRegion *)mr),
- memory_region_name(mr),
- memory_region_name(mr->alias),
- mr->alias_offset,
- mr->alias_offset + MR_SIZE(mr->size),
- mr->enabled ? "" : " [disabled]");
- if (owner) {
- mtree_print_mr_owner(mr);
- }
- qemu_printf("\n");
- }
- } else {
- if (mr->enabled || display_disabled) {
- for (i = 0; i < level; i++) {
- qemu_printf(MTREE_INDENT);
- }
- qemu_printf(TARGET_FMT_plx "-" TARGET_FMT_plx
- " (prio %d, %s%s): %s%s",
- cur_start, cur_end,
- mr->priority,
- mr->nonvolatile ? "nv-" : "",
- memory_region_type((MemoryRegion *)mr),
- memory_region_name(mr),
- mr->enabled ? "" : " [disabled]");
- if (owner) {
- mtree_print_mr_owner(mr);
- }
- qemu_printf("\n");
- }
- }
-
- QTAILQ_INIT(&submr_print_queue);
-
- QTAILQ_FOREACH(submr, &mr->subregions, subregions_link) {
- new_ml = g_new(MemoryRegionList, 1);
- new_ml->mr = submr;
- QTAILQ_FOREACH(ml, &submr_print_queue, mrqueue) {
- if (new_ml->mr->addr < ml->mr->addr ||
- (new_ml->mr->addr == ml->mr->addr &&
- new_ml->mr->priority > ml->mr->priority)) {
- QTAILQ_INSERT_BEFORE(ml, new_ml, mrqueue);
- new_ml = NULL;
- break;
- }
- }
- if (new_ml) {
- QTAILQ_INSERT_TAIL(&submr_print_queue, new_ml, mrqueue);
- }
- }
-
- QTAILQ_FOREACH(ml, &submr_print_queue, mrqueue) {
- mtree_print_mr(ml->mr, level + 1, cur_start,
- alias_print_queue, owner, display_disabled);
- }
-
- QTAILQ_FOREACH_SAFE(ml, &submr_print_queue, mrqueue, next_ml) {
- g_free(ml);
- }
-}
-
-struct FlatViewInfo {
- int counter;
- bool dispatch_tree;
- bool owner;
- AccelClass *ac;
-};
-
-static void mtree_print_flatview(gpointer key, gpointer value,
- gpointer user_data)
-{
- FlatView *view = key;
- GArray *fv_address_spaces = value;
- struct FlatViewInfo *fvi = user_data;
- FlatRange *range = &view->ranges[0];
- MemoryRegion *mr;
- int n = view->nr;
- int i;
- AddressSpace *as;
-
- qemu_printf("FlatView #%d\n", fvi->counter);
- ++fvi->counter;
-
- for (i = 0; i < fv_address_spaces->len; ++i) {
- as = g_array_index(fv_address_spaces, AddressSpace*, i);
- qemu_printf(" AS \"%s\", root: %s",
- as->name, memory_region_name(as->root));
- if (as->root->alias) {
- qemu_printf(", alias %s", memory_region_name(as->root->alias));
- }
- qemu_printf("\n");
- }
-
- qemu_printf(" Root memory region: %s\n",
- view->root ? memory_region_name(view->root) : "(none)");
-
- if (n <= 0) {
- qemu_printf(MTREE_INDENT "No rendered FlatView\n\n");
- return;
- }
-
- while (n--) {
- mr = range->mr;
- if (range->offset_in_region) {
- qemu_printf(MTREE_INDENT TARGET_FMT_plx "-" TARGET_FMT_plx
- " (prio %d, %s%s): %s @" TARGET_FMT_plx,
- int128_get64(range->addr.start),
- int128_get64(range->addr.start)
- + MR_SIZE(range->addr.size),
- mr->priority,
- range->nonvolatile ? "nv-" : "",
- range->readonly ? "rom" : memory_region_type(mr),
- memory_region_name(mr),
- range->offset_in_region);
- } else {
- qemu_printf(MTREE_INDENT TARGET_FMT_plx "-" TARGET_FMT_plx
- " (prio %d, %s%s): %s",
- int128_get64(range->addr.start),
- int128_get64(range->addr.start)
- + MR_SIZE(range->addr.size),
- mr->priority,
- range->nonvolatile ? "nv-" : "",
- range->readonly ? "rom" : memory_region_type(mr),
- memory_region_name(mr));
- }
- if (fvi->owner) {
- mtree_print_mr_owner(mr);
- }
-
- if (fvi->ac) {
- for (i = 0; i < fv_address_spaces->len; ++i) {
- as = g_array_index(fv_address_spaces, AddressSpace*, i);
- if (fvi->ac->has_memory(current_machine, as,
- int128_get64(range->addr.start),
- MR_SIZE(range->addr.size) + 1)) {
- qemu_printf(" %s", fvi->ac->name);
- }
- }
- }
- qemu_printf("\n");
- range++;
- }
-
-#if !defined(CONFIG_USER_ONLY)
- if (fvi->dispatch_tree && view->root) {
- mtree_print_dispatch(view->dispatch, view->root);
- }
-#endif
-
- qemu_printf("\n");
-}
-
-static gboolean mtree_info_flatview_free(gpointer key, gpointer value,
- gpointer user_data)
-{
- FlatView *view = key;
- GArray *fv_address_spaces = value;
-
- g_array_unref(fv_address_spaces);
- flatview_unref(view);
-
- return true;
-}
-
-void mtree_info(bool flatview, bool dispatch_tree, bool owner, bool disabled)
-{
- MemoryRegionListHead ml_head;
- MemoryRegionList *ml, *ml2;
- AddressSpace *as;
-
- if (flatview) {
- FlatView *view;
- struct FlatViewInfo fvi = {
- .counter = 0,
- .dispatch_tree = dispatch_tree,
- .owner = owner,
- };
- GArray *fv_address_spaces;
- GHashTable *views = g_hash_table_new(g_direct_hash, g_direct_equal);
- AccelClass *ac = ACCEL_GET_CLASS(current_accel());
-
- if (ac->has_memory) {
- fvi.ac = ac;
- }
-
- /* Gather all FVs in one table */
- QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
- view = address_space_get_flatview(as);
-
- fv_address_spaces = g_hash_table_lookup(views, view);
- if (!fv_address_spaces) {
- fv_address_spaces = g_array_new(false, false, sizeof(as));
- g_hash_table_insert(views, view, fv_address_spaces);
- }
-
- g_array_append_val(fv_address_spaces, as);
- }
-
- /* Print */
- g_hash_table_foreach(views, mtree_print_flatview, &fvi);
-
- /* Free */
- g_hash_table_foreach_remove(views, mtree_info_flatview_free, 0);
- g_hash_table_unref(views);
-
- return;
- }
-
- QTAILQ_INIT(&ml_head);
-
- QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
- qemu_printf("address-space: %s\n", as->name);
- mtree_print_mr(as->root, 1, 0, &ml_head, owner, disabled);
- qemu_printf("\n");
- }
-
- /* print aliased regions */
- QTAILQ_FOREACH(ml, &ml_head, mrqueue) {
- qemu_printf("memory-region: %s\n", memory_region_name(ml->mr));
- mtree_print_mr(ml->mr, 1, 0, &ml_head, owner, disabled);
- qemu_printf("\n");
- }
-
- QTAILQ_FOREACH_SAFE(ml, &ml_head, mrqueue, ml2) {
- g_free(ml);
- }
-}
-
-void memory_region_init_ram(MemoryRegion *mr,
- struct Object *owner,
- const char *name,
- uint64_t size,
- Error **errp)
-{
- DeviceState *owner_dev;
- Error *err = NULL;
-
- memory_region_init_ram_nomigrate(mr, owner, name, size, &err);
- if (err) {
- error_propagate(errp, err);
- return;
- }
- /* This will assert if owner is neither NULL nor a DeviceState.
- * We only want the owner here for the purposes of defining a
- * unique name for migration. TODO: Ideally we should implement
- * a naming scheme for Objects which are not DeviceStates, in
- * which case we can relax this restriction.
- */
- owner_dev = DEVICE(owner);
- vmstate_register_ram(mr, owner_dev);
-}
-
-void memory_region_init_rom(MemoryRegion *mr,
- struct Object *owner,
- const char *name,
- uint64_t size,
- Error **errp)
-{
- DeviceState *owner_dev;
- Error *err = NULL;
-
- memory_region_init_rom_nomigrate(mr, owner, name, size, &err);
- if (err) {
- error_propagate(errp, err);
- return;
- }
- /* This will assert if owner is neither NULL nor a DeviceState.
- * We only want the owner here for the purposes of defining a
- * unique name for migration. TODO: Ideally we should implement
- * a naming scheme for Objects which are not DeviceStates, in
- * which case we can relax this restriction.
- */
- owner_dev = DEVICE(owner);
- vmstate_register_ram(mr, owner_dev);
-}
-
-void memory_region_init_rom_device(MemoryRegion *mr,
- struct Object *owner,
- const MemoryRegionOps *ops,
- void *opaque,
- const char *name,
- uint64_t size,
- Error **errp)
-{
- DeviceState *owner_dev;
- Error *err = NULL;
-
- memory_region_init_rom_device_nomigrate(mr, owner, ops, opaque,
- name, size, &err);
- if (err) {
- error_propagate(errp, err);
- return;
- }
- /* This will assert if owner is neither NULL nor a DeviceState.
- * We only want the owner here for the purposes of defining a
- * unique name for migration. TODO: Ideally we should implement
- * a naming scheme for Objects which are not DeviceStates, in
- * which case we can relax this restriction.
- */
- owner_dev = DEVICE(owner);
- vmstate_register_ram(mr, owner_dev);
-}
-
-static const TypeInfo memory_region_info = {
- .parent = TYPE_OBJECT,
- .name = TYPE_MEMORY_REGION,
- .class_size = sizeof(MemoryRegionClass),
- .instance_size = sizeof(MemoryRegion),
- .instance_init = memory_region_initfn,
- .instance_finalize = memory_region_finalize,
-};
-
-static const TypeInfo iommu_memory_region_info = {
- .parent = TYPE_MEMORY_REGION,
- .name = TYPE_IOMMU_MEMORY_REGION,
- .class_size = sizeof(IOMMUMemoryRegionClass),
- .instance_size = sizeof(IOMMUMemoryRegion),
- .instance_init = iommu_memory_region_initfn,
- .abstract = true,
-};
-
-static void memory_register_types(void)
-{
- type_register_static(&memory_region_info);
- type_register_static(&iommu_memory_region_info);
-}
-
-type_init(memory_register_types)
+++ /dev/null
-/*
- * QEMU memory mapping
- *
- * Copyright Fujitsu, Corp. 2011, 2012
- *
- * Authors:
- * Wen Congyang <wency@cn.fujitsu.com>
- *
- * 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 "qapi/error.h"
-
-#include "cpu.h"
-#include "sysemu/memory_mapping.h"
-#include "exec/memory.h"
-#include "exec/address-spaces.h"
-
-//#define DEBUG_GUEST_PHYS_REGION_ADD
-
-static void memory_mapping_list_add_mapping_sorted(MemoryMappingList *list,
- MemoryMapping *mapping)
-{
- MemoryMapping *p;
-
- QTAILQ_FOREACH(p, &list->head, next) {
- if (p->phys_addr >= mapping->phys_addr) {
- QTAILQ_INSERT_BEFORE(p, mapping, next);
- return;
- }
- }
- QTAILQ_INSERT_TAIL(&list->head, mapping, next);
-}
-
-static void create_new_memory_mapping(MemoryMappingList *list,
- hwaddr phys_addr,
- hwaddr virt_addr,
- ram_addr_t length)
-{
- MemoryMapping *memory_mapping;
-
- memory_mapping = g_malloc(sizeof(MemoryMapping));
- memory_mapping->phys_addr = phys_addr;
- memory_mapping->virt_addr = virt_addr;
- memory_mapping->length = length;
- list->last_mapping = memory_mapping;
- list->num++;
- memory_mapping_list_add_mapping_sorted(list, memory_mapping);
-}
-
-static inline bool mapping_contiguous(MemoryMapping *map,
- hwaddr phys_addr,
- hwaddr virt_addr)
-{
- return phys_addr == map->phys_addr + map->length &&
- virt_addr == map->virt_addr + map->length;
-}
-
-/*
- * [map->phys_addr, map->phys_addr + map->length) and
- * [phys_addr, phys_addr + length) have intersection?
- */
-static inline bool mapping_have_same_region(MemoryMapping *map,
- hwaddr phys_addr,
- ram_addr_t length)
-{
- return !(phys_addr + length < map->phys_addr ||
- phys_addr >= map->phys_addr + map->length);
-}
-
-/*
- * [map->phys_addr, map->phys_addr + map->length) and
- * [phys_addr, phys_addr + length) have intersection. The virtual address in the
- * intersection are the same?
- */
-static inline bool mapping_conflict(MemoryMapping *map,
- hwaddr phys_addr,
- hwaddr virt_addr)
-{
- return virt_addr - map->virt_addr != phys_addr - map->phys_addr;
-}
-
-/*
- * [map->virt_addr, map->virt_addr + map->length) and
- * [virt_addr, virt_addr + length) have intersection. And the physical address
- * in the intersection are the same.
- */
-static inline void mapping_merge(MemoryMapping *map,
- hwaddr virt_addr,
- ram_addr_t length)
-{
- if (virt_addr < map->virt_addr) {
- map->length += map->virt_addr - virt_addr;
- map->virt_addr = virt_addr;
- }
-
- if ((virt_addr + length) >
- (map->virt_addr + map->length)) {
- map->length = virt_addr + length - map->virt_addr;
- }
-}
-
-void memory_mapping_list_add_merge_sorted(MemoryMappingList *list,
- hwaddr phys_addr,
- hwaddr virt_addr,
- ram_addr_t length)
-{
- MemoryMapping *memory_mapping, *last_mapping;
-
- if (QTAILQ_EMPTY(&list->head)) {
- create_new_memory_mapping(list, phys_addr, virt_addr, length);
- return;
- }
-
- last_mapping = list->last_mapping;
- if (last_mapping) {
- if (mapping_contiguous(last_mapping, phys_addr, virt_addr)) {
- last_mapping->length += length;
- return;
- }
- }
-
- QTAILQ_FOREACH(memory_mapping, &list->head, next) {
- if (mapping_contiguous(memory_mapping, phys_addr, virt_addr)) {
- memory_mapping->length += length;
- list->last_mapping = memory_mapping;
- return;
- }
-
- if (phys_addr + length < memory_mapping->phys_addr) {
- /* create a new region before memory_mapping */
- break;
- }
-
- if (mapping_have_same_region(memory_mapping, phys_addr, length)) {
- if (mapping_conflict(memory_mapping, phys_addr, virt_addr)) {
- continue;
- }
-
- /* merge this region into memory_mapping */
- mapping_merge(memory_mapping, virt_addr, length);
- list->last_mapping = memory_mapping;
- return;
- }
- }
-
- /* this region can not be merged into any existed memory mapping. */
- create_new_memory_mapping(list, phys_addr, virt_addr, length);
-}
-
-void memory_mapping_list_free(MemoryMappingList *list)
-{
- MemoryMapping *p, *q;
-
- QTAILQ_FOREACH_SAFE(p, &list->head, next, q) {
- QTAILQ_REMOVE(&list->head, p, next);
- g_free(p);
- }
-
- list->num = 0;
- list->last_mapping = NULL;
-}
-
-void memory_mapping_list_init(MemoryMappingList *list)
-{
- list->num = 0;
- list->last_mapping = NULL;
- QTAILQ_INIT(&list->head);
-}
-
-void guest_phys_blocks_free(GuestPhysBlockList *list)
-{
- GuestPhysBlock *p, *q;
-
- QTAILQ_FOREACH_SAFE(p, &list->head, next, q) {
- QTAILQ_REMOVE(&list->head, p, next);
- memory_region_unref(p->mr);
- g_free(p);
- }
- list->num = 0;
-}
-
-void guest_phys_blocks_init(GuestPhysBlockList *list)
-{
- list->num = 0;
- QTAILQ_INIT(&list->head);
-}
-
-typedef struct GuestPhysListener {
- GuestPhysBlockList *list;
- MemoryListener listener;
-} GuestPhysListener;
-
-static void guest_phys_blocks_region_add(MemoryListener *listener,
- MemoryRegionSection *section)
-{
- GuestPhysListener *g;
- uint64_t section_size;
- hwaddr target_start, target_end;
- uint8_t *host_addr;
- GuestPhysBlock *predecessor;
-
- /* we only care about RAM */
- if (!memory_region_is_ram(section->mr) ||
- memory_region_is_ram_device(section->mr) ||
- memory_region_is_nonvolatile(section->mr)) {
- return;
- }
-
- g = container_of(listener, GuestPhysListener, listener);
- section_size = int128_get64(section->size);
- target_start = section->offset_within_address_space;
- target_end = target_start + section_size;
- host_addr = memory_region_get_ram_ptr(section->mr) +
- section->offset_within_region;
- predecessor = NULL;
-
- /* find continuity in guest physical address space */
- if (!QTAILQ_EMPTY(&g->list->head)) {
- hwaddr predecessor_size;
-
- predecessor = QTAILQ_LAST(&g->list->head);
- predecessor_size = predecessor->target_end - predecessor->target_start;
-
- /* the memory API guarantees monotonically increasing traversal */
- g_assert(predecessor->target_end <= target_start);
-
- /* we want continuity in both guest-physical and host-virtual memory */
- if (predecessor->target_end < target_start ||
- predecessor->host_addr + predecessor_size != host_addr) {
- predecessor = NULL;
- }
- }
-
- if (predecessor == NULL) {
- /* isolated mapping, allocate it and add it to the list */
- GuestPhysBlock *block = g_malloc0(sizeof *block);
-
- block->target_start = target_start;
- block->target_end = target_end;
- block->host_addr = host_addr;
- block->mr = section->mr;
- memory_region_ref(section->mr);
-
- QTAILQ_INSERT_TAIL(&g->list->head, block, next);
- ++g->list->num;
- } else {
- /* expand predecessor until @target_end; predecessor's start doesn't
- * change
- */
- predecessor->target_end = target_end;
- }
-
-#ifdef DEBUG_GUEST_PHYS_REGION_ADD
- fprintf(stderr, "%s: target_start=" TARGET_FMT_plx " target_end="
- TARGET_FMT_plx ": %s (count: %u)\n", __func__, target_start,
- target_end, predecessor ? "joined" : "added", g->list->num);
-#endif
-}
-
-void guest_phys_blocks_append(GuestPhysBlockList *list)
-{
- GuestPhysListener g = { 0 };
-
- g.list = list;
- g.listener.region_add = &guest_phys_blocks_region_add;
- memory_listener_register(&g.listener, &address_space_memory);
- memory_listener_unregister(&g.listener);
-}
-
-static CPUState *find_paging_enabled_cpu(CPUState *start_cpu)
-{
- CPUState *cpu;
-
- CPU_FOREACH(cpu) {
- if (cpu_paging_enabled(cpu)) {
- return cpu;
- }
- }
-
- return NULL;
-}
-
-void qemu_get_guest_memory_mapping(MemoryMappingList *list,
- const GuestPhysBlockList *guest_phys_blocks,
- Error **errp)
-{
- CPUState *cpu, *first_paging_enabled_cpu;
- GuestPhysBlock *block;
- ram_addr_t offset, length;
-
- first_paging_enabled_cpu = find_paging_enabled_cpu(first_cpu);
- if (first_paging_enabled_cpu) {
- for (cpu = first_paging_enabled_cpu; cpu != NULL;
- cpu = CPU_NEXT(cpu)) {
- Error *err = NULL;
- cpu_get_memory_mapping(cpu, list, &err);
- if (err) {
- error_propagate(errp, err);
- return;
- }
- }
- return;
- }
-
- /*
- * If the guest doesn't use paging, the virtual address is equal to physical
- * address.
- */
- QTAILQ_FOREACH(block, &guest_phys_blocks->head, next) {
- offset = block->target_start;
- length = block->target_end - block->target_start;
- create_new_memory_mapping(list, offset, offset, length);
- }
-}
-
-void qemu_get_guest_simple_memory_mapping(MemoryMappingList *list,
- const GuestPhysBlockList *guest_phys_blocks)
-{
- GuestPhysBlock *block;
-
- QTAILQ_FOREACH(block, &guest_phys_blocks->head, next) {
- create_new_memory_mapping(list, block->target_start, 0,
- block->target_end - block->target_start);
- }
-}
-
-void memory_mapping_filter(MemoryMappingList *list, int64_t begin,
- int64_t length)
-{
- MemoryMapping *cur, *next;
-
- QTAILQ_FOREACH_SAFE(cur, &list->head, next, next) {
- if (cur->phys_addr >= begin + length ||
- cur->phys_addr + cur->length <= begin) {
- QTAILQ_REMOVE(&list->head, cur, next);
- g_free(cur);
- list->num--;
- continue;
- }
-
- if (cur->phys_addr < begin) {
- cur->length -= begin - cur->phys_addr;
- if (cur->virt_addr) {
- cur->virt_addr += begin - cur->phys_addr;
- }
- cur->phys_addr = begin;
- }
-
- if (cur->phys_addr + cur->length > begin + length) {
- cur->length -= cur->phys_addr + cur->length - begin - length;
- }
- }
-}
DirtyBitmapMigBitmapState *dbms;
Error *local_err = NULL;
- bitmap = bdrv_dirty_bitmap_first(bs);
+ FOR_EACH_DIRTY_BITMAP(bs, bitmap) {
+ if (bdrv_dirty_bitmap_name(bitmap)) {
+ break;
+ }
+ }
if (!bitmap) {
return 0;
}
#include "socket.h"
#include "sysemu/runstate.h"
#include "sysemu/sysemu.h"
+#include "sysemu/cpu-throttle.h"
#include "rdma.h"
#include "ram.h"
#include "migration/global_state.h"
/* no migration has happened ever */
/* do not overwrite destination migration status */
return;
- break;
case MIGRATION_STATUS_SETUP:
info->has_status = true;
info->has_total_time = false;
switch (mis->state) {
case MIGRATION_STATUS_NONE:
return;
- break;
case MIGRATION_STATUS_SETUP:
case MIGRATION_STATUS_CANCELLING:
case MIGRATION_STATUS_CANCELLED:
if (params->has_tls_creds) {
assert(params->tls_creds->type == QTYPE_QSTRING);
- dest->tls_creds = g_strdup(params->tls_creds->u.s);
+ dest->tls_creds = params->tls_creds->u.s;
}
if (params->has_tls_hostname) {
assert(params->tls_hostname->type == QTYPE_QSTRING);
- dest->tls_hostname = g_strdup(params->tls_hostname->u.s);
+ dest->tls_hostname = params->tls_hostname->u.s;
}
if (params->has_max_bandwidth) {
#include "migration/colo.h"
#include "block.h"
#include "sysemu/sysemu.h"
+#include "sysemu/cpu-throttle.h"
#include "savevm.h"
#include "qemu/iov.h"
#include "multifd.h"
{
BlockDriverState *bs, *bs1;
QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1;
- int ret = -1;
+ int ret = -1, ret2;
QEMUFile *f;
int saved_vm_running;
uint64_t vm_state_size;
}
ret = qemu_savevm_state(f, errp);
vm_state_size = qemu_ftell(f);
- qemu_fclose(f);
+ ret2 = qemu_fclose(f);
if (ret < 0) {
goto the_end;
}
+ if (ret2 < 0) {
+ ret = ret2;
+ goto the_end;
+ }
/* The bdrv_all_create_snapshot() call that follows acquires the AioContext
* for itself. BDRV_POLL_WHILE() does not support nested locking because
#include "exec/gdbstub.h"
#include "net/net.h"
#include "net/slirp.h"
-#include "chardev/char-mux.h"
#include "ui/qemu-spice.h"
#include "qemu/config-file.h"
#include "qemu/ctype.h"
};
static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports);
+static QTAILQ_HEAD(, NBDExport) closed_exports =
+ QTAILQ_HEAD_INITIALIZER(closed_exports);
/* NBDExportMetaContexts represents a list of contexts to be exported,
* as selected by NBD_OPT_SET_META_CONTEXT. Also used for
g_free(exp->name);
exp->name = NULL;
QTAILQ_REMOVE(&exports, exp, next);
+ QTAILQ_INSERT_TAIL(&closed_exports, exp, next);
}
g_free(exp->description);
exp->description = NULL;
g_free(exp->export_bitmap_context);
}
+ QTAILQ_REMOVE(&closed_exports, exp, next);
g_free(exp);
+ aio_wait_kick();
}
}
nbd_export_close(exp);
aio_context_release(aio_context);
}
+
+ AIO_WAIT_WHILE(NULL, !(QTAILQ_EMPTY(&exports) &&
+ QTAILQ_EMPTY(&closed_exports)));
}
static int coroutine_fn nbd_co_send_iov(NBDClient *client, struct iovec *iov,
static QemuMutex event_mtx;
static QemuCond event_complete_cond;
static int event_unhandled_count;
+static uint32_t max_queue_size;
/*
* + CompareState ++
*/
static int colo_insert_packet(GQueue *queue, Packet *pkt, uint32_t *max_ack)
{
- if (g_queue_get_length(queue) <= MAX_QUEUE_SIZE) {
+ if (g_queue_get_length(queue) <= max_queue_size) {
if (pkt->ip->ip_p == IPPROTO_TCP) {
fill_pkt_tcp_info(pkt, max_ack);
g_queue_insert_sorted(queue,
s->expired_scan_cycle = value;
}
+static void get_max_queue_size(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ uint32_t value = max_queue_size;
+
+ visit_type_uint32(v, name, &value, errp);
+}
+
+static void set_max_queue_size(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ Error *local_err = NULL;
+ uint32_t value;
+
+ visit_type_uint32(v, name, &value, &local_err);
+ if (local_err) {
+ goto out;
+ }
+ if (!value) {
+ error_setg(&local_err, "Property '%s.%s' requires a positive value",
+ object_get_typename(obj), name);
+ goto out;
+ }
+ max_queue_size = value;
+
+out:
+ error_propagate(errp, local_err);
+}
+
static void compare_pri_rs_finalize(SocketReadState *pri_rs)
{
CompareState *s = container_of(pri_rs, CompareState, pri_rs);
s->expired_scan_cycle = REGULAR_PACKET_CHECK_MS;
}
+ if (!max_queue_size) {
+ /* Set default queue size to 1024 */
+ max_queue_size = MAX_QUEUE_SIZE;
+ }
+
if (find_and_check_chardev(&chr, s->pri_indev, errp) ||
!qemu_chr_fe_init(&s->chr_pri_in, chr, errp)) {
return;
compare_get_expired_scan_cycle,
compare_set_expired_scan_cycle, NULL, NULL);
+ object_property_add(obj, "max_queue_size", "uint32",
+ get_max_queue_size,
+ set_max_queue_size, NULL, NULL);
+
s->vnet_hdr = false;
object_property_add_bool(obj, "vnet_hdr_support", compare_get_vnet_hdr,
compare_set_vnet_hdr);
}
if (sock->has_fd) {
- int fd;
+ int fd, ret;
fd = monitor_fd_param(cur_mon, sock->fd, errp);
if (fd == -1) {
return -1;
}
- qemu_set_nonblock(fd);
+ ret = qemu_try_set_nonblock(fd);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d",
+ name, fd);
+ return -1;
+ }
if (!net_socket_fd_init(peer, "socket", name, fd, 1, sock->mcast,
errp)) {
return -1;
{
}
-int tap_probe_vnet_hdr(int fd)
+int tap_probe_vnet_hdr(int fd, Error **errp)
{
return 0;
}
}
}
-int tap_probe_vnet_hdr(int fd)
+int tap_probe_vnet_hdr(int fd, Error **errp)
{
struct ifreq ifr;
if (ioctl(fd, TUNGETIFF, &ifr) != 0) {
- error_report("TUNGETIFF ioctl() failed: %s", strerror(errno));
- return 0;
+ /* TUNGETIFF is available since kernel v2.6.27 */
+ error_setg_errno(errp, errno,
+ "Unable to query TUNGETIFF on FD %d", fd);
+ return -1;
}
return ifr.ifr_flags & IFF_VNET_HDR;
{
}
-int tap_probe_vnet_hdr(int fd)
+int tap_probe_vnet_hdr(int fd, Error **errp)
{
return 0;
}
{
}
-int tap_probe_vnet_hdr(int fd)
+int tap_probe_vnet_hdr(int fd, Error **errp)
{
return 0;
}
}
qemu_set_nonblock(fd);
- vnet_hdr = tap_probe_vnet_hdr(fd);
+ vnet_hdr = tap_probe_vnet_hdr(fd, errp);
+ if (vnet_hdr < 0) {
+ close(fd);
+ return -1;
+ }
s = net_tap_fd_init(peer, "bridge", name, fd, vnet_hdr);
snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s,br=%s", helper,
}
if (vhostfdname) {
+ int ret;
+
vhostfd = monitor_fd_param(cur_mon, vhostfdname, &err);
if (vhostfd == -1) {
if (tap->has_vhostforce && tap->vhostforce) {
}
return;
}
- qemu_set_nonblock(vhostfd);
+ ret = qemu_try_set_nonblock(vhostfd);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d",
+ name, fd);
+ return;
+ }
} else {
vhostfd = open("/dev/vhost-net", O_RDWR);
if (vhostfd < 0) {
Error *err = NULL;
const char *vhostfdname;
char ifname[128];
+ int ret = 0;
assert(netdev->type == NET_CLIENT_DRIVER_TAP);
tap = &netdev->u.tap;
return -1;
}
- qemu_set_nonblock(fd);
+ ret = qemu_try_set_nonblock(fd);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d",
+ name, fd);
+ return -1;
+ }
- vnet_hdr = tap_probe_vnet_hdr(fd);
+ vnet_hdr = tap_probe_vnet_hdr(fd, errp);
+ if (vnet_hdr < 0) {
+ close(fd);
+ return -1;
+ }
net_init_tap_one(tap, peer, "tap", name, NULL,
script, downscript,
char **fds;
char **vhost_fds;
int nfds = 0, nvhosts = 0;
- int ret = 0;
if (tap->has_ifname || tap->has_script || tap->has_downscript ||
tap->has_vnet_hdr || tap->has_helper || tap->has_queues ||
goto free_fail;
}
- qemu_set_nonblock(fd);
+ ret = qemu_try_set_nonblock(fd);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "%s: Can't use file descriptor %d",
+ name, fd);
+ goto free_fail;
+ }
if (i == 0) {
- vnet_hdr = tap_probe_vnet_hdr(fd);
- } else if (vnet_hdr != tap_probe_vnet_hdr(fd)) {
+ vnet_hdr = tap_probe_vnet_hdr(fd, errp);
+ if (vnet_hdr < 0) {
+ goto free_fail;
+ }
+ } else if (vnet_hdr != tap_probe_vnet_hdr(fd, NULL)) {
error_setg(errp,
"vnet_hdr not consistent across given tap fds");
ret = -1;
}
qemu_set_nonblock(fd);
- vnet_hdr = tap_probe_vnet_hdr(fd);
+ vnet_hdr = tap_probe_vnet_hdr(fd, errp);
+ if (vnet_hdr < 0) {
+ close(fd);
+ return -1;
+ }
net_init_tap_one(tap, peer, "bridge", name, ifname,
script, downscript, vhostfdname,
ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen);
void tap_set_sndbuf(int fd, const NetdevTapOptions *tap, Error **errp);
-int tap_probe_vnet_hdr(int fd);
+int tap_probe_vnet_hdr(int fd, Error **errp);
int tap_probe_vnet_hdr_len(int fd, int len);
int tap_probe_has_ufo(int fd);
void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo);
int os_mlock(void)
{
+#ifdef HAVE_MLOCKALL
int ret = 0;
ret = mlockall(MCL_CURRENT | MCL_FUTURE);
}
return ret;
+#else
+ return -ENOSYS;
+#endif
}
--- /dev/null
+#!/usr/bin/env python3
+#
+# This python module implements a ConsoleSocket object which is
+# designed always drain the socket itself, and place
+# the bytes into a in memory buffer for later processing.
+#
+# Optionally a file path can be passed in and we will also
+# dump the characters to this file for debug.
+#
+# Copyright 2020 Linaro
+#
+# Authors:
+# Robert Foley <robert.foley@linaro.org>
+#
+# This code is licensed under the GPL version 2 or later. See
+# the COPYING file in the top-level directory.
+#
+import asyncore
+import socket
+import threading
+import io
+import os
+import sys
+from collections import deque
+import time
+import traceback
+
+class ConsoleSocket(asyncore.dispatcher):
+
+ def __init__(self, address, file=None):
+ self._recv_timeout_sec = 300
+ self._buffer = deque()
+ self._asyncore_thread = None
+ self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ self._sock.connect(address)
+ self._logfile = None
+ if file:
+ self._logfile = open(file, "w")
+ asyncore.dispatcher.__init__(self, sock=self._sock)
+ self._open = True
+ self._thread_start()
+
+ def _thread_start(self):
+ """Kick off a thread to wait on the asyncore.loop"""
+ if self._asyncore_thread is not None:
+ return
+ self._asyncore_thread = threading.Thread(target=asyncore.loop,
+ kwargs={'timeout':1})
+ self._asyncore_thread.daemon = True
+ self._asyncore_thread.start()
+
+ def handle_close(self):
+ """redirect close to base class"""
+ # Call the base class close, but not self.close() since
+ # handle_close() occurs in the context of the thread which
+ # self.close() attempts to join.
+ asyncore.dispatcher.close(self)
+
+ def close(self):
+ """Close the base object and wait for the thread to terminate"""
+ if self._open:
+ self._open = False
+ asyncore.dispatcher.close(self)
+ if self._asyncore_thread is not None:
+ thread, self._asyncore_thread = self._asyncore_thread, None
+ thread.join()
+ if self._logfile:
+ self._logfile.close()
+ self._logfile = None
+
+ def handle_read(self):
+ """process arriving characters into in memory _buffer"""
+ try:
+ data = asyncore.dispatcher.recv(self, 1)
+ # latin1 is needed since there are some chars
+ # we are receiving that cannot be encoded to utf-8
+ # such as 0xe2, 0x80, 0xA6.
+ string = data.decode("latin1")
+ except:
+ print("Exception seen.")
+ traceback.print_exc()
+ return
+ if self._logfile:
+ self._logfile.write("{}".format(string))
+ self._logfile.flush()
+ for c in string:
+ self._buffer.extend(c)
+
+ def recv(self, n=1, sleep_delay_s=0.1):
+ """Return chars from in memory buffer"""
+ start_time = time.time()
+ while len(self._buffer) < n:
+ time.sleep(sleep_delay_s)
+ elapsed_sec = time.time() - start_time
+ if elapsed_sec > self._recv_timeout_sec:
+ raise socket.timeout
+ chars = ''.join([self._buffer.popleft() for i in range(n)])
+ # We choose to use latin1 to remain consistent with
+ # handle_read() and give back the same data as the user would
+ # receive if they were reading directly from the
+ # socket w/o our intervention.
+ return chars.encode("latin1")
+
+ def set_blocking(self):
+ """Maintain compatibility with socket API"""
+ pass
+
+ def settimeout(self, seconds):
+ """Set current timeout on recv"""
+ self._recv_timeout_sec = seconds
import os
import subprocess
import shutil
+import signal
import socket
import tempfile
from typing import Optional, Type
from types import TracebackType
+from qemu.console_socket import ConsoleSocket
from . import qmp
"""
-class MonitorResponseError(qmp.QMPError):
+class AbnormalShutdown(QEMUMachineError):
"""
- Represents erroneous QMP monitor reply
+ Exception raised when a graceful shutdown was requested, but not performed.
"""
- def __init__(self, reply):
- try:
- desc = reply["error"]["desc"]
- except KeyError:
- desc = reply
- super().__init__(desc)
- self.reply = reply
class QEMUMachine:
def __init__(self, binary, args=None, wrapper=None, name=None,
test_dir="/var/tmp", monitor_address=None,
- socket_scm_helper=None, sock_dir=None):
+ socket_scm_helper=None, sock_dir=None,
+ drain_console=False, console_log=None):
'''
Initialize a QEMUMachine
@param test_dir: where to create socket and log file
@param monitor_address: address for QMP monitor
@param socket_scm_helper: helper program, required for send_fd_scm()
+ @param sock_dir: where to create socket (overrides test_dir for sock)
+ @param console_log: (optional) path to console log file
+ @param drain_console: (optional) True to drain console socket to buffer
@note: Qemu process is not started until launch() is used.
'''
if args is None:
self._console_address = None
self._console_socket = None
self._remove_files = []
+ self._user_killed = False
+ self._console_log_path = console_log
+ if self._console_log_path:
+ # In order to log the console, buffering needs to be enabled.
+ self._drain_console = True
+ else:
+ self._drain_console = drain_console
def __enter__(self):
return self
self._qmp.accept()
def _post_shutdown(self):
+ """
+ Called to cleanup the VM instance after the process has exited.
+ May also be called after a failed launch.
+ """
+ # Comprehensive reset for the failed launch case:
+ self._early_cleanup()
+
+ if self._qmp:
+ self._qmp.close()
+ self._qmp = None
+
+ self._load_io_log()
+
if self._qemu_log_file is not None:
self._qemu_log_file.close()
self._qemu_log_file = None
while len(self._remove_files) > 0:
self._remove_if_exists(self._remove_files.pop())
+ exitcode = self.exitcode()
+ if (exitcode is not None and exitcode < 0
+ and not (self._user_killed and exitcode == -signal.SIGKILL)):
+ msg = 'qemu received signal %i; command: "%s"'
+ if self._qemu_full_args:
+ command = ' '.join(self._qemu_full_args)
+ else:
+ command = ''
+ LOG.warning(msg, -int(exitcode), command)
+
+ self._user_killed = False
+ self._launched = False
+
def launch(self):
"""
Launch the VM and make sure we cleanup and expose the
self._launch()
self._launched = True
except:
- self.shutdown()
+ self._post_shutdown()
LOG.debug('Error launching VM')
if self._qemu_full_args:
close_fds=False)
self._post_launch()
- def wait(self):
+ def _early_cleanup(self) -> None:
"""
- Wait for the VM to power off
- """
- self._popen.wait()
- if self._qmp:
- self._qmp.close()
- self._load_io_log()
- self._post_shutdown()
+ Perform any cleanup that needs to happen before the VM exits.
- def shutdown(self, has_quit=False, hard=False):
- """
- Terminate the VM and clean up
+ May be invoked by both soft and hard shutdown in failover scenarios.
+ Called additionally by _post_shutdown for comprehensive cleanup.
"""
# If we keep the console socket open, we may deadlock waiting
# for QEMU to exit, while QEMU is waiting for the socket to
self._console_socket.close()
self._console_socket = None
- if self.is_running():
- if hard:
- self._popen.kill()
- elif self._qmp:
- try:
- if not has_quit:
- self._qmp.cmd('quit')
- self._qmp.close()
- self._popen.wait(timeout=3)
- except:
- self._popen.kill()
- self._popen.wait()
+ def _hard_shutdown(self) -> None:
+ """
+ Perform early cleanup, kill the VM, and wait for it to terminate.
- self._load_io_log()
- self._post_shutdown()
+ :raise subprocess.Timeout: When timeout is exceeds 60 seconds
+ waiting for the QEMU process to terminate.
+ """
+ self._early_cleanup()
+ self._popen.kill()
+ self._popen.wait(timeout=60)
- exitcode = self.exitcode()
- if exitcode is not None and exitcode < 0 and \
- not (exitcode == -9 and hard):
- msg = 'qemu received signal %i: %s'
- if self._qemu_full_args:
- command = ' '.join(self._qemu_full_args)
- else:
- command = ''
- LOG.warning(msg, -int(exitcode), command)
+ def _soft_shutdown(self, has_quit: bool = False,
+ timeout: Optional[int] = 3) -> None:
+ """
+ Perform early cleanup, attempt to gracefully shut down the VM, and wait
+ for it to terminate.
- self._launched = False
+ :param has_quit: When True, don't attempt to issue 'quit' QMP command
+ :param timeout: Optional timeout in seconds for graceful shutdown.
+ Default 3 seconds, A value of None is an infinite wait.
+
+ :raise ConnectionReset: On QMP communication errors
+ :raise subprocess.TimeoutExpired: When timeout is exceeded waiting for
+ the QEMU process to terminate.
+ """
+ self._early_cleanup()
+
+ if self._qmp is not None:
+ if not has_quit:
+ # Might raise ConnectionReset
+ self._qmp.cmd('quit')
+
+ # May raise subprocess.TimeoutExpired
+ self._popen.wait(timeout=timeout)
+
+ def _do_shutdown(self, has_quit: bool = False,
+ timeout: Optional[int] = 3) -> None:
+ """
+ Attempt to shutdown the VM gracefully; fallback to a hard shutdown.
+
+ :param has_quit: When True, don't attempt to issue 'quit' QMP command
+ :param timeout: Optional timeout in seconds for graceful shutdown.
+ Default 3 seconds, A value of None is an infinite wait.
+
+ :raise AbnormalShutdown: When the VM could not be shut down gracefully.
+ The inner exception will likely be ConnectionReset or
+ subprocess.TimeoutExpired. In rare cases, non-graceful termination
+ may result in its own exceptions, likely subprocess.TimeoutExpired.
+ """
+ try:
+ self._soft_shutdown(has_quit, timeout)
+ except Exception as exc:
+ self._hard_shutdown()
+ raise AbnormalShutdown("Could not perform graceful shutdown") \
+ from exc
+
+ def shutdown(self, has_quit: bool = False,
+ hard: bool = False,
+ timeout: Optional[int] = 3) -> None:
+ """
+ Terminate the VM (gracefully if possible) and perform cleanup.
+ Cleanup will always be performed.
+
+ If the VM has not yet been launched, or shutdown(), wait(), or kill()
+ have already been called, this method does nothing.
+
+ :param has_quit: When true, do not attempt to issue 'quit' QMP command.
+ :param hard: When true, do not attempt graceful shutdown, and
+ suppress the SIGKILL warning log message.
+ :param timeout: Optional timeout in seconds for graceful shutdown.
+ Default 3 seconds, A value of None is an infinite wait.
+ """
+ if not self._launched:
+ return
+
+ try:
+ if hard:
+ self._user_killed = True
+ self._hard_shutdown()
+ else:
+ self._do_shutdown(has_quit, timeout=timeout)
+ finally:
+ self._post_shutdown()
def kill(self):
+ """
+ Terminate the VM forcefully, wait for it to exit, and perform cleanup.
+ """
self.shutdown(hard=True)
+ def wait(self, timeout: Optional[int] = 3) -> None:
+ """
+ Wait for the VM to power off and perform post-shutdown cleanup.
+
+ :param timeout: Optional timeout in seconds.
+ Default 3 seconds, A value of None is an infinite wait.
+ """
+ self.shutdown(has_quit=True, timeout=timeout)
+
def set_qmp_monitor(self, enabled=True):
"""
Set the QMP monitor.
if reply is None:
raise qmp.QMPError("Monitor is closed")
if "error" in reply:
- raise MonitorResponseError(reply)
+ raise qmp.QMPResponseError(reply)
return reply["return"]
def get_qmp_event(self, wait=False):
Returns a socket connected to the console
"""
if self._console_socket is None:
- self._console_socket = socket.socket(socket.AF_UNIX,
- socket.SOCK_STREAM)
- self._console_socket.connect(self._console_address)
+ if self._drain_console:
+ self._console_socket = ConsoleSocket(self._console_address,
+ file=self._console_log_path)
+ else:
+ self._console_socket = socket.socket(socket.AF_UNIX,
+ socket.SOCK_STREAM)
+ self._console_socket.connect(self._console_address)
return self._console_socket
import socket
import logging
from typing import (
+ Any,
+ cast,
+ Dict,
Optional,
TextIO,
Type,
+ Tuple,
+ Union,
)
from types import TracebackType
+# QMPMessage is a QMP Message of any kind.
+# e.g. {'yee': 'haw'}
+#
+# QMPReturnValue is the inner value of return values only.
+# {'return': {}} is the QMPMessage,
+# {} is the QMPReturnValue.
+QMPMessage = Dict[str, Any]
+QMPReturnValue = Dict[str, Any]
+
+InternetAddrT = Tuple[str, str]
+UnixAddrT = str
+SocketAddrT = Union[InternetAddrT, UnixAddrT]
+
+
class QMPError(Exception):
"""
QMP base exception
"""
+class QMPProtocolError(QMPError):
+ """
+ QMP protocol error; unexpected response
+ """
+
+
+class QMPResponseError(QMPError):
+ """
+ Represents erroneous QMP monitor reply
+ """
+ def __init__(self, reply: QMPMessage):
+ try:
+ desc = reply['error']['desc']
+ except KeyError:
+ desc = reply
+ super().__init__(desc)
+ self.reply = reply
+
+
class QEMUMonitorProtocol:
"""
Provide an API to connect to QEMU via QEMU Monitor Protocol (QMP) and then
data = self.__sockfile.readline()
if not data:
return None
- resp = json.loads(data)
+ # By definition, any JSON received from QMP is a QMPMessage,
+ # and we are asserting only at static analysis time that it
+ # has a particular shape.
+ resp: QMPMessage = json.loads(data)
if 'event' in resp:
self.logger.debug("<<< %s", resp)
self.__events.append(resp)
self.__sockfile = self.__sock.makefile(mode='r')
return self.__negotiate_capabilities()
- def cmd_obj(self, qmp_cmd):
+ def cmd_obj(self, qmp_cmd: QMPMessage) -> QMPMessage:
"""
Send a QMP command to the QMP Monitor.
@param qmp_cmd: QMP command to be sent as a Python dict
- @return QMP response as a Python dict or None if the connection has
- been closed
+ @return QMP response as a Python dict
"""
self.logger.debug(">>> %s", qmp_cmd)
- try:
- self.__sock.sendall(json.dumps(qmp_cmd).encode('utf-8'))
- except OSError as err:
- if err.errno == errno.EPIPE:
- return None
- raise err
+ self.__sock.sendall(json.dumps(qmp_cmd).encode('utf-8'))
resp = self.__json_read()
+ if resp is None:
+ raise QMPConnectError("Unexpected empty reply from server")
self.logger.debug("<<< %s", resp)
return resp
Build and send a QMP command to the monitor, report errors if any
"""
ret = self.cmd(cmd, kwds)
- if "error" in ret:
- raise Exception(ret['error']['desc'])
- return ret['return']
+ if 'error' in ret:
+ raise QMPResponseError(ret)
+ if 'return' not in ret:
+ raise QMPProtocolError(
+ "'return' key not found in QMP response '{}'".format(str(ret))
+ )
+ return cast(QMPReturnValue, ret['return'])
def pull_event(self, wait=False):
"""
#
# Drivers that are supported in block device operations.
#
-# @vxhs: Since 2.10
# @throttle: Since 2.11
# @nvme: Since 2.12
# @copy-on-read: Since 3.0
'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'rbd',
{ 'name': 'replication', 'if': 'defined(CONFIG_REPLICATION)' },
'sheepdog',
- 'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
+ 'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] }
##
# @BlockdevOptionsFile:
'base': 'BlockdevOptionsGenericFormat',
'data': { '*offset': 'int', '*size': 'int' } }
-##
-# @BlockdevOptionsVxHS:
-#
-# Driver specific block device options for VxHS
-#
-# @vdisk-id: UUID of VxHS volume
-# @server: vxhs server IP, port
-# @tls-creds: TLS credentials ID
-#
-# Since: 2.10
-##
-{ 'struct': 'BlockdevOptionsVxHS',
- 'data': { 'vdisk-id': 'str',
- 'server': 'InetSocketAddressBase',
- '*tls-creds': 'str' } }
-
##
# @BlockdevOptionsThrottle:
#
'vhdx': 'BlockdevOptionsGenericFormat',
'vmdk': 'BlockdevOptionsGenericCOWFormat',
'vpc': 'BlockdevOptionsGenericFormat',
- 'vvfat': 'BlockdevOptionsVVFAT',
- 'vxhs': 'BlockdevOptionsVxHS'
+ 'vvfat': 'BlockdevOptionsVVFAT'
} }
##
# falloc (if defined CONFIG_POSIX_FALLOCATE),
# full (if defined CONFIG_POSIX))
# @nocow: Turn off copy-on-write (valid only on btrfs; default: off)
+# @extent-size-hint: Extent size hint to add to the image file; 0 for not
+# adding an extent size hint (default: 1 MB, since 5.1)
#
# Since: 2.12
##
{ 'struct': 'BlockdevCreateOptionsFile',
- 'data': { 'filename': 'str',
- 'size': 'size',
- '*preallocation': 'PreallocMode',
- '*nocow': 'bool' } }
+ 'data': { 'filename': 'str',
+ 'size': 'size',
+ '*preallocation': 'PreallocMode',
+ '*nocow': 'bool',
+ '*extent-size-hint': 'size'} }
##
# @BlockdevCreateOptionsGluster:
# being.
#
# @rx: since 5.0
+# @avr: since 5.1
#
# Notes: The resulting QMP strings can be appended to the "qemu-system-"
# prefix to produce the corresponding QEMU executable name. This
# Since: 3.0
##
{ 'enum' : 'SysEmuTarget',
- 'data' : [ 'aarch64', 'alpha', 'arm', 'cris', 'hppa', 'i386', 'lm32',
+ 'data' : [ 'aarch64', 'alpha', 'arm', 'avr', 'cris', 'hppa', 'i386', 'lm32',
'm68k', 'microblaze', 'microblazeel', 'mips', 'mips64',
'mips64el', 'mipsel', 'moxie', 'nios2', 'or1k', 'ppc',
'ppc64', 'riscv32', 'riscv64', 'rx', 's390x', 'sh4',
}
if (skip_create && options) {
- warn_report("-o has no effect when skipping image creation");
- warn_report("This will become an error in future QEMU versions.");
+ error_report("-o has no effect when skipping image creation");
+ goto fail_getopt;
}
if (s.has_zero_init && !skip_create) {
goto out;
}
+ if (out_baseimg_param) {
+ if (!qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT)) {
+ warn_report("Deprecated use of backing file without explicit "
+ "backing format");
+ }
+ }
+
/* Check if compression is supported */
if (s.compressed) {
bool encryption =
curr.start = start_offset;
while (curr.start + curr.length < length) {
int64_t offset = curr.start + curr.length;
- int64_t n;
+ int64_t n = length - offset;
- /* Probe up to 1 GiB at a time. */
- n = MIN(1 * GiB, length - offset);
ret = get_block_status(bs, offset, n, &next);
-
if (ret < 0) {
error_report("Could not read file metadata: %s", strerror(-ret));
goto out;
* doesn't change when we switch the backing file.
*/
if (out_baseimg && *out_baseimg) {
- ret = bdrv_change_backing_file(bs, out_baseimg, out_basefmt);
+ ret = bdrv_change_backing_file(bs, out_baseimg, out_basefmt, true);
} else {
- ret = bdrv_change_backing_file(bs, NULL, NULL);
+ ret = bdrv_change_backing_file(bs, NULL, NULL, false);
}
if (ret == -ENOSPC) {
}
if (total_size < current_size && !shrink) {
+ error_report("Use the --shrink option to perform a shrink operation.");
warn_report("Shrinking an image will delete all data beyond the "
"shrunken image's end. Before performing such an "
"operation, make sure there is no important data there.");
-
- if (g_strcmp0(bdrv_get_format_name(blk_bs(blk)), "raw") != 0) {
- error_report(
- "Use the --shrink option to perform a shrink operation.");
- ret = -1;
- goto out;
- } else {
- warn_report("Using the --shrink option will suppress this message. "
- "Note that future versions of qemu-img may refuse to "
- "shrink images without this option.");
- }
+ ret = -1;
+ goto out;
}
/*
Some drivers are:
-``-device ipmi-bmc-sim,id=id[,slave_addr=val][,sdrfile=file][,furareasize=val][,furdatafile=file][,guid=uuid]``
+``-device ipmi-bmc-sim,id=id[,prop[=value][,...]]``
Add an IPMI BMC. This is a simulation of a hardware management
interface processor that normally sits on a system. It provides a
watchdog and the ability to reset and power control the system. You
``-device isa-ipmi-bt,bmc=id[,ioport=val][,irq=val]``
Like the KCS interface, but defines a BT interface. The default port
is 0xe4 and the default interrupt is 5.
+
+``-device pci-ipmi-kcs,bmc=id``
+ Add a KCS IPMI interafce on the PCI bus.
+
+ ``bmc=id``
+ The BMC to connect to, one of ipmi-bmc-sim or ipmi-bmc-extern above.
+
+``-device pci-ipmi-bt,bmc=id``
+ Like the KCS interface, but defines a BT interface on the PCI bus.
ERST
DEF("name", HAS_ARG, QEMU_OPTION_name,
DEF("no-kvm", 0, QEMU_OPTION_no_kvm, "", QEMU_ARCH_I386)
DEF("msg", HAS_ARG, QEMU_OPTION_msg,
- "-msg timestamp[=on|off]\n"
+ "-msg [timestamp[=on|off]][,guest-name=[on|off]]\n"
" control error message format\n"
- " timestamp=on enables timestamps (default: off)\n",
+ " timestamp=on enables timestamps (default: off)\n"
+ " guest-name=on enables guest name prefix but only if\n"
+ " -name guest option is set (default: off)\n",
QEMU_ARCH_ALL)
SRST
-``-msg timestamp[=on|off]``
+``-msg [timestamp[=on|off]][,guest-name[=on|off]]``
Control error message format.
``timestamp=on|off``
Prefix messages with a timestamp. Default is off.
+
+ ``guest-name=on|off``
+ Prefix messages with guest name but only if -name guest option is set
+ otherwise the option is ignored. Default is off.
ERST
DEF("dump-vmstate", HAS_ARG, QEMU_OPTION_dump_vmstate,
stored. The file format is libpcap, so it can be analyzed with
tools such as tcpdump or Wireshark.
- ``-object colo-compare,id=id,primary_in=chardevid,secondary_in=chardevid,outdev=chardevid,iothread=id[,vnet_hdr_support][,notify_dev=id][,compare_timeout=@var{ms}][,expired_scan_cycle=@var{ms}``
- Colo-compare gets packet from primary\_inchardevid and
- secondary\_inchardevid, than compare primary packet with
- secondary packet. If the packets are same, we will output
- primary packet to outdevchardevid, else we will notify
- colo-frame do checkpoint and send primary packet to
- outdevchardevid. In order to improve efficiency, we need to put
- the task of comparison in another thread. If it has the
- vnet\_hdr\_support flag, colo compare will send/recv packet with
- vnet\_hdr\_len. Then compare\_timeout=@var{ms} determines the
- maximum delay colo-compare wait for the packet.
- The expired\_scan\_cycle=@var{ms} to set the period of scanning
- expired primary node network packets.
- If you want to use Xen COLO, will need the notify\_dev to
+ ``-object colo-compare,id=id,primary_in=chardevid,secondary_in=chardevid,outdev=chardevid,iothread=id[,vnet_hdr_support][,notify_dev=id][,compare_timeout=@var{ms}][,expired_scan_cycle=@var{ms}][,max_queue_size=@var{size}]``
+ Colo-compare gets packet from primary\_in chardevid and
+ secondary\_in, then compare whether the payload of primary packet
+ and secondary packet are the same. If same, it will output
+ primary packet to out\_dev, else it will notify COLO-framework to do
+ checkpoint and send primary packet to out\_dev. In order to
+ improve efficiency, we need to put the task of comparison in
+ another iothread. If it has the vnet\_hdr\_support flag,
+ colo compare will send/recv packet with vnet\_hdr\_len.
+ The compare\_timeout=@var{ms} determines the maximum time of the
+ colo-compare hold the packet. The expired\_scan\_cycle=@var{ms}
+ is to set the period of scanning expired primary node network packets.
+ The max\_queue\_size=@var{size} is to set the max compare queue
+ size depend on user environment.
+ If user want to use Xen COLO, need to add the notify\_dev to
notify Xen colo-frame to do checkpoint.
- we must use it with the help of filter-mirror and
- filter-redirector.
+ COLO-compare must be used with the help of filter-mirror,
+ filter-redirector and filter-rewriter.
::
GuestHostName *qmp_guest_get_host_name(Error **errp)
{
GuestHostName *result = NULL;
- gchar const *hostname = g_get_host_name();
- if (hostname != NULL) {
- result = g_new0(GuestHostName, 1);
- result->host_name = g_strdup(hostname);
+ g_autofree char *hostname = qemu_get_host_name(errp);
+
+ /*
+ * We want to avoid using g_get_host_name() because that
+ * caches the result and we wouldn't reflect changes in the
+ * host name.
+ */
+
+ if (!hostname) {
+ hostname = g_strdup("localhost");
}
+
+ result = g_new0(GuestHostName, 1);
+ result->host_name = g_steal_pointer(&hostname);
return result;
}
QString *payload_qstr, *response_qstr;
GIOStatus status;
- g_assert(rsp && s->channel);
+ g_assert(s->channel);
+
+ if (!rsp) {
+ return 0;
+ }
payload_qstr = qobject_to_json(QOBJECT(rsp));
if (!payload_qstr) {
#include <comdef.h>
#include <comutil.h>
#include <sddl.h>
+#include <winsvc.h>
#define BUFFER_SIZE 1024
}
}
-/* Stop QGA VSS provider service from COM+ Application Admin Catalog */
-
+/* Stop QGA VSS provider service using Winsvc API */
STDAPI StopService(void)
{
HRESULT hr;
- COMInitializer initializer;
- COMPointer<IUnknown> pUnknown;
- COMPointer<ICOMAdminCatalog2> pCatalog;
+ SC_HANDLE manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+ SC_HANDLE service = NULL;
- int count = 0;
+ if (!manager) {
+ errmsg(E_FAIL, "Failed to open service manager");
+ hr = E_FAIL;
+ goto out;
+ }
+ service = OpenService(manager, QGA_PROVIDER_NAME, SC_MANAGER_ALL_ACCESS);
- chk(QGAProviderFind(QGAProviderCount, (void *)&count));
- if (count) {
- chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER,
- IID_IUnknown, (void **)pUnknown.replace()));
- chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog2,
- (void **)pCatalog.replace()));
- chk(pCatalog->ShutdownApplication(_bstr_t(QGA_PROVIDER_LNAME)));
+ if (!service) {
+ errmsg(E_FAIL, "Failed to open service");
+ hr = E_FAIL;
+ goto out;
+ }
+ if (!(ControlService(service, SERVICE_CONTROL_STOP, NULL))) {
+ errmsg(E_FAIL, "Failed to stop service");
+ hr = E_FAIL;
}
out:
+ CloseServiceHandle(service);
+ CloseServiceHandle(manager);
return hr;
}
}
}
-static ObjectProperty *
+ObjectProperty *
object_property_try_add(Object *obj, const char *name, const char *type,
ObjectPropertyAccessor *get,
ObjectPropertyAccessor *set,
}
ObjectProperty *
-object_property_add_child(Object *obj, const char *name,
- Object *child)
+object_property_try_add_child(Object *obj, const char *name,
+ Object *child, Error **errp)
{
g_autofree char *type = NULL;
ObjectProperty *op;
type = g_strdup_printf("child<%s>", object_get_typename(child));
- op = object_property_add(obj, name, type, object_get_child_property, NULL,
- object_finalize_child_property, child);
+ op = object_property_try_add(obj, name, type, object_get_child_property,
+ NULL, object_finalize_child_property,
+ child, errp);
+ if (!op) {
+ return NULL;
+ }
op->resolve = object_resolve_child_property;
object_ref(child);
child->parent = obj;
return op;
}
+ObjectProperty *
+object_property_add_child(Object *obj, const char *name,
+ Object *child)
+{
+ return object_property_try_add_child(obj, name, child, &error_abort);
+}
+
void object_property_allow_set_link(const Object *obj, const char *name,
Object *val, Error **errp)
{
}
if (id != NULL) {
- object_property_add_child(object_get_objects_root(),
- id, obj);
+ object_property_try_add_child(object_get_objects_root(),
+ id, obj, &local_err);
+ if (local_err) {
+ goto out;
+ }
}
if (!user_creatable_complete(USER_CREATABLE(obj), &local_err)) {
static int qom_composition_compare(const void *a, const void *b, void *ignore)
{
- return g_strcmp0(a ? object_get_canonical_path_component(a) : NULL,
- b ? object_get_canonical_path_component(b) : NULL);
+ g_autofree char *ac = object_get_canonical_path_component(a);
+ g_autofree char *bc = object_get_canonical_path_component(b);
+
+ return g_strcmp0(ac, bc);
}
static int insert_qom_composition_child(Object *obj, void *opaque)
+++ /dev/null
-/*
- * Test Server
- *
- * Copyright IBM, Corp. 2011
- *
- * Authors:
- * Anthony Liguori <aliguori@us.ibm.com>
- *
- * 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 "qapi/error.h"
-#include "cpu.h"
-#include "sysemu/qtest.h"
-#include "sysemu/runstate.h"
-#include "chardev/char-fe.h"
-#include "exec/ioport.h"
-#include "exec/memory.h"
-#include "hw/irq.h"
-#include "sysemu/accel.h"
-#include "sysemu/cpus.h"
-#include "qemu/config-file.h"
-#include "qemu/option.h"
-#include "qemu/error-report.h"
-#include "qemu/module.h"
-#include "qemu/cutils.h"
-#include "config-devices.h"
-#ifdef CONFIG_PSERIES
-#include "hw/ppc/spapr_rtas.h"
-#endif
-
-#define MAX_IRQ 256
-
-bool qtest_allowed;
-
-static DeviceState *irq_intercept_dev;
-static FILE *qtest_log_fp;
-static CharBackend qtest_chr;
-static GString *inbuf;
-static int irq_levels[MAX_IRQ];
-static qemu_timeval start_time;
-static bool qtest_opened;
-static void (*qtest_server_send)(void*, const char*);
-static void *qtest_server_send_opaque;
-
-#define FMT_timeval "%ld.%06ld"
-
-/**
- * QTest Protocol
- *
- * Line based protocol, request/response based. Server can send async messages
- * so clients should always handle many async messages before the response
- * comes in.
- *
- * Valid requests
- *
- * Clock management:
- *
- * The qtest client is completely in charge of the QEMU_CLOCK_VIRTUAL. qtest commands
- * let you adjust the value of the clock (monotonically). All the commands
- * return the current value of the clock in nanoseconds.
- *
- * > clock_step
- * < OK VALUE
- *
- * Advance the clock to the next deadline. Useful when waiting for
- * asynchronous events.
- *
- * > clock_step NS
- * < OK VALUE
- *
- * Advance the clock by NS nanoseconds.
- *
- * > clock_set NS
- * < OK VALUE
- *
- * Advance the clock to NS nanoseconds (do nothing if it's already past).
- *
- * PIO and memory access:
- *
- * > outb ADDR VALUE
- * < OK
- *
- * > outw ADDR VALUE
- * < OK
- *
- * > outl ADDR VALUE
- * < OK
- *
- * > inb ADDR
- * < OK VALUE
- *
- * > inw ADDR
- * < OK VALUE
- *
- * > inl ADDR
- * < OK VALUE
- *
- * > writeb ADDR VALUE
- * < OK
- *
- * > writew ADDR VALUE
- * < OK
- *
- * > writel ADDR VALUE
- * < OK
- *
- * > writeq ADDR VALUE
- * < OK
- *
- * > readb ADDR
- * < OK VALUE
- *
- * > readw ADDR
- * < OK VALUE
- *
- * > readl ADDR
- * < OK VALUE
- *
- * > readq ADDR
- * < OK VALUE
- *
- * > read ADDR SIZE
- * < OK DATA
- *
- * > write ADDR SIZE DATA
- * < OK
- *
- * > b64read ADDR SIZE
- * < OK B64_DATA
- *
- * > b64write ADDR SIZE B64_DATA
- * < OK
- *
- * > memset ADDR SIZE VALUE
- * < OK
- *
- * ADDR, SIZE, VALUE are all integers parsed with strtoul() with a base of 0.
- * For 'memset' a zero size is permitted and does nothing.
- *
- * DATA is an arbitrarily long hex number prefixed with '0x'. If it's smaller
- * than the expected size, the value will be zero filled at the end of the data
- * sequence.
- *
- * B64_DATA is an arbitrarily long base64 encoded string.
- * If the sizes do not match, the data will be truncated.
- *
- * IRQ management:
- *
- * > irq_intercept_in QOM-PATH
- * < OK
- *
- * > irq_intercept_out QOM-PATH
- * < OK
- *
- * Attach to the gpio-in (resp. gpio-out) pins exported by the device at
- * QOM-PATH. When the pin is triggered, one of the following async messages
- * will be printed to the qtest stream:
- *
- * IRQ raise NUM
- * IRQ lower NUM
- *
- * where NUM is an IRQ number. For the PC, interrupts can be intercepted
- * simply with "irq_intercept_in ioapic" (note that IRQ0 comes out with
- * NUM=0 even though it is remapped to GSI 2).
- *
- * Setting interrupt level:
- *
- * > set_irq_in QOM-PATH NAME NUM LEVEL
- * < OK
- *
- * where NAME is the name of the irq/gpio list, NUM is an IRQ number and
- * LEVEL is an signed integer IRQ level.
- *
- * Forcibly set the given interrupt pin to the given level.
- *
- */
-
-static int hex2nib(char ch)
-{
- if (ch >= '0' && ch <= '9') {
- return ch - '0';
- } else if (ch >= 'a' && ch <= 'f') {
- return 10 + (ch - 'a');
- } else if (ch >= 'A' && ch <= 'F') {
- return 10 + (ch - 'A');
- } else {
- return -1;
- }
-}
-
-static void qtest_get_time(qemu_timeval *tv)
-{
- qemu_gettimeofday(tv);
- tv->tv_sec -= start_time.tv_sec;
- tv->tv_usec -= start_time.tv_usec;
- if (tv->tv_usec < 0) {
- tv->tv_usec += 1000000;
- tv->tv_sec -= 1;
- }
-}
-
-static void qtest_send_prefix(CharBackend *chr)
-{
- qemu_timeval tv;
-
- if (!qtest_log_fp || !qtest_opened) {
- return;
- }
-
- qtest_get_time(&tv);
- fprintf(qtest_log_fp, "[S +" FMT_timeval "] ",
- (long) tv.tv_sec, (long) tv.tv_usec);
-}
-
-static void GCC_FMT_ATTR(1, 2) qtest_log_send(const char *fmt, ...)
-{
- va_list ap;
-
- if (!qtest_log_fp || !qtest_opened) {
- return;
- }
-
- qtest_send_prefix(NULL);
-
- va_start(ap, fmt);
- vfprintf(qtest_log_fp, fmt, ap);
- va_end(ap);
-}
-
-static void qtest_server_char_be_send(void *opaque, const char *str)
-{
- size_t len = strlen(str);
- CharBackend* chr = (CharBackend *)opaque;
- qemu_chr_fe_write_all(chr, (uint8_t *)str, len);
- if (qtest_log_fp && qtest_opened) {
- fprintf(qtest_log_fp, "%s", str);
- }
-}
-
-static void qtest_send(CharBackend *chr, const char *str)
-{
- qtest_server_send(qtest_server_send_opaque, str);
-}
-
-static void GCC_FMT_ATTR(2, 3) qtest_sendf(CharBackend *chr,
- const char *fmt, ...)
-{
- va_list ap;
- gchar *buffer;
-
- va_start(ap, fmt);
- buffer = g_strdup_vprintf(fmt, ap);
- qtest_send(chr, buffer);
- g_free(buffer);
- va_end(ap);
-}
-
-static void qtest_irq_handler(void *opaque, int n, int level)
-{
- qemu_irq old_irq = *(qemu_irq *)opaque;
- qemu_set_irq(old_irq, level);
-
- if (irq_levels[n] != level) {
- CharBackend *chr = &qtest_chr;
- irq_levels[n] = level;
- qtest_send_prefix(chr);
- qtest_sendf(chr, "IRQ %s %d\n",
- level ? "raise" : "lower", n);
- }
-}
-
-static void qtest_process_command(CharBackend *chr, gchar **words)
-{
- const gchar *command;
-
- g_assert(words);
-
- command = words[0];
-
- if (qtest_log_fp) {
- qemu_timeval tv;
- int i;
-
- qtest_get_time(&tv);
- fprintf(qtest_log_fp, "[R +" FMT_timeval "]",
- (long) tv.tv_sec, (long) tv.tv_usec);
- for (i = 0; words[i]; i++) {
- fprintf(qtest_log_fp, " %s", words[i]);
- }
- fprintf(qtest_log_fp, "\n");
- }
-
- g_assert(command);
- if (strcmp(words[0], "irq_intercept_out") == 0
- || strcmp(words[0], "irq_intercept_in") == 0) {
- DeviceState *dev;
- NamedGPIOList *ngl;
-
- g_assert(words[1]);
- dev = DEVICE(object_resolve_path(words[1], NULL));
- if (!dev) {
- qtest_send_prefix(chr);
- qtest_send(chr, "FAIL Unknown device\n");
- return;
- }
-
- if (irq_intercept_dev) {
- qtest_send_prefix(chr);
- if (irq_intercept_dev != dev) {
- qtest_send(chr, "FAIL IRQ intercept already enabled\n");
- } else {
- qtest_send(chr, "OK\n");
- }
- return;
- }
-
- QLIST_FOREACH(ngl, &dev->gpios, node) {
- /* We don't support intercept of named GPIOs yet */
- if (ngl->name) {
- continue;
- }
- if (words[0][14] == 'o') {
- int i;
- for (i = 0; i < ngl->num_out; ++i) {
- qemu_irq *disconnected = g_new0(qemu_irq, 1);
- qemu_irq icpt = qemu_allocate_irq(qtest_irq_handler,
- disconnected, i);
-
- *disconnected = qdev_intercept_gpio_out(dev, icpt,
- ngl->name, i);
- }
- } else {
- qemu_irq_intercept_in(ngl->in, qtest_irq_handler,
- ngl->num_in);
- }
- }
- irq_intercept_dev = dev;
- qtest_send_prefix(chr);
- qtest_send(chr, "OK\n");
- } else if (strcmp(words[0], "set_irq_in") == 0) {
- DeviceState *dev;
- qemu_irq irq;
- char *name;
- int ret;
- int num;
- int level;
-
- g_assert(words[1] && words[2] && words[3] && words[4]);
-
- dev = DEVICE(object_resolve_path(words[1], NULL));
- if (!dev) {
- qtest_send_prefix(chr);
- qtest_send(chr, "FAIL Unknown device\n");
- return;
- }
-
- if (strcmp(words[2], "unnamed-gpio-in") == 0) {
- name = NULL;
- } else {
- name = words[2];
- }
-
- ret = qemu_strtoi(words[3], NULL, 0, &num);
- g_assert(!ret);
- ret = qemu_strtoi(words[4], NULL, 0, &level);
- g_assert(!ret);
-
- irq = qdev_get_gpio_in_named(dev, name, num);
-
- qemu_set_irq(irq, level);
- qtest_send_prefix(chr);
- qtest_send(chr, "OK\n");
- } else if (strcmp(words[0], "outb") == 0 ||
- strcmp(words[0], "outw") == 0 ||
- strcmp(words[0], "outl") == 0) {
- unsigned long addr;
- unsigned long value;
- int ret;
-
- g_assert(words[1] && words[2]);
- ret = qemu_strtoul(words[1], NULL, 0, &addr);
- g_assert(ret == 0);
- ret = qemu_strtoul(words[2], NULL, 0, &value);
- g_assert(ret == 0);
- g_assert(addr <= 0xffff);
-
- if (words[0][3] == 'b') {
- cpu_outb(addr, value);
- } else if (words[0][3] == 'w') {
- cpu_outw(addr, value);
- } else if (words[0][3] == 'l') {
- cpu_outl(addr, value);
- }
- qtest_send_prefix(chr);
- qtest_send(chr, "OK\n");
- } else if (strcmp(words[0], "inb") == 0 ||
- strcmp(words[0], "inw") == 0 ||
- strcmp(words[0], "inl") == 0) {
- unsigned long addr;
- uint32_t value = -1U;
- int ret;
-
- g_assert(words[1]);
- ret = qemu_strtoul(words[1], NULL, 0, &addr);
- g_assert(ret == 0);
- g_assert(addr <= 0xffff);
-
- if (words[0][2] == 'b') {
- value = cpu_inb(addr);
- } else if (words[0][2] == 'w') {
- value = cpu_inw(addr);
- } else if (words[0][2] == 'l') {
- value = cpu_inl(addr);
- }
- qtest_send_prefix(chr);
- qtest_sendf(chr, "OK 0x%04x\n", value);
- } else if (strcmp(words[0], "writeb") == 0 ||
- strcmp(words[0], "writew") == 0 ||
- strcmp(words[0], "writel") == 0 ||
- strcmp(words[0], "writeq") == 0) {
- uint64_t addr;
- uint64_t value;
- int ret;
-
- g_assert(words[1] && words[2]);
- ret = qemu_strtou64(words[1], NULL, 0, &addr);
- g_assert(ret == 0);
- ret = qemu_strtou64(words[2], NULL, 0, &value);
- g_assert(ret == 0);
-
- if (words[0][5] == 'b') {
- uint8_t data = value;
- address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
- &data, 1);
- } else if (words[0][5] == 'w') {
- uint16_t data = value;
- tswap16s(&data);
- address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
- &data, 2);
- } else if (words[0][5] == 'l') {
- uint32_t data = value;
- tswap32s(&data);
- address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
- &data, 4);
- } else if (words[0][5] == 'q') {
- uint64_t data = value;
- tswap64s(&data);
- address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
- &data, 8);
- }
- qtest_send_prefix(chr);
- qtest_send(chr, "OK\n");
- } else if (strcmp(words[0], "readb") == 0 ||
- strcmp(words[0], "readw") == 0 ||
- strcmp(words[0], "readl") == 0 ||
- strcmp(words[0], "readq") == 0) {
- uint64_t addr;
- uint64_t value = UINT64_C(-1);
- int ret;
-
- g_assert(words[1]);
- ret = qemu_strtou64(words[1], NULL, 0, &addr);
- g_assert(ret == 0);
-
- if (words[0][4] == 'b') {
- uint8_t data;
- address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
- &data, 1);
- value = data;
- } else if (words[0][4] == 'w') {
- uint16_t data;
- address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
- &data, 2);
- value = tswap16(data);
- } else if (words[0][4] == 'l') {
- uint32_t data;
- address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
- &data, 4);
- value = tswap32(data);
- } else if (words[0][4] == 'q') {
- address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
- &value, 8);
- tswap64s(&value);
- }
- qtest_send_prefix(chr);
- qtest_sendf(chr, "OK 0x%016" PRIx64 "\n", value);
- } else if (strcmp(words[0], "read") == 0) {
- uint64_t addr, len, i;
- uint8_t *data;
- char *enc;
- int ret;
-
- g_assert(words[1] && words[2]);
- ret = qemu_strtou64(words[1], NULL, 0, &addr);
- g_assert(ret == 0);
- ret = qemu_strtou64(words[2], NULL, 0, &len);
- g_assert(ret == 0);
- /* We'd send garbage to libqtest if len is 0 */
- g_assert(len);
-
- data = g_malloc(len);
- address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data,
- len);
-
- enc = g_malloc(2 * len + 1);
- for (i = 0; i < len; i++) {
- sprintf(&enc[i * 2], "%02x", data[i]);
- }
-
- qtest_send_prefix(chr);
- qtest_sendf(chr, "OK 0x%s\n", enc);
-
- g_free(data);
- g_free(enc);
- } else if (strcmp(words[0], "b64read") == 0) {
- uint64_t addr, len;
- uint8_t *data;
- gchar *b64_data;
- int ret;
-
- g_assert(words[1] && words[2]);
- ret = qemu_strtou64(words[1], NULL, 0, &addr);
- g_assert(ret == 0);
- ret = qemu_strtou64(words[2], NULL, 0, &len);
- g_assert(ret == 0);
-
- data = g_malloc(len);
- address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data,
- len);
- b64_data = g_base64_encode(data, len);
- qtest_send_prefix(chr);
- qtest_sendf(chr, "OK %s\n", b64_data);
-
- g_free(data);
- g_free(b64_data);
- } else if (strcmp(words[0], "write") == 0) {
- uint64_t addr, len, i;
- uint8_t *data;
- size_t data_len;
- int ret;
-
- g_assert(words[1] && words[2] && words[3]);
- ret = qemu_strtou64(words[1], NULL, 0, &addr);
- g_assert(ret == 0);
- ret = qemu_strtou64(words[2], NULL, 0, &len);
- g_assert(ret == 0);
-
- data_len = strlen(words[3]);
- if (data_len < 3) {
- qtest_send(chr, "ERR invalid argument size\n");
- return;
- }
-
- data = g_malloc(len);
- for (i = 0; i < len; i++) {
- if ((i * 2 + 4) <= data_len) {
- data[i] = hex2nib(words[3][i * 2 + 2]) << 4;
- data[i] |= hex2nib(words[3][i * 2 + 3]);
- } else {
- data[i] = 0;
- }
- }
- address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data,
- len);
- g_free(data);
-
- qtest_send_prefix(chr);
- qtest_send(chr, "OK\n");
- } else if (strcmp(words[0], "memset") == 0) {
- uint64_t addr, len;
- uint8_t *data;
- unsigned long pattern;
- int ret;
-
- g_assert(words[1] && words[2] && words[3]);
- ret = qemu_strtou64(words[1], NULL, 0, &addr);
- g_assert(ret == 0);
- ret = qemu_strtou64(words[2], NULL, 0, &len);
- g_assert(ret == 0);
- ret = qemu_strtoul(words[3], NULL, 0, &pattern);
- g_assert(ret == 0);
-
- if (len) {
- data = g_malloc(len);
- memset(data, pattern, len);
- address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
- data, len);
- g_free(data);
- }
-
- qtest_send_prefix(chr);
- qtest_send(chr, "OK\n");
- } else if (strcmp(words[0], "b64write") == 0) {
- uint64_t addr, len;
- uint8_t *data;
- size_t data_len;
- gsize out_len;
- int ret;
-
- g_assert(words[1] && words[2] && words[3]);
- ret = qemu_strtou64(words[1], NULL, 0, &addr);
- g_assert(ret == 0);
- ret = qemu_strtou64(words[2], NULL, 0, &len);
- g_assert(ret == 0);
-
- data_len = strlen(words[3]);
- if (data_len < 3) {
- qtest_send(chr, "ERR invalid argument size\n");
- return;
- }
-
- data = g_base64_decode_inplace(words[3], &out_len);
- if (out_len != len) {
- qtest_log_send("b64write: data length mismatch (told %"PRIu64", "
- "found %zu)\n",
- len, out_len);
- out_len = MIN(out_len, len);
- }
-
- address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data,
- len);
-
- qtest_send_prefix(chr);
- qtest_send(chr, "OK\n");
- } else if (strcmp(words[0], "endianness") == 0) {
- qtest_send_prefix(chr);
-#if defined(TARGET_WORDS_BIGENDIAN)
- qtest_sendf(chr, "OK big\n");
-#else
- qtest_sendf(chr, "OK little\n");
-#endif
-#ifdef CONFIG_PSERIES
- } else if (strcmp(words[0], "rtas") == 0) {
- uint64_t res, args, ret;
- unsigned long nargs, nret;
- int rc;
-
- rc = qemu_strtoul(words[2], NULL, 0, &nargs);
- g_assert(rc == 0);
- rc = qemu_strtou64(words[3], NULL, 0, &args);
- g_assert(rc == 0);
- rc = qemu_strtoul(words[4], NULL, 0, &nret);
- g_assert(rc == 0);
- rc = qemu_strtou64(words[5], NULL, 0, &ret);
- g_assert(rc == 0);
- res = qtest_rtas_call(words[1], nargs, args, nret, ret);
-
- qtest_send_prefix(chr);
- qtest_sendf(chr, "OK %"PRIu64"\n", res);
-#endif
- } else if (qtest_enabled() && strcmp(words[0], "clock_step") == 0) {
- int64_t ns;
-
- if (words[1]) {
- int ret = qemu_strtoi64(words[1], NULL, 0, &ns);
- g_assert(ret == 0);
- } else {
- ns = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
- QEMU_TIMER_ATTR_ALL);
- }
- qtest_clock_warp(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns);
- qtest_send_prefix(chr);
- qtest_sendf(chr, "OK %"PRIi64"\n",
- (int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
- } else if (strcmp(words[0], "module_load") == 0) {
- g_assert(words[1] && words[2]);
-
- qtest_send_prefix(chr);
- if (module_load_one(words[1], words[2])) {
- qtest_sendf(chr, "OK\n");
- } else {
- qtest_sendf(chr, "FAIL\n");
- }
- } else if (qtest_enabled() && strcmp(words[0], "clock_set") == 0) {
- int64_t ns;
- int ret;
-
- g_assert(words[1]);
- ret = qemu_strtoi64(words[1], NULL, 0, &ns);
- g_assert(ret == 0);
- qtest_clock_warp(ns);
- qtest_send_prefix(chr);
- qtest_sendf(chr, "OK %"PRIi64"\n",
- (int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
- } else {
- qtest_send_prefix(chr);
- qtest_sendf(chr, "FAIL Unknown command '%s'\n", words[0]);
- }
-}
-
-static void qtest_process_inbuf(CharBackend *chr, GString *inbuf)
-{
- char *end;
-
- while ((end = strchr(inbuf->str, '\n')) != NULL) {
- size_t offset;
- GString *cmd;
- gchar **words;
-
- offset = end - inbuf->str;
-
- cmd = g_string_new_len(inbuf->str, offset);
- g_string_erase(inbuf, 0, offset + 1);
-
- words = g_strsplit(cmd->str, " ", 0);
- qtest_process_command(chr, words);
- g_strfreev(words);
-
- g_string_free(cmd, TRUE);
- }
-}
-
-static void qtest_read(void *opaque, const uint8_t *buf, int size)
-{
- CharBackend *chr = opaque;
-
- g_string_append_len(inbuf, (const gchar *)buf, size);
- qtest_process_inbuf(chr, inbuf);
-}
-
-static int qtest_can_read(void *opaque)
-{
- return 1024;
-}
-
-static void qtest_event(void *opaque, QEMUChrEvent event)
-{
- int i;
-
- switch (event) {
- case CHR_EVENT_OPENED:
- /*
- * We used to call qemu_system_reset() here, hoping we could
- * use the same process for multiple tests that way. Never
- * used. Injects an extra reset even when it's not used, and
- * that can mess up tests, e.g. -boot once.
- */
- for (i = 0; i < ARRAY_SIZE(irq_levels); i++) {
- irq_levels[i] = 0;
- }
- qemu_gettimeofday(&start_time);
- qtest_opened = true;
- if (qtest_log_fp) {
- fprintf(qtest_log_fp, "[I " FMT_timeval "] OPENED\n",
- (long) start_time.tv_sec, (long) start_time.tv_usec);
- }
- break;
- case CHR_EVENT_CLOSED:
- qtest_opened = false;
- if (qtest_log_fp) {
- qemu_timeval tv;
- qtest_get_time(&tv);
- fprintf(qtest_log_fp, "[I +" FMT_timeval "] CLOSED\n",
- (long) tv.tv_sec, (long) tv.tv_usec);
- }
- break;
- default:
- break;
- }
-}
-void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error **errp)
-{
- Chardev *chr;
-
- chr = qemu_chr_new("qtest", qtest_chrdev, NULL);
-
- if (chr == NULL) {
- error_setg(errp, "Failed to initialize device for qtest: \"%s\"",
- qtest_chrdev);
- return;
- }
-
- if (qtest_log) {
- if (strcmp(qtest_log, "none") != 0) {
- qtest_log_fp = fopen(qtest_log, "w+");
- }
- } else {
- qtest_log_fp = stderr;
- }
-
- qemu_chr_fe_init(&qtest_chr, chr, errp);
- qemu_chr_fe_set_handlers(&qtest_chr, qtest_can_read, qtest_read,
- qtest_event, NULL, &qtest_chr, NULL, true);
- qemu_chr_fe_set_echo(&qtest_chr, true);
-
- inbuf = g_string_new("");
-
- if (!qtest_server_send) {
- qtest_server_set_send_handler(qtest_server_char_be_send, &qtest_chr);
- }
-}
-
-void qtest_server_set_send_handler(void (*send)(void*, const char*),
- void *opaque)
-{
- qtest_server_send = send;
- qtest_server_send_opaque = opaque;
-}
-
-bool qtest_driver(void)
-{
- return qtest_chr.chr != NULL;
-}
-
-void qtest_server_inproc_recv(void *dummy, const char *buf)
-{
- static GString *gstr;
- if (!gstr) {
- gstr = g_string_new(NULL);
- }
- g_string_append(gstr, buf);
- if (gstr->str[gstr->len - 1] == '\n') {
- qtest_process_inbuf(NULL, gstr);
- g_string_truncate(gstr, 0);
- }
-}
Options:
-q, --quiet quiet
- --no-tree run without a kernel tree
+ --no-tree run without a qemu tree
--no-signoff do not check for 'Signed-off-by' line
--patch treat FILE as patchfile
--branch treat args as GIT revision list
--terse one line per report
-f, --file treat FILE as regular source file
--strict fail if only warnings are found
- --root=PATH PATH to the kernel tree root
+ --root=PATH PATH to the qemu tree root
--no-summary suppress the per-file summary
--mailback only produce a report in case of warnings/errors
--summary-file include the filename in summary
}
if (!defined $root) {
- print "Must be run from the top-level dir. of a kernel tree\n";
+ print "Must be run from the top-level dir. of a qemu tree\n";
exit(2);
}
}
--- /dev/null
+#!/usr/bin/env python3
+#
+# Copyright (c) 2019-2020 Red Hat, Inc.
+#
+# Author:
+# Cleber Rosa <crosa@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+"""
+Checks the GitLab pipeline status for a given commit ID
+"""
+
+# pylint: disable=C0103
+
+import argparse
+import http.client
+import json
+import os
+import subprocess
+import time
+import sys
+
+
+def get_local_staging_branch_commit():
+ """
+ Returns the commit sha1 for the *local* branch named "staging"
+ """
+ result = subprocess.run(['git', 'rev-parse', 'staging'],
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.DEVNULL,
+ cwd=os.path.dirname(__file__),
+ universal_newlines=True).stdout.strip()
+ if result == 'staging':
+ raise ValueError("There's no local branch named 'staging'")
+ if len(result) != 40:
+ raise ValueError("Branch staging HEAD doesn't look like a sha1")
+ return result
+
+
+def get_pipeline_status(project_id, commit_sha1):
+ """
+ Returns the JSON content of the pipeline status API response
+ """
+ url = '/api/v4/projects/{}/pipelines?sha={}'.format(project_id,
+ commit_sha1)
+ connection = http.client.HTTPSConnection('gitlab.com')
+ connection.request('GET', url=url)
+ response = connection.getresponse()
+ if response.code != http.HTTPStatus.OK:
+ raise ValueError("Failed to receive a successful response")
+ json_response = json.loads(response.read())
+
+ # As far as I can tell, there should be only one pipeline for the same
+ # project + commit. If this assumption is false, we can add further
+ # filters to the url, such as username, and order_by.
+ if not json_response:
+ raise ValueError("No pipeline found")
+ return json_response[0]
+
+
+def wait_on_pipeline_success(timeout, interval,
+ project_id, commit_sha):
+ """
+ Waits for the pipeline to finish within the given timeout
+ """
+ start = time.time()
+ while True:
+ if time.time() >= (start + timeout):
+ print("Waiting on the pipeline timed out")
+ return False
+
+ status = get_pipeline_status(project_id, commit_sha)
+ if status['status'] == 'running':
+ time.sleep(interval)
+ print('running...')
+ continue
+
+ if status['status'] == 'success':
+ return True
+
+ msg = "Pipeline failed, check: %s" % status['web_url']
+ print(msg)
+ return False
+
+
+def main():
+ """
+ Script entry point
+ """
+ parser = argparse.ArgumentParser(
+ prog='pipeline-status',
+ description='check or wait on a pipeline status')
+
+ parser.add_argument('-t', '--timeout', type=int, default=7200,
+ help=('Amount of time (in seconds) to wait for the '
+ 'pipeline to complete. Defaults to '
+ '%(default)s'))
+ parser.add_argument('-i', '--interval', type=int, default=60,
+ help=('Amount of time (in seconds) to wait between '
+ 'checks of the pipeline status. Defaults '
+ 'to %(default)s'))
+ parser.add_argument('-w', '--wait', action='store_true', default=False,
+ help=('Wether to wait, instead of checking only once '
+ 'the status of a pipeline'))
+ parser.add_argument('-p', '--project-id', type=int, default=11167699,
+ help=('The GitLab project ID. Defaults to the project '
+ 'for https://gitlab.com/qemu-project/qemu, that '
+ 'is, "%(default)s"'))
+ try:
+ default_commit = get_local_staging_branch_commit()
+ commit_required = False
+ except ValueError:
+ default_commit = ''
+ commit_required = True
+ parser.add_argument('-c', '--commit', required=commit_required,
+ default=default_commit,
+ help=('Look for a pipeline associated with the given '
+ 'commit. If one is not explicitly given, the '
+ 'commit associated with the local branch named '
+ '"staging" is used. Default: %(default)s'))
+ parser.add_argument('--verbose', action='store_true', default=False,
+ help=('A minimal verbosity level that prints the '
+ 'overall result of the check/wait'))
+
+ args = parser.parse_args()
+
+ try:
+ if args.wait:
+ success = wait_on_pipeline_success(
+ args.timeout,
+ args.interval,
+ args.project_id,
+ args.commit)
+ else:
+ status = get_pipeline_status(args.project_id,
+ args.commit)
+ success = status['status'] == 'success'
+ except Exception as error: # pylint: disable=W0703
+ success = False
+ if args.verbose:
+ print("ERROR: %s" % error.args[0])
+
+ if success:
+ if args.verbose:
+ print('success')
+ sys.exit(0)
+ else:
+ if args.verbose:
+ print('failure')
+ sys.exit(1)
+
+
+if __name__ == '__main__':
+ main()
# but we can't tell which ones. Prevent issues from the compiler by
# suppressing redundant declaration warnings.
if anyextern:
- output("#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE\n",
- "# pragma GCC diagnostic push\n",
- "# pragma GCC diagnostic ignored \"-Wredundant-decls\"\n",
- "# ifdef __clang__\n"
+ output("#pragma GCC diagnostic push\n",
+ "#pragma GCC diagnostic ignored \"-Wredundant-decls\"\n",
+ "#ifdef __clang__\n"
"# pragma GCC diagnostic ignored \"-Wtypedef-redefinition\"\n",
- "# endif\n",
"#endif\n\n")
out_pats = {}
output('\n')
if anyextern:
- output("#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE\n",
- "# pragma GCC diagnostic pop\n",
- "#endif\n\n")
+ output("#pragma GCC diagnostic pop\n\n")
for n in sorted(formats.keys()):
f = formats[n]
--- /dev/null
+#!/usr/bin/env python3
+
+# Print the percentage of instructions spent in each phase of QEMU
+# execution.
+#
+# Syntax:
+# dissect.py [-h] -- <qemu executable> [<qemu executable options>] \
+# <target executable> [<target executable options>]
+#
+# [-h] - Print the script arguments help message.
+#
+# Example of usage:
+# dissect.py -- qemu-arm coulomb_double-arm
+#
+# This file is a part of the project "TCG Continuous Benchmarking".
+#
+# Copyright (C) 2020 Ahmed Karaman <ahmedkhaledkaraman@gmail.com>
+# Copyright (C) 2020 Aleksandar Markovic <aleksandar.qemu.devel@gmail.com>
+#
+# 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 <https://www.gnu.org/licenses/>.
+
+import argparse
+import os
+import subprocess
+import sys
+import tempfile
+
+
+def get_JIT_line(callgrind_data):
+ """
+ Search for the first instance of the JIT call in
+ the callgrind_annotate output when ran using --tree=caller
+ This is equivalent to the self number of instructions of JIT.
+
+ Parameters:
+ callgrind_data (list): callgrind_annotate output
+
+ Returns:
+ (int): Line number
+ """
+ line = -1
+ for i in range(len(callgrind_data)):
+ if callgrind_data[i].strip('\n') and \
+ callgrind_data[i].split()[-1] == "[???]":
+ line = i
+ break
+ if line == -1:
+ sys.exit("Couldn't locate the JIT call ... Exiting.")
+ return line
+
+
+def main():
+ # Parse the command line arguments
+ parser = argparse.ArgumentParser(
+ usage='dissect.py [-h] -- '
+ '<qemu executable> [<qemu executable options>] '
+ '<target executable> [<target executable options>]')
+
+ parser.add_argument('command', type=str, nargs='+', help=argparse.SUPPRESS)
+
+ args = parser.parse_args()
+
+ # Extract the needed variables from the args
+ command = args.command
+
+ # Insure that valgrind is installed
+ check_valgrind = subprocess.run(
+ ["which", "valgrind"], stdout=subprocess.DEVNULL)
+ if check_valgrind.returncode:
+ sys.exit("Please install valgrind before running the script.")
+
+ # Save all intermediate files in a temporary directory
+ with tempfile.TemporaryDirectory() as tmpdirname:
+ # callgrind output file path
+ data_path = os.path.join(tmpdirname, "callgrind.data")
+ # callgrind_annotate output file path
+ annotate_out_path = os.path.join(tmpdirname, "callgrind_annotate.out")
+
+ # Run callgrind
+ callgrind = subprocess.run((["valgrind",
+ "--tool=callgrind",
+ "--callgrind-out-file=" + data_path]
+ + command),
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.PIPE)
+ if callgrind.returncode:
+ sys.exit(callgrind.stderr.decode("utf-8"))
+
+ # Save callgrind_annotate output
+ with open(annotate_out_path, "w") as output:
+ callgrind_annotate = subprocess.run(
+ ["callgrind_annotate", data_path, "--tree=caller"],
+ stdout=output,
+ stderr=subprocess.PIPE)
+ if callgrind_annotate.returncode:
+ sys.exit(callgrind_annotate.stderr.decode("utf-8"))
+
+ # Read the callgrind_annotate output to callgrind_data[]
+ callgrind_data = []
+ with open(annotate_out_path, 'r') as data:
+ callgrind_data = data.readlines()
+
+ # Line number with the total number of instructions
+ total_instructions_line_number = 20
+ # Get the total number of instructions
+ total_instructions_line_data = \
+ callgrind_data[total_instructions_line_number]
+ total_instructions = total_instructions_line_data.split()[0]
+ total_instructions = int(total_instructions.replace(',', ''))
+
+ # Line number with the JIT self number of instructions
+ JIT_self_instructions_line_number = get_JIT_line(callgrind_data)
+ # Get the JIT self number of instructions
+ JIT_self_instructions_line_data = \
+ callgrind_data[JIT_self_instructions_line_number]
+ JIT_self_instructions = JIT_self_instructions_line_data.split()[0]
+ JIT_self_instructions = int(JIT_self_instructions.replace(',', ''))
+
+ # Line number with the JIT self + inclusive number of instructions
+ # It's the line above the first JIT call when running with --tree=caller
+ JIT_total_instructions_line_number = JIT_self_instructions_line_number-1
+ # Get the JIT self + inclusive number of instructions
+ JIT_total_instructions_line_data = \
+ callgrind_data[JIT_total_instructions_line_number]
+ JIT_total_instructions = JIT_total_instructions_line_data.split()[0]
+ JIT_total_instructions = int(JIT_total_instructions.replace(',', ''))
+
+ # Calculate number of instructions in helpers and code generation
+ helpers_instructions = JIT_total_instructions-JIT_self_instructions
+ code_generation_instructions = total_instructions-JIT_total_instructions
+
+ # Print results (Insert commas in large numbers)
+ # Print total number of instructions
+ print('{:<20}{:>20}\n'.
+ format("Total Instructions:",
+ format(total_instructions, ',')))
+ # Print code generation instructions and percentage
+ print('{:<20}{:>20}\t{:>6.3f}%'.
+ format("Code Generation:",
+ format(code_generation_instructions, ","),
+ (code_generation_instructions / total_instructions) * 100))
+ # Print JIT instructions and percentage
+ print('{:<20}{:>20}\t{:>6.3f}%'.
+ format("JIT Execution:",
+ format(JIT_self_instructions, ","),
+ (JIT_self_instructions / total_instructions) * 100))
+ # Print helpers instructions and percentage
+ print('{:<20}{:>20}\t{:>6.3f}%'.
+ format("Helpers:",
+ format(helpers_instructions, ","),
+ (helpers_instructions/total_instructions)*100))
+
+
+if __name__ == "__main__":
+ main()
from graphviz import Digraph
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'python'))
-from qemu.machine import MonitorResponseError
+from qemu.qmp import (
+ QEMUMonitorProtocol,
+ QMPResponseError,
+)
def perm(arr):
reply = json.loads(subprocess.check_output(ar))
if 'error' in reply:
- raise MonitorResponseError(reply)
+ raise QMPResponseError(reply)
return reply['return']
sub testsuite_error ($)
{
- report "ERROR", "- $_[0]";
+ report "ERROR", "$test_name - $_[0]";
}
sub handle_tap_result ($)
softmmu-main-y = softmmu/main.o
+
+obj-y += arch_init.o
+obj-y += cpus.o
+obj-y += cpu-throttle.o
+obj-y += balloon.o
+obj-y += ioport.o
+obj-y += memory.o
+obj-y += memory_mapping.o
+
+obj-y += qtest.o
+
obj-y += vl.o
vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS)
--- /dev/null
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 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.
+ */
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/arch_init.h"
+#include "hw/pci/pci.h"
+#include "hw/audio/soundhw.h"
+#include "qapi/error.h"
+#include "qemu/config-file.h"
+#include "qemu/error-report.h"
+#include "hw/acpi/acpi.h"
+#include "qemu/help_option.h"
+
+#ifdef TARGET_SPARC
+int graphic_width = 1024;
+int graphic_height = 768;
+int graphic_depth = 8;
+#elif defined(TARGET_M68K)
+int graphic_width = 800;
+int graphic_height = 600;
+int graphic_depth = 8;
+#else
+int graphic_width = 800;
+int graphic_height = 600;
+int graphic_depth = 32;
+#endif
+
+
+#if defined(TARGET_ALPHA)
+#define QEMU_ARCH QEMU_ARCH_ALPHA
+#elif defined(TARGET_ARM)
+#define QEMU_ARCH QEMU_ARCH_ARM
+#elif defined(TARGET_CRIS)
+#define QEMU_ARCH QEMU_ARCH_CRIS
+#elif defined(TARGET_HPPA)
+#define QEMU_ARCH QEMU_ARCH_HPPA
+#elif defined(TARGET_I386)
+#define QEMU_ARCH QEMU_ARCH_I386
+#elif defined(TARGET_LM32)
+#define QEMU_ARCH QEMU_ARCH_LM32
+#elif defined(TARGET_M68K)
+#define QEMU_ARCH QEMU_ARCH_M68K
+#elif defined(TARGET_MICROBLAZE)
+#define QEMU_ARCH QEMU_ARCH_MICROBLAZE
+#elif defined(TARGET_MIPS)
+#define QEMU_ARCH QEMU_ARCH_MIPS
+#elif defined(TARGET_MOXIE)
+#define QEMU_ARCH QEMU_ARCH_MOXIE
+#elif defined(TARGET_NIOS2)
+#define QEMU_ARCH QEMU_ARCH_NIOS2
+#elif defined(TARGET_OPENRISC)
+#define QEMU_ARCH QEMU_ARCH_OPENRISC
+#elif defined(TARGET_PPC)
+#define QEMU_ARCH QEMU_ARCH_PPC
+#elif defined(TARGET_RISCV)
+#define QEMU_ARCH QEMU_ARCH_RISCV
+#elif defined(TARGET_RX)
+#define QEMU_ARCH QEMU_ARCH_RX
+#elif defined(TARGET_S390X)
+#define QEMU_ARCH QEMU_ARCH_S390X
+#elif defined(TARGET_SH4)
+#define QEMU_ARCH QEMU_ARCH_SH4
+#elif defined(TARGET_SPARC)
+#define QEMU_ARCH QEMU_ARCH_SPARC
+#elif defined(TARGET_TRICORE)
+#define QEMU_ARCH QEMU_ARCH_TRICORE
+#elif defined(TARGET_UNICORE32)
+#define QEMU_ARCH QEMU_ARCH_UNICORE32
+#elif defined(TARGET_XTENSA)
+#define QEMU_ARCH QEMU_ARCH_XTENSA
+#elif defined(TARGET_AVR)
+#define QEMU_ARCH QEMU_ARCH_AVR
+#endif
+
+const uint32_t arch_type = QEMU_ARCH;
+
+int kvm_available(void)
+{
+#ifdef CONFIG_KVM
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+int xen_available(void)
+{
+#ifdef CONFIG_XEN
+ return 1;
+#else
+ return 0;
+#endif
+}
--- /dev/null
+/*
+ * Generic Balloon handlers and management
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ * Copyright (C) 2011 Red Hat, Inc.
+ * Copyright (C) 2011 Amit Shah <amit.shah@redhat.com>
+ *
+ * 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/atomic.h"
+#include "sysemu/kvm.h"
+#include "sysemu/balloon.h"
+#include "trace-root.h"
+#include "qapi/error.h"
+#include "qapi/qapi-commands-misc.h"
+#include "qapi/qmp/qerror.h"
+
+static QEMUBalloonEvent *balloon_event_fn;
+static QEMUBalloonStatus *balloon_stat_fn;
+static void *balloon_opaque;
+
+static bool have_balloon(Error **errp)
+{
+ if (kvm_enabled() && !kvm_has_sync_mmu()) {
+ error_set(errp, ERROR_CLASS_KVM_MISSING_CAP,
+ "Using KVM without synchronous MMU, balloon unavailable");
+ return false;
+ }
+ if (!balloon_event_fn) {
+ error_set(errp, ERROR_CLASS_DEVICE_NOT_ACTIVE,
+ "No balloon device has been activated");
+ return false;
+ }
+ return true;
+}
+
+int qemu_add_balloon_handler(QEMUBalloonEvent *event_func,
+ QEMUBalloonStatus *stat_func, void *opaque)
+{
+ if (balloon_event_fn || balloon_stat_fn || balloon_opaque) {
+ /* We're already registered one balloon handler. How many can
+ * a guest really have?
+ */
+ return -1;
+ }
+ balloon_event_fn = event_func;
+ balloon_stat_fn = stat_func;
+ balloon_opaque = opaque;
+ return 0;
+}
+
+void qemu_remove_balloon_handler(void *opaque)
+{
+ if (balloon_opaque != opaque) {
+ return;
+ }
+ balloon_event_fn = NULL;
+ balloon_stat_fn = NULL;
+ balloon_opaque = NULL;
+}
+
+BalloonInfo *qmp_query_balloon(Error **errp)
+{
+ BalloonInfo *info;
+
+ if (!have_balloon(errp)) {
+ return NULL;
+ }
+
+ info = g_malloc0(sizeof(*info));
+ balloon_stat_fn(balloon_opaque, info);
+ return info;
+}
+
+void qmp_balloon(int64_t target, Error **errp)
+{
+ if (!have_balloon(errp)) {
+ return;
+ }
+
+ if (target <= 0) {
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "target", "a size");
+ return;
+ }
+
+ trace_balloon_event(balloon_opaque, target);
+ balloon_event_fn(balloon_opaque, target);
+}
--- /dev/null
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 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.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/thread.h"
+#include "hw/core/cpu.h"
+#include "qemu/main-loop.h"
+#include "sysemu/cpus.h"
+#include "sysemu/cpu-throttle.h"
+
+/* vcpu throttling controls */
+static QEMUTimer *throttle_timer;
+static unsigned int throttle_percentage;
+
+#define CPU_THROTTLE_PCT_MIN 1
+#define CPU_THROTTLE_PCT_MAX 99
+#define CPU_THROTTLE_TIMESLICE_NS 10000000
+
+static void cpu_throttle_thread(CPUState *cpu, run_on_cpu_data opaque)
+{
+ double pct;
+ double throttle_ratio;
+ int64_t sleeptime_ns, endtime_ns;
+
+ if (!cpu_throttle_get_percentage()) {
+ return;
+ }
+
+ pct = (double)cpu_throttle_get_percentage() / 100;
+ throttle_ratio = pct / (1 - pct);
+ /* Add 1ns to fix double's rounding error (like 0.9999999...) */
+ sleeptime_ns = (int64_t)(throttle_ratio * CPU_THROTTLE_TIMESLICE_NS + 1);
+ endtime_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + sleeptime_ns;
+ while (sleeptime_ns > 0 && !cpu->stop) {
+ if (sleeptime_ns > SCALE_MS) {
+ qemu_cond_timedwait_iothread(cpu->halt_cond,
+ sleeptime_ns / SCALE_MS);
+ } else {
+ qemu_mutex_unlock_iothread();
+ g_usleep(sleeptime_ns / SCALE_US);
+ qemu_mutex_lock_iothread();
+ }
+ sleeptime_ns = endtime_ns - qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
+ }
+ atomic_set(&cpu->throttle_thread_scheduled, 0);
+}
+
+static void cpu_throttle_timer_tick(void *opaque)
+{
+ CPUState *cpu;
+ double pct;
+
+ /* Stop the timer if needed */
+ if (!cpu_throttle_get_percentage()) {
+ return;
+ }
+ CPU_FOREACH(cpu) {
+ if (!atomic_xchg(&cpu->throttle_thread_scheduled, 1)) {
+ async_run_on_cpu(cpu, cpu_throttle_thread,
+ RUN_ON_CPU_NULL);
+ }
+ }
+
+ pct = (double)cpu_throttle_get_percentage() / 100;
+ timer_mod(throttle_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) +
+ CPU_THROTTLE_TIMESLICE_NS / (1 - pct));
+}
+
+void cpu_throttle_set(int new_throttle_pct)
+{
+ /* Ensure throttle percentage is within valid range */
+ new_throttle_pct = MIN(new_throttle_pct, CPU_THROTTLE_PCT_MAX);
+ new_throttle_pct = MAX(new_throttle_pct, CPU_THROTTLE_PCT_MIN);
+
+ atomic_set(&throttle_percentage, new_throttle_pct);
+
+ timer_mod(throttle_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) +
+ CPU_THROTTLE_TIMESLICE_NS);
+}
+
+void cpu_throttle_stop(void)
+{
+ atomic_set(&throttle_percentage, 0);
+}
+
+bool cpu_throttle_active(void)
+{
+ return (cpu_throttle_get_percentage() != 0);
+}
+
+int cpu_throttle_get_percentage(void)
+{
+ return atomic_read(&throttle_percentage);
+}
+
+void cpu_throttle_init(void)
+{
+ throttle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT,
+ cpu_throttle_timer_tick, NULL);
+}
--- /dev/null
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 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.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/config-file.h"
+#include "qemu/cutils.h"
+#include "migration/vmstate.h"
+#include "monitor/monitor.h"
+#include "qapi/error.h"
+#include "qapi/qapi-commands-misc.h"
+#include "qapi/qapi-events-run-state.h"
+#include "qapi/qmp/qerror.h"
+#include "qemu/error-report.h"
+#include "qemu/qemu-print.h"
+#include "sysemu/tcg.h"
+#include "sysemu/block-backend.h"
+#include "exec/gdbstub.h"
+#include "sysemu/dma.h"
+#include "sysemu/hw_accel.h"
+#include "sysemu/kvm.h"
+#include "sysemu/hax.h"
+#include "sysemu/hvf.h"
+#include "sysemu/whpx.h"
+#include "exec/exec-all.h"
+
+#include "qemu/thread.h"
+#include "qemu/plugin.h"
+#include "sysemu/cpus.h"
+#include "sysemu/qtest.h"
+#include "qemu/main-loop.h"
+#include "qemu/option.h"
+#include "qemu/bitmap.h"
+#include "qemu/seqlock.h"
+#include "qemu/guest-random.h"
+#include "tcg/tcg.h"
+#include "hw/nmi.h"
+#include "sysemu/replay.h"
+#include "sysemu/runstate.h"
+#include "hw/boards.h"
+#include "hw/hw.h"
+
+#include "sysemu/cpu-throttle.h"
+
+#ifdef CONFIG_LINUX
+
+#include <sys/prctl.h>
+
+#ifndef PR_MCE_KILL
+#define PR_MCE_KILL 33
+#endif
+
+#ifndef PR_MCE_KILL_SET
+#define PR_MCE_KILL_SET 1
+#endif
+
+#ifndef PR_MCE_KILL_EARLY
+#define PR_MCE_KILL_EARLY 1
+#endif
+
+#endif /* CONFIG_LINUX */
+
+static QemuMutex qemu_global_mutex;
+
+int64_t max_delay;
+int64_t max_advance;
+
+bool cpu_is_stopped(CPUState *cpu)
+{
+ return cpu->stopped || !runstate_is_running();
+}
+
+static inline bool cpu_work_list_empty(CPUState *cpu)
+{
+ bool ret;
+
+ qemu_mutex_lock(&cpu->work_mutex);
+ ret = QSIMPLEQ_EMPTY(&cpu->work_list);
+ qemu_mutex_unlock(&cpu->work_mutex);
+ return ret;
+}
+
+static bool cpu_thread_is_idle(CPUState *cpu)
+{
+ if (cpu->stop || !cpu_work_list_empty(cpu)) {
+ return false;
+ }
+ if (cpu_is_stopped(cpu)) {
+ return true;
+ }
+ if (!cpu->halted || cpu_has_work(cpu) ||
+ kvm_halt_in_kernel()) {
+ return false;
+ }
+ return true;
+}
+
+static bool all_cpu_threads_idle(void)
+{
+ CPUState *cpu;
+
+ CPU_FOREACH(cpu) {
+ if (!cpu_thread_is_idle(cpu)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/***********************************************************/
+/* guest cycle counter */
+
+/* Protected by TimersState seqlock */
+
+static bool icount_sleep = true;
+/* Arbitrarily pick 1MIPS as the minimum allowable speed. */
+#define MAX_ICOUNT_SHIFT 10
+
+typedef struct TimersState {
+ /* Protected by BQL. */
+ int64_t cpu_ticks_prev;
+ int64_t cpu_ticks_offset;
+
+ /* Protect fields that can be respectively read outside the
+ * BQL, and written from multiple threads.
+ */
+ QemuSeqLock vm_clock_seqlock;
+ QemuSpin vm_clock_lock;
+
+ int16_t cpu_ticks_enabled;
+
+ /* Conversion factor from emulated instructions to virtual clock ticks. */
+ int16_t icount_time_shift;
+
+ /* Compensate for varying guest execution speed. */
+ int64_t qemu_icount_bias;
+
+ int64_t vm_clock_warp_start;
+ int64_t cpu_clock_offset;
+
+ /* Only written by TCG thread */
+ int64_t qemu_icount;
+
+ /* for adjusting icount */
+ QEMUTimer *icount_rt_timer;
+ QEMUTimer *icount_vm_timer;
+ QEMUTimer *icount_warp_timer;
+} TimersState;
+
+static TimersState timers_state;
+bool mttcg_enabled;
+
+
+/* The current number of executed instructions is based on what we
+ * originally budgeted minus the current state of the decrementing
+ * icount counters in extra/u16.low.
+ */
+static int64_t cpu_get_icount_executed(CPUState *cpu)
+{
+ return (cpu->icount_budget -
+ (cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra));
+}
+
+/*
+ * Update the global shared timer_state.qemu_icount to take into
+ * account executed instructions. This is done by the TCG vCPU
+ * thread so the main-loop can see time has moved forward.
+ */
+static void cpu_update_icount_locked(CPUState *cpu)
+{
+ int64_t executed = cpu_get_icount_executed(cpu);
+ cpu->icount_budget -= executed;
+
+ atomic_set_i64(&timers_state.qemu_icount,
+ timers_state.qemu_icount + executed);
+}
+
+/*
+ * Update the global shared timer_state.qemu_icount to take into
+ * account executed instructions. This is done by the TCG vCPU
+ * thread so the main-loop can see time has moved forward.
+ */
+void cpu_update_icount(CPUState *cpu)
+{
+ seqlock_write_lock(&timers_state.vm_clock_seqlock,
+ &timers_state.vm_clock_lock);
+ cpu_update_icount_locked(cpu);
+ seqlock_write_unlock(&timers_state.vm_clock_seqlock,
+ &timers_state.vm_clock_lock);
+}
+
+static int64_t cpu_get_icount_raw_locked(void)
+{
+ CPUState *cpu = current_cpu;
+
+ if (cpu && cpu->running) {
+ if (!cpu->can_do_io) {
+ error_report("Bad icount read");
+ exit(1);
+ }
+ /* Take into account what has run */
+ cpu_update_icount_locked(cpu);
+ }
+ /* The read is protected by the seqlock, but needs atomic64 to avoid UB */
+ return atomic_read_i64(&timers_state.qemu_icount);
+}
+
+static int64_t cpu_get_icount_locked(void)
+{
+ int64_t icount = cpu_get_icount_raw_locked();
+ return atomic_read_i64(&timers_state.qemu_icount_bias) +
+ cpu_icount_to_ns(icount);
+}
+
+int64_t cpu_get_icount_raw(void)
+{
+ int64_t icount;
+ unsigned start;
+
+ do {
+ start = seqlock_read_begin(&timers_state.vm_clock_seqlock);
+ icount = cpu_get_icount_raw_locked();
+ } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start));
+
+ return icount;
+}
+
+/* Return the virtual CPU time, based on the instruction counter. */
+int64_t cpu_get_icount(void)
+{
+ int64_t icount;
+ unsigned start;
+
+ do {
+ start = seqlock_read_begin(&timers_state.vm_clock_seqlock);
+ icount = cpu_get_icount_locked();
+ } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start));
+
+ return icount;
+}
+
+int64_t cpu_icount_to_ns(int64_t icount)
+{
+ return icount << atomic_read(&timers_state.icount_time_shift);
+}
+
+static int64_t cpu_get_ticks_locked(void)
+{
+ int64_t ticks = timers_state.cpu_ticks_offset;
+ if (timers_state.cpu_ticks_enabled) {
+ ticks += cpu_get_host_ticks();
+ }
+
+ if (timers_state.cpu_ticks_prev > ticks) {
+ /* Non increasing ticks may happen if the host uses software suspend. */
+ timers_state.cpu_ticks_offset += timers_state.cpu_ticks_prev - ticks;
+ ticks = timers_state.cpu_ticks_prev;
+ }
+
+ timers_state.cpu_ticks_prev = ticks;
+ return ticks;
+}
+
+/* return the time elapsed in VM between vm_start and vm_stop. Unless
+ * icount is active, cpu_get_ticks() uses units of the host CPU cycle
+ * counter.
+ */
+int64_t cpu_get_ticks(void)
+{
+ int64_t ticks;
+
+ if (use_icount) {
+ return cpu_get_icount();
+ }
+
+ qemu_spin_lock(&timers_state.vm_clock_lock);
+ ticks = cpu_get_ticks_locked();
+ qemu_spin_unlock(&timers_state.vm_clock_lock);
+ return ticks;
+}
+
+static int64_t cpu_get_clock_locked(void)
+{
+ int64_t time;
+
+ time = timers_state.cpu_clock_offset;
+ if (timers_state.cpu_ticks_enabled) {
+ time += get_clock();
+ }
+
+ return time;
+}
+
+/* Return the monotonic time elapsed in VM, i.e.,
+ * the time between vm_start and vm_stop
+ */
+int64_t cpu_get_clock(void)
+{
+ int64_t ti;
+ unsigned start;
+
+ do {
+ start = seqlock_read_begin(&timers_state.vm_clock_seqlock);
+ ti = cpu_get_clock_locked();
+ } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start));
+
+ return ti;
+}
+
+/* enable cpu_get_ticks()
+ * Caller must hold BQL which serves as mutex for vm_clock_seqlock.
+ */
+void cpu_enable_ticks(void)
+{
+ seqlock_write_lock(&timers_state.vm_clock_seqlock,
+ &timers_state.vm_clock_lock);
+ if (!timers_state.cpu_ticks_enabled) {
+ timers_state.cpu_ticks_offset -= cpu_get_host_ticks();
+ timers_state.cpu_clock_offset -= get_clock();
+ timers_state.cpu_ticks_enabled = 1;
+ }
+ seqlock_write_unlock(&timers_state.vm_clock_seqlock,
+ &timers_state.vm_clock_lock);
+}
+
+/* disable cpu_get_ticks() : the clock is stopped. You must not call
+ * cpu_get_ticks() after that.
+ * Caller must hold BQL which serves as mutex for vm_clock_seqlock.
+ */
+void cpu_disable_ticks(void)
+{
+ seqlock_write_lock(&timers_state.vm_clock_seqlock,
+ &timers_state.vm_clock_lock);
+ if (timers_state.cpu_ticks_enabled) {
+ timers_state.cpu_ticks_offset += cpu_get_host_ticks();
+ timers_state.cpu_clock_offset = cpu_get_clock_locked();
+ timers_state.cpu_ticks_enabled = 0;
+ }
+ seqlock_write_unlock(&timers_state.vm_clock_seqlock,
+ &timers_state.vm_clock_lock);
+}
+
+/* Correlation between real and virtual time is always going to be
+ fairly approximate, so ignore small variation.
+ When the guest is idle real and virtual time will be aligned in
+ the IO wait loop. */
+#define ICOUNT_WOBBLE (NANOSECONDS_PER_SECOND / 10)
+
+static void icount_adjust(void)
+{
+ int64_t cur_time;
+ int64_t cur_icount;
+ int64_t delta;
+
+ /* Protected by TimersState mutex. */
+ static int64_t last_delta;
+
+ /* If the VM is not running, then do nothing. */
+ if (!runstate_is_running()) {
+ return;
+ }
+
+ seqlock_write_lock(&timers_state.vm_clock_seqlock,
+ &timers_state.vm_clock_lock);
+ cur_time = REPLAY_CLOCK_LOCKED(REPLAY_CLOCK_VIRTUAL_RT,
+ cpu_get_clock_locked());
+ cur_icount = cpu_get_icount_locked();
+
+ delta = cur_icount - cur_time;
+ /* FIXME: This is a very crude algorithm, somewhat prone to oscillation. */
+ if (delta > 0
+ && last_delta + ICOUNT_WOBBLE < delta * 2
+ && timers_state.icount_time_shift > 0) {
+ /* The guest is getting too far ahead. Slow time down. */
+ atomic_set(&timers_state.icount_time_shift,
+ timers_state.icount_time_shift - 1);
+ }
+ if (delta < 0
+ && last_delta - ICOUNT_WOBBLE > delta * 2
+ && timers_state.icount_time_shift < MAX_ICOUNT_SHIFT) {
+ /* The guest is getting too far behind. Speed time up. */
+ atomic_set(&timers_state.icount_time_shift,
+ timers_state.icount_time_shift + 1);
+ }
+ last_delta = delta;
+ atomic_set_i64(&timers_state.qemu_icount_bias,
+ cur_icount - (timers_state.qemu_icount
+ << timers_state.icount_time_shift));
+ seqlock_write_unlock(&timers_state.vm_clock_seqlock,
+ &timers_state.vm_clock_lock);
+}
+
+static void icount_adjust_rt(void *opaque)
+{
+ timer_mod(timers_state.icount_rt_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT) + 1000);
+ icount_adjust();
+}
+
+static void icount_adjust_vm(void *opaque)
+{
+ timer_mod(timers_state.icount_vm_timer,
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ NANOSECONDS_PER_SECOND / 10);
+ icount_adjust();
+}
+
+static int64_t qemu_icount_round(int64_t count)
+{
+ int shift = atomic_read(&timers_state.icount_time_shift);
+ return (count + (1 << shift) - 1) >> shift;
+}
+
+static void icount_warp_rt(void)
+{
+ unsigned seq;
+ int64_t warp_start;
+
+ /* The icount_warp_timer is rescheduled soon after vm_clock_warp_start
+ * changes from -1 to another value, so the race here is okay.
+ */
+ do {
+ seq = seqlock_read_begin(&timers_state.vm_clock_seqlock);
+ warp_start = timers_state.vm_clock_warp_start;
+ } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, seq));
+
+ if (warp_start == -1) {
+ return;
+ }
+
+ seqlock_write_lock(&timers_state.vm_clock_seqlock,
+ &timers_state.vm_clock_lock);
+ if (runstate_is_running()) {
+ int64_t clock = REPLAY_CLOCK_LOCKED(REPLAY_CLOCK_VIRTUAL_RT,
+ cpu_get_clock_locked());
+ int64_t warp_delta;
+
+ warp_delta = clock - timers_state.vm_clock_warp_start;
+ if (use_icount == 2) {
+ /*
+ * In adaptive mode, do not let QEMU_CLOCK_VIRTUAL run too
+ * far ahead of real time.
+ */
+ int64_t cur_icount = cpu_get_icount_locked();
+ int64_t delta = clock - cur_icount;
+ warp_delta = MIN(warp_delta, delta);
+ }
+ atomic_set_i64(&timers_state.qemu_icount_bias,
+ timers_state.qemu_icount_bias + warp_delta);
+ }
+ timers_state.vm_clock_warp_start = -1;
+ seqlock_write_unlock(&timers_state.vm_clock_seqlock,
+ &timers_state.vm_clock_lock);
+
+ if (qemu_clock_expired(QEMU_CLOCK_VIRTUAL)) {
+ qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
+ }
+}
+
+static void icount_timer_cb(void *opaque)
+{
+ /* No need for a checkpoint because the timer already synchronizes
+ * with CHECKPOINT_CLOCK_VIRTUAL_RT.
+ */
+ icount_warp_rt();
+}
+
+void qtest_clock_warp(int64_t dest)
+{
+ int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ AioContext *aio_context;
+ assert(qtest_enabled());
+ aio_context = qemu_get_aio_context();
+ while (clock < dest) {
+ int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
+ QEMU_TIMER_ATTR_ALL);
+ int64_t warp = qemu_soonest_timeout(dest - clock, deadline);
+
+ seqlock_write_lock(&timers_state.vm_clock_seqlock,
+ &timers_state.vm_clock_lock);
+ atomic_set_i64(&timers_state.qemu_icount_bias,
+ timers_state.qemu_icount_bias + warp);
+ seqlock_write_unlock(&timers_state.vm_clock_seqlock,
+ &timers_state.vm_clock_lock);
+
+ qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
+ timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]);
+ clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ }
+ qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
+}
+
+void qemu_start_warp_timer(void)
+{
+ int64_t clock;
+ int64_t deadline;
+
+ if (!use_icount) {
+ return;
+ }
+
+ /* Nothing to do if the VM is stopped: QEMU_CLOCK_VIRTUAL timers
+ * do not fire, so computing the deadline does not make sense.
+ */
+ if (!runstate_is_running()) {
+ return;
+ }
+
+ if (replay_mode != REPLAY_MODE_PLAY) {
+ if (!all_cpu_threads_idle()) {
+ return;
+ }
+
+ if (qtest_enabled()) {
+ /* When testing, qtest commands advance icount. */
+ return;
+ }
+
+ replay_checkpoint(CHECKPOINT_CLOCK_WARP_START);
+ } else {
+ /* warp clock deterministically in record/replay mode */
+ if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_START)) {
+ /* vCPU is sleeping and warp can't be started.
+ It is probably a race condition: notification sent
+ to vCPU was processed in advance and vCPU went to sleep.
+ Therefore we have to wake it up for doing someting. */
+ if (replay_has_checkpoint()) {
+ qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
+ }
+ return;
+ }
+ }
+
+ /* We want to use the earliest deadline from ALL vm_clocks */
+ clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT);
+ deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
+ ~QEMU_TIMER_ATTR_EXTERNAL);
+ if (deadline < 0) {
+ static bool notified;
+ if (!icount_sleep && !notified) {
+ warn_report("icount sleep disabled and no active timers");
+ notified = true;
+ }
+ return;
+ }
+
+ if (deadline > 0) {
+ /*
+ * Ensure QEMU_CLOCK_VIRTUAL proceeds even when the virtual CPU goes to
+ * sleep. Otherwise, the CPU might be waiting for a future timer
+ * interrupt to wake it up, but the interrupt never comes because
+ * the vCPU isn't running any insns and thus doesn't advance the
+ * QEMU_CLOCK_VIRTUAL.
+ */
+ if (!icount_sleep) {
+ /*
+ * We never let VCPUs sleep in no sleep icount mode.
+ * If there is a pending QEMU_CLOCK_VIRTUAL timer we just advance
+ * to the next QEMU_CLOCK_VIRTUAL event and notify it.
+ * It is useful when we want a deterministic execution time,
+ * isolated from host latencies.
+ */
+ seqlock_write_lock(&timers_state.vm_clock_seqlock,
+ &timers_state.vm_clock_lock);
+ atomic_set_i64(&timers_state.qemu_icount_bias,
+ timers_state.qemu_icount_bias + deadline);
+ seqlock_write_unlock(&timers_state.vm_clock_seqlock,
+ &timers_state.vm_clock_lock);
+ qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
+ } else {
+ /*
+ * We do stop VCPUs and only advance QEMU_CLOCK_VIRTUAL after some
+ * "real" time, (related to the time left until the next event) has
+ * passed. The QEMU_CLOCK_VIRTUAL_RT clock will do this.
+ * This avoids that the warps are visible externally; for example,
+ * you will not be sending network packets continuously instead of
+ * every 100ms.
+ */
+ seqlock_write_lock(&timers_state.vm_clock_seqlock,
+ &timers_state.vm_clock_lock);
+ if (timers_state.vm_clock_warp_start == -1
+ || timers_state.vm_clock_warp_start > clock) {
+ timers_state.vm_clock_warp_start = clock;
+ }
+ seqlock_write_unlock(&timers_state.vm_clock_seqlock,
+ &timers_state.vm_clock_lock);
+ timer_mod_anticipate(timers_state.icount_warp_timer,
+ clock + deadline);
+ }
+ } else if (deadline == 0) {
+ qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
+ }
+}
+
+static void qemu_account_warp_timer(void)
+{
+ if (!use_icount || !icount_sleep) {
+ return;
+ }
+
+ /* Nothing to do if the VM is stopped: QEMU_CLOCK_VIRTUAL timers
+ * do not fire, so computing the deadline does not make sense.
+ */
+ if (!runstate_is_running()) {
+ return;
+ }
+
+ /* warp clock deterministically in record/replay mode */
+ if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_ACCOUNT)) {
+ return;
+ }
+
+ timer_del(timers_state.icount_warp_timer);
+ icount_warp_rt();
+}
+
+static bool icount_state_needed(void *opaque)
+{
+ return use_icount;
+}
+
+static bool warp_timer_state_needed(void *opaque)
+{
+ TimersState *s = opaque;
+ return s->icount_warp_timer != NULL;
+}
+
+static bool adjust_timers_state_needed(void *opaque)
+{
+ TimersState *s = opaque;
+ return s->icount_rt_timer != NULL;
+}
+
+static bool shift_state_needed(void *opaque)
+{
+ return use_icount == 2;
+}
+
+/*
+ * Subsection for warp timer migration is optional, because may not be created
+ */
+static const VMStateDescription icount_vmstate_warp_timer = {
+ .name = "timer/icount/warp_timer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = warp_timer_state_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT64(vm_clock_warp_start, TimersState),
+ VMSTATE_TIMER_PTR(icount_warp_timer, TimersState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription icount_vmstate_adjust_timers = {
+ .name = "timer/icount/timers",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = adjust_timers_state_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_TIMER_PTR(icount_rt_timer, TimersState),
+ VMSTATE_TIMER_PTR(icount_vm_timer, TimersState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription icount_vmstate_shift = {
+ .name = "timer/icount/shift",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = shift_state_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT16(icount_time_shift, TimersState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+/*
+ * This is a subsection for icount migration.
+ */
+static const VMStateDescription icount_vmstate_timers = {
+ .name = "timer/icount",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = icount_state_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT64(qemu_icount_bias, TimersState),
+ VMSTATE_INT64(qemu_icount, TimersState),
+ VMSTATE_END_OF_LIST()
+ },
+ .subsections = (const VMStateDescription*[]) {
+ &icount_vmstate_warp_timer,
+ &icount_vmstate_adjust_timers,
+ &icount_vmstate_shift,
+ NULL
+ }
+};
+
+static const VMStateDescription vmstate_timers = {
+ .name = "timer",
+ .version_id = 2,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT64(cpu_ticks_offset, TimersState),
+ VMSTATE_UNUSED(8),
+ VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2),
+ VMSTATE_END_OF_LIST()
+ },
+ .subsections = (const VMStateDescription*[]) {
+ &icount_vmstate_timers,
+ NULL
+ }
+};
+
+void cpu_ticks_init(void)
+{
+ seqlock_init(&timers_state.vm_clock_seqlock);
+ qemu_spin_init(&timers_state.vm_clock_lock);
+ vmstate_register(NULL, 0, &vmstate_timers, &timers_state);
+ cpu_throttle_init();
+}
+
+void configure_icount(QemuOpts *opts, Error **errp)
+{
+ const char *option = qemu_opt_get(opts, "shift");
+ bool sleep = qemu_opt_get_bool(opts, "sleep", true);
+ bool align = qemu_opt_get_bool(opts, "align", false);
+ long time_shift = -1;
+
+ if (!option) {
+ if (qemu_opt_get(opts, "align") != NULL) {
+ error_setg(errp, "Please specify shift option when using align");
+ }
+ return;
+ }
+
+ if (align && !sleep) {
+ error_setg(errp, "align=on and sleep=off are incompatible");
+ return;
+ }
+
+ if (strcmp(option, "auto") != 0) {
+ if (qemu_strtol(option, NULL, 0, &time_shift) < 0
+ || time_shift < 0 || time_shift > MAX_ICOUNT_SHIFT) {
+ error_setg(errp, "icount: Invalid shift value");
+ return;
+ }
+ } else if (icount_align_option) {
+ error_setg(errp, "shift=auto and align=on are incompatible");
+ return;
+ } else if (!icount_sleep) {
+ error_setg(errp, "shift=auto and sleep=off are incompatible");
+ return;
+ }
+
+ icount_sleep = sleep;
+ if (icount_sleep) {
+ timers_state.icount_warp_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT,
+ icount_timer_cb, NULL);
+ }
+
+ icount_align_option = align;
+
+ if (time_shift >= 0) {
+ timers_state.icount_time_shift = time_shift;
+ use_icount = 1;
+ return;
+ }
+
+ use_icount = 2;
+
+ /* 125MIPS seems a reasonable initial guess at the guest speed.
+ It will be corrected fairly quickly anyway. */
+ timers_state.icount_time_shift = 3;
+
+ /* Have both realtime and virtual time triggers for speed adjustment.
+ The realtime trigger catches emulated time passing too slowly,
+ the virtual time trigger catches emulated time passing too fast.
+ Realtime triggers occur even when idle, so use them less frequently
+ than VM triggers. */
+ timers_state.vm_clock_warp_start = -1;
+ timers_state.icount_rt_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL_RT,
+ icount_adjust_rt, NULL);
+ timer_mod(timers_state.icount_rt_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT) + 1000);
+ timers_state.icount_vm_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ icount_adjust_vm, NULL);
+ timer_mod(timers_state.icount_vm_timer,
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ NANOSECONDS_PER_SECOND / 10);
+}
+
+/***********************************************************/
+/* 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 *tcg_kick_vcpu_timer;
+static CPUState *tcg_current_rr_cpu;
+
+#define TCG_KICK_PERIOD (NANOSECONDS_PER_SECOND / 10)
+
+static inline int64_t qemu_tcg_next_kick(void)
+{
+ return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + TCG_KICK_PERIOD;
+}
+
+/* Kick the currently round-robin scheduled vCPU to next */
+static void qemu_cpu_kick_rr_next_cpu(void)
+{
+ CPUState *cpu;
+ do {
+ cpu = atomic_mb_read(&tcg_current_rr_cpu);
+ if (cpu) {
+ cpu_exit(cpu);
+ }
+ } while (cpu != atomic_mb_read(&tcg_current_rr_cpu));
+}
+
+/* Kick all RR vCPUs */
+static void qemu_cpu_kick_rr_cpus(void)
+{
+ CPUState *cpu;
+
+ CPU_FOREACH(cpu) {
+ cpu_exit(cpu);
+ };
+}
+
+static void do_nothing(CPUState *cpu, run_on_cpu_data unused)
+{
+}
+
+void qemu_timer_notify_cb(void *opaque, QEMUClockType type)
+{
+ if (!use_icount || type != QEMU_CLOCK_VIRTUAL) {
+ qemu_notify_event();
+ return;
+ }
+
+ if (qemu_in_vcpu_thread()) {
+ /* A CPU is currently running; kick it back out to the
+ * tcg_cpu_exec() loop so it will recalculate its
+ * icount deadline immediately.
+ */
+ qemu_cpu_kick(current_cpu);
+ } else if (first_cpu) {
+ /* qemu_cpu_kick is not enough to kick a halted CPU out of
+ * qemu_tcg_wait_io_event. async_run_on_cpu, instead,
+ * causes cpu_thread_is_idle to return false. This way,
+ * handle_icount_deadline can run.
+ * If we have no CPUs at all for some reason, we don't
+ * need to do anything.
+ */
+ async_run_on_cpu(first_cpu, do_nothing, RUN_ON_CPU_NULL);
+ }
+}
+
+static void kick_tcg_thread(void *opaque)
+{
+ timer_mod(tcg_kick_vcpu_timer, qemu_tcg_next_kick());
+ qemu_cpu_kick_rr_next_cpu();
+}
+
+static void start_tcg_kick_timer(void)
+{
+ assert(!mttcg_enabled);
+ if (!tcg_kick_vcpu_timer && CPU_NEXT(first_cpu)) {
+ tcg_kick_vcpu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ kick_tcg_thread, NULL);
+ }
+ if (tcg_kick_vcpu_timer && !timer_pending(tcg_kick_vcpu_timer)) {
+ timer_mod(tcg_kick_vcpu_timer, qemu_tcg_next_kick());
+ }
+}
+
+static void stop_tcg_kick_timer(void)
+{
+ assert(!mttcg_enabled);
+ if (tcg_kick_vcpu_timer && timer_pending(tcg_kick_vcpu_timer)) {
+ timer_del(tcg_kick_vcpu_timer);
+ }
+}
+
+/***********************************************************/
+void hw_error(const char *fmt, ...)
+{
+ va_list ap;
+ CPUState *cpu;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "qemu: hardware error: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ CPU_FOREACH(cpu) {
+ fprintf(stderr, "CPU #%d:\n", cpu->cpu_index);
+ cpu_dump_state(cpu, stderr, CPU_DUMP_FPU);
+ }
+ va_end(ap);
+ abort();
+}
+
+void cpu_synchronize_all_states(void)
+{
+ CPUState *cpu;
+
+ CPU_FOREACH(cpu) {
+ cpu_synchronize_state(cpu);
+ }
+}
+
+void cpu_synchronize_all_post_reset(void)
+{
+ CPUState *cpu;
+
+ CPU_FOREACH(cpu) {
+ cpu_synchronize_post_reset(cpu);
+ }
+}
+
+void cpu_synchronize_all_post_init(void)
+{
+ CPUState *cpu;
+
+ CPU_FOREACH(cpu) {
+ cpu_synchronize_post_init(cpu);
+ }
+}
+
+void cpu_synchronize_all_pre_loadvm(void)
+{
+ CPUState *cpu;
+
+ CPU_FOREACH(cpu) {
+ cpu_synchronize_pre_loadvm(cpu);
+ }
+}
+
+static int do_vm_stop(RunState state, bool send_stop)
+{
+ int ret = 0;
+
+ if (runstate_is_running()) {
+ runstate_set(state);
+ cpu_disable_ticks();
+ pause_all_vcpus();
+ vm_state_notify(0, state);
+ if (send_stop) {
+ qapi_event_send_stop();
+ }
+ }
+
+ bdrv_drain_all();
+ ret = bdrv_flush_all();
+
+ return ret;
+}
+
+/* Special vm_stop() variant for terminating the process. Historically clients
+ * did not expect a QMP STOP event and so we need to retain compatibility.
+ */
+int vm_shutdown(void)
+{
+ return do_vm_stop(RUN_STATE_SHUTDOWN, false);
+}
+
+static bool cpu_can_run(CPUState *cpu)
+{
+ if (cpu->stop) {
+ return false;
+ }
+ if (cpu_is_stopped(cpu)) {
+ return false;
+ }
+ return true;
+}
+
+static void cpu_handle_guest_debug(CPUState *cpu)
+{
+ gdb_set_stop_cpu(cpu);
+ qemu_system_debug_request();
+ cpu->stopped = true;
+}
+
+#ifdef CONFIG_LINUX
+static void sigbus_reraise(void)
+{
+ sigset_t set;
+ struct sigaction action;
+
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = SIG_DFL;
+ if (!sigaction(SIGBUS, &action, NULL)) {
+ raise(SIGBUS);
+ sigemptyset(&set);
+ sigaddset(&set, SIGBUS);
+ pthread_sigmask(SIG_UNBLOCK, &set, NULL);
+ }
+ perror("Failed to re-raise SIGBUS!\n");
+ abort();
+}
+
+static void sigbus_handler(int n, siginfo_t *siginfo, void *ctx)
+{
+ if (siginfo->si_code != BUS_MCEERR_AO && siginfo->si_code != BUS_MCEERR_AR) {
+ sigbus_reraise();
+ }
+
+ if (current_cpu) {
+ /* Called asynchronously in VCPU thread. */
+ if (kvm_on_sigbus_vcpu(current_cpu, siginfo->si_code, siginfo->si_addr)) {
+ sigbus_reraise();
+ }
+ } else {
+ /* Called synchronously (via signalfd) in main thread. */
+ if (kvm_on_sigbus(siginfo->si_code, siginfo->si_addr)) {
+ sigbus_reraise();
+ }
+ }
+}
+
+static void qemu_init_sigbus(void)
+{
+ struct sigaction action;
+
+ memset(&action, 0, sizeof(action));
+ action.sa_flags = SA_SIGINFO;
+ action.sa_sigaction = sigbus_handler;
+ sigaction(SIGBUS, &action, NULL);
+
+ prctl(PR_MCE_KILL, PR_MCE_KILL_SET, PR_MCE_KILL_EARLY, 0, 0);
+}
+#else /* !CONFIG_LINUX */
+static void qemu_init_sigbus(void)
+{
+}
+#endif /* !CONFIG_LINUX */
+
+static QemuThread io_thread;
+
+/* cpu creation */
+static QemuCond qemu_cpu_cond;
+/* system init */
+static QemuCond qemu_pause_cond;
+
+void qemu_init_cpu_loop(void)
+{
+ qemu_init_sigbus();
+ qemu_cond_init(&qemu_cpu_cond);
+ qemu_cond_init(&qemu_pause_cond);
+ qemu_mutex_init(&qemu_global_mutex);
+
+ qemu_thread_get_self(&io_thread);
+}
+
+void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data)
+{
+ do_run_on_cpu(cpu, func, data, &qemu_global_mutex);
+}
+
+static void qemu_kvm_destroy_vcpu(CPUState *cpu)
+{
+ if (kvm_destroy_vcpu(cpu) < 0) {
+ error_report("kvm_destroy_vcpu failed");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void qemu_tcg_destroy_vcpu(CPUState *cpu)
+{
+}
+
+static void qemu_cpu_stop(CPUState *cpu, bool exit)
+{
+ g_assert(qemu_cpu_is_self(cpu));
+ cpu->stop = false;
+ cpu->stopped = true;
+ if (exit) {
+ cpu_exit(cpu);
+ }
+ qemu_cond_broadcast(&qemu_pause_cond);
+}
+
+static void qemu_wait_io_event_common(CPUState *cpu)
+{
+ atomic_mb_set(&cpu->thread_kicked, false);
+ if (cpu->stop) {
+ qemu_cpu_stop(cpu, false);
+ }
+ process_queued_cpu_work(cpu);
+}
+
+static void qemu_tcg_rr_wait_io_event(void)
+{
+ CPUState *cpu;
+
+ while (all_cpu_threads_idle()) {
+ stop_tcg_kick_timer();
+ qemu_cond_wait(first_cpu->halt_cond, &qemu_global_mutex);
+ }
+
+ start_tcg_kick_timer();
+
+ CPU_FOREACH(cpu) {
+ qemu_wait_io_event_common(cpu);
+ }
+}
+
+static void qemu_wait_io_event(CPUState *cpu)
+{
+ bool slept = false;
+
+ while (cpu_thread_is_idle(cpu)) {
+ if (!slept) {
+ slept = true;
+ qemu_plugin_vcpu_idle_cb(cpu);
+ }
+ qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex);
+ }
+ if (slept) {
+ qemu_plugin_vcpu_resume_cb(cpu);
+ }
+
+#ifdef _WIN32
+ /* Eat dummy APC queued by qemu_cpu_kick_thread. */
+ if (!tcg_enabled()) {
+ SleepEx(0, TRUE);
+ }
+#endif
+ qemu_wait_io_event_common(cpu);
+}
+
+static void *qemu_kvm_cpu_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);
+ if (r < 0) {
+ error_report("kvm_init_vcpu failed: %s", strerror(-r));
+ exit(1);
+ }
+
+ kvm_init_cpu_signals(cpu);
+
+ /* signal CPU creation */
+ cpu->created = true;
+ qemu_cond_signal(&qemu_cpu_cond);
+ 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));
+
+ qemu_kvm_destroy_vcpu(cpu);
+ cpu->created = false;
+ qemu_cond_signal(&qemu_cpu_cond);
+ qemu_mutex_unlock_iothread();
+ rcu_unregister_thread();
+ return NULL;
+}
+
+static void *qemu_dummy_cpu_thread_fn(void *arg)
+{
+#ifdef _WIN32
+ error_report("qtest is not supported under Windows");
+ exit(1);
+#else
+ 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->created = true;
+ qemu_cond_signal(&qemu_cpu_cond);
+ 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;
+#endif
+}
+
+static int64_t tcg_get_icount_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 qemu_icount_round(deadline);
+ } else {
+ return replay_get_instructions();
+ }
+}
+
+static void notify_aio_contexts(void)
+{
+ /* Wake up other AioContexts. */
+ qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
+ qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
+}
+
+static void handle_icount_deadline(void)
+{
+ assert(qemu_in_vcpu_thread());
+ if (use_icount) {
+ int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
+ QEMU_TIMER_ATTR_ALL);
+
+ if (deadline == 0) {
+ notify_aio_contexts();
+ }
+ }
+}
+
+static void prepare_icount_for_run(CPUState *cpu)
+{
+ if (use_icount) {
+ int insns_left;
+
+ /* These should always be cleared by process_icount_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 = tcg_get_icount_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()) {
+ notify_aio_contexts();
+ }
+ }
+}
+
+static void process_icount_data(CPUState *cpu)
+{
+ if (use_icount) {
+ /* Account for executed instructions */
+ cpu_update_icount(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();
+ }
+}
+
+
+static int tcg_cpu_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
+ atomic_set(&tcg_ctx->prof.cpu_exec_time,
+ tcg_ctx->prof.cpu_exec_time + profile_getclock() - ti);
+#endif
+ return ret;
+}
+
+/* Destroy any remaining vCPUs which have been unplugged and have
+ * finished running
+ */
+static void deal_with_unplugged_cpus(void)
+{
+ CPUState *cpu;
+
+ CPU_FOREACH(cpu) {
+ if (cpu->unplug && !cpu_can_run(cpu)) {
+ qemu_tcg_destroy_vcpu(cpu);
+ cpu->created = false;
+ qemu_cond_signal(&qemu_cpu_cond);
+ break;
+ }
+ }
+}
+
+/* Single-threaded TCG
+ *
+ * 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 *qemu_tcg_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->created = true;
+ cpu->can_do_io = 1;
+ qemu_cond_signal(&qemu_cpu_cond);
+ qemu_guest_random_seed_thread_part2(cpu->random_seed);
+
+ /* wait for initial kick-off after machine start */
+ while (first_cpu->stopped) {
+ qemu_cond_wait(first_cpu->halt_cond, &qemu_global_mutex);
+
+ /* process any pending work */
+ CPU_FOREACH(cpu) {
+ current_cpu = cpu;
+ qemu_wait_io_event_common(cpu);
+ }
+ }
+
+ start_tcg_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();
+ /* Account partial waits to QEMU_CLOCK_VIRTUAL. */
+ qemu_account_warp_timer();
+
+ /* Run the timers here. This is much more efficient than
+ * waking up the I/O thread and waiting for completion.
+ */
+ handle_icount_deadline();
+
+ replay_mutex_unlock();
+
+ if (!cpu) {
+ cpu = first_cpu;
+ }
+
+ while (cpu && cpu_work_list_empty(cpu) && !cpu->exit_request) {
+
+ atomic_mb_set(&tcg_current_rr_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();
+ prepare_icount_for_run(cpu);
+
+ r = tcg_cpu_exec(cpu);
+
+ process_icount_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 atomic_mb_set because a spurious wakeup is okay. */
+ atomic_set(&tcg_current_rr_cpu, NULL);
+
+ if (cpu && cpu->exit_request) {
+ atomic_mb_set(&cpu->exit_request, 0);
+ }
+
+ if (use_icount && 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();
+ }
+
+ qemu_tcg_rr_wait_io_event();
+ deal_with_unplugged_cpus();
+ }
+
+ rcu_unregister_thread();
+ return NULL;
+}
+
+static void *qemu_hax_cpu_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->created = true;
+ current_cpu = cpu;
+
+ hax_init_vcpu(cpu);
+ qemu_cond_signal(&qemu_cpu_cond);
+ qemu_guest_random_seed_thread_part2(cpu->random_seed);
+
+ do {
+ if (cpu_can_run(cpu)) {
+ r = hax_smp_cpu_exec(cpu);
+ if (r == EXCP_DEBUG) {
+ cpu_handle_guest_debug(cpu);
+ }
+ }
+
+ qemu_wait_io_event(cpu);
+ } while (!cpu->unplug || cpu_can_run(cpu));
+ rcu_unregister_thread();
+ return NULL;
+}
+
+/* The HVF-specific vCPU thread function. This one should only run when the host
+ * CPU supports the VMX "unrestricted guest" feature. */
+static void *qemu_hvf_cpu_thread_fn(void *arg)
+{
+ CPUState *cpu = arg;
+
+ int r;
+
+ assert(hvf_enabled());
+
+ 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;
+
+ hvf_init_vcpu(cpu);
+
+ /* signal CPU creation */
+ cpu->created = true;
+ qemu_cond_signal(&qemu_cpu_cond);
+ qemu_guest_random_seed_thread_part2(cpu->random_seed);
+
+ do {
+ if (cpu_can_run(cpu)) {
+ r = hvf_vcpu_exec(cpu);
+ if (r == EXCP_DEBUG) {
+ cpu_handle_guest_debug(cpu);
+ }
+ }
+ qemu_wait_io_event(cpu);
+ } while (!cpu->unplug || cpu_can_run(cpu));
+
+ hvf_vcpu_destroy(cpu);
+ cpu->created = false;
+ qemu_cond_signal(&qemu_cpu_cond);
+ qemu_mutex_unlock_iothread();
+ rcu_unregister_thread();
+ return NULL;
+}
+
+static void *qemu_whpx_cpu_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();
+ current_cpu = cpu;
+
+ r = whpx_init_vcpu(cpu);
+ if (r < 0) {
+ fprintf(stderr, "whpx_init_vcpu failed: %s\n", strerror(-r));
+ exit(1);
+ }
+
+ /* signal CPU creation */
+ cpu->created = true;
+ qemu_cond_signal(&qemu_cpu_cond);
+ qemu_guest_random_seed_thread_part2(cpu->random_seed);
+
+ do {
+ if (cpu_can_run(cpu)) {
+ r = whpx_vcpu_exec(cpu);
+ if (r == EXCP_DEBUG) {
+ cpu_handle_guest_debug(cpu);
+ }
+ }
+ while (cpu_thread_is_idle(cpu)) {
+ qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex);
+ }
+ qemu_wait_io_event_common(cpu);
+ } while (!cpu->unplug || cpu_can_run(cpu));
+
+ whpx_destroy_vcpu(cpu);
+ cpu->created = false;
+ qemu_cond_signal(&qemu_cpu_cond);
+ qemu_mutex_unlock_iothread();
+ rcu_unregister_thread();
+ return NULL;
+}
+
+#ifdef _WIN32
+static void CALLBACK dummy_apc_func(ULONG_PTR unused)
+{
+}
+#endif
+
+/* Multi-threaded TCG
+ *
+ * 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 *qemu_tcg_cpu_thread_fn(void *arg)
+{
+ CPUState *cpu = arg;
+
+ assert(tcg_enabled());
+ g_assert(!use_icount);
+
+ rcu_register_thread();
+ tcg_register_thread();
+
+ qemu_mutex_lock_iothread();
+ qemu_thread_get_self(cpu->thread);
+
+ cpu->thread_id = qemu_get_thread_id();
+ cpu->created = true;
+ cpu->can_do_io = 1;
+ current_cpu = cpu;
+ qemu_cond_signal(&qemu_cpu_cond);
+ 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_cpu_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;
+ }
+ }
+
+ atomic_mb_set(&cpu->exit_request, 0);
+ qemu_wait_io_event(cpu);
+ } while (!cpu->unplug || cpu_can_run(cpu));
+
+ qemu_tcg_destroy_vcpu(cpu);
+ cpu->created = false;
+ qemu_cond_signal(&qemu_cpu_cond);
+ qemu_mutex_unlock_iothread();
+ rcu_unregister_thread();
+ return NULL;
+}
+
+static void qemu_cpu_kick_thread(CPUState *cpu)
+{
+#ifndef _WIN32
+ int err;
+
+ if (cpu->thread_kicked) {
+ return;
+ }
+ cpu->thread_kicked = true;
+ err = pthread_kill(cpu->thread->thread, SIG_IPI);
+ if (err && err != ESRCH) {
+ fprintf(stderr, "qemu:%s: %s", __func__, strerror(err));
+ exit(1);
+ }
+#else /* _WIN32 */
+ if (!qemu_cpu_is_self(cpu)) {
+ if (whpx_enabled()) {
+ whpx_vcpu_kick(cpu);
+ } else if (!QueueUserAPC(dummy_apc_func, cpu->hThread, 0)) {
+ fprintf(stderr, "%s: QueueUserAPC failed with error %lu\n",
+ __func__, GetLastError());
+ exit(1);
+ }
+ }
+#endif
+}
+
+void qemu_cpu_kick(CPUState *cpu)
+{
+ qemu_cond_broadcast(cpu->halt_cond);
+ if (tcg_enabled()) {
+ if (qemu_tcg_mttcg_enabled()) {
+ cpu_exit(cpu);
+ } else {
+ qemu_cpu_kick_rr_cpus();
+ }
+ } else {
+ if (hax_enabled()) {
+ /*
+ * FIXME: race condition with the exit_request check in
+ * hax_vcpu_hax_exec
+ */
+ cpu->exit_request = 1;
+ }
+ qemu_cpu_kick_thread(cpu);
+ }
+}
+
+void qemu_cpu_kick_self(void)
+{
+ assert(current_cpu);
+ qemu_cpu_kick_thread(current_cpu);
+}
+
+bool qemu_cpu_is_self(CPUState *cpu)
+{
+ return qemu_thread_is_self(cpu->thread);
+}
+
+bool qemu_in_vcpu_thread(void)
+{
+ return current_cpu && qemu_cpu_is_self(current_cpu);
+}
+
+static __thread bool iothread_locked = false;
+
+bool qemu_mutex_iothread_locked(void)
+{
+ return iothread_locked;
+}
+
+/*
+ * The BQL is taken from so many places that it is worth profiling the
+ * callers directly, instead of funneling them all through a single function.
+ */
+void qemu_mutex_lock_iothread_impl(const char *file, int line)
+{
+ QemuMutexLockFunc bql_lock = atomic_read(&qemu_bql_mutex_lock_func);
+
+ g_assert(!qemu_mutex_iothread_locked());
+ bql_lock(&qemu_global_mutex, file, line);
+ iothread_locked = true;
+}
+
+void qemu_mutex_unlock_iothread(void)
+{
+ g_assert(qemu_mutex_iothread_locked());
+ iothread_locked = false;
+ qemu_mutex_unlock(&qemu_global_mutex);
+}
+
+void qemu_cond_wait_iothread(QemuCond *cond)
+{
+ qemu_cond_wait(cond, &qemu_global_mutex);
+}
+
+void qemu_cond_timedwait_iothread(QemuCond *cond, int ms)
+{
+ qemu_cond_timedwait(cond, &qemu_global_mutex, ms);
+}
+
+static bool all_vcpus_paused(void)
+{
+ CPUState *cpu;
+
+ CPU_FOREACH(cpu) {
+ if (!cpu->stopped) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void pause_all_vcpus(void)
+{
+ CPUState *cpu;
+
+ qemu_clock_enable(QEMU_CLOCK_VIRTUAL, false);
+ CPU_FOREACH(cpu) {
+ if (qemu_cpu_is_self(cpu)) {
+ qemu_cpu_stop(cpu, true);
+ } else {
+ cpu->stop = true;
+ qemu_cpu_kick(cpu);
+ }
+ }
+
+ /* We need to drop the replay_lock so any vCPU threads woken up
+ * can finish their replay tasks
+ */
+ replay_mutex_unlock();
+
+ while (!all_vcpus_paused()) {
+ qemu_cond_wait(&qemu_pause_cond, &qemu_global_mutex);
+ CPU_FOREACH(cpu) {
+ qemu_cpu_kick(cpu);
+ }
+ }
+
+ qemu_mutex_unlock_iothread();
+ replay_mutex_lock();
+ qemu_mutex_lock_iothread();
+}
+
+void cpu_resume(CPUState *cpu)
+{
+ cpu->stop = false;
+ cpu->stopped = false;
+ qemu_cpu_kick(cpu);
+}
+
+void resume_all_vcpus(void)
+{
+ CPUState *cpu;
+
+ if (!runstate_is_running()) {
+ return;
+ }
+
+ qemu_clock_enable(QEMU_CLOCK_VIRTUAL, true);
+ CPU_FOREACH(cpu) {
+ cpu_resume(cpu);
+ }
+}
+
+void cpu_remove_sync(CPUState *cpu)
+{
+ cpu->stop = true;
+ cpu->unplug = true;
+ qemu_cpu_kick(cpu);
+ qemu_mutex_unlock_iothread();
+ qemu_thread_join(cpu->thread);
+ qemu_mutex_lock_iothread();
+}
+
+/* For temporary buffers for forming a name */
+#define VCPU_THREAD_NAME_SIZE 16
+
+static void qemu_tcg_init_vcpu(CPUState *cpu)
+{
+ char thread_name[VCPU_THREAD_NAME_SIZE];
+ static QemuCond *single_tcg_halt_cond;
+ static QemuThread *single_tcg_cpu_thread;
+ static int tcg_region_inited;
+
+ assert(tcg_enabled());
+ /*
+ * Initialize TCG regions--once. Now is a good time, because:
+ * (1) TCG's init context, prologue and target globals have been set up.
+ * (2) qemu_tcg_mttcg_enabled() works now (TCG init code runs before the
+ * -accel flag is processed, so the check doesn't work then).
+ */
+ if (!tcg_region_inited) {
+ tcg_region_inited = 1;
+ tcg_region_init();
+ }
+
+ if (qemu_tcg_mttcg_enabled() || !single_tcg_cpu_thread) {
+ cpu->thread = g_malloc0(sizeof(QemuThread));
+ cpu->halt_cond = g_malloc0(sizeof(QemuCond));
+ qemu_cond_init(cpu->halt_cond);
+
+ if (qemu_tcg_mttcg_enabled()) {
+ /* create a thread per vCPU with TCG (MTTCG) */
+ parallel_cpus = true;
+ snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/TCG",
+ cpu->cpu_index);
+
+ qemu_thread_create(cpu->thread, thread_name, qemu_tcg_cpu_thread_fn,
+ cpu, QEMU_THREAD_JOINABLE);
+
+ } else {
+ /* 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,
+ qemu_tcg_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 {
+ /* For non-MTTCG cases 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;
+ }
+}
+
+static void qemu_hax_start_vcpu(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/HAX",
+ cpu->cpu_index);
+ qemu_thread_create(cpu->thread, thread_name, qemu_hax_cpu_thread_fn,
+ cpu, QEMU_THREAD_JOINABLE);
+#ifdef _WIN32
+ cpu->hThread = qemu_thread_get_handle(cpu->thread);
+#endif
+}
+
+static void qemu_kvm_start_vcpu(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, qemu_kvm_cpu_thread_fn,
+ cpu, QEMU_THREAD_JOINABLE);
+}
+
+static void qemu_hvf_start_vcpu(CPUState *cpu)
+{
+ char thread_name[VCPU_THREAD_NAME_SIZE];
+
+ /* HVF currently does not support TCG, and only runs in
+ * unrestricted-guest mode. */
+ assert(hvf_enabled());
+
+ 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/HVF",
+ cpu->cpu_index);
+ qemu_thread_create(cpu->thread, thread_name, qemu_hvf_cpu_thread_fn,
+ cpu, QEMU_THREAD_JOINABLE);
+}
+
+static void qemu_whpx_start_vcpu(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/WHPX",
+ cpu->cpu_index);
+ qemu_thread_create(cpu->thread, thread_name, qemu_whpx_cpu_thread_fn,
+ cpu, QEMU_THREAD_JOINABLE);
+#ifdef _WIN32
+ cpu->hThread = qemu_thread_get_handle(cpu->thread);
+#endif
+}
+
+static void qemu_dummy_start_vcpu(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, qemu_dummy_cpu_thread_fn, cpu,
+ QEMU_THREAD_JOINABLE);
+}
+
+void qemu_init_vcpu(CPUState *cpu)
+{
+ MachineState *ms = MACHINE(qdev_get_machine());
+
+ cpu->nr_cores = ms->smp.cores;
+ cpu->nr_threads = ms->smp.threads;
+ cpu->stopped = true;
+ cpu->random_seed = qemu_guest_random_seed_thread_part1();
+
+ if (!cpu->as) {
+ /* If the target cpu hasn't set up any address spaces itself,
+ * give it the default one.
+ */
+ cpu->num_ases = 1;
+ cpu_address_space_init(cpu, 0, "cpu-memory", cpu->memory);
+ }
+
+ if (kvm_enabled()) {
+ qemu_kvm_start_vcpu(cpu);
+ } else if (hax_enabled()) {
+ qemu_hax_start_vcpu(cpu);
+ } else if (hvf_enabled()) {
+ qemu_hvf_start_vcpu(cpu);
+ } else if (tcg_enabled()) {
+ qemu_tcg_init_vcpu(cpu);
+ } else if (whpx_enabled()) {
+ qemu_whpx_start_vcpu(cpu);
+ } else {
+ qemu_dummy_start_vcpu(cpu);
+ }
+
+ while (!cpu->created) {
+ qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
+ }
+}
+
+void cpu_stop_current(void)
+{
+ if (current_cpu) {
+ current_cpu->stop = true;
+ cpu_exit(current_cpu);
+ }
+}
+
+int vm_stop(RunState state)
+{
+ if (qemu_in_vcpu_thread()) {
+ qemu_system_vmstop_request_prepare();
+ qemu_system_vmstop_request(state);
+ /*
+ * FIXME: should not return to device code in case
+ * vm_stop() has been requested.
+ */
+ cpu_stop_current();
+ return 0;
+ }
+
+ return do_vm_stop(state, true);
+}
+
+/**
+ * Prepare for (re)starting the VM.
+ * Returns -1 if the vCPUs are not to be restarted (e.g. if they are already
+ * running or in case of an error condition), 0 otherwise.
+ */
+int vm_prepare_start(void)
+{
+ RunState requested;
+
+ qemu_vmstop_requested(&requested);
+ if (runstate_is_running() && requested == RUN_STATE__MAX) {
+ return -1;
+ }
+
+ /* Ensure that a STOP/RESUME pair of events is emitted if a
+ * vmstop request was pending. The BLOCK_IO_ERROR event, for
+ * example, according to documentation is always followed by
+ * the STOP event.
+ */
+ if (runstate_is_running()) {
+ qapi_event_send_stop();
+ qapi_event_send_resume();
+ return -1;
+ }
+
+ /* We are sending this now, but the CPUs will be resumed shortly later */
+ qapi_event_send_resume();
+
+ cpu_enable_ticks();
+ runstate_set(RUN_STATE_RUNNING);
+ vm_state_notify(1, RUN_STATE_RUNNING);
+ return 0;
+}
+
+void vm_start(void)
+{
+ if (!vm_prepare_start()) {
+ resume_all_vcpus();
+ }
+}
+
+/* does a state transition even if the VM is already stopped,
+ current state is forgotten forever */
+int vm_stop_force_state(RunState state)
+{
+ if (runstate_is_running()) {
+ return vm_stop(state);
+ } else {
+ runstate_set(state);
+
+ bdrv_drain_all();
+ /* Make sure to return an error if the flush in a previous vm_stop()
+ * failed. */
+ return bdrv_flush_all();
+ }
+}
+
+void list_cpus(const char *optarg)
+{
+ /* XXX: implement xxx_cpu_list for targets that still miss it */
+#if defined(cpu_list)
+ cpu_list();
+#endif
+}
+
+void qmp_memsave(int64_t addr, int64_t size, const char *filename,
+ bool has_cpu, int64_t cpu_index, Error **errp)
+{
+ FILE *f;
+ uint32_t l;
+ CPUState *cpu;
+ uint8_t buf[1024];
+ int64_t orig_addr = addr, orig_size = size;
+
+ if (!has_cpu) {
+ cpu_index = 0;
+ }
+
+ cpu = qemu_get_cpu(cpu_index);
+ if (cpu == NULL) {
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index",
+ "a CPU number");
+ return;
+ }
+
+ f = fopen(filename, "wb");
+ if (!f) {
+ error_setg_file_open(errp, errno, filename);
+ return;
+ }
+
+ while (size != 0) {
+ l = sizeof(buf);
+ if (l > size)
+ l = size;
+ if (cpu_memory_rw_debug(cpu, addr, buf, l, 0) != 0) {
+ error_setg(errp, "Invalid addr 0x%016" PRIx64 "/size %" PRId64
+ " specified", orig_addr, orig_size);
+ goto exit;
+ }
+ if (fwrite(buf, 1, l, f) != l) {
+ error_setg(errp, QERR_IO_ERROR);
+ goto exit;
+ }
+ addr += l;
+ size -= l;
+ }
+
+exit:
+ fclose(f);
+}
+
+void qmp_pmemsave(int64_t addr, int64_t size, const char *filename,
+ Error **errp)
+{
+ FILE *f;
+ uint32_t l;
+ uint8_t buf[1024];
+
+ f = fopen(filename, "wb");
+ if (!f) {
+ error_setg_file_open(errp, errno, filename);
+ return;
+ }
+
+ while (size != 0) {
+ l = sizeof(buf);
+ if (l > size)
+ l = size;
+ cpu_physical_memory_read(addr, buf, l);
+ if (fwrite(buf, 1, l, f) != l) {
+ error_setg(errp, QERR_IO_ERROR);
+ goto exit;
+ }
+ addr += l;
+ size -= l;
+ }
+
+exit:
+ fclose(f);
+}
+
+void qmp_inject_nmi(Error **errp)
+{
+ nmi_monitor_handle(monitor_get_cpu_index(), errp);
+}
+
+void dump_drift_info(void)
+{
+ if (!use_icount) {
+ return;
+ }
+
+ qemu_printf("Host - Guest clock %"PRIi64" ms\n",
+ (cpu_get_clock() - cpu_get_icount())/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");
+ }
+}
--- /dev/null
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 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.
+ */
+/*
+ * splitted out ioport related stuffs from vl.c.
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "exec/ioport.h"
+#include "trace-root.h"
+#include "exec/memory.h"
+#include "exec/address-spaces.h"
+
+typedef struct MemoryRegionPortioList {
+ MemoryRegion mr;
+ void *portio_opaque;
+ MemoryRegionPortio ports[];
+} MemoryRegionPortioList;
+
+static uint64_t unassigned_io_read(void *opaque, hwaddr addr, unsigned size)
+{
+ return -1ULL;
+}
+
+static void unassigned_io_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+}
+
+const MemoryRegionOps unassigned_io_ops = {
+ .read = unassigned_io_read,
+ .write = unassigned_io_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+void cpu_outb(uint32_t addr, uint8_t val)
+{
+ trace_cpu_out(addr, 'b', val);
+ address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED,
+ &val, 1);
+}
+
+void cpu_outw(uint32_t addr, uint16_t val)
+{
+ uint8_t buf[2];
+
+ trace_cpu_out(addr, 'w', val);
+ stw_p(buf, val);
+ address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED,
+ buf, 2);
+}
+
+void cpu_outl(uint32_t addr, uint32_t val)
+{
+ uint8_t buf[4];
+
+ trace_cpu_out(addr, 'l', val);
+ stl_p(buf, val);
+ address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED,
+ buf, 4);
+}
+
+uint8_t cpu_inb(uint32_t addr)
+{
+ uint8_t val;
+
+ address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED,
+ &val, 1);
+ trace_cpu_in(addr, 'b', val);
+ return val;
+}
+
+uint16_t cpu_inw(uint32_t addr)
+{
+ uint8_t buf[2];
+ uint16_t val;
+
+ address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, buf, 2);
+ val = lduw_p(buf);
+ trace_cpu_in(addr, 'w', val);
+ return val;
+}
+
+uint32_t cpu_inl(uint32_t addr)
+{
+ uint8_t buf[4];
+ uint32_t val;
+
+ address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, buf, 4);
+ val = ldl_p(buf);
+ trace_cpu_in(addr, 'l', val);
+ return val;
+}
+
+void portio_list_init(PortioList *piolist,
+ Object *owner,
+ const MemoryRegionPortio *callbacks,
+ void *opaque, const char *name)
+{
+ unsigned n = 0;
+
+ while (callbacks[n].size) {
+ ++n;
+ }
+
+ piolist->ports = callbacks;
+ piolist->nr = 0;
+ piolist->regions = g_new0(MemoryRegion *, n);
+ piolist->address_space = NULL;
+ piolist->opaque = opaque;
+ piolist->owner = owner;
+ piolist->name = name;
+ piolist->flush_coalesced_mmio = false;
+}
+
+void portio_list_set_flush_coalesced(PortioList *piolist)
+{
+ piolist->flush_coalesced_mmio = true;
+}
+
+void portio_list_destroy(PortioList *piolist)
+{
+ MemoryRegionPortioList *mrpio;
+ unsigned i;
+
+ for (i = 0; i < piolist->nr; ++i) {
+ mrpio = container_of(piolist->regions[i], MemoryRegionPortioList, mr);
+ object_unparent(OBJECT(&mrpio->mr));
+ g_free(mrpio);
+ }
+ g_free(piolist->regions);
+}
+
+static const MemoryRegionPortio *find_portio(MemoryRegionPortioList *mrpio,
+ uint64_t offset, unsigned size,
+ bool write)
+{
+ const MemoryRegionPortio *mrp;
+
+ for (mrp = mrpio->ports; mrp->size; ++mrp) {
+ if (offset >= mrp->offset && offset < mrp->offset + mrp->len &&
+ size == mrp->size &&
+ (write ? (bool)mrp->write : (bool)mrp->read)) {
+ return mrp;
+ }
+ }
+ return NULL;
+}
+
+static uint64_t portio_read(void *opaque, hwaddr addr, unsigned size)
+{
+ MemoryRegionPortioList *mrpio = opaque;
+ const MemoryRegionPortio *mrp = find_portio(mrpio, addr, size, false);
+ uint64_t data;
+
+ data = ((uint64_t)1 << (size * 8)) - 1;
+ if (mrp) {
+ data = mrp->read(mrpio->portio_opaque, mrp->base + addr);
+ } else if (size == 2) {
+ mrp = find_portio(mrpio, addr, 1, false);
+ if (mrp) {
+ data = mrp->read(mrpio->portio_opaque, mrp->base + addr);
+ if (addr + 1 < mrp->offset + mrp->len) {
+ data |= mrp->read(mrpio->portio_opaque, mrp->base + addr + 1) << 8;
+ } else {
+ data |= 0xff00;
+ }
+ }
+ }
+ return data;
+}
+
+static void portio_write(void *opaque, hwaddr addr, uint64_t data,
+ unsigned size)
+{
+ MemoryRegionPortioList *mrpio = opaque;
+ const MemoryRegionPortio *mrp = find_portio(mrpio, addr, size, true);
+
+ if (mrp) {
+ mrp->write(mrpio->portio_opaque, mrp->base + addr, data);
+ } else if (size == 2) {
+ mrp = find_portio(mrpio, addr, 1, true);
+ if (mrp) {
+ mrp->write(mrpio->portio_opaque, mrp->base + addr, data & 0xff);
+ if (addr + 1 < mrp->offset + mrp->len) {
+ mrp->write(mrpio->portio_opaque, mrp->base + addr + 1, data >> 8);
+ }
+ }
+ }
+}
+
+static const MemoryRegionOps portio_ops = {
+ .read = portio_read,
+ .write = portio_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.unaligned = true,
+ .impl.unaligned = true,
+};
+
+static void portio_list_add_1(PortioList *piolist,
+ const MemoryRegionPortio *pio_init,
+ unsigned count, unsigned start,
+ unsigned off_low, unsigned off_high)
+{
+ MemoryRegionPortioList *mrpio;
+ unsigned i;
+
+ /* Copy the sub-list and null-terminate it. */
+ mrpio = g_malloc0(sizeof(MemoryRegionPortioList) +
+ sizeof(MemoryRegionPortio) * (count + 1));
+ mrpio->portio_opaque = piolist->opaque;
+ memcpy(mrpio->ports, pio_init, sizeof(MemoryRegionPortio) * count);
+ memset(mrpio->ports + count, 0, sizeof(MemoryRegionPortio));
+
+ /* Adjust the offsets to all be zero-based for the region. */
+ for (i = 0; i < count; ++i) {
+ mrpio->ports[i].offset -= off_low;
+ mrpio->ports[i].base = start + off_low;
+ }
+
+ memory_region_init_io(&mrpio->mr, piolist->owner, &portio_ops, mrpio,
+ piolist->name, off_high - off_low);
+ if (piolist->flush_coalesced_mmio) {
+ memory_region_set_flush_coalesced(&mrpio->mr);
+ }
+ memory_region_add_subregion(piolist->address_space,
+ start + off_low, &mrpio->mr);
+ piolist->regions[piolist->nr] = &mrpio->mr;
+ ++piolist->nr;
+}
+
+void portio_list_add(PortioList *piolist,
+ MemoryRegion *address_space,
+ uint32_t start)
+{
+ const MemoryRegionPortio *pio, *pio_start = piolist->ports;
+ unsigned int off_low, off_high, off_last, count;
+
+ piolist->address_space = address_space;
+
+ /* Handle the first entry specially. */
+ off_last = off_low = pio_start->offset;
+ off_high = off_low + pio_start->len + pio_start->size - 1;
+ count = 1;
+
+ for (pio = pio_start + 1; pio->size != 0; pio++, count++) {
+ /* All entries must be sorted by offset. */
+ assert(pio->offset >= off_last);
+ off_last = pio->offset;
+
+ /* If we see a hole, break the region. */
+ if (off_last > off_high) {
+ portio_list_add_1(piolist, pio_start, count, start, off_low,
+ off_high);
+ /* ... and start collecting anew. */
+ pio_start = pio;
+ off_low = off_last;
+ off_high = off_low + pio->len + pio_start->size - 1;
+ count = 0;
+ } else if (off_last + pio->len > off_high) {
+ off_high = off_last + pio->len + pio_start->size - 1;
+ }
+ }
+
+ /* There will always be an open sub-list. */
+ portio_list_add_1(piolist, pio_start, count, start, off_low, off_high);
+}
+
+void portio_list_del(PortioList *piolist)
+{
+ MemoryRegionPortioList *mrpio;
+ unsigned i;
+
+ for (i = 0; i < piolist->nr; ++i) {
+ mrpio = container_of(piolist->regions[i], MemoryRegionPortioList, mr);
+ memory_region_del_subregion(piolist->address_space, &mrpio->mr);
+ }
+}
--- /dev/null
+/*
+ * Physical memory management
+ *
+ * Copyright 2011 Red Hat, Inc. and/or its affiliates
+ *
+ * Authors:
+ * Avi Kivity <avi@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "cpu.h"
+#include "exec/memory.h"
+#include "exec/address-spaces.h"
+#include "qapi/visitor.h"
+#include "qemu/bitops.h"
+#include "qemu/error-report.h"
+#include "qemu/main-loop.h"
+#include "qemu/qemu-print.h"
+#include "qom/object.h"
+#include "trace-root.h"
+
+#include "exec/memory-internal.h"
+#include "exec/ram_addr.h"
+#include "sysemu/kvm.h"
+#include "sysemu/runstate.h"
+#include "sysemu/tcg.h"
+#include "sysemu/accel.h"
+#include "hw/boards.h"
+#include "migration/vmstate.h"
+
+//#define DEBUG_UNASSIGNED
+
+static unsigned memory_region_transaction_depth;
+static bool memory_region_update_pending;
+static bool ioeventfd_update_pending;
+bool global_dirty_log;
+
+static QTAILQ_HEAD(, MemoryListener) memory_listeners
+ = QTAILQ_HEAD_INITIALIZER(memory_listeners);
+
+static QTAILQ_HEAD(, AddressSpace) address_spaces
+ = QTAILQ_HEAD_INITIALIZER(address_spaces);
+
+static GHashTable *flat_views;
+
+typedef struct AddrRange AddrRange;
+
+/*
+ * Note that signed integers are needed for negative offsetting in aliases
+ * (large MemoryRegion::alias_offset).
+ */
+struct AddrRange {
+ Int128 start;
+ Int128 size;
+};
+
+static AddrRange addrrange_make(Int128 start, Int128 size)
+{
+ return (AddrRange) { start, size };
+}
+
+static bool addrrange_equal(AddrRange r1, AddrRange r2)
+{
+ return int128_eq(r1.start, r2.start) && int128_eq(r1.size, r2.size);
+}
+
+static Int128 addrrange_end(AddrRange r)
+{
+ return int128_add(r.start, r.size);
+}
+
+static AddrRange addrrange_shift(AddrRange range, Int128 delta)
+{
+ int128_addto(&range.start, delta);
+ return range;
+}
+
+static bool addrrange_contains(AddrRange range, Int128 addr)
+{
+ return int128_ge(addr, range.start)
+ && int128_lt(addr, addrrange_end(range));
+}
+
+static bool addrrange_intersects(AddrRange r1, AddrRange r2)
+{
+ return addrrange_contains(r1, r2.start)
+ || addrrange_contains(r2, r1.start);
+}
+
+static AddrRange addrrange_intersection(AddrRange r1, AddrRange r2)
+{
+ Int128 start = int128_max(r1.start, r2.start);
+ Int128 end = int128_min(addrrange_end(r1), addrrange_end(r2));
+ return addrrange_make(start, int128_sub(end, start));
+}
+
+enum ListenerDirection { Forward, Reverse };
+
+#define MEMORY_LISTENER_CALL_GLOBAL(_callback, _direction, _args...) \
+ do { \
+ MemoryListener *_listener; \
+ \
+ switch (_direction) { \
+ case Forward: \
+ QTAILQ_FOREACH(_listener, &memory_listeners, link) { \
+ if (_listener->_callback) { \
+ _listener->_callback(_listener, ##_args); \
+ } \
+ } \
+ break; \
+ case Reverse: \
+ QTAILQ_FOREACH_REVERSE(_listener, &memory_listeners, link) { \
+ if (_listener->_callback) { \
+ _listener->_callback(_listener, ##_args); \
+ } \
+ } \
+ break; \
+ default: \
+ abort(); \
+ } \
+ } while (0)
+
+#define MEMORY_LISTENER_CALL(_as, _callback, _direction, _section, _args...) \
+ do { \
+ MemoryListener *_listener; \
+ \
+ switch (_direction) { \
+ case Forward: \
+ QTAILQ_FOREACH(_listener, &(_as)->listeners, link_as) { \
+ if (_listener->_callback) { \
+ _listener->_callback(_listener, _section, ##_args); \
+ } \
+ } \
+ break; \
+ case Reverse: \
+ QTAILQ_FOREACH_REVERSE(_listener, &(_as)->listeners, link_as) { \
+ if (_listener->_callback) { \
+ _listener->_callback(_listener, _section, ##_args); \
+ } \
+ } \
+ break; \
+ default: \
+ abort(); \
+ } \
+ } while (0)
+
+/* No need to ref/unref .mr, the FlatRange keeps it alive. */
+#define MEMORY_LISTENER_UPDATE_REGION(fr, as, dir, callback, _args...) \
+ do { \
+ MemoryRegionSection mrs = section_from_flat_range(fr, \
+ address_space_to_flatview(as)); \
+ MEMORY_LISTENER_CALL(as, callback, dir, &mrs, ##_args); \
+ } while(0)
+
+struct CoalescedMemoryRange {
+ AddrRange addr;
+ QTAILQ_ENTRY(CoalescedMemoryRange) link;
+};
+
+struct MemoryRegionIoeventfd {
+ AddrRange addr;
+ bool match_data;
+ uint64_t data;
+ EventNotifier *e;
+};
+
+static bool memory_region_ioeventfd_before(MemoryRegionIoeventfd *a,
+ MemoryRegionIoeventfd *b)
+{
+ if (int128_lt(a->addr.start, b->addr.start)) {
+ return true;
+ } else if (int128_gt(a->addr.start, b->addr.start)) {
+ return false;
+ } else if (int128_lt(a->addr.size, b->addr.size)) {
+ return true;
+ } else if (int128_gt(a->addr.size, b->addr.size)) {
+ return false;
+ } else if (a->match_data < b->match_data) {
+ return true;
+ } else if (a->match_data > b->match_data) {
+ return false;
+ } else if (a->match_data) {
+ if (a->data < b->data) {
+ return true;
+ } else if (a->data > b->data) {
+ return false;
+ }
+ }
+ if (a->e < b->e) {
+ return true;
+ } else if (a->e > b->e) {
+ return false;
+ }
+ return false;
+}
+
+static bool memory_region_ioeventfd_equal(MemoryRegionIoeventfd *a,
+ MemoryRegionIoeventfd *b)
+{
+ return !memory_region_ioeventfd_before(a, b)
+ && !memory_region_ioeventfd_before(b, a);
+}
+
+/* Range of memory in the global map. Addresses are absolute. */
+struct FlatRange {
+ MemoryRegion *mr;
+ hwaddr offset_in_region;
+ AddrRange addr;
+ uint8_t dirty_log_mask;
+ bool romd_mode;
+ bool readonly;
+ bool nonvolatile;
+};
+
+#define FOR_EACH_FLAT_RANGE(var, view) \
+ for (var = (view)->ranges; var < (view)->ranges + (view)->nr; ++var)
+
+static inline MemoryRegionSection
+section_from_flat_range(FlatRange *fr, FlatView *fv)
+{
+ return (MemoryRegionSection) {
+ .mr = fr->mr,
+ .fv = fv,
+ .offset_within_region = fr->offset_in_region,
+ .size = fr->addr.size,
+ .offset_within_address_space = int128_get64(fr->addr.start),
+ .readonly = fr->readonly,
+ .nonvolatile = fr->nonvolatile,
+ };
+}
+
+static bool flatrange_equal(FlatRange *a, FlatRange *b)
+{
+ return a->mr == b->mr
+ && addrrange_equal(a->addr, b->addr)
+ && a->offset_in_region == b->offset_in_region
+ && a->romd_mode == b->romd_mode
+ && a->readonly == b->readonly
+ && a->nonvolatile == b->nonvolatile;
+}
+
+static FlatView *flatview_new(MemoryRegion *mr_root)
+{
+ FlatView *view;
+
+ view = g_new0(FlatView, 1);
+ view->ref = 1;
+ view->root = mr_root;
+ memory_region_ref(mr_root);
+ trace_flatview_new(view, mr_root);
+
+ return view;
+}
+
+/* Insert a range into a given position. Caller is responsible for maintaining
+ * sorting order.
+ */
+static void flatview_insert(FlatView *view, unsigned pos, FlatRange *range)
+{
+ if (view->nr == view->nr_allocated) {
+ view->nr_allocated = MAX(2 * view->nr, 10);
+ view->ranges = g_realloc(view->ranges,
+ view->nr_allocated * sizeof(*view->ranges));
+ }
+ memmove(view->ranges + pos + 1, view->ranges + pos,
+ (view->nr - pos) * sizeof(FlatRange));
+ view->ranges[pos] = *range;
+ memory_region_ref(range->mr);
+ ++view->nr;
+}
+
+static void flatview_destroy(FlatView *view)
+{
+ int i;
+
+ trace_flatview_destroy(view, view->root);
+ if (view->dispatch) {
+ address_space_dispatch_free(view->dispatch);
+ }
+ for (i = 0; i < view->nr; i++) {
+ memory_region_unref(view->ranges[i].mr);
+ }
+ g_free(view->ranges);
+ memory_region_unref(view->root);
+ g_free(view);
+}
+
+static bool flatview_ref(FlatView *view)
+{
+ return atomic_fetch_inc_nonzero(&view->ref) > 0;
+}
+
+void flatview_unref(FlatView *view)
+{
+ if (atomic_fetch_dec(&view->ref) == 1) {
+ trace_flatview_destroy_rcu(view, view->root);
+ assert(view->root);
+ call_rcu(view, flatview_destroy, rcu);
+ }
+}
+
+static bool can_merge(FlatRange *r1, FlatRange *r2)
+{
+ return int128_eq(addrrange_end(r1->addr), r2->addr.start)
+ && r1->mr == r2->mr
+ && int128_eq(int128_add(int128_make64(r1->offset_in_region),
+ r1->addr.size),
+ int128_make64(r2->offset_in_region))
+ && r1->dirty_log_mask == r2->dirty_log_mask
+ && r1->romd_mode == r2->romd_mode
+ && r1->readonly == r2->readonly
+ && r1->nonvolatile == r2->nonvolatile;
+}
+
+/* Attempt to simplify a view by merging adjacent ranges */
+static void flatview_simplify(FlatView *view)
+{
+ unsigned i, j, k;
+
+ i = 0;
+ while (i < view->nr) {
+ j = i + 1;
+ while (j < view->nr
+ && can_merge(&view->ranges[j-1], &view->ranges[j])) {
+ int128_addto(&view->ranges[i].addr.size, view->ranges[j].addr.size);
+ ++j;
+ }
+ ++i;
+ for (k = i; k < j; k++) {
+ memory_region_unref(view->ranges[k].mr);
+ }
+ memmove(&view->ranges[i], &view->ranges[j],
+ (view->nr - j) * sizeof(view->ranges[j]));
+ view->nr -= j - i;
+ }
+}
+
+static bool memory_region_big_endian(MemoryRegion *mr)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ return mr->ops->endianness != DEVICE_LITTLE_ENDIAN;
+#else
+ return mr->ops->endianness == DEVICE_BIG_ENDIAN;
+#endif
+}
+
+static void adjust_endianness(MemoryRegion *mr, uint64_t *data, MemOp op)
+{
+ if ((op & MO_BSWAP) != devend_memop(mr->ops->endianness)) {
+ switch (op & MO_SIZE) {
+ case MO_8:
+ break;
+ case MO_16:
+ *data = bswap16(*data);
+ break;
+ case MO_32:
+ *data = bswap32(*data);
+ break;
+ case MO_64:
+ *data = bswap64(*data);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ }
+}
+
+static inline void memory_region_shift_read_access(uint64_t *value,
+ signed shift,
+ uint64_t mask,
+ uint64_t tmp)
+{
+ if (shift >= 0) {
+ *value |= (tmp & mask) << shift;
+ } else {
+ *value |= (tmp & mask) >> -shift;
+ }
+}
+
+static inline uint64_t memory_region_shift_write_access(uint64_t *value,
+ signed shift,
+ uint64_t mask)
+{
+ uint64_t tmp;
+
+ if (shift >= 0) {
+ tmp = (*value >> shift) & mask;
+ } else {
+ tmp = (*value << -shift) & mask;
+ }
+
+ return tmp;
+}
+
+static hwaddr memory_region_to_absolute_addr(MemoryRegion *mr, hwaddr offset)
+{
+ MemoryRegion *root;
+ hwaddr abs_addr = offset;
+
+ abs_addr += mr->addr;
+ for (root = mr; root->container; ) {
+ root = root->container;
+ abs_addr += root->addr;
+ }
+
+ return abs_addr;
+}
+
+static int get_cpu_index(void)
+{
+ if (current_cpu) {
+ return current_cpu->cpu_index;
+ }
+ return -1;
+}
+
+static MemTxResult memory_region_read_accessor(MemoryRegion *mr,
+ hwaddr addr,
+ uint64_t *value,
+ unsigned size,
+ signed shift,
+ uint64_t mask,
+ MemTxAttrs attrs)
+{
+ uint64_t tmp;
+
+ tmp = mr->ops->read(mr->opaque, addr, size);
+ if (mr->subpage) {
+ trace_memory_region_subpage_read(get_cpu_index(), mr, addr, tmp, size);
+ } else if (trace_event_get_state_backends(TRACE_MEMORY_REGION_OPS_READ)) {
+ hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr);
+ trace_memory_region_ops_read(get_cpu_index(), mr, abs_addr, tmp, size);
+ }
+ memory_region_shift_read_access(value, shift, mask, tmp);
+ return MEMTX_OK;
+}
+
+static MemTxResult memory_region_read_with_attrs_accessor(MemoryRegion *mr,
+ hwaddr addr,
+ uint64_t *value,
+ unsigned size,
+ signed shift,
+ uint64_t mask,
+ MemTxAttrs attrs)
+{
+ uint64_t tmp = 0;
+ MemTxResult r;
+
+ r = mr->ops->read_with_attrs(mr->opaque, addr, &tmp, size, attrs);
+ if (mr->subpage) {
+ trace_memory_region_subpage_read(get_cpu_index(), mr, addr, tmp, size);
+ } else if (trace_event_get_state_backends(TRACE_MEMORY_REGION_OPS_READ)) {
+ hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr);
+ trace_memory_region_ops_read(get_cpu_index(), mr, abs_addr, tmp, size);
+ }
+ memory_region_shift_read_access(value, shift, mask, tmp);
+ return r;
+}
+
+static MemTxResult memory_region_write_accessor(MemoryRegion *mr,
+ hwaddr addr,
+ uint64_t *value,
+ unsigned size,
+ signed shift,
+ uint64_t mask,
+ MemTxAttrs attrs)
+{
+ uint64_t tmp = memory_region_shift_write_access(value, shift, mask);
+
+ if (mr->subpage) {
+ trace_memory_region_subpage_write(get_cpu_index(), mr, addr, tmp, size);
+ } else if (trace_event_get_state_backends(TRACE_MEMORY_REGION_OPS_WRITE)) {
+ hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr);
+ trace_memory_region_ops_write(get_cpu_index(), mr, abs_addr, tmp, size);
+ }
+ mr->ops->write(mr->opaque, addr, tmp, size);
+ return MEMTX_OK;
+}
+
+static MemTxResult memory_region_write_with_attrs_accessor(MemoryRegion *mr,
+ hwaddr addr,
+ uint64_t *value,
+ unsigned size,
+ signed shift,
+ uint64_t mask,
+ MemTxAttrs attrs)
+{
+ uint64_t tmp = memory_region_shift_write_access(value, shift, mask);
+
+ if (mr->subpage) {
+ trace_memory_region_subpage_write(get_cpu_index(), mr, addr, tmp, size);
+ } else if (trace_event_get_state_backends(TRACE_MEMORY_REGION_OPS_WRITE)) {
+ hwaddr abs_addr = memory_region_to_absolute_addr(mr, addr);
+ trace_memory_region_ops_write(get_cpu_index(), mr, abs_addr, tmp, size);
+ }
+ return mr->ops->write_with_attrs(mr->opaque, addr, tmp, size, attrs);
+}
+
+static MemTxResult access_with_adjusted_size(hwaddr addr,
+ uint64_t *value,
+ unsigned size,
+ unsigned access_size_min,
+ unsigned access_size_max,
+ MemTxResult (*access_fn)
+ (MemoryRegion *mr,
+ hwaddr addr,
+ uint64_t *value,
+ unsigned size,
+ signed shift,
+ uint64_t mask,
+ MemTxAttrs attrs),
+ MemoryRegion *mr,
+ MemTxAttrs attrs)
+{
+ uint64_t access_mask;
+ unsigned access_size;
+ unsigned i;
+ MemTxResult r = MEMTX_OK;
+
+ if (!access_size_min) {
+ access_size_min = 1;
+ }
+ if (!access_size_max) {
+ access_size_max = 4;
+ }
+
+ /* FIXME: support unaligned access? */
+ access_size = MAX(MIN(size, access_size_max), access_size_min);
+ access_mask = MAKE_64BIT_MASK(0, access_size * 8);
+ if (memory_region_big_endian(mr)) {
+ for (i = 0; i < size; i += access_size) {
+ r |= access_fn(mr, addr + i, value, access_size,
+ (size - access_size - i) * 8, access_mask, attrs);
+ }
+ } else {
+ for (i = 0; i < size; i += access_size) {
+ r |= access_fn(mr, addr + i, value, access_size, i * 8,
+ access_mask, attrs);
+ }
+ }
+ return r;
+}
+
+static AddressSpace *memory_region_to_address_space(MemoryRegion *mr)
+{
+ AddressSpace *as;
+
+ while (mr->container) {
+ mr = mr->container;
+ }
+ QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
+ if (mr == as->root) {
+ return as;
+ }
+ }
+ return NULL;
+}
+
+/* Render a memory region into the global view. Ranges in @view obscure
+ * ranges in @mr.
+ */
+static void render_memory_region(FlatView *view,
+ MemoryRegion *mr,
+ Int128 base,
+ AddrRange clip,
+ bool readonly,
+ bool nonvolatile)
+{
+ MemoryRegion *subregion;
+ unsigned i;
+ hwaddr offset_in_region;
+ Int128 remain;
+ Int128 now;
+ FlatRange fr;
+ AddrRange tmp;
+
+ if (!mr->enabled) {
+ return;
+ }
+
+ int128_addto(&base, int128_make64(mr->addr));
+ readonly |= mr->readonly;
+ nonvolatile |= mr->nonvolatile;
+
+ tmp = addrrange_make(base, mr->size);
+
+ if (!addrrange_intersects(tmp, clip)) {
+ return;
+ }
+
+ clip = addrrange_intersection(tmp, clip);
+
+ if (mr->alias) {
+ int128_subfrom(&base, int128_make64(mr->alias->addr));
+ int128_subfrom(&base, int128_make64(mr->alias_offset));
+ render_memory_region(view, mr->alias, base, clip,
+ readonly, nonvolatile);
+ return;
+ }
+
+ /* Render subregions in priority order. */
+ QTAILQ_FOREACH(subregion, &mr->subregions, subregions_link) {
+ render_memory_region(view, subregion, base, clip,
+ readonly, nonvolatile);
+ }
+
+ if (!mr->terminates) {
+ return;
+ }
+
+ offset_in_region = int128_get64(int128_sub(clip.start, base));
+ base = clip.start;
+ remain = clip.size;
+
+ fr.mr = mr;
+ fr.dirty_log_mask = memory_region_get_dirty_log_mask(mr);
+ fr.romd_mode = mr->romd_mode;
+ fr.readonly = readonly;
+ fr.nonvolatile = nonvolatile;
+
+ /* Render the region itself into any gaps left by the current view. */
+ for (i = 0; i < view->nr && int128_nz(remain); ++i) {
+ if (int128_ge(base, addrrange_end(view->ranges[i].addr))) {
+ continue;
+ }
+ if (int128_lt(base, view->ranges[i].addr.start)) {
+ now = int128_min(remain,
+ int128_sub(view->ranges[i].addr.start, base));
+ fr.offset_in_region = offset_in_region;
+ fr.addr = addrrange_make(base, now);
+ flatview_insert(view, i, &fr);
+ ++i;
+ int128_addto(&base, now);
+ offset_in_region += int128_get64(now);
+ int128_subfrom(&remain, now);
+ }
+ now = int128_sub(int128_min(int128_add(base, remain),
+ addrrange_end(view->ranges[i].addr)),
+ base);
+ int128_addto(&base, now);
+ offset_in_region += int128_get64(now);
+ int128_subfrom(&remain, now);
+ }
+ if (int128_nz(remain)) {
+ fr.offset_in_region = offset_in_region;
+ fr.addr = addrrange_make(base, remain);
+ flatview_insert(view, i, &fr);
+ }
+}
+
+static MemoryRegion *memory_region_get_flatview_root(MemoryRegion *mr)
+{
+ while (mr->enabled) {
+ if (mr->alias) {
+ if (!mr->alias_offset && int128_ge(mr->size, mr->alias->size)) {
+ /* The alias is included in its entirety. Use it as
+ * the "real" root, so that we can share more FlatViews.
+ */
+ mr = mr->alias;
+ continue;
+ }
+ } else if (!mr->terminates) {
+ unsigned int found = 0;
+ MemoryRegion *child, *next = NULL;
+ QTAILQ_FOREACH(child, &mr->subregions, subregions_link) {
+ if (child->enabled) {
+ if (++found > 1) {
+ next = NULL;
+ break;
+ }
+ if (!child->addr && int128_ge(mr->size, child->size)) {
+ /* A child is included in its entirety. If it's the only
+ * enabled one, use it in the hope of finding an alias down the
+ * way. This will also let us share FlatViews.
+ */
+ next = child;
+ }
+ }
+ }
+ if (found == 0) {
+ return NULL;
+ }
+ if (next) {
+ mr = next;
+ continue;
+ }
+ }
+
+ return mr;
+ }
+
+ return NULL;
+}
+
+/* Render a memory topology into a list of disjoint absolute ranges. */
+static FlatView *generate_memory_topology(MemoryRegion *mr)
+{
+ int i;
+ FlatView *view;
+
+ view = flatview_new(mr);
+
+ if (mr) {
+ render_memory_region(view, mr, int128_zero(),
+ addrrange_make(int128_zero(), int128_2_64()),
+ false, false);
+ }
+ flatview_simplify(view);
+
+ view->dispatch = address_space_dispatch_new(view);
+ for (i = 0; i < view->nr; i++) {
+ MemoryRegionSection mrs =
+ section_from_flat_range(&view->ranges[i], view);
+ flatview_add_to_dispatch(view, &mrs);
+ }
+ address_space_dispatch_compact(view->dispatch);
+ g_hash_table_replace(flat_views, mr, view);
+
+ return view;
+}
+
+static void address_space_add_del_ioeventfds(AddressSpace *as,
+ MemoryRegionIoeventfd *fds_new,
+ unsigned fds_new_nb,
+ MemoryRegionIoeventfd *fds_old,
+ unsigned fds_old_nb)
+{
+ unsigned iold, inew;
+ MemoryRegionIoeventfd *fd;
+ MemoryRegionSection section;
+
+ /* Generate a symmetric difference of the old and new fd sets, adding
+ * and deleting as necessary.
+ */
+
+ iold = inew = 0;
+ while (iold < fds_old_nb || inew < fds_new_nb) {
+ if (iold < fds_old_nb
+ && (inew == fds_new_nb
+ || memory_region_ioeventfd_before(&fds_old[iold],
+ &fds_new[inew]))) {
+ fd = &fds_old[iold];
+ section = (MemoryRegionSection) {
+ .fv = address_space_to_flatview(as),
+ .offset_within_address_space = int128_get64(fd->addr.start),
+ .size = fd->addr.size,
+ };
+ MEMORY_LISTENER_CALL(as, eventfd_del, Forward, §ion,
+ fd->match_data, fd->data, fd->e);
+ ++iold;
+ } else if (inew < fds_new_nb
+ && (iold == fds_old_nb
+ || memory_region_ioeventfd_before(&fds_new[inew],
+ &fds_old[iold]))) {
+ fd = &fds_new[inew];
+ section = (MemoryRegionSection) {
+ .fv = address_space_to_flatview(as),
+ .offset_within_address_space = int128_get64(fd->addr.start),
+ .size = fd->addr.size,
+ };
+ MEMORY_LISTENER_CALL(as, eventfd_add, Reverse, §ion,
+ fd->match_data, fd->data, fd->e);
+ ++inew;
+ } else {
+ ++iold;
+ ++inew;
+ }
+ }
+}
+
+FlatView *address_space_get_flatview(AddressSpace *as)
+{
+ FlatView *view;
+
+ RCU_READ_LOCK_GUARD();
+ do {
+ view = address_space_to_flatview(as);
+ /* If somebody has replaced as->current_map concurrently,
+ * flatview_ref returns false.
+ */
+ } while (!flatview_ref(view));
+ return view;
+}
+
+static void address_space_update_ioeventfds(AddressSpace *as)
+{
+ FlatView *view;
+ FlatRange *fr;
+ unsigned ioeventfd_nb = 0;
+ unsigned ioeventfd_max;
+ MemoryRegionIoeventfd *ioeventfds;
+ AddrRange tmp;
+ unsigned i;
+
+ /*
+ * It is likely that the number of ioeventfds hasn't changed much, so use
+ * the previous size as the starting value, with some headroom to avoid
+ * gratuitous reallocations.
+ */
+ ioeventfd_max = QEMU_ALIGN_UP(as->ioeventfd_nb, 4);
+ ioeventfds = g_new(MemoryRegionIoeventfd, ioeventfd_max);
+
+ view = address_space_get_flatview(as);
+ FOR_EACH_FLAT_RANGE(fr, view) {
+ for (i = 0; i < fr->mr->ioeventfd_nb; ++i) {
+ tmp = addrrange_shift(fr->mr->ioeventfds[i].addr,
+ int128_sub(fr->addr.start,
+ int128_make64(fr->offset_in_region)));
+ if (addrrange_intersects(fr->addr, tmp)) {
+ ++ioeventfd_nb;
+ if (ioeventfd_nb > ioeventfd_max) {
+ ioeventfd_max = MAX(ioeventfd_max * 2, 4);
+ ioeventfds = g_realloc(ioeventfds,
+ ioeventfd_max * sizeof(*ioeventfds));
+ }
+ ioeventfds[ioeventfd_nb-1] = fr->mr->ioeventfds[i];
+ ioeventfds[ioeventfd_nb-1].addr = tmp;
+ }
+ }
+ }
+
+ address_space_add_del_ioeventfds(as, ioeventfds, ioeventfd_nb,
+ as->ioeventfds, as->ioeventfd_nb);
+
+ g_free(as->ioeventfds);
+ as->ioeventfds = ioeventfds;
+ as->ioeventfd_nb = ioeventfd_nb;
+ flatview_unref(view);
+}
+
+/*
+ * Notify the memory listeners about the coalesced IO change events of
+ * range `cmr'. Only the part that has intersection of the specified
+ * FlatRange will be sent.
+ */
+static void flat_range_coalesced_io_notify(FlatRange *fr, AddressSpace *as,
+ CoalescedMemoryRange *cmr, bool add)
+{
+ AddrRange tmp;
+
+ tmp = addrrange_shift(cmr->addr,
+ int128_sub(fr->addr.start,
+ int128_make64(fr->offset_in_region)));
+ if (!addrrange_intersects(tmp, fr->addr)) {
+ return;
+ }
+ tmp = addrrange_intersection(tmp, fr->addr);
+
+ if (add) {
+ MEMORY_LISTENER_UPDATE_REGION(fr, as, Forward, coalesced_io_add,
+ int128_get64(tmp.start),
+ int128_get64(tmp.size));
+ } else {
+ MEMORY_LISTENER_UPDATE_REGION(fr, as, Reverse, coalesced_io_del,
+ int128_get64(tmp.start),
+ int128_get64(tmp.size));
+ }
+}
+
+static void flat_range_coalesced_io_del(FlatRange *fr, AddressSpace *as)
+{
+ CoalescedMemoryRange *cmr;
+
+ QTAILQ_FOREACH(cmr, &fr->mr->coalesced, link) {
+ flat_range_coalesced_io_notify(fr, as, cmr, false);
+ }
+}
+
+static void flat_range_coalesced_io_add(FlatRange *fr, AddressSpace *as)
+{
+ MemoryRegion *mr = fr->mr;
+ CoalescedMemoryRange *cmr;
+
+ if (QTAILQ_EMPTY(&mr->coalesced)) {
+ return;
+ }
+
+ QTAILQ_FOREACH(cmr, &mr->coalesced, link) {
+ flat_range_coalesced_io_notify(fr, as, cmr, true);
+ }
+}
+
+static void address_space_update_topology_pass(AddressSpace *as,
+ const FlatView *old_view,
+ const FlatView *new_view,
+ bool adding)
+{
+ unsigned iold, inew;
+ FlatRange *frold, *frnew;
+
+ /* Generate a symmetric difference of the old and new memory maps.
+ * Kill ranges in the old map, and instantiate ranges in the new map.
+ */
+ iold = inew = 0;
+ while (iold < old_view->nr || inew < new_view->nr) {
+ if (iold < old_view->nr) {
+ frold = &old_view->ranges[iold];
+ } else {
+ frold = NULL;
+ }
+ if (inew < new_view->nr) {
+ frnew = &new_view->ranges[inew];
+ } else {
+ frnew = NULL;
+ }
+
+ if (frold
+ && (!frnew
+ || int128_lt(frold->addr.start, frnew->addr.start)
+ || (int128_eq(frold->addr.start, frnew->addr.start)
+ && !flatrange_equal(frold, frnew)))) {
+ /* In old but not in new, or in both but attributes changed. */
+
+ if (!adding) {
+ flat_range_coalesced_io_del(frold, as);
+ MEMORY_LISTENER_UPDATE_REGION(frold, as, Reverse, region_del);
+ }
+
+ ++iold;
+ } else if (frold && frnew && flatrange_equal(frold, frnew)) {
+ /* In both and unchanged (except logging may have changed) */
+
+ if (adding) {
+ MEMORY_LISTENER_UPDATE_REGION(frnew, as, Forward, region_nop);
+ if (frnew->dirty_log_mask & ~frold->dirty_log_mask) {
+ MEMORY_LISTENER_UPDATE_REGION(frnew, as, Forward, log_start,
+ frold->dirty_log_mask,
+ frnew->dirty_log_mask);
+ }
+ if (frold->dirty_log_mask & ~frnew->dirty_log_mask) {
+ MEMORY_LISTENER_UPDATE_REGION(frnew, as, Reverse, log_stop,
+ frold->dirty_log_mask,
+ frnew->dirty_log_mask);
+ }
+ }
+
+ ++iold;
+ ++inew;
+ } else {
+ /* In new */
+
+ if (adding) {
+ MEMORY_LISTENER_UPDATE_REGION(frnew, as, Forward, region_add);
+ flat_range_coalesced_io_add(frnew, as);
+ }
+
+ ++inew;
+ }
+ }
+}
+
+static void flatviews_init(void)
+{
+ static FlatView *empty_view;
+
+ if (flat_views) {
+ return;
+ }
+
+ flat_views = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
+ (GDestroyNotify) flatview_unref);
+ if (!empty_view) {
+ empty_view = generate_memory_topology(NULL);
+ /* We keep it alive forever in the global variable. */
+ flatview_ref(empty_view);
+ } else {
+ g_hash_table_replace(flat_views, NULL, empty_view);
+ flatview_ref(empty_view);
+ }
+}
+
+static void flatviews_reset(void)
+{
+ AddressSpace *as;
+
+ if (flat_views) {
+ g_hash_table_unref(flat_views);
+ flat_views = NULL;
+ }
+ flatviews_init();
+
+ /* Render unique FVs */
+ QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
+ MemoryRegion *physmr = memory_region_get_flatview_root(as->root);
+
+ if (g_hash_table_lookup(flat_views, physmr)) {
+ continue;
+ }
+
+ generate_memory_topology(physmr);
+ }
+}
+
+static void address_space_set_flatview(AddressSpace *as)
+{
+ FlatView *old_view = address_space_to_flatview(as);
+ MemoryRegion *physmr = memory_region_get_flatview_root(as->root);
+ FlatView *new_view = g_hash_table_lookup(flat_views, physmr);
+
+ assert(new_view);
+
+ if (old_view == new_view) {
+ return;
+ }
+
+ if (old_view) {
+ flatview_ref(old_view);
+ }
+
+ flatview_ref(new_view);
+
+ if (!QTAILQ_EMPTY(&as->listeners)) {
+ FlatView tmpview = { .nr = 0 }, *old_view2 = old_view;
+
+ if (!old_view2) {
+ old_view2 = &tmpview;
+ }
+ address_space_update_topology_pass(as, old_view2, new_view, false);
+ address_space_update_topology_pass(as, old_view2, new_view, true);
+ }
+
+ /* Writes are protected by the BQL. */
+ atomic_rcu_set(&as->current_map, new_view);
+ if (old_view) {
+ flatview_unref(old_view);
+ }
+
+ /* Note that all the old MemoryRegions are still alive up to this
+ * point. This relieves most MemoryListeners from the need to
+ * ref/unref the MemoryRegions they get---unless they use them
+ * outside the iothread mutex, in which case precise reference
+ * counting is necessary.
+ */
+ if (old_view) {
+ flatview_unref(old_view);
+ }
+}
+
+static void address_space_update_topology(AddressSpace *as)
+{
+ MemoryRegion *physmr = memory_region_get_flatview_root(as->root);
+
+ flatviews_init();
+ if (!g_hash_table_lookup(flat_views, physmr)) {
+ generate_memory_topology(physmr);
+ }
+ address_space_set_flatview(as);
+}
+
+void memory_region_transaction_begin(void)
+{
+ qemu_flush_coalesced_mmio_buffer();
+ ++memory_region_transaction_depth;
+}
+
+void memory_region_transaction_commit(void)
+{
+ AddressSpace *as;
+
+ assert(memory_region_transaction_depth);
+ assert(qemu_mutex_iothread_locked());
+
+ --memory_region_transaction_depth;
+ if (!memory_region_transaction_depth) {
+ if (memory_region_update_pending) {
+ flatviews_reset();
+
+ MEMORY_LISTENER_CALL_GLOBAL(begin, Forward);
+
+ QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
+ address_space_set_flatview(as);
+ address_space_update_ioeventfds(as);
+ }
+ memory_region_update_pending = false;
+ ioeventfd_update_pending = false;
+ MEMORY_LISTENER_CALL_GLOBAL(commit, Forward);
+ } else if (ioeventfd_update_pending) {
+ QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
+ address_space_update_ioeventfds(as);
+ }
+ ioeventfd_update_pending = false;
+ }
+ }
+}
+
+static void memory_region_destructor_none(MemoryRegion *mr)
+{
+}
+
+static void memory_region_destructor_ram(MemoryRegion *mr)
+{
+ qemu_ram_free(mr->ram_block);
+}
+
+static bool memory_region_need_escape(char c)
+{
+ return c == '/' || c == '[' || c == '\\' || c == ']';
+}
+
+static char *memory_region_escape_name(const char *name)
+{
+ const char *p;
+ char *escaped, *q;
+ uint8_t c;
+ size_t bytes = 0;
+
+ for (p = name; *p; p++) {
+ bytes += memory_region_need_escape(*p) ? 4 : 1;
+ }
+ if (bytes == p - name) {
+ return g_memdup(name, bytes + 1);
+ }
+
+ escaped = g_malloc(bytes + 1);
+ for (p = name, q = escaped; *p; p++) {
+ c = *p;
+ if (unlikely(memory_region_need_escape(c))) {
+ *q++ = '\\';
+ *q++ = 'x';
+ *q++ = "0123456789abcdef"[c >> 4];
+ c = "0123456789abcdef"[c & 15];
+ }
+ *q++ = c;
+ }
+ *q = 0;
+ return escaped;
+}
+
+static void memory_region_do_init(MemoryRegion *mr,
+ Object *owner,
+ const char *name,
+ uint64_t size)
+{
+ mr->size = int128_make64(size);
+ if (size == UINT64_MAX) {
+ mr->size = int128_2_64();
+ }
+ mr->name = g_strdup(name);
+ mr->owner = owner;
+ mr->ram_block = NULL;
+
+ if (name) {
+ char *escaped_name = memory_region_escape_name(name);
+ char *name_array = g_strdup_printf("%s[*]", escaped_name);
+
+ if (!owner) {
+ owner = container_get(qdev_get_machine(), "/unattached");
+ }
+
+ object_property_add_child(owner, name_array, OBJECT(mr));
+ object_unref(OBJECT(mr));
+ g_free(name_array);
+ g_free(escaped_name);
+ }
+}
+
+void memory_region_init(MemoryRegion *mr,
+ Object *owner,
+ const char *name,
+ uint64_t size)
+{
+ object_initialize(mr, sizeof(*mr), TYPE_MEMORY_REGION);
+ memory_region_do_init(mr, owner, name, size);
+}
+
+static void memory_region_get_container(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ MemoryRegion *mr = MEMORY_REGION(obj);
+ char *path = (char *)"";
+
+ if (mr->container) {
+ path = object_get_canonical_path(OBJECT(mr->container));
+ }
+ visit_type_str(v, name, &path, errp);
+ if (mr->container) {
+ g_free(path);
+ }
+}
+
+static Object *memory_region_resolve_container(Object *obj, void *opaque,
+ const char *part)
+{
+ MemoryRegion *mr = MEMORY_REGION(obj);
+
+ return OBJECT(mr->container);
+}
+
+static void memory_region_get_priority(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ MemoryRegion *mr = MEMORY_REGION(obj);
+ int32_t value = mr->priority;
+
+ visit_type_int32(v, name, &value, errp);
+}
+
+static void memory_region_get_size(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ MemoryRegion *mr = MEMORY_REGION(obj);
+ uint64_t value = memory_region_size(mr);
+
+ visit_type_uint64(v, name, &value, errp);
+}
+
+static void memory_region_initfn(Object *obj)
+{
+ MemoryRegion *mr = MEMORY_REGION(obj);
+ ObjectProperty *op;
+
+ mr->ops = &unassigned_mem_ops;
+ mr->enabled = true;
+ mr->romd_mode = true;
+ mr->global_locking = true;
+ mr->destructor = memory_region_destructor_none;
+ QTAILQ_INIT(&mr->subregions);
+ QTAILQ_INIT(&mr->coalesced);
+
+ op = object_property_add(OBJECT(mr), "container",
+ "link<" TYPE_MEMORY_REGION ">",
+ memory_region_get_container,
+ NULL, /* memory_region_set_container */
+ NULL, NULL);
+ op->resolve = memory_region_resolve_container;
+
+ object_property_add_uint64_ptr(OBJECT(mr), "addr",
+ &mr->addr, OBJ_PROP_FLAG_READ);
+ object_property_add(OBJECT(mr), "priority", "uint32",
+ memory_region_get_priority,
+ NULL, /* memory_region_set_priority */
+ NULL, NULL);
+ object_property_add(OBJECT(mr), "size", "uint64",
+ memory_region_get_size,
+ NULL, /* memory_region_set_size, */
+ NULL, NULL);
+}
+
+static void iommu_memory_region_initfn(Object *obj)
+{
+ MemoryRegion *mr = MEMORY_REGION(obj);
+
+ mr->is_iommu = true;
+}
+
+static uint64_t unassigned_mem_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+#ifdef DEBUG_UNASSIGNED
+ printf("Unassigned mem read " TARGET_FMT_plx "\n", addr);
+#endif
+ return 0;
+}
+
+static void unassigned_mem_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+#ifdef DEBUG_UNASSIGNED
+ printf("Unassigned mem write " TARGET_FMT_plx " = 0x%"PRIx64"\n", addr, val);
+#endif
+}
+
+static bool unassigned_mem_accepts(void *opaque, hwaddr addr,
+ unsigned size, bool is_write,
+ MemTxAttrs attrs)
+{
+ return false;
+}
+
+const MemoryRegionOps unassigned_mem_ops = {
+ .valid.accepts = unassigned_mem_accepts,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static uint64_t memory_region_ram_device_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ MemoryRegion *mr = opaque;
+ uint64_t data = (uint64_t)~0;
+
+ switch (size) {
+ case 1:
+ data = *(uint8_t *)(mr->ram_block->host + addr);
+ break;
+ case 2:
+ data = *(uint16_t *)(mr->ram_block->host + addr);
+ break;
+ case 4:
+ data = *(uint32_t *)(mr->ram_block->host + addr);
+ break;
+ case 8:
+ data = *(uint64_t *)(mr->ram_block->host + addr);
+ break;
+ }
+
+ trace_memory_region_ram_device_read(get_cpu_index(), mr, addr, data, size);
+
+ return data;
+}
+
+static void memory_region_ram_device_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ MemoryRegion *mr = opaque;
+
+ trace_memory_region_ram_device_write(get_cpu_index(), mr, addr, data, size);
+
+ switch (size) {
+ case 1:
+ *(uint8_t *)(mr->ram_block->host + addr) = (uint8_t)data;
+ break;
+ case 2:
+ *(uint16_t *)(mr->ram_block->host + addr) = (uint16_t)data;
+ break;
+ case 4:
+ *(uint32_t *)(mr->ram_block->host + addr) = (uint32_t)data;
+ break;
+ case 8:
+ *(uint64_t *)(mr->ram_block->host + addr) = data;
+ break;
+ }
+}
+
+static const MemoryRegionOps ram_device_mem_ops = {
+ .read = memory_region_ram_device_read,
+ .write = memory_region_ram_device_write,
+ .endianness = DEVICE_HOST_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 8,
+ .unaligned = true,
+ },
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 8,
+ .unaligned = true,
+ },
+};
+
+bool memory_region_access_valid(MemoryRegion *mr,
+ hwaddr addr,
+ unsigned size,
+ bool is_write,
+ MemTxAttrs attrs)
+{
+ if (mr->ops->valid.accepts
+ && !mr->ops->valid.accepts(mr->opaque, addr, size, is_write, attrs)) {
+ return false;
+ }
+
+ if (!mr->ops->valid.unaligned && (addr & (size - 1))) {
+ return false;
+ }
+
+ /* Treat zero as compatibility all valid */
+ if (!mr->ops->valid.max_access_size) {
+ return true;
+ }
+
+ if (size > mr->ops->valid.max_access_size
+ || size < mr->ops->valid.min_access_size) {
+ return false;
+ }
+ return true;
+}
+
+static MemTxResult memory_region_dispatch_read1(MemoryRegion *mr,
+ hwaddr addr,
+ uint64_t *pval,
+ unsigned size,
+ MemTxAttrs attrs)
+{
+ *pval = 0;
+
+ if (mr->ops->read) {
+ return access_with_adjusted_size(addr, pval, size,
+ mr->ops->impl.min_access_size,
+ mr->ops->impl.max_access_size,
+ memory_region_read_accessor,
+ mr, attrs);
+ } else {
+ return access_with_adjusted_size(addr, pval, size,
+ mr->ops->impl.min_access_size,
+ mr->ops->impl.max_access_size,
+ memory_region_read_with_attrs_accessor,
+ mr, attrs);
+ }
+}
+
+MemTxResult memory_region_dispatch_read(MemoryRegion *mr,
+ hwaddr addr,
+ uint64_t *pval,
+ MemOp op,
+ MemTxAttrs attrs)
+{
+ unsigned size = memop_size(op);
+ MemTxResult r;
+
+ if (!memory_region_access_valid(mr, addr, size, false, attrs)) {
+ *pval = unassigned_mem_read(mr, addr, size);
+ return MEMTX_DECODE_ERROR;
+ }
+
+ r = memory_region_dispatch_read1(mr, addr, pval, size, attrs);
+ adjust_endianness(mr, pval, op);
+ return r;
+}
+
+/* Return true if an eventfd was signalled */
+static bool memory_region_dispatch_write_eventfds(MemoryRegion *mr,
+ hwaddr addr,
+ uint64_t data,
+ unsigned size,
+ MemTxAttrs attrs)
+{
+ MemoryRegionIoeventfd ioeventfd = {
+ .addr = addrrange_make(int128_make64(addr), int128_make64(size)),
+ .data = data,
+ };
+ unsigned i;
+
+ for (i = 0; i < mr->ioeventfd_nb; i++) {
+ ioeventfd.match_data = mr->ioeventfds[i].match_data;
+ ioeventfd.e = mr->ioeventfds[i].e;
+
+ if (memory_region_ioeventfd_equal(&ioeventfd, &mr->ioeventfds[i])) {
+ event_notifier_set(ioeventfd.e);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+MemTxResult memory_region_dispatch_write(MemoryRegion *mr,
+ hwaddr addr,
+ uint64_t data,
+ MemOp op,
+ MemTxAttrs attrs)
+{
+ unsigned size = memop_size(op);
+
+ if (!memory_region_access_valid(mr, addr, size, true, attrs)) {
+ unassigned_mem_write(mr, addr, data, size);
+ return MEMTX_DECODE_ERROR;
+ }
+
+ adjust_endianness(mr, &data, op);
+
+ if ((!kvm_eventfds_enabled()) &&
+ memory_region_dispatch_write_eventfds(mr, addr, data, size, attrs)) {
+ return MEMTX_OK;
+ }
+
+ if (mr->ops->write) {
+ return access_with_adjusted_size(addr, &data, size,
+ mr->ops->impl.min_access_size,
+ mr->ops->impl.max_access_size,
+ memory_region_write_accessor, mr,
+ attrs);
+ } else {
+ return
+ access_with_adjusted_size(addr, &data, size,
+ mr->ops->impl.min_access_size,
+ mr->ops->impl.max_access_size,
+ memory_region_write_with_attrs_accessor,
+ mr, attrs);
+ }
+}
+
+void memory_region_init_io(MemoryRegion *mr,
+ Object *owner,
+ const MemoryRegionOps *ops,
+ void *opaque,
+ const char *name,
+ uint64_t size)
+{
+ memory_region_init(mr, owner, name, size);
+ mr->ops = ops ? ops : &unassigned_mem_ops;
+ mr->opaque = opaque;
+ mr->terminates = true;
+}
+
+void memory_region_init_ram_nomigrate(MemoryRegion *mr,
+ Object *owner,
+ const char *name,
+ uint64_t size,
+ Error **errp)
+{
+ memory_region_init_ram_shared_nomigrate(mr, owner, name, size, false, errp);
+}
+
+void memory_region_init_ram_shared_nomigrate(MemoryRegion *mr,
+ Object *owner,
+ const char *name,
+ uint64_t size,
+ bool share,
+ Error **errp)
+{
+ Error *err = NULL;
+ memory_region_init(mr, owner, name, size);
+ mr->ram = true;
+ mr->terminates = true;
+ mr->destructor = memory_region_destructor_ram;
+ mr->ram_block = qemu_ram_alloc(size, share, mr, &err);
+ mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
+ if (err) {
+ mr->size = int128_zero();
+ object_unparent(OBJECT(mr));
+ error_propagate(errp, err);
+ }
+}
+
+void memory_region_init_resizeable_ram(MemoryRegion *mr,
+ Object *owner,
+ const char *name,
+ uint64_t size,
+ uint64_t max_size,
+ void (*resized)(const char*,
+ uint64_t length,
+ void *host),
+ Error **errp)
+{
+ Error *err = NULL;
+ memory_region_init(mr, owner, name, size);
+ mr->ram = true;
+ mr->terminates = true;
+ mr->destructor = memory_region_destructor_ram;
+ mr->ram_block = qemu_ram_alloc_resizeable(size, max_size, resized,
+ mr, &err);
+ mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
+ if (err) {
+ mr->size = int128_zero();
+ object_unparent(OBJECT(mr));
+ error_propagate(errp, err);
+ }
+}
+
+#ifdef CONFIG_POSIX
+void memory_region_init_ram_from_file(MemoryRegion *mr,
+ struct Object *owner,
+ const char *name,
+ uint64_t size,
+ uint64_t align,
+ uint32_t ram_flags,
+ const char *path,
+ Error **errp)
+{
+ Error *err = NULL;
+ memory_region_init(mr, owner, name, size);
+ mr->ram = true;
+ mr->terminates = true;
+ mr->destructor = memory_region_destructor_ram;
+ mr->align = align;
+ mr->ram_block = qemu_ram_alloc_from_file(size, mr, ram_flags, path, &err);
+ mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
+ if (err) {
+ mr->size = int128_zero();
+ object_unparent(OBJECT(mr));
+ error_propagate(errp, err);
+ }
+}
+
+void memory_region_init_ram_from_fd(MemoryRegion *mr,
+ struct Object *owner,
+ const char *name,
+ uint64_t size,
+ bool share,
+ int fd,
+ Error **errp)
+{
+ Error *err = NULL;
+ memory_region_init(mr, owner, name, size);
+ mr->ram = true;
+ mr->terminates = true;
+ mr->destructor = memory_region_destructor_ram;
+ mr->ram_block = qemu_ram_alloc_from_fd(size, mr,
+ share ? RAM_SHARED : 0,
+ fd, &err);
+ mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
+ if (err) {
+ mr->size = int128_zero();
+ object_unparent(OBJECT(mr));
+ error_propagate(errp, err);
+ }
+}
+#endif
+
+void memory_region_init_ram_ptr(MemoryRegion *mr,
+ Object *owner,
+ const char *name,
+ uint64_t size,
+ void *ptr)
+{
+ memory_region_init(mr, owner, name, size);
+ mr->ram = true;
+ mr->terminates = true;
+ mr->destructor = memory_region_destructor_ram;
+ mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
+
+ /* qemu_ram_alloc_from_ptr cannot fail with ptr != NULL. */
+ assert(ptr != NULL);
+ mr->ram_block = qemu_ram_alloc_from_ptr(size, ptr, mr, &error_fatal);
+}
+
+void memory_region_init_ram_device_ptr(MemoryRegion *mr,
+ Object *owner,
+ const char *name,
+ uint64_t size,
+ void *ptr)
+{
+ memory_region_init(mr, owner, name, size);
+ mr->ram = true;
+ mr->terminates = true;
+ mr->ram_device = true;
+ mr->ops = &ram_device_mem_ops;
+ mr->opaque = mr;
+ mr->destructor = memory_region_destructor_ram;
+ mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
+ /* qemu_ram_alloc_from_ptr cannot fail with ptr != NULL. */
+ assert(ptr != NULL);
+ mr->ram_block = qemu_ram_alloc_from_ptr(size, ptr, mr, &error_fatal);
+}
+
+void memory_region_init_alias(MemoryRegion *mr,
+ Object *owner,
+ const char *name,
+ MemoryRegion *orig,
+ hwaddr offset,
+ uint64_t size)
+{
+ memory_region_init(mr, owner, name, size);
+ mr->alias = orig;
+ mr->alias_offset = offset;
+}
+
+void memory_region_init_rom_nomigrate(MemoryRegion *mr,
+ struct Object *owner,
+ const char *name,
+ uint64_t size,
+ Error **errp)
+{
+ memory_region_init_ram_shared_nomigrate(mr, owner, name, size, false, errp);
+ mr->readonly = true;
+}
+
+void memory_region_init_rom_device_nomigrate(MemoryRegion *mr,
+ Object *owner,
+ const MemoryRegionOps *ops,
+ void *opaque,
+ const char *name,
+ uint64_t size,
+ Error **errp)
+{
+ Error *err = NULL;
+ assert(ops);
+ memory_region_init(mr, owner, name, size);
+ mr->ops = ops;
+ mr->opaque = opaque;
+ mr->terminates = true;
+ mr->rom_device = true;
+ mr->destructor = memory_region_destructor_ram;
+ mr->ram_block = qemu_ram_alloc(size, false, mr, &err);
+ if (err) {
+ mr->size = int128_zero();
+ object_unparent(OBJECT(mr));
+ error_propagate(errp, err);
+ }
+}
+
+void memory_region_init_iommu(void *_iommu_mr,
+ size_t instance_size,
+ const char *mrtypename,
+ Object *owner,
+ const char *name,
+ uint64_t size)
+{
+ struct IOMMUMemoryRegion *iommu_mr;
+ struct MemoryRegion *mr;
+
+ object_initialize(_iommu_mr, instance_size, mrtypename);
+ mr = MEMORY_REGION(_iommu_mr);
+ memory_region_do_init(mr, owner, name, size);
+ iommu_mr = IOMMU_MEMORY_REGION(mr);
+ mr->terminates = true; /* then re-forwards */
+ QLIST_INIT(&iommu_mr->iommu_notify);
+ iommu_mr->iommu_notify_flags = IOMMU_NOTIFIER_NONE;
+}
+
+static void memory_region_finalize(Object *obj)
+{
+ MemoryRegion *mr = MEMORY_REGION(obj);
+
+ assert(!mr->container);
+
+ /* We know the region is not visible in any address space (it
+ * does not have a container and cannot be a root either because
+ * it has no references, so we can blindly clear mr->enabled.
+ * memory_region_set_enabled instead could trigger a transaction
+ * and cause an infinite loop.
+ */
+ mr->enabled = false;
+ memory_region_transaction_begin();
+ while (!QTAILQ_EMPTY(&mr->subregions)) {
+ MemoryRegion *subregion = QTAILQ_FIRST(&mr->subregions);
+ memory_region_del_subregion(mr, subregion);
+ }
+ memory_region_transaction_commit();
+
+ mr->destructor(mr);
+ memory_region_clear_coalescing(mr);
+ g_free((char *)mr->name);
+ g_free(mr->ioeventfds);
+}
+
+Object *memory_region_owner(MemoryRegion *mr)
+{
+ Object *obj = OBJECT(mr);
+ return obj->parent;
+}
+
+void memory_region_ref(MemoryRegion *mr)
+{
+ /* MMIO callbacks most likely will access data that belongs
+ * to the owner, hence the need to ref/unref the owner whenever
+ * the memory region is in use.
+ *
+ * The memory region is a child of its owner. As long as the
+ * owner doesn't call unparent itself on the memory region,
+ * ref-ing the owner will also keep the memory region alive.
+ * Memory regions without an owner are supposed to never go away;
+ * we do not ref/unref them because it slows down DMA sensibly.
+ */
+ if (mr && mr->owner) {
+ object_ref(mr->owner);
+ }
+}
+
+void memory_region_unref(MemoryRegion *mr)
+{
+ if (mr && mr->owner) {
+ object_unref(mr->owner);
+ }
+}
+
+uint64_t memory_region_size(MemoryRegion *mr)
+{
+ if (int128_eq(mr->size, int128_2_64())) {
+ return UINT64_MAX;
+ }
+ return int128_get64(mr->size);
+}
+
+const char *memory_region_name(const MemoryRegion *mr)
+{
+ if (!mr->name) {
+ ((MemoryRegion *)mr)->name =
+ object_get_canonical_path_component(OBJECT(mr));
+ }
+ return mr->name;
+}
+
+bool memory_region_is_ram_device(MemoryRegion *mr)
+{
+ return mr->ram_device;
+}
+
+uint8_t memory_region_get_dirty_log_mask(MemoryRegion *mr)
+{
+ uint8_t mask = mr->dirty_log_mask;
+ if (global_dirty_log && mr->ram_block) {
+ mask |= (1 << DIRTY_MEMORY_MIGRATION);
+ }
+ return mask;
+}
+
+bool memory_region_is_logging(MemoryRegion *mr, uint8_t client)
+{
+ return memory_region_get_dirty_log_mask(mr) & (1 << client);
+}
+
+static int memory_region_update_iommu_notify_flags(IOMMUMemoryRegion *iommu_mr,
+ Error **errp)
+{
+ IOMMUNotifierFlag flags = IOMMU_NOTIFIER_NONE;
+ IOMMUNotifier *iommu_notifier;
+ IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr);
+ int ret = 0;
+
+ IOMMU_NOTIFIER_FOREACH(iommu_notifier, iommu_mr) {
+ flags |= iommu_notifier->notifier_flags;
+ }
+
+ if (flags != iommu_mr->iommu_notify_flags && imrc->notify_flag_changed) {
+ ret = imrc->notify_flag_changed(iommu_mr,
+ iommu_mr->iommu_notify_flags,
+ flags, errp);
+ }
+
+ if (!ret) {
+ iommu_mr->iommu_notify_flags = flags;
+ }
+ return ret;
+}
+
+int memory_region_register_iommu_notifier(MemoryRegion *mr,
+ IOMMUNotifier *n, Error **errp)
+{
+ IOMMUMemoryRegion *iommu_mr;
+ int ret;
+
+ if (mr->alias) {
+ return memory_region_register_iommu_notifier(mr->alias, n, errp);
+ }
+
+ /* We need to register for at least one bitfield */
+ iommu_mr = IOMMU_MEMORY_REGION(mr);
+ assert(n->notifier_flags != IOMMU_NOTIFIER_NONE);
+ assert(n->start <= n->end);
+ assert(n->iommu_idx >= 0 &&
+ n->iommu_idx < memory_region_iommu_num_indexes(iommu_mr));
+
+ QLIST_INSERT_HEAD(&iommu_mr->iommu_notify, n, node);
+ ret = memory_region_update_iommu_notify_flags(iommu_mr, errp);
+ if (ret) {
+ QLIST_REMOVE(n, node);
+ }
+ return ret;
+}
+
+uint64_t memory_region_iommu_get_min_page_size(IOMMUMemoryRegion *iommu_mr)
+{
+ IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr);
+
+ if (imrc->get_min_page_size) {
+ return imrc->get_min_page_size(iommu_mr);
+ }
+ return TARGET_PAGE_SIZE;
+}
+
+void memory_region_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n)
+{
+ MemoryRegion *mr = MEMORY_REGION(iommu_mr);
+ IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr);
+ hwaddr addr, granularity;
+ IOMMUTLBEntry iotlb;
+
+ /* If the IOMMU has its own replay callback, override */
+ if (imrc->replay) {
+ imrc->replay(iommu_mr, n);
+ return;
+ }
+
+ granularity = memory_region_iommu_get_min_page_size(iommu_mr);
+
+ for (addr = 0; addr < memory_region_size(mr); addr += granularity) {
+ iotlb = imrc->translate(iommu_mr, addr, IOMMU_NONE, n->iommu_idx);
+ if (iotlb.perm != IOMMU_NONE) {
+ n->notify(n, &iotlb);
+ }
+
+ /* if (2^64 - MR size) < granularity, it's possible to get an
+ * infinite loop here. This should catch such a wraparound */
+ if ((addr + granularity) < addr) {
+ break;
+ }
+ }
+}
+
+void memory_region_unregister_iommu_notifier(MemoryRegion *mr,
+ IOMMUNotifier *n)
+{
+ IOMMUMemoryRegion *iommu_mr;
+
+ if (mr->alias) {
+ memory_region_unregister_iommu_notifier(mr->alias, n);
+ return;
+ }
+ QLIST_REMOVE(n, node);
+ iommu_mr = IOMMU_MEMORY_REGION(mr);
+ memory_region_update_iommu_notify_flags(iommu_mr, NULL);
+}
+
+void memory_region_notify_one(IOMMUNotifier *notifier,
+ IOMMUTLBEntry *entry)
+{
+ IOMMUNotifierFlag request_flags;
+ hwaddr entry_end = entry->iova + entry->addr_mask;
+
+ /*
+ * Skip the notification if the notification does not overlap
+ * with registered range.
+ */
+ if (notifier->start > entry_end || notifier->end < entry->iova) {
+ return;
+ }
+
+ assert(entry->iova >= notifier->start && entry_end <= notifier->end);
+
+ if (entry->perm & IOMMU_RW) {
+ request_flags = IOMMU_NOTIFIER_MAP;
+ } else {
+ request_flags = IOMMU_NOTIFIER_UNMAP;
+ }
+
+ if (notifier->notifier_flags & request_flags) {
+ notifier->notify(notifier, entry);
+ }
+}
+
+void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr,
+ int iommu_idx,
+ IOMMUTLBEntry entry)
+{
+ IOMMUNotifier *iommu_notifier;
+
+ assert(memory_region_is_iommu(MEMORY_REGION(iommu_mr)));
+
+ IOMMU_NOTIFIER_FOREACH(iommu_notifier, iommu_mr) {
+ if (iommu_notifier->iommu_idx == iommu_idx) {
+ memory_region_notify_one(iommu_notifier, &entry);
+ }
+ }
+}
+
+int memory_region_iommu_get_attr(IOMMUMemoryRegion *iommu_mr,
+ enum IOMMUMemoryRegionAttr attr,
+ void *data)
+{
+ IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr);
+
+ if (!imrc->get_attr) {
+ return -EINVAL;
+ }
+
+ return imrc->get_attr(iommu_mr, attr, data);
+}
+
+int memory_region_iommu_attrs_to_index(IOMMUMemoryRegion *iommu_mr,
+ MemTxAttrs attrs)
+{
+ IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr);
+
+ if (!imrc->attrs_to_index) {
+ return 0;
+ }
+
+ return imrc->attrs_to_index(iommu_mr, attrs);
+}
+
+int memory_region_iommu_num_indexes(IOMMUMemoryRegion *iommu_mr)
+{
+ IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr);
+
+ if (!imrc->num_indexes) {
+ return 1;
+ }
+
+ return imrc->num_indexes(iommu_mr);
+}
+
+void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client)
+{
+ uint8_t mask = 1 << client;
+ uint8_t old_logging;
+
+ assert(client == DIRTY_MEMORY_VGA);
+ old_logging = mr->vga_logging_count;
+ mr->vga_logging_count += log ? 1 : -1;
+ if (!!old_logging == !!mr->vga_logging_count) {
+ return;
+ }
+
+ memory_region_transaction_begin();
+ mr->dirty_log_mask = (mr->dirty_log_mask & ~mask) | (log * mask);
+ memory_region_update_pending |= mr->enabled;
+ memory_region_transaction_commit();
+}
+
+void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr,
+ hwaddr size)
+{
+ assert(mr->ram_block);
+ cpu_physical_memory_set_dirty_range(memory_region_get_ram_addr(mr) + addr,
+ size,
+ memory_region_get_dirty_log_mask(mr));
+}
+
+static void memory_region_sync_dirty_bitmap(MemoryRegion *mr)
+{
+ MemoryListener *listener;
+ AddressSpace *as;
+ FlatView *view;
+ FlatRange *fr;
+
+ /* If the same address space has multiple log_sync listeners, we
+ * visit that address space's FlatView multiple times. But because
+ * log_sync listeners are rare, it's still cheaper than walking each
+ * address space once.
+ */
+ QTAILQ_FOREACH(listener, &memory_listeners, link) {
+ if (!listener->log_sync) {
+ continue;
+ }
+ as = listener->address_space;
+ view = address_space_get_flatview(as);
+ FOR_EACH_FLAT_RANGE(fr, view) {
+ if (fr->dirty_log_mask && (!mr || fr->mr == mr)) {
+ MemoryRegionSection mrs = section_from_flat_range(fr, view);
+ listener->log_sync(listener, &mrs);
+ }
+ }
+ flatview_unref(view);
+ }
+}
+
+void memory_region_clear_dirty_bitmap(MemoryRegion *mr, hwaddr start,
+ hwaddr len)
+{
+ MemoryRegionSection mrs;
+ MemoryListener *listener;
+ AddressSpace *as;
+ FlatView *view;
+ FlatRange *fr;
+ hwaddr sec_start, sec_end, sec_size;
+
+ QTAILQ_FOREACH(listener, &memory_listeners, link) {
+ if (!listener->log_clear) {
+ continue;
+ }
+ as = listener->address_space;
+ view = address_space_get_flatview(as);
+ FOR_EACH_FLAT_RANGE(fr, view) {
+ if (!fr->dirty_log_mask || fr->mr != mr) {
+ /*
+ * Clear dirty bitmap operation only applies to those
+ * regions whose dirty logging is at least enabled
+ */
+ continue;
+ }
+
+ mrs = section_from_flat_range(fr, view);
+
+ sec_start = MAX(mrs.offset_within_region, start);
+ sec_end = mrs.offset_within_region + int128_get64(mrs.size);
+ sec_end = MIN(sec_end, start + len);
+
+ if (sec_start >= sec_end) {
+ /*
+ * If this memory region section has no intersection
+ * with the requested range, skip.
+ */
+ continue;
+ }
+
+ /* Valid case; shrink the section if needed */
+ mrs.offset_within_address_space +=
+ sec_start - mrs.offset_within_region;
+ mrs.offset_within_region = sec_start;
+ sec_size = sec_end - sec_start;
+ mrs.size = int128_make64(sec_size);
+ listener->log_clear(listener, &mrs);
+ }
+ flatview_unref(view);
+ }
+}
+
+DirtyBitmapSnapshot *memory_region_snapshot_and_clear_dirty(MemoryRegion *mr,
+ hwaddr addr,
+ hwaddr size,
+ unsigned client)
+{
+ DirtyBitmapSnapshot *snapshot;
+ assert(mr->ram_block);
+ memory_region_sync_dirty_bitmap(mr);
+ snapshot = cpu_physical_memory_snapshot_and_clear_dirty(mr, addr, size, client);
+ memory_global_after_dirty_log_sync();
+ return snapshot;
+}
+
+bool memory_region_snapshot_get_dirty(MemoryRegion *mr, DirtyBitmapSnapshot *snap,
+ hwaddr addr, hwaddr size)
+{
+ assert(mr->ram_block);
+ return cpu_physical_memory_snapshot_get_dirty(snap,
+ memory_region_get_ram_addr(mr) + addr, size);
+}
+
+void memory_region_set_readonly(MemoryRegion *mr, bool readonly)
+{
+ if (mr->readonly != readonly) {
+ memory_region_transaction_begin();
+ mr->readonly = readonly;
+ memory_region_update_pending |= mr->enabled;
+ memory_region_transaction_commit();
+ }
+}
+
+void memory_region_set_nonvolatile(MemoryRegion *mr, bool nonvolatile)
+{
+ if (mr->nonvolatile != nonvolatile) {
+ memory_region_transaction_begin();
+ mr->nonvolatile = nonvolatile;
+ memory_region_update_pending |= mr->enabled;
+ memory_region_transaction_commit();
+ }
+}
+
+void memory_region_rom_device_set_romd(MemoryRegion *mr, bool romd_mode)
+{
+ if (mr->romd_mode != romd_mode) {
+ memory_region_transaction_begin();
+ mr->romd_mode = romd_mode;
+ memory_region_update_pending |= mr->enabled;
+ memory_region_transaction_commit();
+ }
+}
+
+void memory_region_reset_dirty(MemoryRegion *mr, hwaddr addr,
+ hwaddr size, unsigned client)
+{
+ assert(mr->ram_block);
+ cpu_physical_memory_test_and_clear_dirty(
+ memory_region_get_ram_addr(mr) + addr, size, client);
+}
+
+int memory_region_get_fd(MemoryRegion *mr)
+{
+ int fd;
+
+ RCU_READ_LOCK_GUARD();
+ while (mr->alias) {
+ mr = mr->alias;
+ }
+ fd = mr->ram_block->fd;
+
+ return fd;
+}
+
+void *memory_region_get_ram_ptr(MemoryRegion *mr)
+{
+ void *ptr;
+ uint64_t offset = 0;
+
+ RCU_READ_LOCK_GUARD();
+ while (mr->alias) {
+ offset += mr->alias_offset;
+ mr = mr->alias;
+ }
+ assert(mr->ram_block);
+ ptr = qemu_map_ram_ptr(mr->ram_block, offset);
+
+ return ptr;
+}
+
+MemoryRegion *memory_region_from_host(void *ptr, ram_addr_t *offset)
+{
+ RAMBlock *block;
+
+ block = qemu_ram_block_from_host(ptr, false, offset);
+ if (!block) {
+ return NULL;
+ }
+
+ return block->mr;
+}
+
+ram_addr_t memory_region_get_ram_addr(MemoryRegion *mr)
+{
+ return mr->ram_block ? mr->ram_block->offset : RAM_ADDR_INVALID;
+}
+
+void memory_region_ram_resize(MemoryRegion *mr, ram_addr_t newsize, Error **errp)
+{
+ assert(mr->ram_block);
+
+ qemu_ram_resize(mr->ram_block, newsize, errp);
+}
+
+void memory_region_msync(MemoryRegion *mr, hwaddr addr, hwaddr size)
+{
+ if (mr->ram_block) {
+ qemu_ram_msync(mr->ram_block, addr, size);
+ }
+}
+
+void memory_region_writeback(MemoryRegion *mr, hwaddr addr, hwaddr size)
+{
+ /*
+ * Might be extended case needed to cover
+ * different types of memory regions
+ */
+ if (mr->dirty_log_mask) {
+ memory_region_msync(mr, addr, size);
+ }
+}
+
+/*
+ * Call proper memory listeners about the change on the newly
+ * added/removed CoalescedMemoryRange.
+ */
+static void memory_region_update_coalesced_range(MemoryRegion *mr,
+ CoalescedMemoryRange *cmr,
+ bool add)
+{
+ AddressSpace *as;
+ FlatView *view;
+ FlatRange *fr;
+
+ QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
+ view = address_space_get_flatview(as);
+ FOR_EACH_FLAT_RANGE(fr, view) {
+ if (fr->mr == mr) {
+ flat_range_coalesced_io_notify(fr, as, cmr, add);
+ }
+ }
+ flatview_unref(view);
+ }
+}
+
+void memory_region_set_coalescing(MemoryRegion *mr)
+{
+ memory_region_clear_coalescing(mr);
+ memory_region_add_coalescing(mr, 0, int128_get64(mr->size));
+}
+
+void memory_region_add_coalescing(MemoryRegion *mr,
+ hwaddr offset,
+ uint64_t size)
+{
+ CoalescedMemoryRange *cmr = g_malloc(sizeof(*cmr));
+
+ cmr->addr = addrrange_make(int128_make64(offset), int128_make64(size));
+ QTAILQ_INSERT_TAIL(&mr->coalesced, cmr, link);
+ memory_region_update_coalesced_range(mr, cmr, true);
+ memory_region_set_flush_coalesced(mr);
+}
+
+void memory_region_clear_coalescing(MemoryRegion *mr)
+{
+ CoalescedMemoryRange *cmr;
+
+ if (QTAILQ_EMPTY(&mr->coalesced)) {
+ return;
+ }
+
+ qemu_flush_coalesced_mmio_buffer();
+ mr->flush_coalesced_mmio = false;
+
+ while (!QTAILQ_EMPTY(&mr->coalesced)) {
+ cmr = QTAILQ_FIRST(&mr->coalesced);
+ QTAILQ_REMOVE(&mr->coalesced, cmr, link);
+ memory_region_update_coalesced_range(mr, cmr, false);
+ g_free(cmr);
+ }
+}
+
+void memory_region_set_flush_coalesced(MemoryRegion *mr)
+{
+ mr->flush_coalesced_mmio = true;
+}
+
+void memory_region_clear_flush_coalesced(MemoryRegion *mr)
+{
+ qemu_flush_coalesced_mmio_buffer();
+ if (QTAILQ_EMPTY(&mr->coalesced)) {
+ mr->flush_coalesced_mmio = false;
+ }
+}
+
+void memory_region_clear_global_locking(MemoryRegion *mr)
+{
+ mr->global_locking = false;
+}
+
+static bool userspace_eventfd_warning;
+
+void memory_region_add_eventfd(MemoryRegion *mr,
+ hwaddr addr,
+ unsigned size,
+ bool match_data,
+ uint64_t data,
+ EventNotifier *e)
+{
+ MemoryRegionIoeventfd mrfd = {
+ .addr.start = int128_make64(addr),
+ .addr.size = int128_make64(size),
+ .match_data = match_data,
+ .data = data,
+ .e = e,
+ };
+ unsigned i;
+
+ if (kvm_enabled() && (!(kvm_eventfds_enabled() ||
+ userspace_eventfd_warning))) {
+ userspace_eventfd_warning = true;
+ error_report("Using eventfd without MMIO binding in KVM. "
+ "Suboptimal performance expected");
+ }
+
+ if (size) {
+ adjust_endianness(mr, &mrfd.data, size_memop(size) | MO_TE);
+ }
+ memory_region_transaction_begin();
+ for (i = 0; i < mr->ioeventfd_nb; ++i) {
+ if (memory_region_ioeventfd_before(&mrfd, &mr->ioeventfds[i])) {
+ break;
+ }
+ }
+ ++mr->ioeventfd_nb;
+ mr->ioeventfds = g_realloc(mr->ioeventfds,
+ sizeof(*mr->ioeventfds) * mr->ioeventfd_nb);
+ memmove(&mr->ioeventfds[i+1], &mr->ioeventfds[i],
+ sizeof(*mr->ioeventfds) * (mr->ioeventfd_nb-1 - i));
+ mr->ioeventfds[i] = mrfd;
+ ioeventfd_update_pending |= mr->enabled;
+ memory_region_transaction_commit();
+}
+
+void memory_region_del_eventfd(MemoryRegion *mr,
+ hwaddr addr,
+ unsigned size,
+ bool match_data,
+ uint64_t data,
+ EventNotifier *e)
+{
+ MemoryRegionIoeventfd mrfd = {
+ .addr.start = int128_make64(addr),
+ .addr.size = int128_make64(size),
+ .match_data = match_data,
+ .data = data,
+ .e = e,
+ };
+ unsigned i;
+
+ if (size) {
+ adjust_endianness(mr, &mrfd.data, size_memop(size) | MO_TE);
+ }
+ memory_region_transaction_begin();
+ for (i = 0; i < mr->ioeventfd_nb; ++i) {
+ if (memory_region_ioeventfd_equal(&mrfd, &mr->ioeventfds[i])) {
+ break;
+ }
+ }
+ assert(i != mr->ioeventfd_nb);
+ memmove(&mr->ioeventfds[i], &mr->ioeventfds[i+1],
+ sizeof(*mr->ioeventfds) * (mr->ioeventfd_nb - (i+1)));
+ --mr->ioeventfd_nb;
+ mr->ioeventfds = g_realloc(mr->ioeventfds,
+ sizeof(*mr->ioeventfds)*mr->ioeventfd_nb + 1);
+ ioeventfd_update_pending |= mr->enabled;
+ memory_region_transaction_commit();
+}
+
+static void memory_region_update_container_subregions(MemoryRegion *subregion)
+{
+ MemoryRegion *mr = subregion->container;
+ MemoryRegion *other;
+
+ memory_region_transaction_begin();
+
+ memory_region_ref(subregion);
+ QTAILQ_FOREACH(other, &mr->subregions, subregions_link) {
+ if (subregion->priority >= other->priority) {
+ QTAILQ_INSERT_BEFORE(other, subregion, subregions_link);
+ goto done;
+ }
+ }
+ QTAILQ_INSERT_TAIL(&mr->subregions, subregion, subregions_link);
+done:
+ memory_region_update_pending |= mr->enabled && subregion->enabled;
+ memory_region_transaction_commit();
+}
+
+static void memory_region_add_subregion_common(MemoryRegion *mr,
+ hwaddr offset,
+ MemoryRegion *subregion)
+{
+ assert(!subregion->container);
+ subregion->container = mr;
+ subregion->addr = offset;
+ memory_region_update_container_subregions(subregion);
+}
+
+void memory_region_add_subregion(MemoryRegion *mr,
+ hwaddr offset,
+ MemoryRegion *subregion)
+{
+ subregion->priority = 0;
+ memory_region_add_subregion_common(mr, offset, subregion);
+}
+
+void memory_region_add_subregion_overlap(MemoryRegion *mr,
+ hwaddr offset,
+ MemoryRegion *subregion,
+ int priority)
+{
+ subregion->priority = priority;
+ memory_region_add_subregion_common(mr, offset, subregion);
+}
+
+void memory_region_del_subregion(MemoryRegion *mr,
+ MemoryRegion *subregion)
+{
+ memory_region_transaction_begin();
+ assert(subregion->container == mr);
+ subregion->container = NULL;
+ QTAILQ_REMOVE(&mr->subregions, subregion, subregions_link);
+ memory_region_unref(subregion);
+ memory_region_update_pending |= mr->enabled && subregion->enabled;
+ memory_region_transaction_commit();
+}
+
+void memory_region_set_enabled(MemoryRegion *mr, bool enabled)
+{
+ if (enabled == mr->enabled) {
+ return;
+ }
+ memory_region_transaction_begin();
+ mr->enabled = enabled;
+ memory_region_update_pending = true;
+ memory_region_transaction_commit();
+}
+
+void memory_region_set_size(MemoryRegion *mr, uint64_t size)
+{
+ Int128 s = int128_make64(size);
+
+ if (size == UINT64_MAX) {
+ s = int128_2_64();
+ }
+ if (int128_eq(s, mr->size)) {
+ return;
+ }
+ memory_region_transaction_begin();
+ mr->size = s;
+ memory_region_update_pending = true;
+ memory_region_transaction_commit();
+}
+
+static void memory_region_readd_subregion(MemoryRegion *mr)
+{
+ MemoryRegion *container = mr->container;
+
+ if (container) {
+ memory_region_transaction_begin();
+ memory_region_ref(mr);
+ memory_region_del_subregion(container, mr);
+ mr->container = container;
+ memory_region_update_container_subregions(mr);
+ memory_region_unref(mr);
+ memory_region_transaction_commit();
+ }
+}
+
+void memory_region_set_address(MemoryRegion *mr, hwaddr addr)
+{
+ if (addr != mr->addr) {
+ mr->addr = addr;
+ memory_region_readd_subregion(mr);
+ }
+}
+
+void memory_region_set_alias_offset(MemoryRegion *mr, hwaddr offset)
+{
+ assert(mr->alias);
+
+ if (offset == mr->alias_offset) {
+ return;
+ }
+
+ memory_region_transaction_begin();
+ mr->alias_offset = offset;
+ memory_region_update_pending |= mr->enabled;
+ memory_region_transaction_commit();
+}
+
+uint64_t memory_region_get_alignment(const MemoryRegion *mr)
+{
+ return mr->align;
+}
+
+static int cmp_flatrange_addr(const void *addr_, const void *fr_)
+{
+ const AddrRange *addr = addr_;
+ const FlatRange *fr = fr_;
+
+ if (int128_le(addrrange_end(*addr), fr->addr.start)) {
+ return -1;
+ } else if (int128_ge(addr->start, addrrange_end(fr->addr))) {
+ return 1;
+ }
+ return 0;
+}
+
+static FlatRange *flatview_lookup(FlatView *view, AddrRange addr)
+{
+ return bsearch(&addr, view->ranges, view->nr,
+ sizeof(FlatRange), cmp_flatrange_addr);
+}
+
+bool memory_region_is_mapped(MemoryRegion *mr)
+{
+ return mr->container ? true : false;
+}
+
+/* Same as memory_region_find, but it does not add a reference to the
+ * returned region. It must be called from an RCU critical section.
+ */
+static MemoryRegionSection memory_region_find_rcu(MemoryRegion *mr,
+ hwaddr addr, uint64_t size)
+{
+ MemoryRegionSection ret = { .mr = NULL };
+ MemoryRegion *root;
+ AddressSpace *as;
+ AddrRange range;
+ FlatView *view;
+ FlatRange *fr;
+
+ addr += mr->addr;
+ for (root = mr; root->container; ) {
+ root = root->container;
+ addr += root->addr;
+ }
+
+ as = memory_region_to_address_space(root);
+ if (!as) {
+ return ret;
+ }
+ range = addrrange_make(int128_make64(addr), int128_make64(size));
+
+ view = address_space_to_flatview(as);
+ fr = flatview_lookup(view, range);
+ if (!fr) {
+ return ret;
+ }
+
+ while (fr > view->ranges && addrrange_intersects(fr[-1].addr, range)) {
+ --fr;
+ }
+
+ ret.mr = fr->mr;
+ ret.fv = view;
+ range = addrrange_intersection(range, fr->addr);
+ ret.offset_within_region = fr->offset_in_region;
+ ret.offset_within_region += int128_get64(int128_sub(range.start,
+ fr->addr.start));
+ ret.size = range.size;
+ ret.offset_within_address_space = int128_get64(range.start);
+ ret.readonly = fr->readonly;
+ ret.nonvolatile = fr->nonvolatile;
+ return ret;
+}
+
+MemoryRegionSection memory_region_find(MemoryRegion *mr,
+ hwaddr addr, uint64_t size)
+{
+ MemoryRegionSection ret;
+ RCU_READ_LOCK_GUARD();
+ ret = memory_region_find_rcu(mr, addr, size);
+ if (ret.mr) {
+ memory_region_ref(ret.mr);
+ }
+ return ret;
+}
+
+bool memory_region_present(MemoryRegion *container, hwaddr addr)
+{
+ MemoryRegion *mr;
+
+ RCU_READ_LOCK_GUARD();
+ mr = memory_region_find_rcu(container, addr, 1).mr;
+ return mr && mr != container;
+}
+
+void memory_global_dirty_log_sync(void)
+{
+ memory_region_sync_dirty_bitmap(NULL);
+}
+
+void memory_global_after_dirty_log_sync(void)
+{
+ MEMORY_LISTENER_CALL_GLOBAL(log_global_after_sync, Forward);
+}
+
+static VMChangeStateEntry *vmstate_change;
+
+void memory_global_dirty_log_start(void)
+{
+ if (vmstate_change) {
+ qemu_del_vm_change_state_handler(vmstate_change);
+ vmstate_change = NULL;
+ }
+
+ global_dirty_log = true;
+
+ MEMORY_LISTENER_CALL_GLOBAL(log_global_start, Forward);
+
+ /* Refresh DIRTY_MEMORY_MIGRATION bit. */
+ memory_region_transaction_begin();
+ memory_region_update_pending = true;
+ memory_region_transaction_commit();
+}
+
+static void memory_global_dirty_log_do_stop(void)
+{
+ global_dirty_log = false;
+
+ /* Refresh DIRTY_MEMORY_MIGRATION bit. */
+ memory_region_transaction_begin();
+ memory_region_update_pending = true;
+ memory_region_transaction_commit();
+
+ MEMORY_LISTENER_CALL_GLOBAL(log_global_stop, Reverse);
+}
+
+static void memory_vm_change_state_handler(void *opaque, int running,
+ RunState state)
+{
+ if (running) {
+ memory_global_dirty_log_do_stop();
+
+ if (vmstate_change) {
+ qemu_del_vm_change_state_handler(vmstate_change);
+ vmstate_change = NULL;
+ }
+ }
+}
+
+void memory_global_dirty_log_stop(void)
+{
+ if (!runstate_is_running()) {
+ if (vmstate_change) {
+ return;
+ }
+ vmstate_change = qemu_add_vm_change_state_handler(
+ memory_vm_change_state_handler, NULL);
+ return;
+ }
+
+ memory_global_dirty_log_do_stop();
+}
+
+static void listener_add_address_space(MemoryListener *listener,
+ AddressSpace *as)
+{
+ FlatView *view;
+ FlatRange *fr;
+
+ if (listener->begin) {
+ listener->begin(listener);
+ }
+ if (global_dirty_log) {
+ if (listener->log_global_start) {
+ listener->log_global_start(listener);
+ }
+ }
+
+ view = address_space_get_flatview(as);
+ FOR_EACH_FLAT_RANGE(fr, view) {
+ MemoryRegionSection section = section_from_flat_range(fr, view);
+
+ if (listener->region_add) {
+ listener->region_add(listener, §ion);
+ }
+ if (fr->dirty_log_mask && listener->log_start) {
+ listener->log_start(listener, §ion, 0, fr->dirty_log_mask);
+ }
+ }
+ if (listener->commit) {
+ listener->commit(listener);
+ }
+ flatview_unref(view);
+}
+
+static void listener_del_address_space(MemoryListener *listener,
+ AddressSpace *as)
+{
+ FlatView *view;
+ FlatRange *fr;
+
+ if (listener->begin) {
+ listener->begin(listener);
+ }
+ view = address_space_get_flatview(as);
+ FOR_EACH_FLAT_RANGE(fr, view) {
+ MemoryRegionSection section = section_from_flat_range(fr, view);
+
+ if (fr->dirty_log_mask && listener->log_stop) {
+ listener->log_stop(listener, §ion, fr->dirty_log_mask, 0);
+ }
+ if (listener->region_del) {
+ listener->region_del(listener, §ion);
+ }
+ }
+ if (listener->commit) {
+ listener->commit(listener);
+ }
+ flatview_unref(view);
+}
+
+void memory_listener_register(MemoryListener *listener, AddressSpace *as)
+{
+ MemoryListener *other = NULL;
+
+ listener->address_space = as;
+ if (QTAILQ_EMPTY(&memory_listeners)
+ || listener->priority >= QTAILQ_LAST(&memory_listeners)->priority) {
+ QTAILQ_INSERT_TAIL(&memory_listeners, listener, link);
+ } else {
+ QTAILQ_FOREACH(other, &memory_listeners, link) {
+ if (listener->priority < other->priority) {
+ break;
+ }
+ }
+ QTAILQ_INSERT_BEFORE(other, listener, link);
+ }
+
+ if (QTAILQ_EMPTY(&as->listeners)
+ || listener->priority >= QTAILQ_LAST(&as->listeners)->priority) {
+ QTAILQ_INSERT_TAIL(&as->listeners, listener, link_as);
+ } else {
+ QTAILQ_FOREACH(other, &as->listeners, link_as) {
+ if (listener->priority < other->priority) {
+ break;
+ }
+ }
+ QTAILQ_INSERT_BEFORE(other, listener, link_as);
+ }
+
+ listener_add_address_space(listener, as);
+}
+
+void memory_listener_unregister(MemoryListener *listener)
+{
+ if (!listener->address_space) {
+ return;
+ }
+
+ listener_del_address_space(listener, listener->address_space);
+ QTAILQ_REMOVE(&memory_listeners, listener, link);
+ QTAILQ_REMOVE(&listener->address_space->listeners, listener, link_as);
+ listener->address_space = NULL;
+}
+
+void address_space_remove_listeners(AddressSpace *as)
+{
+ while (!QTAILQ_EMPTY(&as->listeners)) {
+ memory_listener_unregister(QTAILQ_FIRST(&as->listeners));
+ }
+}
+
+void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name)
+{
+ memory_region_ref(root);
+ as->root = root;
+ as->current_map = NULL;
+ as->ioeventfd_nb = 0;
+ as->ioeventfds = NULL;
+ QTAILQ_INIT(&as->listeners);
+ QTAILQ_INSERT_TAIL(&address_spaces, as, address_spaces_link);
+ as->name = g_strdup(name ? name : "anonymous");
+ address_space_update_topology(as);
+ address_space_update_ioeventfds(as);
+}
+
+static void do_address_space_destroy(AddressSpace *as)
+{
+ assert(QTAILQ_EMPTY(&as->listeners));
+
+ flatview_unref(as->current_map);
+ g_free(as->name);
+ g_free(as->ioeventfds);
+ memory_region_unref(as->root);
+}
+
+void address_space_destroy(AddressSpace *as)
+{
+ MemoryRegion *root = as->root;
+
+ /* Flush out anything from MemoryListeners listening in on this */
+ memory_region_transaction_begin();
+ as->root = NULL;
+ memory_region_transaction_commit();
+ QTAILQ_REMOVE(&address_spaces, as, address_spaces_link);
+
+ /* At this point, as->dispatch and as->current_map are dummy
+ * entries that the guest should never use. Wait for the old
+ * values to expire before freeing the data.
+ */
+ as->root = root;
+ call_rcu(as, do_address_space_destroy, rcu);
+}
+
+static const char *memory_region_type(MemoryRegion *mr)
+{
+ if (mr->alias) {
+ return memory_region_type(mr->alias);
+ }
+ if (memory_region_is_ram_device(mr)) {
+ return "ramd";
+ } else if (memory_region_is_romd(mr)) {
+ return "romd";
+ } else if (memory_region_is_rom(mr)) {
+ return "rom";
+ } else if (memory_region_is_ram(mr)) {
+ return "ram";
+ } else {
+ return "i/o";
+ }
+}
+
+typedef struct MemoryRegionList MemoryRegionList;
+
+struct MemoryRegionList {
+ const MemoryRegion *mr;
+ QTAILQ_ENTRY(MemoryRegionList) mrqueue;
+};
+
+typedef QTAILQ_HEAD(, MemoryRegionList) MemoryRegionListHead;
+
+#define MR_SIZE(size) (int128_nz(size) ? (hwaddr)int128_get64( \
+ int128_sub((size), int128_one())) : 0)
+#define MTREE_INDENT " "
+
+static void mtree_expand_owner(const char *label, Object *obj)
+{
+ DeviceState *dev = (DeviceState *) object_dynamic_cast(obj, TYPE_DEVICE);
+
+ qemu_printf(" %s:{%s", label, dev ? "dev" : "obj");
+ if (dev && dev->id) {
+ qemu_printf(" id=%s", dev->id);
+ } else {
+ char *canonical_path = object_get_canonical_path(obj);
+ if (canonical_path) {
+ qemu_printf(" path=%s", canonical_path);
+ g_free(canonical_path);
+ } else {
+ qemu_printf(" type=%s", object_get_typename(obj));
+ }
+ }
+ qemu_printf("}");
+}
+
+static void mtree_print_mr_owner(const MemoryRegion *mr)
+{
+ Object *owner = mr->owner;
+ Object *parent = memory_region_owner((MemoryRegion *)mr);
+
+ if (!owner && !parent) {
+ qemu_printf(" orphan");
+ return;
+ }
+ if (owner) {
+ mtree_expand_owner("owner", owner);
+ }
+ if (parent && parent != owner) {
+ mtree_expand_owner("parent", parent);
+ }
+}
+
+static void mtree_print_mr(const MemoryRegion *mr, unsigned int level,
+ hwaddr base,
+ MemoryRegionListHead *alias_print_queue,
+ bool owner, bool display_disabled)
+{
+ MemoryRegionList *new_ml, *ml, *next_ml;
+ MemoryRegionListHead submr_print_queue;
+ const MemoryRegion *submr;
+ unsigned int i;
+ hwaddr cur_start, cur_end;
+
+ if (!mr) {
+ return;
+ }
+
+ cur_start = base + mr->addr;
+ cur_end = cur_start + MR_SIZE(mr->size);
+
+ /*
+ * Try to detect overflow of memory region. This should never
+ * happen normally. When it happens, we dump something to warn the
+ * user who is observing this.
+ */
+ if (cur_start < base || cur_end < cur_start) {
+ qemu_printf("[DETECTED OVERFLOW!] ");
+ }
+
+ if (mr->alias) {
+ MemoryRegionList *ml;
+ bool found = false;
+
+ /* check if the alias is already in the queue */
+ QTAILQ_FOREACH(ml, alias_print_queue, mrqueue) {
+ if (ml->mr == mr->alias) {
+ found = true;
+ }
+ }
+
+ if (!found) {
+ ml = g_new(MemoryRegionList, 1);
+ ml->mr = mr->alias;
+ QTAILQ_INSERT_TAIL(alias_print_queue, ml, mrqueue);
+ }
+ if (mr->enabled || display_disabled) {
+ for (i = 0; i < level; i++) {
+ qemu_printf(MTREE_INDENT);
+ }
+ qemu_printf(TARGET_FMT_plx "-" TARGET_FMT_plx
+ " (prio %d, %s%s): alias %s @%s " TARGET_FMT_plx
+ "-" TARGET_FMT_plx "%s",
+ cur_start, cur_end,
+ mr->priority,
+ mr->nonvolatile ? "nv-" : "",
+ memory_region_type((MemoryRegion *)mr),
+ memory_region_name(mr),
+ memory_region_name(mr->alias),
+ mr->alias_offset,
+ mr->alias_offset + MR_SIZE(mr->size),
+ mr->enabled ? "" : " [disabled]");
+ if (owner) {
+ mtree_print_mr_owner(mr);
+ }
+ qemu_printf("\n");
+ }
+ } else {
+ if (mr->enabled || display_disabled) {
+ for (i = 0; i < level; i++) {
+ qemu_printf(MTREE_INDENT);
+ }
+ qemu_printf(TARGET_FMT_plx "-" TARGET_FMT_plx
+ " (prio %d, %s%s): %s%s",
+ cur_start, cur_end,
+ mr->priority,
+ mr->nonvolatile ? "nv-" : "",
+ memory_region_type((MemoryRegion *)mr),
+ memory_region_name(mr),
+ mr->enabled ? "" : " [disabled]");
+ if (owner) {
+ mtree_print_mr_owner(mr);
+ }
+ qemu_printf("\n");
+ }
+ }
+
+ QTAILQ_INIT(&submr_print_queue);
+
+ QTAILQ_FOREACH(submr, &mr->subregions, subregions_link) {
+ new_ml = g_new(MemoryRegionList, 1);
+ new_ml->mr = submr;
+ QTAILQ_FOREACH(ml, &submr_print_queue, mrqueue) {
+ if (new_ml->mr->addr < ml->mr->addr ||
+ (new_ml->mr->addr == ml->mr->addr &&
+ new_ml->mr->priority > ml->mr->priority)) {
+ QTAILQ_INSERT_BEFORE(ml, new_ml, mrqueue);
+ new_ml = NULL;
+ break;
+ }
+ }
+ if (new_ml) {
+ QTAILQ_INSERT_TAIL(&submr_print_queue, new_ml, mrqueue);
+ }
+ }
+
+ QTAILQ_FOREACH(ml, &submr_print_queue, mrqueue) {
+ mtree_print_mr(ml->mr, level + 1, cur_start,
+ alias_print_queue, owner, display_disabled);
+ }
+
+ QTAILQ_FOREACH_SAFE(ml, &submr_print_queue, mrqueue, next_ml) {
+ g_free(ml);
+ }
+}
+
+struct FlatViewInfo {
+ int counter;
+ bool dispatch_tree;
+ bool owner;
+ AccelClass *ac;
+};
+
+static void mtree_print_flatview(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ FlatView *view = key;
+ GArray *fv_address_spaces = value;
+ struct FlatViewInfo *fvi = user_data;
+ FlatRange *range = &view->ranges[0];
+ MemoryRegion *mr;
+ int n = view->nr;
+ int i;
+ AddressSpace *as;
+
+ qemu_printf("FlatView #%d\n", fvi->counter);
+ ++fvi->counter;
+
+ for (i = 0; i < fv_address_spaces->len; ++i) {
+ as = g_array_index(fv_address_spaces, AddressSpace*, i);
+ qemu_printf(" AS \"%s\", root: %s",
+ as->name, memory_region_name(as->root));
+ if (as->root->alias) {
+ qemu_printf(", alias %s", memory_region_name(as->root->alias));
+ }
+ qemu_printf("\n");
+ }
+
+ qemu_printf(" Root memory region: %s\n",
+ view->root ? memory_region_name(view->root) : "(none)");
+
+ if (n <= 0) {
+ qemu_printf(MTREE_INDENT "No rendered FlatView\n\n");
+ return;
+ }
+
+ while (n--) {
+ mr = range->mr;
+ if (range->offset_in_region) {
+ qemu_printf(MTREE_INDENT TARGET_FMT_plx "-" TARGET_FMT_plx
+ " (prio %d, %s%s): %s @" TARGET_FMT_plx,
+ int128_get64(range->addr.start),
+ int128_get64(range->addr.start)
+ + MR_SIZE(range->addr.size),
+ mr->priority,
+ range->nonvolatile ? "nv-" : "",
+ range->readonly ? "rom" : memory_region_type(mr),
+ memory_region_name(mr),
+ range->offset_in_region);
+ } else {
+ qemu_printf(MTREE_INDENT TARGET_FMT_plx "-" TARGET_FMT_plx
+ " (prio %d, %s%s): %s",
+ int128_get64(range->addr.start),
+ int128_get64(range->addr.start)
+ + MR_SIZE(range->addr.size),
+ mr->priority,
+ range->nonvolatile ? "nv-" : "",
+ range->readonly ? "rom" : memory_region_type(mr),
+ memory_region_name(mr));
+ }
+ if (fvi->owner) {
+ mtree_print_mr_owner(mr);
+ }
+
+ if (fvi->ac) {
+ for (i = 0; i < fv_address_spaces->len; ++i) {
+ as = g_array_index(fv_address_spaces, AddressSpace*, i);
+ if (fvi->ac->has_memory(current_machine, as,
+ int128_get64(range->addr.start),
+ MR_SIZE(range->addr.size) + 1)) {
+ qemu_printf(" %s", fvi->ac->name);
+ }
+ }
+ }
+ qemu_printf("\n");
+ range++;
+ }
+
+#if !defined(CONFIG_USER_ONLY)
+ if (fvi->dispatch_tree && view->root) {
+ mtree_print_dispatch(view->dispatch, view->root);
+ }
+#endif
+
+ qemu_printf("\n");
+}
+
+static gboolean mtree_info_flatview_free(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ FlatView *view = key;
+ GArray *fv_address_spaces = value;
+
+ g_array_unref(fv_address_spaces);
+ flatview_unref(view);
+
+ return true;
+}
+
+void mtree_info(bool flatview, bool dispatch_tree, bool owner, bool disabled)
+{
+ MemoryRegionListHead ml_head;
+ MemoryRegionList *ml, *ml2;
+ AddressSpace *as;
+
+ if (flatview) {
+ FlatView *view;
+ struct FlatViewInfo fvi = {
+ .counter = 0,
+ .dispatch_tree = dispatch_tree,
+ .owner = owner,
+ };
+ GArray *fv_address_spaces;
+ GHashTable *views = g_hash_table_new(g_direct_hash, g_direct_equal);
+ AccelClass *ac = ACCEL_GET_CLASS(current_accel());
+
+ if (ac->has_memory) {
+ fvi.ac = ac;
+ }
+
+ /* Gather all FVs in one table */
+ QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
+ view = address_space_get_flatview(as);
+
+ fv_address_spaces = g_hash_table_lookup(views, view);
+ if (!fv_address_spaces) {
+ fv_address_spaces = g_array_new(false, false, sizeof(as));
+ g_hash_table_insert(views, view, fv_address_spaces);
+ }
+
+ g_array_append_val(fv_address_spaces, as);
+ }
+
+ /* Print */
+ g_hash_table_foreach(views, mtree_print_flatview, &fvi);
+
+ /* Free */
+ g_hash_table_foreach_remove(views, mtree_info_flatview_free, 0);
+ g_hash_table_unref(views);
+
+ return;
+ }
+
+ QTAILQ_INIT(&ml_head);
+
+ QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
+ qemu_printf("address-space: %s\n", as->name);
+ mtree_print_mr(as->root, 1, 0, &ml_head, owner, disabled);
+ qemu_printf("\n");
+ }
+
+ /* print aliased regions */
+ QTAILQ_FOREACH(ml, &ml_head, mrqueue) {
+ qemu_printf("memory-region: %s\n", memory_region_name(ml->mr));
+ mtree_print_mr(ml->mr, 1, 0, &ml_head, owner, disabled);
+ qemu_printf("\n");
+ }
+
+ QTAILQ_FOREACH_SAFE(ml, &ml_head, mrqueue, ml2) {
+ g_free(ml);
+ }
+}
+
+void memory_region_init_ram(MemoryRegion *mr,
+ struct Object *owner,
+ const char *name,
+ uint64_t size,
+ Error **errp)
+{
+ DeviceState *owner_dev;
+ Error *err = NULL;
+
+ memory_region_init_ram_nomigrate(mr, owner, name, size, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ /* This will assert if owner is neither NULL nor a DeviceState.
+ * We only want the owner here for the purposes of defining a
+ * unique name for migration. TODO: Ideally we should implement
+ * a naming scheme for Objects which are not DeviceStates, in
+ * which case we can relax this restriction.
+ */
+ owner_dev = DEVICE(owner);
+ vmstate_register_ram(mr, owner_dev);
+}
+
+void memory_region_init_rom(MemoryRegion *mr,
+ struct Object *owner,
+ const char *name,
+ uint64_t size,
+ Error **errp)
+{
+ DeviceState *owner_dev;
+ Error *err = NULL;
+
+ memory_region_init_rom_nomigrate(mr, owner, name, size, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ /* This will assert if owner is neither NULL nor a DeviceState.
+ * We only want the owner here for the purposes of defining a
+ * unique name for migration. TODO: Ideally we should implement
+ * a naming scheme for Objects which are not DeviceStates, in
+ * which case we can relax this restriction.
+ */
+ owner_dev = DEVICE(owner);
+ vmstate_register_ram(mr, owner_dev);
+}
+
+void memory_region_init_rom_device(MemoryRegion *mr,
+ struct Object *owner,
+ const MemoryRegionOps *ops,
+ void *opaque,
+ const char *name,
+ uint64_t size,
+ Error **errp)
+{
+ DeviceState *owner_dev;
+ Error *err = NULL;
+
+ memory_region_init_rom_device_nomigrate(mr, owner, ops, opaque,
+ name, size, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ /* This will assert if owner is neither NULL nor a DeviceState.
+ * We only want the owner here for the purposes of defining a
+ * unique name for migration. TODO: Ideally we should implement
+ * a naming scheme for Objects which are not DeviceStates, in
+ * which case we can relax this restriction.
+ */
+ owner_dev = DEVICE(owner);
+ vmstate_register_ram(mr, owner_dev);
+}
+
+static const TypeInfo memory_region_info = {
+ .parent = TYPE_OBJECT,
+ .name = TYPE_MEMORY_REGION,
+ .class_size = sizeof(MemoryRegionClass),
+ .instance_size = sizeof(MemoryRegion),
+ .instance_init = memory_region_initfn,
+ .instance_finalize = memory_region_finalize,
+};
+
+static const TypeInfo iommu_memory_region_info = {
+ .parent = TYPE_MEMORY_REGION,
+ .name = TYPE_IOMMU_MEMORY_REGION,
+ .class_size = sizeof(IOMMUMemoryRegionClass),
+ .instance_size = sizeof(IOMMUMemoryRegion),
+ .instance_init = iommu_memory_region_initfn,
+ .abstract = true,
+};
+
+static void memory_register_types(void)
+{
+ type_register_static(&memory_region_info);
+ type_register_static(&iommu_memory_region_info);
+}
+
+type_init(memory_register_types)
--- /dev/null
+/*
+ * QEMU memory mapping
+ *
+ * Copyright Fujitsu, Corp. 2011, 2012
+ *
+ * Authors:
+ * Wen Congyang <wency@cn.fujitsu.com>
+ *
+ * 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 "qapi/error.h"
+
+#include "cpu.h"
+#include "sysemu/memory_mapping.h"
+#include "exec/memory.h"
+#include "exec/address-spaces.h"
+
+//#define DEBUG_GUEST_PHYS_REGION_ADD
+
+static void memory_mapping_list_add_mapping_sorted(MemoryMappingList *list,
+ MemoryMapping *mapping)
+{
+ MemoryMapping *p;
+
+ QTAILQ_FOREACH(p, &list->head, next) {
+ if (p->phys_addr >= mapping->phys_addr) {
+ QTAILQ_INSERT_BEFORE(p, mapping, next);
+ return;
+ }
+ }
+ QTAILQ_INSERT_TAIL(&list->head, mapping, next);
+}
+
+static void create_new_memory_mapping(MemoryMappingList *list,
+ hwaddr phys_addr,
+ hwaddr virt_addr,
+ ram_addr_t length)
+{
+ MemoryMapping *memory_mapping;
+
+ memory_mapping = g_malloc(sizeof(MemoryMapping));
+ memory_mapping->phys_addr = phys_addr;
+ memory_mapping->virt_addr = virt_addr;
+ memory_mapping->length = length;
+ list->last_mapping = memory_mapping;
+ list->num++;
+ memory_mapping_list_add_mapping_sorted(list, memory_mapping);
+}
+
+static inline bool mapping_contiguous(MemoryMapping *map,
+ hwaddr phys_addr,
+ hwaddr virt_addr)
+{
+ return phys_addr == map->phys_addr + map->length &&
+ virt_addr == map->virt_addr + map->length;
+}
+
+/*
+ * [map->phys_addr, map->phys_addr + map->length) and
+ * [phys_addr, phys_addr + length) have intersection?
+ */
+static inline bool mapping_have_same_region(MemoryMapping *map,
+ hwaddr phys_addr,
+ ram_addr_t length)
+{
+ return !(phys_addr + length < map->phys_addr ||
+ phys_addr >= map->phys_addr + map->length);
+}
+
+/*
+ * [map->phys_addr, map->phys_addr + map->length) and
+ * [phys_addr, phys_addr + length) have intersection. The virtual address in the
+ * intersection are the same?
+ */
+static inline bool mapping_conflict(MemoryMapping *map,
+ hwaddr phys_addr,
+ hwaddr virt_addr)
+{
+ return virt_addr - map->virt_addr != phys_addr - map->phys_addr;
+}
+
+/*
+ * [map->virt_addr, map->virt_addr + map->length) and
+ * [virt_addr, virt_addr + length) have intersection. And the physical address
+ * in the intersection are the same.
+ */
+static inline void mapping_merge(MemoryMapping *map,
+ hwaddr virt_addr,
+ ram_addr_t length)
+{
+ if (virt_addr < map->virt_addr) {
+ map->length += map->virt_addr - virt_addr;
+ map->virt_addr = virt_addr;
+ }
+
+ if ((virt_addr + length) >
+ (map->virt_addr + map->length)) {
+ map->length = virt_addr + length - map->virt_addr;
+ }
+}
+
+void memory_mapping_list_add_merge_sorted(MemoryMappingList *list,
+ hwaddr phys_addr,
+ hwaddr virt_addr,
+ ram_addr_t length)
+{
+ MemoryMapping *memory_mapping, *last_mapping;
+
+ if (QTAILQ_EMPTY(&list->head)) {
+ create_new_memory_mapping(list, phys_addr, virt_addr, length);
+ return;
+ }
+
+ last_mapping = list->last_mapping;
+ if (last_mapping) {
+ if (mapping_contiguous(last_mapping, phys_addr, virt_addr)) {
+ last_mapping->length += length;
+ return;
+ }
+ }
+
+ QTAILQ_FOREACH(memory_mapping, &list->head, next) {
+ if (mapping_contiguous(memory_mapping, phys_addr, virt_addr)) {
+ memory_mapping->length += length;
+ list->last_mapping = memory_mapping;
+ return;
+ }
+
+ if (phys_addr + length < memory_mapping->phys_addr) {
+ /* create a new region before memory_mapping */
+ break;
+ }
+
+ if (mapping_have_same_region(memory_mapping, phys_addr, length)) {
+ if (mapping_conflict(memory_mapping, phys_addr, virt_addr)) {
+ continue;
+ }
+
+ /* merge this region into memory_mapping */
+ mapping_merge(memory_mapping, virt_addr, length);
+ list->last_mapping = memory_mapping;
+ return;
+ }
+ }
+
+ /* this region can not be merged into any existed memory mapping. */
+ create_new_memory_mapping(list, phys_addr, virt_addr, length);
+}
+
+void memory_mapping_list_free(MemoryMappingList *list)
+{
+ MemoryMapping *p, *q;
+
+ QTAILQ_FOREACH_SAFE(p, &list->head, next, q) {
+ QTAILQ_REMOVE(&list->head, p, next);
+ g_free(p);
+ }
+
+ list->num = 0;
+ list->last_mapping = NULL;
+}
+
+void memory_mapping_list_init(MemoryMappingList *list)
+{
+ list->num = 0;
+ list->last_mapping = NULL;
+ QTAILQ_INIT(&list->head);
+}
+
+void guest_phys_blocks_free(GuestPhysBlockList *list)
+{
+ GuestPhysBlock *p, *q;
+
+ QTAILQ_FOREACH_SAFE(p, &list->head, next, q) {
+ QTAILQ_REMOVE(&list->head, p, next);
+ memory_region_unref(p->mr);
+ g_free(p);
+ }
+ list->num = 0;
+}
+
+void guest_phys_blocks_init(GuestPhysBlockList *list)
+{
+ list->num = 0;
+ QTAILQ_INIT(&list->head);
+}
+
+typedef struct GuestPhysListener {
+ GuestPhysBlockList *list;
+ MemoryListener listener;
+} GuestPhysListener;
+
+static void guest_phys_blocks_region_add(MemoryListener *listener,
+ MemoryRegionSection *section)
+{
+ GuestPhysListener *g;
+ uint64_t section_size;
+ hwaddr target_start, target_end;
+ uint8_t *host_addr;
+ GuestPhysBlock *predecessor;
+
+ /* we only care about RAM */
+ if (!memory_region_is_ram(section->mr) ||
+ memory_region_is_ram_device(section->mr) ||
+ memory_region_is_nonvolatile(section->mr)) {
+ return;
+ }
+
+ g = container_of(listener, GuestPhysListener, listener);
+ section_size = int128_get64(section->size);
+ target_start = section->offset_within_address_space;
+ target_end = target_start + section_size;
+ host_addr = memory_region_get_ram_ptr(section->mr) +
+ section->offset_within_region;
+ predecessor = NULL;
+
+ /* find continuity in guest physical address space */
+ if (!QTAILQ_EMPTY(&g->list->head)) {
+ hwaddr predecessor_size;
+
+ predecessor = QTAILQ_LAST(&g->list->head);
+ predecessor_size = predecessor->target_end - predecessor->target_start;
+
+ /* the memory API guarantees monotonically increasing traversal */
+ g_assert(predecessor->target_end <= target_start);
+
+ /* we want continuity in both guest-physical and host-virtual memory */
+ if (predecessor->target_end < target_start ||
+ predecessor->host_addr + predecessor_size != host_addr) {
+ predecessor = NULL;
+ }
+ }
+
+ if (predecessor == NULL) {
+ /* isolated mapping, allocate it and add it to the list */
+ GuestPhysBlock *block = g_malloc0(sizeof *block);
+
+ block->target_start = target_start;
+ block->target_end = target_end;
+ block->host_addr = host_addr;
+ block->mr = section->mr;
+ memory_region_ref(section->mr);
+
+ QTAILQ_INSERT_TAIL(&g->list->head, block, next);
+ ++g->list->num;
+ } else {
+ /* expand predecessor until @target_end; predecessor's start doesn't
+ * change
+ */
+ predecessor->target_end = target_end;
+ }
+
+#ifdef DEBUG_GUEST_PHYS_REGION_ADD
+ fprintf(stderr, "%s: target_start=" TARGET_FMT_plx " target_end="
+ TARGET_FMT_plx ": %s (count: %u)\n", __func__, target_start,
+ target_end, predecessor ? "joined" : "added", g->list->num);
+#endif
+}
+
+void guest_phys_blocks_append(GuestPhysBlockList *list)
+{
+ GuestPhysListener g = { 0 };
+
+ g.list = list;
+ g.listener.region_add = &guest_phys_blocks_region_add;
+ memory_listener_register(&g.listener, &address_space_memory);
+ memory_listener_unregister(&g.listener);
+}
+
+static CPUState *find_paging_enabled_cpu(CPUState *start_cpu)
+{
+ CPUState *cpu;
+
+ CPU_FOREACH(cpu) {
+ if (cpu_paging_enabled(cpu)) {
+ return cpu;
+ }
+ }
+
+ return NULL;
+}
+
+void qemu_get_guest_memory_mapping(MemoryMappingList *list,
+ const GuestPhysBlockList *guest_phys_blocks,
+ Error **errp)
+{
+ CPUState *cpu, *first_paging_enabled_cpu;
+ GuestPhysBlock *block;
+ ram_addr_t offset, length;
+
+ first_paging_enabled_cpu = find_paging_enabled_cpu(first_cpu);
+ if (first_paging_enabled_cpu) {
+ for (cpu = first_paging_enabled_cpu; cpu != NULL;
+ cpu = CPU_NEXT(cpu)) {
+ Error *err = NULL;
+ cpu_get_memory_mapping(cpu, list, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ }
+ return;
+ }
+
+ /*
+ * If the guest doesn't use paging, the virtual address is equal to physical
+ * address.
+ */
+ QTAILQ_FOREACH(block, &guest_phys_blocks->head, next) {
+ offset = block->target_start;
+ length = block->target_end - block->target_start;
+ create_new_memory_mapping(list, offset, offset, length);
+ }
+}
+
+void qemu_get_guest_simple_memory_mapping(MemoryMappingList *list,
+ const GuestPhysBlockList *guest_phys_blocks)
+{
+ GuestPhysBlock *block;
+
+ QTAILQ_FOREACH(block, &guest_phys_blocks->head, next) {
+ create_new_memory_mapping(list, block->target_start, 0,
+ block->target_end - block->target_start);
+ }
+}
+
+void memory_mapping_filter(MemoryMappingList *list, int64_t begin,
+ int64_t length)
+{
+ MemoryMapping *cur, *next;
+
+ QTAILQ_FOREACH_SAFE(cur, &list->head, next, next) {
+ if (cur->phys_addr >= begin + length ||
+ cur->phys_addr + cur->length <= begin) {
+ QTAILQ_REMOVE(&list->head, cur, next);
+ g_free(cur);
+ list->num--;
+ continue;
+ }
+
+ if (cur->phys_addr < begin) {
+ cur->length -= begin - cur->phys_addr;
+ if (cur->virt_addr) {
+ cur->virt_addr += begin - cur->phys_addr;
+ }
+ cur->phys_addr = begin;
+ }
+
+ if (cur->phys_addr + cur->length > begin + length) {
+ cur->length -= cur->phys_addr + cur->length - begin - length;
+ }
+ }
+}
--- /dev/null
+/*
+ * Test Server
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * 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 "qapi/error.h"
+#include "cpu.h"
+#include "sysemu/qtest.h"
+#include "sysemu/runstate.h"
+#include "chardev/char-fe.h"
+#include "exec/ioport.h"
+#include "exec/memory.h"
+#include "hw/irq.h"
+#include "sysemu/accel.h"
+#include "sysemu/cpus.h"
+#include "qemu/config-file.h"
+#include "qemu/option.h"
+#include "qemu/error-report.h"
+#include "qemu/module.h"
+#include "qemu/cutils.h"
+#include "config-devices.h"
+#ifdef CONFIG_PSERIES
+#include "hw/ppc/spapr_rtas.h"
+#endif
+
+#define MAX_IRQ 256
+
+bool qtest_allowed;
+
+static DeviceState *irq_intercept_dev;
+static FILE *qtest_log_fp;
+static CharBackend qtest_chr;
+static GString *inbuf;
+static int irq_levels[MAX_IRQ];
+static qemu_timeval start_time;
+static bool qtest_opened;
+static void (*qtest_server_send)(void*, const char*);
+static void *qtest_server_send_opaque;
+
+#define FMT_timeval "%ld.%06ld"
+
+/**
+ * QTest Protocol
+ *
+ * Line based protocol, request/response based. Server can send async messages
+ * so clients should always handle many async messages before the response
+ * comes in.
+ *
+ * Valid requests
+ *
+ * Clock management:
+ *
+ * The qtest client is completely in charge of the QEMU_CLOCK_VIRTUAL. qtest commands
+ * let you adjust the value of the clock (monotonically). All the commands
+ * return the current value of the clock in nanoseconds.
+ *
+ * > clock_step
+ * < OK VALUE
+ *
+ * Advance the clock to the next deadline. Useful when waiting for
+ * asynchronous events.
+ *
+ * > clock_step NS
+ * < OK VALUE
+ *
+ * Advance the clock by NS nanoseconds.
+ *
+ * > clock_set NS
+ * < OK VALUE
+ *
+ * Advance the clock to NS nanoseconds (do nothing if it's already past).
+ *
+ * PIO and memory access:
+ *
+ * > outb ADDR VALUE
+ * < OK
+ *
+ * > outw ADDR VALUE
+ * < OK
+ *
+ * > outl ADDR VALUE
+ * < OK
+ *
+ * > inb ADDR
+ * < OK VALUE
+ *
+ * > inw ADDR
+ * < OK VALUE
+ *
+ * > inl ADDR
+ * < OK VALUE
+ *
+ * > writeb ADDR VALUE
+ * < OK
+ *
+ * > writew ADDR VALUE
+ * < OK
+ *
+ * > writel ADDR VALUE
+ * < OK
+ *
+ * > writeq ADDR VALUE
+ * < OK
+ *
+ * > readb ADDR
+ * < OK VALUE
+ *
+ * > readw ADDR
+ * < OK VALUE
+ *
+ * > readl ADDR
+ * < OK VALUE
+ *
+ * > readq ADDR
+ * < OK VALUE
+ *
+ * > read ADDR SIZE
+ * < OK DATA
+ *
+ * > write ADDR SIZE DATA
+ * < OK
+ *
+ * > b64read ADDR SIZE
+ * < OK B64_DATA
+ *
+ * > b64write ADDR SIZE B64_DATA
+ * < OK
+ *
+ * > memset ADDR SIZE VALUE
+ * < OK
+ *
+ * ADDR, SIZE, VALUE are all integers parsed with strtoul() with a base of 0.
+ * For 'memset' a zero size is permitted and does nothing.
+ *
+ * DATA is an arbitrarily long hex number prefixed with '0x'. If it's smaller
+ * than the expected size, the value will be zero filled at the end of the data
+ * sequence.
+ *
+ * B64_DATA is an arbitrarily long base64 encoded string.
+ * If the sizes do not match, the data will be truncated.
+ *
+ * IRQ management:
+ *
+ * > irq_intercept_in QOM-PATH
+ * < OK
+ *
+ * > irq_intercept_out QOM-PATH
+ * < OK
+ *
+ * Attach to the gpio-in (resp. gpio-out) pins exported by the device at
+ * QOM-PATH. When the pin is triggered, one of the following async messages
+ * will be printed to the qtest stream:
+ *
+ * IRQ raise NUM
+ * IRQ lower NUM
+ *
+ * where NUM is an IRQ number. For the PC, interrupts can be intercepted
+ * simply with "irq_intercept_in ioapic" (note that IRQ0 comes out with
+ * NUM=0 even though it is remapped to GSI 2).
+ *
+ * Setting interrupt level:
+ *
+ * > set_irq_in QOM-PATH NAME NUM LEVEL
+ * < OK
+ *
+ * where NAME is the name of the irq/gpio list, NUM is an IRQ number and
+ * LEVEL is an signed integer IRQ level.
+ *
+ * Forcibly set the given interrupt pin to the given level.
+ *
+ */
+
+static int hex2nib(char ch)
+{
+ if (ch >= '0' && ch <= '9') {
+ return ch - '0';
+ } else if (ch >= 'a' && ch <= 'f') {
+ return 10 + (ch - 'a');
+ } else if (ch >= 'A' && ch <= 'F') {
+ return 10 + (ch - 'A');
+ } else {
+ return -1;
+ }
+}
+
+static void qtest_get_time(qemu_timeval *tv)
+{
+ qemu_gettimeofday(tv);
+ tv->tv_sec -= start_time.tv_sec;
+ tv->tv_usec -= start_time.tv_usec;
+ if (tv->tv_usec < 0) {
+ tv->tv_usec += 1000000;
+ tv->tv_sec -= 1;
+ }
+}
+
+static void qtest_send_prefix(CharBackend *chr)
+{
+ qemu_timeval tv;
+
+ if (!qtest_log_fp || !qtest_opened) {
+ return;
+ }
+
+ qtest_get_time(&tv);
+ fprintf(qtest_log_fp, "[S +" FMT_timeval "] ",
+ (long) tv.tv_sec, (long) tv.tv_usec);
+}
+
+static void GCC_FMT_ATTR(1, 2) qtest_log_send(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!qtest_log_fp || !qtest_opened) {
+ return;
+ }
+
+ qtest_send_prefix(NULL);
+
+ va_start(ap, fmt);
+ vfprintf(qtest_log_fp, fmt, ap);
+ va_end(ap);
+}
+
+static void qtest_server_char_be_send(void *opaque, const char *str)
+{
+ size_t len = strlen(str);
+ CharBackend* chr = (CharBackend *)opaque;
+ qemu_chr_fe_write_all(chr, (uint8_t *)str, len);
+ if (qtest_log_fp && qtest_opened) {
+ fprintf(qtest_log_fp, "%s", str);
+ }
+}
+
+static void qtest_send(CharBackend *chr, const char *str)
+{
+ qtest_server_send(qtest_server_send_opaque, str);
+}
+
+static void GCC_FMT_ATTR(2, 3) qtest_sendf(CharBackend *chr,
+ const char *fmt, ...)
+{
+ va_list ap;
+ gchar *buffer;
+
+ va_start(ap, fmt);
+ buffer = g_strdup_vprintf(fmt, ap);
+ qtest_send(chr, buffer);
+ g_free(buffer);
+ va_end(ap);
+}
+
+static void qtest_irq_handler(void *opaque, int n, int level)
+{
+ qemu_irq old_irq = *(qemu_irq *)opaque;
+ qemu_set_irq(old_irq, level);
+
+ if (irq_levels[n] != level) {
+ CharBackend *chr = &qtest_chr;
+ irq_levels[n] = level;
+ qtest_send_prefix(chr);
+ qtest_sendf(chr, "IRQ %s %d\n",
+ level ? "raise" : "lower", n);
+ }
+}
+
+static void qtest_process_command(CharBackend *chr, gchar **words)
+{
+ const gchar *command;
+
+ g_assert(words);
+
+ command = words[0];
+
+ if (qtest_log_fp) {
+ qemu_timeval tv;
+ int i;
+
+ qtest_get_time(&tv);
+ fprintf(qtest_log_fp, "[R +" FMT_timeval "]",
+ (long) tv.tv_sec, (long) tv.tv_usec);
+ for (i = 0; words[i]; i++) {
+ fprintf(qtest_log_fp, " %s", words[i]);
+ }
+ fprintf(qtest_log_fp, "\n");
+ }
+
+ g_assert(command);
+ if (strcmp(words[0], "irq_intercept_out") == 0
+ || strcmp(words[0], "irq_intercept_in") == 0) {
+ DeviceState *dev;
+ NamedGPIOList *ngl;
+
+ g_assert(words[1]);
+ dev = DEVICE(object_resolve_path(words[1], NULL));
+ if (!dev) {
+ qtest_send_prefix(chr);
+ qtest_send(chr, "FAIL Unknown device\n");
+ return;
+ }
+
+ if (irq_intercept_dev) {
+ qtest_send_prefix(chr);
+ if (irq_intercept_dev != dev) {
+ qtest_send(chr, "FAIL IRQ intercept already enabled\n");
+ } else {
+ qtest_send(chr, "OK\n");
+ }
+ return;
+ }
+
+ QLIST_FOREACH(ngl, &dev->gpios, node) {
+ /* We don't support intercept of named GPIOs yet */
+ if (ngl->name) {
+ continue;
+ }
+ if (words[0][14] == 'o') {
+ int i;
+ for (i = 0; i < ngl->num_out; ++i) {
+ qemu_irq *disconnected = g_new0(qemu_irq, 1);
+ qemu_irq icpt = qemu_allocate_irq(qtest_irq_handler,
+ disconnected, i);
+
+ *disconnected = qdev_intercept_gpio_out(dev, icpt,
+ ngl->name, i);
+ }
+ } else {
+ qemu_irq_intercept_in(ngl->in, qtest_irq_handler,
+ ngl->num_in);
+ }
+ }
+ irq_intercept_dev = dev;
+ qtest_send_prefix(chr);
+ qtest_send(chr, "OK\n");
+ } else if (strcmp(words[0], "set_irq_in") == 0) {
+ DeviceState *dev;
+ qemu_irq irq;
+ char *name;
+ int ret;
+ int num;
+ int level;
+
+ g_assert(words[1] && words[2] && words[3] && words[4]);
+
+ dev = DEVICE(object_resolve_path(words[1], NULL));
+ if (!dev) {
+ qtest_send_prefix(chr);
+ qtest_send(chr, "FAIL Unknown device\n");
+ return;
+ }
+
+ if (strcmp(words[2], "unnamed-gpio-in") == 0) {
+ name = NULL;
+ } else {
+ name = words[2];
+ }
+
+ ret = qemu_strtoi(words[3], NULL, 0, &num);
+ g_assert(!ret);
+ ret = qemu_strtoi(words[4], NULL, 0, &level);
+ g_assert(!ret);
+
+ irq = qdev_get_gpio_in_named(dev, name, num);
+
+ qemu_set_irq(irq, level);
+ qtest_send_prefix(chr);
+ qtest_send(chr, "OK\n");
+ } else if (strcmp(words[0], "outb") == 0 ||
+ strcmp(words[0], "outw") == 0 ||
+ strcmp(words[0], "outl") == 0) {
+ unsigned long addr;
+ unsigned long value;
+ int ret;
+
+ g_assert(words[1] && words[2]);
+ ret = qemu_strtoul(words[1], NULL, 0, &addr);
+ g_assert(ret == 0);
+ ret = qemu_strtoul(words[2], NULL, 0, &value);
+ g_assert(ret == 0);
+ g_assert(addr <= 0xffff);
+
+ if (words[0][3] == 'b') {
+ cpu_outb(addr, value);
+ } else if (words[0][3] == 'w') {
+ cpu_outw(addr, value);
+ } else if (words[0][3] == 'l') {
+ cpu_outl(addr, value);
+ }
+ qtest_send_prefix(chr);
+ qtest_send(chr, "OK\n");
+ } else if (strcmp(words[0], "inb") == 0 ||
+ strcmp(words[0], "inw") == 0 ||
+ strcmp(words[0], "inl") == 0) {
+ unsigned long addr;
+ uint32_t value = -1U;
+ int ret;
+
+ g_assert(words[1]);
+ ret = qemu_strtoul(words[1], NULL, 0, &addr);
+ g_assert(ret == 0);
+ g_assert(addr <= 0xffff);
+
+ if (words[0][2] == 'b') {
+ value = cpu_inb(addr);
+ } else if (words[0][2] == 'w') {
+ value = cpu_inw(addr);
+ } else if (words[0][2] == 'l') {
+ value = cpu_inl(addr);
+ }
+ qtest_send_prefix(chr);
+ qtest_sendf(chr, "OK 0x%04x\n", value);
+ } else if (strcmp(words[0], "writeb") == 0 ||
+ strcmp(words[0], "writew") == 0 ||
+ strcmp(words[0], "writel") == 0 ||
+ strcmp(words[0], "writeq") == 0) {
+ uint64_t addr;
+ uint64_t value;
+ int ret;
+
+ g_assert(words[1] && words[2]);
+ ret = qemu_strtou64(words[1], NULL, 0, &addr);
+ g_assert(ret == 0);
+ ret = qemu_strtou64(words[2], NULL, 0, &value);
+ g_assert(ret == 0);
+
+ if (words[0][5] == 'b') {
+ uint8_t data = value;
+ address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
+ &data, 1);
+ } else if (words[0][5] == 'w') {
+ uint16_t data = value;
+ tswap16s(&data);
+ address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
+ &data, 2);
+ } else if (words[0][5] == 'l') {
+ uint32_t data = value;
+ tswap32s(&data);
+ address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
+ &data, 4);
+ } else if (words[0][5] == 'q') {
+ uint64_t data = value;
+ tswap64s(&data);
+ address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
+ &data, 8);
+ }
+ qtest_send_prefix(chr);
+ qtest_send(chr, "OK\n");
+ } else if (strcmp(words[0], "readb") == 0 ||
+ strcmp(words[0], "readw") == 0 ||
+ strcmp(words[0], "readl") == 0 ||
+ strcmp(words[0], "readq") == 0) {
+ uint64_t addr;
+ uint64_t value = UINT64_C(-1);
+ int ret;
+
+ g_assert(words[1]);
+ ret = qemu_strtou64(words[1], NULL, 0, &addr);
+ g_assert(ret == 0);
+
+ if (words[0][4] == 'b') {
+ uint8_t data;
+ address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
+ &data, 1);
+ value = data;
+ } else if (words[0][4] == 'w') {
+ uint16_t data;
+ address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
+ &data, 2);
+ value = tswap16(data);
+ } else if (words[0][4] == 'l') {
+ uint32_t data;
+ address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
+ &data, 4);
+ value = tswap32(data);
+ } else if (words[0][4] == 'q') {
+ address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
+ &value, 8);
+ tswap64s(&value);
+ }
+ qtest_send_prefix(chr);
+ qtest_sendf(chr, "OK 0x%016" PRIx64 "\n", value);
+ } else if (strcmp(words[0], "read") == 0) {
+ uint64_t addr, len, i;
+ uint8_t *data;
+ char *enc;
+ int ret;
+
+ g_assert(words[1] && words[2]);
+ ret = qemu_strtou64(words[1], NULL, 0, &addr);
+ g_assert(ret == 0);
+ ret = qemu_strtou64(words[2], NULL, 0, &len);
+ g_assert(ret == 0);
+ /* We'd send garbage to libqtest if len is 0 */
+ g_assert(len);
+
+ data = g_malloc(len);
+ address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data,
+ len);
+
+ enc = g_malloc(2 * len + 1);
+ for (i = 0; i < len; i++) {
+ sprintf(&enc[i * 2], "%02x", data[i]);
+ }
+
+ qtest_send_prefix(chr);
+ qtest_sendf(chr, "OK 0x%s\n", enc);
+
+ g_free(data);
+ g_free(enc);
+ } else if (strcmp(words[0], "b64read") == 0) {
+ uint64_t addr, len;
+ uint8_t *data;
+ gchar *b64_data;
+ int ret;
+
+ g_assert(words[1] && words[2]);
+ ret = qemu_strtou64(words[1], NULL, 0, &addr);
+ g_assert(ret == 0);
+ ret = qemu_strtou64(words[2], NULL, 0, &len);
+ g_assert(ret == 0);
+
+ data = g_malloc(len);
+ address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data,
+ len);
+ b64_data = g_base64_encode(data, len);
+ qtest_send_prefix(chr);
+ qtest_sendf(chr, "OK %s\n", b64_data);
+
+ g_free(data);
+ g_free(b64_data);
+ } else if (strcmp(words[0], "write") == 0) {
+ uint64_t addr, len, i;
+ uint8_t *data;
+ size_t data_len;
+ int ret;
+
+ g_assert(words[1] && words[2] && words[3]);
+ ret = qemu_strtou64(words[1], NULL, 0, &addr);
+ g_assert(ret == 0);
+ ret = qemu_strtou64(words[2], NULL, 0, &len);
+ g_assert(ret == 0);
+
+ data_len = strlen(words[3]);
+ if (data_len < 3) {
+ qtest_send(chr, "ERR invalid argument size\n");
+ return;
+ }
+
+ data = g_malloc(len);
+ for (i = 0; i < len; i++) {
+ if ((i * 2 + 4) <= data_len) {
+ data[i] = hex2nib(words[3][i * 2 + 2]) << 4;
+ data[i] |= hex2nib(words[3][i * 2 + 3]);
+ } else {
+ data[i] = 0;
+ }
+ }
+ address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data,
+ len);
+ g_free(data);
+
+ qtest_send_prefix(chr);
+ qtest_send(chr, "OK\n");
+ } else if (strcmp(words[0], "memset") == 0) {
+ uint64_t addr, len;
+ uint8_t *data;
+ unsigned long pattern;
+ int ret;
+
+ g_assert(words[1] && words[2] && words[3]);
+ ret = qemu_strtou64(words[1], NULL, 0, &addr);
+ g_assert(ret == 0);
+ ret = qemu_strtou64(words[2], NULL, 0, &len);
+ g_assert(ret == 0);
+ ret = qemu_strtoul(words[3], NULL, 0, &pattern);
+ g_assert(ret == 0);
+
+ if (len) {
+ data = g_malloc(len);
+ memset(data, pattern, len);
+ address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED,
+ data, len);
+ g_free(data);
+ }
+
+ qtest_send_prefix(chr);
+ qtest_send(chr, "OK\n");
+ } else if (strcmp(words[0], "b64write") == 0) {
+ uint64_t addr, len;
+ uint8_t *data;
+ size_t data_len;
+ gsize out_len;
+ int ret;
+
+ g_assert(words[1] && words[2] && words[3]);
+ ret = qemu_strtou64(words[1], NULL, 0, &addr);
+ g_assert(ret == 0);
+ ret = qemu_strtou64(words[2], NULL, 0, &len);
+ g_assert(ret == 0);
+
+ data_len = strlen(words[3]);
+ if (data_len < 3) {
+ qtest_send(chr, "ERR invalid argument size\n");
+ return;
+ }
+
+ data = g_base64_decode_inplace(words[3], &out_len);
+ if (out_len != len) {
+ qtest_log_send("b64write: data length mismatch (told %"PRIu64", "
+ "found %zu)\n",
+ len, out_len);
+ out_len = MIN(out_len, len);
+ }
+
+ address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data,
+ len);
+
+ qtest_send_prefix(chr);
+ qtest_send(chr, "OK\n");
+ } else if (strcmp(words[0], "endianness") == 0) {
+ qtest_send_prefix(chr);
+#if defined(TARGET_WORDS_BIGENDIAN)
+ qtest_sendf(chr, "OK big\n");
+#else
+ qtest_sendf(chr, "OK little\n");
+#endif
+#ifdef CONFIG_PSERIES
+ } else if (strcmp(words[0], "rtas") == 0) {
+ uint64_t res, args, ret;
+ unsigned long nargs, nret;
+ int rc;
+
+ rc = qemu_strtoul(words[2], NULL, 0, &nargs);
+ g_assert(rc == 0);
+ rc = qemu_strtou64(words[3], NULL, 0, &args);
+ g_assert(rc == 0);
+ rc = qemu_strtoul(words[4], NULL, 0, &nret);
+ g_assert(rc == 0);
+ rc = qemu_strtou64(words[5], NULL, 0, &ret);
+ g_assert(rc == 0);
+ res = qtest_rtas_call(words[1], nargs, args, nret, ret);
+
+ qtest_send_prefix(chr);
+ qtest_sendf(chr, "OK %"PRIu64"\n", res);
+#endif
+ } else if (qtest_enabled() && strcmp(words[0], "clock_step") == 0) {
+ int64_t ns;
+
+ if (words[1]) {
+ int ret = qemu_strtoi64(words[1], NULL, 0, &ns);
+ g_assert(ret == 0);
+ } else {
+ ns = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
+ QEMU_TIMER_ATTR_ALL);
+ }
+ qtest_clock_warp(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns);
+ qtest_send_prefix(chr);
+ qtest_sendf(chr, "OK %"PRIi64"\n",
+ (int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
+ } else if (strcmp(words[0], "module_load") == 0) {
+ g_assert(words[1] && words[2]);
+
+ qtest_send_prefix(chr);
+ if (module_load_one(words[1], words[2])) {
+ qtest_sendf(chr, "OK\n");
+ } else {
+ qtest_sendf(chr, "FAIL\n");
+ }
+ } else if (qtest_enabled() && strcmp(words[0], "clock_set") == 0) {
+ int64_t ns;
+ int ret;
+
+ g_assert(words[1]);
+ ret = qemu_strtoi64(words[1], NULL, 0, &ns);
+ g_assert(ret == 0);
+ qtest_clock_warp(ns);
+ qtest_send_prefix(chr);
+ qtest_sendf(chr, "OK %"PRIi64"\n",
+ (int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
+ } else {
+ qtest_send_prefix(chr);
+ qtest_sendf(chr, "FAIL Unknown command '%s'\n", words[0]);
+ }
+}
+
+static void qtest_process_inbuf(CharBackend *chr, GString *inbuf)
+{
+ char *end;
+
+ while ((end = strchr(inbuf->str, '\n')) != NULL) {
+ size_t offset;
+ GString *cmd;
+ gchar **words;
+
+ offset = end - inbuf->str;
+
+ cmd = g_string_new_len(inbuf->str, offset);
+ g_string_erase(inbuf, 0, offset + 1);
+
+ words = g_strsplit(cmd->str, " ", 0);
+ qtest_process_command(chr, words);
+ g_strfreev(words);
+
+ g_string_free(cmd, TRUE);
+ }
+}
+
+static void qtest_read(void *opaque, const uint8_t *buf, int size)
+{
+ CharBackend *chr = opaque;
+
+ g_string_append_len(inbuf, (const gchar *)buf, size);
+ qtest_process_inbuf(chr, inbuf);
+}
+
+static int qtest_can_read(void *opaque)
+{
+ return 1024;
+}
+
+static void qtest_event(void *opaque, QEMUChrEvent event)
+{
+ int i;
+
+ switch (event) {
+ case CHR_EVENT_OPENED:
+ /*
+ * We used to call qemu_system_reset() here, hoping we could
+ * use the same process for multiple tests that way. Never
+ * used. Injects an extra reset even when it's not used, and
+ * that can mess up tests, e.g. -boot once.
+ */
+ for (i = 0; i < ARRAY_SIZE(irq_levels); i++) {
+ irq_levels[i] = 0;
+ }
+ qemu_gettimeofday(&start_time);
+ qtest_opened = true;
+ if (qtest_log_fp) {
+ fprintf(qtest_log_fp, "[I " FMT_timeval "] OPENED\n",
+ (long) start_time.tv_sec, (long) start_time.tv_usec);
+ }
+ break;
+ case CHR_EVENT_CLOSED:
+ qtest_opened = false;
+ if (qtest_log_fp) {
+ qemu_timeval tv;
+ qtest_get_time(&tv);
+ fprintf(qtest_log_fp, "[I +" FMT_timeval "] CLOSED\n",
+ (long) tv.tv_sec, (long) tv.tv_usec);
+ }
+ break;
+ default:
+ break;
+ }
+}
+void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error **errp)
+{
+ Chardev *chr;
+
+ chr = qemu_chr_new("qtest", qtest_chrdev, NULL);
+
+ if (chr == NULL) {
+ error_setg(errp, "Failed to initialize device for qtest: \"%s\"",
+ qtest_chrdev);
+ return;
+ }
+
+ if (qtest_log) {
+ if (strcmp(qtest_log, "none") != 0) {
+ qtest_log_fp = fopen(qtest_log, "w+");
+ }
+ } else {
+ qtest_log_fp = stderr;
+ }
+
+ qemu_chr_fe_init(&qtest_chr, chr, errp);
+ qemu_chr_fe_set_handlers(&qtest_chr, qtest_can_read, qtest_read,
+ qtest_event, NULL, &qtest_chr, NULL, true);
+ qemu_chr_fe_set_echo(&qtest_chr, true);
+
+ inbuf = g_string_new("");
+
+ if (!qtest_server_send) {
+ qtest_server_set_send_handler(qtest_server_char_be_send, &qtest_chr);
+ }
+}
+
+void qtest_server_set_send_handler(void (*send)(void*, const char*),
+ void *opaque)
+{
+ qtest_server_send = send;
+ qtest_server_send_opaque = opaque;
+}
+
+bool qtest_driver(void)
+{
+ return qtest_chr.chr != NULL;
+}
+
+void qtest_server_inproc_recv(void *dummy, const char *buf)
+{
+ static GString *gstr;
+ if (!gstr) {
+ gstr = g_string_new(NULL);
+ }
+ g_string_append(gstr, buf);
+ if (gstr->str[gstr->len - 1] == '\n') {
+ qtest_process_inbuf(NULL, gstr);
+ g_string_truncate(gstr, 0);
+ }
+}
.name = "timestamp",
.type = QEMU_OPT_BOOL,
},
+ {
+ .name = "guest-name",
+ .type = QEMU_OPT_BOOL,
+ .help = "Prepends guest name for error messages but only if "
+ "-name guest is set otherwise option is ignored\n",
+ },
{ /* end of list */ }
},
};
static void configure_msg(QemuOpts *opts)
{
error_with_timestamp = qemu_opt_get_bool(opts, "timestamp", false);
+ error_with_guestname = qemu_opt_get_bool(opts, "guest-name", false);
}
g_slist_free(accel_list);
exit(0);
}
- if (optarg && strchr(optarg, ':')) {
- error_report("Don't use ':' with -accel, "
- "use -M accel=... for now instead");
- exit(1);
- }
break;
case QEMU_OPTION_usb:
olist = qemu_find_opts("machine");
if (!opts) {
exit(1);
}
+ /* Capture guest name if -msg guest-name is used later */
+ error_guest_name = qemu_opt_get(opts, "guest");
break;
case QEMU_OPTION_prom_env:
if (nb_prom_envs >= MAX_PROM_ENVS) {
.resetvalue = 0x0 },
{ .name = "PMINTENCLR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 2,
.access = PL1_RW, .accessfn = access_tpm,
- .type = ARM_CP_ALIAS | ARM_CP_IO,
+ .type = ARM_CP_ALIAS | ARM_CP_IO | ARM_CP_NO_RAW,
.fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
.writefn = pmintenclr_write, },
{ .name = "PMINTENCLR_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 2,
.access = PL1_RW, .accessfn = access_tpm,
- .type = ARM_CP_ALIAS | ARM_CP_IO,
+ .type = ARM_CP_ALIAS | ARM_CP_IO | ARM_CP_NO_RAW,
.fieldoffset = offsetof(CPUARMState, cp15.c9_pminten),
.writefn = pmintenclr_write },
{ .name = "CCSIDR", .state = ARM_CP_STATE_BOTH,
desc = FIELD_DP32(desc, MTEDESC, ESIZE, 1 << msz);
desc <<= SVE_MTEDESC_SHIFT;
}
- desc = simd_desc(vsz, vsz, scale);
+ desc = simd_desc(vsz, vsz, desc | scale);
t_desc = tcg_const_i32(desc);
tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg));
--- /dev/null
+#
+# QEMU AVR
+#
+# Copyright (c) 2016-2020 Michael Rolnik
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see
+# <http://www.gnu.org/licenses/lgpl-2.1.html>
+#
+
+DECODETREE = $(SRC_PATH)/scripts/decodetree.py
+decode-y = $(SRC_PATH)/target/avr/insn.decode
+
+target/avr/decode_insn.inc.c: $(decode-y) $(DECODETREE)
+ $(call quiet-command, \
+ $(PYTHON) $(DECODETREE) -o $@ --decode decode_insn --insnwidth 16 $<, \
+ "GEN", $(TARGET_DIR)$@)
+
+target/avr/translate.o: target/avr/decode_insn.inc.c
+
+obj-y += translate.o cpu.o helper.o
+obj-y += gdbstub.o
+obj-y += disas.o
+obj-$(CONFIG_SOFTMMU) += machine.o
--- /dev/null
+/*
+ * QEMU AVR CPU
+ *
+ * Copyright (c) 2016-2020 Michael Rolnik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#ifndef AVR_CPU_PARAM_H
+#define AVR_CPU_PARAM_H
+
+#define TARGET_LONG_BITS 32
+/*
+ * TARGET_PAGE_BITS cannot be more than 8 bits because
+ * 1. all IO registers occupy [0x0000 .. 0x00ff] address range, and they
+ * should be implemented as a device and not memory
+ * 2. SRAM starts at the address 0x0100
+ */
+#define TARGET_PAGE_BITS 8
+#define TARGET_PHYS_ADDR_SPACE_BITS 24
+#define TARGET_VIRT_ADDR_SPACE_BITS 24
+#define NB_MMU_MODES 2
+
+#endif
--- /dev/null
+/*
+ * QEMU AVR CPU
+ *
+ * Copyright (c) 2016-2020 Michael Rolnik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#ifndef QEMU_AVR_QOM_H
+#define QEMU_AVR_QOM_H
+
+#include "hw/core/cpu.h"
+
+#define TYPE_AVR_CPU "avr-cpu"
+
+#define AVR_CPU_CLASS(klass) \
+ OBJECT_CLASS_CHECK(AVRCPUClass, (klass), TYPE_AVR_CPU)
+#define AVR_CPU(obj) \
+ OBJECT_CHECK(AVRCPU, (obj), TYPE_AVR_CPU)
+#define AVR_CPU_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(AVRCPUClass, (obj), TYPE_AVR_CPU)
+
+/**
+ * AVRCPUClass:
+ * @parent_realize: The parent class' realize handler.
+ * @parent_reset: The parent class' reset handler.
+ * @vr: Version Register value.
+ *
+ * A AVR CPU model.
+ */
+typedef struct AVRCPUClass {
+ /*< private >*/
+ CPUClass parent_class;
+ /*< public >*/
+ DeviceRealize parent_realize;
+ DeviceReset parent_reset;
+} AVRCPUClass;
+
+typedef struct AVRCPU AVRCPU;
+
+#endif /* !defined (QEMU_AVR_CPU_QOM_H) */
--- /dev/null
+/*
+ * QEMU AVR CPU
+ *
+ * Copyright (c) 2019-2020 Michael Rolnik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/qemu-print.h"
+#include "exec/exec-all.h"
+#include "cpu.h"
+#include "disas/dis-asm.h"
+
+static void avr_cpu_set_pc(CPUState *cs, vaddr value)
+{
+ AVRCPU *cpu = AVR_CPU(cs);
+
+ cpu->env.pc_w = value / 2; /* internally PC points to words */
+}
+
+static bool avr_cpu_has_work(CPUState *cs)
+{
+ AVRCPU *cpu = AVR_CPU(cs);
+ CPUAVRState *env = &cpu->env;
+
+ return (cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_RESET))
+ && cpu_interrupts_enabled(env);
+}
+
+static void avr_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb)
+{
+ AVRCPU *cpu = AVR_CPU(cs);
+ CPUAVRState *env = &cpu->env;
+
+ env->pc_w = tb->pc / 2; /* internally PC points to words */
+}
+
+static void avr_cpu_reset(DeviceState *ds)
+{
+ CPUState *cs = CPU(ds);
+ AVRCPU *cpu = AVR_CPU(cs);
+ AVRCPUClass *mcc = AVR_CPU_GET_CLASS(cpu);
+ CPUAVRState *env = &cpu->env;
+
+ mcc->parent_reset(ds);
+
+ env->pc_w = 0;
+ env->sregI = 1;
+ env->sregC = 0;
+ env->sregZ = 0;
+ env->sregN = 0;
+ env->sregV = 0;
+ env->sregS = 0;
+ env->sregH = 0;
+ env->sregT = 0;
+
+ env->rampD = 0;
+ env->rampX = 0;
+ env->rampY = 0;
+ env->rampZ = 0;
+ env->eind = 0;
+ env->sp = 0;
+
+ env->skip = 0;
+
+ memset(env->r, 0, sizeof(env->r));
+}
+
+static void avr_cpu_disas_set_info(CPUState *cpu, disassemble_info *info)
+{
+ info->mach = bfd_arch_avr;
+ info->print_insn = avr_print_insn;
+}
+
+static void avr_cpu_realizefn(DeviceState *dev, Error **errp)
+{
+ CPUState *cs = CPU(dev);
+ AVRCPUClass *mcc = AVR_CPU_GET_CLASS(dev);
+ Error *local_err = NULL;
+
+ cpu_exec_realizefn(cs, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ qemu_init_vcpu(cs);
+ cpu_reset(cs);
+
+ mcc->parent_realize(dev, errp);
+}
+
+static void avr_cpu_set_int(void *opaque, int irq, int level)
+{
+ AVRCPU *cpu = opaque;
+ CPUAVRState *env = &cpu->env;
+ CPUState *cs = CPU(cpu);
+ uint64_t mask = (1ull << irq);
+
+ if (level) {
+ env->intsrc |= mask;
+ cpu_interrupt(cs, CPU_INTERRUPT_HARD);
+ } else {
+ env->intsrc &= ~mask;
+ if (env->intsrc == 0) {
+ cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
+ }
+ }
+}
+
+static void avr_cpu_initfn(Object *obj)
+{
+ AVRCPU *cpu = AVR_CPU(obj);
+
+ cpu_set_cpustate_pointers(cpu);
+
+ /* Set the number of interrupts supported by the CPU. */
+ qdev_init_gpio_in(DEVICE(cpu), avr_cpu_set_int,
+ sizeof(cpu->env.intsrc) * 8);
+}
+
+static ObjectClass *avr_cpu_class_by_name(const char *cpu_model)
+{
+ ObjectClass *oc;
+
+ oc = object_class_by_name(cpu_model);
+ if (object_class_dynamic_cast(oc, TYPE_AVR_CPU) == NULL ||
+ object_class_is_abstract(oc)) {
+ oc = NULL;
+ }
+ return oc;
+}
+
+static void avr_cpu_dump_state(CPUState *cs, FILE *f, int flags)
+{
+ AVRCPU *cpu = AVR_CPU(cs);
+ CPUAVRState *env = &cpu->env;
+ int i;
+
+ qemu_fprintf(f, "\n");
+ qemu_fprintf(f, "PC: %06x\n", env->pc_w * 2); /* PC points to words */
+ qemu_fprintf(f, "SP: %04x\n", env->sp);
+ qemu_fprintf(f, "rampD: %02x\n", env->rampD >> 16);
+ qemu_fprintf(f, "rampX: %02x\n", env->rampX >> 16);
+ qemu_fprintf(f, "rampY: %02x\n", env->rampY >> 16);
+ qemu_fprintf(f, "rampZ: %02x\n", env->rampZ >> 16);
+ qemu_fprintf(f, "EIND: %02x\n", env->eind >> 16);
+ qemu_fprintf(f, "X: %02x%02x\n", env->r[27], env->r[26]);
+ qemu_fprintf(f, "Y: %02x%02x\n", env->r[29], env->r[28]);
+ qemu_fprintf(f, "Z: %02x%02x\n", env->r[31], env->r[30]);
+ qemu_fprintf(f, "SREG: [ %c %c %c %c %c %c %c %c ]\n",
+ env->sregI ? 'I' : '-',
+ env->sregT ? 'T' : '-',
+ env->sregH ? 'H' : '-',
+ env->sregS ? 'S' : '-',
+ env->sregV ? 'V' : '-',
+ env->sregN ? '-' : 'N', /* Zf has negative logic */
+ env->sregZ ? 'Z' : '-',
+ env->sregC ? 'I' : '-');
+ qemu_fprintf(f, "SKIP: %02x\n", env->skip);
+
+ qemu_fprintf(f, "\n");
+ for (i = 0; i < ARRAY_SIZE(env->r); i++) {
+ qemu_fprintf(f, "R[%02d]: %02x ", i, env->r[i]);
+
+ if ((i % 8) == 7) {
+ qemu_fprintf(f, "\n");
+ }
+ }
+ qemu_fprintf(f, "\n");
+}
+
+static void avr_cpu_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ CPUClass *cc = CPU_CLASS(oc);
+ AVRCPUClass *mcc = AVR_CPU_CLASS(oc);
+
+ mcc->parent_realize = dc->realize;
+ dc->realize = avr_cpu_realizefn;
+
+ device_class_set_parent_reset(dc, avr_cpu_reset, &mcc->parent_reset);
+
+ cc->class_by_name = avr_cpu_class_by_name;
+
+ cc->has_work = avr_cpu_has_work;
+ cc->do_interrupt = avr_cpu_do_interrupt;
+ cc->cpu_exec_interrupt = avr_cpu_exec_interrupt;
+ cc->dump_state = avr_cpu_dump_state;
+ cc->set_pc = avr_cpu_set_pc;
+ cc->memory_rw_debug = avr_cpu_memory_rw_debug;
+ cc->get_phys_page_debug = avr_cpu_get_phys_page_debug;
+ cc->tlb_fill = avr_cpu_tlb_fill;
+ cc->vmsd = &vms_avr_cpu;
+ cc->disas_set_info = avr_cpu_disas_set_info;
+ cc->tcg_initialize = avr_cpu_tcg_init;
+ cc->synchronize_from_tb = avr_cpu_synchronize_from_tb;
+ cc->gdb_read_register = avr_cpu_gdb_read_register;
+ cc->gdb_write_register = avr_cpu_gdb_write_register;
+ cc->gdb_num_core_regs = 35;
+ cc->gdb_core_xml_file = "avr-cpu.xml";
+}
+
+/*
+ * Setting features of AVR core type avr5
+ * --------------------------------------
+ *
+ * This type of AVR core is present in the following AVR MCUs:
+ *
+ * ata5702m322, ata5782, ata5790, ata5790n, ata5791, ata5795, ata5831, ata6613c,
+ * ata6614q, ata8210, ata8510, atmega16, atmega16a, atmega161, atmega162,
+ * atmega163, atmega164a, atmega164p, atmega164pa, atmega165, atmega165a,
+ * atmega165p, atmega165pa, atmega168, atmega168a, atmega168p, atmega168pa,
+ * atmega168pb, atmega169, atmega169a, atmega169p, atmega169pa, atmega16hvb,
+ * atmega16hvbrevb, atmega16m1, atmega16u4, atmega32a, atmega32, atmega323,
+ * atmega324a, atmega324p, atmega324pa, atmega325, atmega325a, atmega325p,
+ * atmega325pa, atmega3250, atmega3250a, atmega3250p, atmega3250pa, atmega328,
+ * atmega328p, atmega328pb, atmega329, atmega329a, atmega329p, atmega329pa,
+ * atmega3290, atmega3290a, atmega3290p, atmega3290pa, atmega32c1, atmega32m1,
+ * atmega32u4, atmega32u6, atmega406, atmega64, atmega64a, atmega640, atmega644,
+ * atmega644a, atmega644p, atmega644pa, atmega645, atmega645a, atmega645p,
+ * atmega6450, atmega6450a, atmega6450p, atmega649, atmega649a, atmega649p,
+ * atmega6490, atmega16hva, atmega16hva2, atmega32hvb, atmega6490a, atmega6490p,
+ * atmega64c1, atmega64m1, atmega64hve, atmega64hve2, atmega64rfr2,
+ * atmega644rfr2, atmega32hvbrevb, at90can32, at90can64, at90pwm161, at90pwm216,
+ * at90pwm316, at90scr100, at90usb646, at90usb647, at94k, m3000
+ */
+static void avr_avr5_initfn(Object *obj)
+{
+ AVRCPU *cpu = AVR_CPU(obj);
+ CPUAVRState *env = &cpu->env;
+
+ set_avr_feature(env, AVR_FEATURE_LPM);
+ set_avr_feature(env, AVR_FEATURE_IJMP_ICALL);
+ set_avr_feature(env, AVR_FEATURE_ADIW_SBIW);
+ set_avr_feature(env, AVR_FEATURE_SRAM);
+ set_avr_feature(env, AVR_FEATURE_BREAK);
+
+ set_avr_feature(env, AVR_FEATURE_2_BYTE_PC);
+ set_avr_feature(env, AVR_FEATURE_2_BYTE_SP);
+ set_avr_feature(env, AVR_FEATURE_JMP_CALL);
+ set_avr_feature(env, AVR_FEATURE_LPMX);
+ set_avr_feature(env, AVR_FEATURE_MOVW);
+ set_avr_feature(env, AVR_FEATURE_MUL);
+}
+
+/*
+ * Setting features of AVR core type avr51
+ * --------------------------------------
+ *
+ * This type of AVR core is present in the following AVR MCUs:
+ *
+ * atmega128, atmega128a, atmega1280, atmega1281, atmega1284, atmega1284p,
+ * atmega128rfa1, atmega128rfr2, atmega1284rfr2, at90can128, at90usb1286,
+ * at90usb1287
+ */
+static void avr_avr51_initfn(Object *obj)
+{
+ AVRCPU *cpu = AVR_CPU(obj);
+ CPUAVRState *env = &cpu->env;
+
+ set_avr_feature(env, AVR_FEATURE_LPM);
+ set_avr_feature(env, AVR_FEATURE_IJMP_ICALL);
+ set_avr_feature(env, AVR_FEATURE_ADIW_SBIW);
+ set_avr_feature(env, AVR_FEATURE_SRAM);
+ set_avr_feature(env, AVR_FEATURE_BREAK);
+
+ set_avr_feature(env, AVR_FEATURE_2_BYTE_PC);
+ set_avr_feature(env, AVR_FEATURE_2_BYTE_SP);
+ set_avr_feature(env, AVR_FEATURE_RAMPZ);
+ set_avr_feature(env, AVR_FEATURE_ELPMX);
+ set_avr_feature(env, AVR_FEATURE_ELPM);
+ set_avr_feature(env, AVR_FEATURE_JMP_CALL);
+ set_avr_feature(env, AVR_FEATURE_LPMX);
+ set_avr_feature(env, AVR_FEATURE_MOVW);
+ set_avr_feature(env, AVR_FEATURE_MUL);
+}
+
+/*
+ * Setting features of AVR core type avr6
+ * --------------------------------------
+ *
+ * This type of AVR core is present in the following AVR MCUs:
+ *
+ * atmega2560, atmega2561, atmega256rfr2, atmega2564rfr2
+ */
+static void avr_avr6_initfn(Object *obj)
+{
+ AVRCPU *cpu = AVR_CPU(obj);
+ CPUAVRState *env = &cpu->env;
+
+ set_avr_feature(env, AVR_FEATURE_LPM);
+ set_avr_feature(env, AVR_FEATURE_IJMP_ICALL);
+ set_avr_feature(env, AVR_FEATURE_ADIW_SBIW);
+ set_avr_feature(env, AVR_FEATURE_SRAM);
+ set_avr_feature(env, AVR_FEATURE_BREAK);
+
+ set_avr_feature(env, AVR_FEATURE_3_BYTE_PC);
+ set_avr_feature(env, AVR_FEATURE_2_BYTE_SP);
+ set_avr_feature(env, AVR_FEATURE_RAMPZ);
+ set_avr_feature(env, AVR_FEATURE_EIJMP_EICALL);
+ set_avr_feature(env, AVR_FEATURE_ELPMX);
+ set_avr_feature(env, AVR_FEATURE_ELPM);
+ set_avr_feature(env, AVR_FEATURE_JMP_CALL);
+ set_avr_feature(env, AVR_FEATURE_LPMX);
+ set_avr_feature(env, AVR_FEATURE_MOVW);
+ set_avr_feature(env, AVR_FEATURE_MUL);
+}
+
+typedef struct AVRCPUInfo {
+ const char *name;
+ void (*initfn)(Object *obj);
+} AVRCPUInfo;
+
+
+static void avr_cpu_list_entry(gpointer data, gpointer user_data)
+{
+ const char *typename = object_class_get_name(OBJECT_CLASS(data));
+
+ qemu_printf("%s\n", typename);
+}
+
+void avr_cpu_list(void)
+{
+ GSList *list;
+ list = object_class_get_list_sorted(TYPE_AVR_CPU, false);
+ g_slist_foreach(list, avr_cpu_list_entry, NULL);
+ g_slist_free(list);
+}
+
+#define DEFINE_AVR_CPU_TYPE(model, initfn) \
+ { \
+ .parent = TYPE_AVR_CPU, \
+ .instance_init = initfn, \
+ .name = AVR_CPU_TYPE_NAME(model), \
+ }
+
+static const TypeInfo avr_cpu_type_info[] = {
+ {
+ .name = TYPE_AVR_CPU,
+ .parent = TYPE_CPU,
+ .instance_size = sizeof(AVRCPU),
+ .instance_init = avr_cpu_initfn,
+ .class_size = sizeof(AVRCPUClass),
+ .class_init = avr_cpu_class_init,
+ .abstract = true,
+ },
+ DEFINE_AVR_CPU_TYPE("avr5", avr_avr5_initfn),
+ DEFINE_AVR_CPU_TYPE("avr51", avr_avr51_initfn),
+ DEFINE_AVR_CPU_TYPE("avr6", avr_avr6_initfn),
+};
+
+DEFINE_TYPES(avr_cpu_type_info)
--- /dev/null
+/*
+ * QEMU AVR CPU
+ *
+ * Copyright (c) 2016-2020 Michael Rolnik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#ifndef QEMU_AVR_CPU_H
+#define QEMU_AVR_CPU_H
+
+#include "cpu-qom.h"
+#include "exec/cpu-defs.h"
+
+#ifdef CONFIG_USER_ONLY
+#error "AVR 8-bit does not support user mode"
+#endif
+
+#define AVR_CPU_TYPE_SUFFIX "-" TYPE_AVR_CPU
+#define AVR_CPU_TYPE_NAME(name) (name AVR_CPU_TYPE_SUFFIX)
+#define CPU_RESOLVING_TYPE TYPE_AVR_CPU
+
+#define TCG_GUEST_DEFAULT_MO 0
+
+/*
+ * AVR has two memory spaces, data & code.
+ * e.g. both have 0 address
+ * ST/LD instructions access data space
+ * LPM/SPM and instruction fetching access code memory space
+ */
+#define MMU_CODE_IDX 0
+#define MMU_DATA_IDX 1
+
+#define EXCP_RESET 1
+#define EXCP_INT(n) (EXCP_RESET + (n) + 1)
+
+/* Number of CPU registers */
+#define NUMBER_OF_CPU_REGISTERS 32
+/* Number of IO registers accessible by ld/st/in/out */
+#define NUMBER_OF_IO_REGISTERS 64
+
+/*
+ * Offsets of AVR memory regions in host memory space.
+ *
+ * This is needed because the AVR has separate code and data address
+ * spaces that both have start from zero but have to go somewhere in
+ * host memory.
+ *
+ * It's also useful to know where some things are, like the IO registers.
+ */
+/* Flash program memory */
+#define OFFSET_CODE 0x00000000
+/* CPU registers, IO registers, and SRAM */
+#define OFFSET_DATA 0x00800000
+/* CPU registers specifically, these are mapped at the start of data */
+#define OFFSET_CPU_REGISTERS OFFSET_DATA
+/*
+ * IO registers, including status register, stack pointer, and memory
+ * mapped peripherals, mapped just after CPU registers
+ */
+#define OFFSET_IO_REGISTERS (OFFSET_DATA + NUMBER_OF_CPU_REGISTERS)
+
+typedef enum AVRFeature {
+ AVR_FEATURE_SRAM,
+
+ AVR_FEATURE_1_BYTE_PC,
+ AVR_FEATURE_2_BYTE_PC,
+ AVR_FEATURE_3_BYTE_PC,
+
+ AVR_FEATURE_1_BYTE_SP,
+ AVR_FEATURE_2_BYTE_SP,
+
+ AVR_FEATURE_BREAK,
+ AVR_FEATURE_DES,
+ AVR_FEATURE_RMW, /* Read Modify Write - XCH LAC LAS LAT */
+
+ AVR_FEATURE_EIJMP_EICALL,
+ AVR_FEATURE_IJMP_ICALL,
+ AVR_FEATURE_JMP_CALL,
+
+ AVR_FEATURE_ADIW_SBIW,
+
+ AVR_FEATURE_SPM,
+ AVR_FEATURE_SPMX,
+
+ AVR_FEATURE_ELPMX,
+ AVR_FEATURE_ELPM,
+ AVR_FEATURE_LPMX,
+ AVR_FEATURE_LPM,
+
+ AVR_FEATURE_MOVW,
+ AVR_FEATURE_MUL,
+ AVR_FEATURE_RAMPD,
+ AVR_FEATURE_RAMPX,
+ AVR_FEATURE_RAMPY,
+ AVR_FEATURE_RAMPZ,
+} AVRFeature;
+
+typedef struct CPUAVRState CPUAVRState;
+
+struct CPUAVRState {
+ uint32_t pc_w; /* 0x003fffff up to 22 bits */
+
+ uint32_t sregC; /* 0x00000001 1 bit */
+ uint32_t sregZ; /* 0x00000001 1 bit */
+ uint32_t sregN; /* 0x00000001 1 bit */
+ uint32_t sregV; /* 0x00000001 1 bit */
+ uint32_t sregS; /* 0x00000001 1 bit */
+ uint32_t sregH; /* 0x00000001 1 bit */
+ uint32_t sregT; /* 0x00000001 1 bit */
+ uint32_t sregI; /* 0x00000001 1 bit */
+
+ uint32_t rampD; /* 0x00ff0000 8 bits */
+ uint32_t rampX; /* 0x00ff0000 8 bits */
+ uint32_t rampY; /* 0x00ff0000 8 bits */
+ uint32_t rampZ; /* 0x00ff0000 8 bits */
+ uint32_t eind; /* 0x00ff0000 8 bits */
+
+ uint32_t r[NUMBER_OF_CPU_REGISTERS]; /* 8 bits each */
+ uint32_t sp; /* 16 bits */
+
+ uint32_t skip; /* if set skip instruction */
+
+ uint64_t intsrc; /* interrupt sources */
+ bool fullacc; /* CPU/MEM if true MEM only otherwise */
+
+ uint64_t features;
+};
+
+/**
+ * AVRCPU:
+ * @env: #CPUAVRState
+ *
+ * A AVR CPU.
+ */
+typedef struct AVRCPU {
+ /*< private >*/
+ CPUState parent_obj;
+ /*< public >*/
+
+ CPUNegativeOffsetState neg;
+ CPUAVRState env;
+} AVRCPU;
+
+extern const struct VMStateDescription vms_avr_cpu;
+
+void avr_cpu_do_interrupt(CPUState *cpu);
+bool avr_cpu_exec_interrupt(CPUState *cpu, int int_req);
+hwaddr avr_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
+int avr_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg);
+int avr_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
+int avr_print_insn(bfd_vma addr, disassemble_info *info);
+
+static inline int avr_feature(CPUAVRState *env, AVRFeature feature)
+{
+ return (env->features & (1U << feature)) != 0;
+}
+
+static inline void set_avr_feature(CPUAVRState *env, int feature)
+{
+ env->features |= (1U << feature);
+}
+
+#define cpu_list avr_cpu_list
+#define cpu_signal_handler cpu_avr_signal_handler
+#define cpu_mmu_index avr_cpu_mmu_index
+
+static inline int avr_cpu_mmu_index(CPUAVRState *env, bool ifetch)
+{
+ return ifetch ? MMU_CODE_IDX : MMU_DATA_IDX;
+}
+
+void avr_cpu_tcg_init(void);
+
+void avr_cpu_list(void);
+int cpu_avr_exec(CPUState *cpu);
+int cpu_avr_signal_handler(int host_signum, void *pinfo, void *puc);
+int avr_cpu_memory_rw_debug(CPUState *cs, vaddr address, uint8_t *buf,
+ int len, bool is_write);
+
+enum {
+ TB_FLAGS_FULL_ACCESS = 1,
+ TB_FLAGS_SKIP = 2,
+};
+
+static inline void cpu_get_tb_cpu_state(CPUAVRState *env, target_ulong *pc,
+ target_ulong *cs_base, uint32_t *pflags)
+{
+ uint32_t flags = 0;
+
+ *pc = env->pc_w * 2;
+ *cs_base = 0;
+
+ if (env->fullacc) {
+ flags |= TB_FLAGS_FULL_ACCESS;
+ }
+ if (env->skip) {
+ flags |= TB_FLAGS_SKIP;
+ }
+
+ *pflags = flags;
+}
+
+static inline int cpu_interrupts_enabled(CPUAVRState *env)
+{
+ return env->sregI != 0;
+}
+
+static inline uint8_t cpu_get_sreg(CPUAVRState *env)
+{
+ uint8_t sreg;
+ sreg = (env->sregC) << 0
+ | (env->sregZ) << 1
+ | (env->sregN) << 2
+ | (env->sregV) << 3
+ | (env->sregS) << 4
+ | (env->sregH) << 5
+ | (env->sregT) << 6
+ | (env->sregI) << 7;
+ return sreg;
+}
+
+static inline void cpu_set_sreg(CPUAVRState *env, uint8_t sreg)
+{
+ env->sregC = (sreg >> 0) & 0x01;
+ env->sregZ = (sreg >> 1) & 0x01;
+ env->sregN = (sreg >> 2) & 0x01;
+ env->sregV = (sreg >> 3) & 0x01;
+ env->sregS = (sreg >> 4) & 0x01;
+ env->sregH = (sreg >> 5) & 0x01;
+ env->sregT = (sreg >> 6) & 0x01;
+ env->sregI = (sreg >> 7) & 0x01;
+}
+
+bool avr_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
+ MMUAccessType access_type, int mmu_idx,
+ bool probe, uintptr_t retaddr);
+
+typedef CPUAVRState CPUArchState;
+typedef AVRCPU ArchCPU;
+
+#include "exec/cpu-all.h"
+
+#endif /* !defined (QEMU_AVR_CPU_H) */
--- /dev/null
+/*
+ * AVR disassembler
+ *
+ * Copyright (c) 2019-2020 Richard Henderson <rth@twiddle.net>
+ * Copyright (c) 2019-2020 Michael Rolnik <mrolnik@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+
+typedef struct {
+ disassemble_info *info;
+ uint16_t next_word;
+ bool next_word_used;
+} DisasContext;
+
+static int to_regs_16_31_by_one(DisasContext *ctx, int indx)
+{
+ return 16 + (indx % 16);
+}
+
+static int to_regs_16_23_by_one(DisasContext *ctx, int indx)
+{
+ return 16 + (indx % 8);
+}
+
+static int to_regs_24_30_by_two(DisasContext *ctx, int indx)
+{
+ return 24 + (indx % 4) * 2;
+}
+
+static int to_regs_00_30_by_two(DisasContext *ctx, int indx)
+{
+ return (indx % 16) * 2;
+}
+
+static uint16_t next_word(DisasContext *ctx)
+{
+ ctx->next_word_used = true;
+ return ctx->next_word;
+}
+
+static int append_16(DisasContext *ctx, int x)
+{
+ return x << 16 | next_word(ctx);
+}
+
+/* Include the auto-generated decoder. */
+static bool decode_insn(DisasContext *ctx, uint16_t insn);
+#include "decode_insn.inc.c"
+
+#define output(mnemonic, format, ...) \
+ (pctx->info->fprintf_func(pctx->info->stream, "%-9s " format, \
+ mnemonic, ##__VA_ARGS__))
+
+int avr_print_insn(bfd_vma addr, disassemble_info *info)
+{
+ DisasContext ctx;
+ DisasContext *pctx = &ctx;
+ bfd_byte buffer[4];
+ uint16_t insn;
+ int status;
+
+ ctx.info = info;
+
+ status = info->read_memory_func(addr, buffer, 4, info);
+ if (status != 0) {
+ info->memory_error_func(status, addr, info);
+ return -1;
+ }
+ insn = bfd_getl16(buffer);
+ ctx.next_word = bfd_getl16(buffer + 2);
+ ctx.next_word_used = false;
+
+ if (!decode_insn(&ctx, insn)) {
+ output(".db", "0x%02x, 0x%02x", buffer[0], buffer[1]);
+ }
+
+ return ctx.next_word_used ? 4 : 2;
+}
+
+
+#define INSN(opcode, format, ...) \
+static bool trans_##opcode(DisasContext *pctx, arg_##opcode * a) \
+{ \
+ output(#opcode, format, ##__VA_ARGS__); \
+ return true; \
+}
+
+#define INSN_MNEMONIC(opcode, mnemonic, format, ...) \
+static bool trans_##opcode(DisasContext *pctx, arg_##opcode * a) \
+{ \
+ output(mnemonic, format, ##__VA_ARGS__); \
+ return true; \
+}
+
+/*
+ * C Z N V S H T I
+ * 0 1 2 3 4 5 6 7
+ */
+static const char brbc[][5] = {
+ "BRCC", "BRNE", "BRPL", "BRVC", "BRGE", "BRHC", "BRTC", "BRID"
+};
+
+static const char brbs[][5] = {
+ "BRCS", "BREQ", "BRMI", "BRVS", "BRLT", "BRHS", "BRTS", "BRIE"
+};
+
+static const char bset[][4] = {
+ "SEC", "SEZ", "SEN", "SEZ", "SES", "SEH", "SET", "SEI"
+};
+
+static const char bclr[][4] = {
+ "CLC", "CLZ", "CLN", "CLZ", "CLS", "CLH", "CLT", "CLI"
+};
+
+/*
+ * Arithmetic Instructions
+ */
+INSN(ADD, "r%d, r%d", a->rd, a->rr)
+INSN(ADC, "r%d, r%d", a->rd, a->rr)
+INSN(ADIW, "r%d:r%d, %d", a->rd + 1, a->rd, a->imm)
+INSN(SUB, "r%d, r%d", a->rd, a->rr)
+INSN(SUBI, "r%d, %d", a->rd, a->imm)
+INSN(SBC, "r%d, r%d", a->rd, a->rr)
+INSN(SBCI, "r%d, %d", a->rd, a->imm)
+INSN(SBIW, "r%d:r%d, %d", a->rd + 1, a->rd, a->imm)
+INSN(AND, "r%d, r%d", a->rd, a->rr)
+INSN(ANDI, "r%d, %d", a->rd, a->imm)
+INSN(OR, "r%d, r%d", a->rd, a->rr)
+INSN(ORI, "r%d, %d", a->rd, a->imm)
+INSN(EOR, "r%d, r%d", a->rd, a->rr)
+INSN(COM, "r%d", a->rd)
+INSN(NEG, "r%d", a->rd)
+INSN(INC, "r%d", a->rd)
+INSN(DEC, "r%d", a->rd)
+INSN(MUL, "r%d, r%d", a->rd, a->rr)
+INSN(MULS, "r%d, r%d", a->rd, a->rr)
+INSN(MULSU, "r%d, r%d", a->rd, a->rr)
+INSN(FMUL, "r%d, r%d", a->rd, a->rr)
+INSN(FMULS, "r%d, r%d", a->rd, a->rr)
+INSN(FMULSU, "r%d, r%d", a->rd, a->rr)
+INSN(DES, "%d", a->imm)
+
+/*
+ * Branch Instructions
+ */
+INSN(RJMP, ".%+d", a->imm * 2)
+INSN(IJMP, "")
+INSN(EIJMP, "")
+INSN(JMP, "0x%x", a->imm * 2)
+INSN(RCALL, ".%+d", a->imm * 2)
+INSN(ICALL, "")
+INSN(EICALL, "")
+INSN(CALL, "0x%x", a->imm * 2)
+INSN(RET, "")
+INSN(RETI, "")
+INSN(CPSE, "r%d, r%d", a->rd, a->rr)
+INSN(CP, "r%d, r%d", a->rd, a->rr)
+INSN(CPC, "r%d, r%d", a->rd, a->rr)
+INSN(CPI, "r%d, %d", a->rd, a->imm)
+INSN(SBRC, "r%d, %d", a->rr, a->bit)
+INSN(SBRS, "r%d, %d", a->rr, a->bit)
+INSN(SBIC, "$%d, %d", a->reg, a->bit)
+INSN(SBIS, "$%d, %d", a->reg, a->bit)
+INSN_MNEMONIC(BRBS, brbs[a->bit], ".%+d", a->imm * 2)
+INSN_MNEMONIC(BRBC, brbc[a->bit], ".%+d", a->imm * 2)
+
+/*
+ * Data Transfer Instructions
+ */
+INSN(MOV, "r%d, r%d", a->rd, a->rr)
+INSN(MOVW, "r%d:r%d, r%d:r%d", a->rd + 1, a->rd, a->rr + 1, a->rr)
+INSN(LDI, "r%d, %d", a->rd, a->imm)
+INSN(LDS, "r%d, %d", a->rd, a->imm)
+INSN(LDX1, "r%d, X", a->rd)
+INSN(LDX2, "r%d, X+", a->rd)
+INSN(LDX3, "r%d, -X", a->rd)
+INSN(LDY2, "r%d, Y+", a->rd)
+INSN(LDY3, "r%d, -Y", a->rd)
+INSN(LDZ2, "r%d, Z+", a->rd)
+INSN(LDZ3, "r%d, -Z", a->rd)
+INSN(LDDY, "r%d, Y+%d", a->rd, a->imm)
+INSN(LDDZ, "r%d, Z+%d", a->rd, a->imm)
+INSN(STS, "%d, r%d", a->imm, a->rd)
+INSN(STX1, "X, r%d", a->rr)
+INSN(STX2, "X+, r%d", a->rr)
+INSN(STX3, "-X, r%d", a->rr)
+INSN(STY2, "Y+, r%d", a->rd)
+INSN(STY3, "-Y, r%d", a->rd)
+INSN(STZ2, "Z+, r%d", a->rd)
+INSN(STZ3, "-Z, r%d", a->rd)
+INSN(STDY, "Y+%d, r%d", a->imm, a->rd)
+INSN(STDZ, "Z+%d, r%d", a->imm, a->rd)
+INSN(LPM1, "")
+INSN(LPM2, "r%d, Z", a->rd)
+INSN(LPMX, "r%d, Z+", a->rd)
+INSN(ELPM1, "")
+INSN(ELPM2, "r%d, Z", a->rd)
+INSN(ELPMX, "r%d, Z+", a->rd)
+INSN(SPM, "")
+INSN(SPMX, "Z+")
+INSN(IN, "r%d, $%d", a->rd, a->imm)
+INSN(OUT, "$%d, r%d", a->imm, a->rd)
+INSN(PUSH, "r%d", a->rd)
+INSN(POP, "r%d", a->rd)
+INSN(XCH, "Z, r%d", a->rd)
+INSN(LAC, "Z, r%d", a->rd)
+INSN(LAS, "Z, r%d", a->rd)
+INSN(LAT, "Z, r%d", a->rd)
+
+/*
+ * Bit and Bit-test Instructions
+ */
+INSN(LSR, "r%d", a->rd)
+INSN(ROR, "r%d", a->rd)
+INSN(ASR, "r%d", a->rd)
+INSN(SWAP, "r%d", a->rd)
+INSN(SBI, "$%d, %d", a->reg, a->bit)
+INSN(CBI, "%d, %d", a->reg, a->bit)
+INSN(BST, "r%d, %d", a->rd, a->bit)
+INSN(BLD, "r%d, %d", a->rd, a->bit)
+INSN_MNEMONIC(BSET, bset[a->bit], "")
+INSN_MNEMONIC(BCLR, bclr[a->bit], "")
+
+/*
+ * MCU Control Instructions
+ */
+INSN(BREAK, "")
+INSN(NOP, "")
+INSN(SLEEP, "")
+INSN(WDR, "")
--- /dev/null
+/*
+ * QEMU AVR gdbstub
+ *
+ * Copyright (c) 2016-2020 Michael Rolnik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "qemu/osdep.h"
+#include "exec/gdbstub.h"
+
+int avr_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
+{
+ AVRCPU *cpu = AVR_CPU(cs);
+ CPUAVRState *env = &cpu->env;
+
+ /* R */
+ if (n < 32) {
+ return gdb_get_reg8(mem_buf, env->r[n]);
+ }
+
+ /* SREG */
+ if (n == 32) {
+ uint8_t sreg = cpu_get_sreg(env);
+
+ return gdb_get_reg8(mem_buf, sreg);
+ }
+
+ /* SP */
+ if (n == 33) {
+ return gdb_get_reg16(mem_buf, env->sp & 0x0000ffff);
+ }
+
+ /* PC */
+ if (n == 34) {
+ return gdb_get_reg32(mem_buf, env->pc_w * 2);
+ }
+
+ return 0;
+}
+
+int avr_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
+{
+ AVRCPU *cpu = AVR_CPU(cs);
+ CPUAVRState *env = &cpu->env;
+
+ /* R */
+ if (n < 32) {
+ env->r[n] = *mem_buf;
+ return 1;
+ }
+
+ /* SREG */
+ if (n == 32) {
+ cpu_set_sreg(env, *mem_buf);
+ return 1;
+ }
+
+ /* SP */
+ if (n == 33) {
+ env->sp = lduw_p(mem_buf);
+ return 2;
+ }
+
+ /* PC */
+ if (n == 34) {
+ env->pc_w = ldl_p(mem_buf) / 2;
+ return 4;
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * QEMU AVR CPU helpers
+ *
+ * Copyright (c) 2016-2020 Michael Rolnik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "exec/exec-all.h"
+#include "exec/address-spaces.h"
+#include "exec/helper-proto.h"
+
+bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
+{
+ bool ret = false;
+ CPUClass *cc = CPU_GET_CLASS(cs);
+ AVRCPU *cpu = AVR_CPU(cs);
+ CPUAVRState *env = &cpu->env;
+
+ if (interrupt_request & CPU_INTERRUPT_RESET) {
+ if (cpu_interrupts_enabled(env)) {
+ cs->exception_index = EXCP_RESET;
+ cc->do_interrupt(cs);
+
+ cs->interrupt_request &= ~CPU_INTERRUPT_RESET;
+
+ ret = true;
+ }
+ }
+ if (interrupt_request & CPU_INTERRUPT_HARD) {
+ if (cpu_interrupts_enabled(env) && env->intsrc != 0) {
+ int index = ctz32(env->intsrc);
+ cs->exception_index = EXCP_INT(index);
+ cc->do_interrupt(cs);
+
+ env->intsrc &= env->intsrc - 1; /* clear the interrupt */
+ cs->interrupt_request &= ~CPU_INTERRUPT_HARD;
+
+ ret = true;
+ }
+ }
+ return ret;
+}
+
+void avr_cpu_do_interrupt(CPUState *cs)
+{
+ AVRCPU *cpu = AVR_CPU(cs);
+ CPUAVRState *env = &cpu->env;
+
+ uint32_t ret = env->pc_w;
+ int vector = 0;
+ int size = avr_feature(env, AVR_FEATURE_JMP_CALL) ? 2 : 1;
+ int base = 0;
+
+ if (cs->exception_index == EXCP_RESET) {
+ vector = 0;
+ } else if (env->intsrc != 0) {
+ vector = ctz32(env->intsrc) + 1;
+ }
+
+ if (avr_feature(env, AVR_FEATURE_3_BYTE_PC)) {
+ cpu_stb_data(env, env->sp--, (ret & 0x0000ff));
+ cpu_stb_data(env, env->sp--, (ret & 0x00ff00) >> 8);
+ cpu_stb_data(env, env->sp--, (ret & 0xff0000) >> 16);
+ } else if (avr_feature(env, AVR_FEATURE_2_BYTE_PC)) {
+ cpu_stb_data(env, env->sp--, (ret & 0x0000ff));
+ cpu_stb_data(env, env->sp--, (ret & 0x00ff00) >> 8);
+ } else {
+ cpu_stb_data(env, env->sp--, (ret & 0x0000ff));
+ }
+
+ env->pc_w = base + vector * size;
+ env->sregI = 0; /* clear Global Interrupt Flag */
+
+ cs->exception_index = -1;
+}
+
+int avr_cpu_memory_rw_debug(CPUState *cs, vaddr addr, uint8_t *buf,
+ int len, bool is_write)
+{
+ return cpu_memory_rw_debug(cs, addr, buf, len, is_write);
+}
+
+hwaddr avr_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
+{
+ return addr; /* I assume 1:1 address correspondance */
+}
+
+bool avr_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
+ MMUAccessType access_type, int mmu_idx,
+ bool probe, uintptr_t retaddr)
+{
+ int prot = 0;
+ MemTxAttrs attrs = {};
+ uint32_t paddr;
+
+ address &= TARGET_PAGE_MASK;
+
+ if (mmu_idx == MMU_CODE_IDX) {
+ /* access to code in flash */
+ paddr = OFFSET_CODE + address;
+ prot = PAGE_READ | PAGE_EXEC;
+ if (paddr + TARGET_PAGE_SIZE > OFFSET_DATA) {
+ error_report("execution left flash memory");
+ abort();
+ }
+ } else if (address < NUMBER_OF_CPU_REGISTERS + NUMBER_OF_IO_REGISTERS) {
+ /*
+ * access to CPU registers, exit and rebuilt this TB to use full access
+ * incase it touches specially handled registers like SREG or SP
+ */
+ AVRCPU *cpu = AVR_CPU(cs);
+ CPUAVRState *env = &cpu->env;
+ env->fullacc = 1;
+ cpu_loop_exit_restore(cs, retaddr);
+ } else {
+ /* access to memory. nothing special */
+ paddr = OFFSET_DATA + address;
+ prot = PAGE_READ | PAGE_WRITE;
+ }
+
+ tlb_set_page_with_attrs(cs, address, paddr, attrs, prot,
+ mmu_idx, TARGET_PAGE_SIZE);
+
+ return true;
+}
+
+/*
+ * helpers
+ */
+
+void helper_sleep(CPUAVRState *env)
+{
+ CPUState *cs = env_cpu(env);
+
+ cs->exception_index = EXCP_HLT;
+ cpu_loop_exit(cs);
+}
+
+void helper_unsupported(CPUAVRState *env)
+{
+ CPUState *cs = env_cpu(env);
+
+ /*
+ * I count not find what happens on the real platform, so
+ * it's EXCP_DEBUG for meanwhile
+ */
+ cs->exception_index = EXCP_DEBUG;
+ if (qemu_loglevel_mask(LOG_UNIMP)) {
+ qemu_log("UNSUPPORTED\n");
+ cpu_dump_state(cs, stderr, 0);
+ }
+ cpu_loop_exit(cs);
+}
+
+void helper_debug(CPUAVRState *env)
+{
+ CPUState *cs = env_cpu(env);
+
+ cs->exception_index = EXCP_DEBUG;
+ cpu_loop_exit(cs);
+}
+
+void helper_break(CPUAVRState *env)
+{
+ CPUState *cs = env_cpu(env);
+
+ cs->exception_index = EXCP_DEBUG;
+ cpu_loop_exit(cs);
+}
+
+void helper_wdr(CPUAVRState *env)
+{
+ CPUState *cs = env_cpu(env);
+
+ /* WD is not implemented yet, placeholder */
+ cs->exception_index = EXCP_DEBUG;
+ cpu_loop_exit(cs);
+}
+
+/*
+ * This function implements IN instruction
+ *
+ * It does the following
+ * a. if an IO register belongs to CPU, its value is read and returned
+ * b. otherwise io address is translated to mem address and physical memory
+ * is read.
+ * c. it caches the value for sake of SBI, SBIC, SBIS & CBI implementation
+ *
+ */
+target_ulong helper_inb(CPUAVRState *env, uint32_t port)
+{
+ target_ulong data = 0;
+
+ switch (port) {
+ case 0x38: /* RAMPD */
+ data = 0xff & (env->rampD >> 16);
+ break;
+ case 0x39: /* RAMPX */
+ data = 0xff & (env->rampX >> 16);
+ break;
+ case 0x3a: /* RAMPY */
+ data = 0xff & (env->rampY >> 16);
+ break;
+ case 0x3b: /* RAMPZ */
+ data = 0xff & (env->rampZ >> 16);
+ break;
+ case 0x3c: /* EIND */
+ data = 0xff & (env->eind >> 16);
+ break;
+ case 0x3d: /* SPL */
+ data = env->sp & 0x00ff;
+ break;
+ case 0x3e: /* SPH */
+ data = env->sp >> 8;
+ break;
+ case 0x3f: /* SREG */
+ data = cpu_get_sreg(env);
+ break;
+ default:
+ /* not a special register, pass to normal memory access */
+ data = address_space_ldub(&address_space_memory,
+ OFFSET_IO_REGISTERS + port,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ }
+
+ return data;
+}
+
+/*
+ * This function implements OUT instruction
+ *
+ * It does the following
+ * a. if an IO register belongs to CPU, its value is written into the register
+ * b. otherwise io address is translated to mem address and physical memory
+ * is written.
+ * c. it caches the value for sake of SBI, SBIC, SBIS & CBI implementation
+ *
+ */
+void helper_outb(CPUAVRState *env, uint32_t port, uint32_t data)
+{
+ data &= 0x000000ff;
+
+ switch (port) {
+ case 0x38: /* RAMPD */
+ if (avr_feature(env, AVR_FEATURE_RAMPD)) {
+ env->rampD = (data & 0xff) << 16;
+ }
+ break;
+ case 0x39: /* RAMPX */
+ if (avr_feature(env, AVR_FEATURE_RAMPX)) {
+ env->rampX = (data & 0xff) << 16;
+ }
+ break;
+ case 0x3a: /* RAMPY */
+ if (avr_feature(env, AVR_FEATURE_RAMPY)) {
+ env->rampY = (data & 0xff) << 16;
+ }
+ break;
+ case 0x3b: /* RAMPZ */
+ if (avr_feature(env, AVR_FEATURE_RAMPZ)) {
+ env->rampZ = (data & 0xff) << 16;
+ }
+ break;
+ case 0x3c: /* EIDN */
+ env->eind = (data & 0xff) << 16;
+ break;
+ case 0x3d: /* SPL */
+ env->sp = (env->sp & 0xff00) | (data);
+ break;
+ case 0x3e: /* SPH */
+ if (avr_feature(env, AVR_FEATURE_2_BYTE_SP)) {
+ env->sp = (env->sp & 0x00ff) | (data << 8);
+ }
+ break;
+ case 0x3f: /* SREG */
+ cpu_set_sreg(env, data);
+ break;
+ default:
+ /* not a special register, pass to normal memory access */
+ address_space_stb(&address_space_memory, OFFSET_IO_REGISTERS + port,
+ data, MEMTXATTRS_UNSPECIFIED, NULL);
+ }
+}
+
+/*
+ * this function implements LD instruction when there is a posibility to read
+ * from a CPU register
+ */
+target_ulong helper_fullrd(CPUAVRState *env, uint32_t addr)
+{
+ uint8_t data;
+
+ env->fullacc = false;
+
+ if (addr < NUMBER_OF_CPU_REGISTERS) {
+ /* CPU registers */
+ data = env->r[addr];
+ } else if (addr < NUMBER_OF_CPU_REGISTERS + NUMBER_OF_IO_REGISTERS) {
+ /* IO registers */
+ data = helper_inb(env, addr - NUMBER_OF_CPU_REGISTERS);
+ } else {
+ /* memory */
+ data = address_space_ldub(&address_space_memory, OFFSET_DATA + addr,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ }
+ return data;
+}
+
+/*
+ * this function implements ST instruction when there is a posibility to write
+ * into a CPU register
+ */
+void helper_fullwr(CPUAVRState *env, uint32_t data, uint32_t addr)
+{
+ env->fullacc = false;
+
+ /* Following logic assumes this: */
+ assert(OFFSET_CPU_REGISTERS == OFFSET_DATA);
+ assert(OFFSET_IO_REGISTERS == OFFSET_CPU_REGISTERS +
+ NUMBER_OF_CPU_REGISTERS);
+
+ if (addr < NUMBER_OF_CPU_REGISTERS) {
+ /* CPU registers */
+ env->r[addr] = data;
+ } else if (addr < NUMBER_OF_CPU_REGISTERS + NUMBER_OF_IO_REGISTERS) {
+ /* IO registers */
+ helper_outb(env, addr - NUMBER_OF_CPU_REGISTERS, data);
+ } else {
+ /* memory */
+ address_space_stb(&address_space_memory, OFFSET_DATA + addr, data,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ }
+}
--- /dev/null
+/*
+ * QEMU AVR CPU helpers
+ *
+ * Copyright (c) 2016-2020 Michael Rolnik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+DEF_HELPER_1(wdr, void, env)
+DEF_HELPER_1(debug, void, env)
+DEF_HELPER_1(break, void, env)
+DEF_HELPER_1(sleep, void, env)
+DEF_HELPER_1(unsupported, void, env)
+DEF_HELPER_3(outb, void, env, i32, i32)
+DEF_HELPER_2(inb, tl, env, i32)
+DEF_HELPER_3(fullwr, void, env, i32, i32)
+DEF_HELPER_2(fullrd, tl, env, i32)
--- /dev/null
+#
+# AVR instruction decode definitions.
+#
+# Copyright (c) 2019-2020 Michael Rolnik <mrolnik@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see <http://www.gnu.org/licenses/>.
+#
+
+#
+# regs_16_31_by_one = [16 .. 31]
+# regs_16_23_by_one = [16 .. 23]
+# regs_24_30_by_two = [24, 26, 28, 30]
+# regs_00_30_by_two = [0, 2, 4, 6, 8, .. 30]
+
+%rd 4:5
+%rr 9:1 0:4
+
+%rd_a 4:4 !function=to_regs_16_31_by_one
+%rd_b 4:3 !function=to_regs_16_23_by_one
+%rd_c 4:2 !function=to_regs_24_30_by_two
+%rr_a 0:4 !function=to_regs_16_31_by_one
+%rr_b 0:3 !function=to_regs_16_23_by_one
+
+%imm6 6:2 0:4
+%imm8 8:4 0:4
+
+%io_imm 9:2 0:4
+%ldst_d_imm 13:1 10:2 0:3
+
+
+&rd_rr rd rr
+&rd_imm rd imm
+
+@op_rd_rr .... .. . ..... .... &rd_rr rd=%rd rr=%rr
+@op_rd_imm6 .... .... .. .. .... &rd_imm rd=%rd_c imm=%imm6
+@op_rd_imm8 .... .... .... .... &rd_imm rd=%rd_a imm=%imm8
+@fmul .... .... . ... . ... &rd_rr rd=%rd_b rr=%rr_b
+
+#
+# Arithmetic Instructions
+#
+ADD 0000 11 . ..... .... @op_rd_rr
+ADC 0001 11 . ..... .... @op_rd_rr
+ADIW 1001 0110 .. .. .... @op_rd_imm6
+SUB 0001 10 . ..... .... @op_rd_rr
+SUBI 0101 .... .... .... @op_rd_imm8
+SBC 0000 10 . ..... .... @op_rd_rr
+SBCI 0100 .... .... .... @op_rd_imm8
+SBIW 1001 0111 .. .. .... @op_rd_imm6
+AND 0010 00 . ..... .... @op_rd_rr
+ANDI 0111 .... .... .... @op_rd_imm8
+OR 0010 10 . ..... .... @op_rd_rr
+ORI 0110 .... .... .... @op_rd_imm8
+EOR 0010 01 . ..... .... @op_rd_rr
+COM 1001 010 rd:5 0000
+NEG 1001 010 rd:5 0001
+INC 1001 010 rd:5 0011
+DEC 1001 010 rd:5 1010
+MUL 1001 11 . ..... .... @op_rd_rr
+MULS 0000 0010 .... .... &rd_rr rd=%rd_a rr=%rr_a
+MULSU 0000 0011 0 ... 0 ... @fmul
+FMUL 0000 0011 0 ... 1 ... @fmul
+FMULS 0000 0011 1 ... 0 ... @fmul
+FMULSU 0000 0011 1 ... 1 ... @fmul
+DES 1001 0100 imm:4 1011
+
+#
+# Branch Instructions
+#
+
+# The 22-bit immediate is partially in the opcode word,
+# and partially in the next. Use append_16 to build the
+# complete 22-bit value.
+%imm_call 4:5 0:1 !function=append_16
+
+@op_bit .... .... . bit:3 ....
+@op_bit_imm .... .. imm:s7 bit:3
+
+RJMP 1100 imm:s12
+IJMP 1001 0100 0000 1001
+EIJMP 1001 0100 0001 1001
+JMP 1001 010 ..... 110 . imm=%imm_call
+RCALL 1101 imm:s12
+ICALL 1001 0101 0000 1001
+EICALL 1001 0101 0001 1001
+CALL 1001 010 ..... 111 . imm=%imm_call
+RET 1001 0101 0000 1000
+RETI 1001 0101 0001 1000
+CPSE 0001 00 . ..... .... @op_rd_rr
+CP 0001 01 . ..... .... @op_rd_rr
+CPC 0000 01 . ..... .... @op_rd_rr
+CPI 0011 .... .... .... @op_rd_imm8
+SBRC 1111 110 rr:5 0 bit:3
+SBRS 1111 111 rr:5 0 bit:3
+SBIC 1001 1001 reg:5 bit:3
+SBIS 1001 1011 reg:5 bit:3
+BRBS 1111 00 ....... ... @op_bit_imm
+BRBC 1111 01 ....... ... @op_bit_imm
+
+#
+# Data Transfer Instructions
+#
+
+%rd_d 4:4 !function=to_regs_00_30_by_two
+%rr_d 0:4 !function=to_regs_00_30_by_two
+
+@io_rd_imm .... . .. ..... .... &rd_imm rd=%rd imm=%io_imm
+@ldst_d .. . . .. . rd:5 . ... &rd_imm imm=%ldst_d_imm
+
+# The 16-bit immediate is completely in the next word.
+# Fields cannot be defined with no bits, so we cannot play
+# the same trick and append to a zero-bit value.
+# Defer reading the immediate until trans_{LDS,STS}.
+@ldst_s .... ... rd:5 .... imm=0
+
+MOV 0010 11 . ..... .... @op_rd_rr
+MOVW 0000 0001 .... .... &rd_rr rd=%rd_d rr=%rr_d
+LDI 1110 .... .... .... @op_rd_imm8
+LDS 1001 000 ..... 0000 @ldst_s
+LDX1 1001 000 rd:5 1100
+LDX2 1001 000 rd:5 1101
+LDX3 1001 000 rd:5 1110
+LDY2 1001 000 rd:5 1001
+LDY3 1001 000 rd:5 1010
+LDZ2 1001 000 rd:5 0001
+LDZ3 1001 000 rd:5 0010
+LDDY 10 . 0 .. 0 ..... 1 ... @ldst_d
+LDDZ 10 . 0 .. 0 ..... 0 ... @ldst_d
+STS 1001 001 ..... 0000 @ldst_s
+STX1 1001 001 rr:5 1100
+STX2 1001 001 rr:5 1101
+STX3 1001 001 rr:5 1110
+STY2 1001 001 rd:5 1001
+STY3 1001 001 rd:5 1010
+STZ2 1001 001 rd:5 0001
+STZ3 1001 001 rd:5 0010
+STDY 10 . 0 .. 1 ..... 1 ... @ldst_d
+STDZ 10 . 0 .. 1 ..... 0 ... @ldst_d
+LPM1 1001 0101 1100 1000
+LPM2 1001 000 rd:5 0100
+LPMX 1001 000 rd:5 0101
+ELPM1 1001 0101 1101 1000
+ELPM2 1001 000 rd:5 0110
+ELPMX 1001 000 rd:5 0111
+SPM 1001 0101 1110 1000
+SPMX 1001 0101 1111 1000
+IN 1011 0 .. ..... .... @io_rd_imm
+OUT 1011 1 .. ..... .... @io_rd_imm
+PUSH 1001 001 rd:5 1111
+POP 1001 000 rd:5 1111
+XCH 1001 001 rd:5 0100
+LAC 1001 001 rd:5 0110
+LAS 1001 001 rd:5 0101
+LAT 1001 001 rd:5 0111
+
+#
+# Bit and Bit-test Instructions
+#
+LSR 1001 010 rd:5 0110
+ROR 1001 010 rd:5 0111
+ASR 1001 010 rd:5 0101
+SWAP 1001 010 rd:5 0010
+SBI 1001 1010 reg:5 bit:3
+CBI 1001 1000 reg:5 bit:3
+BST 1111 101 rd:5 0 bit:3
+BLD 1111 100 rd:5 0 bit:3
+BSET 1001 0100 0 bit:3 1000
+BCLR 1001 0100 1 bit:3 1000
+
+#
+# MCU Control Instructions
+#
+BREAK 1001 0101 1001 1000
+NOP 0000 0000 0000 0000
+SLEEP 1001 0101 1000 1000
+WDR 1001 0101 1010 1000
--- /dev/null
+/*
+ * QEMU AVR CPU
+ *
+ * Copyright (c) 2016-2020 Michael Rolnik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "migration/cpu.h"
+
+static int get_sreg(QEMUFile *f, void *opaque, size_t size,
+ const VMStateField *field)
+{
+ CPUAVRState *env = opaque;
+ uint8_t sreg;
+
+ sreg = qemu_get_byte(f);
+ cpu_set_sreg(env, sreg);
+ return 0;
+}
+
+static int put_sreg(QEMUFile *f, void *opaque, size_t size,
+ const VMStateField *field, QJSON *vmdesc)
+{
+ CPUAVRState *env = opaque;
+ uint8_t sreg = cpu_get_sreg(env);
+
+ qemu_put_byte(f, sreg);
+ return 0;
+}
+
+static const VMStateInfo vms_sreg = {
+ .name = "sreg",
+ .get = get_sreg,
+ .put = put_sreg,
+};
+
+static int get_segment(QEMUFile *f, void *opaque, size_t size,
+ const VMStateField *field)
+{
+ uint32_t *ramp = opaque;
+ uint8_t temp;
+
+ temp = qemu_get_byte(f);
+ *ramp = ((uint32_t)temp) << 16;
+ return 0;
+}
+
+static int put_segment(QEMUFile *f, void *opaque, size_t size,
+ const VMStateField *field, QJSON *vmdesc)
+{
+ uint32_t *ramp = opaque;
+ uint8_t temp = *ramp >> 16;
+
+ qemu_put_byte(f, temp);
+ return 0;
+}
+
+static const VMStateInfo vms_rampD = {
+ .name = "rampD",
+ .get = get_segment,
+ .put = put_segment,
+};
+static const VMStateInfo vms_rampX = {
+ .name = "rampX",
+ .get = get_segment,
+ .put = put_segment,
+};
+static const VMStateInfo vms_rampY = {
+ .name = "rampY",
+ .get = get_segment,
+ .put = put_segment,
+};
+static const VMStateInfo vms_rampZ = {
+ .name = "rampZ",
+ .get = get_segment,
+ .put = put_segment,
+};
+static const VMStateInfo vms_eind = {
+ .name = "eind",
+ .get = get_segment,
+ .put = put_segment,
+};
+
+const VMStateDescription vms_avr_cpu = {
+ .name = "cpu",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(env.pc_w, AVRCPU),
+ VMSTATE_UINT32(env.sp, AVRCPU),
+ VMSTATE_UINT32(env.skip, AVRCPU),
+
+ VMSTATE_UINT32_ARRAY(env.r, AVRCPU, NUMBER_OF_CPU_REGISTERS),
+
+ VMSTATE_SINGLE(env, AVRCPU, 0, vms_sreg, CPUAVRState),
+ VMSTATE_SINGLE(env.rampD, AVRCPU, 0, vms_rampD, uint32_t),
+ VMSTATE_SINGLE(env.rampX, AVRCPU, 0, vms_rampX, uint32_t),
+ VMSTATE_SINGLE(env.rampY, AVRCPU, 0, vms_rampY, uint32_t),
+ VMSTATE_SINGLE(env.rampZ, AVRCPU, 0, vms_rampZ, uint32_t),
+ VMSTATE_SINGLE(env.eind, AVRCPU, 0, vms_eind, uint32_t),
+
+ VMSTATE_END_OF_LIST()
+ }
+};
--- /dev/null
+/*
+ * QEMU AVR CPU
+ *
+ * Copyright (c) 2019-2020 Michael Rolnik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/qemu-print.h"
+#include "tcg/tcg.h"
+#include "cpu.h"
+#include "exec/exec-all.h"
+#include "tcg/tcg-op.h"
+#include "exec/cpu_ldst.h"
+#include "exec/helper-proto.h"
+#include "exec/helper-gen.h"
+#include "exec/log.h"
+#include "exec/translator.h"
+#include "exec/gen-icount.h"
+
+/*
+ * Define if you want a BREAK instruction translated to a breakpoint
+ * Active debugging connection is assumed
+ * This is for
+ * https://github.com/seharris/qemu-avr-tests/tree/master/instruction-tests
+ * tests
+ */
+#undef BREAKPOINT_ON_BREAK
+
+static TCGv cpu_pc;
+
+static TCGv cpu_Cf;
+static TCGv cpu_Zf;
+static TCGv cpu_Nf;
+static TCGv cpu_Vf;
+static TCGv cpu_Sf;
+static TCGv cpu_Hf;
+static TCGv cpu_Tf;
+static TCGv cpu_If;
+
+static TCGv cpu_rampD;
+static TCGv cpu_rampX;
+static TCGv cpu_rampY;
+static TCGv cpu_rampZ;
+
+static TCGv cpu_r[NUMBER_OF_CPU_REGISTERS];
+static TCGv cpu_eind;
+static TCGv cpu_sp;
+
+static TCGv cpu_skip;
+
+static const char reg_names[NUMBER_OF_CPU_REGISTERS][8] = {
+ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+ "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
+ "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
+};
+#define REG(x) (cpu_r[x])
+
+enum {
+ DISAS_EXIT = DISAS_TARGET_0, /* We want return to the cpu main loop. */
+ DISAS_LOOKUP = DISAS_TARGET_1, /* We have a variable condition exit. */
+ DISAS_CHAIN = DISAS_TARGET_2, /* We have a single condition exit. */
+};
+
+typedef struct DisasContext DisasContext;
+
+/* This is the state at translation time. */
+struct DisasContext {
+ TranslationBlock *tb;
+
+ CPUAVRState *env;
+ CPUState *cs;
+
+ target_long npc;
+ uint32_t opcode;
+
+ /* Routine used to access memory */
+ int memidx;
+ int bstate;
+ int singlestep;
+
+ /*
+ * some AVR instructions can make the following instruction to be skipped
+ * Let's name those instructions
+ * A - instruction that can skip the next one
+ * B - instruction that can be skipped. this depends on execution of A
+ * there are two scenarios
+ * 1. A and B belong to the same translation block
+ * 2. A is the last instruction in the translation block and B is the last
+ *
+ * following variables are used to simplify the skipping logic, they are
+ * used in the following manner (sketch)
+ *
+ * TCGLabel *skip_label = NULL;
+ * if (ctx.skip_cond != TCG_COND_NEVER) {
+ * skip_label = gen_new_label();
+ * tcg_gen_brcond_tl(skip_cond, skip_var0, skip_var1, skip_label);
+ * }
+ *
+ * if (free_skip_var0) {
+ * tcg_temp_free(skip_var0);
+ * free_skip_var0 = false;
+ * }
+ *
+ * translate(&ctx);
+ *
+ * if (skip_label) {
+ * gen_set_label(skip_label);
+ * }
+ */
+ TCGv skip_var0;
+ TCGv skip_var1;
+ TCGCond skip_cond;
+ bool free_skip_var0;
+};
+
+void avr_cpu_tcg_init(void)
+{
+ int i;
+
+#define AVR_REG_OFFS(x) offsetof(CPUAVRState, x)
+ cpu_pc = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(pc_w), "pc");
+ cpu_Cf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregC), "Cf");
+ cpu_Zf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregZ), "Zf");
+ cpu_Nf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregN), "Nf");
+ cpu_Vf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregV), "Vf");
+ cpu_Sf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregS), "Sf");
+ cpu_Hf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregH), "Hf");
+ cpu_Tf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregT), "Tf");
+ cpu_If = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregI), "If");
+ cpu_rampD = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampD), "rampD");
+ cpu_rampX = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampX), "rampX");
+ cpu_rampY = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampY), "rampY");
+ cpu_rampZ = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampZ), "rampZ");
+ cpu_eind = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(eind), "eind");
+ cpu_sp = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sp), "sp");
+ cpu_skip = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(skip), "skip");
+
+ for (i = 0; i < NUMBER_OF_CPU_REGISTERS; i++) {
+ cpu_r[i] = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(r[i]),
+ reg_names[i]);
+ }
+#undef AVR_REG_OFFS
+}
+
+static int to_regs_16_31_by_one(DisasContext *ctx, int indx)
+{
+ return 16 + (indx % 16);
+}
+
+static int to_regs_16_23_by_one(DisasContext *ctx, int indx)
+{
+ return 16 + (indx % 8);
+}
+
+static int to_regs_24_30_by_two(DisasContext *ctx, int indx)
+{
+ return 24 + (indx % 4) * 2;
+}
+
+static int to_regs_00_30_by_two(DisasContext *ctx, int indx)
+{
+ return (indx % 16) * 2;
+}
+
+static uint16_t next_word(DisasContext *ctx)
+{
+ return cpu_lduw_code(ctx->env, ctx->npc++ * 2);
+}
+
+static int append_16(DisasContext *ctx, int x)
+{
+ return x << 16 | next_word(ctx);
+}
+
+static bool avr_have_feature(DisasContext *ctx, int feature)
+{
+ if (!avr_feature(ctx->env, feature)) {
+ gen_helper_unsupported(cpu_env);
+ ctx->bstate = DISAS_NORETURN;
+ return false;
+ }
+ return true;
+}
+
+static bool decode_insn(DisasContext *ctx, uint16_t insn);
+#include "decode_insn.inc.c"
+
+/*
+ * Arithmetic Instructions
+ */
+
+/*
+ * Utility functions for updating status registers:
+ *
+ * - gen_add_CHf()
+ * - gen_add_Vf()
+ * - gen_sub_CHf()
+ * - gen_sub_Vf()
+ * - gen_NSf()
+ * - gen_ZNSf()
+ *
+ */
+
+static void gen_add_CHf(TCGv R, TCGv Rd, TCGv Rr)
+{
+ TCGv t1 = tcg_temp_new_i32();
+ TCGv t2 = tcg_temp_new_i32();
+ TCGv t3 = tcg_temp_new_i32();
+
+ tcg_gen_and_tl(t1, Rd, Rr); /* t1 = Rd & Rr */
+ tcg_gen_andc_tl(t2, Rd, R); /* t2 = Rd & ~R */
+ tcg_gen_andc_tl(t3, Rr, R); /* t3 = Rr & ~R */
+ tcg_gen_or_tl(t1, t1, t2); /* t1 = t1 | t2 | t3 */
+ tcg_gen_or_tl(t1, t1, t3);
+
+ tcg_gen_shri_tl(cpu_Cf, t1, 7); /* Cf = t1(7) */
+ tcg_gen_shri_tl(cpu_Hf, t1, 3); /* Hf = t1(3) */
+ tcg_gen_andi_tl(cpu_Hf, cpu_Hf, 1);
+
+ tcg_temp_free_i32(t3);
+ tcg_temp_free_i32(t2);
+ tcg_temp_free_i32(t1);
+}
+
+static void gen_add_Vf(TCGv R, TCGv Rd, TCGv Rr)
+{
+ TCGv t1 = tcg_temp_new_i32();
+ TCGv t2 = tcg_temp_new_i32();
+
+ /* t1 = Rd & Rr & ~R | ~Rd & ~Rr & R */
+ /* = (Rd ^ R) & ~(Rd ^ Rr) */
+ tcg_gen_xor_tl(t1, Rd, R);
+ tcg_gen_xor_tl(t2, Rd, Rr);
+ tcg_gen_andc_tl(t1, t1, t2);
+
+ tcg_gen_shri_tl(cpu_Vf, t1, 7); /* Vf = t1(7) */
+
+ tcg_temp_free_i32(t2);
+ tcg_temp_free_i32(t1);
+}
+
+static void gen_sub_CHf(TCGv R, TCGv Rd, TCGv Rr)
+{
+ TCGv t1 = tcg_temp_new_i32();
+ TCGv t2 = tcg_temp_new_i32();
+ TCGv t3 = tcg_temp_new_i32();
+
+ tcg_gen_not_tl(t1, Rd); /* t1 = ~Rd */
+ tcg_gen_and_tl(t2, t1, Rr); /* t2 = ~Rd & Rr */
+ tcg_gen_or_tl(t3, t1, Rr); /* t3 = (~Rd | Rr) & R */
+ tcg_gen_and_tl(t3, t3, R);
+ tcg_gen_or_tl(t2, t2, t3); /* t2 = ~Rd & Rr | ~Rd & R | R & Rr */
+
+ tcg_gen_shri_tl(cpu_Cf, t2, 7); /* Cf = t2(7) */
+ tcg_gen_shri_tl(cpu_Hf, t2, 3); /* Hf = t2(3) */
+ tcg_gen_andi_tl(cpu_Hf, cpu_Hf, 1);
+
+ tcg_temp_free_i32(t3);
+ tcg_temp_free_i32(t2);
+ tcg_temp_free_i32(t1);
+}
+
+static void gen_sub_Vf(TCGv R, TCGv Rd, TCGv Rr)
+{
+ TCGv t1 = tcg_temp_new_i32();
+ TCGv t2 = tcg_temp_new_i32();
+
+ /* t1 = Rd & ~Rr & ~R | ~Rd & Rr & R */
+ /* = (Rd ^ R) & (Rd ^ R) */
+ tcg_gen_xor_tl(t1, Rd, R);
+ tcg_gen_xor_tl(t2, Rd, Rr);
+ tcg_gen_and_tl(t1, t1, t2);
+
+ tcg_gen_shri_tl(cpu_Vf, t1, 7); /* Vf = t1(7) */
+
+ tcg_temp_free_i32(t2);
+ tcg_temp_free_i32(t1);
+}
+
+static void gen_NSf(TCGv R)
+{
+ tcg_gen_shri_tl(cpu_Nf, R, 7); /* Nf = R(7) */
+ tcg_gen_xor_tl(cpu_Sf, cpu_Nf, cpu_Vf); /* Sf = Nf ^ Vf */
+}
+
+static void gen_ZNSf(TCGv R)
+{
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */
+
+ /* update status register */
+ tcg_gen_shri_tl(cpu_Nf, R, 7); /* Nf = R(7) */
+ tcg_gen_xor_tl(cpu_Sf, cpu_Nf, cpu_Vf); /* Sf = Nf ^ Vf */
+}
+
+/*
+ * Adds two registers without the C Flag and places the result in the
+ * destination register Rd.
+ */
+static bool trans_ADD(DisasContext *ctx, arg_ADD *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_add_tl(R, Rd, Rr); /* Rd = Rd + Rr */
+ tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */
+
+ /* update status register */
+ gen_add_CHf(R, Rd, Rr);
+ gen_add_Vf(R, Rd, Rr);
+ gen_ZNSf(R);
+
+ /* update output registers */
+ tcg_gen_mov_tl(Rd, R);
+
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * Adds two registers and the contents of the C Flag and places the result in
+ * the destination register Rd.
+ */
+static bool trans_ADC(DisasContext *ctx, arg_ADC *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_add_tl(R, Rd, Rr); /* R = Rd + Rr + Cf */
+ tcg_gen_add_tl(R, R, cpu_Cf);
+ tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */
+
+ /* update status register */
+ gen_add_CHf(R, Rd, Rr);
+ gen_add_Vf(R, Rd, Rr);
+ gen_ZNSf(R);
+
+ /* update output registers */
+ tcg_gen_mov_tl(Rd, R);
+
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * Adds an immediate value (0 - 63) to a register pair and places the result
+ * in the register pair. This instruction operates on the upper four register
+ * pairs, and is well suited for operations on the pointer registers. This
+ * instruction is not available in all devices. Refer to the device specific
+ * instruction set summary.
+ */
+static bool trans_ADIW(DisasContext *ctx, arg_ADIW *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_ADIW_SBIW)) {
+ return true;
+ }
+
+ TCGv RdL = cpu_r[a->rd];
+ TCGv RdH = cpu_r[a->rd + 1];
+ int Imm = (a->imm);
+ TCGv R = tcg_temp_new_i32();
+ TCGv Rd = tcg_temp_new_i32();
+
+ tcg_gen_deposit_tl(Rd, RdL, RdH, 8, 8); /* Rd = RdH:RdL */
+ tcg_gen_addi_tl(R, Rd, Imm); /* R = Rd + Imm */
+ tcg_gen_andi_tl(R, R, 0xffff); /* make it 16 bits */
+
+ /* update status register */
+ tcg_gen_andc_tl(cpu_Cf, Rd, R); /* Cf = Rd & ~R */
+ tcg_gen_shri_tl(cpu_Cf, cpu_Cf, 15);
+ tcg_gen_andc_tl(cpu_Vf, R, Rd); /* Vf = R & ~Rd */
+ tcg_gen_shri_tl(cpu_Vf, cpu_Vf, 15);
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */
+ tcg_gen_shri_tl(cpu_Nf, R, 15); /* Nf = R(15) */
+ tcg_gen_xor_tl(cpu_Sf, cpu_Nf, cpu_Vf);/* Sf = Nf ^ Vf */
+
+ /* update output registers */
+ tcg_gen_andi_tl(RdL, R, 0xff);
+ tcg_gen_shri_tl(RdH, R, 8);
+
+ tcg_temp_free_i32(Rd);
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * Subtracts two registers and places the result in the destination
+ * register Rd.
+ */
+static bool trans_SUB(DisasContext *ctx, arg_SUB *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_sub_tl(R, Rd, Rr); /* R = Rd - Rr */
+ tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */
+
+ /* update status register */
+ tcg_gen_andc_tl(cpu_Cf, Rd, R); /* Cf = Rd & ~R */
+ gen_sub_CHf(R, Rd, Rr);
+ gen_sub_Vf(R, Rd, Rr);
+ gen_ZNSf(R);
+
+ /* update output registers */
+ tcg_gen_mov_tl(Rd, R);
+
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * Subtracts a register and a constant and places the result in the
+ * destination register Rd. This instruction is working on Register R16 to R31
+ * and is very well suited for operations on the X, Y, and Z-pointers.
+ */
+static bool trans_SUBI(DisasContext *ctx, arg_SUBI *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = tcg_const_i32(a->imm);
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_sub_tl(R, Rd, Rr); /* R = Rd - Imm */
+ tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */
+
+ /* update status register */
+ gen_sub_CHf(R, Rd, Rr);
+ gen_sub_Vf(R, Rd, Rr);
+ gen_ZNSf(R);
+
+ /* update output registers */
+ tcg_gen_mov_tl(Rd, R);
+
+ tcg_temp_free_i32(R);
+ tcg_temp_free_i32(Rr);
+
+ return true;
+}
+
+/*
+ * Subtracts two registers and subtracts with the C Flag and places the
+ * result in the destination register Rd.
+ */
+static bool trans_SBC(DisasContext *ctx, arg_SBC *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+ TCGv zero = tcg_const_i32(0);
+
+ tcg_gen_sub_tl(R, Rd, Rr); /* R = Rd - Rr - Cf */
+ tcg_gen_sub_tl(R, R, cpu_Cf);
+ tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */
+
+ /* update status register */
+ gen_sub_CHf(R, Rd, Rr);
+ gen_sub_Vf(R, Rd, Rr);
+ gen_NSf(R);
+
+ /*
+ * Previous value remains unchanged when the result is zero;
+ * cleared otherwise.
+ */
+ tcg_gen_movcond_tl(TCG_COND_EQ, cpu_Zf, R, zero, cpu_Zf, zero);
+
+ /* update output registers */
+ tcg_gen_mov_tl(Rd, R);
+
+ tcg_temp_free_i32(zero);
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * SBCI -- Subtract Immediate with Carry
+ */
+static bool trans_SBCI(DisasContext *ctx, arg_SBCI *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = tcg_const_i32(a->imm);
+ TCGv R = tcg_temp_new_i32();
+ TCGv zero = tcg_const_i32(0);
+
+ tcg_gen_sub_tl(R, Rd, Rr); /* R = Rd - Rr - Cf */
+ tcg_gen_sub_tl(R, R, cpu_Cf);
+ tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */
+
+ /* update status register */
+ gen_sub_CHf(R, Rd, Rr);
+ gen_sub_Vf(R, Rd, Rr);
+ gen_NSf(R);
+
+ /*
+ * Previous value remains unchanged when the result is zero;
+ * cleared otherwise.
+ */
+ tcg_gen_movcond_tl(TCG_COND_EQ, cpu_Zf, R, zero, cpu_Zf, zero);
+
+ /* update output registers */
+ tcg_gen_mov_tl(Rd, R);
+
+ tcg_temp_free_i32(zero);
+ tcg_temp_free_i32(R);
+ tcg_temp_free_i32(Rr);
+
+ return true;
+}
+
+/*
+ * Subtracts an immediate value (0-63) from a register pair and places the
+ * result in the register pair. This instruction operates on the upper four
+ * register pairs, and is well suited for operations on the Pointer Registers.
+ * This instruction is not available in all devices. Refer to the device
+ * specific instruction set summary.
+ */
+static bool trans_SBIW(DisasContext *ctx, arg_SBIW *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_ADIW_SBIW)) {
+ return true;
+ }
+
+ TCGv RdL = cpu_r[a->rd];
+ TCGv RdH = cpu_r[a->rd + 1];
+ int Imm = (a->imm);
+ TCGv R = tcg_temp_new_i32();
+ TCGv Rd = tcg_temp_new_i32();
+
+ tcg_gen_deposit_tl(Rd, RdL, RdH, 8, 8); /* Rd = RdH:RdL */
+ tcg_gen_subi_tl(R, Rd, Imm); /* R = Rd - Imm */
+ tcg_gen_andi_tl(R, R, 0xffff); /* make it 16 bits */
+
+ /* update status register */
+ tcg_gen_andc_tl(cpu_Cf, R, Rd);
+ tcg_gen_shri_tl(cpu_Cf, cpu_Cf, 15); /* Cf = R & ~Rd */
+ tcg_gen_andc_tl(cpu_Vf, Rd, R);
+ tcg_gen_shri_tl(cpu_Vf, cpu_Vf, 15); /* Vf = Rd & ~R */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */
+ tcg_gen_shri_tl(cpu_Nf, R, 15); /* Nf = R(15) */
+ tcg_gen_xor_tl(cpu_Sf, cpu_Nf, cpu_Vf); /* Sf = Nf ^ Vf */
+
+ /* update output registers */
+ tcg_gen_andi_tl(RdL, R, 0xff);
+ tcg_gen_shri_tl(RdH, R, 8);
+
+ tcg_temp_free_i32(Rd);
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * Performs the logical AND between the contents of register Rd and register
+ * Rr and places the result in the destination register Rd.
+ */
+static bool trans_AND(DisasContext *ctx, arg_AND *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_and_tl(R, Rd, Rr); /* Rd = Rd and Rr */
+
+ /* update status register */
+ tcg_gen_movi_tl(cpu_Vf, 0); /* Vf = 0 */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */
+ gen_ZNSf(R);
+
+ /* update output registers */
+ tcg_gen_mov_tl(Rd, R);
+
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * Performs the logical AND between the contents of register Rd and a constant
+ * and places the result in the destination register Rd.
+ */
+static bool trans_ANDI(DisasContext *ctx, arg_ANDI *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ int Imm = (a->imm);
+
+ tcg_gen_andi_tl(Rd, Rd, Imm); /* Rd = Rd & Imm */
+
+ /* update status register */
+ tcg_gen_movi_tl(cpu_Vf, 0x00); /* Vf = 0 */
+ gen_ZNSf(Rd);
+
+ return true;
+}
+
+/*
+ * Performs the logical OR between the contents of register Rd and register
+ * Rr and places the result in the destination register Rd.
+ */
+static bool trans_OR(DisasContext *ctx, arg_OR *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_or_tl(R, Rd, Rr);
+
+ /* update status register */
+ tcg_gen_movi_tl(cpu_Vf, 0);
+ gen_ZNSf(R);
+
+ /* update output registers */
+ tcg_gen_mov_tl(Rd, R);
+
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * Performs the logical OR between the contents of register Rd and a
+ * constant and places the result in the destination register Rd.
+ */
+static bool trans_ORI(DisasContext *ctx, arg_ORI *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ int Imm = (a->imm);
+
+ tcg_gen_ori_tl(Rd, Rd, Imm); /* Rd = Rd | Imm */
+
+ /* update status register */
+ tcg_gen_movi_tl(cpu_Vf, 0x00); /* Vf = 0 */
+ gen_ZNSf(Rd);
+
+ return true;
+}
+
+/*
+ * Performs the logical EOR between the contents of register Rd and
+ * register Rr and places the result in the destination register Rd.
+ */
+static bool trans_EOR(DisasContext *ctx, arg_EOR *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+
+ tcg_gen_xor_tl(Rd, Rd, Rr);
+
+ /* update status register */
+ tcg_gen_movi_tl(cpu_Vf, 0);
+ gen_ZNSf(Rd);
+
+ return true;
+}
+
+/*
+ * Clears the specified bits in register Rd. Performs the logical AND
+ * between the contents of register Rd and the complement of the constant mask
+ * K. The result will be placed in register Rd.
+ */
+static bool trans_COM(DisasContext *ctx, arg_COM *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_xori_tl(Rd, Rd, 0xff);
+
+ /* update status register */
+ tcg_gen_movi_tl(cpu_Cf, 1); /* Cf = 1 */
+ tcg_gen_movi_tl(cpu_Vf, 0); /* Vf = 0 */
+ gen_ZNSf(Rd);
+
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * Replaces the contents of register Rd with its two's complement; the
+ * value $80 is left unchanged.
+ */
+static bool trans_NEG(DisasContext *ctx, arg_NEG *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv t0 = tcg_const_i32(0);
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_sub_tl(R, t0, Rd); /* R = 0 - Rd */
+ tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */
+
+ /* update status register */
+ gen_sub_CHf(R, t0, Rd);
+ gen_sub_Vf(R, t0, Rd);
+ gen_ZNSf(R);
+
+ /* update output registers */
+ tcg_gen_mov_tl(Rd, R);
+
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * Adds one -1- to the contents of register Rd and places the result in the
+ * destination register Rd. The C Flag in SREG is not affected by the
+ * operation, thus allowing the INC instruction to be used on a loop counter in
+ * multiple-precision computations. When operating on unsigned numbers, only
+ * BREQ and BRNE branches can be expected to perform consistently. When
+ * operating on two's complement values, all signed branches are available.
+ */
+static bool trans_INC(DisasContext *ctx, arg_INC *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+
+ tcg_gen_addi_tl(Rd, Rd, 1);
+ tcg_gen_andi_tl(Rd, Rd, 0xff);
+
+ /* update status register */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Vf, Rd, 0x80); /* Vf = Rd == 0x80 */
+ gen_ZNSf(Rd);
+
+ return true;
+}
+
+/*
+ * Subtracts one -1- from the contents of register Rd and places the result
+ * in the destination register Rd. The C Flag in SREG is not affected by the
+ * operation, thus allowing the DEC instruction to be used on a loop counter in
+ * multiple-precision computations. When operating on unsigned values, only
+ * BREQ and BRNE branches can be expected to perform consistently. When
+ * operating on two's complement values, all signed branches are available.
+ */
+static bool trans_DEC(DisasContext *ctx, arg_DEC *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+
+ tcg_gen_subi_tl(Rd, Rd, 1); /* Rd = Rd - 1 */
+ tcg_gen_andi_tl(Rd, Rd, 0xff); /* make it 8 bits */
+
+ /* update status register */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Vf, Rd, 0x7f); /* Vf = Rd == 0x7f */
+ gen_ZNSf(Rd);
+
+ return true;
+}
+
+/*
+ * This instruction performs 8-bit x 8-bit -> 16-bit unsigned multiplication.
+ */
+static bool trans_MUL(DisasContext *ctx, arg_MUL *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_MUL)) {
+ return true;
+ }
+
+ TCGv R0 = cpu_r[0];
+ TCGv R1 = cpu_r[1];
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_mul_tl(R, Rd, Rr); /* R = Rd * Rr */
+ tcg_gen_andi_tl(R0, R, 0xff);
+ tcg_gen_shri_tl(R1, R, 8);
+
+ /* update status register */
+ tcg_gen_shri_tl(cpu_Cf, R, 15); /* Cf = R(15) */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */
+
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * This instruction performs 8-bit x 8-bit -> 16-bit signed multiplication.
+ */
+static bool trans_MULS(DisasContext *ctx, arg_MULS *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_MUL)) {
+ return true;
+ }
+
+ TCGv R0 = cpu_r[0];
+ TCGv R1 = cpu_r[1];
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+ TCGv t0 = tcg_temp_new_i32();
+ TCGv t1 = tcg_temp_new_i32();
+
+ tcg_gen_ext8s_tl(t0, Rd); /* make Rd full 32 bit signed */
+ tcg_gen_ext8s_tl(t1, Rr); /* make Rr full 32 bit signed */
+ tcg_gen_mul_tl(R, t0, t1); /* R = Rd * Rr */
+ tcg_gen_andi_tl(R, R, 0xffff); /* make it 16 bits */
+ tcg_gen_andi_tl(R0, R, 0xff);
+ tcg_gen_shri_tl(R1, R, 8);
+
+ /* update status register */
+ tcg_gen_shri_tl(cpu_Cf, R, 15); /* Cf = R(15) */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */
+
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * This instruction performs 8-bit x 8-bit -> 16-bit multiplication of a
+ * signed and an unsigned number.
+ */
+static bool trans_MULSU(DisasContext *ctx, arg_MULSU *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_MUL)) {
+ return true;
+ }
+
+ TCGv R0 = cpu_r[0];
+ TCGv R1 = cpu_r[1];
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+ TCGv t0 = tcg_temp_new_i32();
+
+ tcg_gen_ext8s_tl(t0, Rd); /* make Rd full 32 bit signed */
+ tcg_gen_mul_tl(R, t0, Rr); /* R = Rd * Rr */
+ tcg_gen_andi_tl(R, R, 0xffff); /* make R 16 bits */
+ tcg_gen_andi_tl(R0, R, 0xff);
+ tcg_gen_shri_tl(R1, R, 8);
+
+ /* update status register */
+ tcg_gen_shri_tl(cpu_Cf, R, 15); /* Cf = R(15) */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */
+
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * This instruction performs 8-bit x 8-bit -> 16-bit unsigned
+ * multiplication and shifts the result one bit left.
+ */
+static bool trans_FMUL(DisasContext *ctx, arg_FMUL *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_MUL)) {
+ return true;
+ }
+
+ TCGv R0 = cpu_r[0];
+ TCGv R1 = cpu_r[1];
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_mul_tl(R, Rd, Rr); /* R = Rd * Rr */
+
+ /* update status register */
+ tcg_gen_shri_tl(cpu_Cf, R, 15); /* Cf = R(15) */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */
+
+ /* update output registers */
+ tcg_gen_shli_tl(R, R, 1);
+ tcg_gen_andi_tl(R0, R, 0xff);
+ tcg_gen_shri_tl(R1, R, 8);
+ tcg_gen_andi_tl(R1, R1, 0xff);
+
+
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * This instruction performs 8-bit x 8-bit -> 16-bit signed multiplication
+ * and shifts the result one bit left.
+ */
+static bool trans_FMULS(DisasContext *ctx, arg_FMULS *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_MUL)) {
+ return true;
+ }
+
+ TCGv R0 = cpu_r[0];
+ TCGv R1 = cpu_r[1];
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+ TCGv t0 = tcg_temp_new_i32();
+ TCGv t1 = tcg_temp_new_i32();
+
+ tcg_gen_ext8s_tl(t0, Rd); /* make Rd full 32 bit signed */
+ tcg_gen_ext8s_tl(t1, Rr); /* make Rr full 32 bit signed */
+ tcg_gen_mul_tl(R, t0, t1); /* R = Rd * Rr */
+ tcg_gen_andi_tl(R, R, 0xffff); /* make it 16 bits */
+
+ /* update status register */
+ tcg_gen_shri_tl(cpu_Cf, R, 15); /* Cf = R(15) */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */
+
+ /* update output registers */
+ tcg_gen_shli_tl(R, R, 1);
+ tcg_gen_andi_tl(R0, R, 0xff);
+ tcg_gen_shri_tl(R1, R, 8);
+ tcg_gen_andi_tl(R1, R1, 0xff);
+
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * This instruction performs 8-bit x 8-bit -> 16-bit signed multiplication
+ * and shifts the result one bit left.
+ */
+static bool trans_FMULSU(DisasContext *ctx, arg_FMULSU *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_MUL)) {
+ return true;
+ }
+
+ TCGv R0 = cpu_r[0];
+ TCGv R1 = cpu_r[1];
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+ TCGv t0 = tcg_temp_new_i32();
+
+ tcg_gen_ext8s_tl(t0, Rd); /* make Rd full 32 bit signed */
+ tcg_gen_mul_tl(R, t0, Rr); /* R = Rd * Rr */
+ tcg_gen_andi_tl(R, R, 0xffff); /* make it 16 bits */
+
+ /* update status register */
+ tcg_gen_shri_tl(cpu_Cf, R, 15); /* Cf = R(15) */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */
+
+ /* update output registers */
+ tcg_gen_shli_tl(R, R, 1);
+ tcg_gen_andi_tl(R0, R, 0xff);
+ tcg_gen_shri_tl(R1, R, 8);
+ tcg_gen_andi_tl(R1, R1, 0xff);
+
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * The module is an instruction set extension to the AVR CPU, performing
+ * DES iterations. The 64-bit data block (plaintext or ciphertext) is placed in
+ * the CPU register file, registers R0-R7, where LSB of data is placed in LSB
+ * of R0 and MSB of data is placed in MSB of R7. The full 64-bit key (including
+ * parity bits) is placed in registers R8- R15, organized in the register file
+ * with LSB of key in LSB of R8 and MSB of key in MSB of R15. Executing one DES
+ * instruction performs one round in the DES algorithm. Sixteen rounds must be
+ * executed in increasing order to form the correct DES ciphertext or
+ * plaintext. Intermediate results are stored in the register file (R0-R15)
+ * after each DES instruction. The instruction's operand (K) determines which
+ * round is executed, and the half carry flag (H) determines whether encryption
+ * or decryption is performed. The DES algorithm is described in
+ * "Specifications for the Data Encryption Standard" (Federal Information
+ * Processing Standards Publication 46). Intermediate results in this
+ * implementation differ from the standard because the initial permutation and
+ * the inverse initial permutation are performed each iteration. This does not
+ * affect the result in the final ciphertext or plaintext, but reduces
+ * execution time.
+ */
+static bool trans_DES(DisasContext *ctx, arg_DES *a)
+{
+ /* TODO */
+ if (!avr_have_feature(ctx, AVR_FEATURE_DES)) {
+ return true;
+ }
+
+ qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__);
+
+ return true;
+}
+
+/*
+ * Branch Instructions
+ */
+static void gen_jmp_ez(DisasContext *ctx)
+{
+ tcg_gen_deposit_tl(cpu_pc, cpu_r[30], cpu_r[31], 8, 8);
+ tcg_gen_or_tl(cpu_pc, cpu_pc, cpu_eind);
+ ctx->bstate = DISAS_LOOKUP;
+}
+
+static void gen_jmp_z(DisasContext *ctx)
+{
+ tcg_gen_deposit_tl(cpu_pc, cpu_r[30], cpu_r[31], 8, 8);
+ ctx->bstate = DISAS_LOOKUP;
+}
+
+static void gen_push_ret(DisasContext *ctx, int ret)
+{
+ if (avr_feature(ctx->env, AVR_FEATURE_1_BYTE_PC)) {
+
+ TCGv t0 = tcg_const_i32((ret & 0x0000ff));
+
+ tcg_gen_qemu_st_tl(t0, cpu_sp, MMU_DATA_IDX, MO_UB);
+ tcg_gen_subi_tl(cpu_sp, cpu_sp, 1);
+
+ tcg_temp_free_i32(t0);
+ } else if (avr_feature(ctx->env, AVR_FEATURE_2_BYTE_PC)) {
+
+ TCGv t0 = tcg_const_i32((ret & 0x00ffff));
+
+ tcg_gen_subi_tl(cpu_sp, cpu_sp, 1);
+ tcg_gen_qemu_st_tl(t0, cpu_sp, MMU_DATA_IDX, MO_BEUW);
+ tcg_gen_subi_tl(cpu_sp, cpu_sp, 1);
+
+ tcg_temp_free_i32(t0);
+
+ } else if (avr_feature(ctx->env, AVR_FEATURE_3_BYTE_PC)) {
+
+ TCGv lo = tcg_const_i32((ret & 0x0000ff));
+ TCGv hi = tcg_const_i32((ret & 0xffff00) >> 8);
+
+ tcg_gen_qemu_st_tl(lo, cpu_sp, MMU_DATA_IDX, MO_UB);
+ tcg_gen_subi_tl(cpu_sp, cpu_sp, 2);
+ tcg_gen_qemu_st_tl(hi, cpu_sp, MMU_DATA_IDX, MO_BEUW);
+ tcg_gen_subi_tl(cpu_sp, cpu_sp, 1);
+
+ tcg_temp_free_i32(lo);
+ tcg_temp_free_i32(hi);
+ }
+}
+
+static void gen_pop_ret(DisasContext *ctx, TCGv ret)
+{
+ if (avr_feature(ctx->env, AVR_FEATURE_1_BYTE_PC)) {
+ tcg_gen_addi_tl(cpu_sp, cpu_sp, 1);
+ tcg_gen_qemu_ld_tl(ret, cpu_sp, MMU_DATA_IDX, MO_UB);
+ } else if (avr_feature(ctx->env, AVR_FEATURE_2_BYTE_PC)) {
+ tcg_gen_addi_tl(cpu_sp, cpu_sp, 1);
+ tcg_gen_qemu_ld_tl(ret, cpu_sp, MMU_DATA_IDX, MO_BEUW);
+ tcg_gen_addi_tl(cpu_sp, cpu_sp, 1);
+ } else if (avr_feature(ctx->env, AVR_FEATURE_3_BYTE_PC)) {
+ TCGv lo = tcg_temp_new_i32();
+ TCGv hi = tcg_temp_new_i32();
+
+ tcg_gen_addi_tl(cpu_sp, cpu_sp, 1);
+ tcg_gen_qemu_ld_tl(hi, cpu_sp, MMU_DATA_IDX, MO_BEUW);
+
+ tcg_gen_addi_tl(cpu_sp, cpu_sp, 2);
+ tcg_gen_qemu_ld_tl(lo, cpu_sp, MMU_DATA_IDX, MO_UB);
+
+ tcg_gen_deposit_tl(ret, lo, hi, 8, 16);
+
+ tcg_temp_free_i32(lo);
+ tcg_temp_free_i32(hi);
+ }
+}
+
+static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
+{
+ TranslationBlock *tb = ctx->tb;
+
+ if (ctx->singlestep == 0) {
+ tcg_gen_goto_tb(n);
+ tcg_gen_movi_i32(cpu_pc, dest);
+ tcg_gen_exit_tb(tb, n);
+ } else {
+ tcg_gen_movi_i32(cpu_pc, dest);
+ gen_helper_debug(cpu_env);
+ tcg_gen_exit_tb(NULL, 0);
+ }
+ ctx->bstate = DISAS_NORETURN;
+}
+
+/*
+ * Relative jump to an address within PC - 2K +1 and PC + 2K (words). For
+ * AVR microcontrollers with Program memory not exceeding 4K words (8KB) this
+ * instruction can address the entire memory from every address location. See
+ * also JMP.
+ */
+static bool trans_RJMP(DisasContext *ctx, arg_RJMP *a)
+{
+ int dst = ctx->npc + a->imm;
+
+ gen_goto_tb(ctx, 0, dst);
+
+ return true;
+}
+
+/*
+ * Indirect jump to the address pointed to by the Z (16 bits) Pointer
+ * Register in the Register File. The Z-pointer Register is 16 bits wide and
+ * allows jump within the lowest 64K words (128KB) section of Program memory.
+ * This instruction is not available in all devices. Refer to the device
+ * specific instruction set summary.
+ */
+static bool trans_IJMP(DisasContext *ctx, arg_IJMP *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_IJMP_ICALL)) {
+ return true;
+ }
+
+ gen_jmp_z(ctx);
+
+ return true;
+}
+
+/*
+ * Indirect jump to the address pointed to by the Z (16 bits) Pointer
+ * Register in the Register File and the EIND Register in the I/O space. This
+ * instruction allows for indirect jumps to the entire 4M (words) Program
+ * memory space. See also IJMP. This instruction is not available in all
+ * devices. Refer to the device specific instruction set summary.
+ */
+static bool trans_EIJMP(DisasContext *ctx, arg_EIJMP *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_EIJMP_EICALL)) {
+ return true;
+ }
+
+ gen_jmp_ez(ctx);
+ return true;
+}
+
+/*
+ * Jump to an address within the entire 4M (words) Program memory. See also
+ * RJMP. This instruction is not available in all devices. Refer to the device
+ * specific instruction set summary.0
+ */
+static bool trans_JMP(DisasContext *ctx, arg_JMP *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_JMP_CALL)) {
+ return true;
+ }
+
+ gen_goto_tb(ctx, 0, a->imm);
+
+ return true;
+}
+
+/*
+ * Relative call to an address within PC - 2K + 1 and PC + 2K (words). The
+ * return address (the instruction after the RCALL) is stored onto the Stack.
+ * See also CALL. For AVR microcontrollers with Program memory not exceeding 4K
+ * words (8KB) this instruction can address the entire memory from every
+ * address location. The Stack Pointer uses a post-decrement scheme during
+ * RCALL.
+ */
+static bool trans_RCALL(DisasContext *ctx, arg_RCALL *a)
+{
+ int ret = ctx->npc;
+ int dst = ctx->npc + a->imm;
+
+ gen_push_ret(ctx, ret);
+ gen_goto_tb(ctx, 0, dst);
+
+ return true;
+}
+
+/*
+ * Calls to a subroutine within the entire 4M (words) Program memory. The
+ * return address (to the instruction after the CALL) will be stored onto the
+ * Stack. See also RCALL. The Stack Pointer uses a post-decrement scheme during
+ * CALL. This instruction is not available in all devices. Refer to the device
+ * specific instruction set summary.
+ */
+static bool trans_ICALL(DisasContext *ctx, arg_ICALL *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_IJMP_ICALL)) {
+ return true;
+ }
+
+ int ret = ctx->npc;
+
+ gen_push_ret(ctx, ret);
+ gen_jmp_z(ctx);
+
+ return true;
+}
+
+/*
+ * Indirect call of a subroutine pointed to by the Z (16 bits) Pointer
+ * Register in the Register File and the EIND Register in the I/O space. This
+ * instruction allows for indirect calls to the entire 4M (words) Program
+ * memory space. See also ICALL. The Stack Pointer uses a post-decrement scheme
+ * during EICALL. This instruction is not available in all devices. Refer to
+ * the device specific instruction set summary.
+ */
+static bool trans_EICALL(DisasContext *ctx, arg_EICALL *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_EIJMP_EICALL)) {
+ return true;
+ }
+
+ int ret = ctx->npc;
+
+ gen_push_ret(ctx, ret);
+ gen_jmp_ez(ctx);
+ return true;
+}
+
+/*
+ * Calls to a subroutine within the entire Program memory. The return
+ * address (to the instruction after the CALL) will be stored onto the Stack.
+ * (See also RCALL). The Stack Pointer uses a post-decrement scheme during
+ * CALL. This instruction is not available in all devices. Refer to the device
+ * specific instruction set summary.
+ */
+static bool trans_CALL(DisasContext *ctx, arg_CALL *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_JMP_CALL)) {
+ return true;
+ }
+
+ int Imm = a->imm;
+ int ret = ctx->npc;
+
+ gen_push_ret(ctx, ret);
+ gen_goto_tb(ctx, 0, Imm);
+
+ return true;
+}
+
+/*
+ * Returns from subroutine. The return address is loaded from the STACK.
+ * The Stack Pointer uses a preincrement scheme during RET.
+ */
+static bool trans_RET(DisasContext *ctx, arg_RET *a)
+{
+ gen_pop_ret(ctx, cpu_pc);
+
+ ctx->bstate = DISAS_LOOKUP;
+ return true;
+}
+
+/*
+ * Returns from interrupt. The return address is loaded from the STACK and
+ * the Global Interrupt Flag is set. Note that the Status Register is not
+ * automatically stored when entering an interrupt routine, and it is not
+ * restored when returning from an interrupt routine. This must be handled by
+ * the application program. The Stack Pointer uses a pre-increment scheme
+ * during RETI.
+ */
+static bool trans_RETI(DisasContext *ctx, arg_RETI *a)
+{
+ gen_pop_ret(ctx, cpu_pc);
+ tcg_gen_movi_tl(cpu_If, 1);
+
+ /* Need to return to main loop to re-evaluate interrupts. */
+ ctx->bstate = DISAS_EXIT;
+ return true;
+}
+
+/*
+ * This instruction performs a compare between two registers Rd and Rr, and
+ * skips the next instruction if Rd = Rr.
+ */
+static bool trans_CPSE(DisasContext *ctx, arg_CPSE *a)
+{
+ ctx->skip_cond = TCG_COND_EQ;
+ ctx->skip_var0 = cpu_r[a->rd];
+ ctx->skip_var1 = cpu_r[a->rr];
+ return true;
+}
+
+/*
+ * This instruction performs a compare between two registers Rd and Rr.
+ * None of the registers are changed. All conditional branches can be used
+ * after this instruction.
+ */
+static bool trans_CP(DisasContext *ctx, arg_CP *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_sub_tl(R, Rd, Rr); /* R = Rd - Rr */
+ tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */
+
+ /* update status register */
+ gen_sub_CHf(R, Rd, Rr);
+ gen_sub_Vf(R, Rd, Rr);
+ gen_ZNSf(R);
+
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * This instruction performs a compare between two registers Rd and Rr and
+ * also takes into account the previous carry. None of the registers are
+ * changed. All conditional branches can be used after this instruction.
+ */
+static bool trans_CPC(DisasContext *ctx, arg_CPC *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+ TCGv zero = tcg_const_i32(0);
+
+ tcg_gen_sub_tl(R, Rd, Rr); /* R = Rd - Rr - Cf */
+ tcg_gen_sub_tl(R, R, cpu_Cf);
+ tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */
+ /* update status register */
+ gen_sub_CHf(R, Rd, Rr);
+ gen_sub_Vf(R, Rd, Rr);
+ gen_NSf(R);
+
+ /*
+ * Previous value remains unchanged when the result is zero;
+ * cleared otherwise.
+ */
+ tcg_gen_movcond_tl(TCG_COND_EQ, cpu_Zf, R, zero, cpu_Zf, zero);
+
+ tcg_temp_free_i32(zero);
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * This instruction performs a compare between register Rd and a constant.
+ * The register is not changed. All conditional branches can be used after this
+ * instruction.
+ */
+static bool trans_CPI(DisasContext *ctx, arg_CPI *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ int Imm = a->imm;
+ TCGv Rr = tcg_const_i32(Imm);
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_sub_tl(R, Rd, Rr); /* R = Rd - Rr */
+ tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */
+
+ /* update status register */
+ gen_sub_CHf(R, Rd, Rr);
+ gen_sub_Vf(R, Rd, Rr);
+ gen_ZNSf(R);
+
+ tcg_temp_free_i32(R);
+ tcg_temp_free_i32(Rr);
+
+ return true;
+}
+
+/*
+ * This instruction tests a single bit in a register and skips the next
+ * instruction if the bit is cleared.
+ */
+static bool trans_SBRC(DisasContext *ctx, arg_SBRC *a)
+{
+ TCGv Rr = cpu_r[a->rr];
+
+ ctx->skip_cond = TCG_COND_EQ;
+ ctx->skip_var0 = tcg_temp_new();
+ ctx->free_skip_var0 = true;
+
+ tcg_gen_andi_tl(ctx->skip_var0, Rr, 1 << a->bit);
+ return true;
+}
+
+/*
+ * This instruction tests a single bit in a register and skips the next
+ * instruction if the bit is set.
+ */
+static bool trans_SBRS(DisasContext *ctx, arg_SBRS *a)
+{
+ TCGv Rr = cpu_r[a->rr];
+
+ ctx->skip_cond = TCG_COND_NE;
+ ctx->skip_var0 = tcg_temp_new();
+ ctx->free_skip_var0 = true;
+
+ tcg_gen_andi_tl(ctx->skip_var0, Rr, 1 << a->bit);
+ return true;
+}
+
+/*
+ * This instruction tests a single bit in an I/O Register and skips the
+ * next instruction if the bit is cleared. This instruction operates on the
+ * lower 32 I/O Registers -- addresses 0-31.
+ */
+static bool trans_SBIC(DisasContext *ctx, arg_SBIC *a)
+{
+ TCGv temp = tcg_const_i32(a->reg);
+
+ gen_helper_inb(temp, cpu_env, temp);
+ tcg_gen_andi_tl(temp, temp, 1 << a->bit);
+ ctx->skip_cond = TCG_COND_EQ;
+ ctx->skip_var0 = temp;
+ ctx->free_skip_var0 = true;
+
+ return true;
+}
+
+/*
+ * This instruction tests a single bit in an I/O Register and skips the
+ * next instruction if the bit is set. This instruction operates on the lower
+ * 32 I/O Registers -- addresses 0-31.
+ */
+static bool trans_SBIS(DisasContext *ctx, arg_SBIS *a)
+{
+ TCGv temp = tcg_const_i32(a->reg);
+
+ gen_helper_inb(temp, cpu_env, temp);
+ tcg_gen_andi_tl(temp, temp, 1 << a->bit);
+ ctx->skip_cond = TCG_COND_NE;
+ ctx->skip_var0 = temp;
+ ctx->free_skip_var0 = true;
+
+ return true;
+}
+
+/*
+ * Conditional relative branch. Tests a single bit in SREG and branches
+ * relatively to PC if the bit is cleared. This instruction branches relatively
+ * to PC in either direction (PC - 63 < = destination <= PC + 64). The
+ * parameter k is the offset from PC and is represented in two's complement
+ * form.
+ */
+static bool trans_BRBC(DisasContext *ctx, arg_BRBC *a)
+{
+ TCGLabel *not_taken = gen_new_label();
+
+ TCGv var;
+
+ switch (a->bit) {
+ case 0x00:
+ var = cpu_Cf;
+ break;
+ case 0x01:
+ var = cpu_Zf;
+ break;
+ case 0x02:
+ var = cpu_Nf;
+ break;
+ case 0x03:
+ var = cpu_Vf;
+ break;
+ case 0x04:
+ var = cpu_Sf;
+ break;
+ case 0x05:
+ var = cpu_Hf;
+ break;
+ case 0x06:
+ var = cpu_Tf;
+ break;
+ case 0x07:
+ var = cpu_If;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ tcg_gen_brcondi_i32(TCG_COND_NE, var, 0, not_taken);
+ gen_goto_tb(ctx, 0, ctx->npc + a->imm);
+ gen_set_label(not_taken);
+
+ ctx->bstate = DISAS_CHAIN;
+ return true;
+}
+
+/*
+ * Conditional relative branch. Tests a single bit in SREG and branches
+ * relatively to PC if the bit is set. This instruction branches relatively to
+ * PC in either direction (PC - 63 < = destination <= PC + 64). The parameter k
+ * is the offset from PC and is represented in two's complement form.
+ */
+static bool trans_BRBS(DisasContext *ctx, arg_BRBS *a)
+{
+ TCGLabel *not_taken = gen_new_label();
+
+ TCGv var;
+
+ switch (a->bit) {
+ case 0x00:
+ var = cpu_Cf;
+ break;
+ case 0x01:
+ var = cpu_Zf;
+ break;
+ case 0x02:
+ var = cpu_Nf;
+ break;
+ case 0x03:
+ var = cpu_Vf;
+ break;
+ case 0x04:
+ var = cpu_Sf;
+ break;
+ case 0x05:
+ var = cpu_Hf;
+ break;
+ case 0x06:
+ var = cpu_Tf;
+ break;
+ case 0x07:
+ var = cpu_If;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ tcg_gen_brcondi_i32(TCG_COND_EQ, var, 0, not_taken);
+ gen_goto_tb(ctx, 0, ctx->npc + a->imm);
+ gen_set_label(not_taken);
+
+ ctx->bstate = DISAS_CHAIN;
+ return true;
+}
+
+/*
+ * Data Transfer Instructions
+ */
+
+/*
+ * in the gen_set_addr & gen_get_addr functions
+ * H assumed to be in 0x00ff0000 format
+ * M assumed to be in 0x000000ff format
+ * L assumed to be in 0x000000ff format
+ */
+static void gen_set_addr(TCGv addr, TCGv H, TCGv M, TCGv L)
+{
+
+ tcg_gen_andi_tl(L, addr, 0x000000ff);
+
+ tcg_gen_andi_tl(M, addr, 0x0000ff00);
+ tcg_gen_shri_tl(M, M, 8);
+
+ tcg_gen_andi_tl(H, addr, 0x00ff0000);
+}
+
+static void gen_set_xaddr(TCGv addr)
+{
+ gen_set_addr(addr, cpu_rampX, cpu_r[27], cpu_r[26]);
+}
+
+static void gen_set_yaddr(TCGv addr)
+{
+ gen_set_addr(addr, cpu_rampY, cpu_r[29], cpu_r[28]);
+}
+
+static void gen_set_zaddr(TCGv addr)
+{
+ gen_set_addr(addr, cpu_rampZ, cpu_r[31], cpu_r[30]);
+}
+
+static TCGv gen_get_addr(TCGv H, TCGv M, TCGv L)
+{
+ TCGv addr = tcg_temp_new_i32();
+
+ tcg_gen_deposit_tl(addr, M, H, 8, 8);
+ tcg_gen_deposit_tl(addr, L, addr, 8, 16);
+
+ return addr;
+}
+
+static TCGv gen_get_xaddr(void)
+{
+ return gen_get_addr(cpu_rampX, cpu_r[27], cpu_r[26]);
+}
+
+static TCGv gen_get_yaddr(void)
+{
+ return gen_get_addr(cpu_rampY, cpu_r[29], cpu_r[28]);
+}
+
+static TCGv gen_get_zaddr(void)
+{
+ return gen_get_addr(cpu_rampZ, cpu_r[31], cpu_r[30]);
+}
+
+/*
+ * Load one byte indirect from data space to register and stores an clear
+ * the bits in data space specified by the register. The instruction can only
+ * be used towards internal SRAM. The data location is pointed to by the Z (16
+ * bits) Pointer Register in the Register File. Memory access is limited to the
+ * current data segment of 64KB. To access another data segment in devices with
+ * more than 64KB data space, the RAMPZ in register in the I/O area has to be
+ * changed. The Z-pointer Register is left unchanged by the operation. This
+ * instruction is especially suited for clearing status bits stored in SRAM.
+ */
+static void gen_data_store(DisasContext *ctx, TCGv data, TCGv addr)
+{
+ if (ctx->tb->flags & TB_FLAGS_FULL_ACCESS) {
+ gen_helper_fullwr(cpu_env, data, addr);
+ } else {
+ tcg_gen_qemu_st8(data, addr, MMU_DATA_IDX); /* mem[addr] = data */
+ }
+}
+
+static void gen_data_load(DisasContext *ctx, TCGv data, TCGv addr)
+{
+ if (ctx->tb->flags & TB_FLAGS_FULL_ACCESS) {
+ gen_helper_fullrd(data, cpu_env, addr);
+ } else {
+ tcg_gen_qemu_ld8u(data, addr, MMU_DATA_IDX); /* data = mem[addr] */
+ }
+}
+
+/*
+ * This instruction makes a copy of one register into another. The source
+ * register Rr is left unchanged, while the destination register Rd is loaded
+ * with a copy of Rr.
+ */
+static bool trans_MOV(DisasContext *ctx, arg_MOV *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+
+ tcg_gen_mov_tl(Rd, Rr);
+
+ return true;
+}
+
+/*
+ * This instruction makes a copy of one register pair into another register
+ * pair. The source register pair Rr+1:Rr is left unchanged, while the
+ * destination register pair Rd+1:Rd is loaded with a copy of Rr + 1:Rr. This
+ * instruction is not available in all devices. Refer to the device specific
+ * instruction set summary.
+ */
+static bool trans_MOVW(DisasContext *ctx, arg_MOVW *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_MOVW)) {
+ return true;
+ }
+
+ TCGv RdL = cpu_r[a->rd];
+ TCGv RdH = cpu_r[a->rd + 1];
+ TCGv RrL = cpu_r[a->rr];
+ TCGv RrH = cpu_r[a->rr + 1];
+
+ tcg_gen_mov_tl(RdH, RrH);
+ tcg_gen_mov_tl(RdL, RrL);
+
+ return true;
+}
+
+/*
+ * Loads an 8 bit constant directly to register 16 to 31.
+ */
+static bool trans_LDI(DisasContext *ctx, arg_LDI *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ int imm = a->imm;
+
+ tcg_gen_movi_tl(Rd, imm);
+
+ return true;
+}
+
+/*
+ * Loads one byte from the data space to a register. For parts with SRAM,
+ * the data space consists of the Register File, I/O memory and internal SRAM
+ * (and external SRAM if applicable). For parts without SRAM, the data space
+ * consists of the register file only. The EEPROM has a separate address space.
+ * A 16-bit address must be supplied. Memory access is limited to the current
+ * data segment of 64KB. The LDS instruction uses the RAMPD Register to access
+ * memory above 64KB. To access another data segment in devices with more than
+ * 64KB data space, the RAMPD in register in the I/O area has to be changed.
+ * This instruction is not available in all devices. Refer to the device
+ * specific instruction set summary.
+ */
+static bool trans_LDS(DisasContext *ctx, arg_LDS *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = tcg_temp_new_i32();
+ TCGv H = cpu_rampD;
+ a->imm = next_word(ctx);
+
+ tcg_gen_mov_tl(addr, H); /* addr = H:M:L */
+ tcg_gen_shli_tl(addr, addr, 16);
+ tcg_gen_ori_tl(addr, addr, a->imm);
+
+ gen_data_load(ctx, Rd, addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Loads one byte indirect from the data space to a register. For parts
+ * with SRAM, the data space consists of the Register File, I/O memory and
+ * internal SRAM (and external SRAM if applicable). For parts without SRAM, the
+ * data space consists of the Register File only. In some parts the Flash
+ * Memory has been mapped to the data space and can be read using this command.
+ * The EEPROM has a separate address space. The data location is pointed to by
+ * the X (16 bits) Pointer Register in the Register File. Memory access is
+ * limited to the current data segment of 64KB. To access another data segment
+ * in devices with more than 64KB data space, the RAMPX in register in the I/O
+ * area has to be changed. The X-pointer Register can either be left unchanged
+ * by the operation, or it can be post-incremented or predecremented. These
+ * features are especially suited for accessing arrays, tables, and Stack
+ * Pointer usage of the X-pointer Register. Note that only the low byte of the
+ * X-pointer is updated in devices with no more than 256 bytes data space. For
+ * such devices, the high byte of the pointer is not used by this instruction
+ * and can be used for other purposes. The RAMPX Register in the I/O area is
+ * updated in parts with more than 64KB data space or more than 64KB Program
+ * memory, and the increment/decrement is added to the entire 24-bit address on
+ * such devices. Not all variants of this instruction is available in all
+ * devices. Refer to the device specific instruction set summary. In the
+ * Reduced Core tinyAVR the LD instruction can be used to achieve the same
+ * operation as LPM since the program memory is mapped to the data memory
+ * space.
+ */
+static bool trans_LDX1(DisasContext *ctx, arg_LDX1 *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_xaddr();
+
+ gen_data_load(ctx, Rd, addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_LDX2(DisasContext *ctx, arg_LDX2 *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_xaddr();
+
+ gen_data_load(ctx, Rd, addr);
+ tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */
+
+ gen_set_xaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_LDX3(DisasContext *ctx, arg_LDX3 *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_xaddr();
+
+ tcg_gen_subi_tl(addr, addr, 1); /* addr = addr - 1 */
+ gen_data_load(ctx, Rd, addr);
+ gen_set_xaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Loads one byte indirect with or without displacement from the data space
+ * to a register. For parts with SRAM, the data space consists of the Register
+ * File, I/O memory and internal SRAM (and external SRAM if applicable). For
+ * parts without SRAM, the data space consists of the Register File only. In
+ * some parts the Flash Memory has been mapped to the data space and can be
+ * read using this command. The EEPROM has a separate address space. The data
+ * location is pointed to by the Y (16 bits) Pointer Register in the Register
+ * File. Memory access is limited to the current data segment of 64KB. To
+ * access another data segment in devices with more than 64KB data space, the
+ * RAMPY in register in the I/O area has to be changed. The Y-pointer Register
+ * can either be left unchanged by the operation, or it can be post-incremented
+ * or predecremented. These features are especially suited for accessing
+ * arrays, tables, and Stack Pointer usage of the Y-pointer Register. Note that
+ * only the low byte of the Y-pointer is updated in devices with no more than
+ * 256 bytes data space. For such devices, the high byte of the pointer is not
+ * used by this instruction and can be used for other purposes. The RAMPY
+ * Register in the I/O area is updated in parts with more than 64KB data space
+ * or more than 64KB Program memory, and the increment/decrement/displacement
+ * is added to the entire 24-bit address on such devices. Not all variants of
+ * this instruction is available in all devices. Refer to the device specific
+ * instruction set summary. In the Reduced Core tinyAVR the LD instruction can
+ * be used to achieve the same operation as LPM since the program memory is
+ * mapped to the data memory space.
+ */
+static bool trans_LDY2(DisasContext *ctx, arg_LDY2 *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_yaddr();
+
+ gen_data_load(ctx, Rd, addr);
+ tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */
+
+ gen_set_yaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_LDY3(DisasContext *ctx, arg_LDY3 *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_yaddr();
+
+ tcg_gen_subi_tl(addr, addr, 1); /* addr = addr - 1 */
+ gen_data_load(ctx, Rd, addr);
+ gen_set_yaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_LDDY(DisasContext *ctx, arg_LDDY *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_yaddr();
+
+ tcg_gen_addi_tl(addr, addr, a->imm); /* addr = addr + q */
+ gen_data_load(ctx, Rd, addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Loads one byte indirect with or without displacement from the data space
+ * to a register. For parts with SRAM, the data space consists of the Register
+ * File, I/O memory and internal SRAM (and external SRAM if applicable). For
+ * parts without SRAM, the data space consists of the Register File only. In
+ * some parts the Flash Memory has been mapped to the data space and can be
+ * read using this command. The EEPROM has a separate address space. The data
+ * location is pointed to by the Z (16 bits) Pointer Register in the Register
+ * File. Memory access is limited to the current data segment of 64KB. To
+ * access another data segment in devices with more than 64KB data space, the
+ * RAMPZ in register in the I/O area has to be changed. The Z-pointer Register
+ * can either be left unchanged by the operation, or it can be post-incremented
+ * or predecremented. These features are especially suited for Stack Pointer
+ * usage of the Z-pointer Register, however because the Z-pointer Register can
+ * be used for indirect subroutine calls, indirect jumps and table lookup, it
+ * is often more convenient to use the X or Y-pointer as a dedicated Stack
+ * Pointer. Note that only the low byte of the Z-pointer is updated in devices
+ * with no more than 256 bytes data space. For such devices, the high byte of
+ * the pointer is not used by this instruction and can be used for other
+ * purposes. The RAMPZ Register in the I/O area is updated in parts with more
+ * than 64KB data space or more than 64KB Program memory, and the
+ * increment/decrement/displacement is added to the entire 24-bit address on
+ * such devices. Not all variants of this instruction is available in all
+ * devices. Refer to the device specific instruction set summary. In the
+ * Reduced Core tinyAVR the LD instruction can be used to achieve the same
+ * operation as LPM since the program memory is mapped to the data memory
+ * space. For using the Z-pointer for table lookup in Program memory see the
+ * LPM and ELPM instructions.
+ */
+static bool trans_LDZ2(DisasContext *ctx, arg_LDZ2 *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_zaddr();
+
+ gen_data_load(ctx, Rd, addr);
+ tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */
+
+ gen_set_zaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_LDZ3(DisasContext *ctx, arg_LDZ3 *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_zaddr();
+
+ tcg_gen_subi_tl(addr, addr, 1); /* addr = addr - 1 */
+ gen_data_load(ctx, Rd, addr);
+
+ gen_set_zaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_LDDZ(DisasContext *ctx, arg_LDDZ *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_zaddr();
+
+ tcg_gen_addi_tl(addr, addr, a->imm); /* addr = addr + q */
+ gen_data_load(ctx, Rd, addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Stores one byte from a Register to the data space. For parts with SRAM,
+ * the data space consists of the Register File, I/O memory and internal SRAM
+ * (and external SRAM if applicable). For parts without SRAM, the data space
+ * consists of the Register File only. The EEPROM has a separate address space.
+ * A 16-bit address must be supplied. Memory access is limited to the current
+ * data segment of 64KB. The STS instruction uses the RAMPD Register to access
+ * memory above 64KB. To access another data segment in devices with more than
+ * 64KB data space, the RAMPD in register in the I/O area has to be changed.
+ * This instruction is not available in all devices. Refer to the device
+ * specific instruction set summary.
+ */
+static bool trans_STS(DisasContext *ctx, arg_STS *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = tcg_temp_new_i32();
+ TCGv H = cpu_rampD;
+ a->imm = next_word(ctx);
+
+ tcg_gen_mov_tl(addr, H); /* addr = H:M:L */
+ tcg_gen_shli_tl(addr, addr, 16);
+ tcg_gen_ori_tl(addr, addr, a->imm);
+ gen_data_store(ctx, Rd, addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Stores one byte indirect from a register to data space. For parts with SRAM,
+ * the data space consists of the Register File, I/O memory, and internal SRAM
+ * (and external SRAM if applicable). For parts without SRAM, the data space
+ * consists of the Register File only. The EEPROM has a separate address space.
+ *
+ * The data location is pointed to by the X (16 bits) Pointer Register in the
+ * Register File. Memory access is limited to the current data segment of 64KB.
+ * To access another data segment in devices with more than 64KB data space, the
+ * RAMPX in register in the I/O area has to be changed.
+ *
+ * The X-pointer Register can either be left unchanged by the operation, or it
+ * can be post-incremented or pre-decremented. These features are especially
+ * suited for accessing arrays, tables, and Stack Pointer usage of the
+ * X-pointer Register. Note that only the low byte of the X-pointer is updated
+ * in devices with no more than 256 bytes data space. For such devices, the high
+ * byte of the pointer is not used by this instruction and can be used for other
+ * purposes. The RAMPX Register in the I/O area is updated in parts with more
+ * than 64KB data space or more than 64KB Program memory, and the increment /
+ * decrement is added to the entire 24-bit address on such devices.
+ */
+static bool trans_STX1(DisasContext *ctx, arg_STX1 *a)
+{
+ TCGv Rd = cpu_r[a->rr];
+ TCGv addr = gen_get_xaddr();
+
+ gen_data_store(ctx, Rd, addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_STX2(DisasContext *ctx, arg_STX2 *a)
+{
+ TCGv Rd = cpu_r[a->rr];
+ TCGv addr = gen_get_xaddr();
+
+ gen_data_store(ctx, Rd, addr);
+ tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */
+ gen_set_xaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_STX3(DisasContext *ctx, arg_STX3 *a)
+{
+ TCGv Rd = cpu_r[a->rr];
+ TCGv addr = gen_get_xaddr();
+
+ tcg_gen_subi_tl(addr, addr, 1); /* addr = addr - 1 */
+ gen_data_store(ctx, Rd, addr);
+ gen_set_xaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Stores one byte indirect with or without displacement from a register to data
+ * space. For parts with SRAM, the data space consists of the Register File, I/O
+ * memory, and internal SRAM (and external SRAM if applicable). For parts
+ * without SRAM, the data space consists of the Register File only. The EEPROM
+ * has a separate address space.
+ *
+ * The data location is pointed to by the Y (16 bits) Pointer Register in the
+ * Register File. Memory access is limited to the current data segment of 64KB.
+ * To access another data segment in devices with more than 64KB data space, the
+ * RAMPY in register in the I/O area has to be changed.
+ *
+ * The Y-pointer Register can either be left unchanged by the operation, or it
+ * can be post-incremented or pre-decremented. These features are especially
+ * suited for accessing arrays, tables, and Stack Pointer usage of the Y-pointer
+ * Register. Note that only the low byte of the Y-pointer is updated in devices
+ * with no more than 256 bytes data space. For such devices, the high byte of
+ * the pointer is not used by this instruction and can be used for other
+ * purposes. The RAMPY Register in the I/O area is updated in parts with more
+ * than 64KB data space or more than 64KB Program memory, and the increment /
+ * decrement / displacement is added to the entire 24-bit address on such
+ * devices.
+ */
+static bool trans_STY2(DisasContext *ctx, arg_STY2 *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_yaddr();
+
+ gen_data_store(ctx, Rd, addr);
+ tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */
+ gen_set_yaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_STY3(DisasContext *ctx, arg_STY3 *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_yaddr();
+
+ tcg_gen_subi_tl(addr, addr, 1); /* addr = addr - 1 */
+ gen_data_store(ctx, Rd, addr);
+ gen_set_yaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_STDY(DisasContext *ctx, arg_STDY *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_yaddr();
+
+ tcg_gen_addi_tl(addr, addr, a->imm); /* addr = addr + q */
+ gen_data_store(ctx, Rd, addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Stores one byte indirect with or without displacement from a register to data
+ * space. For parts with SRAM, the data space consists of the Register File, I/O
+ * memory, and internal SRAM (and external SRAM if applicable). For parts
+ * without SRAM, the data space consists of the Register File only. The EEPROM
+ * has a separate address space.
+ *
+ * The data location is pointed to by the Y (16 bits) Pointer Register in the
+ * Register File. Memory access is limited to the current data segment of 64KB.
+ * To access another data segment in devices with more than 64KB data space, the
+ * RAMPY in register in the I/O area has to be changed.
+ *
+ * The Y-pointer Register can either be left unchanged by the operation, or it
+ * can be post-incremented or pre-decremented. These features are especially
+ * suited for accessing arrays, tables, and Stack Pointer usage of the Y-pointer
+ * Register. Note that only the low byte of the Y-pointer is updated in devices
+ * with no more than 256 bytes data space. For such devices, the high byte of
+ * the pointer is not used by this instruction and can be used for other
+ * purposes. The RAMPY Register in the I/O area is updated in parts with more
+ * than 64KB data space or more than 64KB Program memory, and the increment /
+ * decrement / displacement is added to the entire 24-bit address on such
+ * devices.
+ */
+static bool trans_STZ2(DisasContext *ctx, arg_STZ2 *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_zaddr();
+
+ gen_data_store(ctx, Rd, addr);
+ tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */
+
+ gen_set_zaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_STZ3(DisasContext *ctx, arg_STZ3 *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_zaddr();
+
+ tcg_gen_subi_tl(addr, addr, 1); /* addr = addr - 1 */
+ gen_data_store(ctx, Rd, addr);
+
+ gen_set_zaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_STDZ(DisasContext *ctx, arg_STDZ *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_zaddr();
+
+ tcg_gen_addi_tl(addr, addr, a->imm); /* addr = addr + q */
+ gen_data_store(ctx, Rd, addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Loads one byte pointed to by the Z-register into the destination
+ * register Rd. This instruction features a 100% space effective constant
+ * initialization or constant data fetch. The Program memory is organized in
+ * 16-bit words while the Z-pointer is a byte address. Thus, the least
+ * significant bit of the Z-pointer selects either low byte (ZLSB = 0) or high
+ * byte (ZLSB = 1). This instruction can address the first 64KB (32K words) of
+ * Program memory. The Zpointer Register can either be left unchanged by the
+ * operation, or it can be incremented. The incrementation does not apply to
+ * the RAMPZ Register.
+ *
+ * Devices with Self-Programming capability can use the LPM instruction to read
+ * the Fuse and Lock bit values.
+ */
+static bool trans_LPM1(DisasContext *ctx, arg_LPM1 *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_LPM)) {
+ return true;
+ }
+
+ TCGv Rd = cpu_r[0];
+ TCGv addr = tcg_temp_new_i32();
+ TCGv H = cpu_r[31];
+ TCGv L = cpu_r[30];
+
+ tcg_gen_shli_tl(addr, H, 8); /* addr = H:L */
+ tcg_gen_or_tl(addr, addr, L);
+ tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd = mem[addr] */
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_LPM2(DisasContext *ctx, arg_LPM2 *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_LPM)) {
+ return true;
+ }
+
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = tcg_temp_new_i32();
+ TCGv H = cpu_r[31];
+ TCGv L = cpu_r[30];
+
+ tcg_gen_shli_tl(addr, H, 8); /* addr = H:L */
+ tcg_gen_or_tl(addr, addr, L);
+ tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd = mem[addr] */
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_LPMX(DisasContext *ctx, arg_LPMX *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_LPMX)) {
+ return true;
+ }
+
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = tcg_temp_new_i32();
+ TCGv H = cpu_r[31];
+ TCGv L = cpu_r[30];
+
+ tcg_gen_shli_tl(addr, H, 8); /* addr = H:L */
+ tcg_gen_or_tl(addr, addr, L);
+ tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd = mem[addr] */
+ tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */
+ tcg_gen_andi_tl(L, addr, 0xff);
+ tcg_gen_shri_tl(addr, addr, 8);
+ tcg_gen_andi_tl(H, addr, 0xff);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Loads one byte pointed to by the Z-register and the RAMPZ Register in
+ * the I/O space, and places this byte in the destination register Rd. This
+ * instruction features a 100% space effective constant initialization or
+ * constant data fetch. The Program memory is organized in 16-bit words while
+ * the Z-pointer is a byte address. Thus, the least significant bit of the
+ * Z-pointer selects either low byte (ZLSB = 0) or high byte (ZLSB = 1). This
+ * instruction can address the entire Program memory space. The Z-pointer
+ * Register can either be left unchanged by the operation, or it can be
+ * incremented. The incrementation applies to the entire 24-bit concatenation
+ * of the RAMPZ and Z-pointer Registers.
+ *
+ * Devices with Self-Programming capability can use the ELPM instruction to
+ * read the Fuse and Lock bit value.
+ */
+static bool trans_ELPM1(DisasContext *ctx, arg_ELPM1 *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_ELPM)) {
+ return true;
+ }
+
+ TCGv Rd = cpu_r[0];
+ TCGv addr = gen_get_zaddr();
+
+ tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd = mem[addr] */
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_ELPM2(DisasContext *ctx, arg_ELPM2 *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_ELPM)) {
+ return true;
+ }
+
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_zaddr();
+
+ tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd = mem[addr] */
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_ELPMX(DisasContext *ctx, arg_ELPMX *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_ELPMX)) {
+ return true;
+ }
+
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_zaddr();
+
+ tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd = mem[addr] */
+ tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */
+ gen_set_zaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * SPM can be used to erase a page in the Program memory, to write a page
+ * in the Program memory (that is already erased), and to set Boot Loader Lock
+ * bits. In some devices, the Program memory can be written one word at a time,
+ * in other devices an entire page can be programmed simultaneously after first
+ * filling a temporary page buffer. In all cases, the Program memory must be
+ * erased one page at a time. When erasing the Program memory, the RAMPZ and
+ * Z-register are used as page address. When writing the Program memory, the
+ * RAMPZ and Z-register are used as page or word address, and the R1:R0
+ * register pair is used as data(1). When setting the Boot Loader Lock bits,
+ * the R1:R0 register pair is used as data. Refer to the device documentation
+ * for detailed description of SPM usage. This instruction can address the
+ * entire Program memory.
+ *
+ * The SPM instruction is not available in all devices. Refer to the device
+ * specific instruction set summary.
+ *
+ * Note: 1. R1 determines the instruction high byte, and R0 determines the
+ * instruction low byte.
+ */
+static bool trans_SPM(DisasContext *ctx, arg_SPM *a)
+{
+ /* TODO */
+ if (!avr_have_feature(ctx, AVR_FEATURE_SPM)) {
+ return true;
+ }
+
+ return true;
+}
+
+static bool trans_SPMX(DisasContext *ctx, arg_SPMX *a)
+{
+ /* TODO */
+ if (!avr_have_feature(ctx, AVR_FEATURE_SPMX)) {
+ return true;
+ }
+
+ return true;
+}
+
+/*
+ * Loads data from the I/O Space (Ports, Timers, Configuration Registers,
+ * etc.) into register Rd in the Register File.
+ */
+static bool trans_IN(DisasContext *ctx, arg_IN *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv port = tcg_const_i32(a->imm);
+
+ gen_helper_inb(Rd, cpu_env, port);
+
+ tcg_temp_free_i32(port);
+
+ return true;
+}
+
+/*
+ * Stores data from register Rr in the Register File to I/O Space (Ports,
+ * Timers, Configuration Registers, etc.).
+ */
+static bool trans_OUT(DisasContext *ctx, arg_OUT *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv port = tcg_const_i32(a->imm);
+
+ gen_helper_outb(cpu_env, port, Rd);
+
+ tcg_temp_free_i32(port);
+
+ return true;
+}
+
+/*
+ * This instruction stores the contents of register Rr on the STACK. The
+ * Stack Pointer is post-decremented by 1 after the PUSH. This instruction is
+ * not available in all devices. Refer to the device specific instruction set
+ * summary.
+ */
+static bool trans_PUSH(DisasContext *ctx, arg_PUSH *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+
+ gen_data_store(ctx, Rd, cpu_sp);
+ tcg_gen_subi_tl(cpu_sp, cpu_sp, 1);
+
+ return true;
+}
+
+/*
+ * This instruction loads register Rd with a byte from the STACK. The Stack
+ * Pointer is pre-incremented by 1 before the POP. This instruction is not
+ * available in all devices. Refer to the device specific instruction set
+ * summary.
+ */
+static bool trans_POP(DisasContext *ctx, arg_POP *a)
+{
+ /*
+ * Using a temp to work around some strange behaviour:
+ * tcg_gen_addi_tl(cpu_sp, cpu_sp, 1);
+ * gen_data_load(ctx, Rd, cpu_sp);
+ * seems to cause the add to happen twice.
+ * This doesn't happen if either the add or the load is removed.
+ */
+ TCGv t1 = tcg_temp_new_i32();
+ TCGv Rd = cpu_r[a->rd];
+
+ tcg_gen_addi_tl(t1, cpu_sp, 1);
+ gen_data_load(ctx, Rd, t1);
+ tcg_gen_mov_tl(cpu_sp, t1);
+
+ return true;
+}
+
+/*
+ * Exchanges one byte indirect between register and data space. The data
+ * location is pointed to by the Z (16 bits) Pointer Register in the Register
+ * File. Memory access is limited to the current data segment of 64KB. To
+ * access another data segment in devices with more than 64KB data space, the
+ * RAMPZ in register in the I/O area has to be changed.
+ *
+ * The Z-pointer Register is left unchanged by the operation. This instruction
+ * is especially suited for writing/reading status bits stored in SRAM.
+ */
+static bool trans_XCH(DisasContext *ctx, arg_XCH *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_RMW)) {
+ return true;
+ }
+
+ TCGv Rd = cpu_r[a->rd];
+ TCGv t0 = tcg_temp_new_i32();
+ TCGv addr = gen_get_zaddr();
+
+ gen_data_load(ctx, t0, addr);
+ gen_data_store(ctx, Rd, addr);
+ tcg_gen_mov_tl(Rd, t0);
+
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Load one byte indirect from data space to register and set bits in data
+ * space specified by the register. The instruction can only be used towards
+ * internal SRAM. The data location is pointed to by the Z (16 bits) Pointer
+ * Register in the Register File. Memory access is limited to the current data
+ * segment of 64KB. To access another data segment in devices with more than
+ * 64KB data space, the RAMPZ in register in the I/O area has to be changed.
+ *
+ * The Z-pointer Register is left unchanged by the operation. This instruction
+ * is especially suited for setting status bits stored in SRAM.
+ */
+static bool trans_LAS(DisasContext *ctx, arg_LAS *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_RMW)) {
+ return true;
+ }
+
+ TCGv Rr = cpu_r[a->rd];
+ TCGv addr = gen_get_zaddr();
+ TCGv t0 = tcg_temp_new_i32();
+ TCGv t1 = tcg_temp_new_i32();
+
+ gen_data_load(ctx, t0, addr); /* t0 = mem[addr] */
+ tcg_gen_or_tl(t1, t0, Rr);
+ tcg_gen_mov_tl(Rr, t0); /* Rr = t0 */
+ gen_data_store(ctx, t1, addr); /* mem[addr] = t1 */
+
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Load one byte indirect from data space to register and stores and clear
+ * the bits in data space specified by the register. The instruction can
+ * only be used towards internal SRAM. The data location is pointed to by
+ * the Z (16 bits) Pointer Register in the Register File. Memory access is
+ * limited to the current data segment of 64KB. To access another data
+ * segment in devices with more than 64KB data space, the RAMPZ in register
+ * in the I/O area has to be changed.
+ *
+ * The Z-pointer Register is left unchanged by the operation. This instruction
+ * is especially suited for clearing status bits stored in SRAM.
+ */
+static bool trans_LAC(DisasContext *ctx, arg_LAC *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_RMW)) {
+ return true;
+ }
+
+ TCGv Rr = cpu_r[a->rd];
+ TCGv addr = gen_get_zaddr();
+ TCGv t0 = tcg_temp_new_i32();
+ TCGv t1 = tcg_temp_new_i32();
+
+ gen_data_load(ctx, t0, addr); /* t0 = mem[addr] */
+ tcg_gen_andc_tl(t1, t0, Rr); /* t1 = t0 & (0xff - Rr) = t0 & ~Rr */
+ tcg_gen_mov_tl(Rr, t0); /* Rr = t0 */
+ gen_data_store(ctx, t1, addr); /* mem[addr] = t1 */
+
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+
+/*
+ * Load one byte indirect from data space to register and toggles bits in
+ * the data space specified by the register. The instruction can only be used
+ * towards SRAM. The data location is pointed to by the Z (16 bits) Pointer
+ * Register in the Register File. Memory access is limited to the current data
+ * segment of 64KB. To access another data segment in devices with more than
+ * 64KB data space, the RAMPZ in register in the I/O area has to be changed.
+ *
+ * The Z-pointer Register is left unchanged by the operation. This instruction
+ * is especially suited for changing status bits stored in SRAM.
+ */
+static bool trans_LAT(DisasContext *ctx, arg_LAT *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_RMW)) {
+ return true;
+ }
+
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_zaddr();
+ TCGv t0 = tcg_temp_new_i32();
+ TCGv t1 = tcg_temp_new_i32();
+
+ gen_data_load(ctx, t0, addr); /* t0 = mem[addr] */
+ tcg_gen_xor_tl(t1, t0, Rd);
+ tcg_gen_mov_tl(Rd, t0); /* Rd = t0 */
+ gen_data_store(ctx, t1, addr); /* mem[addr] = t1 */
+
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Bit and Bit-test Instructions
+ */
+static void gen_rshift_ZNVSf(TCGv R)
+{
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */
+ tcg_gen_shri_tl(cpu_Nf, R, 7); /* Nf = R(7) */
+ tcg_gen_xor_tl(cpu_Vf, cpu_Nf, cpu_Cf);
+ tcg_gen_xor_tl(cpu_Sf, cpu_Nf, cpu_Vf); /* Sf = Nf ^ Vf */
+}
+
+/*
+ * Shifts all bits in Rd one place to the right. Bit 7 is cleared. Bit 0 is
+ * loaded into the C Flag of the SREG. This operation effectively divides an
+ * unsigned value by two. The C Flag can be used to round the result.
+ */
+static bool trans_LSR(DisasContext *ctx, arg_LSR *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+
+ tcg_gen_andi_tl(cpu_Cf, Rd, 1);
+ tcg_gen_shri_tl(Rd, Rd, 1);
+
+ /* update status register */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, Rd, 0); /* Zf = Rd == 0 */
+ tcg_gen_movi_tl(cpu_Nf, 0);
+ tcg_gen_mov_tl(cpu_Vf, cpu_Cf);
+ tcg_gen_mov_tl(cpu_Sf, cpu_Vf);
+
+ return true;
+}
+
+/*
+ * Shifts all bits in Rd one place to the right. The C Flag is shifted into
+ * bit 7 of Rd. Bit 0 is shifted into the C Flag. This operation, combined
+ * with ASR, effectively divides multi-byte signed values by two. Combined with
+ * LSR it effectively divides multi-byte unsigned values by two. The Carry Flag
+ * can be used to round the result.
+ */
+static bool trans_ROR(DisasContext *ctx, arg_ROR *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv t0 = tcg_temp_new_i32();
+
+ tcg_gen_shli_tl(t0, cpu_Cf, 7);
+
+ /* update status register */
+ tcg_gen_andi_tl(cpu_Cf, Rd, 1);
+
+ /* update output register */
+ tcg_gen_shri_tl(Rd, Rd, 1);
+ tcg_gen_or_tl(Rd, Rd, t0);
+
+ /* update status register */
+ gen_rshift_ZNVSf(Rd);
+
+ tcg_temp_free_i32(t0);
+
+ return true;
+}
+
+/*
+ * Shifts all bits in Rd one place to the right. Bit 7 is held constant. Bit 0
+ * is loaded into the C Flag of the SREG. This operation effectively divides a
+ * signed value by two without changing its sign. The Carry Flag can be used to
+ * round the result.
+ */
+static bool trans_ASR(DisasContext *ctx, arg_ASR *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv t0 = tcg_temp_new_i32();
+
+ /* update status register */
+ tcg_gen_andi_tl(cpu_Cf, Rd, 1); /* Cf = Rd(0) */
+
+ /* update output register */
+ tcg_gen_andi_tl(t0, Rd, 0x80); /* Rd = (Rd & 0x80) | (Rd >> 1) */
+ tcg_gen_shri_tl(Rd, Rd, 1);
+ tcg_gen_or_tl(Rd, Rd, t0);
+
+ /* update status register */
+ gen_rshift_ZNVSf(Rd);
+
+ tcg_temp_free_i32(t0);
+
+ return true;
+}
+
+/*
+ * Swaps high and low nibbles in a register.
+ */
+static bool trans_SWAP(DisasContext *ctx, arg_SWAP *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv t0 = tcg_temp_new_i32();
+ TCGv t1 = tcg_temp_new_i32();
+
+ tcg_gen_andi_tl(t0, Rd, 0x0f);
+ tcg_gen_shli_tl(t0, t0, 4);
+ tcg_gen_andi_tl(t1, Rd, 0xf0);
+ tcg_gen_shri_tl(t1, t1, 4);
+ tcg_gen_or_tl(Rd, t0, t1);
+
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t0);
+
+ return true;
+}
+
+/*
+ * Sets a specified bit in an I/O Register. This instruction operates on
+ * the lower 32 I/O Registers -- addresses 0-31.
+ */
+static bool trans_SBI(DisasContext *ctx, arg_SBI *a)
+{
+ TCGv data = tcg_temp_new_i32();
+ TCGv port = tcg_const_i32(a->reg);
+
+ gen_helper_inb(data, cpu_env, port);
+ tcg_gen_ori_tl(data, data, 1 << a->bit);
+ gen_helper_outb(cpu_env, port, data);
+
+ tcg_temp_free_i32(port);
+ tcg_temp_free_i32(data);
+
+ return true;
+}
+
+/*
+ * Clears a specified bit in an I/O Register. This instruction operates on
+ * the lower 32 I/O Registers -- addresses 0-31.
+ */
+static bool trans_CBI(DisasContext *ctx, arg_CBI *a)
+{
+ TCGv data = tcg_temp_new_i32();
+ TCGv port = tcg_const_i32(a->reg);
+
+ gen_helper_inb(data, cpu_env, port);
+ tcg_gen_andi_tl(data, data, ~(1 << a->bit));
+ gen_helper_outb(cpu_env, port, data);
+
+ tcg_temp_free_i32(data);
+ tcg_temp_free_i32(port);
+
+ return true;
+}
+
+/*
+ * Stores bit b from Rd to the T Flag in SREG (Status Register).
+ */
+static bool trans_BST(DisasContext *ctx, arg_BST *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+
+ tcg_gen_andi_tl(cpu_Tf, Rd, 1 << a->bit);
+ tcg_gen_shri_tl(cpu_Tf, cpu_Tf, a->bit);
+
+ return true;
+}
+
+/*
+ * Copies the T Flag in the SREG (Status Register) to bit b in register Rd.
+ */
+static bool trans_BLD(DisasContext *ctx, arg_BLD *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv t1 = tcg_temp_new_i32();
+
+ tcg_gen_andi_tl(Rd, Rd, ~(1u << a->bit)); /* clear bit */
+ tcg_gen_shli_tl(t1, cpu_Tf, a->bit); /* create mask */
+ tcg_gen_or_tl(Rd, Rd, t1);
+
+ tcg_temp_free_i32(t1);
+
+ return true;
+}
+
+/*
+ * Sets a single Flag or bit in SREG.
+ */
+static bool trans_BSET(DisasContext *ctx, arg_BSET *a)
+{
+ switch (a->bit) {
+ case 0x00:
+ tcg_gen_movi_tl(cpu_Cf, 0x01);
+ break;
+ case 0x01:
+ tcg_gen_movi_tl(cpu_Zf, 0x01);
+ break;
+ case 0x02:
+ tcg_gen_movi_tl(cpu_Nf, 0x01);
+ break;
+ case 0x03:
+ tcg_gen_movi_tl(cpu_Vf, 0x01);
+ break;
+ case 0x04:
+ tcg_gen_movi_tl(cpu_Sf, 0x01);
+ break;
+ case 0x05:
+ tcg_gen_movi_tl(cpu_Hf, 0x01);
+ break;
+ case 0x06:
+ tcg_gen_movi_tl(cpu_Tf, 0x01);
+ break;
+ case 0x07:
+ tcg_gen_movi_tl(cpu_If, 0x01);
+ break;
+ }
+
+ return true;
+}
+
+/*
+ * Clears a single Flag in SREG.
+ */
+static bool trans_BCLR(DisasContext *ctx, arg_BCLR *a)
+{
+ switch (a->bit) {
+ case 0x00:
+ tcg_gen_movi_tl(cpu_Cf, 0x00);
+ break;
+ case 0x01:
+ tcg_gen_movi_tl(cpu_Zf, 0x00);
+ break;
+ case 0x02:
+ tcg_gen_movi_tl(cpu_Nf, 0x00);
+ break;
+ case 0x03:
+ tcg_gen_movi_tl(cpu_Vf, 0x00);
+ break;
+ case 0x04:
+ tcg_gen_movi_tl(cpu_Sf, 0x00);
+ break;
+ case 0x05:
+ tcg_gen_movi_tl(cpu_Hf, 0x00);
+ break;
+ case 0x06:
+ tcg_gen_movi_tl(cpu_Tf, 0x00);
+ break;
+ case 0x07:
+ tcg_gen_movi_tl(cpu_If, 0x00);
+ break;
+ }
+
+ return true;
+}
+
+/*
+ * MCU Control Instructions
+ */
+
+/*
+ * The BREAK instruction is used by the On-chip Debug system, and is
+ * normally not used in the application software. When the BREAK instruction is
+ * executed, the AVR CPU is set in the Stopped Mode. This gives the On-chip
+ * Debugger access to internal resources. If any Lock bits are set, or either
+ * the JTAGEN or OCDEN Fuses are unprogrammed, the CPU will treat the BREAK
+ * instruction as a NOP and will not enter the Stopped mode. This instruction
+ * is not available in all devices. Refer to the device specific instruction
+ * set summary.
+ */
+static bool trans_BREAK(DisasContext *ctx, arg_BREAK *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_BREAK)) {
+ return true;
+ }
+
+#ifdef BREAKPOINT_ON_BREAK
+ tcg_gen_movi_tl(cpu_pc, ctx->npc - 1);
+ gen_helper_debug(cpu_env);
+ ctx->bstate = DISAS_EXIT;
+#else
+ /* NOP */
+#endif
+
+ return true;
+}
+
+/*
+ * This instruction performs a single cycle No Operation.
+ */
+static bool trans_NOP(DisasContext *ctx, arg_NOP *a)
+{
+
+ /* NOP */
+
+ return true;
+}
+
+/*
+ * This instruction sets the circuit in sleep mode defined by the MCU
+ * Control Register.
+ */
+static bool trans_SLEEP(DisasContext *ctx, arg_SLEEP *a)
+{
+ gen_helper_sleep(cpu_env);
+ ctx->bstate = DISAS_NORETURN;
+ return true;
+}
+
+/*
+ * This instruction resets the Watchdog Timer. This instruction must be
+ * executed within a limited time given by the WD prescaler. See the Watchdog
+ * Timer hardware specification.
+ */
+static bool trans_WDR(DisasContext *ctx, arg_WDR *a)
+{
+ gen_helper_wdr(cpu_env);
+
+ return true;
+}
+
+/*
+ * Core translation mechanism functions:
+ *
+ * - translate()
+ * - canonicalize_skip()
+ * - gen_intermediate_code()
+ * - restore_state_to_opc()
+ *
+ */
+static void translate(DisasContext *ctx)
+{
+ uint32_t opcode = next_word(ctx);
+
+ if (!decode_insn(ctx, opcode)) {
+ gen_helper_unsupported(cpu_env);
+ ctx->bstate = DISAS_NORETURN;
+ }
+}
+
+/* Standardize the cpu_skip condition to NE. */
+static bool canonicalize_skip(DisasContext *ctx)
+{
+ switch (ctx->skip_cond) {
+ case TCG_COND_NEVER:
+ /* Normal case: cpu_skip is known to be false. */
+ return false;
+
+ case TCG_COND_ALWAYS:
+ /*
+ * Breakpoint case: cpu_skip is known to be true, via TB_FLAGS_SKIP.
+ * The breakpoint is on the instruction being skipped, at the start
+ * of the TranslationBlock. No need to update.
+ */
+ return false;
+
+ case TCG_COND_NE:
+ if (ctx->skip_var1 == NULL) {
+ tcg_gen_mov_tl(cpu_skip, ctx->skip_var0);
+ } else {
+ tcg_gen_xor_tl(cpu_skip, ctx->skip_var0, ctx->skip_var1);
+ ctx->skip_var1 = NULL;
+ }
+ break;
+
+ default:
+ /* Convert to a NE condition vs 0. */
+ if (ctx->skip_var1 == NULL) {
+ tcg_gen_setcondi_tl(ctx->skip_cond, cpu_skip, ctx->skip_var0, 0);
+ } else {
+ tcg_gen_setcond_tl(ctx->skip_cond, cpu_skip,
+ ctx->skip_var0, ctx->skip_var1);
+ ctx->skip_var1 = NULL;
+ }
+ ctx->skip_cond = TCG_COND_NE;
+ break;
+ }
+ if (ctx->free_skip_var0) {
+ tcg_temp_free(ctx->skip_var0);
+ ctx->free_skip_var0 = false;
+ }
+ ctx->skip_var0 = cpu_skip;
+ return true;
+}
+
+void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns)
+{
+ CPUAVRState *env = cs->env_ptr;
+ DisasContext ctx = {
+ .tb = tb,
+ .cs = cs,
+ .env = env,
+ .memidx = 0,
+ .bstate = DISAS_NEXT,
+ .skip_cond = TCG_COND_NEVER,
+ .singlestep = cs->singlestep_enabled,
+ };
+ target_ulong pc_start = tb->pc / 2;
+ int num_insns = 0;
+
+ if (tb->flags & TB_FLAGS_FULL_ACCESS) {
+ /*
+ * This flag is set by ST/LD instruction we will regenerate it ONLY
+ * with mem/cpu memory access instead of mem access
+ */
+ max_insns = 1;
+ }
+ if (ctx.singlestep) {
+ max_insns = 1;
+ }
+
+ gen_tb_start(tb);
+
+ ctx.npc = pc_start;
+ if (tb->flags & TB_FLAGS_SKIP) {
+ ctx.skip_cond = TCG_COND_ALWAYS;
+ ctx.skip_var0 = cpu_skip;
+ }
+
+ do {
+ TCGLabel *skip_label = NULL;
+
+ /* translate current instruction */
+ tcg_gen_insn_start(ctx.npc);
+ num_insns++;
+
+ /*
+ * this is due to some strange GDB behavior
+ * let's assume main has address 0x100
+ * b main - sets breakpoint at address 0x00000100 (code)
+ * b *0x100 - sets breakpoint at address 0x00800100 (data)
+ */
+ if (unlikely(!ctx.singlestep &&
+ (cpu_breakpoint_test(cs, OFFSET_CODE + ctx.npc * 2, BP_ANY) ||
+ cpu_breakpoint_test(cs, OFFSET_DATA + ctx.npc * 2, BP_ANY)))) {
+ canonicalize_skip(&ctx);
+ tcg_gen_movi_tl(cpu_pc, ctx.npc);
+ gen_helper_debug(cpu_env);
+ goto done_generating;
+ }
+
+ /* Conditionally skip the next instruction, if indicated. */
+ if (ctx.skip_cond != TCG_COND_NEVER) {
+ skip_label = gen_new_label();
+ if (ctx.skip_var0 == cpu_skip) {
+ /*
+ * Copy cpu_skip so that we may zero it before the branch.
+ * This ensures that cpu_skip is non-zero after the label
+ * if and only if the skipped insn itself sets a skip.
+ */
+ ctx.free_skip_var0 = true;
+ ctx.skip_var0 = tcg_temp_new();
+ tcg_gen_mov_tl(ctx.skip_var0, cpu_skip);
+ tcg_gen_movi_tl(cpu_skip, 0);
+ }
+ if (ctx.skip_var1 == NULL) {
+ tcg_gen_brcondi_tl(ctx.skip_cond, ctx.skip_var0, 0, skip_label);
+ } else {
+ tcg_gen_brcond_tl(ctx.skip_cond, ctx.skip_var0,
+ ctx.skip_var1, skip_label);
+ ctx.skip_var1 = NULL;
+ }
+ if (ctx.free_skip_var0) {
+ tcg_temp_free(ctx.skip_var0);
+ ctx.free_skip_var0 = false;
+ }
+ ctx.skip_cond = TCG_COND_NEVER;
+ ctx.skip_var0 = NULL;
+ }
+
+ translate(&ctx);
+
+ if (skip_label) {
+ canonicalize_skip(&ctx);
+ gen_set_label(skip_label);
+ if (ctx.bstate == DISAS_NORETURN) {
+ ctx.bstate = DISAS_CHAIN;
+ }
+ }
+ } while (ctx.bstate == DISAS_NEXT
+ && num_insns < max_insns
+ && (ctx.npc - pc_start) * 2 < TARGET_PAGE_SIZE - 4
+ && !tcg_op_buf_full());
+
+ if (tb->cflags & CF_LAST_IO) {
+ gen_io_end();
+ }
+
+ bool nonconst_skip = canonicalize_skip(&ctx);
+
+ switch (ctx.bstate) {
+ case DISAS_NORETURN:
+ assert(!nonconst_skip);
+ break;
+ case DISAS_NEXT:
+ case DISAS_TOO_MANY:
+ case DISAS_CHAIN:
+ if (!nonconst_skip) {
+ /* Note gen_goto_tb checks singlestep. */
+ gen_goto_tb(&ctx, 1, ctx.npc);
+ break;
+ }
+ tcg_gen_movi_tl(cpu_pc, ctx.npc);
+ /* fall through */
+ case DISAS_LOOKUP:
+ if (!ctx.singlestep) {
+ tcg_gen_lookup_and_goto_ptr();
+ break;
+ }
+ /* fall through */
+ case DISAS_EXIT:
+ if (ctx.singlestep) {
+ gen_helper_debug(cpu_env);
+ } else {
+ tcg_gen_exit_tb(NULL, 0);
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+done_generating:
+ gen_tb_end(tb, num_insns);
+
+ tb->size = (ctx.npc - pc_start) * 2;
+ tb->icount = num_insns;
+
+#ifdef DEBUG_DISAS
+ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
+ && qemu_log_in_addr_range(tb->pc)) {
+ FILE *fd;
+ fd = qemu_log_lock();
+ qemu_log("IN: %s\n", lookup_symbol(tb->pc));
+ log_target_disas(cs, tb->pc, tb->size);
+ qemu_log("\n");
+ qemu_log_unlock(fd);
+ }
+#endif
+}
+
+void restore_state_to_opc(CPUAVRState *env, TranslationBlock *tb,
+ target_ulong *data)
+{
+ env->pc_w = data[0];
+}
obj-$(CONFIG_TCG) += bpt_helper.o cc_helper.o excp_helper.o fpu_helper.o
obj-$(CONFIG_TCG) += int_helper.o mem_helper.o misc_helper.o mpx_helper.o
obj-$(CONFIG_TCG) += seg_helper.o smm_helper.o svm_helper.o
+obj-$(call lnot,$(CONFIG_TCG)) += tcg-stub.o
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
ifeq ($(CONFIG_SOFTMMU),y)
obj-y += machine.o arch_memory_mapping.o arch_dump.o monitor.o
.type = CPUID_FEATURE_WORD,
.feat_names = {
NULL, NULL, "avx512-4vnniw", "avx512-4fmaps",
- NULL, NULL, NULL, NULL,
+ "fsrm", NULL, NULL, NULL,
"avx512-vp2intersect", NULL, "md-clear", NULL,
- NULL, NULL, NULL, NULL,
- NULL, NULL, NULL /* pconfig */, NULL,
+ NULL, NULL, "serialize", NULL,
+ "tsx-ldtrk", NULL, NULL /* pconfig */, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, "spec-ctrl", "stibp",
NULL, "arch-capabilities", "core-capability", "ssbd",
{ /* end of list */ }
}
},
+ {
+ .version = 4,
+ .props = (PropValue[]) {
+ { "vmx-eptp-switching", "on" },
+ { /* end of list */ }
+ }
+ },
{ /* end of list */ }
}
},
{ /* end of list */ }
},
},
+ { .version = 4,
+ .note = "ARCH_CAPABILITIES, no TSX",
+ .props = (PropValue[]) {
+ { "vmx-eptp-switching", "on" },
+ { /* end of list */ }
+ },
+ },
{ /* end of list */ }
}
},
{ /* end of list */ }
},
},
+ {
+ .version = 4,
+ .props = (PropValue[]) {
+ { "sha-ni", "on" },
+ { "avx512ifma", "on" },
+ { "rdpid", "on" },
+ { "fsrm", "on" },
+ { "vmx-rdseed-exit", "on" },
+ { "vmx-pml", "on" },
+ { "vmx-eptp-switching", "on" },
+ { "model", "106" },
+ { /* end of list */ }
+ },
+ },
{ /* end of list */ }
}
},
object_property_set_str(OBJECT(cpu), "vendor", vendor, &error_abort);
x86_cpu_apply_version_props(cpu, model);
+
+ /*
+ * Properties in versioned CPU model are not user specified features.
+ * We can simply clear env->user_features here since it will be filled later
+ * in x86_cpu_expand_features() based on plus_features and minus_features.
+ */
+ memset(&env->user_features, 0, sizeof(env->user_features));
}
#ifndef CONFIG_USER_ONLY
/* init to reset state */
env->hflags2 |= HF2_GIF_MASK;
+ env->hflags &= ~HF_GUEST_MASK;
cpu_x86_update_cr0(env, 0x60000010);
env->a20_mask = ~0x0;
if (kvm_enabled()) {
kvm_arch_reset_vcpu(cpu);
}
- else if (hvf_enabled()) {
- hvf_reset_vcpu(s);
- }
#endif
}
unavailable_features & env->user_features[d->to.index],
"This feature depends on other features that were not requested");
- env->user_features[d->to.index] |= unavailable_features;
env->features[d->to.index] &= ~unavailable_features;
}
}
} else if (cpu->env.cpuid_min_level < 0x14) {
mark_unavailable_features(cpu, FEAT_7_0_EBX,
CPUID_7_0_EBX_INTEL_PT,
- "Intel PT need CPUID leaf 0x14, please set by \"-cpu ...,+intel-pt,level=0x14\"");
+ "Intel PT need CPUID leaf 0x14, please set by \"-cpu ...,+intel-pt,min-level=0x14\"");
}
}
host_cpuid(5, 0, &cpu->mwait.eax, &cpu->mwait.ebx,
&cpu->mwait.ecx, &cpu->mwait.edx);
env->features[FEAT_1_ECX] |= CPUID_EXT_MONITOR;
+ if (kvm_enabled() && kvm_has_waitpkg()) {
+ env->features[FEAT_7_0_ECX] |= CPUID_7_0_ECX_WAITPKG;
+ }
}
if (kvm_enabled() && cpu->ucode_rev == 0) {
cpu->ucode_rev = kvm_arch_get_supported_msr_feature(kvm_state,
#define CPUID_7_0_EDX_AVX512_4VNNIW (1U << 2)
/* AVX512 Multiply Accumulation Single Precision */
#define CPUID_7_0_EDX_AVX512_4FMAPS (1U << 3)
+/* Fast Short Rep Mov */
+#define CPUID_7_0_EDX_FSRM (1U << 4)
/* AVX512 Vector Pair Intersection to a Pair of Mask Registers */
#define CPUID_7_0_EDX_AVX512_VP2INTERSECT (1U << 8)
+/* SERIALIZE instruction */
+#define CPUID_7_0_EDX_SERIALIZE (1U << 14)
+/* TSX Suspend Load Address Tracking instruction */
+#define CPUID_7_0_EDX_TSX_LDTRK (1U << 16)
/* Speculation Control */
#define CPUID_7_0_EDX_SPEC_CTRL (1U << 26)
/* Single Thread Indirect Branch Predictors */
return env->features[FEAT_1_ECX] & CPUID_EXT_VMX;
}
+static inline bool cpu_has_svm(CPUX86State *env)
+{
+ return env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_SVM;
+}
+
/*
* In order for a vCPU to enter VMX operation it must have CR4.VMXE set.
* Since it was set, CR4.VMXE must remain set as long as vCPU is in
/* fpu_helper.c */
void update_fp_status(CPUX86State *env);
void update_mxcsr_status(CPUX86State *env);
+void update_mxcsr_from_sse_status(CPUX86State *env);
static inline void cpu_set_mxcsr(CPUX86State *env, uint32_t mxcsr)
{
}
ptep = pde | PG_NX_MASK;
- /* if PSE bit is set, then we use a 4MB page */
- if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
+ /* if host cr4 PSE bit is set, then we use a 4MB page */
+ if ((pde & PG_PSE_MASK) && (env->nested_pg_mode & SVM_NPT_PSE)) {
page_size = 4096 * 1024;
pte_addr = pde_addr;
static void do_xsave_mxcsr(CPUX86State *env, target_ulong ptr, uintptr_t ra)
{
+ update_mxcsr_from_sse_status(env);
cpu_stl_data_ra(env, ptr + XO(legacy.mxcsr), env->mxcsr, ra);
cpu_stl_data_ra(env, ptr + XO(legacy.mxcsr_mask), 0x0000ffff, ra);
}
}
set_float_rounding_mode(rnd_type, &env->sse_status);
+ /* Set exception flags. */
+ set_float_exception_flags((mxcsr & FPUS_IE ? float_flag_invalid : 0) |
+ (mxcsr & FPUS_ZE ? float_flag_divbyzero : 0) |
+ (mxcsr & FPUS_OE ? float_flag_overflow : 0) |
+ (mxcsr & FPUS_UE ? float_flag_underflow : 0) |
+ (mxcsr & FPUS_PE ? float_flag_inexact : 0),
+ &env->sse_status);
+
/* set denormals are zero */
set_flush_inputs_to_zero((mxcsr & SSE_DAZ) ? 1 : 0, &env->sse_status);
/* set flush to zero */
- set_flush_to_zero((mxcsr & SSE_FZ) ? 1 : 0, &env->fp_status);
+ set_flush_to_zero((mxcsr & SSE_FZ) ? 1 : 0, &env->sse_status);
+}
+
+void update_mxcsr_from_sse_status(CPUX86State *env)
+{
+ if (tcg_enabled()) {
+ uint8_t flags = get_float_exception_flags(&env->sse_status);
+ /*
+ * The MXCSR denormal flag has opposite semantics to
+ * float_flag_input_denormal (the softfloat code sets that flag
+ * only when flushing input denormals to zero, but SSE sets it
+ * only when not flushing them to zero), so is not converted
+ * here.
+ */
+ env->mxcsr |= ((flags & float_flag_invalid ? FPUS_IE : 0) |
+ (flags & float_flag_divbyzero ? FPUS_ZE : 0) |
+ (flags & float_flag_overflow ? FPUS_OE : 0) |
+ (flags & float_flag_underflow ? FPUS_UE : 0) |
+ (flags & float_flag_inexact ? FPUS_PE : 0) |
+ (flags & float_flag_output_denormal ? FPUS_UE | FPUS_PE :
+ 0));
+ }
+}
+
+void helper_update_mxcsr(CPUX86State *env)
+{
+ update_mxcsr_from_sse_status(env);
}
void helper_ldmxcsr(CPUX86State *env, uint32_t val)
return gdb_get_reg32(mem_buf, 0); /* fop */
case IDX_MXCSR_REG:
+ update_mxcsr_from_sse_status(env);
return gdb_get_reg32(mem_buf, env->mxcsr);
case IDX_CTL_CR0_REG:
fprintf(stderr, "Failed to open the hax module\n");
}
- fcntl(fd, F_SETFD, FD_CLOEXEC);
+ qemu_set_cloexec(fd);
return fd;
}
fd = open(vm_name, O_RDWR);
g_free(vm_name);
- fcntl(fd, F_SETFD, FD_CLOEXEC);
+ qemu_set_cloexec(fd);
return fd;
}
if (fd < 0) {
fprintf(stderr, "Failed to open the vcpu devfs\n");
}
- fcntl(fd, F_SETFD, FD_CLOEXEC);
+ qemu_set_cloexec(fd);
return fd;
}
dump_apic_lvt("LVTTHMR", lvt[APIC_LVT_THERMAL], false);
dump_apic_lvt("LVTT", lvt[APIC_LVT_TIMER], true);
- qemu_printf("Timer\t DCR=0x%x (divide by %u) initial_count = %u\n",
+ qemu_printf("Timer\t DCR=0x%x (divide by %u) initial_count = %u"
+ " current_count = %u\n",
s->divide_conf & APIC_DCR_MASK,
divider_conf(s->divide_conf),
- s->initial_count);
+ s->initial_count, apic_get_current_count(s));
qemu_printf("SPIV\t 0x%08x APIC %s, focus=%s, spurious vec %u\n",
s->spurious_vec,
for(i = 0; i < 8; i++) {
fptag |= ((!env->fptags[i]) << i);
}
+ update_mxcsr_from_sse_status(env);
qemu_fprintf(f, "FCW=%04x FSW=%04x [ST=%d] FTW=%02x MXCSR=%08x\n",
env->fpuc,
(env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11,
/* MMX/SSE */
DEF_HELPER_2(ldmxcsr, void, env, i32)
+DEF_HELPER_1(update_mxcsr, void, env)
DEF_HELPER_1(enter_mmx, void, env)
DEF_HELPER_1(emms, void, env)
DEF_HELPER_3(movq, void, env, ptr, ptr)
}
}
-/* TODO: synchronize vcpu state */
static void do_hvf_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg)
{
- CPUState *cpu_state = cpu;
- if (cpu_state->vcpu_dirty == 0) {
- hvf_get_registers(cpu_state);
+ if (!cpu->vcpu_dirty) {
+ hvf_get_registers(cpu);
+ cpu->vcpu_dirty = true;
}
-
- cpu_state->vcpu_dirty = 1;
}
-void hvf_cpu_synchronize_state(CPUState *cpu_state)
+void hvf_cpu_synchronize_state(CPUState *cpu)
{
- if (cpu_state->vcpu_dirty == 0) {
- run_on_cpu(cpu_state, do_hvf_cpu_synchronize_state, RUN_ON_CPU_NULL);
+ if (!cpu->vcpu_dirty) {
+ run_on_cpu(cpu, do_hvf_cpu_synchronize_state, RUN_ON_CPU_NULL);
}
}
-static void do_hvf_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg)
+static void do_hvf_cpu_synchronize_post_reset(CPUState *cpu,
+ run_on_cpu_data arg)
{
- CPUState *cpu_state = cpu;
- hvf_put_registers(cpu_state);
- cpu_state->vcpu_dirty = false;
+ hvf_put_registers(cpu);
+ cpu->vcpu_dirty = false;
}
-void hvf_cpu_synchronize_post_reset(CPUState *cpu_state)
+void hvf_cpu_synchronize_post_reset(CPUState *cpu)
{
- run_on_cpu(cpu_state, do_hvf_cpu_synchronize_post_reset, RUN_ON_CPU_NULL);
+ run_on_cpu(cpu, do_hvf_cpu_synchronize_post_reset, RUN_ON_CPU_NULL);
}
static void do_hvf_cpu_synchronize_post_init(CPUState *cpu,
run_on_cpu_data arg)
{
- CPUState *cpu_state = cpu;
- hvf_put_registers(cpu_state);
- cpu_state->vcpu_dirty = false;
+ hvf_put_registers(cpu);
+ cpu->vcpu_dirty = false;
+}
+
+void hvf_cpu_synchronize_post_init(CPUState *cpu)
+{
+ run_on_cpu(cpu, do_hvf_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
+}
+
+static void do_hvf_cpu_synchronize_pre_loadvm(CPUState *cpu,
+ run_on_cpu_data arg)
+{
+ cpu->vcpu_dirty = true;
}
-void hvf_cpu_synchronize_post_init(CPUState *cpu_state)
+void hvf_cpu_synchronize_pre_loadvm(CPUState *cpu)
{
- run_on_cpu(cpu_state, do_hvf_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
+ run_on_cpu(cpu, do_hvf_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL);
}
static bool ept_emulation_fault(hvf_slot *slot, uint64_t gpa, uint64_t ept_qual)
.log_sync = hvf_log_sync,
};
-void hvf_reset_vcpu(CPUState *cpu) {
- uint64_t pdpte[4] = {0, 0, 0, 0};
- int i;
-
- /* TODO: this shouldn't be needed; there is already a call to
- * cpu_synchronize_all_post_reset in vl.c
- */
- wvmcs(cpu->hvf_fd, VMCS_ENTRY_CTLS, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_IA32_EFER, 0);
-
- /* Initialize PDPTE */
- for (i = 0; i < 4; i++) {
- wvmcs(cpu->hvf_fd, VMCS_GUEST_PDPTE0 + i * 2, pdpte[i]);
- }
-
- macvm_set_cr0(cpu->hvf_fd, 0x60000010);
-
- wvmcs(cpu->hvf_fd, VMCS_CR4_MASK, CR4_VMXE_MASK);
- wvmcs(cpu->hvf_fd, VMCS_CR4_SHADOW, 0x0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_CR4, CR4_VMXE_MASK);
-
- /* set VMCS guest state fields */
- wvmcs(cpu->hvf_fd, VMCS_GUEST_CS_SELECTOR, 0xf000);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_CS_LIMIT, 0xffff);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_CS_ACCESS_RIGHTS, 0x9b);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_CS_BASE, 0xffff0000);
-
- wvmcs(cpu->hvf_fd, VMCS_GUEST_DS_SELECTOR, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_DS_LIMIT, 0xffff);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_DS_ACCESS_RIGHTS, 0x93);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_DS_BASE, 0);
-
- wvmcs(cpu->hvf_fd, VMCS_GUEST_ES_SELECTOR, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_ES_LIMIT, 0xffff);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_ES_ACCESS_RIGHTS, 0x93);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_ES_BASE, 0);
-
- wvmcs(cpu->hvf_fd, VMCS_GUEST_FS_SELECTOR, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_FS_LIMIT, 0xffff);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_FS_ACCESS_RIGHTS, 0x93);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_FS_BASE, 0);
-
- wvmcs(cpu->hvf_fd, VMCS_GUEST_GS_SELECTOR, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_GS_LIMIT, 0xffff);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_GS_ACCESS_RIGHTS, 0x93);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_GS_BASE, 0);
-
- wvmcs(cpu->hvf_fd, VMCS_GUEST_SS_SELECTOR, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_SS_LIMIT, 0xffff);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_SS_ACCESS_RIGHTS, 0x93);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_SS_BASE, 0);
-
- wvmcs(cpu->hvf_fd, VMCS_GUEST_LDTR_SELECTOR, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_LDTR_LIMIT, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_LDTR_ACCESS_RIGHTS, 0x10000);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_LDTR_BASE, 0);
-
- wvmcs(cpu->hvf_fd, VMCS_GUEST_TR_SELECTOR, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_TR_LIMIT, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_TR_ACCESS_RIGHTS, 0x83);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_TR_BASE, 0);
-
- wvmcs(cpu->hvf_fd, VMCS_GUEST_GDTR_LIMIT, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_GDTR_BASE, 0);
-
- wvmcs(cpu->hvf_fd, VMCS_GUEST_IDTR_LIMIT, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_IDTR_BASE, 0);
-
- /*wvmcs(cpu->hvf_fd, VMCS_GUEST_CR2, 0x0);*/
- wvmcs(cpu->hvf_fd, VMCS_GUEST_CR3, 0x0);
-
- wreg(cpu->hvf_fd, HV_X86_RIP, 0xfff0);
- wreg(cpu->hvf_fd, HV_X86_RDX, 0x623);
- wreg(cpu->hvf_fd, HV_X86_RFLAGS, 0x2);
- wreg(cpu->hvf_fd, HV_X86_RSP, 0x0);
- wreg(cpu->hvf_fd, HV_X86_RAX, 0x0);
- wreg(cpu->hvf_fd, HV_X86_RBX, 0x0);
- wreg(cpu->hvf_fd, HV_X86_RCX, 0x0);
- wreg(cpu->hvf_fd, HV_X86_RSI, 0x0);
- wreg(cpu->hvf_fd, HV_X86_RDI, 0x0);
- wreg(cpu->hvf_fd, HV_X86_RBP, 0x0);
-
- for (int i = 0; i < 8; i++) {
- wreg(cpu->hvf_fd, HV_X86_R8 + i, 0x0);
- }
-
- hv_vcpu_invalidate_tlb(cpu->hvf_fd);
- hv_vcpu_flush(cpu->hvf_fd);
-}
-
void hvf_vcpu_destroy(CPUState *cpu)
{
X86CPU *x86_cpu = X86_CPU(cpu);
uint64_t pdpte[4] = {0, 0, 0, 0};
uint64_t efer = rvmcs(vcpu, VMCS_GUEST_IA32_EFER);
uint64_t old_cr0 = rvmcs(vcpu, VMCS_GUEST_CR0);
+ uint64_t changed_cr0 = old_cr0 ^ cr0;
uint64_t mask = CR0_PG | CR0_CD | CR0_NW | CR0_NE | CR0_ET;
+ uint64_t entry_ctls;
if ((cr0 & CR0_PG) && (rvmcs(vcpu, VMCS_GUEST_CR4) & CR4_PAE) &&
!(efer & MSR_EFER_LME)) {
wvmcs(vcpu, VMCS_CR0_SHADOW, cr0);
if (efer & MSR_EFER_LME) {
- if (!(old_cr0 & CR0_PG) && (cr0 & CR0_PG)) {
- enter_long_mode(vcpu, cr0, efer);
- }
- if (/*(old_cr0 & CR0_PG) &&*/ !(cr0 & CR0_PG)) {
- exit_long_mode(vcpu, cr0, efer);
+ if (changed_cr0 & CR0_PG) {
+ if (cr0 & CR0_PG) {
+ enter_long_mode(vcpu, cr0, efer);
+ } else {
+ exit_long_mode(vcpu, cr0, efer);
+ }
}
+ } else {
+ entry_ctls = rvmcs(vcpu, VMCS_ENTRY_CTLS);
+ wvmcs(vcpu, VMCS_ENTRY_CTLS, entry_ctls & ~VM_ENTRY_GUEST_LMA);
}
/* Filter new CR0 after we are finished examining it above. */
wvmcs(vcpu, VMCS_GUEST_CR4, guest_cr4);
wvmcs(vcpu, VMCS_CR4_SHADOW, cr4);
+ wvmcs(vcpu, VMCS_CR4_MASK, CR4_VMXE);
hv_vcpu_invalidate_tlb(vcpu);
hv_vcpu_flush(vcpu);
/* BUG, should take considering overlap.. */
wreg(cpu->hvf_fd, HV_X86_RIP, rip);
+ env->eip = rip;
/* after moving forward in rip, we need to clean INTERRUPTABILITY */
val = rvmcs(cpu->hvf_fd, VMCS_GUEST_INTERRUPTIBILITY);
if (host_tsx_blacklisted()) {
ret &= ~(CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_HLE);
}
- } else if (function == 7 && index == 0 && reg == R_ECX) {
- if (enable_cpu_pm) {
- ret |= CPUID_7_0_ECX_WAITPKG;
- } else {
- ret &= ~CPUID_7_0_ECX_WAITPKG;
- }
} else if (function == 7 && index == 0 && reg == R_EDX) {
/*
* Linux v4.17-v4.20 incorrectly return ARCH_CAPABILITIES on SVM hosts.
if (max_nested_state_len > 0) {
assert(max_nested_state_len >= offsetof(struct kvm_nested_state, data));
- if (cpu_has_vmx(env)) {
+ if (cpu_has_vmx(env) || cpu_has_svm(env)) {
struct kvm_vmx_nested_state_hdr *vmx_hdr;
env->nested_state = g_malloc0(max_nested_state_len);
env->nested_state->size = max_nested_state_len;
env->nested_state->format = KVM_STATE_NESTED_FORMAT_VMX;
- vmx_hdr = &env->nested_state->hdr.vmx;
- vmx_hdr->vmxon_pa = -1ull;
- vmx_hdr->vmcs12_pa = -1ull;
+ if (cpu_has_vmx(env)) {
+ vmx_hdr = &env->nested_state->hdr.vmx;
+ vmx_hdr->vmxon_pa = -1ull;
+ vmx_hdr->vmcs12_pa = -1ull;
+ }
}
}
return 0;
}
+ /*
+ * Copy flags that are affected by reset from env->hflags and env->hflags2.
+ */
+ if (env->hflags & HF_GUEST_MASK) {
+ env->nested_state->flags |= KVM_STATE_NESTED_GUEST_MODE;
+ } else {
+ env->nested_state->flags &= ~KVM_STATE_NESTED_GUEST_MODE;
+ }
+ if (env->hflags2 & HF2_GIF_MASK) {
+ env->nested_state->flags |= KVM_STATE_NESTED_GIF_SET;
+ } else {
+ env->nested_state->flags &= ~KVM_STATE_NESTED_GIF_SET;
+ }
+
assert(env->nested_state->size <= max_nested_state_len);
return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_NESTED_STATE, env->nested_state);
}
return ret;
}
+ /*
+ * Copy flags that are affected by reset to env->hflags and env->hflags2.
+ */
if (env->nested_state->flags & KVM_STATE_NESTED_GUEST_MODE) {
env->hflags |= HF_GUEST_MASK;
} else {
env->hflags &= ~HF_GUEST_MASK;
}
+ if (env->nested_state->flags & KVM_STATE_NESTED_GIF_SET) {
+ env->hflags2 |= HF2_GIF_MASK;
+ } else {
+ env->hflags2 &= ~HF2_GIF_MASK;
+ }
return ret;
}
assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
+ /* must be before kvm_put_nested_state so that EFER.SVME is set */
+ ret = kvm_put_sregs(x86_cpu);
+ if (ret < 0) {
+ return ret;
+ }
+
if (level >= KVM_PUT_RESET_STATE) {
ret = kvm_put_nested_state(x86_cpu);
if (ret < 0) {
if (ret < 0) {
return ret;
}
- ret = kvm_put_sregs(x86_cpu);
- if (ret < 0) {
- return ret;
- }
/* must be before kvm_put_msrs */
ret = kvm_inject_mce_oldstyle(x86_cpu);
if (ret < 0) {
{
abort();
}
+
+bool kvm_has_waitpkg(void)
+{
+ return has_msr_umwait;
+}
bool kvm_enable_x2apic(void);
bool kvm_has_x2apic_api(void);
+bool kvm_has_waitpkg(void);
bool kvm_hv_vpindex_settable(void);
}
};
+static bool svm_nested_state_needed(void *opaque)
+{
+ struct kvm_nested_state *nested_state = opaque;
+
+ /*
+ * HF_GUEST_MASK and HF2_GIF_MASK are already serialized
+ * via hflags and hflags2, all that's left is the opaque
+ * nested state blob.
+ */
+ return (nested_state->format == KVM_STATE_NESTED_FORMAT_SVM &&
+ nested_state->size > offsetof(struct kvm_nested_state, data));
+}
+
+static const VMStateDescription vmstate_svm_nested_state = {
+ .name = "cpu/kvm_nested_state/svm",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = svm_nested_state_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_U64(hdr.svm.vmcb_pa, struct kvm_nested_state),
+ VMSTATE_UINT8_ARRAY(data.svm[0].vmcb12,
+ struct kvm_nested_state,
+ KVM_STATE_NESTED_SVM_VMCB_SIZE),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static bool nested_state_needed(void *opaque)
{
X86CPU *cpu = opaque;
CPUX86State *env = &cpu->env;
return (env->nested_state &&
- vmx_nested_state_needed(env->nested_state));
+ (vmx_nested_state_needed(env->nested_state) ||
+ svm_nested_state_needed(env->nested_state)));
}
static int nested_state_post_load(void *opaque, int version_id)
},
.subsections = (const VMStateDescription*[]) {
&vmstate_vmx_nested_state,
+ &vmstate_svm_nested_state,
NULL
}
};
SevCapability *qmp_query_sev_capabilities(Error **errp)
{
- SevCapability *data;
-
- data = sev_get_capabilities();
- if (!data) {
- error_setg(errp, "SEV feature is not available");
- return NULL;
- }
-
- return data;
+ return sev_get_capabilities(errp);
}
void helper_rsqrtps(CPUX86State *env, ZMMReg *d, ZMMReg *s)
{
+ uint8_t old_flags = get_float_exception_flags(&env->sse_status);
d->ZMM_S(0) = float32_div(float32_one,
float32_sqrt(s->ZMM_S(0), &env->sse_status),
&env->sse_status);
d->ZMM_S(3) = float32_div(float32_one,
float32_sqrt(s->ZMM_S(3), &env->sse_status),
&env->sse_status);
+ set_float_exception_flags(old_flags, &env->sse_status);
}
void helper_rsqrtss(CPUX86State *env, ZMMReg *d, ZMMReg *s)
{
+ uint8_t old_flags = get_float_exception_flags(&env->sse_status);
d->ZMM_S(0) = float32_div(float32_one,
float32_sqrt(s->ZMM_S(0), &env->sse_status),
&env->sse_status);
+ set_float_exception_flags(old_flags, &env->sse_status);
}
void helper_rcpps(CPUX86State *env, ZMMReg *d, ZMMReg *s)
{
+ uint8_t old_flags = get_float_exception_flags(&env->sse_status);
d->ZMM_S(0) = float32_div(float32_one, s->ZMM_S(0), &env->sse_status);
d->ZMM_S(1) = float32_div(float32_one, s->ZMM_S(1), &env->sse_status);
d->ZMM_S(2) = float32_div(float32_one, s->ZMM_S(2), &env->sse_status);
d->ZMM_S(3) = float32_div(float32_one, s->ZMM_S(3), &env->sse_status);
+ set_float_exception_flags(old_flags, &env->sse_status);
}
void helper_rcpss(CPUX86State *env, ZMMReg *d, ZMMReg *s)
{
+ uint8_t old_flags = get_float_exception_flags(&env->sse_status);
d->ZMM_S(0) = float32_div(float32_one, s->ZMM_S(0), &env->sse_status);
+ set_float_exception_flags(old_flags, &env->sse_status);
}
static inline uint64_t helper_extrq(uint64_t src, int shift, int len)
void glue(helper_roundps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s,
uint32_t mode)
{
+ uint8_t old_flags = get_float_exception_flags(&env->sse_status);
signed char prev_rounding_mode;
prev_rounding_mode = env->sse_status.float_rounding_mode;
d->ZMM_S(2) = float32_round_to_int(s->ZMM_S(2), &env->sse_status);
d->ZMM_S(3) = float32_round_to_int(s->ZMM_S(3), &env->sse_status);
-#if 0 /* TODO */
- if (mode & (1 << 3)) {
+ if (mode & (1 << 3) && !(old_flags & float_flag_inexact)) {
set_float_exception_flags(get_float_exception_flags(&env->sse_status) &
~float_flag_inexact,
&env->sse_status);
}
-#endif
env->sse_status.float_rounding_mode = prev_rounding_mode;
}
void glue(helper_roundpd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s,
uint32_t mode)
{
+ uint8_t old_flags = get_float_exception_flags(&env->sse_status);
signed char prev_rounding_mode;
prev_rounding_mode = env->sse_status.float_rounding_mode;
d->ZMM_D(0) = float64_round_to_int(s->ZMM_D(0), &env->sse_status);
d->ZMM_D(1) = float64_round_to_int(s->ZMM_D(1), &env->sse_status);
-#if 0 /* TODO */
- if (mode & (1 << 3)) {
+ if (mode & (1 << 3) && !(old_flags & float_flag_inexact)) {
set_float_exception_flags(get_float_exception_flags(&env->sse_status) &
~float_flag_inexact,
&env->sse_status);
}
-#endif
env->sse_status.float_rounding_mode = prev_rounding_mode;
}
void glue(helper_roundss, SUFFIX)(CPUX86State *env, Reg *d, Reg *s,
uint32_t mode)
{
+ uint8_t old_flags = get_float_exception_flags(&env->sse_status);
signed char prev_rounding_mode;
prev_rounding_mode = env->sse_status.float_rounding_mode;
d->ZMM_S(0) = float32_round_to_int(s->ZMM_S(0), &env->sse_status);
-#if 0 /* TODO */
- if (mode & (1 << 3)) {
+ if (mode & (1 << 3) && !(old_flags & float_flag_inexact)) {
set_float_exception_flags(get_float_exception_flags(&env->sse_status) &
~float_flag_inexact,
&env->sse_status);
}
-#endif
env->sse_status.float_rounding_mode = prev_rounding_mode;
}
void glue(helper_roundsd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s,
uint32_t mode)
{
+ uint8_t old_flags = get_float_exception_flags(&env->sse_status);
signed char prev_rounding_mode;
prev_rounding_mode = env->sse_status.float_rounding_mode;
d->ZMM_D(0) = float64_round_to_int(s->ZMM_D(0), &env->sse_status);
-#if 0 /* TODO */
- if (mode & (1 << 3)) {
+ if (mode & (1 << 3) && !(old_flags & float_flag_inexact)) {
set_float_exception_flags(get_float_exception_flags(&env->sse_status) &
~float_flag_inexact,
&env->sse_status);
}
-#endif
env->sse_status.float_rounding_mode = prev_rounding_mode;
}
return NULL;
}
-SevCapability *sev_get_capabilities(void)
+SevCapability *sev_get_capabilities(Error **errp)
{
+ error_setg(errp, "SEV is not available in this QEMU");
return NULL;
}
static int
sev_get_pdh_info(int fd, guchar **pdh, size_t *pdh_len, guchar **cert_chain,
- size_t *cert_chain_len)
+ size_t *cert_chain_len, Error **errp)
{
guchar *pdh_data = NULL;
guchar *cert_chain_data = NULL;
r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err);
if (r < 0) {
if (err != SEV_RET_INVALID_LEN) {
- error_report("failed to export PDH cert ret=%d fw_err=%d (%s)",
- r, err, fw_error_to_str(err));
+ error_setg(errp, "failed to export PDH cert ret=%d fw_err=%d (%s)",
+ r, err, fw_error_to_str(err));
return 1;
}
}
r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err);
if (r < 0) {
- error_report("failed to export PDH cert ret=%d fw_err=%d (%s)",
- r, err, fw_error_to_str(err));
+ error_setg(errp, "failed to export PDH cert ret=%d fw_err=%d (%s)",
+ r, err, fw_error_to_str(err));
goto e_free;
}
}
SevCapability *
-sev_get_capabilities(void)
+sev_get_capabilities(Error **errp)
{
SevCapability *cap = NULL;
guchar *pdh_data = NULL;
uint32_t ebx;
int fd;
+ if (!kvm_enabled()) {
+ error_setg(errp, "KVM not enabled");
+ return NULL;
+ }
+ if (kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_OP, NULL) < 0) {
+ error_setg(errp, "SEV is not enabled in KVM");
+ return NULL;
+ }
+
fd = open(DEFAULT_SEV_DEVICE, O_RDWR);
if (fd < 0) {
- error_report("%s: Failed to open %s '%s'", __func__,
- DEFAULT_SEV_DEVICE, strerror(errno));
+ error_setg_errno(errp, errno, "Failed to open %s",
+ DEFAULT_SEV_DEVICE);
return NULL;
}
if (sev_get_pdh_info(fd, &pdh_data, &pdh_len,
- &cert_chain_data, &cert_chain_len)) {
+ &cert_chain_data, &cert_chain_len, errp)) {
goto out;
}
extern uint32_t sev_get_cbit_position(void);
extern uint32_t sev_get_reduced_phys_bits(void);
extern char *sev_get_launch_measurement(void);
-extern SevCapability *sev_get_capabilities(void);
+extern SevCapability *sev_get_capabilities(Error **errp);
#endif
#define SVM_NPT_PAE (1 << 0)
#define SVM_NPT_LMA (1 << 1)
#define SVM_NPT_NXE (1 << 2)
+#define SVM_NPT_PSE (1 << 3)
#define SVM_NPTEXIT_P (1ULL << 0)
#define SVM_NPTEXIT_RW (1ULL << 1)
nested_ctl = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb,
control.nested_ctl));
+
+ env->nested_pg_mode = 0;
+
if (nested_ctl & SVM_NPT_ENABLED) {
env->nested_cr3 = x86_ldq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb,
control.nested_cr3));
env->hflags2 |= HF2_NPT_MASK;
- env->nested_pg_mode = 0;
if (env->cr[4] & CR4_PAE_MASK) {
env->nested_pg_mode |= SVM_NPT_PAE;
}
+ if (env->cr[4] & CR4_PSE_MASK) {
+ env->nested_pg_mode |= SVM_NPT_PSE;
+ }
if (env->hflags & HF_LMA_MASK) {
env->nested_pg_mode |= SVM_NPT_LMA;
}
--- /dev/null
+/*
+ * x86 FPU, MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4/PNI helpers
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+
+void update_mxcsr_from_sse_status(CPUX86State *env)
+{
+}
static inline void gen_ins(DisasContext *s, MemOp ot)
{
- if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
- gen_io_start();
- }
gen_string_movl_A0_EDI(s);
/* Note: we must do this dummy write first to be restartable in
case of page fault. */
gen_op_movl_T0_Dshift(s, ot);
gen_op_add_reg_T0(s, s->aflag, R_EDI);
gen_bpt_io(s, s->tmp2_i32, ot);
- if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
- gen_io_end();
- }
}
static inline void gen_outs(DisasContext *s, MemOp ot)
{
- if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
- gen_io_start();
- }
gen_string_movl_A0_ESI(s);
gen_op_ld_v(s, ot, s->T0, s->A0);
gen_op_movl_T0_Dshift(s, ot);
gen_op_add_reg_T0(s, s->aflag, R_ESI);
gen_bpt_io(s, s->tmp2_i32, ot);
- if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
- gen_io_end();
- }
}
/* same method as Valgrind : we generate jumps to current or next
tcg_gen_ext16u_tl(s->T0, cpu_regs[R_EDX]);
gen_check_io(s, ot, pc_start - s->cs_base,
SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes) | 4);
+ if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
+ gen_io_start();
+ }
if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
gen_repz_ins(s, ot, pc_start - s->cs_base, s->pc - s->cs_base);
+ /* jump generated by gen_repz_ins */
} else {
gen_ins(s, ot);
if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
tcg_gen_ext16u_tl(s->T0, cpu_regs[R_EDX]);
gen_check_io(s, ot, pc_start - s->cs_base,
svm_is_rep(prefixes) | 4);
+ if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
+ gen_io_start();
+ }
if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
gen_repz_outs(s, ot, pc_start - s->cs_base, s->pc - s->cs_base);
+ /* jump generated by gen_repz_outs */
} else {
gen_outs(s, ot);
if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
CASE_MODRM_OP(4): /* smsw */
gen_svm_check_intercept(s, pc_start, SVM_EXIT_READ_CR0);
tcg_gen_ld_tl(s->T0, cpu_env, offsetof(CPUX86State, cr[0]));
- if (CODE64(s)) {
- mod = (modrm >> 6) & 3;
- ot = (mod != 3 ? MO_16 : s->dflag);
- } else {
- ot = MO_16;
- }
+ /*
+ * In 32-bit mode, the higher 16 bits of the destination
+ * register are undefined. In practice CR0[31:0] is stored
+ * just like in 64-bit mode.
+ */
+ mod = (modrm >> 6) & 3;
+ ot = (mod != 3 ? MO_16 : s->dflag);
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1);
break;
case 0xee: /* rdpkru */
gen_helper_read_crN(s->T0, cpu_env, tcg_const_i32(reg));
gen_op_mov_reg_v(s, ot, rm, s->T0);
if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
- gen_io_end();
+ gen_jmp(s, s->pc - s->cs_base);
}
}
break;
gen_exception(s, EXCP07_PREX, pc_start - s->cs_base);
break;
}
+ gen_helper_update_mxcsr(cpu_env);
gen_lea_modrm(env, s, modrm);
tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State, mxcsr));
gen_op_st_v(s, MO_32, s->T0, s->A0);
target_ulong helper_mftc0_cause(CPUMIPSState *env)
{
int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
- int32_t tccause;
CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc);
- if (other_tc == other->current_tc) {
- tccause = other->CP0_Cause;
- } else {
- tccause = other->CP0_Cause;
- }
-
- return tccause;
+ return other->CP0_Cause;
}
target_ulong helper_mftc0_status(CPUMIPSState *env)
{
uint32_t wt2;
- wt2 = float32_sub(fst0, fst1, &env->active_fpu.fp_status);
+ wt2 = float32_add(fst0, fst1, &env->active_fpu.fp_status);
update_fcr31(env, GETPC());
return wt2;
}
#include "exec/cpu_ldst.h"
#include "exec/translator.h"
#include "qemu/qemu-print.h"
+#include "exec/gen-icount.h"
/* is_jmp field values */
#define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */
tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc);
gen_helper_raise_exception(dc->cpu_env, tmp);
tcg_temp_free_i32(tmp);
- dc->is_jmp = DISAS_UPDATE;
+ dc->is_jmp = DISAS_NORETURN;
}
static bool use_goto_tb(DisasContext *dc, uint32_t dest)
/* If interrupts were enabled using WRCTL, trigger them. */
#if !defined(CONFIG_USER_ONLY)
if ((instr.imm5 + CR_BASE) == CR_STATUS) {
+ if (tb_cflags(dc->tb) & CF_USE_ICOUNT) {
+ gen_io_start();
+ }
gen_helper_check_interrupts(dc->cpu_env);
+ dc->is_jmp = DISAS_UPDATE;
}
#endif
}
tcg_gen_movi_tl(cpu_R[R_PC], dc->pc);
gen_helper_raise_exception(cpu_env, tmp);
tcg_temp_free_i32(tmp);
- dc->is_jmp = DISAS_UPDATE;
+ dc->is_jmp = DISAS_NORETURN;
}
/* generate intermediate code for basic block 'tb'. */
/* Indicate where the next block should start */
switch (dc->is_jmp) {
case DISAS_NEXT:
+ case DISAS_UPDATE:
/* Save the current PC back into the CPU register */
tcg_gen_movi_tl(cpu_R[R_PC], dc->pc);
tcg_gen_exit_tb(NULL, 0);
default:
case DISAS_JUMP:
- case DISAS_UPDATE:
/* The jump will already have updated the PC register */
tcg_gen_exit_tb(NULL, 0);
break;
+ case DISAS_NORETURN:
case DISAS_TB_JUMP:
/* nothing more to generate */
break;
FIELD(VTYPE, VSEW, 2, 3)
FIELD(VTYPE, VEDIV, 5, 2)
FIELD(VTYPE, RESERVED, 7, sizeof(target_ulong) * 8 - 9)
-FIELD(VTYPE, VILL, sizeof(target_ulong) * 8 - 2, 1)
+FIELD(VTYPE, VILL, sizeof(target_ulong) * 8 - 1, 1)
struct CPURISCVState {
target_ulong gpr[32];
static void gen_vec_rsub16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b)
{
- tcg_gen_vec_sub8_i64(d, b, a);
+ tcg_gen_vec_sub16_i64(d, b, a);
}
static void gen_rsub_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
static void tcg_gen_gvec_rsubs(unsigned vece, uint32_t dofs, uint32_t aofs,
TCGv_i64 c, uint32_t oprsz, uint32_t maxsz)
{
+ static const TCGOpcode vecop_list[] = { INDEX_op_sub_vec, 0 };
static const GVecGen2s rsub_op[4] = {
{ .fni8 = gen_vec_rsub8_i64,
.fniv = gen_rsub_vec,
.fno = gen_helper_vec_rsubs8,
+ .opt_opc = vecop_list,
.vece = MO_8 },
{ .fni8 = gen_vec_rsub16_i64,
.fniv = gen_rsub_vec,
.fno = gen_helper_vec_rsubs16,
+ .opt_opc = vecop_list,
.vece = MO_16 },
{ .fni4 = gen_rsub_i32,
.fniv = gen_rsub_vec,
.fno = gen_helper_vec_rsubs32,
+ .opt_opc = vecop_list,
.vece = MO_32 },
{ .fni8 = gen_rsub_i64,
.fniv = gen_rsub_vec,
.fno = gen_helper_vec_rsubs64,
+ .opt_opc = vecop_list,
.prefer_i64 = TCG_TARGET_REG_BITS == 64,
.vece = MO_64 },
};
if (opivx_widen_check(s, a)) {
return opivx_trans(a->rd, a->rs1, a->rs2, a->vm, fn, s);
}
- return true;
+ return false;
}
#define GEN_OPIVX_WIDEN_TRANS(NAME) \
case PMP_AMATCH_NA4:
sa = this_addr << 2; /* shift up from [xx:0] to [xx+2:2] */
- ea = (this_addr + 4u) - 1u;
+ ea = (sa + 4u) - 1u;
break;
case PMP_AMATCH_NAPOT:
break;
case R_RISCV_JAL:
return reloc_jimm20(code_ptr, (tcg_insn_unit *)value);
- break;
case R_RISCV_CALL:
return reloc_call(code_ptr, (tcg_insn_unit *)value);
- break;
default:
tcg_abort();
}
TCGv_vec b, TCGOpcode opc, TCGCond cond)
{
if (!do_op3(vece, r, a, b, opc)) {
+ const TCGOpcode *hold_list = tcg_swap_vecop_list(NULL);
tcg_gen_cmpsel_vec(cond, vece, r, a, b, a, b);
+ tcg_swap_vecop_list(hold_list);
}
}
@echo " $(MAKE) check-venv Creates a Python venv for tests"
@echo " $(MAKE) check-clean Clean the tests and related data"
@echo
+ @echo "The following are useful for CI builds"
+ @echo " $(MAKE) check-build Build most test binaris"
@echo " $(MAKE) get-vm-images Downloads all images used by acceptance tests, according to configured targets (~350 MB each, 1.5 GB max)"
@echo
@echo
{ export MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$(( $${RANDOM:-0} % 255 + 1))} $2; \
$(foreach COMMAND, $1, \
$(COMMAND) -m=$(SPEED) -k --tap < /dev/null \
- | sed "s/^[a-z][a-z]* [0-9]* /&$(notdir $(COMMAND)) /" || true; ) } \
+ | sed "s/^\(not \)\?ok [0-9]* /&$(notdir $(COMMAND)) /" || true; ) } \
| ./scripts/tap-merge.pl | tee "$@" \
| ./scripts/tap-driver.pl $(if $(V),, --show-failures-only), \
"TAP","$@")
QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \
QTEST_QEMU_IMG=qemu-img$(EXESUF))
+build-qtest: $(patsubst %, %-softmmu/all, $(QTEST_TARGETS)) $(check-qtest-y)
+
+build-unit: $(check-unit-y)
+
check-unit: $(check-unit-y)
$(call do_test_human, $^)
FP_TEST_BIN=$(BUILD_DIR)/tests/fp/fp-test
# the build dir is created by configure
-.PHONY: $(FP_TEST_BIN)
$(FP_TEST_BIN): config-host.h $(test-util-obj-y)
$(call quiet-command, \
$(MAKE) $(SUBDIR_MAKEFLAGS) -C $(dir $@) V="$(V)" $(notdir $@), \
.PHONY: check-softfloat
ifeq ($(CONFIG_TCG),y)
-check-softfloat: check-softfloat-conv check-softfloat-compare check-softfloat-ops
+build-softfloat: $(FP_TEST_BIN)
+check-softfloat: build-softfloat check-softfloat-conv check-softfloat-compare check-softfloat-ops
else
-check-softfloat:
+build-softfloat check-softfloat:
$(call quiet-command, /bin/true, "FLOAT TEST", \
"SKIPPED for non-TCG builds")
endif
--show=$(AVOCADO_SHOW) run --job-results-dir=$(TESTS_RESULTS_DIR) \
--filter-by-tags-include-empty --filter-by-tags-include-empty-key \
$(AVOCADO_TAGS) \
- --failfast=on tests/acceptance, \
+ $(if $(GITLAB_CI),,--failfast=on) tests/acceptance, \
"AVOCADO", "tests/acceptance")
# Consolidated targets
ifeq ($(CONFIG_TOOLS),y)
check-block: $(patsubst %,check-%, $(check-block-y))
endif
-check: check-block check-qapi-schema check-unit check-softfloat check-qtest check-decodetree
+check-build: build-unit build-softfloat build-qtest
+
check-clean:
rm -rf $(check-unit-y) tests/*.o tests/*/*.o $(QEMU_IOTESTS_HELPERS-y)
rm -rf $(sort $(foreach target,$(SYSEMU_TARGET_LIST), $(check-qtest-$(target)-y:%=tests/qtest/%$(EXESUF))) $(check-qtest-generic-y:%=tests/qtest/%$(EXESUF)))
rm -f tests/qtest/dbus-vmstate1-gen-timestamp
rm -rf $(TESTS_VENV_DIR) $(TESTS_RESULTS_DIR)
+check: check-block check-qapi-schema check-unit check-softfloat check-qtest check-decodetree
+
clean: check-clean
# Build the help program automatically
from avocado.utils import vmimage
from avocado.utils import datadrainer
from avocado.utils.path import find_command
+from avocado import skipIf
ACCEL_NOT_AVAILABLE_FMT = "%s accelerator does not seem to be available"
KVM_NOT_AVAILABLE = ACCEL_NOT_AVAILABLE_FMT % "KVM"
chksum = '4caaab5a434fd4d1079149a072fdc7891e354f834d355069ca982fdcaf5a122d'
+ @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab')
def test_s390_ccw_virtio_tcg(self):
"""
:avocado: tags=machine:s390-ccw-virtio
except CmdNotFoundError:
P7ZIP_AVAILABLE = False
+"""
+Round up to next power of 2
+"""
+def pow2ceil(x):
+ return 1 if x == 0 else 2**(x - 1).bit_length()
+
+"""
+Expand file size to next power of 2
+"""
+def image_pow2ceil_expand(path):
+ size = os.path.getsize(path)
+ size_aligned = pow2ceil(size)
+ if size != size_aligned:
+ with open(path, 'ab+') as fd:
+ fd.truncate(size_aligned)
+
class LinuxKernelTest(Test):
KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
'Debian')
exec_command_and_wait_for_pattern(self, 'reboot',
'reboot: Restarting system')
+ # Wait for VM to shut down gracefully
+ self.vm.wait()
@skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code')
def test_mips64el_malta_5KEc_cpio(self):
'3.19.3.mtoman.20150408')
exec_command_and_wait_for_pattern(self, 'reboot',
'reboot: Restarting system')
+ # Wait for VM to shut down gracefully
+ self.vm.wait()
def do_test_mips_malta32el_nanomips(self, kernel_url, kernel_hash):
kernel_path_xz = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
'Allwinner sun4i/sun5i')
exec_command_and_wait_for_pattern(self, 'cat /proc/iomem',
'system-control@1c00000')
- exec_command_and_wait_for_pattern(self, 'reboot',
- 'reboot: Restarting system')
+ # cubieboard's reboot is not functioning; omit reboot test.
def test_arm_cubieboard_sata(self):
"""
'Allwinner sun4i/sun5i')
exec_command_and_wait_for_pattern(self, 'cat /proc/partitions',
'sda')
- exec_command_and_wait_for_pattern(self, 'reboot',
- 'reboot: Restarting system')
+ # cubieboard's reboot is not functioning; omit reboot test.
def test_arm_orangepi(self):
"""
'system-control@1c00000')
exec_command_and_wait_for_pattern(self, 'reboot',
'reboot: Restarting system')
+ # Wait for VM to shut down gracefully
+ self.vm.wait()
def test_arm_orangepi_sd(self):
"""
:avocado: tags=arch:arm
:avocado: tags=machine:orangepi-pc
+ :avocado: tags=device:sd
"""
deb_url = ('https://apt.armbian.com/pool/main/l/'
'linux-4.20.7-sunxi/linux-image-dev-sunxi_5.75_armhf.deb')
rootfs_path_xz = self.fetch_asset(rootfs_url, asset_hash=rootfs_hash)
rootfs_path = os.path.join(self.workdir, 'rootfs.cpio')
archive.lzma_uncompress(rootfs_path_xz, rootfs_path)
+ image_pow2ceil_expand(rootfs_path)
self.vm.set_console()
kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
'3 packets transmitted, 3 packets received, 0% packet loss')
exec_command_and_wait_for_pattern(self, 'reboot',
'reboot: Restarting system')
+ # Wait for VM to shut down gracefully
+ self.vm.wait()
@skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited')
@skipUnless(P7ZIP_AVAILABLE, '7z not installed')
"""
:avocado: tags=arch:arm
:avocado: tags=machine:orangepi-pc
+ :avocado: tags=device:sd
"""
- # This test download a 196MB compressed image and expand it to 932MB...
+ # This test download a 196MB compressed image and expand it to 1GB
image_url = ('https://dl.armbian.com/orangepipc/archive/'
'Armbian_19.11.3_Orangepipc_bionic_current_5.3.9.7z')
image_hash = '196a8ffb72b0123d92cea4a070894813d305c71e'
image_name = 'Armbian_19.11.3_Orangepipc_bionic_current_5.3.9.img'
image_path = os.path.join(self.workdir, image_name)
process.run("7z e -o%s %s" % (self.workdir, image_path_7z))
+ image_pow2ceil_expand(image_path)
self.vm.set_console()
self.vm.add_args('-drive', 'file=' + image_path + ',if=sd,format=raw',
"""
:avocado: tags=arch:arm
:avocado: tags=machine:orangepi-pc
+ :avocado: tags=device:sd
"""
- # This test download a 304MB compressed image and expand it to 1.3GB...
+ # This test download a 304MB compressed image and expand it to 2GB
deb_url = ('http://snapshot.debian.org/archive/debian/'
'20200108T145233Z/pool/main/u/u-boot/'
'u-boot-sunxi_2020.01%2Bdfsg-1_armhf.deb')
image_hash = '2babb29d36d8360adcb39c09e31060945259917a'
image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash)
image_path = os.path.join(self.workdir, 'armv7.img')
- image_drive_args = 'if=sd,format=raw,snapshot=on,file=' + image_path
archive.gzip_uncompress(image_path_gz, image_path)
+ image_pow2ceil_expand(image_path)
+ image_drive_args = 'if=sd,format=raw,snapshot=on,file=' + image_path
# dd if=u-boot-sunxi-with-spl.bin of=armv7.img bs=1K seek=8 conv=notrunc
with open(uboot_path, 'rb') as f_in:
f_out.seek(8 * 1024)
shutil.copyfileobj(f_in, f_out)
- # Extend image, to avoid that NetBSD thinks the partition
- # inside the image is larger than device size itself
- f_out.seek(0, 2)
- f_out.seek(64 * 1024 * 1024, 1)
- f_out.write(bytearray([0x00]))
-
self.vm.set_console()
self.vm.add_args('-nic', 'user',
'-drive', image_drive_args,
# This work is licensed under the terms of the GNU GPL, version 2 or
# later. See the COPYING file in the top-level directory.
+import os
import logging
import tempfile
from avocado_qemu import Test
+from avocado import skipIf
class LinuxInitrd(Test):
max_size + 1)
self.assertRegex(self.vm.get_log(), expected_msg)
+ @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab')
def test_with_2gib_file_should_work_with_linux_v4_16(self):
"""
QEMU has supported up to 4 GiB initrd for recent kernel
self.run_common_commands(wordsize)
self.shutdown_via_ssh()
+ # Wait for VM to shut down gracefully
+ self.vm.wait()
def test_mips_malta32eb_kernel3_2_0(self):
"""
--- /dev/null
+#
+# QEMU AVR acceptance tests
+#
+# Copyright (c) 2019-2020 Michael Rolnik <mrolnik@gmail.com>
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+
+import time
+
+from avocado_qemu import Test
+
+class AVR6Machine(Test):
+ timeout = 5
+
+ def test_freertos(self):
+ """
+ :avocado: tags=arch:avr
+ :avocado: tags=machine:arduino-mega-2560-v3
+ """
+ """
+ https://github.com/seharris/qemu-avr-tests/raw/master/free-rtos/Demo/AVR_ATMega2560_GCC/demo.elf
+ constantly prints out 'ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWX'
+ """
+ rom_url = ('https://github.com/seharris/qemu-avr-tests'
+ '/raw/36c3e67b8755dcf/free-rtos/Demo'
+ '/AVR_ATMega2560_GCC/demo.elf')
+ rom_hash = '7eb521f511ca8f2622e0a3c5e8dd686efbb911d4'
+ rom_path = self.fetch_asset(rom_url, asset_hash=rom_hash)
+
+ self.vm.add_args('-bios', rom_path)
+ self.vm.add_args('-nographic')
+ self.vm.launch()
+
+ time.sleep(2)
+ self.vm.shutdown()
+
+ self.assertIn('ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWX',
+ self.vm.get_log())
from avocado_qemu import Test
from avocado_qemu import wait_for_console_pattern
from avocado.utils import archive
+from avocado import skipIf
NUMPY_AVAILABLE = True
"""
self.do_test_i6400_framebuffer_logo(1)
+ @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab')
def test_mips_malta_i6400_framebuffer_logo_7cores(self):
"""
:avocado: tags=arch:mips64el
"""
self.do_test_i6400_framebuffer_logo(7)
+ @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab')
def test_mips_malta_i6400_framebuffer_logo_8cores(self):
"""
:avocado: tags=arch:mips64el
:avocado: tags=machine:gdbsim-r5f562n7
:avocado: tags=endian:little
"""
- dtb_url = ('https://acc.dl.osdn.jp/users/23/23887/rx-qemu.dtb')
+ dtb_url = ('https://acc.dl.osdn.jp/users/23/23887/rx-virt.dtb')
dtb_hash = '7b4e4e2c71905da44e86ce47adee2210b026ac18'
dtb_path = self.fetch_asset(dtb_url, asset_hash=dtb_hash)
kernel_url = ('http://acc.dl.osdn.jp/users/23/23845/zImage')
logger = logging.getLogger('replay')
logger.info('replay overhead {:.2%}'.format(t2 / t1 - 1))
- @skipIf(os.getenv('CONTINUOUS_INTEGRATION'), 'Running on Travis-CI')
+ @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab')
def test_x86_64_pc(self):
"""
:avocado: tags=arch:x86_64
* here's one we prepared earlier :-)
*/
gnutls_x509_privkey_t privkey;
-# define PRIVATE_KEY \
- "-----BEGIN PRIVATE KEY-----\n" \
- "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBALVcr\n" \
- "BL40Tm6yq88FBhJNw1aaoCjmtg0l4dWQZ/e9Fimx4ARxFpT+ji4FE\n" \
- "Cgl9s/SGqC+1nvlkm9ViSo0j7MKDbnDB+VRHDvMAzQhA2X7e8M0n9\n" \
- "rPolUY2lIVC83q0BBaOBkCj2RSmT2xTEbbC2xLukSrg2WP/ihVOxc\n" \
- "kXRuyFtzAgMBAAECgYB7slBexDwXrtItAMIH6m/U+LUpNe0Xx48OL\n" \
- "IOn4a4whNgO/o84uIwygUK27ZGFZT0kAGAk8CdF9hA6ArcbQ62s1H\n" \
- "myxrUbF9/mrLsQw1NEqpuUk9Ay2Tx5U/wPx35S3W/X2AvR/ZpTnCn\n" \
- "2q/7ym9fyiSoj86drD7BTvmKXlOnOwQJBAPOFMp4mMa9NGpGuEssO\n" \
- "m3Uwbp6lhcP0cA9MK+iOmeANpoKWfBdk5O34VbmeXnGYWEkrnX+9J\n" \
- "bM4wVhnnBWtgBMCQQC+qAEmvwcfhauERKYznMVUVksyeuhxhCe7EK\n" \
- "mPh+U2+g0WwdKvGDgO0PPt1gq0ILEjspMDeMHVdTwkaVBo/uMhAkA\n" \
- "Z5SsZyCP2aTOPFDypXRdI4eqRcjaEPOUBq27r3uYb/jeboVb2weLa\n" \
- "L1MmVuHiIHoa5clswPdWVI2y0em2IGoDAkBPSp/v9VKJEZabk9Frd\n" \
- "a+7u4fanrM9QrEjY3KhduslSilXZZSxrWjjAJPyPiqFb3M8XXA26W\n" \
- "nz1KYGnqYKhLcBAkB7dt57n9xfrhDpuyVEv+Uv1D3VVAhZlsaZ5Pp\n" \
- "dcrhrkJn2sa/+O8OKvdrPSeeu/N5WwYhJf61+CPoenMp7IFci\n" \
- "-----END PRIVATE KEY-----\n"
+# define PRIVATE_KEY \
+ "-----BEGIN RSA PRIVATE KEY-----\n" \
+ "MIIG5AIBAAKCAYEAyjWyLSNm5PZvYUKUcDWGqbLX10b2ood+YaFjWSnJrqx/q3qh\n" \
+ "rVGBJglD25AJENJsmZF3zPP1oMhfIxsXu63Hdkb6Rdlc2RUoUP34x9VC1izH25mR\n" \
+ "6c8DPDp1d6IraZ/llDMI1HsBFz0qGWtvOHgm815XG4PAr/N8rDsuqfv/cJ01KlnO\n" \
+ "0OdO5QRXCJf9g/dYd41MPu7wOXk9FqjQlmRoP59HgtJ+zUpE4z+Keruw9cMT9VJj\n" \
+ "0oT+pQ9ysenqeZ3gbT224T1khrEhT5kifhtFLNyDssRchUUWH0hiqoOO1vgb+850\n" \
+ "W6/1VdxvuPam48py4diSPi1Vip8NITCOBaX9FIpVp4Ruw4rTPVMNMjq9Cpx/DwMP\n" \
+ "9MbfXfnaVaZaMrmq67/zPhl0eVbUrecH2hQ3ZB9oIF4GkNskzlWF5+yPy6zqk304\n" \
+ "AKaiFR6jRyh3YfHo2XFqV8x/hxdsIEXOtEUGhSIcpynsW+ckUCartzu7xbhXjd4b\n" \
+ "kxJT89+riPFYij09AgMBAAECggGBAKyFkaZXXROeejrmHlV6JZGlp+fhgM38gkRz\n" \
+ "+Jp7P7rLLAY3E7gXIPQ91WqAAmwazFNdvHPd9USfkCQYmnAi/VoZhrCPmlsQZRxt\n" \
+ "A5QjjOnEvSPMa6SrXZxGWDCg6R8uMCb4P+FhrPWR1thnRDZOtRTQ+crc50p3mHgt\n" \
+ "6ktXWIJRbqnag8zSfQqCYGtRmhe8sfsWT+Yl4El4+jjaAVU/B364u7+PLmaiphGp\n" \
+ "BdJfTsTwEpgtGkPj+osDmhzXcZkfq3V+fz5JLkemsCiQKmn4VJRpg8c3ZmE8NPNt\n" \
+ "gRtGWZ4W3WKDvhotT65WpQx4+6R8Duux/blNPBmH1Upmwd7kj7GYFBArbCjgd9PT\n" \
+ "xgfCSUZpgOZHHkcgSB+022a8XncXna7WYYij28SLtwImFyu0nNtqECFQHH5u+k6C\n" \
+ "LRYBSN+3t3At8dQuk01NVrJBndmjmXRfxpqUtTdeaNgVpdUYRY98s30G68NYGSra\n" \
+ "aEvhhRSghkcLNetkobpY9pUgeqW/tQKBwQDZHHK9nDMt/zk1TxtILeUSitPXcv1/\n" \
+ "8ufXqO0miHdH23XuXhIEA6Ef26RRVGDGgpjkveDJK/1w5feJ4H/ni4Vclil/cm38\n" \
+ "OwRqjjd7ElHJX6JQbsxEx/gNTk5/QW1iAL9TXUalgepsSXYT6AJ0/CJv0jmJSJ36\n" \
+ "YoKMOM8uqzb2KhN6i+RlJRi5iY53kUhWTJq5ArWvNhUzQNSYODI4bNxlsKSBL2Ik\n" \
+ "LZ5QKHuaEjQet0IlPlfIb4PzMm8CHa/urOcCgcEA7m3zW/lL5bIFoKPjWig5Lbn1\n" \
+ "aHfrG2ngqzWtgWtfZqMH8OkZc1Mdhhmvd46titjiLjeI+UP/uHXR0068PnrNngzl\n" \
+ "tTgwlakzu+bWzqhBm1F+3/341st/FEk07r0P/3/PhezVjwfO8c8Exj7pLxH4wrH0\n" \
+ "ROHgDbClmlJRu6OO78wk1+Vapf5DWa8YfA+q+fdvr7KvgGyytheKMT/b/dsqOq7y\n" \
+ "qZPjmaJKWAvV3RWG8lWHFSdHx2IAHMHfGr17Y/w7AoHBALzwZeYebeekiVucGSjq\n" \
+ "T8SgLhT7zCIx+JMUPjVfYzaUhP/Iu7Lkma6IzWm9nW6Drpy5pUpMzwUWDCLfzU9q\n" \
+ "eseFIl337kEn9wLn+t5OpgAyCqYmlftxbqvdrrBN9uvnrJjWvqk/8wsDrw9JxAGc\n" \
+ "fjeD4nBXUqvYWLXApoR9mZoGKedmoH9pFig4zlO9ig8YITnKYuQ0k6SD0b8agJHc\n" \
+ "Ir0YSUDnRGgpjvFBGbeOCe+FGbohk/EpItJc3IAh5740lwKBwAdXd2DjokSmYKn7\n" \
+ "oeqKxofz6+yVlLW5YuOiuX78sWlVp87xPolgi84vSEnkKM/Xsc8+goc6YstpRVa+\n" \
+ "W+mImoA9YW1dF5HkLeWhTAf9AlgoAEIhbeIfTgBv6KNZSv7RDrDPBBxtXx/vAfSg\n" \
+ "x0ldwk0scZsVYXLKd67yzfV7KdGUdaX4N/xYgfZm/9gCG3+q8NN2KxVHQ5F71BOE\n" \
+ "JeABOaGo9WvnU+DNMIDZjHJMUWVw4MHz/a/UArDf/2CxaPVBNQKBwASg6j4ohSTk\n" \
+ "J7aE6RQ3OBmmDDpixcoCJt9u9SjHVYMlbs5CEJGVSczk0SG3y8P1lOWNDSRnMksZ\n" \
+ "xWnHdP/ogcuYMuvK7UACNAF0zNddtzOhzcpNmejFj+WCHYY/UmPr2/Kf6t7Cxk2K\n" \
+ "3cZ4tqWsiTmBT8Bknmah7L5DrhS+ZBJliDeFAA8fZHdMH0Xjr4UBp9kF90EMTdW1\n" \
+ "Xr5uz7ZrMsYpYQI7mmyqV9SSjUg4iBXwVSoag1iDJ1K8Qg/L7Semgg==\n" \
+ "-----END RSA PRIVATE KEY-----\n"
/*
* This loads the private key we defined earlier
DOCKER_TARGETS := $(patsubst %,docker-image-%,$(DOCKER_IMAGES))
# Use a global constant ccache directory to speed up repetitive builds
DOCKER_CCACHE_DIR := $$HOME/.cache/qemu-docker-ccache
+DOCKER_REGISTRY := $(if $(REGISTRY),$(REGISTRY),registry.gitlab.com/qemu-project/qemu)
DOCKER_TESTS := $(notdir $(shell \
find $(SRC_PATH)/tests/docker/ -name 'test-*' -type f))
ifdef SKIP_DOCKER_BUILD
docker-image-%: $(DOCKER_FILES_DIR)/%.docker
$(call quiet-command, \
- $(DOCKER_SCRIPT) check --quiet qemu:$* $<, \
+ $(DOCKER_SCRIPT) check --quiet qemu/$* $<, \
"CHECK", "$*")
else
docker-image-%: $(DOCKER_FILES_DIR)/%.docker
$(call quiet-command,\
- $(DOCKER_SCRIPT) build -t qemu:$* -f $< \
- $(if $V,,--quiet) $(if $(NOCACHE),--no-cache) \
+ $(DOCKER_SCRIPT) build -t qemu/$* -f $< \
+ $(if $V,,--quiet) \
+ $(if $(NOCACHE),--no-cache, \
+ $(if $(DOCKER_REGISTRY),--registry $(DOCKER_REGISTRY))) \
$(if $(NOUSER),,--add-current-user) \
$(if $(EXTRA_FILES),--extra-files $(EXTRA_FILES))\
$(if $(EXECUTABLE),--include-executable=$(EXECUTABLE)),\
DEB_ARCH=$(DEB_ARCH) \
DEB_TYPE=$(DEB_TYPE) \
$(if $(DEB_URL),DEB_URL=$(DEB_URL),) \
- $(DOCKER_SCRIPT) build qemu:debian-$* $< \
+ $(DOCKER_SCRIPT) build qemu/debian-$* $< \
$(if $V,,--quiet) $(if $(NOCACHE),--no-cache) \
$(if $(NOUSER),,--add-current-user) \
$(if $(EXTRA_FILES),--extra-files $(EXTRA_FILES)) \
$(if $(EXECUTABLE),--include-executable=$(EXECUTABLE)), \
"BUILD","binfmt debian-$* (debootstrapped)"), \
$(call quiet-command, \
- $(DOCKER_SCRIPT) check --quiet qemu:debian-$* $< || \
+ $(DOCKER_SCRIPT) check --quiet qemu/debian-$* $< || \
{ echo "You will need to build $(EXECUTABLE)"; exit 1;},\
"CHECK", "debian-$* exists"))
# Specialist build images, sometimes very limited tools
docker-image-debian-tricore-cross: docker-image-debian9
+docker-image-debian-all-test-cross: docker-image-debian10
docker-image-debian-arm64-test-cross: docker-image-debian11
# These images may be good enough for building tests but not for test builds
@echo ' Include extra files in image.'
@echo ' ENGINE=auto/docker/podman'
@echo ' Specify which container engine to run.'
+ @echo ' REGISTRY=url Cache builds from registry (default:$(DOCKER_REGISTRY))'
# This rule if for directly running against an arbitrary docker target.
# It is called by the expanded docker targets (e.g. make
docker-run-%: CMD = $(shell echo '$@' | sed -e 's/docker-run-\([^@]*\)@\(.*\)/\1/')
docker-run-%: IMAGE = $(shell echo '$@' | sed -e 's/docker-run-\([^@]*\)@\(.*\)/\2/')
docker-run-%:
- @$(MAKE) docker-run TEST=$(CMD) IMAGE=qemu:$(IMAGE)
+ @$(MAKE) docker-run TEST=$(CMD) IMAGE=qemu/$(IMAGE)
docker-clean:
$(call quiet-command, $(DOCKER_SCRIPT) clean)
check_qemu()
{
# default to make check unless the caller specifies
- if test -z "$@"; then
+ if [ $# = 0 ]; then
INVOCATION="check"
else
INVOCATION="$@"
for l in df.splitlines():
if len(l.strip()) == 0 or l.startswith("#"):
continue
- from_pref = "FROM qemu:"
+ from_pref = "FROM qemu/"
if l.startswith(from_pref):
# TODO: Alternatively we could replace this line with "FROM $ID"
# where $ID is the image's hex id obtained with
""" Running Docker commands """
def __init__(self):
self._command = _guess_engine_command()
+
+ if "docker" in self._command and "TRAVIS" not in os.environ:
+ os.environ["DOCKER_BUILDKIT"] = "1"
+ self._buildkit = True
+ else:
+ self._buildkit = False
+
self._instance = None
atexit.register(self._kill_instances)
signal.signal(signal.SIGTERM, self._kill_instances)
return labels.get("com.qemu.dockerfile-checksum", "")
def build_image(self, tag, docker_dir, dockerfile,
- quiet=True, user=False, argv=None, extra_files_cksum=[]):
+ quiet=True, user=False, argv=None, registry=None,
+ extra_files_cksum=[]):
if argv is None:
argv = []
+ # pre-calculate the docker checksum before any
+ # substitutions we make for caching
+ checksum = _text_checksum(_dockerfile_preprocess(dockerfile))
+
+ if registry is not None:
+ sources = re.findall("FROM qemu\/(.*)", dockerfile)
+ # Fetch any cache layers we can, may fail
+ for s in sources:
+ pull_args = ["pull", "%s/qemu/%s" % (registry, s)]
+ if self._do(pull_args, quiet=quiet) != 0:
+ registry = None
+ break
+ # Make substitutions
+ if registry is not None:
+ dockerfile = dockerfile.replace("FROM qemu/",
+ "FROM %s/qemu/" %
+ (registry))
+
tmp_df = tempfile.NamedTemporaryFile(mode="w+t",
encoding='utf-8',
dir=docker_dir, suffix=".docker")
(uname, uid, uname))
tmp_df.write("\n")
- tmp_df.write("LABEL com.qemu.dockerfile-checksum=%s" %
- _text_checksum(_dockerfile_preprocess(dockerfile)))
+ tmp_df.write("LABEL com.qemu.dockerfile-checksum=%s" % (checksum))
for f, c in extra_files_cksum:
tmp_df.write("LABEL com.qemu.%s-checksum=%s" % (f, c))
tmp_df.flush()
- self._do_check(["build", "-t", tag, "-f", tmp_df.name] + argv +
- [docker_dir],
+ build_args = ["build", "-t", tag, "-f", tmp_df.name]
+ if self._buildkit:
+ build_args += ["--build-arg", "BUILDKIT_INLINE_CACHE=1"]
+
+ if registry is not None:
+ pull_args = ["pull", "%s/%s" % (registry, tag)]
+ self._do(pull_args, quiet=quiet)
+ cache = "%s/%s" % (registry, tag)
+ build_args += ["--cache-from", cache]
+ build_args += argv
+ build_args += [docker_dir]
+
+ self._do_check(build_args,
quiet=quiet)
def update_image(self, tag, tarball, quiet=True):
parser.add_argument("--add-current-user", "-u", dest="user",
action="store_true",
help="Add the current user to image's passwd")
+ parser.add_argument("--registry", "-r",
+ help="cache from docker registry")
parser.add_argument("-t", dest="tag",
help="Image Tag")
parser.add_argument("-f", dest="dockerfile",
for k, v in os.environ.items()
if k.lower() in FILTERED_ENV_NAMES]
dkr.build_image(tag, docker_dir, dockerfile,
- quiet=args.quiet, user=args.user, argv=argv,
+ quiet=args.quiet, user=args.user,
+ argv=argv, registry=args.registry,
extra_files_cksum=cksum)
rmtree(docker_dir)
--- /dev/null
+#
+# Docker all cross-compiler target (tests only)
+#
+# While the normal cross builds take care to setup proper multiarch
+# build environments which can cross build QEMU this just installs the
+# basic compilers for as many targets as possible. We shall use this
+# to build and run linux-user tests on GitLab
+#
+FROM qemu/debian10
+
+# What we need to build QEMU itself
+RUN apt update && \
+ DEBIAN_FRONTEND=noninteractive eatmydata \
+ apt build-dep -yy qemu
+
+# Add the foreign architecture we want and install dependencies
+RUN DEBIAN_FRONTEND=noninteractive eatmydata \
+ apt install -y --no-install-recommends \
+ gcc-aarch64-linux-gnu \
+ libc6-dev-arm64-cross \
+ gcc-alpha-linux-gnu \
+ libc6.1-dev-alpha-cross \
+ gcc-arm-linux-gnueabihf \
+ libc6-dev-armhf-cross \
+ gcc-hppa-linux-gnu \
+ libc6-dev-hppa-cross \
+ gcc-m68k-linux-gnu \
+ libc6-dev-m68k-cross \
+ gcc-mips-linux-gnu \
+ libc6-dev-mips-cross \
+ gcc-mips64-linux-gnuabi64 \
+ libc6-dev-mips64-cross \
+ gcc-mips64el-linux-gnuabi64 \
+ libc6-dev-mips64el-cross \
+ gcc-mipsel-linux-gnu \
+ libc6-dev-mipsel-cross \
+ gcc-powerpc-linux-gnu \
+ libc6-dev-powerpc-cross \
+ gcc-powerpc64-linux-gnu \
+ libc6-dev-ppc64-cross \
+ gcc-powerpc64le-linux-gnu \
+ libc6-dev-ppc64el-cross \
+ gcc-riscv64-linux-gnu \
+ libc6-dev-riscv64-cross \
+ gcc-s390x-linux-gnu \
+ libc6-dev-s390x-cross \
+ gcc-sh4-linux-gnu \
+ libc6-dev-sh4-cross \
+ gcc-sparc64-linux-gnu \
+ libc6-dev-sparc64-cross
+
+ENV QEMU_CONFIGURE_OPTS --disable-system --disable-docs --disable-tools
+ENV DEF_TARGET_LIST aarch64-linux-user,alpha-linux-user,arm-linux-user,hppa-linux-user,i386-linux-user,m68k-linux-user,mips-linux-user,mips64-linux-user,mips64el-linux-user,mipsel-linux-user,ppc-linux-user,ppc64-linux-user,ppc64le-linux-user,riscv64-linux-user,s390x-linux-user,sh4-linux-user,sparc64-linux-user
#
# This docker target builds on the debian Buster base image.
#
-FROM qemu:debian10
+FROM qemu/debian10
RUN apt update && \
DEBIAN_FRONTEND=noninteractive eatmydata \
# This docker target is used on non-x86_64 machines which need the
# x86_64 cross compilers installed.
#
-FROM qemu:debian10
+FROM qemu/debian10
MAINTAINER Alex Bennée <alex.bennee@linaro.org>
# Add the foreign architecture we want and install dependencies
# This docker target builds on the debian Stretch base image. Further
# libraries which are not widely available are installed by hand.
#
-FROM qemu:debian10
+FROM qemu/debian10
MAINTAINER Philippe Mathieu-Daudé <f4bug@amsat.org>
RUN apt update && \
#
# This docker target builds on the debian Buster base image.
#
-FROM qemu:debian10
+FROM qemu/debian10
# Add the foreign architecture we want and install dependencies
RUN dpkg --add-architecture arm64
#
# This docker target builds on the debian Bullseye base image.
#
-FROM qemu:debian11
+FROM qemu/debian11
# Add the foreign architecture we want and install dependencies
RUN dpkg --add-architecture arm64
#
# This docker target builds on the debian Stretch base image.
#
-FROM qemu:debian10
+FROM qemu/debian10
MAINTAINER Philippe Mathieu-Daudé <f4bug@amsat.org>
# Add the foreign architecture we want and install dependencies
#
# This docker target builds on the debian Stretch base image.
#
-FROM qemu:debian10
+FROM qemu/debian10
# Add the foreign architecture we want and install dependencies
RUN dpkg --add-architecture armhf
#
# This docker target builds on the debian Buster base image.
#
-FROM qemu:debian10
+FROM qemu/debian10
RUN apt update && \
DEBIAN_FRONTEND=noninteractive eatmydata \
#
# This docker target builds on the debian Buster base image.
#
-FROM qemu:debian10
+FROM qemu/debian10
RUN apt update && \
DEBIAN_FRONTEND=noninteractive eatmydata \
#
# This docker target builds on the debian Buster base image.
#
-FROM qemu:debian10
+FROM qemu/debian10
MAINTAINER Philippe Mathieu-Daudé <f4bug@amsat.org>
#
# This docker target builds on the debian Buster base image.
#
-FROM qemu:debian10
+FROM qemu/debian10
RUN apt update && \
DEBIAN_FRONTEND=noninteractive eatmydata \
# This docker target builds on the debian Stretch base image.
#
-FROM qemu:debian10
+FROM qemu/debian10
MAINTAINER Philippe Mathieu-Daudé <f4bug@amsat.org>
#
# This docker target builds on the debian Stretch base image.
#
-FROM qemu:debian10
+FROM qemu/debian10
MAINTAINER Philippe Mathieu-Daudé <f4bug@amsat.org>
#
# This docker target builds on the debian Buster base image.
#
-FROM qemu:debian10
+FROM qemu/debian10
RUN apt update && \
DEBIAN_FRONTEND=noninteractive eatmydata \
# Docker ppc64 cross-compiler target
#
# This docker target builds on the debian Buster base image.
-FROM qemu:debian10
+FROM qemu/debian10
RUN apt update && \
DEBIAN_FRONTEND=noninteractive eatmydata \
#
# This docker target builds on the debian Stretch base image.
#
-FROM qemu:debian10
+FROM qemu/debian10
# Add the foreign architecture we want and install dependencies
RUN dpkg --add-architecture ppc64el && \
#
# This docker target builds on the debian Buster base image.
#
-FROM qemu:debian10
+FROM qemu/debian10
RUN apt update && \
DEBIAN_FRONTEND=noninteractive eatmydata \
#
# This docker target builds on the debian Stretch base image.
#
-FROM qemu:debian10
+FROM qemu/debian10
# Add the s390x architecture
RUN dpkg --add-architecture s390x
#
# This docker target builds on the debian Buster base image.
#
-FROM qemu:debian10
+FROM qemu/debian10
RUN apt update && \
DEBIAN_FRONTEND=noninteractive eatmydata \
#
# This docker target builds on the debian Buster base image.
#
-FROM qemu:debian10
+FROM qemu/debian10
RUN apt update && \
DEBIAN_FRONTEND=noninteractive eatmydata \
#
# SPDX-License-Identifier: GPL-2.0-or-later
#
-FROM qemu:debian9
+FROM qemu/debian9
MAINTAINER Philippe Mathieu-Daudé <f4bug@amsat.org>
#
# This docker target builds on the debian Stretch MXE base image.
#
-FROM qemu:debian9-mxe
+FROM qemu/debian9-mxe
MAINTAINER Philippe Mathieu-Daudé <f4bug@amsat.org>
#
# This docker target builds on the debian Stretch MXE base image.
#
-FROM qemu:debian9-mxe
+FROM qemu/debian9-mxe
MAINTAINER Philippe Mathieu-Daudé <f4bug@amsat.org>
git \
python3-minimal
-ENV CPU_LIST csp dc232b dc233c
-ENV TOOLCHAIN_RELEASE 2018.02
+ENV CPU_LIST dc232b dc233c de233_fpu dsp3400
+ENV TOOLCHAIN_RELEASE 2020.07
RUN for cpu in $CPU_LIST; do \
curl -#SL http://github.com/foss-xtensa/toolchain/releases/download/$TOOLCHAIN_RELEASE/x86_64-$TOOLCHAIN_RELEASE-xtensa-$cpu-elf.tar.gz \
| tar -xzC /opt; \
done
-ENV PATH $PATH:/opt/$TOOLCHAIN_RELEASE/xtensa-dc232b-elf/bin:/opt/$TOOLCHAIN_RELEASE/xtensa-dc233c-elf/bin:/opt/$TOOLCHAIN_RELEASE/xtensa-csp-elf/bin
+ENV PATH $PATH:/opt/$TOOLCHAIN_RELEASE/xtensa-dc232b-elf/bin:/opt/$TOOLCHAIN_RELEASE/xtensa-dc233c-elf/bin:/opt/$TOOLCHAIN_RELEASE/xtensa-de233_fpu-elf/bin:/opt/$TOOLCHAIN_RELEASE/xtensa-dsp3400-elf/bin
#
# This docker target builds on the debian Stretch base image.
#
-FROM qemu:debian9
+FROM qemu/debian9
MAINTAINER Philippe Mathieu-Daudé <f4bug@amsat.org>
pixman-devel \
python3 \
python3-PyYAML \
+ python3-numpy \
+ python3-opencv \
+ python3-pillow \
+ python3-pip \
python3-sphinx \
+ python3-virtualenv \
rdma-core-devel \
SDL2-devel \
snappy-devel \
systemd-devel \
systemtap-sdt-devel \
tar \
+ tesseract \
+ tesseract-langpack-eng \
texinfo \
usbredir-devel \
virglrenderer-devel \
libxen-dev \
libzstd-dev \
make \
- python3-yaml \
+ python3-numpy \
+ python3-opencv \
+ python3-pil \
+ python3-pip \
python3-sphinx \
+ python3-venv \
+ python3-yaml \
+ rpm2cpio \
sparse \
+ tesseract-ocr \
+ tesseract-ocr-eng \
texinfo \
xfslibs-dev\
vim
RUN dpkg -l $PACKAGES | sort > /packages.txt
ENV FEATURES clang tsan pyyaml sdl2
-# https://bugs.launchpad.net/qemu/+bug/1838763
-ENV QEMU_CONFIGURE_OPTS --disable-libssh
-
# Apply patch https://reviews.llvm.org/D75820
# This is required for TSan in clang-10 to compile with QEMU.
RUN sed -i 's/^const/static const/g' /usr/lib/llvm-10/lib/clang/10.0.0/include/sanitizer/tsan_interface.h
+# -*- Mode: makefile -*-
+#
+# This Makefile example is fairly independent from the main makefile
+# so users can take and adapt it for their build. We only really
+# include config-host.mak so we don't have to repeat probing for
+# cflags that the main configure has already done for us.
+#
+
BUILD_DIR := $(CURDIR)/../..
include $(BUILD_DIR)/config-host.mak
-include $(SRC_PATH)/rules.mak
-$(call set-vpath, $(SRC_PATH)/tests/plugin)
+VPATH += $(SRC_PATH)/tests/plugin
NAMES :=
NAMES += bb
SONAMES := $(addsuffix .so,$(addprefix lib,$(NAMES)))
-QEMU_CFLAGS += -fPIC -Wpsabi
-QEMU_CFLAGS += -I$(SRC_PATH)/include/qemu
+# The main QEMU uses Glib extensively so it's perfectly fine to use it
+# in plugins (which many example do).
+CFLAGS = $(GLIB_CFLAGS)
+CFLAGS += -fPIC
+CFLAGS += $(if $(findstring no-psabi,$(QEMU_CFLAGS)),-Wpsabi)
+CFLAGS += -I$(SRC_PATH)/include/qemu
all: $(SONAMES)
+%.o: %.c
+ $(CC) $(CFLAGS) -c -o $@ $<
+
lib%.so: %.o
$(CC) -shared -Wl,-soname,$@ -o $@ $^ $(LDLIBS)
QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
-static uint64_t bb_count;
-static uint64_t insn_count;
+typedef struct {
+ GMutex lock;
+ int index;
+ uint64_t bb_count;
+ uint64_t insn_count;
+} CPUCount;
+
+/* Used by the inline & linux-user counts */
static bool do_inline;
+static CPUCount inline_count;
+
+/* Dump running CPU total on idle? */
+static bool idle_report;
+static GPtrArray *counts;
+static int max_cpus;
+
+static void gen_one_cpu_report(CPUCount *count, GString *report)
+{
+ if (count->bb_count) {
+ g_string_append_printf(report, "CPU%d: "
+ "bb's: %" PRIu64", insns: %" PRIu64 "\n",
+ count->index,
+ count->bb_count, count->insn_count);
+ }
+}
static void plugin_exit(qemu_plugin_id_t id, void *p)
{
- g_autofree gchar *out = g_strdup_printf(
- "bb's: %" PRIu64", insns: %" PRIu64 "\n",
- bb_count, insn_count);
- qemu_plugin_outs(out);
+ g_autoptr(GString) report = g_string_new("");
+
+ if (do_inline || !max_cpus) {
+ g_string_printf(report, "bb's: %" PRIu64", insns: %" PRIu64 "\n",
+ inline_count.bb_count, inline_count.insn_count);
+ } else {
+ g_ptr_array_foreach(counts, (GFunc) gen_one_cpu_report, report);
+ }
+ qemu_plugin_outs(report->str);
+}
+
+static void vcpu_idle(qemu_plugin_id_t id, unsigned int cpu_index)
+{
+ CPUCount *count = g_ptr_array_index(counts, cpu_index);
+ g_autoptr(GString) report = g_string_new("");
+ gen_one_cpu_report(count, report);
+
+ if (report->len > 0) {
+ g_string_prepend(report, "Idling ");
+ qemu_plugin_outs(report->str);
+ }
}
static void vcpu_tb_exec(unsigned int cpu_index, void *udata)
{
- unsigned long n_insns = (unsigned long)udata;
+ CPUCount *count = max_cpus ?
+ g_ptr_array_index(counts, cpu_index) : &inline_count;
- insn_count += n_insns;
- bb_count++;
+ unsigned long n_insns = (unsigned long)udata;
+ g_mutex_lock(&count->lock);
+ count->insn_count += n_insns;
+ count->bb_count++;
+ g_mutex_unlock(&count->lock);
}
static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
if (do_inline) {
qemu_plugin_register_vcpu_tb_exec_inline(tb, QEMU_PLUGIN_INLINE_ADD_U64,
- &bb_count, 1);
+ &inline_count.bb_count, 1);
qemu_plugin_register_vcpu_tb_exec_inline(tb, QEMU_PLUGIN_INLINE_ADD_U64,
- &insn_count, n_insns);
+ &inline_count.insn_count,
+ n_insns);
} else {
qemu_plugin_register_vcpu_tb_exec_cb(tb, vcpu_tb_exec,
QEMU_PLUGIN_CB_NO_REGS,
const qemu_info_t *info,
int argc, char **argv)
{
- if (argc && strcmp(argv[0], "inline") == 0) {
- do_inline = true;
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ char *opt = argv[i];
+ if (g_strcmp0(opt, "inline") == 0) {
+ do_inline = true;
+ } else if (g_strcmp0(opt, "idle") == 0) {
+ idle_report = true;
+ } else {
+ fprintf(stderr, "option parsing failed: %s\n", opt);
+ return -1;
+ }
+ }
+
+ if (info->system_emulation && !do_inline) {
+ max_cpus = info->system.max_vcpus;
+ counts = g_ptr_array_new();
+ for (i = 0; i < max_cpus; i++) {
+ CPUCount *count = g_new0(CPUCount, 1);
+ g_mutex_init(&count->lock);
+ count->index = i;
+ g_ptr_array_add(counts, count);
+ }
+ } else if (!do_inline) {
+ g_mutex_init(&inline_count.lock);
+ }
+
+ if (idle_report) {
+ qemu_plugin_register_vcpu_idle_cb(id, vcpu_idle);
}
qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
# Any format supporting backing files
_supported_fmt qcow qcow2 vmdk qed
_supported_proto generic
-_unsupported_proto vxhs
_unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat" \
"subformat=streamOptimized"
echo
TEST_IMG=$TEST_IMG_SAVE
-_make_test_img -b "$TEST_IMG.base" 6G
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT 6G
echo "Filling test image"
echo
No errors were found on the image.
Creating test image with backing file
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
Filling test image
=== IO: pattern 1
echo
TEST_IMG="$TEST_IMG_SAVE.orig"
-_make_test_img -b "$TEST_IMG_SAVE.base" 6G
+_make_test_img -b "$TEST_IMG_SAVE.base" -F $IMGFMT 6G
echo "Filling test image"
echo
No errors were found on the image.
Creating test image with backing file
-Formatting 'TEST_DIR/t.IMGFMT.orig', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT.orig', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
Filling test image
=== IO: pattern 1
echo
TEST_IMG="$TEST_IMG_SAVE.orig"
-_make_test_img -b "$TEST_IMG_SAVE.base" 6G
+_make_test_img -b "$TEST_IMG_SAVE.base" -F $IMGFMT 6G
echo "Filling test image"
echo
echo
echo Testing conversion with $backing_option"$TEST_IMG.base" | _filter_testdir | _filter_imgfmt
echo
- $QEMU_IMG convert -f $IMGFMT -O $IMGFMT $backing_option"$TEST_IMG.base" "$TEST_IMG.orig" "$TEST_IMG"
+ $QEMU_IMG convert -f $IMGFMT -O $IMGFMT $backing_option"$TEST_IMG.base" \
+ -o backing_fmt=$IMGFMT "$TEST_IMG.orig" "$TEST_IMG"
echo "Checking if backing clusters are allocated when they shouldn't"
echo
No errors were found on the image.
Creating test image with backing file
-Formatting 'TEST_DIR/t.IMGFMT.orig', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT.orig', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
Filling test image
=== IO: pattern 43
echo
TEST_IMG="$TEST_IMG_SAVE"
-_make_test_img -b "$TEST_IMG.base" 6G
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT 6G
echo "Filling test image"
echo
# Create an image with a null backing file to which committing will fail (with
# ENOSPC so we can distinguish the result from some generic EIO which may be
# generated anywhere in the block layer)
-_make_test_img -b "json:{'driver': '$IMGFMT',
- 'file': {
- 'driver': 'blkdebug',
- 'inject-error': [{
- 'event': 'write_aio',
- 'errno': 28,
- 'once': true
- }],
- 'image': {
- 'driver': 'file',
- 'filename': '$TEST_IMG.base'
- }}}"
+backing="json:{'driver': '$IMGFMT',
+ 'file': {
+ 'driver': 'blkdebug',
+ 'inject-error': [{
+ 'event': 'write_aio',
+ 'errno': 28,
+ 'once': true
+ }],
+ 'image': {
+ 'driver': 'file',
+ 'filename': '$TEST_IMG.base'
+ }}}"
+
+# Filter out newlines and collapse spaces
+backing=$(echo "$backing" | tr -d '\n' | tr -s ' ')
+
+_make_test_img -b "$backing" -F $IMGFMT
# Just write anything so committing will not be a no-op
$QEMU_IO -c 'writev 0 64k' "$TEST_IMG" | _filter_qemu_io
No errors were found on the image.
Creating test image with backing file
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
Filling test image
=== IO: pattern 1
Testing failing commit
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=1048576
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=json:{'driver': 'IMGFMT',,
- 'file': {
- 'driver': 'blkdebug',,
- 'inject-error': [{
- 'event': 'write_aio',,
- 'errno': 28,,
- 'once': true
- }],,
- 'image': {
- 'driver': 'file',,
- 'filename': 'TEST_DIR/t.IMGFMT.base'
- }}}
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=json:{'driver': 'IMGFMT',, 'file': { 'driver': 'blkdebug',, 'inject-error': [{ 'event': 'write_aio',, 'errno': 28,, 'once': true }],, 'image': { 'driver': 'file',, 'filename': 'TEST_DIR/t.IMGFMT.base' }}} backing_fmt=IMGFMT
wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
qemu-img: Block job failed: No space left on device
echo "Creating COW image"
echo
-_make_test_img -b "$TEST_IMG.base_old" 1G
+_make_test_img -b "$TEST_IMG.base_old" -F $IMGFMT 1G
io_pattern writev 0 $((4 * CLUSTER_SIZE)) 0 1 0x33
io_pattern writev $((8 * CLUSTER_SIZE)) $((4 * CLUSTER_SIZE)) 0 1 0x33
echo
echo Rebase and test again
echo
-$QEMU_IMG rebase -b "$TEST_IMG.base_new" "$TEST_IMG"
+$QEMU_IMG rebase -b "$TEST_IMG.base_new" -F $IMGFMT "$TEST_IMG"
io_pattern readv $((0 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x33
io_pattern readv $((1 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x33
io_pattern readv $((2 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x33
TEST_IMG=$BASE_OLD _make_test_img 1M
TEST_IMG=$BASE_NEW _make_test_img 1M
-TEST_IMG=$OVERLAY _make_test_img -b "$BASE_OLD_OREL" 1M
+TEST_IMG=$OVERLAY _make_test_img -b "$BASE_OLD_OREL" -F $IMGFMT 1M
echo
echo
pushd "$TEST_DIR" >/dev/null
-$QEMU_IMG rebase -f "$IMGFMT" -b "$BASE_NEW_OREL" "$OVERLAY_WREL"
+$QEMU_IMG rebase -f "$IMGFMT" -b "$BASE_NEW_OREL" -F $IMGFMT "$OVERLAY_WREL"
popd >/dev/null
# Verify the backing path is correct
-TEST_IMG=$OVERLAY _img_info | grep '^backing file'
+TEST_IMG=$OVERLAY _img_info | grep '^backing file:'
echo
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Creating COW image
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 backing_file=TEST_DIR/t.IMGFMT.base_old
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 backing_file=TEST_DIR/t.IMGFMT.base_old backing_fmt=IMGFMT
=== IO: pattern 0x33
wrote 262144/262144 bytes at offset 0
256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Formatting 'TEST_DIR/subdir/t.IMGFMT.base_old', fmt=IMGFMT size=1048576
Formatting 'TEST_DIR/subdir/t.IMGFMT.base_new', fmt=IMGFMT size=1048576
-Formatting 'TEST_DIR/subdir/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=t.IMGFMT.base_old
+Formatting 'TEST_DIR/subdir/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=t.IMGFMT.base_old backing_fmt=IMGFMT
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
echo
TEST_IMG="$TEST_IMG_SAVE"
-_make_test_img -b "$TEST_IMG.base" $image_size
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT $image_size
echo "Filling test image"
echo
_check_test_img
# Rebase it on top of its base image
-$QEMU_IMG rebase -b "$TEST_IMG.base" "$TEST_IMG"
+$QEMU_IMG rebase -b "$TEST_IMG.base" -F $IMGFMT "$TEST_IMG"
echo
echo block-backup
No errors were found on the image.
Creating test image with backing file
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294968832 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294968832 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
Filling test image
=== IO: pattern 196
# Any format supporting intenal snapshots
_supported_fmt qcow2
_supported_proto generic
-_unsupported_proto vxhs
# Internal snapshots are (currently) impossible with refcount_bits=1,
# and generally impossible with external data files
_unsupported_imgopts 'refcount_bits=1[^0-9]' data_file
def setUp(self):
iotests.create_image(backing_img, TestSingleDrive.image_len)
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img)
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % backing_img,
+ '-F', 'raw', mid_img)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % mid_img,
+ '-F', iotests.imgfmt, test_img)
qemu_io('-f', 'raw', '-c', 'write -P 0x1 0 512', backing_img)
qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x1 524288 512', mid_img)
self.vm = iotests.VM().add_drive("blkdebug::" + test_img,
iotests.create_image(self.imgs[0], self.image_len)
for i in range(1, self.num_imgs):
qemu_img('create', '-f', iotests.imgfmt,
- '-o', 'backing_file=%s' % self.imgs[i-1], self.imgs[i])
+ '-o', 'backing_file=%s' % self.imgs[i-1],
+ '-F', 'raw' if i == 1 else iotests.imgfmt, self.imgs[i])
# Put data into the images we are copying data from
odd_img_indexes = [x for x in reversed(range(self.num_imgs)) if x % 2 == 1]
node_name = 'node%d' % i
job_id = 'stream-%s' % node_name
pending_jobs.append(job_id)
- result = self.vm.qmp('block-stream', device=node_name, job_id=job_id, base=self.imgs[i-2], speed=512*1024)
+ result = self.vm.qmp('block-stream', device=node_name, job_id=job_id, base=self.imgs[i-2], speed=1024)
self.assert_qmp(result, 'return', {})
for job in pending_jobs:
qemu_io('-f', iotests.imgfmt,
'-c', 'write -P 0x55 0 1024', backing_img)
qemu_img('create', '-f', iotests.imgfmt,
- '-o', 'backing_file=%s' % backing_img, child_img)
+ '-o', 'backing_file=%s' % backing_img,
+ '-F', iotests.imgfmt, child_img)
opts.append("children.%d.file.filename=%s" % (i, child_img))
opts.append("children.%d.node-name=node%d" % (i, i))
def setUp(self):
iotests.create_image(backing_img, self.backing_len)
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img, str(self.image_len))
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % backing_img,
+ '-F', 'raw', test_img, str(self.image_len))
self.vm = iotests.VM().add_drive(test_img)
self.vm.launch()
def setUp(self):
qemu_img('create', backing_img, str(TestStreamStop.image_len))
qemu_io('-f', 'raw', '-c', 'write -P 0x1 0 32M', backing_img)
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % backing_img,
+ '-F', 'raw', test_img)
qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x1 32M 32M', test_img)
self.vm = iotests.VM().add_drive("blkdebug::" + test_img)
self.vm.launch()
def setUp(self):
qemu_img('create', backing_img, str(TestSetSpeed.image_len))
qemu_io('-f', 'raw', '-c', 'write -P 0x1 0 32M', backing_img)
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % backing_img,
+ '-F', 'raw', test_img)
qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x1 32M 32M', test_img)
self.vm = iotests.VM().add_drive('blkdebug::' + test_img)
self.vm.launch()
TEST_IMG="$TEST_IMG_SAVE"
-_make_test_img -b "$TEST_IMG.base" 6G
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT 6G
echo
echo "== zero write with backing file =="
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
wrote 1048576/1048576 bytes at offset 0
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
== zero write with backing file ==
wrote 196608/196608 bytes at offset 65536
TEST_IMG="$TEST_IMG_SAVE"
-_make_test_img -b "$TEST_IMG.base" 6G
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT 6G
echo
echo "== COW in a single cluster =="
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 512/512 bytes at offset 130560
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
== COW in a single cluster ==
wrote 2048/2048 bytes at offset 0
TEST_IMG="$TEST_IMG_SAVE"
-_make_test_img -b "$TEST_IMG.base" 6G
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT 6G
echo
echo "== Some concurrent requests touching the same cluster =="
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset 16711680
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
== Some concurrent requests touching the same cluster ==
wrote 65536/65536 bytes at offset XXX
TEST_IMG="$TEST_IMG".base _make_test_img -o "compat=1.1,lazy_refcounts=on" $size
-_make_test_img -o "compat=1.1,lazy_refcounts=on,backing_file=$TEST_IMG.base" $size
+_make_test_img -o "compat=1.1,lazy_refcounts=on,backing_file=$TEST_IMG.base" \
+ -F $IMGFMT $size
$QEMU_IO -c "write 0 512" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG commit "$TEST_IMG"
== Committing to a backing file with lazy_refcounts=on ==
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Image committed.
def setUp(self):
iotests.create_image(backing_img, self.image_len)
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img)
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % backing_img, '-F', 'raw', mid_img)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % mid_img,
+ '-F', iotests.imgfmt, test_img)
qemu_io('-f', 'raw', '-c', 'write -P 0xab 0 524288', backing_img)
qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', mid_img)
self.vm = iotests.VM().add_drive(test_img, "node-name=top,backing.node-name=mid,backing.backing.node-name=base", interface="none")
if exception.errno != errno.EEXIST:
raise
iotests.create_image(self.backing_img_abs, TestRelativePaths.image_len)
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % self.backing_img_abs, self.mid_img_abs)
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % self.mid_img_abs, self.test_img)
- qemu_img('rebase', '-u', '-b', self.backing_img, self.mid_img_abs)
- qemu_img('rebase', '-u', '-b', self.mid_img, self.test_img)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % self.backing_img_abs,
+ '-F', 'raw', self.mid_img_abs)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % self.mid_img_abs,
+ '-F', iotests.imgfmt, self.test_img)
+ qemu_img('rebase', '-u', '-b', self.backing_img,
+ '-F', 'raw', self.mid_img_abs)
+ qemu_img('rebase', '-u', '-b', self.mid_img,
+ '-F', iotests.imgfmt, self.test_img)
qemu_io('-f', 'raw', '-c', 'write -P 0xab 0 524288', self.backing_img_abs)
qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', self.mid_img_abs)
self.vm = iotests.VM().add_drive(self.test_img)
def setUp(self):
qemu_img('create', backing_img, str(TestSetSpeed.image_len))
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img)
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % backing_img, '-F', 'raw', mid_img)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % mid_img,
+ '-F', iotests.imgfmt, test_img)
qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x1 0 512', test_img)
qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', mid_img)
self.vm = iotests.VM().add_drive('blkdebug::' + test_img)
def setUp(self):
iotests.create_image(self.img0, self.image_len)
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % self.img0, self.img1)
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % self.img1, self.img2)
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % self.img2, self.img3)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % self.img0, '-F', 'raw', self.img1)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % self.img1,
+ '-F', iotests.imgfmt, self.img2)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % self.img2,
+ '-F', iotests.imgfmt, self.img3)
qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xab 0 128K', self.img1)
self.vm = iotests.VM().add_drive(self.img3)
self.vm.launch()
def setUp(self):
iotests.create_image(backing_img, self.image_len)
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img)
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % backing_img,
+ '-F', 'raw', mid_img)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % mid_img,
+ '-F', iotests.imgfmt, test_img)
qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x11 0 512k', mid_img)
qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x22 0 512k', test_img)
def setUp(self):
iotests.create_image(backing_img, self.image_len)
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % backing_img, '-F', 'raw', test_img)
self.vm = iotests.VM().add_drive(test_img, "node-name=top,backing.node-name=base")
if iotests.qemu_default_machine == 'pc':
self.vm.add_drive(None, 'media=cdrom', 'ide')
self.assert_no_active_block_jobs()
qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s'
- % (self.image_len, backing_img), target_img)
+ % (self.image_len, backing_img),
+ '-F', 'raw', target_img)
result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
mode='existing', target=self.qmp_target)
self.assert_qmp(result, 'return', {})
def setUp(self):
TestSingleDrive.setUp(self)
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % backing_img, '-F', 'raw', target_img)
args = {'driver': iotests.imgfmt,
'node-name': self.qmp_target,
'file': { 'filename': target_img, 'driver': 'file' } }
def setUp(self):
iotests.create_image(backing_img, TestMirrorNoBacking.image_len)
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % backing_img, '-F', 'raw', test_img)
self.vm = iotests.VM().add_drive(test_img)
self.vm.launch()
def test_complete(self):
self.assert_no_active_block_jobs()
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % backing_img, '-F', 'raw', target_img)
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
mode='existing', target=target_img)
self.assert_qmp(result, 'return', {})
def test_cancel(self):
self.assert_no_active_block_jobs()
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % backing_img, '-F', 'raw', target_img)
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
mode='existing', target=target_img)
self.assert_qmp(result, 'return', {})
qemu_img('create', '-f', iotests.imgfmt, '-o', 'size=%d'
%(TestMirrorNoBacking.image_len), target_backing_img)
qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s'
- % (TestMirrorNoBacking.image_len, target_backing_img), target_img)
+ % (TestMirrorNoBacking.image_len, target_backing_img),
+ '-F', iotests.imgfmt, target_img)
result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
mode='existing', target=target_img)
def setUp(self):
iotests.create_image(backing_img, TestMirrorResized.backing_len)
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % backing_img, '-F', 'raw', test_img)
qemu_img('resize', test_img, '2M')
self.vm = iotests.VM().add_drive(test_img)
self.vm.launch()
# Test COW into the target image. The first half of the
# cluster at MIRROR_GRANULARITY has to be copied from
# backing_img, even though sync='top'.
- qemu_img('create', '-f', iotests.imgfmt, '-ocluster_size=131072,backing_file=%s' %(backing_img), target_img)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-ocluster_size=131072,backing_file=%s' %(backing_img),
+ '-F', 'raw', target_img)
result = self.vm.qmp('drive-mirror', device='drive0', sync='top',
on_source_error='ignore',
mode='existing', target=target_img)
self.vm.shutdown()
# Detach blkdebug to compare images successfully
- qemu_img('rebase', '-f', iotests.imgfmt, '-u', '-b', backing_img, test_img)
+ qemu_img('rebase', '-f', iotests.imgfmt, '-u', '-b', backing_img,
+ '-F', 'raw', test_img)
self.assertTrue(iotests.compare_images(test_img, target_img),
'target image does not match source after mirroring')
self.blkdebug_file = target_img + ".blkdebug"
iotests.create_image(backing_img, TestWriteErrors.image_len)
self.create_blkdebug_file(self.blkdebug_file, "write_aio", 5)
- qemu_img('create', '-f', iotests.imgfmt, '-obacking_file=%s' %(backing_img), test_img)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-obacking_file=%s' %(backing_img), '-F', 'raw', test_img)
self.vm = iotests.VM().add_drive(test_img)
self.target_img = 'blkdebug:%s:%s' % (self.blkdebug_file, target_img)
qemu_img('create', '-f', iotests.imgfmt, '-osize=%d' %(TestWriteErrors.image_len), target_img)
def setUp(self):
qemu_img('create', backing_img, str(TestSetSpeed.image_len))
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % backing_img, '-F', 'raw', test_img)
self.vm = iotests.VM().add_drive(test_img)
self.vm.launch()
echo
echo "== Rebasing the image =="
-$QEMU_IMG rebase -u -b "$TEST_IMG.orig" "$TEST_IMG"
-$QEMU_IMG rebase -b "$TEST_IMG.orig" "$TEST_IMG"
+$QEMU_IMG rebase -u -b "$TEST_IMG.orig" -F $IMGFMT "$TEST_IMG"
+$QEMU_IMG rebase -b "$TEST_IMG.orig" -F $IMGFMT "$TEST_IMG"
_check_test_img
# success, all done
size=128M
_make_test_img $size
-$QEMU_IMG rebase -u -b "$TEST_IMG" "$TEST_IMG"
+$QEMU_IMG rebase -u -b "$TEST_IMG" -F $IMGFMT "$TEST_IMG"
echo
echo "== backing file references self =="
_make_test_img $size
mv "$TEST_IMG" "$TEST_IMG.base"
-_make_test_img -b "$TEST_IMG.base" $size
-$QEMU_IMG rebase -u -b "$TEST_IMG" "$TEST_IMG.base"
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT $size
+$QEMU_IMG rebase -u -b "$TEST_IMG" -F $IMGFMT "$TEST_IMG.base"
echo
echo "== parent references self =="
_make_test_img $size
mv "$TEST_IMG" "$TEST_IMG.1.base"
-_make_test_img -b "$TEST_IMG.1.base" $size
+_make_test_img -b "$TEST_IMG.1.base" -F $IMGFMT $size
mv "$TEST_IMG" "$TEST_IMG.2.base"
-_make_test_img -b "$TEST_IMG.2.base" $size
+_make_test_img -b "$TEST_IMG.2.base" -F $IMGFMT $size
mv "$TEST_IMG" "$TEST_IMG.3.base"
-_make_test_img -b "$TEST_IMG.3.base" $size
-$QEMU_IMG rebase -u -b "$TEST_IMG.2.base" "$TEST_IMG.1.base"
+_make_test_img -b "$TEST_IMG.3.base" -F $IMGFMT $size
+$QEMU_IMG rebase -u -b "$TEST_IMG.2.base" -F $IMGFMT "$TEST_IMG.1.base"
echo
echo "== ancestor references another ancestor =="
_make_test_img $size
mv "$TEST_IMG" "$TEST_IMG.1.base"
-_make_test_img -b "$TEST_IMG.1.base" $size
+_make_test_img -b "$TEST_IMG.1.base" -F $IMGFMT $size
mv "$TEST_IMG" "$TEST_IMG.2.base"
-_make_test_img -b "$TEST_IMG.2.base" $size
+_make_test_img -b "$TEST_IMG.2.base" -F $IMGFMT $size
echo
echo "== finite chain of length 3 (human) =="
-_img_info --backing-chain
+# Exclude backing format, since qed differs from qcow2 on what gets stored
+_img_info --backing-chain | grep -v '^backing file format:'
echo
echo "== finite chain of length 3 (json) =="
-_img_info --backing-chain --output=json
+_img_info --backing-chain --output=json | grep -v 'backing-filename-format'
# success, all done
echo "*** done"
== backing file references self ==
qemu-img: Backing file 'TEST_DIR/t.IMGFMT' creates an infinite loop.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
== parent references self ==
qemu-img: Backing file 'TEST_DIR/t.IMGFMT' creates an infinite loop.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.1.base
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.2.base
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.3.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.1.base backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.2.base backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.3.base backing_fmt=IMGFMT
== ancestor references another ancestor ==
qemu-img: Backing file 'TEST_DIR/t.IMGFMT.2.base' creates an infinite loop.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.1.base
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.2.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.1.base backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.2.base backing_fmt=IMGFMT
== finite chain of length 3 (human) ==
image: TEST_DIR/t.IMGFMT
mv "$TEST_IMG" "$TEST_IMG.base"
-_make_test_img -b "$TEST_IMG.base" 6G
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT 6G
echo
echo "== Some concurrent requests touching the same cluster =="
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset 2031616
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
== Some concurrent requests touching the same cluster ==
blkdebug: Suspended request 'A'
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 compat=1.1 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o compat=0.42 TEST_DIR/t.qcow2 64M
-qemu-img: TEST_DIR/t.qcow2: Invalid parameter '0.42'
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 compat=0.42 lazy_refcounts=off refcount_bits=16
+qemu-img: TEST_DIR/t.qcow2: Invalid parameter '0.42'
qemu-img create -f qcow2 -o compat=foobar TEST_DIR/t.qcow2 64M
-qemu-img: TEST_DIR/t.qcow2: Invalid parameter 'foobar'
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 compat=foobar lazy_refcounts=off refcount_bits=16
+qemu-img: TEST_DIR/t.qcow2: Invalid parameter 'foobar'
== Check preallocation option ==
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 preallocation=metadata compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o preallocation=1234 TEST_DIR/t.qcow2 64M
-qemu-img: TEST_DIR/t.qcow2: Invalid parameter '1234'
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 preallocation=1234 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
+qemu-img: TEST_DIR/t.qcow2: Invalid parameter '1234'
== Check encryption option ==
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 compat=0.10 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=on TEST_DIR/t.qcow2 64M
-qemu-img: TEST_DIR/t.qcow2: Lazy refcounts only supported with compatibility level 1.1 and above (use version=v3 or greater)
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=67108864 compat=0.10 lazy_refcounts=on refcount_bits=16
+qemu-img: TEST_DIR/t.qcow2: Lazy refcounts only supported with compatibility level 1.1 and above (use version=v3 or greater)
*** done
TEST_IMG="$TEST_IMG_SAVE"
-_make_test_img -b "$TEST_IMG.old" $size
+_make_test_img -b "$TEST_IMG.old" -F $IMGFMT $size
$QEMU_IO -c "write -z 0 1048576" "$TEST_IMG" | _filter_qemu_io
echo
echo "== Rebasing the image =="
-$QEMU_IMG rebase -b "$TEST_IMG.new" "$TEST_IMG"
+$QEMU_IMG rebase -b "$TEST_IMG.new" -F $IMGFMT "$TEST_IMG"
$QEMU_IO -c "read -P 0x00 0 1048576" "$TEST_IMG" | _filter_qemu_io
# success, all done
Formatting 'TEST_DIR/t.IMGFMT.new', fmt=IMGFMT size=10485760
wrote 1048576/1048576 bytes at offset 0
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=10485760 backing_file=TEST_DIR/t.IMGFMT.old
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=10485760 backing_file=TEST_DIR/t.IMGFMT.old backing_fmt=IMGFMT
wrote 1048576/1048576 bytes at offset 0
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
_make_test_img $size
cp "$TEST_IMG" "$TEST_IMG.orig"
mv "$TEST_IMG" "$TEST_IMG.base"
-_make_test_img -b "$TEST_IMG.base" $size
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT $size
echo
echo === Unknown option ===
QA output created by 051
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
=== Unknown option ===
QA output created by 051
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
=== Unknown option ===
QA output created by 054
creating too large image (1 EB)
-qemu-img: TEST_DIR/t.IMGFMT: The image size is too large for file format 'IMGFMT' (try using a larger cluster size)
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1152921504606846976
+qemu-img: TEST_DIR/t.IMGFMT: The image size is too large for file format 'IMGFMT' (try using a larger cluster size)
creating too large image (1 EB) using qcow2.py
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296
def setUp(self):
create_image(backing_img, TestSyncModesNoneAndTop.image_len)
- qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
+ qemu_img('create', '-f', iotests.imgfmt,
+ '-o', 'backing_file=%s' % backing_img, '-F', 'raw', test_img)
qemu_io('-c', 'write -P0x41 0 512', test_img)
qemu_io('-c', 'write -P0xd5 1M 32k', test_img)
qemu_io('-c', 'write -P0xdc 32M 124k', test_img)
echo
echo "=== Testing big twoGbMaxExtentFlat ==="
_make_test_img -o "subformat=twoGbMaxExtentFlat" 1000G
-$QEMU_IMG info $TEST_IMG | _filter_testdir | sed -e 's/cid: [0-9]*/cid: XXXXXXXX/'
+_img_info --format-specific | _filter_img_info --format-specific
_cleanup_test_img
echo
=== Testing big twoGbMaxExtentFlat ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824000
-image: TEST_DIR/t.vmdk
-file format: vmdk
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
virtual size: 0.977 TiB (1073741824000 bytes)
-disk size: 1.97 MiB
Format specific information:
- cid: XXXXXXXX
- parent cid: XXXXXXXX
+ cid: XXXXXXXXXX
+ parent cid: XXXXXXXXXX
create type: twoGbMaxExtentFlat
extents:
[0]:
virtual size: 2147483648
- filename: TEST_DIR/t-f001.vmdk
+ filename: TEST_DIR/t-f001.IMGFMT
format: FLAT
[1]:
virtual size: 2147483648
- filename: TEST_DIR/t-f002.vmdk
+ filename: TEST_DIR/t-f002.IMGFMT
format: FLAT
[2]:
virtual size: 2147483648
- filename: TEST_DIR/t-f003.vmdk
+ filename: TEST_DIR/t-f003.IMGFMT
format: FLAT
[3]:
virtual size: 2147483648
- filename: TEST_DIR/t-f004.vmdk
+ filename: TEST_DIR/t-f004.IMGFMT
format: FLAT
[4]:
virtual size: 2147483648
- filename: TEST_DIR/t-f005.vmdk
+ filename: TEST_DIR/t-f005.IMGFMT
format: FLAT
[5]:
virtual size: 2147483648
- filename: TEST_DIR/t-f006.vmdk
+ filename: TEST_DIR/t-f006.IMGFMT
format: FLAT
[6]:
virtual size: 2147483648
- filename: TEST_DIR/t-f007.vmdk
+ filename: TEST_DIR/t-f007.IMGFMT
format: FLAT
[7]:
virtual size: 2147483648
- filename: TEST_DIR/t-f008.vmdk
+ filename: TEST_DIR/t-f008.IMGFMT
format: FLAT
[8]:
virtual size: 2147483648
- filename: TEST_DIR/t-f009.vmdk
+ filename: TEST_DIR/t-f009.IMGFMT
format: FLAT
[9]:
virtual size: 2147483648
- filename: TEST_DIR/t-f010.vmdk
+ filename: TEST_DIR/t-f010.IMGFMT
format: FLAT
[10]:
virtual size: 2147483648
- filename: TEST_DIR/t-f011.vmdk
+ filename: TEST_DIR/t-f011.IMGFMT
format: FLAT
[11]:
virtual size: 2147483648
- filename: TEST_DIR/t-f012.vmdk
+ filename: TEST_DIR/t-f012.IMGFMT
format: FLAT
[12]:
virtual size: 2147483648
- filename: TEST_DIR/t-f013.vmdk
+ filename: TEST_DIR/t-f013.IMGFMT
format: FLAT
[13]:
virtual size: 2147483648
- filename: TEST_DIR/t-f014.vmdk
+ filename: TEST_DIR/t-f014.IMGFMT
format: FLAT
[14]:
virtual size: 2147483648
- filename: TEST_DIR/t-f015.vmdk
+ filename: TEST_DIR/t-f015.IMGFMT
format: FLAT
[15]:
virtual size: 2147483648
- filename: TEST_DIR/t-f016.vmdk
+ filename: TEST_DIR/t-f016.IMGFMT
format: FLAT
[16]:
virtual size: 2147483648
- filename: TEST_DIR/t-f017.vmdk
+ filename: TEST_DIR/t-f017.IMGFMT
format: FLAT
[17]:
virtual size: 2147483648
- filename: TEST_DIR/t-f018.vmdk
+ filename: TEST_DIR/t-f018.IMGFMT
format: FLAT
[18]:
virtual size: 2147483648
- filename: TEST_DIR/t-f019.vmdk
+ filename: TEST_DIR/t-f019.IMGFMT
format: FLAT
[19]:
virtual size: 2147483648
- filename: TEST_DIR/t-f020.vmdk
+ filename: TEST_DIR/t-f020.IMGFMT
format: FLAT
[20]:
virtual size: 2147483648
- filename: TEST_DIR/t-f021.vmdk
+ filename: TEST_DIR/t-f021.IMGFMT
format: FLAT
[21]:
virtual size: 2147483648
- filename: TEST_DIR/t-f022.vmdk
+ filename: TEST_DIR/t-f022.IMGFMT
format: FLAT
[22]:
virtual size: 2147483648
- filename: TEST_DIR/t-f023.vmdk
+ filename: TEST_DIR/t-f023.IMGFMT
format: FLAT
[23]:
virtual size: 2147483648
- filename: TEST_DIR/t-f024.vmdk
+ filename: TEST_DIR/t-f024.IMGFMT
format: FLAT
[24]:
virtual size: 2147483648
- filename: TEST_DIR/t-f025.vmdk
+ filename: TEST_DIR/t-f025.IMGFMT
format: FLAT
[25]:
virtual size: 2147483648
- filename: TEST_DIR/t-f026.vmdk
+ filename: TEST_DIR/t-f026.IMGFMT
format: FLAT
[26]:
virtual size: 2147483648
- filename: TEST_DIR/t-f027.vmdk
+ filename: TEST_DIR/t-f027.IMGFMT
format: FLAT
[27]:
virtual size: 2147483648
- filename: TEST_DIR/t-f028.vmdk
+ filename: TEST_DIR/t-f028.IMGFMT
format: FLAT
[28]:
virtual size: 2147483648
- filename: TEST_DIR/t-f029.vmdk
+ filename: TEST_DIR/t-f029.IMGFMT
format: FLAT
[29]:
virtual size: 2147483648
- filename: TEST_DIR/t-f030.vmdk
+ filename: TEST_DIR/t-f030.IMGFMT
format: FLAT
[30]:
virtual size: 2147483648
- filename: TEST_DIR/t-f031.vmdk
+ filename: TEST_DIR/t-f031.IMGFMT
format: FLAT
[31]:
virtual size: 2147483648
- filename: TEST_DIR/t-f032.vmdk
+ filename: TEST_DIR/t-f032.IMGFMT
format: FLAT
[32]:
virtual size: 2147483648
- filename: TEST_DIR/t-f033.vmdk
+ filename: TEST_DIR/t-f033.IMGFMT
format: FLAT
[33]:
virtual size: 2147483648
- filename: TEST_DIR/t-f034.vmdk
+ filename: TEST_DIR/t-f034.IMGFMT
format: FLAT
[34]:
virtual size: 2147483648
- filename: TEST_DIR/t-f035.vmdk
+ filename: TEST_DIR/t-f035.IMGFMT
format: FLAT
[35]:
virtual size: 2147483648
- filename: TEST_DIR/t-f036.vmdk
+ filename: TEST_DIR/t-f036.IMGFMT
format: FLAT
[36]:
virtual size: 2147483648
- filename: TEST_DIR/t-f037.vmdk
+ filename: TEST_DIR/t-f037.IMGFMT
format: FLAT
[37]:
virtual size: 2147483648
- filename: TEST_DIR/t-f038.vmdk
+ filename: TEST_DIR/t-f038.IMGFMT
format: FLAT
[38]:
virtual size: 2147483648
- filename: TEST_DIR/t-f039.vmdk
+ filename: TEST_DIR/t-f039.IMGFMT
format: FLAT
[39]:
virtual size: 2147483648
- filename: TEST_DIR/t-f040.vmdk
+ filename: TEST_DIR/t-f040.IMGFMT
format: FLAT
[40]:
virtual size: 2147483648
- filename: TEST_DIR/t-f041.vmdk
+ filename: TEST_DIR/t-f041.IMGFMT
format: FLAT
[41]:
virtual size: 2147483648
- filename: TEST_DIR/t-f042.vmdk
+ filename: TEST_DIR/t-f042.IMGFMT
format: FLAT
[42]:
virtual size: 2147483648
- filename: TEST_DIR/t-f043.vmdk
+ filename: TEST_DIR/t-f043.IMGFMT
format: FLAT
[43]:
virtual size: 2147483648
- filename: TEST_DIR/t-f044.vmdk
+ filename: TEST_DIR/t-f044.IMGFMT
format: FLAT
[44]:
virtual size: 2147483648
- filename: TEST_DIR/t-f045.vmdk
+ filename: TEST_DIR/t-f045.IMGFMT
format: FLAT
[45]:
virtual size: 2147483648
- filename: TEST_DIR/t-f046.vmdk
+ filename: TEST_DIR/t-f046.IMGFMT
format: FLAT
[46]:
virtual size: 2147483648
- filename: TEST_DIR/t-f047.vmdk
+ filename: TEST_DIR/t-f047.IMGFMT
format: FLAT
[47]:
virtual size: 2147483648
- filename: TEST_DIR/t-f048.vmdk
+ filename: TEST_DIR/t-f048.IMGFMT
format: FLAT
[48]:
virtual size: 2147483648
- filename: TEST_DIR/t-f049.vmdk
+ filename: TEST_DIR/t-f049.IMGFMT
format: FLAT
[49]:
virtual size: 2147483648
- filename: TEST_DIR/t-f050.vmdk
+ filename: TEST_DIR/t-f050.IMGFMT
format: FLAT
[50]:
virtual size: 2147483648
- filename: TEST_DIR/t-f051.vmdk
+ filename: TEST_DIR/t-f051.IMGFMT
format: FLAT
[51]:
virtual size: 2147483648
- filename: TEST_DIR/t-f052.vmdk
+ filename: TEST_DIR/t-f052.IMGFMT
format: FLAT
[52]:
virtual size: 2147483648
- filename: TEST_DIR/t-f053.vmdk
+ filename: TEST_DIR/t-f053.IMGFMT
format: FLAT
[53]:
virtual size: 2147483648
- filename: TEST_DIR/t-f054.vmdk
+ filename: TEST_DIR/t-f054.IMGFMT
format: FLAT
[54]:
virtual size: 2147483648
- filename: TEST_DIR/t-f055.vmdk
+ filename: TEST_DIR/t-f055.IMGFMT
format: FLAT
[55]:
virtual size: 2147483648
- filename: TEST_DIR/t-f056.vmdk
+ filename: TEST_DIR/t-f056.IMGFMT
format: FLAT
[56]:
virtual size: 2147483648
- filename: TEST_DIR/t-f057.vmdk
+ filename: TEST_DIR/t-f057.IMGFMT
format: FLAT
[57]:
virtual size: 2147483648
- filename: TEST_DIR/t-f058.vmdk
+ filename: TEST_DIR/t-f058.IMGFMT
format: FLAT
[58]:
virtual size: 2147483648
- filename: TEST_DIR/t-f059.vmdk
+ filename: TEST_DIR/t-f059.IMGFMT
format: FLAT
[59]:
virtual size: 2147483648
- filename: TEST_DIR/t-f060.vmdk
+ filename: TEST_DIR/t-f060.IMGFMT
format: FLAT
[60]:
virtual size: 2147483648
- filename: TEST_DIR/t-f061.vmdk
+ filename: TEST_DIR/t-f061.IMGFMT
format: FLAT
[61]:
virtual size: 2147483648
- filename: TEST_DIR/t-f062.vmdk
+ filename: TEST_DIR/t-f062.IMGFMT
format: FLAT
[62]:
virtual size: 2147483648
- filename: TEST_DIR/t-f063.vmdk
+ filename: TEST_DIR/t-f063.IMGFMT
format: FLAT
[63]:
virtual size: 2147483648
- filename: TEST_DIR/t-f064.vmdk
+ filename: TEST_DIR/t-f064.IMGFMT
format: FLAT
[64]:
virtual size: 2147483648
- filename: TEST_DIR/t-f065.vmdk
+ filename: TEST_DIR/t-f065.IMGFMT
format: FLAT
[65]:
virtual size: 2147483648
- filename: TEST_DIR/t-f066.vmdk
+ filename: TEST_DIR/t-f066.IMGFMT
format: FLAT
[66]:
virtual size: 2147483648
- filename: TEST_DIR/t-f067.vmdk
+ filename: TEST_DIR/t-f067.IMGFMT
format: FLAT
[67]:
virtual size: 2147483648
- filename: TEST_DIR/t-f068.vmdk
+ filename: TEST_DIR/t-f068.IMGFMT
format: FLAT
[68]:
virtual size: 2147483648
- filename: TEST_DIR/t-f069.vmdk
+ filename: TEST_DIR/t-f069.IMGFMT
format: FLAT
[69]:
virtual size: 2147483648
- filename: TEST_DIR/t-f070.vmdk
+ filename: TEST_DIR/t-f070.IMGFMT
format: FLAT
[70]:
virtual size: 2147483648
- filename: TEST_DIR/t-f071.vmdk
+ filename: TEST_DIR/t-f071.IMGFMT
format: FLAT
[71]:
virtual size: 2147483648
- filename: TEST_DIR/t-f072.vmdk
+ filename: TEST_DIR/t-f072.IMGFMT
format: FLAT
[72]:
virtual size: 2147483648
- filename: TEST_DIR/t-f073.vmdk
+ filename: TEST_DIR/t-f073.IMGFMT
format: FLAT
[73]:
virtual size: 2147483648
- filename: TEST_DIR/t-f074.vmdk
+ filename: TEST_DIR/t-f074.IMGFMT
format: FLAT
[74]:
virtual size: 2147483648
- filename: TEST_DIR/t-f075.vmdk
+ filename: TEST_DIR/t-f075.IMGFMT
format: FLAT
[75]:
virtual size: 2147483648
- filename: TEST_DIR/t-f076.vmdk
+ filename: TEST_DIR/t-f076.IMGFMT
format: FLAT
[76]:
virtual size: 2147483648
- filename: TEST_DIR/t-f077.vmdk
+ filename: TEST_DIR/t-f077.IMGFMT
format: FLAT
[77]:
virtual size: 2147483648
- filename: TEST_DIR/t-f078.vmdk
+ filename: TEST_DIR/t-f078.IMGFMT
format: FLAT
[78]:
virtual size: 2147483648
- filename: TEST_DIR/t-f079.vmdk
+ filename: TEST_DIR/t-f079.IMGFMT
format: FLAT
[79]:
virtual size: 2147483648
- filename: TEST_DIR/t-f080.vmdk
+ filename: TEST_DIR/t-f080.IMGFMT
format: FLAT
[80]:
virtual size: 2147483648
- filename: TEST_DIR/t-f081.vmdk
+ filename: TEST_DIR/t-f081.IMGFMT
format: FLAT
[81]:
virtual size: 2147483648
- filename: TEST_DIR/t-f082.vmdk
+ filename: TEST_DIR/t-f082.IMGFMT
format: FLAT
[82]:
virtual size: 2147483648
- filename: TEST_DIR/t-f083.vmdk
+ filename: TEST_DIR/t-f083.IMGFMT
format: FLAT
[83]:
virtual size: 2147483648
- filename: TEST_DIR/t-f084.vmdk
+ filename: TEST_DIR/t-f084.IMGFMT
format: FLAT
[84]:
virtual size: 2147483648
- filename: TEST_DIR/t-f085.vmdk
+ filename: TEST_DIR/t-f085.IMGFMT
format: FLAT
[85]:
virtual size: 2147483648
- filename: TEST_DIR/t-f086.vmdk
+ filename: TEST_DIR/t-f086.IMGFMT
format: FLAT
[86]:
virtual size: 2147483648
- filename: TEST_DIR/t-f087.vmdk
+ filename: TEST_DIR/t-f087.IMGFMT
format: FLAT
[87]:
virtual size: 2147483648
- filename: TEST_DIR/t-f088.vmdk
+ filename: TEST_DIR/t-f088.IMGFMT
format: FLAT
[88]:
virtual size: 2147483648
- filename: TEST_DIR/t-f089.vmdk
+ filename: TEST_DIR/t-f089.IMGFMT
format: FLAT
[89]:
virtual size: 2147483648
- filename: TEST_DIR/t-f090.vmdk
+ filename: TEST_DIR/t-f090.IMGFMT
format: FLAT
[90]:
virtual size: 2147483648
- filename: TEST_DIR/t-f091.vmdk
+ filename: TEST_DIR/t-f091.IMGFMT
format: FLAT
[91]:
virtual size: 2147483648
- filename: TEST_DIR/t-f092.vmdk
+ filename: TEST_DIR/t-f092.IMGFMT
format: FLAT
[92]:
virtual size: 2147483648
- filename: TEST_DIR/t-f093.vmdk
+ filename: TEST_DIR/t-f093.IMGFMT
format: FLAT
[93]:
virtual size: 2147483648
- filename: TEST_DIR/t-f094.vmdk
+ filename: TEST_DIR/t-f094.IMGFMT
format: FLAT
[94]:
virtual size: 2147483648
- filename: TEST_DIR/t-f095.vmdk
+ filename: TEST_DIR/t-f095.IMGFMT
format: FLAT
[95]:
virtual size: 2147483648
- filename: TEST_DIR/t-f096.vmdk
+ filename: TEST_DIR/t-f096.IMGFMT
format: FLAT
[96]:
virtual size: 2147483648
- filename: TEST_DIR/t-f097.vmdk
+ filename: TEST_DIR/t-f097.IMGFMT
format: FLAT
[97]:
virtual size: 2147483648
- filename: TEST_DIR/t-f098.vmdk
+ filename: TEST_DIR/t-f098.IMGFMT
format: FLAT
[98]:
virtual size: 2147483648
- filename: TEST_DIR/t-f099.vmdk
+ filename: TEST_DIR/t-f099.IMGFMT
format: FLAT
[99]:
virtual size: 2147483648
- filename: TEST_DIR/t-f100.vmdk
+ filename: TEST_DIR/t-f100.IMGFMT
format: FLAT
[100]:
virtual size: 2147483648
- filename: TEST_DIR/t-f101.vmdk
+ filename: TEST_DIR/t-f101.IMGFMT
format: FLAT
[101]:
virtual size: 2147483648
- filename: TEST_DIR/t-f102.vmdk
+ filename: TEST_DIR/t-f102.IMGFMT
format: FLAT
[102]:
virtual size: 2147483648
- filename: TEST_DIR/t-f103.vmdk
+ filename: TEST_DIR/t-f103.IMGFMT
format: FLAT
[103]:
virtual size: 2147483648
- filename: TEST_DIR/t-f104.vmdk
+ filename: TEST_DIR/t-f104.IMGFMT
format: FLAT
[104]:
virtual size: 2147483648
- filename: TEST_DIR/t-f105.vmdk
+ filename: TEST_DIR/t-f105.IMGFMT
format: FLAT
[105]:
virtual size: 2147483648
- filename: TEST_DIR/t-f106.vmdk
+ filename: TEST_DIR/t-f106.IMGFMT
format: FLAT
[106]:
virtual size: 2147483648
- filename: TEST_DIR/t-f107.vmdk
+ filename: TEST_DIR/t-f107.IMGFMT
format: FLAT
[107]:
virtual size: 2147483648
- filename: TEST_DIR/t-f108.vmdk
+ filename: TEST_DIR/t-f108.IMGFMT
format: FLAT
[108]:
virtual size: 2147483648
- filename: TEST_DIR/t-f109.vmdk
+ filename: TEST_DIR/t-f109.IMGFMT
format: FLAT
[109]:
virtual size: 2147483648
- filename: TEST_DIR/t-f110.vmdk
+ filename: TEST_DIR/t-f110.IMGFMT
format: FLAT
[110]:
virtual size: 2147483648
- filename: TEST_DIR/t-f111.vmdk
+ filename: TEST_DIR/t-f111.IMGFMT
format: FLAT
[111]:
virtual size: 2147483648
- filename: TEST_DIR/t-f112.vmdk
+ filename: TEST_DIR/t-f112.IMGFMT
format: FLAT
[112]:
virtual size: 2147483648
- filename: TEST_DIR/t-f113.vmdk
+ filename: TEST_DIR/t-f113.IMGFMT
format: FLAT
[113]:
virtual size: 2147483648
- filename: TEST_DIR/t-f114.vmdk
+ filename: TEST_DIR/t-f114.IMGFMT
format: FLAT
[114]:
virtual size: 2147483648
- filename: TEST_DIR/t-f115.vmdk
+ filename: TEST_DIR/t-f115.IMGFMT
format: FLAT
[115]:
virtual size: 2147483648
- filename: TEST_DIR/t-f116.vmdk
+ filename: TEST_DIR/t-f116.IMGFMT
format: FLAT
[116]:
virtual size: 2147483648
- filename: TEST_DIR/t-f117.vmdk
+ filename: TEST_DIR/t-f117.IMGFMT
format: FLAT
[117]:
virtual size: 2147483648
- filename: TEST_DIR/t-f118.vmdk
+ filename: TEST_DIR/t-f118.IMGFMT
format: FLAT
[118]:
virtual size: 2147483648
- filename: TEST_DIR/t-f119.vmdk
+ filename: TEST_DIR/t-f119.IMGFMT
format: FLAT
[119]:
virtual size: 2147483648
- filename: TEST_DIR/t-f120.vmdk
+ filename: TEST_DIR/t-f120.IMGFMT
format: FLAT
[120]:
virtual size: 2147483648
- filename: TEST_DIR/t-f121.vmdk
+ filename: TEST_DIR/t-f121.IMGFMT
format: FLAT
[121]:
virtual size: 2147483648
- filename: TEST_DIR/t-f122.vmdk
+ filename: TEST_DIR/t-f122.IMGFMT
format: FLAT
[122]:
virtual size: 2147483648
- filename: TEST_DIR/t-f123.vmdk
+ filename: TEST_DIR/t-f123.IMGFMT
format: FLAT
[123]:
virtual size: 2147483648
- filename: TEST_DIR/t-f124.vmdk
+ filename: TEST_DIR/t-f124.IMGFMT
format: FLAT
[124]:
virtual size: 2147483648
- filename: TEST_DIR/t-f125.vmdk
+ filename: TEST_DIR/t-f125.IMGFMT
format: FLAT
[125]:
virtual size: 2147483648
- filename: TEST_DIR/t-f126.vmdk
+ filename: TEST_DIR/t-f126.IMGFMT
format: FLAT
[126]:
virtual size: 2147483648
- filename: TEST_DIR/t-f127.vmdk
+ filename: TEST_DIR/t-f127.IMGFMT
format: FLAT
[127]:
virtual size: 2147483648
- filename: TEST_DIR/t-f128.vmdk
+ filename: TEST_DIR/t-f128.IMGFMT
format: FLAT
[128]:
virtual size: 2147483648
- filename: TEST_DIR/t-f129.vmdk
+ filename: TEST_DIR/t-f129.IMGFMT
format: FLAT
[129]:
virtual size: 2147483648
- filename: TEST_DIR/t-f130.vmdk
+ filename: TEST_DIR/t-f130.IMGFMT
format: FLAT
[130]:
virtual size: 2147483648
- filename: TEST_DIR/t-f131.vmdk
+ filename: TEST_DIR/t-f131.IMGFMT
format: FLAT
[131]:
virtual size: 2147483648
- filename: TEST_DIR/t-f132.vmdk
+ filename: TEST_DIR/t-f132.IMGFMT
format: FLAT
[132]:
virtual size: 2147483648
- filename: TEST_DIR/t-f133.vmdk
+ filename: TEST_DIR/t-f133.IMGFMT
format: FLAT
[133]:
virtual size: 2147483648
- filename: TEST_DIR/t-f134.vmdk
+ filename: TEST_DIR/t-f134.IMGFMT
format: FLAT
[134]:
virtual size: 2147483648
- filename: TEST_DIR/t-f135.vmdk
+ filename: TEST_DIR/t-f135.IMGFMT
format: FLAT
[135]:
virtual size: 2147483648
- filename: TEST_DIR/t-f136.vmdk
+ filename: TEST_DIR/t-f136.IMGFMT
format: FLAT
[136]:
virtual size: 2147483648
- filename: TEST_DIR/t-f137.vmdk
+ filename: TEST_DIR/t-f137.IMGFMT
format: FLAT
[137]:
virtual size: 2147483648
- filename: TEST_DIR/t-f138.vmdk
+ filename: TEST_DIR/t-f138.IMGFMT
format: FLAT
[138]:
virtual size: 2147483648
- filename: TEST_DIR/t-f139.vmdk
+ filename: TEST_DIR/t-f139.IMGFMT
format: FLAT
[139]:
virtual size: 2147483648
- filename: TEST_DIR/t-f140.vmdk
+ filename: TEST_DIR/t-f140.IMGFMT
format: FLAT
[140]:
virtual size: 2147483648
- filename: TEST_DIR/t-f141.vmdk
+ filename: TEST_DIR/t-f141.IMGFMT
format: FLAT
[141]:
virtual size: 2147483648
- filename: TEST_DIR/t-f142.vmdk
+ filename: TEST_DIR/t-f142.IMGFMT
format: FLAT
[142]:
virtual size: 2147483648
- filename: TEST_DIR/t-f143.vmdk
+ filename: TEST_DIR/t-f143.IMGFMT
format: FLAT
[143]:
virtual size: 2147483648
- filename: TEST_DIR/t-f144.vmdk
+ filename: TEST_DIR/t-f144.IMGFMT
format: FLAT
[144]:
virtual size: 2147483648
- filename: TEST_DIR/t-f145.vmdk
+ filename: TEST_DIR/t-f145.IMGFMT
format: FLAT
[145]:
virtual size: 2147483648
- filename: TEST_DIR/t-f146.vmdk
+ filename: TEST_DIR/t-f146.IMGFMT
format: FLAT
[146]:
virtual size: 2147483648
- filename: TEST_DIR/t-f147.vmdk
+ filename: TEST_DIR/t-f147.IMGFMT
format: FLAT
[147]:
virtual size: 2147483648
- filename: TEST_DIR/t-f148.vmdk
+ filename: TEST_DIR/t-f148.IMGFMT
format: FLAT
[148]:
virtual size: 2147483648
- filename: TEST_DIR/t-f149.vmdk
+ filename: TEST_DIR/t-f149.IMGFMT
format: FLAT
[149]:
virtual size: 2147483648
- filename: TEST_DIR/t-f150.vmdk
+ filename: TEST_DIR/t-f150.IMGFMT
format: FLAT
[150]:
virtual size: 2147483648
- filename: TEST_DIR/t-f151.vmdk
+ filename: TEST_DIR/t-f151.IMGFMT
format: FLAT
[151]:
virtual size: 2147483648
- filename: TEST_DIR/t-f152.vmdk
+ filename: TEST_DIR/t-f152.IMGFMT
format: FLAT
[152]:
virtual size: 2147483648
- filename: TEST_DIR/t-f153.vmdk
+ filename: TEST_DIR/t-f153.IMGFMT
format: FLAT
[153]:
virtual size: 2147483648
- filename: TEST_DIR/t-f154.vmdk
+ filename: TEST_DIR/t-f154.IMGFMT
format: FLAT
[154]:
virtual size: 2147483648
- filename: TEST_DIR/t-f155.vmdk
+ filename: TEST_DIR/t-f155.IMGFMT
format: FLAT
[155]:
virtual size: 2147483648
- filename: TEST_DIR/t-f156.vmdk
+ filename: TEST_DIR/t-f156.IMGFMT
format: FLAT
[156]:
virtual size: 2147483648
- filename: TEST_DIR/t-f157.vmdk
+ filename: TEST_DIR/t-f157.IMGFMT
format: FLAT
[157]:
virtual size: 2147483648
- filename: TEST_DIR/t-f158.vmdk
+ filename: TEST_DIR/t-f158.IMGFMT
format: FLAT
[158]:
virtual size: 2147483648
- filename: TEST_DIR/t-f159.vmdk
+ filename: TEST_DIR/t-f159.IMGFMT
format: FLAT
[159]:
virtual size: 2147483648
- filename: TEST_DIR/t-f160.vmdk
+ filename: TEST_DIR/t-f160.IMGFMT
format: FLAT
[160]:
virtual size: 2147483648
- filename: TEST_DIR/t-f161.vmdk
+ filename: TEST_DIR/t-f161.IMGFMT
format: FLAT
[161]:
virtual size: 2147483648
- filename: TEST_DIR/t-f162.vmdk
+ filename: TEST_DIR/t-f162.IMGFMT
format: FLAT
[162]:
virtual size: 2147483648
- filename: TEST_DIR/t-f163.vmdk
+ filename: TEST_DIR/t-f163.IMGFMT
format: FLAT
[163]:
virtual size: 2147483648
- filename: TEST_DIR/t-f164.vmdk
+ filename: TEST_DIR/t-f164.IMGFMT
format: FLAT
[164]:
virtual size: 2147483648
- filename: TEST_DIR/t-f165.vmdk
+ filename: TEST_DIR/t-f165.IMGFMT
format: FLAT
[165]:
virtual size: 2147483648
- filename: TEST_DIR/t-f166.vmdk
+ filename: TEST_DIR/t-f166.IMGFMT
format: FLAT
[166]:
virtual size: 2147483648
- filename: TEST_DIR/t-f167.vmdk
+ filename: TEST_DIR/t-f167.IMGFMT
format: FLAT
[167]:
virtual size: 2147483648
- filename: TEST_DIR/t-f168.vmdk
+ filename: TEST_DIR/t-f168.IMGFMT
format: FLAT
[168]:
virtual size: 2147483648
- filename: TEST_DIR/t-f169.vmdk
+ filename: TEST_DIR/t-f169.IMGFMT
format: FLAT
[169]:
virtual size: 2147483648
- filename: TEST_DIR/t-f170.vmdk
+ filename: TEST_DIR/t-f170.IMGFMT
format: FLAT
[170]:
virtual size: 2147483648
- filename: TEST_DIR/t-f171.vmdk
+ filename: TEST_DIR/t-f171.IMGFMT
format: FLAT
[171]:
virtual size: 2147483648
- filename: TEST_DIR/t-f172.vmdk
+ filename: TEST_DIR/t-f172.IMGFMT
format: FLAT
[172]:
virtual size: 2147483648
- filename: TEST_DIR/t-f173.vmdk
+ filename: TEST_DIR/t-f173.IMGFMT
format: FLAT
[173]:
virtual size: 2147483648
- filename: TEST_DIR/t-f174.vmdk
+ filename: TEST_DIR/t-f174.IMGFMT
format: FLAT
[174]:
virtual size: 2147483648
- filename: TEST_DIR/t-f175.vmdk
+ filename: TEST_DIR/t-f175.IMGFMT
format: FLAT
[175]:
virtual size: 2147483648
- filename: TEST_DIR/t-f176.vmdk
+ filename: TEST_DIR/t-f176.IMGFMT
format: FLAT
[176]:
virtual size: 2147483648
- filename: TEST_DIR/t-f177.vmdk
+ filename: TEST_DIR/t-f177.IMGFMT
format: FLAT
[177]:
virtual size: 2147483648
- filename: TEST_DIR/t-f178.vmdk
+ filename: TEST_DIR/t-f178.IMGFMT
format: FLAT
[178]:
virtual size: 2147483648
- filename: TEST_DIR/t-f179.vmdk
+ filename: TEST_DIR/t-f179.IMGFMT
format: FLAT
[179]:
virtual size: 2147483648
- filename: TEST_DIR/t-f180.vmdk
+ filename: TEST_DIR/t-f180.IMGFMT
format: FLAT
[180]:
virtual size: 2147483648
- filename: TEST_DIR/t-f181.vmdk
+ filename: TEST_DIR/t-f181.IMGFMT
format: FLAT
[181]:
virtual size: 2147483648
- filename: TEST_DIR/t-f182.vmdk
+ filename: TEST_DIR/t-f182.IMGFMT
format: FLAT
[182]:
virtual size: 2147483648
- filename: TEST_DIR/t-f183.vmdk
+ filename: TEST_DIR/t-f183.IMGFMT
format: FLAT
[183]:
virtual size: 2147483648
- filename: TEST_DIR/t-f184.vmdk
+ filename: TEST_DIR/t-f184.IMGFMT
format: FLAT
[184]:
virtual size: 2147483648
- filename: TEST_DIR/t-f185.vmdk
+ filename: TEST_DIR/t-f185.IMGFMT
format: FLAT
[185]:
virtual size: 2147483648
- filename: TEST_DIR/t-f186.vmdk
+ filename: TEST_DIR/t-f186.IMGFMT
format: FLAT
[186]:
virtual size: 2147483648
- filename: TEST_DIR/t-f187.vmdk
+ filename: TEST_DIR/t-f187.IMGFMT
format: FLAT
[187]:
virtual size: 2147483648
- filename: TEST_DIR/t-f188.vmdk
+ filename: TEST_DIR/t-f188.IMGFMT
format: FLAT
[188]:
virtual size: 2147483648
- filename: TEST_DIR/t-f189.vmdk
+ filename: TEST_DIR/t-f189.IMGFMT
format: FLAT
[189]:
virtual size: 2147483648
- filename: TEST_DIR/t-f190.vmdk
+ filename: TEST_DIR/t-f190.IMGFMT
format: FLAT
[190]:
virtual size: 2147483648
- filename: TEST_DIR/t-f191.vmdk
+ filename: TEST_DIR/t-f191.IMGFMT
format: FLAT
[191]:
virtual size: 2147483648
- filename: TEST_DIR/t-f192.vmdk
+ filename: TEST_DIR/t-f192.IMGFMT
format: FLAT
[192]:
virtual size: 2147483648
- filename: TEST_DIR/t-f193.vmdk
+ filename: TEST_DIR/t-f193.IMGFMT
format: FLAT
[193]:
virtual size: 2147483648
- filename: TEST_DIR/t-f194.vmdk
+ filename: TEST_DIR/t-f194.IMGFMT
format: FLAT
[194]:
virtual size: 2147483648
- filename: TEST_DIR/t-f195.vmdk
+ filename: TEST_DIR/t-f195.IMGFMT
format: FLAT
[195]:
virtual size: 2147483648
- filename: TEST_DIR/t-f196.vmdk
+ filename: TEST_DIR/t-f196.IMGFMT
format: FLAT
[196]:
virtual size: 2147483648
- filename: TEST_DIR/t-f197.vmdk
+ filename: TEST_DIR/t-f197.IMGFMT
format: FLAT
[197]:
virtual size: 2147483648
- filename: TEST_DIR/t-f198.vmdk
+ filename: TEST_DIR/t-f198.IMGFMT
format: FLAT
[198]:
virtual size: 2147483648
- filename: TEST_DIR/t-f199.vmdk
+ filename: TEST_DIR/t-f199.IMGFMT
format: FLAT
[199]:
virtual size: 2147483648
- filename: TEST_DIR/t-f200.vmdk
+ filename: TEST_DIR/t-f200.IMGFMT
format: FLAT
[200]:
virtual size: 2147483648
- filename: TEST_DIR/t-f201.vmdk
+ filename: TEST_DIR/t-f201.IMGFMT
format: FLAT
[201]:
virtual size: 2147483648
- filename: TEST_DIR/t-f202.vmdk
+ filename: TEST_DIR/t-f202.IMGFMT
format: FLAT
[202]:
virtual size: 2147483648
- filename: TEST_DIR/t-f203.vmdk
+ filename: TEST_DIR/t-f203.IMGFMT
format: FLAT
[203]:
virtual size: 2147483648
- filename: TEST_DIR/t-f204.vmdk
+ filename: TEST_DIR/t-f204.IMGFMT
format: FLAT
[204]:
virtual size: 2147483648
- filename: TEST_DIR/t-f205.vmdk
+ filename: TEST_DIR/t-f205.IMGFMT
format: FLAT
[205]:
virtual size: 2147483648
- filename: TEST_DIR/t-f206.vmdk
+ filename: TEST_DIR/t-f206.IMGFMT
format: FLAT
[206]:
virtual size: 2147483648
- filename: TEST_DIR/t-f207.vmdk
+ filename: TEST_DIR/t-f207.IMGFMT
format: FLAT
[207]:
virtual size: 2147483648
- filename: TEST_DIR/t-f208.vmdk
+ filename: TEST_DIR/t-f208.IMGFMT
format: FLAT
[208]:
virtual size: 2147483648
- filename: TEST_DIR/t-f209.vmdk
+ filename: TEST_DIR/t-f209.IMGFMT
format: FLAT
[209]:
virtual size: 2147483648
- filename: TEST_DIR/t-f210.vmdk
+ filename: TEST_DIR/t-f210.IMGFMT
format: FLAT
[210]:
virtual size: 2147483648
- filename: TEST_DIR/t-f211.vmdk
+ filename: TEST_DIR/t-f211.IMGFMT
format: FLAT
[211]:
virtual size: 2147483648
- filename: TEST_DIR/t-f212.vmdk
+ filename: TEST_DIR/t-f212.IMGFMT
format: FLAT
[212]:
virtual size: 2147483648
- filename: TEST_DIR/t-f213.vmdk
+ filename: TEST_DIR/t-f213.IMGFMT
format: FLAT
[213]:
virtual size: 2147483648
- filename: TEST_DIR/t-f214.vmdk
+ filename: TEST_DIR/t-f214.IMGFMT
format: FLAT
[214]:
virtual size: 2147483648
- filename: TEST_DIR/t-f215.vmdk
+ filename: TEST_DIR/t-f215.IMGFMT
format: FLAT
[215]:
virtual size: 2147483648
- filename: TEST_DIR/t-f216.vmdk
+ filename: TEST_DIR/t-f216.IMGFMT
format: FLAT
[216]:
virtual size: 2147483648
- filename: TEST_DIR/t-f217.vmdk
+ filename: TEST_DIR/t-f217.IMGFMT
format: FLAT
[217]:
virtual size: 2147483648
- filename: TEST_DIR/t-f218.vmdk
+ filename: TEST_DIR/t-f218.IMGFMT
format: FLAT
[218]:
virtual size: 2147483648
- filename: TEST_DIR/t-f219.vmdk
+ filename: TEST_DIR/t-f219.IMGFMT
format: FLAT
[219]:
virtual size: 2147483648
- filename: TEST_DIR/t-f220.vmdk
+ filename: TEST_DIR/t-f220.IMGFMT
format: FLAT
[220]:
virtual size: 2147483648
- filename: TEST_DIR/t-f221.vmdk
+ filename: TEST_DIR/t-f221.IMGFMT
format: FLAT
[221]:
virtual size: 2147483648
- filename: TEST_DIR/t-f222.vmdk
+ filename: TEST_DIR/t-f222.IMGFMT
format: FLAT
[222]:
virtual size: 2147483648
- filename: TEST_DIR/t-f223.vmdk
+ filename: TEST_DIR/t-f223.IMGFMT
format: FLAT
[223]:
virtual size: 2147483648
- filename: TEST_DIR/t-f224.vmdk
+ filename: TEST_DIR/t-f224.IMGFMT
format: FLAT
[224]:
virtual size: 2147483648
- filename: TEST_DIR/t-f225.vmdk
+ filename: TEST_DIR/t-f225.IMGFMT
format: FLAT
[225]:
virtual size: 2147483648
- filename: TEST_DIR/t-f226.vmdk
+ filename: TEST_DIR/t-f226.IMGFMT
format: FLAT
[226]:
virtual size: 2147483648
- filename: TEST_DIR/t-f227.vmdk
+ filename: TEST_DIR/t-f227.IMGFMT
format: FLAT
[227]:
virtual size: 2147483648
- filename: TEST_DIR/t-f228.vmdk
+ filename: TEST_DIR/t-f228.IMGFMT
format: FLAT
[228]:
virtual size: 2147483648
- filename: TEST_DIR/t-f229.vmdk
+ filename: TEST_DIR/t-f229.IMGFMT
format: FLAT
[229]:
virtual size: 2147483648
- filename: TEST_DIR/t-f230.vmdk
+ filename: TEST_DIR/t-f230.IMGFMT
format: FLAT
[230]:
virtual size: 2147483648
- filename: TEST_DIR/t-f231.vmdk
+ filename: TEST_DIR/t-f231.IMGFMT
format: FLAT
[231]:
virtual size: 2147483648
- filename: TEST_DIR/t-f232.vmdk
+ filename: TEST_DIR/t-f232.IMGFMT
format: FLAT
[232]:
virtual size: 2147483648
- filename: TEST_DIR/t-f233.vmdk
+ filename: TEST_DIR/t-f233.IMGFMT
format: FLAT
[233]:
virtual size: 2147483648
- filename: TEST_DIR/t-f234.vmdk
+ filename: TEST_DIR/t-f234.IMGFMT
format: FLAT
[234]:
virtual size: 2147483648
- filename: TEST_DIR/t-f235.vmdk
+ filename: TEST_DIR/t-f235.IMGFMT
format: FLAT
[235]:
virtual size: 2147483648
- filename: TEST_DIR/t-f236.vmdk
+ filename: TEST_DIR/t-f236.IMGFMT
format: FLAT
[236]:
virtual size: 2147483648
- filename: TEST_DIR/t-f237.vmdk
+ filename: TEST_DIR/t-f237.IMGFMT
format: FLAT
[237]:
virtual size: 2147483648
- filename: TEST_DIR/t-f238.vmdk
+ filename: TEST_DIR/t-f238.IMGFMT
format: FLAT
[238]:
virtual size: 2147483648
- filename: TEST_DIR/t-f239.vmdk
+ filename: TEST_DIR/t-f239.IMGFMT
format: FLAT
[239]:
virtual size: 2147483648
- filename: TEST_DIR/t-f240.vmdk
+ filename: TEST_DIR/t-f240.IMGFMT
format: FLAT
[240]:
virtual size: 2147483648
- filename: TEST_DIR/t-f241.vmdk
+ filename: TEST_DIR/t-f241.IMGFMT
format: FLAT
[241]:
virtual size: 2147483648
- filename: TEST_DIR/t-f242.vmdk
+ filename: TEST_DIR/t-f242.IMGFMT
format: FLAT
[242]:
virtual size: 2147483648
- filename: TEST_DIR/t-f243.vmdk
+ filename: TEST_DIR/t-f243.IMGFMT
format: FLAT
[243]:
virtual size: 2147483648
- filename: TEST_DIR/t-f244.vmdk
+ filename: TEST_DIR/t-f244.IMGFMT
format: FLAT
[244]:
virtual size: 2147483648
- filename: TEST_DIR/t-f245.vmdk
+ filename: TEST_DIR/t-f245.IMGFMT
format: FLAT
[245]:
virtual size: 2147483648
- filename: TEST_DIR/t-f246.vmdk
+ filename: TEST_DIR/t-f246.IMGFMT
format: FLAT
[246]:
virtual size: 2147483648
- filename: TEST_DIR/t-f247.vmdk
+ filename: TEST_DIR/t-f247.IMGFMT
format: FLAT
[247]:
virtual size: 2147483648
- filename: TEST_DIR/t-f248.vmdk
+ filename: TEST_DIR/t-f248.IMGFMT
format: FLAT
[248]:
virtual size: 2147483648
- filename: TEST_DIR/t-f249.vmdk
+ filename: TEST_DIR/t-f249.IMGFMT
format: FLAT
[249]:
virtual size: 2147483648
- filename: TEST_DIR/t-f250.vmdk
+ filename: TEST_DIR/t-f250.IMGFMT
format: FLAT
[250]:
virtual size: 2147483648
- filename: TEST_DIR/t-f251.vmdk
+ filename: TEST_DIR/t-f251.IMGFMT
format: FLAT
[251]:
virtual size: 2147483648
- filename: TEST_DIR/t-f252.vmdk
+ filename: TEST_DIR/t-f252.IMGFMT
format: FLAT
[252]:
virtual size: 2147483648
- filename: TEST_DIR/t-f253.vmdk
+ filename: TEST_DIR/t-f253.IMGFMT
format: FLAT
[253]:
virtual size: 2147483648
- filename: TEST_DIR/t-f254.vmdk
+ filename: TEST_DIR/t-f254.IMGFMT
format: FLAT
[254]:
virtual size: 2147483648
- filename: TEST_DIR/t-f255.vmdk
+ filename: TEST_DIR/t-f255.IMGFMT
format: FLAT
[255]:
virtual size: 2147483648
- filename: TEST_DIR/t-f256.vmdk
+ filename: TEST_DIR/t-f256.IMGFMT
format: FLAT
[256]:
virtual size: 2147483648
- filename: TEST_DIR/t-f257.vmdk
+ filename: TEST_DIR/t-f257.IMGFMT
format: FLAT
[257]:
virtual size: 2147483648
- filename: TEST_DIR/t-f258.vmdk
+ filename: TEST_DIR/t-f258.IMGFMT
format: FLAT
[258]:
virtual size: 2147483648
- filename: TEST_DIR/t-f259.vmdk
+ filename: TEST_DIR/t-f259.IMGFMT
format: FLAT
[259]:
virtual size: 2147483648
- filename: TEST_DIR/t-f260.vmdk
+ filename: TEST_DIR/t-f260.IMGFMT
format: FLAT
[260]:
virtual size: 2147483648
- filename: TEST_DIR/t-f261.vmdk
+ filename: TEST_DIR/t-f261.IMGFMT
format: FLAT
[261]:
virtual size: 2147483648
- filename: TEST_DIR/t-f262.vmdk
+ filename: TEST_DIR/t-f262.IMGFMT
format: FLAT
[262]:
virtual size: 2147483648
- filename: TEST_DIR/t-f263.vmdk
+ filename: TEST_DIR/t-f263.IMGFMT
format: FLAT
[263]:
virtual size: 2147483648
- filename: TEST_DIR/t-f264.vmdk
+ filename: TEST_DIR/t-f264.IMGFMT
format: FLAT
[264]:
virtual size: 2147483648
- filename: TEST_DIR/t-f265.vmdk
+ filename: TEST_DIR/t-f265.IMGFMT
format: FLAT
[265]:
virtual size: 2147483648
- filename: TEST_DIR/t-f266.vmdk
+ filename: TEST_DIR/t-f266.IMGFMT
format: FLAT
[266]:
virtual size: 2147483648
- filename: TEST_DIR/t-f267.vmdk
+ filename: TEST_DIR/t-f267.IMGFMT
format: FLAT
[267]:
virtual size: 2147483648
- filename: TEST_DIR/t-f268.vmdk
+ filename: TEST_DIR/t-f268.IMGFMT
format: FLAT
[268]:
virtual size: 2147483648
- filename: TEST_DIR/t-f269.vmdk
+ filename: TEST_DIR/t-f269.IMGFMT
format: FLAT
[269]:
virtual size: 2147483648
- filename: TEST_DIR/t-f270.vmdk
+ filename: TEST_DIR/t-f270.IMGFMT
format: FLAT
[270]:
virtual size: 2147483648
- filename: TEST_DIR/t-f271.vmdk
+ filename: TEST_DIR/t-f271.IMGFMT
format: FLAT
[271]:
virtual size: 2147483648
- filename: TEST_DIR/t-f272.vmdk
+ filename: TEST_DIR/t-f272.IMGFMT
format: FLAT
[272]:
virtual size: 2147483648
- filename: TEST_DIR/t-f273.vmdk
+ filename: TEST_DIR/t-f273.IMGFMT
format: FLAT
[273]:
virtual size: 2147483648
- filename: TEST_DIR/t-f274.vmdk
+ filename: TEST_DIR/t-f274.IMGFMT
format: FLAT
[274]:
virtual size: 2147483648
- filename: TEST_DIR/t-f275.vmdk
+ filename: TEST_DIR/t-f275.IMGFMT
format: FLAT
[275]:
virtual size: 2147483648
- filename: TEST_DIR/t-f276.vmdk
+ filename: TEST_DIR/t-f276.IMGFMT
format: FLAT
[276]:
virtual size: 2147483648
- filename: TEST_DIR/t-f277.vmdk
+ filename: TEST_DIR/t-f277.IMGFMT
format: FLAT
[277]:
virtual size: 2147483648
- filename: TEST_DIR/t-f278.vmdk
+ filename: TEST_DIR/t-f278.IMGFMT
format: FLAT
[278]:
virtual size: 2147483648
- filename: TEST_DIR/t-f279.vmdk
+ filename: TEST_DIR/t-f279.IMGFMT
format: FLAT
[279]:
virtual size: 2147483648
- filename: TEST_DIR/t-f280.vmdk
+ filename: TEST_DIR/t-f280.IMGFMT
format: FLAT
[280]:
virtual size: 2147483648
- filename: TEST_DIR/t-f281.vmdk
+ filename: TEST_DIR/t-f281.IMGFMT
format: FLAT
[281]:
virtual size: 2147483648
- filename: TEST_DIR/t-f282.vmdk
+ filename: TEST_DIR/t-f282.IMGFMT
format: FLAT
[282]:
virtual size: 2147483648
- filename: TEST_DIR/t-f283.vmdk
+ filename: TEST_DIR/t-f283.IMGFMT
format: FLAT
[283]:
virtual size: 2147483648
- filename: TEST_DIR/t-f284.vmdk
+ filename: TEST_DIR/t-f284.IMGFMT
format: FLAT
[284]:
virtual size: 2147483648
- filename: TEST_DIR/t-f285.vmdk
+ filename: TEST_DIR/t-f285.IMGFMT
format: FLAT
[285]:
virtual size: 2147483648
- filename: TEST_DIR/t-f286.vmdk
+ filename: TEST_DIR/t-f286.IMGFMT
format: FLAT
[286]:
virtual size: 2147483648
- filename: TEST_DIR/t-f287.vmdk
+ filename: TEST_DIR/t-f287.IMGFMT
format: FLAT
[287]:
virtual size: 2147483648
- filename: TEST_DIR/t-f288.vmdk
+ filename: TEST_DIR/t-f288.IMGFMT
format: FLAT
[288]:
virtual size: 2147483648
- filename: TEST_DIR/t-f289.vmdk
+ filename: TEST_DIR/t-f289.IMGFMT
format: FLAT
[289]:
virtual size: 2147483648
- filename: TEST_DIR/t-f290.vmdk
+ filename: TEST_DIR/t-f290.IMGFMT
format: FLAT
[290]:
virtual size: 2147483648
- filename: TEST_DIR/t-f291.vmdk
+ filename: TEST_DIR/t-f291.IMGFMT
format: FLAT
[291]:
virtual size: 2147483648
- filename: TEST_DIR/t-f292.vmdk
+ filename: TEST_DIR/t-f292.IMGFMT
format: FLAT
[292]:
virtual size: 2147483648
- filename: TEST_DIR/t-f293.vmdk
+ filename: TEST_DIR/t-f293.IMGFMT
format: FLAT
[293]:
virtual size: 2147483648
- filename: TEST_DIR/t-f294.vmdk
+ filename: TEST_DIR/t-f294.IMGFMT
format: FLAT
[294]:
virtual size: 2147483648
- filename: TEST_DIR/t-f295.vmdk
+ filename: TEST_DIR/t-f295.IMGFMT
format: FLAT
[295]:
virtual size: 2147483648
- filename: TEST_DIR/t-f296.vmdk
+ filename: TEST_DIR/t-f296.IMGFMT
format: FLAT
[296]:
virtual size: 2147483648
- filename: TEST_DIR/t-f297.vmdk
+ filename: TEST_DIR/t-f297.IMGFMT
format: FLAT
[297]:
virtual size: 2147483648
- filename: TEST_DIR/t-f298.vmdk
+ filename: TEST_DIR/t-f298.IMGFMT
format: FLAT
[298]:
virtual size: 2147483648
- filename: TEST_DIR/t-f299.vmdk
+ filename: TEST_DIR/t-f299.IMGFMT
format: FLAT
[299]:
virtual size: 2147483648
- filename: TEST_DIR/t-f300.vmdk
+ filename: TEST_DIR/t-f300.IMGFMT
format: FLAT
[300]:
virtual size: 2147483648
- filename: TEST_DIR/t-f301.vmdk
+ filename: TEST_DIR/t-f301.IMGFMT
format: FLAT
[301]:
virtual size: 2147483648
- filename: TEST_DIR/t-f302.vmdk
+ filename: TEST_DIR/t-f302.IMGFMT
format: FLAT
[302]:
virtual size: 2147483648
- filename: TEST_DIR/t-f303.vmdk
+ filename: TEST_DIR/t-f303.IMGFMT
format: FLAT
[303]:
virtual size: 2147483648
- filename: TEST_DIR/t-f304.vmdk
+ filename: TEST_DIR/t-f304.IMGFMT
format: FLAT
[304]:
virtual size: 2147483648
- filename: TEST_DIR/t-f305.vmdk
+ filename: TEST_DIR/t-f305.IMGFMT
format: FLAT
[305]:
virtual size: 2147483648
- filename: TEST_DIR/t-f306.vmdk
+ filename: TEST_DIR/t-f306.IMGFMT
format: FLAT
[306]:
virtual size: 2147483648
- filename: TEST_DIR/t-f307.vmdk
+ filename: TEST_DIR/t-f307.IMGFMT
format: FLAT
[307]:
virtual size: 2147483648
- filename: TEST_DIR/t-f308.vmdk
+ filename: TEST_DIR/t-f308.IMGFMT
format: FLAT
[308]:
virtual size: 2147483648
- filename: TEST_DIR/t-f309.vmdk
+ filename: TEST_DIR/t-f309.IMGFMT
format: FLAT
[309]:
virtual size: 2147483648
- filename: TEST_DIR/t-f310.vmdk
+ filename: TEST_DIR/t-f310.IMGFMT
format: FLAT
[310]:
virtual size: 2147483648
- filename: TEST_DIR/t-f311.vmdk
+ filename: TEST_DIR/t-f311.IMGFMT
format: FLAT
[311]:
virtual size: 2147483648
- filename: TEST_DIR/t-f312.vmdk
+ filename: TEST_DIR/t-f312.IMGFMT
format: FLAT
[312]:
virtual size: 2147483648
- filename: TEST_DIR/t-f313.vmdk
+ filename: TEST_DIR/t-f313.IMGFMT
format: FLAT
[313]:
virtual size: 2147483648
- filename: TEST_DIR/t-f314.vmdk
+ filename: TEST_DIR/t-f314.IMGFMT
format: FLAT
[314]:
virtual size: 2147483648
- filename: TEST_DIR/t-f315.vmdk
+ filename: TEST_DIR/t-f315.IMGFMT
format: FLAT
[315]:
virtual size: 2147483648
- filename: TEST_DIR/t-f316.vmdk
+ filename: TEST_DIR/t-f316.IMGFMT
format: FLAT
[316]:
virtual size: 2147483648
- filename: TEST_DIR/t-f317.vmdk
+ filename: TEST_DIR/t-f317.IMGFMT
format: FLAT
[317]:
virtual size: 2147483648
- filename: TEST_DIR/t-f318.vmdk
+ filename: TEST_DIR/t-f318.IMGFMT
format: FLAT
[318]:
virtual size: 2147483648
- filename: TEST_DIR/t-f319.vmdk
+ filename: TEST_DIR/t-f319.IMGFMT
format: FLAT
[319]:
virtual size: 2147483648
- filename: TEST_DIR/t-f320.vmdk
+ filename: TEST_DIR/t-f320.IMGFMT
format: FLAT
[320]:
virtual size: 2147483648
- filename: TEST_DIR/t-f321.vmdk
+ filename: TEST_DIR/t-f321.IMGFMT
format: FLAT
[321]:
virtual size: 2147483648
- filename: TEST_DIR/t-f322.vmdk
+ filename: TEST_DIR/t-f322.IMGFMT
format: FLAT
[322]:
virtual size: 2147483648
- filename: TEST_DIR/t-f323.vmdk
+ filename: TEST_DIR/t-f323.IMGFMT
format: FLAT
[323]:
virtual size: 2147483648
- filename: TEST_DIR/t-f324.vmdk
+ filename: TEST_DIR/t-f324.IMGFMT
format: FLAT
[324]:
virtual size: 2147483648
- filename: TEST_DIR/t-f325.vmdk
+ filename: TEST_DIR/t-f325.IMGFMT
format: FLAT
[325]:
virtual size: 2147483648
- filename: TEST_DIR/t-f326.vmdk
+ filename: TEST_DIR/t-f326.IMGFMT
format: FLAT
[326]:
virtual size: 2147483648
- filename: TEST_DIR/t-f327.vmdk
+ filename: TEST_DIR/t-f327.IMGFMT
format: FLAT
[327]:
virtual size: 2147483648
- filename: TEST_DIR/t-f328.vmdk
+ filename: TEST_DIR/t-f328.IMGFMT
format: FLAT
[328]:
virtual size: 2147483648
- filename: TEST_DIR/t-f329.vmdk
+ filename: TEST_DIR/t-f329.IMGFMT
format: FLAT
[329]:
virtual size: 2147483648
- filename: TEST_DIR/t-f330.vmdk
+ filename: TEST_DIR/t-f330.IMGFMT
format: FLAT
[330]:
virtual size: 2147483648
- filename: TEST_DIR/t-f331.vmdk
+ filename: TEST_DIR/t-f331.IMGFMT
format: FLAT
[331]:
virtual size: 2147483648
- filename: TEST_DIR/t-f332.vmdk
+ filename: TEST_DIR/t-f332.IMGFMT
format: FLAT
[332]:
virtual size: 2147483648
- filename: TEST_DIR/t-f333.vmdk
+ filename: TEST_DIR/t-f333.IMGFMT
format: FLAT
[333]:
virtual size: 2147483648
- filename: TEST_DIR/t-f334.vmdk
+ filename: TEST_DIR/t-f334.IMGFMT
format: FLAT
[334]:
virtual size: 2147483648
- filename: TEST_DIR/t-f335.vmdk
+ filename: TEST_DIR/t-f335.IMGFMT
format: FLAT
[335]:
virtual size: 2147483648
- filename: TEST_DIR/t-f336.vmdk
+ filename: TEST_DIR/t-f336.IMGFMT
format: FLAT
[336]:
virtual size: 2147483648
- filename: TEST_DIR/t-f337.vmdk
+ filename: TEST_DIR/t-f337.IMGFMT
format: FLAT
[337]:
virtual size: 2147483648
- filename: TEST_DIR/t-f338.vmdk
+ filename: TEST_DIR/t-f338.IMGFMT
format: FLAT
[338]:
virtual size: 2147483648
- filename: TEST_DIR/t-f339.vmdk
+ filename: TEST_DIR/t-f339.IMGFMT
format: FLAT
[339]:
virtual size: 2147483648
- filename: TEST_DIR/t-f340.vmdk
+ filename: TEST_DIR/t-f340.IMGFMT
format: FLAT
[340]:
virtual size: 2147483648
- filename: TEST_DIR/t-f341.vmdk
+ filename: TEST_DIR/t-f341.IMGFMT
format: FLAT
[341]:
virtual size: 2147483648
- filename: TEST_DIR/t-f342.vmdk
+ filename: TEST_DIR/t-f342.IMGFMT
format: FLAT
[342]:
virtual size: 2147483648
- filename: TEST_DIR/t-f343.vmdk
+ filename: TEST_DIR/t-f343.IMGFMT
format: FLAT
[343]:
virtual size: 2147483648
- filename: TEST_DIR/t-f344.vmdk
+ filename: TEST_DIR/t-f344.IMGFMT
format: FLAT
[344]:
virtual size: 2147483648
- filename: TEST_DIR/t-f345.vmdk
+ filename: TEST_DIR/t-f345.IMGFMT
format: FLAT
[345]:
virtual size: 2147483648
- filename: TEST_DIR/t-f346.vmdk
+ filename: TEST_DIR/t-f346.IMGFMT
format: FLAT
[346]:
virtual size: 2147483648
- filename: TEST_DIR/t-f347.vmdk
+ filename: TEST_DIR/t-f347.IMGFMT
format: FLAT
[347]:
virtual size: 2147483648
- filename: TEST_DIR/t-f348.vmdk
+ filename: TEST_DIR/t-f348.IMGFMT
format: FLAT
[348]:
virtual size: 2147483648
- filename: TEST_DIR/t-f349.vmdk
+ filename: TEST_DIR/t-f349.IMGFMT
format: FLAT
[349]:
virtual size: 2147483648
- filename: TEST_DIR/t-f350.vmdk
+ filename: TEST_DIR/t-f350.IMGFMT
format: FLAT
[350]:
virtual size: 2147483648
- filename: TEST_DIR/t-f351.vmdk
+ filename: TEST_DIR/t-f351.IMGFMT
format: FLAT
[351]:
virtual size: 2147483648
- filename: TEST_DIR/t-f352.vmdk
+ filename: TEST_DIR/t-f352.IMGFMT
format: FLAT
[352]:
virtual size: 2147483648
- filename: TEST_DIR/t-f353.vmdk
+ filename: TEST_DIR/t-f353.IMGFMT
format: FLAT
[353]:
virtual size: 2147483648
- filename: TEST_DIR/t-f354.vmdk
+ filename: TEST_DIR/t-f354.IMGFMT
format: FLAT
[354]:
virtual size: 2147483648
- filename: TEST_DIR/t-f355.vmdk
+ filename: TEST_DIR/t-f355.IMGFMT
format: FLAT
[355]:
virtual size: 2147483648
- filename: TEST_DIR/t-f356.vmdk
+ filename: TEST_DIR/t-f356.IMGFMT
format: FLAT
[356]:
virtual size: 2147483648
- filename: TEST_DIR/t-f357.vmdk
+ filename: TEST_DIR/t-f357.IMGFMT
format: FLAT
[357]:
virtual size: 2147483648
- filename: TEST_DIR/t-f358.vmdk
+ filename: TEST_DIR/t-f358.IMGFMT
format: FLAT
[358]:
virtual size: 2147483648
- filename: TEST_DIR/t-f359.vmdk
+ filename: TEST_DIR/t-f359.IMGFMT
format: FLAT
[359]:
virtual size: 2147483648
- filename: TEST_DIR/t-f360.vmdk
+ filename: TEST_DIR/t-f360.IMGFMT
format: FLAT
[360]:
virtual size: 2147483648
- filename: TEST_DIR/t-f361.vmdk
+ filename: TEST_DIR/t-f361.IMGFMT
format: FLAT
[361]:
virtual size: 2147483648
- filename: TEST_DIR/t-f362.vmdk
+ filename: TEST_DIR/t-f362.IMGFMT
format: FLAT
[362]:
virtual size: 2147483648
- filename: TEST_DIR/t-f363.vmdk
+ filename: TEST_DIR/t-f363.IMGFMT
format: FLAT
[363]:
virtual size: 2147483648
- filename: TEST_DIR/t-f364.vmdk
+ filename: TEST_DIR/t-f364.IMGFMT
format: FLAT
[364]:
virtual size: 2147483648
- filename: TEST_DIR/t-f365.vmdk
+ filename: TEST_DIR/t-f365.IMGFMT
format: FLAT
[365]:
virtual size: 2147483648
- filename: TEST_DIR/t-f366.vmdk
+ filename: TEST_DIR/t-f366.IMGFMT
format: FLAT
[366]:
virtual size: 2147483648
- filename: TEST_DIR/t-f367.vmdk
+ filename: TEST_DIR/t-f367.IMGFMT
format: FLAT
[367]:
virtual size: 2147483648
- filename: TEST_DIR/t-f368.vmdk
+ filename: TEST_DIR/t-f368.IMGFMT
format: FLAT
[368]:
virtual size: 2147483648
- filename: TEST_DIR/t-f369.vmdk
+ filename: TEST_DIR/t-f369.IMGFMT
format: FLAT
[369]:
virtual size: 2147483648
- filename: TEST_DIR/t-f370.vmdk
+ filename: TEST_DIR/t-f370.IMGFMT
format: FLAT
[370]:
virtual size: 2147483648
- filename: TEST_DIR/t-f371.vmdk
+ filename: TEST_DIR/t-f371.IMGFMT
format: FLAT
[371]:
virtual size: 2147483648
- filename: TEST_DIR/t-f372.vmdk
+ filename: TEST_DIR/t-f372.IMGFMT
format: FLAT
[372]:
virtual size: 2147483648
- filename: TEST_DIR/t-f373.vmdk
+ filename: TEST_DIR/t-f373.IMGFMT
format: FLAT
[373]:
virtual size: 2147483648
- filename: TEST_DIR/t-f374.vmdk
+ filename: TEST_DIR/t-f374.IMGFMT
format: FLAT
[374]:
virtual size: 2147483648
- filename: TEST_DIR/t-f375.vmdk
+ filename: TEST_DIR/t-f375.IMGFMT
format: FLAT
[375]:
virtual size: 2147483648
- filename: TEST_DIR/t-f376.vmdk
+ filename: TEST_DIR/t-f376.IMGFMT
format: FLAT
[376]:
virtual size: 2147483648
- filename: TEST_DIR/t-f377.vmdk
+ filename: TEST_DIR/t-f377.IMGFMT
format: FLAT
[377]:
virtual size: 2147483648
- filename: TEST_DIR/t-f378.vmdk
+ filename: TEST_DIR/t-f378.IMGFMT
format: FLAT
[378]:
virtual size: 2147483648
- filename: TEST_DIR/t-f379.vmdk
+ filename: TEST_DIR/t-f379.IMGFMT
format: FLAT
[379]:
virtual size: 2147483648
- filename: TEST_DIR/t-f380.vmdk
+ filename: TEST_DIR/t-f380.IMGFMT
format: FLAT
[380]:
virtual size: 2147483648
- filename: TEST_DIR/t-f381.vmdk
+ filename: TEST_DIR/t-f381.IMGFMT
format: FLAT
[381]:
virtual size: 2147483648
- filename: TEST_DIR/t-f382.vmdk
+ filename: TEST_DIR/t-f382.IMGFMT
format: FLAT
[382]:
virtual size: 2147483648
- filename: TEST_DIR/t-f383.vmdk
+ filename: TEST_DIR/t-f383.IMGFMT
format: FLAT
[383]:
virtual size: 2147483648
- filename: TEST_DIR/t-f384.vmdk
+ filename: TEST_DIR/t-f384.IMGFMT
format: FLAT
[384]:
virtual size: 2147483648
- filename: TEST_DIR/t-f385.vmdk
+ filename: TEST_DIR/t-f385.IMGFMT
format: FLAT
[385]:
virtual size: 2147483648
- filename: TEST_DIR/t-f386.vmdk
+ filename: TEST_DIR/t-f386.IMGFMT
format: FLAT
[386]:
virtual size: 2147483648
- filename: TEST_DIR/t-f387.vmdk
+ filename: TEST_DIR/t-f387.IMGFMT
format: FLAT
[387]:
virtual size: 2147483648
- filename: TEST_DIR/t-f388.vmdk
+ filename: TEST_DIR/t-f388.IMGFMT
format: FLAT
[388]:
virtual size: 2147483648
- filename: TEST_DIR/t-f389.vmdk
+ filename: TEST_DIR/t-f389.IMGFMT
format: FLAT
[389]:
virtual size: 2147483648
- filename: TEST_DIR/t-f390.vmdk
+ filename: TEST_DIR/t-f390.IMGFMT
format: FLAT
[390]:
virtual size: 2147483648
- filename: TEST_DIR/t-f391.vmdk
+ filename: TEST_DIR/t-f391.IMGFMT
format: FLAT
[391]:
virtual size: 2147483648
- filename: TEST_DIR/t-f392.vmdk
+ filename: TEST_DIR/t-f392.IMGFMT
format: FLAT
[392]:
virtual size: 2147483648
- filename: TEST_DIR/t-f393.vmdk
+ filename: TEST_DIR/t-f393.IMGFMT
format: FLAT
[393]:
virtual size: 2147483648
- filename: TEST_DIR/t-f394.vmdk
+ filename: TEST_DIR/t-f394.IMGFMT
format: FLAT
[394]:
virtual size: 2147483648
- filename: TEST_DIR/t-f395.vmdk
+ filename: TEST_DIR/t-f395.IMGFMT
format: FLAT
[395]:
virtual size: 2147483648
- filename: TEST_DIR/t-f396.vmdk
+ filename: TEST_DIR/t-f396.IMGFMT
format: FLAT
[396]:
virtual size: 2147483648
- filename: TEST_DIR/t-f397.vmdk
+ filename: TEST_DIR/t-f397.IMGFMT
format: FLAT
[397]:
virtual size: 2147483648
- filename: TEST_DIR/t-f398.vmdk
+ filename: TEST_DIR/t-f398.IMGFMT
format: FLAT
[398]:
virtual size: 2147483648
- filename: TEST_DIR/t-f399.vmdk
+ filename: TEST_DIR/t-f399.IMGFMT
format: FLAT
[399]:
virtual size: 2147483648
- filename: TEST_DIR/t-f400.vmdk
+ filename: TEST_DIR/t-f400.IMGFMT
format: FLAT
[400]:
virtual size: 2147483648
- filename: TEST_DIR/t-f401.vmdk
+ filename: TEST_DIR/t-f401.IMGFMT
format: FLAT
[401]:
virtual size: 2147483648
- filename: TEST_DIR/t-f402.vmdk
+ filename: TEST_DIR/t-f402.IMGFMT
format: FLAT
[402]:
virtual size: 2147483648
- filename: TEST_DIR/t-f403.vmdk
+ filename: TEST_DIR/t-f403.IMGFMT
format: FLAT
[403]:
virtual size: 2147483648
- filename: TEST_DIR/t-f404.vmdk
+ filename: TEST_DIR/t-f404.IMGFMT
format: FLAT
[404]:
virtual size: 2147483648
- filename: TEST_DIR/t-f405.vmdk
+ filename: TEST_DIR/t-f405.IMGFMT
format: FLAT
[405]:
virtual size: 2147483648
- filename: TEST_DIR/t-f406.vmdk
+ filename: TEST_DIR/t-f406.IMGFMT
format: FLAT
[406]:
virtual size: 2147483648
- filename: TEST_DIR/t-f407.vmdk
+ filename: TEST_DIR/t-f407.IMGFMT
format: FLAT
[407]:
virtual size: 2147483648
- filename: TEST_DIR/t-f408.vmdk
+ filename: TEST_DIR/t-f408.IMGFMT
format: FLAT
[408]:
virtual size: 2147483648
- filename: TEST_DIR/t-f409.vmdk
+ filename: TEST_DIR/t-f409.IMGFMT
format: FLAT
[409]:
virtual size: 2147483648
- filename: TEST_DIR/t-f410.vmdk
+ filename: TEST_DIR/t-f410.IMGFMT
format: FLAT
[410]:
virtual size: 2147483648
- filename: TEST_DIR/t-f411.vmdk
+ filename: TEST_DIR/t-f411.IMGFMT
format: FLAT
[411]:
virtual size: 2147483648
- filename: TEST_DIR/t-f412.vmdk
+ filename: TEST_DIR/t-f412.IMGFMT
format: FLAT
[412]:
virtual size: 2147483648
- filename: TEST_DIR/t-f413.vmdk
+ filename: TEST_DIR/t-f413.IMGFMT
format: FLAT
[413]:
virtual size: 2147483648
- filename: TEST_DIR/t-f414.vmdk
+ filename: TEST_DIR/t-f414.IMGFMT
format: FLAT
[414]:
virtual size: 2147483648
- filename: TEST_DIR/t-f415.vmdk
+ filename: TEST_DIR/t-f415.IMGFMT
format: FLAT
[415]:
virtual size: 2147483648
- filename: TEST_DIR/t-f416.vmdk
+ filename: TEST_DIR/t-f416.IMGFMT
format: FLAT
[416]:
virtual size: 2147483648
- filename: TEST_DIR/t-f417.vmdk
+ filename: TEST_DIR/t-f417.IMGFMT
format: FLAT
[417]:
virtual size: 2147483648
- filename: TEST_DIR/t-f418.vmdk
+ filename: TEST_DIR/t-f418.IMGFMT
format: FLAT
[418]:
virtual size: 2147483648
- filename: TEST_DIR/t-f419.vmdk
+ filename: TEST_DIR/t-f419.IMGFMT
format: FLAT
[419]:
virtual size: 2147483648
- filename: TEST_DIR/t-f420.vmdk
+ filename: TEST_DIR/t-f420.IMGFMT
format: FLAT
[420]:
virtual size: 2147483648
- filename: TEST_DIR/t-f421.vmdk
+ filename: TEST_DIR/t-f421.IMGFMT
format: FLAT
[421]:
virtual size: 2147483648
- filename: TEST_DIR/t-f422.vmdk
+ filename: TEST_DIR/t-f422.IMGFMT
format: FLAT
[422]:
virtual size: 2147483648
- filename: TEST_DIR/t-f423.vmdk
+ filename: TEST_DIR/t-f423.IMGFMT
format: FLAT
[423]:
virtual size: 2147483648
- filename: TEST_DIR/t-f424.vmdk
+ filename: TEST_DIR/t-f424.IMGFMT
format: FLAT
[424]:
virtual size: 2147483648
- filename: TEST_DIR/t-f425.vmdk
+ filename: TEST_DIR/t-f425.IMGFMT
format: FLAT
[425]:
virtual size: 2147483648
- filename: TEST_DIR/t-f426.vmdk
+ filename: TEST_DIR/t-f426.IMGFMT
format: FLAT
[426]:
virtual size: 2147483648
- filename: TEST_DIR/t-f427.vmdk
+ filename: TEST_DIR/t-f427.IMGFMT
format: FLAT
[427]:
virtual size: 2147483648
- filename: TEST_DIR/t-f428.vmdk
+ filename: TEST_DIR/t-f428.IMGFMT
format: FLAT
[428]:
virtual size: 2147483648
- filename: TEST_DIR/t-f429.vmdk
+ filename: TEST_DIR/t-f429.IMGFMT
format: FLAT
[429]:
virtual size: 2147483648
- filename: TEST_DIR/t-f430.vmdk
+ filename: TEST_DIR/t-f430.IMGFMT
format: FLAT
[430]:
virtual size: 2147483648
- filename: TEST_DIR/t-f431.vmdk
+ filename: TEST_DIR/t-f431.IMGFMT
format: FLAT
[431]:
virtual size: 2147483648
- filename: TEST_DIR/t-f432.vmdk
+ filename: TEST_DIR/t-f432.IMGFMT
format: FLAT
[432]:
virtual size: 2147483648
- filename: TEST_DIR/t-f433.vmdk
+ filename: TEST_DIR/t-f433.IMGFMT
format: FLAT
[433]:
virtual size: 2147483648
- filename: TEST_DIR/t-f434.vmdk
+ filename: TEST_DIR/t-f434.IMGFMT
format: FLAT
[434]:
virtual size: 2147483648
- filename: TEST_DIR/t-f435.vmdk
+ filename: TEST_DIR/t-f435.IMGFMT
format: FLAT
[435]:
virtual size: 2147483648
- filename: TEST_DIR/t-f436.vmdk
+ filename: TEST_DIR/t-f436.IMGFMT
format: FLAT
[436]:
virtual size: 2147483648
- filename: TEST_DIR/t-f437.vmdk
+ filename: TEST_DIR/t-f437.IMGFMT
format: FLAT
[437]:
virtual size: 2147483648
- filename: TEST_DIR/t-f438.vmdk
+ filename: TEST_DIR/t-f438.IMGFMT
format: FLAT
[438]:
virtual size: 2147483648
- filename: TEST_DIR/t-f439.vmdk
+ filename: TEST_DIR/t-f439.IMGFMT
format: FLAT
[439]:
virtual size: 2147483648
- filename: TEST_DIR/t-f440.vmdk
+ filename: TEST_DIR/t-f440.IMGFMT
format: FLAT
[440]:
virtual size: 2147483648
- filename: TEST_DIR/t-f441.vmdk
+ filename: TEST_DIR/t-f441.IMGFMT
format: FLAT
[441]:
virtual size: 2147483648
- filename: TEST_DIR/t-f442.vmdk
+ filename: TEST_DIR/t-f442.IMGFMT
format: FLAT
[442]:
virtual size: 2147483648
- filename: TEST_DIR/t-f443.vmdk
+ filename: TEST_DIR/t-f443.IMGFMT
format: FLAT
[443]:
virtual size: 2147483648
- filename: TEST_DIR/t-f444.vmdk
+ filename: TEST_DIR/t-f444.IMGFMT
format: FLAT
[444]:
virtual size: 2147483648
- filename: TEST_DIR/t-f445.vmdk
+ filename: TEST_DIR/t-f445.IMGFMT
format: FLAT
[445]:
virtual size: 2147483648
- filename: TEST_DIR/t-f446.vmdk
+ filename: TEST_DIR/t-f446.IMGFMT
format: FLAT
[446]:
virtual size: 2147483648
- filename: TEST_DIR/t-f447.vmdk
+ filename: TEST_DIR/t-f447.IMGFMT
format: FLAT
[447]:
virtual size: 2147483648
- filename: TEST_DIR/t-f448.vmdk
+ filename: TEST_DIR/t-f448.IMGFMT
format: FLAT
[448]:
virtual size: 2147483648
- filename: TEST_DIR/t-f449.vmdk
+ filename: TEST_DIR/t-f449.IMGFMT
format: FLAT
[449]:
virtual size: 2147483648
- filename: TEST_DIR/t-f450.vmdk
+ filename: TEST_DIR/t-f450.IMGFMT
format: FLAT
[450]:
virtual size: 2147483648
- filename: TEST_DIR/t-f451.vmdk
+ filename: TEST_DIR/t-f451.IMGFMT
format: FLAT
[451]:
virtual size: 2147483648
- filename: TEST_DIR/t-f452.vmdk
+ filename: TEST_DIR/t-f452.IMGFMT
format: FLAT
[452]:
virtual size: 2147483648
- filename: TEST_DIR/t-f453.vmdk
+ filename: TEST_DIR/t-f453.IMGFMT
format: FLAT
[453]:
virtual size: 2147483648
- filename: TEST_DIR/t-f454.vmdk
+ filename: TEST_DIR/t-f454.IMGFMT
format: FLAT
[454]:
virtual size: 2147483648
- filename: TEST_DIR/t-f455.vmdk
+ filename: TEST_DIR/t-f455.IMGFMT
format: FLAT
[455]:
virtual size: 2147483648
- filename: TEST_DIR/t-f456.vmdk
+ filename: TEST_DIR/t-f456.IMGFMT
format: FLAT
[456]:
virtual size: 2147483648
- filename: TEST_DIR/t-f457.vmdk
+ filename: TEST_DIR/t-f457.IMGFMT
format: FLAT
[457]:
virtual size: 2147483648
- filename: TEST_DIR/t-f458.vmdk
+ filename: TEST_DIR/t-f458.IMGFMT
format: FLAT
[458]:
virtual size: 2147483648
- filename: TEST_DIR/t-f459.vmdk
+ filename: TEST_DIR/t-f459.IMGFMT
format: FLAT
[459]:
virtual size: 2147483648
- filename: TEST_DIR/t-f460.vmdk
+ filename: TEST_DIR/t-f460.IMGFMT
format: FLAT
[460]:
virtual size: 2147483648
- filename: TEST_DIR/t-f461.vmdk
+ filename: TEST_DIR/t-f461.IMGFMT
format: FLAT
[461]:
virtual size: 2147483648
- filename: TEST_DIR/t-f462.vmdk
+ filename: TEST_DIR/t-f462.IMGFMT
format: FLAT
[462]:
virtual size: 2147483648
- filename: TEST_DIR/t-f463.vmdk
+ filename: TEST_DIR/t-f463.IMGFMT
format: FLAT
[463]:
virtual size: 2147483648
- filename: TEST_DIR/t-f464.vmdk
+ filename: TEST_DIR/t-f464.IMGFMT
format: FLAT
[464]:
virtual size: 2147483648
- filename: TEST_DIR/t-f465.vmdk
+ filename: TEST_DIR/t-f465.IMGFMT
format: FLAT
[465]:
virtual size: 2147483648
- filename: TEST_DIR/t-f466.vmdk
+ filename: TEST_DIR/t-f466.IMGFMT
format: FLAT
[466]:
virtual size: 2147483648
- filename: TEST_DIR/t-f467.vmdk
+ filename: TEST_DIR/t-f467.IMGFMT
format: FLAT
[467]:
virtual size: 2147483648
- filename: TEST_DIR/t-f468.vmdk
+ filename: TEST_DIR/t-f468.IMGFMT
format: FLAT
[468]:
virtual size: 2147483648
- filename: TEST_DIR/t-f469.vmdk
+ filename: TEST_DIR/t-f469.IMGFMT
format: FLAT
[469]:
virtual size: 2147483648
- filename: TEST_DIR/t-f470.vmdk
+ filename: TEST_DIR/t-f470.IMGFMT
format: FLAT
[470]:
virtual size: 2147483648
- filename: TEST_DIR/t-f471.vmdk
+ filename: TEST_DIR/t-f471.IMGFMT
format: FLAT
[471]:
virtual size: 2147483648
- filename: TEST_DIR/t-f472.vmdk
+ filename: TEST_DIR/t-f472.IMGFMT
format: FLAT
[472]:
virtual size: 2147483648
- filename: TEST_DIR/t-f473.vmdk
+ filename: TEST_DIR/t-f473.IMGFMT
format: FLAT
[473]:
virtual size: 2147483648
- filename: TEST_DIR/t-f474.vmdk
+ filename: TEST_DIR/t-f474.IMGFMT
format: FLAT
[474]:
virtual size: 2147483648
- filename: TEST_DIR/t-f475.vmdk
+ filename: TEST_DIR/t-f475.IMGFMT
format: FLAT
[475]:
virtual size: 2147483648
- filename: TEST_DIR/t-f476.vmdk
+ filename: TEST_DIR/t-f476.IMGFMT
format: FLAT
[476]:
virtual size: 2147483648
- filename: TEST_DIR/t-f477.vmdk
+ filename: TEST_DIR/t-f477.IMGFMT
format: FLAT
[477]:
virtual size: 2147483648
- filename: TEST_DIR/t-f478.vmdk
+ filename: TEST_DIR/t-f478.IMGFMT
format: FLAT
[478]:
virtual size: 2147483648
- filename: TEST_DIR/t-f479.vmdk
+ filename: TEST_DIR/t-f479.IMGFMT
format: FLAT
[479]:
virtual size: 2147483648
- filename: TEST_DIR/t-f480.vmdk
+ filename: TEST_DIR/t-f480.IMGFMT
format: FLAT
[480]:
virtual size: 2147483648
- filename: TEST_DIR/t-f481.vmdk
+ filename: TEST_DIR/t-f481.IMGFMT
format: FLAT
[481]:
virtual size: 2147483648
- filename: TEST_DIR/t-f482.vmdk
+ filename: TEST_DIR/t-f482.IMGFMT
format: FLAT
[482]:
virtual size: 2147483648
- filename: TEST_DIR/t-f483.vmdk
+ filename: TEST_DIR/t-f483.IMGFMT
format: FLAT
[483]:
virtual size: 2147483648
- filename: TEST_DIR/t-f484.vmdk
+ filename: TEST_DIR/t-f484.IMGFMT
format: FLAT
[484]:
virtual size: 2147483648
- filename: TEST_DIR/t-f485.vmdk
+ filename: TEST_DIR/t-f485.IMGFMT
format: FLAT
[485]:
virtual size: 2147483648
- filename: TEST_DIR/t-f486.vmdk
+ filename: TEST_DIR/t-f486.IMGFMT
format: FLAT
[486]:
virtual size: 2147483648
- filename: TEST_DIR/t-f487.vmdk
+ filename: TEST_DIR/t-f487.IMGFMT
format: FLAT
[487]:
virtual size: 2147483648
- filename: TEST_DIR/t-f488.vmdk
+ filename: TEST_DIR/t-f488.IMGFMT
format: FLAT
[488]:
virtual size: 2147483648
- filename: TEST_DIR/t-f489.vmdk
+ filename: TEST_DIR/t-f489.IMGFMT
format: FLAT
[489]:
virtual size: 2147483648
- filename: TEST_DIR/t-f490.vmdk
+ filename: TEST_DIR/t-f490.IMGFMT
format: FLAT
[490]:
virtual size: 2147483648
- filename: TEST_DIR/t-f491.vmdk
+ filename: TEST_DIR/t-f491.IMGFMT
format: FLAT
[491]:
virtual size: 2147483648
- filename: TEST_DIR/t-f492.vmdk
+ filename: TEST_DIR/t-f492.IMGFMT
format: FLAT
[492]:
virtual size: 2147483648
- filename: TEST_DIR/t-f493.vmdk
+ filename: TEST_DIR/t-f493.IMGFMT
format: FLAT
[493]:
virtual size: 2147483648
- filename: TEST_DIR/t-f494.vmdk
+ filename: TEST_DIR/t-f494.IMGFMT
format: FLAT
[494]:
virtual size: 2147483648
- filename: TEST_DIR/t-f495.vmdk
+ filename: TEST_DIR/t-f495.IMGFMT
format: FLAT
[495]:
virtual size: 2147483648
- filename: TEST_DIR/t-f496.vmdk
+ filename: TEST_DIR/t-f496.IMGFMT
format: FLAT
[496]:
virtual size: 2147483648
- filename: TEST_DIR/t-f497.vmdk
+ filename: TEST_DIR/t-f497.IMGFMT
format: FLAT
[497]:
virtual size: 2147483648
- filename: TEST_DIR/t-f498.vmdk
+ filename: TEST_DIR/t-f498.IMGFMT
format: FLAT
[498]:
virtual size: 2147483648
- filename: TEST_DIR/t-f499.vmdk
+ filename: TEST_DIR/t-f499.IMGFMT
format: FLAT
[499]:
virtual size: 2147483648
- filename: TEST_DIR/t-f500.vmdk
+ filename: TEST_DIR/t-f500.IMGFMT
format: FLAT
=== Testing malformed VMFS extent description line ===
$QEMU_IO -c 'write 0k 64k' "$BACKING_IMG" | _filter_qemu_io
-_make_test_img -b "$BACKING_IMG" 1G
+_make_test_img -b "$BACKING_IMG" -F $IMGFMT 1G
# Write two clusters, the second one enforces creation of an L2 table after
# the first data cluster.
$QEMU_IO -c 'write 0k 64k' -c 'write 512M 64k' "$TEST_IMG" | _filter_qemu_io
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=1073741824
wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset 536870912
echo
TEST_IMG="$TEST_IMG.base" _make_test_img -o "compat=1.1" 64M
$QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io
-_make_test_img -o "compat=1.1" -b "$TEST_IMG.base" 64M
+_make_test_img -o "compat=1.1" -b "$TEST_IMG.base" -F $IMGFMT 64M
$QEMU_IO -c "read -P 0x2a 0 128k" -c "write -z 0 64k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
_check_test_img
echo
TEST_IMG="$TEST_IMG.base" _make_test_img -o "compat=1.1" 64M
$QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io
-_make_test_img -o "compat=1.1" -b "$TEST_IMG.base" 64M
+_make_test_img -o "compat=1.1" -b "$TEST_IMG.base" -F $IMGFMT 64M
$QEMU_IO -c "write -z 0 64k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG snapshot -c foo "$TEST_IMG"
$QEMU_IO -c "write -P 0x42 0 128k" "$TEST_IMG" | _filter_qemu_io
echo
TEST_IMG="$TEST_IMG.base" _make_test_img -o "compat=1.1" 64M
$QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io
-_make_test_img -o "compat=1.1" -b "$TEST_IMG.base" 64M
+_make_test_img -o "compat=1.1" -b "$TEST_IMG.base" -F $IMGFMT 64M
$QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG snapshot -c foo "$TEST_IMG"
$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
echo "=== Testing progress report without snapshot ==="
echo
TEST_IMG="$TEST_IMG.base" _make_test_img -o "compat=1.1" 4G
-_make_test_img -o "compat=1.1" -b "$TEST_IMG.base" 4G
+_make_test_img -o "compat=1.1" -b "$TEST_IMG.base" -F $IMGFMT 4G
$QEMU_IO -c "write -z 0 64k" \
-c "write -z 1G 64k" \
-c "write -z 2G 64k" \
echo "=== Testing progress report with snapshot ==="
echo
TEST_IMG="$TEST_IMG.base" _make_test_img -o "compat=1.1" 4G
-_make_test_img -o "compat=1.1" -b "$TEST_IMG.base" 4G
+_make_test_img -o "compat=1.1" -b "$TEST_IMG.base" -F $IMGFMT 4G
$QEMU_IO -c "write -z 0 64k" \
-c "write -z 1G 64k" \
-c "write -z 2G 64k" \
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qemu-img: warning: Deprecated use of amend to alter the backing file; use qemu-img rebase instead
read 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
read 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset 0
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 131072/131072 bytes at offset 0
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
=== Testing progress report without snapshot ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=4294967296
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset 1073741824
=== Testing progress report with snapshot ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=4294967296
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset 1073741824
echo "=== Creating an image with a backing file and deleting that file ==="
echo
TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE
-_make_test_img -b "$TEST_IMG.base" $IMG_SIZE
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT $IMG_SIZE
_rm_test_img "$TEST_IMG.base"
# Just open the image and close it right again (this should print an error message)
$QEMU_IO -c quit "$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt
=== Creating an image with a backing file and deleting that file ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=131072
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
qemu-io: can't open device TEST_DIR/t.IMGFMT: Could not open backing file: Could not open 'TEST_DIR/t.IMGFMT.base': No such file or directory
*** done
_supported_fmt qcow2
_supported_proto generic
-_unsupported_proto vxhs
# External data files do not support compressed clusters
# (TODO: Consider writing a version for external data files that does
# not test compressed clusters)
TEST_IMG="$TEST_IMG.base" _make_test_img $size
-_make_test_img -b "$TEST_IMG.base"
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT
$QEMU_IO -c "write -P 0xa5 0 $size" "$TEST_IMG.base" | _filter_qemu_io
echo
== creating backing file ==
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 134217728/134217728 bytes at offset 0
128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation=metadata
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation=metadata
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation=metadata
-qemu-img: TEST_DIR/t.IMGFMT: Cluster size must be a power of two between 512 and 2048k
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 preallocation=metadata
+qemu-img: TEST_DIR/t.IMGFMT: Cluster size must be a power of two between 512 and 2048k
*** done
run_qemu_img create -f $IMGFMT -o cluster_size=4k -o \? "$TEST_IMG" $size
# Looks like a help option, but is part of the backing file name
-run_qemu_img create -f $IMGFMT -u -o backing_file="$TEST_IMG",,help "$TEST_IMG" $size
-run_qemu_img create -f $IMGFMT -u -o backing_file="$TEST_IMG",,\? "$TEST_IMG" $size
+run_qemu_img create -f $IMGFMT -u -o backing_file="$TEST_IMG",,help \
+ -F $IMGFMT "$TEST_IMG" $size
+run_qemu_img create -f $IMGFMT -u -o backing_file="$TEST_IMG",,\? \
+ -F $IMGFMT "$TEST_IMG" $size
# Try to trick qemu-img into creating escaped commas
run_qemu_img create -f $IMGFMT -o backing_file="$TEST_IMG", -o help "$TEST_IMG" $size
run_qemu_img convert -O $IMGFMT -o cluster_size=4k -o \? "$TEST_IMG" "$TEST_IMG".base
# Looks like a help option, but is part of the backing file name
-run_qemu_img convert -O $IMGFMT -o backing_file="$TEST_IMG",,help "$TEST_IMG" "$TEST_IMG".base
-run_qemu_img convert -O $IMGFMT -o backing_file="$TEST_IMG",,\? "$TEST_IMG" "$TEST_IMG".base
+run_qemu_img convert -O $IMGFMT -o backing_fmt=$IMGFMT,backing_file="$TEST_IMG",,help "$TEST_IMG" "$TEST_IMG".base
+run_qemu_img convert -O $IMGFMT -o backing_fmt=$IMGFMT,backing_file="$TEST_IMG",,\? "$TEST_IMG" "$TEST_IMG".base
# Try to trick qemu-img into creating escaped commas
run_qemu_img convert -O $IMGFMT -o backing_file="$TEST_IMG", -o help "$TEST_IMG" "$TEST_IMG".base
encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
+ extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
+ extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
+ extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
+ extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
+ extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
+ extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
+ extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
+ extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
refcount_bits=<num> - Width of a reference count entry in bits
size=<size> - Virtual disk size
-Testing: create -f qcow2 -u -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 128M
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/t.qcow2,,help lazy_refcounts=off refcount_bits=16
+Testing: create -f qcow2 -u -o backing_file=TEST_DIR/t.qcow2,,help -F qcow2 TEST_DIR/t.qcow2 128M
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/t.qcow2,,help backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
-Testing: create -f qcow2 -u -o backing_file=TEST_DIR/t.qcow2,,? TEST_DIR/t.qcow2 128M
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/t.qcow2,,? lazy_refcounts=off refcount_bits=16
+Testing: create -f qcow2 -u -o backing_file=TEST_DIR/t.qcow2,,? -F qcow2 TEST_DIR/t.qcow2 128M
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=134217728 backing_file=TEST_DIR/t.qcow2,,? backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
Testing: create -f qcow2 -o backing_file=TEST_DIR/t.qcow2, -o help TEST_DIR/t.qcow2 128M
qemu-img: Invalid option list: backing_file=TEST_DIR/t.qcow2,
encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
+ extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
+ extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
+ extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
+ extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
+ extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
+ extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
+ extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
+ extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
refcount_bits=<num> - Width of a reference count entry in bits
size=<size> - Virtual disk size
-Testing: convert -O qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
+Testing: convert -O qcow2 -o backing_fmt=qcow2,backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
qemu-img: Could not open 'TEST_DIR/t.qcow2.base': Could not open backing file: Could not open 'TEST_DIR/t.qcow2,help': No such file or directory
-Testing: convert -O qcow2 -o backing_file=TEST_DIR/t.qcow2,,? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
+Testing: convert -O qcow2 -o backing_fmt=qcow2,backing_file=TEST_DIR/t.qcow2,,? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
qemu-img: Could not open 'TEST_DIR/t.qcow2.base': Could not open backing file: Could not open 'TEST_DIR/t.qcow2,?': No such file or directory
Testing: convert -O qcow2 -o backing_file=TEST_DIR/t.qcow2, -o help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
size=<size> - Virtual disk size
Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2
+qemu-img: warning: Deprecated use of amend to alter the backing file; use qemu-img rebase instead
Testing: rebase -u -b -f qcow2 TEST_DIR/t.qcow2
Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,? TEST_DIR/t.qcow2
+qemu-img: warning: Deprecated use of amend to alter the backing file; use qemu-img rebase instead
Testing: rebase -u -b -f qcow2 TEST_DIR/t.qcow2
{
base_image="${TEST_DIR}/$((${1}-1))-${snapshot_virt0}"
snapshot_file="${TEST_DIR}/${1}-${snapshot_virt0}"
- TEST_IMG=$snapshot_file _make_test_img -u -b "${base_image}" "$size"
+ TEST_IMG=$snapshot_file _make_test_img -u -b "${base_image}" -F $IMGFMT "$size"
do_blockdev_add "$1" "'backing': null, " "${snapshot_file}"
}
SNAPSHOTS=$((${SNAPSHOTS}+1))
TEST_IMG="$TEST_IMG.base" _make_test_img "$size"
-_make_test_img -b "${TEST_IMG}.base" "$size"
+_make_test_img -b "${TEST_IMG}.base" -F $IMGFMT "$size"
do_blockdev_add ${SNAPSHOTS} "" "${TEST_IMG}"
blockdev_snapshot ${SNAPSHOTS} error
=== Create a couple of snapshots using blockdev-snapshot ===
-Formatting 'TEST_DIR/11-snapshot-v0.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/10-snapshot-v0.IMGFMT
+Formatting 'TEST_DIR/11-snapshot-v0.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/10-snapshot-v0.IMGFMT backing_fmt=IMGFMT
{ 'execute': 'blockdev-add', 'arguments': { 'driver': 'IMGFMT', 'node-name': 'snap_11', 'backing': null, 'file': { 'driver': 'file', 'filename': 'TEST_DIR/11-snapshot-v0.IMGFMT', 'node-name': 'file_11' } } }
{"return": {}}
{ 'execute': 'blockdev-snapshot', 'arguments': { 'node': 'virtio0', 'overlay':'snap_11' } }
{"return": {}}
-Formatting 'TEST_DIR/12-snapshot-v0.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/11-snapshot-v0.IMGFMT
+Formatting 'TEST_DIR/12-snapshot-v0.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/11-snapshot-v0.IMGFMT backing_fmt=IMGFMT
{ 'execute': 'blockdev-add', 'arguments': { 'driver': 'IMGFMT', 'node-name': 'snap_12', 'backing': null, 'file': { 'driver': 'file', 'filename': 'TEST_DIR/12-snapshot-v0.IMGFMT', 'node-name': 'file_12' } } }
{"return": {}}
{ 'execute': 'blockdev-snapshot', 'arguments': { 'node': 'virtio0', 'overlay':'snap_12' } }
=== Invalid command - snapshot node has a backing image ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
{ 'execute': 'blockdev-add', 'arguments': { 'driver': 'IMGFMT', 'node-name': 'snap_13', 'file': { 'driver': 'file', 'filename': 'TEST_DIR/t.IMGFMT', 'node-name': 'file_13' } } }
{"return": {}}
{ 'execute': 'blockdev-snapshot', 'arguments': { 'node': 'virtio0', 'overlay':'snap_13' } }
echo "=== Testing correct handling of 'backing':null ==="
echo
-_make_test_img -b "$TEST_IMG.base" $IMG_SIZE
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT $IMG_SIZE
# This should read 42
$QEMU_IO -c 'read -P 42 0 512' "$TEST_IMG" | _filter_qemu_io
=== Testing correct handling of 'backing':null ===
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
read 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 512/512 bytes at offset 0
TEST_IMG="$TEST_IMG.base" _make_test_img $size_smaller
-TEST_IMG="$TEST_IMG.snp1" _make_test_img -b "$TEST_IMG.base" $size_larger
+TEST_IMG="$TEST_IMG.snp1" _make_test_img -b "$TEST_IMG.base" -F $IMGFMT $size_larger
-_make_test_img -b "${TEST_IMG}.snp1" $size_larger
+_make_test_img -b "${TEST_IMG}.snp1" -F $IMGFMT $size_larger
echo
echo "=== Base image info before commit and resize ==="
QA output created by 095
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=5242880
-Formatting 'TEST_DIR/t.IMGFMT.snp1', fmt=IMGFMT size=104857600 backing_file=TEST_DIR/t.IMGFMT.base
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=104857600 backing_file=TEST_DIR/t.IMGFMT.snp1
+Formatting 'TEST_DIR/t.IMGFMT.snp1', fmt=IMGFMT size=104857600 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=104857600 backing_file=TEST_DIR/t.IMGFMT.snp1 backing_fmt=IMGFMT
=== Base image info before commit and resize ===
image: TEST_DIR/t.IMGFMT.base
len=$((2100 * 1024 * 1024 + 512)) # larger than 2G, and not cluster aligned
TEST_IMG="$TEST_IMG.base" _make_test_img $len
-TEST_IMG="$TEST_IMG.itmd" _make_test_img -b "$TEST_IMG.base" $len
-_make_test_img -b "$TEST_IMG.itmd" $len
+TEST_IMG="$TEST_IMG.itmd" _make_test_img -b "$TEST_IMG.base" -F $IMGFMT $len
+_make_test_img -b "$TEST_IMG.itmd" -F $IMGFMT $len
$QEMU_IO -c "write -P 1 0x7ffd0000 192k" "$TEST_IMG.base" | _filter_qemu_io
$QEMU_IO -c "write -P 2 0x7ffe0000 128k" "$TEST_IMG.itmd" | _filter_qemu_io
=== Test pass 0 ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112
-Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd
+Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd backing_fmt=IMGFMT
wrote 196608/196608 bytes at offset 2147287040
192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 131072/131072 bytes at offset 2147352576
=== Test pass 1 ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112
-Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd
+Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd backing_fmt=IMGFMT
wrote 196608/196608 bytes at offset 2147287040
192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 131072/131072 bytes at offset 2147352576
=== Test pass 2 ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112
-Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd
+Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd backing_fmt=IMGFMT
wrote 196608/196608 bytes at offset 2147287040
192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 131072/131072 bytes at offset 2147352576
=== Test pass 3 ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112
-Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd
+Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd backing_fmt=IMGFMT
wrote 196608/196608 bytes at offset 2147287040
192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 131072/131072 bytes at offset 2147352576
echo
TEST_IMG="$TEST_IMG.base" _make_test_img 64M
-_make_test_img -b "$TEST_IMG.base" 64M
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT 64M
# Some data that can be leaked when emptying the top image
$QEMU_IO -c 'write 0 64k' "$TEST_IMG" | _filter_qemu_io
=== l1_update ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
qemu-img: Failed to empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error
=== empty_image_prepare ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
qemu-img: Failed to empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error
=== reftable_update ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
qemu-img: Failed to empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error
=== refblock_alloc ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
qemu-img: Failed to empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error
echo
echo "--- create_mode=$create_mode growth_mode=$growth_mode ---"
- _make_test_img -o "preallocation=$create_mode" ${CREATION_SIZE}K
+ # Our calculation below assumes kilobytes as unit for the actual size.
+ # Disable the extent size hint because it would give us a result in
+ # megabytes.
+ _make_test_img -o "preallocation=$create_mode,extent_size_hint=0" ${CREATION_SIZE}K
$QEMU_IMG resize -f "$IMGFMT" --preallocation=$growth_mode "$TEST_IMG" +${GROWTH_SIZE}K
expected_size=0
# plain int. We should use the correct type for the result, and
# this tests we do.
- _make_test_img 2G
+ _make_test_img -o "extent_size_hint=0" 2G
$QEMU_IMG resize -f "$IMGFMT" --preallocation=$growth_mode "$TEST_IMG" +${GROWTH_SIZE}K
actual_size=$($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep 'disk size')
echo
TEST_IMG="$TEST_IMG.base" _make_test_img 64M
-_make_test_img -b "$TEST_IMG_REL.base" 64M
+_make_test_img -b "$TEST_IMG_REL.base" -F $IMGFMT 64M
# qemu should be able to reconstruct the filename, so relative backing names
# should work
# (We have to filter the backing file format because vmdk always
echo
# omit the image size; it should work anyway
-_make_test_img -b "$TEST_IMG_REL.base"
+_make_test_img -b "$TEST_IMG_REL.base" -F $IMGFMT
echo
echo '=== Nodes without a common directory ==='
=== Reconstructable filename ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=t.IMGFMT.base backing_fmt=IMGFMT
image: TEST_DIR/t.IMGFMT
file format: IMGFMT
virtual size: 64 MiB (67108864 bytes)
=== Backing name is always relative to the backed image ===
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=t.IMGFMT.base backing_fmt=IMGFMT
=== Nodes without a common directory ===
QA output created by 111
qemu-img: TEST_DIR/t.IMGFMT: Could not open 'TEST_DIR/t.IMGFMT.inexistent': No such file or directory
-Could not open backing image to determine size.
+Could not open backing image.
*** done
=== refcount_bits limits ===
-qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+qemu-img: TEST_DIR/t.IMGFMT: Refcount width must be a power of two and may not exceed 64 bits
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
refcount bits: 1
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
refcount bits: 16
-qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater)
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater)
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
+qemu-img: TEST_DIR/t.IMGFMT: Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater)
=== Snapshot limit on refcount_bits=1 ===
_supported_fmt qcow2
_supported_proto generic
-_unsupported_proto vxhs
+# At least OpenBSD doesn't seem to have truncate
+_supported_os Linux
# qcow2.py does not work too well with external data files
_unsupported_imgopts data_file
+# Intentionally specify backing file without backing format; demonstrate
+# the difference in warning messages when backing file could be probed.
+# Note that only a non-raw probe result will affect the resulting image.
+truncate -s $((64 * 1024 * 1024)) "$TEST_IMG.orig"
+_make_test_img -b "$TEST_IMG.orig" 64M
TEST_IMG="$TEST_IMG.base" _make_test_img 64M
+$QEMU_IMG convert -O qcow2 -B "$TEST_IMG.orig" "$TEST_IMG.orig" "$TEST_IMG"
_make_test_img -b "$TEST_IMG.base" 64M
+_make_test_img -u -b "$TEST_IMG.base" 64M
# Set an invalid backing file format
$PYTHON qcow2.py "$TEST_IMG" add-header-ext 0xE2792ACA "foo"
$QEMU_IO -c "open $TEST_IMG" -c "read 0 4k" 2>&1 | _filter_qemu_io | _filter_testdir
$QEMU_IO -c "open -o backing.driver=$IMGFMT $TEST_IMG" -c "read 0 4k" | _filter_qemu_io
+# Rebase the image, to show that omitting backing format triggers a warning,
+# but probing now lets us use the backing file.
+$QEMU_IMG rebase -u -b "$TEST_IMG.base" "$TEST_IMG"
+$QEMU_IO -c "open $TEST_IMG" -c "read 0 4k" 2>&1 | _filter_qemu_io | _filter_testdir
+
# success, all done
echo '*** done'
rm -f $seq.full
QA output created by 114
+qemu-img: warning: Deprecated use of backing file without explicit backing format (detected format of raw)
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.orig
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
+qemu-img: warning: Deprecated use of backing file without explicit backing format
+qemu-img: warning: Deprecated use of backing file without explicit backing format (detected format of IMGFMT)
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+qemu-img: warning: Deprecated use of unopened backing file without explicit backing format, use of this image requires potentially unsafe format probing
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
image: TEST_DIR/t.IMGFMT
file format: IMGFMT
no file open, try 'help open'
read 4096/4096 bytes at offset 0
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+qemu-img: warning: Deprecated use of backing file without explicit backing format, use of this image requires potentially unsafe format probing
+read 4096/4096 bytes at offset 0
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
*** done
echo "=== Check allocation status regression with -B ==="
echo
-_make_test_img -b "$TEST_IMG".base
+_make_test_img -b "$TEST_IMG".base -F $IMGFMT
$QEMU_IO -c "write -P 0x22 0 3M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
-$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base "$TEST_IMG" "$TEST_IMG".orig
+$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base \
+ -o backing_fmt=$IMGFMT "$TEST_IMG" "$TEST_IMG".orig
$QEMU_IMG map "$TEST_IMG".orig | _filter_qemu_img_map
echo "=== Check that zero clusters are kept in overlay ==="
echo
-_make_test_img -b "$TEST_IMG".base
+_make_test_img -b "$TEST_IMG".base -F $IMGFMT
$QEMU_IO -c "write -P 0 0 3M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
-$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base "$TEST_IMG" "$TEST_IMG".orig
+$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base -o backing_fmt=$IMGFMT \
+ "$TEST_IMG" "$TEST_IMG".orig
$QEMU_IO -c "read -P 0 0 3M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
-$QEMU_IMG convert -O $IMGFMT -c -B "$TEST_IMG".base "$TEST_IMG" "$TEST_IMG".orig
+$QEMU_IMG convert -O $IMGFMT -c -B "$TEST_IMG".base -o backing_fmt=$IMGFMT \
+ "$TEST_IMG" "$TEST_IMG".orig
$QEMU_IO -c "read -P 0 0 3M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
$QEMU_IO -c "write -z 0 3M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
-$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base "$TEST_IMG" "$TEST_IMG".orig
+$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base -o backing_fmt=$IMGFMT \
+ "$TEST_IMG" "$TEST_IMG".orig
$QEMU_IO -c "read -P 0 0 3M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
-$QEMU_IMG convert -O $IMGFMT -c -B "$TEST_IMG".base "$TEST_IMG" "$TEST_IMG".orig
+$QEMU_IMG convert -O $IMGFMT -c -B "$TEST_IMG".base -o backing_fmt=$IMGFMT \
+ "$TEST_IMG" "$TEST_IMG".orig
$QEMU_IO -c "read -P 0 0 3M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir
# We do not want such a zero write, however, because we are past the
# end of the backing file on the target as well, so we do not need to
# write anything there.
-_make_test_img -b "$TEST_IMG".base 768M
+_make_test_img -b "$TEST_IMG".base 768M -F $IMGFMT
# Use compat=0.10 as the output so there is no zero cluster support
$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base -o compat=0.10 \
- "$TEST_IMG" "$TEST_IMG".orig
+ -o backing_fmt=$IMGFMT "$TEST_IMG" "$TEST_IMG".orig
# See that nothing has been allocated past 64M
$QEMU_IMG map "$TEST_IMG".orig | _filter_qemu_img_map
$QEMU_IO -c 'write -P 0x22 600M 1M' "$TEST_IMG" 2>&1 | _filter_qemu_io
$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base -o compat=0.10 \
- "$TEST_IMG" "$TEST_IMG".orig
+ -o backing_fmt=$IMGFMT "$TEST_IMG" "$TEST_IMG".orig
$QEMU_IMG map "$TEST_IMG".orig | _filter_qemu_img_map
$QEMU_IO -c 'read -P 0x11 255M 1M' \
TEST_IMG="$TEST_IMG".base _make_test_img 64M
$QEMU_IO -c "write -P 0x11 0 32M" "$TEST_IMG".base 2>&1 | _filter_qemu_io | _filter_testdir
-_make_test_img -b "$TEST_IMG".base 64M
+_make_test_img -b "$TEST_IMG".base 64M -F $IMGFMT
$QEMU_IO -c "write -P 0x22 0 3M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
echo
# backing file"
$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base -n "$TEST_IMG" "$TEST_IMG".orig
+echo
+echo '=== -n incompatible with -o ==='
+echo
+
+$QEMU_IMG convert -O $IMGFMT -o preallocation=metadata -n \
+ "$TEST_IMG" "$TEST_IMG".orig && echo "unexpected success"
+
# success, all done
echo '*** done'
rm -f $seq.full
=== Check allocation status regression with -B ===
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 3145728/3145728 bytes at offset 0
3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Offset Length File
=== Check that zero clusters are kept in overlay ===
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 3145728/3145728 bytes at offset 0
3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 3145728/3145728 bytes at offset 0
=== Converting to an overlay larger than its backing file ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=268435456
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=805306368 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=805306368 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
Offset Length File
wrote 1048576/1048576 bytes at offset 267386880
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
wrote 33554432/33554432 bytes at offset 0
32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 3145728/3145728 bytes at offset 0
3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
Formatting 'TEST_DIR/t.IMGFMT.orig', fmt=IMGFMT size=67108864
+
+=== -n incompatible with -o ===
+
+qemu-img: -o has no effect when skipping image creation
*** done
TOP_IMG="$TEST_DIR/image:top.$IMGFMT"
TEST_IMG=$BASE_IMG _make_test_img 64M
-TEST_IMG=$TOP_IMG _make_test_img -b ./image:base.$IMGFMT
+TEST_IMG=$TOP_IMG _make_test_img -b ./image:base.$IMGFMT -F $IMGFMT
# (1) The default cluster size depends on the image format
# (2) vmdk only supports vmdk backing files, so it always reports the
TOP_IMG="file:image:top.$IMGFMT"
TEST_IMG=$BASE_IMG _make_test_img 64M
-TEST_IMG=$TOP_IMG _make_test_img -b "$BASE_IMG"
+TEST_IMG=$TOP_IMG _make_test_img -b "$BASE_IMG" -F $IMGFMT
TEST_IMG=$TOP_IMG _img_info | grep -ve 'cluster_size' -e 'backing file format'
=== Testing relative backing filename resolution ===
Formatting 'TEST_DIR/image:base.IMGFMT', fmt=IMGFMT size=67108864
-Formatting 'TEST_DIR/image:top.IMGFMT', fmt=IMGFMT size=67108864 backing_file=./image:base.IMGFMT
+Formatting 'TEST_DIR/image:top.IMGFMT', fmt=IMGFMT size=67108864 backing_file=./image:base.IMGFMT backing_fmt=IMGFMT
image: TEST_DIR/image:top.IMGFMT
file format: IMGFMT
virtual size: 64 MiB (67108864 bytes)
backing file: ./image:base.IMGFMT (actual path: TEST_DIR/./image:base.IMGFMT)
Formatting 'base.IMGFMT', fmt=IMGFMT size=67108864
-Formatting 'file:image:top.IMGFMT', fmt=IMGFMT size=67108864 backing_file=base.IMGFMT
+Formatting 'file:image:top.IMGFMT', fmt=IMGFMT size=67108864 backing_file=base.IMGFMT backing_fmt=IMGFMT
image: ./image:top.IMGFMT
file format: IMGFMT
virtual size: 64 MiB (67108864 bytes)
IMG_SIZE=64K
_make_test_img $IMG_SIZE
-TEST_IMG="$TEST_IMG.overlay0" _make_test_img -b "$TEST_IMG" $IMG_SIZE
-TEST_IMG="$TEST_IMG.overlay1" _make_test_img -b "$TEST_IMG" $IMG_SIZE
+TEST_IMG="$TEST_IMG.overlay0" _make_test_img -b "$TEST_IMG" -F $IMGFMT $IMG_SIZE
+TEST_IMG="$TEST_IMG.overlay1" _make_test_img -b "$TEST_IMG" -F $IMGFMT $IMG_SIZE
# So that we actually have something to mirror and the job does not return
# immediately (which may be bad because then we cannot know whether the
QA output created by 127
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536
-Formatting 'TEST_DIR/t.IMGFMT.overlay0', fmt=IMGFMT size=65536 backing_file=TEST_DIR/t.IMGFMT
-Formatting 'TEST_DIR/t.IMGFMT.overlay1', fmt=IMGFMT size=65536 backing_file=TEST_DIR/t.IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT.overlay0', fmt=IMGFMT size=65536 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT.overlay1', fmt=IMGFMT size=65536 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT
wrote 42/42 bytes at offset 0
42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{ 'execute': 'qmp_capabilities' }
def setUp(self):
iotests.qemu_img('create', '-f', iotests.imgfmt, self.base_img, "1G")
- iotests.qemu_img('create', '-f', iotests.imgfmt, self.test_img, "-b", self.base_img)
+ iotests.qemu_img('create', '-f', iotests.imgfmt, self.test_img,
+ "-b", self.base_img, '-F', iotests.imgfmt)
iotests.qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x5d 1M 128M', self.test_img)
self.vm = iotests.VM().add_drive(self.test_img)
self.vm.launch()
_supported_fmt qcow2
_supported_proto generic
-_unsupported_proto vxhs
_supported_os Linux
# We are going to use lazy-refcounts
_unsupported_imgopts 'compat=0.10'
_supported_proto file
TEST_IMG="$TEST_IMG.base" _make_test_img 64M
-_make_test_img -b "$TEST_IMG.base"
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT
echo
echo "=== Check that node-name can't be changed ==="
QA output created by 133
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
=== Check that node-name can't be changed ===
_supported_fmt qcow qcow2
_supported_proto generic
-_unsupported_proto vxhs
size=128M
def addBlockDriverStateOverlay(self, node):
self.checkBlockDriverState(node, False)
iotests.qemu_img('create', '-u', '-f', iotests.imgfmt,
- '-b', base_img, new_img, '1M')
+ '-b', base_img, '-F', iotests.imgfmt, new_img, '1M')
opts = {'driver': iotests.imgfmt,
'node-name': node,
'backing': None,
_send_qemu_cmd $QEMU_HANDLE \
"$1" \
"$2" \
- | _filter_img_create_in_qmp | _filter_qmp_empty_return
+ | _filter_img_create | _filter_qmp_empty_return
# We want this to return an error because the block job is still running
_send_qemu_cmd $QEMU_HANDLE \
TEST_IMG="$TEST_DIR/b.$IMGFMT" _make_test_img 1M
-TEST_IMG="$TEST_DIR/m.$IMGFMT" _make_test_img -b "$TEST_DIR/b.$IMGFMT" 1M
-_make_test_img -b "$TEST_DIR/m.$IMGFMT" 1M
+TEST_IMG="$TEST_DIR/m.$IMGFMT" _make_test_img -b "$TEST_DIR/b.$IMGFMT" -F $IMGFMT 1M
+_make_test_img -b "$TEST_DIR/m.$IMGFMT" 1M -F $IMGFMT
_launch_qemu -nodefaults
QA output created by 141
Formatting 'TEST_DIR/b.IMGFMT', fmt=IMGFMT size=1048576
-Formatting 'TEST_DIR/m.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/b.IMGFMT
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/m.IMGFMT
+Formatting 'TEST_DIR/m.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/b.IMGFMT backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/m.IMGFMT backing_fmt=IMGFMT
{'execute': 'qmp_capabilities'}
{"return": {}}
TEST_IMG="$TEST_IMG.base" _make_test_img $size
TEST_IMG="$TEST_IMG.snap" _make_test_img $size
-_make_test_img -b "$TEST_IMG.base" $size
+_make_test_img -b "$TEST_IMG.base" $size -F $IMGFMT
echo
echo === Simple test for all cache modes ===
QA output created by 142
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
Formatting 'TEST_DIR/t.IMGFMT.snap', fmt=IMGFMT size=134217728
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
=== Simple test for all cache modes ===
echo
echo "== Creating test image =="
- _make_test_img -b "${TEST_IMG}.base"
+ _make_test_img -b "${TEST_IMG}.base" -F $IMGFMT
echo
echo "== Launching QEMU, opts: '$opts1' =="
_run_cmd $QEMU_IMG amend -o "size=$size" $L "${TEST_IMG}"
_run_cmd $QEMU_IMG commit $L "${TEST_IMG}"
_run_cmd $QEMU_IMG resize $L "${TEST_IMG}" $size
- _run_cmd $QEMU_IMG rebase $L "${TEST_IMG}" -b "${TEST_IMG}.base"
+ _run_cmd $QEMU_IMG rebase $L "${TEST_IMG}" -b "${TEST_IMG}.base" -F $IMGFMT
_run_cmd $QEMU_IMG snapshot -l $L "${TEST_IMG}"
_run_cmd $QEMU_IMG convert $L "${TEST_IMG}" "${TEST_IMG}.convert"
_run_cmd $QEMU_IMG dd $L if="${TEST_IMG}" of="${TEST_IMG}.convert" bs=512 count=1
# qemu-img create does not support -U
if [ -z "$L" ]; then
_run_cmd $QEMU_IMG create -f $IMGFMT "${TEST_IMG}" \
- -b ${TEST_IMG}.base
+ -b ${TEST_IMG}.base -F $IMGFMT
# Read the file format. It used to be the case that
# file-posix simply truncated the file, but the qcow2
# driver then failed to format it because it was unable
echo
echo "== Creating ${TEST_IMG}.[abc] ==" | _filter_testdir
-$QEMU_IMG create -f qcow2 "${TEST_IMG}.a" -b "${TEST_IMG}" | _filter_img_create
-$QEMU_IMG create -f qcow2 "${TEST_IMG}.b" -b "${TEST_IMG}" | _filter_img_create
-$QEMU_IMG create -f qcow2 "${TEST_IMG}.c" -b "${TEST_IMG}.b" \
+$QEMU_IMG create -f qcow2 "${TEST_IMG}.a" -b "${TEST_IMG}" -F $IMGFMT | _filter_img_create
+$QEMU_IMG create -f qcow2 "${TEST_IMG}.b" -b "${TEST_IMG}" -F $IMGFMT | _filter_img_create
+$QEMU_IMG create -f qcow2 "${TEST_IMG}.c" -b "${TEST_IMG}.b" -F $IMGFMT \
| _filter_img_create
echo
_run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512'
echo "Creating overlay with qemu-img when the guest is running should be allowed"
-_run_cmd $QEMU_IMG create -f $IMGFMT -b "${TEST_IMG}" "${TEST_IMG}.overlay"
+_run_cmd $QEMU_IMG create -f $IMGFMT -b "${TEST_IMG}" -F $IMGFMT "${TEST_IMG}.overlay"
echo "== Closing an image should unlock it =="
_send_qemu_cmd $QEMU_HANDLE \
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=33554432
== Creating test image ==
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
== Launching QEMU, opts: '' ==
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
Is another process using the image [TEST_DIR/t.qcow2]?
-_qemu_img_wrapper rebase TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
+_qemu_img_wrapper rebase TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base -F qcow2
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
Is another process using the image [TEST_DIR/t.qcow2]?
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
Is another process using the image [TEST_DIR/t.qcow2]?
-_qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
+_qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base -F qcow2
qemu-img: TEST_DIR/t.qcow2: Failed to get "write" lock
Is another process using the image [TEST_DIR/t.qcow2]?
file format: IMGFMT
+backing file format: IMGFMT
== Running utility commands -U ==
qemu-img: unrecognized option '-U'
Try 'qemu-img --help' for more information
-_qemu_img_wrapper rebase -U TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
+_qemu_img_wrapper rebase -U TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base -F qcow2
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
Is another process using the image [TEST_DIR/t.qcow2]?
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=33554432
== Creating test image ==
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
== Launching QEMU, opts: 'read-only=on' ==
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
Is another process using the image [TEST_DIR/t.qcow2]?
-_qemu_img_wrapper rebase TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
+_qemu_img_wrapper rebase TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base -F qcow2
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
Is another process using the image [TEST_DIR/t.qcow2]?
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
Is another process using the image [TEST_DIR/t.qcow2]?
-_qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
+_qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base -F qcow2
qemu-img: TEST_DIR/t.qcow2: Failed to get "write" lock
Is another process using the image [TEST_DIR/t.qcow2]?
file format: IMGFMT
+backing file format: IMGFMT
== Running utility commands -U ==
qemu-img: unrecognized option '-U'
Try 'qemu-img --help' for more information
-_qemu_img_wrapper rebase -U TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
+_qemu_img_wrapper rebase -U TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base -F qcow2
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
Is another process using the image [TEST_DIR/t.qcow2]?
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=33554432
== Creating test image ==
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
== Launching QEMU, opts: 'read-only=on,force-share=on' ==
_qemu_img_wrapper resize TEST_DIR/t.qcow2 32M
-_qemu_img_wrapper rebase TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
+_qemu_img_wrapper rebase TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base -F qcow2
_qemu_img_wrapper snapshot -l TEST_DIR/t.qcow2
_qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2
-_qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
+_qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base -F qcow2
file format: IMGFMT
+backing file format: IMGFMT
== Running utility commands -U ==
qemu-img: unrecognized option '-U'
Try 'qemu-img --help' for more information
-_qemu_img_wrapper rebase -U TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base
+_qemu_img_wrapper rebase -U TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base -F qcow2
_qemu_img_wrapper snapshot -l -U TEST_DIR/t.qcow2
== Two devices with the same image (read-only=on,force-share=on - read-only=on,force-share=on) ==
== Creating TEST_DIR/t.qcow2.[abc] ==
-Formatting 'TEST_DIR/t.IMGFMT.a', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT
-Formatting 'TEST_DIR/t.IMGFMT.b', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT
-Formatting 'TEST_DIR/t.IMGFMT.c', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.b
+Formatting 'TEST_DIR/t.IMGFMT.a', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT.b', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT.c', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.b backing_fmt=IMGFMT
== Two devices sharing the same file in backing chain ==
Is another process using the image [TEST_DIR/t.qcow2]?
Creating overlay with qemu-img when the guest is running should be allowed
-_qemu_img_wrapper create -f qcow2 -b TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.overlay
+_qemu_img_wrapper create -f qcow2 -b TEST_DIR/t.qcow2 -F qcow2 TEST_DIR/t.qcow2.overlay
== Closing an image should unlock it ==
{ 'execute': 'human-monitor-command', 'arguments': { 'command-line': 'drive_del d0' } }
{"return": ""}
echo == backing file contains zeros ==
CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $size
-_make_test_img -b "$TEST_IMG.base"
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT
# Make sure that the whole cluster is allocated even for partial write_zeroes
# when the backing file contains zeros
echo == backing file contains non-zero data before write_zeroes ==
CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $size
-_make_test_img -b "$TEST_IMG.base"
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT
# Single cluster; non-zero data at the cluster start
# ... | XX -- 00 -- | ...
echo == backing file contains non-zero data after write_zeroes ==
CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $size
-_make_test_img -b "$TEST_IMG.base"
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT
# Single cluster; non-zero data directly after request
# ... | -- 00 XX -- | ...
echo == write_zeroes covers non-zero data ==
CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $size
-_make_test_img -b "$TEST_IMG.base"
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT
# non-zero data at front of request
# Backing file: -- XX -- --
echo == spanning two clusters, non-zero before request ==
CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $size
-_make_test_img -b "$TEST_IMG.base"
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT
# Two clusters; non-zero data before request:
# 1. At cluster start: 32k: XX -- -- 00 | 00 -- -- --
echo == spanning two clusters, non-zero after request ==
CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $size
-_make_test_img -b "$TEST_IMG.base"
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT
# Two clusters; non-zero data after request:
# 1. Directly after request: 32k: -- -- -- 00 | 00 XX -- --
echo == spanning two clusters, partially overwriting backing file ==
CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $size
-_make_test_img -b "$TEST_IMG.base"
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT
# Backing file: -- -- XX XX | XX XX -- --
# Active layer: -- -- XX 00 | 00 XX -- --
echo == spanning multiple clusters, non-zero in first cluster ==
CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $size
-_make_test_img -b "$TEST_IMG.base"
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT
# Backing file: 64k: XX XX -- -- | -- -- -- -- | -- -- -- --
# Active layer: 64k: XX XX 00 00 | 00 00 00 00 | 00 -- -- --
echo == spanning multiple clusters, non-zero in intermediate cluster ==
CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $size
-_make_test_img -b "$TEST_IMG.base"
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT
# Backing file: 64k: -- -- -- -- | -- XX XX -- | -- -- -- --
# Active layer: 64k: -- -- 00 00 | 00 00 00 00 | 00 -- -- --
echo == spanning multiple clusters, non-zero in final cluster ==
CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $size
-_make_test_img -b "$TEST_IMG.base"
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT
# Backing file: 64k: -- -- -- -- | -- -- -- -- | -- -- XX XX
# Active layer: 64k: -- -- 00 00 | 00 00 00 00 | 00 -- XX XX
echo == spanning multiple clusters, partially overwriting backing file ==
CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $size
-_make_test_img -b "$TEST_IMG.base"
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT
# Backing file: 64k: -- XX XX XX | XX XX XX XX | XX XX XX --
# Active layer: 64k: -- XX 00 00 | 00 00 00 00 | 00 XX XX --
# Write at the front: sector-wise, the request is:
# backing: 128m... | -- --
# active: 128m... | 00 -- -- --
-_make_test_img -b "$TEST_IMG.base" $((size + 2048))
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT $((size + 2048))
$QEMU_IO -c "write -z $size 512" "$TEST_IMG" | _filter_qemu_io
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
# Write at the back: sector-wise, the request is:
# backing: 128m... | -- --
# active: 128m... | -- -- -- 00
-_make_test_img -b "$TEST_IMG.base" $((size + 2048))
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT $((size + 2048))
$QEMU_IO -c "write -z $((size + 1536)) 512" "$TEST_IMG" | _filter_qemu_io
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
# Write at middle: sector-wise, the request is:
# backing: 128m... | -- --
# active: 128m... | -- 00 00 --
-_make_test_img -b "$TEST_IMG.base" $((size + 2048))
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT $((size + 2048))
$QEMU_IO -c "write -z $((size + 512)) 1024" "$TEST_IMG" | _filter_qemu_io
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
# Write entire cluster: sector-wise, the request is:
# backing: 128m... | -- --
# active: 128m... | 00 00 00 00
-_make_test_img -b "$TEST_IMG.base" $((size + 2048))
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT $((size + 2048))
$QEMU_IO -c "write -z $size 2048" "$TEST_IMG" | _filter_qemu_io
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
# Write at the front: sector-wise, the request is:
# backing: 128m... | 00 00
# active: 128m... | 00 -- -- --
-_make_test_img -b "$TEST_IMG.base" $((size + 2048))
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT $((size + 2048))
$QEMU_IO -c "write -z $size 512" "$TEST_IMG" | _filter_qemu_io
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
# Write at the back: sector-wise, the request is:
# backing: 128m... | 00 00
# active: 128m... | -- -- -- 00
-_make_test_img -b "$TEST_IMG.base" $((size + 2048))
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT $((size + 2048))
$QEMU_IO -c "write -z $((size + 1536)) 512" "$TEST_IMG" | _filter_qemu_io
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
# Write at middle: sector-wise, the request is:
# backing: 128m... | 00 00
# active: 128m... | -- 00 00 --
-_make_test_img -b "$TEST_IMG.base" $((size + 2048))
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT $((size + 2048))
$QEMU_IO -c "write -z $((size + 512)) 1024" "$TEST_IMG" | _filter_qemu_io
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
# Write entire cluster: sector-wise, the request is:
# backing: 128m... | 00 00
# active: 128m... | 00 00 00 00
-_make_test_img -b "$TEST_IMG.base" $((size + 2048))
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT $((size + 2048))
$QEMU_IO -c "write -z $size 2048" "$TEST_IMG" | _filter_qemu_io
$QEMU_IO -c "alloc $size 2048" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
# Backing file: 128m... | XX --
# Active layer: 128m... | -- -- 00 --
CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $((size + 1024))
-_make_test_img -b "$TEST_IMG.base" $((size + 2048))
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT $((size + 2048))
$QEMU_IO -c "write -P 1 $((size)) 512" "$TEST_IMG.base" | _filter_qemu_io
$QEMU_IO -c "write -z $((size + 1024)) 512" "$TEST_IMG" | _filter_qemu_io
$QEMU_IO -c "read -P 1 $((size)) 512" "$TEST_IMG" | _filter_qemu_io
# Backing file: 128m: ... | -- XX
# Active layer: 128m: ... | 00 -- -- 00
CLUSTER_SIZE=512 TEST_IMG="$TEST_IMG.base" _make_test_img $((size + 1024))
-_make_test_img -b "$TEST_IMG.base" $((size + 2048))
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT $((size + 2048))
$QEMU_IO -c "write -P 1 $((size + 512)) 512" "$TEST_IMG.base" | _filter_qemu_io
$QEMU_IO -c "write -z $((size)) 512" "$TEST_IMG" | _filter_qemu_io
$QEMU_IO -c "read -P 0 $((size)) 512" "$TEST_IMG" | _filter_qemu_io
== backing file contains zeros ==
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 2048/2048 bytes at offset 0
2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 2048/2048 bytes at offset 10240
== backing file contains non-zero data before write_zeroes ==
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 1024/1024 bytes at offset 32768
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 1024/1024 bytes at offset 34816
== backing file contains non-zero data after write_zeroes ==
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 1024/1024 bytes at offset 34816
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 1024/1024 bytes at offset 33792
== write_zeroes covers non-zero data ==
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 1024/1024 bytes at offset 5120
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 2048/2048 bytes at offset 5120
== spanning two clusters, non-zero before request ==
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 1024/1024 bytes at offset 32768
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 2048/2048 bytes at offset 35840
== spanning two clusters, non-zero after request ==
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 1024/1024 bytes at offset 37888
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 2048/2048 bytes at offset 35840
== spanning two clusters, partially overwriting backing file ==
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 4096/4096 bytes at offset 2048
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 2048/2048 bytes at offset 3072
== spanning multiple clusters, non-zero in first cluster ==
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 2048/2048 bytes at offset 65536
2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 7168/7168 bytes at offset 67584
== spanning multiple clusters, non-zero in intermediate cluster ==
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 2048/2048 bytes at offset 70656
2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 7168/7168 bytes at offset 67584
== spanning multiple clusters, non-zero in final cluster ==
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 2048/2048 bytes at offset 75776
2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 7168/7168 bytes at offset 67584
== spanning multiple clusters, partially overwriting backing file ==
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 10240/10240 bytes at offset 66560
10 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 7168/7168 bytes at offset 67584
2048/2048 bytes allocated at offset 128 MiB
[{ "start": 0, "length": 134219776, "depth": 0, "zero": true, "data": false}]
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134218752
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 512/512 bytes at offset 134217728
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
2048/2048 bytes allocated at offset 128 MiB
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
{ "start": 134217728, "length": 2048, "depth": 0, "zero": true, "data": false}]
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 512/512 bytes at offset 134219264
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
2048/2048 bytes allocated at offset 128 MiB
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
{ "start": 134217728, "length": 2048, "depth": 0, "zero": true, "data": false}]
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 1024/1024 bytes at offset 134218240
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
2048/2048 bytes allocated at offset 128 MiB
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
{ "start": 134217728, "length": 2048, "depth": 0, "zero": true, "data": false}]
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 2048/2048 bytes at offset 134217728
2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
2048/2048 bytes allocated at offset 128 MiB
{ "start": 134217728, "length": 2048, "depth": 0, "zero": true, "data": false}]
wrote 512/512 bytes at offset 134217728
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 512/512 bytes at offset 134217728
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
2048/2048 bytes allocated at offset 128 MiB
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
{ "start": 134217728, "length": 2048, "depth": 0, "zero": true, "data": false}]
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 512/512 bytes at offset 134219264
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
2048/2048 bytes allocated at offset 128 MiB
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
{ "start": 134217728, "length": 2048, "depth": 0, "zero": true, "data": false}]
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 1024/1024 bytes at offset 134218240
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
2048/2048 bytes allocated at offset 128 MiB
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
{ "start": 134217728, "length": 2048, "depth": 0, "zero": true, "data": false}]
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 2048/2048 bytes at offset 134217728
2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
2048/2048 bytes allocated at offset 128 MiB
== unaligned image tail cluster, allocation required ==
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134218752
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 512/512 bytes at offset 134217728
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 512/512 bytes at offset 134218752
[{ "start": 0, "length": 134217728, "depth": 1, "zero": true, "data": false},
{ "start": 134217728, "length": 2048, "depth": 0, "zero": false, "data": true, "offset": OFFSET}]
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134218752
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134219776 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 512/512 bytes at offset 134218240
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 512/512 bytes at offset 134217728
def setUp(self):
qemu_img('create', '-f', iotests.imgfmt, back0_img, '1440K')
- qemu_img('create', '-f', iotests.imgfmt, '-b', back0_img, back1_img)
- qemu_img('create', '-f', iotests.imgfmt, '-b', back1_img, back2_img)
- qemu_img('create', '-f', iotests.imgfmt, '-b', back2_img, source_img)
+ qemu_img('create', '-f', iotests.imgfmt, '-b', back0_img,
+ '-F', iotests.imgfmt, back1_img)
+ qemu_img('create', '-f', iotests.imgfmt, '-b', back1_img,
+ '-F', iotests.imgfmt, back2_img)
+ qemu_img('create', '-f', iotests.imgfmt, '-b', back2_img,
+ '-F', iotests.imgfmt, source_img)
self.vm = iotests.VM()
# Add the BDS via blockdev-add so it stays around after the mirror block
if self.existing:
if self.target_backing:
qemu_img('create', '-f', iotests.imgfmt,
- '-b', self.target_backing, target_img, '1440K')
+ '-b', self.target_backing, '-F', 'raw',
+ target_img, '1440K')
else:
qemu_img('create', '-f', iotests.imgfmt, target_img, '1440K')
_supported_fmt qcow2 qed
_supported_proto generic
-_unsupported_proto vxhs
# Copying files around with cp does not work with external data files
_unsupported_imgopts data_file
# Create source disk
TEST_IMG="$TEST_IMG.backing" _make_test_img 1M
-_make_test_img -b "$TEST_IMG.backing" 1M
+_make_test_img -b "$TEST_IMG.backing" -F $IMGFMT 1M
$QEMU_IO -c 'write -P 1 0 256k' "$TEST_IMG.backing" | _filter_qemu_io
$QEMU_IO -c 'write -P 2 64k 192k' "$TEST_IMG" | _filter_qemu_io
'return'
# Create snapshot
-TEST_IMG="$TEST_IMG.overlay" _make_test_img -u -b "$TEST_IMG" 1M
+TEST_IMG="$TEST_IMG.overlay" _make_test_img -u -b "$TEST_IMG" -F $IMGFMT 1M
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'blockdev-snapshot-sync',
'arguments': { 'device': 'source',
'return'
# Create target image
-TEST_IMG="$TEST_IMG.target.overlay" _make_test_img -u -b "$TEST_IMG.target" 1M
+TEST_IMG="$TEST_IMG.target.overlay" _make_test_img -u -b "$TEST_IMG.target" \
+ -F $IMGFMT 1M
# Mirror snapshot
_send_qemu_cmd $QEMU_HANDLE \
# Copy source backing chain to the target before completing the job
cp "$TEST_IMG.backing" "$TEST_IMG.target.backing"
cp "$TEST_IMG" "$TEST_IMG.target"
-$QEMU_IMG rebase -u -b "$TEST_IMG.target.backing" "$TEST_IMG.target"
+$QEMU_IMG rebase -u -b "$TEST_IMG.target.backing" -F $IMGFMT "$TEST_IMG.target"
# Complete block job
_send_qemu_cmd $QEMU_HANDLE \
QA output created by 156
Formatting 'TEST_DIR/t.IMGFMT.backing', fmt=IMGFMT size=1048576
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.backing
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.backing backing_fmt=IMGFMT
wrote 262144/262144 bytes at offset 0
256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 196608/196608 bytes at offset 65536
192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{ 'execute': 'qmp_capabilities' }
{"return": {}}
-Formatting 'TEST_DIR/t.IMGFMT.overlay', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT.overlay', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT
{ 'execute': 'blockdev-snapshot-sync', 'arguments': { 'device': 'source', 'snapshot-file': 'TEST_DIR/t.IMGFMT.overlay', 'format': 'IMGFMT', 'mode': 'existing' } }
{"return": {}}
{ 'execute': 'human-monitor-command', 'arguments': { 'command-line': 'qemu-io source "write -P 3 128k 128k"' } }
wrote 131072/131072 bytes at offset 131072
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{"return": ""}
-Formatting 'TEST_DIR/t.IMGFMT.target.overlay', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.target
+Formatting 'TEST_DIR/t.IMGFMT.target.overlay', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.target backing_fmt=IMGFMT
{ 'execute': 'drive-mirror', 'arguments': { 'device': 'source', 'target': 'TEST_DIR/t.IMGFMT.target.overlay', 'mode': 'existing', 'sync': 'top' } }
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "source"}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "source"}}
_supported_fmt qcow qcow2
_supported_proto generic
-_unsupported_proto vxhs
size=128M
$QEMU_IO --object $SECRET -c "read -P 0xa 0 $size" --image-opts $IMGSPECBASE | _filter_qemu_io | _filter_testdir
echo "== create overlay =="
-_make_test_img -u --object $SECRET -o "encryption=on,encrypt.key-secret=sec0" -b "$TEST_IMG_BASE" $size
+_make_test_img -u --object $SECRET -o "encryption=on,encrypt.key-secret=sec0" -b "$TEST_IMG_BASE" -F $IMGFMT $size
echo
echo "== writing part of a cluster =="
read 134217728/134217728 bytes at offset 0
128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
== create overlay ==
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base encryption=on
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT encryption=on
== writing part of a cluster ==
wrote 1024/1024 bytes at offset 0
# Create the images
TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE | _filter_imgfmt
-TEST_IMG="$TEST_IMG.int" _make_test_img -b "$TEST_IMG.base" | _filter_imgfmt
-_make_test_img -b "$TEST_IMG.int" | _filter_imgfmt
+TEST_IMG="$TEST_IMG.int" _make_test_img -b "$TEST_IMG.base" -F $IMGFMT | _filter_imgfmt
+_make_test_img -b "$TEST_IMG.int" -F $IMGFMT -F $IMGFMT | _filter_imgfmt
# First test: reopen $TEST.IMG changing the detect-zeroes option on
# its backing file ($TEST_IMG.int).
echo
# Create the images again
TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE | _filter_imgfmt
-TEST_IMG="$TEST_IMG.int" _make_test_img -b "$TEST_IMG.base" | _filter_imgfmt
-_make_test_img -b "$TEST_IMG.int" | _filter_imgfmt
+TEST_IMG="$TEST_IMG.int" _make_test_img -b "$TEST_IMG.base" -F $IMGFMT| _filter_imgfmt
+_make_test_img -b "$TEST_IMG.int" -F $IMGFMT | _filter_imgfmt
_launch_qemu -drive if=none,file="${TEST_IMG}"
_send_qemu_cmd $QEMU_HANDLE \
QA output created by 161
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=1048576
-Formatting 'TEST_DIR/t.IMGFMT.int', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.base
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.int
+Formatting 'TEST_DIR/t.IMGFMT.int', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.int backing_fmt=IMGFMT
*** Change an option on the backing file
*** Commit and then change an option on the backing file
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=1048576
-Formatting 'TEST_DIR/t.IMGFMT.int', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.base
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.int
+Formatting 'TEST_DIR/t.IMGFMT.int', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.int backing_fmt=IMGFMT
{ 'execute': 'qmp_capabilities' }
{"return": {}}
{ 'execute': 'block-commit', 'arguments': { 'device': 'none0', 'top': 'TEST_DIR/t.IMGFMT.int' } }
echo
echo "== creating image with default preallocation =="
-_make_test_img $size | _filter_imgfmt
+_make_test_img -o extent_size_hint=0 $size | _filter_imgfmt
stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $min_blocks $size
for mode in off full falloc; do
echo
echo "== creating image with preallocation $mode =="
- _make_test_img -o preallocation=$mode $size | _filter_imgfmt
+ _make_test_img -o preallocation=$mode,extent_size_hint=0 $size | _filter_imgfmt
stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $min_blocks $size
done
for new_size in 4096 1048576; do
echo
echo "== resize empty image with block_resize =="
- _make_test_img 0 | _filter_imgfmt
+ _make_test_img -o extent_size_hint=0 0 | _filter_imgfmt
_block_resize $TEST_IMG $new_size >/dev/null
stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $min_blocks $new_size
done
len=$((2100 * 1024 * 1024 + 512)) # larger than 2G, and not cluster aligned
TEST_IMG="$TEST_IMG.base" _make_test_img $len
-TEST_IMG="$TEST_IMG.itmd" _make_test_img -b "$TEST_IMG.base" $len
-_make_test_img -b "$TEST_IMG.itmd" $len
+TEST_IMG="$TEST_IMG.itmd" _make_test_img -b "$TEST_IMG.base" -F $IMGFMT $len
+_make_test_img -b "$TEST_IMG.itmd" -F $IMGFMT $len
# Update the top image to use a feature that is incompatible with fast path
case $reason in
snapshot) $QEMU_IMG snapshot -c snap "$TEST_IMG" ;;
=== Test pass snapshot.0 ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112
-Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd
+Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd backing_fmt=IMGFMT
wrote 196608/196608 bytes at offset 2147287040
192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 131072/131072 bytes at offset 2147352576
=== Test pass snapshot.1 ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112
-Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd
+Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd backing_fmt=IMGFMT
wrote 196608/196608 bytes at offset 2147287040
192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 131072/131072 bytes at offset 2147352576
=== Test pass snapshot.2 ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112
-Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd
+Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd backing_fmt=IMGFMT
wrote 196608/196608 bytes at offset 2147287040
192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 131072/131072 bytes at offset 2147352576
=== Test pass snapshot.3 ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112
-Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd
+Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd backing_fmt=IMGFMT
wrote 196608/196608 bytes at offset 2147287040
192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 131072/131072 bytes at offset 2147352576
=== Test pass bitmap.0 ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112
-Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd
+Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd backing_fmt=IMGFMT
QMP_VERSION
{"return": {}}
{"return": {}}
=== Test pass bitmap.1 ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112
-Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd
+Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd backing_fmt=IMGFMT
QMP_VERSION
{"return": {}}
{"return": {}}
=== Test pass bitmap.2 ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112
-Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd
+Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd backing_fmt=IMGFMT
QMP_VERSION
{"return": {}}
{"return": {}}
=== Test pass bitmap.3 ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112
-Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd
+Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd backing_fmt=IMGFMT
QMP_VERSION
{"return": {}}
{"return": {}}
TEST_IMG="$TEST_IMG.base" _make_test_img $size
$QEMU_IO -c "write -P 11 0 $size" "$TEST_IMG.base" | _filter_qemu_io
-_make_test_img -b "$TEST_IMG.base"
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT
$QEMU_IO -c "write -P 22 0 $size" "$TEST_IMG" | _filter_qemu_io
# Limited to 64k max-transfer
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
wrote 134217728/134217728 bytes at offset 0
128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 134217728/134217728 bytes at offset 0
128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
echo
TEST_IMG="$TEST_IMG.base" _make_test_img 64M
-_make_test_img -b "$TEST_IMG.base"
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT
# Offsets chosen at or near 2M boundaries so test works at all cluster sizes
# 8k and larger (smaller clusters fail due to non-contiguous allocations)
=== Testing write zeroes with unmap ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 2097152/2097152 bytes at offset 2097152
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 2097152/2097152 bytes at offset 6291456
$QEMU_IO --object $SECRET0 -c "read -P 0xa 0 $size" --image-opts $IMGSPECBASE | _filter_qemu_io | _filter_testdir
echo "== create overlay =="
-_make_test_img --object $SECRET1 -o "encrypt.format=luks,encrypt.key-secret=sec1,encrypt.iter-time=10" -u -b "$TEST_IMG_BASE" $size
+_make_test_img --object $SECRET1 -o "encrypt.format=luks,encrypt.key-secret=sec1,encrypt.iter-time=10" -u -b "$TEST_IMG_BASE" -F $IMGFMT $size
echo
echo "== writing part of a cluster =="
read 16777216/16777216 bytes at offset 0
16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
== create overlay ==
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
== writing part of a cluster ==
wrote 1024/1024 bytes at offset 0
TEST_IMG="${TEST_IMG}.base" _make_test_img $size
TEST_IMG="${TEST_IMG}.mid" _make_test_img -o "backing_fmt=$IMGFMT" -b "${TEST_IMG}.base"
-_make_test_img -b "${TEST_IMG}.mid"
-TEST_IMG="${TEST_IMG}.ovl2" _make_test_img -b "${TEST_IMG}.mid"
+_make_test_img -b "${TEST_IMG}.mid" -F $IMGFMT
+TEST_IMG="${TEST_IMG}.ovl2" _make_test_img -b "${TEST_IMG}.mid" -F $IMGFMT
$QEMU_IO -c 'write -P 0x55 1M 64k' "${TEST_IMG}.mid" | _filter_qemu_io
echo
TEST_IMG="${TEST_IMG}.base" _make_test_img $size
-TEST_IMG="${TEST_IMG}.mid" _make_test_img -b "${TEST_IMG}.base"
-_make_test_img -b "${TEST_IMG}.mid"
-TEST_IMG="${TEST_IMG}.ovl2" _make_test_img -b "${TEST_IMG}.mid"
-TEST_IMG="${TEST_IMG}.ovl3" _make_test_img -b "${TEST_IMG}.ovl2"
+TEST_IMG="${TEST_IMG}.mid" _make_test_img -b "${TEST_IMG}.base" -F $IMGFMT
+_make_test_img -b "${TEST_IMG}.mid" -F $IMGFMT
+TEST_IMG="${TEST_IMG}.ovl2" _make_test_img -b "${TEST_IMG}.mid" -F $IMGFMT
+TEST_IMG="${TEST_IMG}.ovl3" _make_test_img -b "${TEST_IMG}.ovl2" -F $IMGFMT
$QEMU_IO -c 'write -P 0x55 1M 64k' "${TEST_IMG}.mid" | _filter_qemu_io
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
Formatting 'TEST_DIR/t.IMGFMT.mid', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.mid
-Formatting 'TEST_DIR/t.IMGFMT.ovl2', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.mid
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.mid backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT.ovl2', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.mid backing_fmt=IMGFMT
wrote 65536/65536 bytes at offset 1048576
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{ 'execute': 'qmp_capabilities' }
=== Preparing and starting VM with -drive ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
-Formatting 'TEST_DIR/t.IMGFMT.mid', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.mid
-Formatting 'TEST_DIR/t.IMGFMT.ovl2', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.mid
-Formatting 'TEST_DIR/t.IMGFMT.ovl3', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.ovl2
+Formatting 'TEST_DIR/t.IMGFMT.mid', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.mid backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT.ovl2', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.mid backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT.ovl3', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.ovl2 backing_fmt=IMGFMT
wrote 65536/65536 bytes at offset 1048576
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{ 'execute': 'qmp_capabilities' }
size=64M
TEST_IMG="$TEST_IMG.base" _make_test_img $size
-TEST_IMG="$TEST_IMG.mid" _make_test_img -b "$TEST_IMG.base"
-_make_test_img -b "$TEST_IMG.mid"
+TEST_IMG="$TEST_IMG.mid" _make_test_img -b "$TEST_IMG.base" -F $IMGFMT
+_make_test_img -b "$TEST_IMG.mid" -F $IMGFMT
echo
echo "Change backing file of mid (opened read-only)"
echo "Change backing file of top (opened writable)"
echo
-TEST_IMG="$TEST_IMG.mid" _make_test_img -b "$TEST_IMG.base"
+TEST_IMG="$TEST_IMG.mid" _make_test_img -b "$TEST_IMG.base" -F $IMGFMT
run_qemu -drive if=none,file="$TEST_IMG",node-name=top <<EOF
{"execute":"qmp_capabilities"}
QA output created by 195
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
-Formatting 'TEST_DIR/t.IMGFMT.mid', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.mid
+Formatting 'TEST_DIR/t.IMGFMT.mid', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.mid backing_fmt=IMGFMT
Change backing file of mid (opened read-only)
Change backing file of top (opened writable)
-Formatting 'TEST_DIR/t.IMGFMT.mid', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT.mid', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,node-name=top
{
QMP_VERSION
$QEMU_IO --object $SECRET0 -c "write -P 0xa 0 $size" --image-opts $IMGSPECBASE | _filter_qemu_io | _filter_testdir
echo "== create overlay =="
-_make_test_img --object $SECRET1 -o "encrypt.format=luks,encrypt.key-secret=sec1,encrypt.iter-time=10" -u -b "$TEST_IMG_BASE" $size
+_make_test_img --object $SECRET1 -o "encrypt.format=luks,encrypt.key-secret=sec1,encrypt.iter-time=10" -u -b "$TEST_IMG_BASE" -F $IMGFMT $size
echo
echo "== writing whole image layer =="
wrote 16777216/16777216 bytes at offset 0
16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
== create overlay ==
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
== writing whole image layer ==
wrote 16777216/16777216 bytes at offset 0
file format: IMGFMT
virtual size: 16 MiB (16777216 bytes)
backing file: TEST_DIR/t.IMGFMT.base
+backing file format: IMGFMT
Format specific information:
compression type: zlib
encrypt:
TEST_IMG="$TEST_IMG.base" _make_test_img $size
$QEMU_IO -c "write -P 11 0 $size" "$TEST_IMG.base" | _filter_qemu_io
-_make_test_img -b "$TEST_IMG.base"
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT
$QEMU_IO -c "write -P 22 0 110M" "$TEST_IMG" | _filter_qemu_io
# Limited to 64k max-transfer
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
wrote 134217728/134217728 bytes at offset 0
128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
wrote 115343360/115343360 bytes at offset 0
110 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0
assert qemu_io_silent(base_img_path, '-c', 'write -P 1 0M 1M') == 0
assert qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path,
- top_img_path) == 0
+ '-F', iotests.imgfmt, top_img_path) == 0
assert qemu_io_silent(top_img_path, '-c', 'write -P 2 1M 1M') == 0
log('Done')
assert qemu_img('create', '-f', iotests.imgfmt,
base_img_path, '64M') == 0
assert qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path,
- mid_img_path) == 0
+ '-F', iotests.imgfmt, mid_img_path) == 0
assert qemu_img('create', '-f', iotests.imgfmt, '-b', mid_img_path,
- top_img_path) == 0
+ '-F', iotests.imgfmt, top_img_path) == 0
# Something to commit
assert qemu_io_silent(mid_img_path, '-c', 'write -P 1 0 1M') == 0
TEST_IMG="$TEST_IMG.base" _make_test_img 1M
TEST_IMG="$TEST_IMG.not_base" _make_test_img 1M
-_make_test_img -b "$TEST_IMG.base"
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT
make_opts()
{
QA output created by 225
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=1048576
Formatting 'TEST_DIR/t.IMGFMT.not_base', fmt=IMGFMT size=1048576
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
=== Testing fitting VMDK backing image ===
assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0
# Choose a funny way to describe the backing filename
assert qemu_img('create', '-f', iotests.imgfmt, '-b',
- 'file:' + base_img_path, top_img_path) == 0
+ 'file:' + base_img_path, '-F', iotests.imgfmt,
+ top_img_path) == 0
vm.launch()
# filename unless the backing file is opened implicitly with the
# overlay)
assert qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path,
- top_img_path) == 0
+ '-F', iotests.imgfmt, top_img_path) == 0
# You can only reliably override backing options by using a node
# reference (or by specifying file.filename, but, well...)
echo "=== preallocation=$mode ==="
echo
- _make_test_img -o "preallocation=$mode" 64M
+ _make_test_img -o "preallocation=$mode,extent_size_hint=0" 64M
printf "File size: "
du -b $TEST_IMG | cut -f1
echo "=== External data file: preallocation=$mode ==="
echo
- _make_test_img -o "data_file=$TEST_IMG.data,preallocation=$mode" 64M
+ _make_test_img \
+ -o "data_file=$TEST_IMG.data,preallocation=$mode,extent_size_hint=0" 64M
echo -n "qcow2 file size: "
du -b $TEST_IMG | cut -f1
echo -n "qcow2 disk usage: "
[ $(du -B1 $TEST_IMG | cut -f1) -lt 1048576 ] && echo "low" || echo "high"
echo -n "data disk usage: "
- [ $(du -B1 $TEST_IMG.data | cut -f1) -lt 1048576 ] && echo "low" || echo "high"
+ [ $(du -B1 $TEST_IMG.data | cut -f1) -lt 2097152 ] && echo "low" || echo "high"
done
def setUp(self):
qemu_img('create', '-f', iotests.imgfmt, hd_path[0], '3M')
- qemu_img('create', '-f', iotests.imgfmt, '-b', hd_path[0], hd_path[1])
+ qemu_img('create', '-f', iotests.imgfmt, '-b', hd_path[0],
+ '-F', iotests.imgfmt, hd_path[1])
qemu_img('create', '-f', iotests.imgfmt, hd_path[2], '3M')
qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xa0 0 1M', hd_path[0])
qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xa1 1M 1M', hd_path[1])
# Create the images: base <- int <- active
TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE | _filter_imgfmt
-TEST_IMG="$TEST_IMG.int" _make_test_img -b "$TEST_IMG.base" | _filter_imgfmt
-_make_test_img -b "$TEST_IMG.int" | _filter_imgfmt
+TEST_IMG="$TEST_IMG.int" _make_test_img -b "$TEST_IMG.base" -F $IMGFMT | _filter_imgfmt
+_make_test_img -b "$TEST_IMG.int" -F $IMGFMT | _filter_imgfmt
# Launch QEMU with these two drives:
# none0: base (read-only)
QA output created by 249
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=1048576
-Formatting 'TEST_DIR/t.IMGFMT.int', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.base
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.int
+Formatting 'TEST_DIR/t.IMGFMT.int', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.int backing_fmt=IMGFMT
{ 'execute': 'qmp_capabilities' }
{"return": {}}
echo
-$QEMU_IMG rebase -b "$TEST_IMG.base_new" "$TEST_IMG"
+$QEMU_IMG rebase -b "$TEST_IMG.base_new" -F $IMGFMT "$TEST_IMG"
# Verify the data is correct
$QEMU_IO "$TEST_IMG" \
"""
expected_ret = 0 if expected_match else 1
if baseimg:
- assert qemu_img("rebase", "-u", "-b", baseimg, image) == 0
+ assert qemu_img("rebase", "-u", "-b", baseimg, '-F', iotests.imgfmt,
+ image) == 0
ret = qemu_img("compare", image, reference)
log('qemu_img compare "{:s}" "{:s}" ==> {:s}, {:s}'.format(
image, reference,
disk size: unavailable
--- Testing creation for which the node would need to grow ---
-qemu-img: TEST_DIR/t.IMGFMT: Could not resize image: Image format driver does not support resize
Formatting 'TEST_DIR/t.IMGFMT', fmt=qcow2 size=67108864 preallocation=metadata
+qemu-img: TEST_DIR/t.IMGFMT: Could not resize image: Image format driver does not support resize
*** done
run_test()
{
if [ -n "$BACKING_FILE" ]; then
- _make_test_img -b "$BACKING_FILE" $size
+ _make_test_img -b "$BACKING_FILE" -F $IMGFMT $size
else
_make_test_img $size
fi
echo "=== -blockdev with NBD server on the backing file ==="
echo
-_make_test_img -b "$TEST_IMG.base" $size
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT $size
cat <<EOF |
nbd_server_start unix:$SOCK_DIR/nbd
nbd_server_add -w backing-fmt
=== -blockdev with a backing file ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT.base,node-name=backing-file -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=file -blockdev driver=IMGFMT,file=file,backing=backing-file,node-name=fmt
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) savevm snap0
(qemu) loadvm snap0
(qemu) quit
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT.base,node-name=backing-file -blockdev driver=IMGFMT,file=backing-file,node-name=backing-fmt -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=file -blockdev driver=IMGFMT,file=file,backing=backing-fmt,node-name=fmt
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) savevm snap0
=== -blockdev with NBD server on the backing file ===
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT.base,node-name=backing-file -blockdev driver=IMGFMT,file=backing-file,node-name=backing-fmt -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=file -blockdev driver=IMGFMT,file=file,backing=backing-fmt,node-name=fmt
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) nbd_server_start unix:SOCK_DIR/nbd
# We need a large cluster size, see below for why (above the $QEMU_IO
# invocation)
_make_test_img -o cluster_size=2M,data_file="$TEST_IMG.orig" \
- -b "$TEST_IMG.base" 4G
+ -b "$TEST_IMG.base" -F $IMGFMT 4G
# We want a null-co as the data file, because it allows us to quickly
# "write" 2G of data without using any space.
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=4294967296
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 backing_file=TEST_DIR/t.IMGFMT.base data_file=TEST_DIR/t.IMGFMT.orig
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT data_file=TEST_DIR/t.IMGFMT.orig
wrote 2147483136/2147483136 bytes at offset 768
2 GiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
}
TEST_IMG="$TEST_IMG.base" _make_test_img 64M
-TEST_IMG="$TEST_IMG.mid" _make_test_img -b "$TEST_IMG.base"
-_make_test_img -b "$TEST_IMG.mid"
+TEST_IMG="$TEST_IMG.mid" _make_test_img -b "$TEST_IMG.base" -F $IMGFMT
+_make_test_img -b "$TEST_IMG.mid" -F $IMGFMT
run_qemu \
-blockdev file,node-name=base,filename="$TEST_IMG.base" \
QA output created by 273
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
-Formatting 'TEST_DIR/t.IMGFMT.mid', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.mid
+Formatting 'TEST_DIR/t.IMGFMT.mid', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.mid backing_fmt=IMGFMT
Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev file,node-name=midf,filename=TEST_DIR/t.IMGFMT.mid -blockdev {"driver":"IMGFMT","node-name":"mid","file":"midf","backing":null} -blockdev file,node-name=topf,filename=TEST_DIR/t.IMGFMT -blockdev {"driver":"IMGFMT","file":"topf","node-name":"top","backing":null}
{
QMP_VERSION
def create_chain() -> None:
iotests.qemu_img_log('create', '-f', iotests.imgfmt, base,
str(size_long))
- iotests.qemu_img_log('create', '-f', iotests.imgfmt, '-b', base, mid,
- str(size_short))
- iotests.qemu_img_log('create', '-f', iotests.imgfmt, '-b', mid, top,
- str(size_long))
+ iotests.qemu_img_log('create', '-f', iotests.imgfmt, '-b', base,
+ '-F', iotests.imgfmt, mid, str(size_short))
+ iotests.qemu_img_log('create', '-f', iotests.imgfmt, '-b', mid,
+ '-F', iotests.imgfmt, top, str(size_long))
iotests.qemu_io_log('-c', 'write -P 1 0 %d' % size_long, base)
iotests.log('=== preallocation=%s ===' % prealloc)
iotests.qemu_img_log('create', '-f', iotests.imgfmt, base, base_size)
- iotests.qemu_img_log('create', '-f', iotests.imgfmt, '-b', base, top,
- top_size_old)
+ iotests.qemu_img_log('create', '-f', iotests.imgfmt, '-b', base,
+ '-F', iotests.imgfmt, top, top_size_old)
iotests.qemu_io_log('-c', 'write -P 1 %s 64k' % off, base)
# After this, top_size_old to base_size should be allocated/zeroed.
== Commit tests ==
Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=2097152 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16
+Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid lazy_refcounts=off refcount_bits=16
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
wrote 2097152/2097152 bytes at offset 0
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
virtual size: 2 MiB (2097152 bytes)
cluster_size: 65536
backing file: TEST_DIR/PID-base
+backing file format: IMGFMT
Format specific information:
compat: 1.1
compression type: zlib
=== Testing HMP commit (top -> mid) ===
Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=2097152 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16
+Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid lazy_refcounts=off refcount_bits=16
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
wrote 2097152/2097152 bytes at offset 0
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
virtual size: 2 MiB (2097152 bytes)
cluster_size: 65536
backing file: TEST_DIR/PID-base
+backing file format: IMGFMT
Format specific information:
compat: 1.1
compression type: zlib
=== Testing QMP active commit (top -> mid) ===
Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=2097152 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16
+Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid lazy_refcounts=off refcount_bits=16
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
wrote 2097152/2097152 bytes at offset 0
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
virtual size: 2 MiB (2097152 bytes)
cluster_size: 65536
backing file: TEST_DIR/PID-base
+backing file format: IMGFMT
Format specific information:
compat: 1.1
compression type: zlib
=== preallocation=off ===
Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=6442450944 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1073741824 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1073741824 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
wrote 65536/65536 bytes at offset 5368709120
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== preallocation=metadata ===
Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=34359738368 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=32212254720 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=32212254720 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
wrote 65536/65536 bytes at offset 33285996544
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== preallocation=falloc ===
Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=10485760 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=5242880 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=5242880 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
wrote 65536/65536 bytes at offset 9437184
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== preallocation=full ===
Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=16777216 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=8388608 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=8388608 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
wrote 65536/65536 bytes at offset 11534336
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== preallocation=off ===
Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=393216 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=259072 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=259072 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
wrote 65536/65536 bytes at offset 259072
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== preallocation=off ===
Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=409600 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=262144 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=262144 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
wrote 65536/65536 bytes at offset 344064
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== preallocation=off ===
Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 compression_type=zlib size=524288 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=262144 backing_file=TEST_DIR/PID-base lazy_refcounts=off refcount_bits=16
+Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 compression_type=zlib size=262144 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
wrote 65536/65536 bytes at offset 446464
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
"subformat=twoGbMaxExtentFlat" \
TEST_IMG="$TEST_IMG.base" _make_test_img 64M
-TEST_IMG="$TEST_IMG.mid" _make_test_img -b "$TEST_IMG.base"
-_make_test_img -b "$TEST_IMG.mid"
+TEST_IMG="$TEST_IMG.mid" _make_test_img -b "$TEST_IMG.base" -F $IMGFMT
+_make_test_img -b "$TEST_IMG.mid" -F $IMGFMT
echo
echo '== qemu-img info --backing-chain =='
QA output created by 279
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
-Formatting 'TEST_DIR/t.IMGFMT.mid', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.mid
+Formatting 'TEST_DIR/t.IMGFMT.mid', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.mid backing_fmt=IMGFMT
== qemu-img info --backing-chain ==
image: TEST_DIR/t.IMGFMT
_supported_fmt luks
_supported_proto generic
-_unsupported_proto vxhs
echo "== Create non-UTF8 secret =="
echo -n -e '\x3a\x3c\x3b\xff' > non_utf8_secret
QA output created by 282
== Create non-UTF8 secret ==
== Throws an error because of invalid UTF-8 secret ==
-qemu-img: vol.img: Data from secret sec0 is not valid UTF-8
Formatting 'vol.img', fmt=luks size=4194304 key-secret=sec0
+qemu-img: vol.img: Data from secret sec0 is not valid UTF-8
== Image file should not exist after the error ==
== Create a stub image file and run qemu-img again ==
-qemu-img: vol.img: Data from secret sec0 is not valid UTF-8
Formatting 'vol.img', fmt=luks size=4194304 key-secret=sec0
+qemu-img: vol.img: Data from secret sec0 is not valid UTF-8
== Pre-existing image file should also be deleted after the error ==
- *** done
+*** done
for qcow2_compat in 0.10 1.1; do
echo "# Create an image with compat=$qcow2_compat and a backing file"
- _make_test_img -o "compat=$qcow2_compat" -b "$BACKING_IMG"
+ _make_test_img -o "compat=$qcow2_compat" -b "$BACKING_IMG" -F $IMGFMT
echo "# Fill all clusters with data and then discard them"
$QEMU_IO -c 'write -P 0x01 0 128k' "$TEST_IMG" | _filter_qemu_io
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
# Create an image with compat=0.10 and a backing file
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
# Fill all clusters with data and then discard them
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Offset Length Mapped to File
0 0x20000 0x50000 TEST_DIR/t.qcow2
# Create an image with compat=1.1 and a backing file
-Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 backing_file=TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
# Fill all clusters with data and then discard them
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
--- /dev/null
+#!/usr/bin/env python3
+#
+# Test shutdown when bitmap is exported through NBD server
+#
+# Copyright (c) 2020 Virtuozzo International GmbH.
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+
+import iotests
+
+# The test is unrelated to formats, restrict it to qcow2 to avoid extra runs
+iotests.script_initialize(
+ supported_fmts=['qcow2'],
+)
+
+nbd_sock = iotests.file_path('nbd.sock', base_dir=iotests.sock_dir)
+nbd_uri = 'nbd+unix:///disk?socket=' + nbd_sock
+size = 1024 * 1024
+
+vm = iotests.VM()
+vm.launch()
+
+vm.qmp_log('blockdev-add', **{
+ 'node-name': 'disk',
+ 'driver': 'null-co',
+ 'size': 1024 * 1024,
+})
+
+vm.qmp_log('block-dirty-bitmap-add', **{
+ 'node': 'disk',
+ 'name': 'bitmap0'
+})
+
+vm.qmp_log('nbd-server-start', **{
+ 'addr': {
+ 'type': 'unix',
+ 'data': {'path': nbd_sock}
+ }
+}, filters=[iotests.filter_qmp_testfiles])
+
+vm.qmp_log('nbd-server-add', **{
+ 'device': 'disk',
+ 'writable': True,
+ 'bitmap': 'bitmap0'
+})
+
+p = iotests.QemuIoInteractive('-f', 'raw', nbd_uri)
+# wait for connection and check it:
+iotests.log(p.cmd('read 0 512').rstrip(), filters=[iotests.filter_qemu_io])
+
+vm.shutdown()
+
+p.close()
--- /dev/null
+{"execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "disk", "size": 1048576}}
+{"return": {}}
+{"execute": "block-dirty-bitmap-add", "arguments": {"name": "bitmap0", "node": "disk"}}
+{"return": {}}
+{"execute": "nbd-server-start", "arguments": {"addr": {"data": {"path": "SOCK_DIR/PID-nbd.sock"}, "type": "unix"}}}
+{"return": {}}
+{"execute": "nbd-server-add", "arguments": {"bitmap": "bitmap0", "device": "disk", "writable": true}}
+{"return": {}}
+read 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
--- /dev/null
+#!/usr/bin/env bash
+#
+# Test qcow backing file warnings
+#
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+status=1 # failure is the default!
+
+_cleanup()
+{
+ _cleanup_test_img
+ _rm_test_img "$TEST_IMG.qcow2"
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+. ./common.rc
+. ./common.filter
+
+_supported_fmt qcow
+_supported_proto file
+_supported_os Linux
+
+size=32M
+
+echo
+echo "== qcow backed by qcow =="
+
+TEST_IMG="$TEST_IMG.base" _make_test_img $size
+_make_test_img -b "$TEST_IMG.base" $size
+_img_info
+_make_test_img -b "$TEST_IMG.base" -F $IMGFMT $size
+_img_info
+
+echo
+echo "== mismatched command line detection =="
+
+_make_test_img -b "$TEST_IMG.base" -F vmdk
+_make_test_img -b "$TEST_IMG.base" -F vmdk $size
+echo
+# Use of -u bypasses the backing format sanity check
+_make_test_img -u -b "$TEST_IMG.base" -F vmdk
+_make_test_img -u -b "$TEST_IMG.base" -F vmdk $size
+echo
+# But the format must still be recognized
+_make_test_img -b "$TEST_IMG.base" -F garbage $size
+_make_test_img -u -b "$TEST_IMG.base" -F garbage $size
+_img_info
+
+echo
+echo "== qcow backed by raw =="
+
+rm "$TEST_IMG.base"
+truncate --size=$size "$TEST_IMG.base"
+_make_test_img -b "$TEST_IMG.base" $size
+_img_info
+_make_test_img -b "$TEST_IMG.base" -F raw $size
+_img_info
+
+echo
+echo "== commit cannot change type of raw backing file =="
+TEST_IMG="$TEST_IMG.qcow2" IMGFMT=qcow2 _make_test_img $size
+truncate --size=$size "$TEST_IMG.qcow2"
+$QEMU_IMG convert -n -f raw -O $IMGFMT "$TEST_IMG.qcow2" "$TEST_IMG"
+$QEMU_IMG commit -f $IMGFMT "$TEST_IMG" && echo "unexpected success"
+TEST_IMG="$TEST_IMG.base" _img_info
+
+# success, all done
+echo "*** done"
+rm -f $seq.full
+status=0
--- /dev/null
+QA output created by 301
+
+== qcow backed by qcow ==
+Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=33554432
+qemu-img: warning: Deprecated use of backing file without explicit backing format (detected format of IMGFMT)
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
+virtual size: 32 MiB (33554432 bytes)
+cluster_size: 512
+backing file: TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
+virtual size: 32 MiB (33554432 bytes)
+cluster_size: 512
+backing file: TEST_DIR/t.IMGFMT.base
+
+== mismatched command line detection ==
+qemu-img: TEST_DIR/t.IMGFMT: invalid VMDK image descriptor
+Could not open backing image.
+qemu-img: TEST_DIR/t.IMGFMT: invalid VMDK image descriptor
+Could not open backing image.
+
+qemu-img: TEST_DIR/t.IMGFMT: Image creation needs a size parameter
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=vmdk
+
+qemu-img: TEST_DIR/t.IMGFMT: Unknown driver 'garbage'
+Could not open backing image.
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=garbage
+qemu-img: TEST_DIR/t.IMGFMT: unrecognized backing format 'garbage'
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
+virtual size: 32 MiB (33554432 bytes)
+cluster_size: 512
+backing file: TEST_DIR/t.IMGFMT.base
+
+== qcow backed by raw ==
+qemu-img: warning: Deprecated use of backing file without explicit backing format (detected format of raw)
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
+virtual size: 32 MiB (33554432 bytes)
+cluster_size: 512
+backing file: TEST_DIR/t.IMGFMT.base
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=raw
+image: TEST_DIR/t.IMGFMT
+file format: IMGFMT
+virtual size: 32 MiB (33554432 bytes)
+cluster_size: 512
+backing file: TEST_DIR/t.IMGFMT.base
+
+== commit cannot change type of raw backing file ==
+Formatting 'TEST_DIR/t.qcow.IMGFMT', fmt=IMGFMT size=33554432
+qemu-img: Block job failed: Operation not permitted
+image: TEST_DIR/t.IMGFMT.base
+file format: raw
+virtual size: 32 MiB (33554432 bytes)
+*** done
-nbd test nbd
-ssh test ssh
-nfs test nfs
- -vxhs test vxhs
other options
-xdiff graphical mode diff
xpand=false
;;
- -vxhs)
- IMGPROTO=vxhs
- xpand=false
- ;;
-
-ssh)
IMGPROTO=ssh
xpand=false
fi
export QEMU_NBD_PROG="$(type -p "$QEMU_NBD_PROG")"
-if [ -z "$QEMU_VXHS_PROG" ]; then
- export QEMU_VXHS_PROG="$(set_prog_path qnio_server)"
-fi
-
if [ -x "$build_iotests/socket_scm_helper" ]
then
export SOCKET_SCM_HELPER="$build_iotests/socket_scm_helper"
$SED -s 's/\("actual-size":\s*\)[0-9]\+/\1SIZE/g'
}
+# Filename filters for qemu-img create
+_filter_img_create_filenames()
+{
+ $SED \
+ -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \
+ -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
+ -e "s#$TEST_DIR#TEST_DIR#g" \
+ -e "s#$SOCK_DIR#SOCK_DIR#g" \
+ -e "s#$IMGFMT#IMGFMT#g" \
+ -e 's#nbd:127.0.0.1:[0-9]\\+#TEST_DIR/t.IMGFMT#g' \
+ -e 's#nbd+unix:///\??socket=SOCK_DIR/nbd#TEST_DIR/t.IMGFMT#g'
+}
+
# replace driver-specific options in the "Formatting..." line
-_filter_img_create()
+_do_filter_img_create()
{
# Split the line into the pre-options part ($filename_part, which
# precedes ", fmt=") and the options part ($options, which starts
# (And just echo everything before the first "^Formatting")
readarray formatting_line < <($SED -e 's/, fmt=/\n/')
- filename_part=''
- options=''
- lines=${#formatting_line[@]}
- for ((i = 0; i < $lines; i++)); do
- line=${formatting_line[i]}
- unset formatting_line[i]
-
- filename_part="$filename_part$line"
+ filename_part=${formatting_line[0]}
+ unset formatting_line[0]
- if echo "$line" | grep -q '^Formatting'; then
- next_i=$((i + 1))
- if [ -n "${formatting_line[next_i]}" ]; then
- options="fmt=${formatting_line[@]}"
- fi
- break
- fi
- done
+ options="fmt=${formatting_line[@]}"
# Set grep_data_file to '\|data_file' to keep it; make it empty
# to drop it.
grep_data_file=()
fi
- filename_filters=(
- -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \
- -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
- -e "s#$TEST_DIR#TEST_DIR#g" \
- -e "s#$SOCK_DIR#SOCK_DIR#g" \
- -e "s#$IMGFMT#IMGFMT#g" \
- -e 's#nbd:127.0.0.1:[0-9]\\+#TEST_DIR/t.IMGFMT#g' \
- -e 's#nbd+unix:///\??socket=SOCK_DIR/nbd#TEST_DIR/t.IMGFMT#g'
- )
-
- filename_part=$(echo "$filename_part" | $SED "${filename_filters[@]}")
+ filename_part=$(echo "$filename_part" | _filter_img_create_filenames)
# Break the option line before each option (preserving pre-existing
# line breaks by replacing them by \0 and restoring them at the end),
| $SED -e 's/ \([a-z0-9_.-]*\)=/\n\1=/g' \
| grep -a -e '^fmt' -e '^size' -e '^backing' -e '^preallocation' \
-e '^encryption' "${grep_data_file[@]}" \
- | $SED "${filename_filters[@]}" \
+ | _filter_img_create_filenames \
+ | $SED \
-e 's/^\(fmt\)/0-\1/' \
-e 's/^\(size\)/1-\1/' \
-e 's/^\(backing\)/2-\1/' \
-e 's/^\(data_file\)/3-\1/' \
-e 's/^\(encryption\)/4-\1/' \
-e 's/^\(preallocation\)/8-\1/' \
- | sort \
+ | LC_ALL=C sort \
| $SED -e 's/^[0-9]-//' \
| tr '\n\0' ' \n' \
| $SED -e 's/^ *$//' -e 's/ *$//'
fi
}
-# Filter the "Formatting..." line in QMP output (leaving the QMP output
-# untouched)
-# (In contrast to _filter_img_create(), this function does not support
-# multi-line Formatting output)
-_filter_img_create_in_qmp()
+# Filter qemu-img create output:
+# Pipe all ^Formatting lines through _do_filter_img_create, and all
+# other lines through _filter_img_create_filenames
+_filter_img_create()
{
while read -r line; do
if echo "$line" | grep -q '^Formatting'; then
- echo "$line" | _filter_img_create
+ echo "$line" | _do_filter_img_create
else
- echo "$line"
+ echo "$line" | _filter_img_create_filenames
fi
done
}
-e "s#$IMGFMT#IMGFMT#g" \
-e 's#nbd://127.0.0.1:[0-9]\\+$#TEST_DIR/t.IMGFMT#g' \
-e 's#nbd+unix:///\??socket=SOCK_DIR/nbd#TEST_DIR/t.IMGFMT#g' \
- -e 's#json.*vdisk-id.*vxhs"}}#TEST_DIR/t.IMGFMT#' \
-e "/encrypted: yes/d" \
-e "/cluster_size: [0-9]\\+/d" \
-e "/table_size: [0-9]\\+/d" \
: ${VALGRIND_QEMU_IMG=$VALGRIND_QEMU}
: ${VALGRIND_QEMU_IO=$VALGRIND_QEMU}
: ${VALGRIND_QEMU_NBD=$VALGRIND_QEMU}
-: ${VALGRIND_QEMU_VXHS=$VALGRIND_QEMU}
# The Valgrind own parameters may be set with
# its environment variable VALGRIND_OPTS, e.g.
return $RETVAL
}
-_qemu_vxhs_wrapper()
-{
- local VALGRIND_LOGFILE="${TEST_DIR}"/$$.valgrind
- (
- echo $BASHPID > "${TEST_DIR}/qemu-vxhs.pid"
- VALGRIND_QEMU="${VALGRIND_QEMU_VXHS}" _qemu_proc_exec "${VALGRIND_LOGFILE}" \
- "$QEMU_VXHS_PROG" $QEMU_VXHS_OPTIONS "$@"
- )
- RETVAL=$?
- _qemu_proc_valgrind_log "${VALGRIND_LOGFILE}" $RETVAL
- return $RETVAL
-}
-
# Valgrind bug #409141 https://bugs.kde.org/show_bug.cgi?id=409141
# Until valgrind 3.16+ is ubiquitous, we must work around a hang in
# valgrind when issuing sigkill. Disable valgrind for this invocation.
export QEMU_IMG=_qemu_img_wrapper
export QEMU_IO=_qemu_io_wrapper
export QEMU_NBD=_qemu_nbd_wrapper
-export QEMU_VXHS=_qemu_vxhs_wrapper
if [ "$IMGOPTSSYNTAX" = "true" ]; then
DRIVER="driver=$IMGFMT"
TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT
REMOTE_TEST_DIR="nfs://127.0.0.1$TEST_DIR"
TEST_IMG="nfs://127.0.0.1$TEST_IMG_FILE"
- elif [ "$IMGPROTO" = "vxhs" ]; then
- TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT
- TEST_IMG="vxhs://127.0.0.1:9999/t.$IMGFMT"
else
TEST_IMG=$IMGPROTO:$TEST_DIR/t.$IMGFMT
fi
eval "$QEMU_NBD -v -t -k '$SOCK_DIR/nbd' -f $IMGFMT -e 42 -x '' $TEST_IMG_FILE >/dev/null &"
sleep 1 # FIXME: qemu-nbd needs to be listening before we continue
fi
-
- # Start QNIO server on image directory for vxhs protocol
- if [ $IMGPROTO = "vxhs" ]; then
- eval "$QEMU_VXHS -d $TEST_DIR > /dev/null &"
- sleep 1 # Wait for server to come up.
- fi
}
_rm_test_img()
_stop_nbd_server
rm -f "$TEST_IMG_FILE"
;;
- vxhs)
- if [ -f "${TEST_DIR}/qemu-vxhs.pid" ]; then
- local QEMU_VXHS_PID
- read QEMU_VXHS_PID < "${TEST_DIR}/qemu-vxhs.pid"
- kill ${QEMU_VXHS_PID} >/dev/null 2>&1
- rm -f "${TEST_DIR}/qemu-vxhs.pid"
- fi
- rm -f "$TEST_IMG_FILE"
- ;;
file)
_rm_test_img "$TEST_DIR/t.$IMGFMT"
# use a fixed key so we don't waste system entropy on
# each test run
cat > "${tls_dir}/key.pem" <<EOF
------BEGIN PRIVATE KEY-----
-MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBALVcr
-BL40Tm6yq88FBhJNw1aaoCjmtg0l4dWQZ/e9Fimx4ARxFpT+ji4FE
-Cgl9s/SGqC+1nvlkm9ViSo0j7MKDbnDB+VRHDvMAzQhA2X7e8M0n9
-rPolUY2lIVC83q0BBaOBkCj2RSmT2xTEbbC2xLukSrg2WP/ihVOxc
-kXRuyFtzAgMBAAECgYB7slBexDwXrtItAMIH6m/U+LUpNe0Xx48OL
-IOn4a4whNgO/o84uIwygUK27ZGFZT0kAGAk8CdF9hA6ArcbQ62s1H
-myxrUbF9/mrLsQw1NEqpuUk9Ay2Tx5U/wPx35S3W/X2AvR/ZpTnCn
-2q/7ym9fyiSoj86drD7BTvmKXlOnOwQJBAPOFMp4mMa9NGpGuEssO
-m3Uwbp6lhcP0cA9MK+iOmeANpoKWfBdk5O34VbmeXnGYWEkrnX+9J
-bM4wVhnnBWtgBMCQQC+qAEmvwcfhauERKYznMVUVksyeuhxhCe7EK
-mPh+U2+g0WwdKvGDgO0PPt1gq0ILEjspMDeMHVdTwkaVBo/uMhAkA
-Z5SsZyCP2aTOPFDypXRdI4eqRcjaEPOUBq27r3uYb/jeboVb2weLa
-L1MmVuHiIHoa5clswPdWVI2y0em2IGoDAkBPSp/v9VKJEZabk9Frd
-a+7u4fanrM9QrEjY3KhduslSilXZZSxrWjjAJPyPiqFb3M8XXA26W
-nz1KYGnqYKhLcBAkB7dt57n9xfrhDpuyVEv+Uv1D3VVAhZlsaZ5Pp
-dcrhrkJn2sa/+O8OKvdrPSeeu/N5WwYhJf61+CPoenMp7IFci
------END PRIVATE KEY-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIG5AIBAAKCAYEAyjWyLSNm5PZvYUKUcDWGqbLX10b2ood+YaFjWSnJrqx/q3qh
+rVGBJglD25AJENJsmZF3zPP1oMhfIxsXu63Hdkb6Rdlc2RUoUP34x9VC1izH25mR
+6c8DPDp1d6IraZ/llDMI1HsBFz0qGWtvOHgm815XG4PAr/N8rDsuqfv/cJ01KlnO
+0OdO5QRXCJf9g/dYd41MPu7wOXk9FqjQlmRoP59HgtJ+zUpE4z+Keruw9cMT9VJj
+0oT+pQ9ysenqeZ3gbT224T1khrEhT5kifhtFLNyDssRchUUWH0hiqoOO1vgb+850
+W6/1VdxvuPam48py4diSPi1Vip8NITCOBaX9FIpVp4Ruw4rTPVMNMjq9Cpx/DwMP
+9MbfXfnaVaZaMrmq67/zPhl0eVbUrecH2hQ3ZB9oIF4GkNskzlWF5+yPy6zqk304
+AKaiFR6jRyh3YfHo2XFqV8x/hxdsIEXOtEUGhSIcpynsW+ckUCartzu7xbhXjd4b
+kxJT89+riPFYij09AgMBAAECggGBAKyFkaZXXROeejrmHlV6JZGlp+fhgM38gkRz
++Jp7P7rLLAY3E7gXIPQ91WqAAmwazFNdvHPd9USfkCQYmnAi/VoZhrCPmlsQZRxt
+A5QjjOnEvSPMa6SrXZxGWDCg6R8uMCb4P+FhrPWR1thnRDZOtRTQ+crc50p3mHgt
+6ktXWIJRbqnag8zSfQqCYGtRmhe8sfsWT+Yl4El4+jjaAVU/B364u7+PLmaiphGp
+BdJfTsTwEpgtGkPj+osDmhzXcZkfq3V+fz5JLkemsCiQKmn4VJRpg8c3ZmE8NPNt
+gRtGWZ4W3WKDvhotT65WpQx4+6R8Duux/blNPBmH1Upmwd7kj7GYFBArbCjgd9PT
+xgfCSUZpgOZHHkcgSB+022a8XncXna7WYYij28SLtwImFyu0nNtqECFQHH5u+k6C
+LRYBSN+3t3At8dQuk01NVrJBndmjmXRfxpqUtTdeaNgVpdUYRY98s30G68NYGSra
+aEvhhRSghkcLNetkobpY9pUgeqW/tQKBwQDZHHK9nDMt/zk1TxtILeUSitPXcv1/
+8ufXqO0miHdH23XuXhIEA6Ef26RRVGDGgpjkveDJK/1w5feJ4H/ni4Vclil/cm38
+OwRqjjd7ElHJX6JQbsxEx/gNTk5/QW1iAL9TXUalgepsSXYT6AJ0/CJv0jmJSJ36
+YoKMOM8uqzb2KhN6i+RlJRi5iY53kUhWTJq5ArWvNhUzQNSYODI4bNxlsKSBL2Ik
+LZ5QKHuaEjQet0IlPlfIb4PzMm8CHa/urOcCgcEA7m3zW/lL5bIFoKPjWig5Lbn1
+aHfrG2ngqzWtgWtfZqMH8OkZc1Mdhhmvd46titjiLjeI+UP/uHXR0068PnrNngzl
+tTgwlakzu+bWzqhBm1F+3/341st/FEk07r0P/3/PhezVjwfO8c8Exj7pLxH4wrH0
+ROHgDbClmlJRu6OO78wk1+Vapf5DWa8YfA+q+fdvr7KvgGyytheKMT/b/dsqOq7y
+qZPjmaJKWAvV3RWG8lWHFSdHx2IAHMHfGr17Y/w7AoHBALzwZeYebeekiVucGSjq
+T8SgLhT7zCIx+JMUPjVfYzaUhP/Iu7Lkma6IzWm9nW6Drpy5pUpMzwUWDCLfzU9q
+eseFIl337kEn9wLn+t5OpgAyCqYmlftxbqvdrrBN9uvnrJjWvqk/8wsDrw9JxAGc
+fjeD4nBXUqvYWLXApoR9mZoGKedmoH9pFig4zlO9ig8YITnKYuQ0k6SD0b8agJHc
+Ir0YSUDnRGgpjvFBGbeOCe+FGbohk/EpItJc3IAh5740lwKBwAdXd2DjokSmYKn7
+oeqKxofz6+yVlLW5YuOiuX78sWlVp87xPolgi84vSEnkKM/Xsc8+goc6YstpRVa+
+W+mImoA9YW1dF5HkLeWhTAf9AlgoAEIhbeIfTgBv6KNZSv7RDrDPBBxtXx/vAfSg
+x0ldwk0scZsVYXLKd67yzfV7KdGUdaX4N/xYgfZm/9gCG3+q8NN2KxVHQ5F71BOE
+JeABOaGo9WvnU+DNMIDZjHJMUWVw4MHz/a/UArDf/2CxaPVBNQKBwASg6j4ohSTk
+J7aE6RQ3OBmmDDpixcoCJt9u9SjHVYMlbs5CEJGVSczk0SG3y8P1lOWNDSRnMksZ
+xWnHdP/ogcuYMuvK7UACNAF0zNddtzOhzcpNmejFj+WCHYY/UmPr2/Kf6t7Cxk2K
+3cZ4tqWsiTmBT8Bknmah7L5DrhS+ZBJliDeFAA8fZHdMH0Xjr4UBp9kF90EMTdW1
+Xr5uz7ZrMsYpYQI7mmyqV9SSjUg4iBXwVSoag1iDJ1K8Qg/L7Semgg==
+-----END RSA PRIVATE KEY-----
EOF
}
295 rw
296 rw
297 meta
+299 auto quick
+301 backing quick
# pylint: disable=import-error, wrong-import-position
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
from qemu import qtest
+from qemu.qmp import QMPMessage
assert sys.version_info >= (3, 6)
-# Type Aliases
-QMPResponse = Dict[str, Any]
-
-
# Use this logger for logging messages directly from the iotests module
logger = logging.getLogger('qemu.iotests')
logger.addHandler(logging.NullHandler())
class QemuIoInteractive:
def __init__(self, *args):
- self.args = qemu_io_args + list(args)
+ self.args = qemu_io_args_no_fmt + list(args)
self._p = subprocess.Popen(self.args, stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True)
- assert self._p.stdout.read(9) == 'qemu-io> '
+ out = self._p.stdout.read(9)
+ if out != 'qemu-io> ':
+ # Most probably qemu-io just failed to start.
+ # Let's collect the whole output and exit.
+ out += self._p.stdout.read()
+ self._p.wait(timeout=1)
+ raise ValueError(out)
def close(self):
self._p.communicate('q\n')
return qmsg
def filter_testfiles(msg):
- prefix = os.path.join(test_dir, "%s-" % (os.getpid()))
- return msg.replace(prefix, 'TEST_DIR/PID-')
+ pref1 = os.path.join(test_dir, "%s-" % (os.getpid()))
+ pref2 = os.path.join(sock_dir, "%s-" % (os.getpid()))
+ return msg.replace(pref1, 'TEST_DIR/PID-').replace(pref2, 'SOCK_DIR/PID-')
def filter_qmp_testfiles(qmsg):
def _filter(_key, value):
self._args.append(addr)
return self
- def hmp(self, command_line: str, use_log: bool = False) -> QMPResponse:
+ def hmp(self, command_line: str, use_log: bool = False) -> QMPMessage:
cmd = 'human-monitor-command'
kwargs = {'command-line': command_line}
if use_log:
self.hmp(f'qemu-io {drive} "remove_break bp_{drive}"')
def hmp_qemu_io(self, drive: str, cmd: str,
- use_log: bool = False) -> QMPResponse:
+ use_log: bool = False) -> QMPMessage:
"""Write to a given drive using an HMP command"""
return self.hmp(f'qemu-io {drive} "{cmd}"', use_log=use_log)
struct thread_info {
void (*func)(struct thread_info *);
struct thread_stats stats;
- uint64_t r;
+ /*
+ * Seed is in the range [1..UINT64_MAX], because the RNG requires
+ * a non-zero seed. To use, subtract 1 and compare against the
+ * threshold with </>=. This lets threshold = 0 never match (0% hit),
+ * and threshold = UINT64_MAX always match (100% hit).
+ */
+ uint64_t seed;
bool write_op; /* writes alternate between insertions and removals */
bool resize_down;
} QEMU_ALIGNED(64); /* avoid false sharing among threads */
static void do_rz(struct thread_info *info)
{
struct thread_stats *stats = &info->stats;
+ uint64_t r = info->seed - 1;
- if (info->r < resize_threshold) {
+ if (r < resize_threshold) {
size_t size = info->resize_down ? resize_min : resize_max;
bool resized;
static void do_rw(struct thread_info *info)
{
struct thread_stats *stats = &info->stats;
+ uint64_t r = info->seed - 1;
uint32_t hash;
long *p;
- if (info->r >= update_threshold) {
+ if (r >= update_threshold) {
bool read;
- p = &keys[info->r & (lookup_range - 1)];
+ p = &keys[r & (lookup_range - 1)];
hash = hfunc(*p);
read = qht_lookup(&ht, p, hash);
if (read) {
stats->not_rd++;
}
} else {
- p = &keys[info->r & (update_range - 1)];
+ p = &keys[r & (update_range - 1)];
hash = hfunc(*p);
if (info->write_op) {
bool written = false;
rcu_read_lock();
while (!atomic_read(&test_stop)) {
- info->r = xorshift64star(info->r);
+ info->seed = xorshift64star(info->seed);
info->func(info);
}
rcu_read_unlock();
static void prepare_thread_info(struct thread_info *info, int i)
{
/* seed for the RNG; each thread should have a different one */
- info->r = (i + 1) ^ time(NULL);
+ info->seed = (i + 1) ^ time(NULL);
/* the first update will be a write */
info->write_op = true;
/* the first resize will be down */
static void do_threshold(double rate, uint64_t *threshold)
{
+ /*
+ * For 0 <= rate <= 1, scale to fit in a uint64_t.
+ *
+ * Scale by 2**64, with a special case for 1.0.
+ * The remainder of the possible values are scattered between 0
+ * and 0xfffffffffffff800 (nextafter(0x1p64, 0)).
+ *
+ * Note that we cannot simply scale by UINT64_MAX, because that
+ * value is not representable as an IEEE double value.
+ *
+ * If we scale by the next largest value, nextafter(0x1p64, 0),
+ * then the remainder of the possible values are scattered between
+ * 0 and 0xfffffffffffff000. Which leaves us with a gap between
+ * the final two inputs that is twice as large as any other.
+ */
if (rate == 1.0) {
*threshold = UINT64_MAX;
} else {
- *threshold = (rate * 0xffff000000000000ull)
- + (rate * 0x0000ffffffffffffull);
+ *threshold = rate * 0x1p64;
}
}
check-qtest-x86_64-y += $(check-qtest-i386-y)
+check-qtest-avr-y += boot-serial-test
+
check-qtest-alpha-y += boot-serial-test
check-qtest-alpha-$(CONFIG_VGA) += display-vga-test
tests/qtest/virtio-ccw-test$(EXESUF): tests/qtest/virtio-ccw-test.o
tests/qtest/display-vga-test$(EXESUF): tests/qtest/display-vga-test.o
tests/qtest/qom-test$(EXESUF): tests/qtest/qom-test.o
+tests/qtest/modules-test$(EXESUF): tests/qtest/modules-test.o
tests/qtest/test-hmp$(EXESUF): tests/qtest/test-hmp.o
tests/qtest/machine-none-test$(EXESUF): tests/qtest/machine-none-test.o
tests/qtest/device-plug-test$(EXESUF): tests/qtest/device-plug-test.o
g_free(variant);
g_free(tmp_path);
g_free(tmp_dir_name);
+ g_free(args);
free_test_data(&data);
#else
g_test_skip("TPM disabled");
#include "libqtest.h"
#include "libqos/libqos-spapr.h"
+static const uint8_t bios_avr[] = {
+ 0x88, 0xe0, /* ldi r24, 0x08 */
+ 0x80, 0x93, 0xc1, 0x00, /* sts 0x00C1, r24 ; Enable tx */
+ 0x86, 0xe0, /* ldi r24, 0x06 */
+ 0x80, 0x93, 0xc2, 0x00, /* sts 0x00C2, r24 ; Set the data bits to 8 */
+ 0x84, 0xe5, /* ldi r24, 0x54 */
+ 0x80, 0x93, 0xc6, 0x00, /* sts 0x00C6, r24 ; Output 'T' */
+};
+
static const uint8_t kernel_mcf5208[] = {
0x41, 0xf9, 0xfc, 0x06, 0x00, 0x00, /* lea 0xfc060000,%a0 */
0x10, 0x3c, 0x00, 0x54, /* move.b #'T',%d0 */
static testdef_t tests[] = {
{ "alpha", "clipper", "", "PCI:" },
+ { "avr", "arduino-duemilanove", "", "T", sizeof(bios_avr), NULL, bios_avr },
+ { "avr", "arduino-mega-2560-v3", "", "T", sizeof(bios_avr), NULL, bios_avr},
{ "ppc", "ppce500", "", "U-Boot" },
{ "ppc", "40p", "-vga none -boot d", "Trying cd:," },
{ "ppc", "g3beige", "", "PowerPC,750" },
{
QDict *resp;
char *help;
- char *qom_tree_start, *qom_tree_end;
- char *qtree_start, *qtree_end;
g_test_message("Testing device '%s'", type);
- qom_tree_start = qtest_hmp(qts, "info qom-tree");
- qtree_start = qtest_hmp(qts, "info qtree");
-
resp = qtest_qmp(qts, "{'execute': 'device-list-properties',"
" 'arguments': {'typename': %s}}",
type);
help = qtest_hmp(qts, "device_add \"%s,help\"", type);
g_free(help);
-
- /*
- * Some devices leave dangling pointers in QOM behind.
- * "info qom-tree" or "info qtree" have a good chance at crashing then.
- * Also make sure that the tree did not change.
- */
- qom_tree_end = qtest_hmp(qts, "info qom-tree");
- g_assert_cmpstr(qom_tree_start, ==, qom_tree_end);
- g_free(qom_tree_start);
- g_free(qom_tree_end);
-
- qtree_end = qtest_hmp(qts, "info qtree");
- g_assert_cmpstr(qtree_start, ==, qtree_end);
- g_free(qtree_start);
- g_free(qtree_end);
}
static void test_device_intro_list(void)
static void test_device_intro_none(void)
{
QTestState *qts = qtest_init(common_args);
+ g_autofree char *qom_tree_start = qtest_hmp(qts, "info qom-tree");
+ g_autofree char *qom_tree_end = NULL;
+ g_autofree char *qtree_start = qtest_hmp(qts, "info qtree");
+ g_autofree char *qtree_end = NULL;
test_one_device(qts, "nonexistent");
+
+ /* Make sure that really nothing changed in the trees */
+ qom_tree_end = qtest_hmp(qts, "info qom-tree");
+ g_assert_cmpstr(qom_tree_start, ==, qom_tree_end);
+ qtree_end = qtest_hmp(qts, "info qtree");
+ g_assert_cmpstr(qtree_start, ==, qtree_end);
+
qtest_quit(qts);
}
static void test_device_intro_abstract(void)
{
QTestState *qts = qtest_init(common_args);
+ g_autofree char *qom_tree_start = qtest_hmp(qts, "info qom-tree");
+ g_autofree char *qom_tree_end = NULL;
+ g_autofree char *qtree_start = qtest_hmp(qts, "info qtree");
+ g_autofree char *qtree_end = NULL;
test_one_device(qts, "device");
+
+ /* Make sure that really nothing changed in the trees */
+ qom_tree_end = qtest_hmp(qts, "info qom-tree");
+ g_assert_cmpstr(qom_tree_start, ==, qom_tree_end);
+ qtree_end = qtest_hmp(qts, "info qtree");
+ g_assert_cmpstr(qtree_start, ==, qtree_end);
+
qtest_quit(qts);
}
QList *types;
QListEntry *entry;
const char *type;
- QTestState *qts;
+ QTestState *qts = qtest_init(args);
+ g_autofree char *qom_tree_start = qtest_hmp(qts, "info qom-tree");
+ g_autofree char *qom_tree_end = NULL;
+ g_autofree char *qtree_start = qtest_hmp(qts, "info qtree");
+ g_autofree char *qtree_end = NULL;
- qts = qtest_init(args);
types = device_type_list(qts, false);
QLIST_FOREACH_ENTRY(types, entry) {
test_one_device(qts, type);
}
+ /*
+ * Some devices leave dangling pointers in QOM behind.
+ * "info qom-tree" or "info qtree" have a good chance at crashing then.
+ * Also make sure that the tree did not change.
+ */
+ qom_tree_end = qtest_hmp(qts, "info qom-tree");
+ g_assert_cmpstr(qom_tree_start, ==, qom_tree_end);
+
+ qtree_end = qtest_hmp(qts, "info qtree");
+ g_assert_cmpstr(qtree_start, ==, qtree_end);
+
qobject_unref(types);
qtest_quit(qts);
g_free((void *)args);
#include "sysemu/runstate.h"
#include "sysemu/sysemu.h"
#include "qemu/main-loop.h"
+#include "qemu/rcu.h"
#include "tests/qtest/libqtest.h"
#include "tests/qtest/libqos/qgraph.h"
#include "fuzz.h"
}
/* Run QEMU's softmmu main with the fuzz-target dependent arguments */
- const char *init_cmdline = fuzz_target->get_init_cmdline(fuzz_target);
- init_cmdline = g_strdup_printf("%s -qtest /dev/null -qtest-log %s",
- init_cmdline,
- getenv("QTEST_LOG") ? "/dev/fd/2"
- : "/dev/null");
-
+ GString *cmd_line = fuzz_target->get_init_cmdline(fuzz_target);
+ g_string_append_printf(cmd_line,
+ " -qtest /dev/null -qtest-log %s",
+ getenv("QTEST_LOG") ? "/dev/fd/2" : "/dev/null");
/* Split the runcmd into an argv and argc */
wordexp_t result;
- wordexp(init_cmdline, &result, 0);
+ wordexp(cmd_line->str, &result, 0);
+ g_string_free(cmd_line, true);
qemu_init(result.we_wordc, result.we_wordv, NULL);
/*
- * returns the arg-list that is passed to qemu/softmmu init()
- * Cannot be NULL
+ * Returns the arguments that are passed to qemu/softmmu init(). Freed by
+ * the caller.
*/
- const char* (*get_init_cmdline)(struct FuzzTarget *);
+ GString *(*get_init_cmdline)(struct FuzzTarget *);
/*
* will run once, prior to running qemu/softmmu init.
static const char *i440fx_qtest_argv = TARGET_NAME " -machine accel=qtest"
" -m 0 -display none";
-static const char *i440fx_argv(FuzzTarget *t)
+static GString *i440fx_argv(FuzzTarget *t)
{
- return i440fx_qtest_argv;
+ return g_string_new(i440fx_qtest_argv);
}
static void fork_init(void)
return allocate_objects(qts, current_path + 1, p_alloc);
}
-static const char *qos_build_main_args(void)
+static GString *qos_build_main_args(void)
{
char **path = fuzz_path_vec;
QOSGraphNode *test_node;
/* Prepend the arguments that we need */
g_string_prepend(cmd_line,
TARGET_NAME " -display none -machine accel=qtest -m 64 ");
- return cmd_line->str;
+ return cmd_line;
}
/*
g_free(path_str);
}
-static const char *qos_get_cmdline(FuzzTarget *t)
+static GString *qos_get_cmdline(FuzzTarget *t)
{
/*
* Set a global variable that we use to identify the qos_path for our
{
fuzz_add_qos_target(&(FuzzTarget){
.name = "virtio-scsi-fuzz",
- .description = "Fuzz the virtio-scsi virtual queues, forking"
+ .description = "Fuzz the virtio-scsi virtual queues, forking "
"for each fuzz run",
.pre_vm_init = &counter_shm_init,
.pre_fuzz = &virtio_scsi_pre_fuzz,
fuzz_add_qos_target(&(FuzzTarget){
.name = "virtio-scsi-flags-fuzz",
- .description = "Fuzz the virtio-scsi virtual queues, forking"
+ .description = "Fuzz the virtio-scsi virtual queues, forking "
"for each fuzz run (also fuzzes the virtio flags)",
.pre_vm_init = &counter_shm_init,
.pre_fuzz = &virtio_scsi_pre_fuzz,
/* tested targets list */
{ "arm", "cortex-a15" },
{ "aarch64", "cortex-a57" },
+ { "avr", "avr6-avr-cpu" },
{ "x86_64", "qemu64,apic-id=0" },
{ "i386", "qemu32,apic-id=0" },
{ "alpha", "ev67" },
}
}
-static void test_object_add_without_props(void)
+static void test_object_add_failure_modes(void)
{
QTestState *qts;
QDict *resp;
+ /* attempt to create an object without props */
qts = qtest_init(common_args);
resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
- " {'qom-type': 'memory-backend-ram', 'id': 'ram1' } }");
+ " {'qom-type': 'memory-backend-ram', 'id': 'ram1' } }");
g_assert_nonnull(resp);
qmp_assert_error_class(resp, "GenericError");
+
+ /* attempt to create an object without qom-type */
+ resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
+ " {'id': 'ram1' } }");
+ g_assert_nonnull(resp);
+ qmp_assert_error_class(resp, "GenericError");
+
+ /* attempt to delete an object that does not exist */
+ resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
+ " {'id': 'ram1' } }");
+ g_assert_nonnull(resp);
+ qmp_assert_error_class(resp, "GenericError");
+
+ /* attempt to create 2 objects with duplicate id */
+ resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
+ " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
+ " 'props': {'size': 1048576 } } }");
+ g_assert_nonnull(resp);
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
+ " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
+ " 'props': {'size': 1048576 } } }");
+ g_assert_nonnull(resp);
+ qmp_assert_error_class(resp, "GenericError");
+
+ /* delete ram1 object */
+ resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
+ " {'id': 'ram1' } }");
+ g_assert_nonnull(resp);
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ /* attempt to create an object with a property of a wrong type */
+ resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
+ " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
+ " 'props': {'size': '1048576' } } }");
+ g_assert_nonnull(resp);
+ /* now do it right */
+ qmp_assert_error_class(resp, "GenericError");
+
+ resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
+ " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
+ " 'props': {'size': 1048576 } } }");
+ g_assert_nonnull(resp);
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ /* delete ram1 object */
+ resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
+ " {'id': 'ram1' } }");
+ g_assert_nonnull(resp);
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ /* attempt to create an object without the id */
+ resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
+ " {'qom-type': 'memory-backend-ram',"
+ " 'props': {'size': 1048576 } } }");
+ g_assert_nonnull(resp);
+ qmp_assert_error_class(resp, "GenericError");
+
+ /* now do it right */
+ resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
+ " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
+ " 'props': {'size': 1048576 } } }");
+ g_assert_nonnull(resp);
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ /* delete ram1 object */
+ resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
+ " {'id': 'ram1' } }");
+ g_assert_nonnull(resp);
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ /* attempt to set a non existing property */
+ resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
+ " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
+ " 'props': {'sized': 1048576 } } }");
+ g_assert_nonnull(resp);
+ qmp_assert_error_class(resp, "GenericError");
+
+ /* now do it right */
+ resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
+ " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
+ " 'props': {'size': 1048576 } } }");
+ g_assert_nonnull(resp);
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ /* delete ram1 object without id */
+ resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
+ " {'ida': 'ram1' } }");
+ g_assert_nonnull(resp);
+ qobject_unref(resp);
+
+ /* delete ram1 object */
+ resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
+ " {'id': 'ram1' } }");
+ g_assert_nonnull(resp);
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ /* delete ram1 object that does not exist anymore*/
+ resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
+ " {'id': 'ram1' } }");
+ g_assert_nonnull(resp);
+ qmp_assert_error_class(resp, "GenericError");
+
qtest_quit(qts);
}
qmp_schema_init(&schema);
add_query_tests(&schema);
- qtest_add_func("qmp/object-add-without-props",
- test_object_add_without_props);
- /* TODO: add coverage of generic object-add failure modes */
+ qtest_add_func("qmp/object-add-failure-modes",
+ test_object_add_failure_modes);
ret = g_test_run();
tx(s, tpm_pcrread, sizeof(tpm_pcrread), buffer, sizeof(buffer));
- g_assert_cmpmem(buffer, exp_resp_size, exp_resp, exp_resp_size);
+ /* skip pcrUpdateCounter (14th byte) in comparison */
+ g_assert(exp_resp_size >= 15);
+ g_assert_cmpmem(buffer, 13, exp_resp, 13);
+ g_assert_cmpmem(&buffer[14], exp_resp_size - 14,
+ &exp_resp[14], exp_resp_size - 14);
}
bool tpm_util_swtpm_has_tpm2(void)
DOCKER_COMPILE_CMD="$(DOCKER_SCRIPT) cc \
--cc $(DOCKER_CROSS_CC_GUEST) \
- -i qemu:$(DOCKER_IMAGE) \
+ -i qemu/$(DOCKER_IMAGE) \
-s $(SRC_PATH) -- "
.PHONY: docker-build-guest-tests
$(MAKE) -f $(TCG_MAKE) TARGET="$(TARGET)" CC=$(DOCKER_COMPILE_CMD) \
SRC_PATH="$(SRC_PATH)" BUILD_STATIC=y \
EXTRA_CFLAGS="$(CROSS_CC_GUEST_CFLAGS)"), \
- "BUILD","$(TARGET) guest-tests with docker qemu:$(DOCKER_IMAGE)")
+ "BUILD","$(TARGET) guest-tests with docker qemu/$(DOCKER_IMAGE)")
GUEST_BUILD=docker-build-guest-tests
: ${cross_cc_aarch64="aarch64-linux-gnu-gcc"}
: ${cross_cc_aarch64_be="$cross_cc_aarch64"}
: ${cross_cc_cflags_aarch64_be="-mbig-endian"}
+: $(cross_cc_alpha="alpha-linux-gnu-gcc")
: ${cross_cc_arm="arm-linux-gnueabihf-gcc"}
: ${cross_cc_cflags_armeb="-mbig-endian"}
+: ${cross_cc_hppa="hppa-linux-gnu-gcc"}
: ${cross_cc_i386="i386-pc-linux-gnu-gcc"}
: ${cross_cc_cflags_i386="-m32"}
-: ${cross_cc_x86_64="x86_64-pc-linux-gnu-gcc"}
-: ${cross_cc_cflags_x86_64="-m64"}
+: ${cross_cc_m68k="m68k-linux-gnu-gcc"}
+: $(cross_cc_mips64el="mips64el-linux-gnuabi64-gcc")
+: $(cross_cc_mips64="mips64-linux-gnuabi64-gcc")
+: $(cross_cc_mipsel="mipsel-linux-gnu-gcc")
+: $(cross_cc_mips="mips-linux-gnu-gcc")
: ${cross_cc_ppc="powerpc-linux-gnu-gcc"}
: ${cross_cc_cflags_ppc="-m32"}
-: ${cross_cc_ppc64="powerpc-linux-gnu-gcc"}
-: ${cross_cc_cflags_ppc64="-m64"}
+: ${cross_cc_ppc64="powerpc64-linux-gnu-gcc"}
: ${cross_cc_ppc64le="powerpc64le-linux-gnu-gcc"}
-: ${cross_cc_cflags_s390x="-m64"}
+: $(cross_cc_riscv64="riscv64-linux-gnu-gcc")
+: ${cross_cc_s390x="s390x-linux-gnu-gcc"}
+: $(cross_cc_sh4="sh4-linux-gnu-gcc")
: ${cross_cc_cflags_sparc="-m32 -mv8plus -mcpu=ultrasparc"}
+: ${cross_cc_sparc64="sparc64-linux-gnu-gcc"}
: ${cross_cc_cflags_sparc64="-m64 -mcpu=ultrasparc"}
+: ${cross_cc_x86_64="x86_64-pc-linux-gnu-gcc"}
+: ${cross_cc_cflags_x86_64="-m64"}
for target in $target_list; do
arch=${target%%-*}
container_image=debian-xtensa-cross
# default to the dc232b cpu
- container_cross_cc=/opt/2018.02/xtensa-dc232b-elf/bin/xtensa-dc232b-elf-gcc
+ container_cross_cc=/opt/2020.07/xtensa-dc232b-elf/bin/xtensa-dc232b-elf-gcc
;;
esac
SKIP_I386_TESTS=test-i386-ssse3
X86_64_TESTS:=$(filter test-i386-ssse3, $(ALL_X86_TESTS))
+test-i386-sse-exceptions: CFLAGS += -msse4.1 -mfpmath=sse
+run-test-i386-sse-exceptions: QEMU_OPTS += -cpu max
+run-plugin-test-i386-sse-exceptions-%: QEMU_OPTS += -cpu max
+
test-i386-pcmpistri: CFLAGS += -msse4.2
run-test-i386-pcmpistri: QEMU_OPTS += -cpu max
run-plugin-test-i386-pcmpistri-%: QEMU_OPTS += -cpu max
--- /dev/null
+/* Test SSE exceptions. */
+
+#include <float.h>
+#include <stdint.h>
+#include <stdio.h>
+
+volatile float f_res;
+volatile double d_res;
+
+volatile float f_snan = __builtin_nansf("");
+volatile float f_half = 0.5f;
+volatile float f_third = 1.0f / 3.0f;
+volatile float f_nan = __builtin_nanl("");
+volatile float f_inf = __builtin_inff();
+volatile float f_ninf = -__builtin_inff();
+volatile float f_one = 1.0f;
+volatile float f_two = 2.0f;
+volatile float f_zero = 0.0f;
+volatile float f_nzero = -0.0f;
+volatile float f_min = FLT_MIN;
+volatile float f_true_min = 0x1p-149f;
+volatile float f_max = FLT_MAX;
+volatile float f_nmax = -FLT_MAX;
+
+volatile double d_snan = __builtin_nans("");
+volatile double d_half = 0.5;
+volatile double d_third = 1.0 / 3.0;
+volatile double d_nan = __builtin_nan("");
+volatile double d_inf = __builtin_inf();
+volatile double d_ninf = -__builtin_inf();
+volatile double d_one = 1.0;
+volatile double d_two = 2.0;
+volatile double d_zero = 0.0;
+volatile double d_nzero = -0.0;
+volatile double d_min = DBL_MIN;
+volatile double d_true_min = 0x1p-1074;
+volatile double d_max = DBL_MAX;
+volatile double d_nmax = -DBL_MAX;
+
+volatile int32_t i32_max = INT32_MAX;
+
+#define IE (1 << 0)
+#define ZE (1 << 2)
+#define OE (1 << 3)
+#define UE (1 << 4)
+#define PE (1 << 5)
+#define EXC (IE | ZE | OE | UE | PE)
+
+uint32_t mxcsr_default = 0x1f80;
+uint32_t mxcsr_ftz = 0x9f80;
+
+int main(void)
+{
+ uint32_t mxcsr;
+ int32_t i32_res;
+ int ret = 0;
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = f_snan;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: widen float snan\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = d_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: narrow float underflow\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = d_max;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (OE | PE)) {
+ printf("FAIL: narrow float overflow\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = d_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: narrow float inexact\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = d_snan;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: narrow float snan\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("roundss $4, %0, %0" : "=x" (f_res) : "0" (f_min));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: roundss min\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("roundss $12, %0, %0" : "=x" (f_res) : "0" (f_min));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: roundss no-inexact min\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("roundss $4, %0, %0" : "=x" (f_res) : "0" (f_snan));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: roundss snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("roundss $12, %0, %0" : "=x" (f_res) : "0" (f_snan));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: roundss no-inexact snan\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("roundsd $4, %0, %0" : "=x" (d_res) : "0" (d_min));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: roundsd min\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("roundsd $12, %0, %0" : "=x" (d_res) : "0" (d_min));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: roundsd no-inexact min\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("roundsd $4, %0, %0" : "=x" (d_res) : "0" (d_snan));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: roundsd snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("roundsd $12, %0, %0" : "=x" (d_res) : "0" (d_snan));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: roundsd no-inexact snan\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("comiss %1, %0" : : "x" (f_nan), "x" (f_zero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: comiss nan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("ucomiss %1, %0" : : "x" (f_nan), "x" (f_zero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: ucomiss nan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("ucomiss %1, %0" : : "x" (f_snan), "x" (f_zero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: ucomiss snan\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("comisd %1, %0" : : "x" (d_nan), "x" (d_zero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: comisd nan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("ucomisd %1, %0" : : "x" (d_nan), "x" (d_zero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: ucomisd nan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("ucomisd %1, %0" : : "x" (d_snan), "x" (d_zero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: ucomisd snan\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_max + f_max;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (OE | PE)) {
+ printf("FAIL: float add overflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_max + f_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: float add inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_inf + f_ninf;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: float add inf -inf\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_snan + f_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: float add snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_ftz));
+ f_res = f_true_min + f_true_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: float add FTZ underflow\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_max + d_max;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (OE | PE)) {
+ printf("FAIL: double add overflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_max + d_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: double add inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_inf + d_ninf;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: double add inf -inf\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_snan + d_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: double add snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_ftz));
+ d_res = d_true_min + d_true_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: double add FTZ underflow\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_max - f_nmax;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (OE | PE)) {
+ printf("FAIL: float sub overflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_max - f_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: float sub inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_inf - f_inf;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: float sub inf inf\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_snan - f_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: float sub snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_ftz));
+ f_res = f_min - f_true_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: float sub FTZ underflow\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_max - d_nmax;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (OE | PE)) {
+ printf("FAIL: double sub overflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_max - d_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: double sub inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_inf - d_inf;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: double sub inf inf\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_snan - d_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: double sub snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_ftz));
+ d_res = d_min - d_true_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: double sub FTZ underflow\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_max * f_max;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (OE | PE)) {
+ printf("FAIL: float mul overflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_third * f_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: float mul inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_min * f_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: float mul underflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_inf * f_zero;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: float mul inf 0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_snan * f_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: float mul snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_ftz));
+ f_res = f_min * f_half;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: float mul FTZ underflow\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_max * d_max;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (OE | PE)) {
+ printf("FAIL: double mul overflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_third * d_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: double mul inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_min * d_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: double mul underflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_inf * d_zero;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: double mul inf 0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_snan * d_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: double mul snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_ftz));
+ d_res = d_min * d_half;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: double mul FTZ underflow\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_max / f_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (OE | PE)) {
+ printf("FAIL: float div overflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_one / f_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: float div inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_min / f_max;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: float div underflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_one / f_zero;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != ZE) {
+ printf("FAIL: float div 1 0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_inf / f_zero;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: float div inf 0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_nan / f_zero;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: float div nan 0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_zero / f_zero;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: float div 0 0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_inf / f_inf;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: float div inf inf\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_snan / f_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: float div snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_ftz));
+ f_res = f_min / f_two;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: float div FTZ underflow\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_max / d_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (OE | PE)) {
+ printf("FAIL: double div overflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_one / d_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: double div inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_min / d_max;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: double div underflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_one / d_zero;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != ZE) {
+ printf("FAIL: double div 1 0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_inf / d_zero;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: double div inf 0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_nan / d_zero;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: double div nan 0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_zero / d_zero;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: double div 0 0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_inf / d_inf;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: double div inf inf\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_snan / d_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: double div snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_ftz));
+ d_res = d_min / d_two;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: double div FTZ underflow\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtss %0, %0" : "=x" (f_res) : "0" (f_max));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: sqrtss inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtss %0, %0" : "=x" (f_res) : "0" (f_nmax));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: sqrtss -max\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtss %0, %0" : "=x" (f_res) : "0" (f_ninf));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: sqrtss -inf\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtss %0, %0" : "=x" (f_res) : "0" (f_snan));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: sqrtss snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtss %0, %0" : "=x" (f_res) : "0" (f_nzero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: sqrtss -0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtss %0, %0" : "=x" (f_res) :
+ "0" (-__builtin_nanf("")));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: sqrtss -nan\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtsd %0, %0" : "=x" (d_res) : "0" (d_max));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: sqrtsd inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtsd %0, %0" : "=x" (d_res) : "0" (d_nmax));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: sqrtsd -max\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtsd %0, %0" : "=x" (d_res) : "0" (d_ninf));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: sqrtsd -inf\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtsd %0, %0" : "=x" (d_res) : "0" (d_snan));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: sqrtsd snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtsd %0, %0" : "=x" (d_res) : "0" (d_nzero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: sqrtsd -0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtsd %0, %0" : "=x" (d_res) :
+ "0" (-__builtin_nan("")));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: sqrtsd -nan\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("maxss %1, %0" : : "x" (f_nan), "x" (f_zero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: maxss nan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("minss %1, %0" : : "x" (f_nan), "x" (f_zero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: minss nan\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("maxsd %1, %0" : : "x" (d_nan), "x" (d_zero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: maxsd nan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("minsd %1, %0" : : "x" (d_nan), "x" (d_zero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: minsd nan\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvtsi2ss %1, %0" : "=x" (f_res) : "m" (i32_max));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: cvtsi2ss inexact\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvtsi2sd %1, %0" : "=x" (d_res) : "m" (i32_max));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: cvtsi2sd exact\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvtss2si %1, %0" : "=r" (i32_res) : "x" (1.5f));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: cvtss2si inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvtss2si %1, %0" : "=r" (i32_res) : "x" (0x1p31f));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: cvtss2si 0x1p31\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvtss2si %1, %0" : "=r" (i32_res) : "x" (f_inf));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: cvtss2si inf\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvtsd2si %1, %0" : "=r" (i32_res) : "x" (1.5));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: cvtsd2si inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvtsd2si %1, %0" : "=r" (i32_res) : "x" (0x1p31));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: cvtsd2si 0x1p31\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvtsd2si %1, %0" : "=r" (i32_res) : "x" (d_inf));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: cvtsd2si inf\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvttss2si %1, %0" : "=r" (i32_res) : "x" (1.5f));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: cvttss2si inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvttss2si %1, %0" : "=r" (i32_res) : "x" (0x1p31f));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: cvttss2si 0x1p31\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvttss2si %1, %0" : "=r" (i32_res) : "x" (f_inf));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: cvttss2si inf\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvttsd2si %1, %0" : "=r" (i32_res) : "x" (1.5));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: cvttsd2si inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvttsd2si %1, %0" : "=r" (i32_res) : "x" (0x1p31));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: cvttsd2si 0x1p31\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvttsd2si %1, %0" : "=r" (i32_res) : "x" (d_inf));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: cvttsd2si inf\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("rcpss %0, %0" : "=x" (f_res) : "0" (f_snan));
+ f_res += f_one;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: rcpss snan\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("rsqrtss %0, %0" : "=x" (f_res) : "0" (f_snan));
+ f_res += f_one;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: rsqrtss snan\n");
+ ret = 1;
+ }
+
+ return ret;
+}
#include "qemu/option.h"
#include "qemu/sockets.h"
#include "chardev/char-fe.h"
-#include "chardev/char-mux.h"
#include "sysemu/sysemu.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-char.h"
typedef struct {
int event;
bool got_pong;
+ CharBackend *be;
} CharSocketTestData;
#define SOCKET_PING "Hello"
#define SOCKET_PONG "World"
+typedef void (*char_socket_cb)(void *opaque, QEMUChrEvent event);
static void
char_socket_event(void *opaque, QEMUChrEvent event)
data->event = event;
}
+static void
+char_socket_event_with_error(void *opaque, QEMUChrEvent event)
+{
+ static bool first_error;
+ CharSocketTestData *data = opaque;
+ CharBackend *be = data->be;
+ data->event = event;
+ switch (event) {
+ case CHR_EVENT_OPENED:
+ if (!first_error) {
+ first_error = true;
+ qemu_chr_fe_disconnect(be);
+ }
+ return;
+ case CHR_EVENT_CLOSED:
+ return;
+ default:
+ return;
+ }
+}
+
static void
char_socket_read(void *opaque, const uint8_t *buf, int size)
}
-static void
-char_socket_ping_pong(QIOChannel *ioc)
+static int
+char_socket_ping_pong(QIOChannel *ioc, Error **errp)
{
char greeting[sizeof(SOCKET_PING)];
const char *response = SOCKET_PONG;
- qio_channel_read_all(ioc, greeting, sizeof(greeting), &error_abort);
+ int ret;
+ ret = qio_channel_read_all(ioc, greeting, sizeof(greeting), errp);
+ if (ret != 0) {
+ object_unref(OBJECT(ioc));
+ return -1;
+ }
g_assert(memcmp(greeting, SOCKET_PING, sizeof(greeting)) == 0);
- qio_channel_write_all(ioc, response, sizeof(SOCKET_PONG), &error_abort);
-
+ qio_channel_write_all(ioc, response, sizeof(SOCKET_PONG), errp);
object_unref(OBJECT(ioc));
+ return 0;
}
qio_channel_socket_connect_sync(ioc, addr, &error_abort);
- char_socket_ping_pong(QIO_CHANNEL(ioc));
+ char_socket_ping_pong(QIO_CHANNEL(ioc), &error_abort);
return NULL;
}
reconnect:
data.event = -1;
+ data.be = &be;
qemu_chr_fe_set_handlers(&be, NULL, NULL,
char_socket_event, NULL,
&data, NULL, true);
QIOChannelSocket *ioc = data;
QIOChannelSocket *cioc;
+retry:
cioc = qio_channel_socket_accept(ioc, &error_abort);
g_assert_nonnull(cioc);
- char_socket_ping_pong(QIO_CHANNEL(cioc));
+ if (char_socket_ping_pong(QIO_CHANNEL(cioc), NULL) != 0) {
+ goto retry;
+ }
return NULL;
}
const char *reconnect;
bool wait_connected;
bool fd_pass;
+ char_socket_cb event_cb;
} CharSocketClientTestConfig;
+static void char_socket_client_dupid_test(gconstpointer opaque)
+{
+ const CharSocketClientTestConfig *config = opaque;
+ QIOChannelSocket *ioc;
+ char *optstr;
+ Chardev *chr1, *chr2;
+ SocketAddress *addr;
+ QemuOpts *opts;
+ Error *local_err = NULL;
+
+ /*
+ * Setup a listener socket and determine get its address
+ * so we know the TCP port for the client later
+ */
+ ioc = qio_channel_socket_new();
+ g_assert_nonnull(ioc);
+ qio_channel_socket_listen_sync(ioc, config->addr, 1, &error_abort);
+ addr = qio_channel_socket_get_local_address(ioc, &error_abort);
+ g_assert_nonnull(addr);
+
+ /*
+ * Populate the chardev address based on what the server
+ * is actually listening on
+ */
+ optstr = char_socket_addr_to_opt_str(addr,
+ config->fd_pass,
+ config->reconnect,
+ false);
+
+ opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"),
+ optstr, true);
+ g_assert_nonnull(opts);
+ chr1 = qemu_chr_new_from_opts(opts, NULL, &error_abort);
+ g_assert_nonnull(chr1);
+
+ chr2 = qemu_chr_new_from_opts(opts, NULL, &local_err);
+ g_assert_null(chr2);
+ error_free_or_abort(&local_err);
+
+ object_unref(OBJECT(ioc));
+ qemu_opts_del(opts);
+ object_unparent(OBJECT(chr1));
+ qapi_free_SocketAddress(addr);
+ g_free(optstr);
+}
static void char_socket_client_test(gconstpointer opaque)
{
const CharSocketClientTestConfig *config = opaque;
+ const char_socket_cb event_cb = config->event_cb;
QIOChannelSocket *ioc;
char *optstr;
Chardev *chr;
reconnect:
data.event = -1;
+ data.be = &be;
qemu_chr_fe_set_handlers(&be, NULL, NULL,
- char_socket_event, NULL,
+ event_cb, NULL,
&data, NULL, true);
if (config->reconnect) {
g_assert(data.event == -1);
/* Setup a callback to receive the reply to our greeting */
qemu_chr_fe_set_handlers(&be, char_socket_can_read,
char_socket_read,
- char_socket_event, NULL,
+ event_cb, NULL,
&data, NULL, true);
g_assert(data.event == CHR_EVENT_OPENED);
data.event = -1;
#define SOCKET_CLIENT_TEST(name, addr) \
static CharSocketClientTestConfig client1 ## name = \
- { addr, NULL, false, false }; \
+ { addr, NULL, false, false, char_socket_event }; \
static CharSocketClientTestConfig client2 ## name = \
- { addr, NULL, true, false }; \
+ { addr, NULL, true, false, char_socket_event }; \
static CharSocketClientTestConfig client3 ## name = \
- { addr, ",reconnect=1", false }; \
+ { addr, ",reconnect=1", false, false, char_socket_event }; \
static CharSocketClientTestConfig client4 ## name = \
- { addr, ",reconnect=1", true }; \
+ { addr, ",reconnect=1", true, false, char_socket_event }; \
static CharSocketClientTestConfig client5 ## name = \
- { addr, NULL, false, true }; \
+ { addr, NULL, false, true, char_socket_event }; \
static CharSocketClientTestConfig client6 ## name = \
- { addr, NULL, true, true }; \
+ { addr, NULL, true, true, char_socket_event }; \
+ static CharSocketClientTestConfig client7 ## name = \
+ { addr, ",reconnect=1", true, false, \
+ char_socket_event_with_error }; \
+ static CharSocketClientTestConfig client8 ## name = \
+ { addr, ",reconnect=1", false, false, char_socket_event }; \
g_test_add_data_func("/char/socket/client/mainloop/" # name, \
&client1 ##name, char_socket_client_test); \
g_test_add_data_func("/char/socket/client/wait-conn/" # name, \
g_test_add_data_func("/char/socket/client/mainloop-fdpass/" # name, \
&client5 ##name, char_socket_client_test); \
g_test_add_data_func("/char/socket/client/wait-conn-fdpass/" # name, \
- &client6 ##name, char_socket_client_test)
+ &client6 ##name, char_socket_client_test); \
+ g_test_add_data_func("/char/socket/client/reconnect-error/" # name, \
+ &client7 ##name, char_socket_client_test); \
+ g_test_add_data_func("/char/socket/client/dupid-reconnect/" # name, \
+ &client8 ##name, char_socket_client_dupid_test)
if (has_ipv4) {
SOCKET_SERVER_TEST(tcp, &tcpaddr);
IMAGES := freebsd netbsd openbsd centos fedora
ifneq ($(GENISOIMAGE),)
IMAGES += ubuntu.i386 centos
+ifneq ($(EFI_AARCH64),)
+IMAGES += ubuntu.aarch64 centos.aarch64
+endif
endif
IMAGES_DIR := $(HOME)/.cache/qemu-vm/images
ifneq ($(GENISOIMAGE),)
@echo " vm-build-centos - Build QEMU in CentOS VM, with Docker"
@echo " vm-build-ubuntu.i386 - Build QEMU in ubuntu i386 VM"
+ifneq ($(EFI_AARCH64),)
+ @echo " vm-build-ubuntu.aarch64 - Build QEMU in ubuntu aarch64 VM"
+ @echo " vm-build-centos.aarch64 - Build QEMU in CentOS aarch64 VM"
+else
+ @echo " (to build centos/ubuntu aarch64 images use configure --efi-aarch64)"
+endif
else
@echo " (install genisoimage to build centos/ubuntu images)"
endif
@echo ' EXTRA_CONFIGURE_OPTS="..."'
@echo " J=[0..9]* - Override the -jN parameter for make commands"
@echo " DEBUG=1 - Enable verbose output on host and interactive debugging"
+ @echo " LOG_CONSOLE=1 - Log console to file in: ~/.cache/qemu-vm "
@echo " V=1 - Enable verbose ouput on host and guest commands"
@echo " QEMU_LOCAL=1 - Use QEMU binary local to this build."
@echo " QEMU=/path/to/qemu - Change path to QEMU binary"
@echo " QEMU_IMG=/path/to/qemu-img - Change path to qemu-img tool"
+ifeq ($(PYTHON_YAML),yes)
+ @echo " QEMU_CONFIG=/path/conf.yml - Change path to VM configuration .yml file."
+else
+ @echo " (install python3-yaml to enable support for yaml file to configure a VM.)"
+endif
+ @echo " See conf_example_*.yml for file format details."
vm-build-all: $(addprefix vm-build-, $(IMAGES))
$(if $(V)$(DEBUG), --debug) \
$(if $(GENISOIMAGE),--genisoimage $(GENISOIMAGE)) \
$(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \
+ $(if $(EFI_AARCH64),--efi-aarch64 $(EFI_AARCH64)) \
+ $(if $(LOG_CONSOLE),--log-console) \
--image "$@" \
--force \
--build-image $@, \
$(if $(J),--jobs $(J)) \
$(if $(V),--verbose) \
$(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \
+ $(if $(EFI_AARCH64),--efi-aarch64 $(EFI_AARCH64)) \
+ $(if $(LOG_CONSOLE),--log-console) \
--image "$<" \
$(if $(BUILD_TARGET),--build-target $(BUILD_TARGET)) \
--snapshot \
$(if $(J),--jobs $(J)) \
$(if $(V)$(DEBUG), --debug) \
$(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \
+ $(if $(EFI_AARCH64),--efi-aarch64 $(EFI_AARCH64)) \
+ $(if $(LOG_CONSOLE),--log-console) \
--image "$<" \
--interactive \
false, \
--- /dev/null
+#!/usr/bin/env python3
+#
+# VM testing aarch64 library
+#
+# Copyright 2020 Linaro
+#
+# Authors:
+# Robert Foley <robert.foley@linaro.org>
+#
+# This code is licensed under the GPL version 2 or later. See
+# the COPYING file in the top-level directory.
+#
+import os
+import sys
+import subprocess
+import basevm
+from qemu.accel import kvm_available
+
+# This is the config needed for current version of QEMU.
+# This works for both kvm and tcg.
+CURRENT_CONFIG = {
+ 'cpu' : "max",
+ 'machine' : "virt,gic-version=max",
+}
+
+# The minimum minor version of QEMU we will support with aarch64 VMs is 3.
+# QEMU versions less than 3 have various issues running these VMs.
+QEMU_AARCH64_MIN_VERSION = 3
+
+# The DEFAULT_CONFIG will default to a version of
+# parameters that works for backwards compatibility.
+DEFAULT_CONFIG = {'kvm' : {'cpu' : "host",
+ 'machine' : "virt,gic-version=host"},
+ 'tcg' : {'cpu' : "cortex-a57",
+ 'machine' : "virt"},
+}
+
+def get_config_defaults(vmcls, default_config):
+ """Fetch the configuration defaults for this VM,
+ taking into consideration the defaults for
+ aarch64 first, followed by the defaults for this VM."""
+ config = default_config
+ config.update(aarch_get_config_defaults(vmcls))
+ return config
+
+def aarch_get_config_defaults(vmcls):
+ """Set the defaults for current version of QEMU."""
+ config = CURRENT_CONFIG
+ args = basevm.parse_args(vmcls)
+ qemu_path = basevm.get_qemu_path(vmcls.arch, args.build_path)
+ qemu_version = basevm.get_qemu_version(qemu_path)
+ if qemu_version < QEMU_AARCH64_MIN_VERSION:
+ error = "\nThis major version of QEMU {} is to old for aarch64 VMs.\n"\
+ "The major version must be at least {}.\n"\
+ "To continue with the current build of QEMU, "\
+ "please restart with QEMU_LOCAL=1 .\n"
+ print(error.format(qemu_version, QEMU_AARCH64_MIN_VERSION))
+ exit(1)
+ if qemu_version == QEMU_AARCH64_MIN_VERSION:
+ # We have an older version of QEMU,
+ # set the config values for backwards compatibility.
+ if kvm_available('aarch64'):
+ config.update(DEFAULT_CONFIG['kvm'])
+ else:
+ config.update(DEFAULT_CONFIG['tcg'])
+ return config
+
+def create_flash_images(flash_dir="./", efi_img=""):
+ """Creates the appropriate pflash files
+ for an aarch64 VM."""
+ flash0_path = get_flash_path(flash_dir, "flash0")
+ flash1_path = get_flash_path(flash_dir, "flash1")
+ fd_null = open(os.devnull, 'w')
+ subprocess.check_call(["dd", "if=/dev/zero", "of={}".format(flash0_path),
+ "bs=1M", "count=64"],
+ stdout=fd_null, stderr=subprocess.STDOUT)
+ # A reliable way to get the QEMU EFI image is via an installed package or
+ # via the bios included with qemu.
+ if not os.path.exists(efi_img):
+ sys.stderr.write("*** efi argument is invalid ({})\n".format(efi_img))
+ sys.stderr.write("*** please check --efi-aarch64 argument or "\
+ "install qemu-efi-aarch64 package\n")
+ exit(3)
+ subprocess.check_call(["dd", "if={}".format(efi_img),
+ "of={}".format(flash0_path),
+ "conv=notrunc"],
+ stdout=fd_null, stderr=subprocess.STDOUT)
+ subprocess.check_call(["dd", "if=/dev/zero",
+ "of={}".format(flash1_path),
+ "bs=1M", "count=64"],
+ stdout=fd_null, stderr=subprocess.STDOUT)
+ fd_null.close()
+
+def get_pflash_args(flash_dir="./"):
+ """Returns a string that can be used to
+ boot qemu using the appropriate pflash files
+ for aarch64."""
+ flash0_path = get_flash_path(flash_dir, "flash0")
+ flash1_path = get_flash_path(flash_dir, "flash1")
+ pflash_args_str = "-drive file={},format=raw,if=pflash "\
+ "-drive file={},format=raw,if=pflash"
+ pflash_args = pflash_args_str.format(flash0_path, flash1_path)
+ return pflash_args.split(" ")
+
+def get_flash_path(flash_dir, name):
+ return os.path.join(flash_dir, "{}.img".format(name))
from qemu.machine import QEMUMachine
import subprocess
import hashlib
-import optparse
+import argparse
import atexit
import tempfile
import shutil
import multiprocessing
import traceback
-
-SSH_KEY = open(os.path.join(os.path.dirname(__file__),
- "..", "keys", "id_rsa")).read()
-SSH_PUB_KEY = open(os.path.join(os.path.dirname(__file__),
- "..", "keys", "id_rsa.pub")).read()
-
+import shlex
+
+SSH_KEY_FILE = os.path.join(os.path.dirname(__file__),
+ "..", "keys", "id_rsa")
+SSH_PUB_KEY_FILE = os.path.join(os.path.dirname(__file__),
+ "..", "keys", "id_rsa.pub")
+
+# This is the standard configuration.
+# Any or all of these can be overridden by
+# passing in a config argument to the VM constructor.
+DEFAULT_CONFIG = {
+ 'cpu' : "max",
+ 'machine' : 'pc',
+ 'guest_user' : "qemu",
+ 'guest_pass' : "qemupass",
+ 'root_pass' : "qemupass",
+ 'ssh_key_file' : SSH_KEY_FILE,
+ 'ssh_pub_key_file': SSH_PUB_KEY_FILE,
+ 'memory' : "4G",
+ 'extra_args' : [],
+ 'qemu_args' : "",
+ 'dns' : "",
+ 'ssh_port' : 0,
+ 'install_cmds' : "",
+ 'boot_dev_type' : "block",
+ 'ssh_timeout' : 1,
+}
+BOOT_DEVICE = {
+ 'block' : "-drive file={},if=none,id=drive0,cache=writeback "\
+ "-device virtio-blk,drive=drive0,bootindex=0",
+ 'scsi' : "-device virtio-scsi-device,id=scsi "\
+ "-drive file={},format=raw,if=none,id=hd0 "\
+ "-device scsi-hd,drive=hd0,bootindex=0",
+}
class BaseVM(object):
- GUEST_USER = "qemu"
- GUEST_PASS = "qemupass"
- ROOT_PASS = "qemupass"
envvars = [
"https_proxy",
poweroff = "poweroff"
# enable IPv6 networking
ipv6 = True
+ # This is the timeout on the wait for console bytes.
+ socket_timeout = 120
# Scale up some timeouts under TCG.
# 4 is arbitrary, but greater than 2,
# since we found we need to wait more than twice as long.
tcg_ssh_timeout_multiplier = 4
- def __init__(self, debug=False, vcpus=None, genisoimage=None,
- build_path=None):
+ def __init__(self, args, config=None):
self._guest = None
- self._genisoimage = genisoimage
- self._build_path = build_path
+ self._genisoimage = args.genisoimage
+ self._build_path = args.build_path
+ self._efi_aarch64 = args.efi_aarch64
+ # Allow input config to override defaults.
+ self._config = DEFAULT_CONFIG.copy()
+ if config != None:
+ self._config.update(config)
+ self.validate_ssh_keys()
self._tmpdir = os.path.realpath(tempfile.mkdtemp(prefix="vm-test-",
suffix=".tmp",
dir="."))
atexit.register(shutil.rmtree, self._tmpdir)
-
- self._ssh_key_file = os.path.join(self._tmpdir, "id_rsa")
- open(self._ssh_key_file, "w").write(SSH_KEY)
- subprocess.check_call(["chmod", "600", self._ssh_key_file])
-
- self._ssh_pub_key_file = os.path.join(self._tmpdir, "id_rsa.pub")
- open(self._ssh_pub_key_file, "w").write(SSH_PUB_KEY)
-
- self.debug = debug
+ # Copy the key files to a temporary directory.
+ # Also chmod the key file to agree with ssh requirements.
+ self._config['ssh_key'] = \
+ open(self._config['ssh_key_file']).read().rstrip()
+ self._config['ssh_pub_key'] = \
+ open(self._config['ssh_pub_key_file']).read().rstrip()
+ self._ssh_tmp_key_file = os.path.join(self._tmpdir, "id_rsa")
+ open(self._ssh_tmp_key_file, "w").write(self._config['ssh_key'])
+ subprocess.check_call(["chmod", "600", self._ssh_tmp_key_file])
+
+ self._ssh_tmp_pub_key_file = os.path.join(self._tmpdir, "id_rsa.pub")
+ open(self._ssh_tmp_pub_key_file,
+ "w").write(self._config['ssh_pub_key'])
+
+ self.debug = args.debug
+ self._console_log_path = None
+ if args.log_console:
+ self._console_log_path = \
+ os.path.join(os.path.expanduser("~/.cache/qemu-vm"),
+ "{}.install.log".format(self.name))
self._stderr = sys.stderr
self._devnull = open(os.devnull, "w")
if self.debug:
self._stdout = sys.stdout
else:
self._stdout = self._devnull
+ netdev = "user,id=vnet,hostfwd=:127.0.0.1:{}-:22"
self._args = [ \
- "-nodefaults", "-m", "4G",
- "-cpu", "max",
- "-netdev", "user,id=vnet,hostfwd=:127.0.0.1:0-:22" +
- (",ipv6=no" if not self.ipv6 else ""),
+ "-nodefaults", "-m", self._config['memory'],
+ "-cpu", self._config['cpu'],
+ "-netdev",
+ netdev.format(self._config['ssh_port']) +
+ (",ipv6=no" if not self.ipv6 else "") +
+ (",dns=" + self._config['dns'] if self._config['dns'] else ""),
"-device", "virtio-net-pci,netdev=vnet",
"-vnc", "127.0.0.1:0,to=20"]
- if vcpus and vcpus > 1:
- self._args += ["-smp", "%d" % vcpus]
+ if args.jobs and args.jobs > 1:
+ self._args += ["-smp", "%d" % args.jobs]
if kvm_available(self.arch):
self._args += ["-enable-kvm"]
else:
logging.info("KVM not available, not using -enable-kvm")
self._data_args = []
+ if self._config['qemu_args'] != None:
+ qemu_args = self._config['qemu_args']
+ qemu_args = qemu_args.replace('\n',' ').replace('\r','')
+ # shlex groups quoted arguments together
+ # we need this to keep the quoted args together for when
+ # the QEMU command is issued later.
+ args = shlex.split(qemu_args)
+ self._config['extra_args'] = []
+ for arg in args:
+ if arg:
+ # Preserve quotes around arguments.
+ # shlex above takes them out, so add them in.
+ if " " in arg:
+ arg = '"{}"'.format(arg)
+ self._config['extra_args'].append(arg)
+
+ def validate_ssh_keys(self):
+ """Check to see if the ssh key files exist."""
+ if 'ssh_key_file' not in self._config or\
+ not os.path.exists(self._config['ssh_key_file']):
+ raise Exception("ssh key file not found.")
+ if 'ssh_pub_key_file' not in self._config or\
+ not os.path.exists(self._config['ssh_pub_key_file']):
+ raise Exception("ssh pub key file not found.")
+
+ def wait_boot(self, wait_string=None):
+ """Wait for the standard string we expect
+ on completion of a normal boot.
+ The user can also choose to override with an
+ alternate string to wait for."""
+ if wait_string is None:
+ if self.login_prompt is None:
+ raise Exception("self.login_prompt not defined")
+ wait_string = self.login_prompt
+ # Intentionally bump up the default timeout under TCG,
+ # since the console wait below takes longer.
+ timeout = self.socket_timeout
+ if not kvm_available(self.arch):
+ timeout *= 8
+ self.console_init(timeout=timeout)
+ self.console_wait(wait_string)
+
def _download_with_cache(self, url, sha256sum=None, sha512sum=None):
def check_sha256sum(fname):
if not sha256sum:
"-t",
"-o", "StrictHostKeyChecking=no",
"-o", "UserKnownHostsFile=" + os.devnull,
- "-o", "ConnectTimeout=1",
- "-p", self.ssh_port, "-i", self._ssh_key_file]
+ "-o",
+ "ConnectTimeout={}".format(self._config["ssh_timeout"]),
+ "-p", self.ssh_port, "-i", self._ssh_tmp_key_file]
# If not in debug mode, set ssh to quiet mode to
# avoid printing the results of commands.
if not self.debug:
return r
def ssh(self, *cmd):
- return self._ssh_do(self.GUEST_USER, cmd, False)
+ return self._ssh_do(self._config["guest_user"], cmd, False)
def ssh_root(self, *cmd):
return self._ssh_do("root", cmd, False)
def ssh_check(self, *cmd):
- self._ssh_do(self.GUEST_USER, cmd, True)
+ self._ssh_do(self._config["guest_user"], cmd, True)
def ssh_root_check(self, *cmd):
self._ssh_do("root", cmd, True)
"virtio-blk,drive=%s,serial=%s,bootindex=1" % (name, name)]
def boot(self, img, extra_args=[]):
- args = self._args + [
- "-drive", "file=%s,if=none,id=drive0,cache=writeback" % img,
- "-device", "virtio-blk,drive=drive0,bootindex=0"]
- args += self._data_args + extra_args
+ boot_dev = BOOT_DEVICE[self._config['boot_dev_type']]
+ boot_params = boot_dev.format(img)
+ args = self._args + boot_params.split(' ')
+ args += self._data_args + extra_args + self._config['extra_args']
logging.debug("QEMU args: %s", " ".join(args))
qemu_path = get_qemu_path(self.arch, self._build_path)
- guest = QEMUMachine(binary=qemu_path, args=args)
- guest.set_machine('pc')
+
+ # Since console_log_path is only set when the user provides the
+ # log_console option, we will set drain_console=True so the
+ # console is always drained.
+ guest = QEMUMachine(binary=qemu_path, args=args,
+ console_log=self._console_log_path,
+ drain_console=True)
+ guest.set_machine(self._config['machine'])
guest.set_console()
try:
guest.launch()
raise
atexit.register(self.shutdown)
self._guest = guest
+ # Init console so we can start consuming the chars.
+ self.console_init()
usernet_info = guest.qmp("human-monitor-command",
command_line="info usernet")
self.ssh_port = None
raise Exception("Cannot find ssh port from 'info usernet':\n%s" % \
usernet_info)
- def console_init(self, timeout = 120):
+ def console_init(self, timeout = None):
+ if timeout == None:
+ timeout = self.socket_timeout
vm = self._guest
vm.console_socket.settimeout(timeout)
self.console_raw_path = os.path.join(vm._temp_dir,
self.console_send(command)
def console_ssh_init(self, prompt, user, pw):
- sshkey_cmd = "echo '%s' > .ssh/authorized_keys\n" % SSH_PUB_KEY.rstrip()
+ sshkey_cmd = "echo '%s' > .ssh/authorized_keys\n" \
+ % self._config['ssh_pub_key'].rstrip()
self.console_wait_send("login:", "%s\n" % user)
self.console_wait_send("Password:", "%s\n" % pw)
self.console_wait_send(prompt, "mkdir .ssh\n")
"local-hostname: {}-guest\n".format(name)])
mdata.close()
udata = open(os.path.join(cidir, "user-data"), "w")
- print("guest user:pw {}:{}".format(self.GUEST_USER,
- self.GUEST_PASS))
+ print("guest user:pw {}:{}".format(self._config['guest_user'],
+ self._config['guest_pass']))
udata.writelines(["#cloud-config\n",
"chpasswd:\n",
" list: |\n",
- " root:%s\n" % self.ROOT_PASS,
- " %s:%s\n" % (self.GUEST_USER,
- self.GUEST_PASS),
+ " root:%s\n" % self._config['root_pass'],
+ " %s:%s\n" % (self._config['guest_user'],
+ self._config['guest_pass']),
" expire: False\n",
"users:\n",
- " - name: %s\n" % self.GUEST_USER,
+ " - name: %s\n" % self._config['guest_user'],
" sudo: ALL=(ALL) NOPASSWD:ALL\n",
" ssh-authorized-keys:\n",
- " - %s\n" % SSH_PUB_KEY,
+ " - %s\n" % self._config['ssh_pub_key'],
" - name: root\n",
" ssh-authorized-keys:\n",
- " - %s\n" % SSH_PUB_KEY,
+ " - %s\n" % self._config['ssh_pub_key'],
"locale: en_US.UTF-8\n"])
proxy = os.environ.get("http_proxy")
if not proxy is None:
cwd=cidir,
stdin=self._devnull, stdout=self._stdout,
stderr=self._stdout)
-
return os.path.join(cidir, "cloud-init.iso")
def get_qemu_path(arch, build_path=None):
qemu_path = "qemu-system-" + arch
return qemu_path
+def get_qemu_version(qemu_path):
+ """Get the version number from the current QEMU,
+ and return the major number."""
+ output = subprocess.check_output([qemu_path, '--version'])
+ version_line = output.decode("utf-8")
+ version_num = re.split(' |\(', version_line)[3].split('.')[0]
+ return int(version_num)
+
+def parse_config(config, args):
+ """ Parse yaml config and populate our config structure.
+ The yaml config allows the user to override the
+ defaults for VM parameters. In many cases these
+ defaults can be overridden without rebuilding the VM."""
+ if args.config:
+ config_file = args.config
+ elif 'QEMU_CONFIG' in os.environ:
+ config_file = os.environ['QEMU_CONFIG']
+ else:
+ return config
+ if not os.path.exists(config_file):
+ raise Exception("config file {} does not exist".format(config_file))
+ # We gracefully handle importing the yaml module
+ # since it might not be installed.
+ # If we are here it means the user supplied a .yml file,
+ # so if the yaml module is not installed we will exit with error.
+ try:
+ import yaml
+ except ImportError:
+ print("The python3-yaml package is needed "\
+ "to support config.yaml files")
+ # Instead of raising an exception we exit to avoid
+ # a raft of messy (expected) errors to stdout.
+ exit(1)
+ with open(config_file) as f:
+ yaml_dict = yaml.safe_load(f)
+
+ if 'qemu-conf' in yaml_dict:
+ config.update(yaml_dict['qemu-conf'])
+ else:
+ raise Exception("config file {} is not valid"\
+ " missing qemu-conf".format(config_file))
+ return config
+
def parse_args(vmcls):
def get_default_jobs():
- if kvm_available(vmcls.arch):
- return multiprocessing.cpu_count() // 2
+ if multiprocessing.cpu_count() > 1:
+ if kvm_available(vmcls.arch):
+ return multiprocessing.cpu_count() // 2
+ elif os.uname().machine == "x86_64" and \
+ vmcls.arch in ["aarch64", "x86_64", "i386"]:
+ # MTTCG is available on these arches and we can allow
+ # more cores. but only up to a reasonable limit. User
+ # can always override these limits with --jobs.
+ return min(multiprocessing.cpu_count() // 2, 8)
else:
return 1
- parser = optparse.OptionParser(
- description="VM test utility. Exit codes: "
- "0 = success, "
- "1 = command line error, "
- "2 = environment initialization failed, "
- "3 = test command failed")
- parser.add_option("--debug", "-D", action="store_true",
- help="enable debug output")
- parser.add_option("--image", "-i", default="%s.img" % vmcls.name,
- help="image file name")
- parser.add_option("--force", "-f", action="store_true",
- help="force build image even if image exists")
- parser.add_option("--jobs", type=int, default=get_default_jobs(),
- help="number of virtual CPUs")
- parser.add_option("--verbose", "-V", action="store_true",
- help="Pass V=1 to builds within the guest")
- parser.add_option("--build-image", "-b", action="store_true",
- help="build image")
- parser.add_option("--build-qemu",
- help="build QEMU from source in guest")
- parser.add_option("--build-target",
- help="QEMU build target", default="check")
- parser.add_option("--build-path", default=None,
- help="Path of build directory, "\
- "for using build tree QEMU binary. ")
- parser.add_option("--interactive", "-I", action="store_true",
- help="Interactively run command")
- parser.add_option("--snapshot", "-s", action="store_true",
- help="run tests with a snapshot")
- parser.add_option("--genisoimage", default="genisoimage",
- help="iso imaging tool")
- parser.disable_interspersed_args()
+ parser = argparse.ArgumentParser(
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+ description="Utility for provisioning VMs and running builds",
+ epilog="""Remaining arguments are passed to the command.
+ Exit codes: 0 = success, 1 = command line error,
+ 2 = environment initialization failed,
+ 3 = test command failed""")
+ parser.add_argument("--debug", "-D", action="store_true",
+ help="enable debug output")
+ parser.add_argument("--image", "-i", default="%s.img" % vmcls.name,
+ help="image file name")
+ parser.add_argument("--force", "-f", action="store_true",
+ help="force build image even if image exists")
+ parser.add_argument("--jobs", type=int, default=get_default_jobs(),
+ help="number of virtual CPUs")
+ parser.add_argument("--verbose", "-V", action="store_true",
+ help="Pass V=1 to builds within the guest")
+ parser.add_argument("--build-image", "-b", action="store_true",
+ help="build image")
+ parser.add_argument("--build-qemu",
+ help="build QEMU from source in guest")
+ parser.add_argument("--build-target",
+ help="QEMU build target", default="check")
+ parser.add_argument("--build-path", default=None,
+ help="Path of build directory, "\
+ "for using build tree QEMU binary. ")
+ parser.add_argument("--interactive", "-I", action="store_true",
+ help="Interactively run command")
+ parser.add_argument("--snapshot", "-s", action="store_true",
+ help="run tests with a snapshot")
+ parser.add_argument("--genisoimage", default="genisoimage",
+ help="iso imaging tool")
+ parser.add_argument("--config", "-c", default=None,
+ help="Provide config yaml for configuration. "\
+ "See config_example.yaml for example.")
+ parser.add_argument("--efi-aarch64",
+ default="/usr/share/qemu-efi-aarch64/QEMU_EFI.fd",
+ help="Path to efi image for aarch64 VMs.")
+ parser.add_argument("--log-console", action="store_true",
+ help="Log console to file.")
+ parser.add_argument("commands", nargs="*", help="""Remaining
+ commands after -- are passed to command inside the VM""")
+
return parser.parse_args()
-def main(vmcls):
+def main(vmcls, config=None):
try:
- args, argv = parse_args(vmcls)
- if not argv and not args.build_qemu and not args.build_image:
+ if config == None:
+ config = DEFAULT_CONFIG
+ args = parse_args(vmcls)
+ if not args.commands and not args.build_qemu and not args.build_image:
print("Nothing to do?")
return 1
+ config = parse_config(config, args)
logging.basicConfig(level=(logging.DEBUG if args.debug
else logging.WARN))
- vm = vmcls(debug=args.debug, vcpus=args.jobs,
- genisoimage=args.genisoimage, build_path=args.build_path)
+ vm = vmcls(args, config=config)
if args.build_image:
if os.path.exists(args.image) and not args.force:
sys.stderr.writelines(["Image file exists: %s\n" % args.image,
if args.build_qemu:
vm.add_source_dir(args.build_qemu)
cmd = [vm.BUILD_SCRIPT.format(
- configure_opts = " ".join(argv),
+ configure_opts = " ".join(args.commands),
jobs=int(args.jobs),
target=args.build_target,
verbose = "V=1" if args.verbose else "")]
else:
- cmd = argv
+ cmd = args.commands
img = args.image
if args.snapshot:
img += ",snapshot=on"
--- /dev/null
+# CentOS aarch64 image kickstart file.
+# This file is used by the CentOS installer to
+# script the generation of the image.
+#
+# Copyright 2020 Linaro
+#
+ignoredisk --only-use=vda
+# System bootloader configuration
+bootloader --append=" crashkernel=auto" --location=mbr --boot-drive=vda
+autopart --type=plain
+# Partition clearing information
+clearpart --linux --initlabel --drives=vda
+# Use text mode install
+text
+repo --name="AppStream" --baseurl=file:///run/install/repo/AppStream
+# Use CDROM installation media
+cdrom
+# Keyboard layouts
+keyboard --vckeymap=us --xlayouts=''
+# System language
+lang en_US.UTF-8
+
+# Network information
+network --bootproto=dhcp --device=enp0s1 --onboot=off --ipv6=auto --no-activate
+network --hostname=localhost.localdomain
+# Run the Setup Agent on first boot
+firstboot --enable
+# Do not configure the X Window System
+skipx
+# System services
+services --enabled="chronyd"
+# System timezone
+timezone America/New_York --isUtc
+
+# Shutdown after installation is complete.
+shutdown
+
+%packages
+@^server-product-environment
+kexec-tools
+
+%end
+
+%addon com_redhat_kdump --enable --reserve-mb='auto'
+
+%end
+%anaconda
+pwpolicy root --minlen=6 --minquality=1 --notstrict --nochanges --notempty
+pwpolicy user --minlen=6 --minquality=1 --notstrict --nochanges --emptyok
+pwpolicy luks --minlen=6 --minquality=1 --notstrict --nochanges --notempty
+%end
--- /dev/null
+#!/usr/bin/env python3
+#
+# Centos aarch64 image
+#
+# Copyright 2020 Linaro
+#
+# Authors:
+# Robert Foley <robert.foley@linaro.org>
+# Originally based on ubuntu.aarch64
+#
+# This code is licensed under the GPL version 2 or later. See
+# the COPYING file in the top-level directory.
+#
+
+import os
+import sys
+import subprocess
+import basevm
+import time
+import traceback
+import aarch64vm
+
+DEFAULT_CONFIG = {
+ 'cpu' : "max",
+ 'machine' : "virt,gic-version=max",
+ 'install_cmds' : "yum install -y make git python3 gcc gcc-c++ flex bison, "\
+ "yum install -y glib2-devel pixman-devel zlib-devel, "\
+ "yum install -y perl-Test-Harness, "\
+ "alternatives --set python /usr/bin/python3, "\
+ "sudo dnf config-manager "\
+ "--add-repo=https://download.docker.com/linux/centos/docker-ce.repo,"\
+ "sudo dnf install --nobest -y docker-ce.aarch64,"\
+ "systemctl enable docker",
+ # We increase beyond the default time since during boot
+ # it can take some time (many seconds) to log into the VM.
+ 'ssh_timeout' : 60,
+}
+
+class CentosAarch64VM(basevm.BaseVM):
+ name = "centos.aarch64"
+ arch = "aarch64"
+ login_prompt = "localhost login:"
+ prompt = '[root@localhost ~]#'
+ image_name = "CentOS-8-aarch64-1905-dvd1.iso"
+ image_link = "http://mirrors.usc.edu/pub/linux/distributions/centos/8.0.1905/isos/aarch64/"
+ image_link += image_name
+ BUILD_SCRIPT = """
+ set -e;
+ cd $(mktemp -d);
+ sudo chmod a+r /dev/vdb;
+ tar --checkpoint=.10 -xf /dev/vdb;
+ ./configure {configure_opts};
+ make --output-sync {target} -j{jobs} {verbose};
+ """
+ def set_key_perm(self):
+ """Set permissions properly on certain files to allow
+ ssh access."""
+ self.console_wait_send(self.prompt,
+ "/usr/sbin/restorecon -R -v /root/.ssh\n")
+ self.console_wait_send(self.prompt,
+ "/usr/sbin/restorecon -R -v "\
+ "/home/{}/.ssh\n".format(self._config["guest_user"]))
+
+ def create_kickstart(self):
+ """Generate the kickstart file used to generate the centos image."""
+ # Start with the template for the kickstart.
+ ks_file = "../tests/vm/centos-8-aarch64.ks"
+ subprocess.check_call("cp {} ./ks.cfg".format(ks_file), shell=True)
+ # Append the ssh keys to the kickstart file
+ # as the post processing phase of installation.
+ with open("ks.cfg", "a") as f:
+ # Add in the root pw and guest user.
+ rootpw = "rootpw --plaintext {}\n"
+ f.write(rootpw.format(self._config["root_pass"]))
+ add_user = "user --groups=wheel --name={} "\
+ "--password={} --plaintext\n"
+ f.write(add_user.format(self._config["guest_user"],
+ self._config["guest_pass"]))
+ # Add the ssh keys.
+ f.write("%post --log=/root/ks-post.log\n")
+ f.write("mkdir -p /root/.ssh\n")
+ addkey = 'echo "{}" >> /root/.ssh/authorized_keys\n'
+ addkey_cmd = addkey.format(self._config["ssh_pub_key"])
+ f.write(addkey_cmd)
+ f.write('mkdir -p /home/{}/.ssh\n'.format(self._config["guest_user"]))
+ addkey = 'echo "{}" >> /home/{}/.ssh/authorized_keys\n'
+ addkey_cmd = addkey.format(self._config["ssh_pub_key"],
+ self._config["guest_user"])
+ f.write(addkey_cmd)
+ f.write("%end\n")
+ # Take our kickstart file and create an .iso from it.
+ # The .iso will be provided to qemu as we boot
+ # from the install dvd.
+ # Anaconda will recognize the label "OEMDRV" and will
+ # start the automated installation.
+ gen_iso_img = 'genisoimage -output ks.iso -volid "OEMDRV" ks.cfg'
+ subprocess.check_call(gen_iso_img, shell=True)
+
+ def wait_for_shutdown(self):
+ """We wait for qemu to shutdown the VM and exit.
+ While this happens we display the console view
+ for easier debugging."""
+ # The image creation is essentially done,
+ # so whether or not the wait is successful we want to
+ # wait for qemu to exit (the self.wait()) before we return.
+ try:
+ self.console_wait("reboot: Power down")
+ except Exception as e:
+ sys.stderr.write("Exception hit\n")
+ if isinstance(e, SystemExit) and e.code == 0:
+ return 0
+ traceback.print_exc()
+ finally:
+ self.wait()
+
+ def build_base_image(self, dest_img):
+ """Run through the centos installer to create
+ a base image with name dest_img."""
+ # We create the temp image, and only rename
+ # to destination when we are done.
+ img = dest_img + ".tmp"
+ # Create an empty image.
+ # We will provide this as the install destination.
+ qemu_img_create = "qemu-img create {} 50G".format(img)
+ subprocess.check_call(qemu_img_create, shell=True)
+
+ # Create our kickstart file to be fed to the installer.
+ self.create_kickstart()
+ # Boot the install dvd with the params as our ks.iso
+ os_img = self._download_with_cache(self.image_link)
+ dvd_iso = "centos-8-dvd.iso"
+ subprocess.check_call(["cp", "-f", os_img, dvd_iso])
+ extra_args = "-cdrom ks.iso"
+ extra_args += " -drive file={},if=none,id=drive1,cache=writeback"
+ extra_args += " -device virtio-blk,drive=drive1,bootindex=1"
+ extra_args = extra_args.format(dvd_iso).split(" ")
+ self.boot(img, extra_args=extra_args)
+ self.console_wait_send("change the selection", "\n")
+ # We seem to need to hit esc (chr(27)) twice to abort the
+ # media check, which takes a long time.
+ # Waiting a bit seems to be more reliable before hitting esc.
+ self.console_wait("Checking")
+ time.sleep(5)
+ self.console_wait_send("Checking", chr(27))
+ time.sleep(5)
+ self.console_wait_send("Checking", chr(27))
+ print("Found Checking")
+ # Give sufficient time for the installer to create the image.
+ self.console_init(timeout=7200)
+ self.wait_for_shutdown()
+ os.rename(img, dest_img)
+ print("Done with base image build: {}".format(dest_img))
+
+ def check_create_base_img(self, img_base, img_dest):
+ """Create a base image using the installer.
+ We will use the base image if it exists.
+ This helps cut down on install time in case we
+ need to restart image creation,
+ since the base image creation can take a long time."""
+ if not os.path.exists(img_base):
+ print("Generate new base image: {}".format(img_base))
+ self.build_base_image(img_base);
+ else:
+ print("Use existing base image: {}".format(img_base))
+ # Save a copy of the base image and copy it to dest.
+ # which we will use going forward.
+ subprocess.check_call(["cp", img_base, img_dest])
+
+ def boot(self, img, extra_args=None):
+ aarch64vm.create_flash_images(self._tmpdir, self._efi_aarch64)
+ default_args = aarch64vm.get_pflash_args(self._tmpdir)
+ if extra_args:
+ extra_args.extend(default_args)
+ else:
+ extra_args = default_args
+ # We always add these performance tweaks
+ # because without them, we boot so slowly that we
+ # can time out finding the boot efi device.
+ if '-smp' not in extra_args and \
+ '-smp' not in self._config['extra_args'] and \
+ '-smp' not in self._args:
+ # Only add if not already there to give caller option to change it.
+ extra_args.extend(["-smp", "8"])
+ # We have overridden boot() since aarch64 has additional parameters.
+ # Call down to the base class method.
+ super(CentosAarch64VM, self).boot(img, extra_args=extra_args)
+
+ def build_image(self, img):
+ img_tmp = img + ".tmp"
+ self.check_create_base_img(img + ".base", img_tmp)
+
+ # Boot the new image for the first time to finish installation.
+ self.boot(img_tmp)
+ self.console_init()
+ self.console_wait_send(self.login_prompt, "root\n")
+ self.console_wait_send("Password:",
+ "{}\n".format(self._config["root_pass"]))
+
+ self.set_key_perm()
+ self.console_wait_send(self.prompt, "rpm -q centos-release\n")
+ enable_adapter = "sed -i 's/ONBOOT=no/ONBOOT=yes/g'" \
+ " /etc/sysconfig/network-scripts/ifcfg-enp0s1\n"
+ self.console_wait_send(self.prompt, enable_adapter)
+ self.console_wait_send(self.prompt, "ifup enp0s1\n")
+ self.console_wait_send(self.prompt,
+ 'echo "qemu ALL=(ALL) NOPASSWD:ALL" | '\
+ 'sudo tee /etc/sudoers.d/qemu\n')
+ self.console_wait(self.prompt)
+
+ # Rest of the commands we issue through ssh.
+ self.wait_ssh(wait_root=True)
+
+ # If the user chooses *not* to do the second phase,
+ # then we will jump right to the graceful shutdown
+ if self._config['install_cmds'] != "":
+ install_cmds = self._config['install_cmds'].split(',')
+ for cmd in install_cmds:
+ self.ssh_root(cmd)
+ self.ssh_root("poweroff")
+ self.wait_for_shutdown()
+ os.rename(img_tmp, img)
+ print("image creation complete: {}".format(img))
+ return 0
+
+if __name__ == "__main__":
+ defaults = aarch64vm.get_config_defaults(CentosAarch64VM, DEFAULT_CONFIG)
+ sys.exit(basevm.main(CentosAarch64VM, defaults))
--- /dev/null
+#
+# Example yaml for use by any of the scripts in tests/vm.
+# Can be provided as an environment variable QEMU_CONFIG
+#
+qemu-conf:
+
+ # If any of the below are not provided, we will just use the qemu defaults.
+
+ # Login username and password(has to be sudo enabled)
+ guest_user: qemu
+ guest_pass: "qemupass"
+
+ # Password for root user can be different from guest.
+ root_pass: "qemupass"
+
+ # If one key is provided, both must be provided.
+ #ssh_key: /complete/path/of/your/keyfile/id_rsa
+ #ssh_pub_key: /complete/path/of/your/keyfile/id_rsa.pub
+
+ cpu: max
+ machine: virt,gic-version=max
+ memory: 16G
+
+ # The below is a example for how to configure NUMA topology with
+ # 4 NUMA nodes and 2 different NUMA distances.
+ qemu_args: "-smp cpus=16,sockets=2,cores=8
+ -numa node,cpus=0-3,nodeid=0 -numa node,cpus=4-7,nodeid=1
+ -numa node,cpus=8-11,nodeid=2 -numa node,cpus=12-15,nodeid=3
+ -numa dist,src=0,dst=1,val=15 -numa dist,src=2,dst=3,val=15
+ -numa dist,src=0,dst=2,val=20 -numa dist,src=0,dst=3,val=20
+ -numa dist,src=1,dst=2,val=20 -numa dist,src=1,dst=3,val=20"
+
+ # By default we do not set the DNS.
+ # You override the defaults by setting the below.
+ #dns: 1.234.567.89
+
+ # By default we will use a "block" device, but
+ # you can also boot from a "scsi" device.
+ # Just keep in mind your scripts might need to change
+ # As you will have /dev/sda instead of /dev/vda (for block device)
+ boot_dev_type: "block"
+
+ # By default the ssh port is not fixed.
+ # A fixed ssh port makes it easier for automated tests.
+ #ssh_port: 5555
+
+ # To install a different set of packages, provide a command to issue
+ #install_cmds: "apt-get update ; apt-get build-dep -y qemu"
+
+ # Or to skip the install entirely, just provide ""
+ #install_cmds: ""
--- /dev/null
+#
+# Example yaml for use by any of the x86 based scripts in tests/vm.
+# Can be provided as an environment variable QEMU_CONFIG
+#
+qemu-conf:
+
+ # If any of the below are not provided, we will just use the qemu defaults.
+
+ # Login username and password(has to be sudo enabled)
+ guest_user: "qemu"
+ guest_pass: "qemupass"
+
+ # Password for root user can be different from guest.
+ root_pass: "qemupass"
+
+ # Provide default ssh keys of current user.
+ # You need to edit the below for your user.
+ #ssh_key_file: /home/<user>/.ssh/id_rsa
+ #ssh_pub_key_file: /home/<user>/.ssh/id_rsa.pub
+
+ cpu: max
+ machine: pc
+ memory: 8G
+
+ # The below is a example for how to configure NUMA topology with
+ # 4 NUMA nodes and 2 different NUMA distances.
+ qemu_args: "-smp cpus=8,sockets=2,cores=4
+ -object memory-backend-ram,size=4G,policy=bind,host-nodes=0,id=ram-node0
+ -object memory-backend-ram,size=4G,policy=bind,host-nodes=0,id=ram-node1
+ -object memory-backend-ram,size=4G,policy=bind,host-nodes=1,id=ram-node2
+ -object memory-backend-ram,size=4G,policy=bind,host-nodes=1,id=ram-node3
+ -numa node,cpus=0-1,nodeid=0 -numa node,cpus=2-3,nodeid=1
+ -numa node,cpus=4-5,nodeid=2 -numa node,cpus=6-7,nodeid=3
+ -numa dist,src=0,dst=1,val=15 -numa dist,src=2,dst=3,val=15
+ -numa dist,src=0,dst=2,val=20 -numa dist,src=0,dst=3,val=20
+ -numa dist,src=1,dst=2,val=20 -numa dist,src=1,dst=3,val=20"
+
+ # By default we do not set the DNS.
+ # You override the defaults by setting the below.
+ #dns: "1.234.567.89"
+
+ # By default we will use a "block" device, but
+ # you can also boot from a "scsi" device.
+ # Just keep in mind your scripts might need to change
+ # As you will have /dev/sda instead of /dev/vda (for block device)
+ boot_dev_type: "block"
+
+ # By default the ssh port is not fixed.
+ # A fixed ssh port makes it easier for automated tests.
+ ssh_port: 5555
self.console_wait_send("7) [!] Root password", "7\n")
self.console_wait("Password:")
- self.console_send("%s\n" % self.ROOT_PASS)
+ self.console_send("%s\n" % self._config["root_pass"])
self.console_wait("Password (confirm):")
- self.console_send("%s\n" % self.ROOT_PASS)
+ self.console_send("%s\n" % self._config["root_pass"])
self.console_wait_send("8) [ ] User creation", "8\n")
self.console_wait_send("1) [ ] Create user", "1\n")
self.console_wait_send("3) User name", "3\n")
- self.console_wait_send("ENTER:", "%s\n" % self.GUEST_USER)
+ self.console_wait_send("ENTER:", "%s\n" % self._config["guest_user"])
self.console_wait_send("4) [ ] Use password", "4\n")
self.console_wait_send("5) Password", "5\n")
self.console_wait("Password:")
- self.console_send("%s\n" % self.GUEST_PASS)
+ self.console_send("%s\n" % self._config["guest_pass"])
self.console_wait("Password (confirm):")
- self.console_send("%s\n" % self.GUEST_PASS)
+ self.console_send("%s\n" % self._config["guest_pass"])
self.console_wait_send("7) Groups", "c\n")
while True:
if good:
break
time.sleep(10)
- self.console_send("r\n" % self.GUEST_PASS)
+ self.console_send("r\n" % self._config["guest_pass"])
self.console_wait_send("'b' to begin install", "b\n")
# setup qemu user
prompt = " ~]$"
- self.console_ssh_init(prompt, self.GUEST_USER, self.GUEST_PASS)
+ self.console_ssh_init(prompt, self._config["guest_user"],
+ self._config["guest_pass"])
self.console_wait_send(prompt, "exit\n")
# setup root user
prompt = " ~]#"
- self.console_ssh_init(prompt, "root", self.ROOT_PASS)
+ self.console_ssh_init(prompt, "root", self._config["root_pass"])
self.console_sshd_config(prompt)
# setup virtio-blk #1 (tarfile)
# post-install configuration
self.console_wait("New Password:")
- self.console_send("%s\n" % self.ROOT_PASS)
+ self.console_send("%s\n" % self._config["root_pass"])
self.console_wait("Retype New Password:")
- self.console_send("%s\n" % self.ROOT_PASS)
+ self.console_send("%s\n" % self._config["root_pass"])
self.console_wait_send("Network Configuration", "\n")
self.console_wait_send("IPv4", "y")
# qemu user
self.console_wait_send("Add User Accounts", "y")
self.console_wait("Username")
- self.console_send("%s\n" % self.GUEST_USER)
+ self.console_send("%s\n" % self._config["guest_user"])
self.console_wait("Full name")
- self.console_send("%s\n" % self.GUEST_USER)
+ self.console_send("%s\n" % self._config["guest_user"])
self.console_wait_send("Uid", "\n")
self.console_wait_send("Login group", "\n")
self.console_wait_send("Login group", "\n")
self.console_wait_send("Use an empty password", "\n")
self.console_wait_send("Use a random password", "\n")
self.console_wait("Enter password:")
- self.console_send("%s\n" % self.GUEST_PASS)
+ self.console_send("%s\n" % self._config["guest_pass"])
self.console_wait("Enter password again:")
- self.console_send("%s\n" % self.GUEST_PASS)
+ self.console_send("%s\n" % self._config["guest_pass"])
self.console_wait_send("Lock out", "\n")
self.console_wait_send("OK", "yes\n")
self.console_wait_send("Add another user", "no\n")
# setup qemu user
prompt = "$"
- self.console_ssh_init(prompt, self.GUEST_USER, self.GUEST_PASS)
+ self.console_ssh_init(prompt, self._config["guest_user"], self._config["guest_pass"])
self.console_wait_send(prompt, "exit\n")
# setup root user
prompt = "root@freebsd:~ #"
- self.console_ssh_init(prompt, "root", self.ROOT_PASS)
+ self.console_ssh_init(prompt, "root", self._config["root_pass"])
self.console_sshd_config(prompt)
# setup serial console
self.console_wait_send("d: Change root password", "d\n")
self.console_wait_send("a: Yes", "a\n")
self.console_wait("New password:")
- self.console_send("%s\n" % self.ROOT_PASS)
+ self.console_send("%s\n" % self._config["root_pass"])
self.console_wait("New password:")
- self.console_send("%s\n" % self.ROOT_PASS)
+ self.console_send("%s\n" % self._config["root_pass"])
self.console_wait("Retype new password:")
- self.console_send("%s\n" % self.ROOT_PASS)
+ self.console_send("%s\n" % self._config["root_pass"])
self.console_wait_send("o: Add a user", "o\n")
self.console_wait("username")
- self.console_send("%s\n" % self.GUEST_USER)
+ self.console_send("%s\n" % self._config["guest_user"])
self.console_wait("to group wheel")
self.console_wait_send("a: Yes", "a\n")
self.console_wait_send("a: /bin/sh", "a\n")
self.console_wait("New password:")
- self.console_send("%s\n" % self.GUEST_PASS)
+ self.console_send("%s\n" % self._config["guest_pass"])
self.console_wait("New password:")
- self.console_send("%s\n" % self.GUEST_PASS)
+ self.console_send("%s\n" % self._config["guest_pass"])
self.console_wait("Retype new password:")
- self.console_send("%s\n" % self.GUEST_PASS)
+ self.console_send("%s\n" % self._config["guest_pass"])
self.console_wait_send("a: Configure network", "a\n")
self.console_wait_send("a: vioif0", "a\n")
# setup qemu user
prompt = "localhost$"
- self.console_ssh_init(prompt, self.GUEST_USER, self.GUEST_PASS)
+ self.console_ssh_init(prompt, self._config["guest_user"],
+ self._config["guest_pass"])
self.console_wait_send(prompt, "exit\n")
# setup root user
prompt = "localhost#"
- self.console_ssh_init(prompt, "root", self.ROOT_PASS)
+ self.console_ssh_init(prompt, "root", self._config["root_pass"])
self.console_sshd_config(prompt)
# setup virtio-blk #1 (tarfile)
self.console_wait_send("Which network interface", "done\n")
self.console_wait_send("DNS domain name", "localnet\n")
self.console_wait("Password for root account")
- self.console_send("%s\n" % self.ROOT_PASS)
+ self.console_send("%s\n" % self._config["root_pass"])
self.console_wait("Password for root account")
- self.console_send("%s\n" % self.ROOT_PASS)
+ self.console_send("%s\n" % self._config["root_pass"])
self.console_wait_send("Start sshd(8)", "yes\n")
self.console_wait_send("X Window System", "\n")
self.console_wait_send("xenodm", "\n")
self.console_wait_send("Which speed", "\n")
self.console_wait("Setup a user")
- self.console_send("%s\n" % self.GUEST_USER)
+ self.console_send("%s\n" % self._config["guest_user"])
self.console_wait("Full name")
- self.console_send("%s\n" % self.GUEST_USER)
+ self.console_send("%s\n" % self._config["guest_user"])
self.console_wait("Password")
- self.console_send("%s\n" % self.GUEST_PASS)
+ self.console_send("%s\n" % self._config["guest_pass"])
self.console_wait("Password")
- self.console_send("%s\n" % self.GUEST_PASS)
+ self.console_send("%s\n" % self._config["guest_pass"])
self.console_wait_send("Allow root ssh login", "yes\n")
self.console_wait_send("timezone", "UTC\n")
# setup qemu user
prompt = "$"
- self.console_ssh_init(prompt, self.GUEST_USER, self.GUEST_PASS)
+ self.console_ssh_init(prompt, self._config["guest_user"],
+ self._config["guest_pass"])
self.console_wait_send(prompt, "exit\n")
# setup root user
prompt = "openbsd#"
- self.console_ssh_init(prompt, "root", self.ROOT_PASS)
+ self.console_ssh_init(prompt, "root", self._config["root_pass"])
self.console_sshd_config(prompt)
# setup virtio-blk #1 (tarfile)
--- /dev/null
+#!/usr/bin/env python3
+#
+# Ubuntu aarch64 image
+#
+# Copyright 2020 Linaro
+#
+# Authors:
+# Robert Foley <robert.foley@linaro.org>
+# Originally based on ubuntu.i386 Fam Zheng <famz@redhat.com>
+#
+# This code is licensed under the GPL version 2 or later. See
+# the COPYING file in the top-level directory.
+#
+
+import sys
+import basevm
+import aarch64vm
+import ubuntuvm
+
+DEFAULT_CONFIG = {
+ 'cpu' : "cortex-a57",
+ 'machine' : "virt,gic-version=3",
+ 'install_cmds' : "apt-get update,"\
+ "apt-get build-dep -y --arch-only qemu,"\
+ "apt-get install -y libfdt-dev pkg-config language-pack-en",
+ # We increase beyond the default time since during boot
+ # it can take some time (many seconds) to log into the VM
+ # especially using softmmu.
+ 'ssh_timeout' : 60,
+}
+
+class UbuntuAarch64VM(ubuntuvm.UbuntuVM):
+ name = "ubuntu.aarch64"
+ arch = "aarch64"
+ image_name = "ubuntu-18.04-server-cloudimg-arm64.img"
+ image_link = "https://cloud-images.ubuntu.com/releases/18.04/release/" + image_name
+ image_sha256="0fdcba761965735a8a903d8b88df8e47f156f48715c00508e4315c506d7d3cb1"
+ BUILD_SCRIPT = """
+ set -e;
+ cd $(mktemp -d);
+ sudo chmod a+r /dev/vdb;
+ tar --checkpoint=.10 -xf /dev/vdb;
+ ./configure {configure_opts};
+ make --output-sync {target} -j{jobs} {verbose};
+ """
+ def boot(self, img, extra_args=None):
+ aarch64vm.create_flash_images(self._tmpdir, self._efi_aarch64)
+ default_args = aarch64vm.get_pflash_args(self._tmpdir)
+ if extra_args:
+ extra_args.extend(default_args)
+ else:
+ extra_args = default_args
+ # We always add these performance tweaks
+ # because without them, we boot so slowly that we
+ # can time out finding the boot efi device.
+ if '-smp' not in extra_args and \
+ '-smp' not in self._config['extra_args'] and \
+ '-smp' not in self._args:
+ # Only add if not already there to give caller option to change it.
+ extra_args.extend(["-smp", "8"])
+
+ # We have overridden boot() since aarch64 has additional parameters.
+ # Call down to the base class method.
+ super(UbuntuAarch64VM, self).boot(img, extra_args=extra_args)
+
+if __name__ == "__main__":
+ defaults = aarch64vm.get_config_defaults(UbuntuAarch64VM, DEFAULT_CONFIG)
+ sys.exit(basevm.main(UbuntuAarch64VM, defaults))
# the COPYING file in the top-level directory.
#
-import os
import sys
-import subprocess
import basevm
-import time
+import ubuntuvm
-class UbuntuX86VM(basevm.BaseVM):
+DEFAULT_CONFIG = {
+ 'install_cmds' : "apt-get update,"\
+ "apt-get build-dep -y qemu,"\
+ "apt-get install -y libfdt-dev language-pack-en",
+}
+
+class UbuntuX86VM(ubuntuvm.UbuntuVM):
name = "ubuntu.i386"
arch = "i386"
+ image_link="https://cloud-images.ubuntu.com/releases/bionic/"\
+ "release-20191114/ubuntu-18.04-server-cloudimg-i386.img"
+ image_sha256="28969840626d1ea80bb249c08eef1a4533e8904aa51a327b40f37ac4b4ff04ef"
BUILD_SCRIPT = """
set -e;
cd $(mktemp -d);
make --output-sync {target} -j{jobs} {verbose};
"""
- def build_image(self, img):
- cimg = self._download_with_cache(
- "https://cloud-images.ubuntu.com/releases/bionic/release-20191114/ubuntu-18.04-server-cloudimg-i386.img",
- sha256sum="28969840626d1ea80bb249c08eef1a4533e8904aa51a327b40f37ac4b4ff04ef")
- img_tmp = img + ".tmp"
- subprocess.check_call(["cp", "-f", cimg, img_tmp])
- self.exec_qemu_img("resize", img_tmp, "50G")
- self.boot(img_tmp, extra_args = [
- "-device", "VGA",
- "-cdrom", self.gen_cloud_init_iso()
- ])
- self.wait_ssh()
- self.ssh_root_check("touch /etc/cloud/cloud-init.disabled")
- self.ssh_root_check("apt-get update")
- self.ssh_root_check("apt-get install -y cloud-initramfs-growroot")
- # Don't check the status in case the guest hang up too quickly
- self.ssh_root("sync && reboot")
- time.sleep(5)
- self.wait_ssh()
- # The previous update sometimes doesn't survive a reboot, so do it again
- self.ssh_root_check("sed -ie s/^#\ deb-src/deb-src/g /etc/apt/sources.list")
- self.ssh_root_check("apt-get update")
- self.ssh_root_check("apt-get build-dep -y qemu")
- self.ssh_root_check("apt-get install -y libfdt-dev language-pack-en")
- self.ssh_root("poweroff")
- self.wait()
- os.rename(img_tmp, img)
- return 0
-
if __name__ == "__main__":
- sys.exit(basevm.main(UbuntuX86VM))
+ sys.exit(basevm.main(UbuntuX86VM, DEFAULT_CONFIG))
--- /dev/null
+#!/usr/bin/env python3
+#
+# Ubuntu VM testing library
+#
+# Copyright 2017 Red Hat Inc.
+# Copyright 2020 Linaro
+#
+# Authors:
+# Robert Foley <robert.foley@linaro.org>
+# Originally based on ubuntu.i386 Fam Zheng <famz@redhat.com>
+#
+# This code is licensed under the GPL version 2 or later. See
+# the COPYING file in the top-level directory.
+
+import os
+import subprocess
+import basevm
+
+class UbuntuVM(basevm.BaseVM):
+
+ def __init__(self, args, config=None):
+ self.login_prompt = "ubuntu-{}-guest login:".format(self.arch)
+ basevm.BaseVM.__init__(self, args, config)
+
+ def build_image(self, img):
+ """Build an Ubuntu VM image. The child class will
+ define the install_cmds to init the VM."""
+ os_img = self._download_with_cache(self.image_link,
+ sha256sum=self.image_sha256)
+ img_tmp = img + ".tmp"
+ subprocess.check_call(["cp", "-f", os_img, img_tmp])
+ self.exec_qemu_img("resize", img_tmp, "+50G")
+ ci_img = self.gen_cloud_init_iso()
+
+ self.boot(img_tmp, extra_args = [ "-device", "VGA", "-cdrom", ci_img, ])
+
+ # First command we issue is fix for slow ssh login.
+ self.wait_ssh(wait_root=True,
+ cmd="chmod -x /etc/update-motd.d/*")
+ # Wait for cloud init to finish
+ self.wait_ssh(wait_root=True,
+ cmd="ls /var/lib/cloud/instance/boot-finished")
+ self.ssh_root("touch /etc/cloud/cloud-init.disabled")
+ # Disable auto upgrades.
+ # We want to keep the VM system state stable.
+ self.ssh_root('sed -ie \'s/"1"/"0"/g\' '\
+ '/etc/apt/apt.conf.d/20auto-upgrades')
+ self.ssh_root("sed -ie s/^#\ deb-src/deb-src/g /etc/apt/sources.list")
+
+ # If the user chooses not to do the install phase,
+ # then we will jump right to the graceful shutdown
+ if self._config['install_cmds'] != "":
+ # Issue the install commands.
+ # This can be overriden by the user in the config .yml.
+ install_cmds = self._config['install_cmds'].split(',')
+ for cmd in install_cmds:
+ self.ssh_root(cmd)
+ self.graceful_shutdown()
+ os.rename(img_tmp, img)
+ return 0
#include "ui/input.h"
#include "sysemu/sysemu.h"
#include "sysemu/runstate.h"
+#include "sysemu/cpu-throttle.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-block.h"
#include "qapi/qapi-commands-misc.h"
text_console_resize(s);
if (chr->label) {
- char msg[128];
- int len;
+ char *msg;
s->t_attrib.bgcol = QEMU_COLOR_BLUE;
- len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label);
- vc_chr_write(chr, (uint8_t *)msg, len);
+ msg = g_strdup_printf("%s console\r\n", chr->label);
+ vc_chr_write(chr, (uint8_t *)msg, strlen(msg));
+ g_free(msg);
s->t_attrib = s->t_attrib_default;
}
* proper replacement (native opengl support) is only
* available in 3.16+. Silence the warning if possible.
*/
-#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#endif
gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE);
-#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
#pragma GCC diagnostic pop
-#endif
vc->gfx.dcl.ops = &dcl_egl_ops;
}
} else
#include "qemu/osdep.h"
#include "qemu/thread.h"
+#if defined(CONFIG_SIGNALFD)
#include <sys/syscall.h>
+#endif
struct sigfd_compat_info
{
#endif
sigjmp_buf env;
+#ifdef CONFIG_TSAN
void *tsan_co_fiber;
void *tsan_caller_fiber;
+#endif
#ifdef CONFIG_VALGRIND_H
unsigned int valgrind_stack_id;
int i[2];
};
-/* QEMU_ALWAYS_INLINE only does so if __OPTIMIZE__, so we cannot use it. */
+/*
+ * QEMU_ALWAYS_INLINE only does so if __OPTIMIZE__, so we cannot use it.
+ * always_inline is required to avoid TSan runtime fatal errors.
+ */
static inline __attribute__((always_inline))
void on_new_fiber(CoroutineUContext *co)
{
#endif
}
+/* always_inline is required to avoid TSan runtime fatal errors. */
static inline __attribute__((always_inline))
void finish_switch_fiber(void *fake_stack_save)
{
#endif
}
-static inline __attribute__((always_inline)) void start_switch_fiber(
- CoroutineAction action, void **fake_stack_save,
- const void *bottom, size_t size, void *new_fiber)
+/* always_inline is required to avoid TSan runtime fatal errors. */
+static inline __attribute__((always_inline))
+void start_switch_fiber_asan(CoroutineAction action, void **fake_stack_save,
+ const void *bottom, size_t size)
{
#ifdef CONFIG_ASAN
__sanitizer_start_switch_fiber(
action == COROUTINE_TERMINATE ? NULL : fake_stack_save,
bottom, size);
#endif
+}
+
+/* always_inline is required to avoid TSan runtime fatal errors. */
+static inline __attribute__((always_inline))
+void start_switch_fiber_tsan(void **fake_stack_save,
+ CoroutineUContext *co,
+ bool caller)
+{
#ifdef CONFIG_TSAN
- void *curr_fiber =
- __tsan_get_current_fiber();
+ void *new_fiber = caller ?
+ co->tsan_caller_fiber :
+ co->tsan_co_fiber;
+ void *curr_fiber = __tsan_get_current_fiber();
__tsan_acquire(curr_fiber);
*fake_stack_save = curr_fiber;
/* Initialize longjmp environment and switch back the caller */
if (!sigsetjmp(self->env, 0)) {
- start_switch_fiber(
- COROUTINE_YIELD,
- &fake_stack_save,
- leader.stack,
- leader.stack_size,
- self->tsan_caller_fiber);
+ start_switch_fiber_asan(COROUTINE_YIELD, &fake_stack_save, leader.stack,
+ leader.stack_size);
+ start_switch_fiber_tsan(&fake_stack_save, self, true); /* true=caller */
siglongjmp(*(sigjmp_buf *)co->entry_arg, 1);
}
/* swapcontext() in, siglongjmp() back out */
if (!sigsetjmp(old_env, 0)) {
- start_switch_fiber(
- COROUTINE_YIELD,
- &fake_stack_save,
- co->stack, co->stack_size, co->tsan_co_fiber);
+ start_switch_fiber_asan(COROUTINE_YIELD, &fake_stack_save, co->stack,
+ co->stack_size);
+ start_switch_fiber_tsan(&fake_stack_save,
+ co, false); /* false=not caller */
#ifdef CONFIG_SAFESTACK
/*
}
#ifdef CONFIG_VALGRIND_H
-#if defined(CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE) && !defined(__clang__)
/* Work around an unused variable in the valgrind.h macro... */
+#if !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#endif
{
VALGRIND_STACK_DEREGISTER(co->valgrind_stack_id);
}
-#if defined(CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE) && !defined(__clang__)
+#if !defined(__clang__)
#pragma GCC diagnostic pop
#endif
#endif
ret = sigsetjmp(from->env, 0);
if (ret == 0) {
- start_switch_fiber(action, &fake_stack_save,
- to->stack, to->stack_size, to->tsan_co_fiber);
+ start_switch_fiber_asan(action, &fake_stack_save, to->stack,
+ to->stack_size);
+ start_switch_fiber_tsan(&fake_stack_save,
+ to, false); /* false=not caller */
siglongjmp(to->env, action);
}
{
DIR *dir;
struct dirent *e;
- int r, fd;
+ struct stat st;
+ int r, fd, ret;
char *p;
if (rendernode) {
fd = -1;
while ((e = readdir(dir))) {
- if (e->d_type != DT_CHR) {
- continue;
- }
-
if (strncmp(e->d_name, "renderD", 7)) {
continue;
}
g_free(p);
continue;
}
+
+ /*
+ * prefer fstat() over checking e->d_type == DT_CHR for
+ * portability reasons
+ */
+ ret = fstat(r, &st);
+ if (ret < 0 || (st.st_mode & S_IFMT) != S_IFCHR) {
+ close(r);
+ g_free(p);
+ continue;
+ }
+
fd = r;
g_free(p);
break;
{ "usb-redir", "hw-", "usb-redirect" },
{ "qxl-vga", "hw-", "display-qxl" },
{ "qxl", "hw-", "display-qxl" },
- { "virtio-gpu-device", "hw-", "display-virtio-gpu" },
- { "virtio-gpu-pci", "hw-", "display-virtio-gpu" },
- { "virtio-vga", "hw-", "display-virtio-gpu" },
- { "vhost-user-gpu-device", "hw-", "display-virtio-gpu" },
- { "vhost-user-gpu-pci", "hw-", "display-virtio-gpu" },
- { "vhost-user-vga", "hw-", "display-virtio-gpu" },
{ "chardev-braille", "chardev-", "baum" },
};
#include "qemu/sockets.h"
#include "qemu/thread.h"
#include <libgen.h>
-#include <sys/signal.h>
#include "qemu/cutils.h"
#ifdef CONFIG_LINUX
#include <mach-o/dyld.h>
#endif
+#ifdef __HAIKU__
+#include <kernel/image.h>
+#endif
+
#include "qemu/mmap-alloc.h"
#ifdef CONFIG_DEBUG_STACK_USAGE
assert(f != -1);
}
-void qemu_set_nonblock(int fd)
+int qemu_try_set_nonblock(int fd)
{
int f;
f = fcntl(fd, F_GETFL);
- assert(f != -1);
- f = fcntl(fd, F_SETFL, f | O_NONBLOCK);
-#ifdef __OpenBSD__
if (f == -1) {
+ return -errno;
+ }
+ if (fcntl(fd, F_SETFL, f | O_NONBLOCK) == -1) {
+#ifdef __OpenBSD__
/*
* Previous to OpenBSD 6.3, fcntl(F_SETFL) is not permitted on
* memory devices and sets errno to ENODEV.
* It's OK if we fail to set O_NONBLOCK on devices like /dev/null,
* because they will never block anyway.
*/
- assert(errno == ENODEV);
- }
-#else
- assert(f != -1);
+ if (errno == ENODEV) {
+ return 0;
+ }
#endif
+ return -errno;
+ }
+ return 0;
+}
+
+void qemu_set_nonblock(int fd)
+{
+ int f;
+ f = qemu_try_set_nonblock(fd);
+ assert(f == 0);
}
int socket_set_fast_reuse(int fd)
}
}
}
+#elif defined(__HAIKU__)
+ {
+ image_info ii;
+ int32_t c = 0;
+
+ *buf = '\0';
+ while (get_next_image_info(0, &c, &ii) == B_OK) {
+ if (ii.type == B_APP_IMAGE) {
+ strncpy(buf, ii.name, sizeof(buf));
+ buf[sizeof(buf) - 1] = 0;
+ p = buf;
+ break;
+ }
+ }
+ }
#endif
/* If we don't have any way of figuring out the actual executable
location then try argv[0]. */
}
action->sa_sigaction(info->ssi_signo, &si, NULL);
}
+
+#ifndef HOST_NAME_MAX
+# ifdef _POSIX_HOST_NAME_MAX
+# define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
+# else
+# define HOST_NAME_MAX 255
+# endif
+#endif
+
+char *qemu_get_host_name(Error **errp)
+{
+ long len = -1;
+ g_autofree char *hostname = NULL;
+
+#ifdef _SC_HOST_NAME_MAX
+ len = sysconf(_SC_HOST_NAME_MAX);
+#endif /* _SC_HOST_NAME_MAX */
+
+ if (len < 0) {
+ len = HOST_NAME_MAX;
+ }
+
+ /* Unfortunately, gethostname() below does not guarantee a
+ * NULL terminated string. Therefore, allocate one byte more
+ * to be sure. */
+ hostname = g_new0(char, len + 1);
+
+ if (gethostname(hostname, len) < 0) {
+ error_setg_errno(errp, errno,
+ "cannot get hostname");
+ return NULL;
+ }
+
+ return g_steal_pointer(&hostname);
+}
}
#endif /* CONFIG_LOCALTIME_R */
-void qemu_set_block(int fd)
-{
- unsigned long opt = 0;
- WSAEventSelect(fd, NULL, 0);
- ioctlsocket(fd, FIONBIO, &opt);
-}
-
-void qemu_set_nonblock(int fd)
-{
- unsigned long opt = 1;
- ioctlsocket(fd, FIONBIO, &opt);
- qemu_fd_register(fd);
-}
-
-int socket_set_fast_reuse(int fd)
-{
- /* Enabling the reuse of an endpoint that was used by a socket still in
- * TIME_WAIT state is usually performed by setting SO_REUSEADDR. On Windows
- * fast reuse is the default and SO_REUSEADDR does strange things. So we
- * don't have to do anything here. More info can be found at:
- * http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621.aspx */
- return 0;
-}
-
-
static int socket_error(void)
{
switch (WSAGetLastError()) {
}
}
+void qemu_set_block(int fd)
+{
+ unsigned long opt = 0;
+ WSAEventSelect(fd, NULL, 0);
+ ioctlsocket(fd, FIONBIO, &opt);
+}
+
+int qemu_try_set_nonblock(int fd)
+{
+ unsigned long opt = 1;
+ if (ioctlsocket(fd, FIONBIO, &opt) != NO_ERROR) {
+ return -socket_error();
+ }
+ qemu_fd_register(fd);
+ return 0;
+}
+
+void qemu_set_nonblock(int fd)
+{
+ (void)qemu_try_set_nonblock(fd);
+}
+
+int socket_set_fast_reuse(int fd)
+{
+ /* Enabling the reuse of an endpoint that was used by a socket still in
+ * TIME_WAIT state is usually performed by setting SO_REUSEADDR. On Windows
+ * fast reuse is the default and SO_REUSEADDR does strange things. So we
+ * don't have to do anything here. More info can be found at:
+ * http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621.aspx */
+ return 0;
+}
+
int inet_aton(const char *cp, struct in_addr *ia)
{
uint32_t addr = inet_addr(cp);
}
return true;
}
+
+char *qemu_get_host_name(Error **errp)
+{
+ wchar_t tmp[MAX_COMPUTERNAME_LENGTH + 1];
+ DWORD size = G_N_ELEMENTS(tmp);
+
+ if (GetComputerNameW(tmp, &size) == 0) {
+ error_setg_win32(errp, GetLastError(), "failed close handle");
+ return NULL;
+ }
+
+ return g_utf16_to_utf8(tmp, size, NULL, NULL, NULL);
+}
/* Prepend timestamp to messages */
bool error_with_timestamp;
+bool error_with_guestname;
+const char *error_guest_name;
int error_printf(const char *fmt, ...)
{
g_free(timestr);
}
+ /* Only prepend guest name if -msg guest-name and -name guest=... are set */
+ if (error_with_guestname && error_guest_name && !cur_mon) {
+ error_printf("%s ", error_guest_name);
+ }
+
print_loc();
switch (type) {
#include "qemu/osdep.h"
#include "qemu-common.h"
-#if defined(__GLIBC__)
+#if defined HAVE_PTY_H
# include <pty.h>
#elif defined CONFIG_BSD
# include <termios.h>