diff options
278 files changed, 11770 insertions, 2121 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 81a2960b1a..900437dd2a 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -40,7 +40,7 @@ macos_xcode_task: script: - mkdir build - cd build - - ../configure --extra-cflags='-Wno-error=deprecated-declarations' + - ../configure --extra-cflags='-Wno-error=deprecated-declarations' --enable-modules --enable-werror --cc=clang || { cat config.log meson-logs/meson-log.txt; exit 1; } - gmake -j$(sysctl -n hw.ncpu) - gmake check V=1 diff --git a/.gitlab-ci.d/check-patch.py b/.gitlab-ci.d/check-patch.py index 5a14a25b13..0ff30ee077 100755 --- a/.gitlab-ci.d/check-patch.py +++ b/.gitlab-ci.d/check-patch.py @@ -33,8 +33,16 @@ ancestor = subprocess.check_output(["git", "merge-base", ancestor = ancestor.strip() +log = subprocess.check_output(["git", "log", "--format=%H %s", + ancestor + "..."], + universal_newlines=True) + subprocess.check_call(["git", "remote", "rm", "check-patch"]) +if log == "": + print("\nNo commits since %s, skipping checks\n" % ancestor) + sys.exit(0) + errors = False print("\nChecking all commits since %s...\n" % ancestor) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 66ad7aa5c2..3b15ae5c30 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -24,6 +24,7 @@ include: image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest before_script: - JOBS=$(expr $(nproc) + 1) + - sed -i s,git.qemu.org/git,gitlab.com/qemu-project, .gitmodules script: - mkdir build - cd build @@ -66,6 +67,7 @@ include: - if [ -d ${CI_PROJECT_DIR}/avocado-cache ]; then du -chs ${CI_PROJECT_DIR}/avocado-cache ; fi + - export AVOCADO_ALLOW_UNTRUSTED_CODE=1 after_script: - cd build - python3 -c 'import json; r = json.load(open("tests/results/latest/results.json")); [print(t["logfile"]) for t in r["tests"] if t["status"] not in ("PASS", "SKIP", "CANCEL")]' | xargs cat diff --git a/MAINTAINERS b/MAINTAINERS index 6a197bd358..8c744a9bdf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -755,6 +755,7 @@ L: qemu-arm@nongnu.org S: Supported F: hw/*/npcm7xx* F: include/hw/*/npcm7xx* +F: tests/qtest/npcm7xx* F: pc-bios/npcm7xx_bootrom.bin F: roms/vbootrom @@ -1967,6 +1968,12 @@ F: docs/specs/vmgenid.txt F: tests/qtest/vmgenid-test.c F: stubs/vmgenid.c +LED +M: Philippe Mathieu-Daudé <f4bug@amsat.org> +S: Maintained +F: include/hw/misc/led.h +F: hw/misc/led.c + Unimplemented device M: Peter Maydell <peter.maydell@linaro.org> R: Philippe Mathieu-Daudé <f4bug@amsat.org> @@ -3069,6 +3076,15 @@ L: qemu-block@nongnu.org S: Supported F: tests/image-fuzzer/ +Vhost-user block device backend server +M: Coiby Xu <Coiby.Xu@gmail.com> +S: Maintained +F: block/export/vhost-user-blk-server.c +F: block/export/vhost-user-blk-server.h +F: include/qemu/vhost-user-server.h +F: tests/qtest/libqos/vhost-user-blk.c +F: util/vhost-user-server.c + Replication M: Wen Congyang <wencongyang2@huawei.com> M: Xie Changlong <xiechanglong.d@gmail.com> @@ -92,50 +92,75 @@ endif ifeq ($(NINJA),) .PHONY: config-host.mak x := $(shell rm -rf meson-private meson-info meson-logs) +else +export NINJA endif ifeq ($(wildcard build.ninja),) .PHONY: config-host.mak x := $(shell rm -rf meson-private meson-info meson-logs) endif +ifeq ($(origin prefix),file) +.PHONY: config-host.mak +x := $(shell rm -rf meson-private meson-info meson-logs) +endif # 1. ensure config-host.mak is up-to-date config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/pc-bios $(SRC_PATH)/VERSION - @echo $@ is out-of-date, running configure + @echo config-host.mak is out-of-date, running configure @if test -f meson-private/coredata.dat; then \ ./config.status --skip-meson; \ else \ - ./config.status; \ + ./config.status && touch build.ninja.stamp; \ fi -# 2. ensure generated build files are up-to-date +# 2. meson.stamp exists if meson has run at least once (so ninja reconfigure +# works), but otherwise never needs to be updated +meson-private/coredata.dat: meson.stamp +meson.stamp: config-host.mak + @touch meson.stamp + +# 3. ensure generated build files are up-to-date ifneq ($(NINJA),) -# A separate rule is needed for Makefile dependencies to avoid -n -export NINJA Makefile.ninja: build.ninja - $(quiet-@){ echo 'ninja-targets = \'; $(NINJA) -t targets all | sed 's/:.*//; $$!s/$$/ \\/'; } > $@ + $(quiet-@){ \ + echo 'ninja-targets = \'; \ + $(NINJA) -t targets all | sed 's/:.*//; $$!s/$$/ \\/'; \ + echo 'build-files = \'; \ + $(NINJA) -t query build.ninja | sed -n '1,/^ input:/d; /^ outputs:/q; s/$$/ \\/p'; \ + } > $@.tmp && mv $@.tmp $@ -include Makefile.ninja + +# A separate rule is needed for Makefile dependencies to avoid -n +build.ninja: build.ninja.stamp +build.ninja.stamp: meson.stamp $(build-files) + $(NINJA) $(if $V,-v,) build.ninja && touch $@ endif ifneq ($(MESON),) -# The dependency on config-host.mak ensures that meson has run -Makefile.mtest: build.ninja scripts/mtest2make.py config-host.mak +Makefile.mtest: build.ninja scripts/mtest2make.py $(MESON) introspect --targets --tests --benchmarks | $(PYTHON) scripts/mtest2make.py > $@ -include Makefile.mtest endif -# 3. Rules to bridge to other makefiles +# 4. Rules to bridge to other makefiles ifneq ($(NINJA),) -NINJAFLAGS = $(if $V,-v,) \ +MAKE.n = $(findstring n,$(firstword $(MAKEFLAGS))) +MAKE.k = $(findstring k,$(firstword $(MAKEFLAGS))) +MAKE.q = $(findstring q,$(firstword $(MAKEFLAGS))) +MAKE.nq = $(if $(word 2, $(MAKE.n) $(MAKE.q)),nq) +NINJAFLAGS = $(if $V,-v) $(if $(MAKE.n), -n) $(if $(MAKE.k), -k0) \ $(filter-out -j, $(lastword -j1 $(filter -l% -j%, $(MAKEFLAGS)))) \ - $(subst -k, -k0, $(filter -n -k,$(MAKEFLAGS))) ninja-cmd-goals = $(or $(MAKECMDGOALS), all) ninja-cmd-goals += $(foreach t, $(.tests), $(.test.deps.$t)) makefile-targets := build.ninja ctags TAGS cscope dist clean uninstall -ninja-targets := $(filter-out $(makefile-targets), $(ninja-targets)) +# "ninja -t targets" also lists all prerequisites. If build system +# files are marked as PHONY, however, Make will always try to execute +# "ninja build.ninja". +ninja-targets := $(filter-out $(build-files) $(makefile-targets), $(ninja-targets)) .PHONY: $(ninja-targets) run-ninja $(ninja-targets): run-ninja @@ -143,7 +168,8 @@ $(ninja-targets): run-ninja # --output-sync line. run-ninja: config-host.mak ifneq ($(filter $(ninja-targets), $(ninja-cmd-goals)),) - +@$(NINJA) $(NINJAFLAGS) $(sort $(filter $(ninja-targets), $(ninja-cmd-goals))) | cat + +$(quiet-@)$(if $(MAKE.nq),@:, $(NINJA) \ + $(NINJAFLAGS) $(sort $(filter $(ninja-targets), $(ninja-cmd-goals))) | cat) endif endif @@ -214,7 +240,7 @@ distclean: clean rm -f qemu-plugins-ld.symbols qemu-plugins-ld64.symbols rm -f *-config-target.h *-config-devices.mak *-config-devices.h rm -rf meson-private meson-logs meson-info compile_commands.json - rm -f Makefile.ninja Makefile.mtest + rm -f Makefile.ninja Makefile.mtest build.ninja.stamp meson.stamp rm -f config.log rm -f linux-headers/asm rm -Rf .sdk diff --git a/accel/qtest/qtest-cpus.c b/accel/dummy-cpus.c index 7c5399ed9d..10429fdfb2 100644 --- a/accel/qtest/qtest-cpus.c +++ b/accel/dummy-cpus.c @@ -1,5 +1,5 @@ /* - * QTest accelerator code + * Dummy cpu thread code * * Copyright IBM, Corp. 2011 * @@ -13,26 +13,13 @@ #include "qemu/osdep.h" #include "qemu/rcu.h" -#include "qapi/error.h" -#include "qemu/module.h" -#include "qemu/option.h" -#include "qemu/config-file.h" -#include "sysemu/accel.h" -#include "sysemu/qtest.h" #include "sysemu/cpus.h" -#include "sysemu/cpu-timers.h" #include "qemu/guest-random.h" #include "qemu/main-loop.h" #include "hw/core/cpu.h" -#include "qtest-cpus.h" - -static void *qtest_cpu_thread_fn(void *arg) +static void *dummy_cpu_thread_fn(void *arg) { -#ifdef _WIN32 - error_report("qtest is not supported under Windows"); - exit(1); -#else CPUState *cpu = arg; sigset_t waitset; int r; @@ -69,10 +56,9 @@ static void *qtest_cpu_thread_fn(void *arg) qemu_mutex_unlock_iothread(); rcu_unregister_thread(); return NULL; -#endif } -static void qtest_start_vcpu_thread(CPUState *cpu) +void dummy_start_vcpu_thread(CPUState *cpu) { char thread_name[VCPU_THREAD_NAME_SIZE]; @@ -81,11 +67,6 @@ static void qtest_start_vcpu_thread(CPUState *cpu) qemu_cond_init(cpu->halt_cond); snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/DUMMY", cpu->cpu_index); - qemu_thread_create(cpu->thread, thread_name, qtest_cpu_thread_fn, cpu, + qemu_thread_create(cpu->thread, thread_name, dummy_cpu_thread_fn, cpu, QEMU_THREAD_JOINABLE); } - -const CpusAccel qtest_cpus = { - .create_vcpu_thread = qtest_start_vcpu_thread, - .get_virtual_clock = qtest_get_virtual_clock, -}; diff --git a/accel/meson.build b/accel/meson.build index bb00d0fd13..b26cca227a 100644 --- a/accel/meson.build +++ b/accel/meson.build @@ -5,3 +5,11 @@ subdir('kvm') subdir('tcg') subdir('xen') subdir('stubs') + +dummy_ss = ss.source_set() +dummy_ss.add(files( + 'dummy-cpus.c', +)) + +specific_ss.add_all(when: ['CONFIG_SOFTMMU', 'CONFIG_POSIX'], if_true: dummy_ss) +specific_ss.add_all(when: ['CONFIG_XEN'], if_true: dummy_ss) diff --git a/accel/qtest/meson.build b/accel/qtest/meson.build index e477cb2ae2..a2f3276459 100644 --- a/accel/qtest/meson.build +++ b/accel/qtest/meson.build @@ -1,7 +1,6 @@ qtest_ss = ss.source_set() qtest_ss.add(files( 'qtest.c', - 'qtest-cpus.c', )) specific_ss.add_all(when: ['CONFIG_SOFTMMU', 'CONFIG_POSIX'], if_true: qtest_ss) diff --git a/accel/qtest/qtest-cpus.h b/accel/qtest/qtest-cpus.h deleted file mode 100644 index 739519a472..0000000000 --- a/accel/qtest/qtest-cpus.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Accelerator CPUS Interface - * - * Copyright 2020 SUSE LLC - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#ifndef QTEST_CPUS_H -#define QTEST_CPUS_H - -#include "sysemu/cpus.h" - -extern const CpusAccel qtest_cpus; - -#endif /* QTEST_CPUS_H */ diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c index 537e8b449c..b282cea5cf 100644 --- a/accel/qtest/qtest.c +++ b/accel/qtest/qtest.c @@ -25,7 +25,10 @@ #include "qemu/main-loop.h" #include "hw/core/cpu.h" -#include "qtest-cpus.h" +const CpusAccel qtest_cpus = { + .create_vcpu_thread = dummy_start_vcpu_thread, + .get_virtual_clock = qtest_get_virtual_clock, +}; static int qtest_init_accel(MachineState *ms) { diff --git a/accel/tcg/user-exec-stub.c b/accel/tcg/user-exec-stub.c index f6d8c8fb6f..b876f5c1e4 100644 --- a/accel/tcg/user-exec-stub.c +++ b/accel/tcg/user-exec-stub.c @@ -9,6 +9,10 @@ void cpu_resume(CPUState *cpu) { } +void cpu_remove_sync(CPUState *cpu) +{ +} + void qemu_init_vcpu(CPUState *cpu) { } diff --git a/accel/xen/xen-all.c b/accel/xen/xen-all.c index 60b971d0a8..878a4089d9 100644 --- a/accel/xen/xen-all.c +++ b/accel/xen/xen-all.c @@ -16,6 +16,7 @@ #include "hw/xen/xen_pt.h" #include "chardev/char.h" #include "sysemu/accel.h" +#include "sysemu/cpus.h" #include "sysemu/xen.h" #include "sysemu/runstate.h" #include "migration/misc.h" @@ -153,6 +154,10 @@ static void xen_setup_post(MachineState *ms, AccelState *accel) } } +const CpusAccel xen_cpus = { + .create_vcpu_thread = dummy_start_vcpu_thread, +}; + static int xen_init(MachineState *ms) { MachineClass *mc = MACHINE_GET_CLASS(ms); @@ -180,6 +185,9 @@ static int xen_init(MachineState *ms) * opt out of system RAM being allocated by generic code */ mc->default_ram_id = NULL; + + cpus_register_accel(&xen_cpus); + return 0; } @@ -4458,6 +4458,15 @@ static void bdrv_close(BlockDriverState *bs) } QLIST_INIT(&bs->aio_notifiers); bdrv_drained_end(bs); + + /* + * If we're still inside some bdrv_drain_all_begin()/end() sections, end + * them now since this BDS won't exist anymore when bdrv_drain_all_end() + * gets called. + */ + if (bs->quiesce_counter) { + bdrv_drain_all_end_quiesce(bs); + } } void bdrv_close_all(void) diff --git a/block/coroutines.h b/block/coroutines.h index f69179f5ef..1cb3128b94 100644 --- a/block/coroutines.h +++ b/block/coroutines.h @@ -41,6 +41,7 @@ bdrv_pwritev(BdrvChild *child, int64_t offset, unsigned int bytes, int coroutine_fn bdrv_co_common_block_status_above(BlockDriverState *bs, BlockDriverState *base, + bool include_base, bool want_zero, int64_t offset, int64_t bytes, @@ -50,6 +51,7 @@ bdrv_co_common_block_status_above(BlockDriverState *bs, int generated_co_wrapper bdrv_common_block_status_above(BlockDriverState *bs, BlockDriverState *base, + bool include_base, bool want_zero, int64_t offset, int64_t bytes, diff --git a/block/export/export.c b/block/export/export.c index f2c00d13bf..c3478c6c97 100644 --- a/block/export/export.c +++ b/block/export/export.c @@ -15,15 +15,22 @@ #include "block/block.h" #include "sysemu/block-backend.h" +#include "sysemu/iothread.h" #include "block/export.h" #include "block/nbd.h" #include "qapi/error.h" #include "qapi/qapi-commands-block-export.h" #include "qapi/qapi-events-block-export.h" #include "qemu/id.h" +#if defined(CONFIG_LINUX) && defined(CONFIG_VHOST_USER) +#include "vhost-user-blk-server.h" +#endif static const BlockExportDriver *blk_exp_drivers[] = { &blk_exp_nbd, +#if defined(CONFIG_LINUX) && defined(CONFIG_VHOST_USER) + &blk_exp_vhost_user_blk, +#endif }; /* Only accessed from the main thread */ @@ -57,10 +64,11 @@ static const BlockExportDriver *blk_exp_find_driver(BlockExportType type) BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp) { + bool fixed_iothread = export->has_fixed_iothread && export->fixed_iothread; const BlockExportDriver *drv; BlockExport *exp = NULL; BlockDriverState *bs; - BlockBackend *blk; + BlockBackend *blk = NULL; AioContext *ctx; uint64_t perm; int ret; @@ -96,6 +104,28 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp) ctx = bdrv_get_aio_context(bs); aio_context_acquire(ctx); + if (export->has_iothread) { + IOThread *iothread; + AioContext *new_ctx; + + iothread = iothread_by_id(export->iothread); + if (!iothread) { + error_setg(errp, "iothread \"%s\" not found", export->iothread); + goto fail; + } + + new_ctx = iothread_get_aio_context(iothread); + + ret = bdrv_try_set_aio_context(bs, new_ctx, errp); + if (ret == 0) { + aio_context_release(ctx); + aio_context_acquire(new_ctx); + ctx = new_ctx; + } else if (fixed_iothread) { + goto fail; + } + } + /* * Block exports are used for non-shared storage migration. Make sure * that BDRV_O_INACTIVE is cleared and the image is ready for write @@ -110,6 +140,11 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp) } blk = blk_new(ctx, perm, BLK_PERM_ALL); + + if (!fixed_iothread) { + blk_set_allow_aio_context_change(blk, true); + } + ret = blk_insert_bs(blk, bs, errp); if (ret < 0) { goto fail; diff --git a/block/export/meson.build b/block/export/meson.build index 558ef35d38..9fb4fbf81d 100644 --- a/block/export/meson.build +++ b/block/export/meson.build @@ -1 +1,2 @@ -block_ss.add(files('export.c')) +blockdev_ss.add(files('export.c')) +blockdev_ss.add(when: ['CONFIG_LINUX', 'CONFIG_VHOST_USER'], if_true: files('vhost-user-blk-server.c')) diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c new file mode 100644 index 0000000000..41f4933d6e --- /dev/null +++ b/block/export/vhost-user-blk-server.c @@ -0,0 +1,431 @@ +/* + * Sharing QEMU block devices via vhost-user protocal + * + * Parts of the code based on nbd/server.c. + * + * Copyright (c) Coiby Xu <coiby.xu@gmail.com>. + * Copyright (c) 2020 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "block/block.h" +#include "contrib/libvhost-user/libvhost-user.h" +#include "standard-headers/linux/virtio_blk.h" +#include "qemu/vhost-user-server.h" +#include "vhost-user-blk-server.h" +#include "qapi/error.h" +#include "qom/object_interfaces.h" +#include "sysemu/block-backend.h" +#include "util/block-helpers.h" + +enum { + VHOST_USER_BLK_NUM_QUEUES_DEFAULT = 1, +}; +struct virtio_blk_inhdr { + unsigned char status; +}; + +typedef struct VuBlkReq { + VuVirtqElement elem; + int64_t sector_num; + size_t size; + struct virtio_blk_inhdr *in; + struct virtio_blk_outhdr out; + VuServer *server; + struct VuVirtq *vq; +} VuBlkReq; + +/* vhost user block device */ +typedef struct { + BlockExport export; + VuServer vu_server; + uint32_t blk_size; + QIOChannelSocket *sioc; + struct virtio_blk_config blkcfg; + bool writable; +} VuBlkExport; + +static void vu_blk_req_complete(VuBlkReq *req) +{ + VuDev *vu_dev = &req->server->vu_dev; + + /* IO size with 1 extra status byte */ + vu_queue_push(vu_dev, req->vq, &req->elem, req->size + 1); + vu_queue_notify(vu_dev, req->vq); + + free(req); +} + +static int coroutine_fn +vu_blk_discard_write_zeroes(BlockBackend *blk, struct iovec *iov, + uint32_t iovcnt, uint32_t type) +{ + struct virtio_blk_discard_write_zeroes desc; + ssize_t size = iov_to_buf(iov, iovcnt, 0, &desc, sizeof(desc)); + if (unlikely(size != sizeof(desc))) { + error_report("Invalid size %zd, expect %zu", size, sizeof(desc)); + return -EINVAL; + } + + uint64_t range[2] = { le64_to_cpu(desc.sector) << 9, + le32_to_cpu(desc.num_sectors) << 9 }; + if (type == VIRTIO_BLK_T_DISCARD) { + if (blk_co_pdiscard(blk, range[0], range[1]) == 0) { + return 0; + } + } else if (type == VIRTIO_BLK_T_WRITE_ZEROES) { + if (blk_co_pwrite_zeroes(blk, range[0], range[1], 0) == 0) { + return 0; + } + } + + return -EINVAL; +} + +static void coroutine_fn vu_blk_virtio_process_req(void *opaque) +{ + VuBlkReq *req = opaque; + VuServer *server = req->server; + VuVirtqElement *elem = &req->elem; + uint32_t type; + + VuBlkExport *vexp = container_of(server, VuBlkExport, vu_server); + BlockBackend *blk = vexp->export.blk; + + struct iovec *in_iov = elem->in_sg; + struct iovec *out_iov = elem->out_sg; + unsigned in_num = elem->in_num; + unsigned out_num = elem->out_num; + + /* refer to hw/block/virtio_blk.c */ + if (elem->out_num < 1 || elem->in_num < 1) { + error_report("virtio-blk request missing headers"); + goto err; + } + + if (unlikely(iov_to_buf(out_iov, out_num, 0, &req->out, + sizeof(req->out)) != sizeof(req->out))) { + error_report("virtio-blk request outhdr too short"); + goto err; + } + + iov_discard_front(&out_iov, &out_num, sizeof(req->out)); + + if (in_iov[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) { + error_report("virtio-blk request inhdr too short"); + goto err; + } + + /* We always touch the last byte, so just see how big in_iov is. */ + req->in = (void *)in_iov[in_num - 1].iov_base + + in_iov[in_num - 1].iov_len + - sizeof(struct virtio_blk_inhdr); + iov_discard_back(in_iov, &in_num, sizeof(struct virtio_blk_inhdr)); + + type = le32_to_cpu(req->out.type); + switch (type & ~VIRTIO_BLK_T_BARRIER) { + case VIRTIO_BLK_T_IN: + case VIRTIO_BLK_T_OUT: { + ssize_t ret = 0; + bool is_write = type & VIRTIO_BLK_T_OUT; + req->sector_num = le64_to_cpu(req->out.sector); + + if (is_write && !vexp->writable) { + req->in->status = VIRTIO_BLK_S_IOERR; + break; + } + + int64_t offset = req->sector_num * vexp->blk_size; + QEMUIOVector qiov; + if (is_write) { + qemu_iovec_init_external(&qiov, out_iov, out_num); + ret = blk_co_pwritev(blk, offset, qiov.size, &qiov, 0); + } else { + qemu_iovec_init_external(&qiov, in_iov, in_num); + ret = blk_co_preadv(blk, offset, qiov.size, &qiov, 0); + } + if (ret >= 0) { + req->in->status = VIRTIO_BLK_S_OK; + } else { + req->in->status = VIRTIO_BLK_S_IOERR; + } + break; + } + case VIRTIO_BLK_T_FLUSH: + if (blk_co_flush(blk) == 0) { + req->in->status = VIRTIO_BLK_S_OK; + } else { + req->in->status = VIRTIO_BLK_S_IOERR; + } + break; + case VIRTIO_BLK_T_GET_ID: { + size_t size = MIN(iov_size(&elem->in_sg[0], in_num), + VIRTIO_BLK_ID_BYTES); + snprintf(elem->in_sg[0].iov_base, size, "%s", "vhost_user_blk"); + req->in->status = VIRTIO_BLK_S_OK; + req->size = elem->in_sg[0].iov_len; + break; + } + case VIRTIO_BLK_T_DISCARD: + case VIRTIO_BLK_T_WRITE_ZEROES: { + int rc; + + if (!vexp->writable) { + req->in->status = VIRTIO_BLK_S_IOERR; + break; + } + + rc = vu_blk_discard_write_zeroes(blk, &elem->out_sg[1], out_num, type); + if (rc == 0) { + req->in->status = VIRTIO_BLK_S_OK; + } else { + req->in->status = VIRTIO_BLK_S_IOERR; + } + break; + } + default: + req->in->status = VIRTIO_BLK_S_UNSUPP; + break; + } + + vu_blk_req_complete(req); + return; + +err: + free(req); +} + +static void vu_blk_process_vq(VuDev *vu_dev, int idx) +{ + VuServer *server = container_of(vu_dev, VuServer, vu_dev); + VuVirtq *vq = vu_get_queue(vu_dev, idx); + + while (1) { + VuBlkReq *req; + + req = vu_queue_pop(vu_dev, vq, sizeof(VuBlkReq)); + if (!req) { + break; + } + + req->server = server; + req->vq = vq; + + Coroutine *co = + qemu_coroutine_create(vu_blk_virtio_process_req, req); + qemu_coroutine_enter(co); + } +} + +static void vu_blk_queue_set_started(VuDev *vu_dev, int idx, bool started) +{ + VuVirtq *vq; + + assert(vu_dev); + + vq = vu_get_queue(vu_dev, idx); + vu_set_queue_handler(vu_dev, vq, started ? vu_blk_process_vq : NULL); +} + +static uint64_t vu_blk_get_features(VuDev *dev) +{ + uint64_t features; + VuServer *server = container_of(dev, VuServer, vu_dev); + VuBlkExport *vexp = container_of(server, VuBlkExport, vu_server); + features = 1ull << VIRTIO_BLK_F_SIZE_MAX | + 1ull << VIRTIO_BLK_F_SEG_MAX | + 1ull << VIRTIO_BLK_F_TOPOLOGY | + 1ull << VIRTIO_BLK_F_BLK_SIZE | + 1ull << VIRTIO_BLK_F_FLUSH | + 1ull << VIRTIO_BLK_F_DISCARD | + 1ull << VIRTIO_BLK_F_WRITE_ZEROES | + 1ull << VIRTIO_BLK_F_CONFIG_WCE | + 1ull << VIRTIO_BLK_F_MQ | + 1ull << VIRTIO_F_VERSION_1 | + 1ull << VIRTIO_RING_F_INDIRECT_DESC | + 1ull << VIRTIO_RING_F_EVENT_IDX | + 1ull << VHOST_USER_F_PROTOCOL_FEATURES; + + if (!vexp->writable) { + features |= 1ull << VIRTIO_BLK_F_RO; + } + + return features; +} + +static uint64_t vu_blk_get_protocol_features(VuDev *dev) +{ + return 1ull << VHOST_USER_PROTOCOL_F_CONFIG | + 1ull << VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD; +} + +static int +vu_blk_get_config(VuDev *vu_dev, uint8_t *config, uint32_t len) +{ + /* TODO blkcfg must be little-endian for VIRTIO 1.0 */ + VuServer *server = container_of(vu_dev, VuServer, vu_dev); + VuBlkExport *vexp = container_of(server, VuBlkExport, vu_server); + memcpy(config, &vexp->blkcfg, len); + return 0; +} + +static int +vu_blk_set_config(VuDev *vu_dev, const uint8_t *data, + uint32_t offset, uint32_t size, uint32_t flags) +{ + VuServer *server = container_of(vu_dev, VuServer, vu_dev); + VuBlkExport *vexp = container_of(server, VuBlkExport, vu_server); + uint8_t wce; + + /* don't support live migration */ + if (flags != VHOST_SET_CONFIG_TYPE_MASTER) { + return -EINVAL; + } + + if (offset != offsetof(struct virtio_blk_config, wce) || + size != 1) { + return -EINVAL; + } + + wce = *data; + vexp->blkcfg.wce = wce; + blk_set_enable_write_cache(vexp->export.blk, wce); + return 0; +} + +/* + * When the client disconnects, it sends a VHOST_USER_NONE request + * and vu_process_message will simple call exit which cause the VM + * to exit abruptly. + * To avoid this issue, process VHOST_USER_NONE request ahead + * of vu_process_message. + * + */ +static int vu_blk_process_msg(VuDev *dev, VhostUserMsg *vmsg, int *do_reply) +{ + if (vmsg->request == VHOST_USER_NONE) { + dev->panic(dev, "disconnect"); + return true; + } + return false; +} + +static const VuDevIface vu_blk_iface = { + .get_features = vu_blk_get_features, + .queue_set_started = vu_blk_queue_set_started, + .get_protocol_features = vu_blk_get_protocol_features, + .get_config = vu_blk_get_config, + .set_config = vu_blk_set_config, + .process_msg = vu_blk_process_msg, +}; + +static void blk_aio_attached(AioContext *ctx, void *opaque) +{ + VuBlkExport *vexp = opaque; + + vexp->export.ctx = ctx; + vhost_user_server_attach_aio_context(&vexp->vu_server, ctx); +} + +static void blk_aio_detach(void *opaque) +{ + VuBlkExport *vexp = opaque; + + vhost_user_server_detach_aio_context(&vexp->vu_server); + vexp->export.ctx = NULL; +} + +static void +vu_blk_initialize_config(BlockDriverState *bs, + struct virtio_blk_config *config, + uint32_t blk_size, + uint16_t num_queues) +{ + config->capacity = bdrv_getlength(bs) >> BDRV_SECTOR_BITS; + config->blk_size = blk_size; + config->size_max = 0; + config->seg_max = 128 - 2; + config->min_io_size = 1; + config->opt_io_size = 1; + config->num_queues = num_queues; + config->max_discard_sectors = 32768; + config->max_discard_seg = 1; + config->discard_sector_alignment = config->blk_size >> 9; + config->max_write_zeroes_sectors = 32768; + config->max_write_zeroes_seg = 1; +} + +static void vu_blk_exp_request_shutdown(BlockExport *exp) +{ + VuBlkExport *vexp = container_of(exp, VuBlkExport, export); + + vhost_user_server_stop(&vexp->vu_server); +} + +static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts, + Error **errp) +{ + VuBlkExport *vexp = container_of(exp, VuBlkExport, export); + BlockExportOptionsVhostUserBlk *vu_opts = &opts->u.vhost_user_blk; + Error *local_err = NULL; + uint64_t logical_block_size; + uint16_t num_queues = VHOST_USER_BLK_NUM_QUEUES_DEFAULT; + + vexp->writable = opts->writable; + vexp->blkcfg.wce = 0; + + if (vu_opts->has_logical_block_size) { + logical_block_size = vu_opts->logical_block_size; + } else { + logical_block_size = BDRV_SECTOR_SIZE; + } + check_block_size(exp->id, "logical-block-size", logical_block_size, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return -EINVAL; + } + vexp->blk_size = logical_block_size; + blk_set_guest_block_size(exp->blk, logical_block_size); + + if (vu_opts->has_num_queues) { + num_queues = vu_opts->num_queues; + } + if (num_queues == 0) { + error_setg(errp, "num-queues must be greater than 0"); + return -EINVAL; + } + + vu_blk_initialize_config(blk_bs(exp->blk), &vexp->blkcfg, + logical_block_size, num_queues); + + blk_add_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach, + vexp); + + if (!vhost_user_server_start(&vexp->vu_server, vu_opts->addr, exp->ctx, + num_queues, &vu_blk_iface, errp)) { + blk_remove_aio_context_notifier(exp->blk, blk_aio_attached, + blk_aio_detach, vexp); + return -EADDRNOTAVAIL; + } + + return 0; +} + +static void vu_blk_exp_delete(BlockExport *exp) +{ + VuBlkExport *vexp = container_of(exp, VuBlkExport, export); + + blk_remove_aio_context_notifier(exp->blk, blk_aio_attached, blk_aio_detach, + vexp); +} + +const BlockExportDriver blk_exp_vhost_user_blk = { + .type = BLOCK_EXPORT_TYPE_VHOST_USER_BLK, + .instance_size = sizeof(VuBlkExport), + .create = vu_blk_exp_create, + .delete = vu_blk_exp_delete, + .request_shutdown = vu_blk_exp_request_shutdown, +}; diff --git a/block/export/vhost-user-blk-server.h b/block/export/vhost-user-blk-server.h new file mode 100644 index 0000000000..fcf46fc8a5 --- /dev/null +++ b/block/export/vhost-user-blk-server.h @@ -0,0 +1,19 @@ +/* + * Sharing QEMU block devices via vhost-user protocal + * + * Copyright (c) Coiby Xu <coiby.xu@gmail.com>. + * Copyright (c) 2020 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#ifndef VHOST_USER_BLK_SERVER_H +#define VHOST_USER_BLK_SERVER_H + +#include "block/export.h" + +/* For block/export/export.c */ +extern const BlockExportDriver blk_exp_vhost_user_blk; + +#endif /* VHOST_USER_BLK_SERVER_H */ diff --git a/block/io.c b/block/io.c index 54f0968aee..9918f2499c 100644 --- a/block/io.c +++ b/block/io.c @@ -633,6 +633,19 @@ void bdrv_drain_all_begin(void) } } +void bdrv_drain_all_end_quiesce(BlockDriverState *bs) +{ + int drained_end_counter = 0; + + g_assert(bs->quiesce_counter > 0); + g_assert(!bs->refcnt); + + while (bs->quiesce_counter) { + bdrv_do_drained_end(bs, false, NULL, true, &drained_end_counter); + } + BDRV_POLL_WHILE(bs, qatomic_read(&drained_end_counter) > 0); +} + void bdrv_drain_all_end(void) { BlockDriverState *bs = NULL; @@ -2282,17 +2295,17 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, if (ret & (BDRV_BLOCK_DATA | BDRV_BLOCK_ZERO)) { ret |= BDRV_BLOCK_ALLOCATED; - } else if (want_zero && bs->drv->supports_backing) { + } else if (bs->drv->supports_backing) { BlockDriverState *cow_bs = bdrv_cow_bs(bs); - if (cow_bs) { + if (!cow_bs) { + ret |= BDRV_BLOCK_ZERO; + } else if (want_zero) { int64_t size2 = bdrv_getlength(cow_bs); if (size2 >= 0 && offset >= size2) { ret |= BDRV_BLOCK_ZERO; } - } else { - ret |= BDRV_BLOCK_ZERO; } } @@ -2343,6 +2356,7 @@ early_out: int coroutine_fn bdrv_co_common_block_status_above(BlockDriverState *bs, BlockDriverState *base, + bool include_base, bool want_zero, int64_t offset, int64_t bytes, @@ -2350,34 +2364,84 @@ bdrv_co_common_block_status_above(BlockDriverState *bs, int64_t *map, BlockDriverState **file) { + int ret; BlockDriverState *p; - int ret = 0; - bool first = true; + int64_t eof = 0; + + assert(!include_base || base); /* Can't include NULL base */ - assert(bs != base); - for (p = bs; p != base; p = bdrv_filter_or_cow_bs(p)) { + if (!include_base && bs == base) { + *pnum = bytes; + return 0; + } + + ret = bdrv_co_block_status(bs, want_zero, offset, bytes, pnum, map, file); + if (ret < 0 || *pnum == 0 || ret & BDRV_BLOCK_ALLOCATED || bs == base) { + return ret; + } + + if (ret & BDRV_BLOCK_EOF) { + eof = offset + *pnum; + } + + assert(*pnum <= bytes); + bytes = *pnum; + + for (p = bdrv_filter_or_cow_bs(bs); include_base || p != base; + p = bdrv_filter_or_cow_bs(p)) + { ret = bdrv_co_block_status(p, want_zero, offset, bytes, pnum, map, file); if (ret < 0) { - break; + return ret; } - if (ret & BDRV_BLOCK_ZERO && ret & BDRV_BLOCK_EOF && !first) { + if (*pnum == 0) { /* - * Reading beyond the end of the file continues to read - * zeroes, but we can only widen the result to the - * unallocated length we learned from an earlier - * iteration. + * The top layer deferred to this layer, and because this layer is + * short, any zeroes that we synthesize beyond EOF behave as if they + * were allocated at this layer. + * + * We don't include BDRV_BLOCK_EOF into ret, as upper layer may be + * larger. We'll add BDRV_BLOCK_EOF if needed at function end, see + * below. */ + assert(ret & BDRV_BLOCK_EOF); *pnum = bytes; + if (file) { + *file = p; + } + ret = BDRV_BLOCK_ZERO | BDRV_BLOCK_ALLOCATED; + break; } - if (ret & (BDRV_BLOCK_ZERO | BDRV_BLOCK_DATA)) { + if (ret & BDRV_BLOCK_ALLOCATED) { + /* + * We've found the node and the status, we must break. + * + * Drop BDRV_BLOCK_EOF, as it's not for upper layer, which may be + * larger. We'll add BDRV_BLOCK_EOF if needed at function end, see + * below. + */ + ret &= ~BDRV_BLOCK_EOF; + break; + } + + if (p == base) { + assert(include_base); break; } - /* [offset, pnum] unallocated on this layer, which could be only - * the first part of [offset, bytes]. */ - bytes = MIN(bytes, *pnum); - first = false; + + /* + * OK, [offset, offset + *pnum) region is unallocated on this layer, + * let's continue the diving. + */ + assert(*pnum <= bytes); + bytes = *pnum; } + + if (offset + *pnum == eof) { + ret |= BDRV_BLOCK_EOF; + } + return ret; } @@ -2385,7 +2449,7 @@ int bdrv_block_status_above(BlockDriverState *bs, BlockDriverState *base, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) { - return bdrv_common_block_status_above(bs, base, true, offset, bytes, + return bdrv_common_block_status_above(bs, base, false, true, offset, bytes, pnum, map, file); } @@ -2396,15 +2460,42 @@ int bdrv_block_status(BlockDriverState *bs, int64_t offset, int64_t bytes, offset, bytes, pnum, map, file); } +/* + * Check @bs (and its backing chain) to see if the range defined + * by @offset and @bytes is known to read as zeroes. + * Return 1 if that is the case, 0 otherwise and -errno on error. + * This test is meant to be fast rather than accurate so returning 0 + * does not guarantee non-zero data. + */ +int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset, + int64_t bytes) +{ + int ret; + int64_t pnum = bytes; + + if (!bytes) { + return 1; + } + + ret = bdrv_common_block_status_above(bs, NULL, false, false, offset, + bytes, &pnum, NULL, NULL); + + if (ret < 0) { + return ret; + } + + return (pnum == bytes) && (ret & BDRV_BLOCK_ZERO); +} + int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes, int64_t *pnum) { int ret; int64_t dummy; - ret = bdrv_common_block_status_above(bs, bdrv_filter_or_cow_bs(bs), false, - offset, bytes, pnum ? pnum : &dummy, - NULL, NULL); + ret = bdrv_common_block_status_above(bs, bs, true, false, offset, + bytes, pnum ? pnum : &dummy, NULL, + NULL); if (ret < 0) { return ret; } @@ -2426,52 +2517,19 @@ int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t offset, * at 'offset + *pnum' may return the same allocation status (in other * words, the result is not necessarily the maximum possible range); * but 'pnum' will only be 0 when end of file is reached. - * */ int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, bool include_base, int64_t offset, int64_t bytes, int64_t *pnum) { - BlockDriverState *intermediate; - int ret; - int64_t n = bytes; - - assert(base || !include_base); - - intermediate = top; - while (include_base || intermediate != base) { - int64_t pnum_inter; - int64_t size_inter; - - assert(intermediate); - ret = bdrv_is_allocated(intermediate, offset, bytes, &pnum_inter); - if (ret < 0) { - return ret; - } - if (ret) { - *pnum = pnum_inter; - return 1; - } - - size_inter = bdrv_getlength(intermediate); - if (size_inter < 0) { - return size_inter; - } - if (n > pnum_inter && - (intermediate == top || offset + pnum_inter < size_inter)) { - n = pnum_inter; - } - - if (intermediate == base) { - break; - } - - intermediate = bdrv_filter_or_cow_bs(intermediate); + int ret = bdrv_common_block_status_above(top, base, include_base, false, + offset, bytes, pnum, NULL, NULL); + if (ret < 0) { + return ret; } - *pnum = n; - return 0; + return !!(ret & BDRV_BLOCK_ALLOCATED); } int coroutine_fn diff --git a/block/meson.build b/block/meson.build index 78e8b25232..5dcc1e5cce 100644 --- a/block/meson.build +++ b/block/meson.build @@ -7,7 +7,6 @@ block_ss.add(files( 'backup-top.c', 'blkdebug.c', 'blklogwrites.c', - 'blkreplay.c', 'blkverify.c', 'block-backend.c', 'block-copy.c', @@ -42,6 +41,8 @@ block_ss.add(files( 'write-threshold.c', ), zstd, zlib) +softmmu_ss.add(when: 'CONFIG_TCG', if_true: files('blkreplay.c')) + block_ss.add(when: 'CONFIG_QCOW1', if_true: files('qcow.c')) block_ss.add(when: 'CONFIG_VDI', if_true: files('vdi.c')) block_ss.add(when: 'CONFIG_CLOOP', if_true: files('cloop.c')) diff --git a/block/nvme.c b/block/nvme.c index b48f6f2588..739a0a700c 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -128,6 +128,12 @@ struct BDRVNVMeState { /* PCI address (required for nvme_refresh_filename()) */ char *device; + + struct { + uint64_t completion_errors; + uint64_t aligned_accesses; + uint64_t unaligned_accesses; + } stats; }; #define NVME_BLOCK_OPT_DEVICE "device" @@ -384,6 +390,9 @@ static bool nvme_process_completion(NVMeQueuePair *q) break; } ret = nvme_translate_error(c); + if (ret) { + s->stats.completion_errors++; + } q->cq.head = (q->cq.head + 1) % NVME_QUEUE_SIZE; if (!q->cq.head) { q->cq_phase = !q->cq_phase; @@ -1155,8 +1164,10 @@ static int nvme_co_prw(BlockDriverState *bs, uint64_t offset, uint64_t bytes, assert(QEMU_IS_ALIGNED(bytes, s->page_size)); assert(bytes <= s->max_transfer); if (nvme_qiov_aligned(bs, qiov)) { + s->stats.aligned_accesses++; return nvme_co_prw_aligned(bs, offset, bytes, qiov, is_write, flags); } + s->stats.unaligned_accesses++; trace_nvme_prw_buffered(s, offset, bytes, qiov->niov, is_write); buf = qemu_try_memalign(s->page_size, bytes); @@ -1452,6 +1463,21 @@ static void nvme_unregister_buf(BlockDriverState *bs, void *host) qemu_vfio_dma_unmap(s->vfio, host); } +static BlockStatsSpecific *nvme_get_specific_stats(BlockDriverState *bs) +{ + BlockStatsSpecific *stats = g_new(BlockStatsSpecific, 1); + BDRVNVMeState *s = bs->opaque; + + stats->driver = BLOCKDEV_DRIVER_NVME; + stats->u.nvme = (BlockStatsSpecificNvme) { + .completion_errors = s->stats.completion_errors, + .aligned_accesses = s->stats.aligned_accesses, + .unaligned_accesses = s->stats.unaligned_accesses, + }; + + return stats; +} + static const char *const nvme_strong_runtime_opts[] = { NVME_BLOCK_OPT_DEVICE, NVME_BLOCK_OPT_NAMESPACE, @@ -1485,6 +1511,7 @@ static BlockDriver bdrv_nvme = { .bdrv_refresh_filename = nvme_refresh_filename, .bdrv_refresh_limits = nvme_refresh_limits, .strong_runtime_opts = nvme_strong_runtime_opts, + .bdrv_get_specific_stats = nvme_get_specific_stats, .bdrv_detach_aio_context = nvme_detach_aio_context, .bdrv_attach_aio_context = nvme_attach_aio_context, diff --git a/block/qcow2.c b/block/qcow2.c index b05512718c..4274806a2a 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -2387,26 +2387,26 @@ static bool merge_cow(uint64_t offset, unsigned bytes, return false; } -static bool is_unallocated(BlockDriverState *bs, int64_t offset, int64_t bytes) -{ - int64_t nr; - return !bytes || - (!bdrv_is_allocated_above(bs, NULL, false, offset, bytes, &nr) && - nr == bytes); -} - -static bool is_zero_cow(BlockDriverState *bs, QCowL2Meta *m) +/* + * Return 1 if the COW regions read as zeroes, 0 if not, < 0 on error. + * Note that returning 0 does not guarantee non-zero data. + */ +static int is_zero_cow(BlockDriverState *bs, QCowL2Meta *m) { /* * This check is designed for optimization shortcut so it must be * efficient. - * Instead of is_zero(), use is_unallocated() as it is faster (but not - * as accurate and can result in false negatives). + * Instead of is_zero(), use bdrv_co_is_zero_fast() as it is + * faster (but not as accurate and can result in false negatives). */ - return is_unallocated(bs, m->offset + m->cow_start.offset, - m->cow_start.nb_bytes) && - is_unallocated(bs, m->offset + m->cow_end.offset, - m->cow_end.nb_bytes); + int ret = bdrv_co_is_zero_fast(bs, m->offset + m->cow_start.offset, + m->cow_start.nb_bytes); + if (ret <= 0) { + return ret; + } + + return bdrv_co_is_zero_fast(bs, m->offset + m->cow_end.offset, + m->cow_end.nb_bytes); } static int handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta) @@ -2432,7 +2432,10 @@ static int handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta) continue; } - if (!is_zero_cow(bs, m)) { + ret = is_zero_cow(bs, m); + if (ret < 0) { + return ret; + } else if (ret == 0) { continue; } @@ -3860,8 +3863,20 @@ static bool is_zero(BlockDriverState *bs, int64_t offset, int64_t bytes) if (!bytes) { return true; } - res = bdrv_block_status_above(bs, NULL, offset, bytes, &nr, NULL, NULL); - return res >= 0 && (res & BDRV_BLOCK_ZERO) && nr == bytes; + + /* + * bdrv_block_status_above doesn't merge different types of zeros, for + * example, zeros which come from the region which is unallocated in + * the whole backing chain, and zeros which come because of a short + * backing file. So, we need a loop. + */ + do { + res = bdrv_block_status_above(bs, NULL, offset, bytes, &nr, NULL, NULL); + offset += nr; + bytes -= nr; + } while (res >= 0 && (res & BDRV_BLOCK_ZERO) && nr && bytes); + + return res >= 0 && (res & BDRV_BLOCK_ZERO) && bytes == 0; } static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs, @@ -302,6 +302,7 @@ fdt="auto" netmap="no" sdl="auto" sdl_image="auto" +virtiofsd="auto" virtfs="" libudev="auto" mpath="auto" @@ -362,8 +363,7 @@ cocoa="auto" softmmu="yes" linux_user="no" bsd_user="no" -blobs="yes" -edk2_blobs="no" +blobs="true" pkgversion="" pie="" qom_cast_debug="yes" @@ -962,6 +962,8 @@ for opt do ;; --docdir=*) docdir="$optarg" ;; + --localedir=*) localedir="$optarg" + ;; --sysconfdir=*) sysconfdir="$optarg" ;; --localstatedir=*) local_statedir="$optarg" @@ -971,7 +973,7 @@ for opt do --host=*|--build=*|\ --disable-dependency-tracking|\ --sbindir=*|--sharedstatedir=*|\ - --oldincludedir=*|--datarootdir=*|--infodir=*|--localedir=*|\ + --oldincludedir=*|--datarootdir=*|--infodir=*|\ --htmldir=*|--dvidir=*|--pdfdir=*|--psdir=*) # These switches are silently ignored, for compatibility with # autoconf-generated configure scripts. This allows QEMU's @@ -998,6 +1000,10 @@ for opt do ;; --enable-libudev) libudev="enabled" ;; + --disable-virtiofsd) virtiofsd="disabled" + ;; + --enable-virtiofsd) virtiofsd="enabled" + ;; --disable-mpath) mpath="disabled" ;; --enable-mpath) mpath="enabled" @@ -1203,7 +1209,7 @@ for opt do ;; --enable-membarrier) membarrier="yes" ;; - --disable-blobs) blobs="no" + --disable-blobs) blobs="false" ;; --with-pkgversion=*) pkgversion="$optarg" ;; @@ -1521,7 +1527,6 @@ for opt do esac done -firmwarepath="${firmwarepath:-$prefix/share/qemu-firmware}" libdir="${libdir:-$prefix/lib}" libexecdir="${libexecdir:-$prefix/libexec}" includedir="${includedir:-$prefix/include}" @@ -1532,7 +1537,7 @@ if test "$mingw32" = "yes" ; then docdir="$prefix" bindir="$prefix" sysconfdir="$prefix" - local_statedir= + local_statedir="$prefix" else mandir="${mandir:-$prefix/share/man}" datadir="${datadir:-$prefix/share}" @@ -1541,6 +1546,8 @@ else sysconfdir="${sysconfdir:-$prefix/etc}" local_statedir="${local_statedir:-$prefix/var}" fi +firmwarepath="${firmwarepath:-$datadir/qemu-firmware}" +localedir="${localedir:-$datadir/locale}" case "$cpu" in ppc) @@ -1670,6 +1677,7 @@ Advanced options (experts only): --static enable static build [$static] --mandir=PATH install man pages in PATH --datadir=PATH install firmware in PATH/$qemu_suffix + --localedir=PATH install translation in PATH/$qemu_suffix --docdir=PATH install documentation in PATH/$qemu_suffix --bindir=PATH install binaries in PATH --libdir=PATH install libraries in PATH @@ -1755,6 +1763,7 @@ disabled with --disable-FEATURE, default is enabled if available: vnc-png PNG compression for VNC server cocoa Cocoa UI (Mac OS X only) virtfs VirtFS + virtiofsd build virtiofs daemon (virtiofsd) libudev Use libudev to enumerate host devices mpath Multipath persistent reservation passthrough xen xen backend driver support @@ -2217,18 +2226,6 @@ case " $target_list " in ;; esac -for target in $target_list; do - case "$target" in - arm-softmmu | aarch64-softmmu | i386-softmmu | x86_64-softmmu) - edk2_blobs="yes" - ;; - esac -done -# The EDK2 binaries are compressed with bzip2 -if test "$edk2_blobs" = "yes" && ! has bzip2; then - error_exit "The bzip2 program is required for building QEMU" -fi - feature_not_found() { feature=$1 remedy=$2 @@ -3608,7 +3605,6 @@ else if test "$found" = "no"; then LIBS="$pthread_lib $LIBS" fi - PTHREAD_LIB="$pthread_lib" break fi done @@ -5725,13 +5721,6 @@ if test "$mingw32" = "yes" ; then done fi -qemu_confdir="$sysconfdir/$qemu_suffix" -qemu_moddir="$libdir/$qemu_suffix" -qemu_datadir="$datadir/$qemu_suffix" -qemu_localedir="$datadir/locale" -qemu_icondir="$datadir/icons" -qemu_desktopdir="$datadir/applications" - # We can only support ivshmem if we have eventfd if [ "$eventfd" = "yes" ]; then ivshmem=yes @@ -5898,23 +5887,6 @@ echo "# Automatically generated by configure - do not modify" > $config_host_mak echo >> $config_host_mak echo all: >> $config_host_mak -echo "prefix=$prefix" >> $config_host_mak -echo "bindir=$bindir" >> $config_host_mak -echo "libdir=$libdir" >> $config_host_mak -echo "libexecdir=$libexecdir" >> $config_host_mak -echo "includedir=$includedir" >> $config_host_mak -echo "sysconfdir=$sysconfdir" >> $config_host_mak -echo "qemu_confdir=$qemu_confdir" >> $config_host_mak -echo "qemu_datadir=$qemu_datadir" >> $config_host_mak -echo "qemu_firmwarepath=$firmwarepath" >> $config_host_mak -echo "qemu_moddir=$qemu_moddir" >> $config_host_mak -if test "$mingw32" = "no" ; then - echo "qemu_localstatedir=$local_statedir" >> $config_host_mak -fi -echo "qemu_helperdir=$libexecdir" >> $config_host_mak -echo "qemu_localedir=$qemu_localedir" >> $config_host_mak -echo "qemu_icondir=$qemu_icondir" >> $config_host_mak -echo "qemu_desktopdir=$qemu_desktopdir" >> $config_host_mak echo "GIT=$git" >> $config_host_mak echo "GIT_SUBMODULES=$git_submodules" >> $config_host_mak echo "GIT_UPDATE=$git_update" >> $config_host_mak @@ -6297,9 +6269,6 @@ fi if test "$vhost_user_fs" = "yes" ; then echo "CONFIG_VHOST_USER_FS=y" >> $config_host_mak fi -if test "$blobs" = "yes" ; then - echo "INSTALL_BLOBS=yes" >> $config_host_mak -fi if test "$iovec" = "yes" ; then echo "CONFIG_IOVEC=y" >> $config_host_mak fi @@ -6762,7 +6731,6 @@ echo "GLIB_LIBS=$glib_libs" >> $config_host_mak echo "QEMU_LDFLAGS=$QEMU_LDFLAGS" >> $config_host_mak echo "LDFLAGS_NOPIE=$LDFLAGS_NOPIE" >> $config_host_mak echo "LD_I386_EMULATION=$ld_i386_emulation" >> $config_host_mak -echo "PTHREAD_LIB=$PTHREAD_LIB" >> $config_host_mak echo "EXESUF=$EXESUF" >> $config_host_mak echo "HOST_DSOSUF=$HOST_DSOSUF" >> $config_host_mak echo "LIBS_QGA=$libs_qga" >> $config_host_mak @@ -6777,10 +6745,6 @@ if test "$fuzzing" != "no"; then fi echo "FUZZ_EXE_LDFLAGS=$FUZZ_EXE_LDFLAGS" >> $config_host_mak -if test "$edk2_blobs" = "yes" ; then - echo "DECOMPRESS_EDK2_BLOBS=y" >> $config_host_mak -fi - if test "$rng_none" = "yes"; then echo "CONFIG_RNG_NONE=y" >> $config_host_mak fi @@ -6997,8 +6961,10 @@ NINJA=$ninja $meson setup \ --datadir "$datadir" \ --mandir "$mandir" \ --sysconfdir "$sysconfdir" \ + --localedir "$localedir" \ --localstatedir "$local_statedir" \ -Ddocdir="$docdir" \ + -Dqemu_firmwarepath="$firmwarepath" \ -Dqemu_suffix="$qemu_suffix" \ -Doptimization=$(if test "$debug" = yes; then echo 0; else echo 2; fi) \ -Ddebug=$(if test "$debug_info" = yes; then echo true; else echo false; fi) \ @@ -7012,10 +6978,10 @@ NINJA=$ninja $meson setup \ -Dxen=$xen -Dxen_pci_passthrough=$xen_pci_passthrough -Dtcg=$tcg \ -Dcocoa=$cocoa -Dmpath=$mpath -Dsdl=$sdl -Dsdl_image=$sdl_image \ -Dvnc=$vnc -Dvnc_sasl=$vnc_sasl -Dvnc_jpeg=$vnc_jpeg -Dvnc_png=$vnc_png \ - -Dgettext=$gettext -Dxkbcommon=$xkbcommon -Du2f=$u2f \ + -Dgettext=$gettext -Dxkbcommon=$xkbcommon -Du2f=$u2f -Dvirtiofsd=$virtiofsd \ -Dcapstone=$capstone -Dslirp=$slirp -Dfdt=$fdt \ -Diconv=$iconv -Dcurses=$curses -Dlibudev=$libudev\ - -Ddocs=$docs -Dsphinx_build=$sphinx_build \ + -Ddocs=$docs -Dsphinx_build=$sphinx_build -Dinstall_blobs=$blobs \ $cross_arg \ "$PWD" "$source_path" diff --git a/contrib/gitdm/group-map-individuals b/contrib/gitdm/group-map-individuals index 641169fa63..36bbb77c39 100644 --- a/contrib/gitdm/group-map-individuals +++ b/contrib/gitdm/group-map-individuals @@ -23,3 +23,9 @@ vr_qemu@t-online.de nieklinnenbank@gmail.com devnexen@gmail.com pauldzim@gmail.com +ani@anisinha.ca +sundeep.lkml@gmail.com +mrolnik@gmail.com +huth@tuxfamily.org +jhogan@kernel.org +atar4qemu@gmail.com diff --git a/contrib/libvhost-user/libvhost-user-glib.c b/contrib/libvhost-user/libvhost-user-glib.c index 53f1ca4cdd..0df2ec9271 100644 --- a/contrib/libvhost-user/libvhost-user-glib.c +++ b/contrib/libvhost-user/libvhost-user-glib.c @@ -147,7 +147,7 @@ vug_init(VugDev *dev, uint16_t max_queues, int socket, g_assert(dev); g_assert(iface); - if (!vu_init(&dev->parent, max_queues, socket, panic, set_watch, + if (!vu_init(&dev->parent, max_queues, socket, panic, NULL, set_watch, remove_watch, iface)) { return false; } diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/libvhost-user.c index 9f1285b8a1..bfec8a881a 100644 --- a/contrib/libvhost-user/libvhost-user.c +++ b/contrib/libvhost-user/libvhost-user.c @@ -68,8 +68,6 @@ /* The version of inflight buffer */ #define INFLIGHT_VERSION 1 -#define VHOST_USER_HDR_SIZE offsetof(VhostUserMsg, payload.u64) - /* The version of the protocol we support */ #define VHOST_USER_VERSION 1 #define LIBVHOST_USER_DEBUG 0 @@ -268,7 +266,7 @@ have_userfault(void) } static bool -vu_message_read(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) +vu_message_read_default(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) { char control[CMSG_SPACE(VHOST_MEMORY_BASELINE_NREGIONS * sizeof(int))] = {}; struct iovec iov = { @@ -416,7 +414,7 @@ vu_process_message_reply(VuDev *dev, const VhostUserMsg *vmsg) goto out; } - if (!vu_message_read(dev, dev->slave_fd, &msg_reply)) { + if (!vu_message_read_default(dev, dev->slave_fd, &msg_reply)) { goto out; } @@ -907,7 +905,7 @@ vu_set_mem_table_exec_postcopy(VuDev *dev, VhostUserMsg *vmsg) /* Wait for QEMU to confirm that it's registered the handler for the * faults. */ - if (!vu_message_read(dev, dev->sock, vmsg) || + if (!dev->read_msg(dev, dev->sock, vmsg) || vmsg->size != sizeof(vmsg->payload.u64) || vmsg->payload.u64 != 0) { vu_panic(dev, "failed to receive valid ack for postcopy set-mem-table"); @@ -1869,7 +1867,7 @@ vu_dispatch(VuDev *dev) int reply_requested; bool need_reply, success = false; - if (!vu_message_read(dev, dev->sock, &vmsg)) { + if (!dev->read_msg(dev, dev->sock, &vmsg)) { goto end; } @@ -1920,6 +1918,7 @@ vu_deinit(VuDev *dev) } if (vq->kick_fd != -1) { + dev->remove_watch(dev, vq->kick_fd); close(vq->kick_fd); vq->kick_fd = -1; } @@ -1967,6 +1966,7 @@ vu_init(VuDev *dev, uint16_t max_queues, int socket, vu_panic_cb panic, + vu_read_msg_cb read_msg, vu_set_watch_cb set_watch, vu_remove_watch_cb remove_watch, const VuDevIface *iface) @@ -1984,6 +1984,7 @@ vu_init(VuDev *dev, dev->sock = socket; dev->panic = panic; + dev->read_msg = read_msg ? read_msg : vu_message_read_default; dev->set_watch = set_watch; dev->remove_watch = remove_watch; dev->iface = iface; @@ -2349,7 +2350,7 @@ static void _vu_queue_notify(VuDev *dev, VuVirtq *vq, bool sync) vu_message_write(dev, dev->slave_fd, &vmsg); if (ack) { - vu_message_read(dev, dev->slave_fd, &vmsg); + vu_message_read_default(dev, dev->slave_fd, &vmsg); } return; } diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h index 287ac5fec7..3bbeae8587 100644 --- a/contrib/libvhost-user/libvhost-user.h +++ b/contrib/libvhost-user/libvhost-user.h @@ -36,6 +36,8 @@ */ #define VHOST_USER_MAX_RAM_SLOTS 32 +#define VHOST_USER_HDR_SIZE offsetof(VhostUserMsg, payload.u64) + typedef enum VhostSetConfigType { VHOST_SET_CONFIG_TYPE_MASTER = 0, VHOST_SET_CONFIG_TYPE_MIGRATION = 1, @@ -221,6 +223,7 @@ typedef uint64_t (*vu_get_features_cb) (VuDev *dev); typedef void (*vu_set_features_cb) (VuDev *dev, uint64_t features); typedef int (*vu_process_msg_cb) (VuDev *dev, VhostUserMsg *vmsg, int *do_reply); +typedef bool (*vu_read_msg_cb) (VuDev *dev, int sock, VhostUserMsg *vmsg); typedef void (*vu_queue_set_started_cb) (VuDev *dev, int qidx, bool started); typedef bool (*vu_queue_is_processed_in_order_cb) (VuDev *dev, int qidx); typedef int (*vu_get_config_cb) (VuDev *dev, uint8_t *config, uint32_t len); @@ -389,6 +392,23 @@ struct VuDev { bool broken; uint16_t max_queues; + /* @read_msg: custom method to read vhost-user message + * + * Read data from vhost_user socket fd and fill up + * the passed VhostUserMsg *vmsg struct. + * + * If reading fails, it should close the received set of file + * descriptors as socket message's auxiliary data. + * + * For the details, please refer to vu_message_read in libvhost-user.c + * which will be used by default if not custom method is provided when + * calling vu_init + * + * Returns: true if vhost-user message successfully received, + * otherwise return false. + * + */ + vu_read_msg_cb read_msg; /* @set_watch: add or update the given fd to the watch set, * call cb when condition is met */ vu_set_watch_cb set_watch; @@ -432,6 +452,7 @@ bool vu_init(VuDev *dev, uint16_t max_queues, int socket, vu_panic_cb panic, + vu_read_msg_cb read_msg, vu_set_watch_cb set_watch, vu_remove_watch_cb remove_watch, const VuDevIface *iface); diff --git a/contrib/libvhost-user/meson.build b/contrib/libvhost-user/meson.build index e68dd1a581..a261e7665f 100644 --- a/contrib/libvhost-user/meson.build +++ b/contrib/libvhost-user/meson.build @@ -1,3 +1,4 @@ libvhost_user = static_library('vhost-user', files('libvhost-user.c', 'libvhost-user-glib.c'), build_by_default: false) +vhost_user = declare_dependency(link_with: libvhost_user) diff --git a/contrib/vhost-user-gpu/meson.build b/contrib/vhost-user-gpu/meson.build index 7d9b29da8b..37ecca13ca 100644 --- a/contrib/vhost-user-gpu/meson.build +++ b/contrib/vhost-user-gpu/meson.build @@ -9,6 +9,6 @@ if 'CONFIG_TOOLS' in config_host and 'CONFIG_VIRGL' in config_host \ configure_file(input: '50-qemu-gpu.json.in', output: '50-qemu-gpu.json', - configuration: config_host, + configuration: { 'libexecdir' : get_option('libexecdir') }, install_dir: qemu_datadir / 'vhost-user') endif diff --git a/docs/devel/fuzzing.txt b/docs/devel/fuzzing.txt index 96d71c94d7..03585c1a9b 100644 --- a/docs/devel/fuzzing.txt +++ b/docs/devel/fuzzing.txt @@ -125,6 +125,45 @@ provided by libfuzzer. Libfuzzer passes a byte array and length. Commonly the fuzzer loops over the byte-array interpreting it as a list of qtest commands, addresses, or values. +== The Generic Fuzzer == +Writing a fuzz target can be a lot of effort (especially if a device driver has +not be built-out within libqos). Many devices can be fuzzed to some degree, +without any device-specific code, using the generic-fuzz target. + +The generic-fuzz target is capable of fuzzing devices over their PIO, MMIO, +and DMA input-spaces. To apply the generic-fuzz to a device, we need to define +two env-variables, at minimum: + +QEMU_FUZZ_ARGS= is the set of QEMU arguments used to configure a machine, with +the device attached. For example, if we want to fuzz the virtio-net device +attached to a pc-i440fx machine, we can specify: +QEMU_FUZZ_ARGS="-M pc -nodefaults -netdev user,id=user0 \ + -device virtio-net,netdev=user0" + +QEMU_FUZZ_OBJECTS= is a set of space-delimited strings used to identify the +MemoryRegions that will be fuzzed. These strings are compared against +MemoryRegion names and MemoryRegion owner names, to decide whether each +MemoryRegion should be fuzzed. These strings support globbing. For the +virtio-net example, we could use QEMU_FUZZ_OBJECTS= + * 'virtio-net' + * 'virtio*' + * 'virtio* pcspk' (Fuzz the virtio devices and the PC speaker...) + * '*' (Fuzz the whole machine) + +The "info mtree" and "info qom-tree" monitor commands can be especially useful +for identifying the MemoryRegion and Object names used for matching. + +As a generic rule-of-thumb, the more MemoryRegions/Devices we match, the greater +the input-space, and the smaller the probability of finding crashing inputs for +individual devices. As such, it is usually a good idea to limit the fuzzer to +only a few MemoryRegions. + +To ensure that these env variables have been configured correctly, we can use: + +./qemu-fuzz-i386 --fuzz-target=generic-fuzz -runs=0 + +The output should contain a complete list of matched MemoryRegions. + = Implementation Details = == The Fuzzer's Lifecycle == diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index 0c3e79d31c..0aa7a13bba 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -654,7 +654,7 @@ To manually install Avocado and its dependencies, run: Alternatively, follow the instructions on this link: - http://avocado-framework.readthedocs.io/en/latest/GetStartedGuide.html#installing-avocado + https://avocado-framework.readthedocs.io/en/latest/guides/user/chapters/installing.html Overview -------- @@ -837,7 +837,7 @@ Parameter reference To understand how Avocado parameters are accessed by tests, and how they can be passed to tests, please refer to:: - http://avocado-framework.readthedocs.io/en/latest/WritingTests.html#accessing-test-parameters + https://avocado-framework.readthedocs.io/en/latest/guides/writer/chapters/writing.html#accessing-test-parameters Parameter values can be easily seen in the log files, and will look like the following: diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst index e3e1a3a3a7..b00d405d52 100644 --- a/docs/system/arm/nuvoton.rst +++ b/docs/system/arm/nuvoton.rst @@ -38,11 +38,13 @@ Supported devices * DDR4 memory controller (dummy interface indicating memory training is done) * OTP controllers (no protection features) * Flash Interface Unit (FIU; no protection features) + * Random Number Generator (RNG) + * USB host (USBH) + * GPIO controller Missing devices --------------- - * GPIO controller * LPC/eSPI host-to-BMC interface, including * Keyboard and mouse controller interface (KBCI) @@ -53,13 +55,11 @@ Missing devices * eSPI slave interface * Ethernet controllers (GMAC and EMC) - * USB host (USBH) * USB device (USBD) * SMBus controller (SMBF) * Peripheral SPI controller (PSPI) * Analog to Digital Converter (ADC) * SD/MMC host - * Random Number Generator (RNG) * PECI interface * Pulse Width Modulation (PWM) * Tachometer diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst index 905628f3a0..0ebce37a19 100644 --- a/docs/system/deprecated.rst +++ b/docs/system/deprecated.rst @@ -21,17 +21,6 @@ deprecated. System emulator command line arguments -------------------------------------- -``-machine enforce-config-section=on|off`` (since 3.1) -'''''''''''''''''''''''''''''''''''''''''''''''''''''' - -The ``enforce-config-section`` parameter is replaced by the -``-global migration.send-configuration={on|off}`` option. - -``-no-kvm`` (since 1.3.0) -''''''''''''''''''''''''' - -The ``-no-kvm`` argument is now a synonym for setting ``-accel tcg``. - ``-usbdevice`` (since 2.10.0) ''''''''''''''''''''''''''''' @@ -504,6 +493,12 @@ System emulator command line arguments The ``name`` parameter of the ``-net`` option was a synonym for the ``id`` parameter, which should now be used instead. +``-no-kvm`` (removed in 5.2) +'''''''''''''''''''''''''''' + +The ``-no-kvm`` argument was a synonym for setting ``-machine accel=tcg``. + + QEMU Machine Protocol (QMP) commands ------------------------------------ @@ -688,6 +683,12 @@ Support for invalid topologies is removed, the user must ensure topologies described with -smp include all possible cpus, i.e. *sockets* * *cores* * *threads* = *maxcpus*. +``-machine enforce-config-section=on|off`` (removed 5.2) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``enforce-config-section`` property was replaced by the +``-global migration.send-configuration={on|off}`` option. + Block devices ------------- diff --git a/docs/tools/qemu-img.rst b/docs/tools/qemu-img.rst index c35bd64822..b615aa8419 100644 --- a/docs/tools/qemu-img.rst +++ b/docs/tools/qemu-img.rst @@ -188,6 +188,10 @@ Parameters to convert subcommand: allocated target image depending on the host support for getting allocation information. +.. option:: -r + + Rate limit for the convert process + .. option:: --salvage Try to ignore I/O errors when reading. Unless in quiet mode (``-q``), errors @@ -349,7 +353,7 @@ Command description: state after (the attempt at) repairing it. That is, a successful ``-r all`` will yield the exit code 0, independently of the image state before. -.. option:: commit [--object OBJECTDEF] [--image-opts] [-q] [-f FMT] [-t CACHE] [-b BASE] [-d] [-p] FILENAME +.. option:: commit [--object OBJECTDEF] [--image-opts] [-q] [-f FMT] [-t CACHE] [-b BASE] [-r RATE_LIMIT] [-d] [-p] FILENAME Commit the changes recorded in *FILENAME* in its base image or backing file. If the backing file is smaller than the snapshot, then the backing file will be @@ -371,6 +375,8 @@ Command description: garbage data when read. For this reason, ``-b`` implies ``-d`` (so that the top image stays valid). + The rate limit for the commit process is specified by ``-r``. + .. option:: compare [--object OBJECTDEF] [--image-opts] [-f FMT] [-F FMT] [-T SRC_CACHE] [-p] [-q] [-s] [-U] FILENAME1 FILENAME2 Check if two images have the same content. You can compare images with @@ -408,7 +414,7 @@ Command description: 4 Error on reading data -.. option:: convert [--object OBJECTDEF] [--image-opts] [--target-image-opts] [--target-is-zero] [--bitmaps] [-U] [-C] [-c] [-p] [-q] [-n] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-O OUTPUT_FMT] [-B BACKING_FILE] [-o OPTIONS] [-l SNAPSHOT_PARAM] [-S SPARSE_SIZE] [-m NUM_COROUTINES] [-W] FILENAME [FILENAME2 [...]] OUTPUT_FILENAME +.. option:: convert [--object OBJECTDEF] [--image-opts] [--target-image-opts] [--target-is-zero] [--bitmaps] [-U] [-C] [-c] [-p] [-q] [-n] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-O OUTPUT_FMT] [-B BACKING_FILE] [-o OPTIONS] [-l SNAPSHOT_PARAM] [-S SPARSE_SIZE] [-r RATE_LIMIT] [-m NUM_COROUTINES] [-W] FILENAME [FILENAME2 [...]] OUTPUT_FILENAME Convert the disk image *FILENAME* or a snapshot *SNAPSHOT_PARAM* to disk image *OUTPUT_FILENAME* using format *OUTPUT_FMT*. It can diff --git a/docs/tools/virtiofsd.rst b/docs/tools/virtiofsd.rst index 7ecee49834..34a9e40146 100644 --- a/docs/tools/virtiofsd.rst +++ b/docs/tools/virtiofsd.rst @@ -17,13 +17,24 @@ This program is designed to work with QEMU's ``--device vhost-user-fs-pci`` but should work with any virtual machine monitor (VMM) that supports vhost-user. See the Examples section below. -This program must be run as the root user. Upon startup the program will -switch into a new file system namespace with the shared directory tree as its -root. This prevents "file system escapes" due to symlinks and other file -system objects that might lead to files outside the shared directory. The -program also sandboxes itself using seccomp(2) to prevent ptrace(2) and other -vectors that could allow an attacker to compromise the system after gaining -control of the virtiofsd process. +This program must be run as the root user. The program drops privileges where +possible during startup although it must be able to create and access files +with any uid/gid: + +* The ability to invoke syscalls is limited using seccomp(2). +* Linux capabilities(7) are dropped. + +In "namespace" sandbox mode the program switches into a new file system +namespace and invokes pivot_root(2) to make the shared directory tree its root. +A new pid and net namespace is also created to isolate the process. + +In "chroot" sandbox mode the program invokes chroot(2) to make the shared +directory tree its root. This mode is intended for container environments where +the container runtime has already set up the namespaces and the program does +not have permission to create namespaces itself. + +Both sandbox modes prevent "file system escapes" due to symlinks and other file +system objects that might lead to files outside the shared directory. Options ------- @@ -69,6 +80,13 @@ Options * readdirplus|no_readdirplus - Enable/disable readdirplus. The default is ``readdirplus``. + * sandbox=namespace|chroot - + Sandbox mode: + - namespace: Create mount, pid, and net namespaces and pivot_root(2) into + the shared directory. + - chroot: chroot(2) into shared directory (use in containers). + The default is "namespace". + * source=PATH - Share host directory tree located at PATH. This option is required. @@ -109,6 +127,167 @@ Options timeout. ``always`` sets a long cache lifetime at the expense of coherency. The default is ``auto``. +xattr-mapping +------------- + +By default the name of xattr's used by the client are passed through to the server +file system. This can be a problem where either those xattr names are used +by something on the server (e.g. selinux client/server confusion) or if the +virtiofsd is running in a container with restricted privileges where it cannot +access some attributes. + +A mapping of xattr names can be made using -o xattrmap=mapping where the ``mapping`` +string consists of a series of rules. + +The first matching rule terminates the mapping. +The set of rules must include a terminating rule to match any remaining attributes +at the end. + +Each rule consists of a number of fields separated with a separator that is the +first non-white space character in the rule. This separator must then be used +for the whole rule. +White space may be added before and after each rule. + +Using ':' as the separator a rule is of the form: + +``:type:scope:key:prepend:`` + +**scope** is: + +- 'client' - match 'key' against a xattr name from the client for + setxattr/getxattr/removexattr +- 'server' - match 'prepend' against a xattr name from the server + for listxattr +- 'all' - can be used to make a single rule where both the server + and client matches are triggered. + +**type** is one of: + +- 'prefix' - is designed to prepend and strip a prefix; the modified + attributes then being passed on to the client/server. + +- 'ok' - Causes the rule set to be terminated when a match is found + while allowing matching xattr's through unchanged. + It is intended both as a way of explicitly terminating + the list of rules, and to allow some xattr's to skip following rules. + +- 'bad' - If a client tries to use a name matching 'key' it's + denied using EPERM; when the server passes an attribute + name matching 'prepend' it's hidden. In many ways it's use is very like + 'ok' as either an explict terminator or for special handling of certain + patterns. + +**key** is a string tested as a prefix on an attribute name originating +on the client. It maybe empty in which case a 'client' rule +will always match on client names. + +**prepend** is a string tested as a prefix on an attribute name originating +on the server, and used as a new prefix. It may be empty +in which case a 'server' rule will always match on all names from +the server. + +e.g.: + + ``:prefix:client:trusted.:user.virtiofs.:`` + + will match 'trusted.' attributes in client calls and prefix them before + passing them to the server. + + ``:prefix:server::user.virtiofs.:`` + + will strip 'user.virtiofs.' from all server replies. + + ``:prefix:all:trusted.:user.virtiofs.:`` + + combines the previous two cases into a single rule. + + ``:ok:client:user.::`` + + will allow get/set xattr for 'user.' xattr's and ignore + following rules. + + ``:ok:server::security.:`` + + will pass 'securty.' xattr's in listxattr from the server + and ignore following rules. + + ``:ok:all:::`` + + will terminate the rule search passing any remaining attributes + in both directions. + + ``:bad:server::security.:`` + + would hide 'security.' xattr's in listxattr from the server. + +A simpler 'map' type provides a shorter syntax for the common case: + +``:map:key:prepend:`` + +The 'map' type adds a number of separate rules to add **prepend** as a prefix +to the matched **key** (or all attributes if **key** is empty). +There may be at most one 'map' rule and it must be the last rule in the set. + +xattr-mapping Examples +---------------------- + +1) Prefix all attributes with 'user.virtiofs.' + +:: + +-o xattrmap=":prefix:all::user.virtiofs.::bad:all:::" + + +This uses two rules, using : as the field separator; +the first rule prefixes and strips 'user.virtiofs.', +the second rule hides any non-prefixed attributes that +the host set. + +This is equivalent to the 'map' rule: + +:: +-o xattrmap=":map::user.virtiofs.:" + +2) Prefix 'trusted.' attributes, allow others through + +:: + + "/prefix/all/trusted./user.virtiofs./ + /bad/server//trusted./ + /bad/client/user.virtiofs.// + /ok/all///" + + +Here there are four rules, using / as the field +separator, and also demonstrating that new lines can +be included between rules. +The first rule is the prefixing of 'trusted.' and +stripping of 'user.virtiofs.'. +The second rule hides unprefixed 'trusted.' attributes +on the host. +The third rule stops a guest from explicitly setting +the 'user.virtiofs.' path directly. +Finally, the fourth rule lets all remaining attributes +through. + +This is equivalent to the 'map' rule: + +:: +-o xattrmap="/map/trusted./user.virtiofs./" + +3) Hide 'security.' attributes, and allow everything else + +:: + + "/bad/all/security./security./ + /ok/all///' + +The first rule combines what could be separate client and server +rules into a single 'all' rule, matching 'security.' in either +client arguments or lists returned from the host. This stops +the client seeing any 'security.' attributes on the server and +stops it setting any. + Examples -------- diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 7d040827af..fdf4464b94 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -151,6 +151,7 @@ config TOSA select ZAURUS # scoop select MICRODRIVE select PXA2XX + select LED config SPITZ bool @@ -211,6 +212,7 @@ config SBSA_REF select PL031 # RTC select PL061 # GPIO select USB_EHCI_SYSBUS + select WDT_SBSA config SABRELITE bool @@ -404,6 +406,7 @@ config ASPEED_SOC select TMP105 select TMP421 select UNIMP + select LED config MPS2 bool diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index bdb981d2f8..0ef3f6b412 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -20,6 +20,7 @@ #include "hw/i2c/smbus_eeprom.h" #include "hw/misc/pca9552.h" #include "hw/misc/tmp105.h" +#include "hw/misc/led.h" #include "hw/qdev-properties.h" #include "qemu/log.h" #include "sysemu/block-backend.h" @@ -525,9 +526,20 @@ static void sonorapass_bmc_i2c_init(AspeedMachineState *bmc) static void witherspoon_bmc_i2c_init(AspeedMachineState *bmc) { + static const struct { + unsigned gpio_id; + LEDColor color; + const char *description; + bool gpio_polarity; + } pca1_leds[] = { + {13, LED_COLOR_GREEN, "front-fault-4", GPIO_POLARITY_ACTIVE_LOW}, + {14, LED_COLOR_GREEN, "front-power-3", GPIO_POLARITY_ACTIVE_LOW}, + {15, LED_COLOR_GREEN, "front-id-5", GPIO_POLARITY_ACTIVE_LOW}, + }; AspeedSoCState *soc = &bmc->soc; uint8_t *eeprom_buf = g_malloc0(8 * 1024); DeviceState *dev; + LEDState *led; /* Bus 3: TODO bmp280@77 */ /* Bus 3: TODO max31785@52 */ @@ -538,6 +550,14 @@ static void witherspoon_bmc_i2c_init(AspeedMachineState *bmc) aspeed_i2c_get_bus(&soc->i2c, 3), &error_fatal); + for (size_t i = 0; i < ARRAY_SIZE(pca1_leds); i++) { + led = led_create_simple(OBJECT(bmc), + pca1_leds[i].gpio_polarity, + pca1_leds[i].color, + pca1_leds[i].description); + qdev_connect_gpio_out(dev, pca1_leds[i].gpio_id, + qdev_get_gpio_in(DEVICE(led), 0)); + } i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 4), "tmp423", 0x4c); i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 5), "tmp423", 0x4c); diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index 48909a43c3..dcff13433e 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -121,6 +121,9 @@ static void bcm2835_peripherals_init(Object *obj) /* DWC2 */ object_initialize_child(obj, "dwc2", &s->dwc2, TYPE_DWC2_USB); + /* CPRMAN clock manager */ + object_initialize_child(obj, "cprman", &s->cprman, TYPE_BCM2835_CPRMAN); + object_property_add_const_link(OBJECT(&s->dwc2), "dma-mr", OBJECT(&s->gpu_bus_mr)); } @@ -160,6 +163,15 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) return; } + /* CPRMAN clock manager */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->cprman), errp)) { + return; + } + memory_region_add_subregion(&s->peri_mr, CPRMAN_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cprman), 0)); + qdev_connect_clock_in(DEVICE(&s->uart0), "clk", + qdev_get_clock_out(DEVICE(&s->cprman), "uart-out")); + memory_region_add_subregion(&s->peri_mr, ARMCTRL_IC_OFFSET, sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->ic), 0)); sysbus_pass_irq(SYS_BUS_DEVICE(s), SYS_BUS_DEVICE(&s->ic)); @@ -354,8 +366,7 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) create_unimp(s, &s->txp, "bcm2835-txp", TXP_OFFSET, 0x1000); create_unimp(s, &s->armtmr, "bcm2835-sp804", ARMCTRL_TIMER0_1_OFFSET, 0x40); - create_unimp(s, &s->cprman, "bcm2835-cprman", CPRMAN_OFFSET, 0x1000); - create_unimp(s, &s->a2w, "bcm2835-a2w", A2W_OFFSET, 0x1000); + create_unimp(s, &s->powermgt, "bcm2835-powermgt", PM_OFFSET, 0x114); create_unimp(s, &s->i2s, "bcm2835-i2s", I2S_OFFSET, 0x100); create_unimp(s, &s->smi, "bcm2835-smi", SMI_OFFSET, 0x100); create_unimp(s, &s->spi[0], "bcm2835-spi0", SPI0_OFFSET, 0x20); diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c index f15cc3b405..de7ade2878 100644 --- a/hw/arm/bcm2836.c +++ b/hw/arm/bcm2836.c @@ -17,46 +17,45 @@ #include "hw/arm/raspi_platform.h" #include "hw/sysbus.h" -struct BCM283XInfo { +typedef struct BCM283XClass { + /*< private >*/ + DeviceClass parent_class; + /*< public >*/ const char *name; const char *cpu_type; + unsigned core_count; hwaddr peri_base; /* Peripheral base address seen by the CPU */ hwaddr ctrl_base; /* Interrupt controller and mailboxes etc. */ int clusterid; -}; +} BCM283XClass; -static const BCM283XInfo bcm283x_socs[] = { - { - .name = TYPE_BCM2836, - .cpu_type = ARM_CPU_TYPE_NAME("cortex-a7"), - .peri_base = 0x3f000000, - .ctrl_base = 0x40000000, - .clusterid = 0xf, - }, -#ifdef TARGET_AARCH64 - { - .name = TYPE_BCM2837, - .cpu_type = ARM_CPU_TYPE_NAME("cortex-a53"), - .peri_base = 0x3f000000, - .ctrl_base = 0x40000000, - .clusterid = 0x0, - }, -#endif -}; +#define BCM283X_CLASS(klass) \ + OBJECT_CLASS_CHECK(BCM283XClass, (klass), TYPE_BCM283X) +#define BCM283X_GET_CLASS(obj) \ + OBJECT_GET_CLASS(BCM283XClass, (obj), TYPE_BCM283X) + +static Property bcm2836_enabled_cores_property = + DEFINE_PROP_UINT32("enabled-cpus", BCM283XState, enabled_cpus, 0); static void bcm2836_init(Object *obj) { BCM283XState *s = BCM283X(obj); BCM283XClass *bc = BCM283X_GET_CLASS(obj); - const BCM283XInfo *info = bc->info; int n; - for (n = 0; n < BCM283X_NCPUS; n++) { + for (n = 0; n < bc->core_count; n++) { object_initialize_child(obj, "cpu[*]", &s->cpu[n].core, - info->cpu_type); + bc->cpu_type); + } + if (bc->core_count > 1) { + qdev_property_add_static(DEVICE(obj), &bcm2836_enabled_cores_property); + qdev_prop_set_uint32(DEVICE(obj), "enabled-cpus", bc->core_count); } - object_initialize_child(obj, "control", &s->control, TYPE_BCM2836_CONTROL); + if (bc->ctrl_base) { + object_initialize_child(obj, "control", &s->control, + TYPE_BCM2836_CONTROL); + } object_initialize_child(obj, "peripherals", &s->peripherals, TYPE_BCM2835_PERIPHERALS); @@ -66,13 +65,11 @@ static void bcm2836_init(Object *obj) "vcram-size"); } -static void bcm2836_realize(DeviceState *dev, Error **errp) +static bool bcm283x_common_realize(DeviceState *dev, Error **errp) { BCM283XState *s = BCM283X(dev); BCM283XClass *bc = BCM283X_GET_CLASS(dev); - const BCM283XInfo *info = bc->info; Object *obj; - int n; /* common peripherals from bcm2835 */ @@ -81,21 +78,52 @@ static void bcm2836_realize(DeviceState *dev, Error **errp) object_property_add_const_link(OBJECT(&s->peripherals), "ram", obj); if (!sysbus_realize(SYS_BUS_DEVICE(&s->peripherals), errp)) { - return; + return false; } object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->peripherals), "sd-bus"); sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->peripherals), 0, - info->peri_base, 1); + bc->peri_base, 1); + return true; +} + +static void bcm2835_realize(DeviceState *dev, Error **errp) +{ + BCM283XState *s = BCM283X(dev); + + if (!bcm283x_common_realize(dev, errp)) { + return; + } + + if (!qdev_realize(DEVICE(&s->cpu[0].core), NULL, errp)) { + return; + } + + /* Connect irq/fiq outputs from the interrupt controller. */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 0, + qdev_get_gpio_in(DEVICE(&s->cpu[0].core), ARM_CPU_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 1, + qdev_get_gpio_in(DEVICE(&s->cpu[0].core), ARM_CPU_FIQ)); +} + +static void bcm2836_realize(DeviceState *dev, Error **errp) +{ + BCM283XState *s = BCM283X(dev); + BCM283XClass *bc = BCM283X_GET_CLASS(dev); + int n; + + if (!bcm283x_common_realize(dev, errp)) { + return; + } /* bcm2836 interrupt controller (and mailboxes, etc.) */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->control), errp)) { return; } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->control), 0, info->ctrl_base); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->control), 0, bc->ctrl_base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->peripherals), 0, qdev_get_gpio_in_named(DEVICE(&s->control), "gpu-irq", 0)); @@ -104,11 +132,11 @@ static void bcm2836_realize(DeviceState *dev, Error **errp) for (n = 0; n < BCM283X_NCPUS; n++) { /* TODO: this should be converted to a property of ARM_CPU */ - s->cpu[n].core.mp_affinity = (info->clusterid << 8) | n; + s->cpu[n].core.mp_affinity = (bc->clusterid << 8) | n; /* set periphbase/CBAR value for CPU-local registers */ if (!object_property_set_int(OBJECT(&s->cpu[n].core), "reset-cbar", - info->peri_base, errp)) { + bc->peri_base, errp)) { return; } @@ -142,47 +170,77 @@ static void bcm2836_realize(DeviceState *dev, Error **errp) } } -static Property bcm2836_props[] = { - DEFINE_PROP_UINT32("enabled-cpus", BCM283XState, enabled_cpus, - BCM283X_NCPUS), - DEFINE_PROP_END_OF_LIST() -}; - static void bcm283x_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - BCM283XClass *bc = BCM283X_CLASS(oc); - bc->info = data; - dc->realize = bcm2836_realize; - device_class_set_props(dc, bcm2836_props); /* Reason: Must be wired up in code (see raspi_init() function) */ dc->user_creatable = false; } -static const TypeInfo bcm283x_type_info = { - .name = TYPE_BCM283X, - .parent = TYPE_DEVICE, - .instance_size = sizeof(BCM283XState), - .instance_init = bcm2836_init, - .class_size = sizeof(BCM283XClass), - .abstract = true, +static void bcm2835_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + BCM283XClass *bc = BCM283X_CLASS(oc); + + bc->cpu_type = ARM_CPU_TYPE_NAME("arm1176"); + bc->core_count = 1; + bc->peri_base = 0x20000000; + dc->realize = bcm2835_realize; +}; + +static void bcm2836_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + BCM283XClass *bc = BCM283X_CLASS(oc); + + bc->cpu_type = ARM_CPU_TYPE_NAME("cortex-a7"); + bc->core_count = BCM283X_NCPUS; + bc->peri_base = 0x3f000000; + bc->ctrl_base = 0x40000000; + bc->clusterid = 0xf; + dc->realize = bcm2836_realize; }; -static void bcm2836_register_types(void) +#ifdef TARGET_AARCH64 +static void bcm2837_class_init(ObjectClass *oc, void *data) { - int i; - - type_register_static(&bcm283x_type_info); - for (i = 0; i < ARRAY_SIZE(bcm283x_socs); i++) { - TypeInfo ti = { - .name = bcm283x_socs[i].name, - .parent = TYPE_BCM283X, - .class_init = bcm283x_class_init, - .class_data = (void *) &bcm283x_socs[i], - }; - type_register(&ti); + DeviceClass *dc = DEVICE_CLASS(oc); + BCM283XClass *bc = BCM283X_CLASS(oc); + + bc->cpu_type = ARM_CPU_TYPE_NAME("cortex-a53"); + bc->core_count = BCM283X_NCPUS; + bc->peri_base = 0x3f000000; + bc->ctrl_base = 0x40000000; + bc->clusterid = 0x0; + dc->realize = bcm2836_realize; +}; +#endif + +static const TypeInfo bcm283x_types[] = { + { + .name = TYPE_BCM2835, + .parent = TYPE_BCM283X, + .class_init = bcm2835_class_init, + }, { + .name = TYPE_BCM2836, + .parent = TYPE_BCM283X, + .class_init = bcm2836_class_init, +#ifdef TARGET_AARCH64 + }, { + .name = TYPE_BCM2837, + .parent = TYPE_BCM283X, + .class_init = bcm2837_class_init, +#endif + }, { + .name = TYPE_BCM283X, + .parent = TYPE_DEVICE, + .instance_size = sizeof(BCM283XState), + .instance_init = bcm2836_init, + .class_size = sizeof(BCM283XClass), + .class_init = bcm283x_class_init, + .abstract = true, } -} +}; -type_init(bcm2836_register_types) +DEFINE_TYPES(bcm283x_types) diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index da0510d7ce..f71087860d 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -92,10 +92,12 @@ static void hb_reset_secondary(ARMCPU *cpu, const struct arm_boot_info *info) address_space_stl_notdirty(&address_space_memory, SMP_BOOT_REG + 0x30, 0, MEMTXATTRS_UNSPECIFIED, NULL); + /* fallthrough */ case 3: address_space_stl_notdirty(&address_space_memory, SMP_BOOT_REG + 0x20, 0, MEMTXATTRS_UNSPECIFIED, NULL); + /* fallthrough */ case 2: address_space_stl_notdirty(&address_space_memory, SMP_BOOT_REG + 0x10, 0, diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c index 037f3a26f2..47e2b6fc40 100644 --- a/hw/arm/npcm7xx.c +++ b/hw/arm/npcm7xx.c @@ -44,6 +44,11 @@ #define NPCM7XX_GCR_BA (0xf0800000) #define NPCM7XX_CLK_BA (0xf0801000) #define NPCM7XX_MC_BA (0xf0824000) +#define NPCM7XX_RNG_BA (0xf000b000) + +/* USB Host modules */ +#define NPCM7XX_EHCI_BA (0xf0806000) +#define NPCM7XX_OHCI_BA (0xf0807000) /* Internal AHB SRAM */ #define NPCM7XX_RAM3_BA (0xc0008000) @@ -86,6 +91,19 @@ enum NPCM7xxInterrupt { NPCM7XX_TIMER12_IRQ, NPCM7XX_TIMER13_IRQ, NPCM7XX_TIMER14_IRQ, + NPCM7XX_WDG0_IRQ = 47, /* Timer Module 0 Watchdog */ + NPCM7XX_WDG1_IRQ, /* Timer Module 1 Watchdog */ + NPCM7XX_WDG2_IRQ, /* Timer Module 2 Watchdog */ + NPCM7XX_EHCI_IRQ = 61, + NPCM7XX_OHCI_IRQ = 62, + NPCM7XX_GPIO0_IRQ = 116, + NPCM7XX_GPIO1_IRQ, + NPCM7XX_GPIO2_IRQ, + NPCM7XX_GPIO3_IRQ, + NPCM7XX_GPIO4_IRQ, + NPCM7XX_GPIO5_IRQ, + NPCM7XX_GPIO6_IRQ, + NPCM7XX_GPIO7_IRQ, }; /* Total number of GIC interrupts, including internal Cortex-A9 interrupts. */ @@ -121,6 +139,55 @@ static const hwaddr npcm7xx_fiu3_flash_addr[] = { }; static const struct { + hwaddr regs_addr; + uint32_t unconnected_pins; + uint32_t reset_pu; + uint32_t reset_pd; + uint32_t reset_osrc; + uint32_t reset_odsc; +} npcm7xx_gpio[] = { + { + .regs_addr = 0xf0010000, + .reset_pu = 0xff03ffff, + .reset_pd = 0x00fc0000, + }, { + .regs_addr = 0xf0011000, + .unconnected_pins = 0x0000001e, + .reset_pu = 0xfefffe07, + .reset_pd = 0x010001e0, + }, { + .regs_addr = 0xf0012000, + .reset_pu = 0x780fffff, + .reset_pd = 0x07f00000, + .reset_odsc = 0x00700000, + }, { + .regs_addr = 0xf0013000, + .reset_pu = 0x00fc0000, + .reset_pd = 0xff000000, + }, { + .regs_addr = 0xf0014000, + .reset_pu = 0xffffffff, + }, { + .regs_addr = 0xf0015000, + .reset_pu = 0xbf83f801, + .reset_pd = 0x007c0000, + .reset_osrc = 0x000000f1, + .reset_odsc = 0x3f9f80f1, + }, { + .regs_addr = 0xf0016000, + .reset_pu = 0xfc00f801, + .reset_pd = 0x000007fe, + .reset_odsc = 0x00000800, + }, { + .regs_addr = 0xf0017000, + .unconnected_pins = 0xffffff00, + .reset_pu = 0x0000007f, + .reset_osrc = 0x0000007f, + .reset_odsc = 0x0000007f, + }, +}; + +static const struct { const char *name; hwaddr regs_addr; int cs_count; @@ -253,11 +320,19 @@ static void npcm7xx_init(Object *obj) object_initialize_child(obj, "otp2", &s->fuse_array, TYPE_NPCM7XX_FUSE_ARRAY); object_initialize_child(obj, "mc", &s->mc, TYPE_NPCM7XX_MC); + object_initialize_child(obj, "rng", &s->rng, TYPE_NPCM7XX_RNG); for (i = 0; i < ARRAY_SIZE(s->tim); i++) { object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER); } + for (i = 0; i < ARRAY_SIZE(s->gpio); i++) { + object_initialize_child(obj, "gpio[*]", &s->gpio[i], TYPE_NPCM7XX_GPIO); + } + + object_initialize_child(obj, "ehci", &s->ehci, TYPE_NPCM7XX_EHCI); + object_initialize_child(obj, "ohci", &s->ohci, TYPE_SYSBUS_OHCI); + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_fiu) != ARRAY_SIZE(s->fiu)); for (i = 0; i < ARRAY_SIZE(s->fiu); i++) { object_initialize_child(obj, npcm7xx_fiu[i].name, &s->fiu[i], @@ -353,6 +428,15 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp) qemu_irq irq = npcm7xx_irq(s, first_irq + j); sysbus_connect_irq(sbd, j, irq); } + + /* IRQ for watchdogs */ + sysbus_connect_irq(sbd, NPCM7XX_TIMERS_PER_CTRL, + npcm7xx_irq(s, NPCM7XX_WDG0_IRQ + i)); + /* GPIO that connects clk module with watchdog */ + qdev_connect_gpio_out_named(DEVICE(&s->tim[i]), + NPCM7XX_WATCHDOG_RESET_GPIO_OUT, 0, + qdev_get_gpio_in_named(DEVICE(&s->clk), + NPCM7XX_WATCHDOG_RESET_GPIO_IN, i)); } /* UART0..3 (16550 compatible) */ @@ -362,6 +446,45 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp) serial_hd(i), DEVICE_LITTLE_ENDIAN); } + /* Random Number Generator. Cannot fail. */ + sysbus_realize(SYS_BUS_DEVICE(&s->rng), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->rng), 0, NPCM7XX_RNG_BA); + + /* GPIO modules. Cannot fail. */ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_gpio) != ARRAY_SIZE(s->gpio)); + for (i = 0; i < ARRAY_SIZE(s->gpio); i++) { + Object *obj = OBJECT(&s->gpio[i]); + + object_property_set_uint(obj, "reset-pullup", + npcm7xx_gpio[i].reset_pu, &error_abort); + object_property_set_uint(obj, "reset-pulldown", + npcm7xx_gpio[i].reset_pd, &error_abort); + object_property_set_uint(obj, "reset-osrc", + npcm7xx_gpio[i].reset_osrc, &error_abort); + object_property_set_uint(obj, "reset-odsc", + npcm7xx_gpio[i].reset_odsc, &error_abort); + sysbus_realize(SYS_BUS_DEVICE(obj), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(obj), 0, npcm7xx_gpio[i].regs_addr); + sysbus_connect_irq(SYS_BUS_DEVICE(obj), 0, + npcm7xx_irq(s, NPCM7XX_GPIO0_IRQ + i)); + } + + /* USB Host */ + object_property_set_bool(OBJECT(&s->ehci), "companion-enable", true, + &error_abort); + sysbus_realize(SYS_BUS_DEVICE(&s->ehci), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ehci), 0, NPCM7XX_EHCI_BA); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci), 0, + npcm7xx_irq(s, NPCM7XX_EHCI_IRQ)); + + object_property_set_str(OBJECT(&s->ohci), "masterbus", "usb-bus.0", + &error_abort); + object_property_set_uint(OBJECT(&s->ohci), "num-ports", 1, &error_abort); + sysbus_realize(SYS_BUS_DEVICE(&s->ohci), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ohci), 0, NPCM7XX_OHCI_BA); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->ohci), 0, + npcm7xx_irq(s, NPCM7XX_OHCI_IRQ)); + /* * Flash Interface Unit (FIU). Can fail if incorrect number of chip selects * specified, but this is a programming error. @@ -400,7 +523,6 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp) create_unimplemented_device("npcm7xx.vdmx", 0xe0800000, 4 * KiB); create_unimplemented_device("npcm7xx.pcierc", 0xe1000000, 64 * KiB); create_unimplemented_device("npcm7xx.kcs", 0xf0007000, 4 * KiB); - create_unimplemented_device("npcm7xx.rng", 0xf000b000, 4 * KiB); create_unimplemented_device("npcm7xx.adc", 0xf000c000, 4 * KiB); create_unimplemented_device("npcm7xx.gfxi", 0xf000e000, 4 * KiB); create_unimplemented_device("npcm7xx.gpio[0]", 0xf0010000, 4 * KiB); @@ -447,8 +569,6 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp) create_unimplemented_device("npcm7xx.mcphy", 0xf05f0000, 64 * KiB); create_unimplemented_device("npcm7xx.gmac1", 0xf0802000, 8 * KiB); create_unimplemented_device("npcm7xx.gmac2", 0xf0804000, 8 * KiB); - create_unimplemented_device("npcm7xx.ehci", 0xf0806000, 4 * KiB); - create_unimplemented_device("npcm7xx.ohci", 0xf0807000, 4 * KiB); create_unimplemented_device("npcm7xx.vcd", 0xf0810000, 64 * KiB); create_unimplemented_device("npcm7xx.ece", 0xf0820000, 8 * KiB); create_unimplemented_device("npcm7xx.vdma", 0xf0822000, 8 * KiB); diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c index b5b30f0f38..990509d385 100644 --- a/hw/arm/raspi.c +++ b/hw/arm/raspi.c @@ -70,6 +70,7 @@ FIELD(REV_CODE, MEMORY_SIZE, 20, 3); FIELD(REV_CODE, STYLE, 23, 1); typedef enum RaspiProcessorId { + PROCESSOR_ID_BCM2835 = 0, PROCESSOR_ID_BCM2836 = 1, PROCESSOR_ID_BCM2837 = 2, } RaspiProcessorId; @@ -78,6 +79,7 @@ static const struct { const char *type; int cores_count; } soc_property[] = { + [PROCESSOR_ID_BCM2835] = {TYPE_BCM2835, 1}, [PROCESSOR_ID_BCM2836] = {TYPE_BCM2836, BCM283X_NCPUS}, [PROCESSOR_ID_BCM2837] = {TYPE_BCM2837, BCM283X_NCPUS}, }; @@ -317,6 +319,24 @@ static void raspi_machine_class_common_init(MachineClass *mc, mc->default_ram_id = "ram"; }; +static void raspi0_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc); + + rmc->board_rev = 0x920092; /* Revision 1.2 */ + raspi_machine_class_common_init(mc, rmc->board_rev); +}; + +static void raspi1ap_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc); + + rmc->board_rev = 0x900021; /* Revision 1.1 */ + raspi_machine_class_common_init(mc, rmc->board_rev); +}; + static void raspi2b_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -328,6 +348,15 @@ static void raspi2b_machine_class_init(ObjectClass *oc, void *data) }; #ifdef TARGET_AARCH64 +static void raspi3ap_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc); + + rmc->board_rev = 0x9020e0; /* Revision 1.0 */ + raspi_machine_class_common_init(mc, rmc->board_rev); +}; + static void raspi3b_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -341,11 +370,23 @@ static void raspi3b_machine_class_init(ObjectClass *oc, void *data) static const TypeInfo raspi_machine_types[] = { { + .name = MACHINE_TYPE_NAME("raspi0"), + .parent = TYPE_RASPI_MACHINE, + .class_init = raspi0_machine_class_init, + }, { + .name = MACHINE_TYPE_NAME("raspi1ap"), + .parent = TYPE_RASPI_MACHINE, + .class_init = raspi1ap_machine_class_init, + }, { .name = MACHINE_TYPE_NAME("raspi2b"), .parent = TYPE_RASPI_MACHINE, .class_init = raspi2b_machine_class_init, #ifdef TARGET_AARCH64 }, { + .name = MACHINE_TYPE_NAME("raspi3ap"), + .parent = TYPE_RASPI_MACHINE, + .class_init = raspi3ap_machine_class_init, + }, { .name = MACHINE_TYPE_NAME("raspi3b"), .parent = TYPE_RASPI_MACHINE, .class_init = raspi3b_machine_class_init, diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index 01863510d0..7d9e180c0d 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -40,6 +40,7 @@ #include "hw/qdev-properties.h" #include "hw/usb.h" #include "hw/char/pl011.h" +#include "hw/watchdog/sbsa_gwdt.h" #include "net/net.h" #include "qom/object.h" @@ -64,6 +65,9 @@ enum { SBSA_GIC_DIST, SBSA_GIC_REDIST, SBSA_SECURE_EC, + SBSA_GWDT, + SBSA_GWDT_REFRESH, + SBSA_GWDT_CONTROL, SBSA_SMMU, SBSA_UART, SBSA_RTC, @@ -104,6 +108,8 @@ static const MemMapEntry sbsa_ref_memmap[] = { [SBSA_GIC_DIST] = { 0x40060000, 0x00010000 }, [SBSA_GIC_REDIST] = { 0x40080000, 0x04000000 }, [SBSA_SECURE_EC] = { 0x50000000, 0x00001000 }, + [SBSA_GWDT_REFRESH] = { 0x50010000, 0x00001000 }, + [SBSA_GWDT_CONTROL] = { 0x50011000, 0x00001000 }, [SBSA_UART] = { 0x60000000, 0x00001000 }, [SBSA_RTC] = { 0x60010000, 0x00001000 }, [SBSA_GPIO] = { 0x60020000, 0x00001000 }, @@ -134,6 +140,7 @@ static const int sbsa_ref_irqmap[] = { [SBSA_AHCI] = 10, [SBSA_EHCI] = 11, [SBSA_SMMU] = 12, /* ... to 15 */ + [SBSA_GWDT] = 16, }; static uint64_t sbsa_ref_cpu_mp_affinity(SBSAMachineState *sms, int idx) @@ -448,6 +455,20 @@ static void create_rtc(const SBSAMachineState *sms) sysbus_create_simple("pl031", base, qdev_get_gpio_in(sms->gic, irq)); } +static void create_wdt(const SBSAMachineState *sms) +{ + hwaddr rbase = sbsa_ref_memmap[SBSA_GWDT_REFRESH].base; + hwaddr cbase = sbsa_ref_memmap[SBSA_GWDT_CONTROL].base; + DeviceState *dev = qdev_new(TYPE_WDT_SBSA); + SysBusDevice *s = SYS_BUS_DEVICE(dev); + int irq = sbsa_ref_irqmap[SBSA_GWDT]; + + sysbus_realize_and_unref(s, &error_fatal); + sysbus_mmio_map(s, 0, rbase); + sysbus_mmio_map(s, 1, cbase); + sysbus_connect_irq(s, 0, qdev_get_gpio_in(sms->gic, irq)); +} + static DeviceState *gpio_key_dev; static void sbsa_ref_powerdown_req(Notifier *n, void *opaque) { @@ -731,6 +752,8 @@ static void sbsa_ref_init(MachineState *machine) create_rtc(sms); + create_wdt(sms); + create_gpio(sms); create_ahci(sms); diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 0122700e72..2017ba7a5a 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -1440,6 +1440,7 @@ static const VMStateDescription vmstate_smmuv3 = { .name = "smmuv3", .version_id = 1, .minimum_version_id = 1, + .priority = MIG_PRI_IOMMU, .fields = (VMStateField[]) { VMSTATE_UINT32(features, SMMUv3State), VMSTATE_UINT8(sid_size, SMMUv3State), diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c index c196f0d2f8..fe88ed89fe 100644 --- a/hw/arm/tosa.c +++ b/hw/arm/tosa.c @@ -24,6 +24,7 @@ #include "hw/irq.h" #include "hw/ssi/ssi.h" #include "hw/sysbus.h" +#include "hw/misc/led.h" #include "exec/address-spaces.h" #include "qom/object.h" @@ -81,26 +82,6 @@ struct TosaMiscGPIOState { SysBusDevice parent_obj; }; -static void tosa_gpio_leds(void *opaque, int line, int level) -{ - switch (line) { - case 0: - fprintf(stderr, "blue LED %s.\n", level ? "on" : "off"); - break; - case 1: - fprintf(stderr, "green LED %s.\n", level ? "on" : "off"); - break; - case 2: - fprintf(stderr, "amber LED %s.\n", level ? "on" : "off"); - break; - case 3: - fprintf(stderr, "wlan LED %s.\n", level ? "on" : "off"); - break; - default: - g_assert_not_reached(); - } -} - static void tosa_reset(void *opaque, int line, int level) { if (level) { @@ -112,7 +93,6 @@ static void tosa_misc_gpio_init(Object *obj) { DeviceState *dev = DEVICE(obj); - qdev_init_gpio_in_named(dev, tosa_gpio_leds, "leds", 4); qdev_init_gpio_in_named(dev, tosa_reset, "reset", 1); } @@ -122,6 +102,7 @@ static void tosa_gpio_setup(PXA2xxState *cpu, TC6393xbState *tmio) { DeviceState *misc_gpio; + LEDState *led[4]; misc_gpio = sysbus_create_simple(TYPE_TOSA_MISC_GPIO, -1, NULL); @@ -143,14 +124,23 @@ static void tosa_gpio_setup(PXA2xxState *cpu, qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_JC_CF_IRQ), NULL); + led[0] = led_create_simple(OBJECT(misc_gpio), GPIO_POLARITY_ACTIVE_HIGH, + LED_COLOR_BLUE, "bluetooth"); + led[1] = led_create_simple(OBJECT(misc_gpio), GPIO_POLARITY_ACTIVE_HIGH, + LED_COLOR_GREEN, "note"); + led[2] = led_create_simple(OBJECT(misc_gpio), GPIO_POLARITY_ACTIVE_HIGH, + LED_COLOR_AMBER, "charger-error"); + led[3] = led_create_simple(OBJECT(misc_gpio), GPIO_POLARITY_ACTIVE_HIGH, + LED_COLOR_GREEN, "wlan"); + qdev_connect_gpio_out(scp1, TOSA_GPIO_BT_LED, - qdev_get_gpio_in_named(misc_gpio, "leds", 0)); + qdev_get_gpio_in(DEVICE(led[0]), 0)); qdev_connect_gpio_out(scp1, TOSA_GPIO_NOTE_LED, - qdev_get_gpio_in_named(misc_gpio, "leds", 1)); + qdev_get_gpio_in(DEVICE(led[1]), 0)); qdev_connect_gpio_out(scp1, TOSA_GPIO_CHRG_ERR_LED, - qdev_get_gpio_in_named(misc_gpio, "leds", 2)); + qdev_get_gpio_in(DEVICE(led[2]), 0)); qdev_connect_gpio_out(scp1, TOSA_GPIO_WLAN_LED, - qdev_get_gpio_in_named(misc_gpio, "leds", 3)); + qdev_get_gpio_in(DEVICE(led[3]), 0)); qdev_connect_gpio_out(scp1, TOSA_GPIO_TC6393XB_L3V_ON, tc6393xb_l3v_get(tmio)); diff --git a/hw/arm/trace-events b/hw/arm/trace-events index c8a4d80f6b..a335ee891d 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -41,7 +41,7 @@ smmuv3_get_cd(uint64_t addr) "CD addr: 0x%"PRIx64 smmuv3_decode_cd(uint32_t oas) "oas=%d" smmuv3_decode_cd_tt(int i, uint32_t tsz, uint64_t ttb, uint32_t granule_sz, bool had) "TT[%d]:tsz:%d ttb:0x%"PRIx64" granule_sz:%d had:%d" smmuv3_cmdq_cfgi_ste(int streamid) "streamid =%d" -smmuv3_cmdq_cfgi_ste_range(int start, int end) "start=0x%d - end=0x%d" +smmuv3_cmdq_cfgi_ste_range(int start, int end) "start=0x%x - end=0x%x" smmuv3_cmdq_cfgi_cd(uint32_t sid) "streamid = %d" smmuv3_config_cache_hit(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache HIT for sid %d (hits=%d, misses=%d, hit rate=%d)" smmuv3_config_cache_miss(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache MISS for sid %d (hits=%d, misses=%d, hit rate=%d)" diff --git a/hw/arm/virt.c b/hw/arm/virt.c index e465a988d6..27dbeb549e 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -2261,12 +2261,8 @@ static void virt_memory_plug(HotplugHandler *hotplug_dev, VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); MachineState *ms = MACHINE(hotplug_dev); bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); - Error *local_err = NULL; - pc_dimm_plug(PC_DIMM(dev), MACHINE(vms), &local_err); - if (local_err) { - goto out; - } + pc_dimm_plug(PC_DIMM(dev), MACHINE(vms)); if (is_nvdimm) { nvdimm_plug(ms->nvdimms_state); @@ -2274,9 +2270,6 @@ static void virt_memory_plug(HotplugHandler *hotplug_dev, hotplug_handler_plug(HOTPLUG_HANDLER(vms->acpi_dev), dev, &error_abort); - -out: - error_propagate(errp, local_err); } static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 03e23201b1..ee1282241e 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -561,6 +561,7 @@ static void versal_virt_machine_class_init(ObjectClass *oc, void *data) mc->desc = "Xilinx Versal Virtual development board"; mc->init = versal_virt_init; + mc->min_cpus = XLNX_VERSAL_NR_ACPUS; mc->max_cpus = XLNX_VERSAL_NR_ACPUS; mc->default_cpus = XLNX_VERSAL_NR_ACPUS; mc->no_cdrom = true; diff --git a/hw/char/pl011.c b/hw/char/pl011.c index 13e784f9d9..ede16c781c 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -22,6 +22,7 @@ #include "hw/char/pl011.h" #include "hw/irq.h" #include "hw/sysbus.h" +#include "hw/qdev-clock.h" #include "migration/vmstate.h" #include "chardev/char-fe.h" #include "qemu/log.h" @@ -169,6 +170,25 @@ static void pl011_set_read_trigger(PL011State *s) s->read_trigger = 1; } +static unsigned int pl011_get_baudrate(const PL011State *s) +{ + uint64_t clk; + + if (s->fbrd == 0) { + return 0; + } + + clk = clock_get_hz(s->clk); + return (clk / ((s->ibrd << 6) + s->fbrd)) << 2; +} + +static void pl011_trace_baudrate_change(const PL011State *s) +{ + trace_pl011_baudrate_change(pl011_get_baudrate(s), + clock_get_hz(s->clk), + s->ibrd, s->fbrd); +} + static void pl011_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { @@ -198,9 +218,11 @@ static void pl011_write(void *opaque, hwaddr offset, break; case 9: /* UARTIBRD */ s->ibrd = value; + pl011_trace_baudrate_change(s); break; case 10: /* UARTFBRD */ s->fbrd = value; + pl011_trace_baudrate_change(s); break; case 11: /* UARTLCR_H */ /* Reset the FIFO state on FIFO enable or disable */ @@ -286,12 +308,29 @@ static void pl011_event(void *opaque, QEMUChrEvent event) pl011_put_fifo(opaque, 0x400); } +static void pl011_clock_update(void *opaque) +{ + PL011State *s = PL011(opaque); + + pl011_trace_baudrate_change(s); +} + static const MemoryRegionOps pl011_ops = { .read = pl011_read, .write = pl011_write, .endianness = DEVICE_NATIVE_ENDIAN, }; +static const VMStateDescription vmstate_pl011_clock = { + .name = "pl011/clock", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_CLOCK(clk, PL011State), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_pl011 = { .name = "pl011", .version_id = 2, @@ -314,6 +353,10 @@ static const VMStateDescription vmstate_pl011 = { VMSTATE_INT32(read_count, PL011State), VMSTATE_INT32(read_trigger, PL011State), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * []) { + &vmstate_pl011_clock, + NULL } }; @@ -334,6 +377,8 @@ static void pl011_init(Object *obj) sysbus_init_irq(sbd, &s->irq[i]); } + s->clk = qdev_init_clock_in(DEVICE(obj), "clk", pl011_clock_update, s); + s->read_trigger = 1; s->ifl = 0x12; s->cr = 0x300; diff --git a/hw/char/trace-events b/hw/char/trace-events index 609df10fed..81026f6612 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -65,6 +65,7 @@ pl011_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" pl011_can_receive(uint32_t lcr, int read_count, int r) "LCR 0x%08x read_count %d returning %d" pl011_put_fifo(uint32_t c, int read_count) "new char 0x%x read_count now %d" pl011_put_fifo_full(void) "FIFO now full, RXFF set" +pl011_baudrate_change(unsigned int baudrate, uint64_t clock, uint32_t ibrd, uint32_t fbrd) "new baudrate %u (clk: %" PRIu64 "hz, ibrd: %" PRIu32 ", fbrd: %" PRIu32 ")" # cmsdk-apb-uart.c cmsdk_apb_uart_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB UART read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" diff --git a/hw/core/clock.c b/hw/core/clock.c index f866717a83..8c6af223e7 100644 --- a/hw/core/clock.c +++ b/hw/core/clock.c @@ -54,8 +54,8 @@ bool clock_set(Clock *clk, uint64_t period) if (clk->period == period) { return false; } - trace_clock_set(CLOCK_PATH(clk), CLOCK_PERIOD_TO_NS(clk->period), - CLOCK_PERIOD_TO_NS(period)); + trace_clock_set(CLOCK_PATH(clk), CLOCK_PERIOD_TO_HZ(clk->period), + CLOCK_PERIOD_TO_HZ(period)); clk->period = period; return true; @@ -69,7 +69,7 @@ static void clock_propagate_period(Clock *clk, bool call_callbacks) if (child->period != clk->period) { child->period = clk->period; trace_clock_update(CLOCK_PATH(child), CLOCK_PATH(clk), - CLOCK_PERIOD_TO_NS(clk->period), + CLOCK_PERIOD_TO_HZ(clk->period), call_callbacks); if (call_callbacks && child->callback) { child->callback(child->callback_opaque); diff --git a/hw/core/machine.c b/hw/core/machine.c index d740a7e963..c5e0e79e6d 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -411,24 +411,6 @@ static bool machine_get_suppress_vmdesc(Object *obj, Error **errp) return ms->suppress_vmdesc; } -static void machine_set_enforce_config_section(Object *obj, bool value, - Error **errp) -{ - MachineState *ms = MACHINE(obj); - - warn_report("enforce-config-section is deprecated, please use " - "-global migration.send-configuration=on|off instead"); - - ms->enforce_config_section = value; -} - -static bool machine_get_enforce_config_section(Object *obj, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - return ms->enforce_config_section; -} - static char *machine_get_memory_encryption(Object *obj, Error **errp) { MachineState *ms = MACHINE(obj); @@ -857,11 +839,6 @@ static void machine_class_init(ObjectClass *oc, void *data) object_class_property_set_description(oc, "suppress-vmdesc", "Set on to disable self-describing migration"); - object_class_property_add_bool(oc, "enforce-config-section", - machine_get_enforce_config_section, machine_set_enforce_config_section); - object_class_property_set_description(oc, "enforce-config-section", - "Set on to enforce configuration section migration"); - object_class_property_add_str(oc, "memory-encryption", machine_get_memory_encryption, machine_set_memory_encryption); object_class_property_set_description(oc, "memory-encryption", @@ -876,8 +853,12 @@ static void machine_class_init(ObjectClass *oc, void *data) static void machine_class_base_init(ObjectClass *oc, void *data) { + MachineClass *mc = MACHINE_CLASS(oc); + mc->max_cpus = mc->max_cpus ?: 1; + mc->min_cpus = mc->min_cpus ?: 1; + mc->default_cpus = mc->default_cpus ?: 1; + if (!object_class_is_abstract(oc)) { - MachineClass *mc = MACHINE_CLASS(oc); const char *cname = object_class_get_name(oc); assert(g_str_has_suffix(cname, TYPE_MACHINE_SUFFIX)); mc->name = g_strndup(cname, @@ -926,6 +907,13 @@ static void machine_initfn(Object *obj) /* Register notifier when init is done for sysbus sanity checks */ ms->sysbus_notifier.notify = machine_init_notify; qemu_add_machine_init_done_notifier(&ms->sysbus_notifier); + + /* default to mc->default_cpus */ + ms->smp.cpus = mc->default_cpus; + ms->smp.max_cpus = mc->default_cpus; + ms->smp.cores = 1; + ms->smp.threads = 1; + ms->smp.sockets = 1; } static void machine_finalize(Object *obj) diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c index c6d2beb1da..2aa97cb665 100644 --- a/hw/core/ptimer.c +++ b/hw/core/ptimer.c @@ -117,6 +117,10 @@ static void ptimer_reload(ptimer_state *s, int delta_adjust) } if (delta == 0) { + if (s->enabled == 0) { + /* trigger callback disabled the timer already */ + return; + } if (!qtest_enabled()) { fprintf(stderr, "Timer with delta zero, disabling\n"); } diff --git a/hw/core/qdev-clock.c b/hw/core/qdev-clock.c index 6a9a340d0f..eb05f2a13c 100644 --- a/hw/core/qdev-clock.c +++ b/hw/core/qdev-clock.c @@ -61,6 +61,14 @@ static NamedClockList *qdev_init_clocklist(DeviceState *dev, const char *name, object_get_typename(OBJECT(clk)), (Object **) &ncl->clock, NULL, OBJ_PROP_LINK_STRONG); + /* + * Since the link property has the OBJ_PROP_LINK_STRONG flag, the clk + * object reference count gets decremented on property deletion. + * However object_property_add_link does not increment it since it + * doesn't know the linked object. Increment it here to ensure the + * aliased clock stays alive during this device life-time. + */ + object_ref(OBJECT(clk)); } ncl->clock = clk; diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 49bdd12581..b81a4e8d14 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -30,6 +30,7 @@ #include "sysemu/blockdev.h" #include "net/net.h" #include "hw/pci/pci.h" +#include "util/block-helpers.h" static bool check_prop_still_unset(DeviceState *dev, const char *name, const void *old_val, const char *new_val, @@ -576,16 +577,6 @@ const PropertyInfo qdev_prop_losttickpolicy = { /* --- blocksize --- */ -/* lower limit is sector size */ -#define MIN_BLOCK_SIZE 512 -#define MIN_BLOCK_SIZE_STR "512 B" -/* - * upper limit is arbitrary, 2 MiB looks sufficient for all sensible uses, and - * matches qcow2 cluster size limit - */ -#define MAX_BLOCK_SIZE (2 * MiB) -#define MAX_BLOCK_SIZE_STR "2 MiB" - static void set_blocksize(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -593,6 +584,7 @@ static void set_blocksize(Object *obj, Visitor *v, const char *name, Property *prop = opaque; uint32_t *ptr = qdev_get_prop_ptr(dev, prop); uint64_t value; + Error *local_err = NULL; if (dev->realized) { qdev_prop_set_after_realize(dev, name, errp); @@ -602,24 +594,11 @@ static void set_blocksize(Object *obj, Visitor *v, const char *name, if (!visit_type_size(v, name, &value, errp)) { return; } - /* value of 0 means "unset" */ - if (value && (value < MIN_BLOCK_SIZE || value > MAX_BLOCK_SIZE)) { - error_setg(errp, - "Property %s.%s doesn't take value %" PRIu64 - " (minimum: " MIN_BLOCK_SIZE_STR - ", maximum: " MAX_BLOCK_SIZE_STR ")", - dev->id ? : "", name, value); - return; - } - - /* We rely on power-of-2 blocksizes for bitmasks */ - if ((value & (value - 1)) != 0) { - error_setg(errp, - "Property %s.%s doesn't take value '%" PRId64 "', " - "it's not a power of 2", dev->id ?: "", name, (int64_t)value); + check_block_size(dev->id ? : "", name, value, &local_err); + if (local_err) { + error_propagate(errp, local_err); return; } - *ptr = value; } diff --git a/hw/core/trace-events b/hw/core/trace-events index 1ac60ede6b..360ddeb2c8 100644 --- a/hw/core/trace-events +++ b/hw/core/trace-events @@ -31,6 +31,6 @@ resettable_transitional_function(void *obj, const char *objtype) "obj=%p(%s)" # clock.c clock_set_source(const char *clk, const char *src) "'%s', src='%s'" clock_disconnect(const char *clk) "'%s'" -clock_set(const char *clk, uint64_t old, uint64_t new) "'%s', ns=%"PRIu64"->%"PRIu64 +clock_set(const char *clk, uint64_t old, uint64_t new) "'%s', %"PRIu64"Hz->%"PRIu64"Hz" clock_propagate(const char *clk) "'%s'" -clock_update(const char *clk, const char *src, uint64_t val, int cb) "'%s', src='%s', ns=%"PRIu64", cb=%d" +clock_update(const char *clk, const char *src, uint64_t hz, int cb) "'%s', src='%s', val=%"PRIu64"Hz cb=%d" diff --git a/hw/gpio/meson.build b/hw/gpio/meson.build index 86cae9a0f3..5c0a7d7b95 100644 --- a/hw/gpio/meson.build +++ b/hw/gpio/meson.build @@ -6,6 +6,7 @@ softmmu_ss.add(when: 'CONFIG_PUV3', if_true: files('puv3_gpio.c')) softmmu_ss.add(when: 'CONFIG_ZAURUS', if_true: files('zaurus.c')) softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_gpio.c')) +softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_gpio.c')) softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_gpio.c')) softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_gpio.c')) softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_gpio.c')) diff --git a/hw/gpio/npcm7xx_gpio.c b/hw/gpio/npcm7xx_gpio.c new file mode 100644 index 0000000000..3376901ab1 --- /dev/null +++ b/hw/gpio/npcm7xx_gpio.c @@ -0,0 +1,424 @@ +/* + * Nuvoton NPCM7xx General Purpose Input / Output (GPIO) + * + * Copyright 2020 Google LLC + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "qemu/osdep.h" + +#include "hw/gpio/npcm7xx_gpio.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "trace.h" + +/* 32-bit register indices. */ +enum NPCM7xxGPIORegister { + NPCM7XX_GPIO_TLOCK1, + NPCM7XX_GPIO_DIN, + NPCM7XX_GPIO_POL, + NPCM7XX_GPIO_DOUT, + NPCM7XX_GPIO_OE, + NPCM7XX_GPIO_OTYP, + NPCM7XX_GPIO_MP, + NPCM7XX_GPIO_PU, + NPCM7XX_GPIO_PD, + NPCM7XX_GPIO_DBNC, + NPCM7XX_GPIO_EVTYP, + NPCM7XX_GPIO_EVBE, + NPCM7XX_GPIO_OBL0, + NPCM7XX_GPIO_OBL1, + NPCM7XX_GPIO_OBL2, + NPCM7XX_GPIO_OBL3, + NPCM7XX_GPIO_EVEN, + NPCM7XX_GPIO_EVENS, + NPCM7XX_GPIO_EVENC, + NPCM7XX_GPIO_EVST, + NPCM7XX_GPIO_SPLCK, + NPCM7XX_GPIO_MPLCK, + NPCM7XX_GPIO_IEM, + NPCM7XX_GPIO_OSRC, + NPCM7XX_GPIO_ODSC, + NPCM7XX_GPIO_DOS = 0x68 / sizeof(uint32_t), + NPCM7XX_GPIO_DOC, + NPCM7XX_GPIO_OES, + NPCM7XX_GPIO_OEC, + NPCM7XX_GPIO_TLOCK2 = 0x7c / sizeof(uint32_t), + NPCM7XX_GPIO_REGS_END, +}; + +#define NPCM7XX_GPIO_REGS_SIZE (4 * KiB) + +#define NPCM7XX_GPIO_LOCK_MAGIC1 (0xc0defa73) +#define NPCM7XX_GPIO_LOCK_MAGIC2 (0xc0de1248) + +static void npcm7xx_gpio_update_events(NPCM7xxGPIOState *s, uint32_t din_diff) +{ + uint32_t din_new = s->regs[NPCM7XX_GPIO_DIN]; + + /* Trigger on high level */ + s->regs[NPCM7XX_GPIO_EVST] |= din_new & ~s->regs[NPCM7XX_GPIO_EVTYP]; + /* Trigger on both edges */ + s->regs[NPCM7XX_GPIO_EVST] |= (din_diff & s->regs[NPCM7XX_GPIO_EVTYP] + & s->regs[NPCM7XX_GPIO_EVBE]); + /* Trigger on rising edge */ + s->regs[NPCM7XX_GPIO_EVST] |= (din_diff & din_new + & s->regs[NPCM7XX_GPIO_EVTYP]); + + trace_npcm7xx_gpio_update_events(DEVICE(s)->canonical_path, + s->regs[NPCM7XX_GPIO_EVST], + s->regs[NPCM7XX_GPIO_EVEN]); + qemu_set_irq(s->irq, !!(s->regs[NPCM7XX_GPIO_EVST] + & s->regs[NPCM7XX_GPIO_EVEN])); +} + +static void npcm7xx_gpio_update_pins(NPCM7xxGPIOState *s, uint32_t diff) +{ + uint32_t drive_en; + uint32_t drive_lvl; + uint32_t not_driven; + uint32_t undefined; + uint32_t pin_diff; + uint32_t din_old; + + /* Calculate level of each pin driven by GPIO controller. */ + drive_lvl = s->regs[NPCM7XX_GPIO_DOUT] ^ s->regs[NPCM7XX_GPIO_POL]; + /* If OTYP=1, only drive low (open drain) */ + drive_en = s->regs[NPCM7XX_GPIO_OE] & ~(s->regs[NPCM7XX_GPIO_OTYP] + & drive_lvl); + /* + * If a pin is driven to opposite levels by the GPIO controller and the + * external driver, the result is undefined. + */ + undefined = drive_en & s->ext_driven & (drive_lvl ^ s->ext_level); + if (undefined) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: pins have multiple drivers: 0x%" PRIx32 "\n", + DEVICE(s)->canonical_path, undefined); + } + + not_driven = ~(drive_en | s->ext_driven); + pin_diff = s->pin_level; + + /* Set pins to externally driven level. */ + s->pin_level = s->ext_level & s->ext_driven; + /* Set internally driven pins, ignoring any conflicts. */ + s->pin_level |= drive_lvl & drive_en; + /* Pull up undriven pins with internal pull-up enabled. */ + s->pin_level |= not_driven & s->regs[NPCM7XX_GPIO_PU]; + /* Pins not driven, pulled up or pulled down are undefined */ + undefined |= not_driven & ~(s->regs[NPCM7XX_GPIO_PU] + | s->regs[NPCM7XX_GPIO_PD]); + + /* If any pins changed state, update the outgoing GPIOs. */ + pin_diff ^= s->pin_level; + pin_diff |= undefined & diff; + if (pin_diff) { + int i; + + for (i = 0; i < NPCM7XX_GPIO_NR_PINS; i++) { + uint32_t mask = BIT(i); + if (pin_diff & mask) { + int level = (undefined & mask) ? -1 : !!(s->pin_level & mask); + trace_npcm7xx_gpio_set_output(DEVICE(s)->canonical_path, + i, level); + qemu_set_irq(s->output[i], level); + } + } + } + + /* Calculate new value of DIN after masking and polarity setting. */ + din_old = s->regs[NPCM7XX_GPIO_DIN]; + s->regs[NPCM7XX_GPIO_DIN] = ((s->pin_level & s->regs[NPCM7XX_GPIO_IEM]) + ^ s->regs[NPCM7XX_GPIO_POL]); + + /* See if any new events triggered because of all this. */ + npcm7xx_gpio_update_events(s, din_old ^ s->regs[NPCM7XX_GPIO_DIN]); +} + +static bool npcm7xx_gpio_is_locked(NPCM7xxGPIOState *s) +{ + return s->regs[NPCM7XX_GPIO_TLOCK1] == 1; +} + +static uint64_t npcm7xx_gpio_regs_read(void *opaque, hwaddr addr, + unsigned int size) +{ + hwaddr reg = addr / sizeof(uint32_t); + NPCM7xxGPIOState *s = opaque; + uint64_t value = 0; + + switch (reg) { + case NPCM7XX_GPIO_TLOCK1 ... NPCM7XX_GPIO_EVEN: + case NPCM7XX_GPIO_EVST ... NPCM7XX_GPIO_ODSC: + value = s->regs[reg]; + break; + + case NPCM7XX_GPIO_EVENS ... NPCM7XX_GPIO_EVENC: + case NPCM7XX_GPIO_DOS ... NPCM7XX_GPIO_TLOCK2: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: read from write-only register 0x%" HWADDR_PRIx "\n", + DEVICE(s)->canonical_path, addr); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: read from invalid offset 0x%" HWADDR_PRIx "\n", + DEVICE(s)->canonical_path, addr); + break; + } + + trace_npcm7xx_gpio_read(DEVICE(s)->canonical_path, addr, value); + + return value; +} + +static void npcm7xx_gpio_regs_write(void *opaque, hwaddr addr, uint64_t v, + unsigned int size) +{ + hwaddr reg = addr / sizeof(uint32_t); + NPCM7xxGPIOState *s = opaque; + uint32_t value = v; + uint32_t diff; + + trace_npcm7xx_gpio_write(DEVICE(s)->canonical_path, addr, v); + + if (npcm7xx_gpio_is_locked(s)) { + switch (reg) { + case NPCM7XX_GPIO_TLOCK1: + if (s->regs[NPCM7XX_GPIO_TLOCK2] == NPCM7XX_GPIO_LOCK_MAGIC2 && + value == NPCM7XX_GPIO_LOCK_MAGIC1) { + s->regs[NPCM7XX_GPIO_TLOCK1] = 0; + s->regs[NPCM7XX_GPIO_TLOCK2] = 0; + } + break; + + case NPCM7XX_GPIO_TLOCK2: + s->regs[reg] = value; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write to locked register @ 0x%" HWADDR_PRIx "\n", + DEVICE(s)->canonical_path, addr); + break; + } + + return; + } + + diff = s->regs[reg] ^ value; + + switch (reg) { + case NPCM7XX_GPIO_TLOCK1: + case NPCM7XX_GPIO_TLOCK2: + s->regs[NPCM7XX_GPIO_TLOCK1] = 1; + s->regs[NPCM7XX_GPIO_TLOCK2] = 0; + break; + + case NPCM7XX_GPIO_DIN: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write to read-only register @ 0x%" HWADDR_PRIx "\n", + DEVICE(s)->canonical_path, addr); + break; + + case NPCM7XX_GPIO_POL: + case NPCM7XX_GPIO_DOUT: + case NPCM7XX_GPIO_OE: + case NPCM7XX_GPIO_OTYP: + case NPCM7XX_GPIO_PU: + case NPCM7XX_GPIO_PD: + case NPCM7XX_GPIO_IEM: + s->regs[reg] = value; + npcm7xx_gpio_update_pins(s, diff); + break; + + case NPCM7XX_GPIO_DOS: + s->regs[NPCM7XX_GPIO_DOUT] |= value; + npcm7xx_gpio_update_pins(s, value); + break; + case NPCM7XX_GPIO_DOC: + s->regs[NPCM7XX_GPIO_DOUT] &= ~value; + npcm7xx_gpio_update_pins(s, value); + break; + case NPCM7XX_GPIO_OES: + s->regs[NPCM7XX_GPIO_OE] |= value; + npcm7xx_gpio_update_pins(s, value); + break; + case NPCM7XX_GPIO_OEC: + s->regs[NPCM7XX_GPIO_OE] &= ~value; + npcm7xx_gpio_update_pins(s, value); + break; + + case NPCM7XX_GPIO_EVTYP: + case NPCM7XX_GPIO_EVBE: + case NPCM7XX_GPIO_EVEN: + s->regs[reg] = value; + npcm7xx_gpio_update_events(s, 0); + break; + + case NPCM7XX_GPIO_EVENS: + s->regs[NPCM7XX_GPIO_EVEN] |= value; + npcm7xx_gpio_update_events(s, 0); + break; + case NPCM7XX_GPIO_EVENC: + s->regs[NPCM7XX_GPIO_EVEN] &= ~value; + npcm7xx_gpio_update_events(s, 0); + break; + + case NPCM7XX_GPIO_EVST: + s->regs[reg] &= ~value; + npcm7xx_gpio_update_events(s, 0); + break; + + case NPCM7XX_GPIO_MP: + case NPCM7XX_GPIO_DBNC: + case NPCM7XX_GPIO_OSRC: + case NPCM7XX_GPIO_ODSC: + /* Nothing to do; just store the value. */ + s->regs[reg] = value; + break; + + case NPCM7XX_GPIO_OBL0: + case NPCM7XX_GPIO_OBL1: + case NPCM7XX_GPIO_OBL2: + case NPCM7XX_GPIO_OBL3: + s->regs[reg] = value; + qemu_log_mask(LOG_UNIMP, "%s: Blinking is not implemented\n", + __func__); + break; + + case NPCM7XX_GPIO_SPLCK: + case NPCM7XX_GPIO_MPLCK: + qemu_log_mask(LOG_UNIMP, "%s: Per-pin lock is not implemented\n", + __func__); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write to invalid offset 0x%" HWADDR_PRIx "\n", + DEVICE(s)->canonical_path, addr); + break; + } +} + +static const MemoryRegionOps npcm7xx_gpio_regs_ops = { + .read = npcm7xx_gpio_regs_read, + .write = npcm7xx_gpio_regs_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void npcm7xx_gpio_set_input(void *opaque, int line, int level) +{ + NPCM7xxGPIOState *s = opaque; + + trace_npcm7xx_gpio_set_input(DEVICE(s)->canonical_path, line, level); + + g_assert(line >= 0 && line < NPCM7XX_GPIO_NR_PINS); + + s->ext_driven = deposit32(s->ext_driven, line, 1, level >= 0); + s->ext_level = deposit32(s->ext_level, line, 1, level > 0); + + npcm7xx_gpio_update_pins(s, BIT(line)); +} + +static void npcm7xx_gpio_enter_reset(Object *obj, ResetType type) +{ + NPCM7xxGPIOState *s = NPCM7XX_GPIO(obj); + + memset(s->regs, 0, sizeof(s->regs)); + + s->regs[NPCM7XX_GPIO_PU] = s->reset_pu; + s->regs[NPCM7XX_GPIO_PD] = s->reset_pd; + s->regs[NPCM7XX_GPIO_OSRC] = s->reset_osrc; + s->regs[NPCM7XX_GPIO_ODSC] = s->reset_odsc; +} + +static void npcm7xx_gpio_hold_reset(Object *obj) +{ + NPCM7xxGPIOState *s = NPCM7XX_GPIO(obj); + + npcm7xx_gpio_update_pins(s, -1); +} + +static void npcm7xx_gpio_init(Object *obj) +{ + NPCM7xxGPIOState *s = NPCM7XX_GPIO(obj); + DeviceState *dev = DEVICE(obj); + + memory_region_init_io(&s->mmio, obj, &npcm7xx_gpio_regs_ops, s, + "regs", NPCM7XX_GPIO_REGS_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); + + qdev_init_gpio_in(dev, npcm7xx_gpio_set_input, NPCM7XX_GPIO_NR_PINS); + qdev_init_gpio_out(dev, s->output, NPCM7XX_GPIO_NR_PINS); +} + +static const VMStateDescription vmstate_npcm7xx_gpio = { + .name = "npcm7xx-gpio", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(pin_level, NPCM7xxGPIOState), + VMSTATE_UINT32(ext_level, NPCM7xxGPIOState), + VMSTATE_UINT32(ext_driven, NPCM7xxGPIOState), + VMSTATE_UINT32_ARRAY(regs, NPCM7xxGPIOState, NPCM7XX_GPIO_NR_REGS), + VMSTATE_END_OF_LIST(), + }, +}; + +static Property npcm7xx_gpio_properties[] = { + /* Bit n set => pin n has pullup enabled by default. */ + DEFINE_PROP_UINT32("reset-pullup", NPCM7xxGPIOState, reset_pu, 0), + /* Bit n set => pin n has pulldown enabled by default. */ + DEFINE_PROP_UINT32("reset-pulldown", NPCM7xxGPIOState, reset_pd, 0), + /* Bit n set => pin n has high slew rate by default. */ + DEFINE_PROP_UINT32("reset-osrc", NPCM7xxGPIOState, reset_osrc, 0), + /* Bit n set => pin n has high drive strength by default. */ + DEFINE_PROP_UINT32("reset-odsc", NPCM7xxGPIOState, reset_odsc, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void npcm7xx_gpio_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *reset = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + QEMU_BUILD_BUG_ON(NPCM7XX_GPIO_REGS_END > NPCM7XX_GPIO_NR_REGS); + + dc->desc = "NPCM7xx GPIO Controller"; + dc->vmsd = &vmstate_npcm7xx_gpio; + reset->phases.enter = npcm7xx_gpio_enter_reset; + reset->phases.hold = npcm7xx_gpio_hold_reset; + device_class_set_props(dc, npcm7xx_gpio_properties); +} + +static const TypeInfo npcm7xx_gpio_types[] = { + { + .name = TYPE_NPCM7XX_GPIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NPCM7xxGPIOState), + .class_init = npcm7xx_gpio_class_init, + .instance_init = npcm7xx_gpio_init, + }, +}; +DEFINE_TYPES(npcm7xx_gpio_types); diff --git a/hw/gpio/trace-events b/hw/gpio/trace-events index 6e3f048745..46ab9323bd 100644 --- a/hw/gpio/trace-events +++ b/hw/gpio/trace-events @@ -1,5 +1,12 @@ # See docs/devel/tracing.txt for syntax documentation. +# npcm7xx_gpio.c +npcm7xx_gpio_read(const char *id, uint64_t offset, uint64_t value) " %s offset: 0x%04" PRIx64 " value 0x%08" PRIx64 +npcm7xx_gpio_write(const char *id, uint64_t offset, uint64_t value) "%s offset: 0x%04" PRIx64 " value 0x%08" PRIx64 +npcm7xx_gpio_set_input(const char *id, int32_t line, int32_t level) "%s line: %" PRIi32 " level: %" PRIi32 +npcm7xx_gpio_set_output(const char *id, int32_t line, int32_t level) "%s line: %" PRIi32 " level: %" PRIi32 +npcm7xx_gpio_update_events(const char *id, uint32_t evst, uint32_t even) "%s evst: 0x%08" PRIx32 " even: 0x%08" PRIx32 + # nrf51_gpio.c nrf51_gpio_read(uint64_t offset, uint64_t r) "offset 0x%" PRIx64 " value 0x%" PRIx64 nrf51_gpio_write(uint64_t offset, uint64_t value) "offset 0x%" PRIx64 " value 0x%" PRIx64 diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 4e323755d0..0d9bd7d635 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1265,24 +1265,18 @@ static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, static void pc_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - Error *local_err = NULL; PCMachineState *pcms = PC_MACHINE(hotplug_dev); X86MachineState *x86ms = X86_MACHINE(hotplug_dev); MachineState *ms = MACHINE(hotplug_dev); bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); - pc_dimm_plug(PC_DIMM(dev), MACHINE(pcms), &local_err); - if (local_err) { - goto out; - } + pc_dimm_plug(PC_DIMM(dev), MACHINE(pcms)); if (is_nvdimm) { nvdimm_plug(ms->nvdimms_state); } hotplug_handler_plug(x86ms->acpi_dev, dev, &error_abort); -out: - error_propagate(errp, local_err); } static void pc_memory_unplug_request(HotplugHandler *hotplug_dev, diff --git a/hw/ide/core.c b/hw/ide/core.c index 693b352d5e..e85821637c 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2254,10 +2254,8 @@ static void ide_perform_srst(IDEState *s) /* Cancel PIO callback, reset registers/signature, etc */ ide_reset(s); - if (s->drive_kind == IDE_CD) { - /* ATAPI drives do not set READY or SEEK */ - s->status = 0x00; - } + /* perform diagnostic */ + cmd_exec_dev_diagnostic(s, WIN_DIAGNOSE); } static void ide_bus_perform_srst(void *opaque) @@ -2270,6 +2268,8 @@ static void ide_bus_perform_srst(void *opaque) s = &bus->ifs[i]; ide_perform_srst(s); } + + bus->cmd &= ~IDE_CTRL_RESET; } void ide_ctrl_write(void *opaque, uint32_t addr, uint32_t val) @@ -2282,9 +2282,7 @@ void ide_ctrl_write(void *opaque, uint32_t addr, uint32_t val) /* Device0 and Device1 each have their own control register, * but QEMU models it as just one register in the controller. */ - if ((bus->cmd & IDE_CTRL_RESET) && - !(val & IDE_CTRL_RESET)) { - /* SRST triggers on falling edge */ + if (!(bus->cmd & IDE_CTRL_RESET) && (val & IDE_CTRL_RESET)) { for (i = 0; i < 2; i++) { s = &bus->ifs[i]; s->status |= BUSY_STAT; diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index c30351070b..2ffc986734 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -64,7 +64,7 @@ void pc_dimm_pre_plug(PCDIMMDevice *dimm, MachineState *machine, errp); } -void pc_dimm_plug(PCDIMMDevice *dimm, MachineState *machine, Error **errp) +void pc_dimm_plug(PCDIMMDevice *dimm, MachineState *machine) { PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm, diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index 3185456110..877ecff447 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -93,9 +93,11 @@ config MIPS_ITU config MPS2_FPGAIO bool + select LED config MPS2_SCC bool + select LED config TZ_MPC bool @@ -126,6 +128,9 @@ config AUX config UNIMP bool +config LED + bool + config MAC_VIA bool select MOS6522 diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c new file mode 100644 index 0000000000..7e415a017c --- /dev/null +++ b/hw/misc/bcm2835_cprman.c @@ -0,0 +1,808 @@ +/* + * BCM2835 CPRMAN clock manager + * + * Copyright (c) 2020 Luc Michel <luc@lmichel.fr> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/* + * This peripheral is roughly divided into 3 main parts: + * - the PLLs + * - the PLL channels + * - the clock muxes + * + * A main oscillator (xosc) feeds all the PLLs. Each PLLs has one or more + * channels. Those channel are then connected to the clock muxes. Each mux has + * multiples sources (usually the xosc, some of the PLL channels and some "test + * debug" clocks). A mux is configured to select a given source through its + * control register. Each mux has one output clock that also goes out of the + * CPRMAN. This output clock usually connects to another peripheral in the SoC + * (so a given mux is dedicated to a peripheral). + * + * At each level (PLL, channel and mux), the clock can be altered through + * dividers (and multipliers in case of the PLLs), and can be disabled (in this + * case, the next levels see no clock). + * + * This can be sum-up as follows (this is an example and not the actual BCM2835 + * clock tree): + * + * /-->[PLL]-|->[PLL channel]--... [mux]--> to peripherals + * | |->[PLL channel] muxes takes [mux] + * | \->[PLL channel] inputs from [mux] + * | some channels [mux] + * [xosc]---|-->[PLL]-|->[PLL channel] and other srcs [mux] + * | \->[PLL channel] ...-->[mux] + * | [mux] + * \-->[PLL]--->[PLL channel] [mux] + * + * The page at https://elinux.org/The_Undocumented_Pi gives the actual clock + * tree configuration. + * + * The CPRMAN exposes clock outputs with the name of the clock mux suffixed + * with "-out" (e.g. "uart-out", "h264-out", ...). + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "migration/vmstate.h" +#include "hw/qdev-properties.h" +#include "hw/misc/bcm2835_cprman.h" +#include "hw/misc/bcm2835_cprman_internals.h" +#include "trace.h" + +/* PLL */ + +static void pll_reset(DeviceState *dev) +{ + CprmanPllState *s = CPRMAN_PLL(dev); + const PLLResetInfo *info = &PLL_RESET_INFO[s->id]; + + *s->reg_cm = info->cm; + *s->reg_a2w_ctrl = info->a2w_ctrl; + memcpy(s->reg_a2w_ana, info->a2w_ana, sizeof(info->a2w_ana)); + *s->reg_a2w_frac = info->a2w_frac; +} + +static bool pll_is_locked(const CprmanPllState *pll) +{ + return !FIELD_EX32(*pll->reg_a2w_ctrl, A2W_PLLx_CTRL, PWRDN) + && !FIELD_EX32(*pll->reg_cm, CM_PLLx, ANARST); +} + +static void pll_update(CprmanPllState *pll) +{ + uint64_t freq, ndiv, fdiv, pdiv; + + if (!pll_is_locked(pll)) { + clock_update(pll->out, 0); + return; + } + + pdiv = FIELD_EX32(*pll->reg_a2w_ctrl, A2W_PLLx_CTRL, PDIV); + + if (!pdiv) { + clock_update(pll->out, 0); + return; + } + + ndiv = FIELD_EX32(*pll->reg_a2w_ctrl, A2W_PLLx_CTRL, NDIV); + fdiv = FIELD_EX32(*pll->reg_a2w_frac, A2W_PLLx_FRAC, FRAC); + + if (pll->reg_a2w_ana[1] & pll->prediv_mask) { + /* The prescaler doubles the parent frequency */ + ndiv *= 2; + fdiv *= 2; + } + + /* + * We have a multiplier with an integer part (ndiv) and a fractional part + * (fdiv), and a divider (pdiv). + */ + freq = clock_get_hz(pll->xosc_in) * + ((ndiv << R_A2W_PLLx_FRAC_FRAC_LENGTH) + fdiv); + freq /= pdiv; + freq >>= R_A2W_PLLx_FRAC_FRAC_LENGTH; + + clock_update_hz(pll->out, freq); +} + +static void pll_xosc_update(void *opaque) +{ + pll_update(CPRMAN_PLL(opaque)); +} + +static void pll_init(Object *obj) +{ + CprmanPllState *s = CPRMAN_PLL(obj); + + s->xosc_in = qdev_init_clock_in(DEVICE(s), "xosc-in", pll_xosc_update, s); + s->out = qdev_init_clock_out(DEVICE(s), "out"); +} + +static const VMStateDescription pll_vmstate = { + .name = TYPE_CPRMAN_PLL, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_CLOCK(xosc_in, CprmanPllState), + VMSTATE_END_OF_LIST() + } +}; + +static void pll_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = pll_reset; + dc->vmsd = &pll_vmstate; +} + +static const TypeInfo cprman_pll_info = { + .name = TYPE_CPRMAN_PLL, + .parent = TYPE_DEVICE, + .instance_size = sizeof(CprmanPllState), + .class_init = pll_class_init, + .instance_init = pll_init, +}; + + +/* PLL channel */ + +static void pll_channel_reset(DeviceState *dev) +{ + CprmanPllChannelState *s = CPRMAN_PLL_CHANNEL(dev); + const PLLChannelResetInfo *info = &PLL_CHANNEL_RESET_INFO[s->id]; + + *s->reg_a2w_ctrl = info->a2w_ctrl; +} + +static bool pll_channel_is_enabled(CprmanPllChannelState *channel) +{ + /* + * XXX I'm not sure of the purpose of the LOAD field. The Linux driver does + * not set it when enabling the channel, but does clear it when disabling + * it. + */ + return !FIELD_EX32(*channel->reg_a2w_ctrl, A2W_PLLx_CHANNELy, DISABLE) + && !(*channel->reg_cm & channel->hold_mask); +} + +static void pll_channel_update(CprmanPllChannelState *channel) +{ + uint64_t freq, div; + + if (!pll_channel_is_enabled(channel)) { + clock_update(channel->out, 0); + return; + } + + div = FIELD_EX32(*channel->reg_a2w_ctrl, A2W_PLLx_CHANNELy, DIV); + + if (!div) { + /* + * It seems that when the divider value is 0, it is considered as + * being maximum by the hardware (see the Linux driver). + */ + div = R_A2W_PLLx_CHANNELy_DIV_MASK; + } + + /* Some channels have an additional fixed divider */ + freq = clock_get_hz(channel->pll_in) / (div * channel->fixed_divider); + + clock_update_hz(channel->out, freq); +} + +/* Update a PLL and all its channels */ +static void pll_update_all_channels(BCM2835CprmanState *s, + CprmanPllState *pll) +{ + size_t i; + + pll_update(pll); + + for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) { + CprmanPllChannelState *channel = &s->channels[i]; + if (channel->parent == pll->id) { + pll_channel_update(channel); + } + } +} + +static void pll_channel_pll_in_update(void *opaque) +{ + pll_channel_update(CPRMAN_PLL_CHANNEL(opaque)); +} + +static void pll_channel_init(Object *obj) +{ + CprmanPllChannelState *s = CPRMAN_PLL_CHANNEL(obj); + + s->pll_in = qdev_init_clock_in(DEVICE(s), "pll-in", + pll_channel_pll_in_update, s); + s->out = qdev_init_clock_out(DEVICE(s), "out"); +} + +static const VMStateDescription pll_channel_vmstate = { + .name = TYPE_CPRMAN_PLL_CHANNEL, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_CLOCK(pll_in, CprmanPllChannelState), + VMSTATE_END_OF_LIST() + } +}; + +static void pll_channel_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = pll_channel_reset; + dc->vmsd = &pll_channel_vmstate; +} + +static const TypeInfo cprman_pll_channel_info = { + .name = TYPE_CPRMAN_PLL_CHANNEL, + .parent = TYPE_DEVICE, + .instance_size = sizeof(CprmanPllChannelState), + .class_init = pll_channel_class_init, + .instance_init = pll_channel_init, +}; + + +/* clock mux */ + +static bool clock_mux_is_enabled(CprmanClockMuxState *mux) +{ + return FIELD_EX32(*mux->reg_ctl, CM_CLOCKx_CTL, ENABLE); +} + +static void clock_mux_update(CprmanClockMuxState *mux) +{ + uint64_t freq; + uint32_t div, src = FIELD_EX32(*mux->reg_ctl, CM_CLOCKx_CTL, SRC); + bool enabled = clock_mux_is_enabled(mux); + + *mux->reg_ctl = FIELD_DP32(*mux->reg_ctl, CM_CLOCKx_CTL, BUSY, enabled); + + if (!enabled) { + clock_update(mux->out, 0); + return; + } + + freq = clock_get_hz(mux->srcs[src]); + + if (mux->int_bits == 0 && mux->frac_bits == 0) { + clock_update_hz(mux->out, freq); + return; + } + + /* + * The divider has an integer and a fractional part. The size of each part + * varies with the muxes (int_bits and frac_bits). Both parts are + * concatenated, with the integer part always starting at bit 12. + * + * 31 12 11 0 + * ------------------------------ + * CM_DIV | | int | frac | | + * ------------------------------ + * <-----> <------> + * int_bits frac_bits + */ + div = extract32(*mux->reg_div, + R_CM_CLOCKx_DIV_FRAC_LENGTH - mux->frac_bits, + mux->int_bits + mux->frac_bits); + + if (!div) { + clock_update(mux->out, 0); + return; + } + + freq = muldiv64(freq, 1 << mux->frac_bits, div); + + clock_update_hz(mux->out, freq); +} + +static void clock_mux_src_update(void *opaque) +{ + CprmanClockMuxState **backref = opaque; + CprmanClockMuxState *s = *backref; + CprmanClockMuxSource src = backref - s->backref; + + if (FIELD_EX32(*s->reg_ctl, CM_CLOCKx_CTL, SRC) != src) { + return; + } + + clock_mux_update(s); +} + +static void clock_mux_reset(DeviceState *dev) +{ + CprmanClockMuxState *clock = CPRMAN_CLOCK_MUX(dev); + const ClockMuxResetInfo *info = &CLOCK_MUX_RESET_INFO[clock->id]; + + *clock->reg_ctl = info->cm_ctl; + *clock->reg_div = info->cm_div; +} + +static void clock_mux_init(Object *obj) +{ + CprmanClockMuxState *s = CPRMAN_CLOCK_MUX(obj); + size_t i; + + for (i = 0; i < CPRMAN_NUM_CLOCK_MUX_SRC; i++) { + char *name = g_strdup_printf("srcs[%zu]", i); + s->backref[i] = s; + s->srcs[i] = qdev_init_clock_in(DEVICE(s), name, + clock_mux_src_update, + &s->backref[i]); + g_free(name); + } + + s->out = qdev_init_clock_out(DEVICE(s), "out"); +} + +static const VMStateDescription clock_mux_vmstate = { + .name = TYPE_CPRMAN_CLOCK_MUX, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_ARRAY_CLOCK(srcs, CprmanClockMuxState, + CPRMAN_NUM_CLOCK_MUX_SRC), + VMSTATE_END_OF_LIST() + } +}; + +static void clock_mux_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = clock_mux_reset; + dc->vmsd = &clock_mux_vmstate; +} + +static const TypeInfo cprman_clock_mux_info = { + .name = TYPE_CPRMAN_CLOCK_MUX, + .parent = TYPE_DEVICE, + .instance_size = sizeof(CprmanClockMuxState), + .class_init = clock_mux_class_init, + .instance_init = clock_mux_init, +}; + + +/* DSI0HSCK mux */ + +static void dsi0hsck_mux_update(CprmanDsi0HsckMuxState *s) +{ + bool src_is_plld = FIELD_EX32(*s->reg_cm, CM_DSI0HSCK, SELPLLD); + Clock *src = src_is_plld ? s->plld_in : s->plla_in; + + clock_update(s->out, clock_get(src)); +} + +static void dsi0hsck_mux_in_update(void *opaque) +{ + dsi0hsck_mux_update(CPRMAN_DSI0HSCK_MUX(opaque)); +} + +static void dsi0hsck_mux_init(Object *obj) +{ + CprmanDsi0HsckMuxState *s = CPRMAN_DSI0HSCK_MUX(obj); + DeviceState *dev = DEVICE(obj); + + s->plla_in = qdev_init_clock_in(dev, "plla-in", dsi0hsck_mux_in_update, s); + s->plld_in = qdev_init_clock_in(dev, "plld-in", dsi0hsck_mux_in_update, s); + s->out = qdev_init_clock_out(DEVICE(s), "out"); +} + +static const VMStateDescription dsi0hsck_mux_vmstate = { + .name = TYPE_CPRMAN_DSI0HSCK_MUX, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_CLOCK(plla_in, CprmanDsi0HsckMuxState), + VMSTATE_CLOCK(plld_in, CprmanDsi0HsckMuxState), + VMSTATE_END_OF_LIST() + } +}; + +static void dsi0hsck_mux_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &dsi0hsck_mux_vmstate; +} + +static const TypeInfo cprman_dsi0hsck_mux_info = { + .name = TYPE_CPRMAN_DSI0HSCK_MUX, + .parent = TYPE_DEVICE, + .instance_size = sizeof(CprmanDsi0HsckMuxState), + .class_init = dsi0hsck_mux_class_init, + .instance_init = dsi0hsck_mux_init, +}; + + +/* CPRMAN "top level" model */ + +static uint32_t get_cm_lock(const BCM2835CprmanState *s) +{ + static const int CM_LOCK_MAPPING[CPRMAN_NUM_PLL] = { + [CPRMAN_PLLA] = R_CM_LOCK_FLOCKA_SHIFT, + [CPRMAN_PLLC] = R_CM_LOCK_FLOCKC_SHIFT, + [CPRMAN_PLLD] = R_CM_LOCK_FLOCKD_SHIFT, + [CPRMAN_PLLH] = R_CM_LOCK_FLOCKH_SHIFT, + [CPRMAN_PLLB] = R_CM_LOCK_FLOCKB_SHIFT, + }; + + uint32_t r = 0; + size_t i; + + for (i = 0; i < CPRMAN_NUM_PLL; i++) { + r |= pll_is_locked(&s->plls[i]) << CM_LOCK_MAPPING[i]; + } + + return r; +} + +static uint64_t cprman_read(void *opaque, hwaddr offset, + unsigned size) +{ + BCM2835CprmanState *s = CPRMAN(opaque); + uint64_t r = 0; + size_t idx = offset / sizeof(uint32_t); + + switch (idx) { + case R_CM_LOCK: + r = get_cm_lock(s); + break; + + default: + r = s->regs[idx]; + } + + trace_bcm2835_cprman_read(offset, r); + return r; +} + +static inline void update_pll_and_channels_from_cm(BCM2835CprmanState *s, + size_t idx) +{ + size_t i; + + for (i = 0; i < CPRMAN_NUM_PLL; i++) { + if (PLL_INIT_INFO[i].cm_offset == idx) { + pll_update_all_channels(s, &s->plls[i]); + return; + } + } +} + +static inline void update_channel_from_a2w(BCM2835CprmanState *s, size_t idx) +{ + size_t i; + + for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) { + if (PLL_CHANNEL_INIT_INFO[i].a2w_ctrl_offset == idx) { + pll_channel_update(&s->channels[i]); + return; + } + } +} + +static inline void update_mux_from_cm(BCM2835CprmanState *s, size_t idx) +{ + size_t i; + + for (i = 0; i < CPRMAN_NUM_CLOCK_MUX; i++) { + if ((CLOCK_MUX_INIT_INFO[i].cm_offset == idx) || + (CLOCK_MUX_INIT_INFO[i].cm_offset + 4 == idx)) { + /* matches CM_CTL or CM_DIV mux register */ + clock_mux_update(&s->clock_muxes[i]); + return; + } + } +} + +#define CASE_PLL_A2W_REGS(pll_) \ + case R_A2W_ ## pll_ ## _CTRL: \ + case R_A2W_ ## pll_ ## _ANA0: \ + case R_A2W_ ## pll_ ## _ANA1: \ + case R_A2W_ ## pll_ ## _ANA2: \ + case R_A2W_ ## pll_ ## _ANA3: \ + case R_A2W_ ## pll_ ## _FRAC + +static void cprman_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + BCM2835CprmanState *s = CPRMAN(opaque); + size_t idx = offset / sizeof(uint32_t); + + if (FIELD_EX32(value, CPRMAN, PASSWORD) != CPRMAN_PASSWORD) { + trace_bcm2835_cprman_write_invalid_magic(offset, value); + return; + } + + value &= ~R_CPRMAN_PASSWORD_MASK; + + trace_bcm2835_cprman_write(offset, value); + s->regs[idx] = value; + + switch (idx) { + case R_CM_PLLA ... R_CM_PLLH: + case R_CM_PLLB: + /* + * A given CM_PLLx register is shared by both the PLL and the channels + * of this PLL. + */ + update_pll_and_channels_from_cm(s, idx); + break; + + CASE_PLL_A2W_REGS(PLLA) : + pll_update(&s->plls[CPRMAN_PLLA]); + break; + + CASE_PLL_A2W_REGS(PLLC) : + pll_update(&s->plls[CPRMAN_PLLC]); + break; + + CASE_PLL_A2W_REGS(PLLD) : + pll_update(&s->plls[CPRMAN_PLLD]); + break; + + CASE_PLL_A2W_REGS(PLLH) : + pll_update(&s->plls[CPRMAN_PLLH]); + break; + + CASE_PLL_A2W_REGS(PLLB) : + pll_update(&s->plls[CPRMAN_PLLB]); + break; + + case R_A2W_PLLA_DSI0: + case R_A2W_PLLA_CORE: + case R_A2W_PLLA_PER: + case R_A2W_PLLA_CCP2: + case R_A2W_PLLC_CORE2: + case R_A2W_PLLC_CORE1: + case R_A2W_PLLC_PER: + case R_A2W_PLLC_CORE0: + case R_A2W_PLLD_DSI0: + case R_A2W_PLLD_CORE: + case R_A2W_PLLD_PER: + case R_A2W_PLLD_DSI1: + case R_A2W_PLLH_AUX: + case R_A2W_PLLH_RCAL: + case R_A2W_PLLH_PIX: + case R_A2W_PLLB_ARM: + update_channel_from_a2w(s, idx); + break; + + case R_CM_GNRICCTL ... R_CM_SMIDIV: + case R_CM_TCNTCNT ... R_CM_VECDIV: + case R_CM_PULSECTL ... R_CM_PULSEDIV: + case R_CM_SDCCTL ... R_CM_ARMCTL: + case R_CM_AVEOCTL ... R_CM_EMMCDIV: + case R_CM_EMMC2CTL ... R_CM_EMMC2DIV: + update_mux_from_cm(s, idx); + break; + + case R_CM_DSI0HSCK: + dsi0hsck_mux_update(&s->dsi0hsck_mux); + break; + } +} + +#undef CASE_PLL_A2W_REGS + +static const MemoryRegionOps cprman_ops = { + .read = cprman_read, + .write = cprman_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + /* + * Although this hasn't been checked against real hardware, nor the + * information can be found in a datasheet, it seems reasonable because + * of the "PASSWORD" magic value found in every registers. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + .impl = { + .max_access_size = 4, + }, +}; + +static void cprman_reset(DeviceState *dev) +{ + BCM2835CprmanState *s = CPRMAN(dev); + size_t i; + + memset(s->regs, 0, sizeof(s->regs)); + + for (i = 0; i < CPRMAN_NUM_PLL; i++) { + device_cold_reset(DEVICE(&s->plls[i])); + } + + for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) { + device_cold_reset(DEVICE(&s->channels[i])); + } + + device_cold_reset(DEVICE(&s->dsi0hsck_mux)); + + for (i = 0; i < CPRMAN_NUM_CLOCK_MUX; i++) { + device_cold_reset(DEVICE(&s->clock_muxes[i])); + } + + clock_update_hz(s->xosc, s->xosc_freq); +} + +static void cprman_init(Object *obj) +{ + BCM2835CprmanState *s = CPRMAN(obj); + size_t i; + + for (i = 0; i < CPRMAN_NUM_PLL; i++) { + object_initialize_child(obj, PLL_INIT_INFO[i].name, + &s->plls[i], TYPE_CPRMAN_PLL); + set_pll_init_info(s, &s->plls[i], i); + } + + for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) { + object_initialize_child(obj, PLL_CHANNEL_INIT_INFO[i].name, + &s->channels[i], + TYPE_CPRMAN_PLL_CHANNEL); + set_pll_channel_init_info(s, &s->channels[i], i); + } + + object_initialize_child(obj, "dsi0hsck-mux", + &s->dsi0hsck_mux, TYPE_CPRMAN_DSI0HSCK_MUX); + s->dsi0hsck_mux.reg_cm = &s->regs[R_CM_DSI0HSCK]; + + for (i = 0; i < CPRMAN_NUM_CLOCK_MUX; i++) { + char *alias; + + object_initialize_child(obj, CLOCK_MUX_INIT_INFO[i].name, + &s->clock_muxes[i], + TYPE_CPRMAN_CLOCK_MUX); + set_clock_mux_init_info(s, &s->clock_muxes[i], i); + + /* Expose muxes output as CPRMAN outputs */ + alias = g_strdup_printf("%s-out", CLOCK_MUX_INIT_INFO[i].name); + qdev_alias_clock(DEVICE(&s->clock_muxes[i]), "out", DEVICE(obj), alias); + g_free(alias); + } + + s->xosc = clock_new(obj, "xosc"); + s->gnd = clock_new(obj, "gnd"); + + clock_set(s->gnd, 0); + + memory_region_init_io(&s->iomem, obj, &cprman_ops, + s, "bcm2835-cprman", 0x2000); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); +} + +static void connect_mux_sources(BCM2835CprmanState *s, + CprmanClockMuxState *mux, + const CprmanPllChannel *clk_mapping) +{ + size_t i; + Clock *td0 = s->clock_muxes[CPRMAN_CLOCK_TD0].out; + Clock *td1 = s->clock_muxes[CPRMAN_CLOCK_TD1].out; + + /* For sources from 0 to 3. Source 4 to 9 are mux specific */ + Clock * const CLK_SRC_MAPPING[] = { + [CPRMAN_CLOCK_SRC_GND] = s->gnd, + [CPRMAN_CLOCK_SRC_XOSC] = s->xosc, + [CPRMAN_CLOCK_SRC_TD0] = td0, + [CPRMAN_CLOCK_SRC_TD1] = td1, + }; + + for (i = 0; i < CPRMAN_NUM_CLOCK_MUX_SRC; i++) { + CprmanPllChannel mapping = clk_mapping[i]; + Clock *src; + + if (mapping == CPRMAN_CLOCK_SRC_FORCE_GROUND) { + src = s->gnd; + } else if (mapping == CPRMAN_CLOCK_SRC_DSI0HSCK) { + src = s->dsi0hsck_mux.out; + } else if (i < CPRMAN_CLOCK_SRC_PLLA) { + src = CLK_SRC_MAPPING[i]; + } else { + src = s->channels[mapping].out; + } + + clock_set_source(mux->srcs[i], src); + } +} + +static void cprman_realize(DeviceState *dev, Error **errp) +{ + BCM2835CprmanState *s = CPRMAN(dev); + size_t i; + + for (i = 0; i < CPRMAN_NUM_PLL; i++) { + CprmanPllState *pll = &s->plls[i]; + + clock_set_source(pll->xosc_in, s->xosc); + + if (!qdev_realize(DEVICE(pll), NULL, errp)) { + return; + } + } + + for (i = 0; i < CPRMAN_NUM_PLL_CHANNEL; i++) { + CprmanPllChannelState *channel = &s->channels[i]; + CprmanPll parent = PLL_CHANNEL_INIT_INFO[i].parent; + Clock *parent_clk = s->plls[parent].out; + + clock_set_source(channel->pll_in, parent_clk); + + if (!qdev_realize(DEVICE(channel), NULL, errp)) { + return; + } + } + + clock_set_source(s->dsi0hsck_mux.plla_in, + s->channels[CPRMAN_PLLA_CHANNEL_DSI0].out); + clock_set_source(s->dsi0hsck_mux.plld_in, + s->channels[CPRMAN_PLLD_CHANNEL_DSI0].out); + + if (!qdev_realize(DEVICE(&s->dsi0hsck_mux), NULL, errp)) { + return; + } + + for (i = 0; i < CPRMAN_NUM_CLOCK_MUX; i++) { + CprmanClockMuxState *clock_mux = &s->clock_muxes[i]; + + connect_mux_sources(s, clock_mux, CLOCK_MUX_INIT_INFO[i].src_mapping); + + if (!qdev_realize(DEVICE(clock_mux), NULL, errp)) { + return; + } + } +} + +static const VMStateDescription cprman_vmstate = { + .name = TYPE_BCM2835_CPRMAN, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, BCM2835CprmanState, CPRMAN_NUM_REGS), + VMSTATE_END_OF_LIST() + } +}; + +static Property cprman_properties[] = { + DEFINE_PROP_UINT32("xosc-freq-hz", BCM2835CprmanState, xosc_freq, 19200000), + DEFINE_PROP_END_OF_LIST() +}; + +static void cprman_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = cprman_realize; + dc->reset = cprman_reset; + dc->vmsd = &cprman_vmstate; + device_class_set_props(dc, cprman_properties); +} + +static const TypeInfo cprman_info = { + .name = TYPE_BCM2835_CPRMAN, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835CprmanState), + .class_init = cprman_class_init, + .instance_init = cprman_init, +}; + +static void cprman_register_types(void) +{ + type_register_static(&cprman_info); + type_register_static(&cprman_pll_info); + type_register_static(&cprman_pll_channel_info); + type_register_static(&cprman_clock_mux_info); + type_register_static(&cprman_dsi0hsck_mux_info); +} + +type_init(cprman_register_types); diff --git a/hw/misc/led.c b/hw/misc/led.c new file mode 100644 index 0000000000..5266d026d0 --- /dev/null +++ b/hw/misc/led.c @@ -0,0 +1,161 @@ +/* + * QEMU single LED device + * + * Copyright (C) 2020 Philippe Mathieu-Daudé <f4bug@amsat.org> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "migration/vmstate.h" +#include "hw/qdev-properties.h" +#include "hw/misc/led.h" +#include "hw/irq.h" +#include "trace.h" + +#define LED_INTENSITY_PERCENT_MAX 100 + +static const char * const led_color_name[] = { + [LED_COLOR_VIOLET] = "violet", + [LED_COLOR_BLUE] = "blue", + [LED_COLOR_CYAN] = "cyan", + [LED_COLOR_GREEN] = "green", + [LED_COLOR_AMBER] = "amber", + [LED_COLOR_ORANGE] = "orange", + [LED_COLOR_RED] = "red", +}; + +static bool led_color_name_is_valid(const char *color_name) +{ + for (size_t i = 0; i < ARRAY_SIZE(led_color_name); i++) { + if (strcmp(color_name, led_color_name[i]) == 0) { + return true; + } + } + return false; +} + +void led_set_intensity(LEDState *s, unsigned intensity_percent) +{ + if (intensity_percent > LED_INTENSITY_PERCENT_MAX) { + intensity_percent = LED_INTENSITY_PERCENT_MAX; + } + trace_led_set_intensity(s->description, s->color, intensity_percent); + if (intensity_percent != s->intensity_percent) { + trace_led_change_intensity(s->description, s->color, + s->intensity_percent, intensity_percent); + } + s->intensity_percent = intensity_percent; +} + +unsigned led_get_intensity(LEDState *s) +{ + return s->intensity_percent; +} + +void led_set_state(LEDState *s, bool is_emitting) +{ + led_set_intensity(s, is_emitting ? LED_INTENSITY_PERCENT_MAX : 0); +} + +static void led_set_state_gpio_handler(void *opaque, int line, int new_state) +{ + LEDState *s = LED(opaque); + + assert(line == 0); + led_set_state(s, !!new_state != s->gpio_active_high); +} + +static void led_reset(DeviceState *dev) +{ + LEDState *s = LED(dev); + + led_set_state(s, s->gpio_active_high); +} + +static const VMStateDescription vmstate_led = { + .name = TYPE_LED, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(intensity_percent, LEDState), + VMSTATE_END_OF_LIST() + } +}; + +static void led_realize(DeviceState *dev, Error **errp) +{ + LEDState *s = LED(dev); + + if (s->color == NULL) { + error_setg(errp, "property 'color' not specified"); + return; + } else if (!led_color_name_is_valid(s->color)) { + error_setg(errp, "property 'color' invalid or not supported"); + return; + } + if (s->description == NULL) { + s->description = g_strdup("n/a"); + } + + qdev_init_gpio_in(DEVICE(s), led_set_state_gpio_handler, 1); +} + +static Property led_properties[] = { + DEFINE_PROP_STRING("color", LEDState, color), + DEFINE_PROP_STRING("description", LEDState, description), + DEFINE_PROP_BOOL("gpio-active-high", LEDState, gpio_active_high, true), + DEFINE_PROP_END_OF_LIST(), +}; + +static void led_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "LED"; + dc->vmsd = &vmstate_led; + dc->reset = led_reset; + dc->realize = led_realize; + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); + device_class_set_props(dc, led_properties); +} + +static const TypeInfo led_info = { + .name = TYPE_LED, + .parent = TYPE_DEVICE, + .instance_size = sizeof(LEDState), + .class_init = led_class_init +}; + +static void led_register_types(void) +{ + type_register_static(&led_info); +} + +type_init(led_register_types) + +LEDState *led_create_simple(Object *parentobj, + GpioPolarity gpio_polarity, + LEDColor color, + const char *description) +{ + g_autofree char *name = NULL; + DeviceState *dev; + + dev = qdev_new(TYPE_LED); + qdev_prop_set_bit(dev, "gpio-active-high", + gpio_polarity == GPIO_POLARITY_ACTIVE_HIGH); + qdev_prop_set_string(dev, "color", led_color_name[color]); + if (!description) { + static unsigned undescribed_led_id; + name = g_strdup_printf("undescribed-led-#%u", undescribed_led_id++); + } else { + qdev_prop_set_string(dev, "description", description); + name = g_ascii_strdown(description, -1); + name = g_strdelimit(name, " #", '-'); + } + object_property_add_child(parentobj, name, OBJECT(dev)); + qdev_realize_and_unref(dev, NULL, &error_fatal); + + return LED(dev); +} diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 793d45b1dc..4a06cbabef 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -11,6 +11,7 @@ softmmu_ss.add(when: 'CONFIG_TMP105', if_true: files('tmp105.c')) softmmu_ss.add(when: 'CONFIG_TMP421', if_true: files('tmp421.c')) softmmu_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c')) softmmu_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c')) +softmmu_ss.add(when: 'CONFIG_LED', if_true: files('led.c')) # ARM devices softmmu_ss.add(when: 'CONFIG_PL310', if_true: files('arm_l2x0.c')) @@ -59,6 +60,7 @@ softmmu_ss.add(when: 'CONFIG_MAINSTONE', if_true: files('mst_fpga.c')) softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files( 'npcm7xx_clk.c', 'npcm7xx_gcr.c', + 'npcm7xx_rng.c', )) softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files( 'omap_clk.c', @@ -73,6 +75,7 @@ softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files( 'bcm2835_property.c', 'bcm2835_rng.c', 'bcm2835_thermal.c', + 'bcm2835_cprman.c', )) softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_misc.c')) softmmu_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq_slcr.c', 'zynq-xadc.c')) diff --git a/hw/misc/mps2-fpgaio.c b/hw/misc/mps2-fpgaio.c index 2f3fbeef34..6af0e8f837 100644 --- a/hw/misc/mps2-fpgaio.c +++ b/hw/misc/mps2-fpgaio.c @@ -24,6 +24,7 @@ #include "migration/vmstate.h" #include "hw/registerfields.h" #include "hw/misc/mps2-fpgaio.h" +#include "hw/misc/led.h" #include "hw/qdev-properties.h" #include "qemu/timer.h" @@ -176,12 +177,9 @@ static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value, switch (offset) { case A_LED0: - /* LED bits [1:0] control board LEDs. We don't currently have - * a mechanism for displaying this graphically, so use a trace event. - */ - trace_mps2_fpgaio_leds(value & 0x02 ? '*' : '.', - value & 0x01 ? '*' : '.'); s->led0 = value & 0x3; + led_set_state(s->led[0], value & 0x01); + led_set_state(s->led[1], value & 0x02); break; case A_PRESCALE: resync_counter(s); @@ -239,6 +237,10 @@ static void mps2_fpgaio_reset(DeviceState *dev) s->counter = 0; s->pscntr = 0; s->pscntr_sync_ticks = now; + + for (size_t i = 0; i < ARRAY_SIZE(s->led); i++) { + device_cold_reset(DEVICE(s->led[i])); + } } static void mps2_fpgaio_init(Object *obj) @@ -251,6 +253,16 @@ static void mps2_fpgaio_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } +static void mps2_fpgaio_realize(DeviceState *dev, Error **errp) +{ + MPS2FPGAIO *s = MPS2_FPGAIO(dev); + + s->led[0] = led_create_simple(OBJECT(dev), GPIO_POLARITY_ACTIVE_HIGH, + LED_COLOR_GREEN, "USERLED0"); + s->led[1] = led_create_simple(OBJECT(dev), GPIO_POLARITY_ACTIVE_HIGH, + LED_COLOR_GREEN, "USERLED1"); +} + static bool mps2_fpgaio_counters_needed(void *opaque) { /* Currently vmstate.c insists all subsections have a 'needed' function */ @@ -299,6 +311,7 @@ static void mps2_fpgaio_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &mps2_fpgaio_vmstate; + dc->realize = mps2_fpgaio_realize; dc->reset = mps2_fpgaio_reset; device_class_set_props(dc, mps2_fpgaio_properties); } diff --git a/hw/misc/mps2-scc.c b/hw/misc/mps2-scc.c index 9d0909e7b3..ce1dfe9356 100644 --- a/hw/misc/mps2-scc.c +++ b/hw/misc/mps2-scc.c @@ -20,11 +20,13 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/module.h" +#include "qemu/bitops.h" #include "trace.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #include "hw/registerfields.h" #include "hw/misc/mps2-scc.h" +#include "hw/misc/led.h" #include "hw/qdev-properties.h" REG32(CFG0, 0) @@ -152,18 +154,10 @@ static void mps2_scc_write(void *opaque, hwaddr offset, uint64_t value, s->cfg0 = value; break; case A_CFG1: - /* CFG1 bits [7:0] control the board LEDs. We don't currently have - * a mechanism for displaying this graphically, so use a trace event. - */ - trace_mps2_scc_leds(value & 0x80 ? '*' : '.', - value & 0x40 ? '*' : '.', - value & 0x20 ? '*' : '.', - value & 0x10 ? '*' : '.', - value & 0x08 ? '*' : '.', - value & 0x04 ? '*' : '.', - value & 0x02 ? '*' : '.', - value & 0x01 ? '*' : '.'); s->cfg1 = value; + for (size_t i = 0; i < ARRAY_SIZE(s->led); i++) { + led_set_state(s->led[i], extract32(value, i, 1)); + } break; case A_CFGDATA_OUT: s->cfgdata_out = value; @@ -236,6 +230,9 @@ static void mps2_scc_reset(DeviceState *dev) for (i = 0; i < NUM_OSCCLK; i++) { s->oscclk[i] = s->oscclk_reset[i]; } + for (i = 0; i < ARRAY_SIZE(s->led); i++) { + device_cold_reset(DEVICE(s->led[i])); + } } static void mps2_scc_init(Object *obj) @@ -249,6 +246,14 @@ static void mps2_scc_init(Object *obj) static void mps2_scc_realize(DeviceState *dev, Error **errp) { + MPS2SCC *s = MPS2_SCC(dev); + + for (size_t i = 0; i < ARRAY_SIZE(s->led); i++) { + char *name = g_strdup_printf("SCC LED%zu", i); + s->led[i] = led_create_simple(OBJECT(dev), GPIO_POLARITY_ACTIVE_HIGH, + LED_COLOR_GREEN, name); + g_free(name); + } } static const VMStateDescription mps2_scc_vmstate = { diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c index 21ab4200d1..6732437fe2 100644 --- a/hw/misc/npcm7xx_clk.c +++ b/hw/misc/npcm7xx_clk.c @@ -17,6 +17,7 @@ #include "qemu/osdep.h" #include "hw/misc/npcm7xx_clk.h" +#include "hw/timer/npcm7xx_timer.h" #include "migration/vmstate.h" #include "qemu/error-report.h" #include "qemu/log.h" @@ -24,6 +25,7 @@ #include "qemu/timer.h" #include "qemu/units.h" #include "trace.h" +#include "sysemu/watchdog.h" #define PLLCON_LOKI BIT(31) #define PLLCON_LOKS BIT(30) @@ -87,6 +89,12 @@ static const uint32_t cold_reset_values[NPCM7XX_CLK_NR_REGS] = { [NPCM7XX_CLK_AHBCKFI] = 0x000000c8, }; +/* Register Field Definitions */ +#define NPCM7XX_CLK_WDRCR_CA9C BIT(0) /* Cortex A9 Cores */ + +/* The number of watchdogs that can trigger a reset. */ +#define NPCM7XX_NR_WATCHDOGS (3) + static uint64_t npcm7xx_clk_read(void *opaque, hwaddr offset, unsigned size) { uint32_t reg = offset / sizeof(uint32_t); @@ -187,6 +195,24 @@ static void npcm7xx_clk_write(void *opaque, hwaddr offset, s->regs[reg] = value; } +/* Perform reset action triggered by a watchdog */ +static void npcm7xx_clk_perform_watchdog_reset(void *opaque, int n, + int level) +{ + NPCM7xxCLKState *clk = NPCM7XX_CLK(opaque); + uint32_t rcr; + + g_assert(n >= 0 && n <= NPCM7XX_NR_WATCHDOGS); + rcr = clk->regs[NPCM7XX_CLK_WD0RCR + n]; + if (rcr & NPCM7XX_CLK_WDRCR_CA9C) { + watchdog_perform_action(); + } else { + qemu_log_mask(LOG_UNIMP, + "%s: only CPU reset is implemented. (requested 0x%" PRIx32")\n", + __func__, rcr); + } +} + static const struct MemoryRegionOps npcm7xx_clk_ops = { .read = npcm7xx_clk_read, .write = npcm7xx_clk_write, @@ -226,6 +252,8 @@ static void npcm7xx_clk_init(Object *obj) memory_region_init_io(&s->iomem, obj, &npcm7xx_clk_ops, s, TYPE_NPCM7XX_CLK, 4 * KiB); sysbus_init_mmio(&s->parent, &s->iomem); + qdev_init_gpio_in_named(DEVICE(s), npcm7xx_clk_perform_watchdog_reset, + NPCM7XX_WATCHDOG_RESET_GPIO_IN, NPCM7XX_NR_WATCHDOGS); } static const VMStateDescription vmstate_npcm7xx_clk = { diff --git a/hw/misc/npcm7xx_rng.c b/hw/misc/npcm7xx_rng.c new file mode 100644 index 0000000000..f650f3401f --- /dev/null +++ b/hw/misc/npcm7xx_rng.c @@ -0,0 +1,180 @@ +/* + * Nuvoton NPCM7xx Random Number Generator. + * + * Copyright 2020 Google LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "qemu/osdep.h" + +#include "hw/misc/npcm7xx_rng.h" +#include "migration/vmstate.h" +#include "qemu/bitops.h" +#include "qemu/guest-random.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/units.h" + +#include "trace.h" + +#define NPCM7XX_RNG_REGS_SIZE (4 * KiB) + +#define NPCM7XX_RNGCS (0x00) +#define NPCM7XX_RNGCS_CLKP(rv) extract32(rv, 2, 4) +#define NPCM7XX_RNGCS_DVALID BIT(1) +#define NPCM7XX_RNGCS_RNGE BIT(0) + +#define NPCM7XX_RNGD (0x04) +#define NPCM7XX_RNGMODE (0x08) +#define NPCM7XX_RNGMODE_NORMAL (0x02) + +static bool npcm7xx_rng_is_enabled(NPCM7xxRNGState *s) +{ + return (s->rngcs & NPCM7XX_RNGCS_RNGE) && + (s->rngmode == NPCM7XX_RNGMODE_NORMAL); +} + +static uint64_t npcm7xx_rng_read(void *opaque, hwaddr offset, unsigned size) +{ + NPCM7xxRNGState *s = opaque; + uint64_t value = 0; + + switch (offset) { + case NPCM7XX_RNGCS: + /* + * If the RNG is enabled, but we don't have any valid random data, try + * obtaining some and update the DVALID bit accordingly. + */ + if (!npcm7xx_rng_is_enabled(s)) { + s->rngcs &= ~NPCM7XX_RNGCS_DVALID; + } else if (!(s->rngcs & NPCM7XX_RNGCS_DVALID)) { + uint8_t byte = 0; + + if (qemu_guest_getrandom(&byte, sizeof(byte), NULL) == 0) { + s->rngd = byte; + s->rngcs |= NPCM7XX_RNGCS_DVALID; + } + } + value = s->rngcs; + break; + case NPCM7XX_RNGD: + if (npcm7xx_rng_is_enabled(s) && s->rngcs & NPCM7XX_RNGCS_DVALID) { + s->rngcs &= ~NPCM7XX_RNGCS_DVALID; + value = s->rngd; + s->rngd = 0; + } + break; + case NPCM7XX_RNGMODE: + value = s->rngmode; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: read from invalid offset 0x%" HWADDR_PRIx "\n", + DEVICE(s)->canonical_path, offset); + break; + } + + trace_npcm7xx_rng_read(offset, value, size); + + return value; +} + +static void npcm7xx_rng_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + NPCM7xxRNGState *s = opaque; + + trace_npcm7xx_rng_write(offset, value, size); + + switch (offset) { + case NPCM7XX_RNGCS: + s->rngcs &= NPCM7XX_RNGCS_DVALID; + s->rngcs |= value & ~NPCM7XX_RNGCS_DVALID; + break; + case NPCM7XX_RNGD: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write to read-only register @ 0x%" HWADDR_PRIx "\n", + DEVICE(s)->canonical_path, offset); + break; + case NPCM7XX_RNGMODE: + s->rngmode = value; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write to invalid offset 0x%" HWADDR_PRIx "\n", + DEVICE(s)->canonical_path, offset); + break; + } +} + +static const MemoryRegionOps npcm7xx_rng_ops = { + .read = npcm7xx_rng_read, + .write = npcm7xx_rng_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void npcm7xx_rng_enter_reset(Object *obj, ResetType type) +{ + NPCM7xxRNGState *s = NPCM7XX_RNG(obj); + + s->rngcs = 0; + s->rngd = 0; + s->rngmode = 0; +} + +static void npcm7xx_rng_init(Object *obj) +{ + NPCM7xxRNGState *s = NPCM7XX_RNG(obj); + + memory_region_init_io(&s->iomem, obj, &npcm7xx_rng_ops, s, "regs", + NPCM7XX_RNG_REGS_SIZE); + sysbus_init_mmio(&s->parent, &s->iomem); +} + +static const VMStateDescription vmstate_npcm7xx_rng = { + .name = "npcm7xx-rng", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT8(rngcs, NPCM7xxRNGState), + VMSTATE_UINT8(rngd, NPCM7xxRNGState), + VMSTATE_UINT8(rngmode, NPCM7xxRNGState), + VMSTATE_END_OF_LIST(), + }, +}; + +static void npcm7xx_rng_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "NPCM7xx Random Number Generator"; + dc->vmsd = &vmstate_npcm7xx_rng; + rc->phases.enter = npcm7xx_rng_enter_reset; +} + +static const TypeInfo npcm7xx_rng_types[] = { + { + .name = TYPE_NPCM7XX_RNG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NPCM7xxRNGState), + .class_init = npcm7xx_rng_class_init, + .instance_init = npcm7xx_rng_init, + }, +}; +DEFINE_TYPES(npcm7xx_rng_types); diff --git a/hw/misc/sifive_u_otp.c b/hw/misc/sifive_u_otp.c index c2f3c8e129..60066375ab 100644 --- a/hw/misc/sifive_u_otp.c +++ b/hw/misc/sifive_u_otp.c @@ -19,11 +19,22 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "qemu/log.h" #include "qemu/module.h" #include "hw/misc/sifive_u_otp.h" +#include "sysemu/blockdev.h" +#include "sysemu/block-backend.h" + +#define WRITTEN_BIT_ON 0x1 + +#define SET_FUSEARRAY_BIT(map, i, off, bit) \ + map[i] = bit ? (map[i] | bit << off) : (map[i] & ~(0x1 << off)) + +#define GET_FUSEARRAY_BIT(map, i, off) \ + ((map[i] >> off) & 0x1) static uint64_t sifive_u_otp_read(void *opaque, hwaddr addr, unsigned int size) { @@ -46,6 +57,16 @@ static uint64_t sifive_u_otp_read(void *opaque, hwaddr addr, unsigned int size) if ((s->pce & SIFIVE_U_OTP_PCE_EN) && (s->pdstb & SIFIVE_U_OTP_PDSTB_EN) && (s->ptrim & SIFIVE_U_OTP_PTRIM_EN)) { + + /* read from backend */ + if (s->blk) { + int32_t buf; + + blk_pread(s->blk, s->pa * SIFIVE_U_OTP_FUSE_WORD, &buf, + SIFIVE_U_OTP_FUSE_WORD); + return buf; + } + return s->fuse[s->pa & SIFIVE_U_OTP_PA_MASK]; } else { return 0xff; @@ -123,7 +144,30 @@ static void sifive_u_otp_write(void *opaque, hwaddr addr, s->ptrim = val32; break; case SIFIVE_U_OTP_PWE: - s->pwe = val32; + s->pwe = val32 & SIFIVE_U_OTP_PWE_EN; + + /* PWE is enabled. Ignore PAS=1 (no redundancy cell) */ + if (s->pwe && !s->pas) { + if (GET_FUSEARRAY_BIT(s->fuse_wo, s->pa, s->paio)) { + qemu_log_mask(LOG_GUEST_ERROR, + "write once error: idx<%u>, bit<%u>\n", + s->pa, s->paio); + break; + } + + /* write bit data */ + SET_FUSEARRAY_BIT(s->fuse, s->pa, s->paio, s->pdin); + + /* write to backend */ + if (s->blk) { + blk_pwrite(s->blk, s->pa * SIFIVE_U_OTP_FUSE_WORD, + &s->fuse[s->pa], SIFIVE_U_OTP_FUSE_WORD, 0); + } + + /* update written bit */ + SET_FUSEARRAY_BIT(s->fuse_wo, s->pa, s->paio, WRITTEN_BIT_ON); + } + break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%" HWADDR_PRIx @@ -143,16 +187,48 @@ static const MemoryRegionOps sifive_u_otp_ops = { static Property sifive_u_otp_properties[] = { DEFINE_PROP_UINT32("serial", SiFiveUOTPState, serial, 0), + DEFINE_PROP_DRIVE("drive", SiFiveUOTPState, blk), DEFINE_PROP_END_OF_LIST(), }; static void sifive_u_otp_realize(DeviceState *dev, Error **errp) { SiFiveUOTPState *s = SIFIVE_U_OTP(dev); + DriveInfo *dinfo; memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_u_otp_ops, s, TYPE_SIFIVE_U_OTP, SIFIVE_U_OTP_REG_SIZE); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); + + dinfo = drive_get_next(IF_NONE); + if (dinfo) { + int ret; + uint64_t perm; + int filesize; + BlockBackend *blk; + + blk = blk_by_legacy_dinfo(dinfo); + filesize = SIFIVE_U_OTP_NUM_FUSES * SIFIVE_U_OTP_FUSE_WORD; + if (blk_getlength(blk) < filesize) { + error_setg(errp, "OTP drive size < 16K"); + return; + } + + qdev_prop_set_drive_err(dev, "drive", blk, errp); + + if (s->blk) { + perm = BLK_PERM_CONSISTENT_READ | + (blk_is_read_only(s->blk) ? 0 : BLK_PERM_WRITE); + ret = blk_set_perm(s->blk, perm, BLK_PERM_ALL, errp); + if (ret < 0) { + return; + } + + if (blk_pread(s->blk, 0, s->fuse, filesize) != filesize) { + error_setg(errp, "failed to read the initial flash content"); + } + } + } } static void sifive_u_otp_reset(DeviceState *dev) @@ -165,6 +241,23 @@ static void sifive_u_otp_reset(DeviceState *dev) /* Make a valid content of serial number */ s->fuse[SIFIVE_U_OTP_SERIAL_ADDR] = s->serial; s->fuse[SIFIVE_U_OTP_SERIAL_ADDR + 1] = ~(s->serial); + + if (s->blk) { + /* Put serial number to backend as well*/ + uint32_t serial_data; + int index = SIFIVE_U_OTP_SERIAL_ADDR; + + serial_data = s->serial; + blk_pwrite(s->blk, index * SIFIVE_U_OTP_FUSE_WORD, + &serial_data, SIFIVE_U_OTP_FUSE_WORD, 0); + + serial_data = ~(s->serial); + blk_pwrite(s->blk, (index + 1) * SIFIVE_U_OTP_FUSE_WORD, + &serial_data, SIFIVE_U_OTP_FUSE_WORD, 0); + } + + /* Initialize write-once map */ + memset(s->fuse_wo, 0x00, sizeof(s->fuse_wo)); } static void sifive_u_otp_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 6054f9adf3..b5118acd3f 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -84,7 +84,6 @@ aspeed_scu_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 mps2_scc_read(uint64_t offset, uint64_t data, unsigned size) "MPS2 SCC read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" mps2_scc_write(uint64_t offset, uint64_t data, unsigned size) "MPS2 SCC write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" mps2_scc_reset(void) "MPS2 SCC: reset" -mps2_scc_leds(char led7, char led6, char led5, char led4, char led3, char led2, char led1, char led0) "MPS2 SCC LEDs: %c%c%c%c%c%c%c%c" mps2_scc_cfg_write(unsigned function, unsigned device, uint32_t value) "MPS2 SCC config write: function %d device %d data 0x%" PRIx32 mps2_scc_cfg_read(unsigned function, unsigned device, uint32_t value) "MPS2 SCC config read: function %d device %d data 0x%" PRIx32 @@ -92,7 +91,6 @@ mps2_scc_cfg_read(unsigned function, unsigned device, uint32_t value) "MPS2 SCC mps2_fpgaio_read(uint64_t offset, uint64_t data, unsigned size) "MPS2 FPGAIO read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" mps2_fpgaio_write(uint64_t offset, uint64_t data, unsigned size) "MPS2 FPGAIO write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" mps2_fpgaio_reset(void) "MPS2 FPGAIO: reset" -mps2_fpgaio_leds(char led1, char led0) "MPS2 FPGAIO LEDs: %c%c" # msf2-sysreg.c msf2_sysreg_write(uint64_t offset, uint32_t val, uint32_t prev) "msf2-sysreg write: addr 0x%08" PRIx64 " data 0x%" PRIx32 " prev 0x%" PRIx32 @@ -118,6 +116,10 @@ npcm7xx_clk_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " valu npcm7xx_gcr_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32 npcm7xx_gcr_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32 +# npcm7xx_rng.c +npcm7xx_rng_read(uint64_t offset, uint64_t value, unsigned size) "offset: 0x%04" PRIx64 " value: 0x%02" PRIx64 " size: %u" +npcm7xx_rng_write(uint64_t offset, uint64_t value, unsigned size) "offset: 0x%04" PRIx64 " value: 0x%02" PRIx64 " size: %u" + # stm32f4xx_syscfg.c stm32f4xx_syscfg_set_irq(int gpio, int line, int level) "Interupt: GPIO: %d, Line: %d; Level: %d" stm32f4xx_pulse_exti(int irq) "Pulse EXTI: %d" @@ -223,6 +225,15 @@ via1_adb_poll(uint8_t data, const char *vadbint, int status, int index, int size grlib_ahb_pnp_read(uint64_t addr, uint32_t value) "AHB PnP read addr:0x%03"PRIx64" data:0x%08x" grlib_apb_pnp_read(uint64_t addr, uint32_t value) "APB PnP read addr:0x%03"PRIx64" data:0x%08x" +# led.c +led_set_intensity(const char *color, const char *desc, uint8_t intensity_percent) "LED desc:'%s' color:%s intensity: %u%%" +led_change_intensity(const char *color, const char *desc, uint8_t old_intensity_percent, uint8_t new_intensity_percent) "LED desc:'%s' color:%s intensity %u%% -> %u%%" + # pca9552.c pca955x_gpio_status(const char *description, const char *buf) "%s GPIOs 0-15 [%s]" pca955x_gpio_change(const char *description, unsigned id, unsigned prev_state, unsigned current_state) "%s GPIO id:%u status: %u -> %u" + +# bcm2835_cprman.c +bcm2835_cprman_read(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64 +bcm2835_cprman_write(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64 +bcm2835_cprman_write_invalid_magic(uint64_t offset, uint64_t value) "offset:0x%" PRIx64 " value:0x%" PRIx64 diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c index 2093f1bad0..581320a0e7 100644 --- a/hw/net/spapr_llan.c +++ b/hw/net/spapr_llan.c @@ -688,7 +688,8 @@ static target_ulong h_send_logical_lan(PowerPCCPU *cpu, SpaprVioDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); SpaprVioVlan *dev = VIO_SPAPR_VLAN_DEVICE(sdev); unsigned total_len; - uint8_t *lbuf, *p; + uint8_t *p; + g_autofree uint8_t *lbuf = NULL; int i, nbufs; int ret; @@ -729,7 +730,7 @@ static target_ulong h_send_logical_lan(PowerPCCPU *cpu, return H_RESOURCE; } - lbuf = alloca(total_len); + lbuf = g_malloc(total_len); p = lbuf; for (i = 0; i < nbufs; i++) { ret = spapr_vio_dma_read(sdev, VLAN_BD_ADDR(bufs[i]), diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 63315f2d0f..227075103e 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1483,9 +1483,9 @@ void spapr_free_hpt(SpaprMachineState *spapr) close_htab_fd(spapr); } -void spapr_reallocate_hpt(SpaprMachineState *spapr, int shift, - Error **errp) +int spapr_reallocate_hpt(SpaprMachineState *spapr, int shift, Error **errp) { + ERRP_GUARD(); long rc; /* Clean up any HPT info from a previous boot */ @@ -1495,22 +1495,23 @@ void spapr_reallocate_hpt(SpaprMachineState *spapr, int shift, if (rc == -EOPNOTSUPP) { error_setg(errp, "HPT not supported in nested guests"); - return; + return -EOPNOTSUPP; } if (rc < 0) { /* kernel-side HPT needed, but couldn't allocate one */ - error_setg_errno(errp, errno, - "Failed to allocate KVM HPT of order %d (try smaller maxmem?)", + error_setg_errno(errp, errno, "Failed to allocate KVM HPT of order %d", shift); - /* This is almost certainly fatal, but if the caller really - * wants to carry on with shift == 0, it's welcome to try */ + error_append_hint(errp, "Try smaller maxmem?\n"); + return -errno; } else if (rc > 0) { /* kernel-side HPT allocated */ if (rc != shift) { error_setg(errp, - "Requested order %d HPT, but kernel allocated order %ld (try smaller maxmem?)", + "Requested order %d HPT, but kernel allocated order %ld", shift, rc); + error_append_hint(errp, "Try smaller maxmem?\n"); + return -ENOSPC; } spapr->htab_shift = shift; @@ -1524,7 +1525,7 @@ void spapr_reallocate_hpt(SpaprMachineState *spapr, int shift, if (!spapr->htab) { error_setg_errno(errp, errno, "Could not allocate HPT of order %d", shift); - return; + return -ENOMEM; } memset(spapr->htab, 0, size); @@ -1537,6 +1538,7 @@ void spapr_reallocate_hpt(SpaprMachineState *spapr, int shift, /* We're setting up a hash table, so that means we're not radix */ spapr->patb_entry = 0; spapr_set_all_lpcrs(0, LPCR_HR | LPCR_UPRT); + return 0; } void spapr_setup_hpt(SpaprMachineState *spapr) @@ -2290,11 +2292,13 @@ static int htab_load(QEMUFile *f, void *opaque, int version_id) } if (section_hdr) { + int ret; + /* First section gives the htab size */ - spapr_reallocate_hpt(spapr, section_hdr, &local_err); - if (local_err) { + ret = spapr_reallocate_hpt(spapr, section_hdr, &local_err); + if (ret < 0) { error_report_err(local_err); - return -EINVAL; + return ret; } return 0; } @@ -2345,8 +2349,10 @@ static int htab_load(QEMUFile *f, void *opaque, int version_id) assert(fd >= 0); - rc = kvmppc_load_htab_chunk(f, fd, index, n_valid, n_invalid); + rc = kvmppc_load_htab_chunk(f, fd, index, n_valid, n_invalid, + &local_err); if (rc < 0) { + error_report_err(local_err); return rc; } } @@ -2641,6 +2647,16 @@ static hwaddr spapr_rma_size(SpaprMachineState *spapr, Error **errp) return rma_size; } +static void spapr_create_nvdimm_dr_connectors(SpaprMachineState *spapr) +{ + MachineState *machine = MACHINE(spapr); + int i; + + for (i = 0; i < machine->ram_slots; i++) { + spapr_dr_connector_new(OBJECT(spapr), TYPE_SPAPR_DRC_PMEM, i); + } +} + /* pSeries LPAR / sPAPR hardware init */ static void spapr_machine_init(MachineState *machine) { @@ -3372,7 +3388,7 @@ int spapr_lmb_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, return 0; } -static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size, +static bool spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size, bool dedicated_hp_event_source, Error **errp) { SpaprDrc *drc; @@ -3393,7 +3409,7 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size, addr / SPAPR_MEMORY_BLOCK_SIZE); spapr_drc_detach(drc); } - return; + return false; } if (!hotplugged) { spapr_drc_reset(drc); @@ -3415,52 +3431,43 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size, nr_lmbs); } } + return true; } static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - Error *local_err = NULL; SpaprMachineState *ms = SPAPR_MACHINE(hotplug_dev); PCDIMMDevice *dimm = PC_DIMM(dev); - uint64_t size, addr, slot; + uint64_t size, addr; + int64_t slot; bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); size = memory_device_get_region_size(MEMORY_DEVICE(dev), &error_abort); - pc_dimm_plug(dimm, MACHINE(ms), &local_err); - if (local_err) { - goto out; - } + pc_dimm_plug(dimm, MACHINE(ms)); if (!is_nvdimm) { addr = object_property_get_uint(OBJECT(dimm), - PC_DIMM_ADDR_PROP, &local_err); - if (local_err) { + PC_DIMM_ADDR_PROP, &error_abort); + if (!spapr_add_lmbs(dev, addr, size, + spapr_ovec_test(ms->ov5_cas, OV5_HP_EVT), errp)) { goto out_unplug; } - spapr_add_lmbs(dev, addr, size, - spapr_ovec_test(ms->ov5_cas, OV5_HP_EVT), - &local_err); } else { - slot = object_property_get_uint(OBJECT(dimm), - PC_DIMM_SLOT_PROP, &local_err); - if (local_err) { + slot = object_property_get_int(OBJECT(dimm), + PC_DIMM_SLOT_PROP, &error_abort); + /* We should have valid slot number at this point */ + g_assert(slot >= 0); + if (!spapr_add_nvdimm(dev, slot, errp)) { goto out_unplug; } - spapr_add_nvdimm(dev, slot, &local_err); - } - - if (local_err) { - goto out_unplug; } return; out_unplug: pc_dimm_unplug(dimm, MACHINE(ms)); -out: - error_propagate(errp, local_err); } static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, @@ -3565,8 +3572,8 @@ static SpaprDimmState *spapr_recover_pending_dimm_state(SpaprMachineState *ms, uint64_t addr_start, addr; int i; - addr_start = object_property_get_int(OBJECT(dimm), PC_DIMM_ADDR_PROP, - &error_abort); + addr_start = object_property_get_uint(OBJECT(dimm), PC_DIMM_ADDR_PROP, + &error_abort); addr = addr_start; for (i = 0; i < nr_lmbs; i++) { @@ -3624,7 +3631,6 @@ static void spapr_memory_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { SpaprMachineState *spapr = SPAPR_MACHINE(hotplug_dev); - Error *local_err = NULL; PCDIMMDevice *dimm = PC_DIMM(dev); uint32_t nr_lmbs; uint64_t size, addr_start, addr; @@ -3640,11 +3646,7 @@ static void spapr_memory_unplug_request(HotplugHandler *hotplug_dev, nr_lmbs = size / SPAPR_MEMORY_BLOCK_SIZE; addr_start = object_property_get_uint(OBJECT(dimm), PC_DIMM_ADDR_PROP, - &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } + &error_abort); /* * An existing pending dimm state for this DIMM means that there is an diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index b03620823a..2f7dc3c23d 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -187,8 +187,7 @@ static void spapr_unrealize_vcpu(PowerPCCPU *cpu, SpaprCpuCore *sc) vmstate_unregister(NULL, &vmstate_spapr_cpu_state, cpu->machine_data); } spapr_irq_cpu_intc_destroy(SPAPR_MACHINE(qdev_get_machine()), cpu); - cpu_remove_sync(CPU(cpu)); - object_unparent(OBJECT(cpu)); + qdev_unrealize(DEVICE(cpu)); } /* @@ -213,18 +212,37 @@ static void spapr_cpu_core_reset_handler(void *opaque) spapr_cpu_core_reset(opaque); } +static void spapr_delete_vcpu(PowerPCCPU *cpu) +{ + SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); + + cpu->machine_data = NULL; + g_free(spapr_cpu); + object_unparent(OBJECT(cpu)); +} + static void spapr_cpu_core_unrealize(DeviceState *dev) { SpaprCpuCore *sc = SPAPR_CPU_CORE(OBJECT(dev)); CPUCore *cc = CPU_CORE(dev); int i; - qemu_unregister_reset(spapr_cpu_core_reset_handler, sc); - for (i = 0; i < cc->nr_threads; i++) { - spapr_unrealize_vcpu(sc->threads[i], sc); + if (sc->threads[i]) { + /* + * Since this we can get here from the error path of + * spapr_cpu_core_realize(), make sure we only unrealize + * vCPUs that have already been realized. + */ + if (object_property_get_bool(OBJECT(sc->threads[i]), "realized", + &error_abort)) { + spapr_unrealize_vcpu(sc->threads[i], sc); + } + spapr_delete_vcpu(sc->threads[i]); + } } g_free(sc->threads); + qemu_unregister_reset(spapr_cpu_core_reset_handler, sc); } static bool spapr_realize_vcpu(PowerPCCPU *cpu, SpaprMachineState *spapr, @@ -244,7 +262,7 @@ static bool spapr_realize_vcpu(PowerPCCPU *cpu, SpaprMachineState *spapr, kvmppc_set_papr(cpu); if (spapr_irq_cpu_intc_create(spapr, cpu, errp) < 0) { - cpu_remove_sync(CPU(cpu)); + qdev_unrealize(DEVICE(cpu)); return false; } @@ -294,15 +312,6 @@ err: return NULL; } -static void spapr_delete_vcpu(PowerPCCPU *cpu, SpaprCpuCore *sc) -{ - SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); - - cpu->machine_data = NULL; - g_free(spapr_cpu); - object_unparent(OBJECT(cpu)); -} - static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) { /* We don't use SPAPR_MACHINE() in order to exit gracefully if the user @@ -313,39 +322,23 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) TYPE_SPAPR_MACHINE); SpaprCpuCore *sc = SPAPR_CPU_CORE(OBJECT(dev)); CPUCore *cc = CPU_CORE(OBJECT(dev)); - int i, j; + int i; if (!spapr) { error_setg(errp, TYPE_SPAPR_CPU_CORE " needs a pseries machine"); return; } - sc->threads = g_new(PowerPCCPU *, cc->nr_threads); + qemu_register_reset(spapr_cpu_core_reset_handler, sc); + sc->threads = g_new0(PowerPCCPU *, cc->nr_threads); for (i = 0; i < cc->nr_threads; i++) { sc->threads[i] = spapr_create_vcpu(sc, i, errp); - if (!sc->threads[i]) { - goto err; - } - } - - for (j = 0; j < cc->nr_threads; j++) { - if (!spapr_realize_vcpu(sc->threads[j], spapr, sc, errp)) { - goto err_unrealize; + if (!sc->threads[i] || + !spapr_realize_vcpu(sc->threads[i], spapr, sc, errp)) { + spapr_cpu_core_unrealize(dev); + return; } } - - qemu_register_reset(spapr_cpu_core_reset_handler, sc); - return; - -err_unrealize: - while (--j >= 0) { - spapr_unrealize_vcpu(sc->threads[j], sc); - } -err: - while (--i >= 0) { - spapr_delete_vcpu(sc->threads[i], sc); - } - g_free(sc->threads); } static Property spapr_cpu_core_properties[] = { diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index 697b28c343..77718cde1f 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -586,7 +586,8 @@ static void spapr_dr_connector_class_init(ObjectClass *k, void *data) dk->realize = realize; dk->unrealize = unrealize; /* - * Reason: it crashes FIXME find and document the real reason + * Reason: DR connector needs to be wired to either the machine or to a + * PHB in spapr_dr_connector_new(). */ dk->user_creatable = false; } diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index 1069d0197b..1add53547e 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -1000,10 +1000,22 @@ static void event_scan(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong args, uint32_t nret, target_ulong rets) { + int i; if (nargs != 4 || nret != 1) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } + + for (i = 0; i < EVENT_CLASS_MAX; i++) { + if (rtas_event_log_contains(EVENT_CLASS_MASK(i))) { + const SpaprEventSource *source = + spapr_event_sources_get_source(spapr->event_sources, i); + + g_assert(source->enabled); + qemu_irq_pulse(spapr_qirq(spapr, source->irq)); + } + } + rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND); } diff --git a/hw/ppc/spapr_nvdimm.c b/hw/ppc/spapr_nvdimm.c index b3a489e9fe..a833a63b5e 100644 --- a/hw/ppc/spapr_nvdimm.c +++ b/hw/ppc/spapr_nvdimm.c @@ -89,7 +89,7 @@ bool spapr_nvdimm_validate(HotplugHandler *hotplug_dev, NVDIMMDevice *nvdimm, } -void spapr_add_nvdimm(DeviceState *dev, uint64_t slot, Error **errp) +bool spapr_add_nvdimm(DeviceState *dev, uint64_t slot, Error **errp) { SpaprDrc *drc; bool hotplugged = spapr_drc_hotplugged(dev); @@ -98,25 +98,15 @@ void spapr_add_nvdimm(DeviceState *dev, uint64_t slot, Error **errp) g_assert(drc); if (!spapr_drc_attach(drc, dev, errp)) { - return; + return false; } if (hotplugged) { spapr_hotplug_req_add_by_index(drc); } + return true; } -void spapr_create_nvdimm_dr_connectors(SpaprMachineState *spapr) -{ - MachineState *machine = MACHINE(spapr); - int i; - - for (i = 0; i < machine->ram_slots; i++) { - spapr_dr_connector_new(OBJECT(spapr), TYPE_SPAPR_DRC_PMEM, i); - } -} - - static int spapr_dt_nvdimm(SpaprMachineState *spapr, void *fdt, int parent_offset, NVDIMMDevice *nvdimm) { diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index 21adaae56e..9b3fe3fb1e 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -33,19 +33,36 @@ #include <libfdt.h> #if defined(TARGET_RISCV32) -# define KERNEL_BOOT_ADDRESS 0x80400000 #define fw_dynamic_info_data(__val) cpu_to_le32(__val) #else -# define KERNEL_BOOT_ADDRESS 0x80200000 #define fw_dynamic_info_data(__val) cpu_to_le64(__val) #endif -void riscv_find_and_load_firmware(MachineState *machine, - const char *default_machine_firmware, - hwaddr firmware_load_addr, - symbol_fn_t sym_cb) +bool riscv_is_32_bit(MachineState *machine) +{ + if (!strncmp(machine->cpu_type, "rv32", 4)) { + return true; + } else { + return false; + } +} + +target_ulong riscv_calc_kernel_start_addr(MachineState *machine, + target_ulong firmware_end_addr) { + if (riscv_is_32_bit(machine)) { + return QEMU_ALIGN_UP(firmware_end_addr, 4 * MiB); + } else { + return QEMU_ALIGN_UP(firmware_end_addr, 2 * MiB); + } +} + +target_ulong riscv_find_and_load_firmware(MachineState *machine, + const char *default_machine_firmware, + hwaddr firmware_load_addr, + symbol_fn_t sym_cb) { char *firmware_filename = NULL; + target_ulong firmware_end_addr = firmware_load_addr; if ((!machine->firmware) || (!strcmp(machine->firmware, "default"))) { /* @@ -60,9 +77,12 @@ void riscv_find_and_load_firmware(MachineState *machine, if (firmware_filename) { /* If not "none" load the firmware */ - riscv_load_firmware(firmware_filename, firmware_load_addr, sym_cb); + firmware_end_addr = riscv_load_firmware(firmware_filename, + firmware_load_addr, sym_cb); g_free(firmware_filename); } + + return firmware_end_addr; } char *riscv_find_firmware(const char *firmware_filename) @@ -91,24 +111,28 @@ target_ulong riscv_load_firmware(const char *firmware_filename, hwaddr firmware_load_addr, symbol_fn_t sym_cb) { - uint64_t firmware_entry; + uint64_t firmware_entry, firmware_size, firmware_end; if (load_elf_ram_sym(firmware_filename, NULL, NULL, NULL, - &firmware_entry, NULL, NULL, NULL, + &firmware_entry, NULL, &firmware_end, NULL, 0, EM_RISCV, 1, 0, NULL, true, sym_cb) > 0) { - return firmware_entry; + return firmware_end; } - if (load_image_targphys_as(firmware_filename, firmware_load_addr, - ram_size, NULL) > 0) { - return firmware_load_addr; + firmware_size = load_image_targphys_as(firmware_filename, + firmware_load_addr, ram_size, NULL); + + if (firmware_size > 0) { + return firmware_load_addr + firmware_size; } error_report("could not load firmware '%s'", firmware_filename); exit(1); } -target_ulong riscv_load_kernel(const char *kernel_filename, symbol_fn_t sym_cb) +target_ulong riscv_load_kernel(const char *kernel_filename, + target_ulong kernel_start_addr, + symbol_fn_t sym_cb) { uint64_t kernel_entry; @@ -123,9 +147,9 @@ target_ulong riscv_load_kernel(const char *kernel_filename, symbol_fn_t sym_cb) return kernel_entry; } - if (load_image_targphys_as(kernel_filename, KERNEL_BOOT_ADDRESS, + if (load_image_targphys_as(kernel_filename, kernel_start_addr, ram_size, NULL) > 0) { - return KERNEL_BOOT_ADDRESS; + return kernel_start_addr; } error_report("could not load kernel '%s'", kernel_filename); diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index 0531bd879b..cc758b78b8 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -75,7 +75,8 @@ static void opentitan_board_init(MachineState *machine) } if (machine->kernel_filename) { - riscv_load_kernel(machine->kernel_filename, NULL); + riscv_load_kernel(machine->kernel_filename, + memmap[IBEX_DEV_RAM].base, NULL); } } diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index fcfac16816..59bac4cc9a 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -114,7 +114,8 @@ static void sifive_e_machine_init(MachineState *machine) memmap[SIFIVE_E_DEV_MROM].base, &address_space_memory); if (machine->kernel_filename) { - riscv_load_kernel(machine->kernel_filename, NULL); + riscv_load_kernel(machine->kernel_filename, + memmap[SIFIVE_E_DEV_DTIM].base, NULL); } } diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 6ad975d692..b2472c6627 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -415,6 +415,7 @@ static void sifive_u_machine_init(MachineState *machine) MemoryRegion *main_mem = g_new(MemoryRegion, 1); MemoryRegion *flash0 = g_new(MemoryRegion, 1); target_ulong start_addr = memmap[SIFIVE_U_DEV_DRAM].base; + target_ulong firmware_end_addr, kernel_start_addr; uint32_t start_addr_hi32 = 0x00000000; int i; uint32_t fdt_load_addr; @@ -424,6 +425,8 @@ static void sifive_u_machine_init(MachineState *machine) object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_RISCV_U_SOC); object_property_set_uint(OBJECT(&s->soc), "serial", s->serial, &error_abort); + object_property_set_str(OBJECT(&s->soc), "cpu-type", machine->cpu_type, + &error_abort); qdev_realize(DEVICE(&s->soc), NULL, &error_abort); /* register RAM */ @@ -472,10 +475,15 @@ static void sifive_u_machine_init(MachineState *machine) break; } - riscv_find_and_load_firmware(machine, BIOS_FILENAME, start_addr, NULL); + firmware_end_addr = riscv_find_and_load_firmware(machine, BIOS_FILENAME, + start_addr, NULL); if (machine->kernel_filename) { - kernel_entry = riscv_load_kernel(machine->kernel_filename, NULL); + kernel_start_addr = riscv_calc_kernel_start_addr(machine, + firmware_end_addr); + + kernel_entry = riscv_load_kernel(machine->kernel_filename, + kernel_start_addr, NULL); if (machine->initrd_filename) { hwaddr start; @@ -590,6 +598,11 @@ static void sifive_u_machine_class_init(ObjectClass *oc, void *data) mc->init = sifive_u_machine_init; mc->max_cpus = SIFIVE_U_MANAGEMENT_CPU_COUNT + SIFIVE_U_COMPUTE_CPU_COUNT; mc->min_cpus = SIFIVE_U_MANAGEMENT_CPU_COUNT + 1; +#if defined(TARGET_RISCV32) + mc->default_cpu_type = TYPE_RISCV_CPU_SIFIVE_U34; +#elif defined(TARGET_RISCV64) + mc->default_cpu_type = TYPE_RISCV_CPU_SIFIVE_U54; +#endif mc->default_cpus = mc->min_cpus; object_class_property_add_bool(oc, "start-in-flash", @@ -618,7 +631,6 @@ type_init(sifive_u_machine_init_register_types) static void sifive_u_soc_instance_init(Object *obj) { - MachineState *ms = MACHINE(qdev_get_machine()); SiFiveUSoCState *s = RISCV_U_SOC(obj); object_initialize_child(obj, "e-cluster", &s->e_cluster, TYPE_CPU_CLUSTER); @@ -636,10 +648,6 @@ static void sifive_u_soc_instance_init(Object *obj) object_initialize_child(OBJECT(&s->u_cluster), "u-cpus", &s->u_cpus, TYPE_RISCV_HART_ARRAY); - qdev_prop_set_uint32(DEVICE(&s->u_cpus), "num-harts", ms->smp.cpus - 1); - qdev_prop_set_uint32(DEVICE(&s->u_cpus), "hartid-base", 1); - qdev_prop_set_string(DEVICE(&s->u_cpus), "cpu-type", SIFIVE_U_CPU); - qdev_prop_set_uint64(DEVICE(&s->u_cpus), "resetvec", 0x1004); object_initialize_child(obj, "prci", &s->prci, TYPE_SIFIVE_U_PRCI); object_initialize_child(obj, "otp", &s->otp, TYPE_SIFIVE_U_OTP); @@ -661,6 +669,11 @@ static void sifive_u_soc_realize(DeviceState *dev, Error **errp) int i; NICInfo *nd = &nd_table[0]; + qdev_prop_set_uint32(DEVICE(&s->u_cpus), "num-harts", ms->smp.cpus - 1); + qdev_prop_set_uint32(DEVICE(&s->u_cpus), "hartid-base", 1); + qdev_prop_set_string(DEVICE(&s->u_cpus), "cpu-type", s->cpu_type); + qdev_prop_set_uint64(DEVICE(&s->u_cpus), "resetvec", 0x1004); + sysbus_realize(SYS_BUS_DEVICE(&s->e_cpus), &error_abort); sysbus_realize(SYS_BUS_DEVICE(&s->u_cpus), &error_abort); /* @@ -792,6 +805,7 @@ static void sifive_u_soc_realize(DeviceState *dev, Error **errp) static Property sifive_u_soc_props[] = { DEFINE_PROP_UINT32("serial", SiFiveUSoCState, serial, OTP_SERIAL), + DEFINE_PROP_STRING("cpu-type", SiFiveUSoCState, cpu_type), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index 3fd152a035..facac6e7d2 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -195,6 +195,7 @@ static void spike_board_init(MachineState *machine) MemoryRegion *system_memory = get_system_memory(); MemoryRegion *main_mem = g_new(MemoryRegion, 1); MemoryRegion *mask_rom = g_new(MemoryRegion, 1); + target_ulong firmware_end_addr, kernel_start_addr; uint32_t fdt_load_addr; uint64_t kernel_entry; char *soc_name; @@ -261,12 +262,16 @@ static void spike_board_init(MachineState *machine) memory_region_add_subregion(system_memory, memmap[SPIKE_MROM].base, mask_rom); - riscv_find_and_load_firmware(machine, BIOS_FILENAME, - memmap[SPIKE_DRAM].base, - htif_symbol_callback); + firmware_end_addr = riscv_find_and_load_firmware(machine, BIOS_FILENAME, + memmap[SPIKE_DRAM].base, + htif_symbol_callback); if (machine->kernel_filename) { + kernel_start_addr = riscv_calc_kernel_start_addr(machine, + firmware_end_addr); + kernel_entry = riscv_load_kernel(machine->kernel_filename, + kernel_start_addr, htif_symbol_callback); if (machine->initrd_filename) { diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 41bd2f38ba..6bfd10dfc7 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -493,6 +493,7 @@ static void virt_machine_init(MachineState *machine) char *plic_hart_config, *soc_name; size_t plic_hart_config_len; target_ulong start_addr = memmap[VIRT_DRAM].base; + target_ulong firmware_end_addr, kernel_start_addr; uint32_t fdt_load_addr; uint64_t kernel_entry; DeviceState *mmio_plic, *virtio_plic, *pcie_plic; @@ -602,11 +603,15 @@ static void virt_machine_init(MachineState *machine) memory_region_add_subregion(system_memory, memmap[VIRT_MROM].base, mask_rom); - riscv_find_and_load_firmware(machine, BIOS_FILENAME, - memmap[VIRT_DRAM].base, NULL); + firmware_end_addr = riscv_find_and_load_firmware(machine, BIOS_FILENAME, + start_addr, NULL); if (machine->kernel_filename) { - kernel_entry = riscv_load_kernel(machine->kernel_filename, NULL); + kernel_start_addr = riscv_calc_kernel_start_addr(machine, + firmware_end_addr); + + kernel_entry = riscv_load_kernel(machine->kernel_filename, + kernel_start_addr, NULL); if (machine->initrd_filename) { hwaddr start; diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index e52182f946..2e900335ea 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -101,6 +101,7 @@ static const char *const reset_dev_types[] = { "s390-sclp-event-facility", "s390-flic", "diag288", + TYPE_S390_PCI_HOST_BRIDGE, }; static void subsystem_reset(void) diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index 00f1e4648d..0cf2290826 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -285,11 +285,6 @@ int sclp_service_call_protected(CPUS390XState *env, uint64_t sccb, goto out_write; } - if (!sccb_verify_boundary(sccb, be16_to_cpu(work_sccb->h.length), code)) { - work_sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION); - goto out_write; - } - sclp_c->execute(sclp, work_sccb, code); out_write: s390_cpu_pv_mem_write(env_archcpu(env), 0, work_sccb, diff --git a/hw/sd/sd.c b/hw/sd/sd.c index c3febed243..3091382614 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -102,11 +102,14 @@ struct SDState { uint32_t card_status; uint8_t sd_status[64]; - /* Configurable properties */ + /* Static properties */ + uint8_t spec_version; BlockBackend *blk; bool spi; + /* Runtime changeables */ + uint32_t mode; /* current card mode, one of SDCardModes */ int32_t state; /* current card state, one of SDCardStates */ uint32_t vhs; @@ -251,11 +254,11 @@ static const int sd_cmd_class[SDMMC_CMD_MAX] = { 7, 7, 10, 7, 9, 9, 9, 8, 8, 10, 8, 8, 8, 8, 8, 8, }; -static uint8_t sd_crc7(void *message, size_t width) +static uint8_t sd_crc7(const void *message, size_t width) { int i, bit; uint8_t shift_reg = 0x00; - uint8_t *msg = (uint8_t *) message; + const uint8_t *msg = (const uint8_t *)message; for (i = 0; i < width; i ++, msg ++) for (bit = 7; bit >= 0; bit --) { @@ -267,11 +270,11 @@ static uint8_t sd_crc7(void *message, size_t width) return shift_reg; } -static uint16_t sd_crc16(void *message, size_t width) +static uint16_t sd_crc16(const void *message, size_t width) { int i, bit; uint16_t shift_reg = 0x0000; - uint16_t *msg = (uint16_t *) message; + const uint16_t *msg = (const uint16_t *)message; width <<= 1; for (i = 0; i < width; i ++, msg ++) @@ -824,6 +827,7 @@ static void sd_function_switch(SDState *sd, uint32_t arg) sd->data[12] = 0x80; /* Supported group 1 functions */ sd->data[13] = 0x03; + memset(&sd->data[14], 0, 3); for (i = 0; i < 6; i ++) { new_func = (arg >> (i * 4)) & 0x0f; if (mode && new_func != 0x0f) @@ -1676,7 +1680,7 @@ static sd_rsp_type_t sd_app_command(SDState *sd, return sd_illegal; } -static int cmd_valid_while_locked(SDState *sd, SDRequest *req) +static int cmd_valid_while_locked(SDState *sd, const uint8_t cmd) { /* Valid commands in locked state: * basic class (0) @@ -1687,13 +1691,12 @@ static int cmd_valid_while_locked(SDState *sd, SDRequest *req) * Anything else provokes an "illegal command" response. */ if (sd->expecting_acmd) { - return req->cmd == 41 || req->cmd == 42; + return cmd == 41 || cmd == 42; } - if (req->cmd == 16 || req->cmd == 55) { + if (cmd == 16 || cmd == 55) { return 1; } - return sd_cmd_class[req->cmd] == 0 - || sd_cmd_class[req->cmd] == 7; + return sd_cmd_class[cmd] == 0 || sd_cmd_class[cmd] == 7; } int sd_do_command(SDState *sd, SDRequest *req, @@ -1719,7 +1722,7 @@ int sd_do_command(SDState *sd, SDRequest *req, } if (sd->card_status & CARD_IS_LOCKED) { - if (!cmd_valid_while_locked(sd, req)) { + if (!cmd_valid_while_locked(sd, req->cmd)) { sd->card_status |= ILLEGAL_COMMAND; sd->expecting_acmd = false; qemu_log_mask(LOG_GUEST_ERROR, "SD: Card is locked\n"); @@ -1980,7 +1983,7 @@ uint8_t sd_read_byte(SDState *sd) { /* TODO: Append CRCs */ uint8_t ret; - int io_len; + uint32_t io_len; if (!sd->blk || !blk_is_inserted(sd->blk) || !sd->enable) return 0x00; diff --git a/hw/sd/trace-events b/hw/sd/trace-events index 96c7ea5e52..4140e48540 100644 --- a/hw/sd/trace-events +++ b/hw/sd/trace-events @@ -52,7 +52,7 @@ sdcard_unlock(void) "" sdcard_read_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x" sdcard_write_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x" sdcard_write_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint8_t value) "%s %20s/ CMD%02d value 0x%02x" -sdcard_read_data(const char *proto, const char *cmd_desc, uint8_t cmd, int length) "%s %20s/ CMD%02d len %d" +sdcard_read_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t length) "%s %20s/ CMD%02d len %" PRIu32 sdcard_set_voltage(uint16_t millivolts) "%u mV" # milkymist-memcard.c diff --git a/hw/timer/armv7m_systick.c b/hw/timer/armv7m_systick.c index a8cec7eb56..2f192011eb 100644 --- a/hw/timer/armv7m_systick.c +++ b/hw/timer/armv7m_systick.c @@ -39,26 +39,6 @@ static inline int64_t systick_scale(SysTickState *s) } } -static void systick_reload(SysTickState *s, int reset) -{ - /* The Cortex-M3 Devices Generic User Guide says that "When the - * ENABLE bit is set to 1, the counter loads the RELOAD value from the - * SYST RVR register and then counts down". So, we need to check the - * ENABLE bit before reloading the value. - */ - trace_systick_reload(); - - if ((s->control & SYSTICK_ENABLE) == 0) { - return; - } - - if (reset) { - s->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - } - s->tick += (s->reload + 1) * systick_scale(s); - timer_mod(s->timer, s->tick); -} - static void systick_timer_tick(void *opaque) { SysTickState *s = (SysTickState *)opaque; @@ -70,10 +50,12 @@ static void systick_timer_tick(void *opaque) /* Tell the NVIC to pend the SysTick exception */ qemu_irq_pulse(s->irq); } - if (s->reload == 0) { - s->control &= ~SYSTICK_ENABLE; - } else { - systick_reload(s, 0); + if (ptimer_get_limit(s->ptimer) == 0) { + /* + * Timer expiry with SYST_RVR zero disables the timer + * (but doesn't clear SYST_CSR.ENABLE) + */ + ptimer_stop(s->ptimer); } } @@ -94,30 +76,11 @@ static MemTxResult systick_read(void *opaque, hwaddr addr, uint64_t *data, s->control &= ~SYSTICK_COUNTFLAG; break; case 0x4: /* SysTick Reload Value. */ - val = s->reload; + val = ptimer_get_limit(s->ptimer); break; case 0x8: /* SysTick Current Value. */ - { - int64_t t; - - if ((s->control & SYSTICK_ENABLE) == 0) { - val = 0; - break; - } - t = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - if (t >= s->tick) { - val = 0; - break; - } - val = ((s->tick - (t + 1)) / systick_scale(s)) + 1; - /* The interrupt in triggered when the timer reaches zero. - However the counter is not reloaded until the next clock - tick. This is a hack to return zero during the first tick. */ - if (val > s->reload) { - val = 0; - } + val = ptimer_get_count(s->ptimer); break; - } case 0xc: /* SysTick Calibration Value. */ val = 10000; break; @@ -149,39 +112,50 @@ static MemTxResult systick_write(void *opaque, hwaddr addr, switch (addr) { case 0x0: /* SysTick Control and Status. */ { - uint32_t oldval = s->control; + uint32_t oldval; + ptimer_transaction_begin(s->ptimer); + oldval = s->control; s->control &= 0xfffffff8; s->control |= value & 7; + if ((oldval ^ value) & SYSTICK_ENABLE) { - int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); if (value & SYSTICK_ENABLE) { - if (s->tick) { - s->tick += now; - timer_mod(s->timer, s->tick); - } else { - systick_reload(s, 1); - } + /* + * Always reload the period in case board code has + * changed system_clock_scale. If we ever replace that + * global with a more sensible API then we might be able + * to set the period only when it actually changes. + */ + ptimer_set_period(s->ptimer, systick_scale(s)); + ptimer_run(s->ptimer, 0); } else { - timer_del(s->timer); - s->tick -= now; - if (s->tick < 0) { - s->tick = 0; - } + ptimer_stop(s->ptimer); } } else if ((oldval ^ value) & SYSTICK_CLKSOURCE) { - /* This is a hack. Force the timer to be reloaded - when the reference clock is changed. */ - systick_reload(s, 1); + ptimer_set_period(s->ptimer, systick_scale(s)); } + ptimer_transaction_commit(s->ptimer); break; } case 0x4: /* SysTick Reload Value. */ - s->reload = value; + ptimer_transaction_begin(s->ptimer); + ptimer_set_limit(s->ptimer, value & 0xffffff, 0); + ptimer_transaction_commit(s->ptimer); break; - case 0x8: /* SysTick Current Value. Writes reload the timer. */ - systick_reload(s, 1); + case 0x8: /* SysTick Current Value. */ + /* + * Writing any value clears SYST_CVR to zero and clears + * SYST_CSR.COUNTFLAG. The counter will then reload from SYST_RVR + * on the next clock edge unless SYST_RVR is zero. + */ + ptimer_transaction_begin(s->ptimer); + if (ptimer_get_limit(s->ptimer) == 0) { + ptimer_stop(s->ptimer); + } + ptimer_set_count(s->ptimer, 0); s->control &= ~SYSTICK_COUNTFLAG; + ptimer_transaction_commit(s->ptimer); break; default: qemu_log_mask(LOG_GUEST_ERROR, @@ -210,10 +184,13 @@ static void systick_reset(DeviceState *dev) */ assert(system_clock_scale != 0); + ptimer_transaction_begin(s->ptimer); s->control = 0; - s->reload = 0; - s->tick = 0; - timer_del(s->timer); + ptimer_stop(s->ptimer); + ptimer_set_count(s->ptimer, 0); + ptimer_set_limit(s->ptimer, 0, 0); + ptimer_set_period(s->ptimer, systick_scale(s)); + ptimer_transaction_commit(s->ptimer); } static void systick_instance_init(Object *obj) @@ -229,18 +206,21 @@ static void systick_instance_init(Object *obj) static void systick_realize(DeviceState *dev, Error **errp) { SysTickState *s = SYSTICK(dev); - s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, systick_timer_tick, s); + s->ptimer = ptimer_init(systick_timer_tick, s, + PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD | + PTIMER_POLICY_NO_COUNTER_ROUND_DOWN | + PTIMER_POLICY_NO_IMMEDIATE_RELOAD | + PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT); } static const VMStateDescription vmstate_systick = { .name = "armv7m_systick", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { VMSTATE_UINT32(control, SysTickState), - VMSTATE_UINT32(reload, SysTickState), VMSTATE_INT64(tick, SysTickState), - VMSTATE_TIMER_PTR(timer, SysTickState), + VMSTATE_PTIMER(ptimer, SysTickState), VMSTATE_END_OF_LIST() } }; diff --git a/hw/timer/npcm7xx_timer.c b/hw/timer/npcm7xx_timer.c index 5703e43d40..d24445bd6e 100644 --- a/hw/timer/npcm7xx_timer.c +++ b/hw/timer/npcm7xx_timer.c @@ -17,6 +17,7 @@ #include "qemu/osdep.h" #include "hw/irq.h" +#include "hw/qdev-properties.h" #include "hw/misc/npcm7xx_clk.h" #include "hw/timer/npcm7xx_timer.h" #include "migration/vmstate.h" @@ -60,6 +61,50 @@ enum NPCM7xxTimerRegisters { #define NPCM7XX_TCSR_PRESCALE_START 0 #define NPCM7XX_TCSR_PRESCALE_LEN 8 +#define NPCM7XX_WTCR_WTCLK(rv) extract32(rv, 10, 2) +#define NPCM7XX_WTCR_FREEZE_EN BIT(9) +#define NPCM7XX_WTCR_WTE BIT(7) +#define NPCM7XX_WTCR_WTIE BIT(6) +#define NPCM7XX_WTCR_WTIS(rv) extract32(rv, 4, 2) +#define NPCM7XX_WTCR_WTIF BIT(3) +#define NPCM7XX_WTCR_WTRF BIT(2) +#define NPCM7XX_WTCR_WTRE BIT(1) +#define NPCM7XX_WTCR_WTR BIT(0) + +/* + * The number of clock cycles between interrupt and reset in watchdog, used + * by the software to handle the interrupt before system is reset. + */ +#define NPCM7XX_WATCHDOG_INTERRUPT_TO_RESET_CYCLES 1024 + +/* Start or resume the timer. */ +static void npcm7xx_timer_start(NPCM7xxBaseTimer *t) +{ + int64_t now; + + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + t->expires_ns = now + t->remaining_ns; + timer_mod(&t->qtimer, t->expires_ns); +} + +/* Stop counting. Record the time remaining so we can continue later. */ +static void npcm7xx_timer_pause(NPCM7xxBaseTimer *t) +{ + int64_t now; + + timer_del(&t->qtimer); + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + t->remaining_ns = t->expires_ns - now; +} + +/* Delete the timer and reset it to default state. */ +static void npcm7xx_timer_clear(NPCM7xxBaseTimer *t) +{ + timer_del(&t->qtimer); + t->expires_ns = 0; + t->remaining_ns = 0; +} + /* * Returns the index of timer in the tc->timer array. This can be used to * locate the registers that belong to this timer. @@ -102,6 +147,52 @@ static uint32_t npcm7xx_timer_ns_to_count(NPCM7xxTimer *t, int64_t ns) return count; } +static uint32_t npcm7xx_watchdog_timer_prescaler(const NPCM7xxWatchdogTimer *t) +{ + switch (NPCM7XX_WTCR_WTCLK(t->wtcr)) { + case 0: + return 1; + case 1: + return 256; + case 2: + return 2048; + case 3: + return 65536; + default: + g_assert_not_reached(); + } +} + +static void npcm7xx_watchdog_timer_reset_cycles(NPCM7xxWatchdogTimer *t, + int64_t cycles) +{ + uint32_t prescaler = npcm7xx_watchdog_timer_prescaler(t); + int64_t ns = (NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ) * cycles; + + /* + * The reset function always clears the current timer. The caller of the + * this needs to decide whether to start the watchdog timer based on + * specific flag in WTCR. + */ + npcm7xx_timer_clear(&t->base_timer); + + ns *= prescaler; + t->base_timer.remaining_ns = ns; +} + +static void npcm7xx_watchdog_timer_reset(NPCM7xxWatchdogTimer *t) +{ + int64_t cycles = 1; + uint32_t s = NPCM7XX_WTCR_WTIS(t->wtcr); + + g_assert(s <= 3); + + cycles <<= NPCM7XX_WATCHDOG_BASETIME_SHIFT; + cycles <<= 2 * s; + + npcm7xx_watchdog_timer_reset_cycles(t, cycles); +} + /* * Raise the interrupt line if there's a pending interrupt and interrupts are * enabled for this timer. If not, lower it. @@ -116,16 +207,6 @@ static void npcm7xx_timer_check_interrupt(NPCM7xxTimer *t) trace_npcm7xx_timer_irq(DEVICE(tc)->canonical_path, index, pending); } -/* Start or resume the timer. */ -static void npcm7xx_timer_start(NPCM7xxTimer *t) -{ - int64_t now; - - now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - t->expires_ns = now + t->remaining_ns; - timer_mod(&t->qtimer, t->expires_ns); -} - /* * Called when the counter reaches zero. Sets the interrupt flag, and either * restarts or disables the timer. @@ -138,9 +219,9 @@ static void npcm7xx_timer_reached_zero(NPCM7xxTimer *t) tc->tisr |= BIT(index); if (t->tcsr & NPCM7XX_TCSR_PERIODIC) { - t->remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr); + t->base_timer.remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr); if (t->tcsr & NPCM7XX_TCSR_CEN) { - npcm7xx_timer_start(t); + npcm7xx_timer_start(&t->base_timer); } } else { t->tcsr &= ~(NPCM7XX_TCSR_CEN | NPCM7XX_TCSR_CACT); @@ -149,18 +230,6 @@ static void npcm7xx_timer_reached_zero(NPCM7xxTimer *t) npcm7xx_timer_check_interrupt(t); } -/* Stop counting. Record the time remaining so we can continue later. */ -static void npcm7xx_timer_pause(NPCM7xxTimer *t) -{ - int64_t now; - - timer_del(&t->qtimer); - now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - t->remaining_ns = t->expires_ns - now; - if (t->remaining_ns <= 0) { - npcm7xx_timer_reached_zero(t); - } -} /* * Restart the timer from its initial value. If the timer was enabled and stays @@ -170,10 +239,10 @@ static void npcm7xx_timer_pause(NPCM7xxTimer *t) */ static void npcm7xx_timer_restart(NPCM7xxTimer *t, uint32_t old_tcsr) { - t->remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr); + t->base_timer.remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr); if (old_tcsr & t->tcsr & NPCM7XX_TCSR_CEN) { - npcm7xx_timer_start(t); + npcm7xx_timer_start(&t->base_timer); } } @@ -184,10 +253,10 @@ static uint32_t npcm7xx_timer_read_tdr(NPCM7xxTimer *t) if (t->tcsr & NPCM7XX_TCSR_CEN) { int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - return npcm7xx_timer_ns_to_count(t, t->expires_ns - now); + return npcm7xx_timer_ns_to_count(t, t->base_timer.expires_ns - now); } - return npcm7xx_timer_ns_to_count(t, t->remaining_ns); + return npcm7xx_timer_ns_to_count(t, t->base_timer.remaining_ns); } static void npcm7xx_timer_write_tcsr(NPCM7xxTimer *t, uint32_t new_tcsr) @@ -219,9 +288,9 @@ static void npcm7xx_timer_write_tcsr(NPCM7xxTimer *t, uint32_t new_tcsr) if (npcm7xx_tcsr_prescaler(old_tcsr) != npcm7xx_tcsr_prescaler(new_tcsr)) { /* Recalculate time remaining based on the current TDR value. */ - t->remaining_ns = npcm7xx_timer_count_to_ns(t, tdr); + t->base_timer.remaining_ns = npcm7xx_timer_count_to_ns(t, tdr); if (old_tcsr & t->tcsr & NPCM7XX_TCSR_CEN) { - npcm7xx_timer_start(t); + npcm7xx_timer_start(&t->base_timer); } } @@ -235,10 +304,13 @@ static void npcm7xx_timer_write_tcsr(NPCM7xxTimer *t, uint32_t new_tcsr) if ((old_tcsr ^ new_tcsr) & NPCM7XX_TCSR_CEN) { if (new_tcsr & NPCM7XX_TCSR_CEN) { t->tcsr |= NPCM7XX_TCSR_CACT; - npcm7xx_timer_start(t); + npcm7xx_timer_start(&t->base_timer); } else { t->tcsr &= ~NPCM7XX_TCSR_CACT; - npcm7xx_timer_pause(t); + npcm7xx_timer_pause(&t->base_timer); + if (t->base_timer.remaining_ns <= 0) { + npcm7xx_timer_reached_zero(t); + } } } } @@ -259,9 +331,47 @@ static void npcm7xx_timer_write_tisr(NPCM7xxTimerCtrlState *s, uint32_t value) if (value & (1U << i)) { npcm7xx_timer_check_interrupt(&s->timer[i]); } + } } +static void npcm7xx_timer_write_wtcr(NPCM7xxWatchdogTimer *t, uint32_t new_wtcr) +{ + uint32_t old_wtcr = t->wtcr; + + /* + * WTIF and WTRF are cleared by writing 1. Writing 0 makes these bits + * unchanged. + */ + if (new_wtcr & NPCM7XX_WTCR_WTIF) { + new_wtcr &= ~NPCM7XX_WTCR_WTIF; + } else if (old_wtcr & NPCM7XX_WTCR_WTIF) { + new_wtcr |= NPCM7XX_WTCR_WTIF; + } + if (new_wtcr & NPCM7XX_WTCR_WTRF) { + new_wtcr &= ~NPCM7XX_WTCR_WTRF; + } else if (old_wtcr & NPCM7XX_WTCR_WTRF) { + new_wtcr |= NPCM7XX_WTCR_WTRF; + } + + t->wtcr = new_wtcr; + + if (new_wtcr & NPCM7XX_WTCR_WTR) { + t->wtcr &= ~NPCM7XX_WTCR_WTR; + npcm7xx_watchdog_timer_reset(t); + if (new_wtcr & NPCM7XX_WTCR_WTE) { + npcm7xx_timer_start(&t->base_timer); + } + } else if ((old_wtcr ^ new_wtcr) & NPCM7XX_WTCR_WTE) { + if (new_wtcr & NPCM7XX_WTCR_WTE) { + npcm7xx_timer_start(&t->base_timer); + } else { + npcm7xx_timer_pause(&t->base_timer); + } + } + +} + static hwaddr npcm7xx_tcsr_index(hwaddr reg) { switch (reg) { @@ -353,7 +463,7 @@ static uint64_t npcm7xx_timer_read(void *opaque, hwaddr offset, unsigned size) break; case NPCM7XX_TIMER_WTCR: - value = s->wtcr; + value = s->watchdog_timer.wtcr; break; default: @@ -409,8 +519,7 @@ static void npcm7xx_timer_write(void *opaque, hwaddr offset, return; case NPCM7XX_TIMER_WTCR: - qemu_log_mask(LOG_UNIMP, "%s: WTCR write not implemented: 0x%08x\n", - __func__, value); + npcm7xx_timer_write_wtcr(&s->watchdog_timer, value); return; } @@ -448,15 +557,42 @@ static void npcm7xx_timer_enter_reset(Object *obj, ResetType type) for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) { NPCM7xxTimer *t = &s->timer[i]; - timer_del(&t->qtimer); - t->expires_ns = 0; - t->remaining_ns = 0; + npcm7xx_timer_clear(&t->base_timer); t->tcsr = 0x00000005; t->ticr = 0x00000000; } s->tisr = 0x00000000; - s->wtcr = 0x00000400; + /* + * Set WTCLK to 1(default) and reset all flags except WTRF. + * WTRF is not reset during a core domain reset. + */ + s->watchdog_timer.wtcr = 0x00000400 | (s->watchdog_timer.wtcr & + NPCM7XX_WTCR_WTRF); +} + +static void npcm7xx_watchdog_timer_expired(void *opaque) +{ + NPCM7xxWatchdogTimer *t = opaque; + + if (t->wtcr & NPCM7XX_WTCR_WTE) { + if (t->wtcr & NPCM7XX_WTCR_WTIF) { + if (t->wtcr & NPCM7XX_WTCR_WTRE) { + t->wtcr |= NPCM7XX_WTCR_WTRF; + /* send reset signal to CLK module*/ + qemu_irq_raise(t->reset_signal); + } + } else { + t->wtcr |= NPCM7XX_WTCR_WTIF; + if (t->wtcr & NPCM7XX_WTCR_WTIE) { + /* send interrupt */ + qemu_irq_raise(t->irq); + } + npcm7xx_watchdog_timer_reset_cycles(t, + NPCM7XX_WATCHDOG_INTERRUPT_TO_RESET_CYCLES); + npcm7xx_timer_start(&t->base_timer); + } + } } static void npcm7xx_timer_hold_reset(Object *obj) @@ -467,6 +603,7 @@ static void npcm7xx_timer_hold_reset(Object *obj) for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) { qemu_irq_lower(s->timer[i].irq); } + qemu_irq_lower(s->watchdog_timer.irq); } static void npcm7xx_timer_realize(DeviceState *dev, Error **errp) @@ -474,43 +611,80 @@ static void npcm7xx_timer_realize(DeviceState *dev, Error **errp) NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(dev); SysBusDevice *sbd = &s->parent; int i; + NPCM7xxWatchdogTimer *w; for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) { NPCM7xxTimer *t = &s->timer[i]; t->ctrl = s; - timer_init_ns(&t->qtimer, QEMU_CLOCK_VIRTUAL, npcm7xx_timer_expired, t); + timer_init_ns(&t->base_timer.qtimer, QEMU_CLOCK_VIRTUAL, + npcm7xx_timer_expired, t); sysbus_init_irq(sbd, &t->irq); } + w = &s->watchdog_timer; + w->ctrl = s; + timer_init_ns(&w->base_timer.qtimer, QEMU_CLOCK_VIRTUAL, + npcm7xx_watchdog_timer_expired, w); + sysbus_init_irq(sbd, &w->irq); + memory_region_init_io(&s->iomem, OBJECT(s), &npcm7xx_timer_ops, s, TYPE_NPCM7XX_TIMER, 4 * KiB); sysbus_init_mmio(sbd, &s->iomem); + qdev_init_gpio_out_named(dev, &w->reset_signal, + NPCM7XX_WATCHDOG_RESET_GPIO_OUT, 1); } -static const VMStateDescription vmstate_npcm7xx_timer = { - .name = "npcm7xx-timer", +static const VMStateDescription vmstate_npcm7xx_base_timer = { + .name = "npcm7xx-base-timer", .version_id = 0, .minimum_version_id = 0, .fields = (VMStateField[]) { - VMSTATE_TIMER(qtimer, NPCM7xxTimer), - VMSTATE_INT64(expires_ns, NPCM7xxTimer), - VMSTATE_INT64(remaining_ns, NPCM7xxTimer), + VMSTATE_TIMER(qtimer, NPCM7xxBaseTimer), + VMSTATE_INT64(expires_ns, NPCM7xxBaseTimer), + VMSTATE_INT64(remaining_ns, NPCM7xxBaseTimer), + VMSTATE_END_OF_LIST(), + }, +}; + +static const VMStateDescription vmstate_npcm7xx_timer = { + .name = "npcm7xx-timer", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(base_timer, NPCM7xxTimer, + 0, vmstate_npcm7xx_base_timer, + NPCM7xxBaseTimer), VMSTATE_UINT32(tcsr, NPCM7xxTimer), VMSTATE_UINT32(ticr, NPCM7xxTimer), VMSTATE_END_OF_LIST(), }, }; -static const VMStateDescription vmstate_npcm7xx_timer_ctrl = { - .name = "npcm7xx-timer-ctrl", +static const VMStateDescription vmstate_npcm7xx_watchdog_timer = { + .name = "npcm7xx-watchdog-timer", .version_id = 0, .minimum_version_id = 0, .fields = (VMStateField[]) { + VMSTATE_STRUCT(base_timer, NPCM7xxWatchdogTimer, + 0, vmstate_npcm7xx_base_timer, + NPCM7xxBaseTimer), + VMSTATE_UINT32(wtcr, NPCM7xxWatchdogTimer), + VMSTATE_END_OF_LIST(), + }, +}; + +static const VMStateDescription vmstate_npcm7xx_timer_ctrl = { + .name = "npcm7xx-timer-ctrl", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { VMSTATE_UINT32(tisr, NPCM7xxTimerCtrlState), - VMSTATE_UINT32(wtcr, NPCM7xxTimerCtrlState), VMSTATE_STRUCT_ARRAY(timer, NPCM7xxTimerCtrlState, NPCM7XX_TIMERS_PER_CTRL, 0, vmstate_npcm7xx_timer, NPCM7xxTimer), + VMSTATE_STRUCT(watchdog_timer, NPCM7xxTimerCtrlState, + 0, vmstate_npcm7xx_watchdog_timer, + NPCM7xxWatchdogTimer), VMSTATE_END_OF_LIST(), }, }; diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c index bb0e1c8ee5..58af1a1edb 100644 --- a/hw/timer/sh_timer.c +++ b/hw/timer/sh_timer.c @@ -117,35 +117,55 @@ static void sh_timer_write(void *opaque, hwaddr offset, case 2: freq >>= 6; break; case 3: freq >>= 8; break; case 4: freq >>= 10; break; - case 6: - case 7: if (s->feat & TIMER_FEAT_EXTCLK) break; - default: hw_error("sh_timer_write: Reserved TPSC value\n"); break; + case 6: + case 7: + if (s->feat & TIMER_FEAT_EXTCLK) { + break; + } + /* fallthrough */ + default: + hw_error("sh_timer_write: Reserved TPSC value\n"); } switch ((value & TIMER_TCR_CKEG) >> 3) { - case 0: break; + case 0: + break; case 1: case 2: - case 3: if (s->feat & TIMER_FEAT_EXTCLK) break; - default: hw_error("sh_timer_write: Reserved CKEG value\n"); break; + case 3: + if (s->feat & TIMER_FEAT_EXTCLK) { + break; + } + /* fallthrough */ + default: + hw_error("sh_timer_write: Reserved CKEG value\n"); } switch ((value & TIMER_TCR_ICPE) >> 6) { - case 0: break; + case 0: + break; case 2: - case 3: if (s->feat & TIMER_FEAT_CAPT) break; - default: hw_error("sh_timer_write: Reserved ICPE value\n"); break; + case 3: + if (s->feat & TIMER_FEAT_CAPT) { + break; + } + /* fallthrough */ + default: + hw_error("sh_timer_write: Reserved ICPE value\n"); } - if ((value & TIMER_TCR_UNF) == 0) + if ((value & TIMER_TCR_UNF) == 0) { s->int_level = 0; + } - value &= ~TIMER_TCR_UNF; + value &= ~TIMER_TCR_UNF; - if ((value & TIMER_TCR_ICPF) && (!(s->feat & TIMER_FEAT_CAPT))) + if ((value & TIMER_TCR_ICPF) && (!(s->feat & TIMER_FEAT_CAPT))) { hw_error("sh_timer_write: Reserved ICPF value\n"); + } - value &= ~TIMER_TCR_ICPF; /* capture not supported */ + value &= ~TIMER_TCR_ICPF; /* capture not supported */ - if (value & TIMER_TCR_RESERVED) + if (value & TIMER_TCR_RESERVED) { hw_error("sh_timer_write: Reserved TCR bits set\n"); + } s->tcr = value; ptimer_set_limit(s->timer, s->tcor, 0); ptimer_set_freq(s->timer, freq); @@ -158,8 +178,9 @@ static void sh_timer_write(void *opaque, hwaddr offset, case OFFSET_TCPR: if (s->feat & TIMER_FEAT_CAPT) { s->tcpr = value; - break; - } + break; + } + /* fallthrough */ default: hw_error("sh_timer_write: Bad offset %x\n", (int)offset); } @@ -241,8 +262,9 @@ static uint64_t tmu012_read(void *opaque, hwaddr offset, #endif if (offset >= 0x20) { - if (!(s->feat & TMU012_FEAT_3CHAN)) - hw_error("tmu012_write: Bad channel offset %x\n", (int)offset); + if (!(s->feat & TMU012_FEAT_3CHAN)) { + hw_error("tmu012_write: Bad channel offset %x\n", (int)offset); + } return sh_timer_read(s->timer[2], offset - 0x20); } @@ -272,33 +294,36 @@ static void tmu012_write(void *opaque, hwaddr offset, #endif if (offset >= 0x20) { - if (!(s->feat & TMU012_FEAT_3CHAN)) - hw_error("tmu012_write: Bad channel offset %x\n", (int)offset); + if (!(s->feat & TMU012_FEAT_3CHAN)) { + hw_error("tmu012_write: Bad channel offset %x\n", (int)offset); + } sh_timer_write(s->timer[2], offset - 0x20, value); - return; + return; } if (offset >= 0x14) { sh_timer_write(s->timer[1], offset - 0x14, value); - return; + return; } if (offset >= 0x08) { sh_timer_write(s->timer[0], offset - 0x08, value); - return; + return; } if (offset == 4) { sh_timer_start_stop(s->timer[0], value & (1 << 0)); sh_timer_start_stop(s->timer[1], value & (1 << 1)); - if (s->feat & TMU012_FEAT_3CHAN) + if (s->feat & TMU012_FEAT_3CHAN) { sh_timer_start_stop(s->timer[2], value & (1 << 2)); - else - if (value & (1 << 2)) + } else { + if (value & (1 << 2)) { hw_error("tmu012_write: Bad channel\n"); + } + } - s->tstr = value; - return; + s->tstr = value; + return; } if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) { @@ -314,8 +339,8 @@ static const MemoryRegionOps tmu012_ops = { void tmu012_init(MemoryRegion *sysmem, hwaddr base, int feat, uint32_t freq, - qemu_irq ch0_irq, qemu_irq ch1_irq, - qemu_irq ch2_irq0, qemu_irq ch2_irq1) + qemu_irq ch0_irq, qemu_irq ch1_irq, + qemu_irq ch2_irq0, qemu_irq ch2_irq1) { tmu012_state *s; int timer_feat = (feat & TMU012_FEAT_EXTCLK) ? TIMER_FEAT_EXTCLK : 0; @@ -324,9 +349,10 @@ void tmu012_init(MemoryRegion *sysmem, hwaddr base, s->feat = feat; s->timer[0] = sh_timer_init(freq, timer_feat, ch0_irq); s->timer[1] = sh_timer_init(freq, timer_feat, ch1_irq); - if (feat & TMU012_FEAT_3CHAN) + if (feat & TMU012_FEAT_3CHAN) { s->timer[2] = sh_timer_init(freq, timer_feat | TIMER_FEAT_CAPT, - ch2_irq0); /* ch2_irq1 not supported */ + ch2_irq0); /* ch2_irq1 not supported */ + } memory_region_init_io(&s->iomem, NULL, &tmu012_ops, s, "timer", 0x100000000ULL); diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c index 3730736540..e3758db1b1 100644 --- a/hw/usb/hcd-ehci-sysbus.c +++ b/hw/usb/hcd-ehci-sysbus.c @@ -147,6 +147,24 @@ static const TypeInfo ehci_aw_h3_type_info = { .class_init = ehci_aw_h3_class_init, }; +static void ehci_npcm7xx_class_init(ObjectClass *oc, void *data) +{ + SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); + + sec->capsbase = 0x0; + sec->opregbase = 0x10; + sec->portscbase = 0x44; + sec->portnr = 1; + set_bit(DEVICE_CATEGORY_USB, dc->categories); +} + +static const TypeInfo ehci_npcm7xx_type_info = { + .name = TYPE_NPCM7XX_EHCI, + .parent = TYPE_SYS_BUS_EHCI, + .class_init = ehci_npcm7xx_class_init, +}; + static void ehci_tegra2_class_init(ObjectClass *oc, void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); @@ -269,6 +287,7 @@ static void ehci_sysbus_register_types(void) type_register_static(&ehci_platform_type_info); type_register_static(&ehci_exynos4210_type_info); type_register_static(&ehci_aw_h3_type_info); + type_register_static(&ehci_npcm7xx_type_info); type_register_static(&ehci_tegra2_type_info); type_register_static(&ehci_ppc4xx_type_info); type_register_static(&ehci_fusbh200_type_info); diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index fd122dd4cd..a173707d9b 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -344,6 +344,7 @@ struct EHCIPCIState { #define TYPE_PLATFORM_EHCI "platform-ehci-usb" #define TYPE_EXYNOS4210_EHCI "exynos4210-ehci-usb" #define TYPE_AW_H3_EHCI "aw-h3-ehci-usb" +#define TYPE_NPCM7XX_EHCI "npcm7xx-ehci-usb" #define TYPE_TEGRA2_EHCI "tegra2-ehci-usb" #define TYPE_PPC4xx_EHCI "ppc4xx-ehci-usb" #define TYPE_FUSBH200_EHCI "fusbh200-ehci-usb" diff --git a/hw/watchdog/Kconfig b/hw/watchdog/Kconfig index 293209b291..66e1d029e3 100644 --- a/hw/watchdog/Kconfig +++ b/hw/watchdog/Kconfig @@ -17,3 +17,6 @@ config WDT_DIAG288 config WDT_IMX2 bool + +config WDT_SBSA + bool diff --git a/hw/watchdog/meson.build b/hw/watchdog/meson.build index 9b8725e642..054c403dea 100644 --- a/hw/watchdog/meson.build +++ b/hw/watchdog/meson.build @@ -5,3 +5,4 @@ softmmu_ss.add(when: 'CONFIG_WDT_IB700', if_true: files('wdt_ib700.c')) softmmu_ss.add(when: 'CONFIG_WDT_DIAG288', if_true: files('wdt_diag288.c')) softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('wdt_aspeed.c')) softmmu_ss.add(when: 'CONFIG_WDT_IMX2', if_true: files('wdt_imx2.c')) +softmmu_ss.add(when: 'CONFIG_WDT_SBSA', if_true: files('sbsa_gwdt.c')) diff --git a/hw/watchdog/sbsa_gwdt.c b/hw/watchdog/sbsa_gwdt.c new file mode 100644 index 0000000000..d0998f8489 --- /dev/null +++ b/hw/watchdog/sbsa_gwdt.c @@ -0,0 +1,293 @@ +/* + * Generic watchdog device model for SBSA + * + * The watchdog device has been implemented as revision 1 variant of + * the ARM SBSA specification v6.0 + * (https://developer.arm.com/documentation/den0029/d?lang=en) + * + * Copyright Linaro.org 2020 + * + * Authors: + * Shashi Mallela <shashi.mallela@linaro.org> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at your + * option) any later version. See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "sysemu/reset.h" +#include "sysemu/watchdog.h" +#include "hw/watchdog/sbsa_gwdt.h" +#include "qemu/timer.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" + +static WatchdogTimerModel model = { + .wdt_name = TYPE_WDT_SBSA, + .wdt_description = "SBSA-compliant generic watchdog device", +}; + +static const VMStateDescription vmstate_sbsa_gwdt = { + .name = "sbsa-gwdt", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_TIMER_PTR(timer, SBSA_GWDTState), + VMSTATE_UINT32(wcs, SBSA_GWDTState), + VMSTATE_UINT32(worl, SBSA_GWDTState), + VMSTATE_UINT32(woru, SBSA_GWDTState), + VMSTATE_UINT32(wcvl, SBSA_GWDTState), + VMSTATE_UINT32(wcvu, SBSA_GWDTState), + VMSTATE_END_OF_LIST() + } +}; + +typedef enum WdtRefreshType { + EXPLICIT_REFRESH = 0, + TIMEOUT_REFRESH = 1, +} WdtRefreshType; + +static uint64_t sbsa_gwdt_rread(void *opaque, hwaddr addr, unsigned int size) +{ + SBSA_GWDTState *s = SBSA_GWDT(opaque); + uint32_t ret = 0; + + switch (addr) { + case SBSA_GWDT_WRR: + /* watch refresh read has no effect and returns 0 */ + ret = 0; + break; + case SBSA_GWDT_W_IIDR: + ret = s->id; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "bad address in refresh frame read :" + " 0x%x\n", (int)addr); + } + return ret; +} + +static uint64_t sbsa_gwdt_read(void *opaque, hwaddr addr, unsigned int size) +{ + SBSA_GWDTState *s = SBSA_GWDT(opaque); + uint32_t ret = 0; + + switch (addr) { + case SBSA_GWDT_WCS: + ret = s->wcs; + break; + case SBSA_GWDT_WOR: + ret = s->worl; + break; + case SBSA_GWDT_WORU: + ret = s->woru; + break; + case SBSA_GWDT_WCV: + ret = s->wcvl; + break; + case SBSA_GWDT_WCVU: + ret = s->wcvu; + break; + case SBSA_GWDT_W_IIDR: + ret = s->id; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "bad address in control frame read :" + " 0x%x\n", (int)addr); + } + return ret; +} + +static void sbsa_gwdt_update_timer(SBSA_GWDTState *s, WdtRefreshType rtype) +{ + uint64_t timeout = 0; + + timer_del(s->timer); + + if (s->wcs & SBSA_GWDT_WCS_EN) { + /* + * Extract the upper 16 bits from woru & 32 bits from worl + * registers to construct the 48 bit offset value + */ + timeout = s->woru; + timeout <<= 32; + timeout |= s->worl; + timeout = muldiv64(timeout, NANOSECONDS_PER_SECOND, SBSA_TIMER_FREQ); + timeout += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + if ((rtype == EXPLICIT_REFRESH) || ((rtype == TIMEOUT_REFRESH) && + (!(s->wcs & SBSA_GWDT_WCS_WS0)))) { + /* store the current timeout value into compare registers */ + s->wcvu = timeout >> 32; + s->wcvl = timeout; + } + timer_mod(s->timer, timeout); + } +} + +static void sbsa_gwdt_rwrite(void *opaque, hwaddr offset, uint64_t data, + unsigned size) { + SBSA_GWDTState *s = SBSA_GWDT(opaque); + + if (offset == SBSA_GWDT_WRR) { + s->wcs &= ~(SBSA_GWDT_WCS_WS0 | SBSA_GWDT_WCS_WS1); + + sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "bad address in refresh frame write :" + " 0x%x\n", (int)offset); + } +} + +static void sbsa_gwdt_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) { + SBSA_GWDTState *s = SBSA_GWDT(opaque); + + switch (offset) { + case SBSA_GWDT_WCS: + s->wcs = data & SBSA_GWDT_WCS_EN; + qemu_set_irq(s->irq, 0); + sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH); + break; + + case SBSA_GWDT_WOR: + s->worl = data; + s->wcs &= ~(SBSA_GWDT_WCS_WS0 | SBSA_GWDT_WCS_WS1); + qemu_set_irq(s->irq, 0); + sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH); + break; + + case SBSA_GWDT_WORU: + s->woru = data & SBSA_GWDT_WOR_MASK; + s->wcs &= ~(SBSA_GWDT_WCS_WS0 | SBSA_GWDT_WCS_WS1); + qemu_set_irq(s->irq, 0); + sbsa_gwdt_update_timer(s, EXPLICIT_REFRESH); + break; + + case SBSA_GWDT_WCV: + s->wcvl = data; + break; + + case SBSA_GWDT_WCVU: + s->wcvu = data; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "bad address in control frame write :" + " 0x%x\n", (int)offset); + } + return; +} + +static void wdt_sbsa_gwdt_reset(DeviceState *dev) +{ + SBSA_GWDTState *s = SBSA_GWDT(dev); + + timer_del(s->timer); + + s->wcs = 0; + s->wcvl = 0; + s->wcvu = 0; + s->worl = 0; + s->woru = 0; + s->id = SBSA_GWDT_ID; +} + +static void sbsa_gwdt_timer_sysinterrupt(void *opaque) +{ + SBSA_GWDTState *s = SBSA_GWDT(opaque); + + if (!(s->wcs & SBSA_GWDT_WCS_WS0)) { + s->wcs |= SBSA_GWDT_WCS_WS0; + sbsa_gwdt_update_timer(s, TIMEOUT_REFRESH); + qemu_set_irq(s->irq, 1); + } else { + s->wcs |= SBSA_GWDT_WCS_WS1; + qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n"); + /* + * Reset the watchdog only if the guest gets notified about + * expiry. watchdog_perform_action() may temporarily relinquish + * the BQL; reset before triggering the action to avoid races with + * sbsa_gwdt instructions. + */ + switch (get_watchdog_action()) { + case WATCHDOG_ACTION_DEBUG: + case WATCHDOG_ACTION_NONE: + case WATCHDOG_ACTION_PAUSE: + break; + default: + wdt_sbsa_gwdt_reset(DEVICE(s)); + } + watchdog_perform_action(); + } +} + +static const MemoryRegionOps sbsa_gwdt_rops = { + .read = sbsa_gwdt_rread, + .write = sbsa_gwdt_rwrite, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .valid.unaligned = false, +}; + +static const MemoryRegionOps sbsa_gwdt_ops = { + .read = sbsa_gwdt_read, + .write = sbsa_gwdt_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .valid.unaligned = false, +}; + +static void wdt_sbsa_gwdt_realize(DeviceState *dev, Error **errp) +{ + SBSA_GWDTState *s = SBSA_GWDT(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->rmmio, OBJECT(dev), + &sbsa_gwdt_rops, s, + "sbsa_gwdt.refresh", + SBSA_GWDT_RMMIO_SIZE); + + memory_region_init_io(&s->cmmio, OBJECT(dev), + &sbsa_gwdt_ops, s, + "sbsa_gwdt.control", + SBSA_GWDT_CMMIO_SIZE); + + sysbus_init_mmio(sbd, &s->rmmio); + sysbus_init_mmio(sbd, &s->cmmio); + + sysbus_init_irq(sbd, &s->irq); + + s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sbsa_gwdt_timer_sysinterrupt, + dev); +} + +static void wdt_sbsa_gwdt_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = wdt_sbsa_gwdt_realize; + dc->reset = wdt_sbsa_gwdt_reset; + dc->hotpluggable = false; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->vmsd = &vmstate_sbsa_gwdt; +} + +static const TypeInfo wdt_sbsa_gwdt_info = { + .class_init = wdt_sbsa_gwdt_class_init, + .parent = TYPE_SYS_BUS_DEVICE, + .name = TYPE_WDT_SBSA, + .instance_size = sizeof(SBSA_GWDTState), +}; + +static void wdt_sbsa_gwdt_register_types(void) +{ + watchdog_add_model(&model); + type_register_static(&wdt_sbsa_gwdt_info); +} + +type_init(wdt_sbsa_gwdt_register_types) diff --git a/include/block/block.h b/include/block/block.h index d16c401cb4..4bfe3b546b 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -508,6 +508,8 @@ int bdrv_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes, int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, bool include_base, int64_t offset, int64_t bytes, int64_t *pnum); +int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset, + int64_t bytes); bool bdrv_is_read_only(BlockDriverState *bs); int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, @@ -780,6 +782,12 @@ void bdrv_drained_end(BlockDriverState *bs); void bdrv_drained_end_no_poll(BlockDriverState *bs, int *drained_end_counter); /** + * End all quiescent sections started by bdrv_drain_all_begin(). This is + * only needed when deleting a BDS before bdrv_drain_all_end() is called. + */ +void bdrv_drain_all_end_quiesce(BlockDriverState *bs); + +/** * End a quiescent section started by bdrv_subtree_drained_begin(). */ void bdrv_subtree_drained_end(BlockDriverState *bs); diff --git a/include/elf.h b/include/elf.h index c117a4d1ab..7a418ee559 100644 --- a/include/elf.h +++ b/include/elf.h @@ -26,9 +26,13 @@ typedef int64_t Elf64_Sxword; #define PT_NOTE 4 #define PT_SHLIB 5 #define PT_PHDR 6 +#define PT_LOOS 0x60000000 +#define PT_HIOS 0x6fffffff #define PT_LOPROC 0x70000000 #define PT_HIPROC 0x7fffffff +#define PT_GNU_PROPERTY (PT_LOOS + 0x474e553) + #define PT_MIPS_REGINFO 0x70000000 #define PT_MIPS_RTPROC 0x70000001 #define PT_MIPS_OPTIONS 0x70000002 @@ -172,6 +176,8 @@ typedef struct mips_elf_abiflags_v0 { #define EM_UNICORE32 110 /* UniCore32 */ +#define EM_RX 173 /* Renesas RX family */ + #define EM_RISCV 243 /* RISC-V */ #define EM_NANOMIPS 249 /* Wave Computing nanoMIPS */ @@ -1657,6 +1663,24 @@ typedef struct elf64_shdr { #define NT_ARM_SYSTEM_CALL 0x404 /* ARM system call number */ #define NT_ARM_SVE 0x405 /* ARM Scalable Vector Extension regs */ +/* Defined note types for GNU systems. */ + +#define NT_GNU_PROPERTY_TYPE_0 5 /* Program property */ + +/* Values used in GNU .note.gnu.property notes (NT_GNU_PROPERTY_TYPE_0). */ + +#define GNU_PROPERTY_STACK_SIZE 1 +#define GNU_PROPERTY_NO_COPY_ON_PROTECTED 2 + +#define GNU_PROPERTY_LOPROC 0xc0000000 +#define GNU_PROPERTY_HIPROC 0xdfffffff +#define GNU_PROPERTY_LOUSER 0xe0000000 +#define GNU_PROPERTY_HIUSER 0xffffffff + +#define GNU_PROPERTY_AARCH64_FEATURE_1_AND 0xc0000000 +#define GNU_PROPERTY_AARCH64_FEATURE_1_BTI (1u << 0) +#define GNU_PROPERTY_AARCH64_FEATURE_1_PAC (1u << 1) + /* * Physical entry point into the kernel. * diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 61e13b5038..656a2a8788 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -274,6 +274,8 @@ extern intptr_t qemu_host_page_mask; /* FIXME: Code that sets/uses this is broken and needs to go away. */ #define PAGE_RESERVED 0x0020 #endif +/* Target-specific bits that will be used via page_get_flags(). */ +#define PAGE_TARGET_1 0x0080 #if defined(CONFIG_USER_ONLY) void page_dump(FILE *f); diff --git a/include/exec/memory.h b/include/exec/memory.h index 622207bde1..aff6ef7605 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -42,6 +42,21 @@ typedef struct IOMMUMemoryRegionClass IOMMUMemoryRegionClass; DECLARE_OBJ_CHECKERS(IOMMUMemoryRegion, IOMMUMemoryRegionClass, IOMMU_MEMORY_REGION, TYPE_IOMMU_MEMORY_REGION) +#ifdef CONFIG_FUZZ +void fuzz_dma_read_cb(size_t addr, + size_t len, + MemoryRegion *mr, + bool is_write); +#else +static inline void fuzz_dma_read_cb(size_t addr, + size_t len, + MemoryRegion *mr, + bool is_write) +{ + /* Do Nothing */ +} +#endif + extern bool global_dirty_log; typedef struct MemoryRegionOps MemoryRegionOps; @@ -719,6 +734,11 @@ static inline FlatView *address_space_to_flatview(AddressSpace *as) return qatomic_rcu_read(&as->current_map); } +typedef int (*flatview_cb)(Int128 start, + Int128 len, + const MemoryRegion*, void*); + +void flatview_for_each_range(FlatView *fv, flatview_cb cb , void *opaque); /** * struct MemoryRegionSection: describes a fragment of a #MemoryRegion @@ -2442,6 +2462,7 @@ address_space_read_cached(MemoryRegionCache *cache, hwaddr addr, void *buf, hwaddr len) { assert(addr < cache->len && len <= cache->len - addr); + fuzz_dma_read_cb(cache->xlat + addr, len, cache->mrs.mr, false); if (likely(cache->ptr)) { memcpy(buf, cache->ptr + addr, len); return MEMTX_OK; diff --git a/include/exec/memory_ldst_cached.h.inc b/include/exec/memory_ldst_cached.h.inc index fd4bbb40e7..aff574039f 100644 --- a/include/exec/memory_ldst_cached.h.inc +++ b/include/exec/memory_ldst_cached.h.inc @@ -28,6 +28,7 @@ static inline uint32_t ADDRESS_SPACE_LD_CACHED(l)(MemoryRegionCache *cache, hwaddr addr, MemTxAttrs attrs, MemTxResult *result) { assert(addr < cache->len && 4 <= cache->len - addr); + fuzz_dma_read_cb(cache->xlat + addr, 4, cache->mrs.mr, false); if (likely(cache->ptr)) { return LD_P(l)(cache->ptr + addr); } else { @@ -39,6 +40,7 @@ static inline uint64_t ADDRESS_SPACE_LD_CACHED(q)(MemoryRegionCache *cache, hwaddr addr, MemTxAttrs attrs, MemTxResult *result) { assert(addr < cache->len && 8 <= cache->len - addr); + fuzz_dma_read_cb(cache->xlat + addr, 8, cache->mrs.mr, false); if (likely(cache->ptr)) { return LD_P(q)(cache->ptr + addr); } else { @@ -50,6 +52,7 @@ static inline uint32_t ADDRESS_SPACE_LD_CACHED(uw)(MemoryRegionCache *cache, hwaddr addr, MemTxAttrs attrs, MemTxResult *result) { assert(addr < cache->len && 2 <= cache->len - addr); + fuzz_dma_read_cb(cache->xlat + addr, 2, cache->mrs.mr, false); if (likely(cache->ptr)) { return LD_P(uw)(cache->ptr + addr); } else { diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h index c9ac941a82..479e2346e8 100644 --- a/include/hw/arm/bcm2835_peripherals.h +++ b/include/hw/arm/bcm2835_peripherals.h @@ -23,6 +23,7 @@ #include "hw/misc/bcm2835_mbox.h" #include "hw/misc/bcm2835_mphi.h" #include "hw/misc/bcm2835_thermal.h" +#include "hw/misc/bcm2835_cprman.h" #include "hw/sd/sdhci.h" #include "hw/sd/bcm2835_sdhost.h" #include "hw/gpio/bcm2835_gpio.h" @@ -47,8 +48,8 @@ struct BCM2835PeripheralState { BCM2835MphiState mphi; UnimplementedDeviceState txp; UnimplementedDeviceState armtmr; - UnimplementedDeviceState cprman; - UnimplementedDeviceState a2w; + UnimplementedDeviceState powermgt; + BCM2835CprmanState cprman; PL011State uart0; BCM2835AuxState aux; BCM2835FBState fb; diff --git a/include/hw/arm/bcm2836.h b/include/hw/arm/bcm2836.h index 428c15d316..6f90cabfa3 100644 --- a/include/hw/arm/bcm2836.h +++ b/include/hw/arm/bcm2836.h @@ -26,6 +26,7 @@ OBJECT_DECLARE_TYPE(BCM283XState, BCM283XClass, BCM283X) * them, code using these devices should always handle them via the * BCM283x base class, so they have no BCM2836(obj) etc macros. */ +#define TYPE_BCM2835 "bcm2835" #define TYPE_BCM2836 "bcm2836" #define TYPE_BCM2837 "bcm2837" @@ -43,12 +44,4 @@ struct BCM283XState { BCM2835PeripheralState peripherals; }; -typedef struct BCM283XInfo BCM283XInfo; - -struct BCM283XClass { - DeviceClass parent_class; - const BCM283XInfo *info; -}; - - #endif /* BCM2836_H */ diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h index 13106af215..5469247e38 100644 --- a/include/hw/arm/npcm7xx.h +++ b/include/hw/arm/npcm7xx.h @@ -18,12 +18,16 @@ #include "hw/boards.h" #include "hw/cpu/a9mpcore.h" +#include "hw/gpio/npcm7xx_gpio.h" #include "hw/mem/npcm7xx_mc.h" #include "hw/misc/npcm7xx_clk.h" #include "hw/misc/npcm7xx_gcr.h" +#include "hw/misc/npcm7xx_rng.h" #include "hw/nvram/npcm7xx_otp.h" #include "hw/timer/npcm7xx_timer.h" #include "hw/ssi/npcm7xx_fiu.h" +#include "hw/usb/hcd-ehci.h" +#include "hw/usb/hcd-ohci.h" #include "target/arm/cpu.h" #define NPCM7XX_MAX_NUM_CPUS (2) @@ -75,6 +79,10 @@ typedef struct NPCM7xxState { NPCM7xxOTPState key_storage; NPCM7xxOTPState fuse_array; NPCM7xxMCState mc; + NPCM7xxRNGState rng; + NPCM7xxGPIOState gpio[8]; + EHCISysBusState ehci; + OHCISysBusState ohci; NPCM7xxFIUState fiu[2]; } NPCM7xxState; diff --git a/include/hw/arm/raspi_platform.h b/include/hw/arm/raspi_platform.h index c7f50b260f..e0e6c8ce94 100644 --- a/include/hw/arm/raspi_platform.h +++ b/include/hw/arm/raspi_platform.h @@ -45,9 +45,8 @@ #define ARMCTRL_TIMER0_1_OFFSET (ARM_OFFSET + 0x400) /* Timer 0 and 1 (SP804) */ #define ARMCTRL_0_SBM_OFFSET (ARM_OFFSET + 0x800) /* User 0 (ARM) Semaphores * Doorbells & Mailboxes */ -#define CPRMAN_OFFSET 0x100000 /* Power Management, Watchdog */ -#define CM_OFFSET 0x101000 /* Clock Management */ -#define A2W_OFFSET 0x102000 /* Reset controller */ +#define PM_OFFSET 0x100000 /* Power Management */ +#define CPRMAN_OFFSET 0x101000 /* Clock Management */ #define AVS_OFFSET 0x103000 /* Audio Video Standard */ #define RNG_OFFSET 0x104000 #define GPIO_OFFSET 0x200000 diff --git a/include/hw/boards.h b/include/hw/boards.h index bf53e8a16e..a49e3a6b44 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -268,7 +268,6 @@ struct MachineState { char *firmware; bool iommu; bool suppress_vmdesc; - bool enforce_config_section; bool enable_graphics; char *memory_encryption; char *ram_memdev_id; diff --git a/include/hw/char/pl011.h b/include/hw/char/pl011.h index a91ea50e11..33e5e5317b 100644 --- a/include/hw/char/pl011.h +++ b/include/hw/char/pl011.h @@ -49,6 +49,7 @@ struct PL011State { int read_trigger; CharBackend chr; qemu_irq irq[6]; + Clock *clk; const unsigned char *id; }; diff --git a/include/hw/clock.h b/include/hw/clock.h index cbc5e6ced1..81bcf3e505 100644 --- a/include/hw/clock.h +++ b/include/hw/clock.h @@ -81,6 +81,11 @@ extern const VMStateDescription vmstate_clock; VMSTATE_CLOCK_V(field, state, 0) #define VMSTATE_CLOCK_V(field, state, version) \ VMSTATE_STRUCT_POINTER_V(field, state, version, vmstate_clock, Clock) +#define VMSTATE_ARRAY_CLOCK(field, state, num) \ + VMSTATE_ARRAY_CLOCK_V(field, state, num, 0) +#define VMSTATE_ARRAY_CLOCK_V(field, state, num, version) \ + VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(field, state, num, version, \ + vmstate_clock, Clock) /** * clock_setup_canonical_path: diff --git a/include/hw/gpio/npcm7xx_gpio.h b/include/hw/gpio/npcm7xx_gpio.h new file mode 100644 index 0000000000..b1d771bd77 --- /dev/null +++ b/include/hw/gpio/npcm7xx_gpio.h @@ -0,0 +1,55 @@ +/* + * Nuvoton NPCM7xx General Purpose Input / Output (GPIO) + * + * Copyright 2020 Google LLC + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef NPCM7XX_GPIO_H +#define NPCM7XX_GPIO_H + +#include "exec/memory.h" +#include "hw/sysbus.h" + +/* Number of pins managed by each controller. */ +#define NPCM7XX_GPIO_NR_PINS (32) + +/* + * Number of registers in our device state structure. Don't change this without + * incrementing the version_id in the vmstate. + */ +#define NPCM7XX_GPIO_NR_REGS (0x80 / sizeof(uint32_t)) + +typedef struct NPCM7xxGPIOState { + SysBusDevice parent; + + /* Properties to be defined by the SoC */ + uint32_t reset_pu; + uint32_t reset_pd; + uint32_t reset_osrc; + uint32_t reset_odsc; + + MemoryRegion mmio; + + qemu_irq irq; + qemu_irq output[NPCM7XX_GPIO_NR_PINS]; + + uint32_t pin_level; + uint32_t ext_level; + uint32_t ext_driven; + + uint32_t regs[NPCM7XX_GPIO_NR_REGS]; +} NPCM7xxGPIOState; + +#define TYPE_NPCM7XX_GPIO "npcm7xx-gpio" +#define NPCM7XX_GPIO(obj) \ + OBJECT_CHECK(NPCM7xxGPIOState, (obj), TYPE_NPCM7XX_GPIO) + +#endif /* NPCM7XX_GPIO_H */ diff --git a/hw/intc/sifive_plic.h b/include/hw/intc/sifive_plic.h index b75b1f145d..b75b1f145d 100644 --- a/hw/intc/sifive_plic.h +++ b/include/hw/intc/sifive_plic.h diff --git a/include/hw/mem/pc-dimm.h b/include/hw/mem/pc-dimm.h index aec9527fdd..3d3db82641 100644 --- a/include/hw/mem/pc-dimm.h +++ b/include/hw/mem/pc-dimm.h @@ -72,6 +72,6 @@ struct PCDIMMDeviceClass { void pc_dimm_pre_plug(PCDIMMDevice *dimm, MachineState *machine, const uint64_t *legacy_align, Error **errp); -void pc_dimm_plug(PCDIMMDevice *dimm, MachineState *machine, Error **errp); +void pc_dimm_plug(PCDIMMDevice *dimm, MachineState *machine); void pc_dimm_unplug(PCDIMMDevice *dimm, MachineState *machine); #endif diff --git a/include/hw/misc/bcm2835_cprman.h b/include/hw/misc/bcm2835_cprman.h new file mode 100644 index 0000000000..3df4ceedd2 --- /dev/null +++ b/include/hw/misc/bcm2835_cprman.h @@ -0,0 +1,210 @@ +/* + * BCM2835 CPRMAN clock manager + * + * Copyright (c) 2020 Luc Michel <luc@lmichel.fr> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_MISC_CPRMAN_H +#define HW_MISC_CPRMAN_H + +#include "hw/sysbus.h" +#include "hw/qdev-clock.h" + +#define TYPE_BCM2835_CPRMAN "bcm2835-cprman" + +typedef struct BCM2835CprmanState BCM2835CprmanState; + +DECLARE_INSTANCE_CHECKER(BCM2835CprmanState, CPRMAN, + TYPE_BCM2835_CPRMAN) + +#define CPRMAN_NUM_REGS (0x2000 / sizeof(uint32_t)) + +typedef enum CprmanPll { + CPRMAN_PLLA = 0, + CPRMAN_PLLC, + CPRMAN_PLLD, + CPRMAN_PLLH, + CPRMAN_PLLB, + + CPRMAN_NUM_PLL +} CprmanPll; + +typedef enum CprmanPllChannel { + CPRMAN_PLLA_CHANNEL_DSI0 = 0, + CPRMAN_PLLA_CHANNEL_CORE, + CPRMAN_PLLA_CHANNEL_PER, + CPRMAN_PLLA_CHANNEL_CCP2, + + CPRMAN_PLLC_CHANNEL_CORE2, + CPRMAN_PLLC_CHANNEL_CORE1, + CPRMAN_PLLC_CHANNEL_PER, + CPRMAN_PLLC_CHANNEL_CORE0, + + CPRMAN_PLLD_CHANNEL_DSI0, + CPRMAN_PLLD_CHANNEL_CORE, + CPRMAN_PLLD_CHANNEL_PER, + CPRMAN_PLLD_CHANNEL_DSI1, + + CPRMAN_PLLH_CHANNEL_AUX, + CPRMAN_PLLH_CHANNEL_RCAL, + CPRMAN_PLLH_CHANNEL_PIX, + + CPRMAN_PLLB_CHANNEL_ARM, + + CPRMAN_NUM_PLL_CHANNEL, + + /* Special values used when connecting clock sources to clocks */ + CPRMAN_CLOCK_SRC_NORMAL = -1, + CPRMAN_CLOCK_SRC_FORCE_GROUND = -2, + CPRMAN_CLOCK_SRC_DSI0HSCK = -3, +} CprmanPllChannel; + +typedef enum CprmanClockMux { + CPRMAN_CLOCK_GNRIC, + CPRMAN_CLOCK_VPU, + CPRMAN_CLOCK_SYS, + CPRMAN_CLOCK_PERIA, + CPRMAN_CLOCK_PERII, + CPRMAN_CLOCK_H264, + CPRMAN_CLOCK_ISP, + CPRMAN_CLOCK_V3D, + CPRMAN_CLOCK_CAM0, + CPRMAN_CLOCK_CAM1, + CPRMAN_CLOCK_CCP2, + CPRMAN_CLOCK_DSI0E, + CPRMAN_CLOCK_DSI0P, + CPRMAN_CLOCK_DPI, + CPRMAN_CLOCK_GP0, + CPRMAN_CLOCK_GP1, + CPRMAN_CLOCK_GP2, + CPRMAN_CLOCK_HSM, + CPRMAN_CLOCK_OTP, + CPRMAN_CLOCK_PCM, + CPRMAN_CLOCK_PWM, + CPRMAN_CLOCK_SLIM, + CPRMAN_CLOCK_SMI, + CPRMAN_CLOCK_TEC, + CPRMAN_CLOCK_TD0, + CPRMAN_CLOCK_TD1, + CPRMAN_CLOCK_TSENS, + CPRMAN_CLOCK_TIMER, + CPRMAN_CLOCK_UART, + CPRMAN_CLOCK_VEC, + CPRMAN_CLOCK_PULSE, + CPRMAN_CLOCK_SDC, + CPRMAN_CLOCK_ARM, + CPRMAN_CLOCK_AVEO, + CPRMAN_CLOCK_EMMC, + CPRMAN_CLOCK_EMMC2, + + CPRMAN_NUM_CLOCK_MUX +} CprmanClockMux; + +typedef enum CprmanClockMuxSource { + CPRMAN_CLOCK_SRC_GND = 0, + CPRMAN_CLOCK_SRC_XOSC, + CPRMAN_CLOCK_SRC_TD0, + CPRMAN_CLOCK_SRC_TD1, + CPRMAN_CLOCK_SRC_PLLA, + CPRMAN_CLOCK_SRC_PLLC, + CPRMAN_CLOCK_SRC_PLLD, + CPRMAN_CLOCK_SRC_PLLH, + CPRMAN_CLOCK_SRC_PLLC_CORE1, + CPRMAN_CLOCK_SRC_PLLC_CORE2, + + CPRMAN_NUM_CLOCK_MUX_SRC +} CprmanClockMuxSource; + +typedef struct CprmanPllState { + /*< private >*/ + DeviceState parent_obj; + + /*< public >*/ + CprmanPll id; + + uint32_t *reg_cm; + uint32_t *reg_a2w_ctrl; + uint32_t *reg_a2w_ana; /* ANA[0] .. ANA[3] */ + uint32_t prediv_mask; /* prediv bit in ana[1] */ + uint32_t *reg_a2w_frac; + + Clock *xosc_in; + Clock *out; +} CprmanPllState; + +typedef struct CprmanPllChannelState { + /*< private >*/ + DeviceState parent_obj; + + /*< public >*/ + CprmanPllChannel id; + CprmanPll parent; + + uint32_t *reg_cm; + uint32_t hold_mask; + uint32_t load_mask; + uint32_t *reg_a2w_ctrl; + int fixed_divider; + + Clock *pll_in; + Clock *out; +} CprmanPllChannelState; + +typedef struct CprmanClockMuxState { + /*< private >*/ + DeviceState parent_obj; + + /*< public >*/ + CprmanClockMux id; + + uint32_t *reg_ctl; + uint32_t *reg_div; + int int_bits; + int frac_bits; + + Clock *srcs[CPRMAN_NUM_CLOCK_MUX_SRC]; + Clock *out; + + /* + * Used by clock srcs update callback to retrieve both the clock and the + * source number. + */ + struct CprmanClockMuxState *backref[CPRMAN_NUM_CLOCK_MUX_SRC]; +} CprmanClockMuxState; + +typedef struct CprmanDsi0HsckMuxState { + /*< private >*/ + DeviceState parent_obj; + + /*< public >*/ + CprmanClockMux id; + + uint32_t *reg_cm; + + Clock *plla_in; + Clock *plld_in; + Clock *out; +} CprmanDsi0HsckMuxState; + +struct BCM2835CprmanState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + + CprmanPllState plls[CPRMAN_NUM_PLL]; + CprmanPllChannelState channels[CPRMAN_NUM_PLL_CHANNEL]; + CprmanClockMuxState clock_muxes[CPRMAN_NUM_CLOCK_MUX]; + CprmanDsi0HsckMuxState dsi0hsck_mux; + + uint32_t regs[CPRMAN_NUM_REGS]; + uint32_t xosc_freq; + + Clock *xosc; + Clock *gnd; +}; + +#endif diff --git a/include/hw/misc/bcm2835_cprman_internals.h b/include/hw/misc/bcm2835_cprman_internals.h new file mode 100644 index 0000000000..339759b307 --- /dev/null +++ b/include/hw/misc/bcm2835_cprman_internals.h @@ -0,0 +1,1019 @@ +/* + * BCM2835 CPRMAN clock manager + * + * Copyright (c) 2020 Luc Michel <luc@lmichel.fr> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_MISC_CPRMAN_INTERNALS_H +#define HW_MISC_CPRMAN_INTERNALS_H + +#include "hw/registerfields.h" +#include "hw/misc/bcm2835_cprman.h" + +#define TYPE_CPRMAN_PLL "bcm2835-cprman-pll" +#define TYPE_CPRMAN_PLL_CHANNEL "bcm2835-cprman-pll-channel" +#define TYPE_CPRMAN_CLOCK_MUX "bcm2835-cprman-clock-mux" +#define TYPE_CPRMAN_DSI0HSCK_MUX "bcm2835-cprman-dsi0hsck-mux" + +DECLARE_INSTANCE_CHECKER(CprmanPllState, CPRMAN_PLL, + TYPE_CPRMAN_PLL) +DECLARE_INSTANCE_CHECKER(CprmanPllChannelState, CPRMAN_PLL_CHANNEL, + TYPE_CPRMAN_PLL_CHANNEL) +DECLARE_INSTANCE_CHECKER(CprmanClockMuxState, CPRMAN_CLOCK_MUX, + TYPE_CPRMAN_CLOCK_MUX) +DECLARE_INSTANCE_CHECKER(CprmanDsi0HsckMuxState, CPRMAN_DSI0HSCK_MUX, + TYPE_CPRMAN_DSI0HSCK_MUX) + +/* Register map */ + +/* PLLs */ +REG32(CM_PLLA, 0x104) + FIELD(CM_PLLA, LOADDSI0, 0, 1) + FIELD(CM_PLLA, HOLDDSI0, 1, 1) + FIELD(CM_PLLA, LOADCCP2, 2, 1) + FIELD(CM_PLLA, HOLDCCP2, 3, 1) + FIELD(CM_PLLA, LOADCORE, 4, 1) + FIELD(CM_PLLA, HOLDCORE, 5, 1) + FIELD(CM_PLLA, LOADPER, 6, 1) + FIELD(CM_PLLA, HOLDPER, 7, 1) + FIELD(CM_PLLx, ANARST, 8, 1) +REG32(CM_PLLC, 0x108) + FIELD(CM_PLLC, LOADCORE0, 0, 1) + FIELD(CM_PLLC, HOLDCORE0, 1, 1) + FIELD(CM_PLLC, LOADCORE1, 2, 1) + FIELD(CM_PLLC, HOLDCORE1, 3, 1) + FIELD(CM_PLLC, LOADCORE2, 4, 1) + FIELD(CM_PLLC, HOLDCORE2, 5, 1) + FIELD(CM_PLLC, LOADPER, 6, 1) + FIELD(CM_PLLC, HOLDPER, 7, 1) +REG32(CM_PLLD, 0x10c) + FIELD(CM_PLLD, LOADDSI0, 0, 1) + FIELD(CM_PLLD, HOLDDSI0, 1, 1) + FIELD(CM_PLLD, LOADDSI1, 2, 1) + FIELD(CM_PLLD, HOLDDSI1, 3, 1) + FIELD(CM_PLLD, LOADCORE, 4, 1) + FIELD(CM_PLLD, HOLDCORE, 5, 1) + FIELD(CM_PLLD, LOADPER, 6, 1) + FIELD(CM_PLLD, HOLDPER, 7, 1) +REG32(CM_PLLH, 0x110) + FIELD(CM_PLLH, LOADPIX, 0, 1) + FIELD(CM_PLLH, LOADAUX, 1, 1) + FIELD(CM_PLLH, LOADRCAL, 2, 1) +REG32(CM_PLLB, 0x170) + FIELD(CM_PLLB, LOADARM, 0, 1) + FIELD(CM_PLLB, HOLDARM, 1, 1) + +REG32(A2W_PLLA_CTRL, 0x1100) + FIELD(A2W_PLLx_CTRL, NDIV, 0, 10) + FIELD(A2W_PLLx_CTRL, PDIV, 12, 3) + FIELD(A2W_PLLx_CTRL, PWRDN, 16, 1) + FIELD(A2W_PLLx_CTRL, PRST_DISABLE, 17, 1) +REG32(A2W_PLLC_CTRL, 0x1120) +REG32(A2W_PLLD_CTRL, 0x1140) +REG32(A2W_PLLH_CTRL, 0x1160) +REG32(A2W_PLLB_CTRL, 0x11e0) + +REG32(A2W_PLLA_ANA0, 0x1010) +REG32(A2W_PLLA_ANA1, 0x1014) + FIELD(A2W_PLLx_ANA1, FB_PREDIV, 14, 1) +REG32(A2W_PLLA_ANA2, 0x1018) +REG32(A2W_PLLA_ANA3, 0x101c) + +REG32(A2W_PLLC_ANA0, 0x1030) +REG32(A2W_PLLC_ANA1, 0x1034) +REG32(A2W_PLLC_ANA2, 0x1038) +REG32(A2W_PLLC_ANA3, 0x103c) + +REG32(A2W_PLLD_ANA0, 0x1050) +REG32(A2W_PLLD_ANA1, 0x1054) +REG32(A2W_PLLD_ANA2, 0x1058) +REG32(A2W_PLLD_ANA3, 0x105c) + +REG32(A2W_PLLH_ANA0, 0x1070) +REG32(A2W_PLLH_ANA1, 0x1074) + FIELD(A2W_PLLH_ANA1, FB_PREDIV, 11, 1) +REG32(A2W_PLLH_ANA2, 0x1078) +REG32(A2W_PLLH_ANA3, 0x107c) + +REG32(A2W_PLLB_ANA0, 0x10f0) +REG32(A2W_PLLB_ANA1, 0x10f4) +REG32(A2W_PLLB_ANA2, 0x10f8) +REG32(A2W_PLLB_ANA3, 0x10fc) + +REG32(A2W_PLLA_FRAC, 0x1200) + FIELD(A2W_PLLx_FRAC, FRAC, 0, 20) +REG32(A2W_PLLC_FRAC, 0x1220) +REG32(A2W_PLLD_FRAC, 0x1240) +REG32(A2W_PLLH_FRAC, 0x1260) +REG32(A2W_PLLB_FRAC, 0x12e0) + +/* PLL channels */ +REG32(A2W_PLLA_DSI0, 0x1300) + FIELD(A2W_PLLx_CHANNELy, DIV, 0, 8) + FIELD(A2W_PLLx_CHANNELy, DISABLE, 8, 1) +REG32(A2W_PLLA_CORE, 0x1400) +REG32(A2W_PLLA_PER, 0x1500) +REG32(A2W_PLLA_CCP2, 0x1600) + +REG32(A2W_PLLC_CORE2, 0x1320) +REG32(A2W_PLLC_CORE1, 0x1420) +REG32(A2W_PLLC_PER, 0x1520) +REG32(A2W_PLLC_CORE0, 0x1620) + +REG32(A2W_PLLD_DSI0, 0x1340) +REG32(A2W_PLLD_CORE, 0x1440) +REG32(A2W_PLLD_PER, 0x1540) +REG32(A2W_PLLD_DSI1, 0x1640) + +REG32(A2W_PLLH_AUX, 0x1360) +REG32(A2W_PLLH_RCAL, 0x1460) +REG32(A2W_PLLH_PIX, 0x1560) +REG32(A2W_PLLH_STS, 0x1660) + +REG32(A2W_PLLB_ARM, 0x13e0) + +/* Clock muxes */ +REG32(CM_GNRICCTL, 0x000) + FIELD(CM_CLOCKx_CTL, SRC, 0, 4) + FIELD(CM_CLOCKx_CTL, ENABLE, 4, 1) + FIELD(CM_CLOCKx_CTL, KILL, 5, 1) + FIELD(CM_CLOCKx_CTL, GATE, 6, 1) + FIELD(CM_CLOCKx_CTL, BUSY, 7, 1) + FIELD(CM_CLOCKx_CTL, BUSYD, 8, 1) + FIELD(CM_CLOCKx_CTL, MASH, 9, 2) + FIELD(CM_CLOCKx_CTL, FLIP, 11, 1) +REG32(CM_GNRICDIV, 0x004) + FIELD(CM_CLOCKx_DIV, FRAC, 0, 12) +REG32(CM_VPUCTL, 0x008) +REG32(CM_VPUDIV, 0x00c) +REG32(CM_SYSCTL, 0x010) +REG32(CM_SYSDIV, 0x014) +REG32(CM_PERIACTL, 0x018) +REG32(CM_PERIADIV, 0x01c) +REG32(CM_PERIICTL, 0x020) +REG32(CM_PERIIDIV, 0x024) +REG32(CM_H264CTL, 0x028) +REG32(CM_H264DIV, 0x02c) +REG32(CM_ISPCTL, 0x030) +REG32(CM_ISPDIV, 0x034) +REG32(CM_V3DCTL, 0x038) +REG32(CM_V3DDIV, 0x03c) +REG32(CM_CAM0CTL, 0x040) +REG32(CM_CAM0DIV, 0x044) +REG32(CM_CAM1CTL, 0x048) +REG32(CM_CAM1DIV, 0x04c) +REG32(CM_CCP2CTL, 0x050) +REG32(CM_CCP2DIV, 0x054) +REG32(CM_DSI0ECTL, 0x058) +REG32(CM_DSI0EDIV, 0x05c) +REG32(CM_DSI0PCTL, 0x060) +REG32(CM_DSI0PDIV, 0x064) +REG32(CM_DPICTL, 0x068) +REG32(CM_DPIDIV, 0x06c) +REG32(CM_GP0CTL, 0x070) +REG32(CM_GP0DIV, 0x074) +REG32(CM_GP1CTL, 0x078) +REG32(CM_GP1DIV, 0x07c) +REG32(CM_GP2CTL, 0x080) +REG32(CM_GP2DIV, 0x084) +REG32(CM_HSMCTL, 0x088) +REG32(CM_HSMDIV, 0x08c) +REG32(CM_OTPCTL, 0x090) +REG32(CM_OTPDIV, 0x094) +REG32(CM_PCMCTL, 0x098) +REG32(CM_PCMDIV, 0x09c) +REG32(CM_PWMCTL, 0x0a0) +REG32(CM_PWMDIV, 0x0a4) +REG32(CM_SLIMCTL, 0x0a8) +REG32(CM_SLIMDIV, 0x0ac) +REG32(CM_SMICTL, 0x0b0) +REG32(CM_SMIDIV, 0x0b4) +REG32(CM_TCNTCTL, 0x0c0) +REG32(CM_TCNTCNT, 0x0c4) +REG32(CM_TECCTL, 0x0c8) +REG32(CM_TECDIV, 0x0cc) +REG32(CM_TD0CTL, 0x0d0) +REG32(CM_TD0DIV, 0x0d4) +REG32(CM_TD1CTL, 0x0d8) +REG32(CM_TD1DIV, 0x0dc) +REG32(CM_TSENSCTL, 0x0e0) +REG32(CM_TSENSDIV, 0x0e4) +REG32(CM_TIMERCTL, 0x0e8) +REG32(CM_TIMERDIV, 0x0ec) +REG32(CM_UARTCTL, 0x0f0) +REG32(CM_UARTDIV, 0x0f4) +REG32(CM_VECCTL, 0x0f8) +REG32(CM_VECDIV, 0x0fc) +REG32(CM_PULSECTL, 0x190) +REG32(CM_PULSEDIV, 0x194) +REG32(CM_SDCCTL, 0x1a8) +REG32(CM_SDCDIV, 0x1ac) +REG32(CM_ARMCTL, 0x1b0) +REG32(CM_AVEOCTL, 0x1b8) +REG32(CM_AVEODIV, 0x1bc) +REG32(CM_EMMCCTL, 0x1c0) +REG32(CM_EMMCDIV, 0x1c4) +REG32(CM_EMMC2CTL, 0x1d0) +REG32(CM_EMMC2DIV, 0x1d4) + +/* misc registers */ +REG32(CM_LOCK, 0x114) + FIELD(CM_LOCK, FLOCKH, 12, 1) + FIELD(CM_LOCK, FLOCKD, 11, 1) + FIELD(CM_LOCK, FLOCKC, 10, 1) + FIELD(CM_LOCK, FLOCKB, 9, 1) + FIELD(CM_LOCK, FLOCKA, 8, 1) + +REG32(CM_DSI0HSCK, 0x120) + FIELD(CM_DSI0HSCK, SELPLLD, 0, 1) + +/* + * This field is common to all registers. Each register write value must match + * the CPRMAN_PASSWORD magic value in its 8 MSB. + */ +FIELD(CPRMAN, PASSWORD, 24, 8) +#define CPRMAN_PASSWORD 0x5a + +/* PLL init info */ +typedef struct PLLInitInfo { + const char *name; + size_t cm_offset; + size_t a2w_ctrl_offset; + size_t a2w_ana_offset; + uint32_t prediv_mask; /* Prediv bit in ana[1] */ + size_t a2w_frac_offset; +} PLLInitInfo; + +#define FILL_PLL_INIT_INFO(pll_) \ + .cm_offset = R_CM_ ## pll_, \ + .a2w_ctrl_offset = R_A2W_ ## pll_ ## _CTRL, \ + .a2w_ana_offset = R_A2W_ ## pll_ ## _ANA0, \ + .a2w_frac_offset = R_A2W_ ## pll_ ## _FRAC + +static const PLLInitInfo PLL_INIT_INFO[] = { + [CPRMAN_PLLA] = { + .name = "plla", + .prediv_mask = R_A2W_PLLx_ANA1_FB_PREDIV_MASK, + FILL_PLL_INIT_INFO(PLLA), + }, + [CPRMAN_PLLC] = { + .name = "pllc", + .prediv_mask = R_A2W_PLLx_ANA1_FB_PREDIV_MASK, + FILL_PLL_INIT_INFO(PLLC), + }, + [CPRMAN_PLLD] = { + .name = "plld", + .prediv_mask = R_A2W_PLLx_ANA1_FB_PREDIV_MASK, + FILL_PLL_INIT_INFO(PLLD), + }, + [CPRMAN_PLLH] = { + .name = "pllh", + .prediv_mask = R_A2W_PLLH_ANA1_FB_PREDIV_MASK, + FILL_PLL_INIT_INFO(PLLH), + }, + [CPRMAN_PLLB] = { + .name = "pllb", + .prediv_mask = R_A2W_PLLx_ANA1_FB_PREDIV_MASK, + FILL_PLL_INIT_INFO(PLLB), + }, +}; + +#undef FILL_PLL_CHANNEL_INIT_INFO + +static inline void set_pll_init_info(BCM2835CprmanState *s, + CprmanPllState *pll, + CprmanPll id) +{ + pll->id = id; + pll->reg_cm = &s->regs[PLL_INIT_INFO[id].cm_offset]; + pll->reg_a2w_ctrl = &s->regs[PLL_INIT_INFO[id].a2w_ctrl_offset]; + pll->reg_a2w_ana = &s->regs[PLL_INIT_INFO[id].a2w_ana_offset]; + pll->prediv_mask = PLL_INIT_INFO[id].prediv_mask; + pll->reg_a2w_frac = &s->regs[PLL_INIT_INFO[id].a2w_frac_offset]; +} + + +/* PLL channel init info */ +typedef struct PLLChannelInitInfo { + const char *name; + CprmanPll parent; + size_t cm_offset; + uint32_t cm_hold_mask; + uint32_t cm_load_mask; + size_t a2w_ctrl_offset; + unsigned int fixed_divider; +} PLLChannelInitInfo; + +#define FILL_PLL_CHANNEL_INIT_INFO_common(pll_, channel_) \ + .parent = CPRMAN_ ## pll_, \ + .cm_offset = R_CM_ ## pll_, \ + .cm_load_mask = R_CM_ ## pll_ ## _ ## LOAD ## channel_ ## _MASK, \ + .a2w_ctrl_offset = R_A2W_ ## pll_ ## _ ## channel_ + +#define FILL_PLL_CHANNEL_INIT_INFO(pll_, channel_) \ + FILL_PLL_CHANNEL_INIT_INFO_common(pll_, channel_), \ + .cm_hold_mask = R_CM_ ## pll_ ## _ ## HOLD ## channel_ ## _MASK, \ + .fixed_divider = 1 + +#define FILL_PLL_CHANNEL_INIT_INFO_nohold(pll_, channel_) \ + FILL_PLL_CHANNEL_INIT_INFO_common(pll_, channel_), \ + .cm_hold_mask = 0 + +static PLLChannelInitInfo PLL_CHANNEL_INIT_INFO[] = { + [CPRMAN_PLLA_CHANNEL_DSI0] = { + .name = "plla-dsi0", + FILL_PLL_CHANNEL_INIT_INFO(PLLA, DSI0), + }, + [CPRMAN_PLLA_CHANNEL_CORE] = { + .name = "plla-core", + FILL_PLL_CHANNEL_INIT_INFO(PLLA, CORE), + }, + [CPRMAN_PLLA_CHANNEL_PER] = { + .name = "plla-per", + FILL_PLL_CHANNEL_INIT_INFO(PLLA, PER), + }, + [CPRMAN_PLLA_CHANNEL_CCP2] = { + .name = "plla-ccp2", + FILL_PLL_CHANNEL_INIT_INFO(PLLA, CCP2), + }, + + [CPRMAN_PLLC_CHANNEL_CORE2] = { + .name = "pllc-core2", + FILL_PLL_CHANNEL_INIT_INFO(PLLC, CORE2), + }, + [CPRMAN_PLLC_CHANNEL_CORE1] = { + .name = "pllc-core1", + FILL_PLL_CHANNEL_INIT_INFO(PLLC, CORE1), + }, + [CPRMAN_PLLC_CHANNEL_PER] = { + .name = "pllc-per", + FILL_PLL_CHANNEL_INIT_INFO(PLLC, PER), + }, + [CPRMAN_PLLC_CHANNEL_CORE0] = { + .name = "pllc-core0", + FILL_PLL_CHANNEL_INIT_INFO(PLLC, CORE0), + }, + + [CPRMAN_PLLD_CHANNEL_DSI0] = { + .name = "plld-dsi0", + FILL_PLL_CHANNEL_INIT_INFO(PLLD, DSI0), + }, + [CPRMAN_PLLD_CHANNEL_CORE] = { + .name = "plld-core", + FILL_PLL_CHANNEL_INIT_INFO(PLLD, CORE), + }, + [CPRMAN_PLLD_CHANNEL_PER] = { + .name = "plld-per", + FILL_PLL_CHANNEL_INIT_INFO(PLLD, PER), + }, + [CPRMAN_PLLD_CHANNEL_DSI1] = { + .name = "plld-dsi1", + FILL_PLL_CHANNEL_INIT_INFO(PLLD, DSI1), + }, + + [CPRMAN_PLLH_CHANNEL_AUX] = { + .name = "pllh-aux", + .fixed_divider = 1, + FILL_PLL_CHANNEL_INIT_INFO_nohold(PLLH, AUX), + }, + [CPRMAN_PLLH_CHANNEL_RCAL] = { + .name = "pllh-rcal", + .fixed_divider = 10, + FILL_PLL_CHANNEL_INIT_INFO_nohold(PLLH, RCAL), + }, + [CPRMAN_PLLH_CHANNEL_PIX] = { + .name = "pllh-pix", + .fixed_divider = 10, + FILL_PLL_CHANNEL_INIT_INFO_nohold(PLLH, PIX), + }, + + [CPRMAN_PLLB_CHANNEL_ARM] = { + .name = "pllb-arm", + FILL_PLL_CHANNEL_INIT_INFO(PLLB, ARM), + }, +}; + +#undef FILL_PLL_CHANNEL_INIT_INFO_nohold +#undef FILL_PLL_CHANNEL_INIT_INFO +#undef FILL_PLL_CHANNEL_INIT_INFO_common + +static inline void set_pll_channel_init_info(BCM2835CprmanState *s, + CprmanPllChannelState *channel, + CprmanPllChannel id) +{ + channel->id = id; + channel->parent = PLL_CHANNEL_INIT_INFO[id].parent; + channel->reg_cm = &s->regs[PLL_CHANNEL_INIT_INFO[id].cm_offset]; + channel->hold_mask = PLL_CHANNEL_INIT_INFO[id].cm_hold_mask; + channel->load_mask = PLL_CHANNEL_INIT_INFO[id].cm_load_mask; + channel->reg_a2w_ctrl = &s->regs[PLL_CHANNEL_INIT_INFO[id].a2w_ctrl_offset]; + channel->fixed_divider = PLL_CHANNEL_INIT_INFO[id].fixed_divider; +} + +/* Clock mux init info */ +typedef struct ClockMuxInitInfo { + const char *name; + size_t cm_offset; /* cm_offset[0]->CM_CTL, cm_offset[1]->CM_DIV */ + int int_bits; + int frac_bits; + + CprmanPllChannel src_mapping[CPRMAN_NUM_CLOCK_MUX_SRC]; +} ClockMuxInitInfo; + +/* + * Each clock mux can have up to 10 sources. Sources 0 to 3 are always the + * same (ground, xosc, td0, td1). Sources 4 to 9 are mux specific, and are not + * always populated. The following macros catch all those cases. + */ + +/* Unknown mapping. Connect everything to ground */ +#define SRC_MAPPING_INFO_unknown \ + .src_mapping = { \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, /* gnd */ \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, /* xosc */ \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, /* test debug 0 */ \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, /* test debug 1 */ \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, /* pll a */ \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, /* pll c */ \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, /* pll d */ \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, /* pll h */ \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, /* pll c, core1 */ \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, /* pll c, core2 */ \ + } + +/* Only the oscillator and the two test debug clocks */ +#define SRC_MAPPING_INFO_xosc \ + .src_mapping = { \ + CPRMAN_CLOCK_SRC_NORMAL, \ + CPRMAN_CLOCK_SRC_NORMAL, \ + CPRMAN_CLOCK_SRC_NORMAL, \ + CPRMAN_CLOCK_SRC_NORMAL, \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, \ + } + +/* All the PLL "core" channels */ +#define SRC_MAPPING_INFO_core \ + .src_mapping = { \ + CPRMAN_CLOCK_SRC_NORMAL, \ + CPRMAN_CLOCK_SRC_NORMAL, \ + CPRMAN_CLOCK_SRC_NORMAL, \ + CPRMAN_CLOCK_SRC_NORMAL, \ + CPRMAN_PLLA_CHANNEL_CORE, \ + CPRMAN_PLLC_CHANNEL_CORE0, \ + CPRMAN_PLLD_CHANNEL_CORE, \ + CPRMAN_PLLH_CHANNEL_AUX, \ + CPRMAN_PLLC_CHANNEL_CORE1, \ + CPRMAN_PLLC_CHANNEL_CORE2, \ + } + +/* All the PLL "per" channels */ +#define SRC_MAPPING_INFO_periph \ + .src_mapping = { \ + CPRMAN_CLOCK_SRC_NORMAL, \ + CPRMAN_CLOCK_SRC_NORMAL, \ + CPRMAN_CLOCK_SRC_NORMAL, \ + CPRMAN_CLOCK_SRC_NORMAL, \ + CPRMAN_PLLA_CHANNEL_PER, \ + CPRMAN_PLLC_CHANNEL_PER, \ + CPRMAN_PLLD_CHANNEL_PER, \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, \ + } + +/* + * The DSI0 channels. This one got an intermediate mux between the PLL channels + * and the clock input. + */ +#define SRC_MAPPING_INFO_dsi0 \ + .src_mapping = { \ + CPRMAN_CLOCK_SRC_NORMAL, \ + CPRMAN_CLOCK_SRC_NORMAL, \ + CPRMAN_CLOCK_SRC_NORMAL, \ + CPRMAN_CLOCK_SRC_NORMAL, \ + CPRMAN_CLOCK_SRC_DSI0HSCK, \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, \ + } + +/* The DSI1 channel */ +#define SRC_MAPPING_INFO_dsi1 \ + .src_mapping = { \ + CPRMAN_CLOCK_SRC_NORMAL, \ + CPRMAN_CLOCK_SRC_NORMAL, \ + CPRMAN_CLOCK_SRC_NORMAL, \ + CPRMAN_CLOCK_SRC_NORMAL, \ + CPRMAN_PLLD_CHANNEL_DSI1, \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, \ + CPRMAN_CLOCK_SRC_FORCE_GROUND, \ + } + +#define FILL_CLOCK_MUX_SRC_MAPPING_INIT_INFO(kind_) \ + SRC_MAPPING_INFO_ ## kind_ + +#define FILL_CLOCK_MUX_INIT_INFO(clock_, kind_) \ + .cm_offset = R_CM_ ## clock_ ## CTL, \ + FILL_CLOCK_MUX_SRC_MAPPING_INIT_INFO(kind_) + +static ClockMuxInitInfo CLOCK_MUX_INIT_INFO[] = { + [CPRMAN_CLOCK_GNRIC] = { + .name = "gnric", + FILL_CLOCK_MUX_INIT_INFO(GNRIC, unknown), + }, + [CPRMAN_CLOCK_VPU] = { + .name = "vpu", + .int_bits = 12, + .frac_bits = 8, + FILL_CLOCK_MUX_INIT_INFO(VPU, core), + }, + [CPRMAN_CLOCK_SYS] = { + .name = "sys", + FILL_CLOCK_MUX_INIT_INFO(SYS, unknown), + }, + [CPRMAN_CLOCK_PERIA] = { + .name = "peria", + FILL_CLOCK_MUX_INIT_INFO(PERIA, unknown), + }, + [CPRMAN_CLOCK_PERII] = { + .name = "perii", + FILL_CLOCK_MUX_INIT_INFO(PERII, unknown), + }, + [CPRMAN_CLOCK_H264] = { + .name = "h264", + .int_bits = 4, + .frac_bits = 8, + FILL_CLOCK_MUX_INIT_INFO(H264, core), + }, + [CPRMAN_CLOCK_ISP] = { + .name = "isp", + .int_bits = 4, + .frac_bits = 8, + FILL_CLOCK_MUX_INIT_INFO(ISP, core), + }, + [CPRMAN_CLOCK_V3D] = { + .name = "v3d", + FILL_CLOCK_MUX_INIT_INFO(V3D, core), + }, + [CPRMAN_CLOCK_CAM0] = { + .name = "cam0", + .int_bits = 4, + .frac_bits = 8, + FILL_CLOCK_MUX_INIT_INFO(CAM0, periph), + }, + [CPRMAN_CLOCK_CAM1] = { + .name = "cam1", + .int_bits = 4, + .frac_bits = 8, + FILL_CLOCK_MUX_INIT_INFO(CAM1, periph), + }, + [CPRMAN_CLOCK_CCP2] = { + .name = "ccp2", + FILL_CLOCK_MUX_INIT_INFO(CCP2, unknown), + }, + [CPRMAN_CLOCK_DSI0E] = { + .name = "dsi0e", + .int_bits = 4, + .frac_bits = 8, + FILL_CLOCK_MUX_INIT_INFO(DSI0E, dsi0), + }, + [CPRMAN_CLOCK_DSI0P] = { + .name = "dsi0p", + .int_bits = 0, + .frac_bits = 0, + FILL_CLOCK_MUX_INIT_INFO(DSI0P, dsi0), + }, + [CPRMAN_CLOCK_DPI] = { + .name = "dpi", + .int_bits = 4, + .frac_bits = 8, + FILL_CLOCK_MUX_INIT_INFO(DPI, periph), + }, + [CPRMAN_CLOCK_GP0] = { + .name = "gp0", + .int_bits = 12, + .frac_bits = 12, + FILL_CLOCK_MUX_INIT_INFO(GP0, periph), + }, + [CPRMAN_CLOCK_GP1] = { + .name = "gp1", + .int_bits = 12, + .frac_bits = 12, + FILL_CLOCK_MUX_INIT_INFO(GP1, periph), + }, + [CPRMAN_CLOCK_GP2] = { + .name = "gp2", + .int_bits = 12, + .frac_bits = 12, + FILL_CLOCK_MUX_INIT_INFO(GP2, periph), + }, + [CPRMAN_CLOCK_HSM] = { + .name = "hsm", + .int_bits = 4, + .frac_bits = 8, + FILL_CLOCK_MUX_INIT_INFO(HSM, periph), + }, + [CPRMAN_CLOCK_OTP] = { + .name = "otp", + .int_bits = 4, + .frac_bits = 0, + FILL_CLOCK_MUX_INIT_INFO(OTP, xosc), + }, + [CPRMAN_CLOCK_PCM] = { + .name = "pcm", + .int_bits = 12, + .frac_bits = 12, + FILL_CLOCK_MUX_INIT_INFO(PCM, periph), + }, + [CPRMAN_CLOCK_PWM] = { + .name = "pwm", + .int_bits = 12, + .frac_bits = 12, + FILL_CLOCK_MUX_INIT_INFO(PWM, periph), + }, + [CPRMAN_CLOCK_SLIM] = { + .name = "slim", + .int_bits = 12, + .frac_bits = 12, + FILL_CLOCK_MUX_INIT_INFO(SLIM, periph), + }, + [CPRMAN_CLOCK_SMI] = { + .name = "smi", + .int_bits = 4, + .frac_bits = 8, + FILL_CLOCK_MUX_INIT_INFO(SMI, periph), + }, + [CPRMAN_CLOCK_TEC] = { + .name = "tec", + .int_bits = 6, + .frac_bits = 0, + FILL_CLOCK_MUX_INIT_INFO(TEC, xosc), + }, + [CPRMAN_CLOCK_TD0] = { + .name = "td0", + FILL_CLOCK_MUX_INIT_INFO(TD0, unknown), + }, + [CPRMAN_CLOCK_TD1] = { + .name = "td1", + FILL_CLOCK_MUX_INIT_INFO(TD1, unknown), + }, + [CPRMAN_CLOCK_TSENS] = { + .name = "tsens", + .int_bits = 5, + .frac_bits = 0, + FILL_CLOCK_MUX_INIT_INFO(TSENS, xosc), + }, + [CPRMAN_CLOCK_TIMER] = { + .name = "timer", + .int_bits = 6, + .frac_bits = 12, + FILL_CLOCK_MUX_INIT_INFO(TIMER, xosc), + }, + [CPRMAN_CLOCK_UART] = { + .name = "uart", + .int_bits = 10, + .frac_bits = 12, + FILL_CLOCK_MUX_INIT_INFO(UART, periph), + }, + [CPRMAN_CLOCK_VEC] = { + .name = "vec", + .int_bits = 4, + .frac_bits = 0, + FILL_CLOCK_MUX_INIT_INFO(VEC, periph), + }, + [CPRMAN_CLOCK_PULSE] = { + .name = "pulse", + FILL_CLOCK_MUX_INIT_INFO(PULSE, xosc), + }, + [CPRMAN_CLOCK_SDC] = { + .name = "sdram", + .int_bits = 6, + .frac_bits = 0, + FILL_CLOCK_MUX_INIT_INFO(SDC, core), + }, + [CPRMAN_CLOCK_ARM] = { + .name = "arm", + FILL_CLOCK_MUX_INIT_INFO(ARM, unknown), + }, + [CPRMAN_CLOCK_AVEO] = { + .name = "aveo", + .int_bits = 4, + .frac_bits = 0, + FILL_CLOCK_MUX_INIT_INFO(AVEO, periph), + }, + [CPRMAN_CLOCK_EMMC] = { + .name = "emmc", + .int_bits = 4, + .frac_bits = 8, + FILL_CLOCK_MUX_INIT_INFO(EMMC, periph), + }, + [CPRMAN_CLOCK_EMMC2] = { + .name = "emmc2", + .int_bits = 4, + .frac_bits = 8, + FILL_CLOCK_MUX_INIT_INFO(EMMC2, unknown), + }, +}; + +#undef FILL_CLOCK_MUX_INIT_INFO +#undef FILL_CLOCK_MUX_SRC_MAPPING_INIT_INFO +#undef SRC_MAPPING_INFO_dsi1 +#undef SRC_MAPPING_INFO_dsi0 +#undef SRC_MAPPING_INFO_periph +#undef SRC_MAPPING_INFO_core +#undef SRC_MAPPING_INFO_xosc +#undef SRC_MAPPING_INFO_unknown + +static inline void set_clock_mux_init_info(BCM2835CprmanState *s, + CprmanClockMuxState *mux, + CprmanClockMux id) +{ + mux->id = id; + mux->reg_ctl = &s->regs[CLOCK_MUX_INIT_INFO[id].cm_offset]; + mux->reg_div = &s->regs[CLOCK_MUX_INIT_INFO[id].cm_offset + 1]; + mux->int_bits = CLOCK_MUX_INIT_INFO[id].int_bits; + mux->frac_bits = CLOCK_MUX_INIT_INFO[id].frac_bits; +} + + +/* + * Object reset info + * Those values have been dumped from a Raspberry Pi 3 Model B v1.2 using the + * clk debugfs interface in Linux. + */ +typedef struct PLLResetInfo { + uint32_t cm; + uint32_t a2w_ctrl; + uint32_t a2w_ana[4]; + uint32_t a2w_frac; +} PLLResetInfo; + +static const PLLResetInfo PLL_RESET_INFO[] = { + [CPRMAN_PLLA] = { + .cm = 0x0000008a, + .a2w_ctrl = 0x0002103a, + .a2w_frac = 0x00098000, + .a2w_ana = { 0x00000000, 0x00144000, 0x00000000, 0x00000100 } + }, + + [CPRMAN_PLLC] = { + .cm = 0x00000228, + .a2w_ctrl = 0x0002103e, + .a2w_frac = 0x00080000, + .a2w_ana = { 0x00000000, 0x00144000, 0x00000000, 0x00000100 } + }, + + [CPRMAN_PLLD] = { + .cm = 0x0000020a, + .a2w_ctrl = 0x00021034, + .a2w_frac = 0x00015556, + .a2w_ana = { 0x00000000, 0x00144000, 0x00000000, 0x00000100 } + }, + + [CPRMAN_PLLH] = { + .cm = 0x00000000, + .a2w_ctrl = 0x0002102d, + .a2w_frac = 0x00000000, + .a2w_ana = { 0x00900000, 0x0000000c, 0x00000000, 0x00000000 } + }, + + [CPRMAN_PLLB] = { + /* unknown */ + .cm = 0x00000000, + .a2w_ctrl = 0x00000000, + .a2w_frac = 0x00000000, + .a2w_ana = { 0x00000000, 0x00000000, 0x00000000, 0x00000000 } + } +}; + +typedef struct PLLChannelResetInfo { + /* + * Even though a PLL channel has a CM register, it shares it with its + * parent PLL. The parent already takes care of the reset value. + */ + uint32_t a2w_ctrl; +} PLLChannelResetInfo; + +static const PLLChannelResetInfo PLL_CHANNEL_RESET_INFO[] = { + [CPRMAN_PLLA_CHANNEL_DSI0] = { .a2w_ctrl = 0x00000100 }, + [CPRMAN_PLLA_CHANNEL_CORE] = { .a2w_ctrl = 0x00000003 }, + [CPRMAN_PLLA_CHANNEL_PER] = { .a2w_ctrl = 0x00000000 }, /* unknown */ + [CPRMAN_PLLA_CHANNEL_CCP2] = { .a2w_ctrl = 0x00000100 }, + + [CPRMAN_PLLC_CHANNEL_CORE2] = { .a2w_ctrl = 0x00000100 }, + [CPRMAN_PLLC_CHANNEL_CORE1] = { .a2w_ctrl = 0x00000100 }, + [CPRMAN_PLLC_CHANNEL_PER] = { .a2w_ctrl = 0x00000002 }, + [CPRMAN_PLLC_CHANNEL_CORE0] = { .a2w_ctrl = 0x00000002 }, + + [CPRMAN_PLLD_CHANNEL_DSI0] = { .a2w_ctrl = 0x00000100 }, + [CPRMAN_PLLD_CHANNEL_CORE] = { .a2w_ctrl = 0x00000004 }, + [CPRMAN_PLLD_CHANNEL_PER] = { .a2w_ctrl = 0x00000004 }, + [CPRMAN_PLLD_CHANNEL_DSI1] = { .a2w_ctrl = 0x00000100 }, + + [CPRMAN_PLLH_CHANNEL_AUX] = { .a2w_ctrl = 0x00000004 }, + [CPRMAN_PLLH_CHANNEL_RCAL] = { .a2w_ctrl = 0x00000000 }, + [CPRMAN_PLLH_CHANNEL_PIX] = { .a2w_ctrl = 0x00000000 }, + + [CPRMAN_PLLB_CHANNEL_ARM] = { .a2w_ctrl = 0x00000000 }, /* unknown */ +}; + +typedef struct ClockMuxResetInfo { + uint32_t cm_ctl; + uint32_t cm_div; +} ClockMuxResetInfo; + +static const ClockMuxResetInfo CLOCK_MUX_RESET_INFO[] = { + [CPRMAN_CLOCK_GNRIC] = { + .cm_ctl = 0, /* unknown */ + .cm_div = 0 + }, + + [CPRMAN_CLOCK_VPU] = { + .cm_ctl = 0x00000245, + .cm_div = 0x00003000, + }, + + [CPRMAN_CLOCK_SYS] = { + .cm_ctl = 0, /* unknown */ + .cm_div = 0 + }, + + [CPRMAN_CLOCK_PERIA] = { + .cm_ctl = 0, /* unknown */ + .cm_div = 0 + }, + + [CPRMAN_CLOCK_PERII] = { + .cm_ctl = 0, /* unknown */ + .cm_div = 0 + }, + + [CPRMAN_CLOCK_H264] = { + .cm_ctl = 0x00000244, + .cm_div = 0x00003000, + }, + + [CPRMAN_CLOCK_ISP] = { + .cm_ctl = 0x00000244, + .cm_div = 0x00003000, + }, + + [CPRMAN_CLOCK_V3D] = { + .cm_ctl = 0, /* unknown */ + .cm_div = 0 + }, + + [CPRMAN_CLOCK_CAM0] = { + .cm_ctl = 0x00000000, + .cm_div = 0x00000000, + }, + + [CPRMAN_CLOCK_CAM1] = { + .cm_ctl = 0x00000000, + .cm_div = 0x00000000, + }, + + [CPRMAN_CLOCK_CCP2] = { + .cm_ctl = 0, /* unknown */ + .cm_div = 0 + }, + + [CPRMAN_CLOCK_DSI0E] = { + .cm_ctl = 0x00000000, + .cm_div = 0x00000000, + }, + + [CPRMAN_CLOCK_DSI0P] = { + .cm_ctl = 0x00000000, + .cm_div = 0x00000000, + }, + + [CPRMAN_CLOCK_DPI] = { + .cm_ctl = 0x00000000, + .cm_div = 0x00000000, + }, + + [CPRMAN_CLOCK_GP0] = { + .cm_ctl = 0x00000200, + .cm_div = 0x00000000, + }, + + [CPRMAN_CLOCK_GP1] = { + .cm_ctl = 0x00000096, + .cm_div = 0x00014000, + }, + + [CPRMAN_CLOCK_GP2] = { + .cm_ctl = 0x00000291, + .cm_div = 0x00249f00, + }, + + [CPRMAN_CLOCK_HSM] = { + .cm_ctl = 0x00000000, + .cm_div = 0x00000000, + }, + + [CPRMAN_CLOCK_OTP] = { + .cm_ctl = 0x00000091, + .cm_div = 0x00004000, + }, + + [CPRMAN_CLOCK_PCM] = { + .cm_ctl = 0x00000200, + .cm_div = 0x00000000, + }, + + [CPRMAN_CLOCK_PWM] = { + .cm_ctl = 0x00000200, + .cm_div = 0x00000000, + }, + + [CPRMAN_CLOCK_SLIM] = { + .cm_ctl = 0x00000200, + .cm_div = 0x00000000, + }, + + [CPRMAN_CLOCK_SMI] = { + .cm_ctl = 0x00000000, + .cm_div = 0x00000000, + }, + + [CPRMAN_CLOCK_TEC] = { + .cm_ctl = 0x00000000, + .cm_div = 0x00000000, + }, + + [CPRMAN_CLOCK_TD0] = { + .cm_ctl = 0, /* unknown */ + .cm_div = 0 + }, + + [CPRMAN_CLOCK_TD1] = { + .cm_ctl = 0, /* unknown */ + .cm_div = 0 + }, + + [CPRMAN_CLOCK_TSENS] = { + .cm_ctl = 0x00000091, + .cm_div = 0x0000a000, + }, + + [CPRMAN_CLOCK_TIMER] = { + .cm_ctl = 0x00000291, + .cm_div = 0x00013333, + }, + + [CPRMAN_CLOCK_UART] = { + .cm_ctl = 0x00000296, + .cm_div = 0x0000a6ab, + }, + + [CPRMAN_CLOCK_VEC] = { + .cm_ctl = 0x00000097, + .cm_div = 0x00002000, + }, + + [CPRMAN_CLOCK_PULSE] = { + .cm_ctl = 0, /* unknown */ + .cm_div = 0 + }, + + [CPRMAN_CLOCK_SDC] = { + .cm_ctl = 0x00004006, + .cm_div = 0x00003000, + }, + + [CPRMAN_CLOCK_ARM] = { + .cm_ctl = 0, /* unknown */ + .cm_div = 0 + }, + + [CPRMAN_CLOCK_AVEO] = { + .cm_ctl = 0x00000000, + .cm_div = 0x00000000, + }, + + [CPRMAN_CLOCK_EMMC] = { + .cm_ctl = 0x00000295, + .cm_div = 0x00006000, + }, + + [CPRMAN_CLOCK_EMMC2] = { + .cm_ctl = 0, /* unknown */ + .cm_div = 0 + }, +}; + +#endif diff --git a/include/hw/misc/led.h b/include/hw/misc/led.h new file mode 100644 index 0000000000..aa359b87c2 --- /dev/null +++ b/include/hw/misc/led.h @@ -0,0 +1,97 @@ +/* + * QEMU single LED device + * + * Copyright (C) 2020 Philippe Mathieu-Daudé <f4bug@amsat.org> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef HW_MISC_LED_H +#define HW_MISC_LED_H + +#include "qom/object.h" +#include "hw/qdev-core.h" + +#define TYPE_LED "led" + +/** + * LEDColor: Color of a LED + * + * This set is restricted to physically available LED colors. + * + * LED colors from 'Table 1. Product performance of LUXEON Rebel Color + * Line' of the 'DS68 LUXEON Rebel Color Line' datasheet available at: + * https://www.lumileds.com/products/color-leds/luxeon-rebel-color/ + */ +typedef enum { /* Coarse wavelength range */ + LED_COLOR_VIOLET, /* 425 nm */ + LED_COLOR_BLUE, /* 475 nm */ + LED_COLOR_CYAN, /* 500 nm */ + LED_COLOR_GREEN, /* 535 nm */ + LED_COLOR_AMBER, /* 590 nm */ + LED_COLOR_ORANGE, /* 615 nm */ + LED_COLOR_RED, /* 630 nm */ +} LEDColor; + +struct LEDState { + /* Private */ + DeviceState parent_obj; + /* Public */ + + uint8_t intensity_percent; + qemu_irq irq; + + /* Properties */ + char *description; + char *color; + /* + * Determines whether a GPIO is using a positive (active-high) + * logic (when used with GPIO, the intensity at reset is related + * to the GPIO polarity). + */ + bool gpio_active_high; +}; +typedef struct LEDState LEDState; +DECLARE_INSTANCE_CHECKER(LEDState, LED, TYPE_LED) + +/** + * led_set_intensity: Set the intensity of a LED device + * @s: the LED object + * @intensity_percent: intensity as percentage in range 0 to 100. + */ +void led_set_intensity(LEDState *s, unsigned intensity_percent); + +/** + * led_get_intensity: + * @s: the LED object + * + * Returns: The LED intensity as percentage in range 0 to 100. + */ +unsigned led_get_intensity(LEDState *s); + +/** + * led_set_state: Set the state of a LED device + * @s: the LED object + * @is_emitting: boolean indicating whether the LED is emitting + * + * This utility is meant for LED connected to GPIO. + */ +void led_set_state(LEDState *s, bool is_emitting); + +/** + * led_create_simple: Create and realize a LED device + * @parentobj: the parent object + * @gpio_polarity: GPIO polarity + * @color: color of the LED + * @description: description of the LED (optional) + * + * Create the device state structure, initialize it, and + * drop the reference to it (the device is realized). + * + * Returns: The newly allocated and instantiated LED object. + */ +LEDState *led_create_simple(Object *parentobj, + GpioPolarity gpio_polarity, + LEDColor color, + const char *description); + +#endif /* HW_MISC_LED_H */ diff --git a/include/hw/misc/mps2-fpgaio.h b/include/hw/misc/mps2-fpgaio.h index 80f9227aa6..a010fdb2b6 100644 --- a/include/hw/misc/mps2-fpgaio.h +++ b/include/hw/misc/mps2-fpgaio.h @@ -22,6 +22,7 @@ #define MPS2_FPGAIO_H #include "hw/sysbus.h" +#include "hw/misc/led.h" #include "qom/object.h" #define TYPE_MPS2_FPGAIO "mps2-fpgaio" @@ -33,6 +34,7 @@ struct MPS2FPGAIO { /*< public >*/ MemoryRegion iomem; + LEDState *led[2]; uint32_t led0; uint32_t prescale; diff --git a/include/hw/misc/mps2-scc.h b/include/hw/misc/mps2-scc.h index e922b3c8e0..f65d873203 100644 --- a/include/hw/misc/mps2-scc.h +++ b/include/hw/misc/mps2-scc.h @@ -13,6 +13,7 @@ #define MPS2_SCC_H #include "hw/sysbus.h" +#include "hw/misc/led.h" #include "qom/object.h" #define TYPE_MPS2_SCC "mps2-scc" @@ -26,6 +27,7 @@ struct MPS2SCC { /*< public >*/ MemoryRegion iomem; + LEDState *led[8]; uint32_t cfg0; uint32_t cfg1; diff --git a/include/hw/misc/npcm7xx_clk.h b/include/hw/misc/npcm7xx_clk.h index cdcc9e8534..2338fbbdb5 100644 --- a/include/hw/misc/npcm7xx_clk.h +++ b/include/hw/misc/npcm7xx_clk.h @@ -31,6 +31,8 @@ */ #define NPCM7XX_CLK_NR_REGS (0x70 / sizeof(uint32_t)) +#define NPCM7XX_WATCHDOG_RESET_GPIO_IN "npcm7xx-clk-watchdog-reset-gpio-in" + typedef struct NPCM7xxCLKState { SysBusDevice parent; diff --git a/include/hw/misc/npcm7xx_rng.h b/include/hw/misc/npcm7xx_rng.h new file mode 100644 index 0000000000..5e85fd439d --- /dev/null +++ b/include/hw/misc/npcm7xx_rng.h @@ -0,0 +1,34 @@ +/* + * Nuvoton NPCM7xx Random Number Generator. + * + * Copyright 2020 Google LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef NPCM7XX_RNG_H +#define NPCM7XX_RNG_H + +#include "hw/sysbus.h" + +typedef struct NPCM7xxRNGState { + SysBusDevice parent; + + MemoryRegion iomem; + + uint8_t rngcs; + uint8_t rngd; + uint8_t rngmode; +} NPCM7xxRNGState; + +#define TYPE_NPCM7XX_RNG "npcm7xx-rng" +#define NPCM7XX_RNG(obj) OBJECT_CHECK(NPCM7xxRNGState, (obj), TYPE_NPCM7XX_RNG) + +#endif /* NPCM7XX_RNG_H */ diff --git a/include/hw/misc/sifive_u_otp.h b/include/hw/misc/sifive_u_otp.h index 82c9176c8f..5d0d7df455 100644 --- a/include/hw/misc/sifive_u_otp.h +++ b/include/hw/misc/sifive_u_otp.h @@ -36,6 +36,8 @@ #define SIFIVE_U_OTP_PTRIM 0x34 #define SIFIVE_U_OTP_PWE 0x38 +#define SIFIVE_U_OTP_PWE_EN (1 << 0) + #define SIFIVE_U_OTP_PCE_EN (1 << 0) #define SIFIVE_U_OTP_PDSTB_EN (1 << 0) @@ -44,6 +46,7 @@ #define SIFIVE_U_OTP_PA_MASK 0xfff #define SIFIVE_U_OTP_NUM_FUSES 0x1000 +#define SIFIVE_U_OTP_FUSE_WORD 4 #define SIFIVE_U_OTP_SERIAL_ADDR 0xfc #define SIFIVE_U_OTP_REG_SIZE 0x1000 @@ -75,8 +78,10 @@ struct SiFiveUOTPState { uint32_t ptrim; uint32_t pwe; uint32_t fuse[SIFIVE_U_OTP_NUM_FUSES]; + uint32_t fuse_wo[SIFIVE_U_OTP_NUM_FUSES]; /* config */ uint32_t serial; + BlockBackend *blk; }; #endif /* HW_SIFIVE_U_OTP_H */ diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index bb47896f17..2e89e36cfb 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -846,8 +846,7 @@ void spapr_hotplug_req_add_by_count_indexed(SpaprDrcType drc_type, void spapr_hotplug_req_remove_by_count_indexed(SpaprDrcType drc_type, uint32_t count, uint32_t index); int spapr_hpt_shift_for_ramsize(uint64_t ramsize); -void spapr_reallocate_hpt(SpaprMachineState *spapr, int shift, - Error **errp); +int spapr_reallocate_hpt(SpaprMachineState *spapr, int shift, Error **errp); void spapr_clear_pending_events(SpaprMachineState *spapr); void spapr_clear_pending_hotplug_events(SpaprMachineState *spapr); int spapr_max_server_number(SpaprMachineState *spapr); diff --git a/include/hw/ppc/spapr_nvdimm.h b/include/hw/ppc/spapr_nvdimm.h index b834d82f55..344582d2f5 100644 --- a/include/hw/ppc/spapr_nvdimm.h +++ b/include/hw/ppc/spapr_nvdimm.h @@ -30,7 +30,6 @@ int spapr_pmem_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, void spapr_dt_persistent_memory(SpaprMachineState *spapr, void *fdt); bool spapr_nvdimm_validate(HotplugHandler *hotplug_dev, NVDIMMDevice *nvdimm, uint64_t size, Error **errp); -void spapr_add_nvdimm(DeviceState *dev, uint64_t slot, Error **errp); -void spapr_create_nvdimm_dr_connectors(SpaprMachineState *spapr); +bool spapr_add_nvdimm(DeviceState *dev, uint64_t slot, Error **errp); #endif diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 3761186804..5e737195b5 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -444,6 +444,22 @@ void qdev_machine_creation_done(void); bool qdev_machine_modified(void); /** + * GpioPolarity: Polarity of a GPIO line + * + * GPIO lines use either positive (active-high) logic, + * or negative (active-low) logic. + * + * In active-high logic (%GPIO_POLARITY_ACTIVE_HIGH), a pin is + * active when the voltage on the pin is high (relative to ground); + * whereas in active-low logic (%GPIO_POLARITY_ACTIVE_LOW), a pin + * is active when the voltage on the pin is low (or grounded). + */ +typedef enum { + GPIO_POLARITY_ACTIVE_LOW, + GPIO_POLARITY_ACTIVE_HIGH +} GpioPolarity; + +/** * qdev_get_gpio_in: Get one of a device's anonymous input GPIO lines * @dev: Device whose GPIO we want * @n: Number of the anonymous GPIO line (which must be in range) diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h index 451338780a..0b01988727 100644 --- a/include/hw/riscv/boot.h +++ b/include/hw/riscv/boot.h @@ -23,15 +23,20 @@ #include "exec/cpu-defs.h" #include "hw/loader.h" -void riscv_find_and_load_firmware(MachineState *machine, - const char *default_machine_firmware, - hwaddr firmware_load_addr, - symbol_fn_t sym_cb); +bool riscv_is_32_bit(MachineState *machine); + +target_ulong riscv_calc_kernel_start_addr(MachineState *machine, + target_ulong firmware_end_addr); +target_ulong riscv_find_and_load_firmware(MachineState *machine, + const char *default_machine_firmware, + hwaddr firmware_load_addr, + symbol_fn_t sym_cb); char *riscv_find_firmware(const char *firmware_filename); target_ulong riscv_load_firmware(const char *firmware_filename, hwaddr firmware_load_addr, symbol_fn_t sym_cb); target_ulong riscv_load_kernel(const char *kernel_filename, + target_ulong firmware_end_addr, symbol_fn_t sym_cb); hwaddr riscv_load_initrd(const char *filename, uint64_t mem_size, uint64_t kernel_entry, hwaddr *start); diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h index 22e7e6efa1..a9f7b4a084 100644 --- a/include/hw/riscv/sifive_u.h +++ b/include/hw/riscv/sifive_u.h @@ -48,6 +48,7 @@ typedef struct SiFiveUSoCState { CadenceGEMState gem; uint32_t serial; + char *cpu_type; } SiFiveUSoCState; #define TYPE_RISCV_U_MACHINE MACHINE_TYPE_NAME("sifive_u") diff --git a/include/hw/timer/armv7m_systick.h b/include/hw/timer/armv7m_systick.h index 97cb345ddb..84496faaf9 100644 --- a/include/hw/timer/armv7m_systick.h +++ b/include/hw/timer/armv7m_systick.h @@ -14,6 +14,7 @@ #include "hw/sysbus.h" #include "qom/object.h" +#include "hw/ptimer.h" #define TYPE_SYSTICK "armv7m_systick" @@ -27,7 +28,7 @@ struct SysTickState { uint32_t control; uint32_t reload; int64_t tick; - QEMUTimer *timer; + ptimer_state *ptimer; MemoryRegion iomem; qemu_irq irq; }; diff --git a/include/hw/timer/npcm7xx_timer.h b/include/hw/timer/npcm7xx_timer.h index 878a365a79..6993fd723a 100644 --- a/include/hw/timer/npcm7xx_timer.h +++ b/include/hw/timer/npcm7xx_timer.h @@ -29,14 +29,31 @@ */ #define NPCM7XX_TIMER_NR_REGS (0x54 / sizeof(uint32_t)) +/* The basic watchdog timer period is 2^14 clock cycles. */ +#define NPCM7XX_WATCHDOG_BASETIME_SHIFT 14 + +#define NPCM7XX_WATCHDOG_RESET_GPIO_OUT "npcm7xx-clk-watchdog-reset-gpio-out" + typedef struct NPCM7xxTimerCtrlState NPCM7xxTimerCtrlState; /** - * struct NPCM7xxTimer - Individual timer state. - * @irq: GIC interrupt line to fire on expiration (if enabled). + * struct NPCM7xxBaseTimer - Basic functionality that both regular timer and + * watchdog timer use. * @qtimer: QEMU timer that notifies us on expiration. * @expires_ns: Absolute virtual expiration time. * @remaining_ns: Remaining time until expiration if timer is paused. + */ +typedef struct NPCM7xxBaseTimer { + QEMUTimer qtimer; + int64_t expires_ns; + int64_t remaining_ns; +} NPCM7xxBaseTimer; + +/** + * struct NPCM7xxTimer - Individual timer state. + * @ctrl: The timer module that owns this timer. + * @irq: GIC interrupt line to fire on expiration (if enabled). + * @base_timer: The basic timer functionality for this timer. * @tcsr: The Timer Control and Status Register. * @ticr: The Timer Initial Count Register. */ @@ -44,21 +61,38 @@ typedef struct NPCM7xxTimer { NPCM7xxTimerCtrlState *ctrl; qemu_irq irq; - QEMUTimer qtimer; - int64_t expires_ns; - int64_t remaining_ns; + NPCM7xxBaseTimer base_timer; uint32_t tcsr; uint32_t ticr; } NPCM7xxTimer; /** + * struct NPCM7xxWatchdogTimer - The watchdog timer state. + * @ctrl: The timer module that owns this timer. + * @irq: GIC interrupt line to fire on expiration (if enabled). + * @reset_signal: The GPIO used to send a reset signal. + * @base_timer: The basic timer functionality for this timer. + * @wtcr: The Watchdog Timer Control Register. + */ +typedef struct NPCM7xxWatchdogTimer { + NPCM7xxTimerCtrlState *ctrl; + + qemu_irq irq; + qemu_irq reset_signal; + NPCM7xxBaseTimer base_timer; + + uint32_t wtcr; +} NPCM7xxWatchdogTimer; + +/** * struct NPCM7xxTimerCtrlState - Timer Module device state. * @parent: System bus device. * @iomem: Memory region through which registers are accessed. + * @index: The index of this timer module. * @tisr: The Timer Interrupt Status Register. - * @wtcr: The Watchdog Timer Control Register. * @timer: The five individual timers managed by this module. + * @watchdog_timer: The watchdog timer managed by this module. */ struct NPCM7xxTimerCtrlState { SysBusDevice parent; @@ -66,9 +100,9 @@ struct NPCM7xxTimerCtrlState { MemoryRegion iomem; uint32_t tisr; - uint32_t wtcr; NPCM7xxTimer timer[NPCM7XX_TIMERS_PER_CTRL]; + NPCM7xxWatchdogTimer watchdog_timer; }; #define TYPE_NPCM7XX_TIMER "npcm7xx-timer" diff --git a/include/hw/watchdog/sbsa_gwdt.h b/include/hw/watchdog/sbsa_gwdt.h new file mode 100644 index 0000000000..70b137de30 --- /dev/null +++ b/include/hw/watchdog/sbsa_gwdt.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2020 Linaro Limited + * + * Authors: + * Shashi Mallela <shashi.mallela@linaro.org> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at your + * option) any later version. See the COPYING file in the top-level directory. + * + */ + +#ifndef WDT_SBSA_GWDT_H +#define WDT_SBSA_GWDT_H + +#include "qemu/bitops.h" +#include "hw/sysbus.h" +#include "hw/irq.h" + +#define TYPE_WDT_SBSA "sbsa_gwdt" +#define SBSA_GWDT(obj) \ + OBJECT_CHECK(SBSA_GWDTState, (obj), TYPE_WDT_SBSA) +#define SBSA_GWDT_CLASS(klass) \ + OBJECT_CLASS_CHECK(SBSA_GWDTClass, (klass), TYPE_WDT_SBSA) +#define SBSA_GWDT_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SBSA_GWDTClass, (obj), TYPE_WDT_SBSA) + +/* SBSA Generic Watchdog register definitions */ +/* refresh frame */ +#define SBSA_GWDT_WRR 0x000 + +/* control frame */ +#define SBSA_GWDT_WCS 0x000 +#define SBSA_GWDT_WOR 0x008 +#define SBSA_GWDT_WORU 0x00C +#define SBSA_GWDT_WCV 0x010 +#define SBSA_GWDT_WCVU 0x014 + +/* Watchdog Interface Identification Register */ +#define SBSA_GWDT_W_IIDR 0xFCC + +/* Watchdog Control and Status Register Bits */ +#define SBSA_GWDT_WCS_EN BIT(0) +#define SBSA_GWDT_WCS_WS0 BIT(1) +#define SBSA_GWDT_WCS_WS1 BIT(2) + +#define SBSA_GWDT_WOR_MASK 0x0000FFFF + +/* + * Watchdog Interface Identification Register definition + * considering JEP106 code for ARM in Bits [11:0] + */ +#define SBSA_GWDT_ID 0x1043B + +/* 2 Separate memory regions for each of refresh & control register frames */ +#define SBSA_GWDT_RMMIO_SIZE 0x1000 +#define SBSA_GWDT_CMMIO_SIZE 0x1000 + +#define SBSA_TIMER_FREQ 62500000 /* Hz */ + +typedef struct SBSA_GWDTState { + /* <private> */ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion rmmio; + MemoryRegion cmmio; + qemu_irq irq; + + QEMUTimer *timer; + + uint32_t id; + uint32_t wcs; + uint32_t worl; + uint32_t woru; + uint32_t wcvl; + uint32_t wcvu; +} SBSA_GWDTState; + +#endif /* WDT_SBSA_GWDT_H */ diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index f68ed7db13..4d71dc8fba 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -219,7 +219,6 @@ extern const VMStateInfo vmstate_info_uint64; #define VMS_NULLPTR_MARKER (0x30U) /* '0' */ extern const VMStateInfo vmstate_info_nullptr; -extern const VMStateInfo vmstate_info_float64; extern const VMStateInfo vmstate_info_cpudouble; extern const VMStateInfo vmstate_info_timer; @@ -997,12 +996,6 @@ extern const VMStateInfo vmstate_info_qlist; VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_uint64, uint64_t) -#define VMSTATE_FLOAT64_V(_f, _s, _v) \ - VMSTATE_SINGLE(_f, _s, _v, vmstate_info_float64, float64) - -#define VMSTATE_FLOAT64(_f, _s) \ - VMSTATE_FLOAT64_V(_f, _s, 0) - #define VMSTATE_TIMER_PTR_TEST(_f, _s, _test) \ VMSTATE_POINTER_TEST(_f, _s, _test, vmstate_info_timer, QEMUTimer *) @@ -1114,12 +1107,6 @@ extern const VMStateInfo vmstate_info_qlist; #define VMSTATE_INT64_ARRAY(_f, _s, _n) \ VMSTATE_INT64_ARRAY_V(_f, _s, _n, 0) -#define VMSTATE_FLOAT64_ARRAY_V(_f, _s, _n, _v) \ - VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_float64, float64) - -#define VMSTATE_FLOAT64_ARRAY(_f, _s, _n) \ - VMSTATE_FLOAT64_ARRAY_V(_f, _s, _n, 0) - #define VMSTATE_CPUDOUBLE_ARRAY_V(_f, _s, _n, _v) \ VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_cpudouble, CPU_DoubleU) diff --git a/include/qemu/vhost-user-server.h b/include/qemu/vhost-user-server.h new file mode 100644 index 0000000000..0da4c2cc4c --- /dev/null +++ b/include/qemu/vhost-user-server.h @@ -0,0 +1,65 @@ +/* + * Sharing QEMU devices via vhost-user protocol + * + * Copyright (c) Coiby Xu <coiby.xu@gmail.com>. + * Copyright (c) 2020 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#ifndef VHOST_USER_SERVER_H +#define VHOST_USER_SERVER_H + +#include "contrib/libvhost-user/libvhost-user.h" +#include "io/channel-socket.h" +#include "io/channel-file.h" +#include "io/net-listener.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "standard-headers/linux/virtio_blk.h" + +/* A kick fd that we monitor on behalf of libvhost-user */ +typedef struct VuFdWatch { + VuDev *vu_dev; + int fd; /*kick fd*/ + void *pvt; + vu_watch_cb cb; + QTAILQ_ENTRY(VuFdWatch) next; +} VuFdWatch; + +/** + * VuServer: + * A vhost-user server instance with user-defined VuDevIface callbacks. + * Vhost-user device backends can be implemented using VuServer. VuDevIface + * callbacks and virtqueue kicks run in the given AioContext. + */ +typedef struct { + QIONetListener *listener; + QEMUBH *restart_listener_bh; + AioContext *ctx; + int max_queues; + const VuDevIface *vu_iface; + + /* Protected by ctx lock */ + VuDev vu_dev; + QIOChannel *ioc; /* The I/O channel with the client */ + QIOChannelSocket *sioc; /* The underlying data channel with the client */ + QTAILQ_HEAD(, VuFdWatch) vu_fd_watches; + + Coroutine *co_trip; /* coroutine for processing VhostUserMsg */ +} VuServer; + +bool vhost_user_server_start(VuServer *server, + SocketAddress *unix_socket, + AioContext *ctx, + uint16_t max_queues, + const VuDevIface *vu_iface, + Error **errp); + +void vhost_user_server_stop(VuServer *server); + +void vhost_user_server_attach_aio_context(VuServer *server, AioContext *ctx); +void vhost_user_server_detach_aio_context(VuServer *server); + +#endif /* VHOST_USER_SERVER_H */ diff --git a/include/sysemu/cpus.h b/include/sysemu/cpus.h index 231685955d..e8156728c6 100644 --- a/include/sysemu/cpus.h +++ b/include/sysemu/cpus.h @@ -25,6 +25,9 @@ typedef struct CpusAccel { /* register accel-specific cpus interface implementation */ void cpus_register_accel(const CpusAccel *i); +/* Create a dummy vcpu for CpusAccel->create_vcpu_thread */ +void dummy_start_vcpu_thread(CPUState *); + /* interface available for cpus accelerator threads */ /* For temporary buffers for forming a name */ diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c index d50c1ae583..b591790c22 100644 --- a/linux-user/aarch64/signal.c +++ b/linux-user/aarch64/signal.c @@ -506,10 +506,16 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, + offsetof(struct target_rt_frame_record, tramp); } env->xregs[0] = usig; - env->xregs[31] = frame_addr; env->xregs[29] = frame_addr + fr_ofs; - env->pc = ka->_sa_handler; env->xregs[30] = return_addr; + env->xregs[31] = frame_addr; + env->pc = ka->_sa_handler; + + /* Invoke the signal handler as if by indirect call. */ + if (cpu_isar_feature(aa64_bti, env_archcpu(env))) { + env->btype = 2; + } + if (info) { tswap_siginfo(&frame->info, info); env->xregs[1] = frame_addr + offsetof(struct target_rt_sigframe, info); diff --git a/linux-user/aarch64/syscall_nr.h b/linux-user/aarch64/syscall_nr.h index 85de000b24..6fd5b331e7 100644 --- a/linux-user/aarch64/syscall_nr.h +++ b/linux-user/aarch64/syscall_nr.h @@ -298,7 +298,10 @@ #define TARGET_NR_fspick 433 #define TARGET_NR_pidfd_open 434 #define TARGET_NR_clone3 435 -#define TARGET_NR_syscalls 436 +#define TARGET_NR_close_range 436 +#define TARGET_NR_openat2 437 +#define TARGET_NR_pidfd_getfd 438 +#define TARGET_NR_faccessat2 439 +#define TARGET_NR_syscalls 440 #endif /* LINUX_USER_AARCH64_SYSCALL_NR_H */ - diff --git a/linux-user/alpha/syscall.tbl b/linux-user/alpha/syscall.tbl index 36d42da746..ec8bed9e7b 100644 --- a/linux-user/alpha/syscall.tbl +++ b/linux-user/alpha/syscall.tbl @@ -249,7 +249,7 @@ 316 common mlockall sys_mlockall 317 common munlockall sys_munlockall 318 common sysinfo sys_sysinfo -319 common _sysctl sys_sysctl +319 common _sysctl sys_ni_syscall # 320 was sys_idle 321 common oldumount sys_oldumount 322 common swapon sys_swapon @@ -475,5 +475,7 @@ 543 common fspick sys_fspick 544 common pidfd_open sys_pidfd_open # 545 reserved for clone3 +546 common close_range sys_close_range 547 common openat2 sys_openat2 548 common pidfd_getfd sys_pidfd_getfd +549 common faccessat2 sys_faccessat2 diff --git a/linux-user/arm/syscall.tbl b/linux-user/arm/syscall.tbl index 4d1cf74a2c..171077cbf4 100644 --- a/linux-user/arm/syscall.tbl +++ b/linux-user/arm/syscall.tbl @@ -162,7 +162,7 @@ 146 common writev sys_writev 147 common getsid sys_getsid 148 common fdatasync sys_fdatasync -149 common _sysctl sys_sysctl +149 common _sysctl sys_ni_syscall 150 common mlock sys_mlock 151 common munlock sys_munlock 152 common mlockall sys_mlockall @@ -449,5 +449,7 @@ 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open 435 common clone3 sys_clone3 +436 common close_range sys_close_range 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd +439 common faccessat2 sys_faccessat2 diff --git a/linux-user/elfload.c b/linux-user/elfload.c index f6022fd704..bf8c1bd253 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -12,6 +12,7 @@ #include "qemu/guest-random.h" #include "qemu/units.h" #include "qemu/selfmap.h" +#include "qapi/error.h" #ifdef _ARCH_PPC64 #undef ARCH_DLINFO @@ -1521,6 +1522,39 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #include "elf.h" +/* We must delay the following stanzas until after "elf.h". */ +#if defined(TARGET_AARCH64) + +static bool arch_parse_elf_property(uint32_t pr_type, uint32_t pr_datasz, + const uint32_t *data, + struct image_info *info, + Error **errp) +{ + if (pr_type == GNU_PROPERTY_AARCH64_FEATURE_1_AND) { + if (pr_datasz != sizeof(uint32_t)) { + error_setg(errp, "Ill-formed GNU_PROPERTY_AARCH64_FEATURE_1_AND"); + return false; + } + /* We will extract GNU_PROPERTY_AARCH64_FEATURE_1_BTI later. */ + info->note_flags = *data; + } + return true; +} +#define ARCH_USE_GNU_PROPERTY 1 + +#else + +static bool arch_parse_elf_property(uint32_t pr_type, uint32_t pr_datasz, + const uint32_t *data, + struct image_info *info, + Error **errp) +{ + g_assert_not_reached(); +} +#define ARCH_USE_GNU_PROPERTY 0 + +#endif + struct exec { unsigned int a_info; /* Use macros N_MAGIC, etc for access */ @@ -2372,6 +2406,150 @@ void probe_guest_base(const char *image_name, abi_ulong guest_loaddr, "@ 0x%" PRIx64 "\n", (uint64_t)guest_base); } +enum { + /* The string "GNU\0" as a magic number. */ + GNU0_MAGIC = const_le32('G' | 'N' << 8 | 'U' << 16), + NOTE_DATA_SZ = 1 * KiB, + NOTE_NAME_SZ = 4, + ELF_GNU_PROPERTY_ALIGN = ELF_CLASS == ELFCLASS32 ? 4 : 8, +}; + +/* + * Process a single gnu_property entry. + * Return false for error. + */ +static bool parse_elf_property(const uint32_t *data, int *off, int datasz, + struct image_info *info, bool have_prev_type, + uint32_t *prev_type, Error **errp) +{ + uint32_t pr_type, pr_datasz, step; + + if (*off > datasz || !QEMU_IS_ALIGNED(*off, ELF_GNU_PROPERTY_ALIGN)) { + goto error_data; + } + datasz -= *off; + data += *off / sizeof(uint32_t); + + if (datasz < 2 * sizeof(uint32_t)) { + goto error_data; + } + pr_type = data[0]; + pr_datasz = data[1]; + data += 2; + datasz -= 2 * sizeof(uint32_t); + step = ROUND_UP(pr_datasz, ELF_GNU_PROPERTY_ALIGN); + if (step > datasz) { + goto error_data; + } + + /* Properties are supposed to be unique and sorted on pr_type. */ + if (have_prev_type && pr_type <= *prev_type) { + if (pr_type == *prev_type) { + error_setg(errp, "Duplicate property in PT_GNU_PROPERTY"); + } else { + error_setg(errp, "Unsorted property in PT_GNU_PROPERTY"); + } + return false; + } + *prev_type = pr_type; + + if (!arch_parse_elf_property(pr_type, pr_datasz, data, info, errp)) { + return false; + } + + *off += 2 * sizeof(uint32_t) + step; + return true; + + error_data: + error_setg(errp, "Ill-formed property in PT_GNU_PROPERTY"); + return false; +} + +/* Process NT_GNU_PROPERTY_TYPE_0. */ +static bool parse_elf_properties(int image_fd, + struct image_info *info, + const struct elf_phdr *phdr, + char bprm_buf[BPRM_BUF_SIZE], + Error **errp) +{ + union { + struct elf_note nhdr; + uint32_t data[NOTE_DATA_SZ / sizeof(uint32_t)]; + } note; + + int n, off, datasz; + bool have_prev_type; + uint32_t prev_type; + + /* Unless the arch requires properties, ignore them. */ + if (!ARCH_USE_GNU_PROPERTY) { + return true; + } + + /* If the properties are crazy large, that's too bad. */ + n = phdr->p_filesz; + if (n > sizeof(note)) { + error_setg(errp, "PT_GNU_PROPERTY too large"); + return false; + } + if (n < sizeof(note.nhdr)) { + error_setg(errp, "PT_GNU_PROPERTY too small"); + return false; + } + + if (phdr->p_offset + n <= BPRM_BUF_SIZE) { + memcpy(¬e, bprm_buf + phdr->p_offset, n); + } else { + ssize_t len = pread(image_fd, ¬e, n, phdr->p_offset); + if (len != n) { + error_setg_errno(errp, errno, "Error reading file header"); + return false; + } + } + + /* + * The contents of a valid PT_GNU_PROPERTY is a sequence + * of uint32_t -- swap them all now. + */ +#ifdef BSWAP_NEEDED + for (int i = 0; i < n / 4; i++) { + bswap32s(note.data + i); + } +#endif + + /* + * Note that nhdr is 3 words, and that the "name" described by namesz + * immediately follows nhdr and is thus at the 4th word. Further, all + * of the inputs to the kernel's round_up are multiples of 4. + */ + if (note.nhdr.n_type != NT_GNU_PROPERTY_TYPE_0 || + note.nhdr.n_namesz != NOTE_NAME_SZ || + note.data[3] != GNU0_MAGIC) { + error_setg(errp, "Invalid note in PT_GNU_PROPERTY"); + return false; + } + off = sizeof(note.nhdr) + NOTE_NAME_SZ; + + datasz = note.nhdr.n_descsz + off; + if (datasz > n) { + error_setg(errp, "Invalid note size in PT_GNU_PROPERTY"); + return false; + } + + have_prev_type = false; + prev_type = 0; + while (1) { + if (off == datasz) { + return true; /* end, exit ok */ + } + if (!parse_elf_property(note.data, &off, datasz, info, + have_prev_type, &prev_type, errp)) { + return false; + } + have_prev_type = true; + } +} + /* Load an ELF image into the address space. IMAGE_NAME is the filename of the image, to use in error messages. @@ -2391,16 +2569,17 @@ static void load_elf_image(const char *image_name, int image_fd, struct elfhdr *ehdr = (struct elfhdr *)bprm_buf; struct elf_phdr *phdr; abi_ulong load_addr, load_bias, loaddr, hiaddr, error; - int i, retval; - const char *errmsg; + int i, retval, prot_exec; + Error *err = NULL; /* First of all, some simple consistency checks */ - errmsg = "Invalid ELF image for this architecture"; if (!elf_check_ident(ehdr)) { + error_setg(&err, "Invalid ELF image for this architecture"); goto exit_errmsg; } bswap_ehdr(ehdr); if (!elf_check_ehdr(ehdr)) { + error_setg(&err, "Invalid ELF image for this architecture"); goto exit_errmsg; } @@ -2421,22 +2600,54 @@ static void load_elf_image(const char *image_name, int image_fd, mmap_lock(); - /* Find the maximum size of the image and allocate an appropriate - amount of memory to handle that. */ + /* + * Find the maximum size of the image and allocate an appropriate + * amount of memory to handle that. Locate the interpreter, if any. + */ loaddr = -1, hiaddr = 0; info->alignment = 0; for (i = 0; i < ehdr->e_phnum; ++i) { - if (phdr[i].p_type == PT_LOAD) { - abi_ulong a = phdr[i].p_vaddr - phdr[i].p_offset; + struct elf_phdr *eppnt = phdr + i; + if (eppnt->p_type == PT_LOAD) { + abi_ulong a = eppnt->p_vaddr - eppnt->p_offset; if (a < loaddr) { loaddr = a; } - a = phdr[i].p_vaddr + phdr[i].p_memsz; + a = eppnt->p_vaddr + eppnt->p_memsz; if (a > hiaddr) { hiaddr = a; } ++info->nsegs; - info->alignment |= phdr[i].p_align; + info->alignment |= eppnt->p_align; + } else if (eppnt->p_type == PT_INTERP && pinterp_name) { + g_autofree char *interp_name = NULL; + + if (*pinterp_name) { + error_setg(&err, "Multiple PT_INTERP entries"); + goto exit_errmsg; + } + + interp_name = g_malloc(eppnt->p_filesz); + + if (eppnt->p_offset + eppnt->p_filesz <= BPRM_BUF_SIZE) { + memcpy(interp_name, bprm_buf + eppnt->p_offset, + eppnt->p_filesz); + } else { + retval = pread(image_fd, interp_name, eppnt->p_filesz, + eppnt->p_offset); + if (retval != eppnt->p_filesz) { + goto exit_read; + } + } + if (interp_name[eppnt->p_filesz - 1] != 0) { + error_setg(&err, "Invalid PT_INTERP entry"); + goto exit_errmsg; + } + *pinterp_name = g_steal_pointer(&interp_name); + } else if (eppnt->p_type == PT_GNU_PROPERTY) { + if (!parse_elf_properties(image_fd, info, eppnt, bprm_buf, &err)) { + goto exit_errmsg; + } } } @@ -2490,7 +2701,7 @@ static void load_elf_image(const char *image_name, int image_fd, (ehdr->e_type == ET_EXEC ? MAP_FIXED : 0), -1, 0); if (load_addr == -1) { - goto exit_perror; + goto exit_mmap; } load_bias = load_addr - loaddr; @@ -2525,15 +2736,41 @@ static void load_elf_image(const char *image_name, int image_fd, info->brk = 0; info->elf_flags = ehdr->e_flags; + prot_exec = PROT_EXEC; +#ifdef TARGET_AARCH64 + /* + * If the BTI feature is present, this indicates that the executable + * pages of the startup binary should be mapped with PROT_BTI, so that + * branch targets are enforced. + * + * The startup binary is either the interpreter or the static executable. + * The interpreter is responsible for all pages of a dynamic executable. + * + * Elf notes are backward compatible to older cpus. + * Do not enable BTI unless it is supported. + */ + if ((info->note_flags & GNU_PROPERTY_AARCH64_FEATURE_1_BTI) + && (pinterp_name == NULL || *pinterp_name == 0) + && cpu_isar_feature(aa64_bti, ARM_CPU(thread_cpu))) { + prot_exec |= TARGET_PROT_BTI; + } +#endif + for (i = 0; i < ehdr->e_phnum; i++) { struct elf_phdr *eppnt = phdr + i; if (eppnt->p_type == PT_LOAD) { abi_ulong vaddr, vaddr_po, vaddr_ps, vaddr_ef, vaddr_em, vaddr_len; int elf_prot = 0; - if (eppnt->p_flags & PF_R) elf_prot = PROT_READ; - if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE; - if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC; + if (eppnt->p_flags & PF_R) { + elf_prot |= PROT_READ; + } + if (eppnt->p_flags & PF_W) { + elf_prot |= PROT_WRITE; + } + if (eppnt->p_flags & PF_X) { + elf_prot |= prot_exec; + } vaddr = load_bias + eppnt->p_vaddr; vaddr_po = TARGET_ELF_PAGEOFFSET(vaddr); @@ -2551,7 +2788,7 @@ static void load_elf_image(const char *image_name, int image_fd, image_fd, eppnt->p_offset - vaddr_po); if (error == -1) { - goto exit_perror; + goto exit_mmap; } } @@ -2583,38 +2820,11 @@ static void load_elf_image(const char *image_name, int image_fd, if (vaddr_em > info->brk) { info->brk = vaddr_em; } - } else if (eppnt->p_type == PT_INTERP && pinterp_name) { - char *interp_name; - - if (*pinterp_name) { - errmsg = "Multiple PT_INTERP entries"; - goto exit_errmsg; - } - interp_name = malloc(eppnt->p_filesz); - if (!interp_name) { - goto exit_perror; - } - - if (eppnt->p_offset + eppnt->p_filesz <= BPRM_BUF_SIZE) { - memcpy(interp_name, bprm_buf + eppnt->p_offset, - eppnt->p_filesz); - } else { - retval = pread(image_fd, interp_name, eppnt->p_filesz, - eppnt->p_offset); - if (retval != eppnt->p_filesz) { - goto exit_perror; - } - } - if (interp_name[eppnt->p_filesz - 1] != 0) { - errmsg = "Invalid PT_INTERP entry"; - goto exit_errmsg; - } - *pinterp_name = interp_name; #ifdef TARGET_MIPS } else if (eppnt->p_type == PT_MIPS_ABIFLAGS) { Mips_elf_abiflags_v0 abiflags; if (eppnt->p_filesz < sizeof(Mips_elf_abiflags_v0)) { - errmsg = "Invalid PT_MIPS_ABIFLAGS entry"; + error_setg(&err, "Invalid PT_MIPS_ABIFLAGS entry"); goto exit_errmsg; } if (eppnt->p_offset + eppnt->p_filesz <= BPRM_BUF_SIZE) { @@ -2624,7 +2834,7 @@ static void load_elf_image(const char *image_name, int image_fd, retval = pread(image_fd, &abiflags, sizeof(Mips_elf_abiflags_v0), eppnt->p_offset); if (retval != sizeof(Mips_elf_abiflags_v0)) { - goto exit_perror; + goto exit_read; } } bswap_mips_abiflags(&abiflags); @@ -2649,13 +2859,16 @@ static void load_elf_image(const char *image_name, int image_fd, exit_read: if (retval >= 0) { - errmsg = "Incomplete read of file header"; - goto exit_errmsg; + error_setg(&err, "Incomplete read of file header"); + } else { + error_setg_errno(&err, errno, "Error reading file header"); } - exit_perror: - errmsg = strerror(errno); + goto exit_errmsg; + exit_mmap: + error_setg_errno(&err, errno, "Error mapping file"); + goto exit_errmsg; exit_errmsg: - fprintf(stderr, "%s: %s\n", image_name, errmsg); + error_reportf_err(err, "%s: ", image_name); exit(-1); } @@ -2663,26 +2876,27 @@ static void load_elf_interp(const char *filename, struct image_info *info, char bprm_buf[BPRM_BUF_SIZE]) { int fd, retval; + Error *err = NULL; fd = open(path(filename), O_RDONLY); if (fd < 0) { - goto exit_perror; + error_setg_file_open(&err, errno, filename); + error_report_err(err); + exit(-1); } retval = read(fd, bprm_buf, BPRM_BUF_SIZE); if (retval < 0) { - goto exit_perror; + error_setg_errno(&err, errno, "Error reading file header"); + error_reportf_err(err, "%s: ", filename); + exit(-1); } + if (retval < BPRM_BUF_SIZE) { memset(bprm_buf + retval, 0, BPRM_BUF_SIZE - retval); } load_elf_image(filename, fd, info, NULL, bprm_buf); - return; - - exit_perror: - fprintf(stderr, "%s: %s\n", filename, strerror(errno)); - exit(-1); } static int symfind(const void *s0, const void *s1) @@ -2961,7 +3175,7 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) if (elf_interpreter) { info->load_bias = interp_info.load_bias; info->entry = interp_info.entry; - free(elf_interpreter); + g_free(elf_interpreter); } #ifdef USE_ELF_CORE_DUMP diff --git a/linux-user/hppa/syscall.tbl b/linux-user/hppa/syscall.tbl index 52a15f5cd1..def64d221c 100644 --- a/linux-user/hppa/syscall.tbl +++ b/linux-user/hppa/syscall.tbl @@ -163,7 +163,7 @@ 146 common writev sys_writev compat_sys_writev 147 common getsid sys_getsid 148 common fdatasync sys_fdatasync -149 common _sysctl sys_sysctl compat_sys_sysctl +149 common _sysctl sys_ni_syscall 150 common mlock sys_mlock 151 common munlock sys_munlock 152 common mlockall sys_mlockall @@ -198,8 +198,8 @@ 178 common rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo 179 common rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend 180 common chown sys_chown -181 common setsockopt sys_setsockopt compat_sys_setsockopt -182 common getsockopt sys_getsockopt compat_sys_getsockopt +181 common setsockopt sys_setsockopt sys_setsockopt +182 common getsockopt sys_getsockopt sys_getsockopt 183 common sendmsg sys_sendmsg compat_sys_sendmsg 184 common recvmsg sys_recvmsg compat_sys_recvmsg 185 common semop sys_semop @@ -433,5 +433,7 @@ 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open 435 common clone3 sys_clone3_wrapper +436 common close_range sys_close_range 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd +439 common faccessat2 sys_faccessat2 diff --git a/linux-user/i386/syscall_32.tbl b/linux-user/i386/syscall_32.tbl index c17cb77eb1..9d11028736 100644 --- a/linux-user/i386/syscall_32.tbl +++ b/linux-user/i386/syscall_32.tbl @@ -11,434 +11,436 @@ # # The abi is always "i386" for this file. # -0 i386 restart_syscall sys_restart_syscall __ia32_sys_restart_syscall -1 i386 exit sys_exit __ia32_sys_exit -2 i386 fork sys_fork __ia32_sys_fork -3 i386 read sys_read __ia32_sys_read -4 i386 write sys_write __ia32_sys_write -5 i386 open sys_open __ia32_compat_sys_open -6 i386 close sys_close __ia32_sys_close -7 i386 waitpid sys_waitpid __ia32_sys_waitpid -8 i386 creat sys_creat __ia32_sys_creat -9 i386 link sys_link __ia32_sys_link -10 i386 unlink sys_unlink __ia32_sys_unlink -11 i386 execve sys_execve __ia32_compat_sys_execve -12 i386 chdir sys_chdir __ia32_sys_chdir -13 i386 time sys_time32 __ia32_sys_time32 -14 i386 mknod sys_mknod __ia32_sys_mknod -15 i386 chmod sys_chmod __ia32_sys_chmod -16 i386 lchown sys_lchown16 __ia32_sys_lchown16 +0 i386 restart_syscall sys_restart_syscall +1 i386 exit sys_exit +2 i386 fork sys_fork +3 i386 read sys_read +4 i386 write sys_write +5 i386 open sys_open compat_sys_open +6 i386 close sys_close +7 i386 waitpid sys_waitpid +8 i386 creat sys_creat +9 i386 link sys_link +10 i386 unlink sys_unlink +11 i386 execve sys_execve compat_sys_execve +12 i386 chdir sys_chdir +13 i386 time sys_time32 +14 i386 mknod sys_mknod +15 i386 chmod sys_chmod +16 i386 lchown sys_lchown16 17 i386 break -18 i386 oldstat sys_stat __ia32_sys_stat -19 i386 lseek sys_lseek __ia32_compat_sys_lseek -20 i386 getpid sys_getpid __ia32_sys_getpid -21 i386 mount sys_mount __ia32_compat_sys_mount -22 i386 umount sys_oldumount __ia32_sys_oldumount -23 i386 setuid sys_setuid16 __ia32_sys_setuid16 -24 i386 getuid sys_getuid16 __ia32_sys_getuid16 -25 i386 stime sys_stime32 __ia32_sys_stime32 -26 i386 ptrace sys_ptrace __ia32_compat_sys_ptrace -27 i386 alarm sys_alarm __ia32_sys_alarm -28 i386 oldfstat sys_fstat __ia32_sys_fstat -29 i386 pause sys_pause __ia32_sys_pause -30 i386 utime sys_utime32 __ia32_sys_utime32 +18 i386 oldstat sys_stat +19 i386 lseek sys_lseek compat_sys_lseek +20 i386 getpid sys_getpid +21 i386 mount sys_mount compat_sys_mount +22 i386 umount sys_oldumount +23 i386 setuid sys_setuid16 +24 i386 getuid sys_getuid16 +25 i386 stime sys_stime32 +26 i386 ptrace sys_ptrace compat_sys_ptrace +27 i386 alarm sys_alarm +28 i386 oldfstat sys_fstat +29 i386 pause sys_pause +30 i386 utime sys_utime32 31 i386 stty 32 i386 gtty -33 i386 access sys_access __ia32_sys_access -34 i386 nice sys_nice __ia32_sys_nice +33 i386 access sys_access +34 i386 nice sys_nice 35 i386 ftime -36 i386 sync sys_sync __ia32_sys_sync -37 i386 kill sys_kill __ia32_sys_kill -38 i386 rename sys_rename __ia32_sys_rename -39 i386 mkdir sys_mkdir __ia32_sys_mkdir -40 i386 rmdir sys_rmdir __ia32_sys_rmdir -41 i386 dup sys_dup __ia32_sys_dup -42 i386 pipe sys_pipe __ia32_sys_pipe -43 i386 times sys_times __ia32_compat_sys_times +36 i386 sync sys_sync +37 i386 kill sys_kill +38 i386 rename sys_rename +39 i386 mkdir sys_mkdir +40 i386 rmdir sys_rmdir +41 i386 dup sys_dup +42 i386 pipe sys_pipe +43 i386 times sys_times compat_sys_times 44 i386 prof -45 i386 brk sys_brk __ia32_sys_brk -46 i386 setgid sys_setgid16 __ia32_sys_setgid16 -47 i386 getgid sys_getgid16 __ia32_sys_getgid16 -48 i386 signal sys_signal __ia32_sys_signal -49 i386 geteuid sys_geteuid16 __ia32_sys_geteuid16 -50 i386 getegid sys_getegid16 __ia32_sys_getegid16 -51 i386 acct sys_acct __ia32_sys_acct -52 i386 umount2 sys_umount __ia32_sys_umount +45 i386 brk sys_brk +46 i386 setgid sys_setgid16 +47 i386 getgid sys_getgid16 +48 i386 signal sys_signal +49 i386 geteuid sys_geteuid16 +50 i386 getegid sys_getegid16 +51 i386 acct sys_acct +52 i386 umount2 sys_umount 53 i386 lock -54 i386 ioctl sys_ioctl __ia32_compat_sys_ioctl -55 i386 fcntl sys_fcntl __ia32_compat_sys_fcntl64 +54 i386 ioctl sys_ioctl compat_sys_ioctl +55 i386 fcntl sys_fcntl compat_sys_fcntl64 56 i386 mpx -57 i386 setpgid sys_setpgid __ia32_sys_setpgid +57 i386 setpgid sys_setpgid 58 i386 ulimit -59 i386 oldolduname sys_olduname __ia32_sys_olduname -60 i386 umask sys_umask __ia32_sys_umask -61 i386 chroot sys_chroot __ia32_sys_chroot -62 i386 ustat sys_ustat __ia32_compat_sys_ustat -63 i386 dup2 sys_dup2 __ia32_sys_dup2 -64 i386 getppid sys_getppid __ia32_sys_getppid -65 i386 getpgrp sys_getpgrp __ia32_sys_getpgrp -66 i386 setsid sys_setsid __ia32_sys_setsid -67 i386 sigaction sys_sigaction __ia32_compat_sys_sigaction -68 i386 sgetmask sys_sgetmask __ia32_sys_sgetmask -69 i386 ssetmask sys_ssetmask __ia32_sys_ssetmask -70 i386 setreuid sys_setreuid16 __ia32_sys_setreuid16 -71 i386 setregid sys_setregid16 __ia32_sys_setregid16 -72 i386 sigsuspend sys_sigsuspend __ia32_sys_sigsuspend -73 i386 sigpending sys_sigpending __ia32_compat_sys_sigpending -74 i386 sethostname sys_sethostname __ia32_sys_sethostname -75 i386 setrlimit sys_setrlimit __ia32_compat_sys_setrlimit -76 i386 getrlimit sys_old_getrlimit __ia32_compat_sys_old_getrlimit -77 i386 getrusage sys_getrusage __ia32_compat_sys_getrusage -78 i386 gettimeofday sys_gettimeofday __ia32_compat_sys_gettimeofday -79 i386 settimeofday sys_settimeofday __ia32_compat_sys_settimeofday -80 i386 getgroups sys_getgroups16 __ia32_sys_getgroups16 -81 i386 setgroups sys_setgroups16 __ia32_sys_setgroups16 -82 i386 select sys_old_select __ia32_compat_sys_old_select -83 i386 symlink sys_symlink __ia32_sys_symlink -84 i386 oldlstat sys_lstat __ia32_sys_lstat -85 i386 readlink sys_readlink __ia32_sys_readlink -86 i386 uselib sys_uselib __ia32_sys_uselib -87 i386 swapon sys_swapon __ia32_sys_swapon -88 i386 reboot sys_reboot __ia32_sys_reboot -89 i386 readdir sys_old_readdir __ia32_compat_sys_old_readdir -90 i386 mmap sys_old_mmap __ia32_compat_sys_x86_mmap -91 i386 munmap sys_munmap __ia32_sys_munmap -92 i386 truncate sys_truncate __ia32_compat_sys_truncate -93 i386 ftruncate sys_ftruncate __ia32_compat_sys_ftruncate -94 i386 fchmod sys_fchmod __ia32_sys_fchmod -95 i386 fchown sys_fchown16 __ia32_sys_fchown16 -96 i386 getpriority sys_getpriority __ia32_sys_getpriority -97 i386 setpriority sys_setpriority __ia32_sys_setpriority +59 i386 oldolduname sys_olduname +60 i386 umask sys_umask +61 i386 chroot sys_chroot +62 i386 ustat sys_ustat compat_sys_ustat +63 i386 dup2 sys_dup2 +64 i386 getppid sys_getppid +65 i386 getpgrp sys_getpgrp +66 i386 setsid sys_setsid +67 i386 sigaction sys_sigaction compat_sys_sigaction +68 i386 sgetmask sys_sgetmask +69 i386 ssetmask sys_ssetmask +70 i386 setreuid sys_setreuid16 +71 i386 setregid sys_setregid16 +72 i386 sigsuspend sys_sigsuspend +73 i386 sigpending sys_sigpending compat_sys_sigpending +74 i386 sethostname sys_sethostname +75 i386 setrlimit sys_setrlimit compat_sys_setrlimit +76 i386 getrlimit sys_old_getrlimit compat_sys_old_getrlimit +77 i386 getrusage sys_getrusage compat_sys_getrusage +78 i386 gettimeofday sys_gettimeofday compat_sys_gettimeofday +79 i386 settimeofday sys_settimeofday compat_sys_settimeofday +80 i386 getgroups sys_getgroups16 +81 i386 setgroups sys_setgroups16 +82 i386 select sys_old_select compat_sys_old_select +83 i386 symlink sys_symlink +84 i386 oldlstat sys_lstat +85 i386 readlink sys_readlink +86 i386 uselib sys_uselib +87 i386 swapon sys_swapon +88 i386 reboot sys_reboot +89 i386 readdir sys_old_readdir compat_sys_old_readdir +90 i386 mmap sys_old_mmap compat_sys_ia32_mmap +91 i386 munmap sys_munmap +92 i386 truncate sys_truncate compat_sys_truncate +93 i386 ftruncate sys_ftruncate compat_sys_ftruncate +94 i386 fchmod sys_fchmod +95 i386 fchown sys_fchown16 +96 i386 getpriority sys_getpriority +97 i386 setpriority sys_setpriority 98 i386 profil -99 i386 statfs sys_statfs __ia32_compat_sys_statfs -100 i386 fstatfs sys_fstatfs __ia32_compat_sys_fstatfs -101 i386 ioperm sys_ioperm __ia32_sys_ioperm -102 i386 socketcall sys_socketcall __ia32_compat_sys_socketcall -103 i386 syslog sys_syslog __ia32_sys_syslog -104 i386 setitimer sys_setitimer __ia32_compat_sys_setitimer -105 i386 getitimer sys_getitimer __ia32_compat_sys_getitimer -106 i386 stat sys_newstat __ia32_compat_sys_newstat -107 i386 lstat sys_newlstat __ia32_compat_sys_newlstat -108 i386 fstat sys_newfstat __ia32_compat_sys_newfstat -109 i386 olduname sys_uname __ia32_sys_uname -110 i386 iopl sys_iopl __ia32_sys_iopl -111 i386 vhangup sys_vhangup __ia32_sys_vhangup +99 i386 statfs sys_statfs compat_sys_statfs +100 i386 fstatfs sys_fstatfs compat_sys_fstatfs +101 i386 ioperm sys_ioperm +102 i386 socketcall sys_socketcall compat_sys_socketcall +103 i386 syslog sys_syslog +104 i386 setitimer sys_setitimer compat_sys_setitimer +105 i386 getitimer sys_getitimer compat_sys_getitimer +106 i386 stat sys_newstat compat_sys_newstat +107 i386 lstat sys_newlstat compat_sys_newlstat +108 i386 fstat sys_newfstat compat_sys_newfstat +109 i386 olduname sys_uname +110 i386 iopl sys_iopl +111 i386 vhangup sys_vhangup 112 i386 idle -113 i386 vm86old sys_vm86old __ia32_sys_ni_syscall -114 i386 wait4 sys_wait4 __ia32_compat_sys_wait4 -115 i386 swapoff sys_swapoff __ia32_sys_swapoff -116 i386 sysinfo sys_sysinfo __ia32_compat_sys_sysinfo -117 i386 ipc sys_ipc __ia32_compat_sys_ipc -118 i386 fsync sys_fsync __ia32_sys_fsync -119 i386 sigreturn sys_sigreturn __ia32_compat_sys_sigreturn -120 i386 clone sys_clone __ia32_compat_sys_x86_clone -121 i386 setdomainname sys_setdomainname __ia32_sys_setdomainname -122 i386 uname sys_newuname __ia32_sys_newuname -123 i386 modify_ldt sys_modify_ldt __ia32_sys_modify_ldt -124 i386 adjtimex sys_adjtimex_time32 __ia32_sys_adjtimex_time32 -125 i386 mprotect sys_mprotect __ia32_sys_mprotect -126 i386 sigprocmask sys_sigprocmask __ia32_compat_sys_sigprocmask +113 i386 vm86old sys_vm86old sys_ni_syscall +114 i386 wait4 sys_wait4 compat_sys_wait4 +115 i386 swapoff sys_swapoff +116 i386 sysinfo sys_sysinfo compat_sys_sysinfo +117 i386 ipc sys_ipc compat_sys_ipc +118 i386 fsync sys_fsync +119 i386 sigreturn sys_sigreturn compat_sys_sigreturn +120 i386 clone sys_clone compat_sys_ia32_clone +121 i386 setdomainname sys_setdomainname +122 i386 uname sys_newuname +123 i386 modify_ldt sys_modify_ldt +124 i386 adjtimex sys_adjtimex_time32 +125 i386 mprotect sys_mprotect +126 i386 sigprocmask sys_sigprocmask compat_sys_sigprocmask 127 i386 create_module -128 i386 init_module sys_init_module __ia32_sys_init_module -129 i386 delete_module sys_delete_module __ia32_sys_delete_module +128 i386 init_module sys_init_module +129 i386 delete_module sys_delete_module 130 i386 get_kernel_syms -131 i386 quotactl sys_quotactl __ia32_compat_sys_quotactl32 -132 i386 getpgid sys_getpgid __ia32_sys_getpgid -133 i386 fchdir sys_fchdir __ia32_sys_fchdir -134 i386 bdflush sys_bdflush __ia32_sys_bdflush -135 i386 sysfs sys_sysfs __ia32_sys_sysfs -136 i386 personality sys_personality __ia32_sys_personality +131 i386 quotactl sys_quotactl compat_sys_quotactl32 +132 i386 getpgid sys_getpgid +133 i386 fchdir sys_fchdir +134 i386 bdflush sys_bdflush +135 i386 sysfs sys_sysfs +136 i386 personality sys_personality 137 i386 afs_syscall -138 i386 setfsuid sys_setfsuid16 __ia32_sys_setfsuid16 -139 i386 setfsgid sys_setfsgid16 __ia32_sys_setfsgid16 -140 i386 _llseek sys_llseek __ia32_sys_llseek -141 i386 getdents sys_getdents __ia32_compat_sys_getdents -142 i386 _newselect sys_select __ia32_compat_sys_select -143 i386 flock sys_flock __ia32_sys_flock -144 i386 msync sys_msync __ia32_sys_msync -145 i386 readv sys_readv __ia32_compat_sys_readv -146 i386 writev sys_writev __ia32_compat_sys_writev -147 i386 getsid sys_getsid __ia32_sys_getsid -148 i386 fdatasync sys_fdatasync __ia32_sys_fdatasync -149 i386 _sysctl sys_sysctl __ia32_compat_sys_sysctl -150 i386 mlock sys_mlock __ia32_sys_mlock -151 i386 munlock sys_munlock __ia32_sys_munlock -152 i386 mlockall sys_mlockall __ia32_sys_mlockall -153 i386 munlockall sys_munlockall __ia32_sys_munlockall -154 i386 sched_setparam sys_sched_setparam __ia32_sys_sched_setparam -155 i386 sched_getparam sys_sched_getparam __ia32_sys_sched_getparam -156 i386 sched_setscheduler sys_sched_setscheduler __ia32_sys_sched_setscheduler -157 i386 sched_getscheduler sys_sched_getscheduler __ia32_sys_sched_getscheduler -158 i386 sched_yield sys_sched_yield __ia32_sys_sched_yield -159 i386 sched_get_priority_max sys_sched_get_priority_max __ia32_sys_sched_get_priority_max -160 i386 sched_get_priority_min sys_sched_get_priority_min __ia32_sys_sched_get_priority_min -161 i386 sched_rr_get_interval sys_sched_rr_get_interval_time32 __ia32_sys_sched_rr_get_interval_time32 -162 i386 nanosleep sys_nanosleep_time32 __ia32_sys_nanosleep_time32 -163 i386 mremap sys_mremap __ia32_sys_mremap -164 i386 setresuid sys_setresuid16 __ia32_sys_setresuid16 -165 i386 getresuid sys_getresuid16 __ia32_sys_getresuid16 -166 i386 vm86 sys_vm86 __ia32_sys_ni_syscall +138 i386 setfsuid sys_setfsuid16 +139 i386 setfsgid sys_setfsgid16 +140 i386 _llseek sys_llseek +141 i386 getdents sys_getdents compat_sys_getdents +142 i386 _newselect sys_select compat_sys_select +143 i386 flock sys_flock +144 i386 msync sys_msync +145 i386 readv sys_readv compat_sys_readv +146 i386 writev sys_writev compat_sys_writev +147 i386 getsid sys_getsid +148 i386 fdatasync sys_fdatasync +149 i386 _sysctl sys_ni_syscall +150 i386 mlock sys_mlock +151 i386 munlock sys_munlock +152 i386 mlockall sys_mlockall +153 i386 munlockall sys_munlockall +154 i386 sched_setparam sys_sched_setparam +155 i386 sched_getparam sys_sched_getparam +156 i386 sched_setscheduler sys_sched_setscheduler +157 i386 sched_getscheduler sys_sched_getscheduler +158 i386 sched_yield sys_sched_yield +159 i386 sched_get_priority_max sys_sched_get_priority_max +160 i386 sched_get_priority_min sys_sched_get_priority_min +161 i386 sched_rr_get_interval sys_sched_rr_get_interval_time32 +162 i386 nanosleep sys_nanosleep_time32 +163 i386 mremap sys_mremap +164 i386 setresuid sys_setresuid16 +165 i386 getresuid sys_getresuid16 +166 i386 vm86 sys_vm86 sys_ni_syscall 167 i386 query_module -168 i386 poll sys_poll __ia32_sys_poll +168 i386 poll sys_poll 169 i386 nfsservctl -170 i386 setresgid sys_setresgid16 __ia32_sys_setresgid16 -171 i386 getresgid sys_getresgid16 __ia32_sys_getresgid16 -172 i386 prctl sys_prctl __ia32_sys_prctl -173 i386 rt_sigreturn sys_rt_sigreturn __ia32_compat_sys_rt_sigreturn -174 i386 rt_sigaction sys_rt_sigaction __ia32_compat_sys_rt_sigaction -175 i386 rt_sigprocmask sys_rt_sigprocmask __ia32_compat_sys_rt_sigprocmask -176 i386 rt_sigpending sys_rt_sigpending __ia32_compat_sys_rt_sigpending -177 i386 rt_sigtimedwait sys_rt_sigtimedwait_time32 __ia32_compat_sys_rt_sigtimedwait_time32 -178 i386 rt_sigqueueinfo sys_rt_sigqueueinfo __ia32_compat_sys_rt_sigqueueinfo -179 i386 rt_sigsuspend sys_rt_sigsuspend __ia32_compat_sys_rt_sigsuspend -180 i386 pread64 sys_pread64 __ia32_compat_sys_x86_pread -181 i386 pwrite64 sys_pwrite64 __ia32_compat_sys_x86_pwrite -182 i386 chown sys_chown16 __ia32_sys_chown16 -183 i386 getcwd sys_getcwd __ia32_sys_getcwd -184 i386 capget sys_capget __ia32_sys_capget -185 i386 capset sys_capset __ia32_sys_capset -186 i386 sigaltstack sys_sigaltstack __ia32_compat_sys_sigaltstack -187 i386 sendfile sys_sendfile __ia32_compat_sys_sendfile +170 i386 setresgid sys_setresgid16 +171 i386 getresgid sys_getresgid16 +172 i386 prctl sys_prctl +173 i386 rt_sigreturn sys_rt_sigreturn compat_sys_rt_sigreturn +174 i386 rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction +175 i386 rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask +176 i386 rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending +177 i386 rt_sigtimedwait sys_rt_sigtimedwait_time32 compat_sys_rt_sigtimedwait_time32 +178 i386 rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo +179 i386 rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend +180 i386 pread64 sys_ia32_pread64 +181 i386 pwrite64 sys_ia32_pwrite64 +182 i386 chown sys_chown16 +183 i386 getcwd sys_getcwd +184 i386 capget sys_capget +185 i386 capset sys_capset +186 i386 sigaltstack sys_sigaltstack compat_sys_sigaltstack +187 i386 sendfile sys_sendfile compat_sys_sendfile 188 i386 getpmsg 189 i386 putpmsg -190 i386 vfork sys_vfork __ia32_sys_vfork -191 i386 ugetrlimit sys_getrlimit __ia32_compat_sys_getrlimit -192 i386 mmap2 sys_mmap_pgoff __ia32_sys_mmap_pgoff -193 i386 truncate64 sys_truncate64 __ia32_compat_sys_x86_truncate64 -194 i386 ftruncate64 sys_ftruncate64 __ia32_compat_sys_x86_ftruncate64 -195 i386 stat64 sys_stat64 __ia32_compat_sys_x86_stat64 -196 i386 lstat64 sys_lstat64 __ia32_compat_sys_x86_lstat64 -197 i386 fstat64 sys_fstat64 __ia32_compat_sys_x86_fstat64 -198 i386 lchown32 sys_lchown __ia32_sys_lchown -199 i386 getuid32 sys_getuid __ia32_sys_getuid -200 i386 getgid32 sys_getgid __ia32_sys_getgid -201 i386 geteuid32 sys_geteuid __ia32_sys_geteuid -202 i386 getegid32 sys_getegid __ia32_sys_getegid -203 i386 setreuid32 sys_setreuid __ia32_sys_setreuid -204 i386 setregid32 sys_setregid __ia32_sys_setregid -205 i386 getgroups32 sys_getgroups __ia32_sys_getgroups -206 i386 setgroups32 sys_setgroups __ia32_sys_setgroups -207 i386 fchown32 sys_fchown __ia32_sys_fchown -208 i386 setresuid32 sys_setresuid __ia32_sys_setresuid -209 i386 getresuid32 sys_getresuid __ia32_sys_getresuid -210 i386 setresgid32 sys_setresgid __ia32_sys_setresgid -211 i386 getresgid32 sys_getresgid __ia32_sys_getresgid -212 i386 chown32 sys_chown __ia32_sys_chown -213 i386 setuid32 sys_setuid __ia32_sys_setuid -214 i386 setgid32 sys_setgid __ia32_sys_setgid -215 i386 setfsuid32 sys_setfsuid __ia32_sys_setfsuid -216 i386 setfsgid32 sys_setfsgid __ia32_sys_setfsgid -217 i386 pivot_root sys_pivot_root __ia32_sys_pivot_root -218 i386 mincore sys_mincore __ia32_sys_mincore -219 i386 madvise sys_madvise __ia32_sys_madvise -220 i386 getdents64 sys_getdents64 __ia32_sys_getdents64 -221 i386 fcntl64 sys_fcntl64 __ia32_compat_sys_fcntl64 +190 i386 vfork sys_vfork +191 i386 ugetrlimit sys_getrlimit compat_sys_getrlimit +192 i386 mmap2 sys_mmap_pgoff +193 i386 truncate64 sys_ia32_truncate64 +194 i386 ftruncate64 sys_ia32_ftruncate64 +195 i386 stat64 sys_stat64 compat_sys_ia32_stat64 +196 i386 lstat64 sys_lstat64 compat_sys_ia32_lstat64 +197 i386 fstat64 sys_fstat64 compat_sys_ia32_fstat64 +198 i386 lchown32 sys_lchown +199 i386 getuid32 sys_getuid +200 i386 getgid32 sys_getgid +201 i386 geteuid32 sys_geteuid +202 i386 getegid32 sys_getegid +203 i386 setreuid32 sys_setreuid +204 i386 setregid32 sys_setregid +205 i386 getgroups32 sys_getgroups +206 i386 setgroups32 sys_setgroups +207 i386 fchown32 sys_fchown +208 i386 setresuid32 sys_setresuid +209 i386 getresuid32 sys_getresuid +210 i386 setresgid32 sys_setresgid +211 i386 getresgid32 sys_getresgid +212 i386 chown32 sys_chown +213 i386 setuid32 sys_setuid +214 i386 setgid32 sys_setgid +215 i386 setfsuid32 sys_setfsuid +216 i386 setfsgid32 sys_setfsgid +217 i386 pivot_root sys_pivot_root +218 i386 mincore sys_mincore +219 i386 madvise sys_madvise +220 i386 getdents64 sys_getdents64 +221 i386 fcntl64 sys_fcntl64 compat_sys_fcntl64 # 222 is unused # 223 is unused -224 i386 gettid sys_gettid __ia32_sys_gettid -225 i386 readahead sys_readahead __ia32_compat_sys_x86_readahead -226 i386 setxattr sys_setxattr __ia32_sys_setxattr -227 i386 lsetxattr sys_lsetxattr __ia32_sys_lsetxattr -228 i386 fsetxattr sys_fsetxattr __ia32_sys_fsetxattr -229 i386 getxattr sys_getxattr __ia32_sys_getxattr -230 i386 lgetxattr sys_lgetxattr __ia32_sys_lgetxattr -231 i386 fgetxattr sys_fgetxattr __ia32_sys_fgetxattr -232 i386 listxattr sys_listxattr __ia32_sys_listxattr -233 i386 llistxattr sys_llistxattr __ia32_sys_llistxattr -234 i386 flistxattr sys_flistxattr __ia32_sys_flistxattr -235 i386 removexattr sys_removexattr __ia32_sys_removexattr -236 i386 lremovexattr sys_lremovexattr __ia32_sys_lremovexattr -237 i386 fremovexattr sys_fremovexattr __ia32_sys_fremovexattr -238 i386 tkill sys_tkill __ia32_sys_tkill -239 i386 sendfile64 sys_sendfile64 __ia32_sys_sendfile64 -240 i386 futex sys_futex_time32 __ia32_sys_futex_time32 -241 i386 sched_setaffinity sys_sched_setaffinity __ia32_compat_sys_sched_setaffinity -242 i386 sched_getaffinity sys_sched_getaffinity __ia32_compat_sys_sched_getaffinity -243 i386 set_thread_area sys_set_thread_area __ia32_sys_set_thread_area -244 i386 get_thread_area sys_get_thread_area __ia32_sys_get_thread_area -245 i386 io_setup sys_io_setup __ia32_compat_sys_io_setup -246 i386 io_destroy sys_io_destroy __ia32_sys_io_destroy -247 i386 io_getevents sys_io_getevents_time32 __ia32_sys_io_getevents_time32 -248 i386 io_submit sys_io_submit __ia32_compat_sys_io_submit -249 i386 io_cancel sys_io_cancel __ia32_sys_io_cancel -250 i386 fadvise64 sys_fadvise64 __ia32_compat_sys_x86_fadvise64 +224 i386 gettid sys_gettid +225 i386 readahead sys_ia32_readahead +226 i386 setxattr sys_setxattr +227 i386 lsetxattr sys_lsetxattr +228 i386 fsetxattr sys_fsetxattr +229 i386 getxattr sys_getxattr +230 i386 lgetxattr sys_lgetxattr +231 i386 fgetxattr sys_fgetxattr +232 i386 listxattr sys_listxattr +233 i386 llistxattr sys_llistxattr +234 i386 flistxattr sys_flistxattr +235 i386 removexattr sys_removexattr +236 i386 lremovexattr sys_lremovexattr +237 i386 fremovexattr sys_fremovexattr +238 i386 tkill sys_tkill +239 i386 sendfile64 sys_sendfile64 +240 i386 futex sys_futex_time32 +241 i386 sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity +242 i386 sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity +243 i386 set_thread_area sys_set_thread_area +244 i386 get_thread_area sys_get_thread_area +245 i386 io_setup sys_io_setup compat_sys_io_setup +246 i386 io_destroy sys_io_destroy +247 i386 io_getevents sys_io_getevents_time32 +248 i386 io_submit sys_io_submit compat_sys_io_submit +249 i386 io_cancel sys_io_cancel +250 i386 fadvise64 sys_ia32_fadvise64 # 251 is available for reuse (was briefly sys_set_zone_reclaim) -252 i386 exit_group sys_exit_group __ia32_sys_exit_group -253 i386 lookup_dcookie sys_lookup_dcookie __ia32_compat_sys_lookup_dcookie -254 i386 epoll_create sys_epoll_create __ia32_sys_epoll_create -255 i386 epoll_ctl sys_epoll_ctl __ia32_sys_epoll_ctl -256 i386 epoll_wait sys_epoll_wait __ia32_sys_epoll_wait -257 i386 remap_file_pages sys_remap_file_pages __ia32_sys_remap_file_pages -258 i386 set_tid_address sys_set_tid_address __ia32_sys_set_tid_address -259 i386 timer_create sys_timer_create __ia32_compat_sys_timer_create -260 i386 timer_settime sys_timer_settime32 __ia32_sys_timer_settime32 -261 i386 timer_gettime sys_timer_gettime32 __ia32_sys_timer_gettime32 -262 i386 timer_getoverrun sys_timer_getoverrun __ia32_sys_timer_getoverrun -263 i386 timer_delete sys_timer_delete __ia32_sys_timer_delete -264 i386 clock_settime sys_clock_settime32 __ia32_sys_clock_settime32 -265 i386 clock_gettime sys_clock_gettime32 __ia32_sys_clock_gettime32 -266 i386 clock_getres sys_clock_getres_time32 __ia32_sys_clock_getres_time32 -267 i386 clock_nanosleep sys_clock_nanosleep_time32 __ia32_sys_clock_nanosleep_time32 -268 i386 statfs64 sys_statfs64 __ia32_compat_sys_statfs64 -269 i386 fstatfs64 sys_fstatfs64 __ia32_compat_sys_fstatfs64 -270 i386 tgkill sys_tgkill __ia32_sys_tgkill -271 i386 utimes sys_utimes_time32 __ia32_sys_utimes_time32 -272 i386 fadvise64_64 sys_fadvise64_64 __ia32_compat_sys_x86_fadvise64_64 +252 i386 exit_group sys_exit_group +253 i386 lookup_dcookie sys_lookup_dcookie compat_sys_lookup_dcookie +254 i386 epoll_create sys_epoll_create +255 i386 epoll_ctl sys_epoll_ctl +256 i386 epoll_wait sys_epoll_wait +257 i386 remap_file_pages sys_remap_file_pages +258 i386 set_tid_address sys_set_tid_address +259 i386 timer_create sys_timer_create compat_sys_timer_create +260 i386 timer_settime sys_timer_settime32 +261 i386 timer_gettime sys_timer_gettime32 +262 i386 timer_getoverrun sys_timer_getoverrun +263 i386 timer_delete sys_timer_delete +264 i386 clock_settime sys_clock_settime32 +265 i386 clock_gettime sys_clock_gettime32 +266 i386 clock_getres sys_clock_getres_time32 +267 i386 clock_nanosleep sys_clock_nanosleep_time32 +268 i386 statfs64 sys_statfs64 compat_sys_statfs64 +269 i386 fstatfs64 sys_fstatfs64 compat_sys_fstatfs64 +270 i386 tgkill sys_tgkill +271 i386 utimes sys_utimes_time32 +272 i386 fadvise64_64 sys_ia32_fadvise64_64 273 i386 vserver -274 i386 mbind sys_mbind __ia32_sys_mbind -275 i386 get_mempolicy sys_get_mempolicy __ia32_compat_sys_get_mempolicy -276 i386 set_mempolicy sys_set_mempolicy __ia32_sys_set_mempolicy -277 i386 mq_open sys_mq_open __ia32_compat_sys_mq_open -278 i386 mq_unlink sys_mq_unlink __ia32_sys_mq_unlink -279 i386 mq_timedsend sys_mq_timedsend_time32 __ia32_sys_mq_timedsend_time32 -280 i386 mq_timedreceive sys_mq_timedreceive_time32 __ia32_sys_mq_timedreceive_time32 -281 i386 mq_notify sys_mq_notify __ia32_compat_sys_mq_notify -282 i386 mq_getsetattr sys_mq_getsetattr __ia32_compat_sys_mq_getsetattr -283 i386 kexec_load sys_kexec_load __ia32_compat_sys_kexec_load -284 i386 waitid sys_waitid __ia32_compat_sys_waitid +274 i386 mbind sys_mbind +275 i386 get_mempolicy sys_get_mempolicy compat_sys_get_mempolicy +276 i386 set_mempolicy sys_set_mempolicy +277 i386 mq_open sys_mq_open compat_sys_mq_open +278 i386 mq_unlink sys_mq_unlink +279 i386 mq_timedsend sys_mq_timedsend_time32 +280 i386 mq_timedreceive sys_mq_timedreceive_time32 +281 i386 mq_notify sys_mq_notify compat_sys_mq_notify +282 i386 mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr +283 i386 kexec_load sys_kexec_load compat_sys_kexec_load +284 i386 waitid sys_waitid compat_sys_waitid # 285 sys_setaltroot -286 i386 add_key sys_add_key __ia32_sys_add_key -287 i386 request_key sys_request_key __ia32_sys_request_key -288 i386 keyctl sys_keyctl __ia32_compat_sys_keyctl -289 i386 ioprio_set sys_ioprio_set __ia32_sys_ioprio_set -290 i386 ioprio_get sys_ioprio_get __ia32_sys_ioprio_get -291 i386 inotify_init sys_inotify_init __ia32_sys_inotify_init -292 i386 inotify_add_watch sys_inotify_add_watch __ia32_sys_inotify_add_watch -293 i386 inotify_rm_watch sys_inotify_rm_watch __ia32_sys_inotify_rm_watch -294 i386 migrate_pages sys_migrate_pages __ia32_sys_migrate_pages -295 i386 openat sys_openat __ia32_compat_sys_openat -296 i386 mkdirat sys_mkdirat __ia32_sys_mkdirat -297 i386 mknodat sys_mknodat __ia32_sys_mknodat -298 i386 fchownat sys_fchownat __ia32_sys_fchownat -299 i386 futimesat sys_futimesat_time32 __ia32_sys_futimesat_time32 -300 i386 fstatat64 sys_fstatat64 __ia32_compat_sys_x86_fstatat -301 i386 unlinkat sys_unlinkat __ia32_sys_unlinkat -302 i386 renameat sys_renameat __ia32_sys_renameat -303 i386 linkat sys_linkat __ia32_sys_linkat -304 i386 symlinkat sys_symlinkat __ia32_sys_symlinkat -305 i386 readlinkat sys_readlinkat __ia32_sys_readlinkat -306 i386 fchmodat sys_fchmodat __ia32_sys_fchmodat -307 i386 faccessat sys_faccessat __ia32_sys_faccessat -308 i386 pselect6 sys_pselect6_time32 __ia32_compat_sys_pselect6_time32 -309 i386 ppoll sys_ppoll_time32 __ia32_compat_sys_ppoll_time32 -310 i386 unshare sys_unshare __ia32_sys_unshare -311 i386 set_robust_list sys_set_robust_list __ia32_compat_sys_set_robust_list -312 i386 get_robust_list sys_get_robust_list __ia32_compat_sys_get_robust_list -313 i386 splice sys_splice __ia32_sys_splice -314 i386 sync_file_range sys_sync_file_range __ia32_compat_sys_x86_sync_file_range -315 i386 tee sys_tee __ia32_sys_tee -316 i386 vmsplice sys_vmsplice __ia32_compat_sys_vmsplice -317 i386 move_pages sys_move_pages __ia32_compat_sys_move_pages -318 i386 getcpu sys_getcpu __ia32_sys_getcpu -319 i386 epoll_pwait sys_epoll_pwait __ia32_sys_epoll_pwait -320 i386 utimensat sys_utimensat_time32 __ia32_sys_utimensat_time32 -321 i386 signalfd sys_signalfd __ia32_compat_sys_signalfd -322 i386 timerfd_create sys_timerfd_create __ia32_sys_timerfd_create -323 i386 eventfd sys_eventfd __ia32_sys_eventfd -324 i386 fallocate sys_fallocate __ia32_compat_sys_x86_fallocate -325 i386 timerfd_settime sys_timerfd_settime32 __ia32_sys_timerfd_settime32 -326 i386 timerfd_gettime sys_timerfd_gettime32 __ia32_sys_timerfd_gettime32 -327 i386 signalfd4 sys_signalfd4 __ia32_compat_sys_signalfd4 -328 i386 eventfd2 sys_eventfd2 __ia32_sys_eventfd2 -329 i386 epoll_create1 sys_epoll_create1 __ia32_sys_epoll_create1 -330 i386 dup3 sys_dup3 __ia32_sys_dup3 -331 i386 pipe2 sys_pipe2 __ia32_sys_pipe2 -332 i386 inotify_init1 sys_inotify_init1 __ia32_sys_inotify_init1 -333 i386 preadv sys_preadv __ia32_compat_sys_preadv -334 i386 pwritev sys_pwritev __ia32_compat_sys_pwritev -335 i386 rt_tgsigqueueinfo sys_rt_tgsigqueueinfo __ia32_compat_sys_rt_tgsigqueueinfo -336 i386 perf_event_open sys_perf_event_open __ia32_sys_perf_event_open -337 i386 recvmmsg sys_recvmmsg_time32 __ia32_compat_sys_recvmmsg_time32 -338 i386 fanotify_init sys_fanotify_init __ia32_sys_fanotify_init -339 i386 fanotify_mark sys_fanotify_mark __ia32_compat_sys_fanotify_mark -340 i386 prlimit64 sys_prlimit64 __ia32_sys_prlimit64 -341 i386 name_to_handle_at sys_name_to_handle_at __ia32_sys_name_to_handle_at -342 i386 open_by_handle_at sys_open_by_handle_at __ia32_compat_sys_open_by_handle_at -343 i386 clock_adjtime sys_clock_adjtime32 __ia32_sys_clock_adjtime32 -344 i386 syncfs sys_syncfs __ia32_sys_syncfs -345 i386 sendmmsg sys_sendmmsg __ia32_compat_sys_sendmmsg -346 i386 setns sys_setns __ia32_sys_setns -347 i386 process_vm_readv sys_process_vm_readv __ia32_compat_sys_process_vm_readv -348 i386 process_vm_writev sys_process_vm_writev __ia32_compat_sys_process_vm_writev -349 i386 kcmp sys_kcmp __ia32_sys_kcmp -350 i386 finit_module sys_finit_module __ia32_sys_finit_module -351 i386 sched_setattr sys_sched_setattr __ia32_sys_sched_setattr -352 i386 sched_getattr sys_sched_getattr __ia32_sys_sched_getattr -353 i386 renameat2 sys_renameat2 __ia32_sys_renameat2 -354 i386 seccomp sys_seccomp __ia32_sys_seccomp -355 i386 getrandom sys_getrandom __ia32_sys_getrandom -356 i386 memfd_create sys_memfd_create __ia32_sys_memfd_create -357 i386 bpf sys_bpf __ia32_sys_bpf -358 i386 execveat sys_execveat __ia32_compat_sys_execveat -359 i386 socket sys_socket __ia32_sys_socket -360 i386 socketpair sys_socketpair __ia32_sys_socketpair -361 i386 bind sys_bind __ia32_sys_bind -362 i386 connect sys_connect __ia32_sys_connect -363 i386 listen sys_listen __ia32_sys_listen -364 i386 accept4 sys_accept4 __ia32_sys_accept4 -365 i386 getsockopt sys_getsockopt __ia32_compat_sys_getsockopt -366 i386 setsockopt sys_setsockopt __ia32_compat_sys_setsockopt -367 i386 getsockname sys_getsockname __ia32_sys_getsockname -368 i386 getpeername sys_getpeername __ia32_sys_getpeername -369 i386 sendto sys_sendto __ia32_sys_sendto -370 i386 sendmsg sys_sendmsg __ia32_compat_sys_sendmsg -371 i386 recvfrom sys_recvfrom __ia32_compat_sys_recvfrom -372 i386 recvmsg sys_recvmsg __ia32_compat_sys_recvmsg -373 i386 shutdown sys_shutdown __ia32_sys_shutdown -374 i386 userfaultfd sys_userfaultfd __ia32_sys_userfaultfd -375 i386 membarrier sys_membarrier __ia32_sys_membarrier -376 i386 mlock2 sys_mlock2 __ia32_sys_mlock2 -377 i386 copy_file_range sys_copy_file_range __ia32_sys_copy_file_range -378 i386 preadv2 sys_preadv2 __ia32_compat_sys_preadv2 -379 i386 pwritev2 sys_pwritev2 __ia32_compat_sys_pwritev2 -380 i386 pkey_mprotect sys_pkey_mprotect __ia32_sys_pkey_mprotect -381 i386 pkey_alloc sys_pkey_alloc __ia32_sys_pkey_alloc -382 i386 pkey_free sys_pkey_free __ia32_sys_pkey_free -383 i386 statx sys_statx __ia32_sys_statx -384 i386 arch_prctl sys_arch_prctl __ia32_compat_sys_arch_prctl -385 i386 io_pgetevents sys_io_pgetevents_time32 __ia32_compat_sys_io_pgetevents -386 i386 rseq sys_rseq __ia32_sys_rseq -393 i386 semget sys_semget __ia32_sys_semget -394 i386 semctl sys_semctl __ia32_compat_sys_semctl -395 i386 shmget sys_shmget __ia32_sys_shmget -396 i386 shmctl sys_shmctl __ia32_compat_sys_shmctl -397 i386 shmat sys_shmat __ia32_compat_sys_shmat -398 i386 shmdt sys_shmdt __ia32_sys_shmdt -399 i386 msgget sys_msgget __ia32_sys_msgget -400 i386 msgsnd sys_msgsnd __ia32_compat_sys_msgsnd -401 i386 msgrcv sys_msgrcv __ia32_compat_sys_msgrcv -402 i386 msgctl sys_msgctl __ia32_compat_sys_msgctl -403 i386 clock_gettime64 sys_clock_gettime __ia32_sys_clock_gettime -404 i386 clock_settime64 sys_clock_settime __ia32_sys_clock_settime -405 i386 clock_adjtime64 sys_clock_adjtime __ia32_sys_clock_adjtime -406 i386 clock_getres_time64 sys_clock_getres __ia32_sys_clock_getres -407 i386 clock_nanosleep_time64 sys_clock_nanosleep __ia32_sys_clock_nanosleep -408 i386 timer_gettime64 sys_timer_gettime __ia32_sys_timer_gettime -409 i386 timer_settime64 sys_timer_settime __ia32_sys_timer_settime -410 i386 timerfd_gettime64 sys_timerfd_gettime __ia32_sys_timerfd_gettime -411 i386 timerfd_settime64 sys_timerfd_settime __ia32_sys_timerfd_settime -412 i386 utimensat_time64 sys_utimensat __ia32_sys_utimensat -413 i386 pselect6_time64 sys_pselect6 __ia32_compat_sys_pselect6_time64 -414 i386 ppoll_time64 sys_ppoll __ia32_compat_sys_ppoll_time64 -416 i386 io_pgetevents_time64 sys_io_pgetevents __ia32_sys_io_pgetevents -417 i386 recvmmsg_time64 sys_recvmmsg __ia32_compat_sys_recvmmsg_time64 -418 i386 mq_timedsend_time64 sys_mq_timedsend __ia32_sys_mq_timedsend -419 i386 mq_timedreceive_time64 sys_mq_timedreceive __ia32_sys_mq_timedreceive -420 i386 semtimedop_time64 sys_semtimedop __ia32_sys_semtimedop -421 i386 rt_sigtimedwait_time64 sys_rt_sigtimedwait __ia32_compat_sys_rt_sigtimedwait_time64 -422 i386 futex_time64 sys_futex __ia32_sys_futex -423 i386 sched_rr_get_interval_time64 sys_sched_rr_get_interval __ia32_sys_sched_rr_get_interval -424 i386 pidfd_send_signal sys_pidfd_send_signal __ia32_sys_pidfd_send_signal -425 i386 io_uring_setup sys_io_uring_setup __ia32_sys_io_uring_setup -426 i386 io_uring_enter sys_io_uring_enter __ia32_sys_io_uring_enter -427 i386 io_uring_register sys_io_uring_register __ia32_sys_io_uring_register -428 i386 open_tree sys_open_tree __ia32_sys_open_tree -429 i386 move_mount sys_move_mount __ia32_sys_move_mount -430 i386 fsopen sys_fsopen __ia32_sys_fsopen -431 i386 fsconfig sys_fsconfig __ia32_sys_fsconfig -432 i386 fsmount sys_fsmount __ia32_sys_fsmount -433 i386 fspick sys_fspick __ia32_sys_fspick -434 i386 pidfd_open sys_pidfd_open __ia32_sys_pidfd_open -435 i386 clone3 sys_clone3 __ia32_sys_clone3 -437 i386 openat2 sys_openat2 __ia32_sys_openat2 -438 i386 pidfd_getfd sys_pidfd_getfd __ia32_sys_pidfd_getfd +286 i386 add_key sys_add_key +287 i386 request_key sys_request_key +288 i386 keyctl sys_keyctl compat_sys_keyctl +289 i386 ioprio_set sys_ioprio_set +290 i386 ioprio_get sys_ioprio_get +291 i386 inotify_init sys_inotify_init +292 i386 inotify_add_watch sys_inotify_add_watch +293 i386 inotify_rm_watch sys_inotify_rm_watch +294 i386 migrate_pages sys_migrate_pages +295 i386 openat sys_openat compat_sys_openat +296 i386 mkdirat sys_mkdirat +297 i386 mknodat sys_mknodat +298 i386 fchownat sys_fchownat +299 i386 futimesat sys_futimesat_time32 +300 i386 fstatat64 sys_fstatat64 compat_sys_ia32_fstatat64 +301 i386 unlinkat sys_unlinkat +302 i386 renameat sys_renameat +303 i386 linkat sys_linkat +304 i386 symlinkat sys_symlinkat +305 i386 readlinkat sys_readlinkat +306 i386 fchmodat sys_fchmodat +307 i386 faccessat sys_faccessat +308 i386 pselect6 sys_pselect6_time32 compat_sys_pselect6_time32 +309 i386 ppoll sys_ppoll_time32 compat_sys_ppoll_time32 +310 i386 unshare sys_unshare +311 i386 set_robust_list sys_set_robust_list compat_sys_set_robust_list +312 i386 get_robust_list sys_get_robust_list compat_sys_get_robust_list +313 i386 splice sys_splice +314 i386 sync_file_range sys_ia32_sync_file_range +315 i386 tee sys_tee +316 i386 vmsplice sys_vmsplice compat_sys_vmsplice +317 i386 move_pages sys_move_pages compat_sys_move_pages +318 i386 getcpu sys_getcpu +319 i386 epoll_pwait sys_epoll_pwait +320 i386 utimensat sys_utimensat_time32 +321 i386 signalfd sys_signalfd compat_sys_signalfd +322 i386 timerfd_create sys_timerfd_create +323 i386 eventfd sys_eventfd +324 i386 fallocate sys_ia32_fallocate +325 i386 timerfd_settime sys_timerfd_settime32 +326 i386 timerfd_gettime sys_timerfd_gettime32 +327 i386 signalfd4 sys_signalfd4 compat_sys_signalfd4 +328 i386 eventfd2 sys_eventfd2 +329 i386 epoll_create1 sys_epoll_create1 +330 i386 dup3 sys_dup3 +331 i386 pipe2 sys_pipe2 +332 i386 inotify_init1 sys_inotify_init1 +333 i386 preadv sys_preadv compat_sys_preadv +334 i386 pwritev sys_pwritev compat_sys_pwritev +335 i386 rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo +336 i386 perf_event_open sys_perf_event_open +337 i386 recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32 +338 i386 fanotify_init sys_fanotify_init +339 i386 fanotify_mark sys_fanotify_mark compat_sys_fanotify_mark +340 i386 prlimit64 sys_prlimit64 +341 i386 name_to_handle_at sys_name_to_handle_at +342 i386 open_by_handle_at sys_open_by_handle_at compat_sys_open_by_handle_at +343 i386 clock_adjtime sys_clock_adjtime32 +344 i386 syncfs sys_syncfs +345 i386 sendmmsg sys_sendmmsg compat_sys_sendmmsg +346 i386 setns sys_setns +347 i386 process_vm_readv sys_process_vm_readv compat_sys_process_vm_readv +348 i386 process_vm_writev sys_process_vm_writev compat_sys_process_vm_writev +349 i386 kcmp sys_kcmp +350 i386 finit_module sys_finit_module +351 i386 sched_setattr sys_sched_setattr +352 i386 sched_getattr sys_sched_getattr +353 i386 renameat2 sys_renameat2 +354 i386 seccomp sys_seccomp +355 i386 getrandom sys_getrandom +356 i386 memfd_create sys_memfd_create +357 i386 bpf sys_bpf +358 i386 execveat sys_execveat compat_sys_execveat +359 i386 socket sys_socket +360 i386 socketpair sys_socketpair +361 i386 bind sys_bind +362 i386 connect sys_connect +363 i386 listen sys_listen +364 i386 accept4 sys_accept4 +365 i386 getsockopt sys_getsockopt sys_getsockopt +366 i386 setsockopt sys_setsockopt sys_setsockopt +367 i386 getsockname sys_getsockname +368 i386 getpeername sys_getpeername +369 i386 sendto sys_sendto +370 i386 sendmsg sys_sendmsg compat_sys_sendmsg +371 i386 recvfrom sys_recvfrom compat_sys_recvfrom +372 i386 recvmsg sys_recvmsg compat_sys_recvmsg +373 i386 shutdown sys_shutdown +374 i386 userfaultfd sys_userfaultfd +375 i386 membarrier sys_membarrier +376 i386 mlock2 sys_mlock2 +377 i386 copy_file_range sys_copy_file_range +378 i386 preadv2 sys_preadv2 compat_sys_preadv2 +379 i386 pwritev2 sys_pwritev2 compat_sys_pwritev2 +380 i386 pkey_mprotect sys_pkey_mprotect +381 i386 pkey_alloc sys_pkey_alloc +382 i386 pkey_free sys_pkey_free +383 i386 statx sys_statx +384 i386 arch_prctl sys_arch_prctl compat_sys_arch_prctl +385 i386 io_pgetevents sys_io_pgetevents_time32 compat_sys_io_pgetevents +386 i386 rseq sys_rseq +393 i386 semget sys_semget +394 i386 semctl sys_semctl compat_sys_semctl +395 i386 shmget sys_shmget +396 i386 shmctl sys_shmctl compat_sys_shmctl +397 i386 shmat sys_shmat compat_sys_shmat +398 i386 shmdt sys_shmdt +399 i386 msgget sys_msgget +400 i386 msgsnd sys_msgsnd compat_sys_msgsnd +401 i386 msgrcv sys_msgrcv compat_sys_msgrcv +402 i386 msgctl sys_msgctl compat_sys_msgctl +403 i386 clock_gettime64 sys_clock_gettime +404 i386 clock_settime64 sys_clock_settime +405 i386 clock_adjtime64 sys_clock_adjtime +406 i386 clock_getres_time64 sys_clock_getres +407 i386 clock_nanosleep_time64 sys_clock_nanosleep +408 i386 timer_gettime64 sys_timer_gettime +409 i386 timer_settime64 sys_timer_settime +410 i386 timerfd_gettime64 sys_timerfd_gettime +411 i386 timerfd_settime64 sys_timerfd_settime +412 i386 utimensat_time64 sys_utimensat +413 i386 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 +414 i386 ppoll_time64 sys_ppoll compat_sys_ppoll_time64 +416 i386 io_pgetevents_time64 sys_io_pgetevents +417 i386 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 +418 i386 mq_timedsend_time64 sys_mq_timedsend +419 i386 mq_timedreceive_time64 sys_mq_timedreceive +420 i386 semtimedop_time64 sys_semtimedop +421 i386 rt_sigtimedwait_time64 sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time64 +422 i386 futex_time64 sys_futex +423 i386 sched_rr_get_interval_time64 sys_sched_rr_get_interval +424 i386 pidfd_send_signal sys_pidfd_send_signal +425 i386 io_uring_setup sys_io_uring_setup +426 i386 io_uring_enter sys_io_uring_enter +427 i386 io_uring_register sys_io_uring_register +428 i386 open_tree sys_open_tree +429 i386 move_mount sys_move_mount +430 i386 fsopen sys_fsopen +431 i386 fsconfig sys_fsconfig +432 i386 fsmount sys_fsmount +433 i386 fspick sys_fspick +434 i386 pidfd_open sys_pidfd_open +435 i386 clone3 sys_clone3 +436 i386 close_range sys_close_range +437 i386 openat2 sys_openat2 +438 i386 pidfd_getfd sys_pidfd_getfd +439 i386 faccessat2 sys_faccessat2 diff --git a/linux-user/m68k/syscall.tbl b/linux-user/m68k/syscall.tbl index f4f49fcb76..81fc799d83 100644 --- a/linux-user/m68k/syscall.tbl +++ b/linux-user/m68k/syscall.tbl @@ -156,7 +156,7 @@ 146 common writev sys_writev 147 common getsid sys_getsid 148 common fdatasync sys_fdatasync -149 common _sysctl sys_sysctl +149 common _sysctl sys_ni_syscall 150 common mlock sys_mlock 151 common munlock sys_munlock 152 common mlockall sys_mlockall @@ -435,5 +435,7 @@ 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open 435 common clone3 __sys_clone3 +436 common close_range sys_close_range 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd +439 common faccessat2 sys_faccessat2 diff --git a/linux-user/microblaze/syscall.tbl b/linux-user/microblaze/syscall.tbl index 4c67b11f9c..b4e263916f 100644 --- a/linux-user/microblaze/syscall.tbl +++ b/linux-user/microblaze/syscall.tbl @@ -156,7 +156,7 @@ 146 common writev sys_writev 147 common getsid sys_getsid 148 common fdatasync sys_fdatasync -149 common _sysctl sys_sysctl +149 common _sysctl sys_ni_syscall 150 common mlock sys_mlock 151 common munlock sys_munlock 152 common mlockall sys_mlockall @@ -441,5 +441,7 @@ 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open 435 common clone3 sys_clone3 +436 common close_range sys_close_range 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd +439 common faccessat2 sys_faccessat2 diff --git a/linux-user/mips/syscall-args-o32.c.inc b/linux-user/mips/syscall-args-o32.c.inc index 0ad35857b4..92ee4f921e 100644 --- a/linux-user/mips/syscall-args-o32.c.inc +++ b/linux-user/mips/syscall-args-o32.c.inc @@ -434,3 +434,7 @@ [ 433] = 3, /* fspick */ [ 434] = 2, /* pidfd_open */ [ 435] = 2, /* clone3 */ + [ 436] = 3, /* close_range */ + [ 437] = 4, /* openat2 */ + [ 438] = 3, /* pidfd_getfd */ + [ 439] = 4, /* faccessat2 */ diff --git a/linux-user/mips/syscall_o32.tbl b/linux-user/mips/syscall_o32.tbl index ac586774c9..195b43cf27 100644 --- a/linux-user/mips/syscall_o32.tbl +++ b/linux-user/mips/syscall_o32.tbl @@ -164,7 +164,7 @@ 150 o32 unused150 sys_ni_syscall 151 o32 getsid sys_getsid 152 o32 fdatasync sys_fdatasync -153 o32 _sysctl sys_sysctl compat_sys_sysctl +153 o32 _sysctl sys_ni_syscall 154 o32 mlock sys_mlock 155 o32 munlock sys_munlock 156 o32 mlockall sys_mlockall @@ -184,7 +184,7 @@ 170 o32 connect sys_connect 171 o32 getpeername sys_getpeername 172 o32 getsockname sys_getsockname -173 o32 getsockopt sys_getsockopt compat_sys_getsockopt +173 o32 getsockopt sys_getsockopt sys_getsockopt 174 o32 listen sys_listen 175 o32 recv sys_recv compat_sys_recv 176 o32 recvfrom sys_recvfrom compat_sys_recvfrom @@ -192,7 +192,7 @@ 178 o32 send sys_send 179 o32 sendmsg sys_sendmsg compat_sys_sendmsg 180 o32 sendto sys_sendto -181 o32 setsockopt sys_setsockopt compat_sys_setsockopt +181 o32 setsockopt sys_setsockopt sys_setsockopt 182 o32 shutdown sys_shutdown 183 o32 socket sys_socket 184 o32 socketpair sys_socketpair @@ -423,5 +423,7 @@ 433 o32 fspick sys_fspick 434 o32 pidfd_open sys_pidfd_open 435 o32 clone3 __sys_clone3 +436 o32 close_range sys_close_range 437 o32 openat2 sys_openat2 438 o32 pidfd_getfd sys_pidfd_getfd +439 o32 faccessat2 sys_faccessat2 diff --git a/linux-user/mips64/syscall_n32.tbl b/linux-user/mips64/syscall_n32.tbl index 1f9e8ad636..f9df9edb67 100644 --- a/linux-user/mips64/syscall_n32.tbl +++ b/linux-user/mips64/syscall_n32.tbl @@ -60,8 +60,8 @@ 50 n32 getsockname sys_getsockname 51 n32 getpeername sys_getpeername 52 n32 socketpair sys_socketpair -53 n32 setsockopt compat_sys_setsockopt -54 n32 getsockopt compat_sys_getsockopt +53 n32 setsockopt sys_setsockopt +54 n32 getsockopt sys_getsockopt 55 n32 clone __sys_clone 56 n32 fork __sys_fork 57 n32 execve compat_sys_execve @@ -159,7 +159,7 @@ 149 n32 munlockall sys_munlockall 150 n32 vhangup sys_vhangup 151 n32 pivot_root sys_pivot_root -152 n32 _sysctl compat_sys_sysctl +152 n32 _sysctl sys_ni_syscall 153 n32 prctl sys_prctl 154 n32 adjtimex sys_adjtimex_time32 155 n32 setrlimit compat_sys_setrlimit @@ -374,5 +374,7 @@ 433 n32 fspick sys_fspick 434 n32 pidfd_open sys_pidfd_open 435 n32 clone3 __sys_clone3 +436 n32 close_range sys_close_range 437 n32 openat2 sys_openat2 438 n32 pidfd_getfd sys_pidfd_getfd +439 n32 faccessat2 sys_faccessat2 diff --git a/linux-user/mips64/syscall_n64.tbl b/linux-user/mips64/syscall_n64.tbl index c0b9d802db..557f9954a2 100644 --- a/linux-user/mips64/syscall_n64.tbl +++ b/linux-user/mips64/syscall_n64.tbl @@ -159,7 +159,7 @@ 149 n64 munlockall sys_munlockall 150 n64 vhangup sys_vhangup 151 n64 pivot_root sys_pivot_root -152 n64 _sysctl sys_sysctl +152 n64 _sysctl sys_ni_syscall 153 n64 prctl sys_prctl 154 n64 adjtimex sys_adjtimex 155 n64 setrlimit sys_setrlimit @@ -350,5 +350,7 @@ 433 n64 fspick sys_fspick 434 n64 pidfd_open sys_pidfd_open 435 n64 clone3 __sys_clone3 +436 n64 close_range sys_close_range 437 n64 openat2 sys_openat2 438 n64 pidfd_getfd sys_pidfd_getfd +439 n64 faccessat2 sys_faccessat2 diff --git a/linux-user/mmap.c b/linux-user/mmap.c index f261563420..00c05e6a0f 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -83,6 +83,22 @@ static int validate_prot_to_pageflags(int *host_prot, int prot) *host_prot = (prot & (PROT_READ | PROT_WRITE)) | (prot & PROT_EXEC ? PROT_READ : 0); +#ifdef TARGET_AARCH64 + /* + * The PROT_BTI bit is only accepted if the cpu supports the feature. + * Since this is the unusual case, don't bother checking unless + * the bit has been requested. If set and valid, record the bit + * within QEMU's page_flags. + */ + if (prot & TARGET_PROT_BTI) { + ARMCPU *cpu = ARM_CPU(thread_cpu); + if (cpu_isar_feature(aa64_bti, cpu)) { + valid |= TARGET_PROT_BTI; + page_flags |= PAGE_BTI; + } + } +#endif + return prot & ~valid ? 0 : page_flags; } diff --git a/linux-user/nios2/syscall_nr.h b/linux-user/nios2/syscall_nr.h index 32d485dc9a..e37f40179b 100644 --- a/linux-user/nios2/syscall_nr.h +++ b/linux-user/nios2/syscall_nr.h @@ -318,7 +318,10 @@ #define TARGET_NR_fsmount 432 #define TARGET_NR_fspick 433 #define TARGET_NR_pidfd_open 434 -#define TARGET_NR_syscalls 436 +#define TARGET_NR_close_range 436 +#define TARGET_NR_openat2 437 +#define TARGET_NR_pidfd_getfd 438 +#define TARGET_NR_faccessat2 439 +#define TARGET_NR_syscalls 440 #endif /* LINUX_USER_NIOS2_SYSCALL_NR_H */ - diff --git a/linux-user/openrisc/syscall_nr.h b/linux-user/openrisc/syscall_nr.h index 340383beb2..a8fc029510 100644 --- a/linux-user/openrisc/syscall_nr.h +++ b/linux-user/openrisc/syscall_nr.h @@ -318,7 +318,11 @@ #define TARGET_NR_fsmount 432 #define TARGET_NR_fspick 433 #define TARGET_NR_pidfd_open 434 -#define TARGET_NR_syscalls 436 +#define TARGET_NR_clone3 435 +#define TARGET_NR_close_range 436 +#define TARGET_NR_openat2 437 +#define TARGET_NR_pidfd_getfd 438 +#define TARGET_NR_faccessat2 439 +#define TARGET_NR_syscalls 440 #endif /* LINUX_USER_OPENRISC_SYSCALL_NR_H */ - diff --git a/linux-user/ppc/syscall.tbl b/linux-user/ppc/syscall.tbl index 35b61bfc1b..c2d737ff2e 100644 --- a/linux-user/ppc/syscall.tbl +++ b/linux-user/ppc/syscall.tbl @@ -9,7 +9,9 @@ # 0 nospu restart_syscall sys_restart_syscall 1 nospu exit sys_exit -2 nospu fork ppc_fork +2 32 fork ppc_fork sys_fork +2 64 fork sys_fork +2 spu fork sys_ni_syscall 3 common read sys_read 4 common write sys_write 5 common open sys_open compat_sys_open @@ -158,7 +160,9 @@ 119 32 sigreturn sys_sigreturn compat_sys_sigreturn 119 64 sigreturn sys_ni_syscall 119 spu sigreturn sys_ni_syscall -120 nospu clone ppc_clone +120 32 clone ppc_clone sys_clone +120 64 clone sys_clone +120 spu clone sys_ni_syscall 121 common setdomainname sys_setdomainname 122 common uname sys_newuname 123 common modify_ldt sys_ni_syscall @@ -193,7 +197,7 @@ 146 common writev sys_writev compat_sys_writev 147 common getsid sys_getsid 148 common fdatasync sys_fdatasync -149 nospu _sysctl sys_sysctl compat_sys_sysctl +149 nospu _sysctl sys_ni_syscall 150 common mlock sys_mlock 151 common munlock sys_munlock 152 common mlockall sys_mlockall @@ -240,7 +244,9 @@ 186 spu sendfile sys_sendfile64 187 common getpmsg sys_ni_syscall 188 common putpmsg sys_ni_syscall -189 nospu vfork ppc_vfork +189 32 vfork ppc_vfork sys_vfork +189 64 vfork sys_vfork +189 spu vfork sys_ni_syscall 190 common ugetrlimit sys_getrlimit compat_sys_getrlimit 191 common readahead sys_readahead compat_sys_readahead 192 32 mmap2 sys_mmap2 compat_sys_mmap2 @@ -316,8 +322,8 @@ 248 32 clock_nanosleep sys_clock_nanosleep_time32 248 64 clock_nanosleep sys_clock_nanosleep 248 spu clock_nanosleep sys_clock_nanosleep -249 32 swapcontext ppc_swapcontext ppc32_swapcontext -249 64 swapcontext ppc64_swapcontext +249 32 swapcontext ppc_swapcontext compat_sys_swapcontext +249 64 swapcontext sys_swapcontext 249 spu swapcontext sys_ni_syscall 250 common tgkill sys_tgkill 251 32 utimes sys_utimes_time32 @@ -427,8 +433,8 @@ 336 common recv sys_recv compat_sys_recv 337 common recvfrom sys_recvfrom compat_sys_recvfrom 338 common shutdown sys_shutdown -339 common setsockopt sys_setsockopt compat_sys_setsockopt -340 common getsockopt sys_getsockopt compat_sys_getsockopt +339 common setsockopt sys_setsockopt sys_setsockopt +340 common getsockopt sys_getsockopt sys_getsockopt 341 common sendmsg sys_sendmsg compat_sys_sendmsg 342 common recvmsg sys_recvmsg compat_sys_recvmsg 343 32 recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32 @@ -456,7 +462,7 @@ 361 common bpf sys_bpf 362 nospu execveat sys_execveat compat_sys_execveat 363 32 switch_endian sys_ni_syscall -363 64 switch_endian ppc_switch_endian +363 64 switch_endian sys_switch_endian 363 spu switch_endian sys_ni_syscall 364 common userfaultfd sys_userfaultfd 365 common membarrier sys_membarrier @@ -516,6 +522,10 @@ 432 common fsmount sys_fsmount 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open -435 nospu clone3 ppc_clone3 +435 32 clone3 ppc_clone3 sys_clone3 +435 64 clone3 sys_clone3 +435 spu clone3 sys_ni_syscall +436 common close_range sys_close_range 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd +439 common faccessat2 sys_faccessat2 diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 941ca99722..534753ca12 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -61,6 +61,10 @@ struct image_info { abi_ulong interpreter_loadmap_addr; abi_ulong interpreter_pt_dynamic_addr; struct image_info *other_info; + + /* For target-specific processing of NT_GNU_PROPERTY_TYPE_0. */ + uint32_t note_flags; + #ifdef TARGET_MIPS int fp_abi; int interp_fp_abi; diff --git a/linux-user/riscv/syscall32_nr.h b/linux-user/riscv/syscall32_nr.h index 4fef73e954..079b804dae 100644 --- a/linux-user/riscv/syscall32_nr.h +++ b/linux-user/riscv/syscall32_nr.h @@ -1,5 +1,7 @@ /* * This file contains the system call numbers. + * Do not modify. + * This file is generated by scripts/gensyscalls.sh */ #ifndef LINUX_USER_RISCV_SYSCALL32_NR_H #define LINUX_USER_RISCV_SYSCALL32_NR_H @@ -290,6 +292,10 @@ #define TARGET_NR_fspick 433 #define TARGET_NR_pidfd_open 434 #define TARGET_NR_clone3 435 -#define TARGET_NR_syscalls 436 +#define TARGET_NR_close_range 436 +#define TARGET_NR_openat2 437 +#define TARGET_NR_pidfd_getfd 438 +#define TARGET_NR_faccessat2 439 +#define TARGET_NR_syscalls 440 #endif /* LINUX_USER_RISCV_SYSCALL32_NR_H */ diff --git a/linux-user/riscv/syscall64_nr.h b/linux-user/riscv/syscall64_nr.h index cc82f3244f..d54224ccec 100644 --- a/linux-user/riscv/syscall64_nr.h +++ b/linux-user/riscv/syscall64_nr.h @@ -1,5 +1,7 @@ /* * This file contains the system call numbers. + * Do not modify. + * This file is generated by scripts/gensyscalls.sh */ #ifndef LINUX_USER_RISCV_SYSCALL64_NR_H #define LINUX_USER_RISCV_SYSCALL64_NR_H @@ -296,6 +298,10 @@ #define TARGET_NR_fspick 433 #define TARGET_NR_pidfd_open 434 #define TARGET_NR_clone3 435 -#define TARGET_NR_syscalls 436 +#define TARGET_NR_close_range 436 +#define TARGET_NR_openat2 437 +#define TARGET_NR_pidfd_getfd 438 +#define TARGET_NR_faccessat2 439 +#define TARGET_NR_syscalls 440 #endif /* LINUX_USER_RISCV_SYSCALL64_NR_H */ diff --git a/linux-user/s390x/syscall.tbl b/linux-user/s390x/syscall.tbl index bd7bd3581a..10456bc936 100644 --- a/linux-user/s390x/syscall.tbl +++ b/linux-user/s390x/syscall.tbl @@ -138,7 +138,7 @@ 146 common writev sys_writev compat_sys_writev 147 common getsid sys_getsid sys_getsid 148 common fdatasync sys_fdatasync sys_fdatasync -149 common _sysctl sys_sysctl compat_sys_sysctl +149 common _sysctl - - 150 common mlock sys_mlock sys_mlock 151 common munlock sys_munlock sys_munlock 152 common mlockall sys_mlockall sys_mlockall @@ -372,8 +372,8 @@ 362 common connect sys_connect sys_connect 363 common listen sys_listen sys_listen 364 common accept4 sys_accept4 sys_accept4 -365 common getsockopt sys_getsockopt compat_sys_getsockopt -366 common setsockopt sys_setsockopt compat_sys_setsockopt +365 common getsockopt sys_getsockopt sys_getsockopt +366 common setsockopt sys_setsockopt sys_setsockopt 367 common getsockname sys_getsockname sys_getsockname 368 common getpeername sys_getpeername sys_getpeername 369 common sendto sys_sendto sys_sendto @@ -438,5 +438,7 @@ 433 common fspick sys_fspick sys_fspick 434 common pidfd_open sys_pidfd_open sys_pidfd_open 435 common clone3 sys_clone3 sys_clone3 +436 common close_range sys_close_range sys_close_range 437 common openat2 sys_openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd sys_pidfd_getfd +439 common faccessat2 sys_faccessat2 sys_faccessat2 diff --git a/linux-user/sh4/syscall.tbl b/linux-user/sh4/syscall.tbl index c7a30fcd13..ae0a00beea 100644 --- a/linux-user/sh4/syscall.tbl +++ b/linux-user/sh4/syscall.tbl @@ -156,7 +156,7 @@ 146 common writev sys_writev 147 common getsid sys_getsid 148 common fdatasync sys_fdatasync -149 common _sysctl sys_sysctl +149 common _sysctl sys_ni_syscall 150 common mlock sys_mlock 151 common munlock sys_munlock 152 common mlockall sys_mlockall @@ -438,5 +438,7 @@ 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open # 435 reserved for clone3 +436 common close_range sys_close_range 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd +439 common faccessat2 sys_faccessat2 diff --git a/linux-user/sparc/syscall.tbl b/linux-user/sparc/syscall.tbl index f13615ecde..4af114e84f 100644 --- a/linux-user/sparc/syscall.tbl +++ b/linux-user/sparc/syscall.tbl @@ -147,7 +147,7 @@ 115 32 getgroups32 sys_getgroups 116 common gettimeofday sys_gettimeofday compat_sys_gettimeofday 117 common getrusage sys_getrusage compat_sys_getrusage -118 common getsockopt sys_getsockopt compat_sys_getsockopt +118 common getsockopt sys_getsockopt sys_getsockopt 119 common getcwd sys_getcwd 120 common readv sys_readv compat_sys_readv 121 common writev sys_writev compat_sys_writev @@ -300,7 +300,7 @@ 249 64 nanosleep sys_nanosleep 250 32 mremap sys_mremap 250 64 mremap sys_64_mremap -251 common _sysctl sys_sysctl compat_sys_sysctl +251 common _sysctl sys_ni_syscall 252 common getsid sys_getsid 253 common fdatasync sys_fdatasync 254 32 nfsservctl sys_ni_syscall sys_nis_syscall @@ -425,7 +425,7 @@ 352 common userfaultfd sys_userfaultfd 353 common bind sys_bind 354 common listen sys_listen -355 common setsockopt sys_setsockopt compat_sys_setsockopt +355 common setsockopt sys_setsockopt sys_setsockopt 356 common mlock2 sys_mlock2 357 common copy_file_range sys_copy_file_range 358 common preadv2 sys_preadv2 compat_sys_preadv2 @@ -481,5 +481,7 @@ 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open # 435 reserved for clone3 +436 common close_range sys_close_range 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd +439 common faccessat2 sys_faccessat2 diff --git a/linux-user/sparc64/syscall.tbl b/linux-user/sparc64/syscall.tbl index f13615ecde..4af114e84f 100644 --- a/linux-user/sparc64/syscall.tbl +++ b/linux-user/sparc64/syscall.tbl @@ -147,7 +147,7 @@ 115 32 getgroups32 sys_getgroups 116 common gettimeofday sys_gettimeofday compat_sys_gettimeofday 117 common getrusage sys_getrusage compat_sys_getrusage -118 common getsockopt sys_getsockopt compat_sys_getsockopt +118 common getsockopt sys_getsockopt sys_getsockopt 119 common getcwd sys_getcwd 120 common readv sys_readv compat_sys_readv 121 common writev sys_writev compat_sys_writev @@ -300,7 +300,7 @@ 249 64 nanosleep sys_nanosleep 250 32 mremap sys_mremap 250 64 mremap sys_64_mremap -251 common _sysctl sys_sysctl compat_sys_sysctl +251 common _sysctl sys_ni_syscall 252 common getsid sys_getsid 253 common fdatasync sys_fdatasync 254 32 nfsservctl sys_ni_syscall sys_nis_syscall @@ -425,7 +425,7 @@ 352 common userfaultfd sys_userfaultfd 353 common bind sys_bind 354 common listen sys_listen -355 common setsockopt sys_setsockopt compat_sys_setsockopt +355 common setsockopt sys_setsockopt sys_setsockopt 356 common mlock2 sys_mlock2 357 common copy_file_range sys_copy_file_range 358 common preadv2 sys_preadv2 compat_sys_preadv2 @@ -481,5 +481,7 @@ 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open # 435 reserved for clone3 +436 common close_range sys_close_range 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd +439 common faccessat2 sys_faccessat2 diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 897d20c076..6fef8181e7 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -3491,16 +3491,16 @@ static abi_long do_accept4(int fd, abi_ulong target_addr, return get_errno(safe_accept4(fd, NULL, NULL, host_flags)); } - /* linux returns EINVAL if addrlen pointer is invalid */ + /* linux returns EFAULT if addrlen pointer is invalid */ if (get_user_u32(addrlen, target_addrlen_addr)) - return -TARGET_EINVAL; + return -TARGET_EFAULT; if ((int)addrlen < 0) { return -TARGET_EINVAL; } if (!access_ok(VERIFY_WRITE, target_addr, addrlen)) - return -TARGET_EINVAL; + return -TARGET_EFAULT; addr = alloca(addrlen); @@ -9787,6 +9787,11 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, __put_user(stfs.f_fsid.__val[1], &target_stfs->f_fsid.val[1]); __put_user(stfs.f_namelen, &target_stfs->f_namelen); __put_user(stfs.f_frsize, &target_stfs->f_frsize); +#ifdef _STATFS_F_FLAGS + __put_user(stfs.f_flags, &target_stfs->f_flags); +#else + __put_user(0, &target_stfs->f_flags); +#endif memset(target_stfs->f_spare, 0, sizeof(target_stfs->f_spare)); unlock_user_struct(target_stfs, arg3, 1); } @@ -10525,12 +10530,6 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, case TARGET_NR_fdatasync: return get_errno(fdatasync(arg1)); #endif -#ifdef TARGET_NR__sysctl - case TARGET_NR__sysctl: - /* We don't implement this, but ENOTDIR is always a safe - return value. */ - return -TARGET_ENOTDIR; -#endif case TARGET_NR_sched_getaffinity: { unsigned int mask_size; @@ -10703,7 +10702,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, int deathsig; ret = get_errno(prctl(arg1, &deathsig, arg3, arg4, arg5)); if (!is_error(ret) && arg2 - && put_user_ual(deathsig, arg2)) { + && put_user_s32(deathsig, arg2)) { return -TARGET_EFAULT; } return ret; diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 731c3d5341..cabbfb762d 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -1277,6 +1277,10 @@ struct target_winsize { #define TARGET_PROT_SEM 0x08 #endif +#ifdef TARGET_AARCH64 +#define TARGET_PROT_BTI 0x10 +#endif + /* Common */ #define TARGET_MAP_SHARED 0x01 /* Share changes */ #define TARGET_MAP_PRIVATE 0x02 /* Changes are private */ diff --git a/linux-user/x86_64/syscall_64.tbl b/linux-user/x86_64/syscall_64.tbl index 44d510bc9b..f30d6ae9a6 100644 --- a/linux-user/x86_64/syscall_64.tbl +++ b/linux-user/x86_64/syscall_64.tbl @@ -8,357 +8,359 @@ # # The abi is "common", "64" or "x32" for this file. # -0 common read __x64_sys_read -1 common write __x64_sys_write -2 common open __x64_sys_open -3 common close __x64_sys_close -4 common stat __x64_sys_newstat -5 common fstat __x64_sys_newfstat -6 common lstat __x64_sys_newlstat -7 common poll __x64_sys_poll -8 common lseek __x64_sys_lseek -9 common mmap __x64_sys_mmap -10 common mprotect __x64_sys_mprotect -11 common munmap __x64_sys_munmap -12 common brk __x64_sys_brk -13 64 rt_sigaction __x64_sys_rt_sigaction -14 common rt_sigprocmask __x64_sys_rt_sigprocmask -15 64 rt_sigreturn __x64_sys_rt_sigreturn/ptregs -16 64 ioctl __x64_sys_ioctl -17 common pread64 __x64_sys_pread64 -18 common pwrite64 __x64_sys_pwrite64 -19 64 readv __x64_sys_readv -20 64 writev __x64_sys_writev -21 common access __x64_sys_access -22 common pipe __x64_sys_pipe -23 common select __x64_sys_select -24 common sched_yield __x64_sys_sched_yield -25 common mremap __x64_sys_mremap -26 common msync __x64_sys_msync -27 common mincore __x64_sys_mincore -28 common madvise __x64_sys_madvise -29 common shmget __x64_sys_shmget -30 common shmat __x64_sys_shmat -31 common shmctl __x64_sys_shmctl -32 common dup __x64_sys_dup -33 common dup2 __x64_sys_dup2 -34 common pause __x64_sys_pause -35 common nanosleep __x64_sys_nanosleep -36 common getitimer __x64_sys_getitimer -37 common alarm __x64_sys_alarm -38 common setitimer __x64_sys_setitimer -39 common getpid __x64_sys_getpid -40 common sendfile __x64_sys_sendfile64 -41 common socket __x64_sys_socket -42 common connect __x64_sys_connect -43 common accept __x64_sys_accept -44 common sendto __x64_sys_sendto -45 64 recvfrom __x64_sys_recvfrom -46 64 sendmsg __x64_sys_sendmsg -47 64 recvmsg __x64_sys_recvmsg -48 common shutdown __x64_sys_shutdown -49 common bind __x64_sys_bind -50 common listen __x64_sys_listen -51 common getsockname __x64_sys_getsockname -52 common getpeername __x64_sys_getpeername -53 common socketpair __x64_sys_socketpair -54 64 setsockopt __x64_sys_setsockopt -55 64 getsockopt __x64_sys_getsockopt -56 common clone __x64_sys_clone/ptregs -57 common fork __x64_sys_fork/ptregs -58 common vfork __x64_sys_vfork/ptregs -59 64 execve __x64_sys_execve/ptregs -60 common exit __x64_sys_exit -61 common wait4 __x64_sys_wait4 -62 common kill __x64_sys_kill -63 common uname __x64_sys_newuname -64 common semget __x64_sys_semget -65 common semop __x64_sys_semop -66 common semctl __x64_sys_semctl -67 common shmdt __x64_sys_shmdt -68 common msgget __x64_sys_msgget -69 common msgsnd __x64_sys_msgsnd -70 common msgrcv __x64_sys_msgrcv -71 common msgctl __x64_sys_msgctl -72 common fcntl __x64_sys_fcntl -73 common flock __x64_sys_flock -74 common fsync __x64_sys_fsync -75 common fdatasync __x64_sys_fdatasync -76 common truncate __x64_sys_truncate -77 common ftruncate __x64_sys_ftruncate -78 common getdents __x64_sys_getdents -79 common getcwd __x64_sys_getcwd -80 common chdir __x64_sys_chdir -81 common fchdir __x64_sys_fchdir -82 common rename __x64_sys_rename -83 common mkdir __x64_sys_mkdir -84 common rmdir __x64_sys_rmdir -85 common creat __x64_sys_creat -86 common link __x64_sys_link -87 common unlink __x64_sys_unlink -88 common symlink __x64_sys_symlink -89 common readlink __x64_sys_readlink -90 common chmod __x64_sys_chmod -91 common fchmod __x64_sys_fchmod -92 common chown __x64_sys_chown -93 common fchown __x64_sys_fchown -94 common lchown __x64_sys_lchown -95 common umask __x64_sys_umask -96 common gettimeofday __x64_sys_gettimeofday -97 common getrlimit __x64_sys_getrlimit -98 common getrusage __x64_sys_getrusage -99 common sysinfo __x64_sys_sysinfo -100 common times __x64_sys_times -101 64 ptrace __x64_sys_ptrace -102 common getuid __x64_sys_getuid -103 common syslog __x64_sys_syslog -104 common getgid __x64_sys_getgid -105 common setuid __x64_sys_setuid -106 common setgid __x64_sys_setgid -107 common geteuid __x64_sys_geteuid -108 common getegid __x64_sys_getegid -109 common setpgid __x64_sys_setpgid -110 common getppid __x64_sys_getppid -111 common getpgrp __x64_sys_getpgrp -112 common setsid __x64_sys_setsid -113 common setreuid __x64_sys_setreuid -114 common setregid __x64_sys_setregid -115 common getgroups __x64_sys_getgroups -116 common setgroups __x64_sys_setgroups -117 common setresuid __x64_sys_setresuid -118 common getresuid __x64_sys_getresuid -119 common setresgid __x64_sys_setresgid -120 common getresgid __x64_sys_getresgid -121 common getpgid __x64_sys_getpgid -122 common setfsuid __x64_sys_setfsuid -123 common setfsgid __x64_sys_setfsgid -124 common getsid __x64_sys_getsid -125 common capget __x64_sys_capget -126 common capset __x64_sys_capset -127 64 rt_sigpending __x64_sys_rt_sigpending -128 64 rt_sigtimedwait __x64_sys_rt_sigtimedwait -129 64 rt_sigqueueinfo __x64_sys_rt_sigqueueinfo -130 common rt_sigsuspend __x64_sys_rt_sigsuspend -131 64 sigaltstack __x64_sys_sigaltstack -132 common utime __x64_sys_utime -133 common mknod __x64_sys_mknod +0 common read sys_read +1 common write sys_write +2 common open sys_open +3 common close sys_close +4 common stat sys_newstat +5 common fstat sys_newfstat +6 common lstat sys_newlstat +7 common poll sys_poll +8 common lseek sys_lseek +9 common mmap sys_mmap +10 common mprotect sys_mprotect +11 common munmap sys_munmap +12 common brk sys_brk +13 64 rt_sigaction sys_rt_sigaction +14 common rt_sigprocmask sys_rt_sigprocmask +15 64 rt_sigreturn sys_rt_sigreturn +16 64 ioctl sys_ioctl +17 common pread64 sys_pread64 +18 common pwrite64 sys_pwrite64 +19 64 readv sys_readv +20 64 writev sys_writev +21 common access sys_access +22 common pipe sys_pipe +23 common select sys_select +24 common sched_yield sys_sched_yield +25 common mremap sys_mremap +26 common msync sys_msync +27 common mincore sys_mincore +28 common madvise sys_madvise +29 common shmget sys_shmget +30 common shmat sys_shmat +31 common shmctl sys_shmctl +32 common dup sys_dup +33 common dup2 sys_dup2 +34 common pause sys_pause +35 common nanosleep sys_nanosleep +36 common getitimer sys_getitimer +37 common alarm sys_alarm +38 common setitimer sys_setitimer +39 common getpid sys_getpid +40 common sendfile sys_sendfile64 +41 common socket sys_socket +42 common connect sys_connect +43 common accept sys_accept +44 common sendto sys_sendto +45 64 recvfrom sys_recvfrom +46 64 sendmsg sys_sendmsg +47 64 recvmsg sys_recvmsg +48 common shutdown sys_shutdown +49 common bind sys_bind +50 common listen sys_listen +51 common getsockname sys_getsockname +52 common getpeername sys_getpeername +53 common socketpair sys_socketpair +54 64 setsockopt sys_setsockopt +55 64 getsockopt sys_getsockopt +56 common clone sys_clone +57 common fork sys_fork +58 common vfork sys_vfork +59 64 execve sys_execve +60 common exit sys_exit +61 common wait4 sys_wait4 +62 common kill sys_kill +63 common uname sys_newuname +64 common semget sys_semget +65 common semop sys_semop +66 common semctl sys_semctl +67 common shmdt sys_shmdt +68 common msgget sys_msgget +69 common msgsnd sys_msgsnd +70 common msgrcv sys_msgrcv +71 common msgctl sys_msgctl +72 common fcntl sys_fcntl +73 common flock sys_flock +74 common fsync sys_fsync +75 common fdatasync sys_fdatasync +76 common truncate sys_truncate +77 common ftruncate sys_ftruncate +78 common getdents sys_getdents +79 common getcwd sys_getcwd +80 common chdir sys_chdir +81 common fchdir sys_fchdir +82 common rename sys_rename +83 common mkdir sys_mkdir +84 common rmdir sys_rmdir +85 common creat sys_creat +86 common link sys_link +87 common unlink sys_unlink +88 common symlink sys_symlink +89 common readlink sys_readlink +90 common chmod sys_chmod +91 common fchmod sys_fchmod +92 common chown sys_chown +93 common fchown sys_fchown +94 common lchown sys_lchown +95 common umask sys_umask +96 common gettimeofday sys_gettimeofday +97 common getrlimit sys_getrlimit +98 common getrusage sys_getrusage +99 common sysinfo sys_sysinfo +100 common times sys_times +101 64 ptrace sys_ptrace +102 common getuid sys_getuid +103 common syslog sys_syslog +104 common getgid sys_getgid +105 common setuid sys_setuid +106 common setgid sys_setgid +107 common geteuid sys_geteuid +108 common getegid sys_getegid +109 common setpgid sys_setpgid +110 common getppid sys_getppid +111 common getpgrp sys_getpgrp +112 common setsid sys_setsid +113 common setreuid sys_setreuid +114 common setregid sys_setregid +115 common getgroups sys_getgroups +116 common setgroups sys_setgroups +117 common setresuid sys_setresuid +118 common getresuid sys_getresuid +119 common setresgid sys_setresgid +120 common getresgid sys_getresgid +121 common getpgid sys_getpgid +122 common setfsuid sys_setfsuid +123 common setfsgid sys_setfsgid +124 common getsid sys_getsid +125 common capget sys_capget +126 common capset sys_capset +127 64 rt_sigpending sys_rt_sigpending +128 64 rt_sigtimedwait sys_rt_sigtimedwait +129 64 rt_sigqueueinfo sys_rt_sigqueueinfo +130 common rt_sigsuspend sys_rt_sigsuspend +131 64 sigaltstack sys_sigaltstack +132 common utime sys_utime +133 common mknod sys_mknod 134 64 uselib -135 common personality __x64_sys_personality -136 common ustat __x64_sys_ustat -137 common statfs __x64_sys_statfs -138 common fstatfs __x64_sys_fstatfs -139 common sysfs __x64_sys_sysfs -140 common getpriority __x64_sys_getpriority -141 common setpriority __x64_sys_setpriority -142 common sched_setparam __x64_sys_sched_setparam -143 common sched_getparam __x64_sys_sched_getparam -144 common sched_setscheduler __x64_sys_sched_setscheduler -145 common sched_getscheduler __x64_sys_sched_getscheduler -146 common sched_get_priority_max __x64_sys_sched_get_priority_max -147 common sched_get_priority_min __x64_sys_sched_get_priority_min -148 common sched_rr_get_interval __x64_sys_sched_rr_get_interval -149 common mlock __x64_sys_mlock -150 common munlock __x64_sys_munlock -151 common mlockall __x64_sys_mlockall -152 common munlockall __x64_sys_munlockall -153 common vhangup __x64_sys_vhangup -154 common modify_ldt __x64_sys_modify_ldt -155 common pivot_root __x64_sys_pivot_root -156 64 _sysctl __x64_sys_sysctl -157 common prctl __x64_sys_prctl -158 common arch_prctl __x64_sys_arch_prctl -159 common adjtimex __x64_sys_adjtimex -160 common setrlimit __x64_sys_setrlimit -161 common chroot __x64_sys_chroot -162 common sync __x64_sys_sync -163 common acct __x64_sys_acct -164 common settimeofday __x64_sys_settimeofday -165 common mount __x64_sys_mount -166 common umount2 __x64_sys_umount -167 common swapon __x64_sys_swapon -168 common swapoff __x64_sys_swapoff -169 common reboot __x64_sys_reboot -170 common sethostname __x64_sys_sethostname -171 common setdomainname __x64_sys_setdomainname -172 common iopl __x64_sys_iopl/ptregs -173 common ioperm __x64_sys_ioperm +135 common personality sys_personality +136 common ustat sys_ustat +137 common statfs sys_statfs +138 common fstatfs sys_fstatfs +139 common sysfs sys_sysfs +140 common getpriority sys_getpriority +141 common setpriority sys_setpriority +142 common sched_setparam sys_sched_setparam +143 common sched_getparam sys_sched_getparam +144 common sched_setscheduler sys_sched_setscheduler +145 common sched_getscheduler sys_sched_getscheduler +146 common sched_get_priority_max sys_sched_get_priority_max +147 common sched_get_priority_min sys_sched_get_priority_min +148 common sched_rr_get_interval sys_sched_rr_get_interval +149 common mlock sys_mlock +150 common munlock sys_munlock +151 common mlockall sys_mlockall +152 common munlockall sys_munlockall +153 common vhangup sys_vhangup +154 common modify_ldt sys_modify_ldt +155 common pivot_root sys_pivot_root +156 64 _sysctl sys_ni_syscall +157 common prctl sys_prctl +158 common arch_prctl sys_arch_prctl +159 common adjtimex sys_adjtimex +160 common setrlimit sys_setrlimit +161 common chroot sys_chroot +162 common sync sys_sync +163 common acct sys_acct +164 common settimeofday sys_settimeofday +165 common mount sys_mount +166 common umount2 sys_umount +167 common swapon sys_swapon +168 common swapoff sys_swapoff +169 common reboot sys_reboot +170 common sethostname sys_sethostname +171 common setdomainname sys_setdomainname +172 common iopl sys_iopl +173 common ioperm sys_ioperm 174 64 create_module -175 common init_module __x64_sys_init_module -176 common delete_module __x64_sys_delete_module +175 common init_module sys_init_module +176 common delete_module sys_delete_module 177 64 get_kernel_syms 178 64 query_module -179 common quotactl __x64_sys_quotactl +179 common quotactl sys_quotactl 180 64 nfsservctl 181 common getpmsg 182 common putpmsg 183 common afs_syscall 184 common tuxcall 185 common security -186 common gettid __x64_sys_gettid -187 common readahead __x64_sys_readahead -188 common setxattr __x64_sys_setxattr -189 common lsetxattr __x64_sys_lsetxattr -190 common fsetxattr __x64_sys_fsetxattr -191 common getxattr __x64_sys_getxattr -192 common lgetxattr __x64_sys_lgetxattr -193 common fgetxattr __x64_sys_fgetxattr -194 common listxattr __x64_sys_listxattr -195 common llistxattr __x64_sys_llistxattr -196 common flistxattr __x64_sys_flistxattr -197 common removexattr __x64_sys_removexattr -198 common lremovexattr __x64_sys_lremovexattr -199 common fremovexattr __x64_sys_fremovexattr -200 common tkill __x64_sys_tkill -201 common time __x64_sys_time -202 common futex __x64_sys_futex -203 common sched_setaffinity __x64_sys_sched_setaffinity -204 common sched_getaffinity __x64_sys_sched_getaffinity +186 common gettid sys_gettid +187 common readahead sys_readahead +188 common setxattr sys_setxattr +189 common lsetxattr sys_lsetxattr +190 common fsetxattr sys_fsetxattr +191 common getxattr sys_getxattr +192 common lgetxattr sys_lgetxattr +193 common fgetxattr sys_fgetxattr +194 common listxattr sys_listxattr +195 common llistxattr sys_llistxattr +196 common flistxattr sys_flistxattr +197 common removexattr sys_removexattr +198 common lremovexattr sys_lremovexattr +199 common fremovexattr sys_fremovexattr +200 common tkill sys_tkill +201 common time sys_time +202 common futex sys_futex +203 common sched_setaffinity sys_sched_setaffinity +204 common sched_getaffinity sys_sched_getaffinity 205 64 set_thread_area -206 64 io_setup __x64_sys_io_setup -207 common io_destroy __x64_sys_io_destroy -208 common io_getevents __x64_sys_io_getevents -209 64 io_submit __x64_sys_io_submit -210 common io_cancel __x64_sys_io_cancel +206 64 io_setup sys_io_setup +207 common io_destroy sys_io_destroy +208 common io_getevents sys_io_getevents +209 64 io_submit sys_io_submit +210 common io_cancel sys_io_cancel 211 64 get_thread_area -212 common lookup_dcookie __x64_sys_lookup_dcookie -213 common epoll_create __x64_sys_epoll_create +212 common lookup_dcookie sys_lookup_dcookie +213 common epoll_create sys_epoll_create 214 64 epoll_ctl_old 215 64 epoll_wait_old -216 common remap_file_pages __x64_sys_remap_file_pages -217 common getdents64 __x64_sys_getdents64 -218 common set_tid_address __x64_sys_set_tid_address -219 common restart_syscall __x64_sys_restart_syscall -220 common semtimedop __x64_sys_semtimedop -221 common fadvise64 __x64_sys_fadvise64 -222 64 timer_create __x64_sys_timer_create -223 common timer_settime __x64_sys_timer_settime -224 common timer_gettime __x64_sys_timer_gettime -225 common timer_getoverrun __x64_sys_timer_getoverrun -226 common timer_delete __x64_sys_timer_delete -227 common clock_settime __x64_sys_clock_settime -228 common clock_gettime __x64_sys_clock_gettime -229 common clock_getres __x64_sys_clock_getres -230 common clock_nanosleep __x64_sys_clock_nanosleep -231 common exit_group __x64_sys_exit_group -232 common epoll_wait __x64_sys_epoll_wait -233 common epoll_ctl __x64_sys_epoll_ctl -234 common tgkill __x64_sys_tgkill -235 common utimes __x64_sys_utimes +216 common remap_file_pages sys_remap_file_pages +217 common getdents64 sys_getdents64 +218 common set_tid_address sys_set_tid_address +219 common restart_syscall sys_restart_syscall +220 common semtimedop sys_semtimedop +221 common fadvise64 sys_fadvise64 +222 64 timer_create sys_timer_create +223 common timer_settime sys_timer_settime +224 common timer_gettime sys_timer_gettime +225 common timer_getoverrun sys_timer_getoverrun +226 common timer_delete sys_timer_delete +227 common clock_settime sys_clock_settime +228 common clock_gettime sys_clock_gettime +229 common clock_getres sys_clock_getres +230 common clock_nanosleep sys_clock_nanosleep +231 common exit_group sys_exit_group +232 common epoll_wait sys_epoll_wait +233 common epoll_ctl sys_epoll_ctl +234 common tgkill sys_tgkill +235 common utimes sys_utimes 236 64 vserver -237 common mbind __x64_sys_mbind -238 common set_mempolicy __x64_sys_set_mempolicy -239 common get_mempolicy __x64_sys_get_mempolicy -240 common mq_open __x64_sys_mq_open -241 common mq_unlink __x64_sys_mq_unlink -242 common mq_timedsend __x64_sys_mq_timedsend -243 common mq_timedreceive __x64_sys_mq_timedreceive -244 64 mq_notify __x64_sys_mq_notify -245 common mq_getsetattr __x64_sys_mq_getsetattr -246 64 kexec_load __x64_sys_kexec_load -247 64 waitid __x64_sys_waitid -248 common add_key __x64_sys_add_key -249 common request_key __x64_sys_request_key -250 common keyctl __x64_sys_keyctl -251 common ioprio_set __x64_sys_ioprio_set -252 common ioprio_get __x64_sys_ioprio_get -253 common inotify_init __x64_sys_inotify_init -254 common inotify_add_watch __x64_sys_inotify_add_watch -255 common inotify_rm_watch __x64_sys_inotify_rm_watch -256 common migrate_pages __x64_sys_migrate_pages -257 common openat __x64_sys_openat -258 common mkdirat __x64_sys_mkdirat -259 common mknodat __x64_sys_mknodat -260 common fchownat __x64_sys_fchownat -261 common futimesat __x64_sys_futimesat -262 common newfstatat __x64_sys_newfstatat -263 common unlinkat __x64_sys_unlinkat -264 common renameat __x64_sys_renameat -265 common linkat __x64_sys_linkat -266 common symlinkat __x64_sys_symlinkat -267 common readlinkat __x64_sys_readlinkat -268 common fchmodat __x64_sys_fchmodat -269 common faccessat __x64_sys_faccessat -270 common pselect6 __x64_sys_pselect6 -271 common ppoll __x64_sys_ppoll -272 common unshare __x64_sys_unshare -273 64 set_robust_list __x64_sys_set_robust_list -274 64 get_robust_list __x64_sys_get_robust_list -275 common splice __x64_sys_splice -276 common tee __x64_sys_tee -277 common sync_file_range __x64_sys_sync_file_range -278 64 vmsplice __x64_sys_vmsplice -279 64 move_pages __x64_sys_move_pages -280 common utimensat __x64_sys_utimensat -281 common epoll_pwait __x64_sys_epoll_pwait -282 common signalfd __x64_sys_signalfd -283 common timerfd_create __x64_sys_timerfd_create -284 common eventfd __x64_sys_eventfd -285 common fallocate __x64_sys_fallocate -286 common timerfd_settime __x64_sys_timerfd_settime -287 common timerfd_gettime __x64_sys_timerfd_gettime -288 common accept4 __x64_sys_accept4 -289 common signalfd4 __x64_sys_signalfd4 -290 common eventfd2 __x64_sys_eventfd2 -291 common epoll_create1 __x64_sys_epoll_create1 -292 common dup3 __x64_sys_dup3 -293 common pipe2 __x64_sys_pipe2 -294 common inotify_init1 __x64_sys_inotify_init1 -295 64 preadv __x64_sys_preadv -296 64 pwritev __x64_sys_pwritev -297 64 rt_tgsigqueueinfo __x64_sys_rt_tgsigqueueinfo -298 common perf_event_open __x64_sys_perf_event_open -299 64 recvmmsg __x64_sys_recvmmsg -300 common fanotify_init __x64_sys_fanotify_init -301 common fanotify_mark __x64_sys_fanotify_mark -302 common prlimit64 __x64_sys_prlimit64 -303 common name_to_handle_at __x64_sys_name_to_handle_at -304 common open_by_handle_at __x64_sys_open_by_handle_at -305 common clock_adjtime __x64_sys_clock_adjtime -306 common syncfs __x64_sys_syncfs -307 64 sendmmsg __x64_sys_sendmmsg -308 common setns __x64_sys_setns -309 common getcpu __x64_sys_getcpu -310 64 process_vm_readv __x64_sys_process_vm_readv -311 64 process_vm_writev __x64_sys_process_vm_writev -312 common kcmp __x64_sys_kcmp -313 common finit_module __x64_sys_finit_module -314 common sched_setattr __x64_sys_sched_setattr -315 common sched_getattr __x64_sys_sched_getattr -316 common renameat2 __x64_sys_renameat2 -317 common seccomp __x64_sys_seccomp -318 common getrandom __x64_sys_getrandom -319 common memfd_create __x64_sys_memfd_create -320 common kexec_file_load __x64_sys_kexec_file_load -321 common bpf __x64_sys_bpf -322 64 execveat __x64_sys_execveat/ptregs -323 common userfaultfd __x64_sys_userfaultfd -324 common membarrier __x64_sys_membarrier -325 common mlock2 __x64_sys_mlock2 -326 common copy_file_range __x64_sys_copy_file_range -327 64 preadv2 __x64_sys_preadv2 -328 64 pwritev2 __x64_sys_pwritev2 -329 common pkey_mprotect __x64_sys_pkey_mprotect -330 common pkey_alloc __x64_sys_pkey_alloc -331 common pkey_free __x64_sys_pkey_free -332 common statx __x64_sys_statx -333 common io_pgetevents __x64_sys_io_pgetevents -334 common rseq __x64_sys_rseq +237 common mbind sys_mbind +238 common set_mempolicy sys_set_mempolicy +239 common get_mempolicy sys_get_mempolicy +240 common mq_open sys_mq_open +241 common mq_unlink sys_mq_unlink +242 common mq_timedsend sys_mq_timedsend +243 common mq_timedreceive sys_mq_timedreceive +244 64 mq_notify sys_mq_notify +245 common mq_getsetattr sys_mq_getsetattr +246 64 kexec_load sys_kexec_load +247 64 waitid sys_waitid +248 common add_key sys_add_key +249 common request_key sys_request_key +250 common keyctl sys_keyctl +251 common ioprio_set sys_ioprio_set +252 common ioprio_get sys_ioprio_get +253 common inotify_init sys_inotify_init +254 common inotify_add_watch sys_inotify_add_watch +255 common inotify_rm_watch sys_inotify_rm_watch +256 common migrate_pages sys_migrate_pages +257 common openat sys_openat +258 common mkdirat sys_mkdirat +259 common mknodat sys_mknodat +260 common fchownat sys_fchownat +261 common futimesat sys_futimesat +262 common newfstatat sys_newfstatat +263 common unlinkat sys_unlinkat +264 common renameat sys_renameat +265 common linkat sys_linkat +266 common symlinkat sys_symlinkat +267 common readlinkat sys_readlinkat +268 common fchmodat sys_fchmodat +269 common faccessat sys_faccessat +270 common pselect6 sys_pselect6 +271 common ppoll sys_ppoll +272 common unshare sys_unshare +273 64 set_robust_list sys_set_robust_list +274 64 get_robust_list sys_get_robust_list +275 common splice sys_splice +276 common tee sys_tee +277 common sync_file_range sys_sync_file_range +278 64 vmsplice sys_vmsplice +279 64 move_pages sys_move_pages +280 common utimensat sys_utimensat +281 common epoll_pwait sys_epoll_pwait +282 common signalfd sys_signalfd +283 common timerfd_create sys_timerfd_create +284 common eventfd sys_eventfd +285 common fallocate sys_fallocate +286 common timerfd_settime sys_timerfd_settime +287 common timerfd_gettime sys_timerfd_gettime +288 common accept4 sys_accept4 +289 common signalfd4 sys_signalfd4 +290 common eventfd2 sys_eventfd2 +291 common epoll_create1 sys_epoll_create1 +292 common dup3 sys_dup3 +293 common pipe2 sys_pipe2 +294 common inotify_init1 sys_inotify_init1 +295 64 preadv sys_preadv +296 64 pwritev sys_pwritev +297 64 rt_tgsigqueueinfo sys_rt_tgsigqueueinfo +298 common perf_event_open sys_perf_event_open +299 64 recvmmsg sys_recvmmsg +300 common fanotify_init sys_fanotify_init +301 common fanotify_mark sys_fanotify_mark +302 common prlimit64 sys_prlimit64 +303 common name_to_handle_at sys_name_to_handle_at +304 common open_by_handle_at sys_open_by_handle_at +305 common clock_adjtime sys_clock_adjtime +306 common syncfs sys_syncfs +307 64 sendmmsg sys_sendmmsg +308 common setns sys_setns +309 common getcpu sys_getcpu +310 64 process_vm_readv sys_process_vm_readv +311 64 process_vm_writev sys_process_vm_writev +312 common kcmp sys_kcmp +313 common finit_module sys_finit_module +314 common sched_setattr sys_sched_setattr +315 common sched_getattr sys_sched_getattr +316 common renameat2 sys_renameat2 +317 common seccomp sys_seccomp +318 common getrandom sys_getrandom +319 common memfd_create sys_memfd_create +320 common kexec_file_load sys_kexec_file_load +321 common bpf sys_bpf +322 64 execveat sys_execveat +323 common userfaultfd sys_userfaultfd +324 common membarrier sys_membarrier +325 common mlock2 sys_mlock2 +326 common copy_file_range sys_copy_file_range +327 64 preadv2 sys_preadv2 +328 64 pwritev2 sys_pwritev2 +329 common pkey_mprotect sys_pkey_mprotect +330 common pkey_alloc sys_pkey_alloc +331 common pkey_free sys_pkey_free +332 common statx sys_statx +333 common io_pgetevents sys_io_pgetevents +334 common rseq sys_rseq # don't use numbers 387 through 423, add new calls after the last # 'common' entry -424 common pidfd_send_signal __x64_sys_pidfd_send_signal -425 common io_uring_setup __x64_sys_io_uring_setup -426 common io_uring_enter __x64_sys_io_uring_enter -427 common io_uring_register __x64_sys_io_uring_register -428 common open_tree __x64_sys_open_tree -429 common move_mount __x64_sys_move_mount -430 common fsopen __x64_sys_fsopen -431 common fsconfig __x64_sys_fsconfig -432 common fsmount __x64_sys_fsmount -433 common fspick __x64_sys_fspick -434 common pidfd_open __x64_sys_pidfd_open -435 common clone3 __x64_sys_clone3/ptregs -437 common openat2 __x64_sys_openat2 -438 common pidfd_getfd __x64_sys_pidfd_getfd +424 common pidfd_send_signal sys_pidfd_send_signal +425 common io_uring_setup sys_io_uring_setup +426 common io_uring_enter sys_io_uring_enter +427 common io_uring_register sys_io_uring_register +428 common open_tree sys_open_tree +429 common move_mount sys_move_mount +430 common fsopen sys_fsopen +431 common fsconfig sys_fsconfig +432 common fsmount sys_fsmount +433 common fspick sys_fspick +434 common pidfd_open sys_pidfd_open +435 common clone3 sys_clone3 +436 common close_range sys_close_range +437 common openat2 sys_openat2 +438 common pidfd_getfd sys_pidfd_getfd +439 common faccessat2 sys_faccessat2 # # x32-specific system call numbers start at 512 to avoid cache impact @@ -366,39 +368,39 @@ # on-the-fly for compat_sys_*() compatibility system calls if X86_X32 # is defined. # -512 x32 rt_sigaction __x32_compat_sys_rt_sigaction -513 x32 rt_sigreturn sys32_x32_rt_sigreturn -514 x32 ioctl __x32_compat_sys_ioctl -515 x32 readv __x32_compat_sys_readv -516 x32 writev __x32_compat_sys_writev -517 x32 recvfrom __x32_compat_sys_recvfrom -518 x32 sendmsg __x32_compat_sys_sendmsg -519 x32 recvmsg __x32_compat_sys_recvmsg -520 x32 execve __x32_compat_sys_execve/ptregs -521 x32 ptrace __x32_compat_sys_ptrace -522 x32 rt_sigpending __x32_compat_sys_rt_sigpending -523 x32 rt_sigtimedwait __x32_compat_sys_rt_sigtimedwait_time64 -524 x32 rt_sigqueueinfo __x32_compat_sys_rt_sigqueueinfo -525 x32 sigaltstack __x32_compat_sys_sigaltstack -526 x32 timer_create __x32_compat_sys_timer_create -527 x32 mq_notify __x32_compat_sys_mq_notify -528 x32 kexec_load __x32_compat_sys_kexec_load -529 x32 waitid __x32_compat_sys_waitid -530 x32 set_robust_list __x32_compat_sys_set_robust_list -531 x32 get_robust_list __x32_compat_sys_get_robust_list -532 x32 vmsplice __x32_compat_sys_vmsplice -533 x32 move_pages __x32_compat_sys_move_pages -534 x32 preadv __x32_compat_sys_preadv64 -535 x32 pwritev __x32_compat_sys_pwritev64 -536 x32 rt_tgsigqueueinfo __x32_compat_sys_rt_tgsigqueueinfo -537 x32 recvmmsg __x32_compat_sys_recvmmsg_time64 -538 x32 sendmmsg __x32_compat_sys_sendmmsg -539 x32 process_vm_readv __x32_compat_sys_process_vm_readv -540 x32 process_vm_writev __x32_compat_sys_process_vm_writev -541 x32 setsockopt __x32_compat_sys_setsockopt -542 x32 getsockopt __x32_compat_sys_getsockopt -543 x32 io_setup __x32_compat_sys_io_setup -544 x32 io_submit __x32_compat_sys_io_submit -545 x32 execveat __x32_compat_sys_execveat/ptregs -546 x32 preadv2 __x32_compat_sys_preadv64v2 -547 x32 pwritev2 __x32_compat_sys_pwritev64v2 +512 x32 rt_sigaction compat_sys_rt_sigaction +513 x32 rt_sigreturn compat_sys_x32_rt_sigreturn +514 x32 ioctl compat_sys_ioctl +515 x32 readv compat_sys_readv +516 x32 writev compat_sys_writev +517 x32 recvfrom compat_sys_recvfrom +518 x32 sendmsg compat_sys_sendmsg +519 x32 recvmsg compat_sys_recvmsg +520 x32 execve compat_sys_execve +521 x32 ptrace compat_sys_ptrace +522 x32 rt_sigpending compat_sys_rt_sigpending +523 x32 rt_sigtimedwait compat_sys_rt_sigtimedwait_time64 +524 x32 rt_sigqueueinfo compat_sys_rt_sigqueueinfo +525 x32 sigaltstack compat_sys_sigaltstack +526 x32 timer_create compat_sys_timer_create +527 x32 mq_notify compat_sys_mq_notify +528 x32 kexec_load compat_sys_kexec_load +529 x32 waitid compat_sys_waitid +530 x32 set_robust_list compat_sys_set_robust_list +531 x32 get_robust_list compat_sys_get_robust_list +532 x32 vmsplice compat_sys_vmsplice +533 x32 move_pages compat_sys_move_pages +534 x32 preadv compat_sys_preadv64 +535 x32 pwritev compat_sys_pwritev64 +536 x32 rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo +537 x32 recvmmsg compat_sys_recvmmsg_time64 +538 x32 sendmmsg compat_sys_sendmmsg +539 x32 process_vm_readv compat_sys_process_vm_readv +540 x32 process_vm_writev compat_sys_process_vm_writev +541 x32 setsockopt sys_setsockopt +542 x32 getsockopt sys_getsockopt +543 x32 io_setup compat_sys_io_setup +544 x32 io_submit compat_sys_io_submit +545 x32 execveat compat_sys_execveat +546 x32 preadv2 compat_sys_preadv64v2 +547 x32 pwritev2 compat_sys_pwritev64v2 diff --git a/linux-user/xtensa/syscall.tbl b/linux-user/xtensa/syscall.tbl index 85a9ab1bc0..6276e3c2d3 100644 --- a/linux-user/xtensa/syscall.tbl +++ b/linux-user/xtensa/syscall.tbl @@ -222,7 +222,7 @@ 204 common quotactl sys_quotactl # 205 was old nfsservctl 205 common nfsservctl sys_ni_syscall -206 common _sysctl sys_sysctl +206 common _sysctl sys_ni_syscall 207 common bdflush sys_bdflush 208 common uname sys_newuname 209 common sysinfo sys_sysinfo @@ -406,5 +406,7 @@ 433 common fspick sys_fspick 434 common pidfd_open sys_pidfd_open 435 common clone3 sys_clone3 +436 common close_range sys_close_range 437 common openat2 sys_openat2 438 common pidfd_getfd sys_pidfd_getfd +439 common faccessat2 sys_faccessat2 diff --git a/memory_ldst.c.inc b/memory_ldst.c.inc index c54aee4a95..8d45d2eeff 100644 --- a/memory_ldst.c.inc +++ b/memory_ldst.c.inc @@ -42,6 +42,7 @@ static inline uint32_t glue(address_space_ldl_internal, SUFFIX)(ARG1_DECL, MO_32 | devend_memop(endian), attrs); } else { /* RAM case */ + fuzz_dma_read_cb(addr, 4, mr, false); ptr = qemu_map_ram_ptr(mr->ram_block, addr1); switch (endian) { case DEVICE_LITTLE_ENDIAN: @@ -110,6 +111,7 @@ static inline uint64_t glue(address_space_ldq_internal, SUFFIX)(ARG1_DECL, MO_64 | devend_memop(endian), attrs); } else { /* RAM case */ + fuzz_dma_read_cb(addr, 8, mr, false); ptr = qemu_map_ram_ptr(mr->ram_block, addr1); switch (endian) { case DEVICE_LITTLE_ENDIAN: @@ -175,6 +177,7 @@ uint32_t glue(address_space_ldub, SUFFIX)(ARG1_DECL, r = memory_region_dispatch_read(mr, addr1, &val, MO_8, attrs); } else { /* RAM case */ + fuzz_dma_read_cb(addr, 1, mr, false); ptr = qemu_map_ram_ptr(mr->ram_block, addr1); val = ldub_p(ptr); r = MEMTX_OK; @@ -212,6 +215,7 @@ static inline uint32_t glue(address_space_lduw_internal, SUFFIX)(ARG1_DECL, MO_16 | devend_memop(endian), attrs); } else { /* RAM case */ + fuzz_dma_read_cb(addr, 2, mr, false); ptr = qemu_map_ram_ptr(mr->ram_block, addr1); switch (endian) { case DEVICE_LITTLE_ENDIAN: diff --git a/meson.build b/meson.build index 7627a0ae46..47e32e1fcb 100644 --- a/meson.build +++ b/meson.build @@ -29,8 +29,14 @@ if get_option('qemu_suffix').startswith('/') error('qemu_suffix cannot start with a /') endif +qemu_confdir = get_option('sysconfdir') / get_option('qemu_suffix') qemu_datadir = get_option('datadir') / get_option('qemu_suffix') qemu_docdir = get_option('docdir') / get_option('qemu_suffix') +qemu_moddir = get_option('libdir') / get_option('qemu_suffix') + +qemu_desktopdir = get_option('datadir') / 'applications' +qemu_icondir = get_option('datadir') / 'icons' + config_host_data = configuration_data() genh = [] @@ -198,7 +204,7 @@ else have_xen_pci_passthrough = false endif if not get_option('whpx').disabled() and targetos == 'windows' - if get_option('whpx').enabled() and cpu != 'x86_64' + if get_option('whpx').enabled() and host_machine.cpu() != 'x86_64' error('WHPX requires 64-bit host') elif cc.has_header('WinHvPlatform.h', required: get_option('whpx')) and \ cc.has_header('WinHvEmulation.h', required: get_option('whpx')) @@ -465,70 +471,89 @@ endif iconv = not_found curses = not_found if have_system and not get_option('curses').disabled() - if not get_option('iconv').disabled() - libiconv = cc.find_library('iconv', - required: false, - static: enable_static) - if cc.links(''' - #include <iconv.h> - int main(void) { - iconv_t conv = iconv_open("WCHAR_T", "UCS-2"); - return conv != (iconv_t) -1; - }''', dependencies: [libiconv]) - iconv = declare_dependency(dependencies: [libiconv]) + curses_test = ''' + #include <locale.h> + #include <curses.h> + #include <wchar.h> + int main(void) { + wchar_t wch = L'w'; + setlocale(LC_ALL, ""); + resize_term(0, 0); + addwstr(L"wide chars\n"); + addnwstr(&wch, 1); + add_wch(WACS_DEGREE); + return 0; + }''' + + curses = dependency((targetos == 'windows' ? 'ncurses' : 'ncursesw'), + required: false, + method: 'pkg-config', + static: enable_static) + msg = get_option('curses').enabled() ? 'curses library not found' : '' + if curses.found() + if cc.links(curses_test, dependencies: [curses]) + curses = declare_dependency(compile_args: '-DNCURSES_WIDECHAR', dependencies: [curses]) + else + msg = 'curses package not usable' + curses = not_found endif endif - if get_option('iconv').enabled() and not iconv.found() - error('Cannot detect iconv API') - endif - if iconv.found() - curses_libname_list = ['ncursesw', 'ncurses', 'cursesw', 'pdcurses'] - curses_test = ''' - #include <locale.h> - #include <curses.h> - #include <wchar.h> - int main(void) { - wchar_t wch = L'w'; - setlocale(LC_ALL, ""); - resize_term(0, 0); - addwstr(L"wide chars\n"); - addnwstr(&wch, 1); - add_wch(WACS_DEGREE); - return 0; - }''' - foreach curses_libname : curses_libname_list - libcurses = dependency(curses_libname, - required: false, - method: 'pkg-config', - static: enable_static) - - if not libcurses.found() - dirs = ['/usr/include/ncursesw'] - if targetos == 'windows' - dirs = [] - endif + if not curses.found() + curses_compile_args = ['-DNCURSES_WIDECHAR'] + has_curses_h = cc.has_header('curses.h', args: curses_compile_args) + if targetos != 'windows' and not has_curses_h + message('Trying with /usr/include/ncursesw') + curses_compile_args += ['-I/usr/include/ncursesw'] + has_curses_h = cc.has_header('curses.h', args: curses_compile_args) + endif + if has_curses_h + curses_libname_list = (targetos == 'windows' ? ['pdcurses'] : ['ncursesw', 'cursesw']) + foreach curses_libname : curses_libname_list libcurses = cc.find_library(curses_libname, required: false, - dirs: dirs, static: enable_static) - endif - if libcurses.found() - if cc.links(curses_test, dependencies: [libcurses]) - curses = declare_dependency(compile_args: '-DNCURSES_WIDECHAR', dependencies: [libcurses]) - break + if libcurses.found() + if cc.links(curses_test, args: curses_compile_args, dependencies: libcurses) + curses = declare_dependency(compile_args: curses_compile_args, + dependencies: [libcurses]) + break + else + msg = 'curses library not usable' + endif endif + endforeach + endif + endif + if not get_option('iconv').disabled() + foreach link_args : [ ['-liconv'], [] ] + # Programs will be linked with glib and this will bring in libiconv on FreeBSD. + # We need to use libiconv if available because mixing libiconv's headers with + # the system libc does not work. + # However, without adding glib to the dependencies -L/usr/local/lib will not be + # included in the command line and libiconv will not be found. + if cc.links(''' + #include <iconv.h> + int main(void) { + iconv_t conv = iconv_open("WCHAR_T", "UCS-2"); + return conv != (iconv_t) -1; + }''', args: config_host['GLIB_CFLAGS'].split() + config_host['GLIB_LIBS'].split() + link_args) + iconv = declare_dependency(link_args: link_args, dependencies: glib) + break endif endforeach endif - if not curses.found() - if iconv.found() - if get_option('curses').enabled() - error('Cannot find curses') - endif - elif get_option('curses').enabled() - error('iconv required for curses UI but not available') + if curses.found() and not iconv.found() + if get_option('iconv').enabled() + error('iconv not available') + endif + msg = 'iconv required for curses UI but not available' + curses = not_found + endif + if not curses.found() and msg != '' + if get_option('curses').enabled() + error(msg) else - warning('iconv required for curses UI but not available, disabling') + warning(msg + ', disabling') endif endif endif @@ -715,6 +740,19 @@ endif # config-host.h # ################# +config_host_data.set_quoted('CONFIG_BINDIR', get_option('prefix') / get_option('bindir')) +config_host_data.set_quoted('CONFIG_PREFIX', get_option('prefix')) +config_host_data.set_quoted('CONFIG_QEMU_CONFDIR', get_option('prefix') / qemu_confdir) +config_host_data.set_quoted('CONFIG_QEMU_DATADIR', get_option('prefix') / qemu_datadir) +config_host_data.set_quoted('CONFIG_QEMU_DESKTOPDIR', get_option('prefix') / qemu_desktopdir) +config_host_data.set_quoted('CONFIG_QEMU_FIRMWAREPATH', get_option('qemu_firmwarepath')) +config_host_data.set_quoted('CONFIG_QEMU_HELPERDIR', get_option('prefix') / get_option('libexecdir')) +config_host_data.set_quoted('CONFIG_QEMU_ICONDIR', get_option('prefix') / qemu_icondir) +config_host_data.set_quoted('CONFIG_QEMU_LOCALEDIR', get_option('prefix') / get_option('localedir')) +config_host_data.set_quoted('CONFIG_QEMU_LOCALSTATEDIR', get_option('prefix') / get_option('localstatedir')) +config_host_data.set_quoted('CONFIG_QEMU_MODDIR', get_option('prefix') / qemu_moddir) +config_host_data.set_quoted('CONFIG_SYSCONFDIR', get_option('prefix') / get_option('sysconfdir')) + config_host_data.set('CONFIG_COCOA', cocoa.found()) config_host_data.set('CONFIG_LIBUDEV', libudev.found()) config_host_data.set('CONFIG_MPATH', mpathpersist.found()) @@ -737,9 +775,7 @@ config_host_data.set('QEMU_VERSION_MICRO', meson.project_version().split('.')[2] ignored = ['CONFIG_QEMU_INTERP_PREFIX'] # actually per-target arrays = ['CONFIG_AUDIO_DRIVERS', 'CONFIG_BDRV_RW_WHITELIST', 'CONFIG_BDRV_RO_WHITELIST'] -strings = ['HOST_DSOSUF', 'CONFIG_IASL', 'bindir', 'prefix', 'qemu_confdir', 'qemu_datadir', - 'qemu_moddir', 'qemu_localstatedir', 'qemu_helperdir', 'qemu_localedir', - 'qemu_icondir', 'qemu_desktopdir', 'qemu_firmwarepath', 'sysconfdir'] +strings = ['HOST_DSOSUF', 'CONFIG_IASL'] foreach k, v: config_host if ignored.contains(k) # do nothing @@ -1398,6 +1434,11 @@ trace_events_subdirs += [ 'util', ] +vhost_user = not_found +if 'CONFIG_VHOST_USER' in config_host + subdir('contrib/libvhost-user') +endif + subdir('qapi') subdir('qobject') subdir('stubs') @@ -1438,7 +1479,6 @@ subdir('dump') block_ss.add(files( 'block.c', - 'blockdev-nbd.c', 'blockjob.c', 'job.c', 'qemu-io-cmds.c', @@ -1451,6 +1491,7 @@ subdir('block') blockdev_ss.add(files( 'blockdev.c', + 'blockdev-nbd.c', 'iothread.c', 'job-qmp.c', )) @@ -1459,7 +1500,6 @@ blockdev_ss.add(files( # os-win32.c does not blockdev_ss.add(when: 'CONFIG_POSIX', if_true: files('os-posix.c')) softmmu_ss.add(when: 'CONFIG_WIN32', if_true: [files('os-win32.c')]) -softmmu_ss.add_all(blockdev_ss) common_ss.add(files('cpus-common.c')) @@ -1591,6 +1631,15 @@ block = declare_dependency(link_whole: [libblock], link_args: '@block.syms', dependencies: [crypto, io]) +blockdev_ss = blockdev_ss.apply(config_host, strict: false) +libblockdev = static_library('blockdev', blockdev_ss.sources() + genh, + dependencies: blockdev_ss.dependencies(), + name_suffix: 'fa', + build_by_default: false) + +blockdev = declare_dependency(link_whole: [libblockdev], + dependencies: [block]) + qmp_ss = qmp_ss.apply(config_host, strict: false) libqmp = static_library('qmp', qmp_ss.sources() + genh, dependencies: qmp_ss.dependencies(), @@ -1620,10 +1669,10 @@ foreach m : block_mods + softmmu_mods name_prefix: '', link_whole: m, install: true, - install_dir: config_host['qemu_moddir']) + install_dir: qemu_moddir) endforeach -softmmu_ss.add(authz, block, chardev, crypto, io, qmp) +softmmu_ss.add(authz, blockdev, chardev, crypto, io, qmp) common_ss.add(qom, qemuutil) common_ss.add_all(when: 'CONFIG_SOFTMMU', if_true: [softmmu_ss]) @@ -1781,7 +1830,7 @@ foreach target : target_dirs output: exe['name'] + stp['ext'], capture: true, install: stp['install'], - install_dir: qemu_datadir / '../systemtap/tapset', + install_dir: get_option('datadir') / 'systemtap/tapset', command: [ tracetool, '--group=all', '--format=' + stp['fmt'], '--binary=' + stp['bin'], @@ -1819,7 +1868,7 @@ if have_tools qemu_io = executable('qemu-io', files('qemu-io.c'), dependencies: [block, qemuutil], install: true) qemu_nbd = executable('qemu-nbd', files('qemu-nbd.c'), - dependencies: [block, qemuutil], install: true) + dependencies: [blockdev, qemuutil], install: true) subdir('storage-daemon') subdir('contrib/rdmacm-mux') @@ -1830,7 +1879,6 @@ if have_tools install: true) if 'CONFIG_VHOST_USER' in config_host - subdir('contrib/libvhost-user') subdir('contrib/vhost-user-blk') subdir('contrib/vhost-user-gpu') subdir('contrib/vhost-user-input') @@ -1894,17 +1942,17 @@ endif ######################### summary_info = {} -summary_info += {'Install prefix': config_host['prefix']} -summary_info += {'BIOS directory': config_host['qemu_datadir']} -summary_info += {'firmware path': config_host['qemu_firmwarepath']} -summary_info += {'binary directory': config_host['bindir']} -summary_info += {'library directory': config_host['libdir']} -summary_info += {'module directory': config_host['qemu_moddir']} -summary_info += {'libexec directory': config_host['libexecdir']} -summary_info += {'include directory': config_host['includedir']} -summary_info += {'config directory': config_host['sysconfdir']} +summary_info += {'Install prefix': get_option('prefix')} +summary_info += {'BIOS directory': qemu_datadir} +summary_info += {'firmware path': get_option('qemu_firmwarepath')} +summary_info += {'binary directory': get_option('bindir')} +summary_info += {'library directory': get_option('libdir')} +summary_info += {'module directory': qemu_moddir} +summary_info += {'libexec directory': get_option('libexecdir')} +summary_info += {'include directory': get_option('includedir')} +summary_info += {'config directory': get_option('sysconfdir')} if targetos != 'windows' - summary_info += {'local state directory': config_host['qemu_localstatedir']} + summary_info += {'local state directory': get_option('localstatedir')} summary_info += {'Manual directory': get_option('mandir')} else summary_info += {'local state directory': 'queried at runtime'} @@ -1997,6 +2045,7 @@ summary_info += {'Audio drivers': config_host['CONFIG_AUDIO_DRIVERS']} summary_info += {'Block whitelist (rw)': config_host['CONFIG_BDRV_RW_WHITELIST']} summary_info += {'Block whitelist (ro)': config_host['CONFIG_BDRV_RO_WHITELIST']} summary_info += {'VirtFS support': config_host.has_key('CONFIG_VIRTFS')} +summary_info += {'build virtiofs daemon': have_virtiofsd} summary_info += {'Multipath support': mpathpersist.found()} summary_info += {'VNC support': vnc.found()} if vnc.found() @@ -2016,7 +2065,7 @@ summary_info += {'netmap support': config_host.has_key('CONFIG_NETMAP')} summary_info += {'Linux AIO support': config_host.has_key('CONFIG_LINUX_AIO')} summary_info += {'Linux io_uring support': config_host.has_key('CONFIG_LINUX_IO_URING')} summary_info += {'ATTR/XATTR support': config_host.has_key('CONFIG_ATTR')} -summary_info += {'Install blobs': config_host.has_key('INSTALL_BLOBS')} +summary_info += {'Install blobs': get_option('install_blobs')} summary_info += {'KVM support': config_all.has_key('CONFIG_KVM')} summary_info += {'HAX support': config_all.has_key('CONFIG_HAX')} summary_info += {'HVF support': config_all.has_key('CONFIG_HVF')} diff --git a/meson_options.txt b/meson_options.txt index 967229b66e..b4f1801875 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -2,6 +2,8 @@ option('qemu_suffix', type : 'string', value: 'qemu', description: 'Suffix for QEMU data/modules/config directories (can be empty)') option('docdir', type : 'string', value : 'doc', description: 'Base directory for documentation installation (can be empty)') +option('qemu_firmwarepath', type : 'string', value : '', + description: 'search PATH for firmware files') option('sphinx_build', type : 'string', value : '', description: 'Use specified sphinx-build [$sphinx_build] for building document (default to be empty)') @@ -9,6 +11,8 @@ option('docs', type : 'feature', value : 'auto', description: 'Documentations build support') option('gettext', type : 'boolean', value : true, description: 'Localization of the GTK+ user interface') +option('install_blobs', type : 'boolean', value : true, + description: 'install provided firmware blobs') option('sparse', type : 'feature', value : 'auto', description: 'sparse checker') @@ -58,6 +62,8 @@ option('vnc_sasl', type : 'feature', value : 'auto', description: 'SASL authentication for VNC server') option('xkbcommon', type : 'feature', value : 'auto', description: 'xkbcommon support') +option('virtiofsd', type: 'feature', value: 'auto', + description: 'build virtiofs daemon (virtiofsd)') option('capstone', type: 'combo', value: 'auto', choices: ['disabled', 'enabled', 'auto', 'system', 'internal'], diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c index 5bef793ac0..c61d382be8 100644 --- a/migration/block-dirty-bitmap.c +++ b/migration/block-dirty-bitmap.c @@ -562,8 +562,9 @@ static int add_bitmaps_to_list(DBMSaveState *s, BlockDriverState *bs, dbms->bitmap_alias = g_strdup(bitmap_alias); dbms->bitmap = bitmap; dbms->total_sectors = bdrv_nb_sectors(bs); - dbms->sectors_per_chunk = CHUNK_SIZE * 8 * - bdrv_dirty_bitmap_granularity(bitmap) >> BDRV_SECTOR_BITS; + dbms->sectors_per_chunk = CHUNK_SIZE * 8LLU * + (bdrv_dirty_bitmap_granularity(bitmap) >> BDRV_SECTOR_BITS); + assert(dbms->sectors_per_chunk != 0); if (bdrv_dirty_bitmap_enabled(bitmap)) { dbms->flags |= DIRTY_BITMAP_MIG_START_FLAG_ENABLED; } @@ -1071,18 +1072,15 @@ static int dirty_bitmap_load_header(QEMUFile *f, DBMLoadState *s, return -EINVAL; } - if (!s->cancelled) { - if (bitmap_alias_map) { - bitmap_name = g_hash_table_lookup(bitmap_alias_map, - s->bitmap_alias); - if (!bitmap_name) { - error_report("Error: Unknown bitmap alias '%s' on node " - "'%s' (alias '%s')", s->bitmap_alias, - s->bs->node_name, s->node_alias); - cancel_incoming_locked(s); - } - } else { - bitmap_name = s->bitmap_alias; + bitmap_name = s->bitmap_alias; + if (!s->cancelled && bitmap_alias_map) { + bitmap_name = g_hash_table_lookup(bitmap_alias_map, + s->bitmap_alias); + if (!bitmap_name) { + error_report("Error: Unknown bitmap alias '%s' on node " + "'%s' (alias '%s')", s->bitmap_alias, + s->bs->node_name, s->node_alias); + cancel_incoming_locked(s); } } diff --git a/migration/block.c b/migration/block.c index 737b6499f9..a950977855 100644 --- a/migration/block.c +++ b/migration/block.c @@ -26,6 +26,7 @@ #include "qemu-file.h" #include "migration/vmstate.h" #include "sysemu/block-backend.h" +#include "trace.h" #define BLK_MIG_BLOCK_SIZE (1 << 20) #define BDRV_SECTORS_PER_DIRTY_CHUNK (BLK_MIG_BLOCK_SIZE >> BDRV_SECTOR_BITS) @@ -40,7 +41,7 @@ #define MAX_IO_BUFFERS 512 #define MAX_PARALLEL_IO 16 -//#define DEBUG_BLK_MIGRATION +/* #define DEBUG_BLK_MIGRATION */ #ifdef DEBUG_BLK_MIGRATION #define DPRINTF(fmt, ...) \ @@ -434,10 +435,9 @@ static int init_blk_migration(QEMUFile *f) block_mig_state.total_sector_sum += sectors; if (bmds->shared_base) { - DPRINTF("Start migration for %s with shared base image\n", - bdrv_get_device_name(bs)); + trace_migration_block_init_shared(bdrv_get_device_name(bs)); } else { - DPRINTF("Start full migration for %s\n", bdrv_get_device_name(bs)); + trace_migration_block_init_full(bdrv_get_device_name(bs)); } QSIMPLEQ_INSERT_TAIL(&block_mig_state.bmds_list, bmds, entry); @@ -592,7 +592,7 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds, return (bmds->cur_dirty >= bmds->total_sectors); error: - DPRINTF("Error reading sector %" PRId64 "\n", sector); + trace_migration_block_save_device_dirty(sector); g_free(blk->buf); g_free(blk); return ret; @@ -628,9 +628,9 @@ static int flush_blks(QEMUFile *f) BlkMigBlock *blk; int ret = 0; - DPRINTF("%s Enter submitted %d read_done %d transferred %d\n", - __func__, block_mig_state.submitted, block_mig_state.read_done, - block_mig_state.transferred); + trace_migration_block_flush_blks("Enter", block_mig_state.submitted, + block_mig_state.read_done, + block_mig_state.transferred); blk_mig_lock(); while ((blk = QSIMPLEQ_FIRST(&block_mig_state.blk_list)) != NULL) { @@ -656,9 +656,9 @@ static int flush_blks(QEMUFile *f) } blk_mig_unlock(); - DPRINTF("%s Exit submitted %d read_done %d transferred %d\n", __func__, - block_mig_state.submitted, block_mig_state.read_done, - block_mig_state.transferred); + trace_migration_block_flush_blks("Exit", block_mig_state.submitted, + block_mig_state.read_done, + block_mig_state.transferred); return ret; } @@ -727,8 +727,8 @@ static int block_save_setup(QEMUFile *f, void *opaque) { int ret; - DPRINTF("Enter save live setup submitted %d transferred %d\n", - block_mig_state.submitted, block_mig_state.transferred); + trace_migration_block_save("setup", block_mig_state.submitted, + block_mig_state.transferred); qemu_mutex_lock_iothread(); ret = init_blk_migration(f); @@ -759,8 +759,8 @@ static int block_save_iterate(QEMUFile *f, void *opaque) int64_t last_ftell = qemu_ftell(f); int64_t delta_ftell; - DPRINTF("Enter save live iterate submitted %d transferred %d\n", - block_mig_state.submitted, block_mig_state.transferred); + trace_migration_block_save("iterate", block_mig_state.submitted, + block_mig_state.transferred); ret = flush_blks(f); if (ret) { @@ -825,8 +825,8 @@ static int block_save_complete(QEMUFile *f, void *opaque) { int ret; - DPRINTF("Enter save live complete submitted %d transferred %d\n", - block_mig_state.submitted, block_mig_state.transferred); + trace_migration_block_save("complete", block_mig_state.submitted, + block_mig_state.transferred); ret = flush_blks(f); if (ret) { @@ -851,7 +851,7 @@ static int block_save_complete(QEMUFile *f, void *opaque) /* report completion */ qemu_put_be64(f, (100 << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS); - DPRINTF("Block migration completed\n"); + trace_migration_block_save_complete(); qemu_put_be64(f, BLK_MIG_FLAG_EOS); @@ -884,7 +884,7 @@ static void block_save_pending(QEMUFile *f, void *opaque, uint64_t max_size, pending = max_size + BLK_MIG_BLOCK_SIZE; } - DPRINTF("Enter save live pending %" PRIu64 "\n", pending); + trace_migration_block_save_pending(pending); /* We don't do postcopy */ *res_precopy_only += pending; } @@ -998,7 +998,7 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) (addr == 100) ? '\n' : '\r'); fflush(stdout); } else if (!(flags & BLK_MIG_FLAG_EOS)) { - fprintf(stderr, "Unknown block migration flags: %#x\n", flags); + fprintf(stderr, "Unknown block migration flags: 0x%x\n", flags); return -EINVAL; } ret = qemu_file_get_error(f); diff --git a/migration/migration.c b/migration/migration.c index 0575ecb379..9bb4fee5ac 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -143,9 +143,15 @@ static int migration_maybe_pause(MigrationState *s, int new_state); static void migrate_fd_cancel(MigrationState *s); +static gint page_request_addr_cmp(gconstpointer ap, gconstpointer bp) +{ + uintptr_t a = (uintptr_t) ap, b = (uintptr_t) bp; + + return (a > b) - (a < b); +} + void migration_object_init(void) { - MachineState *ms = MACHINE(qdev_get_machine()); Error *err = NULL; /* This can only be called once. */ @@ -165,20 +171,13 @@ void migration_object_init(void) qemu_event_init(¤t_incoming->main_thread_load_event, false); qemu_sem_init(¤t_incoming->postcopy_pause_sem_dst, 0); qemu_sem_init(¤t_incoming->postcopy_pause_sem_fault, 0); + qemu_mutex_init(¤t_incoming->page_request_mutex); + current_incoming->page_requested = g_tree_new(page_request_addr_cmp); if (!migration_object_check(current_migration, &err)) { error_report_err(err); exit(1); } - - /* - * We cannot really do this in migration_instance_init() since at - * that time global properties are not yet applied, then this - * value will be definitely replaced by something else. - */ - if (ms->enforce_config_section) { - current_migration->send_configuration = true; - } } void migration_shutdown(void) @@ -240,6 +239,11 @@ void migration_incoming_state_destroy(void) qemu_event_reset(&mis->main_thread_load_event); + if (mis->page_requested) { + g_tree_destroy(mis->page_requested); + mis->page_requested = NULL; + } + if (mis->socket_address_list) { qapi_free_SocketAddressList(mis->socket_address_list); mis->socket_address_list = NULL; @@ -316,8 +320,8 @@ error: * Start: Address offset within the RB * Len: Length in bytes required - must be a multiple of pagesize */ -int migrate_send_rp_req_pages(MigrationIncomingState *mis, RAMBlock *rb, - ram_addr_t start) +int migrate_send_rp_message_req_pages(MigrationIncomingState *mis, + RAMBlock *rb, ram_addr_t start) { uint8_t bufc[12 + 1 + 255]; /* start (8), len (4), rbname up to 256 */ size_t msglen = 12; /* start + len */ @@ -353,6 +357,37 @@ int migrate_send_rp_req_pages(MigrationIncomingState *mis, RAMBlock *rb, return migrate_send_rp_message(mis, msg_type, msglen, bufc); } +int migrate_send_rp_req_pages(MigrationIncomingState *mis, + RAMBlock *rb, ram_addr_t start, uint64_t haddr) +{ + void *aligned = (void *)(uintptr_t)(haddr & (-qemu_ram_pagesize(rb))); + bool received; + + WITH_QEMU_LOCK_GUARD(&mis->page_request_mutex) { + received = ramblock_recv_bitmap_test_byte_offset(rb, start); + if (!received && !g_tree_lookup(mis->page_requested, aligned)) { + /* + * The page has not been received, and it's not yet in the page + * request list. Queue it. Set the value of element to 1, so that + * things like g_tree_lookup() will return TRUE (1) when found. + */ + g_tree_insert(mis->page_requested, aligned, (gpointer)1); + mis->page_requested_count++; + trace_postcopy_page_req_add(aligned, mis->page_requested_count); + } + } + + /* + * If the page is there, skip sending the message. We don't even need the + * lock because as long as the page arrived, it'll be there forever. + */ + if (received) { + return 0; + } + + return migrate_send_rp_message_req_pages(mis, rb, start); +} + static bool migration_colo_enabled; bool migration_incoming_colo_enabled(void) { @@ -2478,8 +2513,8 @@ static void migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname, * Since we currently insist on matching page sizes, just sanity check * we're being asked for whole host pages. */ - if (start & (our_host_ps-1) || - (len & (our_host_ps-1))) { + if (start & (our_host_ps - 1) || + (len & (our_host_ps - 1))) { error_report("%s: Misaligned page request, start: " RAM_ADDR_FMT " len: %zd", __func__, start, len); mark_source_rp_bad(ms); @@ -3133,9 +3168,6 @@ static MigThrError postcopy_pause(MigrationState *s) while (true) { QEMUFile *file; - migrate_set_state(&s->state, s->state, - MIGRATION_STATUS_POSTCOPY_PAUSED); - /* Current channel is possibly broken. Release it. */ assert(s->to_dst_file); qemu_mutex_lock(&s->qemu_file_lock); @@ -3146,6 +3178,9 @@ static MigThrError postcopy_pause(MigrationState *s) qemu_file_shutdown(file); qemu_fclose(file); + migrate_set_state(&s->state, s->state, + MIGRATION_STATUS_POSTCOPY_PAUSED); + error_report("Detected IO failure for postcopy. " "Migration paused."); diff --git a/migration/migration.h b/migration/migration.h index deb411aaad..d096b77f74 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -104,6 +104,23 @@ struct MigrationIncomingState { /* List of listening socket addresses */ SocketAddressList *socket_address_list; + + /* A tree of pages that we requested to the source VM */ + GTree *page_requested; + /* For debugging purpose only, but would be nice to keep */ + int page_requested_count; + /* + * The mutex helps to maintain the requested pages that we sent to the + * source, IOW, to guarantee coherent between the page_requests tree and + * the per-ramblock receivedmap. Note! This does not guarantee consistency + * of the real page copy procedures (using UFFDIO_[ZERO]COPY). E.g., even + * if one bit in receivedmap is cleared, UFFDIO_COPY could have happened + * for that page already. This is intended so that the mutex won't + * serialize and blocked by slow operations like UFFDIO_* ioctls. However + * this should be enough to make sure the page_requested tree always + * contains valid information. + */ + QemuMutex page_request_mutex; }; MigrationIncomingState *migration_incoming_get_current(void); @@ -124,8 +141,7 @@ struct MigrationClass { DeviceClass parent_class; }; -struct MigrationState -{ +struct MigrationState { /*< private >*/ DeviceState parent_obj; @@ -332,7 +348,9 @@ void migrate_send_rp_shut(MigrationIncomingState *mis, void migrate_send_rp_pong(MigrationIncomingState *mis, uint32_t value); int migrate_send_rp_req_pages(MigrationIncomingState *mis, RAMBlock *rb, - ram_addr_t start); + ram_addr_t start, uint64_t haddr); +int migrate_send_rp_message_req_pages(MigrationIncomingState *mis, + RAMBlock *rb, ram_addr_t start); void migrate_send_rp_recv_bitmap(MigrationIncomingState *mis, char *block_name); void migrate_send_rp_resume_ack(MigrationIncomingState *mis, uint32_t value); diff --git a/migration/page_cache.c b/migration/page_cache.c index 775582f453..098b436223 100644 --- a/migration/page_cache.c +++ b/migration/page_cache.c @@ -18,14 +18,7 @@ #include "qapi/error.h" #include "qemu/host-utils.h" #include "page_cache.h" - -#ifdef DEBUG_CACHE -#define DPRINTF(fmt, ...) \ - do { fprintf(stdout, "cache: " fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#endif +#include "trace.h" /* the page in cache will not be replaced in two cycles */ #define CACHED_PAGE_LIFETIME 2 @@ -75,7 +68,7 @@ PageCache *cache_init(int64_t new_size, size_t page_size, Error **errp) cache->num_items = 0; cache->max_num_items = num_pages; - DPRINTF("Setting cache buckets to %" PRId64 "\n", cache->max_num_items); + trace_migration_pagecache_init(cache->max_num_items); /* We prefer not to abort if there is no memory */ cache->page_cache = g_try_malloc((cache->max_num_items) * @@ -169,7 +162,7 @@ int cache_insert(PageCache *cache, uint64_t addr, const uint8_t *pdata, if (!it->it_data) { it->it_data = g_try_malloc(cache->page_size); if (!it->it_data) { - DPRINTF("Error allocating page\n"); + trace_migration_pagecache_insert(); return -1; } cache->num_items++; diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 0a2f88a87d..d3bb3a744b 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -403,7 +403,7 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis) strerror(errno)); goto out; } - g_assert(((size_t)testarea & (pagesize-1)) == 0); + g_assert(((size_t)testarea & (pagesize - 1)) == 0); reg_struct.range.start = (uintptr_t)testarea; reg_struct.range.len = pagesize; @@ -684,7 +684,7 @@ int postcopy_request_shared_page(struct PostCopyFD *pcfd, RAMBlock *rb, qemu_ram_get_idstr(rb), rb_offset); return postcopy_wake_shared(pcfd, client_addr, rb); } - migrate_send_rp_req_pages(mis, rb, aligned_rbo); + migrate_send_rp_req_pages(mis, rb, aligned_rbo, client_addr); return 0; } @@ -979,7 +979,8 @@ retry: * Send the request to the source - we want to request one * of our host page sizes (which is >= TPS) */ - ret = migrate_send_rp_req_pages(mis, rb, rb_offset); + ret = migrate_send_rp_req_pages(mis, rb, rb_offset, + msg.arg.pagefault.address); if (ret) { /* May be network failure, try to wait for recovery */ if (ret == -EIO && postcopy_pause_fault_thread(mis)) { @@ -1128,10 +1129,12 @@ int postcopy_ram_incoming_setup(MigrationIncomingState *mis) return 0; } -static int qemu_ufd_copy_ioctl(int userfault_fd, void *host_addr, +static int qemu_ufd_copy_ioctl(MigrationIncomingState *mis, void *host_addr, void *from_addr, uint64_t pagesize, RAMBlock *rb) { + int userfault_fd = mis->userfault_fd; int ret; + if (from_addr) { struct uffdio_copy copy_struct; copy_struct.dst = (uint64_t)(uintptr_t)host_addr; @@ -1147,10 +1150,20 @@ static int qemu_ufd_copy_ioctl(int userfault_fd, void *host_addr, ret = ioctl(userfault_fd, UFFDIO_ZEROPAGE, &zero_struct); } if (!ret) { + qemu_mutex_lock(&mis->page_request_mutex); ramblock_recv_bitmap_set_range(rb, host_addr, pagesize / qemu_target_page_size()); + /* + * If this page resolves a page fault for a previous recorded faulted + * address, take a special note to maintain the requested page list. + */ + if (g_tree_lookup(mis->page_requested, host_addr)) { + g_tree_remove(mis->page_requested, host_addr); + mis->page_requested_count--; + trace_postcopy_page_req_del(host_addr, mis->page_requested_count); + } + qemu_mutex_unlock(&mis->page_request_mutex); mark_postcopy_blocktime_end((uintptr_t)host_addr); - } return ret; } @@ -1185,7 +1198,7 @@ int postcopy_place_page(MigrationIncomingState *mis, void *host, void *from, * which would be slightly cheaper, but we'd have to be careful * of the order of updating our page state. */ - if (qemu_ufd_copy_ioctl(mis->userfault_fd, host, from, pagesize, rb)) { + if (qemu_ufd_copy_ioctl(mis, host, from, pagesize, rb)) { int e = errno; error_report("%s: %s copy host: %p from: %p (size: %zd)", __func__, strerror(e), host, from, pagesize); @@ -1212,7 +1225,7 @@ int postcopy_place_page_zero(MigrationIncomingState *mis, void *host, * but it's not available for everything (e.g. hugetlbpages) */ if (qemu_ram_is_uf_zeroable(rb)) { - if (qemu_ufd_copy_ioctl(mis->userfault_fd, host, NULL, pagesize, rb)) { + if (qemu_ufd_copy_ioctl(mis, host, NULL, pagesize, rb)) { int e = errno; error_report("%s: %s zero host: %p", __func__, strerror(e), host); diff --git a/migration/ram.c b/migration/ram.c index 433489d633..2da2b622ab 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -101,14 +101,16 @@ static struct { static void XBZRLE_cache_lock(void) { - if (migrate_use_xbzrle()) + if (migrate_use_xbzrle()) { qemu_mutex_lock(&XBZRLE.lock); + } } static void XBZRLE_cache_unlock(void) { - if (migrate_use_xbzrle()) + if (migrate_use_xbzrle()) { qemu_mutex_unlock(&XBZRLE.lock); + } } /** @@ -1563,7 +1565,7 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) rs->last_req_rb = ramblock; } trace_ram_save_queue_pages(ramblock->idstr, start, len); - if (start+len > ramblock->used_length) { + if (start + len > ramblock->used_length) { error_report("%s request overrun start=" RAM_ADDR_FMT " len=" RAM_ADDR_FMT " blocklen=" RAM_ADDR_FMT, __func__, start, len, ramblock->used_length); @@ -2741,7 +2743,7 @@ static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host) */ static inline RAMBlock *ram_block_from_stream(QEMUFile *f, int flags) { - static RAMBlock *block = NULL; + static RAMBlock *block; char id[256]; uint8_t len; @@ -3298,7 +3300,7 @@ static int ram_load_postcopy(QEMUFile *f) multifd_recv_sync_main(); break; default: - error_report("Unknown combination of migration flags: %#x" + error_report("Unknown combination of migration flags: 0x%x" " (postcopy mode)", flags); ret = -EINVAL; break; @@ -3576,7 +3578,7 @@ static int ram_load_precopy(QEMUFile *f) if (flags & RAM_SAVE_FLAG_HOOK) { ram_control_load_hook(f, RAM_CONTROL_HOOK, NULL); } else { - error_report("Unknown combination of migration flags: %#x", + error_report("Unknown combination of migration flags: 0x%x", flags); ret = -EINVAL; } diff --git a/migration/rdma.c b/migration/rdma.c index 0340841fad..00eac34232 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -273,7 +273,8 @@ static uint64_t htonll(uint64_t v) return u.llv; } -static uint64_t ntohll(uint64_t v) { +static uint64_t ntohll(uint64_t v) +{ union { uint32_t lv[2]; uint64_t llv; } u; u.llv = v; return ((uint64_t)ntohl(u.lv[0]) << 32) | (uint64_t) ntohl(u.lv[1]); @@ -854,7 +855,7 @@ static int qemu_rdma_broken_ipv6_kernel(struct ibv_context *verbs, Error **errp) */ if (!verbs) { int num_devices, x; - struct ibv_device ** dev_list = ibv_get_device_list(&num_devices); + struct ibv_device **dev_list = ibv_get_device_list(&num_devices); bool roce_found = false; bool ib_found = false; @@ -1288,7 +1289,7 @@ const char *print_wrid(int wrid) * workload information or LRU information is available, do not attempt to use * this feature except for basic testing. */ -//#define RDMA_UNREGISTRATION_EXAMPLE +/* #define RDMA_UNREGISTRATION_EXAMPLE */ /* * Perform a non-optimized memory unregistration after every transfer diff --git a/migration/savevm.c b/migration/savevm.c index ff33e210eb..21ccba9fb3 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -63,7 +63,7 @@ #include "qemu/bitmap.h" #include "net/announce.h" -const unsigned int postcopy_ram_discard_version = 0; +const unsigned int postcopy_ram_discard_version; /* Subcommands for QEMU_VM_COMMAND */ enum qemu_vm_cmd { @@ -520,7 +520,7 @@ static const VMStateDescription vmstate_configuration = { VMSTATE_VBUFFER_ALLOC_UINT32(name, SaveState, 0, NULL, len), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription *[]) { &vmstate_target_page_bits, &vmstate_capabilites, &vmstate_uuid, @@ -2010,6 +2010,49 @@ static int loadvm_postcopy_handle_run(MigrationIncomingState *mis) return LOADVM_QUIT; } +/* We must be with page_request_mutex held */ +static gboolean postcopy_sync_page_req(gpointer key, gpointer value, + gpointer data) +{ + MigrationIncomingState *mis = data; + void *host_addr = (void *) key; + ram_addr_t rb_offset; + RAMBlock *rb; + int ret; + + rb = qemu_ram_block_from_host(host_addr, true, &rb_offset); + if (!rb) { + /* + * This should _never_ happen. However be nice for a migrating VM to + * not crash/assert. Post an error (note: intended to not use *_once + * because we do want to see all the illegal addresses; and this can + * never be triggered by the guest so we're safe) and move on next. + */ + error_report("%s: illegal host addr %p", __func__, host_addr); + /* Try the next entry */ + return FALSE; + } + + ret = migrate_send_rp_message_req_pages(mis, rb, rb_offset); + if (ret) { + /* Please refer to above comment. */ + error_report("%s: send rp message failed for addr %p", + __func__, host_addr); + return FALSE; + } + + trace_postcopy_page_req_sync(host_addr); + + return FALSE; +} + +static void migrate_send_rp_req_pages_pending(MigrationIncomingState *mis) +{ + WITH_QEMU_LOCK_GUARD(&mis->page_request_mutex) { + g_tree_foreach(mis->page_requested, postcopy_sync_page_req, mis); + } +} + static int loadvm_postcopy_handle_resume(MigrationIncomingState *mis) { if (mis->state != MIGRATION_STATUS_POSTCOPY_RECOVER) { @@ -2032,6 +2075,20 @@ static int loadvm_postcopy_handle_resume(MigrationIncomingState *mis) /* Tell source that "we are ready" */ migrate_send_rp_resume_ack(mis, MIGRATION_RESUME_ACK_VALUE); + /* + * After a postcopy recovery, the source should have lost the postcopy + * queue, or potentially the requested pages could have been lost during + * the network down phase. Let's re-sync with the source VM by re-sending + * all the pending pages that we eagerly need, so these threads won't get + * blocked too long due to the recovery. + * + * Without this procedure, the faulted destination VM threads (waiting for + * page requests right before the postcopy is interrupted) can keep hanging + * until the pages are sent by the source during the background copying of + * pages, or another thread faulted on the same address accidentally. + */ + migrate_send_rp_req_pages_pending(mis); + return 0; } diff --git a/migration/trace-events b/migration/trace-events index 338f38b3dd..75de5004ac 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -49,6 +49,7 @@ vmstate_save(const char *idstr, const char *vmsd_name) "%s, %s" vmstate_load(const char *idstr, const char *vmsd_name) "%s, %s" postcopy_pause_incoming(void) "" postcopy_pause_incoming_continued(void) "" +postcopy_page_req_sync(void *host_addr) "sync page req %p" # vmstate.c vmstate_load_field_error(const char *field, int ret) "field \"%s\" load failed, ret = %d" @@ -162,6 +163,7 @@ postcopy_pause_return_path(void) "" postcopy_pause_return_path_continued(void) "" postcopy_pause_continued(void) "" postcopy_start_set_run(void) "" +postcopy_page_req_add(void *addr, int count) "new page req %p total %d" source_return_path_thread_bad_end(void) "" source_return_path_thread_end(void) "" source_return_path_thread_entry(void) "" @@ -272,6 +274,7 @@ postcopy_ram_incoming_cleanup_blocktime(uint64_t total) "total blocktime %" PRIu postcopy_request_shared_page(const char *sharer, const char *rb, uint64_t rb_offset) "for %s in %s offset 0x%"PRIx64 postcopy_request_shared_page_present(const char *sharer, const char *rb, uint64_t rb_offset) "%s already %s offset 0x%"PRIx64 postcopy_wake_shared(uint64_t client_addr, const char *rb) "at 0x%"PRIx64" in %s" +postcopy_page_req_del(void *addr, int count) "resolved page req %p total %d" get_mem_fault_cpu_index(int cpu, uint32_t pid) "cpu: %d, pid: %u" @@ -325,3 +328,16 @@ get_ramblock_vfn_hash(const char *idstr, uint64_t vfn, uint32_t crc) "ramblock n calc_page_dirty_rate(const char *idstr, uint32_t new_crc, uint32_t old_crc) "ramblock name: %s, new crc: %" PRIu32 ", old crc: %" PRIu32 skip_sample_ramblock(const char *idstr, uint64_t ramblock_size) "ramblock name: %s, ramblock size: %" PRIu64 find_page_matched(const char *idstr) "ramblock %s addr or size changed" + +# block.c +migration_block_init_shared(const char *blk_device_name) "Start migration for %s with shared base image" +migration_block_init_full(const char *blk_device_name) "Start full migration for %s" +migration_block_save_device_dirty(int64_t sector) "Error reading sector %" PRId64 +migration_block_flush_blks(const char *action, int submitted, int read_done, int transferred) "%s submitted %d read_done %d transferred %d" +migration_block_save(const char *mig_stage, int submitted, int transferred) "Enter save live %s submitted %d transferred %d" +migration_block_save_complete(void) "Block migration completed" +migration_block_save_pending(uint64_t pending) "Enter save live pending %" PRIu64 + +# page_cache.c +migration_pagecache_init(int64_t max_num_items) "Setting cache buckets to %" PRId64 +migration_pagecache_insert(void) "Error allocating page" diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c index 35e784c9d9..e22d41d73d 100644 --- a/migration/vmstate-types.c +++ b/migration/vmstate-types.c @@ -420,32 +420,6 @@ const VMStateInfo vmstate_info_uint16_equal = { .put = put_uint16, }; -/* floating point */ - -static int get_float64(QEMUFile *f, void *pv, size_t size, - const VMStateField *field) -{ - float64 *v = pv; - - *v = make_float64(qemu_get_be64(f)); - return 0; -} - -static int put_float64(QEMUFile *f, void *pv, size_t size, - const VMStateField *field, QJSON *vmdesc) -{ - uint64_t *v = pv; - - qemu_put_be64(f, float64_val(*v)); - return 0; -} - -const VMStateInfo vmstate_info_float64 = { - .name = "float64", - .get = get_float64, - .put = put_float64, -}; - /* CPU_DoubleU type */ static int get_cpudouble(QEMUFile *f, void *pv, size_t size, diff --git a/migration/vmstate.c b/migration/vmstate.c index bafa890384..e9d2aef66b 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -32,13 +32,13 @@ static int vmstate_n_elems(void *opaque, const VMStateField *field) if (field->flags & VMS_ARRAY) { n_elems = field->num; } else if (field->flags & VMS_VARRAY_INT32) { - n_elems = *(int32_t *)(opaque+field->num_offset); + n_elems = *(int32_t *)(opaque + field->num_offset); } else if (field->flags & VMS_VARRAY_UINT32) { - n_elems = *(uint32_t *)(opaque+field->num_offset); + n_elems = *(uint32_t *)(opaque + field->num_offset); } else if (field->flags & VMS_VARRAY_UINT16) { - n_elems = *(uint16_t *)(opaque+field->num_offset); + n_elems = *(uint16_t *)(opaque + field->num_offset); } else if (field->flags & VMS_VARRAY_UINT8) { - n_elems = *(uint8_t *)(opaque+field->num_offset); + n_elems = *(uint8_t *)(opaque + field->num_offset); } if (field->flags & VMS_MULTIPLY_ELEMENTS) { @@ -54,7 +54,7 @@ static int vmstate_size(void *opaque, const VMStateField *field) int size = field->size; if (field->flags & VMS_VBUFFER) { - size = *(int32_t *)(opaque+field->size_offset); + size = *(int32_t *)(opaque + field->size_offset); if (field->flags & VMS_MULTIPLY) { size *= field->size; } diff --git a/nbd/meson.build b/nbd/meson.build index 0c00a776d3..2baaa36948 100644 --- a/nbd/meson.build +++ b/nbd/meson.build @@ -1,5 +1,7 @@ block_ss.add(files( 'client.c', 'common.c', +)) +blockdev_ss.add(files( 'server.c', )) diff --git a/nbd/server.c b/nbd/server.c index e75c825879..08b621f70a 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -1517,8 +1517,6 @@ static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args, return ret; } - blk_set_allow_aio_context_change(blk, true); - QTAILQ_INIT(&exp->clients); exp->name = g_strdup(arg->name); exp->description = g_strdup(arg->description); diff --git a/net/meson.build b/net/meson.build index 1c7e3a3cb9..1076b0a7ab 100644 --- a/net/meson.build +++ b/net/meson.build @@ -7,7 +7,6 @@ softmmu_ss.add(files( 'eth.c', 'filter-buffer.c', 'filter-mirror.c', - 'filter-replay.c', 'filter-rewriter.c', 'filter.c', 'hub.c', @@ -17,6 +16,8 @@ softmmu_ss.add(files( 'util.c', )) +softmmu_ss.add(when: 'CONFIG_TCG', if_true: files('filter-replay.c')) + softmmu_ss.add(when: 'CONFIG_L2TPV3', if_true: files('l2tpv3.c')) softmmu_ss.add(when: slirp, if_true: files('slirp.c')) softmmu_ss.add(when: ['CONFIG_VDE', vde], if_true: files('vde.c')) diff --git a/pc-bios/descriptors/meson.build b/pc-bios/descriptors/meson.build index 3798d32372..7040834573 100644 --- a/pc-bios/descriptors/meson.build +++ b/pc-bios/descriptors/meson.build @@ -8,7 +8,7 @@ foreach f: [ ] configure_file(input: files(f), output: f, - configuration: {'DATADIR': config_host['qemu_datadir']}, - install: install_blobs, + configuration: {'DATADIR': qemu_datadir}, + install: get_option('install_blobs'), install_dir: qemu_datadir / 'firmware') endforeach diff --git a/pc-bios/meson.build b/pc-bios/meson.build index a0d21be432..fab323af84 100644 --- a/pc-bios/meson.build +++ b/pc-bios/meson.build @@ -1,7 +1,8 @@ -bzip2 = find_program('bzip2') - -install_blobs = 'INSTALL_BLOBS' in config_host -if 'DECOMPRESS_EDK2_BLOBS' in config_host +if 'arm-softmmu' in target_dirs or \ + 'aarch64-softmmu' in target_dirs or \ + 'i386-softmmu' in target_dirs or \ + 'x86_64-softmmu' in target_dirs + bzip2 = find_program('bzip2', required: true) fds = [ 'edk2-aarch64-code.fd', 'edk2-arm-code.fd', @@ -18,7 +19,7 @@ if 'DECOMPRESS_EDK2_BLOBS' in config_host output: f, input: '@0@.bz2'.format(f), capture: true, - install: install_blobs, + install: get_option('install_blobs'), install_dir: qemu_datadir, command: [ bzip2, '-dc', '@INPUT0@' ]) endforeach @@ -85,8 +86,8 @@ blobs = files( 'npcm7xx_bootrom.bin', ) -if install_blobs - install_data(blobs, install_dir: config_host['qemu_datadir']) +if get_option('install_blobs') + install_data(blobs, install_dir: qemu_datadir) endif subdir('descriptors') diff --git a/qapi/block-core.json b/qapi/block-core.json index ee5ebef7f2..e00fc27b5e 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -948,6 +948,27 @@ 'discard-bytes-ok': 'uint64' } } ## +# @BlockStatsSpecificNvme: +# +# NVMe driver statistics +# +# @completion-errors: The number of completion errors. +# +# @aligned-accesses: The number of aligned accesses performed by +# the driver. +# +# @unaligned-accesses: The number of unaligned accesses performed by +# the driver. +# +# Since: 5.2 +## +{ 'struct': 'BlockStatsSpecificNvme', + 'data': { + 'completion-errors': 'uint64', + 'aligned-accesses': 'uint64', + 'unaligned-accesses': 'uint64' } } + +## # @BlockStatsSpecific: # # Block driver specific statistics @@ -959,7 +980,8 @@ 'discriminator': 'driver', 'data': { 'file': 'BlockStatsSpecificFile', - 'host_device': 'BlockStatsSpecificFile' } } + 'host_device': 'BlockStatsSpecificFile', + 'nvme': 'BlockStatsSpecificNvme' } } ## # @BlockStats: diff --git a/qapi/block-export.json b/qapi/block-export.json index 65804834d9..480c497690 100644 --- a/qapi/block-export.json +++ b/qapi/block-export.json @@ -85,6 +85,25 @@ '*bitmap': 'str' } } ## +# @BlockExportOptionsVhostUserBlk: +# +# A vhost-user-blk block export. +# +# @addr: The vhost-user socket on which to listen. Both 'unix' and 'fd' +# SocketAddress types are supported. Passed fds must be UNIX domain +# sockets. +# @logical-block-size: Logical block size in bytes. Defaults to 512 bytes. +# @num-queues: Number of request virtqueues. Must be greater than 0. Defaults +# to 1. +# +# Since: 5.2 +## +{ 'struct': 'BlockExportOptionsVhostUserBlk', + 'data': { 'addr': 'SocketAddress', + '*logical-block-size': 'size', + '*num-queues': 'uint16'} } + +## # @NbdServerAddOptions: # # An NBD block export. @@ -180,11 +199,12 @@ # An enumeration of block export types # # @nbd: NBD export +# @vhost-user-blk: vhost-user-blk export (since 5.2) # # Since: 4.2 ## { 'enum': 'BlockExportType', - 'data': [ 'nbd' ] } + 'data': [ 'nbd', 'vhost-user-blk' ] } ## # @BlockExportOptions: @@ -203,17 +223,29 @@ # export before completion is signalled. (since: 5.2; # default: false) # +# @iothread: The name of the iothread object where the export will run. The +# default is to use the thread currently associated with the +# block node. (since: 5.2) +# +# @fixed-iothread: True prevents the block node from being moved to another +# thread while the export is active. If true and @iothread is +# given, export creation fails if the block node cannot be +# moved to the iothread. The default is false. (since: 5.2) +# # Since: 4.2 ## { 'union': 'BlockExportOptions', 'base': { 'type': 'BlockExportType', 'id': 'str', + '*fixed-iothread': 'bool', + '*iothread': 'str', 'node-name': 'str', '*writable': 'bool', '*writethrough': 'bool' }, 'discriminator': 'type', 'data': { - 'nbd': 'BlockExportOptionsNbd' + 'nbd': 'BlockExportOptionsNbd', + 'vhost-user-blk': 'BlockExportOptionsVhostUserBlk' } } ## diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index cab8234235..b3620f29e5 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -34,9 +34,9 @@ SRST ERST DEF("commit", img_commit, - "commit [--object objectdef] [--image-opts] [-q] [-f fmt] [-t cache] [-b base] [-d] [-p] filename") + "commit [--object objectdef] [--image-opts] [-q] [-f fmt] [-t cache] [-b base] [-r rate_limit] [-d] [-p] filename") SRST -.. option:: commit [--object OBJECTDEF] [--image-opts] [-q] [-f FMT] [-t CACHE] [-b BASE] [-d] [-p] FILENAME +.. option:: commit [--object OBJECTDEF] [--image-opts] [-q] [-f FMT] [-t CACHE] [-b BASE] [-r RATE_LIMIT] [-d] [-p] FILENAME ERST DEF("compare", img_compare, @@ -46,9 +46,9 @@ SRST ERST DEF("convert", img_convert, - "convert [--object objectdef] [--image-opts] [--target-image-opts] [--target-is-zero] [--bitmaps] [-U] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] [--salvage] filename [filename2 [...]] output_filename") + "convert [--object objectdef] [--image-opts] [--target-image-opts] [--target-is-zero] [--bitmaps] [-U] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-r rate_limit] [-m num_coroutines] [-W] [--salvage] filename [filename2 [...]] output_filename") SRST -.. option:: convert [--object OBJECTDEF] [--image-opts] [--target-image-opts] [--target-is-zero] [--bitmaps] [-U] [-C] [-c] [-p] [-q] [-n] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-O OUTPUT_FMT] [-B BACKING_FILE] [-o OPTIONS] [-l SNAPSHOT_PARAM] [-S SPARSE_SIZE] [-m NUM_COROUTINES] [-W] [--salvage] FILENAME [FILENAME2 [...]] OUTPUT_FILENAME +.. option:: convert [--object OBJECTDEF] [--image-opts] [--target-image-opts] [--target-is-zero] [--bitmaps] [-U] [-C] [-c] [-p] [-q] [-n] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-O OUTPUT_FMT] [-B BACKING_FILE] [-o OPTIONS] [-l SNAPSHOT_PARAM] [-S SPARSE_SIZE] [-r RATE_LIMIT] [-m NUM_COROUTINES] [-W] [--salvage] FILENAME [FILENAME2 [...]] OUTPUT_FILENAME ERST DEF("create", img_create, diff --git a/qemu-img.c b/qemu-img.c index 2103507936..a968c74cba 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -50,6 +50,8 @@ #include "block/qapi.h" #include "crypto/init.h" #include "trace/control.h" +#include "qemu/throttle.h" +#include "block/throttle-groups.h" #define QEMU_IMG_VERSION "qemu-img version " QEMU_FULL_VERSION \ "\n" QEMU_COPYRIGHT "\n" @@ -980,6 +982,7 @@ static int img_commit(int argc, char **argv) CommonBlockJobCBInfo cbi; bool image_opts = false; AioContext *aio_context; + int64_t rate_limit = 0; fmt = NULL; cache = BDRV_DEFAULT_CACHE; @@ -991,7 +994,7 @@ static int img_commit(int argc, char **argv) {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":f:ht:b:dpq", + c = getopt_long(argc, argv, ":f:ht:b:dpqr:", long_options, NULL); if (c == -1) { break; @@ -1026,6 +1029,12 @@ static int img_commit(int argc, char **argv) case 'q': quiet = true; break; + case 'r': + rate_limit = cvtnum("rate limit", optarg); + if (rate_limit < 0) { + return 1; + } + break; case OPTION_OBJECT: { QemuOpts *opts; opts = qemu_opts_parse_noisily(&qemu_object_opts, @@ -1099,7 +1108,7 @@ static int img_commit(int argc, char **argv) aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); - commit_active_start("commit", bs, base_bs, JOB_DEFAULT, 0, + commit_active_start("commit", bs, base_bs, JOB_DEFAULT, rate_limit, BLOCKDEV_ON_ERROR_REPORT, NULL, common_block_job_cb, &cbi, false, &local_err); aio_context_release(aio_context); @@ -1662,6 +1671,7 @@ enum ImgConvertBlockStatus { }; #define MAX_COROUTINES 16 +#define CONVERT_THROTTLE_GROUP "img_convert" typedef struct ImgConvertState { BlockBackend **src; @@ -2177,6 +2187,17 @@ static int convert_copy_bitmaps(BlockDriverState *src, BlockDriverState *dst) #define MAX_BUF_SECTORS 32768 +static void set_rate_limit(BlockBackend *blk, int64_t rate_limit) +{ + ThrottleConfig cfg; + + throttle_config_init(&cfg); + cfg.buckets[THROTTLE_BPS_WRITE].avg = rate_limit; + + blk_io_limits_enable(blk, CONVERT_THROTTLE_GROUP); + blk_set_io_limits(blk, &cfg); +} + static int img_convert(int argc, char **argv) { int c, bs_i, flags, src_flags = 0; @@ -2197,6 +2218,7 @@ static int img_convert(int argc, char **argv) bool force_share = false; bool explict_min_sparse = false; bool bitmaps = false; + int64_t rate_limit = 0; ImgConvertState s = (ImgConvertState) { /* Need at least 4k of zeros for sparse detection */ @@ -2219,7 +2241,7 @@ static int img_convert(int argc, char **argv) {"bitmaps", no_argument, 0, OPTION_BITMAPS}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":hf:O:B:Cco:l:S:pt:T:qnm:WU", + c = getopt_long(argc, argv, ":hf:O:B:Cco:l:S:pt:T:qnm:WUr:", long_options, NULL); if (c == -1) { break; @@ -2316,6 +2338,12 @@ static int img_convert(int argc, char **argv) case 'U': force_share = true; break; + case 'r': + rate_limit = cvtnum("rate limit", optarg); + if (rate_limit < 0) { + goto fail_getopt; + } + break; case OPTION_OBJECT: { QemuOpts *object_opts; object_opts = qemu_opts_parse_noisily(&qemu_object_opts, @@ -2705,6 +2733,10 @@ static int img_convert(int argc, char **argv) s.cluster_sectors = bdi.cluster_size / BDRV_SECTOR_SIZE; } + if (rate_limit) { + set_rate_limit(s.target, rate_limit); + } + ret = convert_do_copy(&s); /* Now copy the bitmaps */ diff --git a/qemu-nbd.c b/qemu-nbd.c index bc644a0670..a0701cdf36 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -25,6 +25,7 @@ #include "qapi/error.h" #include "qemu/cutils.h" #include "sysemu/block-backend.h" +#include "sysemu/runstate.h" /* for qemu_system_killed() prototype */ #include "block/block_int.h" #include "block/nbd.h" #include "qemu/main-loop.h" @@ -155,7 +156,11 @@ QEMU_COPYRIGHT "\n" } #ifdef CONFIG_POSIX -static void termsig_handler(int signum) +/* + * The client thread uses SIGTERM to interrupt the server. A signal + * handler ensures that "qemu-nbd -v -c" exits with a nice status code. + */ +void qemu_system_killed(int signum, pid_t pid) { qatomic_cmpxchg(&state, RUNNING, TERMINATE); qemu_notify_event(); @@ -582,18 +587,8 @@ int main(int argc, char **argv) BlockExportOptions *export_opts; #ifdef CONFIG_POSIX - /* - * Exit gracefully on various signals, which includes SIGTERM used - * by 'qemu-nbd -v -c'. - */ - struct sigaction sa_sigterm; - memset(&sa_sigterm, 0, sizeof(sa_sigterm)); - sa_sigterm.sa_handler = termsig_handler; - sigaction(SIGTERM, &sa_sigterm, NULL); - sigaction(SIGINT, &sa_sigterm, NULL); - sigaction(SIGHUP, &sa_sigterm, NULL); - - signal(SIGPIPE, SIG_IGN); + os_setup_early_signal_handling(); + os_setup_signal_handling(); #endif socket_init(); diff --git a/qemu-options.hx b/qemu-options.hx index 1da52a269c..2c83390504 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -34,7 +34,6 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ " dea-key-wrap=on|off controls support for DEA key wrapping (default=on)\n" " suppress-vmdesc=on|off disables self-describing migration (default=off)\n" " nvdimm=on|off controls NVDIMM support (default=off)\n" - " enforce-config-section=on|off enforce configuration section migration (default=off)\n" " memory-encryption=@var{} memory encryption object to use (default=none)\n" " hmat=on|off controls ACPI HMAT support (default=off)\n", QEMU_ARCH_ALL) @@ -91,13 +90,6 @@ SRST ``nvdimm=on|off`` Enables or disables NVDIMM support. The default is off. - ``enforce-config-section=on|off`` - If ``enforce-config-section`` is set to on, force migration code - to send configuration section even if the machine-type sets the - ``migration.send-configuration`` property to off. NOTE: this - parameter is deprecated. Please use ``-global`` - ``migration.send-configuration``\ =on\|off instead. - ``memory-encryption=`` Memory encryption object to use. The default is none. @@ -4351,9 +4343,6 @@ SRST Enable FIPS 140-2 compliance mode. ERST -HXCOMM Deprecated by -accel tcg -DEF("no-kvm", 0, QEMU_OPTION_no_kvm, "", QEMU_ARCH_I386) - DEF("msg", HAS_ARG, QEMU_OPTION_msg, "-msg [timestamp[=on|off]][,guest-name=[on|off]]\n" " control error message format\n" diff --git a/replay/meson.build b/replay/meson.build index f91163fb1e..21aefad220 100644 --- a/replay/meson.build +++ b/replay/meson.build @@ -1,4 +1,4 @@ -softmmu_ss.add(files( +softmmu_ss.add(when: 'CONFIG_TCG', if_true: files( 'replay.c', 'replay-internal.c', 'replay-events.c', @@ -10,4 +10,4 @@ softmmu_ss.add(files( 'replay-audio.c', 'replay-random.c', 'replay-debugging.c', -)) +), if_false: files('stubs-system.c')) diff --git a/replay/stubs-system.c b/replay/stubs-system.c new file mode 100644 index 0000000000..5c262b08f1 --- /dev/null +++ b/replay/stubs-system.c @@ -0,0 +1,96 @@ +#include "qemu/osdep.h" +#include "sysemu/replay.h" +#include "ui/input.h" + +void replay_input_event(QemuConsole *src, InputEvent *evt) +{ + qemu_input_event_send_impl(src, evt); +} + +void replay_input_sync_event(void) +{ + qemu_input_event_sync_impl(); +} + +void replay_add_blocker(Error *reason) +{ +} +void replay_audio_in(size_t *recorded, void *samples, size_t *wpos, size_t size) +{ +} +void replay_audio_out(size_t *played) +{ +} +void replay_breakpoint(void) +{ +} +bool replay_can_snapshot(void) +{ + return true; +} +void replay_configure(struct QemuOpts *opts) +{ +} +void replay_flush_events(void) +{ +} +void replay_gdb_attached(void) +{ +} +bool replay_running_debug(void) +{ + return false; +} +void replay_shutdown_request(ShutdownCause cause) +{ +} +void replay_start(void) +{ +} +void replay_vmstate_init(void) +{ +} + +#include "monitor/monitor.h" +#include "monitor/hmp.h" +#include "qapi/qapi-commands-replay.h" +#include "qapi/error.h" +#include "qemu/error-report.h" + +void hmp_info_replay(Monitor *mon, const QDict *qdict) +{ + error_report("replay support not available"); +} +void hmp_replay_break(Monitor *mon, const QDict *qdict) +{ + error_report("replay support not available"); +} +void hmp_replay_delete_break(Monitor *mon, const QDict *qdict) +{ + error_report("replay support not available"); +} +void hmp_replay_seek(Monitor *mon, const QDict *qdict) +{ + error_report("replay support not available"); +} +ReplayInfo *qmp_query_replay(Error **errp) +{ + error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, + "replay support not available"); + return NULL; +} +void qmp_replay_break(int64_t icount, Error **errp) +{ + error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, + "replay support not available"); +} +void qmp_replay_delete_break(Error **errp) +{ + error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, + "replay support not available"); +} +void qmp_replay_seek(int64_t icount, Error **errp) +{ + error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, + "replay support not available"); +} diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 6ed34970f9..88c858f67c 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -392,7 +392,7 @@ if ($chk_branch) { close $HASH; - die "$P: no revisions returned for revlist '$chk_branch'\n" + die "$P: no revisions returned for revlist '$ARGV[0]'\n" unless @patches; my $i = 1; diff --git a/scripts/gensyscalls.sh b/scripts/gensyscalls.sh index b7b8456f63..bba9fb052c 100755 --- a/scripts/gensyscalls.sh +++ b/scripts/gensyscalls.sh @@ -86,8 +86,7 @@ generate_syscall_nr() read_includes $arch $bits | filter_defines | rename_defines | \ evaluate_values | sort -n -k 3 echo - echo "#endif /* ${guard} */" - echo) > "$file" + echo "#endif /* ${guard} */") > "$file" } mkdir "$TMP/asm" diff --git a/scripts/oss-fuzz/build.sh b/scripts/oss-fuzz/build.sh index 0c3ca9e06f..fcae4a0c26 100755 --- a/scripts/oss-fuzz/build.sh +++ b/scripts/oss-fuzz/build.sh @@ -62,6 +62,9 @@ fi mkdir -p "$DEST_DIR/lib/" # Copy the shared libraries here +mkdir -p "$DEST_DIR/bin/" # Copy executables that shouldn't + # be treated as fuzzers by oss-fuzz here + # Build once to get the list of dynamic lib paths, and copy them over ../configure --disable-werror --cc="$CC" --cxx="$CXX" --enable-fuzzing \ --prefix="$DEST_DIR" --bindir="$DEST_DIR" --datadir="$DEST_DIR/data/" \ @@ -88,13 +91,22 @@ make "-j$(nproc)" qemu-fuzz-i386 V=1 # Copy over the datadir cp -r ../pc-bios/ "$DEST_DIR/pc-bios" +cp "./qemu-fuzz-i386" "$DEST_DIR/bin/" + # Run the fuzzer with no arguments, to print the help-string and get the list # of available fuzz-targets. Copy over the qemu-fuzz-i386, naming it according # to each available fuzz target (See 05509c8e6d fuzz: select fuzz target using # executable name) for target in $(./qemu-fuzz-i386 | awk '$1 ~ /\*/ {print $2}'); do - cp qemu-fuzz-i386 "$DEST_DIR/qemu-fuzz-i386-target-$target" + # Ignore the generic-fuzz target, as it requires some environment variables + # to be configured. We have some generic-fuzz-{pc-q35, floppy, ...} targets + # that are thin wrappers around this target that set the required + # environment variables according to predefined configs. + if [ "$target" != "generic-fuzz" ]; then + ln "$DEST_DIR/bin/qemu-fuzz-i386" \ + "$DEST_DIR/qemu-fuzz-i386-target-$target" + fi done echo "Done. The fuzzers are located in $DEST_DIR" diff --git a/scripts/oss-fuzz/minimize_qtest_trace.py b/scripts/oss-fuzz/minimize_qtest_trace.py new file mode 100755 index 0000000000..5e405a0d5f --- /dev/null +++ b/scripts/oss-fuzz/minimize_qtest_trace.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +This takes a crashing qtest trace and tries to remove superflous operations +""" + +import sys +import os +import subprocess +import time +import struct + +QEMU_ARGS = None +QEMU_PATH = None +TIMEOUT = 5 +CRASH_TOKEN = None + +write_suffix_lookup = {"b": (1, "B"), + "w": (2, "H"), + "l": (4, "L"), + "q": (8, "Q")} + +def usage(): + sys.exit("""\ +Usage: QEMU_PATH="/path/to/qemu" QEMU_ARGS="args" {} input_trace output_trace +By default, will try to use the second-to-last line in the output to identify +whether the crash occred. Optionally, manually set a string that idenitifes the +crash by setting CRASH_TOKEN= +""".format((sys.argv[0]))) + +def check_if_trace_crashes(trace, path): + global CRASH_TOKEN + with open(path, "w") as tracefile: + tracefile.write("".join(trace)) + + rc = subprocess.Popen("timeout -s 9 {timeout}s {qemu_path} {qemu_args} 2>&1\ + < {trace_path}".format(timeout=TIMEOUT, + qemu_path=QEMU_PATH, + qemu_args=QEMU_ARGS, + trace_path=path), + shell=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + stdo = rc.communicate()[0] + output = stdo.decode('unicode_escape') + if rc.returncode == 137: # Timed Out + return False + if len(output.splitlines()) < 2: + return False + + if CRASH_TOKEN is None: + CRASH_TOKEN = output.splitlines()[-2] + + return CRASH_TOKEN in output + + +def minimize_trace(inpath, outpath): + global TIMEOUT + with open(inpath) as f: + trace = f.readlines() + start = time.time() + if not check_if_trace_crashes(trace, outpath): + sys.exit("The input qtest trace didn't cause a crash...") + end = time.time() + print("Crashed in {} seconds".format(end-start)) + TIMEOUT = (end-start)*5 + print("Setting the timeout for {} seconds".format(TIMEOUT)) + print("Identifying Crashes by this string: {}".format(CRASH_TOKEN)) + + i = 0 + newtrace = trace[:] + # For each line + while i < len(newtrace): + # 1.) Try to remove it completely and reproduce the crash. If it works, + # we're done. + prior = newtrace[i] + print("Trying to remove {}".format(newtrace[i])) + # Try to remove the line completely + newtrace[i] = "" + if check_if_trace_crashes(newtrace, outpath): + i += 1 + continue + newtrace[i] = prior + + # 2.) Try to replace write{bwlq} commands with a write addr, len + # command. Since this can require swapping endianness, try both LE and + # BE options. We do this, so we can "trim" the writes in (3) + if (newtrace[i].startswith("write") and not + newtrace[i].startswith("write ")): + suffix = newtrace[i].split()[0][-1] + assert(suffix in write_suffix_lookup) + addr = int(newtrace[i].split()[1], 16) + value = int(newtrace[i].split()[2], 16) + for endianness in ['<', '>']: + data = struct.pack("{end}{size}".format(end=endianness, + size=write_suffix_lookup[suffix][1]), + value) + newtrace[i] = "write {addr} {size} 0x{data}\n".format( + addr=hex(addr), + size=hex(write_suffix_lookup[suffix][0]), + data=data.hex()) + if(check_if_trace_crashes(newtrace, outpath)): + break + else: + newtrace[i] = prior + + # 3.) If it is a qtest write command: write addr len data, try to split + # it into two separate write commands. If splitting the write down the + # middle does not work, try to move the pivot "left" and retry, until + # there is no space left. The idea is to prune unneccessary bytes from + # long writes, while accommodating arbitrary MemoryRegion access sizes + # and alignments. + if newtrace[i].startswith("write "): + addr = int(newtrace[i].split()[1], 16) + length = int(newtrace[i].split()[2], 16) + data = newtrace[i].split()[3][2:] + if length > 1: + leftlength = int(length/2) + rightlength = length - leftlength + newtrace.insert(i+1, "") + while leftlength > 0: + newtrace[i] = "write {addr} {size} 0x{data}\n".format( + addr=hex(addr), + size=hex(leftlength), + data=data[:leftlength*2]) + newtrace[i+1] = "write {addr} {size} 0x{data}\n".format( + addr=hex(addr+leftlength), + size=hex(rightlength), + data=data[leftlength*2:]) + if check_if_trace_crashes(newtrace, outpath): + break + else: + leftlength -= 1 + rightlength += 1 + if check_if_trace_crashes(newtrace, outpath): + i -= 1 + else: + newtrace[i] = prior + del newtrace[i+1] + i += 1 + check_if_trace_crashes(newtrace, outpath) + + +if __name__ == '__main__': + if len(sys.argv) < 3: + usage() + + QEMU_PATH = os.getenv("QEMU_PATH") + QEMU_ARGS = os.getenv("QEMU_ARGS") + if QEMU_PATH is None or QEMU_ARGS is None: + usage() + # if "accel" not in QEMU_ARGS: + # QEMU_ARGS += " -accel qtest" + CRASH_TOKEN = os.getenv("CRASH_TOKEN") + QEMU_ARGS += " -qtest stdio -monitor none -serial none " + minimize_trace(sys.argv[1], sys.argv[2]) diff --git a/scripts/oss-fuzz/reorder_fuzzer_qtest_trace.py b/scripts/oss-fuzz/reorder_fuzzer_qtest_trace.py new file mode 100755 index 0000000000..890e1def85 --- /dev/null +++ b/scripts/oss-fuzz/reorder_fuzzer_qtest_trace.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Use this to convert qtest log info from a generic fuzzer input into a qtest +trace that you can feed into a standard qemu-system process. Example usage: + +QEMU_FUZZ_ARGS="-machine q35,accel=qtest" QEMU_FUZZ_OBJECTS="*" \ + ./i386-softmmu/qemu-fuzz-i386 --fuzz-target=generic-pci-fuzz +# .. Finds some crash +QTEST_LOG=1 FUZZ_SERIALIZE_QTEST=1 \ +QEMU_FUZZ_ARGS="-machine q35,accel=qtest" QEMU_FUZZ_OBJECTS="*" \ + ./i386-softmmu/qemu-fuzz-i386 --fuzz-target=generic-pci-fuzz + /path/to/crash 2> qtest_log_output +scripts/oss-fuzz/reorder_fuzzer_qtest_trace.py qtest_log_output > qtest_trace +./i386-softmmu/qemu-fuzz-i386 -machine q35,accel=qtest \ + -qtest stdin < qtest_trace + +### Details ### + +Some fuzzer make use of hooks that allow us to populate some memory range, just +before a DMA read from that range. This means that the fuzzer can produce +activity that looks like: + [start] read from mmio addr + [end] read from mmio addr + [start] write to pio addr + [start] fill a DMA buffer just in time + [end] fill a DMA buffer just in time + [start] fill a DMA buffer just in time + [end] fill a DMA buffer just in time + [end] write to pio addr + [start] read from mmio addr + [end] read from mmio addr + +We annotate these "nested" DMA writes, so with QTEST_LOG=1 the QTest trace +might look something like: +[R +0.028431] readw 0x10000 +[R +0.028434] outl 0xc000 0xbeef # Triggers a DMA read from 0xbeef and 0xbf00 +[DMA][R +0.034639] write 0xbeef 0x2 0xAAAA +[DMA][R +0.034639] write 0xbf00 0x2 0xBBBB +[R +0.028431] readw 0xfc000 + +This script would reorder the above trace so it becomes: +readw 0x10000 +write 0xbeef 0x2 0xAAAA +write 0xbf00 0x2 0xBBBB +outl 0xc000 0xbeef +readw 0xfc000 + +I.e. by the time, 0xc000 tries to read from DMA, those DMA buffers have already +been set up, removing the need for the DMA hooks. We can simply provide this +reordered trace via -qtest stdio to reproduce the input + +Note: this won't work for traces where the device tries to read from the same +DMA region twice in between MMIO/PIO commands. E.g: + [R +0.028434] outl 0xc000 0xbeef + [DMA][R +0.034639] write 0xbeef 0x2 0xAAAA + [DMA][R +0.034639] write 0xbeef 0x2 0xBBBB + +The fuzzer will annotate suspected double-fetches with [DOUBLE-FETCH]. This +script looks for these tags and warns the users that the resulting trace might +not reproduce the bug. +""" + +import sys + +__author__ = "Alexander Bulekov <alxndr@bu.edu>" +__copyright__ = "Copyright (C) 2020, Red Hat, Inc." +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Alexander Bulekov" +__email__ = "alxndr@bu.edu" + + +def usage(): + sys.exit("Usage: {} /path/to/qtest_log_output".format((sys.argv[0]))) + + +def main(filename): + with open(filename, "r") as f: + trace = f.readlines() + + # Leave only lines that look like logged qtest commands + trace[:] = [x.strip() for x in trace if "[R +" in x + or "[S +" in x and "CLOSED" not in x] + + for i in range(len(trace)): + if i+1 < len(trace): + if "[DMA]" in trace[i+1]: + if "[DOUBLE-FETCH]" in trace[i+1]: + sys.stderr.write("Warning: Likely double fetch on line" + "{}.\n There will likely be problems " + "reproducing behavior with the " + "resulting qtest trace\n\n".format(i+1)) + trace[i], trace[i+1] = trace[i+1], trace[i] + for line in trace: + print(line.split("]")[-1].strip()) + + +if __name__ == '__main__': + if len(sys.argv) == 1: + usage() + main(sys.argv[1]) diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell index c5eef06f3f..b4d06096ab 100755 --- a/scripts/qmp/qmp-shell +++ b/scripts/qmp/qmp-shell @@ -260,7 +260,7 @@ class QMPShell(qmp.QEMUMonitorProtocol): indent = None if self._pretty: indent = 4 - jsobj = json.dumps(qmp, indent=indent) + jsobj = json.dumps(qmp, indent=indent, sort_keys=self._pretty) print(str(jsobj)) def _execute_cmd(self, cmdline): diff --git a/scripts/tracetool.py b/scripts/tracetool.py index 31146242b7..31146242b7 100644..100755 --- a/scripts/tracetool.py +++ b/scripts/tracetool.py diff --git a/scripts/undefsym.py b/scripts/undefsym.py index 69a895cd26..4b6a72d95f 100644 --- a/scripts/undefsym.py +++ b/scripts/undefsym.py @@ -15,12 +15,11 @@ def filter_lines_set(stdout, from_staticlib): linesSet = set() for line in stdout.splitlines(): tokens = line.split(b' ') - if len(tokens) >= 1: - if len(tokens) > 1: - if from_staticlib and tokens[1] == b'U': - continue - if not from_staticlib and tokens[1] != b'U': - continue + if len(tokens) >= 2: + if from_staticlib and tokens[1] == b'U': + continue + if not from_staticlib and tokens[1] != b'U': + continue new_line = b'-Wl,-u,' + tokens[0] if not new_line in linesSet: linesSet.add(new_line) diff --git a/softmmu/memory.c b/softmmu/memory.c index 403ff3abc9..ee4a6bc168 100644 --- a/softmmu/memory.c +++ b/softmmu/memory.c @@ -656,6 +656,19 @@ static void render_memory_region(FlatView *view, } } +void flatview_for_each_range(FlatView *fv, flatview_cb cb , void *opaque) +{ + FlatRange *fr; + + assert(fv); + assert(cb); + + FOR_EACH_FLAT_RANGE(fr, fv) { + if (cb(fr->addr.start, fr->addr.size, fr->mr, opaque)) + break; + } +} + static MemoryRegion *memory_region_get_flatview_root(MemoryRegion *mr) { while (mr->enabled) { @@ -1420,6 +1433,7 @@ MemTxResult memory_region_dispatch_read(MemoryRegion *mr, unsigned size = memop_size(op); MemTxResult r; + fuzz_dma_read_cb(addr, size, mr, false); if (!memory_region_access_valid(mr, addr, size, false, attrs)) { *pval = unassigned_mem_read(mr, addr, size); return MEMTX_DECODE_ERROR; @@ -3233,6 +3247,19 @@ void memory_region_init_rom_device(MemoryRegion *mr, vmstate_register_ram(mr, owner_dev); } +/* + * Support softmmu builds with CONFIG_FUZZ using a weak symbol and a stub for + * the fuzz_dma_read_cb callback + */ +#ifdef CONFIG_FUZZ +void __attribute__((weak)) fuzz_dma_read_cb(size_t addr, + size_t len, + MemoryRegion *mr, + bool is_write) +{ +} +#endif + static const TypeInfo memory_region_info = { .parent = TYPE_OBJECT, .name = TYPE_MEMORY_REGION, diff --git a/softmmu/physmem.c b/softmmu/physmem.c index e319fb2a1e..a9adedb9f8 100644 --- a/softmmu/physmem.c +++ b/softmmu/physmem.c @@ -2832,6 +2832,7 @@ MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, stn_he_p(buf, l, val); } else { /* RAM case */ + fuzz_dma_read_cb(addr, len, mr, false); ram_ptr = qemu_ram_ptr_length(mr->ram_block, addr1, &l, false); memcpy(buf, ram_ptr, l); } @@ -3192,6 +3193,7 @@ void *address_space_map(AddressSpace *as, memory_region_ref(mr); *plen = flatview_extend_translation(fv, addr, len, mr, xlat, l, is_write, attrs); + fuzz_dma_read_cb(addr, *plen, mr, is_write); ptr = qemu_ram_ptr_length(mr->ram_block, xlat, plen, true); return ptr; diff --git a/softmmu/vl.c b/softmmu/vl.c index 14fc527fc6..7c1c6d37ef 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -2540,6 +2540,10 @@ static bool object_create_initial(const char *type, QemuOpts *opts) } #endif + /* Reason: vhost-user-blk-server property "node-name" */ + if (g_str_equal(type, "vhost-user-blk-server")) { + return false; + } /* * Reason: filter-* property "netdev" etc. */ @@ -3502,10 +3506,6 @@ void qemu_init(int argc, char **argv, char **envp) exit(1); } break; - case QEMU_OPTION_no_kvm: - olist = qemu_find_opts("machine"); - qemu_opts_parse_noisily(olist, "accel=tcg", false); - break; case QEMU_OPTION_accel: accel_opts = qemu_opts_parse_noisily(qemu_find_opts("accel"), optarg, true); @@ -3974,18 +3974,6 @@ void qemu_init(int argc, char **argv, char **envp) exit(0); } - /* machine_class: default to UP */ - machine_class->max_cpus = machine_class->max_cpus ?: 1; - machine_class->min_cpus = machine_class->min_cpus ?: 1; - machine_class->default_cpus = machine_class->default_cpus ?: 1; - - /* default to machine_class->default_cpus */ - current_machine->smp.cpus = machine_class->default_cpus; - current_machine->smp.max_cpus = machine_class->default_cpus; - current_machine->smp.cores = 1; - current_machine->smp.threads = 1; - current_machine->smp.sockets = 1; - machine_class->smp_parse(current_machine, qemu_opts_find(qemu_find_opts("smp-opts"), NULL)); diff --git a/storage-daemon/meson.build b/storage-daemon/meson.build index 0409acc3f5..c5adce81c3 100644 --- a/storage-daemon/meson.build +++ b/storage-daemon/meson.build @@ -1,7 +1,6 @@ qsd_ss = ss.source_set() qsd_ss.add(files('qemu-storage-daemon.c')) -qsd_ss.add(block, chardev, qmp, qom, qemuutil) -qsd_ss.add_all(blockdev_ss) +qsd_ss.add(blockdev, chardev, qmp, qom, qemuutil) subdir('qapi') diff --git a/stubs/blk-exp-close-all.c b/stubs/blk-exp-close-all.c new file mode 100644 index 0000000000..1c71316763 --- /dev/null +++ b/stubs/blk-exp-close-all.c @@ -0,0 +1,7 @@ +#include "qemu/osdep.h" +#include "block/export.h" + +/* Only used in programs that support block exports (libblockdev.fa) */ +void blk_exp_close_all(void) +{ +} diff --git a/stubs/meson.build b/stubs/meson.build index 67f2a8c069..82b7ba60ab 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -1,6 +1,7 @@ stub_ss.add(files('arch_type.c')) stub_ss.add(files('bdrv-next-monitor-owned.c')) stub_ss.add(files('blk-commit-all.c')) +stub_ss.add(files('blk-exp-close-all.c')) stub_ss.add(files('blockdev-close-all-bdrv-states.c')) stub_ss.add(files('change-state-handler.c')) stub_ss.add(files('cmos.c')) @@ -32,7 +33,6 @@ stub_ss.add(files('qtest.c')) stub_ss.add(files('ram-block.c')) stub_ss.add(files('ramfb.c')) stub_ss.add(files('replay.c')) -stub_ss.add(files('replay-user.c')) stub_ss.add(files('runstate-check.c')) stub_ss.add(files('set-fd-handler.c')) stub_ss.add(files('sysbus.c')) @@ -46,6 +46,9 @@ stub_ss.add(files('vmstate.c')) stub_ss.add(files('vm-stop.c')) stub_ss.add(files('win32-kbd-hook.c')) stub_ss.add(files('cpu-synchronize-state.c')) +if have_block + stub_ss.add(files('replay-tools.c')) +endif if have_system stub_ss.add(files('semihost.c')) stub_ss.add(files('xen-hw-stub.c')) diff --git a/stubs/replay-tools.c b/stubs/replay-tools.c new file mode 100644 index 0000000000..c06b360e22 --- /dev/null +++ b/stubs/replay-tools.c @@ -0,0 +1,83 @@ +#include "qemu/osdep.h" +#include "sysemu/replay.h" +#include "block/aio.h" + +bool replay_events_enabled(void) +{ + return false; +} + +int64_t replay_save_clock(unsigned int kind, int64_t clock, int64_t raw_icount) +{ + abort(); + return 0; +} + +int64_t replay_read_clock(unsigned int kind) +{ + abort(); + return 0; +} + +uint64_t replay_get_current_icount(void) +{ + return 0; +} + +void replay_bh_schedule_event(QEMUBH *bh) +{ + qemu_bh_schedule(bh); +} + +void replay_bh_schedule_oneshot_event(AioContext *ctx, + QEMUBHFunc *cb, void *opaque) +{ + aio_bh_schedule_oneshot(ctx, cb, opaque); +} + +bool replay_checkpoint(ReplayCheckpoint checkpoint) +{ + return true; +} + +void replay_mutex_lock(void) +{ +} + +void replay_mutex_unlock(void) +{ +} + +void replay_register_char_driver(Chardev *chr) +{ +} + +void replay_chr_be_write(Chardev *s, uint8_t *buf, int len) +{ + abort(); +} + +void replay_char_write_event_save(int res, int offset) +{ + abort(); +} + +void replay_char_write_event_load(int *res, int *offset) +{ + abort(); +} + +int replay_char_read_all_load(uint8_t *buf) +{ + abort(); +} + +void replay_char_read_all_save_error(int res) +{ + abort(); +} + +void replay_char_read_all_save_buf(uint8_t *buf, int offset) +{ + abort(); +} diff --git a/stubs/replay-user.c b/stubs/replay-user.c deleted file mode 100644 index 2ad9e27203..0000000000 --- a/stubs/replay-user.c +++ /dev/null @@ -1,9 +0,0 @@ -#include "qemu/osdep.h" -#include "sysemu/replay.h" -#include "sysemu/sysemu.h" - -void replay_bh_schedule_oneshot_event(AioContext *ctx, - QEMUBHFunc *cb, void *opaque) -{ - aio_bh_schedule_oneshot(ctx, cb, opaque); -} diff --git a/stubs/replay.c b/stubs/replay.c index 45ebe77fb9..9d5b4be339 100644 --- a/stubs/replay.c +++ b/stubs/replay.c @@ -3,83 +3,10 @@ ReplayMode replay_mode; -int64_t replay_save_clock(unsigned int kind, int64_t clock, int64_t raw_icount) -{ - abort(); - return 0; -} - -int64_t replay_read_clock(unsigned int kind) -{ - abort(); - return 0; -} - -bool replay_checkpoint(ReplayCheckpoint checkpoint) -{ - return true; -} - -bool replay_events_enabled(void) -{ - return false; -} - void replay_finish(void) { } -void replay_register_char_driver(Chardev *chr) -{ -} - -void replay_chr_be_write(Chardev *s, uint8_t *buf, int len) -{ - abort(); -} - -void replay_char_write_event_save(int res, int offset) -{ - abort(); -} - -void replay_char_write_event_load(int *res, int *offset) -{ - abort(); -} - -int replay_char_read_all_load(uint8_t *buf) -{ - abort(); -} - -void replay_char_read_all_save_error(int res) -{ - abort(); -} - -void replay_char_read_all_save_buf(uint8_t *buf, int offset) -{ - abort(); -} - -void replay_block_event(QEMUBH *bh, uint64_t id) -{ -} - -uint64_t blkreplay_next_id(void) -{ - return 0; -} - -void replay_mutex_lock(void) -{ -} - -void replay_mutex_unlock(void) -{ -} - void replay_save_random(int ret, void *buf, size_t len) { } @@ -89,11 +16,6 @@ int replay_read_random(void *buf, size_t len) return 0; } -uint64_t replay_get_current_icount(void) -{ - return 0; -} - bool replay_reverse_step(void) { return false; diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 49cd5cabcf..c18a916766 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -3446,6 +3446,11 @@ static inline MemTxAttrs *typecheck_memtxattrs(MemTxAttrs *x) #define arm_tlb_mte_tagged(x) (typecheck_memtxattrs(x)->target_tlb_bit1) /* + * AArch64 usage of the PAGE_TARGET_* bits for linux-user. + */ +#define PAGE_BTI PAGE_TARGET_1 + +/* * Naming convention for isar_feature functions: * Functions which test 32-bit ID registers should have _aa32_ in * their name. Functions which test 64-bit ID registers should have diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 7188808341..072754fa24 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -14507,10 +14507,10 @@ static void disas_data_proc_simd_fp(DisasContext *s, uint32_t insn) */ static bool is_guarded_page(CPUARMState *env, DisasContext *s) { + uint64_t addr = s->base.pc_first; #ifdef CONFIG_USER_ONLY - return false; /* FIXME */ + return page_get_flags(addr) & PAGE_BTI; #else - uint64_t addr = s->base.pc_first; int mmu_idx = arm_to_core_mmu_idx(s->mmu_idx); unsigned int index = tlb_index(env, mmu_idx, addr); CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index e8aa185d4f..2eb41a295a 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -615,7 +615,7 @@ enum { #define FPSCR_VXCVI 8 /* Floating-point invalid operation exception (int) */ #define FPSCR_VE 7 /* Floating-point invalid operation exception enable */ #define FPSCR_OE 6 /* Floating-point overflow exception enable */ -#define FPSCR_UE 5 /* Floating-point undeflow exception enable */ +#define FPSCR_UE 5 /* Floating-point underflow exception enable */ #define FPSCR_ZE 4 /* Floating-point zero divide exception enable */ #define FPSCR_XE 3 /* Floating-point inexact exception enable */ #define FPSCR_NI 2 /* Floating-point non-IEEE mode */ @@ -2331,13 +2331,13 @@ enum { /* Internal hardware exception sources */ PPC_INTERRUPT_DECR, /* Decrementer exception */ PPC_INTERRUPT_HDECR, /* Hypervisor decrementer exception */ - PPC_INTERRUPT_PIT, /* Programmable inteval timer interrupt */ + PPC_INTERRUPT_PIT, /* Programmable interval timer interrupt */ PPC_INTERRUPT_FIT, /* Fixed interval timer interrupt */ PPC_INTERRUPT_WDT, /* Watchdog timer interrupt */ PPC_INTERRUPT_CDOORBELL, /* Critical doorbell interrupt */ PPC_INTERRUPT_DOORBELL, /* Doorbell interrupt */ PPC_INTERRUPT_PERFM, /* Performance monitor interrupt */ - PPC_INTERRUPT_HMI, /* Hypervisor Maintainance interrupt */ + PPC_INTERRUPT_HMI, /* Hypervisor Maintenance interrupt */ PPC_INTERRUPT_HDOORBELL, /* Hypervisor Doorbell interrupt */ PPC_INTERRUPT_HVIRT, /* Hypervisor virtualization interrupt */ }; diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index a988ba15f4..d7411bcc81 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -231,7 +231,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) } /* - * Exception targetting modifiers + * Exception targeting modifiers * * LPES0 is supported on POWER7/8/9 * LPES1 is not supported (old iSeries mode) @@ -1015,7 +1015,7 @@ static void ppc_hw_interrupt(CPUPPCState *env) * This means we will incorrectly execute past the power management * instruction instead of triggering a reset. * - * It generally means a discrepancy between the wakup conditions in the + * It generally means a discrepancy between the wakeup conditions in the * processor has_work implementation and the logic in this function. */ cpu_abort(env_cpu(env), @@ -1191,7 +1191,7 @@ void helper_rfi(CPUPPCState *env) void helper_rfid(CPUPPCState *env) { /* - * The architeture defines a number of rules for which bits can + * The architecture defines a number of rules for which bits can * change but in practice, we handle this in hreg_store_msr() * which will be called by do_rfi(), so there is no need to filter * here diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index ae43b08eb5..9b8c8b70b6 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -1804,7 +1804,7 @@ uint32_t helper_efdcmpeq(CPUPPCState *env, uint64_t op1, uint64_t op2) /* - * VSX_ADD_SUB - VSX floating point add/subract + * VSX_ADD_SUB - VSX floating point add/subtract * name - instruction mnemonic * op - operation (add or sub) * nels - number of elements (1, 2 or 4) diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 15d655b356..b4df127f4a 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -1,5 +1,5 @@ /* - * PowerPC interal definitions for qemu. + * PowerPC internal definitions for qemu. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index d85ba8ffe0..daf690a678 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -487,7 +487,7 @@ int kvm_arch_init_vcpu(CPUState *cs) /* * KVM-HV has transactional memory on POWER8 also without * the KVM_CAP_PPC_HTM extension, so enable it here - * instead as long as it's availble to userspace on the + * instead as long as it's available to userspace on the * host. */ if (qemu_getauxval(AT_HWCAP2) & PPC_FEATURE2_HAS_HTM) { @@ -2683,7 +2683,7 @@ int kvmppc_save_htab(QEMUFile *f, int fd, size_t bufsize, int64_t max_ns) } int kvmppc_load_htab_chunk(QEMUFile *f, int fd, uint32_t index, - uint16_t n_valid, uint16_t n_invalid) + uint16_t n_valid, uint16_t n_invalid, Error **errp) { struct kvm_get_htab_header *buf; size_t chunksize = sizeof(*buf) + n_valid * HASH_PTE_SIZE_64; @@ -2698,14 +2698,13 @@ int kvmppc_load_htab_chunk(QEMUFile *f, int fd, uint32_t index, rc = write(fd, buf, chunksize); if (rc < 0) { - fprintf(stderr, "Error writing KVM hash table: %s\n", - strerror(errno)); - return rc; + error_setg_errno(errp, errno, "Error writing the KVM hash table"); + return -errno; } if (rc != chunksize) { /* We should never get a short write on a single chunk */ - fprintf(stderr, "Short write, restoring KVM hash table\n"); - return -1; + error_setg(errp, "Short write while restoring the KVM hash table"); + return -ENOSPC; } return 0; } diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index 72e05f1cd2..73ce2bc951 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -56,7 +56,7 @@ int kvmppc_define_rtas_kernel_token(uint32_t token, const char *function); int kvmppc_get_htab_fd(bool write, uint64_t index, Error **errp); int kvmppc_save_htab(QEMUFile *f, int fd, size_t bufsize, int64_t max_ns); int kvmppc_load_htab_chunk(QEMUFile *f, int fd, uint32_t index, - uint16_t n_valid, uint16_t n_invalid); + uint16_t n_valid, uint16_t n_invalid, Error **errp); void kvmppc_read_hptes(ppc_hash_pte64_t *hptes, hwaddr ptex, int n); void kvmppc_write_hpte(hwaddr ptex, uint64_t pte0, uint64_t pte1); bool kvmppc_has_cap_fixup_hcalls(void); @@ -316,7 +316,8 @@ static inline int kvmppc_save_htab(QEMUFile *f, int fd, size_t bufsize, } static inline int kvmppc_load_htab_chunk(QEMUFile *f, int fd, uint32_t index, - uint16_t n_valid, uint16_t n_invalid) + uint16_t n_valid, uint16_t n_invalid, + Error **errp) { abort(); } diff --git a/target/ppc/machine.c b/target/ppc/machine.c index 5e58377376..c38e7b1268 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -337,7 +337,7 @@ static int cpu_post_load(void *opaque, int version_id) /* * If we're operating in compat mode, we should be ok as long as - * the destination supports the same compatiblity mode. + * the destination supports the same compatibility mode. * * Otherwise, however, we require that the destination has exactly * the same CPU model as the source. diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index c31d21e6a9..977b2d1561 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -883,7 +883,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, /* * Note on LPCR usage: 970 uses HID4, but our special variant of * store_spr copies relevant fields into env->spr[SPR_LPCR]. - * Similarily we filter unimplemented bits when storing into LPCR + * Similarly we filter unimplemented bits when storing into LPCR * depending on the MMU version. This code can thus just use the * LPCR "as-is". */ diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index 8972714775..50aa18a763 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -179,7 +179,7 @@ static inline int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, } /* Compute access rights */ access = pp_check(ctx->key, pp, ctx->nx); - /* Keep the matching PTE informations */ + /* Keep the matching PTE information */ ctx->raddr = pte1; ctx->prot = access; ret = check_prot(ctx->prot, rw, type); @@ -2176,7 +2176,7 @@ void helper_store_sr(CPUPPCState *env, target_ulong srnum, target_ulong value) env->sr[srnum] = value; /* * Invalidating 256MB of virtual memory in 4kB pages is way - * longer than flusing the whole TLB. + * longer than flushing the whole TLB. */ #if !defined(FLUSH_ALL_TLBS) && 0 { diff --git a/target/ppc/translate_init.c.inc b/target/ppc/translate_init.c.inc index bb66526280..dc68da3cfd 100644 --- a/target/ppc/translate_init.c.inc +++ b/target/ppc/translate_init.c.inc @@ -792,7 +792,7 @@ static void gen_spr_generic(CPUPPCState *env) &spr_read_xer, &spr_write_xer, &spr_read_xer, &spr_write_xer, 0x00000000); - /* Branch contol */ + /* Branch control */ spr_register(env, SPR_LR, "LR", &spr_read_lr, &spr_write_lr, &spr_read_lr, &spr_write_lr, @@ -10328,6 +10328,8 @@ static void ppc_cpu_unrealize(DeviceState *dev) pcc->parent_unrealize(dev); + cpu_remove_sync(CPU(cpu)); + for (i = 0; i < PPC_CPU_OPCODES_LEN; i++) { if (cpu->opcodes[i] == &invalid_handler) { continue; diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index de275782e6..de4705bb57 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -82,9 +82,13 @@ enum { #define VEXT_VERSION_0_07_1 0x00000701 -#define TRANSLATE_PMP_FAIL 2 -#define TRANSLATE_FAIL 1 -#define TRANSLATE_SUCCESS 0 +enum { + TRANSLATE_SUCCESS, + TRANSLATE_FAIL, + TRANSLATE_PMP_FAIL, + TRANSLATE_G_STAGE_FAIL +}; + #define MMU_USER_IDX 3 #define MAX_RISCV_PMPS (16) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 904899054d..4652082df1 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -316,6 +316,9 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) * @physical: This will be set to the calculated physical address * @prot: The returned protection attributes * @addr: The virtual address to be translated + * @fault_pte_addr: If not NULL, this will be set to fault pte address + * when a error occurs on pte address translation. + * This will already be shifted to match htval. * @access_type: The type of MMU access * @mmu_idx: Indicates current privilege level * @first_stage: Are we in first stage translation? @@ -324,6 +327,7 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) */ static int get_physical_address(CPURISCVState *env, hwaddr *physical, int *prot, target_ulong addr, + target_ulong *fault_pte_addr, int access_type, int mmu_idx, bool first_stage, bool two_stage) { @@ -447,11 +451,14 @@ restart: /* Do the second stage translation on the base PTE address. */ int vbase_ret = get_physical_address(env, &vbase, &vbase_prot, - base, MMU_DATA_LOAD, + base, NULL, MMU_DATA_LOAD, mmu_idx, false, true); if (vbase_ret != TRANSLATE_SUCCESS) { - return vbase_ret; + if (fault_pte_addr) { + *fault_pte_addr = (base + idx * ptesize) >> 2; + } + return TRANSLATE_G_STAGE_FAIL; } pte_addr = vbase + idx * ptesize; @@ -632,13 +639,13 @@ hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) int prot; int mmu_idx = cpu_mmu_index(&cpu->env, false); - if (get_physical_address(env, &phys_addr, &prot, addr, 0, mmu_idx, + if (get_physical_address(env, &phys_addr, &prot, addr, NULL, 0, mmu_idx, true, riscv_cpu_virt_enabled(env))) { return -1; } if (riscv_cpu_virt_enabled(env)) { - if (get_physical_address(env, &phys_addr, &prot, phys_addr, + if (get_physical_address(env, &phys_addr, &prot, phys_addr, NULL, 0, mmu_idx, false, true)) { return -1; } @@ -727,19 +734,30 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, if (riscv_cpu_virt_enabled(env) || (riscv_cpu_two_stage_lookup(env) && access_type != MMU_INST_FETCH)) { /* Two stage lookup */ - ret = get_physical_address(env, &pa, &prot, address, access_type, + ret = get_physical_address(env, &pa, &prot, address, + &env->guest_phys_fault_addr, access_type, mmu_idx, true, true); + /* + * A G-stage exception may be triggered during two state lookup. + * And the env->guest_phys_fault_addr has already been set in + * get_physical_address(). + */ + if (ret == TRANSLATE_G_STAGE_FAIL) { + first_stage_error = false; + access_type = MMU_DATA_LOAD; + } + qemu_log_mask(CPU_LOG_MMU, "%s 1st-stage address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx " prot %d\n", __func__, address, ret, pa, prot); - if (ret != TRANSLATE_FAIL) { + if (ret == TRANSLATE_SUCCESS) { /* Second stage lookup */ im_address = pa; - ret = get_physical_address(env, &pa, &prot2, im_address, + ret = get_physical_address(env, &pa, &prot2, im_address, NULL, access_type, mmu_idx, false, true); qemu_log_mask(CPU_LOG_MMU, @@ -768,8 +786,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } } else { /* Single stage lookup */ - ret = get_physical_address(env, &pa, &prot, address, access_type, - mmu_idx, true, false); + ret = get_physical_address(env, &pa, &prot, address, NULL, + access_type, mmu_idx, true, false); qemu_log_mask(CPU_LOG_MMU, "%s address=%" VADDR_PRIx " ret %d physical " @@ -852,6 +870,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) bool async = !!(cs->exception_index & RISCV_EXCP_INT_FLAG); target_ulong cause = cs->exception_index & RISCV_EXCP_INT_MASK; target_ulong deleg = async ? env->mideleg : env->medeleg; + bool write_tval = false; target_ulong tval = 0; target_ulong htval = 0; target_ulong mtval2 = 0; @@ -873,6 +892,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) case RISCV_EXCP_INST_PAGE_FAULT: case RISCV_EXCP_LOAD_PAGE_FAULT: case RISCV_EXCP_STORE_PAGE_FAULT: + write_tval = true; tval = env->badaddr; break; default: @@ -895,7 +915,13 @@ void riscv_cpu_do_interrupt(CPUState *cs) } trace_riscv_trap(env->mhartid, async, cause, env->pc, tval, - riscv_cpu_get_trap_name(cause, async)); + riscv_cpu_get_trap_name(cause, async)); + + qemu_log_mask(CPU_LOG_INT, + "%s: hart:"TARGET_FMT_ld", async:%d, cause:"TARGET_FMT_lx", " + "epc:0x"TARGET_FMT_lx", tval:0x"TARGET_FMT_lx", desc=%s\n", + __func__, env->mhartid, async, cause, env->pc, tval, + riscv_cpu_get_trap_name(cause, async)); if (env->priv <= PRV_S && cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) { @@ -904,7 +930,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) target_ulong hdeleg = async ? env->hideleg : env->hedeleg; if ((riscv_cpu_virt_enabled(env) || - riscv_cpu_two_stage_lookup(env)) && tval) { + riscv_cpu_two_stage_lookup(env)) && write_tval) { /* * If we are writing a guest virtual address to stval, set * this to 1. If we are trapping to VS we will set this to 0 @@ -932,7 +958,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) /* Trap into HS mode, from virt */ riscv_cpu_swap_hypervisor_regs(env); env->hstatus = set_field(env->hstatus, HSTATUS_SPVP, - get_field(env->mstatus, SSTATUS_SPP)); + env->priv); env->hstatus = set_field(env->hstatus, HSTATUS_SPV, riscv_cpu_virt_enabled(env)); diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 9b9ada45a9..4ce73575a7 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -29,7 +29,6 @@ void QEMU_NORETURN riscv_raise_exception(CPURISCVState *env, uint32_t exception, uintptr_t pc) { CPUState *cs = env_cpu(env); - qemu_log_mask(CPU_LOG_INT, "%s: %d\n", __func__, exception); cs->exception_index = exception; cpu_loop_exit_restore(cs, pc); } @@ -334,12 +333,12 @@ target_ulong helper_hyp_x_load(CPURISCVState *env, target_ulong address, riscv_cpu_set_two_stage_lookup(env, true); switch (memop) { - case MO_TEUL: - pte = cpu_ldub_data_ra(env, address, GETPC()); - break; case MO_TEUW: pte = cpu_lduw_data_ra(env, address, GETPC()); break; + case MO_TEUL: + pte = cpu_ldl_data_ra(env, address, GETPC()); + break; default: g_assert_not_reached(); } diff --git a/target/rx/insns.decode b/target/rx/insns.decode index 232a61fc8e..ca9334b37a 100644 --- a/target/rx/insns.decode +++ b/target/rx/insns.decode @@ -7,7 +7,7 @@ # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either -# version 2 of the License, or (at your option) any later version. +# version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/target/rx/op_helper.c b/target/rx/op_helper.c index f89d294f2b..59389f4992 100644 --- a/target/rx/op_helper.c +++ b/target/rx/op_helper.c @@ -318,7 +318,7 @@ void helper_swhile(CPURXState *env, uint32_t sz) env->psw_c = (tmp <= env->regs[2]); } -/* accumlator operations */ +/* accumulator operations */ void helper_rmpa(CPURXState *env, uint32_t sz) { uint64_t result_l, prev; diff --git a/target/rx/translate.c b/target/rx/translate.c index 482278edd2..9ea941c630 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -1089,7 +1089,7 @@ static void rx_sub(TCGv ret, TCGv arg1, TCGv arg2) tcg_gen_xor_i32(temp, arg1, arg2); tcg_gen_and_i32(cpu_psw_o, cpu_psw_o, temp); tcg_temp_free_i32(temp); - /* CMP not requred return */ + /* CMP not required return */ if (ret) { tcg_gen_mov_i32(ret, cpu_psw_s); } diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c index 31ea8df246..42fe0bf4ca 100644 --- a/target/s390x/cpu_features.c +++ b/target/s390x/cpu_features.c @@ -14,6 +14,7 @@ #include "qemu/osdep.h" #include "qemu/module.h" #include "cpu_features.h" +#include "hw/s390x/pv.h" #define DEF_FEAT(_FEAT, _NAME, _TYPE, _BIT, _DESC) \ [S390_FEAT_##_FEAT] = { \ @@ -105,6 +106,10 @@ void s390_fill_feat_block(const S390FeatBitmap features, S390FeatType type, } feat = find_next_bit(features, S390_FEAT_MAX, feat + 1); } + + if (type == S390_FEAT_TYPE_SCLP_FAC134 && s390_is_pv()) { + clear_be_bit(s390_feat_def(S390_FEAT_DIAG_318)->bit, data); + } } void s390_add_from_feat_block(S390FeatBitmap features, S390FeatType type, diff --git a/target/s390x/cpu_features.h b/target/s390x/cpu_features.h index ef52ffce83..87463f064d 100644 --- a/target/s390x/cpu_features.h +++ b/target/s390x/cpu_features.h @@ -81,6 +81,10 @@ const S390FeatGroupDef *s390_feat_group_def(S390FeatGroup group); #define BE_BIT_NR(BIT) (BIT ^ (BITS_PER_LONG - 1)) +static inline void clear_be_bit(unsigned int bit_nr, uint8_t *array) +{ + array[bit_nr / 8] &= ~(0x80 >> (bit_nr % 8)); +} static inline void set_be_bit(unsigned int bit_nr, uint8_t *array) { array[bit_nr / 8] |= 0x80 >> (bit_nr % 8); diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index ca484bfda7..461e0b8f4a 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -29,6 +29,7 @@ #include "hw/pci/pci.h" #endif #include "qapi/qapi-commands-machine-target.h" +#include "hw/s390x/pv.h" #define CPUDEF_INIT(_type, _gen, _ec_ga, _mha_pow, _hmfai, _name, _desc) \ { \ @@ -238,6 +239,9 @@ bool s390_has_feat(S390Feat feat) } return 0; } + if (feat == S390_FEAT_DIAG_318 && s390_is_pv()) { + return false; + } return test_bit(feat, cpu->model->features); } diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c index f13eff688c..baa070fdf7 100644 --- a/target/s390x/kvm.c +++ b/target/s390x/kvm.c @@ -2498,8 +2498,7 @@ void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp) */ set_bit(S390_FEAT_EXTENDED_LENGTH_SCCB, model->features); - /* DIAGNOSE 0x318 is not supported under protected virtualization */ - if (!s390_is_pv() && kvm_check_extension(kvm_state, KVM_CAP_S390_DIAG318)) { + if (kvm_check_extension(kvm_state, KVM_CAP_S390_DIAG318)) { set_bit(S390_FEAT_DIAG_318, model->features); } diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h index dbe58c7888..714e3b5641 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -160,7 +160,7 @@ typedef struct CPUSH4State { uint32_t pteh; /* page table entry high register */ uint32_t ptel; /* page table entry low register */ uint32_t ptea; /* page table entry assistance register */ - uint32_t ttb; /* tranlation table base register */ + uint32_t ttb; /* translation table base register */ uint32_t tea; /* TLB exception address register */ uint32_t tra; /* TRAPA exception register */ uint32_t expevt; /* exception event register */ diff --git a/target/sh4/op_helper.c b/target/sh4/op_helper.c index 14c3db0f48..c0cbb95382 100644 --- a/target/sh4/op_helper.c +++ b/target/sh4/op_helper.c @@ -398,9 +398,11 @@ float32 helper_fsrra_FT(CPUSH4State *env, float32 t0) /* "Approximate" 1/sqrt(x) via actual computation. */ t0 = float32_sqrt(t0, &env->fp_status); t0 = float32_div(float32_one, t0, &env->fp_status); - /* Since this is supposed to be an approximation, an imprecision - exception is required. One supposes this also follows the usual - IEEE rule that other exceptions take precidence. */ + /* + * Since this is supposed to be an approximation, an imprecision + * exception is required. One supposes this also follows the usual + * IEEE rule that other exceptions take precedence. + */ if (get_float_exception_flags(&env->fp_status) == 0) { set_float_exception_flags(float_flag_inexact, &env->fp_status); } diff --git a/target/sh4/translate.c b/target/sh4/translate.c index 60c863d9e1..9312790623 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -1959,9 +1959,11 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) NEXT_INSN; switch (ctx->opcode & 0xf00f) { case 0x6003: /* mov Rm,Rn */ - /* Here we want to recognize ld_dst being saved for later consumtion, - or for another input register being copied so that ld_dst need not - be clobbered during the operation. */ + /* + * Here we want to recognize ld_dst being saved for later consumption, + * or for another input register being copied so that ld_dst need not + * be clobbered during the operation. + */ op_dst = B11_8; mv_src = B7_4; if (op_dst == ld_dst) { diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 6a033e778c..88a32268a1 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -93,6 +93,7 @@ static void xtensa_cpu_reset(DeviceState *dev) !xtensa_abi_call0()) { env->sregs[PS] |= PS_WOE; } + env->sregs[CPENABLE] = 0xff; #endif env->sregs[VECBASE] = env->config->vecbase; env->sregs[IBREAKENABLE] = 0; diff --git a/tests/acceptance/boot_linux.py b/tests/acceptance/boot_linux.py index 0055dc7cee..c743e231f4 100644 --- a/tests/acceptance/boot_linux.py +++ b/tests/acceptance/boot_linux.py @@ -182,10 +182,11 @@ class BootLinuxAarch64(BootLinux): self.add_common_args() self.launch_and_wait() - def test_virt_kvm(self): + def test_virt_kvm_gicv2(self): """ :avocado: tags=accel:kvm :avocado: tags=cpu:host + :avocado: tags=device:gicv2 """ if not kvm_available(self.arch, self.qemu_bin): self.cancel(KVM_NOT_AVAILABLE) @@ -195,6 +196,20 @@ class BootLinuxAarch64(BootLinux): self.add_common_args() self.launch_and_wait() + def test_virt_kvm_gicv3(self): + """ + :avocado: tags=accel:kvm + :avocado: tags=cpu:host + :avocado: tags=device:gicv3 + """ + if not kvm_available(self.arch, self.qemu_bin): + self.cancel(KVM_NOT_AVAILABLE) + self.vm.add_args("-accel", "kvm") + self.vm.add_args("-cpu", "host") + self.vm.add_args("-machine", "virt,gic-version=3") + self.add_common_args() + self.launch_and_wait() + class BootLinuxPPC64(BootLinux): """ diff --git a/tests/acceptance/boot_linux_console.py b/tests/acceptance/boot_linux_console.py index 0118ed5915..8f433a67f8 100644 --- a/tests/acceptance/boot_linux_console.py +++ b/tests/acceptance/boot_linux_console.py @@ -780,6 +780,8 @@ class BootLinuxConsole(LinuxKernelTest): # Wait for VM to shut down gracefully self.vm.wait() + @skipUnless(os.getenv('ARMBIAN_ARTIFACTS_CACHED'), + 'Test artifacts fetched from unreliable dl.armbian.com') @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') @skipUnless(P7ZIP_AVAILABLE, '7z not installed') def test_arm_orangepi_bionic(self): @@ -889,6 +891,28 @@ class BootLinuxConsole(LinuxKernelTest): # Wait for user-space wait_for_console_pattern(self, 'Starting root file system check') + def test_aarch64_raspi3_atf(self): + """ + :avocado: tags=arch:aarch64 + :avocado: tags=machine:raspi3 + :avocado: tags=cpu:cortex-a53 + :avocado: tags=device:pl011 + :avocado: tags=atf + """ + zip_url = ('https://github.com/pbatard/RPi3/releases/download/' + 'v1.15/RPi3_UEFI_Firmware_v1.15.zip') + zip_hash = '74b3bd0de92683cadb14e008a7575e1d0c3cafb9' + zip_path = self.fetch_asset(zip_url, asset_hash=zip_hash) + + archive.extract(zip_path, self.workdir) + efi_fd = os.path.join(self.workdir, 'RPI_EFI.fd') + + self.vm.set_console(console_index=1) + self.vm.add_args('-nodefaults', + '-device', 'loader,file=%s,force-raw=true' % efi_fd) + self.vm.launch() + self.wait_for_console_pattern('version UEFI Firmware v1.15') + def test_s390x_s390_ccw_virtio(self): """ :avocado: tags=arch:s390x diff --git a/tests/acceptance/machine_m68k_nextcube.py b/tests/acceptance/machine_m68k_nextcube.py index 32cf571f94..2baba5fdc2 100644 --- a/tests/acceptance/machine_m68k_nextcube.py +++ b/tests/acceptance/machine_m68k_nextcube.py @@ -9,7 +9,6 @@ import os import re import time import logging -import distutils.spawn from avocado_qemu import Test from avocado import skipUnless @@ -70,7 +69,7 @@ class NextCubeMachine(Test): @skipUnless(PIL_AVAILABLE, 'Python PIL not installed') def test_bootrom_framebuffer_size(self): - screenshot_path = os.path.join(self.workdir, "dump.png") + screenshot_path = os.path.join(self.workdir, "dump.ppm") self.check_bootrom_framebuffer(screenshot_path) width, height = Image.open(screenshot_path).size @@ -79,7 +78,7 @@ class NextCubeMachine(Test): @skipUnless(tesseract_available(3), 'tesseract v3 OCR tool not available') def test_bootrom_framebuffer_ocr_with_tesseract_v3(self): - screenshot_path = os.path.join(self.workdir, "dump.png") + screenshot_path = os.path.join(self.workdir, "dump.ppm") self.check_bootrom_framebuffer(screenshot_path) console_logger = logging.getLogger('console') @@ -95,7 +94,7 @@ class NextCubeMachine(Test): # that it is still alpha-level software. @skipUnless(tesseract_available(4), 'tesseract v4 OCR tool not available') def test_bootrom_framebuffer_ocr_with_tesseract_v4(self): - screenshot_path = os.path.join(self.workdir, "dump.png") + screenshot_path = os.path.join(self.workdir, "dump.ppm") self.check_bootrom_framebuffer(screenshot_path) console_logger = logging.getLogger('console') diff --git a/tests/acceptance/ppc_prep_40p.py b/tests/acceptance/ppc_prep_40p.py index 1515561249..96ba13b894 100644 --- a/tests/acceptance/ppc_prep_40p.py +++ b/tests/acceptance/ppc_prep_40p.py @@ -22,7 +22,6 @@ class IbmPrep40pMachine(Test): # All rights reserved. # U.S. Government Users Restricted Rights - Use, duplication or disclosure # restricted by GSA ADP Schedule Contract with IBM Corp. - @skipIf(os.getenv('CONTINUOUS_INTEGRATION'), 'Running on Travis-CI') @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') def test_factory_firmware_and_netbsd(self): """ @@ -35,7 +34,7 @@ class IbmPrep40pMachine(Test): '7020-40p/P12H0456.IMG') bios_hash = '1775face4e6dc27f3a6ed955ef6eb331bf817f03' bios_path = self.fetch_asset(bios_url, asset_hash=bios_hash) - drive_url = ('https://cdn.netbsd.org/pub/NetBSD/NetBSD-archive/' + drive_url = ('https://archive.netbsd.org/pub/NetBSD-archive/' 'NetBSD-4.0/prep/installation/floppy/generic_com0.fs') drive_hash = 'dbcfc09912e71bd5f0d82c7c1ee43082fb596ceb' drive_path = self.fetch_asset(drive_url, asset_hash=drive_hash) @@ -61,7 +60,6 @@ class IbmPrep40pMachine(Test): wait_for_console_pattern(self, '>> Memory: 192M') wait_for_console_pattern(self, '>> CPU type PowerPC,604') - @skipIf(os.getenv('CONTINUOUS_INTEGRATION'), 'Running on Travis-CI') def test_openbios_and_netbsd(self): """ :avocado: tags=arch:ppc diff --git a/tests/acceptance/reverse_debugging.py b/tests/acceptance/reverse_debugging.py index b72fdf6cdc..be01aca217 100644 --- a/tests/acceptance/reverse_debugging.py +++ b/tests/acceptance/reverse_debugging.py @@ -14,6 +14,7 @@ from avocado import skipIf from avocado_qemu import BUILD_DIR from avocado.utils import gdb from avocado.utils import process +from avocado.utils.network.ports import find_free_port from avocado.utils.path import find_command from boot_linux_console import LinuxKernelTest @@ -33,7 +34,7 @@ class ReverseDebugging(LinuxKernelTest): STEPS = 10 endian_is_le = True - def run_vm(self, record, shift, args, replay_path, image_path): + def run_vm(self, record, shift, args, replay_path, image_path, port): logger = logging.getLogger('replay') vm = self.get_vm() vm.set_console() @@ -43,7 +44,7 @@ class ReverseDebugging(LinuxKernelTest): else: logger.info('replaying the execution...') mode = 'replay' - vm.add_args('-s', '-S') + vm.add_args('-gdb', 'tcp::%d' % port, '-S') vm.add_args('-icount', 'shift=%s,rr=%s,rrfile=%s,rrsnapshot=init' % (shift, mode, replay_path), '-net', 'none') @@ -109,9 +110,10 @@ class ReverseDebugging(LinuxKernelTest): process.run(cmd) replay_path = os.path.join(self.workdir, 'replay.bin') + port = find_free_port() # record the log - vm = self.run_vm(True, shift, args, replay_path, image_path) + vm = self.run_vm(True, shift, args, replay_path, image_path, port) while self.vm_get_icount(vm) <= self.STEPS: pass last_icount = self.vm_get_icount(vm) @@ -120,9 +122,9 @@ class ReverseDebugging(LinuxKernelTest): logger.info("recorded log with %s+ steps" % last_icount) # replay and run debug commands - vm = self.run_vm(False, shift, args, replay_path, image_path) + vm = self.run_vm(False, shift, args, replay_path, image_path, port) logger.info('connecting to gdbstub') - g = gdb.GDBRemote('127.0.0.1', 1234, False, False) + g = gdb.GDBRemote('127.0.0.1', port, False, False) g.connect() r = g.cmd(b'qSupported') if b'qXfer:features:read+' in r: diff --git a/tests/docker/dockerfiles/centos7.docker b/tests/docker/dockerfiles/centos7.docker index 8b273725ee..6f11af1989 100644 --- a/tests/docker/dockerfiles/centos7.docker +++ b/tests/docker/dockerfiles/centos7.docker @@ -31,7 +31,7 @@ ENV PACKAGES \ perl-Test-Harness \ pixman-devel \ python3 \ - SDL-devel \ + SDL2-devel \ spice-glib-devel \ spice-server-devel \ tar \ diff --git a/tests/docker/dockerfiles/centos8.docker b/tests/docker/dockerfiles/centos8.docker index 585dfad9be..54bc6d54cd 100644 --- a/tests/docker/dockerfiles/centos8.docker +++ b/tests/docker/dockerfiles/centos8.docker @@ -2,7 +2,7 @@ FROM centos:8.1.1911 RUN dnf -y update ENV PACKAGES \ - SDL-devel \ + SDL2-devel \ bzip2 \ bzip2-devel \ dbus-daemon \ @@ -18,6 +18,7 @@ ENV PACKAGES \ lzo-devel \ make \ mesa-libEGL-devel \ + nmap-ncat \ nettle-devel \ ninja-build \ perl-Test-Harness \ diff --git a/tests/docker/dockerfiles/debian-amd64.docker b/tests/docker/dockerfiles/debian-amd64.docker index 314c6bae83..55075d9fce 100644 --- a/tests/docker/dockerfiles/debian-amd64.docker +++ b/tests/docker/dockerfiles/debian-amd64.docker @@ -23,6 +23,9 @@ RUN apt update && \ libsnappy-dev \ libvte-dev \ netcat-openbsd \ + openssh-client \ + python3-numpy \ + python3-opencv \ python3-venv # virgl diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index ac79d95418..0b5053f2d0 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -73,6 +73,7 @@ ENV PACKAGES \ mingw64-pixman \ mingw64-pkg-config \ mingw64-SDL2 \ + nmap-ncat \ ncurses-devel \ nettle-devel \ ninja-build \ diff --git a/tests/docker/dockerfiles/ubuntu2004.docker b/tests/docker/dockerfiles/ubuntu2004.docker index 17b37cda38..355bbb3c63 100644 --- a/tests/docker/dockerfiles/ubuntu2004.docker +++ b/tests/docker/dockerfiles/ubuntu2004.docker @@ -47,6 +47,7 @@ ENV PACKAGES flex bison \ libxen-dev \ libzstd-dev \ make \ + netcat-openbsd \ ninja-build \ python3-numpy \ python3-opencv \ diff --git a/tests/fp/meson.build b/tests/fp/meson.build index 24739ad421..3d4fb00f9d 100644 --- a/tests/fp/meson.build +++ b/tests/fp/meson.build @@ -603,7 +603,7 @@ fptest_rounding_args = ['-r', 'all'] # FIXME: i32_to_extF80 (broken), i64_to_extF80 (broken) # extF80_roundToInt (broken) foreach k, v : softfloat_conv_tests - test('fp-test:' + k, fptest, + test('fp-test-' + k, fptest, args: fptest_args + fptest_rounding_args + v.split(), suite: ['softfloat', 'softfloat-conv']) endforeach @@ -612,13 +612,13 @@ endforeach # extF80_{mulAdd} (missing) foreach k, v : softfloat_tests extF80_broken = ['lt_quiet', 'rem'].contains(k) - test('fp-test:' + k, fptest, + test('fp-test-' + k, fptest, args: fptest_args + fptest_rounding_args + ['f16_' + k, 'f32_' + k, 'f64_' + k, 'f128_' + k] + (extF80_broken ? [] : ['extF80_' + k]), suite: ['softfloat', 'softfloat-' + v]) endforeach -test('fp-test:mulAdd', fptest, +test('fp-test-mulAdd', fptest, # no fptest_rounding_args args: fptest_args + ['f16_mulAdd', 'f32_mulAdd', 'f64_mulAdd', 'f128_mulAdd'], diff --git a/tests/ptimer-test-stubs.c b/tests/ptimer-test-stubs.c index e935a1395e..7f801a4d09 100644 --- a/tests/ptimer-test-stubs.c +++ b/tests/ptimer-test-stubs.c @@ -122,8 +122,3 @@ void qemu_bh_delete(QEMUBH *bh) { g_free(bh); } - -void replay_bh_schedule_event(QEMUBH *bh) -{ - bh->cb(bh->opaque); -} diff --git a/tests/qemu-iotests/274 b/tests/qemu-iotests/274 index d4571c5465..76b1ba6a52 100755 --- a/tests/qemu-iotests/274 +++ b/tests/qemu-iotests/274 @@ -115,6 +115,26 @@ with iotests.FilePath('base') as base, \ iotests.qemu_io_log('-c', 'read -P 1 0 %d' % size_short, mid) iotests.qemu_io_log('-c', 'read -P 0 %d %d' % (size_short, size_diff), mid) + iotests.log('=== Testing qemu-img commit (top -> base) ===') + + create_chain() + iotests.qemu_img_log('commit', '-b', base, top) + iotests.img_info_log(base) + iotests.qemu_io_log('-c', 'read -P 1 0 %d' % size_short, base) + iotests.qemu_io_log('-c', 'read -P 0 %d %d' % (size_short, size_diff), base) + + iotests.log('=== Testing QMP active commit (top -> base) ===') + + create_chain() + with create_vm() as vm: + vm.launch() + vm.qmp_log('block-commit', device='top', base_node='base', + job_id='job0', auto_dismiss=False) + vm.run_job('job0', wait=5) + + iotests.img_info_log(mid) + iotests.qemu_io_log('-c', 'read -P 1 0 %d' % size_short, base) + iotests.qemu_io_log('-c', 'read -P 0 %d %d' % (size_short, size_diff), base) iotests.log('== Resize tests ==') diff --git a/tests/qemu-iotests/274.out b/tests/qemu-iotests/274.out index bf5abd4c10..cfe17a8659 100644 --- a/tests/qemu-iotests/274.out +++ b/tests/qemu-iotests/274.out @@ -135,6 +135,74 @@ read 1048576/1048576 bytes at offset 0 read 1048576/1048576 bytes at offset 1048576 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +=== Testing qemu-img commit (top -> base) === +Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 lazy_refcounts=off refcount_bits=16 + +Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 + +Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 + +wrote 2097152/2097152 bytes at offset 0 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +Image committed. + +image: TEST_IMG +file format: IMGFMT +virtual size: 2 MiB (2097152 bytes) +cluster_size: 65536 +Format specific information: + compat: 1.1 + compression type: zlib + lazy refcounts: false + refcount bits: 16 + corrupt: false + extended l2: false + +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +read 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Testing QMP active commit (top -> base) === +Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 lazy_refcounts=off refcount_bits=16 + +Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 + +Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 + +wrote 2097152/2097152 bytes at offset 0 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +{"execute": "block-commit", "arguments": {"auto-dismiss": false, "base-node": "base", "device": "top", "job-id": "job0"}} +{"return": {}} +{"execute": "job-complete", "arguments": {"id": "job0"}} +{"return": {}} +{"data": {"device": "job0", "len": 1048576, "offset": 1048576, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"device": "job0", "len": 1048576, "offset": 1048576, "speed": 0, "type": "commit"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} +image: TEST_IMG +file format: IMGFMT +virtual size: 1 MiB (1048576 bytes) +cluster_size: 65536 +backing file: TEST_DIR/PID-base +backing file format: IMGFMT +Format specific information: + compat: 1.1 + compression type: zlib + lazy refcounts: false + refcount bits: 16 + corrupt: false + extended l2: false + +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +read 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + == Resize tests == === preallocation=off === Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=6442450944 lazy_refcounts=off refcount_bits=16 diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 3830a40d10..f23a5335a8 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -127,6 +127,9 @@ static void free_test_data(test_data *data) { int i; + if (!data->tables) { + return; + } for (i = 0; i < data->tables->len; ++i) { cleanup_table_descriptor(&g_array_index(data->tables, AcpiSdtTable, i)); } @@ -656,6 +659,13 @@ static void test_acpi_one(const char *params, test_data *data) char *args; bool use_uefi = data->uefi_fl1 && data->uefi_fl2; +#ifndef CONFIG_TCG + if (data->tcg_only) { + g_test_skip("TCG disabled, skipping ACPI tcg_only test"); + return; + } +#endif /* CONFIG_TCG */ + if (use_uefi) { /* * TODO: convert '-drive if=pflash' to new syntax (see e33763be7cd3) diff --git a/tests/qtest/fuzz/fuzz.c b/tests/qtest/fuzz/fuzz.c index eb0070437f..7be7226bc0 100644 --- a/tests/qtest/fuzz/fuzz.c +++ b/tests/qtest/fuzz/fuzz.c @@ -118,6 +118,19 @@ static FuzzTarget *fuzz_get_target(char* name) } +/* Sometimes called by libfuzzer to mutate two inputs into one */ +size_t LLVMFuzzerCustomCrossOver(const uint8_t *data1, size_t size1, + const uint8_t *data2, size_t size2, + uint8_t *out, size_t max_out_size, + unsigned int seed) +{ + if (fuzz_target->crossover) { + return fuzz_target->crossover(data1, size1, data2, size2, out, + max_out_size, seed); + } + return 0; +} + /* Executed for each fuzzing-input */ int LLVMFuzzerTestOneInput(const unsigned char *Data, size_t Size) { diff --git a/tests/qtest/fuzz/fuzz.h b/tests/qtest/fuzz/fuzz.h index 8eb765edc8..08e9560a79 100644 --- a/tests/qtest/fuzz/fuzz.h +++ b/tests/qtest/fuzz/fuzz.h @@ -77,6 +77,30 @@ typedef struct FuzzTarget { */ void(*fuzz)(QTestState *, const unsigned char *, size_t); + /* + * The fuzzer can specify a "Custom Crossover" function for combining two + * inputs from the corpus. This function is sometimes called by libfuzzer + * when mutating inputs. + * + * data1: location of first input + * size1: length of first input + * data1: location of second input + * size1: length of second input + * out: where to place the resulting, mutated input + * max_out_size: the maximum length of the input that can be placed in out + * seed: the seed that should be used to make mutations deterministic, when + * needed + * + * See libfuzzer's LLVMFuzzerCustomCrossOver API for more info. + * + * Can be NULL + */ + size_t(*crossover)(const uint8_t *data1, size_t size1, + const uint8_t *data2, size_t size2, + uint8_t *out, size_t max_out_size, + unsigned int seed); + + void *opaque; } FuzzTarget; void flush_events(QTestState *); @@ -91,6 +115,10 @@ void fuzz_qtest_set_serialize(bool option); */ void fuzz_add_target(const FuzzTarget *target); +size_t LLVMFuzzerCustomCrossOver(const uint8_t *data1, size_t size1, + const uint8_t *data2, size_t size2, + uint8_t *out, size_t max_out_size, + unsigned int seed); int LLVMFuzzerTestOneInput(const unsigned char *Data, size_t Size); int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp); diff --git a/tests/qtest/fuzz/generic_fuzz.c b/tests/qtest/fuzz/generic_fuzz.c new file mode 100644 index 0000000000..a8f5864883 --- /dev/null +++ b/tests/qtest/fuzz/generic_fuzz.c @@ -0,0 +1,954 @@ +/* + * Generic Virtual-Device Fuzzing Target + * + * Copyright Red Hat Inc., 2020 + * + * Authors: + * Alexander Bulekov <alxndr@bu.edu> + * + * 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 <wordexp.h> + +#include "hw/core/cpu.h" +#include "tests/qtest/libqos/libqtest.h" +#include "fuzz.h" +#include "fork_fuzz.h" +#include "exec/address-spaces.h" +#include "string.h" +#include "exec/memory.h" +#include "exec/ramblock.h" +#include "exec/address-spaces.h" +#include "hw/qdev-core.h" +#include "hw/pci/pci.h" +#include "hw/boards.h" +#include "generic_fuzz_configs.h" + +/* + * SEPARATOR is used to separate "operations" in the fuzz input + */ +#define SEPARATOR "FUZZ" + +enum cmds { + OP_IN, + OP_OUT, + OP_READ, + OP_WRITE, + OP_PCI_READ, + OP_PCI_WRITE, + OP_DISABLE_PCI, + OP_ADD_DMA_PATTERN, + OP_CLEAR_DMA_PATTERNS, + OP_CLOCK_STEP, +}; + +#define DEFAULT_TIMEOUT_US 100000 +#define USEC_IN_SEC 1000000000 + +#define MAX_DMA_FILL_SIZE 0x10000 + +#define PCI_HOST_BRIDGE_CFG 0xcf8 +#define PCI_HOST_BRIDGE_DATA 0xcfc + +typedef struct { + ram_addr_t addr; + ram_addr_t size; /* The number of bytes until the end of the I/O region */ +} address_range; + +static useconds_t timeout = DEFAULT_TIMEOUT_US; + +static bool qtest_log_enabled; + +/* + * A pattern used to populate a DMA region or perform a memwrite. This is + * useful for e.g. populating tables of unique addresses. + * Example {.index = 1; .stride = 2; .len = 3; .data = "\x00\x01\x02"} + * Renders as: 00 01 02 00 03 02 00 05 02 00 07 02 ... + */ +typedef struct { + uint8_t index; /* Index of a byte to increment by stride */ + uint8_t stride; /* Increment each index'th byte by this amount */ + size_t len; + const uint8_t *data; +} pattern; + +/* Avoid filling the same DMA region between MMIO/PIO commands ? */ +static bool avoid_double_fetches; + +static QTestState *qts_global; /* Need a global for the DMA callback */ + +/* + * List of memory regions that are children of QOM objects specified by the + * user for fuzzing. + */ +static GHashTable *fuzzable_memoryregions; +static GPtrArray *fuzzable_pci_devices; + +struct get_io_cb_info { + int index; + int found; + address_range result; +}; + +static int get_io_address_cb(Int128 start, Int128 size, + const MemoryRegion *mr, void *opaque) { + struct get_io_cb_info *info = opaque; + if (g_hash_table_lookup(fuzzable_memoryregions, mr)) { + if (info->index == 0) { + info->result.addr = (ram_addr_t)start; + info->result.size = (ram_addr_t)size; + info->found = 1; + return 1; + } + info->index--; + } + return 0; +} + +/* + * List of dma regions populated since the last fuzzing command. Used to ensure + * that we only write to each DMA address once, to avoid race conditions when + * building reproducers. + */ +static GArray *dma_regions; + +static GArray *dma_patterns; +static int dma_pattern_index; +static bool pci_disabled; + +/* + * Allocate a block of memory and populate it with a pattern. + */ +static void *pattern_alloc(pattern p, size_t len) +{ + int i; + uint8_t *buf = g_malloc(len); + uint8_t sum = 0; + + for (i = 0; i < len; ++i) { + buf[i] = p.data[i % p.len]; + if ((i % p.len) == p.index) { + buf[i] += sum; + sum += p.stride; + } + } + return buf; +} + +static int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr) +{ + unsigned access_size_max = mr->ops->valid.max_access_size; + + /* + * Regions are assumed to support 1-4 byte accesses unless + * otherwise specified. + */ + if (access_size_max == 0) { + access_size_max = 4; + } + + /* Bound the maximum access by the alignment of the address. */ + if (!mr->ops->impl.unaligned) { + unsigned align_size_max = addr & -addr; + if (align_size_max != 0 && align_size_max < access_size_max) { + access_size_max = align_size_max; + } + } + + /* Don't attempt accesses larger than the maximum. */ + if (l > access_size_max) { + l = access_size_max; + } + l = pow2floor(l); + + return l; +} + +/* + * Call-back for functions that perform DMA reads from guest memory. Confirm + * that the region has not already been populated since the last loop in + * generic_fuzz(), avoiding potential race-conditions, which we don't have + * a good way for reproducing right now. + */ +void fuzz_dma_read_cb(size_t addr, size_t len, MemoryRegion *mr, bool is_write) +{ + /* Are we in the generic-fuzzer or are we using another fuzz-target? */ + if (!qts_global) { + return; + } + + /* + * Return immediately if: + * - We have no DMA patterns defined + * - The length of the DMA read request is zero + * - The DMA read is hitting an MR other than the machine's main RAM + * - The DMA request is not a read (what happens for a address_space_map + * with is_write=True? Can the device use the same pointer to do reads?) + * - The DMA request hits past the bounds of our RAM + */ + if (dma_patterns->len == 0 + || len == 0 + /* || mr != MACHINE(qdev_get_machine())->ram */ + || is_write + || addr > current_machine->ram_size) { + return; + } + + /* + * If we overlap with any existing dma_regions, split the range and only + * populate the non-overlapping parts. + */ + address_range region; + bool double_fetch = false; + for (int i = 0; + i < dma_regions->len && (avoid_double_fetches || qtest_log_enabled); + ++i) { + region = g_array_index(dma_regions, address_range, i); + if (addr < region.addr + region.size && addr + len > region.addr) { + double_fetch = true; + if (addr < region.addr + && avoid_double_fetches) { + fuzz_dma_read_cb(addr, region.addr - addr, mr, is_write); + } + if (addr + len > region.addr + region.size + && avoid_double_fetches) { + fuzz_dma_read_cb(region.addr + region.size, + addr + len - (region.addr + region.size), mr, is_write); + } + return; + } + } + + /* Cap the length of the DMA access to something reasonable */ + len = MIN(len, MAX_DMA_FILL_SIZE); + + address_range ar = {addr, len}; + g_array_append_val(dma_regions, ar); + pattern p = g_array_index(dma_patterns, pattern, dma_pattern_index); + void *buf = pattern_alloc(p, ar.size); + hwaddr l, addr1; + MemoryRegion *mr1; + uint8_t *ram_ptr; + while (len > 0) { + l = len; + mr1 = address_space_translate(first_cpu->as, + addr, &addr1, &l, true, + MEMTXATTRS_UNSPECIFIED); + + if (!(memory_region_is_ram(mr1) || + memory_region_is_romd(mr1))) { + l = memory_access_size(mr1, l, addr1); + } else { + /* ROM/RAM case */ + ram_ptr = qemu_map_ram_ptr(mr1->ram_block, addr1); + memcpy(ram_ptr, buf, l); + break; + } + len -= l; + buf += l; + addr += l; + + } + if (qtest_log_enabled) { + /* + * With QTEST_LOG, use a normal, slow QTest memwrite. Prefix the log + * that will be written by qtest.c with a DMA tag, so we can reorder + * the resulting QTest trace so the DMA fills precede the last PIO/MMIO + * command. + */ + fprintf(stderr, "[DMA] "); + if (double_fetch) { + fprintf(stderr, "[DOUBLE-FETCH] "); + } + fflush(stderr); + } + qtest_memwrite(qts_global, ar.addr, buf, ar.size); + g_free(buf); + + /* Increment the index of the pattern for the next DMA access */ + dma_pattern_index = (dma_pattern_index + 1) % dma_patterns->len; +} + +/* + * Here we want to convert a fuzzer-provided [io-region-index, offset] to + * a physical address. To do this, we iterate over all of the matched + * MemoryRegions. Check whether each region exists within the particular io + * space. Return the absolute address of the offset within the index'th region + * that is a subregion of the io_space and the distance until the end of the + * memory region. + */ +static bool get_io_address(address_range *result, AddressSpace *as, + uint8_t index, + uint32_t offset) { + FlatView *view; + view = as->current_map; + g_assert(view); + struct get_io_cb_info cb_info = {}; + + cb_info.index = index; + + /* + * Loop around the FlatView until we match "index" number of + * fuzzable_memoryregions, or until we know that there are no matching + * memory_regions. + */ + do { + flatview_for_each_range(view, get_io_address_cb , &cb_info); + } while (cb_info.index != index && !cb_info.found); + + *result = cb_info.result; + return cb_info.found; +} + +static bool get_pio_address(address_range *result, + uint8_t index, uint16_t offset) +{ + /* + * PIO BARs can be set past the maximum port address (0xFFFF). Thus, result + * can contain an addr that extends past the PIO space. When we pass this + * address to qtest_in/qtest_out, it is cast to a uint16_t, so we might end + * up fuzzing a completely different MemoryRegion/Device. Therefore, check + * that the address here is within the PIO space limits. + */ + bool found = get_io_address(result, &address_space_io, index, offset); + return result->addr <= 0xFFFF ? found : false; +} + +static bool get_mmio_address(address_range *result, + uint8_t index, uint32_t offset) +{ + return get_io_address(result, &address_space_memory, index, offset); +} + +static void op_in(QTestState *s, const unsigned char * data, size_t len) +{ + enum Sizes {Byte, Word, Long, end_sizes}; + struct { + uint8_t size; + uint8_t base; + uint16_t offset; + } a; + address_range abs; + + if (len < sizeof(a)) { + return; + } + memcpy(&a, data, sizeof(a)); + if (get_pio_address(&abs, a.base, a.offset) == 0) { + return; + } + + switch (a.size %= end_sizes) { + case Byte: + qtest_inb(s, abs.addr); + break; + case Word: + if (abs.size >= 2) { + qtest_inw(s, abs.addr); + } + break; + case Long: + if (abs.size >= 4) { + qtest_inl(s, abs.addr); + } + break; + } +} + +static void op_out(QTestState *s, const unsigned char * data, size_t len) +{ + enum Sizes {Byte, Word, Long, end_sizes}; + struct { + uint8_t size; + uint8_t base; + uint16_t offset; + uint32_t value; + } a; + address_range abs; + + if (len < sizeof(a)) { + return; + } + memcpy(&a, data, sizeof(a)); + + if (get_pio_address(&abs, a.base, a.offset) == 0) { + return; + } + + switch (a.size %= end_sizes) { + case Byte: + qtest_outb(s, abs.addr, a.value & 0xFF); + break; + case Word: + if (abs.size >= 2) { + qtest_outw(s, abs.addr, a.value & 0xFFFF); + } + break; + case Long: + if (abs.size >= 4) { + qtest_outl(s, abs.addr, a.value); + } + break; + } +} + +static void op_read(QTestState *s, const unsigned char * data, size_t len) +{ + enum Sizes {Byte, Word, Long, Quad, end_sizes}; + struct { + uint8_t size; + uint8_t base; + uint32_t offset; + } a; + address_range abs; + + if (len < sizeof(a)) { + return; + } + memcpy(&a, data, sizeof(a)); + + if (get_mmio_address(&abs, a.base, a.offset) == 0) { + return; + } + + switch (a.size %= end_sizes) { + case Byte: + qtest_readb(s, abs.addr); + break; + case Word: + if (abs.size >= 2) { + qtest_readw(s, abs.addr); + } + break; + case Long: + if (abs.size >= 4) { + qtest_readl(s, abs.addr); + } + break; + case Quad: + if (abs.size >= 8) { + qtest_readq(s, abs.addr); + } + break; + } +} + +static void op_write(QTestState *s, const unsigned char * data, size_t len) +{ + enum Sizes {Byte, Word, Long, Quad, end_sizes}; + struct { + uint8_t size; + uint8_t base; + uint32_t offset; + uint64_t value; + } a; + address_range abs; + + if (len < sizeof(a)) { + return; + } + memcpy(&a, data, sizeof(a)); + + if (get_mmio_address(&abs, a.base, a.offset) == 0) { + return; + } + + switch (a.size %= end_sizes) { + case Byte: + qtest_writeb(s, abs.addr, a.value & 0xFF); + break; + case Word: + if (abs.size >= 2) { + qtest_writew(s, abs.addr, a.value & 0xFFFF); + } + break; + case Long: + if (abs.size >= 4) { + qtest_writel(s, abs.addr, a.value & 0xFFFFFFFF); + } + break; + case Quad: + if (abs.size >= 8) { + qtest_writeq(s, abs.addr, a.value); + } + break; + } +} + +static void op_pci_read(QTestState *s, const unsigned char * data, size_t len) +{ + enum Sizes {Byte, Word, Long, end_sizes}; + struct { + uint8_t size; + uint8_t base; + uint8_t offset; + } a; + if (len < sizeof(a) || fuzzable_pci_devices->len == 0 || pci_disabled) { + return; + } + memcpy(&a, data, sizeof(a)); + PCIDevice *dev = g_ptr_array_index(fuzzable_pci_devices, + a.base % fuzzable_pci_devices->len); + int devfn = dev->devfn; + qtest_outl(s, PCI_HOST_BRIDGE_CFG, (1U << 31) | (devfn << 8) | a.offset); + switch (a.size %= end_sizes) { + case Byte: + qtest_inb(s, PCI_HOST_BRIDGE_DATA); + break; + case Word: + qtest_inw(s, PCI_HOST_BRIDGE_DATA); + break; + case Long: + qtest_inl(s, PCI_HOST_BRIDGE_DATA); + break; + } +} + +static void op_pci_write(QTestState *s, const unsigned char * data, size_t len) +{ + enum Sizes {Byte, Word, Long, end_sizes}; + struct { + uint8_t size; + uint8_t base; + uint8_t offset; + uint32_t value; + } a; + if (len < sizeof(a) || fuzzable_pci_devices->len == 0 || pci_disabled) { + return; + } + memcpy(&a, data, sizeof(a)); + PCIDevice *dev = g_ptr_array_index(fuzzable_pci_devices, + a.base % fuzzable_pci_devices->len); + int devfn = dev->devfn; + qtest_outl(s, PCI_HOST_BRIDGE_CFG, (1U << 31) | (devfn << 8) | a.offset); + switch (a.size %= end_sizes) { + case Byte: + qtest_outb(s, PCI_HOST_BRIDGE_DATA, a.value & 0xFF); + break; + case Word: + qtest_outw(s, PCI_HOST_BRIDGE_DATA, a.value & 0xFFFF); + break; + case Long: + qtest_outl(s, PCI_HOST_BRIDGE_DATA, a.value & 0xFFFFFFFF); + break; + } +} + +static void op_add_dma_pattern(QTestState *s, + const unsigned char *data, size_t len) +{ + struct { + /* + * index and stride can be used to increment the index-th byte of the + * pattern by the value stride, for each loop of the pattern. + */ + uint8_t index; + uint8_t stride; + } a; + + if (len < sizeof(a) + 1) { + return; + } + memcpy(&a, data, sizeof(a)); + pattern p = {a.index, a.stride, len - sizeof(a), data + sizeof(a)}; + p.index = a.index % p.len; + g_array_append_val(dma_patterns, p); + return; +} + +static void op_clear_dma_patterns(QTestState *s, + const unsigned char *data, size_t len) +{ + g_array_set_size(dma_patterns, 0); + dma_pattern_index = 0; +} + +static void op_clock_step(QTestState *s, const unsigned char *data, size_t len) +{ + qtest_clock_step_next(s); +} + +static void op_disable_pci(QTestState *s, const unsigned char *data, size_t len) +{ + pci_disabled = true; +} + +static void handle_timeout(int sig) +{ + if (qtest_log_enabled) { + fprintf(stderr, "[Timeout]\n"); + fflush(stderr); + } + _Exit(0); +} + +/* + * Here, we interpret random bytes from the fuzzer, as a sequence of commands. + * Some commands can be variable-width, so we use a separator, SEPARATOR, to + * specify the boundaries between commands. SEPARATOR is used to separate + * "operations" in the fuzz input. Why use a separator, instead of just using + * the operations' length to identify operation boundaries? + * 1. This is a simple way to support variable-length operations + * 2. This adds "stability" to the input. + * For example take the input "AbBcgDefg", where there is no separator and + * Opcodes are capitalized. + * Simply, by removing the first byte, we end up with a very different + * sequence: + * BbcGdefg... + * By adding a separator, we avoid this problem: + * Ab SEP Bcg SEP Defg -> B SEP Bcg SEP Defg + * Since B uses two additional bytes as operands, the first "B" will be + * ignored. The fuzzer actively tries to reduce inputs, so such unused + * bytes are likely to be pruned, eventually. + * + * SEPARATOR is trivial for the fuzzer to discover when using ASan. Optionally, + * SEPARATOR can be manually specified as a dictionary value (see libfuzzer's + * -dict), though this should not be necessary. + * + * As a result, the stream of bytes is converted into a sequence of commands. + * In a simplified example where SEPARATOR is 0xFF: + * 00 01 02 FF 03 04 05 06 FF 01 FF ... + * becomes this sequence of commands: + * 00 01 02 -> op00 (0102) -> in (0102, 2) + * 03 04 05 06 -> op03 (040506) -> write (040506, 3) + * 01 -> op01 (-,0) -> out (-,0) + * ... + * + * Note here that it is the job of the individual opcode functions to check + * that enough data was provided. I.e. in the last command out (,0), out needs + * to check that there is not enough data provided to select an address/value + * for the operation. + */ +static void generic_fuzz(QTestState *s, const unsigned char *Data, size_t Size) +{ + void (*ops[]) (QTestState *s, const unsigned char* , size_t) = { + [OP_IN] = op_in, + [OP_OUT] = op_out, + [OP_READ] = op_read, + [OP_WRITE] = op_write, + [OP_PCI_READ] = op_pci_read, + [OP_PCI_WRITE] = op_pci_write, + [OP_DISABLE_PCI] = op_disable_pci, + [OP_ADD_DMA_PATTERN] = op_add_dma_pattern, + [OP_CLEAR_DMA_PATTERNS] = op_clear_dma_patterns, + [OP_CLOCK_STEP] = op_clock_step, + }; + const unsigned char *cmd = Data; + const unsigned char *nextcmd; + size_t cmd_len; + uint8_t op; + + if (fork() == 0) { + /* + * Sometimes the fuzzer will find inputs that take quite a long time to + * process. Often times, these inputs do not result in new coverage. + * Even if these inputs might be interesting, they can slow down the + * fuzzer, overall. Set a timeout to avoid hurting performance, too much + */ + if (timeout) { + struct sigaction sact; + struct itimerval timer; + + sigemptyset(&sact.sa_mask); + sact.sa_flags = SA_NODEFER; + sact.sa_handler = handle_timeout; + sigaction(SIGALRM, &sact, NULL); + + memset(&timer, 0, sizeof(timer)); + timer.it_value.tv_sec = timeout / USEC_IN_SEC; + timer.it_value.tv_usec = timeout % USEC_IN_SEC; + setitimer(ITIMER_VIRTUAL, &timer, NULL); + } + + op_clear_dma_patterns(s, NULL, 0); + pci_disabled = false; + + while (cmd && Size) { + /* Get the length until the next command or end of input */ + nextcmd = memmem(cmd, Size, SEPARATOR, strlen(SEPARATOR)); + cmd_len = nextcmd ? nextcmd - cmd : Size; + + if (cmd_len > 0) { + /* Interpret the first byte of the command as an opcode */ + op = *cmd % (sizeof(ops) / sizeof((ops)[0])); + ops[op](s, cmd + 1, cmd_len - 1); + + /* Run the main loop */ + flush_events(s); + } + /* Advance to the next command */ + cmd = nextcmd ? nextcmd + sizeof(SEPARATOR) - 1 : nextcmd; + Size = Size - (cmd_len + sizeof(SEPARATOR) - 1); + g_array_set_size(dma_regions, 0); + } + _Exit(0); + } else { + flush_events(s); + wait(0); + } +} + +static void usage(void) +{ + printf("Please specify the following environment variables:\n"); + printf("QEMU_FUZZ_ARGS= the command line arguments passed to qemu\n"); + printf("QEMU_FUZZ_OBJECTS= " + "a space separated list of QOM type names for objects to fuzz\n"); + printf("Optionally: QEMU_AVOID_DOUBLE_FETCH= " + "Try to avoid racy DMA double fetch bugs? %d by default\n", + avoid_double_fetches); + printf("Optionally: QEMU_FUZZ_TIMEOUT= Specify a custom timeout (us). " + "0 to disable. %d by default\n", timeout); + exit(0); +} + +static int locate_fuzz_memory_regions(Object *child, void *opaque) +{ + const char *name; + MemoryRegion *mr; + if (object_dynamic_cast(child, TYPE_MEMORY_REGION)) { + mr = MEMORY_REGION(child); + if ((memory_region_is_ram(mr) || + memory_region_is_ram_device(mr) || + memory_region_is_rom(mr)) == false) { + name = object_get_canonical_path_component(child); + /* + * We don't want duplicate pointers to the same MemoryRegion, so + * try to remove copies of the pointer, before adding it. + */ + g_hash_table_insert(fuzzable_memoryregions, mr, (gpointer)true); + } + } + return 0; +} + +static int locate_fuzz_objects(Object *child, void *opaque) +{ + char *pattern = opaque; + if (g_pattern_match_simple(pattern, object_get_typename(child))) { + /* Find and save ptrs to any child MemoryRegions */ + object_child_foreach_recursive(child, locate_fuzz_memory_regions, NULL); + + /* + * We matched an object. If its a PCI device, store a pointer to it so + * we can map BARs and fuzz its config space. + */ + if (object_dynamic_cast(OBJECT(child), TYPE_PCI_DEVICE)) { + /* + * Don't want duplicate pointers to the same PCIDevice, so remove + * copies of the pointer, before adding it. + */ + g_ptr_array_remove_fast(fuzzable_pci_devices, PCI_DEVICE(child)); + g_ptr_array_add(fuzzable_pci_devices, PCI_DEVICE(child)); + } + } else if (object_dynamic_cast(OBJECT(child), TYPE_MEMORY_REGION)) { + if (g_pattern_match_simple(pattern, + object_get_canonical_path_component(child))) { + MemoryRegion *mr; + mr = MEMORY_REGION(child); + if ((memory_region_is_ram(mr) || + memory_region_is_ram_device(mr) || + memory_region_is_rom(mr)) == false) { + g_hash_table_insert(fuzzable_memoryregions, mr, (gpointer)true); + } + } + } + return 0; +} + +static void generic_pre_fuzz(QTestState *s) +{ + GHashTableIter iter; + MemoryRegion *mr; + char **result; + + if (!getenv("QEMU_FUZZ_OBJECTS")) { + usage(); + } + if (getenv("QTEST_LOG")) { + qtest_log_enabled = 1; + } + if (getenv("QEMU_AVOID_DOUBLE_FETCH")) { + avoid_double_fetches = 1; + } + if (getenv("QEMU_FUZZ_TIMEOUT")) { + timeout = g_ascii_strtoll(getenv("QEMU_FUZZ_TIMEOUT"), NULL, 0); + } + qts_global = s; + + dma_regions = g_array_new(false, false, sizeof(address_range)); + dma_patterns = g_array_new(false, false, sizeof(pattern)); + + fuzzable_memoryregions = g_hash_table_new(NULL, NULL); + fuzzable_pci_devices = g_ptr_array_new(); + + result = g_strsplit(getenv("QEMU_FUZZ_OBJECTS"), " ", -1); + for (int i = 0; result[i] != NULL; i++) { + printf("Matching objects by name %s\n", result[i]); + object_child_foreach_recursive(qdev_get_machine(), + locate_fuzz_objects, + result[i]); + } + g_strfreev(result); + printf("This process will try to fuzz the following MemoryRegions:\n"); + + g_hash_table_iter_init(&iter, fuzzable_memoryregions); + while (g_hash_table_iter_next(&iter, (gpointer)&mr, NULL)) { + printf(" * %s (size %lx)\n", + object_get_canonical_path_component(&(mr->parent_obj)), + (uint64_t)mr->size); + } + + if (!g_hash_table_size(fuzzable_memoryregions)) { + printf("No fuzzable memory regions found...\n"); + exit(1); + } + + counter_shm_init(); +} + +/* + * When libfuzzer gives us two inputs to combine, return a new input with the + * following structure: + * + * Input 1 (data1) + * SEPARATOR + * Clear out the DMA Patterns + * SEPARATOR + * Disable the pci_read/write instructions + * SEPARATOR + * Input 2 (data2) + * + * The idea is to collate the core behaviors of the two inputs. + * For example: + * Input 1: maps a device's BARs, sets up three DMA patterns, and triggers + * device functionality A + * Input 2: maps a device's BARs, sets up one DMA pattern, and triggers device + * functionality B + * + * This function attempts to produce an input that: + * Ouptut: maps a device's BARs, set up three DMA patterns, triggers + * functionality A device, replaces the DMA patterns with a single + * patten, and triggers device functionality B. + */ +static size_t generic_fuzz_crossover(const uint8_t *data1, size_t size1, const + uint8_t *data2, size_t size2, uint8_t *out, + size_t max_out_size, unsigned int seed) +{ + size_t copy_len = 0, size = 0; + + /* Check that we have enough space for data1 and at least part of data2 */ + if (max_out_size <= size1 + strlen(SEPARATOR) * 3 + 2) { + return 0; + } + + /* Copy_Len in the first input */ + copy_len = size1; + memcpy(out + size, data1, copy_len); + size += copy_len; + max_out_size -= copy_len; + + /* Append a separator */ + copy_len = strlen(SEPARATOR); + memcpy(out + size, SEPARATOR, copy_len); + size += copy_len; + max_out_size -= copy_len; + + /* Clear out the DMA Patterns */ + copy_len = 1; + if (copy_len) { + out[size] = OP_CLEAR_DMA_PATTERNS; + } + size += copy_len; + max_out_size -= copy_len; + + /* Append a separator */ + copy_len = strlen(SEPARATOR); + memcpy(out + size, SEPARATOR, copy_len); + size += copy_len; + max_out_size -= copy_len; + + /* Disable PCI ops. Assume data1 took care of setting up PCI */ + copy_len = 1; + if (copy_len) { + out[size] = OP_DISABLE_PCI; + } + size += copy_len; + max_out_size -= copy_len; + + /* Append a separator */ + copy_len = strlen(SEPARATOR); + memcpy(out + size, SEPARATOR, copy_len); + size += copy_len; + max_out_size -= copy_len; + + /* Copy_Len over the second input */ + copy_len = MIN(size2, max_out_size); + memcpy(out + size, data2, copy_len); + size += copy_len; + max_out_size -= copy_len; + + return size; +} + + +static GString *generic_fuzz_cmdline(FuzzTarget *t) +{ + GString *cmd_line = g_string_new(TARGET_NAME); + if (!getenv("QEMU_FUZZ_ARGS")) { + usage(); + } + g_string_append_printf(cmd_line, " -display none \ + -machine accel=qtest, \ + -m 512M %s ", getenv("QEMU_FUZZ_ARGS")); + return cmd_line; +} + +static GString *generic_fuzz_predefined_config_cmdline(FuzzTarget *t) +{ + const generic_fuzz_config *config; + g_assert(t->opaque); + + config = t->opaque; + setenv("QEMU_FUZZ_ARGS", config->args, 1); + setenv("QEMU_FUZZ_OBJECTS", config->objects, 1); + return generic_fuzz_cmdline(t); +} + +static void register_generic_fuzz_targets(void) +{ + fuzz_add_target(&(FuzzTarget){ + .name = "generic-fuzz", + .description = "Fuzz based on any qemu command-line args. ", + .get_init_cmdline = generic_fuzz_cmdline, + .pre_fuzz = generic_pre_fuzz, + .fuzz = generic_fuzz, + .crossover = generic_fuzz_crossover + }); + + GString *name; + const generic_fuzz_config *config; + + for (int i = 0; + i < sizeof(predefined_configs) / sizeof(generic_fuzz_config); + i++) { + config = predefined_configs + i; + name = g_string_new("generic-fuzz"); + g_string_append_printf(name, "-%s", config->name); + fuzz_add_target(&(FuzzTarget){ + .name = name->str, + .description = "Predefined generic-fuzz config.", + .get_init_cmdline = generic_fuzz_predefined_config_cmdline, + .pre_fuzz = generic_pre_fuzz, + .fuzz = generic_fuzz, + .crossover = generic_fuzz_crossover, + .opaque = (void *)config + }); + } +} + +fuzz_target_init(register_generic_fuzz_targets); diff --git a/tests/qtest/fuzz/generic_fuzz_configs.h b/tests/qtest/fuzz/generic_fuzz_configs.h new file mode 100644 index 0000000000..c4d925f9e6 --- /dev/null +++ b/tests/qtest/fuzz/generic_fuzz_configs.h @@ -0,0 +1,121 @@ +/* + * Generic Virtual-Device Fuzzing Target Configs + * + * Copyright Red Hat Inc., 2020 + * + * Authors: + * Alexander Bulekov <alxndr@bu.edu> + * + * 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 GENERIC_FUZZ_CONFIGS_H +#define GENERIC_FUZZ_CONFIGS_H + +#include "qemu/osdep.h" + +typedef struct generic_fuzz_config { + const char *name, *args, *objects; +} generic_fuzz_config; + +const generic_fuzz_config predefined_configs[] = { + { + .name = "virtio-net-pci-slirp", + .args = "-M q35 -nodefaults " + "-device virtio-net,netdev=net0 -netdev user,id=net0", + .objects = "virtio*", + },{ + .name = "virtio-blk", + .args = "-machine q35 -device virtio-blk,drive=disk0 " + "-drive file=null-co://,id=disk0,if=none,format=raw", + .objects = "virtio*", + },{ + .name = "virtio-scsi", + .args = "-machine q35 -device virtio-scsi,num_queues=8 " + "-device scsi-hd,drive=disk0 " + "-drive file=null-co://,id=disk0,if=none,format=raw", + .objects = "scsi* virtio*", + },{ + .name = "virtio-gpu", + .args = "-machine q35 -nodefaults -device virtio-gpu", + .objects = "virtio*", + },{ + .name = "virtio-vga", + .args = "-machine q35 -nodefaults -device virtio-vga", + .objects = "virtio*", + },{ + .name = "virtio-rng", + .args = "-machine q35 -nodefaults -device virtio-rng", + .objects = "virtio*", + },{ + .name = "virtio-balloon", + .args = "-machine q35 -nodefaults -device virtio-balloon", + .objects = "virtio*", + },{ + .name = "virtio-serial", + .args = "-machine q35 -nodefaults -device virtio-serial", + .objects = "virtio*", + },{ + .name = "virtio-mouse", + .args = "-machine q35 -nodefaults -device virtio-mouse", + .objects = "virtio*", + },{ + .name = "e1000", + .args = "-M q35 -nodefaults " + "-device e1000,netdev=net0 -netdev user,id=net0", + .objects = "e1000", + },{ + .name = "e1000e", + .args = "-M q35 -nodefaults " + "-device e1000e,netdev=net0 -netdev user,id=net0", + .objects = "e1000e", + },{ + .name = "cirrus-vga", + .args = "-machine q35 -nodefaults -device cirrus-vga", + .objects = "cirrus*", + },{ + .name = "bochs-display", + .args = "-machine q35 -nodefaults -device bochs-display", + .objects = "bochs*", + },{ + .name = "intel-hda", + .args = "-machine q35 -nodefaults -device intel-hda,id=hda0 " + "-device hda-output,bus=hda0.0 -device hda-micro,bus=hda0.0 " + "-device hda-duplex,bus=hda0.0", + .objects = "intel-hda", + },{ + .name = "ide-hd", + .args = "-machine q35 -nodefaults " + "-drive file=null-co://,if=none,format=raw,id=disk0 " + "-device ide-hd,drive=disk0", + .objects = "ahci*", + },{ + .name = "floppy", + .args = "-machine pc -nodefaults -device floppy,id=floppy0 " + "-drive id=disk0,file=null-co://,file.read-zeroes=on,if=none " + "-device floppy,drive=disk0,drive-type=288", + .objects = "fd* floppy*", + },{ + .name = "xhci", + .args = "-machine q35 -nodefaults " + "-drive file=null-co://,if=none,format=raw,id=disk0 " + "-device qemu-xhci,id=xhci -device usb-tablet,bus=xhci.0 " + "-device usb-bot -device usb-storage,drive=disk0 " + "-chardev null,id=cd0 -chardev null,id=cd1 " + "-device usb-braille,chardev=cd0 -device usb-ccid -device usb-ccid " + "-device usb-kbd -device usb-mouse -device usb-serial,chardev=cd1 " + "-device usb-tablet -device usb-wacom-tablet -device usb-audio", + .objects = "*usb* *uhci* *xhci*", + },{ + .name = "pc-i440fx", + .args = "-machine pc", + .objects = "*", + },{ + .name = "pc-q35", + .args = "-machine q35", + .objects = "*", + } +}; + +#endif diff --git a/tests/qtest/fuzz/meson.build b/tests/qtest/fuzz/meson.build index b31ace7d5a..5162321f30 100644 --- a/tests/qtest/fuzz/meson.build +++ b/tests/qtest/fuzz/meson.build @@ -5,6 +5,7 @@ specific_fuzz_ss.add(files('fuzz.c', 'fork_fuzz.c', 'qos_fuzz.c', specific_fuzz_ss.add(when: 'CONFIG_I440FX', if_true: files('i440fx_fuzz.c')) specific_fuzz_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('virtio_net_fuzz.c')) specific_fuzz_ss.add(when: 'CONFIG_VIRTIO_SCSI', if_true: files('virtio_scsi_fuzz.c')) +specific_fuzz_ss.add(files('generic_fuzz.c')) fork_fuzz = declare_dependency( link_args: config_host['FUZZ_EXE_LDFLAGS'].split() + diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 08929f5ff6..99deff47ef 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -621,7 +621,7 @@ QDict *qtest_qmp_receive(QTestState *s) return response; } /* Stash the event for a later consumption */ - s->pending_events = g_list_prepend(s->pending_events, response); + s->pending_events = g_list_append(s->pending_events, response); } } @@ -795,15 +795,12 @@ void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...) QDict *qtest_qmp_event_ref(QTestState *s, const char *event) { - GList *next = NULL; - QDict *response; - - for (GList *it = s->pending_events; it != NULL; it = next) { + while (s->pending_events) { - next = it->next; - response = (QDict *)it->data; + GList *first = s->pending_events; + QDict *response = (QDict *)first->data; - s->pending_events = g_list_remove_link(s->pending_events, it); + s->pending_events = g_list_delete_link(s->pending_events, first); if (!strcmp(qdict_get_str(response, "event"), event)) { return response; @@ -870,9 +867,14 @@ char *qtest_hmp(QTestState *s, const char *fmt, ...) const char *qtest_get_arch(void) { const char *qemu = qtest_qemu_binary(); - const char *end = strrchr(qemu, '/'); + const char *end = strrchr(qemu, '-'); + + if (!end) { + fprintf(stderr, "Can't determine architecture from binary name.\n"); + abort(); + } - return end + strlen("/qemu-system-"); + return end + 1; } bool qtest_get_irq(QTestState *s, int num) diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 28d4068718..c19f1c8503 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -133,12 +133,17 @@ qtests_sparc64 = \ (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : []) + \ ['prom-env-test', 'boot-serial-test'] +qtests_npcm7xx = \ + ['npcm7xx_gpio-test', + 'npcm7xx_rng-test', + 'npcm7xx_timer-test', + 'npcm7xx_watchdog_timer-test'] qtests_arm = \ (config_all_devices.has_key('CONFIG_PFLASH_CFI02') ? ['pflash-cfi02-test'] : []) + \ + (config_all_devices.has_key('CONFIG_NPCM7XX') ? qtests_npcm7xx : []) + \ ['arm-cpu-features', 'microbit-test', 'm25p80-test', - 'npcm7xx_timer-test', 'test-arm-mptimer', 'boot-serial-test', 'hexloader-test'] @@ -251,7 +256,7 @@ foreach dir : target_dirs } endif # FIXME: missing dependency on the emulator binary and qemu-img - test('qtest-@0@: @1@'.format(target_base, test), + test('qtest-@0@/@1@'.format(target_base, test), qtest_executables[test], depends: [test_deps, qtest_emulator], env: qtest_env, diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index b799dbafb7..4ee26014b7 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -32,7 +32,7 @@ static void check_stop_event(QTestState *who) QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...) { va_list ap; - QDict *resp; + QDict *resp, *ret; va_start(ap, command); qtest_qmp_vsend_fds(who, &fd, 1, command, ap); @@ -44,7 +44,11 @@ QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...) g_assert(!qdict_haskey(resp, "error")); g_assert(qdict_haskey(resp, "return")); - return qdict_get_qdict(resp, "return"); + ret = qdict_get_qdict(resp, "return"); + qobject_ref(ret); + qobject_unref(resp); + + return ret; } /* @@ -53,7 +57,7 @@ QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...) QDict *wait_command(QTestState *who, const char *command, ...) { va_list ap; - QDict *resp; + QDict *resp, *ret; va_start(ap, command); resp = qtest_vqmp(who, command, ap); @@ -64,7 +68,11 @@ QDict *wait_command(QTestState *who, const char *command, ...) g_assert(!qdict_haskey(resp, "error")); g_assert(qdict_haskey(resp, "return")); - return qdict_get_qdict(resp, "return"); + ret = qdict_get_qdict(resp, "return"); + qobject_ref(ret); + qobject_unref(resp); + + return ret; } /* diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index f410ec5996..f2142fbd3c 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -464,6 +464,10 @@ static void migrate_postcopy_start(QTestState *from, QTestState *to) } typedef struct { + /* + * QTEST_LOG=1 may override this. When QTEST_LOG=1, we always dump errors + * unconditionally, because it means the user would like to be verbose. + */ bool hide_stderr; bool use_shmem; /* only launch the target process */ @@ -557,7 +561,7 @@ static int test_migrate_start(QTestState **from, QTestState **to, g_free(bootpath); - if (args->hide_stderr) { + if (!getenv("QTEST_LOG") && args->hide_stderr) { ignore_stderr = "2>/dev/null"; } else { ignore_stderr = ""; diff --git a/tests/qtest/npcm7xx_gpio-test.c b/tests/qtest/npcm7xx_gpio-test.c new file mode 100644 index 0000000000..1004cef812 --- /dev/null +++ b/tests/qtest/npcm7xx_gpio-test.c @@ -0,0 +1,385 @@ +/* + * QTest testcase for the Nuvoton NPCM7xx GPIO modules. + * + * Copyright 2020 Google LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" + +#define NR_GPIO_DEVICES (8) +#define GPIO(x) (0xf0010000 + (x) * 0x1000) +#define GPIO_IRQ(x) (116 + (x)) + +/* GPIO registers */ +#define GP_N_TLOCK1 0x00 +#define GP_N_DIN 0x04 /* Data IN */ +#define GP_N_POL 0x08 /* Polarity */ +#define GP_N_DOUT 0x0c /* Data OUT */ +#define GP_N_OE 0x10 /* Output Enable */ +#define GP_N_OTYP 0x14 +#define GP_N_MP 0x18 +#define GP_N_PU 0x1c /* Pull-up */ +#define GP_N_PD 0x20 /* Pull-down */ +#define GP_N_DBNC 0x24 /* Debounce */ +#define GP_N_EVTYP 0x28 /* Event Type */ +#define GP_N_EVBE 0x2c /* Event Both Edge */ +#define GP_N_OBL0 0x30 +#define GP_N_OBL1 0x34 +#define GP_N_OBL2 0x38 +#define GP_N_OBL3 0x3c +#define GP_N_EVEN 0x40 /* Event Enable */ +#define GP_N_EVENS 0x44 /* Event Set (enable) */ +#define GP_N_EVENC 0x48 /* Event Clear (disable) */ +#define GP_N_EVST 0x4c /* Event Status */ +#define GP_N_SPLCK 0x50 +#define GP_N_MPLCK 0x54 +#define GP_N_IEM 0x58 /* Input Enable */ +#define GP_N_OSRC 0x5c +#define GP_N_ODSC 0x60 +#define GP_N_DOS 0x68 /* Data OUT Set */ +#define GP_N_DOC 0x6c /* Data OUT Clear */ +#define GP_N_OES 0x70 /* Output Enable Set */ +#define GP_N_OEC 0x74 /* Output Enable Clear */ +#define GP_N_TLOCK2 0x7c + +static void gpio_unlock(int n) +{ + if (readl(GPIO(n) + GP_N_TLOCK1) != 0) { + writel(GPIO(n) + GP_N_TLOCK2, 0xc0de1248); + writel(GPIO(n) + GP_N_TLOCK1, 0xc0defa73); + } +} + +/* Restore the GPIO controller to a sensible default state. */ +static void gpio_reset(int n) +{ + gpio_unlock(0); + + writel(GPIO(n) + GP_N_EVEN, 0x00000000); + writel(GPIO(n) + GP_N_EVST, 0xffffffff); + writel(GPIO(n) + GP_N_POL, 0x00000000); + writel(GPIO(n) + GP_N_DOUT, 0x00000000); + writel(GPIO(n) + GP_N_OE, 0x00000000); + writel(GPIO(n) + GP_N_OTYP, 0x00000000); + writel(GPIO(n) + GP_N_PU, 0xffffffff); + writel(GPIO(n) + GP_N_PD, 0x00000000); + writel(GPIO(n) + GP_N_IEM, 0xffffffff); +} + +static void test_dout_to_din(void) +{ + gpio_reset(0); + + /* When output is enabled, DOUT should be reflected on DIN. */ + writel(GPIO(0) + GP_N_OE, 0xffffffff); + /* PU and PD shouldn't have any impact on DIN. */ + writel(GPIO(0) + GP_N_PU, 0xffff0000); + writel(GPIO(0) + GP_N_PD, 0x0000ffff); + writel(GPIO(0) + GP_N_DOUT, 0x12345678); + g_assert_cmphex(readl(GPIO(0) + GP_N_DOUT), ==, 0x12345678); + g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0x12345678); +} + +static void test_pullup_pulldown(void) +{ + gpio_reset(0); + + /* + * When output is disabled, and PD is the inverse of PU, PU should be + * reflected on DIN. If PD is not the inverse of PU, the state of DIN is + * undefined, so we don't test that. + */ + writel(GPIO(0) + GP_N_OE, 0x00000000); + /* DOUT shouldn't have any impact on DIN. */ + writel(GPIO(0) + GP_N_DOUT, 0xffff0000); + writel(GPIO(0) + GP_N_PU, 0x23456789); + writel(GPIO(0) + GP_N_PD, ~0x23456789U); + g_assert_cmphex(readl(GPIO(0) + GP_N_PU), ==, 0x23456789); + g_assert_cmphex(readl(GPIO(0) + GP_N_PD), ==, ~0x23456789U); + g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0x23456789); +} + +static void test_output_enable(void) +{ + gpio_reset(0); + + /* + * With all pins weakly pulled down, and DOUT all-ones, OE should be + * reflected on DIN. + */ + writel(GPIO(0) + GP_N_DOUT, 0xffffffff); + writel(GPIO(0) + GP_N_PU, 0x00000000); + writel(GPIO(0) + GP_N_PD, 0xffffffff); + writel(GPIO(0) + GP_N_OE, 0x3456789a); + g_assert_cmphex(readl(GPIO(0) + GP_N_OE), ==, 0x3456789a); + g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0x3456789a); + + writel(GPIO(0) + GP_N_OEC, 0x00030002); + g_assert_cmphex(readl(GPIO(0) + GP_N_OE), ==, 0x34547898); + g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0x34547898); + + writel(GPIO(0) + GP_N_OES, 0x0000f001); + g_assert_cmphex(readl(GPIO(0) + GP_N_OE), ==, 0x3454f899); + g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0x3454f899); +} + +static void test_open_drain(void) +{ + gpio_reset(0); + + /* + * Upper half of DOUT drives a 1 only if the corresponding bit in OTYP is + * not set. If OTYP is set, DIN is determined by PU/PD. Lower half of + * DOUT always drives a 0 regardless of OTYP; PU/PD have no effect. When + * OE is 0, output is determined by PU/PD; OTYP has no effect. + */ + writel(GPIO(0) + GP_N_OTYP, 0x456789ab); + writel(GPIO(0) + GP_N_OE, 0xf0f0f0f0); + writel(GPIO(0) + GP_N_DOUT, 0xffff0000); + writel(GPIO(0) + GP_N_PU, 0xff00ff00); + writel(GPIO(0) + GP_N_PD, 0x00ff00ff); + g_assert_cmphex(readl(GPIO(0) + GP_N_OTYP), ==, 0x456789ab); + g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0xff900f00); +} + +static void test_polarity(void) +{ + gpio_reset(0); + + /* + * In push-pull mode, DIN should reflect DOUT because the signal is + * inverted in both directions. + */ + writel(GPIO(0) + GP_N_OTYP, 0x00000000); + writel(GPIO(0) + GP_N_OE, 0xffffffff); + writel(GPIO(0) + GP_N_DOUT, 0x56789abc); + writel(GPIO(0) + GP_N_POL, 0x6789abcd); + g_assert_cmphex(readl(GPIO(0) + GP_N_POL), ==, 0x6789abcd); + g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0x56789abc); + + /* + * When turning off the drivers, DIN should reflect the inverse of the + * pulled-up lines. + */ + writel(GPIO(0) + GP_N_OE, 0x00000000); + writel(GPIO(0) + GP_N_POL, 0xffffffff); + writel(GPIO(0) + GP_N_PU, 0x789abcde); + writel(GPIO(0) + GP_N_PD, ~0x789abcdeU); + g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, ~0x789abcdeU); + + /* + * In open-drain mode, DOUT=1 will appear to drive the pin high (since DIN + * is inverted), while DOUT=0 will leave the pin floating. + */ + writel(GPIO(0) + GP_N_OTYP, 0xffffffff); + writel(GPIO(0) + GP_N_OE, 0xffffffff); + writel(GPIO(0) + GP_N_PU, 0xffff0000); + writel(GPIO(0) + GP_N_PD, 0x0000ffff); + writel(GPIO(0) + GP_N_DOUT, 0xff00ff00); + g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0xff00ffff); +} + +static void test_input_mask(void) +{ + gpio_reset(0); + + /* IEM=0 forces the input to zero before polarity inversion. */ + writel(GPIO(0) + GP_N_OE, 0xffffffff); + writel(GPIO(0) + GP_N_DOUT, 0xff00ff00); + writel(GPIO(0) + GP_N_POL, 0xffff0000); + writel(GPIO(0) + GP_N_IEM, 0x87654321); + g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0xff9a4300); +} + +static void test_temp_lock(void) +{ + gpio_reset(0); + + writel(GPIO(0) + GP_N_DOUT, 0x98765432); + + /* Make sure we're unlocked initially. */ + g_assert_cmphex(readl(GPIO(0) + GP_N_TLOCK1), ==, 0); + /* Writing any value to TLOCK1 will lock. */ + writel(GPIO(0) + GP_N_TLOCK1, 0); + g_assert_cmphex(readl(GPIO(0) + GP_N_TLOCK1), ==, 1); + writel(GPIO(0) + GP_N_DOUT, 0xa9876543); + g_assert_cmphex(readl(GPIO(0) + GP_N_DOUT), ==, 0x98765432); + /* Now, try to unlock. */ + gpio_unlock(0); + g_assert_cmphex(readl(GPIO(0) + GP_N_TLOCK1), ==, 0); + writel(GPIO(0) + GP_N_DOUT, 0xa9876543); + g_assert_cmphex(readl(GPIO(0) + GP_N_DOUT), ==, 0xa9876543); + + /* Try it again, but write TLOCK2 to lock. */ + writel(GPIO(0) + GP_N_TLOCK2, 0); + g_assert_cmphex(readl(GPIO(0) + GP_N_TLOCK1), ==, 1); + writel(GPIO(0) + GP_N_DOUT, 0x98765432); + g_assert_cmphex(readl(GPIO(0) + GP_N_DOUT), ==, 0xa9876543); + /* Now, try to unlock. */ + gpio_unlock(0); + g_assert_cmphex(readl(GPIO(0) + GP_N_TLOCK1), ==, 0); + writel(GPIO(0) + GP_N_DOUT, 0x98765432); + g_assert_cmphex(readl(GPIO(0) + GP_N_DOUT), ==, 0x98765432); +} + +static void test_events_level(void) +{ + gpio_reset(0); + + writel(GPIO(0) + GP_N_EVTYP, 0x00000000); + writel(GPIO(0) + GP_N_DOUT, 0xba987654); + writel(GPIO(0) + GP_N_OE, 0xffffffff); + writel(GPIO(0) + GP_N_EVST, 0xffffffff); + + g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0xba987654); + g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); + writel(GPIO(0) + GP_N_DOUT, 0x00000000); + g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0xba987654); + g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); + writel(GPIO(0) + GP_N_EVST, 0x00007654); + g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0xba980000); + g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); + writel(GPIO(0) + GP_N_EVST, 0xba980000); + g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00000000); + g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); +} + +static void test_events_rising_edge(void) +{ + gpio_reset(0); + + writel(GPIO(0) + GP_N_EVTYP, 0xffffffff); + writel(GPIO(0) + GP_N_EVBE, 0x00000000); + writel(GPIO(0) + GP_N_DOUT, 0xffff0000); + writel(GPIO(0) + GP_N_OE, 0xffffffff); + writel(GPIO(0) + GP_N_EVST, 0xffffffff); + + g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00000000); + g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); + writel(GPIO(0) + GP_N_DOUT, 0xff00ff00); + g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x0000ff00); + g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); + writel(GPIO(0) + GP_N_DOUT, 0x00ff0000); + g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00ffff00); + g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); + writel(GPIO(0) + GP_N_EVST, 0x0000f000); + g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00ff0f00); + g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); + writel(GPIO(0) + GP_N_EVST, 0x00ff0f00); + g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00000000); + g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); +} + +static void test_events_both_edges(void) +{ + gpio_reset(0); + + writel(GPIO(0) + GP_N_EVTYP, 0xffffffff); + writel(GPIO(0) + GP_N_EVBE, 0xffffffff); + writel(GPIO(0) + GP_N_DOUT, 0xffff0000); + writel(GPIO(0) + GP_N_OE, 0xffffffff); + writel(GPIO(0) + GP_N_EVST, 0xffffffff); + + g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00000000); + g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); + writel(GPIO(0) + GP_N_DOUT, 0xff00ff00); + g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00ffff00); + g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); + writel(GPIO(0) + GP_N_DOUT, 0xef00ff08); + g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x10ffff08); + g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); + writel(GPIO(0) + GP_N_EVST, 0x0000f000); + g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x10ff0f08); + g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); + writel(GPIO(0) + GP_N_EVST, 0x10ff0f08); + g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00000000); + g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0))); +} + +static void test_gpion_irq(gconstpointer test_data) +{ + intptr_t n = (intptr_t)test_data; + + gpio_reset(n); + + writel(GPIO(n) + GP_N_EVTYP, 0x00000000); + writel(GPIO(n) + GP_N_DOUT, 0x00000000); + writel(GPIO(n) + GP_N_OE, 0xffffffff); + writel(GPIO(n) + GP_N_EVST, 0xffffffff); + writel(GPIO(n) + GP_N_EVEN, 0x00000000); + + /* Trigger an event; interrupts are masked. */ + g_assert_cmphex(readl(GPIO(n) + GP_N_EVST), ==, 0x00000000); + g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(n))); + writel(GPIO(n) + GP_N_DOS, 0x00008000); + g_assert_cmphex(readl(GPIO(n) + GP_N_EVST), ==, 0x00008000); + g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(n))); + + /* Unmask all event interrupts; verify that the interrupt fired. */ + writel(GPIO(n) + GP_N_EVEN, 0xffffffff); + g_assert_true(qtest_get_irq(global_qtest, GPIO_IRQ(n))); + + /* Clear the current bit, set a new bit, irq stays asserted. */ + writel(GPIO(n) + GP_N_DOC, 0x00008000); + g_assert_true(qtest_get_irq(global_qtest, GPIO_IRQ(n))); + writel(GPIO(n) + GP_N_DOS, 0x00000200); + g_assert_true(qtest_get_irq(global_qtest, GPIO_IRQ(n))); + writel(GPIO(n) + GP_N_EVST, 0x00008000); + g_assert_true(qtest_get_irq(global_qtest, GPIO_IRQ(n))); + + /* Mask/unmask the event that's currently active. */ + writel(GPIO(n) + GP_N_EVENC, 0x00000200); + g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(n))); + writel(GPIO(n) + GP_N_EVENS, 0x00000200); + g_assert_true(qtest_get_irq(global_qtest, GPIO_IRQ(n))); + + /* Clear the input and the status bit, irq is deasserted. */ + writel(GPIO(n) + GP_N_DOC, 0x00000200); + g_assert_true(qtest_get_irq(global_qtest, GPIO_IRQ(n))); + writel(GPIO(n) + GP_N_EVST, 0x00000200); + g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(n))); +} + +int main(int argc, char **argv) +{ + int ret; + int i; + + g_test_init(&argc, &argv, NULL); + g_test_set_nonfatal_assertions(); + + qtest_add_func("/npcm7xx_gpio/dout_to_din", test_dout_to_din); + qtest_add_func("/npcm7xx_gpio/pullup_pulldown", test_pullup_pulldown); + qtest_add_func("/npcm7xx_gpio/output_enable", test_output_enable); + qtest_add_func("/npcm7xx_gpio/open_drain", test_open_drain); + qtest_add_func("/npcm7xx_gpio/polarity", test_polarity); + qtest_add_func("/npcm7xx_gpio/input_mask", test_input_mask); + qtest_add_func("/npcm7xx_gpio/temp_lock", test_temp_lock); + qtest_add_func("/npcm7xx_gpio/events/level", test_events_level); + qtest_add_func("/npcm7xx_gpio/events/rising_edge", test_events_rising_edge); + qtest_add_func("/npcm7xx_gpio/events/both_edges", test_events_both_edges); + + for (i = 0; i < NR_GPIO_DEVICES; i++) { + g_autofree char *test_name = + g_strdup_printf("/npcm7xx_gpio/gpio[%d]/irq", i); + qtest_add_data_func(test_name, (void *)(intptr_t)i, test_gpion_irq); + } + + qtest_start("-machine npcm750-evb"); + qtest_irq_intercept_in(global_qtest, "/machine/soc/a9mpcore/gic"); + ret = g_test_run(); + qtest_end(); + + return ret; +} diff --git a/tests/qtest/npcm7xx_rng-test.c b/tests/qtest/npcm7xx_rng-test.c new file mode 100644 index 0000000000..da6e639bf6 --- /dev/null +++ b/tests/qtest/npcm7xx_rng-test.c @@ -0,0 +1,278 @@ +/* + * QTest testcase for the Nuvoton NPCM7xx Random Number Generator + * + * Copyright 2020 Google LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "qemu/osdep.h" + +#include <math.h> + +#include "libqtest-single.h" +#include "qemu/bitops.h" + +#define RNG_BASE_ADDR 0xf000b000 + +/* Control and Status Register */ +#define RNGCS 0x00 +# define DVALID BIT(1) /* Data Valid */ +# define RNGE BIT(0) /* RNG Enable */ +/* Data Register */ +#define RNGD 0x04 +/* Mode Register */ +#define RNGMODE 0x08 +# define ROSEL_NORMAL (2) /* RNG only works in this mode */ + +/* Number of bits to collect for randomness tests. */ +#define TEST_INPUT_BITS (128) + +static void rng_writeb(unsigned int offset, uint8_t value) +{ + writeb(RNG_BASE_ADDR + offset, value); +} + +static uint8_t rng_readb(unsigned int offset) +{ + return readb(RNG_BASE_ADDR + offset); +} + +/* Disable RNG and set normal ring oscillator mode. */ +static void rng_reset(void) +{ + rng_writeb(RNGCS, 0); + rng_writeb(RNGMODE, ROSEL_NORMAL); +} + +/* Reset RNG and then enable it. */ +static void rng_reset_enable(void) +{ + rng_reset(); + rng_writeb(RNGCS, RNGE); +} + +/* Wait until Data Valid bit is set. */ +static bool rng_wait_ready(void) +{ + /* qemu_guest_getrandom may fail. Assume it won't fail 10 times in a row. */ + int retries = 10; + + while (retries-- > 0) { + if (rng_readb(RNGCS) & DVALID) { + return true; + } + } + + return false; +} + +/* + * Perform a frequency (monobit) test, as defined by NIST SP 800-22, on the + * sequence in buf and return the P-value. This represents the probability of a + * truly random sequence having the same proportion of zeros and ones as the + * sequence in buf. + * + * An RNG which always returns 0x00 or 0xff, or has some bits stuck at 0 or 1, + * will fail this test. However, an RNG which always returns 0x55, 0xf0 or some + * other value with an equal number of zeroes and ones will pass. + */ +static double calc_monobit_p(const uint8_t *buf, unsigned int len) +{ + unsigned int i; + double s_obs; + int sn = 0; + + for (i = 0; i < len; i++) { + /* + * Each 1 counts as 1, each 0 counts as -1. + * s = cp - (8 - cp) = 2 * cp - 8 + */ + sn += 2 * ctpop8(buf[i]) - 8; + } + + s_obs = abs(sn) / sqrt(len * BITS_PER_BYTE); + + return erfc(s_obs / sqrt(2)); +} + +/* + * Perform a runs test, as defined by NIST SP 800-22, and return the P-value. + * This represents the probability of a truly random sequence having the same + * number of runs (i.e. uninterrupted sequences of identical bits) as the + * sequence in buf. + */ +static double calc_runs_p(const unsigned long *buf, unsigned int nr_bits) +{ + unsigned int j; + unsigned int k; + int nr_ones = 0; + int vn_obs = 0; + double pi; + + g_assert(nr_bits % BITS_PER_LONG == 0); + + for (j = 0; j < nr_bits / BITS_PER_LONG; j++) { + nr_ones += __builtin_popcountl(buf[j]); + } + pi = (double)nr_ones / nr_bits; + + for (k = 0; k < nr_bits - 1; k++) { + vn_obs += !(test_bit(k, buf) ^ test_bit(k + 1, buf)); + } + vn_obs += 1; + + return erfc(fabs(vn_obs - 2 * nr_bits * pi * (1.0 - pi)) + / (2 * sqrt(2 * nr_bits) * pi * (1.0 - pi))); +} + +/* + * Verifies that DVALID is clear, and RNGD reads zero, when RNGE is cleared, + * and DVALID eventually becomes set when RNGE is set. + */ +static void test_enable_disable(void) +{ + /* Disable: DVALID should not be set, and RNGD should read zero */ + rng_reset(); + g_assert_cmphex(rng_readb(RNGCS), ==, 0); + g_assert_cmphex(rng_readb(RNGD), ==, 0); + + /* Enable: DVALID should be set, but we can't make assumptions about RNGD */ + rng_writeb(RNGCS, RNGE); + g_assert_true(rng_wait_ready()); + g_assert_cmphex(rng_readb(RNGCS), ==, DVALID | RNGE); + + /* Disable: DVALID should not be set, and RNGD should read zero */ + rng_writeb(RNGCS, 0); + g_assert_cmphex(rng_readb(RNGCS), ==, 0); + g_assert_cmphex(rng_readb(RNGD), ==, 0); +} + +/* + * Verifies that the RNG only produces data when RNGMODE is set to 'normal' + * ring oscillator mode. + */ +static void test_rosel(void) +{ + rng_reset_enable(); + g_assert_true(rng_wait_ready()); + rng_writeb(RNGMODE, 0); + g_assert_false(rng_wait_ready()); + rng_writeb(RNGMODE, ROSEL_NORMAL); + g_assert_true(rng_wait_ready()); + rng_writeb(RNGMODE, 0); + g_assert_false(rng_wait_ready()); +} + +/* + * Verifies that a continuous sequence of bits collected after enabling the RNG + * satisfies a monobit test. + */ +static void test_continuous_monobit(void) +{ + uint8_t buf[TEST_INPUT_BITS / BITS_PER_BYTE]; + unsigned int i; + + rng_reset_enable(); + for (i = 0; i < sizeof(buf); i++) { + g_assert_true(rng_wait_ready()); + buf[i] = rng_readb(RNGD); + } + + g_assert_cmpfloat(calc_monobit_p(buf, sizeof(buf)), >, 0.01); +} + +/* + * Verifies that a continuous sequence of bits collected after enabling the RNG + * satisfies a runs test. + */ +static void test_continuous_runs(void) +{ + union { + unsigned long l[TEST_INPUT_BITS / BITS_PER_LONG]; + uint8_t c[TEST_INPUT_BITS / BITS_PER_BYTE]; + } buf; + unsigned int i; + + rng_reset_enable(); + for (i = 0; i < sizeof(buf); i++) { + g_assert_true(rng_wait_ready()); + buf.c[i] = rng_readb(RNGD); + } + + g_assert_cmpfloat(calc_runs_p(buf.l, sizeof(buf) * BITS_PER_BYTE), >, 0.01); +} + +/* + * Verifies that the first data byte collected after enabling the RNG satisfies + * a monobit test. + */ +static void test_first_byte_monobit(void) +{ + /* Enable, collect one byte, disable. Repeat until we have 100 bits. */ + uint8_t buf[TEST_INPUT_BITS / BITS_PER_BYTE]; + unsigned int i; + + rng_reset(); + for (i = 0; i < sizeof(buf); i++) { + rng_writeb(RNGCS, RNGE); + g_assert_true(rng_wait_ready()); + buf[i] = rng_readb(RNGD); + rng_writeb(RNGCS, 0); + } + + g_assert_cmpfloat(calc_monobit_p(buf, sizeof(buf)), >, 0.01); +} + +/* + * Verifies that the first data byte collected after enabling the RNG satisfies + * a runs test. + */ +static void test_first_byte_runs(void) +{ + /* Enable, collect one byte, disable. Repeat until we have 100 bits. */ + union { + unsigned long l[TEST_INPUT_BITS / BITS_PER_LONG]; + uint8_t c[TEST_INPUT_BITS / BITS_PER_BYTE]; + } buf; + unsigned int i; + + rng_reset(); + for (i = 0; i < sizeof(buf); i++) { + rng_writeb(RNGCS, RNGE); + g_assert_true(rng_wait_ready()); + buf.c[i] = rng_readb(RNGD); + rng_writeb(RNGCS, 0); + } + + g_assert_cmpfloat(calc_runs_p(buf.l, sizeof(buf) * BITS_PER_BYTE), >, 0.01); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + g_test_set_nonfatal_assertions(); + + qtest_add_func("npcm7xx_rng/enable_disable", test_enable_disable); + qtest_add_func("npcm7xx_rng/rosel", test_rosel); + qtest_add_func("npcm7xx_rng/continuous/monobit", test_continuous_monobit); + qtest_add_func("npcm7xx_rng/continuous/runs", test_continuous_runs); + qtest_add_func("npcm7xx_rng/first_byte/monobit", test_first_byte_monobit); + qtest_add_func("npcm7xx_rng/first_byte/runs", test_first_byte_runs); + + qtest_start("-machine npcm750-evb"); + ret = g_test_run(); + qtest_end(); + + return ret; +} diff --git a/tests/qtest/npcm7xx_watchdog_timer-test.c b/tests/qtest/npcm7xx_watchdog_timer-test.c new file mode 100644 index 0000000000..54d5d6d8f2 --- /dev/null +++ b/tests/qtest/npcm7xx_watchdog_timer-test.c @@ -0,0 +1,319 @@ +/* + * QTests for Nuvoton NPCM7xx Timer Watchdog Modules. + * + * Copyright 2020 Google LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "qemu/osdep.h" +#include "qemu/timer.h" + +#include "libqos/libqtest.h" +#include "qapi/qmp/qdict.h" + +#define WTCR_OFFSET 0x1c +#define REF_HZ (25000000) + +/* WTCR bit fields */ +#define WTCLK(rv) ((rv) << 10) +#define WTE BIT(7) +#define WTIE BIT(6) +#define WTIS(rv) ((rv) << 4) +#define WTIF BIT(3) +#define WTRF BIT(2) +#define WTRE BIT(1) +#define WTR BIT(0) + +typedef struct Watchdog { + int irq; + uint64_t base_addr; +} Watchdog; + +static const Watchdog watchdog_list[] = { + { + .irq = 47, + .base_addr = 0xf0008000 + }, + { + .irq = 48, + .base_addr = 0xf0009000 + }, + { + .irq = 49, + .base_addr = 0xf000a000 + } +}; + +static int watchdog_index(const Watchdog *wd) +{ + ptrdiff_t diff = wd - watchdog_list; + + g_assert(diff >= 0 && diff < ARRAY_SIZE(watchdog_list)); + + return diff; +} + +static uint32_t watchdog_read_wtcr(QTestState *qts, const Watchdog *wd) +{ + return qtest_readl(qts, wd->base_addr + WTCR_OFFSET); +} + +static void watchdog_write_wtcr(QTestState *qts, const Watchdog *wd, + uint32_t value) +{ + qtest_writel(qts, wd->base_addr + WTCR_OFFSET, value); +} + +static uint32_t watchdog_prescaler(QTestState *qts, const Watchdog *wd) +{ + switch (extract32(watchdog_read_wtcr(qts, wd), 10, 2)) { + case 0: + return 1; + case 1: + return 256; + case 2: + return 2048; + case 3: + return 65536; + default: + g_assert_not_reached(); + } +} + +static QDict *get_watchdog_action(QTestState *qts) +{ + QDict *ev = qtest_qmp_eventwait_ref(qts, "WATCHDOG"); + QDict *data; + + data = qdict_get_qdict(ev, "data"); + qobject_ref(data); + qobject_unref(ev); + return data; +} + +#define RESET_CYCLES 1024 +static uint32_t watchdog_interrupt_cycles(QTestState *qts, const Watchdog *wd) +{ + uint32_t wtis = extract32(watchdog_read_wtcr(qts, wd), 4, 2); + return 1 << (14 + 2 * wtis); +} + +static int64_t watchdog_calculate_steps(uint32_t count, uint32_t prescale) +{ + return (NANOSECONDS_PER_SECOND / REF_HZ) * count * prescale; +} + +static int64_t watchdog_interrupt_steps(QTestState *qts, const Watchdog *wd) +{ + return watchdog_calculate_steps(watchdog_interrupt_cycles(qts, wd), + watchdog_prescaler(qts, wd)); +} + +/* Check wtcr can be reset to default value */ +static void test_init(gconstpointer watchdog) +{ + const Watchdog *wd = watchdog; + QTestState *qts = qtest_init("-machine quanta-gsj"); + + qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); + + watchdog_write_wtcr(qts, wd, WTCLK(1) | WTRF | WTIF | WTR); + g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(1)); + + qtest_quit(qts); +} + +/* Check a watchdog can generate interrupt and reset actions */ +static void test_reset_action(gconstpointer watchdog) +{ + const Watchdog *wd = watchdog; + QTestState *qts = qtest_init("-machine quanta-gsj"); + QDict *ad; + + qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); + + watchdog_write_wtcr(qts, wd, + WTCLK(0) | WTE | WTRF | WTRE | WTIF | WTIE | WTR); + g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, + WTCLK(0) | WTE | WTRE | WTIE); + + /* Check a watchdog can generate an interrupt */ + qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd)); + g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, + WTCLK(0) | WTE | WTIF | WTIE | WTRE); + g_assert_true(qtest_get_irq(qts, wd->irq)); + + /* Check a watchdog can generate a reset signal */ + qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES, + watchdog_prescaler(qts, wd))); + ad = get_watchdog_action(qts); + /* The signal is a reset signal */ + g_assert_false(strcmp(qdict_get_str(ad, "action"), "reset")); + qobject_unref(ad); + qtest_qmp_eventwait(qts, "RESET"); + /* + * Make sure WTCR is reset to default except for WTRF bit which shouldn't + * be reset. + */ + g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(1) | WTRF); + qtest_quit(qts); +} + +/* Check a watchdog works with all possible WTCLK prescalers and WTIS cycles */ +static void test_prescaler(gconstpointer watchdog) +{ + const Watchdog *wd = watchdog; + + for (int wtclk = 0; wtclk < 4; ++wtclk) { + for (int wtis = 0; wtis < 4; ++wtis) { + QTestState *qts = qtest_init("-machine quanta-gsj"); + + qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); + watchdog_write_wtcr(qts, wd, + WTCLK(wtclk) | WTE | WTIF | WTIS(wtis) | WTIE | WTR); + /* + * The interrupt doesn't fire until watchdog_interrupt_steps() + * cycles passed + */ + qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd) - 1); + g_assert_false(watchdog_read_wtcr(qts, wd) & WTIF); + g_assert_false(qtest_get_irq(qts, wd->irq)); + qtest_clock_step(qts, 1); + g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); + g_assert_true(qtest_get_irq(qts, wd->irq)); + + qtest_quit(qts); + } + } +} + +/* + * Check a watchdog doesn't fire if corresponding flags (WTIE and WTRE) are not + * set. + */ +static void test_enabling_flags(gconstpointer watchdog) +{ + const Watchdog *wd = watchdog; + QTestState *qts; + + /* Neither WTIE or WTRE is set, no interrupt or reset should happen */ + qts = qtest_init("-machine quanta-gsj"); + qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); + watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTRF | WTR); + qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd)); + g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); + g_assert_false(qtest_get_irq(qts, wd->irq)); + qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES, + watchdog_prescaler(qts, wd))); + g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); + g_assert_false(watchdog_read_wtcr(qts, wd) & WTRF); + qtest_quit(qts); + + /* Only WTIE is set, interrupt is triggered but reset should not happen */ + qts = qtest_init("-machine quanta-gsj"); + qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); + watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTIE | WTRF | WTR); + qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd)); + g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); + g_assert_true(qtest_get_irq(qts, wd->irq)); + qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES, + watchdog_prescaler(qts, wd))); + g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); + g_assert_false(watchdog_read_wtcr(qts, wd) & WTRF); + qtest_quit(qts); + + /* Only WTRE is set, interrupt is triggered but reset should not happen */ + qts = qtest_init("-machine quanta-gsj"); + qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); + watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTRE | WTRF | WTR); + qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd)); + g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); + g_assert_false(qtest_get_irq(qts, wd->irq)); + qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES, + watchdog_prescaler(qts, wd))); + g_assert_false(strcmp(qdict_get_str(get_watchdog_action(qts), "action"), + "reset")); + qtest_qmp_eventwait(qts, "RESET"); + qtest_quit(qts); + + /* + * The case when both flags are set is already tested in + * test_reset_action(). + */ +} + +/* Check a watchdog can pause and resume by setting WTE bits */ +static void test_pause(gconstpointer watchdog) +{ + const Watchdog *wd = watchdog; + QTestState *qts; + int64_t remaining_steps, steps; + + qts = qtest_init("-machine quanta-gsj"); + qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); + watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTIE | WTRF | WTR); + remaining_steps = watchdog_interrupt_steps(qts, wd); + g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTE | WTIE); + + /* Run for half of the execution period. */ + steps = remaining_steps / 2; + remaining_steps -= steps; + qtest_clock_step(qts, steps); + + /* Pause the watchdog */ + watchdog_write_wtcr(qts, wd, WTCLK(0) | WTIE); + g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTIE); + + /* Run for a long period of time, the watchdog shouldn't fire */ + qtest_clock_step(qts, steps << 4); + g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTIE); + g_assert_false(qtest_get_irq(qts, wd->irq)); + + /* Resume the watchdog */ + watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIE); + g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTE | WTIE); + + /* Run for the reset of the execution period, the watchdog should fire */ + qtest_clock_step(qts, remaining_steps); + g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, + WTCLK(0) | WTE | WTIF | WTIE); + g_assert_true(qtest_get_irq(qts, wd->irq)); + + qtest_quit(qts); +} + +static void watchdog_add_test(const char *name, const Watchdog* wd, + GTestDataFunc fn) +{ + g_autofree char *full_name = g_strdup_printf( + "npcm7xx_watchdog_timer[%d]/%s", watchdog_index(wd), name); + qtest_add_data_func(full_name, wd, fn); +} +#define add_test(name, td) watchdog_add_test(#name, td, test_##name) + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_set_nonfatal_assertions(); + + for (int i = 0; i < ARRAY_SIZE(watchdog_list); ++i) { + const Watchdog *wd = &watchdog_list[i]; + + add_test(init, wd); + add_test(reset_action, wd); + add_test(prescaler, wd); + add_test(enabling_flags, wd); + add_test(pause, wd); + } + + return g_test_run(); +} diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c index 8a4c570e83..1c7186e53c 100644 --- a/tests/qtest/qmp-cmd-test.c +++ b/tests/qtest/qmp-cmd-test.c @@ -31,6 +31,9 @@ static int query_error_class(const char *cmd) #ifndef CONFIG_SPICE { "query-spice", ERROR_CLASS_COMMAND_NOT_FOUND }, #endif +#ifndef CONFIG_TCG + { "query-replay", ERROR_CLASS_COMMAND_NOT_FOUND }, +#endif #ifndef CONFIG_VNC { "query-vnc", ERROR_CLASS_GENERIC_ERROR }, { "query-vnc-servers", ERROR_CLASS_GENERIC_ERROR }, diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index e7249915e7..d7d33e293c 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -25,6 +25,16 @@ run-pauth-%: QEMU_OPTS += -cpu max run-plugin-pauth-%: QEMU_OPTS += -cpu max endif +# BTI Tests +# bti-1 tests the elf notes, so we require special compiler support. +ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_ARMV8_BTI),) +AARCH64_TESTS += bti-1 +bti-1: CFLAGS += -mbranch-protection=standard +bti-1: LDFLAGS += -nostdlib +endif +# bti-2 tests PROT_BTI, so no special compiler support required. +AARCH64_TESTS += bti-2 + # Semihosting smoke test for linux-user AARCH64_TESTS += semihosting run-semihosting: semihosting diff --git a/tests/tcg/aarch64/bti-1.c b/tests/tcg/aarch64/bti-1.c new file mode 100644 index 0000000000..61924f0d7a --- /dev/null +++ b/tests/tcg/aarch64/bti-1.c @@ -0,0 +1,62 @@ +/* + * Branch target identification, basic notskip cases. + */ + +#include "bti-crt.inc.c" + +static void skip2_sigill(int sig, siginfo_t *info, ucontext_t *uc) +{ + uc->uc_mcontext.pc += 8; + uc->uc_mcontext.pstate = 1; +} + +#define NOP "nop" +#define BTI_N "hint #32" +#define BTI_C "hint #34" +#define BTI_J "hint #36" +#define BTI_JC "hint #38" + +#define BTYPE_1(DEST) \ + asm("mov %0,#1; adr x16, 1f; br x16; 1: " DEST "; mov %0,#0" \ + : "=r"(skipped) : : "x16") + +#define BTYPE_2(DEST) \ + asm("mov %0,#1; adr x16, 1f; blr x16; 1: " DEST "; mov %0,#0" \ + : "=r"(skipped) : : "x16", "x30") + +#define BTYPE_3(DEST) \ + asm("mov %0,#1; adr x15, 1f; br x15; 1: " DEST "; mov %0,#0" \ + : "=r"(skipped) : : "x15") + +#define TEST(WHICH, DEST, EXPECT) \ + do { WHICH(DEST); fail += skipped ^ EXPECT; } while (0) + + +int main() +{ + int fail = 0; + int skipped; + + /* Signal-like with SA_SIGINFO. */ + signal_info(SIGILL, skip2_sigill); + + TEST(BTYPE_1, NOP, 1); + TEST(BTYPE_1, BTI_N, 1); + TEST(BTYPE_1, BTI_C, 0); + TEST(BTYPE_1, BTI_J, 0); + TEST(BTYPE_1, BTI_JC, 0); + + TEST(BTYPE_2, NOP, 1); + TEST(BTYPE_2, BTI_N, 1); + TEST(BTYPE_2, BTI_C, 0); + TEST(BTYPE_2, BTI_J, 1); + TEST(BTYPE_2, BTI_JC, 0); + + TEST(BTYPE_3, NOP, 1); + TEST(BTYPE_3, BTI_N, 1); + TEST(BTYPE_3, BTI_C, 1); + TEST(BTYPE_3, BTI_J, 0); + TEST(BTYPE_3, BTI_JC, 0); + + return fail; +} diff --git a/tests/tcg/aarch64/bti-2.c b/tests/tcg/aarch64/bti-2.c new file mode 100644 index 0000000000..65e8e857dd --- /dev/null +++ b/tests/tcg/aarch64/bti-2.c @@ -0,0 +1,116 @@ +/* + * Branch target identification, basic notskip cases. + */ + +#include <stdio.h> +#include <signal.h> +#include <string.h> +#include <unistd.h> +#include <sys/mman.h> + +#ifndef PROT_BTI +#define PROT_BTI 0x10 +#endif + +static void skip2_sigill(int sig, siginfo_t *info, void *vuc) +{ + ucontext_t *uc = vuc; + uc->uc_mcontext.pc += 8; + uc->uc_mcontext.pstate = 1; +} + +#define NOP "nop" +#define BTI_N "hint #32" +#define BTI_C "hint #34" +#define BTI_J "hint #36" +#define BTI_JC "hint #38" + +#define BTYPE_1(DEST) \ + "mov x1, #1\n\t" \ + "adr x16, 1f\n\t" \ + "br x16\n" \ +"1: " DEST "\n\t" \ + "mov x1, #0" + +#define BTYPE_2(DEST) \ + "mov x1, #1\n\t" \ + "adr x16, 1f\n\t" \ + "blr x16\n" \ +"1: " DEST "\n\t" \ + "mov x1, #0" + +#define BTYPE_3(DEST) \ + "mov x1, #1\n\t" \ + "adr x15, 1f\n\t" \ + "br x15\n" \ +"1: " DEST "\n\t" \ + "mov x1, #0" + +#define TEST(WHICH, DEST, EXPECT) \ + WHICH(DEST) "\n" \ + ".if " #EXPECT "\n\t" \ + "eor x1, x1," #EXPECT "\n" \ + ".endif\n\t" \ + "add x0, x0, x1\n\t" + +asm("\n" +"test_begin:\n\t" + BTI_C "\n\t" + "mov x2, x30\n\t" + "mov x0, #0\n\t" + + TEST(BTYPE_1, NOP, 1) + TEST(BTYPE_1, BTI_N, 1) + TEST(BTYPE_1, BTI_C, 0) + TEST(BTYPE_1, BTI_J, 0) + TEST(BTYPE_1, BTI_JC, 0) + + TEST(BTYPE_2, NOP, 1) + TEST(BTYPE_2, BTI_N, 1) + TEST(BTYPE_2, BTI_C, 0) + TEST(BTYPE_2, BTI_J, 1) + TEST(BTYPE_2, BTI_JC, 0) + + TEST(BTYPE_3, NOP, 1) + TEST(BTYPE_3, BTI_N, 1) + TEST(BTYPE_3, BTI_C, 1) + TEST(BTYPE_3, BTI_J, 0) + TEST(BTYPE_3, BTI_JC, 0) + + "ret x2\n" +"test_end:" +); + +int main() +{ + struct sigaction sa; + void *tb, *te; + + void *p = mmap(0, getpagesize(), + PROT_EXEC | PROT_READ | PROT_WRITE | PROT_BTI, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (p == MAP_FAILED) { + perror("mmap"); + return 1; + } + + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = skip2_sigill; + sa.sa_flags = SA_SIGINFO; + if (sigaction(SIGILL, &sa, NULL) < 0) { + perror("sigaction"); + return 1; + } + + /* + * ??? With "extern char test_begin[]", some compiler versions + * will use :got references, and some linker versions will + * resolve this reference to a static symbol incorrectly. + * Bypass this error by using a pc-relative reference directly. + */ + asm("adr %0, test_begin; adr %1, test_end" : "=r"(tb), "=r"(te)); + + memcpy(p, tb, te - tb); + + return ((int (*)(void))p)(); +} diff --git a/tests/tcg/aarch64/bti-crt.inc.c b/tests/tcg/aarch64/bti-crt.inc.c new file mode 100644 index 0000000000..47805f4e35 --- /dev/null +++ b/tests/tcg/aarch64/bti-crt.inc.c @@ -0,0 +1,51 @@ +/* + * Minimal user-environment for testing BTI. + * + * Normal libc is not (yet) built with BTI support enabled, + * and so could generate a BTI TRAP before ever reaching main. + */ + +#include <stdlib.h> +#include <signal.h> +#include <ucontext.h> +#include <asm/unistd.h> + +int main(void); + +void _start(void) +{ + exit(main()); +} + +void exit(int ret) +{ + register int x0 __asm__("x0") = ret; + register int x8 __asm__("x8") = __NR_exit; + + asm volatile("svc #0" : : "r"(x0), "r"(x8)); + __builtin_unreachable(); +} + +/* + * Irritatingly, the user API struct sigaction does not match the + * kernel API struct sigaction. So for simplicity, isolate the + * kernel ABI here, and make this act like signal. + */ +void signal_info(int sig, void (*fn)(int, siginfo_t *, ucontext_t *)) +{ + struct kernel_sigaction { + void (*handler)(int, siginfo_t *, ucontext_t *); + unsigned long flags; + unsigned long restorer; + unsigned long mask; + } sa = { fn, SA_SIGINFO, 0, 0 }; + + register int x0 __asm__("x0") = sig; + register void *x1 __asm__("x1") = &sa; + register void *x2 __asm__("x2") = 0; + register int x3 __asm__("x3") = sizeof(unsigned long); + register int x8 __asm__("x8") = __NR_rt_sigaction; + + asm volatile("svc #0" + : : "r"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x8) : "memory"); +} diff --git a/tests/tcg/configure.sh b/tests/tcg/configure.sh index be51bdb5a4..e1b70e25f2 100755 --- a/tests/tcg/configure.sh +++ b/tests/tcg/configure.sh @@ -240,6 +240,10 @@ for target in $target_list; do -march=armv8.3-a -o $TMPE $TMPC; then echo "CROSS_CC_HAS_ARMV8_3=y" >> $config_target_mak fi + if do_compiler "$target_compiler" $target_compiler_cflags \ + -mbranch-protection=standard -o $TMPE $TMPC; then + echo "CROSS_CC_HAS_ARMV8_BTI=y" >> $config_target_mak + fi ;; esac diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 1595bbc92e..8a29e33e00 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -594,6 +594,7 @@ static void test_graph_change_drain_all(void) g_assert_cmpint(bs_b->quiesce_counter, ==, 0); g_assert_cmpint(b_s->drain_count, ==, 0); + g_assert_cmpint(qemu_get_aio_context()->external_disable_cnt, ==, 0); bdrv_unref(bs_b); blk_unref(blk_b); diff --git a/tests/vhost-user-bridge.c b/tests/vhost-user-bridge.c index 6c3d490611..bd43607a4d 100644 --- a/tests/vhost-user-bridge.c +++ b/tests/vhost-user-bridge.c @@ -520,6 +520,7 @@ vubr_accept_cb(int sock, void *ctx) VHOST_USER_BRIDGE_MAX_QUEUES, conn_fd, vubr_panic, + NULL, vubr_set_watch, vubr_remove_watch, &vuiface)) { @@ -573,6 +574,7 @@ vubr_new(const char *path, bool client) VHOST_USER_BRIDGE_MAX_QUEUES, dev->sock, vubr_panic, + NULL, vubr_set_watch, vubr_remove_watch, &vuiface)) { diff --git a/tools/meson.build b/tools/meson.build index 513bd2ff4f..76bf84df52 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -1,10 +1,23 @@ -have_virtiofsd = (have_system and +have_virtiofsd = (targetos == 'linux' and have_tools and - 'CONFIG_LINUX' in config_host and 'CONFIG_SECCOMP' in config_host and 'CONFIG_LIBCAP_NG' in config_host and 'CONFIG_VHOST_USER' in config_host) +if get_option('virtiofsd').enabled() + if not have_virtiofsd + if targetos != 'linux' + error('virtiofsd requires Linux') + elif 'CONFIG_SECCOMP' not in config_host or 'CONFIG_LIBCAP_NG' not in config_host + error('virtiofsd requires libcap-ng-devel and seccomp-devel') + elif not have_tools or 'CONFIG_VHOST_USER' not in config_host + error('virtiofsd needs tools and vhost-user support') + endif + endif +elif get_option('virtiofsd').disabled() or not have_system + have_virtiofsd = false +endif + if have_virtiofsd subdir('virtiofsd') endif diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c index 89f537f79b..324936948d 100644 --- a/tools/virtiofsd/fuse_virtio.c +++ b/tools/virtiofsd/fuse_virtio.c @@ -1013,8 +1013,8 @@ int virtio_session_mount(struct fuse_session *se) se->vu_socketfd = data_sock; se->virtio_dev->se = se; pthread_rwlock_init(&se->virtio_dev->vu_dispatch_rwlock, NULL); - vu_init(&se->virtio_dev->dev, 2, se->vu_socketfd, fv_panic, fv_set_watch, - fv_remove_watch, &fv_iface); + vu_init(&se->virtio_dev->dev, 2, se->vu_socketfd, fv_panic, NULL, + fv_set_watch, fv_remove_watch, &fv_iface); return 0; } diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c index 85770d63f1..2e181a49b5 100644 --- a/tools/virtiofsd/helper.c +++ b/tools/virtiofsd/helper.c @@ -166,6 +166,14 @@ void fuse_cmdline_help(void) " enable/disable readirplus\n" " default: readdirplus except with " "cache=none\n" + " -o sandbox=namespace|chroot\n" + " sandboxing mode:\n" + " - namespace: mount, pid, and net\n" + " namespaces with pivot_root(2)\n" + " into shared directory\n" + " - chroot: chroot(2) into shared\n" + " directory (use in containers)\n" + " default: namespace\n" " -o timeout=<number> I/O timeout (seconds)\n" " default: depends on cache= option.\n" " -o writeback|no_writeback enable/disable writeback cache\n" diff --git a/tools/virtiofsd/meson.build b/tools/virtiofsd/meson.build index 50022ed89e..e1a4dc98d9 100644 --- a/tools/virtiofsd/meson.build +++ b/tools/virtiofsd/meson.build @@ -15,5 +15,5 @@ executable('virtiofsd', files( configure_file(input: '50-qemu-virtiofsd.json.in', output: '50-qemu-virtiofsd.json', - configuration: config_host, + configuration: { 'libexecdir' : get_option('libexecdir') }, install_dir: qemu_datadir / 'vhost-user') diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index ff53df4451..a0beb986f3 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -64,6 +64,7 @@ #include <syslog.h> #include <unistd.h> +#include "qemu/cutils.h" #include "passthrough_helpers.h" #include "passthrough_seccomp.h" @@ -137,13 +138,26 @@ enum { CACHE_ALWAYS, }; +enum { + SANDBOX_NAMESPACE, + SANDBOX_CHROOT, +}; + +typedef struct xattr_map_entry { + char *key; + char *prepend; + unsigned int flags; +} XattrMapEntry; + struct lo_data { pthread_mutex_t mutex; + int sandbox; int debug; int writeback; int flock; int posix_lock; int xattr; + char *xattrmap; char *source; char *modcaps; double timeout; @@ -157,12 +171,20 @@ struct lo_data { struct lo_map ino_map; /* protected by lo->mutex */ struct lo_map dirp_map; /* protected by lo->mutex */ struct lo_map fd_map; /* protected by lo->mutex */ + XattrMapEntry *xattr_map_list; + size_t xattr_map_nentries; /* An O_PATH file descriptor to /proc/self/fd/ */ int proc_self_fd; }; static const struct fuse_opt lo_opts[] = { + { "sandbox=namespace", + offsetof(struct lo_data, sandbox), + SANDBOX_NAMESPACE }, + { "sandbox=chroot", + offsetof(struct lo_data, sandbox), + SANDBOX_CHROOT }, { "writeback", offsetof(struct lo_data, writeback), 1 }, { "no_writeback", offsetof(struct lo_data, writeback), 0 }, { "source=%s", offsetof(struct lo_data, source), 0 }, @@ -172,6 +194,7 @@ static const struct fuse_opt lo_opts[] = { { "no_posix_lock", offsetof(struct lo_data, posix_lock), 0 }, { "xattr", offsetof(struct lo_data, xattr), 1 }, { "no_xattr", offsetof(struct lo_data, xattr), 0 }, + { "xattrmap=%s", offsetof(struct lo_data, xattrmap), 0 }, { "modcaps=%s", offsetof(struct lo_data, modcaps), 0 }, { "timeout=%lf", offsetof(struct lo_data, timeout), 0 }, { "timeout=", offsetof(struct lo_data, timeout_set), 1 }, @@ -2010,20 +2033,383 @@ static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, fuse_reply_err(req, res == -1 ? errno : 0); } -static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, +/* types */ +/* + * Exit; process attribute unmodified if matched. + * An empty key applies to all. + */ +#define XATTR_MAP_FLAG_OK (1 << 0) +/* + * The attribute is unwanted; + * EPERM on write, hidden on read. + */ +#define XATTR_MAP_FLAG_BAD (1 << 1) +/* + * For attr that start with 'key' prepend 'prepend' + * 'key' may be empty to prepend for all attrs + * key is defined from set/remove point of view. + * Automatically reversed on read + */ +#define XATTR_MAP_FLAG_PREFIX (1 << 2) + +/* scopes */ +/* Apply rule to get/set/remove */ +#define XATTR_MAP_FLAG_CLIENT (1 << 16) +/* Apply rule to list */ +#define XATTR_MAP_FLAG_SERVER (1 << 17) +/* Apply rule to all */ +#define XATTR_MAP_FLAG_ALL (XATTR_MAP_FLAG_SERVER | XATTR_MAP_FLAG_CLIENT) + +static void add_xattrmap_entry(struct lo_data *lo, + const XattrMapEntry *new_entry) +{ + XattrMapEntry *res = g_realloc_n(lo->xattr_map_list, + lo->xattr_map_nentries + 1, + sizeof(XattrMapEntry)); + res[lo->xattr_map_nentries++] = *new_entry; + + lo->xattr_map_list = res; +} + +static void free_xattrmap(struct lo_data *lo) +{ + XattrMapEntry *map = lo->xattr_map_list; + size_t i; + + if (!map) { + return; + } + + for (i = 0; i < lo->xattr_map_nentries; i++) { + g_free(map[i].key); + g_free(map[i].prepend); + }; + + g_free(map); + lo->xattr_map_list = NULL; + lo->xattr_map_nentries = -1; +} + +/* + * Handle the 'map' type, which is sugar for a set of commands + * for the common case of prefixing a subset or everything, + * and allowing anything not prefixed through. + * It must be the last entry in the stream, although there + * can be other entries before it. + * The form is: + * :map:key:prefix: + * + * key maybe empty in which case all entries are prefixed. + */ +static void parse_xattrmap_map(struct lo_data *lo, + const char *rule, char sep) +{ + const char *tmp; + char *key; + char *prefix; + XattrMapEntry tmp_entry; + + if (*rule != sep) { + fuse_log(FUSE_LOG_ERR, + "%s: Expecting '%c' after 'map' keyword, found '%c'\n", + __func__, sep, *rule); + exit(1); + } + + rule++; + + /* At start of 'key' field */ + tmp = strchr(rule, sep); + if (!tmp) { + fuse_log(FUSE_LOG_ERR, + "%s: Missing '%c' at end of key field in map rule\n", + __func__, sep); + exit(1); + } + + key = g_strndup(rule, tmp - rule); + rule = tmp + 1; + + /* At start of prefix field */ + tmp = strchr(rule, sep); + if (!tmp) { + fuse_log(FUSE_LOG_ERR, + "%s: Missing '%c' at end of prefix field in map rule\n", + __func__, sep); + exit(1); + } + + prefix = g_strndup(rule, tmp - rule); + rule = tmp + 1; + + /* + * This should be the end of the string, we don't allow + * any more commands after 'map'. + */ + if (*rule) { + fuse_log(FUSE_LOG_ERR, + "%s: Expecting end of command after map, found '%c'\n", + __func__, *rule); + exit(1); + } + + /* 1st: Prefix matches/everything */ + tmp_entry.flags = XATTR_MAP_FLAG_PREFIX | XATTR_MAP_FLAG_ALL; + tmp_entry.key = g_strdup(key); + tmp_entry.prepend = g_strdup(prefix); + add_xattrmap_entry(lo, &tmp_entry); + + if (!*key) { + /* Prefix all case */ + + /* 2nd: Hide any non-prefixed entries on the host */ + tmp_entry.flags = XATTR_MAP_FLAG_BAD | XATTR_MAP_FLAG_ALL; + tmp_entry.key = g_strdup(""); + tmp_entry.prepend = g_strdup(""); + add_xattrmap_entry(lo, &tmp_entry); + } else { + /* Prefix matching case */ + + /* 2nd: Hide non-prefixed but matching entries on the host */ + tmp_entry.flags = XATTR_MAP_FLAG_BAD | XATTR_MAP_FLAG_SERVER; + tmp_entry.key = g_strdup(""); /* Not used */ + tmp_entry.prepend = g_strdup(key); + add_xattrmap_entry(lo, &tmp_entry); + + /* 3rd: Stop the client accessing prefixed attributes directly */ + tmp_entry.flags = XATTR_MAP_FLAG_BAD | XATTR_MAP_FLAG_CLIENT; + tmp_entry.key = g_strdup(prefix); + tmp_entry.prepend = g_strdup(""); /* Not used */ + add_xattrmap_entry(lo, &tmp_entry); + + /* 4th: Everything else is OK */ + tmp_entry.flags = XATTR_MAP_FLAG_OK | XATTR_MAP_FLAG_ALL; + tmp_entry.key = g_strdup(""); + tmp_entry.prepend = g_strdup(""); + add_xattrmap_entry(lo, &tmp_entry); + } + + g_free(key); + g_free(prefix); +} + +static void parse_xattrmap(struct lo_data *lo) +{ + const char *map = lo->xattrmap; + const char *tmp; + + lo->xattr_map_nentries = 0; + while (*map) { + XattrMapEntry tmp_entry; + char sep; + + if (isspace(*map)) { + map++; + continue; + } + /* The separator is the first non-space of the rule */ + sep = *map++; + if (!sep) { + break; + } + + tmp_entry.flags = 0; + /* Start of 'type' */ + if (strstart(map, "prefix", &map)) { + tmp_entry.flags |= XATTR_MAP_FLAG_PREFIX; + } else if (strstart(map, "ok", &map)) { + tmp_entry.flags |= XATTR_MAP_FLAG_OK; + } else if (strstart(map, "bad", &map)) { + tmp_entry.flags |= XATTR_MAP_FLAG_BAD; + } else if (strstart(map, "map", &map)) { + /* + * map is sugar that adds a number of rules, and must be + * the last entry. + */ + parse_xattrmap_map(lo, map, sep); + return; + } else { + fuse_log(FUSE_LOG_ERR, + "%s: Unexpected type;" + "Expecting 'prefix', 'ok', 'bad' or 'map' in rule %zu\n", + __func__, lo->xattr_map_nentries); + exit(1); + } + + if (*map++ != sep) { + fuse_log(FUSE_LOG_ERR, + "%s: Missing '%c' at end of type field of rule %zu\n", + __func__, sep, lo->xattr_map_nentries); + exit(1); + } + + /* Start of 'scope' */ + if (strstart(map, "client", &map)) { + tmp_entry.flags |= XATTR_MAP_FLAG_CLIENT; + } else if (strstart(map, "server", &map)) { + tmp_entry.flags |= XATTR_MAP_FLAG_SERVER; + } else if (strstart(map, "all", &map)) { + tmp_entry.flags |= XATTR_MAP_FLAG_ALL; + } else { + fuse_log(FUSE_LOG_ERR, + "%s: Unexpected scope;" + " Expecting 'client', 'server', or 'all', in rule %zu\n", + __func__, lo->xattr_map_nentries); + exit(1); + } + + if (*map++ != sep) { + fuse_log(FUSE_LOG_ERR, + "%s: Expecting '%c' found '%c'" + " after scope in rule %zu\n", + __func__, sep, *map, lo->xattr_map_nentries); + exit(1); + } + + /* At start of 'key' field */ + tmp = strchr(map, sep); + if (!tmp) { + fuse_log(FUSE_LOG_ERR, + "%s: Missing '%c' at end of key field of rule %zu", + __func__, sep, lo->xattr_map_nentries); + exit(1); + } + tmp_entry.key = g_strndup(map, tmp - map); + map = tmp + 1; + + /* At start of 'prepend' field */ + tmp = strchr(map, sep); + if (!tmp) { + fuse_log(FUSE_LOG_ERR, + "%s: Missing '%c' at end of prepend field of rule %zu", + __func__, sep, lo->xattr_map_nentries); + exit(1); + } + tmp_entry.prepend = g_strndup(map, tmp - map); + map = tmp + 1; + + add_xattrmap_entry(lo, &tmp_entry); + /* End of rule - go around again for another rule */ + } + + if (!lo->xattr_map_nentries) { + fuse_log(FUSE_LOG_ERR, "Empty xattr map\n"); + exit(1); + } +} + +/* + * For use with getxattr/setxattr/removexattr, where the client + * gives us a name and we may need to choose a different one. + * Allocates a buffer for the result placing it in *out_name. + * If there's no change then *out_name is not set. + * Returns 0 on success + * Can return -EPERM to indicate we block a given attribute + * (in which case out_name is not allocated) + * Can return -ENOMEM to indicate out_name couldn't be allocated. + */ +static int xattr_map_client(const struct lo_data *lo, const char *client_name, + char **out_name) +{ + size_t i; + for (i = 0; i < lo->xattr_map_nentries; i++) { + const XattrMapEntry *cur_entry = lo->xattr_map_list + i; + + if ((cur_entry->flags & XATTR_MAP_FLAG_CLIENT) && + (strstart(client_name, cur_entry->key, NULL))) { + if (cur_entry->flags & XATTR_MAP_FLAG_BAD) { + return -EPERM; + } + if (cur_entry->flags & XATTR_MAP_FLAG_OK) { + /* Unmodified name */ + return 0; + } + if (cur_entry->flags & XATTR_MAP_FLAG_PREFIX) { + *out_name = g_try_malloc(strlen(client_name) + + strlen(cur_entry->prepend) + 1); + if (!*out_name) { + return -ENOMEM; + } + sprintf(*out_name, "%s%s", cur_entry->prepend, client_name); + return 0; + } + } + } + + return -EPERM; +} + +/* + * For use with listxattr where the server fs gives us a name and we may need + * to sanitize this for the client. + * Returns a pointer to the result in *out_name + * This is always the original string or the current string with some prefix + * removed; no reallocation is done. + * Returns 0 on success + * Can return -ENODATA to indicate the name should be dropped from the list. + */ +static int xattr_map_server(const struct lo_data *lo, const char *server_name, + const char **out_name) +{ + size_t i; + const char *end; + + for (i = 0; i < lo->xattr_map_nentries; i++) { + const XattrMapEntry *cur_entry = lo->xattr_map_list + i; + + if ((cur_entry->flags & XATTR_MAP_FLAG_SERVER) && + (strstart(server_name, cur_entry->prepend, &end))) { + if (cur_entry->flags & XATTR_MAP_FLAG_BAD) { + return -ENODATA; + } + if (cur_entry->flags & XATTR_MAP_FLAG_OK) { + *out_name = server_name; + return 0; + } + if (cur_entry->flags & XATTR_MAP_FLAG_PREFIX) { + /* Remove prefix */ + *out_name = end; + return 0; + } + } + } + + return -ENODATA; +} + +static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name, size_t size) { struct lo_data *lo = lo_data(req); char *value = NULL; char procname[64]; + const char *name; + char *mapped_name; struct lo_inode *inode; ssize_t ret; int saverr; int fd = -1; + mapped_name = NULL; + name = in_name; + if (lo->xattrmap) { + ret = xattr_map_client(lo, in_name, &mapped_name); + if (ret < 0) { + if (ret == -EPERM) { + ret = -ENODATA; + } + fuse_reply_err(req, -ret); + return; + } + if (mapped_name) { + name = mapped_name; + } + } + inode = lo_inode(req, ino); if (!inode) { fuse_reply_err(req, EBADF); + g_free(mapped_name); return; } @@ -2088,6 +2474,7 @@ out_err: saverr = errno; out: fuse_reply_err(req, saverr); + g_free(mapped_name); goto out_free; } @@ -2144,8 +2531,60 @@ static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) if (ret == 0) { goto out; } + + if (lo->xattr_map_list) { + /* + * Map the names back, some attributes might be dropped, + * some shortened, but not increased, so we shouldn't + * run out of room. + */ + size_t out_index, in_index; + out_index = 0; + in_index = 0; + while (in_index < ret) { + const char *map_out; + char *in_ptr = value + in_index; + /* Length of current attribute name */ + size_t in_len = strlen(value + in_index) + 1; + + int mapret = xattr_map_server(lo, in_ptr, &map_out); + if (mapret != -ENODATA && mapret != 0) { + /* Shouldn't happen */ + saverr = -mapret; + goto out; + } + if (mapret == 0) { + /* Either unchanged, or truncated */ + size_t out_len; + if (map_out != in_ptr) { + /* +1 copies the NIL */ + out_len = strlen(map_out) + 1; + } else { + /* No change */ + out_len = in_len; + } + /* + * Move result along, may still be needed for an unchanged + * entry if a previous entry was changed. + */ + memmove(value + out_index, map_out, out_len); + + out_index += out_len; + } + in_index += in_len; + } + ret = out_index; + if (ret == 0) { + goto out; + } + } fuse_reply_buf(req, value, ret); } else { + /* + * xattrmap only ever shortens the result, + * so we don't need to do anything clever with the + * allocation length here. + */ fuse_reply_xattr(req, ret); } out_free: @@ -2165,19 +2604,35 @@ out: goto out_free; } -static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, +static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name, const char *value, size_t size, int flags) { char procname[64]; + const char *name; + char *mapped_name; struct lo_data *lo = lo_data(req); struct lo_inode *inode; ssize_t ret; int saverr; int fd = -1; + mapped_name = NULL; + name = in_name; + if (lo->xattrmap) { + ret = xattr_map_client(lo, in_name, &mapped_name); + if (ret < 0) { + fuse_reply_err(req, -ret); + return; + } + if (mapped_name) { + name = mapped_name; + } + } + inode = lo_inode(req, ino); if (!inode) { fuse_reply_err(req, EBADF); + g_free(mapped_name); return; } @@ -2212,21 +2667,38 @@ out: } lo_inode_put(lo, &inode); + g_free(mapped_name); fuse_reply_err(req, saverr); } -static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) +static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *in_name) { char procname[64]; + const char *name; + char *mapped_name; struct lo_data *lo = lo_data(req); struct lo_inode *inode; ssize_t ret; int saverr; int fd = -1; + mapped_name = NULL; + name = in_name; + if (lo->xattrmap) { + ret = xattr_map_client(lo, in_name, &mapped_name); + if (ret < 0) { + fuse_reply_err(req, -ret); + return; + } + if (mapped_name) { + name = mapped_name; + } + } + inode = lo_inode(req, ino); if (!inode) { fuse_reply_err(req, EBADF); + g_free(mapped_name); return; } @@ -2261,6 +2733,7 @@ out: } lo_inode_put(lo, &inode); + g_free(mapped_name); fuse_reply_err(req, saverr); } @@ -2661,14 +3134,54 @@ static void setup_capabilities(char *modcaps_in) } /* + * Use chroot as a weaker sandbox for environments where the process is + * launched without CAP_SYS_ADMIN. + */ +static void setup_chroot(struct lo_data *lo) +{ + lo->proc_self_fd = open("/proc/self/fd", O_PATH); + if (lo->proc_self_fd == -1) { + fuse_log(FUSE_LOG_ERR, "open(\"/proc/self/fd\", O_PATH): %m\n"); + exit(1); + } + + /* + * Make the shared directory the file system root so that FUSE_OPEN + * (lo_open()) cannot escape the shared directory by opening a symlink. + * + * The chroot(2) syscall is later disabled by seccomp and the + * CAP_SYS_CHROOT capability is dropped so that tampering with the chroot + * is not possible. + * + * However, it's still possible to escape the chroot via lo->proc_self_fd + * but that requires first gaining control of the process. + */ + if (chroot(lo->source) != 0) { + fuse_log(FUSE_LOG_ERR, "chroot(\"%s\"): %m\n", lo->source); + exit(1); + } + + /* Move into the chroot */ + if (chdir("/") != 0) { + fuse_log(FUSE_LOG_ERR, "chdir(\"/\"): %m\n"); + exit(1); + } +} + +/* * Lock down this process to prevent access to other processes or files outside * source directory. This reduces the impact of arbitrary code execution bugs. */ static void setup_sandbox(struct lo_data *lo, struct fuse_session *se, bool enable_syslog) { - setup_namespaces(lo, se); - setup_mounts(lo->source); + if (lo->sandbox == SANDBOX_NAMESPACE) { + setup_namespaces(lo, se); + setup_mounts(lo->source); + } else { + setup_chroot(lo); + } + setup_seccomp(enable_syslog); setup_capabilities(g_strdup(lo->modcaps)); } @@ -2806,6 +3319,8 @@ static void fuse_lo_data_cleanup(struct lo_data *lo) close(lo->root.fd); } + free(lo->xattrmap); + free_xattrmap(lo); free(lo->source); } @@ -2815,6 +3330,7 @@ int main(int argc, char *argv[]) struct fuse_session *se; struct fuse_cmdline_opts opts; struct lo_data lo = { + .sandbox = SANDBOX_NAMESPACE, .debug = 0, .writeback = 0, .posix_lock = 0, @@ -2878,12 +3394,11 @@ int main(int argc, char *argv[]) goto err_out1; } - /* - * log_level is 0 if not configured via cmd options (0 is LOG_EMERG, - * and we don't use this log level). - */ if (opts.log_level != 0) { current_log_level = opts.log_level; + } else { + /* default log level is INFO */ + current_log_level = FUSE_LOG_INFO; } lo.debug = opts.debug; if (lo.debug) { @@ -2906,6 +3421,11 @@ int main(int argc, char *argv[]) } else { lo.source = strdup("/"); } + + if (lo.xattrmap) { + parse_xattrmap(&lo); + } + if (!lo.timeout_set) { switch (lo.cache) { case CACHE_NONE: diff --git a/trace/control.c b/trace/control.c index b35e512dce..5669db7eea 100644 --- a/trace/control.c +++ b/trace/control.c @@ -39,6 +39,7 @@ static TraceEventGroup *event_groups; static size_t nevent_groups; static uint32_t next_id; static uint32_t next_vcpu_id; +static bool init_trace_on_startup; QemuOptsList qemu_trace_opts = { .name = "trace", @@ -225,7 +226,9 @@ void trace_init_file(const char *file) { #ifdef CONFIG_TRACE_SIMPLE st_set_trace_file(file); - st_set_trace_file_enabled(true); + if (init_trace_on_startup) { + st_set_trace_file_enabled(true); + } #elif defined CONFIG_TRACE_LOG /* * If both the simple and the log backends are enabled, "--trace file" @@ -299,6 +302,7 @@ char *trace_opt_parse(const char *optarg) } trace_init_events(qemu_opt_get(opts, "events")); trace_file = g_strdup(qemu_opt_get(opts, "file")); + init_trace_on_startup = true; qemu_opts_del(opts); return trace_file; diff --git a/ui/icons/meson.build b/ui/icons/meson.build index b6e21f6ad7..12c52080eb 100644 --- a/ui/icons/meson.build +++ b/ui/icons/meson.build @@ -2,12 +2,12 @@ foreach s: [16, 24, 32, 48, 64, 128, 256, 512] s = '@0@x@0@'.format(s.to_string()) install_data('qemu_@0@.png'.format(s), rename: 'qemu.png', - install_dir: config_host['qemu_icondir'] / 'hicolor' / s / 'apps') + install_dir: qemu_icondir / 'hicolor' / s / 'apps') endforeach install_data('qemu_32x32.bmp', rename: 'qemu.bmp', - install_dir: config_host['qemu_icondir'] / 'hicolor' / '32x32' / 'apps') + install_dir: qemu_icondir / 'hicolor' / '32x32' / 'apps') install_data('qemu.svg', - install_dir: config_host['qemu_icondir'] / 'hicolor' / 'scalable' / 'apps') + install_dir: qemu_icondir / 'hicolor' / 'scalable' / 'apps') diff --git a/ui/meson.build b/ui/meson.build index 5d4906c023..013258a01c 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -139,7 +139,7 @@ subdir('shader') if have_system subdir('icons') - install_data('qemu.desktop', install_dir: config_host['qemu_desktopdir']) + install_data('qemu.desktop', install_dir: qemu_desktopdir) endif modules += {'ui': ui_modules} diff --git a/util/block-helpers.c b/util/block-helpers.c new file mode 100644 index 0000000000..c4851432f5 --- /dev/null +++ b/util/block-helpers.c @@ -0,0 +1,46 @@ +/* + * Block utility functions + * + * Copyright IBM, Corp. 2011 + * Copyright (c) 2020 Coiby Xu <coiby.xu@gmail.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qmp/qerror.h" +#include "block-helpers.h" + +/** + * check_block_size: + * @id: The unique ID of the object + * @name: The name of the property being validated + * @value: The block size in bytes + * @errp: A pointer to an area to store an error + * + * This function checks that the block size meets the following conditions: + * 1. At least MIN_BLOCK_SIZE + * 2. No larger than MAX_BLOCK_SIZE + * 3. A power of 2 + */ +void check_block_size(const char *id, const char *name, int64_t value, + Error **errp) +{ + /* value of 0 means "unset" */ + if (value && (value < MIN_BLOCK_SIZE || value > MAX_BLOCK_SIZE)) { + error_setg(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE, + id, name, value, MIN_BLOCK_SIZE, MAX_BLOCK_SIZE); + return; + } + + /* We rely on power-of-2 blocksizes for bitmasks */ + if ((value & (value - 1)) != 0) { + error_setg(errp, + "Property %s.%s doesn't take value '%" PRId64 + "', it's not a power of 2", + id, name, value); + return; + } +} diff --git a/util/block-helpers.h b/util/block-helpers.h new file mode 100644 index 0000000000..b53295a529 --- /dev/null +++ b/util/block-helpers.h @@ -0,0 +1,19 @@ +#ifndef BLOCK_HELPERS_H +#define BLOCK_HELPERS_H + +#include "qemu/units.h" + +/* lower limit is sector size */ +#define MIN_BLOCK_SIZE INT64_C(512) +#define MIN_BLOCK_SIZE_STR "512 B" +/* + * upper limit is arbitrary, 2 MiB looks sufficient for all sensible uses, and + * matches qcow2 cluster size limit + */ +#define MAX_BLOCK_SIZE (2 * MiB) +#define MAX_BLOCK_SIZE_STR "2 MiB" + +void check_block_size(const char *id, const char *name, int64_t value, + Error **errp); + +#endif /* BLOCK_HELPERS_H */ diff --git a/util/cutils.c b/util/cutils.c index be4e43a9ef..c395974fab 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -949,7 +949,7 @@ char *get_relocated_path(const char *dir) bindir += len_bindir; dir = next_component(dir, &len_dir); bindir = next_component(bindir, &len_bindir); - } while (len_dir == len_bindir && !memcmp(dir, bindir, len_dir)); + } while (len_dir && len_dir == len_bindir && !memcmp(dir, bindir, len_dir)); /* Ascend from bindir to the common prefix with dir. */ while (len_bindir) { diff --git a/util/meson.build b/util/meson.build index e6b207a99e..c5159ad79d 100644 --- a/util/meson.build +++ b/util/meson.build @@ -66,6 +66,10 @@ if have_block util_ss.add(files('main-loop.c')) util_ss.add(files('nvdimm-utils.c')) util_ss.add(files('qemu-coroutine.c', 'qemu-coroutine-lock.c', 'qemu-coroutine-io.c')) + util_ss.add(when: ['CONFIG_LINUX', 'CONFIG_VHOST_USER'], if_true: [ + files('vhost-user-server.c'), vhost_user + ]) + util_ss.add(files('block-helpers.c')) util_ss.add(files('qemu-coroutine-sleep.c')) util_ss.add(files('qemu-co-shared-resource.c')) util_ss.add(files('thread-pool.c', 'qemu-timer.c')) diff --git a/util/vhost-user-server.c b/util/vhost-user-server.c new file mode 100644 index 0000000000..783d847a6d --- /dev/null +++ b/util/vhost-user-server.c @@ -0,0 +1,446 @@ +/* + * Sharing QEMU devices via vhost-user protocol + * + * Copyright (c) Coiby Xu <coiby.xu@gmail.com>. + * Copyright (c) 2020 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "qemu/vhost-user-server.h" +#include "block/aio-wait.h" + +/* + * Theory of operation: + * + * VuServer is started and stopped by vhost_user_server_start() and + * vhost_user_server_stop() from the main loop thread. Starting the server + * opens a vhost-user UNIX domain socket and listens for incoming connections. + * Only one connection is allowed at a time. + * + * The connection is handled by the vu_client_trip() coroutine in the + * VuServer->ctx AioContext. The coroutine consists of a vu_dispatch() loop + * where libvhost-user calls vu_message_read() to receive the next vhost-user + * protocol messages over the UNIX domain socket. + * + * When virtqueues are set up libvhost-user calls set_watch() to monitor kick + * fds. These fds are also handled in the VuServer->ctx AioContext. + * + * Both vu_client_trip() and kick fd monitoring can be stopped by shutting down + * the socket connection. Shutting down the socket connection causes + * vu_message_read() to fail since no more data can be received from the socket. + * After vu_dispatch() fails, vu_client_trip() calls vu_deinit() to stop + * libvhost-user before terminating the coroutine. vu_deinit() calls + * remove_watch() to stop monitoring kick fds and this stops virtqueue + * processing. + * + * When vu_client_trip() has finished cleaning up it schedules a BH in the main + * loop thread to accept the next client connection. + * + * When libvhost-user detects an error it calls panic_cb() and sets the + * dev->broken flag. Both vu_client_trip() and kick fd processing stop when + * the dev->broken flag is set. + * + * It is possible to switch AioContexts using + * vhost_user_server_detach_aio_context() and + * vhost_user_server_attach_aio_context(). They stop monitoring fds in the old + * AioContext and resume monitoring in the new AioContext. The vu_client_trip() + * coroutine remains in a yielded state during the switch. This is made + * possible by QIOChannel's support for spurious coroutine re-entry in + * qio_channel_yield(). The coroutine will restart I/O when re-entered from the + * new AioContext. + */ + +static void vmsg_close_fds(VhostUserMsg *vmsg) +{ + int i; + for (i = 0; i < vmsg->fd_num; i++) { + close(vmsg->fds[i]); + } +} + +static void vmsg_unblock_fds(VhostUserMsg *vmsg) +{ + int i; + for (i = 0; i < vmsg->fd_num; i++) { + qemu_set_nonblock(vmsg->fds[i]); + } +} + +static void panic_cb(VuDev *vu_dev, const char *buf) +{ + error_report("vu_panic: %s", buf); +} + +static bool coroutine_fn +vu_message_read(VuDev *vu_dev, int conn_fd, VhostUserMsg *vmsg) +{ + struct iovec iov = { + .iov_base = (char *)vmsg, + .iov_len = VHOST_USER_HDR_SIZE, + }; + int rc, read_bytes = 0; + Error *local_err = NULL; + const size_t max_fds = G_N_ELEMENTS(vmsg->fds); + VuServer *server = container_of(vu_dev, VuServer, vu_dev); + QIOChannel *ioc = server->ioc; + + vmsg->fd_num = 0; + if (!ioc) { + error_report_err(local_err); + goto fail; + } + + assert(qemu_in_coroutine()); + do { + size_t nfds = 0; + int *fds = NULL; + + /* + * qio_channel_readv_full may have short reads, keeping calling it + * until getting VHOST_USER_HDR_SIZE or 0 bytes in total + */ + rc = qio_channel_readv_full(ioc, &iov, 1, &fds, &nfds, &local_err); + if (rc < 0) { + if (rc == QIO_CHANNEL_ERR_BLOCK) { + assert(local_err == NULL); + qio_channel_yield(ioc, G_IO_IN); + continue; + } else { + error_report_err(local_err); + goto fail; + } + } + + if (nfds > 0) { + if (vmsg->fd_num + nfds > max_fds) { + error_report("A maximum of %zu fds are allowed, " + "however got %zu fds now", + max_fds, vmsg->fd_num + nfds); + g_free(fds); + goto fail; + } + memcpy(vmsg->fds + vmsg->fd_num, fds, nfds * sizeof(vmsg->fds[0])); + vmsg->fd_num += nfds; + g_free(fds); + } + + if (rc == 0) { /* socket closed */ + goto fail; + } + + iov.iov_base += rc; + iov.iov_len -= rc; + read_bytes += rc; + } while (read_bytes != VHOST_USER_HDR_SIZE); + + /* qio_channel_readv_full will make socket fds blocking, unblock them */ + vmsg_unblock_fds(vmsg); + if (vmsg->size > sizeof(vmsg->payload)) { + error_report("Error: too big message request: %d, " + "size: vmsg->size: %u, " + "while sizeof(vmsg->payload) = %zu", + vmsg->request, vmsg->size, sizeof(vmsg->payload)); + goto fail; + } + + struct iovec iov_payload = { + .iov_base = (char *)&vmsg->payload, + .iov_len = vmsg->size, + }; + if (vmsg->size) { + rc = qio_channel_readv_all_eof(ioc, &iov_payload, 1, &local_err); + if (rc != 1) { + if (local_err) { + error_report_err(local_err); + } + goto fail; + } + } + + return true; + +fail: + vmsg_close_fds(vmsg); + + return false; +} + +static coroutine_fn void vu_client_trip(void *opaque) +{ + VuServer *server = opaque; + VuDev *vu_dev = &server->vu_dev; + + while (!vu_dev->broken && vu_dispatch(vu_dev)) { + /* Keep running */ + } + + vu_deinit(vu_dev); + + /* vu_deinit() should have called remove_watch() */ + assert(QTAILQ_EMPTY(&server->vu_fd_watches)); + + object_unref(OBJECT(server->sioc)); + server->sioc = NULL; + + object_unref(OBJECT(server->ioc)); + server->ioc = NULL; + + server->co_trip = NULL; + if (server->restart_listener_bh) { + qemu_bh_schedule(server->restart_listener_bh); + } + aio_wait_kick(); +} + +/* + * a wrapper for vu_kick_cb + * + * since aio_dispatch can only pass one user data pointer to the + * callback function, pack VuDev and pvt into a struct. Then unpack it + * and pass them to vu_kick_cb + */ +static void kick_handler(void *opaque) +{ + VuFdWatch *vu_fd_watch = opaque; + VuDev *vu_dev = vu_fd_watch->vu_dev; + + vu_fd_watch->cb(vu_dev, 0, vu_fd_watch->pvt); + + /* Stop vu_client_trip() if an error occurred in vu_fd_watch->cb() */ + if (vu_dev->broken) { + VuServer *server = container_of(vu_dev, VuServer, vu_dev); + + qio_channel_shutdown(server->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); + } +} + +static VuFdWatch *find_vu_fd_watch(VuServer *server, int fd) +{ + + VuFdWatch *vu_fd_watch, *next; + QTAILQ_FOREACH_SAFE(vu_fd_watch, &server->vu_fd_watches, next, next) { + if (vu_fd_watch->fd == fd) { + return vu_fd_watch; + } + } + return NULL; +} + +static void +set_watch(VuDev *vu_dev, int fd, int vu_evt, + vu_watch_cb cb, void *pvt) +{ + + VuServer *server = container_of(vu_dev, VuServer, vu_dev); + g_assert(vu_dev); + g_assert(fd >= 0); + g_assert(cb); + + VuFdWatch *vu_fd_watch = find_vu_fd_watch(server, fd); + + if (!vu_fd_watch) { + VuFdWatch *vu_fd_watch = g_new0(VuFdWatch, 1); + + QTAILQ_INSERT_TAIL(&server->vu_fd_watches, vu_fd_watch, next); + + vu_fd_watch->fd = fd; + vu_fd_watch->cb = cb; + qemu_set_nonblock(fd); + aio_set_fd_handler(server->ioc->ctx, fd, true, kick_handler, + NULL, NULL, vu_fd_watch); + vu_fd_watch->vu_dev = vu_dev; + vu_fd_watch->pvt = pvt; + } +} + + +static void remove_watch(VuDev *vu_dev, int fd) +{ + VuServer *server; + g_assert(vu_dev); + g_assert(fd >= 0); + + server = container_of(vu_dev, VuServer, vu_dev); + + VuFdWatch *vu_fd_watch = find_vu_fd_watch(server, fd); + + if (!vu_fd_watch) { + return; + } + aio_set_fd_handler(server->ioc->ctx, fd, true, NULL, NULL, NULL, NULL); + + QTAILQ_REMOVE(&server->vu_fd_watches, vu_fd_watch, next); + g_free(vu_fd_watch); +} + + +static void vu_accept(QIONetListener *listener, QIOChannelSocket *sioc, + gpointer opaque) +{ + VuServer *server = opaque; + + if (server->sioc) { + warn_report("Only one vhost-user client is allowed to " + "connect the server one time"); + return; + } + + if (!vu_init(&server->vu_dev, server->max_queues, sioc->fd, panic_cb, + vu_message_read, set_watch, remove_watch, server->vu_iface)) { + error_report("Failed to initialize libvhost-user"); + return; + } + + /* + * Unset the callback function for network listener to make another + * vhost-user client keeping waiting until this client disconnects + */ + qio_net_listener_set_client_func(server->listener, + NULL, + NULL, + NULL); + server->sioc = sioc; + /* + * Increase the object reference, so sioc will not freed by + * qio_net_listener_channel_func which will call object_unref(OBJECT(sioc)) + */ + object_ref(OBJECT(server->sioc)); + qio_channel_set_name(QIO_CHANNEL(sioc), "vhost-user client"); + server->ioc = QIO_CHANNEL(sioc); + object_ref(OBJECT(server->ioc)); + + /* TODO vu_message_write() spins if non-blocking! */ + qio_channel_set_blocking(server->ioc, false, NULL); + + server->co_trip = qemu_coroutine_create(vu_client_trip, server); + + aio_context_acquire(server->ctx); + vhost_user_server_attach_aio_context(server, server->ctx); + aio_context_release(server->ctx); +} + +void vhost_user_server_stop(VuServer *server) +{ + aio_context_acquire(server->ctx); + + qemu_bh_delete(server->restart_listener_bh); + server->restart_listener_bh = NULL; + + if (server->sioc) { + VuFdWatch *vu_fd_watch; + + QTAILQ_FOREACH(vu_fd_watch, &server->vu_fd_watches, next) { + aio_set_fd_handler(server->ctx, vu_fd_watch->fd, true, + NULL, NULL, NULL, vu_fd_watch); + } + + qio_channel_shutdown(server->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); + + AIO_WAIT_WHILE(server->ctx, server->co_trip); + } + + aio_context_release(server->ctx); + + if (server->listener) { + qio_net_listener_disconnect(server->listener); + object_unref(OBJECT(server->listener)); + } +} + +/* + * Allow the next client to connect to the server. Called from a BH in the main + * loop. + */ +static void restart_listener_bh(void *opaque) +{ + VuServer *server = opaque; + + qio_net_listener_set_client_func(server->listener, vu_accept, server, + NULL); +} + +/* Called with ctx acquired */ +void vhost_user_server_attach_aio_context(VuServer *server, AioContext *ctx) +{ + VuFdWatch *vu_fd_watch; + + server->ctx = ctx; + + if (!server->sioc) { + return; + } + + qio_channel_attach_aio_context(server->ioc, ctx); + + QTAILQ_FOREACH(vu_fd_watch, &server->vu_fd_watches, next) { + aio_set_fd_handler(ctx, vu_fd_watch->fd, true, kick_handler, NULL, + NULL, vu_fd_watch); + } + + aio_co_schedule(ctx, server->co_trip); +} + +/* Called with server->ctx acquired */ +void vhost_user_server_detach_aio_context(VuServer *server) +{ + if (server->sioc) { + VuFdWatch *vu_fd_watch; + + QTAILQ_FOREACH(vu_fd_watch, &server->vu_fd_watches, next) { + aio_set_fd_handler(server->ctx, vu_fd_watch->fd, true, + NULL, NULL, NULL, vu_fd_watch); + } + + qio_channel_detach_aio_context(server->ioc); + } + + server->ctx = NULL; +} + +bool vhost_user_server_start(VuServer *server, + SocketAddress *socket_addr, + AioContext *ctx, + uint16_t max_queues, + const VuDevIface *vu_iface, + Error **errp) +{ + QEMUBH *bh; + QIONetListener *listener; + + if (socket_addr->type != SOCKET_ADDRESS_TYPE_UNIX && + socket_addr->type != SOCKET_ADDRESS_TYPE_FD) { + error_setg(errp, "Only socket address types 'unix' and 'fd' are supported"); + return false; + } + + listener = qio_net_listener_new(); + if (qio_net_listener_open_sync(listener, socket_addr, 1, + errp) < 0) { + object_unref(OBJECT(listener)); + return false; + } + + bh = qemu_bh_new(restart_listener_bh, server); + + /* zero out unspecified fields */ + *server = (VuServer) { + .listener = listener, + .restart_listener_bh = bh, + .vu_iface = vu_iface, + .max_queues = max_queues, + .ctx = ctx, + }; + + qio_net_listener_set_name(server->listener, "vhost-user-backend-listener"); + + qio_net_listener_set_client_func(server->listener, + vu_accept, + server, + NULL); + + QTAILQ_INIT(&server->vu_fd_watches); + return true; +} diff --git a/version.texi.in b/version.texi.in deleted file mode 100644 index 0a723b8be6..0000000000 --- a/version.texi.in +++ /dev/null @@ -1,2 +0,0 @@ -@set VERSION @VERSION@ -@set CONFDIR @qemu_confdir@ |