diff options
150 files changed, 4687 insertions, 3312 deletions
diff --git a/.travis.yml b/.travis.yml index c1e99237b2..814be151f4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,8 @@ +# The current Travis default is a container based 14.04 Trust on EC2 +# Additional builds with specific requirements for a full VM need to +# be added as additional matrix: entries later on sudo: false +dist: trusty language: c python: - "2.6" @@ -70,11 +74,13 @@ script: - make ${MAKEFLAGS} && ${TEST_CMD} matrix: include: - # Test with CLang for compile portability - - env: CONFIG="" + # Test with Clang for compile portability (Travis uses clang-5.0) + - env: CONFIG="--disable-system" + compiler: clang + - env: CONFIG="--disable-user" compiler: clang # gprof/gcov are GCC features - - env: CONFIG="--enable-gprof --enable-gcov --disable-pie" + - env: CONFIG="--enable-gprof --enable-gcov --disable-pie --disable-linux-user" compiler: gcc # We manually include builds which we disable "make check" for - env: CONFIG="--enable-debug --enable-tcg-interpreter" @@ -95,80 +101,24 @@ matrix: - env: CONFIG="" os: osx compiler: clang - # Plain Trusty System Build - - env: CONFIG="--disable-linux-user" - sudo: required - addons: - dist: trusty - compiler: gcc - before_install: - - sudo apt-get update -qq - - sudo apt-get build-dep -qq qemu - - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ - - git submodule update --init --recursive - # Plain Trusty Linux User Build - - env: CONFIG="--disable-system" - sudo: required - addons: - dist: trusty - compiler: gcc - before_install: - - sudo apt-get update -qq - - sudo apt-get build-dep -qq qemu - - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ - - git submodule update --init --recursive - # Trusty System build with latest stable clang & python 3.0 - - sudo: required - addons: - dist: trusty - language: generic - compiler: none + # Python builds + - env: CONFIG="--target-list=x86_64-softmmu" python: - "3.0" - env: - - COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9 - - CONFIG="--disable-linux-user --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3" - before_install: - - wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - - - sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main' - - sudo apt-get update -qq - - sudo apt-get install -qq -y clang-3.9 - - sudo apt-get build-dep -qq qemu - - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ - - git submodule update --init --recursive - before_script: - - ./configure ${CONFIG} || cat config.log - # Trusty Linux User build with latest stable clang & python 3.6 - - sudo: required - addons: - dist: trusty - language: generic - compiler: none + - env: CONFIG="--target-list=x86_64-softmmu" python: - "3.6" - env: - - COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9 - - CONFIG="--disable-system --cc=clang-3.9 --cxx=clang++-3.9 --python=/usr/bin/python3" - before_install: - - wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - - - sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main' - - sudo apt-get update -qq - - sudo apt-get install -qq -y clang-3.9 - - sudo apt-get build-dep -qq qemu - - wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ - - git submodule update --init --recursive - before_script: - - ./configure ${CONFIG} || cat config.log # Using newer GCC with sanitizers - addons: apt: + update: true sources: # PPAs for newer toolchains - ubuntu-toolchain-r-test packages: # Extra toolchains - - gcc-5 - - g++-5 + - gcc-7 + - g++-7 # Build dependencies - libaio-dev - libattr1-dev @@ -197,8 +147,8 @@ matrix: language: generic compiler: none env: - - COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5 - - CONFIG="--cc=gcc-5 --cxx=g++-5 --disable-pie --disable-linux-user" + - COMPILER_NAME=gcc CXX=g++-7 CC=gcc-7 + - CONFIG="--cc=gcc-7 --cxx=g++-7 --disable-pie --disable-linux-user" - TEST_CMD="" before_script: - ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || cat config.log diff --git a/MAINTAINERS b/MAINTAINERS index bf482fd4e9..41cd3736a9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -447,6 +447,8 @@ F: hw/timer/cmsdk-apb-timer.c F: include/hw/timer/cmsdk-apb-timer.h F: hw/char/cmsdk-apb-uart.c F: include/hw/char/cmsdk-apb-uart.h +F: hw/misc/tz-ppc.c +F: include/hw/misc/tz-ppc.h ARM cores M: Peter Maydell <peter.maydell@linaro.org> @@ -515,8 +517,11 @@ M: Peter Maydell <peter.maydell@linaro.org> L: qemu-arm@nongnu.org S: Maintained F: hw/arm/mps2.c -F: hw/misc/mps2-scc.c -F: include/hw/misc/mps2-scc.h +F: hw/arm/mps2-tz.c +F: hw/misc/mps2-*.c +F: include/hw/misc/mps2-*.h +F: hw/arm/iotkit.c +F: include/hw/arm/iotkit.h Musicpal M: Jan Kiszka <jan.kiszka@web.de> diff --git a/Makefile.objs b/Makefile.objs index c6c3554203..2c8cb72407 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -196,66 +196,66 @@ vhost-user-blk-obj-y = contrib/vhost-user-blk/ ###################################################################### trace-events-subdirs = -trace-events-subdirs += util -trace-events-subdirs += crypto -trace-events-subdirs += io -trace-events-subdirs += migration +trace-events-subdirs += accel/kvm +trace-events-subdirs += accel/tcg +trace-events-subdirs += audio trace-events-subdirs += block trace-events-subdirs += chardev +trace-events-subdirs += crypto +trace-events-subdirs += hw/9pfs +trace-events-subdirs += hw/acpi +trace-events-subdirs += hw/alpha +trace-events-subdirs += hw/arm +trace-events-subdirs += hw/audio trace-events-subdirs += hw/block trace-events-subdirs += hw/block/dataplane trace-events-subdirs += hw/char -trace-events-subdirs += hw/intc -trace-events-subdirs += hw/net -trace-events-subdirs += hw/rdma -trace-events-subdirs += hw/rdma/vmw -trace-events-subdirs += hw/virtio -trace-events-subdirs += hw/audio -trace-events-subdirs += hw/misc -trace-events-subdirs += hw/misc/macio -trace-events-subdirs += hw/usb -trace-events-subdirs += hw/scsi -trace-events-subdirs += hw/nvram trace-events-subdirs += hw/display -trace-events-subdirs += hw/input -trace-events-subdirs += hw/timer trace-events-subdirs += hw/dma -trace-events-subdirs += hw/sparc -trace-events-subdirs += hw/sparc64 -trace-events-subdirs += hw/sd -trace-events-subdirs += hw/isa -trace-events-subdirs += hw/mem +trace-events-subdirs += hw/hppa trace-events-subdirs += hw/i386 trace-events-subdirs += hw/i386/xen -trace-events-subdirs += hw/9pfs -trace-events-subdirs += hw/ppc +trace-events-subdirs += hw/ide +trace-events-subdirs += hw/input +trace-events-subdirs += hw/intc +trace-events-subdirs += hw/isa +trace-events-subdirs += hw/mem +trace-events-subdirs += hw/misc +trace-events-subdirs += hw/misc/macio +trace-events-subdirs += hw/net +trace-events-subdirs += hw/nvram trace-events-subdirs += hw/pci trace-events-subdirs += hw/pci-host +trace-events-subdirs += hw/ppc +trace-events-subdirs += hw/rdma +trace-events-subdirs += hw/rdma/vmw trace-events-subdirs += hw/s390x +trace-events-subdirs += hw/scsi +trace-events-subdirs += hw/sd +trace-events-subdirs += hw/sparc +trace-events-subdirs += hw/sparc64 +trace-events-subdirs += hw/timer +trace-events-subdirs += hw/tpm +trace-events-subdirs += hw/usb trace-events-subdirs += hw/vfio -trace-events-subdirs += hw/acpi -trace-events-subdirs += hw/arm -trace-events-subdirs += hw/alpha -trace-events-subdirs += hw/hppa +trace-events-subdirs += hw/virtio trace-events-subdirs += hw/xen -trace-events-subdirs += hw/ide -trace-events-subdirs += hw/tpm -trace-events-subdirs += ui -trace-events-subdirs += audio +trace-events-subdirs += io +trace-events-subdirs += linux-user +trace-events-subdirs += migration +trace-events-subdirs += nbd trace-events-subdirs += net +trace-events-subdirs += qapi +trace-events-subdirs += qom +trace-events-subdirs += scsi trace-events-subdirs += target/arm trace-events-subdirs += target/i386 trace-events-subdirs += target/mips -trace-events-subdirs += target/sparc -trace-events-subdirs += target/s390x trace-events-subdirs += target/ppc -trace-events-subdirs += qom -trace-events-subdirs += linux-user -trace-events-subdirs += qapi -trace-events-subdirs += accel/tcg -trace-events-subdirs += accel/kvm -trace-events-subdirs += nbd -trace-events-subdirs += scsi +trace-events-subdirs += target/s390x +trace-events-subdirs += target/sparc +trace-events-subdirs += ui +trace-events-subdirs += util trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 732c919629..d48b56ca38 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -1669,14 +1669,14 @@ static TranslationBlock *tb_find_pc(uintptr_t tc_ptr) } #if !defined(CONFIG_USER_ONLY) -void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr) +void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs) { ram_addr_t ram_addr; MemoryRegion *mr; hwaddr l = 1; rcu_read_lock(); - mr = address_space_translate(as, addr, &addr, &l, false); + mr = address_space_translate(as, addr, &addr, &l, false, attrs); if (!(memory_region_is_ram(mr) || memory_region_is_romd(mr))) { rcu_read_unlock(); diff --git a/block/backup.c b/block/backup.c index 4e228e959b..5661435675 100644 --- a/block/backup.c +++ b/block/backup.c @@ -321,7 +321,7 @@ static void backup_complete(Job *job, void *opaque) { BackupCompleteData *data = opaque; - job_completed(job, data->ret); + job_completed(job, data->ret, NULL); g_free(data); } diff --git a/block/commit.c b/block/commit.c index 620666161b..e1814d9693 100644 --- a/block/commit.c +++ b/block/commit.c @@ -117,7 +117,7 @@ static void commit_complete(Job *job, void *opaque) * bdrv_set_backing_hd() to fail. */ block_job_remove_all_bdrv(bjob); - job_completed(job, ret); + job_completed(job, ret, NULL); g_free(data); /* If bdrv_drop_intermediate() didn't already do that, remove the commit diff --git a/block/create.c b/block/create.c index 8bd8a03719..915cd41bcc 100644 --- a/block/create.c +++ b/block/create.c @@ -24,28 +24,51 @@ #include "qemu/osdep.h" #include "block/block_int.h" +#include "qemu/job.h" #include "qapi/qapi-commands-block-core.h" +#include "qapi/qapi-visit-block-core.h" +#include "qapi/clone-visitor.h" #include "qapi/error.h" -typedef struct BlockdevCreateCo { +typedef struct BlockdevCreateJob { + Job common; BlockDriver *drv; BlockdevCreateOptions *opts; int ret; - Error **errp; -} BlockdevCreateCo; + Error *err; +} BlockdevCreateJob; -static void coroutine_fn bdrv_co_create_co_entry(void *opaque) +static void blockdev_create_complete(Job *job, void *opaque) { - BlockdevCreateCo *cco = opaque; - cco->ret = cco->drv->bdrv_co_create(cco->opts, cco->errp); + BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common); + + job_completed(job, s->ret, s->err); } -void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp) +static void coroutine_fn blockdev_create_run(void *opaque) { + BlockdevCreateJob *s = opaque; + + job_progress_set_remaining(&s->common, 1); + s->ret = s->drv->bdrv_co_create(s->opts, &s->err); + job_progress_update(&s->common, 1); + + qapi_free_BlockdevCreateOptions(s->opts); + job_defer_to_main_loop(&s->common, blockdev_create_complete, NULL); +} + +static const JobDriver blockdev_create_job_driver = { + .instance_size = sizeof(BlockdevCreateJob), + .job_type = JOB_TYPE_CREATE, + .start = blockdev_create_run, +}; + +void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options, + Error **errp) +{ + BlockdevCreateJob *s; const char *fmt = BlockdevDriver_str(options->driver); BlockDriver *drv = bdrv_find_format(fmt); - Coroutine *co; - BlockdevCreateCo cco; /* If the driver is in the schema, we know that it exists. But it may not * be whitelisted. */ @@ -55,22 +78,24 @@ void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp) return; } - /* Call callback if it exists */ + /* Error out if the driver doesn't support .bdrv_co_create */ if (!drv->bdrv_co_create) { error_setg(errp, "Driver does not support blockdev-create"); return; } - cco = (BlockdevCreateCo) { - .drv = drv, - .opts = options, - .ret = -EINPROGRESS, - .errp = errp, - }; - - co = qemu_coroutine_create(bdrv_co_create_co_entry, &cco); - qemu_coroutine_enter(co); - while (cco.ret == -EINPROGRESS) { - aio_poll(qemu_get_aio_context(), true); + /* Create the block job */ + /* TODO Running in the main context. Block drivers need to error out or add + * locking when they use a BDS in a different AioContext. */ + s = job_create(job_id, &blockdev_create_job_driver, NULL, + qemu_get_aio_context(), JOB_DEFAULT | JOB_MANUAL_DISMISS, + NULL, NULL, errp); + if (!s) { + return; } + + s->drv = drv, + s->opts = QAPI_CLONE(BlockdevCreateOptions, options), + + job_start(&s->common); } diff --git a/block/mirror.c b/block/mirror.c index dcb66ec3be..435268bbbf 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -581,7 +581,7 @@ static void mirror_exit(Job *job, void *opaque) blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort); blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort); - job_completed(job, data->ret); + job_completed(job, data->ret, NULL); g_free(data); bdrv_drained_end(src); diff --git a/block/qcow2.c b/block/qcow2.c index 6d532470a8..a007dc4246 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -768,6 +768,7 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, BDRVQcow2State *s = bs->opaque; uint64_t combined_cache_size; bool l2_cache_size_set, refcount_cache_size_set, combined_cache_size_set; + int min_refcount_cache = MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; combined_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_CACHE_SIZE); l2_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_L2_CACHE_SIZE); @@ -804,8 +805,6 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, } else { uint64_t virtual_disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; uint64_t max_l2_cache = virtual_disk_size / (s->cluster_size / 8); - uint64_t min_refcount_cache = - (uint64_t) MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; /* Assign as much memory as possible to the L2 cache, and * use the remainder for the refcount cache */ @@ -825,7 +824,7 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, * s->cluster_size); } if (!refcount_cache_size_set) { - *refcount_cache_size = MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; + *refcount_cache_size = min_refcount_cache; } } diff --git a/block/stream.c b/block/stream.c index a5d6e0cf8a..9264b68a1e 100644 --- a/block/stream.c +++ b/block/stream.c @@ -93,7 +93,7 @@ out: } g_free(s->backing_file_str); - job_completed(job, data->ret); + job_completed(job, data->ret, NULL); g_free(data); } diff --git a/block/vdi.c b/block/vdi.c index 96a22b8e83..668af0a828 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -865,6 +865,7 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, } } + ret = 0; exit: blk_unref(blk); bdrv_unref(bs_file); diff --git a/block/vhdx.c b/block/vhdx.c index 0b1e21c750..b1ba121bb6 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -1951,7 +1951,7 @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts, goto delete_and_exit; } - + ret = 0; delete_and_exit: blk_unref(blk); bdrv_unref(bs); @@ -2231,12 +2231,17 @@ EOF #undef XC_WANT_COMPAT_DEVICEMODEL_API #define __XEN_TOOLS__ #include <xendevicemodel.h> +#include <xenforeignmemory.h> int main(void) { xendevicemodel_handle *xd; + xenforeignmemory_handle *xfmem; xd = xendevicemodel_open(0, 0); xendevicemodel_pin_memory_cacheattr(xd, 0, 0, 0, 0); + xfmem = xenforeignmemory_open(0, 0); + xenforeignmemory_map_resource(xfmem, 0, 0, 0, 0, 0, NULL, 0, 0); + return 0; } EOF @@ -6831,6 +6836,7 @@ case "$target_name" in microblaze|microblazeel) TARGET_ARCH=microblaze bflt="yes" + echo "TARGET_ABI32=y" >> $config_target_mak ;; mips|mipsel) TARGET_ARCH=mips diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt index b9b6eabd08..1366228b2a 100644 --- a/docs/devel/qapi-code-gen.txt +++ b/docs/devel/qapi-code-gen.txt @@ -559,7 +559,7 @@ following example objects: Usage: { 'command': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT, '*returns': TYPE-NAME, '*boxed': true, '*gen': false, '*success-response': false, - '*allow-oob': true } + '*allow-oob': true, '*allow-preconfig': true } Commands are defined by using a dictionary containing several members, where three members are most common. The 'command' member is a @@ -683,6 +683,15 @@ OOB command handlers must satisfy the following conditions: If in doubt, do not implement OOB execution support. +A command may use the optional 'allow-preconfig' key to permit its execution +at early runtime configuration stage (preconfig runstate). +If not specified then a command defaults to 'allow-preconfig': false. + +An example of declaring a command that is enabled during preconfig: + { 'command': 'qmp_capabilities', + 'data': { '*enable': [ 'QMPCapability' ] }, + 'allow-preconfig': true } + === Events === Usage: { 'event': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT, diff --git a/docs/specs/tpm.txt b/docs/specs/tpm.txt index d1d71571e9..c230c4c93e 100644 --- a/docs/specs/tpm.txt +++ b/docs/specs/tpm.txt @@ -200,3 +200,109 @@ crw-------. 1 root root 10, 224 Jul 11 10:11 /dev/tpm0 PCR-00: 35 4E 3B CE 23 9F 38 59 ... ... PCR-23: 00 00 00 00 00 00 00 00 ... + + +=== Migration with the TPM emulator === + +The TPM emulator supports the following types of virtual machine migration: + +- VM save / restore (migration into a file) +- Network migration +- Snapshotting (migration into storage like QoW2 or QED) + +The following command sequences can be used to test VM save / restore. + + +In a 1st terminal start an instance of a swtpm using the following command: + +mkdir /tmp/mytpm1 +swtpm socket --tpmstate dir=/tmp/mytpm1 \ + --ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \ + --log level=20 --tpm2 + +In a 2nd terminal start the VM: + +qemu-system-x86_64 -display sdl -enable-kvm \ + -m 1024 -boot d -bios bios-256k.bin -boot menu=on \ + -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \ + -tpmdev emulator,id=tpm0,chardev=chrtpm \ + -device tpm-tis,tpmdev=tpm0 \ + -monitor stdio \ + test.img + +Verify that the attached TPM is working as expected using applications inside +the VM. + +To store the state of the VM use the following command in the QEMU monitor in +the 2nd terminal: + +(qemu) migrate "exec:cat > testvm.bin" +(qemu) quit + +At this point a file called 'testvm.bin' should exists and the swtpm and QEMU +processes should have ended. + +To test 'VM restore' you have to start the swtpm with the same parameters +as before. If previously a TPM 2 [--tpm2] was saved, --tpm2 must now be +passed again on the command line. + +In the 1st terminal restart the swtpm with the same command line as before: + +swtpm socket --tpmstate dir=/tmp/mytpm1 \ + --ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \ + --log level=20 --tpm2 + +In the 2nd terminal restore the state of the VM using the additonal +'-incoming' option. + +qemu-system-x86_64 -display sdl -enable-kvm \ + -m 1024 -boot d -bios bios-256k.bin -boot menu=on \ + -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \ + -tpmdev emulator,id=tpm0,chardev=chrtpm \ + -device tpm-tis,tpmdev=tpm0 \ + -incoming "exec:cat < testvm.bin" \ + test.img + + +Troubleshooting migration: + +There are several reasons why migration may fail. In case of problems, +please ensure that the command lines adhere to the following rules and, +if possible, that identical versions of QEMU and swtpm are used at all +times. + +VM save and restore: + - QEMU command line parameters should be identical apart from the + '-incoming' option on VM restore + - swtpm command line parameters should be identical + +VM migration to 'localhost': + - QEMU command line parameters should be identical apart from the + '-incoming' option on the destination side + - swtpm command line parameters should point to two different + directories on the source and destination swtpm (--tpmstate dir=...) + (especially if different versions of libtpms were to be used on the + same machine). + +VM migration across the network: + - QEMU command line parameters should be identical apart from the + '-incoming' option on the destination side + - swtpm command line parameters should be identical + +VM Snapshotting: + - QEMU command line parameters should be identical + - swtpm command line parameters should be identical + + +Besides that, migration failure reasons on the swtpm level may include +the following: + + - the versions of the swtpm on the source and destination sides are + incompatible + - downgrading of TPM state may not be supported + - the source and destination libtpms were compiled with different + compile-time options and the destination side refuses to accept the + state + - different migration keys are used on the source and destination side + and the destination side cannot decrypt the migrated state + (swtpm ... --migration-key ... ) @@ -478,6 +478,7 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x * @is_write: whether the translation operation is for write * @is_mmio: whether this can be MMIO, set true if it can * @target_as: the address space targeted by the IOMMU + * @attrs: transaction attributes * * This function is called from RCU critical section. It is the common * part of flatview_do_translate and address_space_translate_cached. @@ -488,7 +489,8 @@ static MemoryRegionSection address_space_translate_iommu(IOMMUMemoryRegion *iomm hwaddr *page_mask_out, bool is_write, bool is_mmio, - AddressSpace **target_as) + AddressSpace **target_as, + MemTxAttrs attrs) { MemoryRegionSection *section; hwaddr page_mask = (hwaddr)-1; @@ -541,6 +543,7 @@ unassigned: * @is_write: whether the translation operation is for write * @is_mmio: whether this can be MMIO, set true if it can * @target_as: the address space targeted by the IOMMU + * @attrs: memory transaction attributes * * This function is called from RCU critical section */ @@ -551,7 +554,8 @@ static MemoryRegionSection flatview_do_translate(FlatView *fv, hwaddr *page_mask_out, bool is_write, bool is_mmio, - AddressSpace **target_as) + AddressSpace **target_as, + MemTxAttrs attrs) { MemoryRegionSection *section; IOMMUMemoryRegion *iommu_mr; @@ -570,7 +574,7 @@ static MemoryRegionSection flatview_do_translate(FlatView *fv, return address_space_translate_iommu(iommu_mr, xlat, plen_out, page_mask_out, is_write, is_mmio, - target_as); + target_as, attrs); } if (page_mask_out) { /* Not behind an IOMMU, use default page size. */ @@ -582,7 +586,7 @@ static MemoryRegionSection flatview_do_translate(FlatView *fv, /* Called from RCU critical section */ IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr, - bool is_write) + bool is_write, MemTxAttrs attrs) { MemoryRegionSection section; hwaddr xlat, page_mask; @@ -592,7 +596,8 @@ IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr, * but page mask. */ section = flatview_do_translate(address_space_to_flatview(as), addr, &xlat, - NULL, &page_mask, is_write, false, &as); + NULL, &page_mask, is_write, false, &as, + attrs); /* Illegal translation */ if (section.mr == &io_mem_unassigned) { @@ -618,7 +623,8 @@ iotlb_fail: /* Called from RCU critical section */ MemoryRegion *flatview_translate(FlatView *fv, hwaddr addr, hwaddr *xlat, - hwaddr *plen, bool is_write) + hwaddr *plen, bool is_write, + MemTxAttrs attrs) { MemoryRegion *mr; MemoryRegionSection section; @@ -626,7 +632,7 @@ MemoryRegion *flatview_translate(FlatView *fv, hwaddr addr, hwaddr *xlat, /* This can be MMIO, so setup MMIO bit. */ section = flatview_do_translate(fv, addr, xlat, plen, NULL, - is_write, true, &as); + is_write, true, &as, attrs); mr = section.mr; if (xen_enabled() && memory_access_is_direct(mr, is_write)) { @@ -898,7 +904,7 @@ static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) if (phys != -1) { /* Locks grabbed by tb_invalidate_phys_addr */ tb_invalidate_phys_addr(cpu->cpu_ases[asidx].as, - phys | (pc & ~TARGET_PAGE_MASK)); + phys | (pc & ~TARGET_PAGE_MASK), attrs); } } #endif @@ -2540,7 +2546,8 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr, } static bool notdirty_mem_accepts(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return is_write; } @@ -2697,7 +2704,7 @@ static MemTxResult flatview_read(FlatView *fv, hwaddr addr, static MemTxResult flatview_write(FlatView *fv, hwaddr addr, MemTxAttrs attrs, const uint8_t *buf, int len); static bool flatview_access_valid(FlatView *fv, hwaddr addr, int len, - bool is_write); + bool is_write, MemTxAttrs attrs); static MemTxResult subpage_read(void *opaque, hwaddr addr, uint64_t *data, unsigned len, MemTxAttrs attrs) @@ -2763,7 +2770,8 @@ static MemTxResult subpage_write(void *opaque, hwaddr addr, } static bool subpage_accepts(void *opaque, hwaddr addr, - unsigned len, bool is_write) + unsigned len, bool is_write, + MemTxAttrs attrs) { subpage_t *subpage = opaque; #if defined(DEBUG_SUBPAGE) @@ -2772,7 +2780,7 @@ static bool subpage_accepts(void *opaque, hwaddr addr, #endif return flatview_access_valid(subpage->fv, addr + subpage->base, - len, is_write); + len, is_write, attrs); } static const MemoryRegionOps subpage_ops = { @@ -2846,7 +2854,8 @@ static void readonly_mem_write(void *opaque, hwaddr addr, } static bool readonly_mem_accepts(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return is_write; } @@ -3150,7 +3159,7 @@ static MemTxResult flatview_write_continue(FlatView *fv, hwaddr addr, } l = len; - mr = flatview_translate(fv, addr, &addr1, &l, true); + mr = flatview_translate(fv, addr, &addr1, &l, true, attrs); } return result; @@ -3166,7 +3175,7 @@ static MemTxResult flatview_write(FlatView *fv, hwaddr addr, MemTxAttrs attrs, MemTxResult result = MEMTX_OK; l = len; - mr = flatview_translate(fv, addr, &addr1, &l, true); + mr = flatview_translate(fv, addr, &addr1, &l, true, attrs); result = flatview_write_continue(fv, addr, attrs, buf, len, addr1, l, mr); @@ -3237,7 +3246,7 @@ MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, } l = len; - mr = flatview_translate(fv, addr, &addr1, &l, false); + mr = flatview_translate(fv, addr, &addr1, &l, false, attrs); } return result; @@ -3252,7 +3261,7 @@ static MemTxResult flatview_read(FlatView *fv, hwaddr addr, MemoryRegion *mr; l = len; - mr = flatview_translate(fv, addr, &addr1, &l, false); + mr = flatview_translate(fv, addr, &addr1, &l, false, attrs); return flatview_read_continue(fv, addr, attrs, buf, len, addr1, l, mr); } @@ -3323,7 +3332,8 @@ static inline void cpu_physical_memory_write_rom_internal(AddressSpace *as, rcu_read_lock(); while (len > 0) { l = len; - mr = address_space_translate(as, addr, &addr1, &l, true); + mr = address_space_translate(as, addr, &addr1, &l, true, + MEMTXATTRS_UNSPECIFIED); if (!(memory_region_is_ram(mr) || memory_region_is_romd(mr))) { @@ -3458,17 +3468,17 @@ static void cpu_notify_map_clients(void) } static bool flatview_access_valid(FlatView *fv, hwaddr addr, int len, - bool is_write) + bool is_write, MemTxAttrs attrs) { MemoryRegion *mr; hwaddr l, xlat; while (len > 0) { l = len; - mr = flatview_translate(fv, addr, &xlat, &l, is_write); + mr = flatview_translate(fv, addr, &xlat, &l, is_write, attrs); if (!memory_access_is_direct(mr, is_write)) { l = memory_access_size(mr, l, addr); - if (!memory_region_access_valid(mr, xlat, l, is_write)) { + if (!memory_region_access_valid(mr, xlat, l, is_write, attrs)) { return false; } } @@ -3480,23 +3490,24 @@ static bool flatview_access_valid(FlatView *fv, hwaddr addr, int len, } bool address_space_access_valid(AddressSpace *as, hwaddr addr, - int len, bool is_write) + int len, bool is_write, + MemTxAttrs attrs) { FlatView *fv; bool result; rcu_read_lock(); fv = address_space_to_flatview(as); - result = flatview_access_valid(fv, addr, len, is_write); + result = flatview_access_valid(fv, addr, len, is_write, attrs); rcu_read_unlock(); return result; } static hwaddr flatview_extend_translation(FlatView *fv, hwaddr addr, - hwaddr target_len, - MemoryRegion *mr, hwaddr base, hwaddr len, - bool is_write) + hwaddr target_len, + MemoryRegion *mr, hwaddr base, hwaddr len, + bool is_write, MemTxAttrs attrs) { hwaddr done = 0; hwaddr xlat; @@ -3512,7 +3523,7 @@ flatview_extend_translation(FlatView *fv, hwaddr addr, len = target_len; this_mr = flatview_translate(fv, addr, &xlat, - &len, is_write); + &len, is_write, attrs); if (this_mr != mr || xlat != base + done) { return done; } @@ -3529,7 +3540,8 @@ flatview_extend_translation(FlatView *fv, hwaddr addr, void *address_space_map(AddressSpace *as, hwaddr addr, hwaddr *plen, - bool is_write) + bool is_write, + MemTxAttrs attrs) { hwaddr len = *plen; hwaddr l, xlat; @@ -3544,7 +3556,7 @@ void *address_space_map(AddressSpace *as, l = len; rcu_read_lock(); fv = address_space_to_flatview(as); - mr = flatview_translate(fv, addr, &xlat, &l, is_write); + mr = flatview_translate(fv, addr, &xlat, &l, is_write, attrs); if (!memory_access_is_direct(mr, is_write)) { if (atomic_xchg(&bounce.in_use, true)) { @@ -3572,7 +3584,7 @@ void *address_space_map(AddressSpace *as, memory_region_ref(mr); *plen = flatview_extend_translation(fv, addr, len, mr, xlat, - l, is_write); + l, is_write, attrs); ptr = qemu_ram_ptr_length(mr->ram_block, xlat, plen, true); rcu_read_unlock(); @@ -3616,7 +3628,8 @@ void *cpu_physical_memory_map(hwaddr addr, hwaddr *plen, int is_write) { - return address_space_map(&address_space_memory, addr, plen, is_write); + return address_space_map(&address_space_memory, addr, plen, is_write, + MEMTXATTRS_UNSPECIFIED); } void cpu_physical_memory_unmap(void *buffer, hwaddr len, @@ -3656,8 +3669,13 @@ int64_t address_space_cache_init(MemoryRegionCache *cache, mr = cache->mrs.mr; memory_region_ref(mr); if (memory_access_is_direct(mr, is_write)) { + /* We don't care about the memory attributes here as we're only + * doing this if we found actual RAM, which behaves the same + * regardless of attributes; so UNSPECIFIED is fine. + */ l = flatview_extend_translation(cache->fv, addr, len, mr, - cache->xlat, l, is_write); + cache->xlat, l, is_write, + MEMTXATTRS_UNSPECIFIED); cache->ptr = qemu_ram_ptr_length(mr->ram_block, cache->xlat, &l, true); } else { cache->ptr = NULL; @@ -3700,7 +3718,7 @@ void address_space_cache_destroy(MemoryRegionCache *cache) */ static inline MemoryRegion *address_space_translate_cached( MemoryRegionCache *cache, hwaddr addr, hwaddr *xlat, - hwaddr *plen, bool is_write) + hwaddr *plen, bool is_write, MemTxAttrs attrs) { MemoryRegionSection section; MemoryRegion *mr; @@ -3719,7 +3737,7 @@ static inline MemoryRegion *address_space_translate_cached( section = address_space_translate_iommu(iommu_mr, xlat, plen, NULL, is_write, true, - &target_as); + &target_as, attrs); return section.mr; } @@ -3734,7 +3752,8 @@ address_space_read_cached_slow(MemoryRegionCache *cache, hwaddr addr, MemoryRegion *mr; l = len; - mr = address_space_translate_cached(cache, addr, &addr1, &l, false); + mr = address_space_translate_cached(cache, addr, &addr1, &l, false, + MEMTXATTRS_UNSPECIFIED); flatview_read_continue(cache->fv, addr, MEMTXATTRS_UNSPECIFIED, buf, len, addr1, l, mr); @@ -3751,7 +3770,8 @@ address_space_write_cached_slow(MemoryRegionCache *cache, hwaddr addr, MemoryRegion *mr; l = len; - mr = address_space_translate_cached(cache, addr, &addr1, &l, true); + mr = address_space_translate_cached(cache, addr, &addr1, &l, true, + MEMTXATTRS_UNSPECIFIED); flatview_write_continue(cache->fv, addr, MEMTXATTRS_UNSPECIFIED, buf, len, addr1, l, mr); @@ -3849,7 +3869,8 @@ bool cpu_physical_memory_is_io(hwaddr phys_addr) rcu_read_lock(); mr = address_space_translate(&address_space_memory, - phys_addr, &phys_addr, &l, false); + phys_addr, &phys_addr, &l, false, + MEMTXATTRS_UNSPECIFIED); res = !(memory_region_is_ram(mr) || memory_region_is_romd(mr)); rcu_read_unlock(); diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 9496f331a8..1e481662ad 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -926,6 +926,15 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) static const ARMInsnFixup *primary_loader; AddressSpace *as = arm_boot_address_space(cpu, info); + /* CPU objects (unlike devices) are not automatically reset on system + * reset, so we must always register a handler to do so. If we're + * actually loading a kernel, the handler is also responsible for + * arranging that we start it correctly. + */ + for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) { + qemu_register_reset(do_cpu_reset, ARM_CPU(cs)); + } + /* The board code is not supposed to set secure_board_setup unless * running its code in secure mode is actually possible, and KVM * doesn't support secure. @@ -1143,15 +1152,6 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) ARM_CPU(cs)->env.boot_info = info; } - /* CPU objects (unlike devices) are not automatically reset on system - * reset, so we must always register a handler to do so. If we're - * actually loading a kernel, the handler is also responsible for - * arranging that we start it correctly. - */ - for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) { - qemu_register_reset(do_cpu_reset, ARM_CPU(cs)); - } - if (!info->skip_dtb_autoload && have_dtb(info)) { if (arm_load_dtb(info->dtb_start, info, info->dtb_limit, as) < 0) { exit(1); diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 92ceee9c0f..74f5744e87 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -400,7 +400,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) AcpiIortItsGroup *its; AcpiIortTable *iort; AcpiIortSmmu3 *smmu; - size_t node_size, iort_length, smmu_offset = 0; + size_t node_size, iort_node_offset, iort_length, smmu_offset = 0; AcpiIortRC *rc; iort = acpi_data_push(table_data, sizeof(*iort)); @@ -413,7 +413,12 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) iort_length = sizeof(*iort); iort->node_count = cpu_to_le32(nb_nodes); - iort->node_offset = cpu_to_le32(sizeof(*iort)); + /* + * Use a copy in case table_data->data moves during acpi_data_push + * operations. + */ + iort_node_offset = sizeof(*iort); + iort->node_offset = cpu_to_le32(iort_node_offset); /* ITS group node */ node_size = sizeof(*its) + sizeof(uint32_t); @@ -429,7 +434,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) int irq = vms->irqmap[VIRT_SMMU]; /* SMMUv3 node */ - smmu_offset = iort->node_offset + node_size; + smmu_offset = iort_node_offset + node_size; node_size = sizeof(*smmu) + sizeof(*idmap); iort_length += node_size; smmu = acpi_data_push(table_data, node_size); @@ -450,7 +455,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) idmap->id_count = cpu_to_le32(0xFFFF); idmap->output_base = 0; /* output IORT node is the ITS group node (the first node) */ - idmap->output_reference = cpu_to_le32(iort->node_offset); + idmap->output_reference = cpu_to_le32(iort_node_offset); } /* Root Complex Node */ @@ -479,9 +484,14 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) idmap->output_reference = cpu_to_le32(smmu_offset); } else { /* output IORT node is the ITS group node (the first node) */ - idmap->output_reference = cpu_to_le32(iort->node_offset); + idmap->output_reference = cpu_to_le32(iort_node_offset); } + /* + * Update the pointer address in case table_data->data moves during above + * acpi_data_push operations. + */ + iort = (AcpiIortTable *)(table_data->data + iort_start); iort->length = cpu_to_le32(iort_length); build_header(linker, table_data, (void *)(table_data->data + iort_start), diff --git a/hw/core/machine.c b/hw/core/machine.c index 2040177664..617e5f8d75 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -737,7 +737,7 @@ static char *cpu_slot_to_string(const CPUArchId *cpu) return g_string_free(s, false); } -static void machine_numa_finish_init(MachineState *machine) +static void machine_numa_finish_cpu_init(MachineState *machine) { int i; bool default_mapping; @@ -792,7 +792,8 @@ void machine_run_board_init(MachineState *machine) MachineClass *machine_class = MACHINE_GET_CLASS(machine); if (nb_numa_nodes) { - machine_numa_finish_init(machine); + numa_complete_configuration(machine); + machine_numa_finish_cpu_init(machine); } /* If the machine supports the valid_cpu_types check and the user diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c index 14d86c254b..8eea757aff 100644 --- a/hw/dma/xlnx-zdma.c +++ b/hw/dma/xlnx-zdma.c @@ -302,7 +302,7 @@ static bool zdma_load_descriptor(XlnxZDMA *s, uint64_t addr, void *buf) qemu_log_mask(LOG_GUEST_ERROR, "zdma: unaligned descriptor at %" PRIx64, addr); - memset(buf, 0xdeadbeef, sizeof(XlnxZDMADescr)); + memset(buf, 0x0, sizeof(XlnxZDMADescr)); s->error = true; return false; } @@ -707,9 +707,11 @@ static uint64_t zdma_read(void *opaque, hwaddr addr, unsigned size) RegisterInfo *r = &s->regs_info[addr / 4]; if (!r->data) { + gchar *path = object_get_canonical_path(OBJECT(s)); qemu_log("%s: Decode error: read from %" HWADDR_PRIx "\n", - object_get_canonical_path(OBJECT(s)), + path, addr); + g_free(path); ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, INV_APB, true); zdma_ch_imr_update_irq(s); return 0; @@ -724,9 +726,11 @@ static void zdma_write(void *opaque, hwaddr addr, uint64_t value, RegisterInfo *r = &s->regs_info[addr / 4]; if (!r->data) { + gchar *path = object_get_canonical_path(OBJECT(s)); qemu_log("%s: Decode error: write to %" HWADDR_PRIx "=%" PRIx64 "\n", - object_get_canonical_path(OBJECT(s)), + path, addr, value); + g_free(path); ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, INV_APB, true); zdma_ch_imr_update_irq(s); return; diff --git a/hw/hppa/dino.c b/hw/hppa/dino.c index c5dcf3104d..26f2704cd5 100644 --- a/hw/hppa/dino.c +++ b/hw/hppa/dino.c @@ -137,7 +137,8 @@ static void gsc_to_pci_forwarding(DinoState *s) } static bool dino_chip_mem_valid(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { switch (addr) { case DINO_IAR0: diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index e36c7bbb40..b4c5b03274 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -425,19 +425,19 @@ static void pc_i440fx_machine_options(MachineClass *m) m->default_display = "std"; } -static void pc_i440fx_2_13_machine_options(MachineClass *m) +static void pc_i440fx_3_0_machine_options(MachineClass *m) { pc_i440fx_machine_options(m); m->alias = "pc"; m->is_default = 1; } -DEFINE_I440FX_MACHINE(v2_13, "pc-i440fx-2.13", NULL, - pc_i440fx_2_13_machine_options); +DEFINE_I440FX_MACHINE(v3_0, "pc-i440fx-3.0", NULL, + pc_i440fx_3_0_machine_options); static void pc_i440fx_2_12_machine_options(MachineClass *m) { - pc_i440fx_2_13_machine_options(m); + pc_i440fx_3_0_machine_options(m); m->is_default = 0; m->alias = NULL; SET_MACHINE_COMPAT(m, PC_COMPAT_2_12); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 2372457c6a..83d6d75efa 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -308,18 +308,18 @@ static void pc_q35_machine_options(MachineClass *m) m->max_cpus = 288; } -static void pc_q35_2_13_machine_options(MachineClass *m) +static void pc_q35_3_0_machine_options(MachineClass *m) { pc_q35_machine_options(m); m->alias = "q35"; } -DEFINE_Q35_MACHINE(v2_13, "pc-q35-2.13", NULL, - pc_q35_2_13_machine_options); +DEFINE_Q35_MACHINE(v3_0, "pc-q35-3.0", NULL, + pc_q35_3_0_machine_options); static void pc_q35_2_12_machine_options(MachineClass *m) { - pc_q35_2_13_machine_options(m); + pc_q35_3_0_machine_options(m); m->alias = NULL; SET_MACHINE_COMPAT(m, PC_COMPAT_2_12); } diff --git a/hw/i386/xen/trace-events b/hw/i386/xen/trace-events index 8dab7bcfe0..8a9077cd4e 100644 --- a/hw/i386/xen/trace-events +++ b/hw/i386/xen/trace-events @@ -15,6 +15,9 @@ cpu_ioreq_pio(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64 cpu_ioreq_pio_read_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio read reg data=0x%"PRIx64" port=0x%"PRIx64" size=%d" cpu_ioreq_pio_write_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio write reg data=0x%"PRIx64" port=0x%"PRIx64" size=%d" cpu_ioreq_move(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p copy dir=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" +xen_map_resource_ioreq(uint32_t id, void *addr) "id: %u addr: %p" +cpu_ioreq_config_read(void *req, uint32_t sbdf, uint32_t reg, uint32_t size, uint32_t data) "I/O=%p sbdf=0x%x reg=%u size=%u data=0x%x" +cpu_ioreq_config_write(void *req, uint32_t sbdf, uint32_t reg, uint32_t size, uint32_t data) "I/O=%p sbdf=0x%x reg=%u size=%u data=0x%x" # xen-mapcache.c xen_map_cache(uint64_t phys_addr) "want 0x%"PRIx64 diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index 6ffa3c22cc..935a3676c8 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -12,6 +12,7 @@ #include "cpu.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" #include "hw/i386/pc.h" #include "hw/i386/apic-msidef.h" #include "hw/xen/xen_common.h" @@ -86,6 +87,14 @@ typedef struct XenPhysmap { QLIST_ENTRY(XenPhysmap) list; } XenPhysmap; +static QLIST_HEAD(, XenPhysmap) xen_physmap; + +typedef struct XenPciDevice { + PCIDevice *pci_dev; + uint32_t sbdf; + QLIST_ENTRY(XenPciDevice) entry; +} XenPciDevice; + typedef struct XenIOState { ioservid_t ioservid; shared_iopage_t *shared_page; @@ -106,8 +115,8 @@ typedef struct XenIOState { struct xs_handle *xenstore; MemoryListener memory_listener; MemoryListener io_listener; + QLIST_HEAD(, XenPciDevice) dev_list; DeviceListener device_listener; - QLIST_HEAD(, XenPhysmap) physmap; hwaddr free_phys_offset; const XenPhysmap *log_for_dirtybit; @@ -274,14 +283,13 @@ void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, MemoryRegion *mr, g_free(pfn_list); } -static XenPhysmap *get_physmapping(XenIOState *state, - hwaddr start_addr, ram_addr_t size) +static XenPhysmap *get_physmapping(hwaddr start_addr, ram_addr_t size) { XenPhysmap *physmap = NULL; start_addr &= TARGET_PAGE_MASK; - QLIST_FOREACH(physmap, &state->physmap, list) { + QLIST_FOREACH(physmap, &xen_physmap, list) { if (range_covers_byte(physmap->start_addr, physmap->size, start_addr)) { return physmap; } @@ -289,23 +297,21 @@ static XenPhysmap *get_physmapping(XenIOState *state, return NULL; } -#ifdef XEN_COMPAT_PHYSMAP -static hwaddr xen_phys_offset_to_gaddr(hwaddr start_addr, - ram_addr_t size, void *opaque) +static hwaddr xen_phys_offset_to_gaddr(hwaddr phys_offset, ram_addr_t size) { - hwaddr addr = start_addr & TARGET_PAGE_MASK; - XenIOState *xen_io_state = opaque; + hwaddr addr = phys_offset & TARGET_PAGE_MASK; XenPhysmap *physmap = NULL; - QLIST_FOREACH(physmap, &xen_io_state->physmap, list) { + QLIST_FOREACH(physmap, &xen_physmap, list) { if (range_covers_byte(physmap->phys_offset, physmap->size, addr)) { - return physmap->start_addr; + return physmap->start_addr + (phys_offset - physmap->phys_offset); } } - return start_addr; + return phys_offset; } +#ifdef XEN_COMPAT_PHYSMAP static int xen_save_physmap(XenIOState *state, XenPhysmap *physmap) { char path[80], value[17]; @@ -355,7 +361,7 @@ static int xen_add_to_physmap(XenIOState *state, hwaddr phys_offset = memory_region_get_ram_addr(mr); const char *mr_name; - if (get_physmapping(state, start_addr, size)) { + if (get_physmapping(start_addr, size)) { return 0; } if (size <= 0) { @@ -384,7 +390,7 @@ go_physmap: physmap->name = mr_name; physmap->phys_offset = phys_offset; - QLIST_INSERT_HEAD(&state->physmap, physmap, list); + QLIST_INSERT_HEAD(&xen_physmap, physmap, list); if (runstate_check(RUN_STATE_INMIGRATE)) { /* Now when we have a physmap entry we can replace a dummy mapping with @@ -428,7 +434,7 @@ static int xen_remove_from_physmap(XenIOState *state, XenPhysmap *physmap = NULL; hwaddr phys_offset = 0; - physmap = get_physmapping(state, start_addr, size); + physmap = get_physmapping(start_addr, size); if (physmap == NULL) { return -1; } @@ -570,6 +576,12 @@ static void xen_device_realize(DeviceListener *listener, if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { PCIDevice *pci_dev = PCI_DEVICE(dev); + XenPciDevice *xendev = g_new(XenPciDevice, 1); + + xendev->pci_dev = pci_dev; + xendev->sbdf = PCI_BUILD_BDF(pci_dev_bus_num(pci_dev), + pci_dev->devfn); + QLIST_INSERT_HEAD(&state->dev_list, xendev, entry); xen_map_pcidev(xen_domid, state->ioservid, pci_dev); } @@ -582,8 +594,17 @@ static void xen_device_unrealize(DeviceListener *listener, if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { PCIDevice *pci_dev = PCI_DEVICE(dev); + XenPciDevice *xendev, *next; xen_unmap_pcidev(xen_domid, state->ioservid, pci_dev); + + QLIST_FOREACH_SAFE(xendev, &state->dev_list, entry, next) { + if (xendev->pci_dev == pci_dev) { + QLIST_REMOVE(xendev, entry); + g_free(xendev); + break; + } + } } } @@ -597,7 +618,7 @@ static void xen_sync_dirty_bitmap(XenIOState *state, int rc, i, j; const XenPhysmap *physmap = NULL; - physmap = get_physmapping(state, start_addr, size); + physmap = get_physmapping(start_addr, size); if (physmap == NULL) { /* not handled */ return; @@ -904,6 +925,62 @@ static void cpu_ioreq_move(ioreq_t *req) } } +static void cpu_ioreq_config(XenIOState *state, ioreq_t *req) +{ + uint32_t sbdf = req->addr >> 32; + uint32_t reg = req->addr; + XenPciDevice *xendev; + + if (req->size != sizeof(uint8_t) && req->size != sizeof(uint16_t) && + req->size != sizeof(uint32_t)) { + hw_error("PCI config access: bad size (%u)", req->size); + } + + if (req->count != 1) { + hw_error("PCI config access: bad count (%u)", req->count); + } + + QLIST_FOREACH(xendev, &state->dev_list, entry) { + if (xendev->sbdf != sbdf) { + continue; + } + + if (!req->data_is_ptr) { + if (req->dir == IOREQ_READ) { + req->data = pci_host_config_read_common( + xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, + req->size); + trace_cpu_ioreq_config_read(req, xendev->sbdf, reg, + req->size, req->data); + } else if (req->dir == IOREQ_WRITE) { + trace_cpu_ioreq_config_write(req, xendev->sbdf, reg, + req->size, req->data); + pci_host_config_write_common( + xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, + req->data, req->size); + } + } else { + uint32_t tmp; + + if (req->dir == IOREQ_READ) { + tmp = pci_host_config_read_common( + xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, + req->size); + trace_cpu_ioreq_config_read(req, xendev->sbdf, reg, + req->size, tmp); + write_phys_req_item(req->data, req, 0, &tmp); + } else if (req->dir == IOREQ_WRITE) { + read_phys_req_item(req->data, req, 0, &tmp); + trace_cpu_ioreq_config_write(req, xendev->sbdf, reg, + req->size, tmp); + pci_host_config_write_common( + xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, + tmp, req->size); + } + } + } +} + static void regs_to_cpu(vmware_regs_t *vmport_regs, ioreq_t *req) { X86CPU *cpu; @@ -976,27 +1053,9 @@ static void handle_ioreq(XenIOState *state, ioreq_t *req) case IOREQ_TYPE_INVALIDATE: xen_invalidate_map_cache(); break; - case IOREQ_TYPE_PCI_CONFIG: { - uint32_t sbdf = req->addr >> 32; - uint32_t val; - - /* Fake a write to port 0xCF8 so that - * the config space access will target the - * correct device model. - */ - val = (1u << 31) | - ((req->addr & 0x0f00) << 16) | - ((sbdf & 0xffff) << 8) | - (req->addr & 0xfc); - do_outp(0xcf8, 4, val); - - /* Now issue the config space access via - * port 0xCFC - */ - req->addr = 0xcfc | (req->addr & 0x03); - cpu_ioreq_pio(req); + case IOREQ_TYPE_PCI_CONFIG: + cpu_ioreq_config(state, req); break; - } default: hw_error("Invalid ioreq type 0x%x\n", req->type); } @@ -1222,7 +1281,7 @@ static void xen_read_physmap(XenIOState *state) xen_domid, entries[i]); physmap->name = xs_read(state->xenstore, 0, path, &len); - QLIST_INSERT_HEAD(&state->physmap, physmap, list); + QLIST_INSERT_HEAD(&xen_physmap, physmap, list); } free(entries); } @@ -1239,13 +1298,39 @@ static void xen_wakeup_notifier(Notifier *notifier, void *data) static int xen_map_ioreq_server(XenIOState *state) { + void *addr = NULL; + xenforeignmemory_resource_handle *fres; xen_pfn_t ioreq_pfn; xen_pfn_t bufioreq_pfn; evtchn_port_t bufioreq_evtchn; int rc; + /* + * Attempt to map using the resource API and fall back to normal + * foreign mapping if this is not supported. + */ + QEMU_BUILD_BUG_ON(XENMEM_resource_ioreq_server_frame_bufioreq != 0); + QEMU_BUILD_BUG_ON(XENMEM_resource_ioreq_server_frame_ioreq(0) != 1); + fres = xenforeignmemory_map_resource(xen_fmem, xen_domid, + XENMEM_resource_ioreq_server, + state->ioservid, 0, 2, + &addr, + PROT_READ | PROT_WRITE, 0); + if (fres != NULL) { + trace_xen_map_resource_ioreq(state->ioservid, addr); + state->buffered_io_page = addr; + state->shared_page = addr + TARGET_PAGE_SIZE; + } else if (errno != EOPNOTSUPP) { + error_report("failed to map ioreq server resources: error %d handle=%p", + errno, xen_xc); + return -1; + } + rc = xen_get_ioreq_server_info(xen_domid, state->ioservid, - &ioreq_pfn, &bufioreq_pfn, + (state->shared_page == NULL) ? + &ioreq_pfn : NULL, + (state->buffered_io_page == NULL) ? + &bufioreq_pfn : NULL, &bufioreq_evtchn); if (rc < 0) { error_report("failed to get ioreq server info: error %d handle=%p", @@ -1253,27 +1338,37 @@ static int xen_map_ioreq_server(XenIOState *state) return rc; } - DPRINTF("shared page at pfn %lx\n", ioreq_pfn); - DPRINTF("buffered io page at pfn %lx\n", bufioreq_pfn); - DPRINTF("buffered io evtchn is %x\n", bufioreq_evtchn); - - state->shared_page = xenforeignmemory_map(xen_fmem, xen_domid, - PROT_READ | PROT_WRITE, - 1, &ioreq_pfn, NULL); if (state->shared_page == NULL) { - error_report("map shared IO page returned error %d handle=%p", - errno, xen_xc); - return -1; + DPRINTF("shared page at pfn %lx\n", ioreq_pfn); + + state->shared_page = xenforeignmemory_map(xen_fmem, xen_domid, + PROT_READ | PROT_WRITE, + 1, &ioreq_pfn, NULL); + if (state->shared_page == NULL) { + error_report("map shared IO page returned error %d handle=%p", + errno, xen_xc); + } } - state->buffered_io_page = xenforeignmemory_map(xen_fmem, xen_domid, - PROT_READ | PROT_WRITE, - 1, &bufioreq_pfn, NULL); if (state->buffered_io_page == NULL) { - error_report("map buffered IO page returned error %d", errno); + DPRINTF("buffered io page at pfn %lx\n", bufioreq_pfn); + + state->buffered_io_page = xenforeignmemory_map(xen_fmem, xen_domid, + PROT_READ | PROT_WRITE, + 1, &bufioreq_pfn, + NULL); + if (state->buffered_io_page == NULL) { + error_report("map buffered IO page returned error %d", errno); + return -1; + } + } + + if (state->shared_page == NULL || state->buffered_io_page == NULL) { return -1; } + DPRINTF("buffered io evtchn is %x\n", bufioreq_evtchn); + state->bufioreq_remote_port = bufioreq_evtchn; return 0; @@ -1374,7 +1469,6 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) qemu_add_vm_change_state_handler(xen_hvm_change_state_handler, state); state->memory_listener = xen_memory_listener; - QLIST_INIT(&state->physmap); memory_listener_register(&state->memory_listener, &address_space_memory); state->log_for_dirtybit = NULL; @@ -1382,6 +1476,7 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) memory_listener_register(&state->io_listener, &address_space_io); state->device_listener = xen_device_listener; + QLIST_INIT(&state->dev_list); device_listener_register(&state->device_listener); /* Initialize backend core & drivers */ @@ -1390,6 +1485,8 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) goto err; } xen_be_register_common(); + + QLIST_INIT(&xen_physmap); xen_read_physmap(state); /* Disable ACPI build because Xen handles it */ @@ -1461,6 +1558,8 @@ void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length) int rc; ram_addr_t start_pfn, nb_pages; + start = xen_phys_offset_to_gaddr(start, length); + if (length == 0) { length = TARGET_PAGE_SIZE; } diff --git a/hw/i386/xen/xen-mapcache.c b/hw/i386/xen/xen-mapcache.c index 541b7693b3..628b813a11 100644 --- a/hw/i386/xen/xen-mapcache.c +++ b/hw/i386/xen/xen-mapcache.c @@ -318,7 +318,7 @@ tryagain: mapcache->last_entry = NULL; #ifdef XEN_COMPAT_PHYSMAP if (!translated && mapcache->phys_offset_to_gaddr) { - phys_addr = mapcache->phys_offset_to_gaddr(phys_addr, size, mapcache->opaque); + phys_addr = mapcache->phys_offset_to_gaddr(phys_addr, size); translated = true; goto tryagain; } diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index 6f467e68a8..204369d0e2 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -572,7 +572,6 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) if (kvm_has_gsi_routing()) { /* set up irq routing */ - kvm_init_irq_routing(kvm_state); for (i = 0; i < s->num_irq - GIC_INTERNAL; ++i) { kvm_irqchip_add_irq_route(kvm_state, i, 0, i); } diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index cb9a3a542d..5c89be1af0 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -427,7 +427,7 @@ static uint64_t icv_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) { GICv3CPUState *cs = icc_cs_from_env(env); int regno = ri->opc2 & 3; - int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1NS; + int grp = (ri->crm & 1) ? GICV3_G1NS : GICV3_G0; uint64_t value = cs->ich_apr[grp][regno]; trace_gicv3_icv_ap_read(ri->crm & 1, regno, gicv3_redist_affid(cs), value); @@ -439,7 +439,7 @@ static void icv_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, { GICv3CPUState *cs = icc_cs_from_env(env); int regno = ri->opc2 & 3; - int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1NS; + int grp = (ri->crm & 1) ? GICV3_G1NS : GICV3_G0; trace_gicv3_icv_ap_write(ri->crm & 1, regno, gicv3_redist_affid(cs), value); @@ -1461,7 +1461,7 @@ static uint64_t icc_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) uint64_t value; int regno = ri->opc2 & 3; - int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1; + int grp = (ri->crm & 1) ? GICV3_G1 : GICV3_G0; if (icv_access(env, grp == GICV3_G0 ? HCR_FMO : HCR_IMO)) { return icv_ap_read(env, ri); @@ -1483,7 +1483,7 @@ static void icc_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, GICv3CPUState *cs = icc_cs_from_env(env); int regno = ri->opc2 & 3; - int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1; + int grp = (ri->crm & 1) ? GICV3_G1 : GICV3_G0; if (icv_access(env, grp == GICV3_G0 ? HCR_FMO : HCR_IMO)) { icv_ap_write(env, ri, value); @@ -2292,7 +2292,7 @@ static uint64_t ich_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) { GICv3CPUState *cs = icc_cs_from_env(env); int regno = ri->opc2 & 3; - int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1NS; + int grp = (ri->crm & 1) ? GICV3_G1NS : GICV3_G0; uint64_t value; value = cs->ich_apr[grp][regno]; @@ -2305,7 +2305,7 @@ static void ich_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, { GICv3CPUState *cs = icc_cs_from_env(env); int regno = ri->opc2 & 3; - int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1NS; + int grp = (ri->crm & 1) ? GICV3_G1NS : GICV3_G0; trace_gicv3_ich_ap_write(ri->crm & 1, regno, gicv3_redist_affid(cs), value); diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index ec371772b3..0279b86cd9 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -243,6 +243,7 @@ static void kvm_dist_putbmp(GICv3State *s, uint32_t offset, if (clroffset != 0) { reg = 0; kvm_gicd_access(s, clroffset, ®, true); + clroffset += 4; } reg = *gic_bmp_ptr32(bmp, irq); kvm_gicd_access(s, offset, ®, true); @@ -760,7 +761,6 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) if (kvm_has_gsi_routing()) { /* set up irq routing */ - kvm_init_irq_routing(kvm_state); for (i = 0; i < s->num_irq - GIC_INTERNAL; ++i) { kvm_irqchip_add_irq_route(kvm_state, i, 0, i); } diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 2a0739d0e9..b23e7f64a8 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -420,14 +420,16 @@ static void fw_cfg_dma_mem_write(void *opaque, hwaddr addr, } static bool fw_cfg_dma_mem_valid(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return !is_write || ((size == 4 && (addr == 0 || addr == 4)) || (size == 8 && addr == 0)); } static bool fw_cfg_data_mem_valid(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return addr == 0; } @@ -439,7 +441,8 @@ static void fw_cfg_ctl_mem_write(void *opaque, hwaddr addr, } static bool fw_cfg_ctl_mem_valid(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return is_write && size == 2; } @@ -458,7 +461,8 @@ static void fw_cfg_comb_write(void *opaque, hwaddr addr, } static bool fw_cfg_comb_valid(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return (size == 1) || (is_write && size == 2); } diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 2e910428f3..2375cbee12 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4070,18 +4070,18 @@ static const TypeInfo spapr_machine_info = { type_init(spapr_machine_register_##suffix) /* - * pseries-2.13 + * pseries-3.0 */ -static void spapr_machine_2_13_instance_options(MachineState *machine) +static void spapr_machine_3_0_instance_options(MachineState *machine) { } -static void spapr_machine_2_13_class_options(MachineClass *mc) +static void spapr_machine_3_0_class_options(MachineClass *mc) { /* Defaults for the latest behaviour inherited from the base class */ } -DEFINE_SPAPR_MACHINE(2_13, "2.13", true); +DEFINE_SPAPR_MACHINE(3_0, "3.0", true); /* * pseries-2.12 @@ -4090,18 +4090,18 @@ DEFINE_SPAPR_MACHINE(2_13, "2.13", true); HW_COMPAT_2_12 \ { \ .driver = TYPE_POWERPC_CPU, \ - .property = "pre-2.13-migration", \ + .property = "pre-3.0-migration", \ .value = "on", \ }, static void spapr_machine_2_12_instance_options(MachineState *machine) { - spapr_machine_2_13_instance_options(machine); + spapr_machine_3_0_instance_options(machine); } static void spapr_machine_2_12_class_options(MachineClass *mc) { - spapr_machine_2_13_class_options(mc); + spapr_machine_3_0_class_options(mc); SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_12); } diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 02a815fd31..d1a5f79678 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -762,7 +762,8 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, mr = s390_get_subregion(mr, offset, len); offset -= mr->addr; - if (!memory_region_access_valid(mr, offset, len, true)) { + if (!memory_region_access_valid(mr, offset, len, true, + MEMTXATTRS_UNSPECIFIED)) { s390_program_interrupt(env, PGM_OPERAND, 6, ra); return 0; } diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index e548d341a0..7ae5fb38dd 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -812,23 +812,23 @@ bool css_migration_enabled(void) .value = "0",\ }, -static void ccw_machine_2_13_instance_options(MachineState *machine) +static void ccw_machine_3_0_instance_options(MachineState *machine) { } -static void ccw_machine_2_13_class_options(MachineClass *mc) +static void ccw_machine_3_0_class_options(MachineClass *mc) { } -DEFINE_CCW_MACHINE(2_13, "2.13", true); +DEFINE_CCW_MACHINE(3_0, "3.0", true); static void ccw_machine_2_12_instance_options(MachineState *machine) { - ccw_machine_2_13_instance_options(machine); + ccw_machine_3_0_instance_options(machine); } static void ccw_machine_2_12_class_options(MachineClass *mc) { - ccw_machine_2_13_class_options(mc); + ccw_machine_3_0_class_options(mc); SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_12); } DEFINE_CCW_MACHINE(2_12, "2.12", false); diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 64ec285826..9ed9727744 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -564,7 +564,8 @@ void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val) } static bool esp_mem_accepts(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return (size == 1) || (is_write && size == 4); } diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c index 6418ef0831..10bc20dbec 100644 --- a/hw/tpm/tpm_emulator.c +++ b/hw/tpm/tpm_emulator.c @@ -4,7 +4,7 @@ * Copyright (c) 2017 Intel Corporation * Author: Amarnath Valluri <amarnath.valluri@intel.com> * - * Copyright (c) 2010 - 2013 IBM Corporation + * Copyright (c) 2010 - 2013, 2018 IBM Corporation * Authors: * Stefan Berger <stefanb@us.ibm.com> * @@ -49,6 +49,19 @@ #define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap)) /* data structures */ + +/* blobs from the TPM; part of VM state when migrating */ +typedef struct TPMBlobBuffers { + uint32_t permanent_flags; + TPMSizedBuffer permanent; + + uint32_t volatil_flags; + TPMSizedBuffer volatil; + + uint32_t savestate_flags; + TPMSizedBuffer savestate; +} TPMBlobBuffers; + typedef struct TPMEmulator { TPMBackend parent; @@ -64,6 +77,8 @@ typedef struct TPMEmulator { unsigned int established_flag:1; unsigned int established_flag_cached:1; + + TPMBlobBuffers state_blobs; } TPMEmulator; @@ -293,7 +308,8 @@ static int tpm_emulator_set_buffer_size(TPMBackend *tb, return 0; } -static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize) +static int tpm_emulator_startup_tpm_resume(TPMBackend *tb, size_t buffersize, + bool is_resume) { TPMEmulator *tpm_emu = TPM_EMULATOR(tb); ptm_init init = { @@ -301,12 +317,17 @@ static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize) }; ptm_res res; + trace_tpm_emulator_startup_tpm_resume(is_resume, buffersize); + if (buffersize != 0 && tpm_emulator_set_buffer_size(tb, buffersize, NULL) < 0) { goto err_exit; } - trace_tpm_emulator_startup_tpm(); + if (is_resume) { + init.u.req.init_flags |= cpu_to_be32(PTM_INIT_FLAG_DELETE_VOLATILE); + } + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init), sizeof(init)) < 0) { error_report("tpm-emulator: could not send INIT: %s", @@ -325,6 +346,11 @@ err_exit: return -1; } +static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize) +{ + return tpm_emulator_startup_tpm_resume(tb, buffersize, false); +} + static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb) { TPMEmulator *tpm_emu = TPM_EMULATOR(tb); @@ -423,16 +449,21 @@ static size_t tpm_emulator_get_buffer_size(TPMBackend *tb) static int tpm_emulator_block_migration(TPMEmulator *tpm_emu) { Error *err = NULL; + ptm_cap caps = PTM_CAP_GET_STATEBLOB | PTM_CAP_SET_STATEBLOB | + PTM_CAP_STOP; - error_setg(&tpm_emu->migration_blocker, - "Migration disabled: TPM emulator not yet migratable"); - migrate_add_blocker(tpm_emu->migration_blocker, &err); - if (err) { - error_report_err(err); - error_free(tpm_emu->migration_blocker); - tpm_emu->migration_blocker = NULL; + if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) { + error_setg(&tpm_emu->migration_blocker, + "Migration disabled: TPM emulator does not support " + "migration"); + migrate_add_blocker(tpm_emu->migration_blocker, &err); + if (err) { + error_report_err(err); + error_free(tpm_emu->migration_blocker); + tpm_emu->migration_blocker = NULL; - return -1; + return -1; + } } return 0; @@ -570,6 +601,267 @@ static const QemuOptDesc tpm_emulator_cmdline_opts[] = { { /* end of list */ }, }; +/* + * Transfer a TPM state blob from the TPM into a provided buffer. + * + * @tpm_emu: TPMEmulator + * @type: the type of blob to transfer + * @tsb: the TPMSizeBuffer to fill with the blob + * @flags: the flags to return to the caller + */ +static int tpm_emulator_get_state_blob(TPMEmulator *tpm_emu, + uint8_t type, + TPMSizedBuffer *tsb, + uint32_t *flags) +{ + ptm_getstate pgs; + ptm_res res; + ssize_t n; + uint32_t totlength, length; + + tpm_sized_buffer_reset(tsb); + + pgs.u.req.state_flags = cpu_to_be32(PTM_STATE_FLAG_DECRYPTED); + pgs.u.req.type = cpu_to_be32(type); + pgs.u.req.offset = 0; + + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_STATEBLOB, + &pgs, sizeof(pgs.u.req), + offsetof(ptm_getstate, u.resp.data)) < 0) { + error_report("tpm-emulator: could not get state blob type %d : %s", + type, strerror(errno)); + return -1; + } + + res = be32_to_cpu(pgs.u.resp.tpm_result); + if (res != 0 && (res & 0x800) == 0) { + error_report("tpm-emulator: Getting the stateblob (type %d) failed " + "with a TPM error 0x%x", type, res); + return -1; + } + + totlength = be32_to_cpu(pgs.u.resp.totlength); + length = be32_to_cpu(pgs.u.resp.length); + if (totlength != length) { + error_report("tpm-emulator: Expecting to read %u bytes " + "but would get %u", totlength, length); + return -1; + } + + *flags = be32_to_cpu(pgs.u.resp.state_flags); + + if (totlength > 0) { + tsb->buffer = g_try_malloc(totlength); + if (!tsb->buffer) { + error_report("tpm-emulator: Out of memory allocating %u bytes", + totlength); + return -1; + } + + n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr, tsb->buffer, totlength); + if (n != totlength) { + error_report("tpm-emulator: Could not read stateblob (type %d); " + "expected %u bytes, got %zd", + type, totlength, n); + return -1; + } + } + tsb->size = totlength; + + trace_tpm_emulator_get_state_blob(type, tsb->size, *flags); + + return 0; +} + +static int tpm_emulator_get_state_blobs(TPMEmulator *tpm_emu) +{ + TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; + + if (tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT, + &state_blobs->permanent, + &state_blobs->permanent_flags) < 0 || + tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE, + &state_blobs->volatil, + &state_blobs->volatil_flags) < 0 || + tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE, + &state_blobs->savestate, + &state_blobs->savestate_flags) < 0) { + goto err_exit; + } + + return 0; + + err_exit: + tpm_sized_buffer_reset(&state_blobs->volatil); + tpm_sized_buffer_reset(&state_blobs->permanent); + tpm_sized_buffer_reset(&state_blobs->savestate); + + return -1; +} + +/* + * Transfer a TPM state blob to the TPM emulator. + * + * @tpm_emu: TPMEmulator + * @type: the type of TPM state blob to transfer + * @tsb: TPMSizedBuffer containing the TPM state blob + * @flags: Flags describing the (encryption) state of the TPM state blob + */ +static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu, + uint32_t type, + TPMSizedBuffer *tsb, + uint32_t flags) +{ + ssize_t n; + ptm_setstate pss; + ptm_res tpm_result; + + if (tsb->size == 0) { + return 0; + } + + pss = (ptm_setstate) { + .u.req.state_flags = cpu_to_be32(flags), + .u.req.type = cpu_to_be32(type), + .u.req.length = cpu_to_be32(tsb->size), + }; + + /* write the header only */ + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_STATEBLOB, &pss, + offsetof(ptm_setstate, u.req.data), 0) < 0) { + error_report("tpm-emulator: could not set state blob type %d : %s", + type, strerror(errno)); + return -1; + } + + /* now the body */ + n = qemu_chr_fe_write_all(&tpm_emu->ctrl_chr, tsb->buffer, tsb->size); + if (n != tsb->size) { + error_report("tpm-emulator: Writing the stateblob (type %d) " + "failed; could not write %u bytes, but only %zd", + type, tsb->size, n); + return -1; + } + + /* now get the result */ + n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr, + (uint8_t *)&pss, sizeof(pss.u.resp)); + if (n != sizeof(pss.u.resp)) { + error_report("tpm-emulator: Reading response from writing stateblob " + "(type %d) failed; expected %zu bytes, got %zd", type, + sizeof(pss.u.resp), n); + return -1; + } + + tpm_result = be32_to_cpu(pss.u.resp.tpm_result); + if (tpm_result != 0) { + error_report("tpm-emulator: Setting the stateblob (type %d) failed " + "with a TPM error 0x%x", type, tpm_result); + return -1; + } + + trace_tpm_emulator_set_state_blob(type, tsb->size, flags); + + return 0; +} + +/* + * Set all the TPM state blobs. + * + * Returns a negative errno code in case of error. + */ +static int tpm_emulator_set_state_blobs(TPMBackend *tb) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; + + trace_tpm_emulator_set_state_blobs(); + + if (tpm_emulator_stop_tpm(tb) < 0) { + trace_tpm_emulator_set_state_blobs_error("Could not stop TPM"); + return -EIO; + } + + if (tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT, + &state_blobs->permanent, + state_blobs->permanent_flags) < 0 || + tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE, + &state_blobs->volatil, + state_blobs->volatil_flags) < 0 || + tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE, + &state_blobs->savestate, + state_blobs->savestate_flags) < 0) { + return -EIO; + } + + trace_tpm_emulator_set_state_blobs_done(); + + return 0; +} + +static int tpm_emulator_pre_save(void *opaque) +{ + TPMBackend *tb = opaque; + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + + trace_tpm_emulator_pre_save(); + + tpm_backend_finish_sync(tb); + + /* get the state blobs from the TPM */ + return tpm_emulator_get_state_blobs(tpm_emu); +} + +/* + * Load the TPM state blobs into the TPM. + * + * Returns negative errno codes in case of error. + */ +static int tpm_emulator_post_load(void *opaque, int version_id) +{ + TPMBackend *tb = opaque; + int ret; + + ret = tpm_emulator_set_state_blobs(tb); + if (ret < 0) { + return ret; + } + + if (tpm_emulator_startup_tpm_resume(tb, 0, true) < 0) { + return -EIO; + } + + return 0; +} + +static const VMStateDescription vmstate_tpm_emulator = { + .name = "tpm-emulator", + .version_id = 0, + .pre_save = tpm_emulator_pre_save, + .post_load = tpm_emulator_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(state_blobs.permanent_flags, TPMEmulator), + VMSTATE_UINT32(state_blobs.permanent.size, TPMEmulator), + VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.permanent.buffer, + TPMEmulator, 0, 0, + state_blobs.permanent.size), + + VMSTATE_UINT32(state_blobs.volatil_flags, TPMEmulator), + VMSTATE_UINT32(state_blobs.volatil.size, TPMEmulator), + VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.volatil.buffer, + TPMEmulator, 0, 0, + state_blobs.volatil.size), + + VMSTATE_UINT32(state_blobs.savestate_flags, TPMEmulator), + VMSTATE_UINT32(state_blobs.savestate.size, TPMEmulator), + VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.savestate.buffer, + TPMEmulator, 0, 0, + state_blobs.savestate.size), + + VMSTATE_END_OF_LIST() + } +}; + static void tpm_emulator_inst_init(Object *obj) { TPMEmulator *tpm_emu = TPM_EMULATOR(obj); @@ -579,6 +871,8 @@ static void tpm_emulator_inst_init(Object *obj) tpm_emu->options = g_new0(TPMEmulatorOptions, 1); tpm_emu->cur_locty_number = ~0; qemu_mutex_init(&tpm_emu->mutex); + + vmstate_register(NULL, -1, &vmstate_tpm_emulator, obj); } /* @@ -600,6 +894,7 @@ static void tpm_emulator_shutdown(TPMEmulator *tpm_emu) static void tpm_emulator_inst_finalize(Object *obj) { TPMEmulator *tpm_emu = TPM_EMULATOR(obj); + TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; tpm_emulator_shutdown(tpm_emu); @@ -614,7 +909,13 @@ static void tpm_emulator_inst_finalize(Object *obj) error_free(tpm_emu->migration_blocker); } + tpm_sized_buffer_reset(&state_blobs->volatil); + tpm_sized_buffer_reset(&state_blobs->permanent); + tpm_sized_buffer_reset(&state_blobs->savestate); + qemu_mutex_destroy(&tpm_emu->mutex); + + vmstate_unregister(NULL, &vmstate_tpm_emulator, obj); } static void tpm_emulator_class_init(ObjectClass *klass, void *data) diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c index 2ac7e74307..12f5c9a759 100644 --- a/hw/tpm/tpm_tis.c +++ b/hw/tpm/tpm_tis.c @@ -894,9 +894,57 @@ static void tpm_tis_reset(DeviceState *dev) tpm_backend_startup_tpm(s->be_driver, s->be_buffer_size); } +/* persistent state handling */ + +static int tpm_tis_pre_save(void *opaque) +{ + TPMState *s = opaque; + uint8_t locty = s->active_locty; + + trace_tpm_tis_pre_save(locty, s->rw_offset); + + if (DEBUG_TIS) { + tpm_tis_dump_state(opaque, 0); + } + + /* + * Synchronize with backend completion. + */ + tpm_backend_finish_sync(s->be_driver); + + return 0; +} + +static const VMStateDescription vmstate_locty = { + .name = "tpm-tis/locty", + .version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(state, TPMLocality), + VMSTATE_UINT32(inte, TPMLocality), + VMSTATE_UINT32(ints, TPMLocality), + VMSTATE_UINT8(access, TPMLocality), + VMSTATE_UINT32(sts, TPMLocality), + VMSTATE_UINT32(iface_id, TPMLocality), + VMSTATE_END_OF_LIST(), + } +}; + static const VMStateDescription vmstate_tpm_tis = { - .name = "tpm", - .unmigratable = 1, + .name = "tpm-tis", + .version_id = 0, + .pre_save = tpm_tis_pre_save, + .fields = (VMStateField[]) { + VMSTATE_BUFFER(buffer, TPMState), + VMSTATE_UINT16(rw_offset, TPMState), + VMSTATE_UINT8(active_locty, TPMState), + VMSTATE_UINT8(aborting_locty, TPMState), + VMSTATE_UINT8(next_locty, TPMState), + + VMSTATE_STRUCT_ARRAY(loc, TPMState, TPM_TIS_NUM_LOCALITIES, 0, + vmstate_locty, TPMLocality), + + VMSTATE_END_OF_LIST() + } }; static Property tpm_tis_properties[] = { diff --git a/hw/tpm/trace-events b/hw/tpm/trace-events index 9a65384088..25bee0cecf 100644 --- a/hw/tpm/trace-events +++ b/hw/tpm/trace-events @@ -20,13 +20,19 @@ tpm_emulator_set_locality(uint8_t locty) "setting locality to %d" tpm_emulator_handle_request(void) "processing TPM command" tpm_emulator_probe_caps(uint64_t caps) "capabilities: 0x%"PRIx64 tpm_emulator_set_buffer_size(uint32_t buffersize, uint32_t minsize, uint32_t maxsize) "buffer size: %u, min: %u, max: %u" -tpm_emulator_startup_tpm(void) "startup" +tpm_emulator_startup_tpm_resume(bool is_resume, size_t buffersize) "is_resume: %d, buffer size: %zu" tpm_emulator_get_tpm_established_flag(uint8_t flag) "got established flag: %d" tpm_emulator_cancel_cmd_not_supt(void) "Backend does not support CANCEL_TPM_CMD" tpm_emulator_handle_device_opts_tpm12(void) "TPM Version 1.2" tpm_emulator_handle_device_opts_tpm2(void) "TPM Version 2" tpm_emulator_handle_device_opts_unspec(void) "TPM Version Unspecified" tpm_emulator_handle_device_opts_startup_error(void) "Startup error" +tpm_emulator_get_state_blob(uint8_t type, uint32_t size, uint32_t flags) "got state blob type %d, %u bytes, flags 0x%08x" +tpm_emulator_set_state_blob(uint8_t type, uint32_t size, uint32_t flags) "set state blob type %d, %u bytes, flags 0x%08x" +tpm_emulator_set_state_blobs(void) "setting state blobs" +tpm_emulator_set_state_blobs_error(const char *msg) "error while setting state blobs: %s" +tpm_emulator_set_state_blobs_done(void) "Done setting state blobs" +tpm_emulator_pre_save(void) "" tpm_emulator_inst_init(void) "" # hw/tpm/tpm_tis.c @@ -44,3 +50,4 @@ tpm_tis_mmio_write_locty_seized(uint8_t locty, uint8_t active) "Locality %d seiz tpm_tis_mmio_write_init_abort(void) "Initiating abort" tpm_tis_mmio_write_lowering_irq(void) "Lowering IRQ" tpm_tis_mmio_write_data2send(uint32_t value, unsigned size) "Data to send to TPM: 0x%08x (size=%d)" +tpm_tis_pre_save(uint8_t locty, uint32_t rw_offset) "locty: %d, rw_offset = %u" diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 07ffa0ba10..8e57265edf 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -324,7 +324,8 @@ static bool vfio_get_vaddr(IOMMUTLBEntry *iotlb, void **vaddr, */ mr = address_space_translate(&address_space_memory, iotlb->translated_addr, - &xlat, &len, writable); + &xlat, &len, writable, + MEMTXATTRS_UNSPECIFIED); if (!memory_region_is_ram(mr)) { error_report("iommu map to non memory area %"HWADDR_PRIx"", xlat); diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index b08290036d..4565b69f83 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -897,7 +897,8 @@ int vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write) trace_vhost_iotlb_miss(dev, 1); iotlb = address_space_get_iotlb_entry(dev->vdev->dma_as, - iova, write); + iova, write, + MEMTXATTRS_UNSPECIFIED); if (iotlb.target_as != NULL) { ret = vhost_memory_region_lookup(dev, iotlb.translated_addr, &uaddr, &len); diff --git a/hw/xen/xen_pt_msi.c b/hw/xen/xen_pt_msi.c index 6d1e3bdeb4..cc514f9157 100644 --- a/hw/xen/xen_pt_msi.c +++ b/hw/xen/xen_pt_msi.c @@ -498,7 +498,8 @@ static uint64_t pci_msix_read(void *opaque, hwaddr addr, } static bool pci_msix_accepts(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return !(addr & (size - 1)); } diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index bd68328ed9..4d09eaba72 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -255,7 +255,7 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, void tlb_set_page(CPUState *cpu, target_ulong vaddr, hwaddr paddr, int prot, int mmu_idx, target_ulong size); -void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr); +void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs); void probe_write(CPUArchState *env, target_ulong addr, int size, int mmu_idx, uintptr_t retaddr); #else @@ -303,7 +303,8 @@ static inline void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, uint16_t idxmap) { } -static inline void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr) +static inline void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, + MemTxAttrs attrs) { } #endif diff --git a/include/exec/helper-head.h b/include/exec/helper-head.h index 15b6a68de3..276dd5afce 100644 --- a/include/exec/helper-head.h +++ b/include/exec/helper-head.h @@ -39,7 +39,7 @@ #define dh_ctype_int int #define dh_ctype_i64 uint64_t #define dh_ctype_s64 int64_t -#define dh_ctype_f16 float16 +#define dh_ctype_f16 uint32_t #define dh_ctype_f32 float32 #define dh_ctype_f64 float64 #define dh_ctype_ptr void * diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index 58399b9318..56c25c0ef7 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -37,7 +37,8 @@ void flatview_unref(FlatView *view); extern const MemoryRegionOps unassigned_mem_ops; bool memory_region_access_valid(MemoryRegion *mr, hwaddr addr, - unsigned size, bool is_write); + unsigned size, bool is_write, + MemTxAttrs attrs); void flatview_add_to_dispatch(FlatView *fv, MemoryRegionSection *section); AddressSpaceDispatch *address_space_dispatch_new(FlatView *fv); diff --git a/include/exec/memory.h b/include/exec/memory.h index a116d936fc..eb2ba06519 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -166,7 +166,8 @@ struct MemoryRegionOps { * as a machine check exception). */ bool (*accepts)(void *opaque, hwaddr addr, - unsigned size, bool is_write); + unsigned size, bool is_write, + MemTxAttrs attrs); } valid; /* Internal implementation constraints: */ struct { @@ -194,29 +195,100 @@ enum IOMMUMemoryRegionAttr { IOMMU_ATTR_SPAPR_TCE_FD }; +/** + * IOMMUMemoryRegionClass: + * + * All IOMMU implementations need to subclass TYPE_IOMMU_MEMORY_REGION + * and provide an implementation of at least the @translate method here + * to handle requests to the memory region. Other methods are optional. + * + * The IOMMU implementation must use the IOMMU notifier infrastructure + * to report whenever mappings are changed, by calling + * memory_region_notify_iommu() (or, if necessary, by calling + * memory_region_notify_one() for each registered notifier). + */ typedef struct IOMMUMemoryRegionClass { /* private */ struct DeviceClass parent_class; /* - * Return a TLB entry that contains a given address. Flag should - * be the access permission of this translation operation. We can - * set flag to IOMMU_NONE to mean that we don't need any - * read/write permission checks, like, when for region replay. + * Return a TLB entry that contains a given address. + * + * The IOMMUAccessFlags indicated via @flag are optional and may + * be specified as IOMMU_NONE to indicate that the caller needs + * the full translation information for both reads and writes. If + * the access flags are specified then the IOMMU implementation + * may use this as an optimization, to stop doing a page table + * walk as soon as it knows that the requested permissions are not + * allowed. If IOMMU_NONE is passed then the IOMMU must do the + * full page table walk and report the permissions in the returned + * IOMMUTLBEntry. (Note that this implies that an IOMMU may not + * return different mappings for reads and writes.) + * + * The returned information remains valid while the caller is + * holding the big QEMU lock or is inside an RCU critical section; + * if the caller wishes to cache the mapping beyond that it must + * register an IOMMU notifier so it can invalidate its cached + * information when the IOMMU mapping changes. + * + * @iommu: the IOMMUMemoryRegion + * @hwaddr: address to be translated within the memory region + * @flag: requested access permissions */ IOMMUTLBEntry (*translate)(IOMMUMemoryRegion *iommu, hwaddr addr, IOMMUAccessFlags flag); - /* Returns minimum supported page size */ + /* Returns minimum supported page size in bytes. + * If this method is not provided then the minimum is assumed to + * be TARGET_PAGE_SIZE. + * + * @iommu: the IOMMUMemoryRegion + */ uint64_t (*get_min_page_size)(IOMMUMemoryRegion *iommu); - /* Called when IOMMU Notifier flag changed */ + /* Called when IOMMU Notifier flag changes (ie when the set of + * events which IOMMU users are requesting notification for changes). + * Optional method -- need not be provided if the IOMMU does not + * need to know exactly which events must be notified. + * + * @iommu: the IOMMUMemoryRegion + * @old_flags: events which previously needed to be notified + * @new_flags: events which now need to be notified + */ void (*notify_flag_changed)(IOMMUMemoryRegion *iommu, IOMMUNotifierFlag old_flags, IOMMUNotifierFlag new_flags); - /* Set this up to provide customized IOMMU replay function */ + /* Called to handle memory_region_iommu_replay(). + * + * The default implementation of memory_region_iommu_replay() is to + * call the IOMMU translate method for every page in the address space + * with flag == IOMMU_NONE and then call the notifier if translate + * returns a valid mapping. If this method is implemented then it + * overrides the default behaviour, and must provide the full semantics + * of memory_region_iommu_replay(), by calling @notifier for every + * translation present in the IOMMU. + * + * Optional method -- an IOMMU only needs to provide this method + * if the default is inefficient or produces undesirable side effects. + * + * Note: this is not related to record-and-replay functionality. + */ void (*replay)(IOMMUMemoryRegion *iommu, IOMMUNotifier *notifier); - /* Get IOMMU misc attributes */ - int (*get_attr)(IOMMUMemoryRegion *iommu, enum IOMMUMemoryRegionAttr, + /* Get IOMMU misc attributes. This is an optional method that + * can be used to allow users of the IOMMU to get implementation-specific + * information. The IOMMU implements this method to handle calls + * by IOMMU users to memory_region_iommu_get_attr() by filling in + * the arbitrary data pointer for any IOMMUMemoryRegionAttr values that + * the IOMMU supports. If the method is unimplemented then + * memory_region_iommu_get_attr() will always return -EINVAL. + * + * @iommu: the IOMMUMemoryRegion + * @attr: attribute being queried + * @data: memory to fill in with the attribute data + * + * Returns 0 on success, or a negative errno; in particular + * returns -EINVAL for unrecognized or unimplemented attribute types. + */ + int (*get_attr)(IOMMUMemoryRegion *iommu, enum IOMMUMemoryRegionAttr attr, void *data); } IOMMUMemoryRegionClass; @@ -682,6 +754,14 @@ void memory_region_init_rom_device_nomigrate(MemoryRegion *mr, * An IOMMU region translates addresses and forwards accesses to a target * memory region. * + * The IOMMU implementation must define a subclass of TYPE_IOMMU_MEMORY_REGION. + * @_iommu_mr should be a pointer to enough memory for an instance of + * that subclass, @instance_size is the size of that subclass, and + * @mrtypename is its name. This function will initialize @_iommu_mr as an + * instance of the subclass, and its methods will then be called to handle + * accesses to the memory region. See the documentation of + * #IOMMUMemoryRegionClass for further details. + * * @_iommu_mr: the #IOMMUMemoryRegion to be initialized * @instance_size: the IOMMUMemoryRegion subclass instance size * @mrtypename: the type name of the #IOMMUMemoryRegion @@ -930,6 +1010,8 @@ void memory_region_register_iommu_notifier(MemoryRegion *mr, * a notifier with the minimum page granularity returned by * mr->iommu_ops->get_page_size(). * + * Note: this is not related to record-and-replay functionality. + * * @iommu_mr: the memory region to observe * @n: the notifier to which to replay iommu mappings */ @@ -939,6 +1021,8 @@ void memory_region_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n); * memory_region_iommu_replay_all: replay existing IOMMU translations * to all the notifiers registered. * + * Note: this is not related to record-and-replay functionality. + * * @iommu_mr: the memory region to observe */ void memory_region_iommu_replay_all(IOMMUMemoryRegion *iommu_mr); @@ -958,7 +1042,9 @@ void memory_region_unregister_iommu_notifier(MemoryRegion *mr, * memory_region_iommu_get_attr: return an IOMMU attr if get_attr() is * defined on the IOMMU. * - * Returns 0 if succeded, error code otherwise. + * Returns 0 on success, or a negative errno otherwise. In particular, + * -EINVAL indicates that the IOMMU does not support the requested + * attribute. * * @iommu_mr: the memory region * @attr: the requested attribute @@ -1787,7 +1873,7 @@ void address_space_cache_destroy(MemoryRegionCache *cache); * entry. Should be called from an RCU critical section. */ IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr, - bool is_write); + bool is_write, MemTxAttrs attrs); /* address_space_translate: translate an address range into an address space * into a MemoryRegion and an address range into that section. Should be @@ -1800,17 +1886,20 @@ IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr, * #MemoryRegion. * @len: pointer to length * @is_write: indicates the transfer direction + * @attrs: memory attributes */ MemoryRegion *flatview_translate(FlatView *fv, hwaddr addr, hwaddr *xlat, - hwaddr *len, bool is_write); + hwaddr *len, bool is_write, + MemTxAttrs attrs); static inline MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr, hwaddr *xlat, - hwaddr *len, bool is_write) + hwaddr *len, bool is_write, + MemTxAttrs attrs) { return flatview_translate(address_space_to_flatview(as), - addr, xlat, len, is_write); + addr, xlat, len, is_write, attrs); } /* address_space_access_valid: check for validity of accessing an address @@ -1827,8 +1916,10 @@ static inline MemoryRegion *address_space_translate(AddressSpace *as, * @addr: address within that address space * @len: length of the area to be checked * @is_write: indicates the transfer direction + * @attrs: memory attributes */ -bool address_space_access_valid(AddressSpace *as, hwaddr addr, int len, bool is_write); +bool address_space_access_valid(AddressSpace *as, hwaddr addr, int len, + bool is_write, MemTxAttrs attrs); /* address_space_map: map a physical memory region into a host virtual address * @@ -1842,9 +1933,10 @@ bool address_space_access_valid(AddressSpace *as, hwaddr addr, int len, bool is_ * @addr: address within that address space * @plen: pointer to length of buffer; updated on return * @is_write: indicates the transfer direction + * @attrs: memory attributes */ void *address_space_map(AddressSpace *as, hwaddr addr, - hwaddr *plen, bool is_write); + hwaddr *plen, bool is_write, MemTxAttrs attrs); /* address_space_unmap: Unmaps a memory region previously mapped by address_space_map() * @@ -1916,7 +2008,7 @@ MemTxResult address_space_read(AddressSpace *as, hwaddr addr, rcu_read_lock(); fv = address_space_to_flatview(as); l = len; - mr = flatview_translate(fv, addr, &addr1, &l, false); + mr = flatview_translate(fv, addr, &addr1, &l, false, attrs); if (len == l && memory_access_is_direct(mr, false)) { ptr = qemu_map_ram_ptr(mr->ram_block, addr1); memcpy(buf, ptr, len); diff --git a/include/hw/xen/xen_common.h b/include/hw/xen/xen_common.h index bbf207dcef..93f631e5bf 100644 --- a/include/hw/xen/xen_common.h +++ b/include/hw/xen/xen_common.h @@ -119,6 +119,22 @@ static inline int xendevicemodel_pin_memory_cacheattr( return xc_domain_pin_memory_cacheattr(xen_xc, domid, start, end, type); } +typedef void xenforeignmemory_resource_handle; + +#define XENMEM_resource_ioreq_server 0 + +#define XENMEM_resource_ioreq_server_frame_bufioreq 0 +#define XENMEM_resource_ioreq_server_frame_ioreq(n) (1 + (n)) + +static inline xenforeignmemory_resource_handle *xenforeignmemory_map_resource( + xenforeignmemory_handle *fmem, domid_t domid, unsigned int type, + unsigned int id, unsigned long frame, unsigned long nr_frames, + void **paddr, int prot, int flags) +{ + errno = EOPNOTSUPP; + return NULL; +} + #endif /* CONFIG_XEN_CTRL_INTERFACE_VERSION < 41100 */ #if CONFIG_XEN_CTRL_INTERFACE_VERSION < 41000 diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index efa0a1150e..f65852fc24 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -913,6 +913,9 @@ extern const VMStateInfo vmstate_info_qtailq; #define VMSTATE_BOOL_ARRAY(_f, _s, _n) \ VMSTATE_BOOL_ARRAY_V(_f, _s, _n, 0) +#define VMSTATE_BOOL_SUB_ARRAY(_f, _s, _start, _num) \ + VMSTATE_SUB_ARRAY(_f, _s, _start, _num, 0, vmstate_info_bool, bool) + #define VMSTATE_UINT16_ARRAY_V(_f, _s, _n, _v) \ VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_uint16, uint16_t) diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index ffb4652f71..b366bb48bd 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -23,6 +23,7 @@ typedef enum QmpCommandOptions QCO_NO_OPTIONS = 0x0, QCO_NO_SUCCESS_RESP = (1U << 0), QCO_ALLOW_OOB = (1U << 1), + QCO_ALLOW_PRECONFIG = (1U << 2), } QmpCommandOptions; typedef struct QmpCommand diff --git a/include/qemu/job.h b/include/qemu/job.h index 8c8badf75e..1d820530fa 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -124,6 +124,9 @@ typedef struct Job { /** Estimated progress_current value at the completion of the job */ int64_t progress_total; + /** Error string for a failed job (NULL if, and only if, job->ret == 0) */ + char *error; + /** ret code passed to job_completed. */ int ret; @@ -466,13 +469,15 @@ void job_transition_to_ready(Job *job); /** * @job: The job being completed. * @ret: The status code. + * @error: The error message for a failing job (only with @ret < 0). If @ret is + * negative, but NULL is given for @error, strerror() is used. * * Marks @job as completed. If @ret is non-zero, the job transaction it is part * of is aborted. If @ret is zero, the job moves into the WAITING state. If it * is the last job to complete in its transaction, all jobs in the transaction * move from WAITING to PENDING. */ -void job_completed(Job *job, int ret); +void job_completed(Job *job, int ret, Error *error); /** Asynchronously complete the specified @job. */ void job_complete(Job *job, Error **errp); diff --git a/include/sysemu/dma.h b/include/sysemu/dma.h index c228c66513..5da3c4e3c5 100644 --- a/include/sysemu/dma.h +++ b/include/sysemu/dma.h @@ -77,7 +77,8 @@ static inline bool dma_memory_valid(AddressSpace *as, DMADirection dir) { return address_space_access_valid(as, addr, len, - dir == DMA_DIRECTION_FROM_DEVICE); + dir == DMA_DIRECTION_FROM_DEVICE, + MEMTXATTRS_UNSPECIFIED); } static inline int dma_memory_rw_relaxed(AddressSpace *as, dma_addr_t addr, @@ -132,7 +133,8 @@ static inline void *dma_memory_map(AddressSpace *as, hwaddr xlen = *len; void *p; - p = address_space_map(as, addr, &xlen, dir == DMA_DIRECTION_FROM_DEVICE); + p = address_space_map(as, addr, &xlen, dir == DMA_DIRECTION_FROM_DEVICE, + MEMTXATTRS_UNSPECIFIED); *len = xlen; return p; } diff --git a/include/sysemu/numa.h b/include/sysemu/numa.h index d99e5474b4..7a0ae751aa 100644 --- a/include/sysemu/numa.h +++ b/include/sysemu/numa.h @@ -22,7 +22,9 @@ struct NumaNodeMem { }; extern NodeInfo numa_info[MAX_NODES]; +int parse_numa(void *opaque, QemuOpts *opts, Error **errp); void parse_numa_opts(MachineState *ms); +void numa_complete_configuration(MachineState *ms); void query_numa_node_mem(NumaNodeMem node_mem[]); extern QemuOptsList qemu_numa_opts; void numa_legacy_auto_assign_ram(MachineClass *mc, NodeInfo *nodes, diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 544ab77a2b..e893f72f3b 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -66,6 +66,7 @@ typedef enum WakeupReason { QEMU_WAKEUP_REASON_OTHER, } WakeupReason; +void qemu_exit_preconfig_request(void); void qemu_system_reset_request(ShutdownCause reason); void qemu_system_suspend_request(void); void qemu_register_suspend_notifier(Notifier *notifier); diff --git a/include/sysemu/xen-mapcache.h b/include/sysemu/xen-mapcache.h index bd4d49e0a4..a03e2f1878 100644 --- a/include/sysemu/xen-mapcache.h +++ b/include/sysemu/xen-mapcache.h @@ -9,9 +9,8 @@ #ifndef XEN_MAPCACHE_H #define XEN_MAPCACHE_H -typedef hwaddr (*phys_offset_to_gaddr_t)(hwaddr start_addr, - ram_addr_t size, - void *opaque); +typedef hwaddr (*phys_offset_to_gaddr_t)(hwaddr phys_offset, + ram_addr_t size); #ifdef CONFIG_XEN void xen_map_cache_init(phys_offset_to_gaddr_t f, @@ -136,14 +136,9 @@ void qmp_job_dismiss(const char *id, Error **errp) static JobInfo *job_query_single(Job *job, Error **errp) { JobInfo *info; - const char *errmsg = NULL; assert(!job_is_internal(job)); - if (job->ret < 0) { - errmsg = strerror(-job->ret); - } - info = g_new(JobInfo, 1); *info = (JobInfo) { .id = g_strdup(job->id), @@ -151,8 +146,8 @@ static JobInfo *job_query_single(Job *job, Error **errp) .status = job->status, .current_progress = job->progress_current, .total_progress = job->progress_total, - .has_error = !!errmsg, - .error = g_strdup(errmsg), + .has_error = !!job->error, + .error = g_strdup(job->error), }; return info; @@ -369,6 +369,7 @@ void job_unref(Job *job) QLIST_REMOVE(job, job_list); + g_free(job->error); g_free(job->id); g_free(job); } @@ -660,6 +661,9 @@ static void job_update_rc(Job *job) job->ret = -ECANCELED; } if (job->ret) { + if (!job->error) { + job->error = g_strdup(strerror(-job->ret)); + } job_state_transition(job, JOB_STATUS_ABORTING); } } @@ -782,6 +786,7 @@ static int job_prepare(Job *job) { if (job->ret == 0 && job->driver->prepare) { job->ret = job->driver->prepare(job); + job_update_rc(job); } return job->ret; } @@ -855,10 +860,17 @@ static void job_completed_txn_success(Job *job) } } -void job_completed(Job *job, int ret) +void job_completed(Job *job, int ret, Error *error) { assert(job && job->txn && !job_is_completed(job)); + job->ret = ret; + if (error) { + assert(job->ret < 0); + job->error = g_strdup(error_get_pretty(error)); + error_free(error); + } + job_update_rc(job); trace_job_completed(job, ret, job->ret); if (job->ret) { @@ -876,7 +888,7 @@ void job_cancel(Job *job, bool force) } job_cancel_async(job, force); if (!job_started(job)) { - job_completed(job, -ECANCELED); + job_completed(job, -ECANCELED, NULL); } else if (job->deferred_to_main_loop) { job_completed_txn_abort(job); } else { diff --git a/linux-user/microblaze/cpu_loop.c b/linux-user/microblaze/cpu_loop.c index 5ffb83dea2..5af12d5b21 100644 --- a/linux-user/microblaze/cpu_loop.c +++ b/linux-user/microblaze/cpu_loop.c @@ -105,8 +105,8 @@ void cpu_loop(CPUMBState *env) queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; default: - printf ("Unhandled hw-exception: 0x%x\n", - env->sregs[SR_ESR] & ESR_EC_MASK); + printf("Unhandled hw-exception: 0x%" PRIx64 "\n", + env->sregs[SR_ESR] & ESR_EC_MASK); cpu_dump_state(cs, stderr, fprintf, 0); exit(EXIT_FAILURE); break; @@ -1266,7 +1266,8 @@ static void unassigned_mem_write(void *opaque, hwaddr addr, } static bool unassigned_mem_accepts(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return false; } @@ -1344,7 +1345,8 @@ static const MemoryRegionOps ram_device_mem_ops = { bool memory_region_access_valid(MemoryRegion *mr, hwaddr addr, unsigned size, - bool is_write) + bool is_write, + MemTxAttrs attrs) { int access_size_min, access_size_max; int access_size, i; @@ -1370,7 +1372,7 @@ bool memory_region_access_valid(MemoryRegion *mr, access_size = MAX(MIN(size, access_size_max), access_size_min); for (i = 0; i < size; i += access_size) { if (!mr->ops->valid.accepts(mr->opaque, addr + i, access_size, - is_write)) { + is_write, attrs)) { return false; } } @@ -1413,7 +1415,7 @@ MemTxResult memory_region_dispatch_read(MemoryRegion *mr, { MemTxResult r; - if (!memory_region_access_valid(mr, addr, size, false)) { + if (!memory_region_access_valid(mr, addr, size, false, attrs)) { *pval = unassigned_mem_read(mr, addr, size); return MEMTX_DECODE_ERROR; } @@ -1455,7 +1457,7 @@ MemTxResult memory_region_dispatch_write(MemoryRegion *mr, unsigned size, MemTxAttrs attrs) { - if (!memory_region_access_valid(mr, addr, size, true)) { + if (!memory_region_access_valid(mr, addr, size, true, attrs)) { unassigned_mem_write(mr, addr, data, size); return MEMTX_DECODE_ERROR; } diff --git a/memory_ldst.inc.c b/memory_ldst.inc.c index 25d6125747..15483987fe 100644 --- a/memory_ldst.inc.c +++ b/memory_ldst.inc.c @@ -33,7 +33,7 @@ static inline uint32_t glue(address_space_ldl_internal, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, false); + mr = TRANSLATE(addr, &addr1, &l, false, attrs); if (l < 4 || !IS_DIRECT(mr, false)) { release_lock |= prepare_mmio_access(mr); @@ -109,7 +109,7 @@ static inline uint64_t glue(address_space_ldq_internal, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, false); + mr = TRANSLATE(addr, &addr1, &l, false, attrs); if (l < 8 || !IS_DIRECT(mr, false)) { release_lock |= prepare_mmio_access(mr); @@ -183,7 +183,7 @@ uint32_t glue(address_space_ldub, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, false); + mr = TRANSLATE(addr, &addr1, &l, false, attrs); if (!IS_DIRECT(mr, false)) { release_lock |= prepare_mmio_access(mr); @@ -219,7 +219,7 @@ static inline uint32_t glue(address_space_lduw_internal, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, false); + mr = TRANSLATE(addr, &addr1, &l, false, attrs); if (l < 2 || !IS_DIRECT(mr, false)) { release_lock |= prepare_mmio_access(mr); @@ -296,7 +296,7 @@ void glue(address_space_stl_notdirty, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, true); + mr = TRANSLATE(addr, &addr1, &l, true, attrs); if (l < 4 || !IS_DIRECT(mr, true)) { release_lock |= prepare_mmio_access(mr); @@ -333,7 +333,7 @@ static inline void glue(address_space_stl_internal, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, true); + mr = TRANSLATE(addr, &addr1, &l, true, attrs); if (l < 4 || !IS_DIRECT(mr, true)) { release_lock |= prepare_mmio_access(mr); @@ -405,7 +405,7 @@ void glue(address_space_stb, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, true); + mr = TRANSLATE(addr, &addr1, &l, true, attrs); if (!IS_DIRECT(mr, true)) { release_lock |= prepare_mmio_access(mr); r = memory_region_dispatch_write(mr, addr1, val, 1, attrs); @@ -438,7 +438,7 @@ static inline void glue(address_space_stw_internal, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, true); + mr = TRANSLATE(addr, &addr1, &l, true, attrs); if (l < 2 || !IS_DIRECT(mr, true)) { release_lock |= prepare_mmio_access(mr); @@ -511,7 +511,7 @@ static void glue(address_space_stq_internal, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, true); + mr = TRANSLATE(addr, &addr1, &l, true, attrs); if (l < 8 || !IS_DIRECT(mr, true)) { release_lock |= prepare_mmio_access(mr); diff --git a/migration/exec.c b/migration/exec.c index 9d0f82f1f0..0bbeb63c97 100644 --- a/migration/exec.c +++ b/migration/exec.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "channel.h" #include "exec.h" +#include "migration.h" #include "io/channel-command.h" #include "trace.h" @@ -48,6 +49,9 @@ static gboolean exec_accept_incoming_migration(QIOChannel *ioc, { migration_channel_process_incoming(ioc); object_unref(OBJECT(ioc)); + if (!migrate_use_multifd()) { + migration_incoming_process(); + } return G_SOURCE_REMOVE; } diff --git a/migration/fd.c b/migration/fd.c index 9a380bbbc4..fee34ffdc0 100644 --- a/migration/fd.c +++ b/migration/fd.c @@ -17,6 +17,7 @@ #include "qemu/osdep.h" #include "channel.h" #include "fd.h" +#include "migration.h" #include "monitor/monitor.h" #include "io/channel-util.h" #include "trace.h" @@ -48,6 +49,9 @@ static gboolean fd_accept_incoming_migration(QIOChannel *ioc, { migration_channel_process_incoming(ioc); object_unref(OBJECT(ioc)); + if (!migrate_use_multifd()) { + migration_incoming_process(); + } return G_SOURCE_REMOVE; } diff --git a/migration/ram.c b/migration/ram.c index 5bcbf7a9f9..c53e8369a3 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -246,7 +246,7 @@ int64_t ramblock_recv_bitmap_send(QEMUFile *file, qemu_put_be64(file, RAMBLOCK_RECV_BITMAP_ENDING); qemu_fflush(file); - free(le_bitmap); + g_free(le_bitmap); if (qemu_file_get_error(file)) { return qemu_file_get_error(file); @@ -3514,7 +3514,7 @@ int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block) ret = 0; out: - free(le_bitmap); + g_free(le_bitmap); return ret; } @@ -1178,8 +1178,7 @@ static void monitor_init_qmp_commands(void) qmp_init_marshal(&qmp_commands); qmp_register_command(&qmp_commands, "query-qmp-schema", - qmp_query_qmp_schema, - QCO_NO_OPTIONS); + qmp_query_qmp_schema, QCO_ALLOW_PRECONFIG); qmp_register_command(&qmp_commands, "device_add", qmp_device_add, QCO_NO_OPTIONS); qmp_register_command(&qmp_commands, "netdev_add", qmp_netdev_add, @@ -1189,7 +1188,7 @@ static void monitor_init_qmp_commands(void) QTAILQ_INIT(&qmp_cap_negotiation_commands); qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities", - qmp_marshal_qmp_capabilities, QCO_NO_OPTIONS); + qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG); } static bool qmp_cap_enabled(Monitor *mon, QMPCapability cap) @@ -3370,6 +3369,12 @@ static void handle_hmp_command(Monitor *mon, const char *cmdline) trace_handle_hmp_command(mon, cmdline); + if (runstate_check(RUN_STATE_PRECONFIG)) { + monitor_printf(mon, "HMP not available in preconfig state, " + "use QMP instead\n"); + return; + } + cmd = monitor_parse_command(mon, cmdline, &cmdline, mon->cmd_table); if (!cmd) { return; diff --git a/net/slirp.c b/net/slirp.c index 692252445a..1e14318b4d 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -157,7 +157,8 @@ static int net_slirp_init(NetClientState *peer, const char *model, const char *bootfile, const char *vdhcp_start, const char *vnameserver, const char *vnameserver6, const char *smb_export, const char *vsmbserver, - const char **dnssearch, Error **errp) + const char **dnssearch, const char *vdomainname, + Error **errp) { /* default settings according to historic slirp */ struct in_addr net = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */ @@ -359,6 +360,11 @@ static int net_slirp_init(NetClientState *peer, const char *model, ip6_dns.s6_addr[15] |= 3; } + if (vdomainname && !*vdomainname) { + error_setg(errp, "'domainname' parameter cannot be empty"); + return -1; + } + nc = qemu_new_net_client(&net_slirp_info, peer, model, name); @@ -371,7 +377,7 @@ static int net_slirp_init(NetClientState *peer, const char *model, s->slirp = slirp_init(restricted, ipv4, net, mask, host, ipv6, ip6_prefix, vprefix6_len, ip6_host, vhostname, tftp_export, bootfile, dhcp, - dns, ip6_dns, dnssearch, s); + dns, ip6_dns, dnssearch, vdomainname, s); QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry); for (config = slirp_configs; config; config = config->next) { @@ -486,7 +492,9 @@ void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict) goto fail_syntax; } - host_port = atoi(p); + if (qemu_strtoi(p, NULL, 10, &host_port)) { + goto fail_syntax; + } err = slirp_remove_hostfwd(s->slirp, is_udp, host_addr, host_port); @@ -958,7 +966,7 @@ int net_init_slirp(const Netdev *netdev, const char *name, user->ipv6_host, user->hostname, user->tftp, user->bootfile, user->dhcpstart, user->dns, user->ipv6_dns, user->smb, - user->smbserver, dnssearch, errp); + user->smbserver, dnssearch, user->domainname, errp); while (slirp_configs) { config = slirp_configs; @@ -141,9 +141,8 @@ static void parse_numa_distance(NumaDistOptions *dist, Error **errp) uint8_t val = dist->val; if (src >= MAX_NODES || dst >= MAX_NODES) { - error_setg(errp, - "Invalid node %d, max possible could be %d", - MAX(src, dst), MAX_NODES); + error_setg(errp, "Parameter '%s' expects an integer between 0 and %d", + src >= MAX_NODES ? "src" : "dst", MAX_NODES - 1); return; } @@ -170,28 +169,11 @@ static void parse_numa_distance(NumaDistOptions *dist, Error **errp) have_numa_distance = true; } -static int parse_numa(void *opaque, QemuOpts *opts, Error **errp) +static +void set_numa_options(MachineState *ms, NumaOptions *object, Error **errp) { - NumaOptions *object = NULL; - MachineState *ms = opaque; Error *err = NULL; - { - Visitor *v = opts_visitor_new(opts); - visit_type_NumaOptions(v, NULL, &object, &err); - visit_free(v); - } - - if (err) { - goto end; - } - - /* Fix up legacy suffix-less format */ - if ((object->type == NUMA_OPTIONS_TYPE_NODE) && object->u.node.has_mem) { - const char *mem_str = qemu_opt_get(opts, "mem"); - qemu_strtosz_MiB(mem_str, NULL, &object->u.node.mem); - } - switch (object->type) { case NUMA_OPTIONS_TYPE_NODE: parse_numa_node(ms, &object->u.node, &err); @@ -225,6 +207,31 @@ static int parse_numa(void *opaque, QemuOpts *opts, Error **errp) } end: + error_propagate(errp, err); +} + +int parse_numa(void *opaque, QemuOpts *opts, Error **errp) +{ + NumaOptions *object = NULL; + MachineState *ms = MACHINE(opaque); + Error *err = NULL; + Visitor *v = opts_visitor_new(opts); + + visit_type_NumaOptions(v, NULL, &object, &err); + visit_free(v); + if (err) { + goto end; + } + + /* Fix up legacy suffix-less format */ + if ((object->type == NUMA_OPTIONS_TYPE_NODE) && object->u.node.has_mem) { + const char *mem_str = qemu_opt_get(opts, "mem"); + qemu_strtosz_MiB(mem_str, NULL, &object->u.node.mem); + } + + set_numa_options(ms, object, &err); + +end: qapi_free_NumaOptions(object); if (err) { error_report_err(err); @@ -339,15 +346,11 @@ void numa_default_auto_assign_ram(MachineClass *mc, NodeInfo *nodes, nodes[i].node_mem = size - usedmem; } -void parse_numa_opts(MachineState *ms) +void numa_complete_configuration(MachineState *ms) { int i; MachineClass *mc = MACHINE_GET_CLASS(ms); - if (qemu_opts_foreach(qemu_find_opts("numa"), parse_numa, ms, NULL)) { - exit(1); - } - /* * If memory hotplug is enabled (slots > 0) but without '-numa' * options explicitly on CLI, guestes will break. @@ -434,6 +437,24 @@ void parse_numa_opts(MachineState *ms) } } +void parse_numa_opts(MachineState *ms) +{ + if (qemu_opts_foreach(qemu_find_opts("numa"), parse_numa, ms, NULL)) { + exit(1); + } +} + +void qmp_set_numa_node(NumaOptions *cmd, Error **errp) +{ + if (!runstate_check(RUN_STATE_PRECONFIG)) { + error_setg(errp, "The command is permitted only in '%s' state", + RunState_str(RUN_STATE_PRECONFIG)); + return; + } + + set_numa_options(MACHINE(qdev_get_machine()), cmd, errp); +} + void numa_cpu_pre_plug(const CPUArchId *slot, DeviceState *dev, Error **errp) { int node_id = object_property_get_int(OBJECT(dev), "node-id", &error_abort); diff --git a/qapi/block-core.json b/qapi/block-core.json index 7dfa77a05c..4b1de474a9 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2424,7 +2424,7 @@ # @vxhs: Since 2.10 # @throttle: Since 2.11 # @nvme: Since 2.12 -# @copy-on-read: Since 2.13 +# @copy-on-read: Since 3.0 # # Since: 2.9 ## @@ -2452,7 +2452,7 @@ # @x-check-cache-dropped: whether to check that page cache was dropped on live # migration. May cause noticeable delays if the image # file is large, do not use in production. -# (default: off) (since: 2.13) +# (default: off) (since: 3.0) # # Since: 2.9 ## @@ -4011,16 +4011,20 @@ } } ## -# @x-blockdev-create: +# @blockdev-create: # -# Create an image format on a given node. -# TODO Replace with something asynchronous (block job?) +# Starts a job to create an image format on a given node. The job is +# automatically finalized, but a manual job-dismiss is required. # -# Since: 2.12 +# @job-id: Identifier for the newly created job. +# +# @options: Options for the image creation. +# +# Since: 3.0 ## -{ 'command': 'x-blockdev-create', - 'data': 'BlockdevCreateOptions', - 'boxed': true } +{ 'command': 'blockdev-create', + 'data': { 'job-id': 'str', + 'options': 'BlockdevCreateOptions' } } ## # @blockdev-open-tray: diff --git a/qapi/common.json b/qapi/common.json index c811d04984..c367adc4b6 100644 --- a/qapi/common.json +++ b/qapi/common.json @@ -140,7 +140,7 @@ # prefix to produce the corresponding QEMU executable name. This # is true even for "qemu-system-x86_64". # -# Since: 2.13 +# Since: 3.0 ## { 'enum' : 'SysEmuTarget', 'data' : [ 'aarch64', 'alpha', 'arm', 'cris', 'hppa', 'i386', 'lm32', diff --git a/qapi/introspect.json b/qapi/introspect.json index c7f67b7d78..80a0a3e656 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -262,13 +262,16 @@ # @allow-oob: whether the command allows out-of-band execution. # (Since: 2.12) # +# @allow-preconfig: command can be executed in preconfig runstate, +# default: false (Since 3.0) +# # TODO: @success-response (currently irrelevant, because it's QGA, not QMP) # # Since: 2.5 ## { 'struct': 'SchemaInfoCommand', 'data': { 'arg-type': 'str', 'ret-type': 'str', - 'allow-oob': 'bool' } } + 'allow-oob': 'bool', 'allow-preconfig': 'bool' } } ## # @SchemaInfoEvent: diff --git a/qapi/job.json b/qapi/job.json index 970124de76..17d10037c4 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -17,10 +17,12 @@ # # @backup: drive backup job type, see "drive-backup" # +# @create: image creation job type, see "blockdev-create" (since 3.0) +# # Since: 1.7 ## { 'enum': 'JobType', - 'data': ['commit', 'stream', 'mirror', 'backup'] } + 'data': ['commit', 'stream', 'mirror', 'backup', 'create'] } ## # @JobStatus: diff --git a/qapi/migration.json b/qapi/migration.json index 3ec418dabf..dc9cc85545 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -89,9 +89,9 @@ # # @postcopy-active: like active, but now in postcopy mode. (since 2.5) # -# @postcopy-paused: during postcopy but paused. (since 2.13) +# @postcopy-paused: during postcopy but paused. (since 3.0) # -# @postcopy-recover: trying to recover from a paused postcopy. (since 2.13) +# @postcopy-recover: trying to recover from a paused postcopy. (since 3.0) # # @completed: migration is finished. # @@ -163,11 +163,11 @@ # # @postcopy-blocktime: total time when all vCPU were blocked during postcopy # live migration. This is only present when the postcopy-blocktime -# migration capability is enabled. (Since 2.13) +# migration capability is enabled. (Since 3.0) # # @postcopy-vcpu-blocktime: list of the postcopy blocktime per vCPU. This is # only present when the postcopy-blocktime migration capability -# is enabled. (Since 2.13) +# is enabled. (Since 3.0) # # # Since: 0.14.0 @@ -374,7 +374,7 @@ # (since 2.12) # # @postcopy-blocktime: Calculate downtime for postcopy live migration -# (since 2.13) +# (since 3.0) # # Since: 1.2 ## @@ -1034,7 +1034,7 @@ # @detach: this argument exists only for compatibility reasons and # is ignored by QEMU # -# @resume: resume one paused migration, default "off". (since 2.13) +# @resume: resume one paused migration, default "off". (since 3.0) # # Returns: nothing on success # @@ -1208,7 +1208,7 @@ # "arguments": { "uri": "tcp:192.168.1.200:12345" } } # <- { "return": {} } # -# Since: 2.13 +# Since: 3.0 ## { 'command': 'migrate-recover', 'data': { 'uri': 'str' }, 'allow-oob': true } @@ -1225,6 +1225,6 @@ # -> { "execute": "migrate-pause" } # <- { "return": {} } # -# Since: 2.13 +# Since: 3.0 ## { 'command': 'migrate-pause', 'allow-oob': true } diff --git a/qapi/misc.json b/qapi/misc.json index efc3d23057..f83a63a0ab 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -37,7 +37,8 @@ # ## { 'command': 'qmp_capabilities', - 'data': { '*enable': [ 'QMPCapability' ] } } + 'data': { '*enable': [ 'QMPCapability' ] }, + 'allow-preconfig': true } ## # @QMPCapability: @@ -155,7 +156,8 @@ # Note: This example has been shortened as the real response is too long. # ## -{ 'command': 'query-commands', 'returns': ['CommandInfo'] } +{ 'command': 'query-commands', 'returns': ['CommandInfo'], + 'allow-preconfig': true } ## # @LostTickPolicy: @@ -558,11 +560,11 @@ # @props: properties describing to which node/socket/core/thread # virtual CPU belongs to, provided if supported by board # -# @arch: base architecture of the cpu; deprecated since 2.13.0 in favor +# @arch: base architecture of the cpu; deprecated since 3.0.0 in favor # of @target # # @target: the QEMU system emulation target, which determines which -# additional fields will be listed (since 2.13) +# additional fields will be listed (since 3.0) # # Since: 2.12 # @@ -1243,6 +1245,29 @@ { 'command': 'cont' } ## +# @exit-preconfig: +# +# Exit from "preconfig" state +# +# This command makes QEMU exit the preconfig state and proceed with +# VM initialization using configuration data provided on the command line +# and via the QMP monitor during the preconfig state. The command is only +# available during the preconfig state (i.e. when the --preconfig command +# line option was in use). +# +# Since 3.0 +# +# Returns: nothing +# +# Example: +# +# -> { "execute": "exit-preconfig" } +# <- { "return": {} } +# +## +{ 'command': 'exit-preconfig', 'allow-preconfig': true } + +## # @system_wakeup: # # Wakeup guest from suspend. Does nothing in case the guest isn't suspended. @@ -2656,7 +2681,8 @@ # ## {'command': 'query-command-line-options', 'data': { '*option': 'str' }, - 'returns': ['CommandLineOptionInfo'] } + 'returns': ['CommandLineOptionInfo'], + 'allow-preconfig': true } ## # @X86CPURegister32: @@ -3267,7 +3293,8 @@ # ]} # ## -{ 'command': 'query-hotpluggable-cpus', 'returns': ['HotpluggableCPU'] } +{ 'command': 'query-hotpluggable-cpus', 'returns': ['HotpluggableCPU'], + 'allow-preconfig': true } ## # @GuidInfo: @@ -3491,3 +3518,17 @@ ## { 'command': 'x-oob-test', 'data' : { 'lock': 'bool' }, 'allow-oob': true } + +## +# @set-numa-node: +# +# Runtime equivalent of '-numa' CLI option, available at +# preconfigure stage to configure numa mapping before initializing +# machine. +# +# Since 3.0 +## +{ 'command': 'set-numa-node', 'boxed': true, + 'data': 'NumaOptions', + 'allow-preconfig': true +} diff --git a/qapi/net.json b/qapi/net.json index b8adf1f03f..32681a1af7 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -160,6 +160,9 @@ # @dnssearch: list of DNS suffixes to search, passed as DHCP option # to the guest # +# @domainname: guest-visible domain name of the virtual nameserver +# (since 2.12) +# # @ipv6-prefix: IPv6 network prefix (default is fec0::) (since # 2.6). The network prefix is given in the usual # hexadecimal IPv6 address notation. @@ -197,6 +200,7 @@ '*dhcpstart': 'str', '*dns': 'str', '*dnssearch': ['String'], + '*domainname': 'str', '*ipv6-prefix': 'str', '*ipv6-prefixlen': 'int', '*ipv6-host': 'str', @@ -498,7 +502,7 @@ # # Since: 1.2 # -# 'vlan': dropped in 2.13 +# 'vlan': dropped in 3.0 ## { 'struct': 'NetLegacy', 'data': { diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index f9377b27fd..935f9e159c 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -18,6 +18,7 @@ #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qbool.h" +#include "sysemu/sysemu.h" QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp) { @@ -101,6 +102,13 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, return NULL; } + if (runstate_check(RUN_STATE_PRECONFIG) && + !(cmd->options & QCO_ALLOW_PRECONFIG)) { + error_setg(errp, "The command '%s' isn't permitted in '%s' state", + cmd->name, RunState_str(RUN_STATE_PRECONFIG)); + return NULL; + } + if (!qdict_haskey(dict, "arguments")) { args = qdict_new(); } else { diff --git a/qapi/run-state.json b/qapi/run-state.json index 1c9fff3aef..332e44897b 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -49,12 +49,15 @@ # @colo: guest is paused to save/restore VM state under colo checkpoint, # VM can not get into this state unless colo capability is enabled # for migration. (since 2.8) +# @preconfig: QEMU is paused before board specific init callback is executed. +# The state is reachable only if the --preconfig CLI option is used. +# (Since 3.0) ## { 'enum': 'RunState', 'data': [ 'debug', 'inmigrate', 'internal-error', 'io-error', 'paused', 'postmigrate', 'prelaunch', 'finish-migrate', 'restore-vm', 'running', 'save-vm', 'shutdown', 'suspended', 'watchdog', - 'guest-panicked', 'colo' ] } + 'guest-panicked', 'colo', 'preconfig' ] } ## # @StatusInfo: @@ -91,7 +94,8 @@ # "status": "running" } } # ## -{ 'command': 'query-status', 'returns': 'StatusInfo' } +{ 'command': 'query-status', 'returns': 'StatusInfo', + 'allow-preconfig': true } ## # @SHUTDOWN: diff --git a/qapi/ui.json b/qapi/ui.json index 3ad7835992..fc18a05f0f 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -1031,7 +1031,7 @@ # @core: Use OpenGL with Core (desktop) Context. # @es: Use OpenGL with ES (embedded systems) Context. # - # Since: 2.13 + # Since: 3.0 # ## { 'enum' : 'DisplayGLMode', diff --git a/qemu-doc.texi b/qemu-doc.texi index 057a48ec1f..f00706b999 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2917,7 +2917,7 @@ The @code{-localtime} option has been replaced by @code{-rtc base=localtime}. The @code{-startdate} option has been replaced by @code{-rtc base=@var{date}}. -@subsection -virtioconsole (since 2.13.0) +@subsection -virtioconsole (since 3.0.0) Option @option{-virtioconsole} has been replaced by @option{-device virtconsole}. @@ -2945,7 +2945,7 @@ from qcow2 images. The ``query-cpus'' command is replaced by the ``query-cpus-fast'' command. -@subsection query-cpus-fast "arch" output member (since 2.13.0) +@subsection query-cpus-fast "arch" output member (since 3.0.0) The ``arch'' output member of the ``query-cpus-fast'' command is replaced by the ``target'' output member. diff --git a/qemu-options.hx b/qemu-options.hx index abbfa6ae9e..c0d3951e9f 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1906,8 +1906,8 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, "-netdev user,id=str[,ipv4[=on|off]][,net=addr[/mask]][,host=addr]\n" " [,ipv6[=on|off]][,ipv6-net=addr[/int]][,ipv6-host=addr]\n" " [,restrict=on|off][,hostname=host][,dhcpstart=addr]\n" - " [,dns=addr][,ipv6-dns=addr][,dnssearch=domain][,tftp=dir]\n" - " [,bootfile=f][,hostfwd=rule][,guestfwd=rule]" + " [,dns=addr][,ipv6-dns=addr][,dnssearch=domain][,domainname=domain]\n" + " [,tftp=dir][,bootfile=f][,hostfwd=rule][,guestfwd=rule]" #ifndef _WIN32 "[,smb=dir[,smbserver=addr]]\n" #endif @@ -2135,6 +2135,9 @@ Example: qemu-system-i386 -nic user,dnssearch=mgmt.example.org,dnssearch=example.org @end example +@item domainname=@var{domain} +Specifies the client domain name reported by the built-in DHCP server. + @item tftp=@var{dir} When using the user mode network stack, activate a built-in TFTP server. The files in @var{dir} will be exposed as the root of a TFTP server. @@ -3299,6 +3302,19 @@ STEXI Run the emulation in single step mode. ETEXI +DEF("preconfig", 0, QEMU_OPTION_preconfig, \ + "--preconfig pause QEMU before machine is initialized\n", + QEMU_ARCH_ALL) +STEXI +@item --preconfig +@findex --preconfig +Pause QEMU for interactive configuration before the machine is created, +which allows querying and configuring properties that will affect +machine initialization. Use the QMP command 'exit-preconfig' to exit +the preconfig state and move to the next state (ie. run guest if -S +isn't used or pause the second time if -S is used). +ETEXI + DEF("S", 0, QEMU_OPTION_S, \ "-S freeze CPU at startup (use 'c' to start execution)\n", QEMU_ARCH_ALL) diff --git a/qemu-tech.texi b/qemu-tech.texi index 52a56ae25e..dcecba83cb 100644 --- a/qemu-tech.texi +++ b/qemu-tech.texi @@ -5,6 +5,7 @@ * CPU emulation:: * Translator Internals:: * QEMU compared to other emulators:: +* Managed start up options:: * Bibliography:: @end menu @@ -314,6 +315,45 @@ VirtualBox [9], Xen [10] and KVM [11] are based on QEMU. QEMU-SystemC [12] uses QEMU to simulate a system where some hardware devices are developed in SystemC. +@node Managed start up options +@section Managed start up options + +In system mode emulation, it's possible to create a VM in a paused state using +the -S command line option. In this state the machine is completely initialized +according to command line options and ready to execute VM code but VCPU threads +are not executing any code. The VM state in this paused state depends on the way +QEMU was started. It could be in: +@table @asis +@item initial state (after reset/power on state) +@item with direct kernel loading, the initial state could be amended to execute +code loaded by QEMU in the VM's RAM and with incoming migration +@item with incoming migration, initial state will by amended with the migrated +machine state after migration completes. +@end table + +This paused state is typically used by users to query machine state and/or +additionally configure the machine (by hotplugging devices) in runtime before +allowing VM code to run. + +However, at the -S pause point, it's impossible to configure options that affect +initial VM creation (like: -smp/-m/-numa ...) or cold plug devices. That's +when the --preconfig command line option should be used. It allows pausing QEMU +before the initial VM creation, in a new preconfig state, where additional +queries and configuration can be performed via QMP before moving on to +the resulting configuration startup. In the preconfig state, QEMU only allows +a limited set of commands over the QMP monitor, where the commands do not +depend on an initialized machine, including but not limited to: +@table @asis +@item qmp_capabilities +@item query-qmp-schema +@item query-commands +@item query-status +@item exit-preconfig +@end table +The full list of commands is in QMP schema which could be queried with +query-qmp-schema, where commands supported at preconfig state have option +'allow-preconfig' set to true. + @node Bibliography @section Bibliography @@ -161,6 +161,16 @@ SpiceInfo *qmp_query_spice(Error **errp) }; #endif +void qmp_exit_preconfig(Error **errp) +{ + if (!runstate_check(RUN_STATE_PRECONFIG)) { + error_setg(errp, "The command is permitted only in '%s' state", + RunState_str(RUN_STATE_PRECONFIG)); + return; + } + qemu_exit_preconfig_request(); +} + void qmp_cont(Error **errp) { BlockBackend *blk; diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 0c5da3a54d..3b0867c14f 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -193,13 +193,15 @@ out: return ret -def gen_register_command(name, success_response, allow_oob): +def gen_register_command(name, success_response, allow_oob, allow_preconfig): options = [] if not success_response: options += ['QCO_NO_SUCCESS_RESP'] if allow_oob: options += ['QCO_ALLOW_OOB'] + if allow_preconfig: + options += ['QCO_ALLOW_PRECONFIG'] if not options: options = ['QCO_NO_OPTIONS'] @@ -275,8 +277,8 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); c_prefix=c_name(self._prefix, protect=False))) genc.add(gen_registry(self._regy, self._prefix)) - def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed, allow_oob): + def visit_command(self, name, info, arg_type, ret_type, gen, + success_response, boxed, allow_oob, allow_preconfig): if not gen: return self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type)) @@ -285,7 +287,8 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); self._genc.add(gen_marshal_output(ret_type)) self._genh.add(gen_marshal_decl(name)) self._genc.add(gen_marshal(name, arg_type, boxed, ret_type)) - self._regy += gen_register_command(name, success_response, allow_oob) + self._regy += gen_register_command(name, success_response, allow_oob, + allow_preconfig) def gen_commands(schema, output_dir, prefix): diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index a032cec375..e82990f0f2 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -872,7 +872,8 @@ def check_keys(expr_elem, meta, required, optional=[]): raise QAPISemError(info, "'%s' of %s '%s' should only use false value" % (key, meta, name)) - if (key == 'boxed' or key == 'allow-oob') and value is not True: + if (key == 'boxed' or key == 'allow-oob' or + key == 'allow-preconfig') and value is not True: raise QAPISemError(info, "'%s' of %s '%s' should only use true value" % (key, meta, name)) @@ -922,7 +923,7 @@ def check_exprs(exprs): meta = 'command' check_keys(expr_elem, 'command', [], ['data', 'returns', 'gen', 'success-response', - 'boxed', 'allow-oob']) + 'boxed', 'allow-oob', 'allow-preconfig']) elif 'event' in expr: meta = 'event' check_keys(expr_elem, 'event', [], ['data', 'boxed']) @@ -1044,8 +1045,8 @@ class QAPISchemaVisitor(object): def visit_alternate_type(self, name, info, variants): pass - def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed, allow_oob): + def visit_command(self, name, info, arg_type, ret_type, gen, + success_response, boxed, allow_oob, allow_preconfig): pass def visit_event(self, name, info, arg_type, boxed): @@ -1422,7 +1423,7 @@ class QAPISchemaAlternateType(QAPISchemaType): class QAPISchemaCommand(QAPISchemaEntity): def __init__(self, name, info, doc, arg_type, ret_type, - gen, success_response, boxed, allow_oob): + gen, success_response, boxed, allow_oob, allow_preconfig): QAPISchemaEntity.__init__(self, name, info, doc) assert not arg_type or isinstance(arg_type, str) assert not ret_type or isinstance(ret_type, str) @@ -1434,6 +1435,7 @@ class QAPISchemaCommand(QAPISchemaEntity): self.success_response = success_response self.boxed = boxed self.allow_oob = allow_oob + self.allow_preconfig = allow_preconfig def check(self, schema): if self._arg_type_name: @@ -1458,7 +1460,8 @@ class QAPISchemaCommand(QAPISchemaEntity): visitor.visit_command(self.name, self.info, self.arg_type, self.ret_type, self.gen, self.success_response, - self.boxed, self.allow_oob) + self.boxed, self.allow_oob, + self.allow_preconfig) class QAPISchemaEvent(QAPISchemaEntity): @@ -1678,6 +1681,7 @@ class QAPISchema(object): success_response = expr.get('success-response', True) boxed = expr.get('boxed', False) allow_oob = expr.get('allow-oob', False) + allow_preconfig = expr.get('allow-preconfig', False) if isinstance(data, OrderedDict): data = self._make_implicit_object_type( name, info, doc, 'arg', self._make_members(data, info)) @@ -1686,7 +1690,7 @@ class QAPISchema(object): rets = self._make_array_type(rets[0], info) self._def_entity(QAPISchemaCommand(name, info, doc, data, rets, gen, success_response, - boxed, allow_oob)) + boxed, allow_oob, allow_preconfig)) def _def_event(self, expr, info, doc): name = expr['event'] diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py index 9b312b2c51..b5630844f9 100644 --- a/scripts/qapi/doc.py +++ b/scripts/qapi/doc.py @@ -226,8 +226,8 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): name=doc.symbol, body=texi_entity(doc, 'Members'))) - def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed, allow_oob): + def visit_command(self, name, info, arg_type, ret_type, gen, + success_response, boxed, allow_oob, allow_preconfig): doc = self.cur_doc if boxed: body = texi_body(doc) diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index f9e67e8227..5b6c72c7b2 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -171,14 +171,15 @@ const QLitObject %(c_name)s = %(c_string)s; {'members': [{'type': self._use_type(m.type)} for m in variants.variants]}) - def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed, allow_oob): + def visit_command(self, name, info, arg_type, ret_type, gen, + success_response, boxed, allow_oob, allow_preconfig): arg_type = arg_type or self._schema.the_empty_object_type ret_type = ret_type or self._schema.the_empty_object_type self._gen_qlit(name, 'command', {'arg-type': self._use_type(arg_type), 'ret-type': self._use_type(ret_type), - 'allow-oob': allow_oob}) + 'allow-oob': allow_oob, + 'allow-preconfig': allow_preconfig}) def visit_event(self, name, info, arg_type, boxed): arg_type = arg_type or self._schema.the_empty_object_type diff --git a/slirp/arp_table.c b/slirp/arp_table.c index bac608f97f..f81963bb88 100644 --- a/slirp/arp_table.c +++ b/slirp/arp_table.c @@ -33,7 +33,7 @@ void arp_table_add(Slirp *slirp, uint32_t ip_addr, uint8_t ethaddr[ETH_ALEN]) int i; DEBUG_CALL("arp_table_add"); - DEBUG_ARG("ip = %s", inet_ntoa(*(struct in_addr *)&ip_addr)); + DEBUG_ARG("ip = %s", inet_ntoa((struct in_addr){.s_addr = ip_addr})); DEBUG_ARGS((dfd, " hw addr = %02x:%02x:%02x:%02x:%02x:%02x\n", ethaddr[0], ethaddr[1], ethaddr[2], ethaddr[3], ethaddr[4], ethaddr[5])); @@ -67,7 +67,7 @@ bool arp_table_search(Slirp *slirp, uint32_t ip_addr, int i; DEBUG_CALL("arp_table_search"); - DEBUG_ARG("ip = %s", inet_ntoa(*(struct in_addr *)&ip_addr)); + DEBUG_ARG("ip = %s", inet_ntoa((struct in_addr){.s_addr = ip_addr})); /* If broadcast address */ if (ip_addr == 0xffffffff || ip_addr == broadcast_addr) { diff --git a/slirp/bootp.c b/slirp/bootp.c index 5dd1a415b5..9e7b53ba94 100644 --- a/slirp/bootp.c +++ b/slirp/bootp.c @@ -298,6 +298,14 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) q += val; } + if (slirp->vdomainname) { + val = strlen(slirp->vdomainname); + *q++ = RFC1533_DOMAINNAME; + *q++ = val; + memcpy(q, slirp->vdomainname, val); + q += val; + } + if (slirp->vdnssearch) { size_t spaceleft = sizeof(rbp->bp_vend) - (q - rbp->bp_vend); val = slirp->vdnssearch_len; diff --git a/slirp/libslirp.h b/slirp/libslirp.h index 540b3e5903..740408a96e 100644 --- a/slirp/libslirp.h +++ b/slirp/libslirp.h @@ -16,7 +16,7 @@ Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork, const char *tftp_path, const char *bootfile, struct in_addr vdhcp_start, struct in_addr vnameserver, struct in6_addr vnameserver6, const char **vdnssearch, - void *opaque); + const char *vdomainname, void *opaque); void slirp_cleanup(Slirp *slirp); void slirp_pollfds_fill(GArray *pollfds, uint32_t *timeout); diff --git a/slirp/ncsi.c b/slirp/ncsi.c index d12ba3e494..7116034afc 100644 --- a/slirp/ncsi.c +++ b/slirp/ncsi.c @@ -1,7 +1,7 @@ /* * NC-SI (Network Controller Sideband Interface) "echo" model * - * Copyright (C) 2016 IBM Corp. + * Copyright (C) 2016-2018 IBM Corp. * * This code is licensed under the GPL version 2 or later. See the * COPYING file in the top-level directory. @@ -11,6 +11,23 @@ #include "ncsi-pkt.h" +static uint32_t ncsi_calculate_checksum(uint16_t *data, int len) +{ + uint32_t checksum = 0; + int i; + + /* + * 32-bit unsigned sum of the NC-SI packet header and NC-SI packet + * payload interpreted as a series of 16-bit unsigned integer values. + */ + for (i = 0; i < len; i++) { + checksum += htons(data[i]); + } + + checksum = (~checksum + 1); + return checksum; +} + /* Get Capabilities */ static int ncsi_rsp_handler_gc(struct ncsi_rsp_pkt_hdr *rnh) { @@ -35,6 +52,20 @@ static int ncsi_rsp_handler_gls(struct ncsi_rsp_pkt_hdr *rnh) return 0; } +/* Get Parameters */ +static int ncsi_rsp_handler_gp(struct ncsi_rsp_pkt_hdr *rnh) +{ + struct ncsi_rsp_gp_pkt *rsp = (struct ncsi_rsp_gp_pkt *) rnh; + + /* no MAC address filters or VLAN filters on the channel */ + rsp->mac_cnt = 0; + rsp->mac_enable = 0; + rsp->vlan_cnt = 0; + rsp->vlan_enable = 0; + + return 0; +} + static const struct ncsi_rsp_handler { unsigned char type; int payload; @@ -60,9 +91,9 @@ static const struct ncsi_rsp_handler { { NCSI_PKT_RSP_EGMF, 4, NULL }, { NCSI_PKT_RSP_DGMF, 4, NULL }, { NCSI_PKT_RSP_SNFC, 4, NULL }, - { NCSI_PKT_RSP_GVI, 36, NULL }, + { NCSI_PKT_RSP_GVI, 40, NULL }, { NCSI_PKT_RSP_GC, 32, ncsi_rsp_handler_gc }, - { NCSI_PKT_RSP_GP, -1, NULL }, + { NCSI_PKT_RSP_GP, 40, ncsi_rsp_handler_gp }, { NCSI_PKT_RSP_GCPS, 172, NULL }, { NCSI_PKT_RSP_GNS, 172, NULL }, { NCSI_PKT_RSP_GNPTS, 172, NULL }, @@ -87,6 +118,9 @@ void ncsi_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) (ncsi_reply + ETH_HLEN); const struct ncsi_rsp_handler *handler = NULL; int i; + int ncsi_rsp_len = sizeof(*nh); + uint32_t checksum; + uint32_t *pchecksum; memset(ncsi_reply, 0, sizeof(ncsi_reply)); @@ -116,15 +150,18 @@ void ncsi_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) /* TODO: handle errors */ handler->handler(rnh); } + ncsi_rsp_len += handler->payload; } else { rnh->common.length = 0; rnh->code = htons(NCSI_PKT_RSP_C_UNAVAILABLE); rnh->reason = htons(NCSI_PKT_RSP_R_UNKNOWN); } - /* TODO: add a checksum at the end of the frame but the specs - * allows it to be zero */ + /* Add the optional checksum at the end of the frame. */ + checksum = ncsi_calculate_checksum((uint16_t *) rnh, ncsi_rsp_len); + pchecksum = (uint32_t *)((void *) rnh + ncsi_rsp_len); + *pchecksum = htonl(checksum); + ncsi_rsp_len += 4; - slirp_output(slirp->opaque, ncsi_reply, ETH_HLEN + sizeof(*nh) + - (handler ? handler->payload : 0) + 4); + slirp_output(slirp->opaque, ncsi_reply, ETH_HLEN + ncsi_rsp_len); } diff --git a/slirp/slirp.c b/slirp/slirp.c index 1cb6b07004..5c3bd6163f 100644 --- a/slirp/slirp.c +++ b/slirp/slirp.c @@ -286,7 +286,7 @@ Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork, const char *tftp_path, const char *bootfile, struct in_addr vdhcp_start, struct in_addr vnameserver, struct in6_addr vnameserver6, const char **vdnssearch, - void *opaque) + const char *vdomainname, void *opaque) { Slirp *slirp = g_malloc0(sizeof(Slirp)); @@ -317,6 +317,7 @@ Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork, } slirp->tftp_prefix = g_strdup(tftp_path); slirp->bootp_filename = g_strdup(bootfile); + slirp->vdomainname = g_strdup(vdomainname); slirp->vdhcp_startaddr = vdhcp_start; slirp->vnameserver_addr = vnameserver; slirp->vnameserver_addr6 = vnameserver6; @@ -349,6 +350,7 @@ void slirp_cleanup(Slirp *slirp) g_free(slirp->vdnssearch); g_free(slirp->tftp_prefix); g_free(slirp->bootp_filename); + g_free(slirp->vdomainname); g_free(slirp); } @@ -676,13 +678,13 @@ void slirp_pollfds_poll(GArray *pollfds, int select_error) /* continue; */ } else { ret = sowrite(so); + if (ret > 0) { + /* Call tcp_output in case we need to send a window + * update to the guest, otherwise it will be stuck + * until it sends a window probe. */ + tcp_output(sototcpcb(so)); + } } - /* - * XXXXX If we wrote something (a lot), there - * could be a need for a window update. - * In the worst case, the remote will send - * a window probe to get things going again - */ } /* diff --git a/slirp/slirp.h b/slirp/slirp.h index 06febfc78b..10b410898a 100644 --- a/slirp/slirp.h +++ b/slirp/slirp.h @@ -193,6 +193,7 @@ struct Slirp { char *bootp_filename; size_t vdnssearch_len; uint8_t *vdnssearch; + char *vdomainname; /* tcp states */ struct socket tcb; diff --git a/slirp/socket.c b/slirp/socket.c index 61347d1a0c..e2a71c9b04 100644 --- a/slirp/socket.c +++ b/slirp/socket.c @@ -701,9 +701,9 @@ tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, memset(&addr, 0, addrlen); DEBUG_CALL("tcp_listen"); - DEBUG_ARG("haddr = %s", inet_ntoa(*(struct in_addr *)&haddr)); + DEBUG_ARG("haddr = %s", inet_ntoa((struct in_addr){.s_addr = haddr})); DEBUG_ARG("hport = %d", ntohs(hport)); - DEBUG_ARG("laddr = %s", inet_ntoa(*(struct in_addr *)&laddr)); + DEBUG_ARG("laddr = %s", inet_ntoa((struct in_addr){.s_addr = laddr})); DEBUG_ARG("lport = %d", ntohs(lport)); DEBUG_ARG("flags = %x", flags); @@ -754,6 +754,8 @@ tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, return NULL; } qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); + opt = 1; + qemu_setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(int)); getsockname(s,(struct sockaddr *)&addr,&addrlen); so->so_ffamily = AF_INET; diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c index da0d53743f..8d0f94b75f 100644 --- a/slirp/tcp_subr.c +++ b/slirp/tcp_subr.c @@ -416,6 +416,8 @@ int tcp_fconnect(struct socket *so, unsigned short af) socket_set_fast_reuse(s); opt = 1; qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt)); + opt = 1; + qemu_setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)); addr = so->fhost.ss; DEBUG_CALL(" connect()ing") diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index e80cfb47c7..0c64c0292e 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -157,8 +157,7 @@ int arm_gen_dynamic_xml(CPUState *cs) RegisterSysregXmlParam param = {cs, s}; cpu->dyn_xml.num_cpregs = 0; - cpu->dyn_xml.cpregs_keys = g_malloc(sizeof(uint32_t *) * - g_hash_table_size(cpu->cp_regs)); + cpu->dyn_xml.cpregs_keys = g_new(uint32_t, g_hash_table_size(cpu->cp_regs)); g_string_printf(s, "<?xml version=\"1.0\"?>"); g_string_append_printf(s, "<!DOCTYPE target SYSTEM \"gdb-target.dtd\">"); g_string_append_printf(s, "<feature name=\"org.qemu.gdb.arm.sys.regs\">"); diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c index f92bdea732..7f6ad3000b 100644 --- a/target/arm/helper-a64.c +++ b/target/arm/helper-a64.c @@ -85,12 +85,12 @@ static inline uint32_t float_rel_to_flags(int res) return flags; } -uint64_t HELPER(vfp_cmph_a64)(float16 x, float16 y, void *fp_status) +uint64_t HELPER(vfp_cmph_a64)(uint32_t x, uint32_t y, void *fp_status) { return float_rel_to_flags(float16_compare_quiet(x, y, fp_status)); } -uint64_t HELPER(vfp_cmpeh_a64)(float16 x, float16 y, void *fp_status) +uint64_t HELPER(vfp_cmpeh_a64)(uint32_t x, uint32_t y, void *fp_status) { return float_rel_to_flags(float16_compare(x, y, fp_status)); } @@ -214,7 +214,7 @@ uint64_t HELPER(neon_cgt_f64)(float64 a, float64 b, void *fpstp) #define float64_three make_float64(0x4008000000000000ULL) #define float64_one_point_five make_float64(0x3FF8000000000000ULL) -float16 HELPER(recpsf_f16)(float16 a, float16 b, void *fpstp) +uint32_t HELPER(recpsf_f16)(uint32_t a, uint32_t b, void *fpstp) { float_status *fpst = fpstp; @@ -259,7 +259,7 @@ float64 HELPER(recpsf_f64)(float64 a, float64 b, void *fpstp) return float64_muladd(a, b, float64_two, 0, fpst); } -float16 HELPER(rsqrtsf_f16)(float16 a, float16 b, void *fpstp) +uint32_t HELPER(rsqrtsf_f16)(uint32_t a, uint32_t b, void *fpstp) { float_status *fpst = fpstp; @@ -366,7 +366,7 @@ uint64_t HELPER(neon_addlp_u16)(uint64_t a) } /* Floating-point reciprocal exponent - see FPRecpX in ARM ARM */ -float16 HELPER(frecpx_f16)(float16 a, void *fpstp) +uint32_t HELPER(frecpx_f16)(uint32_t a, void *fpstp) { float_status *fpst = fpstp; uint16_t val16, sbit; @@ -384,6 +384,8 @@ float16 HELPER(frecpx_f16)(float16 a, void *fpstp) return nan; } + a = float16_squash_input_denormal(a, fpst); + val16 = float16_val(a); sbit = 0x8000 & val16; exp = extract32(val16, 10, 5); @@ -413,6 +415,8 @@ float32 HELPER(frecpx_f32)(float32 a, void *fpstp) return nan; } + a = float32_squash_input_denormal(a, fpst); + val32 = float32_val(a); sbit = 0x80000000ULL & val32; exp = extract32(val32, 23, 8); @@ -442,6 +446,8 @@ float64 HELPER(frecpx_f64)(float64 a, void *fpstp) return nan; } + a = float64_squash_input_denormal(a, fpst); + val64 = float64_val(a); sbit = 0x8000000000000000ULL & val64; exp = extract64(float64_val(a), 52, 11); @@ -695,7 +701,7 @@ void HELPER(casp_be_parallel)(CPUARMState *env, uint32_t rs, uint64_t addr, #define ADVSIMD_HELPER(name, suffix) HELPER(glue(glue(advsimd_, name), suffix)) #define ADVSIMD_HALFOP(name) \ -float16 ADVSIMD_HELPER(name, h)(float16 a, float16 b, void *fpstp) \ +uint32_t ADVSIMD_HELPER(name, h)(uint32_t a, uint32_t b, void *fpstp) \ { \ float_status *fpst = fpstp; \ return float16_ ## name(a, b, fpst); \ @@ -755,7 +761,8 @@ ADVSIMD_HALFOP(mulx) ADVSIMD_TWOHALFOP(mulx) /* fused multiply-accumulate */ -float16 HELPER(advsimd_muladdh)(float16 a, float16 b, float16 c, void *fpstp) +uint32_t HELPER(advsimd_muladdh)(uint32_t a, uint32_t b, uint32_t c, + void *fpstp) { float_status *fpst = fpstp; return float16_muladd(a, b, c, 0, fpst); @@ -786,14 +793,14 @@ uint32_t HELPER(advsimd_muladd2h)(uint32_t two_a, uint32_t two_b, #define ADVSIMD_CMPRES(test) (test) ? 0xffff : 0 -uint32_t HELPER(advsimd_ceq_f16)(float16 a, float16 b, void *fpstp) +uint32_t HELPER(advsimd_ceq_f16)(uint32_t a, uint32_t b, void *fpstp) { float_status *fpst = fpstp; int compare = float16_compare_quiet(a, b, fpst); return ADVSIMD_CMPRES(compare == float_relation_equal); } -uint32_t HELPER(advsimd_cge_f16)(float16 a, float16 b, void *fpstp) +uint32_t HELPER(advsimd_cge_f16)(uint32_t a, uint32_t b, void *fpstp) { float_status *fpst = fpstp; int compare = float16_compare(a, b, fpst); @@ -801,14 +808,14 @@ uint32_t HELPER(advsimd_cge_f16)(float16 a, float16 b, void *fpstp) compare == float_relation_equal); } -uint32_t HELPER(advsimd_cgt_f16)(float16 a, float16 b, void *fpstp) +uint32_t HELPER(advsimd_cgt_f16)(uint32_t a, uint32_t b, void *fpstp) { float_status *fpst = fpstp; int compare = float16_compare(a, b, fpst); return ADVSIMD_CMPRES(compare == float_relation_greater); } -uint32_t HELPER(advsimd_acge_f16)(float16 a, float16 b, void *fpstp) +uint32_t HELPER(advsimd_acge_f16)(uint32_t a, uint32_t b, void *fpstp) { float_status *fpst = fpstp; float16 f0 = float16_abs(a); @@ -818,7 +825,7 @@ uint32_t HELPER(advsimd_acge_f16)(float16 a, float16 b, void *fpstp) compare == float_relation_equal); } -uint32_t HELPER(advsimd_acgt_f16)(float16 a, float16 b, void *fpstp) +uint32_t HELPER(advsimd_acgt_f16)(uint32_t a, uint32_t b, void *fpstp) { float_status *fpst = fpstp; float16 f0 = float16_abs(a); @@ -828,12 +835,12 @@ uint32_t HELPER(advsimd_acgt_f16)(float16 a, float16 b, void *fpstp) } /* round to integral */ -float16 HELPER(advsimd_rinth_exact)(float16 x, void *fp_status) +uint32_t HELPER(advsimd_rinth_exact)(uint32_t x, void *fp_status) { return float16_round_to_int(x, fp_status); } -float16 HELPER(advsimd_rinth)(float16 x, void *fp_status) +uint32_t HELPER(advsimd_rinth)(uint32_t x, void *fp_status) { int old_flags = get_float_exception_flags(fp_status), new_flags; float16 ret; @@ -857,7 +864,7 @@ float16 HELPER(advsimd_rinth)(float16 x, void *fp_status) * setting the mode appropriately before calling the helper. */ -uint32_t HELPER(advsimd_f16tosinth)(float16 a, void *fpstp) +uint32_t HELPER(advsimd_f16tosinth)(uint32_t a, void *fpstp) { float_status *fpst = fpstp; @@ -869,7 +876,7 @@ uint32_t HELPER(advsimd_f16tosinth)(float16 a, void *fpstp) return float16_to_int16(a, fpst); } -uint32_t HELPER(advsimd_f16touinth)(float16 a, void *fpstp) +uint32_t HELPER(advsimd_f16touinth)(uint32_t a, void *fpstp) { float_status *fpst = fpstp; @@ -885,7 +892,7 @@ uint32_t HELPER(advsimd_f16touinth)(float16 a, void *fpstp) * Square Root and Reciprocal square root */ -float16 HELPER(sqrt_f16)(float16 a, void *fpstp) +uint32_t HELPER(sqrt_f16)(uint32_t a, void *fpstp) { float_status *s = fpstp; diff --git a/target/arm/helper.c b/target/arm/helper.c index c0f739972e..f75aa6e9ca 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -863,6 +863,14 @@ static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, env->cp15.cpacr_el1 = value; } +static void cpacr_reset(CPUARMState *env, const ARMCPRegInfo *ri) +{ + /* Call cpacr_write() so that we reset with the correct RAO bits set + * for our CPU features. + */ + cpacr_write(env, ri, 0); +} + static CPAccessResult cpacr_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { @@ -920,7 +928,7 @@ static const ARMCPRegInfo v6_cp_reginfo[] = { { .name = "CPACR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 2, .accessfn = cpacr_access, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.cpacr_el1), - .resetvalue = 0, .writefn = cpacr_write }, + .resetfn = cpacr_reset, .writefn = cpacr_write }, REGINFO_SENTINEL }; @@ -11344,35 +11352,35 @@ DO_VFP_cmp(d, float64) /* Integer to float and float to integer conversions */ -#define CONV_ITOF(name, fsz, sign) \ - float##fsz HELPER(name)(uint32_t x, void *fpstp) \ -{ \ - float_status *fpst = fpstp; \ - return sign##int32_to_##float##fsz((sign##int32_t)x, fpst); \ +#define CONV_ITOF(name, ftype, fsz, sign) \ +ftype HELPER(name)(uint32_t x, void *fpstp) \ +{ \ + float_status *fpst = fpstp; \ + return sign##int32_to_##float##fsz((sign##int32_t)x, fpst); \ } -#define CONV_FTOI(name, fsz, sign, round) \ -uint32_t HELPER(name)(float##fsz x, void *fpstp) \ -{ \ - float_status *fpst = fpstp; \ - if (float##fsz##_is_any_nan(x)) { \ - float_raise(float_flag_invalid, fpst); \ - return 0; \ - } \ - return float##fsz##_to_##sign##int32##round(x, fpst); \ +#define CONV_FTOI(name, ftype, fsz, sign, round) \ +uint32_t HELPER(name)(ftype x, void *fpstp) \ +{ \ + float_status *fpst = fpstp; \ + if (float##fsz##_is_any_nan(x)) { \ + float_raise(float_flag_invalid, fpst); \ + return 0; \ + } \ + return float##fsz##_to_##sign##int32##round(x, fpst); \ } -#define FLOAT_CONVS(name, p, fsz, sign) \ -CONV_ITOF(vfp_##name##to##p, fsz, sign) \ -CONV_FTOI(vfp_to##name##p, fsz, sign, ) \ -CONV_FTOI(vfp_to##name##z##p, fsz, sign, _round_to_zero) +#define FLOAT_CONVS(name, p, ftype, fsz, sign) \ + CONV_ITOF(vfp_##name##to##p, ftype, fsz, sign) \ + CONV_FTOI(vfp_to##name##p, ftype, fsz, sign, ) \ + CONV_FTOI(vfp_to##name##z##p, ftype, fsz, sign, _round_to_zero) -FLOAT_CONVS(si, h, 16, ) -FLOAT_CONVS(si, s, 32, ) -FLOAT_CONVS(si, d, 64, ) -FLOAT_CONVS(ui, h, 16, u) -FLOAT_CONVS(ui, s, 32, u) -FLOAT_CONVS(ui, d, 64, u) +FLOAT_CONVS(si, h, uint32_t, 16, ) +FLOAT_CONVS(si, s, float32, 32, ) +FLOAT_CONVS(si, d, float64, 64, ) +FLOAT_CONVS(ui, h, uint32_t, 16, u) +FLOAT_CONVS(ui, s, float32, 32, u) +FLOAT_CONVS(ui, d, float64, 64, u) #undef CONV_ITOF #undef CONV_FTOI @@ -11465,22 +11473,22 @@ static float16 do_postscale_fp16(float64 f, int shift, float_status *fpst) return float64_to_float16(float64_scalbn(f, -shift, fpst), true, fpst); } -float16 HELPER(vfp_sltoh)(uint32_t x, uint32_t shift, void *fpst) +uint32_t HELPER(vfp_sltoh)(uint32_t x, uint32_t shift, void *fpst) { return do_postscale_fp16(int32_to_float64(x, fpst), shift, fpst); } -float16 HELPER(vfp_ultoh)(uint32_t x, uint32_t shift, void *fpst) +uint32_t HELPER(vfp_ultoh)(uint32_t x, uint32_t shift, void *fpst) { return do_postscale_fp16(uint32_to_float64(x, fpst), shift, fpst); } -float16 HELPER(vfp_sqtoh)(uint64_t x, uint32_t shift, void *fpst) +uint32_t HELPER(vfp_sqtoh)(uint64_t x, uint32_t shift, void *fpst) { return do_postscale_fp16(int64_to_float64(x, fpst), shift, fpst); } -float16 HELPER(vfp_uqtoh)(uint64_t x, uint32_t shift, void *fpst) +uint32_t HELPER(vfp_uqtoh)(uint64_t x, uint32_t shift, void *fpst) { return do_postscale_fp16(uint64_to_float64(x, fpst), shift, fpst); } @@ -11504,32 +11512,32 @@ static float64 do_prescale_fp16(float16 f, int shift, float_status *fpst) } } -uint32_t HELPER(vfp_toshh)(float16 x, uint32_t shift, void *fpst) +uint32_t HELPER(vfp_toshh)(uint32_t x, uint32_t shift, void *fpst) { return float64_to_int16(do_prescale_fp16(x, shift, fpst), fpst); } -uint32_t HELPER(vfp_touhh)(float16 x, uint32_t shift, void *fpst) +uint32_t HELPER(vfp_touhh)(uint32_t x, uint32_t shift, void *fpst) { return float64_to_uint16(do_prescale_fp16(x, shift, fpst), fpst); } -uint32_t HELPER(vfp_toslh)(float16 x, uint32_t shift, void *fpst) +uint32_t HELPER(vfp_toslh)(uint32_t x, uint32_t shift, void *fpst) { return float64_to_int32(do_prescale_fp16(x, shift, fpst), fpst); } -uint32_t HELPER(vfp_toulh)(float16 x, uint32_t shift, void *fpst) +uint32_t HELPER(vfp_toulh)(uint32_t x, uint32_t shift, void *fpst) { return float64_to_uint32(do_prescale_fp16(x, shift, fpst), fpst); } -uint64_t HELPER(vfp_tosqh)(float16 x, uint32_t shift, void *fpst) +uint64_t HELPER(vfp_tosqh)(uint32_t x, uint32_t shift, void *fpst) { return float64_to_int64(do_prescale_fp16(x, shift, fpst), fpst); } -uint64_t HELPER(vfp_touqh)(float16 x, uint32_t shift, void *fpst) +uint64_t HELPER(vfp_touqh)(uint32_t x, uint32_t shift, void *fpst) { return float64_to_uint64(do_prescale_fp16(x, shift, fpst), fpst); } @@ -11565,7 +11573,7 @@ uint32_t HELPER(set_neon_rmode)(uint32_t rmode, CPUARMState *env) } /* Half precision conversions. */ -float32 HELPER(vfp_fcvt_f16_to_f32)(float16 a, void *fpstp, uint32_t ahp_mode) +float32 HELPER(vfp_fcvt_f16_to_f32)(uint32_t a, void *fpstp, uint32_t ahp_mode) { /* Squash FZ16 to 0 for the duration of conversion. In this case, * it would affect flushing input denormals. @@ -11578,7 +11586,7 @@ float32 HELPER(vfp_fcvt_f16_to_f32)(float16 a, void *fpstp, uint32_t ahp_mode) return r; } -float16 HELPER(vfp_fcvt_f32_to_f16)(float32 a, void *fpstp, uint32_t ahp_mode) +uint32_t HELPER(vfp_fcvt_f32_to_f16)(float32 a, void *fpstp, uint32_t ahp_mode) { /* Squash FZ16 to 0 for the duration of conversion. In this case, * it would affect flushing output denormals. @@ -11591,7 +11599,7 @@ float16 HELPER(vfp_fcvt_f32_to_f16)(float32 a, void *fpstp, uint32_t ahp_mode) return r; } -float64 HELPER(vfp_fcvt_f16_to_f64)(float16 a, void *fpstp, uint32_t ahp_mode) +float64 HELPER(vfp_fcvt_f16_to_f64)(uint32_t a, void *fpstp, uint32_t ahp_mode) { /* Squash FZ16 to 0 for the duration of conversion. In this case, * it would affect flushing input denormals. @@ -11604,7 +11612,7 @@ float64 HELPER(vfp_fcvt_f16_to_f64)(float16 a, void *fpstp, uint32_t ahp_mode) return r; } -float16 HELPER(vfp_fcvt_f64_to_f16)(float64 a, void *fpstp, uint32_t ahp_mode) +uint32_t HELPER(vfp_fcvt_f64_to_f16)(float64 a, void *fpstp, uint32_t ahp_mode) { /* Squash FZ16 to 0 for the duration of conversion. In this case, * it would affect flushing output denormals. @@ -11742,7 +11750,7 @@ static bool round_to_inf(float_status *fpst, bool sign_bit) g_assert_not_reached(); } -float16 HELPER(recpe_f16)(float16 input, void *fpstp) +uint32_t HELPER(recpe_f16)(uint32_t input, void *fpstp) { float_status *fpst = fpstp; float16 f16 = float16_squash_input_denormal(input, fpst); @@ -11937,7 +11945,7 @@ static uint64_t recip_sqrt_estimate(int *exp , int exp_off, uint64_t frac) return extract64(estimate, 0, 8) << 44; } -float16 HELPER(rsqrte_f16)(float16 input, void *fpstp) +uint32_t HELPER(rsqrte_f16)(uint32_t input, void *fpstp) { float_status *s = fpstp; float16 f16 = float16_squash_input_denormal(input, s); diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 5141d0adc5..98f5006323 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -664,7 +664,8 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, /* MSI doorbell address is translated by an IOMMU */ rcu_read_lock(); - mr = address_space_translate(as, address, &xlat, &len, true); + mr = address_space_translate(as, address, &xlat, &len, true, + MEMTXATTRS_UNSPECIFIED); if (!mr) { goto unlock; } diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 4dc1404800..9b546a2c18 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -72,6 +72,9 @@ static const struct { {NULL, 0}, }; +/* If no specific version gets selected, default to the following. */ +#define DEFAULT_CPU_VERSION "10.0" + static void mb_cpu_set_pc(CPUState *cs, vaddr value) { MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); @@ -125,6 +128,7 @@ static void mb_cpu_reset(CPUState *s) env->mmu.c_mmu = 3; env->mmu.c_mmu_tlb_access = 3; env->mmu.c_mmu_zones = 16; + env->mmu.c_addr_mask = MAKE_64BIT_MASK(0, cpu->cfg.addr_size); #endif } @@ -141,6 +145,7 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); CPUMBState *env = &cpu->env; uint8_t version_code = 0; + const char *version; int i = 0; Error *local_err = NULL; @@ -150,6 +155,12 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) return; } + if (cpu->cfg.addr_size < 32 || cpu->cfg.addr_size > 64) { + error_setg(errp, "addr-size %d is out of range (32 - 64)", + cpu->cfg.addr_size); + return; + } + qemu_init_vcpu(cs); env->pvr.regs[0] = PVR0_USE_EXC_MASK \ @@ -162,8 +173,9 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) | PVR2_FPU_EXC_MASK \ | 0; - for (i = 0; mb_cpu_lookup[i].name && cpu->cfg.version; i++) { - if (strcmp(mb_cpu_lookup[i].name, cpu->cfg.version) == 0) { + version = cpu->cfg.version ? cpu->cfg.version : DEFAULT_CPU_VERSION; + for (i = 0; mb_cpu_lookup[i].name && version; i++) { + if (strcmp(mb_cpu_lookup[i].name, version) == 0) { version_code = mb_cpu_lookup[i].version_id; break; } @@ -195,8 +207,10 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) env->pvr.regs[5] |= cpu->cfg.dcache_writeback ? PVR5_DCACHE_WRITEBACK_MASK : 0; - env->pvr.regs[10] = 0x0c000000; /* Default to spartan 3a dsp family. */ - env->pvr.regs[11] = PVR11_USE_MMU | (16 << 17); + env->pvr.regs[10] = 0x0c000000 | /* Default to spartan 3a dsp family. */ + (cpu->cfg.addr_size - 32) << PVR10_ASIZE_SHIFT; + env->pvr.regs[11] = (cpu->cfg.use_mmu ? PVR11_USE_MMU : 0) | + 16 << 17; mcc->parent_realize(dev, errp); } @@ -226,6 +240,14 @@ static Property mb_properties[] = { DEFINE_PROP_UINT32("base-vectors", MicroBlazeCPU, cfg.base_vectors, 0), DEFINE_PROP_BOOL("use-stack-protection", MicroBlazeCPU, cfg.stackprot, false), + /* + * This is the C_ADDR_SIZE synth-time configuration option of the + * MicroBlaze cores. Supported values range between 32 and 64. + * + * When set to > 32, 32bit MicroBlaze can emit load/stores + * with extended addressing. + */ + DEFINE_PROP_UINT8("addr-size", MicroBlazeCPU, cfg.addr_size, 32), /* If use-fpu > 0 - FPU is enabled * If use-fpu = 2 - Floating point conversion and square root instructions * are enabled diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index 5be71bc320..3c4e0ba80a 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -23,7 +23,7 @@ #include "qemu-common.h" #include "cpu-qom.h" -#define TARGET_LONG_BITS 32 +#define TARGET_LONG_BITS 64 #define CPUArchState struct CPUMBState @@ -203,6 +203,7 @@ typedef struct CPUMBState CPUMBState; /* Target family PVR mask */ #define PVR10_TARGET_FAMILY_MASK 0xFF000000 +#define PVR10_ASIZE_SHIFT 18 /* MMU descrtiption */ #define PVR11_USE_MMU 0xC0000000 @@ -238,19 +239,19 @@ typedef struct CPUMBState CPUMBState; struct CPUMBState { uint32_t debug; uint32_t btaken; - uint32_t btarget; + uint64_t btarget; uint32_t bimm; uint32_t imm; - uint32_t regs[33]; - uint32_t sregs[24]; + uint32_t regs[32]; + uint64_t sregs[14]; float_status fp_status; /* Stack protectors. Yes, it's a hw feature. */ uint32_t slr, shr; /* lwx/swx reserved address */ #define RES_ADDR_NONE 0xffffffff /* Use 0xffffffff to indicate no reservation */ - uint32_t res_addr; + target_ulong res_addr; uint32_t res_val; /* Internal flags. */ @@ -277,7 +278,7 @@ struct CPUMBState { /* These fields are preserved on reset. */ struct { - uint32_t regs[16]; + uint32_t regs[13]; } pvr; }; @@ -297,6 +298,7 @@ struct MicroBlazeCPU { struct { bool stackprot; uint32_t base_vectors; + uint8_t addr_size; uint8_t use_fpu; uint8_t use_hw_mul; bool use_barrel; @@ -340,8 +342,8 @@ int cpu_mb_signal_handler(int host_signum, void *pinfo, /* FIXME: MB uses variable pages down to 1K but linux only uses 4k. */ #define TARGET_PAGE_BITS 12 -#define TARGET_PHYS_ADDR_SPACE_BITS 32 -#define TARGET_VIRT_ADDR_SPACE_BITS 32 +#define TARGET_PHYS_ADDR_SPACE_BITS 64 +#define TARGET_VIRT_ADDR_SPACE_BITS 64 #define CPU_RESOLVING_TYPE TYPE_MICROBLAZE_CPU @@ -358,13 +360,17 @@ int cpu_mb_signal_handler(int host_signum, void *pinfo, static inline int cpu_mmu_index (CPUMBState *env, bool ifetch) { - /* Are we in nommu mode?. */ - if (!(env->sregs[SR_MSR] & MSR_VM)) - return MMU_NOMMU_IDX; + MicroBlazeCPU *cpu = mb_env_get_cpu(env); - if (env->sregs[SR_MSR] & MSR_UM) - return MMU_USER_IDX; - return MMU_KERNEL_IDX; + /* Are we in nommu mode?. */ + if (!(env->sregs[SR_MSR] & MSR_VM) || !cpu->cfg.use_mmu) { + return MMU_NOMMU_IDX; + } + + if (env->sregs[SR_MSR] & MSR_UM) { + return MMU_USER_IDX; + } + return MMU_KERNEL_IDX; } int mb_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size, int rw, diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c index fac6ee9263..bc753793ec 100644 --- a/target/microblaze/helper.c +++ b/target/microblaze/helper.c @@ -54,22 +54,12 @@ int mb_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw, MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); CPUMBState *env = &cpu->env; unsigned int hit; - unsigned int mmu_available; int r = 1; int prot; - mmu_available = 0; - if (cpu->cfg.use_mmu) { - mmu_available = 1; - if ((cpu->cfg.pvr == C_PVR_FULL) && - (env->pvr.regs[11] & PVR11_USE_MMU) != PVR11_USE_MMU) { - mmu_available = 0; - } - } - /* Translate if the MMU is available and enabled. */ - if (mmu_available && (env->sregs[SR_MSR] & MSR_VM)) { - target_ulong vaddr, paddr; + if (mmu_idx != MMU_NOMMU_IDX) { + uint32_t vaddr, paddr; struct microblaze_mmu_lookup lu; hit = mmu_translate(&env->mmu, &lu, address, rw, mmu_idx); @@ -152,7 +142,8 @@ void mb_cpu_do_interrupt(CPUState *cs) env->sregs[SR_MSR] |= MSR_EIP; qemu_log_mask(CPU_LOG_INT, - "hw exception at pc=%x ear=%x esr=%x iflags=%x\n", + "hw exception at pc=%" PRIx64 " ear=%" PRIx64 " " + "esr=%" PRIx64 " iflags=%x\n", env->sregs[SR_PC], env->sregs[SR_EAR], env->sregs[SR_ESR], env->iflags); log_cpu_state_mask(CPU_LOG_INT, cs, 0); @@ -175,7 +166,8 @@ void mb_cpu_do_interrupt(CPUState *cs) /* was the branch immprefixed?. */ if (env->bimm) { qemu_log_mask(CPU_LOG_INT, - "bimm exception at pc=%x iflags=%x\n", + "bimm exception at pc=%" PRIx64 " " + "iflags=%x\n", env->sregs[SR_PC], env->iflags); env->regs[17] -= 4; log_cpu_state_mask(CPU_LOG_INT, cs, 0); @@ -193,7 +185,8 @@ void mb_cpu_do_interrupt(CPUState *cs) env->sregs[SR_MSR] |= MSR_EIP; qemu_log_mask(CPU_LOG_INT, - "exception at pc=%x ear=%x iflags=%x\n", + "exception at pc=%" PRIx64 " ear=%" PRIx64 " " + "iflags=%x\n", env->sregs[SR_PC], env->sregs[SR_EAR], env->iflags); log_cpu_state_mask(CPU_LOG_INT, cs, 0); env->iflags &= ~(IMM_FLAG | D_FLAG); @@ -230,7 +223,8 @@ void mb_cpu_do_interrupt(CPUState *cs) } #endif qemu_log_mask(CPU_LOG_INT, - "interrupt at pc=%x msr=%x %x iflags=%x\n", + "interrupt at pc=%" PRIx64 " msr=%" PRIx64 " %x " + "iflags=%x\n", env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags); env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM \ @@ -248,7 +242,8 @@ void mb_cpu_do_interrupt(CPUState *cs) assert(!(env->iflags & D_FLAG)); t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; qemu_log_mask(CPU_LOG_INT, - "break at pc=%x msr=%x %x iflags=%x\n", + "break at pc=%" PRIx64 " msr=%" PRIx64 " %x " + "iflags=%x\n", env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags); log_cpu_state_mask(CPU_LOG_INT, cs, 0); env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); @@ -274,9 +269,10 @@ hwaddr mb_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) CPUMBState *env = &cpu->env; target_ulong vaddr, paddr = 0; struct microblaze_mmu_lookup lu; + int mmu_idx = cpu_mmu_index(env, false); unsigned int hit; - if (env->sregs[SR_MSR] & MSR_VM) { + if (mmu_idx != MMU_NOMMU_IDX) { hit = mmu_translate(&env->mmu, &lu, addr, 0, 0); if (hit) { vaddr = addr & TARGET_PAGE_MASK; diff --git a/target/microblaze/helper.h b/target/microblaze/helper.h index 71a6c0858d..2f8bdea22b 100644 --- a/target/microblaze/helper.h +++ b/target/microblaze/helper.h @@ -25,12 +25,12 @@ DEF_HELPER_3(fcmp_ge, i32, env, i32, i32) DEF_HELPER_FLAGS_2(pcmpbf, TCG_CALL_NO_RWG_SE, i32, i32, i32) #if !defined(CONFIG_USER_ONLY) -DEF_HELPER_2(mmu_read, i32, env, i32) -DEF_HELPER_3(mmu_write, void, env, i32, i32) +DEF_HELPER_3(mmu_read, i32, env, i32, i32) +DEF_HELPER_4(mmu_write, void, env, i32, i32, i32) #endif -DEF_HELPER_5(memalign, void, env, i32, i32, i32, i32) -DEF_HELPER_2(stackprot, void, env, i32) +DEF_HELPER_5(memalign, void, env, tl, i32, i32, i32) +DEF_HELPER_2(stackprot, void, env, tl) DEF_HELPER_2(get, i32, i32, i32) DEF_HELPER_3(put, void, i32, i32, i32) diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c index 9d5e6aa8a5..f4ceaea520 100644 --- a/target/microblaze/mmu.c +++ b/target/microblaze/mmu.c @@ -22,8 +22,6 @@ #include "cpu.h" #include "exec/exec-all.h" -#define D(x) - static unsigned int tlb_decode_size(unsigned int f) { static const unsigned int sizes[] = { @@ -81,34 +79,29 @@ unsigned int mmu_translate(struct microblaze_mmu *mmu, { unsigned int i, hit = 0; unsigned int tlb_ex = 0, tlb_wr = 0, tlb_zsel; - unsigned int tlb_size; - uint32_t tlb_tag, tlb_rpn, mask, t0; + uint64_t tlb_tag, tlb_rpn, mask; + uint32_t tlb_size, t0; lu->err = ERR_MISS; for (i = 0; i < ARRAY_SIZE(mmu->rams[RAM_TAG]); i++) { - uint32_t t, d; + uint64_t t, d; /* Lookup and decode. */ t = mmu->rams[RAM_TAG][i]; - D(qemu_log("TLB %d valid=%d\n", i, t & TLB_VALID)); if (t & TLB_VALID) { tlb_size = tlb_decode_size((t & TLB_PAGESZ_MASK) >> 7); if (tlb_size < TARGET_PAGE_SIZE) { - qemu_log("%d pages not supported\n", tlb_size); + qemu_log_mask(LOG_UNIMP, "%d pages not supported\n", tlb_size); abort(); } - mask = ~(tlb_size - 1); + mask = ~((uint64_t)tlb_size - 1); tlb_tag = t & TLB_EPN_MASK; if ((vaddr & mask) != (tlb_tag & mask)) { - D(qemu_log("TLB %d vaddr=%x != tag=%x\n", - i, vaddr & mask, tlb_tag & mask)); continue; } if (mmu->tids[i] && ((mmu->regs[MMU_R_PID] & 0xff) != mmu->tids[i])) { - D(qemu_log("TLB %d pid=%x != tid=%x\n", - i, mmu->regs[MMU_R_PID], mmu->tids[i])); continue; } @@ -123,7 +116,8 @@ unsigned int mmu_translate(struct microblaze_mmu *mmu, t0 &= 0x3; if (tlb_zsel > mmu->c_mmu_zones) { - qemu_log_mask(LOG_GUEST_ERROR, "tlb zone select out of range! %d\n", tlb_zsel); + qemu_log_mask(LOG_GUEST_ERROR, + "tlb zone select out of range! %d\n", tlb_zsel); t0 = 1; /* Ignore. */ } @@ -164,6 +158,7 @@ unsigned int mmu_translate(struct microblaze_mmu *mmu, tlb_rpn = d & TLB_RPN_MASK; lu->vaddr = tlb_tag; + lu->paddr = tlb_rpn & mmu->c_addr_mask; lu->paddr = tlb_rpn; lu->size = tlb_size; lu->err = ERR_HIT; @@ -173,13 +168,14 @@ unsigned int mmu_translate(struct microblaze_mmu *mmu, } } done: - D(qemu_log("MMU vaddr=%x rw=%d tlb_wr=%d tlb_ex=%d hit=%d\n", - vaddr, rw, tlb_wr, tlb_ex, hit)); + qemu_log_mask(CPU_LOG_MMU, + "MMU vaddr=%" PRIx64 " rw=%d tlb_wr=%d tlb_ex=%d hit=%d\n", + vaddr, rw, tlb_wr, tlb_ex, hit); return hit; } /* Writes/reads to the MMU's special regs end up here. */ -uint32_t mmu_read(CPUMBState *env, uint32_t rn) +uint32_t mmu_read(CPUMBState *env, bool ext, uint32_t rn) { unsigned int i; uint32_t r = 0; @@ -188,50 +184,65 @@ uint32_t mmu_read(CPUMBState *env, uint32_t rn) qemu_log_mask(LOG_GUEST_ERROR, "MMU access on MMU-less system\n"); return 0; } + if (ext && rn != MMU_R_TLBLO) { + qemu_log_mask(LOG_GUEST_ERROR, "Extended access only to TLBLO.\n"); + return 0; + } switch (rn) { /* Reads to HI/LO trig reads from the mmu rams. */ case MMU_R_TLBLO: case MMU_R_TLBHI: if (!(env->mmu.c_mmu_tlb_access & 1)) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid access to MMU reg %d\n", rn); return 0; } i = env->mmu.regs[MMU_R_TLBX] & 0xff; - r = env->mmu.rams[rn & 1][i]; + r = extract64(env->mmu.rams[rn & 1][i], ext * 32, 32); if (rn == MMU_R_TLBHI) env->mmu.regs[MMU_R_PID] = env->mmu.tids[i]; break; case MMU_R_PID: case MMU_R_ZPR: if (!(env->mmu.c_mmu_tlb_access & 1)) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid access to MMU reg %d\n", rn); return 0; } r = env->mmu.regs[rn]; break; + case MMU_R_TLBX: + r = env->mmu.regs[rn]; + break; case MMU_R_TLBSX: qemu_log_mask(LOG_GUEST_ERROR, "TLBSX is write-only.\n"); break; default: - r = env->mmu.regs[rn]; + qemu_log_mask(LOG_GUEST_ERROR, "Invalid MMU register %d.\n", rn); break; } - D(qemu_log("%s rn=%d=%x\n", __func__, rn, r)); + qemu_log_mask(CPU_LOG_MMU, "%s rn=%d=%x\n", __func__, rn, r); return r; } -void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) +void mmu_write(CPUMBState *env, bool ext, uint32_t rn, uint32_t v) { MicroBlazeCPU *cpu = mb_env_get_cpu(env); + uint64_t tmp64; unsigned int i; - D(qemu_log("%s rn=%d=%x old=%x\n", __func__, rn, v, env->mmu.regs[rn])); + qemu_log_mask(CPU_LOG_MMU, + "%s rn=%d=%x old=%x\n", __func__, rn, v, env->mmu.regs[rn]); if (env->mmu.c_mmu < 2 || !env->mmu.c_mmu_tlb_access) { qemu_log_mask(LOG_GUEST_ERROR, "MMU access on MMU-less system\n"); return; } + if (ext && rn != MMU_R_TLBLO) { + qemu_log_mask(LOG_GUEST_ERROR, "Extended access only to TLBLO.\n"); + return; + } switch (rn) { /* Writes to HI/LO trig writes to the mmu rams. */ @@ -240,18 +251,19 @@ void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) i = env->mmu.regs[MMU_R_TLBX] & 0xff; if (rn == MMU_R_TLBHI) { if (i < 3 && !(v & TLB_VALID) && qemu_loglevel_mask(~0)) - qemu_log_mask(LOG_GUEST_ERROR, "invalidating index %x at pc=%x\n", + qemu_log_mask(LOG_GUEST_ERROR, + "invalidating index %x at pc=%" PRIx64 "\n", i, env->sregs[SR_PC]); env->mmu.tids[i] = env->mmu.regs[MMU_R_PID] & 0xff; mmu_flush_idx(env, i); } - env->mmu.rams[rn & 1][i] = v; - - D(qemu_log("%s ram[%d][%d]=%x\n", __func__, rn & 1, i, v)); + tmp64 = env->mmu.rams[rn & 1][i]; + env->mmu.rams[rn & 1][i] = deposit64(tmp64, ext * 32, 32, v); break; case MMU_R_ZPR: if (env->mmu.c_mmu_tlb_access <= 1) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid access to MMU reg %d\n", rn); return; } @@ -264,7 +276,8 @@ void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) break; case MMU_R_PID: if (env->mmu.c_mmu_tlb_access <= 1) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid access to MMU reg %d\n", rn); return; } @@ -283,7 +296,8 @@ void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) int hit; if (env->mmu.c_mmu_tlb_access <= 1) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid access to MMU reg %d\n", rn); return; } @@ -291,12 +305,13 @@ void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) v & TLB_EPN_MASK, 0, cpu_mmu_index(env, false)); if (hit) { env->mmu.regs[MMU_R_TLBX] = lu.idx; - } else - env->mmu.regs[MMU_R_TLBX] |= 0x80000000; + } else { + env->mmu.regs[MMU_R_TLBX] |= R_TBLX_MISS_MASK; + } break; } default: - env->mmu.regs[rn] = v; + qemu_log_mask(LOG_GUEST_ERROR, "Invalid MMU register %d.\n", rn); break; } } diff --git a/target/microblaze/mmu.h b/target/microblaze/mmu.h index 3b7a9983d5..a4272b6356 100644 --- a/target/microblaze/mmu.h +++ b/target/microblaze/mmu.h @@ -28,7 +28,7 @@ #define RAM_TAG 0 /* Tag portion */ -#define TLB_EPN_MASK 0xFFFFFC00 /* Effective Page Number */ +#define TLB_EPN_MASK MAKE_64BIT_MASK(10, 64 - 10) #define TLB_PAGESZ_MASK 0x00000380 #define TLB_PAGESZ(x) (((x) & 0x7) << 7) #define PAGESZ_1K 0 @@ -42,7 +42,7 @@ #define TLB_VALID 0x00000040 /* Entry is valid */ /* Data portion */ -#define TLB_RPN_MASK 0xFFFFFC00 /* Real Page Number */ +#define TLB_RPN_MASK MAKE_64BIT_MASK(10, 64 - 10) #define TLB_PERM_MASK 0x00000300 #define TLB_EX 0x00000200 /* Instruction execution allowed */ #define TLB_WR 0x00000100 /* Writes permitted */ @@ -54,20 +54,25 @@ #define TLB_M 0x00000002 /* Memory is coherent */ #define TLB_G 0x00000001 /* Memory is guarded from prefetch */ +/* TLBX */ +#define R_TBLX_MISS_SHIFT 31 +#define R_TBLX_MISS_MASK (1U << R_TBLX_MISS_SHIFT) + #define TLB_ENTRIES 64 struct microblaze_mmu { /* Data and tag brams. */ - uint32_t rams[2][TLB_ENTRIES]; + uint64_t rams[2][TLB_ENTRIES]; /* We keep a separate ram for the tids to avoid the 48 bit tag width. */ uint8_t tids[TLB_ENTRIES]; /* Control flops. */ - uint32_t regs[8]; + uint32_t regs[3]; int c_mmu; int c_mmu_tlb_access; int c_mmu_zones; + uint64_t c_addr_mask; /* Mask to apply to physical addresses. */ }; struct microblaze_mmu_lookup @@ -85,6 +90,6 @@ struct microblaze_mmu_lookup unsigned int mmu_translate(struct microblaze_mmu *mmu, struct microblaze_mmu_lookup *lu, target_ulong vaddr, int rw, int mmu_idx); -uint32_t mmu_read(CPUMBState *env, uint32_t rn); -void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v); +uint32_t mmu_read(CPUMBState *env, bool ea, uint32_t rn); +void mmu_write(CPUMBState *env, bool ea, uint32_t rn, uint32_t v); void mmu_init(struct microblaze_mmu *mmu); diff --git a/target/microblaze/op_helper.c b/target/microblaze/op_helper.c index 1b4fe796e7..7cdbbcccae 100644 --- a/target/microblaze/op_helper.c +++ b/target/microblaze/op_helper.c @@ -94,16 +94,17 @@ void helper_debug(CPUMBState *env) { int i; - qemu_log("PC=%8.8x\n", env->sregs[SR_PC]); - qemu_log("rmsr=%x resr=%x rear=%x debug[%x] imm=%x iflags=%x\n", + qemu_log("PC=%" PRIx64 "\n", env->sregs[SR_PC]); + qemu_log("rmsr=%" PRIx64 " resr=%" PRIx64 " rear=%" PRIx64 " " + "debug[%x] imm=%x iflags=%x\n", env->sregs[SR_MSR], env->sregs[SR_ESR], env->sregs[SR_EAR], env->debug, env->imm, env->iflags); - qemu_log("btaken=%d btarget=%x mode=%s(saved=%s) eip=%d ie=%d\n", + qemu_log("btaken=%d btarget=%" PRIx64 " mode=%s(saved=%s) eip=%d ie=%d\n", env->btaken, env->btarget, (env->sregs[SR_MSR] & MSR_UM) ? "user" : "kernel", (env->sregs[SR_MSR] & MSR_UMS) ? "user" : "kernel", - (env->sregs[SR_MSR] & MSR_EIP), - (env->sregs[SR_MSR] & MSR_IE)); + (bool)(env->sregs[SR_MSR] & MSR_EIP), + (bool)(env->sregs[SR_MSR] & MSR_IE)); for (i = 0; i < 32; i++) { qemu_log("r%2.2d=%8.8x ", i, env->regs[i]); if ((i + 1) % 4 == 0) @@ -439,12 +440,14 @@ uint32_t helper_pcmpbf(uint32_t a, uint32_t b) return 0; } -void helper_memalign(CPUMBState *env, uint32_t addr, uint32_t dr, uint32_t wr, +void helper_memalign(CPUMBState *env, target_ulong addr, + uint32_t dr, uint32_t wr, uint32_t mask) { if (addr & mask) { qemu_log_mask(CPU_LOG_INT, - "unaligned access addr=%x mask=%x, wr=%d dr=r%d\n", + "unaligned access addr=" TARGET_FMT_lx + " mask=%x, wr=%d dr=r%d\n", addr, mask, wr, dr); env->sregs[SR_EAR] = addr; env->sregs[SR_ESR] = ESR_EC_UNALIGNED_DATA | (wr << 10) \ @@ -459,10 +462,11 @@ void helper_memalign(CPUMBState *env, uint32_t addr, uint32_t dr, uint32_t wr, } } -void helper_stackprot(CPUMBState *env, uint32_t addr) +void helper_stackprot(CPUMBState *env, target_ulong addr) { if (addr < env->slr || addr > env->shr) { - qemu_log_mask(CPU_LOG_INT, "Stack protector violation at %x %x %x\n", + qemu_log_mask(CPU_LOG_INT, "Stack protector violation at " + TARGET_FMT_lx " %x %x\n", addr, env->slr, env->shr); env->sregs[SR_EAR] = addr; env->sregs[SR_ESR] = ESR_EC_STACKPROT; @@ -472,14 +476,14 @@ void helper_stackprot(CPUMBState *env, uint32_t addr) #if !defined(CONFIG_USER_ONLY) /* Writes/reads to the MMU's special regs end up here. */ -uint32_t helper_mmu_read(CPUMBState *env, uint32_t rn) +uint32_t helper_mmu_read(CPUMBState *env, uint32_t ext, uint32_t rn) { - return mmu_read(env, rn); + return mmu_read(env, ext, rn); } -void helper_mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) +void helper_mmu_write(CPUMBState *env, uint32_t ext, uint32_t rn, uint32_t v) { - mmu_write(env, rn, v); + mmu_write(env, ext, rn, v); } void mb_cpu_unassigned_access(CPUState *cs, hwaddr addr, diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 0872dc9ded..b79600cba5 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -52,22 +52,22 @@ #define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ #define DISAS_TB_JUMP DISAS_TARGET_2 /* only pc was modified statically */ -static TCGv env_debug; -static TCGv cpu_R[32]; -static TCGv cpu_SR[18]; -static TCGv env_imm; -static TCGv env_btaken; -static TCGv env_btarget; -static TCGv env_iflags; +static TCGv_i32 env_debug; +static TCGv_i32 cpu_R[32]; +static TCGv_i64 cpu_SR[14]; +static TCGv_i32 env_imm; +static TCGv_i32 env_btaken; +static TCGv_i64 env_btarget; +static TCGv_i32 env_iflags; static TCGv env_res_addr; -static TCGv env_res_val; +static TCGv_i32 env_res_val; #include "exec/gen-icount.h" /* This is the state at translation time. */ typedef struct DisasContext { MicroBlazeCPU *cpu; - target_ulong pc; + uint32_t pc; /* Decoder. */ int type_b; @@ -105,16 +105,15 @@ static const char *regnames[] = static const char *special_regnames[] = { - "rpc", "rmsr", "sr2", "sr3", "sr4", "sr5", "sr6", "sr7", - "sr8", "sr9", "sr10", "sr11", "sr12", "sr13", "sr14", "sr15", - "sr16", "sr17", "sr18" + "rpc", "rmsr", "sr2", "rear", "sr4", "resr", "sr6", "rfsr", + "sr8", "sr9", "sr10", "rbtr", "sr12", "redr" }; static inline void t_sync_flags(DisasContext *dc) { /* Synch the tb dependent flags between translator and runtime. */ if (dc->tb_flags != dc->synced_flags) { - tcg_gen_movi_tl(env_iflags, dc->tb_flags); + tcg_gen_movi_i32(env_iflags, dc->tb_flags); dc->synced_flags = dc->tb_flags; } } @@ -124,7 +123,7 @@ static inline void t_gen_raise_exception(DisasContext *dc, uint32_t index) TCGv_i32 tmp = tcg_const_i32(index); t_sync_flags(dc); - tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); + tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc); gen_helper_raise_exception(cpu_env, tmp); tcg_temp_free_i32(tmp); dc->is_jmp = DISAS_UPDATE; @@ -143,41 +142,70 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) { if (use_goto_tb(dc, dest)) { tcg_gen_goto_tb(n); - tcg_gen_movi_tl(cpu_SR[SR_PC], dest); + tcg_gen_movi_i64(cpu_SR[SR_PC], dest); tcg_gen_exit_tb((uintptr_t)dc->tb + n); } else { - tcg_gen_movi_tl(cpu_SR[SR_PC], dest); + tcg_gen_movi_i64(cpu_SR[SR_PC], dest); tcg_gen_exit_tb(0); } } -static void read_carry(DisasContext *dc, TCGv d) +static void read_carry(DisasContext *dc, TCGv_i32 d) { - tcg_gen_shri_tl(d, cpu_SR[SR_MSR], 31); + tcg_gen_extrl_i64_i32(d, cpu_SR[SR_MSR]); + tcg_gen_shri_i32(d, d, 31); } /* * write_carry sets the carry bits in MSR based on bit 0 of v. * v[31:1] are ignored. */ -static void write_carry(DisasContext *dc, TCGv v) +static void write_carry(DisasContext *dc, TCGv_i32 v) { - TCGv t0 = tcg_temp_new(); - tcg_gen_shli_tl(t0, v, 31); - tcg_gen_sari_tl(t0, t0, 31); - tcg_gen_andi_tl(t0, t0, (MSR_C | MSR_CC)); - tcg_gen_andi_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], - ~(MSR_C | MSR_CC)); - tcg_gen_or_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t0); - tcg_temp_free(t0); + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(t0, v); + /* Deposit bit 0 into MSR_C and the alias MSR_CC. */ + tcg_gen_deposit_i64(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t0, 2, 1); + tcg_gen_deposit_i64(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t0, 31, 1); + tcg_temp_free_i64(t0); } static void write_carryi(DisasContext *dc, bool carry) { - TCGv t0 = tcg_temp_new(); - tcg_gen_movi_tl(t0, carry); + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_movi_i32(t0, carry); write_carry(dc, t0); - tcg_temp_free(t0); + tcg_temp_free_i32(t0); +} + +/* + * Returns true if the insn an illegal operation. + * If exceptions are enabled, an exception is raised. + */ +static bool trap_illegal(DisasContext *dc, bool cond) +{ + if (cond && (dc->tb_flags & MSR_EE_FLAG) + && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { + tcg_gen_movi_i64(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); + t_gen_raise_exception(dc, EXCP_HW_EXCP); + } + return cond; +} + +/* + * Returns true if the insn is illegal in userspace. + * If exceptions are enabled, an exception is raised. + */ +static bool trap_userspace(DisasContext *dc, bool cond) +{ + int mem_index = cpu_mmu_index(&dc->cpu->env, false); + bool cond_user = cond && mem_index == MMU_USER_IDX; + + if (cond_user && (dc->tb_flags & MSR_EE_FLAG)) { + tcg_gen_movi_i64(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); + t_gen_raise_exception(dc, EXCP_HW_EXCP); + } + return cond_user; } /* True if ALU operand b is a small immediate that may deserve @@ -188,13 +216,13 @@ static inline int dec_alu_op_b_is_small_imm(DisasContext *dc) return dc->type_b && !(dc->tb_flags & IMM_FLAG); } -static inline TCGv *dec_alu_op_b(DisasContext *dc) +static inline TCGv_i32 *dec_alu_op_b(DisasContext *dc) { if (dc->type_b) { if (dc->tb_flags & IMM_FLAG) - tcg_gen_ori_tl(env_imm, env_imm, dc->imm); + tcg_gen_ori_i32(env_imm, env_imm, dc->imm); else - tcg_gen_movi_tl(env_imm, (int32_t)((int16_t)dc->imm)); + tcg_gen_movi_i32(env_imm, (int32_t)((int16_t)dc->imm)); return &env_imm; } else return &cpu_R[dc->rb]; @@ -203,7 +231,7 @@ static inline TCGv *dec_alu_op_b(DisasContext *dc) static void dec_add(DisasContext *dc) { unsigned int k, c; - TCGv cf; + TCGv_i32 cf; k = dc->opcode & 4; c = dc->opcode & 2; @@ -217,15 +245,15 @@ static void dec_add(DisasContext *dc) /* k - keep carry, no need to update MSR. */ /* If rd == r0, it's a nop. */ if (dc->rd) { - tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); if (c) { /* c - Add carry into the result. */ - cf = tcg_temp_new(); + cf = tcg_temp_new_i32(); read_carry(dc, cf); - tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf); - tcg_temp_free(cf); + tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->rd], cf); + tcg_temp_free_i32(cf); } } return; @@ -233,31 +261,31 @@ static void dec_add(DisasContext *dc) /* From now on, we can assume k is zero. So we need to update MSR. */ /* Extract carry. */ - cf = tcg_temp_new(); + cf = tcg_temp_new_i32(); if (c) { read_carry(dc, cf); } else { - tcg_gen_movi_tl(cf, 0); + tcg_gen_movi_i32(cf, 0); } if (dc->rd) { - TCGv ncf = tcg_temp_new(); + TCGv_i32 ncf = tcg_temp_new_i32(); gen_helper_carry(ncf, cpu_R[dc->ra], *(dec_alu_op_b(dc)), cf); - tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); - tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf); + tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->rd], cf); write_carry(dc, ncf); - tcg_temp_free(ncf); + tcg_temp_free_i32(ncf); } else { gen_helper_carry(cf, cpu_R[dc->ra], *(dec_alu_op_b(dc)), cf); write_carry(dc, cf); } - tcg_temp_free(cf); + tcg_temp_free_i32(cf); } static void dec_sub(DisasContext *dc) { unsigned int u, cmp, k, c; - TCGv cf, na; + TCGv_i32 cf, na; u = dc->imm & 2; k = dc->opcode & 4; @@ -283,15 +311,15 @@ static void dec_sub(DisasContext *dc) /* k - keep carry, no need to update MSR. */ /* If rd == r0, it's a nop. */ if (dc->rd) { - tcg_gen_sub_tl(cpu_R[dc->rd], *(dec_alu_op_b(dc)), cpu_R[dc->ra]); + tcg_gen_sub_i32(cpu_R[dc->rd], *(dec_alu_op_b(dc)), cpu_R[dc->ra]); if (c) { /* c - Add carry into the result. */ - cf = tcg_temp_new(); + cf = tcg_temp_new_i32(); read_carry(dc, cf); - tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf); - tcg_temp_free(cf); + tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->rd], cf); + tcg_temp_free_i32(cf); } } return; @@ -299,41 +327,38 @@ static void dec_sub(DisasContext *dc) /* From now on, we can assume k is zero. So we need to update MSR. */ /* Extract carry. And complement a into na. */ - cf = tcg_temp_new(); - na = tcg_temp_new(); + cf = tcg_temp_new_i32(); + na = tcg_temp_new_i32(); if (c) { read_carry(dc, cf); } else { - tcg_gen_movi_tl(cf, 1); + tcg_gen_movi_i32(cf, 1); } /* d = b + ~a + c. carry defaults to 1. */ - tcg_gen_not_tl(na, cpu_R[dc->ra]); + tcg_gen_not_i32(na, cpu_R[dc->ra]); if (dc->rd) { - TCGv ncf = tcg_temp_new(); + TCGv_i32 ncf = tcg_temp_new_i32(); gen_helper_carry(ncf, na, *(dec_alu_op_b(dc)), cf); - tcg_gen_add_tl(cpu_R[dc->rd], na, *(dec_alu_op_b(dc))); - tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf); + tcg_gen_add_i32(cpu_R[dc->rd], na, *(dec_alu_op_b(dc))); + tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->rd], cf); write_carry(dc, ncf); - tcg_temp_free(ncf); + tcg_temp_free_i32(ncf); } else { gen_helper_carry(cf, na, *(dec_alu_op_b(dc)), cf); write_carry(dc, cf); } - tcg_temp_free(cf); - tcg_temp_free(na); + tcg_temp_free_i32(cf); + tcg_temp_free_i32(na); } static void dec_pattern(DisasContext *dc) { unsigned int mode; - if ((dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) - && !dc->cpu->cfg.use_pcmp_instr) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, !dc->cpu->cfg.use_pcmp_instr)) { + return; } mode = dc->opcode & 3; @@ -347,14 +372,14 @@ static void dec_pattern(DisasContext *dc) case 2: LOG_DIS("pcmpeq r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); if (dc->rd) { - tcg_gen_setcond_tl(TCG_COND_EQ, cpu_R[dc->rd], + tcg_gen_setcond_i32(TCG_COND_EQ, cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); } break; case 3: LOG_DIS("pcmpne r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); if (dc->rd) { - tcg_gen_setcond_tl(TCG_COND_NE, cpu_R[dc->rd], + tcg_gen_setcond_i32(TCG_COND_NE, cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); } break; @@ -381,9 +406,9 @@ static void dec_and(DisasContext *dc) return; if (not) { - tcg_gen_andc_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_andc_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); } else - tcg_gen_and_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_and_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); } static void dec_or(DisasContext *dc) @@ -395,7 +420,7 @@ static void dec_or(DisasContext *dc) LOG_DIS("or r%d r%d r%d imm=%x\n", dc->rd, dc->ra, dc->rb, dc->imm); if (dc->rd) - tcg_gen_or_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_or_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); } static void dec_xor(DisasContext *dc) @@ -407,43 +432,54 @@ static void dec_xor(DisasContext *dc) LOG_DIS("xor r%d\n", dc->rd); if (dc->rd) - tcg_gen_xor_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_xor_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); } -static inline void msr_read(DisasContext *dc, TCGv d) +static inline void msr_read(DisasContext *dc, TCGv_i32 d) { - tcg_gen_mov_tl(d, cpu_SR[SR_MSR]); + tcg_gen_extrl_i64_i32(d, cpu_SR[SR_MSR]); } -static inline void msr_write(DisasContext *dc, TCGv v) +static inline void msr_write(DisasContext *dc, TCGv_i32 v) { - TCGv t; + TCGv_i64 t; - t = tcg_temp_new(); + t = tcg_temp_new_i64(); dc->cpustate_changed = 1; /* PVR bit is not writable. */ - tcg_gen_andi_tl(t, v, ~MSR_PVR); - tcg_gen_andi_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], MSR_PVR); - tcg_gen_or_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t); - tcg_temp_free(t); + tcg_gen_extu_i32_i64(t, v); + tcg_gen_andi_i64(t, t, ~MSR_PVR); + tcg_gen_andi_i64(cpu_SR[SR_MSR], cpu_SR[SR_MSR], MSR_PVR); + tcg_gen_or_i64(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t); + tcg_temp_free_i64(t); } static void dec_msr(DisasContext *dc) { CPUState *cs = CPU(dc->cpu); - TCGv t0, t1; - unsigned int sr, to, rn; - int mem_index = cpu_mmu_index(&dc->cpu->env, false); + TCGv_i32 t0, t1; + unsigned int sr, rn; + bool to, clrset, extended = false; - sr = dc->imm & ((1 << 14) - 1); - to = dc->imm & (1 << 14); + sr = extract32(dc->imm, 0, 14); + to = extract32(dc->imm, 14, 1); + clrset = extract32(dc->imm, 15, 1) == 0; dc->type_b = 1; - if (to) + if (to) { dc->cpustate_changed = 1; + } + + /* Extended MSRs are only available if addr_size > 32. */ + if (dc->cpu->cfg.addr_size > 32) { + /* The E-bit is encoded differently for To/From MSR. */ + static const unsigned int e_bit[] = { 19, 24 }; + + extended = extract32(dc->imm, e_bit[to], 1); + } /* msrclr and msrset. */ - if (!(dc->imm & (1 << 15))) { - unsigned int clr = dc->ir & (1 << 16); + if (clrset) { + bool clr = extract32(dc->ir, 16, 1); LOG_DIS("msr%s r%d imm=%x\n", clr ? "clr" : "set", dc->rd, dc->imm); @@ -453,52 +489,51 @@ static void dec_msr(DisasContext *dc) return; } - if ((dc->tb_flags & MSR_EE_FLAG) - && mem_index == MMU_USER_IDX && (dc->imm != 4 && dc->imm != 0)) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_userspace(dc, dc->imm != 4 && dc->imm != 0)) { return; } if (dc->rd) msr_read(dc, cpu_R[dc->rd]); - t0 = tcg_temp_new(); - t1 = tcg_temp_new(); + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); msr_read(dc, t0); - tcg_gen_mov_tl(t1, *(dec_alu_op_b(dc))); + tcg_gen_mov_i32(t1, *(dec_alu_op_b(dc))); if (clr) { - tcg_gen_not_tl(t1, t1); - tcg_gen_and_tl(t0, t0, t1); + tcg_gen_not_i32(t1, t1); + tcg_gen_and_i32(t0, t0, t1); } else - tcg_gen_or_tl(t0, t0, t1); + tcg_gen_or_i32(t0, t0, t1); msr_write(dc, t0); - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc + 4); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc + 4); dc->is_jmp = DISAS_UPDATE; return; } - if (to) { - if ((dc->tb_flags & MSR_EE_FLAG) - && mem_index == MMU_USER_IDX) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); - return; - } + if (trap_userspace(dc, to)) { + return; } #if !defined(CONFIG_USER_ONLY) /* Catch read/writes to the mmu block. */ if ((sr & ~0xff) == 0x1000) { + TCGv_i32 tmp_ext = tcg_const_i32(extended); + TCGv_i32 tmp_sr; + sr &= 7; + tmp_sr = tcg_const_i32(sr); LOG_DIS("m%ss sr%d r%d imm=%x\n", to ? "t" : "f", sr, dc->ra, dc->imm); - if (to) - gen_helper_mmu_write(cpu_env, tcg_const_tl(sr), cpu_R[dc->ra]); - else - gen_helper_mmu_read(cpu_R[dc->rd], cpu_env, tcg_const_tl(sr)); + if (to) { + gen_helper_mmu_write(cpu_env, tmp_ext, tmp_sr, cpu_R[dc->ra]); + } else { + gen_helper_mmu_read(cpu_R[dc->rd], cpu_env, tmp_ext, tmp_sr); + } + tcg_temp_free_i32(tmp_sr); + tcg_temp_free_i32(tmp_ext); return; } #endif @@ -511,20 +546,18 @@ static void dec_msr(DisasContext *dc) case 1: msr_write(dc, cpu_R[dc->ra]); break; - case 0x3: - tcg_gen_mov_tl(cpu_SR[SR_EAR], cpu_R[dc->ra]); - break; - case 0x5: - tcg_gen_mov_tl(cpu_SR[SR_ESR], cpu_R[dc->ra]); - break; - case 0x7: - tcg_gen_andi_tl(cpu_SR[SR_FSR], cpu_R[dc->ra], 31); + case SR_EAR: + case SR_ESR: + case SR_FSR: + tcg_gen_extu_i32_i64(cpu_SR[sr], cpu_R[dc->ra]); break; case 0x800: - tcg_gen_st_tl(cpu_R[dc->ra], cpu_env, offsetof(CPUMBState, slr)); + tcg_gen_st_i32(cpu_R[dc->ra], + cpu_env, offsetof(CPUMBState, slr)); break; case 0x802: - tcg_gen_st_tl(cpu_R[dc->ra], cpu_env, offsetof(CPUMBState, shr)); + tcg_gen_st_i32(cpu_R[dc->ra], + cpu_env, offsetof(CPUMBState, shr)); break; default: cpu_abort(CPU(dc->cpu), "unknown mts reg %x\n", sr); @@ -535,44 +568,32 @@ static void dec_msr(DisasContext *dc) switch (sr) { case 0: - tcg_gen_movi_tl(cpu_R[dc->rd], dc->pc); + tcg_gen_movi_i32(cpu_R[dc->rd], dc->pc); break; case 1: msr_read(dc, cpu_R[dc->rd]); break; - case 0x3: - tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_EAR]); - break; - case 0x5: - tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_ESR]); - break; - case 0x7: - tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_FSR]); - break; - case 0xb: - tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_BTR]); + case SR_EAR: + if (extended) { + tcg_gen_extrh_i64_i32(cpu_R[dc->rd], cpu_SR[sr]); + break; + } + case SR_ESR: + case SR_FSR: + case SR_BTR: + tcg_gen_extrl_i64_i32(cpu_R[dc->rd], cpu_SR[sr]); break; case 0x800: - tcg_gen_ld_tl(cpu_R[dc->rd], cpu_env, offsetof(CPUMBState, slr)); + tcg_gen_ld_i32(cpu_R[dc->rd], + cpu_env, offsetof(CPUMBState, slr)); break; case 0x802: - tcg_gen_ld_tl(cpu_R[dc->rd], cpu_env, offsetof(CPUMBState, shr)); + tcg_gen_ld_i32(cpu_R[dc->rd], + cpu_env, offsetof(CPUMBState, shr)); break; - case 0x2000: - case 0x2001: - case 0x2002: - case 0x2003: - case 0x2004: - case 0x2005: - case 0x2006: - case 0x2007: - case 0x2008: - case 0x2009: - case 0x200a: - case 0x200b: - case 0x200c: + case 0x2000 ... 0x200c: rn = sr & 0xf; - tcg_gen_ld_tl(cpu_R[dc->rd], + tcg_gen_ld_i32(cpu_R[dc->rd], cpu_env, offsetof(CPUMBState, pvr.regs[rn])); break; default: @@ -582,21 +603,17 @@ static void dec_msr(DisasContext *dc) } if (dc->rd == 0) { - tcg_gen_movi_tl(cpu_R[0], 0); + tcg_gen_movi_i32(cpu_R[0], 0); } } /* Multiplier unit. */ static void dec_mul(DisasContext *dc) { - TCGv tmp; + TCGv_i32 tmp; unsigned int subcode; - if ((dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) - && !dc->cpu->cfg.use_hw_mul) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, !dc->cpu->cfg.use_hw_mul)) { return; } @@ -604,7 +621,7 @@ static void dec_mul(DisasContext *dc) if (dc->type_b) { LOG_DIS("muli r%d r%d %x\n", dc->rd, dc->ra, dc->imm); - tcg_gen_mul_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_mul_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); return; } @@ -613,29 +630,31 @@ static void dec_mul(DisasContext *dc) /* nop??? */ } - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i32(); switch (subcode) { case 0: LOG_DIS("mul r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); - tcg_gen_mul_tl(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + tcg_gen_mul_i32(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); break; case 1: LOG_DIS("mulh r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); - tcg_gen_muls2_tl(tmp, cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + tcg_gen_muls2_i32(tmp, cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); break; case 2: LOG_DIS("mulhsu r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); - tcg_gen_mulsu2_tl(tmp, cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + tcg_gen_mulsu2_i32(tmp, cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); break; case 3: LOG_DIS("mulhu r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); - tcg_gen_mulu2_tl(tmp, cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + tcg_gen_mulu2_i32(tmp, cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); break; default: cpu_abort(CPU(dc->cpu), "unknown MUL insn %x\n", subcode); break; } - tcg_temp_free(tmp); + tcg_temp_free_i32(tmp); } /* Div unit. */ @@ -646,10 +665,8 @@ static void dec_div(DisasContext *dc) u = dc->imm & 2; LOG_DIS("div\n"); - if ((dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) - && !dc->cpu->cfg.use_div) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, !dc->cpu->cfg.use_div)) { + return; } if (u) @@ -659,20 +676,16 @@ static void dec_div(DisasContext *dc) gen_helper_divs(cpu_R[dc->rd], cpu_env, *(dec_alu_op_b(dc)), cpu_R[dc->ra]); if (!dc->rd) - tcg_gen_movi_tl(cpu_R[dc->rd], 0); + tcg_gen_movi_i32(cpu_R[dc->rd], 0); } static void dec_barrel(DisasContext *dc) { - TCGv t0; + TCGv_i32 t0; unsigned int imm_w, imm_s; bool s, t, e = false, i = false; - if ((dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) - && !dc->cpu->cfg.use_barrel) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, !dc->cpu->cfg.use_barrel)) { return; } @@ -710,45 +723,45 @@ static void dec_barrel(DisasContext *dc) imm_s, width); } } else { - t0 = tcg_temp_new(); + t0 = tcg_temp_new_i32(); - tcg_gen_mov_tl(t0, *(dec_alu_op_b(dc))); - tcg_gen_andi_tl(t0, t0, 31); + tcg_gen_mov_i32(t0, *(dec_alu_op_b(dc))); + tcg_gen_andi_i32(t0, t0, 31); if (s) { - tcg_gen_shl_tl(cpu_R[dc->rd], cpu_R[dc->ra], t0); + tcg_gen_shl_i32(cpu_R[dc->rd], cpu_R[dc->ra], t0); } else { if (t) { - tcg_gen_sar_tl(cpu_R[dc->rd], cpu_R[dc->ra], t0); + tcg_gen_sar_i32(cpu_R[dc->rd], cpu_R[dc->ra], t0); } else { - tcg_gen_shr_tl(cpu_R[dc->rd], cpu_R[dc->ra], t0); + tcg_gen_shr_i32(cpu_R[dc->rd], cpu_R[dc->ra], t0); } } - tcg_temp_free(t0); + tcg_temp_free_i32(t0); } } static void dec_bit(DisasContext *dc) { CPUState *cs = CPU(dc->cpu); - TCGv t0; + TCGv_i32 t0; unsigned int op; - int mem_index = cpu_mmu_index(&dc->cpu->env, false); op = dc->ir & ((1 << 9) - 1); switch (op) { case 0x21: /* src. */ - t0 = tcg_temp_new(); + t0 = tcg_temp_new_i32(); LOG_DIS("src r%d r%d\n", dc->rd, dc->ra); - tcg_gen_andi_tl(t0, cpu_SR[SR_MSR], MSR_CC); + tcg_gen_extrl_i64_i32(t0, cpu_SR[SR_MSR]); + tcg_gen_andi_i32(t0, t0, MSR_CC); write_carry(dc, cpu_R[dc->ra]); if (dc->rd) { - tcg_gen_shri_tl(cpu_R[dc->rd], cpu_R[dc->ra], 1); - tcg_gen_or_tl(cpu_R[dc->rd], cpu_R[dc->rd], t0); + tcg_gen_shri_i32(cpu_R[dc->rd], cpu_R[dc->ra], 1); + tcg_gen_or_i32(cpu_R[dc->rd], cpu_R[dc->rd], t0); } - tcg_temp_free(t0); + tcg_temp_free_i32(t0); break; case 0x1: @@ -760,9 +773,9 @@ static void dec_bit(DisasContext *dc) write_carry(dc, cpu_R[dc->ra]); if (dc->rd) { if (op == 0x41) - tcg_gen_shri_tl(cpu_R[dc->rd], cpu_R[dc->ra], 1); + tcg_gen_shri_i32(cpu_R[dc->rd], cpu_R[dc->ra], 1); else - tcg_gen_sari_tl(cpu_R[dc->rd], cpu_R[dc->ra], 1); + tcg_gen_sari_i32(cpu_R[dc->rd], cpu_R[dc->ra], 1); } break; case 0x60: @@ -779,29 +792,16 @@ static void dec_bit(DisasContext *dc) case 0x76: /* wdc. */ LOG_DIS("wdc r%d\n", dc->ra); - if ((dc->tb_flags & MSR_EE_FLAG) - && mem_index == MMU_USER_IDX) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); - return; - } + trap_userspace(dc, true); break; case 0x68: /* wic. */ LOG_DIS("wic r%d\n", dc->ra); - if ((dc->tb_flags & MSR_EE_FLAG) - && mem_index == MMU_USER_IDX) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); - return; - } + trap_userspace(dc, true); break; case 0xe0: - if ((dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) - && !dc->cpu->cfg.use_pcmp_instr) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, !dc->cpu->cfg.use_pcmp_instr)) { + return; } if (dc->cpu->cfg.use_pcmp_instr) { tcg_gen_clzi_i32(cpu_R[dc->rd], cpu_R[dc->ra], 32); @@ -828,101 +828,128 @@ static inline void sync_jmpstate(DisasContext *dc) { if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) { if (dc->jmp == JMP_DIRECT) { - tcg_gen_movi_tl(env_btaken, 1); + tcg_gen_movi_i32(env_btaken, 1); } dc->jmp = JMP_INDIRECT; - tcg_gen_movi_tl(env_btarget, dc->jmp_pc); + tcg_gen_movi_i64(env_btarget, dc->jmp_pc); } } static void dec_imm(DisasContext *dc) { LOG_DIS("imm %x\n", dc->imm << 16); - tcg_gen_movi_tl(env_imm, (dc->imm << 16)); + tcg_gen_movi_i32(env_imm, (dc->imm << 16)); dc->tb_flags |= IMM_FLAG; dc->clear_imm = 0; } -static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t) +static inline void compute_ldst_addr(DisasContext *dc, bool ea, TCGv t) { - unsigned int extimm = dc->tb_flags & IMM_FLAG; - /* Should be set to one if r1 is used by loadstores. */ - int stackprot = 0; + bool extimm = dc->tb_flags & IMM_FLAG; + /* Should be set to true if r1 is used by loadstores. */ + bool stackprot = false; + TCGv_i32 t32; /* All load/stores use ra. */ if (dc->ra == 1 && dc->cpu->cfg.stackprot) { - stackprot = 1; + stackprot = true; } /* Treat the common cases first. */ if (!dc->type_b) { - /* If any of the regs is r0, return a ptr to the other. */ + if (ea) { + int addr_size = dc->cpu->cfg.addr_size; + + if (addr_size == 32) { + tcg_gen_extu_i32_tl(t, cpu_R[dc->rb]); + return; + } + + tcg_gen_concat_i32_i64(t, cpu_R[dc->rb], cpu_R[dc->ra]); + if (addr_size < 64) { + /* Mask off out of range bits. */ + tcg_gen_andi_i64(t, t, MAKE_64BIT_MASK(0, addr_size)); + } + return; + } + + /* If any of the regs is r0, set t to the value of the other reg. */ if (dc->ra == 0) { - return &cpu_R[dc->rb]; + tcg_gen_extu_i32_tl(t, cpu_R[dc->rb]); + return; } else if (dc->rb == 0) { - return &cpu_R[dc->ra]; + tcg_gen_extu_i32_tl(t, cpu_R[dc->ra]); + return; } if (dc->rb == 1 && dc->cpu->cfg.stackprot) { - stackprot = 1; + stackprot = true; } - *t = tcg_temp_new(); - tcg_gen_add_tl(*t, cpu_R[dc->ra], cpu_R[dc->rb]); + t32 = tcg_temp_new_i32(); + tcg_gen_add_i32(t32, cpu_R[dc->ra], cpu_R[dc->rb]); + tcg_gen_extu_i32_tl(t, t32); + tcg_temp_free_i32(t32); if (stackprot) { - gen_helper_stackprot(cpu_env, *t); + gen_helper_stackprot(cpu_env, t); } - return t; + return; } /* Immediate. */ + t32 = tcg_temp_new_i32(); if (!extimm) { - if (dc->imm == 0) { - return &cpu_R[dc->ra]; - } - *t = tcg_temp_new(); - tcg_gen_movi_tl(*t, (int32_t)((int16_t)dc->imm)); - tcg_gen_add_tl(*t, cpu_R[dc->ra], *t); + tcg_gen_addi_i32(t32, cpu_R[dc->ra], (int16_t)dc->imm); } else { - *t = tcg_temp_new(); - tcg_gen_add_tl(*t, cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_add_i32(t32, cpu_R[dc->ra], *(dec_alu_op_b(dc))); } + tcg_gen_extu_i32_tl(t, t32); + tcg_temp_free_i32(t32); if (stackprot) { - gen_helper_stackprot(cpu_env, *t); + gen_helper_stackprot(cpu_env, t); } - return t; + return; } static void dec_load(DisasContext *dc) { - TCGv t, v, *addr; - unsigned int size, rev = 0, ex = 0; + TCGv_i32 v; + TCGv addr; + unsigned int size; + bool rev = false, ex = false, ea = false; + int mem_index = cpu_mmu_index(&dc->cpu->env, false); TCGMemOp mop; mop = dc->opcode & 3; size = 1 << mop; if (!dc->type_b) { - rev = (dc->ir >> 9) & 1; - ex = (dc->ir >> 10) & 1; + ea = extract32(dc->ir, 7, 1); + rev = extract32(dc->ir, 9, 1); + ex = extract32(dc->ir, 10, 1); } mop |= MO_TE; if (rev) { mop ^= MO_BSWAP; } - if (size > 4 && (dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, size > 4)) { return; } - LOG_DIS("l%d%s%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : "", - ex ? "x" : ""); + if (trap_userspace(dc, ea)) { + return; + } + + LOG_DIS("l%d%s%s%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : "", + ex ? "x" : "", + ea ? "ea" : ""); t_sync_flags(dc); - addr = compute_ldst_addr(dc, &t); + addr = tcg_temp_new(); + compute_ldst_addr(dc, ea, addr); + /* Extended addressing bypasses the MMU. */ + mem_index = ea ? MMU_NOMMU_IDX : mem_index; /* * When doing reverse accesses we need to do two things. @@ -941,17 +968,10 @@ static void dec_load(DisasContext *dc) 11 -> 00 */ TCGv low = tcg_temp_new(); - /* Force addr into the temp. */ - if (addr != &t) { - t = tcg_temp_new(); - tcg_gen_mov_tl(t, *addr); - addr = &t; - } - - tcg_gen_andi_tl(low, t, 3); + tcg_gen_andi_tl(low, addr, 3); tcg_gen_sub_tl(low, tcg_const_tl(3), low); - tcg_gen_andi_tl(t, t, ~3); - tcg_gen_or_tl(t, t, low); + tcg_gen_andi_tl(addr, addr, ~3); + tcg_gen_or_tl(addr, addr, low); tcg_temp_free(low); break; } @@ -959,14 +979,7 @@ static void dec_load(DisasContext *dc) case 2: /* 00 -> 10 10 -> 00. */ - /* Force addr into the temp. */ - if (addr != &t) { - t = tcg_temp_new(); - tcg_gen_xori_tl(t, *addr, 2); - addr = &t; - } else { - tcg_gen_xori_tl(t, t, 2); - } + tcg_gen_xori_tl(addr, addr, 2); break; default: cpu_abort(CPU(dc->cpu), "Invalid reverse size\n"); @@ -976,13 +989,7 @@ static void dec_load(DisasContext *dc) /* lwx does not throw unaligned access errors, so force alignment */ if (ex) { - /* Force addr into the temp. */ - if (addr != &t) { - t = tcg_temp_new(); - tcg_gen_mov_tl(t, *addr); - addr = &t; - } - tcg_gen_andi_tl(t, t, ~3); + tcg_gen_andi_tl(addr, addr, ~3); } /* If we get a fault on a dslot, the jmpstate better be in sync. */ @@ -995,90 +1002,92 @@ static void dec_load(DisasContext *dc) * into v. If the load succeeds, we verify alignment of the * address and if that succeeds we write into the destination reg. */ - v = tcg_temp_new(); - tcg_gen_qemu_ld_tl(v, *addr, cpu_mmu_index(&dc->cpu->env, false), mop); + v = tcg_temp_new_i32(); + tcg_gen_qemu_ld_i32(v, addr, mem_index, mop); if ((dc->cpu->env.pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) { - tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); - gen_helper_memalign(cpu_env, *addr, tcg_const_tl(dc->rd), - tcg_const_tl(0), tcg_const_tl(size - 1)); + tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc); + gen_helper_memalign(cpu_env, addr, tcg_const_i32(dc->rd), + tcg_const_i32(0), tcg_const_i32(size - 1)); } if (ex) { - tcg_gen_mov_tl(env_res_addr, *addr); - tcg_gen_mov_tl(env_res_val, v); + tcg_gen_mov_tl(env_res_addr, addr); + tcg_gen_mov_i32(env_res_val, v); } if (dc->rd) { - tcg_gen_mov_tl(cpu_R[dc->rd], v); + tcg_gen_mov_i32(cpu_R[dc->rd], v); } - tcg_temp_free(v); + tcg_temp_free_i32(v); if (ex) { /* lwx */ /* no support for AXI exclusive so always clear C */ write_carryi(dc, 0); } - if (addr == &t) - tcg_temp_free(t); + tcg_temp_free(addr); } static void dec_store(DisasContext *dc) { - TCGv t, *addr, swx_addr; + TCGv addr; TCGLabel *swx_skip = NULL; - unsigned int size, rev = 0, ex = 0; + unsigned int size; + bool rev = false, ex = false, ea = false; + int mem_index = cpu_mmu_index(&dc->cpu->env, false); TCGMemOp mop; mop = dc->opcode & 3; size = 1 << mop; if (!dc->type_b) { - rev = (dc->ir >> 9) & 1; - ex = (dc->ir >> 10) & 1; + ea = extract32(dc->ir, 7, 1); + rev = extract32(dc->ir, 9, 1); + ex = extract32(dc->ir, 10, 1); } mop |= MO_TE; if (rev) { mop ^= MO_BSWAP; } - if (size > 4 && (dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, size > 4)) { return; } - LOG_DIS("s%d%s%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : "", - ex ? "x" : ""); + trap_userspace(dc, ea); + + LOG_DIS("s%d%s%s%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : "", + ex ? "x" : "", + ea ? "ea" : ""); t_sync_flags(dc); /* If we get a fault on a dslot, the jmpstate better be in sync. */ sync_jmpstate(dc); - addr = compute_ldst_addr(dc, &t); + /* SWX needs a temp_local. */ + addr = ex ? tcg_temp_local_new() : tcg_temp_new(); + compute_ldst_addr(dc, ea, addr); + /* Extended addressing bypasses the MMU. */ + mem_index = ea ? MMU_NOMMU_IDX : mem_index; - swx_addr = tcg_temp_local_new(); if (ex) { /* swx */ - TCGv tval; + TCGv_i32 tval; - /* Force addr into the swx_addr. */ - tcg_gen_mov_tl(swx_addr, *addr); - addr = &swx_addr; /* swx does not throw unaligned access errors, so force alignment */ - tcg_gen_andi_tl(swx_addr, swx_addr, ~3); + tcg_gen_andi_tl(addr, addr, ~3); write_carryi(dc, 1); swx_skip = gen_new_label(); - tcg_gen_brcond_tl(TCG_COND_NE, env_res_addr, swx_addr, swx_skip); + tcg_gen_brcond_tl(TCG_COND_NE, env_res_addr, addr, swx_skip); /* Compare the value loaded at lwx with current contents of the reserved location. FIXME: This only works for system emulation where we can expect this compare and the following write to be atomic. For user emulation we need to add atomicity between threads. */ - tval = tcg_temp_new(); - tcg_gen_qemu_ld_tl(tval, swx_addr, cpu_mmu_index(&dc->cpu->env, false), - MO_TEUL); - tcg_gen_brcond_tl(TCG_COND_NE, env_res_val, tval, swx_skip); + tval = tcg_temp_new_i32(); + tcg_gen_qemu_ld_i32(tval, addr, cpu_mmu_index(&dc->cpu->env, false), + MO_TEUL); + tcg_gen_brcond_i32(TCG_COND_NE, env_res_val, tval, swx_skip); write_carryi(dc, 0); - tcg_temp_free(tval); + tcg_temp_free_i32(tval); } if (rev && size != 4) { @@ -1092,17 +1101,10 @@ static void dec_store(DisasContext *dc) 11 -> 00 */ TCGv low = tcg_temp_new(); - /* Force addr into the temp. */ - if (addr != &t) { - t = tcg_temp_new(); - tcg_gen_mov_tl(t, *addr); - addr = &t; - } - - tcg_gen_andi_tl(low, t, 3); + tcg_gen_andi_tl(low, addr, 3); tcg_gen_sub_tl(low, tcg_const_tl(3), low); - tcg_gen_andi_tl(t, t, ~3); - tcg_gen_or_tl(t, t, low); + tcg_gen_andi_tl(addr, addr, ~3); + tcg_gen_or_tl(addr, addr, low); tcg_temp_free(low); break; } @@ -1111,79 +1113,74 @@ static void dec_store(DisasContext *dc) /* 00 -> 10 10 -> 00. */ /* Force addr into the temp. */ - if (addr != &t) { - t = tcg_temp_new(); - tcg_gen_xori_tl(t, *addr, 2); - addr = &t; - } else { - tcg_gen_xori_tl(t, t, 2); - } + tcg_gen_xori_tl(addr, addr, 2); break; default: cpu_abort(CPU(dc->cpu), "Invalid reverse size\n"); break; } } - tcg_gen_qemu_st_tl(cpu_R[dc->rd], *addr, cpu_mmu_index(&dc->cpu->env, false), mop); + tcg_gen_qemu_st_i32(cpu_R[dc->rd], addr, mem_index, mop); /* Verify alignment if needed. */ if ((dc->cpu->env.pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) { - tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); + tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc); /* FIXME: if the alignment is wrong, we should restore the value * in memory. One possible way to achieve this is to probe * the MMU prior to the memaccess, thay way we could put * the alignment checks in between the probe and the mem * access. */ - gen_helper_memalign(cpu_env, *addr, tcg_const_tl(dc->rd), - tcg_const_tl(1), tcg_const_tl(size - 1)); + gen_helper_memalign(cpu_env, addr, tcg_const_i32(dc->rd), + tcg_const_i32(1), tcg_const_i32(size - 1)); } if (ex) { gen_set_label(swx_skip); } - tcg_temp_free(swx_addr); - if (addr == &t) - tcg_temp_free(t); + tcg_temp_free(addr); } static inline void eval_cc(DisasContext *dc, unsigned int cc, - TCGv d, TCGv a, TCGv b) + TCGv_i32 d, TCGv_i32 a) { + static const int mb_to_tcg_cc[] = { + [CC_EQ] = TCG_COND_EQ, + [CC_NE] = TCG_COND_NE, + [CC_LT] = TCG_COND_LT, + [CC_LE] = TCG_COND_LE, + [CC_GE] = TCG_COND_GE, + [CC_GT] = TCG_COND_GT, + }; + switch (cc) { - case CC_EQ: - tcg_gen_setcond_tl(TCG_COND_EQ, d, a, b); - break; - case CC_NE: - tcg_gen_setcond_tl(TCG_COND_NE, d, a, b); - break; - case CC_LT: - tcg_gen_setcond_tl(TCG_COND_LT, d, a, b); - break; - case CC_LE: - tcg_gen_setcond_tl(TCG_COND_LE, d, a, b); - break; - case CC_GE: - tcg_gen_setcond_tl(TCG_COND_GE, d, a, b); - break; - case CC_GT: - tcg_gen_setcond_tl(TCG_COND_GT, d, a, b); - break; - default: - cpu_abort(CPU(dc->cpu), "Unknown condition code %x.\n", cc); - break; + case CC_EQ: + case CC_NE: + case CC_LT: + case CC_LE: + case CC_GE: + case CC_GT: + tcg_gen_setcondi_i32(mb_to_tcg_cc[cc], d, a, 0); + break; + default: + cpu_abort(CPU(dc->cpu), "Unknown condition code %x.\n", cc); + break; } } -static void eval_cond_jmp(DisasContext *dc, TCGv pc_true, TCGv pc_false) +static void eval_cond_jmp(DisasContext *dc, TCGv_i64 pc_true, TCGv_i64 pc_false) { - TCGLabel *l1 = gen_new_label(); - /* Conditional jmp. */ - tcg_gen_mov_tl(cpu_SR[SR_PC], pc_false); - tcg_gen_brcondi_tl(TCG_COND_EQ, env_btaken, 0, l1); - tcg_gen_mov_tl(cpu_SR[SR_PC], pc_true); - gen_set_label(l1); + TCGv_i64 tmp_btaken = tcg_temp_new_i64(); + TCGv_i64 tmp_zero = tcg_const_i64(0); + + tcg_gen_extu_i32_i64(tmp_btaken, env_btaken); + tcg_gen_movcond_i64(TCG_COND_NE, cpu_SR[SR_PC], + tmp_btaken, tmp_zero, + pc_true, pc_false); + + tcg_temp_free_i64(tmp_btaken); + tcg_temp_free_i64(tmp_zero); } static void dec_bcc(DisasContext *dc) @@ -1199,28 +1196,28 @@ static void dec_bcc(DisasContext *dc) if (dslot) { dc->delayed_branch = 2; dc->tb_flags |= D_FLAG; - tcg_gen_st_tl(tcg_const_tl(dc->type_b && (dc->tb_flags & IMM_FLAG)), + tcg_gen_st_i32(tcg_const_i32(dc->type_b && (dc->tb_flags & IMM_FLAG)), cpu_env, offsetof(CPUMBState, bimm)); } if (dec_alu_op_b_is_small_imm(dc)) { int32_t offset = (int32_t)((int16_t)dc->imm); /* sign-extend. */ - tcg_gen_movi_tl(env_btarget, dc->pc + offset); + tcg_gen_movi_i64(env_btarget, dc->pc + offset); dc->jmp = JMP_DIRECT_CC; dc->jmp_pc = dc->pc + offset; } else { dc->jmp = JMP_INDIRECT; - tcg_gen_movi_tl(env_btarget, dc->pc); - tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc))); + tcg_gen_extu_i32_i64(env_btarget, *(dec_alu_op_b(dc))); + tcg_gen_addi_i64(env_btarget, env_btarget, dc->pc); + tcg_gen_andi_i64(env_btarget, env_btarget, UINT32_MAX); } - eval_cc(dc, cc, env_btaken, cpu_R[dc->ra], tcg_const_tl(0)); + eval_cc(dc, cc, env_btaken, cpu_R[dc->ra]); } static void dec_br(DisasContext *dc) { unsigned int dslot, link, abs, mbar; - int mem_index = cpu_mmu_index(&dc->cpu->env, false); dslot = dc->ir & (1 << 20); abs = dc->ir & (1 << 19); @@ -1240,7 +1237,7 @@ static void dec_br(DisasContext *dc) tcg_gen_st_i32(tmp_1, cpu_env, -offsetof(MicroBlazeCPU, env) +offsetof(CPUState, halted)); - tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc + 4); + tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc + 4); gen_helper_raise_exception(cpu_env, tmp_hlt); tcg_temp_free_i32(tmp_hlt); tcg_temp_free_i32(tmp_1); @@ -1261,23 +1258,21 @@ static void dec_br(DisasContext *dc) if (dslot) { dc->delayed_branch = 2; dc->tb_flags |= D_FLAG; - tcg_gen_st_tl(tcg_const_tl(dc->type_b && (dc->tb_flags & IMM_FLAG)), + tcg_gen_st_i32(tcg_const_i32(dc->type_b && (dc->tb_flags & IMM_FLAG)), cpu_env, offsetof(CPUMBState, bimm)); } if (link && dc->rd) - tcg_gen_movi_tl(cpu_R[dc->rd], dc->pc); + tcg_gen_movi_i32(cpu_R[dc->rd], dc->pc); dc->jmp = JMP_INDIRECT; if (abs) { - tcg_gen_movi_tl(env_btaken, 1); - tcg_gen_mov_tl(env_btarget, *(dec_alu_op_b(dc))); + tcg_gen_movi_i32(env_btaken, 1); + tcg_gen_extu_i32_i64(env_btarget, *(dec_alu_op_b(dc))); if (link && !dslot) { if (!(dc->tb_flags & IMM_FLAG) && (dc->imm == 8 || dc->imm == 0x18)) t_gen_raise_exception(dc, EXCP_BREAK); if (dc->imm == 0) { - if ((dc->tb_flags & MSR_EE_FLAG) && mem_index == MMU_USER_IDX) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_userspace(dc, true)) { return; } @@ -1289,116 +1284,115 @@ static void dec_br(DisasContext *dc) dc->jmp = JMP_DIRECT; dc->jmp_pc = dc->pc + (int32_t)((int16_t)dc->imm); } else { - tcg_gen_movi_tl(env_btaken, 1); - tcg_gen_movi_tl(env_btarget, dc->pc); - tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc))); + tcg_gen_movi_i32(env_btaken, 1); + tcg_gen_extu_i32_i64(env_btarget, *(dec_alu_op_b(dc))); + tcg_gen_addi_i64(env_btarget, env_btarget, dc->pc); + tcg_gen_andi_i64(env_btarget, env_btarget, UINT32_MAX); } } } static inline void do_rti(DisasContext *dc) { - TCGv t0, t1; - t0 = tcg_temp_new(); - t1 = tcg_temp_new(); - tcg_gen_shri_tl(t0, cpu_SR[SR_MSR], 1); - tcg_gen_ori_tl(t1, cpu_SR[SR_MSR], MSR_IE); - tcg_gen_andi_tl(t0, t0, (MSR_VM | MSR_UM)); - - tcg_gen_andi_tl(t1, t1, ~(MSR_VM | MSR_UM)); - tcg_gen_or_tl(t1, t1, t0); + TCGv_i32 t0, t1; + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(t1, cpu_SR[SR_MSR]); + tcg_gen_shri_i32(t0, t1, 1); + tcg_gen_ori_i32(t1, t1, MSR_IE); + tcg_gen_andi_i32(t0, t0, (MSR_VM | MSR_UM)); + + tcg_gen_andi_i32(t1, t1, ~(MSR_VM | MSR_UM)); + tcg_gen_or_i32(t1, t1, t0); msr_write(dc, t1); - tcg_temp_free(t1); - tcg_temp_free(t0); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); dc->tb_flags &= ~DRTI_FLAG; } static inline void do_rtb(DisasContext *dc) { - TCGv t0, t1; - t0 = tcg_temp_new(); - t1 = tcg_temp_new(); - tcg_gen_andi_tl(t1, cpu_SR[SR_MSR], ~MSR_BIP); - tcg_gen_shri_tl(t0, t1, 1); - tcg_gen_andi_tl(t0, t0, (MSR_VM | MSR_UM)); - - tcg_gen_andi_tl(t1, t1, ~(MSR_VM | MSR_UM)); - tcg_gen_or_tl(t1, t1, t0); + TCGv_i32 t0, t1; + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(t1, cpu_SR[SR_MSR]); + tcg_gen_andi_i32(t1, t1, ~MSR_BIP); + tcg_gen_shri_i32(t0, t1, 1); + tcg_gen_andi_i32(t0, t0, (MSR_VM | MSR_UM)); + + tcg_gen_andi_i32(t1, t1, ~(MSR_VM | MSR_UM)); + tcg_gen_or_i32(t1, t1, t0); msr_write(dc, t1); - tcg_temp_free(t1); - tcg_temp_free(t0); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); dc->tb_flags &= ~DRTB_FLAG; } static inline void do_rte(DisasContext *dc) { - TCGv t0, t1; - t0 = tcg_temp_new(); - t1 = tcg_temp_new(); - - tcg_gen_ori_tl(t1, cpu_SR[SR_MSR], MSR_EE); - tcg_gen_andi_tl(t1, t1, ~MSR_EIP); - tcg_gen_shri_tl(t0, t1, 1); - tcg_gen_andi_tl(t0, t0, (MSR_VM | MSR_UM)); - - tcg_gen_andi_tl(t1, t1, ~(MSR_VM | MSR_UM)); - tcg_gen_or_tl(t1, t1, t0); + TCGv_i32 t0, t1; + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + + tcg_gen_extrl_i64_i32(t1, cpu_SR[SR_MSR]); + tcg_gen_ori_i32(t1, t1, MSR_EE); + tcg_gen_andi_i32(t1, t1, ~MSR_EIP); + tcg_gen_shri_i32(t0, t1, 1); + tcg_gen_andi_i32(t0, t0, (MSR_VM | MSR_UM)); + + tcg_gen_andi_i32(t1, t1, ~(MSR_VM | MSR_UM)); + tcg_gen_or_i32(t1, t1, t0); msr_write(dc, t1); - tcg_temp_free(t1); - tcg_temp_free(t0); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); dc->tb_flags &= ~DRTE_FLAG; } static void dec_rts(DisasContext *dc) { unsigned int b_bit, i_bit, e_bit; - int mem_index = cpu_mmu_index(&dc->cpu->env, false); + TCGv_i64 tmp64; i_bit = dc->ir & (1 << 21); b_bit = dc->ir & (1 << 22); e_bit = dc->ir & (1 << 23); + if (trap_userspace(dc, i_bit || b_bit || e_bit)) { + return; + } + dc->delayed_branch = 2; dc->tb_flags |= D_FLAG; - tcg_gen_st_tl(tcg_const_tl(dc->type_b && (dc->tb_flags & IMM_FLAG)), + tcg_gen_st_i32(tcg_const_i32(dc->type_b && (dc->tb_flags & IMM_FLAG)), cpu_env, offsetof(CPUMBState, bimm)); if (i_bit) { LOG_DIS("rtid ir=%x\n", dc->ir); - if ((dc->tb_flags & MSR_EE_FLAG) - && mem_index == MMU_USER_IDX) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); - } dc->tb_flags |= DRTI_FLAG; } else if (b_bit) { LOG_DIS("rtbd ir=%x\n", dc->ir); - if ((dc->tb_flags & MSR_EE_FLAG) - && mem_index == MMU_USER_IDX) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); - } dc->tb_flags |= DRTB_FLAG; } else if (e_bit) { LOG_DIS("rted ir=%x\n", dc->ir); - if ((dc->tb_flags & MSR_EE_FLAG) - && mem_index == MMU_USER_IDX) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); - } dc->tb_flags |= DRTE_FLAG; } else LOG_DIS("rts ir=%x\n", dc->ir); dc->jmp = JMP_INDIRECT; - tcg_gen_movi_tl(env_btaken, 1); - tcg_gen_add_tl(env_btarget, cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_movi_i32(env_btaken, 1); + + tmp64 = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(env_btarget, *(dec_alu_op_b(dc))); + tcg_gen_extu_i32_i64(tmp64, cpu_R[dc->ra]); + tcg_gen_add_i64(env_btarget, env_btarget, tmp64); + tcg_gen_andi_i64(env_btarget, env_btarget, UINT32_MAX); + tcg_temp_free_i64(tmp64); } static int dec_check_fpuv2(DisasContext *dc) { if ((dc->cpu->cfg.use_fpu != 2) && (dc->tb_flags & MSR_EE_FLAG)) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_FPU); + tcg_gen_movi_i64(cpu_SR[SR_ESR], ESR_EC_FPU); t_gen_raise_exception(dc, EXCP_HW_EXCP); } return (dc->cpu->cfg.use_fpu == 2) ? 0 : PVR2_USE_FPU2_MASK; @@ -1408,11 +1402,7 @@ static void dec_fpu(DisasContext *dc) { unsigned int fpu_insn; - if ((dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) - && !dc->cpu->cfg.use_fpu) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, !dc->cpu->cfg.use_fpu)) { return; } @@ -1511,10 +1501,7 @@ static void dec_fpu(DisasContext *dc) static void dec_null(DisasContext *dc) { - if ((dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, true)) { return; } qemu_log_mask(LOG_GUEST_ERROR, "unknown insn pc=%x opc=%x\n", dc->pc, dc->opcode); @@ -1524,37 +1511,34 @@ static void dec_null(DisasContext *dc) /* Insns connected to FSL or AXI stream attached devices. */ static void dec_stream(DisasContext *dc) { - int mem_index = cpu_mmu_index(&dc->cpu->env, false); TCGv_i32 t_id, t_ctrl; int ctrl; LOG_DIS("%s%s imm=%x\n", dc->rd ? "get" : "put", dc->type_b ? "" : "d", dc->imm); - if ((dc->tb_flags & MSR_EE_FLAG) && (mem_index == MMU_USER_IDX)) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_userspace(dc, true)) { return; } - t_id = tcg_temp_new(); + t_id = tcg_temp_new_i32(); if (dc->type_b) { - tcg_gen_movi_tl(t_id, dc->imm & 0xf); + tcg_gen_movi_i32(t_id, dc->imm & 0xf); ctrl = dc->imm >> 10; } else { - tcg_gen_andi_tl(t_id, cpu_R[dc->rb], 0xf); + tcg_gen_andi_i32(t_id, cpu_R[dc->rb], 0xf); ctrl = dc->imm >> 5; } - t_ctrl = tcg_const_tl(ctrl); + t_ctrl = tcg_const_i32(ctrl); if (dc->rd == 0) { gen_helper_put(t_id, t_ctrl, cpu_R[dc->ra]); } else { gen_helper_get(cpu_R[dc->rd], t_id, t_ctrl); } - tcg_temp_free(t_id); - tcg_temp_free(t_ctrl); + tcg_temp_free_i32(t_id); + tcg_temp_free_i32(t_ctrl); } static struct decoder_info { @@ -1595,13 +1579,7 @@ static inline void decode(DisasContext *dc, uint32_t ir) if (dc->ir) dc->nr_nops = 0; else { - if ((dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) - && (dc->cpu->env.pvr.regs[2] & PVR2_OPCODE_0x0_ILL_MASK)) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); - return; - } + trap_illegal(dc, dc->cpu->env.pvr.regs[2] & PVR2_OPCODE_0x0_ILL_MASK); LOG_DIS("nr_nops=%d\t", dc->nr_nops); dc->nr_nops++; @@ -1636,7 +1614,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) struct DisasContext ctx; struct DisasContext *dc = &ctx; uint32_t page_start, org_flags; - target_ulong npc; + uint32_t npc; int num_insns; int max_insns; @@ -1679,7 +1657,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) #if SIM_COMPAT if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { - tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); + tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc); gen_helper_debug(); } #endif @@ -1721,7 +1699,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) dc->tb_flags &= ~D_FLAG; /* If it is a direct jump, try direct chaining. */ if (dc->jmp == JMP_INDIRECT) { - eval_cond_jmp(dc, env_btarget, tcg_const_tl(dc->pc)); + eval_cond_jmp(dc, env_btarget, tcg_const_i64(dc->pc)); dc->is_jmp = DISAS_JUMP; } else if (dc->jmp == JMP_DIRECT) { t_sync_flags(dc); @@ -1731,7 +1709,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) TCGLabel *l1 = gen_new_label(); t_sync_flags(dc); /* Conditional jmp. */ - tcg_gen_brcondi_tl(TCG_COND_NE, env_btaken, 0, l1); + tcg_gen_brcondi_i32(TCG_COND_NE, env_btaken, 0, l1); gen_goto_tb(dc, 1, dc->pc); gen_set_label(l1); gen_goto_tb(dc, 0, dc->jmp_pc); @@ -1754,7 +1732,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) { if (dc->tb_flags & D_FLAG) { dc->is_jmp = DISAS_UPDATE; - tcg_gen_movi_tl(cpu_SR[SR_PC], npc); + tcg_gen_movi_i64(cpu_SR[SR_PC], npc); sync_jmpstate(dc); } else npc = dc->jmp_pc; @@ -1766,7 +1744,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) if (dc->is_jmp == DISAS_NEXT && (dc->cpustate_changed || org_flags != dc->tb_flags)) { dc->is_jmp = DISAS_UPDATE; - tcg_gen_movi_tl(cpu_SR[SR_PC], npc); + tcg_gen_movi_i64(cpu_SR[SR_PC], npc); } t_sync_flags(dc); @@ -1774,7 +1752,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) TCGv_i32 tmp = tcg_const_i32(EXCP_DEBUG); if (dc->is_jmp != DISAS_JUMP) { - tcg_gen_movi_tl(cpu_SR[SR_PC], npc); + tcg_gen_movi_i64(cpu_SR[SR_PC], npc); } gen_helper_raise_exception(cpu_env, tmp); tcg_temp_free_i32(tmp); @@ -1824,17 +1802,19 @@ void mb_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, if (!env || !f) return; - cpu_fprintf(f, "IN: PC=%x %s\n", + cpu_fprintf(f, "IN: PC=%" PRIx64 " %s\n", env->sregs[SR_PC], lookup_symbol(env->sregs[SR_PC])); - cpu_fprintf(f, "rmsr=%x resr=%x rear=%x debug=%x imm=%x iflags=%x fsr=%x\n", + cpu_fprintf(f, "rmsr=%" PRIx64 " resr=%" PRIx64 " rear=%" PRIx64 " " + "debug=%x imm=%x iflags=%x fsr=%" PRIx64 "\n", env->sregs[SR_MSR], env->sregs[SR_ESR], env->sregs[SR_EAR], env->debug, env->imm, env->iflags, env->sregs[SR_FSR]); - cpu_fprintf(f, "btaken=%d btarget=%x mode=%s(saved=%s) eip=%d ie=%d\n", + cpu_fprintf(f, "btaken=%d btarget=%" PRIx64 " mode=%s(saved=%s) " + "eip=%d ie=%d\n", env->btaken, env->btarget, (env->sregs[SR_MSR] & MSR_UM) ? "user" : "kernel", (env->sregs[SR_MSR] & MSR_UMS) ? "user" : "kernel", - (env->sregs[SR_MSR] & MSR_EIP), - (env->sregs[SR_MSR] & MSR_IE)); + (bool)(env->sregs[SR_MSR] & MSR_EIP), + (bool)(env->sregs[SR_MSR] & MSR_IE)); for (i = 0; i < 32; i++) { cpu_fprintf(f, "r%2.2d=%8.8x ", i, env->regs[i]); @@ -1848,34 +1828,34 @@ void mb_tcg_init(void) { int i; - env_debug = tcg_global_mem_new(cpu_env, + env_debug = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMBState, debug), "debug0"); - env_iflags = tcg_global_mem_new(cpu_env, + env_iflags = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMBState, iflags), "iflags"); - env_imm = tcg_global_mem_new(cpu_env, + env_imm = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMBState, imm), "imm"); - env_btarget = tcg_global_mem_new(cpu_env, + env_btarget = tcg_global_mem_new_i64(cpu_env, offsetof(CPUMBState, btarget), "btarget"); - env_btaken = tcg_global_mem_new(cpu_env, + env_btaken = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMBState, btaken), "btaken"); env_res_addr = tcg_global_mem_new(cpu_env, offsetof(CPUMBState, res_addr), "res_addr"); - env_res_val = tcg_global_mem_new(cpu_env, + env_res_val = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMBState, res_val), "res_val"); for (i = 0; i < ARRAY_SIZE(cpu_R); i++) { - cpu_R[i] = tcg_global_mem_new(cpu_env, + cpu_R[i] = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMBState, regs[i]), regnames[i]); } for (i = 0; i < ARRAY_SIZE(cpu_SR); i++) { - cpu_SR[i] = tcg_global_mem_new(cpu_env, + cpu_SR[i] = tcg_global_mem_new_i64(cpu_env, offsetof(CPUMBState, sregs[i]), special_regnames[i]); } diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 7ccd2f460e..0247c1f04c 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1215,7 +1215,7 @@ struct PowerPCCPU { uint64_t mig_insns_flags2; uint32_t mig_nb_BATs; bool pre_2_10_migration; - bool pre_2_13_migration; + bool pre_3_0_migration; int32_t mig_slb_nr; }; diff --git a/target/ppc/machine.c b/target/ppc/machine.c index ba1b9e531f..b2745ec4e5 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -150,11 +150,11 @@ static bool cpu_pre_2_8_migration(void *opaque, int version_id) } #if defined(TARGET_PPC64) -static bool cpu_pre_2_13_migration(void *opaque, int version_id) +static bool cpu_pre_3_0_migration(void *opaque, int version_id) { PowerPCCPU *cpu = opaque; - return cpu->pre_2_13_migration; + return cpu->pre_3_0_migration; } #endif @@ -220,7 +220,7 @@ static int cpu_pre_save(void *opaque) cpu->mig_insns_flags2 = env->insns_flags2 & insns_compat_mask2; cpu->mig_nb_BATs = env->nb_BATs; } - if (cpu->pre_2_13_migration) { + if (cpu->pre_3_0_migration) { if (cpu->hash64_opts) { cpu->mig_slb_nr = cpu->hash64_opts->slb_size; } @@ -517,7 +517,7 @@ static const VMStateDescription vmstate_slb = { .needed = slb_needed, .post_load = slb_post_load, .fields = (VMStateField[]) { - VMSTATE_INT32_TEST(mig_slb_nr, PowerPCCPU, cpu_pre_2_13_migration), + VMSTATE_INT32_TEST(mig_slb_nr, PowerPCCPU, cpu_pre_3_0_migration), VMSTATE_SLB_ARRAY(env.slb, PowerPCCPU, MAX_SLB_ENTRIES), VMSTATE_END_OF_LIST() } diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index a1db20e3a8..aa200cba4c 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -431,7 +431,8 @@ const ppc_hash_pte64_t *ppc_hash64_map_hptes(PowerPCCPU *cpu, return NULL; } - hptes = address_space_map(CPU(cpu)->as, base + pte_offset, &plen, false); + hptes = address_space_map(CPU(cpu)->as, base + pte_offset, &plen, false, + MEMTXATTRS_UNSPECIFIED); if (plen < (n * HASH_PTE_SIZE_64)) { hw_error("%s: Unable to map all requested HPTEs\n", __func__); } diff --git a/target/ppc/translate_init.inc.c b/target/ppc/translate_init.inc.c index a0b3f184b2..ab782cb32a 100644 --- a/target/ppc/translate_init.inc.c +++ b/target/ppc/translate_init.inc.c @@ -10427,7 +10427,7 @@ static Property ppc_cpu_properties[] = { DEFINE_PROP_BOOL("pre-2.8-migration", PowerPCCPU, pre_2_8_migration, false), DEFINE_PROP_BOOL("pre-2.10-migration", PowerPCCPU, pre_2_10_migration, false), - DEFINE_PROP_BOOL("pre-2.13-migration", PowerPCCPU, pre_2_13_migration, + DEFINE_PROP_BOOL("pre-3.0-migration", PowerPCCPU, pre_3_0_migration, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/target/riscv/helper.c b/target/riscv/helper.c index 95889f23b9..29e1a603dc 100644 --- a/target/riscv/helper.c +++ b/target/riscv/helper.c @@ -210,7 +210,7 @@ restart: MemoryRegion *mr; hwaddr l = sizeof(target_ulong), addr1; mr = address_space_translate(cs->as, pte_addr, - &addr1, &l, false); + &addr1, &l, false, MEMTXATTRS_UNSPECIFIED); if (memory_access_is_direct(mr, true)) { target_ulong *pte_pa = qemu_map_ram_ptr(mr->ram_block, addr1); diff --git a/target/s390x/diag.c b/target/s390x/diag.c index 0a1fabee51..acb0f3d4af 100644 --- a/target/s390x/diag.c +++ b/target/s390x/diag.c @@ -86,7 +86,8 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra) return; } if (!address_space_access_valid(&address_space_memory, addr, - sizeof(IplParameterBlock), false)) { + sizeof(IplParameterBlock), false, + MEMTXATTRS_UNSPECIFIED)) { s390_program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO, ra); return; } @@ -115,7 +116,8 @@ out: return; } if (!address_space_access_valid(&address_space_memory, addr, - sizeof(IplParameterBlock), true)) { + sizeof(IplParameterBlock), true, + MEMTXATTRS_UNSPECIFIED)) { s390_program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO, ra); return; } diff --git a/target/s390x/excp_helper.c b/target/s390x/excp_helper.c index dfee221111..f0ce60cff2 100644 --- a/target/s390x/excp_helper.c +++ b/target/s390x/excp_helper.c @@ -120,7 +120,8 @@ int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr orig_vaddr, int size, /* check out of RAM access */ if (!address_space_access_valid(&address_space_memory, raddr, - TARGET_PAGE_SIZE, rw)) { + TARGET_PAGE_SIZE, rw, + MEMTXATTRS_UNSPECIFIED)) { DPRINTF("%s: raddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__, (uint64_t)raddr, (uint64_t)ram_size); trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_AUTO); diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c index a25deef5dd..145b62a7ef 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -461,7 +461,8 @@ static int translate_pages(S390CPU *cpu, vaddr addr, int nr_pages, return ret; } if (!address_space_access_valid(&address_space_memory, pages[i], - TARGET_PAGE_SIZE, is_write)) { + TARGET_PAGE_SIZE, is_write, + MEMTXATTRS_UNSPECIFIED)) { trigger_access_exception(env, PGM_ADDRESSING, ILEN_AUTO, 0); return -EFAULT; } diff --git a/target/s390x/sigp.c b/target/s390x/sigp.c index aff1530c82..c1f9245797 100644 --- a/target/s390x/sigp.c +++ b/target/s390x/sigp.c @@ -280,7 +280,8 @@ static void sigp_set_prefix(CPUState *cs, run_on_cpu_data arg) cpu_synchronize_state(cs); if (!address_space_access_valid(&address_space_memory, addr, - sizeof(struct LowCore), false)) { + sizeof(struct LowCore), false, + MEMTXATTRS_UNSPECIFIED)) { set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); return; } diff --git a/target/xtensa/op_helper.c b/target/xtensa/op_helper.c index e3bcbe10d6..8a8c763c63 100644 --- a/target/xtensa/op_helper.c +++ b/target/xtensa/op_helper.c @@ -105,7 +105,8 @@ static void tb_invalidate_virtual_addr(CPUXtensaState *env, uint32_t vaddr) int ret = xtensa_get_physical_addr(env, false, vaddr, 2, 0, &paddr, &page_size, &access); if (ret == 0) { - tb_invalidate_phys_addr(&address_space_memory, paddr); + tb_invalidate_phys_addr(&address_space_memory, paddr, + MEMTXATTRS_UNSPECIFIED); } } diff --git a/tests/Makefile.include b/tests/Makefile.include index 3b9a5e31a2..86f90c0cb0 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -297,6 +297,7 @@ check-qtest-i386-$(CONFIG_VHOST_USER_NET_TEST_i386) += tests/vhost-user-test$(EX ifeq ($(CONFIG_VHOST_USER_NET_TEST_i386),) check-qtest-x86_64-$(CONFIG_VHOST_USER_NET_TEST_x86_64) += tests/vhost-user-test$(EXESUF) endif +check-qtest-i386-$(CONFIG_TPM) += tests/tpm-crb-swtpm-test$(EXESUF) check-qtest-i386-$(CONFIG_TPM) += tests/tpm-crb-test$(EXESUF) check-qtest-i386-$(CONFIG_TPM) += tests/tpm-tis-test$(EXESUF) check-qtest-i386-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF) @@ -524,6 +525,7 @@ qapi-schema += missing-type.json qapi-schema += nested-struct-data.json qapi-schema += non-objects.json qapi-schema += oob-test.json +qapi-schema += allow-preconfig-test.json qapi-schema += pragma-doc-required-crap.json qapi-schema += pragma-extra-junk.json qapi-schema += pragma-name-case-whitelist-crap.json @@ -721,6 +723,8 @@ tests/test-util-sockets$(EXESUF): tests/test-util-sockets.o \ tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y) tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \ tests/io-channel-helpers.o tests/socket-helpers.o $(test-io-obj-y) +tests/tpm-crb-swtpm-test$(EXESUF): tests/tpm-crb-swtpm-test.o tests/tpm-emu.o \ + tests/tpm-util.o $(test-io-obj-y) tests/tpm-crb-test$(EXESUF): tests/tpm-crb-test.o tests/tpm-emu.o $(test-io-obj-y) tests/tpm-tis-test$(EXESUF): tests/tpm-tis-test.o tests/tpm-emu.o $(test-io-obj-y) tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \ diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index ef1a3e62eb..31f21a43f5 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -4,7 +4,8 @@ DOCKER_SUFFIX := .docker DOCKER_FILES_DIR := $(SRC_PATH)/tests/docker/dockerfiles -DOCKER_IMAGES := $(notdir $(basename $(wildcard $(DOCKER_FILES_DIR)/*.docker))) +DOCKER_DEPRECATED_IMAGES := debian +DOCKER_IMAGES := $(filter-out $(DOCKER_DEPRECATED_IMAGES),$(sort $(notdir $(basename $(wildcard $(DOCKER_FILES_DIR)/*.docker))))) 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 @@ -63,7 +64,7 @@ docker-image-debian-win64-cross: docker-image-debian8-mxe docker-image-travis: NOUSER=1 # Expand all the pre-requistes for each docker image and test combination -$(foreach i,$(DOCKER_IMAGES), \ +$(foreach i,$(DOCKER_IMAGES) $(DOCKER_DEPRECATED_IMAGES), \ $(foreach t,$(DOCKER_TESTS) $(DOCKER_TOOLS), \ $(eval .PHONY: docker-$t@$i) \ $(eval docker-$t@$i: docker-image-$i docker-run-$t@$i) \ diff --git a/tests/docker/dockerfiles/travis.docker b/tests/docker/dockerfiles/travis.docker index 605b6e429b..c5ad39b533 100644 --- a/tests/docker/dockerfiles/travis.docker +++ b/tests/docker/dockerfiles/travis.docker @@ -1,8 +1,13 @@ -FROM quay.io/travisci/travis-ruby +FROM travisci/ci-garnet:packer-1512502276-986baf0 ENV DEBIAN_FRONTEND noninteractive ENV LANG en_US.UTF-8 ENV LC_ALL en_US.UTF-8 +RUN cat /etc/apt/sources.list | sed "s/# deb-src/deb-src/" >> /etc/apt/sources.list RUN apt-get update RUN apt-get -y build-dep qemu RUN apt-get -y install device-tree-compiler python2.7 python-yaml dh-autoreconf gdb strace lsof net-tools -ENV FEATURES pyyaml +# Travis tools require PhantomJS / Neo4j / Maven accessible +# in their PATH (QEMU build won't access them). +ENV PATH /usr/local/phantomjs/bin:/usr/local/phantomjs:/usr/local/neo4j-3.2.7/bin:/usr/local/maven-3.5.2/bin:/usr/local/cmake-3.9.2/bin:/usr/local/clang-5.0.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +ENV FEATURES clang pyyaml +USER travis diff --git a/tests/libqtest.c b/tests/libqtest.c index 43fb97e035..e0ca19dbfe 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -1098,3 +1098,10 @@ void qtest_qmp_device_del(const char *id) qobject_unref(response1); qobject_unref(response2); } + +bool qmp_rsp_is_err(QDict *rsp) +{ + QDict *error = qdict_get_qdict(rsp, "error"); + qobject_unref(rsp); + return !!error; +} diff --git a/tests/libqtest.h b/tests/libqtest.h index cbe8df4473..ac52872cbe 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -972,4 +972,13 @@ void qtest_qmp_device_add(const char *driver, const char *id, const char *fmt, */ void qtest_qmp_device_del(const char *id); +/** + * qmp_rsp_is_err: + * @rsp: QMP response to check for error + * + * Test @rsp for error and discard @rsp. + * Returns 'true' if there is error in @rsp and 'false' otherwise. + */ +bool qmp_rsp_is_err(QDict *rsp); + #endif diff --git a/tests/numa-test.c b/tests/numa-test.c index 169213fc1c..b7a6ef8815 100644 --- a/tests/numa-test.c +++ b/tests/numa-test.c @@ -260,6 +260,66 @@ static void aarch64_numa_cpu(const void *data) g_free(cli); } +static void pc_dynamic_cpu_cfg(const void *data) +{ + QObject *e; + QDict *resp; + QList *cpus; + QTestState *qs; + + qs = qtest_startf("%s %s", data ? (char *)data : "", + "-nodefaults --preconfig -smp 2"); + + /* create 2 numa nodes */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'node', 'nodeid': 0 } }"))); + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'node', 'nodeid': 1 } }"))); + + /* map 2 cpus in non default reverse order + * i.e socket1->node0, socket0->node1 + */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'cpu', 'node-id': 0, 'socket-id': 1 } }"))); + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'cpu', 'node-id': 1, 'socket-id': 0 } }"))); + + /* let machine initialization to complete and run */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'exit-preconfig' }"))); + qtest_qmp_eventwait(qs, "RESUME"); + + /* check that CPUs are mapped as expected */ + resp = qtest_qmp(qs, "{ 'execute': 'query-hotpluggable-cpus'}"); + g_assert(qdict_haskey(resp, "return")); + cpus = qdict_get_qlist(resp, "return"); + g_assert(cpus); + while ((e = qlist_pop(cpus))) { + const QDict *cpu, *props; + int64_t socket, node; + + cpu = qobject_to(QDict, e); + g_assert(qdict_haskey(cpu, "props")); + props = qdict_get_qdict(cpu, "props"); + + g_assert(qdict_haskey(props, "node-id")); + node = qdict_get_int(props, "node-id"); + g_assert(qdict_haskey(props, "socket-id")); + socket = qdict_get_int(props, "socket-id"); + + if (socket == 0) { + g_assert_cmpint(node, ==, 1); + } else if (socket == 1) { + g_assert_cmpint(node, ==, 0); + } else { + g_assert(false); + } + qobject_unref(e); + } + qobject_unref(resp); + + qtest_quit(qs); +} + int main(int argc, char **argv) { const char *args = NULL; @@ -278,6 +338,7 @@ int main(int argc, char **argv) if (!strcmp(arch, "i386") || !strcmp(arch, "x86_64")) { qtest_add_data_func("/numa/pc/cpu/explicit", args, pc_numa_cpu); + qtest_add_data_func("/numa/pc/dynamic/cpu", args, pc_dynamic_cpu_cfg); } if (!strcmp(arch, "ppc64")) { diff --git a/tests/qapi-schema/allow-preconfig-test.err b/tests/qapi-schema/allow-preconfig-test.err new file mode 100644 index 0000000000..700d583306 --- /dev/null +++ b/tests/qapi-schema/allow-preconfig-test.err @@ -0,0 +1 @@ +tests/qapi-schema/allow-preconfig-test.json:2: 'allow-preconfig' of command 'allow-preconfig-test' should only use true value diff --git a/tests/qapi-schema/allow-preconfig-test.exit b/tests/qapi-schema/allow-preconfig-test.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/allow-preconfig-test.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/allow-preconfig-test.json b/tests/qapi-schema/allow-preconfig-test.json new file mode 100644 index 0000000000..d9f0e914df --- /dev/null +++ b/tests/qapi-schema/allow-preconfig-test.json @@ -0,0 +1,2 @@ +# Check against allow-preconfig illegal value +{ 'command': 'allow-preconfig-test', 'allow-preconfig': 'some-string' } diff --git a/tests/qapi-schema/allow-preconfig-test.out b/tests/qapi-schema/allow-preconfig-test.out new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/qapi-schema/allow-preconfig-test.out diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index 63058b1590..9c8a4838e1 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -28,9 +28,9 @@ object q_obj_cmd-arg member arg2: str optional=True member arg3: bool optional=False command cmd q_obj_cmd-arg -> Object - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False command cmd-boxed Object -> None - gen=True success_response=True boxed=True oob=False + gen=True success_response=True boxed=True oob=False preconfig=False doc freeform body= = Section diff --git a/tests/qapi-schema/ident-with-escape.out b/tests/qapi-schema/ident-with-escape.out index 82213aa51d..24c976f473 100644 --- a/tests/qapi-schema/ident-with-escape.out +++ b/tests/qapi-schema/ident-with-escape.out @@ -5,4 +5,4 @@ module ident-with-escape.json object q_obj_fooA-arg member bar1: str optional=False command fooA q_obj_fooA-arg -> None - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False diff --git a/tests/qapi-schema/indented-expr.out b/tests/qapi-schema/indented-expr.out index 862678f8f4..bd8a48630e 100644 --- a/tests/qapi-schema/indented-expr.out +++ b/tests/qapi-schema/indented-expr.out @@ -3,6 +3,6 @@ enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE module indented-expr.json command eins None -> None - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False command zwei None -> None - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 06e30f452e..46c7282945 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -139,8 +139,8 @@ { 'command': 'boxed-struct', 'boxed': true, 'data': 'UserDefZero' } { 'command': 'boxed-union', 'data': 'UserDefNativeListUnion', 'boxed': true } -# Smoke test on Out-Of-Band -{ 'command': 'an-oob-command', 'allow-oob': true } +# Smoke test on Out-Of-Band and allow-preconfig-test +{ 'command': 'test-flags-command', 'allow-oob': true, 'allow-preconfig': true } # For testing integer range flattening in opts-visitor. The following schema # corresponds to the option format: diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 467577d770..542a19c407 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -16,7 +16,7 @@ object Empty1 object Empty2 base Empty1 command user_def_cmd0 Empty2 -> Empty2 - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False enum QEnumTwo ['value1', 'value2'] prefix QENUM_TWO object UserDefOne @@ -143,31 +143,31 @@ object UserDefNativeListUnion case sizes: q_obj_sizeList-wrapper case any: q_obj_anyList-wrapper command user_def_cmd None -> None - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False object q_obj_user_def_cmd1-arg member ud1a: UserDefOne optional=False command user_def_cmd1 q_obj_user_def_cmd1-arg -> None - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False object q_obj_user_def_cmd2-arg member ud1a: UserDefOne optional=False member ud1b: UserDefOne optional=True command user_def_cmd2 q_obj_user_def_cmd2-arg -> UserDefTwo - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False object q_obj_guest-get-time-arg member a: int optional=False member b: int optional=True command guest-get-time q_obj_guest-get-time-arg -> int - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False object q_obj_guest-sync-arg member arg: any optional=False command guest-sync q_obj_guest-sync-arg -> any - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False command boxed-struct UserDefZero -> None - gen=True success_response=True boxed=True oob=False + gen=True success_response=True boxed=True oob=False preconfig=False command boxed-union UserDefNativeListUnion -> None - gen=True success_response=True boxed=True oob=False -command an-oob-command None -> None - gen=True success_response=True boxed=False oob=True + gen=True success_response=True boxed=True oob=False preconfig=False +command test-flags-command None -> None + gen=True success_response=True boxed=False oob=True preconfig=True object UserDefOptions member i64: intList optional=True member u64: uint64List optional=True @@ -231,4 +231,4 @@ object q_obj___org.qemu_x-command-arg member c: __org.qemu_x-Union2 optional=False member d: __org.qemu_x-Alt optional=False command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Union1 - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index c1a144ba29..4512a41504 100644 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -41,12 +41,12 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): print('alternate %s' % name) self._print_variants(variants) - def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed, allow_oob): + def visit_command(self, name, info, arg_type, ret_type, gen, + success_response, boxed, allow_oob, allow_preconfig): print('command %s %s -> %s' % \ (name, arg_type and arg_type.name, ret_type and ret_type.name)) - print(' gen=%s success_response=%s boxed=%s oob=%s' % \ - (gen, success_response, boxed, allow_oob)) + print(' gen=%s success_response=%s boxed=%s oob=%s preconfig=%s' % \ + (gen, success_response, boxed, allow_oob, allow_preconfig)) def visit_event(self, name, info, arg_type, boxed): print('event %s %s' % (name, arg_type and arg_type.name)) diff --git a/tests/qemu-iotests/206 b/tests/qemu-iotests/206 index 0a18b2b19a..128c334c7c 100755 --- a/tests/qemu-iotests/206 +++ b/tests/qemu-iotests/206 @@ -1,9 +1,11 @@ -#!/bin/bash +#!/usr/bin/env python # # Test qcow2 and file image creation # # Copyright (C) 2018 Red Hat, Inc. # +# Creator/Owner: Kevin Wolf <kwolf@redhat.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 @@ -18,419 +20,263 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -# creator -owner=kwolf@redhat.com - -seq=`basename $0` -echo "QA output created by $seq" - -here=`pwd` -status=1 # failure is the default! - -# get standard environment, filters and checks -. ./common.rc -. ./common.filter - -_supported_fmt qcow2 -_supported_proto file -_supported_os Linux - -function do_run_qemu() -{ - echo Testing: "$@" - $QEMU -nographic -qmp stdio -serial none "$@" - echo -} - -function run_qemu() -{ - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ - | _filter_qemu | _filter_imgfmt \ - | _filter_actual_image_size -} - -echo -echo "=== Successful image creation (defaults) ===" -echo - -size=$((128 * 1024 * 1024)) - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "file", - "filename": "$TEST_IMG", - "size": 0 - } -} -{ "execute": "blockdev-add", - "arguments": { - "driver": "file", - "node-name": "imgfile", - "filename": "$TEST_IMG" - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "imgfile", - "size": $size - } -} -{ "execute": "quit" } -EOF - -_img_info --format-specific - -echo -echo "=== Successful image creation (inline blockdev-add, explicit defaults) ===" -echo - -# Choose a different size to show that we got a new image -size=$((64 * 1024 * 1024)) - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "file", - "filename": "$TEST_IMG", - "size": 0, - "preallocation": "off", - "nocow": false - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - }, - "size": $size, - "version": "v3", - "cluster-size": 65536, - "preallocation": "off", - "lazy-refcounts": false, - "refcount-bits": 16 - } -} -{ "execute": "quit" } -EOF - -_img_info --format-specific - -echo -echo "=== Successful image creation (v3 non-default options) ===" -echo - -# Choose a different size to show that we got a new image -size=$((32 * 1024 * 1024)) - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "file", - "filename": "$TEST_IMG", - "size": 0, - "preallocation": "falloc", - "nocow": true - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - }, - "size": $size, - "version": "v3", - "cluster-size": 2097152, - "preallocation": "metadata", - "lazy-refcounts": true, - "refcount-bits": 1 - } -} -{ "execute": "quit" } -EOF - -_img_info --format-specific - -echo -echo "=== Successful image creation (v2 non-default options) ===" -echo - -mv $TEST_IMG $TEST_IMG.base - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "file", - "filename": "$TEST_IMG", - "size": 0 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - }, - "size": $size, - "backing-file": "$TEST_IMG.base", - "backing-fmt": "qcow2", - "version": "v2", - "cluster-size": 512 - } -} -{ "execute": "quit" } -EOF - -_img_info --format-specific - -echo -echo "=== Successful image creation (encrypted) ===" -echo - -run_qemu -object secret,id=keysec0,data="foo" <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - }, - "size": $size, - "encrypt": { - "format": "luks", - "key-secret": "keysec0", - "cipher-alg": "twofish-128", - "cipher-mode": "ctr", - "ivgen-alg": "plain64", - "ivgen-hash-alg": "md5", - "hash-alg": "sha1", - "iter-time": 10 - } - } -} -{ "execute": "quit" } -EOF - -_img_info --format-specific | _filter_img_info --format-specific - -echo -echo "=== Invalid BlockdevRef ===" -echo - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "this doesn't exist", - "size": $size - } -} -{ "execute": "quit" } -EOF - - -echo -echo "=== Invalid sizes ===" -echo - -# TODO Negative image sizes aren't handled correctly, but this is a problem -# with QAPI's implementation of the 'size' type and affects other commands as -# well. Once this is fixed, we may want to add a test case here. - -# 1. Misaligned image size -# 2. 2^64 - 512 -# 3. 2^63 = 8 EB (qemu-img enforces image sizes less than this) -# 4. 2^63 - 512 (generally valid, but qcow2 can't handle images this size) - -run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 1234 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 18446744073709551104 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 9223372036854775808 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 9223372036854775296 - } -} -{ "execute": "quit" } -EOF - -echo -echo "=== Invalid version ===" -echo - -run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "version": "v1" - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "version": "v2", - "lazy-refcounts": true - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "version": "v2", - "refcount-bits": 8 - } -} -{ "execute": "quit" } -EOF - -echo -echo "=== Invalid backing file options ===" -echo - -run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "backing-file": "/dev/null", - "preallocation": "full" - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "backing-fmt": "$IMGFMT" - } -} -{ "execute": "quit" } -EOF - -echo -echo "=== Invalid cluster size ===" -echo - -run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "cluster-size": 1234 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "cluster-size": 128 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "cluster-size": 4194304 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "cluster-size": 0 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 281474976710656, - "cluster-size": 512 - } -} -{ "execute": "quit" } -EOF - -echo -echo "=== Invalid refcount width ===" -echo - -run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "refcount-bits": 128 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "refcount-bits": 0 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "refcount-bits": 7 - } -} -{ "execute": "quit" } -EOF - -# success, all done -echo "*** done" -rm -f $seq.full -status=0 +import iotests +from iotests import imgfmt + +iotests.verify_image_format(supported_fmts=['qcow2']) + +def blockdev_create(vm, options): + result = vm.qmp_log('blockdev-create', job_id='job0', options=options) + + if 'return' in result: + assert result['return'] == {} + vm.run_job('job0') + iotests.log("") + +with iotests.FilePath('t.qcow2') as disk_path, \ + iotests.FilePath('t.qcow2.base') as backing_path, \ + iotests.VM() as vm: + + vm.add_object('secret,id=keysec0,data=foo') + + # + # Successful image creation (defaults) + # + iotests.log("=== Successful image creation (defaults) ===") + iotests.log("") + + size = 128 * 1024 * 1024 + + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) + + vm.qmp_log('blockdev-add', driver='file', filename=disk_path, + node_name='imgfile') + + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'imgfile', + 'size': size }) + vm.shutdown() + + iotests.img_info_log(disk_path) + + # + # Successful image creation (inline blockdev-add, explicit defaults) + # + iotests.log("=== Successful image creation (inline blockdev-add, explicit defaults) ===") + iotests.log("") + + # Choose a different size to show that we got a new image + size = 64 * 1024 * 1024 + + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0, + 'preallocation': 'off', + 'nocow': False }) + + blockdev_create(vm, { 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'version': 'v3', + 'cluster-size': 65536, + 'preallocation': 'off', + 'lazy-refcounts': False, + 'refcount-bits': 16 }) + vm.shutdown() + + iotests.img_info_log(disk_path) + + # + # Successful image creation (v3 non-default options) + # + iotests.log("=== Successful image creation (v3 non-default options) ===") + iotests.log("") + + # Choose a different size to show that we got a new image + size = 32 * 1024 * 1024 + + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0, + 'preallocation': 'falloc', + 'nocow': True }) + + blockdev_create(vm, { 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'version': 'v3', + 'cluster-size': 2097152, + 'preallocation': 'metadata', + 'lazy-refcounts': True, + 'refcount-bits': 1 }) + vm.shutdown() + + iotests.img_info_log(disk_path) + + # + # Successful image creation (v2 non-default options) + # + iotests.log("=== Successful image creation (v2 non-default options) ===") + iotests.log("") + + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) + + blockdev_create(vm, { 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'backing-file': backing_path, + 'backing-fmt': 'qcow2', + 'version': 'v2', + 'cluster-size': 512 }) + vm.shutdown() + + iotests.img_info_log(disk_path) + + # + # Successful image creation (encrypted) + # + iotests.log("=== Successful image creation (encrypted) ===") + iotests.log("") + + vm.launch() + blockdev_create(vm, { 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'encrypt': { + 'format': 'luks', + 'key-secret': 'keysec0', + 'cipher-alg': 'twofish-128', + 'cipher-mode': 'ctr', + 'ivgen-alg': 'plain64', + 'ivgen-hash-alg': 'md5', + 'hash-alg': 'sha1', + 'iter-time': 10, + }}) + vm.shutdown() + + iotests.img_info_log(disk_path) + + # + # Invalid BlockdevRef + # + iotests.log("=== Invalid BlockdevRef ===") + iotests.log("") + + vm.launch() + blockdev_create(vm, { 'driver': imgfmt, + 'file': "this doesn't exist", + 'size': size }) + vm.shutdown() + + # + # Invalid sizes + # + iotests.log("=== Invalid sizes ===") + + # TODO Negative image sizes aren't handled correctly, but this is a problem + # with QAPI's implementation of the 'size' type and affects other commands + # as well. Once this is fixed, we may want to add a test case here. + # + # 1. Misaligned image size + # 2. 2^64 - 512 + # 3. 2^63 = 8 EB (qemu-img enforces image sizes less than this) + # 4. 2^63 - 512 (generally valid, but qcow2 can't handle images this size) + + vm.add_blockdev('driver=file,filename=%s,node-name=node0' % (disk_path)) + + vm.launch() + for size in [ 1234, 18446744073709551104, 9223372036854775808, + 9223372036854775296 ]: + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': size }) + vm.shutdown() + + # + # Invalid version + # + iotests.log("=== Invalid version ===") + + vm.launch() + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': 67108864, + 'version': 'v1' }) + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': 67108864, + 'version': 'v2', + 'lazy-refcounts': True }) + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': 67108864, + 'version': 'v2', + 'refcount-bits': 8 }) + vm.shutdown() + + # + # Invalid backing file options + # + iotests.log("=== Invalid backing file options ===") + + vm.launch() + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': 67108864, + 'backing-file': '/dev/null', + 'preallocation': 'full' }) + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': 67108864, + 'backing-fmt': imgfmt }) + vm.shutdown() + + # + # Invalid cluster size + # + iotests.log("=== Invalid cluster size ===") + + vm.launch() + for csize in [ 1234, 128, 4194304, 0 ]: + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': 67108864, + 'cluster-size': csize }) + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': 281474976710656, + 'cluster-size': 512 }) + vm.shutdown() + + # + # Invalid refcount width + # + iotests.log("=== Invalid refcount width ===") + + vm.launch() + for refcount_bits in [ 128, 0, 7 ]: + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': 67108864, + 'refcount-bits': refcount_bits }) + vm.shutdown() diff --git a/tests/qemu-iotests/206.out b/tests/qemu-iotests/206.out index 042342ae9d..789eebe57b 100644 --- a/tests/qemu-iotests/206.out +++ b/tests/qemu-iotests/206.out @@ -1,17 +1,18 @@ -QA output created by 206 - === Successful image creation (defaults) === -Testing: -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}} +{u'return': {}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'imgfile', 'size': 134217728}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: TEST_DIR/t.IMGFMT +image: TEST_IMG file format: IMGFMT virtual size: 128M (134217728 bytes) cluster_size: 65536 @@ -23,15 +24,17 @@ Format specific information: === Successful image creation (inline blockdev-add, explicit defaults) === -Testing: -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'nocow': False, 'preallocation': 'off', 'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: TEST_DIR/t.IMGFMT +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 65536, 'refcount-bits': 16, 'version': 'v3', 'preallocation': 'off', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'lazy-refcounts': False, 'driver': 'qcow2', 'size': 67108864}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +image: TEST_IMG file format: IMGFMT virtual size: 64M (67108864 bytes) cluster_size: 65536 @@ -43,15 +46,17 @@ Format specific information: === Successful image creation (v3 non-default options) === -Testing: -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'nocow': True, 'preallocation': 'falloc', 'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 2097152, 'refcount-bits': 1, 'version': 'v3', 'preallocation': 'metadata', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'lazy-refcounts': True, 'driver': 'qcow2', 'size': 33554432}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: TEST_DIR/t.IMGFMT +image: TEST_IMG file format: IMGFMT virtual size: 32M (33554432 bytes) cluster_size: 2097152 @@ -63,19 +68,21 @@ Format specific information: === Successful image creation (v2 non-default options) === -Testing: -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: TEST_DIR/t.IMGFMT +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 512, 'backing-fmt': 'qcow2', 'driver': 'qcow2', 'version': 'v2', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'backing-file': 'TEST_DIR/PID-t.qcow2.base', 'size': 33554432}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +image: TEST_IMG file format: IMGFMT virtual size: 32M (33554432 bytes) cluster_size: 512 -backing file: TEST_DIR/t.IMGFMT.base +backing file: TEST_IMG.base backing file format: IMGFMT Format specific information: compat: 0.10 @@ -83,16 +90,16 @@ Format specific information: === Successful image creation (encrypted) === -Testing: -object secret,id=keysec0,data=foo -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'encrypt': {'key-secret': 'keysec0', 'iter-time': 10, 'cipher-mode': 'ctr', 'ivgen-hash-alg': 'md5', 'cipher-alg': 'twofish-128', 'format': 'luks', 'ivgen-alg': 'plain64', 'hash-alg': 'sha1'}, 'driver': 'qcow2', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'size': 33554432}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: TEST_DIR/t.IMGFMT +image: TEST_IMG file format: IMGFMT virtual size: 32M (33554432 bytes) +encrypted: yes +cluster_size: 65536 Format specific information: compat: 1.1 lazy refcounts: false @@ -101,13 +108,13 @@ Format specific information: ivgen alg: plain64 hash alg: sha1 cipher alg: twofish-128 - uuid: 00000000-0000-0000-0000-000000000000 + uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX format: luks cipher mode: ctr slots: [0]: active: true - iters: 1024 + iters: XXX key offset: 4096 stripes: 4000 [1]: @@ -132,78 +139,118 @@ Format specific information: active: false key offset: 462848 payload offset: 528384 - master key iters: 1024 + master key iters: XXX corrupt: false === Invalid BlockdevRef === -Testing: -QMP_VERSION -{"return": {}} -{"error": {"class": "GenericError", "desc": "Cannot find device=this doesn't exist nor node_name=this doesn't exist"}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} - +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': "this doesn't exist", 'size': 33554432}}} +{u'return': {}} +Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} === Invalid sizes === - -Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -QMP_VERSION -{"return": {}} -{"error": {"class": "GenericError", "desc": "Image size must be a multiple of 512 bytes"}} -{"error": {"class": "GenericError", "desc": "Could not resize image: Image size cannot be negative"}} -{"error": {"class": "GenericError", "desc": "Could not resize image: Image size cannot be negative"}} -{"error": {"class": "GenericError", "desc": "Could not resize image: Failed to grow the L1 table: File too large"}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} - +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 1234}}} +{u'return': {}} +Job failed: Image size must be a multiple of 512 bytes +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 18446744073709551104L}}} +{u'return': {}} +Job failed: Could not resize image: Image size cannot be negative +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 9223372036854775808L}}} +{u'return': {}} +Job failed: Could not resize image: Image size cannot be negative +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 9223372036854775296}}} +{u'return': {}} +Job failed: Could not resize image: Failed to grow the L1 table: File too large +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} === Invalid version === +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'version': 'v1', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +{u'error': {u'class': u'GenericError', u'desc': u"Invalid parameter 'v1'"}} -Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -QMP_VERSION -{"return": {}} -{"error": {"class": "GenericError", "desc": "Invalid parameter 'v1'"}} -{"error": {"class": "GenericError", "desc": "Lazy refcounts only supported with compatibility level 1.1 and above (use version=v3 or greater)"}} -{"error": {"class": "GenericError", "desc": "Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater)"}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'lazy-refcounts': True, 'version': 'v2', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Lazy refcounts only supported with compatibility level 1.1 and above (use version=v3 or greater) +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 8, 'version': 'v2', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater) +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} === Invalid backing file options === - -Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -QMP_VERSION -{"return": {}} -{"error": {"class": "GenericError", "desc": "Backing file and preallocation cannot be used at the same time"}} -{"error": {"class": "GenericError", "desc": "Backing format cannot be used without backing file"}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} - +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'preallocation': 'full', 'driver': 'qcow2', 'backing-file': '/dev/null', 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Backing file and preallocation cannot be used at the same time +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'backing-fmt': 'qcow2', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Backing format cannot be used without backing file +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} === Invalid cluster size === - -Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -QMP_VERSION -{"return": {}} -{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}} -{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}} -{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}} -{"error": {"class": "GenericError", "desc": "Cluster size must be a power of two between 512 and 2048k"}} -{"error": {"class": "GenericError", "desc": "Could not resize image: Failed to grow the L1 table: File too large"}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} - +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 1234, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Cluster size must be a power of two between 512 and 2048k +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 128, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Cluster size must be a power of two between 512 and 2048k +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 4194304, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Cluster size must be a power of two between 512 and 2048k +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 0, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Cluster size must be a power of two between 512 and 2048k +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 512, 'driver': 'qcow2', 'file': 'node0', 'size': 281474976710656}}} +{u'return': {}} +Job failed: Could not resize image: Failed to grow the L1 table: File too large +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} === Invalid refcount width === +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 128, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Refcount width must be a power of two and may not exceed 64 bits +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 0, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Refcount width must be a power of two and may not exceed 64 bits +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 7, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Refcount width must be a power of two and may not exceed 64 bits +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -QMP_VERSION -{"return": {}} -{"error": {"class": "GenericError", "desc": "Refcount width must be a power of two and may not exceed 64 bits"}} -{"error": {"class": "GenericError", "desc": "Refcount width must be a power of two and may not exceed 64 bits"}} -{"error": {"class": "GenericError", "desc": "Refcount width must be a power of two and may not exceed 64 bits"}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} - -*** done diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207 index f5c77852d1..444ae233ae 100755 --- a/tests/qemu-iotests/207 +++ b/tests/qemu-iotests/207 @@ -1,9 +1,11 @@ -#!/bin/bash +#!/usr/bin/env python # # Test ssh image creation # # Copyright (C) 2018 Red Hat, Inc. # +# Creator/Owner: Kevin Wolf <kwolf@redhat.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 @@ -18,244 +20,198 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -# creator -owner=kwolf@redhat.com - -seq=`basename $0` -echo "QA output created by $seq" - -here=`pwd` -status=1 # failure is the default! - -# get standard environment, filters and checks -. ./common.rc -. ./common.filter - -_supported_fmt raw -_supported_proto ssh -_supported_os Linux - -function do_run_qemu() -{ - echo Testing: "$@" - $QEMU -nographic -qmp stdio -serial none "$@" - echo -} - -function run_qemu() -{ - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ - | _filter_qemu | _filter_imgfmt \ - | _filter_actual_image_size -} - -echo -echo "=== Successful image creation (defaults) ===" -echo - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "ssh", - "location": { - "path": "$TEST_IMG_FILE", - "server": { - "host": "127.0.0.1", - "port": "22" - } - }, - "size": 4194304 - } -} -{ "execute": "quit" } -EOF - -_img_info | _filter_img_info -echo -TEST_IMG=$TEST_IMG_FILE _img_info | _filter_img_info - -echo -echo "=== Test host-key-check options ===" -echo - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "ssh", - "location": { - "path": "$TEST_IMG_FILE", - "server": { - "host": "127.0.0.1", - "port": "22" - }, - "host-key-check": { - "mode": "none" - } - }, - "size": 8388608 - } -} -{ "execute": "quit" } -EOF - -_img_info | _filter_img_info - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "ssh", - "location": { - "path": "$TEST_IMG_FILE", - "server": { - "host": "127.0.0.1", - "port": "22" - }, - "host-key-check": { - "mode": "known_hosts" - } - }, - "size": 4194304 - } -} -{ "execute": "quit" } -EOF - -_img_info | _filter_img_info - - -key=$(ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | - cut -d" " -f3 | base64 -d | md5sum -b | cut -d" " -f1) - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "ssh", - "location": { - "path": "$TEST_IMG_FILE", - "server": { - "host": "127.0.0.1", - "port": "22" - }, - "host-key-check": { - "mode": "hash", - "type": "md5", - "hash": "wrong" - } - }, - "size": 8388608 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "ssh", - "location": { - "path": "$TEST_IMG_FILE", - "server": { - "host": "127.0.0.1", - "port": "22" - }, - "host-key-check": { - "mode": "hash", - "type": "md5", - "hash": "$key" - } - }, - "size": 8388608 - } -} -{ "execute": "quit" } -EOF - -_img_info | _filter_img_info - - -key=$(ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | - cut -d" " -f3 | base64 -d | sha1sum -b | cut -d" " -f1) - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "ssh", - "location": { - "path": "$TEST_IMG_FILE", - "server": { - "host": "127.0.0.1", - "port": "22" - }, - "host-key-check": { - "mode": "hash", - "type": "sha1", - "hash": "wrong" - } - }, - "size": 4194304 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "ssh", - "location": { - "path": "$TEST_IMG_FILE", - "server": { - "host": "127.0.0.1", - "port": "22" - }, - "host-key-check": { - "mode": "hash", - "type": "sha1", - "hash": "$key" - } - }, - "size": 4194304 - } -} -{ "execute": "quit" } -EOF - -_img_info | _filter_img_info - -echo -echo "=== Invalid path and user ===" -echo - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "ssh", - "location": { - "path": "/this/is/not/an/existing/path", - "server": { - "host": "127.0.0.1", - "port": "22" - } - }, - "size": 4194304 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "ssh", - "location": { - "path": "$TEST_IMG_FILE", - "user": "invalid user", - "server": { - "host": "127.0.0.1", - "port": "22" - } - }, - "size": 4194304 - } -} -{ "execute": "quit" } -EOF - -# success, all done -echo "*** done" -rm -f $seq.full -status=0 +import iotests +import subprocess +import re + +iotests.verify_image_format(supported_fmts=['raw']) +iotests.verify_protocol(supported=['ssh']) + +def filter_hash(msg): + return re.sub("'hash': '[0-9a-f]+'", "'hash': HASH", msg) + +def blockdev_create(vm, options): + result = vm.qmp_log('blockdev-create', job_id='job0', options=options, + filters=[iotests.filter_testfiles, filter_hash]) + + if 'return' in result: + assert result['return'] == {} + vm.run_job('job0') + iotests.log("") + +with iotests.FilePath('t.img') as disk_path, \ + iotests.VM() as vm: + + remote_path = iotests.remote_filename(disk_path) + + # + # Successful image creation (defaults) + # + iotests.log("=== Successful image creation (defaults) ===") + iotests.log("") + + vm.launch() + blockdev_create(vm, { 'driver': 'ssh', + 'location': { + 'path': disk_path, + 'server': { + 'host': '127.0.0.1', + 'port': '22' + } + }, + 'size': 4194304 }) + vm.shutdown() + + iotests.img_info_log(remote_path, filter_path=disk_path) + iotests.log("") + iotests.img_info_log(disk_path) + + # + # Test host-key-check options + # + iotests.log("=== Test host-key-check options ===") + iotests.log("") + + vm.launch() + blockdev_create(vm, { 'driver': 'ssh', + 'location': { + 'path': disk_path, + 'server': { + 'host': '127.0.0.1', + 'port': '22' + }, + 'host-key-check': { + 'mode': 'none' + } + }, + 'size': 8388608 }) + vm.shutdown() + + iotests.img_info_log(remote_path, filter_path=disk_path) + + vm.launch() + blockdev_create(vm, { 'driver': 'ssh', + 'location': { + 'path': disk_path, + 'server': { + 'host': '127.0.0.1', + 'port': '22' + }, + 'host-key-check': { + 'mode': 'known_hosts' + } + }, + 'size': 4194304 }) + vm.shutdown() + + iotests.img_info_log(remote_path, filter_path=disk_path) + + md5_key = subprocess.check_output( + 'ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' + + 'cut -d" " -f3 | base64 -d | md5sum -b | cut -d" " -f1', + shell=True).rstrip() + + vm.launch() + blockdev_create(vm, { 'driver': 'ssh', + 'location': { + 'path': disk_path, + 'server': { + 'host': '127.0.0.1', + 'port': '22' + }, + 'host-key-check': { + 'mode': 'hash', + 'type': 'md5', + 'hash': 'wrong', + } + }, + 'size': 2097152 }) + blockdev_create(vm, { 'driver': 'ssh', + 'location': { + 'path': disk_path, + 'server': { + 'host': '127.0.0.1', + 'port': '22' + }, + 'host-key-check': { + 'mode': 'hash', + 'type': 'md5', + 'hash': md5_key, + } + }, + 'size': 8388608 }) + vm.shutdown() + + iotests.img_info_log(remote_path, filter_path=disk_path) + + sha1_key = subprocess.check_output( + 'ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' + + 'cut -d" " -f3 | base64 -d | sha1sum -b | cut -d" " -f1', + shell=True).rstrip() + + vm.launch() + blockdev_create(vm, { 'driver': 'ssh', + 'location': { + 'path': disk_path, + 'server': { + 'host': '127.0.0.1', + 'port': '22' + }, + 'host-key-check': { + 'mode': 'hash', + 'type': 'sha1', + 'hash': 'wrong', + } + }, + 'size': 2097152 }) + blockdev_create(vm, { 'driver': 'ssh', + 'location': { + 'path': disk_path, + 'server': { + 'host': '127.0.0.1', + 'port': '22' + }, + 'host-key-check': { + 'mode': 'hash', + 'type': 'sha1', + 'hash': sha1_key, + } + }, + 'size': 4194304 }) + vm.shutdown() + + iotests.img_info_log(remote_path, filter_path=disk_path) + + # + # Invalid path and user + # + iotests.log("=== Invalid path and user ===") + iotests.log("") + + vm.launch() + blockdev_create(vm, { 'driver': 'ssh', + 'location': { + 'path': '/this/is/not/an/existing/path', + 'server': { + 'host': '127.0.0.1', + 'port': '22' + }, + 'host-key-check': { + 'mode': 'none' + } + }, + 'size': 4194304 }) + blockdev_create(vm, { 'driver': 'ssh', + 'location': { + 'path': disk_path, + 'user': 'invalid user', + 'server': { + 'host': '127.0.0.1', + 'port': '22' + }, + 'host-key-check': { + 'mode': 'none' + } + }, + 'size': 4194304 }) + vm.shutdown() diff --git a/tests/qemu-iotests/207.out b/tests/qemu-iotests/207.out index 417deee970..078b7e63cb 100644 --- a/tests/qemu-iotests/207.out +++ b/tests/qemu-iotests/207.out @@ -1,75 +1,80 @@ -QA output created by 207 - === Successful image creation (defaults) === -Testing: -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}} +image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} file format: IMGFMT virtual size: 4.0M (4194304 bytes) -image: TEST_DIR/t.IMGFMT + +image: TEST_IMG file format: IMGFMT virtual size: 4.0M (4194304 bytes) === Test host-key-check options === -Testing: -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'none'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 8388608}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}} +image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} file format: IMGFMT virtual size: 8.0M (8388608 bytes) -Testing: -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} - -image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'known_hosts'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} file format: IMGFMT virtual size: 4.0M (4194304 bytes) -Testing: -QMP_VERSION -{"return": {}} -{"error": {"class": "GenericError", "desc": "remote host key does not match host_key_check 'wrong'"}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} - -image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': 'wrong', 'type': 'md5', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 2097152}}} +{u'return': {}} +Job failed: remote host key does not match host_key_check 'wrong' +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': HASH, 'type': 'md5', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 8388608}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} file format: IMGFMT virtual size: 8.0M (8388608 bytes) -Testing: -QMP_VERSION -{"return": {}} -{"error": {"class": "GenericError", "desc": "remote host key does not match host_key_check 'wrong'"}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} - -image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_DIR/t.IMGFMT"}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': 'wrong', 'type': 'sha1', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 2097152}}} +{u'return': {}} +Job failed: remote host key does not match host_key_check 'wrong' +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': HASH, 'type': 'sha1', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} file format: IMGFMT virtual size: 4.0M (4194304 bytes) === Invalid path and user === -Testing: -QMP_VERSION -{"return": {}} -{"error": {"class": "GenericError", "desc": "failed to open remote file '/this/is/not/an/existing/path': Failed opening remote file (libssh2 error code: -31)"}} -{"error": {"class": "GenericError", "desc": "failed to authenticate using publickey authentication and the identities held by your ssh-agent"}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': '/this/is/not/an/existing/path', 'host-key-check': {'mode': 'none'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} +{u'return': {}} +Job failed: failed to open remote file '/this/is/not/an/existing/path': Failed opening remote file (libssh2 error code: -31) +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'none'}, 'user': 'invalid user', 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} +{u'return': {}} +Job failed: failed to authenticate using publickey authentication and the identities held by your ssh-agent +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -*** done diff --git a/tests/qemu-iotests/210 b/tests/qemu-iotests/210 index e607c0d296..d142841e2b 100755 --- a/tests/qemu-iotests/210 +++ b/tests/qemu-iotests/210 @@ -1,9 +1,11 @@ -#!/bin/bash +#!/usr/bin/env python # # Test luks and file image creation # # Copyright (C) 2018 Red Hat, Inc. # +# Creator/Owner: Kevin Wolf <kwolf@redhat.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 @@ -18,230 +20,165 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -# creator -owner=kwolf@redhat.com - -seq=`basename $0` -echo "QA output created by $seq" - -here=`pwd` -status=1 # failure is the default! - -# get standard environment, filters and checks -. ./common.rc -. ./common.filter - -_supported_fmt luks -_supported_proto file -_supported_os Linux - -function do_run_qemu() -{ - echo Testing: "$@" - $QEMU -nographic -qmp stdio -serial none "$@" - echo -} - -function run_qemu() -{ - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ - | _filter_qemu | _filter_imgfmt \ - | _filter_actual_image_size -} - -echo -echo "=== Successful image creation (defaults) ===" -echo - -size=$((128 * 1024 * 1024)) - -run_qemu -object secret,id=keysec0,data="foo" <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "file", - "filename": "$TEST_IMG_FILE", - "size": 0 - } -} -{ "execute": "blockdev-add", - "arguments": { - "driver": "file", - "node-name": "imgfile", - "filename": "$TEST_IMG_FILE" - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "imgfile", - "key-secret": "keysec0", - "size": $size, - "iter-time": 10 - } -} -{ "execute": "quit" } -EOF - -_img_info --format-specific | _filter_img_info --format-specific - -echo -echo "=== Successful image creation (with non-default options) ===" -echo - -# Choose a different size to show that we got a new image -size=$((64 * 1024 * 1024)) - -run_qemu -object secret,id=keysec0,data="foo" <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "file", - "filename": "$TEST_IMG_FILE", - "size": 0 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": { - "driver": "file", - "filename": "$TEST_IMG_FILE" - }, - "size": $size, - "key-secret": "keysec0", - "cipher-alg": "twofish-128", - "cipher-mode": "ctr", - "ivgen-alg": "plain64", - "ivgen-hash-alg": "md5", - "hash-alg": "sha1", - "iter-time": 10 - } -} -{ "execute": "quit" } -EOF - -_img_info --format-specific | _filter_img_info --format-specific - -echo -echo "=== Invalid BlockdevRef ===" -echo - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "this doesn't exist", - "size": $size - } -} -{ "execute": "quit" } -EOF - -echo -echo "=== Zero size ===" -echo - -run_qemu -blockdev driver=file,filename="$TEST_IMG_FILE",node-name=node0 \ - -object secret,id=keysec0,data="foo" <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "key-secret": "keysec0", - "size": 0, - "iter-time": 10 - } -} -{ "execute": "quit" } -EOF - -_img_info | _filter_img_info - - -echo -echo "=== Invalid sizes ===" -echo - -# TODO Negative image sizes aren't handled correctly, but this is a problem -# with QAPI's implementation of the 'size' type and affects other commands as -# well. Once this is fixed, we may want to add a test case here. - -# 1. 2^64 - 512 -# 2. 2^63 = 8 EB (qemu-img enforces image sizes less than this) -# 3. 2^63 - 512 (generally valid, but with the crypto header the file will -# exceed 63 bits) - -run_qemu -blockdev driver=file,filename="$TEST_IMG_FILE",node-name=node0 \ - -object secret,id=keysec0,data="foo" <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "key-secret": "keysec0", - "size": 18446744073709551104 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "key-secret": "keysec0", - "size": 9223372036854775808 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "key-secret": "keysec0", - "size": 9223372036854775296 - } -} -{ "execute": "quit" } -EOF - -echo -echo "=== Resize image with invalid sizes ===" -echo - -run_qemu -blockdev driver=file,filename="$TEST_IMG_FILE",node-name=node0 \ - -blockdev driver=luks,file=node0,key-secret=keysec0,node-name=node1 \ - -object secret,id=keysec0,data="foo" <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "block_resize", - "arguments": { - "node-name": "node1", - "size": 9223372036854775296 - } -} -{ "execute": "block_resize", - "arguments": { - "node-name": "node1", - "size": 9223372036854775808 - } -} -{ "execute": "block_resize", - "arguments": { - "node-name": "node1", - "size": 18446744073709551104 - } -} -{ "execute": "block_resize", - "arguments": { - "node-name": "node1", - "size": -9223372036854775808 - } -} -{ "execute": "quit" } -EOF - -_img_info | _filter_img_info - -# success, all done -echo "*** done" -rm -f $seq.full -status=0 +import iotests +from iotests import imgfmt + +iotests.verify_image_format(supported_fmts=['luks']) +iotests.verify_protocol(supported=['file']) + +def blockdev_create(vm, options): + result = vm.qmp_log('blockdev-create', job_id='job0', options=options) + + if 'return' in result: + assert result['return'] == {} + vm.run_job('job0') + iotests.log("") + +with iotests.FilePath('t.luks') as disk_path, \ + iotests.VM() as vm: + + vm.add_object('secret,id=keysec0,data=foo') + + # + # Successful image creation (defaults) + # + iotests.log("=== Successful image creation (defaults) ===") + iotests.log("") + + size = 128 * 1024 * 1024 + + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) + + vm.qmp_log('blockdev-add', driver='file', filename=disk_path, + node_name='imgfile') + + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'imgfile', + 'key-secret': 'keysec0', + 'size': size, + 'iter-time': 10 }) + vm.shutdown() + + # TODO Proper support for images to be used with imgopts and/or protocols + iotests.img_info_log( + 'driver=luks,file.driver=file,file.filename=%s,key-secret=keysec0' % (disk_path), + filter_path=disk_path, + extra_args=['--object', 'secret,id=keysec0,data=foo'], + imgopts=True) + + # + # Successful image creation (with non-default options) + # + iotests.log("=== Successful image creation (with non-default options) ===") + iotests.log("") + + size = 64 * 1024 * 1024 + + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) + blockdev_create(vm, { 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'key-secret': 'keysec0', + 'cipher-alg': 'twofish-128', + 'cipher-mode': 'ctr', + 'ivgen-alg': 'plain64', + 'ivgen-hash-alg': 'md5', + 'hash-alg': 'sha1', + 'iter-time': 10 }) + vm.shutdown() + + # TODO Proper support for images to be used with imgopts and/or protocols + iotests.img_info_log( + 'driver=luks,file.driver=file,file.filename=%s,key-secret=keysec0' % (disk_path), + filter_path=disk_path, + extra_args=['--object', 'secret,id=keysec0,data=foo'], + imgopts=True) + + # + # Invalid BlockdevRef + # + iotests.log("=== Invalid BlockdevRef ===") + iotests.log("") + + size = 64 * 1024 * 1024 + + vm.launch() + blockdev_create(vm, { 'driver': imgfmt, + 'file': "this doesn't exist", + 'size': size }) + vm.shutdown() + + # + # Zero size + # + iotests.log("=== Zero size ===") + iotests.log("") + + vm.add_blockdev('driver=file,filename=%s,node-name=node0' % (disk_path)) + vm.launch() + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'key-secret': 'keysec0', + 'size': 0, + 'iter-time': 10 }) + vm.shutdown() + + # TODO Proper support for images to be used with imgopts and/or protocols + iotests.img_info_log( + 'driver=luks,file.driver=file,file.filename=%s,key-secret=keysec0' % (disk_path), + filter_path=disk_path, + extra_args=['--object', 'secret,id=keysec0,data=foo'], + imgopts=True) + + # + # Invalid sizes + # + + # TODO Negative image sizes aren't handled correctly, but this is a problem + # with QAPI's implementation of the 'size' type and affects other commands as + # well. Once this is fixed, we may want to add a test case here. + + # 1. 2^64 - 512 + # 2. 2^63 = 8 EB (qemu-img enforces image sizes less than this) + # 3. 2^63 - 512 (generally valid, but with the crypto header the file will + # exceed 63 bits) + iotests.log("=== Invalid sizes ===") + iotests.log("") + + vm.launch() + for size in [ 18446744073709551104, 9223372036854775808, 9223372036854775296 ]: + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'key-secret': 'keysec0', + 'size': size }) + vm.shutdown() + + # + # Resize image with invalid sizes + # + iotests.log("=== Resize image with invalid sizes ===") + iotests.log("") + + vm.add_blockdev('driver=luks,file=node0,key-secret=keysec0,node-name=node1') + vm.launch() + vm.qmp_log('block_resize', node_name='node1', size=9223372036854775296) + vm.qmp_log('block_resize', node_name='node1', size=9223372036854775808) + vm.qmp_log('block_resize', node_name='node1', size=18446744073709551104) + vm.qmp_log('block_resize', node_name='node1', size=-9223372036854775808) + vm.shutdown() + + # TODO Proper support for images to be used with imgopts and/or protocols + iotests.img_info_log( + 'driver=luks,file.driver=file,file.filename=%s,key-secret=keysec0' % (disk_path), + filter_path=disk_path, + extra_args=['--object', 'secret,id=keysec0,data=foo'], + imgopts=True) diff --git a/tests/qemu-iotests/210.out b/tests/qemu-iotests/210.out index 8198f8c829..078ba544a1 100644 --- a/tests/qemu-iotests/210.out +++ b/tests/qemu-iotests/210.out @@ -1,29 +1,31 @@ -QA output created by 210 - === Successful image creation (defaults) === -Testing: -object secret,id=keysec0,data=foo -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}} +{u'return': {}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'iter-time': 10, 'driver': 'luks', 'file': 'imgfile', 'size': 134217728}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "key-secret": "keysec0"} +image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_IMG"}, "key-secret": "keysec0"} file format: IMGFMT virtual size: 128M (134217728 bytes) +encrypted: yes Format specific information: ivgen alg: plain64 hash alg: sha256 cipher alg: aes-256 - uuid: 00000000-0000-0000-0000-000000000000 + uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX cipher mode: xts slots: [0]: active: true - iters: 1024 + iters: XXX key offset: 4096 stripes: 4000 [1]: @@ -48,31 +50,34 @@ Format specific information: active: false key offset: 1810432 payload offset: 2068480 - master key iters: 1024 + master key iters: XXX === Successful image creation (with non-default options) === -Testing: -object secret,id=keysec0,data=foo -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'hash-alg': 'sha1', 'cipher-mode': 'ctr', 'cipher-alg': 'twofish-128', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}, 'iter-time': 10, 'ivgen-alg': 'plain64', 'ivgen-hash-alg': 'md5', 'driver': 'luks', 'size': 67108864}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "key-secret": "keysec0"} +image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_IMG"}, "key-secret": "keysec0"} file format: IMGFMT virtual size: 64M (67108864 bytes) +encrypted: yes Format specific information: ivgen alg: plain64 hash alg: sha1 cipher alg: twofish-128 - uuid: 00000000-0000-0000-0000-000000000000 + uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX cipher mode: ctr slots: [0]: active: true - iters: 1024 + iters: XXX key offset: 4096 stripes: 4000 [1]: @@ -97,56 +102,130 @@ Format specific information: active: false key offset: 462848 payload offset: 528384 - master key iters: 1024 + master key iters: XXX === Invalid BlockdevRef === -Testing: -QMP_VERSION -{"return": {}} -{"error": {"class": "GenericError", "desc": "Cannot find device=this doesn't exist nor node_name=this doesn't exist"}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} - +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'luks', 'file': "this doesn't exist", 'size': 67108864}}} +{u'return': {}} +Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} === Zero size === -Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -object secret,id=keysec0,data=foo -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'iter-time': 10, 'driver': 'luks', 'file': 'node0', 'size': 0}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "key-secret": "keysec0"} +image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_IMG"}, "key-secret": "keysec0"} file format: IMGFMT virtual size: 0 (0 bytes) +encrypted: yes +Format specific information: + ivgen alg: plain64 + hash alg: sha256 + cipher alg: aes-256 + uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX + cipher mode: xts + slots: + [0]: + active: true + iters: XXX + key offset: 4096 + stripes: 4000 + [1]: + active: false + key offset: 262144 + [2]: + active: false + key offset: 520192 + [3]: + active: false + key offset: 778240 + [4]: + active: false + key offset: 1036288 + [5]: + active: false + key offset: 1294336 + [6]: + active: false + key offset: 1552384 + [7]: + active: false + key offset: 1810432 + payload offset: 2068480 + master key iters: XXX === Invalid sizes === -Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -object secret,id=keysec0,data=foo -QMP_VERSION -{"return": {}} -{"error": {"class": "GenericError", "desc": "The requested file size is too large"}} -{"error": {"class": "GenericError", "desc": "The requested file size is too large"}} -{"error": {"class": "GenericError", "desc": "The requested file size is too large"}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'driver': 'luks', 'file': 'node0', 'size': 18446744073709551104L}}} +{u'return': {}} +Job failed: The requested file size is too large +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'driver': 'luks', 'file': 'node0', 'size': 9223372036854775808L}}} +{u'return': {}} +Job failed: The requested file size is too large +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'driver': 'luks', 'file': 'node0', 'size': 9223372036854775296}}} +{u'return': {}} +Job failed: The requested file size is too large +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} === Resize image with invalid sizes === -Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -blockdev driver=IMGFMT,file=node0,key-secret=keysec0,node-name=node1 -object secret,id=keysec0,data=foo -QMP_VERSION -{"return": {}} -{"error": {"class": "GenericError", "desc": "The requested file size is too large"}} -{"error": {"class": "GenericError", "desc": "Invalid parameter type for 'size', expected: integer"}} -{"error": {"class": "GenericError", "desc": "Invalid parameter type for 'size', expected: integer"}} -{"error": {"class": "GenericError", "desc": "Parameter 'size' expects a >0 size"}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} - -image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "key-secret": "keysec0"} +{'execute': 'block_resize', 'arguments': {'size': 9223372036854775296, 'node_name': 'node1'}} +{u'error': {u'class': u'GenericError', u'desc': u'The requested file size is too large'}} +{'execute': 'block_resize', 'arguments': {'size': 9223372036854775808L, 'node_name': 'node1'}} +{u'error': {u'class': u'GenericError', u'desc': u"Invalid parameter type for 'size', expected: integer"}} +{'execute': 'block_resize', 'arguments': {'size': 18446744073709551104L, 'node_name': 'node1'}} +{u'error': {u'class': u'GenericError', u'desc': u"Invalid parameter type for 'size', expected: integer"}} +{'execute': 'block_resize', 'arguments': {'size': -9223372036854775808, 'node_name': 'node1'}} +{u'error': {u'class': u'GenericError', u'desc': u"Parameter 'size' expects a >0 size"}} +image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_IMG"}, "key-secret": "keysec0"} file format: IMGFMT virtual size: 0 (0 bytes) -*** done +encrypted: yes +Format specific information: + ivgen alg: plain64 + hash alg: sha256 + cipher alg: aes-256 + uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX + cipher mode: xts + slots: + [0]: + active: true + iters: XXX + key offset: 4096 + stripes: 4000 + [1]: + active: false + key offset: 262144 + [2]: + active: false + key offset: 520192 + [3]: + active: false + key offset: 778240 + [4]: + active: false + key offset: 1036288 + [5]: + active: false + key offset: 1294336 + [6]: + active: false + key offset: 1552384 + [7]: + active: false + key offset: 1810432 + payload offset: 2068480 + master key iters: XXX + diff --git a/tests/qemu-iotests/211 b/tests/qemu-iotests/211 index 1edec26517..7b7985db6c 100755 --- a/tests/qemu-iotests/211 +++ b/tests/qemu-iotests/211 @@ -1,9 +1,11 @@ -#!/bin/bash +#!/usr/bin/env python # # Test VDI and file image creation # # Copyright (C) 2018 Red Hat, Inc. # +# Creator/Owner: Kevin Wolf <kwolf@redhat.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 @@ -18,229 +20,154 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -# creator -owner=kwolf@redhat.com - -seq=`basename $0` -echo "QA output created by $seq" - -here=`pwd` -status=1 # failure is the default! - -# get standard environment, filters and checks -. ./common.rc -. ./common.filter - -_supported_fmt vdi -_supported_proto file -_supported_os Linux - -function do_run_qemu() -{ - echo Testing: "$@" - $QEMU -nographic -qmp stdio -serial none "$@" - echo -} - -function run_qemu() -{ - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ - | _filter_qemu | _filter_imgfmt \ - | _filter_actual_image_size -} - -echo -echo "=== Successful image creation (defaults) ===" -echo - -size=$((128 * 1024 * 1024)) - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "file", - "filename": "$TEST_IMG", - "size": 0 - } -} -{ "execute": "blockdev-add", - "arguments": { - "driver": "file", - "node-name": "imgfile", - "filename": "$TEST_IMG" - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "imgfile", - "size": $size - } -} -{ "execute": "quit" } -EOF - -_img_info --format-specific | _filter_img_info --format-specific -$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map - -echo -echo "=== Successful image creation (explicit defaults) ===" -echo - -# Choose a different size to show that we got a new image -size=$((64 * 1024 * 1024)) - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "file", - "filename": "$TEST_IMG", - "size": 0 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - }, - "size": $size, - "preallocation": "off" - } -} -{ "execute": "quit" } -EOF - -_img_info --format-specific | _filter_img_info --format-specific -$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map - -echo -echo "=== Successful image creation (with non-default options) ===" -echo - -# Choose a different size to show that we got a new image -size=$((32 * 1024 * 1024)) - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "file", - "filename": "$TEST_IMG", - "size": 0 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - }, - "size": $size, - "preallocation": "metadata" - } -} -{ "execute": "quit" } -EOF - -_img_info --format-specific | _filter_img_info --format-specific -$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map - -echo -echo "=== Invalid BlockdevRef ===" -echo - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "this doesn't exist", - "size": $size - } -} -{ "execute": "quit" } -EOF - -echo -echo "=== Zero size ===" -echo - -run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 0 - } -} -{ "execute": "quit" } -EOF - -_img_info | _filter_img_info - -echo -echo "=== Maximum size ===" -echo - -run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 562949819203584 - } -} -{ "execute": "quit" } -EOF - -_img_info | _filter_img_info - -echo -echo "=== Invalid sizes ===" -echo - -# TODO Negative image sizes aren't handled correctly, but this is a problem -# with QAPI's implementation of the 'size' type and affects other commands as -# well. Once this is fixed, we may want to add a test case here. - -# 1. 2^64 - 512 -# 2. 2^63 = 8 EB (qemu-img enforces image sizes less than this) -# 3. 0x1fffff8000001 (one byte more than maximum image size for VDI) - -run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 18446744073709551104 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 9223372036854775808 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 562949819203585 - } -} -{ "execute": "quit" } -EOF - -# success, all done -echo "*** done" -rm -f $seq.full -status=0 +import iotests +from iotests import imgfmt + +iotests.verify_image_format(supported_fmts=['vdi']) +iotests.verify_protocol(supported=['file']) + +def blockdev_create(vm, options): + result = vm.qmp_log('blockdev-create', job_id='job0', options=options) + + if 'return' in result: + assert result['return'] == {} + vm.run_job('job0') + iotests.log("") + +with iotests.FilePath('t.vdi') as disk_path, \ + iotests.VM() as vm: + + # + # Successful image creation (defaults) + # + iotests.log("=== Successful image creation (defaults) ===") + iotests.log("") + + size = 128 * 1024 * 1024 + + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) + + vm.qmp_log('blockdev-add', driver='file', filename=disk_path, + node_name='imgfile') + + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'imgfile', + 'size': size }) + vm.shutdown() + + iotests.img_info_log(disk_path) + iotests.log(iotests.qemu_img_pipe('map', '--output=json', disk_path)) + + # + # Successful image creation (explicit defaults) + # + iotests.log("=== Successful image creation (explicit defaults) ===") + iotests.log("") + + size = 64 * 1024 * 1024 + + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) + blockdev_create(vm, { 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'preallocation': 'off' }) + vm.shutdown() + + iotests.img_info_log(disk_path) + iotests.log(iotests.qemu_img_pipe('map', '--output=json', disk_path)) + + # + # Successful image creation (with non-default options) + # + iotests.log("=== Successful image creation (with non-default options) ===") + iotests.log("") + + size = 32 * 1024 * 1024 + + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) + blockdev_create(vm, { 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'preallocation': 'metadata' }) + vm.shutdown() + + iotests.img_info_log(disk_path) + iotests.log(iotests.qemu_img_pipe('map', '--output=json', disk_path)) + + # + # Invalid BlockdevRef + # + iotests.log("=== Invalid BlockdevRef ===") + iotests.log("") + + vm.launch() + blockdev_create(vm, { 'driver': imgfmt, + 'file': "this doesn't exist", + 'size': size }) + vm.shutdown() + + # + # Zero size + # + iotests.log("=== Zero size ===") + iotests.log("") + + vm.add_blockdev('driver=file,filename=%s,node-name=node0' % (disk_path)) + vm.launch() + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': 0 }) + vm.shutdown() + + iotests.img_info_log(disk_path) + + # + # Maximum size + # + iotests.log("=== Maximum size ===") + iotests.log("") + + vm.launch() + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': 562949819203584 }) + vm.shutdown() + + iotests.img_info_log(disk_path) + + # + # Invalid sizes + # + + # TODO Negative image sizes aren't handled correctly, but this is a problem + # with QAPI's implementation of the 'size' type and affects other commands + # as well. Once this is fixed, we may want to add a test case here. + + # 1. 2^64 - 512 + # 2. 2^63 = 8 EB (qemu-img enforces image sizes less than this) + # 3. 0x1fffff8000001 (one byte more than maximum image size for VDI) + + iotests.log("=== Invalid sizes ===") + iotests.log("") + + vm.launch() + for size in [ 18446744073709551104, 9223372036854775808, 562949819203585 ]: + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': size }) + vm.shutdown() diff --git a/tests/qemu-iotests/211.out b/tests/qemu-iotests/211.out index 3247bbaa64..6feaea3978 100644 --- a/tests/qemu-iotests/211.out +++ b/tests/qemu-iotests/211.out @@ -1,97 +1,112 @@ -QA output created by 211 - === Successful image creation (defaults) === -Testing: -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}} +{u'return': {}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'imgfile', 'size': 134217728}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: TEST_DIR/t.IMGFMT +image: TEST_IMG file format: IMGFMT virtual size: 128M (134217728 bytes) +cluster_size: 1048576 + [{ "start": 0, "length": 134217728, "depth": 0, "zero": true, "data": false}] === Successful image creation (explicit defaults) === -Testing: -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: TEST_DIR/t.IMGFMT +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'preallocation': 'off', 'driver': 'vdi', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}, 'size': 67108864}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +image: TEST_IMG file format: IMGFMT virtual size: 64M (67108864 bytes) +cluster_size: 1048576 + [{ "start": 0, "length": 67108864, "depth": 0, "zero": true, "data": false}] === Successful image creation (with non-default options) === -Testing: -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: TEST_DIR/t.IMGFMT +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'preallocation': 'metadata', 'driver': 'vdi', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}, 'size': 33554432}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +image: TEST_IMG file format: IMGFMT virtual size: 32M (33554432 bytes) -[{ "start": 0, "length": 3072, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 3072, "length": 33551360, "depth": 0, "zero": true, "data": true, "offset": OFFSET}] +cluster_size: 1048576 -=== Invalid BlockdevRef === +[{ "start": 0, "length": 3072, "depth": 0, "zero": false, "data": true, "offset": 1024}, +{ "start": 3072, "length": 33551360, "depth": 0, "zero": true, "data": true, "offset": 4096}] -Testing: -QMP_VERSION -{"return": {}} -{"error": {"class": "GenericError", "desc": "Cannot find device=this doesn't exist nor node_name=this doesn't exist"}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +=== Invalid BlockdevRef === +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': "this doesn't exist", 'size': 33554432}}} +{u'return': {}} +Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} === Zero size === -Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 0}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: TEST_DIR/t.IMGFMT +image: TEST_IMG file format: IMGFMT virtual size: 0 (0 bytes) +cluster_size: 1048576 === Maximum size === -Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 562949819203584}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: TEST_DIR/t.IMGFMT +image: TEST_IMG file format: IMGFMT virtual size: 512T (562949819203584 bytes) +cluster_size: 1048576 === Invalid sizes === -Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -QMP_VERSION -{"return": {}} -{"error": {"class": "GenericError", "desc": "Unsupported VDI image size (size is 0xfffffffffffffe00, max supported is 0x1fffff8000000)"}} -{"error": {"class": "GenericError", "desc": "Unsupported VDI image size (size is 0x8000000000000000, max supported is 0x1fffff8000000)"}} -{"error": {"class": "GenericError", "desc": "Unsupported VDI image size (size is 0x1fffff8000001, max supported is 0x1fffff8000000)"}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 18446744073709551104L}}} +{u'return': {}} +Job failed: Unsupported VDI image size (size is 0xfffffffffffffe00, max supported is 0x1fffff8000000) +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 9223372036854775808L}}} +{u'return': {}} +Job failed: Unsupported VDI image size (size is 0x8000000000000000, max supported is 0x1fffff8000000) +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 562949819203585}}} +{u'return': {}} +Job failed: Unsupported VDI image size (size is 0x1fffff8000001, max supported is 0x1fffff8000000) +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -*** done diff --git a/tests/qemu-iotests/212 b/tests/qemu-iotests/212 index e5a1ba77ce..95c8810d83 100755 --- a/tests/qemu-iotests/212 +++ b/tests/qemu-iotests/212 @@ -1,9 +1,11 @@ -#!/bin/bash +#!/usr/bin/env python # # Test parallels and file image creation # # Copyright (C) 2018 Red Hat, Inc. # +# Creator/Owner: Kevin Wolf <kwolf@redhat.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 @@ -18,309 +20,176 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -# creator -owner=kwolf@redhat.com - -seq=`basename $0` -echo "QA output created by $seq" - -here=`pwd` -status=1 # failure is the default! - -# get standard environment, filters and checks -. ./common.rc -. ./common.filter - -_supported_fmt parallels -_supported_proto file -_supported_os Linux - -function do_run_qemu() -{ - echo Testing: "$@" - $QEMU -nographic -qmp stdio -serial none "$@" - echo -} - -function run_qemu() -{ - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ - | _filter_qemu | _filter_imgfmt \ - | _filter_actual_image_size -} - -echo -echo "=== Successful image creation (defaults) ===" -echo - -size=$((128 * 1024 * 1024)) - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "file", - "filename": "$TEST_IMG", - "size": 0 - } -} -{ "execute": "blockdev-add", - "arguments": { - "driver": "file", - "node-name": "imgfile", - "filename": "$TEST_IMG" - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "imgfile", - "size": $size - } -} -{ "execute": "quit" } -EOF - -_img_info --format-specific | _filter_img_info --format-specific - -echo -echo "=== Successful image creation (explicit defaults) ===" -echo - -# Choose a different size to show that we got a new image -size=$((64 * 1024 * 1024)) - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "file", - "filename": "$TEST_IMG", - "size": 0 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - }, - "size": $size, - "cluster-size": 1048576 - } -} -{ "execute": "quit" } -EOF - -_img_info --format-specific | _filter_img_info --format-specific - -echo -echo "=== Successful image creation (with non-default options) ===" -echo - -# Choose a different size to show that we got a new image -size=$((32 * 1024 * 1024)) - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "file", - "filename": "$TEST_IMG", - "size": 0 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - }, - "size": $size, - "cluster-size": 65536 - } -} -{ "execute": "quit" } -EOF - -_img_info --format-specific | _filter_img_info --format-specific - -echo -echo "=== Invalid BlockdevRef ===" -echo - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "this doesn't exist", - "size": $size - } -} -{ "execute": "quit" } -EOF - -echo -echo "=== Zero size ===" -echo - -run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 0 - } -} -{ "execute": "quit" } -EOF - -_img_info | _filter_img_info - -echo -echo "=== Maximum size ===" -echo - -run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 4503599627369984 - } -} -{ "execute": "quit" } -EOF - -_img_info | _filter_img_info - -echo -echo "=== Invalid sizes ===" -echo - -# TODO Negative image sizes aren't handled correctly, but this is a problem -# with QAPI's implementation of the 'size' type and affects other commands as -# well. Once this is fixed, we may want to add a test case here. - -# 1. Misaligned image size -# 2. 2^64 - 512 -# 3. 2^63 = 8 EB (qemu-img enforces image sizes less than this) -# 4. 2^63 - 512 (generally valid, but with the image header the file will -# exceed 63 bits) -# 5. 2^52 (512 bytes more than maximum image size) - -run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 1234 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 18446744073709551104 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 9223372036854775808 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 9223372036854775296 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 4503599627370497 - } -} -{ "execute": "quit" } -EOF - -echo -echo "=== Invalid cluster size ===" -echo - -run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "cluster-size": 1234 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "cluster-size": 128 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "cluster-size": 4294967296 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "cluster-size": 9223372036854775808 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "cluster-size": 18446744073709551104 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "cluster-size": 0 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 281474976710656, - "cluster-size": 512 - } -} -{ "execute": "quit" } -EOF - - -# success, all done -echo "*** done" -rm -f $seq.full -status=0 +import iotests +from iotests import imgfmt + +iotests.verify_image_format(supported_fmts=['parallels']) +iotests.verify_protocol(supported=['file']) + +def blockdev_create(vm, options): + result = vm.qmp_log('blockdev-create', job_id='job0', options=options) + + if 'return' in result: + assert result['return'] == {} + vm.run_job('job0') + iotests.log("") + +with iotests.FilePath('t.parallels') as disk_path, \ + iotests.VM() as vm: + + # + # Successful image creation (defaults) + # + iotests.log("=== Successful image creation (defaults) ===") + iotests.log("") + + size = 128 * 1024 * 1024 + + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) + + vm.qmp_log('blockdev-add', driver='file', filename=disk_path, + node_name='imgfile') + + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'imgfile', + 'size': size }) + vm.shutdown() + + iotests.img_info_log(disk_path) + + # + # Successful image creation (explicit defaults) + # + iotests.log("=== Successful image creation (explicit defaults) ===") + iotests.log("") + + # Choose a different size to show that we got a new image + size = 64 * 1024 * 1024 + + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) + blockdev_create(vm, { 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'cluster-size': 1048576 }) + vm.shutdown() + + iotests.img_info_log(disk_path) + + # + # Successful image creation (with non-default options) + # + iotests.log("=== Successful image creation (with non-default options) ===") + iotests.log("") + + # Choose a different size to show that we got a new image + size = 32 * 1024 * 1024 + + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) + blockdev_create(vm, { 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'cluster-size': 65536 }) + vm.shutdown() + + iotests.img_info_log(disk_path) + + # + # Invalid BlockdevRef + # + iotests.log("=== Invalid BlockdevRef ===") + iotests.log("") + + vm.launch() + blockdev_create(vm, { 'driver': imgfmt, + 'file': "this doesn't exist", + 'size': size }) + vm.shutdown() + + # + # Zero size + # + iotests.log("=== Zero size ===") + iotests.log("") + + vm.add_blockdev('driver=file,filename=%s,node-name=node0' % (disk_path)) + vm.launch() + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': 0 }) + vm.shutdown() + + iotests.img_info_log(disk_path) + + # + # Maximum size + # + iotests.log("=== Maximum size ===") + iotests.log("") + + vm.launch() + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': 4503599627369984}) + vm.shutdown() + + iotests.img_info_log(disk_path) + + # + # Invalid sizes + # + + # TODO Negative image sizes aren't handled correctly, but this is a problem + # with QAPI's implementation of the 'size' type and affects other commands + # as well. Once this is fixed, we may want to add a test case here. + + # 1. Misaligned image size + # 2. 2^64 - 512 + # 3. 2^63 = 8 EB (qemu-img enforces image sizes less than this) + # 4. 2^63 - 512 (generally valid, but with the image header the file will + # exceed 63 bits) + # 5. 2^52 (512 bytes more than maximum image size) + + iotests.log("=== Invalid sizes ===") + iotests.log("") + + vm.launch() + for size in [ 1234, 18446744073709551104, 9223372036854775808, + 9223372036854775296, 4503599627370497 ]: + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': size }) + vm.shutdown() + + # + # Invalid cluster size + # + iotests.log("=== Invalid cluster size ===") + iotests.log("") + + vm.launch() + for csize in [ 1234, 128, 4294967296, 9223372036854775808, + 18446744073709551104, 0 ]: + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': 67108864, + 'cluster-size': csize }) + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': 281474976710656, + 'cluster-size': 512 }) + vm.shutdown() diff --git a/tests/qemu-iotests/212.out b/tests/qemu-iotests/212.out index 587de6fad0..9150da7a2c 100644 --- a/tests/qemu-iotests/212.out +++ b/tests/qemu-iotests/212.out @@ -1,111 +1,156 @@ -QA output created by 212 - === Successful image creation (defaults) === -Testing: -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}} +{u'return': {}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'imgfile', 'size': 134217728}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: TEST_DIR/t.IMGFMT +image: TEST_IMG file format: IMGFMT virtual size: 128M (134217728 bytes) === Successful image creation (explicit defaults) === -Testing: -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: TEST_DIR/t.IMGFMT +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 1048576, 'driver': 'parallels', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}, 'size': 67108864}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +image: TEST_IMG file format: IMGFMT virtual size: 64M (67108864 bytes) === Successful image creation (with non-default options) === -Testing: -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 65536, 'driver': 'parallels', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}, 'size': 33554432}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: TEST_DIR/t.IMGFMT +image: TEST_IMG file format: IMGFMT virtual size: 32M (33554432 bytes) === Invalid BlockdevRef === -Testing: -QMP_VERSION -{"return": {}} -{"error": {"class": "GenericError", "desc": "Cannot find device=this doesn't exist nor node_name=this doesn't exist"}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} - +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': "this doesn't exist", 'size': 33554432}}} +{u'return': {}} +Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} === Zero size === -Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 0}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: TEST_DIR/t.IMGFMT +image: TEST_IMG file format: IMGFMT virtual size: 0 (0 bytes) === Maximum size === -Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 4503599627369984}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: TEST_DIR/t.IMGFMT +image: TEST_IMG file format: IMGFMT virtual size: 4096T (4503599627369984 bytes) === Invalid sizes === -Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -QMP_VERSION -{"return": {}} -{"error": {"class": "GenericError", "desc": "Image size must be a multiple of 512 bytes"}} -{"error": {"class": "GenericError", "desc": "Image size is too large for this cluster size"}} -{"error": {"class": "GenericError", "desc": "Image size is too large for this cluster size"}} -{"error": {"class": "GenericError", "desc": "Image size is too large for this cluster size"}} -{"error": {"class": "GenericError", "desc": "Image size is too large for this cluster size"}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} - +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 1234}}} +{u'return': {}} +Job failed: Image size must be a multiple of 512 bytes +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 18446744073709551104L}}} +{u'return': {}} +Job failed: Image size is too large for this cluster size +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 9223372036854775808L}}} +{u'return': {}} +Job failed: Image size is too large for this cluster size +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 9223372036854775296}}} +{u'return': {}} +Job failed: Image size is too large for this cluster size +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 4503599627370497}}} +{u'return': {}} +Job failed: Image size is too large for this cluster size +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} === Invalid cluster size === -Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -QMP_VERSION -{"return": {}} -{"error": {"class": "GenericError", "desc": "Cluster size must be a multiple of 512 bytes"}} -{"error": {"class": "GenericError", "desc": "Cluster size must be a multiple of 512 bytes"}} -{"error": {"class": "GenericError", "desc": "Cluster size is too large"}} -{"error": {"class": "GenericError", "desc": "Cluster size is too large"}} -{"error": {"class": "GenericError", "desc": "Cluster size is too large"}} -{"error": {"class": "GenericError", "desc": "Image size is too large for this cluster size"}} -{"error": {"class": "GenericError", "desc": "Image size is too large for this cluster size"}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} - -*** done +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 1234, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Cluster size must be a multiple of 512 bytes +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 128, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Cluster size must be a multiple of 512 bytes +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 4294967296, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Cluster size is too large +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 9223372036854775808L, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Cluster size is too large +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 18446744073709551104L, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Cluster size is too large +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 0, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Image size is too large for this cluster size +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 512, 'driver': 'parallels', 'file': 'node0', 'size': 281474976710656}}} +{u'return': {}} +Job failed: Image size is too large for this cluster size +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + diff --git a/tests/qemu-iotests/213 b/tests/qemu-iotests/213 index 3a00a0f6d6..4054439e3c 100755 --- a/tests/qemu-iotests/213 +++ b/tests/qemu-iotests/213 @@ -1,9 +1,11 @@ -#!/bin/bash +#!/usr/bin/env python # # Test vhdx and file image creation # # Copyright (C) 2018 Red Hat, Inc. # +# Creator/Owner: Kevin Wolf <kwolf@redhat.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 @@ -18,332 +20,190 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -# creator -owner=kwolf@redhat.com - -seq=`basename $0` -echo "QA output created by $seq" - -here=`pwd` -status=1 # failure is the default! - -# get standard environment, filters and checks -. ./common.rc -. ./common.filter - -_supported_fmt vhdx -_supported_proto file -_supported_os Linux - -function do_run_qemu() -{ - echo Testing: "$@" - $QEMU -nographic -qmp stdio -serial none "$@" - echo -} - -function run_qemu() -{ - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ - | _filter_qemu | _filter_imgfmt \ - | _filter_actual_image_size -} - -echo -echo "=== Successful image creation (defaults) ===" -echo - -size=$((128 * 1024 * 1024)) - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "file", - "filename": "$TEST_IMG", - "size": 0 - } -} -{ "execute": "blockdev-add", - "arguments": { - "driver": "file", - "node-name": "imgfile", - "filename": "$TEST_IMG" - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "imgfile", - "size": $size - } -} -{ "execute": "quit" } -EOF - -_img_info --format-specific | _filter_img_info --format-specific - -echo -echo "=== Successful image creation (explicit defaults) ===" -echo - -# Choose a different size to show that we got a new image -size=$((64 * 1024 * 1024)) - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "file", - "filename": "$TEST_IMG", - "size": 0 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - }, - "size": $size, - "log-size": 1048576, - "block-size": 8388608, - "subformat": "dynamic", - "block-state-zero": true - } -} -{ "execute": "quit" } -EOF - -_img_info --format-specific | _filter_img_info --format-specific - -echo -echo "=== Successful image creation (with non-default options) ===" -echo - -# Choose a different size to show that we got a new image -size=$((32 * 1024 * 1024)) - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "file", - "filename": "$TEST_IMG", - "size": 0 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": { - "driver": "file", - "filename": "$TEST_IMG" - }, - "size": $size, - "log-size": 8388608, - "block-size": 268435456, - "subformat": "fixed", - "block-state-zero": false - } -} -{ "execute": "quit" } -EOF - -_img_info --format-specific | _filter_img_info --format-specific - -echo -echo "=== Invalid BlockdevRef ===" -echo - -run_qemu <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "this doesn't exist", - "size": $size - } -} -{ "execute": "quit" } -EOF - -echo -echo "=== Zero size ===" -echo - -run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 0 - } -} -{ "execute": "quit" } -EOF - -_img_info | _filter_img_info - -echo -echo "=== Maximum size ===" -echo - -run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 70368744177664 - } -} -{ "execute": "quit" } -EOF - -_img_info | _filter_img_info - -echo -echo "=== Invalid sizes ===" -echo - -# TODO Negative image sizes aren't handled correctly, but this is a problem -# with QAPI's implementation of the 'size' type and affects other commands as -# well. Once this is fixed, we may want to add a test case here. - -# 1. 2^64 - 512 -# 2. 2^63 = 8 EB (qemu-img enforces image sizes less than this) -# 3. 2^63 - 512 (generally valid, but with the image header the file will -# exceed 63 bits) -# 4. 2^46 + 1 (one byte more than maximum image size) - -run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 18446744073709551104 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 9223372036854775808 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 9223372036854775296 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 70368744177665 - } -} -{ "execute": "quit" } -EOF - -echo -echo "=== Invalid block size ===" -echo - -run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "block-size": 1234567 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "block-size": 128 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "block-size": 3145728 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "block-size": 536870912 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "block-size": 0 - } -} -{ "execute": "quit" } -EOF - -echo -echo "=== Invalid log size ===" -echo - -run_qemu -blockdev driver=file,filename="$TEST_IMG",node-name=node0 <<EOF -{ "execute": "qmp_capabilities" } -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "log-size": 1234567 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "log-size": 128 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "log-size": 4294967296 - } -} -{ "execute": "x-blockdev-create", - "arguments": { - "driver": "$IMGFMT", - "file": "node0", - "size": 67108864, - "log-size": 0 - } -} -{ "execute": "quit" } -EOF - - -# success, all done -echo "*** done" -rm -f $seq.full -status=0 +import iotests +from iotests import imgfmt + +iotests.verify_image_format(supported_fmts=['vhdx']) +iotests.verify_protocol(supported=['file']) + +def blockdev_create(vm, options): + result = vm.qmp_log('blockdev-create', job_id='job0', options=options) + + if 'return' in result: + assert result['return'] == {} + vm.run_job('job0') + iotests.log("") + +with iotests.FilePath('t.vhdx') as disk_path, \ + iotests.VM() as vm: + + # + # Successful image creation (defaults) + # + iotests.log("=== Successful image creation (defaults) ===") + iotests.log("") + + size = 128 * 1024 * 1024 + + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) + + vm.qmp_log('blockdev-add', driver='file', filename=disk_path, + node_name='imgfile') + + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'imgfile', + 'size': size }) + vm.shutdown() + + iotests.img_info_log(disk_path) + + # + # Successful image creation (explicit defaults) + # + iotests.log("=== Successful image creation (explicit defaults) ===") + iotests.log("") + + # Choose a different size to show that we got a new image + size = 64 * 1024 * 1024 + + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) + blockdev_create(vm, { 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'log-size': 1048576, + 'block-size': 8388608, + 'subformat': 'dynamic', + 'block-state-zero': True }) + vm.shutdown() + + iotests.img_info_log(disk_path) + + # + # Successful image creation (with non-default options) + # + iotests.log("=== Successful image creation (with non-default options) ===") + iotests.log("") + + # Choose a different size to show that we got a new image + size = 32 * 1024 * 1024 + + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) + blockdev_create(vm, { 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'log-size': 8388608, + 'block-size': 268435456, + 'subformat': 'fixed', + 'block-state-zero': False }) + vm.shutdown() + + iotests.img_info_log(disk_path) + + # + # Invalid BlockdevRef + # + iotests.log("=== Invalid BlockdevRef ===") + iotests.log("") + + vm.launch() + blockdev_create(vm, { 'driver': imgfmt, + 'file': "this doesn't exist", + 'size': size }) + vm.shutdown() + + # + # Zero size + # + iotests.log("=== Zero size ===") + iotests.log("") + + vm.add_blockdev('driver=file,filename=%s,node-name=node0' % (disk_path)) + vm.launch() + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': 0 }) + vm.shutdown() + + iotests.img_info_log(disk_path) + + # + # Maximum size + # + iotests.log("=== Maximum size ===") + iotests.log("") + + vm.launch() + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': 70368744177664 }) + vm.shutdown() + + iotests.img_info_log(disk_path) + + # + # Invalid sizes + # + + # TODO Negative image sizes aren't handled correctly, but this is a problem + # with QAPI's implementation of the 'size' type and affects other commands + # as well. Once this is fixed, we may want to add a test case here. + + # 1. 2^64 - 512 + # 2. 2^63 = 8 EB (qemu-img enforces image sizes less than this) + # 3. 2^63 - 512 (generally valid, but with the image header the file will + # exceed 63 bits) + # 4. 2^46 + 1 (one byte more than maximum image size) + + iotests.log("=== Invalid sizes ===") + iotests.log("") + + vm.launch() + for size in [ 18446744073709551104, 9223372036854775808, + 9223372036854775296, 70368744177665 ]: + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': size }) + vm.shutdown() + + # + # Invalid block size + # + iotests.log("=== Invalid block size ===") + iotests.log("") + + vm.launch() + for bsize in [ 1234567, 128, 3145728, 536870912, 0 ]: + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': 67108864, + 'block-size': bsize }) + vm.shutdown() + + # + # Invalid log size + # + iotests.log("=== Invalid log size ===") + iotests.log("") + + vm.launch() + for lsize in [ 1234567, 128, 4294967296, 0 ]: + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': 67108864, + 'log-size': lsize }) + vm.shutdown() diff --git a/tests/qemu-iotests/213.out b/tests/qemu-iotests/213.out index 8e8fc29cbc..e1dcd47201 100644 --- a/tests/qemu-iotests/213.out +++ b/tests/qemu-iotests/213.out @@ -1,121 +1,169 @@ -QA output created by 213 - === Successful image creation (defaults) === -Testing: -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}} +{u'return': {}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'imgfile', 'size': 134217728}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: TEST_DIR/t.IMGFMT +image: TEST_IMG file format: IMGFMT virtual size: 128M (134217728 bytes) +cluster_size: 8388608 === Successful image creation (explicit defaults) === -Testing: -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: TEST_DIR/t.IMGFMT +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'block-size': 8388608, 'driver': 'vhdx', 'subformat': 'dynamic', 'log-size': 1048576, 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}, 'block-state-zero': True, 'size': 67108864}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +image: TEST_IMG file format: IMGFMT virtual size: 64M (67108864 bytes) +cluster_size: 8388608 === Successful image creation (with non-default options) === -Testing: -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'block-size': 268435456, 'driver': 'vhdx', 'subformat': 'fixed', 'log-size': 8388608, 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}, 'block-state-zero': False, 'size': 33554432}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: TEST_DIR/t.IMGFMT +image: TEST_IMG file format: IMGFMT virtual size: 32M (33554432 bytes) +cluster_size: 268435456 === Invalid BlockdevRef === -Testing: -QMP_VERSION -{"return": {}} -{"error": {"class": "GenericError", "desc": "Cannot find device=this doesn't exist nor node_name=this doesn't exist"}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} - +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': "this doesn't exist", 'size': 33554432}}} +{u'return': {}} +Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} === Zero size === -Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 0}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: TEST_DIR/t.IMGFMT +image: TEST_IMG file format: IMGFMT virtual size: 0 (0 bytes) +cluster_size: 8388608 === Maximum size === -Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -QMP_VERSION -{"return": {}} -{"return": {}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 70368744177664}}} +{u'return': {}} +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} -image: TEST_DIR/t.IMGFMT +image: TEST_IMG file format: IMGFMT virtual size: 64T (70368744177664 bytes) +cluster_size: 67108864 === Invalid sizes === -Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -QMP_VERSION -{"return": {}} -{"error": {"class": "GenericError", "desc": "Image size too large; max of 64TB"}} -{"error": {"class": "GenericError", "desc": "Image size too large; max of 64TB"}} -{"error": {"class": "GenericError", "desc": "Image size too large; max of 64TB"}} -{"error": {"class": "GenericError", "desc": "Image size too large; max of 64TB"}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} - +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 18446744073709551104L}}} +{u'return': {}} +Job failed: Image size too large; max of 64TB +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 9223372036854775808L}}} +{u'return': {}} +Job failed: Image size too large; max of 64TB +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 9223372036854775296}}} +{u'return': {}} +Job failed: Image size too large; max of 64TB +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 70368744177665}}} +{u'return': {}} +Job failed: Image size too large; max of 64TB +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} === Invalid block size === -Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -QMP_VERSION -{"return": {}} -{"error": {"class": "GenericError", "desc": "Block size must be a multiple of 1 MB"}} -{"error": {"class": "GenericError", "desc": "Block size must be a multiple of 1 MB"}} -{"error": {"class": "GenericError", "desc": "Block size must be a power of two"}} -{"error": {"class": "GenericError", "desc": "Block size must not exceed 268435456"}} -{"error": {"class": "GenericError", "desc": "Block size must be a multiple of 1 MB"}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} - +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 1234567, 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Block size must be a multiple of 1 MB +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 128, 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Block size must be a multiple of 1 MB +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 3145728, 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Block size must be a power of two +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 536870912, 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Block size must not exceed 268435456 +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 0, 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Block size must be a multiple of 1 MB +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} === Invalid log size === -Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -QMP_VERSION -{"return": {}} -{"error": {"class": "GenericError", "desc": "Log size must be a multiple of 1 MB"}} -{"error": {"class": "GenericError", "desc": "Log size must be a multiple of 1 MB"}} -{"error": {"class": "GenericError", "desc": "Log size must be smaller than 4 GB"}} -{"error": {"class": "GenericError", "desc": "Log size must be a multiple of 1 MB"}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} - -*** done +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 1234567, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Log size must be a multiple of 1 MB +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 128, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Log size must be a multiple of 1 MB +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 4294967296, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Log size must be smaller than 4 GB +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + +{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 0, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} +{u'return': {}} +Job failed: Log size must be a multiple of 1 MB +{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} +{u'return': {}} + diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 28159d837a..fdbdd8b300 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -109,6 +109,20 @@ def qemu_img_pipe(*args): sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args)))) return subp.communicate()[0] +def img_info_log(filename, filter_path=None, imgopts=False, extra_args=[]): + args = [ 'info' ] + if imgopts: + args.append('--image-opts') + else: + args += [ '-f', imgfmt ] + args += extra_args + args.append(filename) + + output = qemu_img_pipe(*args) + if not filter_path: + filter_path = filename + log(filter_img_info(output, filter_path)) + def qemu_io(*args): '''Run qemu-io and return the stdout data''' args = qemu_io_args + list(args) @@ -206,6 +220,22 @@ def filter_qmp_event(event): event['timestamp']['microseconds'] = 'USECS' return event +def filter_testfiles(msg): + prefix = os.path.join(test_dir, "%s-" % (os.getpid())) + return msg.replace(prefix, 'TEST_DIR/PID-') + +def filter_img_info(output, filename): + lines = [] + for line in output.split('\n'): + if 'disk size' in line or 'actual-size' in line: + continue + line = line.replace(filename, 'TEST_IMG') \ + .replace(imgfmt, 'IMGFMT') + line = re.sub('iters: [0-9]+', 'iters: XXX', line) + line = re.sub('uuid: [-a-f0-9]+', 'uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', line) + lines.append(line) + return '\n'.join(lines) + def log(msg, filters=[]): for flt in filters: msg = flt(msg) @@ -281,6 +311,13 @@ def file_path(*names): return paths[0] if len(paths) == 1 else paths +def remote_filename(path): + if imgproto == 'file': + return path + elif imgproto == 'ssh': + return "ssh://127.0.0.1%s" % (path) + else: + raise Exception("Protocol %s not supported" % (imgproto)) class VM(qtest.QEMUQtestMachine): '''A QEMU VM''' @@ -383,6 +420,37 @@ class VM(qtest.QEMUQtestMachine): output_list += [key + '=' + obj[key]] return ','.join(output_list) + def get_qmp_events_filtered(self, wait=True): + result = [] + for ev in self.get_qmp_events(wait=wait): + result.append(filter_qmp_event(ev)) + return result + + def qmp_log(self, cmd, filters=[filter_testfiles], **kwargs): + logmsg = "{'execute': '%s', 'arguments': %s}" % (cmd, kwargs) + log(logmsg, filters) + result = self.qmp(cmd, **kwargs) + log(str(result), filters) + return result + + def run_job(self, job, auto_finalize=True, auto_dismiss=False): + while True: + for ev in self.get_qmp_events_filtered(wait=True): + if ev['event'] == 'JOB_STATUS_CHANGE': + status = ev['data']['status'] + if status == 'aborting': + result = self.qmp('query-jobs') + for j in result['return']: + if j['id'] == job: + log('Job failed: %s' % (j['error'])) + elif status == 'pending' and not auto_finalize: + self.qmp_log('job-finalize', id=job) + elif status == 'concluded' and not auto_dismiss: + self.qmp_log('job-dismiss', id=job) + elif status == 'null': + return + else: + iotests.log(ev) index_re = re.compile(r'([^\[]+)\[([^\]]+)\]') @@ -548,6 +616,16 @@ def verify_image_format(supported_fmts=[], unsupported_fmts=[]): if not_sup or (imgfmt in unsupported_fmts): notrun('not suitable for this image format: %s' % imgfmt) +def verify_protocol(supported=[], unsupported=[]): + assert not (supported and unsupported) + + if 'generic' in supported: + return + + not_sup = supported and (imgproto not in supported) + if not_sup or (imgproto in unsupported): + notrun('not suitable for this protocol: %s' % imgproto) + def verify_platform(supported_oses=['linux']): if True not in [sys.platform.startswith(x) for x in supported_oses]: notrun('not suitable for this OS: %s' % sys.platform) diff --git a/tests/qmp-test.c b/tests/qmp-test.c index 88f867f8c0..a49cbc6fde 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -392,6 +392,45 @@ static void add_query_tests(QmpSchema *schema) } } +static void test_qmp_preconfig(void) +{ + QDict *rsp, *ret; + QTestState *qs = qtest_startf("%s --preconfig", common_args); + + /* preconfig state */ + /* enabled commands, no error expected */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-commands' }"))); + + /* forbidden commands, expected error */ + g_assert(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-cpus' }"))); + + /* check that query-status returns preconfig state */ + rsp = qtest_qmp(qs, "{ 'execute': 'query-status' }"); + ret = qdict_get_qdict(rsp, "return"); + g_assert(ret); + g_assert_cmpstr(qdict_get_try_str(ret, "status"), ==, "preconfig"); + qobject_unref(rsp); + + /* exit preconfig state */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'exit-preconfig' }"))); + qtest_qmp_eventwait(qs, "RESUME"); + + /* check that query-status returns running state */ + rsp = qtest_qmp(qs, "{ 'execute': 'query-status' }"); + ret = qdict_get_qdict(rsp, "return"); + g_assert(ret); + g_assert_cmpstr(qdict_get_try_str(ret, "status"), ==, "running"); + qobject_unref(rsp); + + /* check that exit-preconfig returns error after exiting preconfig */ + g_assert(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'exit-preconfig' }"))); + + /* enabled commands, no error expected */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-cpus' }"))); + + qtest_quit(qs); +} + int main(int argc, char *argv[]) { QmpSchema schema; @@ -403,6 +442,7 @@ int main(int argc, char *argv[]) qtest_add_func("qmp/oob", test_qmp_oob); qmp_schema_init(&schema); add_query_tests(&schema); + qtest_add_func("qmp/preconfig", test_qmp_preconfig); ret = g_test_run(); diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 2cba63b881..a11c4cfbf2 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -498,7 +498,7 @@ typedef struct TestBlockJob { static void test_job_completed(Job *job, void *opaque) { - job_completed(job, 0); + job_completed(job, 0, NULL); } static void coroutine_fn test_job_start(void *opaque) diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index fce836639a..58d9b87fb2 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -34,7 +34,7 @@ static void test_block_job_complete(Job *job, void *opaque) rc = -ECANCELED; } - job_completed(job, rc); + job_completed(job, rc, NULL); bdrv_unref(bs); } diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index e408d52351..cb42f06e61 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -167,7 +167,7 @@ static void cancel_job_completed(Job *job, void *opaque) { CancelJob *s = opaque; s->completed = true; - job_completed(job, 0); + job_completed(job, 0, NULL); } static void cancel_job_complete(Job *job, Error **errp) diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index e0ed461f58..491b0c4a44 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -16,7 +16,7 @@ void qmp_user_def_cmd(Error **errp) { } -void qmp_an_oob_command(Error **errp) +void qmp_test_flags_command(Error **errp) { } diff --git a/tests/tpm-crb-swtpm-test.c b/tests/tpm-crb-swtpm-test.c new file mode 100644 index 0000000000..c2bde0cbaa --- /dev/null +++ b/tests/tpm-crb-swtpm-test.c @@ -0,0 +1,247 @@ +/* + * QTest testcase for TPM CRB talking to external swtpm and swtpm migration + * + * Copyright (c) 2018 IBM Corporation + * with parts borrowed from migration-test.c that is: + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Stefan Berger <stefanb@linux.vnet.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 <glib/gstdio.h> + +#include "hw/acpi/tpm.h" +#include "io/channel-socket.h" +#include "libqtest.h" +#include "tpm-util.h" +#include "sysemu/tpm.h" +#include "qapi/qmp/qdict.h" + +typedef struct TestState { + char *src_tpm_path; + char *dst_tpm_path; + char *uri; +} TestState; + +bool got_stop; + +static void migrate(QTestState *who, const char *uri) +{ + QDict *rsp; + gchar *cmd; + + cmd = g_strdup_printf("{ 'execute': 'migrate'," + "'arguments': { 'uri': '%s' } }", + uri); + rsp = qtest_qmp(who, cmd); + g_free(cmd); + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); +} + +/* + * Events can get in the way of responses we are actually waiting for. + */ +static QDict *wait_command(QTestState *who, const char *command) +{ + const char *event_string; + QDict *response; + + response = qtest_qmp(who, command); + + while (qdict_haskey(response, "event")) { + /* OK, it was an event */ + event_string = qdict_get_str(response, "event"); + if (!strcmp(event_string, "STOP")) { + got_stop = true; + } + qobject_unref(response); + response = qtest_qmp_receive(who); + } + return response; +} + +static void wait_for_migration_complete(QTestState *who) +{ + while (true) { + QDict *rsp, *rsp_return; + bool completed; + const char *status; + + rsp = wait_command(who, "{ 'execute': 'query-migrate' }"); + rsp_return = qdict_get_qdict(rsp, "return"); + status = qdict_get_str(rsp_return, "status"); + completed = strcmp(status, "completed") == 0; + g_assert_cmpstr(status, !=, "failed"); + qobject_unref(rsp); + if (completed) { + return; + } + usleep(1000); + } +} + +static void migration_start_qemu(QTestState **src_qemu, QTestState **dst_qemu, + SocketAddress *src_tpm_addr, + SocketAddress *dst_tpm_addr, + const char *miguri) +{ + char *src_qemu_args, *dst_qemu_args; + + src_qemu_args = g_strdup_printf( + "-chardev socket,id=chr,path=%s " + "-tpmdev emulator,id=dev,chardev=chr " + "-device tpm-crb,tpmdev=dev ", + src_tpm_addr->u.q_unix.path); + + *src_qemu = qtest_init(src_qemu_args); + + dst_qemu_args = g_strdup_printf( + "-chardev socket,id=chr,path=%s " + "-tpmdev emulator,id=dev,chardev=chr " + "-device tpm-crb,tpmdev=dev " + "-incoming %s", + dst_tpm_addr->u.q_unix.path, + miguri); + + *dst_qemu = qtest_init(dst_qemu_args); + + free(src_qemu_args); + free(dst_qemu_args); +} + +static void tpm_crb_swtpm_test(const void *data) +{ + char *args = NULL; + QTestState *s; + SocketAddress *addr = NULL; + gboolean succ; + GPid swtpm_pid; + GError *error = NULL; + const TestState *ts = data; + + succ = tpm_util_swtpm_start(ts->src_tpm_path, &swtpm_pid, &addr, &error); + /* succ may be false if swtpm is not available */ + if (!succ) { + return; + } + + args = g_strdup_printf( + "-chardev socket,id=chr,path=%s " + "-tpmdev emulator,id=dev,chardev=chr " + "-device tpm-crb,tpmdev=dev", + addr->u.q_unix.path); + + s = qtest_start(args); + g_free(args); + + tpm_util_startup(s, tpm_util_crb_transfer); + tpm_util_pcrextend(s, tpm_util_crb_transfer); + + unsigned char tpm_pcrread_resp[] = + "\x80\x01\x00\x00\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00" + "\x00\x01\x00\x0b\x03\x00\x04\x00\x00\x00\x00\x01\x00\x20\xf6\x85" + "\x98\xe5\x86\x8d\xe6\x8b\x97\x29\x99\x60\xf2\x71\x7d\x17\x67\x89" + "\xa4\x2f\x9a\xae\xa8\xc7\xb7\xaa\x79\xa8\x62\x56\xc1\xde"; + tpm_util_pcrread(s, tpm_util_crb_transfer, tpm_pcrread_resp, + sizeof(tpm_pcrread_resp)); + + qtest_end(); + tpm_util_swtpm_kill(swtpm_pid); + + if (addr) { + g_unlink(addr->u.q_unix.path); + qapi_free_SocketAddress(addr); + } +} + +static void tpm_crb_swtpm_migration_test(const void *data) +{ + const TestState *ts = data; + gboolean succ; + GPid src_tpm_pid, dst_tpm_pid; + SocketAddress *src_tpm_addr = NULL, *dst_tpm_addr = NULL; + GError *error = NULL; + QTestState *src_qemu, *dst_qemu; + + succ = tpm_util_swtpm_start(ts->src_tpm_path, &src_tpm_pid, + &src_tpm_addr, &error); + /* succ may be false if swtpm is not available */ + if (!succ) { + return; + } + + succ = tpm_util_swtpm_start(ts->dst_tpm_path, &dst_tpm_pid, + &dst_tpm_addr, &error); + /* succ may be false if swtpm is not available */ + if (!succ) { + goto err_src_tpm_kill; + } + + migration_start_qemu(&src_qemu, &dst_qemu, src_tpm_addr, dst_tpm_addr, + ts->uri); + + tpm_util_startup(src_qemu, tpm_util_crb_transfer); + tpm_util_pcrextend(src_qemu, tpm_util_crb_transfer); + + unsigned char tpm_pcrread_resp[] = + "\x80\x01\x00\x00\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00" + "\x00\x01\x00\x0b\x03\x00\x04\x00\x00\x00\x00\x01\x00\x20\xf6\x85" + "\x98\xe5\x86\x8d\xe6\x8b\x97\x29\x99\x60\xf2\x71\x7d\x17\x67\x89" + "\xa4\x2f\x9a\xae\xa8\xc7\xb7\xaa\x79\xa8\x62\x56\xc1\xde"; + tpm_util_pcrread(src_qemu, tpm_util_crb_transfer, tpm_pcrread_resp, + sizeof(tpm_pcrread_resp)); + + migrate(src_qemu, ts->uri); + wait_for_migration_complete(src_qemu); + + tpm_util_pcrread(dst_qemu, tpm_util_crb_transfer, tpm_pcrread_resp, + sizeof(tpm_pcrread_resp)); + + qtest_quit(dst_qemu); + qtest_quit(src_qemu); + + tpm_util_swtpm_kill(dst_tpm_pid); + if (dst_tpm_addr) { + g_unlink(dst_tpm_addr->u.q_unix.path); + qapi_free_SocketAddress(dst_tpm_addr); + } + +err_src_tpm_kill: + tpm_util_swtpm_kill(src_tpm_pid); + if (src_tpm_addr) { + g_unlink(src_tpm_addr->u.q_unix.path); + qapi_free_SocketAddress(src_tpm_addr); + } +} + +int main(int argc, char **argv) +{ + int ret; + TestState ts = { 0 }; + + ts.src_tpm_path = g_dir_make_tmp("qemu-tpm-crb-swtpm-test.XXXXXX", NULL); + ts.dst_tpm_path = g_dir_make_tmp("qemu-tpm-crb-swtpm-test.XXXXXX", NULL); + ts.uri = g_strdup_printf("unix:%s/migsocket", ts.src_tpm_path); + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + + qtest_add_data_func("/tpm/crb-swtpm/test", &ts, tpm_crb_swtpm_test); + qtest_add_data_func("/tpm/crb-swtpm-migration/test", &ts, + tpm_crb_swtpm_migration_test); + ret = g_test_run(); + + g_rmdir(ts.dst_tpm_path); + g_free(ts.dst_tpm_path); + g_rmdir(ts.src_tpm_path); + g_free(ts.src_tpm_path); + g_free(ts.uri); + + return ret; +} diff --git a/tests/tpm-util.c b/tests/tpm-util.c new file mode 100644 index 0000000000..c9b3947c1c --- /dev/null +++ b/tests/tpm-util.c @@ -0,0 +1,186 @@ +/* + * QTest TPM utilities + * + * Copyright (c) 2018 IBM Corporation + * Copyright (c) 2018 Red Hat, Inc. + * + * Authors: + * Stefan Berger <stefanb@linux.vnet.ibm.com> + * Marc-AndrĂ© Lureau <marcandre.lureau@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. + */ + +#include "qemu/osdep.h" + +#include "hw/acpi/tpm.h" +#include "libqtest.h" +#include "tpm-util.h" + +void tpm_util_crb_transfer(QTestState *s, + const unsigned char *req, size_t req_size, + unsigned char *rsp, size_t rsp_size) +{ + uint64_t caddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR); + uint64_t raddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR); + + qtest_writeb(s, TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 1); + + qtest_memwrite(s, caddr, req, req_size); + + uint32_t sts, start = 1; + uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; + qtest_writel(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START, start); + while (true) { + start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START); + if ((start & 1) == 0) { + break; + } + if (g_get_monotonic_time() >= end_time) { + break; + } + }; + start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START); + g_assert_cmpint(start & 1, ==, 0); + sts = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); + g_assert_cmpint(sts & 1, ==, 0); + + qtest_memread(s, raddr, rsp, rsp_size); +} + +void tpm_util_startup(QTestState *s, tx_func *tx) +{ + unsigned char buffer[1024]; + unsigned char tpm_startup[] = + "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00"; + unsigned char tpm_startup_resp[] = + "\x80\x01\x00\x00\x00\x0a\x00\x00\x00\x00"; + + tx(s, tpm_startup, sizeof(tpm_startup), buffer, sizeof(buffer)); + + g_assert_cmpmem(buffer, sizeof(tpm_startup_resp), + tpm_startup_resp, sizeof(tpm_startup_resp)); +} + +void tpm_util_pcrextend(QTestState *s, tx_func *tx) +{ + unsigned char buffer[1024]; + unsigned char tpm_pcrextend[] = + "\x80\x02\x00\x00\x00\x41\x00\x00\x01\x82\x00\x00\x00\x0a\x00\x00" + "\x00\x09\x40\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00" + "\x0b\x74\x65\x73\x74\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00"; + + unsigned char tpm_pcrextend_resp[] = + "\x80\x02\x00\x00\x00\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x01\x00\x00"; + + tx(s, tpm_pcrextend, sizeof(tpm_pcrextend), buffer, sizeof(buffer)); + + g_assert_cmpmem(buffer, sizeof(tpm_pcrextend_resp), + tpm_pcrextend_resp, sizeof(tpm_pcrextend_resp)); +} + +void tpm_util_pcrread(QTestState *s, tx_func *tx, + const unsigned char *exp_resp, size_t exp_resp_size) +{ + unsigned char buffer[1024]; + unsigned char tpm_pcrread[] = + "\x80\x01\x00\x00\x00\x14\x00\x00\x01\x7e\x00\x00\x00\x01\x00\x0b" + "\x03\x00\x04\x00"; + + tx(s, tpm_pcrread, sizeof(tpm_pcrread), buffer, sizeof(buffer)); + + g_assert_cmpmem(buffer, exp_resp_size, exp_resp, exp_resp_size); +} + +static gboolean tpm_util_swtpm_has_tpm2(void) +{ + gint mystdout; + gboolean succ; + unsigned i; + char buffer[10240]; + ssize_t n; + gchar *swtpm_argv[] = { + g_strdup("swtpm"), g_strdup("socket"), g_strdup("--help"), NULL + }; + + succ = g_spawn_async_with_pipes(NULL, swtpm_argv, NULL, + G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, + NULL, &mystdout, NULL, NULL); + if (!succ) { + goto cleanup; + } + + n = read(mystdout, buffer, sizeof(buffer) - 1); + if (n < 0) { + goto cleanup; + } + buffer[n] = 0; + if (!strstr(buffer, "--tpm2")) { + succ = false; + } + + cleanup: + for (i = 0; swtpm_argv[i]; i++) { + g_free(swtpm_argv[i]); + } + + return succ; +} + +gboolean tpm_util_swtpm_start(const char *path, GPid *pid, + SocketAddress **addr, GError **error) +{ + char *swtpm_argv_tpmstate = g_strdup_printf("dir=%s", path); + char *swtpm_argv_ctrl = g_strdup_printf("type=unixio,path=%s/sock", + path); + gchar *swtpm_argv[] = { + g_strdup("swtpm"), g_strdup("socket"), + g_strdup("--tpmstate"), swtpm_argv_tpmstate, + g_strdup("--ctrl"), swtpm_argv_ctrl, + g_strdup("--tpm2"), + NULL + }; + gboolean succ; + unsigned i; + + succ = tpm_util_swtpm_has_tpm2(); + if (!succ) { + goto cleanup; + } + + *addr = g_new0(SocketAddress, 1); + (*addr)->type = SOCKET_ADDRESS_TYPE_UNIX; + (*addr)->u.q_unix.path = g_build_filename(path, "sock", NULL); + + succ = g_spawn_async(NULL, swtpm_argv, NULL, G_SPAWN_SEARCH_PATH, + NULL, NULL, pid, error); + +cleanup: + for (i = 0; swtpm_argv[i]; i++) { + g_free(swtpm_argv[i]); + } + + return succ; +} + +void tpm_util_swtpm_kill(GPid pid) +{ + int n; + + if (!pid) { + return; + } + + g_spawn_close_pid(pid); + + n = kill(pid, 0); + if (n < 0) { + return; + } + + kill(pid, SIGKILL); +} diff --git a/tests/tpm-util.h b/tests/tpm-util.h new file mode 100644 index 0000000000..d155d99aea --- /dev/null +++ b/tests/tpm-util.h @@ -0,0 +1,36 @@ +/* + * QTest TPM utilities + * + * Copyright (c) 2018 IBM Corporation + * + * Authors: + * Stefan Berger <stefanb@linux.vnet.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. + */ + +#ifndef TESTS_TPM_UTIL_H +#define TESTS_TPM_UTIL_H + +#include "qemu/osdep.h" +#include "io/channel-socket.h" + +typedef void (tx_func)(QTestState *s, + const unsigned char *req, size_t req_size, + unsigned char *rsp, size_t rsp_size); + +void tpm_util_crb_transfer(QTestState *s, + const unsigned char *req, size_t req_size, + unsigned char *rsp, size_t rsp_size); + +void tpm_util_startup(QTestState *s, tx_func *tx); +void tpm_util_pcrextend(QTestState *s, tx_func *tx); +void tpm_util_pcrread(QTestState *s, tx_func *tx, + const unsigned char *exp_resp, size_t exp_resp_size); + +gboolean tpm_util_swtpm_start(const char *path, GPid *pid, + SocketAddress **addr, GError **error); +void tpm_util_swtpm_kill(GPid pid); + +#endif /* TESTS_TPM_UTIL_H */ diff --git a/ui/cocoa.m b/ui/cocoa.m index 110b393e4e..2991ed4f19 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -44,6 +44,9 @@ #ifndef MAC_OS_X_VERSION_10_6 #define MAC_OS_X_VERSION_10_6 1060 #endif +#ifndef MAC_OS_X_VERSION_10_9 +#define MAC_OS_X_VERSION_10_9 1090 +#endif #ifndef MAC_OS_X_VERSION_10_10 #define MAC_OS_X_VERSION_10_10 101000 #endif @@ -79,6 +82,13 @@ #define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask #define NSWindowStyleMaskTitled NSTitledWindowMask #endif +/* 10.13 deprecates NSFileHandlingPanelOKButton in favour of + * NSModalResponseOK, which was introduced in 10.9. Define + * it for older versions. + */ +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9 +#define NSModalResponseOK NSFileHandlingPanelOKButton +#endif //#define DEBUG @@ -1218,7 +1228,7 @@ QemuCocoaView *cocoaView; [openPanel setCanChooseFiles: YES]; [openPanel setAllowsMultipleSelection: NO]; [openPanel setAllowedFileTypes: supportedImageFileTypes]; - if([openPanel runModal] == NSFileHandlingPanelOKButton) { + if([openPanel runModal] == NSModalResponseOK) { NSString * file = [[[openPanel URLs] objectAtIndex: 0] path]; if(file == nil) { NSBeep(); @@ -561,7 +561,7 @@ static int default_driver_check(void *opaque, QemuOpts *opts, Error **errp) /***********************************************************/ /* QEMU state */ -static RunState current_run_state = RUN_STATE_PRELAUNCH; +static RunState current_run_state = RUN_STATE_PRECONFIG; /* We use RUN_STATE__MAX but any invalid value will do */ static RunState vmstop_requested = RUN_STATE__MAX; @@ -574,6 +574,13 @@ typedef struct { static const RunStateTransition runstate_transitions_def[] = { /* from -> to */ + { RUN_STATE_PRECONFIG, RUN_STATE_PRELAUNCH }, + /* Early switch to inmigrate state to allow -incoming CLI option work + * as it used to. TODO: delay actual switching to inmigrate state to + * the point after machine is built and remove this hack. + */ + { RUN_STATE_PRECONFIG, RUN_STATE_INMIGRATE }, + { RUN_STATE_DEBUG, RUN_STATE_RUNNING }, { RUN_STATE_DEBUG, RUN_STATE_FINISH_MIGRATE }, { RUN_STATE_DEBUG, RUN_STATE_PRELAUNCH }, @@ -1515,6 +1522,7 @@ static pid_t shutdown_pid; static int powerdown_requested; static int debug_requested; static int suspend_requested; +static bool preconfig_exit_requested = true; static WakeupReason wakeup_reason; static NotifierList powerdown_notifiers = NOTIFIER_LIST_INITIALIZER(powerdown_notifiers); @@ -1599,6 +1607,11 @@ static int qemu_debug_requested(void) return r; } +void qemu_exit_preconfig_request(void) +{ + preconfig_exit_requested = true; +} + /* * Reset the VM. Issue an event unless @reason is SHUTDOWN_CAUSE_NONE. */ @@ -1772,6 +1785,13 @@ static bool main_loop_should_exit(void) RunState r; ShutdownCause request; + if (preconfig_exit_requested) { + if (runstate_check(RUN_STATE_PRECONFIG)) { + runstate_set(RUN_STATE_PRELAUNCH); + } + preconfig_exit_requested = false; + return true; + } if (qemu_debug_requested()) { vm_stop(RUN_STATE_DEBUG); } @@ -3551,6 +3571,9 @@ int main(int argc, char **argv, char **envp) exit(1); } break; + case QEMU_OPTION_preconfig: + preconfig_exit_requested = false; + break; case QEMU_OPTION_enable_kvm: olist = qemu_find_opts("machine"); qemu_opts_parse_noisily(olist, "accel=kvm", false); @@ -3922,6 +3945,12 @@ int main(int argc, char **argv, char **envp) replay_configure(icount_opts); + if (incoming && !preconfig_exit_requested) { + error_report("'preconfig' and 'incoming' options are " + "mutually exclusive"); + exit(EXIT_FAILURE); + } + machine_class = select_machine(); set_memory_options(&ram_slots, &maxram_size, machine_class); @@ -4441,6 +4470,10 @@ int main(int argc, char **argv, char **envp) } parse_numa_opts(current_machine); + /* do monitor/qmp handling at preconfig state if requested */ + main_loop(); + + /* from here on runstate is RUN_STATE_PRELAUNCH */ machine_run_board_init(current_machine); realtime_init(); |